From 6c56f61421f2f9b5c69808592f4c8be09f025d68 Mon Sep 17 00:00:00 2001 From: "Zhou,Junde" Date: Sun, 27 Mar 2022 09:12:50 +0800 Subject: [PATCH] support clounix asic for pegatron fn8656 product Signed-off-by: Zhou,Junde --- .../x86_64-pegatron_fn8656_bnf-r0/default_sku | 1 + .../fn8656-bnf/buffers.json.j2 | 2 + .../fn8656-bnf/buffers_defaults_t0.j2 | 61 + .../fn8656-bnf/buffers_defaults_t1.j2 | 61 + .../fn8656-bnf/lightning-fn8656_bnf.dsh | 1011 +++ .../fn8656-bnf/lightning-fn8656_bnf.led | Bin 0 -> 1140 bytes .../fn8656-bnf/pg_profile_lookup.ini | 17 + .../fn8656-bnf/port_config.ini | 57 + .../fn8656-bnf/qos.json.j2 | 198 + .../fn8656-bnf/sai.profile | 3 + .../installer.conf | 1 + .../lightning-fn8656_bnf.cfg | 24 + .../media_settings.json | 4437 +++++++++++ .../x86_64-pegatron_fn8656_bnf-r0/pcie.yaml | 5 + .../platform.json | 344 + .../platform_components.json | 14 + .../plugins/eeprom.py | 20 + .../plugins/psuutil.py | 92 + .../plugins/sfputil.py | 483 ++ .../pmon_daemon_control.json | 6 + .../sensors.conf | 42 + .../system_health_monitoring_config.json | 11 + .../thermal_policy.json | 31 + .../per_namespace/syncd.service.j2 | 3 + platform/clounix/clounix-modules.dep | 11 + platform/clounix/clounix-modules.mk | 8 + platform/clounix/clounix-modules/README.md | 2 + .../clounix/clounix-modules/debian/changelog | 11 + .../clounix/clounix-modules/debian/compat | 1 + .../clounix/clounix-modules/debian/control | 12 + platform/clounix/clounix-modules/debian/rules | 34 + .../clounix/clounix-modules/modules/Makefile | 94 + .../clounix/clounix-modules/modules/README | 32 + .../modules/init.d/clx-modules | 62 + .../modules/service/clx-modules.service | 13 + .../modules/src/clx_dev/osal_isymbol.c | 45 + .../modules/src/clx_dev/osal_mdc.c | 2587 +++++++ .../modules/src/clx_netif/hal_dawn_pkt_knl.c | 6356 ++++++++++++++++ .../src/clx_netif/hal_lightning_pkt_knl.c | 6600 +++++++++++++++++ .../modules/src/clx_netif/netif_knl.c | 304 + .../modules/src/clx_netif/netif_nl.c | 812 ++ .../modules/src/clx_netif/netif_osal.c | 755 ++ .../modules/src/clx_netif/netif_perf.c | 803 ++ .../clounix-modules/modules/src/inc/aml/aml.h | 108 + .../modules/src/inc/clx_error.h | 82 + .../modules/src/inc/clx_types.h | 329 + .../modules/src/inc/cmlib/cmlib.h | 103 + .../modules/src/inc/cmlib/cmlib_list.h | 219 + .../modules/src/inc/hal/common/hal_dev.h | 58 + .../modules/src/inc/hal/common/hal_dflt.h | 412 + .../modules/src/inc/hal_dawn_pkt_knl.h | 2195 ++++++ .../modules/src/inc/hal_lightning_pkt_knl.h | 2293 ++++++ .../modules/src/inc/netif_knl.h | 477 ++ .../modules/src/inc/netif_nl.h | 105 + .../modules/src/inc/netif_osal.h | 380 + .../modules/src/inc/netif_perf.h | 82 + .../modules/src/inc/osal/osal_mdc.h | 262 + .../modules/src/inc/osal/osal_types.h | 318 + platform/clounix/clx-utils.dep | 9 + platform/clounix/clx-utils.mk | 4 + platform/clounix/clx-utils/Makefile | 16 + .../clounix-utils/port_breakout/__init__.py | 0 .../port_breakout/api_libpcfgparsing.txt | 42 + .../port_breakout/libpcfgparsing.py | 616 ++ .../clounix-utils/port_breakout/main.py | 1122 +++ .../port_breakout/port_config.clx.j2 | 87 + .../clx-utils/clounix-utils/scripts/sfpdet | 2144 ++++++ .../clounix/clx-utils/clounix-utils/setup.py | 30 + platform/clounix/docker-ptf-clounix.mk | 7 + platform/clounix/docker-saiserver-clounix.mk | 22 + .../docker-saiserver-clounix/Dockerfile.j2 | 72 + .../base_image_files/00README.md | 21 + .../base_image_files/clx_diag | Bin 0 -> 10456 bytes .../base_image_files/clx_ipython | 5 + .../base_image_files/rsyslog.conf | 76 + .../base_image_files/sai.profile | 2 + .../base_image_files/warm-verifier | Bin 0 -> 8816 bytes .../clounix/docker-saiserver-clounix/clx_diag | Bin 0 -> 10456 bytes .../install_clingkernel.sh | 13 + .../ipython_config.json | 6 + .../docker-saiserver-clounix/profile.ini | 5 + .../clounix/docker-saiserver-clounix/start.sh | 7 + .../docker-saiserver-clounix/supervisord.conf | 32 + .../docker-saiserver-clounix/warm-verifier | Bin 0 -> 8816 bytes platform/clounix/docker-syncd-clounix-rpc.dep | 9 + platform/clounix/docker-syncd-clounix-rpc.mk | 27 + .../docker-syncd-clounix-rpc/Dockerfile.j2 | 59 + .../base_image_files/clx_diag | 3 + .../base_image_files/clx_icling | 5 + .../base_image_files/clx_ipython | 5 + .../ptf_nn_agent.conf | 10 + platform/clounix/docker-syncd-clounix.dep | 11 + platform/clounix/docker-syncd-clounix.mk | 19 + .../docker-syncd-clounix/Dockerfile.j2 | 76 + .../base_image_files/clx_diag | 3 + .../base_image_files/clx_icling | 5 + .../base_image_files/clx_ipython | 5 + .../docker-syncd-clounix/critical_processes | 2 + .../install_clingkernel.sh | 13 + .../docker-syncd-clounix/ipython_config.json | 6 + .../docker-syncd-clounix/supervisord.conf | 38 + platform/clounix/libsaithrift-dev.dep | 13 + platform/clounix/libsaithrift-dev.mk | 20 + platform/clounix/one-image.dep | 2 + platform/clounix/one-image.mk | 16 + .../clounix/platform-modules-pegatron.dep | 10 + platform/clounix/platform-modules-pegatron.mk | 11 + platform/clounix/platform.conf | 0 platform/clounix/rules.dep | 7 + platform/clounix/rules.mk | 27 + platform/clounix/sai.dep | 13 + platform/clounix/sai.mk | 42 + .../sonic-platform-modules-pegatron/LICENSE | 16 + .../sonic-platform-modules-pegatron/README.md | 1 + .../common/modules/Makefile | 3 + .../common/modules/clounix_sysfs_common.h | 16 + .../common/modules/clounix_sysfs_main.c | 445 ++ .../common/modules/clounix_sysfs_var.h | 15 + .../common/modules/i2c-mux-pca9641.c | 427 ++ .../common/modules/pegatron_hwmon_mcu.c | 1930 +++++ .../common/modules/pub/pegatron_pub.h | 42 + .../debian/changelog | 5 + .../debian/compat | 1 + .../debian/control | 11 + .../debian/rules | 162 + .../fn8656-bnf/bios_bin/afulnx_64 | Bin 0 -> 1044160 bytes .../fn8656-bnf/modules/Makefile | 5 + .../fn8656-bnf/modules/clounix_watchdog.c | 790 ++ .../fn8656-bnf/modules/iTCO_vendor.h | 16 + .../modules/pegatron_fn8656_bnf_cpld.c | 1692 +++++ .../modules/pegatron_fn8656_bnf_psu.c | 919 +++ .../modules/pegatron_fn8656_bnf_sfp.c | 1323 ++++ .../modules/pegatron_fn8656_bnf_watchdog.c | 464 ++ .../fn8656-bnf/pegaProcess/__init__.py | 0 .../fn8656-bnf/pegaProcess/common.py | 88 + .../fn8656-bnf/pegaProcess/device.py | 471 ++ .../fn8656-bnf/pegaProcess/fwupdates.py | 356 + .../fn8656-bnf/pegaProcess/helper.py | 156 + .../fn8656-bnf/pegaProcess/main.py | 610 ++ .../scripts/platform_update_reboot_cause | 24 + .../fn8656-bnf/scripts/sensors | 5 + .../service/fn8656_bnf-platform-init.service | 13 + .../fn8656-bnf/service/platform_api_mgnt.sh | 44 + .../fn8656-bnf/setup.py | 15 + .../fn8656-bnf/sonic_platform/__init__.py | 3 + .../fn8656-bnf/sonic_platform/chassis.py | 508 ++ .../fn8656-bnf/sonic_platform/component.py | 272 + .../fn8656-bnf/sonic_platform/eeprom.py | 110 + .../fn8656-bnf/sonic_platform/fan.py | 294 + .../fn8656-bnf/sonic_platform/fan_drawer.py | 119 + .../fn8656-bnf/sonic_platform/helper.py | 156 + .../fn8656-bnf/sonic_platform/platform.py | 20 + .../fn8656-bnf/sonic_platform/psu.py | 391 + .../fn8656-bnf/sonic_platform/qsfp.py | 612 ++ .../fn8656-bnf/sonic_platform/qsfp_8436.py | 1079 +++ .../fn8656-bnf/sonic_platform/qsfp_cmis.py | 1782 +++++ .../fn8656-bnf/sonic_platform/sfp.py | 790 ++ .../fn8656-bnf/sonic_platform/thermal.py | 226 + .../sonic_platform/thermal_actions.py | 70 + .../sonic_platform/thermal_conditions.py | 20 + .../sonic_platform/thermal_infos.py | 168 + .../sonic_platform/thermal_manager.py | 40 + .../fn8656-bnf/sonic_platform/watchdog.py | 127 + .../fn8656-bnf/utils/install_firmware.sh | 192 + .../fn8656-bnf/utils/install_firmware_ext.sh | 45 + .../utils/pegatron_fn8656_bnf_util.py | 58 + 166 files changed, 54831 insertions(+) create mode 100644 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/default_sku create mode 100644 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/buffers.json.j2 create mode 100644 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/buffers_defaults_t0.j2 create mode 100644 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/buffers_defaults_t1.j2 create mode 100755 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/lightning-fn8656_bnf.dsh create mode 100644 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/lightning-fn8656_bnf.led create mode 100644 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/pg_profile_lookup.ini create mode 100755 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/port_config.ini create mode 100644 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/qos.json.j2 create mode 100755 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/sai.profile create mode 100644 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/installer.conf create mode 100755 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/lightning-fn8656_bnf.cfg create mode 100644 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/media_settings.json create mode 100644 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/pcie.yaml create mode 100644 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/platform.json create mode 100644 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/platform_components.json create mode 100755 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/plugins/eeprom.py create mode 100755 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/plugins/psuutil.py create mode 100755 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/plugins/sfputil.py create mode 100644 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/pmon_daemon_control.json create mode 100644 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/sensors.conf create mode 100644 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/system_health_monitoring_config.json create mode 100644 device/pegatron/x86_64-pegatron_fn8656_bnf-r0/thermal_policy.json create mode 100644 platform/clounix/clounix-modules.dep create mode 100644 platform/clounix/clounix-modules.mk create mode 100644 platform/clounix/clounix-modules/README.md create mode 100644 platform/clounix/clounix-modules/debian/changelog create mode 100644 platform/clounix/clounix-modules/debian/compat create mode 100644 platform/clounix/clounix-modules/debian/control create mode 100755 platform/clounix/clounix-modules/debian/rules create mode 100755 platform/clounix/clounix-modules/modules/Makefile create mode 100644 platform/clounix/clounix-modules/modules/README create mode 100755 platform/clounix/clounix-modules/modules/init.d/clx-modules create mode 100644 platform/clounix/clounix-modules/modules/service/clx-modules.service create mode 100644 platform/clounix/clounix-modules/modules/src/clx_dev/osal_isymbol.c create mode 100644 platform/clounix/clounix-modules/modules/src/clx_dev/osal_mdc.c create mode 100755 platform/clounix/clounix-modules/modules/src/clx_netif/hal_dawn_pkt_knl.c create mode 100755 platform/clounix/clounix-modules/modules/src/clx_netif/hal_lightning_pkt_knl.c create mode 100755 platform/clounix/clounix-modules/modules/src/clx_netif/netif_knl.c create mode 100755 platform/clounix/clounix-modules/modules/src/clx_netif/netif_nl.c create mode 100755 platform/clounix/clounix-modules/modules/src/clx_netif/netif_osal.c create mode 100755 platform/clounix/clounix-modules/modules/src/clx_netif/netif_perf.c create mode 100644 platform/clounix/clounix-modules/modules/src/inc/aml/aml.h create mode 100644 platform/clounix/clounix-modules/modules/src/inc/clx_error.h create mode 100644 platform/clounix/clounix-modules/modules/src/inc/clx_types.h create mode 100644 platform/clounix/clounix-modules/modules/src/inc/cmlib/cmlib.h create mode 100644 platform/clounix/clounix-modules/modules/src/inc/cmlib/cmlib_list.h create mode 100644 platform/clounix/clounix-modules/modules/src/inc/hal/common/hal_dev.h create mode 100644 platform/clounix/clounix-modules/modules/src/inc/hal/common/hal_dflt.h create mode 100755 platform/clounix/clounix-modules/modules/src/inc/hal_dawn_pkt_knl.h create mode 100755 platform/clounix/clounix-modules/modules/src/inc/hal_lightning_pkt_knl.h create mode 100644 platform/clounix/clounix-modules/modules/src/inc/netif_knl.h create mode 100755 platform/clounix/clounix-modules/modules/src/inc/netif_nl.h create mode 100755 platform/clounix/clounix-modules/modules/src/inc/netif_osal.h create mode 100755 platform/clounix/clounix-modules/modules/src/inc/netif_perf.h create mode 100644 platform/clounix/clounix-modules/modules/src/inc/osal/osal_mdc.h create mode 100644 platform/clounix/clounix-modules/modules/src/inc/osal/osal_types.h create mode 100644 platform/clounix/clx-utils.dep create mode 100644 platform/clounix/clx-utils.mk create mode 100644 platform/clounix/clx-utils/Makefile create mode 100644 platform/clounix/clx-utils/clounix-utils/port_breakout/__init__.py create mode 100644 platform/clounix/clx-utils/clounix-utils/port_breakout/api_libpcfgparsing.txt create mode 100644 platform/clounix/clx-utils/clounix-utils/port_breakout/libpcfgparsing.py create mode 100644 platform/clounix/clx-utils/clounix-utils/port_breakout/main.py create mode 100644 platform/clounix/clx-utils/clounix-utils/port_breakout/port_config.clx.j2 create mode 100644 platform/clounix/clx-utils/clounix-utils/scripts/sfpdet create mode 100644 platform/clounix/clx-utils/clounix-utils/setup.py create mode 100644 platform/clounix/docker-ptf-clounix.mk create mode 100644 platform/clounix/docker-saiserver-clounix.mk create mode 100644 platform/clounix/docker-saiserver-clounix/Dockerfile.j2 create mode 100644 platform/clounix/docker-saiserver-clounix/base_image_files/00README.md create mode 100755 platform/clounix/docker-saiserver-clounix/base_image_files/clx_diag create mode 100755 platform/clounix/docker-saiserver-clounix/base_image_files/clx_ipython create mode 100644 platform/clounix/docker-saiserver-clounix/base_image_files/rsyslog.conf create mode 100755 platform/clounix/docker-saiserver-clounix/base_image_files/sai.profile create mode 100755 platform/clounix/docker-saiserver-clounix/base_image_files/warm-verifier create mode 100755 platform/clounix/docker-saiserver-clounix/clx_diag create mode 100755 platform/clounix/docker-saiserver-clounix/install_clingkernel.sh create mode 100644 platform/clounix/docker-saiserver-clounix/ipython_config.json create mode 100644 platform/clounix/docker-saiserver-clounix/profile.ini create mode 100755 platform/clounix/docker-saiserver-clounix/start.sh create mode 100644 platform/clounix/docker-saiserver-clounix/supervisord.conf create mode 100755 platform/clounix/docker-saiserver-clounix/warm-verifier create mode 100644 platform/clounix/docker-syncd-clounix-rpc.dep create mode 100644 platform/clounix/docker-syncd-clounix-rpc.mk create mode 100644 platform/clounix/docker-syncd-clounix-rpc/Dockerfile.j2 create mode 100755 platform/clounix/docker-syncd-clounix-rpc/base_image_files/clx_diag create mode 100755 platform/clounix/docker-syncd-clounix-rpc/base_image_files/clx_icling create mode 100755 platform/clounix/docker-syncd-clounix-rpc/base_image_files/clx_ipython create mode 100644 platform/clounix/docker-syncd-clounix-rpc/ptf_nn_agent.conf create mode 100644 platform/clounix/docker-syncd-clounix.dep create mode 100644 platform/clounix/docker-syncd-clounix.mk create mode 100755 platform/clounix/docker-syncd-clounix/Dockerfile.j2 create mode 100755 platform/clounix/docker-syncd-clounix/base_image_files/clx_diag create mode 100755 platform/clounix/docker-syncd-clounix/base_image_files/clx_icling create mode 100755 platform/clounix/docker-syncd-clounix/base_image_files/clx_ipython create mode 100644 platform/clounix/docker-syncd-clounix/critical_processes create mode 100755 platform/clounix/docker-syncd-clounix/install_clingkernel.sh create mode 100644 platform/clounix/docker-syncd-clounix/ipython_config.json create mode 100644 platform/clounix/docker-syncd-clounix/supervisord.conf create mode 100644 platform/clounix/libsaithrift-dev.dep create mode 100644 platform/clounix/libsaithrift-dev.mk create mode 100644 platform/clounix/one-image.dep create mode 100644 platform/clounix/one-image.mk create mode 100644 platform/clounix/platform-modules-pegatron.dep create mode 100644 platform/clounix/platform-modules-pegatron.mk create mode 100644 platform/clounix/platform.conf create mode 100644 platform/clounix/rules.dep create mode 100644 platform/clounix/rules.mk create mode 100644 platform/clounix/sai.dep create mode 100644 platform/clounix/sai.mk create mode 100644 platform/clounix/sonic-platform-modules-pegatron/LICENSE create mode 100644 platform/clounix/sonic-platform-modules-pegatron/README.md create mode 100644 platform/clounix/sonic-platform-modules-pegatron/common/modules/Makefile create mode 100644 platform/clounix/sonic-platform-modules-pegatron/common/modules/clounix_sysfs_common.h create mode 100644 platform/clounix/sonic-platform-modules-pegatron/common/modules/clounix_sysfs_main.c create mode 100644 platform/clounix/sonic-platform-modules-pegatron/common/modules/clounix_sysfs_var.h create mode 100644 platform/clounix/sonic-platform-modules-pegatron/common/modules/i2c-mux-pca9641.c create mode 100644 platform/clounix/sonic-platform-modules-pegatron/common/modules/pegatron_hwmon_mcu.c create mode 100644 platform/clounix/sonic-platform-modules-pegatron/common/modules/pub/pegatron_pub.h create mode 100644 platform/clounix/sonic-platform-modules-pegatron/debian/changelog create mode 100644 platform/clounix/sonic-platform-modules-pegatron/debian/compat create mode 100755 platform/clounix/sonic-platform-modules-pegatron/debian/control create mode 100755 platform/clounix/sonic-platform-modules-pegatron/debian/rules create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/bios_bin/afulnx_64 create mode 100644 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/Makefile create mode 100644 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/clounix_watchdog.c create mode 100644 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/iTCO_vendor.h create mode 100644 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/pegatron_fn8656_bnf_cpld.c create mode 100644 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/pegatron_fn8656_bnf_psu.c create mode 100644 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/pegatron_fn8656_bnf_sfp.c create mode 100644 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/pegatron_fn8656_bnf_watchdog.c create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/__init__.py create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/common.py create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/device.py create mode 100644 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/fwupdates.py create mode 100644 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/helper.py create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/main.py create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/scripts/platform_update_reboot_cause create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/scripts/sensors create mode 100644 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/service/fn8656_bnf-platform-init.service create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/service/platform_api_mgnt.sh create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/setup.py create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/__init__.py create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/chassis.py create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/component.py create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/eeprom.py create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/fan.py create mode 100644 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/fan_drawer.py create mode 100644 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/helper.py create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/platform.py create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/psu.py create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/qsfp.py create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/qsfp_8436.py create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/qsfp_cmis.py create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/sfp.py create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/thermal.py create mode 100644 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/thermal_actions.py create mode 100644 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/thermal_conditions.py create mode 100644 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/thermal_infos.py create mode 100644 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/thermal_manager.py create mode 100644 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/watchdog.py create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/utils/install_firmware.sh create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/utils/install_firmware_ext.sh create mode 100755 platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/utils/pegatron_fn8656_bnf_util.py diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/default_sku b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/default_sku new file mode 100644 index 000000000000..5dc4f8a03888 --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/default_sku @@ -0,0 +1 @@ +fn8656-bnf l2 diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/buffers.json.j2 b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/buffers.json.j2 new file mode 100644 index 000000000000..9354b7ec5b59 --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/buffers.json.j2 @@ -0,0 +1,2 @@ +{%- set default_topo = 't0' %} +{%- include 'buffers_config.j2' %} \ No newline at end of file diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/buffers_defaults_t0.j2 b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/buffers_defaults_t0.j2 new file mode 100644 index 000000000000..1cd9a15bc86e --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/buffers_defaults_t0.j2 @@ -0,0 +1,61 @@ +{% set default_cable = '5m' %} +{% set ingress_lossless_pool_size = '20971328' %} +{% set ingress_lossy_pool_size = '20971328' %} +{% set egress_lossless_pool_size = '20971328' %} +{% set egress_lossy_pool_size = '20971328' %} + +{%- macro generate_port_lists(PORT_ALL) %} + {# Generate list of ports #} + {%- for port_idx in range(0, 56) %} + {%- if PORT_ALL.append("Ethernet%d" % (port_idx*4)) %}{%- endif %} + {%- endfor %} +{%- endmacro %} + +{%- macro generate_buffer_pool_and_profiles() %} + "BUFFER_POOL": { + "ingress_lossless_pool": { + "size": "{{ ingress_lossless_pool_size }}", + "type": "ingress", + "mode": "static" + }, + "ingress_lossy_pool": { + "size": "{{ ingress_lossy_pool_size }}", + "type": "ingress", + "mode": "dynamic" + }, + "egress_lossless_pool": { + "size": "{{ egress_lossless_pool_size }}", + "type": "egress", + "mode": "dynamic" + }, + "egress_lossy_pool": { + "size": "{{ egress_lossy_pool_size }}", + "type": "egress", + "mode": "dynamic" + } + }, + "BUFFER_PROFILE": { + "ingress_lossless_profile": { + "pool":"[BUFFER_POOL|ingress_lossless_pool]", + "xon":"78400", + "xoff":"132160", + "size":"3584", + "static_th":"82880" + }, + "ingress_lossy_profile": { + "pool":"[BUFFER_POOL|ingress_lossy_pool]", + "size":"3584", + "dynamic_th":"-1" + }, + "egress_lossless_profile": { + "pool":"[BUFFER_POOL|egress_lossless_pool]", + "size":"0", + "static_th":"{{ egress_lossless_pool_size }}" + }, + "egress_lossy_profile": { + "pool":"[BUFFER_POOL|egress_lossy_pool]", + "size":"3584", + "dynamic_th":"-4" + } + }, +{%- endmacro %} diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/buffers_defaults_t1.j2 b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/buffers_defaults_t1.j2 new file mode 100644 index 000000000000..1cd9a15bc86e --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/buffers_defaults_t1.j2 @@ -0,0 +1,61 @@ +{% set default_cable = '5m' %} +{% set ingress_lossless_pool_size = '20971328' %} +{% set ingress_lossy_pool_size = '20971328' %} +{% set egress_lossless_pool_size = '20971328' %} +{% set egress_lossy_pool_size = '20971328' %} + +{%- macro generate_port_lists(PORT_ALL) %} + {# Generate list of ports #} + {%- for port_idx in range(0, 56) %} + {%- if PORT_ALL.append("Ethernet%d" % (port_idx*4)) %}{%- endif %} + {%- endfor %} +{%- endmacro %} + +{%- macro generate_buffer_pool_and_profiles() %} + "BUFFER_POOL": { + "ingress_lossless_pool": { + "size": "{{ ingress_lossless_pool_size }}", + "type": "ingress", + "mode": "static" + }, + "ingress_lossy_pool": { + "size": "{{ ingress_lossy_pool_size }}", + "type": "ingress", + "mode": "dynamic" + }, + "egress_lossless_pool": { + "size": "{{ egress_lossless_pool_size }}", + "type": "egress", + "mode": "dynamic" + }, + "egress_lossy_pool": { + "size": "{{ egress_lossy_pool_size }}", + "type": "egress", + "mode": "dynamic" + } + }, + "BUFFER_PROFILE": { + "ingress_lossless_profile": { + "pool":"[BUFFER_POOL|ingress_lossless_pool]", + "xon":"78400", + "xoff":"132160", + "size":"3584", + "static_th":"82880" + }, + "ingress_lossy_profile": { + "pool":"[BUFFER_POOL|ingress_lossy_pool]", + "size":"3584", + "dynamic_th":"-1" + }, + "egress_lossless_profile": { + "pool":"[BUFFER_POOL|egress_lossless_pool]", + "size":"0", + "static_th":"{{ egress_lossless_pool_size }}" + }, + "egress_lossy_profile": { + "pool":"[BUFFER_POOL|egress_lossy_pool]", + "size":"3584", + "dynamic_th":"-4" + } + }, +{%- endmacro %} diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/lightning-fn8656_bnf.dsh b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/lightning-fn8656_bnf.dsh new file mode 100755 index 000000000000..b55e10de11ab --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/lightning-fn8656_bnf.dsh @@ -0,0 +1,1011 @@ +init start stage unit=0 low-level +init set port-map unit=0 port=0 eth-macro=12 lane=4 max-speed=100g active=true +init set port-map unit=0 port=1 eth-macro=12 lane=5 max-speed=50g active=false +init set port-map unit=0 port=2 eth-macro=12 lane=6 max-speed=100g active=false +init set port-map unit=0 port=3 eth-macro=12 lane=7 max-speed=50g active=false +init set port-map unit=0 port=4 eth-macro=13 lane=4 max-speed=100g active=true +init set port-map unit=0 port=5 eth-macro=13 lane=5 max-speed=50g active=false +init set port-map unit=0 port=6 eth-macro=13 lane=6 max-speed=100g active=false +init set port-map unit=0 port=7 eth-macro=13 lane=7 max-speed=50g active=false +init set port-map unit=0 port=8 eth-macro=12 lane=0 max-speed=100g active=true +init set port-map unit=0 port=9 eth-macro=12 lane=1 max-speed=50g active=false +init set port-map unit=0 port=10 eth-macro=12 lane=2 max-speed=100g active=false +init set port-map unit=0 port=11 eth-macro=12 lane=3 max-speed=50g active=false +init set port-map unit=0 port=12 eth-macro=13 lane=0 max-speed=100g active=true +init set port-map unit=0 port=13 eth-macro=13 lane=1 max-speed=50g active=false +init set port-map unit=0 port=14 eth-macro=13 lane=2 max-speed=100g active=false +init set port-map unit=0 port=15 eth-macro=13 lane=3 max-speed=50g active=false +init set port-map unit=0 port=16 eth-macro=14 lane=4 max-speed=100g active=true +init set port-map unit=0 port=17 eth-macro=14 lane=5 max-speed=50g active=false +init set port-map unit=0 port=18 eth-macro=14 lane=6 max-speed=100g active=false +init set port-map unit=0 port=19 eth-macro=14 lane=7 max-speed=50g active=false +init set port-map unit=0 port=20 eth-macro=15 lane=4 max-speed=100g active=true +init set port-map unit=0 port=21 eth-macro=15 lane=5 max-speed=50g active=false +init set port-map unit=0 port=22 eth-macro=15 lane=6 max-speed=100g active=false +init set port-map unit=0 port=23 eth-macro=15 lane=7 max-speed=50g active=false +init set port-map unit=0 port=24 eth-macro=14 lane=0 max-speed=100g active=true +init set port-map unit=0 port=25 eth-macro=14 lane=1 max-speed=50g active=false +init set port-map unit=0 port=26 eth-macro=14 lane=2 max-speed=100g active=false +init set port-map unit=0 port=27 eth-macro=14 lane=3 max-speed=50g active=false +init set port-map unit=0 port=28 eth-macro=15 lane=0 max-speed=100g active=true +init set port-map unit=0 port=29 eth-macro=15 lane=1 max-speed=50g active=false +init set port-map unit=0 port=30 eth-macro=15 lane=2 max-speed=100g active=false +init set port-map unit=0 port=31 eth-macro=15 lane=3 max-speed=50g active=false +init set port-map unit=0 port=32 eth-macro=16 lane=0 max-speed=100g active=true +init set port-map unit=0 port=33 eth-macro=16 lane=1 max-speed=50g active=false +init set port-map unit=0 port=34 eth-macro=16 lane=2 max-speed=100g active=false +init set port-map unit=0 port=35 eth-macro=16 lane=3 max-speed=50g active=false +init set port-map unit=0 port=36 eth-macro=17 lane=0 max-speed=100g active=true +init set port-map unit=0 port=37 eth-macro=17 lane=1 max-speed=50g active=false +init set port-map unit=0 port=38 eth-macro=17 lane=2 max-speed=100g active=false +init set port-map unit=0 port=39 eth-macro=17 lane=3 max-speed=50g active=false +init set port-map unit=0 port=40 eth-macro=16 lane=4 max-speed=100g active=true +init set port-map unit=0 port=41 eth-macro=16 lane=5 max-speed=50g active=false +init set port-map unit=0 port=42 eth-macro=16 lane=6 max-speed=100g active=false +init set port-map unit=0 port=43 eth-macro=16 lane=7 max-speed=50g active=false +init set port-map unit=0 port=44 eth-macro=17 lane=4 max-speed=100g active=true +init set port-map unit=0 port=45 eth-macro=17 lane=5 max-speed=50g active=false +init set port-map unit=0 port=46 eth-macro=17 lane=6 max-speed=100g active=false +init set port-map unit=0 port=47 eth-macro=17 lane=7 max-speed=50g active=false +init set port-map unit=0 port=48 eth-macro=18 lane=0 max-speed=100g active=true +init set port-map unit=0 port=49 eth-macro=18 lane=1 max-speed=50g active=false +init set port-map unit=0 port=50 eth-macro=18 lane=2 max-speed=100g active=false +init set port-map unit=0 port=51 eth-macro=18 lane=3 max-speed=50g active=false +init set port-map unit=0 port=52 eth-macro=19 lane=0 max-speed=100g active=true +init set port-map unit=0 port=53 eth-macro=19 lane=1 max-speed=50g active=false +init set port-map unit=0 port=54 eth-macro=19 lane=2 max-speed=100g active=false +init set port-map unit=0 port=55 eth-macro=19 lane=3 max-speed=50g active=false +init set port-map unit=0 port=56 eth-macro=18 lane=4 max-speed=100g active=true +init set port-map unit=0 port=57 eth-macro=18 lane=5 max-speed=50g active=false +init set port-map unit=0 port=58 eth-macro=18 lane=6 max-speed=100g active=false +init set port-map unit=0 port=59 eth-macro=18 lane=7 max-speed=50g active=false +init set port-map unit=0 port=60 eth-macro=19 lane=4 max-speed=100g active=true +init set port-map unit=0 port=61 eth-macro=19 lane=5 max-speed=50g active=false +init set port-map unit=0 port=62 eth-macro=19 lane=6 max-speed=100g active=false +init set port-map unit=0 port=63 eth-macro=19 lane=7 max-speed=50g active=false +init set port-map unit=0 port=64 eth-macro=4 lane=4 max-speed=100g active=true +init set port-map unit=0 port=65 eth-macro=4 lane=5 max-speed=50g active=false +init set port-map unit=0 port=66 eth-macro=4 lane=6 max-speed=100g active=false +init set port-map unit=0 port=67 eth-macro=4 lane=7 max-speed=50g active=false +init set port-map unit=0 port=68 eth-macro=5 lane=4 max-speed=100g active=true +init set port-map unit=0 port=69 eth-macro=5 lane=5 max-speed=50g active=false +init set port-map unit=0 port=70 eth-macro=5 lane=6 max-speed=100g active=false +init set port-map unit=0 port=71 eth-macro=5 lane=7 max-speed=50g active=false +init set port-map unit=0 port=72 eth-macro=4 lane=0 max-speed=100g active=true +init set port-map unit=0 port=73 eth-macro=4 lane=1 max-speed=50g active=false +init set port-map unit=0 port=74 eth-macro=4 lane=2 max-speed=100g active=false +init set port-map unit=0 port=75 eth-macro=4 lane=3 max-speed=50g active=false +init set port-map unit=0 port=76 eth-macro=5 lane=0 max-speed=100g active=true +init set port-map unit=0 port=77 eth-macro=5 lane=1 max-speed=50g active=false +init set port-map unit=0 port=78 eth-macro=5 lane=2 max-speed=100g active=false +init set port-map unit=0 port=79 eth-macro=5 lane=3 max-speed=50g active=false +init set port-map unit=0 port=80 eth-macro=6 lane=4 max-speed=100g active=true +init set port-map unit=0 port=81 eth-macro=6 lane=5 max-speed=50g active=false +init set port-map unit=0 port=82 eth-macro=6 lane=6 max-speed=100g active=false +init set port-map unit=0 port=83 eth-macro=6 lane=7 max-speed=50g active=false +init set port-map unit=0 port=84 eth-macro=7 lane=4 max-speed=100g active=true +init set port-map unit=0 port=85 eth-macro=7 lane=5 max-speed=50g active=false +init set port-map unit=0 port=86 eth-macro=7 lane=6 max-speed=100g active=false +init set port-map unit=0 port=87 eth-macro=7 lane=7 max-speed=50g active=false +init set port-map unit=0 port=88 eth-macro=6 lane=0 max-speed=100g active=true +init set port-map unit=0 port=89 eth-macro=6 lane=1 max-speed=50g active=false +init set port-map unit=0 port=90 eth-macro=6 lane=2 max-speed=100g active=false +init set port-map unit=0 port=91 eth-macro=6 lane=3 max-speed=50g active=false +init set port-map unit=0 port=92 eth-macro=7 lane=0 max-speed=100g active=true +init set port-map unit=0 port=93 eth-macro=7 lane=1 max-speed=50g active=false +init set port-map unit=0 port=94 eth-macro=7 lane=2 max-speed=100g active=false +init set port-map unit=0 port=95 eth-macro=7 lane=3 max-speed=50g active=false +init set port-map unit=0 port=96 eth-macro=8 lane=0 max-speed=100g active=true +init set port-map unit=0 port=97 eth-macro=8 lane=1 max-speed=50g active=false +init set port-map unit=0 port=98 eth-macro=8 lane=2 max-speed=100g active=false +init set port-map unit=0 port=99 eth-macro=8 lane=3 max-speed=50g active=false +init set port-map unit=0 port=100 eth-macro=9 lane=0 max-speed=100g active=true +init set port-map unit=0 port=101 eth-macro=9 lane=1 max-speed=50g active=false +init set port-map unit=0 port=102 eth-macro=9 lane=2 max-speed=100g active=false +init set port-map unit=0 port=103 eth-macro=9 lane=3 max-speed=50g active=false +init set port-map unit=0 port=104 eth-macro=8 lane=4 max-speed=100g active=true +init set port-map unit=0 port=105 eth-macro=8 lane=5 max-speed=50g active=false +init set port-map unit=0 port=106 eth-macro=8 lane=6 max-speed=100g active=false +init set port-map unit=0 port=107 eth-macro=8 lane=7 max-speed=50g active=false +init set port-map unit=0 port=108 eth-macro=9 lane=4 max-speed=100g active=true +init set port-map unit=0 port=109 eth-macro=9 lane=5 max-speed=50g active=false +init set port-map unit=0 port=110 eth-macro=9 lane=6 max-speed=100g active=false +init set port-map unit=0 port=111 eth-macro=9 lane=7 max-speed=50g active=false +init set port-map unit=0 port=112 eth-macro=10 lane=0 max-speed=100g active=true +init set port-map unit=0 port=113 eth-macro=10 lane=1 max-speed=50g active=false +init set port-map unit=0 port=114 eth-macro=10 lane=2 max-speed=100g active=false +init set port-map unit=0 port=115 eth-macro=10 lane=3 max-speed=50g active=false +init set port-map unit=0 port=116 eth-macro=11 lane=0 max-speed=100g active=true +init set port-map unit=0 port=117 eth-macro=11 lane=1 max-speed=50g active=false +init set port-map unit=0 port=118 eth-macro=11 lane=2 max-speed=100g active=false +init set port-map unit=0 port=119 eth-macro=11 lane=3 max-speed=50g active=false +init set port-map unit=0 port=120 eth-macro=10 lane=4 max-speed=100g active=true +init set port-map unit=0 port=121 eth-macro=10 lane=5 max-speed=50g active=false +init set port-map unit=0 port=122 eth-macro=10 lane=6 max-speed=100g active=false +init set port-map unit=0 port=123 eth-macro=10 lane=7 max-speed=50g active=false +init set port-map unit=0 port=124 eth-macro=11 lane=4 max-speed=100g active=true +init set port-map unit=0 port=125 eth-macro=11 lane=5 max-speed=50g active=false +init set port-map unit=0 port=126 eth-macro=11 lane=6 max-speed=100g active=false +init set port-map unit=0 port=127 eth-macro=11 lane=7 max-speed=50g active=false +init set port-map unit=0 port=128 eth-macro=28 lane=4 max-speed=100g active=true +init set port-map unit=0 port=129 eth-macro=28 lane=5 max-speed=50g active=false +init set port-map unit=0 port=130 eth-macro=28 lane=6 max-speed=100g active=false +init set port-map unit=0 port=131 eth-macro=28 lane=7 max-speed=50g active=false +init set port-map unit=0 port=132 eth-macro=29 lane=4 max-speed=100g active=true +init set port-map unit=0 port=133 eth-macro=29 lane=5 max-speed=50g active=false +init set port-map unit=0 port=134 eth-macro=29 lane=6 max-speed=100g active=false +init set port-map unit=0 port=135 eth-macro=29 lane=7 max-speed=50g active=false +init set port-map unit=0 port=136 eth-macro=28 lane=0 max-speed=100g active=true +init set port-map unit=0 port=137 eth-macro=28 lane=1 max-speed=50g active=false +init set port-map unit=0 port=138 eth-macro=28 lane=2 max-speed=100g active=false +init set port-map unit=0 port=139 eth-macro=28 lane=3 max-speed=50g active=false +init set port-map unit=0 port=140 eth-macro=29 lane=0 max-speed=100g active=true +init set port-map unit=0 port=141 eth-macro=29 lane=1 max-speed=50g active=false +init set port-map unit=0 port=142 eth-macro=29 lane=2 max-speed=100g active=false +init set port-map unit=0 port=143 eth-macro=29 lane=3 max-speed=50g active=false +init set port-map unit=0 port=144 eth-macro=30 lane=4 max-speed=100g active=true +init set port-map unit=0 port=145 eth-macro=30 lane=5 max-speed=50g active=false +init set port-map unit=0 port=146 eth-macro=30 lane=6 max-speed=100g active=false +init set port-map unit=0 port=147 eth-macro=30 lane=7 max-speed=50g active=false +init set port-map unit=0 port=148 eth-macro=31 lane=4 max-speed=100g active=true +init set port-map unit=0 port=149 eth-macro=31 lane=5 max-speed=50g active=false +init set port-map unit=0 port=150 eth-macro=31 lane=6 max-speed=100g active=false +init set port-map unit=0 port=151 eth-macro=31 lane=7 max-speed=50g active=false +init set port-map unit=0 port=152 eth-macro=30 lane=0 max-speed=100g active=true +init set port-map unit=0 port=153 eth-macro=30 lane=1 max-speed=50g active=false +init set port-map unit=0 port=154 eth-macro=30 lane=2 max-speed=100g active=false +init set port-map unit=0 port=155 eth-macro=30 lane=3 max-speed=50g active=false +init set port-map unit=0 port=156 eth-macro=31 lane=0 max-speed=100g active=true +init set port-map unit=0 port=157 eth-macro=31 lane=1 max-speed=50g active=false +init set port-map unit=0 port=158 eth-macro=31 lane=2 max-speed=100g active=false +init set port-map unit=0 port=159 eth-macro=31 lane=3 max-speed=50g active=false +init set port-map unit=0 port=160 eth-macro=0 lane=0 max-speed=100g active=true +init set port-map unit=0 port=161 eth-macro=0 lane=1 max-speed=50g active=false +init set port-map unit=0 port=162 eth-macro=0 lane=2 max-speed=100g active=false +init set port-map unit=0 port=163 eth-macro=0 lane=3 max-speed=50g active=false +init set port-map unit=0 port=164 eth-macro=1 lane=0 max-speed=100g active=true +init set port-map unit=0 port=165 eth-macro=1 lane=1 max-speed=50g active=false +init set port-map unit=0 port=166 eth-macro=1 lane=2 max-speed=100g active=false +init set port-map unit=0 port=167 eth-macro=1 lane=3 max-speed=50g active=false +init set port-map unit=0 port=168 eth-macro=0 lane=4 max-speed=100g active=true +init set port-map unit=0 port=169 eth-macro=0 lane=5 max-speed=50g active=false +init set port-map unit=0 port=170 eth-macro=0 lane=6 max-speed=100g active=false +init set port-map unit=0 port=171 eth-macro=0 lane=7 max-speed=50g active=false +init set port-map unit=0 port=172 eth-macro=1 lane=4 max-speed=100g active=true +init set port-map unit=0 port=173 eth-macro=1 lane=5 max-speed=50g active=false +init set port-map unit=0 port=174 eth-macro=1 lane=6 max-speed=100g active=false +init set port-map unit=0 port=175 eth-macro=1 lane=7 max-speed=50g active=false +init set port-map unit=0 port=176 eth-macro=2 lane=0 max-speed=100g active=true +init set port-map unit=0 port=177 eth-macro=2 lane=1 max-speed=50g active=false +init set port-map unit=0 port=178 eth-macro=2 lane=2 max-speed=100g active=false +init set port-map unit=0 port=179 eth-macro=2 lane=3 max-speed=50g active=false +init set port-map unit=0 port=180 eth-macro=3 lane=0 max-speed=100g active=true +init set port-map unit=0 port=181 eth-macro=3 lane=1 max-speed=50g active=false +init set port-map unit=0 port=182 eth-macro=3 lane=2 max-speed=100g active=false +init set port-map unit=0 port=183 eth-macro=3 lane=3 max-speed=50g active=false +init set port-map unit=0 port=184 eth-macro=2 lane=4 max-speed=100g active=true +init set port-map unit=0 port=185 eth-macro=2 lane=5 max-speed=50g active=false +init set port-map unit=0 port=186 eth-macro=2 lane=6 max-speed=100g active=false +init set port-map unit=0 port=187 eth-macro=2 lane=7 max-speed=50g active=false +init set port-map unit=0 port=188 eth-macro=3 lane=4 max-speed=100g active=true +init set port-map unit=0 port=189 eth-macro=3 lane=5 max-speed=50g active=false +init set port-map unit=0 port=190 eth-macro=3 lane=6 max-speed=100g active=false +init set port-map unit=0 port=191 eth-macro=3 lane=7 max-speed=50g active=false +init set port-map unit=0 port=192 eth-macro=20 lane=0 max-speed=400g active=true +init set port-map unit=0 port=193 eth-macro=20 lane=1 max-speed=50g active=false +init set port-map unit=0 port=194 eth-macro=20 lane=2 max-speed=100g active=false +init set port-map unit=0 port=195 eth-macro=20 lane=3 max-speed=50g active=false +init set port-map unit=0 port=196 eth-macro=20 lane=4 max-speed=200g active=false +init set port-map unit=0 port=197 eth-macro=20 lane=5 max-speed=50g active=false +init set port-map unit=0 port=198 eth-macro=20 lane=6 max-speed=100g active=false +init set port-map unit=0 port=199 eth-macro=20 lane=7 max-speed=50g active=false +init set port-map unit=0 port=200 eth-macro=21 lane=0 max-speed=400g active=true +init set port-map unit=0 port=201 eth-macro=21 lane=1 max-speed=50g active=false +init set port-map unit=0 port=202 eth-macro=21 lane=2 max-speed=100g active=false +init set port-map unit=0 port=203 eth-macro=21 lane=3 max-speed=50g active=false +init set port-map unit=0 port=204 eth-macro=21 lane=4 max-speed=200g active=false +init set port-map unit=0 port=205 eth-macro=21 lane=5 max-speed=50g active=false +init set port-map unit=0 port=206 eth-macro=21 lane=6 max-speed=100g active=false +init set port-map unit=0 port=207 eth-macro=21 lane=7 max-speed=50g active=false +init set port-map unit=0 port=208 eth-macro=22 lane=0 max-speed=400g active=true +init set port-map unit=0 port=209 eth-macro=22 lane=1 max-speed=50g active=false +init set port-map unit=0 port=210 eth-macro=22 lane=2 max-speed=100g active=false +init set port-map unit=0 port=211 eth-macro=22 lane=3 max-speed=50g active=false +init set port-map unit=0 port=212 eth-macro=22 lane=4 max-speed=200g active=false +init set port-map unit=0 port=213 eth-macro=22 lane=5 max-speed=50g active=false +init set port-map unit=0 port=214 eth-macro=22 lane=6 max-speed=100g active=false +init set port-map unit=0 port=215 eth-macro=22 lane=7 max-speed=50g active=false +init set port-map unit=0 port=216 eth-macro=23 lane=0 max-speed=400g active=true +init set port-map unit=0 port=217 eth-macro=23 lane=1 max-speed=50g active=false +init set port-map unit=0 port=218 eth-macro=23 lane=2 max-speed=100g active=false +init set port-map unit=0 port=219 eth-macro=23 lane=3 max-speed=50g active=false +init set port-map unit=0 port=220 eth-macro=23 lane=4 max-speed=200g active=false +init set port-map unit=0 port=221 eth-macro=23 lane=5 max-speed=50g active=false +init set port-map unit=0 port=222 eth-macro=23 lane=6 max-speed=100g active=false +init set port-map unit=0 port=223 eth-macro=23 lane=7 max-speed=50g active=false +init set port-map unit=0 port=224 eth-macro=24 lane=0 max-speed=400g active=true +init set port-map unit=0 port=225 eth-macro=24 lane=1 max-speed=50g active=false +init set port-map unit=0 port=226 eth-macro=24 lane=2 max-speed=100g active=false +init set port-map unit=0 port=227 eth-macro=24 lane=3 max-speed=50g active=false +init set port-map unit=0 port=228 eth-macro=24 lane=4 max-speed=200g active=false +init set port-map unit=0 port=229 eth-macro=24 lane=5 max-speed=50g active=false +init set port-map unit=0 port=230 eth-macro=24 lane=6 max-speed=100g active=false +init set port-map unit=0 port=231 eth-macro=24 lane=7 max-speed=50g active=false +init set port-map unit=0 port=232 eth-macro=25 lane=0 max-speed=400g active=true +init set port-map unit=0 port=233 eth-macro=25 lane=1 max-speed=50g active=false +init set port-map unit=0 port=234 eth-macro=25 lane=2 max-speed=100g active=false +init set port-map unit=0 port=235 eth-macro=25 lane=3 max-speed=50g active=false +init set port-map unit=0 port=236 eth-macro=25 lane=4 max-speed=200g active=false +init set port-map unit=0 port=237 eth-macro=25 lane=5 max-speed=50g active=false +init set port-map unit=0 port=238 eth-macro=25 lane=6 max-speed=100g active=false +init set port-map unit=0 port=239 eth-macro=25 lane=7 max-speed=50g active=false +init set port-map unit=0 port=240 eth-macro=26 lane=0 max-speed=400g active=true +init set port-map unit=0 port=241 eth-macro=26 lane=1 max-speed=50g active=false +init set port-map unit=0 port=242 eth-macro=26 lane=2 max-speed=100g active=false +init set port-map unit=0 port=243 eth-macro=26 lane=3 max-speed=50g active=false +init set port-map unit=0 port=244 eth-macro=26 lane=4 max-speed=200g active=false +init set port-map unit=0 port=245 eth-macro=26 lane=5 max-speed=50g active=false +init set port-map unit=0 port=246 eth-macro=26 lane=6 max-speed=100g active=false +init set port-map unit=0 port=247 eth-macro=26 lane=7 max-speed=50g active=false +init set port-map unit=0 port=248 eth-macro=27 lane=0 max-speed=400g active=true +init set port-map unit=0 port=249 eth-macro=27 lane=1 max-speed=50g active=false +init set port-map unit=0 port=250 eth-macro=27 lane=2 max-speed=100g active=false +init set port-map unit=0 port=251 eth-macro=27 lane=3 max-speed=50g active=false +init set port-map unit=0 port=252 eth-macro=27 lane=4 max-speed=200g active=false +init set port-map unit=0 port=253 eth-macro=27 lane=5 max-speed=50g active=false +init set port-map unit=0 port=254 eth-macro=27 lane=6 max-speed=100g active=false +init set port-map unit=0 port=255 eth-macro=27 lane=7 max-speed=50g active=false +init set port-map unit=0 port=257 eth-macro=32 lane=0 max-speed=10g active=true guarantee=true cpi=true +init set port-map unit=0 port=258 eth-macro=32 lane=1 max-speed=10g active=true guarantee=true cpi=true init-done=true +init start stage unit=0 task-rsrc +init start stage unit=0 module +init start stage unit=0 task + +phy set lane-swap unit=0 portlist=0 lane-cnt=4 property=tx data=0x7.5.4.6 +phy set lane-swap unit=0 portlist=4 lane-cnt=4 property=tx data=0x7.4.6.5 +phy set lane-swap unit=0 portlist=8 lane-cnt=4 property=tx data=0x3.0.1.2 +phy set lane-swap unit=0 portlist=12 lane-cnt=4 property=tx data=0x2.1.3.0 +phy set lane-swap unit=0 portlist=16 lane-cnt=4 property=tx data=0x6.4.5.7 +phy set lane-swap unit=0 portlist=20 lane-cnt=4 property=tx data=0x7.4.6.5 +phy set lane-swap unit=0 portlist=24 lane-cnt=4 property=tx data=0x2.0.3.1 +phy set lane-swap unit=0 portlist=28 lane-cnt=4 property=tx data=0x3.0.1.2 +phy set lane-swap unit=0 portlist=32 lane-cnt=4 property=tx data=0x3.2.0.1 +phy set lane-swap unit=0 portlist=36 lane-cnt=4 property=tx data=0x2.0.3.1 +phy set lane-swap unit=0 portlist=40 lane-cnt=4 property=tx data=0x7.4.6.5 +phy set lane-swap unit=0 portlist=44 lane-cnt=4 property=tx data=0x6.4.5.7 +phy set lane-swap unit=0 portlist=48 lane-cnt=4 property=tx data=0x2.1.3.0 +phy set lane-swap unit=0 portlist=52 lane-cnt=4 property=tx data=0x3.0.1.2 +phy set lane-swap unit=0 portlist=56 lane-cnt=4 property=tx data=0x7.4.6.5 +phy set lane-swap unit=0 portlist=60 lane-cnt=4 property=tx data=0x7.5.4.6 +phy set lane-swap unit=0 portlist=64 lane-cnt=4 property=tx data=0x7.4.5.6 +phy set lane-swap unit=0 portlist=68 lane-cnt=4 property=tx data=0x7.5.4.6 +phy set lane-swap unit=0 portlist=72 lane-cnt=4 property=tx data=0x3.1.0.2 +phy set lane-swap unit=0 portlist=76 lane-cnt=4 property=tx data=0x2.0.3.1 +phy set lane-swap unit=0 portlist=80 lane-cnt=4 property=tx data=0x5.4.6.7 +phy set lane-swap unit=0 portlist=84 lane-cnt=4 property=tx data=0x7.5.4.6 +phy set lane-swap unit=0 portlist=88 lane-cnt=4 property=tx data=0x3.0.2.1 +phy set lane-swap unit=0 portlist=92 lane-cnt=4 property=tx data=0x3.1.0.2 +phy set lane-swap unit=0 portlist=96 lane-cnt=4 property=tx data=0x3.1.2.0 +phy set lane-swap unit=0 portlist=100 lane-cnt=4 property=tx data=0x3.0.1.2 +phy set lane-swap unit=0 portlist=104 lane-cnt=4 property=tx data=0x6.5.7.4 +phy set lane-swap unit=0 portlist=108 lane-cnt=4 property=tx data=0x6.4.7.5 +phy set lane-swap unit=0 portlist=112 lane-cnt=4 property=tx data=0x1.0.3.2 +phy set lane-swap unit=0 portlist=116 lane-cnt=4 property=tx data=0x1.0.3.2 +phy set lane-swap unit=0 portlist=120 lane-cnt=4 property=tx data=0x6.4.5.7 +phy set lane-swap unit=0 portlist=124 lane-cnt=4 property=tx data=0x7.4.6.5 +phy set lane-swap unit=0 portlist=128 lane-cnt=4 property=tx data=0x7.6.5.4 +phy set lane-swap unit=0 portlist=132 lane-cnt=4 property=tx data=0x7.4.6.5 +phy set lane-swap unit=0 portlist=136 lane-cnt=4 property=tx data=0x3.1.0.2 +phy set lane-swap unit=0 portlist=140 lane-cnt=4 property=tx data=0x2.0.1.3 +phy set lane-swap unit=0 portlist=144 lane-cnt=4 property=tx data=0x7.5.4.6 +phy set lane-swap unit=0 portlist=148 lane-cnt=4 property=tx data=0x7.4.6.5 +phy set lane-swap unit=0 portlist=152 lane-cnt=4 property=tx data=0x3.1.0.2 +phy set lane-swap unit=0 portlist=156 lane-cnt=4 property=tx data=0x2.1.0.3 +phy set lane-swap unit=0 portlist=160 lane-cnt=4 property=tx data=0x2.1.0.3 +phy set lane-swap unit=0 portlist=164 lane-cnt=4 property=tx data=0x3.1.0.2 +phy set lane-swap unit=0 portlist=168 lane-cnt=4 property=tx data=0x7.4.6.5 +phy set lane-swap unit=0 portlist=172 lane-cnt=4 property=tx data=0x7.5.4.6 +phy set lane-swap unit=0 portlist=176 lane-cnt=4 property=tx data=0x2.0.1.3 +phy set lane-swap unit=0 portlist=180 lane-cnt=4 property=tx data=0x1.0.3.2 +phy set lane-swap unit=0 portlist=184 lane-cnt=4 property=tx data=0x5.4.7.6 +phy set lane-swap unit=0 portlist=188 lane-cnt=4 property=tx data=0x7.6.5.4 +phy set lane-swap unit=0 portlist=192 lane-cnt=8 property=tx data=0x7.6.4.5.1.3.2.0 +phy set lane-swap unit=0 portlist=200 lane-cnt=8 property=tx data=0x6.7.4.5.3.0.1.2 +phy set lane-swap unit=0 portlist=208 lane-cnt=8 property=tx data=0x6.7.4.5.3.2.0.1 +phy set lane-swap unit=0 portlist=216 lane-cnt=8 property=tx data=0x5.4.6.7.3.2.1.0 +phy set lane-swap unit=0 portlist=224 lane-cnt=8 property=tx data=0x7.6.5.4.0.3.1.2 +phy set lane-swap unit=0 portlist=232 lane-cnt=8 property=tx data=0x5.7.4.6.0.1.3.2 +phy set lane-swap unit=0 portlist=240 lane-cnt=8 property=tx data=0x5.6.4.7.2.3.0.1 +phy set lane-swap unit=0 portlist=248 lane-cnt=8 property=tx data=0x4.5.7.6.3.2.1.0 +phy set lane-swap unit=0 portlist=258 lane-cnt=1 property=tx data=0x1 +phy set lane-swap unit=0 portlist=0 lane-cnt=4 property=rx data=0x7.6.5.4 +phy set lane-swap unit=0 portlist=4 lane-cnt=4 property=rx data=0x5.4.6.7 +phy set lane-swap unit=0 portlist=8 lane-cnt=4 property=rx data=0x1.0.3.2 +phy set lane-swap unit=0 portlist=12 lane-cnt=4 property=rx data=0x1.0.3.2 +phy set lane-swap unit=0 portlist=16 lane-cnt=4 property=rx data=0x5.4.7.6 +phy set lane-swap unit=0 portlist=20 lane-cnt=4 property=rx data=0x5.4.6.7 +phy set lane-swap unit=0 portlist=24 lane-cnt=4 property=rx data=0x3.2.0.1 +phy set lane-swap unit=0 portlist=28 lane-cnt=4 property=rx data=0x3.2.0.1 +phy set lane-swap unit=0 portlist=32 lane-cnt=4 property=rx data=0x3.2.0.1 +phy set lane-swap unit=0 portlist=36 lane-cnt=4 property=rx data=0x3.2.1.0 +phy set lane-swap unit=0 portlist=40 lane-cnt=4 property=rx data=0x5.4.6.7 +phy set lane-swap unit=0 portlist=44 lane-cnt=4 property=rx data=0x5.4.7.6 +phy set lane-swap unit=0 portlist=48 lane-cnt=4 property=rx data=0x1.0.3.2 +phy set lane-swap unit=0 portlist=52 lane-cnt=4 property=rx data=0x1.0.3.2 +phy set lane-swap unit=0 portlist=56 lane-cnt=4 property=rx data=0x5.4.6.7 +phy set lane-swap unit=0 portlist=60 lane-cnt=4 property=rx data=0x7.6.5.4 +phy set lane-swap unit=0 portlist=64 lane-cnt=4 property=rx data=0x5.4.7.6 +phy set lane-swap unit=0 portlist=68 lane-cnt=4 property=rx data=0x5.4.6.7 +phy set lane-swap unit=0 portlist=72 lane-cnt=4 property=rx data=0x1.0.2.3 +phy set lane-swap unit=0 portlist=76 lane-cnt=4 property=rx data=0x1.0.3.2 +phy set lane-swap unit=0 portlist=80 lane-cnt=4 property=rx data=0x5.4.7.6 +phy set lane-swap unit=0 portlist=84 lane-cnt=4 property=rx data=0x6.5.4.7 +phy set lane-swap unit=0 portlist=88 lane-cnt=4 property=rx data=0x2.1.0.3 +phy set lane-swap unit=0 portlist=92 lane-cnt=4 property=rx data=0x1.0.3.2 +phy set lane-swap unit=0 portlist=96 lane-cnt=4 property=rx data=0x3.2.0.1 +phy set lane-swap unit=0 portlist=100 lane-cnt=4 property=rx data=0x3.2.1.0 +phy set lane-swap unit=0 portlist=104 lane-cnt=4 property=rx data=0x5.4.6.7 +phy set lane-swap unit=0 portlist=108 lane-cnt=4 property=rx data=0x5.4.7.6 +phy set lane-swap unit=0 portlist=112 lane-cnt=4 property=rx data=0x1.0.3.2 +phy set lane-swap unit=0 portlist=116 lane-cnt=4 property=rx data=0x1.0.2.3 +phy set lane-swap unit=0 portlist=120 lane-cnt=4 property=rx data=0x5.4.6.7 +phy set lane-swap unit=0 portlist=124 lane-cnt=4 property=rx data=0x5.4.7.6 +phy set lane-swap unit=0 portlist=128 lane-cnt=4 property=rx data=0x7.6.4.5 +phy set lane-swap unit=0 portlist=132 lane-cnt=4 property=rx data=0x7.6.5.4 +phy set lane-swap unit=0 portlist=136 lane-cnt=4 property=rx data=0x3.2.1.0 +phy set lane-swap unit=0 portlist=140 lane-cnt=4 property=rx data=0x1.0.3.2 +phy set lane-swap unit=0 portlist=144 lane-cnt=4 property=rx data=0x5.4.7.6 +phy set lane-swap unit=0 portlist=148 lane-cnt=4 property=rx data=0x5.4.6.7 +phy set lane-swap unit=0 portlist=152 lane-cnt=4 property=rx data=0x3.2.1.0 +phy set lane-swap unit=0 portlist=156 lane-cnt=4 property=rx data=0x3.2.0.1 +phy set lane-swap unit=0 portlist=160 lane-cnt=4 property=rx data=0x3.2.0.1 +phy set lane-swap unit=0 portlist=164 lane-cnt=4 property=rx data=0x3.2.1.0 +phy set lane-swap unit=0 portlist=168 lane-cnt=4 property=rx data=0x5.4.6.7 +phy set lane-swap unit=0 portlist=172 lane-cnt=4 property=rx data=0x5.4.7.6 +phy set lane-swap unit=0 portlist=176 lane-cnt=4 property=rx data=0x1.0.2.3 +phy set lane-swap unit=0 portlist=180 lane-cnt=4 property=rx data=0x3.2.1.0 +phy set lane-swap unit=0 portlist=184 lane-cnt=4 property=rx data=0x7.6.4.5 +phy set lane-swap unit=0 portlist=188 lane-cnt=4 property=rx data=0x7.6.5.4 +phy set lane-swap unit=0 portlist=192 lane-cnt=8 property=rx data=0x5.4.7.6.1.0.3.2 +phy set lane-swap unit=0 portlist=200 lane-cnt=8 property=rx data=0x6.7.5.4.2.0.3.1 +phy set lane-swap unit=0 portlist=208 lane-cnt=8 property=rx data=0x6.4.7.5.2.0.3.1 +phy set lane-swap unit=0 portlist=216 lane-cnt=8 property=rx data=0x6.7.5.4.1.3.0.2 +phy set lane-swap unit=0 portlist=224 lane-cnt=8 property=rx data=0x4.6.7.5.2.1.3.0 +phy set lane-swap unit=0 portlist=232 lane-cnt=8 property=rx data=0x4.5.6.7.3.1.0.2 +phy set lane-swap unit=0 portlist=240 lane-cnt=8 property=rx data=0x6.7.5.4.3.0.2.1 +phy set lane-swap unit=0 portlist=248 lane-cnt=8 property=rx data=0x5.4.6.7.3.1.2.0 +phy set lane-swap unit=0 portlist=258 lane-cnt=1 property=rx data=0x1 +phy set polarity-rev unit=0 portlist=0 lane-cnt=4 property=tx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=4 lane-cnt=4 property=tx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=8 lane-cnt=4 property=tx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=12 lane-cnt=4 property=tx data=0x0.0.0.0 +phy set polarity-rev unit=0 portlist=16 lane-cnt=4 property=tx data=0x0.0.1.1 +phy set polarity-rev unit=0 portlist=20 lane-cnt=4 property=tx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=24 lane-cnt=4 property=tx data=0x0.0.0.0 +phy set polarity-rev unit=0 portlist=28 lane-cnt=4 property=tx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=32 lane-cnt=4 property=tx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=36 lane-cnt=4 property=tx data=0x0.0.0.0 +phy set polarity-rev unit=0 portlist=40 lane-cnt=4 property=tx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=44 lane-cnt=4 property=tx data=0x0.0.1.1 +phy set polarity-rev unit=0 portlist=48 lane-cnt=4 property=tx data=0x0.0.0.0 +phy set polarity-rev unit=0 portlist=52 lane-cnt=4 property=tx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=56 lane-cnt=4 property=tx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=60 lane-cnt=4 property=tx data=0x0.0.0.0 +phy set polarity-rev unit=0 portlist=64 lane-cnt=4 property=tx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=68 lane-cnt=4 property=tx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=72 lane-cnt=4 property=tx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=76 lane-cnt=4 property=tx data=0x0.0.0.0 +phy set polarity-rev unit=0 portlist=80 lane-cnt=4 property=tx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=84 lane-cnt=4 property=tx data=0x0.0.1.1 +phy set polarity-rev unit=0 portlist=88 lane-cnt=4 property=tx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=92 lane-cnt=4 property=tx data=0x0.0.0.0 +phy set polarity-rev unit=0 portlist=96 lane-cnt=4 property=tx data=0x0.0.0.0 +phy set polarity-rev unit=0 portlist=100 lane-cnt=4 property=tx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=104 lane-cnt=4 property=tx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=108 lane-cnt=4 property=tx data=0x0.0.1.1 +phy set polarity-rev unit=0 portlist=112 lane-cnt=4 property=tx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=116 lane-cnt=4 property=tx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=120 lane-cnt=4 property=tx data=0x0.0.0.0 +phy set polarity-rev unit=0 portlist=124 lane-cnt=4 property=tx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=128 lane-cnt=4 property=tx data=0x0.0.1.1 +phy set polarity-rev unit=0 portlist=132 lane-cnt=4 property=tx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=136 lane-cnt=4 property=tx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=140 lane-cnt=4 property=tx data=0x0.0.0.0 +phy set polarity-rev unit=0 portlist=144 lane-cnt=4 property=tx data=0x0.0.0.0 +phy set polarity-rev unit=0 portlist=148 lane-cnt=4 property=tx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=152 lane-cnt=4 property=tx data=0x0.0.1.1 +phy set polarity-rev unit=0 portlist=156 lane-cnt=4 property=tx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=160 lane-cnt=4 property=tx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=164 lane-cnt=4 property=tx data=0x0.0.1.1 +phy set polarity-rev unit=0 portlist=168 lane-cnt=4 property=tx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=172 lane-cnt=4 property=tx data=0x0.0.0.0 +phy set polarity-rev unit=0 portlist=176 lane-cnt=4 property=tx data=0x0.0.0.0 +phy set polarity-rev unit=0 portlist=180 lane-cnt=4 property=tx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=184 lane-cnt=4 property=tx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=188 lane-cnt=4 property=tx data=0x0.0.1.1 +phy set polarity-rev unit=0 portlist=192 lane-cnt=8 property=tx data=0x1.0.1.1.1.1.0.1 +phy set polarity-rev unit=0 portlist=200 lane-cnt=8 property=tx data=0x0.0.0.0.0.0.0.1 +phy set polarity-rev unit=0 portlist=208 lane-cnt=8 property=tx data=0x0.1.0.1.1.1.1.0 +phy set polarity-rev unit=0 portlist=216 lane-cnt=8 property=tx data=0x0.1.1.0.1.0.1.0 +phy set polarity-rev unit=0 portlist=224 lane-cnt=8 property=tx data=0x1.1.0.1.0.1.1.0 +phy set polarity-rev unit=0 portlist=232 lane-cnt=8 property=tx data=0x1.1.0.0.0.0.0.1 +phy set polarity-rev unit=0 portlist=240 lane-cnt=8 property=tx data=0x0.1.0.0.0.0.0.0 +phy set polarity-rev unit=0 portlist=248 lane-cnt=8 property=tx data=0x1.1.0.0.0.0.1.1 +phy set polarity-rev unit=0 portlist=258 lane-cnt=1 property=tx data=0x0 +phy set polarity-rev unit=0 portlist=0 lane-cnt=4 property=rx data=0x0.0.0.0 +phy set polarity-rev unit=0 portlist=4 lane-cnt=4 property=rx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=8 lane-cnt=4 property=rx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=12 lane-cnt=4 property=rx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=16 lane-cnt=4 property=rx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=20 lane-cnt=4 property=rx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=24 lane-cnt=4 property=rx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=28 lane-cnt=4 property=rx data=0x0.0.1.1 +phy set polarity-rev unit=0 portlist=32 lane-cnt=4 property=rx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=36 lane-cnt=4 property=rx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=40 lane-cnt=4 property=rx data=0x0.0.0.0 +phy set polarity-rev unit=0 portlist=44 lane-cnt=4 property=rx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=48 lane-cnt=4 property=rx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=52 lane-cnt=4 property=rx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=56 lane-cnt=4 property=rx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=60 lane-cnt=4 property=rx data=0x0.0.0.0 +phy set polarity-rev unit=0 portlist=64 lane-cnt=4 property=rx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=68 lane-cnt=4 property=rx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=72 lane-cnt=4 property=rx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=76 lane-cnt=4 property=rx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=80 lane-cnt=4 property=rx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=84 lane-cnt=4 property=rx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=88 lane-cnt=4 property=rx data=0x0.0.1.1 +phy set polarity-rev unit=0 portlist=92 lane-cnt=4 property=rx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=96 lane-cnt=4 property=rx data=0x0.0.1.1 +phy set polarity-rev unit=0 portlist=100 lane-cnt=4 property=rx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=104 lane-cnt=4 property=rx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=108 lane-cnt=4 property=rx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=112 lane-cnt=4 property=rx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=116 lane-cnt=4 property=rx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=120 lane-cnt=4 property=rx data=0x0.0.1.1 +phy set polarity-rev unit=0 portlist=124 lane-cnt=4 property=rx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=128 lane-cnt=4 property=rx data=0x0.0.0.0 +phy set polarity-rev unit=0 portlist=132 lane-cnt=4 property=rx data=0x0.0.1.1 +phy set polarity-rev unit=0 portlist=136 lane-cnt=4 property=rx data=0x0.0.0.0 +phy set polarity-rev unit=0 portlist=140 lane-cnt=4 property=rx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=144 lane-cnt=4 property=rx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=148 lane-cnt=4 property=rx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=152 lane-cnt=4 property=rx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=156 lane-cnt=4 property=rx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=160 lane-cnt=4 property=rx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=164 lane-cnt=4 property=rx data=0x0.0.1.0 +phy set polarity-rev unit=0 portlist=168 lane-cnt=4 property=rx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=172 lane-cnt=4 property=rx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=176 lane-cnt=4 property=rx data=0x0.0.0.1 +phy set polarity-rev unit=0 portlist=180 lane-cnt=4 property=rx data=0x0.0.1.1 +phy set polarity-rev unit=0 portlist=184 lane-cnt=4 property=rx data=0x0.0.0.0 +phy set polarity-rev unit=0 portlist=188 lane-cnt=4 property=rx data=0x0.0.0.0 +phy set polarity-rev unit=0 portlist=192 lane-cnt=8 property=rx data=0x0.0.0.1.0.1.0.1 +phy set polarity-rev unit=0 portlist=200 lane-cnt=8 property=rx data=0x0.1.1.1.0.1.1.1 +phy set polarity-rev unit=0 portlist=208 lane-cnt=8 property=rx data=0x1.1.0.0.0.0.1.1 +phy set polarity-rev unit=0 portlist=216 lane-cnt=8 property=rx data=0x0.1.1.0.1.0.1.0 +phy set polarity-rev unit=0 portlist=224 lane-cnt=8 property=rx data=0x0.0.1.1.1.1.1.0 +phy set polarity-rev unit=0 portlist=232 lane-cnt=8 property=rx data=0x1.0.1.0.1.0.1.1 +phy set polarity-rev unit=0 portlist=240 lane-cnt=8 property=rx data=0x0.1.1.1.1.1.0.1 +phy set polarity-rev unit=0 portlist=248 lane-cnt=8 property=rx data=0x0.0.1.0.0.0.1.0 +phy set polarity-rev unit=0 portlist=258 lane-cnt=1 property=rx data=0x0 +phy set pre-emphasis unit=0 portlist=0 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=0 lane-cnt=4 property=cn1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=0 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=0 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=0 lane-cnt=4 property=c0 data=0x2a.29.2a.2a +phy set pre-emphasis unit=0 portlist=4 lane-cnt=4 property=cn2 data=0x02.01.01.01 +phy set pre-emphasis unit=0 portlist=4 lane-cnt=4 property=cn1 data=0xf6.f6.f6.f7 +phy set pre-emphasis unit=0 portlist=4 lane-cnt=4 property=c1 data=0xf5.f6.f5.f6 +phy set pre-emphasis unit=0 portlist=4 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=4 lane-cnt=4 property=c0 data=0x29.27.28.29 +phy set pre-emphasis unit=0 portlist=8 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=8 lane-cnt=4 property=cn1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=8 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=8 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=8 lane-cnt=4 property=c0 data=0x2a.2a.2a.29 +phy set pre-emphasis unit=0 portlist=12 lane-cnt=4 property=cn2 data=0x02.02.02.02 +phy set pre-emphasis unit=0 portlist=12 lane-cnt=4 property=cn1 data=0xf7.f7.f7.f6 +phy set pre-emphasis unit=0 portlist=12 lane-cnt=4 property=c1 data=0xf6.f6.f6.f5 +phy set pre-emphasis unit=0 portlist=12 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=12 lane-cnt=4 property=c0 data=0x2a.29.2a.28 +phy set pre-emphasis unit=0 portlist=16 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=16 lane-cnt=4 property=cn1 data=0xf6.f6.f6.f8 +phy set pre-emphasis unit=0 portlist=16 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=16 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=16 lane-cnt=4 property=c0 data=0x29.2a.2a.29 +phy set pre-emphasis unit=0 portlist=20 lane-cnt=4 property=cn2 data=0x02.01.02.01 +phy set pre-emphasis unit=0 portlist=20 lane-cnt=4 property=cn1 data=0xf7.f6.f7.f9 +phy set pre-emphasis unit=0 portlist=20 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=20 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=20 lane-cnt=4 property=c0 data=0x2a.29.2a.29 +phy set pre-emphasis unit=0 portlist=24 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=24 lane-cnt=4 property=cn1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=24 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=24 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=24 lane-cnt=4 property=c0 data=0x29.2a.2a.2a +phy set pre-emphasis unit=0 portlist=28 lane-cnt=4 property=cn2 data=0x02.02.02.02 +phy set pre-emphasis unit=0 portlist=28 lane-cnt=4 property=cn1 data=0xf7.f7.f7.f7 +phy set pre-emphasis unit=0 portlist=28 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=28 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=28 lane-cnt=4 property=c0 data=0x2a.2a.2a.2a +phy set pre-emphasis unit=0 portlist=32 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=32 lane-cnt=4 property=cn1 data=0xf6.f7.f7.f7 +phy set pre-emphasis unit=0 portlist=32 lane-cnt=4 property=c1 data=0xf6.f7.f7.f6 +phy set pre-emphasis unit=0 portlist=32 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=32 lane-cnt=4 property=c0 data=0x2a.2c.2c.29 +phy set pre-emphasis unit=0 portlist=36 lane-cnt=4 property=cn2 data=0x02.02.02.02 +phy set pre-emphasis unit=0 portlist=36 lane-cnt=4 property=cn1 data=0xf7.f6.f7.f7 +phy set pre-emphasis unit=0 portlist=36 lane-cnt=4 property=c1 data=0xf6.f5.f6.f5 +phy set pre-emphasis unit=0 portlist=36 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=36 lane-cnt=4 property=c0 data=0x2a.28.2a.28 +phy set pre-emphasis unit=0 portlist=40 lane-cnt=4 property=cn2 data=0x01.01.01.02 +phy set pre-emphasis unit=0 portlist=40 lane-cnt=4 property=cn1 data=0xf6.f6.f7.f7 +phy set pre-emphasis unit=0 portlist=40 lane-cnt=4 property=c1 data=0xf6.f6.f7.f7 +phy set pre-emphasis unit=0 portlist=40 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=40 lane-cnt=4 property=c0 data=0x2a.2a.2a.2b +phy set pre-emphasis unit=0 portlist=44 lane-cnt=4 property=cn2 data=0x02.02.01.02 +phy set pre-emphasis unit=0 portlist=44 lane-cnt=4 property=cn1 data=0xf7.f7.f7.f7 +phy set pre-emphasis unit=0 portlist=44 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=44 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=44 lane-cnt=4 property=c0 data=0x2a.2a.29.2a +phy set pre-emphasis unit=0 portlist=48 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=48 lane-cnt=4 property=cn1 data=0xf7.f7.f7.f6 +phy set pre-emphasis unit=0 portlist=48 lane-cnt=4 property=c1 data=0xf7.f7.f7.f6 +phy set pre-emphasis unit=0 portlist=48 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=48 lane-cnt=4 property=c0 data=0x2a.2c.2c.2a +phy set pre-emphasis unit=0 portlist=52 lane-cnt=4 property=cn2 data=0x01.02.02.02 +phy set pre-emphasis unit=0 portlist=52 lane-cnt=4 property=cn1 data=0xf8.f7.f8.f7 +phy set pre-emphasis unit=0 portlist=52 lane-cnt=4 property=c1 data=0xf7.f6.f7.f6 +phy set pre-emphasis unit=0 portlist=52 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=52 lane-cnt=4 property=c0 data=0x29.2a.2a.2a +phy set pre-emphasis unit=0 portlist=56 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=56 lane-cnt=4 property=cn1 data=0xf6.f7.f7.f7 +phy set pre-emphasis unit=0 portlist=56 lane-cnt=4 property=c1 data=0xf6.f7.f7.f7 +phy set pre-emphasis unit=0 portlist=56 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=56 lane-cnt=4 property=c0 data=0x29.2c.2b.29 +phy set pre-emphasis unit=0 portlist=60 lane-cnt=4 property=cn2 data=0x01.02.02.02 +phy set pre-emphasis unit=0 portlist=60 lane-cnt=4 property=cn1 data=0xf8.f6.f7.f7 +phy set pre-emphasis unit=0 portlist=60 lane-cnt=4 property=c1 data=0xf7.f5.f6.f6 +phy set pre-emphasis unit=0 portlist=60 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=60 lane-cnt=4 property=c0 data=0x29.28.2a.2a +phy set pre-emphasis unit=0 portlist=64 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=64 lane-cnt=4 property=cn1 data=0xf7.f7.f8.f8 +phy set pre-emphasis unit=0 portlist=64 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=64 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=64 lane-cnt=4 property=c0 data=0x28.28.28.28 +phy set pre-emphasis unit=0 portlist=68 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=68 lane-cnt=4 property=cn1 data=0xf7.f7.f7.f7 +phy set pre-emphasis unit=0 portlist=68 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=68 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=68 lane-cnt=4 property=c0 data=0x29.29.29.29 +phy set pre-emphasis unit=0 portlist=72 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=72 lane-cnt=4 property=cn1 data=0xf7.f8.f7.f7 +phy set pre-emphasis unit=0 portlist=72 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=72 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=72 lane-cnt=4 property=c0 data=0x28.28.28.28 +phy set pre-emphasis unit=0 portlist=76 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=76 lane-cnt=4 property=cn1 data=0xf6.f6.f7.f7 +phy set pre-emphasis unit=0 portlist=76 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=76 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=76 lane-cnt=4 property=c0 data=0x2a.2a.29.29 +phy set pre-emphasis unit=0 portlist=80 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=80 lane-cnt=4 property=cn1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=80 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=80 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=80 lane-cnt=4 property=c0 data=0x2a.2a.2a.2a +phy set pre-emphasis unit=0 portlist=84 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=84 lane-cnt=4 property=cn1 data=0xf6.f7.f6.f6 +phy set pre-emphasis unit=0 portlist=84 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=84 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=84 lane-cnt=4 property=c0 data=0x2a.29.2a.2a +phy set pre-emphasis unit=0 portlist=88 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=88 lane-cnt=4 property=cn1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=88 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=88 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=88 lane-cnt=4 property=c0 data=0x2a.2a.2a.2a +phy set pre-emphasis unit=0 portlist=92 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=92 lane-cnt=4 property=cn1 data=0xf7.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=92 lane-cnt=4 property=c1 data=0xf7.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=92 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=92 lane-cnt=4 property=c0 data=0x29.2a.2a.2a +phy set pre-emphasis unit=0 portlist=96 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=96 lane-cnt=4 property=cn1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=96 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=96 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=96 lane-cnt=4 property=c0 data=0x2a.2a.2a.2a +phy set pre-emphasis unit=0 portlist=100 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=100 lane-cnt=4 property=cn1 data=0xf6.f7.f6.f6 +phy set pre-emphasis unit=0 portlist=100 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=100 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=100 lane-cnt=4 property=c0 data=0x2a.29.2a.2a +phy set pre-emphasis unit=0 portlist=104 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=104 lane-cnt=4 property=cn1 data=0xf6.f6.f7.f7 +phy set pre-emphasis unit=0 portlist=104 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=104 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=104 lane-cnt=4 property=c0 data=0x2a.2a.2a.2a +phy set pre-emphasis unit=0 portlist=108 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=108 lane-cnt=4 property=cn1 data=0xf6.f6.f7.f6 +phy set pre-emphasis unit=0 portlist=108 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=108 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=108 lane-cnt=4 property=c0 data=0x2a.2a.28.2a +phy set pre-emphasis unit=0 portlist=112 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=112 lane-cnt=4 property=cn1 data=0xf6.f7.f7.f7 +phy set pre-emphasis unit=0 portlist=112 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=112 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=112 lane-cnt=4 property=c0 data=0x2a.29.29.29 +phy set pre-emphasis unit=0 portlist=116 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=116 lane-cnt=4 property=cn1 data=0xf8.f6.f6.f8 +phy set pre-emphasis unit=0 portlist=116 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=116 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=116 lane-cnt=4 property=c0 data=0x29.2a.2a.27 +phy set pre-emphasis unit=0 portlist=120 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=120 lane-cnt=4 property=cn1 data=0xf7.f7.f8.f7 +phy set pre-emphasis unit=0 portlist=120 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=120 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=120 lane-cnt=4 property=c0 data=0x2a.29.2a.29 +phy set pre-emphasis unit=0 portlist=124 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=124 lane-cnt=4 property=cn1 data=0xf6.f6.f7.f6 +phy set pre-emphasis unit=0 portlist=124 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=124 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=124 lane-cnt=4 property=c0 data=0x2a.2a.2a.2a +phy set pre-emphasis unit=0 portlist=128 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=128 lane-cnt=4 property=cn1 data=0xf7.f7.f8.f8 +phy set pre-emphasis unit=0 portlist=128 lane-cnt=4 property=c1 data=0xf7.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=128 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=128 lane-cnt=4 property=c0 data=0x2c.2b.2a.2b +phy set pre-emphasis unit=0 portlist=132 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=132 lane-cnt=4 property=cn1 data=0xf8.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=132 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=132 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=132 lane-cnt=4 property=c0 data=0x2b.2a.2a.2a +phy set pre-emphasis unit=0 portlist=136 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=136 lane-cnt=4 property=cn1 data=0xf6.f7.f6.f7 +phy set pre-emphasis unit=0 portlist=136 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=136 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=136 lane-cnt=4 property=c0 data=0x2a.2b.2a.2b +phy set pre-emphasis unit=0 portlist=140 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=140 lane-cnt=4 property=cn1 data=0xf6.f6.f6.f7 +phy set pre-emphasis unit=0 portlist=140 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=140 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=140 lane-cnt=4 property=c0 data=0x2a.2a.2a.2b +phy set pre-emphasis unit=0 portlist=144 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=144 lane-cnt=4 property=cn1 data=0xf7.f8.f6.f7 +phy set pre-emphasis unit=0 portlist=144 lane-cnt=4 property=c1 data=0xf7.f7.f7.f7 +phy set pre-emphasis unit=0 portlist=144 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=144 lane-cnt=4 property=c0 data=0x2c.2c.2b.2c +phy set pre-emphasis unit=0 portlist=148 lane-cnt=4 property=cn2 data=0x02.01.01.01 +phy set pre-emphasis unit=0 portlist=148 lane-cnt=4 property=cn1 data=0xf8.f7.f7.f7 +phy set pre-emphasis unit=0 portlist=148 lane-cnt=4 property=c1 data=0xf6.f7.f6.f6 +phy set pre-emphasis unit=0 portlist=148 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=148 lane-cnt=4 property=c0 data=0x29.2c.2a.2b +phy set pre-emphasis unit=0 portlist=152 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=152 lane-cnt=4 property=cn1 data=0xf7.f6.f7.f6 +phy set pre-emphasis unit=0 portlist=152 lane-cnt=4 property=c1 data=0xf7.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=152 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=152 lane-cnt=4 property=c0 data=0x2c.2a.2a.2a +phy set pre-emphasis unit=0 portlist=156 lane-cnt=4 property=cn2 data=0x02.02.02.02 +phy set pre-emphasis unit=0 portlist=156 lane-cnt=4 property=cn1 data=0xf7.f6.f7.f7 +phy set pre-emphasis unit=0 portlist=156 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=156 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=156 lane-cnt=4 property=c0 data=0x2a.29.2a.2a +phy set pre-emphasis unit=0 portlist=160 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=160 lane-cnt=4 property=cn1 data=0xf7.f7.f7.f6 +phy set pre-emphasis unit=0 portlist=160 lane-cnt=4 property=c1 data=0xf7.f7.f7.f6 +phy set pre-emphasis unit=0 portlist=160 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=160 lane-cnt=4 property=c0 data=0x2c.2c.2c.2a +phy set pre-emphasis unit=0 portlist=164 lane-cnt=4 property=cn2 data=0x02.02.02.02 +phy set pre-emphasis unit=0 portlist=164 lane-cnt=4 property=cn1 data=0xf7.f6.f7.f6 +phy set pre-emphasis unit=0 portlist=164 lane-cnt=4 property=c1 data=0xf6.f5.f6.f5 +phy set pre-emphasis unit=0 portlist=164 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=164 lane-cnt=4 property=c0 data=0x2a.28.2a.28 +phy set pre-emphasis unit=0 portlist=168 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=168 lane-cnt=4 property=cn1 data=0xf6.f6.f6.f7 +phy set pre-emphasis unit=0 portlist=168 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=168 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=168 lane-cnt=4 property=c0 data=0x2a.2a.2a.2a +phy set pre-emphasis unit=0 portlist=172 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=172 lane-cnt=4 property=cn1 data=0xf6.f7.f6.f6 +phy set pre-emphasis unit=0 portlist=172 lane-cnt=4 property=c1 data=0xf6.f6.f5.f5 +phy set pre-emphasis unit=0 portlist=172 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=172 lane-cnt=4 property=c0 data=0x2a.2a.29.29 +phy set pre-emphasis unit=0 portlist=176 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=176 lane-cnt=4 property=cn1 data=0xf7.f6.f6.f7 +phy set pre-emphasis unit=0 portlist=176 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=176 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=176 lane-cnt=4 property=c0 data=0x2b.2a.2a.2b +phy set pre-emphasis unit=0 portlist=180 lane-cnt=4 property=cn2 data=0x02.02.01.02 +phy set pre-emphasis unit=0 portlist=180 lane-cnt=4 property=cn1 data=0xf6.f6.f7.f6 +phy set pre-emphasis unit=0 portlist=180 lane-cnt=4 property=c1 data=0xf5.f6.f6.f5 +phy set pre-emphasis unit=0 portlist=180 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=180 lane-cnt=4 property=c0 data=0x28.29.2a.28 +phy set pre-emphasis unit=0 portlist=184 lane-cnt=4 property=cn2 data=0x01.01.01.01 +phy set pre-emphasis unit=0 portlist=184 lane-cnt=4 property=cn1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=184 lane-cnt=4 property=c1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=184 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=184 lane-cnt=4 property=c0 data=0x2a.2a.2a.2a +phy set pre-emphasis unit=0 portlist=188 lane-cnt=4 property=cn2 data=0x02.02.02.02 +phy set pre-emphasis unit=0 portlist=188 lane-cnt=4 property=cn1 data=0xf6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=188 lane-cnt=4 property=c1 data=0xf4.f4.f5.f4 +phy set pre-emphasis unit=0 portlist=188 lane-cnt=4 property=c2 data=0x00.00.00.00 +phy set pre-emphasis unit=0 portlist=188 lane-cnt=4 property=c0 data=0x25.27.28.23 +phy set pre-emphasis unit=0 portlist=192 lane-cnt=8 property=cn2 data=0x01.01.01.01.01.01.01.01 +phy set pre-emphasis unit=0 portlist=192 lane-cnt=8 property=cn1 data=0xf7.f7.f7.f7.f7.f7.f7.f7 +phy set pre-emphasis unit=0 portlist=192 lane-cnt=8 property=c1 data=0xf7.f7.f7.f7.f7.f7.f7.f7 +phy set pre-emphasis unit=0 portlist=192 lane-cnt=8 property=c2 data=0x00.00.00.00.00.00.00.00 +phy set pre-emphasis unit=0 portlist=192 lane-cnt=8 property=c0 data=0x2c.2c.2b.2b.2c.2c.2c.2c +phy set pre-emphasis unit=0 portlist=200 lane-cnt=8 property=cn2 data=0x01.01.01.01.01.01.01.01 +phy set pre-emphasis unit=0 portlist=200 lane-cnt=8 property=cn1 data=0xf7.f6.f7.f7.f7.f7.f7.f6 +phy set pre-emphasis unit=0 portlist=200 lane-cnt=8 property=c1 data=0xf7.f6.f7.f7.f7.f7.f7.f6 +phy set pre-emphasis unit=0 portlist=200 lane-cnt=8 property=c2 data=0x00.00.00.00.00.00.00.00 +phy set pre-emphasis unit=0 portlist=200 lane-cnt=8 property=c0 data=0x2c.2a.2c.2c.2c.2c.2c.2a +phy set pre-emphasis unit=0 portlist=208 lane-cnt=8 property=cn2 data=0x01.01.01.01.01.01.01.01 +phy set pre-emphasis unit=0 portlist=208 lane-cnt=8 property=cn1 data=0xf7.f7.f7.f6.f7.f7.f7.f7 +phy set pre-emphasis unit=0 portlist=208 lane-cnt=8 property=c1 data=0xf7.f6.f7.f6.f7.f6.f7.f6 +phy set pre-emphasis unit=0 portlist=208 lane-cnt=8 property=c2 data=0x00.00.00.00.00.00.00.00 +phy set pre-emphasis unit=0 portlist=208 lane-cnt=8 property=c0 data=0x2c.2b.2c.2a.2b.2b.2c.2b +phy set pre-emphasis unit=0 portlist=216 lane-cnt=8 property=cn2 data=0x01.01.01.01.01.01.01.01 +phy set pre-emphasis unit=0 portlist=216 lane-cnt=8 property=cn1 data=0xf7.f7.f7.f7.f7.f7.f7.f7 +phy set pre-emphasis unit=0 portlist=216 lane-cnt=8 property=c1 data=0xf6.f6.f7.f6.f6.f6.f7.f6 +phy set pre-emphasis unit=0 portlist=216 lane-cnt=8 property=c2 data=0x00.00.00.00.00.00.00.00 +phy set pre-emphasis unit=0 portlist=216 lane-cnt=8 property=c0 data=0x2b.2b.2c.2b.2b.2b.2b.2b +phy set pre-emphasis unit=0 portlist=224 lane-cnt=8 property=cn2 data=0x01.01.01.01.01.01.01.01 +phy set pre-emphasis unit=0 portlist=224 lane-cnt=8 property=cn1 data=0xf7.f7.f7.f7.f7.f7.f7.f7 +phy set pre-emphasis unit=0 portlist=224 lane-cnt=8 property=c1 data=0xf6.f6.f6.f6.f7.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=224 lane-cnt=8 property=c2 data=0x00.00.00.00.00.00.00.00 +phy set pre-emphasis unit=0 portlist=224 lane-cnt=8 property=c0 data=0x2b.2b.2b.2b.2a.2a.2a.2b +phy set pre-emphasis unit=0 portlist=232 lane-cnt=8 property=cn2 data=0x01.01.01.01.01.01.01.01 +phy set pre-emphasis unit=0 portlist=232 lane-cnt=8 property=cn1 data=0xf7.f7.f7.f7.f8.f7.f7.f6 +phy set pre-emphasis unit=0 portlist=232 lane-cnt=8 property=c1 data=0xf6.f6.f6.f6.f6.f6.f6.f7 +phy set pre-emphasis unit=0 portlist=232 lane-cnt=8 property=c2 data=0x00.00.00.00.00.00.00.00 +phy set pre-emphasis unit=0 portlist=232 lane-cnt=8 property=c0 data=0x2a.2a.2a.2b.28.2b.2b.2b +phy set pre-emphasis unit=0 portlist=240 lane-cnt=8 property=cn2 data=0x01.02.01.01.01.01.01.01 +phy set pre-emphasis unit=0 portlist=240 lane-cnt=8 property=cn1 data=0xf6.f5.f6.f7.f6.f7.f8.f7 +phy set pre-emphasis unit=0 portlist=240 lane-cnt=8 property=c1 data=0xf6.f6.f6.f6.f6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=240 lane-cnt=8 property=c2 data=0x00.00.00.00.00.00.00.00 +phy set pre-emphasis unit=0 portlist=240 lane-cnt=8 property=c0 data=0x2a.28.2a.2a.2a.2a.2b.2a +phy set pre-emphasis unit=0 portlist=248 lane-cnt=8 property=cn2 data=0x01.01.01.01.01.01.01.01 +phy set pre-emphasis unit=0 portlist=248 lane-cnt=8 property=cn1 data=0xf6.f6.f6.f7.f6.f7.f6.f7 +phy set pre-emphasis unit=0 portlist=248 lane-cnt=8 property=c1 data=0xf6.f6.f6.f6.f6.f6.f6.f6 +phy set pre-emphasis unit=0 portlist=248 lane-cnt=8 property=c2 data=0x00.00.00.00.00.00.00.00 +phy set pre-emphasis unit=0 portlist=248 lane-cnt=8 property=c0 data=0x2a.2a.2a.2a.2a.2b.2a.2a +phy set pre-emphasis unit=0 portlist=257 lane-cnt=1 property=cn2 data=0x0 +phy set pre-emphasis unit=0 portlist=257 lane-cnt=1 property=cn1 data=0x0 +phy set pre-emphasis unit=0 portlist=257 lane-cnt=1 property=c1 data=0xfa +phy set pre-emphasis unit=0 portlist=257 lane-cnt=1 property=c2 data=0x0 +phy set pre-emphasis unit=0 portlist=257 lane-cnt=1 property=c0 data=0x24 +phy set pre-emphasis unit=0 portlist=258 lane-cnt=1 property=cn2 data=0x0 +phy set pre-emphasis unit=0 portlist=258 lane-cnt=1 property=cn1 data=0x0 +phy set pre-emphasis unit=0 portlist=258 lane-cnt=1 property=c1 data=0xfa +phy set pre-emphasis unit=0 portlist=258 lane-cnt=1 property=c2 data=0x0 +phy set pre-emphasis unit=0 portlist=258 lane-cnt=1 property=c0 data=0x24 +port set property unit=0 portlist=0 speed=100g +port set property unit=0 portlist=4 speed=100g +port set property unit=0 portlist=8 speed=100g +port set property unit=0 portlist=12 speed=100g +port set property unit=0 portlist=16 speed=100g +port set property unit=0 portlist=20 speed=100g +port set property unit=0 portlist=24 speed=100g +port set property unit=0 portlist=28 speed=100g +port set property unit=0 portlist=32 speed=100g +port set property unit=0 portlist=36 speed=100g +port set property unit=0 portlist=40 speed=100g +port set property unit=0 portlist=44 speed=100g +port set property unit=0 portlist=48 speed=100g +port set property unit=0 portlist=52 speed=100g +port set property unit=0 portlist=56 speed=100g +port set property unit=0 portlist=60 speed=100g +port set property unit=0 portlist=64 speed=100g +port set property unit=0 portlist=68 speed=100g +port set property unit=0 portlist=72 speed=100g +port set property unit=0 portlist=76 speed=100g +port set property unit=0 portlist=80 speed=100g +port set property unit=0 portlist=84 speed=100g +port set property unit=0 portlist=88 speed=100g +port set property unit=0 portlist=92 speed=100g +port set property unit=0 portlist=96 speed=100g +port set property unit=0 portlist=100 speed=100g +port set property unit=0 portlist=104 speed=100g +port set property unit=0 portlist=108 speed=100g +port set property unit=0 portlist=112 speed=100g +port set property unit=0 portlist=116 speed=100g +port set property unit=0 portlist=120 speed=100g +port set property unit=0 portlist=124 speed=100g +port set property unit=0 portlist=128 speed=100g +port set property unit=0 portlist=132 speed=100g +port set property unit=0 portlist=136 speed=100g +port set property unit=0 portlist=140 speed=100g +port set property unit=0 portlist=144 speed=100g +port set property unit=0 portlist=148 speed=100g +port set property unit=0 portlist=152 speed=100g +port set property unit=0 portlist=156 speed=100g +port set property unit=0 portlist=160 speed=100g +port set property unit=0 portlist=164 speed=100g +port set property unit=0 portlist=168 speed=100g +port set property unit=0 portlist=172 speed=100g +port set property unit=0 portlist=176 speed=100g +port set property unit=0 portlist=180 speed=100g +port set property unit=0 portlist=184 speed=100g +port set property unit=0 portlist=188 speed=100g +port set property unit=0 portlist=192 speed=400g +port set property unit=0 portlist=200 speed=400g +port set property unit=0 portlist=208 speed=400g +port set property unit=0 portlist=216 speed=400g +port set property unit=0 portlist=224 speed=400g +port set property unit=0 portlist=232 speed=400g +port set property unit=0 portlist=240 speed=400g +port set property unit=0 portlist=248 speed=400g +port set property unit=0 portlist=258 speed=10g +port set property unit=0 portlist=0 medium-type=cr2 +port set property unit=0 portlist=4 medium-type=cr2 +port set property unit=0 portlist=8 medium-type=cr2 +port set property unit=0 portlist=12 medium-type=cr2 +port set property unit=0 portlist=16 medium-type=cr2 +port set property unit=0 portlist=20 medium-type=cr2 +port set property unit=0 portlist=24 medium-type=cr2 +port set property unit=0 portlist=28 medium-type=cr2 +port set property unit=0 portlist=32 medium-type=cr2 +port set property unit=0 portlist=36 medium-type=cr2 +port set property unit=0 portlist=40 medium-type=cr2 +port set property unit=0 portlist=44 medium-type=cr2 +port set property unit=0 portlist=48 medium-type=cr2 +port set property unit=0 portlist=52 medium-type=cr2 +port set property unit=0 portlist=56 medium-type=cr2 +port set property unit=0 portlist=60 medium-type=cr2 +port set property unit=0 portlist=64 medium-type=cr2 +port set property unit=0 portlist=68 medium-type=cr2 +port set property unit=0 portlist=72 medium-type=cr2 +port set property unit=0 portlist=76 medium-type=cr2 +port set property unit=0 portlist=80 medium-type=cr2 +port set property unit=0 portlist=84 medium-type=cr2 +port set property unit=0 portlist=88 medium-type=cr2 +port set property unit=0 portlist=92 medium-type=cr2 +port set property unit=0 portlist=96 medium-type=cr2 +port set property unit=0 portlist=100 medium-type=cr2 +port set property unit=0 portlist=104 medium-type=cr2 +port set property unit=0 portlist=108 medium-type=cr2 +port set property unit=0 portlist=112 medium-type=cr2 +port set property unit=0 portlist=116 medium-type=cr2 +port set property unit=0 portlist=120 medium-type=cr2 +port set property unit=0 portlist=124 medium-type=cr2 +port set property unit=0 portlist=128 medium-type=cr2 +port set property unit=0 portlist=132 medium-type=cr2 +port set property unit=0 portlist=136 medium-type=cr2 +port set property unit=0 portlist=140 medium-type=cr2 +port set property unit=0 portlist=144 medium-type=cr2 +port set property unit=0 portlist=148 medium-type=cr2 +port set property unit=0 portlist=152 medium-type=cr2 +port set property unit=0 portlist=156 medium-type=cr2 +port set property unit=0 portlist=160 medium-type=cr2 +port set property unit=0 portlist=164 medium-type=cr2 +port set property unit=0 portlist=168 medium-type=cr2 +port set property unit=0 portlist=172 medium-type=cr2 +port set property unit=0 portlist=176 medium-type=cr2 +port set property unit=0 portlist=180 medium-type=cr2 +port set property unit=0 portlist=184 medium-type=cr2 +port set property unit=0 portlist=188 medium-type=cr2 +port set property unit=0 portlist=192 medium-type=cr8 +port set property unit=0 portlist=200 medium-type=cr8 +port set property unit=0 portlist=208 medium-type=cr8 +port set property unit=0 portlist=216 medium-type=cr8 +port set property unit=0 portlist=224 medium-type=cr8 +port set property unit=0 portlist=232 medium-type=cr8 +port set property unit=0 portlist=240 medium-type=cr8 +port set property unit=0 portlist=248 medium-type=cr8 +port set property unit=0 portlist=258 medium-type=kr +port set property unit=0 portlist=0 fec=rs544 +port set property unit=0 portlist=4 fec=rs544 +port set property unit=0 portlist=8 fec=rs544 +port set property unit=0 portlist=12 fec=rs544 +port set property unit=0 portlist=16 fec=rs544 +port set property unit=0 portlist=20 fec=rs544 +port set property unit=0 portlist=24 fec=rs544 +port set property unit=0 portlist=28 fec=rs544 +port set property unit=0 portlist=32 fec=rs544 +port set property unit=0 portlist=36 fec=rs544 +port set property unit=0 portlist=40 fec=rs544 +port set property unit=0 portlist=44 fec=rs544 +port set property unit=0 portlist=48 fec=rs544 +port set property unit=0 portlist=52 fec=rs544 +port set property unit=0 portlist=56 fec=rs544 +port set property unit=0 portlist=60 fec=rs544 +port set property unit=0 portlist=64 fec=rs544 +port set property unit=0 portlist=68 fec=rs544 +port set property unit=0 portlist=72 fec=rs544 +port set property unit=0 portlist=76 fec=rs544 +port set property unit=0 portlist=80 fec=rs544 +port set property unit=0 portlist=84 fec=rs544 +port set property unit=0 portlist=88 fec=rs544 +port set property unit=0 portlist=92 fec=rs544 +port set property unit=0 portlist=96 fec=rs544 +port set property unit=0 portlist=100 fec=rs544 +port set property unit=0 portlist=104 fec=rs544 +port set property unit=0 portlist=108 fec=rs544 +port set property unit=0 portlist=112 fec=rs544 +port set property unit=0 portlist=116 fec=rs544 +port set property unit=0 portlist=120 fec=rs544 +port set property unit=0 portlist=124 fec=rs544 +port set property unit=0 portlist=128 fec=rs544 +port set property unit=0 portlist=132 fec=rs544 +port set property unit=0 portlist=136 fec=rs544 +port set property unit=0 portlist=140 fec=rs544 +port set property unit=0 portlist=144 fec=rs544 +port set property unit=0 portlist=148 fec=rs544 +port set property unit=0 portlist=152 fec=rs544 +port set property unit=0 portlist=156 fec=rs544 +port set property unit=0 portlist=160 fec=rs544 +port set property unit=0 portlist=164 fec=rs544 +port set property unit=0 portlist=168 fec=rs544 +port set property unit=0 portlist=172 fec=rs544 +port set property unit=0 portlist=176 fec=rs544 +port set property unit=0 portlist=180 fec=rs544 +port set property unit=0 portlist=184 fec=rs544 +port set property unit=0 portlist=188 fec=rs544 +port set property unit=0 portlist=192 fec=rs544 +port set property unit=0 portlist=200 fec=rs544 +port set property unit=0 portlist=208 fec=rs544 +port set property unit=0 portlist=216 fec=rs544 +port set property unit=0 portlist=224 fec=rs544 +port set property unit=0 portlist=232 fec=rs544 +port set property unit=0 portlist=240 fec=rs544 +port set property unit=0 portlist=248 fec=rs544 +port set property unit=0 portlist=0 admin=disable +port set property unit=0 portlist=4 admin=disable +port set property unit=0 portlist=8 admin=disable +port set property unit=0 portlist=12 admin=disable +port set property unit=0 portlist=16 admin=disable +port set property unit=0 portlist=20 admin=disable +port set property unit=0 portlist=24 admin=disable +port set property unit=0 portlist=28 admin=disable +port set property unit=0 portlist=32 admin=disable +port set property unit=0 portlist=36 admin=disable +port set property unit=0 portlist=40 admin=disable +port set property unit=0 portlist=44 admin=disable +port set property unit=0 portlist=48 admin=disable +port set property unit=0 portlist=52 admin=disable +port set property unit=0 portlist=56 admin=disable +port set property unit=0 portlist=60 admin=disable +port set property unit=0 portlist=64 admin=disable +port set property unit=0 portlist=68 admin=disable +port set property unit=0 portlist=72 admin=disable +port set property unit=0 portlist=76 admin=disable +port set property unit=0 portlist=80 admin=disable +port set property unit=0 portlist=84 admin=disable +port set property unit=0 portlist=88 admin=disable +port set property unit=0 portlist=92 admin=disable +port set property unit=0 portlist=96 admin=disable +port set property unit=0 portlist=100 admin=disable +port set property unit=0 portlist=104 admin=disable +port set property unit=0 portlist=108 admin=disable +port set property unit=0 portlist=112 admin=disable +port set property unit=0 portlist=116 admin=disable +port set property unit=0 portlist=120 admin=disable +port set property unit=0 portlist=124 admin=disable +port set property unit=0 portlist=128 admin=disable +port set property unit=0 portlist=132 admin=disable +port set property unit=0 portlist=136 admin=disable +port set property unit=0 portlist=140 admin=disable +port set property unit=0 portlist=144 admin=disable +port set property unit=0 portlist=148 admin=disable +port set property unit=0 portlist=152 admin=disable +port set property unit=0 portlist=156 admin=disable +port set property unit=0 portlist=160 admin=disable +port set property unit=0 portlist=164 admin=disable +port set property unit=0 portlist=168 admin=disable +port set property unit=0 portlist=172 admin=disable +port set property unit=0 portlist=176 admin=disable +port set property unit=0 portlist=180 admin=disable +port set property unit=0 portlist=184 admin=disable +port set property unit=0 portlist=188 admin=disable +port set property unit=0 portlist=192 admin=disable +port set property unit=0 portlist=200 admin=disable +port set property unit=0 portlist=208 admin=disable +port set property unit=0 portlist=216 admin=disable +port set property unit=0 portlist=224 admin=disable +port set property unit=0 portlist=232 admin=disable +port set property unit=0 portlist=240 admin=disable +port set property unit=0 portlist=248 admin=disable + +port set adver unit=0 portlist=258 speed-10g-kr +port set property unit=0 portlist=258 an-lt=enable +port set property unit=0 portlist=258 admin=disable diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/lightning-fn8656_bnf.led b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/lightning-fn8656_bnf.led new file mode 100644 index 0000000000000000000000000000000000000000..2f0476d8870e45714ca08043f0374bfcfd437533 GIT binary patch literal 1140 zcmW;IU60aW5J2H6TTC&{ATuAYyV>J%&%sv#cLLe*NEJFXU1bhVSjHg z%@1awCHtejGC!J0tL>lcjrrNEwK>}Og*lR$Vt)2s{Dv#P;i^sij)~tfX(PX25AQGnpw`EwVN;M3&4H@#6dV3I$)G&~m;+&X>rw zlCM$nHA*e<4HDlVX*J)W<~!6{#kZ*V7M0fUJsQ48qs zW8!yA+Q=^$`30jk@G}N}#-Mflh>jo8X+1xo=O^@9%MWPz0j;J+248Nv-?RJi6$-vW zq2+vuoG+1UC10cDYm}OQjSXDR^wes;L(O-nwTf?1@hvK?;d?ZEk4Bpp?uU8dewbg~ zkKb_RH(a%e-!bt!CT-*wjQoO88~7OmKV#53eniKQ=(L`n(DM^|t>p)_{D4-A2lqp` zXNGulKfXf2S17ccFOl;la;@ZRlzfd+OMHXGH%MB|cc}RewN~*hD!xUfHGGeT@6l+P KS6uviP5cG%a9&ja literal 0 HcmV?d00001 diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/pg_profile_lookup.ini b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/pg_profile_lookup.ini new file mode 100644 index 000000000000..d98b0eca6d19 --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/pg_profile_lookup.ini @@ -0,0 +1,17 @@ +# PG lossless profiles. +# speed cable size xon xoff threshold + 10000 5m 3584 32256 59136 36736 + 25000 5m 3584 41216 68096 45696 + 40000 5m 3584 47488 74368 51968 + 50000 5m 3584 52864 79744 57344 + 100000 5m 3584 78400 132160 82880 + 10000 40m 3584 32256 59136 36736 + 25000 40m 3584 41216 68096 45696 + 40000 40m 3584 47488 74368 51968 + 50000 40m 3584 52864 79744 57344 + 100000 40m 3584 78400 132160 82880 + 10000 300m 3584 32256 65856 36736 + 25000 300m 3584 41216 84672 45696 + 40000 300m 3584 47488 101024 51968 + 50000 300m 3584 52864 113120 57344 + 100000 300m 3584 78400 198688 82880 \ No newline at end of file diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/port_config.ini b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/port_config.ini new file mode 100755 index 000000000000..67266566606b --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/port_config.ini @@ -0,0 +1,57 @@ +# name lanes alias speed index mtu fec +Ethernet0 100,101 Ethernet0 100000 0 9100 rs +Ethernet4 108,109 Ethernet4 100000 1 9100 rs +Ethernet8 96,97 Ethernet8 100000 2 9100 rs +Ethernet12 104,105 Ethernet12 100000 3 9100 rs +Ethernet16 116,117 Ethernet16 100000 4 9100 rs +Ethernet20 124,125 Ethernet20 100000 5 9100 rs +Ethernet24 112,113 Ethernet24 100000 6 9100 rs +Ethernet28 120,121 Ethernet28 100000 7 9100 rs +Ethernet32 128,129 Ethernet32 100000 8 9100 rs +Ethernet36 136,137 Ethernet36 100000 9 9100 rs +Ethernet40 132,133 Ethernet40 100000 10 9100 rs +Ethernet44 140,141 Ethernet44 100000 11 9100 rs +Ethernet48 144,145 Ethernet48 100000 12 9100 rs +Ethernet52 152,153 Ethernet52 100000 13 9100 rs +Ethernet56 148,149 Ethernet56 100000 14 9100 rs +Ethernet60 156,157 Ethernet60 100000 15 9100 rs +Ethernet64 36,37 Ethernet64 100000 16 9100 rs +Ethernet68 44,45 Ethernet68 100000 17 9100 rs +Ethernet72 32,33 Ethernet72 100000 18 9100 rs +Ethernet76 40,41 Ethernet76 100000 19 9100 rs +Ethernet80 52,53 Ethernet80 100000 20 9100 rs +Ethernet84 60,61 Ethernet84 100000 21 9100 rs +Ethernet88 48,49 Ethernet88 100000 22 9100 rs +Ethernet92 56,57 Ethernet92 100000 23 9100 rs +Ethernet96 64,65 Ethernet96 100000 24 9100 rs +Ethernet100 72,73 Ethernet100 100000 25 9100 rs +Ethernet104 68,69 Ethernet104 100000 26 9100 rs +Ethernet108 76,77 Ethernet108 100000 27 9100 rs +Ethernet112 80,81 Ethernet112 100000 28 9100 rs +Ethernet116 88,89 Ethernet116 100000 29 9100 rs +Ethernet120 84,85 Ethernet120 100000 30 9100 rs +Ethernet124 92,93 Ethernet124 100000 31 9100 rs +Ethernet128 228,229 Ethernet128 100000 32 9100 rs +Ethernet132 236,237 Ethernet132 100000 33 9100 rs +Ethernet136 224,225 Ethernet136 100000 34 9100 rs +Ethernet140 232,233 Ethernet140 100000 35 9100 rs +Ethernet144 244,245 Ethernet144 100000 36 9100 rs +Ethernet148 252,253 Ethernet148 100000 37 9100 rs +Ethernet152 240,241 Ethernet152 100000 38 9100 rs +Ethernet156 248,249 Ethernet156 100000 39 9100 rs +Ethernet160 0,1 Ethernet160 100000 40 9100 rs +Ethernet164 8,9 Ethernet168 100000 41 9100 rs +Ethernet168 4,5 Ethernet164 100000 42 9100 rs +Ethernet172 12,13 Ethernet172 100000 43 9100 rs +Ethernet176 16,17 Ethernet176 100000 44 9100 rs +Ethernet180 24,25 Ethernet180 100000 45 9100 rs +Ethernet184 20,21 Ethernet184 100000 46 9100 rs +Ethernet188 28,29 Ethernet188 100000 47 9100 rs +Ethernet192 160,161,162,163,164,165,166,167 Ethernet192 400000 48 9100 rs +Ethernet200 168,169,170,171,172,173,174,175 Ethernet200 400000 49 9100 rs +Ethernet208 176,177,178,179,180,181,182,183 Ethernet208 400000 50 9100 rs +Ethernet216 184,185,186,187,188,189,190,191 Ethernet216 400000 51 9100 rs +Ethernet224 192,193,194,195,196,197,198,199 Ethernet224 400000 52 9100 rs +Ethernet232 200,201,202,203,204,205,206,207 Ethernet232 400000 53 9100 rs +Ethernet240 208,209,210,211,212,213,214,215 Ethernet240 400000 54 9100 rs +Ethernet248 216,217,218,219,220,221,222,223 Ethernet248 400000 55 9100 rs diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/qos.json.j2 b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/qos.json.j2 new file mode 100644 index 000000000000..08dd0322d639 --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/qos.json.j2 @@ -0,0 +1,198 @@ +{ + "TC_TO_PRIORITY_GROUP_MAP": { + "AZURE": { + "0": "0", + "1": "1", + "2": "2", + "3": "3", + "4": "4", + "5": "5", + "6": "6", + "7": "7" + } + }, + "MAP_PFC_PRIORITY_TO_QUEUE": { + "AZURE": { + "0": "0", + "1": "1", + "2": "2", + "3": "3", + "4": "4", + "5": "5", + "6": "6", + "7": "7" + } + }, + "TC_TO_QUEUE_MAP": { + "AZURE": { + "0": "0", + "1": "1", + "2": "2", + "3": "3", + "4": "4", + "5": "5", + "6": "6", + "7": "7" + } + }, + "DSCP_TO_TC_MAP": { + "AZURE": { + "0" : "1", + "1" : "1", + "2" : "1", + "3" : "3", + "4" : "4", + "5" : "2", + "6" : "1", + "7" : "1", + "8" : "0", + "9" : "1", + "10": "1", + "11": "1", + "12": "1", + "13": "1", + "14": "1", + "15": "1", + "16": "1", + "17": "1", + "18": "1", + "19": "1", + "20": "1", + "21": "1", + "22": "1", + "23": "1", + "24": "1", + "25": "1", + "26": "1", + "27": "1", + "28": "1", + "29": "1", + "30": "1", + "31": "1", + "32": "1", + "33": "1", + "34": "1", + "35": "1", + "36": "1", + "37": "1", + "38": "1", + "39": "1", + "40": "1", + "41": "1", + "42": "1", + "43": "1", + "44": "1", + "45": "1", + "46": "5", + "47": "1", + "48": "6", + "49": "1", + "50": "1", + "51": "1", + "52": "1", + "53": "1", + "54": "1", + "55": "1", + "56": "1", + "57": "1", + "58": "1", + "59": "1", + "60": "1", + "61": "1", + "62": "1", + "63": "1" + } + }, + "SCHEDULER": { + "scheduler.0" : { + "type":"DWRR", + "weight": "25" + }, + "scheduler.1" : { + "type":"DWRR", + "weight": "30" + }, + "scheduler.2" : { + "type":"DWRR", + "weight": "20" + } + }, + "PORT_QOS_MAP": { + {% for port in PORT %} + "{{ port }}": { + "dscp_to_tc_map" : "[DSCP_TO_TC_MAP|AZURE]", + "tc_to_queue_map" : "[TC_TO_QUEUE_MAP|AZURE]", + "tc_to_pg_map" : "[TC_TO_PRIORITY_GROUP_MAP|AZURE]", + "pfc_to_queue_map": "[MAP_PFC_PRIORITY_TO_QUEUE|AZURE]", + "pfc_enable" : "3,4" + }{% if not loop.last %},{% endif %} + + {% endfor %} + }, + "WRED_PROFILE": { + "AZURE_LOSSY" : { + "wred_green_enable":"true", + "wred_yellow_enable":"true", + "wred_red_enable":"true", + "ecn":"ecn_all", + "red_max_threshold":"516096", + "red_min_threshold":"516096", + "yellow_max_threshold":"516096", + "yellow_min_threshold":"516096", + "green_max_threshold": "184128", + "green_min_threshold": "184128" + }, + "AZURE_LOSSLESS" : { + "wred_green_enable":"true", + "wred_yellow_enable":"true", + "wred_red_enable":"true", + "ecn":"ecn_all", + "red_max_threshold":"516096", + "red_min_threshold":"516096", + "yellow_max_threshold":"516096", + "yellow_min_threshold":"516096", + "green_max_threshold": "184128", + "green_min_threshold": "184128" + } + }, + "QUEUE": { +{% for port in PORT %} + "{{ port }}|3": { + "scheduler" : "[SCHEDULER|scheduler.1]", + "wred_profile": "[WRED_PROFILE|AZURE_LOSSLESS]" + }, +{% endfor %} +{% for port in PORT %} + "{{ port }}|4": { + "scheduler" : "[SCHEDULER|scheduler.1]", + "wred_profile": "[WRED_PROFILE|AZURE_LOSSLESS]" + }, +{% endfor %} +{% for port in PORT %} + "{{ port }}|0": { + "scheduler": "[SCHEDULER|scheduler.0]" + }, +{% endfor %} +{% for port in PORT %} + "{{ port }}|1": { + "scheduler": "[SCHEDULER|scheduler.0]" + }, +{% endfor %} +{% for port in PORT %} + "{{ port }}|2": { + "scheduler": "[SCHEDULER|scheduler.0]" + }, +{% endfor %} +{% for port in PORT %} + "{{ port }}|5": { + "scheduler": "[SCHEDULER|scheduler.0]" + }, +{% endfor %} +{% for port in PORT %} + "{{ port }}|6": { + "scheduler": "[SCHEDULER|scheduler.0]" + }{% if not loop.last %},{% endif %} + +{% endfor %} + } +} diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/sai.profile b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/sai.profile new file mode 100755 index 000000000000..d86f9ede0609 --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/fn8656-bnf/sai.profile @@ -0,0 +1,3 @@ +SAI_INIT_CONFIG_FILE=/usr/share/sonic/platform/lightning-fn8656_bnf.cfg +SAI_DSH_CONFIG_FILE=/usr/share/sonic/hwsku/lightning-fn8656_bnf.dsh +SAI_INIT_LED_CONFIG_FILE=/usr/share/sonic/hwsku/lightning-fn8656_bnf.led diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/installer.conf b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/installer.conf new file mode 100644 index 000000000000..9db3677850cb --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/installer.conf @@ -0,0 +1 @@ +ONIE_PLATFORM_EXTRA_CMDLINE_LINUX= diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/lightning-fn8656_bnf.cfg b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/lightning-fn8656_bnf.cfg new file mode 100755 index 000000000000..24c1094b0e50 --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/lightning-fn8656_bnf.cfg @@ -0,0 +1,24 @@ +#This configuration file is for customer init value feature. Please refer to clx_cfg.h/clx_cfg.c for detail. +#1. The lines beginning with # are comment lines. The lines beginning with number are the setting lines. +#2. There are five parameters which can be set. +# 1) the first is unit. +# 2) the second is CLX_CFG_TYPE_XXX. Refer to CLX_CFG_TYPE_T. +# 3) the 3-5 are {param0, param1, value} pairs. Refer to CLX_CFG_VALUE_T. Support HEX format. +# 4) the (unit, CLX_CFG_TYPE_XXX, param0, param1) group is the key to get the correspingding value. +# There should be no same (unit, CLX_CFG_TYPE_XXX, param0, param1) group. +#3. User must follow correct format to apply the setting. Please refer to below commentted example(#0 CLX_CFG_TYPE_L2_ADDR_MODE 0 0 1); +#4. Usage under the linux shell: +# 1) ./image-path/image-name -c cfg-path/CLX_Ari_EVB_24.cfg : mamually specify directory path if they are not in current work dirctory. +# 2) ./image-name -c CLX_Ari_EVB_24.cfg : the image and the CLX_Ari_EVB_24.cfg are in the current work directory. + +#unit CLX_CFG_TYPE_XXX param0 param1 value +#---- ---------------- ------ ------ ----- +0 CLX_CFG_TYPE_USE_UNIT_PORT 0 0 1 +#0 CLX_CFG_TYPE_LED_CFG 0 0 3 +0 CLX_CFG_TYPE_CPI_PORT_MODE 257 0 1 +0 CLX_CFG_TYPE_CPI_PORT_MODE 258 0 1 +0 CLX_CFG_TYPE_USER_BUF_CTRL 0 0 1 +0 CLX_CFG_TYPE_HASH_L2_FDB_REGION_ENTRY_NUM 0 0 49152 +0 CLX_CFG_TYPE_HASH_L3_WITH_IPV6_PREFIX_64_REGION_ENTRY_NUM 0 0 32768 +0 CLX_CFG_TYPE_L2_POLLING_INTERVAL 0 0 400 +0 CLX_CFG_TYPE_PORT_BREAKOUT_LANE_NUM_MAX 0 0 8 diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/media_settings.json b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/media_settings.json new file mode 100644 index 000000000000..39690c0edd14 --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/media_settings.json @@ -0,0 +1,4437 @@ +{ + "PORT_MEDIA_SETTINGS": { + "0": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x29", + "lane2":"0x2a", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfd", + "lane1":"0xfc", + "lane2":"0xfd", + "lane3":"0xfd" + }, + "post1":{ + "lane0":"0xfd", + "lane1":"0xfa", + "lane2":"0xfd", + "lane3":"0xfd" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x2c", + "lane2":"0x32", + "lane3":"0x32" + } + } + }, + "1": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x02" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf5", + "lane2":"0xf5", + "lane3":"0xf5" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x29", + "lane1":"0x28", + "lane2":"0x27", + "lane3":"0x29" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x35", + "lane1":"0x34", + "lane2":"0x35", + "lane3":"0x34" + } + } + }, + "2": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x29", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfd", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc" + }, + "post1":{ + "lane0":"0xfd", + "lane1":"0xfa", + "lane2":"0xfa", + "lane3":"0xfa" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x2c", + "lane2":"0x2c", + "lane3":"0x2c" + } + } + }, + "3": { + "Default": { + "pre2":{ + "lane0":"0x02", + "lane1":"0x02", + "lane2":"0x02", + "lane3":"0x02" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf7" + }, + "post1":{ + "lane0":"0xf5", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x28", + "lane1":"0x2a", + "lane2":"0x29", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x34", + "lane1":"0x32", + "lane2":"0x34", + "lane3":"0x32" + } + } + }, + "4": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf8", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x29", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x29" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfe", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc" + }, + "post1":{ + "lane0":"0xfa", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2c", + "lane1":"0x2c", + "lane2":"0x2c", + "lane3":"0x2c" + } + } + }, + "5": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x02", + "lane2":"0x01", + "lane3":"0x02" + }, + "pre1": { + "lane0":"0xf9", + "lane1":"0xf7", + "lane2":"0xf6", + "lane3":"0xf7" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x29", + "lane1":"0x2a", + "lane2":"0x29", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfc", + "lane3":"0xfb" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfc", + "lane3":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x34", + "lane1":"0x34", + "lane2":"0x35", + "lane3":"0x34" + } + } + }, + "6": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x29" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfe", + "lane1":"0xfd", + "lane2":"0xfc", + "lane3":"0xfc" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfa", + "lane2":"0xfa", + "lane3":"0xfa" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2c", + "lane1":"0x2c", + "lane2":"0x2c", + "lane3":"0x2c" + } + } + }, + "7": { + "Default": { + "pre2":{ + "lane0":"0x02", + "lane1":"0x02", + "lane2":"0x02", + "lane3":"0x02" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf7" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x34", + "lane1":"0x32", + "lane2":"0x32", + "lane3":"0x32" + } + } + }, + "8": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x29", + "lane1":"0x2c", + "lane2":"0x2c", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfd", + "lane3":"0xfd" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfd", + "lane3":"0xfa" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2c", + "lane1":"0x2c", + "lane2":"0x2a", + "lane3":"0x2c" + } + } + }, + "9": { + "Default": { + "pre2":{ + "lane0":"0x02", + "lane1":"0x02", + "lane2":"0x02", + "lane3":"0x02" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf6", + "lane3":"0xf7" + }, + "post1":{ + "lane0":"0xf5", + "lane1":"0xf6", + "lane2":"0xf5", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x28", + "lane1":"0x2a", + "lane2":"0x28", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfd", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x35", + "lane1":"0x32", + "lane2":"0x34", + "lane3":"0x32" + } + } + }, + "10": { + "Default": { + "pre2":{ + "lane0":"0x02", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2b", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfd", + "lane1":"0xfd", + "lane2":"0xfd", + "lane3":"0xfd" + }, + "post1":{ + "lane0":"0xfd", + "lane1":"0xfd", + "lane2":"0xfa", + "lane3":"0xfa" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x31", + "lane1":"0x2a", + "lane2":"0x2c", + "lane3":"0x2c" + } + } + }, + "11": { + "Default": { + "pre2":{ + "lane0":"0x02", + "lane1":"0x01", + "lane2":"0x02", + "lane3":"0x02" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf7" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x29", + "lane2":"0x2a", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x32", + "lane3":"0x32" + } + } + }, + "12": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf7" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf7" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2c", + "lane2":"0x2c", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfd", + "lane1":"0xfc", + "lane2":"0xfd", + "lane3":"0xfc" + }, + "post1":{ + "lane0":"0xfa", + "lane1":"0xfc", + "lane2":"0xfd", + "lane3":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x30", + "lane1":"0x2d", + "lane2":"0x2a", + "lane3":"0x2c" + } + } + }, + "13": { + "Default": { + "pre2":{ + "lane0":"0x02", + "lane1":"0x02", + "lane2":"0x02", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf8", + "lane2":"0xf7", + "lane3":"0xf8" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf7", + "lane2":"0xf6", + "lane3":"0xf7" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x29" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x32", + "lane3":"0x32" + } + } + }, + "14": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x29", + "lane1":"0x2b", + "lane2":"0x2c", + "lane3":"0x29" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfd", + "lane1":"0xfd", + "lane2":"0xfd", + "lane3":"0xfc" + }, + "post1":{ + "lane0":"0xfa", + "lane1":"0xfd", + "lane2":"0xfd", + "lane3":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2c", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x2c" + } + } + }, + "15": { + "Default": { + "pre2":{ + "lane0":"0x02", + "lane1":"0x02", + "lane2":"0x02", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf6", + "lane3":"0xf8" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf5", + "lane3":"0xf7" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2a", + "lane2":"0x28", + "lane3":"0x29" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfc" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x34", + "lane3":"0x32" + } + } + }, + "16": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf8", + "lane1":"0xf8", + "lane2":"0xf7", + "lane3":"0xf7" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x28", + "lane1":"0x28", + "lane2":"0x28", + "lane3":"0x28" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfb", + "lane2":"0xfc", + "lane3":"0xfc" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfb", + "lane2":"0xfc", + "lane3":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x34", + "lane1":"0x32", + "lane2":"0x34", + "lane3":"0x32" + } + } + }, + "17": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf7" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x29", + "lane1":"0x29", + "lane2":"0x29", + "lane3":"0x29" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfb", + "lane2":"0xfc", + "lane3":"0xfb" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfb", + "lane2":"0xfc", + "lane3":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x32", + "lane3":"0x32" + } + } + }, + "18": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf8", + "lane3":"0xf7" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x28", + "lane1":"0x28", + "lane2":"0x28", + "lane3":"0x28" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfc", + "lane3":"0xfb" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfc", + "lane3":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x33", + "lane2":"0x32", + "lane3":"0x32" + } + } + }, + "19": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x29", + "lane1":"0x29", + "lane2":"0x2a", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfc", + "lane3":"0xfb" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x35", + "lane2":"0x36", + "lane3":"0x32" + } + } + }, + "20": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfc" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x32", + "lane3":"0x32" + } + } + }, + "21": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf7", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2a", + "lane2":"0x29", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x32", + "lane3":"0x32" + } + } + }, + "22": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x32", + "lane3":"0x32" + } + } + }, + "23": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf7" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf7" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x29" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfc", + "lane2":"0xfb", + "lane3":"0xfc" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfc", + "lane2":"0xfb", + "lane3":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x2b", + "lane2":"0x33", + "lane3":"0x2b" + } + } + }, + "24": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfb" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x2d", + "lane2":"0x32", + "lane3":"0x32" + } + } + }, + "25": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf7", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2a", + "lane2":"0x29", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x2b", + "lane2":"0x2b", + "lane3":"0x2b" + } + } + }, + "26": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x34", + "lane1":"0x2d", + "lane2":"0x32", + "lane3":"0x32" + } + } + }, + "27": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf7", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x28", + "lane2":"0x2a", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2f", + "lane1":"0x2b", + "lane2":"0x2b", + "lane3":"0x2b" + } + } + }, + "28": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x29", + "lane1":"0x29", + "lane2":"0x29", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfd", + "lane2":"0xfb", + "lane3":"0xfc" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfd", + "lane2":"0xfb", + "lane3":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2d", + "lane1":"0x2d", + "lane2":"0x32", + "lane3":"0x2d" + } + } + }, + "29": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf8", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf8" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x27", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x29" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2b", + "lane1":"0x2b", + "lane2":"0x2d", + "lane3":"0x2b" + } + } + }, + "30": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf8", + "lane2":"0xf7", + "lane3":"0xf7" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x29", + "lane1":"0x2a", + "lane2":"0x29", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfd", + "lane2":"0xfc", + "lane3":"0xfc" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfd", + "lane2":"0xfc", + "lane3":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2d", + "lane1":"0x23", + "lane2":"0x2d", + "lane3":"0x2d" + } + } + }, + "31": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf7", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2b", + "lane1":"0x2b", + "lane2":"0x2b", + "lane3":"0x2b" + } + } + }, + "32": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf8", + "lane1":"0xf8", + "lane2":"0xf7", + "lane3":"0xf7" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf7" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2b", + "lane1":"0x2a", + "lane2":"0x2b", + "lane3":"0x2c" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfd", + "lane2":"0xfd", + "lane3":"0xfc" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfd", + "lane2":"0xfd", + "lane3":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2c", + "lane1":"0x2d", + "lane2":"0x2a", + "lane3":"0x2b" + } + } + }, + "33": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf8" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x2b" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfc" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x32", + "lane3":"0x32" + } + } + }, + "34": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf6", + "lane2":"0xf7", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2b", + "lane1":"0x2a", + "lane2":"0x2b", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfd", + "lane1":"0xfd", + "lane2":"0xfc", + "lane3":"0xfd" + }, + "post1":{ + "lane0":"0xfd", + "lane1":"0xfd", + "lane2":"0xfc", + "lane3":"0xfd" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2d", + "lane2":"0x2c", + "lane3":"0x2d" + } + } + }, + "35": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2b", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfb", + "lane2":"0xfc", + "lane3":"0xfb" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfb", + "lane2":"0xfc", + "lane3":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x32", + "lane3":"0x32" + } + } + }, + "36": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf6", + "lane2":"0xf8", + "lane3":"0xf7" + }, + "post1":{ + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf7" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2c", + "lane1":"0x2b", + "lane2":"0x2c", + "lane3":"0x2c" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfd", + "lane3":"0xfd" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfd", + "lane3":"0xfd" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2c", + "lane1":"0x2c", + "lane2":"0x2a", + "lane3":"0x2a" + } + } + }, + "37": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x02" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf8" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf7", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2b", + "lane1":"0x2a", + "lane2":"0x2c", + "lane3":"0x29" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x34", + "lane3":"0x34" + } + } + }, + "38": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf7", + "lane2":"0xf6", + "lane3":"0xf7" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf7" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x2c" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfa", + "lane3":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2c", + "lane1":"0x2c", + "lane2":"0x30", + "lane3":"0x2c" + } + } + }, + "39": { + "Default": { + "pre2":{ + "lane0":"0x02", + "lane1":"0x02", + "lane2":"0x02", + "lane3":"0x02" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf6", + "lane3":"0xf7" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2a", + "lane2":"0x29", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x34", + "lane3":"0x32" + } + } + }, + "40": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf7" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf7" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2c", + "lane2":"0x2c", + "lane3":"0x2c" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfd", + "lane2":"0xfc", + "lane3":"0xfd" + }, + "post1":{ + "lane0":"0xfa", + "lane1":"0xfd", + "lane2":"0xfc", + "lane3":"0xfd" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2c", + "lane1":"0x2a", + "lane2":"0x2c", + "lane3":"0x2a" + } + } + }, + "41": { + "Default": { + "pre2":{ + "lane0":"0x02", + "lane1":"0x02", + "lane2":"0x02", + "lane3":"0x02" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf7", + "lane2":"0xf6", + "lane3":"0xf7" + }, + "post1":{ + "lane0":"0xf5", + "lane1":"0xf6", + "lane2":"0xf5", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x28", + "lane1":"0x2a", + "lane2":"0x28", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x34", + "lane1":"0x32", + "lane2":"0x34", + "lane3":"0x32" + } + } + }, + "42": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfd", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc" + }, + "post1":{ + "lane0":"0xfd", + "lane1":"0xfa", + "lane2":"0xfa", + "lane3":"0xfa" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x2c", + "lane2":"0x2c", + "lane3":"0x2c" + } + } + }, + "43": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf7", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf5", + "lane1":"0xf5", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x29", + "lane1":"0x29", + "lane2":"0x2a", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfb", + "lane3":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x35", + "lane1":"0x36", + "lane2":"0x35", + "lane3":"0x34" + } + } + }, + "44": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf7" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2b", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x2b" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfd", + "lane2":"0xfd", + "lane3":"0xfd" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfd", + "lane2":"0xfd", + "lane3":"0xfd" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x32", + "lane3":"0x32" + } + } + }, + "45": { + "Default": { + "pre2":{ + "lane0":"0x02", + "lane1":"0x01", + "lane2":"0x02", + "lane3":"0x02" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf7", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf5", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf5" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x28", + "lane1":"0x2a", + "lane2":"0x29", + "lane3":"0x28" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfd", + "lane2":"0xfb", + "lane3":"0xfd" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfd", + "lane2":"0xfb", + "lane3":"0xfd" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x34", + "lane3":"0x32" + } + } + }, + "46": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x2a" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfa", + "lane2":"0xfd", + "lane3":"0xfa" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfa", + "lane2":"0xfd", + "lane3":"0xfa" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x32", + "lane3":"0x32" + } + } + }, + "47": { + "Default": { + "pre2":{ + "lane0":"0x02", + "lane1":"0x02", + "lane2":"0x02", + "lane3":"0x02" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6" + }, + "post1":{ + "lane0":"0xf4", + "lane1":"0xf5", + "lane2":"0xf4", + "lane3":"0xf4" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x23", + "lane1":"0x28", + "lane2":"0x27", + "lane3":"0x25" + } + }, + "DSFP-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfb", + "lane2":"0xfc", + "lane3":"0xfb" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfb", + "lane2":"0xfc", + "lane3":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00" + }, + "main":{ + "lane0":"0x35", + "lane1":"0x33", + "lane2":"0x32", + "lane3":"0x33" + } + } + }, + "48": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01", + "lane4":"0x01", + "lane5":"0x01", + "lane6":"0x01", + "lane7":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf7", + "lane4":"0xf7", + "lane5":"0xf7", + "lane6":"0xf7", + "lane7":"0xf7" + }, + "post1":{ + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf7", + "lane4":"0xf7", + "lane5":"0xf7", + "lane6":"0xf7", + "lane7":"0xf7" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x2c", + "lane1":"0x2c", + "lane2":"0x2c", + "lane3":"0x2c", + "lane4":"0x2b", + "lane5":"0x2b", + "lane6":"0x2c", + "lane7":"0x2c" + } + + }, + "QSFP-DD-media_interface: AOC": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfc", + "lane6":"0xfc", + "lane7":"0xfc" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfc", + "lane6":"0xfc", + "lane7":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x2d", + "lane1":"0x2d", + "lane2":"0x2d", + "lane3":"0x2d", + "lane4":"0x2d", + "lane5":"0x2d", + "lane6":"0x2d", + "lane7":"0x2d" + } + }, + "QSFP28-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfc", + "lane6":"0xfc", + "lane7":"0xfc" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfc", + "lane6":"0xfc", + "lane7":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x2d", + "lane1":"0x2d", + "lane2":"0x2d", + "lane3":"0x2d", + "lane4":"0x2d", + "lane5":"0x2d", + "lane6":"0x2d", + "lane7":"0x2d" + } + } + }, + "49": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01", + "lane4":"0x01", + "lane5":"0x01", + "lane6":"0x01", + "lane7":"0x01" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf7", + "lane4":"0xf7", + "lane5":"0xf7", + "lane6":"0xf6", + "lane7":"0xf7" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf7", + "lane4":"0xf7", + "lane5":"0xf7", + "lane6":"0xf6", + "lane7":"0xf7" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2c", + "lane2":"0x2c", + "lane3":"0x2c", + "lane4":"0x2c", + "lane5":"0x2c", + "lane6":"0x2a", + "lane7":"0x2c" + } + }, + "QSFP-DD-media_interface: AOC": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfc", + "lane6":"0xfc", + "lane7":"0xfc" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfc", + "lane6":"0xfc", + "lane7":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x2b", + "lane1":"0x2b", + "lane2":"0x2b", + "lane3":"0x2b", + "lane4":"0x2b", + "lane5":"0x2b", + "lane6":"0x2b", + "lane7":"0x2b" + } + }, + "QSFP28-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfc", + "lane6":"0xfc", + "lane7":"0xfc" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfc", + "lane6":"0xfc", + "lane7":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x2b", + "lane1":"0x2b", + "lane2":"0x2b", + "lane3":"0x2b", + "lane4":"0x2b", + "lane5":"0x2b", + "lane6":"0x2b", + "lane7":"0x2b" + } + } + }, + "50": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01", + "lane4":"0x01", + "lane5":"0x01", + "lane6":"0x01", + "lane7":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf7", + "lane4":"0xf6", + "lane5":"0xf7", + "lane6":"0xf7", + "lane7":"0xf7" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf7", + "lane2":"0xf6", + "lane3":"0xf7", + "lane4":"0xf6", + "lane5":"0xf7", + "lane6":"0xf6", + "lane7":"0xf7" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x2b", + "lane1":"0x2c", + "lane2":"0x2b", + "lane3":"0x2b", + "lane4":"0x2a", + "lane5":"0x2c", + "lane6":"0x2b", + "lane7":"0x2c" + } + }, + "QSFP-DD-media_interface: AOC": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfc", + "lane2":"0xfb", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfc", + "lane6":"0xfb", + "lane7":"0xfc" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfc", + "lane2":"0xfb", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfc", + "lane6":"0xfb", + "lane7":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x2d", + "lane2":"0x32", + "lane3":"0x2d", + "lane4":"0x32", + "lane5":"0x2d", + "lane6":"0x32", + "lane7":"0x2d" + } + }, + "QSFP28-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfc", + "lane2":"0xfb", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfc", + "lane6":"0xfb", + "lane7":"0xfc" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfc", + "lane2":"0xfb", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfc", + "lane6":"0xfb", + "lane7":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x2d", + "lane2":"0x32", + "lane3":"0x2d", + "lane4":"0x32", + "lane5":"0x2d", + "lane6":"0x32", + "lane7":"0x2d" + } + } + }, + "51": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01", + "lane4":"0x01", + "lane5":"0x01", + "lane6":"0x01", + "lane7":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf7", + "lane4":"0xf7", + "lane5":"0xf7", + "lane6":"0xf7", + "lane7":"0xf7" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf7", + "lane2":"0xf6", + "lane3":"0xf6", + "lane4":"0xf6", + "lane5":"0xf7", + "lane6":"0xf6", + "lane7":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x2b", + "lane1":"0x2b", + "lane2":"0x2b", + "lane3":"0x2b", + "lane4":"0x2b", + "lane5":"0x2c", + "lane6":"0x2b", + "lane7":"0x2b" + } + }, + "QSFP-DD-media_interface: AOC": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfc", + "lane6":"0xfb", + "lane7":"0xfc" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfc", + "lane6":"0xfb", + "lane7":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x2b", + "lane1":"0x2b", + "lane2":"0x2b", + "lane3":"0x2b", + "lane4":"0x2b", + "lane5":"0x2b", + "lane6":"0x32", + "lane7":"0x2b" + } + }, + "QSFP28-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "pre1": { + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfc", + "lane6":"0xfb", + "lane7":"0xfc" + }, + "post1":{ + "lane0":"0xfc", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfc", + "lane6":"0xfb", + "lane7":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x2b", + "lane1":"0x2b", + "lane2":"0x2b", + "lane3":"0x2b", + "lane4":"0x2b", + "lane5":"0x2b", + "lane6":"0x32", + "lane7":"0x2b" + } + } + }, + "52": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01", + "lane4":"0x01", + "lane5":"0x01", + "lane6":"0x01", + "lane7":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf7", + "lane4":"0xf7", + "lane5":"0xf7", + "lane6":"0xf7", + "lane7":"0xf7" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6", + "lane4":"0xf6", + "lane5":"0xf6", + "lane6":"0xf6", + "lane7":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x2b", + "lane1":"0x2a", + "lane2":"0x2a", + "lane3":"0x2a", + "lane4":"0x2b", + "lane5":"0x2b", + "lane6":"0x2b", + "lane7":"0x2b" + } + }, + "QSFP-DD-media_interface: AOC": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfb", + "lane6":"0xfc", + "lane7":"0xfb" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfb", + "lane6":"0xfc", + "lane7":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x32", + "lane3":"0x32", + "lane4":"0x32", + "lane5":"0x32", + "lane6":"0x32", + "lane7":"0x32" + } + }, + "QSFP28-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfb", + "lane6":"0xfc", + "lane7":"0xfb" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfc", + "lane2":"0xfc", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfb", + "lane6":"0xfc", + "lane7":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x32", + "lane3":"0x32", + "lane4":"0x32", + "lane5":"0x32", + "lane6":"0x32", + "lane7":"0x32" + } + } + }, + "53": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01", + "lane4":"0x01", + "lane5":"0x01", + "lane6":"0x01", + "lane7":"0x01" + }, + "pre1": { + "lane0":"0xf6", + "lane1":"0xf7", + "lane2":"0xf7", + "lane3":"0xf8", + "lane4":"0xf7", + "lane5":"0xf7", + "lane6":"0xf7", + "lane7":"0xf7" + }, + "post1":{ + "lane0":"0xf7", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6", + "lane4":"0xf6", + "lane5":"0xf6", + "lane6":"0xf6", + "lane7":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x2b", + "lane1":"0x2b", + "lane2":"0x2b", + "lane3":"0x28", + "lane4":"0x2b", + "lane5":"0x2a", + "lane6":"0x2a", + "lane7":"0x2a" + } + }, + "QSFP-DD-media_interface: AOC": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb", + "lane4":"0xfb", + "lane5":"0xfc", + "lane6":"0xfb", + "lane7":"0xfc" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb", + "lane4":"0xfb", + "lane5":"0xfc", + "lane6":"0xfb", + "lane7":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x32", + "lane3":"0x32", + "lane4":"0x32", + "lane5":"0x2b", + "lane6":"0x32", + "lane7":"0x2b" + } + }, + "QSFP28-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb", + "lane4":"0xfb", + "lane5":"0xfc", + "lane6":"0xfb", + "lane7":"0xfc" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb", + "lane4":"0xfb", + "lane5":"0xfc", + "lane6":"0xfb", + "lane7":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x32", + "lane3":"0x32", + "lane4":"0x32", + "lane5":"0x2b", + "lane6":"0x32", + "lane7":"0x2b" + } + } + }, + "54": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01", + "lane4":"0x01", + "lane5":"0x01", + "lane6":"0x02", + "lane7":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf8", + "lane2":"0xf7", + "lane3":"0xf6", + "lane4":"0xf7", + "lane5":"0xf6", + "lane6":"0xf5", + "lane7":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6", + "lane4":"0xf6", + "lane5":"0xf6", + "lane6":"0xf6", + "lane7":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2b", + "lane2":"0x2a", + "lane3":"0x2a", + "lane4":"0x2a", + "lane5":"0x2a", + "lane6":"0x28", + "lane7":"0x2a" + } + }, + "QSFP-DD-media_interface: AOC": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfc", + "lane2":"0xfb", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfc", + "lane6":"0xfc", + "lane7":"0xfc" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfc", + "lane2":"0xfb", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfc", + "lane6":"0xfc", + "lane7":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x32", + "lane3":"0x32", + "lane4":"0x32", + "lane5":"0x32", + "lane6":"0x32", + "lane7":"0x32" + } + }, + "QSFP28-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfc", + "lane2":"0xfb", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfc", + "lane6":"0xfc", + "lane7":"0xfc" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfc", + "lane2":"0xfb", + "lane3":"0xfc", + "lane4":"0xfc", + "lane5":"0xfc", + "lane6":"0xfc", + "lane7":"0xfc" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x32", + "lane3":"0x32", + "lane4":"0x32", + "lane5":"0x32", + "lane6":"0x32", + "lane7":"0x32" + } + } + }, + "55": { + "Default": { + "pre2":{ + "lane0":"0x01", + "lane1":"0x01", + "lane2":"0x01", + "lane3":"0x01", + "lane4":"0x01", + "lane5":"0x01", + "lane6":"0x01", + "lane7":"0x01" + }, + "pre1": { + "lane0":"0xf7", + "lane1":"0xf6", + "lane2":"0xf7", + "lane3":"0xf6", + "lane4":"0xf7", + "lane5":"0xf6", + "lane6":"0xf6", + "lane7":"0xf6" + }, + "post1":{ + "lane0":"0xf6", + "lane1":"0xf6", + "lane2":"0xf6", + "lane3":"0xf6", + "lane4":"0xf6", + "lane5":"0xf6", + "lane6":"0xf6", + "lane7":"0xf6" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x2a", + "lane1":"0x2a", + "lane2":"0x2b", + "lane3":"0x2a", + "lane4":"0x2a", + "lane5":"0x2a", + "lane6":"0x2a", + "lane7":"0x2a" + } + }, + "QSFP-DD-media_interface: AOC": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb", + "lane4":"0xfb", + "lane5":"0xfb", + "lane6":"0xfb", + "lane7":"0xfb" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb", + "lane4":"0xfb", + "lane5":"0xfb", + "lane6":"0xfb", + "lane7":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x32", + "lane3":"0x32", + "lane4":"0x32", + "lane5":"0x32", + "lane6":"0x32", + "lane7":"0x32" + } + }, + "QSFP28-{'media_interface':'AOC'}": { + "pre2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "pre1": { + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb", + "lane4":"0xfb", + "lane5":"0xfb", + "lane6":"0xfb", + "lane7":"0xfb" + }, + "post1":{ + "lane0":"0xfb", + "lane1":"0xfb", + "lane2":"0xfb", + "lane3":"0xfb", + "lane4":"0xfb", + "lane5":"0xfb", + "lane6":"0xfb", + "lane7":"0xfb" + }, + "post2":{ + "lane0":"0x00", + "lane1":"0x00", + "lane2":"0x00", + "lane3":"0x00", + "lane4":"0x00", + "lane5":"0x00", + "lane6":"0x00", + "lane7":"0x00" + }, + "main":{ + "lane0":"0x32", + "lane1":"0x32", + "lane2":"0x32", + "lane3":"0x32", + "lane4":"0x32", + "lane5":"0x32", + "lane6":"0x32", + "lane7":"0x32" + } + } + } + } +} diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/pcie.yaml b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/pcie.yaml new file mode 100644 index 000000000000..6fecdda382d0 --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/pcie.yaml @@ -0,0 +1,5 @@ +- bus: '01' + dev: '00' + fn: '0' + id: '8579' + name: 'Ethernet controller: Device 1d9f:8579 (rev 01)' diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/platform.json b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/platform.json new file mode 100644 index 000000000000..05ffde70d759 --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/platform.json @@ -0,0 +1,344 @@ +{ + "chassis": { + "name": "fn8656-bnf", + "watchdog": { + "name":"watchdog0" + }, + "components": [ + { + "name": "CPLD-A", + "description":"Main board side CPLD A", + "version":"21.0", + "firmware_version_path":"/sys/bus/i2c/devices/6-0074/cpld_sw_version", + "hw_version_path":"/sys/bus/i2c/devices/6-0074/cpld_hw_version" + }, + { + "name": "CPLD-B", + "description":"Main board side CPLD B", + "version":"21.0", + "firmware_version_path":"/sys/bus/i2c/devices/7-0075/cpld_sw_version", + "hw_version_path":"/sys/bus/i2c/devices/7-0075/cpld_hw_version" + }, + { + "name": "CPLD-C", + "description":"Main board side CPLD C", + "version":"21.0", + "firmware_version_path":"/sys/bus/i2c/devices/8-0076/cpld_sw_version", + "hw_version_path":"/sys/bus/i2c/devices/8-0076/cpld_hw_version" + }, + { + "name": "CPLD-D", + "description":"NPU board side CPLD", + "version":"20.0", + "firmware_version_path":"/sys/bus/i2c/devices/1-0018/cpld_sw_version", + "hw_version_path":"/sys/bus/i2c/devices/1-0018/cpld_hw_version" + }, + { + "name": "MCU-MB", + "description":"Main board side MCU", + "version":"0.5", + "firmware_version_path":"/sys/bus/i2c/devices/4-0070/hwmon/hwmon4/mb_fw_version", + "hw_version_path":"/sys/bus/i2c/devices/4-0070/hwmon/hwmon4/mb_hw_version" + }, + { + "name": "MCU-FB", + "description":"Fan board side MCU", + "version":"0.10", + "firmware_version_path":"/sys/bus/i2c/devices/4-0070/hwmon/hwmon4/fb_fw_version", + "hw_version_path":"/sys/bus/i2c/devices/4-0070/hwmon/hwmon4/fb_hw_version" + }, + { + "name": "FPGA", + "description":"Main board side FPGA", + "version":"18.0", + "firmware_version_path":"/sys/devices/pci0000:00/0000:00:1c.4/0000:0a:00.0/fpga_sw_version", + "hw_version_path":"/sys/devices/pci0000:00/0000:00:1c.4/0000:0a:00.0/fpga_hw_version" + }, + { + "name": "BIOS", + "description":"Performs initialization of hardware components during booting", + "version":"0.10", + "firmware_version_path":"dmidecode -s bios-version", + "hw_version_path":"dmidecode -s bios-version" + } + ], + "fans": [], + "fan_drawers": [ + { + "name": "Draw1", + "fans": [ + { + "name": "FAN1" + }, + { + "name": "FAN2" + }, + { + "name": "FAN3" + }, + { + "name": "FAN4" + }, + { + "name": "FAN5" + }, + { + "name": "FAN6" + } + ] + } + ], + "psus": [ + { + "name": "PSU1", + "fans": [ + { + "name": "PSU1-FAN1" + } + ], + "thermals": [ + { + "name": "PSU1 Ambient", + "position": "PSU" + }, + { + "name": "PSU1 SR Hotspot", + "position": "PSU" + }, + { + "name": "PSU1 PFC Hotspot", + "position": "PSU" + } + ] + }, + { + "name": "PSU2", + "fans": [ + { + "name": "PSU2-FAN1" + } + ], + "thermals" :[ + { + "name": "PSU2 Ambient", + "position": "PSU" + }, + { + "name": "PSU2 SR Hotspot", + "position": "PSU" + }, + { + "name": "PSU2 PFC Hotspot", + "position": "PSU" + } + ] + } + ], + "thermals": [ + { + "name": "LM75BD / Ambient MAC side", + "position": "LM75BD" + }, + { + "name": "LM75BD / Ambient MAC", + "position": "LM75BD" + }, + { + "name": "LM75BD / Ambient FAN", + "position": "LM75BD" + }, + { + "name": "LM75BD / Ambient NPU", + "position": "LM75BD" + }, + { + "name": "CPU / Core 0", + "position": "CPU" + }, + { + "name": "CPU / Core 1", + "position": "CPU" + }, + { + "name": "CPU / Core 2", + "position": "CPU" + }, + { + "name": "CPU / Core 3", + "position": "CPU" + } + ], + "sfps": [ + { + "name": "Ethernet0" + }, + { + "name": "Ethernet4" + }, + { + "name": "Ethernet8" + }, + { + "name": "Ethernet12" + }, + { + "name": "Ethernet16" + }, + { + "name": "Ethernet20" + }, + { + "name": "Ethernet24" + }, + { + "name": "Ethernet28" + }, + { + "name": "Ethernet32" + }, + { + "name": "Ethernet36" + }, + { + "name": "Ethernet40" + }, + { + "name": "Ethernet44" + }, + { + "name": "Ethernet48" + }, + { + "name": "Ethernet52" + }, + { + "name": "Ethernet56" + }, + { + "name": "Ethernet60" + }, + { + "name": "Ethernet64" + }, + { + "name": "Ethernet68" + }, + { + "name": "Ethernet72" + }, + { + "name": "Ethernet76" + }, + { + "name": "Ethernet80" + }, + { + "name": "Ethernet84" + }, + { + "name": "Ethernet88" + }, + { + "name": "Ethernet92" + }, + { + "name": "Ethernet96" + }, + { + "name": "Etherne100" + }, + { + "name": "Etherne104" + }, + { + "name": "Etherne108" + }, + { + "name": "Ethernet112" + }, + { + "name": "Ethernet116" + }, + { + "name": "Ethernet120" + }, + { + "name": "Ethernet124" + }, + { + "name": "Ethernet128" + }, + { + "name": "Ethernet132" + }, + { + "name": "Ethernet136" + }, + { + "name": "Ethernet140" + }, + { + "name": "Ethernet144" + }, + { + "name": "Ethernet148" + }, + { + "name": "Ethernet152" + }, + { + "name": "Ethernet156" + }, + { + "name": "Ethernet160" + }, + { + "name": "Ethernet164" + }, + { + "name": "Ethernet168" + }, + { + "name": "Ethernet172" + }, + { + "name": "Ethernet176" + }, + { + "name": "Ethernet180" + }, + { + "name": "Ethernet184" + }, + { + "name": "Ethernet188" + }, + { + "name": "Ethernet192" + }, + { + "name": "Ethernet200" + }, + { + "name": "Ethernet208" + }, + { + "name": "Ethernet216" + }, + { + "name": "Ethernet224" + }, + { + "name": "Ethernet232" + }, + { + "name": "Ethernet240" + }, + { + "name": "Ethernet248" + } + ] + }, + "interfaces" :{} + +} diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/platform_components.json b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/platform_components.json new file mode 100644 index 000000000000..8be1fbdb5f5f --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/platform_components.json @@ -0,0 +1,14 @@ +{ + "chassis": { + "FN8656-BNF": { + "component": { + "CPLD-A": {}, + "CPLD-B": {}, + "CPLD-C": {}, + "CPLD-D": {}, + "MCU": {}, + "BIOS": {} + } + } + } +} diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/plugins/eeprom.py b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/plugins/eeprom.py new file mode 100755 index 000000000000..6a9dd2400f24 --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/plugins/eeprom.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +try: + #import binascii + #import time + #import optparse + #import warnings + #import os + #import sys + #from sonic_eeprom import eeprom_base + from sonic_eeprom import eeprom_tlvinfo + #import subprocess +except ImportError as e: + raise ImportError (str(e) + "- required module not found") + +class board(eeprom_tlvinfo.TlvInfoDecoder): + _TLV_INFO_MAX_LEN = 256 + def __init__(self, name, path, cpld_root, ro): + self.eeprom_path = "/sys/bus/i2c/devices/9-0054/eeprom" + super(board, self).__init__(self.eeprom_path, 0, '', True) diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/plugins/psuutil.py b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/plugins/psuutil.py new file mode 100755 index 000000000000..400900337d98 --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/plugins/psuutil.py @@ -0,0 +1,92 @@ +# +# psuutil.py +# Platform-specific PSU status interface for SONiC +# + + +import os.path + +try: + from sonic_psu.psu_base import PsuBase +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class PsuUtil(PsuBase): + """Platform-specific PSUutil class""" + + SYSFS_PSU_DIR = "/sys/bus/i2c/devices/6-0074" + + def __init__(self): + PsuBase.__init__(self) + + + # Get sysfs attribute + def get_attr_value(self, attr_path): + + retval = 'ERR' + if (not os.path.isfile(attr_path)): + return retval + + try: + with open(attr_path, 'r') as fd: + retval = fd.read() + except Exception as error: + logging.error("Unable to open ", attr_path, " file !") + + retval = retval.rstrip('\r\n') + + fd.close() + return retval + + def get_num_psus(self): + """ + Retrieves the number of PSUs available on the device + :return: An integer, the number of PSUs available on the device + """ + MAX_PSUS = 2 + return MAX_PSUS + + def get_psu_status(self, index): + """ + Retrieves the oprational status of power supply unit (PSU) defined + by index + :param index: An integer, index of the PSU of which to query status + :return: Boolean, True if PSU is operating properly, False if PSU is\ + faulty + """ + status = 0 + attr_file = 'psu_'+str(index)+'_status' + attr_path = self.SYSFS_PSU_DIR +'/' + attr_file + + attr_value = self.get_attr_value(attr_path) + + if (attr_value != 'ERR'): + attr_value = int(attr_value, 16) + # Check for PSU status + if (attr_value == 1): + status = 1 + + return status + + def get_psu_presence(self, index): + """ + Retrieves the presence status of power supply unit (PSU) defined + by index + :param index: An integer, index of the PSU of which to query status + :return: Boolean, True if PSU is plugged, False if not + """ + status = 0 + attr_file = 'psu_'+str(index)+'_present' + attr_path = self.SYSFS_PSU_DIR +'/' + attr_file + + attr_value = self.get_attr_value(attr_path) + + if (attr_value != 'ERR'): + attr_value = int(attr_value, 16) + # Check for PSU presence + if (attr_value == 0): + status = 1 + + return status + diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/plugins/sfputil.py b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/plugins/sfputil.py new file mode 100755 index 000000000000..50a716997e97 --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/plugins/sfputil.py @@ -0,0 +1,483 @@ +#!/usr/bin/env python + +try: + import os + import sys + import re + import time + from sonic_sfp.sfputilbase import SfpUtilBase + from sonic_sfp.sff8472 import sff8472Dom +except ImportError as e: + raise ImportError (str(e) + "- required module not found") + +SFP_STATUS_INSERTED = '1' +SFP_STATUS_REMOVED = '0' +SFP_TEMPE_OFFSET = 96 +SFP_TEMPE_WIDTH = 2 +SFP_VOLT_OFFSET = 98 +SFP_VOLT_WIDTH = 2 +SFP_CHANNL_MON_OFFSET = 100 +SFP_CHANNL_MON_WIDTH = 6 +SFP_MODULE_THRESHOLD_OFFSET = 112 +SFP_MODULE_THRESHOLD_WIDTH = 5 +SFP_CHANNL_THRESHOLD_OFFSET = 112 +SFP_CHANNL_THRESHOLD_WIDTH = 6 + +class SfpUtil(SfpUtilBase): + """Platform specific sfputil class""" + + PORT_START = 0 + PORT_END = 55 + PORTS_IN_BLOCK = 55 + QSFP_PORT_START = 0 + QSFP_PORT_END = 55 + + cplda_sfp_num = 0 + cpldb_sfp_num = 28 + cpldc_sfp_num = 28 + + _port_to_eeprom_mapping = {} + port_to_i2c_mapping = {} + + @property + def port_start(self): + return self.PORT_START + + @property + def port_end(self): + return self.PORT_END + + @property + def qsfp_port_start(self): + return self.QSFP_PORT_START + + @property + def qsfp_port_end(self): + return self.QSFP_PORT_END + + @property + def qsfp_ports(self): + return range(self.QSFP_PORT_START, self.PORTS_IN_BLOCK + 1) + + @property + def port_to_eeprom_mapping(self): + return self._port_to_eeprom_mapping + + + def __init__(self): + for x in range(self.port_start, self.port_end+1): + eeprom_path = '/sys/devices/pci0000:00/0000:00:1c.4/0000:0a:00.0/sfp'+str(x+1)+'_eeprom' + self.port_to_eeprom_mapping[x] = eeprom_path + SfpUtilBase.__init__(self) + + + def get_presence(self, port_num): + if port_num < self.port_start or port_num > self.port_end: + return False + + if port_num < self.cpldb_sfp_num: + presence_path = '/sys/bus/i2c/devices/7-0075/sfp'+str(port_num+1)+'_present' + elif port_num < self.cpldb_sfp_num + self.cpldc_sfp_num: + presence_path = '/sys/bus/i2c/devices/8-0076/sfp'+str(port_num+1)+'_present' + + try: + file = open(presence_path) + except IOError as e: + print ("Error: unable to open file: %s" % str(e)) + return False + + value = int(file.readline().rstrip(),16) + + file.close() + if value & 0x1 == 1: + return True + + return False + + def get_low_power_mode(self, port_num): + if port_num not in self.qsfp_ports: + return False + + if port_num < self.cpldb_sfp_num: + lowpower_path = '/sys/bus/i2c/devices/7-0075/sfp'+str(port_num+1)+'_lowpower' + else: + lowpower_path = '/sys/bus/i2c/devices/8-0076/sfp'+str(port_num+1)+'_lowpower' + + try: + file = open(lowpower_path) + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False + + value = int(file.readline().rstrip(),16) + + file.close() + if value == 1: + return True + + return False + + def set_low_power_mode(self, port_num, lpmode): + if port_num not in self.qsfp_ports: + return False + + if port_num < self.cpldb_sfp_num: + lowpower_path = '/sys/bus/i2c/devices/7-0075/sfp'+str(port_num+1)+'_lowpower' + else: + lowpower_path = '/sys/bus/i2c/devices/8-0076/sfp'+str(port_num+1)+'_lowpower' + + # LPMode is active high; set or clear the bit accordingly + if lpmode is True: + value = 1 + else: + value = 0 + + try: + file = open(lowpower_path, "r+") + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False + + file.seek(0) + file.write(str(value)) + file.close() + + return True + + def reset(self, port_num): + if port_num not in self.qsfp_ports: + return False + if port_num < self.cpldb_sfp_num: + reset_path = '/sys/bus/i2c/devices/7-0075/sfp'+str(port_num+1)+'_reset' + else: + reset_path = '/sys/bus/i2c/devices/8-0076/sfp'+str(port_num+1)+'_reset' + + try: + file = open(reset_path, "r+") + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False + + file.seek(0) + file.write(str(1)) + file.write(str(0)) + file.close() + + # Sleep 1 second to allow it to settle + time.sleep(1) + + try: + file = open(reset_path, "r+") + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False + + file.seek(0) + file.write(str(1)) + file.close() + + return True + + def read_porttab_mappings(self, porttabfile,asic_inst=0): + logical = [] + logical_to_bcm = {} + logical_to_physical = {} + physical_to_logical = {} + last_fp_port_index = 0 + last_portname = "" + first = 1 + port_pos_in_file = 0 + parse_fmt_port_config_ini = False + + try: + f = open(porttabfile) + except: + raise + + parse_fmt_port_config_ini = (os.path.basename(porttabfile) == "port_config.ini") + + # Read the porttab file and generate dicts + # with mapping for future reference. + # XXX: move the porttab + # parsing stuff to a separate module, or reuse + # if something already exists + for line in f: + line.strip() + if re.search("^#", line) is not None: + continue + + # Parsing logic for 'port_config.ini' file + if (parse_fmt_port_config_ini): + # bcm_port is not explicitly listed in port_config.ini format + # Currently we assume ports are listed in numerical order according to bcm_port + # so we use the port's position in the file (zero-based) as bcm_port + portname = line.split()[0] + + bcm_port = str(port_pos_in_file) + + if len(line.split()) >= 4: + fp_port_index = int(line.split()[4]) + else: + fp_port_index = portname.split("Ethernet").pop() + fp_port_index = int(fp_port_index.split("s").pop(0))/4 + else: # Parsing logic for older 'portmap.ini' file + (portname, bcm_port) = line.split("=")[1].split(",")[:2] + + fp_port_index = portname.split("Ethernet").pop() + fp_port_index = int(fp_port_index.split("s").pop(0))/4 + + if ((len(self.sfp_ports) > 0) and (fp_port_index not in self.sfp_ports)): + continue + + if(fp_port_index > self.QSFP_PORT_END): + continue + + if first == 1: + # Initialize last_[physical|logical]_port + # to the first valid port + last_fp_port_index = fp_port_index + last_portname = portname + first = 0 + + logical.append(portname) + + logical_to_bcm[portname] = "xe" + bcm_port + logical_to_physical[portname] = [fp_port_index] + if physical_to_logical.get(fp_port_index) is None: + physical_to_logical[fp_port_index] = [portname] + else: + physical_to_logical[fp_port_index].append( + portname) + + if (fp_port_index - last_fp_port_index) > 1: + # last port was a gang port + for p in range(last_fp_port_index+1, fp_port_index): + logical_to_physical[last_portname].append(p) + if physical_to_logical.get(p) is None: + physical_to_logical[p] = [last_portname] + else: + physical_to_logical[p].append(last_portname) + + last_fp_port_index = fp_port_index + last_portname = portname + + port_pos_in_file += 1 + + self.logical = logical + self.logical_to_bcm = logical_to_bcm + self.logical_to_physical = logical_to_physical + self.physical_to_logical = physical_to_logical + + """ + print "logical: " + self.logical + print "logical to bcm: " + self.logical_to_bcm + print "logical to physical: " + self.logical_to_physical + print "physical to logical: " + self.physical_to_logical + """ + + @property + def _get_presence_bitmap(self): + + bits = [] + for x in range(self.port_start, self.port_end+1): + bits.append(str(int(self.get_presence(x)))) + + rev = "".join(bits[::-1]) + return int(rev,2) + + data = {'present':1} + def get_transceiver_change_event(self, timeout=0): + port_dict = {} + + if timeout == 0: + cd_ms = sys.maxsize + else: + cd_ms = timeout + + #poll per second + while cd_ms > 0: + reg_value = self._get_presence_bitmap + changed_ports = self.data['present'] ^ reg_value + if changed_ports != 0: + break + time.sleep(1) + cd_ms = cd_ms - 1000 + + if changed_ports != 0: + for port in range (self.port_start, self.port_end+1): + # Mask off the bit corresponding to our port + mask = (1 << (port - self.port_start)) + if changed_ports & mask: + if (reg_value & mask) == 0: + port_dict[port] = SFP_STATUS_REMOVED + else: + port_dict[port] = SFP_STATUS_INSERTED + + # Update cache + self.data['present'] = reg_value + return True, port_dict + else: + return True, {} + + def get_transceiver_dom_info_dict(self, port_num): + transceiver_dom_info_dict = {} + + dom_info_dict_keys = ['temperature', 'voltage', 'rx1power', + 'rx2power', 'rx3power', 'rx4power', + 'tx1bias', 'tx2bias', 'tx3bias', + 'tx4bias', 'tx1power', 'tx2power', + 'tx3power', 'tx4power', + ] + transceiver_dom_info_dict = dict.fromkeys(dom_info_dict_keys, 'N/A') + + if port_num in self.qsfp_ports: + return SfpUtilBase.get_transceiver_dom_info_dict(self, port_num) + elif port_num in self.osfp_ports: + return SfpUtilBase.get_transceiver_dom_info_dict(self, port_num) + else: + bdata='' + offset = 256 + file_path = self._get_port_eeprom_path(port_num, self.DOM_EEPROM_ADDR) + if not self._sfp_eeprom_present(file_path, 0): + return None + + try: + sysfsfile_eeprom = open(file_path, mode="rb", buffering=0) + bdata = sysfsfile_eeprom.read() + except IOError: + print("Error: reading sysfs file %s" % file_path) + return None + if len(bdata) <= 256: + sysfsfile_eeprom.close() + return None + + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return None + dom_temperature_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + SFP_TEMPE_OFFSET), SFP_TEMPE_WIDTH) + if dom_temperature_raw is not None: + dom_temperature_data = sfpd_obj.parse_temperature(dom_temperature_raw, 0) + else: + return None + + dom_voltage_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + SFP_VOLT_OFFSET), SFP_VOLT_WIDTH) + if dom_voltage_raw is not None: + dom_voltage_data = sfpd_obj.parse_voltage(dom_voltage_raw, 0) + else: + return None + + dom_channel_monitor_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + SFP_CHANNL_MON_OFFSET), SFP_CHANNL_MON_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params(dom_channel_monitor_raw, 0) + else: + return None + + try: + sysfsfile_eeprom.close() + except IOError: + print("Error: closing sysfs file %s" % file_path) + return None + + transceiver_dom_info_dict['temperature'] = dom_temperature_data['data']['Temperature']['value'] + transceiver_dom_info_dict['voltage'] = dom_voltage_data['data']['Vcc']['value'] + transceiver_dom_info_dict['rx1power'] = dom_channel_monitor_data['data']['RXPower']['value'] + transceiver_dom_info_dict['rx2power'] = 'N/A' + transceiver_dom_info_dict['rx3power'] = 'N/A' + transceiver_dom_info_dict['rx4power'] = 'N/A' + transceiver_dom_info_dict['tx1bias'] = dom_channel_monitor_data['data']['TXBias']['value'] + transceiver_dom_info_dict['tx2bias'] = 'N/A' + transceiver_dom_info_dict['tx3bias'] = 'N/A' + transceiver_dom_info_dict['tx4bias'] = 'N/A' + transceiver_dom_info_dict['tx1power'] = dom_channel_monitor_data['data']['TXPower']['value'] + transceiver_dom_info_dict['tx2power'] = 'N/A' + transceiver_dom_info_dict['tx3power'] = 'N/A' + transceiver_dom_info_dict['tx4power'] = 'N/A' + + return transceiver_dom_info_dict + + def get_transceiver_dom_threshold_info_dict(self, port_num): + transceiver_dom_threshold_info_dict = {} + + dom_info_dict_keys = ['temphighalarm', 'temphighwarning', + 'templowalarm', 'templowwarning', + 'vcchighalarm', 'vcchighwarning', + 'vcclowalarm', 'vcclowwarning', + 'rxpowerhighalarm', 'rxpowerhighwarning', + 'rxpowerlowalarm', 'rxpowerlowwarning', + 'txpowerhighalarm', 'txpowerhighwarning', + 'txpowerlowalarm', 'txpowerlowwarning', + 'txbiashighalarm', 'txbiashighwarning', + 'txbiaslowalarm', 'txbiaslowwarning' + ] + transceiver_dom_threshold_info_dict = dict.fromkeys(dom_info_dict_keys, 'N/A') + if port_num in self.qsfp_ports: + return SfpUtilBase.get_transceiver_dom_threshold_info_dict(self, port_num) + elif port_num in self.osfp_ports: + # Below part is added to avoid fail xcvrd, shall be implemented later + return SfpUtilBase.get_transceiver_dom_threshold_info_dict(self, port_num) + else: + offset = 256 + bdata='' + file_path = self._get_port_eeprom_path(port_num, self.DOM_EEPROM_ADDR) + if not self._sfp_eeprom_present(file_path, 0): + return None + + try: + sysfsfile_eeprom = open(file_path, mode="rb", buffering=0) + bdata = sysfsfile_eeprom.read() + except IOError: + print("Error: reading sysfs file %s" % file_path) + return None + if len(bdata) <= 256: + sysfsfile_eeprom.close() + return None + + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return None + + dom_module_threshold_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, + (offset + SFP_MODULE_THRESHOLD_OFFSET), + SFP_MODULE_THRESHOLD_WIDTH) + if dom_module_threshold_raw is not None: + dom_module_threshold_data = sfpd_obj.parse_module_monitor_params(dom_module_threshold_raw, 0) + else: + return None + + dom_channel_threshold_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, + (offset + SFP_CHANNL_THRESHOLD_OFFSET), + SFP_CHANNL_THRESHOLD_WIDTH) + if dom_channel_threshold_raw is not None: + dom_channel_threshold_data = sfpd_obj.parse_channel_thresh_monitor_params(dom_channel_threshold_raw, 0) + else: + return None + + try: + sysfsfile_eeprom.close() + except IOError: + print("Error: closing sysfs file %s" % file_path) + return None + + # Threshold Data + transceiver_dom_threshold_info_dict['temphighalarm'] = dom_module_threshold_data['data']['TempHighAlarm']['value'] + transceiver_dom_threshold_info_dict['templowalarm'] = dom_module_threshold_data['data']['TempLowAlarm']['value'] + transceiver_dom_threshold_info_dict['temphighwarning'] = dom_module_threshold_data['data']['TempHighWarning']['value'] + transceiver_dom_threshold_info_dict['templowwarning'] = dom_module_threshold_data['data']['TempLowWarning']['value'] + transceiver_dom_threshold_info_dict['vcchighalarm'] = dom_module_threshold_data['data']['VccHighAlarm']['value'] + transceiver_dom_threshold_info_dict['vcclowalarm'] = dom_module_threshold_data['data']['VccLowAlarm']['value'] + transceiver_dom_threshold_info_dict['vcchighwarning'] = dom_module_threshold_data['data']['VccHighWarning']['value'] + transceiver_dom_threshold_info_dict['vcclowwarning'] = dom_module_threshold_data['data']['VccLowWarning']['value'] + transceiver_dom_threshold_info_dict['txbiashighalarm'] = dom_channel_threshold_data['data']['BiasHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiaslowalarm'] = dom_channel_threshold_data['data']['BiasLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiashighwarning'] = dom_channel_threshold_data['data']['BiasHighWarning']['value'] + transceiver_dom_threshold_info_dict['txbiaslowwarning'] = dom_channel_threshold_data['data']['BiasLowWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerhighalarm'] = dom_channel_threshold_data['data']['TxPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerlowalarm'] = dom_channel_threshold_data['data']['TxPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerhighwarning'] = dom_channel_threshold_data['data']['TxPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerlowwarning'] = dom_channel_threshold_data['data']['TxPowerLowWarning']['value'] + transceiver_dom_threshold_info_dict['rxpowerhighalarm'] = dom_channel_threshold_data['data']['RxPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowalarm'] = dom_channel_threshold_data['data']['RxPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerhighwarning'] = dom_channel_threshold_data['data']['RxPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowwarning'] = dom_channel_threshold_data['data']['RxPowerLowWarning']['value'] + + return transceiver_dom_threshold_info_dict diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/pmon_daemon_control.json b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/pmon_daemon_control.json new file mode 100644 index 000000000000..a1e8eac808f1 --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/pmon_daemon_control.json @@ -0,0 +1,6 @@ +{ + "skip_ledd": true, + "skip_xcvrd": false, + "skip_pcied": false, + "skip_syseepromd": false +} diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/sensors.conf b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/sensors.conf new file mode 100644 index 000000000000..c43ef94a7173 --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/sensors.conf @@ -0,0 +1,42 @@ +# libsensors configuration file for Clounix. +# The i2c bus portion is omit because adapter name +# changes every time when system boot up. + +chip "lm75b-i2c-*-4a" + label temp1 "LM75BD / Ambient NPU" + set temp1_max 80 + set temp1_max_hyst 95 + +chip "pega_hwmon_mcu-i2c-*-70" + label temp1 "LM75BD / Ambient MAC side" + label temp2 "LM75BD / Ambient MAC" + label temp3 "LM75BD / Ambient FAN" + set temp1_max 80 + set temp2_max 80 + set temp3_max 80 + set temp1_crit 90 + set temp2_crit 90 + set temp3_crit 90 + +chip "fn8656_bnf_psu-i2c-*-58" + label temp1 "PSU1 Ambient" + label temp2 "PSU1 SR Hotspot" + label temp3 "PSU1 PFC Hotspot" + set temp1_max 60 + set temp2_max 80 + set temp3_max 80 + set temp1_crit 65 #no higher than 65,manufacture temperature max + set temp2_crit 122 #no higher than 122,manufacture temperature max + set temp3_crit 96 #no higher than 96,manufacture temperature max + +chip "fn8656_bnf_psu-i2c-*-59" + label temp1 "PSU2 Ambient" + label temp2 "PSU2 SR Hotspot" + label temp3 "PSU2 PFC Hotspot" + set temp1_max 60 + set temp2_max 80 + set temp3_max 80 + set temp1_crit 65 #no higher than 65,manufacture temperature max + set temp2_crit 122 #no higher than 122,manufacture temperature max + set temp3_crit 96 #no higher than 96,manufacture temperature max + \ No newline at end of file diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/system_health_monitoring_config.json b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/system_health_monitoring_config.json new file mode 100644 index 000000000000..acf607dc4c3a --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/system_health_monitoring_config.json @@ -0,0 +1,11 @@ +{ + "services_to_ignore": [], + "devices_to_ignore": ["asic"], + "user_defined_checkers": [], + "polling_interval": 60, + "led_color": { + "fault": "red", + "normal": "green", + "booting": "blink_green" + } +} diff --git a/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/thermal_policy.json b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/thermal_policy.json new file mode 100644 index 000000000000..f67c982f8681 --- /dev/null +++ b/device/pegatron/x86_64-pegatron_fn8656_bnf-r0/thermal_policy.json @@ -0,0 +1,31 @@ +{ + "thermal_control_algorithm": { + "run_at_boot_up": "true" + }, + "info_types": [ + { + "type": "chassis_info" + }, + { + "type": "fan_info" + }, + { + "type": "thermal_info" + } + ], + "policies": [ + { + "name": "temp over high critical threshold", + "conditions": [ + { + "type": "thermal.over.high_critical_threshold" + } + ], + "actions": [ + { + "type": "switch.power_cycling" + } + ] + } + ] +} \ No newline at end of file diff --git a/files/build_templates/per_namespace/syncd.service.j2 b/files/build_templates/per_namespace/syncd.service.j2 index 9acf97c29fd4..e099a6986725 100644 --- a/files/build_templates/per_namespace/syncd.service.j2 +++ b/files/build_templates/per_namespace/syncd.service.j2 @@ -13,6 +13,9 @@ After=opennsl-modules.service {% elif sonic_asic_platform == 'nephos' %} Requires=nps-modules.service After=nps-modules.service +{% elif sonic_asic_platform == 'clounix' %} +Requires=clx-modules.service +After=clx-modules.service {% endif %} Requires=updategraph.service After=updategraph.service diff --git a/platform/clounix/clounix-modules.dep b/platform/clounix/clounix-modules.dep new file mode 100644 index 000000000000..0bbfc3e4c2ca --- /dev/null +++ b/platform/clounix/clounix-modules.dep @@ -0,0 +1,11 @@ + +SPATH := $($(CLOUNIX_MODULE)_SRC_PATH) +DEP_FILES := $(SONIC_COMMON_FILES_LIST) platform/clounix/clounix-modules.mk platform/clounix/clounix-modules.dep +DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST) +SMDEP_FILES := $(addprefix $(SPATH)/,$(shell cd $(SPATH) && git ls-files)) + +$(CLOUNIX_MODULE)_CACHE_MODE := GIT_CONTENT_SHA +$(CLOUNIX_MODULE)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST) +$(CLOUNIX_MODULE)_DEP_FILES := $(DEP_FILES) +$(CLOUNIX_MODULE)_SMDEP_FILES := $(SMDEP_FILES) +$(CLOUNIX_MODULE)_SMDEP_PATHS := $(SPATH) diff --git a/platform/clounix/clounix-modules.mk b/platform/clounix/clounix-modules.mk new file mode 100644 index 000000000000..34c419d26eb7 --- /dev/null +++ b/platform/clounix/clounix-modules.mk @@ -0,0 +1,8 @@ +# Clounix Platform modules + +VERSION = 1.0.1 + +CLOUNIX_MODULE = clounix-modules_$(VERSION)_amd64.deb +$(CLOUNIX_MODULE)_SRC_PATH = $(PLATFORM_PATH)/clounix-modules +$(CLOUNIX_MODULE)_DEPENDS += $(LINUX_HEADERS) $(LINUX_HEADERS_COMMON) $(CLOUNIX_SAI) +SONIC_DPKG_DEBS += $(CLOUNIX_MODULE) diff --git a/platform/clounix/clounix-modules/README.md b/platform/clounix/clounix-modules/README.md new file mode 100644 index 000000000000..7dcf5034bd6e --- /dev/null +++ b/platform/clounix/clounix-modules/README.md @@ -0,0 +1,2 @@ +# Clounix-modules +Device drivers for support of Clounix platform for the SONiC project diff --git a/platform/clounix/clounix-modules/debian/changelog b/platform/clounix/clounix-modules/debian/changelog new file mode 100644 index 000000000000..581abba03fd8 --- /dev/null +++ b/platform/clounix/clounix-modules/debian/changelog @@ -0,0 +1,11 @@ +clounix-modules (1.0.1) unstable; urgency=low + + * Upgrade ko version to 3.0.0 + + -- Support Tue, 17 Mar 2020 15:54:00 +0800 + +clounix-modules (1.0.0) unstable; urgency=low + + * Initial release + + -- Support Fri, 15 Mar 2019 15:54:00 +0800 diff --git a/platform/clounix/clounix-modules/debian/compat b/platform/clounix/clounix-modules/debian/compat new file mode 100644 index 000000000000..ec635144f600 --- /dev/null +++ b/platform/clounix/clounix-modules/debian/compat @@ -0,0 +1 @@ +9 diff --git a/platform/clounix/clounix-modules/debian/control b/platform/clounix/clounix-modules/debian/control new file mode 100644 index 000000000000..184b102e5123 --- /dev/null +++ b/platform/clounix/clounix-modules/debian/control @@ -0,0 +1,12 @@ +Source: clounix-modules +Section: main +Priority: extra +Maintainer: support +Build-Depends: debhelper (>= 8.0.0), bzip2 +Standards-Version: 3.9.3 + +Package: clounix-modules +Architecture: amd64 +Depends: linux-image-4.19.0-12-2-amd64-unsigned +Description: kernel modules for clounix asic + diff --git a/platform/clounix/clounix-modules/debian/rules b/platform/clounix/clounix-modules/debian/rules new file mode 100755 index 000000000000..27e4a94db35b --- /dev/null +++ b/platform/clounix/clounix-modules/debian/rules @@ -0,0 +1,34 @@ +#!/usr/bin/make -f +export INSTALL_MOD_DIR:=extra + +PACKAGE_NAME := clounix-modules +KVERSION ?= $(shell uname -r) +KERNEL_SRC := /lib/modules/$(KVERSION) +SERVICE_DIR := service +INITD_DIR := init.d +MODULE_SRC := $(shell pwd)/modules +CURRENT_DIR := $(cd "$(dirname "$0")"; pwd) + +%: + dh $@ + +override_dh_auto_build: + ls + make -C $(MODULE_SRC) + +override_dh_auto_install: + dh_installdirs -p$(PACKAGE_NAME) /lib/systemd/system + cp $(MODULE_SRC)/service/*.service debian/$(PACKAGE_NAME)/lib/systemd/system/ + dh_installdirs -p$(PACKAGE_NAME) /etc/init.d + cp $(MODULE_SRC)/init.d/* debian/$(PACKAGE_NAME)/etc/init.d/ + dh_installdirs -p$(PACKAGE_NAME) $(KERNEL_SRC)/extra/ + cp $(MODULE_SRC)/build/module/*.ko debian/$(PACKAGE_NAME)/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); + +override_dh_usrlocal: + +override_dh_pysupport: + +override_dh_clean: + dh_clean + test -d $(MODULE_SRC)/build || rm -rf $(MODULE_SRC)/build + diff --git a/platform/clounix/clounix-modules/modules/Makefile b/platform/clounix/clounix-modules/modules/Makefile new file mode 100755 index 000000000000..c424338180f3 --- /dev/null +++ b/platform/clounix/clounix-modules/modules/Makefile @@ -0,0 +1,94 @@ +################################################################################ +# Copyright (C) 2021 Clounix, Inc. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of version 2 of the GNU General Public +# License as published by the Free Software Foundation. +# +# 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 +# version 2 along with this program. +################################################################################ +################################################################################ +KERNEL_MODULE := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) + +################################################################################ +MAKE := $(shell which make) +RM := rm -rf +MKDIR := mkdir -p +CP := cp +MV := mv +TEST_PATH := test -d + +#KVERSION ?= $(shell uname -r) +KVERSION ?= 4.19.0-12-2-amd64 +OS_PATH := /lib/modules/$(KVERSION)/build +SRC_PATH := $(KERNEL_MODULE)/src +INC_PATH := $(SRC_PATH)/inc +BUILD_OUTPUT_DIR := $(KERNEL_MODULE)/build +MODULE_OUTPUT_DIR := $(BUILD_OUTPUT_DIR)/module + +all: compile install +################################################################################ +EXTRA_CFLAGS += -I$(INC_PATH) +EXTRA_CFLAGS += -I$(SRC_PATH)/clx_netif/inc/ +EXTRA_CFLAGS += -DCLX_EN_NETIF +EXTRA_CFLAGS += -DCLX_EN_DAWN +EXTRA_CFLAGS += -DCLX_EN_LIGHTNING +EXTRA_CFLAGS += -DCLX_EN_DEBUG +EXTRA_CFLAGS += -DCLX_LINUX_USER_MODE +EXTRA_CFLAGS += -DCLX_EN_LITTLE_ENDIAN +EXTRA_CFLAGS += -DCLX_EN_COMPILER_SUPPORT_FUNCTION +EXTRA_CFLAGS += -DCLX_EN_COMPILER_SUPPORT_LONG_LONG + +ifeq ($(shell uname -m),x86_64) +EXTRA_CFLAGS += -DCLX_EN_HOST_64_BIT_LITTLE_ENDIAN +EXTRA_CFLAGS += -DCLX_EN_64BIT_ADDR +else +EXTRA_CFLAGS += -DCLX_EN_HOST_32_BIT_LITTLE_ENDIAN +endif + +################################################################################ +DEV_MODULE_NAME := clx_dev +NETIF_MODULE_NAME := clx_netif + +################################################################################ +DEV_OBJS_TOTAL := ./src/clx_dev/osal_mdc.o +DEV_OBJS_TOTAL += ./src/clx_dev/osal_isymbol.o + +NETIF_OBJS_TOTAL := ./src/clx_netif/netif_knl.o +NETIF_OBJS_TOTAL += ./src/clx_netif/netif_osal.o +NETIF_OBJS_TOTAL += ./src/clx_netif/netif_perf.o +NETIF_OBJS_TOTAL += ./src/clx_netif/netif_nl.o +NETIF_OBJS_TOTAL += ./src/clx_netif/hal_dawn_pkt_knl.o +NETIF_OBJS_TOTAL += ./src/clx_netif/hal_lightning_pkt_knl.o + +obj-m := $(DEV_MODULE_NAME).o $(NETIF_MODULE_NAME).o +$(DEV_MODULE_NAME)-objs := $(DEV_OBJS_TOTAL) +$(NETIF_MODULE_NAME)-objs := $(NETIF_OBJS_TOTAL) + +KBUILD_EXTRA_SYMBOLS := $(BUILD_OUTPUT_DIR)/Module.symvers +################################################################################ +folder: + $(TEST_PATH) $(BUILD_OUTPUT_DIR) || $(MKDIR) $(BUILD_OUTPUT_DIR) + $(TEST_PATH) $(BUILD_OUTPUT_DIR)/src || $(MKDIR) $(BUILD_OUTPUT_DIR)/src + +compile:: folder + touch $(BUILD_OUTPUT_DIR)/Makefile + $(MAKE) -C $(OS_PATH) M=$(BUILD_OUTPUT_DIR) src=$(shell pwd) modules EXTRA_CFLAGS="$(EXTRA_CFLAGS)" KBUILD_EXTRA_SYMBOLS=$(KBUILD_EXTRA_SYMBOLS) + +install:: + $(TEST_PATH) $(MODULE_OUTPUT_DIR) || $(MKDIR) $(MODULE_OUTPUT_DIR) + $(MV) $(BUILD_OUTPUT_DIR)/$(DEV_MODULE_NAME).ko $(MODULE_OUTPUT_DIR)/$(DEV_MODULE_NAME).ko + $(MV) $(BUILD_OUTPUT_DIR)/$(NETIF_MODULE_NAME).ko $(MODULE_OUTPUT_DIR)/$(NETIF_MODULE_NAME).ko + +clean:: + $(RM) $(BUILD_OUTPUT_DIR) + +.PHONY: all compile install clean +.NOTPARALLEL: all compile install clean + diff --git a/platform/clounix/clounix-modules/modules/README b/platform/clounix/clounix-modules/modules/README new file mode 100644 index 000000000000..796fe7de82e1 --- /dev/null +++ b/platform/clounix/clounix-modules/modules/README @@ -0,0 +1,32 @@ +################################################################################ +# Copyright (C) 2021 Clounix, Inc. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of version 2 of the GNU General Public +# License as published by the Free Software Foundation. +# +# 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 +# version 2 along with this program. +################################################################################ +Step 1~4 show how to build and execute CLX kernel modules. + +1. Modify clounix-modules/Makefile to specify the output directory to BUILD_OUTPUT_DIR. + The default output path is clounix-modules/build. + +2. Compile: + cd clounix-modules/ && make + +3. The output kernel modules will be found in $(BUILD_OUTPUT_DIR)/modules/ + - clx_dev.ko + - clx_netif.ko + +4. Load modules: + (1) insmod clx_dev.ko + (2) insmod clx_netif.ko + + Note that the module inserting sequence cannot be changed. diff --git a/platform/clounix/clounix-modules/modules/init.d/clx-modules b/platform/clounix/clounix-modules/modules/init.d/clx-modules new file mode 100755 index 000000000000..d78678b7b469 --- /dev/null +++ b/platform/clounix/clounix-modules/modules/init.d/clx-modules @@ -0,0 +1,62 @@ +#!/bin/bash +# This script load/unload clx kernel modules + +### BEGIN INIT INFO +# Provides: load-clx-modules +# Required-Start: +# Required-Stop: +# Should-Start: +# Should-Stop: +# Default-Start: S +# Default-Stop: 0 6 +# Short-Description: Load clx kernel modules +### END INIT INFO + +case "$1" in +start) + echo -n "Load clx kernel modules... " + + RMEM_SIZE=`cat /proc/sys/net/core/rmem_max` + if [ $RMEM_SIZE -lt 8388608 ]; then + echo "8388608" > /proc/sys/net/core/rmem_max + fi + WMEM_SIZE=`cat /proc/sys/net/core/wmem_max` + if [ $WMEM_SIZE -lt 25165824 ]; then + echo "25165824" > /proc/sys/net/core/wmem_max + fi + + if ! modprobe clx_dev ; then + modprobe nps_dev + fi + if ! modprobe clx_netif ; then + modprobe nps_netif + fi + + echo "done." + ;; + +stop) + echo -n "Unload clx kernel modules... " + + if ! rmmod clx_netif ; then + rmmod nps_netif + fi + if ! rmmod clx_dev ; then + rmmod nps_dev + fi + + echo "done." + ;; + +force-reload|restart) + echo "Not supported" + ;; + +*) + echo "Usage: /etc/init.d/clx-modules {start|stop}" + exit 1 + ;; +esac + +exit 0 + diff --git a/platform/clounix/clounix-modules/modules/service/clx-modules.service b/platform/clounix/clounix-modules/modules/service/clx-modules.service new file mode 100644 index 000000000000..5adb42bfece1 --- /dev/null +++ b/platform/clounix/clounix-modules/modules/service/clx-modules.service @@ -0,0 +1,13 @@ +[Unit] +Description=Clounix kernel modules init +After=local-fs.target +Before=syncd.service + +[Service] +Type=oneshot +ExecStart=-/etc/init.d/clx-modules start +ExecStop=-/etc/init.d/clx-modules stop +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/platform/clounix/clounix-modules/modules/src/clx_dev/osal_isymbol.c b/platform/clounix/clounix-modules/modules/src/clx_dev/osal_isymbol.c new file mode 100644 index 000000000000..f9036ad243c8 --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/clx_dev/osal_isymbol.c @@ -0,0 +1,45 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: osal_isymbol.c +* PURPOSE: +* It provide global OSAL symbol export for linux kernel module +* NOTES: +*/ +#include +#include + +/* ----------------------------------------------------- */ +#include +/* dma */ +extern struct pci_dev *_ptr_ext_pci_dev; +EXPORT_SYMBOL(_ptr_ext_pci_dev); + +#if defined(CLX_LINUX_KERNEL_MODE) +#include +extern CLX_INIT_WRITE_FUNC_T _ext_dsh_write_func; +EXPORT_SYMBOL(_ext_dsh_write_func); +#endif + +#if defined(CLX_LINUX_USER_MODE) +EXPORT_SYMBOL(osal_mdc_readPciReg); +EXPORT_SYMBOL(osal_mdc_writePciReg); +#if defined(CLX_EN_NETIF) +/* intr */ +/* for kernel module, this API will be exported by script with other OSAL functions in osal_symbol.c */ +EXPORT_SYMBOL(osal_mdc_registerIsr); +#endif +#endif diff --git a/platform/clounix/clounix-modules/modules/src/clx_dev/osal_mdc.c b/platform/clounix/clounix-modules/modules/src/clx_dev/osal_mdc.c new file mode 100644 index 000000000000..2bc0246a6e6a --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/clx_dev/osal_mdc.c @@ -0,0 +1,2587 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: osal_mdc.c + * PURPOSE: + * 1. Provide device operate from AML interface + * NOTES: + * + */ + +/* INCLUDE FILE DECLARATIONS + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(CLX_LINUX_USER_MODE) +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,12,0) +#include +#else +#include +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0) +#if defined(OSAL_MDC_DMA_RESERVED_MEM_CACHEABLE) + #define IOREMAP_API(a, b) ioremap(a, b) +#else + #define IOREMAP_API(a, b) ioremap_nocache(a, b) +#endif +#else + #define IOREMAP_API(a, b) ioremap(a, b) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +/* #define OSAL_MDC_EN_MSI */ +/* #define OSAL_MDC_DMA_RESERVED_MEM_CACHEABLE */ +/* #define OSAL_MDC_EN_TEST */ + +/* NAMING CONSTANT DECLARATIONS + */ +#define OSAL_MDC_PCI_BAR0_OFFSET (0x0) +#define OSAL_MDC_ERR printk + +/* MACRO FUNCTION DECLARATIONS + */ + +/* DATA TYPE DECLARATIONS + */ +typedef struct +{ + UI32_T unit; + struct pci_dev *ptr_pci_dev; + UI32_T *ptr_mmio_virt_addr; + int irq; + AML_DEV_ISR_FUNC_T isr_callback; + void *ptr_isr_data; + +} OSAL_MDC_DEV_T; + +typedef struct +{ + OSAL_MDC_DEV_T dev[CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM]; + UI32_T dev_num; + OSAL_MDC_DMA_INFO_T dma_info; + +} OSAL_MDC_CB_T; + +#if defined(CLX_LINUX_USER_MODE) + +typedef struct +{ + OSAL_MDC_IOCTL_CALLBACK_FUNC_T callback[OSAL_MDC_IOCTL_TYPE_LAST]; + +} OSAL_MDC_IOCTL_CB_T; + +#if !defined(CLX_EN_DMA_RESERVED) +typedef struct +{ + CLX_ADDR_T phy_addr; + UI32_T size; + struct list_head list; + +} OSAL_MDC_USER_MODE_DMA_NODE_T; +#endif + +#endif + + +#if defined(CLX_LINUX_KERNEL_MODE) + +/* re-define the interface to align OSAL_MDC's implementation with the prototype of CMLIB */ +#define osal_mdc_list_create(__capa__, __type__, __name__, __list__) _osal_mdc_list_create(__capa__, __list__) +#define osal_mdc_list_destroy(__list__, __callback__) _osal_mdc_list_destroy(__list__) +#define osal_mdc_list_getNodeData(__list__, __node__, __data__) _osal_mdc_list_getNodeData(__list__, __node__, __data__) +#define osal_mdc_list_next(__list__, __node__, __next__) _osal_mdc_list_next(__list__, __node__, __next__) +#define osal_mdc_list_locateHead(__list__, __node__) _osal_mdc_list_locateHead(__list__, __node__) +#define osal_mdc_list_insertToHead(__list__, __data__) _osal_mdc_list_insertToHead(__list__, __data__) +#define osal_mdc_list_deleteByData(__list__, __data__) _osal_mdc_list_deleteByData(__list__, __data__) + +#if defined(CLX_EN_DMA_RESERVED) +#define osal_mdc_list_insertBefore(__list__, __node__, __data__) _osal_mdc_list_insertBefore(__list__, __node__, __data__) +#define osal_mdc_list_prev(__list__, __node__, __prev__) _osal_mdc_list_prev(__list__, __node__, __prev__) +#endif + +#define OSAL_MDC_LIST_TYPE_DOUBLE (1) /* don't care the type, always be double */ +#define OSAL_MDC_LIST_TYPE_SINGLE (0) /* don't care the type, always be double */ + +static CLX_ERROR_NO_T +_osal_mdc_list_create( + const UI32_T capacity, + OSAL_MDC_LIST_T **pptr_list ) +{ + CLX_ERROR_NO_T rc = CLX_E_NO_MEMORY; + + *pptr_list = NULL; + + *pptr_list = osal_alloc(sizeof(OSAL_MDC_LIST_T)); + if (NULL != *pptr_list) + { + (*pptr_list)->capacity = capacity; + (*pptr_list)->node_cnt = 0; + (*pptr_list)->ptr_head_node = NULL; + (*pptr_list)->ptr_tail_node = NULL; + rc = CLX_E_OK; + } + + return (rc); +} + +static CLX_ERROR_NO_T +_osal_mdc_list_destroy( + OSAL_MDC_LIST_T *ptr_list ) +{ + OSAL_MDC_LIST_NODE_T *ptr_cur_node, *ptr_next_node; + CLX_ERROR_NO_T rc = CLX_E_OTHERS; + + if (NULL != ptr_list) + { + if (ptr_list->node_cnt != 0) + { + OSAL_MDC_ERR("dma list not empty, node num=%d\n", + ptr_list->node_cnt); + ptr_cur_node = ptr_list->ptr_head_node; + while(NULL != ptr_cur_node) + { + ptr_next_node = ptr_cur_node->ptr_next; + osal_free(ptr_cur_node); + ptr_list->node_cnt--; + ptr_cur_node = ptr_next_node; + } + } + + osal_free(ptr_list); + rc = CLX_E_OK; + } + + return (rc); +} + +static CLX_ERROR_NO_T +_osal_mdc_list_getNodeData( + OSAL_MDC_LIST_T *ptr_list, + OSAL_MDC_LIST_NODE_T *ptr_node, + void **pptr_node_data ) +{ + CLX_ERROR_NO_T rc = CLX_E_OTHERS; + + if (NULL != ptr_list) + { + if (NULL != ptr_node) + { + *pptr_node_data = ptr_node->ptr_data; + rc = CLX_E_OK; + } + } + + return (rc); +} + +static CLX_ERROR_NO_T +_osal_mdc_list_insertToHead( + OSAL_MDC_LIST_T *ptr_list, + void *ptr_data ) +{ + OSAL_MDC_LIST_NODE_T *ptr_new_node = NULL; + CLX_ERROR_NO_T rc = CLX_E_OTHERS; + + if (NULL != ptr_list) + { + ptr_new_node = osal_alloc(sizeof(OSAL_MDC_LIST_NODE_T)); + if (NULL != ptr_new_node) + { + ptr_new_node->ptr_data = ptr_data; + + /* no former node */ + ptr_new_node->ptr_prev = NULL; + + if (NULL != ptr_list->ptr_head_node) + { + ptr_list->ptr_head_node->ptr_prev = ptr_new_node; + ptr_new_node->ptr_next = ptr_list->ptr_head_node; + } + else + { + /* 1st node insertion */ + ptr_list->ptr_tail_node = ptr_new_node; + ptr_new_node->ptr_next = NULL; + } + + ptr_list->ptr_head_node = ptr_new_node; + ptr_list->node_cnt++; + rc = CLX_E_OK; + } + } + + return (rc); +} + +#if defined(CLX_EN_DMA_RESERVED) +static CLX_ERROR_NO_T +_osal_mdc_list_insertBefore( + OSAL_MDC_LIST_T *ptr_list, + OSAL_MDC_LIST_NODE_T *ptr_node, + void *ptr_data ) +{ + OSAL_MDC_LIST_NODE_T *ptr_new_node = NULL; + OSAL_MDC_LIST_NODE_T *ptr_prev_node = ptr_node->ptr_prev; + CLX_ERROR_NO_T rc = CLX_E_OTHERS; + + if (NULL != ptr_list) + { + if (NULL != ptr_node) + { + ptr_new_node = osal_alloc(sizeof(OSAL_MDC_LIST_NODE_T)); + if (NULL != ptr_new_node) + { + ptr_new_node->ptr_data = ptr_data; + + /* location */ + if (NULL != ptr_prev_node) + { + ptr_prev_node->ptr_next = ptr_new_node; + } + ptr_new_node->ptr_prev = ptr_prev_node; + ptr_new_node->ptr_next = ptr_node; + ptr_node->ptr_prev = ptr_new_node; + + /* update head if necessary */ + if (ptr_list->ptr_head_node == ptr_node) + { + ptr_list->ptr_head_node = ptr_new_node; + } + + ptr_list->node_cnt++; + rc = CLX_E_OK; + } + } + } + + return (rc); +} +#endif + +static CLX_ERROR_NO_T +_osal_mdc_list_deleteTargetNode( + OSAL_MDC_LIST_T *ptr_list, + OSAL_MDC_LIST_NODE_T *ptr_target_node) +{ + OSAL_MDC_LIST_NODE_T *ptr_prev_node = ptr_target_node->ptr_prev; + OSAL_MDC_LIST_NODE_T *ptr_next_node = ptr_target_node->ptr_next; + + if (ptr_target_node == ptr_list->ptr_head_node) + { + ptr_list->ptr_head_node = ptr_next_node; + if (NULL != ptr_next_node) + { + ptr_next_node->ptr_prev = NULL; + } + else + { + /* there's only 1 node in the list, and it gets removed */ + ptr_list->ptr_tail_node = NULL; + } + } + else if (ptr_target_node == ptr_list->ptr_tail_node) + { + /* at least 2 nodes in the list, and the target node locates tail */ + ptr_list->ptr_tail_node = ptr_prev_node; + ptr_prev_node->ptr_next = NULL; + } + else + { + /* intermediate node */ + ptr_prev_node->ptr_next = ptr_next_node; + ptr_next_node->ptr_prev = ptr_prev_node; + } + + osal_free(ptr_target_node); + ptr_list->node_cnt--; + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_osal_mdc_list_deleteByData( + OSAL_MDC_LIST_T *ptr_list, + void *ptr_delete_data) +{ + OSAL_MDC_LIST_NODE_T *ptr_tmp_node; + CLX_ERROR_NO_T rc = CLX_E_OTHERS; + + if (NULL != ptr_list) + { + ptr_tmp_node = ptr_list->ptr_head_node; + while (NULL != ptr_tmp_node) + { + if (ptr_tmp_node->ptr_data == ptr_delete_data) + { + rc = _osal_mdc_list_deleteTargetNode(ptr_list, ptr_tmp_node); + break; + } + else + { + ptr_tmp_node = ptr_tmp_node->ptr_next; + } + } + } + + return (rc); +} + + +static CLX_ERROR_NO_T +_osal_mdc_list_locateHead( + OSAL_MDC_LIST_T *ptr_list, + OSAL_MDC_LIST_NODE_T **pptr_node ) +{ + CLX_ERROR_NO_T rc = CLX_E_OTHERS; + + if (NULL != ptr_list) + { + *pptr_node = ptr_list->ptr_head_node; + if (NULL != *pptr_node) + { + rc = CLX_E_OK; + } + } + + return (rc); +} + +static CLX_ERROR_NO_T +_osal_mdc_list_next( + OSAL_MDC_LIST_T *ptr_list, + OSAL_MDC_LIST_NODE_T *ptr_node, + OSAL_MDC_LIST_NODE_T **pptr_next_node ) +{ + CLX_ERROR_NO_T rc = CLX_E_OTHERS; + + if (NULL != ptr_list) + { + if (NULL != ptr_node) + { + *pptr_next_node = ptr_node->ptr_next; + if (NULL != *pptr_next_node) + { + rc = CLX_E_OK; + } + } + } + + return (rc); +} + +#if defined(CLX_EN_DMA_RESERVED) +static CLX_ERROR_NO_T +_osal_mdc_list_prev( + OSAL_MDC_LIST_T *ptr_list, + OSAL_MDC_LIST_NODE_T *ptr_node, + OSAL_MDC_LIST_NODE_T **pptr_prev_node ) +{ + CLX_ERROR_NO_T rc = CLX_E_OTHERS; + + if (NULL != ptr_list) + { + if (NULL != ptr_node) + { + *pptr_prev_node = ptr_node->ptr_prev; + if (NULL != *pptr_prev_node) + { + rc = CLX_E_OK; + } + } + } + + return (rc); +} +#endif /* End of defined(CLX_EN_DMA_RESERVED) */ + +#endif /* End if defined(CLX_LINUX_KERNEL_MODE) */ + +/* GLOBAL VARIABLE DECLARATIONS + */ +static OSAL_MDC_CB_T _osal_mdc_cb; + +/* To let system callback function to access AML database */ +static AML_DEV_T *_ptr_osal_mdc_dev; + +/* Interface */ +struct pci_dev *_ptr_ext_pci_dev; + + +/* STATIC VARIABLE DECLARATIONS + */ +/* --------------------------------------------------------------------------- I2C interface */ +#if defined(AML_EN_I2C) +extern CLX_ERROR_NO_T +dev_switch_readBuffer( + const UI32_T addr, + const UI32_T addr_len, + UI8_T *ptr_buf, + const UI32_T buf_len); + +extern CLX_ERROR_NO_T +dev_switch_writeBuffer( + const UI32_T addr, + const UI32_T addr_len, + const UI8_T *ptr_buf, + const UI32_T buf_len); + +static CLX_ERROR_NO_T +_osal_mdc_readI2cReg( + const UI32_T unit, + const UI32_T offset, + UI32_T *ptr_data, + const UI32_T len) +{ + return dev_switch_readBuffer(offset, sizeof(offset), (UI8_T *)ptr_data, len); +} + +static CLX_ERROR_NO_T +_osal_mdc_writeI2cReg( + const UI32_T unit, + const UI32_T offset, + const UI32_T *ptr_data, + const UI32_T len) +{ + return dev_switch_writeBuffer(offset, sizeof(offset), (UI8_T *)ptr_data, len); +} + +static CLX_ERROR_NO_T +_osal_mdc_probeI2cDevice(void) +{ + /* I2C interface will be probed in BSP. */ + _ptr_osal_mdc_dev->if_type = AML_DEV_TYPE_I2C; + _ptr_osal_mdc_dev->access.read_callback = _osal_mdc_readI2cReg; + _ptr_osal_mdc_dev->access.write_callback = _osal_mdc_writeI2cReg; + + _ptr_osal_mdc_dev->id.device = HAL_DEVICE_ID_CL3258; + _ptr_osal_mdc_dev->id.vendor = HAL_CLX_VENDOR_ID; + _ptr_osal_mdc_dev->id.revision = HAL_REVISION_ID_E2; + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_osal_mdc_removeI2cDevice(void) +{ + /* I2C interface will be removed in BSP. */ + return (CLX_E_OK); +} + +/* --------------------------------------------------------------------------- PCI interface */ +#else + +static CLX_ERROR_NO_T +_osal_mdc_getPciMmioInfo( + struct pci_dev *pdev, + UI32_T **pptr_base_addr) +{ + CLX_ERROR_NO_T rc = CLX_E_OTHERS; + CLX_ADDR_T phy_addr; + UI32_T reg_space_sz; + + phy_addr = pci_resource_start(pdev, OSAL_MDC_PCI_BAR0_OFFSET); + reg_space_sz = pci_resource_len(pdev, OSAL_MDC_PCI_BAR0_OFFSET); + + if (0 == pci_request_region(pdev, OSAL_MDC_PCI_BAR0_OFFSET, OSAL_MDC_DRIVER_NAME)) + { + *pptr_base_addr = IOREMAP_API(phy_addr, reg_space_sz); + if (NULL != *pptr_base_addr) + { + rc = CLX_E_OK; + } + } + return (rc); +} + +CLX_ERROR_NO_T +osal_mdc_readPciReg( + const UI32_T unit, + const UI32_T offset, + UI32_T *ptr_data, + const UI32_T len) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + UI32_T idx; + UI32_T count; + volatile UI32_T *ptr_base_addr = _osal_mdc_cb.dev[unit].ptr_mmio_virt_addr; + + if (NULL != ptr_base_addr) + { + if (OSAL_MDC_PCI_BUS_WIDTH == len) + { + *ptr_data = *((UI32_T *)((CLX_HUGE_T)ptr_base_addr + offset)); + } + else + { + if (0 == (len % OSAL_MDC_PCI_BUS_WIDTH)) + { + count = len / OSAL_MDC_PCI_BUS_WIDTH; + for (idx = 0; idx < count; idx++) + { + *(ptr_data + idx) = *((UI32_T *)((CLX_HUGE_T)ptr_base_addr + offset + idx * 4)); + } + } + else + { + rc = CLX_E_OTHERS; + } + } + } + else + { + rc = CLX_E_NOT_INITED; + } + + return (rc); +} + +CLX_ERROR_NO_T +osal_mdc_writePciReg( + const UI32_T unit, + const UI32_T offset, + const UI32_T *ptr_data, + const UI32_T len) +{ + UI32_T idx; + UI32_T count; + volatile UI32_T *ptr_base_addr = _osal_mdc_cb.dev[unit].ptr_mmio_virt_addr; + CLX_ERROR_NO_T rc = CLX_E_OK; + + if (NULL != ptr_base_addr) + { + if (OSAL_MDC_PCI_BUS_WIDTH == len) + { + *((UI32_T *)((CLX_HUGE_T)ptr_base_addr + offset)) = *ptr_data; + } + else + { + if (0 == (len % OSAL_MDC_PCI_BUS_WIDTH)) + { + count = len / OSAL_MDC_PCI_BUS_WIDTH; + for (idx = 0; idx < count; idx++) + { + *((UI32_T *)((CLX_HUGE_T)ptr_base_addr + offset + idx * 4)) = *(ptr_data + idx); + } + } + else + { + rc = CLX_E_OTHERS; + } + } + } + else + { + rc = CLX_E_NOT_INITED; + } + + return (rc); +} + +static int +_osal_mdc_probePciCallback( + struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int linux_rc; + UI16_T device_id; + UI16_T vendor_id; + UI8_T revision_id; + CLX_ERROR_NO_T rc = CLX_E_OK; + + linux_rc = pci_enable_device(pdev); + if (0 == linux_rc) + { + _ptr_osal_mdc_dev->if_type = AML_DEV_TYPE_PCI; + + pci_read_config_word(pdev, PCI_DEVICE_ID, &device_id); + pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor_id); + pci_read_config_byte(pdev, PCI_REVISION_ID, &revision_id); + + _ptr_osal_mdc_dev->id.device = (UI32_T)device_id; + _ptr_osal_mdc_dev->id.vendor = (UI32_T)vendor_id; + _ptr_osal_mdc_dev->id.revision = (UI32_T)revision_id; + +#if defined(CLX_LINUX_KERNEL_MODE) + _ptr_osal_mdc_dev->access.read_callback = osal_mdc_readPciReg; + _ptr_osal_mdc_dev->access.write_callback = osal_mdc_writePciReg; +#endif + + rc = _osal_mdc_getPciMmioInfo(pdev, &_osal_mdc_cb.dev[_osal_mdc_cb.dev_num].ptr_mmio_virt_addr); + if (CLX_E_OK == rc) + { + /* Save the database to pdev structure for system callback to release recource, + * such like disconnecting ISR etc. + */ + _osal_mdc_cb.dev[_osal_mdc_cb.dev_num].irq = pdev->irq; + _osal_mdc_cb.dev[_osal_mdc_cb.dev_num].ptr_pci_dev = pdev; + _osal_mdc_cb.dev[_osal_mdc_cb.dev_num].unit = _osal_mdc_cb.dev_num; + + pci_set_drvdata(pdev, &_osal_mdc_cb.dev[_osal_mdc_cb.dev_num]); + + /* To set the bus master bit on device to enable the DMA transaction from PCIe EP to RC + * The bus master bit gets cleared when pci_disable_device() is called + */ + pci_set_master(pdev); + +#if !defined(CLX_EN_DMA_RESERVED) + if (NULL == _osal_mdc_cb.dma_info.ptr_dma_dev) + { + /* This variable is for dma_alloc_coherent */ + _osal_mdc_cb.dma_info.ptr_dma_dev = &pdev->dev; + } +#endif + _osal_mdc_cb.dev_num++; + _ptr_osal_mdc_dev++; + } + } + else + { + OSAL_MDC_ERR("enable pci dev failed, linux_rc=%d\n", linux_rc); + } + + return (0); +} + +static void +_osal_mdc_removePciCallback( + struct pci_dev *pdev) +{ + OSAL_MDC_DEV_T *ptr_dev = (OSAL_MDC_DEV_T *)pci_get_drvdata(pdev); + + iounmap(ptr_dev->ptr_mmio_virt_addr); + pci_release_region(pdev, OSAL_MDC_PCI_BAR0_OFFSET); + pci_disable_device(pdev); + _osal_mdc_cb.dev_num--; +} + +static struct pci_device_id _osal_mdc_id_table[] = +{ + {PCI_DEVICE(HAL_CLX_VENDOR_ID, PCI_ANY_ID)}, + {PCI_DEVICE(HAL_CL_VENDOR_ID, PCI_ANY_ID)}, +}; + +static struct pci_driver _osal_mdc_pci_driver = +{ + .name = OSAL_MDC_DRIVER_NAME, + .id_table = _osal_mdc_id_table, + .probe = _osal_mdc_probePciCallback, + .remove = _osal_mdc_removePciCallback, +}; + +static CLX_ERROR_NO_T +_osal_mdc_probePciDevice(void) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + + if (pci_register_driver(&_osal_mdc_pci_driver) < 0) + { + OSAL_MDC_ERR("Cannot find PCI device\n"); + rc = CLX_E_OTHERS; + } + return (rc); +} + +static CLX_ERROR_NO_T +_osal_mdc_removePciDevice(void) +{ + pci_unregister_driver(&_osal_mdc_pci_driver); + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_osal_mdc_tunePciPerf( + const UI32_T unit) +{ + struct pci_dev *ptr_ep_dev = _osal_mdc_cb.dev[unit].ptr_pci_dev; + struct pci_dev *ptr_rc_dev = ptr_ep_dev->bus->self; + int ext_cap = 0; + UI32_T data_32 = 0; + UI16_T data_16 = 0; + + ext_cap = pci_find_ext_capability(ptr_rc_dev, 0x19); + if (0 == ext_cap) + { + OSAL_MDC_ERR("Cannot find PCI extended ID 0x19\n"); + return CLX_E_OTHERS; + } + + /* disable response timer */ + osal_mdc_readPciReg(unit, 0x3EE16C, &data_32, sizeof(UI32_T)); +#if defined(CLX_EN_BIG_ENDIAN) + data_32 &= 0x0000FFFF; +#else + data_32 &= 0xFFFF0000; +#endif + osal_mdc_writePciReg(unit, 0x3EE16C, &data_32, sizeof(UI32_T)); + + /* perform */ + pci_read_config_word(ptr_rc_dev, ext_cap + 0x4, &data_16); + data_16 |= 0x1; + pci_write_config_word(ptr_rc_dev, ext_cap + 0x4, data_16); + + /* retrain */ + pci_read_config_word(ptr_rc_dev, ptr_rc_dev->pcie_cap + 0x10, &data_16); + data_16 |= 0x20; + pci_write_config_word(ptr_rc_dev, ptr_rc_dev->pcie_cap + 0x10, data_16); + + msleep(100); + + /* clear */ + pci_read_config_word(ptr_rc_dev, ext_cap + 0x4, &data_16); + data_16 &= ~0x1; + pci_write_config_word(ptr_rc_dev, ext_cap + 0x4, data_16); + + return CLX_E_OK; +} + +static CLX_ERROR_NO_T +_osal_mdc_maskStatus( + const UI32_T unit) +{ + struct pci_dev *ptr_ep_dev = _osal_mdc_cb.dev[unit].ptr_pci_dev; + struct pci_dev *ptr_rc_dev = ptr_ep_dev->bus->self; + int ext_cap = 0; + UI32_T data_32 = 0; + + ext_cap = pci_find_ext_capability(ptr_rc_dev, 0x1); + if (0 != ext_cap) + { + /* Mask */ + pci_read_config_dword(ptr_rc_dev, ext_cap + 0x8, &data_32); + data_32 |= 0x20; + pci_write_config_dword(ptr_rc_dev, ext_cap + 0x8, data_32); + } + + return CLX_E_OK; +} + +static CLX_ERROR_NO_T +_osal_mdc_clearStatus( + const UI32_T unit) +{ + struct pci_dev *ptr_ep_dev = _osal_mdc_cb.dev[unit].ptr_pci_dev; + struct pci_dev *ptr_rc_dev = ptr_ep_dev->bus->self; + int ext_cap = 0; + UI32_T data_32 = 0; + + ext_cap = pci_find_ext_capability(ptr_rc_dev, 0x1); + if (0 != ext_cap) + { + /* Clear */ + pci_write_config_word(ptr_rc_dev, ptr_rc_dev->pcie_cap + 0xa, 0x04); + pci_write_config_word(ptr_rc_dev, ptr_rc_dev->pcie_cap + 0x12, 0x8000); + pci_write_config_dword(ptr_rc_dev, ext_cap + 0x4, 0x20); + + /* UnMask */ + pci_read_config_dword(ptr_rc_dev, ext_cap + 0x8, &data_32); + data_32 &= ~0x20; + pci_write_config_dword(ptr_rc_dev, ext_cap + 0x8, data_32); + } + + return CLX_E_OK; +} + +static CLX_ERROR_NO_T +_osal_mdc_savePciConfig( + const UI32_T unit) +{ + struct pci_dev *ptr_dev = _osal_mdc_cb.dev[unit].ptr_pci_dev; + CLX_ERROR_NO_T rc = CLX_E_OK; + + rc = _osal_mdc_maskStatus(unit); + + if (CLX_E_OK == rc) + { + pci_save_state(ptr_dev); + } + + return rc; +} + +static CLX_ERROR_NO_T +_osal_mdc_restorePciConfig( + const UI32_T unit) +{ +#define OSAL_MDC_PCI_PRESENT_POLL_CNT (100) +#define OSAL_MDC_PCI_PRESENT_POLL_INTERVAL (10) /* ms */ + + struct pci_dev *ptr_dev = _osal_mdc_cb.dev[unit].ptr_pci_dev; + UI32_T poll_cnt = 0; + CLX_ERROR_NO_T rc = CLX_E_OK; + + /* standard: at least 100ms for link recovery */ + msleep(100); + + /* make sure pci device is there before restoring the config space */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) + while ((0 == pci_device_is_present(ptr_dev)) && +#else + while ((0 == pci_dev_present(_osal_mdc_id_table)) && +#endif + (poll_cnt < OSAL_MDC_PCI_PRESENT_POLL_CNT)) + { + msleep(OSAL_MDC_PCI_PRESENT_POLL_INTERVAL); + poll_cnt++; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) + if (1 == pci_device_is_present(ptr_dev)) +#else + if (1 == pci_dev_present(_osal_mdc_id_table)) +#endif + { + pci_restore_state(ptr_dev); + rc = CLX_E_OK; + } + else + { + OSAL_MDC_ERR("detect pci device failed\n"); + rc = CLX_E_OTHERS; + } + + if (CLX_E_OK == rc) + { + rc = _osal_mdc_clearStatus(unit); + } + + if (CLX_E_OK == rc) + { + if (HAL_DEVICE_ID_CL8500 == (ptr_dev->device & 0xFF00)) + { + rc = _osal_mdc_tunePciPerf(unit); + } + } + + return (rc); +} + +static CLX_ERROR_NO_T +_osal_mdc_tunePciDevice( + AML_DEV_T *ptr_dev, + const UI32_T dev_num) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + UI32_T idx = 0; + + for (idx = 0; (idx < dev_num) && (CLX_E_OK == rc); idx++) + { + if (HAL_DEVICE_ID_CL8500 == (ptr_dev[idx].id.device & 0xFF00)) + { + rc = _osal_mdc_tunePciPerf(idx); + } + } + + return rc; +} + +#endif /* End of AML_EN_I2C */ + +/* --------------------------------------------------------------------------- DMA */ +#if defined(CLX_LINUX_KERNEL_MODE) + +static CLX_ERROR_NO_T +_osal_mdc_searchDmaVirtAddr( + OSAL_MDC_LIST_T *ptr_dma_list, + const void *ptr_virt_addr, + OSAL_MDC_LIST_NODE_T **pptr_node, + OSAL_MDC_DMA_NODE_T **pptr_node_data) +{ + OSAL_MDC_LIST_NODE_T *ptr_curr_node; + OSAL_MDC_DMA_NODE_T *ptr_curr_node_data; + CLX_ERROR_NO_T rc; + + rc = osal_mdc_list_locateHead(ptr_dma_list, &ptr_curr_node); + while (CLX_E_OK == rc) + { + rc = osal_mdc_list_getNodeData(ptr_dma_list, ptr_curr_node, (void **)&ptr_curr_node_data); + if (CLX_E_OK == rc) + { + if (ptr_curr_node_data->ptr_virt_addr == ptr_virt_addr) + { + *pptr_node = ptr_curr_node; + *pptr_node_data = ptr_curr_node_data; + break; + } + rc = osal_mdc_list_next(ptr_dma_list, ptr_curr_node, &ptr_curr_node); + } + } + return (rc); +} + +static CLX_ERROR_NO_T +_osal_mdc_destroyDmaNodeList( + OSAL_MDC_DMA_INFO_T *ptr_dma_info) +{ + OSAL_MDC_LIST_T *ptr_dma_list = ptr_dma_info->ptr_dma_list; + OSAL_MDC_LIST_NODE_T *ptr_curr_node = NULL; + OSAL_MDC_DMA_NODE_T *ptr_curr_node_data = NULL; + CLX_ERROR_NO_T rc = CLX_E_NOT_INITED; + + if (NULL != ptr_dma_list) + { + rc = osal_mdc_list_locateHead(ptr_dma_list, &ptr_curr_node); + while (CLX_E_OK == rc) + { + rc = osal_mdc_list_getNodeData(ptr_dma_list, ptr_curr_node, (void **)&ptr_curr_node_data); + if ((CLX_E_OK == rc) && (NULL != ptr_curr_node_data)) + { + rc = osal_mdc_list_deleteByData(ptr_dma_list, ptr_curr_node_data); + if (CLX_E_OK == rc) + { + kfree(ptr_curr_node_data); + } + } + rc = osal_mdc_list_locateHead(ptr_dma_list, &ptr_curr_node); + } + rc = osal_mdc_list_destroy(ptr_dma_list, NULL); + if (CLX_E_OK == rc) + { + ptr_dma_info->ptr_dma_list = NULL; + } + } + return (rc); +} + +#endif /* End of CLX_LINUX_KERNEL_MODE */ + +#if defined(CLX_EN_DMA_RESERVED) + +#if defined(CLX_LINUX_KERNEL_MODE) + +#if defined(OSAL_MDC_EN_TEST) +static CLX_ERROR_NO_T +_osal_mdc_dumpRsrvDmaList(void) +{ + OSAL_MDC_DMA_INFO_T *ptr_dma_info = &_osal_mdc_cb.dma_info; + OSAL_MDC_LIST_NODE_T *ptr_curr_node; + OSAL_MDC_DMA_NODE_T *ptr_curr_node_data; + UI32_T node = 0; + CLX_ERROR_NO_T rc = CLX_E_OK; + + rc = osal_mdc_list_locateHead(ptr_dma_info->ptr_dma_list, + &ptr_curr_node); + while (CLX_E_OK == rc) + { + rc = osal_mdc_list_getNodeData(ptr_dma_info->ptr_dma_list, + ptr_curr_node, (void **)&ptr_curr_node_data); + if (CLX_E_OK == rc) + { + OSAL_MDC_ERR( + "node %d. virt addr=%p, phy addr=%p, size=%d, avbl=%d\n", node, + ptr_curr_node_data->ptr_virt_addr, ptr_curr_node_data->phy_addr, + ptr_curr_node_data->size, ptr_curr_node_data->available); + } + rc = osal_mdc_list_next(ptr_dma_info->ptr_dma_list, + ptr_curr_node, &ptr_curr_node); + node++; + } + return (rc); +} +#endif + +static CLX_ERROR_NO_T +_osal_mdc_createRsrvDmaNodeList( + OSAL_MDC_DMA_INFO_T *ptr_dma_info) +{ + OSAL_MDC_DMA_NODE_T *ptr_node_data; + CLX_ERROR_NO_T rc; + + rc = osal_mdc_list_create(OSAL_MDC_DMA_LIST_SZ_UNLIMITED, + OSAL_MDC_LIST_TYPE_DOUBLE, + OSAL_MDC_DMA_LIST_NAME, + &ptr_dma_info->ptr_dma_list); + if (CLX_E_OK == rc) + { + /* The first node, which contains all of the reserved memory */ + ptr_node_data = kmalloc(sizeof(OSAL_MDC_DMA_NODE_T), GFP_KERNEL); + if (NULL != ptr_node_data) + { + ptr_node_data->ptr_virt_addr = ptr_dma_info->ptr_rsrv_virt_addr; + ptr_node_data->phy_addr = ptr_dma_info->rsrv_phy_addr; + ptr_node_data->size = ptr_dma_info->rsrv_size; + ptr_node_data->available = TRUE; + rc = osal_mdc_list_insertToHead(ptr_dma_info->ptr_dma_list, ptr_node_data); + } + else + { + rc = CLX_E_NO_MEMORY; + } + } + return (rc); +} + +static CLX_ERROR_NO_T +_osal_mdc_searchAvblRsrvDmaNode( + OSAL_MDC_LIST_T *ptr_dma_list, + const UI32_T size, + OSAL_MDC_LIST_NODE_T **pptr_avbl_node) +{ + OSAL_MDC_LIST_NODE_T *ptr_curr_node; + OSAL_MDC_DMA_NODE_T *ptr_curr_node_data; + CLX_ERROR_NO_T rc; + + rc = osal_mdc_list_locateHead(ptr_dma_list, &ptr_curr_node); + while (CLX_E_OK == rc) + { + rc = osal_mdc_list_getNodeData(ptr_dma_list, ptr_curr_node, (void **)&ptr_curr_node_data); + if (CLX_E_OK == rc) + { + if ((TRUE == ptr_curr_node_data->available) && (ptr_curr_node_data->size >= size)) + { + *pptr_avbl_node = ptr_curr_node; + break; + } + } + rc = osal_mdc_list_next(ptr_dma_list, ptr_curr_node, &ptr_curr_node); + } + return (rc); +} + +static CLX_ERROR_NO_T +_osal_mdc_splitRsrvDmaNodes( + OSAL_MDC_LIST_T *ptr_dma_list, + OSAL_MDC_LIST_NODE_T *ptr_ori_node, + const UI32_T size, + OSAL_MDC_DMA_NODE_T **pptr_new_node_data) +{ + OSAL_MDC_DMA_NODE_T *ptr_ori_node_data; + CLX_ERROR_NO_T rc; + + rc = osal_mdc_list_getNodeData(ptr_dma_list, ptr_ori_node, (void **)&ptr_ori_node_data); + + if (CLX_E_OK == rc) + { + *pptr_new_node_data = kmalloc(sizeof(OSAL_MDC_DMA_NODE_T), GFP_KERNEL); + + /* Create a new node */ + (*pptr_new_node_data)->size = size; + (*pptr_new_node_data)->phy_addr = ptr_ori_node_data->phy_addr; + (*pptr_new_node_data)->ptr_virt_addr = ptr_ori_node_data->ptr_virt_addr; + (*pptr_new_node_data)->available = TRUE; + + /* Update the original node */ + ptr_ori_node_data->size -= size; + ptr_ori_node_data->phy_addr += size; + ptr_ori_node_data->ptr_virt_addr = + (void *)((CLX_HUGE_T)ptr_ori_node_data->ptr_virt_addr + (CLX_HUGE_T)size); + + rc = osal_mdc_list_insertBefore(ptr_dma_list, ptr_ori_node, (void *)*pptr_new_node_data); + if (CLX_E_OK != rc) + { + OSAL_MDC_ERR("insert rsrv dma node to list failed, size=%d, rc=%d\n", size, rc); + /* Recovery */ + ptr_ori_node_data->size += size; + ptr_ori_node_data->phy_addr -= size; + ptr_ori_node_data->ptr_virt_addr = + (void *)((CLX_HUGE_T)ptr_ori_node_data->ptr_virt_addr - (CLX_HUGE_T)size); + kfree(*pptr_new_node_data); + } + } + return (rc); +} + +static void * +_osal_mdc_allocRsrvDmaMem( + OSAL_MDC_DMA_INFO_T *ptr_dma_info, + const UI32_T size) +{ + OSAL_MDC_LIST_T *ptr_dma_list = ptr_dma_info->ptr_dma_list; + OSAL_MDC_LIST_NODE_T *ptr_node = NULL; + OSAL_MDC_DMA_NODE_T *ptr_node_data; + OSAL_MDC_DMA_NODE_T *ptr_new_node_data; + void *ptr_virt_addr = NULL; + CLX_ERROR_NO_T rc; + + rc = _osal_mdc_searchAvblRsrvDmaNode(ptr_dma_list, size, &ptr_node); + if (CLX_E_OK == rc) + { + rc = osal_mdc_list_getNodeData(ptr_dma_list, ptr_node, (void **)&ptr_node_data); + if (CLX_E_OK == rc) + { + /* If the node size just fit the user's requirement, just give it to user */ + if (ptr_node_data->size == size) + { + ptr_node_data->available = FALSE; + ptr_virt_addr = ptr_node_data->ptr_virt_addr; + } + /* or split a new node with user required size. */ + else + { + rc = _osal_mdc_splitRsrvDmaNodes(ptr_dma_list, ptr_node, size, &ptr_new_node_data); + if (CLX_E_OK == rc) + { + ptr_new_node_data->available = FALSE; + ptr_virt_addr = ptr_new_node_data->ptr_virt_addr; + } + } + } + } + return (ptr_virt_addr); +} + +static CLX_ERROR_NO_T +_osal_mdc_mergeTwoRsrvDmaNodes( + OSAL_MDC_LIST_T *ptr_dma_list, + OSAL_MDC_DMA_NODE_T *ptr_first_node_data, + OSAL_MDC_DMA_NODE_T *ptr_second_node_data) +{ + ptr_first_node_data->size += ptr_second_node_data->size; + + osal_mdc_list_deleteByData(ptr_dma_list, ptr_second_node_data); + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_osal_mdc_mergeRsrvDmaNodes( + OSAL_MDC_LIST_T *ptr_dma_list, + OSAL_MDC_LIST_NODE_T *ptr_curr_node) +{ + OSAL_MDC_LIST_NODE_T *ptr_prev_node; + OSAL_MDC_LIST_NODE_T *ptr_next_node; + OSAL_MDC_DMA_NODE_T *ptr_curr_node_data; + OSAL_MDC_DMA_NODE_T *ptr_prev_node_data; + OSAL_MDC_DMA_NODE_T *ptr_next_node_data; + CLX_ERROR_NO_T rc; + + rc = osal_mdc_list_getNodeData(ptr_dma_list, ptr_curr_node, (void **)&ptr_curr_node_data); + if (CLX_E_OK == rc) + { + /* First, check if the previous node is available */ + rc = osal_mdc_list_prev(ptr_dma_list, ptr_curr_node, &ptr_prev_node); + if (CLX_E_OK == rc) + { + osal_mdc_list_getNodeData(ptr_dma_list, ptr_prev_node, (void **)&ptr_prev_node_data); + if (TRUE == ptr_prev_node_data->available) + { + _osal_mdc_mergeTwoRsrvDmaNodes(ptr_dma_list, ptr_prev_node_data, ptr_curr_node_data); + ptr_curr_node = ptr_prev_node; + ptr_curr_node_data = ptr_prev_node_data; + } + } + + /* then, check if the next node is available */ + rc = osal_mdc_list_next(ptr_dma_list, ptr_curr_node, &ptr_next_node); + if (CLX_E_OK == rc) + { + rc = osal_mdc_list_getNodeData(ptr_dma_list, ptr_next_node, (void **)&ptr_next_node_data); + if (CLX_E_OK == rc) + { + if (TRUE == ptr_next_node_data->available) + { + _osal_mdc_mergeTwoRsrvDmaNodes(ptr_dma_list, ptr_curr_node_data, ptr_next_node_data); + } + } + } + } + return (rc); +} + +static CLX_ERROR_NO_T +_osal_mdc_freeRsrvDmaMem( + OSAL_MDC_DMA_INFO_T *ptr_dma_info, + void *ptr_virt_addr) +{ + OSAL_MDC_LIST_T *ptr_dma_list = ptr_dma_info->ptr_dma_list; + OSAL_MDC_LIST_NODE_T *ptr_node = NULL; + OSAL_MDC_DMA_NODE_T *ptr_node_data = NULL; + CLX_ERROR_NO_T rc; + + rc = _osal_mdc_searchDmaVirtAddr(ptr_dma_list, ptr_virt_addr, &ptr_node, &ptr_node_data); + if (CLX_E_OK == rc) + { + ptr_node_data->available = TRUE; + _osal_mdc_mergeRsrvDmaNodes(ptr_dma_list, ptr_node); + } + return (rc); +} + +#endif /* End of CLX_LINUX_KERNEL_MODE */ + +static CLX_ERROR_NO_T +_osal_mdc_initRsrvDmaMem( + OSAL_MDC_DMA_INFO_T *ptr_dma_info) +{ + struct resource *ptr_res; + CLX_ERROR_NO_T rc = CLX_E_OK; + + ptr_dma_info->rsrv_size = (CLX_ADDR_T)CLX_DMA_RESERVED_SZ * 1024 * 1024; + ptr_dma_info->rsrv_phy_addr = (CLX_ADDR_T)CLX_OS_MEMORY_SZ * 1024 * 1024; + ptr_res = request_mem_region(ptr_dma_info->rsrv_phy_addr, + ptr_dma_info->rsrv_size, "clx_rsrv_mem"); + if (NULL != ptr_res) + { + ptr_dma_info->ptr_rsrv_virt_addr = IOREMAP_API(ptr_dma_info->rsrv_phy_addr, + ptr_dma_info->rsrv_size); + if (NULL == ptr_dma_info->ptr_rsrv_virt_addr) + { + OSAL_MDC_ERR( + "IOREMAP_API() failed, phy addr=" CLX_ADDR_PRINT ", size=" CLX_ADDR_PRINT "\n", + ptr_dma_info->rsrv_phy_addr, ptr_dma_info->rsrv_size); + + rc = CLX_E_OTHERS; + } + } + else + { + OSAL_MDC_ERR( + "request_mem_region() failed, phy addr=" CLX_ADDR_PRINT ", size=" CLX_ADDR_PRINT "\n", + ptr_dma_info->rsrv_phy_addr, ptr_dma_info->rsrv_size); + + rc = CLX_E_OTHERS; + } + return (rc); +} + +static CLX_ERROR_NO_T +_osal_mdc_deinitRsrvDmaMem( + OSAL_MDC_DMA_INFO_T *ptr_dma_info) +{ + if (NULL != ptr_dma_info->ptr_rsrv_virt_addr) + { + iounmap(ptr_dma_info->ptr_rsrv_virt_addr); + } + if (0x0 != ptr_dma_info->rsrv_phy_addr) + { + release_mem_region(ptr_dma_info->rsrv_phy_addr, + ptr_dma_info->rsrv_size); + } + return (CLX_E_OK); +} + + +#else /* Else of CLX_EN_DMA_RESERVED */ + + +#if defined(CLX_LINUX_KERNEL_MODE) + +#if defined(OSAL_MDC_EN_TEST) +static CLX_ERROR_NO_T +_osal_mdc_dumpSysDmaList(void) +{ + OSAL_MDC_DMA_INFO_T *ptr_dma_info = &_osal_mdc_cb.dma_info; + OSAL_MDC_LIST_NODE_T *ptr_curr_node; + OSAL_MDC_DMA_NODE_T *ptr_curr_node_data; + UI32_T node = 0; + CLX_ERROR_NO_T rc = CLX_E_OK; + + rc = osal_mdc_list_locateHead(ptr_dma_info->ptr_dma_list, + &ptr_curr_node); + while (CLX_E_OK == rc) + { + rc = osal_mdc_list_getNodeData(ptr_dma_info->ptr_dma_list, + ptr_curr_node, (void **)&ptr_curr_node_data); + if (CLX_E_OK == rc) + { + OSAL_MDC_ERR( + "node %d. virt addr=%p, phy addr=%p, size=%d\n", node, + ptr_curr_node_data->ptr_virt_addr, ptr_curr_node_data->phy_addr, + ptr_curr_node_data->size); + } + + rc = osal_mdc_list_next(ptr_dma_info->ptr_dma_list, + ptr_curr_node, &ptr_curr_node); + node++; + + } + return (rc); +} +#endif + +static CLX_ERROR_NO_T +_osal_mdc_createSysDmaNodeList( + OSAL_MDC_DMA_INFO_T *ptr_dma_info) +{ + CLX_ERROR_NO_T rc; + + rc = osal_mdc_list_create(OSAL_MDC_DMA_LIST_SZ_UNLIMITED, + OSAL_MDC_LIST_TYPE_SINGLY, + OSAL_MDC_DMA_LIST_NAME, + &ptr_dma_info->ptr_dma_list); + return (rc); +} +#if !defined(CLX_LAMP) + +static void * +_osal_mdc_allocSysDmaMem( + OSAL_MDC_DMA_INFO_T *ptr_dma_info, + const UI32_T size) +{ + dma_addr_t phy_addr; + OSAL_MDC_DMA_NODE_T *ptr_node_data; + void *ptr_virt_addr = NULL; + CLX_ERROR_NO_T rc = CLX_E_OK; + + ptr_virt_addr = dma_alloc_coherent(ptr_dma_info->ptr_dma_dev, size, &phy_addr, GFP_ATOMIC); + if (NULL != ptr_virt_addr) + { + ptr_node_data = kmalloc(sizeof(OSAL_MDC_DMA_NODE_T), GFP_KERNEL); + ptr_node_data->phy_addr = (CLX_ADDR_T)phy_addr; + ptr_node_data->ptr_virt_addr = ptr_virt_addr; + ptr_node_data->size = size; + + rc = osal_mdc_list_insertToHead(ptr_dma_info->ptr_dma_list, ptr_node_data); + if (CLX_E_OK != rc) + { + kfree(ptr_node_data); + dma_free_coherent(ptr_dma_info->ptr_dma_dev, size, + ptr_virt_addr, phy_addr); + ptr_virt_addr = NULL; + } + } + return (ptr_virt_addr); +} + +static CLX_ERROR_NO_T +_osal_mdc_freeSysDmaMem( + OSAL_MDC_DMA_INFO_T *ptr_dma_info, + void *ptr_virt_addr) +{ + OSAL_MDC_LIST_NODE_T *ptr_node = NULL; + OSAL_MDC_DMA_NODE_T *ptr_node_data = NULL; + CLX_ERROR_NO_T rc; + + rc = _osal_mdc_searchDmaVirtAddr(ptr_dma_info->ptr_dma_list, ptr_virt_addr, &ptr_node, &ptr_node_data); + if (CLX_E_OK == rc) + { + dma_free_coherent(ptr_dma_info->ptr_dma_dev, ptr_node_data->size, + ptr_virt_addr, ptr_node_data->phy_addr); + + osal_mdc_list_deleteByData(ptr_dma_info->ptr_dma_list, ptr_node_data); + kfree(ptr_node_data); + } + return (rc); +} +#endif +#endif /* End of CLX_LINUX_KERNEL_MODE */ + +#endif /* End of CLX_EN_DMA_RESERVED */ + +#if defined(CLX_LINUX_KERNEL_MODE) + +void * +osal_mdc_allocDmaMem( + const UI32_T size) +{ + void *ptr_virt_addr = NULL; + +#if defined(CLX_LAMP) + ptr_virt_addr = osal_alloc(size); +#else + OSAL_MDC_DMA_INFO_T *ptr_dma_info = &_osal_mdc_cb.dma_info; + osal_takeSemaphore(&ptr_dma_info->sema, CLX_SEMAPHORE_WAIT_FOREVER); + +#if defined(CLX_EN_DMA_RESERVED) + ptr_virt_addr = _osal_mdc_allocRsrvDmaMem(ptr_dma_info, size); +#else + ptr_virt_addr = _osal_mdc_allocSysDmaMem(ptr_dma_info, size); +#endif + + osal_giveSemaphore(&ptr_dma_info->sema); +#endif + + return ptr_virt_addr; +} + +CLX_ERROR_NO_T +osal_mdc_freeDmaMem( + void *ptr_virt_addr) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + +#if defined(CLX_LAMP) + osal_free(ptr_virt_addr); +#else + OSAL_MDC_DMA_INFO_T *ptr_dma_info = &_osal_mdc_cb.dma_info; + + osal_takeSemaphore(&ptr_dma_info->sema, CLX_SEMAPHORE_WAIT_FOREVER); + +#if defined(CLX_EN_DMA_RESERVED) + rc = _osal_mdc_freeRsrvDmaMem(ptr_dma_info, ptr_virt_addr); +#else + + rc = _osal_mdc_freeSysDmaMem(ptr_dma_info, ptr_virt_addr); +#endif + osal_giveSemaphore(&ptr_dma_info->sema); + + if (CLX_E_OK != rc) + { + OSAL_MDC_ERR("free dma mem failed, virt addr=%p\n", ptr_virt_addr); + } +#endif + + return (rc); +} + +CLX_ERROR_NO_T +osal_mdc_convertPhyToVirt( + const CLX_ADDR_T phy_addr, + void **pptr_virt_addr) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + +#if defined(CLX_EN_DMA_RESERVED) + OSAL_MDC_DMA_INFO_T *ptr_dma_info = &_osal_mdc_cb.dma_info; + CLX_HUGE_T rsrv_virt_base = (CLX_HUGE_T)ptr_dma_info->ptr_rsrv_virt_addr; + CLX_ADDR_T rsrv_phy_base = ptr_dma_info->rsrv_phy_addr; +#endif + +#if defined(CLX_EN_DMA_RESERVED) + *pptr_virt_addr = (void *)(rsrv_virt_base + (CLX_HUGE_T)(phy_addr - rsrv_phy_base)); +#else + *pptr_virt_addr = NULL; + *pptr_virt_addr = phys_to_virt(phy_addr); + rc = (NULL == *pptr_virt_addr) ? (CLX_E_ENTRY_NOT_FOUND) : (CLX_E_OK); +#endif + +#if defined(AML_EN_CUSTOM_DMA_ADDR) + if (CLX_E_OK != rc) + { + /* Here the user may invoke the API for their private DMA + * address conversion. + */ + } +#endif + return (rc); +} + +CLX_ERROR_NO_T +osal_mdc_convertVirtToPhy( + void *ptr_virt_addr, + CLX_ADDR_T *ptr_phy_addr) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + +#if defined(CLX_EN_DMA_RESERVED) + OSAL_MDC_DMA_INFO_T *ptr_dma_info = &_osal_mdc_cb.dma_info; + CLX_HUGE_T rsrv_virt_base = (CLX_HUGE_T)ptr_dma_info->ptr_rsrv_virt_addr; + CLX_ADDR_T rsrv_phy_base = ptr_dma_info->rsrv_phy_addr; +#endif + +#if defined(CLX_EN_DMA_RESERVED) + *ptr_phy_addr = (CLX_ADDR_T)((CLX_HUGE_T)rsrv_phy_base + + (CLX_HUGE_T)ptr_virt_addr - rsrv_virt_base); +#else + *ptr_phy_addr = 0x0; + *ptr_phy_addr = virt_to_phys(ptr_virt_addr); + rc = (0x0 == *ptr_phy_addr) ? (CLX_E_ENTRY_NOT_FOUND) : (CLX_E_OK); +#endif + +#if defined(AML_EN_CUSTOM_DMA_ADDR) + if (CLX_E_OK != rc) + { + /* Here the user may invoke the API for their private DMA + * address conversion. + */ + } +#endif + return (rc); +} + +CLX_ERROR_NO_T +osal_mdc_initDmaMem(void) +{ + OSAL_MDC_DMA_INFO_T *ptr_dma_info = &_osal_mdc_cb.dma_info; + CLX_ERROR_NO_T rc = CLX_E_OK; + + rc = osal_createSemaphore(OSAL_MDC_DMA_SEMAPHORE_NAME, + CLX_SEMAPHORE_BINARY, &ptr_dma_info->sema); + if (CLX_E_OK == rc) + { +#if defined(CLX_EN_DMA_RESERVED) + rc = _osal_mdc_initRsrvDmaMem(ptr_dma_info); + if (CLX_E_OK == rc) + { + rc = _osal_mdc_createRsrvDmaNodeList(ptr_dma_info); + } +#else + rc = _osal_mdc_createSysDmaNodeList(ptr_dma_info); +#endif + } + return (rc); +} + +CLX_ERROR_NO_T +osal_mdc_deinitDmaMem(void) +{ + OSAL_MDC_DMA_INFO_T *ptr_dma_info = &_osal_mdc_cb.dma_info; + + /* Common function for both reserved/system memory. */ + _osal_mdc_destroyDmaNodeList(ptr_dma_info); + +#if defined(CLX_EN_DMA_RESERVED) + _osal_mdc_deinitRsrvDmaMem(ptr_dma_info); +#endif + + osal_destroySemaphore(&ptr_dma_info->sema); + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_mdc_flushCache( + void *ptr_virt_addr, + const UI32_T size) +{ +#if defined(CONFIG_NOT_COHERENT_CACHE) || defined(CONFIG_DMA_NONCOHERENT) + +#if defined(dma_cache_wback_inv) + dma_cache_wback_inv((CLX_HUGE_T)ptr_virt_addr, size); +#else + dma_cache_sync(NULL, ptr_virt_addr, size, DMA_TO_DEVICE); +#endif + +#endif + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_mdc_invalidateCache( + void *ptr_virt_addr, + const UI32_T size) +{ +#if defined(CONFIG_NOT_COHERENT_CACHE) || defined(CONFIG_DMA_NONCOHERENT) + +#if defined(dma_cache_wback_inv) + dma_cache_wback_inv((CLX_HUGE_T)ptr_virt_addr, size); +#else + dma_cache_sync(NULL, ptr_virt_addr, size, DMA_FROM_DEVICE); +#endif + +#endif + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_mdc_savePciConfig( + const UI32_T unit) +{ + return _osal_mdc_savePciConfig(unit); +} + +CLX_ERROR_NO_T +osal_mdc_restorePciConfig( + const UI32_T unit) +{ + return _osal_mdc_restorePciConfig(unit); +} + +#endif /* End of CLX_LINUX_KERNEL_MODE */ + +/* --------------------------------------------------------------------------- Interrupt */ +#if defined(CLX_LINUX_USER_MODE) +static UI32_T _osal_mdc_isr_init_bitmap = 0; /* To record the dev request_irq */ +static UI32_T _osal_mdc_isr_dev_bitmap; /* To record the dev bitmap */ +static spinlock_t _osal_mdc_isr_dev_bitmap_lock; +static wait_queue_head_t _osal_mdc_isr_wait; +static UI32_T _osal_mdc_isr_mask_addr; +static UI32_T _osal_mdc_isr_mask_val; + + +static inline CLX_ERROR_NO_T +_osal_mdc_initInterrupt(void) +{ + /* init top and bottom halves */ + init_waitqueue_head(&_osal_mdc_isr_wait); + + /* init lock and clear device bitmap */ + spin_lock_init(&_osal_mdc_isr_dev_bitmap_lock); + _osal_mdc_isr_dev_bitmap = 0; + _osal_mdc_isr_init_bitmap = 0; + + /* clear chip interrupt mask address and value */ + _osal_mdc_isr_mask_addr = 0; + _osal_mdc_isr_mask_val = 0; + + return (CLX_E_OK); +} + +/* top half */ +static inline CLX_ERROR_NO_T +_osal_mdc_notifyUserProcess( + const UI32_T unit) +{ + unsigned long flags = 0; + + /* mask chip interrupt */ + osal_mdc_writePciReg(unit, _osal_mdc_isr_mask_addr, + &_osal_mdc_isr_mask_val, sizeof(UI32_T)); + + /* set the device bitmap. */ + spin_lock_irqsave(&_osal_mdc_isr_dev_bitmap_lock, flags); + _osal_mdc_isr_dev_bitmap |= (1U << unit); + spin_unlock_irqrestore(&_osal_mdc_isr_dev_bitmap_lock, flags); + + /* notify user process. */ + wake_up_interruptible(&_osal_mdc_isr_wait); + + return (CLX_E_OK); +} + +static inline CLX_ERROR_NO_T +_osal_mdc_waitEvent( + UI32_T *ptr_dev_bitmap) +{ + unsigned long flags = 0; + + wait_event_interruptible(_osal_mdc_isr_wait, (0 != _osal_mdc_isr_dev_bitmap)); + + /* save and clear the device bitmap. */ + spin_lock_irqsave(&_osal_mdc_isr_dev_bitmap_lock, flags); + *ptr_dev_bitmap = _osal_mdc_isr_dev_bitmap; + _osal_mdc_isr_dev_bitmap = 0; + spin_unlock_irqrestore(&_osal_mdc_isr_dev_bitmap_lock, flags); + + return (CLX_E_OK); +} + +static ssize_t +_osal_mdc_read( + struct file *filep, + char __user *buf, + size_t count, + loff_t *ppos) +{ + ssize_t ret; + UI32_T dev_bitmap = 0; + + if (count != sizeof(UI32_T)) + { + return -EINVAL; + } + + /* check if request_irq is inited. */ + if (0 != _osal_mdc_isr_init_bitmap) + { + _osal_mdc_waitEvent(&dev_bitmap); + } + + /* copy the device bitmap to user process. */ + if (0 != copy_to_user(buf, &dev_bitmap, count)) + { + ret = -EFAULT; + } + else + { + ret = count; + } + + return ret; +} +#endif /* End of CLX_LINUX_USER_MODE */ + +static irqreturn_t +_osal_mdc_systemIntrCallback( + int irq, + void *ptr_cookie) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + int linux_rc = IRQ_HANDLED; + OSAL_MDC_DEV_T *ptr_dev = (OSAL_MDC_DEV_T *)ptr_cookie; + + /* Invoke kernel callback, the callback function exist only in below cases: + * 1. SDK built in kernel mode + * 2. SDK built in user mode, NetIF kernel module is enabled + */ + if (NULL != ptr_dev->isr_callback) + { + rc = ptr_dev->isr_callback(ptr_dev->ptr_isr_data); + if (CLX_E_OK != rc) + { + OSAL_MDC_ERR("handle irq failed, rc=%d\n", rc); + linux_rc = IRQ_NONE; + } + } + +#if defined(CLX_LINUX_USER_MODE) + /* Notify user process */ + rc = _osal_mdc_notifyUserProcess(ptr_dev->unit); + if (CLX_E_OK != rc) + { + OSAL_MDC_ERR("notify intr to usr failed, rc=%d\n", rc); + linux_rc = IRQ_NONE; + } +#endif + + return (linux_rc); +} + +CLX_ERROR_NO_T +osal_mdc_registerIsr( + const UI32_T unit, + AML_DEV_ISR_FUNC_T handler, + void *ptr_cookie) +{ + OSAL_MDC_DEV_T *ptr_dev = &_osal_mdc_cb.dev[unit]; + + ptr_dev->isr_callback = handler; + ptr_dev->ptr_isr_data = (void *)((CLX_HUGE_T)unit); + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_mdc_connectIsr( + const UI32_T unit, + AML_DEV_ISR_FUNC_T handler, + AML_DEV_ISR_DATA_T *ptr_cookie) +{ + OSAL_MDC_DEV_T *ptr_dev = &_osal_mdc_cb.dev[unit]; + int linux_rc = 0; + CLX_ERROR_NO_T rc = CLX_E_OK; + +#if defined(CLX_LINUX_USER_MODE) + if (NULL != ptr_cookie) + { + _osal_mdc_isr_mask_addr = ptr_cookie->mask_addr; + _osal_mdc_isr_mask_val = ptr_cookie->mask_val; + } +#endif + + if (NULL == ptr_dev->isr_callback) + { +#if defined(CLX_LINUX_KERNEL_MODE) + /* In user mode, the following database is created in user space. */ + ptr_dev->isr_callback = handler; + ptr_dev->ptr_isr_data = (void *)((CLX_HUGE_T)unit); +#endif + +#if defined(OSAL_MDC_EN_MSI) + /* If "no_msi" flag is set, it means the device doesn't support MSI. */ + if (1 != ptr_dev->ptr_pci_dev->no_msi) + { + linux_rc = pci_enable_msi(ptr_dev->ptr_pci_dev); + if (0 == linux_rc) + { + /* The system gives a new irq number if MSI is enabled sucessfully. */ + ptr_dev->irq = ptr_dev->ptr_pci_dev->irq; + } + else + { + OSAL_MDC_ERR("pci_enable_msi() failed, rc=%d\n", linux_rc); + rc = CLX_E_OTHERS; + } + } +#endif + + linux_rc = request_irq(ptr_dev->irq, _osal_mdc_systemIntrCallback, + 0, OSAL_MDC_DRIVER_NAME, (void *)ptr_dev); + + if (0 != linux_rc) + { + OSAL_MDC_ERR("request_irq() failed, rc=%d\n", linux_rc); + rc = CLX_E_OTHERS; + } + } + else + { + OSAL_MDC_ERR("double req isr err\n"); + rc = CLX_E_OTHERS; + } + return (rc); +} + +CLX_ERROR_NO_T +osal_mdc_disconnectIsr( + const UI32_T unit) +{ + OSAL_MDC_DEV_T *ptr_dev = &_osal_mdc_cb.dev[unit]; + + free_irq(ptr_dev->irq, (void *)ptr_dev); + +#if defined(OSAL_MDC_EN_MSI) + /* Must free the irq before disabling MSI */ + pci_disable_msi(ptr_dev->ptr_pci_dev); +#endif + +#if defined(CLX_LINUX_KERNEL_MODE) + ptr_dev->isr_callback = NULL; + ptr_dev->ptr_isr_data = NULL; +#endif + +#if defined(CLX_LINUX_USER_MODE) + _osal_mdc_isr_mask_addr = 0x0; + _osal_mdc_isr_mask_val = 0x0; +#endif + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_mdc_initDevice( + AML_DEV_T *ptr_dev_list, + UI32_T *ptr_dev_num) +{ + OSAL_MDC_CB_T *ptr_cb = &_osal_mdc_cb; + CLX_ERROR_NO_T rc = CLX_E_OK; + + _ptr_osal_mdc_dev = ptr_dev_list; + + memset(ptr_cb, 0x0, sizeof(OSAL_MDC_CB_T)); + +#if defined(AML_EN_I2C) + rc = _osal_mdc_probeI2cDevice(); + *ptr_dev_num = 1; +#else + rc = _osal_mdc_probePciDevice(); + *ptr_dev_num = ptr_cb->dev_num; + + if (CLX_E_OK == rc) + { + rc = _osal_mdc_tunePciDevice(ptr_dev_list, *ptr_dev_num); + } + + _ptr_ext_pci_dev = _osal_mdc_cb.dev[0].ptr_pci_dev; +#endif /* End of AML_EN_I2C */ + + return (rc); +} + +CLX_ERROR_NO_T +osal_mdc_deinitDevice(void) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + +#if defined(AML_EN_I2C) + rc = _osal_mdc_removeI2cDevice(); +#else + if (NULL != _ptr_ext_pci_dev) + { + rc = _osal_mdc_removePciDevice(); + _ptr_ext_pci_dev = NULL; + } +#endif + + return (rc); +} + +/*****************************************************************************/ +#if defined(CLX_LINUX_USER_MODE) +/* Interface */ +static UI32_T _osal_mdc_devInited = 0; + +/* DMA */ +#if defined(CLX_EN_DMA_RESERVED) +static UI32_T _osal_mdc_rsvDmaInited = 0; +#else +static struct list_head _osal_mdc_sysDmaList[2]; /* To avoid memory corruption when cold-boot */ +static UI32_T _osal_mdc_sysCurDmaListIdx = 0; +#endif + +/* IOCTL */ +static OSAL_MDC_IOCTL_CB_T _osal_mdc_ioctl_cb; +static AML_DEV_T _osal_mdc_ioctl_dev[CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM] = {}; + +static int +_osal_mdc_open( + struct inode *ptr_inode, + struct file *ptr_file) +{ + return (0); +} + +static int +_osal_mdc_release( + struct inode *ptr_inode, + struct file *ptr_file) +{ + return (0); +} + +static struct vm_operations_struct _osal_mdc_remap_vm_ops = +{ + .open = NULL, + .close = NULL, +}; + +static int +_osal_mdc_mmap( + struct file *filp, + struct vm_area_struct *vma) +{ + size_t size = vma->vm_end - vma->vm_start; + int linux_rc = 0; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,0,48) + pgprot_val(vma->vm_page_prot) |= (_PAGE_NO_CACHE | _PAGE_GUARDED); +#else + UI32_T dev_idx; + OSAL_MDC_DEV_T *ptr_dev; + CLX_ADDR_T phy_addr = vma->vm_pgoff << PAGE_SHIFT; + + /* check mmio base phy addr */ + for (dev_idx = 0, ptr_dev = &_osal_mdc_cb.dev[0]; + dev_idx < _osal_mdc_cb.dev_num; + dev_idx++, ptr_dev++) + { + if ((NULL != ptr_dev->ptr_pci_dev) && + (phy_addr == pci_resource_start(ptr_dev->ptr_pci_dev, OSAL_MDC_PCI_BAR0_OFFSET))) + { + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + break; + } + } +#endif + + vma->vm_flags |= VM_IO; + if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + size, vma->vm_page_prot)) + { + linux_rc = -EAGAIN; + } + vma->vm_ops = &_osal_mdc_remap_vm_ops; + return (linux_rc); +} + +#if defined(CLX_EN_DMA_RESERVED) + +static CLX_ERROR_NO_T +_osal_mdc_ioctl_initRsrvDmaMemCallback( + const UI32_T unit, + void *ptr_data) +{ + OSAL_MDC_DMA_INFO_T *ptr_dma_info = &_osal_mdc_cb.dma_info; + OSAL_MDC_IOCTL_DMA_DATA_T *ptr_ioctl_data = (OSAL_MDC_IOCTL_DMA_DATA_T *)ptr_data; + CLX_ERROR_NO_T rc = CLX_E_OK; + + if (0 == _osal_mdc_rsvDmaInited) + { + rc = _osal_mdc_initRsrvDmaMem(ptr_dma_info); + _osal_mdc_rsvDmaInited = 1; + } + ptr_ioctl_data->rsrv_dma_phy_addr = ptr_dma_info->rsrv_phy_addr; + ptr_ioctl_data->rsrv_dma_size = ptr_dma_info->rsrv_size; + + return (rc); +} + +static CLX_ERROR_NO_T +_osal_mdc_ioctl_deinitRsrvDmaMemCallback( + const UI32_T unit, + void *ptr_data) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + + rc = _osal_mdc_deinitRsrvDmaMem(&_osal_mdc_cb.dma_info); + _osal_mdc_rsvDmaInited = 0; + + return (rc); +} + +#else + +static CLX_ERROR_NO_T +_osal_mdc_clearSysDmaList( + UI32_T dmaListIdx) +{ + OSAL_MDC_DMA_INFO_T *ptr_dma_info = &_osal_mdc_cb.dma_info; + OSAL_MDC_USER_MODE_DMA_NODE_T *ptr_curr_node_data = NULL; + OSAL_MDC_USER_MODE_DMA_NODE_T *ptr_next_node_data = NULL; + + list_for_each_entry_safe(ptr_curr_node_data, ptr_next_node_data, + &_osal_mdc_sysDmaList[dmaListIdx], list) + { + list_del(&(ptr_curr_node_data->list)); + dma_free_coherent(ptr_dma_info->ptr_dma_dev, + ptr_curr_node_data->size, + phys_to_virt(ptr_curr_node_data->phy_addr), + ptr_curr_node_data->phy_addr); + kfree(ptr_curr_node_data); + } + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_osal_mdc_ioctl_allocSysDmaMemCallback( + const UI32_T unit, + void *ptr_data) +{ + OSAL_MDC_DMA_INFO_T *ptr_dma_info = &_osal_mdc_cb.dma_info; + OSAL_MDC_IOCTL_DMA_DATA_T *ptr_ioctl_data = (OSAL_MDC_IOCTL_DMA_DATA_T *)ptr_data; + OSAL_MDC_USER_MODE_DMA_NODE_T *ptr_node_data = NULL; + void *virt_addr; + +/* To defense the compatible data type of 32bit and 64bit are not synchronized */ +#if defined(CONFIG_ARCH_DMA_ADDR_T_64BIT) /* Bus addressing is 64-bit */\ +&& !defined(CLX_EN_64BIT_ADDR) /* SDK follows HOST with 32-bit addr*/\ +&& (defined(CLX_EN_HOST_32_BIT_LITTLE_ENDIAN) || defined(CLX_EN_HOST_32_BIT_BIG_ENDIAN))/* HOST is 32-bit */ +#error "The DMA address of OS is 64bit. Please enable CLX_EN_64BIT_ADDR in SDK." +#endif + +#if !defined(CONFIG_ARCH_DMA_ADDR_T_64BIT) && defined(CLX_EN_64BIT_ADDR) +#error "The DMA address of OS is not 64bit. Please disable CLX_EN_64BIT_ADDR in SDK." +#endif + + if (dma_set_mask_and_coherent(ptr_dma_info->ptr_dma_dev, DMA_BIT_MASK(32))) { + dev_err(ptr_dma_info->ptr_dma_dev, "dma_set_mask_and_coherent failed\n"); + } + + ptr_ioctl_data->size = round_up(ptr_ioctl_data->size, PAGE_SIZE); + virt_addr = dma_alloc_coherent(ptr_dma_info->ptr_dma_dev, ptr_ioctl_data->size, + (dma_addr_t *)&ptr_ioctl_data->phy_addr, GFP_KERNEL | GFP_DMA32); + if (virt_addr == NULL) + { + return (CLX_E_NO_MEMORY); + } + + ptr_node_data = kmalloc(sizeof(OSAL_MDC_USER_MODE_DMA_NODE_T), GFP_KERNEL); + if (NULL != ptr_node_data) + { + memset(ptr_node_data, 0, sizeof(OSAL_MDC_USER_MODE_DMA_NODE_T)); + ptr_node_data->phy_addr = ptr_ioctl_data->phy_addr; + ptr_node_data->size = ptr_ioctl_data->size; + list_add(&(ptr_node_data->list), &_osal_mdc_sysDmaList[_osal_mdc_sysCurDmaListIdx]); + ptr_ioctl_data->phy_addr = virt_to_phys(virt_addr); + } + else + { + dma_free_coherent(ptr_dma_info->ptr_dma_dev, ptr_ioctl_data->size, + virt_addr, ptr_ioctl_data->phy_addr); + + return (CLX_E_NO_MEMORY); + } + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_osal_mdc_ioctl_freeSysDmaMemCallback( + const UI32_T unit, + void *ptr_data) +{ + OSAL_MDC_DMA_INFO_T *ptr_dma_info = &_osal_mdc_cb.dma_info; + OSAL_MDC_IOCTL_DMA_DATA_T *ptr_ioctl_data = (OSAL_MDC_IOCTL_DMA_DATA_T *)ptr_data; + + OSAL_MDC_USER_MODE_DMA_NODE_T *ptr_curr_node_data = NULL; + OSAL_MDC_USER_MODE_DMA_NODE_T *ptr_next_node_data = NULL; + + + list_for_each_entry_safe(ptr_curr_node_data, ptr_next_node_data, + &_osal_mdc_sysDmaList[_osal_mdc_sysCurDmaListIdx], list) + { + if (ptr_curr_node_data->phy_addr == ptr_ioctl_data->phy_addr) + { + list_del(&(ptr_curr_node_data->list)); + kfree(ptr_curr_node_data); + break; + } + } + + dma_free_coherent(ptr_dma_info->ptr_dma_dev, ptr_ioctl_data->size, + phys_to_virt(ptr_ioctl_data->phy_addr), ptr_ioctl_data->phy_addr); + + return (CLX_E_OK); +} + +#endif + +static CLX_ERROR_NO_T +_osal_mdc_getPciInfoToIoctlData( + const OSAL_MDC_DEV_T *ptr_dev_list, + OSAL_MDC_IOCTL_DEV_DATA_T *ptr_dev_data) +{ + UI32_T idx; + + /* Search for PCIe device and get the MMIO base address. */ + for (idx = 0; idx < _osal_mdc_cb.dev_num; idx++) + { + if (NULL != ptr_dev_list[idx].ptr_pci_dev) + { + ptr_dev_data->pci_mmio_phy_start[idx] = + pci_resource_start(ptr_dev_list[idx].ptr_pci_dev, OSAL_MDC_PCI_BAR0_OFFSET); + + ptr_dev_data->pci_mmio_size[idx] = + pci_resource_len(ptr_dev_list[idx].ptr_pci_dev, OSAL_MDC_PCI_BAR0_OFFSET); + } + } + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_osal_mdc_getDeviceIdToIoctlData( + AML_DEV_T *ptr_dev, + OSAL_MDC_IOCTL_DEV_DATA_T *ptr_dev_data, + const UI32_T dev_num) +{ + UI32_T idx; + + for (idx = 0; idx < ptr_dev_data->dev_num; idx++) + { + ptr_dev_data->id[idx].device = ptr_dev[idx].id.device; + ptr_dev_data->id[idx].vendor = ptr_dev[idx].id.vendor; + ptr_dev_data->id[idx].revision = ptr_dev[idx].id.revision; + } + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_osal_mdc_ioctl_initDeviceCallback( + const UI32_T unit, + void *ptr_data) +{ + OSAL_MDC_CB_T *ptr_cb = &_osal_mdc_cb; + OSAL_MDC_DEV_T *ptr_dev_list = _osal_mdc_cb.dev; + OSAL_MDC_IOCTL_DEV_DATA_T *ptr_ioctl_data = (OSAL_MDC_IOCTL_DEV_DATA_T *)ptr_data; + + /* "dev" is just created for invoking osal_mdc_initDevice, + * it is no use once the device IDs are copy to ptr_ioctl_data. + */ + + CLX_ERROR_NO_T rc = CLX_E_OK; + + if (0 == _osal_mdc_devInited) + { + rc = osal_mdc_initDevice(_osal_mdc_ioctl_dev, &ptr_ioctl_data->dev_num); + } + else + { + /* ptr_cb->dev_num was initialized in osal_mdc_initDevice(); */ + ptr_ioctl_data->dev_num = ptr_cb->dev_num; + } + + if (ptr_cb->dev_num >= CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM) + { + OSAL_MDC_ERR("dev num=%d > max support num=%d\n", + ptr_ioctl_data->dev_num, CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM); + } + +#if !defined(CLX_EN_DMA_RESERVED) + if (0 == _osal_mdc_devInited) + { + /* Create two DMA memory lists and use 1st. */ + INIT_LIST_HEAD(&_osal_mdc_sysDmaList[0]); + INIT_LIST_HEAD(&_osal_mdc_sysDmaList[1]); + _osal_mdc_sysCurDmaListIdx = 0; + } + else + { + /* Delay free the old list until the chip is reset. + * When we kill the process, the chip continues to write to the DMA memory. + * If we free the old DMA memory before stopping the chip, there could be memory corruption. + */ + _osal_mdc_sysCurDmaListIdx = ((_osal_mdc_sysCurDmaListIdx + 1) & 0x1); + rc = _osal_mdc_clearSysDmaList(_osal_mdc_sysCurDmaListIdx); + } +#endif + + if (CLX_E_OK == rc) + { + rc = _osal_mdc_getDeviceIdToIoctlData(_osal_mdc_ioctl_dev, ptr_ioctl_data, ptr_ioctl_data->dev_num); + } + if (CLX_E_OK == rc) + { + rc = _osal_mdc_getPciInfoToIoctlData(ptr_dev_list, ptr_ioctl_data); + } + + _osal_mdc_devInited = 1; + return (rc); +} + +static CLX_ERROR_NO_T +_osal_mdc_ioctl_deinitDeviceCallback( + const UI32_T unit, + void *ptr_data) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + +#if !defined(CLX_EN_DMA_RESERVED) + _osal_mdc_clearSysDmaList(0); + _osal_mdc_clearSysDmaList(1); +#endif + + if (0 != _osal_mdc_devInited) + { + rc = osal_mdc_deinitDevice(); + _osal_mdc_devInited = 0; + } + + return (rc); +} + +static CLX_ERROR_NO_T +_osal_mdc_ioctl_connectIsrCallback( + const UI32_T unit, + void *ptr_data) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + + if (0 == (_osal_mdc_isr_init_bitmap & (1U << unit))) + { + rc = osal_mdc_connectIsr(unit, NULL, ptr_data); + if (CLX_E_OK == rc) + { + _osal_mdc_isr_init_bitmap |= (1U << unit); + } + } + return (rc); +} + +static CLX_ERROR_NO_T +_osal_mdc_ioctl_disconnectIsrCallback( + const UI32_T unit, + void *ptr_data) +{ + /* To make the user-space polling task return from read. */ + _osal_mdc_notifyUserProcess(unit); + + osal_mdc_disconnectIsr(unit); + _osal_mdc_isr_init_bitmap &= ~(1U << unit); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_osal_mdc_ioctl_savePciConfigCallback( + const UI32_T unit, + void *ptr_data) +{ + return _osal_mdc_savePciConfig(unit); +} + +static CLX_ERROR_NO_T +_osal_mdc_ioctl_restorePciConfigCallback( + const UI32_T unit, + void *ptr_data) +{ + return _osal_mdc_restorePciConfig(unit); +} + +static CLX_ERROR_NO_T +_osal_mdc_registerIoctlCallback( + const OSAL_MDC_IOCTL_TYPE_T type, + const OSAL_MDC_IOCTL_CALLBACK_FUNC_T func) +{ + OSAL_MDC_IOCTL_CB_T *ptr_cb = &_osal_mdc_ioctl_cb; + CLX_ERROR_NO_T rc = CLX_E_OTHERS; + + if (type < OSAL_MDC_IOCTL_TYPE_LAST) + { + if (NULL == ptr_cb->callback[type]) + { + ptr_cb->callback[type] = func; + rc = CLX_E_OK; + } + else + { + OSAL_MDC_ERR("register ioctl callback failed, type=%d exist\n", type); + } + } + else + { + OSAL_MDC_ERR("register ioctl callback failed, type=%d >= max=%d\n", + type, OSAL_MDC_IOCTL_TYPE_LAST); + } + return (rc); +} + +static CLX_ERROR_NO_T +_osal_mdc_initIoctl(void) +{ + memset(&_osal_mdc_ioctl_cb, 0x0, sizeof(OSAL_MDC_IOCTL_CB_T)); + + _osal_mdc_registerIoctlCallback(OSAL_MDC_IOCTL_TYPE_MDC_INIT_DEV, + _osal_mdc_ioctl_initDeviceCallback); + + _osal_mdc_registerIoctlCallback(OSAL_MDC_IOCTL_TYPE_MDC_DEINIT_DEV, + _osal_mdc_ioctl_deinitDeviceCallback); +#if defined(CLX_EN_DMA_RESERVED) + _osal_mdc_registerIoctlCallback(OSAL_MDC_IOCTL_TYPE_MDC_INIT_RSRV_DMA_MEM, + _osal_mdc_ioctl_initRsrvDmaMemCallback); + + _osal_mdc_registerIoctlCallback(OSAL_MDC_IOCTL_TYPE_MDC_DEINIT_RSRV_DMA_MEM, + _osal_mdc_ioctl_deinitRsrvDmaMemCallback); +#else + _osal_mdc_registerIoctlCallback(OSAL_MDC_IOCTL_TYPE_MDC_ALLOC_SYS_DMA_MEM, + _osal_mdc_ioctl_allocSysDmaMemCallback); + + _osal_mdc_registerIoctlCallback(OSAL_MDC_IOCTL_TYPE_MDC_FREE_SYS_DMA_MEM, + _osal_mdc_ioctl_freeSysDmaMemCallback); +#endif + _osal_mdc_registerIoctlCallback(OSAL_MDC_IOCTL_TYPE_MDC_CONNECT_ISR, + _osal_mdc_ioctl_connectIsrCallback); + + _osal_mdc_registerIoctlCallback(OSAL_MDC_IOCTL_TYPE_MDC_DISCONNECT_ISR, + _osal_mdc_ioctl_disconnectIsrCallback); + + _osal_mdc_registerIoctlCallback(OSAL_MDC_IOCTL_TYPE_MDC_SAVE_PCI_CONFIG, + _osal_mdc_ioctl_savePciConfigCallback); + + _osal_mdc_registerIoctlCallback(OSAL_MDC_IOCTL_TYPE_MDC_RESTORE_PCI_CONFIG, + _osal_mdc_ioctl_restorePciConfigCallback); + return (CLX_E_OK); +} + +static long +_osal_mdc_ioctl( + struct file *filp, + unsigned int cmd, + unsigned long arg) +{ +#define OSAL_MDC_IOCTL_LOCAL_BUF_SIZE (128) + + OSAL_MDC_IOCTL_CB_T *ptr_cb = &_osal_mdc_ioctl_cb; + OSAL_MDC_IOCTL_CMD_T *ptr_cmd = (OSAL_MDC_IOCTL_CMD_T *)&cmd; + UI32_T unit = ptr_cmd->field.unit; + OSAL_MDC_IOCTL_TYPE_T type = ptr_cmd->field.type; + OSAL_MDC_IOCTL_ACCESS_T access = ptr_cmd->field.access; + UI32_T data_size = ptr_cmd->field.size; + UI8_T temp_buf[OSAL_MDC_IOCTL_LOCAL_BUF_SIZE]; + UI8_T *ptr_temp_buf; + int linux_rc = 0; + + if (NULL != ptr_cb->callback[type]) + { + if (data_size > OSAL_MDC_IOCTL_LOCAL_BUF_SIZE) + { + ptr_temp_buf = kmalloc(data_size, GFP_KERNEL); + } + else + { + ptr_temp_buf = temp_buf; + } + + /*************************************************************/ + if (OSAL_MDC_IOCTL_ACCESS_WRITE == access) + { + /* type: FREE_SYS_DMA_MEM : DMA physical address + * CONNECT_ISR : Chip interrupt mask address and value + */ + if (copy_from_user(ptr_temp_buf, (int __user *)arg, data_size)) + { + linux_rc = -EFAULT; + } + else + { + if (CLX_E_OK != ptr_cb->callback[type](unit, (void *)ptr_temp_buf)) + { + linux_rc = -EFAULT; + } + } + } + else if (OSAL_MDC_IOCTL_ACCESS_READ == access) + { + /* type: INIT_DEV : PCIe device and vendor ID, mmio address and size + * INIT_RSRV_DMA_MEM : Reserved DMA physical address and size + */ + if (CLX_E_OK != ptr_cb->callback[type](unit, (void *)ptr_temp_buf)) + { + linux_rc = -EFAULT; + } + else + { + if (copy_to_user((int __user *)arg, ptr_temp_buf, data_size)) + { + linux_rc = -EFAULT; + } + } + } + else if (OSAL_MDC_IOCTL_ACCESS_READ_WRITE == access) + { + /* type: ALLOC_SYS_DMA_MEM : DMA physical address + */ + if (copy_from_user(ptr_temp_buf, (int __user *)arg, data_size)) + { + linux_rc = -EFAULT; + } + else + { + if (CLX_E_OK != ptr_cb->callback[type](unit, (void *)ptr_temp_buf)) + { + linux_rc = -EFAULT; + } + else + { + if (copy_to_user((int __user *)arg, ptr_temp_buf, data_size)) + { + linux_rc = -EFAULT; + } + } + } + } + else if (OSAL_MDC_IOCTL_ACCESS_NONE == access) + { + /* type: DEINIT_DEV + * DEINIT_RSRV_DMA_MEM + * DISCONNECT_ISR + * SAVE_PCI_CONFIG + * RESTORE_PCI_CONFIG + */ + if (CLX_E_OK != ptr_cb->callback[type](unit, (void *)ptr_temp_buf)) + { + linux_rc = -EFAULT; + } + } + /*************************************************************/ + + if (data_size > OSAL_MDC_IOCTL_LOCAL_BUF_SIZE) + { + kfree(ptr_temp_buf); + } + } + else + { + OSAL_MDC_ERR("invalid ioctl, cmd=%u, arg=%lu, type=%d\n", cmd, arg, type); + } + return (linux_rc); +} + +#ifdef CONFIG_COMPAT +static long +_osal_mdc_compat_ioctl( + struct file *filp, + unsigned int cmd, + unsigned long arg) +{ + return _osal_mdc_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static struct file_operations _osal_mdc_fops = +{ + .owner = THIS_MODULE, + .open = _osal_mdc_open, + .read = _osal_mdc_read, + .release = _osal_mdc_release, + .unlocked_ioctl = _osal_mdc_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = _osal_mdc_compat_ioctl, +#endif + .mmap = _osal_mdc_mmap, +}; + +static struct miscdevice _osal_mdc_misc = +{ + .minor = OSAL_MDC_DRIVER_MISC_MINOR_NUM, + .name = OSAL_MDC_DRIVER_NAME, + .fops = & _osal_mdc_fops, +}; + +static int __init +osal_mdc_module_init(void) +{ + int linux_rc; + + _osal_mdc_initIoctl(); /* To register IOCTL callback functions. */ + _osal_mdc_initInterrupt(); /* To init structs for top and bottom halves */ + + linux_rc = misc_register(&_osal_mdc_misc); + if (0 != linux_rc) + { + OSAL_MDC_ERR("register dev %s failed, linux_rc=%d\n", OSAL_MDC_DRIVER_NAME, linux_rc); + } + return (linux_rc); +} + +static void __exit +osal_mdc_module_exit(void) +{ + int unit = 0; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,2,8) + int linux_rc; + + linux_rc = misc_deregister(&_osal_mdc_misc); + if (0 != linux_rc) + { + OSAL_MDC_ERR("de-register dev %s failed, linux_rc=%d\n", OSAL_MDC_DRIVER_NAME, linux_rc); + } +#else + misc_deregister(&_osal_mdc_misc); +#endif + + /* ref: _osal_mdc_ioctl_disconnectIsrCallback */ + for (unit = 0; unit < CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM; unit++) + { + if (0 != (_osal_mdc_isr_init_bitmap & (1U << unit))) + { + osal_mdc_disconnectIsr(unit); + _osal_mdc_isr_init_bitmap &= ~(1U << unit); + } + } + + /* ref: _osal_mdc_ioctl_deinitRsrvDmaMemCallback */ +#if defined(CLX_EN_DMA_RESERVED) + if (1 == _osal_mdc_rsvDmaInited) + { + _osal_mdc_deinitRsrvDmaMem(&_osal_mdc_cb.dma_info); + _osal_mdc_rsvDmaInited = 0; + } +#endif + + /* ref: _osal_mdc_ioctl_deinitDeviceCallback */ + if (1 == _osal_mdc_devInited) + { +#if !defined(CLX_EN_DMA_RESERVED) + _osal_mdc_clearSysDmaList(0); + _osal_mdc_clearSysDmaList(1); +#endif + osal_mdc_deinitDevice(); + _osal_mdc_devInited = 0; + } +} + +#else + +static int __init +osal_mdc_module_init(void) +{ + return (0); +} + +static void __exit +osal_mdc_module_exit(void) +{ +} + +#endif /* End of CLX_LINUX_USER_MODE */ + +module_init(osal_mdc_module_init); +module_exit(osal_mdc_module_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Clounix"); +MODULE_DESCRIPTION("SDK Kernel Module"); diff --git a/platform/clounix/clounix-modules/modules/src/clx_netif/hal_dawn_pkt_knl.c b/platform/clounix/clounix-modules/modules/src/clx_netif/hal_dawn_pkt_knl.c new file mode 100755 index 000000000000..4a4267ddc257 --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/clx_netif/hal_dawn_pkt_knl.c @@ -0,0 +1,6356 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: hal_dawn_pkt_knl.c + * PURPOSE: + * To provide Linux kernel for PDMA TX/RX control. + * + * NOTES: + * + */ + +/***************************************************************************** + * INCLUDE FILE DECLARATIONS + ***************************************************************************** + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* netif */ +#include +#include +#include + +#include + +/* clx_sdk */ +#include +#include + +/***************************************************************************** + * CHIP DEPENDENT VARIABLES + ***************************************************************************** + */ +/* Interrupt */ +#define HAL_DAWN_PKT_ERR_REG(__unit__) (_hal_dawn_pkt_intr_vec[0].intr_reg) +#define HAL_DAWN_PKT_TCH_REG(__unit__, __channel__) (_hal_dawn_pkt_intr_vec[1 + (__channel__)].intr_reg) +#define HAL_DAWN_PKT_RCH_REG(__unit__, __channel__) (_hal_dawn_pkt_intr_vec[5 + (__channel__)].intr_reg) + +#define HAL_DAWN_PKT_ERR_EVENT(__unit__) (&_hal_dawn_pkt_intr_vec[0].intr_event) +#define HAL_DAWN_PKT_TCH_EVENT(__unit__, __channel__) (&_hal_dawn_pkt_intr_vec[1 + (__channel__)].intr_event) +#define HAL_DAWN_PKT_RCH_EVENT(__unit__, __channel__) (&_hal_dawn_pkt_intr_vec[5 + (__channel__)].intr_event) + +#define HAL_DAWN_PKT_ERR_CNT(__unit__) (_hal_dawn_pkt_intr_vec[0].intr_cnt) +#define HAL_DAWN_PKT_TCH_CNT(__unit__, __channel__) (_hal_dawn_pkt_intr_vec[1 + (__channel__)].intr_cnt) +#define HAL_DAWN_PKT_RCH_CNT(__unit__, __channel__) (_hal_dawn_pkt_intr_vec[5 + (__channel__)].intr_cnt) + + +/* This flag value will be specified when user inserts kernel module. */ +#define HAL_DAWN_PKT_DBG_ERR (0x1UL << 0) +#define HAL_DAWN_PKT_DBG_TX (0x1UL << 1) +#define HAL_DAWN_PKT_DBG_RX (0x1UL << 2) +#define HAL_DAWN_PKT_DBG_INTF (0x1UL << 3) +#define HAL_DAWN_PKT_DBG_PROFILE (0x1UL << 4) +#define HAL_DAWN_PKT_DBG_COMMON (0x1UL << 5) +#define HAL_DAWN_PKT_DBG_NETLINK (0x1UL << 6) + +extern UI32_T ext_dbg_flag; + + +#define HAL_DAWN_PKT_DBG(__flag__, ...) do \ +{ \ + if (0 != ((__flag__) & (ext_dbg_flag))) \ + { \ + osal_printf(__VA_ARGS__); \ + } \ +}while (0) + +typedef struct +{ + UI32_T intr_reg; + CLX_SEMAPHORE_ID_T intr_event; + UI32_T intr_cnt; + +} HAL_DAWN_PKT_INTR_VEC_T; + +typedef struct HAL_DAWN_PKT_PROFILE_NODE_S +{ + HAL_DAWN_PKT_NETIF_PROFILE_T *ptr_profile; + struct HAL_DAWN_PKT_PROFILE_NODE_S *ptr_next_node; + +} HAL_DAWN_PKT_PROFILE_NODE_T; + +typedef struct +{ + HAL_DAWN_PKT_NETIF_INTF_T meta; + struct net_device *ptr_net_dev; + HAL_DAWN_PKT_PROFILE_NODE_T *ptr_profile_list; /* the profiles binding to this interface */ + +} HAL_DAWN_PKT_NETIF_PORT_DB_T; + + +static HAL_DAWN_PKT_INTR_VEC_T _hal_dawn_pkt_intr_vec[] = +{ + { /* 0: PDMA_ERR */ 1UL << 0, 0x0, 0 }, + { /* 1: TX_CH0 */ 1UL << 28, 0x0, 0 }, + { /* 2: TX_CH1 */ 1UL << 29, 0x0, 0 }, + { /* 3: TX_CH2 */ 1UL << 30, 0x0, 0 }, + { /* 4: TX_CH3 */ 1UL << 31, 0x0, 0 }, + { /* 5: RX_CH0 */ 1UL << 12, 0x0, 0 }, + { /* 6: RX_CH1 */ 1UL << 13, 0x0, 0 }, + { /* 7: RX_CH2 */ 1UL << 14, 0x0, 0 }, + { /* 8: RX_CH3 */ 1UL << 15, 0x0, 0 }, +}; + +/***************************************************************************** + * NAMING CONSTANT DECLARATIONS + ***************************************************************************** + */ +/* Sleep Time Definitions */ +#define HAL_DAWN_PKT_TX_DEQUE_SLEEP() osal_sleepThread(1000) /* us */ +#define HAL_DAWN_PKT_RX_DEQUE_SLEEP() osal_sleepThread(1000) /* us */ +#define HAL_DAWN_PKT_TX_ENQUE_RETRY_SLEEP() osal_sleepThread(1000) /* us */ +#define HAL_DAWN_PKT_RX_ENQUE_RETRY_SLEEP() osal_sleepThread(1000) /* us */ +#define HAL_DAWN_PKT_ALLOC_MEM_RETRY_SLEEP() osal_sleepThread(1000) /* us */ + +/* Network Device Definitions */ +/* In case that the watchdog alarm during warm-boot if intf isn't killed */ +#define HAL_DAWN_PKT_PORT_NUM (128) + +#define HAL_DAWN_PKT_TX_TIMEOUT (30*HZ) +#define HAL_DAWN_PKT_MAX_ETH_FRAME_SIZE (HAL_DAWN_PKT_RX_MAX_LEN) +#define HAL_DAWN_PKT_MAX_PORT_NUM (HAL_DAWN_PKT_PORT_NUM + 1) /* CPU port */ + +#define HAL_DAWN_PKT_NET_PROFILE_NUM_MAX (256) + +static HAL_DAWN_PKT_NETIF_PROFILE_T *_ptr_hal_dawn_pkt_profile_entry[HAL_DAWN_PKT_NET_PROFILE_NUM_MAX] = {0}; +static HAL_DAWN_PKT_NETIF_PORT_DB_T _hal_dawn_pkt_port_db[HAL_DAWN_PKT_MAX_PORT_NUM]; + +/***************************************************************************** + * MACRO VLAUE DECLARATIONS + ***************************************************************************** + */ + +/***************************************************************************** + * MACRO FUNCTION DECLARATIONS + ***************************************************************************** + */ +/*---------------------------------------------------------------------------*/ +#define HAL_DAWN_PKT_GET_DRV_CB_PTR(unit) (&_hal_dawn_pkt_drv_cb[unit]) +/*---------------------------------------------------------------------------*/ +#define HAL_DAWN_PKT_GET_TX_CB_PTR(unit) (&_hal_dawn_pkt_tx_cb[unit]) +#define HAL_DAWN_PKT_GET_TX_PDMA_PTR(unit, channel) (&_hal_dawn_pkt_tx_cb[unit].pdma[channel]) +#define HAL_DAWN_PKT_GET_TX_GPD_PTR(unit, channel, gpd) (&_hal_dawn_pkt_tx_cb[unit].pdma[channel].ptr_gpd_align_start_addr[gpd]) +/*---------------------------------------------------------------------------*/ +#define HAL_DAWN_PKT_GET_RX_CB_PTR(unit) (&_hal_dawn_pkt_rx_cb[unit]) +#define HAL_DAWN_PKT_GET_RX_PDMA_PTR(unit, channel) (&_hal_dawn_pkt_rx_cb[unit].pdma[channel]) +#define HAL_DAWN_PKT_GET_RX_GPD_PTR(unit, channel, gpd) (&_hal_dawn_pkt_rx_cb[unit].pdma[channel].ptr_gpd_align_start_addr[gpd]) +/*---------------------------------------------------------------------------*/ +#define HAL_DAWN_PKT_GET_PORT_DB(port) (&_hal_dawn_pkt_port_db[port]) +#define HAL_DAWN_PKT_GET_PORT_PROFILE_LIST(port) (_hal_dawn_pkt_port_db[port].ptr_profile_list) +#define HAL_DAWN_PKT_GET_PORT_NETDEV(port) _hal_dawn_pkt_port_db[port].ptr_net_dev + +/***************************************************************************** + * DATA TYPE DECLARATIONS + ***************************************************************************** + */ +/* ----------------------------------------------------------------------------------- General structure */ +typedef struct +{ + UI32_T unit; + UI32_T channel; + +} HAL_DAWN_PKT_ISR_COOKIE_T; + +typedef struct +{ + CLX_HUGE_T que_id; + CLX_SEMAPHORE_ID_T sema; + UI32_T len; /* Software CPU queue maximum length. */ + UI32_T weight; /* The weight for thread de-queue algorithm. */ + +} HAL_DAWN_PKT_SW_QUEUE_T; + +typedef struct +{ + /* handleErrorTask */ + CLX_THREAD_ID_T err_task_id; + + /* INTR dispatcher */ + CLX_ISRLOCK_ID_T intr_lock; + UI32_T intr_bitmap; + +#define HAL_DAWN_PKT_INIT_DRV (1UL << 0) +#define HAL_DAWN_PKT_INIT_TASK (1UL << 1) +#define HAL_DAWN_PKT_INIT_INTR (1UL << 2) +#define HAL_DAWN_PKT_INIT_RX_START (1UL << 3) + /* a bitmap to record the init status */ + UI32_T init_flag; + +} HAL_DAWN_PKT_DRV_CB_T; + +/* ----------------------------------------------------------------------------------- TX structure */ +typedef struct +{ + /* CLX_SEMAPHORE_ID_T sema; */ + + /* since the Tx GPD ring may be accessed by multiple process including + * ndo_start_xmit (SW IRQ), it must be protected with an ISRLOCK + * instead of the original semaphore + */ + CLX_ISRLOCK_ID_T ring_lock; + + UI32_T used_idx; /* SW send index = LAMP simulate the Tx HW index */ + UI32_T free_idx; /* SW free index */ + UI32_T used_gpd_num; + UI32_T free_gpd_num; + UI32_T gpd_num; + + HAL_DAWN_PKT_TX_GPD_T *ptr_gpd_start_addr; + HAL_DAWN_PKT_TX_GPD_T *ptr_gpd_align_start_addr; + BOOL_T err_flag; + + /* ASYNC */ + HAL_DAWN_PKT_TX_SW_GPD_T **pptr_sw_gpd_ring; + HAL_DAWN_PKT_TX_SW_GPD_T **pptr_sw_gpd_bulk; /* temporary store packets to be enque */ + + /* SYNC_INTR */ + CLX_SEMAPHORE_ID_T sync_intr_sema; + +} HAL_DAWN_PKT_TX_PDMA_T; + +typedef struct +{ + HAL_DAWN_PKT_TX_WAIT_T wait_mode; + HAL_DAWN_PKT_TX_PDMA_T pdma[HAL_DAWN_PKT_TX_CHANNEL_LAST]; + HAL_DAWN_PKT_TX_CNT_T cnt; + + /* handleTxDoneTask */ + CLX_THREAD_ID_T isr_task_id[HAL_DAWN_PKT_TX_CHANNEL_LAST]; + HAL_DAWN_PKT_ISR_COOKIE_T isr_task_cookie[HAL_DAWN_PKT_TX_CHANNEL_LAST]; + + /* txTask */ + HAL_DAWN_PKT_SW_QUEUE_T sw_queue; + CLX_SEMAPHORE_ID_T sync_sema; + CLX_THREAD_ID_T task_id; + BOOL_T running; /* TRUE when Init txTask + * FALSE when Destroy txTask + */ + /* to block net intf Tx in driver level since netif_tx_disable() + * cannot always prevent intf from Tx in time + */ + BOOL_T net_tx_allowed; + +} HAL_DAWN_PKT_TX_CB_T; + +/* ----------------------------------------------------------------------------------- RX structure */ +typedef struct +{ + CLX_SEMAPHORE_ID_T sema; + UI32_T cur_idx; /* SW free index */ + UI32_T gpd_num; + + HAL_DAWN_PKT_RX_GPD_T *ptr_gpd_start_addr; + HAL_DAWN_PKT_RX_GPD_T *ptr_gpd_align_start_addr; + BOOL_T err_flag; + struct sk_buff **pptr_skb_ring; +} HAL_DAWN_PKT_RX_PDMA_T; + +typedef struct +{ + /* Rx system configuration */ + UI32_T buf_len; + + HAL_DAWN_PKT_RX_SCHED_T sched_mode; + HAL_DAWN_PKT_RX_PDMA_T pdma[HAL_DAWN_PKT_RX_CHANNEL_LAST]; + HAL_DAWN_PKT_RX_CNT_T cnt; + + /* handleRxDoneTask */ + CLX_THREAD_ID_T isr_task_id[HAL_DAWN_PKT_RX_CHANNEL_LAST]; + HAL_DAWN_PKT_ISR_COOKIE_T isr_task_cookie[HAL_DAWN_PKT_RX_CHANNEL_LAST]; + + /* rxTask */ + HAL_DAWN_PKT_SW_QUEUE_T sw_queue[HAL_DAWN_PKT_RX_QUEUE_NUM]; + UI32_T deque_idx; + CLX_SEMAPHORE_ID_T sync_sema; + CLX_THREAD_ID_T task_id; + CLX_SEMAPHORE_ID_T deinit_sema; /* To sync-up the Rx-stop and thread flush queues */ + BOOL_T running; /* TRUE when rxStart + * FALSE when rxStop + */ + +} HAL_DAWN_PKT_RX_CB_T; + +/* ----------------------------------------------------------------------------------- Network Device */ +struct net_device_priv +{ + struct net_device *ptr_net_dev; + struct net_device_stats stats; + UI32_T unit; + UI32_T id; + UI32_T port; + UI16_T vlan; + UI32_T speed; +}; + +typedef enum +{ + HAL_DAWN_PKT_DEST_NETDEV = 0, + HAL_DAWN_PKT_DEST_SDK, +#if defined(NETIF_EN_NETLINK) + HAL_DAWN_PKT_DEST_NETLINK, +#endif + HAL_DAWN_PKT_DEST_DROP, + HAL_DAWN_PKT_DEST_LAST +} HAL_DAWN_PKT_DEST_T; + +/***************************************************************************** + * GLOBAL VARIABLE DECLARATIONS + ***************************************************************************** + */ + +/***************************************************************************** + * STATIC VARIABLE DECLARATIONS + ***************************************************************************** + */ +/*---------------------------------------------------------------------------*/ +static HAL_DAWN_PKT_DRV_CB_T _hal_dawn_pkt_drv_cb[CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM]; +static HAL_DAWN_PKT_TX_CB_T _hal_dawn_pkt_tx_cb[CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM]; +static HAL_DAWN_PKT_RX_CB_T _hal_dawn_pkt_rx_cb[CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM]; +/*---------------------------------------------------------------------------*/ + +/***************************************************************************** + * LOCAL SUBPROGRAM DECLARATIONS + ***************************************************************************** + */ +/* ----------------------------------------------------------------------------------- Interrupt */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_enableIntr( + const UI32_T unit, + const UI32_T intr_bitmap) +{ + HAL_DAWN_PKT_DRV_CB_T *ptr_cb = HAL_DAWN_PKT_GET_DRV_CB_PTR(unit); + CLX_IRQ_FLAGS_T irq_flag = 0; + UI32_T intr_en = 0; + + osal_takeIsrLock(&ptr_cb->intr_lock, &irq_flag); + osal_mdc_readPciReg(unit, HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_CP_COMMON_INT_EN_HI), &intr_en, sizeof(intr_en)); + intr_en |= intr_bitmap; + osal_mdc_writePciReg(unit, HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_CP_COMMON_INT_EN_HI), &intr_en, sizeof(intr_en)); + osal_giveIsrLock(&ptr_cb->intr_lock, &irq_flag); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_disableIntr( + const UI32_T unit, + const UI32_T intr_bitmap) +{ + HAL_DAWN_PKT_DRV_CB_T *ptr_cb = HAL_DAWN_PKT_GET_DRV_CB_PTR(unit); + CLX_IRQ_FLAGS_T irq_flag = 0; + UI32_T intr_en = 0; + + osal_takeIsrLock(&ptr_cb->intr_lock, &irq_flag); + osal_mdc_readPciReg(unit, HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_CP_COMMON_INT_EN_HI), &intr_en, sizeof(intr_en)); + intr_en &= ~intr_bitmap; + osal_mdc_writePciReg(unit, HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_CP_COMMON_INT_EN_HI), &intr_en, sizeof(intr_en)); + osal_giveIsrLock(&ptr_cb->intr_lock, &irq_flag); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_maskIntr( + const UI32_T unit, + const UI32_T intr_bitmap) +{ + HAL_DAWN_PKT_DRV_CB_T *ptr_cb = HAL_DAWN_PKT_GET_DRV_CB_PTR(unit); + CLX_IRQ_FLAGS_T irq_flag = 0; + + osal_takeIsrLock(&ptr_cb->intr_lock, &irq_flag); + osal_mdc_writePciReg(unit, HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_CP_COMMON_INT_MASK_CLR_HI), &intr_bitmap, sizeof(intr_bitmap)); + osal_giveIsrLock(&ptr_cb->intr_lock, &irq_flag); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_unmaskIntr( + const UI32_T unit, + const UI32_T intr_bitmap) +{ + HAL_DAWN_PKT_DRV_CB_T *ptr_cb = HAL_DAWN_PKT_GET_DRV_CB_PTR(unit); + CLX_IRQ_FLAGS_T irq_flag = 0; + + osal_takeIsrLock(&ptr_cb->intr_lock, &irq_flag); + osal_mdc_writePciReg(unit, HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_CP_COMMON_INT_MASK_SET_HI), &intr_bitmap, sizeof(intr_bitmap)); + osal_giveIsrLock(&ptr_cb->intr_lock, &irq_flag); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_dispatcher( + void *ptr_cookie) +{ + UI32_T unit = (UI32_T)((CLX_HUGE_T)ptr_cookie); + HAL_DAWN_PKT_DRV_CB_T *ptr_cb = HAL_DAWN_PKT_GET_DRV_CB_PTR(unit); + CLX_IRQ_FLAGS_T irq_flag = 0; + + UI32_T idx = 0, vec = sizeof(_hal_dawn_pkt_intr_vec) / sizeof(HAL_DAWN_PKT_INTR_VEC_T); + UI32_T intr_mask = ptr_cb->intr_bitmap; + UI32_T intr_unmask = 0; + UI32_T intr_status = 0; + + /* MASK, READ and CLEAR PKT IRQs */ + osal_takeIsrLock(&ptr_cb->intr_lock, &irq_flag); + osal_mdc_writePciReg(unit, HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_CP_COMMON_INT_MASK_CLR_HI), &intr_mask, sizeof(UI32_T)); + osal_mdc_readPciReg (unit, HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_CP_COMMON_INT_STAT_HI), &intr_status, sizeof(UI32_T)); + intr_status = intr_status & intr_mask; + osal_mdc_writePciReg(unit, HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_CP_COMMON_INT_CLR_HI), &intr_status, sizeof(UI32_T)); + osal_giveIsrLock(&ptr_cb->intr_lock, &irq_flag); + + /* Module thread handle and unmask the interrupt */ + intr_unmask = intr_status ^ intr_mask; + if (0x0 != intr_status) + { + for (idx = 0; idx < vec; idx++) + { + if (_hal_dawn_pkt_intr_vec[idx].intr_reg & intr_status) + { + osal_triggerEvent(&_hal_dawn_pkt_intr_vec[idx].intr_event); + _hal_dawn_pkt_intr_vec[idx].intr_cnt++; + } + } + } + + /* UNMASK other PKT IRQs */ + osal_takeIsrLock(&ptr_cb->intr_lock, &irq_flag); + osal_mdc_writePciReg(unit, HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_CP_COMMON_INT_MASK_SET_HI), &intr_unmask, sizeof(UI32_T)); + osal_giveIsrLock(&ptr_cb->intr_lock, &irq_flag); + + return (CLX_E_OK); +} + +/* ----------------------------------------------------------------------------------- RW HW Regs */ +/* FUNCTION NAME: _hal_dawn_pkt_startTxChannelReg + * PURPOSE: + * To issue "START" command to the target TX channel. + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * gpd_num -- The GPD ring length of the channel + * OUTPUT: + * None. + * RETURN: + * CLX_E_OK -- Successfully configure the register. + * CLX_E_OTHERS -- Configure the register failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_startTxChannelReg( + const UI32_T unit, + const HAL_DAWN_PKT_TX_CHANNEL_T channel, + const UI32_T gpd_num) +{ + HAL_DAWN_PKT_TCH_CMD_REG_T tch_cmd; + + tch_cmd.reg = 0x0; + tch_cmd.field.tch_start = 0x1; + tch_cmd.field.tch_gpd_add_no_lo = gpd_num & 0xff; + tch_cmd.field.tch_gpd_add_no_hi = (gpd_num & 0xff00) >> 8; + + osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_PDMA_TCH_REG(HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_TCH_CMD), channel), + &tch_cmd.reg, sizeof(HAL_DAWN_PKT_TCH_CMD_REG_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_startRxChannelReg + * PURPOSE: + * To issue "START" command to the target RX channel. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * gpd_num -- The GPD ring length of the channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure the register. + * CLX_E_OTHERS -- Configure the register failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_startRxChannelReg( + const UI32_T unit, + const HAL_DAWN_PKT_RX_CHANNEL_T channel, + const UI32_T gpd_num) +{ + HAL_DAWN_PKT_RCH_CMD_REG_T rch_cmd; + + rch_cmd.reg = 0x0; + rch_cmd.field.rch_start = 0x1; + rch_cmd.field.rch_gpd_add_no_lo = gpd_num & 0xff; + rch_cmd.field.rch_gpd_add_no_hi = (gpd_num & 0xff00) >> 8; + + osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_PDMA_RCH_REG(HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_RCH_CMD), channel), + &rch_cmd.reg, sizeof(HAL_DAWN_PKT_RCH_CMD_REG_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_resumeTxChannelReg + * PURPOSE: + * To issue "RESUME" command to the target TX channel. + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * gpd_num -- The GPD ring length of the channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure the register. + * CLX_E_OTHERS -- Configure the register failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_resumeTxChannelReg( + const UI32_T unit, + const HAL_DAWN_PKT_TX_CHANNEL_T channel, + const UI32_T gpd_num) +{ + HAL_DAWN_PKT_TCH_CMD_REG_T tch_cmd; + + tch_cmd.reg = 0x0; + tch_cmd.field.tch_resume = 0x1; + tch_cmd.field.tch_gpd_add_no_lo = gpd_num & 0xff; + tch_cmd.field.tch_gpd_add_no_hi = (gpd_num & 0xff00) >> 8; + + osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_PDMA_TCH_REG(HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_TCH_CMD), channel), + &tch_cmd.reg, sizeof(HAL_DAWN_PKT_TCH_CMD_REG_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_resumeRxChannelReg + * PURPOSE: + * To issue "RESUME" command to the target RX channel. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * gpd_num -- The GPD ring length of the channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure the register. + * CLX_E_OTHERS -- Configure the register failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_resumeRxChannelReg( + const UI32_T unit, + const HAL_DAWN_PKT_RX_CHANNEL_T channel, + const UI32_T gpd_num) +{ + HAL_DAWN_PKT_RCH_CMD_REG_T rch_cmd; + + rch_cmd.reg = 0x0; + rch_cmd.field.rch_resume = 0x1; + rch_cmd.field.rch_gpd_add_no_lo = gpd_num & 0xff; + rch_cmd.field.rch_gpd_add_no_hi = (gpd_num & 0xff00) >> 8; + + osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_PDMA_RCH_REG(HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_RCH_CMD), channel), + &rch_cmd.reg, sizeof(HAL_DAWN_PKT_RCH_CMD_REG_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_stopTxChannelReg + * PURPOSE: + * To issue "STOP" command to the target TX channel. + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure the register. + * CLX_E_OTHERS -- Configure the register failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_stopTxChannelReg( + const UI32_T unit, + const HAL_DAWN_PKT_TX_CHANNEL_T channel) +{ + HAL_DAWN_PKT_TCH_CMD_REG_T tch_cmd; + + tch_cmd.reg = 0x0; + tch_cmd.field.tch_stop = 0x1; + + osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_PDMA_TCH_REG(HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_TCH_CMD), channel), + &tch_cmd.reg, sizeof(HAL_DAWN_PKT_TCH_CMD_REG_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_stopRxChannelReg + * PURPOSE: + * To issue "STOP" command to the target RX channel. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure the register. + * CLX_E_OTHERS -- Configure the register failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_stopRxChannelReg( + const UI32_T unit, + const HAL_DAWN_PKT_RX_CHANNEL_T channel) +{ + HAL_DAWN_PKT_RCH_CMD_REG_T rch_cmd; + + rch_cmd.reg = 0x0; + rch_cmd.field.rch_stop = 0x1; + + osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_PDMA_RCH_REG(HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_RCH_CMD), channel), + &rch_cmd.reg, sizeof(HAL_DAWN_PKT_RCH_CMD_REG_T)); + + return (CLX_E_OK); +} + +/* ----------------------------------------------------------------------------------- Init HW Regs */ +/* FUNCTION NAME: _hal_dawn_pkt_setTxGpdStartAddrReg + * PURPOSE: + * To configure the start address and the length of target GPD ring of TX channel. + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * gpd_start_addr -- The start address of the GPD ring + * gpd_ring_sz -- The size of the GPD ring + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure the register. + * CLX_E_OTHERS -- Configure the register failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_setTxGpdStartAddrReg( + const UI32_T unit, + const HAL_DAWN_PKT_TX_CHANNEL_T channel, + const CLX_ADDR_T gpd_start_addr, + const UI32_T gpd_ring_sz) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + UI32_T tch_gpd_ring_start_addr_lo = 0; + UI32_T tch_gpd_ring_start_addr_hi = 0; + UI32_T tch_gpd_ring_size = 0; + + /* Configure the low 32-bit address. */ + tch_gpd_ring_start_addr_lo = (UI32_T)CLX_ADDR_64_LOW(gpd_start_addr); + + rc = osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_PDMA_TCH_REG(HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_TCH_GPD_RING_START_ADDR_LO), channel), + &tch_gpd_ring_start_addr_lo, sizeof(UI32_T)); + + /* Configure the high 32-bit address. */ + if (CLX_E_OK == rc) + { + tch_gpd_ring_start_addr_hi = (UI32_T)CLX_ADDR_64_HI(gpd_start_addr); + + rc = osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_PDMA_TCH_REG(HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_TCH_GPD_RING_START_ADDR_HI), channel), + &tch_gpd_ring_start_addr_hi, sizeof(UI32_T)); + } + + /* Configure the GPD ring size. */ + if (CLX_E_OK == rc) + { + tch_gpd_ring_size = gpd_ring_sz; + + rc = osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_PDMA_TCH_REG(HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_TCH_GPD_RING_SIZE), channel), + &tch_gpd_ring_size, sizeof(UI32_T)); + } + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_setRxGpdStartAddrReg + * PURPOSE: + * To configure the start address and the length of target GPD ring of RX channel. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * gpd_start_addr -- The start address of the GPD ring + * gpd_ring_sz -- The size of the GPD ring + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure the register. + * CLX_E_OTHERS -- Configure the register failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_setRxGpdStartAddrReg( + const UI32_T unit, + const HAL_DAWN_PKT_RX_CHANNEL_T channel, + const CLX_ADDR_T gpd_start_addr, + const UI32_T gpd_ring_sz) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + UI32_T rch_gpd_ring_start_addr_lo = 0; + UI32_T rch_gpd_ring_start_addr_hi = 0; + UI32_T rch_gpd_ring_size = 0; + + /* Configure the low 32-bit address. */ + rch_gpd_ring_start_addr_lo = (UI32_T)CLX_ADDR_64_LOW(gpd_start_addr); + + rc = osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_PDMA_RCH_REG(HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_RCH_GPD_RING_START_ADDR_LO), channel), + &rch_gpd_ring_start_addr_lo, sizeof(UI32_T)); + + /* Configure the high 32-bit address. */ + if (CLX_E_OK == rc) + { + rch_gpd_ring_start_addr_hi = (UI32_T)CLX_ADDR_64_HI(gpd_start_addr); + + rc = osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_PDMA_RCH_REG(HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_RCH_GPD_RING_START_ADDR_HI), channel), + &rch_gpd_ring_start_addr_hi, sizeof(UI32_T)); + } + + /* Configure the GPD ring size. */ + if (CLX_E_OK == rc) + { + rch_gpd_ring_size = gpd_ring_sz; + + rc = osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_PDMA_RCH_REG(HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_RCH_GPD_RING_SIZE), channel), + &rch_gpd_ring_size, sizeof(UI32_T)); + } + + return (rc); +} + +/* ----------------------------------------------------------------------------------- ISR HW Regs */ +/* FUNCTION NAME: _hal_dawn_pkt_maskAllTxL2IsrReg + * PURPOSE: + * To mask all the TX L2 interrupts for the specified channel. + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully mask all the TX L2 interrupts. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_maskAllTxL2IsrReg( + const UI32_T unit, + const HAL_DAWN_PKT_TX_CHANNEL_T channel) +{ + UI32_T reg = 0; + + HAL_DAWN_PKT_CLR_BITMAP(reg, + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_HWO_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_CHKSM_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_NO_OVFL_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_DMA_READ_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_BUF_SIZE_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_RUNT_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_OVSZ_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_LEN_MISMATCH_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_PKTPL_DMA_READ_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_COS_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_GT255_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_PFC | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_CREDIT_UDFL_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_DMA_WRITE_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_STOP_CMD_CPLT); + + osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_PDMA_TCH_REG(HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_TCH_INT_MASK), channel), + ®, sizeof(UI32_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_maskAllRxL2IsrReg + * PURPOSE: + * To mask all the L2 interrupts for the specified channel. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully mask all the L2 interrupts. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_maskAllRxL2IsrReg( + const UI32_T unit, + const HAL_DAWN_PKT_RX_CHANNEL_T channel) +{ + UI32_T reg = 0; + + HAL_DAWN_PKT_CLR_BITMAP(reg, + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_LOW | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_EMPTY | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_ERROR | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_GPD_CHKSM_ERROR | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_DMA_READ_ERROR | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_DMA_WRITE_ERROR | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_STOP_CMD_CPLT | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_GPD_GT255_ERROR | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_TOD_UNINIT | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_PKT_ERROR_DROP | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_UDSZ_DROP | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_OVSZ_DROP | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_CMDQ_OVF_DROP | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_FIFO_OVF_DROP); + + osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_PDMA_RCH_REG(HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_RCH_INT_MASK), channel), + ®, sizeof(UI32_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_unmaskAllTxL2IsrReg + * PURPOSE: + * To unmask all the TX L2 interrupts for the specified channel. + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully unmask all the TX L2 interrupts. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_unmaskAllTxL2IsrReg( + const UI32_T unit, + const HAL_DAWN_PKT_TX_CHANNEL_T channel) +{ + UI32_T reg = 0; + + HAL_DAWN_PKT_SET_BITMAP(reg, + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_HWO_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_CHKSM_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_NO_OVFL_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_DMA_READ_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_BUF_SIZE_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_RUNT_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_OVSZ_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_LEN_MISMATCH_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_PKTPL_DMA_READ_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_COS_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_GT255_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_PFC | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_CREDIT_UDFL_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_DMA_WRITE_ERROR | + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_STOP_CMD_CPLT); + + osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_PDMA_TCH_REG(HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_TCH_INT_MASK), channel), + ®, sizeof(UI32_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_unmaskAllRxL2IsrReg + * PURPOSE: + * To unmask all the L2 interrupts for the specified channel. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully unmask all the L2 interrupts. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_unmaskAllRxL2IsrReg( + const UI32_T unit, + const HAL_DAWN_PKT_RX_CHANNEL_T channel) +{ + UI32_T reg = 0; + + HAL_DAWN_PKT_SET_BITMAP(reg, + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_LOW | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_EMPTY | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_ERROR | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_GPD_CHKSM_ERROR | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_DMA_READ_ERROR | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_DMA_WRITE_ERROR | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_STOP_CMD_CPLT | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_GPD_GT255_ERROR | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_TOD_UNINIT | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_PKT_ERROR_DROP | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_UDSZ_DROP | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_OVSZ_DROP | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_CMDQ_OVF_DROP | + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_FIFO_OVF_DROP); + + osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_PDMA_RCH_REG(HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_RCH_INT_MASK), channel), + ®, sizeof(UI32_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_clearTxL2IsrStatusReg + * PURPOSE: + * To clear the status of TX L2 interrupts for the specified channel. + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * isr_bitmap -- The bitmap used to specify the target ISRs + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully clear L1 ISR status. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_clearTxL2IsrStatusReg( + const UI32_T unit, + const HAL_DAWN_PKT_TX_CHANNEL_T channel, + const HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_T isr_bitmap) +{ + UI32_T reg = 0; + + HAL_DAWN_PKT_SET_BITMAP(reg, isr_bitmap); + + osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_PDMA_TCH_REG(HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_TCH_INT_CLR), channel), + ®, sizeof(UI32_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_clearRxL2IsrStatusReg + * PURPOSE: + * To clear the status of RX L2 interrupts for the specified channel. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * isr_bitmap -- The bitmap used to specify the target ISRs + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully clear RX L2 ISR status. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_clearRxL2IsrStatusReg( + const UI32_T unit, + const HAL_DAWN_PKT_RX_CHANNEL_T channel, + const HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_T isr_bitmap) +{ + UI32_T reg = 0; + + HAL_DAWN_PKT_SET_BITMAP(reg, isr_bitmap); + + osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_PDMA_RCH_REG(HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_RCH_INT_CLR), channel), + ®, sizeof(UI32_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_dawn_pkt_getTxIntrCnt + * PURPOSE: + * To get the PDMA TX interrupt counters of the target channel. + * INPUT: + * unit -- The unit ID + * channel -- The target channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully get the counters. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_dawn_pkt_getTxIntrCnt( + const UI32_T unit, + const UI32_T channel, + UI32_T *ptr_intr_cnt) +{ + *ptr_intr_cnt = HAL_DAWN_PKT_TCH_CNT(unit, channel); + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_dawn_pkt_getRxIntrCnt + * PURPOSE: + * To get the PDMA RX interrupt counters of the target channel. + * INPUT: + * unit -- The unit ID + * channel -- The target channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully get the counters. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_dawn_pkt_getRxIntrCnt( + const UI32_T unit, + const UI32_T channel, + UI32_T *ptr_intr_cnt) +{ + *ptr_intr_cnt = HAL_DAWN_PKT_RCH_CNT(unit, channel); + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_dawn_pkt_getTxKnlCnt + * PURPOSE: + * To get the PDMA TX counters of the target channel. + * INPUT: + * unit -- The unit ID + * ptr_cookie -- Pointer of the TX cookie + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully get the counters. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_dawn_pkt_getTxKnlCnt( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_CH_CNT_COOKIE_T *ptr_cookie) +{ + HAL_DAWN_PKT_TX_CB_T *ptr_tx_cb = HAL_DAWN_PKT_GET_TX_CB_PTR(unit); + + osal_io_copyToUser(&ptr_cookie->tx_cnt, &ptr_tx_cb->cnt, sizeof(HAL_DAWN_PKT_TX_CNT_T)); + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_dawn_pkt_getRxKnlCnt + * PURPOSE: + * To get the PDMA RX counters of the target channel. + * INPUT: + * unit -- The unit ID + * ptr_cookie -- Pointer of the RX cookie + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully get the counters. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_dawn_pkt_getRxKnlCnt( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_CH_CNT_COOKIE_T *ptr_cookie) +{ + HAL_DAWN_PKT_RX_CB_T *ptr_rx_cb = HAL_DAWN_PKT_GET_RX_CB_PTR(unit); + + osal_io_copyToUser(&ptr_cookie->rx_cnt, &ptr_rx_cb->cnt, sizeof(HAL_DAWN_PKT_RX_CNT_T)); + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_dawn_pkt_clearTxKnlCnt + * PURPOSE: + * To clear the PDMA TX counters of the target channel. + * INPUT: + * unit -- The unit ID + * ptr_cookie -- Pointer of the TX cookie + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully clear the counters. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_dawn_pkt_clearTxKnlCnt( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_TX_COOKIE_T *ptr_cookie) +{ + HAL_DAWN_PKT_TX_CB_T *ptr_tx_cb = HAL_DAWN_PKT_GET_TX_CB_PTR(unit); + + osal_memset(&ptr_tx_cb->cnt, 0, sizeof(HAL_DAWN_PKT_TX_CNT_T)); + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_dawn_pkt_clearRxKnlCnt + * PURPOSE: + * To clear the PDMA RX counters of the target channel. + * INPUT: + * unit -- The unit ID + * ptr_cookie -- Pointer of the RX cookie + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully clear the counters. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_dawn_pkt_clearRxKnlCnt( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_RX_COOKIE_T *ptr_cookie) +{ + HAL_DAWN_PKT_RX_CB_T *ptr_rx_cb = HAL_DAWN_PKT_GET_RX_CB_PTR(unit); + + osal_memset(&ptr_rx_cb->cnt, 0, sizeof(HAL_DAWN_PKT_RX_CNT_T)); + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_dawn_pkt_setPortAttr + * PURPOSE: + * To set the port attributes such as status or speeds. + * INPUT: + * unit -- The unit ID + * ptr_cookie -- Pointer of the Port cookie + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully set the attributes. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_dawn_pkt_setPortAttr( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_PORT_COOKIE_T *ptr_cookie) +{ +#define HAL_DAWN_PKT_PORT_STATUS_UP (1) +#define HAL_DAWN_PKT_PORT_STATUS_DOWN (0) + struct net_device *ptr_net_dev; + struct net_device_priv *ptr_priv; + UI32_T port; + UI32_T status; + CLX_PORT_SPEED_T speed; + + osal_io_copyFromUser(&port, &ptr_cookie->port, sizeof(UI32_T)); + osal_io_copyFromUser(&status, &ptr_cookie->status, sizeof(UI32_T)); + osal_io_copyFromUser(&speed, &ptr_cookie->speed, sizeof(CLX_PORT_SPEED_T)); + + ptr_net_dev = HAL_DAWN_PKT_GET_PORT_NETDEV(port); + if ((NULL != ptr_net_dev) && (portspeed = SPEED_1000; + break; + case CLX_PORT_SPEED_10G: + ptr_priv->speed = SPEED_10000; + break; + case CLX_PORT_SPEED_25G: + ptr_priv->speed = 25000; + break; + case CLX_PORT_SPEED_40G: + ptr_priv->speed = 40000; + break; + case CLX_PORT_SPEED_50G: + ptr_priv->speed = 50000; + break; + case CLX_PORT_SPEED_100G: + ptr_priv->speed = 100000; + break; + default: + break; + } + } + return (CLX_E_OK); +} + +static void +_hal_dawn_pkt_lockRxChannelAll( + const UI32_T unit) +{ + UI32_T rch; + HAL_DAWN_PKT_RX_PDMA_T *ptr_rx_pdma; + + for (rch = 0; rch < HAL_DAWN_PKT_RX_CHANNEL_LAST; rch++) + { + ptr_rx_pdma = HAL_DAWN_PKT_GET_RX_PDMA_PTR(unit, rch); + osal_takeSemaphore(&ptr_rx_pdma->sema, CLX_SEMAPHORE_WAIT_FOREVER); + } +} + +static void +_hal_dawn_pkt_unlockRxChannelAll( + const UI32_T unit) +{ + UI32_T rch; + HAL_DAWN_PKT_RX_PDMA_T *ptr_rx_pdma; + + for (rch = 0; rch < HAL_DAWN_PKT_RX_CHANNEL_LAST; rch++) + { + ptr_rx_pdma = HAL_DAWN_PKT_GET_RX_PDMA_PTR(unit, rch); + osal_giveSemaphore(&ptr_rx_pdma->sema); + } +} + +#if defined(NETIF_EN_NETLINK) + +static CLX_ERROR_NO_T +_hal_dawn_pkt_setIntfProperty( + const UI32_T unit, + HAL_DAWN_PKT_NL_IOCTL_COOKIE_T *ptr_cookie) +{ + UI32_T intf_id; + NETIF_NL_INTF_PROPERTY_T property; + UI32_T param0; + UI32_T param1; + CLX_ERROR_NO_T rc; + + osal_io_copyFromUser(&intf_id, &ptr_cookie->intf_id, sizeof(UI32_T)); + osal_io_copyFromUser(&property, &ptr_cookie->property, sizeof(NETIF_NL_INTF_PROPERTY_T)); + osal_io_copyFromUser(¶m0, &ptr_cookie->param0, sizeof(UI32_T)); + osal_io_copyFromUser(¶m1, &ptr_cookie->param1, sizeof(UI32_T)); + + _hal_dawn_pkt_lockRxChannelAll(unit); + + rc = netif_nl_setIntfProperty(unit, intf_id, property, param0, param1); + + _hal_dawn_pkt_unlockRxChannelAll(unit); + + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + return (rc); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_getIntfProperty( + const UI32_T unit, + HAL_DAWN_PKT_NL_IOCTL_COOKIE_T *ptr_cookie) +{ + UI32_T intf_id; + NETIF_NL_INTF_PROPERTY_T property; + UI32_T param0; + UI32_T param1; + CLX_ERROR_NO_T rc; + + osal_io_copyFromUser(&intf_id, &ptr_cookie->intf_id, sizeof(UI32_T)); + osal_io_copyFromUser(&property, &ptr_cookie->property, sizeof(NETIF_NL_INTF_PROPERTY_T)); + osal_io_copyFromUser(¶m0, &ptr_cookie->param0, sizeof(UI32_T)); + + rc = netif_nl_getIntfProperty(unit, intf_id, property, ¶m0, ¶m1); + + osal_io_copyToUser(&ptr_cookie->param0, ¶m0, sizeof(UI32_T)); + osal_io_copyToUser(&ptr_cookie->param1, ¶m1, sizeof(UI32_T)); + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + return (rc); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_createNetlink( + const UI32_T unit, + HAL_DAWN_PKT_NL_IOCTL_COOKIE_T *ptr_cookie) +{ + NETIF_NL_NETLINK_T netlink; + UI32_T netlink_id; + CLX_ERROR_NO_T rc; + + osal_io_copyFromUser(&netlink, &ptr_cookie->netlink, sizeof(NETIF_NL_NETLINK_T)); + + _hal_dawn_pkt_lockRxChannelAll(unit); + + rc = netif_nl_createNetlink(unit, &netlink, &netlink_id); + + _hal_dawn_pkt_unlockRxChannelAll(unit); + + osal_io_copyToUser(&ptr_cookie->netlink.id, &netlink_id, sizeof(UI32_T)); + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + return (rc); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_destroyNetlink( + const UI32_T unit, + HAL_DAWN_PKT_NL_IOCTL_COOKIE_T *ptr_cookie) +{ + UI32_T netlink_id; + CLX_ERROR_NO_T rc; + + osal_io_copyFromUser(&netlink_id, &ptr_cookie->netlink.id, sizeof(UI32_T)); + + _hal_dawn_pkt_lockRxChannelAll(unit); + + rc = netif_nl_destroyNetlink(unit, netlink_id); + + _hal_dawn_pkt_unlockRxChannelAll(unit); + + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + return (rc); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_getNetlink( + const UI32_T unit, + HAL_DAWN_PKT_NL_IOCTL_COOKIE_T *ptr_cookie) +{ + UI32_T id; + NETIF_NL_NETLINK_T netlink; + CLX_ERROR_NO_T rc; + + osal_io_copyFromUser(&id, &ptr_cookie->netlink.id, sizeof(UI32_T)); + + rc = netif_nl_getNetlink(unit, id, &netlink); + if (CLX_E_OK == rc) + { + osal_io_copyToUser(&ptr_cookie->netlink, &netlink, sizeof(NETIF_NL_NETLINK_T)); + } + else + { + rc = CLX_E_ENTRY_NOT_FOUND; + } + + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + return (CLX_E_OK); +} + +#endif +/* ----------------------------------------------------------------------------------- independent func */ +/* FUNCTION NAME: _hal_dawn_pkt_enQueue + * PURPOSE: + * To enqueue the target data. + * INPUT: + * ptr_que -- Pointer for the target queue + * ptr_data -- Pointer for the data to be enqueued + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully enqueue the data. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_enQueue( + HAL_DAWN_PKT_SW_QUEUE_T *ptr_que, + void *ptr_data) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + + osal_takeSemaphore(&ptr_que->sema, CLX_SEMAPHORE_WAIT_FOREVER); + rc = osal_que_enque(&ptr_que->que_id, ptr_data); + osal_giveSemaphore(&ptr_que->sema); + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_deQueue + * PURPOSE: + * To dequeue the target data. + * INPUT: + * ptr_que -- Pointer for the target queue + * pptr_data -- Pointer for the data pointer to be dequeued + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully dequeue the data. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_deQueue( + HAL_DAWN_PKT_SW_QUEUE_T *ptr_que, + void **pptr_data) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + + osal_takeSemaphore(&ptr_que->sema, CLX_SEMAPHORE_WAIT_FOREVER); + rc = osal_que_deque(&ptr_que->que_id, pptr_data); + osal_giveSemaphore(&ptr_que->sema); + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_getQueueCount + * PURPOSE: + * To obtain the current GPD number in the target RX queue. + * INPUT: + * ptr_que -- Pointer for the target queue + * ptr_count -- Pointer for the data count + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully obtain the GPD count. + * CLX_E_BAD_PARAMETER -- Parameter pointer is null. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_getQueueCount( + HAL_DAWN_PKT_SW_QUEUE_T *ptr_que, + UI32_T *ptr_count) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + + osal_takeSemaphore(&ptr_que->sema, CLX_SEMAPHORE_WAIT_FOREVER); + osal_que_getCount(&ptr_que->que_id, ptr_count); + osal_giveSemaphore(&ptr_que->sema); + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_allocRxPayloadBuf + * PURPOSE: + * To allocate the RX packet payload buffer for the GPD. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * gpd_idx -- The current GPD index + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully allocate the buffer. + * CLX_E_NO_MEMORY -- Allocate the buffer failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_allocRxPayloadBuf( + const UI32_T unit, + const UI32_T channel, + const UI32_T gpd_idx) +{ + CLX_ERROR_NO_T rc = CLX_E_NO_MEMORY; + HAL_DAWN_PKT_RX_CB_T *ptr_rx_cb = HAL_DAWN_PKT_GET_RX_CB_PTR(unit); + volatile HAL_DAWN_PKT_RX_GPD_T *ptr_rx_gpd = HAL_DAWN_PKT_GET_RX_GPD_PTR(unit, channel, gpd_idx); + CLX_ADDR_T phy_addr = 0; + + HAL_DAWN_PKT_RX_PDMA_T *ptr_rx_pdma = HAL_DAWN_PKT_GET_RX_PDMA_PTR(unit, channel); + struct sk_buff *ptr_skb = NULL; + + ptr_skb = osal_skb_alloc(ptr_rx_cb->buf_len); + if (NULL != ptr_skb) + { + /* map skb to dma */ + phy_addr = osal_skb_mapDma(ptr_skb, DMA_FROM_DEVICE); + if (0x0 == phy_addr) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_ERR, + "u=%u, rxch=%u, skb dma map err, size=%u\n", + unit, channel, ptr_skb->len); + osal_skb_free(ptr_skb); + rc = CLX_E_NO_MEMORY; + } + else + { + ptr_rx_pdma->pptr_skb_ring[gpd_idx] = ptr_skb; + rc = CLX_E_OK; + } + } + + if (CLX_E_OK == rc) + { + ptr_rx_gpd->data_buf_addr_hi = CLX_ADDR_64_HI(phy_addr); + ptr_rx_gpd->data_buf_addr_lo = CLX_ADDR_64_LOW(phy_addr); + ptr_rx_gpd->avbl_buf_len = ptr_rx_cb->buf_len; + } + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_freeRxPayloadBuf + * PURPOSE: + * To free the RX packet payload buffer for the GPD. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * gpd_idx -- The current GPD index + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully free the buffer. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_freeRxPayloadBuf( + const UI32_T unit, + const UI32_T channel, + const UI32_T gpd_idx) +{ + CLX_ERROR_NO_T rc = CLX_E_OTHERS; + volatile HAL_DAWN_PKT_RX_GPD_T *ptr_rx_gpd = HAL_DAWN_PKT_GET_RX_GPD_PTR(unit, channel, gpd_idx); + CLX_ADDR_T phy_addr = 0; + + HAL_DAWN_PKT_RX_PDMA_T *ptr_rx_pdma = HAL_DAWN_PKT_GET_RX_PDMA_PTR(unit, channel); + struct sk_buff *ptr_skb = NULL; + + phy_addr = CLX_ADDR_32_TO_64(ptr_rx_gpd->data_buf_addr_hi, ptr_rx_gpd->data_buf_addr_lo); + if (0x0 != phy_addr) + { + /* unmap dma */ + ptr_skb = ptr_rx_pdma->pptr_skb_ring[gpd_idx]; + osal_skb_unmapDma(phy_addr, ptr_skb->len, DMA_FROM_DEVICE); + osal_skb_free(ptr_skb); + rc = CLX_E_OK; + } + + if (CLX_E_OK == rc) + { + ptr_rx_gpd->data_buf_addr_hi = 0x0; + ptr_rx_gpd->data_buf_addr_lo = 0x0; + } + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_freeRxPayloadBufGpd + * PURPOSE: + * To free the RX packet payload buffer for the GPD. + * INPUT: + * unit -- The unit ID + * ptr_sw_gpd -- The pointer of RX SW GPD + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully free the buffer. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_freeRxPayloadBufGpd( + const UI32_T unit, + HAL_DAWN_PKT_RX_SW_GPD_T *ptr_sw_gpd) +{ + CLX_ERROR_NO_T rc = CLX_E_OTHERS; + CLX_ADDR_T phy_addr = 0; + + struct sk_buff *ptr_skb = NULL; + + phy_addr = CLX_ADDR_32_TO_64(ptr_sw_gpd->rx_gpd.data_buf_addr_hi, ptr_sw_gpd->rx_gpd.data_buf_addr_lo); + if (0x0 != phy_addr) + { + ptr_skb = ptr_sw_gpd->ptr_cookie; + osal_skb_free(ptr_skb); + rc = CLX_E_OK; + } + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_initTxPdmaRing + * PURPOSE: + * To initialize the GPD ring of target TX channel. + * + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully initialize the GPD ring. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_initTxPdmaRing( + const UI32_T unit, + const HAL_DAWN_PKT_TX_CHANNEL_T channel) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_DAWN_PKT_TX_PDMA_T *ptr_tx_pdma = HAL_DAWN_PKT_GET_TX_PDMA_PTR(unit, channel); + volatile HAL_DAWN_PKT_TX_GPD_T *ptr_tx_gpd = NULL; + CLX_ADDR_T phy_addr = 0; + UI32_T gpd_idx = 0; + + for (gpd_idx = 0; gpd_idx < ptr_tx_pdma->gpd_num; gpd_idx++) + { + ptr_tx_gpd = HAL_DAWN_PKT_GET_TX_GPD_PTR(unit, channel, gpd_idx); + osal_memset((void *)ptr_tx_gpd, 0x0, sizeof(HAL_DAWN_PKT_TX_GPD_T)); + ptr_tx_gpd->ioc = HAL_DAWN_PKT_IOC_HAS_INTR; + ptr_tx_gpd->ch = HAL_DAWN_PKT_CH_LAST_GPD; + ptr_tx_gpd->hwo = HAL_DAWN_PKT_HWO_SW_OWN; + osal_dma_flushCache((void *)ptr_tx_gpd, sizeof(HAL_DAWN_PKT_TX_GPD_T)); + } + + phy_addr = osal_dma_convertVirtToPhy(ptr_tx_pdma->ptr_gpd_align_start_addr); + rc = _hal_dawn_pkt_setTxGpdStartAddrReg(unit, channel, phy_addr, ptr_tx_pdma->gpd_num); + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_initRxPdmaRing + * PURPOSE: + * To initialize the RX GPD ring. + * INPUT: + * unit -- The target unit + * channel -- The target RX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully initialize the RX GPD ring. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_initRxPdmaRing( + const UI32_T unit, + const HAL_DAWN_PKT_RX_CHANNEL_T channel) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_DAWN_PKT_RX_PDMA_T *ptr_rx_pdma = HAL_DAWN_PKT_GET_RX_PDMA_PTR(unit, channel); + volatile HAL_DAWN_PKT_RX_GPD_T *ptr_rx_gpd = NULL; + CLX_ADDR_T phy_addr = 0; + UI32_T gpd_idx = 0; + + for (gpd_idx = 0; gpd_idx < ptr_rx_pdma->gpd_num; gpd_idx++) + { + ptr_rx_gpd = HAL_DAWN_PKT_GET_RX_GPD_PTR(unit, channel, gpd_idx); + osal_memset((void *)ptr_rx_gpd, 0x0, sizeof(HAL_DAWN_PKT_RX_GPD_T)); + ptr_rx_gpd->ioc = HAL_DAWN_PKT_IOC_NO_INTR; + ptr_rx_gpd->hwo = HAL_DAWN_PKT_HWO_SW_OWN; + osal_dma_flushCache((void *)ptr_rx_gpd, sizeof(HAL_DAWN_PKT_RX_GPD_T)); + } + + phy_addr = osal_dma_convertVirtToPhy(ptr_rx_pdma->ptr_gpd_align_start_addr); + rc = _hal_dawn_pkt_setRxGpdStartAddrReg(unit, channel, phy_addr, ptr_rx_pdma->gpd_num); + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_initRxPdmaRingBuf + * PURPOSE: + * To de-init the Rx PDMA ring configuration. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully de-init the Rx PDMA ring. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_initRxPdmaRingBuf( + const UI32_T unit, + const HAL_DAWN_PKT_RX_CHANNEL_T channel) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_DAWN_PKT_RX_CB_T *ptr_rx_cb = HAL_DAWN_PKT_GET_RX_CB_PTR(unit); + HAL_DAWN_PKT_RX_PDMA_T *ptr_rx_pdma = HAL_DAWN_PKT_GET_RX_PDMA_PTR(unit, channel); + volatile HAL_DAWN_PKT_RX_GPD_T *ptr_rx_gpd = NULL; + UI32_T gpd_idx = 0; + + if (0 == ptr_rx_cb->buf_len) + { + return (CLX_E_BAD_PARAMETER); + } + + for (gpd_idx = 0; gpd_idx < ptr_rx_pdma->gpd_num; gpd_idx++) + { + ptr_rx_gpd = HAL_DAWN_PKT_GET_RX_GPD_PTR(unit, channel, gpd_idx); + osal_dma_invalidateCache((void *)ptr_rx_gpd, sizeof(HAL_DAWN_PKT_RX_GPD_T)); + + rc = _hal_dawn_pkt_allocRxPayloadBuf(unit, channel, gpd_idx); + if (CLX_E_OK == rc) + { + ptr_rx_gpd->ioc = HAL_DAWN_PKT_IOC_HAS_INTR; + ptr_rx_gpd->hwo = HAL_DAWN_PKT_HWO_HW_OWN; + osal_dma_flushCache((void *)ptr_rx_gpd, sizeof(HAL_DAWN_PKT_RX_GPD_T)); + } + else + { + ptr_rx_cb->cnt.no_memory++; + break; + } + } + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_deinitRxPdmaRingBuf + * PURPOSE: + * To de-init the Rx PDMA ring configuration. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully de-init the Rx PDMA ring. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_deinitRxPdmaRingBuf( + const UI32_T unit, + const HAL_DAWN_PKT_RX_CHANNEL_T channel) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_DAWN_PKT_RX_PDMA_T *ptr_rx_pdma = HAL_DAWN_PKT_GET_RX_PDMA_PTR(unit, channel); + volatile HAL_DAWN_PKT_RX_GPD_T *ptr_rx_gpd = NULL; + UI32_T gpd_idx = 0; + + for (gpd_idx = 0; ((gpd_idx < ptr_rx_pdma->gpd_num) && (CLX_E_OK == rc)); gpd_idx++) + { + /* mark the GPD as invalid to prevent Rx-done task to process it */ + ptr_rx_gpd = HAL_DAWN_PKT_GET_RX_GPD_PTR(unit, channel, gpd_idx); + ptr_rx_gpd->hwo = HAL_DAWN_PKT_HWO_HW_OWN; + osal_dma_flushCache((void *)ptr_rx_gpd, sizeof(HAL_DAWN_PKT_RX_GPD_T)); + + rc = _hal_dawn_pkt_freeRxPayloadBuf(unit, channel, gpd_idx); + } + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_recoverTxPdma + * PURPOSE: + * To recover the PDMA status to the initial state. + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully recover PDMA. + * NOTES: + * + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_recoverTxPdma( + const UI32_T unit, + const HAL_DAWN_PKT_TX_CHANNEL_T channel) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_DAWN_PKT_TX_PDMA_T *ptr_tx_pdma = HAL_DAWN_PKT_GET_TX_PDMA_PTR(unit, channel); + + /* Release the software GPD ring and configure it again. */ + ptr_tx_pdma->used_idx = 0; + ptr_tx_pdma->free_idx = 0; + ptr_tx_pdma->used_gpd_num = 0; + ptr_tx_pdma->free_gpd_num = ptr_tx_pdma->gpd_num; + + _hal_dawn_pkt_stopTxChannelReg(unit, channel); + rc = _hal_dawn_pkt_initTxPdmaRing(unit, channel); + _hal_dawn_pkt_startTxChannelReg(unit, channel, 0); + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_recoverRxPdma + * PURPOSE: + * To recover the RX PDMA from the error state. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully recovery the PDMA. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_recoverRxPdma( + const UI32_T unit, + const HAL_DAWN_PKT_RX_CHANNEL_T channel) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_DAWN_PKT_RX_PDMA_T *ptr_rx_pdma = HAL_DAWN_PKT_GET_RX_PDMA_PTR(unit, channel); + + /* Release the software GPD ring and configure it again. */ + ptr_rx_pdma->cur_idx = 0; + + _hal_dawn_pkt_stopRxChannelReg(unit, channel); + rc = _hal_dawn_pkt_deinitRxPdmaRingBuf(unit, channel); + if (CLX_E_OK != rc) + { + return (rc); + } + rc = _hal_dawn_pkt_initRxPdmaRing(unit, channel); + if (CLX_E_OK != rc) + { + return (rc); + } + rc = _hal_dawn_pkt_initRxPdmaRingBuf(unit, channel); + if (CLX_E_OK != rc) + { + return (rc); + } + _hal_dawn_pkt_startRxChannelReg(unit, channel, ptr_rx_pdma->gpd_num); + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_freeTxGpdList + * PURPOSE: + * To free the TX SW GPD link list. + * INPUT: + * unit -- The unit ID + * ptr_sw_gpd -- The pointer of TX SW GPD + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully free the GPD list. + * NOTES: + * None + */ +static void +_hal_dawn_pkt_freeTxGpdList( + UI32_T unit, + HAL_DAWN_PKT_TX_SW_GPD_T *ptr_sw_gpd) +{ + HAL_DAWN_PKT_TX_SW_GPD_T *ptr_sw_gpd_cur = NULL; + + while (NULL != ptr_sw_gpd) + { + ptr_sw_gpd_cur = ptr_sw_gpd; + ptr_sw_gpd = ptr_sw_gpd->ptr_next; + osal_free(ptr_sw_gpd_cur); + } +} + +/* FUNCTION NAME: _hal_dawn_pkt_freeRxGpdList + * PURPOSE: + * To free the RX SW GPD link list. + * INPUT: + * unit -- The unit ID + * ptr_sw_gpd -- The pointer of RX SW GPD + * free_payload -- TRUE: To free the buf in SDK, FALSE: in user process. + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully recovery the PDMA. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_freeRxGpdList( + UI32_T unit, + HAL_DAWN_PKT_RX_SW_GPD_T *ptr_sw_gpd, + BOOL_T free_payload) +{ + HAL_DAWN_PKT_RX_SW_GPD_T *ptr_sw_gpd_cur = NULL; + + while (NULL != ptr_sw_gpd) + { + ptr_sw_gpd_cur = ptr_sw_gpd; + ptr_sw_gpd = ptr_sw_gpd->ptr_next; + if (TRUE == free_payload) + { + _hal_dawn_pkt_freeRxPayloadBufGpd(unit, ptr_sw_gpd_cur); + } + osal_free(ptr_sw_gpd_cur); + } + + return (CLX_E_OK); +} + +/* ----------------------------------------------------------------------------------- pkt_drv */ +/* FUNCTION NAME: _hal_dawn_pkt_txEnQueueBulk + * PURPOSE: + * To enqueue numbers of packet in the bulk buffer + * INPUT: + * unit -- The unit ID + * channel -- The target channel + * number -- The number of packet to be enqueue + * OUTPUT: + * None + * RETURN: + * None + * NOTES: + * None + */ +static void +_hal_dawn_pkt_txEnQueueBulk( + const UI32_T unit, + const UI32_T channel, + const UI32_T number) +{ + HAL_DAWN_PKT_TX_PDMA_T *ptr_tx_pdma = HAL_DAWN_PKT_GET_TX_PDMA_PTR(unit, channel); + HAL_DAWN_PKT_TX_SW_GPD_T *ptr_sw_gpd = NULL; + UI32_T idx; + + for (idx = 0; idx < number; idx++) + { + ptr_sw_gpd = ptr_tx_pdma->pptr_sw_gpd_bulk[idx]; + ptr_tx_pdma->pptr_sw_gpd_bulk[idx] = NULL; + if (NULL != ptr_sw_gpd->callback) + { + ptr_sw_gpd->callback(unit, ptr_sw_gpd, ptr_sw_gpd->ptr_cookie); + } + } +} + + +/* FUNCTION NAME: _hal_dawn_pkt_strictTxDeQueue + * PURPOSE: + * To dequeue the packets based on the strict algorithm. + * INPUT: + * unit -- The unit ID + * ptr_cookie -- Pointer of the TX cookie + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully dequeue the packets. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_strictTxDeQueue( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_TX_COOKIE_T *ptr_cookie) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_DAWN_PKT_TX_CB_T *ptr_tx_cb = HAL_DAWN_PKT_GET_TX_CB_PTR(unit); + HAL_DAWN_PKT_TX_SW_GPD_T *ptr_sw_gpd = NULL; + CLX_ADDR_T sw_gpd_addr; + UI32_T que_cnt = 0; + + /* get queue count */ + _hal_dawn_pkt_getQueueCount(&ptr_tx_cb->sw_queue, &que_cnt); + + /* wait txTask event */ + if (0 == que_cnt) + { + osal_waitEvent(&ptr_tx_cb->sync_sema); + if (FALSE == ptr_tx_cb->running) + { + return (CLX_E_OTHERS); /* deinit */ + } + + ptr_tx_cb->cnt.wait_event++; + + /* re-get queue count */ + _hal_dawn_pkt_getQueueCount(&ptr_tx_cb->sw_queue, &que_cnt); + } + + /* deque */ + if (que_cnt > 0) + { + rc = _hal_dawn_pkt_deQueue(&ptr_tx_cb->sw_queue, (void **)&ptr_sw_gpd); + if (CLX_E_OK == rc) + { + ptr_tx_cb->cnt.deque_ok++; + + sw_gpd_addr = (CLX_ADDR_T)ptr_sw_gpd->ptr_cookie; + + /* Give the address of pre-saved SW GPD back to userspace */ + osal_io_copyToUser(&ptr_cookie->done_sw_gpd_addr, + &sw_gpd_addr, + sizeof(CLX_ADDR_T)); + + /* free kernel sw_gpd */ + _hal_dawn_pkt_freeTxGpdList(unit, ptr_sw_gpd); + } + else + { + ptr_tx_cb->cnt.deque_fail++; + } + } + else + { + /* It may happen at last gpd, return error and do not invoke callback. */ + rc = CLX_E_OTHERS; + } + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_rxCheckReason + * PURPOSE: + * To check the packets to linux kernel/user. + * INPUT: + * ptr_rx_gpd -- Pointer of the RX GPD + * ptr_hit_prof -- Pointer of the hit flag + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully dispatch the packets. + * NOTES: + * Reference to pkt_srv. + */ +static void +_hal_dawn_pkt_rxCheckReason( + volatile HAL_DAWN_PKT_RX_GPD_T *ptr_rx_gpd, + HAL_DAWN_PKT_NETIF_PROFILE_T *ptr_profile, + BOOL_T *ptr_hit_prof) +{ + HAL_PKT_RX_REASON_BITMAP_T *ptr_reason_bitmap = &ptr_profile->reason_bitmap; + UI32_T bitval = 0; + UI32_T bitmap = 0x0; + + if (0 == (ptr_profile->flags & HAL_DAWN_PKT_NETIF_PROFILE_FLAGS_REASON)) + { + /* It means that reason doesn't metters */ + *ptr_hit_prof = TRUE; + return; + } + +#define HAL_DAWN_PKT_DI_NON_L3_CPU_MIN (HAL_EXCPT_CPU_BASE_ID + HAL_EXCPT_CPU_NON_L3_MIN) +#define HAL_DAWN_PKT_DI_NON_L3_CPU_MAX (HAL_EXCPT_CPU_BASE_ID + HAL_EXCPT_CPU_NON_L3_MAX) +#define HAL_DAWN_PKT_DI_L3_CPU_MIN (HAL_EXCPT_CPU_BASE_ID + HAL_EXCPT_CPU_L3_MIN) +#define HAL_DAWN_PKT_DI_L3_CPU_MAX (HAL_EXCPT_CPU_BASE_ID + HAL_EXCPT_CPU_L3_MAX) + + switch (ptr_rx_gpd->itmh_eth.typ) + { + case HAL_DAWN_PKT_TMH_TYPE_ITMH_ETH: + + /* IPP non-L3 exception */ + if (ptr_rx_gpd->itmh_eth.dst_idx >= HAL_DAWN_PKT_DI_NON_L3_CPU_MIN && + ptr_rx_gpd->itmh_eth.dst_idx <= HAL_DAWN_PKT_DI_NON_L3_CPU_MAX) + { + bitval = ptr_rx_gpd->itmh_eth.dst_idx - HAL_DAWN_PKT_DI_NON_L3_CPU_MIN; + bitmap = 1UL << (bitval % 32); + if (0 != (ptr_reason_bitmap->ipp_excpt_bitmap[bitval / 32] & bitmap)) + { + *ptr_hit_prof = TRUE; + break; + } + } + + /* IPP L3 exception */ + if (ptr_rx_gpd->itmh_eth.dst_idx >= HAL_DAWN_PKT_DI_L3_CPU_MIN && + ptr_rx_gpd->itmh_eth.dst_idx <= HAL_DAWN_PKT_DI_L3_CPU_MAX) + { + bitmap = ptr_rx_gpd->itmh_eth.dst_idx - HAL_DAWN_PKT_DI_L3_CPU_MIN; + if (0 != (ptr_reason_bitmap->ipp_l3_excpt_bitmap[0] & bitmap)) + { + *ptr_hit_prof = TRUE; + break; + } + } + + /* IPP cp_to_cpu_bmap */ + bitmap = ptr_rx_gpd->itmh_eth.cp_to_cpu_bmap; + if (0 != (ptr_reason_bitmap->ipp_copy2cpu_bitmap[0] & bitmap)) + { + *ptr_hit_prof = TRUE; + break; + } + + /* IPP cp_to_cpu_rsn */ + bitval = ptr_rx_gpd->itmh_eth.cp_to_cpu_code; + bitmap = 1UL << (bitval % 32); + if (0 != (ptr_reason_bitmap->ipp_rsn_bitmap[bitval / 32] & bitmap)) + { + *ptr_hit_prof = TRUE; + break; + } + break; + + case HAL_DAWN_PKT_TMH_TYPE_ITMH_FAB: + case HAL_DAWN_PKT_TMH_TYPE_ETMH_FAB: + break; + + case HAL_DAWN_PKT_TMH_TYPE_ETMH_ETH: + + /* EPP exception */ + if (1 == ptr_rx_gpd->etmh_eth.redir) + { + bitval = ptr_rx_gpd->etmh_eth.excpt_code_mir_bmap; + bitmap = 1UL << (bitval % 32); + if (0 != (ptr_reason_bitmap->epp_excpt_bitmap[bitval / 32] & bitmap)) + { + *ptr_hit_prof = TRUE; + break; + } + } + + /* EPP cp_to_cpu_bmap */ + bitmap = ((ptr_rx_gpd->etmh_eth.cp_to_cpu_bmap_w0 << 7) | + (ptr_rx_gpd->etmh_eth.cp_to_cpu_bmap_w1)); + if (0 != (ptr_reason_bitmap->epp_copy2cpu_bitmap[0] & bitmap)) + { + *ptr_hit_prof = TRUE; + break; + } + break; + + default: + *ptr_hit_prof = FALSE; + break; + } +} + +static BOOL_T +_hal_dawn_pkt_comparePatternWithPayload( + volatile HAL_DAWN_PKT_RX_GPD_T *ptr_rx_gpd, + const UI8_T *ptr_pattern, + const UI8_T *ptr_mask, + const UI32_T offset) +{ + CLX_ADDR_T phy_addr = 0; + UI8_T *ptr_virt_addr = NULL; + UI32_T idx; + + /* Get the packet payload */ + phy_addr = CLX_ADDR_32_TO_64(ptr_rx_gpd->data_buf_addr_hi, ptr_rx_gpd->data_buf_addr_lo); + ptr_virt_addr = (C8_T *) osal_dma_convertPhyToVirt(phy_addr); + + for (idx=0; idxflags & (HAL_DAWN_PKT_NETIF_PROFILE_FLAGS_PATTERN_0 | + HAL_DAWN_PKT_NETIF_PROFILE_FLAGS_PATTERN_1 | + HAL_DAWN_PKT_NETIF_PROFILE_FLAGS_PATTERN_2 | + HAL_DAWN_PKT_NETIF_PROFILE_FLAGS_PATTERN_3)) != 0) + { + /* Need to compare the payload with at least one of the four patterns */ + /* Pre-assume that the result is positive */ + *ptr_hit_prof = TRUE; + + /* If any of the following comparison fails, the result will be changed to negtive */ + } + else + { + return; + } + + for (idx=0; idxflags & (HAL_DAWN_PKT_NETIF_PROFILE_FLAGS_PATTERN_0 << idx))) + { + match = _hal_dawn_pkt_comparePatternWithPayload(ptr_rx_gpd, + ptr_profile->pattern[idx], + ptr_profile->mask[idx], + ptr_profile->offset[idx]); + if (TRUE == match) + { + /* Do nothing */ + } + else + { + /* Change the result to negtive */ + *ptr_hit_prof = FALSE; + break; + } + } + } +} + +static void +_hal_dawn_pkt_matchUserProfile( + volatile HAL_DAWN_PKT_RX_GPD_T *ptr_rx_gpd, + HAL_DAWN_PKT_PROFILE_NODE_T *ptr_profile_list, + HAL_DAWN_PKT_NETIF_PROFILE_T **pptr_profile_hit) +{ + HAL_DAWN_PKT_PROFILE_NODE_T *ptr_curr_node = ptr_profile_list; + BOOL_T hit; + + *pptr_profile_hit = NULL; + + while (NULL != ptr_curr_node) + { + /* 1st match reason */ + _hal_dawn_pkt_rxCheckReason(ptr_rx_gpd, ptr_curr_node->ptr_profile, &hit); + if (TRUE == hit) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_PROFILE, + "rx prof matched by reason\n"); + + /* Then, check pattern */ + _hal_dawn_pkt_rxCheckPattern(ptr_rx_gpd, ptr_curr_node->ptr_profile, &hit); + if (TRUE == hit) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_PROFILE, + "rx prof matched by pattern\n"); + + *pptr_profile_hit = ptr_curr_node->ptr_profile; + break; + } + } + + /* Seach the next profile (priority lower) */ + ptr_curr_node = ptr_curr_node->ptr_next_node; + } +} + +static void +_hal_dawn_pkt_getPacketDest( + volatile HAL_DAWN_PKT_RX_GPD_T *ptr_rx_gpd, + HAL_DAWN_PKT_DEST_T *ptr_dest, + void **pptr_cookie) +{ + UI32_T port; + HAL_DAWN_PKT_PROFILE_NODE_T *ptr_profile_list; + HAL_DAWN_PKT_NETIF_PROFILE_T *ptr_profile_hit; + + port = ptr_rx_gpd->itmh_eth.igr_phy_port; + ptr_profile_list = HAL_DAWN_PKT_GET_PORT_PROFILE_LIST(port); + + _hal_dawn_pkt_matchUserProfile(ptr_rx_gpd, + ptr_profile_list, + &ptr_profile_hit); + if (NULL != ptr_profile_hit) + { +#if defined(NETIF_EN_NETLINK) + if (HAL_DAWN_PKT_NETIF_RX_DST_NETLINK == ptr_profile_hit->dst_type) + { + *ptr_dest = HAL_DAWN_PKT_DEST_NETLINK; + *pptr_cookie = (void *)&ptr_profile_hit->netlink; + } + else + { + *ptr_dest = HAL_DAWN_PKT_DEST_SDK; + } +#else + *ptr_dest = HAL_DAWN_PKT_DEST_SDK; +#endif + } + else + { + *ptr_dest = HAL_DAWN_PKT_DEST_NETDEV; + } +} + +/* FUNCTION NAME: _hal_dawn_pkt_rxEnQueue + * PURPOSE: + * To enqueue the packets to multiple queues. + * INPUT: + * unit -- The unit ID + * channel -- The target channel + * ptr_sw_gpd -- Pointer for the SW Rx GPD link list + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully enqueue the packets. + * NOTES: + * None + */ +static void +_hal_dawn_pkt_rxEnQueue( + const UI32_T unit, + const UI32_T channel, + HAL_DAWN_PKT_RX_SW_GPD_T *ptr_sw_gpd) +{ + HAL_DAWN_PKT_RX_CB_T *ptr_rx_cb = HAL_DAWN_PKT_GET_RX_CB_PTR(unit); + HAL_DAWN_PKT_RX_SW_GPD_T *ptr_sw_first_gpd = ptr_sw_gpd; + void *ptr_virt_addr = NULL; + CLX_ADDR_T phy_addr = 0; + HAL_DAWN_PKT_DEST_T dest_type; + + /* skb meta */ + UI32_T port = 0, len = 0, total_len = 0; + struct net_device *ptr_net_dev = NULL; + struct net_device_priv *ptr_priv = NULL; + struct sk_buff *ptr_skb = NULL, *ptr_merge_skb = NULL; + UI32_T copy_offset; + void *ptr_dest; + +#if defined(PERF_EN_TEST) + /* To verify kernel Rx performance */ + if (CLX_E_OK == perf_rxTest()) + { + while (NULL != ptr_sw_gpd) + { + len += (HAL_DAWN_PKT_CH_LAST_GPD == ptr_sw_gpd->rx_gpd.ch)? + ptr_sw_gpd->rx_gpd.cnsm_buf_len : ptr_sw_gpd->rx_gpd.avbl_buf_len; + + total_len += len; + + /* unmap dma */ + phy_addr = CLX_ADDR_32_TO_64(ptr_sw_gpd->rx_gpd.data_buf_addr_hi, ptr_sw_gpd->rx_gpd.data_buf_addr_lo); + osal_skb_unmapDma(phy_addr, len, DMA_FROM_DEVICE); + /* next */ + ptr_sw_gpd = ptr_sw_gpd->ptr_next; + } + perf_rxCallback(total_len); + _hal_dawn_pkt_freeRxGpdList(unit, ptr_sw_first_gpd, TRUE); + return ; + } +#endif + + _hal_dawn_pkt_getPacketDest(&ptr_sw_gpd->rx_gpd, &dest_type, &ptr_dest); + +#if defined(NETIF_EN_NETLINK) + if ((HAL_DAWN_PKT_DEST_NETDEV == dest_type) || + (HAL_DAWN_PKT_DEST_NETLINK == dest_type)) +#else + if (HAL_DAWN_PKT_DEST_NETDEV == dest_type) +#endif + { + /* need to encap the packet as skb */ + ptr_sw_gpd = ptr_sw_first_gpd; + while (NULL != ptr_sw_gpd) + { + len = (HAL_DAWN_PKT_CH_LAST_GPD == ptr_sw_gpd->rx_gpd.ch)? + ptr_sw_gpd->rx_gpd.cnsm_buf_len : ptr_sw_gpd->rx_gpd.avbl_buf_len; + + total_len += len; + + /* unmap dma */ + phy_addr = CLX_ADDR_32_TO_64(ptr_sw_gpd->rx_gpd.data_buf_addr_hi, ptr_sw_gpd->rx_gpd.data_buf_addr_lo); + ptr_virt_addr = ptr_sw_gpd->ptr_cookie; + + ptr_skb = (struct sk_buff *)ptr_virt_addr; + + /* note here ptr_skb->len is the total buffer size not means the actual Rx packet len + * it should be updated later + */ + osal_skb_unmapDma(phy_addr, ptr_skb->len, DMA_FROM_DEVICE); + + /* reset ptr_skb->len with real packet len instead of total buffer size */ + if (NULL == ptr_sw_gpd->ptr_next) + { + /* strip CRC padded by asic for the last gpd segment */ + ptr_skb->len = len - ETH_FCS_LEN; + } + else + { + ptr_skb->len = len; + } + + skb_set_tail_pointer(ptr_skb, ptr_skb->len); + + /* next */ + ptr_sw_gpd = ptr_sw_gpd->ptr_next; + } + + port = ptr_sw_first_gpd->rx_gpd.itmh_eth.igr_phy_port; + ptr_net_dev = HAL_DAWN_PKT_GET_PORT_NETDEV(port); + + /* if the packet is composed of multiple gpd (skb), need to merge it into a single skb */ + if (NULL != ptr_sw_first_gpd->ptr_next) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_RX, + "u=%u, rxch=%u, rcv pkt size=%u > gpd buf size=%u\n", + unit, channel, total_len, ptr_rx_cb->buf_len); + ptr_merge_skb = osal_skb_alloc(total_len - ETH_FCS_LEN); + if (NULL != ptr_merge_skb) + { + copy_offset = 0; + ptr_sw_gpd = ptr_sw_first_gpd; + while (NULL != ptr_sw_gpd) + { + ptr_skb = (struct sk_buff *)ptr_sw_gpd->ptr_cookie; + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_RX, + "u=%u, rxch=%u, copy size=%u to buf offset=%u\n", + unit, channel, ptr_skb->len, copy_offset); + + memcpy(&(((UI8_T *)ptr_merge_skb->data)[copy_offset]), + ptr_skb->data, ptr_skb->len); + copy_offset += ptr_skb->len; + ptr_sw_gpd = ptr_sw_gpd->ptr_next; + } + /* put the merged skb to ptr_skb for the following process */ + ptr_skb = ptr_merge_skb; + } + else + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_RX), + "u=%u, rxch=%u, alloc skb failed, size=%u\n", + unit, channel, (total_len - ETH_FCS_LEN)); + } + + /* free both sw_gpd and the skb attached on it */ + _hal_dawn_pkt_freeRxGpdList(unit, ptr_sw_first_gpd, TRUE); + } + else + { + /* free only sw_gpd */ + _hal_dawn_pkt_freeRxGpdList(unit, ptr_sw_first_gpd, FALSE); + } + + /* if NULL netdev, drop the skb */ + if (NULL == ptr_net_dev) + { + ptr_rx_cb->cnt.channel[channel].netdev_miss++; + osal_skb_free(ptr_skb); + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_RX), + "u=%u, rxch=%u, find netdev failed\n", + unit, channel); + return; + } + + /* skb handling */ + ptr_skb->dev = ptr_net_dev; + ptr_skb->pkt_type = PACKET_HOST; /* this packet is for me */ + ptr_skb->ip_summed = CHECKSUM_UNNECESSARY; /* skip checksum */ + + /* send to linux */ + if (dest_type == HAL_DAWN_PKT_DEST_NETDEV) + { + /* skip ethernet header only for Linux net interface*/ + ptr_skb->protocol = eth_type_trans(ptr_skb, ptr_net_dev); + osal_skb_recv(ptr_skb); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) + ptr_net_dev->last_rx = jiffies; +#endif + ptr_priv = netdev_priv(ptr_net_dev); + ptr_priv->stats.rx_packets++; + ptr_priv->stats.rx_bytes += total_len; + } +#if defined(NETIF_EN_NETLINK) + else + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_PROFILE, + "hit profile dest=netlink, name=%s, mcgrp=%s\n", + ((NETIF_NL_RX_DST_NETLINK_T *)ptr_dest)->name, + ((NETIF_NL_RX_DST_NETLINK_T *)ptr_dest)->mc_group_name); + netif_nl_rxSkb(unit, ptr_skb, ptr_dest); + } +#endif + } + else if (HAL_DAWN_PKT_DEST_SDK == dest_type) + { + while (0 != _hal_dawn_pkt_enQueue(&ptr_rx_cb->sw_queue[channel], ptr_sw_gpd)) + { + ptr_rx_cb->cnt.channel[channel].enque_retry++; + HAL_DAWN_PKT_RX_ENQUE_RETRY_SLEEP(); + } + ptr_rx_cb->cnt.channel[channel].enque_ok++; + + osal_triggerEvent(&ptr_rx_cb->sync_sema); + ptr_rx_cb->cnt.channel[channel].trig_event++; + } + else if (HAL_DAWN_PKT_DEST_DROP == dest_type) + { + _hal_dawn_pkt_freeRxGpdList(unit, ptr_sw_first_gpd, TRUE); + } + else + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_RX), + "u=%u, rxch=%u, invalid pkt dest=%d\n", + unit, channel, dest_type); + } +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_flushRxQueue( + const UI32_T unit, + HAL_DAWN_PKT_SW_QUEUE_T *ptr_que) +{ + HAL_DAWN_PKT_RX_SW_GPD_T *ptr_sw_gpd_knl = NULL; + CLX_ERROR_NO_T rc; + + while (1) + { + rc = _hal_dawn_pkt_deQueue(ptr_que, (void **)&ptr_sw_gpd_knl); + if (CLX_E_OK == rc) + { + _hal_dawn_pkt_freeRxGpdList(unit, ptr_sw_gpd_knl, TRUE); + } + else + { + break; + } + } + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_schedRxDeQueue + * PURPOSE: + * To dequeue the packets based on the configured algorithm. + * INPUT: + * unit -- The unit ID + * ptr_cookie -- Pointer of the RX cookie + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully dequeue the packets. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_schedRxDeQueue( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_RX_COOKIE_T *ptr_cookie) +{ + HAL_DAWN_PKT_IOCTL_RX_COOKIE_T ioctl_data; + HAL_DAWN_PKT_IOCTL_RX_GPD_T ioctl_gpd; + HAL_DAWN_PKT_RX_CB_T *ptr_rx_cb = HAL_DAWN_PKT_GET_RX_CB_PTR(unit); + HAL_DAWN_PKT_RX_SW_GPD_T *ptr_sw_gpd_knl = NULL; + HAL_DAWN_PKT_RX_SW_GPD_T *ptr_sw_first_gpd_knl = NULL; + UI32_T que_cnt = 0; + UI32_T queue = 0; + UI32_T idx = 0; + UI32_T gpd_idx = 0; + /* copy Rx sw_gpd */ + volatile HAL_DAWN_PKT_RX_GPD_T *ptr_rx_gpd = NULL; + void *ptr_virt_addr = NULL; + CLX_ADDR_T phy_addr = 0; + UI32_T buf_len = 0; + CLX_ERROR_NO_T rc = CLX_E_OK; + + /* normal process */ + if (TRUE == ptr_rx_cb->running) + { + /* get queue and count */ + for (idx = 0; idx < HAL_DAWN_PKT_RX_QUEUE_NUM; idx++) + { + /* to gurantee the opportunity where each queue can be handler */ + queue = ((ptr_rx_cb->deque_idx + idx) % HAL_DAWN_PKT_RX_QUEUE_NUM); + _hal_dawn_pkt_getQueueCount(&ptr_rx_cb->sw_queue[queue], &que_cnt); + if (que_cnt > 0) + { + ptr_rx_cb->deque_idx = ((queue + 1) % HAL_DAWN_PKT_RX_QUEUE_NUM); + break; + } + } + + /* If all of the queues are empty, wait rxTask event */ + if (0 == que_cnt) + { + osal_waitEvent(&ptr_rx_cb->sync_sema); + + ptr_rx_cb->cnt.wait_event++; + + /* re-get queue and count */ + for (queue = 0; queue < HAL_DAWN_PKT_RX_QUEUE_NUM; queue++) + { + _hal_dawn_pkt_getQueueCount(&ptr_rx_cb->sw_queue[queue], &que_cnt); + if (que_cnt > 0) + { + ptr_rx_cb->deque_idx = ((queue + 1) % HAL_DAWN_PKT_RX_QUEUE_NUM); + break; + } + } + } + + /* deque */ + if ((que_cnt > 0) && (queue < HAL_DAWN_PKT_RX_QUEUE_NUM)) + { + rc = _hal_dawn_pkt_deQueue(&ptr_rx_cb->sw_queue[queue], (void **)&ptr_sw_gpd_knl); + if (CLX_E_OK == rc) + { + ptr_rx_cb->cnt.channel[queue].deque_ok++; + ptr_sw_first_gpd_knl = ptr_sw_gpd_knl; + + osal_io_copyFromUser(&ioctl_data, ptr_cookie, sizeof(HAL_DAWN_PKT_IOCTL_RX_COOKIE_T)); + + while (NULL != ptr_sw_gpd_knl) + { + /* get the IOCTL GPD from user */ + osal_io_copyFromUser(&ioctl_gpd, + ((void *)((CLX_HUGE_T)ioctl_data.ioctl_gpd_addr)) + + gpd_idx*sizeof(HAL_DAWN_PKT_IOCTL_RX_GPD_T), + sizeof(HAL_DAWN_PKT_IOCTL_RX_GPD_T)); + + /* get knl buf addr */ + ptr_rx_gpd = &ptr_sw_gpd_knl->rx_gpd; + phy_addr = CLX_ADDR_32_TO_64(ptr_rx_gpd->data_buf_addr_hi, ptr_rx_gpd->data_buf_addr_lo); + + ptr_virt_addr = ptr_sw_gpd_knl->ptr_cookie; + osal_skb_unmapDma(phy_addr, ((struct sk_buff *)ptr_virt_addr)->len, DMA_FROM_DEVICE); + + buf_len = (HAL_DAWN_PKT_CH_LAST_GPD == ptr_rx_gpd->ch)? + ptr_rx_gpd->cnsm_buf_len : ptr_rx_gpd->avbl_buf_len; + + /* overwrite whole rx_gpd to user + * the user should re-assign the correct value to data_buf_addr_hi, data_buf_addr_low + * after this IOCTL returns + */ + osal_io_copyToUser((void *)((CLX_HUGE_T)ioctl_gpd.hw_gpd_addr), + &ptr_sw_gpd_knl->rx_gpd, + sizeof(HAL_DAWN_PKT_RX_GPD_T)); + /* copy buf */ + /* DMA buf address allocated by the user is store in ptr_ioctl_data->gpd[idx].cookie */ + osal_io_copyToUser((void *)((CLX_HUGE_T)ioctl_gpd.dma_buf_addr), + ((struct sk_buff *)ptr_virt_addr)->data, buf_len); + ptr_sw_gpd_knl->ptr_cookie = ptr_virt_addr; + + /* next */ + ptr_sw_gpd_knl = ptr_sw_gpd_knl->ptr_next; + gpd_idx++; + } + + /* Must free kernel sw_gpd */ + _hal_dawn_pkt_freeRxGpdList(unit, ptr_sw_first_gpd_knl, TRUE); + } + else + { + ptr_rx_cb->cnt.channel[queue].deque_fail++; + } + } + else + { + /* it means that all queue's are flush -> rx stop flow */ + rc = CLX_E_OTHERS; + } + } + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_waitTxDone + * PURPOSE: + * To determine the next action after transfer the packet to HW. + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * ptr_sw_gpd -- Pointer for the SW Tx GPD link list + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully perform the target action. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_waitTxDone( + const UI32_T unit, + const HAL_DAWN_PKT_TX_CHANNEL_T channel, + HAL_DAWN_PKT_TX_SW_GPD_T *ptr_sw_gpd) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_DAWN_PKT_TX_CB_T *ptr_tx_cb = HAL_DAWN_PKT_GET_TX_CB_PTR(unit); + HAL_DAWN_PKT_TX_PDMA_T *ptr_tx_pdma = HAL_DAWN_PKT_GET_TX_PDMA_PTR(unit, channel); + volatile HAL_DAWN_PKT_TX_GPD_T *ptr_tx_gpd = NULL; + UI32_T last_gpd_idx = 0; + UI32_T loop_cnt = 0; + + if (HAL_DAWN_PKT_TX_WAIT_ASYNC == ptr_tx_cb->wait_mode) + { + ; + } + else if (HAL_DAWN_PKT_TX_WAIT_SYNC_INTR == ptr_tx_cb->wait_mode) + { + osal_takeSemaphore(&ptr_tx_pdma->sync_intr_sema, HAL_DAWN_PKT_PDMA_TX_INTR_TIMEOUT); + /* rc = _hal_dawn_pkt_invokeTxGpdCallback(unit, ptr_sw_gpd); */ + } + else if (HAL_DAWN_PKT_TX_WAIT_SYNC_POLL == ptr_tx_cb->wait_mode) + { + last_gpd_idx = ptr_tx_pdma->free_idx + ptr_tx_pdma->used_gpd_num; + last_gpd_idx %= ptr_tx_pdma->gpd_num; + ptr_tx_gpd = HAL_DAWN_PKT_GET_TX_GPD_PTR(unit, channel, last_gpd_idx); + + while (HAL_DAWN_PKT_HWO_HW_OWN == ptr_tx_gpd->hwo) + { + osal_dma_invalidateCache((void *)ptr_tx_gpd, sizeof(HAL_DAWN_PKT_TX_GPD_T)); + loop_cnt++; + if (0 == loop_cnt % HAL_DAWN_PKT_PDMA_TX_POLL_MAX_LOOP) + { + ptr_tx_cb->cnt.channel[channel].poll_timeout++; + rc = CLX_E_OTHERS; + break; + } + } + if (HAL_DAWN_PKT_ECC_ERROR_OCCUR == ptr_tx_gpd->ecce) + { + ptr_tx_cb->cnt.channel[channel].ecc_err++; + } + if (CLX_E_OK == rc) + { + ptr_tx_pdma->free_gpd_num += ptr_tx_pdma->used_gpd_num; + ptr_tx_pdma->used_gpd_num = 0; + ptr_tx_pdma->free_idx = ptr_tx_pdma->used_idx; + /* rc = _hal_dawn_pkt_invokeTxGpdCallback(unit, ptr_sw_gpd); */ + } + } + + return (rc); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_resumeAllIntf( + const UI32_T unit) +{ + struct net_device *ptr_net_dev = NULL; + UI32_T port; + + /* Unregister net devices by id */ + for (port = 0; port < HAL_DAWN_PKT_MAX_PORT_NUM; port++) + { + ptr_net_dev = HAL_DAWN_PKT_GET_PORT_NETDEV(port); + if (NULL != ptr_net_dev) + { + if (netif_queue_stopped(ptr_net_dev)) + { + netif_wake_queue(ptr_net_dev); + } + } + } + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_suspendAllIntf( + const UI32_T unit) +{ + struct net_device *ptr_net_dev = NULL; + UI32_T port; + + /* Unregister net devices by id */ + for (port = 0; port < HAL_DAWN_PKT_MAX_PORT_NUM; port++) + { + ptr_net_dev = HAL_DAWN_PKT_GET_PORT_NETDEV(port); + if (NULL != ptr_net_dev) + { + netif_stop_queue(ptr_net_dev); + } + } + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_stopAllIntf( + const UI32_T unit) +{ + struct net_device *ptr_net_dev = NULL; + UI32_T port; + + /* Unregister net devices by id */ + for (port = 0; port < HAL_DAWN_PKT_MAX_PORT_NUM; port++) + { + ptr_net_dev = HAL_DAWN_PKT_GET_PORT_NETDEV(port); + if (NULL != ptr_net_dev) + { + netif_tx_disable(ptr_net_dev); + } + } + + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_dawn_pkt_sendGpd + * PURPOSE: + * To perform the packet transmission form CPU to the switch. + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * ptr_sw_gpd -- Pointer for the SW Tx GPD link list + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully perform the transferring. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_dawn_pkt_sendGpd( + const UI32_T unit, + const HAL_DAWN_PKT_TX_CHANNEL_T channel, + HAL_DAWN_PKT_TX_SW_GPD_T *ptr_sw_gpd) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_DAWN_PKT_TX_CB_T *ptr_tx_cb = HAL_DAWN_PKT_GET_TX_CB_PTR(unit); + HAL_DAWN_PKT_TX_PDMA_T *ptr_tx_pdma = HAL_DAWN_PKT_GET_TX_PDMA_PTR(unit, channel); + volatile HAL_DAWN_PKT_TX_GPD_T *ptr_tx_gpd = NULL; + HAL_DAWN_PKT_TX_SW_GPD_T *ptr_sw_first_gpd = ptr_sw_gpd; + UI32_T used_idx = 0; + UI32_T used_gpd_num = ptr_sw_gpd->gpd_num; + CLX_IRQ_FLAGS_T irq_flags; + HAL_DAWN_PKT_DRV_CB_T *ptr_cb = HAL_DAWN_PKT_GET_DRV_CB_PTR(unit); + + if (0 != (ptr_cb->init_flag & HAL_DAWN_PKT_INIT_TASK)) + { + osal_takeIsrLock(&ptr_tx_pdma->ring_lock, &irq_flags); + + /* If not PDMA error */ + if (FALSE == ptr_tx_pdma->err_flag) + { + /* Make Sure GPD is enough */ + if (ptr_tx_pdma->free_gpd_num >= used_gpd_num) + { + used_idx = ptr_tx_pdma->used_idx; + while (NULL != ptr_sw_gpd) + { + ptr_tx_gpd = HAL_DAWN_PKT_GET_TX_GPD_PTR(unit, channel, used_idx); + osal_dma_invalidateCache((void *)ptr_tx_gpd, sizeof(HAL_DAWN_PKT_TX_GPD_T)); + + if (HAL_DAWN_PKT_HWO_HW_OWN == ptr_tx_gpd->hwo) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_TX), + "u=%u, txch=%u, free gpd idx out-of-sync\n", + unit, channel); + rc = CLX_E_TABLE_FULL; + break; + } + + /* Fill in HW-GPD Ring */ + osal_memcpy((void *)ptr_tx_gpd, &ptr_sw_gpd->tx_gpd, sizeof(HAL_DAWN_PKT_TX_GPD_T)); + osal_dma_flushCache((void *)ptr_tx_gpd, sizeof(HAL_DAWN_PKT_TX_GPD_T)); + + /* next */ + used_idx++; + used_idx %= ptr_tx_pdma->gpd_num; + ptr_sw_gpd = ptr_sw_gpd->ptr_next; + } + + if (HAL_DAWN_PKT_TX_WAIT_ASYNC == ptr_tx_cb->wait_mode) + { + /* Fill 1st GPD in SW-GPD Ring */ + ptr_tx_pdma->pptr_sw_gpd_ring[ptr_tx_pdma->used_idx] = ptr_sw_first_gpd; + } + + /* update Tx PDMA */ + ptr_tx_pdma->used_idx = used_idx; + ptr_tx_pdma->used_gpd_num += used_gpd_num; + ptr_tx_pdma->free_gpd_num -= used_gpd_num; + + _hal_dawn_pkt_resumeTxChannelReg(unit, channel, used_gpd_num); + ptr_tx_cb->cnt.channel[channel].send_ok++; + + _hal_dawn_pkt_waitTxDone(unit, channel, ptr_sw_first_gpd); + + /* reserve 1 packet buffer for each port in case that the suspension is too late */ +#define HAL_DAWN_PKT_KNL_TX_RING_AVBL_GPD_LOW (HAL_DAWN_PORT_NUM) + if (ptr_tx_pdma->free_gpd_num < HAL_DAWN_PKT_KNL_TX_RING_AVBL_GPD_LOW) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_TX, + "u=%u, txch=%u, tx avbl gpd < %d, suspend all netdev\n", + unit, channel, HAL_DAWN_PKT_KNL_TX_RING_AVBL_GPD_LOW); + _hal_dawn_pkt_suspendAllIntf(unit); + } + } + else + { + rc = CLX_E_TABLE_FULL; + } + } + else + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_TX), + "u=%u, txch=%u, pdma hw err\n", + unit, channel); + rc = CLX_E_OTHERS; + } + + osal_giveIsrLock(&ptr_tx_pdma->ring_lock, &irq_flags); + } + else + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_ERR, + "Tx failed, task already deinit\n"); + rc = CLX_E_OTHERS; + } + + return (rc); +} + +/* ----------------------------------------------------------------------------------- pkt_srv */ +/* ----------------------------------------------------------------------------------- Rx Init */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_rxStop( + const UI32_T unit) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_DAWN_PKT_RX_CHANNEL_T channel = 0; + UI32_T idx; + HAL_DAWN_PKT_RX_CB_T *ptr_rx_cb = HAL_DAWN_PKT_GET_RX_CB_PTR(unit); + HAL_DAWN_PKT_DRV_CB_T *ptr_cb = HAL_DAWN_PKT_GET_DRV_CB_PTR(unit); + HAL_DAWN_PKT_RX_PDMA_T *ptr_rx_pdma = NULL; + + /* Check if Rx is already stopped*/ + if (0 == (ptr_cb->init_flag & HAL_DAWN_PKT_INIT_RX_START)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_RX | HAL_DAWN_PKT_DBG_ERR), + "u=%u, rx stop failed, not started\n", unit); + return (CLX_E_OK); + } + + /* Check if PKT Drv/Task were de-init before stopping Rx */ + /* Currently, we help to stop Rx when deinit Drv/Task, so it shouldn't enter below logic */ + if ((0 == (ptr_cb->init_flag & HAL_DAWN_PKT_INIT_TASK)) || + (0 == (ptr_cb->init_flag & HAL_DAWN_PKT_INIT_DRV))) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_RX | HAL_DAWN_PKT_DBG_ERR), + "u=%u, rx stop failed, pkt task & pkt drv not init\n", unit); + return (CLX_E_OK); + } + + /* Deinit Rx PDMA and free buf for Rx GPD */ + for (channel = 0; channel < HAL_DAWN_PKT_RX_CHANNEL_LAST; channel++) + { + ptr_rx_pdma = HAL_DAWN_PKT_GET_RX_PDMA_PTR(unit, channel); + + osal_takeSemaphore(&ptr_rx_pdma->sema, CLX_SEMAPHORE_WAIT_FOREVER); + _hal_dawn_pkt_stopRxChannelReg(unit, channel); + rc = _hal_dawn_pkt_deinitRxPdmaRingBuf(unit, channel); + osal_giveSemaphore(&ptr_rx_pdma->sema); + } + + /* flush packets in all queues since Rx task may be blocked in user space + * in this case it won't do ioctl to kernel to handle remaining packets + */ + for (idx = 0; idx < HAL_DAWN_PKT_RX_QUEUE_NUM; idx++) + { + _hal_dawn_pkt_flushRxQueue(unit, &ptr_rx_cb->sw_queue[idx]); + } + + /* Return user thread */ + ptr_rx_cb->running = FALSE; + ptr_cb->init_flag &= (~HAL_DAWN_PKT_INIT_RX_START); + + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_RX, + "u=%u, rx stop done, init flag=0x%x\n", unit, ptr_cb->init_flag); + + osal_triggerEvent(&ptr_rx_cb->sync_sema); + + return (rc); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_rxStart( + const UI32_T unit) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_DAWN_PKT_RX_CHANNEL_T channel = 0; + HAL_DAWN_PKT_RX_CB_T *ptr_rx_cb = HAL_DAWN_PKT_GET_RX_CB_PTR(unit); + HAL_DAWN_PKT_DRV_CB_T *ptr_cb = HAL_DAWN_PKT_GET_DRV_CB_PTR(unit); + HAL_DAWN_PKT_RX_PDMA_T *ptr_rx_pdma = NULL; + + if (0 != (ptr_cb->init_flag & HAL_DAWN_PKT_INIT_RX_START)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_RX | HAL_DAWN_PKT_DBG_ERR), + "u=%u, rx start failed, already started\n", unit); + return (CLX_E_OK); + } + + /* init Rx PDMA and alloc buf for Rx GPD */ + for (channel = 0; channel < HAL_DAWN_PKT_RX_CHANNEL_LAST; channel++) + { + ptr_rx_pdma = HAL_DAWN_PKT_GET_RX_PDMA_PTR(unit, channel); + + osal_takeSemaphore(&ptr_rx_pdma->sema, CLX_SEMAPHORE_WAIT_FOREVER); + rc = _hal_dawn_pkt_initRxPdmaRingBuf(unit, channel); + if (CLX_E_OK == rc) + { + ptr_rx_pdma->cur_idx = 0; + _hal_dawn_pkt_startRxChannelReg(unit, channel, ptr_rx_pdma->gpd_num); + } + osal_giveSemaphore(&ptr_rx_pdma->sema); + } + + /* enable to dequeue rx packets */ + ptr_rx_cb->running = TRUE; + + /* set the flag to record init state */ + ptr_cb->init_flag |= HAL_DAWN_PKT_INIT_RX_START; + + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_RX, + "u=%u, rx start done, init flag=0x%x\n", unit, ptr_cb->init_flag); + return (rc); +} + +/* FUNCTION NAME: hal_dawn_pkt_setRxKnlConfig + * PURPOSE: + * 1. To stop the Rx channel and deinit the Rx subsystem. + * 2. To init the Rx subsystem and start the Rx channel. + * 3. To restart the Rx subsystem + * INPUT: + * unit -- The unit ID + * ptr_cookie -- Pointer of the RX cookie + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure the RX parameters. + * CLX_E_OTHERS -- Configure the parameter failed. + * NOTES: + * + */ +CLX_ERROR_NO_T +hal_dawn_pkt_setRxKnlConfig( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_RX_COOKIE_T *ptr_cookie) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_DAWN_PKT_RX_CB_T *ptr_rx_cb = HAL_DAWN_PKT_GET_RX_CB_PTR(unit); + HAL_DAWN_PKT_DRV_CB_T *ptr_cb = HAL_DAWN_PKT_GET_DRV_CB_PTR(unit); + HAL_DAWN_PKT_IOCTL_RX_TYPE_T rx_type = HAL_DAWN_PKT_IOCTL_RX_TYPE_LAST; + + osal_io_copyFromUser(&rx_type, &ptr_cookie->rx_type, sizeof(HAL_DAWN_PKT_IOCTL_RX_TYPE_T)); + + if (HAL_DAWN_PKT_IOCTL_RX_TYPE_DEINIT == rx_type) + { + _hal_dawn_pkt_rxStop(unit); + } + if (HAL_DAWN_PKT_IOCTL_RX_TYPE_INIT == rx_type) + { + /* To prevent buffer size from being on-the-fly changed */ + if (0 != (ptr_cb->init_flag & HAL_DAWN_PKT_INIT_RX_START)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_RX | HAL_DAWN_PKT_DBG_ERR), + "u=%u, rx stop failed, not started\n", unit); + return (CLX_E_OK); + } + + osal_io_copyFromUser(&ptr_rx_cb->buf_len, &ptr_cookie->buf_len, sizeof(UI32_T)); + _hal_dawn_pkt_rxStart(unit); + } + + return (rc); +} + +/* FUNCTION NAME: hal_dawn_pkt_getRxKnlConfig + * PURPOSE: + * To get the Rx subsystem configuration. + * INPUT: + * unit -- The unit ID + * ptr_cookie -- Pointer of the RX cookie + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure the RX parameters. + * CLX_E_OTHERS -- Configure the parameter failed. + * NOTES: + * + */ +CLX_ERROR_NO_T +hal_dawn_pkt_getRxKnlConfig( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_RX_COOKIE_T *ptr_cookie) +{ + HAL_DAWN_PKT_RX_CB_T *ptr_rx_cb = HAL_DAWN_PKT_GET_RX_CB_PTR(unit); + + osal_io_copyToUser(&ptr_cookie->buf_len, &ptr_rx_cb->buf_len, sizeof(UI32_T)); + + return (CLX_E_OK); +} + +/* ----------------------------------------------------------------------------------- Deinit */ +/* FUNCTION NAME: hal_dawn_pkt_deinitTask + * PURPOSE: + * To de-initialize the Task for packet module. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully dinitialize the control block. + * CLX_E_OTHERS -- Initialize the control block failed. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_dawn_pkt_deinitTask( + const UI32_T unit) +{ + HAL_DAWN_PKT_DRV_CB_T *ptr_cb = HAL_DAWN_PKT_GET_DRV_CB_PTR(unit); + HAL_DAWN_PKT_TX_CB_T *ptr_tx_cb = HAL_DAWN_PKT_GET_TX_CB_PTR(unit); + HAL_DAWN_PKT_RX_CB_T *ptr_rx_cb = HAL_DAWN_PKT_GET_RX_CB_PTR(unit); + UI32_T channel = 0; + + /* to prevent net intf from Tx packet */ + ptr_tx_cb->net_tx_allowed = FALSE; + + /* In case that some undestroyed net intf keep Tx after task deinit */ + _hal_dawn_pkt_stopAllIntf(unit); + + if (0 == (ptr_cb->init_flag & HAL_DAWN_PKT_INIT_TASK)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_RX | HAL_DAWN_PKT_DBG_ERR), + "u=%u, rx stop failed, not started\n", unit); + return (CLX_E_OK); + } + + /* Need to stop Rx before de-init Task */ + if (0 != (ptr_cb->init_flag & HAL_DAWN_PKT_INIT_RX_START)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_RX | HAL_DAWN_PKT_DBG_ERR), + "u=%u, pkt task deinit failed, rx not stop\n", unit); + + _hal_dawn_pkt_rxStop(unit); + } + + /* Make the Rx IOCTL from userspace return back*/ + osal_triggerEvent(&ptr_rx_cb->sync_sema); + + /* Destroy txTask */ + if (HAL_DAWN_PKT_TX_WAIT_ASYNC == ptr_tx_cb->wait_mode) + { + ptr_tx_cb->running = FALSE; + osal_triggerEvent(&ptr_tx_cb->sync_sema); + } + + /* Destroy handleRxDoneTask */ + for (channel = 0; channel < HAL_DAWN_PKT_RX_CHANNEL_LAST; channel++) + { + osal_stopThread(&ptr_rx_cb->isr_task_id[channel]); + osal_triggerEvent(HAL_DAWN_PKT_RCH_EVENT(unit, channel)); + osal_destroyThread(&ptr_rx_cb->isr_task_id[channel]); + } + + /* Destroy handleTxDoneTask */ + for (channel = 0; channel < HAL_DAWN_PKT_TX_CHANNEL_LAST; channel++) + { + osal_stopThread(&ptr_tx_cb->isr_task_id[channel]); + osal_triggerEvent(HAL_DAWN_PKT_TCH_EVENT(unit, channel)); + osal_destroyThread(&ptr_tx_cb->isr_task_id[channel]); + } + + /* Destroy handleErrorTask */ + osal_stopThread(&ptr_cb->err_task_id); + osal_triggerEvent(HAL_DAWN_PKT_ERR_EVENT(unit)); + osal_destroyThread(&ptr_cb->err_task_id); + + /* Set the flag to record init state */ + ptr_cb->init_flag &= (~HAL_DAWN_PKT_INIT_TASK); + + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_RX, + "u=%u, pkt task deinit done, init flag=0x%x\n", + unit, ptr_cb->init_flag); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_deinitTxPdma + * PURPOSE: + * To de-initialize the Tx PDMA configuration of the specified channel. + * INPUT: + * unit -- The unit ID + * channel -- The target Tx channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully de-init the Tx PDMA. + * CLX_E_OTHERS -- De-init the Tx PDMA failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_deinitTxPdma( + const UI32_T unit, + const HAL_DAWN_PKT_TX_CHANNEL_T channel) +{ + HAL_DAWN_PKT_TX_CB_T *ptr_tx_cb = HAL_DAWN_PKT_GET_TX_CB_PTR(unit); + HAL_DAWN_PKT_TX_PDMA_T *ptr_tx_pdma = HAL_DAWN_PKT_GET_TX_PDMA_PTR(unit, channel); + + _hal_dawn_pkt_stopTxChannelReg(unit, channel); + + /* Free DMA and flush queue */ + osal_dma_free(ptr_tx_pdma->ptr_gpd_start_addr); + + if (HAL_DAWN_PKT_TX_WAIT_ASYNC == ptr_tx_cb->wait_mode) + { + osal_free(ptr_tx_pdma->pptr_sw_gpd_ring); + osal_free(ptr_tx_pdma->pptr_sw_gpd_bulk); + } + else if (HAL_DAWN_PKT_TX_WAIT_SYNC_INTR == ptr_tx_cb->wait_mode) + { + osal_destroySemaphore(&ptr_tx_pdma->sync_intr_sema); + } + + osal_destroyIsrLock(&ptr_tx_pdma->ring_lock); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_deinitRxPdma + * PURPOSE: + * To de-initialize the Rx PDMA configuration of the specified channel. + * INPUT: + * unit -- The unit ID + * channel -- The target Rx channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully de-init the Rx PDMA. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_deinitRxPdma( + const UI32_T unit, + const HAL_DAWN_PKT_RX_CHANNEL_T channel) +{ + HAL_DAWN_PKT_RX_PDMA_T *ptr_rx_pdma = HAL_DAWN_PKT_GET_RX_PDMA_PTR(unit, channel); + + /* Free DMA */ + osal_takeSemaphore(&ptr_rx_pdma->sema, CLX_SEMAPHORE_WAIT_FOREVER); + osal_dma_free(ptr_rx_pdma->ptr_gpd_start_addr); + osal_giveSemaphore(&ptr_rx_pdma->sema); + osal_destroySemaphore(&ptr_rx_pdma->sema); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_deinitPktCb + * PURPOSE: + * To de-init the control block of Drv. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully de-init the control block. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_deinitPktCb( + const UI32_T unit) +{ + HAL_DAWN_PKT_DRV_CB_T *ptr_cb = HAL_DAWN_PKT_GET_DRV_CB_PTR(unit); + UI32_T idx = 0, vec = sizeof(_hal_dawn_pkt_intr_vec) / sizeof(HAL_DAWN_PKT_INTR_VEC_T); + + for (idx = 0; idx < vec; idx++) + { + osal_destroyEvent(&_hal_dawn_pkt_intr_vec[idx].intr_event); + ptr_cb->intr_bitmap &= ~(_hal_dawn_pkt_intr_vec[idx].intr_reg); + } + + /* Unregister PKT interrupt functions */ + osal_mdc_registerIsr(unit, NULL, NULL); + osal_destroyIsrLock(&ptr_cb->intr_lock); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_deinitPktTxCb + * PURPOSE: + * To de-init the control block of Tx PDMA. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully de-init the control block. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_deinitPktTxCb( + const UI32_T unit) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_DAWN_PKT_TX_CB_T *ptr_tx_cb = HAL_DAWN_PKT_GET_TX_CB_PTR(unit); + HAL_DAWN_PKT_TX_CHANNEL_T channel = 0; + + /* Deinitialize TX PDMA sub-system.*/ + for (channel = 0; channel < HAL_DAWN_PKT_TX_CHANNEL_LAST; channel++) + { + _hal_dawn_pkt_deinitTxPdma(unit, channel); + } + + if (HAL_DAWN_PKT_TX_WAIT_ASYNC == ptr_tx_cb->wait_mode) + { + /* Destroy the sync semaphore of txTask */ + osal_destroyEvent(&ptr_tx_cb->sync_sema); + + /* Deinitialize Tx GPD-queue (of first SW-GPD) from handleTxDoneTask to txTask */ + osal_destroySemaphore(&ptr_tx_cb->sw_queue.sema); + osal_que_destroy(&ptr_tx_cb->sw_queue.que_id); + } + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_deinitPktRxCb + * PURPOSE: + * To de-init the control block of Rx PDMA. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully de-init the control block. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_deinitPktRxCb( + const UI32_T unit) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_DAWN_PKT_RX_CB_T *ptr_rx_cb = HAL_DAWN_PKT_GET_RX_CB_PTR(unit); + HAL_DAWN_PKT_RX_CHANNEL_T channel = 0; + UI32_T queue = 0; + + /* Deinitialize RX PDMA sub-system */ + for (channel = 0; channel < HAL_DAWN_PKT_RX_CHANNEL_LAST; channel++) + { + _hal_dawn_pkt_deinitRxPdma(unit, channel); + } + + /* Destroy the sync semaphore of rxTask */ + osal_destroyEvent(&ptr_rx_cb->sync_sema); + + /* Deinitialize Rx GPD-queue (of first SW-GPD) from handleRxDoneTask to rxTask */ + for (queue = 0; queue < HAL_DAWN_PKT_RX_QUEUE_NUM; queue++) + { + osal_destroySemaphore(&ptr_rx_cb->sw_queue[queue].sema); + osal_que_destroy(&ptr_rx_cb->sw_queue[queue].que_id); + } + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_deinitL1Isr + * PURPOSE: + * To de-initialize the PDMA L1 ISR configuration. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully de-initialize for the L1 ISR. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_deinitL1Isr( + const UI32_T unit) +{ + UI32_T idx = 0, vec = sizeof(_hal_dawn_pkt_intr_vec) / sizeof(HAL_DAWN_PKT_INTR_VEC_T); + + for (idx = 0; idx < vec; idx++) + { + _hal_dawn_pkt_maskIntr(unit, _hal_dawn_pkt_intr_vec[idx].intr_reg); + _hal_dawn_pkt_disableIntr(unit, _hal_dawn_pkt_intr_vec[idx].intr_reg); + } + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_deinitL2Isr + * PURPOSE: + * To initialize the PDMA L2 ISR configuration. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure for the L2 ISR. + * CLX_E_OTHERS -- Configure failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_deinitL2Isr( + const UI32_T unit) +{ + HAL_DAWN_PKT_L2_ISR_T isr_status = 0x0; + + HAL_DAWN_PKT_SET_BITMAP(isr_status, HAL_DAWN_PKT_L2_ISR_RCH0); + HAL_DAWN_PKT_SET_BITMAP(isr_status, HAL_DAWN_PKT_L2_ISR_RCH1); + HAL_DAWN_PKT_SET_BITMAP(isr_status, HAL_DAWN_PKT_L2_ISR_RCH2); + HAL_DAWN_PKT_SET_BITMAP(isr_status, HAL_DAWN_PKT_L2_ISR_RCH3); + HAL_DAWN_PKT_SET_BITMAP(isr_status, HAL_DAWN_PKT_L2_ISR_TCH0); + HAL_DAWN_PKT_SET_BITMAP(isr_status, HAL_DAWN_PKT_L2_ISR_TCH1); + HAL_DAWN_PKT_SET_BITMAP(isr_status, HAL_DAWN_PKT_L2_ISR_TCH2); + HAL_DAWN_PKT_SET_BITMAP(isr_status, HAL_DAWN_PKT_L2_ISR_TCH3); + HAL_DAWN_PKT_SET_BITMAP(isr_status, HAL_DAWN_PKT_L2_ISR_RX_QID_MAP_ERR); + HAL_DAWN_PKT_SET_BITMAP(isr_status, HAL_DAWN_PKT_L2_ISR_RX_FRAME_ERR); + + osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_ERR_INT_MASK_SET), + &isr_status, sizeof(UI32_T)); + + isr_status = 0x0; + osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_ERR_INT_EN), + &isr_status, sizeof(UI32_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_dawn_pkt_deinitPktDrv + * PURPOSE: + * To invoke the functions to de-initialize the control block for each + * PDMA subsystem. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully de-initialize the control blocks. + * CLX_E_OTHERS -- De-initialize the control blocks failed. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_dawn_pkt_deinitPktDrv( + const UI32_T unit) +{ + HAL_DAWN_PKT_DRV_CB_T *ptr_cb = HAL_DAWN_PKT_GET_DRV_CB_PTR(unit); + CLX_ERROR_NO_T rc = CLX_E_OK; + + if (0 == (ptr_cb->init_flag & HAL_DAWN_PKT_INIT_DRV)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_RX | HAL_DAWN_PKT_DBG_ERR), + "u=%u, pkt drv deinit failed, not inited\n", unit); + return (CLX_E_OK); + } + + rc = _hal_dawn_pkt_deinitL2Isr(unit); + + if (CLX_E_OK == rc) + { + rc = _hal_dawn_pkt_deinitL1Isr(unit); + } + if (CLX_E_OK == rc) + { + rc = _hal_dawn_pkt_deinitPktRxCb(unit); + } + if (CLX_E_OK == rc) + { + rc = _hal_dawn_pkt_deinitPktTxCb(unit); + } + if (CLX_E_OK == rc) + { + rc = _hal_dawn_pkt_deinitPktCb(unit); + } + + ptr_cb->init_flag &= (~HAL_DAWN_PKT_INIT_DRV); + + + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_COMMON, + "u=%u, pkt drv deinit done, init flag=0x%x\n", + unit, ptr_cb->init_flag); + return (rc); +} + +/* ----------------------------------------------------------------------------------- Init */ +/* FUNCTION NAME: _hal_dawn_pkt_handleTxErrStat + * PURPOSE: + * To handle the TX flow control ISR. + * INPUT: + * unit -- The unit ID + * channel -- The target channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully handle the interrpt. + * NOTES: + * + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_handleTxErrStat( + const UI32_T unit, + const HAL_DAWN_PKT_TX_CHANNEL_T channel) +{ + HAL_DAWN_PKT_TX_CB_T *ptr_tx_cb = HAL_DAWN_PKT_GET_TX_CB_PTR(unit); + HAL_DAWN_PKT_TX_PDMA_T *ptr_tx_pdma = HAL_DAWN_PKT_GET_TX_PDMA_PTR(unit, channel); + CLX_IRQ_FLAGS_T irg_flags; + + if (HAL_DAWN_PKT_TX_WAIT_SYNC_INTR == ptr_tx_cb->wait_mode) + { + /* Notify the TX process to make it release the channel semaphore. */ + osal_giveSemaphore(&ptr_tx_pdma->sync_intr_sema); + } + + /* Set the error flag. */ + + osal_takeIsrLock(&ptr_tx_pdma->ring_lock, &irg_flags); + ptr_tx_pdma->err_flag = TRUE; + osal_giveIsrLock(&ptr_tx_pdma->ring_lock, &irg_flags); + + osal_triggerEvent(HAL_DAWN_PKT_TCH_EVENT(unit, channel)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_handleRxErrStat + * PURPOSE: + * To handle the error which occurs in RX channels. + * INPUT: + * unit -- The unit ID + * channel -- The channel where the error occurs + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully handle the error situation. + * NOTES: + * + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_handleRxErrStat( + const UI32_T unit, + const HAL_DAWN_PKT_RX_CHANNEL_T channel) +{ + HAL_DAWN_PKT_RX_PDMA_T *ptr_rx_pdma = HAL_DAWN_PKT_GET_RX_PDMA_PTR(unit, channel); + + /* Set the error flag. */ + osal_takeSemaphore(&ptr_rx_pdma->sema, CLX_SEMAPHORE_WAIT_FOREVER); + ptr_rx_pdma->err_flag = TRUE; + osal_giveSemaphore(&ptr_rx_pdma->sema); + + osal_triggerEvent(HAL_DAWN_PKT_RCH_EVENT(unit, channel)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_handleTxL2Isr + * PURPOSE: + * To handle the TX L2 interrupt according to the ISR status. + * INPUT: + * unit -- The unit ID + * channel -- The channel where the interrupt occurs + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully handle the L2 interrupt. + * NOTES: + * + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_handleTxL2Isr( + const UI32_T unit, + const HAL_DAWN_PKT_TX_CHANNEL_T channel) +{ + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_T isr_status = 0x0; + HAL_DAWN_PKT_TX_CB_T *ptr_tx_cb = HAL_DAWN_PKT_GET_TX_CB_PTR(unit); + + osal_mdc_readPciReg(unit, + HAL_DAWN_PKT_GET_PDMA_TCH_REG(HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_TCH_INT_STAT), channel), + &isr_status, sizeof(isr_status)); + + _hal_dawn_pkt_maskAllTxL2IsrReg(unit, channel); + + if (0 != (isr_status & HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_HWO_ERROR)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_TX), + "u=%u, txch=%u, pdma gpd hwo err\n", unit, channel); + _hal_dawn_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_HWO_ERROR); + ptr_tx_cb->cnt.channel[channel].gpd_hwo_err++; + _hal_dawn_pkt_handleTxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_CHKSM_ERROR)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_TX), + "u=%u, txch=%u, pdma gpd chksum err\n", unit, channel); + _hal_dawn_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_CHKSM_ERROR); + ptr_tx_cb->cnt.channel[channel].gpd_chksm_err++; + _hal_dawn_pkt_handleTxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_NO_OVFL_ERROR)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_TX), + "u=%u, txch=%u, pdma gpd num overflow err\n", unit, channel); + _hal_dawn_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_NO_OVFL_ERROR); + ptr_tx_cb->cnt.channel[channel].gpd_no_ovfl_err++; + _hal_dawn_pkt_handleTxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_DMA_READ_ERROR)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_TX), + "u=%u, txch=%u, pdma gpd dma read err\n", unit, channel); + _hal_dawn_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_DMA_READ_ERROR); + ptr_tx_cb->cnt.channel[channel].gpd_dma_read_err++; + _hal_dawn_pkt_handleTxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_BUF_SIZE_ERROR)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_TX), + "u=%u, txch=%u, pdma buf size err\n", unit, channel); + _hal_dawn_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_BUF_SIZE_ERROR); + ptr_tx_cb->cnt.channel[channel].buf_size_err++; + _hal_dawn_pkt_handleTxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_RUNT_ERROR)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_TX, + "u=%u, txch=%u, pdma pkt runt\n", unit, channel); + _hal_dawn_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_RUNT_ERROR); + ptr_tx_cb->cnt.channel[channel].runt_err++; + } + if (0 != (isr_status & HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_OVSZ_ERROR)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_TX, + "u=%u, txch=%u, pdma pkt over size\n", unit, channel);; + _hal_dawn_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_OVSZ_ERROR); + ptr_tx_cb->cnt.channel[channel].ovsz_err++; + } + if (0 != (isr_status & HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_LEN_MISMATCH_ERROR)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_TX), + "u=%u, txch=%u, pdma len mismatch err\n", unit, channel); + _hal_dawn_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_LEN_MISMATCH_ERROR); + ptr_tx_cb->cnt.channel[channel].len_mismatch_err++; + } + if (0 != (isr_status & HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_PKTPL_DMA_READ_ERROR)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_TX), + "u=%u, txch=%u, pdma pkt buf dma read err\n", unit, channel); + _hal_dawn_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_PKTPL_DMA_READ_ERROR); + ptr_tx_cb->cnt.channel[channel].pktpl_dma_read_err++; + _hal_dawn_pkt_handleTxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_COS_ERROR)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_TX), + "u=%u, txch=%u, pdma tx cos err\n", unit, channel); + _hal_dawn_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_COS_ERROR); + ptr_tx_cb->cnt.channel[channel].cos_err++; + } + if (0 != (isr_status & HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_GT255_ERROR)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_TX), + "u=%u, txch=%u, pdma gpd num > 255 err\n", unit, channel); + _hal_dawn_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_GT255_ERROR); + ptr_tx_cb->cnt.channel[channel].gpd_gt255_err++; + _hal_dawn_pkt_handleTxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_PFC)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_TX, + "u=%u, txch=%u, pdma flow ctrl\n", unit, channel); + _hal_dawn_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_PFC); + ptr_tx_cb->cnt.channel[channel].pfc++; + } + if (0 != (isr_status & HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_CREDIT_UDFL_ERROR)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_TX), + "u=%u, txch=%u, pdma credit underflow err\n", unit, channel); + _hal_dawn_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_CREDIT_UDFL_ERROR); + ptr_tx_cb->cnt.channel[channel].credit_udfl_err++; + _hal_dawn_pkt_handleTxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_DMA_WRITE_ERROR)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_TX), + "u=%u, txch=%u, pdma dma write err\n", unit, channel); + _hal_dawn_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_DMA_WRITE_ERROR); + ptr_tx_cb->cnt.channel[channel].dma_write_err++; + _hal_dawn_pkt_handleTxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_STOP_CMD_CPLT)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_TX, + "u=%u, txch=%u, pdma stop done\n", unit, channel); + _hal_dawn_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_STOP_CMD_CPLT); + ptr_tx_cb->cnt.channel[channel].sw_issue_stop++; + } + if (0 != isr_status) + { + _hal_dawn_pkt_unmaskAllTxL2IsrReg(unit, channel); + } + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_handleRxL2Isr + * PURPOSE: + * To handle the RX L2 interrupt according to the ISR status. + * INPUT: + * unit -- The unit ID + * channel -- The channel where the interrupt occurs + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully handle the L2 interrupt. + * NOTES: + * + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_handleRxL2Isr( + const UI32_T unit, + const HAL_DAWN_PKT_RX_CHANNEL_T channel) +{ + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_T isr_status = 0x0; + HAL_DAWN_PKT_RX_CB_T *ptr_rx_cb = HAL_DAWN_PKT_GET_RX_CB_PTR(unit); + + osal_mdc_readPciReg(unit, + HAL_DAWN_PKT_GET_PDMA_RCH_REG(HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_RCH_INT_STAT), channel), + &isr_status, sizeof(isr_status)); + + _hal_dawn_pkt_maskAllRxL2IsrReg(unit, channel); + + if (0 != (isr_status & HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_LOW)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_RX, + "u=%u, rxch=%u, pdma avbl gpd low\n", unit, channel); + _hal_dawn_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_LOW); + ptr_rx_cb->cnt.channel[channel].avbl_gpd_low++; + } + if (0 != (isr_status & HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_EMPTY)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_RX, + "u=%u, rxch=%u, pdma avbl gpd empty\n", unit, channel); + _hal_dawn_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_EMPTY); + ptr_rx_cb->cnt.channel[channel].avbl_gpd_empty++; + } + if (0 != (isr_status & HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_ERROR)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_RX), + "u=%u, rxch=%u, pdma avbl gpd err\n", unit, channel); + _hal_dawn_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_ERROR); + ptr_rx_cb->cnt.channel[channel].avbl_gpd_err++; + _hal_dawn_pkt_handleRxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_GPD_CHKSM_ERROR)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_RX), + "u=%u, rxch=%u, pdma gpd chksum err\n", unit, channel); + _hal_dawn_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_GPD_CHKSM_ERROR); + ptr_rx_cb->cnt.channel[channel].gpd_chksm_err++; + _hal_dawn_pkt_handleRxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_DMA_READ_ERROR)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_RX), + "u=%u, rxch=%u, pdma dma read err\n", unit, channel); + _hal_dawn_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_DMA_READ_ERROR); + ptr_rx_cb->cnt.channel[channel].dma_read_err++; + _hal_dawn_pkt_handleRxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_DMA_WRITE_ERROR)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_RX), + "u=%u, rxch=%u, pdma dma write err\n", unit, channel); + _hal_dawn_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_DMA_WRITE_ERROR); + ptr_rx_cb->cnt.channel[channel].dma_write_err++; + _hal_dawn_pkt_handleRxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_STOP_CMD_CPLT)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_RX, + "u=%u, rxch=%u, pdma stop done\n", unit, channel); + _hal_dawn_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_STOP_CMD_CPLT); + ptr_rx_cb->cnt.channel[channel].sw_issue_stop++; + } + if (0 != (isr_status & HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_GPD_GT255_ERROR)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_RX), + "u=%u, rxch=%u, pdma gpd num > 255 err\n", unit, channel); + _hal_dawn_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_GPD_GT255_ERROR); + ptr_rx_cb->cnt.channel[channel].gpd_gt255_err++; + _hal_dawn_pkt_handleRxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_TOD_UNINIT)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_RX), + "u=%u, rxch=%u, pdma tod ununit err\n", unit, channel); + _hal_dawn_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_TOD_UNINIT); + ptr_rx_cb->cnt.channel[channel].tod_uninit++; + _hal_dawn_pkt_handleRxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_PKT_ERROR_DROP)) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_RX), + "u=%u, rxch=%u, pdma pkt err drop\n", unit, channel); + _hal_dawn_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_PKT_ERROR_DROP); + ptr_rx_cb->cnt.channel[channel].pkt_err_drop++; + } + if (0 != (isr_status & HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_UDSZ_DROP)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_RX, + "u=%u, rxch=%u, pdma pkt under size\n", unit, channel); + _hal_dawn_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_UDSZ_DROP); + ptr_rx_cb->cnt.channel[channel].udsz_drop++; + } + if (0 != (isr_status & HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_OVSZ_DROP)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_RX, + "u=%u, rxch=%u, pdma pkt over size\n", unit, channel); + _hal_dawn_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_OVSZ_DROP); + ptr_rx_cb->cnt.channel[channel].ovsz_drop++; + } + if (0 != (isr_status & HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_CMDQ_OVF_DROP)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_RX, + "u=%u, rxch=%u, pdma cmdq overflow\n", unit, channel); + _hal_dawn_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_CMDQ_OVF_DROP); + ptr_rx_cb->cnt.channel[channel].cmdq_ovf_drop++; + } + if (0 != (isr_status & HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_FIFO_OVF_DROP)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_RX, + "u=%u, rxch=%u, pdma fifo overflow\n", unit, channel); + _hal_dawn_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_FIFO_OVF_DROP); + ptr_rx_cb->cnt.channel[channel].fifo_ovf_drop++; + } + if (0 != isr_status) + { + _hal_dawn_pkt_unmaskAllRxL2IsrReg(unit, channel); + } + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_handleErrorTask + * PURPOSE: + * To invoke the corresponding handler for the L2 interrupts. + * INPUT: + * ptr_argv -- The unit ID + * OUTPUT: + * None + * RETURN: + * None + * NOTES: + * None + */ +static void +_hal_dawn_pkt_handleErrorTask( + void *ptr_argv) +{ + UI32_T unit = (UI32_T)((CLX_HUGE_T)ptr_argv); + HAL_DAWN_PKT_L2_ISR_T isr_status = 0x0; + + osal_initRunThread(); + do + { + /* receive Error-ISR */ + osal_waitEvent(HAL_DAWN_PKT_ERR_EVENT(unit)); + if (CLX_E_OK != osal_isRunThread()) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_COMMON, + "u=%u, err task destroyed\n", unit); + break; /* deinit-thread */ + } + + osal_mdc_readPciReg(unit, + HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_ERR_INT_STAT), + &isr_status, sizeof(UI32_T)); + + if (0 != (HAL_DAWN_PKT_L2_ISR_RCH0 & isr_status)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_COMMON, "u=%u, rxch=0, rcv err isr, status=0x%x\n", + unit, isr_status); + _hal_dawn_pkt_handleRxL2Isr(unit, HAL_DAWN_PKT_RX_CHANNEL_0); + } + if (0 != (HAL_DAWN_PKT_L2_ISR_RCH1 & isr_status)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_COMMON, "u=%u, rxch=1, rcv err isr, status=0x%x\n", + unit, isr_status); + _hal_dawn_pkt_handleRxL2Isr(unit, HAL_DAWN_PKT_RX_CHANNEL_1); + } + if (0 != (HAL_DAWN_PKT_L2_ISR_RCH2 & isr_status)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_COMMON, "u=%u, rxch=2, rcv err isr, status=0x%x\n", + unit, isr_status); + _hal_dawn_pkt_handleRxL2Isr(unit, HAL_DAWN_PKT_RX_CHANNEL_2); + } + if (0 != (HAL_DAWN_PKT_L2_ISR_RCH3 & isr_status)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_COMMON, "u=%u, rxch=3, rcv err isr, status=0x%x\n", + unit, isr_status); + _hal_dawn_pkt_handleRxL2Isr(unit, HAL_DAWN_PKT_RX_CHANNEL_3); + } + if (0 != (HAL_DAWN_PKT_L2_ISR_TCH0 & isr_status)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_COMMON, "u=%u, txch=0, rcv err isr, status=0x%x\n", + unit, isr_status); + _hal_dawn_pkt_handleTxL2Isr(unit, HAL_DAWN_PKT_TX_CHANNEL_0); + } + if (0 != (HAL_DAWN_PKT_L2_ISR_TCH1 & isr_status)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_COMMON, "u=%u, txch=1, rcv err isr, status=0x%x\n", + unit, isr_status); + _hal_dawn_pkt_handleTxL2Isr(unit, HAL_DAWN_PKT_TX_CHANNEL_1); + } + if (0 != (HAL_DAWN_PKT_L2_ISR_TCH2 & isr_status)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_COMMON, "u=%u, txch=2, rcv err isr, status=0x%x\n", + unit, isr_status); + _hal_dawn_pkt_handleTxL2Isr(unit, HAL_DAWN_PKT_TX_CHANNEL_2); + } + if (0 != (HAL_DAWN_PKT_L2_ISR_TCH3 & isr_status)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_COMMON, "u=%u, txch=3, rcv err isr, status=0x%x\n", + unit, isr_status); + _hal_dawn_pkt_handleTxL2Isr(unit, HAL_DAWN_PKT_TX_CHANNEL_3); + } + if (0 != (HAL_DAWN_PKT_L2_ISR_RX_QID_MAP_ERR & isr_status)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_COMMON, "u=%u, rcv rx qid map err isr, status=0x%x\n", + unit, isr_status); + } + if (0 != (HAL_DAWN_PKT_L2_ISR_RX_FRAME_ERR & isr_status)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_COMMON, "u=%u, rcv rx frame err isr, status=0x%x\n", + unit, isr_status); + } + if (0 != isr_status) + { + osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_ERR_INT_CLR), + &isr_status, sizeof(UI32_T)); + + _hal_dawn_pkt_unmaskIntr(unit, HAL_DAWN_PKT_ERR_REG(unit)); + } + + } while (CLX_E_OK == osal_isRunThread()); + osal_exitRunThread(); +} + +/* FUNCTION NAME: _hal_dawn_pkt_handleTxDoneTask + * PURPOSE: + * To handle the TX done interrupt for the specified TX channel. + * INPUT: + * ptr_argv -- The unit ID and channel ID + * OUTPUT: + * None + * RETURN: + * None + * NOTES: + * None + */ +static void +_hal_dawn_pkt_handleTxDoneTask( + void *ptr_argv) +{ + /* cookie or index */ + UI32_T unit = ((HAL_DAWN_PKT_ISR_COOKIE_T *)ptr_argv)->unit; + HAL_DAWN_PKT_TX_CHANNEL_T channel = (HAL_DAWN_PKT_TX_CHANNEL_T) + ((HAL_DAWN_PKT_ISR_COOKIE_T *)ptr_argv)->channel; + /* control block */ + HAL_DAWN_PKT_TX_CB_T *ptr_tx_cb = HAL_DAWN_PKT_GET_TX_CB_PTR(unit); + HAL_DAWN_PKT_TX_PDMA_T *ptr_tx_pdma = HAL_DAWN_PKT_GET_TX_PDMA_PTR(unit, channel); + volatile HAL_DAWN_PKT_TX_GPD_T *ptr_tx_gpd = NULL; + UI32_T first_gpd_idx = 0; /* To record the first GPD */ + UI32_T loop_cnt = 0; + CLX_IRQ_FLAGS_T irg_flags; + unsigned long timeout = 0; + UI32_T bulk_pkt_cnt = 0, idx; + + osal_initRunThread(); + do + { + /* receive Tx-Done-ISR */ + osal_waitEvent(HAL_DAWN_PKT_TCH_EVENT(unit, channel)); + if (CLX_E_OK != osal_isRunThread()) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_TX, + "u=%u, txch=%u, tx done task destroyed\n", unit, channel); + break; /* deinit-thread */ + } + + /* protect Tx PDMA + * for sync-intr, the sema is locked by sendGpd + */ + if (HAL_DAWN_PKT_TX_WAIT_SYNC_INTR != ptr_tx_cb->wait_mode) + { + osal_takeIsrLock(&ptr_tx_pdma->ring_lock, &irg_flags); + } + + loop_cnt = ptr_tx_pdma->used_gpd_num; + while (loop_cnt > 0) + { + ptr_tx_gpd = HAL_DAWN_PKT_GET_TX_GPD_PTR(unit, channel, ptr_tx_pdma->free_idx); + osal_dma_invalidateCache((void *)ptr_tx_gpd, sizeof(HAL_DAWN_PKT_TX_GPD_T)); + + /* If hwo=HW, it might be: + * 1. err_flag=TRUE -> HW breakdown -> enque and recover -> break + * 2. err_flag=FALSE -> HW busy -> break + */ + if (HAL_DAWN_PKT_HWO_HW_OWN == ptr_tx_gpd->hwo) + { + if (TRUE == ptr_tx_pdma->err_flag) + { + /* flush the incomplete Tx packet */ + if (HAL_DAWN_PKT_TX_WAIT_ASYNC == ptr_tx_cb->wait_mode) + { + for (idx = 0; idx < ptr_tx_pdma->gpd_num; idx++) + { + if (NULL != ptr_tx_pdma->pptr_sw_gpd_ring[idx]) + { + ptr_tx_pdma->pptr_sw_gpd_bulk[bulk_pkt_cnt] + = ptr_tx_pdma->pptr_sw_gpd_ring[idx]; + ptr_tx_pdma->pptr_sw_gpd_ring[idx] = NULL; + bulk_pkt_cnt++; + } + } + } + + /* do error recover */ + first_gpd_idx = 0; + if (CLX_E_OK == _hal_dawn_pkt_recoverTxPdma(unit, channel)) + { + ptr_tx_pdma->err_flag = FALSE; + ptr_tx_cb->cnt.channel[channel].err_recover++; + } + else + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_TX | HAL_DAWN_PKT_DBG_ERR), + "u=%u, txch=%u, err recover failed\n", + unit, channel); + } + } + else + { + } + break; + } + + if (HAL_DAWN_PKT_TX_WAIT_ASYNC == ptr_tx_cb->wait_mode) + { + /* If hwo=SW and ch=0, record the head of sw gpd in bulk buf */ + if (HAL_DAWN_PKT_CH_LAST_GPD == ptr_tx_gpd->ch) + { + ptr_tx_pdma->pptr_sw_gpd_bulk[bulk_pkt_cnt] + = ptr_tx_pdma->pptr_sw_gpd_ring[first_gpd_idx]; + + bulk_pkt_cnt++; + ptr_tx_pdma->pptr_sw_gpd_ring[first_gpd_idx] = NULL; + + /* next SW-GPD must be the head of another PKT->SW-GPD */ + first_gpd_idx = ptr_tx_pdma->free_idx + 1; + first_gpd_idx %= ptr_tx_pdma->gpd_num; + } + } + + if (HAL_DAWN_PKT_ECC_ERROR_OCCUR == ptr_tx_gpd->ecce) + { + ptr_tx_cb->cnt.channel[channel].ecc_err++; + } + + /* update Tx PDMA */ + ptr_tx_pdma->free_idx++; + ptr_tx_pdma->free_idx %= ptr_tx_pdma->gpd_num; + ptr_tx_pdma->used_gpd_num--; + ptr_tx_pdma->free_gpd_num++; + loop_cnt--; + } + + /* let the netdev resume Tx */ + _hal_dawn_pkt_resumeAllIntf(unit); + + /* update ISR and counter */ + ptr_tx_cb->cnt.channel[channel].tx_done++; + + _hal_dawn_pkt_unmaskIntr(unit, HAL_DAWN_PKT_TCH_REG(unit, channel)); + + if (HAL_DAWN_PKT_TX_WAIT_SYNC_INTR != ptr_tx_cb->wait_mode) + { + osal_giveIsrLock(&ptr_tx_pdma->ring_lock, &irg_flags); + } + else + { + osal_giveSemaphore(&ptr_tx_pdma->sync_intr_sema); + } + + /* enque packet after releasing the spinlock */ + _hal_dawn_pkt_txEnQueueBulk(unit, channel, bulk_pkt_cnt); + bulk_pkt_cnt = 0; + + /* prevent this task from executing too long */ + if (!(time_before(jiffies, timeout))) + { + schedule(); + timeout = jiffies + 1; /* continuously free tx descriptor for 1 tick */ + } + + } while (CLX_E_OK == osal_isRunThread()); + osal_exitRunThread(); +} + +/* FUNCTION NAME: _hal_dawn_pkt_handleRxDoneTask + * PURPOSE: + * To handle the RX done interrupt for the specified RX channel. + * INPUT: + * ptr_argv -- The unit ID and channel ID + * OUTPUT: + * None + * RETURN: + * None + * NOTES: + * None + */ +static void +_hal_dawn_pkt_handleRxDoneTask( + void *ptr_argv) +{ + /* cookie or index */ + UI32_T unit = ((HAL_DAWN_PKT_ISR_COOKIE_T *)ptr_argv)->unit; + HAL_DAWN_PKT_RX_CHANNEL_T channel = (HAL_DAWN_PKT_RX_CHANNEL_T) + ((HAL_DAWN_PKT_ISR_COOKIE_T *)ptr_argv)->channel; + + /* control block */ + HAL_DAWN_PKT_RX_CB_T *ptr_rx_cb = HAL_DAWN_PKT_GET_RX_CB_PTR(unit); + HAL_DAWN_PKT_RX_PDMA_T *ptr_rx_pdma = HAL_DAWN_PKT_GET_RX_PDMA_PTR(unit, channel); + volatile HAL_DAWN_PKT_RX_GPD_T *ptr_rx_gpd = NULL; + + BOOL_T first = TRUE; + BOOL_T last = FALSE; + HAL_DAWN_PKT_RX_SW_GPD_T *ptr_sw_gpd = NULL; + HAL_DAWN_PKT_RX_SW_GPD_T *ptr_sw_first_gpd = NULL; + UI32_T loop_cnt = 0; + unsigned long timeout = 0; + + osal_initRunThread(); + do + { + /* receive Rx-Done-ISR */ + osal_waitEvent(HAL_DAWN_PKT_RCH_EVENT(unit, channel)); + if (CLX_E_OK != osal_isRunThread()) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_RX, + "u=%u, rxch=%u, rx done task destroyed\n", unit, channel); + break; /* deinit-thread */ + } + + /* check if Rx-system is inited */ + if (0 == ptr_rx_cb->buf_len) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_RX | HAL_DAWN_PKT_DBG_ERR), + "u=%u, rxch=%u, rx gpd buf len=0\n", + unit, channel); + continue; + } + + /* protect Rx PDMA */ + osal_takeSemaphore(&ptr_rx_pdma->sema, CLX_SEMAPHORE_WAIT_FOREVER); + loop_cnt = ptr_rx_pdma->gpd_num; + while (loop_cnt > 0) + { + ptr_rx_gpd = HAL_DAWN_PKT_GET_RX_GPD_PTR(unit, channel, ptr_rx_pdma->cur_idx); + osal_dma_invalidateCache((void *)ptr_rx_gpd, sizeof(HAL_DAWN_PKT_RX_GPD_T)); + + /* If hwo=HW, it might be: + * 1. err_flag=TRUE -> HW breakdown -> enque and recover -> break + * 2. err_flag=FALSE -> HW busy -> break + */ + if (HAL_DAWN_PKT_HWO_HW_OWN == ptr_rx_gpd->hwo) + { + if (TRUE == ptr_rx_pdma->err_flag) + { + /* free the last incomplete Rx packet */ + if ((NULL != ptr_sw_first_gpd) && + (NULL != ptr_sw_gpd)) + { + ptr_sw_gpd->ptr_next = NULL; + ptr_sw_first_gpd->rx_complete = FALSE; + _hal_dawn_pkt_rxEnQueue(unit, channel, ptr_sw_first_gpd); + ptr_sw_first_gpd = NULL; + } + + /* do error recover */ + first = TRUE; + last = FALSE; + if (CLX_E_OK == _hal_dawn_pkt_recoverRxPdma(unit, channel)) + { + ptr_rx_pdma->err_flag = FALSE; + ptr_rx_cb->cnt.channel[channel].err_recover++; + } + else + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_RX | HAL_DAWN_PKT_DBG_ERR), + "u=%u, rxch=%u, err recover failed\n", + unit, channel); + } + } + else + { + } + break; + } + + /* Move HW-GPD to SW-GPD and append to a link-list */ + if (TRUE == first) + { + ptr_sw_first_gpd = (HAL_DAWN_PKT_RX_SW_GPD_T *)osal_alloc(sizeof(HAL_DAWN_PKT_RX_SW_GPD_T)); + ptr_sw_gpd = ptr_sw_first_gpd; + if (NULL != ptr_sw_gpd) + { + memcpy(&ptr_sw_gpd->rx_gpd, (void *)ptr_rx_gpd, sizeof(HAL_DAWN_PKT_RX_GPD_T)); + first = FALSE; + } + else + { + ptr_rx_cb->cnt.no_memory++; + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_RX | HAL_DAWN_PKT_DBG_ERR), + "u=%u, rxch=%u, alloc 1st sw gpd failed, size=%zu\n", + unit, channel, sizeof(HAL_DAWN_PKT_RX_SW_GPD_T)); + break; + } + } + else + { + ptr_sw_gpd->ptr_next = (HAL_DAWN_PKT_RX_SW_GPD_T *)osal_alloc(sizeof(HAL_DAWN_PKT_RX_SW_GPD_T)); + ptr_sw_gpd = ptr_sw_gpd->ptr_next; + if (NULL != ptr_sw_gpd) + { + memcpy(&ptr_sw_gpd->rx_gpd, (void *)ptr_rx_gpd, sizeof(HAL_DAWN_PKT_RX_GPD_T)); + } + else + { + ptr_rx_cb->cnt.no_memory++; + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_RX | HAL_DAWN_PKT_DBG_ERR), + "u=%u, rxch=%u, alloc mid sw gpd failed, size=%zu\n", + unit, channel, sizeof(HAL_DAWN_PKT_RX_SW_GPD_T)); + break; + } + } + + ptr_sw_gpd->ptr_cookie = ptr_rx_pdma->pptr_skb_ring[ptr_rx_pdma->cur_idx]; + + /* If hwo=SW and ch=0, enque SW-GPD and signal rxTask */ + if (HAL_DAWN_PKT_CH_LAST_GPD == ptr_rx_gpd->ch) + { + last = TRUE; + } + + /* If hwo=SW and ch=*, re-alloc-buf and resume */ + while (CLX_E_OK != _hal_dawn_pkt_allocRxPayloadBuf(unit, channel, ptr_rx_pdma->cur_idx)) + { + ptr_rx_cb->cnt.no_memory++; + HAL_DAWN_PKT_ALLOC_MEM_RETRY_SLEEP(); + } + ptr_rx_gpd->ioc = HAL_DAWN_PKT_IOC_HAS_INTR; + ptr_rx_gpd->hwo = HAL_DAWN_PKT_HWO_HW_OWN; + osal_dma_flushCache((void *)ptr_rx_gpd, sizeof(HAL_DAWN_PKT_RX_GPD_T)); + + /* Enque the SW-GPD to rxTask */ + if (TRUE == last) + { + ptr_sw_gpd->ptr_next = NULL; + ptr_sw_first_gpd->rx_complete = TRUE; + _hal_dawn_pkt_rxEnQueue(unit, channel, ptr_sw_first_gpd); + ptr_sw_first_gpd = NULL; + + /* To rebuild the SW GPD link list */ + first = TRUE; + last = FALSE; + } + + _hal_dawn_pkt_resumeRxChannelReg(unit, channel, 1); + + /* update Rx PDMA */ + ptr_rx_pdma->cur_idx++; + ptr_rx_pdma->cur_idx %= ptr_rx_pdma->gpd_num; + loop_cnt--; + } + + osal_giveSemaphore(&ptr_rx_pdma->sema); + + /* update ISR and counter */ + ptr_rx_cb->cnt.channel[channel].rx_done++; + + _hal_dawn_pkt_unmaskIntr(unit, HAL_DAWN_PKT_RCH_REG(unit, channel)); + + /* prevent this task from executing too long */ + if (!(time_before(jiffies, timeout))) + { + schedule(); + timeout = jiffies + 1; /* continuously rx for 1 tick */ + } + + } while (CLX_E_OK == osal_isRunThread()); + osal_exitRunThread(); +} + +static void +_hal_dawn_pkt_net_dev_tx_callback( + const UI32_T unit, + HAL_DAWN_PKT_TX_SW_GPD_T *ptr_sw_gpd, + struct sk_buff *ptr_skb) +{ + CLX_ADDR_T phy_addr = 0; + + /* unmap dma */ + phy_addr = CLX_ADDR_32_TO_64(ptr_sw_gpd->tx_gpd.data_buf_addr_hi, ptr_sw_gpd->tx_gpd.data_buf_addr_lo); + osal_skb_unmapDma(phy_addr, ptr_skb->len, DMA_TO_DEVICE); + + /* free skb */ + osal_skb_free(ptr_skb); + + /* free gpd */ + osal_free(ptr_sw_gpd); +} + +/* FUNCTION NAME: hal_dawn_pkt_initTask + * PURPOSE: + * To initialize the Task for packet module. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully dinitialize the control block. + * CLX_E_OTHERS -- Initialize the control block failed. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_dawn_pkt_initTask( + const UI32_T unit) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_DAWN_PKT_DRV_CB_T *ptr_cb = HAL_DAWN_PKT_GET_DRV_CB_PTR(unit); + HAL_DAWN_PKT_TX_CB_T *ptr_tx_cb = HAL_DAWN_PKT_GET_TX_CB_PTR(unit); + HAL_DAWN_PKT_RX_CB_T *ptr_rx_cb = HAL_DAWN_PKT_GET_RX_CB_PTR(unit); + UI32_T channel = 0; + + if (0 != (ptr_cb->init_flag & HAL_DAWN_PKT_INIT_TASK)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_ERR, + "u=%u, pkt task init failed, not inited\n", unit); + return (rc); + } + + /* Init handleErrorTask */ + rc = osal_createThread("ERROR", HAL_DFLT_CFG_PKT_ERROR_ISR_THREAD_STACK, + HAL_DFLT_CFG_PKT_ERROR_ISR_THREAD_PRI, _hal_dawn_pkt_handleErrorTask, + (void *)((CLX_HUGE_T)unit), &ptr_cb->err_task_id); + + /* Init handleTxDoneTask */ + for (channel = 0; ((channel < HAL_DAWN_PKT_TX_CHANNEL_LAST) && (CLX_E_OK == rc)); channel++) + { + ptr_tx_cb->isr_task_cookie[channel].unit = unit; + ptr_tx_cb->isr_task_cookie[channel].channel = channel; + + rc = osal_createThread("TX_ISR", HAL_DFLT_CFG_PKT_TX_ISR_THREAD_STACK, + HAL_DFLT_CFG_PKT_TX_ISR_THREAD_PRI, _hal_dawn_pkt_handleTxDoneTask, + (void *)&ptr_tx_cb->isr_task_cookie[channel], + &ptr_tx_cb->isr_task_id[channel]); + } + + /* Init handleRxDoneTask */ + for (channel = 0; ((channel < HAL_DAWN_PKT_RX_CHANNEL_LAST) && (CLX_E_OK == rc)); channel++) + { + ptr_rx_cb->isr_task_cookie[channel].unit = unit; + ptr_rx_cb->isr_task_cookie[channel].channel = channel; + + rc = osal_createThread("RX_ISR", HAL_DFLT_CFG_PKT_RX_ISR_THREAD_STACK, + HAL_DFLT_CFG_PKT_RX_ISR_THREAD_PRI, _hal_dawn_pkt_handleRxDoneTask, + (void *)&ptr_rx_cb->isr_task_cookie[channel], + &ptr_rx_cb->isr_task_id[channel]); + } + + /* Init txTask */ + if (HAL_DAWN_PKT_TX_WAIT_ASYNC == ptr_tx_cb->wait_mode) + { + ptr_tx_cb->running = TRUE; + } + + ptr_cb->init_flag |= HAL_DAWN_PKT_INIT_TASK; + + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_COMMON, + "u=%u, pkt task init done, init flag=0x%x\n", unit, ptr_cb->init_flag); + + /* For some specail case in warmboot, the netifs are not destroyed during sdk deinit + * but stopped, here we need to resume them with the original carrier status + */ + _hal_dawn_pkt_resumeAllIntf(unit); + + ptr_tx_cb->net_tx_allowed = TRUE; + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_initTxPdma + * PURPOSE: + * To initialize the TX PDMA. + * INPUT: + * unit -- The unit ID + * channel -- The target Tx channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully initialize the TX PDMA. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_initTxPdma( + const UI32_T unit, + const HAL_DAWN_PKT_TX_CHANNEL_T channel) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_DAWN_PKT_TX_CB_T *ptr_tx_cb = HAL_DAWN_PKT_GET_TX_CB_PTR(unit); + HAL_DAWN_PKT_TX_PDMA_T *ptr_tx_pdma = HAL_DAWN_PKT_GET_TX_PDMA_PTR(unit, channel); + CLX_IRQ_FLAGS_T irg_flags; + + /* Isr lock to protect Tx PDMA */ + osal_createIsrLock("TCH_LCK", &ptr_tx_pdma->ring_lock); + + if (HAL_DAWN_PKT_TX_WAIT_SYNC_INTR == ptr_tx_cb->wait_mode) + { + /* Sync semaphore to signal sendTxPacket */ + osal_createSemaphore("TCH_SYN", CLX_SEMAPHORE_SYNC, &ptr_tx_pdma->sync_intr_sema); + } + + /* Reset Tx PDMA */ + osal_takeIsrLock(&ptr_tx_pdma->ring_lock, &irg_flags); + + ptr_tx_pdma->used_idx = 0; + ptr_tx_pdma->free_idx = 0; + ptr_tx_pdma->used_gpd_num = 0; + ptr_tx_pdma->free_gpd_num = HAL_DFLT_CFG_PKT_TX_GPD_NUM; + ptr_tx_pdma->gpd_num = HAL_DFLT_CFG_PKT_TX_GPD_NUM; + + /* Prepare the HW-GPD ring */ + ptr_tx_pdma->ptr_gpd_start_addr = (HAL_DAWN_PKT_TX_GPD_T *)osal_dma_alloc( + (ptr_tx_pdma->gpd_num + 1) * sizeof(HAL_DAWN_PKT_TX_GPD_T)); + + if (NULL != ptr_tx_pdma->ptr_gpd_start_addr) + { + osal_memset(ptr_tx_pdma->ptr_gpd_start_addr, 0x0, + (ptr_tx_pdma->gpd_num + 1) * sizeof(HAL_DAWN_PKT_TX_GPD_T)); + + ptr_tx_pdma->ptr_gpd_align_start_addr = (HAL_DAWN_PKT_TX_GPD_T *)HAL_DAWN_PKT_PDMA_ALIGN_ADDR( + (CLX_HUGE_T)ptr_tx_pdma->ptr_gpd_start_addr, sizeof(HAL_DAWN_PKT_TX_GPD_T)); + + rc = _hal_dawn_pkt_initTxPdmaRing(unit, channel); + if (CLX_E_OK == rc) + { + _hal_dawn_pkt_startTxChannelReg(unit, channel, 0); + } + } + else + { + ptr_tx_cb->cnt.no_memory++; + rc = CLX_E_NO_MEMORY; + } + + if (HAL_DAWN_PKT_TX_WAIT_ASYNC == ptr_tx_cb->wait_mode) + { + if (CLX_E_OK == rc) + { + /* Prepare the SW-GPD ring */ + ptr_tx_pdma->pptr_sw_gpd_ring = (HAL_DAWN_PKT_TX_SW_GPD_T **)osal_alloc( + ptr_tx_pdma->gpd_num * sizeof(HAL_DAWN_PKT_TX_SW_GPD_T *)); + + if (NULL != ptr_tx_pdma->pptr_sw_gpd_ring) + { + osal_memset(ptr_tx_pdma->pptr_sw_gpd_ring, 0x0, + ptr_tx_pdma->gpd_num * sizeof(HAL_DAWN_PKT_TX_SW_GPD_T *)); + } + else + { + ptr_tx_cb->cnt.no_memory++; + rc = CLX_E_NO_MEMORY; + } + + /* a temp buffer to store the 1st sw gpd for each packet to be enque + * we cannot enque packet before release a spinlock + */ + ptr_tx_pdma->pptr_sw_gpd_bulk = (HAL_DAWN_PKT_TX_SW_GPD_T **)osal_alloc( + ptr_tx_pdma->gpd_num * sizeof(HAL_DAWN_PKT_TX_SW_GPD_T *)); + + if (NULL != ptr_tx_pdma->pptr_sw_gpd_bulk) + { + osal_memset(ptr_tx_pdma->pptr_sw_gpd_bulk, 0x0, + ptr_tx_pdma->gpd_num * sizeof(HAL_DAWN_PKT_TX_SW_GPD_T *)); + } + else + { + ptr_tx_cb->cnt.no_memory++; + rc = CLX_E_NO_MEMORY; + } + } + } + + osal_giveIsrLock(&ptr_tx_pdma->ring_lock, &irg_flags); + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_initRxPdma + * PURPOSE: + * To initialize the RX PDMA. + * INPUT: + * unit -- The unit ID + * channel -- The target Rx channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully initialize the RX PDMA. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_initRxPdma( + const UI32_T unit, + const HAL_DAWN_PKT_RX_CHANNEL_T channel) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_DAWN_PKT_RX_CB_T *ptr_rx_cb = HAL_DAWN_PKT_GET_RX_CB_PTR(unit); + HAL_DAWN_PKT_RX_PDMA_T *ptr_rx_pdma = HAL_DAWN_PKT_GET_RX_PDMA_PTR(unit, channel); + + /* Binary semaphore to protect Rx PDMA */ + osal_createSemaphore("RCH_LCK", CLX_SEMAPHORE_BINARY, &ptr_rx_pdma->sema); + + /* Reset Rx PDMA */ + osal_takeSemaphore(&ptr_rx_pdma->sema, CLX_SEMAPHORE_WAIT_FOREVER); + ptr_rx_pdma->cur_idx = 0; + ptr_rx_pdma->gpd_num = HAL_DFLT_CFG_PKT_RX_GPD_NUM; + + /* Prepare the HW-GPD ring */ + ptr_rx_pdma->ptr_gpd_start_addr = (HAL_DAWN_PKT_RX_GPD_T *)osal_dma_alloc( + (ptr_rx_pdma->gpd_num + 1) * sizeof(HAL_DAWN_PKT_RX_GPD_T)); + + if (NULL != ptr_rx_pdma->ptr_gpd_start_addr) + { + osal_memset(ptr_rx_pdma->ptr_gpd_start_addr, 0, + (ptr_rx_pdma->gpd_num + 1) * sizeof(HAL_DAWN_PKT_RX_GPD_T)); + + ptr_rx_pdma->ptr_gpd_align_start_addr = (HAL_DAWN_PKT_RX_GPD_T *)HAL_DAWN_PKT_PDMA_ALIGN_ADDR( + (CLX_HUGE_T)ptr_rx_pdma->ptr_gpd_start_addr, sizeof(HAL_DAWN_PKT_RX_GPD_T)); + + /* will initRxPdmaRingBuf and start RCH after setRxConfig */ + rc = _hal_dawn_pkt_initRxPdmaRing(unit, channel); + } + else + { + ptr_rx_cb->cnt.no_memory++; + rc = CLX_E_NO_MEMORY; + } + + if (CLX_E_OK == rc) + { + /* Prepare the SKB ring */ + ptr_rx_pdma->pptr_skb_ring = (struct sk_buff **)osal_alloc( + ptr_rx_pdma->gpd_num * sizeof(struct sk_buff *)); + + if (NULL != ptr_rx_pdma->pptr_skb_ring) + { + osal_memset(ptr_rx_pdma->pptr_skb_ring, 0x0, + ptr_rx_pdma->gpd_num * sizeof(struct sk_buff *)); + } + else + { + ptr_rx_cb->cnt.no_memory++; + rc = CLX_E_NO_MEMORY; + } + } + + osal_giveSemaphore(&ptr_rx_pdma->sema); + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_initPktCb + * PURPOSE: + * To initialize the control block of Drv. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully initialize the control block. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_initPktCb( + const UI32_T unit) +{ + HAL_DAWN_PKT_DRV_CB_T *ptr_cb = HAL_DAWN_PKT_GET_DRV_CB_PTR(unit); + UI32_T idx = 0, vec = sizeof(_hal_dawn_pkt_intr_vec) / sizeof(HAL_DAWN_PKT_INTR_VEC_T); + + osal_memset(ptr_cb, 0x0, sizeof(HAL_DAWN_PKT_DRV_CB_T)); + + /* Register PKT interrupt functions */ + osal_createIsrLock("ISR_LOCK", &ptr_cb->intr_lock); + osal_mdc_registerIsr(unit, _hal_dawn_pkt_dispatcher, (void *)((CLX_HUGE_T)unit)); + + for (idx = 0; idx < vec; idx++) + { + osal_createEvent("ISR_EVENT", &_hal_dawn_pkt_intr_vec[idx].intr_event); + ptr_cb->intr_bitmap |= (_hal_dawn_pkt_intr_vec[idx].intr_reg); + } + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_initPktTxCb + * PURPOSE: + * To initialize the control block of Rx PDMA. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully initialize the control block. + * CLX_E_OTHERS -- Configure failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_initPktTxCb( + const UI32_T unit) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_DAWN_PKT_TX_CB_T *ptr_tx_cb = HAL_DAWN_PKT_GET_TX_CB_PTR(unit); + HAL_DAWN_PKT_TX_CHANNEL_T channel = 0; + + osal_memset(ptr_tx_cb, 0x0, sizeof(HAL_DAWN_PKT_TX_CB_T)); + + ptr_tx_cb->wait_mode = HAL_DAWN_PKT_TX_WAIT_MODE; + + if (HAL_DAWN_PKT_TX_WAIT_ASYNC == ptr_tx_cb->wait_mode) + { + /* Sync semaphore to signal txTask */ + osal_createEvent("TX_SYNC", &ptr_tx_cb->sync_sema); + + /* Initialize Tx GPD-queue (of first SW-GPD) from handleTxDoneTask to txTask */ + ptr_tx_cb->sw_queue.len = HAL_DFLT_CFG_PKT_TX_QUEUE_LEN; + ptr_tx_cb->sw_queue.weight = 0; + + osal_createSemaphore("TX_QUE", CLX_SEMAPHORE_BINARY, &ptr_tx_cb->sw_queue.sema); + osal_que_create(&ptr_tx_cb->sw_queue.que_id, ptr_tx_cb->sw_queue.len); + } + else if (HAL_DAWN_PKT_TX_WAIT_SYNC_POLL == ptr_tx_cb->wait_mode) + { + /* Disable TX done ISR. */ + for (channel = 0; channel < HAL_DAWN_PKT_TX_CHANNEL_LAST; channel++) + { + _hal_dawn_pkt_disableIntr(unit, HAL_DAWN_PKT_TCH_REG(unit, channel)); + } + } + + /* Init Tx PDMA */ + for (channel = 0; ((channel < HAL_DAWN_PKT_TX_CHANNEL_LAST) && (CLX_E_OK == rc)); channel++) + { + rc = _hal_dawn_pkt_initTxPdma(unit, channel); + } + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_initPktRxCb + * PURPOSE: + * To initialize the control block of Rx PDMA. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully initialize the control block. + * CLX_E_OTHERS -- Configure failed. + * NOTES: + * + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_initPktRxCb( + const UI32_T unit) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_DAWN_PKT_RX_CB_T *ptr_rx_cb = HAL_DAWN_PKT_GET_RX_CB_PTR(unit); + HAL_DAWN_PKT_RX_CHANNEL_T channel = 0; + UI32_T queue = 0; + + osal_memset(ptr_rx_cb, 0x0, sizeof(HAL_DAWN_PKT_RX_CB_T)); + + ptr_rx_cb->sched_mode = HAL_DFLT_CFG_PKT_RX_SCHED_MODE; + + /* Sync semaphore to signal rxTask */ + osal_createEvent("RX_SYNC", &ptr_rx_cb->sync_sema); + + /* Initialize Rx GPD-queue (of first SW-GPD) from handleRxDoneTask to rxTask */ + for (queue = 0; ((queue < HAL_DAWN_PKT_RX_QUEUE_NUM) && (CLX_E_OK == rc)); queue++) + { + ptr_rx_cb->sw_queue[queue].len = HAL_DFLT_CFG_PKT_RX_QUEUE_LEN; + ptr_rx_cb->sw_queue[queue].weight = HAL_DFLT_CFG_PKT_RX_QUEUE_WEIGHT; + + osal_createSemaphore("RX_QUE", CLX_SEMAPHORE_BINARY, &ptr_rx_cb->sw_queue[queue].sema); + osal_que_create(&ptr_rx_cb->sw_queue[queue].que_id, ptr_rx_cb->sw_queue[queue].len); + } + + /* Init Rx PDMA */ + for (channel = 0; ((channel < HAL_DAWN_PKT_RX_CHANNEL_LAST) && (CLX_E_OK == rc)); channel++) + { + rc = _hal_dawn_pkt_initRxPdma(unit, channel); + } + + return (rc); +} + +/* FUNCTION NAME: _hal_dawn_pkt_initL1Isr + * PURPOSE: + * To initialize the PDMA L1 ISR configuration. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully initialize the L1 ISR. + * CLX_E_OTHERS -- Configure failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_initL1Isr( + const UI32_T unit) +{ + UI32_T idx = 0, vec = sizeof(_hal_dawn_pkt_intr_vec) / sizeof(HAL_DAWN_PKT_INTR_VEC_T); + + for (idx = 0; idx < vec; idx++) + { + _hal_dawn_pkt_enableIntr(unit, _hal_dawn_pkt_intr_vec[idx].intr_reg); + _hal_dawn_pkt_unmaskIntr(unit, _hal_dawn_pkt_intr_vec[idx].intr_reg); + } + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_dawn_pkt_initL2Isr + * PURPOSE: + * To initialize the PDMA L2 ISR configuration. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure for the L2 ISR. + * CLX_E_OTHERS -- Configure failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_dawn_pkt_initL2Isr( + const UI32_T unit) +{ + HAL_DAWN_PKT_L2_ISR_T isr_status = 0x0; + + HAL_DAWN_PKT_SET_BITMAP(isr_status, HAL_DAWN_PKT_L2_ISR_RCH0); + HAL_DAWN_PKT_SET_BITMAP(isr_status, HAL_DAWN_PKT_L2_ISR_RCH1); + HAL_DAWN_PKT_SET_BITMAP(isr_status, HAL_DAWN_PKT_L2_ISR_RCH2); + HAL_DAWN_PKT_SET_BITMAP(isr_status, HAL_DAWN_PKT_L2_ISR_RCH3); + HAL_DAWN_PKT_SET_BITMAP(isr_status, HAL_DAWN_PKT_L2_ISR_TCH0); + HAL_DAWN_PKT_SET_BITMAP(isr_status, HAL_DAWN_PKT_L2_ISR_TCH1); + HAL_DAWN_PKT_SET_BITMAP(isr_status, HAL_DAWN_PKT_L2_ISR_TCH2); + HAL_DAWN_PKT_SET_BITMAP(isr_status, HAL_DAWN_PKT_L2_ISR_TCH3); + HAL_DAWN_PKT_SET_BITMAP(isr_status, HAL_DAWN_PKT_L2_ISR_RX_QID_MAP_ERR); + HAL_DAWN_PKT_SET_BITMAP(isr_status, HAL_DAWN_PKT_L2_ISR_RX_FRAME_ERR); + + osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_ERR_INT_EN), + &isr_status, sizeof(UI32_T)); + + osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_ERR_INT_MASK_SET), + &isr_status, sizeof(UI32_T)); + + return (CLX_E_OK); + +} + +CLX_ERROR_NO_T +_hal_dawn_pkt_resetIosCreditCfg( + const UI32_T unit) +{ +#define HAL_DAWN_PKT_PDMA_CREDIT_CFG_RESET_OFFSET (16) + + UI32_T credit_cfg = 0x0; + UI32_T idx; + + for (idx=0; idxptr_profile = ptr_new_profile; + + /* Create the 1st node in the interface profile list */ + if (NULL == *pptr_profile_list) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_PROFILE, + "prof list empty\n"); + *pptr_profile_list = ptr_new_prof_node; + ptr_new_prof_node->ptr_next_node = NULL; + } + else + { + ptr_prev_node = *pptr_profile_list; + ptr_curr_node = *pptr_profile_list; + + while (ptr_curr_node != NULL) + { + if (ptr_curr_node->ptr_profile->priority <= ptr_new_profile->priority) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_PROFILE, + "find prof id=%d (%s) higher priority=%d, search next\n", + ptr_curr_node->ptr_profile->id, + ptr_curr_node->ptr_profile->name, + ptr_curr_node->ptr_profile->priority); + /* Search the next node */ + ptr_prev_node = ptr_curr_node; + ptr_curr_node = ptr_curr_node->ptr_next_node; + } + else + { + /* Insert intermediate node */ + ptr_new_prof_node->ptr_next_node = ptr_curr_node; + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_PROFILE, + "insert prof id=%d (%s) before prof id=%d (%s) (priority=%d >= %d)\n", + ptr_new_prof_node->ptr_profile->id, + ptr_new_prof_node->ptr_profile->name, + ptr_curr_node->ptr_profile->id, + ptr_curr_node->ptr_profile->name, + ptr_new_prof_node->ptr_profile->priority, + ptr_curr_node->ptr_profile->priority); + + if (ptr_prev_node == ptr_curr_node) + { + /* There is no previous node: change the root */ + *pptr_profile_list = ptr_new_prof_node; + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_PROFILE, + "insert prof id=%d (%s) to head (priority=%d)\n", + ptr_new_prof_node->ptr_profile->id, + ptr_new_prof_node->ptr_profile->name, + ptr_new_prof_node->ptr_profile->priority); + } + else + { + ptr_prev_node->ptr_next_node = ptr_new_prof_node; + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_PROFILE, + "insert prof id=%d (%s) after prof id=%d (%s) (priority=%d <= %d)\n", + ptr_new_prof_node->ptr_profile->id, + ptr_new_prof_node->ptr_profile->name, + ptr_prev_node->ptr_profile->id, + ptr_prev_node->ptr_profile->name, + ptr_new_prof_node->ptr_profile->priority, + ptr_prev_node->ptr_profile->priority); + } + + return (CLX_E_OK); + } + } + + /* Insert node to the tail of list */ + ptr_prev_node->ptr_next_node = ptr_new_prof_node; + ptr_new_prof_node->ptr_next_node = NULL; + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_PROFILE, + "insert prof id=%d (%s) to tail, after prof id=%d (%s) (priority=%d <= %d)\n", + ptr_new_prof_node->ptr_profile->id, + ptr_new_prof_node->ptr_profile->name, + ptr_prev_node->ptr_profile->id, + ptr_prev_node->ptr_profile->name, + ptr_new_prof_node->ptr_profile->priority, + ptr_prev_node->ptr_profile->priority); + } + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_addProfToAllIntf( + HAL_DAWN_PKT_NETIF_PROFILE_T *ptr_new_profile) +{ + UI32_T port; + HAL_DAWN_PKT_NETIF_PORT_DB_T *ptr_port_db; + + for (port = 0; port < HAL_DAWN_PKT_MAX_PORT_NUM; port++) + { + ptr_port_db = HAL_DAWN_PKT_GET_PORT_DB(port); + /* Shall we check if the interface is ever created on the port?? */ + /* if (NULL != ptr_port_db->ptr_net_dev) */ + if (1) + { + _hal_dawn_pkt_addProfToList(ptr_new_profile, &ptr_port_db->ptr_profile_list); + } + } + + return (CLX_E_OK); +} + +static HAL_DAWN_PKT_NETIF_PROFILE_T * +_hal_dawn_pkt_delProfFromListById( + const UI32_T id, + HAL_DAWN_PKT_PROFILE_NODE_T **pptr_profile_list) +{ + HAL_DAWN_PKT_PROFILE_NODE_T *ptr_temp_node; + HAL_DAWN_PKT_PROFILE_NODE_T *ptr_curr_node, *ptr_prev_node; + HAL_DAWN_PKT_NETIF_PROFILE_T *ptr_profile = NULL;; + + if (NULL != *pptr_profile_list) + { + /* Check the 1st node */ + if (id == (*pptr_profile_list)->ptr_profile->id) + { + ptr_profile = (*pptr_profile_list)->ptr_profile; + ptr_temp_node = (*pptr_profile_list); + (*pptr_profile_list) = ptr_temp_node->ptr_next_node; + + if (NULL != ptr_temp_node->ptr_next_node) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_PROFILE, + "choose prof id=%d (%s) as new head\n", + ptr_temp_node->ptr_next_node->ptr_profile->id, + ptr_temp_node->ptr_next_node->ptr_profile->name); + } + else + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_PROFILE, + "prof list is empty\n"); + } + + + osal_free(ptr_temp_node); + } + else + { + ptr_prev_node = *pptr_profile_list; + ptr_curr_node = ptr_prev_node->ptr_next_node; + + while (NULL != ptr_curr_node) + { + if (id != ptr_curr_node->ptr_profile->id) + { + ptr_prev_node = ptr_curr_node; + ptr_curr_node = ptr_curr_node->ptr_next_node; + } + else + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_PROFILE, + "find prof id=%d, free done\n", id); + + ptr_profile = ptr_curr_node->ptr_profile; + ptr_prev_node->ptr_next_node = ptr_curr_node->ptr_next_node; + osal_free(ptr_curr_node); + break; + } + } + } + } + + if (NULL == ptr_profile) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_PROFILE | HAL_DAWN_PKT_DBG_ERR), + "find prof failed, id=%d\n", id); + } + + return (ptr_profile); +} + + +static CLX_ERROR_NO_T +_hal_dawn_pkt_delProfFromAllIntfById( + const UI32_T id) +{ + UI32_T port; + HAL_DAWN_PKT_NETIF_PORT_DB_T *ptr_port_db; + + for (port = 0; port < HAL_DAWN_PKT_MAX_PORT_NUM; port++) + { + ptr_port_db = HAL_DAWN_PKT_GET_PORT_DB(port); + /* Shall we check if the interface is ever created on the port?? */ + /* if (NULL != ptr_port_db->ptr_net_dev) */ + if (1) + { + _hal_dawn_pkt_delProfFromListById(id, &ptr_port_db->ptr_profile_list); + } + } + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_allocProfEntry( + HAL_DAWN_PKT_NETIF_PROFILE_T *ptr_profile) +{ + UI32_T idx; + + for (idx=0; idxid = idx; + return (CLX_E_OK); + } + } + return (CLX_E_TABLE_FULL); +} + +static HAL_DAWN_PKT_NETIF_PROFILE_T * +_hal_dawn_pkt_freeProfEntry( + const UI32_T id) +{ + HAL_DAWN_PKT_NETIF_PROFILE_T *ptr_profile = NULL; + + if (id < HAL_DAWN_PKT_NET_PROFILE_NUM_MAX) + { + ptr_profile = _ptr_hal_dawn_pkt_profile_entry[id]; + _ptr_hal_dawn_pkt_profile_entry[id] = NULL; + } + + return (ptr_profile); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_destroyAllIntf( + const UI32_T unit) +{ + HAL_DAWN_PKT_NETIF_PORT_DB_T *ptr_port_db; + UI32_T port = 0; + + /* Unregister net devices by id, although the "id" is now relavent to "port" we still perform a search */ + for (port = 0; port < HAL_DAWN_PKT_MAX_PORT_NUM; port++) + { + ptr_port_db = HAL_DAWN_PKT_GET_PORT_DB(port); + if (NULL != ptr_port_db->ptr_net_dev) /* valid intf */ + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_INTF, + "u=%u, find intf %s (id=%d) on phy port=%d, destroy done\n", + unit, + ptr_port_db->meta.name, + ptr_port_db->meta.port, + ptr_port_db->meta.port); + + netif_tx_disable(ptr_port_db->ptr_net_dev); + unregister_netdev(ptr_port_db->ptr_net_dev); + free_netdev(ptr_port_db->ptr_net_dev); + + /* Don't need to remove profiles on this port. + * In fact, the profile is binding to "port" not "intf". + */ + /* _hal_dawn_pkt_destroyProfList(ptr_port_db->ptr_profile_list); */ + + osal_memset(ptr_port_db, 0x0, sizeof(HAL_DAWN_PKT_NETIF_PORT_DB_T)); + } + } + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_delProfListOnAllIntf( + const UI32_T unit) +{ + HAL_DAWN_PKT_NETIF_PORT_DB_T *ptr_port_db; + UI32_T port = 0; + HAL_DAWN_PKT_PROFILE_NODE_T *ptr_curr_node, *ptr_next_node; + + /* Unregister net devices by id, although the "id" is now relavent to "port" we still perform a search */ + for (port = 0; port < HAL_DAWN_PKT_MAX_PORT_NUM; port++) + { + ptr_port_db = HAL_DAWN_PKT_GET_PORT_DB(port); + if (NULL != ptr_port_db->ptr_profile_list) /* valid intf */ + { + ptr_curr_node = ptr_port_db->ptr_profile_list; + while (NULL != ptr_curr_node) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_PROFILE, + "u=%u, del prof id=%d on phy port=%d\n", + unit, ptr_curr_node->ptr_profile->id, port); + + ptr_next_node = ptr_curr_node->ptr_next_node; + osal_free(ptr_curr_node); + ptr_curr_node = ptr_next_node; + } + } + } + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_destroyAllProfile( + const UI32_T unit) +{ + HAL_DAWN_PKT_NETIF_PROFILE_T *ptr_profile; + UI32_T prof_id; + + _hal_dawn_pkt_delProfListOnAllIntf(unit); + + for (prof_id=0; prof_idid, + ptr_profile->name, + ptr_profile->priority, + ptr_profile->flags); + osal_free(ptr_profile); + } + } + + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_dawn_pkt_initPktDrv + * PURPOSE: + * To invoke the functions to initialize the control block for each + * PDMA subsystem. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully initialize the control blocks. + * CLX_E_OTHERS -- Initialize the control blocks failed. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_dawn_pkt_initPktDrv( + const UI32_T unit) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + UI32_T channel = 0; + UI32_T flush_intr = 0x0; + UI32_T clear_intr = 0xffffffff; + HAL_DAWN_PKT_DRV_CB_T *ptr_cb = HAL_DAWN_PKT_GET_DRV_CB_PTR(unit); + + /* There's a case that PDMA Tx is on-going when doing chip reset, + * where PDMA may hang and be not programmable since current Tx packet + * stucks due to IOS credit too low. + * Thus, we always reset IOS credit value before progrmming Tx PDMA. + */ + _hal_dawn_pkt_resetIosCreditCfg(unit); + + /* Since the users may kill SDK application without a de-init flow, + * we help to detect if NETIF is ever init before, and perform deinit. + * (Because the users cannot perform Task init bypassing Drv init, this + * check is required only in here) + */ + if (0 != (ptr_cb->init_flag & HAL_DAWN_PKT_INIT_DRV)) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_ERR, + "u=%u, init pkt drv failed, inited\n", unit); + + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_ERR, + "u=%u, stop rx pkt\n", unit); + _hal_dawn_pkt_rxStop(unit); + + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_ERR, + "u=%u, stop all intf\n", unit); + _hal_dawn_pkt_stopAllIntf(unit); + + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_ERR, + "u=%u, deinit pkt task\n", unit); + + hal_dawn_pkt_deinitTask(unit); + + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_ERR, + "u=%u, deinit pkt drv\n", unit); + hal_dawn_pkt_deinitPktDrv(unit); + + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_ERR, + "u=%u, destroy all prof\n", unit); + _hal_dawn_pkt_destroyAllProfile(unit); + + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_ERR, + "u=%u, destroy all intf\n", unit); + _hal_dawn_pkt_destroyAllIntf(unit); + } + + /* [cold-boot] 1. stop DMA channel + * 2. disable/mask/clear the interrupt status. + */ + osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_ERR_INT_EN), + &flush_intr, sizeof(UI32_T)); + + osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_ERR_INT_MASK_SET), + &clear_intr, sizeof(UI32_T)); + + osal_mdc_writePciReg(unit, + HAL_DAWN_PKT_GET_MMIO(HAL_DAWN_PKT_PDMA_ERR_INT_CLR), + &clear_intr, sizeof(UI32_T)); + + for (channel = 0; channel < HAL_DAWN_PKT_TX_CHANNEL_LAST; channel++) + { + _hal_dawn_pkt_stopTxChannelReg(unit, channel); + _hal_dawn_pkt_maskAllTxL2IsrReg(unit, channel); + _hal_dawn_pkt_clearTxL2IsrStatusReg(unit, channel, clear_intr); + } + + for (channel = 0; channel < HAL_DAWN_PKT_RX_CHANNEL_LAST; channel++) + { + _hal_dawn_pkt_stopRxChannelReg(unit, channel); + _hal_dawn_pkt_maskAllRxL2IsrReg(unit, channel); + _hal_dawn_pkt_clearRxL2IsrStatusReg(unit, channel, clear_intr); + } + + rc = _hal_dawn_pkt_initPktCb(unit); + if (CLX_E_OK == rc) + { + rc = _hal_dawn_pkt_initPktTxCb(unit); + } + if (CLX_E_OK == rc) + { + rc = _hal_dawn_pkt_initPktRxCb(unit); + } + if (CLX_E_OK == rc) + { + rc = _hal_dawn_pkt_initL1Isr(unit); + } + if (CLX_E_OK == rc) + { + rc = _hal_dawn_pkt_initL2Isr(unit); + } + + /* Set the flag to record init state */ + ptr_cb->init_flag |= HAL_DAWN_PKT_INIT_DRV; + + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_COMMON, + "u=%u, pkt drv init done, init flag=0x%x\n", unit, ptr_cb->init_flag); + + return (rc); +} + +/* ----------------------------------------------------------------------------------- Init: I/O */ +CLX_ERROR_NO_T +hal_dawn_pkt_getNetDev( + const UI32_T unit, + const UI32_T port, + struct net_device **pptr_net_dev) +{ + *pptr_net_dev = HAL_DAWN_PKT_GET_PORT_NETDEV(port); + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +hal_dawn_pkt_prepareGpd( + const UI32_T unit, + const CLX_ADDR_T phy_addr, + const UI32_T len, + const UI32_T port, + HAL_DAWN_PKT_TX_SW_GPD_T *ptr_sw_gpd) +{ + /* fill up tx_gpd */ + ptr_sw_gpd->tx_gpd.data_buf_addr_hi = CLX_ADDR_64_HI(phy_addr); + ptr_sw_gpd->tx_gpd.data_buf_addr_lo = CLX_ADDR_64_LOW(phy_addr); + ptr_sw_gpd->tx_gpd.data_buf_size = len; + ptr_sw_gpd->tx_gpd.chksum = 0x0; + ptr_sw_gpd->tx_gpd.ipc = 0; /* Raw mode, sent to plane 0 */ + ptr_sw_gpd->tx_gpd.prg = HAL_DAWN_PKT_PRG_PROCESS_GPD; + ptr_sw_gpd->tx_gpd.hwo = HAL_DAWN_PKT_HWO_HW_OWN; + ptr_sw_gpd->tx_gpd.ch = HAL_DAWN_PKT_CH_LAST_GPD; + ptr_sw_gpd->tx_gpd.ioc = HAL_DAWN_PKT_IOC_HAS_INTR; + ptr_sw_gpd->tx_gpd.pkt_len = len; + ptr_sw_gpd->tx_gpd.crcc = HAL_DAWN_PKT_CRCC_SUM_BY_HW; + + /* fill up cpu header */ + ptr_sw_gpd->tx_gpd.itmh_eth.skip_ipp = 1; + ptr_sw_gpd->tx_gpd.itmh_eth.skip_epp = 1; + ptr_sw_gpd->tx_gpd.itmh_eth.color = 0; /* Green */ + ptr_sw_gpd->tx_gpd.itmh_eth.tc = 15; /* Max tc */ + ptr_sw_gpd->tx_gpd.itmh_eth.igr_phy_port = 0; + + ptr_sw_gpd->tx_gpd.pph_l2.mrk_pcp_val = 7; /* Max pcp */ + ptr_sw_gpd->tx_gpd.pph_l2.mrk_pcp_dei_en = 1; + + /* destination index + * 1. to local ETH port + * 2. to remote ETH port + * 3. to remote CPU + */ + ptr_sw_gpd->tx_gpd.itmh_eth.dst_idx = port; + + /* [CL8360] we should set all-1 for the following fields to skip some tm-logic */ + + /* TM header */ + ptr_sw_gpd->tx_gpd.itmh_eth.src_idx = 0x7fff; + ptr_sw_gpd->tx_gpd.itmh_eth.intf_fdid = 0x3fff; + ptr_sw_gpd->tx_gpd.itmh_eth.src_supp_tag = 0x1f; + ptr_sw_gpd->tx_gpd.itmh_eth.nvo3_mgid = 0x6fff; + ptr_sw_gpd->tx_gpd.itmh_eth.nvo3_src_supp_tag_w0 = 0x1; + ptr_sw_gpd->tx_gpd.itmh_eth.nvo3_src_supp_tag_w1 = 0xf; + + /* PP header */ + ptr_sw_gpd->tx_gpd.pph_l2.nvo3_encap_idx = HAL_INVALID_NVO3_ENCAP_IDX; + ptr_sw_gpd->tx_gpd.pph_l2.nvo3_adj_idx = HAL_INVALID_NVO3_ADJ_IDX; + + return (CLX_E_OK); +} + +/* ----------------------------------------------------------------------------------- Init: net_dev_ops */ +static int +_hal_dawn_pkt_net_dev_init( + struct net_device *ptr_net_dev) +{ + return 0; +} + +static int +_hal_dawn_pkt_net_dev_open( + struct net_device *ptr_net_dev) +{ + netif_start_queue(ptr_net_dev); + +#if defined(PERF_EN_TEST) + /* Tx (len, tx_channel, rx_channel, test_skb) */ + perf_test(64, 1, 0, FALSE); + perf_test(64, 2, 0, FALSE); + perf_test(64, 4, 0, FALSE); + + perf_test(1518, 1, 0, FALSE); + perf_test(1518, 2, 0, FALSE); + perf_test(1518, 4, 0, FALSE); + + perf_test(9216, 1, 0, FALSE); + perf_test(9216, 2, 0, FALSE); + perf_test(9216, 4, 0, FALSE); + + /* Rx (len, tx_channel, rx_channel, test_skb) */ + perf_test(64, 0, 1, FALSE); + perf_test(64, 0, 3, FALSE); + perf_test(64, 0, 4, FALSE); + + perf_test(1518, 0, 1, FALSE); + perf_test(1518, 0, 3, FALSE); + perf_test(1518, 0, 4, FALSE); + + perf_test(9216, 0, 1, FALSE); + perf_test(9216, 0, 3, FALSE); + perf_test(9216, 0, 4, FALSE); +#endif + + return 0; +} + +static int +_hal_dawn_pkt_net_dev_stop( + struct net_device *ptr_net_dev) +{ + netif_stop_queue(ptr_net_dev); + return 0; +} + +static int +_hal_dawn_pkt_net_dev_ioctl( + struct net_device *ptr_net_dev, + struct ifreq *ptr_ifreq, + int cmd) +{ + return 0; +} + +static netdev_tx_t +_hal_dawn_pkt_net_dev_tx( + struct sk_buff *ptr_skb, + struct net_device *ptr_net_dev) +{ + struct net_device_priv *ptr_priv = netdev_priv(ptr_net_dev); + HAL_DAWN_PKT_TX_CB_T *ptr_tx_cb; + /* chip meta */ + unsigned int unit; + unsigned int channel = 0; + HAL_DAWN_PKT_TX_SW_GPD_T *ptr_sw_gpd = NULL; + void *ptr_virt_addr = NULL; + CLX_ADDR_T phy_addr = 0x0; + + if (NULL == ptr_priv) + { + /* in case that the netdev has been freed/reset somewhere */ + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_ERR, "get netdev_priv failed\n"); + return -EFAULT; + } + + /* check skb */ + if (NULL == ptr_skb) + { + ptr_priv->stats.tx_errors++; + return -EFAULT; + } + + unit = ptr_priv->unit; + + ptr_tx_cb = HAL_DAWN_PKT_GET_TX_CB_PTR(unit); + /* for warm de-init procedure, if any net intf not destroyed, it is possible + * that kernel still has packets to send causing segmentation fault + */ + if (FALSE == ptr_tx_cb->net_tx_allowed) { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_ERR, "net tx during sdk de-init\n"); + ptr_priv->stats.tx_dropped++; + osal_skb_free(ptr_skb); + return NETDEV_TX_OK; + } + + /* pad to 60-bytes if skb_len < 60, see: eth_skb_pad(skb) */ + if (ptr_skb->len < ETH_ZLEN) + { + skb_pad(ptr_skb, ETH_ZLEN - ptr_skb->len); + skb_set_tail_pointer(ptr_skb, ETH_ZLEN); + ptr_skb->len = ETH_ZLEN; + } + + /* pad 4-bytes for chip-crc */ + skb_pad(ptr_skb, ETH_FCS_LEN); + skb_set_tail_pointer(ptr_skb, ETH_FCS_LEN); + ptr_skb->len += ETH_FCS_LEN; + + /* alloc gpd */ + ptr_sw_gpd = osal_alloc(sizeof(HAL_DAWN_PKT_TX_SW_GPD_T)); + if (NULL == ptr_sw_gpd) + { + ptr_priv->stats.tx_errors++; + osal_skb_free(ptr_skb); + } + else + { + /* map skb to dma */ + ptr_virt_addr = ptr_skb->data; + phy_addr = osal_skb_mapDma(ptr_skb, DMA_TO_DEVICE); + if (0x0 == phy_addr) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_ERR, "u=%u, txch=%u, skb dma map err\n", + unit, channel); + ptr_priv->stats.tx_errors++; + osal_skb_free(ptr_skb); + osal_free(ptr_sw_gpd); + } + else + { + /* trans skb to gpd */ + memset(ptr_sw_gpd, 0x0, sizeof(HAL_DAWN_PKT_TX_SW_GPD_T)); + ptr_sw_gpd->callback = (void *)_hal_dawn_pkt_net_dev_tx_callback; + ptr_sw_gpd->ptr_cookie = (void *)ptr_skb; + ptr_sw_gpd->gpd_num = 1; + ptr_sw_gpd->ptr_next = NULL; + ptr_sw_gpd->channel = channel; + + /* prepare gpd */ + hal_dawn_pkt_prepareGpd(unit, phy_addr, ptr_skb->len, ptr_priv->port, ptr_sw_gpd); + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,6,7) + ptr_net_dev->trans_start = jiffies; +#else + netdev_get_tx_queue(ptr_net_dev, 0)->trans_start = jiffies; +#endif + /* send gpd */ + if (CLX_E_OK == hal_dawn_pkt_sendGpd(unit, channel, ptr_sw_gpd)) + { + ptr_priv->stats.tx_packets++; + ptr_priv->stats.tx_bytes += ptr_skb->len; + } + else + { + ptr_priv->stats.tx_fifo_errors++; /* to record the extreme cases where packets are dropped */ + ptr_priv->stats.tx_dropped++; + + osal_skb_unmapDma(phy_addr, ptr_skb->len, DMA_TO_DEVICE); + osal_skb_free(ptr_skb); + osal_free(ptr_sw_gpd); + } + } + } + + return NETDEV_TX_OK; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,10,0) +static void +_hal_dawn_pkt_net_dev_tx_timeout( + struct net_device *ptr_net_dev, + unsigned int txqueue) +#else +static void +_hal_dawn_pkt_net_dev_tx_timeout( + struct net_device *ptr_net_dev) +#endif +{ + netif_stop_queue(ptr_net_dev); + osal_sleepThread(1000); + netif_wake_queue(ptr_net_dev); +} + +static struct net_device_stats * +_hal_dawn_pkt_net_dev_get_stats( + struct net_device *ptr_net_dev) +{ + struct net_device_priv *ptr_priv = netdev_priv(ptr_net_dev); + + return (&ptr_priv->stats); +} + +static int +_hal_dawn_pkt_net_dev_set_mtu( + struct net_device *ptr_net_dev, + int new_mtu) +{ + if (new_mtu < 64 || new_mtu > 9216) + { + return -EINVAL; + } + ptr_net_dev->mtu = new_mtu; /* This mtu need to be synced to chip's */ + return 0; +} + +static int +_hal_dawn_pkt_net_dev_set_mac( + struct net_device *ptr_net_dev, + void *ptr_mac_addr) +{ + struct sockaddr *ptr_addr = ptr_mac_addr; + + memcpy(ptr_net_dev->dev_addr, ptr_addr->sa_data, ptr_net_dev->addr_len); + return 0; +} + +static void +_hal_dawn_pkt_net_dev_set_rx_mode( + struct net_device *ptr_dev) +{ + if (ptr_dev->flags & IFF_PROMISC) + { + } + else + { + if (ptr_dev->flags & IFF_ALLMULTI) + { + } + else + { + if (netdev_mc_empty(ptr_dev)) + { + return; + } + } + } +} + +static struct net_device_ops _hal_dawn_pkt_net_dev_ops = +{ + .ndo_init = _hal_dawn_pkt_net_dev_init, + .ndo_open = _hal_dawn_pkt_net_dev_open, + .ndo_stop = _hal_dawn_pkt_net_dev_stop, + .ndo_do_ioctl = _hal_dawn_pkt_net_dev_ioctl, + .ndo_start_xmit = _hal_dawn_pkt_net_dev_tx, + .ndo_tx_timeout = _hal_dawn_pkt_net_dev_tx_timeout, + .ndo_get_stats = _hal_dawn_pkt_net_dev_get_stats, + .ndo_change_mtu = _hal_dawn_pkt_net_dev_set_mtu, + .ndo_set_mac_address = _hal_dawn_pkt_net_dev_set_mac, + .ndo_set_rx_mode = _hal_dawn_pkt_net_dev_set_rx_mode, +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0) +static int +_hal_dawn_pkt_net_dev_ethtool_get( + struct net_device *ptr_dev, + struct ethtool_cmd *ptr_cmd) +{ + struct net_device_priv *ptr_priv; + + ptr_cmd->supported = SUPPORTED_1000baseT_Full | SUPPORTED_FIBRE; + ptr_cmd->port = PORT_FIBRE; + ptr_cmd->duplex = DUPLEX_FULL; + + ptr_priv = netdev_priv(ptr_dev); + ethtool_cmd_speed_set(ptr_cmd, ptr_priv->speed); + + return 0; +} +#endif + +static struct ethtool_ops _hal_dawn_pkt_net_dev_ethtool_ops = +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0) + .get_settings = _hal_dawn_pkt_net_dev_ethtool_get, +#endif + .get_link = ethtool_op_get_link, +}; + +static void +_hal_dawn_pkt_setup( + struct net_device *ptr_net_dev) +{ + struct net_device_priv *ptr_priv = netdev_priv(ptr_net_dev); + + /* setup net device */ + ether_setup(ptr_net_dev); + ptr_net_dev->netdev_ops = &_hal_dawn_pkt_net_dev_ops; + ptr_net_dev->ethtool_ops = &_hal_dawn_pkt_net_dev_ethtool_ops; + ptr_net_dev->watchdog_timeo = HAL_DAWN_PKT_TX_TIMEOUT; + ptr_net_dev->mtu = HAL_DAWN_PKT_MAX_ETH_FRAME_SIZE; /* This mtu need to be synced to chip's */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0) + ptr_net_dev->min_mtu = 64; + ptr_net_dev->max_mtu = 65535; +#endif + random_ether_addr(ptr_net_dev->dev_addr); /* Please use the mac-addr of interface. */ + + /* setup private data */ + ptr_priv->ptr_net_dev = ptr_net_dev; + memset(&ptr_priv->stats, 0, sizeof(struct net_device_stats)); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_createIntf( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_NETIF_COOKIE_T *ptr_cookie) +{ + HAL_DAWN_PKT_NETIF_INTF_T net_intf = {0}; + HAL_DAWN_PKT_NETIF_PORT_DB_T *ptr_port_db; + struct net_device *ptr_net_dev = NULL; + struct net_device_priv *ptr_priv = NULL; + CLX_ERROR_NO_T rc = CLX_E_OK; + + /* Lock all Rx tasks to avoid any access to the intf during packet processing */ + /* Only Rx tasks are locked since Tx action is performed under a spinlock protection */ + _hal_dawn_pkt_lockRxChannelAll(unit); + + osal_io_copyFromUser(&net_intf, &ptr_cookie->net_intf, sizeof(HAL_DAWN_PKT_NETIF_INTF_T)); + + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_INTF, "u=%u, create intf name=%s, phy port=%d\n", + unit, net_intf.name, net_intf.port); + + /* To check if the interface with the same name exists in kernel */ + ptr_net_dev = dev_get_by_name(&init_net, net_intf.name); + if (NULL != ptr_net_dev) + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_ERR | HAL_DAWN_PKT_DBG_INTF), + "u=%u, create intf failed, exist same name=%s\n", + unit, net_intf.name); + + dev_put(ptr_net_dev); + +#if defined(HAL_DAWN_PKT_FORCR_REMOVE_DUPLICATE_NETDEV) + ptr_net_dev->operstate = IF_OPER_DOWN; + netif_carrier_off(ptr_net_dev); + netif_tx_disable(ptr_net_dev); + unregister_netdev(ptr_net_dev); + free_netdev(ptr_net_dev); +#endif + _hal_dawn_pkt_unlockRxChannelAll(unit); + return (CLX_E_ENTRY_EXISTS); + } + + /* Bind the net dev and intf meta data to internel port-based array */ + ptr_port_db = HAL_DAWN_PKT_GET_PORT_DB(net_intf.port); + if (ptr_port_db->ptr_net_dev == NULL) + { + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) + ptr_net_dev = alloc_netdev(sizeof(struct net_device_priv), + net_intf.name, NET_NAME_UNKNOWN, _hal_dawn_pkt_setup); +#else + ptr_net_dev = alloc_netdev(sizeof(struct net_device_priv), + net_intf.name, _hal_dawn_pkt_setup); +#endif + memcpy(ptr_net_dev->dev_addr, net_intf.mac, ptr_net_dev->addr_len); + + ptr_priv = netdev_priv(ptr_net_dev); + + /* Port info will be used when packet sent from this netdev */ + ptr_priv->port = net_intf.port; + ptr_priv->id = net_intf.port; + ptr_priv->unit = unit; + + register_netdev(ptr_net_dev); + + netif_carrier_off(ptr_net_dev); + + net_intf.id = net_intf.port; /* Currently, id is 1-to-1 mapped to port */ + osal_memcpy(&ptr_port_db->meta, &net_intf, sizeof(HAL_DAWN_PKT_NETIF_INTF_T)); + + ptr_port_db->ptr_net_dev = ptr_net_dev; + + /* Copy the intf-id to user space */ + osal_io_copyToUser(&ptr_cookie->net_intf, &net_intf, sizeof(HAL_DAWN_PKT_NETIF_INTF_T)); + } + else + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_INTF | HAL_DAWN_PKT_DBG_ERR), + "u=%u, create intf failed, exist on phy port=%d\n", + unit, net_intf.port); + /* The user needs to delete the existing intf binding to the same port */ + rc = CLX_E_ENTRY_EXISTS; + } + + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + _hal_dawn_pkt_unlockRxChannelAll(unit); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_destroyIntf( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_NETIF_COOKIE_T *ptr_cookie) +{ + HAL_DAWN_PKT_NETIF_INTF_T net_intf = {0}; + HAL_DAWN_PKT_NETIF_PORT_DB_T *ptr_port_db; + UI32_T port = 0; + CLX_ERROR_NO_T rc = CLX_E_ENTRY_NOT_FOUND; + + /* Lock all Rx tasks to avoid any access to the intf during packet processing */ + /* Only Rx tasks are locked since Tx action is performed under a spinlock protection */ + _hal_dawn_pkt_lockRxChannelAll(unit); + + osal_io_copyFromUser(&net_intf, &ptr_cookie->net_intf, sizeof(HAL_DAWN_PKT_NETIF_INTF_T)); + + /* Unregister net devices by id, although the "id" is now relavent to "port" we still perform a search */ + for (port = 0; port < HAL_DAWN_PKT_MAX_PORT_NUM; port++) + { + ptr_port_db = HAL_DAWN_PKT_GET_PORT_DB(port); + if (NULL != ptr_port_db->ptr_net_dev) /* valid intf */ + { + if (ptr_port_db->meta.id == net_intf.id) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_INTF, + "u=%u, find intf %s (id=%d) on phy port=%d, destroy done\n", + unit, + ptr_port_db->meta.name, + ptr_port_db->meta.id, + ptr_port_db->meta.port); + + netif_carrier_off(ptr_port_db->ptr_net_dev); + netif_tx_disable(ptr_port_db->ptr_net_dev); + unregister_netdev(ptr_port_db->ptr_net_dev); + free_netdev(ptr_port_db->ptr_net_dev); + + /* Don't need to remove profiles on this port. + * In fact, the profile is binding to "port" not "intf". + */ + /* _hal_dawn_pkt_destroyProfList(ptr_port_db->ptr_profile_list); */ + + osal_memset(ptr_port_db, 0x0, sizeof(HAL_DAWN_PKT_NETIF_PORT_DB_T)); + rc = CLX_E_OK; + break; + } + } + } + + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + _hal_dawn_pkt_unlockRxChannelAll(unit); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_traverseProfList( + UI32_T intf_id, + HAL_DAWN_PKT_PROFILE_NODE_T *ptr_prof_list) +{ + HAL_DAWN_PKT_PROFILE_NODE_T *ptr_curr_node; + + ptr_curr_node = ptr_prof_list; + + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_INTF, "intf id=%d, prof list=", intf_id); + while(NULL != ptr_curr_node) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_INTF, "%s (%d) => ", + ptr_curr_node->ptr_profile->name, + ptr_curr_node->ptr_profile->priority); + ptr_curr_node = ptr_curr_node->ptr_next_node; + } + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_INTF, "null\n"); + return (CLX_E_OK); +} + + +static CLX_ERROR_NO_T +_hal_dawn_pkt_getIntf( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_NETIF_COOKIE_T *ptr_cookie) +{ + HAL_DAWN_PKT_NETIF_INTF_T net_intf = {0}; + HAL_DAWN_PKT_NETIF_PORT_DB_T *ptr_port_db; + UI32_T port = 0; + CLX_ERROR_NO_T rc = CLX_E_ENTRY_NOT_FOUND; + + osal_io_copyFromUser(&net_intf, &ptr_cookie->net_intf, sizeof(HAL_DAWN_PKT_NETIF_INTF_T)); + + for (port = 0; port < HAL_DAWN_PKT_MAX_PORT_NUM; port++) + { + ptr_port_db = HAL_DAWN_PKT_GET_PORT_DB(port); + if (NULL != ptr_port_db->ptr_net_dev) /* valid intf */ + { + if (ptr_port_db->meta.id == net_intf.id) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_INTF, "u=%u, find intf id=%d\n", unit, net_intf.id); + _hal_dawn_pkt_traverseProfList(net_intf.id, ptr_port_db->ptr_profile_list); + osal_io_copyToUser(&ptr_cookie->net_intf, &ptr_port_db->meta, sizeof(HAL_DAWN_PKT_NETIF_INTF_T)); + rc = CLX_E_OK; + break; + } + } + } + + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + return (CLX_E_OK); +} + +static HAL_DAWN_PKT_NETIF_PROFILE_T * +_hal_dawn_pkt_getProfEntry( + const UI32_T id) +{ + HAL_DAWN_PKT_NETIF_PROFILE_T *ptr_profile = NULL; + + if (id < HAL_DAWN_PKT_NET_PROFILE_NUM_MAX) + { + if (NULL != _ptr_hal_dawn_pkt_profile_entry[id]) + { + ptr_profile = _ptr_hal_dawn_pkt_profile_entry[id]; + } + } + + return (ptr_profile); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_createProfile( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_NETIF_COOKIE_T *ptr_cookie) +{ + HAL_DAWN_PKT_NETIF_PROFILE_T *ptr_profile; + HAL_DAWN_PKT_NETIF_PORT_DB_T *ptr_port_db; + CLX_ERROR_NO_T rc; + + /* Lock all Rx tasks to avoid profiles being refered during packet processing */ + /* Need to lock all Rx tasks since packets from all Rx channels do profile lookup */ + _hal_dawn_pkt_lockRxChannelAll(unit); + + ptr_profile = osal_alloc(sizeof(HAL_DAWN_PKT_NETIF_PROFILE_T)); + osal_io_copyFromUser(ptr_profile, &ptr_cookie->net_profile, + sizeof(HAL_DAWN_PKT_NETIF_PROFILE_T)); + + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_PROFILE, + "u=%u, create prof name=%s, priority=%d, flag=0x%x\n", + unit, + ptr_profile->name, + ptr_profile->priority, + ptr_profile->flags); + + /* Save the profile to the profile array and assign the index to ptr_profile->id */ + rc = _hal_dawn_pkt_allocProfEntry(ptr_profile); + if (CLX_E_OK == rc) + { + /* Insert the profile to the corresponding (port) interface */ + if ((ptr_profile->flags & HAL_DAWN_PKT_NETIF_PROFILE_FLAGS_PORT) != 0) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_PROFILE, + "u=%u, bind prof to phy port=%d\n", unit, ptr_profile->port); + ptr_port_db = HAL_DAWN_PKT_GET_PORT_DB(ptr_profile->port); + _hal_dawn_pkt_addProfToList(ptr_profile, &ptr_port_db->ptr_profile_list); + } + else + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_PROFILE, + "u=%u, bind prof to all intf\n", unit); + _hal_dawn_pkt_addProfToAllIntf(ptr_profile); + } + + /* Copy the ptr_profile->id to user space */ + osal_io_copyToUser(&ptr_cookie->net_profile, ptr_profile, sizeof(HAL_DAWN_PKT_NETIF_PROFILE_T)); + } + else + { + HAL_DAWN_PKT_DBG((HAL_DAWN_PKT_DBG_PROFILE | HAL_DAWN_PKT_DBG_ERR), + "u=%u, alloc prof entry failed, tbl full\n", unit); + osal_free(ptr_profile); + } + + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + _hal_dawn_pkt_unlockRxChannelAll(unit); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_destroyProfile( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_NETIF_COOKIE_T *ptr_cookie) +{ + HAL_DAWN_PKT_NETIF_PROFILE_T profile = {0}; + HAL_DAWN_PKT_NETIF_PROFILE_T *ptr_profile; + CLX_ERROR_NO_T rc = CLX_E_OK; + + /* Lock all Rx tasks to avoid profiles being refered during packet processing */ + /* Need to lock all Rx tasks since packets from all Rx channels do profile lookup */ + _hal_dawn_pkt_lockRxChannelAll(unit); + + osal_io_copyFromUser(&profile, &ptr_cookie->net_profile, + sizeof(HAL_DAWN_PKT_NETIF_PROFILE_T)); + + /* Remove the profile from corresponding interface (port) */ + _hal_dawn_pkt_delProfFromAllIntfById(profile.id); + + ptr_profile = _hal_dawn_pkt_freeProfEntry(profile.id); + if (NULL != ptr_profile) + { + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_PROFILE, + "u=%u, destroy prof id=%d, name=%s, priority=%d, flag=0x%x\n", + unit, + ptr_profile->id, + ptr_profile->name, + ptr_profile->priority, + ptr_profile->flags); + osal_free(ptr_profile); + } + + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + _hal_dawn_pkt_unlockRxChannelAll(unit); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_getProfile( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_NETIF_COOKIE_T *ptr_cookie) +{ + HAL_DAWN_PKT_NETIF_PROFILE_T profile = {0}; + HAL_DAWN_PKT_NETIF_PROFILE_T *ptr_profile; + CLX_ERROR_NO_T rc = CLX_E_OK; + + osal_io_copyFromUser(&profile, &ptr_cookie->net_profile, sizeof(HAL_DAWN_PKT_NETIF_PROFILE_T)); + + ptr_profile = _hal_dawn_pkt_getProfEntry(profile.id); + if (NULL != ptr_profile) + { + osal_io_copyToUser(&ptr_cookie->net_profile, ptr_profile, sizeof(HAL_DAWN_PKT_NETIF_PROFILE_T)); + } + else + { + rc = CLX_E_ENTRY_NOT_FOUND; + } + + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_getIntfCnt( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_NETIF_COOKIE_T *ptr_cookie) +{ + HAL_DAWN_PKT_NETIF_INTF_T net_intf = {0}; + HAL_DAWN_PKT_NETIF_INTF_CNT_T intf_cnt = {0}; + HAL_DAWN_PKT_NETIF_PORT_DB_T *ptr_port_db; + struct net_device_priv *ptr_priv; + UI32_T port = 0; + CLX_ERROR_NO_T rc = CLX_E_ENTRY_NOT_FOUND; + + osal_io_copyFromUser(&net_intf, &ptr_cookie->net_intf, sizeof(HAL_DAWN_PKT_NETIF_INTF_T)); + + for (port = 0; port < HAL_DAWN_PKT_MAX_PORT_NUM; port++) + { + ptr_port_db = HAL_DAWN_PKT_GET_PORT_DB(port); + if (NULL != ptr_port_db->ptr_net_dev) /* valid intf */ + { + if (ptr_port_db->meta.id == net_intf.id) + { + ptr_priv = netdev_priv(ptr_port_db->ptr_net_dev); + intf_cnt.rx_pkt = ptr_priv->stats.rx_packets; + intf_cnt.tx_pkt = ptr_priv->stats.tx_packets; + intf_cnt.tx_error = ptr_priv->stats.tx_errors; + intf_cnt.tx_queue_full = ptr_priv->stats.tx_fifo_errors; + + rc = CLX_E_OK; + break; + } + } + } + + osal_io_copyToUser(&ptr_cookie->cnt, &intf_cnt, sizeof(HAL_DAWN_PKT_NETIF_INTF_CNT_T)); + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_dawn_pkt_clearIntfCnt( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_NETIF_COOKIE_T *ptr_cookie) +{ + HAL_DAWN_PKT_NETIF_INTF_T net_intf = {0}; + HAL_DAWN_PKT_NETIF_PORT_DB_T *ptr_port_db; + struct net_device_priv *ptr_priv; + UI32_T port = 0; + CLX_ERROR_NO_T rc = CLX_E_ENTRY_NOT_FOUND; + + osal_io_copyFromUser(&net_intf, &ptr_cookie->net_intf, sizeof(HAL_DAWN_PKT_NETIF_INTF_T)); + + for (port = 0; port < HAL_DAWN_PKT_MAX_PORT_NUM; port++) + { + ptr_port_db = HAL_DAWN_PKT_GET_PORT_DB(port); + if (NULL != ptr_port_db->ptr_net_dev) /* valid intf */ + { + if (ptr_port_db->meta.id == net_intf.id) + { + ptr_priv = netdev_priv(ptr_port_db->ptr_net_dev); + ptr_priv->stats.rx_packets = 0; + ptr_priv->stats.tx_packets = 0; + ptr_priv->stats.tx_errors = 0; + ptr_priv->stats.tx_fifo_errors = 0; + + rc = CLX_E_OK; + break; + } + } + } + + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + return (CLX_E_OK); +} + +/* ----------------------------------------------------------------------------------- Init: dev_ops */ +static void +_hal_dawn_pkt_dev_tx_callback( + const UI32_T unit, + HAL_DAWN_PKT_TX_SW_GPD_T *ptr_sw_gpd, + HAL_DAWN_PKT_TX_SW_GPD_T *ptr_sw_gpd_usr) +{ + UI32_T channel = ptr_sw_gpd->channel; + HAL_DAWN_PKT_TX_CB_T *ptr_tx_cb = HAL_DAWN_PKT_GET_TX_CB_PTR(unit); + + while (0 != _hal_dawn_pkt_enQueue(&ptr_tx_cb->sw_queue, ptr_sw_gpd)) + { + ptr_tx_cb->cnt.channel[channel].enque_retry++; + HAL_DAWN_PKT_TX_ENQUE_RETRY_SLEEP(); + } + ptr_tx_cb->cnt.channel[channel].enque_ok++; + + osal_triggerEvent(&ptr_tx_cb->sync_sema); + ptr_tx_cb->cnt.channel[channel].trig_event++; +} + +ssize_t +hal_dawn_pkt_dev_tx( + struct file *file, + const char __user *buf, + size_t count, + loff_t *pos) +{ + int ret = 0; + int idx = 0; + unsigned int unit = 0; + unsigned int channel = 0; + HAL_DAWN_PKT_IOCTL_TX_COOKIE_T tx_cookie; + HAL_DAWN_PKT_IOCTL_TX_GPD_T ioctl_gpd; + HAL_DAWN_PKT_TX_SW_GPD_T *ptr_sw_gpd_knl = NULL; + HAL_DAWN_PKT_TX_SW_GPD_T *ptr_first_sw_gpd_knl = NULL; + + /* copy the tx-cookie */ + osal_io_copyFromUser(&tx_cookie, (void *)buf, sizeof(HAL_DAWN_PKT_IOCTL_TX_COOKIE_T)); + + unit = tx_cookie.unit; + channel = tx_cookie.channel; + + ptr_sw_gpd_knl = osal_alloc(sizeof(HAL_DAWN_PKT_TX_SW_GPD_T)); + ptr_first_sw_gpd_knl = ptr_sw_gpd_knl; + + /* create SW GPD based on the content of each IOCTL GPD */ + while (1) + { + osal_io_copyFromUser(&ioctl_gpd, + ((void *)((CLX_HUGE_T)tx_cookie.ioctl_gpd_addr)) + +idx*sizeof(HAL_DAWN_PKT_IOCTL_TX_GPD_T), + sizeof(HAL_DAWN_PKT_IOCTL_TX_GPD_T)); + + ptr_sw_gpd_knl->channel = ioctl_gpd.channel; + ptr_sw_gpd_knl->gpd_num = ioctl_gpd.gpd_num; + ptr_sw_gpd_knl->ptr_cookie = (void *)ioctl_gpd.cookie; + + /* directly copy user's HW GPD */ + osal_io_copyFromUser(&ptr_sw_gpd_knl->tx_gpd, + (void *)((CLX_HUGE_T)ioctl_gpd.hw_gpd_addr), + sizeof(HAL_DAWN_PKT_TX_GPD_T)); + + /* replace the callback */ + ptr_sw_gpd_knl->callback = (void *)_hal_dawn_pkt_dev_tx_callback; + + /* save the first SW GPD address from userspace since + * we have replaced the original callback + */ + ptr_sw_gpd_knl->ptr_cookie = (void *)ioctl_gpd.sw_gpd_addr; + + if (HAL_DAWN_PKT_CH_LAST_GPD == ptr_sw_gpd_knl->tx_gpd.ch) + { + ptr_sw_gpd_knl->ptr_next = NULL; + break; + } + else + { + ptr_sw_gpd_knl->ptr_next = (HAL_DAWN_PKT_TX_SW_GPD_T *)osal_alloc( + sizeof(HAL_DAWN_PKT_TX_SW_GPD_T)); + ptr_sw_gpd_knl = ptr_sw_gpd_knl->ptr_next; + idx++; + } + } + + ret = hal_dawn_pkt_sendGpd(unit, channel, ptr_first_sw_gpd_knl); + if (CLX_E_OK != ret) + { + _hal_dawn_pkt_freeTxGpdList(unit, ptr_first_sw_gpd_knl); + } + + /* return 0 if success */ + return (ret); +} + +long +hal_dawn_pkt_dev_ioctl( + struct file *filp, + unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + + /* cmd */ + HAL_DAWN_PKT_IOCTL_CMD_T *ptr_cmd = (HAL_DAWN_PKT_IOCTL_CMD_T *)&cmd; + unsigned int unit = ptr_cmd->field.unit; + HAL_DAWN_PKT_IOCTL_TYPE_T type = ptr_cmd->field.type; + + HAL_DAWN_PKT_DBG(HAL_DAWN_PKT_DBG_COMMON, "u=%u, ioctl type=%u, cmd=%u\n", + unit, type, cmd); + + switch (type) + { + /* network interface */ + case HAL_DAWN_PKT_IOCTL_TYPE_CREATE_INTF: + ret = _hal_dawn_pkt_createIntf(unit, (HAL_DAWN_PKT_IOCTL_NETIF_COOKIE_T *)arg); + break; + + case HAL_DAWN_PKT_IOCTL_TYPE_DESTROY_INTF: + ret = _hal_dawn_pkt_destroyIntf(unit, (HAL_DAWN_PKT_IOCTL_NETIF_COOKIE_T *)arg); + break; + + case HAL_DAWN_PKT_IOCTL_TYPE_GET_INTF: + ret = _hal_dawn_pkt_getIntf(unit, (HAL_DAWN_PKT_IOCTL_NETIF_COOKIE_T *)arg); + break; + + case HAL_DAWN_PKT_IOCTL_TYPE_CREATE_PROFILE: + ret = _hal_dawn_pkt_createProfile(unit, (HAL_DAWN_PKT_IOCTL_NETIF_COOKIE_T *)arg); + break; + + case HAL_DAWN_PKT_IOCTL_TYPE_DESTROY_PROFILE: + ret = _hal_dawn_pkt_destroyProfile(unit, (HAL_DAWN_PKT_IOCTL_NETIF_COOKIE_T *)arg); + break; + + case HAL_DAWN_PKT_IOCTL_TYPE_GET_PROFILE: + ret = _hal_dawn_pkt_getProfile(unit, (HAL_DAWN_PKT_IOCTL_NETIF_COOKIE_T *)arg); + break; + + case HAL_DAWN_PKT_IOCTL_TYPE_GET_INTF_CNT: + ret = _hal_dawn_pkt_getIntfCnt(unit, (HAL_DAWN_PKT_IOCTL_NETIF_COOKIE_T *)arg); + break; + + case HAL_DAWN_PKT_IOCTL_TYPE_CLEAR_INTF_CNT: + ret = _hal_dawn_pkt_clearIntfCnt(unit, (HAL_DAWN_PKT_IOCTL_NETIF_COOKIE_T *)arg); + break; + + /* driver */ + case HAL_DAWN_PKT_IOCTL_TYPE_WAIT_RX_FREE: + ret = _hal_dawn_pkt_schedRxDeQueue(unit, (HAL_DAWN_PKT_IOCTL_RX_COOKIE_T *)arg); + break; + + case HAL_DAWN_PKT_IOCTL_TYPE_WAIT_TX_FREE: + ret = _hal_dawn_pkt_strictTxDeQueue(unit, (HAL_DAWN_PKT_IOCTL_TX_COOKIE_T *)arg); + break; + + case HAL_DAWN_PKT_IOCTL_TYPE_SET_RX_CFG: + ret = hal_dawn_pkt_setRxKnlConfig(unit, (HAL_DAWN_PKT_IOCTL_RX_COOKIE_T *)arg); + break; + + case HAL_DAWN_PKT_IOCTL_TYPE_GET_RX_CFG: + ret = hal_dawn_pkt_getRxKnlConfig(unit, (HAL_DAWN_PKT_IOCTL_RX_COOKIE_T *)arg); + break; + + case HAL_DAWN_PKT_IOCTL_TYPE_DEINIT_TASK: + ret = hal_dawn_pkt_deinitTask(unit); + break; + + case HAL_DAWN_PKT_IOCTL_TYPE_DEINIT_DRV: + ret = hal_dawn_pkt_deinitPktDrv(unit); + break; + + case HAL_DAWN_PKT_IOCTL_TYPE_INIT_TASK: + ret = hal_dawn_pkt_initTask(unit); + break; + + case HAL_DAWN_PKT_IOCTL_TYPE_INIT_DRV: + ret = hal_dawn_pkt_initPktDrv(unit); + break; + + /* counter */ + case HAL_DAWN_PKT_IOCTL_TYPE_GET_TX_CNT: + ret = hal_dawn_pkt_getTxKnlCnt(unit, (HAL_DAWN_PKT_IOCTL_CH_CNT_COOKIE_T *)arg); + break; + + case HAL_DAWN_PKT_IOCTL_TYPE_GET_RX_CNT: + ret = hal_dawn_pkt_getRxKnlCnt(unit, (HAL_DAWN_PKT_IOCTL_CH_CNT_COOKIE_T *)arg); + break; + + case HAL_DAWN_PKT_IOCTL_TYPE_CLEAR_TX_CNT: + ret = hal_dawn_pkt_clearTxKnlCnt(unit, (HAL_DAWN_PKT_IOCTL_TX_COOKIE_T *)arg); + break; + + case HAL_DAWN_PKT_IOCTL_TYPE_CLEAR_RX_CNT: + ret = hal_dawn_pkt_clearRxKnlCnt(unit, (HAL_DAWN_PKT_IOCTL_RX_COOKIE_T *)arg); + break; + + case HAL_DAWN_PKT_IOCTL_TYPE_SET_PORT_ATTR: + ret = hal_dawn_pkt_setPortAttr(unit, (HAL_DAWN_PKT_IOCTL_PORT_COOKIE_T *)arg); + break; + +#if defined(NETIF_EN_NETLINK) + case HAL_DAWN_PKT_IOCTL_TYPE_NL_SET_INTF_PROPERTY: + ret = _hal_dawn_pkt_setIntfProperty(unit, (HAL_DAWN_PKT_NL_IOCTL_COOKIE_T *)arg); + break; + case HAL_DAWN_PKT_IOCTL_TYPE_NL_GET_INTF_PROPERTY: + ret = _hal_dawn_pkt_getIntfProperty(unit, (HAL_DAWN_PKT_NL_IOCTL_COOKIE_T *)arg); + break; + case HAL_DAWN_PKT_IOCTL_TYPE_NL_CREATE_NETLINK: + ret = _hal_dawn_pkt_createNetlink(unit, (HAL_DAWN_PKT_NL_IOCTL_COOKIE_T *)arg); + break; + case HAL_DAWN_PKT_IOCTL_TYPE_NL_DESTROY_NETLINK: + ret = _hal_dawn_pkt_destroyNetlink(unit, (HAL_DAWN_PKT_NL_IOCTL_COOKIE_T *)arg); + break; + case HAL_DAWN_PKT_IOCTL_TYPE_NL_GET_NETLINK: + ret = _hal_dawn_pkt_getNetlink(unit, (HAL_DAWN_PKT_NL_IOCTL_COOKIE_T *)arg); + break; +#endif + + default: + ret = -1; + break; + } + + return (ret); +} + +/* ----------------------------------------------------------------------------------- Init/Deinit */ +CLX_ERROR_NO_T +hal_dawn_pkt_init( + const UI32_T unit) +{ + /* Init Thread */ + osal_init(); + + /* Reset all database*/ + osal_memset(_hal_dawn_pkt_port_db, 0x0, + (HAL_DAWN_PKT_MAX_PORT_NUM * sizeof(HAL_DAWN_PKT_NETIF_PORT_DB_T))); + osal_memset(_hal_dawn_pkt_rx_cb, 0x0, + CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM*sizeof(HAL_DAWN_PKT_RX_CB_T)); + osal_memset(_hal_dawn_pkt_tx_cb, 0x0, + CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM*sizeof(HAL_DAWN_PKT_TX_CB_T)); + osal_memset(_hal_dawn_pkt_drv_cb, 0x0, + CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM*sizeof(HAL_DAWN_PKT_DRV_CB_T)); + +#if defined(NETIF_EN_NETLINK) + netif_nl_init(); +#endif + + return (0); +} + +CLX_ERROR_NO_T +hal_dawn_pkt_exit( + const UI32_T unit) +{ + /* 1st. Stop all netdev (if any) to prevent kernel from Tx new packets */ + _hal_dawn_pkt_stopAllIntf(unit); + + /* 2nd. Stop Rx HW DMA and free all the DMA buffer hooked on the ring */ + _hal_dawn_pkt_rxStop(unit); + + /* 3rd. Need to wait Rx done task process all the availavle packets on GPD ring */ +#define HAL_DAWN_PKT_MODULE_EXIT_HOLD_TIME_US (1000000) + osal_sleepThread(HAL_DAWN_PKT_MODULE_EXIT_HOLD_TIME_US); + + /* 4th. Stop all the internal tasks (if any) */ + hal_dawn_pkt_deinitTask(unit); + + /* 5th. Deinit pkt driver for common database/interrupt source (if required) */ + hal_dawn_pkt_deinitPktDrv(unit); + + /* 6th. Clean up those intf/profiles not been destroyed */ + _hal_dawn_pkt_destroyAllProfile(unit); + _hal_dawn_pkt_destroyAllIntf(unit); + + osal_deinit(); + + return (0); +} diff --git a/platform/clounix/clounix-modules/modules/src/clx_netif/hal_lightning_pkt_knl.c b/platform/clounix/clounix-modules/modules/src/clx_netif/hal_lightning_pkt_knl.c new file mode 100755 index 000000000000..0a801f08b383 --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/clx_netif/hal_lightning_pkt_knl.c @@ -0,0 +1,6600 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: hal_lightning_pkt_knl.c + * PURPOSE: + * To provide Linux kernel for PDMA TX/RX control. + * + * NOTES: + * + */ + +/***************************************************************************** + * INCLUDE FILE DECLARATIONS + ***************************************************************************** + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* netif */ +#include +#include +#include + +#include + +/* clx_sdk */ +#include +#include + +/***************************************************************************** + * CHIP DEPENDENT VARIABLES + ***************************************************************************** + */ +/* Interrupt */ +#define HAL_LIGHTNING_PKT_ERR_REG(__unit__) (_hal_lightning_pkt_intr_vec[0].intr_reg) +#define HAL_LIGHTNING_PKT_TCH_REG(__unit__, __channel__) (_hal_lightning_pkt_intr_vec[1 + (__channel__)].intr_reg) +#define HAL_LIGHTNING_PKT_RCH_REG(__unit__, __channel__) (_hal_lightning_pkt_intr_vec[5 + (__channel__)].intr_reg) + +#define HAL_LIGHTNING_PKT_ERR_EVENT(__unit__) (&_hal_lightning_pkt_intr_vec[0].intr_event) +#define HAL_LIGHTNING_PKT_TCH_EVENT(__unit__, __channel__) (&_hal_lightning_pkt_intr_vec[1 + (__channel__)].intr_event) +#define HAL_LIGHTNING_PKT_RCH_EVENT(__unit__, __channel__) (&_hal_lightning_pkt_intr_vec[5 + (__channel__)].intr_event) + +#define HAL_LIGHTNING_PKT_ERR_CNT(__unit__) (_hal_lightning_pkt_intr_vec[0].intr_cnt) +#define HAL_LIGHTNING_PKT_TCH_CNT(__unit__, __channel__) (_hal_lightning_pkt_intr_vec[1 + (__channel__)].intr_cnt) +#define HAL_LIGHTNING_PKT_RCH_CNT(__unit__, __channel__) (_hal_lightning_pkt_intr_vec[5 + (__channel__)].intr_cnt) + + +/* This flag value will be specified when user inserts kernel module. */ +#define HAL_LIGHTNING_PKT_DBG_ERR (0x1UL << 0) +#define HAL_LIGHTNING_PKT_DBG_TX (0x1UL << 1) +#define HAL_LIGHTNING_PKT_DBG_RX (0x1UL << 2) +#define HAL_LIGHTNING_PKT_DBG_INTF (0x1UL << 3) +#define HAL_LIGHTNING_PKT_DBG_PROFILE (0x1UL << 4) +#define HAL_LIGHTNING_PKT_DBG_COMMON (0x1UL << 5) +#define HAL_LIGHTNING_PKT_DBG_NETLINK (0x1UL << 6) + +extern UI32_T ext_dbg_flag; + +#define HAL_LIGHTNING_PKT_DBG(__flag__, ...) do \ +{ \ + if (0 != ((__flag__) & (ext_dbg_flag))) \ + { \ + osal_printf(__VA_ARGS__); \ + } \ +}while (0) + +typedef struct +{ + UI32_T intr_reg; + CLX_SEMAPHORE_ID_T intr_event; + UI32_T intr_cnt; + +} HAL_LIGHTNING_PKT_INTR_VEC_T; + +typedef struct HAL_LIGHTNING_PKT_PROFILE_NODE_S +{ + HAL_LIGHTNING_PKT_NETIF_PROFILE_T *ptr_profile; + struct HAL_LIGHTNING_PKT_PROFILE_NODE_S *ptr_next_node; + +} HAL_LIGHTNING_PKT_PROFILE_NODE_T; + +typedef struct +{ + HAL_LIGHTNING_PKT_NETIF_INTF_T meta; + struct net_device *ptr_net_dev; + HAL_LIGHTNING_PKT_PROFILE_NODE_T *ptr_profile_list; /* the profiles binding to this interface */ + +} HAL_LIGHTNING_PKT_NETIF_PORT_DB_T; + + +static HAL_LIGHTNING_PKT_INTR_VEC_T _hal_lightning_pkt_intr_vec[] = +{ + { /* 0: PDMA_ERR */ 1UL << 0, 0x0, 0 }, + { /* 1: TX_CH0 */ 1UL << 28, 0x0, 0 }, + { /* 2: TX_CH1 */ 1UL << 29, 0x0, 0 }, + { /* 3: TX_CH2 */ 1UL << 30, 0x0, 0 }, + { /* 4: TX_CH3 */ 1UL << 31, 0x0, 0 }, + { /* 5: RX_CH0 */ 1UL << 24, 0x0, 0 }, + { /* 6: RX_CH1 */ 1UL << 25, 0x0, 0 }, + { /* 7: RX_CH2 */ 1UL << 26, 0x0, 0 }, + { /* 8: RX_CH3 */ 1UL << 27, 0x0, 0 }, +}; + +/***************************************************************************** + * NAMING CONSTANT DECLARATIONS + ***************************************************************************** + */ +/* Sleep Time Definitions */ +#define HAL_LIGHTNING_PKT_TX_DEQUE_SLEEP() osal_sleepThread(1000) /* us */ +#define HAL_LIGHTNING_PKT_RX_DEQUE_SLEEP() osal_sleepThread(1000) /* us */ +#define HAL_LIGHTNING_PKT_TX_ENQUE_RETRY_SLEEP() osal_sleepThread(1000) /* us */ +#define HAL_LIGHTNING_PKT_RX_ENQUE_RETRY_SLEEP() osal_sleepThread(1000) /* us */ +#define HAL_LIGHTNING_PKT_ALLOC_MEM_RETRY_SLEEP() osal_sleepThread(1000) /* us */ + +/* Network Device Definitions */ +/* In case that the watchdog alarm during warm-boot if intf isn't killed */ +#define HAL_LIGHTNING_PKT_TX_TIMEOUT (30*HZ) +#define HAL_LIGHTNING_PKT_MAX_ETH_FRAME_SIZE (HAL_LIGHTNING_PKT_RX_MAX_LEN) +#define HAL_LIGHTNING_PKT_MAX_PORT_NUM (HAL_LIGHTNING_PORT_NUM + 1) /* CPU port */ + +#define HAL_LIGHTNING_PKT_NET_PROFILE_NUM_MAX (256) + +static HAL_LIGHTNING_PKT_NETIF_PROFILE_T *_ptr_hal_lightning_pkt_profile_entry[HAL_LIGHTNING_PKT_NET_PROFILE_NUM_MAX] = {0}; +static HAL_LIGHTNING_PKT_NETIF_PORT_DB_T _hal_lightning_pkt_port_db[HAL_LIGHTNING_PKT_MAX_PORT_NUM]; + +/***************************************************************************** + * MACRO VLAUE DECLARATIONS + ***************************************************************************** + */ + +/***************************************************************************** + * MACRO FUNCTION DECLARATIONS + ***************************************************************************** + */ +/*---------------------------------------------------------------------------*/ +#define HAL_LIGHTNING_PKT_GET_DRV_CB_PTR(unit) (&_hal_lightning_pkt_drv_cb[unit]) +/*---------------------------------------------------------------------------*/ +#define HAL_LIGHTNING_PKT_GET_TX_CB_PTR(unit) (&_hal_lightning_pkt_tx_cb[unit]) +#define HAL_LIGHTNING_PKT_GET_TX_PDMA_PTR(unit, channel) (&_hal_lightning_pkt_tx_cb[unit].pdma[channel]) +#define HAL_LIGHTNING_PKT_GET_TX_GPD_PTR(unit, channel, gpd) (&_hal_lightning_pkt_tx_cb[unit].pdma[channel].ptr_gpd_align_start_addr[gpd]) +/*---------------------------------------------------------------------------*/ +#define HAL_LIGHTNING_PKT_GET_RX_CB_PTR(unit) (&_hal_lightning_pkt_rx_cb[unit]) +#define HAL_LIGHTNING_PKT_GET_RX_PDMA_PTR(unit, channel) (&_hal_lightning_pkt_rx_cb[unit].pdma[channel]) +#define HAL_LIGHTNING_PKT_GET_RX_GPD_PTR(unit, channel, gpd) (&_hal_lightning_pkt_rx_cb[unit].pdma[channel].ptr_gpd_align_start_addr[gpd]) +/*---------------------------------------------------------------------------*/ +#define HAL_LIGHTNING_PKT_GET_PORT_DB(port) (&_hal_lightning_pkt_port_db[port]) +#define HAL_LIGHTNING_PKT_GET_PORT_PROFILE_LIST(port) (_hal_lightning_pkt_port_db[port].ptr_profile_list) +#define HAL_LIGHTNING_PKT_GET_PORT_NETDEV(port) _hal_lightning_pkt_port_db[port].ptr_net_dev + +/***************************************************************************** + * DATA TYPE DECLARATIONS + ***************************************************************************** + */ +/* ----------------------------------------------------------------------------------- General structure */ +typedef struct +{ + UI32_T unit; + UI32_T channel; + +} HAL_LIGHTNING_PKT_ISR_COOKIE_T; + +typedef struct +{ + CLX_HUGE_T que_id; + CLX_SEMAPHORE_ID_T sema; + UI32_T len; /* Software CPU queue maximum length. */ + UI32_T weight; /* The weight for thread de-queue algorithm. */ + +} HAL_LIGHTNING_PKT_SW_QUEUE_T; + +typedef struct +{ + /* handleErrorTask */ + CLX_THREAD_ID_T err_task_id; + + /* INTR dispatcher */ + CLX_ISRLOCK_ID_T intr_lock; + UI32_T intr_bitmap; + +#define HAL_LIGHTNING_PKT_INIT_DRV (1UL << 0) +#define HAL_LIGHTNING_PKT_INIT_TASK (1UL << 1) +#define HAL_LIGHTNING_PKT_INIT_INTR (1UL << 2) +#define HAL_LIGHTNING_PKT_INIT_RX_START (1UL << 3) + /* a bitmap to record the init status */ + UI32_T init_flag; + +} HAL_LIGHTNING_PKT_DRV_CB_T; + +/* ----------------------------------------------------------------------------------- TX structure */ +typedef struct +{ + /* CLX_SEMAPHORE_ID_T sema; */ + + /* since the Tx GPD ring may be accessed by multiple process including + * ndo_start_xmit (SW IRQ), it must be protected with an ISRLOCK + * instead of the original semaphore + */ + CLX_ISRLOCK_ID_T ring_lock; + + UI32_T used_idx; /* SW send index = LAMP simulate the Tx HW index */ + UI32_T free_idx; /* SW free index */ + UI32_T used_gpd_num; + UI32_T free_gpd_num; + UI32_T gpd_num; + + HAL_LIGHTNING_PKT_TX_GPD_T *ptr_gpd_start_addr; + HAL_LIGHTNING_PKT_TX_GPD_T *ptr_gpd_align_start_addr; + BOOL_T err_flag; + + /* ASYNC */ + HAL_LIGHTNING_PKT_TX_SW_GPD_T **pptr_sw_gpd_ring; + HAL_LIGHTNING_PKT_TX_SW_GPD_T **pptr_sw_gpd_bulk; /* temporary store packets to be enque */ + + /* SYNC_INTR */ + CLX_SEMAPHORE_ID_T sync_intr_sema; + +} HAL_LIGHTNING_PKT_TX_PDMA_T; + +typedef struct +{ + HAL_LIGHTNING_PKT_TX_WAIT_T wait_mode; + HAL_LIGHTNING_PKT_TX_PDMA_T pdma[HAL_LIGHTNING_PKT_TX_CHANNEL_LAST]; + HAL_LIGHTNING_PKT_TX_CNT_T cnt; + + /* handleTxDoneTask */ + CLX_THREAD_ID_T isr_task_id[HAL_LIGHTNING_PKT_TX_CHANNEL_LAST]; + HAL_LIGHTNING_PKT_ISR_COOKIE_T isr_task_cookie[HAL_LIGHTNING_PKT_TX_CHANNEL_LAST]; + + /* txTask */ + HAL_LIGHTNING_PKT_SW_QUEUE_T sw_queue; + CLX_SEMAPHORE_ID_T sync_sema; + CLX_THREAD_ID_T task_id; + BOOL_T running; /* TRUE when Init txTask + * FALSE when Destroy txTask + */ + /* to block net intf Tx in driver level since netif_tx_disable() + * cannot always prevent intf from Tx in time + */ + BOOL_T net_tx_allowed; +} HAL_LIGHTNING_PKT_TX_CB_T; + +/* ----------------------------------------------------------------------------------- RX structure */ +typedef struct +{ + CLX_SEMAPHORE_ID_T sema; + UI32_T cur_idx; /* SW free index */ + UI32_T gpd_num; + + HAL_LIGHTNING_PKT_RX_GPD_T *ptr_gpd_start_addr; + HAL_LIGHTNING_PKT_RX_GPD_T *ptr_gpd_align_start_addr; + BOOL_T err_flag; + struct sk_buff **pptr_skb_ring; +} HAL_LIGHTNING_PKT_RX_PDMA_T; + +typedef struct +{ + /* Rx system configuration */ + UI32_T buf_len; + + HAL_LIGHTNING_PKT_RX_SCHED_T sched_mode; + HAL_LIGHTNING_PKT_RX_PDMA_T pdma[HAL_LIGHTNING_PKT_RX_CHANNEL_LAST]; + HAL_LIGHTNING_PKT_RX_CNT_T cnt; + + /* handleRxDoneTask */ + CLX_THREAD_ID_T isr_task_id[HAL_LIGHTNING_PKT_RX_CHANNEL_LAST]; + HAL_LIGHTNING_PKT_ISR_COOKIE_T isr_task_cookie[HAL_LIGHTNING_PKT_RX_CHANNEL_LAST]; + + /* rxTask */ + HAL_LIGHTNING_PKT_SW_QUEUE_T sw_queue[HAL_LIGHTNING_PKT_RX_QUEUE_NUM]; + UI32_T deque_idx; + CLX_SEMAPHORE_ID_T sync_sema; + CLX_THREAD_ID_T task_id; + CLX_SEMAPHORE_ID_T deinit_sema; /* To sync-up the Rx-stop and thread flush queues */ + BOOL_T running; /* TRUE when rxStart + * FALSE when rxStop + */ + +} HAL_LIGHTNING_PKT_RX_CB_T; + +/* ----------------------------------------------------------------------------------- Network Device */ +struct net_device_priv +{ + struct net_device *ptr_net_dev; + struct net_device_stats stats; + UI32_T unit; + UI32_T id; + UI32_T port; + UI16_T vlan; + UI32_T speed; +}; + +typedef enum +{ + HAL_LIGHTNING_PKT_DEST_NETDEV = 0, + HAL_LIGHTNING_PKT_DEST_SDK, +#if defined(NETIF_EN_NETLINK) + HAL_LIGHTNING_PKT_DEST_NETLINK, +#endif + HAL_LIGHTNING_PKT_DEST_DROP, + HAL_LIGHTNING_PKT_DEST_LAST +} HAL_LIGHTNING_PKT_DEST_T; + +/***************************************************************************** + * GLOBAL VARIABLE DECLARATIONS + ***************************************************************************** + */ + +/***************************************************************************** + * STATIC VARIABLE DECLARATIONS + ***************************************************************************** + */ +/*---------------------------------------------------------------------------*/ +static HAL_LIGHTNING_PKT_DRV_CB_T _hal_lightning_pkt_drv_cb[CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM]; +static HAL_LIGHTNING_PKT_TX_CB_T _hal_lightning_pkt_tx_cb[CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM]; +static HAL_LIGHTNING_PKT_RX_CB_T _hal_lightning_pkt_rx_cb[CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM]; +/*---------------------------------------------------------------------------*/ + +/***************************************************************************** + * LOCAL SUBPROGRAM DECLARATIONS + ***************************************************************************** + */ +/* ----------------------------------------------------------------------------------- Interrupt */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_enableIntr( + const UI32_T unit, + const UI32_T intr_bitmap) +{ + HAL_LIGHTNING_PKT_DRV_CB_T *ptr_cb = HAL_LIGHTNING_PKT_GET_DRV_CB_PTR(unit); + CLX_IRQ_FLAGS_T irq_flag = 0; + UI32_T intr_en = 0; + + osal_takeIsrLock(&ptr_cb->intr_lock, &irq_flag); + osal_mdc_readPciReg(unit, HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_CP_COMMON_INT_EN_HI), &intr_en, sizeof(intr_en)); + intr_en |= intr_bitmap; + osal_mdc_writePciReg(unit, HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_CP_COMMON_INT_EN_HI), &intr_en, sizeof(intr_en)); + osal_giveIsrLock(&ptr_cb->intr_lock, &irq_flag); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_disableIntr( + const UI32_T unit, + const UI32_T intr_bitmap) +{ + HAL_LIGHTNING_PKT_DRV_CB_T *ptr_cb = HAL_LIGHTNING_PKT_GET_DRV_CB_PTR(unit); + CLX_IRQ_FLAGS_T irq_flag = 0; + UI32_T intr_en = 0; + + osal_takeIsrLock(&ptr_cb->intr_lock, &irq_flag); + osal_mdc_readPciReg(unit, HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_CP_COMMON_INT_EN_HI), &intr_en, sizeof(intr_en)); + intr_en &= ~intr_bitmap; + osal_mdc_writePciReg(unit, HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_CP_COMMON_INT_EN_HI), &intr_en, sizeof(intr_en)); + osal_giveIsrLock(&ptr_cb->intr_lock, &irq_flag); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_maskIntr( + const UI32_T unit, + const UI32_T intr_bitmap) +{ + HAL_LIGHTNING_PKT_DRV_CB_T *ptr_cb = HAL_LIGHTNING_PKT_GET_DRV_CB_PTR(unit); + CLX_IRQ_FLAGS_T irq_flag = 0; + + osal_takeIsrLock(&ptr_cb->intr_lock, &irq_flag); + osal_mdc_writePciReg(unit, HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_CP_COMMON_INT_MASK_CLR_HI), &intr_bitmap, sizeof(intr_bitmap)); + osal_giveIsrLock(&ptr_cb->intr_lock, &irq_flag); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_unmaskIntr( + const UI32_T unit, + const UI32_T intr_bitmap) +{ + HAL_LIGHTNING_PKT_DRV_CB_T *ptr_cb = HAL_LIGHTNING_PKT_GET_DRV_CB_PTR(unit); + CLX_IRQ_FLAGS_T irq_flag = 0; + + osal_takeIsrLock(&ptr_cb->intr_lock, &irq_flag); + osal_mdc_writePciReg(unit, HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_CP_COMMON_INT_MASK_SET_HI), &intr_bitmap, sizeof(intr_bitmap)); + osal_giveIsrLock(&ptr_cb->intr_lock, &irq_flag); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_dispatcher( + void *ptr_cookie) +{ + UI32_T unit = (UI32_T)((CLX_HUGE_T)ptr_cookie); + HAL_LIGHTNING_PKT_DRV_CB_T *ptr_cb = HAL_LIGHTNING_PKT_GET_DRV_CB_PTR(unit); + CLX_IRQ_FLAGS_T irq_flag = 0; + + UI32_T idx = 0, vec = sizeof(_hal_lightning_pkt_intr_vec) / sizeof(HAL_LIGHTNING_PKT_INTR_VEC_T); + UI32_T intr_mask = ptr_cb->intr_bitmap; + UI32_T intr_unmask = 0; + UI32_T intr_status = 0; + + /* MASK, READ and CLEAR PKT IRQs */ + osal_takeIsrLock(&ptr_cb->intr_lock, &irq_flag); + osal_mdc_writePciReg(unit, HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_CP_COMMON_INT_MASK_CLR_HI), &intr_mask, sizeof(UI32_T)); + osal_mdc_readPciReg (unit, HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_CP_COMMON_INT_STAT_HI), &intr_status, sizeof(UI32_T)); + intr_status = intr_status & intr_mask; + osal_mdc_writePciReg(unit, HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_CP_COMMON_INT_CLR_HI), &intr_status, sizeof(UI32_T)); + osal_giveIsrLock(&ptr_cb->intr_lock, &irq_flag); + + /* Module thread handle and unmask the interrupt */ + intr_unmask = intr_status ^ intr_mask; + if (0x0 != intr_status) + { + for (idx = 0; idx < vec; idx++) + { + if (_hal_lightning_pkt_intr_vec[idx].intr_reg & intr_status) + { + osal_triggerEvent(&_hal_lightning_pkt_intr_vec[idx].intr_event); + _hal_lightning_pkt_intr_vec[idx].intr_cnt++; + } + } + } + + /* UNMASK other PKT IRQs */ + osal_takeIsrLock(&ptr_cb->intr_lock, &irq_flag); + osal_mdc_writePciReg(unit, HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_CP_COMMON_INT_MASK_SET_HI), &intr_unmask, sizeof(UI32_T)); + osal_giveIsrLock(&ptr_cb->intr_lock, &irq_flag); + + return (CLX_E_OK); +} + +/* ----------------------------------------------------------------------------------- RW HW Regs */ +/* FUNCTION NAME: _hal_lightning_pkt_startTxChannelReg + * PURPOSE: + * To issue "START" command to the target TX channel. + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * gpd_num -- The GPD ring length of the channel + * OUTPUT: + * None. + * RETURN: + * CLX_E_OK -- Successfully configure the register. + * CLX_E_OTHERS -- Configure the register failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_startTxChannelReg( + const UI32_T unit, + const HAL_LIGHTNING_PKT_TX_CHANNEL_T channel, + const UI32_T gpd_num) +{ + HAL_LIGHTNING_PKT_TCH_CMD_REG_T tch_cmd; + + tch_cmd.reg = 0x0; + tch_cmd.field.tch_start = 0x1; + tch_cmd.field.tch_gpd_add_no_lo = gpd_num & 0xff; + tch_cmd.field.tch_gpd_add_no_hi = (gpd_num & 0xff00) >> 8; + + osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_PDMA_TCH_REG(HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_TCH_CMD), channel), + &tch_cmd.reg, sizeof(HAL_LIGHTNING_PKT_TCH_CMD_REG_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_startRxChannelReg + * PURPOSE: + * To issue "START" command to the target RX channel. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * gpd_num -- The GPD ring length of the channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure the register. + * CLX_E_OTHERS -- Configure the register failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_startRxChannelReg( + const UI32_T unit, + const HAL_LIGHTNING_PKT_RX_CHANNEL_T channel, + const UI32_T gpd_num) +{ + HAL_LIGHTNING_PKT_RCH_CMD_REG_T rch_cmd; + + rch_cmd.reg = 0x0; + rch_cmd.field.rch_start = 0x1; + rch_cmd.field.rch_gpd_add_no_lo = gpd_num & 0xff; + rch_cmd.field.rch_gpd_add_no_hi = (gpd_num & 0xff00) >> 8; + + osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_PDMA_RCH_REG(HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_RCH_CMD), channel), + &rch_cmd.reg, sizeof(HAL_LIGHTNING_PKT_RCH_CMD_REG_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_resumeTxChannelReg + * PURPOSE: + * To issue "RESUME" command to the target TX channel. + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * gpd_num -- The GPD ring length of the channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure the register. + * CLX_E_OTHERS -- Configure the register failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_resumeTxChannelReg( + const UI32_T unit, + const HAL_LIGHTNING_PKT_TX_CHANNEL_T channel, + const UI32_T gpd_num) +{ + HAL_LIGHTNING_PKT_TCH_CMD_REG_T tch_cmd; + + tch_cmd.reg = 0x0; + tch_cmd.field.tch_resume = 0x1; + tch_cmd.field.tch_gpd_add_no_lo = gpd_num & 0xff; + tch_cmd.field.tch_gpd_add_no_hi = (gpd_num & 0xff00) >> 8; + + osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_PDMA_TCH_REG(HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_TCH_CMD), channel), + &tch_cmd.reg, sizeof(HAL_LIGHTNING_PKT_TCH_CMD_REG_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_resumeRxChannelReg + * PURPOSE: + * To issue "RESUME" command to the target RX channel. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * gpd_num -- The GPD ring length of the channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure the register. + * CLX_E_OTHERS -- Configure the register failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_resumeRxChannelReg( + const UI32_T unit, + const HAL_LIGHTNING_PKT_RX_CHANNEL_T channel, + const UI32_T gpd_num) +{ + HAL_LIGHTNING_PKT_RCH_CMD_REG_T rch_cmd; + + rch_cmd.reg = 0x0; + rch_cmd.field.rch_resume = 0x1; + rch_cmd.field.rch_gpd_add_no_lo = gpd_num & 0xff; + rch_cmd.field.rch_gpd_add_no_hi = (gpd_num & 0xff00) >> 8; + + osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_PDMA_RCH_REG(HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_RCH_CMD), channel), + &rch_cmd.reg, sizeof(HAL_LIGHTNING_PKT_RCH_CMD_REG_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_stopTxChannelReg + * PURPOSE: + * To issue "STOP" command to the target TX channel. + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure the register. + * CLX_E_OTHERS -- Configure the register failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_stopTxChannelReg( + const UI32_T unit, + const HAL_LIGHTNING_PKT_TX_CHANNEL_T channel) +{ + HAL_LIGHTNING_PKT_TCH_CMD_REG_T tch_cmd; + + tch_cmd.reg = 0x0; + tch_cmd.field.tch_stop = 0x1; + + osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_PDMA_TCH_REG(HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_TCH_CMD), channel), + &tch_cmd.reg, sizeof(HAL_LIGHTNING_PKT_TCH_CMD_REG_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_stopRxChannelReg + * PURPOSE: + * To issue "STOP" command to the target RX channel. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure the register. + * CLX_E_OTHERS -- Configure the register failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_stopRxChannelReg( + const UI32_T unit, + const HAL_LIGHTNING_PKT_RX_CHANNEL_T channel) +{ + HAL_LIGHTNING_PKT_RCH_CMD_REG_T rch_cmd; + + rch_cmd.reg = 0x0; + rch_cmd.field.rch_stop = 0x1; + + osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_PDMA_RCH_REG(HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_RCH_CMD), channel), + &rch_cmd.reg, sizeof(HAL_LIGHTNING_PKT_RCH_CMD_REG_T)); + + return (CLX_E_OK); +} + +/* ----------------------------------------------------------------------------------- Init HW Regs */ +/* FUNCTION NAME: _hal_lightning_pkt_setTxGpdStartAddrReg + * PURPOSE: + * To configure the start address and the length of target GPD ring of TX channel. + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * gpd_start_addr -- The start address of the GPD ring + * gpd_ring_sz -- The size of the GPD ring + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure the register. + * CLX_E_OTHERS -- Configure the register failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_setTxGpdStartAddrReg( + const UI32_T unit, + const HAL_LIGHTNING_PKT_TX_CHANNEL_T channel, + const CLX_ADDR_T gpd_start_addr, + const UI32_T gpd_ring_sz) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + UI32_T tch_gpd_ring_start_addr_lo = 0; + UI32_T tch_gpd_ring_start_addr_hi = 0; + UI32_T tch_gpd_ring_size = 0; + + /* Configure the low 32-bit address. */ + tch_gpd_ring_start_addr_lo = (UI32_T)CLX_ADDR_64_LOW(gpd_start_addr); + + rc = osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_PDMA_TCH_REG(HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_TCH_GPD_RING_START_ADDR_LO), channel), + &tch_gpd_ring_start_addr_lo, sizeof(UI32_T)); + + /* Configure the high 32-bit address. */ + if (CLX_E_OK == rc) + { + tch_gpd_ring_start_addr_hi = (UI32_T)CLX_ADDR_64_HI(gpd_start_addr); + + rc = osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_PDMA_TCH_REG(HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_TCH_GPD_RING_START_ADDR_HI), channel), + &tch_gpd_ring_start_addr_hi, sizeof(UI32_T)); + } + + /* Configure the GPD ring size. */ + if (CLX_E_OK == rc) + { + tch_gpd_ring_size = gpd_ring_sz; + + rc = osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_PDMA_TCH_REG(HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_TCH_GPD_RING_SIZE), channel), + &tch_gpd_ring_size, sizeof(UI32_T)); + } + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_setRxGpdStartAddrReg + * PURPOSE: + * To configure the start address and the length of target GPD ring of RX channel. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * gpd_start_addr -- The start address of the GPD ring + * gpd_ring_sz -- The size of the GPD ring + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure the register. + * CLX_E_OTHERS -- Configure the register failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_setRxGpdStartAddrReg( + const UI32_T unit, + const HAL_LIGHTNING_PKT_RX_CHANNEL_T channel, + const CLX_ADDR_T gpd_start_addr, + const UI32_T gpd_ring_sz) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + UI32_T rch_gpd_ring_start_addr_lo = 0; + UI32_T rch_gpd_ring_start_addr_hi = 0; + UI32_T rch_gpd_ring_size = 0; + + /* Configure the low 32-bit address. */ + rch_gpd_ring_start_addr_lo = (UI32_T)CLX_ADDR_64_LOW(gpd_start_addr); + + rc = osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_PDMA_RCH_REG(HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_RCH_GPD_RING_START_ADDR_LO), channel), + &rch_gpd_ring_start_addr_lo, sizeof(UI32_T)); + + /* Configure the high 32-bit address. */ + if (CLX_E_OK == rc) + { + rch_gpd_ring_start_addr_hi = (UI32_T)CLX_ADDR_64_HI(gpd_start_addr); + + rc = osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_PDMA_RCH_REG(HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_RCH_GPD_RING_START_ADDR_HI), channel), + &rch_gpd_ring_start_addr_hi, sizeof(UI32_T)); + } + + /* Configure the GPD ring size. */ + if (CLX_E_OK == rc) + { + rch_gpd_ring_size = gpd_ring_sz; + + rc = osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_PDMA_RCH_REG(HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_RCH_GPD_RING_SIZE), channel), + &rch_gpd_ring_size, sizeof(UI32_T)); + } + + return (rc); +} + +/* ----------------------------------------------------------------------------------- ISR HW Regs */ +/* FUNCTION NAME: _hal_lightning_pkt_maskAllTxL2IsrReg + * PURPOSE: + * To mask all the TX L2 interrupts for the specified channel. + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully mask all the TX L2 interrupts. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_maskAllTxL2IsrReg( + const UI32_T unit, + const HAL_LIGHTNING_PKT_TX_CHANNEL_T channel) +{ + UI32_T reg = 0; + + HAL_LIGHTNING_PKT_CLR_BITMAP(reg, + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_HWO_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_CHKSM_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_NO_OVFL_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_DMA_READ_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_BUF_SIZE_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_RUNT_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_OVSZ_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_LEN_MISMATCH_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_PKTPL_DMA_READ_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_COS_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_GT255_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_PFC | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_CREDIT_UDFL_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_DMA_WRITE_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_STOP_CMD_CPLT); + + osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_PDMA_TCH_REG(HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_TCH_INT_MASK), channel), + ®, sizeof(UI32_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_maskAllRxL2IsrReg + * PURPOSE: + * To mask all the L2 interrupts for the specified channel. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully mask all the L2 interrupts. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_maskAllRxL2IsrReg( + const UI32_T unit, + const HAL_LIGHTNING_PKT_RX_CHANNEL_T channel) +{ + UI32_T reg = 0; + + HAL_LIGHTNING_PKT_CLR_BITMAP(reg, + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_LOW | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_EMPTY | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_ERROR | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_GPD_CHKSM_ERROR | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_DMA_READ_ERROR | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_DMA_WRITE_ERROR | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_STOP_CMD_CPLT | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_GPD_GT255_ERROR | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_TOD_UNINIT | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_PKT_ERROR_DROP | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_UDSZ_DROP | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_OVSZ_DROP | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_CMDQ_OVF_DROP | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_FIFO_OVF_DROP); + + osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_PDMA_RCH_REG(HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_RCH_INT_MASK), channel), + ®, sizeof(UI32_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_unmaskAllTxL2IsrReg + * PURPOSE: + * To unmask all the TX L2 interrupts for the specified channel. + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully unmask all the TX L2 interrupts. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_unmaskAllTxL2IsrReg( + const UI32_T unit, + const HAL_LIGHTNING_PKT_TX_CHANNEL_T channel) +{ + UI32_T reg = 0; + + HAL_LIGHTNING_PKT_SET_BITMAP(reg, + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_HWO_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_CHKSM_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_NO_OVFL_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_DMA_READ_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_BUF_SIZE_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_RUNT_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_OVSZ_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_LEN_MISMATCH_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_PKTPL_DMA_READ_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_COS_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_GT255_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_PFC | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_CREDIT_UDFL_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_DMA_WRITE_ERROR | + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_STOP_CMD_CPLT); + + osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_PDMA_TCH_REG(HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_TCH_INT_MASK), channel), + ®, sizeof(UI32_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_unmaskAllRxL2IsrReg + * PURPOSE: + * To unmask all the L2 interrupts for the specified channel. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully unmask all the L2 interrupts. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_unmaskAllRxL2IsrReg( + const UI32_T unit, + const HAL_LIGHTNING_PKT_RX_CHANNEL_T channel) +{ + UI32_T reg = 0; + + HAL_LIGHTNING_PKT_SET_BITMAP(reg, + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_LOW | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_EMPTY | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_ERROR | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_GPD_CHKSM_ERROR | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_DMA_READ_ERROR | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_DMA_WRITE_ERROR | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_STOP_CMD_CPLT | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_GPD_GT255_ERROR | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_TOD_UNINIT | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_PKT_ERROR_DROP | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_UDSZ_DROP | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_OVSZ_DROP | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_CMDQ_OVF_DROP | + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_FIFO_OVF_DROP); + + osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_PDMA_RCH_REG(HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_RCH_INT_MASK), channel), + ®, sizeof(UI32_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_clearTxL2IsrStatusReg + * PURPOSE: + * To clear the status of TX L2 interrupts for the specified channel. + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * isr_bitmap -- The bitmap used to specify the target ISRs + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully clear L1 ISR status. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_clearTxL2IsrStatusReg( + const UI32_T unit, + const HAL_LIGHTNING_PKT_TX_CHANNEL_T channel, + const HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_T isr_bitmap) +{ + UI32_T reg = 0; + + HAL_LIGHTNING_PKT_SET_BITMAP(reg, isr_bitmap); + + osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_PDMA_TCH_REG(HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_TCH_INT_CLR), channel), + ®, sizeof(UI32_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_clearRxL2IsrStatusReg + * PURPOSE: + * To clear the status of RX L2 interrupts for the specified channel. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * isr_bitmap -- The bitmap used to specify the target ISRs + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully clear RX L2 ISR status. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_clearRxL2IsrStatusReg( + const UI32_T unit, + const HAL_LIGHTNING_PKT_RX_CHANNEL_T channel, + const HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_T isr_bitmap) +{ + UI32_T reg = 0; + + HAL_LIGHTNING_PKT_SET_BITMAP(reg, isr_bitmap); + + osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_PDMA_RCH_REG(HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_RCH_INT_CLR), channel), + ®, sizeof(UI32_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_lightning_pkt_getTxIntrCnt + * PURPOSE: + * To get the PDMA TX interrupt counters of the target channel. + * INPUT: + * unit -- The unit ID + * channel -- The target channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully get the counters. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_lightning_pkt_getTxIntrCnt( + const UI32_T unit, + const UI32_T channel, + UI32_T *ptr_intr_cnt) +{ + *ptr_intr_cnt = HAL_LIGHTNING_PKT_TCH_CNT(unit, channel); + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_lightning_pkt_getRxIntrCnt + * PURPOSE: + * To get the PDMA RX interrupt counters of the target channel. + * INPUT: + * unit -- The unit ID + * channel -- The target channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully get the counters. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_lightning_pkt_getRxIntrCnt( + const UI32_T unit, + const UI32_T channel, + UI32_T *ptr_intr_cnt) +{ + *ptr_intr_cnt = HAL_LIGHTNING_PKT_RCH_CNT(unit, channel); + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_lightning_pkt_getTxKnlCnt + * PURPOSE: + * To get the PDMA TX counters of the target channel. + * INPUT: + * unit -- The unit ID + * ptr_cookie -- Pointer of the TX cookie + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully get the counters. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_lightning_pkt_getTxKnlCnt( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_CH_CNT_COOKIE_T *ptr_cookie) +{ + HAL_LIGHTNING_PKT_TX_CB_T *ptr_tx_cb = HAL_LIGHTNING_PKT_GET_TX_CB_PTR(unit); + + osal_io_copyToUser(&ptr_cookie->tx_cnt, &ptr_tx_cb->cnt, sizeof(HAL_LIGHTNING_PKT_TX_CNT_T)); + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_lightning_pkt_getRxKnlCnt + * PURPOSE: + * To get the PDMA RX counters of the target channel. + * INPUT: + * unit -- The unit ID + * ptr_cookie -- Pointer of the RX cookie + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully get the counters. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_lightning_pkt_getRxKnlCnt( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_CH_CNT_COOKIE_T *ptr_cookie) +{ + HAL_LIGHTNING_PKT_RX_CB_T *ptr_rx_cb = HAL_LIGHTNING_PKT_GET_RX_CB_PTR(unit); + + osal_io_copyToUser(&ptr_cookie->rx_cnt, &ptr_rx_cb->cnt, sizeof(HAL_LIGHTNING_PKT_RX_CNT_T)); + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_lightning_pkt_clearTxKnlCnt + * PURPOSE: + * To clear the PDMA TX counters of the target channel. + * INPUT: + * unit -- The unit ID + * ptr_cookie -- Pointer of the TX cookie + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully clear the counters. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_lightning_pkt_clearTxKnlCnt( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_TX_COOKIE_T *ptr_cookie) +{ + HAL_LIGHTNING_PKT_TX_CB_T *ptr_tx_cb = HAL_LIGHTNING_PKT_GET_TX_CB_PTR(unit); + + osal_memset(&ptr_tx_cb->cnt, 0, sizeof(HAL_LIGHTNING_PKT_TX_CNT_T)); + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_lightning_pkt_clearRxKnlCnt + * PURPOSE: + * To clear the PDMA RX counters of the target channel. + * INPUT: + * unit -- The unit ID + * ptr_cookie -- Pointer of the RX cookie + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully clear the counters. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_lightning_pkt_clearRxKnlCnt( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_RX_COOKIE_T *ptr_cookie) +{ + HAL_LIGHTNING_PKT_RX_CB_T *ptr_rx_cb = HAL_LIGHTNING_PKT_GET_RX_CB_PTR(unit); + + osal_memset(&ptr_rx_cb->cnt, 0, sizeof(HAL_LIGHTNING_PKT_RX_CNT_T)); + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_lightning_pkt_setPortAttr + * PURPOSE: + * To set the port attributes such as status or speeds. + * INPUT: + * unit -- The unit ID + * ptr_cookie -- Pointer of the Port cookie + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully set the attributes. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_lightning_pkt_setPortAttr( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_PORT_COOKIE_T *ptr_cookie) +{ +#define HAL_LIGHTNING_PKT_PORT_STATUS_UP (1) +#define HAL_LIGHTNING_PKT_PORT_STATUS_DOWN (0) + struct net_device *ptr_net_dev; + struct net_device_priv *ptr_priv; + UI32_T port; + UI32_T status; + CLX_PORT_SPEED_T speed; + + osal_io_copyFromUser(&port, &ptr_cookie->port, sizeof(UI32_T)); + osal_io_copyFromUser(&status, &ptr_cookie->status, sizeof(UI32_T)); + osal_io_copyFromUser(&speed, &ptr_cookie->speed, sizeof(CLX_PORT_SPEED_T)); + + ptr_net_dev = HAL_LIGHTNING_PKT_GET_PORT_NETDEV(port); + if ((NULL != ptr_net_dev) && (portspeed = SPEED_1000; + break; + case CLX_PORT_SPEED_10G: + ptr_priv->speed = SPEED_10000; + break; + case CLX_PORT_SPEED_25G: + ptr_priv->speed = 25000; + break; + case CLX_PORT_SPEED_40G: + ptr_priv->speed = 40000; + break; + case CLX_PORT_SPEED_50G: + ptr_priv->speed = 50000; + break; + case CLX_PORT_SPEED_100G: + ptr_priv->speed = 100000; + break; + case CLX_PORT_SPEED_200G: + ptr_priv->speed = 200000; + break; + case CLX_PORT_SPEED_400G: + ptr_priv->speed = 400000; + break; + default: + break; + } + } + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_lightning_pkt_getPortAttr + * PURPOSE: + * To get the port attributes such as status or speeds. + * INPUT: + * unit -- The unit ID + * ptr_cookie -- Pointer of the Port cookie + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully set the attributes. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_lightning_pkt_getPortAttr( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_PORT_COOKIE_T *ptr_cookie) +{ + struct net_device *ptr_net_dev; + struct net_device_priv *ptr_priv; + UI32_T port; + UI32_T status; + CLX_PORT_SPEED_T speed; + + osal_io_copyFromUser(&port, &ptr_cookie->port, sizeof(UI32_T)); + + ptr_net_dev = HAL_LIGHTNING_PKT_GET_PORT_NETDEV(port); + if ((NULL == ptr_net_dev) || (port >= HAL_LIGHTNING_PKT_MAX_PORT_NUM)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_ERR, + "%s(%d): Failed to get netdev, port %d\n", + __FUNCTION__, __LINE__, port); + return -1; + } + status = netif_carrier_ok(ptr_net_dev); + + ptr_priv = netdev_priv(ptr_net_dev); + switch(ptr_priv->speed) + { + case SPEED_1000: + speed = CLX_PORT_SPEED_1G; + break; + case SPEED_10000: + speed = CLX_PORT_SPEED_10G; + break; + case 25000: + speed = CLX_PORT_SPEED_25G; + break; + case 40000: + speed = CLX_PORT_SPEED_40G; + break; + case 50000: + speed = CLX_PORT_SPEED_50G; + break; + case 100000: + speed = CLX_PORT_SPEED_100G; + break; + case 200000: + speed = CLX_PORT_SPEED_200G; + break; + case 400000: + speed = CLX_PORT_SPEED_400G; + break; + default: + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_ERR, + "%s(%d): Unknown speed %d, port %d\n", + __FUNCTION__, __LINE__, ptr_priv->speed, port); + speed = CLX_PORT_SPEED_400G; + break; + } + osal_io_copyToUser(&ptr_cookie->status, &status, sizeof(UI32_T)); + osal_io_copyToUser(&ptr_cookie->speed, &speed, sizeof(CLX_PORT_SPEED_T)); + return (CLX_E_OK); +} + + +static void +_hal_lightning_pkt_lockRxChannelAll( + const UI32_T unit) +{ + UI32_T rch; + HAL_LIGHTNING_PKT_RX_PDMA_T *ptr_rx_pdma; + + for (rch = 0; rch < HAL_LIGHTNING_PKT_RX_CHANNEL_LAST; rch++) + { + ptr_rx_pdma = HAL_LIGHTNING_PKT_GET_RX_PDMA_PTR(unit, rch); + osal_takeSemaphore(&ptr_rx_pdma->sema, CLX_SEMAPHORE_WAIT_FOREVER); + } +} + +static void +_hal_lightning_pkt_unlockRxChannelAll( + const UI32_T unit) +{ + UI32_T rch; + HAL_LIGHTNING_PKT_RX_PDMA_T *ptr_rx_pdma; + + for (rch = 0; rch < HAL_LIGHTNING_PKT_RX_CHANNEL_LAST; rch++) + { + ptr_rx_pdma = HAL_LIGHTNING_PKT_GET_RX_PDMA_PTR(unit, rch); + osal_giveSemaphore(&ptr_rx_pdma->sema); + } +} + +#if defined(NETIF_EN_NETLINK) + +static CLX_ERROR_NO_T +_hal_lightning_pkt_setIntfProperty( + const UI32_T unit, + HAL_LIGHTNING_PKT_NL_IOCTL_COOKIE_T *ptr_cookie) +{ + UI32_T intf_id; + NETIF_NL_INTF_PROPERTY_T property; + UI32_T param0; + UI32_T param1; + CLX_ERROR_NO_T rc; + + osal_io_copyFromUser(&intf_id, &ptr_cookie->intf_id, sizeof(UI32_T)); + osal_io_copyFromUser(&property, &ptr_cookie->property, sizeof(NETIF_NL_INTF_PROPERTY_T)); + osal_io_copyFromUser(¶m0, &ptr_cookie->param0, sizeof(UI32_T)); + osal_io_copyFromUser(¶m1, &ptr_cookie->param1, sizeof(UI32_T)); + + _hal_lightning_pkt_lockRxChannelAll(unit); + + rc = netif_nl_setIntfProperty(unit, intf_id, property, param0, param1); + + _hal_lightning_pkt_unlockRxChannelAll(unit); + + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + return (rc); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_getIntfProperty( + const UI32_T unit, + HAL_LIGHTNING_PKT_NL_IOCTL_COOKIE_T *ptr_cookie) +{ + UI32_T intf_id; + NETIF_NL_INTF_PROPERTY_T property; + UI32_T param0; + UI32_T param1; + CLX_ERROR_NO_T rc; + + osal_io_copyFromUser(&intf_id, &ptr_cookie->intf_id, sizeof(UI32_T)); + osal_io_copyFromUser(&property, &ptr_cookie->property, sizeof(NETIF_NL_INTF_PROPERTY_T)); + osal_io_copyFromUser(¶m0, &ptr_cookie->param0, sizeof(UI32_T)); + + rc = netif_nl_getIntfProperty(unit, intf_id, property, ¶m0, ¶m1); + + osal_io_copyToUser(&ptr_cookie->param0, ¶m0, sizeof(UI32_T)); + osal_io_copyToUser(&ptr_cookie->param1, ¶m1, sizeof(UI32_T)); + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + return (rc); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_createNetlink( + const UI32_T unit, + HAL_LIGHTNING_PKT_NL_IOCTL_COOKIE_T *ptr_cookie) +{ + NETIF_NL_NETLINK_T netlink; + UI32_T netlink_id; + CLX_ERROR_NO_T rc; + + osal_io_copyFromUser(&netlink, &ptr_cookie->netlink, sizeof(NETIF_NL_NETLINK_T)); + + _hal_lightning_pkt_lockRxChannelAll(unit); + + rc = netif_nl_createNetlink(unit, &netlink, &netlink_id); + + _hal_lightning_pkt_unlockRxChannelAll(unit); + + osal_io_copyToUser(&ptr_cookie->netlink.id, &netlink_id, sizeof(UI32_T)); + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + return (rc); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_destroyNetlink( + const UI32_T unit, + HAL_LIGHTNING_PKT_NL_IOCTL_COOKIE_T *ptr_cookie) +{ + UI32_T netlink_id; + CLX_ERROR_NO_T rc; + + osal_io_copyFromUser(&netlink_id, &ptr_cookie->netlink.id, sizeof(UI32_T)); + + _hal_lightning_pkt_lockRxChannelAll(unit); + + rc = netif_nl_destroyNetlink(unit, netlink_id); + + _hal_lightning_pkt_unlockRxChannelAll(unit); + + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + return (rc); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_getNetlink( + const UI32_T unit, + HAL_LIGHTNING_PKT_NL_IOCTL_COOKIE_T *ptr_cookie) +{ + UI32_T id; + NETIF_NL_NETLINK_T netlink; + CLX_ERROR_NO_T rc; + + osal_io_copyFromUser(&id, &ptr_cookie->netlink.id, sizeof(UI32_T)); + + rc = netif_nl_getNetlink(unit, id, &netlink); + if (CLX_E_OK == rc) + { + osal_io_copyToUser(&ptr_cookie->netlink, &netlink, sizeof(NETIF_NL_NETLINK_T)); + } + else + { + rc = CLX_E_ENTRY_NOT_FOUND; + } + + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + return (CLX_E_OK); +} + +#endif + +/* ----------------------------------------------------------------------------------- independent func */ +/* FUNCTION NAME: _hal_lightning_pkt_enQueue + * PURPOSE: + * To enqueue the target data. + * INPUT: + * ptr_que -- Pointer for the target queue + * ptr_data -- Pointer for the data to be enqueued + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully enqueue the data. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_enQueue( + HAL_LIGHTNING_PKT_SW_QUEUE_T *ptr_que, + void *ptr_data) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + + osal_takeSemaphore(&ptr_que->sema, CLX_SEMAPHORE_WAIT_FOREVER); + rc = osal_que_enque(&ptr_que->que_id, ptr_data); + osal_giveSemaphore(&ptr_que->sema); + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_deQueue + * PURPOSE: + * To dequeue the target data. + * INPUT: + * ptr_que -- Pointer for the target queue + * pptr_data -- Pointer for the data pointer to be dequeued + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully dequeue the data. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_deQueue( + HAL_LIGHTNING_PKT_SW_QUEUE_T *ptr_que, + void **pptr_data) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + + osal_takeSemaphore(&ptr_que->sema, CLX_SEMAPHORE_WAIT_FOREVER); + rc = osal_que_deque(&ptr_que->que_id, pptr_data); + osal_giveSemaphore(&ptr_que->sema); + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_getQueueCount + * PURPOSE: + * To obtain the current GPD number in the target RX queue. + * INPUT: + * ptr_que -- Pointer for the target queue + * ptr_count -- Pointer for the data count + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully obtain the GPD count. + * CLX_E_BAD_PARAMETER -- Parameter pointer is null. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_getQueueCount( + HAL_LIGHTNING_PKT_SW_QUEUE_T *ptr_que, + UI32_T *ptr_count) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + + osal_takeSemaphore(&ptr_que->sema, CLX_SEMAPHORE_WAIT_FOREVER); + osal_que_getCount(&ptr_que->que_id, ptr_count); + osal_giveSemaphore(&ptr_que->sema); + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_allocRxPayloadBuf + * PURPOSE: + * To allocate the RX packet payload buffer for the GPD. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * gpd_idx -- The current GPD index + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully allocate the buffer. + * CLX_E_NO_MEMORY -- Allocate the buffer failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_allocRxPayloadBuf( + const UI32_T unit, + const UI32_T channel, + const UI32_T gpd_idx) +{ + CLX_ERROR_NO_T rc = CLX_E_NO_MEMORY; + HAL_LIGHTNING_PKT_RX_CB_T *ptr_rx_cb = HAL_LIGHTNING_PKT_GET_RX_CB_PTR(unit); + volatile HAL_LIGHTNING_PKT_RX_GPD_T *ptr_rx_gpd = HAL_LIGHTNING_PKT_GET_RX_GPD_PTR(unit, channel, gpd_idx); + CLX_ADDR_T phy_addr = 0; + + HAL_LIGHTNING_PKT_RX_PDMA_T *ptr_rx_pdma = HAL_LIGHTNING_PKT_GET_RX_PDMA_PTR(unit, channel); + struct sk_buff *ptr_skb = NULL; + + ptr_skb = osal_skb_alloc(ptr_rx_cb->buf_len); + if (NULL != ptr_skb) + { + /* map skb to dma */ + phy_addr = osal_skb_mapDma(ptr_skb, DMA_FROM_DEVICE); + if (0x0 == phy_addr) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_ERR, + "u=%u, rxch=%u, skb dma map err, size=%u\n", + unit, channel, ptr_skb->len); + osal_skb_free(ptr_skb); + rc = CLX_E_NO_MEMORY; + } + else + { + ptr_rx_pdma->pptr_skb_ring[gpd_idx] = ptr_skb; + rc = CLX_E_OK; + } + } + + if (CLX_E_OK == rc) + { + ptr_rx_gpd->data_buf_addr_hi = CLX_ADDR_64_HI(phy_addr); + ptr_rx_gpd->data_buf_addr_lo = CLX_ADDR_64_LOW(phy_addr); + ptr_rx_gpd->avbl_buf_len = ptr_rx_cb->buf_len; + } + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_freeRxPayloadBuf + * PURPOSE: + * To free the RX packet payload buffer for the GPD. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * gpd_idx -- The current GPD index + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully free the buffer. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_freeRxPayloadBuf( + const UI32_T unit, + const UI32_T channel, + const UI32_T gpd_idx) +{ + CLX_ERROR_NO_T rc = CLX_E_OTHERS; + volatile HAL_LIGHTNING_PKT_RX_GPD_T *ptr_rx_gpd = HAL_LIGHTNING_PKT_GET_RX_GPD_PTR(unit, channel, gpd_idx); + CLX_ADDR_T phy_addr = 0; + + HAL_LIGHTNING_PKT_RX_PDMA_T *ptr_rx_pdma = HAL_LIGHTNING_PKT_GET_RX_PDMA_PTR(unit, channel); + struct sk_buff *ptr_skb = NULL; + + phy_addr = CLX_ADDR_32_TO_64(ptr_rx_gpd->data_buf_addr_hi, ptr_rx_gpd->data_buf_addr_lo); + if (0x0 != phy_addr) + { + /* unmap dma */ + ptr_skb = ptr_rx_pdma->pptr_skb_ring[gpd_idx]; + osal_skb_unmapDma(phy_addr, ptr_skb->len, DMA_FROM_DEVICE); + osal_skb_free(ptr_skb); + rc = CLX_E_OK; + } + + if (CLX_E_OK == rc) + { + ptr_rx_gpd->data_buf_addr_hi = 0x0; + ptr_rx_gpd->data_buf_addr_lo = 0x0; + } + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_freeRxPayloadBufGpd + * PURPOSE: + * To free the RX packet payload buffer for the GPD. + * INPUT: + * unit -- The unit ID + * ptr_sw_gpd -- The pointer of RX SW GPD + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully free the buffer. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_freeRxPayloadBufGpd( + const UI32_T unit, + HAL_LIGHTNING_PKT_RX_SW_GPD_T *ptr_sw_gpd) +{ + CLX_ERROR_NO_T rc = CLX_E_OTHERS; + CLX_ADDR_T phy_addr = 0; + + struct sk_buff *ptr_skb = NULL; + + phy_addr = CLX_ADDR_32_TO_64(ptr_sw_gpd->rx_gpd.data_buf_addr_hi, ptr_sw_gpd->rx_gpd.data_buf_addr_lo); + if (0x0 != phy_addr) + { + ptr_skb = ptr_sw_gpd->ptr_cookie; + osal_skb_free(ptr_skb); + rc = CLX_E_OK; + } + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_initTxPdmaRing + * PURPOSE: + * To initialize the GPD ring of target TX channel. + * + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully initialize the GPD ring. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_initTxPdmaRing( + const UI32_T unit, + const HAL_LIGHTNING_PKT_TX_CHANNEL_T channel) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_LIGHTNING_PKT_TX_PDMA_T *ptr_tx_pdma = HAL_LIGHTNING_PKT_GET_TX_PDMA_PTR(unit, channel); + volatile HAL_LIGHTNING_PKT_TX_GPD_T *ptr_tx_gpd = NULL; + CLX_ADDR_T phy_addr = 0; + UI32_T gpd_idx = 0; + + for (gpd_idx = 0; gpd_idx < ptr_tx_pdma->gpd_num; gpd_idx++) + { + ptr_tx_gpd = HAL_LIGHTNING_PKT_GET_TX_GPD_PTR(unit, channel, gpd_idx); + osal_memset((void *)ptr_tx_gpd, 0x0, sizeof(HAL_LIGHTNING_PKT_TX_GPD_T)); + ptr_tx_gpd->ioc = HAL_LIGHTNING_PKT_IOC_HAS_INTR; + ptr_tx_gpd->ch = HAL_LIGHTNING_PKT_CH_LAST_GPD; + ptr_tx_gpd->hwo = HAL_LIGHTNING_PKT_HWO_SW_OWN; + osal_dma_flushCache((void *)ptr_tx_gpd, sizeof(HAL_LIGHTNING_PKT_TX_GPD_T)); + } + + phy_addr = osal_dma_convertVirtToPhy(ptr_tx_pdma->ptr_gpd_align_start_addr); + rc = _hal_lightning_pkt_setTxGpdStartAddrReg(unit, channel, phy_addr, ptr_tx_pdma->gpd_num); + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_initRxPdmaRing + * PURPOSE: + * To initialize the RX GPD ring. + * INPUT: + * unit -- The target unit + * channel -- The target RX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully initialize the RX GPD ring. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_initRxPdmaRing( + const UI32_T unit, + const HAL_LIGHTNING_PKT_RX_CHANNEL_T channel) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_LIGHTNING_PKT_RX_PDMA_T *ptr_rx_pdma = HAL_LIGHTNING_PKT_GET_RX_PDMA_PTR(unit, channel); + volatile HAL_LIGHTNING_PKT_RX_GPD_T *ptr_rx_gpd = NULL; + CLX_ADDR_T phy_addr = 0; + UI32_T gpd_idx = 0; + + for (gpd_idx = 0; gpd_idx < ptr_rx_pdma->gpd_num; gpd_idx++) + { + ptr_rx_gpd = HAL_LIGHTNING_PKT_GET_RX_GPD_PTR(unit, channel, gpd_idx); + osal_memset((void *)ptr_rx_gpd, 0x0, sizeof(HAL_LIGHTNING_PKT_RX_GPD_T)); + ptr_rx_gpd->ioc = HAL_LIGHTNING_PKT_IOC_NO_INTR; + ptr_rx_gpd->hwo = HAL_LIGHTNING_PKT_HWO_SW_OWN; + osal_dma_flushCache((void *)ptr_rx_gpd, sizeof(HAL_LIGHTNING_PKT_RX_GPD_T)); + } + + phy_addr = osal_dma_convertVirtToPhy(ptr_rx_pdma->ptr_gpd_align_start_addr); + rc = _hal_lightning_pkt_setRxGpdStartAddrReg(unit, channel, phy_addr, ptr_rx_pdma->gpd_num); + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_initRxPdmaRingBuf + * PURPOSE: + * To de-init the Rx PDMA ring configuration. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully de-init the Rx PDMA ring. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_initRxPdmaRingBuf( + const UI32_T unit, + const HAL_LIGHTNING_PKT_RX_CHANNEL_T channel) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_LIGHTNING_PKT_RX_CB_T *ptr_rx_cb = HAL_LIGHTNING_PKT_GET_RX_CB_PTR(unit); + HAL_LIGHTNING_PKT_RX_PDMA_T *ptr_rx_pdma = HAL_LIGHTNING_PKT_GET_RX_PDMA_PTR(unit, channel); + volatile HAL_LIGHTNING_PKT_RX_GPD_T *ptr_rx_gpd = NULL; + UI32_T gpd_idx = 0; + + if (0 == ptr_rx_cb->buf_len) + { + return (CLX_E_BAD_PARAMETER); + } + + for (gpd_idx = 0; gpd_idx < ptr_rx_pdma->gpd_num; gpd_idx++) + { + ptr_rx_gpd = HAL_LIGHTNING_PKT_GET_RX_GPD_PTR(unit, channel, gpd_idx); + osal_dma_invalidateCache((void *)ptr_rx_gpd, sizeof(HAL_LIGHTNING_PKT_RX_GPD_T)); + + rc = _hal_lightning_pkt_allocRxPayloadBuf(unit, channel, gpd_idx); + if (CLX_E_OK == rc) + { + ptr_rx_gpd->ioc = HAL_LIGHTNING_PKT_IOC_HAS_INTR; + ptr_rx_gpd->hwo = HAL_LIGHTNING_PKT_HWO_HW_OWN; + osal_dma_flushCache((void *)ptr_rx_gpd, sizeof(HAL_LIGHTNING_PKT_RX_GPD_T)); + } + else + { + ptr_rx_cb->cnt.no_memory++; + break; + } + } + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_deinitRxPdmaRingBuf + * PURPOSE: + * To de-init the Rx PDMA ring configuration. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully de-init the Rx PDMA ring. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_deinitRxPdmaRingBuf( + const UI32_T unit, + const HAL_LIGHTNING_PKT_RX_CHANNEL_T channel) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_LIGHTNING_PKT_RX_PDMA_T *ptr_rx_pdma = HAL_LIGHTNING_PKT_GET_RX_PDMA_PTR(unit, channel); + volatile HAL_LIGHTNING_PKT_RX_GPD_T *ptr_rx_gpd = NULL; + UI32_T gpd_idx = 0; + + for (gpd_idx = 0; ((gpd_idx < ptr_rx_pdma->gpd_num) && (CLX_E_OK == rc)); gpd_idx++) + { + /* mark the GPD as invalid to prevent Rx-done task to process it */ + ptr_rx_gpd = HAL_LIGHTNING_PKT_GET_RX_GPD_PTR(unit, channel, gpd_idx); + ptr_rx_gpd->hwo = HAL_LIGHTNING_PKT_HWO_HW_OWN; + osal_dma_flushCache((void *)ptr_rx_gpd, sizeof(HAL_LIGHTNING_PKT_RX_GPD_T)); + + rc = _hal_lightning_pkt_freeRxPayloadBuf(unit, channel, gpd_idx); + } + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_recoverTxPdma + * PURPOSE: + * To recover the PDMA status to the initial state. + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully recover PDMA. + * NOTES: + * + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_recoverTxPdma( + const UI32_T unit, + const HAL_LIGHTNING_PKT_TX_CHANNEL_T channel) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_LIGHTNING_PKT_TX_PDMA_T *ptr_tx_pdma = HAL_LIGHTNING_PKT_GET_TX_PDMA_PTR(unit, channel); + + /* Release the software GPD ring and configure it again. */ + ptr_tx_pdma->used_idx = 0; + ptr_tx_pdma->free_idx = 0; + ptr_tx_pdma->used_gpd_num = 0; + ptr_tx_pdma->free_gpd_num = ptr_tx_pdma->gpd_num; + + _hal_lightning_pkt_stopTxChannelReg(unit, channel); + rc = _hal_lightning_pkt_initTxPdmaRing(unit, channel); + _hal_lightning_pkt_startTxChannelReg(unit, channel, 0); + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_recoverRxPdma + * PURPOSE: + * To recover the RX PDMA from the error state. + * INPUT: + * unit -- The unit ID + * channel -- The target RX channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully recovery the PDMA. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_recoverRxPdma( + const UI32_T unit, + const HAL_LIGHTNING_PKT_RX_CHANNEL_T channel) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_LIGHTNING_PKT_RX_PDMA_T *ptr_rx_pdma = HAL_LIGHTNING_PKT_GET_RX_PDMA_PTR(unit, channel); + + /* Release the software GPD ring and configure it again. */ + ptr_rx_pdma->cur_idx = 0; + + _hal_lightning_pkt_stopRxChannelReg(unit, channel); + rc = _hal_lightning_pkt_deinitRxPdmaRingBuf(unit, channel); + if (CLX_E_OK != rc) + { + return (rc); + } + rc = _hal_lightning_pkt_initRxPdmaRing(unit, channel); + if (CLX_E_OK != rc) + { + return (rc); + } + rc = _hal_lightning_pkt_initRxPdmaRingBuf(unit, channel); + if (CLX_E_OK != rc) + { + return (rc); + } + _hal_lightning_pkt_startRxChannelReg(unit, channel, ptr_rx_pdma->gpd_num); + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_freeTxGpdList + * PURPOSE: + * To free the TX SW GPD link list. + * INPUT: + * unit -- The unit ID + * ptr_sw_gpd -- The pointer of TX SW GPD + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully free the GPD list. + * NOTES: + * None + */ +static void +_hal_lightning_pkt_freeTxGpdList( + UI32_T unit, + HAL_LIGHTNING_PKT_TX_SW_GPD_T *ptr_sw_gpd) +{ + HAL_LIGHTNING_PKT_TX_SW_GPD_T *ptr_sw_gpd_cur = NULL; + + while (NULL != ptr_sw_gpd) + { + ptr_sw_gpd_cur = ptr_sw_gpd; + ptr_sw_gpd = ptr_sw_gpd->ptr_next; + osal_free(ptr_sw_gpd_cur); + } +} + +/* FUNCTION NAME: _hal_lightning_pkt_freeRxGpdList + * PURPOSE: + * To free the RX SW GPD link list. + * INPUT: + * unit -- The unit ID + * ptr_sw_gpd -- The pointer of RX SW GPD + * free_payload -- TRUE: To free the buf in SDK, FALSE: in user process. + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully recovery the PDMA. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_freeRxGpdList( + UI32_T unit, + HAL_LIGHTNING_PKT_RX_SW_GPD_T *ptr_sw_gpd, + BOOL_T free_payload) +{ + HAL_LIGHTNING_PKT_RX_SW_GPD_T *ptr_sw_gpd_cur = NULL; + + while (NULL != ptr_sw_gpd) + { + ptr_sw_gpd_cur = ptr_sw_gpd; + ptr_sw_gpd = ptr_sw_gpd->ptr_next; + if (TRUE == free_payload) + { + _hal_lightning_pkt_freeRxPayloadBufGpd(unit, ptr_sw_gpd_cur); + } + osal_free(ptr_sw_gpd_cur); + } + + return (CLX_E_OK); +} + +/* ----------------------------------------------------------------------------------- pkt_drv */ +/* FUNCTION NAME: _hal_lightning_pkt_txEnQueueBulk + * PURPOSE: + * To enqueue numbers of packet in the bulk buffer + * INPUT: + * unit -- The unit ID + * channel -- The target channel + * number -- The number of packet to be enqueue + * OUTPUT: + * None + * RETURN: + * None + * NOTES: + * None + */ +static void +_hal_lightning_pkt_txEnQueueBulk( + const UI32_T unit, + const UI32_T channel, + const UI32_T number) +{ + HAL_LIGHTNING_PKT_TX_PDMA_T *ptr_tx_pdma = HAL_LIGHTNING_PKT_GET_TX_PDMA_PTR(unit, channel); + HAL_LIGHTNING_PKT_TX_SW_GPD_T *ptr_sw_gpd = NULL; + UI32_T idx; + + for (idx = 0; idx < number; idx++) + { + ptr_sw_gpd = ptr_tx_pdma->pptr_sw_gpd_bulk[idx]; + ptr_tx_pdma->pptr_sw_gpd_bulk[idx] = NULL; + if (NULL != ptr_sw_gpd->callback) + { + ptr_sw_gpd->callback(unit, ptr_sw_gpd, ptr_sw_gpd->ptr_cookie); + } + } +} + + +/* FUNCTION NAME: _hal_lightning_pkt_strictTxDeQueue + * PURPOSE: + * To dequeue the packets based on the strict algorithm. + * INPUT: + * unit -- The unit ID + * ptr_cookie -- Pointer of the TX cookie + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully dequeue the packets. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_strictTxDeQueue( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_TX_COOKIE_T *ptr_cookie) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_LIGHTNING_PKT_TX_CB_T *ptr_tx_cb = HAL_LIGHTNING_PKT_GET_TX_CB_PTR(unit); + HAL_LIGHTNING_PKT_TX_SW_GPD_T *ptr_sw_gpd = NULL; + CLX_ADDR_T sw_gpd_addr; + UI32_T que_cnt = 0; + + /* get queue count */ + _hal_lightning_pkt_getQueueCount(&ptr_tx_cb->sw_queue, &que_cnt); + + /* wait txTask event */ + if (0 == que_cnt) + { + osal_waitEvent(&ptr_tx_cb->sync_sema); + if (FALSE == ptr_tx_cb->running) + { + return (CLX_E_OTHERS); /* deinit */ + } + + ptr_tx_cb->cnt.wait_event++; + + /* re-get queue count */ + _hal_lightning_pkt_getQueueCount(&ptr_tx_cb->sw_queue, &que_cnt); + } + + /* deque */ + if (que_cnt > 0) + { + rc = _hal_lightning_pkt_deQueue(&ptr_tx_cb->sw_queue, (void **)&ptr_sw_gpd); + if (CLX_E_OK == rc) + { + ptr_tx_cb->cnt.deque_ok++; + + sw_gpd_addr = (CLX_ADDR_T)ptr_sw_gpd->ptr_cookie; + + /* Give the address of pre-saved SW GPD back to userspace */ + osal_io_copyToUser(&ptr_cookie->done_sw_gpd_addr, + &sw_gpd_addr, + sizeof(CLX_ADDR_T)); + + /* free kernel sw_gpd */ + _hal_lightning_pkt_freeTxGpdList(unit, ptr_sw_gpd); + } + else + { + ptr_tx_cb->cnt.deque_fail++; + } + } + else + { + /* It may happen at last gpd, return error and do not invoke callback. */ + rc = CLX_E_OTHERS; + } + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_rxCheckReason + * PURPOSE: + * To check the packets to linux kernel/user. + * INPUT: + * ptr_rx_gpd -- Pointer of the RX GPD + * ptr_hit_prof -- Pointer of the hit flag + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully dispatch the packets. + * NOTES: + * Reference to pkt_srv. + */ +static void +_hal_lightning_pkt_rxCheckReason( + volatile HAL_LIGHTNING_PKT_RX_GPD_T *ptr_rx_gpd, + HAL_LIGHTNING_PKT_NETIF_PROFILE_T *ptr_profile, + BOOL_T *ptr_hit_prof) +{ + HAL_PKT_RX_REASON_BITMAP_T *ptr_reason_bitmap = &ptr_profile->reason_bitmap; + UI32_T bitval = 0; + UI32_T bitmap = 0x0; + + if (0 == (ptr_profile->flags & HAL_LIGHTNING_PKT_NETIF_PROFILE_FLAGS_REASON)) + { + /* It means that reason doesn't metters */ + *ptr_hit_prof = TRUE; + return; + } + +#define HAL_LIGHTNING_PKT_DI_NON_L3_CPU_MIN (HAL_EXCPT_CPU_BASE_ID + HAL_EXCPT_CPU_NON_L3_MIN) +#define HAL_LIGHTNING_PKT_DI_NON_L3_CPU_MAX (HAL_EXCPT_CPU_BASE_ID + HAL_EXCPT_CPU_NON_L3_MAX) +#define HAL_LIGHTNING_PKT_DI_L3_CPU_MIN (HAL_EXCPT_CPU_BASE_ID + HAL_EXCPT_CPU_L3_MIN) +#define HAL_LIGHTNING_PKT_DI_L3_CPU_MAX (HAL_EXCPT_CPU_BASE_ID + HAL_EXCPT_CPU_L3_MAX) + + switch (ptr_rx_gpd->itmh_eth.typ) + { + case HAL_LIGHTNING_PKT_TMH_TYPE_ITMH_ETH: + + /* IPP non-L3 exception */ + if (ptr_rx_gpd->itmh_eth.dst_idx >= HAL_LIGHTNING_PKT_DI_NON_L3_CPU_MIN && + ptr_rx_gpd->itmh_eth.dst_idx <= HAL_LIGHTNING_PKT_DI_NON_L3_CPU_MAX) + { + bitval = ptr_rx_gpd->itmh_eth.dst_idx - HAL_LIGHTNING_PKT_DI_NON_L3_CPU_MIN; + bitmap = 1UL << (bitval % 32); + if (0 != (ptr_reason_bitmap->ipp_excpt_bitmap[bitval / 32] & bitmap)) + { + *ptr_hit_prof = TRUE; + break; + } + } + + /* IPP L3 exception */ + if (ptr_rx_gpd->itmh_eth.dst_idx >= HAL_LIGHTNING_PKT_DI_L3_CPU_MIN && + ptr_rx_gpd->itmh_eth.dst_idx <= HAL_LIGHTNING_PKT_DI_L3_CPU_MAX) + { + bitmap = ptr_rx_gpd->itmh_eth.dst_idx - HAL_LIGHTNING_PKT_DI_L3_CPU_MIN; + if (0 != (ptr_reason_bitmap->ipp_l3_excpt_bitmap[0] & bitmap)) + { + *ptr_hit_prof = TRUE; + break; + } + } + + /* IPP cp_to_cpu_bmap */ + bitmap = ptr_rx_gpd->itmh_eth.cp_to_cpu_bmap; + if (0 != (ptr_reason_bitmap->ipp_copy2cpu_bitmap[0] & bitmap)) + { + *ptr_hit_prof = TRUE; + break; + } + + /* IPP cp_to_cpu_rsn */ + bitval = ptr_rx_gpd->itmh_eth.cp_to_cpu_code; + bitmap = 1UL << (bitval % 32); + if (0 != (ptr_reason_bitmap->ipp_rsn_bitmap[bitval / 32] & bitmap)) + { + *ptr_hit_prof = TRUE; + break; + } + break; + + case HAL_LIGHTNING_PKT_TMH_TYPE_ITMH_FAB: + case HAL_LIGHTNING_PKT_TMH_TYPE_ETMH_FAB: + break; + + case HAL_LIGHTNING_PKT_TMH_TYPE_ETMH_ETH: + + /* EPP exception */ + if (1 == ptr_rx_gpd->etmh_eth.redir) + { + bitval = ptr_rx_gpd->etmh_eth.excpt_code_mir_bmap; + bitmap = 1UL << (bitval % 32); + if (0 != (ptr_reason_bitmap->epp_excpt_bitmap[bitval / 32] & bitmap)) + { + *ptr_hit_prof = TRUE; + break; + } + } + + /* EPP cp_to_cpu_bmap */ + bitmap = ((ptr_rx_gpd->etmh_eth.cp_to_cpu_bmap_w0 << 7) | + (ptr_rx_gpd->etmh_eth.cp_to_cpu_bmap_w1)); + if (0 != (ptr_reason_bitmap->epp_copy2cpu_bitmap[0] & bitmap)) + { + *ptr_hit_prof = TRUE; + break; + } + break; + + default: + *ptr_hit_prof = FALSE; + break; + } +} + +static BOOL_T +_hal_lightning_pkt_comparePatternWithPayload( + volatile HAL_LIGHTNING_PKT_RX_GPD_T *ptr_rx_gpd, + const UI8_T *ptr_pattern, + const UI8_T *ptr_mask, + const UI32_T offset) +{ + CLX_ADDR_T phy_addr = 0; + UI8_T *ptr_virt_addr = NULL; + UI32_T idx; + + /* Get the packet payload */ + phy_addr = CLX_ADDR_32_TO_64(ptr_rx_gpd->data_buf_addr_hi, ptr_rx_gpd->data_buf_addr_lo); + ptr_virt_addr = (C8_T *) osal_dma_convertPhyToVirt(phy_addr); + + for (idx=0; idxflags & (HAL_LIGHTNING_PKT_NETIF_PROFILE_FLAGS_PATTERN_0 | + HAL_LIGHTNING_PKT_NETIF_PROFILE_FLAGS_PATTERN_1 | + HAL_LIGHTNING_PKT_NETIF_PROFILE_FLAGS_PATTERN_2 | + HAL_LIGHTNING_PKT_NETIF_PROFILE_FLAGS_PATTERN_3)) != 0) + { + /* Need to compare the payload with at least one of the four patterns */ + /* Pre-assume that the result is positive */ + *ptr_hit_prof = TRUE; + + /* If any of the following comparison fails, the result will be changed to negtive */ + } + else + { + return; + } + + for (idx=0; idxflags & (HAL_LIGHTNING_PKT_NETIF_PROFILE_FLAGS_PATTERN_0 << idx))) + { + match = _hal_lightning_pkt_comparePatternWithPayload(ptr_rx_gpd, + ptr_profile->pattern[idx], + ptr_profile->mask[idx], + ptr_profile->offset[idx]); + if (TRUE == match) + { + /* Do nothing */ + } + else + { + /* Change the result to negtive */ + *ptr_hit_prof = FALSE; + break; + } + } + } +} + +static void +_hal_lightning_pkt_matchUserProfile( + volatile HAL_LIGHTNING_PKT_RX_GPD_T *ptr_rx_gpd, + HAL_LIGHTNING_PKT_PROFILE_NODE_T *ptr_profile_list, + HAL_LIGHTNING_PKT_NETIF_PROFILE_T **pptr_profile_hit) +{ + HAL_LIGHTNING_PKT_PROFILE_NODE_T *ptr_curr_node = ptr_profile_list; + BOOL_T hit; + + *pptr_profile_hit = NULL; + + while (NULL != ptr_curr_node) + { + /* 1st match reason */ + _hal_lightning_pkt_rxCheckReason(ptr_rx_gpd, ptr_curr_node->ptr_profile, &hit); + if (TRUE == hit) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_PROFILE, + "rx prof matched by reason\n"); + + /* Then, check pattern */ + _hal_lightning_pkt_rxCheckPattern(ptr_rx_gpd, ptr_curr_node->ptr_profile, &hit); + if (TRUE == hit) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_PROFILE, + "rx prof matched by pattern\n"); + + *pptr_profile_hit = ptr_curr_node->ptr_profile; + break; + } + } + + /* Seach the next profile (priority lower) */ + ptr_curr_node = ptr_curr_node->ptr_next_node; + } +} + +static void +_hal_lightning_pkt_getPacketDest( + volatile HAL_LIGHTNING_PKT_RX_GPD_T *ptr_rx_gpd, + HAL_LIGHTNING_PKT_DEST_T *ptr_dest, + void **pptr_cookie) +{ + UI32_T port; + HAL_LIGHTNING_PKT_PROFILE_NODE_T *ptr_profile_list; + HAL_LIGHTNING_PKT_NETIF_PROFILE_T *ptr_profile_hit; + + port = ptr_rx_gpd->itmh_eth.igr_phy_port; + ptr_profile_list = HAL_LIGHTNING_PKT_GET_PORT_PROFILE_LIST(port); + + _hal_lightning_pkt_matchUserProfile(ptr_rx_gpd, + ptr_profile_list, + &ptr_profile_hit); + if (NULL != ptr_profile_hit) + { +#if defined(NETIF_EN_NETLINK) + if (HAL_LIGHTNING_PKT_NETIF_RX_DST_NETLINK == ptr_profile_hit->dst_type) + { + *ptr_dest = HAL_LIGHTNING_PKT_DEST_NETLINK; + *pptr_cookie = (void *)&ptr_profile_hit->netlink; + } + else + { + *ptr_dest = HAL_LIGHTNING_PKT_DEST_SDK; + } +#else + *ptr_dest = HAL_LIGHTNING_PKT_DEST_SDK; +#endif + } + else + { + *ptr_dest = HAL_LIGHTNING_PKT_DEST_NETDEV; + } +} + +/* FUNCTION NAME: _hal_lightning_pkt_rxEnQueue + * PURPOSE: + * To enqueue the packets to multiple queues. + * INPUT: + * unit -- The unit ID + * channel -- The target channel + * ptr_sw_gpd -- Pointer for the SW Rx GPD link list + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully enqueue the packets. + * NOTES: + * None + */ +static void +_hal_lightning_pkt_rxEnQueue( + const UI32_T unit, + const UI32_T channel, + HAL_LIGHTNING_PKT_RX_SW_GPD_T *ptr_sw_gpd) +{ + HAL_LIGHTNING_PKT_RX_CB_T *ptr_rx_cb = HAL_LIGHTNING_PKT_GET_RX_CB_PTR(unit); + HAL_LIGHTNING_PKT_RX_SW_GPD_T *ptr_sw_first_gpd = ptr_sw_gpd; + void *ptr_virt_addr = NULL; + CLX_ADDR_T phy_addr = 0; + HAL_LIGHTNING_PKT_DEST_T dest_type; + + /* skb meta */ + UI32_T port = 0, len = 0, total_len = 0; + struct net_device *ptr_net_dev = NULL; + struct net_device_priv *ptr_priv = NULL; + struct sk_buff *ptr_skb = NULL, *ptr_merge_skb = NULL; + UI32_T copy_offset; + void *ptr_dest; + +#if defined(PERF_EN_TEST) + /* To verify kernel Rx performance */ + if (CLX_E_OK == perf_rxTest()) + { + while (NULL != ptr_sw_gpd) + { + len += (HAL_LIGHTNING_PKT_CH_LAST_GPD == ptr_sw_gpd->rx_gpd.ch)? + ptr_sw_gpd->rx_gpd.cnsm_buf_len : ptr_sw_gpd->rx_gpd.avbl_buf_len; + + total_len += len; + + /* unmap dma */ + phy_addr = CLX_ADDR_32_TO_64(ptr_sw_gpd->rx_gpd.data_buf_addr_hi, ptr_sw_gpd->rx_gpd.data_buf_addr_lo); + osal_skb_unmapDma(phy_addr, len, DMA_FROM_DEVICE); + /* next */ + ptr_sw_gpd = ptr_sw_gpd->ptr_next; + } + perf_rxCallback(total_len); + _hal_lightning_pkt_freeRxGpdList(unit, ptr_sw_first_gpd, TRUE); + return ; + } +#endif + + _hal_lightning_pkt_getPacketDest(&ptr_sw_gpd->rx_gpd, &dest_type, &ptr_dest); + +#if defined(NETIF_EN_NETLINK) + if ((HAL_LIGHTNING_PKT_DEST_NETDEV == dest_type) || + (HAL_LIGHTNING_PKT_DEST_NETLINK == dest_type)) +#else + if (HAL_LIGHTNING_PKT_DEST_NETDEV == dest_type) +#endif + { + /* need to encap the packet as skb */ + ptr_sw_gpd = ptr_sw_first_gpd; + while (NULL != ptr_sw_gpd) + { + len = (HAL_LIGHTNING_PKT_CH_LAST_GPD == ptr_sw_gpd->rx_gpd.ch)? + ptr_sw_gpd->rx_gpd.cnsm_buf_len : ptr_sw_gpd->rx_gpd.avbl_buf_len; + + total_len += len; + + /* unmap dma */ + phy_addr = CLX_ADDR_32_TO_64(ptr_sw_gpd->rx_gpd.data_buf_addr_hi, ptr_sw_gpd->rx_gpd.data_buf_addr_lo); + ptr_virt_addr = ptr_sw_gpd->ptr_cookie; + + ptr_skb = (struct sk_buff *)ptr_virt_addr; + + /* note here ptr_skb->len is the total buffer size not means the actual Rx packet len + * it should be updated later + */ + osal_skb_unmapDma(phy_addr, ptr_skb->len, DMA_FROM_DEVICE); + + /* reset ptr_skb->len with real packet len instead of total buffer size */ + if (NULL == ptr_sw_gpd->ptr_next) + { + /* strip CRC padded by asic for the last gpd segment */ + ptr_skb->len = len - ETH_FCS_LEN; + } + else + { + ptr_skb->len = len; + } + + skb_set_tail_pointer(ptr_skb, ptr_skb->len); + + /* next */ + ptr_sw_gpd = ptr_sw_gpd->ptr_next; + } + + port = ptr_sw_first_gpd->rx_gpd.itmh_eth.igr_phy_port; + ptr_net_dev = HAL_LIGHTNING_PKT_GET_PORT_NETDEV(port); + + /* if the packet is composed of multiple gpd (skb), need to merge it into a single skb */ + if (NULL != ptr_sw_first_gpd->ptr_next) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_RX, + "u=%u, rxch=%u, rcv pkt size=%u > gpd buf size=%u\n", + unit, channel, total_len, ptr_rx_cb->buf_len); + ptr_merge_skb = osal_skb_alloc(total_len - ETH_FCS_LEN); + if (NULL != ptr_merge_skb) + { + copy_offset = 0; + ptr_sw_gpd = ptr_sw_first_gpd; + while (NULL != ptr_sw_gpd) + { + ptr_skb = (struct sk_buff *)ptr_sw_gpd->ptr_cookie; + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_RX, + "u=%u, rxch=%u, copy size=%u to buf offset=%u\n", + unit, channel, ptr_skb->len, copy_offset); + + memcpy(&(((UI8_T *)ptr_merge_skb->data)[copy_offset]), + ptr_skb->data, ptr_skb->len); + copy_offset += ptr_skb->len; + ptr_sw_gpd = ptr_sw_gpd->ptr_next; + } + /* put the merged skb to ptr_skb for the following process */ + ptr_skb = ptr_merge_skb; + } + else + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_RX), + "u=%u, rxch=%u, alloc skb failed, size=%u\n", + unit, channel, (total_len - ETH_FCS_LEN)); + } + + /* free both sw_gpd and the skb attached on it */ + _hal_lightning_pkt_freeRxGpdList(unit, ptr_sw_first_gpd, TRUE); + } + else + { + /* free only sw_gpd */ + _hal_lightning_pkt_freeRxGpdList(unit, ptr_sw_first_gpd, FALSE); + } + + /* if NULL netdev, drop the skb */ + if (NULL == ptr_net_dev) + { + ptr_rx_cb->cnt.channel[channel].netdev_miss++; + osal_skb_free(ptr_skb); + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_RX), + "u=%u, rxch=%u, find netdev failed\n", + unit, channel); + return; + } + + /* skb handling */ + ptr_skb->dev = ptr_net_dev; + ptr_skb->pkt_type = PACKET_HOST; /* this packet is for me */ + ptr_skb->ip_summed = CHECKSUM_UNNECESSARY; /* skip checksum */ + + /* send to linux */ + if (dest_type == HAL_LIGHTNING_PKT_DEST_NETDEV) + { + /* skip ethernet header only for Linux net interface*/ + ptr_skb->protocol = eth_type_trans(ptr_skb, ptr_net_dev); + osal_skb_recv(ptr_skb); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) + ptr_net_dev->last_rx = jiffies; +#endif + ptr_priv = netdev_priv(ptr_net_dev); + ptr_priv->stats.rx_packets++; + ptr_priv->stats.rx_bytes += total_len; + } +#if defined(NETIF_EN_NETLINK) + else + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_PROFILE, + "hit profile dest=netlink, name=%s, mcgrp=%s\n", + ((NETIF_NL_RX_DST_NETLINK_T *)ptr_dest)->name, + ((NETIF_NL_RX_DST_NETLINK_T *)ptr_dest)->mc_group_name); + netif_nl_rxSkb(unit, ptr_skb, ptr_dest); + } +#endif + } + else if (HAL_LIGHTNING_PKT_DEST_SDK == dest_type) + { + while (0 != _hal_lightning_pkt_enQueue(&ptr_rx_cb->sw_queue[channel], ptr_sw_gpd)) + { + ptr_rx_cb->cnt.channel[channel].enque_retry++; + HAL_LIGHTNING_PKT_RX_ENQUE_RETRY_SLEEP(); + } + ptr_rx_cb->cnt.channel[channel].enque_ok++; + + osal_triggerEvent(&ptr_rx_cb->sync_sema); + ptr_rx_cb->cnt.channel[channel].trig_event++; + } + else if (HAL_LIGHTNING_PKT_DEST_DROP == dest_type) + { + _hal_lightning_pkt_freeRxGpdList(unit, ptr_sw_first_gpd, TRUE); + } + else + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_RX), + "u=%u, rxch=%u, invalid pkt dest=%d\n", + unit, channel, dest_type); + } +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_flushRxQueue( + const UI32_T unit, + HAL_LIGHTNING_PKT_SW_QUEUE_T *ptr_que) +{ + HAL_LIGHTNING_PKT_RX_SW_GPD_T *ptr_sw_gpd_knl = NULL; + CLX_ERROR_NO_T rc; + + while (1) + { + rc = _hal_lightning_pkt_deQueue(ptr_que, (void **)&ptr_sw_gpd_knl); + if (CLX_E_OK == rc) + { + _hal_lightning_pkt_freeRxGpdList(unit, ptr_sw_gpd_knl, TRUE); + } + else + { + break; + } + } + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_schedRxDeQueue + * PURPOSE: + * To dequeue the packets based on the configured algorithm. + * INPUT: + * unit -- The unit ID + * ptr_cookie -- Pointer of the RX cookie + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully dequeue the packets. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_schedRxDeQueue( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_RX_COOKIE_T *ptr_cookie) +{ + HAL_LIGHTNING_PKT_IOCTL_RX_COOKIE_T ioctl_data; + HAL_LIGHTNING_PKT_IOCTL_RX_GPD_T ioctl_gpd; + HAL_LIGHTNING_PKT_RX_CB_T *ptr_rx_cb = HAL_LIGHTNING_PKT_GET_RX_CB_PTR(unit); + HAL_LIGHTNING_PKT_RX_SW_GPD_T *ptr_sw_gpd_knl = NULL; + HAL_LIGHTNING_PKT_RX_SW_GPD_T *ptr_sw_first_gpd_knl = NULL; + UI32_T que_cnt = 0; + UI32_T queue = 0; + UI32_T idx = 0; + UI32_T gpd_idx = 0; + /* copy Rx sw_gpd */ + volatile HAL_LIGHTNING_PKT_RX_GPD_T *ptr_rx_gpd = NULL; + void *ptr_virt_addr = NULL; + CLX_ADDR_T phy_addr = 0; + UI32_T buf_len = 0; + CLX_ERROR_NO_T rc = CLX_E_OK; + + /* normal process */ + if (TRUE == ptr_rx_cb->running) + { + /* get queue and count */ + for (idx = 0; idx < HAL_LIGHTNING_PKT_RX_QUEUE_NUM; idx++) + { + /* to gurantee the opportunity where each queue can be handler */ + queue = ((ptr_rx_cb->deque_idx + idx) % HAL_LIGHTNING_PKT_RX_QUEUE_NUM); + _hal_lightning_pkt_getQueueCount(&ptr_rx_cb->sw_queue[queue], &que_cnt); + if (que_cnt > 0) + { + ptr_rx_cb->deque_idx = ((queue + 1) % HAL_LIGHTNING_PKT_RX_QUEUE_NUM); + break; + } + } + + /* If all of the queues are empty, wait rxTask event */ + if (0 == que_cnt) + { + osal_waitEvent(&ptr_rx_cb->sync_sema); + + ptr_rx_cb->cnt.wait_event++; + + /* re-get queue and count */ + for (queue = 0; queue < HAL_LIGHTNING_PKT_RX_QUEUE_NUM; queue++) + { + _hal_lightning_pkt_getQueueCount(&ptr_rx_cb->sw_queue[queue], &que_cnt); + if (que_cnt > 0) + { + ptr_rx_cb->deque_idx = ((queue + 1) % HAL_LIGHTNING_PKT_RX_QUEUE_NUM); + break; + } + } + } + + /* deque */ + if ((que_cnt > 0) && (queue < HAL_LIGHTNING_PKT_RX_QUEUE_NUM)) + { + rc = _hal_lightning_pkt_deQueue(&ptr_rx_cb->sw_queue[queue], (void **)&ptr_sw_gpd_knl); + if (CLX_E_OK == rc) + { + ptr_rx_cb->cnt.channel[queue].deque_ok++; + ptr_sw_first_gpd_knl = ptr_sw_gpd_knl; + + osal_io_copyFromUser(&ioctl_data, ptr_cookie, sizeof(HAL_LIGHTNING_PKT_IOCTL_RX_COOKIE_T)); + + while (NULL != ptr_sw_gpd_knl) + { + /* get the IOCTL GPD from user */ + osal_io_copyFromUser(&ioctl_gpd, + ((void *)((CLX_HUGE_T)ioctl_data.ioctl_gpd_addr)) + + gpd_idx*sizeof(HAL_LIGHTNING_PKT_IOCTL_RX_GPD_T), + sizeof(HAL_LIGHTNING_PKT_IOCTL_RX_GPD_T)); + + /* get knl buf addr */ + ptr_rx_gpd = &ptr_sw_gpd_knl->rx_gpd; + phy_addr = CLX_ADDR_32_TO_64(ptr_rx_gpd->data_buf_addr_hi, ptr_rx_gpd->data_buf_addr_lo); + + ptr_virt_addr = ptr_sw_gpd_knl->ptr_cookie; + osal_skb_unmapDma(phy_addr, ((struct sk_buff *)ptr_virt_addr)->len, DMA_FROM_DEVICE); + + buf_len = (HAL_LIGHTNING_PKT_CH_LAST_GPD == ptr_rx_gpd->ch)? + ptr_rx_gpd->cnsm_buf_len : ptr_rx_gpd->avbl_buf_len; + + /* overwrite whole rx_gpd to user + * the user should re-assign the correct value to data_buf_addr_hi, data_buf_addr_low + * after this IOCTL returns + */ + osal_io_copyToUser((void *)((CLX_HUGE_T)ioctl_gpd.hw_gpd_addr), + &ptr_sw_gpd_knl->rx_gpd, + sizeof(HAL_LIGHTNING_PKT_RX_GPD_T)); + /* copy buf */ + /* DMA buf address allocated by the user is store in ptr_ioctl_data->gpd[idx].cookie */ + osal_io_copyToUser((void *)((CLX_HUGE_T)ioctl_gpd.dma_buf_addr), + ((struct sk_buff *)ptr_virt_addr)->data, buf_len); + ptr_sw_gpd_knl->ptr_cookie = ptr_virt_addr; + + /* next */ + ptr_sw_gpd_knl = ptr_sw_gpd_knl->ptr_next; + gpd_idx++; + } + + /* Must free kernel sw_gpd */ + _hal_lightning_pkt_freeRxGpdList(unit, ptr_sw_first_gpd_knl, TRUE); + } + else + { + ptr_rx_cb->cnt.channel[queue].deque_fail++; + } + } + else + { + /* it means that all queue's are flush -> rx stop flow */ + rc = CLX_E_OTHERS; + } + } + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_waitTxDone + * PURPOSE: + * To determine the next action after transfer the packet to HW. + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * ptr_sw_gpd -- Pointer for the SW Tx GPD link list + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully perform the target action. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_waitTxDone( + const UI32_T unit, + const HAL_LIGHTNING_PKT_TX_CHANNEL_T channel, + HAL_LIGHTNING_PKT_TX_SW_GPD_T *ptr_sw_gpd) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_LIGHTNING_PKT_TX_CB_T *ptr_tx_cb = HAL_LIGHTNING_PKT_GET_TX_CB_PTR(unit); + HAL_LIGHTNING_PKT_TX_PDMA_T *ptr_tx_pdma = HAL_LIGHTNING_PKT_GET_TX_PDMA_PTR(unit, channel); + volatile HAL_LIGHTNING_PKT_TX_GPD_T *ptr_tx_gpd = NULL; + UI32_T last_gpd_idx = 0; + UI32_T loop_cnt = 0; + + if (HAL_LIGHTNING_PKT_TX_WAIT_ASYNC == ptr_tx_cb->wait_mode) + { + ; + } + else if (HAL_LIGHTNING_PKT_TX_WAIT_SYNC_INTR == ptr_tx_cb->wait_mode) + { + osal_takeSemaphore(&ptr_tx_pdma->sync_intr_sema, HAL_LIGHTNING_PKT_PDMA_TX_INTR_TIMEOUT); + /* rc = _hal_lightning_pkt_invokeTxGpdCallback(unit, ptr_sw_gpd); */ + } + else if (HAL_LIGHTNING_PKT_TX_WAIT_SYNC_POLL == ptr_tx_cb->wait_mode) + { + last_gpd_idx = ptr_tx_pdma->free_idx + ptr_tx_pdma->used_gpd_num; + last_gpd_idx %= ptr_tx_pdma->gpd_num; + ptr_tx_gpd = HAL_LIGHTNING_PKT_GET_TX_GPD_PTR(unit, channel, last_gpd_idx); + + while (HAL_LIGHTNING_PKT_HWO_HW_OWN == ptr_tx_gpd->hwo) + { + osal_dma_invalidateCache((void *)ptr_tx_gpd, sizeof(HAL_LIGHTNING_PKT_TX_GPD_T)); + loop_cnt++; + if (0 == loop_cnt % HAL_LIGHTNING_PKT_PDMA_TX_POLL_MAX_LOOP) + { + ptr_tx_cb->cnt.channel[channel].poll_timeout++; + rc = CLX_E_OTHERS; + break; + } + } + if (HAL_LIGHTNING_PKT_ECC_ERROR_OCCUR == ptr_tx_gpd->ecce) + { + ptr_tx_cb->cnt.channel[channel].ecc_err++; + } + if (CLX_E_OK == rc) + { + ptr_tx_pdma->free_gpd_num += ptr_tx_pdma->used_gpd_num; + ptr_tx_pdma->used_gpd_num = 0; + ptr_tx_pdma->free_idx = ptr_tx_pdma->used_idx; + /* rc = _hal_lightning_pkt_invokeTxGpdCallback(unit, ptr_sw_gpd); */ + } + } + + return (rc); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_resumeAllIntf( + const UI32_T unit) +{ + struct net_device *ptr_net_dev = NULL; + UI32_T port; + + /* Unregister net devices by id */ + for (port = 0; port < HAL_LIGHTNING_PKT_MAX_PORT_NUM; port++) + { + ptr_net_dev = HAL_LIGHTNING_PKT_GET_PORT_NETDEV(port); + if (NULL != ptr_net_dev) + { + if (netif_queue_stopped(ptr_net_dev)) + { + netif_wake_queue(ptr_net_dev); + } + } + } + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_suspendAllIntf( + const UI32_T unit) +{ + struct net_device *ptr_net_dev = NULL; + UI32_T port; + + /* Unregister net devices by id */ + for (port = 0; port < HAL_LIGHTNING_PKT_MAX_PORT_NUM; port++) + { + ptr_net_dev = HAL_LIGHTNING_PKT_GET_PORT_NETDEV(port); + if (NULL != ptr_net_dev) + { + netif_stop_queue(ptr_net_dev); + } + } + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_stopAllIntf( + const UI32_T unit) +{ + struct net_device *ptr_net_dev = NULL; + UI32_T port; + + /* Unregister net devices by id */ + for (port = 0; port < HAL_LIGHTNING_PKT_MAX_PORT_NUM; port++) + { + ptr_net_dev = HAL_LIGHTNING_PKT_GET_PORT_NETDEV(port); + if (NULL != ptr_net_dev) + { + netif_tx_disable(ptr_net_dev); + } + } + + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_lightning_pkt_sendGpd + * PURPOSE: + * To perform the packet transmission form CPU to the switch. + * INPUT: + * unit -- The unit ID + * channel -- The target TX channel + * ptr_sw_gpd -- Pointer for the SW Tx GPD link list + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully perform the transferring. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_lightning_pkt_sendGpd( + const UI32_T unit, + const HAL_LIGHTNING_PKT_TX_CHANNEL_T channel, + HAL_LIGHTNING_PKT_TX_SW_GPD_T *ptr_sw_gpd) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_LIGHTNING_PKT_TX_CB_T *ptr_tx_cb = HAL_LIGHTNING_PKT_GET_TX_CB_PTR(unit); + HAL_LIGHTNING_PKT_TX_PDMA_T *ptr_tx_pdma = HAL_LIGHTNING_PKT_GET_TX_PDMA_PTR(unit, channel); + volatile HAL_LIGHTNING_PKT_TX_GPD_T *ptr_tx_gpd = NULL; + HAL_LIGHTNING_PKT_TX_SW_GPD_T *ptr_sw_first_gpd = ptr_sw_gpd; + UI32_T used_idx = 0; + UI32_T used_gpd_num = ptr_sw_gpd->gpd_num; + CLX_IRQ_FLAGS_T irq_flags; + HAL_LIGHTNING_PKT_DRV_CB_T *ptr_cb = HAL_LIGHTNING_PKT_GET_DRV_CB_PTR(unit); + + if (0 != (ptr_cb->init_flag & HAL_LIGHTNING_PKT_INIT_TASK)) + { + osal_takeIsrLock(&ptr_tx_pdma->ring_lock, &irq_flags); + + /* If not PDMA error */ + if (FALSE == ptr_tx_pdma->err_flag) + { + /* Make Sure GPD is enough */ + if (ptr_tx_pdma->free_gpd_num >= used_gpd_num) + { + used_idx = ptr_tx_pdma->used_idx; + while (NULL != ptr_sw_gpd) + { + ptr_tx_gpd = HAL_LIGHTNING_PKT_GET_TX_GPD_PTR(unit, channel, used_idx); + osal_dma_invalidateCache((void *)ptr_tx_gpd, sizeof(HAL_LIGHTNING_PKT_TX_GPD_T)); + + if (HAL_LIGHTNING_PKT_HWO_HW_OWN == ptr_tx_gpd->hwo) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_TX), + "u=%u, txch=%u, free gpd idx out-of-sync\n", + unit, channel); + rc = CLX_E_TABLE_FULL; + break; + } + + /* Fill in HW-GPD Ring */ + osal_memcpy((void *)ptr_tx_gpd, &ptr_sw_gpd->tx_gpd, sizeof(HAL_LIGHTNING_PKT_TX_GPD_T)); + osal_dma_flushCache((void *)ptr_tx_gpd, sizeof(HAL_LIGHTNING_PKT_TX_GPD_T)); + + /* next */ + used_idx++; + used_idx %= ptr_tx_pdma->gpd_num; + ptr_sw_gpd = ptr_sw_gpd->ptr_next; + } + + if (HAL_LIGHTNING_PKT_TX_WAIT_ASYNC == ptr_tx_cb->wait_mode) + { + /* Fill 1st GPD in SW-GPD Ring */ + ptr_tx_pdma->pptr_sw_gpd_ring[ptr_tx_pdma->used_idx] = ptr_sw_first_gpd; + } + + /* update Tx PDMA */ + ptr_tx_pdma->used_idx = used_idx; + ptr_tx_pdma->used_gpd_num += used_gpd_num; + ptr_tx_pdma->free_gpd_num -= used_gpd_num; + + _hal_lightning_pkt_resumeTxChannelReg(unit, channel, used_gpd_num); + ptr_tx_cb->cnt.channel[channel].send_ok++; + + _hal_lightning_pkt_waitTxDone(unit, channel, ptr_sw_first_gpd); + + /* reserve 1 packet buffer for each port in case that the suspension is too late */ +#define HAL_LIGHTNING_PKT_KNL_TX_RING_AVBL_GPD_LOW (HAL_LIGHTNING_PORT_NUM) + if (ptr_tx_pdma->free_gpd_num < HAL_LIGHTNING_PKT_KNL_TX_RING_AVBL_GPD_LOW) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_TX, + "u=%u, txch=%u, tx avbl gpd < %d, suspend all netdev\n", + unit, channel, HAL_LIGHTNING_PKT_KNL_TX_RING_AVBL_GPD_LOW); + _hal_lightning_pkt_suspendAllIntf(unit); + } + } + else + { + rc = CLX_E_TABLE_FULL; + } + } + else + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_TX), + "u=%u, txch=%u, pdma hw err\n", + unit, channel); + rc = CLX_E_OTHERS; + } + + osal_giveIsrLock(&ptr_tx_pdma->ring_lock, &irq_flags); + } + else + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_ERR, + "Tx failed, task already deinit\n"); + rc = CLX_E_OTHERS; + } + return (rc); +} + +/* ----------------------------------------------------------------------------------- pkt_srv */ +/* ----------------------------------------------------------------------------------- Rx Init */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_rxStop( + const UI32_T unit) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_LIGHTNING_PKT_RX_CHANNEL_T channel = 0; + UI32_T idx; + HAL_LIGHTNING_PKT_RX_CB_T *ptr_rx_cb = HAL_LIGHTNING_PKT_GET_RX_CB_PTR(unit); + HAL_LIGHTNING_PKT_DRV_CB_T *ptr_cb = HAL_LIGHTNING_PKT_GET_DRV_CB_PTR(unit); + HAL_LIGHTNING_PKT_RX_PDMA_T *ptr_rx_pdma = NULL; + + /* Check if Rx is already stopped*/ + if (0 == (ptr_cb->init_flag & HAL_LIGHTNING_PKT_INIT_RX_START)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_RX | HAL_LIGHTNING_PKT_DBG_ERR), + "u=%u, rx stop failed, not started\n", unit); + return (CLX_E_OK); + } + + /* Check if PKT Drv/Task were de-init before stopping Rx */ + /* Currently, we help to stop Rx when deinit Drv/Task, so it shouldn't enter below logic */ + if ((0 == (ptr_cb->init_flag & HAL_LIGHTNING_PKT_INIT_TASK)) || + (0 == (ptr_cb->init_flag & HAL_LIGHTNING_PKT_INIT_DRV))) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_RX | HAL_LIGHTNING_PKT_DBG_ERR), + "u=%u, rx stop failed, pkt task & pkt drv not init\n", unit); + return (CLX_E_OK); + } + + /* Deinit Rx PDMA and free buf for Rx GPD */ + for (channel = 0; channel < HAL_LIGHTNING_PKT_RX_CHANNEL_LAST; channel++) + { + ptr_rx_pdma = HAL_LIGHTNING_PKT_GET_RX_PDMA_PTR(unit, channel); + + osal_takeSemaphore(&ptr_rx_pdma->sema, CLX_SEMAPHORE_WAIT_FOREVER); + _hal_lightning_pkt_stopRxChannelReg(unit, channel); + rc = _hal_lightning_pkt_deinitRxPdmaRingBuf(unit, channel); + osal_giveSemaphore(&ptr_rx_pdma->sema); + } + + + /* flush packets in all queues since Rx task may be blocked in user space + * in this case it won't do ioctl to kernel to handle remaining packets + */ + for (idx = 0; idx < HAL_LIGHTNING_PKT_RX_QUEUE_NUM; idx++) + { + _hal_lightning_pkt_flushRxQueue(unit, &ptr_rx_cb->sw_queue[idx]); + } + + /* Return user thread */ + ptr_rx_cb->running = FALSE; + ptr_cb->init_flag &= (~HAL_LIGHTNING_PKT_INIT_RX_START); + + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_RX, + "u=%u, rx stop done, init flag=0x%x\n", unit, ptr_cb->init_flag); + + osal_triggerEvent(&ptr_rx_cb->sync_sema); + + return (rc); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_rxStart( + const UI32_T unit) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_LIGHTNING_PKT_RX_CHANNEL_T channel = 0; + HAL_LIGHTNING_PKT_RX_CB_T *ptr_rx_cb = HAL_LIGHTNING_PKT_GET_RX_CB_PTR(unit); + HAL_LIGHTNING_PKT_DRV_CB_T *ptr_cb = HAL_LIGHTNING_PKT_GET_DRV_CB_PTR(unit); + HAL_LIGHTNING_PKT_RX_PDMA_T *ptr_rx_pdma = NULL; + + if (0 != (ptr_cb->init_flag & HAL_LIGHTNING_PKT_INIT_RX_START)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_RX | HAL_LIGHTNING_PKT_DBG_ERR), + "u=%u, rx start failed, already started\n", unit); + return (CLX_E_OK); + } + + /* init Rx PDMA and alloc buf for Rx GPD */ + for (channel = 0; channel < HAL_LIGHTNING_PKT_RX_CHANNEL_LAST; channel++) + { + ptr_rx_pdma = HAL_LIGHTNING_PKT_GET_RX_PDMA_PTR(unit, channel); + + osal_takeSemaphore(&ptr_rx_pdma->sema, CLX_SEMAPHORE_WAIT_FOREVER); + rc = _hal_lightning_pkt_initRxPdmaRingBuf(unit, channel); + if (CLX_E_OK == rc) + { + ptr_rx_pdma->cur_idx = 0; + _hal_lightning_pkt_startRxChannelReg(unit, channel, ptr_rx_pdma->gpd_num); + } + osal_giveSemaphore(&ptr_rx_pdma->sema); + } + + /* enable to dequeue rx packets */ + ptr_rx_cb->running = TRUE; + + /* set the flag to record init state */ + ptr_cb->init_flag |= HAL_LIGHTNING_PKT_INIT_RX_START; + + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_RX, + "u=%u, rx start done, init flag=0x%x\n", unit, ptr_cb->init_flag); + return (rc); +} + +/* FUNCTION NAME: hal_lightning_pkt_setRxKnlConfig + * PURPOSE: + * 1. To stop the Rx channel and deinit the Rx subsystem. + * 2. To init the Rx subsystem and start the Rx channel. + * 3. To restart the Rx subsystem + * INPUT: + * unit -- The unit ID + * ptr_cookie -- Pointer of the RX cookie + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure the RX parameters. + * CLX_E_OTHERS -- Configure the parameter failed. + * NOTES: + * + */ +CLX_ERROR_NO_T +hal_lightning_pkt_setRxKnlConfig( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_RX_COOKIE_T *ptr_cookie) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_LIGHTNING_PKT_RX_CB_T *ptr_rx_cb = HAL_LIGHTNING_PKT_GET_RX_CB_PTR(unit); + HAL_LIGHTNING_PKT_DRV_CB_T *ptr_cb = HAL_LIGHTNING_PKT_GET_DRV_CB_PTR(unit); + HAL_LIGHTNING_PKT_IOCTL_RX_TYPE_T rx_type = HAL_LIGHTNING_PKT_IOCTL_RX_TYPE_LAST; + + osal_io_copyFromUser(&rx_type, &ptr_cookie->rx_type, sizeof(HAL_LIGHTNING_PKT_IOCTL_RX_TYPE_T)); + + if (HAL_LIGHTNING_PKT_IOCTL_RX_TYPE_DEINIT == rx_type) + { + _hal_lightning_pkt_rxStop(unit); + } + if (HAL_LIGHTNING_PKT_IOCTL_RX_TYPE_INIT == rx_type) + { + /* To prevent buffer size from being on-the-fly changed */ + if (0 != (ptr_cb->init_flag & HAL_LIGHTNING_PKT_INIT_RX_START)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_RX | HAL_LIGHTNING_PKT_DBG_ERR), + "u=%u, rx stop failed, not started\n", unit); + return (CLX_E_OK); + } + + osal_io_copyFromUser(&ptr_rx_cb->buf_len, &ptr_cookie->buf_len, sizeof(UI32_T)); + _hal_lightning_pkt_rxStart(unit); + } + + return (rc); +} + +/* FUNCTION NAME: hal_lightning_pkt_getRxKnlConfig + * PURPOSE: + * To get the Rx subsystem configuration. + * INPUT: + * unit -- The unit ID + * ptr_cookie -- Pointer of the RX cookie + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure the RX parameters. + * CLX_E_OTHERS -- Configure the parameter failed. + * NOTES: + * + */ +CLX_ERROR_NO_T +hal_lightning_pkt_getRxKnlConfig( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_RX_COOKIE_T *ptr_cookie) +{ + HAL_LIGHTNING_PKT_RX_CB_T *ptr_rx_cb = HAL_LIGHTNING_PKT_GET_RX_CB_PTR(unit); + + osal_io_copyToUser(&ptr_cookie->buf_len, &ptr_rx_cb->buf_len, sizeof(UI32_T)); + + return (CLX_E_OK); +} + +/* ----------------------------------------------------------------------------------- Deinit */ +/* FUNCTION NAME: hal_lightning_pkt_deinitTask + * PURPOSE: + * To de-initialize the Task for packet module. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully dinitialize the control block. + * CLX_E_OTHERS -- Initialize the control block failed. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_lightning_pkt_deinitTask( + const UI32_T unit) +{ + HAL_LIGHTNING_PKT_DRV_CB_T *ptr_cb = HAL_LIGHTNING_PKT_GET_DRV_CB_PTR(unit); + HAL_LIGHTNING_PKT_TX_CB_T *ptr_tx_cb = HAL_LIGHTNING_PKT_GET_TX_CB_PTR(unit); + HAL_LIGHTNING_PKT_RX_CB_T *ptr_rx_cb = HAL_LIGHTNING_PKT_GET_RX_CB_PTR(unit); + UI32_T channel = 0; + + /* to prevent net intf from Tx packet */ + ptr_tx_cb->net_tx_allowed = FALSE; + + /* In case that some undestroyed net intf keep Tx after task deinit */ + _hal_lightning_pkt_stopAllIntf(unit); + + if (0 == (ptr_cb->init_flag & HAL_LIGHTNING_PKT_INIT_TASK)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_RX | HAL_LIGHTNING_PKT_DBG_ERR), + "u=%u, rx stop failed, not started\n", unit); + return (CLX_E_OK); + } + + /* Need to stop Rx before de-init Task */ + if (0 != (ptr_cb->init_flag & HAL_LIGHTNING_PKT_INIT_RX_START)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_RX | HAL_LIGHTNING_PKT_DBG_ERR), + "u=%u, pkt task deinit failed, rx not stop\n", unit); + + _hal_lightning_pkt_rxStop(unit); + } + + /* Make the Rx IOCTL from userspace return back*/ + osal_triggerEvent(&ptr_rx_cb->sync_sema); + + /* Destroy txTask */ + if (HAL_LIGHTNING_PKT_TX_WAIT_ASYNC == ptr_tx_cb->wait_mode) + { + ptr_tx_cb->running = FALSE; + osal_triggerEvent(&ptr_tx_cb->sync_sema); + } + + /* Destroy handleRxDoneTask */ + for (channel = 0; channel < HAL_LIGHTNING_PKT_RX_CHANNEL_LAST; channel++) + { + osal_stopThread(&ptr_rx_cb->isr_task_id[channel]); + osal_triggerEvent(HAL_LIGHTNING_PKT_RCH_EVENT(unit, channel)); + osal_destroyThread(&ptr_rx_cb->isr_task_id[channel]); + } + + /* Destroy handleTxDoneTask */ + for (channel = 0; channel < HAL_LIGHTNING_PKT_TX_CHANNEL_LAST; channel++) + { + osal_stopThread(&ptr_tx_cb->isr_task_id[channel]); + osal_triggerEvent(HAL_LIGHTNING_PKT_TCH_EVENT(unit, channel)); + osal_destroyThread(&ptr_tx_cb->isr_task_id[channel]); + } + + /* Destroy handleErrorTask */ + osal_stopThread(&ptr_cb->err_task_id); + osal_triggerEvent(HAL_LIGHTNING_PKT_ERR_EVENT(unit)); + osal_destroyThread(&ptr_cb->err_task_id); + + /* Set the flag to record init state */ + ptr_cb->init_flag &= (~HAL_LIGHTNING_PKT_INIT_TASK); + + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_RX, + "u=%u, pkt task deinit done, init flag=0x%x\n", + unit, ptr_cb->init_flag); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_deinitTxPdma + * PURPOSE: + * To de-initialize the Tx PDMA configuration of the specified channel. + * INPUT: + * unit -- The unit ID + * channel -- The target Tx channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully de-init the Tx PDMA. + * CLX_E_OTHERS -- De-init the Tx PDMA failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_deinitTxPdma( + const UI32_T unit, + const HAL_LIGHTNING_PKT_TX_CHANNEL_T channel) +{ + HAL_LIGHTNING_PKT_TX_CB_T *ptr_tx_cb = HAL_LIGHTNING_PKT_GET_TX_CB_PTR(unit); + HAL_LIGHTNING_PKT_TX_PDMA_T *ptr_tx_pdma = HAL_LIGHTNING_PKT_GET_TX_PDMA_PTR(unit, channel); + + _hal_lightning_pkt_stopTxChannelReg(unit, channel); + + /* Free DMA and flush queue */ + osal_dma_free(ptr_tx_pdma->ptr_gpd_start_addr); + + if (HAL_LIGHTNING_PKT_TX_WAIT_ASYNC == ptr_tx_cb->wait_mode) + { + osal_free(ptr_tx_pdma->pptr_sw_gpd_ring); + osal_free(ptr_tx_pdma->pptr_sw_gpd_bulk); + } + else if (HAL_LIGHTNING_PKT_TX_WAIT_SYNC_INTR == ptr_tx_cb->wait_mode) + { + osal_destroySemaphore(&ptr_tx_pdma->sync_intr_sema); + } + + osal_destroyIsrLock(&ptr_tx_pdma->ring_lock); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_deinitRxPdma + * PURPOSE: + * To de-initialize the Rx PDMA configuration of the specified channel. + * INPUT: + * unit -- The unit ID + * channel -- The target Rx channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully de-init the Rx PDMA. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_deinitRxPdma( + const UI32_T unit, + const HAL_LIGHTNING_PKT_RX_CHANNEL_T channel) +{ + HAL_LIGHTNING_PKT_RX_PDMA_T *ptr_rx_pdma = HAL_LIGHTNING_PKT_GET_RX_PDMA_PTR(unit, channel); + + /* Free DMA */ + osal_takeSemaphore(&ptr_rx_pdma->sema, CLX_SEMAPHORE_WAIT_FOREVER); + osal_dma_free(ptr_rx_pdma->ptr_gpd_start_addr); + osal_giveSemaphore(&ptr_rx_pdma->sema); + osal_destroySemaphore(&ptr_rx_pdma->sema); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_deinitPktCb + * PURPOSE: + * To de-init the control block of Drv. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully de-init the control block. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_deinitPktCb( + const UI32_T unit) +{ + HAL_LIGHTNING_PKT_DRV_CB_T *ptr_cb = HAL_LIGHTNING_PKT_GET_DRV_CB_PTR(unit); + UI32_T idx = 0, vec = sizeof(_hal_lightning_pkt_intr_vec) / sizeof(HAL_LIGHTNING_PKT_INTR_VEC_T); + + for (idx = 0; idx < vec; idx++) + { + osal_destroyEvent(&_hal_lightning_pkt_intr_vec[idx].intr_event); + ptr_cb->intr_bitmap &= ~(_hal_lightning_pkt_intr_vec[idx].intr_reg); + } + + /* Unregister PKT interrupt functions */ + osal_mdc_registerIsr(unit, NULL, NULL); + osal_destroyIsrLock(&ptr_cb->intr_lock); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_deinitPktTxCb + * PURPOSE: + * To de-init the control block of Tx PDMA. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully de-init the control block. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_deinitPktTxCb( + const UI32_T unit) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_LIGHTNING_PKT_TX_CB_T *ptr_tx_cb = HAL_LIGHTNING_PKT_GET_TX_CB_PTR(unit); + HAL_LIGHTNING_PKT_TX_CHANNEL_T channel = 0; + + /* Deinitialize TX PDMA sub-system.*/ + for (channel = 0; channel < HAL_LIGHTNING_PKT_TX_CHANNEL_LAST; channel++) + { + _hal_lightning_pkt_deinitTxPdma(unit, channel); + } + + if (HAL_LIGHTNING_PKT_TX_WAIT_ASYNC == ptr_tx_cb->wait_mode) + { + /* Destroy the sync semaphore of txTask */ + osal_destroyEvent(&ptr_tx_cb->sync_sema); + + /* Deinitialize Tx GPD-queue (of first SW-GPD) from handleTxDoneTask to txTask */ + osal_destroySemaphore(&ptr_tx_cb->sw_queue.sema); + osal_que_destroy(&ptr_tx_cb->sw_queue.que_id); + } + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_deinitPktRxCb + * PURPOSE: + * To de-init the control block of Rx PDMA. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully de-init the control block. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_deinitPktRxCb( + const UI32_T unit) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_LIGHTNING_PKT_RX_CB_T *ptr_rx_cb = HAL_LIGHTNING_PKT_GET_RX_CB_PTR(unit); + HAL_LIGHTNING_PKT_RX_CHANNEL_T channel = 0; + UI32_T queue = 0; + + /* Deinitialize RX PDMA sub-system */ + for (channel = 0; channel < HAL_LIGHTNING_PKT_RX_CHANNEL_LAST; channel++) + { + _hal_lightning_pkt_deinitRxPdma(unit, channel); + } + + /* Destroy the sync semaphore of rxTask */ + osal_destroyEvent(&ptr_rx_cb->sync_sema); + + /* Deinitialize Rx GPD-queue (of first SW-GPD) from handleRxDoneTask to rxTask */ + for (queue = 0; queue < HAL_LIGHTNING_PKT_RX_QUEUE_NUM; queue++) + { + osal_destroySemaphore(&ptr_rx_cb->sw_queue[queue].sema); + osal_que_destroy(&ptr_rx_cb->sw_queue[queue].que_id); + } + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_deinitL1Isr + * PURPOSE: + * To de-initialize the PDMA L1 ISR configuration. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully de-initialize for the L1 ISR. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_deinitL1Isr( + const UI32_T unit) +{ + UI32_T idx = 0, vec = sizeof(_hal_lightning_pkt_intr_vec) / sizeof(HAL_LIGHTNING_PKT_INTR_VEC_T); + + for (idx = 0; idx < vec; idx++) + { + _hal_lightning_pkt_maskIntr(unit, _hal_lightning_pkt_intr_vec[idx].intr_reg); + _hal_lightning_pkt_disableIntr(unit, _hal_lightning_pkt_intr_vec[idx].intr_reg); + } + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_deinitL2Isr + * PURPOSE: + * To initialize the PDMA L2 ISR configuration. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure for the L2 ISR. + * CLX_E_OTHERS -- Configure failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_deinitL2Isr( + const UI32_T unit) +{ + HAL_LIGHTNING_PKT_L2_ISR_T isr_status = 0x0; + + HAL_LIGHTNING_PKT_SET_BITMAP(isr_status, HAL_LIGHTNING_PKT_L2_ISR_RCH0); + HAL_LIGHTNING_PKT_SET_BITMAP(isr_status, HAL_LIGHTNING_PKT_L2_ISR_RCH1); + HAL_LIGHTNING_PKT_SET_BITMAP(isr_status, HAL_LIGHTNING_PKT_L2_ISR_RCH2); + HAL_LIGHTNING_PKT_SET_BITMAP(isr_status, HAL_LIGHTNING_PKT_L2_ISR_RCH3); + HAL_LIGHTNING_PKT_SET_BITMAP(isr_status, HAL_LIGHTNING_PKT_L2_ISR_TCH0); + HAL_LIGHTNING_PKT_SET_BITMAP(isr_status, HAL_LIGHTNING_PKT_L2_ISR_TCH1); + HAL_LIGHTNING_PKT_SET_BITMAP(isr_status, HAL_LIGHTNING_PKT_L2_ISR_TCH2); + HAL_LIGHTNING_PKT_SET_BITMAP(isr_status, HAL_LIGHTNING_PKT_L2_ISR_TCH3); + HAL_LIGHTNING_PKT_SET_BITMAP(isr_status, HAL_LIGHTNING_PKT_L2_ISR_RX_QID_MAP_ERR); + HAL_LIGHTNING_PKT_SET_BITMAP(isr_status, HAL_LIGHTNING_PKT_L2_ISR_RX_FRAME_ERR); + + osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_ERR_INT_MASK_SET), + &isr_status, sizeof(UI32_T)); + + isr_status = 0x0; + osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_ERR_INT_EN), + &isr_status, sizeof(UI32_T)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_lightning_pkt_deinitPktDrv + * PURPOSE: + * To invoke the functions to de-initialize the control block for each + * PDMA subsystem. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully de-initialize the control blocks. + * CLX_E_OTHERS -- De-initialize the control blocks failed. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_lightning_pkt_deinitPktDrv( + const UI32_T unit) +{ + HAL_LIGHTNING_PKT_DRV_CB_T *ptr_cb = HAL_LIGHTNING_PKT_GET_DRV_CB_PTR(unit); + CLX_ERROR_NO_T rc = CLX_E_OK; + + if (0 == (ptr_cb->init_flag & HAL_LIGHTNING_PKT_INIT_DRV)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_RX | HAL_LIGHTNING_PKT_DBG_ERR), + "u=%u, pkt drv deinit failed, not inited\n", unit); + return (CLX_E_OK); + } + + rc = _hal_lightning_pkt_deinitL2Isr(unit); + + if (CLX_E_OK == rc) + { + rc = _hal_lightning_pkt_deinitL1Isr(unit); + } + if (CLX_E_OK == rc) + { + rc = _hal_lightning_pkt_deinitPktRxCb(unit); + } + if (CLX_E_OK == rc) + { + rc = _hal_lightning_pkt_deinitPktTxCb(unit); + } + if (CLX_E_OK == rc) + { + rc = _hal_lightning_pkt_deinitPktCb(unit); + } + + ptr_cb->init_flag &= (~HAL_LIGHTNING_PKT_INIT_DRV); + + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_COMMON, + "u=%u, pkt drv deinit done, init flag=0x%x\n", + unit, ptr_cb->init_flag); + return (rc); +} + +/* ----------------------------------------------------------------------------------- Init */ +/* FUNCTION NAME: _hal_lightning_pkt_handleTxErrStat + * PURPOSE: + * To handle the TX flow control ISR. + * INPUT: + * unit -- The unit ID + * channel -- The target channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully handle the interrpt. + * NOTES: + * + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_handleTxErrStat( + const UI32_T unit, + const HAL_LIGHTNING_PKT_TX_CHANNEL_T channel) +{ + HAL_LIGHTNING_PKT_TX_CB_T *ptr_tx_cb = HAL_LIGHTNING_PKT_GET_TX_CB_PTR(unit); + HAL_LIGHTNING_PKT_TX_PDMA_T *ptr_tx_pdma = HAL_LIGHTNING_PKT_GET_TX_PDMA_PTR(unit, channel); + CLX_IRQ_FLAGS_T irg_flags; + + if (HAL_LIGHTNING_PKT_TX_WAIT_SYNC_INTR == ptr_tx_cb->wait_mode) + { + /* Notify the TX process to make it release the channel semaphore. */ + osal_giveSemaphore(&ptr_tx_pdma->sync_intr_sema); + } + + /* Set the error flag. */ + + osal_takeIsrLock(&ptr_tx_pdma->ring_lock, &irg_flags); + ptr_tx_pdma->err_flag = TRUE; + osal_giveIsrLock(&ptr_tx_pdma->ring_lock, &irg_flags); + + osal_triggerEvent(HAL_LIGHTNING_PKT_TCH_EVENT(unit, channel)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_handleRxErrStat + * PURPOSE: + * To handle the error which occurs in RX channels. + * INPUT: + * unit -- The unit ID + * channel -- The channel where the error occurs + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully handle the error situation. + * NOTES: + * + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_handleRxErrStat( + const UI32_T unit, + const HAL_LIGHTNING_PKT_RX_CHANNEL_T channel) +{ + HAL_LIGHTNING_PKT_RX_PDMA_T *ptr_rx_pdma = HAL_LIGHTNING_PKT_GET_RX_PDMA_PTR(unit, channel); + + /* Set the error flag. */ + osal_takeSemaphore(&ptr_rx_pdma->sema, CLX_SEMAPHORE_WAIT_FOREVER); + ptr_rx_pdma->err_flag = TRUE; + osal_giveSemaphore(&ptr_rx_pdma->sema); + + osal_triggerEvent(HAL_LIGHTNING_PKT_RCH_EVENT(unit, channel)); + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_handleTxL2Isr + * PURPOSE: + * To handle the TX L2 interrupt according to the ISR status. + * INPUT: + * unit -- The unit ID + * channel -- The channel where the interrupt occurs + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully handle the L2 interrupt. + * NOTES: + * + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_handleTxL2Isr( + const UI32_T unit, + const HAL_LIGHTNING_PKT_TX_CHANNEL_T channel) +{ + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_T isr_status = 0x0; + HAL_LIGHTNING_PKT_TX_CB_T *ptr_tx_cb = HAL_LIGHTNING_PKT_GET_TX_CB_PTR(unit); + + osal_mdc_readPciReg(unit, + HAL_LIGHTNING_PKT_GET_PDMA_TCH_REG(HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_TCH_INT_STAT), channel), + &isr_status, sizeof(isr_status)); + + _hal_lightning_pkt_maskAllTxL2IsrReg(unit, channel); + + if (0 != (isr_status & HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_HWO_ERROR)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_TX), + "u=%u, txch=%u, pdma gpd hwo err\n", unit, channel); + _hal_lightning_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_HWO_ERROR); + ptr_tx_cb->cnt.channel[channel].gpd_hwo_err++; + _hal_lightning_pkt_handleTxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_CHKSM_ERROR)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_TX), + "u=%u, txch=%u, pdma gpd chksum err\n", unit, channel); + _hal_lightning_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_CHKSM_ERROR); + ptr_tx_cb->cnt.channel[channel].gpd_chksm_err++; + _hal_lightning_pkt_handleTxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_NO_OVFL_ERROR)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_TX), + "u=%u, txch=%u, pdma gpd num overflow err\n", unit, channel); + _hal_lightning_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_NO_OVFL_ERROR); + ptr_tx_cb->cnt.channel[channel].gpd_no_ovfl_err++; + _hal_lightning_pkt_handleTxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_DMA_READ_ERROR)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_TX), + "u=%u, txch=%u, pdma gpd dma read err\n", unit, channel); + _hal_lightning_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_DMA_READ_ERROR); + ptr_tx_cb->cnt.channel[channel].gpd_dma_read_err++; + _hal_lightning_pkt_handleTxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_BUF_SIZE_ERROR)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_TX), + "u=%u, txch=%u, pdma buf size err\n", unit, channel); + _hal_lightning_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_BUF_SIZE_ERROR); + ptr_tx_cb->cnt.channel[channel].buf_size_err++; + _hal_lightning_pkt_handleTxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_RUNT_ERROR)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_TX, + "u=%u, txch=%u, pdma pkt runt\n", unit, channel); + _hal_lightning_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_RUNT_ERROR); + ptr_tx_cb->cnt.channel[channel].runt_err++; + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_OVSZ_ERROR)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_TX, + "u=%u, txch=%u, pdma pkt over size\n", unit, channel); + _hal_lightning_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_OVSZ_ERROR); + ptr_tx_cb->cnt.channel[channel].ovsz_err++; + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_LEN_MISMATCH_ERROR)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_TX), + "u=%u, txch=%u, pdma len mismatch err\n", unit, channel); + _hal_lightning_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_LEN_MISMATCH_ERROR); + ptr_tx_cb->cnt.channel[channel].len_mismatch_err++; + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_PKTPL_DMA_READ_ERROR)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_TX), + "u=%u, txch=%u, pdma pkt buf dma read err\n", unit, channel); + _hal_lightning_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_PKTPL_DMA_READ_ERROR); + ptr_tx_cb->cnt.channel[channel].pktpl_dma_read_err++; + _hal_lightning_pkt_handleTxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_COS_ERROR)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_TX), + "u=%u, txch=%u, pdma tx cos err\n", unit, channel); + _hal_lightning_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_COS_ERROR); + ptr_tx_cb->cnt.channel[channel].cos_err++; + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_GT255_ERROR)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_TX), + "u=%u, txch=%u, pdma gpd num > 255 err\n", unit, channel); + _hal_lightning_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_GT255_ERROR); + ptr_tx_cb->cnt.channel[channel].gpd_gt255_err++; + _hal_lightning_pkt_handleTxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_PFC)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_TX, + "u=%u, txch=%u, pdma flow ctrl\n", unit, channel); + _hal_lightning_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_PFC); + ptr_tx_cb->cnt.channel[channel].pfc++; + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_CREDIT_UDFL_ERROR)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_TX), + "u=%u, txch=%u, pdma credit underflow err\n", unit, channel); + _hal_lightning_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_CREDIT_UDFL_ERROR); + ptr_tx_cb->cnt.channel[channel].credit_udfl_err++; + _hal_lightning_pkt_handleTxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_DMA_WRITE_ERROR)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_TX), + "u=%u, txch=%u, pdma dma write err\n", unit, channel); + _hal_lightning_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_DMA_WRITE_ERROR); + ptr_tx_cb->cnt.channel[channel].dma_write_err++; + _hal_lightning_pkt_handleTxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_STOP_CMD_CPLT)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_TX, + "u=%u, txch=%u, pdma stop done\n", unit, channel); + _hal_lightning_pkt_clearTxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_STOP_CMD_CPLT); + ptr_tx_cb->cnt.channel[channel].sw_issue_stop++; + } + if (0 != isr_status) + { + _hal_lightning_pkt_unmaskAllTxL2IsrReg(unit, channel); + } + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_handleRxL2Isr + * PURPOSE: + * To handle the RX L2 interrupt according to the ISR status. + * INPUT: + * unit -- The unit ID + * channel -- The channel where the interrupt occurs + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully handle the L2 interrupt. + * NOTES: + * + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_handleRxL2Isr( + const UI32_T unit, + const HAL_LIGHTNING_PKT_RX_CHANNEL_T channel) +{ + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_T isr_status = 0x0; + HAL_LIGHTNING_PKT_RX_CB_T *ptr_rx_cb = HAL_LIGHTNING_PKT_GET_RX_CB_PTR(unit); + + osal_mdc_readPciReg(unit, + HAL_LIGHTNING_PKT_GET_PDMA_RCH_REG(HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_RCH_INT_STAT), channel), + &isr_status, sizeof(isr_status)); + + _hal_lightning_pkt_maskAllRxL2IsrReg(unit, channel); + + if (0 != (isr_status & HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_LOW)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_RX, + "u=%u, rxch=%u, pdma avbl gpd low\n", unit, channel); + _hal_lightning_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_LOW); + ptr_rx_cb->cnt.channel[channel].avbl_gpd_low++; + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_EMPTY)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_RX, + "u=%u, rxch=%u, pdma avbl gpd empty\n", unit, channel); + _hal_lightning_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_EMPTY); + ptr_rx_cb->cnt.channel[channel].avbl_gpd_empty++; + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_ERROR)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_RX), + "u=%u, rxch=%u, pdma avbl gpd err\n", unit, channel); + _hal_lightning_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_ERROR); + ptr_rx_cb->cnt.channel[channel].avbl_gpd_err++; + _hal_lightning_pkt_handleRxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_GPD_CHKSM_ERROR)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_RX), + "u=%u, rxch=%u, pdma gpd chksum err\n", unit, channel); + _hal_lightning_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_GPD_CHKSM_ERROR); + ptr_rx_cb->cnt.channel[channel].gpd_chksm_err++; + _hal_lightning_pkt_handleRxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_DMA_READ_ERROR)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_RX), + "u=%u, rxch=%u, pdma dma read err\n", unit, channel); + _hal_lightning_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_DMA_READ_ERROR); + ptr_rx_cb->cnt.channel[channel].dma_read_err++; + _hal_lightning_pkt_handleRxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_DMA_WRITE_ERROR)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_RX), + "u=%u, rxch=%u, pdma dma write err\n", unit, channel); + _hal_lightning_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_DMA_WRITE_ERROR); + ptr_rx_cb->cnt.channel[channel].dma_write_err++; + _hal_lightning_pkt_handleRxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_STOP_CMD_CPLT)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_RX, + "u=%u, rxch=%u, pdma stop done\n", unit, channel); + _hal_lightning_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_STOP_CMD_CPLT); + ptr_rx_cb->cnt.channel[channel].sw_issue_stop++; + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_GPD_GT255_ERROR)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_RX), + "u=%u, rxch=%u, pdma gpd num > 255 err\n", unit, channel); + _hal_lightning_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_GPD_GT255_ERROR); + ptr_rx_cb->cnt.channel[channel].gpd_gt255_err++; + _hal_lightning_pkt_handleRxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_TOD_UNINIT)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_RX), + "u=%u, rxch=%u, pdma tod ununit err\n", unit, channel); + _hal_lightning_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_TOD_UNINIT); + ptr_rx_cb->cnt.channel[channel].tod_uninit++; + _hal_lightning_pkt_handleRxErrStat(unit, channel); + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_PKT_ERROR_DROP)) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_RX), + "u=%u, rxch=%u, pdma pkt err drop\n", unit, channel); + _hal_lightning_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_PKT_ERROR_DROP); + ptr_rx_cb->cnt.channel[channel].pkt_err_drop++; + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_UDSZ_DROP)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_RX, + "u=%u, rxch=%u, pdma pkt under size\n", unit, channel); + _hal_lightning_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_UDSZ_DROP); + ptr_rx_cb->cnt.channel[channel].udsz_drop++; + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_OVSZ_DROP)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_RX, + "u=%u, rxch=%u, pdma pkt over size\n", unit, channel); + _hal_lightning_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_OVSZ_DROP); + ptr_rx_cb->cnt.channel[channel].ovsz_drop++; + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_CMDQ_OVF_DROP)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_RX, + "u=%u, rxch=%u, pdma cmdq overflow\n", unit, channel); + _hal_lightning_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_CMDQ_OVF_DROP); + ptr_rx_cb->cnt.channel[channel].cmdq_ovf_drop++; + } + if (0 != (isr_status & HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_FIFO_OVF_DROP)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_RX, + "u=%u, rxch=%u, pdma fifo overflow\n", unit, channel); + _hal_lightning_pkt_clearRxL2IsrStatusReg(unit, channel, HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_FIFO_OVF_DROP); + ptr_rx_cb->cnt.channel[channel].fifo_ovf_drop++; + } + if (0 != isr_status) + { + _hal_lightning_pkt_unmaskAllRxL2IsrReg(unit, channel); + } + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_handleErrorTask + * PURPOSE: + * To invoke the corresponding handler for the L2 interrupts. + * INPUT: + * ptr_argv -- The unit ID + * OUTPUT: + * None + * RETURN: + * None + * NOTES: + * None + */ +static void +_hal_lightning_pkt_handleErrorTask( + void *ptr_argv) +{ + UI32_T unit = (UI32_T)((CLX_HUGE_T)ptr_argv); + HAL_LIGHTNING_PKT_L2_ISR_T isr_status = 0x0; + + osal_initRunThread(); + do + { + /* receive Error-ISR */ + osal_waitEvent(HAL_LIGHTNING_PKT_ERR_EVENT(unit)); + if (CLX_E_OK != osal_isRunThread()) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_COMMON, + "u=%u, err task destroyed\n", unit); + break; /* deinit-thread */ + } + + osal_mdc_readPciReg(unit, + HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_ERR_INT_STAT), + &isr_status, sizeof(UI32_T)); + + if (0 != (HAL_LIGHTNING_PKT_L2_ISR_RCH0 & isr_status)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_COMMON, "u=%u, rxch=0, rcv err isr, status=0x%x\n", + unit, isr_status); + _hal_lightning_pkt_handleRxL2Isr(unit, HAL_LIGHTNING_PKT_RX_CHANNEL_0); + } + if (0 != (HAL_LIGHTNING_PKT_L2_ISR_RCH1 & isr_status)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_COMMON, "u=%u, rxch=1, rcv err isr, status=0x%x\n", + unit, isr_status); + _hal_lightning_pkt_handleRxL2Isr(unit, HAL_LIGHTNING_PKT_RX_CHANNEL_1); + } + if (0 != (HAL_LIGHTNING_PKT_L2_ISR_RCH2 & isr_status)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_COMMON, "u=%u, rxch=2, rcv err isr, status=0x%x\n", + unit, isr_status); + _hal_lightning_pkt_handleRxL2Isr(unit, HAL_LIGHTNING_PKT_RX_CHANNEL_2); + } + if (0 != (HAL_LIGHTNING_PKT_L2_ISR_RCH3 & isr_status)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_COMMON, "u=%u, rxch=3, rcv err isr, status=0x%x\n", + unit, isr_status); + _hal_lightning_pkt_handleRxL2Isr(unit, HAL_LIGHTNING_PKT_RX_CHANNEL_3); + } + if (0 != (HAL_LIGHTNING_PKT_L2_ISR_TCH0 & isr_status)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_COMMON, "u=%u, txch=0, rcv err isr, status=0x%x\n", + unit, isr_status); + _hal_lightning_pkt_handleTxL2Isr(unit, HAL_LIGHTNING_PKT_TX_CHANNEL_0); + } + if (0 != (HAL_LIGHTNING_PKT_L2_ISR_TCH1 & isr_status)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_COMMON, "u=%u, txch=1, rcv err isr, status=0x%x\n", + unit, isr_status); + _hal_lightning_pkt_handleTxL2Isr(unit, HAL_LIGHTNING_PKT_TX_CHANNEL_1); + } + if (0 != (HAL_LIGHTNING_PKT_L2_ISR_TCH2 & isr_status)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_COMMON, "u=%u, txch=2, rcv err isr, status=0x%x\n", + unit, isr_status); + _hal_lightning_pkt_handleTxL2Isr(unit, HAL_LIGHTNING_PKT_TX_CHANNEL_2); + } + if (0 != (HAL_LIGHTNING_PKT_L2_ISR_TCH3 & isr_status)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_COMMON, "u=%u, txch=3, rcv err isr, status=0x%x\n", + unit, isr_status); + _hal_lightning_pkt_handleTxL2Isr(unit, HAL_LIGHTNING_PKT_TX_CHANNEL_3); + } + if (0 != (HAL_LIGHTNING_PKT_L2_ISR_RX_QID_MAP_ERR & isr_status)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_COMMON, "u=%u, rcv rx qid map err isr, status=0x%x\n", + unit, isr_status); + } + if (0 != (HAL_LIGHTNING_PKT_L2_ISR_RX_FRAME_ERR & isr_status)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_COMMON, "u=%u, rcv rx frame err isr, status=0x%x\n", + unit, isr_status); + } + if (0 != isr_status) + { + osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_ERR_INT_CLR), + &isr_status, sizeof(UI32_T)); + + _hal_lightning_pkt_unmaskIntr(unit, HAL_LIGHTNING_PKT_ERR_REG(unit)); + } + + } while (CLX_E_OK == osal_isRunThread()); + osal_exitRunThread(); +} + +/* FUNCTION NAME: _hal_lightning_pkt_handleTxDoneTask + * PURPOSE: + * To handle the TX done interrupt for the specified TX channel. + * INPUT: + * ptr_argv -- The unit ID and channel ID + * OUTPUT: + * None + * RETURN: + * None + * NOTES: + * None + */ +static void +_hal_lightning_pkt_handleTxDoneTask( + void *ptr_argv) +{ + /* cookie or index */ + UI32_T unit = ((HAL_LIGHTNING_PKT_ISR_COOKIE_T *)ptr_argv)->unit; + HAL_LIGHTNING_PKT_TX_CHANNEL_T channel = (HAL_LIGHTNING_PKT_TX_CHANNEL_T) + ((HAL_LIGHTNING_PKT_ISR_COOKIE_T *)ptr_argv)->channel; + /* control block */ + HAL_LIGHTNING_PKT_TX_CB_T *ptr_tx_cb = HAL_LIGHTNING_PKT_GET_TX_CB_PTR(unit); + HAL_LIGHTNING_PKT_TX_PDMA_T *ptr_tx_pdma = HAL_LIGHTNING_PKT_GET_TX_PDMA_PTR(unit, channel); + volatile HAL_LIGHTNING_PKT_TX_GPD_T *ptr_tx_gpd = NULL; + UI32_T first_gpd_idx = 0; /* To record the first GPD */ + UI32_T loop_cnt = 0; + CLX_IRQ_FLAGS_T irg_flags; + unsigned long timeout = 0; + UI32_T bulk_pkt_cnt = 0, idx; + + osal_initRunThread(); + do + { + /* receive Tx-Done-ISR */ + osal_waitEvent(HAL_LIGHTNING_PKT_TCH_EVENT(unit, channel)); + if (CLX_E_OK != osal_isRunThread()) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_TX, + "u=%u, txch=%u, tx done task destroyed\n", unit, channel); + break; /* deinit-thread */ + } + + /* protect Tx PDMA + * for sync-intr, the sema is locked by sendGpd + */ + if (HAL_LIGHTNING_PKT_TX_WAIT_SYNC_INTR != ptr_tx_cb->wait_mode) + { + osal_takeIsrLock(&ptr_tx_pdma->ring_lock, &irg_flags); + } + + loop_cnt = ptr_tx_pdma->used_gpd_num; + while (loop_cnt > 0) + { + ptr_tx_gpd = HAL_LIGHTNING_PKT_GET_TX_GPD_PTR(unit, channel, ptr_tx_pdma->free_idx); + osal_dma_invalidateCache((void *)ptr_tx_gpd, sizeof(HAL_LIGHTNING_PKT_TX_GPD_T)); + + /* If hwo=HW, it might be: + * 1. err_flag=TRUE -> HW breakdown -> enque and recover -> break + * 2. err_flag=FALSE -> HW busy -> break + */ + if (HAL_LIGHTNING_PKT_HWO_HW_OWN == ptr_tx_gpd->hwo) + { + if (TRUE == ptr_tx_pdma->err_flag) + { + /* flush the incomplete Tx packet */ + if (HAL_LIGHTNING_PKT_TX_WAIT_ASYNC == ptr_tx_cb->wait_mode) + { + for (idx = 0; idx < ptr_tx_pdma->gpd_num; idx++) + { + if (NULL != ptr_tx_pdma->pptr_sw_gpd_ring[idx]) + { + ptr_tx_pdma->pptr_sw_gpd_bulk[bulk_pkt_cnt] + = ptr_tx_pdma->pptr_sw_gpd_ring[idx]; + ptr_tx_pdma->pptr_sw_gpd_ring[idx] = NULL; + bulk_pkt_cnt++; + } + } + } + + /* do error recover */ + first_gpd_idx = 0; + if (CLX_E_OK == _hal_lightning_pkt_recoverTxPdma(unit, channel)) + { + ptr_tx_pdma->err_flag = FALSE; + ptr_tx_cb->cnt.channel[channel].err_recover++; + } + else + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_TX | HAL_LIGHTNING_PKT_DBG_ERR), + "u=%u, txch=%u, err recover failed\n", + unit, channel); + } + } + else + { + } + break; + } + + if (HAL_LIGHTNING_PKT_TX_WAIT_ASYNC == ptr_tx_cb->wait_mode) + { + /* If hwo=SW and ch=0, record the head of sw gpd in bulk buf */ + if (HAL_LIGHTNING_PKT_CH_LAST_GPD == ptr_tx_gpd->ch) + { + ptr_tx_pdma->pptr_sw_gpd_bulk[bulk_pkt_cnt] + = ptr_tx_pdma->pptr_sw_gpd_ring[first_gpd_idx]; + + bulk_pkt_cnt++; + ptr_tx_pdma->pptr_sw_gpd_ring[first_gpd_idx] = NULL; + + /* next SW-GPD must be the head of another PKT->SW-GPD */ + first_gpd_idx = ptr_tx_pdma->free_idx + 1; + first_gpd_idx %= ptr_tx_pdma->gpd_num; + } + } + + if (HAL_LIGHTNING_PKT_ECC_ERROR_OCCUR == ptr_tx_gpd->ecce) + { + ptr_tx_cb->cnt.channel[channel].ecc_err++; + } + + /* update Tx PDMA */ + ptr_tx_pdma->free_idx++; + ptr_tx_pdma->free_idx %= ptr_tx_pdma->gpd_num; + ptr_tx_pdma->used_gpd_num--; + ptr_tx_pdma->free_gpd_num++; + loop_cnt--; + } + + /* let the netdev resume Tx */ + _hal_lightning_pkt_resumeAllIntf(unit); + + /* update ISR and counter */ + ptr_tx_cb->cnt.channel[channel].tx_done++; + + _hal_lightning_pkt_unmaskIntr(unit, HAL_LIGHTNING_PKT_TCH_REG(unit, channel)); + + if (HAL_LIGHTNING_PKT_TX_WAIT_SYNC_INTR != ptr_tx_cb->wait_mode) + { + osal_giveIsrLock(&ptr_tx_pdma->ring_lock, &irg_flags); + } + else + { + osal_giveSemaphore(&ptr_tx_pdma->sync_intr_sema); + } + + /* enque packet after releasing the spinlock */ + _hal_lightning_pkt_txEnQueueBulk(unit, channel, bulk_pkt_cnt); + bulk_pkt_cnt = 0; + + /* prevent this task from executing too long */ + if (!(time_before(jiffies, timeout))) + { + schedule(); + timeout = jiffies + 1; /* continuously free tx descriptor for 1 tick */ + } + + } while (CLX_E_OK == osal_isRunThread()); + osal_exitRunThread(); +} + +/* FUNCTION NAME: _hal_lightning_pkt_handleRxDoneTask + * PURPOSE: + * To handle the RX done interrupt for the specified RX channel. + * INPUT: + * ptr_argv -- The unit ID and channel ID + * OUTPUT: + * None + * RETURN: + * None + * NOTES: + * None + */ +static void +_hal_lightning_pkt_handleRxDoneTask( + void *ptr_argv) +{ + /* cookie or index */ + UI32_T unit = ((HAL_LIGHTNING_PKT_ISR_COOKIE_T *)ptr_argv)->unit; + HAL_LIGHTNING_PKT_RX_CHANNEL_T channel = (HAL_LIGHTNING_PKT_RX_CHANNEL_T) + ((HAL_LIGHTNING_PKT_ISR_COOKIE_T *)ptr_argv)->channel; + + /* control block */ + HAL_LIGHTNING_PKT_RX_CB_T *ptr_rx_cb = HAL_LIGHTNING_PKT_GET_RX_CB_PTR(unit); + HAL_LIGHTNING_PKT_RX_PDMA_T *ptr_rx_pdma = HAL_LIGHTNING_PKT_GET_RX_PDMA_PTR(unit, channel); + volatile HAL_LIGHTNING_PKT_RX_GPD_T *ptr_rx_gpd = NULL; + + BOOL_T first = TRUE; + BOOL_T last = FALSE; + HAL_LIGHTNING_PKT_RX_SW_GPD_T *ptr_sw_gpd = NULL; + HAL_LIGHTNING_PKT_RX_SW_GPD_T *ptr_sw_first_gpd = NULL; + UI32_T loop_cnt = 0; + unsigned long timeout = 0; + + osal_initRunThread(); + do + { + /* receive Rx-Done-ISR */ + osal_waitEvent(HAL_LIGHTNING_PKT_RCH_EVENT(unit, channel)); + if (CLX_E_OK != osal_isRunThread()) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_RX, + "u=%u, rxch=%u, rx done task destroyed\n", unit, channel); + break; /* deinit-thread */ + } + + /* check if Rx-system is inited */ + if (0 == ptr_rx_cb->buf_len) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_RX | HAL_LIGHTNING_PKT_DBG_ERR), + "u=%u, rxch=%u, rx gpd buf len=0\n", + unit, channel); + continue; + } + + /* protect Rx PDMA */ + osal_takeSemaphore(&ptr_rx_pdma->sema, CLX_SEMAPHORE_WAIT_FOREVER); + loop_cnt = ptr_rx_pdma->gpd_num; + while (loop_cnt > 0) + { + ptr_rx_gpd = HAL_LIGHTNING_PKT_GET_RX_GPD_PTR(unit, channel, ptr_rx_pdma->cur_idx); + osal_dma_invalidateCache((void *)ptr_rx_gpd, sizeof(HAL_LIGHTNING_PKT_RX_GPD_T)); + + /* If hwo=HW, it might be: + * 1. err_flag=TRUE -> HW breakdown -> enque and recover -> break + * 2. err_flag=FALSE -> HW busy -> break + */ + if (HAL_LIGHTNING_PKT_HWO_HW_OWN == ptr_rx_gpd->hwo) + { + if (TRUE == ptr_rx_pdma->err_flag) + { + /* free the last incomplete Rx packet */ + if ((NULL != ptr_sw_first_gpd) && + (NULL != ptr_sw_gpd)) + { + ptr_sw_gpd->ptr_next = NULL; + ptr_sw_first_gpd->rx_complete = FALSE; + _hal_lightning_pkt_rxEnQueue(unit, channel, ptr_sw_first_gpd); + ptr_sw_first_gpd = NULL; + } + + /* do error recover */ + first = TRUE; + last = FALSE; + if (CLX_E_OK == _hal_lightning_pkt_recoverRxPdma(unit, channel)) + { + ptr_rx_pdma->err_flag = FALSE; + ptr_rx_cb->cnt.channel[channel].err_recover++; + } + else + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_RX | HAL_LIGHTNING_PKT_DBG_ERR), + "u=%u, rxch=%u, err recover failed\n", + unit, channel); + } + } + else + { + } + break; + } + + /* Move HW-GPD to SW-GPD and append to a link-list */ + if (TRUE == first) + { + ptr_sw_first_gpd = (HAL_LIGHTNING_PKT_RX_SW_GPD_T *)osal_alloc(sizeof(HAL_LIGHTNING_PKT_RX_SW_GPD_T)); + ptr_sw_gpd = ptr_sw_first_gpd; + if (NULL != ptr_sw_gpd) + { + memcpy(&ptr_sw_gpd->rx_gpd, (void *)ptr_rx_gpd, sizeof(HAL_LIGHTNING_PKT_RX_GPD_T)); + first = FALSE; + } + else + { + ptr_rx_cb->cnt.no_memory++; + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_RX | HAL_LIGHTNING_PKT_DBG_ERR), + "u=%u, rxch=%u, alloc 1st sw gpd failed, size=%zu\n", + unit, channel, sizeof(HAL_LIGHTNING_PKT_RX_SW_GPD_T)); + break; + } + } + else + { + ptr_sw_gpd->ptr_next = (HAL_LIGHTNING_PKT_RX_SW_GPD_T *)osal_alloc(sizeof(HAL_LIGHTNING_PKT_RX_SW_GPD_T)); + ptr_sw_gpd = ptr_sw_gpd->ptr_next; + if (NULL != ptr_sw_gpd) + { + memcpy(&ptr_sw_gpd->rx_gpd, (void *)ptr_rx_gpd, sizeof(HAL_LIGHTNING_PKT_RX_GPD_T)); + } + else + { + ptr_rx_cb->cnt.no_memory++; + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_RX | HAL_LIGHTNING_PKT_DBG_ERR), + "u=%u, rxch=%u, alloc mid sw gpd failed, size=%zu\n", + unit, channel, sizeof(HAL_LIGHTNING_PKT_RX_SW_GPD_T)); + break; + } + } + + ptr_sw_gpd->ptr_cookie = ptr_rx_pdma->pptr_skb_ring[ptr_rx_pdma->cur_idx]; + + /* If hwo=SW and ch=0, enque SW-GPD and signal rxTask */ + if (HAL_LIGHTNING_PKT_CH_LAST_GPD == ptr_rx_gpd->ch) + { + last = TRUE; + } + + /* If hwo=SW and ch=*, re-alloc-buf and resume */ + while (CLX_E_OK != _hal_lightning_pkt_allocRxPayloadBuf(unit, channel, ptr_rx_pdma->cur_idx)) + { + ptr_rx_cb->cnt.no_memory++; + HAL_LIGHTNING_PKT_ALLOC_MEM_RETRY_SLEEP(); + } + ptr_rx_gpd->ioc = HAL_LIGHTNING_PKT_IOC_HAS_INTR; + ptr_rx_gpd->hwo = HAL_LIGHTNING_PKT_HWO_HW_OWN; + osal_dma_flushCache((void *)ptr_rx_gpd, sizeof(HAL_LIGHTNING_PKT_RX_GPD_T)); + + /* Enque the SW-GPD to rxTask */ + if (TRUE == last) + { + ptr_sw_gpd->ptr_next = NULL; + ptr_sw_first_gpd->rx_complete = TRUE; + _hal_lightning_pkt_rxEnQueue(unit, channel, ptr_sw_first_gpd); + ptr_sw_first_gpd = NULL; + + /* To rebuild the SW GPD link list */ + first = TRUE; + last = FALSE; + } + + _hal_lightning_pkt_resumeRxChannelReg(unit, channel, 1); + + /* update Rx PDMA */ + ptr_rx_pdma->cur_idx++; + ptr_rx_pdma->cur_idx %= ptr_rx_pdma->gpd_num; + loop_cnt--; + } + + osal_giveSemaphore(&ptr_rx_pdma->sema); + + /* update ISR and counter */ + ptr_rx_cb->cnt.channel[channel].rx_done++; + + _hal_lightning_pkt_unmaskIntr(unit, HAL_LIGHTNING_PKT_RCH_REG(unit, channel)); + + /* prevent this task from executing too long */ + if (!(time_before(jiffies, timeout))) + { + schedule(); + timeout = jiffies + 1; /* continuously rx for 1 tick */ + } + + } while (CLX_E_OK == osal_isRunThread()); + osal_exitRunThread(); +} + +static void +_hal_lightning_pkt_net_dev_tx_callback( + const UI32_T unit, + HAL_LIGHTNING_PKT_TX_SW_GPD_T *ptr_sw_gpd, + struct sk_buff *ptr_skb) +{ + CLX_ADDR_T phy_addr = 0; + + /* unmap dma */ + phy_addr = CLX_ADDR_32_TO_64(ptr_sw_gpd->tx_gpd.data_buf_addr_hi, ptr_sw_gpd->tx_gpd.data_buf_addr_lo); + osal_skb_unmapDma(phy_addr, ptr_skb->len, DMA_TO_DEVICE); + + /* free skb */ + osal_skb_free(ptr_skb); + + /* free gpd */ + osal_free(ptr_sw_gpd); +} + +/* FUNCTION NAME: hal_lightning_pkt_initTask + * PURPOSE: + * To initialize the Task for packet module. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully dinitialize the control block. + * CLX_E_OTHERS -- Initialize the control block failed. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_lightning_pkt_initTask( + const UI32_T unit) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_LIGHTNING_PKT_DRV_CB_T *ptr_cb = HAL_LIGHTNING_PKT_GET_DRV_CB_PTR(unit); + HAL_LIGHTNING_PKT_TX_CB_T *ptr_tx_cb = HAL_LIGHTNING_PKT_GET_TX_CB_PTR(unit); + HAL_LIGHTNING_PKT_RX_CB_T *ptr_rx_cb = HAL_LIGHTNING_PKT_GET_RX_CB_PTR(unit); + UI32_T channel = 0; + + if (0 != (ptr_cb->init_flag & HAL_LIGHTNING_PKT_INIT_TASK)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_ERR, + "u=%u, pkt task init failed, not inited\n", unit); + return (rc); + } + + /* Init handleErrorTask */ + rc = osal_createThread("ERROR", HAL_DFLT_CFG_PKT_ERROR_ISR_THREAD_STACK, + HAL_DFLT_CFG_PKT_ERROR_ISR_THREAD_PRI, _hal_lightning_pkt_handleErrorTask, + (void *)((CLX_HUGE_T)unit), &ptr_cb->err_task_id); + + /* Init handleTxDoneTask */ + for (channel = 0; ((channel < HAL_LIGHTNING_PKT_TX_CHANNEL_LAST) && (CLX_E_OK == rc)); channel++) + { + ptr_tx_cb->isr_task_cookie[channel].unit = unit; + ptr_tx_cb->isr_task_cookie[channel].channel = channel; + + rc = osal_createThread("TX_ISR", HAL_DFLT_CFG_PKT_TX_ISR_THREAD_STACK, + HAL_DFLT_CFG_PKT_TX_ISR_THREAD_PRI, _hal_lightning_pkt_handleTxDoneTask, + (void *)&ptr_tx_cb->isr_task_cookie[channel], + &ptr_tx_cb->isr_task_id[channel]); + } + + /* Init handleRxDoneTask */ + for (channel = 0; ((channel < HAL_LIGHTNING_PKT_RX_CHANNEL_LAST) && (CLX_E_OK == rc)); channel++) + { + ptr_rx_cb->isr_task_cookie[channel].unit = unit; + ptr_rx_cb->isr_task_cookie[channel].channel = channel; + + rc = osal_createThread("RX_ISR", HAL_DFLT_CFG_PKT_RX_ISR_THREAD_STACK, + HAL_DFLT_CFG_PKT_RX_ISR_THREAD_PRI, _hal_lightning_pkt_handleRxDoneTask, + (void *)&ptr_rx_cb->isr_task_cookie[channel], + &ptr_rx_cb->isr_task_id[channel]); + } + + /* Init txTask */ + if (HAL_LIGHTNING_PKT_TX_WAIT_ASYNC == ptr_tx_cb->wait_mode) + { + ptr_tx_cb->running = TRUE; + } + + ptr_cb->init_flag |= HAL_LIGHTNING_PKT_INIT_TASK; + + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_COMMON, + "u=%u, pkt task init done, init flag=0x%x\n", unit, ptr_cb->init_flag); + + /* For some specail case in warmboot, the netifs are not destroyed during sdk deinit + * but stopped, here we need to resume them with the original carrier status + */ + _hal_lightning_pkt_resumeAllIntf(unit); + + ptr_tx_cb->net_tx_allowed = TRUE; + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_initTxPdma + * PURPOSE: + * To initialize the TX PDMA. + * INPUT: + * unit -- The unit ID + * channel -- The target Tx channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully initialize the TX PDMA. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_initTxPdma( + const UI32_T unit, + const HAL_LIGHTNING_PKT_TX_CHANNEL_T channel) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_LIGHTNING_PKT_TX_CB_T *ptr_tx_cb = HAL_LIGHTNING_PKT_GET_TX_CB_PTR(unit); + HAL_LIGHTNING_PKT_TX_PDMA_T *ptr_tx_pdma = HAL_LIGHTNING_PKT_GET_TX_PDMA_PTR(unit, channel); + CLX_IRQ_FLAGS_T irg_flags; + + /* Isr lock to protect Tx PDMA */ + osal_createIsrLock("TCH_LCK", &ptr_tx_pdma->ring_lock); + + if (HAL_LIGHTNING_PKT_TX_WAIT_SYNC_INTR == ptr_tx_cb->wait_mode) + { + /* Sync semaphore to signal sendTxPacket */ + osal_createSemaphore("TCH_SYN", CLX_SEMAPHORE_SYNC, &ptr_tx_pdma->sync_intr_sema); + } + + /* Reset Tx PDMA */ + osal_takeIsrLock(&ptr_tx_pdma->ring_lock, &irg_flags); + + ptr_tx_pdma->used_idx = 0; + ptr_tx_pdma->free_idx = 0; + ptr_tx_pdma->used_gpd_num = 0; + ptr_tx_pdma->free_gpd_num = HAL_DFLT_CFG_PKT_TX_GPD_NUM; + ptr_tx_pdma->gpd_num = HAL_DFLT_CFG_PKT_TX_GPD_NUM; + + /* Prepare the HW-GPD ring */ + ptr_tx_pdma->ptr_gpd_start_addr = (HAL_LIGHTNING_PKT_TX_GPD_T *)osal_dma_alloc( + (ptr_tx_pdma->gpd_num + 1) * sizeof(HAL_LIGHTNING_PKT_TX_GPD_T)); + + if (NULL != ptr_tx_pdma->ptr_gpd_start_addr) + { + osal_memset(ptr_tx_pdma->ptr_gpd_start_addr, 0x0, + (ptr_tx_pdma->gpd_num + 1) * sizeof(HAL_LIGHTNING_PKT_TX_GPD_T)); + + ptr_tx_pdma->ptr_gpd_align_start_addr = (HAL_LIGHTNING_PKT_TX_GPD_T *)HAL_LIGHTNING_PKT_PDMA_ALIGN_ADDR( + (CLX_HUGE_T)ptr_tx_pdma->ptr_gpd_start_addr, sizeof(HAL_LIGHTNING_PKT_TX_GPD_T)); + + rc = _hal_lightning_pkt_initTxPdmaRing(unit, channel); + if (CLX_E_OK == rc) + { + _hal_lightning_pkt_startTxChannelReg(unit, channel, 0); + } + } + else + { + ptr_tx_cb->cnt.no_memory++; + rc = CLX_E_NO_MEMORY; + } + + if (HAL_LIGHTNING_PKT_TX_WAIT_ASYNC == ptr_tx_cb->wait_mode) + { + if (CLX_E_OK == rc) + { + /* Prepare the SW-GPD ring */ + ptr_tx_pdma->pptr_sw_gpd_ring = (HAL_LIGHTNING_PKT_TX_SW_GPD_T **)osal_alloc( + ptr_tx_pdma->gpd_num * sizeof(HAL_LIGHTNING_PKT_TX_SW_GPD_T *)); + + if (NULL != ptr_tx_pdma->pptr_sw_gpd_ring) + { + osal_memset(ptr_tx_pdma->pptr_sw_gpd_ring, 0x0, + ptr_tx_pdma->gpd_num * sizeof(HAL_LIGHTNING_PKT_TX_SW_GPD_T *)); + } + else + { + ptr_tx_cb->cnt.no_memory++; + rc = CLX_E_NO_MEMORY; + } + + /* a temp buffer to store the 1st sw gpd for each packet to be enque + * we cannot enque packet before release a spinlock + */ + ptr_tx_pdma->pptr_sw_gpd_bulk = (HAL_LIGHTNING_PKT_TX_SW_GPD_T **)osal_alloc( + ptr_tx_pdma->gpd_num * sizeof(HAL_LIGHTNING_PKT_TX_SW_GPD_T *)); + + if (NULL != ptr_tx_pdma->pptr_sw_gpd_bulk) + { + osal_memset(ptr_tx_pdma->pptr_sw_gpd_bulk, 0x0, + ptr_tx_pdma->gpd_num * sizeof(HAL_LIGHTNING_PKT_TX_SW_GPD_T *)); + } + else + { + ptr_tx_cb->cnt.no_memory++; + rc = CLX_E_NO_MEMORY; + } + } + } + + osal_giveIsrLock(&ptr_tx_pdma->ring_lock, &irg_flags); + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_initRxPdma + * PURPOSE: + * To initialize the RX PDMA. + * INPUT: + * unit -- The unit ID + * channel -- The target Rx channel + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully initialize the RX PDMA. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_initRxPdma( + const UI32_T unit, + const HAL_LIGHTNING_PKT_RX_CHANNEL_T channel) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_LIGHTNING_PKT_RX_CB_T *ptr_rx_cb = HAL_LIGHTNING_PKT_GET_RX_CB_PTR(unit); + HAL_LIGHTNING_PKT_RX_PDMA_T *ptr_rx_pdma = HAL_LIGHTNING_PKT_GET_RX_PDMA_PTR(unit, channel); + + /* Binary semaphore to protect Rx PDMA */ + osal_createSemaphore("RCH_LCK", CLX_SEMAPHORE_BINARY, &ptr_rx_pdma->sema); + + /* Reset Rx PDMA */ + osal_takeSemaphore(&ptr_rx_pdma->sema, CLX_SEMAPHORE_WAIT_FOREVER); + ptr_rx_pdma->cur_idx = 0; + ptr_rx_pdma->gpd_num = HAL_DFLT_CFG_PKT_RX_GPD_NUM; + + /* Prepare the HW-GPD ring */ + ptr_rx_pdma->ptr_gpd_start_addr = (HAL_LIGHTNING_PKT_RX_GPD_T *)osal_dma_alloc( + (ptr_rx_pdma->gpd_num + 1) * sizeof(HAL_LIGHTNING_PKT_RX_GPD_T)); + + if (NULL != ptr_rx_pdma->ptr_gpd_start_addr) + { + osal_memset(ptr_rx_pdma->ptr_gpd_start_addr, 0, + (ptr_rx_pdma->gpd_num + 1) * sizeof(HAL_LIGHTNING_PKT_RX_GPD_T)); + + ptr_rx_pdma->ptr_gpd_align_start_addr = (HAL_LIGHTNING_PKT_RX_GPD_T *)HAL_LIGHTNING_PKT_PDMA_ALIGN_ADDR( + (CLX_HUGE_T)ptr_rx_pdma->ptr_gpd_start_addr, sizeof(HAL_LIGHTNING_PKT_RX_GPD_T)); + + /* will initRxPdmaRingBuf and start RCH after setRxConfig */ + rc = _hal_lightning_pkt_initRxPdmaRing(unit, channel); + } + else + { + ptr_rx_cb->cnt.no_memory++; + rc = CLX_E_NO_MEMORY; + } + + if (CLX_E_OK == rc) + { + /* Prepare the SKB ring */ + ptr_rx_pdma->pptr_skb_ring = (struct sk_buff **)osal_alloc( + ptr_rx_pdma->gpd_num * sizeof(struct sk_buff *)); + + if (NULL != ptr_rx_pdma->pptr_skb_ring) + { + osal_memset(ptr_rx_pdma->pptr_skb_ring, 0x0, + ptr_rx_pdma->gpd_num * sizeof(struct sk_buff *)); + } + else + { + ptr_rx_cb->cnt.no_memory++; + rc = CLX_E_NO_MEMORY; + } + } + + osal_giveSemaphore(&ptr_rx_pdma->sema); + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_initPktCb + * PURPOSE: + * To initialize the control block of Drv. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully initialize the control block. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_initPktCb( + const UI32_T unit) +{ + HAL_LIGHTNING_PKT_DRV_CB_T *ptr_cb = HAL_LIGHTNING_PKT_GET_DRV_CB_PTR(unit); + UI32_T idx = 0, vec = sizeof(_hal_lightning_pkt_intr_vec) / sizeof(HAL_LIGHTNING_PKT_INTR_VEC_T); + + osal_memset(ptr_cb, 0x0, sizeof(HAL_LIGHTNING_PKT_DRV_CB_T)); + + /* Register PKT interrupt functions */ + osal_createIsrLock("ISR_LOCK", &ptr_cb->intr_lock); + osal_mdc_registerIsr(unit, _hal_lightning_pkt_dispatcher, (void *)((CLX_HUGE_T)unit)); + + for (idx = 0; idx < vec; idx++) + { + osal_createEvent("ISR_EVENT", &_hal_lightning_pkt_intr_vec[idx].intr_event); + ptr_cb->intr_bitmap |= (_hal_lightning_pkt_intr_vec[idx].intr_reg); + } + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_initPktTxCb + * PURPOSE: + * To initialize the control block of Rx PDMA. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully initialize the control block. + * CLX_E_OTHERS -- Configure failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_initPktTxCb( + const UI32_T unit) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_LIGHTNING_PKT_TX_CB_T *ptr_tx_cb = HAL_LIGHTNING_PKT_GET_TX_CB_PTR(unit); + HAL_LIGHTNING_PKT_TX_CHANNEL_T channel = 0; + + osal_memset(ptr_tx_cb, 0x0, sizeof(HAL_LIGHTNING_PKT_TX_CB_T)); + + ptr_tx_cb->wait_mode = HAL_LIGHTNING_PKT_TX_WAIT_MODE; + + if (HAL_LIGHTNING_PKT_TX_WAIT_ASYNC == ptr_tx_cb->wait_mode) + { + /* Sync semaphore to signal txTask */ + osal_createEvent("TX_SYNC", &ptr_tx_cb->sync_sema); + + /* Initialize Tx GPD-queue (of first SW-GPD) from handleTxDoneTask to txTask */ + ptr_tx_cb->sw_queue.len = HAL_DFLT_CFG_PKT_TX_QUEUE_LEN; + ptr_tx_cb->sw_queue.weight = 0; + + osal_createSemaphore("TX_QUE", CLX_SEMAPHORE_BINARY, &ptr_tx_cb->sw_queue.sema); + osal_que_create(&ptr_tx_cb->sw_queue.que_id, ptr_tx_cb->sw_queue.len); + } + else if (HAL_LIGHTNING_PKT_TX_WAIT_SYNC_POLL == ptr_tx_cb->wait_mode) + { + /* Disable TX done ISR. */ + for (channel = 0; channel < HAL_LIGHTNING_PKT_TX_CHANNEL_LAST; channel++) + { + _hal_lightning_pkt_disableIntr(unit, HAL_LIGHTNING_PKT_TCH_REG(unit, channel)); + } + } + + /* Init Tx PDMA */ + for (channel = 0; ((channel < HAL_LIGHTNING_PKT_TX_CHANNEL_LAST) && (CLX_E_OK == rc)); channel++) + { + rc = _hal_lightning_pkt_initTxPdma(unit, channel); + } + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_initPktRxCb + * PURPOSE: + * To initialize the control block of Rx PDMA. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully initialize the control block. + * CLX_E_OTHERS -- Configure failed. + * NOTES: + * + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_initPktRxCb( + const UI32_T unit) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + HAL_LIGHTNING_PKT_RX_CB_T *ptr_rx_cb = HAL_LIGHTNING_PKT_GET_RX_CB_PTR(unit); + HAL_LIGHTNING_PKT_RX_CHANNEL_T channel = 0; + UI32_T queue = 0; + + osal_memset(ptr_rx_cb, 0x0, sizeof(HAL_LIGHTNING_PKT_RX_CB_T)); + + ptr_rx_cb->sched_mode = HAL_DFLT_CFG_PKT_RX_SCHED_MODE; + + /* Sync semaphore to signal rxTask */ + osal_createEvent("RX_SYNC", &ptr_rx_cb->sync_sema); + + /* Initialize Rx GPD-queue (of first SW-GPD) from handleRxDoneTask to rxTask */ + for (queue = 0; ((queue < HAL_LIGHTNING_PKT_RX_QUEUE_NUM) && (CLX_E_OK == rc)); queue++) + { + ptr_rx_cb->sw_queue[queue].len = HAL_DFLT_CFG_PKT_RX_QUEUE_LEN; + ptr_rx_cb->sw_queue[queue].weight = HAL_DFLT_CFG_PKT_RX_QUEUE_WEIGHT; + + osal_createSemaphore("RX_QUE", CLX_SEMAPHORE_BINARY, &ptr_rx_cb->sw_queue[queue].sema); + osal_que_create(&ptr_rx_cb->sw_queue[queue].que_id, ptr_rx_cb->sw_queue[queue].len); + } + + /* Init Rx PDMA */ + for (channel = 0; ((channel < HAL_LIGHTNING_PKT_RX_CHANNEL_LAST) && (CLX_E_OK == rc)); channel++) + { + rc = _hal_lightning_pkt_initRxPdma(unit, channel); + } + + return (rc); +} + +/* FUNCTION NAME: _hal_lightning_pkt_initL1Isr + * PURPOSE: + * To initialize the PDMA L1 ISR configuration. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully initialize the L1 ISR. + * CLX_E_OTHERS -- Configure failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_initL1Isr( + const UI32_T unit) +{ + UI32_T idx = 0, vec = sizeof(_hal_lightning_pkt_intr_vec) / sizeof(HAL_LIGHTNING_PKT_INTR_VEC_T); + + for (idx = 0; idx < vec; idx++) + { + _hal_lightning_pkt_enableIntr(unit, _hal_lightning_pkt_intr_vec[idx].intr_reg); + _hal_lightning_pkt_unmaskIntr(unit, _hal_lightning_pkt_intr_vec[idx].intr_reg); + } + + return (CLX_E_OK); +} + +/* FUNCTION NAME: _hal_lightning_pkt_initL2Isr + * PURPOSE: + * To initialize the PDMA L2 ISR configuration. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully configure for the L2 ISR. + * CLX_E_OTHERS -- Configure failed. + * NOTES: + * None + */ +static CLX_ERROR_NO_T +_hal_lightning_pkt_initL2Isr( + const UI32_T unit) +{ + HAL_LIGHTNING_PKT_L2_ISR_T isr_status = 0x0; + + HAL_LIGHTNING_PKT_SET_BITMAP(isr_status, HAL_LIGHTNING_PKT_L2_ISR_RCH0); + HAL_LIGHTNING_PKT_SET_BITMAP(isr_status, HAL_LIGHTNING_PKT_L2_ISR_RCH1); + HAL_LIGHTNING_PKT_SET_BITMAP(isr_status, HAL_LIGHTNING_PKT_L2_ISR_RCH2); + HAL_LIGHTNING_PKT_SET_BITMAP(isr_status, HAL_LIGHTNING_PKT_L2_ISR_RCH3); + HAL_LIGHTNING_PKT_SET_BITMAP(isr_status, HAL_LIGHTNING_PKT_L2_ISR_TCH0); + HAL_LIGHTNING_PKT_SET_BITMAP(isr_status, HAL_LIGHTNING_PKT_L2_ISR_TCH1); + HAL_LIGHTNING_PKT_SET_BITMAP(isr_status, HAL_LIGHTNING_PKT_L2_ISR_TCH2); + HAL_LIGHTNING_PKT_SET_BITMAP(isr_status, HAL_LIGHTNING_PKT_L2_ISR_TCH3); + HAL_LIGHTNING_PKT_SET_BITMAP(isr_status, HAL_LIGHTNING_PKT_L2_ISR_RX_QID_MAP_ERR); + HAL_LIGHTNING_PKT_SET_BITMAP(isr_status, HAL_LIGHTNING_PKT_L2_ISR_RX_FRAME_ERR); + + osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_ERR_INT_EN), + &isr_status, sizeof(UI32_T)); + + osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_ERR_INT_MASK_SET), + &isr_status, sizeof(UI32_T)); + + return (CLX_E_OK); + +} + +CLX_ERROR_NO_T +_hal_lightning_pkt_resetIosCreditCfg( + const UI32_T unit) +{ +#define HAL_LIGHTNING_PKT_PDMA_CREDIT_CFG_RESET_OFFSET (16) + + UI32_T credit_cfg = 0x0; + UI32_T idx; + + for (idx=0; idxptr_profile = ptr_new_profile; + + /* Create the 1st node in the interface profile list */ + if (NULL == *pptr_profile_list) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_PROFILE, + "prof list empty\n"); + *pptr_profile_list = ptr_new_prof_node; + ptr_new_prof_node->ptr_next_node = NULL; + } + else + { + ptr_prev_node = *pptr_profile_list; + ptr_curr_node = *pptr_profile_list; + + while (ptr_curr_node != NULL) + { + if (ptr_curr_node->ptr_profile->priority <= ptr_new_profile->priority) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_PROFILE, + "find prof id=%d (%s) higher priority=%d, search next\n", + ptr_curr_node->ptr_profile->id, + ptr_curr_node->ptr_profile->name, + ptr_curr_node->ptr_profile->priority); + /* Search the next node */ + ptr_prev_node = ptr_curr_node; + ptr_curr_node = ptr_curr_node->ptr_next_node; + } + else + { + /* Insert intermediate node */ + ptr_new_prof_node->ptr_next_node = ptr_curr_node; + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_PROFILE, + "insert prof id=%d (%s) before prof id=%d (%s) (priority=%d >= %d)\n", + ptr_new_prof_node->ptr_profile->id, + ptr_new_prof_node->ptr_profile->name, + ptr_curr_node->ptr_profile->id, + ptr_curr_node->ptr_profile->name, + ptr_new_prof_node->ptr_profile->priority, + ptr_curr_node->ptr_profile->priority); + + if (ptr_prev_node == ptr_curr_node) + { + /* There is no previous node: change the root */ + *pptr_profile_list = ptr_new_prof_node; + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_PROFILE, + "insert prof id=%d (%s) to head (priority=%d)\n", + ptr_new_prof_node->ptr_profile->id, + ptr_new_prof_node->ptr_profile->name, + ptr_new_prof_node->ptr_profile->priority); + } + else + { + ptr_prev_node->ptr_next_node = ptr_new_prof_node; + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_PROFILE, + "insert prof id=%d (%s) after prof id=%d (%s) (priority=%d <= %d)\n", + ptr_new_prof_node->ptr_profile->id, + ptr_new_prof_node->ptr_profile->name, + ptr_prev_node->ptr_profile->id, + ptr_prev_node->ptr_profile->name, + ptr_new_prof_node->ptr_profile->priority, + ptr_prev_node->ptr_profile->priority); + } + + return (CLX_E_OK); + } + } + + /* Insert node to the tail of list */ + ptr_prev_node->ptr_next_node = ptr_new_prof_node; + ptr_new_prof_node->ptr_next_node = NULL; + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_PROFILE, + "insert prof id=%d (%s) to tail, after prof id=%d (%s) (priority=%d <= %d)\n", + ptr_new_prof_node->ptr_profile->id, + ptr_new_prof_node->ptr_profile->name, + ptr_prev_node->ptr_profile->id, + ptr_prev_node->ptr_profile->name, + ptr_new_prof_node->ptr_profile->priority, + ptr_prev_node->ptr_profile->priority); + } + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_addProfToAllIntf( + HAL_LIGHTNING_PKT_NETIF_PROFILE_T *ptr_new_profile) +{ + UI32_T port; + HAL_LIGHTNING_PKT_NETIF_PORT_DB_T *ptr_port_db; + + for (port = 0; port < HAL_LIGHTNING_PKT_MAX_PORT_NUM; port++) + { + ptr_port_db = HAL_LIGHTNING_PKT_GET_PORT_DB(port); + /* Shall we check if the interface is ever created on the port?? */ + /* if (NULL != ptr_port_db->ptr_net_dev) */ + if (1) + { + _hal_lightning_pkt_addProfToList(ptr_new_profile, &ptr_port_db->ptr_profile_list); + } + } + + return (CLX_E_OK); +} + +static HAL_LIGHTNING_PKT_NETIF_PROFILE_T * +_hal_lightning_pkt_delProfFromListById( + const UI32_T id, + HAL_LIGHTNING_PKT_PROFILE_NODE_T **pptr_profile_list) +{ + HAL_LIGHTNING_PKT_PROFILE_NODE_T *ptr_temp_node; + HAL_LIGHTNING_PKT_PROFILE_NODE_T *ptr_curr_node, *ptr_prev_node; + HAL_LIGHTNING_PKT_NETIF_PROFILE_T *ptr_profile = NULL;; + + if (NULL != *pptr_profile_list) + { + /* Check the 1st node */ + if (id == (*pptr_profile_list)->ptr_profile->id) + { + ptr_profile = (*pptr_profile_list)->ptr_profile; + ptr_temp_node = (*pptr_profile_list); + (*pptr_profile_list) = ptr_temp_node->ptr_next_node; + + if (NULL != ptr_temp_node->ptr_next_node) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_PROFILE, + "choose prof id=%d (%s) as new head\n", + ptr_temp_node->ptr_next_node->ptr_profile->id, + ptr_temp_node->ptr_next_node->ptr_profile->name); + } + else + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_PROFILE, + "prof list is empty\n"); + } + + + osal_free(ptr_temp_node); + } + else + { + ptr_prev_node = *pptr_profile_list; + ptr_curr_node = ptr_prev_node->ptr_next_node; + + while (NULL != ptr_curr_node) + { + if (id != ptr_curr_node->ptr_profile->id) + { + ptr_prev_node = ptr_curr_node; + ptr_curr_node = ptr_curr_node->ptr_next_node; + } + else + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_PROFILE, + "find prof id=%d, free done\n", id); + + ptr_profile = ptr_curr_node->ptr_profile; + ptr_prev_node->ptr_next_node = ptr_curr_node->ptr_next_node; + osal_free(ptr_curr_node); + break; + } + } + } + } + + if (NULL == ptr_profile) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_PROFILE | HAL_LIGHTNING_PKT_DBG_ERR), + "find prof failed, id=%d\n", id); + } + + return (ptr_profile); +} + + +static CLX_ERROR_NO_T +_hal_lightning_pkt_delProfFromAllIntfById( + const UI32_T id) +{ + UI32_T port; + HAL_LIGHTNING_PKT_NETIF_PORT_DB_T *ptr_port_db; + + for (port = 0; port < HAL_LIGHTNING_PKT_MAX_PORT_NUM; port++) + { + ptr_port_db = HAL_LIGHTNING_PKT_GET_PORT_DB(port); + /* Shall we check if the interface is ever created on the port?? */ + /* if (NULL != ptr_port_db->ptr_net_dev) */ + if (1) + { + _hal_lightning_pkt_delProfFromListById(id, &ptr_port_db->ptr_profile_list); + } + } + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_allocProfEntry( + HAL_LIGHTNING_PKT_NETIF_PROFILE_T *ptr_profile) +{ + UI32_T idx; + + for (idx=0; idxid = idx; + return (CLX_E_OK); + } + } + return (CLX_E_TABLE_FULL); +} + +static HAL_LIGHTNING_PKT_NETIF_PROFILE_T * +_hal_lightning_pkt_freeProfEntry( + const UI32_T id) +{ + HAL_LIGHTNING_PKT_NETIF_PROFILE_T *ptr_profile = NULL; + + if (id < HAL_LIGHTNING_PKT_NET_PROFILE_NUM_MAX) + { + ptr_profile = _ptr_hal_lightning_pkt_profile_entry[id]; + _ptr_hal_lightning_pkt_profile_entry[id] = NULL; + } + + return (ptr_profile); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_destroyAllIntf( + const UI32_T unit) +{ + HAL_LIGHTNING_PKT_NETIF_PORT_DB_T *ptr_port_db; + UI32_T port = 0; + + /* Unregister net devices by id, although the "id" is now relavent to "port" we still perform a search */ + for (port = 0; port < HAL_LIGHTNING_PKT_MAX_PORT_NUM; port++) + { + ptr_port_db = HAL_LIGHTNING_PKT_GET_PORT_DB(port); + if (NULL != ptr_port_db->ptr_net_dev) /* valid intf */ + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_INTF, + "u=%u, find intf %s (id=%d) on phy port=%d, destroy done\n", + unit, + ptr_port_db->meta.name, + ptr_port_db->meta.port, + ptr_port_db->meta.port); + + netif_tx_disable(ptr_port_db->ptr_net_dev); + unregister_netdev(ptr_port_db->ptr_net_dev); + free_netdev(ptr_port_db->ptr_net_dev); + + /* Don't need to remove profiles on this port. + * In fact, the profile is binding to "port" not "intf". + */ + /* _hal_lightning_pkt_destroyProfList(ptr_port_db->ptr_profile_list); */ + + osal_memset(ptr_port_db, 0x0, sizeof(HAL_LIGHTNING_PKT_NETIF_PORT_DB_T)); + } + } + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_delProfListOnAllIntf( + const UI32_T unit) +{ + HAL_LIGHTNING_PKT_NETIF_PORT_DB_T *ptr_port_db; + UI32_T port = 0; + HAL_LIGHTNING_PKT_PROFILE_NODE_T *ptr_curr_node, *ptr_next_node; + + /* Unregister net devices by id, although the "id" is now relavent to "port" we still perform a search */ + for (port = 0; port < HAL_LIGHTNING_PKT_MAX_PORT_NUM; port++) + { + ptr_port_db = HAL_LIGHTNING_PKT_GET_PORT_DB(port); + if (NULL != ptr_port_db->ptr_profile_list) /* valid intf */ + { + ptr_curr_node = ptr_port_db->ptr_profile_list; + while (NULL != ptr_curr_node) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_PROFILE, + "u=%u, del prof id=%d on phy port=%d\n", + unit, ptr_curr_node->ptr_profile->id, port); + + ptr_next_node = ptr_curr_node->ptr_next_node; + osal_free(ptr_curr_node); + ptr_curr_node = ptr_next_node; + } + } + } + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_destroyAllProfile( + const UI32_T unit) +{ + HAL_LIGHTNING_PKT_NETIF_PROFILE_T *ptr_profile; + UI32_T prof_id; + + _hal_lightning_pkt_delProfListOnAllIntf(unit); + + for (prof_id=0; prof_idid, + ptr_profile->name, + ptr_profile->priority, + ptr_profile->flags); + osal_free(ptr_profile); + } + } + + return (CLX_E_OK); +} + +/* FUNCTION NAME: hal_lightning_pkt_initPktDrv + * PURPOSE: + * To invoke the functions to initialize the control block for each + * PDMA subsystem. + * INPUT: + * unit -- The unit ID + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successfully initialize the control blocks. + * CLX_E_OTHERS -- Initialize the control blocks failed. + * NOTES: + * None + */ +CLX_ERROR_NO_T +hal_lightning_pkt_initPktDrv( + const UI32_T unit) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + UI32_T channel = 0; + UI32_T flush_intr = 0x0; + UI32_T clear_intr = 0xffffffff; + HAL_LIGHTNING_PKT_DRV_CB_T *ptr_cb = HAL_LIGHTNING_PKT_GET_DRV_CB_PTR(unit); + + /* There's a case that PDMA Tx is on-going when doing chip reset, + * where PDMA may hang and be not programmable since current Tx packet + * stucks due to IOS credit too low. + * Thus, we always reset IOS credit value before progrmming Tx PDMA. + */ + _hal_lightning_pkt_resetIosCreditCfg(unit); + + /* Since the users may kill SDK application without a de-init flow, + * we help to detect if NETIF is ever init before, and perform deinit. + * (Because the users cannot perform Task init bypassing Drv init, this + * check is required only in here) + */ + if (0 != (ptr_cb->init_flag & HAL_LIGHTNING_PKT_INIT_DRV)) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_ERR, + "u=%u, init pkt drv failed, inited\n", unit); + + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_ERR, + "u=%u, stop rx pkt\n", unit); + _hal_lightning_pkt_rxStop(unit); + + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_ERR, + "u=%u, stop all intf\n", unit); + _hal_lightning_pkt_stopAllIntf(unit); + + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_ERR, + "u=%u, deinit pkt task\n", unit); + + hal_lightning_pkt_deinitTask(unit); + + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_ERR, + "u=%u, deinit pkt drv\n", unit); + hal_lightning_pkt_deinitPktDrv(unit); + + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_ERR, + "u=%u, destroy all prof\n", unit); + _hal_lightning_pkt_destroyAllProfile(unit); + + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_ERR, + "u=%u, destroy all intf\n", unit); + _hal_lightning_pkt_destroyAllIntf(unit); + } + + /* [cold-boot] 1. stop DMA channel + * 2. disable/mask/clear the interrupt status. + */ + osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_ERR_INT_EN), + &flush_intr, sizeof(UI32_T)); + + osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_ERR_INT_MASK_SET), + &clear_intr, sizeof(UI32_T)); + + osal_mdc_writePciReg(unit, + HAL_LIGHTNING_PKT_GET_MMIO(HAL_LIGHTNING_PKT_PDMA_ERR_INT_CLR), + &clear_intr, sizeof(UI32_T)); + + for (channel = 0; channel < HAL_LIGHTNING_PKT_TX_CHANNEL_LAST; channel++) + { + _hal_lightning_pkt_stopTxChannelReg(unit, channel); + _hal_lightning_pkt_maskAllTxL2IsrReg(unit, channel); + _hal_lightning_pkt_clearTxL2IsrStatusReg(unit, channel, clear_intr); + } + + for (channel = 0; channel < HAL_LIGHTNING_PKT_RX_CHANNEL_LAST; channel++) + { + _hal_lightning_pkt_stopRxChannelReg(unit, channel); + _hal_lightning_pkt_maskAllRxL2IsrReg(unit, channel); + _hal_lightning_pkt_clearRxL2IsrStatusReg(unit, channel, clear_intr); + } + + rc = _hal_lightning_pkt_initPktCb(unit); + if (CLX_E_OK == rc) + { + rc = _hal_lightning_pkt_initPktTxCb(unit); + } + if (CLX_E_OK == rc) + { + rc = _hal_lightning_pkt_initPktRxCb(unit); + } + if (CLX_E_OK == rc) + { + rc = _hal_lightning_pkt_initL1Isr(unit); + } + if (CLX_E_OK == rc) + { + rc = _hal_lightning_pkt_initL2Isr(unit); + } + + /* Set the flag to record init state */ + ptr_cb->init_flag |= HAL_LIGHTNING_PKT_INIT_DRV; + + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_COMMON, + "u=%u, pkt drv init done, init flag=0x%x\n", unit, ptr_cb->init_flag); + + return (rc); +} + +/* ----------------------------------------------------------------------------------- Init: I/O */ +CLX_ERROR_NO_T +hal_lightning_pkt_getNetDev( + const UI32_T unit, + const UI32_T port, + struct net_device **pptr_net_dev) +{ + *pptr_net_dev = HAL_LIGHTNING_PKT_GET_PORT_NETDEV(port); + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +_hal_lightning_pkt_isProtocolPkt(struct sk_buff *skb) +{ + struct ethhdr *ether = eth_hdr(skb); + struct iphdr *ip_header = ip_hdr(skb); + const struct ipv6hdr *ip6h = ipv6_hdr(skb); + struct udphdr *udp_header = udp_hdr(skb); + struct tcphdr *tcp_header = tcp_hdr(skb); + unsigned int src_ip = (unsigned int)ip_header->saddr; + unsigned int dest_ip = (unsigned int)ip_header->daddr; + unsigned int src_port = 0; + unsigned int dest_port = 0; + u8 lacp_addr[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 }; + u8 udld_addr[6] = { 0x01, 0x00, 0x0c, 0xcc, 0xcc, 0xcc }; + + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_TX, + "queue_mapping=%u skbaddr=%p vlan_tagged=%d vlan_proto=0x%04x vlan_tci=0x%04x protocol=0x%04x ip_summed=%d len=%u data_len=%u", + skb->queue_mapping, skb, + skb_vlan_tag_present(skb), ntohs(skb->vlan_proto), skb_vlan_tag_get(skb), + ntohs(skb->protocol), skb->ip_summed, skb->len, + skb->data_len); + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_TX, "Source: %x:%x:%x:%x:%x:%x\n", + ether->h_source[0], ether->h_source[1], ether->h_source[2], ether->h_source[3], ether->h_source[4], ether->h_source[5]); + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_TX, "Destination: %x:%x:%x:%x:%x:%x\n", + ether->h_dest[0], ether->h_dest[1], ether->h_dest[2], ether->h_dest[3], ether->h_dest[4], ether->h_dest[5]); + + if (ip_header->protocol == IPPROTO_UDP) { + udp_header = (struct udphdr *)skb_transport_header(skb); + src_port = (unsigned int)ntohs(udp_header->source); + dest_port = (unsigned int)ntohs(udp_header->dest); + } else if (ip_header->protocol == IPPROTO_TCP) { + tcp_header = (struct tcphdr *)skb_transport_header(skb); + src_port = (unsigned int)ntohs(tcp_header->source); + dest_port = (unsigned int)ntohs(tcp_header->dest); + } + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_TX, + "OUT packet info: src ip: %u, src port: %u; dest ip: %u, dest port: %u; proto: %u\n", + src_ip, src_port, dest_ip, dest_port, ip_header->protocol); + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_TX, "IPv6 protocol: %d\n", ip6h->nexthdr); + + //UDLD + if (ether_addr_equal(ether->h_dest, udld_addr)){ + return 1; + } + + switch(ntohs(skb->protocol)){ + //IP + case ETH_P_IP: + if ((ip_header->protocol == IPPROTO_ICMP) || + (ip_header->protocol == IPPROTO_IGMP) || + (ip_header->protocol == IPPROTO_PIM) || + //OSPF + (ip_header->protocol == 89) || + (ip_header->protocol == IPPROTO_EGP)) { + return 1; + } + + if (ip_header->protocol == IPPROTO_UDP) { + src_port = (unsigned int)ntohs(udp_header->source); + dest_port = (unsigned int)ntohs(udp_header->dest); + } else if (ip_header->protocol == IPPROTO_TCP) { + src_port = (unsigned int)ntohs(tcp_header->source); + dest_port = (unsigned int)ntohs(tcp_header->dest); + } else { + return 0; + } + + if ((ip_header->protocol == IPPROTO_TCP) && ( + //BGP + (dest_port == 179) || + //SSH + (dest_port == 22))){ + return 1; + } + + if ((ip_header->protocol == IPPROTO_UDP) && ( + //Sflow + (dest_port == 6343) || + //DHCP + (dest_port == 67) || + (dest_port == 68) || + //BFD + (dest_port == 3784) || + (dest_port == 4784) || + //SNMP + (dest_port == 161) || + (dest_port == 162))){ + return 1; + } + + break; + + //IPv6 + case ETH_P_IPV6: + if (ip6h->nexthdr == IPPROTO_ICMPV6) { + return 1; + } + udp_header = (struct udphdr *)((void *)ip6h + sizeof(struct ipv6hdr)); + tcp_header = (struct tcphdr *)((void *)ip6h + sizeof(struct ipv6hdr)); + + if (ip_header->protocol == IPPROTO_UDP) { + src_port = (unsigned int)ntohs(udp_header->source); + dest_port = (unsigned int)ntohs(udp_header->dest); + } else if (ip_header->protocol == IPPROTO_TCP) { + src_port = (unsigned int)ntohs(tcp_header->source); + dest_port = (unsigned int)ntohs(tcp_header->dest); + } else { + return 0; + } + + if ((ip6h->nexthdr == IPPROTO_UDP) && + //DHCPv6 + ((dest_port == 547) || (dest_port == 546) || + //BFDv6 + (dest_port == 3784) || + (dest_port == 4784))){ + return 1; + } + + if ((ip6h->nexthdr == IPPROTO_TCP) && + //BGPv6 and ssh + ((dest_port == 179) || (dest_port == 22))){ + return 1; + } + + //OSPFv6 + if (ip6h->nexthdr == 89){ + return 1; + } + break; + case ETH_P_ARP: + case ETH_P_RARP: + case 0x88cc: //LLDP + return 1; + break; + + case ETH_P_SLOW: + if (ether_addr_equal(ether->h_dest, lacp_addr)){ + return 1; + } + break; + default: + return 0; + break; + } + + return 0; +} + + +CLX_ERROR_NO_T +hal_lightning_pkt_prepareGpd( + const UI32_T unit, + const CLX_ADDR_T phy_addr, + const struct sk_buff *ptr_skb, + const UI32_T port, + HAL_LIGHTNING_PKT_TX_SW_GPD_T *ptr_sw_gpd) +{ + /* fill up tx_gpd */ + ptr_sw_gpd->tx_gpd.data_buf_addr_hi = CLX_ADDR_64_HI(phy_addr); + ptr_sw_gpd->tx_gpd.data_buf_addr_lo = CLX_ADDR_64_LOW(phy_addr); + ptr_sw_gpd->tx_gpd.data_buf_size = ptr_skb->len; + ptr_sw_gpd->tx_gpd.chksum = 0x0; + ptr_sw_gpd->tx_gpd.ipc = 0; /* Raw mode, sent to plane 0 */ + ptr_sw_gpd->tx_gpd.prg = HAL_LIGHTNING_PKT_PRG_PROCESS_GPD; + ptr_sw_gpd->tx_gpd.hwo = HAL_LIGHTNING_PKT_HWO_HW_OWN; + ptr_sw_gpd->tx_gpd.ch = HAL_LIGHTNING_PKT_CH_LAST_GPD; + ptr_sw_gpd->tx_gpd.ioc = HAL_LIGHTNING_PKT_IOC_HAS_INTR; + ptr_sw_gpd->tx_gpd.pkt_len = ptr_skb->len; + + /* fill up cpu header */ + ptr_sw_gpd->tx_gpd.itmh_eth.skip_ipp = 1; + ptr_sw_gpd->tx_gpd.itmh_eth.skip_epp = 1; + ptr_sw_gpd->tx_gpd.itmh_eth.color = 0; /* Green */ + ptr_sw_gpd->tx_gpd.itmh_eth.tc = 15; /* Max tc */ + ptr_sw_gpd->tx_gpd.itmh_eth.igr_phy_port = 0; + + ptr_sw_gpd->tx_gpd.pph_l2.mrk_pcp_val = 7; /* Max pcp */ + ptr_sw_gpd->tx_gpd.pph_l2.mrk_pcp_dei_en = 1; + + /* + if (!_hal_lightning_pkt_isProtocolPkt(ptr_skb)){ + ptr_sw_gpd->tx_gpd.itmh_eth.tc = 0; + ptr_sw_gpd->tx_gpd.pph_l2.mrk_pcp_val = 0; + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_TX, "Set TC and PCP to 0\n"); + } + */ + + /* destination index + * 1. to local ETH port + * 2. to remote ETH port + * 3. to remote CPU + */ + ptr_sw_gpd->tx_gpd.itmh_eth.dst_idx = port; + + /* [CL8570] we should set all-1 for the following fields to skip some tm-logic */ + + /* TM header */ + ptr_sw_gpd->tx_gpd.itmh_eth.src_idx = 0x7fff; + ptr_sw_gpd->tx_gpd.itmh_eth.intf_fdid = 0x3fff; + ptr_sw_gpd->tx_gpd.itmh_eth.src_supp_tag = 0x1f; + ptr_sw_gpd->tx_gpd.itmh_eth.nvo3_mgid = 0x6fff; + ptr_sw_gpd->tx_gpd.itmh_eth.nvo3_src_supp_tag_w0 = 0x1; + ptr_sw_gpd->tx_gpd.itmh_eth.nvo3_src_supp_tag_w1 = 0xf; + + /* PP header */ + ptr_sw_gpd->tx_gpd.pph_l2.nvo3_encap_idx = HAL_INVALID_NVO3_ENCAP_IDX; + ptr_sw_gpd->tx_gpd.pph_l2.nvo3_adj_idx = HAL_INVALID_NVO3_ADJ_IDX; + + return (CLX_E_OK); +} + +/* ----------------------------------------------------------------------------------- Init: net_dev_ops */ +static int +_hal_lightning_pkt_net_dev_init( + struct net_device *ptr_net_dev) +{ + return 0; +} + +static int +_hal_lightning_pkt_net_dev_open( + struct net_device *ptr_net_dev) +{ + netif_start_queue(ptr_net_dev); + +#if defined(PERF_EN_TEST) + /* Tx (len, tx_channel, rx_channel, test_skb) */ + perf_test(64, 1, 0, FALSE); + perf_test(64, 2, 0, FALSE); + perf_test(64, 4, 0, FALSE); + + perf_test(1518, 1, 0, FALSE); + perf_test(1518, 2, 0, FALSE); + perf_test(1518, 4, 0, FALSE); + + perf_test(9216, 1, 0, FALSE); + perf_test(9216, 2, 0, FALSE); + perf_test(9216, 4, 0, FALSE); + + /* Rx (len, tx_channel, rx_channel, test_skb) */ + perf_test(64, 0, 1, FALSE); + perf_test(64, 0, 3, FALSE); + perf_test(64, 0, 4, FALSE); + + perf_test(1518, 0, 1, FALSE); + perf_test(1518, 0, 3, FALSE); + perf_test(1518, 0, 4, FALSE); + + perf_test(9216, 0, 1, FALSE); + perf_test(9216, 0, 3, FALSE); + perf_test(9216, 0, 4, FALSE); +#endif + + return 0; +} + +static int +_hal_lightning_pkt_net_dev_stop( + struct net_device *ptr_net_dev) +{ + netif_stop_queue(ptr_net_dev); + return 0; +} + +static int +_hal_lightning_pkt_net_dev_ioctl( + struct net_device *ptr_net_dev, + struct ifreq *ptr_ifreq, + int cmd) +{ + return 0; +} + +static netdev_tx_t +_hal_lightning_pkt_net_dev_tx( + struct sk_buff *ptr_skb, + struct net_device *ptr_net_dev) +{ + struct net_device_priv *ptr_priv = netdev_priv(ptr_net_dev); + HAL_LIGHTNING_PKT_TX_CB_T *ptr_tx_cb; + /* chip meta */ + unsigned int unit; + unsigned int channel = 0; + HAL_LIGHTNING_PKT_TX_SW_GPD_T *ptr_sw_gpd = NULL; + void *ptr_virt_addr = NULL; + CLX_ADDR_T phy_addr = 0x0; + + if (NULL == ptr_priv) + { + /* in case that the netdev has been freed/reset somewhere */ + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_ERR, "get netdev_priv failed\n"); + return -EFAULT; + } + + /* check skb */ + if (NULL == ptr_skb) + { + ptr_priv->stats.tx_errors++; + return -EFAULT; + } + + unit = ptr_priv->unit; + + ptr_tx_cb = HAL_LIGHTNING_PKT_GET_TX_CB_PTR(unit); + + /* for warm de-init procedure, if any net intf not destroyed, it is possible + * that kernel still has packets to send causing segmentation fault + */ + if (FALSE == ptr_tx_cb->net_tx_allowed) { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_ERR, "net tx during sdk de-init\n"); + ptr_priv->stats.tx_dropped++; + osal_skb_free(ptr_skb); + return NETDEV_TX_OK; + } + + /* pad to 60-bytes if skb_len < 60, see: eth_skb_pad(skb) */ + if (ptr_skb->len < ETH_ZLEN) + { + skb_pad(ptr_skb, ETH_ZLEN - ptr_skb->len); + skb_set_tail_pointer(ptr_skb, ETH_ZLEN); + ptr_skb->len = ETH_ZLEN; + } + + /* pad 4-bytes for chip-crc */ + skb_pad(ptr_skb, ETH_FCS_LEN); + skb_set_tail_pointer(ptr_skb, ETH_FCS_LEN); + ptr_skb->len += ETH_FCS_LEN; + + /* alloc gpd */ + ptr_sw_gpd = osal_alloc(sizeof(HAL_LIGHTNING_PKT_TX_SW_GPD_T)); + if (NULL == ptr_sw_gpd) + { + ptr_priv->stats.tx_errors++; + osal_skb_free(ptr_skb); + } + else + { + /* map skb to dma */ + ptr_virt_addr = ptr_skb->data; + phy_addr = osal_skb_mapDma(ptr_skb, DMA_TO_DEVICE); + if (0x0 == phy_addr) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_ERR, "u=%u, txch=%u, skb dma map err\n", + unit, channel); + ptr_priv->stats.tx_errors++; + osal_skb_free(ptr_skb); + osal_free(ptr_sw_gpd); + } + else + { + /* trans skb to gpd */ + memset(ptr_sw_gpd, 0x0, sizeof(HAL_LIGHTNING_PKT_TX_SW_GPD_T)); + ptr_sw_gpd->callback = (void *)_hal_lightning_pkt_net_dev_tx_callback; + ptr_sw_gpd->ptr_cookie = (void *)ptr_skb; + ptr_sw_gpd->gpd_num = 1; + ptr_sw_gpd->ptr_next = NULL; + ptr_sw_gpd->channel = channel; + /* prepare gpd */ + hal_lightning_pkt_prepareGpd(unit, phy_addr, ptr_skb, ptr_priv->port, ptr_sw_gpd); + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,6,7) + ptr_net_dev->trans_start = jiffies; +#else + netdev_get_tx_queue(ptr_net_dev, 0)->trans_start = jiffies; +#endif + /* send gpd */ + if (CLX_E_OK == hal_lightning_pkt_sendGpd(unit, channel, ptr_sw_gpd)) + { + ptr_priv->stats.tx_packets++; + ptr_priv->stats.tx_bytes += ptr_skb->len; + } + else + { + ptr_priv->stats.tx_fifo_errors++; /* to record the extreme cases where packets are dropped */ + ptr_priv->stats.tx_dropped++; + + osal_skb_unmapDma(phy_addr, ptr_skb->len, DMA_TO_DEVICE); + osal_skb_free(ptr_skb); + osal_free(ptr_sw_gpd); + } + } + } + + return NETDEV_TX_OK; +} +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,10,0) +static void +_hal_lightning_pkt_net_dev_tx_timeout( + struct net_device *ptr_net_dev, + unsigned int txqueue) +#else +static void +_hal_lightning_pkt_net_dev_tx_timeout( + struct net_device *ptr_net_dev) +#endif +{ + netif_stop_queue(ptr_net_dev); + osal_sleepThread(1000); + netif_wake_queue(ptr_net_dev); +} + +static struct net_device_stats * +_hal_lightning_pkt_net_dev_get_stats( + struct net_device *ptr_net_dev) +{ + struct net_device_priv *ptr_priv = netdev_priv(ptr_net_dev); + + return (&ptr_priv->stats); +} + +static int +_hal_lightning_pkt_net_dev_set_mtu( + struct net_device *ptr_net_dev, + int new_mtu) +{ + if (new_mtu < 64 || new_mtu > 9216) + { + return -EINVAL; + } + ptr_net_dev->mtu = new_mtu; /* This mtu need to be synced to chip's */ + return 0; +} + +static int +_hal_lightning_pkt_net_dev_set_mac( + struct net_device *ptr_net_dev, + void *ptr_mac_addr) +{ + struct sockaddr *ptr_addr = ptr_mac_addr; + + memcpy(ptr_net_dev->dev_addr, ptr_addr->sa_data, ptr_net_dev->addr_len); + return 0; +} + +static void +_hal_lightning_pkt_net_dev_set_rx_mode( + struct net_device *ptr_dev) +{ + if (ptr_dev->flags & IFF_PROMISC) + { + } + else + { + if (ptr_dev->flags & IFF_ALLMULTI) + { + } + else + { + if (netdev_mc_empty(ptr_dev)) + { + return; + } + } + } +} + +static struct net_device_ops _hal_lightning_pkt_net_dev_ops = +{ + .ndo_init = _hal_lightning_pkt_net_dev_init, + .ndo_open = _hal_lightning_pkt_net_dev_open, + .ndo_stop = _hal_lightning_pkt_net_dev_stop, + .ndo_do_ioctl = _hal_lightning_pkt_net_dev_ioctl, + .ndo_start_xmit = _hal_lightning_pkt_net_dev_tx, + .ndo_tx_timeout = _hal_lightning_pkt_net_dev_tx_timeout, + .ndo_get_stats = _hal_lightning_pkt_net_dev_get_stats, + .ndo_change_mtu = _hal_lightning_pkt_net_dev_set_mtu, + .ndo_set_mac_address = _hal_lightning_pkt_net_dev_set_mac, + .ndo_set_rx_mode = _hal_lightning_pkt_net_dev_set_rx_mode, +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0) +static int +_hal_lightning_pkt_net_dev_ethtool_get( + struct net_device *ptr_dev, + struct ethtool_cmd *ptr_cmd) +{ + struct net_device_priv *ptr_priv; + + ptr_cmd->supported = SUPPORTED_1000baseT_Full | SUPPORTED_FIBRE; + ptr_cmd->port = PORT_FIBRE; + ptr_cmd->duplex = DUPLEX_FULL; + + ptr_priv = netdev_priv(ptr_dev); + ethtool_cmd_speed_set(ptr_cmd, ptr_priv->speed); + + return 0; +} +#endif + +static struct ethtool_ops _hal_lightning_pkt_net_dev_ethtool_ops = +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0) + .get_settings = _hal_lightning_pkt_net_dev_ethtool_get, +#endif + .get_link = ethtool_op_get_link, +}; + +static void +_hal_lightning_pkt_setup( + struct net_device *ptr_net_dev) +{ + struct net_device_priv *ptr_priv = netdev_priv(ptr_net_dev); + + /* setup net device */ + ether_setup(ptr_net_dev); + ptr_net_dev->netdev_ops = &_hal_lightning_pkt_net_dev_ops; + ptr_net_dev->ethtool_ops = &_hal_lightning_pkt_net_dev_ethtool_ops; + ptr_net_dev->watchdog_timeo = HAL_LIGHTNING_PKT_TX_TIMEOUT; + ptr_net_dev->mtu = HAL_LIGHTNING_PKT_MAX_ETH_FRAME_SIZE; /* This mtu need to be synced to chip's */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0) + ptr_net_dev->min_mtu = 64; + ptr_net_dev->max_mtu = 65535; +#endif + random_ether_addr(ptr_net_dev->dev_addr); /* Please use the mac-addr of interface. */ + + /* setup private data */ + ptr_priv->ptr_net_dev = ptr_net_dev; + memset(&ptr_priv->stats, 0, sizeof(struct net_device_stats)); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_createIntf( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_NETIF_COOKIE_T *ptr_cookie) +{ + HAL_LIGHTNING_PKT_NETIF_INTF_T net_intf = {0}; + HAL_LIGHTNING_PKT_NETIF_PORT_DB_T *ptr_port_db; + struct net_device *ptr_net_dev = NULL; + struct net_device_priv *ptr_priv = NULL; + CLX_ERROR_NO_T rc = CLX_E_OK; + + /* Lock all Rx tasks to avoid any access to the intf during packet processing */ + /* Only Rx tasks are locked since Tx action is performed under a spinlock protection */ + _hal_lightning_pkt_lockRxChannelAll(unit); + + osal_io_copyFromUser(&net_intf, &ptr_cookie->net_intf, sizeof(HAL_LIGHTNING_PKT_NETIF_INTF_T)); + + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_INTF, "u=%u, create intf name=%s, phy port=%d\n", + unit, net_intf.name, net_intf.port); + + /* To check if the interface with the same name exists in kernel */ + ptr_net_dev = dev_get_by_name(&init_net, net_intf.name); + if (NULL != ptr_net_dev) + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_ERR | HAL_LIGHTNING_PKT_DBG_INTF), + "u=%u, create intf failed, exist same name=%s\n", + unit, net_intf.name); + + dev_put(ptr_net_dev); + +#if defined(HAL_LIGHTNING_PKT_FORCR_REMOVE_DUPLICATE_NETDEV) + ptr_net_dev->operstate = IF_OPER_DOWN; + netif_carrier_off(ptr_net_dev); + netif_tx_disable(ptr_net_dev); + unregister_netdev(ptr_net_dev); + free_netdev(ptr_net_dev); +#endif + _hal_lightning_pkt_unlockRxChannelAll(unit); + return (CLX_E_ENTRY_EXISTS); + } + + /* Bind the net dev and intf meta data to internel port-based array */ + ptr_port_db = HAL_LIGHTNING_PKT_GET_PORT_DB(net_intf.port); + if (ptr_port_db->ptr_net_dev == NULL) + { + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) + ptr_net_dev = alloc_netdev(sizeof(struct net_device_priv), + net_intf.name, NET_NAME_UNKNOWN, _hal_lightning_pkt_setup); +#else + ptr_net_dev = alloc_netdev(sizeof(struct net_device_priv), + net_intf.name, _hal_lightning_pkt_setup); +#endif + memcpy(ptr_net_dev->dev_addr, net_intf.mac, ptr_net_dev->addr_len); + + ptr_priv = netdev_priv(ptr_net_dev); + + /* Port info will be used when packet sent from this netdev */ + ptr_priv->port = net_intf.port; + ptr_priv->id = net_intf.port; + ptr_priv->unit = unit; + + register_netdev(ptr_net_dev); + + netif_carrier_off(ptr_net_dev); + + net_intf.id = net_intf.port; /* Currently, id is 1-to-1 mapped to port */ + osal_memcpy(&ptr_port_db->meta, &net_intf, sizeof(HAL_LIGHTNING_PKT_NETIF_INTF_T)); + + ptr_port_db->ptr_net_dev = ptr_net_dev; + + /* Copy the intf-id to user space */ + osal_io_copyToUser(&ptr_cookie->net_intf, &net_intf, sizeof(HAL_LIGHTNING_PKT_NETIF_INTF_T)); + } + else + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_INTF | HAL_LIGHTNING_PKT_DBG_ERR), + "u=%u, create intf failed, exist on phy port=%d\n", + unit, net_intf.port); + /* The user needs to delete the existing intf binding to the same port */ + rc = CLX_E_ENTRY_EXISTS; + } + + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + _hal_lightning_pkt_unlockRxChannelAll(unit); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_destroyIntf( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_NETIF_COOKIE_T *ptr_cookie) +{ + HAL_LIGHTNING_PKT_NETIF_INTF_T net_intf = {0}; + HAL_LIGHTNING_PKT_NETIF_PORT_DB_T *ptr_port_db; + UI32_T port = 0; + CLX_ERROR_NO_T rc = CLX_E_ENTRY_NOT_FOUND; + + /* Lock all Rx tasks to avoid any access to the intf during packet processing */ + /* Only Rx tasks are locked since Tx action is performed under a spinlock protection */ + _hal_lightning_pkt_lockRxChannelAll(unit); + + osal_io_copyFromUser(&net_intf, &ptr_cookie->net_intf, sizeof(HAL_LIGHTNING_PKT_NETIF_INTF_T)); + + /* Unregister net devices by id, although the "id" is now relavent to "port" we still perform a search */ + for (port = 0; port < HAL_LIGHTNING_PKT_MAX_PORT_NUM; port++) + { + ptr_port_db = HAL_LIGHTNING_PKT_GET_PORT_DB(port); + if (NULL != ptr_port_db->ptr_net_dev) /* valid intf */ + { + if (ptr_port_db->meta.id == net_intf.id) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_INTF, + "u=%u, find intf %s (id=%d) on phy port=%d, destroy done\n", + unit, + ptr_port_db->meta.name, + ptr_port_db->meta.id, + ptr_port_db->meta.port); + + netif_carrier_off(ptr_port_db->ptr_net_dev); + netif_tx_disable(ptr_port_db->ptr_net_dev); + unregister_netdev(ptr_port_db->ptr_net_dev); + free_netdev(ptr_port_db->ptr_net_dev); + + /* Don't need to remove profiles on this port. + * In fact, the profile is binding to "port" not "intf". + */ + /* _hal_lightning_pkt_destroyProfList(ptr_port_db->ptr_profile_list); */ + + osal_memset(ptr_port_db, 0x0, sizeof(HAL_LIGHTNING_PKT_NETIF_PORT_DB_T)); + rc = CLX_E_OK; + break; + } + } + } + + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + _hal_lightning_pkt_unlockRxChannelAll(unit); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_traverseProfList( + UI32_T intf_id, + HAL_LIGHTNING_PKT_PROFILE_NODE_T *ptr_prof_list) +{ + HAL_LIGHTNING_PKT_PROFILE_NODE_T *ptr_curr_node; + + ptr_curr_node = ptr_prof_list; + + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_INTF, "intf id=%d, prof list=", intf_id); + while(NULL != ptr_curr_node) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_INTF, "%s (%d) => ", + ptr_curr_node->ptr_profile->name, + ptr_curr_node->ptr_profile->priority); + ptr_curr_node = ptr_curr_node->ptr_next_node; + } + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_INTF, "null\n"); + return (CLX_E_OK); +} + + +static CLX_ERROR_NO_T +_hal_lightning_pkt_getIntf( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_NETIF_COOKIE_T *ptr_cookie) +{ + HAL_LIGHTNING_PKT_NETIF_INTF_T net_intf = {0}; + HAL_LIGHTNING_PKT_NETIF_PORT_DB_T *ptr_port_db; + UI32_T port = 0; + CLX_ERROR_NO_T rc = CLX_E_ENTRY_NOT_FOUND; + + osal_io_copyFromUser(&net_intf, &ptr_cookie->net_intf, sizeof(HAL_LIGHTNING_PKT_NETIF_INTF_T)); + + for (port = 0; port < HAL_LIGHTNING_PKT_MAX_PORT_NUM; port++) + { + ptr_port_db = HAL_LIGHTNING_PKT_GET_PORT_DB(port); + if (NULL != ptr_port_db->ptr_net_dev) /* valid intf */ + { + if (ptr_port_db->meta.id == net_intf.id) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_INTF, "u=%u, find intf id=%d\n", unit, net_intf.id); + _hal_lightning_pkt_traverseProfList(net_intf.id, ptr_port_db->ptr_profile_list); + osal_io_copyToUser(&ptr_cookie->net_intf, &ptr_port_db->meta, sizeof(HAL_LIGHTNING_PKT_NETIF_INTF_T)); + rc = CLX_E_OK; + break; + } + } + } + + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + return (CLX_E_OK); +} + +static HAL_LIGHTNING_PKT_NETIF_PROFILE_T * +_hal_lightning_pkt_getProfEntry( + const UI32_T id) +{ + HAL_LIGHTNING_PKT_NETIF_PROFILE_T *ptr_profile = NULL; + + if (id < HAL_LIGHTNING_PKT_NET_PROFILE_NUM_MAX) + { + if (NULL != _ptr_hal_lightning_pkt_profile_entry[id]) + { + ptr_profile = _ptr_hal_lightning_pkt_profile_entry[id]; + } + } + + return (ptr_profile); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_createProfile( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_NETIF_COOKIE_T *ptr_cookie) +{ + HAL_LIGHTNING_PKT_NETIF_PROFILE_T *ptr_profile; + HAL_LIGHTNING_PKT_NETIF_PORT_DB_T *ptr_port_db; + CLX_ERROR_NO_T rc; + + /* Lock all Rx tasks to avoid profiles being refered during packet processing */ + /* Need to lock all Rx tasks since packets from all Rx channels do profile lookup */ + _hal_lightning_pkt_lockRxChannelAll(unit); + + ptr_profile = osal_alloc(sizeof(HAL_LIGHTNING_PKT_NETIF_PROFILE_T)); + osal_io_copyFromUser(ptr_profile, &ptr_cookie->net_profile, + sizeof(HAL_LIGHTNING_PKT_NETIF_PROFILE_T)); + + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_PROFILE, + "u=%u, create prof name=%s, priority=%d, flag=0x%x\n", + unit, + ptr_profile->name, + ptr_profile->priority, + ptr_profile->flags); + + /* Save the profile to the profile array and assign the index to ptr_profile->id */ + rc = _hal_lightning_pkt_allocProfEntry(ptr_profile); + if (CLX_E_OK == rc) + { + /* Insert the profile to the corresponding (port) interface */ + if ((ptr_profile->flags & HAL_LIGHTNING_PKT_NETIF_PROFILE_FLAGS_PORT) != 0) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_PROFILE, + "u=%u, bind prof to phy port=%d\n", unit, ptr_profile->port); + ptr_port_db = HAL_LIGHTNING_PKT_GET_PORT_DB(ptr_profile->port); + _hal_lightning_pkt_addProfToList(ptr_profile, &ptr_port_db->ptr_profile_list); + } + else + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_PROFILE, + "u=%u, bind prof to all intf\n", unit); + _hal_lightning_pkt_addProfToAllIntf(ptr_profile); + } + + /* Copy the ptr_profile->id to user space */ + osal_io_copyToUser(&ptr_cookie->net_profile, ptr_profile, sizeof(HAL_LIGHTNING_PKT_NETIF_PROFILE_T)); + } + else + { + HAL_LIGHTNING_PKT_DBG((HAL_LIGHTNING_PKT_DBG_PROFILE | HAL_LIGHTNING_PKT_DBG_ERR), + "u=%u, alloc prof entry failed, tbl full\n", unit); + osal_free(ptr_profile); + } + + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + _hal_lightning_pkt_unlockRxChannelAll(unit); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_destroyProfile( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_NETIF_COOKIE_T *ptr_cookie) +{ + HAL_LIGHTNING_PKT_NETIF_PROFILE_T profile = {0}; + HAL_LIGHTNING_PKT_NETIF_PROFILE_T *ptr_profile; + CLX_ERROR_NO_T rc = CLX_E_OK; + + /* Lock all Rx tasks to avoid profiles being refered during packet processing */ + /* Need to lock all Rx tasks since packets from all Rx channels do profile lookup */ + _hal_lightning_pkt_lockRxChannelAll(unit); + + osal_io_copyFromUser(&profile, &ptr_cookie->net_profile, + sizeof(HAL_LIGHTNING_PKT_NETIF_PROFILE_T)); + + /* Remove the profile from corresponding interface (port) */ + _hal_lightning_pkt_delProfFromAllIntfById(profile.id); + + ptr_profile = _hal_lightning_pkt_freeProfEntry(profile.id); + if (NULL != ptr_profile) + { + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_PROFILE, + "u=%u, destroy prof id=%d, name=%s, priority=%d, flag=0x%x\n", + unit, + ptr_profile->id, + ptr_profile->name, + ptr_profile->priority, + ptr_profile->flags); + osal_free(ptr_profile); + } + + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + _hal_lightning_pkt_unlockRxChannelAll(unit); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_getProfile( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_NETIF_COOKIE_T *ptr_cookie) +{ + HAL_LIGHTNING_PKT_NETIF_PROFILE_T profile = {0}; + HAL_LIGHTNING_PKT_NETIF_PROFILE_T *ptr_profile; + CLX_ERROR_NO_T rc = CLX_E_OK; + + osal_io_copyFromUser(&profile, &ptr_cookie->net_profile, sizeof(HAL_LIGHTNING_PKT_NETIF_PROFILE_T)); + + ptr_profile = _hal_lightning_pkt_getProfEntry(profile.id); + if (NULL != ptr_profile) + { + osal_io_copyToUser(&ptr_cookie->net_profile, ptr_profile, sizeof(HAL_LIGHTNING_PKT_NETIF_PROFILE_T)); + } + else + { + rc = CLX_E_ENTRY_NOT_FOUND; + } + + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_getIntfCnt( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_NETIF_COOKIE_T *ptr_cookie) +{ + HAL_LIGHTNING_PKT_NETIF_INTF_T net_intf = {0}; + HAL_LIGHTNING_PKT_NETIF_INTF_CNT_T intf_cnt = {0}; + HAL_LIGHTNING_PKT_NETIF_PORT_DB_T *ptr_port_db; + struct net_device_priv *ptr_priv; + UI32_T port = 0; + CLX_ERROR_NO_T rc = CLX_E_ENTRY_NOT_FOUND; + + osal_io_copyFromUser(&net_intf, &ptr_cookie->net_intf, sizeof(HAL_LIGHTNING_PKT_NETIF_INTF_T)); + + for (port = 0; port < HAL_LIGHTNING_PKT_MAX_PORT_NUM; port++) + { + ptr_port_db = HAL_LIGHTNING_PKT_GET_PORT_DB(port); + if (NULL != ptr_port_db->ptr_net_dev) /* valid intf */ + { + if (ptr_port_db->meta.id == net_intf.id) + { + ptr_priv = netdev_priv(ptr_port_db->ptr_net_dev); + intf_cnt.rx_pkt = ptr_priv->stats.rx_packets; + intf_cnt.tx_pkt = ptr_priv->stats.tx_packets; + intf_cnt.tx_error = ptr_priv->stats.tx_errors; + intf_cnt.tx_queue_full = ptr_priv->stats.tx_fifo_errors; + + rc = CLX_E_OK; + break; + } + } + } + + osal_io_copyToUser(&ptr_cookie->cnt, &intf_cnt, sizeof(HAL_LIGHTNING_PKT_NETIF_INTF_CNT_T)); + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_hal_lightning_pkt_clearIntfCnt( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_NETIF_COOKIE_T *ptr_cookie) +{ + HAL_LIGHTNING_PKT_NETIF_INTF_T net_intf = {0}; + HAL_LIGHTNING_PKT_NETIF_PORT_DB_T *ptr_port_db; + struct net_device_priv *ptr_priv; + UI32_T port = 0; + CLX_ERROR_NO_T rc = CLX_E_ENTRY_NOT_FOUND; + + osal_io_copyFromUser(&net_intf, &ptr_cookie->net_intf, sizeof(HAL_LIGHTNING_PKT_NETIF_INTF_T)); + + for (port = 0; port < HAL_LIGHTNING_PKT_MAX_PORT_NUM; port++) + { + ptr_port_db = HAL_LIGHTNING_PKT_GET_PORT_DB(port); + if (NULL != ptr_port_db->ptr_net_dev) /* valid intf */ + { + if (ptr_port_db->meta.id == net_intf.id) + { + ptr_priv = netdev_priv(ptr_port_db->ptr_net_dev); + ptr_priv->stats.rx_packets = 0; + ptr_priv->stats.tx_packets = 0; + ptr_priv->stats.tx_errors = 0; + ptr_priv->stats.tx_fifo_errors = 0; + + rc = CLX_E_OK; + break; + } + } + } + + osal_io_copyToUser(&ptr_cookie->rc, &rc, sizeof(CLX_ERROR_NO_T)); + + return (CLX_E_OK); +} + +/* ----------------------------------------------------------------------------------- Init: dev_ops */ +static void +_hal_lightning_pkt_dev_tx_callback( + const UI32_T unit, + HAL_LIGHTNING_PKT_TX_SW_GPD_T *ptr_sw_gpd, + HAL_LIGHTNING_PKT_TX_SW_GPD_T *ptr_sw_gpd_usr) +{ + UI32_T channel = ptr_sw_gpd->channel; + HAL_LIGHTNING_PKT_TX_CB_T *ptr_tx_cb = HAL_LIGHTNING_PKT_GET_TX_CB_PTR(unit); + + while (0 != _hal_lightning_pkt_enQueue(&ptr_tx_cb->sw_queue, ptr_sw_gpd)) + { + ptr_tx_cb->cnt.channel[channel].enque_retry++; + HAL_LIGHTNING_PKT_TX_ENQUE_RETRY_SLEEP(); + } + ptr_tx_cb->cnt.channel[channel].enque_ok++; + + osal_triggerEvent(&ptr_tx_cb->sync_sema); + ptr_tx_cb->cnt.channel[channel].trig_event++; +} + +ssize_t +hal_lightning_pkt_dev_tx( + struct file *file, + const char __user *buf, + size_t count, + loff_t *pos) +{ + int ret = 0; + int idx = 0; + unsigned int unit = 0; + unsigned int channel = 0; + HAL_LIGHTNING_PKT_IOCTL_TX_COOKIE_T tx_cookie; + HAL_LIGHTNING_PKT_IOCTL_TX_GPD_T ioctl_gpd; + HAL_LIGHTNING_PKT_TX_SW_GPD_T *ptr_sw_gpd_knl = NULL; + HAL_LIGHTNING_PKT_TX_SW_GPD_T *ptr_first_sw_gpd_knl = NULL; + + /* copy the tx-cookie */ + osal_io_copyFromUser(&tx_cookie, (void *)buf, sizeof(HAL_LIGHTNING_PKT_IOCTL_TX_COOKIE_T)); + + unit = tx_cookie.unit; + channel = tx_cookie.channel; + + ptr_sw_gpd_knl = osal_alloc(sizeof(HAL_LIGHTNING_PKT_TX_SW_GPD_T)); + ptr_first_sw_gpd_knl = ptr_sw_gpd_knl; + + /* create SW GPD based on the content of each IOCTL GPD */ + while (1) + { + osal_io_copyFromUser(&ioctl_gpd, + ((void *)((CLX_HUGE_T)tx_cookie.ioctl_gpd_addr)) + +idx*sizeof(HAL_LIGHTNING_PKT_IOCTL_TX_GPD_T), + sizeof(HAL_LIGHTNING_PKT_IOCTL_TX_GPD_T)); + + ptr_sw_gpd_knl->channel = ioctl_gpd.channel; + ptr_sw_gpd_knl->gpd_num = ioctl_gpd.gpd_num; + ptr_sw_gpd_knl->ptr_cookie = (void *)ioctl_gpd.cookie; + + /* directly copy user's HW GPD */ + osal_io_copyFromUser(&ptr_sw_gpd_knl->tx_gpd, + (void *)((CLX_HUGE_T)ioctl_gpd.hw_gpd_addr), + sizeof(HAL_LIGHTNING_PKT_TX_GPD_T)); + + /* replace the callback */ + ptr_sw_gpd_knl->callback = (void *)_hal_lightning_pkt_dev_tx_callback; + + /* save the first SW GPD address from userspace since + * we have replaced the original callback + */ + ptr_sw_gpd_knl->ptr_cookie = (void *)ioctl_gpd.sw_gpd_addr; + + if (HAL_LIGHTNING_PKT_CH_LAST_GPD == ptr_sw_gpd_knl->tx_gpd.ch) + { + ptr_sw_gpd_knl->ptr_next = NULL; + break; + } + else + { + ptr_sw_gpd_knl->ptr_next = (HAL_LIGHTNING_PKT_TX_SW_GPD_T *)osal_alloc( + sizeof(HAL_LIGHTNING_PKT_TX_SW_GPD_T)); + ptr_sw_gpd_knl = ptr_sw_gpd_knl->ptr_next; + idx++; + } + } + + ret = hal_lightning_pkt_sendGpd(unit, channel, ptr_first_sw_gpd_knl); + if (CLX_E_OK != ret) + { + _hal_lightning_pkt_freeTxGpdList(unit, ptr_first_sw_gpd_knl); + } + + /* return 0 if success */ + return (ret); +} + +long +hal_lightning_pkt_dev_ioctl( + struct file *filp, + unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + + /* cmd */ + HAL_LIGHTNING_PKT_IOCTL_CMD_T *ptr_cmd = (HAL_LIGHTNING_PKT_IOCTL_CMD_T *)&cmd; + unsigned int unit = ptr_cmd->field.unit; + HAL_LIGHTNING_PKT_IOCTL_TYPE_T type = ptr_cmd->field.type; + + HAL_LIGHTNING_PKT_DBG(HAL_LIGHTNING_PKT_DBG_COMMON, "u=%u, ioctl type=%u, cmd=%u\n", + unit, type, cmd); + + switch (type) + { + /* network interface */ + case HAL_LIGHTNING_PKT_IOCTL_TYPE_CREATE_INTF: + ret = _hal_lightning_pkt_createIntf(unit, (HAL_LIGHTNING_PKT_IOCTL_NETIF_COOKIE_T *)arg); + break; + + case HAL_LIGHTNING_PKT_IOCTL_TYPE_DESTROY_INTF: + ret = _hal_lightning_pkt_destroyIntf(unit, (HAL_LIGHTNING_PKT_IOCTL_NETIF_COOKIE_T *)arg); + break; + + case HAL_LIGHTNING_PKT_IOCTL_TYPE_GET_INTF: + ret = _hal_lightning_pkt_getIntf(unit, (HAL_LIGHTNING_PKT_IOCTL_NETIF_COOKIE_T *)arg); + break; + + case HAL_LIGHTNING_PKT_IOCTL_TYPE_CREATE_PROFILE: + ret = _hal_lightning_pkt_createProfile(unit, (HAL_LIGHTNING_PKT_IOCTL_NETIF_COOKIE_T *)arg); + break; + + case HAL_LIGHTNING_PKT_IOCTL_TYPE_DESTROY_PROFILE: + ret = _hal_lightning_pkt_destroyProfile(unit, (HAL_LIGHTNING_PKT_IOCTL_NETIF_COOKIE_T *)arg); + break; + + case HAL_LIGHTNING_PKT_IOCTL_TYPE_GET_PROFILE: + ret = _hal_lightning_pkt_getProfile(unit, (HAL_LIGHTNING_PKT_IOCTL_NETIF_COOKIE_T *)arg); + break; + + case HAL_LIGHTNING_PKT_IOCTL_TYPE_GET_INTF_CNT: + ret = _hal_lightning_pkt_getIntfCnt(unit, (HAL_LIGHTNING_PKT_IOCTL_NETIF_COOKIE_T *)arg); + break; + + case HAL_LIGHTNING_PKT_IOCTL_TYPE_CLEAR_INTF_CNT: + ret = _hal_lightning_pkt_clearIntfCnt(unit, (HAL_LIGHTNING_PKT_IOCTL_NETIF_COOKIE_T *)arg); + break; + + /* driver */ + case HAL_LIGHTNING_PKT_IOCTL_TYPE_WAIT_RX_FREE: + ret = _hal_lightning_pkt_schedRxDeQueue(unit, (HAL_LIGHTNING_PKT_IOCTL_RX_COOKIE_T *)arg); + break; + + case HAL_LIGHTNING_PKT_IOCTL_TYPE_WAIT_TX_FREE: + ret = _hal_lightning_pkt_strictTxDeQueue(unit, (HAL_LIGHTNING_PKT_IOCTL_TX_COOKIE_T *)arg); + break; + + case HAL_LIGHTNING_PKT_IOCTL_TYPE_SET_RX_CFG: + ret = hal_lightning_pkt_setRxKnlConfig(unit, (HAL_LIGHTNING_PKT_IOCTL_RX_COOKIE_T *)arg); + break; + + case HAL_LIGHTNING_PKT_IOCTL_TYPE_GET_RX_CFG: + ret = hal_lightning_pkt_getRxKnlConfig(unit, (HAL_LIGHTNING_PKT_IOCTL_RX_COOKIE_T *)arg); + break; + + case HAL_LIGHTNING_PKT_IOCTL_TYPE_DEINIT_TASK: + ret = hal_lightning_pkt_deinitTask(unit); + break; + + case HAL_LIGHTNING_PKT_IOCTL_TYPE_DEINIT_DRV: + ret = hal_lightning_pkt_deinitPktDrv(unit); + break; + + case HAL_LIGHTNING_PKT_IOCTL_TYPE_INIT_TASK: + ret = hal_lightning_pkt_initTask(unit); + break; + + case HAL_LIGHTNING_PKT_IOCTL_TYPE_INIT_DRV: + ret = hal_lightning_pkt_initPktDrv(unit); + break; + + /* counter */ + case HAL_LIGHTNING_PKT_IOCTL_TYPE_GET_TX_CNT: + ret = hal_lightning_pkt_getTxKnlCnt(unit, (HAL_LIGHTNING_PKT_IOCTL_CH_CNT_COOKIE_T *)arg); + break; + + case HAL_LIGHTNING_PKT_IOCTL_TYPE_GET_RX_CNT: + ret = hal_lightning_pkt_getRxKnlCnt(unit, (HAL_LIGHTNING_PKT_IOCTL_CH_CNT_COOKIE_T *)arg); + break; + + case HAL_LIGHTNING_PKT_IOCTL_TYPE_CLEAR_TX_CNT: + ret = hal_lightning_pkt_clearTxKnlCnt(unit, (HAL_LIGHTNING_PKT_IOCTL_TX_COOKIE_T *)arg); + break; + + case HAL_LIGHTNING_PKT_IOCTL_TYPE_CLEAR_RX_CNT: + ret = hal_lightning_pkt_clearRxKnlCnt(unit, (HAL_LIGHTNING_PKT_IOCTL_RX_COOKIE_T *)arg); + break; + + case HAL_LIGHTNING_PKT_IOCTL_TYPE_SET_PORT_ATTR: + ret = hal_lightning_pkt_setPortAttr(unit, (HAL_LIGHTNING_PKT_IOCTL_PORT_COOKIE_T *)arg); + break; + + case HAL_LIGHTNING_PKT_IOCTL_TYPE_GET_PORT_ATTR: + ret = hal_lightning_pkt_getPortAttr(unit, (HAL_LIGHTNING_PKT_IOCTL_PORT_COOKIE_T *)arg); + break; + +#if defined(NETIF_EN_NETLINK) + case HAL_LIGHTNING_PKT_IOCTL_TYPE_NL_SET_INTF_PROPERTY: + ret = _hal_lightning_pkt_setIntfProperty(unit, (HAL_LIGHTNING_PKT_NL_IOCTL_COOKIE_T *)arg); + break; + case HAL_LIGHTNING_PKT_IOCTL_TYPE_NL_GET_INTF_PROPERTY: + ret = _hal_lightning_pkt_getIntfProperty(unit, (HAL_LIGHTNING_PKT_NL_IOCTL_COOKIE_T *)arg); + break; + case HAL_LIGHTNING_PKT_IOCTL_TYPE_NL_CREATE_NETLINK: + ret = _hal_lightning_pkt_createNetlink(unit, (HAL_LIGHTNING_PKT_NL_IOCTL_COOKIE_T *)arg); + break; + case HAL_LIGHTNING_PKT_IOCTL_TYPE_NL_DESTROY_NETLINK: + ret = _hal_lightning_pkt_destroyNetlink(unit, (HAL_LIGHTNING_PKT_NL_IOCTL_COOKIE_T *)arg); + break; + case HAL_LIGHTNING_PKT_IOCTL_TYPE_NL_GET_NETLINK: + ret = _hal_lightning_pkt_getNetlink(unit, (HAL_LIGHTNING_PKT_NL_IOCTL_COOKIE_T *)arg); + break; +#endif + + default: + ret = -1; + break; + } + + return (ret); +} + +/* ----------------------------------------------------------------------------------- Init/Deinit */ +CLX_ERROR_NO_T +hal_lightning_pkt_init( + const UI32_T unit) +{ + /* Init Thread */ + osal_init(); + + /* Reset all database*/ + osal_memset(_hal_lightning_pkt_port_db, 0x0, + (HAL_LIGHTNING_PKT_MAX_PORT_NUM * sizeof(HAL_LIGHTNING_PKT_NETIF_PORT_DB_T))); + osal_memset(_hal_lightning_pkt_rx_cb, 0x0, + CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM*sizeof(HAL_LIGHTNING_PKT_RX_CB_T)); + osal_memset(_hal_lightning_pkt_tx_cb, 0x0, + CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM*sizeof(HAL_LIGHTNING_PKT_TX_CB_T)); + osal_memset(_hal_lightning_pkt_drv_cb, 0x0, + CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM*sizeof(HAL_LIGHTNING_PKT_DRV_CB_T)); + +#if defined(NETIF_EN_NETLINK) + netif_nl_init(); +#endif + + return (0); +} + +CLX_ERROR_NO_T +hal_lightning_pkt_exit( + const UI32_T unit) +{ + /* 1st. Stop all netdev (if any) to prevent kernel from Tx new packets */ + _hal_lightning_pkt_stopAllIntf(unit); + + /* 2nd. Stop Rx HW DMA and free all the DMA buffer hooked on the ring */ + _hal_lightning_pkt_rxStop(unit); + + /* 3rd. Need to wait Rx done task process all the availavle packets on GPD ring */ + + /* 4th. Stop all the internal tasks (if any) */ + hal_lightning_pkt_deinitTask(unit); + + /* 5th. Deinit pkt driver for common database/interrupt source (if required) */ + hal_lightning_pkt_deinitPktDrv(unit); + + /* 6th. Clean up those intf/profiles not been destroyed */ + _hal_lightning_pkt_destroyAllProfile(unit); + _hal_lightning_pkt_destroyAllIntf(unit); + + osal_deinit(); + + return (0); +} diff --git a/platform/clounix/clounix-modules/modules/src/clx_netif/netif_knl.c b/platform/clounix/clounix-modules/modules/src/clx_netif/netif_knl.c new file mode 100755 index 000000000000..5a6eedaf7b7a --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/clx_netif/netif_knl.c @@ -0,0 +1,304 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: netif_knl.c + * PURPOSE: + * To provide operations registered to kernel callback and + * dispatch the operations to different chip drivers. + * + * NOTES: + * + */ + +/***************************************************************************** + * INCLUDE FILE DECLARATIONS + ***************************************************************************** + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if defined(CLX_EN_LIGHTNING) && defined(CLX_EN_DAWN) +#define NETIF_KNL_SUPPORT_CHIP "Lightning/Dawn" +#elif defined(CLX_EN_LIGHTNING) +#define NETIF_KNL_SUPPORT_CHIP "Lightning" +#else +#define NETIF_KNL_SUPPORT_CHIP "Dawn" +#endif + +#define NETIF_KNL_MODULE_DESC "NETIF Kernel Module (" NETIF_KNL_SUPPORT_CHIP ")" + +#define NETIF_KNL_DRIVER_MINOR_NUM (252) /* DO NOT use MISC_DYNAMIC_MINOR */ +#define NETIF_KNL_DRIVER_NAME "clx_netif" + +#define NETIF_KNL_IO_ERROR_RC (-1) + +typedef ssize_t +(*NETIF_KNL_DEV_TX_FUNC_T)( + struct file *file, + const char __user *buf, + size_t count, + loff_t *pos); + +typedef long +(*NETIF_KNL_DEV_IOCTL_FUNC_T)( + struct file *filp, + unsigned int cmd, + unsigned long arg); + +typedef CLX_ERROR_NO_T +(*NETIF_KNL_DEV_INIT_T)( + const UI32_T unit); + +typedef CLX_ERROR_NO_T +(*NETIF_KNL_DEV_EXIT_T)( + const UI32_T unit); + +typedef struct +{ + NETIF_KNL_DEV_IOCTL_FUNC_T ioctl; + NETIF_KNL_DEV_TX_FUNC_T tx; + NETIF_KNL_DEV_INIT_T init; + NETIF_KNL_DEV_EXIT_T exit; +} NETIF_KNL_DEV_OPS_T; + +typedef struct +{ + UI16_T dev_id; + NETIF_KNL_DEV_OPS_T ops; + +} NETIF_KNL_CB_T; + +extern struct pci_dev *_ptr_ext_pci_dev; +static NETIF_KNL_CB_T _netif_knl_cb; +UI32_T ext_dbg_flag = 0; + +#define NETIF_KNL_DEVICE_IS_LIGHTNING(__dev_id__) (HAL_DEVICE_ID_CL8500 == (__dev_id__ & 0xFF00)) +#define NETIF_KNL_DEVICE_IS_DAWN(__dev_id__) (HAL_DEVICE_ID_CL8300 == (__dev_id__ & 0xFF00)) + +#define NETIF_KNL_DBG_FLAG_COMMON (0x1UL << 5) + +#define NETIF_KNL_DBG(__flag__, ...) do \ +{ \ + if (0 != ((__flag__) & (ext_dbg_flag))) \ + { \ + osal_printf(__VA_ARGS__); \ + } \ +}while (0) + +static CLX_ERROR_NO_T +_netif_knl_initDevOps( + const UI16_T dev_id, + NETIF_KNL_DEV_OPS_T *ptr_ops) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + + if (NETIF_KNL_DEVICE_IS_LIGHTNING(dev_id)) + { +#if defined(CLX_EN_LIGHTNING) + NETIF_KNL_DBG(NETIF_KNL_DBG_FLAG_COMMON, + "lightning ops hooked\n"); + ptr_ops->init = hal_lightning_pkt_init; + ptr_ops->exit = hal_lightning_pkt_exit; + ptr_ops->tx = hal_lightning_pkt_dev_tx; + ptr_ops->ioctl = hal_lightning_pkt_dev_ioctl; +#else + NETIF_KNL_DBG(NETIF_KNL_DBG_FLAG_COMMON, + "lightning detected, but ops not support\n"); +#endif + } + else if (NETIF_KNL_DEVICE_IS_DAWN(dev_id)) + { +#if defined(CLX_EN_DAWN) + NETIF_KNL_DBG(NETIF_KNL_DBG_FLAG_COMMON, + "dawn ops hooked\n"); + ptr_ops->init = hal_dawn_pkt_init; + ptr_ops->exit = hal_dawn_pkt_exit; + ptr_ops->tx = hal_dawn_pkt_dev_tx; + ptr_ops->ioctl = hal_dawn_pkt_dev_ioctl; +#else + NETIF_KNL_DBG(NETIF_KNL_DBG_FLAG_COMMON, + "dawn detected, but ops not support\n"); +#endif + } + else + { + NETIF_KNL_DBG(NETIF_KNL_DBG_FLAG_COMMON, + "unknown chip family, dev_id=0x%x\n", + dev_id); + rc = CLX_E_OTHERS; + } + + return (rc); +} + +static CLX_ERROR_NO_T +_netif_knl_getDevId( + UI16_T *ptr_dev_id) +{ + pci_read_config_word(_ptr_ext_pci_dev, PCI_DEVICE_ID, ptr_dev_id); + return (CLX_E_OK); +} + +static int +_netif_knl_dev_open( + struct inode *inode, + struct file *file) +{ + UI32_T unit = 0; + CLX_ERROR_NO_T rc; + + _netif_knl_getDevId(&_netif_knl_cb.dev_id); + + rc = _netif_knl_initDevOps(_netif_knl_cb.dev_id, + &_netif_knl_cb.ops); + if (CLX_E_OK == rc) + { + if (_netif_knl_cb.ops.init != NULL) + { + _netif_knl_cb.ops.init(unit); + } + } + + return (0); +} + +static int +_netif_knl_dev_close( + struct inode *inode, + struct file *file) +{ + return (0); +} + +static ssize_t +_netif_knl_dev_tx( + struct file *file, + const char __user *buf, + size_t count, + loff_t *pos) +{ + long ret = NETIF_KNL_IO_ERROR_RC; + + if (_netif_knl_cb.ops.tx != NULL) + { + ret = _netif_knl_cb.ops.tx(file, buf, count, pos); + } + + return (ret); +} + +static ssize_t +_netif_knl_dev_rx( + struct file *file, + char __user *buf, + size_t count, + loff_t *pos) +{ + return (0); +} + +static long +_netif_knl_dev_ioctl( + struct file *filp, + unsigned int cmd, + unsigned long arg) +{ + long ret = NETIF_KNL_IO_ERROR_RC; + + if (_netif_knl_cb.ops.ioctl != NULL) + { + ret = _netif_knl_cb.ops.ioctl(filp, cmd, arg); + } + + return (ret); +} + +#ifdef CONFIG_COMPAT +static long +_netif_knl_dev_compat_ioctl( + struct file *filp, + unsigned int cmd, + unsigned long arg) +{ + return _netif_knl_dev_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static struct file_operations _netif_knl_dev_ops = +{ + .owner = THIS_MODULE, + .open = _netif_knl_dev_open, + .release = _netif_knl_dev_close, + .write = _netif_knl_dev_tx, + .read = _netif_knl_dev_rx, + .unlocked_ioctl = _netif_knl_dev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = _netif_knl_dev_compat_ioctl, +#endif +}; + +static struct miscdevice _netif_knl_dev = +{ + .minor = NETIF_KNL_DRIVER_MINOR_NUM, + .name = NETIF_KNL_DRIVER_NAME, + .fops = &_netif_knl_dev_ops, +}; + +static int __init +netif_knl_init(void) +{ + misc_register(&_netif_knl_dev); + + osal_memset(&_netif_knl_cb, 0x0, sizeof(NETIF_KNL_CB_T)); + + return (0); +} + +static void __exit +netif_knl_exit(void) +{ + UI32_T unit = 0; + + if (_netif_knl_cb.ops.exit != NULL) + { + _netif_knl_cb.ops.exit(unit); + } + + misc_deregister(&_netif_knl_dev); +} + +module_init(netif_knl_init); +module_exit(netif_knl_exit); + +module_param(ext_dbg_flag, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(ext_dbg_flag, "bit0:error, bit1:tx, bit2:rx, bit3:intf, bit4:profile, " \ + "bit5:common, bit6:netlink"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Clounix"); +MODULE_DESCRIPTION(NETIF_KNL_MODULE_DESC); diff --git a/platform/clounix/clounix-modules/modules/src/clx_netif/netif_nl.c b/platform/clounix/clounix-modules/modules/src/clx_netif/netif_nl.c new file mode 100755 index 000000000000..0764972eeaa4 --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/clx_netif/netif_nl.c @@ -0,0 +1,812 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: netif_xxx.c + * PURPOSE: + * It provide xxx API. + * NOTES: + */ +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +extern UI32_T ext_dbg_flag; + +#define NETIF_NL_DBG(__flag__, ...) do \ +{ \ + if (0 != ((__flag__) & (ext_dbg_flag))) \ + { \ + osal_printf(__VA_ARGS__); \ + } \ +}while (0) + +#define NETIF_NL_DBG_NETLINK (0x1UL << 6) + +#define NETIF_NL_FAMILY_NUM_MAX (256) +#define NETIF_NL_INTF_NUM_MAX (256) + +#define NETIF_NL_GET_FAMILY_META(__idx__) &(_netif_nl_cb.fam_entry[__idx__].meta) +#define NETIF_NL_GET_INTF_IGR_SAMPLE_RATE(__inft_id__) (_netif_nl_cb.intf_entry[__inft_id__].igr_sample_rate) + +#define NETIF_NL_FAMILY_IS_PSAMPLE(__ptr_family__) (0 == strncmp(__ptr_family__->name, \ + NETIF_NL_PSAMPLE_FAMILY_NAME, \ + NETIF_NL_NETLINK_NAME_LEN)) ? 1 : 0 + +/* porting part */ +#define NETIF_NL_VER_NUM (1) +#define NETIF_NL_PSAMPLE_MAX_ATTR_NUM (NETIF_NL_PSAMPLE_ATTR_LAST) +#define NETIF_NL_REGISTER_FAMILY(__family__) genl_register_family(__family__) + +#define NETIF_NL_UNREGISTER_FAMILY(__family__) genl_unregister_family(__family__) +#define NETIF_NL_ALLOC_SKB(__len__) genlmsg_new(__len__, GFP_ATOMIC) +#define NETIF_NL_FREE_SKB(__ptr_skb__) nlmsg_free(__ptr_skb__) + +#define NETIF_NL_SEND_PKT(__ptr_family__, __mcgrp_id__, __ptr_skb__) \ + genlmsg_multicast_netns(__ptr_family__, \ + &init_net, \ + __ptr_skb__, \ + 0, /* pid, avoid loop */ \ + __mcgrp_id__, \ + GFP_ATOMIC) +#define NETIF_NL_SET_SKB_ATTR_HDR(__skb__, __family__, __hdr_len__, __cmd__) \ + genlmsg_put(__skb__, 0, 0, __family__, \ + __hdr_len__, __cmd__) +#define NETIF_NL_END_SKB_ATTR_HDR(__skb__, __hdr__) genlmsg_end(__skb__, __hdr__) + +#define NETIF_NL_SET_16_BIT_ATTR(__skb__, __attr__, __data__) nla_put_u16(__skb__, __attr__, __data__) +#define NETIF_NL_SET_32_BIT_ATTR(__skb__, __attr__, __data__) nla_put_u32(__skb__, __attr__, __data__) + + +/* + * <----------- nla_total_size(payload) -------------> + * +------------------+- - -+- - - - - - - - - +- - -+ + * | Attribute Header | Pad | Payload | Pad | + * +------------------+- - -+- - - - - - - - - +- - -+ + * + * + * <-------- nla_attr_size(payload) ----------> + * +------------------+- - -+- - - - - - - - - +- - -+ + * | Attribute Header | Pad | Payload | Pad | + * +------------------+- - -+- - - - - - - - - +- - -+ + * + */ +/* total size = attr data size + attr header size */ +#define NETIF_NL_GET_ATTR_TOTAL_SIZE(__data_size__) nla_total_size(__data_size__) +#define NETIF_NL_GET_ATTR_SIZE(__data_size__) nla_attr_size(__data_size__) /* without padding */ + + +/* psample's family and group parameter */ +#define NETIF_NL_PSAMPLE_FAMILY_NAME "psample" +#define NETIF_NL_PSAMPLE_MC_GROUP_NAME_DATA "packets" +#define NETIF_NL_PSAMPLE_MC_GROUP_NAME_CFG "config" +#define NETIF_NL_PSAMPLE_MC_GROUP_NUM (NETIF_NL_PSAMPLE_MC_GROUP_ID_LAST) +#define NETIF_NL_DEFAULT_MC_GROUP_NUM (1) + +#define NETIF_NL_PSAMPLE_PKT_LEN_MAX (9216) +#define NETIF_NL_PSAMPLE_DFLT_USR_GROUP_ID (1) + +typedef enum +{ + NETIF_NL_PSAMPLE_MC_GROUP_ID_CONFIG = 0, + NETIF_NL_PSAMPLE_MC_GROUP_ID_SAMPLE, + NETIF_NL_PSAMPLE_MC_GROUP_ID_LAST, +} NETIF_NL_PSAMPLE_MC_GROUP_ID_T; + +typedef enum +{ + NETIF_NL_PSAMPLE_ATTR_IIFINDEX = 0, + NETIF_NL_PSAMPLE_ATTR_OIFINDEX, + NETIF_NL_PSAMPLE_ATTR_ORIGSIZE, + NETIF_NL_PSAMPLE_ATTR_SAMPLE_GROUP, + NETIF_NL_PSAMPLE_ATTR_GROUP_SEQ, + NETIF_NL_PSAMPLE_ATTR_SAMPLE_RATE, + NETIF_NL_PSAMPLE_ATTR_DATA, + NETIF_NL_PSAMPLE_ATTR_LAST +} NETIF_NL_PSAMPLE_ATTR_ID_T; + + +typedef struct genl_multicast_group NETIF_NL_MC_GROUP_T; +typedef struct genl_family NETIF_NL_FAMILY_T; + +static NETIF_NL_MC_GROUP_T _netif_nl_psample_mc_group[NETIF_NL_PSAMPLE_MC_GROUP_ID_LAST]; +static C8_T *_ptr_netif_nl_psample_mc_group_name[NETIF_NL_PSAMPLE_MC_GROUP_ID_LAST] = + { + NETIF_NL_PSAMPLE_MC_GROUP_NAME_CFG, + NETIF_NL_PSAMPLE_MC_GROUP_NAME_DATA + }; + +static NETIF_NL_MC_GROUP_T _netif_nl_default_mc_group[NETIF_NL_DEFAULT_MC_GROUP_NUM]; +static C8_T *_ptr_netif_nl_default_mc_group_name[NETIF_NL_DEFAULT_MC_GROUP_NUM] = + { + "default", + }; + +typedef struct +{ + NETIF_NL_FAMILY_T meta; + BOOL_T valid; + +} NETIF_NL_FAMILY_ENTRY_T; + +typedef struct +{ + UI32_T igr_sample_rate; + UI32_T egr_sample_rate; + UI32_T trunc_size; +} NETIF_NL_INTF_ENTRY_T; + +typedef struct +{ + NETIF_NL_FAMILY_ENTRY_T fam_entry[NETIF_NL_FAMILY_NUM_MAX]; + NETIF_NL_INTF_ENTRY_T intf_entry[NETIF_NL_INTF_NUM_MAX]; /* sorted in intf_id */ + UI32_T seq_num; +} NETIF_NL_CB_T; + +static NETIF_NL_CB_T _netif_nl_cb; + +/* should extract to common */ +struct net_device_priv +{ + struct net_device *ptr_net_dev; + struct net_device_stats stats; + UI32_T unit; + UI32_T id; + UI32_T port; + UI16_T vlan; + UI32_T speed; +}; + +static CLX_ERROR_NO_T +_netif_nl_setIntfIgrSampleRate( + const UI32_T unit, + const UI32_T id, + const UI32_T rate) +{ + NETIF_NL_CB_T *ptr_cb = &_netif_nl_cb; + + ptr_cb->intf_entry[id].igr_sample_rate = rate; + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_netif_nl_setIntfEgrSampleRate( + const UI32_T unit, + const UI32_T id, + const UI32_T rate) +{ + NETIF_NL_CB_T *ptr_cb = &_netif_nl_cb; + + ptr_cb->intf_entry[id].egr_sample_rate = rate; + + return (CLX_E_OK); +} + + +CLX_ERROR_NO_T +netif_nl_setIntfProperty( + const UI32_T unit, + const UI32_T id, + const NETIF_NL_INTF_PROPERTY_T property, + const UI32_T param0, + const UI32_T param1) +{ + CLX_ERROR_NO_T rc = CLX_E_BAD_PARAMETER; + + if (NETIF_NL_INTF_PROPERTY_IGR_SAMPLING_RATE == property) + { + NETIF_NL_DBG(NETIF_NL_DBG_NETLINK, + "receive set igr sample rate req, id=%d, property=%d, param0=%d, param=%d\n", + id, property, param0, param1); + rc = _netif_nl_setIntfIgrSampleRate(unit, id, param0); + } + else if (NETIF_NL_INTF_PROPERTY_EGR_SAMPLING_RATE == property) + { + NETIF_NL_DBG(NETIF_NL_DBG_NETLINK, + "receive set egr sample rate req, id=%d, property=%d, param0=%d, param=%d\n", + id, property, param0, param1); + rc = _netif_nl_setIntfEgrSampleRate(unit, id, param0); + } + else + { + NETIF_NL_DBG(NETIF_NL_DBG_NETLINK, + "[error] unknown property, property=%d\n", property); + } + + return (rc); +} + +static CLX_ERROR_NO_T +_netif_nl_getIntfIgrSampleRate( + const UI32_T unit, + const UI32_T id, + UI32_T *ptr_rate) +{ + NETIF_NL_CB_T *ptr_cb = &_netif_nl_cb; + + *ptr_rate = ptr_cb->intf_entry[id].igr_sample_rate; + + return (CLX_E_OK); +} + +static CLX_ERROR_NO_T +_netif_nl_getIntfEgrSampleRate( + const UI32_T unit, + const UI32_T id, + UI32_T *ptr_rate) +{ + NETIF_NL_CB_T *ptr_cb = &_netif_nl_cb; + + *ptr_rate = ptr_cb->intf_entry[id].egr_sample_rate; + + return (CLX_E_OK); +} + + +CLX_ERROR_NO_T +netif_nl_getIntfProperty( + const UI32_T unit, + const UI32_T id, + const NETIF_NL_INTF_PROPERTY_T property, + UI32_T *ptr_param0, + UI32_T *ptr_param1) +{ + CLX_ERROR_NO_T rc = CLX_E_BAD_PARAMETER; + + if (NETIF_NL_INTF_PROPERTY_IGR_SAMPLING_RATE == property) + { + rc = _netif_nl_getIntfIgrSampleRate(unit, id, ptr_param0); + } + else if (NETIF_NL_INTF_PROPERTY_EGR_SAMPLING_RATE == property) + { + rc = _netif_nl_getIntfEgrSampleRate(unit, id, ptr_param0); + } + else + { + NETIF_NL_DBG(NETIF_NL_DBG_NETLINK, + "[error] unknown property, property=%d\n", + property); + } + + return (rc); +} + +CLX_ERROR_NO_T +_netif_nl_allocNlFamilyEntry( + NETIF_NL_CB_T *ptr_cb, + UI32_T *ptr_index) +{ + UI32_T idx; + CLX_ERROR_NO_T rc = CLX_E_TABLE_FULL; + + for (idx = 0; idx < NETIF_NL_FAMILY_NUM_MAX; idx++) + { + if (FALSE == ptr_cb->fam_entry[idx].valid) + { + *ptr_index = idx; + ptr_cb->fam_entry[idx].valid = TRUE; + rc = CLX_E_OK; + break; + } + } + + return (rc); +} + +void +_netif_nl_freeNlFamilyEntry( + NETIF_NL_CB_T *ptr_cb, + const UI32_T index) +{ + NETIF_NL_DBG(NETIF_NL_DBG_NETLINK, + "[DBG] free netlink family entry, idx=%d\n", + index); + ptr_cb->fam_entry[index].valid = FALSE; +} + +CLX_ERROR_NO_T +_netif_nl_setNlMcgroupPsample( + NETIF_NL_FAMILY_T *ptr_nl_family) +{ + NETIF_NL_MC_GROUP_T *ptr_nl_mc_group = _netif_nl_psample_mc_group; + UI32_T idx; + + /* init the mc group and hook the group to family */ + osal_memset(ptr_nl_mc_group, 0x0, + (NETIF_NL_PSAMPLE_MC_GROUP_NUM * sizeof(NETIF_NL_MC_GROUP_T))); + + for (idx = 0; idx < NETIF_NL_PSAMPLE_MC_GROUP_ID_LAST; idx++) + { + osal_memcpy(ptr_nl_mc_group[idx].name, + _ptr_netif_nl_psample_mc_group_name[idx], + osal_strlen(_ptr_netif_nl_psample_mc_group_name[idx])); + } + ptr_nl_family->n_mcgrps = NETIF_NL_PSAMPLE_MC_GROUP_NUM; + ptr_nl_family->mcgrps = ptr_nl_mc_group; + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +_netif_nl_setNlMcgroupDefault( + NETIF_NL_FAMILY_T *ptr_nl_family) +{ + NETIF_NL_MC_GROUP_T *ptr_nl_mc_group = _netif_nl_default_mc_group; + UI32_T idx; + + /* init the mc group and hook the group to family */ + osal_memset(ptr_nl_mc_group, 0x0, + (NETIF_NL_DEFAULT_MC_GROUP_NUM * sizeof(NETIF_NL_MC_GROUP_T))); + + for (idx = 0; idx < NETIF_NL_DEFAULT_MC_GROUP_NUM; idx++) + { + osal_memcpy(ptr_nl_mc_group[idx].name, + _ptr_netif_nl_default_mc_group_name[idx], + osal_strlen(_ptr_netif_nl_default_mc_group_name[idx])); + } + ptr_nl_family->n_mcgrps = NETIF_NL_DEFAULT_MC_GROUP_NUM; + ptr_nl_family->mcgrps = ptr_nl_mc_group; + + return (CLX_E_OK); +} + +#define NETIF_NL_IS_FAMILY_ENTRY_VALID(__idx__) \ + (TRUE == _netif_nl_cb.fam_entry[__idx__].valid) ? (TRUE) : (FALSE) +CLX_ERROR_NO_T +netif_nl_createNetlink( + const UI32_T unit, + NETIF_NL_NETLINK_T *ptr_netlink, + UI32_T *ptr_netlink_id) +{ + NETIF_NL_CB_T *ptr_cb = &_netif_nl_cb; + UI32_T entry_id; + NETIF_NL_FAMILY_T *ptr_nl_family; + NETIF_NL_MC_GROUP_T *ptr_nl_mcgrp; + UI32_T idx; + int ret; + CLX_ERROR_NO_T rc; + + rc = _netif_nl_allocNlFamilyEntry(ptr_cb, &entry_id); + if (CLX_E_OK == rc) + { + ptr_nl_family = NETIF_NL_GET_FAMILY_META(entry_id); + + /* fill in the meta data for that netlink family */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) + ptr_nl_family->id = GENL_ID_GENERATE; /* family id can be ignored since linux 4.10 */ +#endif + ptr_nl_family->version = NETIF_NL_VER_NUM; + ptr_nl_family->maxattr = NETIF_NL_PSAMPLE_MAX_ATTR_NUM; + ptr_nl_family->netnsok = true; + osal_memcpy(ptr_nl_family->name, ptr_netlink->name, NETIF_NL_NETLINK_NAME_LEN); + + /* fill in the mc group info */ + ptr_nl_mcgrp = osal_alloc(sizeof(NETIF_NL_MC_GROUP_T)*ptr_netlink->mc_group_num); + if (NULL != ptr_nl_mcgrp) + { + NETIF_NL_DBG(NETIF_NL_DBG_NETLINK, "[DBG] create mc group:\n"); + for (idx = 0; idx < ptr_netlink->mc_group_num; idx++) + { + NETIF_NL_DBG(NETIF_NL_DBG_NETLINK, + "[DBG] - mcgrp%d: %s\n", idx, ptr_netlink->mc_group[idx].name); + osal_memcpy(ptr_nl_mcgrp[idx].name, ptr_netlink->mc_group[idx].name, + NETIF_NL_NETLINK_NAME_LEN); + } + ptr_nl_family->n_mcgrps = ptr_netlink->mc_group_num; + ptr_nl_family->mcgrps = ptr_nl_mcgrp; + + /* register the family to kernel */ + ret = NETIF_NL_REGISTER_FAMILY(ptr_nl_family); + if (0 == ret) + { + *ptr_netlink_id = entry_id; + NETIF_NL_DBG(NETIF_NL_DBG_NETLINK, + "[DBG] create netlink family, name=%s, entry_idx=%d, mcgrp_num=%d\n", + ptr_netlink->name, entry_id, ptr_nl_family->n_mcgrps); + rc = CLX_E_OK; + } + else + { + NETIF_NL_DBG(NETIF_NL_DBG_NETLINK, + "[DBG] register netlink family failed, name=%s, ret=%d\n", + ptr_netlink->name, ret); + osal_free(ptr_nl_mcgrp); + _netif_nl_freeNlFamilyEntry(ptr_cb, entry_id); + rc = CLX_E_OTHERS; + } + } + else + { + NETIF_NL_DBG(NETIF_NL_DBG_NETLINK, "[DBG] alloc mcgrp failed\n"); + rc = CLX_E_NO_MEMORY; + } + } + + return (rc); +} + +CLX_ERROR_NO_T +netif_nl_destroyNetlink( + const UI32_T unit, + const UI32_T netlink_id) +{ + NETIF_NL_CB_T *ptr_cb = &_netif_nl_cb; + UI32_T entry_idx = netlink_id; + NETIF_NL_FAMILY_T *ptr_nl_family; + int ret; + CLX_ERROR_NO_T rc; + + if (TRUE == NETIF_NL_IS_FAMILY_ENTRY_VALID(entry_idx)) + { + ptr_nl_family = NETIF_NL_GET_FAMILY_META(entry_idx); + ret = NETIF_NL_UNREGISTER_FAMILY(ptr_nl_family); + if (0 == ret) + { + osal_free(ptr_nl_family->mcgrps); + _netif_nl_freeNlFamilyEntry(ptr_cb, entry_idx); + rc = CLX_E_OK; + } + else + { + NETIF_NL_DBG(NETIF_NL_DBG_NETLINK, + "[DBG] unregister netlink family failed, name=%s, ret=%d\n", + ptr_nl_family->name, ret); + rc = CLX_E_OTHERS; + } + } + else + { + NETIF_NL_DBG(NETIF_NL_DBG_NETLINK, + "[DBG] destroy netlink failed, invalid netlink_id %d\n", + netlink_id); + rc = CLX_E_ENTRY_NOT_FOUND; + } + + return (rc); +} + +CLX_ERROR_NO_T +netif_nl_getNetlink( + const UI32_T unit, + const UI32_T netlink_id, + NETIF_NL_NETLINK_T *ptr_netlink) +{ + UI32_T entry_idx = netlink_id; + NETIF_NL_FAMILY_T *ptr_meta; + UI32_T grp_idx; + CLX_ERROR_NO_T rc = CLX_E_OK; + + if (TRUE == NETIF_NL_IS_FAMILY_ENTRY_VALID(entry_idx)) + { + NETIF_NL_DBG(NETIF_NL_DBG_NETLINK, + "[DBG] get valid netlink, id=%d\n", netlink_id); + + ptr_netlink->id = netlink_id; + ptr_meta = NETIF_NL_GET_FAMILY_META(entry_idx); + + ptr_netlink->mc_group_num = ptr_meta->n_mcgrps; + osal_memcpy(ptr_netlink->name, ptr_meta->name, NETIF_NL_NETLINK_NAME_LEN); + + for (grp_idx = 0; grp_idx < ptr_meta->n_mcgrps; grp_idx++) + { + osal_memcpy(ptr_netlink->mc_group[grp_idx].name, + ptr_meta->mcgrps[grp_idx].name, + NETIF_NL_NETLINK_NAME_LEN); + } + } + else + { + NETIF_NL_DBG(NETIF_NL_DBG_NETLINK, + "[DBG] get netlink failed, invalid netlink_id %d\n", + netlink_id); + rc = CLX_E_ENTRY_NOT_FOUND; + } + + return (rc); +} + + +CLX_ERROR_NO_T +_netif_nl_getFamilyByName( + NETIF_NL_CB_T *ptr_cb, + const C8_T *ptr_name, + NETIF_NL_FAMILY_T **pptr_nl_family) +{ + UI32_T idx; + CLX_ERROR_NO_T rc = CLX_E_ENTRY_NOT_FOUND; + + for (idx = 0; idx < NETIF_NL_FAMILY_NUM_MAX; idx++) + { + if ((TRUE == ptr_cb->fam_entry[idx].valid) && + (0 == strncmp(ptr_cb->fam_entry[idx].meta.name, + ptr_name, + NETIF_NL_NETLINK_NAME_LEN))) + { + *pptr_nl_family = &(ptr_cb->fam_entry[idx].meta); + rc = CLX_E_OK; + break; + } + } + + if (CLX_E_ENTRY_NOT_FOUND == rc) + { + NETIF_NL_DBG(NETIF_NL_DBG_NETLINK, + "[DBG] find family failed, name=%s\n", + ptr_name); + } + + return (rc); +} + +CLX_ERROR_NO_T +_netif_nl_getMcgrpIdByName( + NETIF_NL_FAMILY_T *ptr_nl_family, + const C8_T *ptr_mcgrp_name, + UI32_T *ptr_mcgrp_id) +{ + UI32_T idx; + CLX_ERROR_NO_T rc = CLX_E_ENTRY_NOT_FOUND; + + for (idx = 0; idx < ptr_nl_family->n_mcgrps; idx++) + { + if ((0 == strncmp(ptr_nl_family->mcgrps[idx].name, + ptr_mcgrp_name, + NETIF_NL_NETLINK_NAME_LEN))) + { + *ptr_mcgrp_id = idx; + rc = CLX_E_OK; + break; + } + } + + if (CLX_E_OK != rc) + { + NETIF_NL_DBG(NETIF_NL_DBG_NETLINK, + "[DBG] find mcgrp %s failed in family %s\n", + ptr_mcgrp_name, ptr_nl_family->name); + } + + return (rc); +} + +CLX_ERROR_NO_T +_netif_nl_allocPsampleSkb( + NETIF_NL_CB_T *ptr_cb, + NETIF_NL_FAMILY_T *ptr_nl_family, + struct sk_buff *ptr_ori_skb, + struct sk_buff **pptr_nl_skb) +{ + UI32_T msg_hdr_len; + UI32_T data_len; + struct sk_buff *ptr_nl_skb; + UI16_T igr_intf_idx; + struct net_device_priv *ptr_priv; + UI32_T rate; + UI32_T intf_id; + void *ptr_nl_hdr = NULL; + struct nlattr *ptr_nl_attr; + CLX_ERROR_NO_T rc = CLX_E_OK; + + /* make sure the total len (original pkt len + hdr msg) < PSAMPLE_MAX_PACKET_SIZE */ + + msg_hdr_len = NETIF_NL_GET_ATTR_TOTAL_SIZE(sizeof(UI16_T)) + /* PSAMPLE_ATTR_IIFINDEX */ + NETIF_NL_GET_ATTR_TOTAL_SIZE(sizeof(UI32_T)) + /* PSAMPLE_ATTR_SAMPLE_RATE */ + NETIF_NL_GET_ATTR_TOTAL_SIZE(sizeof(UI32_T)) + /* PSAMPLE_ATTR_ORIGSIZE */ + NETIF_NL_GET_ATTR_TOTAL_SIZE(sizeof(UI32_T)) + /* PSAMPLE_ATTR_SAMPLE_GROUP */ + NETIF_NL_GET_ATTR_TOTAL_SIZE(sizeof(UI32_T)); /* PSAMPLE_ATTR_GROUP_SEQ */ + + data_len = NETIF_NL_GET_ATTR_TOTAL_SIZE(ptr_ori_skb->len); + + if ((msg_hdr_len + NETIF_NL_GET_ATTR_TOTAL_SIZE(ptr_ori_skb->len)) > NETIF_NL_PSAMPLE_PKT_LEN_MAX) + { + data_len = NETIF_NL_PSAMPLE_PKT_LEN_MAX - msg_hdr_len - NLA_HDRLEN - NLA_ALIGNTO; + } + else + { + data_len = ptr_ori_skb->len; + } + + ptr_nl_skb = NETIF_NL_ALLOC_SKB(NETIF_NL_GET_ATTR_TOTAL_SIZE(data_len) + msg_hdr_len); + if (NULL != ptr_nl_skb) + { + /* to create a netlink msg header (cmd=0) */ + ptr_nl_hdr = NETIF_NL_SET_SKB_ATTR_HDR(ptr_nl_skb, ptr_nl_family, 0, 0); + if (NULL != ptr_nl_hdr) + { + /* obtain the intf index for the igr_port */ + igr_intf_idx = ptr_ori_skb->dev->ifindex; + NETIF_NL_SET_16_BIT_ATTR(ptr_nl_skb, NETIF_NL_PSAMPLE_ATTR_IIFINDEX, + (UI16_T)igr_intf_idx); + + /* meta header */ + /* use the igr port id as the index for the database to get sample rate */ + ptr_priv = netdev_priv(ptr_ori_skb->dev); + intf_id = ptr_priv->port; + rate = NETIF_NL_GET_INTF_IGR_SAMPLE_RATE(intf_id); + NETIF_NL_SET_32_BIT_ATTR(ptr_nl_skb, NETIF_NL_PSAMPLE_ATTR_SAMPLE_RATE, rate); + NETIF_NL_SET_32_BIT_ATTR(ptr_nl_skb, NETIF_NL_PSAMPLE_ATTR_ORIGSIZE, data_len); + NETIF_NL_SET_32_BIT_ATTR(ptr_nl_skb, NETIF_NL_PSAMPLE_ATTR_SAMPLE_GROUP, + NETIF_NL_PSAMPLE_DFLT_USR_GROUP_ID); + NETIF_NL_SET_32_BIT_ATTR(ptr_nl_skb, NETIF_NL_PSAMPLE_ATTR_GROUP_SEQ, ptr_cb->seq_num); + ptr_cb->seq_num++; + + /* data */ + ptr_nl_attr = (struct nlattr *)skb_put(ptr_nl_skb, NETIF_NL_GET_ATTR_TOTAL_SIZE(data_len)); + ptr_nl_attr->nla_type = NETIF_NL_PSAMPLE_ATTR_DATA; + /* get the attr size without padding, since it's the last one */ + ptr_nl_attr->nla_len = NETIF_NL_GET_ATTR_SIZE(data_len); + skb_copy_bits(ptr_ori_skb, 0, nla_data(ptr_nl_attr), data_len); + + NETIF_NL_END_SKB_ATTR_HDR(ptr_nl_skb, ptr_nl_hdr); + } + else + { + rc = CLX_E_OTHERS; + } + } + else + { + rc = CLX_E_OTHERS; + } + + *pptr_nl_skb = ptr_nl_skb; + + return (rc); +} + +CLX_ERROR_NO_T +_netif_nl_allocNetlinkSkb( + NETIF_NL_CB_T *ptr_cb, + NETIF_NL_FAMILY_T *ptr_nl_family, + struct sk_buff *ptr_ori_skb, + struct sk_buff **pptr_nl_skb) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + + /* need to fill specific skb header format */ + if (NETIF_NL_FAMILY_IS_PSAMPLE(ptr_nl_family)) + { + rc = _netif_nl_allocPsampleSkb(ptr_cb, ptr_nl_family, + ptr_ori_skb, pptr_nl_skb); + if (CLX_E_OK != rc) + { + NETIF_NL_DBG(NETIF_NL_DBG_NETLINK, + "[DBG] alloc netlink skb failed\n"); + } + } + else + { + NETIF_NL_DBG(NETIF_NL_DBG_NETLINK, + "[DBG] unknown netlink family\n"); + rc = CLX_E_OTHERS; + } + + return (rc); +} + +CLX_ERROR_NO_T +_netif_nl_sendNetlinkSkb( + NETIF_NL_FAMILY_T *ptr_nl_family, + UI32_T nl_mcgrp_id, + struct sk_buff *ptr_nl_skb) +{ + int ret; + CLX_ERROR_NO_T rc; + + ret = NETIF_NL_SEND_PKT(ptr_nl_family, nl_mcgrp_id, ptr_nl_skb); + if (0 == ret) + { + rc = CLX_E_OK; + } + else + { + /* in errno_base.h, #define ESRCH 3 : No such process */ + NETIF_NL_DBG(NETIF_NL_DBG_NETLINK, + "send skb to mc group failed, ret=%d\n", ret); + rc = CLX_E_OTHERS; + } + + return (rc); +} + +void +_netif_nl_freeNetlinkSkb( + struct sk_buff *ptr_nl_skb) +{ + NETIF_NL_DBG(NETIF_NL_DBG_NETLINK, "[DBG] free nl skb\n"); + NETIF_NL_FREE_SKB(ptr_nl_skb); +} + +CLX_ERROR_NO_T +_netif_nl_forwardPkt( + NETIF_NL_CB_T *ptr_cb, + NETIF_NL_RX_DST_NETLINK_T *ptr_nl_dest, + struct sk_buff *ptr_ori_skb) +{ + struct sk_buff *ptr_nl_skb = NULL; + NETIF_NL_FAMILY_T *ptr_nl_family; + UI32_T nl_mcgrp_id; + CLX_ERROR_NO_T rc; + + rc = _netif_nl_getFamilyByName(ptr_cb, ptr_nl_dest->name, + &ptr_nl_family); + if (CLX_E_OK == rc) + { + rc = _netif_nl_getMcgrpIdByName(ptr_nl_family, ptr_nl_dest->mc_group_name, + &nl_mcgrp_id); + if (CLX_E_OK == rc) + { + rc = _netif_nl_allocNetlinkSkb(ptr_cb, ptr_nl_family, + ptr_ori_skb, &ptr_nl_skb); + if (CLX_E_OK == rc) + { + rc = _netif_nl_sendNetlinkSkb(ptr_nl_family, nl_mcgrp_id, + ptr_nl_skb); + if (CLX_E_OK != rc) + { + /* _netif_nl_freeNetlinkSkb(ptr_nl_skb); */ + } + } + } + } + + return (rc); +} + +CLX_ERROR_NO_T +netif_nl_rxSkb( + const UI32_T unit, + struct sk_buff *ptr_skb, + void *ptr_cookie) +{ + NETIF_NL_CB_T *ptr_cb = &_netif_nl_cb; + + NETIF_NL_RX_DST_NETLINK_T *ptr_nl_dest; + CLX_ERROR_NO_T rc; + + ptr_nl_dest = (NETIF_NL_RX_DST_NETLINK_T *)ptr_cookie; + + /* send the packet to netlink mcgroup */ + rc = _netif_nl_forwardPkt(ptr_cb, ptr_nl_dest, ptr_skb); + + /* need to free the original skb anyway */ + osal_skb_free(ptr_skb); + + return (rc); +} + +CLX_ERROR_NO_T +netif_nl_init(void) +{ + osal_memset(&_netif_nl_cb, 0x0, sizeof(NETIF_NL_CB_T)); + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +netif_nl_deinit(void) +{ + return (CLX_E_OK); +} + diff --git a/platform/clounix/clounix-modules/modules/src/clx_netif/netif_osal.c b/platform/clounix/clounix-modules/modules/src/clx_netif/netif_osal.c new file mode 100755 index 000000000000..8472425266b3 --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/clx_netif/netif_osal.c @@ -0,0 +1,755 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: netif_osal.c + * PURPOSE: + * It provide customer linux API. + * NOTES: + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) +#include +#endif +#include + +/* ----------------------------------------------------------------------------------- macro value */ +#define OSAL_US_PER_SECOND (1000000) /* macro second per second */ +#define OSAL_NS_PER_USECOND (1000) /* nano second per macro second */ + +/* ----------------------------------------------------------------------------------- macro function */ +#define OSAL_LOG_ERR(msg, ...) \ + osal_printf("\033[31m\033[0m"msg, __LINE__, ##__VA_ARGS__) + +/* ----------------------------------------------------------------------------------- struct */ +extern struct pci_dev *_ptr_ext_pci_dev; + +static linux_thread_t _osal_thread_head = {{0}}; + +/* ----------------------------------------------------------------------------------- function */ +/* general */ +void * +osal_memset( + void *ptr_mem, + const I32_T value, + const UI32_T num) +{ + return memset(ptr_mem, value, num); +} + +void * +osal_memcpy( + void *ptr_dst, + const void *ptr_src, + const UI32_T num) +{ + return memcpy(ptr_dst, ptr_src, num); +} + +UI32_T +osal_strlen( + const C8_T *ptr_str) +{ + return strlen(ptr_str); +} + +void +osal_printf( + const C8_T *ptr_fmt, + ...) +{ + va_list ap; + char buf[OSAL_PRN_BUF_SZ]; + + if (NULL != ptr_fmt) + { + va_start(ap, ptr_fmt); + vsnprintf(buf, OSAL_PRN_BUF_SZ, ptr_fmt, ap); + va_end(ap); + + printk("%s", buf); + } +} + +void * +osal_alloc( + const UI32_T size) +{ + return kmalloc(size, GFP_ATOMIC); +} + +void +osal_free( + const void *ptr_mem) +{ + kfree(ptr_mem); +} + +/* thread */ +CLX_ERROR_NO_T +osal_init(void) +{ + linux_thread_t *ptr_thread_head = &_osal_thread_head; + + memset(ptr_thread_head, 0x0, sizeof(linux_thread_t)); + + /* init */ + ptr_thread_head->ptr_prev = ptr_thread_head; + ptr_thread_head->ptr_next = ptr_thread_head; + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_deinit(void) +{ + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_createThread( + const C8_T *ptr_thread_name, + const UI32_T stack_size, + const UI32_T priority, + void (function)(void*), + void *ptr_arg, + CLX_THREAD_ID_T *ptr_thread_id) +{ + char dft_name[OSAL_THREAD_NAME_LEN + 1] = OSAL_THREAD_DFT_NAME; + linux_thread_t *ptr_thread_head = &_osal_thread_head; + linux_thread_t *ptr_thread_node = osal_alloc(sizeof(linux_thread_t)); + + /* process name */ + osal_memcpy(ptr_thread_node->name, (0 == osal_strlen(ptr_thread_name))? + dft_name : ptr_thread_name, OSAL_THREAD_NAME_LEN); + ptr_thread_node->name[OSAL_THREAD_NAME_LEN] = '\0'; + + /* init */ + ptr_thread_node->ptr_task = kthread_create((int(*)(void *))function, ptr_arg, ptr_thread_name); + ptr_thread_node->ptr_task->policy = SCHED_RR; + ptr_thread_node->ptr_task->rt_priority = priority; + ptr_thread_node->is_stop = FALSE; + + *ptr_thread_id = (CLX_THREAD_ID_T)ptr_thread_node; + + wake_up_process(ptr_thread_node->ptr_task); + + /* append the thread_node */ + ptr_thread_node->ptr_prev = ptr_thread_head->ptr_prev; + ptr_thread_head->ptr_prev->ptr_next = ptr_thread_node; + ptr_thread_node->ptr_next = ptr_thread_head; + ptr_thread_head->ptr_prev = ptr_thread_node; + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_stopThread( + CLX_THREAD_ID_T *ptr_thread_id) +{ + linux_thread_t *ptr_thread_node = (linux_thread_t *)(*ptr_thread_id); + + ptr_thread_node->is_stop = TRUE; + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_destroyThread( + CLX_THREAD_ID_T *ptr_thread_id) +{ + linux_thread_t *ptr_thread_node = (linux_thread_t *)(*ptr_thread_id); + + kthread_stop(ptr_thread_node->ptr_task); + + /* remove the thread_node */ + ptr_thread_node->ptr_next->ptr_prev = ptr_thread_node->ptr_prev; + ptr_thread_node->ptr_prev->ptr_next = ptr_thread_node->ptr_next; + + osal_free(ptr_thread_node); + *ptr_thread_id = 0; + + return (CLX_E_OK); +} + +void +osal_initRunThread(void) +{ + /* for reboot or shutdown without stopping kthread */ + allow_signal(SIGTERM); +} + +CLX_ERROR_NO_T +osal_isRunThread(void) +{ + linux_thread_t *ptr_thread_node = _osal_thread_head.ptr_next; + + while (1) + { + if (ptr_thread_node == &_osal_thread_head) + { + OSAL_LOG_ERR("Cannot find task %p.\n", current); + break; + } + if (ptr_thread_node->ptr_task == current) + { + break; + } + ptr_thread_node = ptr_thread_node->ptr_next; + } + + if ((TRUE == ptr_thread_node->is_stop) || (signal_pending(current))) + { + return (CLX_E_OTHERS); + } + + return (CLX_E_OK); +} + +void +osal_exitRunThread(void) +{ + while (!kthread_should_stop() && !signal_pending(current)) + { + osal_sleepThread(OSAL_NS_PER_USECOND); + } +} + +/* semaphore */ +CLX_ERROR_NO_T +osal_createSemaphore( + const C8_T *ptr_sema_name, + const UI32_T sema_count, + CLX_SEMAPHORE_ID_T *ptr_semaphore_id) +{ + char dft_name[OSAL_SEMA_NAME_LEN + 1] = OSAL_SEMA_DFT_NAME; + linux_sema_t *ptr_sema = osal_alloc(sizeof(linux_sema_t)); + + /* process name */ + osal_memcpy(ptr_sema->name, (0 == osal_strlen(ptr_sema_name))? + dft_name : ptr_sema_name, OSAL_SEMA_NAME_LEN); + ptr_sema->name[OSAL_SEMA_NAME_LEN] = '\0'; + + /* init */ + sema_init(&ptr_sema->lock, CLX_SEMAPHORE_BINARY); + + *ptr_semaphore_id = (CLX_SEMAPHORE_ID_T)ptr_sema; + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_takeSemaphore( + CLX_SEMAPHORE_ID_T *ptr_semaphore_id, + UI32_T time_out) +{ + linux_sema_t *ptr_sema = (linux_sema_t *)(*ptr_semaphore_id); + + if (in_interrupt()) + { + return (CLX_E_OTHERS); + } + + if (!down_interruptible(&ptr_sema->lock)) + { + return (CLX_E_OK); + } + + return (CLX_E_OTHERS); +} + +CLX_ERROR_NO_T +osal_giveSemaphore( + CLX_SEMAPHORE_ID_T *ptr_semaphore_id) +{ + linux_sema_t *ptr_sema = (linux_sema_t *)(*ptr_semaphore_id); + + up(&ptr_sema->lock); + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_destroySemaphore( + CLX_SEMAPHORE_ID_T *ptr_semaphore_id) +{ + linux_sema_t *ptr_sema = (linux_sema_t *)(*ptr_semaphore_id); + + osal_free(ptr_sema); + *ptr_semaphore_id = 0; + + return (CLX_E_OK); +} + +/* event */ +CLX_ERROR_NO_T +osal_createEvent( + const C8_T *ptr_event_name, + CLX_SEMAPHORE_ID_T *ptr_event_id) +{ + char dft_name[OSAL_EVENT_NAME_LEN + 1] = OSAL_EVENT_DFT_NAME; + linux_event_t *ptr_event = osal_alloc(sizeof(linux_event_t)); + + /* process name */ + osal_memcpy(ptr_event->name, (0 == osal_strlen(ptr_event_name))? + dft_name : ptr_event_name, OSAL_EVENT_NAME_LEN); + ptr_event->name[OSAL_EVENT_NAME_LEN] = '\0'; + + /* init */ + ptr_event->condition = FALSE; + init_waitqueue_head(&ptr_event->wait_que); + + *ptr_event_id = (CLX_SEMAPHORE_ID_T)ptr_event; + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_waitEvent( + CLX_SEMAPHORE_ID_T *ptr_event_id) +{ + linux_event_t *ptr_event = (linux_event_t *)(*ptr_event_id); + + if (!wait_event_interruptible(ptr_event->wait_que, ptr_event->condition)) + { + ptr_event->condition = FALSE; + + return (CLX_E_OK); + } + + return (CLX_E_OTHERS); +} + +CLX_ERROR_NO_T +osal_triggerEvent( + CLX_SEMAPHORE_ID_T *ptr_event_id) +{ + linux_event_t *ptr_event = (linux_event_t *)(*ptr_event_id); + + ptr_event->condition = TRUE; + wake_up_interruptible(&ptr_event->wait_que); + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_destroyEvent( + CLX_SEMAPHORE_ID_T *ptr_event_id) +{ + linux_event_t *ptr_event = (linux_event_t *)(*ptr_event_id); + + osal_free(ptr_event); + *ptr_event_id = 0; + + return (CLX_E_OK); +} + +/* isr_lock */ +CLX_ERROR_NO_T +osal_createIsrLock( + const C8_T *ptr_isrlock_name, + CLX_ISRLOCK_ID_T *ptr_isrlock_id) +{ + char dft_name[OSAL_SPIN_NAME_LEN + 1] = OSAL_SPIN_DFT_NAME; + linux_isrlock_t *ptr_isrlock = osal_alloc(sizeof(linux_isrlock_t)); + + /* process name */ + osal_memcpy(ptr_isrlock->name, (0 == osal_strlen(ptr_isrlock_name))? + dft_name : ptr_isrlock_name, OSAL_SPIN_NAME_LEN); + ptr_isrlock->name[OSAL_SPIN_NAME_LEN] = '\0'; + + /* init */ + spin_lock_init(&ptr_isrlock->spinlock); + + *ptr_isrlock_id = (CLX_ISRLOCK_ID_T)ptr_isrlock; + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_takeIsrLock( + CLX_ISRLOCK_ID_T *ptr_isrlock_id, + CLX_IRQ_FLAGS_T *ptr_irq_flags) +{ + linux_isrlock_t *ptr_isrlock = (linux_isrlock_t *)(*ptr_isrlock_id); + unsigned long flags = 0; + + spin_lock_irqsave(&ptr_isrlock->spinlock, flags); + *ptr_irq_flags = (CLX_IRQ_FLAGS_T)flags; + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_giveIsrLock( + CLX_ISRLOCK_ID_T *ptr_isrlock_id, + CLX_IRQ_FLAGS_T *ptr_irq_flags) +{ + linux_isrlock_t *ptr_isrlock = (linux_isrlock_t *)(*ptr_isrlock_id); + unsigned long flags = 0; + + flags = (unsigned long)(*ptr_irq_flags); + spin_unlock_irqrestore(&ptr_isrlock->spinlock, flags); + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_destroyIsrLock( + CLX_ISRLOCK_ID_T *ptr_isrlock_id) +{ + linux_isrlock_t *ptr_isrlock = (linux_isrlock_t *)(*ptr_isrlock_id); + + osal_free(ptr_isrlock); + *ptr_isrlock_id = 0; + + return (CLX_E_OK); +} + +/* time */ +CLX_ERROR_NO_T +osal_sleepThread( + const UI32_T usecond) +{ + UI32_T tick_usec; /* how many usec per second */ + UI32_T jiffies; + + if (0 != usecond) + { + /* HZ : times/sec, tick = 1/HZ */ + tick_usec = OSAL_TICKS_PER_SEC / HZ; + if (in_interrupt() || (usecond < tick_usec)) + { + return (-1); + } + else + { + DECLARE_WAIT_QUEUE_HEAD(suspend_queue); + + if (usecond > 0xFFFFFFFF - (tick_usec - 1)) + { + jiffies = 0xFFFFFFFF / tick_usec; + } + else + { + jiffies = (usecond + (tick_usec - 1)) / tick_usec; + } + + return wait_event_interruptible_timeout(suspend_queue, 0, jiffies); + } + } + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_getTime( + CLX_TIME_T *ptr_time) +{ + struct timespec64 usec_time; + + ktime_get_real_ts64(&usec_time); + *(CLX_TIME_T *)ptr_time = (usec_time.tv_sec * OSAL_US_PER_SECOND) + usec_time.tv_nsec; + + return (CLX_E_OK); +} + +/* queue */ +CLX_ERROR_NO_T +osal_que_create( + CLX_HUGE_T *ptr_queue_id, + UI32_T capacity) +{ + linux_queue_t *ptr_queue = osal_alloc(sizeof(linux_queue_t)); + + ptr_queue->head = 0; + ptr_queue->tail = 0; + ptr_queue->wr_cnt = 0; + ptr_queue->rd_cnt = 0; + ptr_queue->capacity = capacity; + ptr_queue->ptr_entry = osal_alloc(sizeof(linux_queue_entry_t) * capacity); + memset(ptr_queue->ptr_entry, 0x0, sizeof(linux_queue_entry_t) * capacity); + + *ptr_queue_id = (CLX_HUGE_T)ptr_queue; + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_que_enque( + CLX_HUGE_T *ptr_queue_id, + void *ptr_data) +{ + linux_queue_t *ptr_queue = (linux_queue_t *)(*ptr_queue_id); + + if (ptr_queue->wr_cnt - ptr_queue->rd_cnt >= ptr_queue->capacity) + { + return (CLX_E_OTHERS); + } + + /* save data to the tail */ + ptr_queue->ptr_entry[ptr_queue->tail].ptr_data = ptr_data; + + /* calculate tail and wr_cnt */ + ptr_queue->tail++; + if (ptr_queue->tail >= ptr_queue->capacity) + { + ptr_queue->tail = 0; + } + + ptr_queue->wr_cnt++; + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_que_deque( + CLX_HUGE_T *ptr_queue_id, + void **pptr_data) +{ + linux_queue_t *ptr_queue = (linux_queue_t *)(*ptr_queue_id); + + if (ptr_queue->wr_cnt == ptr_queue->rd_cnt) + { + return (CLX_E_OTHERS); + } + + /* get data from head */ + *pptr_data = ptr_queue->ptr_entry[ptr_queue->head].ptr_data; + ptr_queue->ptr_entry[ptr_queue->head].ptr_data = NULL; + + /* calculate head and rd_cnt */ + ptr_queue->head++; + if (ptr_queue->head >= ptr_queue->capacity) + { + ptr_queue->head = 0; + } + + ptr_queue->rd_cnt++; + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_que_destroy( + CLX_HUGE_T *ptr_queue_id) +{ + linux_queue_t *ptr_queue = (linux_queue_t *)(*ptr_queue_id); + + osal_free(ptr_queue->ptr_entry); + osal_free(ptr_queue); + *ptr_queue_id = 0; + + return (CLX_E_OK); +} + +CLX_ERROR_NO_T +osal_que_getCount( + CLX_HUGE_T *ptr_queue_id, + unsigned int *ptr_count) +{ + linux_queue_t *ptr_queue = (linux_queue_t *)(*ptr_queue_id); + + *ptr_count = ptr_queue->wr_cnt - ptr_queue->rd_cnt; + + return (CLX_E_OK); +} + +/* IO */ +int +osal_io_copyToUser( + void *ptr_usr_buf, + void *ptr_knl_buf, + unsigned int size) +{ + return copy_to_user(ptr_usr_buf, ptr_knl_buf, size); +} + +int +osal_io_copyFromUser( + void *ptr_knl_buf, + void *ptr_usr_buf, + unsigned int size) +{ + return copy_from_user(ptr_knl_buf, ptr_usr_buf, size); +} + +/* dma */ +void * +osal_dma_alloc( + const UI32_T size) +{ + struct device *ptr_dev = &_ptr_ext_pci_dev->dev; + linux_dma_t *ptr_dma_node = NULL; + dma_addr_t phy_addr = 0x0; + + ptr_dma_node = dma_alloc_coherent(ptr_dev, sizeof(linux_dma_t) + size, &phy_addr, GFP_ATOMIC); + ptr_dma_node->size = sizeof(linux_dma_t) + size; + ptr_dma_node->phy_addr = phy_addr; + + return (void *)ptr_dma_node->data; +} + +CLX_ERROR_NO_T +osal_dma_free( + void *ptr_dma_mem) +{ + struct device *ptr_dev = &_ptr_ext_pci_dev->dev; + linux_dma_t *ptr_dma_node = (linux_dma_t *)(ptr_dma_mem - sizeof(linux_dma_t)); + + dma_free_coherent(ptr_dev, ptr_dma_node->size, ptr_dma_node, ptr_dma_node->phy_addr); + + return (CLX_E_OK); +} + +dma_addr_t +osal_dma_convertVirtToPhy( + void *ptr_virt_addr) +{ + return virt_to_phys(ptr_virt_addr); +} + +void * +osal_dma_convertPhyToVirt( + const dma_addr_t phy_addr) +{ + return phys_to_virt(phy_addr); +} + +int +osal_dma_flushCache( + void *ptr_virt_addr, + const unsigned int size) +{ +#if defined(CONFIG_NOT_COHERENT_CACHE) || defined(CONFIG_DMA_NONCOHERENT) +#if defined(dma_cache_wback_inv) + dma_cache_wback_inv((CLX_HUGE_T)ptr_virt_addr, size); +#else + dma_cache_sync(NULL, ptr_virt_addr, size, DMA_TO_DEVICE); +#endif +#endif + return (0); +} + +int +osal_dma_invalidateCache( + void *ptr_virt_addr, + const unsigned int size) +{ +#if defined(CONFIG_NOT_COHERENT_CACHE) || defined(CONFIG_DMA_NONCOHERENT) +#if defined(dma_cache_wback_inv) + dma_cache_wback_inv((CLX_HUGE_T)ptr_virt_addr, size); +#else + dma_cache_sync(NULL, ptr_virt_addr, size, DMA_FROM_DEVICE); +#endif +#endif + return (0); +} + +/* skb */ +struct sk_buff * +osal_skb_alloc( + UI32_T size) +{ + struct sk_buff *ptr_skb = NULL; + + /* + * 1. alloc_skb (len, flag) : GFP_KERNEL + * 2. netdev_alloc_skb (dev, len) : GFP_ATOMIC + * 3. dev_alloc_skb (len) : GFP_ATOMIC + * 4. netdev_alloc_skb_ip_align (dev, len) : GFP_ATOMIC + * + * note: Eth header is 14-bytes, we reservd 2-bytes to alignment Ip header + */ + ptr_skb = dev_alloc_skb(size + NET_IP_ALIGN); + skb_reserve(ptr_skb, NET_IP_ALIGN); + skb_put(ptr_skb, size); + + return (ptr_skb); +} + +void +osal_skb_free( + struct sk_buff *ptr_skb) +{ + /* + * 1. dev_kfree_skb (*skb) : release in process context + * 2. dev_kfree_skb_irq (*skb) : release in interrupt context + * 3. dev_kfree_skb_any (*skb) : release in any context + */ + dev_kfree_skb_any(ptr_skb); +} + +dma_addr_t +osal_skb_mapDma( + struct sk_buff *ptr_skb, + enum dma_data_direction dir) +{ + struct device *ptr_dev = &_ptr_ext_pci_dev->dev; + dma_addr_t phy_addr = 0x0; + + phy_addr = dma_map_single(ptr_dev, ptr_skb->data, ptr_skb->len, dir); + if (dma_mapping_error(ptr_dev, phy_addr)) + { + phy_addr = 0x0; + } + + return (phy_addr); +} + +void +osal_skb_unmapDma( + const dma_addr_t phy_addr, + UI32_T size, + enum dma_data_direction dir) +{ + struct device *ptr_dev = &_ptr_ext_pci_dev->dev; + + dma_unmap_single(ptr_dev, phy_addr, size, dir); +} + +void +osal_skb_send( + struct sk_buff *ptr_skb) +{ + dev_queue_xmit(ptr_skb); +} + +void +osal_skb_recv( + struct sk_buff *ptr_skb) +{ + /* 1. netif_rx() : handle skb in process context + * 2. netif_rx_ni() : handle skb in interrupt context + * 3. netif_receive_skb() : for NAPI + */ + netif_rx(ptr_skb); +} + diff --git a/platform/clounix/clounix-modules/modules/src/clx_netif/netif_perf.c b/platform/clounix/clounix-modules/modules/src/clx_netif/netif_perf.c new file mode 100755 index 000000000000..8a24771b78fb --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/clx_netif/netif_perf.c @@ -0,0 +1,803 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: netif_perf.c + * PURPOSE: + * It provide customer performance test API. + * NOTES: + */ +#include +#include + +#include +#include + +#include + +#if defined (CLX_EN_DAWN) +#include +#endif + +#if defined (CLX_EN_LIGHTNING) +#include +#endif + +/* -------------------------------------------------------------- switch */ +#define PERF_TX_CHANNEL_NUM_MAX (HAL_LIGHTNING_PKT_TX_CHANNEL_LAST) +#define PERF_RX_CHANNEL_NUM_MAX (HAL_LIGHTNING_PKT_RX_CHANNEL_LAST) + +#define NETIF_KNL_DEVICE_IS_LIGHTNING(__dev_id__) (HAL_DEVICE_ID_CL8500 == (__dev_id__ & 0xFF00)) +#define NETIF_KNL_DEVICE_IS_DAWN(__dev_id__) (HAL_DEVICE_ID_CL8300 == (__dev_id__ & 0xFF00)) + +static UI32_T perf_tx_channel_num = HAL_LIGHTNING_PKT_TX_CHANNEL_LAST; +static UI32_T perf_rx_channel_num = HAL_LIGHTNING_PKT_RX_CHANNEL_LAST; + +extern struct pci_dev *_ptr_ext_pci_dev; +static UI16_T dev_id = 0; + +/* -------------------------------------------------------------- common */ +#define PERF_TX_PERF_NUM (1000000) /* max: 4294967 */ +#define PERF_TX_PERF_MESG (50000) +#define PERF_TX_PERF_FAIL (10000) +#define PERF_RX_PERF_NUM (1000000) /* max: 4294967 */ +#define PERF_RX_PERF_MESG (50000) +#define PERF_RX_PERF_FAIL (10000) + +/* -------------------------------------------------------------- callbacks for chip dependency */ +/* Tx */ +typedef CLX_ERROR_NO_T +(*PERF_TX_GET_INTR_T)( + const UI32_T unit, + const UI32_T channel, + UI32_T *ptr_intr_cnt); + +typedef CLX_ERROR_NO_T +(*PERF_TX_GET_NETDEV_T)( + const UI32_T unit, + const UI32_T port, + struct net_device **pptr_net_dev); + +typedef CLX_ERROR_NO_T +(*PERF_TX_PREPARE_GPD_T)( + const UI32_T unit, + const CLX_ADDR_T phy_addr, + const UI32_T len, + const UI32_T port, + void *ptr_sw_gpd); + +typedef CLX_ERROR_NO_T +(*PERF_TX_SEND_GPD_T)( + const UI32_T unit, + const UI32_T channel, + void *ptr_sw_gpd); + +/* Rx */ +typedef CLX_ERROR_NO_T +(*PERF_RX_GET_INTR_T)( + const UI32_T unit, + const UI32_T channel, + UI32_T *ptr_intr_cnt); + +/* -------------------------------------------------------------- structs */ +typedef enum +{ + PERF_DIR_TX = 0, + PERF_DIR_RX, + PERF_DIR_LAST, + +} PERF_DIR_T; + +typedef struct +{ + UI32_T unit; + UI32_T channel; + UI32_T len; + UI32_T num; + UI32_T port; + BOOL_T test_skb; + +} PERF_COOKIE_T; + +typedef struct +{ + /* netif-only */ + PERF_COOKIE_T tx_cookie [PERF_TX_CHANNEL_NUM_MAX]; + + CLX_THREAD_ID_T tx_task [PERF_TX_CHANNEL_NUM_MAX]; + CLX_SEMAPHORE_ID_T start_sync [PERF_TX_CHANNEL_NUM_MAX]; + CLX_SEMAPHORE_ID_T end_sync [PERF_TX_CHANNEL_NUM_MAX]; + UI32_T send_ok [PERF_TX_CHANNEL_NUM_MAX]; + UI32_T send_fail [PERF_TX_CHANNEL_NUM_MAX]; + + /* chip dependent callbacks */ + PERF_TX_GET_INTR_T get_intr_cnt; + PERF_TX_GET_NETDEV_T get_netdev; + PERF_TX_PREPARE_GPD_T prepare_gpd; + PERF_TX_SEND_GPD_T send_gpd; + +} PERF_TX_PERF_CB_T; + +typedef struct +{ + /* netif-only */ + BOOL_T rx_test; + + CLX_SEMAPHORE_ID_T start_sync; + CLX_SEMAPHORE_ID_T end_sync; + UI32_T target_num; + UI32_T target_len; + UI32_T recv_pass; + UI32_T recv_fail; + + /* duplicate packets */ + UI32_T rch_qid_map_lo [PERF_RX_CHANNEL_NUM_MAX]; + UI32_T rch_qid_map_hi [PERF_RX_CHANNEL_NUM_MAX]; + + /* chip dependent callbacks */ + PERF_RX_GET_INTR_T get_intr_cnt; + +} PERF_RX_PERF_CB_T; + +CLX_ERROR_NO_T +hal_perf_pkt_getTxIntrCnt( + const UI32_T unit, + const UI32_T channel, + UI32_T *ptr_intr_cnt) +{ + CLX_ERROR_NO_T ret; + + if (NETIF_KNL_DEVICE_IS_DAWN(dev_id)) + { + ret = hal_dawn_pkt_getTxIntrCnt(unit,channel,ptr_intr_cnt); + } + else if (NETIF_KNL_DEVICE_IS_LIGHTNING(dev_id)) + { + ret = hal_lightning_pkt_getTxIntrCnt(unit,channel,ptr_intr_cnt); + } + else + { + osal_printf("unknown chip family, dev_id=0x%x\n",dev_id); + ret = CLX_E_OTHERS; + } + return ret; +} +CLX_ERROR_NO_T +hal_perf_pkt_getNetDev( + const UI32_T unit, + const UI32_T port, + struct net_device **pptr_net_dev) +{ + CLX_ERROR_NO_T ret; + + if (NETIF_KNL_DEVICE_IS_DAWN(dev_id)) + { + ret = hal_dawn_pkt_getNetDev(unit,port,pptr_net_dev); + } + else if (NETIF_KNL_DEVICE_IS_LIGHTNING(dev_id)) + { + ret = hal_lightning_pkt_getNetDev(unit,port,pptr_net_dev); + } + else + { + osal_printf("unknown chip family, dev_id=0x%x\n",dev_id); + ret = CLX_E_OTHERS; + } + return ret; +} +CLX_ERROR_NO_T +hal_perf_pkt_prepareGpd( + const UI32_T unit, + const CLX_ADDR_T phy_addr, + const UI32_T len, + const UI32_T port, + void *ptr_sw_gpd) +{ + CLX_ERROR_NO_T ret; + + if (NETIF_KNL_DEVICE_IS_DAWN(dev_id)) + { + ret = hal_dawn_pkt_prepareGpd(unit,phy_addr,len,port,(HAL_DAWN_PKT_TX_SW_GPD_T*)ptr_sw_gpd); + } + else if (NETIF_KNL_DEVICE_IS_LIGHTNING(dev_id)) + { + ret = hal_lightning_pkt_prepareGpd(unit,phy_addr,len,port,(HAL_LIGHTNING_PKT_TX_SW_GPD_T*)ptr_sw_gpd); + } + else + { + osal_printf("unknown chip family, dev_id=0x%x\n",dev_id); + ret = CLX_E_OTHERS; + } + return ret; +} +CLX_ERROR_NO_T +hal_perf_pkt_sendGpd( + const UI32_T unit, + const UI32_T channel, + void *ptr_sw_gpd) +{ + CLX_ERROR_NO_T ret; + if (NETIF_KNL_DEVICE_IS_DAWN(dev_id)) + { + ret = hal_dawn_pkt_sendGpd(unit,channel,(HAL_DAWN_PKT_TX_SW_GPD_T*)ptr_sw_gpd); + } + else if (NETIF_KNL_DEVICE_IS_LIGHTNING(dev_id)) + { + ret = hal_lightning_pkt_sendGpd(unit,channel,(HAL_LIGHTNING_PKT_TX_SW_GPD_T*)ptr_sw_gpd); + } + else + { + osal_printf("unknown chip family, dev_id=0x%x\n",dev_id); + ret = CLX_E_OTHERS; + } + return ret; +} +CLX_ERROR_NO_T +hal_perf_pkt_getRxIntrCnt( + const UI32_T unit, + const UI32_T channel, + UI32_T *ptr_intr_cnt) +{ + CLX_ERROR_NO_T ret; + + if (NETIF_KNL_DEVICE_IS_DAWN(dev_id)) + { + ret = hal_dawn_pkt_getRxIntrCnt(unit,channel,ptr_intr_cnt); + } + else if (NETIF_KNL_DEVICE_IS_LIGHTNING(dev_id)) + { + ret = hal_lightning_pkt_getRxIntrCnt(unit,channel,ptr_intr_cnt); + } + else + { + osal_printf("unknown chip family, dev_id=0x%x\n",dev_id); + ret = CLX_E_OTHERS; + } + return ret; +} + +/* -------------------------------------------------------------- statics */ +static PERF_TX_PERF_CB_T _perf_tx_perf_cb = +{ + .get_intr_cnt = hal_perf_pkt_getTxIntrCnt, + .get_netdev = hal_perf_pkt_getNetDev, /* test_skb = TRUE */ + .prepare_gpd = hal_perf_pkt_prepareGpd, /* test_skb = FALSE */ + .send_gpd = hal_perf_pkt_sendGpd, /* test_skb = FALSE */ +}; + +static PERF_RX_PERF_CB_T _perf_rx_perf_cb = +{ + .get_intr_cnt = hal_perf_pkt_getRxIntrCnt, +}; + +/* -------------------------------------------------------------- functions */ +static void +_perf_duplicateRxPacket( + const UI32_T unit, + const UI32_T rx_channel, + const BOOL_T enable) +{ + ; +} + +static void +_perf_showPerf( + PERF_DIR_T dir, + UI32_T channel, + UI32_T len, + UI32_T num, + UI32_T intr, + UI32_T duration) +{ + UI32_T tx_channel = 0; + UI32_T tx_fail = 0; + + if (duration < 1000) + { + osal_printf("***Error***, %d packets cost < 1000 us.\n", num); + return ; + } + + osal_printf("\n"); + + if (PERF_DIR_TX == dir) + { + osal_printf("Tx-perf\n"); + } + else + { + osal_printf("Rx-perf\n"); + } + + osal_printf("------------------------------------\n"); + osal_printf("channel number : %d\n", channel); + osal_printf("packet length (bytes): %d\n", len); + osal_printf("packet number : %d\n", num); + osal_printf("time duration (us) : %d\n", duration); + osal_printf("------------------------------------\n"); + osal_printf("avg. packet rate (pps) : %d\n", (num * 1000) / (duration / 1000)); + osal_printf("avg. throughput (Mbps) : %d\n", ((num / 1000) * len * 8) / (duration / 1000)); + osal_printf("interrupt number : %d\n", intr); + + if (PERF_DIR_TX == dir) + { + for (tx_channel = 0; tx_channel < channel; tx_channel++) + { + tx_fail += _perf_tx_perf_cb.send_fail[tx_channel]; + } + osal_printf("Tx fail : %d\n", tx_fail); + } + + osal_printf("------------------------------------\n"); +} + +static void +_perf_getIntrCnt( + UI32_T unit, + PERF_DIR_T dir, + UI32_T *ptr_intr_cnt) +{ + UI32_T intr_cnt = 0; + UI32_T channel = 0; + + if (PERF_DIR_TX == dir) + { + for (channel = 0; channel < perf_tx_channel_num; channel++) + { + _perf_tx_perf_cb.get_intr_cnt(unit, channel, &intr_cnt); + *ptr_intr_cnt += intr_cnt; + } + } + else + { + for (channel = 0; channel < perf_rx_channel_num; channel++) + { + _perf_rx_perf_cb.get_intr_cnt(unit, channel, &intr_cnt); + *ptr_intr_cnt += intr_cnt; + } + } +} + +static void +_perf_txCallback( + const UI32_T unit, + void *ptr_sw_gpd, + void *ptr_virt_addr) +{ + /* free dma */ + osal_dma_free(ptr_virt_addr); + + /* free gpd */ + osal_free(ptr_sw_gpd); +} + +static void +_perf_txTask( + void *ptr_argv) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + UI32_T unit = ((PERF_COOKIE_T *)ptr_argv)->unit; + UI32_T channel = ((PERF_COOKIE_T *)ptr_argv)->channel; + UI32_T len = ((PERF_COOKIE_T *)ptr_argv)->len; + UI32_T num = ((PERF_COOKIE_T *)ptr_argv)->num; + UI32_T port = ((PERF_COOKIE_T *)ptr_argv)->port; + BOOL_T test_skb = ((PERF_COOKIE_T *)ptr_argv)->test_skb; + + /* test targets */ + void *ptr_sw_gpd = NULL; + struct sk_buff *ptr_skb = NULL; + + /* temp variables */ + UI32_T send_fail = 0; + void *ptr_virt_addr = NULL; + CLX_ADDR_T phy_addr = 0x0; + + osal_initRunThread(); + do + { + rc = osal_waitEvent(&_perf_tx_perf_cb.start_sync[channel]); + if (CLX_E_OK != osal_isRunThread()) + { + break; /* deinit-thread */ + } + + while (_perf_tx_perf_cb.send_ok[channel] < num) + { + if (0 == (_perf_tx_perf_cb.send_ok[channel] % PERF_TX_PERF_MESG)) + { + printk("T"); + } + + if (TRUE == test_skb) + { + ptr_skb = osal_skb_alloc(len); + ptr_skb->len = len; + _perf_tx_perf_cb.get_netdev(unit, port, &ptr_skb->dev); + + /* send skb */ + osal_skb_send(ptr_skb); + } + else + { + /* prepare buf */ + ptr_virt_addr = osal_dma_alloc(len); + phy_addr = osal_dma_convertVirtToPhy(ptr_virt_addr); + + if (NETIF_KNL_DEVICE_IS_DAWN(dev_id)) + { + ptr_sw_gpd = (HAL_DAWN_PKT_TX_SW_GPD_T*)osal_alloc(sizeof(HAL_DAWN_PKT_TX_SW_GPD_T)); + if (NULL == ptr_sw_gpd) + { + osal_printf("***Error***, alloc sw-gpd fail.\n"); + break; + } + osal_memset(ptr_sw_gpd, 0x0, sizeof(HAL_DAWN_PKT_TX_SW_GPD_T)); + + /* trans skb to gpd */ + ((HAL_DAWN_PKT_TX_SW_GPD_T*)ptr_sw_gpd)->callback = (void *)_perf_txCallback; + ((HAL_DAWN_PKT_TX_SW_GPD_T*)ptr_sw_gpd)->ptr_cookie = (void *)ptr_virt_addr; + ((HAL_DAWN_PKT_TX_SW_GPD_T*)ptr_sw_gpd)->gpd_num = 1; + ((HAL_DAWN_PKT_TX_SW_GPD_T*)ptr_sw_gpd)->ptr_next = NULL; + ((HAL_DAWN_PKT_TX_SW_GPD_T*)ptr_sw_gpd)->channel = channel; + + } + else if (NETIF_KNL_DEVICE_IS_LIGHTNING(dev_id)) + { + ptr_sw_gpd = (void*)osal_alloc(sizeof(HAL_LIGHTNING_PKT_TX_SW_GPD_T)); + if (NULL == ptr_sw_gpd) + { + osal_printf("***Error***, alloc sw-gpd fail.\n"); + break; + } + osal_memset(ptr_sw_gpd, 0x0, sizeof(HAL_LIGHTNING_PKT_TX_SW_GPD_T)); + + /* trans skb to gpd */ + ((HAL_LIGHTNING_PKT_TX_SW_GPD_T*)ptr_sw_gpd)->callback = (void *)_perf_txCallback; + ((HAL_LIGHTNING_PKT_TX_SW_GPD_T*)ptr_sw_gpd)->ptr_cookie = (void *)ptr_virt_addr; + ((HAL_LIGHTNING_PKT_TX_SW_GPD_T*)ptr_sw_gpd)->gpd_num = 1; + ((HAL_LIGHTNING_PKT_TX_SW_GPD_T*)ptr_sw_gpd)->ptr_next = NULL; + ((HAL_LIGHTNING_PKT_TX_SW_GPD_T*)ptr_sw_gpd)->channel = channel; + } + + /* prepare gpd */ + rc = _perf_tx_perf_cb.prepare_gpd(unit, phy_addr, len, port, ptr_sw_gpd); + + /* send gpd */ + rc = _perf_tx_perf_cb.send_gpd(unit, channel, ptr_sw_gpd); + if (CLX_E_OK == rc) + { + _perf_tx_perf_cb.send_ok[channel]++; + send_fail = 0; + } + else + { + _perf_tx_perf_cb.send_fail[channel]++; + if (send_fail++ >= PERF_TX_PERF_FAIL) + { + osal_printf("***Error***, Tch-%d send fail over %d packet(s). (rc: %d)\n", + channel, PERF_TX_PERF_FAIL, rc); + break; + } + + _perf_txCallback(unit, ptr_sw_gpd, ptr_virt_addr); + osal_sleepThread(1000); + } + } + } + + osal_triggerEvent(&_perf_tx_perf_cb.end_sync[channel]); + } + while (CLX_E_OK == osal_isRunThread()); + osal_exitRunThread(); +} + +static void +_perf_txDeinit( + const UI32_T unit, + const UI32_T tx_channel) +{ + UI32_T channel = 0; + + for (channel = 0; channel < tx_channel; channel++) + { + /* destroy Tx resources */ + osal_stopThread (&_perf_tx_perf_cb.tx_task [channel]); + osal_triggerEvent(&_perf_tx_perf_cb.start_sync [channel]); + osal_destroyThread(&_perf_tx_perf_cb.tx_task [channel]); + osal_destroyEvent(&_perf_tx_perf_cb.end_sync [channel]); + osal_destroyEvent(&_perf_tx_perf_cb.start_sync [channel]); + } +} + +static void +_perf_txInit( + const UI32_T unit, + const UI32_T tx_channel, + const UI32_T len, + BOOL_T test_skb) +{ + UI32_T channel = 0; + + for (channel = 0; channel < tx_channel; channel++) + { + _perf_tx_perf_cb.send_ok [channel] = 0; + _perf_tx_perf_cb.send_fail[channel] = 0; + + /* create Tx resources */ + osal_createEvent("TX_START", &_perf_tx_perf_cb.start_sync [channel]); + osal_createEvent("TX_END", &_perf_tx_perf_cb.end_sync [channel]); + + _perf_tx_perf_cb.tx_cookie[channel].unit = unit; + _perf_tx_perf_cb.tx_cookie[channel].channel = channel; + _perf_tx_perf_cb.tx_cookie[channel].len = len; + _perf_tx_perf_cb.tx_cookie[channel].num = PERF_TX_PERF_NUM / tx_channel; + _perf_tx_perf_cb.tx_cookie[channel].port = 0; + _perf_tx_perf_cb.tx_cookie[channel].test_skb = test_skb; + + osal_createThread( + "TX_PERF", 64 * 1024, 90, + _perf_txTask, + (void *)&_perf_tx_perf_cb.tx_cookie[channel], + &_perf_tx_perf_cb.tx_task[channel]); + } +} + +static void +_perf_rxDeinit( + const UI32_T unit, + const UI32_T rx_channel) +{ + /* turn-off Rx test */ + _perf_rx_perf_cb.rx_test = FALSE; + + /* destroy Rx resources */ + osal_destroyEvent(&_perf_rx_perf_cb.end_sync); + osal_destroyEvent(&_perf_rx_perf_cb.start_sync); + + /* disable duplicate Rx packets to channels */ + _perf_duplicateRxPacket(unit, rx_channel, FALSE); +} + +static void +_perf_rxInit( + const UI32_T unit, + const UI32_T rx_channel, + const UI32_T len) +{ + /* enable duplicate Rx packets to channels */ + _perf_duplicateRxPacket(unit, rx_channel, TRUE); + + /* create Rx callback resources */ + _perf_rx_perf_cb.target_num = PERF_RX_PERF_NUM; + _perf_rx_perf_cb.target_len = len; + _perf_rx_perf_cb.recv_pass = 0; + + osal_createEvent("RX_START", &_perf_rx_perf_cb.start_sync); + osal_createEvent("RX_END", &_perf_rx_perf_cb.end_sync); + + /* turn-on Rx test */ + _perf_rx_perf_cb.rx_test = TRUE; +} + +/* FUNCTION NAME: perf_rxCallback + * PURPOSE: + * To count the Rx-gpd for Rx-test. + * INPUT: + * len -- To check if the Rx-gpd length equals to test length. + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successful operation. + * NOTES: + * None + */ +CLX_ERROR_NO_T +perf_rxCallback( + const UI32_T len) +{ + /* check length */ + if (len == _perf_rx_perf_cb.target_len) + { + _perf_rx_perf_cb.recv_pass++; + } + else + { + if (_perf_rx_perf_cb.recv_fail++ >= PERF_RX_PERF_FAIL) + { + _perf_rx_perf_cb.recv_fail = 0; + } + } + + /* send signals */ + if (0 == _perf_rx_perf_cb.recv_pass) + { + ; /* do nothing */ + } + else if (1 == _perf_rx_perf_cb.recv_pass) + { + osal_triggerEvent(&_perf_rx_perf_cb.start_sync); + } + else if (_perf_rx_perf_cb.recv_pass == _perf_rx_perf_cb.target_num) + { + osal_triggerEvent(&_perf_rx_perf_cb.end_sync); + } + else if (0 == (_perf_rx_perf_cb.recv_pass % PERF_RX_PERF_MESG)) + { + printk("R"); + } + + return (CLX_E_OK); +} + +/* FUNCTION NAME: perf_rxTest + * PURPOSE: + * To check if Rx-test is going. + * INPUT: + * None + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successful operation. + * NOTES: + * None + */ +CLX_ERROR_NO_T +perf_rxTest(void) +{ + if (FALSE == _perf_rx_perf_cb.rx_test) + { + return (CLX_E_OTHERS); + } + + return (CLX_E_OK); +} + +/* FUNCTION NAME: perf_test + * PURPOSE: + * To do Tx-test or Rx-test. + * INPUT: + * len -- Test length + * tx_channel -- Test Tx channel numbers + * rx_channel -- Test Rx channel numbers + * test_skb -- Test GPD or SKB + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successful operation. + * NOTES: + * None + */ +CLX_ERROR_NO_T +perf_test( + UI32_T len, + UI32_T tx_channel, + UI32_T rx_channel, + BOOL_T test_skb) +{ + CLX_ERROR_NO_T rc = CLX_E_OK; + CLX_TIME_T start_time; + CLX_TIME_T end_time; + UI32_T unit = 0, channel = 0; + UI32_T tx_pkt_cnt = 0, tx_start_intr = 0, tx_end_intr = 0; + UI32_T rx_pkt_cnt = 0, rx_start_intr = 0, rx_end_intr = 0; + + if ((0 == tx_channel) && (0 == rx_channel)) + { + return (CLX_E_NOT_SUPPORT); + } + + pci_read_config_word(_ptr_ext_pci_dev, PCI_DEVICE_ID, &dev_id); + if (NETIF_KNL_DEVICE_IS_DAWN(dev_id)) + { + perf_tx_channel_num = HAL_DAWN_PKT_TX_CHANNEL_LAST; + perf_rx_channel_num = HAL_DAWN_PKT_RX_CHANNEL_LAST; + } + else if (NETIF_KNL_DEVICE_IS_LIGHTNING(dev_id)) + { + perf_tx_channel_num = HAL_LIGHTNING_PKT_TX_CHANNEL_LAST; + perf_rx_channel_num = HAL_LIGHTNING_PKT_RX_CHANNEL_LAST; + } + else + { + osal_printf("unknown chip family, dev_id=0x%x\n",dev_id); + return CLX_E_OTHERS; + } + + /* start test */ + if ((tx_channel > 0) && (rx_channel > 0)) + { + _perf_getIntrCnt(unit, PERF_DIR_TX, &tx_start_intr); + _perf_getIntrCnt(unit, PERF_DIR_RX, &rx_start_intr); + _perf_txInit(unit, tx_channel, len, test_skb); + _perf_rxInit(unit, rx_channel, len); + + /* wait 1st Rx GPD done */ + osal_waitEvent(&_perf_rx_perf_cb.start_sync); + + /* ------------- in-time ------------- */ + osal_getTime(&start_time); + for (channel = 0; channel < tx_channel; channel++) + { + osal_triggerEvent(&_perf_tx_perf_cb.start_sync[channel]); + } + for (channel = 0; channel < tx_channel; channel++) + { + osal_waitEvent(&_perf_tx_perf_cb.end_sync[channel]); + tx_pkt_cnt += _perf_tx_perf_cb.send_ok[channel]; + } + rx_pkt_cnt = _perf_rx_perf_cb.recv_pass; + osal_getTime(&end_time); + /* ------------- in-time ------------- */ + + _perf_txDeinit(unit, tx_channel); + _perf_rxDeinit(unit, rx_channel); + _perf_getIntrCnt(unit, PERF_DIR_TX, &tx_end_intr); + _perf_getIntrCnt(unit, PERF_DIR_RX, &rx_end_intr); + + _perf_showPerf(PERF_DIR_TX, + tx_channel, len, tx_pkt_cnt, tx_end_intr - tx_start_intr, end_time - start_time); + + _perf_showPerf(PERF_DIR_RX, + rx_channel, len, rx_pkt_cnt, rx_end_intr - rx_start_intr, end_time - start_time); + } + else if (tx_channel > 0) + { + _perf_getIntrCnt(unit, PERF_DIR_TX, &tx_start_intr); + _perf_txInit(unit, tx_channel, len, test_skb); + + /* ------------- in-time ------------- */ + osal_getTime(&start_time); + for (channel = 0; channel < tx_channel; channel++) + { + osal_triggerEvent(&_perf_tx_perf_cb.start_sync[channel]); + } + for (channel = 0; channel < tx_channel; channel++) + { + osal_waitEvent(&_perf_tx_perf_cb.end_sync[channel]); + tx_pkt_cnt += _perf_tx_perf_cb.send_ok[channel]; + } + osal_getTime(&end_time); + /* ------------- in-time ------------- */ + + _perf_txDeinit(unit, tx_channel); + _perf_getIntrCnt(unit, PERF_DIR_TX, &tx_end_intr); + + _perf_showPerf(PERF_DIR_TX, + tx_channel, len, tx_pkt_cnt, tx_end_intr - tx_start_intr, end_time - start_time); + } + else if (rx_channel > 0) + { + _perf_getIntrCnt(unit, PERF_DIR_RX, &rx_start_intr); + _perf_rxInit(unit, rx_channel, len); + + /* wait 1st Rx GPD done */ + osal_waitEvent(&_perf_rx_perf_cb.start_sync); + + /* ------------- in-time ------------- */ + osal_getTime(&start_time); + osal_waitEvent(&_perf_rx_perf_cb.end_sync); + osal_getTime(&end_time); + /* ------------- in-time ------------- */ + + _perf_rxDeinit(unit, rx_channel); + _perf_getIntrCnt(unit, PERF_DIR_RX, &rx_end_intr); + + _perf_showPerf(PERF_DIR_RX, + rx_channel, len, PERF_RX_PERF_NUM, rx_end_intr - rx_start_intr, end_time - start_time); + } + + return (rc); +} + diff --git a/platform/clounix/clounix-modules/modules/src/inc/aml/aml.h b/platform/clounix/clounix-modules/modules/src/inc/aml/aml.h new file mode 100644 index 000000000000..14fca2b3a8fb --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/inc/aml/aml.h @@ -0,0 +1,108 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: aml.h + * PURPOSE: + * 1. Provide whole AML resource initialization API. + * 2. Provide configuration access APIs. + * 3. Provide ISR registration and deregistration APIs. + * 4. Provide memory access. + * 5. Provide DMA management APIs. + * 6. Provide address translation APIs. + * NOTES: + */ + +#ifndef AML_H +#define AML_H + + +/* INCLUDE FILE DECLARATIONS + */ +#include +#include + + +/* NAMING CONSTANT DECLARATIONS + */ + +/* #define AML_EN_I2C */ +/* #define AML_EN_CUSTOM_DMA_ADDR */ + +/* MACRO FUNCTION DECLARATIONS + */ + +/* DATA TYPE DECLARATIONS + */ +typedef enum +{ + AML_DEV_TYPE_PCI, + AML_DEV_TYPE_I2C, + AML_DEV_TYPE_SPI, + AML_DEV_TYPE_LAST + +} AML_HW_IF_T; + +typedef CLX_ERROR_NO_T +(*AML_DEV_READ_FUNC_T)( + const UI32_T unit, + const UI32_T addr_offset, + UI32_T *ptr_data, + const UI32_T len); + +typedef CLX_ERROR_NO_T +(*AML_DEV_WRITE_FUNC_T)( + const UI32_T unit, + const UI32_T addr_offset, + const UI32_T *ptr_data, + const UI32_T len); + +typedef CLX_ERROR_NO_T +(*AML_DEV_ISR_FUNC_T)( + void *ptr_data); + +/* To mask the chip interrupt in kernel interrupt routine. */ +typedef struct +{ + UI32_T mask_addr; + UI32_T mask_val; + +} AML_DEV_ISR_DATA_T; + +/* To read or write the HW-intf registers. */ +typedef struct +{ + AML_DEV_READ_FUNC_T read_callback; + AML_DEV_WRITE_FUNC_T write_callback; + +} AML_DEV_ACCESS_T; + +typedef struct +{ + UI32_T vendor; + UI32_T device; + UI32_T revision; + +} AML_DEV_ID_T; + + +typedef struct +{ + AML_HW_IF_T if_type; + AML_DEV_ID_T id; + AML_DEV_ACCESS_T access; + +} AML_DEV_T; +#endif /* #ifndef AML_H */ diff --git a/platform/clounix/clounix-modules/modules/src/inc/clx_error.h b/platform/clounix/clounix-modules/modules/src/inc/clx_error.h new file mode 100644 index 000000000000..21d505ce4d6c --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/inc/clx_error.h @@ -0,0 +1,82 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: clx_error.h + * PURPOSE: + * Define the generic error code on CLX SDK. + * NOTES: + */ + +#ifndef CLX_ERROR_H +#define CLX_ERROR_H + +/* INCLUDE FILE DECLARATIONS + */ +#include + + +/* NAMING CONSTANT DECLARATIONS + */ + +/* MACRO FUNCTION DECLARATIONS + */ + +/* DATA TYPE DECLARATIONS + */ + +typedef enum +{ + CLX_E_OK = 0, /* Ok and no error */ + CLX_E_BAD_PARAMETER, /* Parameter is wrong */ + CLX_E_NO_MEMORY, /* No memory is available */ + CLX_E_TABLE_FULL, /* Table is full */ + CLX_E_ENTRY_NOT_FOUND, /* Entry is not found */ + CLX_E_ENTRY_EXISTS, /* Entry already exists */ + CLX_E_NOT_SUPPORT, /* Feature is not supported */ + CLX_E_ALREADY_INITED, /* Module is reinitialized */ + CLX_E_NOT_INITED, /* Module is not initialized */ + CLX_E_OTHERS, /* Other errors */ + CLX_E_ENTRY_IN_USE, /* Entry is in use */ + CLX_E_TIMEOUT, /* Time out error */ + CLX_E_OP_INVALID, /* Operation is invalid */ + CLX_E_OP_STOPPED, /* Operation is stopped by user callback */ + CLX_E_OP_INCOMPLETE, /* Operation is incomplete */ + CLX_E_LAST +} CLX_ERROR_NO_T; + +/* EXPORTED SUBPROGRAM SPECIFICATIONS + */ +/* FUNCTION NAME: clx_error_getString + * PURPOSE: + * To obtain the error string of the specified error code + * + * INPUT: + * cause -- The specified error code + * OUTPUT: + * None + * RETURN: + * Pointer to the target error string + * + * NOTES: + * + * + */ +C8_T * +clx_error_getString( + const CLX_ERROR_NO_T cause ); + +#endif /* CLX_ERROR_H */ + diff --git a/platform/clounix/clounix-modules/modules/src/inc/clx_types.h b/platform/clounix/clounix-modules/modules/src/inc/clx_types.h new file mode 100644 index 000000000000..a0d483bb3a68 --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/inc/clx_types.h @@ -0,0 +1,329 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: clx_types.h + * PURPOSE: + * Define the commom data type in CLX SDK. + * NOTES: + */ + +#ifndef CLX_TYPES_H +#define CLX_TYPES_H + +/* INCLUDE FILE DECLARATIONS + */ + +#include + +/* NAMING CONSTANT DECLARATIONS + */ + +#define CLX_BIT_OFF 0 +#define CLX_BIT_ON 1 + +#define CLX_INVALID_ID (0xFFFFFFFF) +#define CLX_PORT_INVALID (CLX_INVALID_ID) +#define CLX_SEG_INVALID (CLX_INVALID_ID) + +/* for CPU Rx packet, indicate that the packet + * is not received from remote switch + */ +#define CLX_PATH_INVALID (CLX_INVALID_ID) + +#define CLX_SEMAPHORE_BINARY (1) +#define CLX_SEMAPHORE_SYNC (0) +#define CLX_SEMAPHORE_WAIT_FOREVER (0xFFFFFFFF) + +/* MACRO FUNCTION DECLARATIONS + */ +#if defined(CLX_EN_HOST_32_BIT_BIG_ENDIAN) || defined(CLX_EN_HOST_32_BIT_LITTLE_ENDIAN) +typedef unsigned int CLX_HUGE_T; +#elif defined(CLX_EN_HOST_64_BIT_BIG_ENDIAN) || defined(CLX_EN_HOST_64_BIT_LITTLE_ENDIAN) +typedef unsigned long long int CLX_HUGE_T; +#else +#error "The 32bit and 64bit compatible data type are not defined !!" +#endif + +#if defined(CLX_EN_HOST_64_BIT_BIG_ENDIAN) || defined(CLX_EN_HOST_64_BIT_LITTLE_ENDIAN) || defined(CLX_EN_64BIT_ADDR) +typedef unsigned long long int CLX_ADDR_T; +#else +typedef unsigned int CLX_ADDR_T; +#endif + +#if defined(CLX_EN_HOST_64_BIT_BIG_ENDIAN) || defined(CLX_EN_HOST_64_BIT_LITTLE_ENDIAN) || defined(CLX_EN_64BIT_ADDR) +#define CLX_ADDR_PRINT "0x%llx" +#else +#define CLX_ADDR_PRINT "0x%x" +#endif + +#if defined(CLX_EN_HOST_64_BIT_BIG_ENDIAN) || defined(CLX_EN_HOST_64_BIT_LITTLE_ENDIAN) || defined(CLX_EN_64BIT_ADDR) +#define CLX_ADDR_64_HI(__addr__) ((UI32_T)((__addr__) >> 32)) +#define CLX_ADDR_64_LOW(__addr__) ((UI32_T)((__addr__) & 0xFFFFFFFF)) +#define CLX_ADDR_32_TO_64(__hi32__,__low32__) (((unsigned long long int)(__low32__)) | \ + (((unsigned long long int)(__hi32__)) << 32)) +#else +#define CLX_ADDR_64_HI(__addr__) (0) +#define CLX_ADDR_64_LOW(__addr__) (__addr__) +#define CLX_ADDR_32_TO_64(__hi32__,__low32__) (__low32__) +#endif + +#define CLX_BITMAP_SIZE(bit_num) ((((bit_num) - 1) / 32) + 1) +#define CLX_IPV4_IS_MULTICAST(addr) (0xE0000000 == ((addr) & 0xF0000000)) +#define CLX_IPV6_IS_MULTICAST(addr) (0xFF == (((UI8_T *)(addr))[0])) +#define CLX_MAC_IS_MULTICAST(mac) ((mac[0]) & (0x1)) + +/* DATA TYPE DECLARATIONS + */ +typedef UI8_T CLX_BIT_MASK_8_T; +typedef UI16_T CLX_BIT_MASK_16_T; +typedef UI32_T CLX_BIT_MASK_32_T; +typedef UI64_T CLX_BIT_MASK_64_T; + +typedef UI8_T CLX_MAC_T[6]; +typedef UI32_T CLX_IPV4_T; +typedef UI8_T CLX_IPV6_T[16]; + +typedef UI32_T CLX_TIME_T; + +/* Bridge Domain id data type. */ +typedef UI32_T CLX_BRIDGE_DOMAIN_T; + +/* TRILL nickname type. */ +typedef UI16_T CLX_TRILL_NICKNAME_T; + +typedef union CLX_IP_U +{ + + CLX_IPV4_T ipv4_addr; + CLX_IPV6_T ipv6_addr; + +}CLX_IP_T; + +typedef struct CLX_IP_ADDR_S +{ + CLX_IP_T ip_addr; + CLX_IP_T ip_mask; + BOOL_T ipv4 ; +}CLX_IP_ADDR_T; + +/* Tunnel type*/ +typedef enum +{ + CLX_TUNNEL_TYPE_IPV4INIPV4 = 0, /* RFC2003, IPv4-in-IPv4 tunnel */ + CLX_TUNNEL_TYPE_IPV4INIPV6, /* RFC2003, IPv4-in-IPv6 tunnel */ + CLX_TUNNEL_TYPE_IPV6INIPV4, /* RFC2003, IPv6-in-IPv4 tunnel */ + CLX_TUNNEL_TYPE_IPV6INIPV6, /* RFC2003, IPv6-in-IPv6 tunnel */ + CLX_TUNNEL_TYPE_GREIPV4INIPV4, /* RFC2784/RFC2890,GRE IPv4-in-IPv4 tunnel */ + CLX_TUNNEL_TYPE_GREIPV6INIPV4, /* RFC2784/RFC2890,GRE IPv6-in-IPv4 tunnel */ + CLX_TUNNEL_TYPE_GREIPV4INIPV6, /* RFC2784/RFC2890,GRE IPv4-in-IPv6 tunnel */ + CLX_TUNNEL_TYPE_GREIPV6INIPV6, /* RFC2784/RFC2890,GRE IPv6-in-IPv6 tunnel */ + CLX_TUNNEL_TYPE_GRE_NSH, + CLX_TUNNEL_TYPE_6TO4, /* RFC3056, 6to4 tunnel*/ + CLX_TUNNEL_TYPE_ISATAP, /* RFC5214, ISATAP tunnel */ + CLX_TUNNEL_TYPE_NVGRE_L2, + CLX_TUNNEL_TYPE_NVGRE_V4, + CLX_TUNNEL_TYPE_NVGRE_V6, + CLX_TUNNEL_TYPE_NVGRE_NSH, + CLX_TUNNEL_TYPE_VXLAN, + CLX_TUNNEL_TYPE_GTP_V4, + CLX_TUNNEL_TYPE_GTP_V6, + CLX_TUNNEL_TYPE_MPLSINGRE, + CLX_TUNNEL_TYPE_VXLANGPE_L2, + CLX_TUNNEL_TYPE_VXLANGPE_V4, + CLX_TUNNEL_TYPE_VXLANGPE_V6, + CLX_TUNNEL_TYPE_VXLANGPE_NSH, + CLX_TUNNEL_TYPE_FLEX0_L2, + CLX_TUNNEL_TYPE_FLEX0_V4, + CLX_TUNNEL_TYPE_FLEX0_V6, + CLX_TUNNEL_TYPE_FLEX0_NSH, + CLX_TUNNEL_TYPE_FLEX1_L2, + CLX_TUNNEL_TYPE_FLEX1_V4, + CLX_TUNNEL_TYPE_FLEX1_V6, + CLX_TUNNEL_TYPE_FLEX1_NSH, + CLX_TUNNEL_TYPE_FLEX2_L2, + CLX_TUNNEL_TYPE_FLEX2_V4, + CLX_TUNNEL_TYPE_FLEX2_V6, + CLX_TUNNEL_TYPE_FLEX2_NSH, + CLX_TUNNEL_TYPE_FLEX3_L2, + CLX_TUNNEL_TYPE_FLEX3_V4, + CLX_TUNNEL_TYPE_FLEX3_V6, + CLX_TUNNEL_TYPE_FLEX3_NSH, + CLX_TUNNEL_TYPE_LAST +} CLX_TUNNEL_TYPE_T; + +/* tunnel key */ +typedef struct CLX_TUNNEL_KEY_S +{ + CLX_IP_ADDR_T src_ip; /* key: The outer source IP address used by tunnel encapsulation.*/ + CLX_IP_ADDR_T dst_ip; /* key: The outer destination IP address used by tunnel encapsulation. + * For automatic tunnel, this is not required. If not specified, + * its ip address value must be set to 0, but the IP version + * must be same with src_ip. + */ + CLX_TUNNEL_TYPE_T tunnel_type; /*key: The tunnel type.*/ +}CLX_TUNNEL_KEY_T; + +typedef UI16_T CLX_VLAN_T; +typedef UI32_T CLX_PORT_T; + +typedef enum{ + CLX_PORT_TYPE_NORMAL = 0, + CLX_PORT_TYPE_UNIT_PORT, + CLX_PORT_TYPE_LAG, + CLX_PORT_TYPE_VM_ETAG, + CLX_PORT_TYPE_VM_VNTAG, + CLX_PORT_TYPE_VM_VEPA, + CLX_PORT_TYPE_FCOE, + CLX_PORT_TYPE_IP_TUNNEL, + CLX_PORT_TYPE_TRILL, + CLX_PORT_TYPE_MPLS, + CLX_PORT_TYPE_MPLS_PW, + CLX_PORT_TYPE_CPU_PORT, + CLX_PORT_TYPE_SFC, + CLX_PORT_TYPE_LAST +}CLX_PORT_TYPE_T; + +/*support Green/Yellow/Red color*/ +typedef enum +{ + CLX_COLOR_GREEN = 0, + CLX_COLOR_YELLOW, + CLX_COLOR_RED, + CLX_COLOR_LAST +}CLX_COLOR_T; +typedef enum +{ + CLX_FWD_ACTION_FLOOD = 0, + CLX_FWD_ACTION_NORMAL, + CLX_FWD_ACTION_DROP, + CLX_FWD_ACTION_COPY_TO_CPU, + CLX_FWD_ACTION_REDIRECT_TO_CPU, + CLX_FWD_ACTION_FLOOD_COPY_TO_CPU, + CLX_FWD_ACTION_DROP_COPY_TO_CPU, + CLX_FWD_ACTION_LAST +} CLX_FWD_ACTION_T; + +typedef CLX_HUGE_T CLX_THREAD_ID_T; +typedef CLX_HUGE_T CLX_SEMAPHORE_ID_T; +typedef CLX_HUGE_T CLX_ISRLOCK_ID_T; +typedef CLX_HUGE_T CLX_IRQ_FLAGS_T; + +typedef enum +{ + CLX_DIR_INGRESS = 0, + CLX_DIR_EGRESS, + CLX_DIR_BOTH, + CLX_DIR_LAST +}CLX_DIR_T; + +typedef enum +{ + CLX_VLAN_ACTION_SET, + CLX_VLAN_ACTION_KEEP, + CLX_VLAN_ACTION_REMOVE, + CLX_VLAN_ACTION_LAST +} CLX_VLAN_ACTION_T; + +/* VLAN Precedence */ +/* 000 = SUBNET_PROTOCOL_MAC_PORT + * 001 = SUBNET_MAC_PROTOCOL_PORT + * 010 = PROTOCOL_SUBNET_MAC_PORT + * 011 = PROTOCOL_MAC_SUBNET_PORT + * 100 = MAC_SUBNET_PROTOCOL_PORT + * 101 = MAC_PROTOCOL_SUBNET_PORT + */ +typedef enum +{ + CLX_VLAN_PRECEDENCE_SUBNET_MAC_PROTOCOL_PORT = 1, + CLX_VLAN_PRECEDENCE_MAC_SUBNET_PROTOCOL_PORT = 4, + CLX_VLAN_PRECEDENCE_PORT_ONLY = 7, + CLX_VLAN_PRECEDENCE_FAVOR_TYPE = 8, + CLX_VLAN_PRECEDENCE_FAVOR_ADDR = 9, + CLX_VLAN_PRECEDENCE_LAST +} CLX_VLAN_PRECEDENCE_T; + +/* VLAN Tag Type */ +typedef enum +{ + CLX_VLAN_TAG_NONE = 0, /* UnTag */ + CLX_VLAN_TAG_SINGLE_PRI, /* Single Customer/Service Priority Tag */ + CLX_VLAN_TAG_SINGLE, /* Single Customer/Service Tag */ + CLX_VLAN_TAG_DOUBLE_PRI, /* Double Tag with any VID=0 */ + CLX_VLAN_TAG_DOUBLE, /* Double Tag */ + CLX_VLAN_TAG_LAST +} CLX_VLAN_TAG_T; + +typedef struct CLX_BUM_INFO_S +{ + UI32_T mcast_id; + UI32_T group_label; /* l2 da group label */ + UI32_T vid; /* used when FLAGS_ADD_VID is set */ + +#define CLX_BUM_INFO_FLAGS_MCAST_VALID (1U << 0) +#define CLX_BUM_INFO_FLAGS_TO_CPU (1U << 1) +#define CLX_BUM_INFO_FLAGS_ADD_VID (1U << 2) /* single tag to double tag (i.e) QinQ */ +#define CLX_BUM_INFO_FLAGS_TRILL_ALL_TREE (1U << 3) + UI32_T flags; +} CLX_BUM_INFO_T; + +typedef enum +{ + CLX_PHY_TYPE_INTERNAL = 0x0, + CLX_PHY_TYPE_EXTERNAL, + CLX_PHY_TYPE_LAST +} CLX_PHY_TYPE_T; + +typedef enum +{ + CLX_PHY_DEVICE_ADDR_PMA_PMD = 1, + CLX_PHY_DEVICE_ADDR_WIS = 2, + CLX_PHY_DEVICE_ADDR_PCS = 3, + CLX_PHY_DEVICE_ADDR_PHY_XS = 4, + CLX_PHY_DEVICE_ADDR_DTE_XS = 5, + CLX_PHY_DEVICE_ADDR_TC = 6, + CLX_PHY_DEVICE_ADDR_AN = 7, + CLX_PHY_DEVICE_ADDR_VENDOR_1 = 30, + CLX_PHY_DEVICE_ADDR_VENDOR_2 = 31, + CLX_PHY_DEVICE_ADDR_LAST +} CLX_PHY_DEVICE_ADDR_T; + +typedef enum +{ + CLX_BULK_OP_MODE_ERR_STOP = 0, + CLX_BULK_OP_MODE_ERR_CONTINUE, + CLX_BULK_OP_MODE_LAST +} CLX_BULK_OP_MODE_T; + +typedef struct CLX_RANGE_INFO_S +{ + UI32_T min_id; + UI32_T max_id; + UI32_T max_member_cnt; + +#define CLX_RANGE_INFO_FLAGS_MAX_MEMBER_CNT (1U << 0) + UI32_T flags; +} CLX_RANGE_INFO_T; + +typedef struct CLX_FDL_INFO_S +{ + UI32_T probability /* percentage from 0~100 */; + UI32_T threshold; /* range 0 ~ (2^20)-1 */ +} CLX_FDL_INFO_T; + +/* EXPORTED SUBPROGRAM SPECIFICATIONS + */ + +#endif /* CLX_TYPES_H */ diff --git a/platform/clounix/clounix-modules/modules/src/inc/cmlib/cmlib.h b/platform/clounix/clounix-modules/modules/src/inc/cmlib/cmlib.h new file mode 100644 index 000000000000..fa6af133d7ef --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/inc/cmlib/cmlib.h @@ -0,0 +1,103 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: cmlib.h + * PURPOSE: + * this file is used to provide init common library operations to other users. + * NOTES: + * it contains operations as below: + * 1. init common library + * 2. deinit common library. + * 3. register debug commands for all sub-modules. + * 4. deregister debug commands for all sub-modules. + * 5. provide debug functions for sub-modules. + * + * + * + */ +#ifndef CMLIB_H +#define CMLIB_H + +/* INCLUDE FILE DECLARATIONS + */ + +#include +#include + +/* NAMING CONSTANT DECLARATIONS + */ +#define CMLIB_NAME_MAX_LEN (32) + +/* MACRO FUNCTION DECLARATIONS + */ + + +/* FUNCTION NAME: CMLIB_MULTI_OVERFLOW + * PURPOSE: + * it is used to test if two 32-bit unsigned operand multiply will be overflow + * INPUT: + * ui32a -- operand a + * ui32b -- operand b + * OUTPUT: + * None. + * RETURN: + * 0 -- not overflow + * non 0 -- overflow + * NOTES: + * + */ +#define CMLIB_MULTI_OVERFLOW(ui32a, ui32b) \ + ((ui32a) ? ((~0U/(UI32_T)(ui32a)) < (UI32_T)(ui32b)) : 0) + +/* FUNCTION NAME: CMLIB_ADD_OVERFLOW + * PURPOSE: + * it is used to test if two operands add overflow. + * INPUT: + * ui32a -- operand a + * ui32b -- operand b + * OUTPUT: + * None. + * RETURN: + * 0 -- not overflow + * non 0 -- overflow + * NOTES: + * + */ +#define CMLIB_ADD_OVERFLOW(ui32a, ui32b) \ + ((UI32_T)(ui32a) > ~((UI32_T)(ui32b))) + +/* DATA TYPE DECLARATIONS + */ + +typedef struct CMLIB_DBG_LIST_NODE_S +{ + void *ptr_data; + struct CMLIB_DBG_LIST_NODE_S *ptr_next; +} CMLIB_DBG_LIST_NODE_T; + + +typedef struct +{ + CMLIB_DBG_LIST_NODE_T *ptr_front; + CLX_SEMAPHORE_ID_T sem; +} CMLIB_DBG_HEAD_T; + + +/* EXPORTED SUBPROGRAM SPECIFICATIONS + */ + +#endif /* End of CMLIB_H */ + diff --git a/platform/clounix/clounix-modules/modules/src/inc/cmlib/cmlib_list.h b/platform/clounix/clounix-modules/modules/src/inc/cmlib/cmlib_list.h new file mode 100644 index 000000000000..52d327f8da38 --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/inc/cmlib/cmlib_list.h @@ -0,0 +1,219 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: cmlib_list.h + * PURPOSE: + * this file is used to provide linked list operations to other users. + * linked list include: singly linked list, doubly linked list + * + * NOTES: + * it contains operations as below: + * 1. create a linked list + * 2. destroy a linked list + * 3. insert user data to a linked list + * 4. delete a user data from a linked list + * 5. locate a user data from a linked list + * 6. get next node from a linked list node + * 7. get previous node from a linked list node + * 7. get a linked list nodes count + * + */ +#ifndef CMLIB_LIST_H +#define CMLIB_LIST_H +/* INCLUDE FILE DECLARATIONS + */ +#include +/* NAMING CONSTANT DECLARATIONS + */ + + + +/* linked list type */ +typedef enum +{ + CMLIB_LIST_TYPE_SINGLY = 0, /* singly linked list type */ + CMLIB_LIST_TYPE_DOUBLY, /* doubly linked list type */ + CMLIB_LIST_TYPE_LAST, +} CMLIB_LIST_TYPE_T; + +/* MACRO FUNCTION DECLARATIONS + */ +/* DATA TYPE DECLARATIONS + */ + +/* linked list node */ +typedef struct CMLIB_LIST_NODE_S +{ + void *ptr_data; /* node data */ + struct CMLIB_LIST_NODE_S *ptr_next; /* point to next link node */ + struct CMLIB_LIST_NODE_S *ptr_prev; /* point to previous link node */ +} CMLIB_LIST_NODE_T; + +struct CMLIB_LIST_S; + +/* FUNCTION TYPE NAME: CMLIB_LIST_CMP_FUNC_T + * PURPOSE: + * it is used to find which position the node should be inserted. + * INPUT: + * ptr_node_data -- the checked linked list node data. + * ptr_insert_data -- the data will be inserted. + * OUTPUT: + * None. + * RETURN: + * 0 -- the insert node should be inserted before the node. + * non 0 -- it is not the right position. + * NOTES: + * + */ +typedef I32_T (*CMLIB_LIST_CMP_FUNC_T)( + void *ptr_node_data, + void *ptr_insert_data ); + +/* FUNCTION TYPE NAME: CMLIB_LIST_LOCATE_FUNC_T + * PURPOSE: + * it is used to locate a linked list node. + * INPUT: + * ptr_node_data -- the checked linked list node data. + * ptr_cookie -- the data used to check the linked list node. it is + * one of locate function parameters. + * OUTPUT: + * None. + * RETURN: + * 0 -- locate success + * non 0 -- locate failed, should continue to check. + * NOTES: + * + */ +typedef I32_T (*CMLIB_LIST_LOCATE_FUNC_T)( + void *ptr_node_data, + void *ptr_cookie ); + + +/* FUNCTION TYPE NAME: CMLIB_LIST_DESTROY_FUNC_T + * PURPOSE: + * it is used to release linked list node when destroy a linked list. + * INPUT: + * ptr_node_data -- the linked list node data will be destroyed. + * OUTPUT: + * None. + * RETURN: + * None. + * NOTES: + * + */ +typedef void (*CMLIB_LIST_DESTROY_FUNC_T)( + void *ptr_node_data ); + +typedef CMLIB_LIST_DESTROY_FUNC_T CMLIB_LIST_DELETE_FUNC_T; + +/* linked list operations */ +typedef struct +{ + CLX_ERROR_NO_T (*getNodeData) ( + struct CMLIB_LIST_S *ptr_list, /* the node owner */ + CMLIB_LIST_NODE_T *ptr_node, /* the node will be get the data */ + void **pptr_node_data );/* the data pointer saved in the node */ + + CLX_ERROR_NO_T (*insertByFunc)( /* it used to insert a node by a function */ + struct CMLIB_LIST_S *ptr_list, /* the linked list in which the node is inserted */ + void *ptr_data, /* the inserted data */ + CMLIB_LIST_CMP_FUNC_T insert_callback );/* the insert function decide where the data is + * inserted. + */ + + CLX_ERROR_NO_T (*insertToHead)( /* it used to insert a data to the head */ + struct CMLIB_LIST_S *ptr_list, /* the linked list in which the node is inserted */ + void *ptr_data ); /* the inserted data */ + + CLX_ERROR_NO_T (*insertToTail)( /* it used to insert a data to the tail */ + struct CMLIB_LIST_S *ptr_list, /* the linked list in which the node is inserted */ + void *ptr_data ); /* the inserted data */ + + CLX_ERROR_NO_T (*insertBefore)( /* insert a data before a specified node */ + struct CMLIB_LIST_S *ptr_list, /* the linked list in which the node is inserted */ + CMLIB_LIST_NODE_T *ptr_node, /* the specified node */ + void *ptr_insert_data ); /* the inserted data */ + + CLX_ERROR_NO_T (*insertAfter)( /* insert a data after a specified node */ + struct CMLIB_LIST_S *ptr_list, /* the linked list in which the node is inserted */ + CMLIB_LIST_NODE_T *ptr_node, /* the specified node */ + void *ptr_insert_data ); /* the inserted data */ + + CLX_ERROR_NO_T (*deleteNode)( /* delete a node from a linked list */ + struct CMLIB_LIST_S *ptr_list, /* the linked list from which delete */ + CMLIB_LIST_NODE_T *ptr_delete_node ); /* the node will be deleted */ + + CLX_ERROR_NO_T (*deleteByData)( + struct CMLIB_LIST_S *ptr_list, + void *ptr_data ); + + CLX_ERROR_NO_T (*locateByFunc)( /* locate a node from a linked list */ + struct CMLIB_LIST_S *ptr_list, /* the linked list from which locate */ + void *ptr_cookie, /* the cookie data for locate callback */ + CMLIB_LIST_LOCATE_FUNC_T locate_callback, /* locate calback function */ + CMLIB_LIST_NODE_T **pptr_node ); /* the located node */ + + CLX_ERROR_NO_T (*locateHead)( /* get the head node from a linked list */ + struct CMLIB_LIST_S *ptr_list, /* the linked list from which get */ + CMLIB_LIST_NODE_T **pptr_node ); /* the head node */ + + CLX_ERROR_NO_T (*locateTail)( /* get the tail node from a linked list */ + struct CMLIB_LIST_S *ptr_list, /* the linked list from which get */ + CMLIB_LIST_NODE_T **pptr_node ); /* the tail node */ + + CLX_ERROR_NO_T (*next)( /* get next node of the specified node */ + struct CMLIB_LIST_S *ptr_list, /* the node of this linked list */ + CMLIB_LIST_NODE_T *ptr_node, /* the speicified node */ + CMLIB_LIST_NODE_T **pptr_next_node); /* the next node */ + + CLX_ERROR_NO_T (*prev)( /* get previous node of the specified node */ + struct CMLIB_LIST_S *ptr_list, /* the node of this linked list */ + CMLIB_LIST_NODE_T *ptr_node, /* the speicified node */ + CMLIB_LIST_NODE_T **pptr_prev_node );/* the previous node */ + + CLX_ERROR_NO_T (*getLength)( /* get the node count of a linked list */ + struct CMLIB_LIST_S *ptr_list, /* the linked list */ + UI32_T *ptr_length); /* the linked list, output parameter */ + + CLX_ERROR_NO_T (*destroy)( /* destroy a linked list */ + struct CMLIB_LIST_S *ptr_list, /* the destroyed linked list */ + CMLIB_LIST_DESTROY_FUNC_T destroy_callback ); /* the destroy function for + * releasing linked list node data + */ + CLX_ERROR_NO_T (*deleteAll)( /* destroy all nodes from a linked list*/ + struct CMLIB_LIST_S *ptr_list, /* the linked list*/ + CMLIB_LIST_DELETE_FUNC_T delete_callback); /* the delete function for releasing node data*/ + +} CMLIB_LIST_OPS_T; + + +/* linked list head */ +typedef struct CMLIB_LIST_S +{ + CMLIB_LIST_NODE_T *ptr_head_node; /* linked list head node */ + CMLIB_LIST_NODE_T *ptr_tail_node; /* linked list tail node */ + CMLIB_LIST_TYPE_T type; /* list type */ + UI32_T capacity; /* max count of nodes in list + * size=0: the capacity is unlimited. + * size>0: the capacity is limited. + */ + void *ptr_node_pool; /* node pool */ + UI32_T node_count; /* the count of nodes in the list */ + CMLIB_LIST_OPS_T *ptr_ops; /* linked list operations */ +} CMLIB_LIST_T; + +#endif /* End of CMLIB_LIST_H */ + diff --git a/platform/clounix/clounix-modules/modules/src/inc/hal/common/hal_dev.h b/platform/clounix/clounix-modules/modules/src/inc/hal/common/hal_dev.h new file mode 100644 index 000000000000..2d867f03b427 --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/inc/hal/common/hal_dev.h @@ -0,0 +1,58 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: hal_dev.h + * PURPOSE: + * Provide a list of device IDs. + * + * NOTES: + */ + +#ifndef HAL_DEV_H +#define HAL_DEV_H + +/* INCLUDE FILE DECLARATIONS + */ +/* NAMING CONSTANT DECLARATIONS + */ +#define HAL_CLX_VENDOR_ID (0x0E8D) +#define HAL_CL_VENDOR_ID (0x1D9F) + +#define HAL_DEVICE_ID_CL3257 (0x3257) +#define HAL_DEVICE_ID_CL3258 (0x3258) + +#define HAL_DEVICE_ID_CL8300 (0x8300) /* chip family */ +#define HAL_DEVICE_ID_CL8363 (0x8363) /* 1.08T 1Bin */ +#define HAL_DEVICE_ID_CL8365 (0x8365) /* 1.8T 1Bin */ +#define HAL_DEVICE_ID_CL8366 (0x8366) /* 2.4T 1Bin */ +#define HAL_DEVICE_ID_CL8367 (0x8367) /* 3.2T 1Bin */ +#define HAL_DEVICE_ID_CL8368 (0x8368) /* 3.2T 2Bin */ +#define HAL_DEVICE_ID_CL8369 (0x8369) /* 6.4T 2Bin */ + +#define HAL_DEVICE_ID_CL8500 (0x8500) /* chip family */ +#define HAL_DEVICE_ID_CL8571 (0x8571) /* 3.2T 2Bin */ +#define HAL_DEVICE_ID_CL8573 (0x8573) /* 3.2T 2Bin */ +#define HAL_DEVICE_ID_CL8575 (0x8575) /* 4.0T 4Bin */ +#define HAL_DEVICE_ID_CL8577 (0x8577) /* 6.4T 4Bin */ +#define HAL_DEVICE_ID_CL8578 (0x8578) /* 8.0T 4Bin */ +#define HAL_DEVICE_ID_CL8579 (0x8579) /* 12.8T 4Bin */ + +#define HAL_REVISION_ID_E1 (0x01) +#define HAL_REVISION_ID_E2 (0x02) + +#define HAL_INVALID_DEVICE_ID (0xFFFFFFFF) + +#endif /* #ifndef HAL_DEV_H */ diff --git a/platform/clounix/clounix-modules/modules/src/inc/hal/common/hal_dflt.h b/platform/clounix/clounix-modules/modules/src/inc/hal/common/hal_dflt.h new file mode 100644 index 000000000000..7966fd4f1ce9 --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/inc/hal/common/hal_dflt.h @@ -0,0 +1,412 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: hal_dflt.h + * PURPOSE: + * 1. Provide definition for sdk default + * 2. Provide registration/deregistration + * 3. Provide data structure declaration + * 4. Provide + * + * NOTES: + */ + +#ifndef HAL_DFLT_H +#define HAL_DFLT_H + + +/* INCLUDE FILE DECLARATIONS + */ +#include +#include + + +/* NAMING CONSTANT DECLARATIONS + */ + +/* NAMING CONSTANT DECLARATIONS + ----USER CONFIG (follow CLX_CFG_TYPE_T enum sequence)---- + */ +/* SWC module */ +#if defined(CLX_EN_SDN) +#define HAL_DFLT_CFG_CHIP_MODE (1) + /* chip operating mode. 0: legacy mode; 1:hybrid mode */ +#else +#define HAL_DFLT_CFG_CHIP_MODE (0) + /* chip operating mode. 0: legacy mode; 1:hybrid mode */ +#endif + +/* PORT module related configuration */ +#define HAL_DFLT_CFG_PORT_MAX_SPEED (0) + /* port max speed is not applied */ +#define HAL_DFLT_CFG_PORT_LANE_NUM (0) + /* port lane num is not applied */ +#define HAL_DFLT_CFG_PORT_TX_LANE (0) + /* port tx lane is not applied */ +#define HAL_DFLT_CFG_PORT_RX_LANE (0) + /* port rx lane is not appied */ +#define HAL_DFLT_CFG_PORT_TX_POLARITY_REV (0) + /* port tx polarity rev is not applied */ +#define HAL_DFLT_CFG_PORT_RX_POLARITY_REV (0) + /* port rx polarity rev is not applied */ +#define HAL_DFLT_CFG_PORT_EXT_LANE (0) + /* port ext lane is not applied */ +#define HAL_DFLT_CFG_PORT_VALID (0) + /* port valid is not applied */ + +/* l2 module related configuration */ +#define HAL_DFLT_CFG_L2_THREAD_PRI (50) +#define HAL_DFLT_CFG_L2_THREAD_STACK (64 * 1024) + /* customize L2 thread stack size in bytes */ +#define HAL_DFLT_CFG_L2_ADDR_MODE (0) + /* L2 address operation mode. 0: Polling mode, 1: FIFO mode */ +#define HAL_DFLT_CFG_L2_POLLING_INTERVAL (4000) + /* Times (msec) to poll all L2 FDB entries */ + +/* PKT module related configuration */ +#define HAL_DFLT_CFG_PKT_TX_GPD_NUM (1024) +#define HAL_DFLT_CFG_PKT_RX_GPD_NUM (1024) +#define HAL_DFLT_CFG_PKT_RX_SCHED_MODE (0) + /* 0: RR mode, 1: WRR mode */ +#define HAL_DFLT_CFG_PKT_TX_QUEUE_LEN (HAL_DFLT_CFG_PKT_TX_GPD_NUM * 10) +#define HAL_DFLT_CFG_PKT_RX_QUEUE_LEN (HAL_DFLT_CFG_PKT_RX_GPD_NUM * 10) +#define HAL_DFLT_CFG_PKT_RX_QUEUE_WEIGHT (0) + /* valid while CLX_CFG_TYPE_PKT_RX_SCHED_MODE is 1 + * param0: queue + * param1: NA + * value : weight + */ +#define HAL_DFLT_CFG_PKT_RX_ISR_THREAD_PRI (80) +#define HAL_DFLT_CFG_PKT_RX_ISR_THREAD_STACK (64 * 1024) + /* customize PKT RX ISR thread stack size in bytes */ +#define HAL_DFLT_CFG_PKT_RX_FREE_THREAD_PRI (80) +#define HAL_DFLT_CFG_PKT_RX_FREE_THREAD_STACK (64 * 1024) + /* customize PKT RX free thread stack size in bytes */ +#define HAL_DFLT_CFG_PKT_TX_ISR_THREAD_PRI (80) +#define HAL_DFLT_CFG_PKT_TX_ISR_THREAD_STACK (64 * 1024) + /* customize PKT TX ISR thread stack size in bytes */ +#define HAL_DFLT_CFG_PKT_TX_FREE_THREAD_PRI (80) +#define HAL_DFLT_CFG_PKT_TX_FREE_THREAD_STACK (64 * 1024) + /* customize PKT TX free thread stack size in bytes */ +#define HAL_DFLT_CFG_PKT_ERROR_ISR_THREAD_PRI (80) +#define HAL_DFLT_CFG_PKT_ERROR_ISR_THREAD_STACK (64 * 1024) + /* customize PKT ERROR ISR thread stack size in bytes */ +#define HAL_DFLT_CFG_PKT_DMA_ENHANCE_ENABLE (0) + + +/* STAT module related configuration */ +#define HAL_DFLT_CFG_CNT_THREAD_PRI (0) +#define HAL_DFLT_CFG_CNT_THREAD_STACK (0) + /* customize CNT thread stack size in bytes */ + +/* IFMON module related configuration */ +#define HAL_DFLT_CFG_IFMON_THREAD_PRI (0) +#define HAL_DFLT_CFG_IFMON_THREAD_STACK (0) + /* customize IFMON thread stack size in bytes */ + +/* share memory related configuration */ +#define HAL_DFLT_CFG_SHARE_MEM_SDN_ENTRY_NUM (0) + /* SDN flow table entry number from share memory */ +#define HAL_DFLT_CFG_SHARE_MEM_L3_ENTRY_NUM (0) + /* L3 entry number from share memory */ +#define HAL_DFLT_CFG_SHARE_MEM_L2_ENTRY_NUM (0) + /* L2 entry number from share memory */ + +/* DLB related configuration */ +#define HAL_DFLT_CFG_DLB_MONITOR_MODE (0) + /* DLB monitor mode. 1: async, 0: sync */ +#define HAL_DFLT_CFG_DLB_LAG_MONITOR_THREAD_PRI (0) +#define HAL_DFLT_CFG_DLB_LAG_MONITOR_THREAD_SLEEP_TIME (0) +#define HAL_DFLT_CFG_DLB_L3_MONITOR_THREAD_PRI (0) +#define HAL_DFLT_CFG_DLB_L3_MONITOR_THREAD_SLEEP_TIME (0) +#define HAL_DFLT_CFG_DLB_L3_INTR_THREAD_PRI (0) +#define HAL_DFLT_CFG_DLB_NVO3_MONITOR_THREAD_PRI (0) +#define HAL_DFLT_CFG_DLB_NVO3_MONITOR_THREAD_SLEEP_TIME (0) +#define HAL_DFLT_CFG_DLB_NVO3_INTR_THREAD_PRI (0) + +/* l3 related configuration */ +#define HAL_DFLT_CFG_L3_ECMP_MIN_BLOCK_SIZE (0) +#define HAL_DFLT_CFG_L3_ECMP_BLOCK_SIZE (0) +#define HAL_DFLT_CFG_TCAM_L3_WITH_IPV6_PREFIX_128_REGION_ENTRY_NUM (0) +#define HAL_DFLT_CFG_TCAM_L3_WITH_IPV6_PREFIX_64_REGION_ENTRY_NUM (0) + +/* share memory related configuration */ +#define HAL_DFLT_CFG_HASH_L2_FDB_REGION_ENTRY_NUM (0) +#define HAL_DFLT_CFG_HASH_L2_GROUP_REGION_ENTRY_NUM (0) +#define HAL_DFLT_CFG_HASH_SECURITY_REGION_ENTRY_NUM (0) +#define HAL_DFLT_CFG_HASH_L3_WITH_IPV6_PREFIX_128_REGION_ENTRY_NUM (0) +#define HAL_DFLT_CFG_HASH_L3_WITH_IPV6_PREFIX_64_REGION_ENTRY_NUM (0) +#define HAL_DFLT_CFG_HASH_L3_WITHOUT_PREFIX_REGION_ENTRY_NUM (0) +#define HAL_DFLT_CFG_HASH_L3_RPF_REGION_ENTRY_NUM (0) +#define HAL_DFLT_CFG_HASH_FLOW_REGION_ENTRY_NUM (0) + +#define HAL_DFLT_CFG_PORT_FC_MODE (0) + /* only use to init port TM buffer + * configuration for specific FC mode, + * which not enable/disable FC/PFC + * for the port/pcp. + * param0: port. + * param1: Invalid. + * value : 0, FC disable; + * 1, 802.3x FC; + * 2, PFC. + */ +#define HAL_DFLT_CFG_PORT_PFC_STATE (0) + /* valid while CLX_CFG_TYPE_PORT_TYPE_FC_MODE + * of the port is PFC. + * param0: port. + * param1: pcp. + * value : 0, PFC disable; + * 1, PFC enable. + */ +#define HAL_DFLT_CFG_PORT_PFC_QUEUE_STATE (0) + /* valid while CLX_CFG_TYPE_PORT_TYPE_FC_MODE + * of the port is PFC. + * param0: port. + * param1: queue. + * value : 0, PFC disable; + * 1, PFC enable; + */ +#define HAL_DFLT_CFG_PORT_PFC_MAPPING (0) + /* valid while CLX_CFG_TYPE_PORT_FC_MODE + * of the port is PFC. + * param0: port. + * param1: queue. + * value : PCP bitmap; + * + */ +#define HAL_DFLT_CFG_TRILL_ENABLE (0) + /* TRILL module related configuration */ +#define HAL_DFLT_CFG_USE_UNIT_PORT (0) + /* use UNIT_PORT or native port of CLX_PORT_T + * 1 : UNIT_PORT, 0 : native port + */ +#define HAL_DFLT_CFG_MAC_VLAN_ENABLE (0) + /* use dadicate mac vlan table */ +#define HAL_DFLT_CFG_CPI_PORT_MODE (0) + /* use to init CPI port working mode. + * param0: CPI port number. + * param1: NA. + * value : 0, CPI mode. + * 1, Ether mode. + */ +#define HAL_DFLT_CFG_PHY_ADDR (0) +#define HAL_DFLT_CFG_LED_CFG (0) +#define HAL_DFLT_CFG_USER_BUF_CTRL (0) +#define HAL_DFLT_CFG_FAIR_BUF_CTRL (0) + /* to enable the fairness in flow-control traffic. + * value : 0, disable fairness. + * 1, enable fairness. + */ +#define HAL_DFLT_CFG_HRM_BUF_SIZE (0) + /* to assign the head room size of port speed. + * param0: Port speed. + * 0, 1G (default) + * 1, 10G + * 2, 25G + * 3, 40G + * 4, 50G + * 5, 100G + * value : cell number. + */ +#define HAL_DFLT_CFG_STEERING_TRUNCATE_ENABLE (0) + /* set value 0: Do not truncate steering packets. + * set value 1: steering packets will be trucated to 1 cell and + * the cell size is based on chip. + */ +#define HAL_DFLT_CFG_FABRIC_MODE_ENABLE (0) + /* set value 0: Non-farbic chip mode. (default) + * set value 1: Fabric chip mode. + */ +#define HAL_DFLT_CFG_ACL_TCP_FLAGS_ENCODE_ENABLE (1) + /* set value 0: Do not encode tcp flags at acl entry. + * (Can only match bit 0-6 of tcp flags.) + * set value 1: Encode tcp flags at acl entry. (default) + */ +#define HAL_DFLT_CFG_TCAM_ECC_SCAN_ENABLE (0) + /* set value 0: Disable ECC TCAM scanning. (default) + * set value 1: Enable ECC TCAM scanning. + */ +#define HAL_DFLT_CFG_PORT_BUF_MAX (0) + /* + * Port max buffer threshold and unit is cell count. + * param0: port. + * param1: 0, ingress; + * 1, egress. + * value : 0, disable; + * others, enable max threshold. + */ +#define HAL_DFLT_CFG_INGRESS_DYNAMIC_BUF (0) + /* + * Queue dynamic alpha setting and value will be + * enlarge to multiple of 256. For example, set value + * as 16 to indicate alpha as 1/16. Set value + * as 256 to indicate alpha as 1. + * param0: port. + * param1: queue (0~7: sc). + * value : alpha * 256. + */ +#define HAL_DFLT_CFG_EGRESS_DYNAMIC_BUF (0) + /* + * Queue dynamic alpha setting and value will be + * enlarge to multiple of 256. For example, set value + * as 16 to indicate alpha as 1/16. Set value + * as 256 to indicate alpha as 1. + * param0: port. + * param1: queue (0~7: uc, 8~15: mc). + * value : alpha * 256. + */ + +#define HAL_DFLT_CFG_DCQCN_ENABLE (0) + /* set value 0: Disable DCQCN. (default) + * set value 1: Enable DCQCN. + */ + +#define HAL_DFLT_CFG_QUEUE_GROUP_MAP (0) + /* To map MC egress accounting from qeueu to group. + * param0: queue id. (0~7). + * value : group id. + */ + +#define HAL_DFLT_CFG_PRI_GROUP_MAP (0) + /* To account egress group from egress queue. + * PG setting is availabled when CLX_CFG_TYPE_USER_BUF_CTRL is enabled. + * param0: queue id. (0~47 for PCIE, 0-7 for others). + * param1: 0, Front port UC type. + 1, Front port MC type. + 2, Local CPU type. + 3, Remote CPU type. + 4, PCIE type. + 5, MIRROR type. + * value : group id. + */ + + +#define HAL_DFLT_CFG_MPLS_SR_NUM (0) + /* MPLS Segment Routing + * value: encapsulation number + */ +#define HAL_DFLT_CFG_L2_BYPASS_LAG_PRUNE_GROUP_NUM (0) + /* default value: 0 */ +#define HAL_DFLT_CFG_L3_BYPASS_LAG_PRUNE_GROUP_NUM (0) + /* default value: 0 */ + +/* DTEL related configuration */ +#define HAL_DFLT_CFG_DTEL_PROFILE_NUM (0) + /* DTEL profile number, it share with sFlow */ + +/* l3 related configuration */ +#define HAL_DFLT_CFG_TCAM_L3_SIP_ENABLE (0) + /* default value: 0, to expand TCAM capacity */ +#define HAL_DFLT_CFG_L3_ECMP_FDL_ENABLE (0) + /* default value: 0, to reserve adj for FDL */ + +#define HAL_DFLT_CFG_HASH_L3_IPV4_PREFIX_LENGTH_ENTRY_NUM (0) + /* param0: prefix-length (1~32) */ + /* param1: 1: vrf, 0: global */ + /* value: entry number */ + +#define HAL_DFLT_CFG_HASH_L3_IPV6_PREFIX_LENGTH_ENTRY_NUM (0) + /* param0: prefix-length (1~128) */ + /* param1: 1: vrf, 0: global */ + /* value: entry number */ + +#define HAL_DFLT_CFG_ACL_DROP_REDIRECT_CPU_PKT (0) + /* set value 0: acl drop_cpu action would not drop redirect to cpu pkt. + * (default) + * set value 1: acl drop_cpu action would drop redirect to cpu pkt. + */ +#define HAL_DFLT_CFG_STACKING_CHIP_PORT_NUM (0) + /* In stacking/chassis system, max used port num per device. + * default value: hw default max port + */ +#define HAL_DFLT_CFG_BUF_SNAPSHOT_INTERVAL (0) + +#define HAL_DFLT_CFG_LAG_MC_RESILIENT_ENABLE (0) + /* set value 0: disable (default). + * set value 1: enable. + */ + + +/* NAMING CONSTANT DECLARATIONS + ----DEFAULT PROPERTY ---- + */ +/* ---- VLAN ---- */ +#define HAL_DFLT_VLAN (1) +#define HAL_DFLT_BDID (1) + +/* ---- PORT ---- */ +#define HAL_DFLT_L2_MTU (1536) +#define HAL_DFLT_TPID_1ST (0x8100) +#define HAL_DFLT_TPID_2ND (0x0) + + + + +/* ---- IFMON ---- */ + +/* ---- L2 ---- */ + +/* ---- STP ---- */ +#define HAL_DFLT_STP_ID (0) + +/* ---- LAG ---- */ + +/* ---- MIR ---- */ + +/* ---- L3 ---- */ + +/* ---- L3T ---- */ + +/* ---- QOS ---- */ + +/* ---- METER ---- */ + +/* ---- PKT ---- */ + +/* ---- ACL ---- */ + +/* ---- STAT ---- */ + +/* ---- SEC ---- */ + +/* ---- SFLOW ---- */ + +/* ---- TM ---- */ + +/* ---- VM ---- */ + +/* ---- FCOE ---- */ + +/* ---- NV ---- */ + +/* ---- SWC ---- */ + +/* ---- SDN ---- */ + +/* ---- MPLS ---- */ + +/* ---- TRILL ---- */ + +/* ---- SFC ---- */ + +/* ---- STK ---- */ + + +/* MACRO FUNCTION DECLARATIONS + */ + +#endif diff --git a/platform/clounix/clounix-modules/modules/src/inc/hal_dawn_pkt_knl.h b/platform/clounix/clounix-modules/modules/src/inc/hal_dawn_pkt_knl.h new file mode 100755 index 000000000000..2d8c6ae8d077 --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/inc/hal_dawn_pkt_knl.h @@ -0,0 +1,2195 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: hal_dawn_pkt_knl.h + * PURPOSE: + * To provide Linux kernel for PDMA TX/RX control. + * + * NOTES: + */ + +#ifndef HAL_DAWN_PKT_KNL_H +#define HAL_DAWN_PKT_KNL_H +#include +#include + +/* CP_COMMON */ +#define HAL_DAWN_PKT_CP_COMMON_INT_EN_HI (0x012C0000) +#define HAL_DAWN_PKT_CP_COMMON_INT_LO_HI (0x012C0004) +#define HAL_DAWN_PKT_CP_COMMON_INT_LVL_HI (0x012C0008) +#define HAL_DAWN_PKT_CP_COMMON_INT_LVL_LO (0x012C000C) +#define HAL_DAWN_PKT_CP_COMMON_INT_MASK_SET_HI (0x012C0010) +#define HAL_DAWN_PKT_CP_COMMON_INT_MASK_SET_LO (0x012C0014) +#define HAL_DAWN_PKT_CP_COMMON_INT_MASK_CLR_HI (0x012C0018) +#define HAL_DAWN_PKT_CP_COMMON_INT_MASK_CLR_LO (0x012C001C) +#define HAL_DAWN_PKT_CP_COMMON_INT_MASK_VAL_HI (0x012C0020) +#define HAL_DAWN_PKT_CP_COMMON_INT_MASK_VAL_LO (0x012C0024) +#define HAL_DAWN_PKT_CP_COMMON_INT_STAT_HI (0x012C0028) +#define HAL_DAWN_PKT_CP_COMMON_INT_STAT_LO (0x012C002C) +#define HAL_DAWN_PKT_CP_COMMON_INT_CLR_HI (0x012C0030) +#define HAL_DAWN_PKT_CP_COMMON_INT_CLR_LO (0x012C0034) +#define HAL_DAWN_PKT_CP_COMMON_INT_SET_HI (0x012C0038) +#define HAL_DAWN_PKT_CP_COMMON_INT_SET_LO (0x012C003C) + +/* PDMA */ +#define HAL_DAWN_PKT_PDMA_ERR_INT_STAT (0x013F1000) +#define HAL_DAWN_PKT_PDMA_ERR_INT_CLR (0x013F1004) +#define HAL_DAWN_PKT_PDMA_ERR_INT_EN (0x013F1010) +#define HAL_DAWN_PKT_PDMA_ERR_INT_LVL (0x013F1014) +#define HAL_DAWN_PKT_PDMA_ERR_INT_MASK_SET (0x013F1018) +#define HAL_DAWN_PKT_PDMA_ERR_INT_MASK_CLR (0x013F101C) +#define HAL_DAWN_PKT_PDMA_ERR_INT_MASK_VAL (0x013F1020) +#define HAL_DAWN_PKT_PDMA_ERR_INT_SET (0x013F1024) +#define HAL_DAWN_PKT_PDMA_CREDIT_CFG (0x013F1100) + +/* Rx */ +#define HAL_DAWN_PKT_PDMA_RCH_GPD_RING_START_ADDR_LO (0x013F12E4) +#define HAL_DAWN_PKT_PDMA_RCH_GPD_RING_START_ADDR_HI (0x013F12E8) +#define HAL_DAWN_PKT_PDMA_RCH_GPD_RING_SIZE (0x013F12EC) +#define HAL_DAWN_PKT_PDMA_RCH_CMD (0x013F1300) +#define HAL_DAWN_PKT_PDMA_RCH_INT_EN (0x013F1360) +#define HAL_DAWN_PKT_PDMA_RCH_INT_LVL (0x013F1364) +#define HAL_DAWN_PKT_PDMA_RCH_INT_MASK (0x013F1368) +#define HAL_DAWN_PKT_PDMA_RCH_INT_SET (0x013F1370) +#define HAL_DAWN_PKT_PDMA_RCH_INT_CLR (0x013F1374) +#define HAL_DAWN_PKT_PDMA_RCH_INT_STAT (0x013F1378) + +/* Tx */ +#define HAL_DAWN_PKT_PDMA_TCH_GPD_RING_START_ADDR_LO (0x013F1A00) +#define HAL_DAWN_PKT_PDMA_TCH_GPD_RING_START_ADDR_HI (0x013F1A04) +#define HAL_DAWN_PKT_PDMA_TCH_GPD_RING_SIZE (0x013F1A08) +#define HAL_DAWN_PKT_PDMA_TCH_CMD (0x013F1A20) +#define HAL_DAWN_PKT_PDMA_TCH_INT_EN (0x013F1A40) +#define HAL_DAWN_PKT_PDMA_TCH_INT_LVL (0x013F1A44) +#define HAL_DAWN_PKT_PDMA_TCH_INT_MASK (0x013F1A48) +#define HAL_DAWN_PKT_PDMA_TCH_INT_SET (0x013F1A50) +#define HAL_DAWN_PKT_PDMA_TCH_INT_CLR (0x013F1A54) +#define HAL_DAWN_PKT_PDMA_TCH_INT_STAT (0x013F1A58) + +#define HAL_DAWN_PKT_GET_MMIO(__tbl__) (0x00FFFFFF & (__tbl__)) +#define HAL_DAWN_PKT_GET_PDMA_RCH_REG(__tbl__, __channel__) ((__tbl__) + (0x200 * (__channel__))) +#define HAL_DAWN_PKT_GET_PDMA_TCH_REG(__tbl__, __channel__) ((__tbl__) + (0x100 * (__channel__))) + + +#define HAL_DAWN_PORT_NUM (128) +#define HAL_DAWN_PLANE_NUM (4) +#define HAL_DAWN_PLANE_BITS (2) +#define HAL_DAWN_PLANE_MASK (0x3) + +/* NAMING DECLARATIONS + */ +/* PKT definitions */ +#define HAL_DAWN_PKT_TX_MAX_LEN (9216) +#define HAL_DAWN_PKT_RX_MAX_LEN (9216 + 86) /* EPP tunnel header */ +#define HAL_DAWN_PKT_MIN_LEN (64) /* Ethernet definition */ +#define HAL_DAWN_PKT_TMH_HDR_SZ (20) +#define HAL_DAWN_PKT_PPH_HDR_SZ (20) +#define HAL_DAWN_PKT_CRC_LEN (4) + +/* CH */ +#define HAL_DAWN_PKT_CH_LAST_GPD (0) +#define HAL_DAWN_PKT_CH_MIDDLE_GPD (1) + +/* PRG */ +#define HAL_DAWN_PKT_PRG_PROCESS_GPD (0) /* Normal */ +#define HAL_DAWN_PKT_PRG_SKIP_GPD (1) /* Skip */ + +/* CRCC */ +#define HAL_DAWN_PKT_CRCC_SUM_BY_HW (0) /* calculated by HW */ +#define HAL_DAWN_PKT_CRCC_SUM_BY_SW (1) /* calculated by SW */ + +/* IOC */ +#define HAL_DAWN_PKT_IOC_NO_INTR (0) /* trigger interrupt each GPD */ +#define HAL_DAWN_PKT_IOC_HAS_INTR (1) /* trigger interrupt when ch=0, default setting */ + +/* HWO */ +#define HAL_DAWN_PKT_HWO_SW_OWN (0) +#define HAL_DAWN_PKT_HWO_HW_OWN (1) + +/* ECC */ +#define HAL_DAWN_PKT_ECC_ERROR_OCCUR (1) + +/* CPU, CPI queue number */ +#define HAL_DAWN_PKT_CPU_QUE_NUM (48) +#define HAL_DAWN_PKT_CPI_QUE_NUM (8) + +/* PDMA Definitions */ +#define HAL_DAWN_PKT_PDMA_MAX_GPD_PER_PKT (10) /* <= 256 */ +#define HAL_DAWN_PKT_PDMA_TX_INTR_TIMEOUT (10 * 1000) /* us */ +#define HAL_DAWN_PKT_PDMA_TX_POLL_MAX_LOOP (10 * 1000) /* int */ + +/* Mode */ +#define HAL_DAWN_PKT_TX_WAIT_MODE (HAL_DAWN_PKT_TX_WAIT_ASYNC) + +/* TX Queue */ +#define HAL_DAWN_PKT_TX_TASK_MAX_LOOP (HAL_DFLT_CFG_PKT_TX_QUEUE_LEN) + +/* RX Queue */ +#define HAL_DAWN_PKT_RX_QUEUE_NUM (HAL_DAWN_PKT_RX_CHANNEL_LAST) +#define HAL_DAWN_PKT_RX_TASK_MAX_LOOP (HAL_DFLT_CFG_PKT_TX_QUEUE_LEN) + +/* MACRO FUNCTION DECLARATIONS + */ +/*---------------------------------------------------------------------------*/ +/* [CL8360] Alignment to 64-bytes */ +#if defined(CLX_EN_HOST_64_BIT_BIG_ENDIAN) || defined(CLX_EN_HOST_64_BIT_LITTLE_ENDIAN) +#define HAL_DAWN_PKT_PDMA_ALIGN_ADDR(pdma_addr, align_sz) (((pdma_addr) + (align_sz)) & 0xFFFFFFFFFFFFFFC0) +#else +#define HAL_DAWN_PKT_PDMA_ALIGN_ADDR(pdma_addr, align_sz) (((pdma_addr) + (align_sz)) & 0xFFFFFFC0) +#endif +/*---------------------------------------------------------------------------*/ +#if defined(CLX_EN_BIG_ENDIAN) +#define HAL_DAWN_PKT_ENDIAN_SWAP32(val) (val) +#else +#define HAL_DAWN_PKT_ENDIAN_SWAP32(val) CMLIB_UTIL_ENDIAN_SWAP32(val) +#endif +/*---------------------------------------------------------------------------*/ +#define HAL_DAWN_PKT_GET_BIT(flags, bit) ((((flags) & (bit)) > 0)? 1 : 0) +/*---------------------------------------------------------------------------*/ +#define HAL_DAWN_PKT_SET_BITMAP(bitmap, mask_bitmap) (bitmap = ((bitmap) | (mask_bitmap))) +#define HAL_DAWN_PKT_CLR_BITMAP(bitmap, mask_bitmap) (bitmap = ((bitmap) & (~(mask_bitmap)))) +/*---------------------------------------------------------------------------*/ +#define HAL_DAWN_PKT_GET_TX_INTR_TYPE(channel) (HAL_INTR_TX_CH0 + channel) +#define HAL_DAWN_PKT_GET_RX_INTR_TYPE(channel) (HAL_INTR_RX_CH0 + channel) + +/* DATA TYPE DECLARATIONS + */ +typedef enum +{ + HAL_DAWN_PKT_TX_WAIT_ASYNC = 0, + HAL_DAWN_PKT_TX_WAIT_SYNC_INTR = 1, + HAL_DAWN_PKT_TX_WAIT_SYNC_POLL = 2 + +} HAL_DAWN_PKT_TX_WAIT_T; + +typedef enum +{ + HAL_DAWN_PKT_RX_SCHED_RR = 0, + HAL_DAWN_PKT_RX_SCHED_WRR = 1 + +} HAL_DAWN_PKT_RX_SCHED_T; + +/* GPD and Packet Strucutre Definition */ +#if defined(CLX_EN_BIG_ENDIAN) + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T typ : 2; + UI32_T tc : 4; + UI32_T color : 2; + UI32_T srv : 3; + UI32_T trig : 1; + UI32_T igr_phy_port :12; + UI32_T hsh_val_w0 : 8; + /* CLX DWORD 1 */ + UI32_T hsh_val_w1 : 2; + UI32_T dst_idx :15; + UI32_T src_idx :15; + /* CLX DWORD 2 */ + UI32_T intf_fdid :14; + UI32_T nvo3_mgid_is_transit : 1; + UI32_T skip_epp : 1; + UI32_T steer_applied : 1; + UI32_T nvo3_ip_tnl_decap_prop_ttl : 1; + UI32_T nvo3_mpls_uhp_prop_ttl : 1; + UI32_T ecn : 2; + UI32_T store_and_forward : 1; + UI32_T lag_epoch : 1; + UI32_T src_supp_tag : 5; + UI32_T one_arm_rte_srv_fdid : 1; + UI32_T fab_one_arm_rte : 1; + UI32_T skip_ipp : 1; + UI32_T igr_fab_port_grp : 1; + /* CLX DWORD 3 */ + UI32_T : 2; + UI32_T nvo3_mgid :15; + UI32_T nvo3_intf :14; + UI32_T nvo3_src_supp_tag_w0 : 1; + /* CLX DWORD 4 */ + UI32_T nvo3_src_supp_tag_w1 : 4; + UI32_T mir_bmap : 8; + UI32_T cp_to_cpu_code : 4; + UI32_T cp_to_cpu_bmap :16; +} HAL_DAWN_PKT_ITMH_ETH_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T typ : 2; + UI32_T tc : 4; + UI32_T color : 2; + UI32_T srv : 3; + UI32_T trig : 1; + UI32_T igr_phy_port :12; + UI32_T hsh_val_w0 : 8; + /* CLX DWORD 1 */ + UI32_T hsh_val_w1 : 2; + UI32_T dst_idx :15; + UI32_T src_idx :15; + /* CLX DWORD 2 */ + UI32_T intf_fdid :14; + UI32_T nvo3_mgid_is_transit : 1; + UI32_T skip_epp : 1; + UI32_T steer_applied : 1; + UI32_T nvo3_ip_tnl_decap_prop_ttl : 1; + UI32_T nvo3_mpls_uhp_prop_ttl : 1; + UI32_T ecn : 2; + UI32_T store_and_forward : 1; + UI32_T lag_epoch : 1; + UI32_T src_supp_tag : 5; + UI32_T one_arm_rte_srv_fdid : 1; + UI32_T fab_one_arm_rte : 1; + UI32_T : 2; + /* CLX DWORD 3 */ + UI32_T :32; + /* CLX DWORD 4 */ + UI32_T :20; + UI32_T had_uturn : 1; + UI32_T : 2; + UI32_T excpt_code : 8; + UI32_T exp_dscp_mrkd : 1; +} HAL_DAWN_PKT_ETMH_FAB_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T typ : 2; + UI32_T tc : 4; + UI32_T color : 2; + UI32_T srv : 3; + UI32_T trig : 1; + UI32_T igr_phy_port :12; + UI32_T hsh_val_w0 : 8; + /* CLX DWORD 1 */ + UI32_T hsh_val_w1 : 2; + UI32_T dst_idx :15; + UI32_T src_idx :15; + /* CLX DWORD 2 */ + UI32_T intf_fdid :14; + UI32_T nvo3_mgid_is_transit : 1; + UI32_T skip_epp : 1; + UI32_T steer_applied : 1; + UI32_T nvo3_ip_tnl_decap_prop_ttl : 1; + UI32_T nvo3_mpls_uhp_prop_ttl : 1; + UI32_T ecn : 2; + UI32_T igr_fab_port_grp : 1; + UI32_T redir : 1; + UI32_T excpt_code_mir_bmap : 8; + UI32_T cp_to_cpu_bmap_w0 : 1; + /* CLX DWORD 3 */ + UI32_T cp_to_cpu_bmap_w1 : 7; + UI32_T egr_phy_port :12; + UI32_T src_supp_pnd : 1; + UI32_T mc_vid_ctl : 3; + UI32_T mc_vid_1st_w0 : 9; + /* CLX DWORD 4 */ + UI32_T mc_vid_1st_w1 : 3; + UI32_T mc_vid_2nd :12; + UI32_T mc_decr_ttl : 1; + UI32_T mc_is_routed : 1; + UI32_T mc_mel_vld : 1; + UI32_T mc_cp_idx :13; + UI32_T exp_dscp_mrkd : 1; +} HAL_DAWN_PKT_ETMH_ETH_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T decap_act : 3; + UI32_T igr_l2_vid_num : 2; + UI32_T nvo3_encap_idx :14; + UI32_T mpls_pw_cw_vld : 1; + UI32_T hit_idx_w0 :12; + /* CLX DWORD 1 */ + UI32_T hit_idx_w1 : 7; + UI32_T nvo3_adj_idx : 8; + UI32_T seg_vmid_w0 :17; + /* CLX DWORD 2 */ + UI32_T seg_vmid_w1 : 7; + UI32_T : 1; + UI32_T l2_sa_lrn_en_hw_cvs : 1; + UI32_T l2_sa_lrn_en_hw : 1; + UI32_T vid_ctl : 3; + UI32_T vid_1st :12; + UI32_T vid_2nd_w0 : 7; + /* CLX DWORD 3 */ + UI32_T vid_2nd_w1 : 5; + UI32_T flw_lbl :10; + UI32_T rewr_idx_ctl : 2; + UI32_T rewr_idx_0 :13; + UI32_T rewr_idx_1_w0 : 2; + /* CLX DWORD 4 */ + UI32_T rewr_idx_1_w1 :11; + UI32_T mrk_pcp_dei_en : 1; + UI32_T mrk_pcp_val : 3; + UI32_T mrk_dei_val : 1; + UI32_T ts :16; +} HAL_DAWN_PKT_PPH_L2_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T decap_act : 3; + UI32_T igr_l2_vid_num : 2; + UI32_T nvo3_encap_idx :14; + UI32_T mpls_pw_cw_vld : 1; + UI32_T hit_idx_w0 :12; + /* CLX DWORD 1 */ + UI32_T hit_idx_w1 : 7; + UI32_T nvo3_adj_idx : 8; + UI32_T seg_vmid_w0 :17; + /* CLX DWORD 2 */ + UI32_T seg_vmid_w1 : 7; + UI32_T : 1; + UI32_T rpf_pnd : 1; + UI32_T adj_idx :18; + UI32_T is_mc : 1; + UI32_T decr_ttl : 1; + UI32_T decap_prop_ttl : 1; + UI32_T mrk_dscp_en : 1; + UI32_T mrk_dscp_val_w0 : 1; + /* CLX DWORD 3 */ + UI32_T mrk_dscp_val_w1 : 5; + UI32_T flw_lbl :10; + UI32_T rewr_idx_ctl : 2; + UI32_T rewr_idx_0 :13; + UI32_T rewr_idx_1_w0 : 2; + /* CLX DWORD 4 */ + UI32_T rewr_idx_1_w1 :11; + UI32_T mrk_pcp_dei_en : 1; + UI32_T mrk_pcp_val : 3; + UI32_T mrk_dei_val : 1; + UI32_T ts :16; +} HAL_DAWN_PKT_PPH_L3UC_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T decap_act : 3; + UI32_T igr_l2_vid_num : 2; + UI32_T nvo3_encap_idx :14; + UI32_T mpls_pw_cw_vld : 1; + UI32_T hit_idx_w0 :12; + /* CLX DWORD 1 */ + UI32_T hit_idx_w1 : 7; + UI32_T nvo3_adj_idx : 8; + UI32_T vid_1st :12; + UI32_T vid_2nd_w0 : 5; + /* CLX DWORD 2 */ + UI32_T vid_2nd_w1 : 7; + UI32_T :15; + UI32_T l2_sa_lrn_en_hw_cvs : 1; + UI32_T l2_sa_lrn_en_hw : 1; + UI32_T vid_ctl : 3; + UI32_T is_mc : 1; + UI32_T : 1; + UI32_T decap_prop_ttl : 1; + UI32_T mrk_dscp_en : 1; + UI32_T mrk_dscp_val_w0 : 1; + /* CLX DWORD 3 */ + UI32_T mrk_dscp_val_w1 : 5; + UI32_T flw_lbl :10; + UI32_T rewr_idx_ctl : 2; + UI32_T rewr_idx_0 :13; + UI32_T rewr_idx_1_w0 : 2; + /* CLX DWORD 4 */ + UI32_T rewr_idx_1_w1 :11; + UI32_T mrk_pcp_dei_en : 1; + UI32_T mrk_pcp_val : 3; + UI32_T mrk_dei_val : 1; + UI32_T ts :16; +} HAL_DAWN_PKT_PPH_L3MC_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T decap_act : 3; + UI32_T igr_l2_vid_num : 2; + UI32_T nvo3_encap_idx :14; + UI32_T : 1; + UI32_T hit_idx_w0 :12; + /* CLX DWORD 1 */ + UI32_T hit_idx_w1 : 7; + UI32_T nvo3_adj_idx : 8; + UI32_T seg_vmid_w0 :17; + /* CLX DWORD 2 */ + UI32_T seg_vmid_w1 : 7; + UI32_T : 2; + UI32_T adj_idx :18; + UI32_T : 1; + UI32_T decr_ttl : 1; + UI32_T decap_prop_ttl : 1; + UI32_T mrk_exp_en : 1; + UI32_T : 1; + /* CLX DWORD 3 */ + UI32_T : 2; + UI32_T mrk_exp_val : 3; + UI32_T php_pop_keep_inner_qos : 1; + UI32_T :26; + /* CLX DWORD 4 */ + UI32_T :11; + UI32_T mrk_pcp_dei_en : 1; + UI32_T mrk_pcp_val : 3; + UI32_T mrk_dei_val : 1; + UI32_T ts :16; +} HAL_DAWN_PKT_PPH_L25_T; + +#elif defined(CLX_EN_LITTLE_ENDIAN) + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T hsh_val_w0 : 8; + UI32_T igr_phy_port :12; + UI32_T trig : 1; + UI32_T srv : 3; + UI32_T color : 2; + UI32_T tc : 4; + UI32_T typ : 2; + /* CLX DWORD 1 */ + UI32_T src_idx :15; + UI32_T dst_idx :15; + UI32_T hsh_val_w1 : 2; + /* CLX DWORD 2 */ + UI32_T igr_fab_port_grp : 1; + UI32_T skip_ipp : 1; + UI32_T fab_one_arm_rte : 1; + UI32_T one_arm_rte_srv_fdid : 1; + UI32_T src_supp_tag : 5; + UI32_T lag_epoch : 1; + UI32_T store_and_forward : 1; + UI32_T ecn : 2; + UI32_T nvo3_mpls_uhp_prop_ttl : 1; + UI32_T nvo3_ip_tnl_decap_prop_ttl : 1; + UI32_T steer_applied : 1; + UI32_T skip_epp : 1; + UI32_T nvo3_mgid_is_transit : 1; + UI32_T intf_fdid :14; + /* CLX DWORD 3 */ + UI32_T nvo3_src_supp_tag_w0 : 1; + UI32_T nvo3_intf :14; + UI32_T nvo3_mgid :15; + UI32_T : 2; + /* CLX DWORD 4 */ + UI32_T cp_to_cpu_bmap :16; + UI32_T cp_to_cpu_code : 4; + UI32_T mir_bmap : 8; + UI32_T nvo3_src_supp_tag_w1 : 4; +} HAL_DAWN_PKT_ITMH_ETH_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T hsh_val_w0 : 8; + UI32_T igr_phy_port :12; + UI32_T trig : 1; + UI32_T srv : 3; + UI32_T color : 2; + UI32_T tc : 4; + UI32_T typ : 2; + /* CLX DWORD 1 */ + UI32_T src_idx :15; + UI32_T dst_idx :15; + UI32_T hsh_val_w1 : 2; + /* CLX DWORD 2 */ + UI32_T : 2; + UI32_T fab_one_arm_rte : 1; + UI32_T one_arm_rte_srv_fdid : 1; + UI32_T src_supp_tag : 5; + UI32_T lag_epoch : 1; + UI32_T store_and_forward : 1; + UI32_T ecn : 2; + UI32_T nvo3_mpls_uhp_prop_ttl : 1; + UI32_T nvo3_ip_tnl_decap_prop_ttl : 1; + UI32_T steer_applied : 1; + UI32_T skip_epp : 1; + UI32_T nvo3_mgid_is_transit : 1; + UI32_T intf_fdid :14; + /* CLX DWORD 3 */ + UI32_T :32; + /* CLX DWORD 4 */ + UI32_T exp_dscp_mrkd : 1; + UI32_T excpt_code : 8; + UI32_T : 2; + UI32_T had_uturn : 1; + UI32_T :20; +} HAL_DAWN_PKT_ETMH_FAB_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T hsh_val_w0 : 8; + UI32_T igr_phy_port :12; + UI32_T trig : 1; + UI32_T srv : 3; + UI32_T color : 2; + UI32_T tc : 4; + UI32_T typ : 2; + /* CLX DWORD 1 */ + UI32_T src_idx :15; + UI32_T dst_idx :15; + UI32_T hsh_val_w1 : 2; + /* CLX DWORD 2 */ + UI32_T cp_to_cpu_bmap_w0 : 1; + UI32_T excpt_code_mir_bmap : 8; + UI32_T redir : 1; + UI32_T igr_fab_port_grp : 1; + UI32_T ecn : 2; + UI32_T nvo3_mpls_uhp_prop_ttl : 1; + UI32_T nvo3_ip_tnl_decap_prop_ttl : 1; + UI32_T steer_applied : 1; + UI32_T skip_epp : 1; + UI32_T nvo3_mgid_is_transit : 1; + UI32_T intf_fdid :14; + /* CLX DWORD 3 */ + UI32_T mc_vid_1st_w0 : 9; + UI32_T mc_vid_ctl : 3; + UI32_T src_supp_pnd : 1; + UI32_T egr_phy_port :12; + UI32_T cp_to_cpu_bmap_w1 : 7; + /* CLX DWORD 4 */ + UI32_T exp_dscp_mrkd : 1; + UI32_T mc_cp_idx :13; + UI32_T mc_mel_vld : 1; + UI32_T mc_is_routed : 1; + UI32_T mc_decr_ttl : 1; + UI32_T mc_vid_2nd :12; + UI32_T mc_vid_1st_w1 : 3; +} HAL_DAWN_PKT_ETMH_ETH_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T hit_idx_w0 :12; + UI32_T mpls_pw_cw_vld : 1; + UI32_T nvo3_encap_idx :14; + UI32_T igr_l2_vid_num : 2; + UI32_T decap_act : 3; + /* CLX DWORD 1 */ + UI32_T seg_vmid_w0 :17; + UI32_T nvo3_adj_idx : 8; + UI32_T hit_idx_w1 : 7; + /* CLX DWORD 2 */ + UI32_T vid_2nd_w0 : 7; + UI32_T vid_1st :12; + UI32_T vid_ctl : 3; + UI32_T l2_sa_lrn_en_hw : 1; + UI32_T l2_sa_lrn_en_hw_cvs : 1; + UI32_T : 1; + UI32_T seg_vmid_w1 : 7; + /* CLX DWORD 3 */ + UI32_T rewr_idx_1_w0 : 2; + UI32_T rewr_idx_0 :13; + UI32_T rewr_idx_ctl : 2; + UI32_T flw_lbl :10; + UI32_T vid_2nd_w1 : 5; + /* CLX DWORD 4 */ + UI32_T ts :16; + UI32_T mrk_dei_val : 1; + UI32_T mrk_pcp_val : 3; + UI32_T mrk_pcp_dei_en : 1; + UI32_T rewr_idx_1_w1 :11; +} HAL_DAWN_PKT_PPH_L2_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T hit_idx_w0 :12; + UI32_T mpls_pw_cw_vld : 1; + UI32_T nvo3_encap_idx :14; + UI32_T igr_l2_vid_num : 2; + UI32_T decap_act : 3; + /* CLX DWORD 1 */ + UI32_T seg_vmid_w0 :17; + UI32_T nvo3_adj_idx : 8; + UI32_T hit_idx_w1 : 7; + /* CLX DWORD 2 */ + UI32_T mrk_dscp_val_w0 : 1; + UI32_T mrk_dscp_en : 1; + UI32_T decap_prop_ttl : 1; + UI32_T decr_ttl : 1; + UI32_T is_mc : 1; + UI32_T adj_idx :18; + UI32_T rpf_pnd : 1; + UI32_T : 1; + UI32_T seg_vmid_w1 : 7; + /* CLX DWORD 3 */ + UI32_T rewr_idx_1_w0 : 2; + UI32_T rewr_idx_0 :13; + UI32_T rewr_idx_ctl : 2; + UI32_T flw_lbl :10; + UI32_T mrk_dscp_val_w1 : 5; + /* CLX DWORD 4 */ + UI32_T ts :16; + UI32_T mrk_dei_val : 1; + UI32_T mrk_pcp_val : 3; + UI32_T mrk_pcp_dei_en : 1; + UI32_T rewr_idx_1_w1 :11; +} HAL_DAWN_PKT_PPH_L3UC_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T hit_idx_w0 :12; + UI32_T mpls_pw_cw_vld : 1; + UI32_T nvo3_encap_idx :14; + UI32_T igr_l2_vid_num : 2; + UI32_T decap_act : 3; + /* CLX DWORD 1 */ + UI32_T vid_2nd_w0 : 5; + UI32_T vid_1st :12; + UI32_T nvo3_adj_idx : 8; + UI32_T hit_idx_w1 : 7; + /* CLX DWORD 2 */ + UI32_T mrk_dscp_val_w0 : 1; + UI32_T mrk_dscp_en : 1; + UI32_T decap_prop_ttl : 1; + UI32_T : 1; + UI32_T is_mc : 1; + UI32_T vid_ctl : 3; + UI32_T l2_sa_lrn_en_hw : 1; + UI32_T l2_sa_lrn_en_hw_cvs : 1; + UI32_T :15; + UI32_T vid_2nd_w1 : 7; + /* CLX DWORD 3 */ + UI32_T rewr_idx_1_w0 : 2; + UI32_T rewr_idx_0 :13; + UI32_T rewr_idx_ctl : 2; + UI32_T flw_lbl :10; + UI32_T mrk_dscp_val_w1 : 5; + /* CLX DWORD 4 */ + UI32_T ts :16; + UI32_T mrk_dei_val : 1; + UI32_T mrk_pcp_val : 3; + UI32_T mrk_pcp_dei_en : 1; + UI32_T rewr_idx_1_w1 :11; +} HAL_DAWN_PKT_PPH_L3MC_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T hit_idx_w0 :12; + UI32_T : 1; + UI32_T nvo3_encap_idx :14; + UI32_T igr_l2_vid_num : 2; + UI32_T decap_act : 3; + /* CLX DWORD 1 */ + UI32_T seg_vmid_w0 :17; + UI32_T nvo3_adj_idx : 8; + UI32_T hit_idx_w1 : 7; + /* CLX DWORD 2 */ + UI32_T : 1; + UI32_T mrk_exp_en : 1; + UI32_T decap_prop_ttl : 1; + UI32_T decr_ttl : 1; + UI32_T : 1; + UI32_T adj_idx :18; + UI32_T : 2; + UI32_T seg_vmid_w1 : 7; + /* CLX DWORD 3 */ + UI32_T :26; + UI32_T php_pop_keep_inner_qos : 1; + UI32_T mrk_exp_val : 3; + UI32_T : 2; + /* CLX DWORD 4 */ + UI32_T ts :16; + UI32_T mrk_dei_val : 1; + UI32_T mrk_pcp_val : 3; + UI32_T mrk_pcp_dei_en : 1; + UI32_T :11; +} HAL_DAWN_PKT_PPH_L25_T; + +#else +#error "Host GPD endian is not defined!!\n" +#endif + +#if defined(CLX_EN_BIG_ENDIAN) + +/* RX GPD STRUCTURE */ +typedef struct +{ + UI32_T data_buf_addr_lo; + UI32_T data_buf_addr_hi; + UI32_T chksum : 16; + UI32_T ioc : 1; + UI32_T : 1; + UI32_T avbl_buf_len : 14; + UI32_T : 32; + + union + { + HAL_DAWN_PKT_ITMH_ETH_T itmh_eth; + HAL_DAWN_PKT_ETMH_FAB_T etmh_fab; + HAL_DAWN_PKT_ETMH_ETH_T etmh_eth; + }; + union + { + HAL_DAWN_PKT_PPH_L2_T pph_l2; + HAL_DAWN_PKT_PPH_L3UC_T pph_l3uc; + HAL_DAWN_PKT_PPH_L3MC_T pph_l3mc; + HAL_DAWN_PKT_PPH_L25_T pph_l25; + }; + + UI32_T : 32; + UI32_T hwo : 1; + UI32_T ch : 1; + UI32_T trn : 1; + UI32_T ecce : 1; + UI32_T errf : 1; + UI32_T : 5; + UI32_T queue : 6; + UI32_T : 2; + UI32_T cnsm_buf_len : 14; + +} HAL_DAWN_PKT_RX_GPD_T; + +/* TX GPD STRUCTURE */ +typedef struct +{ + UI32_T data_buf_addr_lo; + UI32_T data_buf_addr_hi; + UI32_T chksum : 16; + UI32_T ioc : 1; + UI32_T : 1; + UI32_T data_buf_size : 14; + UI32_T : 32; + + union + { + HAL_DAWN_PKT_ITMH_ETH_T itmh_eth; + HAL_DAWN_PKT_ETMH_FAB_T etmh_fab; + HAL_DAWN_PKT_ETMH_ETH_T etmh_eth; + }; + union + { + HAL_DAWN_PKT_PPH_L2_T pph_l2; + HAL_DAWN_PKT_PPH_L3UC_T pph_l3uc; + HAL_DAWN_PKT_PPH_L3MC_T pph_l3mc; + HAL_DAWN_PKT_PPH_L25_T pph_l25; + }; + + UI32_T : 16; + UI32_T ptp_hdr : 16; + UI32_T hwo : 1; + UI32_T ch : 1; + UI32_T : 1; + UI32_T ecce : 1; + UI32_T : 4; + UI32_T cos : 3; + UI32_T phc : 1; /* PTP Header Control */ + UI32_T ipc : 2; /* Ingress Plane Control */ + UI32_T crcc : 1; + UI32_T prg : 1; /* Purge */ + UI32_T : 2; + UI32_T pkt_len : 14; /* Total packet length */ + +} HAL_DAWN_PKT_TX_GPD_T; + +#elif defined(CLX_EN_LITTLE_ENDIAN) + +/* RX GPD STRUCTURE */ +typedef struct +{ + UI32_T data_buf_addr_lo; + UI32_T data_buf_addr_hi; + UI32_T avbl_buf_len : 14; + UI32_T : 1; + UI32_T ioc : 1; + UI32_T chksum : 16; + UI32_T : 32; + + union + { + HAL_DAWN_PKT_ITMH_ETH_T itmh_eth; + HAL_DAWN_PKT_ETMH_FAB_T etmh_fab; + HAL_DAWN_PKT_ETMH_ETH_T etmh_eth; + }; + union + { + HAL_DAWN_PKT_PPH_L2_T pph_l2; + HAL_DAWN_PKT_PPH_L3UC_T pph_l3uc; + HAL_DAWN_PKT_PPH_L3MC_T pph_l3mc; + HAL_DAWN_PKT_PPH_L25_T pph_l25; + }; + + UI32_T : 32; + UI32_T cnsm_buf_len : 14; + UI32_T : 2; + UI32_T queue : 6; + UI32_T : 5; + UI32_T errf : 1; + UI32_T ecce : 1; + UI32_T trn : 1; + UI32_T ch : 1; + UI32_T hwo : 1; + +} HAL_DAWN_PKT_RX_GPD_T; + +/* TX GPD STRUCTURE */ +typedef struct +{ + UI32_T data_buf_addr_lo; + UI32_T data_buf_addr_hi; + UI32_T data_buf_size : 14; + UI32_T : 1; + UI32_T ioc : 1; + UI32_T chksum : 16; + UI32_T : 32; + + union + { + HAL_DAWN_PKT_ITMH_ETH_T itmh_eth; + HAL_DAWN_PKT_ETMH_FAB_T etmh_fab; + HAL_DAWN_PKT_ETMH_ETH_T etmh_eth; + }; + union + { + HAL_DAWN_PKT_PPH_L2_T pph_l2; + HAL_DAWN_PKT_PPH_L3UC_T pph_l3uc; + HAL_DAWN_PKT_PPH_L3MC_T pph_l3mc; + HAL_DAWN_PKT_PPH_L25_T pph_l25; + }; + + UI32_T ptp_hdr : 16; + UI32_T : 16; + UI32_T pkt_len : 14; /* Total packet length */ + UI32_T : 2; + UI32_T prg : 1; /* Purge */ + UI32_T crcc : 1; + UI32_T ipc : 2; /* Ingress Plane Control */ + UI32_T phc : 1; /* PTP Header Control */ + UI32_T cos : 3; + UI32_T : 4; + UI32_T ecce : 1; + UI32_T : 1; + UI32_T ch : 1; + UI32_T hwo : 1; +} HAL_DAWN_PKT_TX_GPD_T; + +#else +#error "Host GPD endian is not defined\n" +#endif + +/* ----------------------------------------------------------------------------------- PP Type */ +typedef enum +{ + HAL_DAWN_PKT_TMH_TYPE_ITMH_ETH = 0, + HAL_DAWN_PKT_TMH_TYPE_ITMH_FAB, + HAL_DAWN_PKT_TMH_TYPE_ETMH_FAB, + HAL_DAWN_PKT_TMH_TYPE_ETMH_ETH, + HAL_DAWN_PKT_TMH_TYPE_LAST + +} HAL_DAWN_PKT_TMH_TYPE_T; + +typedef enum +{ + HAL_DAWN_PKT_TMH_SRV_L2 = 0, + HAL_DAWN_PKT_TMH_SRV_L25_MPLS, + HAL_DAWN_PKT_TMH_SRV_L3, + HAL_DAWN_PKT_TMH_SRV_EGR, /* L3 downgrade L2 */ + HAL_DAWN_PKT_TMH_SRV_L25_NSH, + HAL_DAWN_PKT_TMH_SRV_L25_TRILL, + HAL_DAWN_PKT_TMH_SRV_LAST + +} HAL_DAWN_PKT_TMH_SRV_T; + +typedef enum +{ + HAL_DAWN_PKT_TMH_DECAP_NONE = 0, + HAL_DAWN_PKT_TMH_DECAP_1_MPLS_LABEL, + HAL_DAWN_PKT_TMH_DECAP_2_MPLS_LABEL, + HAL_DAWN_PKT_TMH_DECAP_3_MPLS_LABEL, + HAL_DAWN_PKT_TMH_DECAP_4_MPLS_LABEL, + HAL_DAWN_PKT_TMH_DECAP_IP_TRILL_NSH, + HAL_DAWN_PKT_TMH_DECAP_LAST + +} HAL_DAWN_PKT_TMH_DECAP_T; + +typedef struct +{ + union + { + HAL_DAWN_PKT_ITMH_ETH_T itmh_eth; + HAL_DAWN_PKT_ETMH_FAB_T etmh_fab; + HAL_DAWN_PKT_ETMH_ETH_T etmh_eth; + }; +} HAL_DAWN_PKT_TMH_T; + +typedef struct +{ + union + { + HAL_DAWN_PKT_PPH_L2_T pph_l2; + HAL_DAWN_PKT_PPH_L3UC_T pph_l3uc; + HAL_DAWN_PKT_PPH_L3MC_T pph_l3mc; + HAL_DAWN_PKT_PPH_L25_T pph_l25; + }; +} HAL_DAWN_PKT_PPH_T; + +/* ----------------------------------------------------------------------------------- Reg Type */ +typedef enum +{ + HAL_DAWN_PKT_L2_ISR_RCH0 = (0x1UL << 0), + HAL_DAWN_PKT_L2_ISR_RCH1 = (0x1UL << 1), + HAL_DAWN_PKT_L2_ISR_RCH2 = (0x1UL << 2), + HAL_DAWN_PKT_L2_ISR_RCH3 = (0x1UL << 3), + HAL_DAWN_PKT_L2_ISR_TCH0 = (0x1UL << 4), + HAL_DAWN_PKT_L2_ISR_TCH1 = (0x1UL << 5), + HAL_DAWN_PKT_L2_ISR_TCH2 = (0x1UL << 6), + HAL_DAWN_PKT_L2_ISR_TCH3 = (0x1UL << 7), + HAL_DAWN_PKT_L2_ISR_RX_QID_MAP_ERR = (0x1UL << 8), + HAL_DAWN_PKT_L2_ISR_RX_FRAME_ERR = (0x1UL << 9) + +} HAL_DAWN_PKT_L2_ISR_T; + +typedef enum +{ + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_HWO_ERROR = (0x1UL << 0), /* Tx GPD.hwo = 0 */ + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_CHKSM_ERROR = (0x1UL << 1), /* Tx GPD.chksm is error */ + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_NO_OVFL_ERROR = (0x1UL << 2), /* S/W push too much GPD */ + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_DMA_READ_ERROR = (0x1UL << 3), /* AXI Rd Error when do GPD read */ + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_BUF_SIZE_ERROR = (0x1UL << 4), /* Tx GPD.data_buf_size = 0 */ + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_RUNT_ERROR = (0x1UL << 5), /* Tx GPD.pkt_len < 64 */ + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_OVSZ_ERROR = (0x1UL << 6), /* Tx GPD.pkt_len = 9217 */ + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_LEN_MISMATCH_ERROR = (0x1UL << 7), /* Tx GPD.pkt_len != sum of data_buf_size */ + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_PKTPL_DMA_READ_ERROR = (0x1UL << 8), /* AXI Rd Error when do Payload read */ + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_COS_ERROR = (0x1UL << 9), /* Tx GPD.cos is not match cos_to_tch_map */ + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_GPD_GT255_ERROR = (0x1UL << 10), /* Multi-GPD packet's GPD# > 255 */ + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_PFC = (0x1UL << 11), /* */ + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_CREDIT_UDFL_ERROR = (0x1UL << 12), /* Credit Underflow (count down to 0) */ + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_DMA_WRITE_ERROR = (0x1UL << 13), /* AXI Wr Error (GPD Write-Back) */ + HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_STOP_CMD_CPLT = (0x1UL << 14) + +} HAL_DAWN_PKT_TX_CHANNEL_L2_ISR_T; + +typedef enum +{ + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_LOW = (0x1UL << 0), /* Rx GPD.avbl_gpd_num < threshold */ + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_EMPTY = (0x1UL << 1), /* Rx GPD.avbl_gpd_num = 0 */ + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_ERROR = (0x1UL << 2), /* Rx GPD.hwo = 0 */ + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_GPD_CHKSM_ERROR = (0x1UL << 3), /* Rx GPD.chksm is error */ + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_DMA_READ_ERROR = (0x1UL << 4), /* DMAR error occurs in PCIE */ + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_DMA_WRITE_ERROR = (0x1UL << 5), /* DMAW error occurs in PCIE */ + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_STOP_CMD_CPLT = (0x1UL << 6), /* Stop Completion Acknowledge */ + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_GPD_GT255_ERROR = (0x1UL << 7), /* Multi-GPD packet's GPD# > 255 */ + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_TOD_UNINIT = (0x1UL << 8), /* */ + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_PKT_ERROR_DROP = (0x1UL << 9), /* */ + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_UDSZ_DROP = (0x1UL << 10), /* */ + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_OVSZ_DROP = (0x1UL << 11), /* */ + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_CMDQ_OVF_DROP = (0x1UL << 12), /* */ + HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_FIFO_OVF_DROP = (0x1UL << 13) + +} HAL_DAWN_PKT_RX_CHANNEL_L2_ISR_T; + +typedef enum +{ + HAL_DAWN_PKT_TX_CHANNEL_CFG_IOC = (0x1UL << 0), + HAL_DAWN_PKT_TX_CHANNEL_CFG_CHKSUM = (0x1UL << 1), + HAL_DAWN_PKT_TX_CHANNEL_CFG_PFC = (0x1UL << 2), + HAL_DAWN_PKT_TX_CHANNEL_CFG_PKT_LEN_CHK = (0x1UL << 3), + HAL_DAWN_PKT_TX_CHANNEL_CFG_EARLY_DONE_IRQ = (0x1UL << 4), + HAL_DAWN_PKT_TX_CHANNEL_CFG_CHK_COS = (0x1UL << 5), + HAL_DAWN_PKT_TX_CHANNEL_CFG_ADV_GPD_WRBK = (0x1UL << 6), + HAL_DAWN_PKT_TX_CHANNEL_CFG_GPD_WRBK_FULL_PKT_LEN = (0x1UL << 7), + HAL_DAWN_PKT_TX_CHANNEL_CFG_LAST = (0x1UL << 8) + +} HAL_DAWN_PKT_TX_CHANNEL_CFG_T; + +typedef enum +{ + HAL_DAWN_PKT_RX_CHANNEL_CFG_IOC = (0x1UL << 0), + HAL_DAWN_PKT_RX_CHANNEL_CFG_CHKSUM = (0x1UL << 1), + HAL_DAWN_PKT_RX_CHANNEL_CFG_LAST = (0x1UL << 2) + +} HAL_DAWN_PKT_RX_CHANNEL_CFG_T; + +/* ----------------------------------------------------------------------------------- Tx */ +typedef enum +{ + HAL_DAWN_PKT_TX_CHANNEL_0 = 0, + HAL_DAWN_PKT_TX_CHANNEL_1, + HAL_DAWN_PKT_TX_CHANNEL_2, + HAL_DAWN_PKT_TX_CHANNEL_3, + HAL_DAWN_PKT_TX_CHANNEL_LAST + +} HAL_DAWN_PKT_TX_CHANNEL_T; + +typedef void +(*HAL_DAWN_PKT_TX_FUNC_T)( + const UI32_T unit, + const void *ptr_sw_gpd, /* SW-GPD to be processed */ + void *ptr_coockie); /* Private data of SDK */ + +typedef struct HAL_DAWN_PKT_TX_SW_GPD_S +{ + HAL_DAWN_PKT_TX_FUNC_T callback; /* (unit, ptr_sw_gpd, ptr_cookie) */ + void *ptr_cookie; /* Pointer of CLX_PKT_TX_PKT_T */ + HAL_DAWN_PKT_TX_GPD_T tx_gpd; + UI32_T gpd_num; + struct HAL_DAWN_PKT_TX_SW_GPD_S *ptr_next; + +#if defined (CLX_EN_NETIF) + UI32_T channel; /* For counter */ +#endif + +} HAL_DAWN_PKT_TX_SW_GPD_T; + +typedef struct +{ + UI32_T send_ok; + UI32_T gpd_empty; + UI32_T poll_timeout; + + /* queue */ + UI32_T enque_ok; + UI32_T enque_retry; + + /* event */ + UI32_T trig_event; + + /* normal interrupt */ + UI32_T tx_done; + + /* abnormal interrupt */ + UI32_T gpd_hwo_err; /* bit-0 */ + UI32_T gpd_chksm_err; /* bit-1 */ + UI32_T gpd_no_ovfl_err; /* bit-2 */ + UI32_T gpd_dma_read_err; /* bit-3 */ + UI32_T buf_size_err; /* bit-4 */ + UI32_T runt_err; /* bit-5 */ + UI32_T ovsz_err; /* bit-6 */ + UI32_T len_mismatch_err; /* bit-7 */ + UI32_T pktpl_dma_read_err; /* bit-8 */ + UI32_T cos_err; /* bit-9 */ + UI32_T gpd_gt255_err; /* bit-10 */ + UI32_T pfc; /* bit-11 */ + UI32_T credit_udfl_err; /* bit-12 */ + UI32_T dma_write_err; /* bit-13 */ + UI32_T sw_issue_stop; /* bit-14 */ + + /* others */ + UI32_T err_recover; + UI32_T ecc_err; + +} HAL_DAWN_PKT_TX_CHANNEL_CNT_T; + +typedef struct +{ + HAL_DAWN_PKT_TX_CHANNEL_CNT_T channel[HAL_DAWN_PKT_TX_CHANNEL_LAST]; + UI32_T invoke_gpd_callback; + UI32_T no_memory; + + /* queue */ + UI32_T deque_ok; + UI32_T deque_fail; + + /* event */ + UI32_T wait_event; + +} HAL_DAWN_PKT_TX_CNT_T; + +/* ----------------------------------------------------------------------------------- Rx */ +typedef enum +{ + HAL_DAWN_PKT_RX_CHANNEL_0 = 0, + HAL_DAWN_PKT_RX_CHANNEL_1, + HAL_DAWN_PKT_RX_CHANNEL_2, + HAL_DAWN_PKT_RX_CHANNEL_3, + HAL_DAWN_PKT_RX_CHANNEL_LAST +} HAL_DAWN_PKT_RX_CHANNEL_T; + +typedef enum +{ + HAL_DAWN_PKT_C_NEXT = 0, /* callback continuous */ + HAL_DAWN_PKT_C_STOP = 1, + HAL_DAWN_PKT_C_OTHERS = 2 +} HAL_DAWN_PKT_CALLBACK_NO_T; + +typedef enum +{ + HAL_DAWN_PKT_RX_CALLBACK_ACTION_INSERT = 0, + HAL_DAWN_PKT_RX_CALLBACK_ACTION_APPEND = 1, + HAL_DAWN_PKT_RX_CALLBACK_ACTION_DELETE = 2, + HAL_DAWN_PKT_RX_CALLBACK_ACTION_DELETE_ALL = 3 +} HAL_DAWN_PKT_RX_CALLBACK_ACTION_T; + +typedef HAL_DAWN_PKT_CALLBACK_NO_T +(*HAL_DAWN_PKT_RX_FUNC_T)( + const UI32_T unit, + const void *ptr_sw_gpd, /* SW-GPD to be processed */ + void *ptr_cookie); /* Private data of SDK */ + +typedef struct HAL_DAWN_PKT_RX_CALLBACK_S +{ + HAL_DAWN_PKT_RX_FUNC_T callback; /* (unit, ptr_sw_gpd, ptr_cookie) */ + void *ptr_cookie; + struct HAL_DAWN_PKT_RX_CALLBACK_S *ptr_next; +} HAL_DAWN_PKT_RX_CALLBACK_T; + +typedef struct HAL_DAWN_PKT_RX_SW_GPD_S +{ + BOOL_T rx_complete; /* FALSE when PDMA error occurs */ + HAL_DAWN_PKT_RX_GPD_T rx_gpd; + struct HAL_DAWN_PKT_RX_SW_GPD_S *ptr_next; + +#if defined (CLX_EN_NETIF) + void *ptr_cookie; /* Pointer of virt-addr */ +#endif + +} HAL_DAWN_PKT_RX_SW_GPD_T; + +typedef struct +{ + /* queue */ + UI32_T enque_ok; + UI32_T enque_retry; + UI32_T deque_ok; + UI32_T deque_fail; + + /* event */ + UI32_T trig_event; + + /* normal interrupt */ + UI32_T rx_done; + + /* abnormal interrupt */ + UI32_T avbl_gpd_low; /* bit-0 */ + UI32_T avbl_gpd_empty; /* bit-1 */ + UI32_T avbl_gpd_err; /* bit-2 */ + UI32_T gpd_chksm_err; /* bit-3 */ + UI32_T dma_read_err; /* bit-4 */ + UI32_T dma_write_err; /* bit-5 */ + UI32_T sw_issue_stop; /* bit-6 */ + UI32_T gpd_gt255_err; /* bit-7 */ + UI32_T tod_uninit; /* bit-8 */ + UI32_T pkt_err_drop; /* bit-9 */ + UI32_T udsz_drop; /* bit-10 */ + UI32_T ovsz_drop; /* bit-11 */ + UI32_T cmdq_ovf_drop; /* bit-12 */ + UI32_T fifo_ovf_drop; /* bit-13 */ + + /* others */ + UI32_T err_recover; + UI32_T ecc_err; + +#if defined (CLX_EN_NETIF) + /* it means that user doesn't create intf on that port */ + UI32_T netdev_miss; +#endif + + +} HAL_DAWN_PKT_RX_CHANNEL_CNT_T; + +typedef struct +{ + HAL_DAWN_PKT_RX_CHANNEL_CNT_T channel[HAL_DAWN_PKT_RX_CHANNEL_LAST]; + UI32_T invoke_gpd_callback; + UI32_T no_memory; + + /* event */ + UI32_T wait_event; + +} HAL_DAWN_PKT_RX_CNT_T; + +/* ----------------------------------------------------------------------------------- Reg */ +#if defined(CLX_EN_LITTLE_ENDIAN) + +typedef union +{ + UI32_T reg; + struct + { + UI32_T tch_axlen_cfg : 3; + UI32_T : 5; + UI32_T tch_axi_free_arvalid : 1; + UI32_T : 7; + UI32_T tch_arvalid_thrhold_cfg : 2; + UI32_T : 6; + UI32_T tch_rready_low_4_hdr : 1; + UI32_T tch_ios_crdt_add_en : 1; + UI32_T : 6; + } field; +} HAL_DAWN_PKT_AXI_LEN_CFG_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T pdma_lbk_en : 1; + UI32_T : 3; + UI32_T pdma_lbk_plane : 2; + UI32_T : 2; + UI32_T pm_lbk_en : 1; + UI32_T : 7; + UI32_T pm_lbk_rqid : 6; + UI32_T : 2; + UI32_T : 8; + } field; +} HAL_DAWN_PKT_LBK_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T pdma_lbk_rqid0 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid1 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid2 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid3 : 6; + UI32_T : 2; + } field; +} HAL_DAWN_PKT_LBK_RQID0_3_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T pdma_lbk_rqid4 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid5 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid6 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid7 : 6; + UI32_T : 2; + } field; +} HAL_DAWN_PKT_LBK_RQID4_7_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T cos_pfc_sts0 : 8; + UI32_T cos_pfc_sts1 : 8; + UI32_T cos_pfc_sts2 : 8; + UI32_T cos_pfc_sts3 : 8; + } field; +} HAL_DAWN_PKT_COS_PFC_STS_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T pdma_ela_en : 1; + UI32_T : 7; + UI32_T pdma_ela_valid_sel : 8; + UI32_T : 8; + UI32_T : 8; + } field; +} HAL_DAWN_PKT_ELA_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T pdma_ela_word0_sel : 8; + UI32_T pdma_ela_word1_sel : 8; + UI32_T pdma_ela_word2_sel : 8; + UI32_T pdma_ela_word3_sel : 8; + } field; +} HAL_DAWN_PKT_ELA_SEL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T ingr_pln_ios_credit_base_size_lo : 8; + UI32_T ingr_pln_ios_credit_base_size_hi : 8; + UI32_T ingr_pln_ios_credit_set : 1; + UI32_T : 7; + UI32_T : 1; + UI32_T ingr_pln_full_pkt_mode : 1; + UI32_T : 6; + } field; +} HAL_DAWN_PKT_IGR_PLN_CREDIT_CFG_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T ingr_pln_cur_ios_credit_lo : 8; + UI32_T ingr_pln_cur_ios_credit_hi : 8; + UI32_T ingr_pln_ios_credit_ovfl : 1; + UI32_T ingr_pln_ios_credit_udfl : 1; + UI32_T : 6; + UI32_T : 8; + } field; +} HAL_DAWN_PKT_IGR_PLN_CREDIT_STS_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T ingr_pln_ios_credit_rdy_lo_bound : 8; + UI32_T ingr_pln_ios_credit_rdy_hi_bound : 8; + UI32_T : 8; + UI32_T : 8; + } field; +} HAL_DAWN_PKT_IGR_PLN_CREDIT_THR_REG_T; + + +typedef union +{ + UI32_T reg; + struct + { + UI32_T rch_stomp_crc_en : 1; + UI32_T : 7; + UI32_T rch_crc_regen_en : 1; + UI32_T : 7; + UI32_T rch_pfc_fun_en : 1; + UI32_T : 7; + UI32_T : 8; + } field; +} HAL_DAWN_PKT_RCH_STOMP_CRC_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T rch_ioc_en : 1; + UI32_T : 7; + UI32_T rch_chksm_en : 1; + UI32_T : 7; + UI32_T : 8; + UI32_T : 8; + } field; +} HAL_DAWN_PKT_RCH_MISC_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T rch_gpd_pfc_lo : 8; + UI32_T rch_gpd_pfc_hi : 8; + UI32_T : 8; + UI32_T : 8; + } field; +} HAL_DAWN_PKT_RCH_GPD_PFC_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T rch_fifo_pfc_lo_lo : 8; + UI32_T rch_fifo_pfc_lo_hi : 3; + UI32_T : 5; + UI32_T rch_fifo_pfc_hi_lo : 8; + UI32_T rch_fifo_pfc_hi_hi : 3; + UI32_T : 5; + } field; +} HAL_DAWN_PKT_RCH_FIFO_PFC_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T rch_cmdq_pfc_lo : 5; + UI32_T : 3; + UI32_T rch_cmdq_pfc_hi : 5; + UI32_T : 3; + UI32_T : 8; + UI32_T : 8; + } field; +} HAL_DAWN_PKT_RCH_CMDQ_PFC_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T rch_start : 1; + UI32_T rch_resume : 1; + UI32_T rch_stop : 1; + UI32_T : 5; + UI32_T : 8; + UI32_T rch_gpd_add_no_lo : 8; + UI32_T rch_gpd_add_no_hi : 8; + } field; +} HAL_DAWN_PKT_RCH_CMD_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T rch_fifo_ovf_drop_cnt_clr : 1; + UI32_T rch_cmdq_ovf_drop_cnt_clr : 1; + UI32_T rch_ovsz_drop_cnt_clr : 1; + UI32_T rch_udsz_drop_cnt_clr : 1; + UI32_T rch_pkterr_drop_cnt_clr : 1; + UI32_T rch_flush_cnt_clr : 1; + UI32_T : 2; + UI32_T : 8; + UI32_T : 8; + UI32_T : 8; + } field; +} HAL_DAWN_PKT_RCH_CNT_CLR_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T rch_active : 1; + UI32_T rch_avbl_gpd_pfc : 1; + UI32_T rch_fifo_pfc : 1; + UI32_T rch_cmdq_pfc : 1; + UI32_T rch_pfc : 1; + UI32_T : 3; + UI32_T : 8; + UI32_T rch_avbl_gpd_no_lo : 8; + UI32_T rch_avbl_gpd_no_hi : 8; + } field; +} HAL_DAWN_PKT_RCH_STATUS_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T tch_ioc_en : 1; + UI32_T tch_chksm_en : 1; + UI32_T tch_pfc_en : 1; + UI32_T tch_pktlen_chk_en : 1; + UI32_T tch_early_done_irq : 1; + UI32_T tch_chk_cos_en : 1; + UI32_T tch_adv_gpd_wrbk : 1; + UI32_T tch_gpd_wrbk_full_pkt_len : 1; + UI32_T : 8; + UI32_T : 8; + UI32_T : 8; + } field; +} HAL_DAWN_PKT_TCH_CFG_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T tch_start : 1; + UI32_T tch_resume : 1; + UI32_T tch_stop : 1; + UI32_T : 5; + UI32_T : 8; + UI32_T tch_gpd_add_no_lo : 8; + UI32_T tch_gpd_add_no_hi : 8; + } field; +} HAL_DAWN_PKT_TCH_CMD_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T tch_active : 1; + UI32_T tch_pfc : 1; + UI32_T tch_gpd_rd_dma_act : 1; + UI32_T : 5; + UI32_T : 8; + UI32_T tch_avbl_gpd_no : 1; + UI32_T : 7; + UI32_T : 8; + } field; +} HAL_DAWN_PKT_TCH_STS_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T tch_gpd_dmar_qos : 4; + UI32_T : 4; + UI32_T tch_pkt_dmar_qos : 4; + UI32_T : 4; + UI32_T tch_gpd_dmaw_qos : 4; + UI32_T : 4; + UI32_T : 8; + } field; +} HAL_DAWN_PKT_TCH_QOS_CFG_REG_T; + +#elif defined(CLX_EN_BIG_ENDIAN) + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 6; + UI32_T tch_ios_crdt_add_en : 1; + UI32_T tch_rready_low_4_hdr : 1; + UI32_T : 6; + UI32_T tch_arvalid_thrhold_cfg : 2; + UI32_T : 7; + UI32_T tch_axi_free_arvalid : 1; + UI32_T : 5; + UI32_T tch_axlen_cfg : 3; + } field; +} HAL_DAWN_PKT_AXI_LEN_CFG_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 2; + UI32_T pm_lbk_rqid : 6; + UI32_T : 7; + UI32_T pm_lbk_en : 1; + UI32_T : 2; + UI32_T pdma_lbk_plane : 2; + UI32_T : 3; + UI32_T pdma_lbk_en : 1; + } field; +} HAL_DAWN_PKT_LBK_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 2; + UI32_T pdma_lbk_rqid3 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid2 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid1 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid0 : 6; + } field; +} HAL_DAWN_PKT_LBK_RQID0_3_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 2; + UI32_T pdma_lbk_rqid7 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid6 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid5 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid4 : 6; + } field; +} HAL_DAWN_PKT_LBK_RQID4_7_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T cos_pfc_sts3 : 8; + UI32_T cos_pfc_sts2 : 8; + UI32_T cos_pfc_sts1 : 8; + UI32_T cos_pfc_sts0 : 8; + } field; +} HAL_DAWN_PKT_COS_PFC_STS_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 8; + UI32_T pdma_ela_valid_sel : 8; + UI32_T : 7; + UI32_T pdma_ela_en : 1; + } field; +} HAL_DAWN_PKT_ELA_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T pdma_ela_word3_sel : 8; + UI32_T pdma_ela_word2_sel : 8; + UI32_T pdma_ela_word1_sel : 8; + UI32_T pdma_ela_word0_sel : 8; + } field; +} HAL_DAWN_PKT_ELA_SEL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 6; + UI32_T ingr_pln_full_pkt_mode : 1; + UI32_T : 1; + UI32_T : 7; + UI32_T ingr_pln_ios_credit_set : 1; + UI32_T ingr_pln_ios_credit_base_size_hi : 8; + UI32_T ingr_pln_ios_credit_base_size_lo : 8; + } field; +} HAL_DAWN_PKT_IGR_PLN_CREDIT_CFG_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 6; + UI32_T ingr_pln_ios_credit_udfl : 1; + UI32_T ingr_pln_ios_credit_ovfl : 1; + UI32_T ingr_pln_cur_ios_credit_hi : 8; + UI32_T ingr_pln_cur_ios_credit_lo : 8; + } field; +} HAL_DAWN_PKT_IGR_PLN_CREDIT_STS_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 8; + UI32_T ingr_pln_ios_credit_rdy_hi_bound : 8; + UI32_T ingr_pln_ios_credit_rdy_lo_bound : 8; + } field; +} HAL_DAWN_PKT_IGR_PLN_CREDIT_THR_REG_T; + + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 7; + UI32_T rch_pfc_fun_en : 1; + UI32_T : 7; + UI32_T rch_crc_regen_en : 1; + UI32_T : 7; + UI32_T rch_stomp_crc_en : 1; + } field; +} HAL_DAWN_PKT_RCH_STOMP_CRC_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 8; + UI32_T : 7; + UI32_T rch_chksm_en : 1; + UI32_T : 7; + UI32_T rch_ioc_en : 1; + } field; +} HAL_DAWN_PKT_RCH_MISC_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 8; + UI32_T rch_gpd_pfc_hi : 8; + UI32_T rch_gpd_pfc_lo : 8; + } field; +} HAL_DAWN_PKT_RCH_GPD_PFC_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 5; + UI32_T rch_fifo_pfc_hi_hi : 3; + UI32_T rch_fifo_pfc_hi_lo : 8; + UI32_T : 5; + UI32_T rch_fifo_pfc_lo_hi : 3; + UI32_T rch_fifo_pfc_lo_lo : 8; + } field; +} HAL_DAWN_PKT_RCH_FIFO_PFC_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 8; + UI32_T : 3; + UI32_T rch_cmdq_pfc_hi : 5; + UI32_T : 3; + UI32_T rch_cmdq_pfc_lo : 5; + } field; +} HAL_DAWN_PKT_RCH_CMDQ_PFC_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T rch_gpd_add_no_hi : 8; + UI32_T rch_gpd_add_no_lo : 8; + UI32_T : 8; + UI32_T : 5; + UI32_T rch_stop : 1; + UI32_T rch_resume : 1; + UI32_T rch_start : 1; + } field; +} HAL_DAWN_PKT_RCH_CMD_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 8; + UI32_T : 8; + UI32_T : 2; + UI32_T rch_flush_cnt_clr : 1; + UI32_T rch_pkterr_drop_cnt_clr : 1; + UI32_T rch_udsz_drop_cnt_clr : 1; + UI32_T rch_ovsz_drop_cnt_clr : 1; + UI32_T rch_cmdq_ovf_drop_cnt_clr : 1; + UI32_T rch_fifo_ovf_drop_cnt_clr : 1; + } field; +} HAL_DAWN_PKT_RCH_CNT_CLR_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T rch_avbl_gpd_no_hi : 8; + UI32_T rch_avbl_gpd_no_lo : 8; + UI32_T : 8; + UI32_T : 3; + UI32_T rch_pfc : 1; + UI32_T rch_cmdq_pfc : 1; + UI32_T rch_fifo_pfc : 1; + UI32_T rch_avbl_gpd_pfc : 1; + UI32_T rch_active : 1; + } field; +} HAL_DAWN_PKT_RCH_STATUS_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 8; + UI32_T : 8; + UI32_T tch_gpd_wrbk_full_pkt_len : 1; + UI32_T tch_adv_gpd_wrbk : 1; + UI32_T tch_chk_cos_en : 1; + UI32_T tch_early_done_irq : 1; + UI32_T tch_pktlen_chk_en : 1; + UI32_T tch_pfc_en : 1; + UI32_T tch_chksm_en : 1; + UI32_T tch_ioc_en : 1; + } field; +} HAL_DAWN_PKT_TCH_CFG_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T tch_gpd_add_no_hi : 8; + UI32_T tch_gpd_add_no_lo : 8; + UI32_T : 8; + UI32_T : 5; + UI32_T tch_stop : 1; + UI32_T tch_resume : 1; + UI32_T tch_start : 1; + } field; +} HAL_DAWN_PKT_TCH_CMD_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 7; + UI32_T tch_avbl_gpd_no : 1; + UI32_T : 8; + UI32_T : 5; + UI32_T tch_gpd_rd_dma_act : 1; + UI32_T tch_pfc : 1; + UI32_T tch_active : 1; + } field; +} HAL_DAWN_PKT_TCH_STS_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 4; + UI32_T tch_gpd_dmaw_qos : 4; + UI32_T : 4; + UI32_T tch_pkt_dmar_qos : 4; + UI32_T : 4; + UI32_T tch_gpd_dmar_qos : 4; + } field; +} HAL_DAWN_PKT_TCH_QOS_CFG_REG_T; + +#else +#error "Host GPD endian is not defined\n" +#endif + +/* ----------------------------------------------------------------------------------- CLX_EN_NETIF */ +#if defined (CLX_EN_NETIF) +#define HAL_DAWN_PKT_DRIVER_MAJOR_NUM (10) +#define HAL_DAWN_PKT_DRIVER_MINOR_NUM (252) /* DO NOT use MISC_DYNAMIC_MINOR */ +#define HAL_DAWN_PKT_DRIVER_NAME "clx_netif" +#define HAL_DAWN_PKT_DRIVER_PATH "/dev/"HAL_DAWN_PKT_DRIVER_NAME + +/* These requirements come from CLX_NETIF APIs. + * clx_netif -> hal_pkt_drv -> hal_pkt_knl + */ + +typedef struct +{ + UI32_T tx_pkt; + UI32_T tx_queue_full; + UI32_T tx_error; + UI32_T rx_pkt; + +} HAL_DAWN_PKT_NETIF_INTF_CNT_T; + +typedef struct +{ + /* unique key */ + UI32_T id; + C8_T name[CLX_NETIF_NAME_LEN]; + UI32_T port; /* only support unit port and local port */ + + /* metadata */ + UI8_T mac[6]; + +#define HAL_DAWN_PKT_NETIF_INTF_FLAGS_MAC (1UL << 0) + UI32_T flags; + + +} HAL_DAWN_PKT_NETIF_INTF_T; + +#if defined(NETIF_EN_NETLINK) +typedef struct +{ + C8_T name[CLX_NETIF_NAME_LEN]; + C8_T mc_group_name[CLX_NETIF_NAME_LEN]; +} HAL_DAWN_PKT_NETIF_RX_DST_NETLINK_T; +#endif + +typedef enum +{ + HAL_DAWN_PKT_NETIF_RX_DST_SDK = 0, +#if defined(NETIF_EN_NETLINK) + HAL_DAWN_PKT_NETIF_RX_DST_NETLINK, +#endif + HAL_DAWN_PKT_NETIF_RX_DST_LAST +} HAL_DAWN_PKT_NETIF_RX_DST_TYPE_T; + +typedef struct +{ + /* unique key */ + UI32_T id; + C8_T name[CLX_NETIF_NAME_LEN]; + UI32_T priority; + + /* match fields */ + UI32_T port; /* only support unit port and local port */ + HAL_PKT_RX_REASON_BITMAP_T reason_bitmap; + UI8_T pattern[CLX_NETIF_PROFILE_PATTERN_NUM][CLX_NETIF_PROFILE_PATTERN_LEN]; + UI8_T mask[CLX_NETIF_PROFILE_PATTERN_NUM][CLX_NETIF_PROFILE_PATTERN_LEN]; + UI32_T offset[CLX_NETIF_PROFILE_PATTERN_NUM]; + + /* for each flag 1:must hit, 0:don't care */ +#define HAL_DAWN_PKT_NETIF_PROFILE_FLAGS_PORT (1UL << 0) +#define HAL_DAWN_PKT_NETIF_PROFILE_FLAGS_REASON (1UL << 1) +#define HAL_DAWN_PKT_NETIF_PROFILE_FLAGS_PATTERN_0 (1UL << 2) +#define HAL_DAWN_PKT_NETIF_PROFILE_FLAGS_PATTERN_1 (1UL << 3) +#define HAL_DAWN_PKT_NETIF_PROFILE_FLAGS_PATTERN_2 (1UL << 4) +#define HAL_DAWN_PKT_NETIF_PROFILE_FLAGS_PATTERN_3 (1UL << 5) + UI32_T flags; + + HAL_DAWN_PKT_NETIF_RX_DST_TYPE_T dst_type; +#if defined(NETIF_EN_NETLINK) + HAL_DAWN_PKT_NETIF_RX_DST_NETLINK_T netlink; +#endif + +} HAL_DAWN_PKT_NETIF_PROFILE_T; + + +/* These requirements come from CLX_PKT APIs. + * clx_pkt -> hal_pkt_srv -> hal_pkt_drv -> hal_pkt_knl + */ +typedef enum +{ + /* network interface */ + HAL_DAWN_PKT_IOCTL_TYPE_CREATE_INTF = 0, + HAL_DAWN_PKT_IOCTL_TYPE_DESTROY_INTF, + HAL_DAWN_PKT_IOCTL_TYPE_GET_INTF, + HAL_DAWN_PKT_IOCTL_TYPE_CREATE_PROFILE, + HAL_DAWN_PKT_IOCTL_TYPE_DESTROY_PROFILE, + HAL_DAWN_PKT_IOCTL_TYPE_GET_PROFILE, + HAL_DAWN_PKT_IOCTL_TYPE_GET_INTF_CNT, + HAL_DAWN_PKT_IOCTL_TYPE_CLEAR_INTF_CNT, + /* driver */ + HAL_DAWN_PKT_IOCTL_TYPE_WAIT_RX_FREE, + HAL_DAWN_PKT_IOCTL_TYPE_WAIT_TX_FREE, /* waitTxFree(ASYNC) */ + HAL_DAWN_PKT_IOCTL_TYPE_SET_RX_CFG, /* setRxConfig */ + HAL_DAWN_PKT_IOCTL_TYPE_GET_RX_CFG, /* getRxConfig */ + HAL_DAWN_PKT_IOCTL_TYPE_DEINIT_TASK, /* deinitTask */ + HAL_DAWN_PKT_IOCTL_TYPE_DEINIT_DRV, /* deinitDrv */ + HAL_DAWN_PKT_IOCTL_TYPE_INIT_TASK, /* initTask */ + HAL_DAWN_PKT_IOCTL_TYPE_INIT_DRV, /* initDrv */ + /* counter */ + HAL_DAWN_PKT_IOCTL_TYPE_GET_TX_CNT, + HAL_DAWN_PKT_IOCTL_TYPE_GET_RX_CNT, + HAL_DAWN_PKT_IOCTL_TYPE_CLEAR_TX_CNT, + HAL_DAWN_PKT_IOCTL_TYPE_CLEAR_RX_CNT, + /* port attribute */ + HAL_DAWN_PKT_IOCTL_TYPE_SET_PORT_ATTR, +#if defined(NETIF_EN_NETLINK) + HAL_DAWN_PKT_IOCTL_TYPE_NL_SET_INTF_PROPERTY, + HAL_DAWN_PKT_IOCTL_TYPE_NL_GET_INTF_PROPERTY, + HAL_DAWN_PKT_IOCTL_TYPE_NL_CREATE_NETLINK, + HAL_DAWN_PKT_IOCTL_TYPE_NL_DESTROY_NETLINK, + HAL_DAWN_PKT_IOCTL_TYPE_NL_GET_NETLINK, +#endif + HAL_DAWN_PKT_IOCTL_TYPE_LAST + +} HAL_DAWN_PKT_IOCTL_TYPE_T; + +typedef enum +{ + HAL_DAWN_PKT_IOCTL_RX_TYPE_INIT = 0, + HAL_DAWN_PKT_IOCTL_RX_TYPE_DEINIT, + HAL_DAWN_PKT_IOCTL_RX_TYPE_LAST, + +} HAL_DAWN_PKT_IOCTL_RX_TYPE_T; + +typedef struct +{ + UI32_T unit; + UI32_T channel; + HAL_DAWN_PKT_RX_CNT_T rx_cnt; + HAL_DAWN_PKT_TX_CNT_T tx_cnt; + CLX_ERROR_NO_T rc; + +} HAL_DAWN_PKT_IOCTL_CH_CNT_COOKIE_T; + +typedef struct +{ + UI32_T unit; + HAL_DAWN_PKT_NETIF_INTF_T net_intf; /* addIntf[In,Out], delIntf[In] */ + HAL_DAWN_PKT_NETIF_PROFILE_T net_profile; /* createProfile[In,Out], destroyProfile[In] */ + HAL_DAWN_PKT_NETIF_INTF_CNT_T cnt; + CLX_ERROR_NO_T rc; + +} HAL_DAWN_PKT_IOCTL_NETIF_COOKIE_T; + +typedef struct +{ + CLX_ADDR_T callback; /* (unit, ptr_sw_gpd, ptr_cookie) */ + CLX_ADDR_T cookie; /* Pointer of CLX_PKT_TX_PKT_T */ + UI32_T channel; + UI32_T gpd_num; + CLX_ADDR_T hw_gpd_addr; + CLX_ADDR_T sw_gpd_addr; + +} HAL_DAWN_PKT_IOCTL_TX_GPD_T; + +typedef struct +{ + UI32_T unit; + UI32_T channel; /* sendGpd[In] */ + CLX_ADDR_T ioctl_gpd_addr; /* sendGpd[In] */ + CLX_ADDR_T done_sw_gpd_addr; /* waitTxFree[Out] */ + +} HAL_DAWN_PKT_IOCTL_TX_COOKIE_T; + +typedef struct +{ + BOOL_T rx_complete; /* FALSE when PDMA error occurs */ + CLX_ADDR_T hw_gpd_addr; /* Pointer to HW GPD in user's SW GPD struct */ + CLX_ADDR_T dma_buf_addr; /* Pointer to DMA buffer allocated by the user (virtual) */ + +} HAL_DAWN_PKT_IOCTL_RX_GPD_T; + +typedef struct +{ + UI32_T unit; + UI32_T channel; /* getRxCnt[In], clearRxInt[In] */ + CLX_ADDR_T ioctl_gpd_addr; /* waitRxFree[Out] */ + UI32_T buf_len; /* setRxCfg[In] */ + HAL_DAWN_PKT_IOCTL_RX_TYPE_T rx_type; /* setRxCfg[In] */ + +} HAL_DAWN_PKT_IOCTL_RX_COOKIE_T; + +typedef struct +{ + UI32_T port; + UI32_T status; + CLX_PORT_SPEED_T speed; + +} HAL_DAWN_PKT_IOCTL_PORT_COOKIE_T; + +#if defined(NETIF_EN_NETLINK) + +typedef struct +{ + /* intf property */ + UI32_T intf_id; + CLX_NETIF_INTF_PROPERTY_T property; + UI32_T param0; + UI32_T param1; + + /* netlink */ + CLX_NETIF_NETLINK_T netlink; + + CLX_ERROR_NO_T rc; + +} HAL_DAWN_PKT_NL_IOCTL_COOKIE_T; + + +#endif /* End of NETIF_EN_NETLINK */ + +typedef union +{ + UI32_T value; + struct + { + UI32_T unit : 6; /* Maximum unit number is 64. */ + HAL_DAWN_PKT_IOCTL_TYPE_T type : 10; /* Maximum 1024 IOCTL types */ + UI32_T rsvd : 16; + } field; + +} HAL_DAWN_PKT_IOCTL_CMD_T; + +#endif /* End of CLX_EN_NETIF */ + +/*---------------------------------------------------------------------------*/ +/* perf */ +CLX_ERROR_NO_T +hal_dawn_pkt_getTxIntrCnt( + const UI32_T unit, + const UI32_T channel, + UI32_T *ptr_intr_cnt); + +CLX_ERROR_NO_T +hal_dawn_pkt_getRxIntrCnt( + const UI32_T unit, + const UI32_T channel, + UI32_T *ptr_intr_cnt); + +/* ioctl */ +CLX_ERROR_NO_T +hal_dawn_pkt_getTxKnlCnt( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_CH_CNT_COOKIE_T *ptr_cookie); + +CLX_ERROR_NO_T +hal_dawn_pkt_getRxKnlCnt( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_CH_CNT_COOKIE_T *ptr_cookie); + +CLX_ERROR_NO_T +hal_dawn_pkt_clearTxKnlCnt( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_TX_COOKIE_T *ptr_cookie); + +CLX_ERROR_NO_T +hal_dawn_pkt_clearRxKnlCnt( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_RX_COOKIE_T *ptr_cookie); + +CLX_ERROR_NO_T +hal_dawn_pkt_setRxKnlConfig( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_RX_COOKIE_T *ptr_cookie); + +CLX_ERROR_NO_T +hal_dawn_pkt_getRxKnlConfig( + const UI32_T unit, + HAL_DAWN_PKT_IOCTL_RX_COOKIE_T *ptr_cookie); + +/* perf */ +CLX_ERROR_NO_T +hal_dawn_pkt_getNetDev( + const UI32_T unit, + const UI32_T port, + struct net_device **pptr_net_dev); + +CLX_ERROR_NO_T +hal_dawn_pkt_prepareGpd( + const UI32_T unit, + const CLX_ADDR_T phy_addr, + const UI32_T len, + const UI32_T port, + HAL_DAWN_PKT_TX_SW_GPD_T *ptr_sw_gpd); + +CLX_ERROR_NO_T +hal_dawn_pkt_sendGpd( + const UI32_T unit, + const HAL_DAWN_PKT_TX_CHANNEL_T channel, + HAL_DAWN_PKT_TX_SW_GPD_T *ptr_sw_gpd); + +CLX_ERROR_NO_T +hal_dawn_pkt_init( + const UI32_T unit); + +CLX_ERROR_NO_T +hal_dawn_pkt_exit( + const UI32_T unit); + +ssize_t +hal_dawn_pkt_dev_tx( + struct file *file, + const char __user *buf, + size_t count, + loff_t *pos); + +long +hal_dawn_pkt_dev_ioctl( + struct file *filp, + unsigned int cmd, + unsigned long arg); + +#endif /* end of HAL_DAWN_PKT_KNL_H */ diff --git a/platform/clounix/clounix-modules/modules/src/inc/hal_lightning_pkt_knl.h b/platform/clounix/clounix-modules/modules/src/inc/hal_lightning_pkt_knl.h new file mode 100755 index 000000000000..307ef417709c --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/inc/hal_lightning_pkt_knl.h @@ -0,0 +1,2293 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: hal_lightning_pkt_knl.h + * PURPOSE: + * To provide Linux kernel for PDMA TX/RX control. + * + * NOTES: + */ + +#ifndef HAL_LIGHTNING_PKT_KNL_H +#define HAL_LIGHTNING_PKT_KNL_H + +#include +#include + +/* CP_COMMON */ +#define HAL_LIGHTNING_PKT_CP_COMMON_INT_EN_HI (0x000FC000) +#define HAL_LIGHTNING_PKT_CP_COMMON_INT_EN_LO (0x000FC004) +#define HAL_LIGHTNING_PKT_CP_COMMON_INT_LVL_HI (0x000FC008) +#define HAL_LIGHTNING_PKT_CP_COMMON_INT_LVL_LO (0x000FC00C) +#define HAL_LIGHTNING_PKT_CP_COMMON_INT_MASK_SET_HI (0x000FC010) +#define HAL_LIGHTNING_PKT_CP_COMMON_INT_MASK_SET_LO (0x000FC014) +#define HAL_LIGHTNING_PKT_CP_COMMON_INT_MASK_CLR_HI (0x000FC018) +#define HAL_LIGHTNING_PKT_CP_COMMON_INT_MASK_CLR_LO (0x000FC034) +#define HAL_LIGHTNING_PKT_CP_COMMON_INT_MASK_VAL_HI (0x000FC020) +#define HAL_LIGHTNING_PKT_CP_COMMON_INT_MASK_VAL_LO (0x000FC024) +#define HAL_LIGHTNING_PKT_CP_COMMON_INT_STAT_HI (0x000FC028) +#define HAL_LIGHTNING_PKT_CP_COMMON_INT_STAT_LO (0x000FC02C) +#define HAL_LIGHTNING_PKT_CP_COMMON_INT_CLR_HI (0x000FC030) +#define HAL_LIGHTNING_PKT_CP_COMMON_INT_CLR_LO (0x000FC034) +#define HAL_LIGHTNING_PKT_CP_COMMON_INT_SET_HI (0x000FC038) +#define HAL_LIGHTNING_PKT_CP_COMMON_INT_SET_LO (0x000FC03C) + +/* PDMA */ +#define HAL_LIGHTNING_PKT_PDMA_ERR_INT_STAT (0x003F1000) +#define HAL_LIGHTNING_PKT_PDMA_ERR_INT_CLR (0x003F1004) +#define HAL_LIGHTNING_PKT_PDMA_ERR_INT_EN (0x003F1010) +#define HAL_LIGHTNING_PKT_PDMA_ERR_INT_LVL (0x003F1014) +#define HAL_LIGHTNING_PKT_PDMA_ERR_INT_MASK_SET (0x003F1018) +#define HAL_LIGHTNING_PKT_PDMA_ERR_INT_MASK_CLR (0x003F101C) +#define HAL_LIGHTNING_PKT_PDMA_ERR_INT_MASK_VAL (0x003F1020) +#define HAL_LIGHTNING_PKT_PDMA_ERR_INT_SET (0x003F1024) +#define HAL_LIGHTNING_PKT_PDMA_CREDIT_CFG (0x003F1100) + +/* Rx */ +#define HAL_LIGHTNING_PKT_PDMA_RCH_GPD_RING_START_ADDR_LO (0x003F12E4) +#define HAL_LIGHTNING_PKT_PDMA_RCH_GPD_RING_START_ADDR_HI (0x003F12E8) +#define HAL_LIGHTNING_PKT_PDMA_RCH_GPD_RING_SIZE (0x003F12EC) +#define HAL_LIGHTNING_PKT_PDMA_RCH_CMD (0x003F1300) +#define HAL_LIGHTNING_PKT_PDMA_RCH_INT_EN (0x003F1360) +#define HAL_LIGHTNING_PKT_PDMA_RCH_INT_LVL (0x003F1364) +#define HAL_LIGHTNING_PKT_PDMA_RCH_INT_MASK (0x003F1368) +#define HAL_LIGHTNING_PKT_PDMA_RCH_INT_SET (0x003F1374) +#define HAL_LIGHTNING_PKT_PDMA_RCH_INT_CLR (0x003F1378) +#define HAL_LIGHTNING_PKT_PDMA_RCH_INT_STAT (0x003F1370) + +/* Tx */ +#define HAL_LIGHTNING_PKT_PDMA_TCH_GPD_RING_START_ADDR_LO (0x003F1A00) +#define HAL_LIGHTNING_PKT_PDMA_TCH_GPD_RING_START_ADDR_HI (0x003F1A04) +#define HAL_LIGHTNING_PKT_PDMA_TCH_GPD_RING_SIZE (0x003F1A08) +#define HAL_LIGHTNING_PKT_PDMA_TCH_CMD (0x003F1A20) +#define HAL_LIGHTNING_PKT_PDMA_TCH_INT_EN (0x003F1A40) +#define HAL_LIGHTNING_PKT_PDMA_TCH_INT_LVL (0x003F1A44) +#define HAL_LIGHTNING_PKT_PDMA_TCH_INT_MASK (0x003F1A48) +#define HAL_LIGHTNING_PKT_PDMA_TCH_INT_SET (0x003F1A54) +#define HAL_LIGHTNING_PKT_PDMA_TCH_INT_CLR (0x003F1A58) +#define HAL_LIGHTNING_PKT_PDMA_TCH_INT_STAT (0x003F1A50) + +#define HAL_LIGHTNING_PKT_GET_MMIO(__tbl__) (0x00FFFFFF & (__tbl__)) +#define HAL_LIGHTNING_PKT_GET_PDMA_RCH_REG(__tbl__, __channel__) ((__tbl__) + (0x200 * (__channel__))) +#define HAL_LIGHTNING_PKT_GET_PDMA_TCH_REG(__tbl__, __channel__) ((__tbl__) + (0x100 * (__channel__))) + +#define HAL_LIGHTNING_PORT_NUM (256) +#define HAL_LIGHTNING_PLANE_NUM (8) +#define HAL_LIGHTNING_PLANE_BITS (3) +#define HAL_LIGHTNING_PLANE_MASK (0x7) + +/* NAMING DECLARATIONS + */ +/* PKT definitions */ +#define HAL_LIGHTNING_PKT_TX_MAX_LEN (9216) +#define HAL_LIGHTNING_PKT_RX_MAX_LEN (9216 + 86) /* EPP tunnel header */ +#define HAL_LIGHTNING_PKT_MIN_LEN (64) /* Ethernet definition */ +#define HAL_LIGHTNING_PKT_TMH_HDR_SZ (20) +#define HAL_LIGHTNING_PKT_PPH_HDR_SZ (20) +#define HAL_LIGHTNING_PKT_CRC_LEN (4) + +/* CH */ +#define HAL_LIGHTNING_PKT_CH_LAST_GPD (0) +#define HAL_LIGHTNING_PKT_CH_MIDDLE_GPD (1) + +/* PRG */ +#define HAL_LIGHTNING_PKT_PRG_PROCESS_GPD (0) /* Normal */ +#define HAL_LIGHTNING_PKT_PRG_SKIP_GPD (1) /* Skip */ + +/* CRCC */ +#define HAL_LIGHTNING_PKT_CRCC_SUM_BY_HW (0) /* calculated by HW */ +#define HAL_LIGHTNING_PKT_CRCC_SUM_BY_SW (1) /* calculated by SW */ + +/* IOC */ +#define HAL_LIGHTNING_PKT_IOC_NO_INTR (0) /* trigger interrupt each GPD */ +#define HAL_LIGHTNING_PKT_IOC_HAS_INTR (1) /* trigger interrupt when ch=0, default setting */ + +/* HWO */ +#define HAL_LIGHTNING_PKT_HWO_SW_OWN (0) +#define HAL_LIGHTNING_PKT_HWO_HW_OWN (1) + +/* ECC */ +#define HAL_LIGHTNING_PKT_ECC_ERROR_OCCUR (1) + +/* CPU, CPI queue number */ +#define HAL_LIGHTNING_PKT_CPU_QUE_NUM (48) +#define HAL_LIGHTNING_PKT_CPI_QUE_NUM (8) + +/* PDMA Definitions */ +#define HAL_LIGHTNING_PKT_PDMA_MAX_GPD_PER_PKT (10) /* <= 256 */ +#define HAL_LIGHTNING_PKT_PDMA_TX_INTR_TIMEOUT (10 * 1000) /* us */ +#define HAL_LIGHTNING_PKT_PDMA_TX_POLL_MAX_LOOP (10 * 1000) /* int */ + +/* Mode */ +#define HAL_LIGHTNING_PKT_TX_WAIT_MODE (HAL_LIGHTNING_PKT_TX_WAIT_ASYNC) + +/* TX Queue */ +#define HAL_LIGHTNING_PKT_TX_TASK_MAX_LOOP (HAL_DFLT_CFG_PKT_TX_QUEUE_LEN) + +/* RX Queue */ +#define HAL_LIGHTNING_PKT_RX_QUEUE_NUM (HAL_LIGHTNING_PKT_RX_CHANNEL_LAST) +#define HAL_LIGHTNING_PKT_RX_TASK_MAX_LOOP (HAL_DFLT_CFG_PKT_RX_QUEUE_LEN) + +/* MACRO FUNCTION DECLARATIONS + */ +/*---------------------------------------------------------------------------*/ +/* [CL8570] Alignment to 64-bytes */ +#if defined(CLX_EN_HOST_64_BIT_BIG_ENDIAN) || defined(CLX_EN_HOST_64_BIT_LITTLE_ENDIAN) +#define HAL_LIGHTNING_PKT_PDMA_ALIGN_ADDR(pdma_addr, align_sz) (((pdma_addr) + (align_sz)) & 0xFFFFFFFFFFFFFFC0) +#else +#define HAL_LIGHTNING_PKT_PDMA_ALIGN_ADDR(pdma_addr, align_sz) (((pdma_addr) + (align_sz)) & 0xFFFFFFC0) +#endif +/*---------------------------------------------------------------------------*/ +#if defined(CLX_EN_BIG_ENDIAN) +#define HAL_LIGHTNING_PKT_ENDIAN_SWAP32(val) (val) +#else +#define HAL_LIGHTNING_PKT_ENDIAN_SWAP32(val) CMLIB_UTIL_ENDIAN_SWAP32(val) +#endif +/*---------------------------------------------------------------------------*/ +#define HAL_LIGHTNING_PKT_GET_BIT(flags, bit) ((((flags) & (bit)) > 0)? 1 : 0) +/*---------------------------------------------------------------------------*/ +#define HAL_LIGHTNING_PKT_SET_BITMAP(bitmap, mask_bitmap) (bitmap = ((bitmap) | (mask_bitmap))) +#define HAL_LIGHTNING_PKT_CLR_BITMAP(bitmap, mask_bitmap) (bitmap = ((bitmap) & (~(mask_bitmap)))) +/*---------------------------------------------------------------------------*/ +#define HAL_LIGHTNING_PKT_GET_TX_INTR_TYPE(channel) (HAL_INTR_TX_CH0 + channel) +#define HAL_LIGHTNING_PKT_GET_RX_INTR_TYPE(channel) (HAL_INTR_RX_CH0 + channel) + +/* DATA TYPE DECLARATIONS + */ +typedef enum +{ + HAL_LIGHTNING_PKT_TX_WAIT_ASYNC = 0, + HAL_LIGHTNING_PKT_TX_WAIT_SYNC_INTR = 1, + HAL_LIGHTNING_PKT_TX_WAIT_SYNC_POLL = 2 + +} HAL_LIGHTNING_PKT_TX_WAIT_T; + +typedef enum +{ + HAL_LIGHTNING_PKT_RX_SCHED_RR = 0, + HAL_LIGHTNING_PKT_RX_SCHED_WRR = 1 + +} HAL_LIGHTNING_PKT_RX_SCHED_T; + +/* GPD and Packet Strucutre Definition */ +#if defined(CLX_EN_BIG_ENDIAN) + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T typ : 2; + UI32_T tc : 4; + UI32_T color : 2; + UI32_T srv : 3; + UI32_T trig : 1; + UI32_T igr_phy_port :12; + UI32_T hsh_val_w0 : 8; + /* CLX DWORD 1 */ + UI32_T hsh_val_w1 : 2; + UI32_T dst_idx :15; + UI32_T src_idx :15; + /* CLX DWORD 2 */ + UI32_T intf_fdid :14; + UI32_T nvo3_mgid_is_transit : 1; + UI32_T skip_epp : 1; + UI32_T steer_applied : 1; + UI32_T : 2; + UI32_T ecn : 2; + UI32_T store_and_forward : 1; + UI32_T lag_epoch : 1; + UI32_T src_supp_tag : 5; + UI32_T : 2; + UI32_T skip_ipp : 1; + UI32_T igr_fab_port_grp : 1; + /* CLX DWORD 3 */ + UI32_T :32; + /* CLX DWORD 4 */ + UI32_T :20; + UI32_T had_uturn : 1; + UI32_T fab_stacking_supp : 2; + UI32_T chip_igr_lag_port : 8; + UI32_T exp_dscp_mrkd : 1; +} HAL_LIGHTNING_PKT_ITMH_FAB_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T typ : 2; + UI32_T tc : 4; + UI32_T color : 2; + UI32_T srv : 3; + UI32_T trig : 1; + UI32_T igr_phy_port :12; + UI32_T hsh_val_w0 : 8; + /* CLX DWORD 1 */ + UI32_T hsh_val_w1 : 2; + UI32_T dst_idx :15; + UI32_T src_idx :15; + /* CLX DWORD 2 */ + UI32_T intf_fdid :14; + UI32_T nvo3_mgid_is_transit : 1; + UI32_T skip_epp : 1; + UI32_T steer_applied : 1; + UI32_T nvo3_ip_tnl_decap_prop_ttl : 1; + UI32_T nvo3_mpls_uhp_prop_ttl : 1; + UI32_T ecn : 2; + UI32_T store_and_forward : 1; + UI32_T lag_epoch : 1; + UI32_T src_supp_tag : 5; + UI32_T one_arm_rte_srv_fdid : 1; + UI32_T fab_one_arm_rte : 1; + UI32_T skip_ipp : 1; + UI32_T igr_fab_port_grp : 1; + /* CLX DWORD 3 */ + UI32_T : 2; + UI32_T nvo3_mgid :15; + UI32_T nvo3_intf :14; + UI32_T nvo3_src_supp_tag_w0 : 1; + /* CLX DWORD 4 */ + UI32_T nvo3_src_supp_tag_w1 : 4; + UI32_T mir_bmap : 8; + UI32_T cp_to_cpu_code : 4; + UI32_T cp_to_cpu_bmap :16; +} HAL_LIGHTNING_PKT_ITMH_ETH_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T typ : 2; + UI32_T tc : 4; + UI32_T color : 2; + UI32_T srv : 3; + UI32_T trig : 1; + UI32_T igr_phy_port :12; + UI32_T hsh_val_w0 : 8; + /* CLX DWORD 1 */ + UI32_T hsh_val_w1 : 2; + UI32_T dst_idx :15; + UI32_T src_idx :15; + /* CLX DWORD 2 */ + UI32_T intf_fdid :14; + UI32_T nvo3_mgid_is_transit : 1; + UI32_T skip_epp : 1; + UI32_T steer_applied : 1; + UI32_T nvo3_ip_tnl_decap_prop_ttl : 1; + UI32_T nvo3_mpls_uhp_prop_ttl : 1; + UI32_T ecn : 2; + UI32_T store_and_forward : 1; + UI32_T lag_epoch : 1; + UI32_T src_supp_tag : 5; + UI32_T one_arm_rte_srv_fdid : 1; + UI32_T fab_one_arm_rte : 1; + UI32_T : 2; + /* CLX DWORD 3 */ + UI32_T :32; + /* CLX DWORD 4 */ + UI32_T :20; + UI32_T had_uturn : 1; + UI32_T : 2; + UI32_T excpt_code : 8; + UI32_T exp_dscp_mrkd : 1; +} HAL_LIGHTNING_PKT_ETMH_FAB_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T typ : 2; + UI32_T tc : 4; + UI32_T color : 2; + UI32_T srv : 3; + UI32_T trig : 1; + UI32_T igr_phy_port :12; + UI32_T hsh_val_w0 : 8; + /* CLX DWORD 1 */ + UI32_T hsh_val_w1 : 2; + UI32_T dst_idx :15; + UI32_T src_idx :15; + /* CLX DWORD 2 */ + UI32_T intf_fdid :14; + UI32_T nvo3_mgid_is_transit : 1; + UI32_T skip_epp : 1; + UI32_T steer_applied : 1; + UI32_T nvo3_ip_tnl_decap_prop_ttl : 1; + UI32_T nvo3_mpls_uhp_prop_ttl : 1; + UI32_T ecn : 2; + UI32_T igr_fab_port_grp : 1; + UI32_T redir : 1; + UI32_T excpt_code_mir_bmap : 8; + UI32_T cp_to_cpu_bmap_w0 : 1; + /* CLX DWORD 3 */ + UI32_T cp_to_cpu_bmap_w1 : 7; + UI32_T egr_phy_port :12; + UI32_T src_supp_pnd : 1; + UI32_T mc_vid_ctl : 3; + UI32_T mc_vid_1st_w0 : 9; + /* CLX DWORD 4 */ + UI32_T mc_vid_1st_w1 : 3; + UI32_T mc_vid_2nd :12; + UI32_T mc_decr_ttl : 1; + UI32_T mc_is_routed : 1; + UI32_T mc_mel_vld : 1; + UI32_T mc_cp_idx :13; + UI32_T exp_dscp_mrkd : 1; +} HAL_LIGHTNING_PKT_ETMH_ETH_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T decap_act : 3; + UI32_T igr_l2_vid_num : 2; + UI32_T nvo3_encap_idx :14; + UI32_T mpls_pw_cw_vld : 1; + UI32_T hit_idx_w0 :12; + /* CLX DWORD 1 */ + UI32_T hit_idx_w1 : 7; + UI32_T nvo3_adj_idx : 8; + UI32_T seg_vmid_w0 :17; + /* CLX DWORD 2 */ + UI32_T seg_vmid_w1 : 7; + UI32_T : 1; + UI32_T l2_sa_lrn_en_hw_cvs : 1; + UI32_T l2_sa_lrn_en_hw : 1; + UI32_T vid_ctl : 3; + UI32_T vid_1st :12; + UI32_T vid_2nd_w0 : 7; + /* CLX DWORD 3 */ + UI32_T vid_2nd_w1 : 5; + UI32_T flw_lbl :10; + UI32_T rewr_idx_ctl : 2; + UI32_T rewr_idx_0 :13; + UI32_T rewr_idx_1_w0 : 2; + /* CLX DWORD 4 */ + UI32_T rewr_idx_1_w1 :11; + UI32_T mrk_pcp_dei_en : 1; + UI32_T mrk_pcp_val : 3; + UI32_T mrk_dei_val : 1; + UI32_T ts_0_7 : 8; + UI32_T ts_8_15 : 8; +} HAL_LIGHTNING_PKT_PPH_L2_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T decap_act : 3; + UI32_T igr_l2_vid_num : 2; + UI32_T nvo3_encap_idx :14; + UI32_T mpls_pw_cw_vld : 1; + UI32_T hit_idx_w0 :12; + /* CLX DWORD 1 */ + UI32_T hit_idx_w1 : 7; + UI32_T nvo3_adj_idx : 8; + UI32_T seg_vmid_w0 :17; + /* CLX DWORD 2 */ + UI32_T seg_vmid_w1 : 7; + UI32_T : 1; + UI32_T rpf_pnd : 1; + UI32_T adj_idx :18; + UI32_T is_mc : 1; + UI32_T decr_ttl : 1; + UI32_T decap_prop_ttl : 1; + UI32_T mrk_dscp_en : 1; + UI32_T mrk_dscp_val_w0 : 1; + /* CLX DWORD 3 */ + UI32_T mrk_dscp_val_w1 : 5; + UI32_T flw_lbl :10; + UI32_T rewr_idx_ctl : 2; + UI32_T rewr_idx_0 :13; + UI32_T rewr_idx_1_w0 : 2; + /* CLX DWORD 4 */ + UI32_T rewr_idx_1_w1 :11; + UI32_T mrk_pcp_dei_en : 1; + UI32_T mrk_pcp_val : 3; + UI32_T mrk_dei_val : 1; + UI32_T ts_0_7 : 8; + UI32_T ts_8_15 : 8; +} HAL_LIGHTNING_PKT_PPH_L3UC_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T decap_act : 3; + UI32_T igr_l2_vid_num : 2; + UI32_T nvo3_encap_idx :14; + UI32_T mpls_pw_cw_vld : 1; + UI32_T hit_idx_w0 :12; + /* CLX DWORD 1 */ + UI32_T hit_idx_w1 : 7; + UI32_T nvo3_adj_idx : 8; + UI32_T vid_1st :12; + UI32_T vid_2nd_w0 : 5; + /* CLX DWORD 2 */ + UI32_T vid_2nd_w1 : 7; + UI32_T :15; + UI32_T l2_sa_lrn_en_hw_cvs : 1; + UI32_T l2_sa_lrn_en_hw : 1; + UI32_T vid_ctl : 3; + UI32_T is_mc : 1; + UI32_T : 1; + UI32_T decap_prop_ttl : 1; + UI32_T mrk_dscp_en : 1; + UI32_T mrk_dscp_val_w0 : 1; + /* CLX DWORD 3 */ + UI32_T mrk_dscp_val_w1 : 5; + UI32_T flw_lbl :10; + UI32_T rewr_idx_ctl : 2; + UI32_T rewr_idx_0 :13; + UI32_T rewr_idx_1_w0 : 2; + /* CLX DWORD 4 */ + UI32_T rewr_idx_1_w1 :11; + UI32_T mrk_pcp_dei_en : 1; + UI32_T mrk_pcp_val : 3; + UI32_T mrk_dei_val : 1; + UI32_T ts_0_7 : 8; + UI32_T ts_8_15 : 8; +} HAL_LIGHTNING_PKT_PPH_L3MC_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T decap_act : 3; + UI32_T igr_l2_vid_num : 2; + UI32_T nvo3_encap_idx :14; + UI32_T : 1; + UI32_T hit_idx_w0 :12; + /* CLX DWORD 1 */ + UI32_T hit_idx_w1 : 7; + UI32_T nvo3_adj_idx : 8; + UI32_T seg_vmid_w0 :17; + /* CLX DWORD 2 */ + UI32_T seg_vmid_w1 : 7; + UI32_T : 2; + UI32_T adj_idx :18; + UI32_T : 1; + UI32_T decr_ttl : 1; + UI32_T decap_prop_ttl : 1; + UI32_T mrk_exp_en : 1; + UI32_T : 1; + /* CLX DWORD 3 */ + UI32_T : 2; + UI32_T mrk_exp_val : 3; + UI32_T php_pop_keep_inner_qos : 1; + UI32_T :26; + /* CLX DWORD 4 */ + UI32_T :11; + UI32_T mrk_pcp_dei_en : 1; + UI32_T mrk_pcp_val : 3; + UI32_T mrk_dei_val : 1; + UI32_T ts_0_7 : 8; + UI32_T ts_8_15 : 8; +} HAL_LIGHTNING_PKT_PPH_L25_T; + +#elif defined(CLX_EN_LITTLE_ENDIAN) + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T hsh_val_w0 : 8; + UI32_T igr_phy_port :12; + UI32_T trig : 1; + UI32_T srv : 3; + UI32_T color : 2; + UI32_T tc : 4; + UI32_T typ : 2; + /* CLX DWORD 1 */ + UI32_T src_idx :15; + UI32_T dst_idx :15; + UI32_T hsh_val_w1 : 2; + /* CLX DWORD 2 */ + UI32_T igr_fab_port_grp : 1; + UI32_T skip_ipp : 1; + UI32_T : 2; + UI32_T src_supp_tag : 5; + UI32_T lag_epoch : 1; + UI32_T store_and_forward : 1; + UI32_T ecn : 2; + UI32_T : 2; + UI32_T steer_applied : 1; + UI32_T skip_epp : 1; + UI32_T nvo3_mgid_is_transit : 1; + UI32_T intf_fdid :14; + /* CLX DWORD 3 */ + UI32_T :32; + /* CLX DWORD 4 */ + UI32_T exp_dscp_mrkd : 1; + UI32_T chip_igr_lag_port : 8; + UI32_T fab_stacking_supp : 2; + UI32_T had_uturn : 1; + UI32_T :20; +} HAL_LIGHTNING_PKT_ITMH_FAB_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T hsh_val_w0 : 8; + UI32_T igr_phy_port :12; + UI32_T trig : 1; + UI32_T srv : 3; + UI32_T color : 2; + UI32_T tc : 4; + UI32_T typ : 2; + /* CLX DWORD 1 */ + UI32_T src_idx :15; + UI32_T dst_idx :15; + UI32_T hsh_val_w1 : 2; + /* CLX DWORD 2 */ + UI32_T igr_fab_port_grp : 1; + UI32_T skip_ipp : 1; + UI32_T fab_one_arm_rte : 1; + UI32_T one_arm_rte_srv_fdid : 1; + UI32_T src_supp_tag : 5; + UI32_T lag_epoch : 1; + UI32_T store_and_forward : 1; + UI32_T ecn : 2; + UI32_T nvo3_mpls_uhp_prop_ttl : 1; + UI32_T nvo3_ip_tnl_decap_prop_ttl : 1; + UI32_T steer_applied : 1; + UI32_T skip_epp : 1; + UI32_T nvo3_mgid_is_transit : 1; + UI32_T intf_fdid :14; + /* CLX DWORD 3 */ + UI32_T nvo3_src_supp_tag_w0 : 1; + UI32_T nvo3_intf :14; + UI32_T nvo3_mgid :15; + UI32_T : 2; + /* CLX DWORD 4 */ + UI32_T cp_to_cpu_bmap :16; + UI32_T cp_to_cpu_code : 4; + UI32_T mir_bmap : 8; + UI32_T nvo3_src_supp_tag_w1 : 4; +} HAL_LIGHTNING_PKT_ITMH_ETH_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T hsh_val_w0 : 8; + UI32_T igr_phy_port :12; + UI32_T trig : 1; + UI32_T srv : 3; + UI32_T color : 2; + UI32_T tc : 4; + UI32_T typ : 2; + /* CLX DWORD 1 */ + UI32_T src_idx :15; + UI32_T dst_idx :15; + UI32_T hsh_val_w1 : 2; + /* CLX DWORD 2 */ + UI32_T : 2; + UI32_T fab_one_arm_rte : 1; + UI32_T one_arm_rte_srv_fdid : 1; + UI32_T src_supp_tag : 5; + UI32_T lag_epoch : 1; + UI32_T store_and_forward : 1; + UI32_T ecn : 2; + UI32_T nvo3_mpls_uhp_prop_ttl : 1; + UI32_T nvo3_ip_tnl_decap_prop_ttl : 1; + UI32_T steer_applied : 1; + UI32_T skip_epp : 1; + UI32_T nvo3_mgid_is_transit : 1; + UI32_T intf_fdid :14; + /* CLX DWORD 3 */ + UI32_T :32; + /* CLX DWORD 4 */ + UI32_T exp_dscp_mrkd : 1; + UI32_T excpt_code : 8; + UI32_T : 2; + UI32_T had_uturn : 1; + UI32_T :20; +} HAL_LIGHTNING_PKT_ETMH_FAB_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T hsh_val_w0 : 8; + UI32_T igr_phy_port :12; + UI32_T trig : 1; + UI32_T srv : 3; + UI32_T color : 2; + UI32_T tc : 4; + UI32_T typ : 2; + /* CLX DWORD 1 */ + UI32_T src_idx :15; + UI32_T dst_idx :15; + UI32_T hsh_val_w1 : 2; + /* CLX DWORD 2 */ + UI32_T cp_to_cpu_bmap_w0 : 1; + UI32_T excpt_code_mir_bmap : 8; + UI32_T redir : 1; + UI32_T igr_fab_port_grp : 1; + UI32_T ecn : 2; + UI32_T nvo3_mpls_uhp_prop_ttl : 1; + UI32_T nvo3_ip_tnl_decap_prop_ttl : 1; + UI32_T steer_applied : 1; + UI32_T skip_epp : 1; + UI32_T nvo3_mgid_is_transit : 1; + UI32_T intf_fdid :14; + /* CLX DWORD 3 */ + UI32_T mc_vid_1st_w0 : 9; + UI32_T mc_vid_ctl : 3; + UI32_T src_supp_pnd : 1; + UI32_T egr_phy_port :12; + UI32_T cp_to_cpu_bmap_w1 : 7; + /* CLX DWORD 4 */ + UI32_T exp_dscp_mrkd : 1; + UI32_T mc_cp_idx :13; + UI32_T mc_mel_vld : 1; + UI32_T mc_is_routed : 1; + UI32_T mc_decr_ttl : 1; + UI32_T mc_vid_2nd :12; + UI32_T mc_vid_1st_w1 : 3; +} HAL_LIGHTNING_PKT_ETMH_ETH_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T hit_idx_w0 :12; + UI32_T mpls_pw_cw_vld : 1; + UI32_T nvo3_encap_idx :14; + UI32_T igr_l2_vid_num : 2; + UI32_T decap_act : 3; + /* CLX DWORD 1 */ + UI32_T seg_vmid_w0 :17; + UI32_T nvo3_adj_idx : 8; + UI32_T hit_idx_w1 : 7; + /* CLX DWORD 2 */ + UI32_T vid_2nd_w0 : 7; + UI32_T vid_1st :12; + UI32_T vid_ctl : 3; + UI32_T l2_sa_lrn_en_hw : 1; + UI32_T l2_sa_lrn_en_hw_cvs : 1; + UI32_T : 1; + UI32_T seg_vmid_w1 : 7; + /* CLX DWORD 3 */ + UI32_T rewr_idx_1_w0 : 2; + UI32_T rewr_idx_0 :13; + UI32_T rewr_idx_ctl : 2; + UI32_T flw_lbl :10; + UI32_T vid_2nd_w1 : 5; + /* CLX DWORD 4 */ + UI32_T ts_8_15 : 8; + UI32_T ts_0_7 : 8; + UI32_T mrk_dei_val : 1; + UI32_T mrk_pcp_val : 3; + UI32_T mrk_pcp_dei_en : 1; + UI32_T rewr_idx_1_w1 :11; +} HAL_LIGHTNING_PKT_PPH_L2_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T hit_idx_w0 :12; + UI32_T mpls_pw_cw_vld : 1; + UI32_T nvo3_encap_idx :14; + UI32_T igr_l2_vid_num : 2; + UI32_T decap_act : 3; + /* CLX DWORD 1 */ + UI32_T seg_vmid_w0 :17; + UI32_T nvo3_adj_idx : 8; + UI32_T hit_idx_w1 : 7; + /* CLX DWORD 2 */ + UI32_T mrk_dscp_val_w0 : 1; + UI32_T mrk_dscp_en : 1; + UI32_T decap_prop_ttl : 1; + UI32_T decr_ttl : 1; + UI32_T is_mc : 1; + UI32_T adj_idx :18; + UI32_T rpf_pnd : 1; + UI32_T : 1; + UI32_T seg_vmid_w1 : 7; + /* CLX DWORD 3 */ + UI32_T rewr_idx_1_w0 : 2; + UI32_T rewr_idx_0 :13; + UI32_T rewr_idx_ctl : 2; + UI32_T flw_lbl :10; + UI32_T mrk_dscp_val_w1 : 5; + /* CLX DWORD 4 */ + UI32_T ts_8_15 : 8; + UI32_T ts_0_7 : 8; + UI32_T mrk_dei_val : 1; + UI32_T mrk_pcp_val : 3; + UI32_T mrk_pcp_dei_en : 1; + UI32_T rewr_idx_1_w1 :11; +} HAL_LIGHTNING_PKT_PPH_L3UC_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T hit_idx_w0 :12; + UI32_T mpls_pw_cw_vld : 1; + UI32_T nvo3_encap_idx :14; + UI32_T igr_l2_vid_num : 2; + UI32_T decap_act : 3; + /* CLX DWORD 1 */ + UI32_T vid_2nd_w0 : 5; + UI32_T vid_1st :12; + UI32_T nvo3_adj_idx : 8; + UI32_T hit_idx_w1 : 7; + /* CLX DWORD 2 */ + UI32_T mrk_dscp_val_w0 : 1; + UI32_T mrk_dscp_en : 1; + UI32_T decap_prop_ttl : 1; + UI32_T : 1; + UI32_T is_mc : 1; + UI32_T vid_ctl : 3; + UI32_T l2_sa_lrn_en_hw : 1; + UI32_T l2_sa_lrn_en_hw_cvs : 1; + UI32_T :15; + UI32_T vid_2nd_w1 : 7; + /* CLX DWORD 3 */ + UI32_T rewr_idx_1_w0 : 2; + UI32_T rewr_idx_0 :13; + UI32_T rewr_idx_ctl : 2; + UI32_T flw_lbl :10; + UI32_T mrk_dscp_val_w1 : 5; + /* CLX DWORD 4 */ + UI32_T ts_8_15 : 8; + UI32_T ts_0_7 : 8; + UI32_T mrk_dei_val : 1; + UI32_T mrk_pcp_val : 3; + UI32_T mrk_pcp_dei_en : 1; + UI32_T rewr_idx_1_w1 :11; +} HAL_LIGHTNING_PKT_PPH_L3MC_T; + +typedef struct +{ + /* CLX DWORD 0 */ + UI32_T hit_idx_w0 :12; + UI32_T : 1; + UI32_T nvo3_encap_idx :14; + UI32_T igr_l2_vid_num : 2; + UI32_T decap_act : 3; + /* CLX DWORD 1 */ + UI32_T seg_vmid_w0 :17; + UI32_T nvo3_adj_idx : 8; + UI32_T hit_idx_w1 : 7; + /* CLX DWORD 2 */ + UI32_T : 1; + UI32_T mrk_exp_en : 1; + UI32_T decap_prop_ttl : 1; + UI32_T decr_ttl : 1; + UI32_T : 1; + UI32_T adj_idx :18; + UI32_T : 2; + UI32_T seg_vmid_w1 : 7; + /* CLX DWORD 3 */ + UI32_T :26; + UI32_T php_pop_keep_inner_qos : 1; + UI32_T mrk_exp_val : 3; + UI32_T : 2; + /* CLX DWORD 4 */ + UI32_T ts_8_15 : 8; + UI32_T ts_0_7 : 8; + UI32_T mrk_dei_val : 1; + UI32_T mrk_pcp_val : 3; + UI32_T mrk_pcp_dei_en : 1; + UI32_T :11; +} HAL_LIGHTNING_PKT_PPH_L25_T; + +#else +#error "Host GPD endian is not defined!!\n" +#endif + +#if defined(CLX_EN_BIG_ENDIAN) + +/* RX GPD STRUCTURE */ +typedef struct +{ + UI32_T data_buf_addr_lo; + UI32_T data_buf_addr_hi; + UI32_T chksum : 16; + UI32_T ioc : 1; + UI32_T : 1; + UI32_T avbl_buf_len : 14; + UI32_T : 32; + + union + { + HAL_LIGHTNING_PKT_ITMH_FAB_T itmh_fab; + HAL_LIGHTNING_PKT_ITMH_ETH_T itmh_eth; + HAL_LIGHTNING_PKT_ETMH_FAB_T etmh_fab; + HAL_LIGHTNING_PKT_ETMH_ETH_T etmh_eth; + }; + union + { + HAL_LIGHTNING_PKT_PPH_L2_T pph_l2; + HAL_LIGHTNING_PKT_PPH_L3UC_T pph_l3uc; + HAL_LIGHTNING_PKT_PPH_L3MC_T pph_l3mc; + HAL_LIGHTNING_PKT_PPH_L25_T pph_l25; + }; + + UI32_T ts_16_23 : 8; + UI32_T ts_24_31 : 8; + UI32_T : 6; + UI32_T ts_32_33 : 2; + UI32_T : 8; + UI32_T hwo : 1; + UI32_T ch : 1; + UI32_T trn : 1; + UI32_T ecce : 1; + UI32_T errf : 1; + UI32_T : 5; + UI32_T queue : 6; + UI32_T : 2; + UI32_T cnsm_buf_len : 14; + +} HAL_LIGHTNING_PKT_RX_GPD_T; + +/* TX GPD STRUCTURE */ +typedef struct +{ + UI32_T data_buf_addr_lo; + UI32_T data_buf_addr_hi; + UI32_T chksum : 16; + UI32_T ioc : 1; + UI32_T : 1; + UI32_T data_buf_size : 14; + UI32_T : 32; + + union + { + HAL_LIGHTNING_PKT_ITMH_FAB_T itmh_fab; + HAL_LIGHTNING_PKT_ITMH_ETH_T itmh_eth; + HAL_LIGHTNING_PKT_ETMH_FAB_T etmh_fab; + HAL_LIGHTNING_PKT_ETMH_ETH_T etmh_eth; + }; + union + { + HAL_LIGHTNING_PKT_PPH_L2_T pph_l2; + HAL_LIGHTNING_PKT_PPH_L3UC_T pph_l3uc; + HAL_LIGHTNING_PKT_PPH_L3MC_T pph_l3mc; + HAL_LIGHTNING_PKT_PPH_L25_T pph_l25; + }; + + UI32_T : 16; + UI32_T ptp_hdr : 16; + UI32_T hwo : 1; + UI32_T ch : 1; + UI32_T : 1; + UI32_T ecce : 1; + UI32_T crce : 1; + UI32_T : 3; + UI32_T cos : 3; + UI32_T phc : 1; /* PTP Header Control */ + UI32_T ipc : 3; /* Ingress Plane Control */ + UI32_T prg : 1; /* Purge */ + UI32_T : 2; + UI32_T pkt_len : 14; /* Total packet length */ + +} HAL_LIGHTNING_PKT_TX_GPD_T; + +#elif defined(CLX_EN_LITTLE_ENDIAN) + +/* RX GPD STRUCTURE */ +typedef struct +{ + UI32_T data_buf_addr_lo; + UI32_T data_buf_addr_hi; + UI32_T avbl_buf_len : 14; + UI32_T : 1; + UI32_T ioc : 1; + UI32_T chksum : 16; + UI32_T : 32; + + union + { + HAL_LIGHTNING_PKT_ITMH_FAB_T itmh_fab; + HAL_LIGHTNING_PKT_ITMH_ETH_T itmh_eth; + HAL_LIGHTNING_PKT_ETMH_FAB_T etmh_fab; + HAL_LIGHTNING_PKT_ETMH_ETH_T etmh_eth; + }; + union + { + HAL_LIGHTNING_PKT_PPH_L2_T pph_l2; + HAL_LIGHTNING_PKT_PPH_L3UC_T pph_l3uc; + HAL_LIGHTNING_PKT_PPH_L3MC_T pph_l3mc; + HAL_LIGHTNING_PKT_PPH_L25_T pph_l25; + }; + + UI32_T : 8; + UI32_T ts_32_33 : 2; + UI32_T : 6; + UI32_T ts_24_31 : 8; + UI32_T ts_16_23 : 8; + UI32_T cnsm_buf_len : 14; + UI32_T : 2; + UI32_T queue : 6; + UI32_T : 5; + UI32_T errf : 1; + UI32_T ecce : 1; + UI32_T trn : 1; + UI32_T ch : 1; + UI32_T hwo : 1; + +} HAL_LIGHTNING_PKT_RX_GPD_T; + +/* TX GPD STRUCTURE */ +typedef struct +{ + UI32_T data_buf_addr_lo; + UI32_T data_buf_addr_hi; + UI32_T data_buf_size : 14; + UI32_T : 1; + UI32_T ioc : 1; + UI32_T chksum : 16; + UI32_T : 32; + + union + { + HAL_LIGHTNING_PKT_ITMH_FAB_T itmh_fab; + HAL_LIGHTNING_PKT_ITMH_ETH_T itmh_eth; + HAL_LIGHTNING_PKT_ETMH_FAB_T etmh_fab; + HAL_LIGHTNING_PKT_ETMH_ETH_T etmh_eth; + }; + union + { + HAL_LIGHTNING_PKT_PPH_L2_T pph_l2; + HAL_LIGHTNING_PKT_PPH_L3UC_T pph_l3uc; + HAL_LIGHTNING_PKT_PPH_L3MC_T pph_l3mc; + HAL_LIGHTNING_PKT_PPH_L25_T pph_l25; + }; + + UI32_T ptp_hdr : 16; + UI32_T : 16; + UI32_T pkt_len : 14; /* Total packet length */ + UI32_T : 2; + UI32_T prg : 1; /* Purge */ + UI32_T ipc : 3; /* Ingress Plane Control */ + UI32_T phc : 1; /* PTP Header Control */ + UI32_T cos : 3; + UI32_T : 3; + UI32_T crce : 1; + UI32_T ecce : 1; + UI32_T : 1; + UI32_T ch : 1; + UI32_T hwo : 1; +} HAL_LIGHTNING_PKT_TX_GPD_T; + +#else +#error "Host GPD endian is not defined\n" +#endif + +/* ----------------------------------------------------------------------------------- PP Type */ +typedef enum +{ + HAL_LIGHTNING_PKT_TMH_TYPE_ITMH_ETH = 0, + HAL_LIGHTNING_PKT_TMH_TYPE_ITMH_FAB, + HAL_LIGHTNING_PKT_TMH_TYPE_ETMH_FAB, + HAL_LIGHTNING_PKT_TMH_TYPE_ETMH_ETH, + HAL_LIGHTNING_PKT_TMH_TYPE_LAST + +} HAL_LIGHTNING_PKT_TMH_TYPE_T; + +typedef enum +{ + HAL_LIGHTNING_PKT_TMH_SRV_L2 = 0, + HAL_LIGHTNING_PKT_TMH_SRV_L25_MPLS, + HAL_LIGHTNING_PKT_TMH_SRV_L3, + HAL_LIGHTNING_PKT_TMH_SRV_EGR, /* L3 downgrade L2 */ + HAL_LIGHTNING_PKT_TMH_SRV_L25_NSH, + HAL_LIGHTNING_PKT_TMH_SRV_L25_TRILL, + HAL_LIGHTNING_PKT_TMH_SRV_LAST + +} HAL_LIGHTNING_PKT_TMH_SRV_T; + +typedef enum +{ + HAL_LIGHTNING_PKT_TMH_DECAP_NONE = 0, + HAL_LIGHTNING_PKT_TMH_DECAP_1_MPLS_LABEL, + HAL_LIGHTNING_PKT_TMH_DECAP_2_MPLS_LABEL, + HAL_LIGHTNING_PKT_TMH_DECAP_3_MPLS_LABEL, + HAL_LIGHTNING_PKT_TMH_DECAP_4_MPLS_LABEL, + HAL_LIGHTNING_PKT_TMH_DECAP_IP_TRILL_NSH, + HAL_LIGHTNING_PKT_TMH_DECAP_LAST + +} HAL_LIGHTNING_PKT_TMH_DECAP_T; + +typedef struct +{ + union + { + HAL_LIGHTNING_PKT_ITMH_FAB_T itmh_fab; + HAL_LIGHTNING_PKT_ITMH_ETH_T itmh_eth; + HAL_LIGHTNING_PKT_ETMH_FAB_T etmh_fab; + HAL_LIGHTNING_PKT_ETMH_ETH_T etmh_eth; + }; +} HAL_LIGHTNING_PKT_TMH_T; + +typedef struct +{ + union + { + HAL_LIGHTNING_PKT_PPH_L2_T pph_l2; + HAL_LIGHTNING_PKT_PPH_L3UC_T pph_l3uc; + HAL_LIGHTNING_PKT_PPH_L3MC_T pph_l3mc; + HAL_LIGHTNING_PKT_PPH_L25_T pph_l25; + }; +} HAL_LIGHTNING_PKT_PPH_T; + +/* ----------------------------------------------------------------------------------- Reg Type */ +typedef enum +{ + HAL_LIGHTNING_PKT_L2_ISR_RCH0 = (0x1UL << 0), + HAL_LIGHTNING_PKT_L2_ISR_RCH1 = (0x1UL << 1), + HAL_LIGHTNING_PKT_L2_ISR_RCH2 = (0x1UL << 2), + HAL_LIGHTNING_PKT_L2_ISR_RCH3 = (0x1UL << 3), + HAL_LIGHTNING_PKT_L2_ISR_TCH0 = (0x1UL << 4), + HAL_LIGHTNING_PKT_L2_ISR_TCH1 = (0x1UL << 5), + HAL_LIGHTNING_PKT_L2_ISR_TCH2 = (0x1UL << 6), + HAL_LIGHTNING_PKT_L2_ISR_TCH3 = (0x1UL << 7), + HAL_LIGHTNING_PKT_L2_ISR_RX_QID_MAP_ERR = (0x1UL << 8), + HAL_LIGHTNING_PKT_L2_ISR_RX_FRAME_ERR = (0x1UL << 9) + +} HAL_LIGHTNING_PKT_L2_ISR_T; + +typedef enum +{ + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_HWO_ERROR = (0x1UL << 0), /* Tx GPD.hwo = 0 */ + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_CHKSM_ERROR = (0x1UL << 1), /* Tx GPD.chksm is error */ + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_NO_OVFL_ERROR = (0x1UL << 2), /* S/W push too much GPD */ + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_DMA_READ_ERROR = (0x1UL << 3), /* AXI Rd Error when do GPD read */ + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_BUF_SIZE_ERROR = (0x1UL << 4), /* Tx GPD.data_buf_size = 0 */ + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_RUNT_ERROR = (0x1UL << 5), /* Tx GPD.pkt_len < 64 */ + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_OVSZ_ERROR = (0x1UL << 6), /* Tx GPD.pkt_len = 9217 */ + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_LEN_MISMATCH_ERROR = (0x1UL << 7), /* Tx GPD.pkt_len != sum of data_buf_size */ + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_PKTPL_DMA_READ_ERROR = (0x1UL << 8), /* AXI Rd Error when do Payload read */ + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_COS_ERROR = (0x1UL << 9), /* Tx GPD.cos is not match cos_to_tch_map */ + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_GPD_GT255_ERROR = (0x1UL << 10), /* Multi-GPD packet's GPD# > 255 */ + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_PFC = (0x1UL << 11), /* */ + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_CREDIT_UDFL_ERROR = (0x1UL << 12), /* Credit Underflow (count down to 0) */ + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_DMA_WRITE_ERROR = (0x1UL << 13), /* AXI Wr Error (GPD Write-Back) */ + HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_STOP_CMD_CPLT = (0x1UL << 14) + +} HAL_LIGHTNING_PKT_TX_CHANNEL_L2_ISR_T; + +typedef enum +{ + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_LOW = (0x1UL << 0), /* Rx GPD.avbl_gpd_num < threshold */ + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_EMPTY = (0x1UL << 1), /* Rx GPD.avbl_gpd_num = 0 */ + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_AVAIL_GPD_ERROR = (0x1UL << 2), /* Rx GPD.hwo = 0 */ + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_GPD_CHKSM_ERROR = (0x1UL << 3), /* Rx GPD.chksm is error */ + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_DMA_READ_ERROR = (0x1UL << 4), /* DMAR error occurs in PCIE */ + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_DMA_WRITE_ERROR = (0x1UL << 5), /* DMAW error occurs in PCIE */ + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_STOP_CMD_CPLT = (0x1UL << 6), /* Stop Completion Acknowledge */ + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_GPD_GT255_ERROR = (0x1UL << 7), /* Multi-GPD packet's GPD# > 255 */ + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_TOD_UNINIT = (0x1UL << 8), /* */ + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_PKT_ERROR_DROP = (0x1UL << 9), /* */ + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_UDSZ_DROP = (0x1UL << 10), /* */ + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_OVSZ_DROP = (0x1UL << 11), /* */ + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_CMDQ_OVF_DROP = (0x1UL << 12), /* */ + HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_FIFO_OVF_DROP = (0x1UL << 13) + +} HAL_LIGHTNING_PKT_RX_CHANNEL_L2_ISR_T; + +typedef enum +{ + HAL_LIGHTNING_PKT_TX_CHANNEL_CFG_IOC = (0x1UL << 0), + HAL_LIGHTNING_PKT_TX_CHANNEL_CFG_CHKSUM = (0x1UL << 1), + HAL_LIGHTNING_PKT_TX_CHANNEL_CFG_PFC = (0x1UL << 2), + HAL_LIGHTNING_PKT_TX_CHANNEL_CFG_PKT_LEN_CHK = (0x1UL << 3), + HAL_LIGHTNING_PKT_TX_CHANNEL_CFG_EARLY_DONE_IRQ = (0x1UL << 4), + HAL_LIGHTNING_PKT_TX_CHANNEL_CFG_CHK_COS = (0x1UL << 5), + HAL_LIGHTNING_PKT_TX_CHANNEL_CFG_ADV_GPD_WRBK = (0x1UL << 6), + HAL_LIGHTNING_PKT_TX_CHANNEL_CFG_GPD_WRBK_FULL_PKT_LEN = (0x1UL << 7), + HAL_LIGHTNING_PKT_TX_CHANNEL_CFG_LAST = (0x1UL << 8) + +} HAL_LIGHTNING_PKT_TX_CHANNEL_CFG_T; + +typedef enum +{ + HAL_LIGHTNING_PKT_RX_CHANNEL_CFG_IOC = (0x1UL << 0), + HAL_LIGHTNING_PKT_RX_CHANNEL_CFG_CHKSUM = (0x1UL << 1), + HAL_LIGHTNING_PKT_RX_CHANNEL_CFG_LAST = (0x1UL << 2) + +} HAL_LIGHTNING_PKT_RX_CHANNEL_CFG_T; + +/* ----------------------------------------------------------------------------------- Tx */ +typedef enum +{ + HAL_LIGHTNING_PKT_TX_CHANNEL_0 = 0, + HAL_LIGHTNING_PKT_TX_CHANNEL_1, + HAL_LIGHTNING_PKT_TX_CHANNEL_2, + HAL_LIGHTNING_PKT_TX_CHANNEL_3, + HAL_LIGHTNING_PKT_TX_CHANNEL_LAST + +} HAL_LIGHTNING_PKT_TX_CHANNEL_T; + +typedef void +(*HAL_LIGHTNING_PKT_TX_FUNC_T)( + const UI32_T unit, + const void *ptr_sw_gpd, /* SW-GPD to be processed */ + void *ptr_coockie); /* Private data of SDK */ + +typedef struct HAL_LIGHTNING_PKT_TX_SW_GPD_S +{ + HAL_LIGHTNING_PKT_TX_FUNC_T callback; /* (unit, ptr_sw_gpd, ptr_cookie) */ + void *ptr_cookie; /* Pointer of CLX_PKT_TX_PKT_T */ + HAL_LIGHTNING_PKT_TX_GPD_T tx_gpd; + UI32_T gpd_num; + struct HAL_LIGHTNING_PKT_TX_SW_GPD_S *ptr_next; + +#if defined (CLX_EN_NETIF) + UI32_T channel; /* For counter */ +#endif + +} HAL_LIGHTNING_PKT_TX_SW_GPD_T; + +typedef struct +{ + UI32_T send_ok; + UI32_T gpd_empty; + UI32_T poll_timeout; + + /* queue */ + UI32_T enque_ok; + UI32_T enque_retry; + + /* event */ + UI32_T trig_event; + + /* normal interrupt */ + UI32_T tx_done; + + /* abnormal interrupt */ + UI32_T gpd_hwo_err; /* bit-0 */ + UI32_T gpd_chksm_err; /* bit-1 */ + UI32_T gpd_no_ovfl_err; /* bit-2 */ + UI32_T gpd_dma_read_err; /* bit-3 */ + UI32_T buf_size_err; /* bit-4 */ + UI32_T runt_err; /* bit-5 */ + UI32_T ovsz_err; /* bit-6 */ + UI32_T len_mismatch_err; /* bit-7 */ + UI32_T pktpl_dma_read_err; /* bit-8 */ + UI32_T cos_err; /* bit-9 */ + UI32_T gpd_gt255_err; /* bit-10 */ + UI32_T pfc; /* bit-11 */ + UI32_T credit_udfl_err; /* bit-12 */ + UI32_T dma_write_err; /* bit-13 */ + UI32_T sw_issue_stop; /* bit-14 */ + + /* others */ + UI32_T err_recover; + UI32_T ecc_err; + +} HAL_LIGHTNING_PKT_TX_CHANNEL_CNT_T; + +typedef struct +{ + HAL_LIGHTNING_PKT_TX_CHANNEL_CNT_T channel[HAL_LIGHTNING_PKT_TX_CHANNEL_LAST]; + UI32_T invoke_gpd_callback; + UI32_T no_memory; + + /* queue */ + UI32_T deque_ok; + UI32_T deque_fail; + + /* event */ + UI32_T wait_event; + +} HAL_LIGHTNING_PKT_TX_CNT_T; + +/* ----------------------------------------------------------------------------------- Rx */ +typedef enum +{ + HAL_LIGHTNING_PKT_RX_CHANNEL_0 = 0, + HAL_LIGHTNING_PKT_RX_CHANNEL_1, + HAL_LIGHTNING_PKT_RX_CHANNEL_2, + HAL_LIGHTNING_PKT_RX_CHANNEL_3, + HAL_LIGHTNING_PKT_RX_CHANNEL_LAST +} HAL_LIGHTNING_PKT_RX_CHANNEL_T; + +typedef enum +{ + HAL_LIGHTNING_PKT_C_NEXT = 0, /* callback continuous */ + HAL_LIGHTNING_PKT_C_STOP = 1, + HAL_LIGHTNING_PKT_C_OTHERS = 2 +} HAL_LIGHTNING_PKT_CALLBACK_NO_T; + +typedef enum +{ + HAL_LIGHTNING_PKT_RX_CALLBACK_ACTION_INSERT = 0, + HAL_LIGHTNING_PKT_RX_CALLBACK_ACTION_APPEND = 1, + HAL_LIGHTNING_PKT_RX_CALLBACK_ACTION_DELETE = 2, + HAL_LIGHTNING_PKT_RX_CALLBACK_ACTION_DELETE_ALL = 3 +} HAL_LIGHTNING_PKT_RX_CALLBACK_ACTION_T; + +typedef HAL_LIGHTNING_PKT_CALLBACK_NO_T +(*HAL_LIGHTNING_PKT_RX_FUNC_T)( + const UI32_T unit, + const void *ptr_sw_gpd, /* SW-GPD to be processed */ + void *ptr_cookie); /* Private data of SDK */ + +typedef struct HAL_LIGHTNING_PKT_RX_CALLBACK_S +{ + HAL_LIGHTNING_PKT_RX_FUNC_T callback; /* (unit, ptr_sw_gpd, ptr_cookie) */ + void *ptr_cookie; + struct HAL_LIGHTNING_PKT_RX_CALLBACK_S *ptr_next; +} HAL_LIGHTNING_PKT_RX_CALLBACK_T; + +typedef struct HAL_LIGHTNING_PKT_RX_SW_GPD_S +{ + BOOL_T rx_complete; /* FALSE when PDMA error occurs */ + HAL_LIGHTNING_PKT_RX_GPD_T rx_gpd; + struct HAL_LIGHTNING_PKT_RX_SW_GPD_S *ptr_next; + +#if defined (CLX_EN_NETIF) + void *ptr_cookie; /* Pointer of virt-addr */ +#endif + +} HAL_LIGHTNING_PKT_RX_SW_GPD_T; + +typedef struct +{ + /* queue */ + UI32_T enque_ok; + UI32_T enque_retry; + UI32_T deque_ok; + UI32_T deque_fail; + + /* event */ + UI32_T trig_event; + + /* normal interrupt */ + UI32_T rx_done; + + /* abnormal interrupt */ + UI32_T avbl_gpd_low; /* bit-0 */ + UI32_T avbl_gpd_empty; /* bit-1 */ + UI32_T avbl_gpd_err; /* bit-2 */ + UI32_T gpd_chksm_err; /* bit-3 */ + UI32_T dma_read_err; /* bit-4 */ + UI32_T dma_write_err; /* bit-5 */ + UI32_T sw_issue_stop; /* bit-6 */ + UI32_T gpd_gt255_err; /* bit-7 */ + UI32_T tod_uninit; /* bit-8 */ + UI32_T pkt_err_drop; /* bit-9 */ + UI32_T udsz_drop; /* bit-10 */ + UI32_T ovsz_drop; /* bit-11 */ + UI32_T cmdq_ovf_drop; /* bit-12 */ + UI32_T fifo_ovf_drop; /* bit-13 */ + + /* others */ + UI32_T err_recover; + UI32_T ecc_err; + +#if defined (CLX_EN_NETIF) + /* it means that user doesn't create intf on that port */ + UI32_T netdev_miss; +#endif + + +} HAL_LIGHTNING_PKT_RX_CHANNEL_CNT_T; + +typedef struct +{ + HAL_LIGHTNING_PKT_RX_CHANNEL_CNT_T channel[HAL_LIGHTNING_PKT_RX_CHANNEL_LAST]; + UI32_T invoke_gpd_callback; + UI32_T no_memory; + + /* event */ + UI32_T wait_event; + +} HAL_LIGHTNING_PKT_RX_CNT_T; + +/* ----------------------------------------------------------------------------------- Reg */ +#if defined(CLX_EN_LITTLE_ENDIAN) + +typedef union +{ + UI32_T reg; + struct + { + UI32_T tch_axlen_cfg : 3; + UI32_T : 5; + UI32_T tch_axi_free_arvalid : 1; + UI32_T : 7; + UI32_T tch_arvalid_thrhold_cfg : 2; + UI32_T : 6; + UI32_T tch_rready_low_4_hdr : 1; + UI32_T tch_ios_crdt_add_en : 1; + UI32_T : 6; + } field; +} HAL_LIGHTNING_PKT_AXI_LEN_CFG_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T pdma_lbk_en : 1; + UI32_T : 3; + UI32_T pdma_lbk_plane : 2; + UI32_T : 2; + UI32_T pm_lbk_en : 1; + UI32_T : 7; + UI32_T pm_lbk_rqid : 6; + UI32_T : 2; + UI32_T : 8; + } field; +} HAL_LIGHTNING_PKT_LBK_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T pdma_lbk_rqid0 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid1 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid2 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid3 : 6; + UI32_T : 2; + } field; +} HAL_LIGHTNING_PKT_LBK_RQID0_3_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T pdma_lbk_rqid4 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid5 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid6 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid7 : 6; + UI32_T : 2; + } field; +} HAL_LIGHTNING_PKT_LBK_RQID4_7_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T cos_pfc_sts0 : 8; + UI32_T cos_pfc_sts1 : 8; + UI32_T cos_pfc_sts2 : 8; + UI32_T cos_pfc_sts3 : 8; + } field; +} HAL_LIGHTNING_PKT_COS_PFC_STS_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T pdma_ela_en : 1; + UI32_T : 7; + UI32_T pdma_ela_valid_sel : 8; + UI32_T : 8; + UI32_T : 8; + } field; +} HAL_LIGHTNING_PKT_ELA_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T pdma_ela_word0_sel : 8; + UI32_T pdma_ela_word1_sel : 8; + UI32_T pdma_ela_word2_sel : 8; + UI32_T pdma_ela_word3_sel : 8; + } field; +} HAL_LIGHTNING_PKT_ELA_SEL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T ingr_pln_ios_credit_base_size_lo : 8; + UI32_T ingr_pln_ios_credit_base_size_hi : 8; + UI32_T ingr_pln_ios_credit_set : 1; + UI32_T : 7; + UI32_T : 1; + UI32_T ingr_pln_full_pkt_mode : 1; + UI32_T : 6; + } field; +} HAL_LIGHTNING_PKT_IGR_PLN_CREDIT_CFG_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T ingr_pln_cur_ios_credit_lo : 8; + UI32_T ingr_pln_cur_ios_credit_hi : 8; + UI32_T ingr_pln_ios_credit_ovfl : 1; + UI32_T ingr_pln_ios_credit_udfl : 1; + UI32_T : 6; + UI32_T : 8; + } field; +} HAL_LIGHTNING_PKT_IGR_PLN_CREDIT_STS_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T ingr_pln_ios_credit_rdy_lo_bound : 8; + UI32_T ingr_pln_ios_credit_rdy_hi_bound : 8; + UI32_T : 8; + UI32_T : 8; + } field; +} HAL_LIGHTNING_PKT_IGR_PLN_CREDIT_THR_REG_T; + + +typedef union +{ + UI32_T reg; + struct + { + UI32_T rch_stomp_crc_en : 1; + UI32_T : 7; + UI32_T rch_crc_regen_en : 1; + UI32_T : 7; + UI32_T rch_pfc_fun_en : 1; + UI32_T : 7; + UI32_T : 8; + } field; +} HAL_LIGHTNING_PKT_RCH_STOMP_CRC_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T rch_ioc_en : 1; + UI32_T : 7; + UI32_T rch_chksm_en : 1; + UI32_T : 7; + UI32_T : 8; + UI32_T : 8; + } field; +} HAL_LIGHTNING_PKT_RCH_MISC_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T rch_gpd_pfc_lo : 8; + UI32_T rch_gpd_pfc_hi : 8; + UI32_T : 8; + UI32_T : 8; + } field; +} HAL_LIGHTNING_PKT_RCH_GPD_PFC_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T rch_fifo_pfc_lo_lo : 8; + UI32_T rch_fifo_pfc_lo_hi : 3; + UI32_T : 5; + UI32_T rch_fifo_pfc_hi_lo : 8; + UI32_T rch_fifo_pfc_hi_hi : 3; + UI32_T : 5; + } field; +} HAL_LIGHTNING_PKT_RCH_FIFO_PFC_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T rch_cmdq_pfc_lo : 5; + UI32_T : 3; + UI32_T rch_cmdq_pfc_hi : 5; + UI32_T : 3; + UI32_T : 8; + UI32_T : 8; + } field; +} HAL_LIGHTNING_PKT_RCH_CMDQ_PFC_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T rch_start : 1; + UI32_T rch_resume : 1; + UI32_T rch_stop : 1; + UI32_T : 5; + UI32_T : 8; + UI32_T rch_gpd_add_no_lo : 8; + UI32_T rch_gpd_add_no_hi : 8; + } field; +} HAL_LIGHTNING_PKT_RCH_CMD_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T rch_fifo_ovf_drop_cnt_clr : 1; + UI32_T rch_cmdq_ovf_drop_cnt_clr : 1; + UI32_T rch_ovsz_drop_cnt_clr : 1; + UI32_T rch_udsz_drop_cnt_clr : 1; + UI32_T rch_pkterr_drop_cnt_clr : 1; + UI32_T rch_flush_cnt_clr : 1; + UI32_T : 2; + UI32_T : 8; + UI32_T : 8; + UI32_T : 8; + } field; +} HAL_LIGHTNING_PKT_RCH_CNT_CLR_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T rch_active : 1; + UI32_T rch_avbl_gpd_pfc : 1; + UI32_T rch_fifo_pfc : 1; + UI32_T rch_cmdq_pfc : 1; + UI32_T rch_pfc : 1; + UI32_T : 3; + UI32_T : 8; + UI32_T rch_avbl_gpd_no_lo : 8; + UI32_T rch_avbl_gpd_no_hi : 8; + } field; +} HAL_LIGHTNING_PKT_RCH_STATUS_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T tch_ioc_en : 1; + UI32_T tch_chksm_en : 1; + UI32_T tch_pfc_en : 1; + UI32_T tch_pktlen_chk_en : 1; + UI32_T tch_early_done_irq : 1; + UI32_T tch_chk_cos_en : 1; + UI32_T tch_adv_gpd_wrbk : 1; + UI32_T tch_gpd_wrbk_full_pkt_len : 1; + UI32_T : 8; + UI32_T : 8; + UI32_T : 8; + } field; +} HAL_LIGHTNING_PKT_TCH_CFG_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T tch_start : 1; + UI32_T tch_resume : 1; + UI32_T tch_stop : 1; + UI32_T : 5; + UI32_T : 8; + UI32_T tch_gpd_add_no_lo : 8; + UI32_T tch_gpd_add_no_hi : 8; + } field; +} HAL_LIGHTNING_PKT_TCH_CMD_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T tch_active : 1; + UI32_T tch_pfc : 1; + UI32_T tch_gpd_rd_dma_act : 1; + UI32_T : 5; + UI32_T : 8; + UI32_T tch_avbl_gpd_no : 1; + UI32_T : 7; + UI32_T : 8; + } field; +} HAL_LIGHTNING_PKT_TCH_STS_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T tch_gpd_dmar_qos : 4; + UI32_T : 4; + UI32_T tch_pkt_dmar_qos : 4; + UI32_T : 4; + UI32_T tch_gpd_dmaw_qos : 4; + UI32_T : 4; + UI32_T : 8; + } field; +} HAL_LIGHTNING_PKT_TCH_QOS_CFG_REG_T; + +#elif defined(CLX_EN_BIG_ENDIAN) + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 6; + UI32_T tch_ios_crdt_add_en : 1; + UI32_T tch_rready_low_4_hdr : 1; + UI32_T : 6; + UI32_T tch_arvalid_thrhold_cfg : 2; + UI32_T : 7; + UI32_T tch_axi_free_arvalid : 1; + UI32_T : 5; + UI32_T tch_axlen_cfg : 3; + } field; +} HAL_LIGHTNING_PKT_AXI_LEN_CFG_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 2; + UI32_T pm_lbk_rqid : 6; + UI32_T : 7; + UI32_T pm_lbk_en : 1; + UI32_T : 2; + UI32_T pdma_lbk_plane : 2; + UI32_T : 3; + UI32_T pdma_lbk_en : 1; + } field; +} HAL_LIGHTNING_PKT_LBK_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 2; + UI32_T pdma_lbk_rqid3 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid2 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid1 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid0 : 6; + } field; +} HAL_LIGHTNING_PKT_LBK_RQID0_3_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 2; + UI32_T pdma_lbk_rqid7 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid6 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid5 : 6; + UI32_T : 2; + UI32_T pdma_lbk_rqid4 : 6; + } field; +} HAL_LIGHTNING_PKT_LBK_RQID4_7_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T cos_pfc_sts3 : 8; + UI32_T cos_pfc_sts2 : 8; + UI32_T cos_pfc_sts1 : 8; + UI32_T cos_pfc_sts0 : 8; + } field; +} HAL_LIGHTNING_PKT_COS_PFC_STS_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 8; + UI32_T pdma_ela_valid_sel : 8; + UI32_T : 7; + UI32_T pdma_ela_en : 1; + } field; +} HAL_LIGHTNING_PKT_ELA_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T pdma_ela_word3_sel : 8; + UI32_T pdma_ela_word2_sel : 8; + UI32_T pdma_ela_word1_sel : 8; + UI32_T pdma_ela_word0_sel : 8; + } field; +} HAL_LIGHTNING_PKT_ELA_SEL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 6; + UI32_T ingr_pln_full_pkt_mode : 1; + UI32_T : 1; + UI32_T : 7; + UI32_T ingr_pln_ios_credit_set : 1; + UI32_T ingr_pln_ios_credit_base_size_hi : 8; + UI32_T ingr_pln_ios_credit_base_size_lo : 8; + } field; +} HAL_LIGHTNING_PKT_IGR_PLN_CREDIT_CFG_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 6; + UI32_T ingr_pln_ios_credit_udfl : 1; + UI32_T ingr_pln_ios_credit_ovfl : 1; + UI32_T ingr_pln_cur_ios_credit_hi : 8; + UI32_T ingr_pln_cur_ios_credit_lo : 8; + } field; +} HAL_LIGHTNING_PKT_IGR_PLN_CREDIT_STS_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 8; + UI32_T ingr_pln_ios_credit_rdy_hi_bound : 8; + UI32_T ingr_pln_ios_credit_rdy_lo_bound : 8; + } field; +} HAL_LIGHTNING_PKT_IGR_PLN_CREDIT_THR_REG_T; + + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 7; + UI32_T rch_pfc_fun_en : 1; + UI32_T : 7; + UI32_T rch_crc_regen_en : 1; + UI32_T : 7; + UI32_T rch_stomp_crc_en : 1; + } field; +} HAL_LIGHTNING_PKT_RCH_STOMP_CRC_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 8; + UI32_T : 7; + UI32_T rch_chksm_en : 1; + UI32_T : 7; + UI32_T rch_ioc_en : 1; + } field; +} HAL_LIGHTNING_PKT_RCH_MISC_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 8; + UI32_T rch_gpd_pfc_hi : 8; + UI32_T rch_gpd_pfc_lo : 8; + } field; +} HAL_LIGHTNING_PKT_RCH_GPD_PFC_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 5; + UI32_T rch_fifo_pfc_hi_hi : 3; + UI32_T rch_fifo_pfc_hi_lo : 8; + UI32_T : 5; + UI32_T rch_fifo_pfc_lo_hi : 3; + UI32_T rch_fifo_pfc_lo_lo : 8; + } field; +} HAL_LIGHTNING_PKT_RCH_FIFO_PFC_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 8; + UI32_T : 3; + UI32_T rch_cmdq_pfc_hi : 5; + UI32_T : 3; + UI32_T rch_cmdq_pfc_lo : 5; + } field; +} HAL_LIGHTNING_PKT_RCH_CMDQ_PFC_CTRL_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T rch_gpd_add_no_hi : 8; + UI32_T rch_gpd_add_no_lo : 8; + UI32_T : 8; + UI32_T : 5; + UI32_T rch_stop : 1; + UI32_T rch_resume : 1; + UI32_T rch_start : 1; + } field; +} HAL_LIGHTNING_PKT_RCH_CMD_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 8; + UI32_T : 8; + UI32_T : 2; + UI32_T rch_flush_cnt_clr : 1; + UI32_T rch_pkterr_drop_cnt_clr : 1; + UI32_T rch_udsz_drop_cnt_clr : 1; + UI32_T rch_ovsz_drop_cnt_clr : 1; + UI32_T rch_cmdq_ovf_drop_cnt_clr : 1; + UI32_T rch_fifo_ovf_drop_cnt_clr : 1; + } field; +} HAL_LIGHTNING_PKT_RCH_CNT_CLR_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T rch_avbl_gpd_no_hi : 8; + UI32_T rch_avbl_gpd_no_lo : 8; + UI32_T : 8; + UI32_T : 3; + UI32_T rch_pfc : 1; + UI32_T rch_cmdq_pfc : 1; + UI32_T rch_fifo_pfc : 1; + UI32_T rch_avbl_gpd_pfc : 1; + UI32_T rch_active : 1; + } field; +} HAL_LIGHTNING_PKT_RCH_STATUS_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 8; + UI32_T : 8; + UI32_T tch_gpd_wrbk_full_pkt_len : 1; + UI32_T tch_adv_gpd_wrbk : 1; + UI32_T tch_chk_cos_en : 1; + UI32_T tch_early_done_irq : 1; + UI32_T tch_pktlen_chk_en : 1; + UI32_T tch_pfc_en : 1; + UI32_T tch_chksm_en : 1; + UI32_T tch_ioc_en : 1; + } field; +} HAL_LIGHTNING_PKT_TCH_CFG_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T tch_gpd_add_no_hi : 8; + UI32_T tch_gpd_add_no_lo : 8; + UI32_T : 8; + UI32_T : 5; + UI32_T tch_stop : 1; + UI32_T tch_resume : 1; + UI32_T tch_start : 1; + } field; +} HAL_LIGHTNING_PKT_TCH_CMD_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 7; + UI32_T tch_avbl_gpd_no : 1; + UI32_T : 8; + UI32_T : 5; + UI32_T tch_gpd_rd_dma_act : 1; + UI32_T tch_pfc : 1; + UI32_T tch_active : 1; + } field; +} HAL_LIGHTNING_PKT_TCH_STS_REG_T; + +typedef union +{ + UI32_T reg; + struct + { + UI32_T : 8; + UI32_T : 4; + UI32_T tch_gpd_dmaw_qos : 4; + UI32_T : 4; + UI32_T tch_pkt_dmar_qos : 4; + UI32_T : 4; + UI32_T tch_gpd_dmar_qos : 4; + } field; +} HAL_LIGHTNING_PKT_TCH_QOS_CFG_REG_T; + +#else +#error "Host GPD endian is not defined\n" +#endif + +/* ----------------------------------------------------------------------------------- CLX_EN_NETIF */ +#if defined (CLX_EN_NETIF) +#define HAL_LIGHTNING_PKT_DRIVER_MAJOR_NUM (10) +#define HAL_LIGHTNING_PKT_DRIVER_MINOR_NUM (252) /* DO NOT use MISC_DYNAMIC_MINOR */ +#define HAL_LIGHTNING_PKT_DRIVER_NAME "clx_netif" +#define HAL_LIGHTNING_PKT_DRIVER_PATH "/dev/"HAL_LIGHTNING_PKT_DRIVER_NAME + +/* These requirements come from CLX_NETIF APIs. + * clx_netif -> hal_lightning_pkt_drv -> hal_lightning_pkt_knl + */ + +typedef struct +{ + UI32_T tx_pkt; + UI32_T tx_queue_full; + UI32_T tx_error; + UI32_T rx_pkt; + +} HAL_LIGHTNING_PKT_NETIF_INTF_CNT_T; + +typedef struct +{ + /* unique key */ + UI32_T id; + C8_T name[CLX_NETIF_NAME_LEN]; + UI32_T port; /* only support unit port and local port */ + + /* metadata */ + UI8_T mac[6]; + +#define HAL_LIGHTNING_PKT_NETIF_INTF_FLAGS_MAC (1UL << 0) + UI32_T flags; + + +} HAL_LIGHTNING_PKT_NETIF_INTF_T; + +#if defined(NETIF_EN_NETLINK) +typedef struct +{ + C8_T name[CLX_NETIF_NAME_LEN]; + C8_T mc_group_name[CLX_NETIF_NAME_LEN]; +} HAL_LIGHTNING_PKT_NETIF_RX_DST_NETLINK_T; +#endif + +typedef enum +{ + HAL_LIGHTNING_PKT_NETIF_RX_DST_SDK = 0, +#if defined(NETIF_EN_NETLINK) + HAL_LIGHTNING_PKT_NETIF_RX_DST_NETLINK, +#endif + HAL_LIGHTNING_PKT_NETIF_RX_DST_LAST +} HAL_LIGHTNING_PKT_NETIF_RX_DST_TYPE_T; + +typedef struct +{ + /* unique key */ + UI32_T id; + C8_T name[CLX_NETIF_NAME_LEN]; + UI32_T priority; + + /* match fields */ + UI32_T port; /* only support unit port and local port */ + HAL_PKT_RX_REASON_BITMAP_T reason_bitmap; + UI8_T pattern[CLX_NETIF_PROFILE_PATTERN_NUM][CLX_NETIF_PROFILE_PATTERN_LEN]; + UI8_T mask[CLX_NETIF_PROFILE_PATTERN_NUM][CLX_NETIF_PROFILE_PATTERN_LEN]; + UI32_T offset[CLX_NETIF_PROFILE_PATTERN_NUM]; + + /* for each flag 1:must hit, 0:don't care */ +#define HAL_LIGHTNING_PKT_NETIF_PROFILE_FLAGS_PORT (1UL << 0) +#define HAL_LIGHTNING_PKT_NETIF_PROFILE_FLAGS_REASON (1UL << 1) +#define HAL_LIGHTNING_PKT_NETIF_PROFILE_FLAGS_PATTERN_0 (1UL << 2) +#define HAL_LIGHTNING_PKT_NETIF_PROFILE_FLAGS_PATTERN_1 (1UL << 3) +#define HAL_LIGHTNING_PKT_NETIF_PROFILE_FLAGS_PATTERN_2 (1UL << 4) +#define HAL_LIGHTNING_PKT_NETIF_PROFILE_FLAGS_PATTERN_3 (1UL << 5) + UI32_T flags; + + HAL_LIGHTNING_PKT_NETIF_RX_DST_TYPE_T dst_type; +#if defined(NETIF_EN_NETLINK) + HAL_LIGHTNING_PKT_NETIF_RX_DST_NETLINK_T netlink; +#endif + +} HAL_LIGHTNING_PKT_NETIF_PROFILE_T; + + +/* These requirements come from CLX_PKT APIs. + * clx_pkt -> hal_lightning_pkt_srv -> hal_lightning_pkt_drv -> hal_lightning_pkt_knl + */ +typedef enum +{ + /* network interface */ + HAL_LIGHTNING_PKT_IOCTL_TYPE_CREATE_INTF = 0, + HAL_LIGHTNING_PKT_IOCTL_TYPE_DESTROY_INTF, + HAL_LIGHTNING_PKT_IOCTL_TYPE_GET_INTF, + HAL_LIGHTNING_PKT_IOCTL_TYPE_CREATE_PROFILE, + HAL_LIGHTNING_PKT_IOCTL_TYPE_DESTROY_PROFILE, + HAL_LIGHTNING_PKT_IOCTL_TYPE_GET_PROFILE, + HAL_LIGHTNING_PKT_IOCTL_TYPE_GET_INTF_CNT, + HAL_LIGHTNING_PKT_IOCTL_TYPE_CLEAR_INTF_CNT, + /* driver */ + HAL_LIGHTNING_PKT_IOCTL_TYPE_WAIT_RX_FREE, + HAL_LIGHTNING_PKT_IOCTL_TYPE_WAIT_TX_FREE, /* waitTxFree(ASYNC) */ + HAL_LIGHTNING_PKT_IOCTL_TYPE_SET_RX_CFG, /* setRxConfig */ + HAL_LIGHTNING_PKT_IOCTL_TYPE_GET_RX_CFG, /* getRxConfig */ + HAL_LIGHTNING_PKT_IOCTL_TYPE_DEINIT_TASK, /* deinitTask */ + HAL_LIGHTNING_PKT_IOCTL_TYPE_DEINIT_DRV, /* deinitDrv */ + HAL_LIGHTNING_PKT_IOCTL_TYPE_INIT_TASK, /* initTask */ + HAL_LIGHTNING_PKT_IOCTL_TYPE_INIT_DRV, /* initDrv */ + /* counter */ + HAL_LIGHTNING_PKT_IOCTL_TYPE_GET_TX_CNT, + HAL_LIGHTNING_PKT_IOCTL_TYPE_GET_RX_CNT, + HAL_LIGHTNING_PKT_IOCTL_TYPE_CLEAR_TX_CNT, + HAL_LIGHTNING_PKT_IOCTL_TYPE_CLEAR_RX_CNT, + /* port attribute */ + HAL_LIGHTNING_PKT_IOCTL_TYPE_SET_PORT_ATTR, + HAL_LIGHTNING_PKT_IOCTL_TYPE_GET_PORT_ATTR, +#if defined(NETIF_EN_NETLINK) + HAL_LIGHTNING_PKT_IOCTL_TYPE_NL_SET_INTF_PROPERTY, + HAL_LIGHTNING_PKT_IOCTL_TYPE_NL_GET_INTF_PROPERTY, + HAL_LIGHTNING_PKT_IOCTL_TYPE_NL_CREATE_NETLINK, + HAL_LIGHTNING_PKT_IOCTL_TYPE_NL_DESTROY_NETLINK, + HAL_LIGHTNING_PKT_IOCTL_TYPE_NL_GET_NETLINK, +#endif + HAL_LIGHTNING_PKT_IOCTL_TYPE_LAST + +} HAL_LIGHTNING_PKT_IOCTL_TYPE_T; + +typedef enum +{ + HAL_LIGHTNING_PKT_IOCTL_RX_TYPE_INIT = 0, + HAL_LIGHTNING_PKT_IOCTL_RX_TYPE_DEINIT, + HAL_LIGHTNING_PKT_IOCTL_RX_TYPE_LAST, + +} HAL_LIGHTNING_PKT_IOCTL_RX_TYPE_T; + +typedef struct +{ + UI32_T unit; + UI32_T channel; + HAL_LIGHTNING_PKT_RX_CNT_T rx_cnt; + HAL_LIGHTNING_PKT_TX_CNT_T tx_cnt; + CLX_ERROR_NO_T rc; + +} HAL_LIGHTNING_PKT_IOCTL_CH_CNT_COOKIE_T; + +typedef struct +{ + UI32_T unit; + HAL_LIGHTNING_PKT_NETIF_INTF_T net_intf; /* addIntf[In,Out], delIntf[In] */ + HAL_LIGHTNING_PKT_NETIF_PROFILE_T net_profile; /* createProfile[In,Out], destroyProfile[In] */ + HAL_LIGHTNING_PKT_NETIF_INTF_CNT_T cnt; + CLX_ERROR_NO_T rc; + +} HAL_LIGHTNING_PKT_IOCTL_NETIF_COOKIE_T; + +typedef struct +{ + CLX_ADDR_T callback; /* (unit, ptr_sw_gpd, ptr_cookie) */ + CLX_ADDR_T cookie; /* Pointer of CLX_PKT_TX_PKT_T */ + UI32_T channel; + UI32_T gpd_num; + CLX_ADDR_T hw_gpd_addr; + CLX_ADDR_T sw_gpd_addr; + +} HAL_LIGHTNING_PKT_IOCTL_TX_GPD_T; + +typedef struct +{ + UI32_T unit; + UI32_T channel; /* sendGpd[In] */ + CLX_ADDR_T ioctl_gpd_addr; /* sendGpd[In] */ + CLX_ADDR_T done_sw_gpd_addr; /* waitTxFree[Out] */ + +} HAL_LIGHTNING_PKT_IOCTL_TX_COOKIE_T; + +typedef struct +{ + BOOL_T rx_complete; /* FALSE when PDMA error occurs */ + CLX_ADDR_T hw_gpd_addr; /* Pointer to HW GPD in user's SW GPD struct */ + CLX_ADDR_T dma_buf_addr; /* Pointer to DMA buffer allocated by the user (virtual) */ + +} HAL_LIGHTNING_PKT_IOCTL_RX_GPD_T; + +typedef struct +{ + UI32_T unit; + UI32_T channel; /* getRxCnt[In], clearRxInt[In] */ + CLX_ADDR_T ioctl_gpd_addr; /* waitRxFree[Out] */ + UI32_T buf_len; /* setRxCfg[In] */ + HAL_LIGHTNING_PKT_IOCTL_RX_TYPE_T rx_type; /* setRxCfg[In] */ + +} HAL_LIGHTNING_PKT_IOCTL_RX_COOKIE_T; + +typedef struct +{ + UI32_T port; + UI32_T status; + CLX_PORT_SPEED_T speed; + +} HAL_LIGHTNING_PKT_IOCTL_PORT_COOKIE_T; + + +#if defined(NETIF_EN_NETLINK) + +typedef struct +{ + /* intf property */ + UI32_T intf_id; + CLX_NETIF_INTF_PROPERTY_T property; + UI32_T param0; + UI32_T param1; + + /* netlink */ + CLX_NETIF_NETLINK_T netlink; + + CLX_ERROR_NO_T rc; + +} HAL_LIGHTNING_PKT_NL_IOCTL_COOKIE_T; + + +#endif /* End of NETIF_EN_NETLINK */ + +typedef union +{ + UI32_T value; + struct + { + UI32_T unit : 6; /* Maximum unit number is 64. */ + HAL_LIGHTNING_PKT_IOCTL_TYPE_T type : 10; /* Maximum 1024 IOCTL types */ + UI32_T rsvd : 16; + } field; + +} HAL_LIGHTNING_PKT_IOCTL_CMD_T; + +#endif /* End of CLX_EN_NETIF */ + +//} +/*---------------------------------------------------------------------------*/ +/* perf */ +CLX_ERROR_NO_T +hal_lightning_pkt_getTxIntrCnt( + const UI32_T unit, + const UI32_T channel, + UI32_T *ptr_intr_cnt); + +CLX_ERROR_NO_T +hal_lightning_pkt_getRxIntrCnt( + const UI32_T unit, + const UI32_T channel, + UI32_T *ptr_intr_cnt); + +/* ioctl */ +CLX_ERROR_NO_T +hal_lightning_pkt_getTxKnlCnt( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_CH_CNT_COOKIE_T *ptr_cookie); + +CLX_ERROR_NO_T +hal_lightning_pkt_getRxKnlCnt( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_CH_CNT_COOKIE_T *ptr_cookie); + +CLX_ERROR_NO_T +hal_lightning_pkt_clearTxKnlCnt( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_TX_COOKIE_T *ptr_cookie); + +CLX_ERROR_NO_T +hal_lightning_pkt_clearRxKnlCnt( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_RX_COOKIE_T *ptr_cookie); + +CLX_ERROR_NO_T +hal_lightning_pkt_setRxKnlConfig( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_RX_COOKIE_T *ptr_cookie); + +CLX_ERROR_NO_T +hal_lightning_pkt_getRxKnlConfig( + const UI32_T unit, + HAL_LIGHTNING_PKT_IOCTL_RX_COOKIE_T *ptr_cookie); + +/* perf */ +CLX_ERROR_NO_T +hal_lightning_pkt_getNetDev( + const UI32_T unit, + const UI32_T port, + struct net_device **pptr_net_dev); + +CLX_ERROR_NO_T +hal_lightning_pkt_prepareGpd( + const UI32_T unit, + const CLX_ADDR_T phy_addr, + const struct sk_buff *ptr_skb, + const UI32_T port, + HAL_LIGHTNING_PKT_TX_SW_GPD_T *ptr_sw_gpd); + +CLX_ERROR_NO_T +hal_lightning_pkt_sendGpd( + const UI32_T unit, + const HAL_LIGHTNING_PKT_TX_CHANNEL_T channel, + HAL_LIGHTNING_PKT_TX_SW_GPD_T *ptr_sw_gpd); + +CLX_ERROR_NO_T +hal_lightning_pkt_init( + const UI32_T unit); + +CLX_ERROR_NO_T +hal_lightning_pkt_exit( + const UI32_T unit); + +ssize_t +hal_lightning_pkt_dev_tx( + struct file *file, + const char __user *buf, + size_t count, + loff_t *pos); + +long +hal_lightning_pkt_dev_ioctl( + struct file *filp, + unsigned int cmd, + unsigned long arg); + +#endif /* end of HAL_LIGHTNING_PKT_KNL_H */ diff --git a/platform/clounix/clounix-modules/modules/src/inc/netif_knl.h b/platform/clounix/clounix-modules/modules/src/inc/netif_knl.h new file mode 100644 index 000000000000..a6d83df90ad1 --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/inc/netif_knl.h @@ -0,0 +1,477 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: netif_knl.h + * NOTES: + */ +#ifndef NETIF_KNL_H +#define NETIF_KNL_H + +#include +#include + +#define CLX_NETIF_NAME_LEN (16) +#define CLX_NETIF_PROFILE_NUM_MAX (256) +#define CLX_NETIF_PROFILE_PATTERN_NUM (4) +#define CLX_NETIF_PROFILE_PATTERN_LEN (8) + +#define NETIF_EN_NETLINK + +/* Port Speed */ +typedef enum +{ + CLX_PORT_SPEED_1G = 1000, + CLX_PORT_SPEED_10G = 10000, + CLX_PORT_SPEED_25G = 25000, + CLX_PORT_SPEED_40G = 40000, + CLX_PORT_SPEED_50G = 50000, + CLX_PORT_SPEED_100G = 100000, + CLX_PORT_SPEED_200G = 200000, + CLX_PORT_SPEED_400G = 400000, + CLX_PORT_SPEED_LAST +} CLX_PORT_SPEED_T; + +#define HAL_INVALID_GROUP_LABEL (0) +#define HAL_INVALID_NVO3_ENCAP_IDX (0x3FFF) +#define HAL_INVALID_NVO3_ADJ_IDX (0xFF) +#define HAL_INVALID_LCL_INTF_GRP (0x1FFF) +#define HAL_INVALID_CNT_MTR_IDX (0x7FFF) +#define HAL_INVALID_FDID (0) /* means fdid is not created */ +#define HAL_INVALID_L3_INTF (0) /* means L3 interface is disabled */ +#define HAL_INVALID_FRR_STATE_IDX (0) /* means ECMP FRR is disabled */ +#define HAL_INVALID_SEG_VMID (0xFFFFFF) +#define HAL_INVALID_CPU_ID (0x1F) +#define HAL_INVALID_DOS_IDX (0xF) +#define HAL_INVALID_HW_BUM_OFFSET (0x3) +#define HAL_INVALID_IEV_RSLT_IDX (0x3FFFF) +#define HAL_INVALID_PHB_HW_IDX (0x1F) /* SRV INTF uses invalid index */ +#define HAL_DEFAULT_PHB_HW_IDX (0x1F) /* LCL INTF uses default index */ +#define HAL_RSV_FDID (0x3FFF) /* HW reserved; all 1 means use outer vid as FDID */ +#define HAL_RSV_L3_INTF (0x3FFF) /* HW reserved; all 1 means use outer vid as L3 Intf */ + /* all 1 means invalid at egress side */ +#define HAL_INVALID_ADJ_IDX (0x3FFFF) /* For 18bit adj_idx */ +#define HAL_INVALID_MTR_HW_IDX (0x1FFFF) +#define HAL_RSV_MEL_IDX (0x1FFF) + +#define HAL_LAG_PORT_NUM (512) +#define HAL_L2_MGID_NUM (16384) /* non-replicate mgid */ +#define HAL_L3_MGID_NUM (8192) /* replicate mgid */ +#define HAL_MGID_NUM (HAL_L2_MGID_NUM + HAL_L3_MGID_NUM) +#define HAL_EXCPT_CPU_NUM (256) +#define HAL_EXCPT_DROP_NUM (256) +#define HAL_DROP_NUM (512) +#define HAL_MIRROR_NUM (256) +#define HAL_REDIRECT_CPU_NUM (2048) +#define HAL_TUNNEL_NUM (8192) +#define HAL_NSH_NUM (8192) +#define HAL_FRR_NUM (4096) +#define HAL_ECMP_NUM (2048) +#define HAL_INVALID_NUM (10240) + +#define HAL_EXCPT_CPU_BASE_ID (28 * 1024) +#define HAL_EXCPT_CPU_NON_L3_MIN (0) +#define HAL_EXCPT_CPU_NON_L3_MAX (HAL_EXCPT_CPU_NON_L3_MIN + HAL_EXCPT_CPU_NUM - 1) +#define HAL_EXCPT_CPU_L3_MIN (HAL_EXCPT_CPU_NON_L3_MIN + HAL_EXCPT_CPU_NUM) +#define HAL_EXCPT_CPU_L3_MAX (HAL_EXCPT_CPU_L3_MIN + HAL_EXCPT_CPU_NUM - 1) + +#if defined(NETIF_EN_NETLINK) + +#define CLX_NETIF_NETLINK_NUM_MAX (256) +#define CLX_NETIF_NETLINK_MC_GROUP_NUM_MAX (32) + +typedef enum +{ + CLX_NETIF_INTF_PROPERTY_IGR_SAMPLING_RATE, + CLX_NETIF_INTF_PROPERTY_EGR_SAMPLING_RATE, + CLX_NETIF_INTF_PROPERTY_LAST +} CLX_NETIF_INTF_PROPERTY_T; + +typedef struct +{ + C8_T name[CLX_NETIF_NAME_LEN]; + +} CLX_NETIF_NETLINK_MC_GROUP_T; + +typedef struct +{ + UI32_T id; + C8_T name[CLX_NETIF_NAME_LEN]; + CLX_NETIF_NETLINK_MC_GROUP_T mc_group[CLX_NETIF_NETLINK_MC_GROUP_NUM_MAX]; + UI32_T mc_group_num; + +} CLX_NETIF_NETLINK_T; + +#endif + +/* The reason defiend for the packets which are copied/redirected to CPU. */ + +/* RX Packet Reasons */ +typedef enum +{ + CLX_PKT_RX_REASON_ECIA_SFLOW = 0, /* Egress flow-based sflow hit. */ + CLX_PKT_RX_REASON_VLAN_MISS_VLAN_CHK, /* VLAN lookup miss or not accepted. */ + CLX_PKT_RX_REASON_PAR_ERR, /* Parser error to CPU. */ + CLX_PKT_RX_REASON_PAR_WARN, /* Parser warning to CPU. */ + CLX_PKT_RX_REASON_DOS_CHK, /* DoS checking fail to CPU. */ + CLX_PKT_RX_REASON_BFD_CTRL_PKT, /* BFD control packet to CPU. */ + CLX_PKT_RX_REASON_INVALID_BFD_PKT, /* Invalid BFD control packet to CPU. */ + CLX_PKT_RX_REASON_SA_LEARN_FAIL, /* Add to l2fdb fail to CPU. */ + CLX_PKT_RX_REASON_TUNNEL_ECN_CU, /* The current unused ECN combination defined in RFC6040. */ + CLX_PKT_RX_REASON_FCOE_CLASS_2_F, /* Copy FCoE Class 2 packet to cpu when packet is dropped. */ + CLX_PKT_RX_REASON_URPF_CHECK_FAIL, /* L3 URPF check fail to CPU. */ + CLX_PKT_RX_REASON_L3_LKP_MISS, /* L3 lookup miss to CPU. */ + CLX_PKT_RX_REASON_ICMP_REDIRECT, /* The same L3 interface for ingress and egress to CPU. */ + CLX_PKT_RX_REASON_IPV4_HDR_OPTION, /* IPv4 header with option to CPU. */ + CLX_PKT_RX_REASON_VCLAG_INVALID, /* VM tag invalid to CPU. */ + CLX_PKT_RX_REASON_VM_RPF_FAIL, /* VM RPF check fail to CPU. */ + /* 16 */ + CLX_PKT_RX_REASON_VM_L2_FDB_MISS, /* VM l2fdb lookup miss to CPU. */ + CLX_PKT_RX_REASON_IGR_PORT_SFLOW, /* Ingress port-based sflow hit. */ + CLX_PKT_RX_REASON_EGR_PORT_SFLOW, /* Egress port-based sflow hit. */ + CLX_PKT_RX_REASON_IGR_FD_SFLOW, /* Ingress fd-based sflow hit. */ + CLX_PKT_RX_REASON_ICIA_SFLOW, /* Ingress flow-based sflow hit. */ + CLX_PKT_RX_REASON_IPSG_CHK, /* IPSG check fail to CPU. */ + CLX_PKT_RX_REASON_FCOE_IFR_TO_CPU, /* FCoE extend header include IFR header. */ + CLX_PKT_RX_REASON_TTL_EXPIRE, /* L3 TTL check fail to CPU. */ + CLX_PKT_RX_REASON_L3MC_RPF_CHECK, /* L3MC RPF check fail to CPU */ + CLX_PKT_RX_REASON_IPV6_HOP_BY_HOP_EXT_HDR, /* IPv6 with hop by hop extension header to CPU. */ + CLX_PKT_RX_REASON_VM_PDU, /* Received frames whose E-CID matches the IBR upstream Port's PCID. */ + CLX_PKT_RX_REASON_PIM_REGISTER, /* For source hit and group miss, send to CPU for PIM-registration. */ + CLX_PKT_RX_REASON_TUNNEL_TERM_LKP_MISS, /* Tunnel term lookup failed with bank error or entry miss. */ + CLX_PKT_RX_REASON_PP, /* Protocol/Management packet for SDN. */ + CLX_PKT_RX_REASON_EGR_FD_SFLOW, /* Egress fd-based sflow hit. */ + CLX_PKT_RX_REASON_VXLAN_ROUTER_ALERT, /* VXLAN packet with router alert to CPU. */ + /* 32 */ + CLX_PKT_RX_REASON_NVGRE_ROUTER_ALERT, /* NVGRE packet with router alert to CPU. */ + CLX_PKT_RX_REASON_EX_PMOD_LIMIT, /* Packet modification is excess PMOD's limitation. */ + CLX_PKT_RX_REASON_L3MC_SPT_READY_UNSET, /* L3MC to CPU to trigger setting Shortest Path Tree ready bit. */ + CLX_PKT_RX_REASON_IGR_MTU_FAIL, /* Ingress MTU check fail to CPU. */ + CLX_PKT_RX_REASON_EGR_MTU_FAIL, /* Egress MTU check fail to CPU. */ + CLX_PKT_RX_REASON_VXLAN_PING, /* VXLAN packet to CPU. */ + CLX_PKT_RX_REASON_REDIRECT_TO_CPU_L2MC, /* L2MC Redirect to CPU. */ + CLX_PKT_RX_REASON_REDIRECT_TO_CPU_L3MC, /* L3MC Redirect to CPU. */ + CLX_PKT_RX_REASON_REDIRECT_TO_CPU_L2UC, /* L2UC Redirect to CPU. */ + CLX_PKT_RX_REASON_REDIRECT_TO_CPU_L3UC, /* L3UC Redirect to CPU. */ + CLX_PKT_RX_REASON_SDN_IGR_FLOW_SFLOW, /* SDN ingress flow-based sflow hit. */ + CLX_PKT_RX_REASON_SDN_EGR_FLOW_SFLOW, /* SDN egress flow-based sflow hit. */ + CLX_PKT_RX_REASON_SDN_HW_LIMITATION, /* SDN hardware limitation to CPU. */ + CLX_PKT_RX_REASON_SDN_OTHERS, /* SDN other exceptions to CPU. */ + CLX_PKT_RX_REASON_SDN_PKT_IN, /* SDN output controller port (packet-in) to CPU. */ + CLX_PKT_RX_REASON_SDN_TO_LOCAL, /* SDN output local port to CPU. */ + /* 48 */ + CLX_PKT_RX_REASON_CIA_0, /* CIA To CPU. */ + CLX_PKT_RX_REASON_CIA_1, /* CIA To CPU. */ + CLX_PKT_RX_REASON_CIA_2, /* CIA To CPU. */ + CLX_PKT_RX_REASON_CIA_3, /* CIA To CPU. */ + CLX_PKT_RX_REASON_CIA_4, /* CIA To CPU. */ + CLX_PKT_RX_REASON_CIA_5, /* CIA To CPU. */ + CLX_PKT_RX_REASON_CIA_6, /* CIA To CPU. */ + CLX_PKT_RX_REASON_CIA_7, /* CIA To CPU. */ + CLX_PKT_RX_REASON_USR_DEFINE_0, + CLX_PKT_RX_REASON_USR_DEFINE_1, + CLX_PKT_RX_REASON_USR_DEFINE_2, + CLX_PKT_RX_REASON_USR_DEFINE_3, + CLX_PKT_RX_REASON_USR_DEFINE_4, + CLX_PKT_RX_REASON_USR_DEFINE_5, + CLX_PKT_RX_REASON_USR_DEFINE_6, + CLX_PKT_RX_REASON_USR_DEFINE_7, + /* 64 */ + CLX_PKT_RX_REASON_USR_DEFINE_8, + CLX_PKT_RX_REASON_USR_DEFINE_9, + CLX_PKT_RX_REASON_USR_DEFINE_10, + CLX_PKT_RX_REASON_USR_DEFINE_11, + CLX_PKT_RX_REASON_USR_DEFINE_12, + CLX_PKT_RX_REASON_USR_DEFINE_13, + CLX_PKT_RX_REASON_L2_LKP_MISS, /* L2 lookup failed with the bank error or entry miss. */ + CLX_PKT_RX_REASON_L2_HDR_MISS, /* l2 bridge copy without ingress Ethernet header. */ + CLX_PKT_RX_REASON_L2_SA_MISS, /* L2 lookup SA miss. */ + CLX_PKT_RX_REASON_L2_SA_MOVE, /* L2 lookup SA hit but interface move. */ + CLX_PKT_RX_REASON_IPV4_VER_ERR, /* IPv4 packet has bad version. */ + CLX_PKT_RX_REASON_IPV4_OPT, /* IPv4 packet has options. */ + CLX_PKT_RX_REASON_IPV4_LEN_ERR, /* IPv4 packet Internet Header Length is unmatched to payload length. */ + CLX_PKT_RX_REASON_IPV4_CHKSM_ERR, /* IPv4 packet checksum error. */ + CLX_PKT_RX_REASON_IPV4_MC_MALFORMED, /* IPv4 multicast packet is malformed from RFC. */ + CLX_PKT_RX_REASON_IPV4_MC_LKP_MISS, /* IPv4 multicast packet lookup miss. */ + /* 80 */ + CLX_PKT_RX_REASON_IPV6_VER_ERR, /* IPv6 packet has bad version. */ + CLX_PKT_RX_REASON_IPV6_LEN_ERR, /* IPv6 packet total length does not cover 40-Bytes IPv6 base header. */ + CLX_PKT_RX_REASON_IPV6_MC_MALFORMED, /* IPv6 multicast packet is malformed from RFC. */ + CLX_PKT_RX_REASON_IPV6_MC_LKP_MISS, /* IPv6 multicast packet lookup miss. */ + CLX_PKT_RX_REASON_FCOE_VER_ERR, /* FCoE packet has bad version. */ + CLX_PKT_RX_REASON_FCOE_LKP_MISS, /* FCoE DID lookup failed, bank error or entry miss. */ + CLX_PKT_RX_REASON_FCOE_ZONING_FAIL, /* FCoE zoning check failed to CPU. */ + CLX_PKT_RX_REASON_MPLS_CTRL_PKT, /* MPLS control packets to CPU. */ + CLX_PKT_RX_REASON_MPLS_INVALID_PKT, /* MPLS packet is illegal from RFC. */ + CLX_PKT_RX_REASON_MPLS_LKP_MISS, /* MPLS lookup failed with the bank error or entry miss. */ + CLX_PKT_RX_REASON_MPLS_UHP_P2P_MISS, /* */ + CLX_PKT_RX_REASON_MPLS_UHP_TTL_0, /* MPLS UHP packet TTL is 0. */ + CLX_PKT_RX_REASON_MPLS_UHP_TTL_1, /* MPLS UHP packet TTL is 1. */ + CLX_PKT_RX_REASON_MPLS_TRANSIT_TTL_0, /* MPLS transit packet TTL is 0. */ + CLX_PKT_RX_REASON_MPLS_TRANSIT_TTL_1, /* MPLS transit packet TTL is 1. */ + CLX_PKT_RX_REASON_MPLS_TERM_TTL_0, /* MPLS term packet TTL is 0. */ + /* 96 */ + CLX_PKT_RX_REASON_MPLS_TERM_TTL_1, /* MPLS term packet TTL is 1. */ + CLX_PKT_RX_REASON_IP_TUNNEL_CTRL_PKT, /* IP tunnel control packets to CPU. */ + CLX_PKT_RX_REASON_IP_TUNNEL_INNER_IPV4_UC_LCL, /* IP tunnel packet has inner IPv4 unicast link local address. */ + CLX_PKT_RX_REASON_IP_TUNNEL_INNER_IPV6_UC_LCL, /* IP tunnel packet has inner IPv6 unicast link local address. */ + CLX_PKT_RX_REASON_IP_TUNNEL_INNER_MC_LCL, /* IP tunnel packet has inner multicast link local address. */ + CLX_PKT_RX_REASON_IP_TUNNEL_INNER_VER_ERR, /* IP tunnel packet inner is not IPv4 and IPv6. */ + CLX_PKT_RX_REASON_IP_TUNNEL_INNER_L3_ROUTE, /* Not allow GRE, IPoMPLS or MPLS VPN routed by inner header. */ + CLX_PKT_RX_REASON_IP_TUNNEL_OUTER_IPV4_FRAG, /* IP tunnel packet outer IPv4 fragment offset is not 0. */ + CLX_PKT_RX_REASON_IP_TUNNEL_OUTER_IPV4_OPT, /* IP tunnel packet outer IPv4 has options. */ + CLX_PKT_RX_REASON_IP_TUNNEL_OUTER_IPV4_AH, /* IP tunnel packet outer IPv4 has authentication header. */ + CLX_PKT_RX_REASON_IP_TUNNEL_OUTER_IPV4_TTL_0, /* IP tunnel packet outer IPv4 TTL is 0. */ + CLX_PKT_RX_REASON_IP_TUNNEL_OUTER_IPV4_TTL_1, /* IP tunnel packet outer IPv4 TTL is 1. */ + CLX_PKT_RX_REASON_IP_TUNNEL_OUTER_IPV6_FRAG, /* IP tunnel packet outer IPv6 fragment offset is not 0. */ + CLX_PKT_RX_REASON_IP_TUNNEL_OUTER_IPV6_OPT, /* IP tunnel packet outer IPv6 has extension header. */ + CLX_PKT_RX_REASON_IP_TUNNEL_OUTER_IPV6_AH, /* IP tunnel packet outer IPv6 has authentication header. */ + CLX_PKT_RX_REASON_IP_TUNNEL_OUTER_IPV6_TTL_0, /* IP tunnel packet outer IPv6 TTL is 0. */ + /* 112 */ + CLX_PKT_RX_REASON_IP_TUNNEL_OUTER_IPV6_TTL_1, /* IP tunnel packet outer IPv6 TTL is 1. */ + CLX_PKT_RX_REASON_IPUC_TUNNEL_INNER_VLAN_MISS, /* IP unicast tunnel packet inner VLAN miss. */ + CLX_PKT_RX_REASON_IPMC_TUNNEL_INNER_VLAN_MISS, /* IP multicast tunnel packet inner VLAN miss. */ + CLX_PKT_RX_REASON_AUTO_TUNNEL_DIP_MISS, /* Auto tunnel outer IPv4 and inner IPv6 DIP are unmatched. */ + CLX_PKT_RX_REASON_AUTO_TUNNEL_SIP_MISS, /* Auto tunnel outer IPv4 and inner IPv6 SIP are unmatched. */ + CLX_PKT_RX_REASON_ETHER_IP_VER_ERR, /* Ethernet-within-IP tunnel version is illegal from RFC 3378. */ + CLX_PKT_RX_REASON_GRE_VER_ERR, /* GRE tunnel version is illegal from RFC 2784. */ + CLX_PKT_RX_REASON_GRE_RSVD_NON_ZERO, /* GRE tunnel reserved fields is illegal from RFC 2784. */ + CLX_PKT_RX_REASON_GRE_CTRL_FLAG_ERR, /* GRE tunnel control flag is illegal from RFC 2784. */ + CLX_PKT_RX_REASON_GRE_ERSPAN_TYP2_VER_ERR, /* GRE ERSPAN type II tunnel version is illegal from ERSPAN draft. */ + CLX_PKT_RX_REASON_GRE_ERSPAN_TYP3_VER_ERR, /* GRE ERSPAN type III tunnel version is illegal from ERSPAN draft. */ + CLX_PKT_RX_REASON_GRE_ERSPAN_TYP3_FT_ERR, /* GRE ERSPAN type III tunnel frame type is illegal from ERSPAN draft.*/ + CLX_PKT_RX_REASON_GRE_ERSPAN_TERM_LKP_MISS, /* GRE ERSPAN tunnel term lookup miss. */ + CLX_PKT_RX_REASON_VXLAN_BAS_RSVD_NON_ZERO, /* VXLAN Basic header reserved fields is illegal from RFC. */ + CLX_PKT_RX_REASON_VXLAN_BAS_VNI_FLAG_ERR, /* VXLAN Basic header VNI flag is illegal from RFC. */ + CLX_PKT_RX_REASON_VXLAN_BAS_CTRL_FLAG_ERR, /* VXLAN Basic header control flag is illegal from RFC. */ + /* 128 */ + CLX_PKT_RX_REASON_VXLAN_BAS_UDP_CHKSM_ERR, /* VXLAN Basic header UDP checksum is abnormal. */ + CLX_PKT_RX_REASON_VXLAN_GPE_VNI_FLAG_ERR, /* VXLAN GRE header VNI flag is illegal from RFC. */ + CLX_PKT_RX_REASON_VXLAN_GPE_CTRL_FLAG_ERR, /* VXLAN GPE header control flag is illegal from RFC. */ + CLX_PKT_RX_REASON_VXLAN_GPE_UDP_CHKSM_ERR, /* VXLAN GPE header UDP checksum is abnormal. */ + CLX_PKT_RX_REASON_TRILL_VER_ERR, /* TRILL version is illegal from RFC. */ + CLX_PKT_RX_REASON_TRILL_MC_FLAG_ERR, /* TRILL multicast flag is unmatched DMAC. */ + CLX_PKT_RX_REASON_TRILL_OPT, /* TRILL packet has option or option header length is illegal. */ + CLX_PKT_RX_REASON_TRILL_LKP_MISS, /* TRILL lookup failed with the bank error or entry miss. */ + CLX_PKT_RX_REASON_TRILL_TRANSIT_TTL_0, /* TRILL transit packet TTL is 0. */ + CLX_PKT_RX_REASON_TRILL_TRANSIT_TTL_1, /* TRILL transit packet TTL is 1. */ + CLX_PKT_RX_REASON_TRILL_TERM_TTL_0, /* TRILL term packet TTL is 0. */ + CLX_PKT_RX_REASON_TRILL_TERM_TTL_1, /* TRILL term packet TTL is 1. */ + CLX_PKT_RX_REASON_TRILL_MRPF_CHECK_FAIL, /* TRILL multicast RPF check failed. */ + CLX_PKT_RX_REASON_NSH_CTRL_PKT, /* NSH control packets to CPU. */ + CLX_PKT_RX_REASON_NSH_INVALID_PKT, /* NSH packet is illegal from RFC. */ + CLX_PKT_RX_REASON_NSH_LKP_MISS, /* NSH lookup failed with the bank error or entry miss. */ + /* 144 */ + CLX_PKT_RX_REASON_ECMP_LKP_MISS, /* ECMP lookup failed with the bank error or entry miss. */ + CLX_PKT_RX_REASON_ACL_LKP_MISS, /* ACL lookup failed with the bank error or entry miss. */ + CLX_PKT_RX_REASON_FLOW_LKP_MISS, /* Flow lookup failed with the bank error or entry miss. */ + CLX_PKT_RX_REASON_IGR_FLOW_SFLOW, /* Ingress flow-based sflow hit. */ + CLX_PKT_RX_REASON_EGR_FLOW_SFLOW, /* Egress flow-based sflow hit. */ + CLX_PKT_RX_REASON_HW_ERROR, /* HW error. e.g. ECC. */ + CLX_PKT_RX_REASON_1588_RX_PKT, /* User spcified the 1588 packets. */ + CLX_PKT_RX_REASON_STP_BLOCK, /* STP block packets to CPU. */ + CLX_PKT_RX_REASON_STACKING_NEIGHBOR, /* The stacking packets from neighbor. */ + CLX_PKT_RX_REASON_STACKING_BROADCAST, /* The stacking broadcast packets. */ + CLX_PKT_RX_REASON_STACKING_LOOP, /* The stacking path is loop. */ + CLX_PKT_RX_REASON_STORM_CONTROL, /* Storm Control packets to CPU. */ + CLX_PKT_RX_REASON_TUNNEL_INIT_LKP_MISS, /* Tunnel init lookup failed with bank error or entry miss. */ + CLX_PKT_RX_REASON_TUNNEL_INNER_VLAN_MISS, /* IP Tunnel or TRILL packets miss inner VLAN. */ + CLX_PKT_RX_REASON_TUNNEL_INNER_VLAN_UNEXP, /* IP Tunnel or TRILL packets not allowed inner VLAN. */ + CLX_PKT_RX_REASON_IGR_L3_MTU_FAIL, /* Ingress L3 MTU check fail to CPU. */ + /* 160 */ + CLX_PKT_RX_REASON_EGR_L3_MTU_FAIL, /* Egress L3 MTU check fail to CPU. */ + CLX_PKT_RX_REASON_IGR_TUNNEL_MTU_FAIL, /* Ingress Tunnel MTU check fail to CPU. */ + CLX_PKT_RX_REASON_EGR_TUNNEL_MTU_FAIL, /* Egress Tunnel MTU check fail to CPU. */ + CLX_PKT_RX_REASON_EGR_IPV4_TTL_1, /* Egress IPv4 interface configure TTL 1. */ + CLX_PKT_RX_REASON_EGR_IPV6_TTL_1, /* Egress IPv6 interface configure TTL 1. */ + CLX_PKT_RX_REASON_COPY_TO_CPU_L2MC, /* L2MC Copy to CPU. */ + CLX_PKT_RX_REASON_COPY_TO_CPU_L3MC, /* L3MC Copy to CPU. */ + CLX_PKT_RX_REASON_COPY_TO_CPU_L2UC, /* L2UC Copy to CPU. */ + CLX_PKT_RX_REASON_COPY_TO_CPU_L3UC, /* L3UC Copy to CPU. */ + CLX_PKT_RX_REASON_FLEX_TUNNEL_UDP_CHKSM_ERR, /* Flex tunnel packet UDP checksum is abnormal. */ + CLX_PKT_RX_REASON_FLEX_TUNNEL_0_CHK, /* Flex tunnel 0 with sanity, type, or length check fail. */ + CLX_PKT_RX_REASON_FLEX_TUNNEL_1_CHK, /* Flex tunnel 1 with sanity, type, or length check fail. */ + CLX_PKT_RX_REASON_FLEX_TUNNEL_2_CHK, /* Flex tunnel 2 with sanity, type, or length check fail. */ + CLX_PKT_RX_REASON_FLEX_TUNNEL_3_CHK, /* Flex tunnel 3 with sanity, type, or length check fail. */ + CLX_PKT_RX_REASON_EGR_SFLOW_HIGH_LATENCY, /* Sample high latency packet. */ + CLX_PKT_RX_REASON_DPP_LOOPBACK, /* DPP loopback packet */ + /* 176 */ + CLX_PKT_RX_REASON_PPPOE_SRV_UNKNOWN, /* PPPOE service unknown */ + CLX_PKT_RX_REASON_PPPOE_HDR_ERR, /* PPPOE header error */ + CLX_PKT_RX_REASON_PPPOE_ENCAP_ERR, /* PPPOE encap error */ + CLX_PKT_RX_REASON_IP_TUNNEL_IPV4_ERR, /* IP tunnel IPv4 header error */ + CLX_PKT_RX_REASON_IP_TUNNEL_IPV6_ERR, /* IP tunnel IPv6 header error */ + CLX_PKT_RX_REASON_DECAP_NSH_TTL_1, /* NSH TLL 1 error */ + CLX_PKT_RX_REASON_TRANSIT_NSH_TTL_1, /* NSH TLL 1 error */ + CLX_PKT_RX_REASON_PORT_MTR_DROP, /* Meter over rate drop */ + CLX_PKT_RX_REASON_WECMP, /* WECMP config error */ + CLX_PKT_RX_REASON_IOAM_NODE_LEN_INVALID_IPV6, /* IOAM over IPv6 node len not align */ + CLX_PKT_RX_REASON_IOAM_NODE_LEN_INVALID_GRE, /* IOAM over GRE node len not align */ + CLX_PKT_RX_REASON_IOAM_NODE_LEN_INVALID_GPE, /* IOAM over GPE node len not align */ + CLX_PKT_RX_REASON_OTHERS, /* None of the above reasons */ + + CLX_PKT_RX_REASON_LAST +} CLX_PKT_RX_REASON_T; + +#define CLX_PKT_RX_REASON_BITMAP_SIZE (CLX_BITMAP_SIZE(CLX_PKT_RX_REASON_LAST)) + +/* The data structure is the reason bitmap for "to CPU" reason codes. */ +typedef UI32_T CLX_PKT_RX_REASON_BITMAP_T[CLX_PKT_RX_REASON_BITMAP_SIZE]; + + +/* ----------------------------------------------------------------------------------- struct */ +typedef struct +{ + UI32_T tx_pkt; + UI32_T tx_queue_full; + UI32_T tx_error; + UI32_T rx_pkt; + +} CLX_NETIF_INTF_CNT_T; + +typedef struct +{ + /* unique key */ + UI32_T id; + C8_T name[CLX_NETIF_NAME_LEN]; + CLX_PORT_T port; /* only support unit port and local port */ + + /* metadata */ + CLX_MAC_T mac; + +#define CLX_NETIF_INTF_FLAGS_MAC (1UL << 0) + UI32_T flags; + +} CLX_NETIF_INTF_T; + + +#if defined(NETIF_EN_NETLINK) +typedef struct +{ + C8_T name[CLX_NETIF_NAME_LEN]; + C8_T mc_group_name[CLX_NETIF_NAME_LEN]; +} CLX_NETIF_RX_DST_NETLINK_T; +#endif + +typedef enum +{ + CLX_NETIF_RX_DST_SDK = 0, +#if defined(NETIF_EN_NETLINK) + CLX_NETIF_RX_DST_NETLINK, +#endif + CLX_NETIF_RX_DST_LAST +} CLX_NETIF_RX_DST_TYPE_T; + +typedef struct +{ + /* unique key */ + UI32_T id; + C8_T name[CLX_NETIF_NAME_LEN]; + UI32_T priority; + + /* match fields */ + CLX_PORT_T port; /* only support unit port and local port */ + CLX_PKT_RX_REASON_BITMAP_T reason_bitmap; + UI8_T pattern[CLX_NETIF_PROFILE_PATTERN_NUM][CLX_NETIF_PROFILE_PATTERN_LEN]; + UI8_T mask[CLX_NETIF_PROFILE_PATTERN_NUM][CLX_NETIF_PROFILE_PATTERN_LEN]; + UI32_T offset[CLX_NETIF_PROFILE_PATTERN_NUM]; + + /* for each flag 1:must hit, 0:don't care */ +#define CLX_NETIF_PROFILE_FLAGS_PORT (1UL << 0) +#define CLX_NETIF_PROFILE_FLAGS_REASON (1UL << 1) +#define CLX_NETIF_PROFILE_FLAGS_PATTERN_0 (1UL << 2) +#define CLX_NETIF_PROFILE_FLAGS_PATTERN_1 (1UL << 3) +#define CLX_NETIF_PROFILE_FLAGS_PATTERN_2 (1UL << 4) +#define CLX_NETIF_PROFILE_FLAGS_PATTERN_3 (1UL << 5) + UI32_T flags; + + CLX_NETIF_RX_DST_TYPE_T dst_type; +#if defined(NETIF_EN_NETLINK) + CLX_NETIF_RX_DST_NETLINK_T netlink; +#endif + +} CLX_NETIF_PROFILE_T; + +/* NAMING CONSTANT DECLARATIONS + */ +/* Keep these values applied to different modules. */ +#define HAL_PKT_IPP_EXCPT_NUM (256) +#define HAL_PKT_EPP_EXCPT_NUM (64) +#define HAL_PKT_IPP_L3_EXCPT_NUM (6) +#define HAL_PKT_IPP_RSN_NUM (16) +#define HAL_PKT_IPP_COPY2CPU_NUM (16) +#define HAL_PKT_EPP_COPY2CPU_NUM (8) + +/* IEV_CFG_EXCPT_EN_W1_SDK_REDIRECT_TO_CPU_L2UC_FIELD_ID */ +#define HAL_PKT_IPP_EXCPT_IEV_SDK_REDIRECT_TO_CPU_L2UC (192 + 23) + +/* IEV_CFG_EXCPT_EN_W1_SDK_REDIRECT_TO_CPU_L3UC_FIELD_ID */ +#define HAL_PKT_IPP_EXCPT_IEV_SDK_REDIRECT_TO_CPU_L3UC (192 + 24) + +/* IEV_CFG_EXCPT_EN_W1_SDK_L3UC_DA_MISS_FIELD_ID */ +#define HAL_PKT_IPP_EXCPT_IEV_SDK_L3UC_DA_MISS (192 + 30) + +/* IEV_CFG_EXCPT_EN_W1_SDK_L3MC_PIM_REGISTER_FIELD_ID */ +#define HAL_PKT_IPP_EXCPT_IEV_SDK_L3MC_PIM_REGISTER (192 + 31) + +/* IEV_CFG_EXCPT_EN_W0_SDK_FLEX_DECAP_0_REASON_0_FIELD_ID */ +#define HAL_PKT_IPP_EXCPT_IEV_SDK_FLEX_DECAP_0_REASON_0 (224 + 8) + +/* HW define offset + * Module write IEV.cp_to_cpu_idx=1-3 + * Module write IEV_CFG_CP_TO_CPU_BIT_POS[offset + cp_to_cpu_idx - 1] + */ +#define HAL_PKT_IEV_CP_TO_CPU_BIT_POS_L2UC_DA (0) +#define HAL_PKT_IEV_CP_TO_CPU_BIT_POS_L2MC_DA (3) +#define HAL_PKT_IEV_CP_TO_CPU_BIT_POS_L2SA (6) +#define HAL_PKT_IEV_CP_TO_CPU_BIT_POS_MPLS (9) +#define HAL_PKT_IEV_CP_TO_CPU_BIT_POS_TRILL (12) +#define HAL_PKT_IEV_CP_TO_CPU_BIT_POS_FCOE (15) +#define HAL_PKT_IEV_CP_TO_CPU_BIT_POS_L3UC_DA (18) +#define HAL_PKT_IEV_CP_TO_CPU_BIT_POS_L3UC_SA (21) +#define HAL_PKT_IEV_CP_TO_CPU_BIT_POS_L3MC (24) +#define HAL_PKT_IEV_CP_TO_CPU_BIT_POS_FLW_UC (27) +#define HAL_PKT_IEV_CP_TO_CPU_BIT_POS_FLW_MC (30) + +/* capacities are the same between CL8360 and CL8570 */ +#define HAL_PKT_IPP_EXCPT_BITMAP_SIZE (CLX_BITMAP_SIZE(HAL_PKT_IPP_EXCPT_NUM)) +#define HAL_PKT_IPP_L3_EXCPT_BITMAP_SIZE (CLX_BITMAP_SIZE(HAL_PKT_IPP_L3_EXCPT_NUM)) +#define HAL_PKT_EPP_EXCPT_BITMAP_SIZE (CLX_BITMAP_SIZE(HAL_PKT_EPP_EXCPT_NUM)) +#define HAL_PKT_IPP_RSN_BITMAP_SIZE (CLX_BITMAP_SIZE(HAL_PKT_IPP_RSN_NUM)) +#define HAL_PKT_IPP_COPY2CPU_BITMAP_SIZE (CLX_BITMAP_SIZE(HAL_PKT_IPP_COPY2CPU_NUM)) +#define HAL_PKT_EPP_COPY2CPU_BITMAP_SIZE (CLX_BITMAP_SIZE(HAL_PKT_EPP_COPY2CPU_NUM)) + +typedef UI32_T HAL_PKT_IPP_EXCPT_BITMAP_T[HAL_PKT_IPP_EXCPT_BITMAP_SIZE]; +typedef UI32_T HAL_PKT_IPP_L3_EXCPT_BITMAP_T[HAL_PKT_IPP_L3_EXCPT_BITMAP_SIZE]; +typedef UI32_T HAL_PKT_EPP_EXCPT_BITMAP_T[HAL_PKT_EPP_EXCPT_BITMAP_SIZE]; +typedef UI32_T HAL_PKT_IPP_RSN_BITMAP_T[HAL_PKT_IPP_RSN_BITMAP_SIZE]; +typedef UI32_T HAL_PKT_IPP_COPY2CPU_BITMAP_T[HAL_PKT_IPP_COPY2CPU_BITMAP_SIZE]; +typedef UI32_T HAL_PKT_EPP_COPY2CPU_BITMAP_T[HAL_PKT_EPP_COPY2CPU_BITMAP_SIZE]; + +typedef struct +{ + /* excpt */ + HAL_PKT_IPP_EXCPT_BITMAP_T ipp_excpt_bitmap; + HAL_PKT_IPP_L3_EXCPT_BITMAP_T ipp_l3_excpt_bitmap; + HAL_PKT_EPP_EXCPT_BITMAP_T epp_excpt_bitmap; + + /* cp */ + HAL_PKT_IPP_RSN_BITMAP_T ipp_rsn_bitmap; + HAL_PKT_IPP_COPY2CPU_BITMAP_T ipp_copy2cpu_bitmap; + HAL_PKT_EPP_COPY2CPU_BITMAP_T epp_copy2cpu_bitmap; + +} HAL_PKT_RX_REASON_BITMAP_T; + +#endif /* End of NETIF_KNL_H */ diff --git a/platform/clounix/clounix-modules/modules/src/inc/netif_nl.h b/platform/clounix/clounix-modules/modules/src/inc/netif_nl.h new file mode 100755 index 000000000000..cb46cfa5d92f --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/inc/netif_nl.h @@ -0,0 +1,105 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: netif_nl.h + * PURPOSE: + * It provide xxx API. + * NOTES: + */ + +#ifndef NETIF_NL_H +#define NETIF_NL_H + +#include + +#define NETIF_NL_NETLINK_MC_GROUP_NUM (32) +#define NETIF_NL_NETLINK_NAME_LEN (16) + +typedef enum +{ + NETIF_NL_INTF_PROPERTY_IGR_SAMPLING_RATE, + NETIF_NL_INTF_PROPERTY_EGR_SAMPLING_RATE, + NETIF_NL_INTF_PROPERTY_LAST +} NETIF_NL_INTF_PROPERTY_T; + +/* must be the same with CLX_NETIF_RX_DST_NETLINK_T */ +typedef struct +{ + C8_T name[NETIF_NL_NETLINK_NAME_LEN]; + C8_T mc_group_name[NETIF_NL_NETLINK_NAME_LEN]; +} NETIF_NL_RX_DST_NETLINK_T; + +/* must be the same with CLX_NETIF_NETLINK_MC_GROUP_T */ +typedef struct +{ + C8_T name[NETIF_NL_NETLINK_NAME_LEN]; + +} NETIF_NL_NETLINK_MC_GROUP_T; + +/* must be the same with CLX_NETIF_NETLINK_T */ +typedef struct +{ + UI32_T id; + C8_T name[NETIF_NL_NETLINK_NAME_LEN]; + NETIF_NL_NETLINK_MC_GROUP_T mc_group[NETIF_NL_NETLINK_MC_GROUP_NUM]; + UI32_T mc_group_num; + +} NETIF_NL_NETLINK_T; + +CLX_ERROR_NO_T +netif_nl_rxSkb( + const UI32_T unit, + struct sk_buff *ptr_skb, + void *ptr_cookie); + +CLX_ERROR_NO_T +netif_nl_setIntfProperty( + const UI32_T unit, + const UI32_T id, + const NETIF_NL_INTF_PROPERTY_T property, + const UI32_T param0, + const UI32_T param1); + +CLX_ERROR_NO_T +netif_nl_getIntfProperty( + const UI32_T unit, + const UI32_T port, + const NETIF_NL_INTF_PROPERTY_T property, + UI32_T *ptr_param0, + UI32_T *ptr_param1); + +CLX_ERROR_NO_T +netif_nl_createNetlink( + const UI32_T unit, + NETIF_NL_NETLINK_T *ptr_netlink, + UI32_T *ptr_netlink_id); + +CLX_ERROR_NO_T +netif_nl_destroyNetlink( + const UI32_T unit, + const UI32_T group_id); + +CLX_ERROR_NO_T +netif_nl_getNetlink( + const UI32_T unit, + const UI32_T netlink_id, + NETIF_NL_NETLINK_T *ptr_netlink); + + +CLX_ERROR_NO_T +netif_nl_init(void); + +#endif /* end of NETIF_NL_H */ diff --git a/platform/clounix/clounix-modules/modules/src/inc/netif_osal.h b/platform/clounix/clounix-modules/modules/src/inc/netif_osal.h new file mode 100755 index 000000000000..3f210b88e667 --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/inc/netif_osal.h @@ -0,0 +1,380 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: netif_osal.h + * PURPOSE: + * It provide customer linux API. + * NOTES: + */ + +#ifndef NETIF_OSAL_H +#define NETIF_OSAL_H + +/* + * ENOMEM : 12 - Out of memory + * EFAULT : 14 - Bad address + * EBUSY : 16 - Device or resource busy + * ENODEV : 19 - No such device + * EINVAL : 22 - Invalid argument + + * + * NETDEV_TX_OK : 0x00 + * NETDEV_TX_BUSY : 0x10 + + * + * ETH_HLEN : 14 dmac + smac + etyp + * ETH_ZLEN : 60 minimum ethernet frame size + * ETH_DATA_LEN : 1500 + * ETH_FRAME_LEN : 1514 + * ETH_FCS_LEN : 4 + * + * ETH_P_IP : 0x0800 + * ETH_P_ARP : 0x0806 + * ETH_P_IPV6 : 0x86DD + * ETH_P_SLOW : 0x8809 + * ETH_P_1588 : 0x88F7 + + * + * NET_IP_ALIGN : 2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* ----------------------------------------------------------------------------------- macro value */ +/* Thread */ +#define OSAL_THREAD_NAME_LEN (16) +#define OSAL_THREAD_DFT_NAME ("Unknown") + +/* Semaphore */ +#define OSAL_SEMA_NAME_LEN (16) +#define OSAL_SEMA_DFT_NAME ("Unknown") + +/* Event */ +#define OSAL_EVENT_NAME_LEN (16) +#define OSAL_EVENT_DFT_NAME ("Unknown") + +/* Spinlock */ +#define OSAL_SPIN_NAME_LEN (16) +#define OSAL_SPIN_DFT_NAME ("Unknown") + +/* Queue */ +#define OSAL_QUEUE_NAME_LEN (16) +#define OSAL_QUEUE_DFT_NAME ("Unknown") + +#define OSAL_PRN_BUF_SZ (256) +#define OSAL_TICKS_PER_SEC (1000000) + +/* ----------------------------------------------------------------------------------- struct */ +typedef struct linux_thread_s +{ + char name[OSAL_THREAD_NAME_LEN + 1]; + struct task_struct *ptr_task; + unsigned int is_stop; + struct linux_thread_s *ptr_prev; + struct linux_thread_s *ptr_next; + +} linux_thread_t; + +typedef struct +{ + char name[OSAL_SEMA_NAME_LEN + 1]; + struct semaphore lock; + +} linux_sema_t; + +typedef struct +{ + char name[OSAL_EVENT_NAME_LEN + 1]; + wait_queue_head_t wait_que; + unsigned int condition; + +} linux_event_t; + +typedef struct +{ + char name[OSAL_SPIN_NAME_LEN + 1]; + spinlock_t spinlock; + +} linux_isrlock_t; + +typedef struct +{ + void *ptr_data; +} linux_queue_entry_t; + +typedef struct +{ + char name[OSAL_QUEUE_NAME_LEN + 1]; + int head; /* index of the queue head entry can be read */ + int tail; /* index of the queue tail entry can be write */ + unsigned int wr_cnt; /* enqueue total count */ + unsigned int rd_cnt; /* dequeue total count */ + unsigned int capacity; /* the queue size */ + linux_queue_entry_t *ptr_entry; /* the queue entry buffer */ + +} linux_queue_t; + +typedef struct +{ + unsigned int size; + dma_addr_t phy_addr; + char data[0]; + +} linux_dma_t; + +/* ----------------------------------------------------------------------------------- function */ +void * +osal_memset( + void *ptr_mem, + const I32_T value, + const UI32_T num); + +void * +osal_memcpy( + void *ptr_dst, + const void *ptr_src, + const UI32_T num); + +UI32_T +osal_strlen( + const C8_T *ptr_str); + +void +osal_printf( + const C8_T *ptr_fmt, + ...); + +void * +osal_alloc( + const UI32_T size); + +void +osal_free( + const void *ptr_mem); + +/* thread */ +CLX_ERROR_NO_T +osal_init(void); + +CLX_ERROR_NO_T +osal_deinit(void); + +CLX_ERROR_NO_T +osal_createThread ( + const C8_T *ptr_thread_name, + const UI32_T stack_size, + const UI32_T priority, + void (function)(void*), + void *ptr_arg, + CLX_THREAD_ID_T *ptr_thread_id); + +CLX_ERROR_NO_T +osal_stopThread( + CLX_THREAD_ID_T *ptr_thread_id); + +CLX_ERROR_NO_T +osal_destroyThread( + CLX_THREAD_ID_T *ptr_thread_id); + +void +osal_initRunThread( + void); + +CLX_ERROR_NO_T +osal_isRunThread( + void); + +void +osal_exitRunThread( + void); + +/* semaphore */ +CLX_ERROR_NO_T +osal_createSemaphore( + const C8_T *ptr_sema_name, + const UI32_T sema_count, + CLX_SEMAPHORE_ID_T *ptr_semaphore_id); + +CLX_ERROR_NO_T +osal_takeSemaphore( + CLX_SEMAPHORE_ID_T *ptr_semaphore_id, + UI32_T time_out); + +CLX_ERROR_NO_T +osal_giveSemaphore( + CLX_SEMAPHORE_ID_T *ptr_semaphore_id); + +CLX_ERROR_NO_T +osal_destroySemaphore( + CLX_SEMAPHORE_ID_T *ptr_semaphore_id); + +/* event */ +CLX_ERROR_NO_T +osal_createEvent( + const C8_T *ptr_event_name, + CLX_SEMAPHORE_ID_T *ptr_event_id); + +CLX_ERROR_NO_T +osal_waitEvent( + CLX_SEMAPHORE_ID_T *ptr_event_id); + +CLX_ERROR_NO_T +osal_triggerEvent( + CLX_SEMAPHORE_ID_T *ptr_event_id); + +CLX_ERROR_NO_T +osal_destroyEvent( + CLX_SEMAPHORE_ID_T *ptr_event_id); + +/* isr_lock */ +CLX_ERROR_NO_T +osal_createIsrLock( + const C8_T *ptr_isrlock_name, + CLX_ISRLOCK_ID_T *ptr_isrlock_id); + +CLX_ERROR_NO_T +osal_takeIsrLock( + CLX_ISRLOCK_ID_T *ptr_isrlock_id, + CLX_IRQ_FLAGS_T *ptr_irq_flags); + +CLX_ERROR_NO_T +osal_giveIsrLock( + CLX_ISRLOCK_ID_T *ptr_isrlock_id, + CLX_IRQ_FLAGS_T *ptr_irq_flags); + +CLX_ERROR_NO_T +osal_destroyIsrLock( + CLX_ISRLOCK_ID_T *ptr_isrlock_id); + +/* timer */ +CLX_ERROR_NO_T +osal_sleepThread( + const UI32_T usecond); + +CLX_ERROR_NO_T +osal_getTime( + CLX_TIME_T *ptr_time); + +/* queue */ +CLX_ERROR_NO_T +osal_que_create( + CLX_HUGE_T *ptr_queue_id, + UI32_T capacity); + +CLX_ERROR_NO_T +osal_que_enque( + CLX_HUGE_T *ptr_queue_id, + void *ptr_data); + +CLX_ERROR_NO_T +osal_que_deque( + CLX_HUGE_T *ptr_queue_id, + void **pptr_data); + +CLX_ERROR_NO_T +osal_que_destroy( + CLX_HUGE_T *ptr_queue_id); + +CLX_ERROR_NO_T +osal_que_getCount( + CLX_HUGE_T *ptr_queue_id, + unsigned int *ptr_count); + +/* IO */ +int +osal_io_copyToUser( + void *ptr_usr_buf, + void *ptr_knl_buf, + unsigned int size); + +int +osal_io_copyFromUser( + void *ptr_knl_buf, + void *ptr_usr_buf, + unsigned int size); + +/* dma */ +void * +osal_dma_alloc( + const UI32_T size); + +CLX_ERROR_NO_T +osal_dma_free( + void *ptr_dma_mem); + +dma_addr_t +osal_dma_convertVirtToPhy( + void *ptr_virt_addr); + +void * +osal_dma_convertPhyToVirt( + const dma_addr_t phy_addr); + +int +osal_dma_flushCache( + void *ptr_virt_addr, + const unsigned int size); + +int +osal_dma_invalidateCache( + void *ptr_virt_addr, + const unsigned int size); + +/* skb */ +struct sk_buff * +osal_skb_alloc( + UI32_T size); + +void +osal_skb_free( + struct sk_buff *ptr_skb); + +dma_addr_t +osal_skb_mapDma( + struct sk_buff *ptr_skb, + enum dma_data_direction dir); + +void +osal_skb_unmapDma( + const dma_addr_t phy_addr, + UI32_T size, + enum dma_data_direction dir); + +void +osal_skb_send( + struct sk_buff *ptr_skb); + +void +osal_skb_recv( + struct sk_buff *ptr_skb); + +#endif /* end of NETIF_OSAL_H */ diff --git a/platform/clounix/clounix-modules/modules/src/inc/netif_perf.h b/platform/clounix/clounix-modules/modules/src/inc/netif_perf.h new file mode 100755 index 000000000000..fab397ccebec --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/inc/netif_perf.h @@ -0,0 +1,82 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: netif_perf.h + * PURPOSE: + * It provide customer performance test API. + * NOTES: + */ + +#ifndef NETIF_PERF_H +#define NETIF_PERF_H + +/* #define PERF_EN_TEST */ + +/* FUNCTION NAME: perf_rxCallback + * PURPOSE: + * To count the Rx-gpd for Rx-test. + * INPUT: + * len -- To check if the Rx-gpd length equals to test length. + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successful operation. + * NOTES: + * None + */ +CLX_ERROR_NO_T +perf_rxCallback( + const UI32_T len); + +/* FUNCTION NAME: perf_rxTest + * PURPOSE: + * To check if Rx-test is going. + * INPUT: + * None + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successful operation. + * NOTES: + * None + */ +CLX_ERROR_NO_T +perf_rxTest( + void); + +/* FUNCTION NAME: perf_test + * PURPOSE: + * To do Tx-test or Rx-test. + * INPUT: + * len -- Test length + * tx_channel -- Test Tx channel numbers + * rx_channel -- Test Rx channel numbers + * test_skb -- Test GPD or SKB + * OUTPUT: + * None + * RETURN: + * CLX_E_OK -- Successful operation. + * NOTES: + * None + */ +CLX_ERROR_NO_T +perf_test( + UI32_T len, + UI32_T tx_channel, + UI32_T rx_channel, + BOOL_T test_skb); + +#endif /* end of NETIF_PERF_H */ diff --git a/platform/clounix/clounix-modules/modules/src/inc/osal/osal_mdc.h b/platform/clounix/clounix-modules/modules/src/inc/osal/osal_mdc.h new file mode 100644 index 000000000000..086740ed8672 --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/inc/osal/osal_mdc.h @@ -0,0 +1,262 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: osal_mdc.h + * PURPOSE: + * 1. Provide device operate from AML interface + * NOTES: + * + */ + +#ifndef OSAL_MDC_H +#define OSAL_MDC_H + +/* INCLUDE FILE DECLARATIONS */ +#if !defined(CLX_LINUX_KERNEL_MODE) +#include +#endif +#include +#define CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM (16) +#include + +#define OSAL_MDC_DRIVER_NAME "clx_dev" +#define OSAL_MDC_DRIVER_MISC_MAJOR_NUM (10) +#define OSAL_MDC_DRIVER_MISC_MINOR_NUM (250) +#define OSAL_MDC_PCI_BUS_WIDTH (4) + +#define OSAL_MDC_DMA_LIST_SZ_UNLIMITED (0) +#define OSAL_MDC_DMA_LIST_NAME "RSRV_DMA" +#define OSAL_MDC_DMA_SEMAPHORE_NAME "DMALIST" + +/* NAMING CONSTANT DECLARATIONS + */ + +/* linked list node */ +#if defined(CLX_LINUX_KERNEL_MODE) + +typedef struct OSAL_MDC_LIST_NODE_S +{ + void *ptr_data; /* node data */ + struct OSAL_MDC_LIST_NODE_S *ptr_next; /* point to next link node */ + struct OSAL_MDC_LIST_NODE_S *ptr_prev; /* point to previous link node */ +} OSAL_MDC_LIST_NODE_T; + +/* linked list head */ +typedef struct OSAL_MDC_LIST_S +{ + OSAL_MDC_LIST_NODE_T *ptr_head_node; /* linked list head node */ + OSAL_MDC_LIST_NODE_T *ptr_tail_node; /* linked list tail node */ + UI32_T capacity; /* max count of nodes in list + * size=0: the capacity is unlimited. + * size>0: the capacity is limited. + */ + UI32_T node_cnt; /* the count of nodes in the list */ +} OSAL_MDC_LIST_T; + +#endif /* End of defined(CLX_LINUX_KERNEL_MODE) */ + +typedef struct +{ + CLX_ADDR_T phy_addr; + void *ptr_virt_addr; + CLX_ADDR_T size; + +#if defined(CLX_EN_DMA_RESERVED) + BOOL_T available; +#endif + +} OSAL_MDC_DMA_NODE_T; + +typedef struct +{ +#if defined(CLX_EN_DMA_RESERVED) + void *ptr_rsrv_virt_addr; + CLX_ADDR_T rsrv_phy_addr; + CLX_ADDR_T rsrv_size; +#else + struct device *ptr_dma_dev; /* for allocate/free system memory */ +#endif + +#if defined(CLX_LINUX_KERNEL_MODE) + OSAL_MDC_LIST_T *ptr_dma_list; +#else + CMLIB_LIST_T *ptr_dma_list; +#endif + + CLX_SEMAPHORE_ID_T sema; + +} OSAL_MDC_DMA_INFO_T; + +#if defined(CLX_LINUX_USER_MODE) + +/* Data type of IOCTL argument for DMA management */ +typedef struct +{ +#if defined(CLX_EN_DMA_RESERVED) + CLX_ADDR_T rsrv_dma_phy_addr; /* information of reserved memory */ + CLX_ADDR_T rsrv_dma_size; +#else + CLX_ADDR_T phy_addr; /* information of system memory */ + CLX_ADDR_T size; +#endif +} OSAL_MDC_IOCTL_DMA_DATA_T; + +/* Data type of IOCTL argument for device initialization */ +#pragma pack (push,1) +typedef struct +{ + AML_DEV_ID_T id[CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM]; + CLX_ADDR_T pci_mmio_phy_start[CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM]; + CLX_ADDR_T pci_mmio_size[CLX_CFG_MAXIMUM_CHIPS_PER_SYSTEM]; + UI32_T dev_num; +} OSAL_MDC_IOCTL_DEV_DATA_T; +#pragma pack (pop) + +typedef enum +{ + OSAL_MDC_IOCTL_ACCESS_READ = 0, + OSAL_MDC_IOCTL_ACCESS_WRITE, + OSAL_MDC_IOCTL_ACCESS_READ_WRITE, + OSAL_MDC_IOCTL_ACCESS_NONE, + OSAL_MDC_IOCTL_ACCESS_LAST + +} OSAL_MDC_IOCTL_ACCESS_T; + +typedef enum +{ + OSAL_MDC_IOCTL_TYPE_MDC_INIT_DEV = 0, + OSAL_MDC_IOCTL_TYPE_MDC_DEINIT_DEV, + OSAL_MDC_IOCTL_TYPE_MDC_INIT_RSRV_DMA_MEM, + OSAL_MDC_IOCTL_TYPE_MDC_DEINIT_RSRV_DMA_MEM, + OSAL_MDC_IOCTL_TYPE_MDC_ALLOC_SYS_DMA_MEM, + OSAL_MDC_IOCTL_TYPE_MDC_FREE_SYS_DMA_MEM, + OSAL_MDC_IOCTL_TYPE_MDC_CONNECT_ISR, + OSAL_MDC_IOCTL_TYPE_MDC_DISCONNECT_ISR, + OSAL_MDC_IOCTL_TYPE_MDC_SAVE_PCI_CONFIG, + OSAL_MDC_IOCTL_TYPE_MDC_RESTORE_PCI_CONFIG, + OSAL_MDC_IOCTL_TYPE_LAST + +} OSAL_MDC_IOCTL_TYPE_T; + +typedef union +{ + UI32_T value; + struct + { + UI32_T access : 2; /* 0:read, 1:write, 2:read and write, 3:none */ + UI32_T unit : 6; /* Maximum unit number is 64. */ + UI32_T size :14; /* Maximum IOCTL data size is 16KB. */ + UI32_T type :10; /* Maximum 1024 IOCTL types */ + } field; +} OSAL_MDC_IOCTL_CMD_T; + +typedef CLX_ERROR_NO_T +(*OSAL_MDC_IOCTL_CALLBACK_FUNC_T)( + const UI32_T unit, + void *ptr_data); + +#endif /* End of CLX_LINUX_USER_MODE */ + + +/* MACRO FUNCTION DECLARATIONS + */ + +/* DATA TYPE DECLARATIONS + */ + +/* EXPORTED SUBPROGRAM SPECIFICATIONS + */ +CLX_ERROR_NO_T +osal_mdc_readPciReg( + const UI32_T unit, + const UI32_T offset, + UI32_T *ptr_data, + const UI32_T len); + +CLX_ERROR_NO_T +osal_mdc_writePciReg( + const UI32_T unit, + const UI32_T offset, + const UI32_T *ptr_data, + const UI32_T len); + +CLX_ERROR_NO_T +osal_mdc_initDevice( + AML_DEV_T *ptr_dev_list, + UI32_T *ptr_dev_num); + +CLX_ERROR_NO_T +osal_mdc_deinitDevice(void); + +CLX_ERROR_NO_T +osal_mdc_initDmaMem(void); + +CLX_ERROR_NO_T +osal_mdc_deinitDmaMem(void); + +void * +osal_mdc_allocDmaMem( + const UI32_T size); + +CLX_ERROR_NO_T +osal_mdc_freeDmaMem( + void *ptr_virt_addr); + +CLX_ERROR_NO_T +osal_mdc_convertVirtToPhy( + void *ptr_virt_addr, + CLX_ADDR_T *ptr_phy_addr); + +CLX_ERROR_NO_T +osal_mdc_convertPhyToVirt( + const CLX_ADDR_T phy_addr, + void **pptr_virt_addr); + +CLX_ERROR_NO_T +osal_mdc_registerIsr( + const UI32_T unit, + AML_DEV_ISR_FUNC_T handler, + void *ptr_cookie); + +CLX_ERROR_NO_T +osal_mdc_connectIsr( + const UI32_T unit, + AML_DEV_ISR_FUNC_T handler, + AML_DEV_ISR_DATA_T *ptr_cookie); + +CLX_ERROR_NO_T +osal_mdc_disconnectIsr( + const UI32_T unit); + +CLX_ERROR_NO_T +osal_mdc_flushCache( + void *ptr_virt_addr, + const UI32_T size); + +CLX_ERROR_NO_T +osal_mdc_invalidateCache( + void *ptr_virt_addr, + const UI32_T size); + +CLX_ERROR_NO_T +osal_mdc_savePciConfig( + const UI32_T unit); + +CLX_ERROR_NO_T +osal_mdc_restorePciConfig( + const UI32_T unit); + +#endif /* OSAL_MDC_H */ diff --git a/platform/clounix/clounix-modules/modules/src/inc/osal/osal_types.h b/platform/clounix/clounix-modules/modules/src/inc/osal/osal_types.h new file mode 100644 index 000000000000..1bcea9a83a79 --- /dev/null +++ b/platform/clounix/clounix-modules/modules/src/inc/osal/osal_types.h @@ -0,0 +1,318 @@ +/* + * Copyright 2022 Clounix + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* FILE NAME: osal_types.h + * PURPOSE: + * Define the commom data type in CLX SDK. + * NOTES: + */ + +#ifndef OSAL_TYPES_H +#define OSAL_TYPES_H + +/* INCLUDE FILE DECLARATIONS + */ + +/* NAMING CONSTANT DECLARATIONS + */ + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef NULL +#define NULL (void *)0 +#endif + +#if defined(CLX_EN_HOST_64_BIT_BIG_ENDIAN) +#define UI64_MSW 0 +#define UI64_LSW 1 +#elif defined(CLX_EN_HOST_64_BIT_LITTLE_ENDIAN) +#define UI64_MSW 1 +#define UI64_LSW 0 +#else +#define UI64_MSW 1 +#define UI64_LSW 0 +#endif + +/* DATA TYPE DECLARATIONS + */ +typedef int BOOL_T; +typedef signed char I8_T; +typedef unsigned char UI8_T; +typedef signed short I16_T; +typedef unsigned short UI16_T; +typedef signed int I32_T; +typedef unsigned int UI32_T; +typedef char C8_T; + +#if defined(CLX_EN_COMPILER_SUPPORT_LONG_LONG) +typedef signed long long int I64_T; +typedef unsigned long long int UI64_T; +#else +typedef struct +{ + I32_T i64[2]; +} I64_T; + +typedef struct +{ + UI32_T ui64[2]; +} UI64_T; +#endif + +#if defined(CLX_EN_COMPILER_SUPPORT_LONG_LONG) +#define UI64_HI(dst) ((UI32_T)((dst) >> 32)) +#define UI64_LOW(dst) ((UI32_T)((dst) & 0xffffffff)) +#define UI64_ASSIGN(dst, high, low) ((dst) = (((UI64_T)(high)) << 32 | (UI64_T)(low))) +#define UI64_SET(dst, src) ((dst) = (src)) +#define UI64_ADD_UI32(dst, src) ((dst) += ((UI64_T)(src))) +#define UI64_SUB_UI32(dst, src) ((dst) -= ((UI64_T)(src))) +#define UI64_ADD_UI64(dst, src) ((dst) += (src)) +#define UI64_SUB_UI64(dst, src) ((dst) -= (src)) +#define UI64_AND(dst, src) ((dst) &= (src)) +#define UI64_OR(dst, src) ((dst) |= (src)) +#define UI64_XOR(dst, src) ((dst) ^= (src)) +#define UI64_NOT(dst) ((dst) = ~(dst)) +#define UI64_MULT_UI32(dst, src) ((dst) *= (src)) +#define UI64_MULT_UI64(dst, src) ((dst) *= (src)) +/* UI64_T type data comparion: + * if data1 > data2 return 1 + * if data1 < data2 return -1 + * if data1 == data2 return 0 + */ +#define UI64_CMP(data1, data2) (((data1) > (data2)) ? 1 : (((data1) < (data2)) ? -1 : 0)) + +#else +#define UI64_HI(dst) ((dst).ui64[UI64_MSW]) +#define UI64_LOW(dst) ((dst).ui64[UI64_LSW]) +#define UI64_ASSIGN(dst, high, low) \ + do \ + { \ + UI64_HI(dst) = (high); \ + UI64_LOW(dst) = (low); \ + } while(0) + +#define UI64_SET(dst, src) \ + do \ + { \ + UI64_HI(dst) = UI64_HI(src); \ + UI64_LOW(dst) = UI64_LOW(src); \ + } while(0) + + +#define UI64_ADD_UI32(dst, src) \ + do \ + { \ + UI32_T _i_ = UI64_LOW(dst); \ + UI64_LOW(dst) += (src); \ + if (UI64_LOW(dst) < _i_) \ + { \ + UI64_HI(dst)++; \ + } \ + } while(0) + +#define UI64_SUB_UI32(dst, src) \ + do \ + { \ + UI32_T _i_ = UI64_LOW(dst); \ + UI64_LOW(dst) -= src; \ + if (UI64_LOW(dst) > _i_) \ + { \ + UI64_HI(dst)--; \ + } \ + } while(0) + + +#define UI64_ADD_UI64(dst, src) \ + do \ + { \ + UI32_T _i_ = UI64_LOW(dst); \ + UI64_LOW(dst) += UI64_LOW(src); \ + if (UI64_LOW(dst) < _i_) \ + { \ + UI64_HI(dst)++; \ + } \ + UI64_HI(dst) += UI64_HI(src); \ + } while(0) + +#define UI64_SUB_UI64(dst, src) \ + do { \ + UI32_T _i_ = UI64_LOW(dst); \ + UI64_LOW(dst) -= UI64_LOW(src); \ + if (UI64_LOW(dst) > _i_) \ + { \ + UI64_HI(dst)--; \ + } \ + UI64_HI(dst) -= UI64_HI(src); \ + } while(0) + + +#define UI64_AND(dst, src) \ + do { \ + UI64_HI(dst) &= UI64_HI(src); \ + UI64_LOW(dst) &= UI64_LOW(src); \ + } while(0) + +#define UI64_OR(dst, src) \ + do { \ + UI64_HI(dst) |= UI64_HI(src); \ + UI64_LOW(dst) |= UI64_LOW(src); \ + } while(0) + +#define UI64_XOR(dst, src) \ + do { \ + UI64_HI(dst) ^= UI64_HI(src); \ + UI64_LOW(dst) ^= UI64_LOW(src); \ + } while(0) + +#define UI64_NOT(dst) \ + do { \ + UI64_HI(dst) = ~UI64_HI(dst); \ + UI64_LOW(dst) = ~UI64_LOW(dst); \ + } while(0) + +/* UI64_T type data comparion: + * if data1 > data2 return 1 + * if data1 < data2 return -1 + * if data1 == data2 return 0 + */ +#define UI64_CMP(data1, data2) \ + (((data1).ui64[UI64_MSW] > (data2).ui64[UI64_MSW]) \ + ? 1 : (((data1).ui64[UI64_MSW] == (data2).ui64[UI64_MSW]) \ + ? (((data1).ui64[UI64_LSW] == (data2).ui64[UI64_LSW]) \ + ? 0 :(((data1).ui64[UI64_LSW] > (data2).ui64[UI64_LSW]) \ + ? 1 : -1)) : -1)) + +#define UI64_MULT_UI64(dst, src) \ + do \ + { \ + UI32_T _ret_low_ = 0; \ + UI32_T _ret_high_ = 0; \ + UI32_T _i_ = 0; \ + UI32_T _j_ = 0; \ + UI32_T _temp_ = 0; \ + UI32_T dst_t[4] = {0, 0, 0, 0}; \ + UI32_T src_t[4] = {0, 0, 0, 0}; \ + dst_t[0] = UI64_LOW(dst) & 0xFFFF; \ + dst_t[1] = UI64_LOW(dst) >> 16; \ + dst_t[2] = UI64_HI(dst) & 0xFFFF; \ + dst_t[3] = UI64_HI(dst) >> 16; \ + src_t[0] = UI64_LOW(src) & 0xFFFF; \ + src_t[1] = UI64_LOW(src) >> 16; \ + src_t[2] = UI64_HI(src) & 0xFFFF; \ + src_t[3] = UI64_HI(src) >> 16; \ + for(_i_ = 0; _i_ < 4; _i_++) \ + { \ + for(_j_ = 0; _j_ < 4; _j_++) \ + { \ + if((dst_t[_i_] != 0) && (src_t[_j_] != 0)) \ + { \ + _temp_ = dst_t[_i_] * src_t[_j_]; \ + if(0 == (_i_ + _j_)) \ + { \ + _ret_low_ += _temp_; \ + if (_ret_low_ < _temp_) \ + { \ + _ret_high_++; \ + } \ + } \ + if(1 == (_i_ + _j_)) \ + { \ + _ret_low_ += (_temp_ << 16); \ + if (_ret_low_ < (_temp_ << 16)) \ + { \ + _ret_high_++; \ + } \ + _ret_high_ += (_temp_ >> 16); \ + } \ + if(2 == (_i_+_j_)) \ + { \ + _ret_high_ += _temp_; \ + } \ + if(3 == (_i_ + _j_)) \ + { \ + _ret_high_ += (_temp_ << 16); \ + } \ + } \ + } \ + } \ + UI64_HI(dst) = _ret_high_; \ + UI64_LOW(dst) = _ret_low_; \ + } while(0) + +#define UI64_MULT_UI32(dst, src) \ + do \ + { \ + UI32_T _ret_low_ = 0; \ + UI32_T _ret_high_ = 0; \ + UI32_T _i_ = 0; \ + UI32_T _j_ = 0; \ + UI32_T _temp_ = 0; \ + UI32_T dst_t[4] = {0, 0, 0, 0}; \ + UI32_T src_t[2] = {0, 0}; \ + dst_t[0] = UI64_LOW(dst) & 0xFFFF; \ + dst_t[1] = UI64_LOW(dst) >> 16; \ + dst_t[2] = UI64_HI(dst) & 0xFFFF; \ + dst_t[3] = UI64_HI(dst) >> 16; \ + src_t[0] = src & 0xFFFF; \ + src_t[1] = src >> 16; \ + for(_i_ = 0; _i_ < 4; _i_++) \ + { \ + for(_j_ = 0; _j_ < 2; _j_++) \ + { \ + if((dst_t[_i_] != 0) && (src_t[_j_] != 0)) \ + { \ + _temp_ = dst_t[_i_] * src_t[_j_]; \ + if(0 == (_i_ + _j_)) \ + { \ + _ret_low_ += _temp_; \ + if (_ret_low_ < _temp_) \ + { \ + _ret_high_++; \ + } \ + } \ + if(1 == (_i_ + _j_)) \ + { \ + _ret_low_ += (_temp_ << 16); \ + if (_ret_low_ < (_temp_ << 16)) \ + { \ + _ret_high_++; \ + } \ + _ret_high_ += (_temp_ >> 16); \ + } \ + if(2 == (_i_ + _j_)) \ + { \ + _ret_high_ += _temp_; \ + } \ + if(3 == (_i_ + _j_)) \ + { \ + _ret_high_ += (_temp_ << 16); \ + } \ + } \ + } \ + } \ + UI64_HI(dst) = _ret_high_; \ + UI64_LOW(dst) = _ret_low_; \ + } while(0) + +#endif + +#endif /* OSAL_TYPES_H */ diff --git a/platform/clounix/clx-utils.dep b/platform/clounix/clx-utils.dep new file mode 100644 index 000000000000..683255bd4227 --- /dev/null +++ b/platform/clounix/clx-utils.dep @@ -0,0 +1,9 @@ + +SPATH := $($(CLX_UTILS)_SRC_PATH) +DEP_FILES := $(SONIC_COMMON_FILES_LIST) platform/clounix/clx-utils.mk platform/clounix/clx-utils.dep +DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST) +DEP_FILES += $(shell git ls-files $(SPATH)) + +$(CLX_UTILS)_CACHE_MODE := GIT_CONTENT_SHA +$(CLX_UTILS)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST) +$(CLX_UTILS)_DEP_FILES := $(DEP_FILES) diff --git a/platform/clounix/clx-utils.mk b/platform/clounix/clx-utils.mk new file mode 100644 index 000000000000..455bea19611b --- /dev/null +++ b/platform/clounix/clx-utils.mk @@ -0,0 +1,4 @@ +CLX_UTILS = python-clx-utils_0.1-1_all.deb +$(CLX_UTILS)_SRC_PATH = $(PLATFORM_PATH)/clx-utils + +SONIC_MAKE_DEBS += $(CLX_UTILS) diff --git a/platform/clounix/clx-utils/Makefile b/platform/clounix/clx-utils/Makefile new file mode 100644 index 000000000000..4960c050841f --- /dev/null +++ b/platform/clounix/clx-utils/Makefile @@ -0,0 +1,16 @@ +.ONESHELL: +SHELL = /bin/bash +.SHELLFLAGS += -e + +MAIN_TARGET = python-clx-utils_0.1-1_all.deb + +$(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% : + pushd ./clounix-utils + + # Build source and Debian packages + rm -rf deb_dist/* + python setup.py --command-packages=stdeb.command bdist_deb + popd + + # Move the newly-built .deb package to the destination directory + mv ./clounix-utils/deb_dist/$* $(DEST)/ diff --git a/platform/clounix/clx-utils/clounix-utils/port_breakout/__init__.py b/platform/clounix/clx-utils/clounix-utils/port_breakout/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/platform/clounix/clx-utils/clounix-utils/port_breakout/api_libpcfgparsing.txt b/platform/clounix/clx-utils/clounix-utils/port_breakout/api_libpcfgparsing.txt new file mode 100644 index 000000000000..5d17b08b854d --- /dev/null +++ b/platform/clounix/clx-utils/clounix-utils/port_breakout/api_libpcfgparsing.txt @@ -0,0 +1,42 @@ + +# parse_port_config_ini(config_file_path) +{ + 'phy_port_info':{ + physical port:{ + 'user_port':{ name:{ + 'lanes':[logic_lane1, logic_lane2...], + 'alias':'', + 'max-speed':dd + } + } + 'lane_list': [logic_lane1, logic_lane2] + } + } + 'logical_lane_2_phy_port':{ + logic_lane: physical port + } + 'user_port_2_phy_port': { + user_port: phy port + } +} + +# parse_clx_dsh(config_file_path) +{ + 'clx_port_2_logic_lane': { + clx_port(unit,port): [logic_lane1, logic_lane2] + }, + 'logic_lane_2_clx_port': { + logic_lane: clx_port(unit,port) + }, + 'pre-emphasis': { + # key: value + (logic_lane, property): hex + }, + 'polarity-rev': + # key: value + (logic_lane, property): hex + 'lane-swap': + # key: value + (logic_lane, property): hex +} + diff --git a/platform/clounix/clx-utils/clounix-utils/port_breakout/libpcfgparsing.py b/platform/clounix/clx-utils/clounix-utils/port_breakout/libpcfgparsing.py new file mode 100644 index 000000000000..042f6d58e899 --- /dev/null +++ b/platform/clounix/clx-utils/clounix-utils/port_breakout/libpcfgparsing.py @@ -0,0 +1,616 @@ +#import os.path +import re +import logging +import subprocess +#from pprint import pprint, pformat +from pprint import pprint +#from collections import OrderedDict + +KEY_CLX_PORT_INFO = 'clx_port_info' +KEY_CLX_PORT_INFO_V_ETH_MACRO = 'eth-macro' +KEY_CLX_PORT_INFO_V_CLX_LANE = 'clx-lane' +KEY_CLX_PORT_INFO_V_MAX_SPEED = 'max-speed' +KEY_CLX_PORT_INFO_V_PLANE = 'plane' +KEY_CLX_PORT_INFO_V_PPID = 'ppid' + +KEY_PRE_EMPHASIS = 'pre-emphasis' +KEY_POLARITY_REV = 'polarity-rev' +KEY_LANE_SWAP = 'lane-swap' +KEY_MEDIUM_TYPE = 'medium-type' +KEY_AN = 'an' +KEY_ADVER = 'adver' +KEY_ADMIN = 'admin' +KEY_CPI_PORT_INFO = 'cpi_port_info' + +KEY_PHY_PORT_INFO = 'phy_port_info' +KEY_PHY_PORT_INFO_V_LANE_LIST = 'lane_list' +KEY_PHY_PORT_INFO_V_USER_PORT = 'user_port' +KEY_PHY_PORT_INFO_V_USER_PORT_V_ALIAS = 'alias' +KEY_PHY_PORT_INFO_V_USER_PORT_V_LANES = 'lanes' +KEY_PHY_PORT_INFO_V_USER_PORT_V_MAX_SPEED = 'max-speed' + +KEY_LOGIC_LANE_2_PHY_PORT = 'logical_lane_2_phy_port' +KEY_USER_PORT_2_PHY_PORT = 'user_port_2_phy_port' +KEY_CLX_PORT_2_LOGIC_LANE = 'clx_port_2_logic_lane' +KEY_LOGIC_LANE_2_CLX_PORT = 'logic_lane_2_clx_port' +KEY_CPI_PORT_MAP_DATA = 'cpi_port_map_data' +CPI_PORT_LIST = ["129", "130"] + +logger=logging.getLogger() + + +def _run_diag_command(command, verbose=False): + cmd = ['clx_diag', command ] + mapping_str_buffer = subprocess.check_output(cmd) + if verbose: + print pprint(mapping_str_buffer) + return mapping_str_buffer + + +def _diag_show_info_port(): + """ + run clx_diag cmd to get clx_port and macro lane mapping + :return: + """ + + # get clx_port to macro and lane mapping + # unit/port eth-macro lane max-speed act gua bin plane hw-mac tm-mac mpid ppid + # 0/ 0 0 0 100000 1 0 0 1 16 0 0 0 + cmd = 'diag show info port' + mapping_str_buffer = _run_diag_command(cmd) + return mapping_str_buffer + + +def parse_diag_show_info_port(): + """ + parsing diag show info port output + :param: None + :return: + {'clx_port_info':{ clx_port: { + 'eth-macro': eth-macro, + 'clx-lane': clx-lane, + 'max-speed': max-speed, + 'plane': plane, + 'ppid': ppid + } + } + } + """ + diag_show = _diag_show_info_port() + # unit/port eth-macro lane max-speed act gua bin plane hw-mac tm-mac mpid ppid + # 0/ 0 0 0 100000 1 0 0 1 16 0 0 0 + pr_str = r'''^\s+(?P\d+)\s*/\s*(?P\d+)\s+ #0/0 + (?P\d+)\s+ #eth-macro + (?P\d+)\s+ #lane + (?P\d+)\s+ #max-speed + \d+\s+ #act + \d+\s+ #gua + \d+\s+ #bin + (?P\d+)\s+ #plane + \d+\s+ #hw-mac + \d+\s+ #tm-mac + \d+\s+ #mpid + (?P\d+) #ppid + .*$''' + pat = re.compile(pr_str, re.I | re.M | re.X) + result = pat.finditer(diag_show) + + data = { + KEY_CLX_PORT_INFO: dict() + } + for p in result: + unit = p.group('unit') + port = p.group('port') + eth_macro = p.group('macro') + clx_lane = p.group('clx_lane') + max_speed = p.group('speed') + plane = p.group('plane') + ppid = p.group('ppid') + + data[KEY_CLX_PORT_INFO][(unit, port)] = { + KEY_CLX_PORT_INFO_V_ETH_MACRO: eth_macro, + KEY_CLX_PORT_INFO_V_CLX_LANE: clx_lane, + KEY_CLX_PORT_INFO_V_MAX_SPEED: max_speed, + KEY_CLX_PORT_INFO_V_PLANE: plane, + KEY_CLX_PORT_INFO_V_PPID: ppid + } + return data + + +def parse_port_config_ini(port_config_file): + """ + parse phy port to (eth-group, lane) mapping from config file + :param: port_config_file + :return: + { + 'phy_port_info':{ + physical port:{ + 'user_port':{ name:{ + 'lanes':[logic_lane1, logic_lane2...], + 'alias':'', + 'max-speed':dd + } + } + 'lane_list': [logic_lane1, logic_lane2] + } + } + 'logical_lane_2_phy_port':{ + logic_lane: physical port + } + 'user_port_2_phy_port': { + user_port: phy port + } + } + """ + with open(port_config_file, mode='r') as f: + config = f.read() + + # match content of port_config.ini: + # name lanes alias index speed + # Ethernet0 0,1,2,3 Ethernet1/1 0 100000 + title = [] + data_dict = { + KEY_PHY_PORT_INFO: dict(), + KEY_LOGIC_LANE_2_PHY_PORT: dict(), + KEY_USER_PORT_2_PHY_PORT: dict() + } + for line in config.split('\n'): + if re.search('^#', line): + # The current format is: # name lanes alias index speed + title = line.split()[1:] + continue + content = line.split() + if len(content) == 0 or len(content) != len(title) : # skip blank or invalid line + continue + user_port_name = content[title.index('name')] + logic_lanes = content[title.index('lanes')] + logic_lane_list = logic_lanes.split(',') + user_port_alias = content[title.index('alias')] + phy_port = content[title.index('index')] + if 'speed' in title: + max_speed = content[title.index('speed')] + else: + max_speed = str(25000 * len(logic_lane_list)) + + # update user_port_2_phy_port_mapping + data_dict[KEY_USER_PORT_2_PHY_PORT][user_port_name] = phy_port + + # update logical_lane_2_phy_port mapping + for logic_lane in logic_lane_list: + data_dict[KEY_LOGIC_LANE_2_PHY_PORT][logic_lane] = phy_port + + # -- update phy_port_info -- + # phy_port_info->value + phy_port_info = data_dict[KEY_PHY_PORT_INFO] + if phy_port not in phy_port_info: + phy_port_info[phy_port] = dict() + phy_port_info_value = phy_port_info[phy_port] + + # phy_port_info->value->user_port->value + k = KEY_PHY_PORT_INFO_V_USER_PORT + if k not in phy_port_info_value: + phy_port_info_value[k] = OrderedDict() + all_user_port_value = phy_port_info_value[k] + + # get phy_port_info->value->user_port->value->(port_name)->value + if user_port_name not in all_user_port_value: + all_user_port_value[user_port_name] = dict() + cur_user_port_value = all_user_port_value[user_port_name] + + # update phy_port_info->value->user_port->value->(port_name)->value->alias->value + cur_user_port_value[KEY_PHY_PORT_INFO_V_USER_PORT_V_ALIAS] = user_port_alias + + # update phy_port_info->value->user_port->value->(port_name)->value->max-speed->value + cur_user_port_value[KEY_PHY_PORT_INFO_V_USER_PORT_V_MAX_SPEED] = max_speed + + # update phy_port_info->value->user_port->value->(port_name)->value->lanes->value + cur_user_port_value[KEY_PHY_PORT_INFO_V_USER_PORT_V_LANES] = logic_lane_list[:] + + # updte phy_port_info->value->lane_list->value + k=KEY_PHY_PORT_INFO_V_LANE_LIST + if k not in phy_port_info_value: + phy_port_info_value[k] = list() + phy_port_info_value[k].extend(logic_lane_list) + + return data_dict + + +def _parse_port_map(config_file): + """ + :return: + { + 'clx_port_2_logical_lane': { + clx_port(unit,port): [logic_lane1, logic_lane2] + }, + 'logical_lane_2_clx_port': { + logic_lane: clx_port(unit,port) + }, + 'cpi_port_map_data':{ + 'init set port-map port=129 eth-macro=0 lane=1 max-speed=10g active=true guarantee=true cpi=true', + 'init set port-map port=130 eth-macro=0 lane=0 max-speed=10g active=true guarantee=true cpi=true init-done=true' + } + } + """ + with open(config_file, mode='r') as f: + config_content = f.read() + + #init set port-map port=0 eth-macro=2 lane=0 max-speed=25g active=true + pat_str = r'''^init\s+set\s+port-map\s+ + (unit=(?P\d+)\s+){0,1} # unit=0 + port=(?P\d+)\s+ # port=0 + eth-macro=(?P\d+)\s+ #eth-macro=4 + lane=(?P\d+)\s+ #lane=0 + .*$ + ''' + pat = re.compile(pat_str, re.M | re.I | re.X) + result = pat.finditer(config_content) + data_dict={ + KEY_CLX_PORT_2_LOGIC_LANE: dict(), + KEY_LOGIC_LANE_2_CLX_PORT: dict(), + KEY_CPI_PORT_MAP_DATA: list() + } + for pre in result: + unit = pre.group('unit') + if unit is None: + unit = '0' + + port = pre.group('port') + if port in CPI_PORT_LIST: # skip cpi ports + data_dict[KEY_CPI_PORT_MAP_DATA].append(pre.group(0)) + continue + + eth_macro = pre.group('eth_macro') + + lane = pre.group('lane') + + logic_lane = macro_lane_2_logic_lane(eth_macro, lane) + + clx_port_tuple = (unit, port) + + clx_port_2_logic_lane = data_dict[KEY_CLX_PORT_2_LOGIC_LANE] + if clx_port_tuple not in clx_port_2_logic_lane: + clx_port_2_logic_lane[clx_port_tuple] = list() + clx_port_2_logic_lane[clx_port_tuple].append(logic_lane) + + data_dict[KEY_LOGIC_LANE_2_CLX_PORT][logic_lane] = clx_port_tuple + + return data_dict + + +def _parse_phy_attr_from_file(config_file, clx_port_2_logic_lane): + """ + parse pre-emphasis|lane-swap/polarity-rev of phy port from clx/dsh file + :param config_file: + :return: + { + 'pre-emphasis': { + # key: value + (logic_lane, property): hex_value, + cpi_port_info: [ + 'phy set pre-emphasis portlist=129 lane-cnt=1 property=c2 data=0x01', + 'phy set pre-emphasis portlist=130 lane-cnt=1 property=c1 data=0x03' + ] + }, + 'polarity-rev': { + # key: value + (logic_lane, property): hex_value, + cpi_port_info: [ + 'phy set polarity-rev portlist=129 lane-cnt=1 property=tx data=0x0' + 'phy set polarity-rev portlist=130 lane-cnt=1 property=tx data=0x0' + ] + } + 'lane-swap': { + # key: value + (logic_lane, property): hex_value + cpi_port_info: [ + 'phy set lane-swap portlist=129 lane-cnt=1 property=tx data=0x1', + 'phy set lane-swap portlist=130 lane-cnt=1 property=tx data=0x0' + ] + } + } + """ + # content of lane-swap config file: + # phy set lane-swap portlist=48-53 lane-cnt=4 property=tx data=0x03.02.01.00 + # phy set polarity-rev portlist=53 lane-cnt=4 property=rx data=0x0.0.0.0 + with open(config_file, mode='r') as f: + config_content = f.read() + + pat_str = r'''^phy\s+set\s+(?Plane-swap|polarity-rev|pre-emphasis)\s+ #pre-emphasis|phy set lane-swap/polarity-rev + (unit=(?P\d+)\s+){0,1} #unit=0 + portlist=(?P[\d\-,]+)\s+ #portlist=0 + lane-cnt=(?P\d+)\s+ #lane-cnt=4 + property=(?P\w+)\s+ #property=tx/rx/c2/c1/c0/cn1 + data=(?P[\w\.]+) #data=0x01.01.01.01 + .*?$ + ''' + pat = re.compile(pat_str, re.M | re.I | re.X) + result = pat.finditer(config_content) + data_dict={ + KEY_PRE_EMPHASIS: dict(), + KEY_LANE_SWAP: dict(), + KEY_POLARITY_REV: dict() + } + for pre in result: + phy_attr = pre.group('phy_attr') + unit = pre.group('unit') + if unit is None: + unit = '0' + portlist = pre.group('portlist') + lane_cnt = pre.group('lane_cnt') + property_data = pre.group('property') + data = pre.group('data') + data_hl = [hex(y)[2:] for y in [int(x, 16) for x in data.split('.')]] + if int(lane_cnt) != len(data_hl): + logger.error("lane count mismatch value : {}\n".format(pre.group(0))) + continue + + for p in parse_portlist_range(portlist): + # if CPI ports, just mark down and continue parsing next match + if p in CPI_PORT_LIST: + if phy_attr == 'pre-emphasis': + attr_value = data_dict[KEY_PRE_EMPHASIS] + elif phy_attr == 'lane-swap': + attr_value = data_dict[KEY_LANE_SWAP] + elif phy_attr == 'polarity-rev': + attr_value = data_dict[KEY_POLARITY_REV] + if KEY_CPI_PORT_INFO not in attr_value: + attr_value[KEY_CPI_PORT_INFO] = list() + attr_value[KEY_CPI_PORT_INFO].append(pre.group(0)) + break + + # get first logic_lane number from clx_port(unit,port) + k = (unit, p) + if k not in clx_port_2_logic_lane: + logger.error("(unit {},port {}) is not in clx_port_2_logic_lane\n".format(unit, p)) + continue + logic_lane_start = int(clx_port_2_logic_lane[k][0]) + + # store all data with key (logic_lane, property) + for index, logic_lane in enumerate(range(logic_lane_start, logic_lane_start + int(lane_cnt))): + if phy_attr == 'pre-emphasis': + attr_value = data_dict[KEY_PRE_EMPHASIS] + elif phy_attr == 'lane-swap': + attr_value = data_dict[KEY_LANE_SWAP] + elif phy_attr == 'polarity-rev': + attr_value = data_dict[KEY_POLARITY_REV] + k = (str(logic_lane), property_data) + attr_value[k] = data_hl[index] + + return data_dict + + +def parse_clx_dsh(config_file): + """ + :return: + { + 'clx_port_2_logical_lane': { + clx_port(unit,port): [logic_lane1, logic_lane2] + }, + 'logical_lane_2_clx_port': { + logic_lane: clx_port(unit,port) + }, + 'cpi_port_map_data':[ + 'init set port-map port=129 eth-macro=0 lane=1 max-speed=10g active=true guarantee=true cpi=true', + 'init set port-map port=130 eth-macro=0 lane=0 max-speed=10g active=true guarantee=true cpi=true init-done=true' + ], + 'pre-emphasis': { + # key: value + (logic_lane, property): hex_value, + cpi_port_info: [ + 'phy set pre-emphasis portlist=129 lane-cnt=1 property=c2 data=0x01', + 'phy set pre-emphasis portlist=130 lane-cnt=1 property=c1 data=0x03' + ] + }, + 'polarity-rev': { + # key: value + (logic_lane, property): hex_value, + cpi_port_info: [ + 'phy set polarity-rev portlist=129 lane-cnt=1 property=tx data=0x0' + 'phy set polarity-rev portlist=130 lane-cnt=1 property=tx data=0x0' + ] + }, + 'lane-swap': { + # key: value + (logic_lane, property): hex_value + cpi_port_info: [ + 'phy set lane-swap portlist=129 lane-cnt=1 property=tx data=0x1', + 'phy set lane-swap portlist=130 lane-cnt=1 property=tx data=0x0' + ] + }, + 'medium-type': { + clx_port(unit,port): 'sr', + cpi_port_info: [ + 'port set property portlist=129-130 medium-type=kr' + ] + }, + 'an': { + clx_port(unit,port): 'enable', + cpi_port_info: [ + 'port set property portlist=129-130 an=enable' + ] + }, + 'adver': { + clx_port(unit,port): ['speed-100g','speed-25g'], + cpi_port_info: [ + 'port set adver portlist=129-130 speed-10g-kr' + ] + }, + 'admin': { + clx_port(unit,port): 'enable', + cpi_port_info: [ + 'port set property portlist=129-130 admin=enable' + ] + } + } + """ + data_dict = dict() + data_port_map = _parse_port_map(config_file) + data_phy_attribute = _parse_phy_attr_from_file(config_file, data_port_map[KEY_CLX_PORT_2_LOGIC_LANE]) + data_port_property = _parse_port_property_from_file(config_file) + data_dict.update(data_port_map) + data_dict.update(data_phy_attribute) + data_dict.update(data_port_property) + return data_dict + + +def _parse_port_property_from_file(config_file): + """ + parse adver | medium-type | an | admin status of port from clx/dsh file + + :param : config_file + :return: + { + 'medium-type': { + clx_port(unit,port): 'sr', + cpi_port_info: [ + 'port set property portlist=129-130 medium-type=kr' + ] + }, + 'an': { + clx_port(unit,port): 'enable', + cpi_port_info: [ + 'port set property portlist=129-130 an=enable' + ] + }, + 'adver': { + clx_port(unit,port): ['speed-100g','speed-25g'], + cpi_port_info: [ + 'port set adver portlist=129-130 speed-10g-kr' + ] + }, + 'admin': { + clx_port(unit,port): 'enable', + cpi_port_info: [ + 'port set property portlist=129-130 admin=enable' + ] + } + } + """ + # content of config file: + + # port set property portlist=129-130 medium-type=kr + # port set adver portlist=129-130 speed-10g-kr + # port set property portlist=129-130 an=enable + # port set property portlist=129-130 admin=enable + + with open(config_file, mode='r') as f: + config_content = f.read() + + data_dict={ + KEY_MEDIUM_TYPE: dict(), + KEY_AN: dict(), + KEY_ADMIN: dict(), + KEY_ADVER: dict() + } + + pat_str = r'''^port\s+set\s+property\s+ #port set property + (unit=(?P\d+)\s+){0,1} #unit=0 + portlist=(?P[\d\-,]+)\s+ #portlist=0 + (?Pmedium-type|an|admin)=(?P[\w]+) + .*?$ + ''' + pat = re.compile(pat_str, re.M | re.I | re.X) + result = pat.finditer(config_content) + for pre in result: + unit = pre.group('unit') + if unit is None: + unit = '0' + portlist = pre.group('portlist') + property_data = pre.group('property') + data = pre.group('data') + + for p in parse_portlist_range(portlist): + if property_data == 'medium-type': + attr_value = data_dict[KEY_MEDIUM_TYPE] + elif property_data == 'an': + attr_value = data_dict[KEY_AN] + elif property_data == 'admin': + attr_value = data_dict[KEY_ADMIN] + # CPI ports + if p in CPI_PORT_LIST: + if KEY_CPI_PORT_INFO not in attr_value: + attr_value[KEY_CPI_PORT_INFO] = list() + attr_value[KEY_CPI_PORT_INFO].append(pre.group(0)) + break + # clx ports + attr_value[(unit, p)] = data + + pat_str = r'''^port\s+set\s+adver\s+ #port set adver + (unit=(?P\d+)\s+){0,1} #unit=0 + portlist=(?P[\d\-,]+)\s+ #portlist=0 + (?P.*?$) + ''' + pat = re.compile(pat_str, re.M | re.I | re.X) + result = pat.finditer(config_content) + for pre in result: + unit = pre.group('unit') + if unit is None: + unit = '0' + portlist = pre.group('portlist') + data = pre.group('data') + + for p in parse_portlist_range(portlist): + attr_value = data_dict[KEY_ADVER] + # CPI ports + if p in CPI_PORT_LIST: + if KEY_CPI_PORT_INFO not in attr_value: + attr_value[KEY_CPI_PORT_INFO] = list() + attr_value[KEY_CPI_PORT_INFO].append(pre.group(0)) + break + # clx ports + attr_value[(unit, p)] = data.split() + + return data_dict + +def parse_portlist_range(portlist_str): + portlist=list() + if not portlist_str: + return portlist + # validation check + invaild_chr = r'[^\d,-]' + if re.search(invaild_chr, portlist_str): + raise Exception('Invalid portlist range! {}'.format(portlist_str)) + for p_range in portlist_str.split(','): + res = p_range.split('-') + if len(res) == 1: + portlist.extend(res) + elif len(res) != 2: + continue + else: + portlist.extend( + str(x) for x in range(int(res[0]), int(res[1])+1) + ) + # sort and filter duplicate item + portlist = sorted(set(portlist),key=int) + return portlist + + +def create_portlist_range(portlist): + if len(portlist) == 0: + return '' + plist = sorted(set(portlist),key=int) # filter duplicate item + tmp_result = list() + tmp_range = list() + tmp_result.append(tmp_range) + for index, p in enumerate(plist): + if index == 0 : + tmp_range.append(p) + elif int(plist[index-1]) +1 == int(p): + tmp_range.append(p) + else: + tmp_range = list() + tmp_range.append(p) + tmp_result.append(tmp_range) + portlist_str = ','.join( + x[0] if len(x)==1 else x[0] + '-' + x[-1] for x in tmp_result + ) + return portlist_str + + +def logic_lane_2_macro_lane(logic_lane): + logic_lane = int(logic_lane) + return (str(logic_lane // 4), str(logic_lane % 4)) + + +def macro_lane_2_logic_lane(macro, clx_lane): + return str(int(macro)*4 + int(clx_lane)) diff --git a/platform/clounix/clx-utils/clounix-utils/port_breakout/main.py b/platform/clounix/clx-utils/clounix-utils/port_breakout/main.py new file mode 100644 index 000000000000..e3cffafb7f97 --- /dev/null +++ b/platform/clounix/clx-utils/clounix-utils/port_breakout/main.py @@ -0,0 +1,1122 @@ +#!/usr/bin/python3 +# +# Terminology +# fpport: Front Panel Port +# user_port: Port name which configured in port_config.ini +# clx_port: Port index which used by clx sdk + +import sys +import os +import os.path +import logging +import logging.handlers +import shutil +import subprocess +import re +import jinja2 +import datetime +import json +from pprint import pformat +from collections import OrderedDict, defaultdict +from tabulate import tabulate +import click + +import libpcfgparsing as PCFG +from sonic_py_common.device_info import get_machine_info +from sonic_py_common.device_info import get_platform_info +#from swsssdk import ConfigDBConnector, SonicV2Connector +from swsssdk import SonicV2Connector + +LOG_IDENTIFIER = 'clx_port_breakout' +LOG_FORMAT = "%(levelname)s: [%(funcName)s] %(message)s" +SCRIPT_DIR = os.path.dirname(__file__) + +PORT_BREAKOUT_TYPE_NONE = "NO_BREAKOUT" +PORT_BREAKOUT_TYPE_2X = "BREAKOUT_2X" +PORT_BREAKOUT_TYPE_4X = "BREAKOUT_4X" +PORT_BREAKOUT_TYPE_2_NUM_MAP = { + PORT_BREAKOUT_TYPE_NONE: 1, + PORT_BREAKOUT_TYPE_2X: 2, + PORT_BREAKOUT_TYPE_4X: 4 +} +PORT_BREAKOUT_NUM_2_TYPE_MAP = dict(zip(PORT_BREAKOUT_TYPE_2_NUM_MAP.values(), PORT_BREAKOUT_TYPE_2_NUM_MAP.keys())) +PORT_BREAKOUT_TYPE_LIST = PORT_BREAKOUT_TYPE_2_NUM_MAP.keys() + +PLANE_PORTS_MAX_COUNT = 32 +DEFAULT_MEDIA_PREFIX = 'sr' # for fiber +DEFAULT_MDIO_DEVAD = '0x1E' +DEFAULT_MDIO_ADDR = '0x2' +DEFAULT_MDIO_VALUE = '0x8000' # for fiber + +PLATFORM_PATH_TEMPLATE = "/usr/share/sonic/device/{platform}" +HWSKU_PATH_TEMPLATE = PLATFORM_PATH_TEMPLATE + "/{hwsku}" +USER_DEFINED_HWSKU_TEMPLATE = "{hwsku}-UD" +PORT_CONFIG_INI = 'port_config.ini' +PORT_CONFIG_CLX = 'port_config.clx' +DEFAULT_SKU = 'default_sku' +ORIG_DEFAULT_SKU = 'default_sku.origin' +CONFIG_DB_JSON_FILE = '/etc/sonic/config_db.json' +BAK_CONFIG_DB_JSON_FILE = CONFIG_DB_JSON_FILE + '-' + LOG_IDENTIFIER + +TMP_USER_PORT_NAME_PREFIX = 'tmp_port' +TMP_USER_PORT_NAME_TEMPLATE = TMP_USER_PORT_NAME_PREFIX + '{fpport}/{usr_port_num}' + +# Return Value Enum +NOOP = None +BREAK_SUCCESS = 0 +ERR_NOTSUPPORT = -1 +ERR_UNKOWN_BREAKOUT_TYPE = -2 +ERR_OVER_MAXCOUNT = -3 +ERR_UNKNOWN_PORT = -4 +ERR_PORT_DISABLED = -5 +ERR_VALIDATION_CHECK_FAILED = -6 + +# Render Template +PORT_CONFIG_CLX_TEMPLATE = 'port_config.clx.j2' + +PORT_BREAKOUT_TABLE_NAME = 'PORT_BREAKOUT' +PORT_BREAKOUT_JSON_FILE = '/etc/sonic/port_breakout.json' + +# STATE DB Schema +PORT_CFG_STATE_NAME = 'PORT_CFG_STATE' +STATE_MODIFIED = 'modified' + + +# --- init logger --- +logger = logging.getLogger(LOG_IDENTIFIER) +logger.setLevel(logging.INFO) +logger.addHandler(logging.NullHandler()) + + +def init_logger(log_level=logging.INFO, stdout=True): + global logger + if stdout: + stdout_handler=logging.StreamHandler(sys.stdout) + stdout_handler.setFormatter(logging.Formatter(LOG_FORMAT)) + logger.addHandler(stdout_handler) + logger.setLevel(log_level) + + +def m_print(msg, *arg, **kwargs): + click.echo(msg) + + +def get_hwsku_from_default_sku(default_sku): + with open(default_sku, 'r') as fh: + content = fh.read() + hwsku, _ = content.split(' ') + return hwsku + + +def get_port_config_ini(platform, hwsku): + hwsku_dir = HWSKU_PATH_TEMPLATE.format(platform=platform, hwsku=hwsku) + return os.path.join(hwsku_dir, PORT_CONFIG_INI) + + +def get_port_config_clx(platform, hwsku): + hwsku_dir = HWSKU_PATH_TEMPLATE.format(platform=platform, hwsku=hwsku) + return os.path.join(hwsku_dir, PORT_CONFIG_CLX) + + +def get_platform(): + machine_info = get_machine_info() + platform = get_platform_info(machine_info) + return platform + + +def render(src_tpl, *args, **kargs): + env = jinja2.Environment(loader=jinja2.FileSystemLoader(os.path.dirname(src_tpl))) + t = env.get_template(os.path.basename(src_tpl)) + return t.render(*args, **kargs) + + +def write_file(dst_file, content): + logger.debug("write content to {}".format(dst_file)) + with open(dst_file, 'w+') as fh: + fh.write(content) + + +def logic_lane_2_macro_lane(logic_lane): + return PCFG.logic_lane_2_macro_lane(logic_lane) + + +def make_tabulate(list1, list2, headers=None): + if not headers: + headers=['From', 'to'] + data = [('\n'.join(list1), '\n'.join(list2))] + return tabulate(data, + headers=headers, + tablefmt="simple", + missingval="", + disable_numparse=True) + +def state_db(op, name, value=''): + db = SonicV2Connector() + db.connect(db.STATE_DB) + res = -1 + client = db.redis_clients[db.STATE_DB] + if op == 'set': + res = client.set(name, value) + elif op == 'del': + res = client.delete(name, value) + elif op == 'get': + res = client.get(name) + if res == -1: + raise Exception('Unsupport action!') + return res + + +def set_state_db(name, value): + return state_db('set', name, value) + + +def get_state_db(name): + return state_db('get', name) + + +class PortBreakout: + def __init__(self): + self._dryrun_ = True + self.platform = get_platform() + self.platform_dir = PLATFORM_PATH_TEMPLATE.format(platform=self.platform) + self.default_sku = os.path.join(self.platform_dir, DEFAULT_SKU) + self.orig_default_sku = os.path.join(self.platform_dir, ORIG_DEFAULT_SKU) + if self.is_firsttime_running: + self._create_usr_defined_hwsku_dir() + self.default_hwsku = get_hwsku_from_default_sku(self.default_sku) + self.default_hwsku_dir = HWSKU_PATH_TEMPLATE.format( + platform=self.platform, + hwsku=self.default_hwsku) + self.orig_default_hwsku = get_hwsku_from_default_sku(self.orig_default_sku) + self.orig_default_hwsku_dir = HWSKU_PATH_TEMPLATE.format( + platform=self.platform, + hwsku=self.orig_default_hwsku) + self._init_port_profile() + + def _init_port_profile(self): + self.cur_port_profile = PortProfile(self.platform, self.default_hwsku) + self.orig_port_profile = PortProfile(self.platform, self.orig_default_hwsku) + + # --- Breakout APIs --- + @property + def is_firsttime_running(self): + return not os.path.exists(self.orig_default_sku) + + def show(self, fpport_index_list=None, show_index_0based=True, print_msg=True): + if fpport_index_list and len(fpport_index_list) != 0: + self.validation_check(fpport_index_list, is_valid=True) + all_fp_list = fpport_index_list + else: + all_fp_list = self.orig_port_profile.get_fpports() + cur_fp_list = self.cur_port_profile.get_fpports() + headers = ['FrontPannelPort(0-based)' if show_index_0based else 'FrontPannelPort(1-based)', 'BreakoutPorts', 'Lanes'] + data = [] + + for fp in all_fp_list: + #FrontPannelPort + item= [fp] if show_index_0based else [str(int(fp)+1)] + if fp not in cur_fp_list: # disabled ports + item[0] = item[0] + ' (disabled)' + data.append(item) + continue + fp_breakout_ports = self.cur_port_profile.get_fpport_user_ports(fp) + if TMP_USER_PORT_NAME_PREFIX in fp_breakout_ports[0]: # modified(enable/breakout) ports + item[0] = item[0] + ' (modified)' + # BreakoutPorts + item.append("\n".join(fp_breakout_ports)) + # Lanes + item.append("\n".join( + ",".join(self.cur_port_profile.get_user_port_lanes(p, fp)) for p in fp_breakout_ports)) + data.append(item) + + msg = tabulate(data, + headers=headers, + tablefmt="simple", + missingval="", + disable_numparse=True) + if print_msg: + m_print(msg) + return msg + + def is_port_breakable(self, fpport_index): + """ + Breakable port should have more than 1 logic lane + :param : fpport_index + :return: + """ + return self.is_valid_port(fpport_index) and len(self.orig_port_profile.get_fpport_lanes(fpport_index)) > 1 + + def is_valid_port(self, fpport_index): + """ + Return true if port in original port config ini + """ + return fpport_index in self.orig_port_profile.get_fpports() + + def is_enabled_port(self, fpport_index): + fp_ports = self.cur_port_profile.get_fpports() + return fpport_index in fp_ports + + def breakout_fpports(self, fpport_index_list, breakout_type=PORT_BREAKOUT_TYPE_4X): + self.validation_check(fpport_index_list, is_valid=True, is_enabled=True, is_breakable=True) + for fpport_index in fpport_index_list: + if breakout_type not in PORT_BREAKOUT_TYPE_2_NUM_MAP: + logger.error('Unsupport breakout type {}!'.format(breakout_type)) + return ERR_UNKOWN_BREAKOUT_TYPE + if self.cur_port_profile.is_over_plane_ports_cnt_limit(fpport_index, breakout_type): + logger.error('Over limitation of max plane ports({}) when try to breakout fp port {}(0-based), Abort!'.format(PLANE_PORTS_MAX_COUNT, fpport_index)) + return ERR_OVER_MAXCOUNT + self._do_breakout(fpport_index, breakout_type) + self._genereate_cfg() + + def _do_breakout(self, fpport_index, breakout_type): + fp_usr_ports = self.cur_port_profile.get_fpport_user_ports(fpport_index) + cur_port_breakout_count = len(fp_usr_ports) + exp_port_breakout_count = PORT_BREAKOUT_TYPE_2_NUM_MAP[breakout_type] + if cur_port_breakout_count == exp_port_breakout_count: + logger.warning('Front pannel port {}(0-based) has been setted to be {}).'.format(fpport_index, breakout_type)) + return NOOP + logger.info('Front pannel port {}(0-based) breakout type change from {} to {} '.format(fpport_index, PORT_BREAKOUT_NUM_2_TYPE_MAP[cur_port_breakout_count], breakout_type)) + fp_max_speed = sum(int(self.cur_port_profile.get_user_port_speed(usr_port, fpport_index)) for usr_port in fp_usr_ports) + fp_lanes_reverse = sorted(self.cur_port_profile.get_fpport_lanes(fpport_index), key=int, reverse=True) + p_alias_prefix = self.cur_port_profile.get_user_port_alias(fp_usr_ports[0], fpport_index).rpartition('/')[0] + p_lane_count = len(fp_lanes_reverse) / PORT_BREAKOUT_TYPE_2_NUM_MAP[breakout_type] + tmp_usr_port_infos = OrderedDict() + for port_cnt in range(1, PORT_BREAKOUT_TYPE_2_NUM_MAP[breakout_type] + 1): + p_name = TMP_USER_PORT_NAME_TEMPLATE.format(fpport=fpport_index, usr_port_num=str(port_cnt)) + p_lanes = [fp_lanes_reverse.pop() for i in range(p_lane_count)] + p_alias = p_alias_prefix + '/' + str(port_cnt) + p_speed = fp_max_speed / PORT_BREAKOUT_TYPE_2_NUM_MAP[breakout_type] + tmp_usr_port_infos[p_name] = { + 'lanes': p_lanes, + 'alias': p_alias, + 'max-speed': p_speed + } + self.cur_port_profile.update_fpport_user_port_info(fpport_index, tmp_usr_port_infos) + logger.info('Breakout front panel port {}(0-based): \n'.format(fpport_index) + make_tabulate(fp_usr_ports, tmp_usr_port_infos.keys())) + + + def disable_fpports(self, fpport_index_list): + self.validation_check(fpport_index_list, is_valid=True) + for fpport_index in fpport_index_list: + if not self.is_enabled_port(fpport_index): + logger.warning('Front panel port {}(0-based) has been disabled!!'.format(fpport_index)) + continue + self._disable_fpport(fpport_index) + self._genereate_cfg() + + def _disable_fpport(self, fpport_index): + self.cur_port_profile.disable_fpport(fpport_index) + logger.info('Disable Front panel port {}(0-based)'.format(fpport_index)) + + def enable_fpports(self, fpport_index_list): + self.validation_check(fpport_index_list, is_valid=True) + for fpport_index in fpport_index_list: + if self.is_enabled_port(fpport_index): + logger.warning('Front panel port {}(0-based) has been enabled!!'.format(fpport_index)) + continue + # check is over plane ports limitation after enable a fp port. + exp_breakout_count = PORT_BREAKOUT_TYPE_2_NUM_MAP[PORT_BREAKOUT_TYPE_NONE] + fp_plane = self.orig_port_profile.get_fpport_plane(fpport_index) + cur_plane_ports_cnt = self.cur_port_profile.get_plane_port_count(fp_plane) + if cur_plane_ports_cnt + exp_breakout_count > PLANE_PORTS_MAX_COUNT: + logger.error('Over limitation of max plane ports({}) when try to enable fp port {}(0-based), Abort!'.format(PLANE_PORTS_MAX_COUNT, fpport_index)) + return ERR_OVER_MAXCOUNT + self._enable_fpport(fpport_index) + self._genereate_cfg() + + def _enable_fpport(self, fpport_index): + # generate user ports info of the fp port. + # use front panel port info from origin port_config.ini + fp_usr_ports = self.orig_port_profile.get_fpport_user_ports(fpport_index) + fp_max_speed = sum(int(self.orig_port_profile.get_user_port_speed(usr_port, fpport_index)) for usr_port in fp_usr_ports) + fp_lanes = self.orig_port_profile.get_fpport_lanes(fpport_index) + p_alias_prefix = self.orig_port_profile.get_user_port_alias(fp_usr_ports[0], fpport_index).rpartition('/')[0] + tmp_usr_port_infos = OrderedDict() + tmp_usr_port_name = TMP_USER_PORT_NAME_TEMPLATE.format(fpport=fpport_index, usr_port_num=1) + tmp_usr_port_infos[tmp_usr_port_name] = { + 'lanes': fp_lanes, + 'alias': p_alias_prefix + '/1', + 'max-speed': fp_max_speed + } + # update current port profile with the new user ports info + self.cur_port_profile.update_fpport_user_port_info(fpport_index, tmp_usr_port_infos) + logger.info('Enable Front panel port {}(0-based)'.format(fpport_index)) + + def reset_fpports(self, fpport_index_list): + self.validation_check(fpport_index_list, is_valid=True, is_enabled=True) + for fpport_index in fpport_index_list: + self._do_breakout(fpport_index, breakout_type=PORT_BREAKOUT_TYPE_NONE) + logger.info('Reset Front panel port {}(0-based)'.format(fpport_index)) + self._genereate_cfg() + + def reset_all(self): + shutil.rmtree(self.default_hwsku_dir) + shutil.copytree(self.orig_default_hwsku_dir, self.default_hwsku_dir) + self.reset_config_db_json() + logger.warning('Reset all front panel ports breakout configuration!') + + def reset_config_db_json(self): + """ + Trigger to regenerate configuration as firsttime on next booting. + Last configuration will be backuped. + :return: + """ + # backup config db json + if os.path.exists(CONFIG_DB_JSON_FILE): + now = datetime.datetime.now() + timestamp = str(now.year) + str(now.month) + str(now.day) + str(now.hour) + str(now.minute) + "_" + str(now.second) + bak_cfg_json = BAK_CONFIG_DB_JSON_FILE + '-' + timestamp + shutil.copy(CONFIG_DB_JSON_FILE, bak_cfg_json) + os.remove(CONFIG_DB_JSON_FILE) + logger.warning("Set default!! Previous config_db.json is stored to - {}.".format(bak_cfg_json)) + else: + logger.info("config_db.json not found! Skip backup!") + + # touch firsttime + res = subprocess.check_output('sonic_installer list', shell=True, stderr=subprocess.STDOUT) + boot_image = re.search(r"Current: SONiC-OS-([^\s]*)", res).group(1) + first_time_flag = '/host/image-{}/platform/firsttime'.format(boot_image) + with open(first_time_flag, 'w+') as fh: + pass + logger.warning("Touch firsttime booting flag!") + + def _generate_port_config_ini(self): + header = ('# name', 'lanes', 'alias', 'index', 'speed') + name_template = "Ethernet{}" + next_name_index = 0 + alias_tpl = "Ethernet{}/{}" + data = list() + cur_fpports = self.cur_port_profile.get_fpports() + orig_fpports = self.orig_port_profile.get_fpports() + for fpport_index in orig_fpports: + if fpport_index not in cur_fpports: + next_name_index += len(self.orig_port_profile.get_fpport_lanes(fpport_index)) + continue + usr_ports = self.cur_port_profile.get_fpport_user_ports(fpport_index) + # user ports of front panel port should be stored in a OrderedDict + for index, usr_p in enumerate(usr_ports, start=1): + usr_port_lanes = self.cur_port_profile.get_user_port_lanes(usr_p, fpport_index) + item = [ + name_template.format(next_name_index), + ','.join(usr_port_lanes), + alias_tpl.format(int(fpport_index)+1, index), + fpport_index, + self.cur_port_profile.get_user_port_speed(usr_p, fpport_index) + ] + data.append(item) + next_name_index += len(usr_port_lanes) + self.new_port_config_ini = tabulate(data, headers=header, tablefmt='plain') + logger.info('Generated new port config ini content.') + logger.debug('New port config ini content \n' + self.new_port_config_ini) + + def _generate_port_config_clx(self): + port_map_list = list() + lane_swap_list = list() + polarity_rev_list = list() + pre_emphasis_list = list() + port_mdio_list = list() + port_medium_list = list() + port_speed_list = list() + + user_port_2_clx_port = dict() + next_unit_port_index = dict() + + # generate clx_port id + for fpport_index in self.cur_port_profile.get_fpports(): + for usr_p in self.cur_port_profile.get_fpport_user_ports(fpport_index): + unit = self.cur_port_profile.get_user_port_unit(usr_p, fpport_index) + if unit not in next_unit_port_index: + next_unit_port_index[unit] = 0 + port = next_unit_port_index[unit] + user_port_2_clx_port[usr_p] = (unit, port) + next_unit_port_index[unit] += 1 + + for fpport_index in self.cur_port_profile.get_fpports(): + user_ports = self.cur_port_profile.get_fpport_user_ports(fpport_index) + # port-map & mdio & speed & medium + for usr_p in user_ports: + p_unit, p_port = user_port_2_clx_port[usr_p] + p_lanes = self.cur_port_profile.get_user_port_lanes(usr_p, fpport_index) + p_macro, p_lane = logic_lane_2_macro_lane(p_lanes[0]) + p_max_speed = int(self.cur_port_profile.get_user_port_speed(usr_p, fpport_index)) // 1000 # convert speed unit from 1 to k + port_map_list.append( + {'unit': p_unit, + 'clx_port': p_port, + 'macro': p_macro, + 'clx_lane': p_lane, + 'max_speed': p_max_speed + } + ) + # mdio + port_mdio_list.append( + {'unit': p_unit, + 'clx_port': p_port, + 'devad': DEFAULT_MDIO_DEVAD, + 'addr': DEFAULT_MDIO_ADDR, + 'value': DEFAULT_MDIO_VALUE + } + ) + # speed + port_speed_list.append( + {'unit': p_unit, + 'clx_port': p_port, + 'speed': p_max_speed + } + ) + # medium + p_medium = DEFAULT_MEDIA_PREFIX + if len(p_lanes) > 1: + p_medium += str(len(p_lanes)) # sr2, sr4 + port_medium_list.append( + {'unit': p_unit, + 'clx_port': p_port, + 'medium': p_medium + } + ) + + fp_lane_list = self.cur_port_profile.get_fpport_lanes(fpport_index) + fp_1st_p_unit, fp_1st_p_clx_port= user_port_2_clx_port[user_ports[0]] + # lane-swap + fp_lane_swap_data = self.cur_port_profile.get_fpport_lane_swap(fpport_index) + lane_swap_list.append( + {"unit": fp_1st_p_unit, + "clx_port": fp_1st_p_clx_port, + "lane_cnt": len(fp_lane_list), + "data": fp_lane_swap_data} + ) + # polarity-rev + fp_polarity_rev_data = self.cur_port_profile.get_fpport_polarity_rev(fpport_index) + polarity_rev_list.append( + {"unit": fp_1st_p_unit, + "clx_port": fp_1st_p_clx_port, + "lane_cnt": len(fp_lane_list), + "data": fp_polarity_rev_data} + ) + # pre-emphasis + fp_pre_emphasis_data = self.cur_port_profile.get_fpport_pre_emphasis(fpport_index) + pre_emphasis_list.append( + {"unit": fp_1st_p_unit, + "clx_port": fp_1st_p_clx_port, + "lane_cnt": len(fp_lane_list), + "data": fp_pre_emphasis_data} + ) + cpi_data = self._generate_cpi_port_config_clx() + self.new_port_config_clx = render( + src_tpl=os.path.join(SCRIPT_DIR, PORT_CONFIG_CLX_TEMPLATE), + port_map=port_map_list, + lane_swap=lane_swap_list, + polarity_rev=polarity_rev_list, + pre_emphasis=pre_emphasis_list, + port_speed=port_speed_list, + port_medium=port_medium_list, + port_mdio=port_mdio_list, + **cpi_data + ) + logger.info('Generated new port config clx content.') + logger.debug("New port config clx content \n" + self.new_port_config_clx ) + + def _generate_cpi_port_config_clx(self): + data_dict = { + 'cpi_port_map': self.cur_port_profile.port_clx[PCFG.KEY_CPI_PORT_MAP_DATA], + 'cpi_lane_swap': self.cur_port_profile.port_clx[PCFG.KEY_LANE_SWAP].get(PCFG.KEY_CPI_PORT_INFO, []), + 'cpi_polarity_rev': self.cur_port_profile.port_clx[PCFG.KEY_POLARITY_REV].get(PCFG.KEY_CPI_PORT_INFO, []), + 'cpi_pre_emphasis': self.cur_port_profile.port_clx[PCFG.KEY_PRE_EMPHASIS].get(PCFG.KEY_CPI_PORT_INFO, []), + 'cpi_medium_type': self.cur_port_profile.port_clx[PCFG.KEY_MEDIUM_TYPE].get(PCFG.KEY_CPI_PORT_INFO, []), + 'cpi_adver': self.cur_port_profile.port_clx[PCFG.KEY_ADVER].get(PCFG.KEY_CPI_PORT_INFO, []), + 'cpi_an': self.cur_port_profile.port_clx[PCFG.KEY_AN].get(PCFG.KEY_CPI_PORT_INFO, []), + 'cpi_admin': self.cur_port_profile.port_clx[PCFG.KEY_ADMIN].get(PCFG.KEY_CPI_PORT_INFO, []) + } + return data_dict + + def save_breakout_cfg(self, write_to_json=True, set_state=True): + p_ini_file = get_port_config_ini(self.platform, self.default_hwsku) + p_clx_file = get_port_config_clx(self.platform, self.default_hwsku) + logger.info('save port config ini to {}'.format(p_ini_file)) + write_file(p_ini_file, self.new_port_config_ini) + logger.info('save port config clx to {}'.format(p_clx_file)) + write_file(p_clx_file, self.new_port_config_clx) + if set_state: + logger.info('Set STATE_DB(6) {}:{}'.format(PORT_CFG_STATE_NAME, STATE_MODIFIED)) + self._set_portcfg_state(STATE_MODIFIED) + if write_to_json: + self.write_to_json() + + def _create_usr_defined_hwsku_dir(self): + if not os.path.exists(self.default_sku): + raise Exception('default_sku not found: {}'.format(self.default_sku)) + cur_hwsku = get_hwsku_from_default_sku(self.default_sku) + cur_hwsku_dir = HWSKU_PATH_TEMPLATE.format(platform=self.platform, hwsku=cur_hwsku) + usr_defined_hwsku = USER_DEFINED_HWSKU_TEMPLATE.format(hwsku=cur_hwsku) + usr_defined_hwsku_dir = HWSKU_PATH_TEMPLATE.format(platform=self.platform, hwsku=usr_defined_hwsku) + + # bakup origin default_sku file + shutil.copy(self.default_sku, self.orig_default_sku) + # update hwsku to user defined hwsku in default_sku + with open(self.default_sku, "w") as fh: + fh.write(usr_defined_hwsku + " t1") + logger.info("Change hwsku from '{}' to '{}' ".format(cur_hwsku, usr_defined_hwsku)) + # copy hwsku dir to user defined hwsku dir for later use + shutil.copytree(cur_hwsku_dir, usr_defined_hwsku_dir) + logger.info("Copy hwsku dir from '{}' to '{}'".format(cur_hwsku_dir, usr_defined_hwsku_dir)) + + + def _genereate_cfg(self): + self._generate_port_config_ini() + self._generate_port_config_clx() + if not self.dryrun: + self.save_breakout_cfg() + self.reset_config_db_json() + else: + logger.info('Dryrun mode, skip saving the breakout configuration!') + + @property + def dryrun(self): + return self._dryrun_ + + @dryrun.setter + def dryrun(self, val): + self._dryrun_ = bool(val) + + def validation_check(self, fpport_index_list, is_valid=False, is_enabled=False, is_breakable=False, is_disabled=False): + validation_msg = 'Front pannel port should be -' + validation_msg += ' Valid,' if is_valid else '' + validation_msg += ' Enabled,' if is_enabled else '' + validation_msg += ' Breakable,' if is_breakable else '' + validation_msg += ' Disabled,' if is_disabled else '' + logger.info(validation_msg) + logger.info("Checking front panel ports index(0-based) - {}".format(fpport_index_list)) + check_pass = True + check_result_dict = { + 'invalid_port_list': list(), + 'enabled_port_list': list(), + 'disabled_port_list': list(), + 'unbreakable_port_list': list() + } + for fpport_index in fpport_index_list: + if is_valid and not self.is_valid_port(fpport_index): + check_result_dict['invalid_port_list'].append(fpport_index) + check_pass = False + continue + if is_enabled and not self.is_enabled_port(fpport_index): + check_result_dict['disabled_port_list'].append(fpport_index) + check_pass = False + continue + if is_disabled and self.is_enabled_port(fpport_index): + check_result_dict['enabled_port_list'].append(fpport_index) + check_pass = False + continue + if is_breakable and not self.is_port_breakable(fpport_index): + check_result_dict['unbreakable_port_list'].append(fpport_index) + check_pass = False + continue + if not check_pass: + logger.warning("Validation check failed !") + for k, v in check_result_dict.iteritems(): + if len(v) == 0 : + continue + if k == 'invalid_port_list': + logger.warning('Invalid front panel port index(0-based) - {}'.format(str(v))) + if k == 'enabled_port_list': + logger.warning('Invalid action for enabled front panel port index(0-based) - {}'.format(str(v))) + if k == 'disabled_port_list': + logger.warning('Invalid action for disabled front panel port index(0-based) - {}'.format(str(v))) + if k == 'unbreakable_port_list': + logger.warning('Unbreakable front panel port index(0-based) - {}'.format(str(v))) + raise Exception('Validation check failed !') + else: + logger.info("Validation check pass.") + return True + + def recover_from_json(self, json_file=PORT_BREAKOUT_JSON_FILE): + ''' + recover port breakout configuration from presaved port breakout json file. + this method should be called only by clounix service. + this method should be executed only on sonic first time booting. + ''' + logger.info('Start recover port breakout configuration from {}'.format(json_file)) + if not os.path.exists(json_file): + raise Exception('Not found {}!!'.format(json_file)) + with open(json_file, 'r') as fh: + pb_cfg_from_db = json.load(fh).get(PORT_BREAKOUT_TABLE_NAME) + if not pb_cfg_from_db: + return + + #all_fp_list = self.orig_port_profile.get_fpports() + params = pb_cfg_from_db['parameters'] + fp_index_0based = params['fp_index_0based_flag'] + fpports_cfg = pb_cfg_from_db['fpports'] + disabled_fp_list = [] + enabled_fp_list = [] + breakout_fp_dict = defaultdict(list) + for fp, fv in fpports_cfg.iteritems(): + fp_index = fp if fp_index_0based else str(int(fp) - 1) # convert fp to 0based fp_index + if not self.is_valid_port(fp_index): + raise Exception('Unknown front pannel {}(0based)'.format(fp_index)) + if fv['status'] == 'disabled': + disabled_fp_list.append(fp_index) + else: + enabled_fp_list.append(fp_index) + breakou_type = PORT_BREAKOUT_NUM_2_TYPE_MAP[int(fv['breakout_num'])] + breakout_fp_dict[breakou_type].append(fp_index) + + logger.info('Disable front panel port list(0based): {} '.format(PCFG.create_portlist_range(disabled_fp_list))) + for p in sorted(disabled_fp_list, key=int): + self._disable_fpport(p) + logger.info('Enable front panel port list(0based): {}' .format(PCFG.create_portlist_range(enabled_fp_list))) + for p in sorted(enabled_fp_list, key=int): + self._enable_fpport(p) + for b_type, p_list in breakout_fp_dict.iteritems(): + logger.info('Beakout type: {}, breakout front panel port list(0based): {}'.format(b_type, PCFG.create_portlist_range(p_list))) + for p in sorted(p_list, key=int): + self._do_breakout(p, b_type) + + # Should not reset config db and wirte port breakout configuration to json as it's recovering configuration + self._generate_port_config_ini() + self._generate_port_config_clx() + self.save_breakout_cfg(write_to_json=False, set_state=False) + logger.info('End recover port breakout configuration from config_db.json') + + + def write_to_json(self, fp_index_0based=False): + ''' + write port breakout configuration to json file; + :params: bool fp_index_0based, write with 0based fp port index if set True else with 1based port index. + ''' + logger.info('write breakout configuration to {}. Use `config save -y` if you want to save the configuration.'.format(PORT_BREAKOUT_JSON_FILE)) + port_breakout_table = { PORT_BREAKOUT_TABLE_NAME: { 'parameters': {'fp_index_0based_flag': fp_index_0based}, + 'fpports': {} } } + all_fp_list = self.orig_port_profile.get_fpports() + for fp in all_fp_list: + fv={} + if self.is_enabled_port(fp): # disabled front panel ports + fv['status'] = 'enabled' + fv['breakout_num'] = len(self.cur_port_profile.get_fpport_user_ports(fp)) + else: # modified or enabled front panel ports + fv['status'] = 'disabled' + fp_1based_index = fp if fp_index_0based else str(int(fp) + 1) + port_breakout_table[PORT_BREAKOUT_TABLE_NAME]['fpports'][fp_1based_index] = fv + if not os.path.exists(os.path.dirname(PORT_BREAKOUT_JSON_FILE)): + os.makedirs(os.path.dirname(PORT_BREAKOUT_JSON_FILE)) + with open(PORT_BREAKOUT_JSON_FILE, 'w+') as fh: + fh.write(json.dumps(port_breakout_table)) + + def _set_portcfg_state(self, state): + set_state_db(PORT_CFG_STATE_NAME, state) + + +class PortProfile: + """ + # parse_port_config_ini(config_file_path) + { + 'phy_port_info':{ + physical port:{ + 'user_port':{ name:{ + 'lanes':[logic_lane1, logic_lane2...], + 'alias':'', + 'max-speed':dd + } + } + 'lane_list': [logic_lane1, logic_lane2] + } + } + 'logical_lane_2_phy_port':{ + logic_lane: physical port + } + 'user_port_2_phy_port': { + user_port: phy port + } + } + + # parse_clx_dsh(config_file_path) + { + 'clx_port_2_logic_lane': { + clx_port(unit,port): [logic_lane1, logic_lane2] + }, + 'logic_lane_2_clx_port': { + logic_lane: clx_port(unit,port) + }, + 'pre-emphasis': { + # key: value + (logic_lane, property): hex + }, + 'polarity-rev': + # key: value + (logic_lane, property): hex + 'lane-swap': + # key: value + (logic_lane, property): hex + } + """ + def __init__(self, platform, hwsku): + PCFG.logger = logger + self.platform = platform + self.hwsku = hwsku + self.port_config = PCFG.parse_port_config_ini(get_port_config_ini(self.platform, self.hwsku)) + self.phy_port_info = self.port_config[PCFG.KEY_PHY_PORT_INFO] + self.port_clx = PCFG.parse_clx_dsh(get_port_config_clx(self.platform, self.hwsku)) + logger.debug( "{} - port_config - \n {}".format(self.hwsku,pformat(self.port_config))) + logger.debug( "{} - port_clx - \n {}".format(self.hwsku, pformat(self.port_clx))) + + def get_fpports(self): + return sorted(self.phy_port_info.keys(), key=int) + + def is_over_plane_ports_cnt_limit(self, fpport_index, breakout_type): + port_plane = self.get_fpport_plane(fpport_index) + plane_port_count = self.get_plane_port_count(port_plane) + fp_breakout_count = len(self.get_fpport_user_ports(fpport_index)) + fp_expect_breakout_count = PORT_BREAKOUT_TYPE_2_NUM_MAP[breakout_type] + return (plane_port_count - fp_breakout_count + fp_expect_breakout_count) > PLANE_PORTS_MAX_COUNT + + def get_fpport_info(self, fpport_index): + return self.phy_port_info.get(fpport_index) + + def get_user_port_lanes(self, user_port_name, fpport_index=None): + v = self.get_user_port_item(user_port_name, + PCFG.KEY_PHY_PORT_INFO_V_USER_PORT_V_LANES, + fpport_index) + return sorted(v, key=int) + + def get_user_port_alias(self, user_port_name, fpport_index=None): + v = self.get_user_port_item(user_port_name, + PCFG.KEY_PHY_PORT_INFO_V_USER_PORT_V_ALIAS, + fpport_index) + return v + + def get_user_port_speed(self, user_port_name, fpport_index=None): + v = self.get_user_port_item(user_port_name, + PCFG.KEY_PHY_PORT_INFO_V_USER_PORT_V_MAX_SPEED, + fpport_index) + if not v: + v ='0' + return v + + def get_user_port_unit(self, user_port_name, fpport_index=None): + if not fpport_index: + fpport_index = self.port_config[PCFG.KEY_USER_PORT_2_PHY_PORT].get(user_port_name) + if not fpport_index: + raise Exception('Can not find front panel index of user port {}'.format(user_port_name)) + fp_lanes = self.get_fpport_lanes(fpport_index) + clx_port = None + for logic_lane in fp_lanes: + clx_port = self.port_clx[PCFG.KEY_LOGIC_LANE_2_CLX_PORT].get(logic_lane) + if clx_port: + return clx_port[0] + return None + + def get_user_port_clx_port(self, user_port_name, fpport_index=None): + usr_p_lanes = self.get_user_port_lanes(user_port_name, fpport_index) + clx_port = None + for logic_lane in usr_p_lanes: + clx_port = self.port_clx[PCFG.KEY_LOGIC_LANE_2_CLX_PORT].get(logic_lane) + if clx_port: + return clx_port[1] + return None + + def get_user_port_item(self, user_port_name, key, fpport_index=None): + user_port = self.get_fpport_user_port_info(user_port_name, fpport_index) + return user_port.get(key) + + def get_fpport_item(self, fpport_index, key): + fp = self.phy_port_info[fpport_index] + return fp.get(key) + + def get_fpport_lanes(self, fpport_index): + return sorted( self.get_fpport_item(fpport_index, PCFG.KEY_PHY_PORT_INFO_V_LANE_LIST), key=int) + + def get_fpport_lane_swap(self, fpport_index): + lane_list = self.get_fpport_lanes(fpport_index) + data = { + 'rx': [], + 'tx': [] + } + t_lane_swap = self.port_clx[PCFG.KEY_LANE_SWAP] + for logic_lane in lane_list: + data['rx'].append(t_lane_swap.get((logic_lane, 'rx'))) + data['tx'].append(t_lane_swap.get((logic_lane, 'tx'))) + return data + + def get_fpport_polarity_rev(self, fpport_index): + lane_list = self.get_fpport_lanes(fpport_index) + data = { + 'rx': [], + 'tx': [] + } + t_polarity_rev = self.port_clx[PCFG.KEY_POLARITY_REV] + for logic_lane in lane_list: + data['rx'].append(t_polarity_rev.get((logic_lane, 'rx'))) + data['tx'].append(t_polarity_rev.get((logic_lane, 'tx'))) + return data + + def get_fpport_pre_emphasis(self, fpport_index): + """ + return pre-emphasis configuration from default config file(clounix_opt.dsh). + """ + DEFAULT_PRE_EMPHASIS_OPT_CFG_FILE_NAME = 'clounix_opt.dsh' + pre_emphasis_cfg_file = os.path.join( + '/usr/share/sonic/device', + self.platform, + self.hwsku, + DEFAULT_PRE_EMPHASIS_OPT_CFG_FILE_NAME + ) + t_pre_emphasis = PCFG.parse_clx_dsh(pre_emphasis_cfg_file)[PCFG.KEY_PRE_EMPHASIS] + lane_list = self.get_fpport_lanes(fpport_index) + data = { + 'c2': [], + 'cn1': [], + 'c1': [], + 'c0': [] + } + for logic_lane in lane_list: + for p in data.keys(): + data[p].append(t_pre_emphasis.get((logic_lane, p))) + return data + + def get_fpport_user_port_info(self, user_port_name, fpport_index=None): + if fpport_index: + fp = self.phy_port_info[fpport_index] + return fp[PCFG.KEY_PHY_PORT_INFO_V_USER_PORT].get(user_port_name) + else: + user_port = None + for v in self.phy_port_info.itervalues(): + user_port = v[PCFG.KEY_PHY_PORT_INFO_V_USER_PORT].get(user_port_name) + if user_port: + break + return user_port + + def get_fpport_user_ports(self, fpport_index): + fp = self.phy_port_info[fpport_index] + usr_p_infos = fp[PCFG.KEY_PHY_PORT_INFO_V_USER_PORT].items() + # sort user port name by logic_lane + usr_p_infos.sort( + key=lambda x: int(x[1][PCFG.KEY_PHY_PORT_INFO_V_USER_PORT_V_LANES][0]) + ) + usr_ports = [p[0] for p in usr_p_infos] + return usr_ports + + def get_fpport_plane(self, fpport_index): + fp_1st_lane = self.get_fpport_lanes(fpport_index)[0] + fp_plane = str(int(fp_1st_lane)//64) + return fp_plane + + def get_plane_port_count(self, plane): + count = 0 + for fp in self.get_fpports(): + if self.get_fpport_plane(fp) == plane: + count += len(self.get_fpport_user_ports(fp)) + return count + + def set_user_port_item(self, user_port_name, key, value, fpport_index=None): + u = self.get_fpport_user_port_info(user_port_name, fpport_index) + u[key] = value + + def update_fpport_user_port_info(self, fpport_index, usr_port_infos): + tmp_usr_port_odict = OrderedDict() + tmp_fp_lane_list = [] + for usr_p, v in usr_port_infos.iteritems(): + tmp_usr_port_odict[usr_p] = { + PCFG.KEY_PHY_PORT_INFO_V_USER_PORT_V_LANES: v['lanes'], + PCFG.KEY_PHY_PORT_INFO_V_USER_PORT_V_ALIAS: v['alias'], + PCFG.KEY_PHY_PORT_INFO_V_USER_PORT_V_MAX_SPEED: v['max-speed'] + } + for lane in v['lanes']: + if lane not in tmp_fp_lane_list: + tmp_fp_lane_list.append(lane) + tmp_fp_lane_list.sort(key=int) + fp = self.get_fpport_info(fpport_index) + if not fp: # disabled fp + fp = dict() + fp[PCFG.KEY_PHY_PORT_INFO_V_LANE_LIST] = tmp_fp_lane_list + self.phy_port_info[fpport_index] = fp + fp[PCFG.KEY_PHY_PORT_INFO_V_USER_PORT] = tmp_usr_port_odict + + def disable_fpport(self, fpport_index): + if fpport_index in self.phy_port_info: + self.phy_port_info.pop(fpport_index) + +def get_fp_ports_4_click(ctx, args, incomplete): + # available until click version >=7.0 + pb = ctx.obj['pb'] + cur_fpports = pb.cur_port_profile.get_fpports() + orig_fpports = pb.orig_port_profile.get_fpports() + content = 'Current front panel ports(0-based): {}\n'.format(PCFG.create_portlist_range(cur_fpports)) + content += 'All front panel ports(0-based): {}'.format(PCFG.create_portlist_range(orig_fpports)) + + +# +# 'cli' group (root group) +# + +@click.group() +@click.pass_context +@click.option('-l','--log-level', + type=click.Choice(('DEBUG', 'INFO', 'WARN', 'ERROR')), + help='Set log level. Default is WARN.') +@click.option('-0','--fp-index-0based', is_flag=True, default=False, + help='Default use 1-based port index.') +@click.option('-d','--dryrun', is_flag=True, + help='Change would not be saved.') +def cli(ctx, log_level, fp_index_0based, dryrun): + """ + Port breakout related configuration. + """ + if os.geteuid() != 0: + exit("Root privileges are required for this operation") + + context = { + "pb": PortBreakout() + } + context.update(ctx.params) # store parameters for subcommand used. + ctx.obj = context + + LOG_LEVEL_MAP = { + 'ERROR': logging.ERROR, + 'WARN': logging.WARN, + 'INFO': logging.INFO, + 'DEBUG': logging.DEBUG + } + log_level = LOG_LEVEL_MAP.get(str(log_level), logging.WARN) + init_logger(log_level) + pb = ctx.obj['pb'] + pb.dryrun = dryrun + +# +# 'port-breakout' command ("port-breakout breakout") +# + +@cli.command() +@click.pass_context +@click.argument('fpport_range', + metavar='', + required=True) +@click.argument('breakout_num', + metavar='[]', + default=PORT_BREAKOUT_TYPE_2_NUM_MAP[PORT_BREAKOUT_TYPE_4X], + type=click.Choice(map(str, PORT_BREAKOUT_TYPE_2_NUM_MAP.values())), + required=False) +def breakout(ctx, fpport_range, breakout_num): + """ + Port breakout configuration. + Breakout front panel ports to 2 or 4 sub-ports. + """ + pb = ctx.obj["pb"] + fpport_list = PCFG.parse_portlist_range(fpport_range) + # convert front panel port index from 1based to 0based + if not ctx.obj['fp_index_0based']: + fpport_list = map(lambda x: str(int(x)-1), fpport_list) + breakout_type = PORT_BREAKOUT_NUM_2_TYPE_MAP[int(breakout_num)] + pb.breakout_fpports(fpport_list, breakout_type) + +# +# 'port-breakout' command ("port-breakout enable") +# + +@cli.command() +@click.pass_context +@click.argument('fpport_range', + metavar='', + required=True) +def enable(ctx, fpport_range): + """ + Enable front panel ports. + Disabe front panel ports would not get initialized. + """ + pb = ctx.obj["pb"] + fpport_list = PCFG.parse_portlist_range(fpport_range) + # convert front panel port index from 1based to 0based + if not ctx.obj['fp_index_0based']: + fpport_list = map(lambda x: str(int(x)-1), fpport_list) + pb.enable_fpports(fpport_list) + +# +# 'port-breakout' command ("port-breakout disable") +# + +@cli.command() +@click.pass_context +@click.argument('fpport_range', + metavar='', + required=True) +def disable(ctx, fpport_range): + """ + Disable front panel ports. + Disabe front panel ports would not get initialized. + """ + pb = ctx.obj["pb"] + fpport_list = PCFG.parse_portlist_range(fpport_range) + # convert front panel port index from 1based to 0based + if not ctx.obj['fp_index_0based']: + fpport_list = map(lambda x: str(int(x)-1), fpport_list) + pb.disable_fpports(fpport_list) + +# +# 'port-breakout' command ("port-breakout reset") +# + +@cli.command() +@click.pass_context +@click.argument('fpport_range', + metavar='', + required=True) +def reset(ctx, fpport_range): + """ + Reset breakout configuration for specified ports. + """ + pb = ctx.obj["pb"] + fpport_list = PCFG.parse_portlist_range(fpport_range) + # convert front panel port index from 1based to 0based + if not ctx.obj['fp_index_0based']: + fpport_list = map(lambda x: str(int(x)-1), fpport_list) + pb.reset_fpports(fpport_list) + +# +# 'port-breakout' command ("port-breakout reset-all") +# + +@cli.command('reset-all') +@click.pass_context +@click.option('-y','--yes', is_flag=True) +def reset_all(ctx, yes): + """ + Reset breakout configuration for all ports. + """ + if not yes: + click.confirm('Reset all ports breakout configuration?', abort=True, default=False) + pb = ctx.obj["pb"] + pb.reset_all() + +# +# 'port-breakout' command ("port-breakout show") +# + +@cli.command() +@click.pass_context +@click.argument('fpport_range', + metavar='', + required=False) +def show(ctx, fpport_range): + """Show port breakout related information.""" + pb = ctx.obj["pb"] + fp_index_0based = ctx.obj['fp_index_0based'] + fpport_list = PCFG.parse_portlist_range(fpport_range) + if not fp_index_0based: + fpport_list = map(lambda x: str(int(x)-1), fpport_list) + pb.show(fpport_list, fp_index_0based) + +# +# 'port-breakout' command ("port-breakout recover-from-json") +# + +@cli.command('recover-from-json') +@click.pass_context +@click.option('-j', '--json-file', + metavar='', + required=False, + default=PORT_BREAKOUT_JSON_FILE) +def recover_from_json(ctx, json_file): + """Recover port breakout configuration from json.""" + pb = ctx.obj["pb"] + pb.recover_from_json(json_file) + +if __name__=='__main__': + cli() + diff --git a/platform/clounix/clx-utils/clounix-utils/port_breakout/port_config.clx.j2 b/platform/clounix/clx-utils/clounix-utils/port_breakout/port_config.clx.j2 new file mode 100644 index 000000000000..3e92a02a93cf --- /dev/null +++ b/platform/clounix/clx-utils/clounix-utils/port_breakout/port_config.clx.j2 @@ -0,0 +1,87 @@ +init start stage low-level + +{# port-map #} +{%- for pm in port_map -%} +init set port-map unit={{ pm.unit }} port={{ pm.clx_port }} eth-macro={{ pm.macro }} lane={{ pm.clx_lane }} max-speed={{pm.max_speed}}g active=true +{% endfor -%} +{# cpi port-map #} +{%- for c_pm in cpi_port_map -%} +{{ c_pm }} +{% endfor %} + +init start stage task-rsrc +init start stage module +init start stage task + +{# lane-swap #} +{%- for ls in lane_swap -%} +phy set lane-swap unit={{ ls.unit }} portlist={{ ls.clx_port }} lane-cnt={{ ls.lane_cnt }} property=tx data=0x{{ls.data['tx'] | join('.')}} +{% endfor %} +{%- for ls in lane_swap -%} +phy set lane-swap unit={{ ls.unit }} portlist={{ ls.clx_port }} lane-cnt={{ ls.lane_cnt }} property=rx data=0x{{ls.data['rx'] | join('.')}} +{% endfor %} +{# cpi lane-swap #} +{%- for c_ls in cpi_lane_swap -%} +{{ c_ls }} +{% endfor %} + +{# polarity-rev #} +{%- for pr in polarity_rev -%} +phy set polarity-rev unit={{ pr.unit }} portlist={{ pr.clx_port }} lane-cnt={{ pr.lane_cnt }} property=tx data=0x{{pr.data['tx'] | join('.')}} +{% endfor %} +{%- for pr in polarity_rev -%} +phy set polarity-rev unit={{ pr.unit }} portlist={{ pr.clx_port }} lane-cnt={{ pr.lane_cnt }} property=rx data=0x{{pr.data['rx'] | join('.')}} +{% endfor %} +{# cpi polarity-rev #} +{%- for c_pr in cpi_polarity_rev -%} +{{ c_pr }} +{% endfor %} + +{# pre_emphasis #} +{%- for pe in pre_emphasis -%} +phy set pre-emphasis unit={{ pe.unit }} portlist={{ pe.clx_port }} lane-cnt={{ pe.lane_cnt }} property=c2 data=0x{{pe.data['c2'] | join('.')}} +phy set pre-emphasis unit={{ pe.unit }} portlist={{ pe.clx_port }} lane-cnt={{ pe.lane_cnt }} property=cn1 data=0x{{pe.data['cn1'] | join('.')}} +phy set pre-emphasis unit={{ pe.unit }} portlist={{ pe.clx_port }} lane-cnt={{ pe.lane_cnt }} property=c1 data=0x{{pe.data['c1'] | join('.')}} +phy set pre-emphasis unit={{ pe.unit }} portlist={{ pe.clx_port }} lane-cnt={{ pe.lane_cnt }} property=c0 data=0x{{pe.data['c0'] | join('.')}} +{% endfor %} +{# cpi pre_emphasis #} +{%- for c_pe in cpi_pre_emphasis -%} +{{ c_pe }} +{% endfor %} + +{# mdio #} +{%- for m in port_mdio -%} +phy set mdio unit={{ m.unit }} portlist={{ m.clx_port }} devad={{ m.devad }} addr={{ m.addr }} data={{ m.value }} +{% endfor %} + +{# port-speed #} +{%- for ps in port_speed -%} +port set property unit={{ ps.unit }} portlist={{ ps.clx_port }} speed={{ ps.speed }}g +{% endfor %} + +{# port-medium-type #} +{%- for pm in port_medium -%} +port set property unit={{ pm.unit }} portlist={{ pm.clx_port }} medium-type={{pm.medium}} +{% endfor %} + +{# cpi medium-type #} +{%- for c_mt in cpi_medium_type -%} +{{ c_mt }} +{% endfor -%} +{# cpi adver #} +{%- for c_adver in cpi_adver -%} +{{ c_adver }} +{% endfor -%} +{# cpi an #} +{%- for c_an in cpi_an -%} +{{ c_an }} +{% endfor -%} +{# cpi admin #} +{%- for c_admin in cpi_admin -%} +{{ c_admin }} +{% endfor %} + +{# disable all ports #} +{%- for pm in port_map -%} +port set property unit={{pm.unit}} portlist={{pm.clx_port}} admin=disable +{% endfor %} diff --git a/platform/clounix/clx-utils/clounix-utils/scripts/sfpdet b/platform/clounix/clx-utils/clounix-utils/scripts/sfpdet new file mode 100644 index 000000000000..bdd01693aee9 --- /dev/null +++ b/platform/clounix/clx-utils/clounix-utils/scripts/sfpdet @@ -0,0 +1,2144 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (C) 2018 Clounix, 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 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 . + +import os +import sys +import time +import subprocess +import re +import logging +import logging.handlers +import imp +import operator +import timeit +import threading +import shutil +import functools +import collections +import click +import traceback +from port_breakout import libpcfgparsing as cfg_parser +from port_breakout.main import get_state_db, PORT_CFG_STATE_NAME, STATE_MODIFIED +from tabulate import tabulate +import itertools +import json + +# python version +if sys.version > '3': + import queue as que +else: + import Queue as que + +# +START_UP_CFG_FILE = '/etc/sonic/sfpdet_cfg.json' + +# platform path +PLATFORM_ROOT_PATH = '/usr/share/sonic/device' +PLATFORM_PLUGINS = 'plugins' +PLATFORM_SPECIFIC_MODULE_NAME = 'sfputil' +PLATFORM_SPECIFIC_CLASS_NAME = "SfpUtil" + +# Global platform-specific sfputil class instance +g_platform_sfputil = None + +# port config file path +PORT_MAP_FILE_NAME = 'port_map.ini.' +PORT_CFG_FILE_NAME = 'port_config.ini' +PORT_CFG_CLX_FILE_NAME = 'port_config.clx' + +# pre emphasis config filename extension +PRE_EMPHASIS_FILENAME_EXT = '.dsh' + +# clounix default pre-emphasis config file name +DEFAULT_PRE_EMPHASIS_OPT_CFG_FILE_NAME = 'clounix_opt' + PRE_EMPHASIS_FILENAME_EXT +DEFAULT_PRE_EMPHASIS_DAC_CFG_FILE_NAME = 'clounix_dac' + PRE_EMPHASIS_FILENAME_EXT + +""" +key:user logic lane num +vlaue:phy port num +""" +g_logical_lane_2_phy_port_info = dict() + +""" +key:user logic port name +vlaue:phy port num +""" +g_user_port_2_phy_port_info = dict() + +""" +key:clx_port=>(unit, port) +vlaue:[laneObj1, laneObj2] +""" +g_clx_port_2_logical_lane_info = dict() + +""" +key: logic lane +vlaue:clx_port=>(unit, port) +""" +g_logical_lane_2_clx_port_info = dict() + +""" +#global port info dict +#key:physical port +#value:{ + 'user_port':{name:{ + 'lanes':[logic_lane1, logic_lane2...], + 'alias':'', + 'clx_port':(unit, port) + 'max-speed':dd + 'media':'XRd'} + 'mdio':{(devad, addr):value}} + 'lane_list': [logic_lane1, logic_lane2] + 'present_sfp_info':{'Vendor Name':'', + 'Vendor PN':'', + 'Identifier':'', + 'Connector':'' + 'Complance Code'}, + 'pre_emphasis':{(sfp info key):{(unit, clx port, lane count, property): data) + 'usr_config_media':'opt'/'dac' + }} + } +""" +g_phy_port_info = dict() + + +def key_2_str(key): + return str(key) + + +KEY_PHY_PORT_INFO = key_2_str +KEY_PHY_PORT_INFO_V_USER_PORT = cfg_parser.KEY_PHY_PORT_INFO_V_USER_PORT +KEY_PHY_PORT_INFO_V_LANE_LIST = cfg_parser.KEY_PHY_PORT_INFO_V_LANE_LIST +KEY_PHY_PORT_INFO_V_USER_PORT_V = key_2_str +KEY_PHY_PORT_INFO_V_USER_PORT_V_ALIAS = cfg_parser.KEY_PHY_PORT_INFO_V_USER_PORT_V_ALIAS +KEY_PHY_PORT_INFO_V_USER_PORT_V_LANES = cfg_parser.KEY_PHY_PORT_INFO_V_USER_PORT_V_LANES +KEY_PHY_PORT_INFO_V_USER_PORT_V_CLX_PORT = 'clx_port' +KEY_PHY_PORT_INFO_V_USER_PORT_V_MEDIA = 'media' +KEY_PHY_PORT_INFO_V_USER_PORT_V_MDIO = 'mdio' +KEY_PHY_PORT_INFO_V_USER_PORT_V_LANES_V = key_2_str +KEY_PHY_PORT_INFO_V_USER_PORT_V_LANES_V_ETH_MACRO = 'eth_macro' +KEY_PHY_PORT_INFO_V_PRESENT_SFP_INFO = 'present_sfp_info' +KEY_PHY_PORT_INFO_V_PRESENT_SFP_INFO_VENDOR_NAME = 'Vendor Name' +KEY_PHY_PORT_INFO_V_PRESENT_SFP_INFO_VENDOR_PN = 'Vendor PN' +KEY_PHY_PORT_INFO_V_PRESENT_SFP_INFO_IDENTIFIER = 'Identifier' +KEY_PHY_PORT_INFO_V_PRESENT_SFP_INFO_CONNECTOR = 'Connector' +KEY_PHY_PORT_INFO_V_PRESENT_SFP_INFO_COMPLIANCE_CODE = 'Specification compliance' +KEY_PHY_PORT_INFO_V_PRE_EMPHASIS = 'pre_emphasis' +KEY_PHY_PORT_INFO_V_USR_CFG_MEDIA = 'usr_config_media' +KEY_PHY_PORT_INFO_V_USR_CFG_DSH = 'usr_config_dsh' + +g_platform_name = '' +g_hwsku_name = '' + + +def _get_key_fuzzy_match_in_obj(key_str, obj): + """ + case and space ignore match + :param key_str: a string key + :param obj: iterable obj + :return: + """ + key_slice = str(key_str).lower().split(' ') + pt = re.compile(r'[\s\-_~.!@#%$&]*'.join(key_slice)) + + for key in obj: + if pt.search(key.lower()) is not None: + return key + return None + + +def _get_func_meta(): + import sys + try: + raise Exception() + except Exception: + exc_info = sys.exc_info() + trace_obj = exc_info[2] + trace_obj = trace_obj.tb_frame + up_frame = trace_obj.f_back + + return __name__ + '-->' + up_frame.f_code.co_name + '(line ' + str(up_frame.f_lineno) + ')' + + +def decro_timeit(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + worker_id = threading.current_thread().name + t0 = timeit.default_timer() + ret = func(*args, **kwargs) + t1 = timeit.default_timer() + g_sfp_logger.debug('of worker {} {} cost time: {}s\n'.format( + func.__name__, worker_id, t1 - t0)) + g_sfp_logger.debug('args: {}s\n'.format(list(args) + list(kwargs))) + + return ret + + return wrapper + + +g_sfp_logger = None + + +@decro_timeit +def _init_logging(logging_level=logging.INFO): + global g_sfp_logger + + logger_file_name = '/var/log/transceiver.log' + logger_fize_size = 10 + logger_file_num = 5 + + # init already, just return + if g_sfp_logger is None: + # create logger + logger_name = 'sfp_monitor_logger' + g_sfp_logger = logging.getLogger(logger_name) + g_sfp_logger.setLevel(logging_level) + + # create file handler + fh = logging.handlers.RotatingFileHandler(logger_file_name, mode='a+', maxBytes=logger_fize_size * 1024 * 1024, + backupCount=logger_file_num, delay=0) + fh.setLevel(logging_level) + + # create formatter + fmt = "%(asctime)-15s %(levelname)s %(filename)s[%(lineno)d]: %(funcName)s - %(message)s" + # datefmt = "%a %d %b %Y %H:%M:%S" + formatter = logging.Formatter(fmt) + + # add handler and formatter to _sys_logger + fh.setFormatter(formatter) + g_sfp_logger.addHandler(fh) + + +@decro_timeit +def _get_platform_and_hwsku(): + """ + get hardware sku + :return: (platform, hwsku) + """ + global g_platform_name + global g_hwsku_name + + if g_platform_name != '' and g_hwsku_name != '': + return g_platform_name, g_hwsku_name + + platform = "" + hwsku = "" + cmd = ['show', 'platform', 'summary'] + cmd_out = subprocess.check_output(cmd) + g_sfp_logger.debug(cmd_out) + pt = re.compile(r'^Platform:\s*([^\n\r\b]*).*HwSKU\s*:\s*([^\n\r\b]*)', re.I | re.M | re.S) + result = pt.search(cmd_out) + if result is not None: + g_sfp_logger.debug("type result:{}, result:{}\nmatch:{}\n".format(type(result), result, result.group())) + platform = result.group(1) + hwsku = result.group(2) + + if platform == "": + cmd = ['show', 'platform', 'syseeprom'] + cmd_out = subprocess.check_output(cmd) + g_sfp_logger.debug(cmd_out) + pt = re.compile(r'^Platform\s+Name\s+\w+\s+\d+\s+([^\n\r\b]*)', re.I | re.M | re.S) + result = pt.search(cmd_out) + if result is not None: + g_sfp_logger.debug("type result:{}, result:{}\nmatch:{}\n".format(type(result), result, result.group())) + platform = result.group(1) + + g_platform_name, g_hwsku_name = platform, hwsku + return platform, hwsku + + +def _get_path_to_platform_hwsku(): + """ + Returns path to platform and hwsku + :return: (path to platform, path to hwsku) + """ + # Get platform and hwsku + (platform, hwsku) = _get_platform_and_hwsku() + + # Load platform module from source + platform_path = "/".join([PLATFORM_ROOT_PATH, platform]) + hwsku_path = "/".join([platform_path, hwsku]) + + return platform_path, hwsku_path + + +@decro_timeit +def _load_platform_sfputil(): + """ + Loads platform specific sfputil module from source + :return: + """ + global g_platform_sfputil + + # Get platform and hwsku path + (platform_path, hwsku_path) = _get_path_to_platform_hwsku() + + try: + module_file = "/".join([platform_path, PLATFORM_PLUGINS, PLATFORM_SPECIFIC_MODULE_NAME + ".py"]) + module = imp.load_source(PLATFORM_SPECIFIC_MODULE_NAME, module_file) + except IOError as e: + g_sfp_logger.error("Failed to load platform module '%s': %s" % (PLATFORM_SPECIFIC_MODULE_NAME, str(e))) + return -1 + + try: + platform_sfputil_class = getattr(module, PLATFORM_SPECIFIC_CLASS_NAME) + g_platform_sfputil = platform_sfputil_class() + + # overwride _sfp_eeprom_present of baseclass for performance + def wrap_sfp_eeprom_present(sysfs_sfp_i2c_client_eeprompath, offset): + return True + + g_platform_sfputil._sfp_eeprom_present = wrap_sfp_eeprom_present + except AttributeError as e: + g_sfp_logger.error("Failed to instantiate '%s' class: %s\n" % (PLATFORM_SPECIFIC_CLASS_NAME, str(e))) + return -2 + + return 0 + + +def _get_path_to_port_config_file(): + """ + Returns path to port config file + :return: + """ + # Get platform and hwsku path + platform_path, hwsku_path = _get_path_to_platform_hwsku() + + # First check for the presence of the new 'port_config.ini' file + port_config_file_path = os.path.join(hwsku_path, PORT_CFG_FILE_NAME) + if os.path.isfile(port_config_file_path): + return port_config_file_path + else: + return '' + + +def _get_path_to_dflt_cfg_clx_file(): + """ + Returns path to default port config clx file + :return: + """ + platform_path, hwsku_path = _get_path_to_platform_hwsku() + clx_cfg_full_path = os.path.join(hwsku_path, PORT_CFG_CLX_FILE_NAME) + if os.path.isfile(clx_cfg_full_path): + return clx_cfg_full_path + else: + return '' + + +@decro_timeit +def _valid_global_data(): + global g_phy_port_info + + for phy_port, phy_port_value in g_phy_port_info.items(): + if KEY_PHY_PORT_INFO_V_USER_PORT not in phy_port_value \ + or KEY_PHY_PORT_INFO_V_PRE_EMPHASIS not in phy_port_value\ + or len(phy_port_value[phy_port_value]) == 0: + err_msg = "invalid phy_port_info[{}] = {}\n".format(phy_port, phy_port_value) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + +def _parse_pre_emphasis_info(dsh_file_path_list): + """ + parse pre-emphasis info from dsh file, if dsh_file_path is None then parse default clounix dsh file + :param dsh_file_path_list: + :return: + """ + global g_phy_port_info + + # generate pre-emphasis cmd for every phy port, like + # phy set pre-emphasis portlist=44 lane-cnt=4 property=c0 data=0x1c.1c.1c.1c + def _my_udf_lane_data_cmp(kx, ky): + if int(kx[0]) < int(ky[0]): + return -1 + elif int(kx[0]) == int(ky[0]): + return 0 + else: + return 1 + + for dsh_file_path in dsh_file_path_list: + parsed_dsh_info = cfg_parser.parse_clx_dsh(dsh_file_path) + dsh_file_name = os.path.split(dsh_file_path)[1] + + # init phy port pre-emphasis configuration + phy_port_emphasis_grouped_by_property = dict() + parsed_pre_emphasis = parsed_dsh_info[cfg_parser.KEY_PRE_EMPHASIS] + if cfg_parser.KEY_CPI_PORT_INFO in parsed_pre_emphasis: + parsed_pre_emphasis.pop(cfg_parser.KEY_CPI_PORT_INFO) + for (lane, prop), data in parsed_pre_emphasis.items(): + if lane not in g_logical_lane_2_phy_port_info: + g_sfp_logger.info("{}: lane {} is not in any phy port".format(_get_func_meta(), lane)) + continue + + key_phy_port = g_logical_lane_2_phy_port_info[lane] + if key_phy_port not in phy_port_emphasis_grouped_by_property: + phy_port_emphasis_grouped_by_property[key_phy_port] = dict() + value_phy_port = phy_port_emphasis_grouped_by_property[key_phy_port] + if prop not in value_phy_port: + value_phy_port.update({prop: [(lane, data)]}) + else: + value_phy_port[prop].append((lane, data)) + + for key_phy_port, value_prop_key_emphasis in phy_port_emphasis_grouped_by_property.items(): + cur_phy_port_pre_emphasis_dict = {dsh_file_name: {}} + for prop, lane_data_list in value_prop_key_emphasis.items(): + sort_lane_data_list = sorted(lane_data_list, key=functools.cmp_to_key(_my_udf_lane_data_cmp)) + lane = sort_lane_data_list[0][0] + unit = g_logical_lane_2_clx_port_info[lane][0] + clx_port = g_logical_lane_2_clx_port_info[lane][1] + lane_cnt = len(sort_lane_data_list) + pre_data = '0x' + '.'.join([d[1] for d in sort_lane_data_list]) + cur_phy_port_pre_emphasis_dict[dsh_file_name].update({(unit, clx_port, lane_cnt, prop): pre_data}) + + if KEY_PHY_PORT_INFO_V_PRE_EMPHASIS not in g_phy_port_info[key_phy_port]: + g_phy_port_info[key_phy_port][KEY_PHY_PORT_INFO_V_PRE_EMPHASIS] = dict() + g_phy_port_info[key_phy_port][KEY_PHY_PORT_INFO_V_PRE_EMPHASIS].update(cur_phy_port_pre_emphasis_dict) + + +@decro_timeit +def _parse_phy_port_info(): + """ + load sfputil module from device and init port phy2logic config mapping + :return: + """ + + global g_platform_sfputil + global g_logical_lane_2_phy_port_info + global g_user_port_2_phy_port_info + global g_clx_port_2_logical_lane_info + global g_logical_lane_2_clx_port_info + global g_phy_port_info + + # clear global database + g_platform_sfputil = None + g_logical_lane_2_phy_port_info.clear() + g_user_port_2_phy_port_info.clear() + g_clx_port_2_logical_lane_info.clear() + g_phy_port_info.clear() + + # load sfputil module + ret = _load_platform_sfputil() + if 0 != ret: + err_msg = "{}:fail({})\n".format(_get_func_meta(), ret) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + # init phy port to sonic logical port mappnig of g_platform_sfputil + try: + port_config_file_path = _get_path_to_port_config_file() + g_platform_sfputil.read_porttab_mappings(port_config_file_path) + except Exception as e: + err_msg = "{}:read port config file fail({})".format(_get_func_meta(), str(e)) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + # parse config.ini + try: + parsed_phy_port_info = cfg_parser.parse_port_config_ini(port_config_file_path) + if cfg_parser.KEY_PHY_PORT_INFO not in parsed_phy_port_info \ + or len(parsed_phy_port_info[cfg_parser.KEY_PHY_PORT_INFO]) == 0: + raise Exception(cfg_parser.KEY_PHY_PORT_INFO + ' is not in ' + parsed_phy_port_info.keys()) + if cfg_parser.KEY_LOGIC_LANE_2_PHY_PORT not in parsed_phy_port_info \ + or len(parsed_phy_port_info[cfg_parser.KEY_LOGIC_LANE_2_PHY_PORT]) == 0: + raise Exception(cfg_parser.KEY_LOGIC_LANE_2_PHY_PORT + ' is not in ' + parsed_phy_port_info.keys()) + if cfg_parser.KEY_USER_PORT_2_PHY_PORT not in parsed_phy_port_info \ + or len(parsed_phy_port_info[cfg_parser.KEY_USER_PORT_2_PHY_PORT]) == 0: + raise Exception(cfg_parser.KEY_USER_PORT_2_PHY_PORT + ' is not in ' + parsed_phy_port_info.keys()) + except Exception as e: + err_msg = "{}:parse {} fail({})".format(_get_func_meta(), port_config_file_path, e) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + # update global phy port--logic user port -- lane database + g_phy_port_info.update(parsed_phy_port_info[cfg_parser.KEY_PHY_PORT_INFO]) + g_logical_lane_2_phy_port_info.update(parsed_phy_port_info[cfg_parser.KEY_LOGIC_LANE_2_PHY_PORT]) + g_user_port_2_phy_port_info.update(parsed_phy_port_info[cfg_parser.KEY_USER_PORT_2_PHY_PORT]) + + # parse config_clx + try: + parsed_dsh_info = cfg_parser.parse_clx_dsh(_get_path_to_dflt_cfg_clx_file()) + if cfg_parser.KEY_CLX_PORT_2_LOGIC_LANE not in parsed_dsh_info \ + or len(parsed_dsh_info[cfg_parser.KEY_CLX_PORT_2_LOGIC_LANE]) == 0: + raise Exception(cfg_parser.KEY_CLX_PORT_2_LOGIC_LANE + ' is not in ' + parsed_dsh_info.keys()) + if cfg_parser.KEY_LOGIC_LANE_2_CLX_PORT not in parsed_dsh_info \ + or len(parsed_dsh_info[cfg_parser.KEY_LOGIC_LANE_2_CLX_PORT]) == 0: + raise Exception(cfg_parser.KEY_LOGIC_LANE_2_CLX_PORT + ' is not in ' + parsed_dsh_info.keys()) + if cfg_parser.KEY_PRE_EMPHASIS not in parsed_dsh_info \ + or len(parsed_dsh_info[cfg_parser.KEY_PRE_EMPHASIS]) == 0: + raise Exception(cfg_parser.KEY_PRE_EMPHASIS + ' is not in ' + parsed_dsh_info.keys()) + except Exception as e: + err_msg = "{}:parse {} fail({})".format(_get_func_meta(), _get_path_to_dflt_cfg_clx_file(), e) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + g_clx_port_2_logical_lane_info.update(parsed_dsh_info[cfg_parser.KEY_CLX_PORT_2_LOGIC_LANE]) + g_logical_lane_2_clx_port_info.update(parsed_dsh_info[cfg_parser.KEY_LOGIC_LANE_2_CLX_PORT]) + + # fill clx port for every user port + for clx_port, clx_port_lanes in g_clx_port_2_logical_lane_info.items(): + # get clx port of every user port + clx_port_lane = clx_port_lanes[0] + phy_port = g_logical_lane_2_phy_port_info[clx_port_lane] + user_port_list = g_phy_port_info[phy_port][KEY_PHY_PORT_INFO_V_USER_PORT] + for user_port_name, user_port_value in user_port_list.items(): + if clx_port_lane in user_port_value[KEY_PHY_PORT_INFO_V_USER_PORT_V_LANES]: + user_port_value[KEY_PHY_PORT_INFO_V_USER_PORT_V_CLX_PORT] = clx_port + user_port_value[KEY_PHY_PORT_INFO_V_USER_PORT_V_MEDIA] =\ + parsed_dsh_info[cfg_parser.KEY_MEDIUM_TYPE][clx_port] + + +def decro_get_docker_exec(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + cmd = ['docker', 'exec', '-i', 'syncd', 'bash'] + popen_obj = subprocess.Popen(cmd, bufsize=1024 * 1024, stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + exec_cmd = func(popen_obj, *args, **kwargs) + cmd_out = popen_obj.communicate() + if cmd_out[1] != '': + # cmd exec fail + err_msg = "cmd={} type cmd_out {}, cmd_out:{}\n".format(exec_cmd, type(cmd_out), cmd_out) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + else: + return exec_cmd, cmd_out[0] + + return wrapper + + +@decro_timeit +def _get_port_sfp_present_status(phy_port): + """ + get sfp present status of port + :param phy_port: + :return: True if present + """ + global g_platform_sfputil + + present = g_platform_sfputil.get_presence(phy_port) + + return present + + +def _get_phy_user_port_list(phy_port_value): + k = KEY_PHY_PORT_INFO_V_USER_PORT + if k not in phy_port_value: + g_sfp_logger.error("key {} is not in {}\n".format(k, phy_port_value)) + return {} + return phy_port_value[k] + + +@decro_timeit +def _get_port_sfp_info(phy_port): + """ + get eeprom sfp dict info from sfp moudule + :param phy_port:phy_port + :return: a sfp info dict + """ + global g_platform_sfputil + sfp_info_data = {} + + sfp_info_eeprom = g_platform_sfputil.get_eeprom_dict(phy_port) + if sfp_info_eeprom is None: + err_msg = "can't read sfp info of phy port {}\n".format(phy_port) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + # vendor info, ref SFF-8024 Transceiver Management + if ('interface' not in sfp_info_eeprom) or ('data' not in sfp_info_eeprom['interface']): + err_msg = "unknown sfp info {} of phy port {}\n".format(sfp_info_data, phy_port) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + pat = re.compile(r'\s+') + sp_replace = '-' + sfp_info_eeprom_data = sfp_info_eeprom['interface']['data'] + k = KEY_PHY_PORT_INFO_V_PRESENT_SFP_INFO_VENDOR_NAME + fuzzy_key = _get_key_fuzzy_match_in_obj(k, sfp_info_eeprom_data) + if fuzzy_key is not None: + sfp_info_data[k] = pat.sub(sp_replace, sfp_info_eeprom_data[fuzzy_key]).lower() + else: + g_sfp_logger.info("unknown sfp vendor name {} of phy port {}\n".format(sfp_info_data, phy_port)) + sfp_info_data[k] = 'unknown' + + k = KEY_PHY_PORT_INFO_V_PRESENT_SFP_INFO_VENDOR_PN + fuzzy_key = _get_key_fuzzy_match_in_obj(k, sfp_info_eeprom_data) + if fuzzy_key is not None: + sfp_info_data[k] = pat.sub(sp_replace, sfp_info_eeprom_data[fuzzy_key]).lower() + else: + g_sfp_logger.info("unknown sfp vendor pn {} of phy port {}\n".format(sfp_info_data, phy_port)) + sfp_info_data[k] = 'unknown' + + k = KEY_PHY_PORT_INFO_V_PRESENT_SFP_INFO_IDENTIFIER + fuzzy_key = _get_key_fuzzy_match_in_obj(k, sfp_info_eeprom_data) + if fuzzy_key is not None: + sfp_info_data[k] = pat.sub('', sfp_info_eeprom_data[fuzzy_key]).lower() + else: + g_sfp_logger.info("unknown sfp identifier {} of phy port {}\n".format(sfp_info_data, phy_port)) + sfp_info_data[k] = 'unknown' + + k = KEY_PHY_PORT_INFO_V_PRESENT_SFP_INFO_CONNECTOR + fuzzy_key = _get_key_fuzzy_match_in_obj(k, sfp_info_eeprom_data) + if fuzzy_key is not None: + sfp_info_data[k] = pat.sub('', sfp_info_eeprom_data[fuzzy_key]).lower() + else: + g_sfp_logger.info("unknown sfp connector {} of phy port {}\n".format(sfp_info_data, phy_port)) + sfp_info_data[k] = 'unknown' + + # {'10/40G Ethernet Compliance Code': '40GBASE-CR4'} + k = KEY_PHY_PORT_INFO_V_PRESENT_SFP_INFO_COMPLIANCE_CODE + fuzzy_key = _get_key_fuzzy_match_in_obj(k, sfp_info_eeprom_data) + if fuzzy_key is not None: + sfp_info_data[k] = str(sfp_info_eeprom_data[fuzzy_key]).lower() + else: + g_sfp_logger.info("unknown sfp specification compliance {} of phy port {}\n".format(sfp_info_data, + phy_port)) + sfp_info_data[k] = 'unknown' + + return sfp_info_data + + +def _get_sfp_type(sfp_info): + """ + get sfp type from sfp eeprom info + :param sfp_info: + :return: 'dac'--copper; 'opt'--fiber + """ + + # default 'opt' + media_type = 'opt' + + dac_connector_dict = {'copperpigtail': 'dac', "noseparableconnector": 'dac'} + k = KEY_PHY_PORT_INFO_V_PRESENT_SFP_INFO_CONNECTOR + if k in sfp_info: + connector_value = str(sfp_info[k]).lower().replace(' ', '') + if connector_value in dac_connector_dict: + media_type = dac_connector_dict[connector_value] + + media_base = '' + k = KEY_PHY_PORT_INFO_V_PRESENT_SFP_INFO_COMPLIANCE_CODE + if k in sfp_info: + pt_str = r'.*-(?P[ceskl]r)(?P\d*)\s*' + pt = re.compile(pt_str, re.I) + result = pt.search(sfp_info[k]) + if result is not None: + media_base = result.group('media_base') + + # ?? + if media_base.lower() == 'cr' and media_type == 'opt': + media_type = 'dac' + elif media_base.lower() == 'sr' and media_type == 'dac': + media_type = 'opt' + + return media_type + + +def _set_phy_port_media_mdio(phy_port_value, media_type='opt'): + """ + set media and mdio to opt of given physical port + :param phy_port_value: + :param media_type: + :return: + """ + + if 'opt' == media_type: + media_config = 'sr' + mdio_config = {('0x1e', '0x2'): '0x8000'} + else: + media_config = 'cr' + mdio_config = {('0x1e', '0x2'): '0x0'} + + # get phy_port_info->value->user_port + k = KEY_PHY_PORT_INFO_V_USER_PORT + all_user_port_value = phy_port_value[k] + + for port in all_user_port_value: + k_lanes = KEY_PHY_PORT_INFO_V_USER_PORT_V_LANES + port_lanes_cnt = len(all_user_port_value[port][k_lanes]) + if port_lanes_cnt == 1: + media_value = media_config + else: + media_value = media_config + str(port_lanes_cnt) + k = KEY_PHY_PORT_INFO_V_USER_PORT_V_MEDIA + all_user_port_value[port][k] = media_value + + # find current clx port and update mdio value + k = KEY_PHY_PORT_INFO_V_USER_PORT_V_MDIO + all_user_port_value[port][k] = mdio_config + + +@decro_timeit +def _get_phy_port_admin_status(user_port_list): + """ + get port status of user logic ports + :param user_port_list: + :return: + """ + port_admin_status = dict() + + cmd = ['show', 'interface', 'status'] + pcmd = subprocess.Popen(cmd, bufsize=-1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + cmd_out = pcmd.communicate() + if cmd_out[1] != '': + # cmd exec fail + err_msg = "cmd={} cmd_out:\n {}\n".format(cmd, cmd_out) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + # Interface Lanes Speed MTU Alias Oper Admin + # ----------- ------- ------- ----- ------------ ------ ------- + # Ethernet12 20 10G 9100 Ethernet13/1 up up + pt_str = r'''^\s* + (?P({}))\s+ #interface + (?P[\d,]+)\s+ #lanes + [\w/]+\s+ #speed + \d+\s+ #mtu + (?P[\w/]+)\s+ + [\w/]+\s+ + (?P[\w/]+) + .*$ + ''' + port_name_list = '{}'.format('|'.join(list(user_port_list))) + pt_str = pt_str.format(port_name_list) + pt = re.compile(pt_str, re.I | re.M | re.X) + result = pt.finditer(cmd_out[0]) + if result is None: + err_msg = "port {} cmd={} search fail cmd_out={}\n".format(list(user_port_list), cmd, cmd_out[0]) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + for r in result: + user_port = r.group('port_name') + status = r.group('admin') + clx_port = user_port_list[user_port][KEY_PHY_PORT_INFO_V_USER_PORT_V_CLX_PORT] + port_admin_status[user_port] = (status, clx_port) + + return port_admin_status + + +@decro_timeit +@decro_get_docker_exec +def _admin_up_user_ports(popen_obj, port_admin_status): + exec_cmd = [] + + for user_port, (admin, (port_unit, port_index)) in port_admin_status.items(): + if str(admin).lower() == 'up'.lower(): + cmd_str = 'clx_diag port set property unit={} portlist={} admin=enable'.format( + port_unit, port_index) + popen_obj.stdin.write(cmd_str + '\n') + exec_cmd.append(cmd_str) + time.sleep(0.005) + + return exec_cmd + + +@decro_timeit +@decro_get_docker_exec +def _admin_down_user_ports(popen_obj, user_port_list): + exec_cmd = [] + + for port in user_port_list: + k = KEY_PHY_PORT_INFO_V_USER_PORT_V_CLX_PORT + clx_port = user_port_list[port][k] + cmd_str = 'clx_diag port set property unit={} portlist={} admin=disable'.format( + clx_port[0], clx_port[1]) + popen_obj.stdin.write(cmd_str + '\n') + exec_cmd.append(cmd_str) + time.sleep(0.005) + + return exec_cmd + + +def _get_phy_port_media(user_port_list): + media_config = dict() + + for p in user_port_list: + k = KEY_PHY_PORT_INFO_V_USER_PORT_V_CLX_PORT + clx_port = user_port_list[p][k] + k = KEY_PHY_PORT_INFO_V_USER_PORT_V_MEDIA + media = user_port_list[p][k] + media_config[clx_port] = media + + return media_config + + +def _get_phy_port_mdio(user_port_list): + mdio_config = dict() + + for p in user_port_list: + k = KEY_PHY_PORT_INFO_V_USER_PORT_V_CLX_PORT + clx_port = user_port_list[p][k] + k = KEY_PHY_PORT_INFO_V_USER_PORT_V_MDIO + mdio = user_port_list[p][k] + mdio_config[clx_port] = mdio + + return mdio_config + + +# config order should be C2 -> CN1 -> C1 -> C0 +def _my_udf_pre_emphasis_cmp(kx, ky): + order_map = {'c2': 1, 'cn1': 2, 'c1': 3, 'c0': 4} + kx_priority = order_map[kx[3]] + ky_priority = order_map[ky[3]] + if kx_priority < ky_priority: + return -1 + elif kx_priority == ky_priority: + return 0 + else: + return 1 + + +@decro_timeit +def _get_port_running_config_exec_cmd(popen_obj, pre_emphasis_config, user_port_list): + exec_cmd = [] + + # show media + for port_name in user_port_list: + k = KEY_PHY_PORT_INFO_V_USER_PORT_V_CLX_PORT + unit, port = user_port_list[port_name][k] + cmd_str = 'clx_diag port show property unit={0} portlist={1}\n'.format( + unit, port) + popen_obj.stdin.write(cmd_str) + exec_cmd.append(cmd_str) + time.sleep(0.005) + + # show pre-emphasis + ordered_key_list = sorted(list(pre_emphasis_config), key=functools.cmp_to_key(_my_udf_pre_emphasis_cmp)) + for (unit, clx_port, lane_cnt, property_data) in ordered_key_list: + cmd_str = 'clx_diag phy show pre-emphasis unit={0} portlist={1}' \ + ' lane-cnt={2} property={3}'.format(unit, clx_port, lane_cnt, property_data) + '\n' + popen_obj.stdin.write(cmd_str) + exec_cmd.append(cmd_str) + time.sleep(0.005) + + return exec_cmd + + +@decro_timeit +def _get_port_running_config_mdio_exec_cmd(popen_obj, user_port_list): + exec_cmd = [] + + # show mdio + portlist_key_ol = sorted(list(user_port_list)) + for port_name in portlist_key_ol: + k = KEY_PHY_PORT_INFO_V_USER_PORT_V_CLX_PORT + unit, port = user_port_list[port_name][k] + k = KEY_PHY_PORT_INFO_V_USER_PORT_V_MDIO + mdio_config_ol = sorted(list(user_port_list[port_name][k])) + for devad, addr in mdio_config_ol: + cmd_str = 'clx_diag phy show mdio unit={0} portlist={1}' \ + ' devad={2} addr={3}'.format(unit, port, devad, addr) + '\n' + popen_obj.stdin.write(cmd_str) + exec_cmd.append(cmd_str) + time.sleep(0.005) + + return exec_cmd + + +@decro_timeit +@decro_get_docker_exec +def _get_port_running_config_check_output(popen_obj, pre_emphasis_config, user_port_list): + return _get_port_running_config_exec_cmd(popen_obj, pre_emphasis_config, user_port_list) + + +@decro_timeit +@decro_get_docker_exec +def _get_port_running_config_mdio_check_output(popen_obj, user_port_list): + return _get_port_running_config_mdio_exec_cmd(popen_obj, user_port_list) + + +@decro_timeit +def _get_port_running_config(show_cmd_buf, pre_emphasis_config, user_port_list): + # get media + # port speed medium admin an eee fec flow-ctrl status loopback cut-through + # ---- ----- ------ ----- --- --- --- --------- ------ -------- ----------- + # 0 100g sr4 en dis dis dis dis down dis dis + running_media = dict() + portlist = [] + for port_name in user_port_list: + k = KEY_PHY_PORT_INFO_V_USER_PORT_V_CLX_PORT + portlist.append(user_port_list[port_name][k][1]) + pt_str = r'''^\s* + (?P{})\s+ #interface + (?P[^\s]+)\s+ #speed + (?P\w+)\s+ #media + (?P\w+)\s+ #admin + .*$ + ''' + pt_str = pt_str.format('|'.join(portlist)) + pt = re.compile(pt_str, re.I | re.M | re.X) + result = pt.finditer(show_cmd_buf) + for r in result: + k = ('0', r.group('port')) + running_media[k] = r.group('media') + + # get pre-emphasis + # clx_diag phy show pre-emphasis unit={0} portlist={1} + # lane-cnt={2} property={3}' + running_pre_emphasis = dict() + ordered_key_list = sorted(list(pre_emphasis_config), key=functools.cmp_to_key(_my_udf_pre_emphasis_cmp)) + pt = re.compile(r'^.*\s+data\s*=\s*(?P[\w.]+).*$', re.I | re.M) + running_pre_emphasis_ol = pt.findall(show_cmd_buf) + for i in range(0, len(running_pre_emphasis_ol)): + hl = [hex(y)[2:] for y in [int(x, 16) for x in running_pre_emphasis_ol[i].split('.')]] + uniform_data = '0x' + '.'.join(hl) + k = ordered_key_list[i] + running_pre_emphasis[k] = uniform_data + + return running_media, running_pre_emphasis + + +@decro_timeit +def _get_port_running_config_mdio(show_cmd_buf, user_port_list): + # get mdio + # admin@switch:~$ _clx_diag phy show mdio unit=0 portlist=0 devad=0x1E addr=0x2 + # port 000: data=0x0000 + running_mdio = dict() + + pt = re.compile(r'^.*\s+data\s*=\s*(?P[\w.]+).*$', re.I | re.M) + portlist_key_ol = sorted(list(user_port_list)) + rst_mdio_ol = pt.findall(show_cmd_buf) + for i in range(0, len(rst_mdio_ol)): + hl = [hex(y)[2:] for y in [int(x, 16) for x in rst_mdio_ol[i].split('.')]] + uniform_data = '0x' + '.'.join(hl) + port_name = portlist_key_ol[i] + k = KEY_PHY_PORT_INFO_V_USER_PORT_V_CLX_PORT + unit, port = user_port_list[port_name][k] + k = KEY_PHY_PORT_INFO_V_USER_PORT_V_MDIO + mdio_config_ol = sorted(list(user_port_list[port_name][k])) + k = (unit, port) + for devad, addr in mdio_config_ol: + if k in running_mdio: + running_mdio[k].update({(devad, addr): uniform_data}) + else: + running_mdio[k] = {(devad, addr): uniform_data} + + return running_mdio + + +def _cmp_running_media_pre_emphasis(running_media, + media_config, + running_mdio, + mdio_config, + running_pre_emphasis, + pre_emphasis_config): + media_changed = 0 + if operator.ne(running_media, media_config): + media_changed = 1 + + mdio_changed = 0 + if operator.ne(running_mdio, mdio_config): + mdio_changed = 1 + + pre_emphasis_changed = 0 + if operator.ne(running_pre_emphasis, pre_emphasis_config): + pre_emphasis_changed = 1 + + return media_changed, mdio_changed, pre_emphasis_changed + + +@decro_timeit +def _check_port_running_config( + media_config, + mdio_config, + pre_emphasis_config, + user_port_list): + exec_cmd, output = _get_port_running_config_check_output(pre_emphasis_config, user_port_list) + running_media, running_pre_emphasis = \ + _get_port_running_config(output, pre_emphasis_config, user_port_list) + + exec_cmd, output = _get_port_running_config_mdio_check_output(user_port_list) + running_mdio = \ + _get_port_running_config_mdio(output, user_port_list) + + if len(running_media) != len(media_config) \ + or len(running_mdio) != len(mdio_config) \ + or len(running_pre_emphasis) != len(pre_emphasis_config): + err_msg = "{}: geting running config failed\n".format(_get_func_meta()) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + media_changed, mdio_changed, pre_emphasis_changed = \ + _cmp_running_media_pre_emphasis(running_media, + media_config, + running_mdio, + mdio_config, + running_pre_emphasis, + pre_emphasis_config) + + return media_changed, mdio_changed, pre_emphasis_changed + + +@decro_timeit +def _set_port_running_media_pre_emphasis_exec_cmd( + popen_obj, + media_changed, + media_config, + pre_emphasis_changed, + pre_emphasis_config, + user_port_list): + cmd_change = [] + + if 0 != media_changed: + for (unit, clx_port), media in media_config.items(): + # clx_diag port set property unit=0 portlist=26 medium-type=cr4 + cmd_str = 'clx_diag port set property unit={0} portlist={1} medium-type={2}'.format( + unit, clx_port, media) + popen_obj.stdin.write(cmd_str + '\n') + time.sleep(0.005) + cmd_change.append(cmd_str) + + if 0 != pre_emphasis_changed: + ordered_key_list = sorted(list(pre_emphasis_config), key=functools.cmp_to_key(_my_udf_pre_emphasis_cmp)) + for (unit, clx_port, lane_cnt, property_data) in ordered_key_list: + data = pre_emphasis_config[(unit, clx_port, lane_cnt, property_data)] + cmd_str = 'clx_diag phy set pre-emphasis unit={0} portlist={1} lane-cnt={2} ' \ + 'property={3} data={4}'.format(unit, clx_port, lane_cnt, property_data, data) + popen_obj.stdin.write(cmd_str + '\n') + time.sleep(0.005) + cmd_change.append(cmd_str) + + _get_port_running_config_exec_cmd(popen_obj, pre_emphasis_config, user_port_list) + + return cmd_change + + +@decro_timeit +def _set_port_running_mdio_exec_cmd( + popen_obj, + mdio_changed, + mdio_config, + user_port_list): + cmd_change = [] + + if 0 != mdio_changed: + for (unit, clx_port), mdio_values in mdio_config.items(): + # clx_diag phy show mdio portlist=0 devad=0x1E addr=0x2 data=0x8000 + for (devad, addr), v in mdio_values.items(): + cmd_str = 'clx_diag phy set mdio unit={0} portlist={1} devad={2} addr={3} data={4}'.format( + unit, clx_port, devad, addr, v) + popen_obj.stdin.write(cmd_str + '\n') + time.sleep(0.005) + cmd_change.append(cmd_str) + + _get_port_running_config_mdio_exec_cmd(popen_obj, user_port_list) + + return cmd_change + + +@decro_timeit +@decro_get_docker_exec +def _set_port_running_config_check_output( + popen_obj, + media_changed, + media_config, + pre_emphasis_changed, + pre_emphasis_config, + user_port_list): + # set pre-emphasis in chip + exec_cmd = _set_port_running_media_pre_emphasis_exec_cmd(popen_obj, + media_changed, + media_config, + pre_emphasis_changed, + pre_emphasis_config, + user_port_list) + + return exec_cmd + + +@decro_timeit +@decro_get_docker_exec +def _set_port_running_config_mdio_check_output( + popen_obj, + mdio_changed, + mdio_config, + user_port_list): + # set pre-emphasis in chip + exec_cmd = _set_port_running_mdio_exec_cmd(popen_obj, mdio_changed, mdio_config, user_port_list) + + return exec_cmd + + +@decro_timeit +def _set_port_running_config_hw(media_changed, + media_config, + mdio_changed, + mdio_config, + pre_emphasis_changed, + pre_emphasis_config, + user_port_list): + # exec media and pre-emphasis config cmd + cmd_change_media_pre_emphasis, output = _set_port_running_config_check_output(media_changed, + media_config, + pre_emphasis_changed, + pre_emphasis_config, + user_port_list) + running_media, running_pre_emphasis = _get_port_running_config(output, pre_emphasis_config, user_port_list) + + # exec mdio cmd + cmd_change_mdio, output = _set_port_running_config_mdio_check_output(mdio_changed, mdio_config, user_port_list) + running_mdio = _get_port_running_config_mdio(output, user_port_list) + + check_media_changed, check_mdio_changed, check_pre_emphasis_changed = \ + _cmp_running_media_pre_emphasis(running_media, + media_config, + running_mdio, + mdio_config, + running_pre_emphasis, + pre_emphasis_config) + cmd_change = cmd_change_media_pre_emphasis + cmd_change_mdio + ret = (cmd_change, check_media_changed, running_media, check_mdio_changed, running_mdio, + check_pre_emphasis_changed, running_pre_emphasis) + return ret + + +@decro_timeit +def _set_port_running_config(media_changed, + media_config, + mdio_changed, + mdio_config, + pre_emphasis_changed, + pre_emphasis_config, + user_port_list): + + ret = _set_port_running_config_hw(media_changed, + media_config, + mdio_changed, + mdio_config, + pre_emphasis_changed, + pre_emphasis_config, + user_port_list) + cmd_change = ret[0] + check_media_changed = ret[1] + running_media = ret[2] + running_mdio = ret[4] + check_pre_emphasis_changed = ret[5] + running_pre_emphasis = ret[6] + if check_media_changed: + err_msg = "{}: change port {} media from {} to {} failed\n".format( + _get_func_meta(), user_port_list, running_media, media_config) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + if check_pre_emphasis_changed: + err_msg = "{}: change port {} pre-emphasis from {} to {} failed\n".format( + _get_func_meta(), user_port_list, running_pre_emphasis, pre_emphasis_config) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + if media_changed: + g_sfp_logger.critical("{}: change port {} media from {} to {} succeed\n".format( + _get_func_meta(), user_port_list, running_media, media_config)) + + if mdio_changed: + g_sfp_logger.critical("{}: change port {} mdio from {} to {} succeed\n".format( + _get_func_meta(), user_port_list, running_mdio, mdio_config)) + + if pre_emphasis_changed: + g_sfp_logger.critical("{}: change port {} pre-emphasis from {} to {} succeed\n".format( + _get_func_meta(), user_port_list, running_pre_emphasis, pre_emphasis_config)) + + return cmd_change + + +def update_phy_port_sfp_info(phy_port_value, sfp_info): + if len(phy_port_value) > 0: + phy_port_value[KEY_PHY_PORT_INFO_V_PRESENT_SFP_INFO] = sfp_info + + +@decro_timeit +def _update_phy_port_running_config(phy_port_value): + """ + update media and pre-emphasis config of phy port according to sfp_info + :param phy_port_value: + :return: + """ + user_port_list = _get_phy_user_port_list(phy_port_value) + + # get phy port media config + port_media_config = _get_phy_port_media(user_port_list) + + # get phy port mdio config + port_mdio_config = _get_phy_port_mdio(user_port_list) + + # get phy port sfp specific pre-emphasis config + phy_port_all_pre_emphasis = phy_port_value[KEY_PHY_PORT_INFO_V_PRE_EMPHASIS] + dsh_file = phy_port_value[KEY_PHY_PORT_INFO_V_USR_CFG_DSH] + if dsh_file is not None: + port_pre_emphasis_config = phy_port_all_pre_emphasis[dsh_file] + else: + first_user_port = user_port_list.values()[0] + media_type = first_user_port[KEY_PHY_PORT_INFO_V_USER_PORT_V_MEDIA] + if 'sr' in media_type: + port_pre_emphasis_config = phy_port_all_pre_emphasis[DEFAULT_PRE_EMPHASIS_OPT_CFG_FILE_NAME] + else: + port_pre_emphasis_config = phy_port_all_pre_emphasis[DEFAULT_PRE_EMPHASIS_DAC_CFG_FILE_NAME] + + # check if running media and pre-emphasis in chip is need to set + media_changed, mdio_changed, pre_emphasis_changed = _check_port_running_config( + port_media_config, port_mdio_config, port_pre_emphasis_config, user_port_list) + + # running config in chip is correct, + if (media_changed == 0) and (mdio_changed == 0) and (pre_emphasis_changed == 0): + g_sfp_logger.critical('{}: port {} are correctly set already\n'.format(_get_func_meta(), + user_port_list)) + return [] + + # get and save user port admin status + user_port_admin_status = _get_phy_port_admin_status(user_port_list) + + # shutdown user ports + try: + _admin_down_user_ports(user_port_list) + except Exception as e: + _admin_up_user_ports(user_port_admin_status) + raise e + + # set port media of phy port + cmd_change = _set_port_running_config(media_changed, + port_media_config, + mdio_changed, + port_mdio_config, + pre_emphasis_changed, + port_pre_emphasis_config, + user_port_list) + + # recover admin status of logical ports + _admin_up_user_ports(user_port_admin_status) + + return cmd_change + + +@decro_timeit +def _is_sys_running(): + """ + check if swss docker is running + :return: True for running, false for not running + """ + + # check redis database to confirm it's running status + pcmd = subprocess.Popen(['redis-cli'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + cmd_input = 'select 1\nHLEN HIDDEN\n' + cmd_out = pcmd.communicate(cmd_input) + if cmd_out[1] != '': + # cmd exec fail + err_msg = "{}: cmd={} fail: cmd_out:{}\n)".format(_get_func_meta(), cmd_input, cmd_out) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + pt = re.compile(r'OK.*(\d+).*', re.I | re.S) + result = pt.search(cmd_out[0]) + if result is None: + err_msg = "{}: cmd={} match fail: cmd_out:{}\n)".format(_get_func_meta(), cmd_input, cmd_out[0]) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + # check if interface is initialized + cmd = ['show', 'interface', 'status'] + pcmd = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + cmd_out = pcmd.communicate() + if cmd_out[1] != '': + # cmd exec fail + err_msg = "{}: cmd={} fail: cmd_out:{}\n)".format(_get_func_meta(), cmd, cmd_out) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + # Interface Lanes Speed MTU Alias Oper Admin + # ----------- ------- ------- ----- ------------ ------ ------- + # Ethernet12 20 10G 9100 Ethernet13/1 up up + pt_str = r'''^\s* + (?P\w+)\s+ #interface + (?P[\d,]+)\s+ #lanes + [\w/]+\s+ #speed + \d+\s+ #mtu + (?P[\w/]+)\s+ + (up|down)\s+ + (?P\w+)\s* + .*$ + ''' + pt = re.compile(pt_str, re.I | re.M | re.X) + result = pt.search(cmd_out[0]) + if result is None: + err_msg = "{}: cmd={} match fail: cmd_out:{}\n)".format(_get_func_meta(), cmd, cmd_out[0]) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + return True + + +def _get_pre_emphasis_change_cmd(cmd_queue): + """ + get all change cmd from msg queue with none block method + :param cmd_queue: + :return: + """ + all_change_cmd = [] + + if cmd_queue is None: + return all_change_cmd + + # do not block! + while not cmd_queue.empty(): + try: + change_cmd = cmd_queue.get(False) + all_change_cmd += change_cmd + except que.Empty: + pass + all_change_cmd = [cmd[len('clx_diag '):] for cmd in all_change_cmd] + return all_change_cmd + + +def _partion_start_up_config(clx_cfg_full_path): + worker_id = threading.current_thread() + + # split content of port_cfg.clx into n partions + pat_str = r'''^\s*port\s+set\s+property\s+ #property + ([\w]+\s*=\s*(?P\d+)\s+){0,1} #unit=0 + [\w]+\s*=\s*(?P[\d\-]+)\s+ #portlist=0 + medium-type\s*=\s*(?P\w+) #media-type=sr + .*$ + ''' + pat_pre_media = re.compile(pat_str, re.M | re.I | re.X) + + pat_str = r'''^\s*phy\s+set\s+mdio\s+ #phy set mdio + ([\w]+\s*=\s*(?P\d+)\s+){0,1} #unit=0 + [\w]+\s*=\s*(?P[\d\-]+)\s+ #portlist=0 + [\w\-]+\s*=\s*(?P\w+)\s+ #devad + [\w]+\s*=\s*(?P\w+)\s+ #addr + .*$ + ''' + pat_pre_mdio = re.compile(pat_str, re.M | re.I | re.X) + + pat_str = r'''^\s*phy\s+set\s+pre-emphasis\s+ #phy set pre-emphasis + ([\w]+\s*=\s*(?P\d+)\s+){0,1} #unit=0 + [\w]+\s*=\s*(?P[\d\-]+)\s+ #portlist=0 + [\w\-]+\s*=\s*(?P\d+)\s+ #lane-cnt=4 + [\w]+\s*=\s*(?P\w+)\s+ #property=cn1 + .*$ + ''' + pat_pre_emphasis = re.compile(pat_str, re.M | re.I | re.X) + + block_reg_dict = {'media': pat_pre_media, 'mdio': pat_pre_mdio, 'emphasis': pat_pre_emphasis} + block_index = {} + + cfg_file_block_buf = [] + with open(clx_cfg_full_path, mode='r') as clx_cfg_fp: + lines_buf = clx_cfg_fp.readlines() + cur_block_index = -1 + last_block_type = 'NULL' + for line in lines_buf: + if line.strip() == '' and last_block_type != 'NULL': + 'keep blank lines' + cur_block_type = last_block_type + else: + cur_block_type = 'other' + for k in block_reg_dict: + result = block_reg_dict[k].search(line) + if result is not None: + cur_block_type = k + break + + if (cur_block_type != last_block_type) and \ + ((cur_block_type not in block_reg_dict) or (cur_block_type not in block_index)): + cur_block_index += 1 + block_index[cur_block_type] = cur_block_index + cfg_file_block_buf.append(line) + else: + dst_index = block_index[cur_block_type] + cfg_file_block_buf[dst_index] += line + + last_block_type = cur_block_type + + for i in range(0, len(cfg_file_block_buf)): + g_sfp_logger.debug('worker {} split {} to {} parts part {} = {}\n'.format( + worker_id, clx_cfg_full_path, len(cfg_file_block_buf), i, cfg_file_block_buf[i])) + + return cfg_file_block_buf, block_index + + +def _update_media_cmd_of_startup_cfg(startup_cfg_buf, block_index, change_cmd_buf): + worker_id = threading.current_thread() + + pat_str = r'''^\s*port\s+set\s+property\s+ + (unit\s*=\s*(?P\d+)\s+){0,1} #unit=0 + [\w]+\s*=\s*(?P[\d\-]+)\s+ #portlist=0 + medium-type\s*=\s*(?P\w+) #media-type=sr + .*$ + ''' + pat = re.compile(pat_str, re.M | re.I | re.X) + for cmd in change_cmd_buf: + result = pat.search(cmd) + if result is None: + continue + unit = result.group('unit') + if unit is None: + unit = 0 + clx_port = result.group('portlist') + change_cmd_pat_str = r'''^\s*port\s+set\s+property\s+ + (unit\s*=\s*{}\s+){} #unit=0 + portlist\s*=\s*{}\s+ #portlist=0 + .*$ + ''' + change_cmd_pat_str = change_cmd_pat_str.format(unit, '{0,1}', clx_port) + change_cmd_buf_pat = re.compile(change_cmd_pat_str, re.M | re.I | re.X) + startup_cfg_buf[block_index], sub_cnt = change_cmd_buf_pat.subn(cmd, startup_cfg_buf[block_index]) + if sub_cnt == 0: + startup_cfg_buf[block_index] += cmd + '\n' + g_sfp_logger.debug('worker {} new cmd {} add in buffer\n'.format(worker_id, cmd)) + else: + g_sfp_logger.debug('worker {} cmd {} updated in buffer\n'.format(worker_id, cmd)) + + +def _update_mdio_cmd_of_startup_cfg(startup_cfg_buf, block_index, change_cmd_buf): + worker_id = threading.current_thread() + + pat_str = r'''^phy\s+set\s+mdio\s+ #phy set mdio + (unit\s*=\s*(?P\d+)\s+){0,1} #unit=0 + portlist\s*=\s*(?P[\d\-]+)\s+ #portlist=0 + devad\s*=\s*(?P\w+)\s+ #devad + addr\s*=\s*(?P\w+)\s+ #addr + .*$ + ''' + pat = re.compile(pat_str, re.M | re.I | re.X) + for cmd in change_cmd_buf: + result = pat.search(cmd) + if result is None: + continue + unit = result.group('unit') + if unit is None: + unit = 0 + clx_port = result.group('portlist') + devad = result.group('devad') + addr = result.group('addr') + + change_cmd_pat_str = r'''^phy\s+set\s+mdio\s+ #phy set mdio + (unit\s*=\s*{}\s+){} #unit=0 + portlist\s*=\s*{}\s+ #portlist=0 + devad\s*=\s*{}\s+ #devad + addr\s*=\s*{}\s+ #addr + .*$ + ''' + change_cmd_pat_str = change_cmd_pat_str.format(unit, '{0,1}', clx_port, devad, addr) + change_cmd_buf_pat = re.compile(change_cmd_pat_str, re.M | re.I | re.X) + startup_cfg_buf[block_index], sub_cnt = change_cmd_buf_pat.subn(cmd, startup_cfg_buf[block_index]) + if sub_cnt == 0: + startup_cfg_buf[block_index] += cmd + '\n' + g_sfp_logger.debug('worker {} new cmd {} add in buffer\n'.format(worker_id, cmd)) + else: + g_sfp_logger.debug('worker {} cmd {} updated in buffer\n'.format(worker_id, cmd)) + + +def _update_pre_emphasis_cmd_of_startup_cfg(startup_cfg_buf, block_index, change_cmd_buf): + worker_id = threading.current_thread() + + pat_str = r'''^phy\s+set\s+pre-emphasis\s+ #phy set pre-emphasis + (unit\s*=\s*(?P\d+)\s+){0,1} #unit=0 + portlist\s*=\s*(?P[\d\-]+)\s+ #portlist=0 + lane-cnt\s*=\s*(?P\d+)\s+ #lane-cnt=4 + property\s*=\s*(?P\w+)\s+ #property=cn1 + .*$ + ''' + pat = re.compile(pat_str, re.M | re.I | re.X) + for cmd in change_cmd_buf: + result = pat.search(cmd) + if result is None: + continue + unit = result.group('unit') + if unit is None: + unit = 0 + clx_port = result.group('portlist') + lane_cnt = result.group('lane_cnt') + property_data = result.group('property') + + change_cmd_pat_str = r'''^phy\s+set\s+pre-emphasis\s+ #phy set pre-emphasis + (unit\s*=\s*{}\s+){} #unit=0 + portlist\s*=\s*{}\s+ #portlist=0 + lane-cnt\s*=\s*{}\s+ #lane-cnt=4 + property\s*=\s*{}\s+ #property=cn1 + .*$ + ''' + change_cmd_pat_str = change_cmd_pat_str.format(unit, '{0,1}', clx_port, lane_cnt, property_data) + change_cmd_buf_pat = re.compile(change_cmd_pat_str, re.M | re.I | re.X) + startup_cfg_buf[block_index], sub_cnt = change_cmd_buf_pat.subn(cmd, startup_cfg_buf[block_index]) + if sub_cnt == 0: + startup_cfg_buf[block_index] += cmd + '\n' + g_sfp_logger.debug('worker {} new cmd {} add in buffer\n'.format(worker_id, cmd)) + else: + g_sfp_logger.debug('worker {} cmd {} updated in buffer\n'.format(worker_id, cmd)) + + +@decro_timeit +def _auto_save_startup_config(cmd_queue): + """ + auto save change cmd in port_cfg.clx + :param cmd_queue: + :return: + """ + + worker_id = threading.current_thread() + + # get all change cmds from msg queue + change_cmd = _get_pre_emphasis_change_cmd(cmd_queue) + if len(change_cmd) == 0: + return 0 + + platform_path, hwsku_path = _get_path_to_platform_hwsku() + clx_cfg_full_path = os.path.join(hwsku_path, PORT_CFG_CLX_FILE_NAME) + clx_cfg_exist = os.path.isfile(clx_cfg_full_path) + clx_cfg_backup_full_path = clx_cfg_full_path + '.backup' + backup_exist = os.path.isfile(clx_cfg_backup_full_path) + + # check port_cfg.clx and backup file + if (clx_cfg_exist, backup_exist) == (True, True): + # perfect + g_sfp_logger.debug('worker {} {} and {} are exist.\n'.format( + worker_id, clx_cfg_full_path, clx_cfg_backup_full_path)) + elif (clx_cfg_exist, backup_exist) == (True, False): + # backup port_config.clx + shutil.copy(clx_cfg_full_path, clx_cfg_backup_full_path) + g_sfp_logger.critical('worker {} backup {} to {}\n'.format( + worker_id, clx_cfg_full_path, clx_cfg_backup_full_path)) + elif (clx_cfg_exist, backup_exist) == (False, True): + # restore port_config.clx from backup file + shutil.copy(clx_cfg_backup_full_path, clx_cfg_full_path) + g_sfp_logger.warning('worker {} {} is restored from {}\n'.format( + worker_id, clx_cfg_full_path, clx_cfg_backup_full_path)) + elif (clx_cfg_exist, backup_exist) == (False, False): + # port_config.clx and backup are lost + g_sfp_logger.error('worker {} {} is lost\n'.format(worker_id, clx_cfg_full_path)) + return -1 + + # update cmd in cfg file buf + cfg_file_block_buf, block_index = _partion_start_up_config(clx_cfg_full_path) + if 'media' not in block_index: + media_block_index = len(cfg_file_block_buf) - 1 + else: + media_block_index = block_index['media'] + _update_media_cmd_of_startup_cfg(cfg_file_block_buf, media_block_index, change_cmd) + + if 'mdio' not in block_index: + mdio_block_index = len(cfg_file_block_buf) - 1 + else: + mdio_block_index = block_index['mdio'] + _update_mdio_cmd_of_startup_cfg(cfg_file_block_buf, mdio_block_index, change_cmd) + + if 'emphasis' not in block_index: + pre_emphasis_block_index = len(cfg_file_block_buf) - 1 + else: + pre_emphasis_block_index = block_index['emphasis'] + _update_pre_emphasis_cmd_of_startup_cfg(cfg_file_block_buf, pre_emphasis_block_index, change_cmd) + + # write new config to port_cfg.clx + clx_cfg_tmp_full_path = clx_cfg_full_path + '.tmp' + updated_cfg_content = '' + for buf_block in cfg_file_block_buf: + updated_cfg_content += buf_block + + with open(clx_cfg_tmp_full_path, mode='w') as clx_cfg_tmp_fp: + clx_cfg_tmp_fp.write(updated_cfg_content) + g_sfp_logger.debug('worker {} write cmd to {}\n'.format(worker_id, clx_cfg_tmp_full_path)) + + os.remove(clx_cfg_full_path) + g_sfp_logger.debug('worker {} remove {}\n'.format(worker_id, clx_cfg_full_path)) + + os.rename(clx_cfg_tmp_full_path, clx_cfg_full_path) + g_sfp_logger.debug('worker {} rename {} to {}\n'.format(worker_id, clx_cfg_tmp_full_path, clx_cfg_full_path)) + + g_sfp_logger.critical('worker {} save cmd to {}\n'.format(worker_id, clx_cfg_full_path)) + + +@decro_timeit +def _detect_config_by_port(phy_port_key, phy_port_value): + """ + + :param phy_port_key: + :param phy_port_value: + :return: + """ + if KEY_PHY_PORT_INFO_V_USER_PORT not in phy_port_value: + err_msg = "{} : phy port has no user port {}\n".format(_get_func_meta(), phy_port_key) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + # check sfp info and set media and mdio config for phy port + user_cfg_meida = phy_port_value[KEY_PHY_PORT_INFO_V_USR_CFG_MEDIA] + if user_cfg_meida is None: + # 0 means sfp is not present, we can't auto detect + if 0 == len(phy_port_value[KEY_PHY_PORT_INFO_V_PRESENT_SFP_INFO]): + return [] + + # set media and pre-emphasis of port + cmd_change = _update_phy_port_running_config(phy_port_value) + + return cmd_change + + +@decro_timeit +def _detect_config_by_phy_port(port_port_info_dict, msg_queue, debug_verbose): + """ + check sfp type and set pre-emphasis of all ports + :param port_port_info_dict: + :param msg_queue: + :param debug_verbose: + :return: + """ + + all_changed_cmd = [] + for phy_port_key, phy_port_value in port_port_info_dict.items(): + + # show port config detail info before detect and config + if debug_verbose > 0: + print('Running settings before detecting:') + _show_phy_port({phy_port_key: phy_port_value}, debug_verbose) + + port_changed_cmd = _detect_config_by_port(phy_port_key, phy_port_value) + all_changed_cmd += port_changed_cmd + + # show port config detail info after detect and config + if debug_verbose > 0: + print('Running settings after detecting:') + _show_phy_port({phy_port_key: phy_port_value}, debug_verbose) + + # send sfp change cmd to auto save dynamic pre-emphasis config + if (msg_queue is not None) and (len(all_changed_cmd) != 0): + msg_queue.put(all_changed_cmd) + g_sfp_logger.info('{}: change pre-emphasis:\n{}\n'.format(_get_func_meta(), all_changed_cmd)) + + +def _parse_user_port_list(user_port_input=None): + """ + parse user input user port group string like 'Ethernet1-4,Ethernet7' to a + phycial port list [0, 1, 2], + :param user_port_input: + :return: + """ + + phy_port_list = [] + if user_port_input is None: + phy_port_list = sorted(g_phy_port_info.keys(), key=int) + return phy_port_list + + user_port_list = [] + for port_num_seg in [s.strip() for s in user_port_input.split(',')]: + seg_list = port_num_seg.split('-') + if len(seg_list) == 2: + user_port_list.extend(['Ethernet' + str(p) for p in range(int(seg_list[0].strip('Ethernet')), + int(seg_list[1].strip('Ethernet')) + 1)]) + elif len(seg_list) == 1: + user_port_list.append('Ethernet' + seg_list[0].strip('Ethernet')) + else: + err_msg = "{}: invalid user port name {}\n".format( + _get_func_meta(), '-'.join(seg_list)) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + for user_port in user_port_list: + if user_port not in g_user_port_2_phy_port_info: + err_msg = "{}: invalid user port {}\n".format(_get_func_meta(), user_port) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + phy_port = g_user_port_2_phy_port_info[user_port] + if phy_port not in g_phy_port_info: + err_msg = "{}: invalid user port {} has no valid phy port mapping entry {}\n".format( + _get_func_meta(), user_port, phy_port) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + phy_port_list.append(phy_port) + + return phy_port_list + + +def _parse_user_config_media_list(user_config_media): + """ + get user config media list ['dac', 'opt'] from user input string 'dac,opt' + :param user_config_media: + :return: + """ + if user_config_media is None: + target_user_config_media_list = [None] + else: + target_user_config_media_list = [m for m in user_config_media.split(',')] + for media in target_user_config_media_list: + if media != 'opt' and media != 'dac': + err_msg = "{}: invalid user port media type {}, it should be opt or dac\n".format( + _get_func_meta(), media) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + return target_user_config_media_list + + +@decro_timeit +def get_dsh_file_list(): + """ + list all valid dsh file in device dir for user + :return: + """ + dsh_file_list = [] + + platform_path, hwsku_path = _get_path_to_platform_hwsku() + for f in os.listdir(hwsku_path): + if os.path.splitext(f)[1] == '.dsh': + dsh_file_list.append(f) + + return dsh_file_list + + +def _parse_dsh_file_list(dsh_file_input=None): + """ + parse user input dsh file string like oem_a.dsh,oem_b.dsh to a list [oem_a.dsh, oem_b.dsh] + :param dsh_file_input: + :return: + """ + + if dsh_file_input is None: + dsh_file_list = [None] + else: + dsh_file_list = [f.strip() for f in dsh_file_input.split(',')] + all_dsh_file = get_dsh_file_list() + diff_dsh_file = set(dsh_file_list) - set(all_dsh_file) + if len(diff_dsh_file) != 0: + err_msg = "{}: {} does not exist, all valid dsh files are {}\n".format( + _get_func_meta(), diff_dsh_file, all_dsh_file) + g_sfp_logger.error(err_msg) + raise Exception(err_msg) + + return dsh_file_list + + +def _get_dsh_file_path(): + target_dsh_file = [] + + dsh_file_name_list = get_dsh_file_list() + platform_path, hwsku_path = _get_path_to_platform_hwsku() + for dsh_file in dsh_file_name_list: + target_dsh_file.append(os.path.join(hwsku_path, dsh_file)) + + return target_dsh_file + + +@decro_timeit +def _init_phy_port_info(): + # first of all, we need to init logging + _init_logging() + + # _is_sys_running() + # init global phy port info database form config.ini and port_config.clx + _parse_phy_port_info() + + # get dsh file + target_dsh_file_list = _get_dsh_file_path() + _parse_pre_emphasis_info(target_dsh_file_list) + + # set default media and mdio for port + for phy_port, phy_port_value in g_phy_port_info.items(): + first_user_port = phy_port_value[KEY_PHY_PORT_INFO_V_USER_PORT].itervalues().next() + media_hw = first_user_port[KEY_PHY_PORT_INFO_V_USER_PORT_V_MEDIA] + media = 'opt' if 'sr' in media_hw else 'dac' + _set_phy_port_media_mdio(phy_port_value, media) + + +@decro_timeit +def _init_detect_config_port(phy_port_media_dsh): + """ + get phy port configuration of user port and valid dsh file + :phy_port_media_dsh:{'0': (None, 'clounix_dac.dsh')} + :return: + """ + target_phy_port_info_dict = collections.OrderedDict() + + # get target_phy_port_info_dict from key list + for phy_port, (media, dsh) in phy_port_media_dsh.items(): + target_phy_port_info_dict[phy_port] = g_phy_port_info[phy_port] + target_phy_port_info_dict[phy_port][KEY_PHY_PORT_INFO_V_USR_CFG_MEDIA] = media + target_phy_port_info_dict[phy_port][KEY_PHY_PORT_INFO_V_USR_CFG_DSH] = dsh + user_port_name_list = ','.join(target_phy_port_info_dict[phy_port][KEY_PHY_PORT_INFO_V_USER_PORT].keys()) + if target_phy_port_info_dict[phy_port][KEY_PHY_PORT_INFO_V_USR_CFG_MEDIA] is None: + if KEY_PHY_PORT_INFO_V_PRESENT_SFP_INFO not in target_phy_port_info_dict[phy_port]: + present = _get_port_sfp_present_status(int(phy_port)) + if not present: + target_phy_port_info_dict[phy_port][KEY_PHY_PORT_INFO_V_PRESENT_SFP_INFO] = {} + print('No transceiver is plugged into {}'.format(user_port_name_list)) + else: + target_phy_port_info_dict[phy_port][KEY_PHY_PORT_INFO_V_PRESENT_SFP_INFO] = \ + _get_port_sfp_info(int(phy_port)) + sfp_info = target_phy_port_info_dict[phy_port][KEY_PHY_PORT_INFO_V_PRESENT_SFP_INFO] + media_type = _get_sfp_type(sfp_info) + if len(sfp_info) != 0: + print('{} transceiver is plugged into port {}'.format( + 'opt(fiber)'.title() if media_type == 'opt' else 'dac(copper)'.title(), + user_port_name_list)) + else: + media_type = target_phy_port_info_dict[phy_port][KEY_PHY_PORT_INFO_V_USR_CFG_MEDIA] + print('{} transceiver is configured by user for port {}'.format( + 'opt(fiber)'.title() if media_type == 'opt' else 'dac(copper)'.title(), + user_port_name_list)) + + _set_phy_port_media_mdio(target_phy_port_info_dict[phy_port], media_type) + + return target_phy_port_info_dict + + +@decro_timeit +@decro_get_docker_exec +def _get_port_raw_allin1_running_config(popen_obj, phy_port_value): + """ + get raw running media, pre-emphasis, mdio config of given user port list, + this func depend on _init_phy_info, so call call _init_phy_info and then call + this func + :param popen_obj: + :param phy_port_value: + :return: + """ + exec_cmd = [] + + phy_port_lane_cnt = len(phy_port_value[KEY_PHY_PORT_INFO_V_LANE_LIST]) + user_port_list = phy_port_value[KEY_PHY_PORT_INFO_V_USER_PORT] + portlist_key_ol = sorted(list(user_port_list)) + for user_port in portlist_key_ol: + k = KEY_PHY_PORT_INFO_V_USER_PORT_V_CLX_PORT + unit, clx_port = user_port_list[user_port][k] + + # display media type of port + cmd_str = 'clx_diag port show property unit={0} portlist={1}\n'.format( + unit, clx_port) + popen_obj.stdin.write(cmd_str) + exec_cmd.append(cmd_str) + time.sleep(0.005) + + # display pre-emphasis + if portlist_key_ol.index(user_port) == 0: + prop_list = ['c2', 'cn1', 'c1', 'c0'] + for prop in prop_list: + cmd_str = 'clx_diag phy show pre-emphasis unit={0} portlist={1}' \ + ' lane-cnt={2} property={3}'.format(unit, clx_port, phy_port_lane_cnt, prop) + '\n' + popen_obj.stdin.write(cmd_str) + exec_cmd.append(cmd_str) + time.sleep(0.005) + + # show mdio + k = KEY_PHY_PORT_INFO_V_USER_PORT_V_MDIO + mdio_config_ol = sorted(list(user_port_list[user_port][k])) + for dev, add in mdio_config_ol: + cmd_str = 'clx_diag phy show mdio unit={0} portlist={1}' \ + ' devad={2} addr={3}'.format(unit, clx_port, dev, add) + '\n' + popen_obj.stdin.write(cmd_str) + exec_cmd.append(cmd_str) + time.sleep(0.005) + + return exec_cmd + + +def _get_port_allin1_running_config(phy_port_value): + """ + get hw running meida, pre-emphasis and mdio etc. + :param phy_port_value: + :return: + """ + # get runnming settings of phy + # exec_cmd: + # clx_diag port show property unit=0 portlist=0 + # clx_diag phy show pre-emphasis unit=0 portlist=0 lane-cnt=4 property=c2 + # clx_diag phy show pre-emphasis unit=0 portlist=0 lane-cnt=4 property=cn1 + # clx_diag phy show pre-emphasis unit=0 portlist=0 lane-cnt=4 property=c1 + # clx_diag phy show pre-emphasis unit=0 portlist=0 lane-cnt=4 property=c0 + # clx_diag phy show mdio unit=0 portlist=0 devad=0x1e addr=0x2 + # output_line_list = output.split('\n'): + # ['port speed medium admin an eee fec flow-ctrl status loopback cut-through', + # '---- ----- ------ ----- --- --- --- --------- ------ -------- -----------', + # '0 100g sr4 en dis dis dis dis down dis dis ', + # 'port 000: data=0x0002.0002.0002.0002', + # 'port 000: data=0x0001.0001.0001.0001', + # 'port 000: data=0x0007.0007.0007.0007', + # 'port 000: data=0x001a.001a.001a.001a', + # 'port 000: data=0x8001', ''] + exec_cmd, output = _get_port_raw_allin1_running_config(phy_port_value) + output_line_list = [l for l in output.split('\n') if len(l) > 0] + if len(output_line_list) < 8: + print('Get media failed: \nExec_cmd:\n{}\nCmd_output:\n{}\n'.format(exec_cmd, output)) + return + + media = [v for v in output_line_list[2].split(' ') if len(v) > 0][2].strip() + idx = output_line_list[3].find('0x') + pre_emphasis = 'c2:{}\n'.format(output_line_list[3][idx:]) + pre_emphasis += 'cn1:{}\n'.format(output_line_list[4][idx:]) + pre_emphasis += 'c1:{}\n'.format(output_line_list[5][idx:]) + pre_emphasis += 'c0:{}\n'.format(output_line_list[6][idx:]) + mdio = '{}\n'.format(output_line_list[7][idx:]) + + return {KEY_PHY_PORT_INFO_V_USER_PORT_V_MEDIA: media, + KEY_PHY_PORT_INFO_V_PRE_EMPHASIS: pre_emphasis, + KEY_PHY_PORT_INFO_V_USER_PORT_V_MDIO: mdio} + + +@decro_timeit +def _show_phy_port(phy_port_info, debug_verbose): + """ + This func depends on _init_phy_port_info. + Display phy port to user port mapping, and running meida, pre-emphasis + and mdio setting in chip of given ports. + :param phy_port_info: + :param debug_verbose: + :return: + """ + fmt = 'simple' + table = [] + headers = ['Logic port', 'Media'] + if debug_verbose > 0: + headers.extend(['Pre-emphasis', 'MDIO', 'Phy port', 'Lanes', 'CLX Port']) + + for phy_port, phy_port_value in phy_port_info.items(): + for user_port, user_port_info_value in phy_port_value[KEY_PHY_PORT_INFO_V_USER_PORT].items(): + if debug_verbose == 0: + media = user_port_info_value[KEY_PHY_PORT_INFO_V_USER_PORT_V_MEDIA] + media = 'dac(copper)' if 'cr' in media else 'opt(fiber)' + port_row = [user_port, media] + else: + running_config = _get_port_allin1_running_config(phy_port_value) + media = running_config[KEY_PHY_PORT_INFO_V_USER_PORT_V_MEDIA] + media = 'dac(copper)' if 'cr' in media else 'opt(fiber)' + pre_emphasis = running_config[KEY_PHY_PORT_INFO_V_PRE_EMPHASIS] + mdio = running_config[KEY_PHY_PORT_INFO_V_USER_PORT_V_MDIO] + port_row = [user_port, media, pre_emphasis, mdio, phy_port, + user_port_info_value[KEY_PHY_PORT_INFO_V_USER_PORT_V_LANES], + user_port_info_value[KEY_PHY_PORT_INFO_V_USER_PORT_V_CLX_PORT]] + table.append(port_row) + + print(tabulate(table, headers, tablefmt=fmt)) + + +@decro_timeit +def _convert_user_port_to_phy_port(user_port_str=None, + user_config_media_str=None, + dsh_file_str=None): + """ + get phy port tuple from user input + :param user_port_str: + :param user_config_media_str: + :param dsh_file_str: + :return:{'0': (None, None), '1': ('opt', None)] + """ + + # get phy port dict from user input list string like Ethernet1-4;Ethernet8 + phy_port_list = _parse_user_port_list(user_port_str) + + # broadcast last user config media value to all other ports, and update into g_phy_port_info + media_list = _parse_user_config_media_list(user_config_media_str) + media_list.extend(list(itertools.repeat(media_list[-1], len(phy_port_list) - len(media_list)))) + + # parse user input dsh file list + dsh_list = _parse_dsh_file_list(dsh_file_str) + dsh_list.extend(list(itertools.repeat(dsh_list[-1], len(phy_port_list) - len(dsh_list)))) + + target_phy_port_media_dsh = collections.OrderedDict() + for port in phy_port_list: + idx = phy_port_list.index(port) + target_phy_port_media_dsh.update({port: (media_list[idx], dsh_list[idx])}) + + return target_phy_port_media_dsh + + +@decro_timeit +def show_user_port(user_port_str=None, debug_verbose=0): + """ + This func depends on _init_phy_port_info. + Display phy port to user port mapping, and running meida, pre-emphasis + and mdio setting in chip of given ports. + Phy port num: 0 + Lanes: + User port list: + Ethernet0: + Lanes: + Clx port: + Transceiver info: + Transceiver media type: opt + Running media,pre-emphasis and mdio setting: + ... + :param user_port_str: + :param debug_verbose + :return: + """ + + _init_phy_port_info() + + target_phy_port_info = collections.OrderedDict() + phy_port_media_dsh = _convert_user_port_to_phy_port(user_port_str) + for phy_port in phy_port_media_dsh: + target_phy_port_info[phy_port] = g_phy_port_info[phy_port] + + _show_phy_port(target_phy_port_info, debug_verbose) + + +@decro_timeit +def show_dsh_file(): + """ + display dsh files list + :return: + """ + _init_logging() + + dsh_file_list = get_dsh_file_list() + for f in dsh_file_list: + print(f) + + +@decro_timeit +def detect_config_by_phy_port(config_file=START_UP_CFG_FILE, + debug_verbose=0): + """ + detect and get sfp Transceiver info, then config pre-emphasis, media, mdio value in chip, + this func should be called when system is booting up + :param phy_port_media_dsh:{'0': (None, 'clounix_dac.dsh')} + :param debug_verbose: + :return: + """ + with open(config_file, 'r') as fp: + phy_port_media_dsh = json.load(fp, object_pairs_hook=collections.OrderedDict) + + _init_phy_port_info() + + # get target phy port dict + phy_port_info_dict = _init_detect_config_port(phy_port_media_dsh) + + # detect and config phy ports + msg_queue = que.Queue() + _detect_config_by_phy_port(phy_port_info_dict, msg_queue, debug_verbose) + + # auto save or log port dynamic pre-emphasis config to port_config.clx + _auto_save_startup_config(msg_queue) + + +@decro_timeit +def detect_config_by_user_port(user_port_str=None, + user_config_media_str=None, + dsh_file_str=None, + debug_verbose=0): + """ + detect and get sfp Transceiver info, then config pre-emphasis, media, mdio value in chip + :param user_port_str: + :param user_config_media_str: + :param dsh_file_str: + :param debug_verbose: + :return: + """ + + _init_phy_port_info() + + # get target phy port dict + phy_port_media_dsh = _convert_user_port_to_phy_port(user_port_str, user_config_media_str, dsh_file_str) + + # get target phy port dict + phy_port_info_dict = _init_detect_config_port(phy_port_media_dsh) + + # detect and config phy ports + msg_queue = que.Queue() + _detect_config_by_phy_port(phy_port_info_dict, msg_queue, debug_verbose) + + # auto save or log port dynamic pre-emphasis config to port_config.clx + _auto_save_startup_config(msg_queue) + + # if detect port succeed, update start up config(START_UP_CFG_FILE) + cfg = collections.OrderedDict() + with open(START_UP_CFG_FILE, mode='a+') as fp: + try: + cfg = json.load(fp, object_pairs_hook=collections.OrderedDict) + except ValueError as e: + err_msg = "{}: {} in {}\n".format(_get_func_meta(), e, START_UP_CFG_FILE) + g_sfp_logger.info(err_msg) + pass + cfg.update(phy_port_media_dsh) + with open(START_UP_CFG_FILE, mode='w') as fp: + json.dump(cfg, fp, indent=4) + + +# This is our main entrypoint - the main 'sfputil' command +@click.group() +def cli(): + """sfpdet - Command line utility for configuring port's MEDIA""" + + if os.geteuid() != 0: + print "Root privileges are required for this operation" + sys.exit(1) + # Make sure user has reboot after use port-breakout cli! + if get_state_db(PORT_CFG_STATE_NAME) == STATE_MODIFIED: + click.echo('Please reboot after port breakout!!') + sys.exit(1) + + +# 'show' subgroup +@cli.group() +def show(): + """Display status of SFP transceivers""" + pass + + +# 'media' subcommand +@show.command() +@click.option('-p', '--port', metavar='', help="Port list like Ethernet1;Ethernet3-5") +@click.option('-v', '--verbose', metavar='', count=True, default=0, help="Detail debug info") +def media(port=None, verbose=0): + """Display all port info include physical, logical, clx port, transceiver and running media, + pre-emphasis and mdio phy setting info""" + try: + show_user_port(port, verbose) + except Exception as e: + print(traceback.format_exc(e)) + + + +# 'dshfile' subcommand +@show.command() +def dshfile(): + """Display available dsh files for transceiver.""" + try: + show_dsh_file() + except Exception as e: + print(traceback.format_exc(e)) + + +def get_portcfg_state(self): + return get_state_db(PORT_CFG_STATE_NAME) + +# 'config' subcommand +@cli.command() +@click.option('-p', '--port', metavar='', help="Port list like Ethernet1,Ethernet3-5") +@click.option('-m', '--media', metavar='', help="Transceiver media type `opt` or `dac`") +@click.option('-d', '--dsh', metavar='', help=".dsh file list like vendor_an_opt.dsh,vendor_an_dac.dsh") +@click.option('-v', '--verbose', metavar='', count=True, help="Detail debug info") +def config(port=None, media=None, dsh=None, verbose=0): + """Auto detect and configure pre-emphasis, media and mdio for port.""" + try: + detect_config_by_user_port(port, media, dsh, verbose) + except Exception as e: + print(traceback.format_exc(e)) + +@cli.command('recover-from-json') +@click.pass_context +@click.option('-j', '--json-file', + metavar='', + required=False, + default=START_UP_CFG_FILE) +@click.option('-v', '--verbose', metavar='', count=True, help="detail debug info") +def recover_from_json(ctx, json_file, verbose=0): + """Recover port MEDIA configuration from json.""" + detect_config_by_phy_port(json_file, verbose) + + +if __name__ == '__main__': + cli() diff --git a/platform/clounix/clx-utils/clounix-utils/setup.py b/platform/clounix/clx-utils/clounix-utils/setup.py new file mode 100644 index 000000000000..89923b6934eb --- /dev/null +++ b/platform/clounix/clx-utils/clounix-utils/setup.py @@ -0,0 +1,30 @@ +from setuptools import setup +import glob + +setup( + name='clx-utils', + version='0.1', + description='Clounix Command-line utilities for SONiC', + license='Apache 2.0', + maintainer='Zhiqian.Wu', + maintainer_email='Zhiqian.Wu@clounixinc.com', + author='Clounix Team', + packages=[ + 'port_breakout', + ], + package_data={ + 'port_breakout': ['port_config.clx.j2'] + }, + scripts=[ + 'scripts/sfpdet' + ], + data_files=[ + ('/etc/bash_completion.d', glob.glob('bash_completion.d/*')), + ], + entry_points={ + 'console_scripts': [ + 'port-breakout=port_breakout.main:cli' + ] + }, + keywords='sonic SONiC utilities command line cli CLI clounix' +) diff --git a/platform/clounix/docker-ptf-clounix.mk b/platform/clounix/docker-ptf-clounix.mk new file mode 100644 index 000000000000..7595a2678bd0 --- /dev/null +++ b/platform/clounix/docker-ptf-clounix.mk @@ -0,0 +1,7 @@ +# docker image for docker-ptf-clounix + +DOCKER_PTF_CLOUNIX = docker-ptf-clounix.gz +$(DOCKER_PTF_CLOUNIX)_PATH = $(DOCKERS_PATH)/docker-ptf +$(DOCKER_PTF_CLOUNIX)_DEPENDS += $(LIBTHRIFT) $(PYTHON_THRIFT) $(PTF) $(PYTHON_SAITHRIFT) +$(DOCKER_PTF_CLOUNIX)_LOAD_DOCKERS += $(DOCKER_BASE_BUSTER) +SONIC_DOCKER_IMAGES += $(DOCKER_PTF_CLOUNIX) diff --git a/platform/clounix/docker-saiserver-clounix.mk b/platform/clounix/docker-saiserver-clounix.mk new file mode 100644 index 000000000000..beb4a48a0208 --- /dev/null +++ b/platform/clounix/docker-saiserver-clounix.mk @@ -0,0 +1,22 @@ +# docker image for clounix saiserver + +DOCKER_SAISERVER_CLOUNIX_STEM = docker-saiserver-clounix +DOCKER_SAISERVER_CLOUNIX = $(DOCKER_SAISERVER_CLOUNIX_STEM).gz +$(DOCKER_SAISERVER_CLOUNIX)_PATH = $(PLATFORM_PATH)/docker-saiserver-clounix +$(DOCKER_SAISERVER_CLOUNIX)_DEPENDS += $(SAISERVER) $(CLOUNIX_SAI_DEV) +$(DOCKER_SAISERVER_CLOUNIX)_FILES += $(DSSERVE) +$(DOCKER_SAISERVER_CLOUNIX)_LOAD_DOCKERS += $(DOCKER_CONFIG_ENGINE_BUSTER) +$(DOCKER_SAISERVER_CLOUNIX)_DBG_IMAGE_PACKAGES = $($(DOCKER_CONFIG_ENGINE_BUSTER)_DBG_IMAGE_PACKAGES) + + +SONIC_DOCKER_IMAGES += $(DOCKER_SAISERVER_CLOUNIX) +DOCKER_SAISERVER_CLOUNIX_DBG = $(DOCKER_SAISERVER_CLOUNIX_STEM)-$(DBG_IMAGE_MARK).gz +SONIC_DOCKER_DBG_IMAGES += $(DOCKER_SAISERVER_CLOUNIX_DBG) + +$(DOCKER_SAISERVER_CLOUNIX)_CONTAINER_NAME = saiserver +$(DOCKER_SAISERVER_CLOUNIX)_BASE_IMAGE_FILES += clx_ipython:/usr/bin/clx_ipython +$(DOCKER_SAISERVER_CLOUNIX)_RUN_OPT += --net=host --privileged -t +$(DOCKER_SAISERVER_CLOUNIX)_RUN_OPT += -v /host/machine.conf:/etc/machine.conf +$(DOCKER_SAISERVER_CLOUNIX)_RUN_OPT += -v /var/run/docker-saiserver:/var/run/sswsyncd +$(DOCKER_SAISERVER_CLOUNIX)_RUN_OPT += -v /etc/sonic:/etc/sonic:ro +$(DOCKER_SAISERVER_CLOUNIX)_RUN_OPT += -v /host/warmboot:/var/warmboot diff --git a/platform/clounix/docker-saiserver-clounix/Dockerfile.j2 b/platform/clounix/docker-saiserver-clounix/Dockerfile.j2 new file mode 100644 index 000000000000..f9be48490700 --- /dev/null +++ b/platform/clounix/docker-saiserver-clounix/Dockerfile.j2 @@ -0,0 +1,72 @@ +FROM docker-config-engine-buster + +ARG docker_container_name +COPY ["base_image_files/rsyslog.conf", "/etc/rsyslog.conf"] +RUN [ -f /etc/rsyslog.conf ] && sed -ri "s/%syslogtag%/$docker_container_name#%syslogtag%/;" /etc/rsyslog.conf + +## Make apt-get non-interactive +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install libboost-atomic1.71.0 + +COPY \ +{% for deb in docker_saiserver_clounix_debs.split(' ') -%} +debs/{{ deb }}{{' '}} +{%- endfor -%} +debs/ + +RUN dpkg_apt() { [ -f $1 ] && { dpkg -i $1 || apt-get -y install -f; } || return 1; } ; \ +{% for deb in docker_saiserver_clounix_debs.split(' ') -%} +dpkg_apt debs/{{ deb }}{{'; '}} +{%- endfor %} + +## TODO: add kmod into Depends +RUN apt-get install -yf kmod \ +# Install IPython, jupyter-console packages + jupyter-console \ + jupyter-client + +COPY ["files/dsserve", "start.sh", "/usr/bin/"] +COPY ["clx_diag", "/usr/bin/"] +COPY ["warm-verifier", "/usr/bin/"] +RUN chmod +x /usr/bin/clx_diag /usr/bin/warm-verifier /usr/bin/dsserve + +COPY ["base_image_files", "/etc/clounix"] +COPY ["base_image_files/port_config.ini", "/etc/sai/portmap.ini"] +COPY ["profile.ini", "/etc/sai/"] + +COPY ["supervisord.conf", "/etc/supervisor/conf.d/"] + +# IPython kernel config +COPY ["ipython_config.json", "/etc/ipython/"] + +# Install IPython Cling Kernel +#{%- if INCLUDE_CLING_KERNEL == "yes" %} +#COPY ["install_clingkernel.sh", "/usr/bin/"] +#COPY ["cling-0.9-buster.tar", "/opt/"] +#RUN /usr/bin/install_clingkernel.sh + +RUN apt-get install -y build-essential \ + libc-dev \ + libc6-dev-i386 + +ENV CLING_OPTS "-I/usr/include/sai/ \ + -I/usr/include/clounix/ \ + -I/usr/include/clx_system/clx_sdk/include \ + -I/usr/include/clx_system/clx_sdk/include/cdb \ + -I/usr/include/clx_system/clx_sdk/include/osal \ + -I/usr/include/clx_system/clx_sdk/src/inc \ + -DCLX_EN_HOST_64_BIT_LITTLE_ENDIAN \ + -DCLX_EN_LITTLE_ENDIAN \ + -DCLX_EN_COMPILER_SUPPORT_LONG_LONG \ + -DCLX_LINUX_USER_MODE \ + -DCLX_SDK \ + -DCLX_EN_NETIF \ + -DCLX_EN_64BIT_ADDR " +#{%- endif %} + +## Clean up +RUN apt-get clean -y; apt-get autoclean -y; apt-get autoremove -y +RUN rm -rf /debs + +ENTRYPOINT ["/usr/local/bin/supervisord"] diff --git a/platform/clounix/docker-saiserver-clounix/base_image_files/00README.md b/platform/clounix/docker-saiserver-clounix/base_image_files/00README.md new file mode 100644 index 000000000000..d61c1ad2134d --- /dev/null +++ b/platform/clounix/docker-saiserver-clounix/base_image_files/00README.md @@ -0,0 +1,21 @@ +Clounix 12.8T Leo Link Recover +============================= + +### For 400G(8x50g PAM4) DAC cable like between FP1 and FP2 +```shell +lightning_linkup-dac.sh portlist=0,8 400g cr8 8 + +### For 400G(8x50g PAM4) AOC cable like between FP1 and FP2 +```shell +lightning_linkup-aoc.sh portlist=0,8 400g lr8 8 +``` + +### For 100G(4x25g NRZ) DAC cable like between FP1 and FP2 +```shell +lightning_linkup-dac.sh portlist=0,4 100g cr4 4 +``` + +### For 100G(4x25g NRZ) AOC cable like between FP1 and FP2 +```shell +lightning_linkup-aoc.sh portlist=0,4 100g lr4 4 +``` diff --git a/platform/clounix/docker-saiserver-clounix/base_image_files/clx_diag b/platform/clounix/docker-saiserver-clounix/base_image_files/clx_diag new file mode 100755 index 0000000000000000000000000000000000000000..8d47ca16c6144156447f3146f46d43f31a80459a GIT binary patch literal 10456 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 literal 0 HcmV?d00001 diff --git a/platform/clounix/docker-saiserver-clounix/clx_diag b/platform/clounix/docker-saiserver-clounix/clx_diag new file mode 100755 index 0000000000000000000000000000000000000000..8d47ca16c6144156447f3146f46d43f31a80459a GIT binary patch literal 10456 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 literal 0 HcmV?d00001 diff --git a/platform/clounix/docker-syncd-clounix-rpc.dep b/platform/clounix/docker-syncd-clounix-rpc.dep new file mode 100644 index 000000000000..70d3d06ad61c --- /dev/null +++ b/platform/clounix/docker-syncd-clounix-rpc.dep @@ -0,0 +1,9 @@ +#DPKG FRK +DPATH := $($(DOCKER_SYNCD_CLOUNIX_RPC)_PATH) +DEP_FILES := $(SONIC_COMMON_FILES_LIST) platform/clounix/docker-syncd-clounix-rpc.mk platform/clounix/docker-syncd-clounix-rpc.dep +DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST) +DEP_FILES += $(shell git ls-files $(DPATH)) + +$(DOCKER_SYNCD_BASE)_CACHE_MODE := GIT_CONTENT_SHA +$(DOCKER_SYNCD_BASE)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST) +$(DOCKER_SYNCD_BASE)_DEP_FILES := $(DEP_FILES) diff --git a/platform/clounix/docker-syncd-clounix-rpc.mk b/platform/clounix/docker-syncd-clounix-rpc.mk new file mode 100644 index 000000000000..2e1ef4b2dc1b --- /dev/null +++ b/platform/clounix/docker-syncd-clounix-rpc.mk @@ -0,0 +1,27 @@ +# docker image for clounix syncd with rpc + +DOCKER_SYNCD_CLOUNIX_RPC = docker-syncd-clounix-rpc.gz +$(DOCKER_SYNCD_CLOUNIX_RPC)_PATH = $(PLATFORM_PATH)/docker-syncd-clounix-rpc +$(DOCKER_SYNCD_CLOUNIX_RPC)_DEPENDS += $(SYNCD_RPC) $(LIBTHRIFT) $(PTF) +$(DOCKER_SYNCD_CLOUNIX_RPC)_DBG_DEPENDS += $(SYNCD_RPC_DBG) \ + $(LIBSWSSCOMMON_DBG) \ + $(LIBSAIMETADATA_DBG) \ + $(LIBSAIREDIS_DBG) +$(DOCKER_SYNCD_CLOUNIX_RPC)_FILES += $(DSSERVE) +$(DOCKER_SYNCD_CLOUNIX_RPC)_LOAD_DOCKERS += $(DOCKER_SYNCD_BASE) +SONIC_DOCKER_IMAGES += $(DOCKER_SYNCD_CLOUNIX_RPC) +ifeq ($(ENABLE_SYNCD_RPC),y) +SONIC_INSTALL_DOCKER_IMAGES += $(DOCKER_SYNCD_CLOUNIX_RPC) +endif + +$(DOCKER_SYNCD_CLOUNIX_RPC)_CONTAINER_NAME = syncd + +$(DOCKER_SYNCD_CLOUNIX_RPC)_BASE_IMAGE_FILES += clx_diag:/usr/bin/clx_diag +$(DOCKER_SYNCD_CLOUNIX_RPC)_BASE_IMAGE_FILES += clx_ipython:/usr/bin/clx_ipython +$(DOCKER_SYNCD_CLOUNIX_RPC)_BASE_IMAGE_FILES += clx_icling:/usr/bin/clx_icling + +$(DOCKER_SYNCD_CLOUNIX_RPC)_RUN_OPT += --net=host --privileged -t +$(DOCKER_SYNCD_CLOUNIX_RPC)_RUN_OPT += -v /host/machine.conf:/etc/machine.conf +$(DOCKER_SYNCD_CLOUNIX_RPC)_RUN_OPT += -v /host/warmboot:/var/warmboot +$(DOCKER_SYNCD_CLOUNIX_RPC)_RUN_OPT += -v /var/run/docker-syncd:/var/run/sswsyncd +$(DOCKER_SYNCD_CLOUNIX_RPC)_RUN_OPT += -v /etc/sonic:/etc/sonic:ro diff --git a/platform/clounix/docker-syncd-clounix-rpc/Dockerfile.j2 b/platform/clounix/docker-syncd-clounix-rpc/Dockerfile.j2 new file mode 100644 index 000000000000..f1ecf58a2aa1 --- /dev/null +++ b/platform/clounix/docker-syncd-clounix-rpc/Dockerfile.j2 @@ -0,0 +1,59 @@ +FROM docker-syncd-clounix + +## Make apt-get non-interactive +ENV DEBIAN_FRONTEND=noninteractive + +COPY \ +{% for deb in docker_syncd_clounix_rpc_debs.split(' ') -%} +debs/{{ deb }}{{' '}} +{%- endfor -%} +debs/ + +RUN apt-get purge -y syncd + +## Pre-install the fundamental packages +RUN apt-get update \ + && apt-get -y install \ + net-tools \ + python-pip \ + python-setuptools \ + build-essential \ + libssl-dev \ + libffi-dev \ + python-dev \ + wget \ + cmake \ + libqt5core5a \ + libqt5network5 \ + libboost-atomic1.71.0 + +RUN dpkg_apt() { [ -f $1 ] && { dpkg -i $1 || apt-get -y install -f; } || return 1; } ; \ +{% for deb in docker_syncd_clounix_rpc_debs.split(' ') -%} +dpkg_apt debs/{{ deb }}{{'; '}} +{%- endfor %} + +RUN wget https://github.com/nanomsg/nanomsg/archive/1.0.0.tar.gz \ + && tar xvfz 1.0.0.tar.gz \ + && cd nanomsg-1.0.0 \ + && mkdir -p build \ + && cmake . \ + && make install \ + && ldconfig \ + && cd .. \ + && rm -fr nanomsg-1.0.0 \ + && rm -f 1.0.0.tar.gz \ + && pip2 install cffi==1.7.0 \ + && pip2 install --upgrade cffi==1.7.0 \ + && pip2 install wheel \ + && pip2 install nnpy \ + && mkdir -p /opt \ + && cd /opt \ + && git clone https://github.com/p4lang/ptf.git \ + && cp ptf/ptf_nn/ptf_nn_agent.py ./ \ + && apt-get clean -y; apt-get autoclean -y; apt-get autoremove -y \ + && rm -rf /root/deps + +RUN chmod +x /usr/bin/clx_diag /usr/bin/warm-verifier /usr/bin/dsserve +COPY ["ptf_nn_agent.conf", "/etc/supervisor/conf.d/"] + +ENTRYPOINT ["/usr/local/bin/supervisord"] diff --git a/platform/clounix/docker-syncd-clounix-rpc/base_image_files/clx_diag b/platform/clounix/docker-syncd-clounix-rpc/base_image_files/clx_diag new file mode 100755 index 000000000000..d6fd648de5c6 --- /dev/null +++ b/platform/clounix/docker-syncd-clounix-rpc/base_image_files/clx_diag @@ -0,0 +1,3 @@ +#!/bin/bash + +docker exec -i syncd clx_diag "$@" diff --git a/platform/clounix/docker-syncd-clounix-rpc/base_image_files/clx_icling b/platform/clounix/docker-syncd-clounix-rpc/base_image_files/clx_icling new file mode 100755 index 000000000000..994343866af0 --- /dev/null +++ b/platform/clounix/docker-syncd-clounix-rpc/base_image_files/clx_icling @@ -0,0 +1,5 @@ +#!/bin/bash + +docker exec -it syncd clx_diag icling +docker exec -it syncd jupyter-console --existing clx-icling-shell.json + diff --git a/platform/clounix/docker-syncd-clounix-rpc/base_image_files/clx_ipython b/platform/clounix/docker-syncd-clounix-rpc/base_image_files/clx_ipython new file mode 100755 index 000000000000..818832f3b952 --- /dev/null +++ b/platform/clounix/docker-syncd-clounix-rpc/base_image_files/clx_ipython @@ -0,0 +1,5 @@ +#!/bin/bash + +docker exec -it syncd clx_diag ipy +docker exec -it syncd jupyter-console --existing clx-ipython-shell.json + diff --git a/platform/clounix/docker-syncd-clounix-rpc/ptf_nn_agent.conf b/platform/clounix/docker-syncd-clounix-rpc/ptf_nn_agent.conf new file mode 100644 index 000000000000..fa1ed0eb1622 --- /dev/null +++ b/platform/clounix/docker-syncd-clounix-rpc/ptf_nn_agent.conf @@ -0,0 +1,10 @@ +[program:ptf_nn_agent] +command=/usr/bin/python /opt/ptf_nn_agent.py --device-socket 1@tcp://0.0.0.0:10900 -i 1-3@Ethernet12 --set-iface-rcv-buffer=109430400 +process_name=ptf_nn_agent +stdout_logfile=/tmp/ptf_nn_agent.out.log +stderr_logfile=/tmp/ptf_nn_agent.err.log +redirect_stderr=false +autostart=true +autorestart=true +startsecs=1 +numprocs=1 diff --git a/platform/clounix/docker-syncd-clounix.dep b/platform/clounix/docker-syncd-clounix.dep new file mode 100644 index 000000000000..e26d484e8c89 --- /dev/null +++ b/platform/clounix/docker-syncd-clounix.dep @@ -0,0 +1,11 @@ +#DPKG FRK +DPATH := $($(DOCKER_SYNCD_BASE)_PATH) +DEP_FILES := $(SONIC_COMMON_FILES_LIST) platform/clounix/docker-syncd-clounix.mk platform/clounix/docker-syncd-clounix.dep platform/clounix/sai.mk +DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST) +DEP_FILES += $(shell git ls-files $(DPATH)) + +$(DOCKER_SYNCD_BASE)_CACHE_MODE := GIT_CONTENT_SHA +$(DOCKER_SYNCD_BASE)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST) +$(DOCKER_SYNCD_BASE)_DEP_FILES := $(DEP_FILES) + +$(eval $(call add_dbg_docker,$(DOCKER_SYNCD_BASE),$(DOCKER_SYNCD_BASE_DBG))) diff --git a/platform/clounix/docker-syncd-clounix.mk b/platform/clounix/docker-syncd-clounix.mk new file mode 100644 index 000000000000..0c2e249567f1 --- /dev/null +++ b/platform/clounix/docker-syncd-clounix.mk @@ -0,0 +1,19 @@ +# docker image for clounix syncd + +DOCKER_SYNCD_PLATFORM_CODE = clounix +include $(PLATFORM_PATH)/../template/docker-syncd-base.mk + +$(DOCKER_SYNCD_BASE)_DEPENDS += $(SYNCD) $(CLOUNIX_SAI_DEV) $(CLOUNIX_WARM_VERIFIER) +$(DOCKER_SYNCD_BASE)_FILES = $(DSSERVE) + +$(DOCKER_SYNCD_BASE)_DBG_DEPENDS += $(SYNCD_DBG) \ + $(LIBSWSSCOMMON_DBG) \ + $(LIBSAIMETADATA_DBG) \ + $(LIBSAIREDIS_DBG) + +$(DOCKER_SYNCD_BASE)_RUN_OPT += -v /host/warmboot:/var/warmboot +$(DOCKER_SYNCD_BASE)_RUN_OPT += -v /var/run/docker-syncd:/var/run/sswsyncd + +$(DOCKER_SYNCD_BASE)_BASE_IMAGE_FILES += clx_diag:/usr/bin/clx_diag +$(DOCKER_SYNCD_BASE)_BASE_IMAGE_FILES += clx_ipython:/usr/bin/clx_ipython +$(DOCKER_SYNCD_BASE)_BASE_IMAGE_FILES += clx_icling:/usr/bin/clx_icling diff --git a/platform/clounix/docker-syncd-clounix/Dockerfile.j2 b/platform/clounix/docker-syncd-clounix/Dockerfile.j2 new file mode 100755 index 000000000000..5d722ba3a888 --- /dev/null +++ b/platform/clounix/docker-syncd-clounix/Dockerfile.j2 @@ -0,0 +1,76 @@ +{% from "dockers/dockerfile-macros.j2" import install_debian_packages %} +FROM docker-config-engine-buster + +ARG docker_container_name +RUN [ -f /etc/rsyslog.conf ] && sed -ri "s/%syslogtag%/$docker_container_name#%syslogtag%/;" /etc/rsyslog.conf + +## Make apt-get non-interactive +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update + +COPY \ +{% for deb in docker_syncd_clounix_debs.split(' ') -%} +debs/{{ deb }}{{' '}} +{%- endfor -%} +debs/ + +COPY \ +{% for deb in docker_syncd_clounix_pydebs.split(' ') -%} +python-debs/{{ deb }}{{' '}} +{%- endfor -%} +debs/ + +RUN apt-get install -y libxml2 \ +# Install IPython, jupyter-console packages + jupyter-console \ + jupyter-client + +RUN dpkg -i \ +{% for deb in docker_syncd_clounix_debs.split(' ') -%} +debs/{{ deb }}{{' '}} +{%- endfor %} + +##RUN dpkg -i \ +##{% for deb in docker_syncd_clounix_pydebs.split(' ') -%} +##debs/{{ deb }}{{' '}} +##{%- endfor %} + +COPY ["files/dsserve", "/usr/bin/"] +RUN chmod +x /usr/bin/dsserve + +COPY ["supervisord.conf", "/etc/supervisor/conf.d/"] +COPY ["files/supervisor-proc-exit-listener", "/usr/bin"] +COPY ["critical_processes", "/etc/supervisor/"] + +# IPython kernel config +COPY ["ipython_config.json", "/etc/ipython/"] + +# Install IPython Cling Kernel +#COPY ["install_clingkernel.sh", "/usr/bin/"] +#COPY ["cling-0.9-buster.tar", "/opt/"] +#RUN /usr/bin/install_clingkernel.sh + +RUN apt-get install -y build-essential \ + libc-dev \ + libc6-dev-i386 + +ENV CLING_OPTS "-I/usr/include/sai/ \ + -I/usr/include/clounix/ \ + -I/usr/include/clx_system/clx_sdk/include \ + -I/usr/include/clx_system/clx_sdk/include/cdb \ + -I/usr/include/clx_system/clx_sdk/include/osal \ + -I/usr/include/clx_system/clx_sdk/src/inc \ + -DCLX_EN_HOST_64_BIT_LITTLE_ENDIAN \ + -DCLX_EN_LITTLE_ENDIAN \ + -DCLX_EN_COMPILER_SUPPORT_LONG_LONG \ + -DCLX_LINUX_USER_MODE \ + -DCLX_SDK \ + -DCLX_EN_NETIF \ + -DCLX_EN_64BIT_ADDR " + +## Clean up +RUN apt-get clean -y; apt-get autoclean -y; apt-get autoremove -y +RUN rm -rf /debs + +ENTRYPOINT ["/usr/local/bin/supervisord"] diff --git a/platform/clounix/docker-syncd-clounix/base_image_files/clx_diag b/platform/clounix/docker-syncd-clounix/base_image_files/clx_diag new file mode 100755 index 000000000000..d6fd648de5c6 --- /dev/null +++ b/platform/clounix/docker-syncd-clounix/base_image_files/clx_diag @@ -0,0 +1,3 @@ +#!/bin/bash + +docker exec -i syncd clx_diag "$@" diff --git a/platform/clounix/docker-syncd-clounix/base_image_files/clx_icling b/platform/clounix/docker-syncd-clounix/base_image_files/clx_icling new file mode 100755 index 000000000000..994343866af0 --- /dev/null +++ b/platform/clounix/docker-syncd-clounix/base_image_files/clx_icling @@ -0,0 +1,5 @@ +#!/bin/bash + +docker exec -it syncd clx_diag icling +docker exec -it syncd jupyter-console --existing clx-icling-shell.json + diff --git a/platform/clounix/docker-syncd-clounix/base_image_files/clx_ipython b/platform/clounix/docker-syncd-clounix/base_image_files/clx_ipython new file mode 100755 index 000000000000..818832f3b952 --- /dev/null +++ b/platform/clounix/docker-syncd-clounix/base_image_files/clx_ipython @@ -0,0 +1,5 @@ +#!/bin/bash + +docker exec -it syncd clx_diag ipy +docker exec -it syncd jupyter-console --existing clx-ipython-shell.json + diff --git a/platform/clounix/docker-syncd-clounix/critical_processes b/platform/clounix/docker-syncd-clounix/critical_processes new file mode 100644 index 000000000000..d1163a9c3046 --- /dev/null +++ b/platform/clounix/docker-syncd-clounix/critical_processes @@ -0,0 +1,2 @@ +program:dsserve +program:syncd diff --git a/platform/clounix/docker-syncd-clounix/install_clingkernel.sh b/platform/clounix/docker-syncd-clounix/install_clingkernel.sh new file mode 100755 index 000000000000..5596647dcee1 --- /dev/null +++ b/platform/clounix/docker-syncd-clounix/install_clingkernel.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +echo "Install IPython ClingKernel ..." +cd /opt/ && tar xvf cling-0.9-buster.tar -C /usr/ && \ +cd /usr/share/cling/tools/Jupyter/kernel/ && \ +pip3 install -e . && \ +jupyter-kernelspec install cling-cpp11 && \ +jupyter-kernelspec install cling-cpp14 && \ +jupyter-kernelspec install cling-cpp1z && \ +jupyter-kernelspec install cling-cpp17 && \ +jupyter-kernelspec list && \ +rm -rf /opt/cling-0.9-buster.tar +echo "Done" diff --git a/platform/clounix/docker-syncd-clounix/ipython_config.json b/platform/clounix/docker-syncd-clounix/ipython_config.json new file mode 100644 index 000000000000..ce59391ab3bd --- /dev/null +++ b/platform/clounix/docker-syncd-clounix/ipython_config.json @@ -0,0 +1,6 @@ +{ + "ConnectionFileMixin": { + "connection_file" : "clx-ipython-shell.json" + } +} + diff --git a/platform/clounix/docker-syncd-clounix/supervisord.conf b/platform/clounix/docker-syncd-clounix/supervisord.conf new file mode 100644 index 000000000000..3437400338be --- /dev/null +++ b/platform/clounix/docker-syncd-clounix/supervisord.conf @@ -0,0 +1,38 @@ +[supervisord] +logfile_maxbytes=1MB +logfile_backups=2 +nodaemon=true + +[eventlistener:dependent-startup] +command=python3 -m supervisord_dependent_startup +autostart=true +autorestart=unexpected +startretries=0 +exitcodes=0,3 +events=PROCESS_STATE +buffer_size=25 + +[eventlistener:supervisor-proc-exit-listener] +command=python3 /usr/bin/supervisor-proc-exit-listener --container-name syncd +events=PROCESS_STATE_EXITED,PROCESS_STATE_RUNNING +autostart=true +autorestart=unexpected + +[program:rsyslogd] +command=/usr/sbin/rsyslogd -n -iNONE +priority=1 +autostart=false +autorestart=unexpected +stdout_logfile=syslog +stderr_logfile=syslog +dependent_startup=true + +[program:syncd] +command=/usr/bin/syncd_start.sh +priority=2 +autostart=false +autorestart=false +stdout_logfile=syslog +stderr_logfile=syslog +dependent_startup=true +dependent_startup_wait_for=rsyslogd:running diff --git a/platform/clounix/libsaithrift-dev.dep b/platform/clounix/libsaithrift-dev.dep new file mode 100644 index 000000000000..658835665636 --- /dev/null +++ b/platform/clounix/libsaithrift-dev.dep @@ -0,0 +1,13 @@ +#DPKG FRK +SPATH := $($(LIBSAITHRIFT_DEV)_SRC_PATH) +DEP_FILES := $(SONIC_COMMON_FILES_LIST) platform/clounix/libsaithrift-dev.mk platform/clounix/libsaithrift-dev.dep +DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST) +SMDEP_PATHS := $(SPATH) +$(foreach path, $(SMDEP_PATHS), $(eval $(path) :=$(filter-out $(SMDEP_PATHS),$(addprefix $(path)/, $(shell cd $(path) && git ls-files | grep -Ev " " ))))) + +$(LIBSAITHRIFT_DEV)_CACHE_MODE := GIT_CONTENT_SHA +$(LIBSAITHRIFT_DEV)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST) +$(LIBSAITHRIFT_DEV)_DEP_FILES := $(DEP_FILES) +$(LIBSAITHRIFT_DEV)_SMDEP_FILES := $(foreach path, $(SMDEP_PATHS), $($(path))) +$(LIBSAITHRIFT_DEV)_SMDEP_PATHS := $(SMDEP_PATHS) + diff --git a/platform/clounix/libsaithrift-dev.mk b/platform/clounix/libsaithrift-dev.mk new file mode 100644 index 000000000000..906ce44b4238 --- /dev/null +++ b/platform/clounix/libsaithrift-dev.mk @@ -0,0 +1,20 @@ +# libsaithrift-dev package + +SAI_VER = 1.7.1 + +LIBSAITHRIFT_DEV = libsaithrift-dev_$(SAI_VER)_amd64.deb +$(LIBSAITHRIFT_DEV)_SRC_PATH = $(PLATFORM_PATH)/saithrift/ +$(LIBSAITHRIFT_DEV)_DEPENDS += $(LIBTHRIFT) $(LIBTHRIFT_DEV) $(PYTHON_THRIFT) $(THRIFT_COMPILER) $(CLOUNIX_SAI) $(CLOUNIX_SAI_DEV) +$(LIBSAITHRIFT_DEV)_RDEPENDS += $(LIBTHRIFT) $(CLOUNIX_SAI) +SONIC_DPKG_DEBS += $(LIBSAITHRIFT_DEV) + +PYTHON_SAITHRIFT = python-saithrift_$(SAI_VER)_amd64.deb +$(eval $(call add_extra_package,$(LIBSAITHRIFT_DEV),$(PYTHON_SAITHRIFT))) + +SAISERVER = saiserver_$(SAI_VER)_amd64.deb +$(SAISERVER)_RDEPENDS += $(LIBTHRIFT) $(CLOUNIX_SAI) +$(eval $(call add_extra_package,$(LIBSAITHRIFT_DEV),$(SAISERVER))) + +SAISERVER_DBG = saiserver-dbg_$(SAI_VER)_amd64.deb +$(SAISERVER_DBG)_RDEPENDS += $(SAISERVER) +$(eval $(call add_extra_package,$(LIBSAITHRIFT_DEV),$(SAISERVER_DBG))) diff --git a/platform/clounix/one-image.dep b/platform/clounix/one-image.dep new file mode 100644 index 000000000000..c5399d808172 --- /dev/null +++ b/platform/clounix/one-image.dep @@ -0,0 +1,2 @@ +#DPKG FRK +$(SONIC_ONE_IMAGE)_CACHE_MODE := none diff --git a/platform/clounix/one-image.mk b/platform/clounix/one-image.mk new file mode 100644 index 000000000000..b9f685effc08 --- /dev/null +++ b/platform/clounix/one-image.mk @@ -0,0 +1,16 @@ +# sonic clounix one image installer + +SONIC_ONE_IMAGE = sonic-clounix.bin +$(SONIC_ONE_IMAGE)_MACHINE = clounix +$(SONIC_ONE_IMAGE)_IMAGE_TYPE = onie +$(SONIC_ONE_IMAGE)_INSTALLS += $(CLOUNIX_MODULE) $(CLX_UTILS) +$(SONIC_ONE_IMAGE)_INSTALLS += $(SYSTEMD_SONIC_GENERATOR) +$(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(PEGATRON_FN8656_BNF_PLATFORM_MODULE) + +ifeq ($(INSTALL_DEBUG_TOOLS),y) +$(SONIC_ONE_IMAGE)_DOCKERS += $(SONIC_INSTALL_DOCKER_DBG_IMAGES) +$(SONIC_ONE_IMAGE)_DOCKERS += $(filter-out $(patsubst %-$(DBG_IMAGE_MARK).gz,%.gz, $(SONIC_INSTALL_DOCKER_DBG_IMAGES)), $(SONIC_INSTALL_DOCKER_IMAGES)) +else +$(SONIC_ONE_IMAGE)_DOCKERS = $(SONIC_INSTALL_DOCKER_IMAGES) +endif +SONIC_INSTALLERS += $(SONIC_ONE_IMAGE) diff --git a/platform/clounix/platform-modules-pegatron.dep b/platform/clounix/platform-modules-pegatron.dep new file mode 100644 index 000000000000..fc8ca5fe2bc6 --- /dev/null +++ b/platform/clounix/platform-modules-pegatron.dep @@ -0,0 +1,10 @@ + +MPATH := $($(PEGATRON_FN8656_BNF_PLATFORM_MODULE)_SRC_PATH) +DEP_FILES := $(SONIC_COMMON_FILES_LIST) platform/clounix/platform-modules-pegatron.mk platform/clounix/platform-modules-pegatron.dep +DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST) +DEP_FILES += $(shell git ls-files $(MPATH)) + +$(PEGATRON_FN8656_BNF_PLATFORM_MODULE)_CACHE_MODE := GIT_CONTENT_SHA +$(PEGATRON_FN8656_BNF_PLATFORM_MODULE)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST) +$(PEGATRON_FN8656_BNF_PLATFORM_MODULE)_DEP_FILES := $(DEP_FILES) + diff --git a/platform/clounix/platform-modules-pegatron.mk b/platform/clounix/platform-modules-pegatron.mk new file mode 100644 index 000000000000..0ae02644a4a3 --- /dev/null +++ b/platform/clounix/platform-modules-pegatron.mk @@ -0,0 +1,11 @@ +# Pegatron Platform modules +PEGATRON_PLATFORM_MODULE_VERSION = 1.0.0 +export PEGATRON_PLATFORM_MODULE_VERSION + +PEGATRON_FN8656_BNF_PLATFORM_MODULE = sonic-platform-pegatron-fn8656-bnf_$(PEGATRON_PLATFORM_MODULE_VERSION)_amd64.deb +$(PEGATRON_FN8656_BNF_PLATFORM_MODULE)_SRC_PATH = $(PLATFORM_PATH)/sonic-platform-modules-pegatron +$(PEGATRON_FN8656_BNF_PLATFORM_MODULE)_DEPENDS += $(LINUX_HEADERS) $(LINUX_HEADERS_COMMON) +$(PEGATRON_FN8656_BNF_PLATFORM_MODULE)_PLATFORM = x86_64-pegatron_fn8656_bnf-r0 +SONIC_DPKG_DEBS += $(PEGATRON_FN8656_BNF_PLATFORM_MODULE) + +$(eval $(call add_extra_package,$(PEGATRON_FN8656_BNF_PLATFORM_MODULE))) diff --git a/platform/clounix/platform.conf b/platform/clounix/platform.conf new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/platform/clounix/rules.dep b/platform/clounix/rules.dep new file mode 100644 index 000000000000..e8f9f77a0a0c --- /dev/null +++ b/platform/clounix/rules.dep @@ -0,0 +1,7 @@ +#DPKG FRK +include $(PLATFORM_PATH)/clounix-modules.dep +include $(PLATFORM_PATH)/clx-utils.dep +include $(PLATFORM_PATH)/platform-modules-pegatron.dep +include $(PLATFORM_PATH)/docker-syncd-clounix.dep +include $(PLATFORM_PATH)/docker-syncd-clounix-rpc.dep +include $(PLATFORM_PATH)/one-image.dep diff --git a/platform/clounix/rules.mk b/platform/clounix/rules.mk new file mode 100644 index 000000000000..fecb693b9cbf --- /dev/null +++ b/platform/clounix/rules.mk @@ -0,0 +1,27 @@ +include $(PLATFORM_PATH)/sai.mk +include $(PLATFORM_PATH)/clx-utils.mk +include $(PLATFORM_PATH)/clounix-modules.mk +include $(PLATFORM_PATH)/platform-modules-pegatron.mk +include $(PLATFORM_PATH)/docker-syncd-clounix.mk +include $(PLATFORM_PATH)/docker-syncd-clounix-rpc.mk +include $(PLATFORM_PATH)/one-image.mk +include $(PLATFORM_PATH)/libsaithrift-dev.mk +include $(PLATFORM_PATH)/docker-ptf-clounix.mk +include $(PLATFORM_PATH)/docker-saiserver-clounix.mk + +DSSERVE = dsserve +$(DSSERVE)_URL = "https://sonicstorage.blob.core.windows.net/packages/20190307/dsserve?sv=2015-04-05&sr=b&sig=lk7BH3DtW%2F5ehc0Rkqfga%2BUCABI0UzQmDamBsZH9K6w%3D&se=2038-05-06T22%3A34%3A45Z&sp=r" +SONIC_ONLINE_FILES += $(DSSERVE) + +SONIC_ALL += $(SONIC_ONE_IMAGE) $(DOCKER_FPM) + +# Inject clounix sai into syncd +$(SYNCD)_DEPENDS += $(CLOUNIX_SAI) $(CLOUNIX_SAI_DEV) +$(SYNCD)_UNINSTALLS += $(CLOUNIX_SAI_DEV) + +ifeq ($(ENABLE_SYNCD_RPC),y) +$(SYNCD)_DEPENDS += $(LIBSAITHRIFT_DEV) +endif + +# Runtime dependency on clounix sai is set only for syncd +$(SYNCD)_RDEPENDS += $(CLOUNIX_SAI) diff --git a/platform/clounix/sai.dep b/platform/clounix/sai.dep new file mode 100644 index 000000000000..29b5feefcece --- /dev/null +++ b/platform/clounix/sai.dep @@ -0,0 +1,13 @@ + +SPATH := $($(CLOUNIX_SAI)_SRC_PATH) +DEP_FILES := $(SONIC_COMMON_FILES_LIST) platform/clounix/sai.mk platform/clounix/sai.dep +DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST) +SMDEP_PATHS := $(SPATH) $(SPATH)/clx_system +$(foreach path, $(SMDEP_PATHS), $(eval $(path) :=$(filter-out $(SMDEP_PATHS),$(addprefix $(path)/, $(shell cd $(path) && git ls-files | grep -Ev " " ))))) + +$(CLOUNIX_SAI)_CACHE_MODE := GIT_CONTENT_SHA +$(CLOUNIX_SAI)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST) +$(CLOUNIX_SAI)_DEP_FILES := $(DEP_FILES) +$(CLOUNIX_SAI)_SMDEP_FILES := $(foreach path, $(SMDEP_PATHS), $($(path))) +$(CLOUNIX_SAI)_SMDEP_PATHS := $(SMDEP_PATHS) + diff --git a/platform/clounix/sai.mk b/platform/clounix/sai.mk new file mode 100644 index 000000000000..300615d25c03 --- /dev/null +++ b/platform/clounix/sai.mk @@ -0,0 +1,42 @@ +SDK_VERSION = 3.0.0 +SAI_HEADER_VERSION = 1.7.1.0-1.RC1 +#SAI_CHIP_ID = dawn +SAI_CHIP_ID = lightning +export SAI_HEADER_VERSION SAI_CHIP_ID + +# Place here URL where SAI deb exist +# +SAI_DEB_URL = yes +ifeq ($(SAI_DEB_URL),no) +CLOUNIX_SAI = libsaiclx_$(SAI_HEADER_VERSION)_amd64.deb +$(CLOUNIX_SAI)_SRC_PATH = $(PLATFORM_PATH)/clnx-sai +$(CLOUNIX_SAI)_DEPENDS += $(LINUX_HEADERS) $(LINUX_HEADERS_COMMON) +SONIC_DPKG_DEBS += $(CLOUNIX_SAI) + +CLOUNIX_SAI_DEV = libsaiclx-dev_$(SAI_HEADER_VERSION)_amd64.deb +$(CLOUNIX_SAI_DEV)_DEPENDS += $(CLOUNIX_SAI) + +CLOUNIX_WARM_VERIFIER = clx-app_$(SAI_HEADER_VERSION)_amd64.deb +$(CLOUNIX_WARM_VERIFIER)_DEPENDS += $(CLOUNIX_SAI) + +CLOUNIX_SAI_DBG = libsaiclx-dbg_$(SAI_HEADER_VERSION)_amd64.deb +$(CLOUNIX_SAI_DBG)_DEPENDS += $(CLOUNIX_SAI) +$(eval $(call add_derived_package,$(CLOUNIX_SAI),$(CLOUNIX_SAI_DEV))) +$(eval $(call add_derived_package,$(CLOUNIX_SAI),$(CLOUNIX_WARM_VERIFIER))) +$(eval $(call add_derived_package,$(CLOUNIX_SAI),$(CLOUNIX_SAI_DBG))) +else +CLOUNIX_SAI = libsaiclx_$(SAI_HEADER_VERSION)_amd64.deb +$(CLOUNIX_SAI)_PATH = $(PLATFORM_PATH)/clx-sai +$(CLOUNIX_SAI)_URL = "https://github.com/clounix/sai_release/raw/main/sai_available/libsaiclx_$(SAI_HEADER_VERSION)_amd64.deb" + +CLOUNIX_SAI_DEV = libsaiclx-dev_$(SAI_HEADER_VERSION)_amd64.deb +$(CLOUNIX_SAI_DEV)_PATH = $(PLATFORM_PATH)/clx-sai +$(CLOUNIX_SAI_DEV)_URL = "https://github.com/clounix/sai_release/raw/main/sai_available/libsaiclx-dev_$(SAI_HEADER_VERSION)_amd64.deb" + +CLOUNIX_SAI_DBG = libsaiclx-dbg_$(SAI_HEADER_VERSION)_amd64.deb +(CLOUNIX_SAI_DBG)_PATH = $(PLATFORM_PATH)/clx-sai +$(CLOUNIX_SAI_DBG)_URL = "https://github.com/clounix/sai_release/raw/main/sai_available/libsaiclx-dbg_$(SAI_HEADER_VERSION)_amd64.deb" +#SONIC_COPY_DEBS += $(CLOUNIX_SAI) $(CLOUNIX_SAI_DEV) +SONIC_ONLINE_DEBS+= $(CLOUNIX_SAI) $(CLOUNIX_SAI_DEV) +endif + diff --git a/platform/clounix/sonic-platform-modules-pegatron/LICENSE b/platform/clounix/sonic-platform-modules-pegatron/LICENSE new file mode 100644 index 000000000000..a23cc2b232cd --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/LICENSE @@ -0,0 +1,16 @@ +Copyright (C) 2016 Microsoft, Inc +Copyright (C) 2018 Pegatron Corporation. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 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/clounix/sonic-platform-modules-pegatron/README.md b/platform/clounix/sonic-platform-modules-pegatron/README.md new file mode 100644 index 000000000000..32444b4b8916 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/README.md @@ -0,0 +1 @@ +platform drivers of Pegatron products for the SONiC project diff --git a/platform/clounix/sonic-platform-modules-pegatron/common/modules/Makefile b/platform/clounix/sonic-platform-modules-pegatron/common/modules/Makefile new file mode 100644 index 000000000000..4a685c2650bc --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/common/modules/Makefile @@ -0,0 +1,3 @@ +obj-m += i2c-mux-pca9641.o +obj-m += pegatron_hwmon_mcu.o +obj-m += clounix_sysfs_main.o diff --git a/platform/clounix/sonic-platform-modules-pegatron/common/modules/clounix_sysfs_common.h b/platform/clounix/sonic-platform-modules-pegatron/common/modules/clounix_sysfs_common.h new file mode 100644 index 000000000000..64d878510572 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/common/modules/clounix_sysfs_common.h @@ -0,0 +1,16 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct clounix_node_info { + struct list_head node; + char name[64]; + char info[256]; + struct kobj_attribute kobj_attr; +}; diff --git a/platform/clounix/sonic-platform-modules-pegatron/common/modules/clounix_sysfs_main.c b/platform/clounix/sonic-platform-modules-pegatron/common/modules/clounix_sysfs_main.c new file mode 100644 index 000000000000..84bc19b50cdb --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/common/modules/clounix_sysfs_main.c @@ -0,0 +1,445 @@ +#include "clounix_sysfs_common.h" +#include "clounix_sysfs_var.h" + +static struct kobject *clounix_switch; +static struct kernfs_node *fail_node = NULL; +atomic_t is_work = ATOMIC_INIT(0); + +LIST_HEAD(clounix_list_head); +DEFINE_SPINLOCK(node_rw_lock); + +static inline int in_work(void) +{ + if (atomic_read(&is_work) < 0) + return 1; + + if (atomic_add_negative(1, &is_work) != 0) { + atomic_dec(&is_work); + return 1; + } + + return 0; +} + +static inline void out_work(void) +{ + atomic_dec(&is_work); +} + +static char *skip_root(char *path) +{ + if (memcmp(path, SYSFS_ROOT, strlen(SYSFS_ROOT)) == 0) { + path += strlen(SYSFS_ROOT); + return path; + } + + return NULL; +} + +static void traverse_sysfs_by_rbtree(struct kernfs_node *parent) +{ + struct rb_node *node; + struct kernfs_node *sd; + + if (parent == NULL) + return; + + node = rb_first(&(parent->dir.children)); + + while (node != NULL) { + sd = container_of(node, struct kernfs_node, rb); + printk(KERN_DEBUG "node name: %s \t type: %x parent dir %s\r\n", sd->name ? sd->name : "null", sd->flags, sd->parent->name); + node = rb_next(node); + } +} + +static struct kernfs_node *search_sysfs_node_by_path(struct kernfs_node *root, char *path) +{ + char *start, *end; + struct kernfs_node *sd = root; + + if (path == NULL || root == NULL) + goto out; + + if ((start = skip_root(path)) == NULL) + start = path; + + while (start != NULL) { + while (*start == '/') + start++; + + if ((end = strchr(start, '/')) != NULL) { + *end = '\0'; + sd = sysfs_get_dirent(sd, start); + sysfs_put(sd); + *end = '/'; + + if (sd == NULL) + goto out; + + if ((sd->flags & KERNFS_LINK) != 0) + sd = sd->symlink.target_kn; + + start = end+1; + } else { + sd = sysfs_get_dirent(sd, start); + sysfs_put(sd); + break; + } + } + + return sd; + +out: + return NULL; +} + +static struct kernfs_node *pass_path(struct kobject **kobj, struct kernfs_node *root, char *path, char **name) +{ + struct kernfs_node *sd = NULL; + char *start, *end; + + if ((start = skip_root(path)) == NULL) { + printk(KERN_DEBUG "path format err\r\n"); + return NULL; + } + + while (start != NULL) { + while (*start == '/') + start++; + + if ((end = strchr(start, '/')) != NULL) { + *end = '\0'; + if ((sd = sysfs_get_dirent(root, start)) == NULL) { + if ((*kobj = kobject_create_and_add(start, root->priv)) == NULL) + goto path_err; + + printk(KERN_DEBUG "create missing dir %s\r\n", (*kobj)->sd->name); + sd = (*kobj)->sd; + sysfs_get(sd); + } + + sysfs_put(sd); + *end = '/'; + root = sd; + start = end+1; + continue; + } + *kobj = root->priv; + *name = start; + break; + } + + return root; + +path_err: + return NULL; +} + +static int create_link_auto(struct kernfs_node *root, struct kernfs_node *target, char *path) +{ + struct kernfs_node *sd; + struct kobject *kobj = NULL; + struct kobject fake_kobj = {0}; + char *start; + int err_no; + + if ((root = pass_path(&kobj, root, path, &start)) == NULL) + return -ENOMEM; + + if ((sd = sysfs_get_dirent(root, start)) != NULL) { + sysfs_put(sd); + printk(KERN_DEBUG "link exist\r\n"); + return -EEXIST; + } + /* + Some sysfs node is create by func 'sysfs_create_group(s)', the node's kernfs_node struct's + member 'priv' is 'attribute' not 'kobject'.Can use fake kobj to create link, because the + 'sysfs_crate_link' only use kobj->sd as link source and never use other member in kobj. + */ + fake_kobj.sd = target; + + if ((err_no = sysfs_create_link(kobj, &fake_kobj, start)) != 0) { + printk(KERN_DEBUG "fail to create link %s in dir %s\r\n", start, kobj->sd->name); + return err_no; + } + + return 0; +} + +static char *format_check(const char *buf) +{ + char *p = (char *)buf; + + if ((p = strchr(p, ' ')) != NULL) { + if (*(p+1) != ' ') + return p; + else + return NULL; + } + + return NULL; +} + +static struct clounix_node_info *find_node_from_list(struct attribute *attr) +{ + struct clounix_node_info *pos = NULL; + struct clounix_node_info *tmp = NULL; + + list_for_each_entry_safe(pos, tmp, &clounix_list_head, node) { + if (attr == &(pos->kobj_attr.attr)) + return pos; + } + + return NULL; +} + +static void rm_all_node(void) +{ + struct clounix_node_info *pos = NULL; + struct clounix_node_info *tmp = NULL; + + spin_lock(&node_rw_lock); + list_for_each_entry_safe(pos, tmp, &clounix_list_head, node) { + list_del_init(&(pos->node)); + kfree(pos); + } + spin_unlock(&node_rw_lock); +} + +static ssize_t node_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + struct clounix_node_info *pos = NULL; + int len = 0; + + spin_lock(&node_rw_lock); + if ((pos = find_node_from_list(&(attr->attr))) != NULL) + len = sprintf(buf, "%s\r\n", pos->info); + spin_unlock(&node_rw_lock); + + return len; +} + +static int add_node(struct kernfs_node *root, char *buf) +{ + int err_no; + char *node_path, *info; + struct kobject *kobj; + struct kernfs_node *tmp; + struct clounix_node_info *attr_node; + + node_path = format_check(buf); + if (node_path == NULL) + return -ENOENT; + + info = node_path + 1; + *node_path = '\0'; + node_path = buf; + + if ((root = pass_path(&kobj, root, node_path, &node_path)) == NULL) + return -ENOENT; + + if (strlen(node_path) > MAX_NODE_NAME_LEN) + return -ENOMEM; + + if (strlen(info) > MAX_NODE_INFO_LEN) + return -ENOMEM; + + if ((tmp = sysfs_get_dirent(root, node_path)) != NULL) { + sysfs_put(tmp); + printk(KERN_DEBUG "node exist\r\n"); + return -EEXIST; + } + + if ((attr_node = kzalloc(sizeof(struct clounix_node_info), GFP_ATOMIC)) == NULL) + return -ENOMEM; + + strcpy(attr_node->name, node_path); + strcpy(attr_node->info, info); + attr_node->kobj_attr.attr.name = attr_node->name; + attr_node->kobj_attr.attr.mode = VERIFY_OCTAL_PERMISSIONS(0444); + attr_node->kobj_attr.show = node_show; + attr_node->kobj_attr.store = NULL; + + if ((err_no = sysfs_create_file(kobj, &(attr_node->kobj_attr.attr))) != 0) { + kfree(attr_node); + return err_no; + } + + spin_lock(&node_rw_lock); + list_add_tail(&(attr_node->node), &clounix_list_head); + spin_unlock(&node_rw_lock); + + return 0; +} + +static int del_node(struct kernfs_node *root, char *link) +{ + struct kernfs_node *sd; + struct clounix_node_info *node = NULL; + + if ((sd = search_sysfs_node_by_path(root, link)) == NULL) + goto not_find; + + if ((sd->flags & KERNFS_LINK) != 0) + sysfs_remove_link(sd->parent->priv, sd->name); + else if ((sd->flags & KERNFS_DIR) != 0) + kobject_del(sd->priv); + else if ((sd->flags & KERNFS_FILE) != 0) { + spin_lock(&node_rw_lock); + if ((node = find_node_from_list(sd->priv)) == NULL) { + spin_unlock(&node_rw_lock); + goto not_find; + } + list_del_init(&node->node); + spin_unlock(&node_rw_lock); + + sysfs_remove_file(sd->parent->priv, sd->priv); + kfree(node); + } + + return 0; + +not_find: + printk(KERN_DEBUG "del target not found\r\n"); + return -1; +} + +static int cmd_guess(const char *buf) +{ + if (memcmp(buf, DEL_CMD, strlen(DEL_CMD)) == 0) + return DEL_OPS; + else if (memcmp(buf, ADD_CMD, strlen(ADD_CMD)) == 0) + return ADD_OPS; + else + return 0; +} + +static int add_new_link(struct kernfs_node *root, char *src, char *node) +{ + struct kernfs_node *target = NULL; + + if ((target = search_sysfs_node_by_path(root, src)) == NULL) + goto path_err; + + if ((target->flags & KERNFS_LINK) != 0) + target = target->symlink.target_kn; + + create_link_auto(root, target, node); + + return 0; + +path_err: + printk(KERN_DEBUG "not find sysfs node: %s\r\n", src); + return -1; +} + +static ssize_t cmd_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + traverse_sysfs_by_rbtree(fail_node); + + return 0; +} + +static ssize_t cmd_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct kernfs_node *root = clounix_switch->sd->parent; + char *src, *pra; + char *p; + + if (in_work() == 1) + return count; + + if (count <= MIN_PATH_LEN || count > PAGE_SIZE) + goto format_err; + + if ((p = format_check(buf)) == NULL) + goto format_err; + + if ((src = kzalloc(PAGE_SIZE, GFP_ATOMIC)) == NULL) + goto format_err; + + memcpy(src, buf, count-1); + pra = src + (p - buf); + *pra = '\0'; + pra++; + + switch (cmd_guess(buf)) { + case DEL_OPS: + if (del_node(root, pra) != 0) + goto path_err; + break; + case ADD_OPS: + if (add_node(root, pra) != 0) + goto path_err; + break; + default: + if (add_new_link(root, src, pra) != 0) + goto path_err; + break; + } + + kfree(src); + out_work(); + return count; + +path_err: + printk(KERN_DEBUG "not find %s\r\n", buf); +format_err: + printk(KERN_DEBUG KERN_ALERT PATH_FORMAT); + kfree(src); + out_work(); + return count; +} + +static struct kobj_attribute ctrl_attr = __ATTR(clounix_cmd, 0644, cmd_show, cmd_store); + +static struct attribute * ctrl_attrs[] = { + &ctrl_attr.attr, + NULL +}; + +static const struct attribute_group control_attr_group = { + .attrs = ctrl_attrs, +}; + +static int __init main_init(void) +{ + int err_no = 0; + + printk(KERN_DEBUG "clounix kobj node_init!\r\n"); + + clounix_switch = kobject_create_and_add(CLOUNIX_DIR_NAME, NULL); + if (clounix_switch == NULL) + return -ENOMEM; + + if ((err_no = sysfs_create_group(clounix_switch, &control_attr_group)) != 0) + goto err_out; + + return 0; + +err_out: + printk(KERN_DEBUG "has err_no %d\r\n", err_no); + kobject_put(clounix_switch); + return -ENOMEM; +} + +static void __exit main_exit(void) +{ + printk(KERN_DEBUG "clounix kobj node_del!\r\n"); + + if (atomic_sub_return(2, &is_work) != -2) { + while (atomic_read(&is_work) != -2) {}; + } + sysfs_remove_group(clounix_switch, &control_attr_group); + + rm_all_node(); + kobject_del(clounix_switch); +} + +module_init(main_init); +module_exit(main_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("baohx@clounix.com"); +MODULE_DESCRIPTION("clounix sysfs operate interface"); diff --git a/platform/clounix/sonic-platform-modules-pegatron/common/modules/clounix_sysfs_var.h b/platform/clounix/sonic-platform-modules-pegatron/common/modules/clounix_sysfs_var.h new file mode 100644 index 000000000000..ab0425e84d96 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/common/modules/clounix_sysfs_var.h @@ -0,0 +1,15 @@ +#define MIN_PATH_LEN (16) +#define MAX_NAME_LEN (PAGE_SIZE/2) + +#define PATH_FORMAT "Format:\r\nFor create: target_node path link_node_path.\r\nFor del: del link_node_path\r\nThe path format must be full path like \"/sys/bus/...\"\r\n" + +#define SYSFS_ROOT "/sys" +#define CLOUNIX_DIR_NAME "switch" +#define DEL_CMD "del " +#define ADD_CMD "add " + +#define MAX_NODE_NAME_LEN (64-1) +#define MAX_NODE_INFO_LEN (256-1) + +#define DEL_OPS 1 +#define ADD_OPS 2 diff --git a/platform/clounix/sonic-platform-modules-pegatron/common/modules/i2c-mux-pca9641.c b/platform/clounix/sonic-platform-modules-pegatron/common/modules/i2c-mux-pca9641.c new file mode 100644 index 000000000000..1499a9a70256 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/common/modules/i2c-mux-pca9641.c @@ -0,0 +1,427 @@ +/* + * I2C multiplexer driver for PCA9641 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 +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,18,0) +#include +#endif +#include "pegatron_pub.h" + +/* + * The PCA9641 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 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) + +/* 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 + +static void pca9641_release_bus(struct i2c_client *client); +static int pca9641_arbitrate(struct i2c_client *client); +static uint loglevel = LOG_INFO | LOG_WARNING | LOG_ERR; + +enum pca_type { + pca_9541, + pca_9641, +}; + +struct pca9641 { + struct i2c_client *client; + unsigned long select_timeout; + unsigned long arb_timeout; +}; + +static const struct i2c_device_id pca9641_id[] = { + { "pca9541", pca_9541 }, + { "pca9641", pca_9641 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, pca9641_id); + +#ifdef CONFIG_OF +static const struct of_device_id pca9641_of_match[] = { + { .compatible = "nxp,pca9541" }, + { .compatible = "nxp,pca9641" }, + {} +}; +MODULE_DEVICE_TABLE(of, pca9641_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 pca9641_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 = adap->algo->master_xfer(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 pca9641_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 = adap->algo->master_xfer(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 + */ + +#define pca9641_other_lock(r) ((r) & PCA9641_STS_OTHER_LOCK) +#define pca9641_lock_grant(r) ((r) & PCA9641_CTL_LOCK_GRANT) + +static inline int pca9641_busoff(int ctl, int sts) +{ + return !(ctl & PCA9641_CTL_LOCK_GRANT) && + !(sts & PCA9641_STS_OTHER_LOCK); +} + + +/* Release bus. Also reset NTESTON and BUSINIT if it was set. */ +static void pca9641_release_bus(struct i2c_client *client) +{ + pca9641_reg_write(client, PCA9641_CONTROL, 0); +} + + +/* + * 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. + */ + +/* + * 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 pca9641 *data = i2c_mux_priv(muxc); + int reg_ctl, reg_sts; + + reg_ctl = pca9641_reg_read(client, PCA9641_CONTROL); + if (reg_ctl < 0) { + return reg_ctl; + } + reg_sts = pca9641_reg_read(client, PCA9641_STATUS); + pega_print(DEBUG, "PCA9641_CONTROL:0x%x,PCA9641_STATUS:0x%x\n", reg_ctl,reg_sts); + + if (pca9641_busoff(reg_ctl, reg_sts)) { + /* + * Bus is off. Request ownership or turn it on unless + * other master requested ownership. + */ + //printk(KERN_WARNING "(ar)lock req\n"); + reg_ctl |= PCA9641_CTL_LOCK_REQ | PCA9641_CTL_IDLE_TIMER_DIS | PCA9641_CTL_SMBUS_DIS; + pca9641_reg_write(client, PCA9641_CONTROL, reg_ctl); + reg_ctl = pca9641_reg_read(client, PCA9641_CONTROL); + if (pca9641_lock_grant(reg_ctl)) { + /* + * Other master did not request ownership, + * or arbitration timeout expired. Take the bus. + */ + //printk(KERN_WARNING "(ar)grant\n"); + reg_ctl |= PCA9641_CTL_BUS_CONNECT + | PCA9641_CTL_LOCK_REQ; + pca9641_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 (pca9641_lock_grant(reg_ctl)) { + /* + * Bus is on, and we own it. We are done with acquisition. + */ + //printk(KERN_WARNING "(ar)grant already\n"); + reg_ctl |= PCA9641_CTL_BUS_CONNECT | PCA9641_CTL_LOCK_REQ; + pca9641_reg_write(client, PCA9641_CONTROL, reg_ctl); + + return 1; + } else if (pca9641_other_lock(reg_sts)) { + /* + * Other master owns the bus. + * If arbitration timeout has expired, force ownership. + * Otherwise request it. + */ + //printk(KERN_WARNING "(ar) other grant already\n"); + data->select_timeout = SELECT_DELAY_LONG; + reg_ctl |= PCA9641_CTL_LOCK_REQ; + pca9641_reg_write(client, PCA9641_CONTROL, reg_ctl); + } + return 0; +} + +static int pca9641_select_chan(struct i2c_mux_core *muxc, u32 chan) +{ + struct pca9641 *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + int ret; + unsigned long timeout = jiffies + ARB2_TIMEOUT; + /* give up after this time */ + + pega_print(DEBUG, "(ar)select\n"); + data->arb_timeout = jiffies + ARB_TIMEOUT; + /* force bus ownership after this time */ + do { + ret = pca9641_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)); + pega_print(WARNING,"arbiter acquisition failure (timeout)\n"); + return -ETIMEDOUT; +} + +static int pca9641_release_chan(struct i2c_mux_core *muxc, u32 chan) +{ + struct pca9641 *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + pca9641_release_bus(client); + return 0; +} + +/* + * I2C init/probing/exit functions + */ +static int pca9641_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adap = client->adapter; +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0) + struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev); +#endif + struct i2c_mux_core *muxc; + struct pca9641 *data; + int force; + int ret = -ENODEV; + //int detect_id; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + /* + * I2C accesses are unprotected here. + * We have to lock the adapter before releasing the bus. + */ + i2c_lock_bus(adap, I2C_LOCK_SEGMENT); + pca9641_release_bus(client); + i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); + + /* Create mux adapter */ + + force = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,18,0) + if (pdata) + force = pdata->modes[0].adap_id; +#endif + 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) { + dev_err(&client->dev, "failed to register master selector\n"); + return ret; + } + + dev_info(&client->dev, "registered master selector for I2C %s\n", + client->name); + + return 0; +} + +static int pca9641_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(pca9641_of_match), + }, + .probe = pca9641_probe, + .remove = pca9641_remove, + .id_table = pca9641_id, +}; + +module_i2c_driver(pca9641_driver); + +module_param(loglevel,uint,0664); +MODULE_PARM_DESC(loglevel,"0x01-LOG_ERR,0x02-LOG_WARNING,0x04-LOG_INFO,0x08-LOG_DEBUG"); +MODULE_AUTHOR("Guenter Roeck "); +MODULE_DESCRIPTION("PCA9641 I2C master selector driver"); +MODULE_LICENSE("GPL v2"); diff --git a/platform/clounix/sonic-platform-modules-pegatron/common/modules/pegatron_hwmon_mcu.c b/platform/clounix/sonic-platform-modules-pegatron/common/modules/pegatron_hwmon_mcu.c new file mode 100644 index 000000000000..8bab9be95d0b --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/common/modules/pegatron_hwmon_mcu.c @@ -0,0 +1,1930 @@ +/* + * A MCU driver connect to hwmon + * + * Copyright (C) 2018 Pegatron Corporation. + * Peter5_Lin + * + * 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 "pegatron_pub.h" + +#undef pega_DEBUG +#ifdef pega_DEBUG +#define DBG(x) x +#else +#define DBG(x) +#endif /* DEBUG */ + +#define FW_UPGRADE_COMMAND 0xA5 +#define FAN_DISABLE_COMMAND 0x20 +#define FAN_ENABLE_COMMAND 0x21 +#define FAN_LED_SETTO_MANUAL_COMMAND 0x30 +#define FAN_LED_SETTO_AUTO_COMMAND 0x31 +#define FAN_LED_GREENOFF_COMMAND 0x40 +#define FAN_LED_GREENON_COMMAND 0x41 +#define FAN_LED_AMBEROFF_COMMAND 0x50 +#define FAN_LED_AMBERON_COMMAND 0x51 +#define SMART_FAN_ENABLE_BIT 0 +#define SMART_FAN_SETTING_ENABLE_BIT 0 +#define SA56004X_REMOTE_TEMP_ALERT_BIT 4 +#define I2C_FANBOARD_TIMEOUT_BIT 0 +#define ALERT_MODE_BIT 0 +#define GET_BIT(data, bit, value) value = (data >> bit) & 0x1 +#define SET_BIT(data, bit) data |= (1 << bit) +#define CLEAR_BIT(data, bit) data &= ~(1 << bit) + +enum fan_alert +{ + FAN_WRONG_AIRFLOW = 0, + FAN_OUTER_RPM_OVER_ALERT_BIT, + FAN_OUTER_RPM_UNDER_ALERT_BIT, + FAN_OUTER_RPM_ZERO_ALERT_BIT, + FAN_INNER_RPM_OVER_ALERT_BIT, + FAN_INNER_RPM_UNDER_ALERT_BIT, + FAN_INNER_RPM_ZERO_ALERT_BIT, + FAN_NOTCONNECT_ALERT_BIT, +}; + +enum fan_status +{ + SYSTEM_AIRFLOW_DIR, + FAN_STATUS_AIRFLOW, + FAN_ALERT_BIT, + FAN_LED_AMBER_BIT, + FAN_LED_GREEN_BIT, + FAN_LED_AUTO_BIT, + FAN_ENABLE_BIT, + FAN_PRESENT_BIT, +}; + +enum hwmon_mcu_register +{ + MB_FW_UG_REG = 0, + FB_FW_UG_REG, + MB_HW_VER_REG, + FB_HW_SKUVER_REG, + MB_FW_VER_REG, + FB_FW_VER_REG, + + FAN_PWM_REG = 16, + + SF_ENABLE_REG, + SF_SETTING_ENABLE_REG, + SF_DEVICE_REG, + SF_UPDATE_REG, + SF_TEMP_MAX_REG, + SF_TEMP_MID_REG, + SF_TEMP_MIN_REG, + SF_PWM_MAX_REG, + SF_PWM_MID_REG, + SF_PWM_MIN_REG, + + FAN1_INNER_RPM_REG = 32, + FAN2_INNER_RPM_REG, + FAN3_INNER_RPM_REG, + FAN4_INNER_RPM_REG, + FAN5_INNER_RPM_REG, + FAN6_INNER_RPM_REG, + + FAN1_OUTER_RPM_REG = 48, + FAN2_OUTER_RPM_REG, + FAN3_OUTER_RPM_REG, + FAN4_OUTER_RPM_REG, + FAN5_OUTER_RPM_REG, + FAN6_OUTER_RPM_REG, + + FAN1_STATUS_REG = 64, + FAN2_STATUS_REG, + FAN3_STATUS_REG, + FAN4_STATUS_REG, + FAN5_STATUS_REG, + FAN6_STATUS_REG, + + ADC_UNDER_VOL_ALERT_REG = 80, + ADC_OVER_VOL_ALERT_REG, + TS_OVER_TEMP_ALERT_REG, + + FAN1_ALERT_REG, + FAN2_ALERT_REG, + FAN3_ALERT_REG, + FAN4_ALERT_REG, + FAN5_ALERT_REG, + FAN6_ALERT_REG, + + I2C_BUS_ALERT_REG, + ALERT_MODE_REG, + + MONITOR_ADC_VOLTAGE_REG = 96, + + LM_0X48_TEMP_REG = 112, + LM_0X49_TEMP_REG, + LM_0X4A_TEMP_REG, + SA56004X_LOCAL_TEMP_REG, + SA56004X_REMOTE_TEMP_REG, + +}; + +#define HWMON_MCU_TEMP_NUM 5 +struct hwmon_mcu_data { + struct i2c_client *client; + int temp_max[HWMON_MCU_TEMP_NUM]; + int temp_crit[HWMON_MCU_TEMP_NUM]; + int temp_min[HWMON_MCU_TEMP_NUM]; +}; +int hwmon_temp_threshold_max[HWMON_MCU_TEMP_NUM] = {56, 65, 60, 65, 65}; +int hwmon_temp_threshold_crit[HWMON_MCU_TEMP_NUM] = {66, 75, 70, 75, 75}; +int hwmon_temp_threshold_min[HWMON_MCU_TEMP_NUM] = {4, 10, 1, 5, 4}; + + +static struct mutex pega_hwmon_mcu_lock; + +static uint loglevel = LOG_INFO | LOG_WARNING | LOG_ERR; +static char debug[MAX_DEBUG_INFO_LEN] = "debug info: \n\ +we should set smartFan_enable to 1 instead of setting the fan ratio. \n"; + +#define MAX_FAN_NUM 6 +#define MOTOR_NUM_PER_FAN 1 + +static LIST_HEAD(mcu_client_list); +static struct mutex list_lock; + +struct mcu_client_node { + struct i2c_client *client; + struct list_head list; +}; + +static int pega_hwmon_mcu_read(struct i2c_client *client, u8 reg) +{ + int data = -EPERM; + + mutex_lock(&pega_hwmon_mcu_lock); + + data = i2c_smbus_read_word_data(client, reg); + + mutex_unlock(&pega_hwmon_mcu_lock); + + return data; +} + +static int pega_hwmon_mcu_write(struct i2c_client *client, u8 reg, u8 val) +{ + int ret = -EIO; + + mutex_lock(&pega_hwmon_mcu_lock); + + ret = i2c_smbus_write_byte_data(client, reg, val); + + mutex_unlock(&pega_hwmon_mcu_lock); + + return ret; +} +int pega_mcu_read(unsigned short addr, u8 reg) +{ + struct list_head *list_node = NULL; + struct mcu_client_node *mcu_mode = NULL; + int data = -EPERM; + + mutex_lock(&list_lock); + + list_for_each(list_node, &mcu_client_list) + { + mcu_mode = list_entry(list_node, struct mcu_client_node, list); + + if (mcu_mode->client->addr == addr) { + //data = i2c_smbus_read_byte_data(mcu_mode->client, reg); + data = pega_hwmon_mcu_read(mcu_mode->client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", addr, reg, data); + break; + } + } + + mutex_unlock(&list_lock); + + return data; +} +EXPORT_SYMBOL(pega_mcu_read); + + +int pega_mcu_write(unsigned short addr, u8 reg, u8 val) +{ + struct list_head *list_node = NULL; + struct mcu_client_node *mcu_mode = NULL; + int ret = -EIO; + + mutex_lock(&list_lock); + + list_for_each(list_node, &mcu_client_list) + { + mcu_mode = list_entry(list_node, struct mcu_client_node, list); + + if (mcu_mode->client->addr == addr) { + //ret = i2c_smbus_write_byte_data(mcu_mode->client, reg, val); + ret = pega_hwmon_mcu_write(mcu_mode->client, reg, val); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", addr, reg, val); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(pega_mcu_write); + +static ssize_t mainBoardUpgrade(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 reg = MB_FW_UG_REG; + long val = 0; + + if (kstrtol(buf, 16, &val)) + { + return -EINVAL; + } + + if(val) + pega_hwmon_mcu_write(client, reg, FW_UPGRADE_COMMAND); + else + pega_hwmon_mcu_write(client, reg, 0xff); + + pega_print(DEBUG,"addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, FW_UPGRADE_COMMAND); + + return count; +} + +static ssize_t fanBoardUpgrade(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 reg = FB_FW_UG_REG; + long val = 0; + + if (kstrtol(buf, 16, &val)) + { + return -EINVAL; + } + + if(val) + pega_hwmon_mcu_write(client, reg, FW_UPGRADE_COMMAND); + else + pega_hwmon_mcu_write(client, reg, 0xff); + + pega_print(DEBUG,"addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, FW_UPGRADE_COMMAND); + + return count; +} + +static ssize_t get_MB_HW_version(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0; + u8 reg = MB_HW_VER_REG; + + data = pega_hwmon_mcu_read(client, reg); + + pega_print(DEBUG,"addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, FW_UPGRADE_COMMAND); + + data &= 0x1f; + + return sprintf(buf, "%02x\n", data); +} + +static ssize_t get_FB_HW_version(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0; + u8 reg = FB_HW_SKUVER_REG; + + data = pega_hwmon_mcu_read(client, reg); + + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + + data = (data >> 5) & 0x7; + + return sprintf(buf, "%02x\n", data); +} + +static ssize_t get_FB_boardId(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0; + u8 reg = FB_HW_SKUVER_REG; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + data &= 0x1f; + + return sprintf(buf, "%02x\n", data); +} + +static ssize_t get_MB_FW_version(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, major_ver = 0, minor_ver = 0; + u8 reg = MB_FW_VER_REG; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + major_ver = (data >> 4) & 0xf; + minor_ver = data & 0xf; + + return sprintf(buf, "%d.%d\n", major_ver, minor_ver); +} + +static ssize_t get_FB_FW_version(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, major_ver = 0, minor_ver = 0; + u8 reg = FB_FW_VER_REG; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + major_ver = (data >> 4) & 0xf; + minor_ver = data & 0xf; + + return sprintf(buf, "%d.%d\n", major_ver, minor_ver); +} + +static ssize_t get_pwr_down(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 reg = 0xb2; + u8 data = 0; + u8 pwr_down = 0; + + pega_hwmon_mcu_write(client, 0xB0, 0x0); + pega_hwmon_mcu_write(client, 0xB1, 0xa0); + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + pwr_down = ((data == 0xff)?0:1); + + return sprintf(buf, "0x%02x\n", pwr_down); +} + +static ssize_t get_fan_PWM(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0; + u8 reg = FAN_PWM_REG; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + + return sprintf(buf, "%d\n", data); +} + +static ssize_t set_fan_pwm(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 reg = FAN_PWM_REG; + long val = 0; + + if (kstrtol(buf, 10, &val)) + { + return -EINVAL; + } + + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %lx\r\n", client->addr, reg, val); + pega_hwmon_mcu_write(client, reg, val); + + return count; +} + +static ssize_t get_smartFan_enable(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = SF_ENABLE_REG; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, SMART_FAN_ENABLE_BIT, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t set_smartFan_enable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0; + u8 reg = SF_ENABLE_REG; + long val = 0; + + if (kstrtol(buf, 10, &val)) + { + return -EINVAL; + } + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + + if(val) + SET_BIT(data, SMART_FAN_ENABLE_BIT); + else + CLEAR_BIT(data, SMART_FAN_ENABLE_BIT); + pega_hwmon_mcu_write(client, reg, data); + + return count; +} + +static ssize_t get_smartFan_setting_enable(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = SF_SETTING_ENABLE_REG; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, SMART_FAN_SETTING_ENABLE_BIT, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t set_smartFan_setting_enable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0; + u8 reg = SF_SETTING_ENABLE_REG; + long val = 0; + + if (kstrtol(buf, 10, &val)) + { + return -EINVAL; + } + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + + if(val) + SET_BIT(data, SMART_FAN_SETTING_ENABLE_BIT); + else + CLEAR_BIT(data, SMART_FAN_SETTING_ENABLE_BIT); + pega_hwmon_mcu_write(client, reg, data); + + return count; +} + +static ssize_t get_smartFan_device(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0; + u8 reg = SF_DEVICE_REG; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + + return sprintf(buf, "%x\n", data); +} + +static ssize_t set_smartFan_device(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 reg = SF_DEVICE_REG; + long val = 0; + + if (kstrtol(buf, 16, &val)) + { + return -EINVAL; + } + + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %lx\r\n", client->addr, reg, val); + + pega_hwmon_mcu_write(client, reg, val); + + return count; +} + +static ssize_t get_smartFan_update(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0; + u8 reg = SF_UPDATE_REG; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + + return sprintf(buf, "%d\n", data); +} + +static ssize_t set_smartFan_update(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 reg = SF_UPDATE_REG; + long val = 0; + + if (kstrtol(buf, 10, &val)) + { + return -EINVAL; + } + + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %lx\r\n", client->addr, reg, val); + + pega_hwmon_mcu_write(client, reg, val); + + return count; +} + +static ssize_t get_smartFan_max_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0; + u8 reg = SF_TEMP_MAX_REG; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + + return sprintf(buf, "%d\n", data); +} + +static ssize_t set_smartFan_max_temp(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 reg = SF_TEMP_MAX_REG; + long val = 0; + + if (kstrtol(buf, 10, &val)) + { + return -EINVAL; + } + + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %lx\r\n", client->addr, reg, val); + + pega_hwmon_mcu_write(client, reg, val); + + return count; +} + +static ssize_t get_smartFan_mid_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0; + u8 reg = SF_TEMP_MID_REG; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + + return sprintf(buf, "%d\n", data); +} + +static ssize_t set_smartFan_mid_temp(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 reg = SF_TEMP_MID_REG; + long val = 0; + + if (kstrtol(buf, 10, &val)) + { + return -EINVAL; + } + + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %lx\r\n", client->addr, reg, val); + + pega_hwmon_mcu_write(client, reg, val); + + return count; +} + +static ssize_t get_smartFan_min_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0; + u8 reg = SF_TEMP_MIN_REG; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + + return sprintf(buf, "%d\n", data); +} + +static ssize_t set_smartFan_min_temp(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 reg = SF_TEMP_MIN_REG; + long val = 0; + + if (kstrtol(buf, 10, &val)) + { + return -EINVAL; + } + + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %lx\r\n", client->addr, reg, val); + + pega_hwmon_mcu_write(client, reg, val); + + return count; +} + +static ssize_t get_smartFan_max_pwm(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0; + u8 reg = SF_PWM_MAX_REG; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + + return sprintf(buf, "%d\n", data); +} + +static ssize_t set_smartFan_max_pwm(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 reg = SF_PWM_MAX_REG; + long val = 0; + + if (kstrtol(buf, 10, &val)) + { + return -EINVAL; + } + + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %lx\r\n", client->addr, reg, val); + + pega_hwmon_mcu_write(client, reg, val); + + return count; +} + +static ssize_t get_smartFan_mid_pwm(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0; + u8 reg = SF_PWM_MID_REG; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + + return sprintf(buf, "%d\n", data); +} + +static ssize_t set_smartFan_mid_pwm(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 reg = SF_PWM_MID_REG; + long val = 0; + + if (kstrtol(buf, 10, &val)) + { + return -EINVAL; + } + + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %lx\r\n", client->addr, reg, val); + + pega_hwmon_mcu_write(client, reg, val); + + return count; +} + +static ssize_t get_smartFan_min_pwm(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0; + u8 reg = SF_PWM_MIN_REG; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + + return sprintf(buf, "%d\n", data); +} + +static ssize_t set_smartFan_min_pwm(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 reg = SF_PWM_MIN_REG; + long val = 0; + + if (kstrtol(buf, 10, &val)) + { + return -EINVAL; + } + + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %lx\r\n", client->addr, reg, val); + + pega_hwmon_mcu_write(client, reg, val); + + return count; +} + +static ssize_t get_fan_inner_rpm(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u16 data = 0; + u8 reg = FAN1_INNER_RPM_REG + attr->index; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + + return sprintf(buf, "%d\n", data); +} + +static ssize_t get_fan_outer_rpm(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u16 data = 0; + u8 reg = FAN1_OUTER_RPM_REG + attr->index; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + + return sprintf(buf, "%d\n", data); +} + +static ssize_t get_fan_airflow_dir(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = FAN1_STATUS_REG + attr->index; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, SYSTEM_AIRFLOW_DIR, val); + + return sprintf(buf, "%d\n", val); +} + + +#define FAN_ABSENT 1 +#define FAN_POWER_OK 0 + +#define KWAI_FAN_ABSENT 0 +#define KWAI_FAN_PRESENT_NORMAL 1 +#define KWAI_FAN_PRESENT_ABNORMAL 2 + +static ssize_t get_fan_present(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0,present = 0,alarm = 0; + u8 reg = FAN1_STATUS_REG + attr->index; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, FAN_PRESENT_BIT, present); + GET_BIT(data, FAN_ALERT_BIT, alarm); + + + if(present == FAN_ABSENT) + { + val = KWAI_FAN_ABSENT; + } + else + { + if(alarm == FAN_POWER_OK) + { + val = KWAI_FAN_PRESENT_NORMAL; + } + else + { + val = KWAI_FAN_PRESENT_ABNORMAL; + } + } + + return sprintf(buf, "0x%02x\n", val); +} + +static ssize_t get_fan_enable(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = FAN1_STATUS_REG + attr->index; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, FAN_ENABLE_BIT, val); + + return sprintf(buf, "%d\n", val); +} + + +static ssize_t set_fan_enable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 reg = FAN1_STATUS_REG + attr->index; + long val = 0; + + if (kstrtol(buf, 10, &val)) + { + return -EINVAL; + } + + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %lx\r\n", client->addr, reg, val); + + if(val) + pega_hwmon_mcu_write(client, reg, FAN_ENABLE_COMMAND); + else + pega_hwmon_mcu_write(client, reg, FAN_DISABLE_COMMAND); + + return count; +} + +static ssize_t get_fan_led_auto(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = FAN1_STATUS_REG + attr->index; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, FAN_LED_AUTO_BIT, val); + + return sprintf(buf, "%d\n", val); +} + + +static ssize_t set_fan_led_auto(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 reg = FAN1_STATUS_REG + attr->index; + long val = 0; + + if (kstrtol(buf, 10, &val)) + { + return -EINVAL; + } + + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %lx\r\n", client->addr, reg, val); + + if(val) + pega_hwmon_mcu_write(client, reg, FAN_LED_SETTO_AUTO_COMMAND); + else + pega_hwmon_mcu_write(client, reg, FAN_LED_SETTO_MANUAL_COMMAND); + + return count; +} + +static ssize_t get_fan_led_green(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = FAN1_STATUS_REG + attr->index; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, FAN_LED_GREEN_BIT, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t get_fan_led_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = FAN1_STATUS_REG + attr->index; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + //Kwai's requirement + if(data & (1 << FAN_LED_GREEN_BIT)) + { + val = 1; + } + else if(data & (1 << FAN_LED_AMBER_BIT)) + { + val = 3; + } + + return sprintf(buf, "%d\n", val); +} + +static ssize_t set_fan_led_green(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 reg = FAN1_STATUS_REG + attr->index; + long val = 0; + + if (kstrtol(buf, 10, &val)) + { + return -EINVAL; + } + + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %lx\r\n", client->addr, reg, val); + + if(val) + pega_hwmon_mcu_write(client, reg, FAN_LED_GREENON_COMMAND); + else + pega_hwmon_mcu_write(client, reg, FAN_LED_GREENOFF_COMMAND); + + return count; +} + +static ssize_t get_fan_led_amber(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = FAN1_STATUS_REG + attr->index; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, FAN_LED_AMBER_BIT, val); + + return sprintf(buf, "%d\n", val); +} + + +static ssize_t set_fan_led_amber(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 reg = FAN1_STATUS_REG + attr->index; + long val = 0; + + if (kstrtol(buf, 10, &val)) + { + return -EINVAL; + } + + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %lx\r\n", client->addr, reg, val); + + if(val) + pega_hwmon_mcu_write(client, reg, FAN_LED_AMBERON_COMMAND); + else + pega_hwmon_mcu_write(client, reg, FAN_LED_AMBEROFF_COMMAND); + + return count; +} + +static ssize_t get_fan_status_alert(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = FAN1_STATUS_REG + attr->index; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, FAN_ALERT_BIT, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t get_adc_under_vol_alert(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = ADC_UNDER_VOL_ALERT_REG; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, attr->index, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t get_adc_over_vol_alert(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = ADC_OVER_VOL_ALERT_REG; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, attr->index, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t get_temp_alert(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = TS_OVER_TEMP_ALERT_REG; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, attr->index, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t get_fan_wrong_airflow_alert(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = FAN1_ALERT_REG + attr->index; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, FAN_WRONG_AIRFLOW, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t get_fan_outerRPMOver_alert(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = FAN1_ALERT_REG + attr->index; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, FAN_OUTER_RPM_OVER_ALERT_BIT, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t get_fan_outerRPMUnder_alert(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = FAN1_ALERT_REG + attr->index; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, FAN_OUTER_RPM_UNDER_ALERT_BIT, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t get_fan_outerRPMZero_alert(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = FAN1_ALERT_REG + attr->index; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, FAN_OUTER_RPM_ZERO_ALERT_BIT, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t get_fan_innerRPMOver_alert(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = FAN1_ALERT_REG + attr->index; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, FAN_INNER_RPM_OVER_ALERT_BIT, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t get_fan_innerRPMUnder_alert(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = FAN1_ALERT_REG + attr->index; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, FAN_INNER_RPM_UNDER_ALERT_BIT, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t get_fan_innerRPMZero_alert(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = FAN1_ALERT_REG + attr->index; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, FAN_INNER_RPM_ZERO_ALERT_BIT, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t get_fan_notconnect_alert(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = FAN1_ALERT_REG + attr->index; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, FAN_NOTCONNECT_ALERT_BIT, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t get_i2c_timeout(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = I2C_BUS_ALERT_REG; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, (I2C_FANBOARD_TIMEOUT_BIT + attr->index), val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t get_alert_mode(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, val = 0; + u8 reg = ALERT_MODE_REG; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, ALERT_MODE_BIT, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t set_alert_mode(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0; + u8 reg = ALERT_MODE_REG; + long val = 0; + + if (kstrtol(buf, 10, &val)) + { + return -EINVAL; + } + + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %lx\r\n", client->addr, reg, val); + + if(val) + SET_BIT(data, ALERT_MODE_BIT); + else + CLEAR_BIT(data, ALERT_MODE_BIT); + pega_hwmon_mcu_write(client, reg, data); + + return count; +} + +static ssize_t get_adc_vol(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u16 data = 0, reg = MONITOR_ADC_VOLTAGE_REG + attr->index; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + + return sprintf(buf, "%d.%02d\n", data/1000, data%1000); +} + +static ssize_t get_hwmon_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0; + u8 reg = LM_0X48_TEMP_REG + attr->index; + + data = pega_hwmon_mcu_read(client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + + return sprintf(buf, "%d\n", data*1000); +} +static ssize_t get_label(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + return sprintf(buf, "temp%d\n", attr->index); +} + +static ssize_t get_hwmon_temp_min(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", drv_data->temp_min[attr->index]); +} + +static ssize_t get_hwmon_temp_max(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", drv_data->temp_max[attr->index]); +} +static ssize_t set_hwmon_temp_max(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + long val = 0; + + if (kstrtol(buf, 10, &val)) + { + return -EINVAL; + } + + drv_data->temp_max[attr->index] = val; + return count; +} +static ssize_t get_hwmon_temp_crit(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", drv_data->temp_crit[attr->index]); +} +static ssize_t set_hwmon_temp_crit(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct hwmon_mcu_data *drv_data = dev_get_drvdata(dev); + long val = 0; + + if (kstrtol(buf, 10, &val)) + { + return -EINVAL; + } + + drv_data->temp_crit[attr->index] = val; + return count; +} + +static ssize_t get_total_fan_num(struct device *dev, + struct device_attribute *da, char *buf) +{ + return sprintf(buf, "%d\n", MAX_FAN_NUM); +} +static ssize_t get_motor_num(struct device *dev, + struct device_attribute *da, char *buf) +{ + return sprintf(buf, "%d\n", MOTOR_NUM_PER_FAN); +} +static ssize_t get_fan_model_name(struct device *dev, + struct device_attribute *da, char *buf) +{ + return sprintf(buf, "XG40561BX-1Q313-S9H"); +} +static ssize_t get_fan_serial_num(struct device *dev, + struct device_attribute *da, char *buf) +{ + return sprintf(buf, "N/A"); +} +static ssize_t get_fan_vendor(struct device *dev, + struct device_attribute *da, char *buf) +{ + return sprintf(buf, "SUNON"); +} +static ssize_t get_fan_part_num(struct device *dev, + struct device_attribute *da, char *buf) +{ + return sprintf(buf, "N/A"); +} +static ssize_t get_fan_hard_version(struct device *dev, + struct device_attribute *da, char *buf) +{ + return sprintf(buf, "1.0"); +} +static ssize_t get_fan_motor_speed_target(struct device *dev, + struct device_attribute *da, char *buf) +{ + return sprintf(buf, "27500"); +} +static ssize_t get_fan_motor_speed_tolerance(struct device *dev, + struct device_attribute *da, char *buf) +{ + //10 percentage + return sprintf(buf, "2750"); +} +#define SET_FAN_ATTR(_num) \ + static SENSOR_DEVICE_ATTR(fan##_num##_inner_rpm, S_IRUGO, get_fan_inner_rpm, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(fan##_num##_outer_rpm, S_IRUGO, get_fan_outer_rpm, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(fan##_num##_present, S_IRUGO, get_fan_present, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(fan##_num##_enable, S_IRUGO | S_IWUSR, get_fan_enable, set_fan_enable, _num-1); \ + static SENSOR_DEVICE_ATTR(fan##_num##_led_auto, S_IRUGO | S_IWUSR, get_fan_led_auto, set_fan_led_auto, _num-1); \ + static SENSOR_DEVICE_ATTR(fan##_num##_led_status, S_IRUGO, get_fan_led_status, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(fan##_num##_led_green, S_IRUGO | S_IWUSR, get_fan_led_green, set_fan_led_green, _num-1); \ + static SENSOR_DEVICE_ATTR(fan##_num##_led_amber, S_IRUGO | S_IWUSR, get_fan_led_amber, set_fan_led_amber, _num-1); \ + static SENSOR_DEVICE_ATTR(fan##_num##_status_alert, S_IRUGO, get_fan_status_alert, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(fan##_num##_wrongAirflow_alert, S_IRUGO, get_fan_wrong_airflow_alert, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(fan##_num##_outerRPMOver_alert, S_IRUGO, get_fan_outerRPMOver_alert, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(fan##_num##_outerRPMUnder_alert, S_IRUGO, get_fan_outerRPMUnder_alert, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(fan##_num##_outerRPMZero_alert, S_IRUGO, get_fan_outerRPMZero_alert, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(fan##_num##_innerRPMOver_alert, S_IRUGO, get_fan_innerRPMOver_alert, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(fan##_num##_innerRPMUnder_alert, S_IRUGO, get_fan_innerRPMUnder_alert, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(fan##_num##_innerRPMZero_alert, S_IRUGO, get_fan_innerRPMZero_alert, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(fan##_num##_notconnect_alert, S_IRUGO, get_fan_notconnect_alert, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(fan##_num##_airflow_dir, S_IRUGO, get_fan_airflow_dir, NULL, _num-1);\ + static SENSOR_DEVICE_ATTR(fan##_num##_motor_num, S_IRUGO, get_motor_num, NULL, _num-1);\ + static SENSOR_DEVICE_ATTR(fan##_num##_model_name, S_IRUGO, get_fan_model_name, NULL, _num-1);\ + static SENSOR_DEVICE_ATTR(fan##_num##_serial_num, S_IRUGO, get_fan_serial_num, NULL, _num-1);\ + static SENSOR_DEVICE_ATTR(fan##_num##_vendor, S_IRUGO, get_fan_vendor, NULL, _num-1);\ + static SENSOR_DEVICE_ATTR(fan##_num##_part_num, S_IRUGO, get_fan_part_num, NULL, _num-1);\ + static SENSOR_DEVICE_ATTR(fan##_num##_hard_version, S_IRUGO, get_fan_hard_version, NULL, _num-1);\ + static SENSOR_DEVICE_ATTR(fan##_num##_speed_target, S_IRUGO, get_fan_motor_speed_target, NULL, _num-1);\ + static SENSOR_DEVICE_ATTR(fan##_num##_speed_tolerance, S_IRUGO, get_fan_motor_speed_tolerance, NULL, _num-1); + + + +SET_FAN_ATTR(1);SET_FAN_ATTR(2);SET_FAN_ATTR(3);SET_FAN_ATTR(4);SET_FAN_ATTR(5);SET_FAN_ATTR(6); + +#define SET_ADC_ATTR(_num) \ + static SENSOR_DEVICE_ATTR(ADC##_num##_under_alert, S_IRUGO, get_adc_under_vol_alert, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(ADC##_num##_over_alert, S_IRUGO, get_adc_over_vol_alert, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(ADC##_num##_vol, S_IRUGO, get_adc_vol, NULL, _num-1) + +SET_ADC_ATTR(1);SET_ADC_ATTR(2);SET_ADC_ATTR(3);SET_ADC_ATTR(4);SET_ADC_ATTR(5);SET_ADC_ATTR(6);SET_ADC_ATTR(7);SET_ADC_ATTR(8); + +static SENSOR_DEVICE_ATTR(mb_fw_upgrade, S_IWUSR, NULL, mainBoardUpgrade, 0); +static SENSOR_DEVICE_ATTR(fb_fw_upgrade, S_IWUSR, NULL, fanBoardUpgrade, 0); +static SENSOR_DEVICE_ATTR(mb_hw_version, S_IRUGO, get_MB_HW_version, NULL, 0); +static SENSOR_DEVICE_ATTR(fb_hw_version, S_IRUGO, get_FB_HW_version, NULL, 0); +static SENSOR_DEVICE_ATTR(fb_board_id, S_IRUGO, get_FB_boardId, NULL, 0); +static SENSOR_DEVICE_ATTR(mb_fw_version, S_IRUGO, get_MB_FW_version, NULL, 0); +static SENSOR_DEVICE_ATTR(fb_fw_version, S_IRUGO, get_FB_FW_version, NULL, 0); +static SENSOR_DEVICE_ATTR(fan_pwm, S_IRUGO | S_IWUSR, get_fan_PWM, set_fan_pwm, 0); +static SENSOR_DEVICE_ATTR(pwr_down, S_IRUGO, get_pwr_down, NULL, 0); + +static SENSOR_DEVICE_ATTR(smartFan_enable, S_IRUGO | S_IWUSR, get_smartFan_enable, set_smartFan_enable, 0); +static SENSOR_DEVICE_ATTR(smartFan_setting_enable, S_IRUGO | S_IWUSR, get_smartFan_setting_enable, set_smartFan_setting_enable, 0); +static SENSOR_DEVICE_ATTR(smartFan_device, S_IRUGO | S_IWUSR, get_smartFan_device, set_smartFan_device, 0); +static SENSOR_DEVICE_ATTR(smartFan_update, S_IRUGO | S_IWUSR, get_smartFan_update, set_smartFan_update, 0); +static SENSOR_DEVICE_ATTR(smartFan_max_temp, S_IRUGO | S_IWUSR, get_smartFan_max_temp, set_smartFan_max_temp, 0); +static SENSOR_DEVICE_ATTR(smartFan_mid_temp, S_IRUGO | S_IWUSR, get_smartFan_mid_temp, set_smartFan_mid_temp, 0); +static SENSOR_DEVICE_ATTR(smartFan_min_temp, S_IRUGO | S_IWUSR, get_smartFan_min_temp, set_smartFan_min_temp, 0); +static SENSOR_DEVICE_ATTR(smartFan_max_pwm, S_IRUGO | S_IWUSR, get_smartFan_max_pwm, set_smartFan_max_pwm, 0); +static SENSOR_DEVICE_ATTR(smartFan_mid_pwm, S_IRUGO | S_IWUSR, get_smartFan_mid_pwm, set_smartFan_mid_pwm, 0); +static SENSOR_DEVICE_ATTR(smartFan_min_pwm, S_IRUGO | S_IWUSR, get_smartFan_min_pwm, set_smartFan_min_pwm, 0); + +static SENSOR_DEVICE_ATTR(lm75_48_temp_alert, S_IRUGO, get_temp_alert, NULL, 5); +static SENSOR_DEVICE_ATTR(lm75_49_temp_alert, S_IRUGO, get_temp_alert, NULL, 4); +static SENSOR_DEVICE_ATTR(lm75_4a_temp_alert, S_IRUGO, get_temp_alert, NULL, 3); +static SENSOR_DEVICE_ATTR(sa56004x_Ltemp_alert, S_IRUGO, get_temp_alert, NULL, 2); +static SENSOR_DEVICE_ATTR(sa56004x_Rtemp_alert, S_IRUGO, get_temp_alert, NULL, 1); +static SENSOR_DEVICE_ATTR(fanBoard_alert, S_IRUGO, get_temp_alert, NULL, 0); + +static SENSOR_DEVICE_ATTR(i2c_fb_timeout, S_IRUGO, get_i2c_timeout, NULL, 0); +static SENSOR_DEVICE_ATTR(i2c_remote_timeout, S_IRUGO, get_i2c_timeout, NULL, 1); +static SENSOR_DEVICE_ATTR(i2c_local_timeout, S_IRUGO, get_i2c_timeout, NULL, 2); +static SENSOR_DEVICE_ATTR(i2c_lm75_48_timeout, S_IRUGO, get_i2c_timeout, NULL, 3); +static SENSOR_DEVICE_ATTR(i2c_lm75_49_timeout, S_IRUGO, get_i2c_timeout, NULL, 4); +static SENSOR_DEVICE_ATTR(alert_mode, S_IRUGO | S_IWUSR, get_alert_mode, set_alert_mode, 0); + +static SENSOR_DEVICE_ATTR(fan_num, S_IRUGO, get_total_fan_num, NULL, 0); + +#define SET_TEMP_ATTR(_num) \ + static SENSOR_DEVICE_ATTR(temp##_num##_input, S_IRUGO, get_hwmon_temp, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(temp##_num##_label, S_IRUGO, get_label, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(temp##_num##_min, S_IRUGO, get_hwmon_temp_min, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(temp##_num##_max, S_IRUGO, get_hwmon_temp_max, set_hwmon_temp_max, _num-1); \ + static SENSOR_DEVICE_ATTR(temp##_num##_crit, S_IRUGO, get_hwmon_temp_crit, set_hwmon_temp_crit, _num-1); + +SET_TEMP_ATTR(1);SET_TEMP_ATTR(2);SET_TEMP_ATTR(3);SET_TEMP_ATTR(4);SET_TEMP_ATTR(5); + +static struct attribute *pega_hwmon_mcu_attrs[] = { + &sensor_dev_attr_mb_fw_upgrade.dev_attr.attr, + &sensor_dev_attr_fb_fw_upgrade.dev_attr.attr, + &sensor_dev_attr_mb_hw_version.dev_attr.attr, + &sensor_dev_attr_fb_hw_version.dev_attr.attr, + &sensor_dev_attr_fb_board_id.dev_attr.attr, + &sensor_dev_attr_mb_fw_version.dev_attr.attr, + &sensor_dev_attr_fb_fw_version.dev_attr.attr, + &sensor_dev_attr_fan_pwm.dev_attr.attr, + &sensor_dev_attr_pwr_down.dev_attr.attr, + + &sensor_dev_attr_smartFan_enable.dev_attr.attr, + &sensor_dev_attr_smartFan_setting_enable.dev_attr.attr, + &sensor_dev_attr_smartFan_device.dev_attr.attr, + &sensor_dev_attr_smartFan_update.dev_attr.attr, + &sensor_dev_attr_smartFan_max_temp.dev_attr.attr, + &sensor_dev_attr_smartFan_mid_temp.dev_attr.attr, + &sensor_dev_attr_smartFan_min_temp.dev_attr.attr, + &sensor_dev_attr_smartFan_max_pwm.dev_attr.attr, + &sensor_dev_attr_smartFan_mid_pwm.dev_attr.attr, + &sensor_dev_attr_smartFan_min_pwm.dev_attr.attr, + + &sensor_dev_attr_fan1_inner_rpm.dev_attr.attr, + &sensor_dev_attr_fan2_inner_rpm.dev_attr.attr, + &sensor_dev_attr_fan3_inner_rpm.dev_attr.attr, + &sensor_dev_attr_fan4_inner_rpm.dev_attr.attr, + &sensor_dev_attr_fan5_inner_rpm.dev_attr.attr, + &sensor_dev_attr_fan6_inner_rpm.dev_attr.attr, + + &sensor_dev_attr_fan1_outer_rpm.dev_attr.attr, + &sensor_dev_attr_fan2_outer_rpm.dev_attr.attr, + &sensor_dev_attr_fan3_outer_rpm.dev_attr.attr, + &sensor_dev_attr_fan4_outer_rpm.dev_attr.attr, + &sensor_dev_attr_fan5_outer_rpm.dev_attr.attr, + &sensor_dev_attr_fan6_outer_rpm.dev_attr.attr, + + &sensor_dev_attr_fan1_present.dev_attr.attr, + &sensor_dev_attr_fan2_present.dev_attr.attr, + &sensor_dev_attr_fan3_present.dev_attr.attr, + &sensor_dev_attr_fan4_present.dev_attr.attr, + &sensor_dev_attr_fan5_present.dev_attr.attr, + &sensor_dev_attr_fan6_present.dev_attr.attr, + + &sensor_dev_attr_fan1_enable.dev_attr.attr, + &sensor_dev_attr_fan2_enable.dev_attr.attr, + &sensor_dev_attr_fan3_enable.dev_attr.attr, + &sensor_dev_attr_fan4_enable.dev_attr.attr, + &sensor_dev_attr_fan5_enable.dev_attr.attr, + &sensor_dev_attr_fan6_enable.dev_attr.attr, + + &sensor_dev_attr_fan1_led_auto.dev_attr.attr, + &sensor_dev_attr_fan2_led_auto.dev_attr.attr, + &sensor_dev_attr_fan3_led_auto.dev_attr.attr, + &sensor_dev_attr_fan4_led_auto.dev_attr.attr, + &sensor_dev_attr_fan5_led_auto.dev_attr.attr, + &sensor_dev_attr_fan6_led_auto.dev_attr.attr, + + &sensor_dev_attr_fan1_led_status.dev_attr.attr, + &sensor_dev_attr_fan2_led_status.dev_attr.attr, + &sensor_dev_attr_fan3_led_status.dev_attr.attr, + &sensor_dev_attr_fan4_led_status.dev_attr.attr, + &sensor_dev_attr_fan5_led_status.dev_attr.attr, + &sensor_dev_attr_fan6_led_status.dev_attr.attr, + + &sensor_dev_attr_fan1_led_green.dev_attr.attr, + &sensor_dev_attr_fan2_led_green.dev_attr.attr, + &sensor_dev_attr_fan3_led_green.dev_attr.attr, + &sensor_dev_attr_fan4_led_green.dev_attr.attr, + &sensor_dev_attr_fan5_led_green.dev_attr.attr, + &sensor_dev_attr_fan6_led_green.dev_attr.attr, + + &sensor_dev_attr_fan1_led_amber.dev_attr.attr, + &sensor_dev_attr_fan2_led_amber.dev_attr.attr, + &sensor_dev_attr_fan3_led_amber.dev_attr.attr, + &sensor_dev_attr_fan4_led_amber.dev_attr.attr, + &sensor_dev_attr_fan5_led_amber.dev_attr.attr, + &sensor_dev_attr_fan6_led_amber.dev_attr.attr, + + &sensor_dev_attr_fan1_status_alert.dev_attr.attr, + &sensor_dev_attr_fan2_status_alert.dev_attr.attr, + &sensor_dev_attr_fan3_status_alert.dev_attr.attr, + &sensor_dev_attr_fan4_status_alert.dev_attr.attr, + &sensor_dev_attr_fan5_status_alert.dev_attr.attr, + &sensor_dev_attr_fan6_status_alert.dev_attr.attr, + + &sensor_dev_attr_fan1_airflow_dir.dev_attr.attr, + &sensor_dev_attr_fan2_airflow_dir.dev_attr.attr, + &sensor_dev_attr_fan3_airflow_dir.dev_attr.attr, + &sensor_dev_attr_fan4_airflow_dir.dev_attr.attr, + &sensor_dev_attr_fan5_airflow_dir.dev_attr.attr, + &sensor_dev_attr_fan6_airflow_dir.dev_attr.attr, + + &sensor_dev_attr_fan1_motor_num.dev_attr.attr, + &sensor_dev_attr_fan2_motor_num.dev_attr.attr, + &sensor_dev_attr_fan3_motor_num.dev_attr.attr, + &sensor_dev_attr_fan4_motor_num.dev_attr.attr, + &sensor_dev_attr_fan5_motor_num.dev_attr.attr, + &sensor_dev_attr_fan6_motor_num.dev_attr.attr, + + &sensor_dev_attr_fan1_model_name.dev_attr.attr, + &sensor_dev_attr_fan2_model_name.dev_attr.attr, + &sensor_dev_attr_fan3_model_name.dev_attr.attr, + &sensor_dev_attr_fan4_model_name.dev_attr.attr, + &sensor_dev_attr_fan5_model_name.dev_attr.attr, + &sensor_dev_attr_fan6_model_name.dev_attr.attr, + + &sensor_dev_attr_fan1_serial_num.dev_attr.attr, + &sensor_dev_attr_fan2_serial_num.dev_attr.attr, + &sensor_dev_attr_fan3_serial_num.dev_attr.attr, + &sensor_dev_attr_fan4_serial_num.dev_attr.attr, + &sensor_dev_attr_fan5_serial_num.dev_attr.attr, + &sensor_dev_attr_fan6_serial_num.dev_attr.attr, + + &sensor_dev_attr_fan1_vendor.dev_attr.attr, + &sensor_dev_attr_fan2_vendor.dev_attr.attr, + &sensor_dev_attr_fan3_vendor.dev_attr.attr, + &sensor_dev_attr_fan4_vendor.dev_attr.attr, + &sensor_dev_attr_fan5_vendor.dev_attr.attr, + &sensor_dev_attr_fan6_vendor.dev_attr.attr, + + &sensor_dev_attr_fan1_part_num.dev_attr.attr, + &sensor_dev_attr_fan2_part_num.dev_attr.attr, + &sensor_dev_attr_fan3_part_num.dev_attr.attr, + &sensor_dev_attr_fan4_part_num.dev_attr.attr, + &sensor_dev_attr_fan5_part_num.dev_attr.attr, + &sensor_dev_attr_fan6_part_num.dev_attr.attr, + + &sensor_dev_attr_fan1_hard_version.dev_attr.attr, + &sensor_dev_attr_fan2_hard_version.dev_attr.attr, + &sensor_dev_attr_fan3_hard_version.dev_attr.attr, + &sensor_dev_attr_fan4_hard_version.dev_attr.attr, + &sensor_dev_attr_fan5_hard_version.dev_attr.attr, + &sensor_dev_attr_fan6_hard_version.dev_attr.attr, + + &sensor_dev_attr_fan1_speed_target.dev_attr.attr, + &sensor_dev_attr_fan2_speed_target.dev_attr.attr, + &sensor_dev_attr_fan3_speed_target.dev_attr.attr, + &sensor_dev_attr_fan4_speed_target.dev_attr.attr, + &sensor_dev_attr_fan5_speed_target.dev_attr.attr, + &sensor_dev_attr_fan6_speed_target.dev_attr.attr, + + &sensor_dev_attr_fan1_speed_tolerance.dev_attr.attr, + &sensor_dev_attr_fan2_speed_tolerance.dev_attr.attr, + &sensor_dev_attr_fan3_speed_tolerance.dev_attr.attr, + &sensor_dev_attr_fan4_speed_tolerance.dev_attr.attr, + &sensor_dev_attr_fan5_speed_tolerance.dev_attr.attr, + &sensor_dev_attr_fan6_speed_tolerance.dev_attr.attr, + + &sensor_dev_attr_fan_num.dev_attr.attr, + &sensor_dev_attr_ADC1_under_alert.dev_attr.attr, + &sensor_dev_attr_ADC2_under_alert.dev_attr.attr, + &sensor_dev_attr_ADC3_under_alert.dev_attr.attr, + &sensor_dev_attr_ADC4_under_alert.dev_attr.attr, + &sensor_dev_attr_ADC5_under_alert.dev_attr.attr, + &sensor_dev_attr_ADC6_under_alert.dev_attr.attr, + &sensor_dev_attr_ADC7_under_alert.dev_attr.attr, + &sensor_dev_attr_ADC8_under_alert.dev_attr.attr, + + &sensor_dev_attr_ADC1_over_alert.dev_attr.attr, + &sensor_dev_attr_ADC2_over_alert.dev_attr.attr, + &sensor_dev_attr_ADC3_over_alert.dev_attr.attr, + &sensor_dev_attr_ADC4_over_alert.dev_attr.attr, + &sensor_dev_attr_ADC5_over_alert.dev_attr.attr, + &sensor_dev_attr_ADC6_over_alert.dev_attr.attr, + &sensor_dev_attr_ADC7_over_alert.dev_attr.attr, + &sensor_dev_attr_ADC8_over_alert.dev_attr.attr, + + &sensor_dev_attr_lm75_48_temp_alert.dev_attr.attr, + &sensor_dev_attr_lm75_49_temp_alert.dev_attr.attr, + &sensor_dev_attr_lm75_4a_temp_alert.dev_attr.attr, + &sensor_dev_attr_sa56004x_Ltemp_alert.dev_attr.attr, + &sensor_dev_attr_sa56004x_Rtemp_alert.dev_attr.attr, + &sensor_dev_attr_fanBoard_alert.dev_attr.attr, + + &sensor_dev_attr_fan1_wrongAirflow_alert.dev_attr.attr, + &sensor_dev_attr_fan2_wrongAirflow_alert.dev_attr.attr, + &sensor_dev_attr_fan3_wrongAirflow_alert.dev_attr.attr, + &sensor_dev_attr_fan4_wrongAirflow_alert.dev_attr.attr, + &sensor_dev_attr_fan5_wrongAirflow_alert.dev_attr.attr, + &sensor_dev_attr_fan6_wrongAirflow_alert.dev_attr.attr, + + &sensor_dev_attr_fan1_outerRPMOver_alert.dev_attr.attr, + &sensor_dev_attr_fan2_outerRPMOver_alert.dev_attr.attr, + &sensor_dev_attr_fan3_outerRPMOver_alert.dev_attr.attr, + &sensor_dev_attr_fan4_outerRPMOver_alert.dev_attr.attr, + &sensor_dev_attr_fan5_outerRPMOver_alert.dev_attr.attr, + &sensor_dev_attr_fan6_outerRPMOver_alert.dev_attr.attr, + + &sensor_dev_attr_fan1_outerRPMUnder_alert.dev_attr.attr, + &sensor_dev_attr_fan2_outerRPMUnder_alert.dev_attr.attr, + &sensor_dev_attr_fan3_outerRPMUnder_alert.dev_attr.attr, + &sensor_dev_attr_fan4_outerRPMUnder_alert.dev_attr.attr, + &sensor_dev_attr_fan5_outerRPMUnder_alert.dev_attr.attr, + &sensor_dev_attr_fan6_outerRPMUnder_alert.dev_attr.attr, + + &sensor_dev_attr_fan1_outerRPMZero_alert.dev_attr.attr, + &sensor_dev_attr_fan2_outerRPMZero_alert.dev_attr.attr, + &sensor_dev_attr_fan3_outerRPMZero_alert.dev_attr.attr, + &sensor_dev_attr_fan4_outerRPMZero_alert.dev_attr.attr, + &sensor_dev_attr_fan5_outerRPMZero_alert.dev_attr.attr, + &sensor_dev_attr_fan6_outerRPMZero_alert.dev_attr.attr, + + &sensor_dev_attr_fan1_innerRPMOver_alert.dev_attr.attr, + &sensor_dev_attr_fan2_innerRPMOver_alert.dev_attr.attr, + &sensor_dev_attr_fan3_innerRPMOver_alert.dev_attr.attr, + &sensor_dev_attr_fan4_innerRPMOver_alert.dev_attr.attr, + &sensor_dev_attr_fan5_innerRPMOver_alert.dev_attr.attr, + &sensor_dev_attr_fan6_innerRPMOver_alert.dev_attr.attr, + + &sensor_dev_attr_fan1_innerRPMUnder_alert.dev_attr.attr, + &sensor_dev_attr_fan2_innerRPMUnder_alert.dev_attr.attr, + &sensor_dev_attr_fan3_innerRPMUnder_alert.dev_attr.attr, + &sensor_dev_attr_fan4_innerRPMUnder_alert.dev_attr.attr, + &sensor_dev_attr_fan5_innerRPMUnder_alert.dev_attr.attr, + &sensor_dev_attr_fan6_innerRPMUnder_alert.dev_attr.attr, + + &sensor_dev_attr_fan1_innerRPMZero_alert.dev_attr.attr, + &sensor_dev_attr_fan2_innerRPMZero_alert.dev_attr.attr, + &sensor_dev_attr_fan3_innerRPMZero_alert.dev_attr.attr, + &sensor_dev_attr_fan4_innerRPMZero_alert.dev_attr.attr, + &sensor_dev_attr_fan5_innerRPMZero_alert.dev_attr.attr, + &sensor_dev_attr_fan6_innerRPMZero_alert.dev_attr.attr, + + &sensor_dev_attr_fan1_notconnect_alert.dev_attr.attr, + &sensor_dev_attr_fan2_notconnect_alert.dev_attr.attr, + &sensor_dev_attr_fan3_notconnect_alert.dev_attr.attr, + &sensor_dev_attr_fan4_notconnect_alert.dev_attr.attr, + &sensor_dev_attr_fan5_notconnect_alert.dev_attr.attr, + &sensor_dev_attr_fan6_notconnect_alert.dev_attr.attr, + + &sensor_dev_attr_i2c_fb_timeout.dev_attr.attr, + &sensor_dev_attr_i2c_remote_timeout.dev_attr.attr, + &sensor_dev_attr_i2c_local_timeout.dev_attr.attr, + &sensor_dev_attr_i2c_lm75_48_timeout.dev_attr.attr, + &sensor_dev_attr_i2c_lm75_49_timeout.dev_attr.attr, + &sensor_dev_attr_alert_mode.dev_attr.attr, + + &sensor_dev_attr_ADC1_vol.dev_attr.attr, + &sensor_dev_attr_ADC2_vol.dev_attr.attr, + &sensor_dev_attr_ADC3_vol.dev_attr.attr, + &sensor_dev_attr_ADC4_vol.dev_attr.attr, + &sensor_dev_attr_ADC5_vol.dev_attr.attr, + &sensor_dev_attr_ADC6_vol.dev_attr.attr, + &sensor_dev_attr_ADC7_vol.dev_attr.attr, + &sensor_dev_attr_ADC8_vol.dev_attr.attr, + + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp5_input.dev_attr.attr, + + &sensor_dev_attr_temp1_label.dev_attr.attr, + &sensor_dev_attr_temp2_label.dev_attr.attr, + &sensor_dev_attr_temp3_label.dev_attr.attr, + &sensor_dev_attr_temp4_label.dev_attr.attr, + &sensor_dev_attr_temp5_label.dev_attr.attr, + + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &sensor_dev_attr_temp4_crit.dev_attr.attr, + &sensor_dev_attr_temp5_crit.dev_attr.attr, + + NULL +}; + +//static const struct attribute_group pega_hwmon_mcu_group = { .attrs = pega_hwmon_mcu_attributes}; +ATTRIBUTE_GROUPS(pega_hwmon_mcu); + +static void pega_hwmon_mcu_add_client(struct i2c_client *client) +{ + struct mcu_client_node *node = kzalloc(sizeof(struct mcu_client_node), GFP_KERNEL); + + if (!node) { + pega_print(ERR, "Can't allocate mcu_client_node (0x%x)\n", client->addr); + return; + } + + node->client = client; + + mutex_lock(&list_lock); + list_add(&node->list, &mcu_client_list); + mutex_unlock(&list_lock); + pega_print(INFO, "mcu client list added.\n"); +} + +static void pega_hwmon_mcu_remove_client(struct i2c_client *client) +{ + struct list_head *list_node = NULL; + struct mcu_client_node *mcu_mode = NULL; + int found = 0; + + mutex_lock(&list_lock); + + list_for_each(list_node, &mcu_client_list) + { + mcu_mode = list_entry(list_node, struct mcu_client_node, list); + + if (mcu_mode->client == client) { + found = 1; + break; + } + } + + if (found) { + list_del(list_node); + kfree(mcu_mode); + } + + mutex_unlock(&list_lock); + pega_print(INFO, "mcu client list removed.\n"); +} + + + +static int pega_hwmon_mcu_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int i; + struct device *dev = &client->dev; + struct hwmon_mcu_data *data; + struct device *hwmon_dev; + + data = devm_kzalloc(dev, sizeof(struct hwmon_mcu_data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + data->client = client; + + for (i = 0; i < HWMON_MCU_TEMP_NUM; i++) { + data->temp_max[i] = hwmon_temp_threshold_max[i] * 1000; + data->temp_crit[i] = hwmon_temp_threshold_crit[i] * 1000; + data->temp_min[i] = hwmon_temp_threshold_min[i] * 1000; + } + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, pega_hwmon_mcu_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + //pega_hwmon_mcu_add_client(client); + pega_print(INFO, "chip found\n"); + + return 0; +} + +static int pega_hwmon_mcu_remove(struct i2c_client *client) +{ + //pega_hwmon_mcu_remove_client(client); + return 0; +} + +static const struct i2c_device_id pega_hwmon_mcu_id[] = { + { "pega_hwmon_mcu", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, pega_hwmon_mcu_id); + +static struct i2c_driver pega_hwmon_mcu_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "pegatron_hwmon_mcu", + }, + .probe = pega_hwmon_mcu_probe, + .remove = pega_hwmon_mcu_remove, + .id_table = pega_hwmon_mcu_id, +}; + +static int __init pega_hwmon_mcu_init(void) +{ + mutex_init(&pega_hwmon_mcu_lock); + mutex_init(&list_lock); + + return i2c_add_driver(&pega_hwmon_mcu_driver); +} + +static void __exit pega_hwmon_mcu_exit(void) +{ + i2c_del_driver(&pega_hwmon_mcu_driver); +} + +module_param(loglevel, uint, 0644); +module_param_string(debug, debug, MAX_DEBUG_INFO_LEN, 0644); +MODULE_PARM_DESC(loglevel,"0x01-LOG_ERR,0x02-LOG_WARNING,0x04-LOG_INFO,0x08-LOG_DEBUG"); +MODULE_PARM_DESC(debug,"help info"); + +MODULE_AUTHOR("Peter5 Lin "); +MODULE_DESCRIPTION("pega_hwmon_mcu driver"); +MODULE_LICENSE("GPL"); + +module_init(pega_hwmon_mcu_init); +module_exit(pega_hwmon_mcu_exit); diff --git a/platform/clounix/sonic-platform-modules-pegatron/common/modules/pub/pegatron_pub.h b/platform/clounix/sonic-platform-modules-pegatron/common/modules/pub/pegatron_pub.h new file mode 100644 index 000000000000..29784aa83dc9 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/common/modules/pub/pegatron_pub.h @@ -0,0 +1,42 @@ + +#ifndef __PEGATRON_PUB_H__ +#define __PEGATRON_PUB_H__ + +#include + +#ifndef KBUILD_MODNAME +#define KBUILD_MODNAME __FILE__ +#endif + +#ifdef pr_fmt +#undef pr_fmt +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ +enum log_level +{ + LOG_ERR = 0x01, + LOG_WARNING = 0x02, + LOG_INFO = 0x04, + LOG_DEBUG = 0x08, +}; + +#define pega_print(level, fmt, ...) \ + do \ + { \ + if (LOG_##level & loglevel) \ + printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); \ + } while (0) + + +#define MAX_DEBUG_INFO_LEN 1024 + +#endif diff --git a/platform/clounix/sonic-platform-modules-pegatron/debian/changelog b/platform/clounix/sonic-platform-modules-pegatron/debian/changelog new file mode 100644 index 000000000000..1c60b2d58336 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/debian/changelog @@ -0,0 +1,5 @@ +sonic-pegatron-platform-modules (1.0.0) unstable; urgency=low + + * Add fn8656-bnf + + -- Pegatron Wed, 13 May 2020 17:41:32 +0800 diff --git a/platform/clounix/sonic-platform-modules-pegatron/debian/compat b/platform/clounix/sonic-platform-modules-pegatron/debian/compat new file mode 100644 index 000000000000..ec635144f600 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/debian/compat @@ -0,0 +1 @@ +9 diff --git a/platform/clounix/sonic-platform-modules-pegatron/debian/control b/platform/clounix/sonic-platform-modules-pegatron/debian/control new file mode 100755 index 000000000000..6481c8de0f08 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/debian/control @@ -0,0 +1,11 @@ +Source: sonic-pegatron-platform-modules +Section: main +Priority: extra +Maintainer: Pegatron +Build-Depends: debhelper (>= 8.0.0), bzip2 +Standards-Version: 3.9.3 + +Package: sonic-platform-pegatron-fn8656-bnf +Architecture: amd64 +Description: kernel modules for platform devices such as fan, led, sfp + diff --git a/platform/clounix/sonic-platform-modules-pegatron/debian/rules b/platform/clounix/sonic-platform-modules-pegatron/debian/rules new file mode 100755 index 000000000000..e3a8a7ea07a7 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/debian/rules @@ -0,0 +1,162 @@ +#!/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 ?= python2 + +PACKAGE_PRE_NAME := sonic-platform-pegatron +KVERSION ?= $(shell uname -r) +KERNEL_SRC := /lib/modules/$(KVERSION) +MOD_SRC_DIR:= $(shell pwd) +MODULE_DIRS:= fn8656-bnf +MODULE_DIR := modules +UTILS_DIR := utils +SERVICE_DIR := service +SCRIPTS_DIR := scripts +BIOS_DIR := bios_bin +CONF_DIR := conf +PROCESS_DIR := pegaProcess +IXGBE_VERSION := 5.9.4 + +MODULE_CFLAGS:=-I$(MOD_SRC_DIR)/common/modules/pub +# For fn8032-bnf/fn8032-bnf ixgbe driver, maurice + +%: + dh $@ --with systemd,python2,python3 --buildsystem=pybuild + +clean: + dh_testdir + dh_testroot + dh_clean + +build: + # For fn8032-bnf ixgbe driver, maurice + make clean -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/common/modules; \ + make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/common/modules EXTRA_CFLAGS+=${MODULE_CFLAGS}; \ + (for mod in $(MODULE_DIRS); do \ + if [ $$mod = "fn8656-bnf" ]; then \ + make clean -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \ + make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules EXTRA_CFLAGS+=${MODULE_CFLAGS}; \ + cd $(MOD_SRC_DIR)/$${mod}; \ + python2 setup.py build; \ + python2 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/utils; \ + python3 setup.py build; \ + python3 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/utils; \ + cd $(MOD_SRC_DIR); \ + cd $(MOD_SRC_DIR); \ + fi; \ + if [ $$mod = "fn8032-bnf" ]; then \ + make clean -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \ + make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules EXTRA_CFLAGS+=${MODULE_CFLAGS}; \ + cd $(MOD_SRC_DIR)/$${mod}; \ + python2 setup.py build; \ + python2 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/utils; \ + python3 setup.py build; \ + python3 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/utils; \ + cd $(MOD_SRC_DIR); \ + cd $(MOD_SRC_DIR); \ + fi; \ + if [ $$mod = "fn6254-dnf" ]; then \ + # For fn6254-dnf ixgbe driver \ + rm -rf ./ixgbe-$(IXGBE_VERSION); \ + wget -O ixgbe.tar.gz "https://downloadmirror.intel.com/30240/eng/ixgbe-5.9.4.tar.gz"; \ + tar xzf ixgbe.tar.gz; \ + rm ixgbe-$(IXGBE_VERSION)/src/Makefile ixgbe-$(IXGBE_VERSION)/src/common.mk ixgbe-$(IXGBE_VERSION)/src/Module.supported; \ + git init ixgbe-$(IXGBE_VERSION)/src; \ + git --git-dir=./ixgbe-$(IXGBE_VERSION)/src/.git --work-tree=ixgbe-$(IXGBE_VERSION)/src add --all; \ + git --git-dir=./ixgbe-$(IXGBE_VERSION)/src/.git --work-tree=ixgbe-$(IXGBE_VERSION)/src commit -m "unmodified ixgbe source"; \ + cp ./fn6254-dnf/modules/pegatron_fn6254_dnf_ixgbe/*.patch ./ixgbe-$(IXGBE_VERSION)/src/; \ + git --git-dir=./ixgbe-$(IXGBE_VERSION)/src/.git --work-tree=ixgbe-$(IXGBE_VERSION)/src am 0001-Add-clounix-porting-driver.patch; \ + cp ./ixgbe-$(IXGBE_VERSION)/src/* $(MOD_SRC_DIR)/fn6254-dnf/$(MODULE_DIR)/pegatron_fn6254_dnf_ixgbe/; \ + make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \ + fi; \ + done) + + # Build afulnx_64 for SONiC + (for mod in $(MODULE_DIRS); do \ + cd $(MOD_SRC_DIR)/$${mod}/$(BIOS_DIR); \ + ./afulnx_64 /GENDRV KERNEL=$(KERNEL_SRC)/build .;\ + ./afulnx_64 /MAKEDRV KERNEL=$(KERNEL_SRC)/build .;\ + make KERNEL=$(KERNEL_SRC)/build;\ + cd $(MOD_SRC_DIR); \ + done) + + +binary: binary-arch binary-indep + # Nothing to do + +binary-arch: + # Nothing to do + +#install: build + #dh_testdir + #dh_testroot + #dh_clean -k + #dh_installdirs + +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} /usr/local/bin; \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod} /usr/local/bin/${PROCESS_DIR}; \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod} /usr/bin; \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod} /lib/systemd/system; \ + cp $(MOD_SRC_DIR)/$${mod}/$(MODULE_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + cp $(MOD_SRC_DIR)/common/$(MODULE_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + cp $(MOD_SRC_DIR)/$${mod}/$(UTILS_DIR)/* debian/$(PACKAGE_PRE_NAME)-$${mod}/usr/local/bin/; \ + cp $(MOD_SRC_DIR)/$${mod}/$(SERVICE_DIR)/*.service debian/$(PACKAGE_PRE_NAME)-$${mod}/lib/systemd/system/; \ + cp $(MOD_SRC_DIR)/$${mod}/$(SCRIPTS_DIR)/* debian/$(PACKAGE_PRE_NAME)-$${mod}/usr/bin/; \ + cp $(MOD_SRC_DIR)/$${mod}/${PROCESS_DIR}/* debian/$(PACKAGE_PRE_NAME)-$${mod}/usr/local/bin/${PROCESS_DIR}; \ + if [ $$mod = "fn8656-bnf" ]; then \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod} /usr/share/sonic/device/x86_64-pegatron_fn8656_bnf-r0; \ + cp $(MOD_SRC_DIR)/$${mod}/$(UTILS_DIR)/*.whl debian/$(PACKAGE_PRE_NAME)-$${mod}/usr/share/sonic/device/x86_64-pegatron_fn8656_bnf-r0; \ + cp $(MOD_SRC_DIR)/$${mod}/$(SCRIPTS_DIR)/platform_update_reboot_cause debian/$(PACKAGE_PRE_NAME)-$${mod}/usr/share/sonic/device/x86_64-pegatron_fn8656_bnf-r0; \ + # Add bios upgrade tool to device(usr/local/bin), maurice 20201125 \ + cp $(MOD_SRC_DIR)/$${mod}/$(SERVICE_DIR)/platform_api_mgnt.sh debian/$(PACKAGE_PRE_NAME)-$${mod}/usr/local/bin/; \ + fi; \ + if [ $$mod = "fn8032-bnf" ]; then \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod} /usr/share/sonic/device/x86_64-pegatron_fn8032_bnf-r0; \ + cp $(MOD_SRC_DIR)/$${mod}/$(UTILS_DIR)/*.whl debian/$(PACKAGE_PRE_NAME)-$${mod}/usr/share/sonic/device/x86_64-pegatron_fn8032_bnf-r0; \ + cp $(MOD_SRC_DIR)/$${mod}/$(SCRIPTS_DIR)/platform_update_reboot_cause debian/$(PACKAGE_PRE_NAME)-$${mod}/usr/share/sonic/device/x86_64-pegatron_fn8032_bnf-r0; \ + # Add bios upgrade tool to device(usr/local/bin), maurice 20201125 \ + cp -r $(MOD_SRC_DIR)/$${mod}/bios_bin debian/$(PACKAGE_PRE_NAME)-$${mod}/usr/local/bin/; \ + cp $(MOD_SRC_DIR)/$${mod}/$(SERVICE_DIR)/platform_api_mgnt.sh debian/$(PACKAGE_PRE_NAME)-$${mod}/usr/local/bin/; \ + fi; \ + if [ $$mod = "fn6254-dnf" ]; then \ + # For fn6254-dnf ixgbe driver \ + cp $(MOD_SRC_DIR)/fn6254-dnf/$(MODULE_DIR)/pegatron_fn6254_dnf_ixgbe/pegatron_fn6254_dnf_ixgbe.ko debian/$(PACKAGE_PRE_NAME)-fn6254-dnf/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + fi; \ + 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/clounix/sonic-platform-modules-pegatron/fn8656-bnf/bios_bin/afulnx_64 b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/bios_bin/afulnx_64 new file mode 100755 index 0000000000000000000000000000000000000000..ac0b87e7eb106ab4dfdd8d61e0ec9982488e8cb9 GIT binary patch literal 1044160 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 z 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

    k`;h4JER-4*KNt1Y2wu$W;)C6qXts# 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

    RO1Nz9&m_a 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 D@cD@+ literal 0 HcmV?d00001 diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/Makefile b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/Makefile new file mode 100644 index 000000000000..ba082e51804c --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/Makefile @@ -0,0 +1,5 @@ +obj-m += pegatron_fn8656_bnf_cpld.o +obj-m += pegatron_fn8656_bnf_sfp.o +obj-m += pegatron_fn8656_bnf_psu.o +obj-m += pegatron_fn8656_bnf_watchdog.o +obj-m += clounix_watchdog.o diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/clounix_watchdog.c b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/clounix_watchdog.c new file mode 100644 index 000000000000..40508c6d8558 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/clounix_watchdog.c @@ -0,0 +1,790 @@ +/* + * intel TCO Watchdog Driver + * + * (c) Copyright 2006-2011 Wim Van Sebroeck . + * + * 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. + * + * Neither Wim Van Sebroeck nor Iguana vzw. admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + * The TCO watchdog is implemented in the following I/O controller hubs: + * (See the intel documentation on http://developer.intel.com.) + * document number 290655-003, 290677-014: 82801AA (ICH), 82801AB (ICHO) + * document number 290687-002, 298242-027: 82801BA (ICH2) + * document number 290733-003, 290739-013: 82801CA (ICH3-S) + * document number 290716-001, 290718-007: 82801CAM (ICH3-M) + * document number 290744-001, 290745-025: 82801DB (ICH4) + * document number 252337-001, 252663-008: 82801DBM (ICH4-M) + * document number 273599-001, 273645-002: 82801E (C-ICH) + * document number 252516-001, 252517-028: 82801EB (ICH5), 82801ER (ICH5R) + * document number 300641-004, 300884-013: 6300ESB + * document number 301473-002, 301474-026: 82801F (ICH6) + * document number 313082-001, 313075-006: 631xESB, 632xESB + * document number 307013-003, 307014-024: 82801G (ICH7) + * document number 322896-001, 322897-001: NM10 + * document number 313056-003, 313057-017: 82801H (ICH8) + * document number 316972-004, 316973-012: 82801I (ICH9) + * document number 319973-002, 319974-002: 82801J (ICH10) + * document number 322169-001, 322170-003: 5 Series, 3400 Series (PCH) + * document number 320066-003, 320257-008: EP80597 (IICH) + * document number 324645-001, 324646-001: Cougar Point (CPT) + * document number TBD : Patsburg (PBG) + * document number TBD : DH89xxCC + * document number TBD : Panther Point + * document number TBD : Lynx Point + * document number TBD : Lynx Point-LP + */ + +/* + * Includes, defines, variables, module parameters, ... + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +/* Module and version information */ +#define DRV_NAME "iTCO_wdt" +#define DRV_VERSION "1.11" + +/* Includes */ +#include /* For ACPI support */ +#include /* For module specific items */ +#include /* For new moduleparam's */ +#include /* For standard types (like size_t) */ +#include /* For the -ENODEV/... values */ +#include /* For printk/panic/... */ +#include /* For the watchdog specific items */ +#include /* For __init/__exit/... */ +#include /* For file operations */ +#include /* For platform_driver framework */ +#include /* For pci functions */ +#include /* For io-port access */ +#include /* For spin_lock/spin_unlock/... */ +#include /* For copy_to_user/put_user/... */ +#include /* For inb/outb/... */ +#include +#include +#include +#include +#include +#include +#include + +#include "iTCO_vendor.h" +#include "pegatron_pub.h" + +/* Address definitions for the TCO */ +/* TCO base address */ +#define TCOBASE(p) ((p)->tco_res->start) +/* SMI Control and Enable Register */ +#define SMI_EN(p) ((p)->smi_res->start) + +#define TCO_RLD(p) (TCOBASE(p) + 0x00) /* TCO Timer Reload/Curr. Value */ +#define TCOv1_TMR(p) (TCOBASE(p) + 0x01) /* TCOv1 Timer Initial Value*/ +#define TCO_DAT_IN(p) (TCOBASE(p) + 0x02) /* TCO Data In Register */ +#define TCO_DAT_OUT(p) (TCOBASE(p) + 0x03) /* TCO Data Out Register */ +#define TCO1_STS(p) (TCOBASE(p) + 0x04) /* TCO1 Status Register */ +#define TCO2_STS(p) (TCOBASE(p) + 0x06) /* TCO2 Status Register */ +#define TCO1_CNT(p) (TCOBASE(p) + 0x08) /* TCO1 Control Register */ +#define TCO2_CNT(p) (TCOBASE(p) + 0x0a) /* TCO2 Control Register */ +#define TCOv2_TMR(p) (TCOBASE(p) + 0x12) /* TCOv2 Timer Initial Value*/ + +/* internal variables */ +struct iTCO_wdt_private { + struct watchdog_device wddev; + + /* TCO version/generation */ + unsigned int iTCO_version; + struct resource *tco_res; + struct resource *smi_res; + /* + * NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2), + * or memory-mapped PMC register bit 4 (TCO version 3). + */ + struct resource *gcs_pmc_res; + unsigned long __iomem *gcs_pmc; + /* the lock for io operations */ + spinlock_t io_lock; + /* the PCI-device */ + struct pci_dev *pci_dev; + /* whether or not the watchdog has been suspended */ + bool suspended; + /* no reboot API private data */ + void *no_reboot_priv; + /* no reboot update function pointer */ + int (*update_no_reboot_bit)(void *p, bool set); +}; + +/* module parameters */ +#define WATCHDOG_TIMEOUT 30 /* 30 sec default heartbeat */ +static int heartbeat = WATCHDOG_TIMEOUT; /* in seconds */ +module_param(heartbeat, int, 0644); +MODULE_PARM_DESC(heartbeat, "Watchdog timeout in seconds. " + "5..76 (TCO v1) or 3..614 (TCO v2), default=" + __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static int turn_SMI_watchdog_clear_off = 1; +module_param(turn_SMI_watchdog_clear_off, int, 0); +MODULE_PARM_DESC(turn_SMI_watchdog_clear_off, + "Turn off SMI clearing watchdog (depends on TCO-version)(default=1)"); + +static uint loglevel = LOG_INFO | LOG_WARNING | LOG_ERR; +static char watchdog_debug[MAX_DEBUG_INFO_LEN] = "This watchdog iTCO is in Intel SoC, more details please refer to Intel datasheet\n"; + +/* + * Some TCO specific functions + */ + +/* + * The iTCO v1 and v2's internal timer is stored as ticks which decrement + * every 0.6 seconds. v3's internal timer is stored as seconds (some + * datasheets incorrectly state 0.6 seconds). + */ +static inline unsigned int seconds_to_ticks(struct iTCO_wdt_private *p, + int secs) +{ + return p->iTCO_version == 3 ? secs : (secs * 10) / 6; +} + +static inline unsigned int ticks_to_seconds(struct iTCO_wdt_private *p, + int ticks) +{ + return p->iTCO_version == 3 ? ticks : (ticks * 6) / 10; +} + +static inline u32 no_reboot_bit(struct iTCO_wdt_private *p) +{ + u32 enable_bit; + + switch (p->iTCO_version) { + case 5: + case 3: + enable_bit = 0x00000010; + break; + case 2: + enable_bit = 0x00000020; + break; + case 4: + case 1: + default: + enable_bit = 0x00000002; + break; + } + + return enable_bit; +} + +static int update_no_reboot_bit_def(void *priv, bool set) +{ + return 0; +} + +static int update_no_reboot_bit_pci(void *priv, bool set) +{ + struct iTCO_wdt_private *p = priv; + u32 val32 = 0, newval32 = 0; + + pci_read_config_dword(p->pci_dev, 0xd4, &val32); + if (set) + val32 |= no_reboot_bit(p); + else + val32 &= ~no_reboot_bit(p); + pci_write_config_dword(p->pci_dev, 0xd4, val32); + pci_read_config_dword(p->pci_dev, 0xd4, &newval32); + + /* make sure the update is successful */ + if (val32 != newval32) + return -EIO; + + return 0; +} + +static int update_no_reboot_bit_mem(void *priv, bool set) +{ + struct iTCO_wdt_private *p = priv; + u32 val32 = 0, newval32 = 0; + + val32 = readl(p->gcs_pmc); + if (set) + val32 |= no_reboot_bit(p); + else + val32 &= ~no_reboot_bit(p); + writel(val32, p->gcs_pmc); + newval32 = readl(p->gcs_pmc); + + /* make sure the update is successful */ + if (val32 != newval32) + return -EIO; + + return 0; +} + +static void iTCO_wdt_no_reboot_bit_setup(struct iTCO_wdt_private *p, + struct itco_wdt_platform_data *pdata) +{ + if (pdata->update_no_reboot_bit) { + p->update_no_reboot_bit = pdata->update_no_reboot_bit; + p->no_reboot_priv = pdata->no_reboot_priv; + return; + } + + if (p->iTCO_version >= 2) + p->update_no_reboot_bit = update_no_reboot_bit_mem; + else if (p->iTCO_version == 1) + p->update_no_reboot_bit = update_no_reboot_bit_pci; + else + p->update_no_reboot_bit = update_no_reboot_bit_def; + + p->no_reboot_priv = p; +} + +static int iTCO_wdt_start(struct watchdog_device *wd_dev) +{ + struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); + unsigned int val; + + spin_lock(&p->io_lock); + + iTCO_vendor_pre_start(p->smi_res, wd_dev->timeout); + + /* disable chipset's NO_REBOOT bit */ + if (p->update_no_reboot_bit(p->no_reboot_priv, false)) { + spin_unlock(&p->io_lock); + pega_print(ERR, "failed to reset NO_REBOOT flag, reboot disabled by hardware/BIOS\n"); + return -EIO; + } + + /* Force the timer to its reload value by writing to the TCO_RLD + register */ + if (p->iTCO_version >= 2) + outw(0x01, TCO_RLD(p)); + else if (p->iTCO_version == 1) + outb(0x01, TCO_RLD(p)); + + /* Bit 11: TCO Timer Halt -> 0 = The TCO timer is enabled to count */ + val = inw(TCO1_CNT(p)); + val &= 0xf7ff; + outw(val, TCO1_CNT(p)); + val = inw(TCO1_CNT(p)); + spin_unlock(&p->io_lock); + + if (val & 0x0800) + return -1; + return 0; +} + +static int iTCO_wdt_stop(struct watchdog_device *wd_dev) +{ + struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); + unsigned int val; + + spin_lock(&p->io_lock); + + iTCO_vendor_pre_stop(p->smi_res); + + /* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */ + val = inw(TCO1_CNT(p)); + val |= 0x0800; + outw(val, TCO1_CNT(p)); + val = inw(TCO1_CNT(p)); + + /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ + p->update_no_reboot_bit(p->no_reboot_priv, true); + + spin_unlock(&p->io_lock); + + if ((val & 0x0800) == 0) + return -1; + return 0; +} + +static int iTCO_wdt_ping(struct watchdog_device *wd_dev) +{ + struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); + + spin_lock(&p->io_lock); + + iTCO_vendor_pre_keepalive(p->smi_res, wd_dev->timeout); + + /* Reload the timer by writing to the TCO Timer Counter register */ + if (p->iTCO_version >= 2) { + outw(0x01, TCO_RLD(p)); + } else if (p->iTCO_version == 1) { + /* Reset the timeout status bit so that the timer + * needs to count down twice again before rebooting */ + outw(0x0008, TCO1_STS(p)); /* write 1 to clear bit */ + + outb(0x01, TCO_RLD(p)); + } + + spin_unlock(&p->io_lock); + return 0; +} + +static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t) +{ + struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); + unsigned int val16; + unsigned char val8; + unsigned int tmrval; + + tmrval = seconds_to_ticks(p, t); + + /* For TCO v1 the timer counts down twice before rebooting */ + if (p->iTCO_version == 1) + tmrval /= 2; + + /* from the specs: */ + /* "Values of 0h-3h are ignored and should not be attempted" */ + if (tmrval < 0x04) + return -EINVAL; + if ((p->iTCO_version >= 2 && tmrval > 0x3ff) || + (p->iTCO_version == 1 && tmrval > 0x03f)) + return -EINVAL; + + iTCO_vendor_pre_set_heartbeat(tmrval); + + /* Write new heartbeat to watchdog */ + if (p->iTCO_version >= 2) { + spin_lock(&p->io_lock); + val16 = inw(TCOv2_TMR(p)); + val16 &= 0xfc00; + val16 |= tmrval; + outw(val16, TCOv2_TMR(p)); + val16 = inw(TCOv2_TMR(p)); + spin_unlock(&p->io_lock); + + if ((val16 & 0x3ff) != tmrval) + return -EINVAL; + } else if (p->iTCO_version == 1) { + spin_lock(&p->io_lock); + val8 = inb(TCOv1_TMR(p)); + val8 &= 0xc0; + val8 |= (tmrval & 0xff); + outb(val8, TCOv1_TMR(p)); + val8 = inb(TCOv1_TMR(p)); + spin_unlock(&p->io_lock); + + if ((val8 & 0x3f) != tmrval) + return -EINVAL; + } + + wd_dev->timeout = t; + return 0; +} + +static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev) +{ + struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); + unsigned int val16; + unsigned char val8; + unsigned int time_left = 0; + + /* read the TCO Timer */ + if (p->iTCO_version >= 2) { + spin_lock(&p->io_lock); + val16 = inw(TCO_RLD(p)); + val16 &= 0x3ff; + spin_unlock(&p->io_lock); + + time_left = ticks_to_seconds(p, val16); + } else if (p->iTCO_version == 1) { + spin_lock(&p->io_lock); + val8 = inb(TCO_RLD(p)); + val8 &= 0x3f; + if (!(inw(TCO1_STS(p)) & 0x0008)) + val8 += (inb(TCOv1_TMR(p)) & 0x3f); + spin_unlock(&p->io_lock); + + time_left = ticks_to_seconds(p, val8); + } + return time_left; +} + +/* + * Kernel Interfaces + */ + +static const struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, + .firmware_version = 0, + .identity = DRV_NAME, +}; + +static const struct watchdog_ops iTCO_wdt_ops = { + .owner = THIS_MODULE, + .start = iTCO_wdt_start, + .stop = iTCO_wdt_stop, + .ping = iTCO_wdt_ping, + .set_timeout = iTCO_wdt_set_timeout, + .get_timeleft = iTCO_wdt_get_timeleft, +}; + +#if 1 //add by clounix +static struct kobject *parent_kobj = NULL; +static unsigned int work_status = 0; +static struct iTCO_wdt_private *priv_data = NULL; +static struct hrtimer work_timer = {0}; +static struct work_struct work_task = {0}; + +void ping_work(struct work_struct *work) +{ + priv_data->wddev.ops->ping(&(priv_data->wddev)); +} + +static enum hrtimer_restart timer_work_expired(struct hrtimer *timer) +{ + if (work_status == 0) + return HRTIMER_NORESTART; + + if (work_status == 1) { + schedule_work(&work_task); + hrtimer_start(&work_timer, ms_to_ktime((heartbeat*1000)/2), HRTIMER_MODE_REL); + } + + return HRTIMER_NORESTART; +} + +ssize_t clounix_ping_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + if (*buf != '1') + return -EPERM; + + priv_data->wddev.ops->ping(&(priv_data->wddev)); + + return count; +} + +ssize_t clounix_timeout_ctrl_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\r\n", heartbeat); +} + +ssize_t clounix_timeout_ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + if (count > 10) + return -EPERM; + + if (kstrtoint(buf, 0, &heartbeat) != 0) + return -EPERM; + + hrtimer_cancel(&work_timer); + priv_data->wddev.ops->stop(&(priv_data->wddev)); + + if (priv_data->wddev.ops->set_timeout(&(priv_data->wddev), heartbeat) != 0) { + priv_data->wddev.ops->set_timeout(&(priv_data->wddev), WATCHDOG_TIMEOUT); + pega_print(INFO, "timeout value out of range, using %d\n", + WATCHDOG_TIMEOUT); + heartbeat = WATCHDOG_TIMEOUT; + } + + priv_data->wddev.ops->start(&(priv_data->wddev)); + hrtimer_start(&work_timer, 0, HRTIMER_MODE_REL); + set_bit(WDOG_ACTIVE, &priv_data->wddev.status); + + return count; +} +ssize_t clounix_ctrl_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\r\n", work_status); +} + +ssize_t clounix_ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + if (*buf == '1') { + if (work_status == 0) { + work_status = 1; + priv_data->wddev.ops->start(&(priv_data->wddev)); + hrtimer_start(&work_timer, 0, HRTIMER_MODE_REL); + set_bit(WDOG_ACTIVE, &priv_data->wddev.status); + } + } else if (*buf == '0') { + if (work_status == 1) { + work_status = 0; + priv_data->wddev.ops->ping(&(priv_data->wddev)); + priv_data->wddev.ops->stop(&(priv_data->wddev)); + hrtimer_cancel(&work_timer); + flush_work(&work_task); + clear_bit(WDOG_ACTIVE, &priv_data->wddev.status); + } + } else { + return -EINVAL; + } + + return count; +} + +DEVICE_ATTR_WO(clounix_ping); +DEVICE_ATTR_RW(clounix_timeout_ctrl); +DEVICE_ATTR_RW(clounix_ctrl); +static struct attribute *wdt_attrs[] = { + &dev_attr_clounix_ping.attr, + &dev_attr_clounix_timeout_ctrl.attr, + &dev_attr_clounix_ctrl.attr, + NULL +}; +const struct attribute_group ops_group = { + .attrs = wdt_attrs +}; +#endif + +/* + * Init & exit routines + */ + +static int iTCO_wdt_probe(struct platform_device *pdev) +{ + + struct device *dev = &pdev->dev; + struct itco_wdt_platform_data *pdata = dev_get_platdata(dev); + struct iTCO_wdt_private *p; + unsigned long val32; + int ret; + + if (!pdata) + return -ENODEV; + + p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + + spin_lock_init(&p->io_lock); + + p->tco_res = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_IO_TCO); + if (!p->tco_res) + return -ENODEV; + + p->smi_res = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_IO_SMI); + if (!p->smi_res) + return -ENODEV; + + p->iTCO_version = pdata->version; + p->pci_dev = to_pci_dev(dev->parent); + + iTCO_wdt_no_reboot_bit_setup(p, pdata); + + /* + * Get the Memory-Mapped GCS or PMC register, we need it for the + * NO_REBOOT flag (TCO v2 and v3). + */ + if (p->iTCO_version >= 2 && !pdata->update_no_reboot_bit) { + p->gcs_pmc_res = platform_get_resource(pdev, + IORESOURCE_MEM, + ICH_RES_MEM_GCS_PMC); + p->gcs_pmc = devm_ioremap_resource(dev, p->gcs_pmc_res); + if (IS_ERR(p->gcs_pmc)) + return PTR_ERR(p->gcs_pmc); + } + + /* Check chipset's NO_REBOOT bit */ + if (p->update_no_reboot_bit(p->no_reboot_priv, false) && + iTCO_vendor_check_noreboot_on()) { + pega_print(INFO, "unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n"); + return -ENODEV; /* Cannot reset NO_REBOOT bit */ + } + + /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ + p->update_no_reboot_bit(p->no_reboot_priv, true); + + /* The TCO logic uses the TCO_EN bit in the SMI_EN register */ + if (!devm_request_region(dev, p->smi_res->start, + resource_size(p->smi_res), + pdev->name)) { + pega_print(ERR, "I/O address 0x%04llx already in use, device disabled\n", + (u64)SMI_EN(p)); + return -EBUSY; + } + if (turn_SMI_watchdog_clear_off >= p->iTCO_version) { + /* + * Bit 13: TCO_EN -> 0 + * Disables TCO logic generating an SMI# + */ + val32 = inl(SMI_EN(p)); + val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */ + outl(val32, SMI_EN(p)); + } + + if (!devm_request_region(dev, p->tco_res->start, + resource_size(p->tco_res), + pdev->name)) { + pega_print(ERR, "I/O address 0x%04llx already in use, device disabled\n", + (u64)TCOBASE(p)); + return -EBUSY; + } + + pega_print(INFO, "Found a %s TCO device (Version=%d, TCOBASE=0x%04llx)\n", + pdata->name, pdata->version, (u64)TCOBASE(p)); + + /* Clear out the (probably old) status */ + switch (p->iTCO_version) { + case 5: + case 4: + outw(0x0008, TCO1_STS(p)); /* Clear the Time Out Status bit */ + outw(0x0002, TCO2_STS(p)); /* Clear SECOND_TO_STS bit */ + break; + case 3: + outl(0x20008, TCO1_STS(p)); + break; + case 2: + case 1: + default: + outw(0x0008, TCO1_STS(p)); /* Clear the Time Out Status bit */ + outw(0x0002, TCO2_STS(p)); /* Clear SECOND_TO_STS bit */ + outw(0x0004, TCO2_STS(p)); /* Clear BOOT_STS bit */ + break; + } + + p->wddev.info = &ident, + p->wddev.ops = &iTCO_wdt_ops, + p->wddev.bootstatus = 0; + p->wddev.timeout = WATCHDOG_TIMEOUT; + watchdog_set_nowayout(&p->wddev, nowayout); + p->wddev.parent = dev; + + + watchdog_set_drvdata(&p->wddev, p); + platform_set_drvdata(pdev, p); + + if (priv_data == NULL) + priv_data = p; + + /* Make sure the watchdog is not running */ + iTCO_wdt_stop(&p->wddev); + + /* Check that the heartbeat value is within it's range; + if not reset to the default */ + if (iTCO_wdt_set_timeout(&p->wddev, heartbeat)) { + iTCO_wdt_set_timeout(&p->wddev, WATCHDOG_TIMEOUT); + pega_print(INFO, "timeout value out of range, using %d\n", + WATCHDOG_TIMEOUT); + } + + watchdog_stop_on_reboot(&p->wddev); + ret = devm_watchdog_register_device(dev, &p->wddev); + if (ret != 0) { + pega_print(ERR, "cannot register watchdog device (err=%d)\n", ret); + return ret; + } + + pega_print(INFO, "initialized. heartbeat=%d sec (nowayout=%d)\n", + heartbeat, nowayout); + + INIT_WORK(&work_task, ping_work); + hrtimer_init(&work_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + work_timer.function = timer_work_expired; + parent_kobj = pdev->dev.kobj.parent; + + return sysfs_create_group(parent_kobj, &ops_group); +} + +static int iTCO_wdt_remove(struct platform_device *pdev) +{ + struct iTCO_wdt_private *p = platform_get_drvdata(pdev); + + hrtimer_cancel(&work_timer); + flush_work(&work_task); + sysfs_remove_group(parent_kobj, &ops_group); + priv_data->wddev.ops->ping(&(priv_data->wddev)); + + /* Stop the timer before we leave */ + if (!nowayout) + iTCO_wdt_stop(&p->wddev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +/* + * Suspend-to-idle requires this, because it stops the ticks and timekeeping, so + * the watchdog cannot be pinged while in that state. In ACPI sleep states the + * watchdog is stopped by the platform firmware. + */ + +#ifdef CONFIG_ACPI +static inline bool need_suspend(void) +{ + return acpi_target_system_state() == ACPI_STATE_S0; +} +#else +static inline bool need_suspend(void) { return true; } +#endif + +static int iTCO_wdt_suspend_noirq(struct device *dev) +{ + struct iTCO_wdt_private *p = dev_get_drvdata(dev); + int ret = 0; + + p->suspended = false; + if (watchdog_active(&p->wddev) && need_suspend()) { + ret = iTCO_wdt_stop(&p->wddev); + if (!ret) + p->suspended = true; + } + return ret; +} + +static int iTCO_wdt_resume_noirq(struct device *dev) +{ + struct iTCO_wdt_private *p = dev_get_drvdata(dev); + + if (p->suspended) + iTCO_wdt_start(&p->wddev); + + return 0; +} + +static const struct dev_pm_ops iTCO_wdt_pm = { + .suspend_noirq = iTCO_wdt_suspend_noirq, + .resume_noirq = iTCO_wdt_resume_noirq, +}; + +#define ITCO_WDT_PM_OPS (&iTCO_wdt_pm) +#else +#define ITCO_WDT_PM_OPS NULL +#endif /* CONFIG_PM_SLEEP */ + +static struct platform_driver iTCO_wdt_driver = { + .probe = iTCO_wdt_probe, + .remove = iTCO_wdt_remove, + .driver = { + .name = DRV_NAME, + .pm = ITCO_WDT_PM_OPS, + }, +}; + +static int __init iTCO_wdt_init_module(void) +{ + pega_print(INFO, "Intel TCO WatchDog Timer Driver v%s\n", DRV_VERSION); + + return platform_driver_register(&iTCO_wdt_driver); +} + +static void __exit iTCO_wdt_cleanup_module(void) +{ + platform_driver_unregister(&iTCO_wdt_driver); + pega_print(INFO, "Watchdog Module Unloaded\n"); +} + +module_init(iTCO_wdt_init_module); +module_exit(iTCO_wdt_cleanup_module); + +module_param(loglevel, uint, 0644); +module_param_string(watchdog_debug, watchdog_debug, MAX_DEBUG_INFO_LEN, 0644); + +MODULE_AUTHOR("Wim Van Sebroeck "); +MODULE_DESCRIPTION("Intel TCO WatchDog Timer Driver"); +MODULE_VERSION(DRV_VERSION); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/iTCO_vendor.h b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/iTCO_vendor.h new file mode 100644 index 000000000000..7b82a7c6e7c3 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/iTCO_vendor.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* iTCO Vendor Specific Support hooks */ +#ifdef CONFIG_ITCO_VENDOR_SUPPORT +extern void iTCO_vendor_pre_start(struct resource *, unsigned int); +extern void iTCO_vendor_pre_stop(struct resource *); +extern void iTCO_vendor_pre_keepalive(struct resource *, unsigned int); +extern void iTCO_vendor_pre_set_heartbeat(unsigned int); +extern int iTCO_vendor_check_noreboot_on(void); +#else +#define iTCO_vendor_pre_start(acpibase, heartbeat) {} +#define iTCO_vendor_pre_stop(acpibase) {} +#define iTCO_vendor_pre_keepalive(acpibase, heartbeat) {} +#define iTCO_vendor_pre_set_heartbeat(heartbeat) {} +#define iTCO_vendor_check_noreboot_on() 1 + /* 1=check noreboot; 0=don't check */ +#endif diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/pegatron_fn8656_bnf_cpld.c b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/pegatron_fn8656_bnf_cpld.c new file mode 100644 index 000000000000..c9604154dc32 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/pegatron_fn8656_bnf_cpld.c @@ -0,0 +1,1692 @@ +/* + * A CPLD driver for the fn8656_bnf + * + * Copyright (C) 2018 Pegatron Corporation. + * Peter5_Lin + * + * 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 "pegatron_pub.h" + + +#define PEGA_RD(x) 1 + +#define CPLD_MAX_NUM 4 +#define CPLD_SFP_MAX_GROUP 3 +#define SFP_PORT_MAX_NUM 56 +#define SFP_EEPROM_SIZE 256 +#define QSFP_FIRST_PORT 48 +#define CPLDA_SFP_NUM 0 +#define CPLDB_SFP_NUM 28 +#define CPLDC_SFP_NUM 28 +#define CPLDA_ADDRESS 0x74 +#define CPLDB_ADDRESS 0x75 +#define CPLDC_ADDRESS 0x76 +#define CPLDD_ADDRESS 0x18 +#define LM75B_ADDRESS 0x4a +#define LM75B_TEMP_REG 0x0 + + +#define GET_BIT(data, bit, value) value = (data >> bit) & 0x1 +#define SET_BIT(data, bit) data |= (1 << bit) +#define CLEAR_BIT(data, bit) data &= ~(1 << bit) + +static LIST_HEAD(cpld_client_list); +static struct mutex list_lock; +/* Addresses scanned for pegatron_fn8656_bnf_cpld + */ +static const unsigned short normal_i2c[] = { CPLDA_ADDRESS, CPLDB_ADDRESS, CPLDC_ADDRESS, CPLDD_ADDRESS, LM75B_ADDRESS, I2C_CLIENT_END }; + +static uint loglevel = LOG_INFO | LOG_WARNING | LOG_ERR; +static char sfp_debug[MAX_DEBUG_INFO_LEN] = "debug info: \n\ +init_mode --- 0: High power mode, 1: Low power mode \n\ +power_on --- 0: Not finished, 1: Finished \n\ +reset --- 0: Reset Keep low, 1: Reset Keep high \n\ +present --- 0: Module insert, 1: Module absent \n\ +interrupt --- 0: Interrupt active 1: Normal operation\n"; + +static char cpld_debug[MAX_DEBUG_INFO_LEN] = "cpld debug info: \n"; +static char sysled_debug[MAX_DEBUG_INFO_LEN] = "sysled debug info: \n"; + +enum{ + DEFAULT_REQUIREMENT = 0x0, + KS_REQUIREMENT = 0x01 +}; + +static uint requirement_flag = DEFAULT_REQUIREMENT; + +struct cpld_client_node { + struct i2c_client *client; + struct list_head list; +}; + +int pegatron_fn8656_bnf_cpld_read(unsigned short addr, u8 reg) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int data = -EPERM; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == addr) { + data = i2c_smbus_read_byte_data(cpld_node->client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", addr, reg, data); + break; + } + } + + mutex_unlock(&list_lock); + + return data; +} +EXPORT_SYMBOL(pegatron_fn8656_bnf_cpld_read); + + +int pegatron_fn8656_bnf_cpld_write(unsigned short addr, u8 reg, u8 val) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EIO; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == addr) { + ret = i2c_smbus_write_byte_data(cpld_node->client, reg, val); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", addr, reg, val); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(pegatron_fn8656_bnf_cpld_write); + +#if PEGA_RD(CPLD_VERSION) + +#define CPLD_VERSION_REG 0x0 + #define CPLD_HW_VERSION_BIT_OFFSET 5 + #define CPLD_HW_VERSION_BIT_MSK (0x3 << CPLD_HW_VERSION_BIT_OFFSET) + #define CPLD_SW_VERSION_BIT_OFFSET 0 + #define CPLD_SW_VERSION_BIT_MSK (0x1F << CPLD_SW_VERSION_BIT_OFFSET) + +static ssize_t read_cpld_HWversion(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 data = 0, reg = CPLD_VERSION_REG; + + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + + return sprintf(buf, "0x%02x\n", (data & CPLD_HW_VERSION_BIT_MSK) >> CPLD_HW_VERSION_BIT_OFFSET); +} + +static ssize_t read_cpld_SWversion(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 data = 0, reg = CPLD_VERSION_REG; + + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + + return sprintf(buf, "%02d\n", (data & CPLD_SW_VERSION_BIT_MSK) >> CPLD_SW_VERSION_BIT_OFFSET); +} +#endif //CPLD_VERSION + +#if PEGA_RD(LED) + +#define CPLD_A_LED_CONTROL_REG_1 0x5 +#define CPLD_A_SYS_LED_CTL_BIT_OFFSET 5 +#define CPLD_A_SYS_LED_CTL_BIT_MSK (0x7 << CPLD_A_SYS_LED_CTL_BIT_OFFSET) +#define CPLD_A_PWR_LED_CTL_BIT_OFFSET 2 +#define CPLD_A_PWR_LED_CTL_BIT_MSK (0x7 << CPLD_A_PWR_LED_CTL_BIT_OFFSET) + +#define CPLD_A_LED_CONTROL_REG_2 0x6 +#define CPLD_A_FAN_LED_CTL_BIT_OFFSET 0 +#define CPLD_A_FAN_LED_CTL_BIT_MSK (0x7 << CPLD_A_FAN_LED_CTL_BIT_OFFSET) +#define CPLD_A_SERIAL_LED_BIT_OFFSET 3 +#define CPLD_A_SERIAL_LED_BIT_MASK (0x1 << CPLD_A_SERIAL_LED_BIT_OFFSET) +#define CPLD_A_LED_CONTROL_REG_3 0x7 +#define CPLD_A_ALL_LED_CTL_BIT_OFFSET 0 +#define CPLD_A_ALL_LED_CTL_BIT_MSK (0xFF << CPLD_A_ALL_LED_CTL_BIT_OFFSET) +#define CPLD_BC_LED_CONTROL_REG_1 0x01 +#define CPLD_BC_SERIAL_LED_BIT_OFFSET 2 +#define CPLD_BC_SERIAL_LED_BIT_MASK (0x1 << CPLD_BC_SERIAL_LED_BIT_OFFSET) + +#define CPLD_D_LED_CONTROL_REG 0x6 +#define CPLD_D_LOC_LED_CTL_BIT_OFFSET 0 +#define CPLD_D_LOC_LED_CTL_BIT_MSK (0x3 << CPLD_D_LOC_LED_CTL_BIT_OFFSET) + +enum _sysfs_led_attr { + ALL_LED_CTRL = 0, + SERIAL_LED_CTRL, + SYS_LED_CTRL, + PWR_LED_CTRL, + LOC_LED_CTRL, + FAN_LED_CTRL, + LED_ATTR_END +}; +enum _cpld_led_bit_attr{ + CPLD_LED_STATUS_GREEN_ON = 0, + CPLD_LED_STATUS_RED_ON, + CPLD_LED_STATUS_OFF, + CPLD_LED_STATUS_MIX_ON, + CPLD_LED_STATUS_GREEN_BLINK, + CPLD_LED_STATUS_RED_BLINK, + CPLD_LED_STATUS_MIX_BLINK, + CPLD_LED_STATUS_NOT_SUPPORT +}; +enum _kwai_led_bit_attr{ + KWAI_LED_STATUS_OFF = 0, + KWAI_LED_STATUS_GREEN_ON, + KWAI_LED_STATUS_YELLOW_ON, + KWAI_LED_STATUS_RED_ON, + KWAI_LED_STATUS_GREEN_BLINK, + KWAI_LED_STATUS_YELLOW_BLINK, + KWAI_LED_STATUS_RED_BLINK, + KWAI_LED_STATUS_BLLUE_ON, + KWAI_LED_STATUS_BLLUE_BLINK, + KWAI_LED_STATUS_NOT_EXIST +}; + + +static u8 map_ledstatus_cpldconfig_2_userconfig[] = {KWAI_LED_STATUS_GREEN_ON,KWAI_LED_STATUS_RED_ON,KWAI_LED_STATUS_OFF,KWAI_LED_STATUS_NOT_EXIST, + KWAI_LED_STATUS_GREEN_BLINK,KWAI_LED_STATUS_RED_BLINK,KWAI_LED_STATUS_NOT_EXIST}; + + +static u8 map_ledstatus_userconfig_2_cpldconfig[] = {CPLD_LED_STATUS_OFF,CPLD_LED_STATUS_GREEN_ON,CPLD_LED_STATUS_NOT_SUPPORT,CPLD_LED_STATUS_RED_ON,CPLD_LED_STATUS_GREEN_BLINK, + CPLD_LED_STATUS_NOT_SUPPORT,CPLD_LED_STATUS_RED_BLINK,CPLD_LED_STATUS_NOT_SUPPORT,CPLD_LED_STATUS_NOT_SUPPORT}; + +static ssize_t show_led_status(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); + u8 data = 0, reg = 0,bit_offset = 0,bit_msk = 0; + bool attr_cpld_2_user = FALSE; + + switch(attr->index) { + case ALL_LED_CTRL: + reg = CPLD_A_LED_CONTROL_REG_3; + bit_offset = CPLD_A_ALL_LED_CTL_BIT_OFFSET; + bit_msk = CPLD_A_ALL_LED_CTL_BIT_MSK; + break; + case SERIAL_LED_CTRL: + if(client->addr == CPLDA_ADDRESS) + { + reg = CPLD_A_LED_CONTROL_REG_2; + bit_offset = CPLD_A_SERIAL_LED_BIT_OFFSET; + bit_msk = CPLD_A_SERIAL_LED_BIT_MASK; + } + else if((client->addr == CPLDB_ADDRESS) || (client->addr == CPLDC_ADDRESS)) + { + reg = CPLD_BC_LED_CONTROL_REG_1; + bit_offset = CPLD_BC_SERIAL_LED_BIT_OFFSET; + bit_msk = CPLD_BC_SERIAL_LED_BIT_MASK; + } + break; + case SYS_LED_CTRL: + reg = CPLD_A_LED_CONTROL_REG_1; + bit_offset = CPLD_A_SYS_LED_CTL_BIT_OFFSET; + bit_msk = CPLD_A_SYS_LED_CTL_BIT_MSK; + attr_cpld_2_user = TRUE; + break; + case PWR_LED_CTRL: + reg = CPLD_A_LED_CONTROL_REG_1; + bit_offset = CPLD_A_PWR_LED_CTL_BIT_OFFSET; + bit_msk = CPLD_A_PWR_LED_CTL_BIT_MSK; + attr_cpld_2_user = TRUE; + break; + case FAN_LED_CTRL: + reg = CPLD_A_LED_CONTROL_REG_2; + bit_offset = CPLD_A_FAN_LED_CTL_BIT_OFFSET; + bit_msk = CPLD_A_FAN_LED_CTL_BIT_MSK; + attr_cpld_2_user = TRUE; + break; + case LOC_LED_CTRL: + reg = CPLD_D_LED_CONTROL_REG; + bit_offset = CPLD_D_LOC_LED_CTL_BIT_OFFSET; + bit_msk = CPLD_D_LOC_LED_CTL_BIT_MSK; + break; + default: + pega_print(ERR,"incorrect led type\n"); + return sprintf(buf, "ERR"); + } + + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + data = (data & bit_msk) >> bit_offset; + + if(data >= CPLD_LED_STATUS_NOT_SUPPORT) + return -EINVAL; + if(attr_cpld_2_user == TRUE) + data = map_ledstatus_cpldconfig_2_userconfig[data]; + + return sprintf(buf, "%d\n", data); +} + +static ssize_t set_led_status(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); + u8 data = 0, reg = 0,bit_offset = 0,bit_msk = 0; + long val = 0; + bool attr_user_2_cpld = FALSE; + + switch(attr->index) { + case ALL_LED_CTRL: + reg = CPLD_A_LED_CONTROL_REG_3; + bit_offset = CPLD_A_ALL_LED_CTL_BIT_OFFSET; + bit_msk = CPLD_A_ALL_LED_CTL_BIT_MSK; + break; + case SERIAL_LED_CTRL: + if(client->addr == CPLDA_ADDRESS) + { + reg = CPLD_A_LED_CONTROL_REG_2; + bit_offset = CPLD_A_SERIAL_LED_BIT_OFFSET; + bit_msk = CPLD_A_SERIAL_LED_BIT_MASK; + } + else if((client->addr == CPLDB_ADDRESS) || (client->addr == CPLDC_ADDRESS)) + { + reg = CPLD_BC_LED_CONTROL_REG_1; + bit_offset = CPLD_BC_SERIAL_LED_BIT_OFFSET; + bit_msk = CPLD_BC_SERIAL_LED_BIT_MASK; + } + break; + case SYS_LED_CTRL: + reg = CPLD_A_LED_CONTROL_REG_1; + bit_offset = CPLD_A_SYS_LED_CTL_BIT_OFFSET; + bit_msk = CPLD_A_SYS_LED_CTL_BIT_MSK; + attr_user_2_cpld = TRUE; + break; + case PWR_LED_CTRL: + reg = CPLD_A_LED_CONTROL_REG_1; + bit_offset = CPLD_A_PWR_LED_CTL_BIT_OFFSET; + bit_msk = CPLD_A_PWR_LED_CTL_BIT_MSK; + attr_user_2_cpld = TRUE; + break; + case FAN_LED_CTRL: + reg = CPLD_A_LED_CONTROL_REG_2; + bit_offset = CPLD_A_FAN_LED_CTL_BIT_OFFSET; + bit_msk = CPLD_A_FAN_LED_CTL_BIT_MSK; + attr_user_2_cpld = TRUE; + break; + case LOC_LED_CTRL: + reg = CPLD_D_LED_CONTROL_REG; + bit_offset = CPLD_D_LOC_LED_CTL_BIT_OFFSET; + bit_msk = CPLD_D_LOC_LED_CTL_BIT_MSK; + break; + default: + pega_print(ERR,"incorrect led type\n"); + return -EINVAL; + } + if (kstrtol(buf, 16, &val)) + { + return -EINVAL; + } + if(val >= KWAI_LED_STATUS_NOT_EXIST) + { + return -EINVAL; + } + if(attr_user_2_cpld == TRUE) + val = map_ledstatus_userconfig_2_cpldconfig[val]; + if(val >= CPLD_LED_STATUS_NOT_SUPPORT) + { + return -EINVAL; + } + + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + data = (val << bit_offset) | (data & (~bit_msk)); + + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + pegatron_fn8656_bnf_cpld_write(client->addr, reg, data); + + return count; +} +#endif //LED + +#if PEGA_RD(EEPROM_WRITE_ENABLE) +#define CPLD_A_EEPROM_WRITE_REG 0x1 +#define CPLD_A_EEPROM_WRITE_BIT 1 +static ssize_t show_eeprom_write_enable(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 data = 0, val = 0, reg = CPLD_A_EEPROM_WRITE_REG; + + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, reg, val); + + return sprintf(buf, "0x%02x\n", val); +} + +static ssize_t set_eeprom_write_enable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 data = 0, reg = CPLD_A_EEPROM_WRITE_REG; + long val = 0; + + if (kstrtol(buf, 16, &val)) + { + return -EINVAL; + } + + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + if(val) + SET_BIT(data, CPLD_A_EEPROM_WRITE_BIT); + else + CLEAR_BIT(data, CPLD_A_EEPROM_WRITE_BIT); + + pegatron_fn8656_bnf_cpld_write(client->addr, reg, data); + + return count; +} +#endif //EEPROM_WRITE_ENABLE + +#if PEGA_RD(PSU) +#define CPLD_A_PSU_REG 0x2 + #define CPLD_A_PSU_PWOK_BASE 0 + #define CPLD_A_PSU_PRESENT_BASE 2 + #define CPLD_A_PSU_AC_OK_BASE 4 + +#define PSU_ABSENT 1 +#define PSU_POWER_OK 1 + +#define KWAI_PSU_ABSENT 0 +#define KWAI_PSU_PRESENT_POWER_OK 1 +#define KWAI_PSU_PRESENT_POWER_NOT_OK 2 + +static ssize_t read_psu_status(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); + u8 data = 0, val=0, reg = CPLD_A_PSU_REG; + u8 power_good = 0, ac_good = 0, present = 0; + + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + + GET_BIT(data, (CPLD_A_PSU_PWOK_BASE + attr->index), power_good); + GET_BIT(data, (CPLD_A_PSU_AC_OK_BASE + attr->index), ac_good); + GET_BIT(data, (CPLD_A_PSU_PRESENT_BASE + attr->index), present); + + if(present == PSU_ABSENT) + { + val = KWAI_PSU_ABSENT; + } + else + { + if((ac_good == PSU_POWER_OK)&&(power_good == PSU_POWER_OK)) + { + val = KWAI_PSU_PRESENT_POWER_OK; + } + else + { + val = KWAI_PSU_PRESENT_POWER_NOT_OK; + } + } + + return sprintf(buf, "0x%02x\n", val); +} +#endif //PSU + +#define CPLD_A_INT_REG 0x0B +static ssize_t read_int_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 data = 0, val=0, reg = CPLD_A_INT_REG,fan_fault = 0, psu_fault = 0; + + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + GET_BIT(data, 5, fan_fault); + //3V3 system power alert + GET_BIT(data, 0, psu_fault); + + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x,Fan board MCU:%d,3V3 system power alert:%d\r\n", client->addr, reg, data, fan_fault, psu_fault); + val = fan_fault | (psu_fault << 1); + + return sprintf(buf, "0x%02x\n", val); +} + +#define CPLD_D_RST_REG 0x09 +#define CPLD_D_SYS_RST_BIT 3 //systemp RST bit 0: reset low active, write and clear 1:Normal operation +#define CPLD_D_COLD_RST_FLAG_BIT 4 +#define CPLD_D_WARM_RST_FLAG_BIT 5 +static ssize_t set_sys_rst(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 reg, data = 0; + long val = 0; + + if (kstrtol(buf, 16, &val)) + { + return -EINVAL; + } + + reg = CPLD_D_RST_REG; + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + if(val) + CLEAR_BIT(data, CPLD_D_SYS_RST_BIT); + else + SET_BIT(data, CPLD_D_SYS_RST_BIT); + + pegatron_fn8656_bnf_cpld_write(client->addr, reg, data); + + return count; +} + +static ssize_t show_sys_rst(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 data = 0, val = 0, reg = CPLD_D_RST_REG; + + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, CPLD_D_SYS_RST_BIT, val); + + return sprintf(buf, "0x%02x\n", val); +} + +#define CPLD_D_WDT_REG 0x12 +#define CPLD_D_WATCHDOG_TMO_BIT 6 +extern int pega_mcu_read(unsigned short addr, u8 reg); +extern int pega_mcu_write(unsigned short addr, u8 reg, u8 val); +#define MCU_MB_ADDRESS 0x70 +static ssize_t show_reboot_cause(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 data = 0, watchdog = 0, reg = CPLD_D_WDT_REG; + u8 reboot_cause = 0; + u8 cold_rst = 0, warm_rst = 0, pwr_down = 0; + + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, CPLD_D_WATCHDOG_TMO_BIT, watchdog); + reg = CPLD_D_RST_REG; + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, CPLD_D_COLD_RST_FLAG_BIT, cold_rst); + GET_BIT(data, CPLD_D_WARM_RST_FLAG_BIT, warm_rst); +#if 0//power down reason is from mcu + pega_mcu_write(MCU_MB_ADDRESS, 0xB0, 0x0); + pega_mcu_write(MCU_MB_ADDRESS, 0xB1, 0xa0); + pwr_down = ((pega_mcu_read(MCU_MB_ADDRESS, 0xB2) == 0xff)?0:1); +#endif + + reboot_cause = pwr_down | (watchdog << 1) | (cold_rst << 2) | (warm_rst << 3); + return sprintf(buf, "0x%02x\n", reboot_cause); +} + +#if PEGA_RD(DSFP) + +#define DSFP_PRESENT_ADDRESS 0x21 +#define DSFP_RESET_ADDRESS_BASE 0X25 +#define DSFP_IRQ_STATUS_ADDRESS 0x2C +#define DSFP_LOW_POWER_ADDRESS 0x30 + +#define GET_DSFP_STATUS_ADDRESS(idx, reg) \ + do{\ + if(idx >=0 && idx < 24)\ + {\ + reg = DSFP_PRESENT_ADDRESS + ((idx % 24) / 8);\ + }\ + else if(idx >=24 && idx < 28)\ + {\ + reg = 0x24;\ + }\ + else if(idx >=28 && idx < 36)\ + {\ + reg = 0x21;\ + }\ + else if (idx >= 36 && idx < 44)\ + {\ + reg = 0x22;\ + }\ + else if(idx >=44 && idx <48)\ + {\ + reg = 0x23;\ + }\ + } while(0) + +#define GET_DSFP_RST_ADDRESS(idx, reg) \ + reg = (DSFP_RESET_ADDRESS_BASE + ((idx % 28) / 4)); + +#define GET_DSFP_LOWPOWER_ADDRESS(idx, reg) \ + reg = DSFP_LOW_POWER_ADDRESS + ((idx % 28) / 8) + +#define GET_DSFP_IRQ_STATUS_ADDRESS(idx, reg) \ + reg = DSFP_IRQ_STATUS_ADDRESS + ((idx % 28) / 8) + + +static ssize_t get_dsfp_present(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); + u8 data = 0, val = 0, reg; + + GET_DSFP_STATUS_ADDRESS(attr->index, reg); + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "attr->index:%d,addr: 0x%x, reg: %x, data: %x\r\n",attr->index, client->addr, reg, data); + + if(requirement_flag & KS_REQUIREMENT) + { + data = ~data; + } + if(attr->index >= 28) + GET_BIT(data, (attr->index - 4) % 8, val); + else + GET_BIT(data, attr->index % 8, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t get_dsfp_irq_status(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); + u8 data = 0, val = 0, reg; + + GET_DSFP_IRQ_STATUS_ADDRESS(attr->index, reg); + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, attr->index % 8, val); + + if(requirement_flag & KS_REQUIREMENT) + { + GET_BIT(~data, attr->index % 8, val); + } + return sprintf(buf, "%d\n", val); +} + +static ssize_t set_dsfp_power_on(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t get_dsfp_power_on(struct device *dev, struct device_attribute *da, + char *buf) +{ + u8 val = 1; + return sprintf(buf, "%d\n", val); +} + +/*DSFP CPLD 0:reset 1:not reset. KWAI 0:not reset 1:reset*/ +static ssize_t get_dsfp_reset(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); + u8 reg, data =0; + + GET_DSFP_RST_ADDRESS(attr->index, reg); + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + data = (data >> ((attr->index % 4)*2)) & 0x3; + + return sprintf(buf, "%d\n", !data); +} + +static ssize_t set_dsfp_reset(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); + u8 reg, data = 0; + long val = 0; + + if (kstrtol(buf, 16, &val)) + { + return -EINVAL; + } + + GET_DSFP_RST_ADDRESS(attr->index, reg); + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + CLEAR_BIT(data, (attr->index % 4)*2); + CLEAR_BIT(data, ((attr->index % 4)*2+1)); + data |= ((!val) & 0x3) << ((attr->index % 4)*2); + + pegatron_fn8656_bnf_cpld_write(client->addr, reg, data); + + return count; +} + +/*DSFP CPLD 0:Low 1:High. KWAI 0:High 1:Low*/ +static ssize_t get_dsfp_lowpower(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); + u8 data = 0, val = 0, reg; + u32 port_bit = 0; + if((attr->index >= 44 && attr->index < 48) || (attr->index >= 24 && attr->index < 28)) + port_bit = attr->index % 4; + else + port_bit = attr->index % 8; + + GET_DSFP_LOWPOWER_ADDRESS(attr->index, reg); + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, port_bit, val); + return sprintf(buf, "0x%02x\n", !val);//convert val for Kwai requirement +} + +static ssize_t set_dsfp_lowpower(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); + u8 data = 0, reg; + long val = 0; + u32 port_bit = 0; + if((attr->index >= 44 && attr->index < 48) || (attr->index >= 24 && attr->index < 28)) + port_bit = attr->index % 4; + else + port_bit = attr->index % 8; + + if (kstrtol(buf, 16, &val)) + { + return -EINVAL; + } + GET_DSFP_LOWPOWER_ADDRESS(attr->index, reg); + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + + if(val)//convert val for Kwai requirement + CLEAR_BIT(data, port_bit); + else + SET_BIT(data, port_bit); + + pegatron_fn8656_bnf_cpld_write(client->addr, reg, data); + + return count; +} +#endif //DSFP + + +#if PEGA_RD(QSFP) + +#define QSFP_PRESENT_ADDRESS 0x24 +#define QSFP_RESET_ADDRESS_BASE 0X2A +#define QSFP_IRQ_STATUS_ADDRESS 0x2F +#define QSFP_LOW_POWER_ADDRESS 0x33 + + +#define GET_QSFP_STATUS_ADDRESS(idx, reg) \ + reg = QSFP_PRESENT_ADDRESS + +#define GET_QSFP_RST_ADDRESS(idx, reg) \ + reg = QSFP_RESET_ADDRESS_BASE + ((idx % QSFP_FIRST_PORT) / 4) + +#define GET_QSFP_LOWPOWER_ADDRESS(idx, reg) \ + reg = QSFP_LOW_POWER_ADDRESS + +#define GET_QSFP_IRQ_STATUS_ADDRESS(idx, reg) \ + reg = QSFP_IRQ_STATUS_ADDRESS + +static ssize_t get_qsfp_present(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); + u8 data = 0, val = 0, reg; + + GET_QSFP_STATUS_ADDRESS(attr->index, reg); + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, attr->index % 8, val); + + if(requirement_flag & KS_REQUIREMENT) + { + GET_BIT(~data, attr->index % 8, val); + } + return sprintf(buf, "%d\n", val); +} +static ssize_t get_qsfp_irq_status(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); + u8 data = 0, val = 0, reg; + + GET_QSFP_IRQ_STATUS_ADDRESS(attr->index, reg); + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, attr->index % 8, val); + + if(requirement_flag & KS_REQUIREMENT) + { + GET_BIT(~data, attr->index % 8, val); + } + return sprintf(buf, "%d\n", val); +} +static ssize_t get_qsfp_power_on(struct device *dev, struct device_attribute *da, + char *buf) +{ + u8 val = 1; + return sprintf(buf, "%d\n", val); +} + +/*QSFP CPLD 0:reset 1:not reset. KWAI 0:not reset 1:reset*/ +static ssize_t get_qsfp_reset(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); + u8 reg, data =0; + + GET_QSFP_RST_ADDRESS(attr->index, reg); + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + data = (data >> ((attr->index % 4)*2)) & 0x3; + + return sprintf(buf, "%d\n", !data); +} + +static ssize_t set_qsfp_reset(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); + u8 reg, data = 0; + long val = 0; + + if (kstrtol(buf, 16, &val)) + { + return -EINVAL; + } + + GET_QSFP_RST_ADDRESS(attr->index, reg); + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + CLEAR_BIT(data, (attr->index % 4)*2); + CLEAR_BIT(data, ((attr->index % 4)*2+1)); + data |= ((!val) & 0x3) << ((attr->index % 4)*2); + + pegatron_fn8656_bnf_cpld_write(client->addr, reg, data); + + return count; +} + +/*QSFP-DD CPLD 0:High 1:Low. KWAI 0:High 1:Low*/ +static ssize_t get_qsfp_lowpower(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); + u8 data = 0, val = 0, reg; + + GET_QSFP_LOWPOWER_ADDRESS(attr->index, reg); + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, (attr->index % 8), val); + return sprintf(buf, "0x%02x\n", val); +} + +static ssize_t set_qsfp_lowpower(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); + u8 data = 0, reg; + long val = 0; + + if (kstrtol(buf, 16, &val)) + { + return -EINVAL; + } + GET_QSFP_LOWPOWER_ADDRESS(attr->index, reg); + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + if(val) + SET_BIT(data, (attr->index % 8)); + else + CLEAR_BIT(data, (attr->index % 8)); + + pegatron_fn8656_bnf_cpld_write(client->addr, reg, data); + + return count; +} +#endif //QSFP + +#define MAX_SFP_NUM_BIT_MASK 0xffffffffffffffUL //56 sfp + +static ssize_t get_all_port_sfp_present(struct device *dev, struct device_attribute *da, + char *buf) +{ + u8 data1 = 0, data2 = 0,i = 0; + ssize_t present_bit_map = 0 , tmp = 0; + for (i = 0 ; i < 4;i++) + { + data1 = pegatron_fn8656_bnf_cpld_read(CPLDB_ADDRESS, DSFP_PRESENT_ADDRESS + i); + data2 = pegatron_fn8656_bnf_cpld_read(CPLDC_ADDRESS, DSFP_PRESENT_ADDRESS + i); + present_bit_map |= (data1 & 0xffUL) << i*8 | (data2 & 0xffUL) << (32 + i*8); + } + + /*8 registers,only 56bits in use, bit29-32 & bit53-56 reserved */ + tmp = (present_bit_map & 0xfffffffUL) | ((present_bit_map >> 4) & ~0xfffffffUL); + present_bit_map = (tmp & 0xffffffffffffUL) | ((tmp >> 4) & ~0xffffffffffffUL); + if(requirement_flag & KS_REQUIREMENT) + { + present_bit_map = (~present_bit_map) & MAX_SFP_NUM_BIT_MASK; + } + + pega_print(DEBUG, "present_bit_map:0x%lx\r\n", present_bit_map); + + return sprintf(buf, "0x%lx\n", present_bit_map); +} + +static ssize_t get_all_port_sfp_power_on(struct device *dev, struct device_attribute *da, + char *buf) +{ + size_t poweron_bit_map = MAX_SFP_NUM_BIT_MASK; + pega_print(DEBUG, "poweron_bit_map:%lx\r\n", poweron_bit_map); + return sprintf(buf, "0x%lx\n", poweron_bit_map); +} + +static ssize_t get_cpld_alias(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + ssize_t ret_len = 0; + + switch(client->addr) + { + case CPLDA_ADDRESS: + ret_len = sprintf(buf, "CPLD-A"); + break; + case CPLDB_ADDRESS: + ret_len = sprintf(buf, "CPLD-B"); + break; + case CPLDC_ADDRESS: + ret_len = sprintf(buf, "CPLD-C"); + break; + case CPLDD_ADDRESS: + ret_len = sprintf(buf, "CPLD-D"); + break; + default: + pega_print(WARNING, "Error address %x\n", client->addr); + break; + } + + return ret_len; +} + +static ssize_t get_cpld_type(struct device *dev, struct device_attribute *da, + char *buf) +{ + return sprintf(buf, "LCMXO2-1200HC\n"); +} +static ssize_t get_cpld_board_version(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 data = 0, reg = CPLD_VERSION_REG; + + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + + return sprintf(buf, "0x%02x\n", (data & CPLD_HW_VERSION_BIT_MSK) >> CPLD_HW_VERSION_BIT_OFFSET); +} +static ssize_t get_cpld_num(struct device *dev, struct device_attribute *da, + char *buf) +{ + return sprintf(buf, "%d\n",CPLD_MAX_NUM); +} +static ssize_t get_sfp_num(struct device *dev, struct device_attribute *da, + char *buf) +{ + return sprintf(buf, "%d\n",SFP_PORT_MAX_NUM); +} +static ssize_t get_cpld_debug(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int count = 0; + + switch (attr->index) { + case 0: + count = sprintf(buf, "%s\n", sysled_debug); + break; + case 1: + count = sprintf(buf, "%s\n", sfp_debug); + break; + case 2: + count = sprintf(buf, "%s\n", cpld_debug); + break; + default: + pega_print(WARNING,"no debug info\n"); + } + return count; +} + +static ssize_t set_cpld_debug(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int debug_len = (count < MAX_DEBUG_INFO_LEN) ? count : MAX_DEBUG_INFO_LEN; + + switch (attr->index) { + case 0: + memset(sysled_debug,0,strlen(sysled_debug)); + strncpy(sysled_debug, buf, debug_len); + break; + case 1: + memset(sfp_debug,0,strlen(sysled_debug)); + strncpy(sfp_debug, buf, debug_len); + break; + case 2: + memset(cpld_debug,0,strlen(sysled_debug)); + strncpy(cpld_debug, buf, debug_len); + break; + default: + pega_print(WARNING,"no debug info\n"); + } + return debug_len; +} + +/* +static ssize_t set_sfp_debug(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + strncpy(sysled_debug,buf, (count < MAX_DEBUG_INFO_LEN) ? count : MAX_DEBUG_INFO_LEN); + + return (count < MAX_DEBUG_INFO_LEN) ? count : MAX_DEBUG_INFO_LEN; +} + +static ssize_t set_sysled_debug(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + strncpy(sysled_debug,buf, (count < MAX_DEBUG_INFO_LEN) ? count : MAX_DEBUG_INFO_LEN); + + return (count < MAX_DEBUG_INFO_LEN) ? count : MAX_DEBUG_INFO_LEN; +} +*/ + +static SENSOR_DEVICE_ATTR(cpld_hw_version, S_IRUGO, read_cpld_HWversion, NULL, 0); +static SENSOR_DEVICE_ATTR(cpld_sw_version, S_IRUGO, read_cpld_SWversion, NULL, 0); +static SENSOR_DEVICE_ATTR(cpld_alias, S_IRUGO, get_cpld_alias, NULL, 0); +static SENSOR_DEVICE_ATTR(cpld_type, S_IRUGO, get_cpld_type, NULL, 0); +static SENSOR_DEVICE_ATTR(cpld_board_version, S_IRUGO, get_cpld_board_version, NULL, 0); +static SENSOR_DEVICE_ATTR(cpld_num, S_IRUGO, get_cpld_num, NULL, 0); +static SENSOR_DEVICE_ATTR(sfp_port_num, S_IRUGO, get_sfp_num, NULL, 0); +static SENSOR_DEVICE_ATTR(debug_cpld, S_IRUGO | S_IWUSR, get_cpld_debug, set_cpld_debug, 2); +static SENSOR_DEVICE_ATTR(debug_sfp, S_IRUGO | S_IWUSR, get_cpld_debug, set_cpld_debug, 1); +static SENSOR_DEVICE_ATTR(debug_sysled, S_IRUGO | S_IWUSR, get_cpld_debug, set_cpld_debug, 0); + +static SENSOR_DEVICE_ATTR(cpld_allled_ctrl, S_IRUGO | S_IWUSR, show_led_status, set_led_status, ALL_LED_CTRL); +static SENSOR_DEVICE_ATTR(serial_led_enable, S_IRUGO | S_IWUSR, show_led_status, set_led_status, SERIAL_LED_CTRL); +static SENSOR_DEVICE_ATTR(sys_led, S_IRUGO | S_IWUSR, show_led_status, set_led_status, SYS_LED_CTRL); +static SENSOR_DEVICE_ATTR(pwr_led, S_IRUGO , show_led_status, set_led_status, PWR_LED_CTRL); +static SENSOR_DEVICE_ATTR(fan_led, S_IRUGO , show_led_status, set_led_status, FAN_LED_CTRL); +static SENSOR_DEVICE_ATTR(loc_led, S_IRUGO | S_IWUSR, show_led_status, set_led_status, LOC_LED_CTRL); +static SENSOR_DEVICE_ATTR(sys_rst, S_IRUGO | S_IWUSR, show_sys_rst, set_sys_rst, 0); +static SENSOR_DEVICE_ATTR(reboot_cause, S_IRUGO , show_reboot_cause, NULL, 0); + +static SENSOR_DEVICE_ATTR(eeprom_write_enable, S_IRUGO | S_IWUSR, show_eeprom_write_enable, set_eeprom_write_enable, 0); +static SENSOR_DEVICE_ATTR(psu_1_status, S_IRUGO, read_psu_status, NULL, 0); +static SENSOR_DEVICE_ATTR(psu_2_status, S_IRUGO, read_psu_status, NULL, 1); +static SENSOR_DEVICE_ATTR(int_status, S_IRUGO, read_int_status, NULL, 0); + +static SENSOR_DEVICE_ATTR(sfp_all_present, S_IRUGO, get_all_port_sfp_present, NULL, 0); +static SENSOR_DEVICE_ATTR(sfp_all_power_on, S_IRUGO, get_all_port_sfp_power_on, NULL, 0); + +#define SET_DSFP_ATTR(_num) \ + static SENSOR_DEVICE_ATTR(sfp##_num##_present, S_IRUGO, get_dsfp_present, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(sfp##_num##_reset, S_IRUGO | S_IWUSR, get_dsfp_reset, set_dsfp_reset, _num-1); \ + static SENSOR_DEVICE_ATTR(sfp##_num##_lowpower, S_IRUGO | S_IWUSR, get_dsfp_lowpower, set_dsfp_lowpower, _num-1); \ + static SENSOR_DEVICE_ATTR(sfp##_num##_irq_status, S_IRUGO, get_dsfp_irq_status, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(sfp##_num##_power_on, S_IRUGO | S_IWUSR, get_dsfp_power_on, set_dsfp_power_on,_num-1) + +#define SET_QSFP_ATTR(_num) \ + static SENSOR_DEVICE_ATTR(sfp##_num##_present, S_IRUGO, get_qsfp_present, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(sfp##_num##_reset, S_IRUGO | S_IWUSR, get_qsfp_reset, set_qsfp_reset, _num-1); \ + static SENSOR_DEVICE_ATTR(sfp##_num##_lowpower, S_IRUGO | S_IWUSR, get_qsfp_lowpower, set_qsfp_lowpower, _num-1); \ + static SENSOR_DEVICE_ATTR(sfp##_num##_irq_status, S_IRUGO, get_qsfp_irq_status, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(sfp##_num##_power_on, S_IRUGO | S_IWUSR, get_qsfp_power_on, NULL,_num-1) + +SET_DSFP_ATTR(1);SET_DSFP_ATTR(2);SET_DSFP_ATTR(3);SET_DSFP_ATTR(4);SET_DSFP_ATTR(5);SET_DSFP_ATTR(6);SET_DSFP_ATTR(7);SET_DSFP_ATTR(8);SET_DSFP_ATTR(9); +SET_DSFP_ATTR(10);SET_DSFP_ATTR(11);SET_DSFP_ATTR(12);SET_DSFP_ATTR(13);SET_DSFP_ATTR(14);SET_DSFP_ATTR(15);SET_DSFP_ATTR(16);SET_DSFP_ATTR(17);SET_DSFP_ATTR(18); +SET_DSFP_ATTR(19);SET_DSFP_ATTR(20);SET_DSFP_ATTR(21);SET_DSFP_ATTR(22);SET_DSFP_ATTR(23);SET_DSFP_ATTR(24);SET_DSFP_ATTR(25);SET_DSFP_ATTR(26);SET_DSFP_ATTR(27); +SET_DSFP_ATTR(28);SET_DSFP_ATTR(29);SET_DSFP_ATTR(30);SET_DSFP_ATTR(31);SET_DSFP_ATTR(32);SET_DSFP_ATTR(33);SET_DSFP_ATTR(34);SET_DSFP_ATTR(35);SET_DSFP_ATTR(36); +SET_DSFP_ATTR(37);SET_DSFP_ATTR(38);SET_DSFP_ATTR(39);SET_DSFP_ATTR(40);SET_DSFP_ATTR(41);SET_DSFP_ATTR(42);SET_DSFP_ATTR(43);SET_DSFP_ATTR(44);SET_DSFP_ATTR(45); +SET_DSFP_ATTR(46);SET_DSFP_ATTR(47);SET_DSFP_ATTR(48); +SET_QSFP_ATTR(49);SET_QSFP_ATTR(50);SET_QSFP_ATTR(51);SET_QSFP_ATTR(52);SET_QSFP_ATTR(53);SET_QSFP_ATTR(54);SET_QSFP_ATTR(55);SET_QSFP_ATTR(56); + +static struct attribute *pegatron_fn8656_bnf_cpldA_attributes[] = { + &sensor_dev_attr_cpld_hw_version.dev_attr.attr, + &sensor_dev_attr_cpld_sw_version.dev_attr.attr, + &sensor_dev_attr_cpld_alias.dev_attr.attr, + &sensor_dev_attr_cpld_type.dev_attr.attr, + &sensor_dev_attr_cpld_board_version.dev_attr.attr, + &sensor_dev_attr_cpld_num.dev_attr.attr, + &sensor_dev_attr_debug_cpld.dev_attr.attr, + &sensor_dev_attr_debug_sfp.dev_attr.attr, + &sensor_dev_attr_debug_sysled.dev_attr.attr, + + &sensor_dev_attr_cpld_allled_ctrl.dev_attr.attr, + &sensor_dev_attr_serial_led_enable.dev_attr.attr, + &sensor_dev_attr_sys_led.dev_attr.attr, + &sensor_dev_attr_pwr_led.dev_attr.attr, + &sensor_dev_attr_fan_led.dev_attr.attr, + &sensor_dev_attr_eeprom_write_enable.dev_attr.attr, + + &sensor_dev_attr_psu_1_status.dev_attr.attr, + &sensor_dev_attr_psu_2_status.dev_attr.attr, + &sensor_dev_attr_int_status.dev_attr.attr, + NULL +}; + +static struct attribute *pegatron_fn8656_bnf_cpldB_attributes[] = { + &sensor_dev_attr_cpld_hw_version.dev_attr.attr, + &sensor_dev_attr_cpld_sw_version.dev_attr.attr, + &sensor_dev_attr_cpld_alias.dev_attr.attr, + &sensor_dev_attr_cpld_type.dev_attr.attr, + &sensor_dev_attr_cpld_board_version.dev_attr.attr, + &sensor_dev_attr_sfp_port_num.dev_attr.attr, + + &sensor_dev_attr_serial_led_enable.dev_attr.attr, + + &sensor_dev_attr_sfp_all_present.dev_attr.attr, + &sensor_dev_attr_sfp_all_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp1_present.dev_attr.attr, + &sensor_dev_attr_sfp1_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp1_reset.dev_attr.attr, + &sensor_dev_attr_sfp1_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp1_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp2_present.dev_attr.attr, + &sensor_dev_attr_sfp2_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp2_reset.dev_attr.attr, + &sensor_dev_attr_sfp2_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp2_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp3_present.dev_attr.attr, + &sensor_dev_attr_sfp3_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp3_reset.dev_attr.attr, + &sensor_dev_attr_sfp3_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp3_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp4_present.dev_attr.attr, + &sensor_dev_attr_sfp4_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp4_reset.dev_attr.attr, + &sensor_dev_attr_sfp4_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp4_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp5_present.dev_attr.attr, + &sensor_dev_attr_sfp5_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp5_reset.dev_attr.attr, + &sensor_dev_attr_sfp5_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp5_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp6_present.dev_attr.attr, + &sensor_dev_attr_sfp6_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp6_reset.dev_attr.attr, + &sensor_dev_attr_sfp6_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp6_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp7_present.dev_attr.attr, + &sensor_dev_attr_sfp7_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp7_reset.dev_attr.attr, + &sensor_dev_attr_sfp7_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp7_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp8_present.dev_attr.attr, + &sensor_dev_attr_sfp8_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp8_reset.dev_attr.attr, + &sensor_dev_attr_sfp8_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp8_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp9_present.dev_attr.attr, + &sensor_dev_attr_sfp9_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp9_reset.dev_attr.attr, + &sensor_dev_attr_sfp9_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp9_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp10_present.dev_attr.attr, + &sensor_dev_attr_sfp10_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp10_reset.dev_attr.attr, + &sensor_dev_attr_sfp10_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp10_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp11_present.dev_attr.attr, + &sensor_dev_attr_sfp11_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp11_reset.dev_attr.attr, + &sensor_dev_attr_sfp11_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp11_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp12_present.dev_attr.attr, + &sensor_dev_attr_sfp12_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp12_reset.dev_attr.attr, + &sensor_dev_attr_sfp12_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp12_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp13_present.dev_attr.attr, + &sensor_dev_attr_sfp13_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp13_reset.dev_attr.attr, + &sensor_dev_attr_sfp13_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp13_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp14_present.dev_attr.attr, + &sensor_dev_attr_sfp14_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp14_reset.dev_attr.attr, + &sensor_dev_attr_sfp14_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp14_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp15_present.dev_attr.attr, + &sensor_dev_attr_sfp15_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp15_reset.dev_attr.attr, + &sensor_dev_attr_sfp15_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp15_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp16_present.dev_attr.attr, + &sensor_dev_attr_sfp16_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp16_reset.dev_attr.attr, + &sensor_dev_attr_sfp16_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp16_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp17_present.dev_attr.attr, + &sensor_dev_attr_sfp17_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp17_reset.dev_attr.attr, + &sensor_dev_attr_sfp17_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp17_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp18_present.dev_attr.attr, + &sensor_dev_attr_sfp18_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp18_reset.dev_attr.attr, + &sensor_dev_attr_sfp18_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp18_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp19_present.dev_attr.attr, + &sensor_dev_attr_sfp19_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp19_reset.dev_attr.attr, + &sensor_dev_attr_sfp19_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp19_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp20_present.dev_attr.attr, + &sensor_dev_attr_sfp20_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp20_reset.dev_attr.attr, + &sensor_dev_attr_sfp20_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp20_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp21_present.dev_attr.attr, + &sensor_dev_attr_sfp21_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp21_reset.dev_attr.attr, + &sensor_dev_attr_sfp21_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp21_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp22_present.dev_attr.attr, + &sensor_dev_attr_sfp22_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp22_reset.dev_attr.attr, + &sensor_dev_attr_sfp22_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp22_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp23_present.dev_attr.attr, + &sensor_dev_attr_sfp23_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp23_reset.dev_attr.attr, + &sensor_dev_attr_sfp23_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp23_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp24_present.dev_attr.attr, + &sensor_dev_attr_sfp24_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp24_reset.dev_attr.attr, + &sensor_dev_attr_sfp24_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp24_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp25_present.dev_attr.attr, + &sensor_dev_attr_sfp25_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp25_reset.dev_attr.attr, + &sensor_dev_attr_sfp25_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp25_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp26_present.dev_attr.attr, + &sensor_dev_attr_sfp26_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp26_reset.dev_attr.attr, + &sensor_dev_attr_sfp26_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp26_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp27_present.dev_attr.attr, + &sensor_dev_attr_sfp27_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp27_reset.dev_attr.attr, + &sensor_dev_attr_sfp27_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp27_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp28_present.dev_attr.attr, + &sensor_dev_attr_sfp28_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp28_reset.dev_attr.attr, + &sensor_dev_attr_sfp28_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp28_power_on.dev_attr.attr, + + NULL +}; + +static struct attribute* pegatron_fn8656_bnf_cpldC_attributes[] = { + &sensor_dev_attr_cpld_hw_version.dev_attr.attr, + &sensor_dev_attr_cpld_sw_version.dev_attr.attr, + &sensor_dev_attr_cpld_alias.dev_attr.attr, + &sensor_dev_attr_cpld_type.dev_attr.attr, + &sensor_dev_attr_cpld_board_version.dev_attr.attr, + + &sensor_dev_attr_serial_led_enable.dev_attr.attr, + + &sensor_dev_attr_sfp_all_present.dev_attr.attr, + &sensor_dev_attr_sfp_all_power_on.dev_attr.attr, + + /*dsfp 29-48*/ + &sensor_dev_attr_sfp29_present.dev_attr.attr, + &sensor_dev_attr_sfp29_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp29_reset.dev_attr.attr, + &sensor_dev_attr_sfp29_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp29_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp30_present.dev_attr.attr, + &sensor_dev_attr_sfp30_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp30_reset.dev_attr.attr, + &sensor_dev_attr_sfp30_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp30_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp31_present.dev_attr.attr, + &sensor_dev_attr_sfp31_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp31_reset.dev_attr.attr, + &sensor_dev_attr_sfp31_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp31_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp32_present.dev_attr.attr, + &sensor_dev_attr_sfp32_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp32_reset.dev_attr.attr, + &sensor_dev_attr_sfp32_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp32_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp33_present.dev_attr.attr, + &sensor_dev_attr_sfp33_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp33_reset.dev_attr.attr, + &sensor_dev_attr_sfp33_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp33_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp34_present.dev_attr.attr, + &sensor_dev_attr_sfp34_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp34_reset.dev_attr.attr, + &sensor_dev_attr_sfp34_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp34_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp35_present.dev_attr.attr, + &sensor_dev_attr_sfp35_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp35_reset.dev_attr.attr, + &sensor_dev_attr_sfp35_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp35_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp36_present.dev_attr.attr, + &sensor_dev_attr_sfp36_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp36_reset.dev_attr.attr, + &sensor_dev_attr_sfp36_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp36_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp37_present.dev_attr.attr, + &sensor_dev_attr_sfp37_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp37_reset.dev_attr.attr, + &sensor_dev_attr_sfp37_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp37_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp38_present.dev_attr.attr, + &sensor_dev_attr_sfp38_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp38_reset.dev_attr.attr, + &sensor_dev_attr_sfp38_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp38_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp39_present.dev_attr.attr, + &sensor_dev_attr_sfp39_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp39_reset.dev_attr.attr, + &sensor_dev_attr_sfp39_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp39_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp40_present.dev_attr.attr, + &sensor_dev_attr_sfp40_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp40_reset.dev_attr.attr, + &sensor_dev_attr_sfp40_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp40_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp41_present.dev_attr.attr, + &sensor_dev_attr_sfp41_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp41_reset.dev_attr.attr, + &sensor_dev_attr_sfp41_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp41_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp42_present.dev_attr.attr, + &sensor_dev_attr_sfp42_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp42_reset.dev_attr.attr, + &sensor_dev_attr_sfp42_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp42_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp43_present.dev_attr.attr, + &sensor_dev_attr_sfp43_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp43_reset.dev_attr.attr, + &sensor_dev_attr_sfp43_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp43_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp44_present.dev_attr.attr, + &sensor_dev_attr_sfp44_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp44_reset.dev_attr.attr, + &sensor_dev_attr_sfp44_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp44_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp45_present.dev_attr.attr, + &sensor_dev_attr_sfp45_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp45_reset.dev_attr.attr, + &sensor_dev_attr_sfp45_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp45_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp46_present.dev_attr.attr, + &sensor_dev_attr_sfp46_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp46_reset.dev_attr.attr, + &sensor_dev_attr_sfp46_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp46_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp47_present.dev_attr.attr, + &sensor_dev_attr_sfp47_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp47_reset.dev_attr.attr, + &sensor_dev_attr_sfp47_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp47_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp48_present.dev_attr.attr, + &sensor_dev_attr_sfp48_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp48_reset.dev_attr.attr, + &sensor_dev_attr_sfp48_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp48_power_on.dev_attr.attr, + + /*qsfp 49-56*/ + &sensor_dev_attr_sfp49_present.dev_attr.attr, + &sensor_dev_attr_sfp49_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp49_reset.dev_attr.attr, + &sensor_dev_attr_sfp49_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp49_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp50_present.dev_attr.attr, + &sensor_dev_attr_sfp50_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp50_reset.dev_attr.attr, + &sensor_dev_attr_sfp50_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp50_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp51_present.dev_attr.attr, + &sensor_dev_attr_sfp51_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp51_reset.dev_attr.attr, + &sensor_dev_attr_sfp51_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp51_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp52_present.dev_attr.attr, + &sensor_dev_attr_sfp52_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp52_reset.dev_attr.attr, + &sensor_dev_attr_sfp52_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp52_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp53_present.dev_attr.attr, + &sensor_dev_attr_sfp53_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp53_reset.dev_attr.attr, + &sensor_dev_attr_sfp53_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp53_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp54_present.dev_attr.attr, + &sensor_dev_attr_sfp54_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp54_reset.dev_attr.attr, + &sensor_dev_attr_sfp54_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp54_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp55_present.dev_attr.attr, + &sensor_dev_attr_sfp55_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp55_reset.dev_attr.attr, + &sensor_dev_attr_sfp55_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp55_power_on.dev_attr.attr, + + &sensor_dev_attr_sfp56_present.dev_attr.attr, + &sensor_dev_attr_sfp56_lowpower.dev_attr.attr, + &sensor_dev_attr_sfp56_reset.dev_attr.attr, + &sensor_dev_attr_sfp56_irq_status.dev_attr.attr, + &sensor_dev_attr_sfp56_power_on.dev_attr.attr, + NULL +}; + +static struct attribute *pegatron_fn8656_bnf_cpldD_attributes[] = { + &sensor_dev_attr_cpld_hw_version.dev_attr.attr, + &sensor_dev_attr_cpld_sw_version.dev_attr.attr, + &sensor_dev_attr_loc_led.dev_attr.attr, + &sensor_dev_attr_cpld_alias.dev_attr.attr, + &sensor_dev_attr_cpld_type.dev_attr.attr, + &sensor_dev_attr_cpld_board_version.dev_attr.attr, + &sensor_dev_attr_sys_rst.dev_attr.attr, + &sensor_dev_attr_reboot_cause.dev_attr.attr, + + NULL +}; + +static ssize_t get_lm75b_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + u16 data = 0; + u8 reg = LM75B_TEMP_REG; + + data = pegatron_fn8656_bnf_cpld_read(client->addr, reg); + pega_print(DEBUG,"%s - addr: 0x%x, reg: %x, data: %x\r\n", __func__, client->addr, reg, data); + sprintf(buf, "%d\n", data); + return strlen(buf); + +} + +static SENSOR_DEVICE_ATTR(lm75b_temp, S_IRUGO, get_lm75b_temp, NULL, 0); + +static struct attribute *pegatron_fn8656_bnf_lm75b_attributes[] = { + &sensor_dev_attr_lm75b_temp.dev_attr.attr, + NULL +}; + +static const struct attribute_group pegatron_fn8656_bnf_cpldA_group = { .attrs = pegatron_fn8656_bnf_cpldA_attributes}; +static const struct attribute_group pegatron_fn8656_bnf_cpldB_group = { .attrs = pegatron_fn8656_bnf_cpldB_attributes}; +static const struct attribute_group pegatron_fn8656_bnf_cpldC_group = { .attrs = pegatron_fn8656_bnf_cpldC_attributes}; +static const struct attribute_group pegatron_fn8656_bnf_cpldD_group = { .attrs = pegatron_fn8656_bnf_cpldD_attributes}; +static const struct attribute_group pegatron_fn8656_bnf_lm75b_group = { .attrs = pegatron_fn8656_bnf_lm75b_attributes}; + +static void pegatron_fn8656_bnf_cpld_add_client(struct i2c_client *client) +{ + struct cpld_client_node *node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL); + + if (!node) { + pega_print(ERR, "Can't allocate cpld_client_node (0x%x)\n", client->addr); + return; + } + + node->client = client; + + mutex_lock(&list_lock); + list_add(&node->list, &cpld_client_list); + mutex_unlock(&list_lock); +} + +static void pegatron_fn8656_bnf_cpld_remove_client(struct i2c_client *client) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int found = 0; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client == client) { + found = 1; + break; + } + } + + if (found) { + list_del(list_node); + kfree(cpld_node); + } + + mutex_unlock(&list_lock); +} + +static int pegatron_fn8656_bnf_cpld_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + pega_print(ERR, "i2c_check_functionality failed (0x%x)\n", client->addr); + status = -EIO; + goto exit; + } + + /* Register sysfs hooks */ + switch(client->addr) + { + case CPLDA_ADDRESS: + status = sysfs_create_group(&client->dev.kobj, &pegatron_fn8656_bnf_cpldA_group); + break; + case CPLDB_ADDRESS: + status = sysfs_create_group(&client->dev.kobj, &pegatron_fn8656_bnf_cpldB_group); + break; + case CPLDC_ADDRESS: + status = sysfs_create_group(&client->dev.kobj, &pegatron_fn8656_bnf_cpldC_group); + break; + case CPLDD_ADDRESS: + status = sysfs_create_group(&client->dev.kobj, &pegatron_fn8656_bnf_cpldD_group); + break; + case LM75B_ADDRESS: + status = sysfs_create_group(&client->dev.kobj, &pegatron_fn8656_bnf_lm75b_group); + break; + default: + pega_print(WARNING, "i2c_check_CPLD failed (0x%x)\n", client->addr); + status = -EIO; + goto exit; + break; + } + + if (status) { + goto exit; + } + + pega_print(INFO, "chip found\n"); + pegatron_fn8656_bnf_cpld_add_client(client); + + return 0; + +exit: + return status; +} + +static int pegatron_fn8656_bnf_cpld_remove(struct i2c_client *client) +{ + switch(client->addr) + { + case CPLDA_ADDRESS: + sysfs_remove_group(&client->dev.kobj, &pegatron_fn8656_bnf_cpldA_group); + break; + case CPLDB_ADDRESS: + sysfs_remove_group(&client->dev.kobj, &pegatron_fn8656_bnf_cpldB_group); + break; + case CPLDC_ADDRESS: + sysfs_remove_group(&client->dev.kobj, &pegatron_fn8656_bnf_cpldC_group); + break; + case CPLDD_ADDRESS: + sysfs_remove_group(&client->dev.kobj, &pegatron_fn8656_bnf_cpldD_group); + break; + case LM75B_ADDRESS: + sysfs_remove_group(&client->dev.kobj, &pegatron_fn8656_bnf_lm75b_group); + break; + default: + pega_print(WARNING, "i2c_remove_CPLD failed (0x%x)\n", client->addr); + break; + } + + + pegatron_fn8656_bnf_cpld_remove_client(client); + return 0; +} + +static const struct i2c_device_id pegatron_fn8656_bnf_cpld_id[] = { + { "fn8656_bnf_cpld", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, pegatron_fn8656_bnf_cpld_id); + +static struct i2c_driver pegatron_fn8656_bnf_cpld_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "pegatron_fn8656_bnf_cpld", + }, + .probe = pegatron_fn8656_bnf_cpld_probe, + .remove = pegatron_fn8656_bnf_cpld_remove, + .id_table = pegatron_fn8656_bnf_cpld_id, + .address_list = normal_i2c, +}; + +static int __init pegatron_fn8656_bnf_cpld_init(void) +{ + mutex_init(&list_lock); + + return i2c_add_driver(&pegatron_fn8656_bnf_cpld_driver); +} + +static void __exit pegatron_fn8656_bnf_cpld_exit(void) +{ + i2c_del_driver(&pegatron_fn8656_bnf_cpld_driver); +} + +module_param(loglevel,uint,0644); +module_param(requirement_flag,uint,0664); + +MODULE_PARM_DESC(loglevel,"0x01-LOG_ERR,0x02-LOG_WARNING,0x04-LOG_INFO,0x08-LOG_DEBUG"); +MODULE_PARM_DESC(requirement_flag,"custom requirement.eg:requirement_flag=1 for KS"); + +MODULE_AUTHOR("Peter5 Lin "); +MODULE_DESCRIPTION("pegatron_fn8656_bnf_cpld driver"); +MODULE_LICENSE("GPL"); + +module_init(pegatron_fn8656_bnf_cpld_init); +module_exit(pegatron_fn8656_bnf_cpld_exit); diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/pegatron_fn8656_bnf_psu.c b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/pegatron_fn8656_bnf_psu.c new file mode 100644 index 000000000000..b15ce5e8e98f --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/pegatron_fn8656_bnf_psu.c @@ -0,0 +1,919 @@ +/* + * 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 "pegatron_pub.h" + +#undef PEGA_DEBUG +/*#define pega_DEBUG*/ +#ifdef PEGA_DEBUG +#define DBG(x) x +#else +#define DBG(x) +#endif /* DEBUG */ + +#define PSU_EEPROM_FRU_50_ADDRESS 0x50 +#define PSU_EEPROM_FRU_51_ADDRESS 0x51 +#define PSU_58_ADDRESS 0x58 +#define PSU_59_ADDRESS 0x59 +#define PSU_VOUT_MODE_REG 0x20 +#define PSU_VOUT_STATUS_REG 0x7A +#define PSU_IOUT_STATUS_REG 0x7B +#define PSU_INPUT_STATUS_REG 0x7C +#define PSU_TEMP_STATUS_REG 0x7D +#define PSU_FANS_1_2_STATUS 0x81 + + +#define PSU_VOUT_OVER_VOLTAGE_BIT 7 +#define PSU_IOUT_OVER_CURRENT_FAULT_BIT 7 +#define PSU_IOUT_OVER_CURRENT_WARNING_BIT 5 +#define PSU_IPUT_OVER_CURRENT_WARNING_BIT 1 +#define PSU_IPUT_INSUFFICIENT_BIT 3 +#define PSU_TEMP_OVER_TEMP_FAULT_BIT 7 +#define PSU_TEMP_OVER_TEMP_WARNING_BIT 6 + + +#define PM_BUS_BLOCK_MAX_LEN 32 + +enum _sysfs_attributes { + PSU_V_IN = 0, + PSU_I_IN, + PSU_V_OUT, + PSU_I_OUT, + PSU_TEMP1_INPUT, + PSU_TEMP2_INPUT, + PSU_TEMP3_INPUT, + PSU_FAN1_SPEED, + + PSU_P_OUT, + PSU_P_IN, + PSU_PMBUS_REVISION, + PSU_MFR_ID, + PSU_MFR_MODEL, + PSU_MFR_REVISION, + PSU_MFR_LOCATION, + PSU_MFR_DATE, + PSU_MFR_SERIAL, + + PSU_VIN_MIN, + PSU_VIN_MAX, + PSU_IIN_MAX, + PSU_PIN_MAX, + PSU_VOUT_MIN, + PSU_VOUT_MAX, + PSU_IOUT_MAX, + PSU_POUT_MAX, + + //PSU_FAN1_FAULT, + //PSU_FAN1_DUTY_CYCLE, + PSU_MFR_TEMP1_MAX, + PSU_MFR_TEMP2_MAX, + PSU_MFR_TEMP3_MAX, + + PSU_ATTR_END +}; +enum _psu_pmbus_reg{ + PSU_VIN_REG = 0x88, + PSU_IIN_REG, + + PSU_VOUT_REG = 0x8B, + PSU_IOUT_REG, + PSU_TEMP1_REG, + PSU_TEMP2_REG, + PSU_TEMP3_REG, + PSU_FAN_SPEED_1_REG, + + PSU_POUT_REG = 0x96, + PSU_PIN_REG, + PSU_PMBUS_REVISION_REG, + PSU_MFR_ID_REG, + PSU_MFR_MODEL_REG, + PSU_MFR_REVISION_REG, + PSU_MFR_LOCATION_REG, + PSU_MFR_DATE_REG, + PSU_MFR_SERIAL_REG, + + PSU_VIN_MIN_REG = 0xA0, + PSU_VIN_MAX_REG, + PSU_IIN_MAX_REG, + PSU_PIN_MAX_REG, + PSU_VOUT_MIN_REG, + PSU_VOUT_MAX_REG, + PSU_IOUT_MAX_REG, + PSU_POUT_MAX_REG, + + PSU_MFR_TEMP1_MAX_REG = 0xC0, + PSU_MFR_TEMP2_MAX_REG, + PSU_MFR_TEMP3_MAX_REG, + +}; +u8 psu_pmbus_reg_map[PSU_ATTR_END] = { + PSU_VIN_REG,PSU_IIN_REG,PSU_VOUT_REG,PSU_IOUT_REG,PSU_TEMP1_REG,PSU_TEMP2_REG,PSU_TEMP3_REG,PSU_FAN_SPEED_1_REG, + PSU_POUT_REG,PSU_PIN_REG,PSU_PMBUS_REVISION_REG,PSU_MFR_ID_REG, PSU_MFR_MODEL_REG,PSU_MFR_REVISION_REG , PSU_MFR_LOCATION_REG,PSU_MFR_DATE_REG ,PSU_MFR_SERIAL_REG, + PSU_VIN_MIN_REG,PSU_VIN_MAX_REG,PSU_IIN_MAX_REG,PSU_PIN_MAX_REG,PSU_VOUT_MIN_REG,PSU_VOUT_MAX_REG,PSU_IOUT_MAX_REG,PSU_POUT_MAX_REG, + PSU_MFR_TEMP1_MAX_REG,PSU_MFR_TEMP2_MAX_REG,PSU_MFR_TEMP3_MAX_REG +}; + +enum power_type { + POWER_AC = 0, + POWER_DC = 1 +}; + +#define GET_BIT(data, bit, value) value = (data >> bit) & 0x1 +#define SET_BIT(data, bit) data |= (1 << bit) +#define CLEAR_BIT(data, bit) data &= ~(1 << bit) + +struct psu_client_node { + struct i2c_client *client; + struct list_head list; +}; + +#define MAX_PSU_NUMS 2 +#define MAX_PSU_TEMP_SENSORS 3 +struct fn8656_bnf_psu_data { + struct i2c_client *client; + uint total_psu_num; + uint temp_sensor_num; + enum power_type type; + + int temp_max[MAX_PSU_TEMP_SENSORS]; + int temp_min[MAX_PSU_TEMP_SENSORS]; + int temp_crit[MAX_PSU_TEMP_SENSORS]; +}; +int psu_temp_max[MAX_PSU_TEMP_SENSORS] = {80, 90, 105}; +int psu_temp_min[MAX_PSU_TEMP_SENSORS] = {-2, 7, 14}; +int psu_temp_crit[MAX_PSU_TEMP_SENSORS] = {85, 95, 110}; +static const unsigned short normal_i2c[] = { PSU_EEPROM_FRU_50_ADDRESS,PSU_EEPROM_FRU_51_ADDRESS,PSU_58_ADDRESS, PSU_59_ADDRESS, I2C_CLIENT_END }; +static LIST_HEAD(psu_client_list); +static struct mutex list_lock; + +static uint loglevel = LOG_INFO | LOG_WARNING | LOG_ERR; +static char debug[MAX_DEBUG_INFO_LEN] = "debug info: \n\ +echo 8 > */loglevel --- log driver's debug details \n"; + +static int pega_fn8656_bnf_psu_read(unsigned short addr, u8 reg) +{ + struct list_head *list_node = NULL; + struct psu_client_node *psu_node = NULL; + int data = -EPERM; + + mutex_lock(&list_lock); + + list_for_each(list_node, &psu_client_list) + { + psu_node = list_entry(list_node, struct psu_client_node, list); + + if (psu_node->client->addr == addr) { + data = i2c_smbus_read_byte_data(psu_node->client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: 0x%x, data: 0x%x\r\n", addr, reg, data); + break; + } + } + + mutex_unlock(&list_lock); + + return data; +} + +static int pega_fn8656_bnf_psu_read_word(unsigned short addr, u8 reg) +{ + struct list_head *list_node = NULL; + struct psu_client_node *psu_node = NULL; + int data = -EPERM; + + mutex_lock(&list_lock); + + list_for_each(list_node, &psu_client_list) + { + psu_node = list_entry(list_node, struct psu_client_node, list); + + if (psu_node->client->addr == addr) { + data = i2c_smbus_read_word_data(psu_node->client, reg); + pega_print(DEBUG, "addr: 0x%x, reg: 0x%x, data: 0x%x\r\n", addr, reg, data); + break; + } + } + + mutex_unlock(&list_lock); + + return data; +} + +static int pega_fn8656_bnf_psu_read_block(unsigned short addr, u8 reg,u8* buf) +{ + struct list_head *list_node = NULL; + struct psu_client_node *psu_node = NULL; + int ret_val = -EPERM,i = 0; + + mutex_lock(&list_lock); + + list_for_each(list_node, &psu_client_list) + { + psu_node = list_entry(list_node, struct psu_client_node, list); + + if (psu_node->client->addr == addr) { + ret_val = i2c_smbus_read_block_data(psu_node->client, reg, buf); + pega_print(DEBUG, "addr: 0x%x, reg: 0x%x, length:%d buf: ", addr, reg,ret_val); + for(i = 0;i < ret_val; i++){ + pega_print(DEBUG,"0x%x ",buf[i]); + } + break; + } + } + + mutex_unlock(&list_lock); + + return ret_val; +} + +static ssize_t read_psu_alarm(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 vout_data = 0, temp_data = 0,fan_data = 0,val = 0; + + vout_data = pega_fn8656_bnf_psu_read(client->addr, PSU_VOUT_STATUS_REG); + temp_data = pega_fn8656_bnf_psu_read(client->addr, PSU_TEMP_STATUS_REG); + fan_data = pega_fn8656_bnf_psu_read(client->addr, PSU_FANS_1_2_STATUS); + pega_print(DEBUG, "vout_data:0x%x,temp_data:0x%x,fan_data:0x%x\r\n", vout_data,temp_data,fan_data); + + //BIT0: Normal, Bit0: temprature status, BIT1:fan status, BIT2:vol status + if(temp_data & 0xff) + SET_BIT(val, 0); + if(fan_data & 0xff) + SET_BIT(val, 1); + if(vout_data & 0xff) + SET_BIT(val, 2); + + return sprintf(buf, "0x%02x\n", val); +} +static ssize_t get_psu_led_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 vout_data = 0, temp_data = 0,fan_data = 0,led_status = 0; + + vout_data = pega_fn8656_bnf_psu_read(client->addr, PSU_VOUT_STATUS_REG); + temp_data = pega_fn8656_bnf_psu_read(client->addr, PSU_TEMP_STATUS_REG); + fan_data = pega_fn8656_bnf_psu_read(client->addr, PSU_FANS_1_2_STATUS); + pega_print(DEBUG, "vout_data:0x%x,temp_data:0x%x,fan_data:0x%x\r\n", vout_data,temp_data,fan_data); + + //Kwai's led color definition:1 green,3 red + if(temp_data & 0xff || fan_data & 0xff || vout_data & 0xff) + { + led_status = 3; + } + else + { + led_status = 1; + } + + return sprintf(buf, "0x%02x\n", led_status); +} + + +static ssize_t read_psu_vout_over_voltage(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, reg = PSU_VOUT_STATUS_REG, val = 0; + + data = pega_fn8656_bnf_psu_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, PSU_VOUT_OVER_VOLTAGE_BIT, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t read_psu_iout_over_current_fault(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, reg = PSU_IOUT_STATUS_REG, val = 0; + + data = pega_fn8656_bnf_psu_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, PSU_IOUT_OVER_CURRENT_FAULT_BIT, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t read_psu_iout_over_current_warning(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, reg = PSU_IOUT_STATUS_REG, val = 0; + + data = pega_fn8656_bnf_psu_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, PSU_IOUT_OVER_CURRENT_WARNING_BIT, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t read_psu_iput_over_current_warning(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, reg = PSU_INPUT_STATUS_REG, val = 0; + + data = pega_fn8656_bnf_psu_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, PSU_IPUT_OVER_CURRENT_WARNING_BIT, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t read_psu_iput_insufficient(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, reg = PSU_INPUT_STATUS_REG, val = 0; + + data = pega_fn8656_bnf_psu_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, PSU_IPUT_INSUFFICIENT_BIT, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t read_psu_temp_over_temp_fault(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, reg = PSU_TEMP_STATUS_REG, val = 0; + + data = pega_fn8656_bnf_psu_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, PSU_TEMP_OVER_TEMP_FAULT_BIT, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t read_psu_temp_over_temp_warning(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data = 0, reg = PSU_TEMP_STATUS_REG, val = 0; + + data = pega_fn8656_bnf_psu_read(client->addr, reg); + pega_print(DEBUG, "addr: 0x%x, reg: %x, data: %x\r\n", client->addr, reg, data); + GET_BIT(data, PSU_TEMP_OVER_TEMP_WARNING_BIT, val); + + return sprintf(buf, "%d\n", val); +} + +static int two_complement_to_int(u16 data, u8 valid_bit, int mask) +{ + u16 valid_data = data & mask; + bool is_negative = valid_data >> (valid_bit - 1); + + return is_negative ? (-(((~valid_data) & mask) + 1)) : valid_data; +} + +static ssize_t show_linear(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u16 read_value = 0; + int exponent, mantissa; + int multiplier = 1000; + int ret_val; + + read_value = pega_fn8656_bnf_psu_read_word(client->addr, psu_pmbus_reg_map[attr->index]); + exponent = two_complement_to_int(read_value >> 11, 5, 0x1f); + mantissa = two_complement_to_int(read_value & 0x7ff, 11, 0x7ff); + + pega_print(DEBUG,"addr:0x%x, reg:0x%x, read_value:0x%x,exponent:%d,mantissa:%d\n",client->addr,psu_pmbus_reg_map[attr->index],read_value,exponent,mantissa); + if(exponent >= 0) + ret_val = (mantissa << exponent) * multiplier; + else + ret_val = (mantissa * multiplier) / (1 << -exponent); + + if(PSU_FAN1_SPEED == attr->index) + return sprintf(buf, "%d\n", ret_val/1000); + else if(PSU_POUT_MAX == attr->index) + ret_val = ret_val * 105 / 100; //the power_out_max can have a 5%tolenrence + return sprintf(buf, "%d.%d\n", ret_val/1000,ret_val%1000); +} + +static ssize_t read_psu_vol_out(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + int data = 0, ret_val = 0; + int exponent, mantissa; + int multiplier = 1000; + u16 vout_mode; + + vout_mode = pega_fn8656_bnf_psu_read(client->addr, PSU_VOUT_MODE_REG); + data = pega_fn8656_bnf_psu_read_word(client->addr, psu_pmbus_reg_map[attr->index]); + + exponent = two_complement_to_int(vout_mode, 5, 0x1f); + mantissa = data; + + pega_print(DEBUG, "addr:0x%x, reg:0x%x, read_value:0x%x,exponent:%d,mantissa:%d\n", client->addr, psu_pmbus_reg_map[attr->index], data, exponent, mantissa); + if (exponent >= 0) + ret_val = (mantissa << exponent) * multiplier; + else + ret_val = (mantissa * multiplier) / (1 << -exponent); + + /*the vol_out_max can have a 3% tolenrence.*/ + if(attr->index == PSU_VOUT_MAX) + ret_val = ret_val * 103 / 100; + else if(attr->index == PSU_VOUT_MIN) + ret_val = ret_val * 97 / 100; + + return sprintf(buf, "%d.%d\n", ret_val / 1000, ret_val % 1000); +} + +static ssize_t pega_fn8656_bnf_psu_read_data_direct(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u16 read_value = 0; + int ret_val; + /* X = 1/m * (Y * 10^-R - b) */ + int m = 1, b = 0, r = -3; + + pega_print(DEBUG, "m:%d,b:%d,r:%d\n", m, b, r); + read_value = pega_fn8656_bnf_psu_read_word(client->addr, psu_pmbus_reg_map[attr->index]); + ret_val = 1 / m * (read_value * (10 << (-r)) - b); + + return sprintf(buf, "%d.%d\n", ret_val / 1000, ret_val % 1000); +} + +static ssize_t show_manufacture_info(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u8 data[PM_BUS_BLOCK_MAX_LEN] = {0}, read_count; + + read_count = pega_fn8656_bnf_psu_read_block(client->addr, psu_pmbus_reg_map[attr->index], data); + data[read_count] = '\0'; + return sprintf(buf, "%s\n", data); +} + +static ssize_t get_label(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + ssize_t size = 0; + + switch (attr->index) { + case 0: + size = sprintf(buf, "PSU Ambient\n"); + break; + case 1: + size = sprintf(buf, "PSU SR Hotspot\n"); + break; + case 2: + size = sprintf(buf, "PSU PFC Hotspot\n"); + break; + default: + pega_print(WARNING, "only 3 PSU temp sensors\n"); + break; + } + return size; +} +static ssize_t get_psu_sensor_type(struct device *dev, struct device_attribute *da, + char *buf) +{ + return sprintf(buf, "N/A\n"); +} + +static ssize_t get_psu_sensor_temp_min(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", drv_data->temp_min[attr->index]); +} + +static ssize_t get_psu_sensor_temp_max(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", drv_data->temp_max[attr->index]); +} + +static ssize_t set_psu_sensor_temp_max(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + long val = 0; + + if (kstrtol(buf, 10, &val)) { + return -EINVAL; + } + + drv_data->temp_max[attr->index] = val; + return count; +} + +static ssize_t get_psu_sensor_temperature(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u16 read_value = 0; + int exponent, mantissa; + int multiplier = 1000; + int ret_val; + u8 reg = attr->index + PSU_TEMP1_REG; + + if (attr->index >= MAX_PSU_TEMP_SENSORS) { + pega_print(WARNING, "only 3 PSU temp sensors\n"); + return -ENXIO; + } + read_value = pega_fn8656_bnf_psu_read_word(client->addr, reg); + exponent = two_complement_to_int(read_value >> 11, 5, 0x1f); + mantissa = two_complement_to_int(read_value & 0x7ff, 11, 0x7ff); + + pega_print(DEBUG, "addr:0x%x, reg:0x%x, read_value:0x%x,exponent:%d,mantissa:%d\n", client->addr, reg, read_value, exponent, mantissa); + if (exponent >= 0) + ret_val = (mantissa << exponent) * multiplier; + else + ret_val = (mantissa * multiplier) / (1 << -exponent); + + return sprintf(buf, "%d\n", ret_val); +} + +static ssize_t get_psu_sensor_temp_crit(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + struct i2c_client *client = drv_data->client; + u16 read_value = 0; + int exponent, mantissa; + int multiplier = 1000; + int ret_val; + u8 reg = attr->index + PSU_MFR_TEMP1_MAX_REG; + int temp_sensor = attr->index; + + if (attr->index >= MAX_PSU_TEMP_SENSORS) { + pega_print(WARNING, "only 3 PSU temp sensors\n"); + return -ENXIO; + } + read_value = pega_fn8656_bnf_psu_read_word(client->addr, reg); + exponent = two_complement_to_int(read_value >> 11, 5, 0x1f); + mantissa = two_complement_to_int(read_value & 0x7ff, 11, 0x7ff); + + pega_print(DEBUG, "addr:0x%x, reg:0x%x, read_value:0x%x,exponent:%d,mantissa:%d\n", client->addr, reg, read_value, exponent, mantissa); + if (exponent >= 0) + ret_val = (mantissa << exponent) * multiplier; + else + ret_val = (mantissa * multiplier) / (1 << -exponent); + + /* PSU vendor didnot set the max temperature*/ + if(ret_val == 0) + { + ret_val = drv_data->temp_crit[temp_sensor]; + } + + return sprintf(buf, "%d\n", ret_val); +} + +static ssize_t set_psu_sensor_temp_crit(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + long val = 0; + // int temp_sensor = attr->index - PSU_MFR_TEMP1_MAX_REG; + int temp_sensor = attr->index; + + if (kstrtol(buf, 10, &val)) { + return -EINVAL; + } + + drv_data->temp_crit[temp_sensor] = val; + return count; +} + +static ssize_t get_total_psu_num(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + if (!drv_data) { + pega_print(ERR, "fn8656_bnf_psu_data is NULL!\n"); + return -ENOENT; + } + return sprintf(buf, "%d\n", drv_data->total_psu_num); +} +static ssize_t get_temp_sensor_num(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + if (!drv_data) { + pega_print(ERR, "fn8656_bnf_psu_data is NULL!\n"); + return -ENOENT; + } + return sprintf(buf, "%d\n", drv_data->temp_sensor_num); +} +static ssize_t get_psu_type(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct fn8656_bnf_psu_data *drv_data = dev_get_drvdata(dev); + if (!drv_data) { + pega_print(ERR, "fn8656_bnf_psu_data is NULL!\n"); + return -ENOENT; + } + return sprintf(buf, "%d\n", drv_data->type); +} + +static SENSOR_DEVICE_ATTR(psu_led_status, S_IRUGO, get_psu_led_status, NULL, 0); +static SENSOR_DEVICE_ATTR(psu_alarm, S_IRUGO, read_psu_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(vout_over_voltage, S_IRUGO, read_psu_vout_over_voltage, NULL, 0); +static SENSOR_DEVICE_ATTR(iout_over_current_fault, S_IRUGO, read_psu_iout_over_current_fault, NULL, 0); +static SENSOR_DEVICE_ATTR(iout_over_current_warning, S_IRUGO, read_psu_iout_over_current_warning, NULL, 0); +static SENSOR_DEVICE_ATTR(iput_over_current_warning, S_IRUGO, read_psu_iput_over_current_warning, NULL, 0); +static SENSOR_DEVICE_ATTR(iput_insufficient, S_IRUGO, read_psu_iput_insufficient, NULL, 0); +static SENSOR_DEVICE_ATTR(temp_over_temp_fault, S_IRUGO, read_psu_temp_over_temp_fault, NULL, 0); +static SENSOR_DEVICE_ATTR(temp_over_temp_warning, S_IRUGO, read_psu_temp_over_temp_warning, NULL, 0); +static SENSOR_DEVICE_ATTR(vol_out, S_IRUGO, read_psu_vol_out, NULL, PSU_V_OUT); +static SENSOR_DEVICE_ATTR(vol_in, S_IRUGO, show_linear, NULL, PSU_V_IN); +static SENSOR_DEVICE_ATTR(curr_out, S_IRUGO, show_linear, NULL, PSU_I_OUT); +static SENSOR_DEVICE_ATTR(curr_in, S_IRUGO, show_linear, NULL, PSU_I_IN); +static SENSOR_DEVICE_ATTR(power_out, S_IRUGO, show_linear, NULL, PSU_P_OUT); +static SENSOR_DEVICE_ATTR(power_in, S_IRUGO, show_linear, NULL, PSU_P_IN); +static SENSOR_DEVICE_ATTR(fan1_speed, S_IRUGO, show_linear, NULL, PSU_FAN1_SPEED); + +static SENSOR_DEVICE_ATTR(manuafacture_id,S_IRUGO, show_manufacture_info, NULL, PSU_MFR_ID); +static SENSOR_DEVICE_ATTR(manuafacture_model,S_IRUGO, show_manufacture_info, NULL, PSU_MFR_MODEL); +static SENSOR_DEVICE_ATTR(manuafacture_revision,S_IRUGO, show_manufacture_info, NULL, PSU_MFR_REVISION); +static SENSOR_DEVICE_ATTR(manuafacture_serial,S_IRUGO, show_manufacture_info, NULL, PSU_MFR_SERIAL); +static SENSOR_DEVICE_ATTR(manuafacture_date,S_IRUGO, show_manufacture_info, NULL, PSU_MFR_DATE); +static SENSOR_DEVICE_ATTR(manuafacture_location,S_IRUGO, show_manufacture_info, NULL, PSU_MFR_LOCATION); +static SENSOR_DEVICE_ATTR(manuafacture_part_num,S_IRUGO, show_manufacture_info, NULL, PSU_MFR_MODEL); + +static SENSOR_DEVICE_ATTR(vol_in_min,S_IRUGO, show_linear, NULL, PSU_VIN_MIN); +static SENSOR_DEVICE_ATTR(vol_in_max,S_IRUGO, show_linear, NULL, PSU_VIN_MAX); +static SENSOR_DEVICE_ATTR(curr_in_max,S_IRUGO, show_linear, NULL, PSU_IIN_MAX); +static SENSOR_DEVICE_ATTR(power_in_max,S_IRUGO, pega_fn8656_bnf_psu_read_data_direct, NULL, PSU_PIN_MAX); +static SENSOR_DEVICE_ATTR(vol_out_min,S_IRUGO, read_psu_vol_out, NULL, PSU_VOUT_MIN); +static SENSOR_DEVICE_ATTR(vol_out_max,S_IRUGO, read_psu_vol_out, NULL, PSU_VOUT_MAX); +static SENSOR_DEVICE_ATTR(curr_out_max,S_IRUGO, show_linear, NULL, PSU_IOUT_MAX); +static SENSOR_DEVICE_ATTR(power_out_max,S_IRUGO, pega_fn8656_bnf_psu_read_data_direct, NULL, PSU_POUT_MAX); + +static SENSOR_DEVICE_ATTR(psu_num, S_IRUGO, get_total_psu_num, NULL, 0); +static SENSOR_DEVICE_ATTR(sensor_num, S_IRUGO, get_temp_sensor_num, NULL, 0); +static SENSOR_DEVICE_ATTR(psu_type, S_IRUGO, get_psu_type, NULL, 0); +static SENSOR_DEVICE_ATTR(psu_sensor_type, S_IRUGO, get_psu_sensor_type, NULL, 0); + +#define SET_TEMP_ATTR(_num) \ + static SENSOR_DEVICE_ATTR(temp##_num##_input, S_IRUGO, get_psu_sensor_temperature, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(temp##_num##_label, S_IRUGO, get_label, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(temp##_num##_max, S_IRUGO, get_psu_sensor_temp_max, set_psu_sensor_temp_max, _num-1); \ + static SENSOR_DEVICE_ATTR(temp##_num##_min, S_IRUGO, get_psu_sensor_temp_min, NULL, _num-1); \ + static SENSOR_DEVICE_ATTR(temp##_num##_crit, S_IRUGO, get_psu_sensor_temp_crit, set_psu_sensor_temp_crit, _num-1); + +SET_TEMP_ATTR(1);SET_TEMP_ATTR(2);SET_TEMP_ATTR(3); + +static struct attribute *pega_fn8656_bnf_psu_attrs[] = { + &sensor_dev_attr_psu_led_status.dev_attr.attr, + &sensor_dev_attr_psu_alarm.dev_attr.attr, + &sensor_dev_attr_vout_over_voltage.dev_attr.attr, + &sensor_dev_attr_iout_over_current_fault.dev_attr.attr, + &sensor_dev_attr_iout_over_current_warning.dev_attr.attr, + &sensor_dev_attr_iput_over_current_warning.dev_attr.attr, + &sensor_dev_attr_iput_insufficient.dev_attr.attr, + &sensor_dev_attr_temp_over_temp_fault.dev_attr.attr, + &sensor_dev_attr_temp_over_temp_warning.dev_attr.attr, + + &sensor_dev_attr_vol_out.dev_attr.attr, + &sensor_dev_attr_vol_in.dev_attr.attr, + &sensor_dev_attr_curr_out.dev_attr.attr, + &sensor_dev_attr_curr_in.dev_attr.attr, + &sensor_dev_attr_power_out.dev_attr.attr, + &sensor_dev_attr_power_in.dev_attr.attr, + &sensor_dev_attr_fan1_speed.dev_attr.attr, + + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp1_label.dev_attr.attr, + &sensor_dev_attr_temp2_label.dev_attr.attr, + &sensor_dev_attr_temp3_label.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + + &sensor_dev_attr_manuafacture_id.dev_attr.attr, + &sensor_dev_attr_manuafacture_model.dev_attr.attr, + &sensor_dev_attr_manuafacture_revision.dev_attr.attr, + &sensor_dev_attr_manuafacture_serial.dev_attr.attr, + &sensor_dev_attr_manuafacture_date.dev_attr.attr, + &sensor_dev_attr_manuafacture_location.dev_attr.attr, + &sensor_dev_attr_manuafacture_part_num.dev_attr.attr, + &sensor_dev_attr_vol_in_min.dev_attr.attr, + &sensor_dev_attr_vol_in_max.dev_attr.attr, + &sensor_dev_attr_curr_in_max.dev_attr.attr, + &sensor_dev_attr_power_in_max.dev_attr.attr, + &sensor_dev_attr_vol_out_min.dev_attr.attr, + &sensor_dev_attr_vol_out_max.dev_attr.attr, + &sensor_dev_attr_curr_out_max.dev_attr.attr, + &sensor_dev_attr_power_out_max.dev_attr.attr, + + &sensor_dev_attr_psu_num.dev_attr.attr, + &sensor_dev_attr_sensor_num.dev_attr.attr, + &sensor_dev_attr_psu_type.dev_attr.attr, + &sensor_dev_attr_psu_sensor_type.dev_attr.attr, + NULL +}; + +//static const struct attribute_group pega_fn8656_bnf_psu_group = { .attrs = pega_fn8656_bnf_psu_attributes}; +ATTRIBUTE_GROUPS(pega_fn8656_bnf_psu); +static void pega_fn8656_bnf_psu_add_client(struct i2c_client *client) +{ + struct psu_client_node *node = kzalloc(sizeof(struct psu_client_node), GFP_KERNEL); + + if (!node) { + pega_print(ERR, "Can't allocate psu_client_node (0x%x)\n", client->addr); + return; + } + + node->client = client; + + mutex_lock(&list_lock); + list_add(&node->list, &psu_client_list); + mutex_unlock(&list_lock); +} + +static void pega_fn8656_bnf_psu_remove_client(struct i2c_client *client) +{ + struct list_head *list_node = NULL; + struct psu_client_node *psu_node = NULL; + int found = 0; + + mutex_lock(&list_lock); + + list_for_each(list_node, &psu_client_list) + { + psu_node = list_entry(list_node, struct psu_client_node, list); + + if (psu_node->client == client) { + found = 1; + break; + } + } + + if (found) { + list_del(list_node); + kfree(psu_node); + } + + mutex_unlock(&list_lock); +} + +static int pega_fn8656_bnf_psu_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status = 0 , i = 0; + struct device *dev = &client->dev; + struct fn8656_bnf_psu_data *data; + struct device *hwmon_dev; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + pega_print(ERR, "i2c_check_functionality failed (0x%x)\n", client->addr); + status = -EIO; + goto exit; + } + + data = devm_kzalloc(dev, sizeof(struct fn8656_bnf_psu_data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + data->client = client; + data->total_psu_num = MAX_PSU_NUMS; + data->temp_sensor_num = MAX_PSU_TEMP_SENSORS; + data->type = POWER_AC; + for (i = 0; i < MAX_PSU_TEMP_SENSORS; i++) { + data->temp_max[i] = psu_temp_max[i] * 1000; + data->temp_min[i] = psu_temp_min[i] * 1000; + data->temp_crit[i] = psu_temp_crit[i] *1000; + } + + /* Register sysfs hooks */ + switch (client->addr) { + case PSU_58_ADDRESS: + case PSU_59_ADDRESS: + pega_print(ERR,"hwmon_dev register\n"); + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, pega_fn8656_bnf_psu_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + break; + default: + pega_print(WARNING, "i2c_check_psu failed (0x%x)\n", client->addr); + status = -EIO; + goto exit; + break; + } + + if (status) { + goto exit; + } + + pega_print(INFO, "chip found\n"); + pega_fn8656_bnf_psu_add_client(client); + + return 0; + +exit: + return status; +} + +static int pega_fn8656_bnf_psu_remove(struct i2c_client *client) +{ + pega_fn8656_bnf_psu_remove_client(client); + return 0; +} + +static const struct i2c_device_id pega_fn8656_bnf_psu_id[] = { + { "fn8656_bnf_psu", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, pega_fn8656_bnf_psu_id); + +static struct i2c_driver pega_fn8656_bnf_psu_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "pegatron_fn8656_bnf_psu", + }, + .probe = pega_fn8656_bnf_psu_probe, + .remove = pega_fn8656_bnf_psu_remove, + .id_table = pega_fn8656_bnf_psu_id, + .address_list = normal_i2c, +}; + +static int __init pega_fn8656_bnf_psu_init(void) +{ + mutex_init(&list_lock); + + return i2c_add_driver(&pega_fn8656_bnf_psu_driver); +} + +static void __exit pega_fn8656_bnf_psu_exit(void) +{ + i2c_del_driver(&pega_fn8656_bnf_psu_driver); +} + +module_param(loglevel, uint, 0644); +module_param_string(debug, debug, MAX_DEBUG_INFO_LEN, 0644); +MODULE_PARM_DESC(loglevel, "0x01-LOG_ERR,0x02-LOG_WARNING,0x04-LOG_INFO,0x08-LOG_DEBUG"); +MODULE_PARM_DESC(debug, "help info"); + +MODULE_AUTHOR("Peter5 Lin "); +MODULE_DESCRIPTION("pega_fn8656_bnf_psu driver"); +MODULE_LICENSE("GPL"); + +module_init(pega_fn8656_bnf_psu_init); +module_exit(pega_fn8656_bnf_psu_exit); diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/pegatron_fn8656_bnf_sfp.c b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/pegatron_fn8656_bnf_sfp.c new file mode 100644 index 000000000000..51dc830bdad9 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/pegatron_fn8656_bnf_sfp.c @@ -0,0 +1,1323 @@ +/* + * pegatron_fn8656_bnf_sfp.c - A driver to read and write the EEPROM on optical transceivers + * (SFP and QSFP) + * + * Copyright (C) 2014 Cumulus networks Inc. + * Copyright (C) 2017 Finisar Corp. + * Copyright (C) 2019 Pegatron 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 PEGA_DEBUG 1 +/*#define pega_DEBUG*/ +#ifdef PEGA_DEBUG +#define DBG(x) x +#else +#define DBG(x) +#endif /* DEBUG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pegatron_pub.h" +#include +#include +#include +#include +#include +#include + +#define NUM_ADDRESS 2 + +/* The maximum length of a port name */ +#define MAX_PORT_NAME_LEN 20 + +/* fundamental unit of addressing for EEPROM */ +#define SFP_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 SFP_ARCH_PAGES 256 +#define ONE_ADDR_EEPROM_SIZE ((1 + SFP_ARCH_PAGES) * SFP_PAGE_SIZE) +#define ONE_ADDR_EEPROM_UNPAGED_SIZE (2 * SFP_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 + SFP_ARCH_PAGES) * SFP_PAGE_SIZE) +#define TWO_ADDR_EEPROM_UNPAGED_SIZE (4 * SFP_PAGE_SIZE) +#define TWO_ADDR_NO_0X51_SIZE (2 * SFP_PAGE_SIZE) + +/* + * 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 + +/* a few constants to find our way around the EEPROM */\ +#define SFP_EEPROM_A0_ADDR 0xA0 +#define SFP_EEPROM_A2_ADDR 0xA2 +#define SFP_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 SFP_ID_REG 0 +#define SFP_READ_OP 0 +#define SFP_WRITE_OP 1 +#define SFP_EOF 0 /* used for access beyond end of device */ +#define MAX_PORT_NUM (56) +#define GET_BIT(data, bit, value) value = (data >> bit) & 0x1 +#define SET_BIT(data, bit) data |= (1 << bit) +#define CLEAR_BIT(data, bit) data &= ~(1 << bit) + +#define SFP_FAST_BYTE_LENGTH 4 + +#define SFP_LOWER_PAGE (0x0) +#define SFP_CACHE_PAGE_NUM (0x11) +#define MAX_CACHE_LEN (SFP_PAGE_SIZE*(SFP_CACHE_PAGE_NUM + 1)) //SFP_CACHE_PAGE_NUM upper pages and 1 lower page + +#define FPGA_I2C_RD_MODE_BLOCK 1 +#define FPGA_I2C_RD_MODE_BYTE 0 + +static struct task_struct *sfp_task; +static char sfp_eeprom_cache[MAX_PORT_NUM][MAX_CACHE_LEN]; +static int enable_sfp_eeprom_cache = FALSE; +static int enable_sfp_three_batch =TRUE; + +struct fn8656_bnf_sfp_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 */ + + /* dev_class: ONE_ADDR (QSFP) or TWO_ADDR (SFP) */ + int dev_class; + int slave_addr; + unsigned int write_max; +}; + +struct kwai_fpga{ + struct pci_dev *pdev; + struct mutex lock; + void __iomem *mmio; + unsigned int i2c_mode; + struct fn8656_bnf_sfp_platform_data chip[MAX_PORT_NUM+1]; +}; + +/* + * 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 = SFP_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 = 250; + +static uint loglevel = LOG_INFO | LOG_WARNING | LOG_ERR; +static char fpga_debug[MAX_DEBUG_INFO_LEN] = "fpga debug info: \n"; +/*-------------------------------------------------------------------------*/ +/* + * 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 + */ + +#define FPGA_HW_VERSION (0) +#define FPGA_SW_VERSION (4) + +#define FPGA_NUM 1 + +static ssize_t get_fpga_chip_num(struct device *dev, struct device_attribute *da, + char *buf) +{ + return sprintf(buf, "%d\n", FPGA_NUM); +} +static ssize_t get_fpga_type(struct device *dev, struct device_attribute *da, + char *buf) +{ + return sprintf(buf, "xilinx artix-7:xc7a75tfgg484-2\n"); +} +static ssize_t get_fpga_alias(struct device *dev, struct device_attribute *da, + char *buf) +{ + return sprintf(buf, "fpga\n"); +} + +static ssize_t read_fpga_HWversion(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct kwai_fpga *fpga = pdev->sysdata; + + uint8_t data = readl(fpga->mmio + FPGA_HW_VERSION); + pega_print(DEBUG,"read fpga addr:0x%x,data:0x%x\n",fpga->mmio + FPGA_HW_VERSION,data); + + return sprintf(buf, "%02d\n", data); +} + +static ssize_t read_fpga_SWversion(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct kwai_fpga *fpga = pci_dev->sysdata; + + uint8_t data = readl(fpga->mmio + FPGA_SW_VERSION); + pega_print(DEBUG,"read fpga addr:0x%x,data:0x%x\n",fpga->mmio + FPGA_SW_VERSION,data); + + return sprintf(buf, "%02d\n", data); +} + +struct fpga_device_attribute{ + struct device_attribute dev_attr; + int index; +}; +#define to_fpga_dev_attr(_dev_attr) \ + container_of(_dev_attr, struct fpga_device_attribute, dev_attr) + +#define FPGA_ATTR(_name, _mode, _show, _store, _index) \ + { .dev_attr = __ATTR(_name, _mode, _show, _store), \ + .index = _index } + +#define FPGA_DEVICE_ATTR(_name, _mode, _show, _store, _index) \ +struct fpga_device_attribute fpga_dev_attr_##_name \ + = FPGA_ATTR(_name, _mode, _show, _store, _index) + +static FPGA_DEVICE_ATTR(fpga_chip_num, S_IRUGO, get_fpga_chip_num, NULL, 0); +static FPGA_DEVICE_ATTR(fpga_alias, S_IRUGO, get_fpga_alias, NULL, 0); +static FPGA_DEVICE_ATTR(fpga_type, S_IRUGO, get_fpga_type, NULL, 0); +static FPGA_DEVICE_ATTR(fpga_hw_version, S_IRUGO, read_fpga_HWversion, NULL, 0); +static FPGA_DEVICE_ATTR(fpga_sw_version, S_IRUGO, read_fpga_SWversion, NULL, 0); + +static struct attribute *fn8656_bnf_fpga_attributes[] = { + &fpga_dev_attr_fpga_type.dev_attr.attr, + &fpga_dev_attr_fpga_alias.dev_attr.attr, + &fpga_dev_attr_fpga_chip_num.dev_attr.attr, + &fpga_dev_attr_fpga_hw_version.dev_attr.attr, + &fpga_dev_attr_fpga_sw_version.dev_attr.attr, + NULL +}; +static const struct attribute_group fn8656_bnf_fpga_group = { .attrs = fn8656_bnf_fpga_attributes}; + +static uint8_t fn8656_bnf_sfp_translate_offset(struct kwai_fpga *sfp, + loff_t *offset, int num) +{ + unsigned int page = 0; + + /* if SFP style, offset > 255, shift to i2c addr 0x51 */ + if (sfp->chip[num].dev_class == TWO_ADDR) { + if (*offset > 255) { + sfp->chip[num].slave_addr = SFP_EEPROM_A2_ADDR; + *offset -= 256; + } else { + sfp->chip[num].slave_addr = SFP_EEPROM_A0_ADDR; + } + } + + /* + * 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 < SFP_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 = SFP_PAGE_SIZE + (*offset & 0x7f); + + return page; /* note also returning clifn8656_bnf_sfp_translate_offsetent and offset */ +} + +#define FPGA_PORT_MGR_CFG (0x1000) +#define FPGA_PORT_MGR_CTRL (0x1004) +#define FPGA_PORT_MGR_STAT (0x1008) +#define FPGA_PORT_MGR_MUX (0x1010) +#define FPGA_PORT_BATCH_DATA (0x100000) +/* + *i2c controller 0 is used for port 0~24 + *i2c controller 1 is used for port 24~48 + *i2c controller 2 is used for port 48~56 +*/ +#define i2c_dev(mode, port, idx) \ +do { \ + if (((port) < 24 ) || (!mode)) \ + idx = 0; \ + else if ((port) < 48) \ + idx = 1; \ + else \ + idx = 2; \ +}while(0) + +#define port_mgr_cfg_reg(base_addr, idx) \ +(base_addr + FPGA_PORT_MGR_CFG + 0x20 * (idx)) + +#define port_mgr_ctrl_reg(base_addr, idx) \ +(base_addr + FPGA_PORT_MGR_CTRL + 0x20 * (idx)) + +#define port_mgr_stat_reg(base_addr, idx) \ +(base_addr + FPGA_PORT_MGR_STAT + 0x20 * (idx)) + +#define port_mgr_mux_reg(base_addr, idx) \ +(base_addr + FPGA_PORT_MGR_MUX + 0x20 * (idx)) + +#define port_mgr_batch_reg(base_addr, idx, offset) \ +(base_addr + FPGA_PORT_BATCH_DATA + 0x10000 * (idx) + offset) + +#define port2data(mode, port, data) \ +do { \ + if (((port) < 24) || (!mode)) \ + data = (port); \ + else if ((port) < 48 ) \ + data = (port) % 24; \ + else \ + data = (port) % 48; \ +}while(0) + + +static int fpga_i2c_byte_read(struct kwai_fpga *sfp, + int port, + char *buf, unsigned int offset) +{ + uint32_t data; + uint32_t idx = 0; + int i = 0; + + i2c_dev(sfp->i2c_mode, (port - 1), idx); + data = 0x80000000; + writel(data, port_mgr_cfg_reg(sfp->mmio, idx)); + + data = 0x40fa0100 | ((sfp->chip[port].slave_addr & 0xFF) << 8) | (sfp->chip[port].slave_addr & 0xFF); + writel(data, port_mgr_cfg_reg(sfp->mmio, idx)); + + port2data(sfp->i2c_mode, (port - 1), data); + writel(data, port_mgr_mux_reg(sfp->mmio, idx)); + + data = 0x81000000 | ((offset&0xFF) << 16); + writel(data, port_mgr_ctrl_reg(sfp->mmio, idx)); + pega_print(DEBUG, "fpga_i2c_byte_read port %d, offset:0x%x, idx:%d,cfg:%x@%x mux:%x@%x ctrl:%x@%x stat:%x@%x\n", port, offset, idx, + port_mgr_cfg_reg(sfp->mmio, idx), readl(port_mgr_cfg_reg(sfp->mmio, idx)), + port_mgr_mux_reg(sfp->mmio, idx), readl(port_mgr_mux_reg(sfp->mmio, idx)), + port_mgr_ctrl_reg(sfp->mmio, idx), readl(port_mgr_ctrl_reg(sfp->mmio, idx)), + port_mgr_stat_reg(sfp->mmio, idx), readl(port_mgr_stat_reg(sfp->mmio, idx)) + ); + do { + data = readl(port_mgr_stat_reg(sfp->mmio, idx)); + if ((data & 0xC0000000) == 0x80000000) { + *buf = data & 0xFF; + return 0; + } + usleep_range(100,200); + }while(i++ < 1000); + return -ENXIO; +} +static int fpga_i2c_block_read(struct kwai_fpga *sfp, + int port, + char *buf, unsigned int offset,size_t count) +{ + uint32_t data; + uint32_t idx = 0; + int i = 0; + int buf_offset = 0; + + i2c_dev(sfp->i2c_mode, (port - 1), idx); + data = 0x80000000; + writel(data, port_mgr_cfg_reg(sfp->mmio, idx)); + + data = 0x40fa0100 | ((sfp->chip[port].slave_addr & 0xFF) << 8) | (sfp->chip[port].slave_addr & 0xFF); + writel(data, port_mgr_cfg_reg(sfp->mmio, idx)); + + port2data(sfp->i2c_mode, (port - 1), data); + writel(data, port_mgr_mux_reg(sfp->mmio, idx)); + + data = 0x82000000 | ((offset&0xFF) << 16) | ((count & 0xFF) << 8); + writel(data, port_mgr_ctrl_reg(sfp->mmio, idx)); + pega_print(DEBUG, "fpga_i2c_block_read port %d, offset:0x%x, idx:%d,cfg:%x@%x mux:%x@%x ctrl:%x@%x stat:%x@%x\n", port, offset, idx, + port_mgr_cfg_reg(sfp->mmio, idx), readl(port_mgr_cfg_reg(sfp->mmio, idx)), + port_mgr_mux_reg(sfp->mmio, idx), readl(port_mgr_mux_reg(sfp->mmio, idx)), + port_mgr_ctrl_reg(sfp->mmio, idx), readl(port_mgr_ctrl_reg(sfp->mmio, idx)), + port_mgr_stat_reg(sfp->mmio, idx), readl(port_mgr_stat_reg(sfp->mmio, idx)) + ); + do { + data = readl(port_mgr_stat_reg(sfp->mmio, idx)); + if ((data & 0xC0000000) == 0x80000000) { + for(buf_offset = 0; buf_offset < count;buf_offset++){ + if ((enable_sfp_three_batch == FALSE) && (idx)) + { + pega_print(DEBUG, "fpga_i2c_block_read bach:@%x\n", port_mgr_batch_reg(sfp->mmio, idx, buf_offset)); + } +else + *buf++ = readb(port_mgr_batch_reg(sfp->mmio, idx, buf_offset)); + } + return buf_offset; + } + usleep_range(100,200); + } while(i++ < 1000); + return -ENXIO; +} + +static int fpga_i2c_byte_write(struct kwai_fpga *sfp, + int port, + char *buf, unsigned int offset) +{ + uint32_t data; + uint32_t idx = 0; + int i = 0; + + i2c_dev(sfp->i2c_mode, (port - 1), idx); + data = 0x80000000; + writel(data, port_mgr_cfg_reg(sfp->mmio, idx)); + + data = 0x40fa0100 | ((sfp->chip[port].slave_addr & 0xFF) << 8) | (sfp->chip[port].slave_addr & 0xFF); + writel(data, port_mgr_cfg_reg(sfp->mmio, idx)); + + port2data(sfp->i2c_mode, (port - 1), data); + writel(data, port_mgr_mux_reg(sfp->mmio, idx)); + + data = 0x84000000 | ((offset & 0xFF) << 16) | (*buf & 0xFF); + writel(data, port_mgr_ctrl_reg(sfp->mmio, idx)); + pega_print(DEBUG, "fpga_i2c_byte_write port %d, offset:0x%x, idx:%d,cfg:%x@%x mux:%x@%x ctrl:%x@%x stat:%x@%x\n", port, offset, idx, + port_mgr_cfg_reg(sfp->mmio, idx), readl(port_mgr_cfg_reg(sfp->mmio, idx)), + port_mgr_mux_reg(sfp->mmio, idx), readl(port_mgr_mux_reg(sfp->mmio, idx)), + port_mgr_ctrl_reg(sfp->mmio, idx), readl(port_mgr_ctrl_reg(sfp->mmio, idx)), + port_mgr_stat_reg(sfp->mmio, idx), readl(port_mgr_stat_reg(sfp->mmio, idx)) + ); + do { + data = readl(port_mgr_stat_reg(sfp->mmio, idx)); + if (data & 0x80000000) { + return 0; + } + }while(i++ < 1000); + return -ENXIO; +} + +static ssize_t fn8656_bnf_sfp_eeprom_read_byte_by_byte(struct kwai_fpga *sfp, + int port, + char *buf, unsigned int offset, size_t count) +{ + unsigned long timeout, write_time; + int ret; + int i = 0; + + timeout = jiffies + msecs_to_jiffies(write_timeout); + do { + write_time = jiffies; + ret = fpga_i2c_byte_read(sfp, port, buf + i, (offset + i)); + pega_print(DEBUG, "read register byte by byte %d, offset:0x%x, data :0x%x ret:%d\n", i, offset + i, *(buf + i), ret); + if (ret == -ENXIO) /* no module present */ + return ret; + i++; + } while (time_before(write_time, timeout) && (i < count)); + + if (i == count) + return count; + + return -ETIMEDOUT; +} + +static int fpga_i2c_read(struct kwai_fpga *sfp, + int port, + char *buf, unsigned int offset,size_t count) +{ + if (sfp->i2c_mode == FPGA_I2C_RD_MODE_BLOCK) + return fpga_i2c_block_read(sfp, port, buf, offset, count); + else + return fn8656_bnf_sfp_eeprom_read_byte_by_byte(sfp, port, buf, offset, count); +} + +static ssize_t fn8656_bnf_sfp_eeprom_read(struct kwai_fpga *sfp, + int port, + char *buf, unsigned int offset, size_t count) +{ + unsigned long timeout, read_time; + int status; + if (count < SFP_FAST_BYTE_LENGTH) + { + return fn8656_bnf_sfp_eeprom_read_byte_by_byte(sfp, port, buf, offset, count); + } + /*smaller eeproms can work given some SMBus extension calls */ + if (count > SFP_PAGE_SIZE) + count = SFP_PAGE_SIZE; + + /* + * 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; + + status = fpga_i2c_read(sfp, port, buf, offset, count); + + pega_print(DEBUG, "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; + + } while (time_before(read_time, timeout)); + + return -ETIMEDOUT; +} + +static ssize_t fn8656_bnf_sfp_eeprom_write_byte_by_byte( + struct kwai_fpga *sfp, + int port, + const char *buf, unsigned int offset, size_t count) +{ + unsigned long timeout, write_time; + int ret; + int i = 0; + + timeout = jiffies + msecs_to_jiffies(write_timeout); + do { + write_time = jiffies; + ret = fpga_i2c_byte_write(sfp, port, (char *)buf + i, (offset + i)); + pega_print(DEBUG, "Write register byte by byte %zu, offset:0x%x, data :0x%x ret:%d\n", + count, offset + i, *(buf + i), ret); + if (ret == -ENXIO) + return ret; + i++; + //usleep_range(10000, 15000); + } while (time_before(write_time, timeout) && (i < count)); + + if (i == count) { + return count; + } + + return -ETIMEDOUT; +} + + +static ssize_t fn8656_bnf_sfp_eeprom_write(struct kwai_fpga *sfp, + int port, + const char *buf, + unsigned int offset, size_t count) +{ + ssize_t status; + unsigned long timeout, write_time; + unsigned int next_page_start; + + /* write max is at most a page + * (In this driver, write_max is actually one byte!) + */ + if (count > sfp->chip[port].write_max) + count = sfp->chip[port].write_max; + + /* shorten count if necessary to avoid crossing page boundary */ + next_page_start = roundup(offset + 1, SFP_PAGE_SIZE); + if ((offset + count) > next_page_start) + count = next_page_start - offset; + + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; + + /* + * 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; + + status = fn8656_bnf_sfp_eeprom_write_byte_by_byte(sfp, port, buf, offset, count); + if (status == count) /* happy path */ + return count; + + if (status == -ENXIO) /* no module present */ + return status; + + } while (time_before(write_time, timeout)); + + return -ETIMEDOUT; +} + +static ssize_t fn8656_bnf_sfp_eeprom_update_client(struct kwai_fpga *sfp, + char *buf, loff_t off, size_t count, int num) +{ + ssize_t retval = 0; + uint8_t page = 0; + uint8_t page_check = 0; + loff_t phy_offset = off; + int ret = 0; + + page = fn8656_bnf_sfp_translate_offset(sfp, &phy_offset, num); + + pega_print(DEBUG,"off %lld page:%d phy_offset:%lld, count:%ld\n", + off, page, phy_offset, (long int) count); + if (page > 0) { + ret = fn8656_bnf_sfp_eeprom_write(sfp, num, &page, + SFP_PAGE_SELECT_REG, 1); + if (ret < 0) { + pega_print(WARNING,"Write page register for page %d failed ret:%d!\n",page, ret); + return ret; + } + } + wmb(); + + ret = fn8656_bnf_sfp_eeprom_read(sfp, num, + &page_check , SFP_PAGE_SELECT_REG, 1); + if (ret < 0) { + pega_print(DEBUG,"Read page register for page %d failed ret:%d!\n",page, ret); + return ret; + } + pega_print(DEBUG,"read page register %d checked %d, ret:%d\n",page, page_check, ret); + + while (count) { + ssize_t status; + + status = fn8656_bnf_sfp_eeprom_read(sfp, num, + 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 = fn8656_bnf_sfp_eeprom_write(sfp, num, &page, + SFP_PAGE_SELECT_REG, 1); + if (ret < 0) { + pega_print(ERR,"Restore page register to 0 failed:%d!\n", ret); + /* error only if nothing has been transferred */ + if (retval == 0) + retval = ret; + } + } + return retval; +} + +static ssize_t fn8656_bnf_sfp_eeprom_write_client(struct kwai_fpga *sfp, + char *buf, loff_t off, size_t count, int num) +{ + ssize_t retval = 0; + uint8_t page = 0; + uint8_t page_check = 0; + loff_t phy_offset = off; + int ret = 0; + + page = fn8656_bnf_sfp_translate_offset(sfp, &phy_offset, num); + + pega_print(DEBUG,"off %lld page:%d phy_offset:%lld, count:%ld\n", + off, page, phy_offset, (long int) count); + if (page > 0) { + ret = fn8656_bnf_sfp_eeprom_write(sfp, num, &page, + SFP_PAGE_SELECT_REG, 1); + if (ret < 0) { + pega_print(WARNING,"Write page register for page %d failed ret:%d!\n",page, ret); + return ret; + } + } + wmb(); + + ret = fn8656_bnf_sfp_eeprom_read(sfp, num, + &page_check , SFP_PAGE_SELECT_REG, 1); + if (ret < 0) { + pega_print(WARNING,"Read page register for page %d failed ret:%d!\n",page, ret); + return ret; + } + pega_print(DEBUG,"read page register %d checked %d, ret:%d\n",page, page_check, ret); + + ret = fn8656_bnf_sfp_eeprom_write(sfp, num, + buf, phy_offset, count); + + if (ret < 0) { + retval = ret; + pega_print(ERR,"write eeprom failed:offset:0x%x bytes:%d ret:%d!\n", phy_offset, num, ret); + } + /*update sfp eeprom cache buffer after write*/ + if(enable_sfp_eeprom_cache == TRUE) + fn8656_bnf_sfp_eeprom_read(sfp,num, &sfp_eeprom_cache[num-1][page*SFP_PAGE_SIZE+phy_offset], phy_offset, count); + + if (page > 0) { + /* return the page register to page 0 (why?) */ + page = 0; + ret = fn8656_bnf_sfp_eeprom_write(sfp, num, &page, + SFP_PAGE_SELECT_REG, 1); + if (ret < 0) { + pega_print(ERR,"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 fn8656_bnf_sfp_page_legal(struct kwai_fpga *sfp, + loff_t off, size_t len, int num) +{ + u8 regval; + int not_pageable; + int status; + size_t maxlen; + + if (off < 0) + return -EINVAL; + + if (sfp->chip[num].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 SFP_EOF; + /* in between, are pages supported? */ + status = fn8656_bnf_sfp_eeprom_read(sfp, num, ®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 SFP_EOF; + + /* will be accessing addr 0x51, is that supported? */ + /* byte 92, bit 6 implies DDM support, 0x51 support */ + status = fn8656_bnf_sfp_eeprom_read(sfp, num, ®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 SFP_EOF; + maxlen = TWO_ADDR_NO_0X51_SIZE - off; + } + } + len = (len > maxlen) ? maxlen : len; + pega_print(DEBUG,"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 SFP_EOF; + /* in between, are pages supported? */ + status = fn8656_bnf_sfp_eeprom_read(sfp, num, ®val, + ONE_ADDR_PAGEABLE_REG, 1); + if (status < 0) + return status; /* error out (no module?) */ + + if (sfp->chip[num].dev_class == ONE_ADDR) { + not_pageable = QSFP_NOT_PAGEABLE; + } else { + not_pageable = CMIS_NOT_PAGEABLE; + } + pega_print(DEBUG,"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 SFP_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; + pega_print(DEBUG,"page_legal, QSFP, off %lld len %ld\n", + off, (long int) len); + } + return len; +} + +static ssize_t fn8656_bnf_sfp_read(struct kwai_fpga *sfp, + char *buf, loff_t off, size_t len, int num) +{ + 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; + + if (unlikely(!len)) + return len; + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&sfp->lock); + + /* + * Confirm this access fits within the device suppored addr range + */ + status = fn8656_bnf_sfp_page_legal(sfp, off, len, num); + if ((status == SFP_EOF) || (status < 0)) { + mutex_unlock(&sfp->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 * SFP_PAGE_SIZE; + chunk_end_offset = chunk_start_offset + SFP_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 < SFP_PAGE_SIZE) + chunk_len = pending_len; + else + chunk_len = SFP_PAGE_SIZE; + } + + /* + * note: chunk_offset is from the start of the EEPROM, + * not the start of the chunk + */ + status = fn8656_bnf_sfp_eeprom_update_client(sfp, buf, + chunk_offset, chunk_len, num); + if (status != chunk_len) { + /* This is another 'no device present' path */ + if (status > 0) + retval += status; + if (retval == 0) + retval = status; + break; + } + buf += status; + pending_len -= status; + retval += status; + } + mutex_unlock(&sfp->lock); + + return retval; +} + +static ssize_t fn8656_bnf_sfp_write(struct kwai_fpga *sfp, + char *buf, loff_t off, size_t len, int num) +{ + 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; + + if (unlikely(!len)) + return len; + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&sfp->lock); + + /* + * Confirm this access fits within the device suppored addr range + */ + status = fn8656_bnf_sfp_page_legal(sfp, off, len, num); + if ((status == SFP_EOF) || (status < 0)) { + mutex_unlock(&sfp->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 * SFP_PAGE_SIZE; + chunk_end_offset = chunk_start_offset + SFP_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 < SFP_PAGE_SIZE) + chunk_len = pending_len; + else + chunk_len = SFP_PAGE_SIZE; + } + + /* + * note: chunk_offset is from the start of the EEPROM, + * not the start of the chunk + */ + status = fn8656_bnf_sfp_eeprom_write_client(sfp, buf, + chunk_offset, chunk_len, num); + if (status != chunk_len) { + /* This is another 'no device present' path */ + if (status > 0) + retval += status; + if (retval == 0) + retval = status; + break; + } + buf += status; + pending_len -= status; + retval += status; + } + mutex_unlock(&sfp->lock); + + return retval; +} + + +int sfp_poll(void *data) +{ + struct kwai_fpga * sfp = (struct kwai_fpga *)data; + int port = 0; + unsigned int offset = 0 ; + int ret = 0; + + while(!kthread_should_stop()) + { + if(enable_sfp_eeprom_cache == FALSE) + { + usleep_range(1000000, 1500000); + continue; + } + + for(port = 0; port < MAX_PORT_NUM; port ++) + { + if(kthread_should_stop()) + return 0; + ret = fn8656_bnf_sfp_read(sfp, sfp_eeprom_cache[port], 0,MAX_CACHE_LEN, port+1); + if(ret != MAX_CACHE_LEN) + { + memset(sfp_eeprom_cache[port],0x0,MAX_CACHE_LEN); + sfp_eeprom_cache[port][0] = ret; + pega_print(DEBUG,"port:%d,ret:%d\n",port+1,sfp_eeprom_cache[port][0]); + } + usleep_range(1000000, 1500000); + } + } + return 0; +} + +static ssize_t +fn8656_bnf_sfp_bin_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct pci_dev *dev = to_pci_dev(container_of(kobj, struct device, kobj)); + struct kwai_fpga *fpga = dev->sysdata; + int port = (char *)(attr->private) - (char *)NULL; + + if(enable_sfp_eeprom_cache == TRUE) + { + memcpy(buf,&sfp_eeprom_cache[port-1][off],count); + if(sfp_eeprom_cache[port-1][0] < 0) + count = buf[0]; + return count; + } + + return fn8656_bnf_sfp_read(fpga, buf, off, count, port); +} + +static ssize_t +fn8656_bnf_sfp_bin_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct pci_dev *dev = to_pci_dev(container_of(kobj, struct device, kobj)); + struct kwai_fpga *fpga = dev->sysdata; + + return fn8656_bnf_sfp_write(fpga, buf, off, count, (char *)(attr->private) - (char *)NULL); +} + +#define SFP_EEPROM_ATTR(_num) \ + static struct bin_attribute sfp##_num##_eeprom_attr = { \ + .attr = { \ + .name = __stringify(sfp##_num##_eeprom), \ + .mode = S_IRUGO|S_IWUSR \ + }, \ + .private = (void *)_num, \ + .size = TWO_ADDR_EEPROM_SIZE, \ + .read = fn8656_bnf_sfp_bin_read, \ + .write = fn8656_bnf_sfp_bin_write, \ + } + +SFP_EEPROM_ATTR(1);SFP_EEPROM_ATTR(2);SFP_EEPROM_ATTR(3);SFP_EEPROM_ATTR(4);SFP_EEPROM_ATTR(5); +SFP_EEPROM_ATTR(6);SFP_EEPROM_ATTR(7);SFP_EEPROM_ATTR(8);SFP_EEPROM_ATTR(9);SFP_EEPROM_ATTR(10); +SFP_EEPROM_ATTR(11);SFP_EEPROM_ATTR(12);SFP_EEPROM_ATTR(13);SFP_EEPROM_ATTR(14);SFP_EEPROM_ATTR(15); +SFP_EEPROM_ATTR(16);SFP_EEPROM_ATTR(17);SFP_EEPROM_ATTR(18);SFP_EEPROM_ATTR(19);SFP_EEPROM_ATTR(20); +SFP_EEPROM_ATTR(21);SFP_EEPROM_ATTR(22);SFP_EEPROM_ATTR(23);SFP_EEPROM_ATTR(24);SFP_EEPROM_ATTR(25); +SFP_EEPROM_ATTR(26);SFP_EEPROM_ATTR(27);SFP_EEPROM_ATTR(28);SFP_EEPROM_ATTR(29);SFP_EEPROM_ATTR(30); +SFP_EEPROM_ATTR(31);SFP_EEPROM_ATTR(32);SFP_EEPROM_ATTR(33);SFP_EEPROM_ATTR(34);SFP_EEPROM_ATTR(35); +SFP_EEPROM_ATTR(36);SFP_EEPROM_ATTR(37);SFP_EEPROM_ATTR(38);SFP_EEPROM_ATTR(39);SFP_EEPROM_ATTR(40); +SFP_EEPROM_ATTR(41);SFP_EEPROM_ATTR(42);SFP_EEPROM_ATTR(43);SFP_EEPROM_ATTR(44);SFP_EEPROM_ATTR(45); +SFP_EEPROM_ATTR(46);SFP_EEPROM_ATTR(47);SFP_EEPROM_ATTR(48);SFP_EEPROM_ATTR(49);SFP_EEPROM_ATTR(50); +SFP_EEPROM_ATTR(51);SFP_EEPROM_ATTR(52);SFP_EEPROM_ATTR(53);SFP_EEPROM_ATTR(54);SFP_EEPROM_ATTR(55); +SFP_EEPROM_ATTR(56); + +static struct bin_attribute *fn8656_bnf_sfp_epprom_attributes[] = { + &sfp1_eeprom_attr, &sfp2_eeprom_attr, &sfp3_eeprom_attr, &sfp4_eeprom_attr, &sfp5_eeprom_attr, + &sfp6_eeprom_attr, &sfp7_eeprom_attr, &sfp8_eeprom_attr, &sfp9_eeprom_attr, &sfp10_eeprom_attr, + &sfp11_eeprom_attr, &sfp12_eeprom_attr, &sfp13_eeprom_attr, &sfp14_eeprom_attr, &sfp15_eeprom_attr, + &sfp16_eeprom_attr, &sfp17_eeprom_attr, &sfp18_eeprom_attr, &sfp19_eeprom_attr, &sfp20_eeprom_attr, + &sfp21_eeprom_attr, &sfp22_eeprom_attr, &sfp23_eeprom_attr, &sfp24_eeprom_attr, &sfp25_eeprom_attr, + &sfp26_eeprom_attr, &sfp27_eeprom_attr, &sfp28_eeprom_attr, &sfp29_eeprom_attr, &sfp30_eeprom_attr, + &sfp31_eeprom_attr, &sfp32_eeprom_attr, &sfp33_eeprom_attr, &sfp34_eeprom_attr, &sfp35_eeprom_attr, + &sfp36_eeprom_attr, &sfp37_eeprom_attr, &sfp38_eeprom_attr, &sfp39_eeprom_attr, &sfp40_eeprom_attr, + &sfp41_eeprom_attr, &sfp42_eeprom_attr, &sfp43_eeprom_attr, &sfp44_eeprom_attr, &sfp45_eeprom_attr, + &sfp46_eeprom_attr, &sfp47_eeprom_attr, &sfp48_eeprom_attr, &sfp49_eeprom_attr, &sfp50_eeprom_attr, + &sfp51_eeprom_attr, &sfp52_eeprom_attr, &sfp53_eeprom_attr, &sfp54_eeprom_attr, &sfp55_eeprom_attr, + &sfp56_eeprom_attr, + NULL +}; + +static const struct attribute_group fn8656_bnf_sfp_group = { .bin_attrs = fn8656_bnf_sfp_epprom_attributes}; + +static int kwai_fpga_probe(struct pci_dev *pdev, + const struct pci_device_id *pci_id) +{ + struct kwai_fpga *fpga; + int err, i = 0; + int status = 0; + uint8_t sw_ver = 0; + + + fpga = kzalloc(sizeof(struct kwai_fpga), GFP_KERNEL); + if (!fpga) + return -ENOMEM; + + fpga->pdev = pdev; + pdev->sysdata = (void *)fpga; + mutex_init(&fpga->lock); + + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR "kwai_fpga: Can't enable device.\n"); + goto err_dev; + } + if (!devm_request_mem_region(&pdev->dev, pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0), + "kwai_fpga")) { + printk(KERN_WARNING "kwai_fpga: Can't request iomem (0x%llx).\n", + (unsigned long long)pci_resource_start(pdev, 0)); + err = -EBUSY; + goto err_disable; + } + pci_set_master(pdev); + pci_set_drvdata(pdev, fpga); + + fpga->mmio = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); + if (!fpga->mmio) { + printk(KERN_ERR "kwai_fpga: ioremap() failed\n"); + err = -EIO; + goto err_disable; + } + + for(i = 1; i <= MAX_PORT_NUM; i++) + { + fpga->chip[i].dev_class = CMIS_ADDR; + fpga->chip[i].byte_len = ONE_ADDR_EEPROM_SIZE; + fpga->chip[i].write_max = I2C_SMBUS_BLOCK_MAX; + fpga->chip[i].slave_addr = SFP_EEPROM_A0_ADDR; + } + status = sysfs_create_group(&pdev->dev.kobj, &fn8656_bnf_sfp_group); + if (status) { + goto err_disable; + } + status = sysfs_create_group(&pdev->dev.kobj, &fn8656_bnf_fpga_group); + if (status) { + goto err_disable; + } + + sfp_task = kthread_run(sfp_poll, (void*)fpga, "sfpd"); + if(!sfp_task){ + pega_print(ERR,"Unable to start kernel thread.\n"); + return -ECHILD; + } + + sw_ver = readl(fpga->mmio + FPGA_SW_VERSION); + if(sw_ver > 0x6) { + fpga->i2c_mode = FPGA_I2C_RD_MODE_BLOCK; + } + else { + fpga->i2c_mode = FPGA_I2C_RD_MODE_BYTE; + } + + return 0; + +err_disable: + pci_disable_device(pdev); +err_dev: + kfree(fpga); + + return err; +} + +static void kwai_fpga_remove(struct pci_dev *pdev) +{ + struct kwai_fpga *fpga = pdev->sysdata; + + if(sfp_task) + { + kthread_stop(sfp_task); + sfp_task = NULL; + } + sysfs_remove_group(&pdev->dev.kobj, &fn8656_bnf_sfp_group); + sysfs_remove_group(&pdev->dev.kobj, &fn8656_bnf_fpga_group); + devm_iounmap(&pdev->dev, fpga->mmio); + pci_disable_device(pdev); + kfree(fpga); +} + +static const struct pci_device_id kwai_fpga_pci_tbl[] = { + { PCI_DEVICE(0x10EE, 0x7021) }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, kwai_fpga_pci_tbl); + +static struct pci_driver kwai_fpga_pci_driver = { + .name = "kwai_fpga", + .id_table = kwai_fpga_pci_tbl, + .probe = kwai_fpga_probe, + .remove = kwai_fpga_remove, + .suspend = NULL, + .resume = NULL, +}; + +module_pci_driver(kwai_fpga_pci_driver); + +module_param(loglevel, uint, 0664); +module_param(enable_sfp_eeprom_cache, uint, 0664); +module_param(enable_sfp_three_batch, uint, 0664); +module_param_string(fpga_debug, fpga_debug, MAX_DEBUG_INFO_LEN, 0664); + +MODULE_AUTHOR("Bao Hengxi "); +MODULE_DESCRIPTION("fn8656_bnf_sfp driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/pegatron_fn8656_bnf_watchdog.c b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/pegatron_fn8656_bnf_watchdog.c new file mode 100644 index 000000000000..d05c899db78d --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/modules/pegatron_fn8656_bnf_watchdog.c @@ -0,0 +1,464 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "linux/kthread.h" +#include +#include +#include +#include +#include +#include +#include "pegatron_pub.h" + +enum watchdog_sensor_sysfs_attributes +{ + ARM, + DISARM, + IS_ARMED, + FEED_DOG, + IDENTIFY, + STATE, + TIMELEFT, + TIMEOUT, + RESET, + WDT_ENABLE, + WDT_DEBUG +}; + +#define MODULE_NAME "pegatron_fn8656_watchdog" +#define ERROR_SUCCESS 0 + +struct kobject *kobj_switch = NULL; + +extern int pegatron_fn8656_bnf_cpld_read(unsigned short cpld_addr, u8 reg); +extern int pegatron_fn8656_bnf_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + + +static uint loglevel = LOG_INFO | LOG_WARNING | LOG_ERR; +static struct kobject * kobj_watchdog_root = NULL; + +unsigned long armed_jiffies; +unsigned long curr_jiffies; +u32 timeout = 32; + +#define WATCHDOG_CPLD_ADDRESS 0x18 + +#define WATCHDOG_CPLD_WDT_REG 0x12 +#define WATCHDOG_CPLD_WDT_ENB 0x1 /*0: Enable 1: Disable*/ +#define WATCHDOG_CPLD_WDT_ENB_MASK 0x1 +#define WATCHDOG_CPLD_WDT_FEED 0x80 /*WDT_CPU_FLAG*/ +#define WATCHDOG_CPLD_WDT_TIMEOUT_MASK 0xe +#define WATCHDOG_CPLD_WDT_TIMEOUT_1S (0 << 1) +#define WATCHDOG_CPLD_WDT_TIMEOUT_3S (1 << 1) +#define WATCHDOG_CPLD_WDT_TIMEOUT_5S (2 << 1) +#define WATCHDOG_CPLD_WDT_TIMEOUT_10S (3 << 1) +#define WATCHDOG_CPLD_WDT_TIMEOUT_30S (4 << 1) +#define WATCHDOG_CPLD_WDT_TIMEOUT_60S (5 << 1) +#define WATCHDOG_CPLD_WDT_TIMEOUT_120S (6 << 1) +#define WATCHDOG_CPLD_WDT_TIMEOUT_180S (7 << 1) + +#define WATCHDOG_CPLD_WDT_TIMELEFT_REG 0x14 + + +#define timeout_set(timeout, set_value) \ +do { \ + set_value &= (~WATCHDOG_CPLD_WDT_TIMEOUT_MASK); \ + if (timeout <= 5) \ + set_value |= (WATCHDOG_CPLD_WDT_TIMEOUT_5S & WATCHDOG_CPLD_WDT_TIMEOUT_MASK); \ + else if (timeout <= 10) \ + set_value |= (WATCHDOG_CPLD_WDT_TIMEOUT_10S & WATCHDOG_CPLD_WDT_TIMEOUT_MASK); \ + else if (timeout <= 30) \ + set_value |= (WATCHDOG_CPLD_WDT_TIMEOUT_30S & WATCHDOG_CPLD_WDT_TIMEOUT_MASK); \ + else if (timeout <= 60) \ + set_value |= (WATCHDOG_CPLD_WDT_TIMEOUT_60S & WATCHDOG_CPLD_WDT_TIMEOUT_MASK); \ + else if (timeout <= 120) \ + set_value |= (WATCHDOG_CPLD_WDT_TIMEOUT_120S & WATCHDOG_CPLD_WDT_TIMEOUT_MASK); \ + else \ + set_value |= (WATCHDOG_CPLD_WDT_TIMEOUT_180S & WATCHDOG_CPLD_WDT_TIMEOUT_MASK); \ +}while(0) + + +static ssize_t bsp_cpu_cpld_write_byte(u8 data, u8 reg) +{ + return pegatron_fn8656_bnf_cpld_write(WATCHDOG_CPLD_ADDRESS, reg, data); +} + +static ssize_t bsp_cpu_cpld_read_byte(u8 *data, u8 reg) +{ + *data = pegatron_fn8656_bnf_cpld_read(WATCHDOG_CPLD_ADDRESS, reg); + return 0; +} + +static ssize_t bsp_sysfs_watchdog_custom_set_attr(struct device *kobject, struct device_attribute *da, const char *buf, size_t count) +{ + int temp = 0; + int ret = ERROR_SUCCESS; + u8 set_value = 0; + u8 get_value = 0; + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + switch (attr->index) + { + case ARM: + { + if (sscanf(buf, "%d", &temp) <= 0) + { + pega_print(ERR, "Format '%s' error, integer expected!", buf); + ret = -EINVAL; + } + else + { + if (0 == temp) + { + set_value |= WATCHDOG_CPLD_WDT_ENB; + pega_print(DEBUG,"watchdog timer disabled!"); + } + else + { + set_value &= (~WATCHDOG_CPLD_WDT_ENB); + pega_print(DEBUG,"watchdog timer enabled!"); + } + + armed_jiffies = jiffies; + if (temp > 255) + temp = 255; + timeout_set(temp, set_value); + timeout = temp; + ret = bsp_cpu_cpld_write_byte(set_value, WATCHDOG_CPLD_WDT_REG); + } + break; + } + case TIMEOUT: + { + if (sscanf(buf, "%d", &temp) <= 0) + { + pega_print(DEBUG, "Format '%s' error, integer expected!", buf); + ret = -EINVAL; + } + else + { + if (temp > 255) + temp = 255; + ret = bsp_cpu_cpld_read_byte(&get_value, WATCHDOG_CPLD_WDT_REG); + set_value = get_value; + timeout_set(temp, set_value); + timeout = temp; + ret = bsp_cpu_cpld_write_byte(set_value, WATCHDOG_CPLD_WDT_REG); + } + break; + } + case DISARM: + { + if (sscanf(buf, "%d", &temp) <= 0) + { + pega_print(DEBUG, "Format '%s' error, integer expected!", buf); + ret = -EINVAL; + } + else + { + timeout_set(temp, set_value); + set_value |= WATCHDOG_CPLD_WDT_ENB; + ret = bsp_cpu_cpld_write_byte(set_value, WATCHDOG_CPLD_WDT_REG); + pega_print(DEBUG,"watchdog timer disabled!"); + } + break; + } + case WDT_ENABLE: + { + if (sscanf(buf, "%d", &temp) <= 0) + { + pega_print(DEBUG, "Format '%s' error, integer expected!", buf); + ret = -EINVAL; + } + else + { + ret = bsp_cpu_cpld_read_byte(&get_value, WATCHDOG_CPLD_WDT_REG); + if(((get_value & WATCHDOG_CPLD_WDT_ENB_MASK) == 1) && (temp == 1)) + { + armed_jiffies = jiffies; + set_value = get_value & (~WATCHDOG_CPLD_WDT_ENB); + ret = bsp_cpu_cpld_write_byte(set_value, WATCHDOG_CPLD_WDT_REG); + pega_print(DEBUG,"watchdog timer enabled!"); + } + else if(((get_value & WATCHDOG_CPLD_WDT_ENB_MASK) == 0) && (temp == 0)) + { + set_value = get_value | WATCHDOG_CPLD_WDT_ENB; + ret = bsp_cpu_cpld_write_byte(set_value, WATCHDOG_CPLD_WDT_REG); + pega_print(DEBUG,"watchdog timer disabled!"); + } + } + break; + } + case FEED_DOG: + case RESET: + { + ret = bsp_cpu_cpld_read_byte(&get_value, WATCHDOG_CPLD_WDT_REG); + armed_jiffies = jiffies; + pega_print(DEBUG,"feed dog!"); + break; + } + default: + { + pega_print(ERR, "Not found attr %d for watchdog", attr->index); + ret = -ENOSYS; + break; + } + } + +exit: + if (ret != ERROR_SUCCESS) + { + count = ret; + } + return count; +} + +static ssize_t bsp_sysfs_watchdog_custom_get_attr(struct device *kobj, struct device_attribute *da, char *buf) +{ + ssize_t index = 0; + int ret = ERROR_SUCCESS; + u8 get_value; + int int_value =0; + //int float_value =0; + unsigned int run_time = 0; + int left_time = 0; + u8 timeout_data[8] = {1, 3, 5, 10, 30, 60, 120, 180}; + + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + + switch(attr->index) + { + case ARM: + case TIMEOUT: + { + ret = bsp_cpu_cpld_read_byte(&get_value, WATCHDOG_CPLD_WDT_REG); + index = scnprintf(buf, PAGE_SIZE, "%d\n", timeout_data[(get_value >> 1) & 0x7]); + break; + } + case WDT_ENABLE: + case IS_ARMED: + { + ret = bsp_cpu_cpld_read_byte(&get_value, WATCHDOG_CPLD_WDT_REG); + if((get_value & WATCHDOG_CPLD_WDT_ENB_MASK) == 1) + { + index = sprintf(buf, "%d\n", 0); + } + else + { + index = sprintf(buf, "%d\n", 1); + } + break; + } + case DISARM: + { + ret = bsp_cpu_cpld_read_byte(&get_value, WATCHDOG_CPLD_WDT_REG); + if((get_value & WATCHDOG_CPLD_WDT_ENB_MASK) == 1) + { + index = scnprintf(buf, PAGE_SIZE, "%d\n", 1); + } + else + { + index = scnprintf(buf, PAGE_SIZE, "%d\n", 0); + } + break; + } + case IDENTIFY: + { + index = scnprintf(buf, PAGE_SIZE, "pegatron_cpld_wdt\n"); + break; + } + case STATE: + { + ret = bsp_cpu_cpld_read_byte(&get_value, WATCHDOG_CPLD_WDT_REG); + if((get_value & WATCHDOG_CPLD_WDT_ENB_MASK) == 1) + { + index = scnprintf(buf, PAGE_SIZE, "inactive\n"); + } + else + { + index = scnprintf(buf, PAGE_SIZE, "active\n"); + } + break; + } + case TIMELEFT: + { +#if 0 + curr_jiffies = jiffies; + + run_time = jiffies_to_msecs(curr_jiffies - armed_jiffies) / 1000; + + left_time = timeout - run_time; + if (left_time < 0) + left_time = 0; +#else + + ret = bsp_cpu_cpld_read_byte(&get_value, WATCHDOG_CPLD_WDT_TIMELEFT_REG); + left_time = get_value; +#endif + + index = sprintf(buf, "%d\n", left_time); + break; + } + case WDT_DEBUG: + { + ret = bsp_cpu_cpld_read_byte(&get_value, WATCHDOG_CPLD_WDT_REG); + int_value = timeout_data[(get_value >> 1) & 0x7]; + + if((get_value & WATCHDOG_CPLD_WDT_ENB_MASK) == 0) + { + index = scnprintf(buf, PAGE_SIZE, "enable: 1\ntimeout:%d\n", int_value); + } + else + { + index = scnprintf(buf, PAGE_SIZE, "enable: 0\ntimeout:%d\n", int_value); + } + index += scnprintf(buf + index, PAGE_SIZE - index, "%s\n", "BSP DEBUG WDT"); + break; + } + default: + { + pega_print(DEBUG, "watchdog attribte %d is invalid\n", attr->index); + break; + } + } +exit: + return index; +} + +//debug node +static SENSOR_DEVICE_ATTR(arm, S_IRUGO|S_IWUSR, bsp_sysfs_watchdog_custom_get_attr, bsp_sysfs_watchdog_custom_set_attr, ARM); +static SENSOR_DEVICE_ATTR(disarm, S_IRUGO|S_IWUSR, bsp_sysfs_watchdog_custom_get_attr, bsp_sysfs_watchdog_custom_set_attr, DISARM); +static SENSOR_DEVICE_ATTR(is_armed, S_IRUGO, bsp_sysfs_watchdog_custom_get_attr, NULL, IS_ARMED); +static SENSOR_DEVICE_ATTR(feed_dog, S_IWUSR, NULL, bsp_sysfs_watchdog_custom_set_attr, FEED_DOG); + +static SENSOR_DEVICE_ATTR(identify, S_IRUGO, bsp_sysfs_watchdog_custom_get_attr, NULL, IDENTIFY); +static SENSOR_DEVICE_ATTR(state, S_IRUGO, bsp_sysfs_watchdog_custom_get_attr, NULL, STATE); +static SENSOR_DEVICE_ATTR(timeleft, S_IRUGO, bsp_sysfs_watchdog_custom_get_attr, NULL, TIMELEFT); +static SENSOR_DEVICE_ATTR(timeout, S_IRUGO|S_IWUSR, bsp_sysfs_watchdog_custom_get_attr, bsp_sysfs_watchdog_custom_set_attr, TIMEOUT); +static SENSOR_DEVICE_ATTR(reset, S_IWUSR, NULL, bsp_sysfs_watchdog_custom_set_attr, RESET); +static SENSOR_DEVICE_ATTR(enable, S_IRUGO|S_IWUSR, bsp_sysfs_watchdog_custom_get_attr, bsp_sysfs_watchdog_custom_set_attr, WDT_ENABLE); + +static SENSOR_DEVICE_ATTR(debug, S_IRUGO, bsp_sysfs_watchdog_custom_get_attr, NULL, WDT_DEBUG); +//BSPMODULE_DEBUG_RW_ATTR_DEF(loglevel, BSP_WATCHDOG_MODULE); + +static struct attribute *watchdog_debug_attributes[] = +{ + &sensor_dev_attr_arm.dev_attr.attr, + &sensor_dev_attr_disarm.dev_attr.attr, + &sensor_dev_attr_is_armed.dev_attr.attr, + &sensor_dev_attr_feed_dog.dev_attr.attr, + + &sensor_dev_attr_identify.dev_attr.attr, + &sensor_dev_attr_state.dev_attr.attr, + &sensor_dev_attr_timeleft.dev_attr.attr, + &sensor_dev_attr_timeout.dev_attr.attr, + &sensor_dev_attr_reset.dev_attr.attr, + &sensor_dev_attr_enable.dev_attr.attr, + &sensor_dev_attr_debug.dev_attr.attr, +// &bspmodule_loglevel.attr, + NULL +}; + +static const struct attribute_group watchdog_switch_attribute_group = +{ + .attrs = watchdog_debug_attributes, +}; + +void watchdog_sysfs_exit(void) +{ + if (kobj_watchdog_root != NULL && kobj_watchdog_root->state_initialized) + { + sysfs_remove_group(kobj_watchdog_root, &watchdog_switch_attribute_group); + kobject_put(kobj_watchdog_root); + } + return; +} + +int watchdog_sysfs_init(void) +{ + int ret = ERROR_SUCCESS; + kobj_watchdog_root = kobject_create_and_add("watchdog", kobj_switch); + + if (kobj_watchdog_root == NULL) + { + pega_print(ERR, "create kobj_watchdog_root failed!"); + ret = -ENOMEM; + goto exit; + } + + ret = sysfs_create_group(kobj_watchdog_root, &watchdog_switch_attribute_group); + +exit: + if (ret != 0) + { + pega_print(ERR, "watchdog sysfs init failed!\n"); + watchdog_sysfs_exit(); + } + + return ret; +} + +int bsp_sysfs_init(void) +{ + int ret = ERROR_SUCCESS; + + kobj_switch = kobject_create_and_add("clx", kernel_kobj->parent); + + + return ret; +} + + +void bsp_sysfs_release_kobjs(void) +{ + if (kobj_switch != NULL) + { + kobject_put(kobj_switch); + } + return; +} + + + +static int __init watchdog_init(void) +{ + int ret = ERROR_SUCCESS; + + bsp_sysfs_init(); + ret = watchdog_sysfs_init(); + if (ERROR_SUCCESS == ret) + { + pega_print(INFO,"pegatron_fn8656_watchdog module finished and success!"); + } + else + { + pega_print(INFO,"pegatron_fn8656_watchdog module finished and failed!"); + } + + return ret; +} + + +static void __exit watchdog_exit(void) +{ + watchdog_sysfs_exit(); + bsp_sysfs_release_kobjs(); + pega_print(INFO,"pegatron_fn8656_watchdog module uninstalled !\n"); + return; +} + +module_param(loglevel,uint,0644); + +MODULE_PARM_DESC(loglevel,"0x01-LOG_ERR,0x02-LOG_WARNING,0x04-LOG_INFO,0x08-LOG_DEBUG"); + + +module_init(watchdog_init); +module_exit(watchdog_exit); + +MODULE_AUTHOR("Junde Zhou "); +MODULE_DESCRIPTION("pegatron watchdog driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/__init__.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/__init__.py new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/common.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/common.py new file mode 100755 index 000000000000..1503bb660594 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/common.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +# +# 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 . + +import sys +import subprocess, threading +import socket +#import signal, select +#from functools import partial +#import queue as Queue + +RUN = False +PASS = 0 +FAIL = 1 + +SOCKET_PORT = 50000 +SOCKET_RECV_BUFFER = 4096 +SOCKET_TIME_OUT = 20 +FILE_LOCK = threading.Lock() +SOCKET_LOCK = threading.Lock() + +CMD_TYPE = ['global', 'device'] +RESPONSE_ERROR_PARAMETER = "Parameters error" +I2C_PREFIX = '/sys/bus/i2c/devices/' + +def doBash(cmd): + status, output = subprocess.getstatusoutput(cmd) + + return status, output + +def doSend(msg, port): + if SOCKET_LOCK.acquire(): + host = socket.gethostname() + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(SOCKET_TIME_OUT) + + try: + s.connect((host, port)) + except Exception: + sys.exit(0) + + s.sendall(msg.encode(encoding='utf-8')) + result = s.recv(SOCKET_RECV_BUFFER).decode(encoding='utf-8') + s.close() + SOCKET_LOCK.release() + return result + +def readFile(path): + if FILE_LOCK.acquire(): + try: + file = open(path) + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + FILE_LOCK.release() + return 'Error' + + value = file.readline().rstrip() + file.close() + FILE_LOCK.release() + + return value + +def writeFile(path, value): + if FILE_LOCK.acquire(): + try: + file = open(path, "r+") + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + FILE_LOCK.release() + return 'Error' + + file.seek(0) + file.write(str(value)) + file.close() + FILE_LOCK.release() + + return "Success" diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/device.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/device.py new file mode 100755 index 000000000000..4cb877d311ea --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/device.py @@ -0,0 +1,471 @@ +#!/usr/bin/env python3 +# +# 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 . + +import common + +TOTAL_PORT_NUM = 56 +SFP_MAX_NUM = 0 +CPLDA_SFP_NUM = 0 +CPLDB_SFP_NUM = 28 +CPLDC_SFP_NUM = 28 +FAN_NUM = 6 +MOTOR_NUM_PER_FAN = 1 +CPLD_NUM = 4 +PSU_NUM = 2 +TEMP_SENSOR_NUM_PER_PSU = 3 + +THERMAL_SENSOR_NUM = 8 +NEED_WORK = 5 +PATH_INDEX = 5 +SYSSENSOR_PREFIX = '/sys/switch/sensor/' +SENSOR_PATH = SYSSENSOR_PREFIX+"temp"+'{}'+'/' + +THERMAL_NODE_COMBINATION = ( + "temp_input", + "temp_max_hyst", + "temp_max", + "temp_alias", + "temp_type", +) + +THERMAL_COMBINATION = { + 0: ( + "temp1_input", + "temp1_max", + "temp1_crit", + "Ambient MAC side", + "LM75BD / Ambient MAC side", + "/sys/bus/i2c/devices/4-0070/hwmon/hwmon4/", + ), + 1: ( + "temp2_input", + "temp2_max", + "temp2_crit", + "Ambient MAC", + "ASIC", + "/sys/bus/i2c/devices/4-0070/hwmon/hwmon4/", + ), + 2: ( + "temp3_input", + "temp3_max", + "temp3_crit", + "Ambient FAN", + "LM75BD / Ambient FAN", + "/sys/bus/i2c/devices/4-0070/hwmon/hwmon4/", + ), + 3: ( + "temp1_input", + "temp1_max", + "temp1_max_hyst", + "Ambient NPU", + "LM75BD / Ambient NPU", + "/sys/bus/i2c/devices/1-004a/hwmon/hwmon1/", + ), + 4: ( + "temp2_input", + "temp2_max", + "temp2_crit", + "CPU", + "Core 0", + "/sys/devices/platform/coretemp.0/hwmon/hwmon0/" + ), + 5: ( + "temp3_input", + "temp3_max", + "temp3_crit", + "CPU", + "Core 1", + "/sys/devices/platform/coretemp.0/hwmon/hwmon0/" + ), + 6: ( + "temp4_input", + "temp4_max", + "temp4_crit", + "CPU", + "Core 2", + "/sys/devices/platform/coretemp.0/hwmon/hwmon0/" + ), + 7: ( + "temp5_input", + "temp5_max", + "temp5_crit", + "CPU", + "Core 3", + "/sys/devices/platform/coretemp.0/hwmon/hwmon0/" + ), +} + +DEVICE_BUS = {'cpld': ['6-0074', '7-0075', '8-0076'], + 'fan': ['4-0070'], + 'psu': ['2-0058', '3-0059'], + 'status_led': ['6-0074'], + 'fp_0_led': ['7-0075'], + 'fp_1_led': ['8-0076'] + } +DEVICE_BUS_FAN_NODE = ['4-0070/hwmon/hwmon4/'] +DEVICE_BUS_PSU_NODE = ['2-0058/hwmon/hwmon2/', '3-0059/hwmon/hwmon3/'] + +class DeviceThread(common.threading.Thread): + def __init__(self,threadname, q): + common.threading.Thread.__init__(self,name = threadname) + self.queue = q + + def run(self): + while common.RUN: + message = self.queue.get() + self.onMessage(message) + + def onMessage(self, message): + """ + Commands: + led : Led controls + locate : Blink locator LED + sensors : Read HW monitors + """ + + if len(message.command) < 1: + result = self.onMessage.__doc__ + else: + if message.command[0] == 'init': + result = deviceInit() + elif message.command[0] == 'led': + result = ledControls(message.command[1:]) + elif message.command[0] == 'locate': + locatethread = common.threading.Thread(target = locateDeviceLed) + locatethread.start() + result = 'Success' + elif message.command[0] == 'sensors': + result = getSensors() + else: + result = self.onMessage.__doc__ + + if (message.callback is not None): + message.callback(result) + +STATUS_ALERT = {'fan': ['wrongAirflow_alert', 'outerRPMOver_alert', 'outerRPMUnder_alert', 'outerRPMZero_alert', + 'innerRPMOver_alert', 'innerRPMUnder_alert', 'innerRPMZero_alert', 'notconnect_alert'], + 'psu': ['vout_over_voltage', 'iout_over_current_fault', 'iout_over_current_warning', + 'iput_over_current_warning', 'iput_insufficient', 'temp_over_temp_fault', 'temp_over_temp_warning']} +class PlatformStatusThread(common.threading.Thread): + def __init__(self,threadname, timer): + self.running = True + common.threading.Thread.__init__(self,name = threadname) + self.timer = timer + self.fan_led_status = 'off' + self.psu_led_status = 'off' + + def run(self): + while common.RUN: + self.checkPlatformStatus() + common.time.sleep(self.timer) + + def checkPlatformStatus(self): + total_result = common.PASS + total_result += self.checkFanStatus() + total_result += self.checkPsuStatus() + + def checkFanStatus(self): + fan_result = common.PASS + fan_bus = DEVICE_BUS['fan'] + fan_bus_nodes = DEVICE_BUS_FAN_NODE + fan_alert = STATUS_ALERT['fan'] + fan_led = LED_COMMAND['fan_led'] + fan_normal = 'green' + fan_abnormal = 'blink_amber' + led_bus = DEVICE_BUS['status_led'] + led_path = common.I2C_PREFIX + led_bus[0] + '/' + LED_NODES[3] + + status, output = common.doBash("ls " + common.I2C_PREFIX) + if output.find(fan_bus[0]) != -1: + for num in range(0,FAN_NUM): + for alert_type in fan_alert: + path = common.I2C_PREFIX + fan_bus_nodes[0] + "/fan" + str(num+1) + "_" + alert_type + result = common.readFile(path) + if result != 'Error': + fan_result += int(result) + if fan_result != common.PASS: + if self.fan_led_status != fan_abnormal: + common.writeFile(led_path, fan_led[fan_abnormal]) + self.fan_led_status = fan_abnormal + common.syslog.syslog(common.syslog.LOG_ERR, 'FAN Status Error !!!') + return common.FAIL + + if self.fan_led_status != fan_normal: + common.writeFile(led_path, fan_led[fan_normal]) + self.fan_led_status = fan_normal + common.syslog.syslog(common.syslog.LOG_ERR, 'FAN Status Normal !!!') + return common.PASS + + def checkPsuStatus(self): + psu_result = common.PASS + psu_bus = DEVICE_BUS['psu'] + psu_bus_nodes = DEVICE_BUS_PSU_NODE + psu_alert = STATUS_ALERT['psu'] + psu_led = LED_COMMAND['pwr_led'] + psu_normal = 'green' + psu_abnormal = 'blink_amber' + led_bus = DEVICE_BUS['status_led'] + led_path = common.I2C_PREFIX + led_bus[0] + '/' + LED_NODES[1] + + status, output = common.doBash("ls " + common.I2C_PREFIX) + if output.find(psu_bus[0]) != -1 and output.find(psu_bus[1]) != -1: + for nodes in psu_bus_nodes: + for alert_type in psu_alert: + path = common.I2C_PREFIX + nodes + "/" + alert_type + result = common.readFile(path) + if result != 'Error': + psu_result += int(result) + if psu_result != common.PASS: + if self.psu_led_status != psu_abnormal: + common.writeFile(led_path, psu_led[psu_abnormal]) + self.psu_led_status = psu_abnormal + common.syslog.syslog(common.syslog.LOG_ERR, 'PSU Status Error !!!') + return common.FAIL + + if self.psu_led_status != psu_normal: + common.writeFile(led_path, psu_led[psu_normal]) + self.psu_led_status = psu_normal + common.syslog.syslog(common.syslog.LOG_ERR, 'PSU Status Normal !!!') + return common.PASS + +LED_COMMAND = {'sys_led': {'green':'1', 'amber':'3', 'off':'0', 'blink_green':'4', 'blink_amber':'6'}, + 'pwr_led': {'green':'1', 'amber':'3', 'off':'0', 'blink_green':'4', 'blink_amber':'6'}, + 'loc_led': {'on':'0', 'off':'1', 'blink':'2'}, + 'fan_led': {'green':'1', 'amber':'3', 'off':'0', 'blink_green':'4', 'blink_amber':'6'}, + 'cpld_allled_ctrl': {'off':'3', 'mix':'1', 'amber':'2', 'normal':'0'}, + 'fp_0_led': {'disable':'0', 'enable':'1'}, + 'fp_1_led': {'disable':'0', 'enable':'1'}, + 'serial_led_enable': {'disable':'0', 'enable':'1'}} +LED_NODES = ['sys_led', 'pwr_led', 'loc_led', 'fan_led', "cpld_allled_ctrl", "serial_led_enable"] +def ledControls(args): + """ + Commands: + set : Set led config + get : Get led status + """ + COMMAND_TYPE = ['set', 'get'] + if len(args) < 1 or args[0] not in COMMAND_TYPE: + return ledControls.__doc__ + + result = setGetLed(args[0:]) + + return result + +def setGetLed(args): + """ + Commands: + sys_led : System status led [green/amber/off/blink_green/blink_amber] + pwr_led : Power status led [green/amber/off/blink_green/blink_amber] + loc_led : Locator led [on/off/blink] + fan_led : Fan led [green/amber/off/blink_green/blink_amber] + """ + if len(args) < 3 or args[1] not in LED_COMMAND: + return setGetLed.__doc__ + + for i in range(0,len(LED_NODES)): + if args[1] == LED_NODES[i]: + led_bus = DEVICE_BUS['status_led'] + path = common.I2C_PREFIX + led_bus[0] + '/' + LED_NODES[i] + if args[1] == 'fp_0_led': + led_bus = DEVICE_BUS['fp_0_led'] + path = common.I2C_PREFIX + led_bus[0] + '/' + 'serial_led_enable' + if args[1] == 'fp_1_led': + led_bus = DEVICE_BUS['fp_1_led'] + path = common.I2C_PREFIX + led_bus[0] + '/' + 'serial_led_enable' + command = LED_COMMAND[args[1]] + + if args[0] == 'set': + if args[2] in command: + data = command[args[2]] + result = common.writeFile(path, data) + else: + result = setGetLed.__doc__ + else: + result = common.readFile(node) + if result != "Error": + result = list (command.keys()) [list (command.values()).index (result)] + + return result + +def locateDeviceLed(): + setGetLed(['set', 'loc_led', 'blink']) + common.time.sleep(20) + setGetLed(['set', 'loc_led', 'off']) + +SENSORS_PATH = common.I2C_PREFIX + '4-0070/hwmon/hwmon4/' #maurice arbiter +SENSORS_NODES = {'fan_rpm': ['_inner_rpm', '_outer_rpm'], + 'fan_vol': ['ADC1_vol', 'ADC2_vol','ADC3_vol', 'ADC4_vol','ADC5_vol', 'ADC6_vol', 'ADC7_vol', 'ADC8_vol'], + 'temp':['temp1_input', 'temp2_input', 'temp3_input'], + 'fan_alert':['_status_alert', '_wrongAirflow_alert', '_outerRPMOver_alert', '_outerRPMUnder_alert', + '_outerRPMZero_alert', '_innerRPMOver_alert', '_innerRPMUnder_alert', '_innerRPMZero_alert', '_notconnect_alert'], + 'vol_alert':['_under_alert', '_over_alert'], + 'temp_alert':['lm75_48_temp_alert', 'lm75_49_temp_alert', 'lm75_4a_temp_alert', 'sa56004x_Ltemp_alert', 'sa56004x_Rtemp_alert']} +SENSORS_TYPE = {'fan_rpm': ['Inner RPM', 'Outer RPM'], + 'fan_vol': ['P0.1', 'P0.2','P0.6', 'P0.7','P1.0', 'P1.1', 'P1.2', 'P1.6']} +def getSensors(): + string = '' + # Firmware version + val = common.readFile(SENSORS_PATH + 'mb_fw_version') + string = '\n' + "MB-SW Version: " + val + + val = common.readFile(SENSORS_PATH + 'fb_fw_version') + string += '\n' + "FB-SW Version: " + val + + # Fan + string += getFan() + + # HW Monitor + #string += '\n' + getHWMonitor() + + # Voltage + string += '\n' + getVoltage() + + return string + +def getFan(): + string = '' + for i in range(0,FAN_NUM): + # Status + result = getFanStatus(i) + string += '\n\n' + "FAN " + str(i+1) + ": " + result + + if result == 'Disconnect': + continue + + # Alert + result = getFanAlert(i) + string += '\n' + " Status: " + result + + # Inner RPM + result = getFanInnerRPM(i) + string += '\n' + " Inner RPM: " + result.rjust(10) + " RPM" + + # Outer RPM + result = getFanOuterRPM(i) + string += '\n' + " Outer RPM: " + result.rjust(10) + " RPM" + + return string + +def getFanStatus(num): + val = common.readFile(SENSORS_PATH + 'fan' + str(num+1) + '_present') + if val != 'Error': + if (int(val, 16) & 0x1) == 0x1: + result = 'Connect' + else: + result = 'Disconnect' + else: + result = val + return result + +def getFanAlert(num): + alert = 0 + alert_types = SENSORS_NODES['fan_alert'] + for alert_type in alert_types: + val = common.readFile(SENSORS_PATH + 'fan' + str(num+1) + alert_type) + if val != 'Error': + alert += int(val, 16) + else: + return val + + if alert > 0: + result = 'Warning' + else: + result = 'Normal' + + return result + +def getFanInnerRPM(num): + return common.readFile(SENSORS_PATH + 'fan' + str(num+1) + '_inner_rpm') + +def getFanOuterRPM(num): + return common.readFile(SENSORS_PATH + 'fan' + str(num+1) + '_outer_rpm') + +def getHWMonitor(): + string = '' + try: + temp_type = SENSORS_TYPE['temp'] + except Exception: + return string + + for types in temp_type: + val = common.readFile(SENSORS_PATH + types) + val_alert = common.readFile(SENSORS_PATH + types + '_alert') + if val_alert != 'Error': + if int(val_alert, 16) == 1: + alert = 'Warning' + else: + alert = 'Normal' + else: + alert = val_alert + string += '\n' + types + ": " + val + " C" + " ( " + alert + " )" + + return string + +def getVoltage(): + string = '' + nodes = SENSORS_NODES['fan_vol'] + types = SENSORS_TYPE['fan_vol'] + for i in range(0,len(nodes)): + val = common.readFile(SENSORS_PATH + nodes[i]) + alert = getVoltageAlert(i) + string += '\n' + types[i] + ": " + val + " V ( " + alert + " )" + + return string + +def getVoltageAlert(num): + alert = 0 + nodes = SENSORS_NODES['vol_alert'] + for node in nodes: + val = common.readFile(SENSORS_PATH + 'ADC' + str(num+1) + node) + if val != 'Error': + alert += int(val, 16) + else: + return val + + if alert > 0: + result = 'Warning' + else: + result = 'Normal' + + return result + +DEVICE_INIT = {'led': [['set', 'sys_led', 'green'], ['set', 'pwr_led', 'green'], ['set', 'fan_led', 'green'], ['set', 'cpld_allled_ctrl', 'normal'], ['set', 'serial_led_enable', 'enable'], ['set', 'fp_0_led', 'enable'], ['set', 'fp_1_led', 'enable']]} +def deviceInit(): + # Set led + for i in range(0,len(DEVICE_INIT['led'])): + setGetLed(DEVICE_INIT['led'][i]) + + cpld_bus = DEVICE_BUS['cpld'] + # Set QSFP reset to normal + for x in range(SFP_MAX_NUM, TOTAL_PORT_NUM): + if x < CPLDB_SFP_NUM: + bus = cpld_bus[1] + else: + bus = cpld_bus[2] + + path = common.I2C_PREFIX + bus + '/sfp' + str(x+1) + '_reset' + common.writeFile(path, "0") + + # Set QSFP lpmode to high + for x in range(SFP_MAX_NUM, TOTAL_PORT_NUM): + if x < CPLDB_SFP_NUM: + bus = cpld_bus[1] + else: + bus = cpld_bus[2] + + path = common.I2C_PREFIX + bus + '/sfp' + str(x+1) + '_lowpower' + common.writeFile(path, "0") + + return diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/fwupdates.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/fwupdates.py new file mode 100644 index 000000000000..6d7416418c90 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/fwupdates.py @@ -0,0 +1,356 @@ +#!/usr/bin/env python + +######################################################################## +# Clounix +# +# Module contains an implementation of SONiC Platform Base API and +# provides the Components' (e.g., BIOS, CPLD, FPGA, etc.) available in +# the platform +# +######################################################################## + +try: + import os + import re +# from .helper import APIHelper + import helper +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +BIOS_QUERY_VERSION_COMMAND = "dmidecode -s bios-version" + + +class Component(): + """Clounix Platform-specific Component class""" + def __init__(self, component_index,component_conf): + self.__index = component_index + self.__conf = component_conf + self.__api_helper = helper.APIHelper() + self.set_fw_state(False) + print("%s install fw" % self.get_name()) + self.auto_updates() + + def set_fw_state(self, state): + self.__fw_state = state + return + + def get_fw_state(self): + return self.__fw_state + + def get_version(self): + """ + Retrieves the name of the component + + Returns: + A string containing the name of the component + """ + return self.__conf[self.__index]['version'] + + def auto_updates(self): + if self.get_name() == 'BIOS': + return False + #CPLD main borad is same verson for A/B/C and skip CPLD A/B + if self.get_name() == 'CPLD-A': + return False + if self.get_name() == 'CPLD-B': + return False + + file_ext_dist = {'MCU-MB':'.efm8', 'MCU-FB':'.efm8', 'CPLD-A':'.vme', 'CPLD-B':'.vme','CPLD-C':'.vme','CPLD-D':'.vme','FPGA':'.bin', 'BIOS':'.bin'} + file_name_dist = {'MCU-MB':'mcu_mb', 'MCU-FB':'mcu_fb', 'CPLD-A':'cpld_mb', 'CPLD-B':'cpld_mb','CPLD-C':'cpld_mb','CPLD-D':'cpld_cpu','FPGA':'fpga_mb', 'BIOS':'bios_cpu'} + + version = ''.join(self.get_version()).replace('.', '_') + image_path = '/usr/local/bin/clounix/pegatron_fn8656_{}_v{}{}'.format(file_name_dist[self.get_name()], version, file_ext_dist[self.get_name()]) + #print("fw updates %s.%s.%s.%s.file %d" %(self.get_name(), self.get_firmware_version(), self.get_version(), image_path, os.path.isfile(image_path))) + if not os.path.isfile(image_path): + print("ERROR: File {} doesn't exist or is not a file".format(image_path)) + return False + if not self.check_image_validity(image_path): + return False + + self.set_fw_state(True) + self.__fw_image_path = image_path + + return True + + def get_name(self): + """ + Retrieves the name of the component + + Returns: + A string containing the name of the component + """ + return self.__conf[self.__index]['name'] + + def get_description(self): + """ + Retrieves the description of the component + + Returns: + A string containing the description of the component + """ + return self.__conf[self.__index]['description'] + + def get_version_path(self): + """ + Retrieves the version path of the component + + Returns: + A string containing the version path of the component + """ + return self.__conf[self.__index]['firmware_version_path'] + + def get_hw_version_path(self): + """ + Retrieves the version path of the component + + Returns: + A string containing the version path of the component + """ + return self.__conf[self.__index]['hw_version_path'] + """ + def get_hw_version(self): + hw_version = 'N/A' + attr_rv = self.__api_helper.read_one_line_file(self.get_hw_version_path()) + if attr_rv != None: + hw_version = attr_rv + else: + if self.get_name() == 'BIOS': + ret,bios_ver = self.__api_helper.run_command(BIOS_QUERY_VERSION_COMMAND) + if ret: + if bios_ver: + hw_version = re.findall('\d',bios_ver)[0] + return hw_version + """ + + + def get_firmware_version(self): + """ + Retrieves the firmware version of the component + + Returns: + A string containing the firmware version of the component + """ + firmware_version = 'N/A' + attr_rv = self.__api_helper.read_one_line_file(self.get_version_path()) + if attr_rv != None: + firmware_version = attr_rv + else: + if self.get_name() == 'BIOS': + ret,bios_ver = self.__api_helper.run_command(BIOS_QUERY_VERSION_COMMAND) + if ret: + if bios_ver: + firmware_version = bios_ver + return firmware_version + + def get_hw_version(self): + return self.get_firmware_version() + + def check_file_validity(self, image_path): + """ + Check installation file validation + + Returns: + bool: True if installation file is valid, False if not + """ + + if not os.path.isfile(image_path): + print("ERROR: File {} doesn't exist or is not a file".format(image_path)) + return False + file_ext_dist = {'MCU-MB':'.efm8', 'MCU-FB':'.efm8', 'CPLD-A':'.vme', 'CPLD-B':'.vme','CPLD-C':'.vme','CPLD-D':'.vme','FPGA':'.bin', 'BIOS':'.bin'} + name_list = os.path.splitext(image_path) + image_ext_name = file_ext_dist[self.get_name()] + if image_ext_name is not None: + if name_list[1] != image_ext_name: + print("ERROR: Extend name of file {} is wrong. Image for {} should have extend name {}".format(image_path, self.get_name(), image_ext_name)) + return False + file_name_dist = {'MCU-MB':'mcu_mb', 'MCU-FB':'mcu_fb', 'CPLD-A':'cpld_mb', 'CPLD-B':'cpld_mb','CPLD-C':'cpld_mb','CPLD-D':'cpld_cpu', 'FPGA':'fpga_mb', 'BIOS':'bios_cpu'} + image_key_name = file_name_dist[self.get_name()] + if image_key_name is not None: + if not image_key_name in image_path: + print("ERROR: File name of file {} is wrong. Image for {} should have key name {}".format(image_path, self.get_name(), image_key_name)) + return False + + return True + + def version_calc_value(self, version): + """ + transfer version string to value, eg 1.2.3.4 tranfer to 1 * 100 + 2 *1 + 3 * 0.01 + 4 * 0.0001 + + Returns: + version value + """ + version_split = version.split('.') + sum = 0 + factor=100 + for val in version_split: + sum += float(val) * factor + factor = factor * 0.01 + return sum + + def check_version_validity(self, image_path): + """ + Check installation version validation + + Returns: + bool: True if installation version is valid, False if not + """ + + run_version = self.get_firmware_version() + + install_version = re.findall(r'v(.+?)\.', image_path) + if install_version: + version = ''.join(install_version).replace('_', '.') + else: + print("please make sure installation version is from released version like as _V0_10.xxx") + return False + + version_val = self.version_calc_value(version) + run_version_val = self.version_calc_value(run_version) + if version_val == run_version_val: + print("installation version %.2f is same as runing version %.2f." % (version_val, run_version_val)) + return False + + return True + + def check_image_validity(self, image_path): + """ + Check installation image validation + + Returns: + bool: True if installation is valid, False if not + """ + + if not self.check_file_validity(image_path): + return False + + if not self.check_version_validity(image_path): + return False + + return True + + def install_firmware(self, img_path): + """ + Installs firmware to the component + + Args: + image_path: A string, path to firmware image + + Returns: + A boolean, True if install was successful, False if not + """ + image_path = self.__fw_image_path + if self.get_name() == 'BIOS': + cmd = "/usr/local/bin/install_firmware.sh bios "+ image_path + elif self.get_name() == 'MCU-MB': + cmd = "/usr/local/bin/install_firmware.sh mcu-mb "+ image_path + elif self.get_name() == 'MCU-FB': + cmd = "/usr/local/bin/install_firmware.sh mcu-fb "+ image_path + elif self.get_name() == 'CPLD-A' or self.get_name() == 'CPLD-B' or self.get_name() == 'CPLD-C': + cmd = "/usr/local/bin/install_firmware.sh cpld-mb "+ image_path + elif self.get_name() == 'CPLD-D': + cmd = "/usr/local/bin/install_firmware.sh cpld-cpu "+ image_path + elif self.get_name() == 'FPGA': + cmd = "/usr/local/bin/install_firmware.sh fpga "+ image_path + else: + print(self.get_name() + 'frimware upgrade did not implement') + + os.system(cmd) + print("image install. %s......" % cmd) + return True + + def get_presence(self): + """ + Retrieves the presence of the device + + Returns: + bool: True if device is present, False if not + """ + return True + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + + Returns: + string: Model/part number of device + """ + model = 'N/A' + return model + + def get_serial(self): + """ + Retrieves the serial number of the device + + Returns: + string: Serial number of device + """ + serial = 'N/A' + return serial + + def get_status(self): + """ + Retrieves the operational status of the device + + Returns: + A boolean value, True if device is operating properly, False if not + """ + return self.get_presence() + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return -1 + + def is_replaceable(self): + """ + Indicate whether Fan is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False + +class Fwupdates(): + """Clounix Platform-specific Fwupdates class""" + def __init__(self): + self.__api_helper = helper.APIHelper() + chassis_conf = self.__api_helper.get_attr_conf("chassis") + self.__conf = chassis_conf + self._component_list = [] + + self.__num_of_components = len(chassis_conf['components']) + # Initialize COMPONENT + for index in range(0, self.__num_of_components): + component = Component(index,component_conf=chassis_conf['components']) + self._component_list.append(component) + + # check whether firmware should be udpated or not + for index in range(0, self.__num_of_components): + fw_state = self._component_list[index].get_fw_state() + #reboot full system for firmware upgrade + if fw_state : + self._component_list[index].install_firmware("None") + + + # check whether reboot or not when firmware upgrade is done + for index in range(0, self.__num_of_components): + fw_state = self._component_list[index].get_fw_state() + #reboot full system for firmware upgrade + if fw_state : + print("#############reboot system and update firmware") + os.system('sudo reboot') + +def fwupdate(): + + if os.path.isfile("/usr/local/bin/fw_updates_identify"): + return + + Fwupdates() + return +""" +if __name__=='__main__': + fwupdate() +""" diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/helper.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/helper.py new file mode 100644 index 000000000000..eed5d5482954 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/helper.py @@ -0,0 +1,156 @@ +import os +import struct +import subprocess +import json +from mmap import * + +from sonic_py_common import device_info + +HOST_CHK_CMD = "docker > /dev/null 2>&1" +EMPTY_STRING = "" + +HOST_PLATFORM_JSON = '/usr/share/sonic/device/x86_64-pegatron_fn8656_bnf-r0/platform.json' +PMON_PLATFORM_JSON = '/usr/share/sonic/platform/platform.json' + +class APIHelper(): + + def __init__(self): + (self.platform, self.hwsku) = device_info.get_platform_and_hwsku() + + def is_host(self): + return os.system(HOST_CHK_CMD) == 0 + + def pci_get_value(self, resource, offset): + status = True + result = "" + try: + fd = os.open(resource, os.O_RDWR) + mm = mmap(fd, 0) + mm.seek(int(offset)) + read_data_stream = mm.read(4) + result = struct.unpack('I', read_data_stream) + except Exception: + status = False + return status, result + + def run_command(self, cmd): + status = True + result = "" + try: + p = subprocess.Popen( + cmd, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err == '': + result = raw_data.strip() + except Exception: + status = False + return status, result + + def run_interactive_command(self, cmd): + try: + os.system(cmd) + except Exception: + return False + return True + + def read_txt_file(self, file_path): + try: + with open(file_path, 'r') as fd: + data = fd.read() + fd.close() + return data.strip() + except IOError: + pass + return None + + def read_one_line_file(self, file_path): + try: + with open(file_path, 'r') as fd: + data = fd.readline() + fd.close() + return data.strip() + except IOError: + pass + return None + + def write_txt_file(self, file_path, value): + try: + with open(file_path, 'w') as fd: + fd.write(str(value)) + fd.close() + except IOError: + return False + return True + + def get_cpld_reg_value(self, getreg_path, register): + cmd = "echo {1} > {0}; cat {0}".format(getreg_path, register) + status, result = self.run_command(cmd) + return result if status else None + + def ipmi_raw(self, netfn, cmd): + status = True + result = "" + try: + cmd = "ipmitool raw {} {}".format(str(netfn), str(cmd)) + p = subprocess.Popen( + cmd, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err == '': + result = raw_data.strip() + else: + status = False + except Exception: + status = False + return status, result + + def ipmi_fru_id(self, id, key=None): + status = True + result = "" + try: + cmd = "ipmitool fru print {}".format(str( + id)) if not key else "ipmitool fru print {0} | grep '{1}' ".format(str(id), str(key)) + + p = subprocess.Popen( + cmd, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err == '': + result = raw_data.strip() + else: + status = False + except Exception: + status = False + return status, result + + def ipmi_set_ss_thres(self, id, threshold_key, value): + status = True + result = "" + try: + cmd = "ipmitool sensor thresh '{}' {} {}".format( + str(id), str(threshold_key), str(value)) + p = subprocess.Popen( + cmd, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err == '': + result = raw_data.strip() + else: + status = False + except Exception: + status = False + return status, result + + def get_attr_conf(self, attr): + """ + Retrieves the json object from json file path + + Returns: + A json object + """ + json_path = '' + if self.is_host() is True: + json_path = HOST_PLATFORM_JSON + else : + json_path = PMON_PLATFORM_JSON + with open(json_path, 'r') as f: + json_data = json.load(f) + + return json_data[attr] diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/main.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/main.py new file mode 100755 index 000000000000..e4029ada9d27 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/pegaProcess/main.py @@ -0,0 +1,610 @@ +#!/usr/bin/env python3 +# +# 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 . + +import common, device +#import fwupdates +import os + +HOST = '' +SOCKET_LIST = [] +SOCKET_MAX_CLIENT = 10 +QUEUES = [] +THREADS = [] +FUNCS = {} + +class GlobalThread(common.threading.Thread): + def __init__(self,threadname, q): + common.threading.Thread.__init__(self,name = threadname) + self.queue = q + + def run(self): + while common.RUN: + message = self.queue.get() + self.onMessage(message) + + def onMessage(self, message): + + """ + Commands: + uninstall : Uninstall platform drivers + """ + if len(message.command) < 1: + result = self.onMessage.__doc__ + else: + if message.command[0] == 'uninstall': + common.RUN = False + doUninstall() + result = 'Success' + else: + result = self.onMessage.__doc__ + if (message.callback is not None): + message.callback(result) + +class messageObject(object): + def __init__(self, command, callback): + super(messageObject, self).__init__() + self.command = command + self.callback = callback + +def callback(sock, result): + sock.sendall(result.encode(encoding='utf-8')) + +def messageHandler(): + server_socket = common.socket.socket(common.socket.AF_INET, common.socket.SOCK_STREAM) + server_socket.setsockopt(common.socket.SOL_SOCKET, common.socket.SO_REUSEADDR, 1) + server_socket.bind((HOST, common.SOCKET_PORT)) + server_socket.listen(SOCKET_MAX_CLIENT) + SOCKET_LIST.append(server_socket) + + while(common.RUN): + ready_to_read,ready_to_write,in_error = common.select.select(SOCKET_LIST,[],[],0) + for sock in ready_to_read: + if sock == server_socket: + sockfd, addr = server_socket.accept() + SOCKET_LIST.append(sockfd) + else: + try: + data = sock.recv(common.SOCKET_RECV_BUFFER).decode(encoding='utf-8') + if data: + cb = common.partial(callback, sock) + cmdlist = data.split() + + if cmdlist[0] not in common.CMD_TYPE: + callback(sock, 'Fail') + continue + + msg = messageObject(cmdlist[1:], cb) + FUNCS[cmdlist[0]].put(msg) + continue + else: + if sock in SOCKET_LIST: + SOCKET_LIST.remove(sock) + except: + raise + continue + common.time.sleep(0.2) + + server_socket.close() + +KERNEL_MODULE = [ +'clounix_sysfs_main', +'i2c_dev', +'i2c-mux', +'i2c-mux-pca9641', +'i2c-mux-pca954x force_deselect_on_exit=1', +'lm75', +'pegatron_fn8656_bnf_cpld loglevel=7 requirement_flag=1', +'pegatron_hwmon_mcu loglevel=7', +'pegatron_fn8656_bnf_psu loglevel=7', +'pegatron_fn8656_bnf_sfp loglevel=7', +'pegatron_fn8656_bnf_watchdog loglevel=7', +'clounix_watchdog'] +#'pegatron_fn8656_bnf_ixgbe'] + +def checkDriver(): + for i in range(0, len(KERNEL_MODULE)): + status, output = common.doBash("lsmod | grep " + KERNEL_MODULE[i]) + if status: + common.doBash("modprobe " + KERNEL_MODULE[i]) + return + +i2c_topology_dict=[ + {'bus': "i2c-0", 'driver' : "pca9641", 'address': "0x71"}, + {'bus': "i2c-1", 'driver' : "fn8656_bnf_cpld", 'address': "0x18"}, + {'bus': "i2c-1", 'driver' : "pca9545", 'address': "0x72"}, + {'bus': "i2c-1", 'driver' : "pca9548", 'address': "0x73"}, + {'bus': "i2c-1", 'driver' : "lm75b", 'address': "0x4a"}, + {'bus': "i2c-2", 'driver' : "fn8656_bnf_psu", 'address': "0x58"}, + {'bus': "i2c-3", 'driver' : "fn8656_bnf_psu", 'address': "0x59"}, + {'bus': "i2c-4", 'driver' : "pega_hwmon_mcu", 'address': "0x70"}, + {'bus': "i2c-6", 'driver' : "fn8656_bnf_cpld", 'address': "0x74"}, + {'bus': "i2c-7", 'driver' : "fn8656_bnf_cpld", 'address': "0x75"}, + {'bus': "i2c-8", 'driver' : "fn8656_bnf_cpld", 'address': "0x76"}, + {'bus': "i2c-9", 'driver' : "24c02", 'address': "0x54"} +] +SOFT_LINK_NODE_TYPE = ['sensor','transceiver','psu','fan','cpld','syseeprom','sysled','watchdog','fpga'] +TRANSCIEVER_PREFIX = '/sys/switch/transceiver/' +PSU_PREFIX = '/sys/switch/psu/' +FAN_PREFIX = '/sys/switch/fan/' +CPLD_PREFIX = '/sys/switch/cpld/' +SYSEEPROM_PREFIX = '/sys/switch/syseeprom/' +SYSLED_PREFIX = '/sys/switch/sysled/' +WATCHDOG_PREFIX = '/sys/switch/watchdog/' +FPGA_PREFIX = '/sys/switch/fpga/' +SENSOR_PREFIX = '/sys/switch/sensor/' + +FPGA_TOP_NODE_DICT = [ + {'src_node':'/sys/module/pegatron_fn8656_bnf_sfp/parameters/loglevel', 'dest_node':FPGA_PREFIX+'loglevel'}, + {'src_node':'/sys/module/pegatron_fn8656_bnf_sfp/parameters/fpga_debug', 'dest_node':FPGA_PREFIX+'debug'}, + {'src_node':'/sys/devices/pci0000:00/0000:00:1c.4/0000:0a:00.0/'+'fpga_chip_num', 'dest_node':FPGA_PREFIX+'num'}, + {'src_node':'/sys/devices/pci0000:00/0000:00:1c.4/0000:0a:00.0/'+'fpga_alias', 'dest_node':FPGA_PREFIX+'alias'}, + {'src_node':'/sys/devices/pci0000:00/0000:00:1c.4/0000:0a:00.0/'+'fpga_type', 'dest_node':FPGA_PREFIX+'type'}, + {'src_node':'/sys/devices/pci0000:00/0000:00:1c.4/0000:0a:00.0/'+'fpga_hw_version', 'dest_node':FPGA_PREFIX+'hw_version'}, + {'src_node':'/sys/devices/pci0000:00/0000:00:1c.4/0000:0a:00.0/'+'fpga_sw_version', 'dest_node':FPGA_PREFIX+'board_version'}, +] +SYSEEPROM_TOP_NODE_DICT = [ + {'src_node':'/sys/module/m24c02/parameters/loglevel', 'dest_node':SYSEEPROM_PREFIX+'loglevel'}, + {'src_node':'/sys/module/m24c02/parameters/debug', 'dest_node':SYSEEPROM_PREFIX+'debug'}, + {'src_node':'/sys/module/m24c02/parameters/bsp_version', 'dest_node':SYSEEPROM_PREFIX+'bsp_version'}, + {'src_node':common.I2C_PREFIX+'9-0054/eeprom', 'dest_node':SYSEEPROM_PREFIX+'eeprom'}, +] +WATCHDOG_TOP_NODE_DICT = [ + {'src_node':'/sys/module/pegatron_fn8656_bnf_watchdog/parameters/loglevel', 'dest_node':'/sys/clx/watchdog/'+'loglevel'}, + {'src_node':'/sys/clx/watchdog', 'dest_node':'/sys/switch/watchdog'}, +] +''' +WATCHDOG_TOP_NODE_DICT = [ + {'src_node':'/sys/module/clounix_watchdog/parameters/loglevel', 'dest_node':WATCHDOG_PREFIX+'loglevel'}, + {'src_node':'/sys/module/clounix_watchdog/parameters/watchdog_debug', 'dest_node':WATCHDOG_PREFIX+'debug'}, + {'src_node':'/sys/bus/platform/devices/iTCO_wdt.0.auto/watchdog/watchdog0/identity', 'dest_node':WATCHDOG_PREFIX+'identity'}, + {'src_node':'/sys/bus/platform/devices/iTCO_wdt.0.auto/watchdog/watchdog0/state', 'dest_node':WATCHDOG_PREFIX+'state'}, + {'src_node':'/sys/bus/platform/devices/iTCO_wdt.0.auto/watchdog/watchdog0/timeleft', 'dest_node':WATCHDOG_PREFIX+'timeleft'}, + {'src_node':'/sys/devices/pci0000:00/0000:00:1f.0/clounix_ctrl', 'dest_node':WATCHDOG_PREFIX+'enable'}, + {'src_node':'/sys/devices/pci0000:00/0000:00:1f.0/clounix_timeout_ctrl', 'dest_node':WATCHDOG_PREFIX+'timeout'}, + {'src_node':'/sys/devices/pci0000:00/0000:00:1f.0/clounix_ping', 'dest_node':WATCHDOG_PREFIX+'reset'}, +] +''' +SYSLED_TOP_NODE_DICT = [ + {'src_node':'/sys/module/pegatron_fn8656_bnf_cpld/parameters/loglevel', 'dest_node':SYSLED_PREFIX+'loglevel'}, + {'src_node':common.I2C_PREFIX+'6-0074/debug_sysled', 'dest_node':SYSLED_PREFIX+'debug'}, + {'src_node':common.I2C_PREFIX+'6-0074/sys_led', 'dest_node':SYSLED_PREFIX+'sys_led_status'}, + #not supported{'src_node':common.I2C_PREFIX+'6-0074/', 'dest_node':SYSLED_PREFIX+'bmc_led_status'}, + {'src_node':common.I2C_PREFIX+'6-0074/fan_led', 'dest_node':SYSLED_PREFIX+'fan_led_status'}, + {'src_node':common.I2C_PREFIX+'6-0074/pwr_led', 'dest_node':SYSLED_PREFIX+'psu_led_status'}, + {'src_node':common.I2C_PREFIX+'1-0018/loc_led', 'dest_node':SYSLED_PREFIX+'id_led_status'}, +] + +TRANSCIEVER_TOP_NODE_DICT = [ + {'src_node':'/sys/module/pegatron_fn8656_bnf_cpld/parameters/loglevel', 'dest_node':TRANSCIEVER_PREFIX+'loglevel'}, + {'src_node':common.I2C_PREFIX+'6-0074/debug_sfp', 'dest_node':TRANSCIEVER_PREFIX+'debug'}, + {'src_node':common.I2C_PREFIX+'7-0075/sfp_port_num', 'dest_node':TRANSCIEVER_PREFIX+'num'}, + {'src_node':common.I2C_PREFIX+'7-0075/sfp_all_present', 'dest_node':TRANSCIEVER_PREFIX+'present'}, + {'src_node':common.I2C_PREFIX+'7-0075/sfp_all_power_on', 'dest_node':TRANSCIEVER_PREFIX+'power_on'}, +] +TRANSCIEVER_PORT_NODE_DICT = [ + {'src_node' : '/sys/devices/pci0000:00/0000:00:1c.4/0000:0a:00.0{}'+'sfp{}_eeprom', 'dest_node' : TRANSCIEVER_PREFIX+'eth{}/eeprom'}, + {'src_node' : common.I2C_PREFIX+'{}'+'/sfp{}_present', 'dest_node' : TRANSCIEVER_PREFIX+'eth{}/present'}, + {'src_node' : common.I2C_PREFIX+'{}'+'/sfp{}_reset', 'dest_node' : TRANSCIEVER_PREFIX+'eth{}/reset'}, + {'src_node' : common.I2C_PREFIX+'{}'+'/sfp{}_lowpower', 'dest_node' : TRANSCIEVER_PREFIX+'eth{}/lpmode'}, + {'src_node' : common.I2C_PREFIX+'{}'+'/sfp{}_irq_status', 'dest_node' : TRANSCIEVER_PREFIX+'eth{}/interrupt'}, + {'src_node' : common.I2C_PREFIX+'{}'+'/sfp{}_power_on', 'dest_node' : TRANSCIEVER_PREFIX+'eth{}/power_on'}, +] + +FAN_TOP_NODE_DICT = [ + {'src_node':'/sys/module/pegatron_hwmon_mcu/parameters/loglevel', 'dest_node':FAN_PREFIX+'loglevel'}, + {'src_node':'/sys/module/pegatron_hwmon_mcu/parameters/debug', 'dest_node':FAN_PREFIX+'debug'}, + {'src_node':common.I2C_PREFIX+'4-0070/hwmon/hwmon4/fan_num', 'dest_node':FAN_PREFIX+'num'} +] +FAN_ATTR_NODE_DICT = [ + {'src_node' : common.I2C_PREFIX+'4-0070/hwmon/hwmon4/fan{}_model_name', 'dest_node' : FAN_PREFIX+'fan{}/model_name'}, + {'src_node' : common.I2C_PREFIX+'4-0070/hwmon/hwmon4/fan{}_serial_num', 'dest_node' : FAN_PREFIX+'fan{}/serial_number'}, + {'src_node' : common.I2C_PREFIX+'4-0070/hwmon/hwmon4/fan{}_vendor', 'dest_node' : FAN_PREFIX+'fan{}/vendor'}, + {'src_node' : common.I2C_PREFIX+'4-0070/hwmon/hwmon4/fan{}_part_num', 'dest_node' : FAN_PREFIX+'fan{}/part_number'}, + {'src_node' : common.I2C_PREFIX+'4-0070/hwmon/hwmon4/fan{}_hard_version', 'dest_node' : FAN_PREFIX+'fan{}/hardware_version'}, + {'src_node' : common.I2C_PREFIX+'4-0070/hwmon/hwmon4/fan{}_motor_num', 'dest_node' : FAN_PREFIX+'fan{}/num_motors'}, + {'src_node' : common.I2C_PREFIX+'4-0070/hwmon/hwmon4/fan{}_present', 'dest_node' : FAN_PREFIX+'fan{}/status'}, + {'src_node' : common.I2C_PREFIX+'4-0070/hwmon/hwmon4/fan{}_led_status', 'dest_node' : FAN_PREFIX+'fan{}/led_status'} +] +FAN_MOTOR_NODE_DICT = [ + {'src_node' : common.I2C_PREFIX+'4-0070/hwmon/hwmon4/fan{}_inner_rpm', 'dest_node' : FAN_PREFIX+'fan{}/motor{}/speed'}, + {'src_node' : common.I2C_PREFIX+'4-0070/hwmon/hwmon4/fan{}_speed_tolerance', 'dest_node' : FAN_PREFIX+'fan{}/motor{}/speed_tolerance'}, + {'src_node' : common.I2C_PREFIX+'4-0070/hwmon/hwmon4/fan{}_speed_target', 'dest_node' : FAN_PREFIX+'fan{}/motor{}/speed_target'}, + {'src_node' : common.I2C_PREFIX+'4-0070/hwmon/hwmon4/fan_pwm', 'dest_node' : FAN_PREFIX+'fan{}/motor{}/ratio'}, + {'src_node' : common.I2C_PREFIX+'4-0070/hwmon/hwmon4/fan{}_airflow_dir', 'dest_node' : FAN_PREFIX+'fan{}/motor{}/direction'} +] + +PSU_TOP_NODE_DICT = [ + {'src_node':'/sys/module/pegatron_hwmon_mcu/parameters/loglevel', 'dest_node':PSU_PREFIX+'loglevel'}, + {'src_node':'/sys/module/pegatron_hwmon_mcu/parameters/debug', 'dest_node':PSU_PREFIX+'debug'}, + {'src_node':common.I2C_PREFIX+'2-0058/hwmon/hwmon2/psu_num', 'dest_node':PSU_PREFIX+'num'} +] +PSU_ATTR_NODE_DICT = [ + {'src_node' : common.I2C_PREFIX+'{}/manuafacture_model', 'dest_node' : PSU_PREFIX+'psu{}/model_name'}, + {'src_node' : common.I2C_PREFIX+'{}/manuafacture_serial', 'dest_node' : PSU_PREFIX+'psu{}/serial_number'}, + {'src_node' : common.I2C_PREFIX+'{}/manuafacture_id', 'dest_node' : PSU_PREFIX+'psu{}/vendor'}, + {'src_node' : common.I2C_PREFIX+'{}/manuafacture_date', 'dest_node' : PSU_PREFIX+'psu{}/date'}, + {'src_node' : common.I2C_PREFIX+'{}/manuafacture_revision', 'dest_node' : PSU_PREFIX+'psu{}/hardware_version'}, + {'src_node' : common.I2C_PREFIX+'{}/psu_alarm', 'dest_node' : PSU_PREFIX+'psu{}/alarm'}, + {'src_node' : common.I2C_PREFIX+'{}/curr_in_max', 'dest_node' : PSU_PREFIX+'psu{}/alarm_threshold_curr'}, + {'src_node' : common.I2C_PREFIX+'{}/vol_in_max', 'dest_node' : PSU_PREFIX+'psu{}/alarm_threshold_vol'}, + {'src_node' : common.I2C_PREFIX+'{}/manuafacture_part_num', 'dest_node' : PSU_PREFIX+'psu{}/part_number'}, + {'src_node' : common.I2C_PREFIX+'{}/power_out_max', 'dest_node' : PSU_PREFIX+'psu{}/max_output_power'}, + {'src_node' : common.I2C_PREFIX+'{}/curr_in', 'dest_node' : PSU_PREFIX+'psu{}/in_curr'}, + {'src_node' : common.I2C_PREFIX+'{}/vol_in', 'dest_node' : PSU_PREFIX+'psu{}/in_vol'}, + {'src_node' : common.I2C_PREFIX+'{}/power_in', 'dest_node' : PSU_PREFIX+'psu{}/in_power'}, + {'src_node' : common.I2C_PREFIX+'{}/curr_out', 'dest_node' : PSU_PREFIX+'psu{}/out_curr'}, + {'src_node' : common.I2C_PREFIX+'{}/vol_out', 'dest_node' : PSU_PREFIX+'psu{}/out_vol'}, + {'src_node' : common.I2C_PREFIX+'{}/power_out', 'dest_node' : PSU_PREFIX+'psu{}/out_power'}, + {'src_node' : common.I2C_PREFIX+'{}/psu_{}_status', 'dest_node' : PSU_PREFIX+'psu{}/status'}, + {'src_node' : common.I2C_PREFIX+'{}/psu_led_status', 'dest_node' : PSU_PREFIX+'psu{}/led_status'}, + {'src_node' : common.I2C_PREFIX+'{}/psu_type', 'dest_node' : PSU_PREFIX+'psu{}/type'}, + {'src_node' : common.I2C_PREFIX+'{}/sensor_num', 'dest_node' : PSU_PREFIX+'psu{}/num_temp_sensors'}, + {'src_node' : common.I2C_PREFIX+'{}/fan1_speed', 'dest_node' : PSU_PREFIX+'psu{}/fan_speed'}, + {'src_node' : common.I2C_PREFIX+'{}/vol_out_max', 'dest_node' : PSU_PREFIX+'psu{}/max_output_vol'}, + {'src_node' : common.I2C_PREFIX+'{}/vol_out_min', 'dest_node' : PSU_PREFIX+'psu{}/min_output_vol'} +] + +SENSOR_TOP_NODE_DICT = [ + {'src_node':'/sys/module/pegatron_hwmon_mcu/parameters/loglevel', 'dest_node':SENSOR_PREFIX+'loglevel'}, + {'src_node':'/sys/module/pegatron_hwmon_mcu/parameters/debug', 'dest_node':SENSOR_PREFIX+'debug'}, +] +PSU_SENSOR_NODE_DICT = [ + {'src_node' : common.I2C_PREFIX+'{}/temp{}_label', 'dest_node' : PSU_PREFIX+'psu{}/temp{}/temp_alias'}, + {'src_node' : common.I2C_PREFIX+'{}/psu_sensor_type', 'dest_node' : PSU_PREFIX+'psu{}/temp{}/temp_type'}, + {'src_node' : common.I2C_PREFIX+'{}/temp{}_max', 'dest_node' : PSU_PREFIX+'psu{}/temp{}/temp_max_hyst'}, + {'src_node' : common.I2C_PREFIX+'{}/temp{}_min', 'dest_node' : PSU_PREFIX+'psu{}/temp{}/temp_min'}, + {'src_node' : common.I2C_PREFIX+'{}/temp{}_crit', 'dest_node' : PSU_PREFIX+'psu{}/temp{}/temp_max'}, + {'src_node' : common.I2C_PREFIX+'{}/temp{}_input', 'dest_node' : PSU_PREFIX+'psu{}/temp{}/temp_input'}, +] + +CPLD_TOP_NODE_DICT = [ + {'src_node':'/sys/module/pegatron_fn8656_bnf_cpld/parameters/loglevel', 'dest_node':CPLD_PREFIX+'loglevel'}, + {'src_node':common.I2C_PREFIX+'6-0074/debug_cpld', 'dest_node':CPLD_PREFIX+'debug'}, + {'src_node':common.I2C_PREFIX+'6-0074/cpld_num', 'dest_node':CPLD_PREFIX+'num'}, + #not supported{'src_node':common.I2C_PREFIX+'', 'dest_node':CPLD_PREFIX+'reboot_cause'}, +] +CPLD_DETAIL_NODE_DICT = [ + {'src_node' : common.I2C_PREFIX+'{}'+'/cpld_alias', 'dest_node' : CPLD_PREFIX+'cpld{}/alias'}, + {'src_node' : common.I2C_PREFIX+'{}'+'/cpld_type', 'dest_node' : CPLD_PREFIX+'cpld{}/type'}, + {'src_node' : common.I2C_PREFIX+'{}'+'/cpld_hw_version', 'dest_node' : CPLD_PREFIX+'cpld{}/hw_version'}, + {'src_node' : common.I2C_PREFIX+'{}'+'/cpld_board_version', 'dest_node' : CPLD_PREFIX+'cpld{}/board_version'} +] +def linkDeviceNode(node_type): + + if node_type == 'sensor': + num = str(device.THERMAL_SENSOR_NUM) + cmd = device.SYSSENSOR_PREFIX + "num_temp_sensors " + num + common.doBash("echo add " + cmd + " > /sys/switch/clounix_cmd") + for node in SENSOR_TOP_NODE_DICT: + #check whether the soft link exists + if not os.path.exists(node['dest_node']) : + common.doBash("echo "+node['src_node']+' '+node['dest_node']+" > /sys/switch/clounix_cmd") + + for sensor_index in range(device.THERMAL_SENSOR_NUM): + thermal_info = device.THERMAL_COMBINATION[sensor_index] + for work_index in range(device.NEED_WORK): + src_path = thermal_info[device.PATH_INDEX] + thermal_info[work_index] + dst_path = device.SENSOR_PATH.format(sensor_index) + dst_path = dst_path + device.THERMAL_NODE_COMBINATION[work_index] + if work_index < 3 : + common.doBash("echo " + src_path + " " + dst_path + " > /sys/switch/clounix_cmd") + else : + common.doBash("echo " + "add " + dst_path + " " + thermal_info[work_index] + " > /sys/switch/clounix_cmd") + + elif node_type == 'transceiver': + #transceiver top level + for node in TRANSCIEVER_TOP_NODE_DICT: + #check whether the soft link exists + if not os.path.exists(node['dest_node']) : + common.doBash("echo "+node['src_node']+' '+node['dest_node']+" > /sys/switch/clounix_cmd") + #transceiver port node + for port_num in range(device.SFP_MAX_NUM + 1, device.TOTAL_PORT_NUM + 1): + for node in TRANSCIEVER_PORT_NODE_DICT: + if port_num < device.CPLDB_SFP_NUM + 1: + if node['dest_node'].format(port_num).split('/')[-1] == 'eeprom': + bus = '/' + else: + bus = '7-0075' + else: + if node['dest_node'].format(port_num).split('/')[-1] == 'eeprom': + bus = '/' + else: + bus = '8-0076' + #check whether the link exists + if not os.path.exists(node['dest_node'].format(port_num)) : + common.doBash("echo "+node['src_node'].format(bus,port_num)+' '+node['dest_node'].format(port_num)+" > /sys/switch/clounix_cmd") + elif node_type == 'fan': + for node in FAN_TOP_NODE_DICT: + #check whether the soft link exists + if not os.path.exists(node['dest_node']) : + common.doBash("echo "+node['src_node']+' '+node['dest_node']+" > /sys/switch/clounix_cmd") + + for fan_num in range(1, device.FAN_NUM + 1): + for node in FAN_ATTR_NODE_DICT: + if not os.path.exists(node['dest_node'].format(fan_num)) : + common.doBash("echo "+node['src_node'].format(fan_num)+' '+node['dest_node'].format(fan_num)+" > /sys/switch/clounix_cmd") + #fan motor level + for motor_num in range(0, device.MOTOR_NUM_PER_FAN): + for motor_node in FAN_MOTOR_NODE_DICT: + if not os.path.exists(motor_node['dest_node'].format(fan_num,motor_num)) : + common.doBash("echo "+motor_node['src_node'].format(fan_num)+' '+motor_node['dest_node'].format(fan_num,motor_num)+" > /sys/switch/clounix_cmd") + elif node_type == 'cpld': + for node in CPLD_TOP_NODE_DICT: + #check whether the soft link exists + if not os.path.exists(node['dest_node']) : + common.doBash("echo "+node['src_node']+' '+node['dest_node']+" > /sys/switch/clounix_cmd") + + cpld_bus = ['1-0018','6-0074','7-0075','8-0076'] + for cpld_num in range(1, device.CPLD_NUM + 1): + for node in CPLD_DETAIL_NODE_DICT: + #check whether the link exists + if not os.path.exists(node['dest_node'].format(cpld_num)) : + common.doBash("echo "+node['src_node'].format(cpld_bus[cpld_num-1])+' '+node['dest_node'].format(cpld_num)+" > /sys/switch/clounix_cmd") + elif node_type == 'psu': + for node in PSU_TOP_NODE_DICT: + #check whether the soft link exists + if not os.path.exists(node['dest_node']) : + common.doBash("echo "+node['src_node']+' '+node['dest_node']+" > /sys/switch/clounix_cmd") + + for psu_num in range(1, device.PSU_NUM + 1): + for node in PSU_ATTR_NODE_DICT: + if not os.path.exists(node['dest_node'].format(psu_num)) : + if node['dest_node'].format(psu_num).split('/')[-1] == 'status' : + bus = '6-0074' + common.doBash("echo "+node['src_node'].format(bus,psu_num)+' '+node['dest_node'].format(psu_num)+" > /sys/switch/clounix_cmd") + continue + if psu_num == 1: + bus = '2-0058/hwmon/hwmon2' + elif psu_num == 2: + bus = '3-0059/hwmon/hwmon3' + common.doBash("echo "+node['src_node'].format(bus)+' '+node['dest_node'].format(psu_num)+" > /sys/switch/clounix_cmd") + + #psu temp sensor level + for temp_sensor_num in range(0, device.TEMP_SENSOR_NUM_PER_PSU): + for temp_sensor_node in PSU_SENSOR_NODE_DICT: + if not os.path.exists(temp_sensor_node['dest_node'].format(psu_num,temp_sensor_num)) : + common.doBash("echo "+temp_sensor_node['src_node'].format(bus,temp_sensor_num+1)+' '+temp_sensor_node['dest_node'].format(psu_num,temp_sensor_num)+" > /sys/switch/clounix_cmd") + + elif node_type == 'syseeprom': + for node in SYSEEPROM_TOP_NODE_DICT: + #check whether the soft link exists + if not os.path.exists(node['dest_node']) : + common.doBash("echo "+node['src_node']+' '+node['dest_node']+" > /sys/switch/clounix_cmd") + + elif node_type == 'fpga': + for node in FPGA_TOP_NODE_DICT: + #check whether the soft link exists + if not os.path.exists(node['dest_node']) : + fd = os.open(node['src_node'], os.O_RDONLY) + common.doBash("echo "+node['src_node']+' '+node['dest_node']+" > /sys/switch/clounix_cmd") + os.close(fd) + + elif node_type == 'watchdog': + for node in WATCHDOG_TOP_NODE_DICT: + #check whether the soft link exists + if not os.path.exists(node['dest_node']) : + common.doBash("echo "+node['src_node']+' '+node['dest_node']+" > /sys/switch/clounix_cmd") + + elif node_type == 'sysled': + for node in SYSLED_TOP_NODE_DICT: + #check whether the soft link exists + if not os.path.exists(node['dest_node']) : + common.doBash("echo "+node['src_node']+' '+node['dest_node']+" > /sys/switch/clounix_cmd") + + +def deleteLinkDeviceNode(node_type): + if node_type == 'sensor': + cmd = device.SYSSENSOR_PREFIX + "num_temp_sensors" + common.doBash("echo del " + cmd + " > /sys/swtich/clounix_cmd") + for sensor_index in range(device.THERMAL_SENSOR_NUM): + for work_index in range(device.NEED_WORK): + dst_path = device.SENSOR_PATH.format(sensor_index) + dst_path = dst_path + device.THERMAL_NODE_COMBINATION[work_index] + common.doBash("del " + dst_path + " > /sys/switch/clounix_cmd") + + elif node_type == 'transceiver': + #transceiver + for node in TRANSCIEVER_TOP_NODE_DICT: + #check whether the link exists + if os.path.exists(node['dest_node']) : + common.doBash("echo del "+node['dest_node']+" > /sys/switch/clounix_cmd") + + for port_num in range(device.SFP_MAX_NUM + 1, device.TOTAL_PORT_NUM + 1): + for node in TRANSCIEVER_PORT_NODE_DICT: + #check whether the link exists + if os.path.exists(node['dest_node'].format(port_num)) : + #del /sys/switch/transceiver/eth*/* + common.doBash("echo del "+node['dest_node'].format(port_num)+" > /sys/switch/clounix_cmd") + #del /sys/switch/transceiver/eth* + common.doBash("echo del /sys/switch/transceiver/eth{}".format(port_num)+" > /sys/switch/clounix_cmd") + elif node_type == 'fan': + #fan + for node in FAN_TOP_NODE_DICT: + #check whether the link exists + if os.path.exists(node['dest_node']) : + common.doBash("echo del "+node['dest_node']+" > /sys/switch/clounix_cmd") + + for fan_num in range(1, device.FAN_NUM + 1): + for node in FAN_ATTR_NODE_DICT: + for motor_num in range(0, device.MOTOR_NUM_PER_FAN): + for motor_node in FAN_MOTOR_NODE_DICT: + #del /sys/switch/fan/fan*/motor*/* + if os.path.exists(motor_node['dest_node'].format(fan_num,motor_num)) : + common.doBash("echo del "+motor_node['dest_node'].format(fan_num,motor_num)+" > /sys/switch/clounix_cmd") + #del /sys/switch/fan/fan*/motor* + if os.path.exists("/sys/switch/fan/fan{}/motor{}".format(fan_num,motor_num)) : + common.doBash("echo del /sys/switch/fan/fan{}/motor{}".format(fan_num,motor_num)+" > /sys/switch/clounix_cmd") + #check whether the link exists + if os.path.exists(node['dest_node'].format(fan_num)) : + #del /sys/switch/fan/fan*/* + common.doBash("echo del "+node['dest_node'].format(fan_num)+" > /sys/switch/clounix_cmd") + #del /sys/switch/fan/fan* + common.doBash("echo del /sys/switch/fan/fan{}".format(fan_num)+" > /sys/switch/clounix_cmd") + elif node_type == 'cpld': + #transceiver + for node in CPLD_TOP_NODE_DICT: + #check whether the link exists + if os.path.exists(node['dest_node']) : + common.doBash("echo del "+node['dest_node']+" > /sys/switch/clounix_cmd") + + for cpld_num in range(1, device.CPLD_NUM + 1): + for node in CPLD_DETAIL_NODE_DICT: + #check whether the link exists + if os.path.exists(node['dest_node'].format(cpld_num)) : + #del /sys/switch/cpld/cpld*/* + common.doBash("echo del "+node['dest_node'].format(cpld_num)+" > /sys/switch/clounix_cmd") + #del /sys/switch/cpld/cpld* + common.doBash("echo del /sys/switch/cpld/cpld{}".format(cpld_num)+" > /sys/switch/clounix_cmd") + elif node_type == 'psu': + #psu + for node in PSU_TOP_NODE_DICT: + #check whether the link exists + if os.path.exists(node['dest_node']) : + common.doBash("echo del "+node['dest_node']+" > /sys/switch/clounix_cmd") + + for psu_num in range(1, device.PSU_NUM + 1): + for node in PSU_ATTR_NODE_DICT: + for temp_sensor_num in range(0, device.MOTOR_NUM_PER_FAN): + for motor_node in PSU_SENSOR_NODE_DICT: + #del /sys/switch/psu/psu*/temp*/* + if os.path.exists(motor_node['dest_node'].format(psu_num,temp_sensor_num)) : + common.doBash("echo del "+motor_node['dest_node'].format(psu_num,temp_sensor_num)+" > /sys/switch/clounix_cmd") + #del /sys/switch/psu/psu*/temp* + if os.path.exists("/sys/switch/psu/psu{}/temp{}".format(psu_num,temp_sensor_num)) : + common.doBash("echo del /sys/switch/psu/psu{}/temp{}".format(psu_num,temp_sensor_num)+" > /sys/switch/clounix_cmd") + #check whether the link exists + if os.path.exists(node['dest_node'].format(psu_num)) : + #del /sys/switch/psu/psu*/* + common.doBash("echo del "+node['dest_node'].format(psu_num)+" > /sys/switch/clounix_cmd") + #del /sys/switch/psu/psu* + common.doBash("echo del /sys/switch/psu/psu{}".format(psu_num)+" > /sys/switch/clounix_cmd") + + elif node_type == 'syseeprom': + for node in SYSEEPROM_TOP_NODE_DICT: + #check whether the link exists + if os.path.exists(node['dest_node']) : + common.doBash("echo del "+node['dest_node']+" > /sys/switch/clounix_cmd") + + elif node_type == 'fpga': + for node in FPGA_TOP_NODE_DICT: + #check whether the link exists + if os.path.exists(node['dest_node']) : + common.doBash("echo del "+node['dest_node']+" > /sys/switch/clounix_cmd") + + elif node_type == 'watchdog': + for node in WATCHDOG_TOP_NODE_DICT: + #check whether the link exists + if os.path.exists(node['dest_node']) : + common.doBash("echo del "+node['dest_node']+" > /sys/switch/clounix_cmd") + + elif node_type == 'sysled': + for node in SYSLED_TOP_NODE_DICT: + #check whether the link exists + if os.path.exists(node['dest_node']) : + common.doBash("echo del "+node['dest_node']+" > /sys/switch/clounix_cmd") + + + +def doInstall(): + common.doBash("depmod -a") + common.doBash("rmmod hid_cp2112") + common.doBash("modprobe -r iTCO_wdt") + checkDriver() + for o in i2c_topology_dict: + common.doBash("echo " + o['driver'] + " " + o['address'] + " > " + common.I2C_PREFIX + o['bus'] + "/new_device") + + for node_type in SOFT_LINK_NODE_TYPE: + linkDeviceNode(node_type) + return + +def setupThreads(): + global THREADS, QUEUES + + # Queues + # Global + queueGlobal = common.Queue.Queue() + QUEUES.append(queueGlobal) + FUNCS['global'] = queueGlobal + + # Device + queueDevice = common.Queue.Queue() + QUEUES.append(queueDevice) + FUNCS['device'] = queueDevice + + # Threads + # Global + threadGlobal = GlobalThread('Global Handler', queueGlobal) + THREADS.append(threadGlobal) + + # Device + threadDevice = device.DeviceThread('Device Handler', queueDevice) + THREADS.append(threadDevice) + + # Check platform status + threadPlatformStatus = device.PlatformStatusThread('Platform Status Handler', 0.3) + THREADS.append(threadPlatformStatus) + +def functionInit(): + setupThreads() + for thread in THREADS: + thread.start() + return + +def deviceInit(): + msg = messageObject(['init'], None) + FUNCS['device'].put(msg) + return + +def do_platformApiInit(): + print("Platform API Init....") + common.doBash("/usr/local/bin/platform_api_mgnt.sh init") + return + +def do_platformApiInstall(): + print("Platform API Install....") + common.doBash("/usr/local/bin/platform_api_mgnt.sh install") + return + +# Platform uninitialize +def doUninstall(): + for node_type in SOFT_LINK_NODE_TYPE: + deleteLinkDeviceNode(node_type) + for i in range(0, len(KERNEL_MODULE))[::-1]: + common.doBash("modprobe -rq " + KERNEL_MODULE[i]) + for o in i2c_topology_dict: + common.doBash("echo " + o['address'] + " > " + common.I2C_PREFIX + o['bus'] + "/delete_device") + return + +def main(): #start 20200219 + args = common.sys.argv[1:] + + if len(args[0:]) < 1: + common.sys.exit(0) + + if args[0] == 'install': + common.RUN = True + doInstall() + do_platformApiInit() + do_platformApiInstall() + #fwupdates.fwupdate() + functionInit() + deviceInit() + messageHandler() + + + common.sys.exit(0) + +if __name__ == "__main__": + main() diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/scripts/platform_update_reboot_cause b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/scripts/platform_update_reboot_cause new file mode 100755 index 000000000000..6d21055bec0d --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/scripts/platform_update_reboot_cause @@ -0,0 +1,24 @@ +#!/bin/bash +rst_mode=$1 +function sys_cold_rst() +{ + #it is done to write CPLD register also + #CPLD-D 0x9 BIT3 0 + echo -e '\xe' | dd of=/dev/port bs=1 seek=3321 +} + +function sys_warm_rst() +{ + echo -e '\x6' | dd of=/dev/port bs=1 seek=3321 +} + + +case "$rst_mode" in +cold) + sys_cold_rst + ;; +warm) + sys_warm_rst + ;; +esac + diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/scripts/sensors b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/scripts/sensors new file mode 100755 index 000000000000..a4121f77d7c4 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/scripts/sensors @@ -0,0 +1,5 @@ +#!/bin/bash +docker exec -i pmon sensors "$@" + +#To probe sensors not part of lm-sensors +pegatron_fn8656_bnf_util.py cmd device sensors diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/service/fn8656_bnf-platform-init.service b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/service/fn8656_bnf-platform-init.service new file mode 100644 index 000000000000..c3ae566740f2 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/service/fn8656_bnf-platform-init.service @@ -0,0 +1,13 @@ +[Unit] +Description=Pegastron fn8656-bnf Platform initialization service +Before=network.target +DefaultDependencies=no + +[Service] +Type=simple +ExecStart=/usr/local/bin/pegaProcess/main.py install +ExecStop=/usr/local/bin/pegatron_fn8656_bnf_util.py uninstall +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/service/platform_api_mgnt.sh b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/service/platform_api_mgnt.sh new file mode 100755 index 000000000000..8baa6b05035b --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/service/platform_api_mgnt.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +PREV_REBOOT_CAUSE="/host/reboot-cause/" +DEVICE="/usr/share/sonic/device" +PLATFORM=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform) +FILES=$DEVICE/$PLATFORM/api_files + +install() { + # Install sonic-platform package + if [ -e $DEVICE/$PLATFORM/sonic_platform-1.0-py2-none-any.whl ]; then + pip install $DEVICE/$PLATFORM/sonic_platform-1.0-py2-none-any.whl + fi + if [ -e $DEVICE/$PLATFORM/sonic_platform-1.0-py3-none-any.whl ]; then + pip3 install $DEVICE/$PLATFORM/sonic_platform-1.0-py3-none-any.whl + fi +} + +init() { + # mount needed files for sonic-platform package + mkdir -p $FILES + + mkdir -p $FILES/reboot-cause + mount -B $PREV_REBOOT_CAUSE $FILES/reboot-cause +} + +deinit() { + # deinit sonic-platform package + umount -f $PREV_REBOOT_CAUSE $FILES/reboot-cause >/dev/null 2>/dev/null +} + +uninstall() { + # Uninstall sonic-platform package + pip uninstall -y sonic-platform >/dev/null 2>/dev/null +} + +case "$1" in +install | uninstall | init | deinit) + $1 + ;; +*) + echo "Usage: $0 {install|uninstall|init|deinit}" + exit 1 + ;; +esac diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/setup.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/setup.py new file mode 100755 index 000000000000..6334310ae18d --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/setup.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +import os +#import sys +from setuptools import setup +os.listdir + +setup( + name='sonic_platform', + version='1.0', + description='Module to initialize Pegatron FN8656-BNF platforms', + + packages=['sonic_platform'], +) + diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/__init__.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/__init__.py new file mode 100755 index 000000000000..4bfefa0fb636 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/__init__.py @@ -0,0 +1,3 @@ +__all__ = ["platform", "chassis"] +from sonic_platform import * + diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/chassis.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/chassis.py new file mode 100755 index 000000000000..b16d363afcd6 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/chassis.py @@ -0,0 +1,508 @@ +#!/usr/bin/env python +# +# Clounix +# Name: chassis.py, version: 1.0 +# +# Description: Module contains the definitions of SONiC platform APIs +# + +try: + import os + import sys + import time + from sonic_platform_base.chassis_base import ChassisBase + from sonic_platform.eeprom import Eeprom + from sonic_platform.fan import Fan + from sonic_platform.fan_drawer import FanDrawer + from sonic_platform.psu import Psu + from sonic_platform.qsfp import QSfp + from sonic_platform.thermal import Thermal + from sonic_platform.component import Component + from sonic_platform.watchdog import Watchdog + from .helper import APIHelper +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +SFP_STATUS_INSERTED = '1' +SFP_STATUS_REMOVED = '0' + +# FAN status definition, shall be aligned with the definition in get_fan_change_event() of ChassisBase +FAN_STATUS_REMOVED = '0' +FAN_STATUS_INSERTED = '1' + +# PSU status definition, shall be aligned with the definition in get_change_event() of ChassisBase +PSU_STATUS_REMOVED = '0' +PSU_STATUS_INSERTED = '1' + +REBOOT_CAUSE_FILE = "/host/reboot-cause/reboot-cause.txt" +THERMAL_OVERLOAD_POSITION_FILE = "/host/reboot-cause/platform/thermal_overload_position" +ADDITIONAL_FAULT_CAUSE_FILE = "/host/reboot-cause/platform/additional_fault_cause" + +class Chassis(ChassisBase): + _voltage_list = [] + + def __init__(self): + self.__api_helper = APIHelper() + chassis_conf = self.__api_helper.get_attr_conf("chassis") + self.__conf = chassis_conf + + self.__num_of_psus = len(chassis_conf['psus']) + self.__num_of_sfps = len(chassis_conf['sfps']) + self.__num_of_thermals = len(chassis_conf['thermals']) + self.__num_of_components = len(chassis_conf['components']) + self.__num_of_fans = len(chassis_conf['fans']) + self.__num_of_fan_drawers = len(chassis_conf['fan_drawers']) + + self.__start_of_qsfp = 0 + self.__attr_led_path_prefix = '/sys/switch/sysled/' + ChassisBase.__init__(self) + self.REBOOT_CAUSE_THERMAL_OVERLOAD_CPU = "Thermal Overload:CPU" + self.REBOOT_CAUSE_THERMAL_OVERLOAD_ASIC = "Thermal Overload:ASIC" + self.REBOOT_CAUSE_THERMAL_OVERLOAD_OTHER = "Thermal Overload:Other" + self.REBOOT_CAUSE_WATCHDOG = "Watchdog" + self.REBOOT_CAUSE_COLD_RESET = "CPU Cold Reset" + self.REBOOT_CAUSE_WARM_RESET = "CPU Warm Reset" + self.REBOOT_CAUSE_PSU_SHUTDOWN = "PSU Shutdown" + + # Initialize EEPROM + self._eeprom = Eeprom() + + # Initialize CHASSIS FAN + fan_conf=self.__conf['fans'] + for x in range(0, self.__num_of_fans): + fan_conf[x].update({'container':'chassis'}) + fan_conf[x].update({'container_index': self.__index}) + fan = Fan(x, fan_conf) + self._fan_list.append(fan) + + # Initialize CHASSIS FAN DRWAER + for drawer_index in range(0, self.__num_of_fan_drawers): + fandrawer_conf=chassis_conf['fan_drawers'] + fan_drawer = FanDrawer(drawer_index,fandrawer_conf) + self._fan_drawer_list.append(fan_drawer) + + + # Initialize PSU + for index in range(0, self.__num_of_psus): + psu_conf=chassis_conf['psus'] + psu = Psu(index,psu_conf) + self._psu_list.append(psu) + + # Initialize SFP + for index in range(0, self.__num_of_sfps): + qsfp_conf=chassis_conf['sfps'] + sfp = QSfp(index,qsfp_conf) + self._sfp_list.append(sfp) + + # Initialize CHASSIS THERMAL + thermal_conf=self.__conf['thermals'] + for x in range(0, self.__num_of_thermals): + thermal_conf[x].update({'container':'chassis'}) + thermal_conf[x].update({'container_index':0}) + thermal = Thermal(x,thermal_conf) + self._thermal_list.append(thermal) + + # Initialize COMPONENT + for index in range(0, self.__num_of_components): + component_conf=chassis_conf['components'] + component = Component(index,component_conf) + self._component_list.append(component) + + # Initialize WATCHDOG + watchdog_conf=chassis_conf['watchdog'] + self._watchdog = Watchdog(watchdog_conf) + +############################################## +# Device methods +############################################## + def get_thermal_manager(self): + from .thermal_manager import ThermalManager + return ThermalManager + + def get_name(self): + """ + Retrieves the name of the chassis + Returns: + string: The name of the chassis + """ + return self.__conf['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 + """ + return self._eeprom.part_number_str() + + def get_serial(self): + """ + Retrieves the serial number of the chassis + Returns: + string: Serial number of chassis + """ + return self._eeprom.serial_number_str() + + def get_status(self): + """ + Retrieves the operational status of the chassis + Returns: + bool: A boolean value, True if chassis is operating properly + False if not + """ + return True + +############################################## +# Chassis methods +############################################## + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return -1 + + def is_replaceable(self): + """ + Indicate whether Chassis is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False + + def set_status_led(self, color): + """ + Sets the state of the fan module status LED + + Args: + color: A string representing the color with which to set the + fan module status LED + + Returns: + bool: True if status LED state is set successfully, False if not + """ + ret_val = False + if color == "green": + led_value = 1 + elif color == "red": + led_value = 3 + elif color == "blink_green": + led_value = 4 + else: + return False + ret_val = self.__api_helper.write_txt_file(self.__attr_led_path_prefix + 'sys_led_status',led_value) + + return ret_val + + def get_status_led(self): + """ + Gets the state of the fan status LED + + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings above + """ + color = "off" + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_led_path_prefix + 'sys_led_status') + if (attr_rv != None): + if (int(attr_rv, 16) == 0x1) : + color = "green" + elif (int(attr_rv, 16) == 0x4): + color = "blink_green" + elif (int(attr_rv, 16) == 0x0): + color = "off" + else: + color = "red" + + return color + + def get_sfp(self, index): + """ + Retrieves sfp represented by (0-based) index + + Args: + index: An integer, the index (0-based) of the sfp to retrieve. + The index should be the sequence of a physical port in a chassis, + starting from 0. + For example, 0 for Ethernet0, 1 for Ethernet4 and so on. + + Returns: + An object dervied from SfpBase representing the specified sfp + """ + sfp = None + + try: + # The index will start from 0 + sfp = self._sfp_list[index] + except IndexError: + sys.stderr.write("SFP index {} out of range (0-{})\n".format( + index, len(self._sfp_list))) + return sfp + + def get_eeprom(self): + """ + Retreives eeprom device on this chassis + + Returns: + An object derived from WatchdogBase representing the hardware + eeprom device + """ + return self._eeprom + + def get_all_voltages(self): + """ + Retrieves all voltages available on this chassis + + Returns: + A list of objects derived from VoltageBase representing all voltages + available on this chassis + """ + return self._voltage_list + + def get_base_mac(self): + """ + Retrieves the base MAC address for the chassis + + Returns: + A string containing the MAC address in the format + 'XX:XX:XX:XX:XX:XX' + """ + return self._eeprom.base_mac_address() + + def get_serial_number(self): + """ + Retrieves the hardware serial number for the chassis + + Returns: + A string containing the hardware serial number for this chassis. + """ + return self._eeprom.serial_number_str() + + 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'} + """ + return self._eeprom.system_eeprom_info() + + 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. + """ + reboot_cause = self.REBOOT_CAUSE_NON_HARDWARE + thermal_overload_pos = 'None' + addational_fault_cause = 'None' + hw_reboot_cause = 'Unknown' + + #reservered for hardware reboot cause + if hw_reboot_cause != 'Unknown': + reboot_cause = self.REBOOT_CAUSE_HARDWARE_OTHER + + #software reboot cause + sw_reboot_cause = self.__api_helper.read_one_line_file(REBOOT_CAUSE_FILE) or "Unknown" + description = sw_reboot_cause + + #check power down reboot-cause + REBOOT_CAUSE_PWR_DOWN_PATH='/sys/devices/pci0000:00/0000:00:1f.3/i2c-0/i2c-1/i2c-4/4-0070/hwmon/hwmon4/pwr_down' + REBOOT_CAUSE_CPLD_PATH='/sys/devices/pci0000:00/0000:00:1f.3/i2c-0/i2c-1/1-0018/reboot_cause' + reboot_cause_pwr_down = 0 + reboot_cause_cpld = 0 + if os.path.isfile(REBOOT_CAUSE_PWR_DOWN_PATH): + attr_rv = self.__api_helper.read_one_line_file(REBOOT_CAUSE_PWR_DOWN_PATH) + reboot_cause_pwr_down = int(attr_rv, 16) + + #check watchdog/cold reboot + if os.path.isfile(REBOOT_CAUSE_CPLD_PATH): + attr_rv = self.__api_helper.read_one_line_file(REBOOT_CAUSE_CPLD_PATH) + reboot_cause_cpld = int(attr_rv, 16) + + #thermal policy reboot cause + if os.path.isfile(THERMAL_OVERLOAD_POSITION_FILE): + thermal_overload_pos = self.__api_helper.read_one_line_file(THERMAL_OVERLOAD_POSITION_FILE) + + if os.path.isfile(ADDITIONAL_FAULT_CAUSE_FILE): + addational_fault_cause = self.__api_helper.read_one_line_file(ADDITIONAL_FAULT_CAUSE_FILE) + + if (reboot_cause_pwr_down & 0x1): + reboot_cause = self.REBOOT_CAUSE_PSU_SHUTDOWN + elif (reboot_cause_cpld & 0x2): + reboot_cause = self.REBOOT_CAUSE_WATCHDOG + elif (reboot_cause_cpld & 0x4): + reboot_cause = self.REBOOT_CAUSE_COLD_RESET + elif (reboot_cause_cpld & 0x8): + reboot_cause = self.REBOOT_CAUSE_WARM_RESET + elif "CPU" in thermal_overload_pos: + reboot_cause = self.REBOOT_CAUSE_THERMAL_OVERLOAD_CPU + description = thermal_overload_pos + os.remove(THERMAL_OVERLOAD_POSITION_FILE) + elif "NPU" in thermal_overload_pos: + reboot_cause = self.REBOOT_CAUSE_THERMAL_OVERLOAD_ASIC + description = thermal_overload_pos + os.remove(THERMAL_OVERLOAD_POSITION_FILE) + elif thermal_overload_pos is not 'None': + reboot_cause = self.REBOOT_CAUSE_THERMAL_OVERLOAD_OTHER + description = thermal_overload_pos + os.remove(THERMAL_OVERLOAD_POSITION_FILE) + + if addational_fault_cause is not 'None': + description = ' '.join([description, addational_fault_cause]) + os.remove(ADDITIONAL_FAULT_CAUSE_FILE) + + prev_reboot_cause = (reboot_cause, description) + return prev_reboot_cause + + + + @property + def _get_presence_bitmap(self): + return self._sfp_list[1].get_all_presence() + + @property + def _get_fan_status_bit_map(self): + bit_map = 0 + for index in range(0,self.get_num_fan_drawers()): + bit_map |= int(self.get_fan_drawer(index).get_fan(0).get_presence()) << index + return bit_map + @property + def _get_psu_status_bit_map(self): + bit_map = 0 + for index in range(0,self.get_num_psus()): + bit_map |= int(self.get_psu(index).get_presence()) << index + return bit_map + + + data = {'sfp_present':0,'fan_status':0,'psu_status':0} + def get_transceiver_change_event(self, timeout=1000): + port_dict = {} + + if timeout == 0: + cd_ms = sys.maxsize + else: + cd_ms = timeout + + #poll per second + while cd_ms > 0: + reg_value = self._get_presence_bitmap + changed_ports = self.data['present'] ^ reg_value + if changed_ports != 0: + break + time.sleep(1) + cd_ms = cd_ms - 1000 + + if changed_ports != 0: + for port in range(0, self.__num_of_sfps): + # Mask off the bit corresponding to our port + mask = (1 << (port - 0)) + if changed_ports & mask: + if (reg_value & mask) == 0: + port_dict[port] = SFP_STATUS_REMOVED + else: + port_dict[port] = SFP_STATUS_INSERTED + + # Update cache + self.data['present'] = reg_value + return True, port_dict + else: + return True, {} + + def get_fan_change_event(self, timeout=1000): + fan_dict = {} + + if timeout == 0: + cd_ms = sys.maxsize + else: + cd_ms = timeout + + #poll per second + while cd_ms > 0: + reg_value = self._get_fan_status_bit_map + changed_fans = self.data['fan_status'] ^ reg_value + if changed_fans != 0: + break + time.sleep(1) + cd_ms = cd_ms - 1000 + + if changed_fans != 0: + for fan in range(0,self.get_num_fan_drawers()): + # Mask off the bit corresponding to our fan + mask = (1 << (fan - 0)) + if changed_fans & mask: + if (reg_value & mask) == 0: + fan_dict[fan] = PSU_STATUS_REMOVED + else: + fan_dict[fan] = PSU_STATUS_INSERTED + # Update cache + self.data['fan_status'] = reg_value + return {True: fan_dict} + else: + return {False: {}} + + def get_psu_change_event(self, timeout=1000): + psu_dict = {} + + if timeout == 0: + cd_ms = sys.maxsize + else: + cd_ms = timeout + + #poll per second + while cd_ms > 0: + reg_value = self._get_psu_status_bit_map + changed_psus = self.data['psu_status'] ^ reg_value + if changed_psus != 0: + break + time.sleep(1) + cd_ms = cd_ms - 1000 + + if changed_psus != 0: + for psu in range(0, self.get_num_psus()): + # Mask off the bit corresponding to our psu + mask = (1 << (psu - 0)) + if changed_psus & mask: + if (reg_value & mask) == 0: + psu_dict[psu] = FAN_STATUS_REMOVED + else: + psu_dict[psu] = FAN_STATUS_INSERTED + # Update cache + self.data['psu_status'] = reg_value + return {True: psu_dict} + else: + return {False:{}} + + def get_change_event(self, timeout=0): + res_dict = { + 'component': {}, + 'fan': {}, + 'module': {}, + 'psu': {}, + 'sfp': {}, + 'thermal': {}, + } + ''' get transceiver change event ''' + res_dict['sfp'].clear() + status, res_dict['sfp'] = self.get_transceiver_change_event(timeout) + return status, res_dict diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/component.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/component.py new file mode 100755 index 000000000000..5ed5de881cf5 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/component.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python + +######################################################################## +# Clounix +# +# Module contains an implementation of SONiC Platform Base API and +# provides the Components' (e.g., BIOS, CPLD, FPGA, etc.) available in +# the platform +# +######################################################################## + +try: + import os + import re + from sonic_platform_base.component_base import ComponentBase + from .helper import APIHelper +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +BIOS_QUERY_VERSION_COMMAND = "dmidecode -s bios-version" + + +class Component(ComponentBase): + """Clounix Platform-specific Component class""" + def __init__(self, component_index,component_conf): + self.__index = component_index + self.__conf = component_conf + self.__api_helper = APIHelper() + + def get_name(self): + """ + Retrieves the name of the component + + Returns: + A string containing the name of the component + """ + return self.__conf[self.__index]['name'] + + def get_description(self): + """ + Retrieves the description of the component + + Returns: + A string containing the description of the component + """ + return self.__conf[self.__index]['description'] + + def get_version_path(self): + """ + Retrieves the version path of the component + + Returns: + A string containing the version path of the component + """ + return self.__conf[self.__index]['firmware_version_path'] + + def get_hw_version_path(self): + """ + Retrieves the version path of the component + + Returns: + A string containing the version path of the component + """ + return self.__conf[self.__index]['hw_version_path'] + """ + def get_hw_version(self): + hw_version = 'N/A' + attr_rv = self.__api_helper.read_one_line_file(self.get_hw_version_path()) + if attr_rv != None: + hw_version = attr_rv + else: + if self.get_name() == 'BIOS': + ret,bios_ver = self.__api_helper.run_command(BIOS_QUERY_VERSION_COMMAND) + if ret: + if bios_ver: + hw_version = re.findall('\d',bios_ver)[0] + return hw_version + """ + + + def get_firmware_version(self): + """ + Retrieves the firmware version of the component + + Returns: + A string containing the firmware version of the component + """ + firmware_version = 'N/A' + attr_rv = self.__api_helper.read_one_line_file(self.get_version_path()) + if attr_rv != None: + firmware_version = attr_rv + else: + if self.get_name() == 'BIOS': + ret,bios_ver = self.__api_helper.run_command(BIOS_QUERY_VERSION_COMMAND) + if ret: + if bios_ver: + firmware_version = bios_ver + return firmware_version + + def get_hw_version(self): + return self.get_firmware_version() + + def check_file_validity(self, image_path): + """ + Check installation file validation + + Returns: + bool: True if installation file is valid, False if not + """ + + if not os.path.isfile(image_path): + print("ERROR: File {} doesn't exist or is not a file".format(image_path)) + return False + file_ext_dist = {'MCU-MB':'.efm8', 'MCU-FB':'.efm8', 'CPLD-A':'.vme', 'CPLD-B':'.vme','CPLD-C':'.vme','CPLD-D':'.vme','FPGA':'.bin', 'BIOS':'.bin'} + name_list = os.path.splitext(image_path) + image_ext_name = file_ext_dist[self.get_name()] + if image_ext_name is not None: + if name_list[1] != image_ext_name: + print("ERROR: Extend name of file {} is wrong. Image for {} should have extend name {}".format(image_path, self.get_name(), image_ext_name)) + return False + file_name_dist = {'MCU-MB':'mcu_mb', 'MCU-FB':'mcu_fb', 'CPLD-A':'cpld_mb', 'CPLD-B':'cpld_mb','CPLD-C':'cpld_mb','CPLD-D':'cpld_cpu', 'FPGA':'fpga_mb', 'BIOS':'bios_cpu'} + image_key_name = file_name_dist[self.get_name()] + if image_key_name is not None: + if not image_key_name in image_path: + print("ERROR: File name of file {} is wrong. Image for {} should have key name {}".format(image_path, self.get_name(), image_key_name)) + return False + + return True + + def version_calc_value(self, version): + """ + transfer version string to value, eg 1.2.3.4 tranfer to 1 * 100 + 2 *1 + 3 * 0.01 + 4 * 0.0001 + + Returns: + version value + """ + version_split = version.split('.') + sum = 0 + factor=100 + for val in version_split: + sum += float(val) * factor + factor = factor * 0.01 + return sum + + def check_version_validity(self, image_path): + """ + Check installation version validation + + Returns: + bool: True if installation version is valid, False if not + """ + + run_version = self.get_firmware_version() + + install_version = re.findall(r'v(.+?)\.', image_path) + if install_version: + version = ''.join(install_version).replace('_', '.') + else: + print("please make sure installation version is from released version like as _V0_10.xxx") + return False + + version_val = self.version_calc_value(version) + run_version_val = self.version_calc_value(run_version) + if version_val == run_version_val: + print("skip installation version %.2f for runing version %.2f." % (version_val, run_version_val)) + return False + + return True + + def check_image_validity(self, image_path): + """ + Check installation image validation + + Returns: + bool: True if installation is valid, False if not + """ + + if not self.check_file_validity(image_path): + return False + + if not self.check_version_validity(image_path): + return False + + return True + + 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 + """ + if not self.check_image_validity(image_path): + return False + + if self.get_name() == 'BIOS': + cmd = "/usr/local/bin/install_firmware.sh bios "+ image_path + elif self.get_name() == 'MCU-MB': + cmd = "/usr/local/bin/install_firmware.sh mcu-mb "+ image_path + elif self.get_name() == 'MCU-FB': + cmd = "/usr/local/bin/install_firmware.sh mcu-fb "+ image_path + elif self.get_name() == 'CPLD-A' or self.get_name() == 'CPLD-B' or self.get_name() == 'CPLD-C': + cmd = "/usr/local/bin/install_firmware.sh cpld-mb "+ image_path + elif self.get_name() == 'CPLD-D': + cmd = "/usr/local/bin/install_firmware.sh cpld-cpu "+ image_path + elif self.get_name() == 'FPGA': + cmd = "/usr/local/bin/install_firmware.sh fpga "+ image_path + else: + print(self.get_name() + 'frimware upgrade did not implement') + + os.system(cmd) + #start service + os.system("/usr/local/bin/install_firmware_ext.sh start") + return True + + def get_presence(self): + """ + Retrieves the presence of the device + + Returns: + bool: True if device is present, False if not + """ + return True + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + + Returns: + string: Model/part number of device + """ + model = 'N/A' + return model + + def get_serial(self): + """ + Retrieves the serial number of the device + + Returns: + string: Serial number of device + """ + serial = 'N/A' + return serial + + def get_status(self): + """ + Retrieves the operational status of the device + + Returns: + A boolean value, True if device is operating properly, False if not + """ + return self.get_presence() + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return -1 + + def is_replaceable(self): + """ + Indicate whether Fan is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/eeprom.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/eeprom.py new file mode 100755 index 000000000000..f7f744b02974 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/eeprom.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +# +# Name: eeprom.py, version: 1.0 +# +# Description: Module contains the definitions of SONiC platform APIs +# + +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): + + def __init__(self): + self.__eeprom_path = "/sys/class/i2c-adapter/i2c-9/9-0054/eeprom" + super(Eeprom, self).__init__(self.__eeprom_path, 0, '', True) + self.__eeprom_tlv_dict = dict() + try: + self.__eeprom_data = self.read_eeprom() + except: + self.__eeprom_data = "N/A" + raise RuntimeError("Eeprom is not Programmed") + else: + eeprom = self.__eeprom_data + + if not self.is_valid_tlvinfo_header(eeprom): + return + + total_length = ((eeprom[9]) << 8) | (eeprom[10]) + tlv_index = self._TLV_INFO_HDR_LEN + tlv_end = self._TLV_INFO_HDR_LEN + total_length + + while (tlv_index + 2) < len(eeprom) and tlv_index < tlv_end: + if not self.is_valid_tlv(eeprom[tlv_index:]): + break + + tlv = eeprom[tlv_index:tlv_index + 2 + + (eeprom[tlv_index + 1])] + code = "0x%02X" % ((tlv[0])) + + if (tlv[0]) == self._TLV_CODE_VENDOR_EXT: + value = str(((tlv[2]) << 24) | ((tlv[3]) << 16) | + ((tlv[4]) << 8) | (tlv[5])) + value += str(tlv[6:6 + (tlv[1])]) + else: + name, value = self.decoder(None, tlv) + + self.__eeprom_tlv_dict[code] = value + if (eeprom[tlv_index]) == self._TLV_CODE_CRC_32: + break + + tlv_index += (eeprom[tlv_index+1]) + 2 + + def serial_number_str(self): + (is_valid, results) = self.get_tlv_field( + self.__eeprom_data, self._TLV_CODE_SERIAL_NUMBER) + if not is_valid: + return "N/A" + return results[2] + + def base_mac_address(self): + (is_valid, t) = self.get_tlv_field( + self.__eeprom_data, self._TLV_CODE_MAC_BASE) + if not is_valid or t[1] != 6: + return super(eeprom_tlvinfo.TlvInfoDecoder, self).switchaddrstr(self.__eeprom_data) + + return ":".join([binascii.b2a_hex(T) for T in t[2]]) + + def modelstr(self): + (is_valid, results) = self.get_tlv_field( + self.__eeprom_data, self._TLV_CODE_PRODUCT_NAME) + if not is_valid: + return "N/A" + + return results[2] + + def part_number_str(self): + (is_valid, results) = self.get_tlv_field( + self.__eeprom_data, self._TLV_CODE_PART_NUMBER) + if not is_valid: + return "N/A" + + return results[2] + + def serial_tag_str(self): + (is_valid, results) = self.get_tlv_field( + self.__eeprom_data, self._TLV_CODE_SERVICE_TAG) + if not is_valid: + return "N/A" + + return results[2] + + def revision_str(self): + (is_valid, results) = self.get_tlv_field( + self.__eeprom_data, self._TLV_CODE_DEVICE_VERSION) + if not is_valid: + return "N/A" + + return results[2] + + def system_eeprom_info(self): + """ + Returns a dictionary, where keys are the type code defined in + ONIE EEPROM format and values are their corresponding values + found in the system EEPROM. + """ + return self.__eeprom_tlv_dict + diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/fan.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/fan.py new file mode 100755 index 000000000000..71a4cc638a5e --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/fan.py @@ -0,0 +1,294 @@ +#!/usr/bin/env python +# +# Name: fan.py, version: 1.0 +# +# Description: Module contains the definitions of SONiC platform APIs +# + +try: + from sonic_platform_base.fan_base import FanBase + from .helper import APIHelper +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +class Fan(FanBase): + + def __init__(self, index,fan_conf): + self.__index = index + self.__conf = fan_conf + self.__api_helper = APIHelper() + self.__attr_path_prefix = '' + self.__is_psu_fan = False + FanBase.__init__(self) + + if fan_conf[self.__index]['container'] == 'psu': + self.__attr_path_prefix = '/sys/switch/psu/psu{}/'.format(fan_conf[self.__index]['container_index'] + 1) + self.__is_psu_fan = True + elif fan_conf[self.__index]['container'] == 'fan_drawer': + self.__attr_path_prefix = '/sys/switch/fan/fan{}/'.format(self.__index+1) + else: + #chassis fan to be done + self.__attr_path_prefix = '/sys/switch/fan/fan{}/'.format(self.__index+1) + +############################################## +# Device methods +############################################## + + def get_name(self): + """ + Retrieves the name of the device + + Returns: + string: The name of the device + """ + return self.__conf[self.__index]['name'] + + def get_presence(self): + """ + Retrieves the presence of the device + + Returns: + bool: True if device is present, False if not + """ + presence = False + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'status') + if (attr_rv != None): + attr_rv = int(attr_rv, 16) + if (attr_rv): + presence = True + return presence + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + + Returns: + string: Model/part number of device + """ + model = 'N/A' + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'model_name') + if (attr_rv != None): + model = attr_rv + + return model + + def get_serial(self): + """ + Retrieves the serial number of the device + + Returns: + string: Serial number of device + """ + serial = 'N/A' + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'serial_number') + if (attr_rv != None): + serial = attr_rv + + return serial + + def get_status(self): + """ + Retrieves the operational status of the device + + Returns: + A boolean value, True if device is operating properly, False if not + """ + status = False + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'status') + if (attr_rv != None): + attr_rv = int(attr_rv, 16) + if (attr_rv == 0x1): + status = True + return status + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return self.__index + 1 + + def is_replaceable(self): + """ + Indicate whether Fan is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True + + def get_vendor(self): + vendor = 'N/A' + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'vendor') + if (attr_rv != None): + vendor = attr_rv + return vendor + + def get_part_number(self): + part_num = 'N/A' + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'part_number') + if (attr_rv != None): + part_num = attr_rv + return part_num + + def get_hw_version(self): + hw_version = 'N/A' + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'hardware_version') + if (attr_rv != None): + hw_version = attr_rv + return hw_version + +############################################## +# FAN methods +############################################## + + def get_direction(self): + """ + Retrieves the direction of fan + + Returns: + A string, either FAN_DIRECTION_INTAKE or FAN_DIRECTION_EXHAUST + depending on fan direction + """ + direction = self.FAN_DIRECTION_EXHAUST + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'direction') + if (attr_rv != None): + attr_rv = int(attr_rv) + if attr_rv == 0: + direction = self.FAN_DIRECTION_EXHAUST + else: + direction = self.FAN_DIRECTION_INTAKE + + return direction + + def get_speed(self): + """ + Retrieves the speed of fan as a percentage of full speed + + Returns: + An integer, the percentage of full fan speed, in the range 0 (off) + to 100 (full speed) + """ + speed = 0 + if self.__is_psu_fan : + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'fan_speed') + else: + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'motor0/speed') + + if (attr_rv != None): + attr_rv = int(attr_rv) + if (attr_rv >= 0): + speed = attr_rv + + return speed + + def get_ratio(self): + """ + Retrieves the speed of fan as a percentage of full speed + + Returns: + An integer, the percentage of full fan speed, in the range 0 (off) + to 100 (full speed) + """ + ratio = 0 + if self.__is_psu_fan : + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'fan_speed') + else: + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'motor0/ratio') + + if (attr_rv != None): + attr_rv = int(attr_rv) + if (attr_rv >= 0): + ratio = attr_rv + + return ratio + + 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() + + 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 100 + + def set_speed(self, speed): + """ + Sets the fan speed + + Args: + speed: An integer, the percentage of full fan speed to set fan to, + in the range 0 (off) to 100 (full speed) + + Returns: + A boolean, True if speed is set successfully, False if not + """ + raise NotImplementedError + + def set_status_led(self, color): + """ + Sets the state of the fan module status LED + + Args: + color: A string representing the color with which to set the + fan module status LED + + Returns: + bool: True if status LED state is set successfully, False if not + """ + ret_val = False + if color == self.STATUS_LED_COLOR_GREEN: + led_value = 1 + elif color == self.STATUS_LED_COLOR_RED: + led_value = 3 + else: + return False + ret_val = self.__api_helper.write_txt_file(self.__attr_path_prefix + 'led_status',led_value) + + return ret_val + + def get_status_led(self): + """ + Gets the state of the fan status LED + + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings above + """ + color = self.STATUS_LED_COLOR_OFF + if self.get_presence() is False : + return color + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'led_status') + if (attr_rv != None): + if (int(attr_rv, 16) == 0x1 or int(attr_rv, 16) == 0x4) : + color = self.STATUS_LED_COLOR_GREEN + elif (int(attr_rv, 16) == 0x3 or int(attr_rv, 16) == 0x6) : + color = self.STATUS_LED_COLOR_RED + + if self.__is_psu_fan : + if self.get_status() is False: + color = self.STATUS_LED_COLOR_RED + + return color + + diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/fan_drawer.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/fan_drawer.py new file mode 100644 index 000000000000..c6c711941511 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/fan_drawer.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python + +############################################################################# +# Clounix +# +# Module contains an implementation of SONiC Platform Base API and +# provides the fan status which are available in the platform +# +############################################################################# + +try: + from sonic_platform_base.fan_drawer_base import FanDrawerBase + from sonic_platform.fan import Fan +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class FanDrawer(FanDrawerBase): + + def __init__(self, index,fandrawer_conf): + self.__conf = fandrawer_conf + self.__num_of_fans = len(self.__conf[index]['fans']) + self.__index = index + self._fan_list = [] + FanDrawerBase.__init__(self) + + # Initialize FAN_DRAWER FAN + fan_conf=self.__conf[self.__index]['fans'] + for x in range(0, self.__num_of_fans): + fan_conf[x].update({'container':'fan_drawer'}) + fan_conf[x].update({'container_index': self.__index}) + fan = Fan(x, fan_conf) + self._fan_list.append(fan) + + def set_status_led(self, color): + """ + Sets the state of the fan drawer status LED + Args: + color: A string representing the color with which to set the + fan drawer status LED + Returns: + bool: True if status LED state is set successfully, False if not + """ + return self._fan_list[0].set_status_led(color) + + def get_status_led(self): + """ + Gets the state of the fan drawer LED + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings above + """ + return self._fan_list[0].get_status_led() + + def get_name(self): + """ + Retrieves the name of the device + Returns: + string: The name of the device + """ + return self.__conf[self.__index]['name'] + + def get_presence(self): + """ + Retrieves the presence of the FAN + Returns: + bool: True if FAN is present, False if not + """ + return self._fan_list[0].get_presence() + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + return self._fan_list[0].get_model() + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + return self._fan_list[0].get_serial() + + def get_status(self): + """ + Retrieves the operational status of the device + Returns: + A boolean value, True if device is operating properly, False if not + """ + return self._fan_list[0].get_status() + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return self.__index + 1 + + def is_replaceable(self): + """ + Indicate whether Fan is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True + + def get_vendor(self): + return self._fan_list[0].get_vendor() + + + def get_part_number(self): + return self._fan_list[0].get_part_number() + + def get_hw_version(self): + return self._fan_list[0].get_hw_version() diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/helper.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/helper.py new file mode 100644 index 000000000000..eed5d5482954 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/helper.py @@ -0,0 +1,156 @@ +import os +import struct +import subprocess +import json +from mmap import * + +from sonic_py_common import device_info + +HOST_CHK_CMD = "docker > /dev/null 2>&1" +EMPTY_STRING = "" + +HOST_PLATFORM_JSON = '/usr/share/sonic/device/x86_64-pegatron_fn8656_bnf-r0/platform.json' +PMON_PLATFORM_JSON = '/usr/share/sonic/platform/platform.json' + +class APIHelper(): + + def __init__(self): + (self.platform, self.hwsku) = device_info.get_platform_and_hwsku() + + def is_host(self): + return os.system(HOST_CHK_CMD) == 0 + + def pci_get_value(self, resource, offset): + status = True + result = "" + try: + fd = os.open(resource, os.O_RDWR) + mm = mmap(fd, 0) + mm.seek(int(offset)) + read_data_stream = mm.read(4) + result = struct.unpack('I', read_data_stream) + except Exception: + status = False + return status, result + + def run_command(self, cmd): + status = True + result = "" + try: + p = subprocess.Popen( + cmd, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err == '': + result = raw_data.strip() + except Exception: + status = False + return status, result + + def run_interactive_command(self, cmd): + try: + os.system(cmd) + except Exception: + return False + return True + + def read_txt_file(self, file_path): + try: + with open(file_path, 'r') as fd: + data = fd.read() + fd.close() + return data.strip() + except IOError: + pass + return None + + def read_one_line_file(self, file_path): + try: + with open(file_path, 'r') as fd: + data = fd.readline() + fd.close() + return data.strip() + except IOError: + pass + return None + + def write_txt_file(self, file_path, value): + try: + with open(file_path, 'w') as fd: + fd.write(str(value)) + fd.close() + except IOError: + return False + return True + + def get_cpld_reg_value(self, getreg_path, register): + cmd = "echo {1} > {0}; cat {0}".format(getreg_path, register) + status, result = self.run_command(cmd) + return result if status else None + + def ipmi_raw(self, netfn, cmd): + status = True + result = "" + try: + cmd = "ipmitool raw {} {}".format(str(netfn), str(cmd)) + p = subprocess.Popen( + cmd, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err == '': + result = raw_data.strip() + else: + status = False + except Exception: + status = False + return status, result + + def ipmi_fru_id(self, id, key=None): + status = True + result = "" + try: + cmd = "ipmitool fru print {}".format(str( + id)) if not key else "ipmitool fru print {0} | grep '{1}' ".format(str(id), str(key)) + + p = subprocess.Popen( + cmd, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err == '': + result = raw_data.strip() + else: + status = False + except Exception: + status = False + return status, result + + def ipmi_set_ss_thres(self, id, threshold_key, value): + status = True + result = "" + try: + cmd = "ipmitool sensor thresh '{}' {} {}".format( + str(id), str(threshold_key), str(value)) + p = subprocess.Popen( + cmd, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err == '': + result = raw_data.strip() + else: + status = False + except Exception: + status = False + return status, result + + def get_attr_conf(self, attr): + """ + Retrieves the json object from json file path + + Returns: + A json object + """ + json_path = '' + if self.is_host() is True: + json_path = HOST_PLATFORM_JSON + else : + json_path = PMON_PLATFORM_JSON + with open(json_path, 'r') as f: + json_data = json.load(f) + + return json_data[attr] diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/platform.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/platform.py new file mode 100755 index 000000000000..7d6bda4502de --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/platform.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +# +# Name: platform.py, version: 1.0 +# +# Description: Module contains the definitions of SONiC platform APIs +# + + +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/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/psu.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/psu.py new file mode 100755 index 000000000000..fde2da8f8707 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/psu.py @@ -0,0 +1,391 @@ +#!/usr/bin/env python +# +# Name: psu.py, version: 1.0 +# +# Description: Module contains the definitions of SONiC platform APIs +# + +try: + #import os + from sonic_platform_base.psu_base import PsuBase + from sonic_platform.fan import Fan + from sonic_platform.thermal import Thermal + from .helper import APIHelper +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +class Psu(PsuBase): + + def __init__(self, index,psu_conf): + self.__num_of_fans = len(psu_conf[index]['fans']) + self.__num_of_thermals = len(psu_conf[index]['thermals']) + self.__index = index + self.__conf = psu_conf + self.__api_helper = APIHelper() + self.__attr_path_prefix = '/sys/switch/psu/psu{}/'.format(self.__index + 1) + + self._fan_list = [] + self._thermal_list = [] + PsuBase.__init__(self) + # Initialize PSU FAN + fan_conf=self.__conf[self.__index]['fans'] + for x in range(0, self.__num_of_fans): + fan_conf[x].update({'container':'psu'}) + fan_conf[x].update({'container_index': self.__index}) + fan = Fan(x, fan_conf) + self._fan_list.append(fan) + + # Initialize PSU THERMAL + thermal_conf=self.__conf[self.__index]['thermals'] + for x in range(0, self.__num_of_thermals): + thermal_conf[x].update({'container':'psu'}) + thermal_conf[x].update({'container_index': self.__index}) + thermal = Thermal(x,thermal_conf) + self._thermal_list.append(thermal) + +############################################## +# Device methods +############################################## + + def get_name(self): + """ + Retrieves the name of the device + + Returns: + string: The name of the device + """ + return self.__conf[self.__index]['name'] + + def get_presence(self): + """ + Retrieves the presence of the device + + Returns: + bool: True if device is present, False if not + """ + presence = False + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'status') + if (attr_rv != None): + attr_rv = int(attr_rv, 16) + if (attr_rv): + presence = True + return presence + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + + Returns: + string: Model/part number of device + """ + model = 'N/A' + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'model_name') + if (attr_rv != None): + model = attr_rv + + return model + + def get_serial(self): + """ + Retrieves the serial number of the device + + Returns: + string: Serial number of device + """ + serial = 'N/A' + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'serial_number') + if (attr_rv != None): + serial = attr_rv + + return serial + + def get_status(self): + """ + Retrieves the operational status of the device + + Returns: + A boolean value, True if device is operating properly, False if not + """ + status = False + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'status') + if (attr_rv != None): + attr_rv = int(attr_rv, 16) + if(attr_rv == 0x1): + status = True + return status + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return self.__index + 1 + + def is_replaceable(self): + """ + Indicate whether Fan is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True + + def get_date(self): + date = 'N/A' + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'date') + if (attr_rv != None): + date = attr_rv + return date + + def get_vendor(self): + vendor = 'N/A' + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'vendor') + if (attr_rv != None): + vendor = attr_rv + return vendor + + def get_hw_version(self): + hw_version = 'N/A' + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'hardware_version') + if (attr_rv != None): + hw_version = attr_rv + return hw_version + + def get_alarm(self): + alarm = 'Exception: ' + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'alarm') + if (attr_rv != None): + attr_rv = int(attr_rv, 16) + if(attr_rv == 0x0): + alarm = 'Normal' + return alarm + elif alarm & 0x01: + alarm += ' Temperature' + elif alarm & 0x02: + alarm += ' Fan' + elif alarm & 0x04: + alarm += ' Voltage' + + return alarm + + def get_alarm_threshold_curr(self): + alarm_threshold_curr = 'N/A' + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'alarm_threshold_curr') + if (attr_rv != None): + alarm_threshold_curr = attr_rv + return alarm_threshold_curr + + def get_alarm_threshold_vol(self): + alarm_threshold_vol = 'N/A' + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'alarm_threshold_vol') + if (attr_rv != None): + alarm_threshold_vol = attr_rv + return alarm_threshold_vol + + def get_part_number(self): + part_number = 'N/A' + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'part_number') + if (attr_rv != None): + part_number = attr_rv + return part_number + + def get_in_current(self): + in_curr = 'N/A' + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'in_curr') + if (attr_rv != None): + in_curr = attr_rv + return in_curr + def get_in_voltage(self): + in_vol = 'N/A' + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'in_vol') + if (attr_rv != None): + in_vol = attr_rv + return in_vol + def get_in_power(self): + in_power = 'N/A' + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'in_power') + if (attr_rv != None): + in_power = attr_rv + return in_power + +############################################## +# PSU methods +############################################## + + def get_voltage(self): + """ + Retrieves current PSU voltage output + + Returns: + A float number, the output voltage in volts, + e.g. 12.1 + """ + voltage_out = 0.0 + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'out_vol') + if (attr_rv != None): + voltage_out = float(attr_rv) + + return voltage_out + + def get_current(self): + """ + Retrieves present electric current supplied by PSU + + Returns: + A float number, the electric current in amperes, e.g 15.4 + """ + current_out = 0.0 + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'out_curr') + if (attr_rv != None): + current_out = float(attr_rv) + + return current_out + + def get_power(self): + """ + Retrieves current energy supplied by PSU + + Returns: + A float number, the power in watts, e.g. 302.6 + """ + power_out = 0.0 + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'out_power') + if (attr_rv != None): + power_out = float(attr_rv) + + return power_out + + def get_powergood_status(self): + """ + Retrieves the powergood status of PSU + + Returns: + A boolean, True if PSU has stablized its output voltages and passed all + its internal self-tests, False if not. + """ + return self.get_status() + + def set_status_led(self, color): + """ + Sets the state of the PSU status LED + + Args: + color: A string representing the color with which to set the + PSU status LED + + Returns: + bool: True if status LED state is set successfully, False if not + """ + raise NotImplementedError + + def get_status_led(self): + """ + Gets the state of the PSU status LED + + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings above + """ + color = "off" + + if self.get_presence() is False : + return color + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'led_status') + if (attr_rv != None): + if (int(attr_rv, 16) == 0x1 or int(attr_rv, 16) == 0x4) : + color = "green" + elif (int(attr_rv, 16) == 0x3 or int(attr_rv, 16) == 0x6) : + color = "red" + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'status') + if (attr_rv != None): + attr_rv = int(attr_rv, 16) + if(attr_rv == 0x2): + color = "red" + return color + + 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 + """ + voltage_high_threshold = 0.0 + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'max_output_vol') + if (attr_rv != None): + voltage_high_threshold = float(attr_rv) + + return voltage_high_threshold + + 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 + """ + voltage_low_threshold = 0.0 + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'min_output_vol') + if (attr_rv != None): + voltage_low_threshold = float(attr_rv) + + return voltage_low_threshold + + 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 + """ + max_power_out = 0.0 + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'max_output_power') + if (attr_rv != None): + max_power_out = float(attr_rv) + + return max_power_out + + 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 + """ + if self.get_presence(): + return self.get_thermal(0).get_high_threshold() + else: + return 0.0 + + 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 + """ + if self.get_presence(): + return self.get_thermal(0).get_temperature() + else: + return 0.0 + + + diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/qsfp.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/qsfp.py new file mode 100755 index 000000000000..d0dc5b27239f --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/qsfp.py @@ -0,0 +1,612 @@ +#!/usr/bin/env python +# +# Name: qsfp.py, version: 1.0 +# +# Description: Module contains the definitions of SONiC platform APIs +# + +import os +#import time +#from ctypes import create_string_buffer + +try: + from sonic_platform_base.sfp_base import SfpBase + # from sonic_platform_base.sonic_sfp.sfputilhelper import SfpUtilHelper + from .helper import APIHelper + from sonic_platform.qsfp_8436 import QSfp_SFF8436 + from sonic_platform.qsfp_cmis import QSfp_CMIS +except ImportError as e: + raise ImportError (str(e) + "- required module not found") + + +XCVR_TYPE_OFFSET = 0 +XCVR_TYPE_WIDTH = 1 + +SFP_TYPE_CODE_LIST = [ + '03' # SFP/SFP+/SFP28 +] +QSFP_TYPE_CODE_LIST = [ + '0d', # QSFP+ or later + '11' # QSFP28 or later +] +QSFP_DD_TYPE_CODE_LIST = [ + '18', # QSFP-DD Double Density 8X Pluggable Transceiver + '1b' # QSFP-DD Double Density 8X Pluggable Transceiver +] +CMIS_TYPE_CODE_LIST = [ + '18', # QSFP-DD Double Density 8X Pluggable Transceiver + '1b' # DSFP Dual Small Form Factor Pluggable Transceiver +] + + +SFP_TYPE = "SFP" +QSFP_TYPE = "QSFP" +OSFP_TYPE = "OSFP" +QSFP_DD_TYPE = "QSFP_DD" + +TRANSCEIVER_PATH = '/sys/switch/transceiver/' + +class QSfp(SfpBase): + + port_start = 0 + port_end = 55 + + dom_supported = True + dom_temp_supported = True + dom_volt_supported = True + dom_rx_power_supported = True + dom_tx_power_supported = True + dom_tx_disable_supported = True + calibration = 1 + + def __init__(self, index,qsfp_conf): + self.__index = index + self.__conf = qsfp_conf + self.__api_helper = APIHelper() + + self.__attr_path_prefix = '/sys/switch/transceiver/eth{}/'.format(self.__index+1) + + self.__presence_attr = None + self.__eeprom_path = None + + if self.__index in range(0, self.port_end + 1): + self.__eeprom_path = TRANSCEIVER_PATH+'eth{}/'.format(self.__index+1)+'eeprom' + self.__presence_attr = TRANSCEIVER_PATH+'eth{}/'.format(self.__index+1)+'present' + self.__reset_attr = TRANSCEIVER_PATH+'eth{}/'.format(self.__index+1)+'reset' + self.__lpmode_attr = TRANSCEIVER_PATH+'eth{}/'.format(self.__index+1)+'lpmode' + SfpBase.__init__(self) + self._detect_sfp_type(QSFP_DD_TYPE) + self.info_dict_keys = ['type', 'hardware_rev', 'serial', 'manufacturer', + 'model', 'connector', 'encoding', 'ext_identifier', + 'ext_rateselect_compliance', 'cable_type', 'cable_length', + 'nominal_bit_rate', 'specification_compliance', 'vendor_date', 'vendor_oui', 'application_advertisement'] + self.qsfp_8436 = QSfp_SFF8436(index,qsfp_conf) + self.qsfp_cmis = QSfp_CMIS(index,qsfp_conf) + self.is_cmis = False + + def _detect_sfp_type(self, sfp_type): + sfp_type = QSFP_DD_TYPE + self.is_cmis = False + + if not self.get_presence(): + self.sfp_type = sfp_type + return + + eeprom_raw = [] + + eeprom_raw = self._read_eeprom_specific_bytes( + XCVR_TYPE_OFFSET, XCVR_TYPE_WIDTH) + if eeprom_raw: + if eeprom_raw[0] in SFP_TYPE_CODE_LIST: + self.sfp_type = SFP_TYPE + elif eeprom_raw[0] in QSFP_TYPE_CODE_LIST: + #self.sfp_type = QSFP_TYPE + self.sfp_type = QSFP_DD_TYPE #xcvrd.py unable to set SI settings when sfp_type is QSFP + elif eeprom_raw[0] in QSFP_DD_TYPE_CODE_LIST: + self.sfp_type = QSFP_DD_TYPE + else: + self.sfp_type = sfp_type + if eeprom_raw[0] in CMIS_TYPE_CODE_LIST : + self.is_cmis = True + else: + self.sfp_type = sfp_type + + + def __is_host(self): + return os.system("docker > /dev/null 2>&1") == 0 + + def _read_eeprom_specific_bytes(self, offset, num_bytes): + sysfsfile_eeprom = None + eeprom_raw = [] + + for i in range(0, num_bytes): + eeprom_raw.append("0x00") + + sysfs_eeprom_path = self.__eeprom_path + try: + sysfsfile_eeprom = open(sysfs_eeprom_path, mode="rb", buffering=0) + sysfsfile_eeprom.seek(offset) + raw = sysfsfile_eeprom.read(num_bytes) + raw_len = len(raw) + for n in range(0, raw_len): + eeprom_raw[n] = hex(raw[n])[2:].zfill(2) + except BaseException: + pass + finally: + if sysfsfile_eeprom: + sysfsfile_eeprom.close() + + return eeprom_raw + + def read_eeprom(self, offset, num_bytes): + sysfsfile_eeprom = None + eeprom_read_bytes = [] + eeprom_raw = [] + raw = [] + + for i in range(0, num_bytes): + eeprom_raw.append("0x00") + + sysfs_eeprom_path = self.__eeprom_path + try: + sysfsfile_eeprom = open(sysfs_eeprom_path, mode="rb", buffering=0) + sysfsfile_eeprom.seek(offset) + raw = sysfsfile_eeprom.read(num_bytes) + raw_len = len(raw) + eeprom_read_bytes = bytearray(raw) + for n in range(0, raw_len): + eeprom_raw[n] = hex(raw[n])[2:].zfill(2) + except Exception: + pass + finally: + if sysfsfile_eeprom: + sysfsfile_eeprom.close() + + return eeprom_read_bytes + + def write_eeprom(self, offset, num_bytes, write_buffer): + try: + with open(self.__eeprom_path, mode='r+b', buffering=0) as f: + f.seek(offset) + f.write(write_buffer[0:num_bytes]) + f.close() + except (OSError, IOError): + return False + return True + +############################################## +# Device methods +############################################## + + def get_name(self): + """ + Retrieves the name of the device + + Returns: + string: The name of the device + """ + return self.__conf[self.__index]['name'] + + def get_presence(self): + """ + Retrieves the presence of the device + + Returns: + bool: True if device is present, False if not + """ + presence = False + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'present') + if (attr_rv != None): + attr_rv = int(attr_rv, 16) + if ((attr_rv & 0x1) == 1): + presence = True + return presence + + def get_all_presence(self): + """ + Retrieves the presence of the device + + Returns: + bool: True if device is present, False if not + """ + + presence_bit = 0 + attr_rv = self.__api_helper.read_one_line_file(TRANSCEIVER_PATH + 'present') + if (attr_rv != None): + presence_bit = int(attr_rv, 16) + return presence_bit + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + + Returns: + string: Model/part number of device + """ + transceiver_info_dict = self.get_transceiver_info() + return transceiver_info_dict.get("model", "N/A") + + def get_serial(self): + """ + Retrieves the serial number of the device + + Returns: + string: Serial number of device + """ + transceiver_info_dict = self.get_transceiver_info() + return transceiver_info_dict.get("serial", "N/A") + + def get_status(self): + """ + Retrieves the operational status of the device + + Returns: + A boolean value, True if device is operating properly, False if not + """ + return self.get_presence() and not self.get_reset_status() + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return self.__index + + def is_replaceable(self): + """ + Indicate whether Chassis is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True +############################################## +# SFP methods +############################################## + def get_transceiver_info(self): + """ + Retrieves transceiver info of this SFP + + Returns: + A dict which contains following keys/values : + ======================================================================== + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + type |1*255VCHAR |type of SFP + hardware_rev |1*255VCHAR |hardware version of SFP + serial |1*255VCHAR |serial number of the SFP + manufacturer |1*255VCHAR |SFP vendor name + model |1*255VCHAR |SFP model name + connector |1*255VCHAR |connector information + encoding |1*255VCHAR |encoding information + ext_identifier |1*255VCHAR |extend identifier + ext_rateselect_compliance |1*255VCHAR |extended rateSelect compliance + cable_length |INT |cable length in m + mominal_bit_rate |INT |nominal bit rate by 100Mbs + specification_compliance |1*255VCHAR |specification compliance + vendor_date |1*255VCHAR |vendor date + vendor_oui |1*255VCHAR |vendor OUI + ======================================================================== + """ + self._detect_sfp_type(self.sfp_type) + if self.is_cmis == True: + return self.qsfp_cmis.get_transceiver_info() + else: + return self.qsfp_8436.get_transceiver_info() + + + def get_transceiver_bulk_status(self): + """ + Retrieves transceiver bulk status of this SFP + + Returns: + A dict which contains following keys/values : + ======================================================================== + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + rx_los |BOOLEAN |RX loss-of-signal status, True if has RX los, False if not. + tx_fault |BOOLEAN |TX fault status, True if has TX fault, False if not. + reset_status |BOOLEAN |reset status, True if SFP in reset, False if not. + lp_mode |BOOLEAN |low power mode status, True in lp mode, False if not. + tx_disable |BOOLEAN |TX disable status, True TX disabled, False if not. + tx_disabled_channel |HEX |disabled TX channels in hex, bits 0 to 3 represent channel 0 + | |to channel 3. + temperature |INT |module temperature in Celsius + voltage |INT |supply voltage in mV + txbias |INT |TX Bias Current in mA, n is the channel number, + | |for example, tx2bias stands for tx bias of channel 2. + rxpower |INT |received optical power in mW, n is the channel number, + | |for example, rx2power stands for rx power of channel 2. + txpower |INT |TX output power in mW, n is the channel number, + | |for example, tx2power stands for tx power of channel 2. + ======================================================================== + """ + if self.is_cmis == True: + return self.qsfp_cmis.get_transceiver_bulk_status() + else: + return self.qsfp_8436.get_transceiver_bulk_status() + + def get_transceiver_threshold_info(self): + """ + Retrieves transceiver threshold info of this SFP + + Returns: + A dict which contains following keys/values : + ======================================================================== + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + temphighalarm |FLOAT |High Alarm Threshold value of temperature in Celsius. + templowalarm |FLOAT |Low Alarm Threshold value of temperature in Celsius. + temphighwarning |FLOAT |High Warning Threshold value of temperature in Celsius. + templowwarning |FLOAT |Low Warning Threshold value of temperature in Celsius. + vcchighalarm |FLOAT |High Alarm Threshold value of supply voltage in mV. + vcclowalarm |FLOAT |Low Alarm Threshold value of supply voltage in mV. + vcchighwarning |FLOAT |High Warning Threshold value of supply voltage in mV. + vcclowwarning |FLOAT |Low Warning Threshold value of supply voltage in mV. + rxpowerhighalarm |FLOAT |High Alarm Threshold value of received power in dBm. + rxpowerlowalarm |FLOAT |Low Alarm Threshold value of received power in dBm. + rxpowerhighwarning |FLOAT |High Warning Threshold value of received power in dBm. + rxpowerlowwarning |FLOAT |Low Warning Threshold value of received power in dBm. + txpowerhighalarm |FLOAT |High Alarm Threshold value of transmit power in dBm. + txpowerlowalarm |FLOAT |Low Alarm Threshold value of transmit power in dBm. + txpowerhighwarning |FLOAT |High Warning Threshold value of transmit power in dBm. + txpowerlowwarning |FLOAT |Low Warning Threshold value of transmit power in dBm. + txbiashighalarm |FLOAT |High Alarm Threshold value of tx Bias Current in mA. + txbiaslowalarm |FLOAT |Low Alarm Threshold value of tx Bias Current in mA. + txbiashighwarning |FLOAT |High Warning Threshold value of tx Bias Current in mA. + txbiaslowwarning |FLOAT |Low Warning Threshold value of tx Bias Current in mA. + ======================================================================== + """ + if self.is_cmis == True: + return self.qsfp_cmis.get_transceiver_threshold_info() + else: + return self.qsfp_8436.get_transceiver_threshold_info() + def get_reset_status(self): + """ + Retrieves the reset status of SFP + + Returns: + A Boolean, True if reset enabled, False if disabled + """ + reset_status = False + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'reset') + if (attr_rv != None): + attr_rv = int(attr_rv, 16) + if ((attr_rv & 0x1) == 1): + reset_status = True + return reset_status + + def get_rx_los(self): + """ + Retrieves the RX LOS (loss-of-signal) status of SFP + + Returns: + A list of boolean values, representing the RX LOS status + of each available channel, value is True if SFP channel + has RX LOS, False if not. + E.g., for a tranceiver with four channels: [False, False, True, False] + Note : RX LOS status is latched until a call to get_rx_los or a reset. + """ + if self.is_cmis == True: + return self.qsfp_cmis.get_rx_los() + else: + return self.qsfp_8436.get_rx_los() + + def get_tx_fault(self): + """ + Retrieves the TX fault status of SFP + + Returns: + A list of boolean values, representing the TX fault status + of each available channel, value is True if SFP channel + has TX fault, False if not. + E.g., for a tranceiver with four channels: [False, False, True, False] + Note : TX fault status is lached until a call to get_tx_fault or a reset. + """ + if self.is_cmis == True: + return self.qsfp_cmis.get_tx_fault() + else: + return self.qsfp_8436.get_tx_fault() + + def get_tx_disable(self): + """ + Retrieves the tx_disable status of this SFP + + Returns: + A Boolean, True if tx_disable is enabled, False if disabled + """ + if self.is_cmis == True: + return self.qsfp_cmis.get_tx_disable() + else: + return self.qsfp_8436.get_tx_disable() + + def get_tx_disable_channel(self): + """ + Retrieves the TX disabled channels in this SFP + + Returns: + A hex of 4 bits (bit 0 to bit 3 as channel 0 to channel 3) to represent + TX channels which have been disabled in this SFP. + As an example, a returned value of 0x5 indicates that channel 0 + and channel 2 have been disabled. + """ + if self.is_cmis == True: + return self.qsfp_cmis.get_tx_disable_channel() + else: + return self.qsfp_8436.get_tx_disable_channel() + + def get_lpmode(self): + """ + Retrieves the lpmode (low power mode) status of this SFP + + Returns: + A Boolean, True if lpmode is enabled, False if disabled + """ + if self.is_cmis == True: + return self.qsfp_cmis.get_lpmode() + else: + return self.qsfp_8436.get_lpmode() + + def get_power_override(self): + """ + Retrieves the power-override status of this SFP + + Returns: + A Boolean, True if power-override is enabled, False if disabled + """ + return None + + def get_temperature(self): + """ + Retrieves the temperature of this SFP + + Returns: + An integer number of current temperature in Celsius + """ + if self.is_cmis == True: + return self.qsfp_cmis.get_temperature() + else: + return self.qsfp_8436.get_temperature() + + def get_voltage(self): + """ + Retrieves the supply voltage of this SFP + + Returns: + An integer number of supply voltage in mV + """ + if self.is_cmis == True: + return self.qsfp_cmis.get_voltage() + else: + return self.qsfp_8436.get_voltage() + + + def get_tx_bias(self): + """ + Retrieves the TX bias current of this SFP + + Returns: + A list of four integer numbers, representing TX bias in mA + for channel 0 to channel 4. + Ex. ['110.09', '111.12', '108.21', '112.09'] + """ + if self.is_cmis == True: + return self.qsfp_cmis.get_tx_bias() + else: + return self.qsfp_8436.get_tx_bias() + + def get_rx_power(self): + """ + Retrieves the received optical power for this SFP + + Returns: + A list of four integer numbers, representing received optical + power in mW for channel 0 to channel 4. + Ex. ['1.77', '1.71', '1.68', '1.70'] + """ + if self.is_cmis == True: + return self.qsfp_cmis.get_rx_power() + else: + return self.qsfp_8436.get_rx_power() + + def get_tx_power(self): + """ + Retrieves the TX power of this SFP + + Returns: + A list of four integer numbers, representing TX power in mW + for channel 0 to channel 4. + Ex. ['1.86', '1.86', '1.86', '1.86'] + """ + if self.is_cmis == True: + return self.qsfp_cmis.get_tx_power() + else: + return self.qsfp_8436.get_tx_power() + + def reset(self): + """ + Reset SFP and return all user module settings to their default srate. + + Returns: + A boolean, True if successful, False if not + """ + if self.is_cmis == True: + return self.qsfp_cmis.reset() + else: + return self.qsfp_8436.reset() + + def tx_disable(self, tx_disable): + """ + Disable SFP TX for all channels + + Args: + tx_disable : A Boolean, True to enable tx_disable mode, False to disable + tx_disable mode. + + Returns: + A boolean, True if tx_disable is set successfully, False if not + """ + if self.is_cmis == True: + return self.qsfp_cmis.tx_disable() + else: + return self.qsfp_8436.tx_disable() + + + def tx_disable_channel(self, channel, disable): + """ + Sets the tx_disable for specified SFP channels + + Args: + channel : A hex of 4 bits (bit 0 to bit 3) which represent channel 0 to 3, + e.g. 0x5 for channel 0 and channel 2. + disable : A boolean, True to disable TX channels specified in channel, + False to enable + + Returns: + A boolean, True if successful, False if not + """ + if self.is_cmis == True: + return self.qsfp_cmis.tx_disable_channel() + else: + return self.qsfp_8436.tx_disable_channel() + + def set_lpmode(self, lpmode): + """ + Sets the lpmode (low power mode) of SFP + + Args: + lpmode: A Boolean, True to enable lpmode, False to disable it + Note : lpmode can be overridden by set_power_override + + Returns: + A boolean, True if lpmode is set successfully, False if not + """ + if self.is_cmis == True: + return self.qsfp_cmis.set_lpmode() + else: + return self.qsfp_8436.set_lpmode() + + def set_power_override(self, power_override, power_set): + """ + Sets SFP power level using power_override and power_set + + Args: + power_override : + A Boolean, True to override set_lpmode and use power_set + to control SFP power, False to disable SFP power control + through power_override/power_set and use set_lpmode + to control SFP power. + power_set : + Only valid when power_override is True. + A Boolean, True to set SFP to low power mode, False to set + SFP to high power mode. + + Returns: + A boolean, True if power-override and power_set are set successfully, + False if not + """ + return True + diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/qsfp_8436.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/qsfp_8436.py new file mode 100755 index 000000000000..299d766166ca --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/qsfp_8436.py @@ -0,0 +1,1079 @@ +#!/usr/bin/env python +# +# Name: qsfp.py, version: 1.0 +# +# Description: Module contains the definitions of SONiC platform APIs +# + +try: + import os + import logging + from ctypes import create_string_buffer + from sonic_platform_base.sfp_base import SfpBase + from sonic_platform_base.sonic_sfp.sff8436 import sff8436Dom + from sonic_platform_base.sonic_sfp.sff8436 import sff8436InterfaceId + from sonic_platform_base.sonic_sfp.sff8024 import ext_specification_compliance + #from sonic_platform_base.sonic_sfp.sfputilhelper import SfpUtilHelper + from .helper import APIHelper +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +INFO_OFFSET = 128 +THRE_OFFSET = 384 #128*3 +DOM_OFFSET = 0 + +XCVR_INTFACE_BULK_OFFSET = 0 +XCVR_INTFACE_BULK_WIDTH_QSFP = 20 +XCVR_HW_REV_WIDTH_QSFP = 2 +XCVR_CABLE_LENGTH_WIDTH_QSFP = 5 +XCVR_VENDOR_NAME_OFFSET = 20 +XCVR_VENDOR_NAME_WIDTH = 16 +XCVR_VENDOR_OUI_OFFSET = 37 +XCVR_VENDOR_OUI_WIDTH = 3 +XCVR_VENDOR_PN_OFFSET = 40 +XCVR_VENDOR_PN_WIDTH = 16 +XCVR_HW_REV_OFFSET = 56 +XCVR_HW_REV_WIDTH_OSFP = 2 +XCVR_SPECIFICATION_ETH_COMPLIANCE_OFFSET = 3 +XCVR_SPECIFICATION_ETH_COMPLIANCE_WIDTH = 1 +XCVR_EXT_SPECIFICATION_COMPLIANCE_OFFSET = 64 +XCVR_EXT_SPECIFICATION_COMPLIANCE_WIDTH = 1 +XCVR_VENDOR_SN_OFFSET = 68 +XCVR_VENDOR_SN_WIDTH = 16 +XCVR_VENDOR_DATE_OFFSET = 84 +XCVR_VENDOR_DATE_WIDTH = 8 +XCVR_DOM_CAPABILITY_OFFSET = 92 +XCVR_DOM_CAPABILITY_WIDTH = 1 + +# Offset for values in QSFP eeprom +QSFP_DOM_REV_OFFSET = 1 +QSFP_DOM_REV_WIDTH = 1 +QSFP_TEMPE_OFFSET = 22 +QSFP_TEMPE_WIDTH = 2 +QSFP_VOLT_OFFSET = 26 +QSFP_VOLT_WIDTH = 2 +QSFP_CHANNL_MON_OFFSET = 34 +QSFP_CHANNL_MON_WIDTH = 16 +QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH = 24 +QSFP_CONTROL_OFFSET = 86 +QSFP_CONTROL_WIDTH = 8 +QSFP_CHANNL_RX_LOS_STATUS_OFFSET = 3 +QSFP_CHANNL_RX_LOS_STATUS_WIDTH = 1 +QSFP_CHANNL_TX_FAULT_STATUS_OFFSET = 4 +QSFP_CHANNL_TX_FAULT_STATUS_WIDTH = 1 +QSFP_POWEROVERRIDE_OFFSET = 93 +QSFP_POWEROVERRIDE_WIDTH = 1 +QSFP_MODULE_THRESHOLD_OFFSET = 128 +QSFP_MODULE_THRESHOLD_WIDTH = 24 +QSFP_CHANNEL_THRESHOLD_OFFSET = 176 +QSFP_CHANNEL_THRESHOLD_WIDTH = 16 + +QSFP_REG_VALUE_ENABLE = "0x1" +QSFP_REG_VALUE_DISABLE = "0x0" +TRANSCEIVER_PATH = '/sys/switch/transceiver/' + +class QSfp_SFF8436(SfpBase): + + + def __init__(self, index,qsfp_conf): + self.__index = index + self.__conf = qsfp_conf + self.__api_helper = APIHelper() + + self.__attr_path_prefix = '/sys/switch/transceiver/eth{}/'.format(self.__index+1) + self.__port_end = 55 + + self.__presence_attr = None + self.__eeprom_path = None + if self.__index in range(0, self.__port_end + 1): + self.__eeprom_path = TRANSCEIVER_PATH+'eth{}/'.format(self.__index+1)+'eeprom' + self.__presence_attr = TRANSCEIVER_PATH+'eth{}/'.format(self.__index+1)+'present' + self.__reset_attr = TRANSCEIVER_PATH+'eth{}/'.format(self.__index+1)+'reset' + self.__lpmode_attr = TRANSCEIVER_PATH+'eth{}/'.format(self.__index+1)+'lpmode' + + SfpBase.__init__(self) + + def __get_attr_value(self, attr_path): + + retval = 'ERR' + if (not os.path.isfile(attr_path)): + return retval + + try: + with open(attr_path, 'r') as fd: + retval = fd.read() + except Exception as error: + logging.error("Unable to open ", attr_path, " file !") + + retval = retval.rstrip(' \t\n\r') + return retval + + def __set_attr_value(self, attr_path, value): + + try: + with open(attr_path, 'r+') as reg_file: + reg_file.write(value) + except IOError as e: + logging.error("Error: unable to open file: '%s'" % str(e)) + return False + + return True + + def __read_eeprom_specific_bytes(self, offset, num_bytes): + sysfsfile_eeprom = None + eeprom_raw = [] + + for i in range(0, num_bytes): + eeprom_raw.append("0x00") + + sysfs_eeprom_path = self.__eeprom_path + try: + sysfsfile_eeprom = open(sysfs_eeprom_path, mode="rb", buffering=0) + sysfsfile_eeprom.seek(offset) + raw = sysfsfile_eeprom.read(num_bytes) + raw_len = len(raw) + for n in range(0, raw_len): + eeprom_raw[n] = hex(raw[n])[2:].zfill(2) + except BaseException: + pass + finally: + if sysfsfile_eeprom: + sysfsfile_eeprom.close() + + return eeprom_raw + + def __write_eeprom_specific_bytes(self, offset, buffer): + sysfs_eeprom_path = self.__eeprom_path + + try: + with open(sysfs_eeprom_path, "r+b") as sysfsfile_eeprom: + sysfsfile_eeprom.seek(offset) + sysfsfile_eeprom.write(buffer[0]) + except IOError as e: + logging.error("Error: unable to open file: '%s'" % str(e)) + return False + + return True + + def __convert_string_to_num(self, value_str): + if "-inf" in value_str: + return 'N/A' + elif "Unknown" in value_str: + return 'N/A' + elif 'dBm' in value_str: + t_str = value_str.rstrip('dBm') + return float(t_str) + elif 'mA' in value_str: + t_str = value_str.rstrip('mA') + return float(t_str) + elif 'C' in value_str: + t_str = value_str.rstrip('C') + return float(t_str) + elif 'Volts' in value_str: + t_str = value_str.rstrip('Volts') + return float(t_str) + else: + return 'N/A' + +############################################## +# Device methods +############################################## + + def get_name(self): + """ + Retrieves the name of the device + + Returns: + string: The name of the device + """ + return self.__conf[self.__index]['name'] + + def get_presence(self): + """ + Retrieves the presence of the device + + Returns: + bool: True if device is present, False if not + """ + presence = False + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'present') + if (attr_rv != None): + attr_rv = int(attr_rv, 16) + if ((attr_rv & 0x1) == 1): + presence = True + return presence + + def get_all_presence(self): + """ + Retrieves the presence of the device + + Returns: + bool: True if device is present, False if not + """ + + presence_bit = 0 + attr_rv = self.__api_helper.read_one_line_file(TRANSCEIVER_PATH + 'present') + if (attr_rv != None): + presence_bit = int(attr_rv, 16) + return presence_bit + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + + Returns: + string: Model/part number of device + """ + transceiver_info_dict = self.get_transceiver_info() + return transceiver_info_dict.get("model", "N/A") + + def get_serial(self): + """ + Retrieves the serial number of the device + + Returns: + string: Serial number of device + """ + transceiver_info_dict = self.get_transceiver_info() + return transceiver_info_dict.get("serial", "N/A") + + def get_status(self): + """ + Retrieves the operational status of the device + + Returns: + A boolean value, True if device is operating properly, False if not + """ + return self.get_presence() and not self.get_reset_status() + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return self.__index + + def is_replaceable(self): + """ + Indicate whether Chassis is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True +############################################## +# SFP methods +############################################## + def is_copper(self): + ETHERNET_10_40G_COMPLIANCE = { + 1: "40G Active Cable (XLPPI)", + 2: "40GBASE-LR4", + 4: "40GBASE-SR4", + 8: "40GBASE-CR4", + 16: "10GBASE-SR", + 32: "10GBASE-LR", + 64: "10GBASE-LRM", + 128: "Extended", + } + eth_compliance_raw = self.__read_eeprom_specific_bytes(INFO_OFFSET + XCVR_SPECIFICATION_ETH_COMPLIANCE_OFFSET, XCVR_SPECIFICATION_ETH_COMPLIANCE_WIDTH) + try: + eth_compliance = ETHERNET_10_40G_COMPLIANCE[int(eth_compliance_raw[0],16)] + except Exception: + logging.error("cannot specify ethernet10_40G compliance") + return None + ext_spec_compliance_raw = self.__read_eeprom_specific_bytes(INFO_OFFSET + XCVR_EXT_SPECIFICATION_COMPLIANCE_OFFSET, XCVR_EXT_SPECIFICATION_COMPLIANCE_WIDTH) + ext_spec_compliance = ext_specification_compliance[(ext_spec_compliance_raw[0])] + + if eth_compliance is None or ext_spec_compliance is None: + return None + + return eth_compliance == "40GBASE-CR4" or \ + "CR" in ext_spec_compliance or \ + "ACC" in ext_spec_compliance or \ + "Copper" in ext_spec_compliance + + def get_transceiver_info(self): + """ + Retrieves transceiver info of this SFP + + Returns: + A dict which contains following keys/values : + ======================================================================== + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + type |1*255VCHAR |type of SFP + hardwarerev |1*255VCHAR |hardware version of SFP + serialnum |1*255VCHAR |serial number of the SFP + manufacturename |1*255VCHAR |SFP vendor name + modelname |1*255VCHAR |SFP model name + Connector |1*255VCHAR |connector information + encoding |1*255VCHAR |encoding information + ext_identifier |1*255VCHAR |extend identifier + ext_rateselect_compliance |1*255VCHAR |extended rateSelect compliance + cable_length |INT |cable length in m + mominal_bit_rate |INT |nominal bit rate by 100Mbs + specification_compliance |1*255VCHAR |specification compliance + vendor_date |1*255VCHAR |vendor date + vendor_oui |1*255VCHAR |vendor OUI + ======================================================================== + """ + transceiver_info_dict_keys = ['type', 'hardware_rev', + 'serialnum', 'manufacturename', + 'modelname', 'Connector', + 'encoding', 'ext_identifier', + 'ext_rateselect_compliance', 'cable_type', + 'cable_length', 'nominal_bit_rate', + 'specification_compliance', 'vendor_date', + 'vendor_oui'] + + qsfp_cable_length_tup = ('Length(km)', 'Length OM3(2m)', + 'Length OM2(m)', 'Length OM1(m)', 'Length Cable Assembly(m)') + + qsfp_compliance_code_tup = ('10/40G Ethernet Compliance Code', 'SONET Compliance codes', + 'SAS/SATA compliance codes', 'Gigabit Ethernet Compliant codes', + 'Fibre Channel link length/Transmitter Technology', 'Fibre Channel transmission media', + 'Fibre Channel Speed') + + sfpi_obj = sff8436InterfaceId() + if not self.get_presence() or not sfpi_obj: + return {} + + offset = INFO_OFFSET + + sfp_interface_bulk_raw = self.__read_eeprom_specific_bytes((offset + XCVR_INTFACE_BULK_OFFSET), XCVR_INTFACE_BULK_WIDTH_QSFP) + sfp_interface_bulk_data = sfpi_obj.parse_sfp_info_bulk(sfp_interface_bulk_raw, 0) + + sfp_vendor_name_raw = self.__read_eeprom_specific_bytes((offset + XCVR_VENDOR_NAME_OFFSET), XCVR_VENDOR_NAME_WIDTH) + sfp_vendor_name_data = sfpi_obj.parse_vendor_name(sfp_vendor_name_raw, 0) + + sfp_vendor_pn_raw = self.__read_eeprom_specific_bytes((offset + XCVR_VENDOR_PN_OFFSET), XCVR_VENDOR_PN_WIDTH) + sfp_vendor_pn_data = sfpi_obj.parse_vendor_pn(sfp_vendor_pn_raw, 0) + + sfp_vendor_rev_raw = self.__read_eeprom_specific_bytes((offset + XCVR_HW_REV_OFFSET), XCVR_HW_REV_WIDTH_QSFP) + sfp_vendor_rev_data = sfpi_obj.parse_vendor_rev(sfp_vendor_rev_raw, 0) + + sfp_vendor_sn_raw = self.__read_eeprom_specific_bytes((offset + XCVR_VENDOR_SN_OFFSET), XCVR_VENDOR_SN_WIDTH) + sfp_vendor_sn_data = sfpi_obj.parse_vendor_sn(sfp_vendor_sn_raw, 0) + + sfp_vendor_oui_raw = self.__read_eeprom_specific_bytes((offset + XCVR_VENDOR_OUI_OFFSET), XCVR_VENDOR_OUI_WIDTH) + if sfp_vendor_oui_raw is not None: + sfp_vendor_oui_data = sfpi_obj.parse_vendor_oui(sfp_vendor_oui_raw, 0) + + sfp_vendor_date_raw = self.__read_eeprom_specific_bytes((offset + XCVR_VENDOR_DATE_OFFSET), XCVR_VENDOR_DATE_WIDTH) + sfp_vendor_date_data = sfpi_obj.parse_vendor_date(sfp_vendor_date_raw, 0) + + transceiver_info_dict = dict.fromkeys(transceiver_info_dict_keys, 'N/A') + + if sfp_interface_bulk_data: + transceiver_info_dict['type'] = sfp_interface_bulk_data['data']['type']['value'] + transceiver_info_dict['connector'] = sfp_interface_bulk_data['data']['Connector']['value'] + transceiver_info_dict['encoding'] = sfp_interface_bulk_data['data']['EncodingCodes']['value'] + transceiver_info_dict['ext_identifier'] = sfp_interface_bulk_data['data']['Extended Identifier']['value'] + transceiver_info_dict['ext_rateselect_compliance'] = sfp_interface_bulk_data['data']['RateIdentifier']['value'] + transceiver_info_dict['type_abbrv_name'] = sfp_interface_bulk_data['data']['type_abbrv_name']['value'] + transceiver_info_dict['nominal_bit_rate'] = str(sfp_interface_bulk_data['data']['Nominal Bit Rate(100Mbs)']['value']) + + transceiver_info_dict['manufacturer'] = sfp_vendor_name_data['data']['Vendor Name']['value'] if sfp_vendor_name_data else 'N/A' + transceiver_info_dict['model'] = sfp_vendor_pn_data['data']['Vendor PN']['value'] if sfp_vendor_pn_data else 'N/A' + transceiver_info_dict['vendor_rev'] = sfp_vendor_rev_data['data']['Vendor Rev']['value'] if sfp_vendor_rev_data else 'N/A' + transceiver_info_dict['serial'] = sfp_vendor_sn_data['data']['Vendor SN']['value'] if sfp_vendor_sn_data else 'N/A' + transceiver_info_dict['vendor_oui'] = sfp_vendor_oui_data['data']['Vendor OUI']['value'] if sfp_vendor_oui_data else 'N/A' + transceiver_info_dict['vendor_date'] = sfp_vendor_date_data['data']['VendorDataCode(YYYY-MM-DD Lot)']['value'] if sfp_vendor_date_data else 'N/A' + + transceiver_info_dict['cable_type'] = "Unknown" + transceiver_info_dict['cable_length'] = "Unknown" + for key in qsfp_cable_length_tup: + if key in sfp_interface_bulk_data['data']: + transceiver_info_dict['cable_type'] = key + transceiver_info_dict['cable_length'] = str(sfp_interface_bulk_data['data'][key]['value']) + + compliance_code_dict = dict() + for key in qsfp_compliance_code_tup: + if key in sfp_interface_bulk_data['data']['Specification compliance']['value']: + compliance_code_dict[key] = sfp_interface_bulk_data['data']['Specification compliance']['value'][key]['value'] + sfp_ext_specification_compliance_raw = self.__read_eeprom_specific_bytes(offset + XCVR_EXT_SPECIFICATION_COMPLIANCE_OFFSET, XCVR_EXT_SPECIFICATION_COMPLIANCE_WIDTH) + if sfp_ext_specification_compliance_raw is not None: + sfp_ext_specification_compliance_data = sfpi_obj.parse_ext_specification_compliance(sfp_ext_specification_compliance_raw[0 : 1], 0) + if sfp_ext_specification_compliance_data['data']['Extended Specification compliance']['value'] != "Unspecified": + compliance_code_dict['Extended Specification compliance'] = sfp_ext_specification_compliance_data['data']['Extended Specification compliance']['value'] + transceiver_info_dict['specification_compliance'] = str(compliance_code_dict) + + transceiver_info_dict['nominal_bit_rate'] = str(sfp_interface_bulk_data['data']['Nominal Bit Rate(100Mbs)']['value']) + + if self.is_copper(): + transceiver_info_dict['specification_compliance'] = "{'media_interface':'DAC'}" + else: + transceiver_info_dict['specification_compliance'] = "{'media_interface':'AOC'}" + + return transceiver_info_dict + + def get_transceiver_bulk_status(self): + """ + Retrieves transceiver bulk status of this SFP + + Returns: + A dict which contains following keys/values : + ======================================================================== + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + rx_los |BOOLEAN |RX loss-of-signal status, True if has RX los, False if not. + tx_fault |BOOLEAN |TX fault status, True if has TX fault, False if not. + reset_status |BOOLEAN |reset status, True if SFP in reset, False if not. + lp_mode |BOOLEAN |low power mode status, True in lp mode, False if not. + tx_disable |BOOLEAN |TX disable status, True TX disabled, False if not. + tx_disabled_channel |HEX |disabled TX channels in hex, bits 0 to 3 represent channel 0 + | |to channel 3. + temperature |INT |module temperature in Celsius + voltage |INT |supply voltage in mV + txbias |INT |TX Bias Current in mA, n is the channel number, + | |for example, tx2bias stands for tx bias of channel 2. + rxpower |INT |received optical power in mW, n is the channel number, + | |for example, rx2power stands for rx power of channel 2. + txpower |INT |TX output power in mW, n is the channel number, + | |for example, tx2power stands for tx power of channel 2. + ======================================================================== + """ + transceiver_dom_info_dict_keys = ['temperature', 'voltage', + 'rx1power', 'rx2power', + 'rx3power', 'rx4power', + 'rx5power', 'rx6power', + 'rx7power', 'rx8power', + 'tx1bias', 'tx2bias', + 'tx3bias', 'tx4bias', + 'tx5bias', 'tx6bias', + 'tx7bias', 'tx8bias', + 'tx1power', 'tx2power', + 'tx3power', 'tx4power', + 'tx5power', 'tx6power', + 'tx7power', 'tx8power' + ] + + sfpd_obj = sff8436Dom() + sfpi_obj = sff8436InterfaceId() + + if not self.get_presence() or not sfpi_obj or not sfpd_obj: + return {} + + transceiver_dom_info_dict = dict.fromkeys(transceiver_dom_info_dict_keys, 'N/A') + offset = DOM_OFFSET + offset_xcvr = INFO_OFFSET + + # QSFP capability byte parse, through this byte can know whether it support tx_power or not. + # TODO: in the future when decided to migrate to support SFF-8636 instead of SFF-8436, + # need to add more code for determining the capability and version compliance + # in SFF-8636 dom capability definitions evolving with the versions. + qsfp_dom_capability_raw = self.__read_eeprom_specific_bytes((offset_xcvr + XCVR_DOM_CAPABILITY_OFFSET), XCVR_DOM_CAPABILITY_WIDTH) + if qsfp_dom_capability_raw is not None: + qspf_dom_capability_data = sfpi_obj.parse_dom_capability(qsfp_dom_capability_raw, 0) + else: + return None + + dom_temperature_raw = self.__read_eeprom_specific_bytes((offset + QSFP_TEMPE_OFFSET), QSFP_TEMPE_WIDTH) + if dom_temperature_raw is not None: + dom_temperature_data = sfpd_obj.parse_temperature(dom_temperature_raw, 0) + transceiver_dom_info_dict['temperature'] = dom_temperature_data['data']['Temperature']['value'] + + dom_voltage_raw = self.__read_eeprom_specific_bytes((offset + QSFP_VOLT_OFFSET), QSFP_VOLT_WIDTH) + if dom_voltage_raw is not None: + dom_voltage_data = sfpd_obj.parse_voltage(dom_voltage_raw, 0) + transceiver_dom_info_dict['voltage'] = dom_voltage_data['data']['Vcc']['value'] + + qsfp_dom_rev_raw = self.__read_eeprom_specific_bytes((offset + QSFP_DOM_REV_OFFSET), QSFP_DOM_REV_WIDTH) + if qsfp_dom_rev_raw is not None: + qsfp_dom_rev_data = sfpd_obj.parse_sfp_dom_rev(qsfp_dom_rev_raw, 0) + qsfp_dom_rev = qsfp_dom_rev_data['data']['dom_rev']['value'] + + # The tx_power monitoring is only available on QSFP which compliant with SFF-8636 + # and claimed that it support tx_power with one indicator bit. + dom_channel_monitor_data = {} + dom_channel_monitor_raw = None + qsfp_tx_power_support = qspf_dom_capability_data['data']['Tx_power_support']['value'] + if (qsfp_dom_rev[0:8] != 'SFF-8636' or (qsfp_dom_rev[0:8] == 'SFF-8636' and qsfp_tx_power_support != 'on')): + dom_channel_monitor_raw = self.__read_eeprom_specific_bytes((offset + QSFP_CHANNL_MON_OFFSET), QSFP_CHANNL_MON_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params(dom_channel_monitor_raw, 0) + + else: + dom_channel_monitor_raw = self.__read_eeprom_specific_bytes((offset + QSFP_CHANNL_MON_OFFSET), QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power(dom_channel_monitor_raw, 0) + transceiver_dom_info_dict['tx1power'] = dom_channel_monitor_data['data']['TX1Power']['value'] + transceiver_dom_info_dict['tx2power'] = dom_channel_monitor_data['data']['TX2Power']['value'] + transceiver_dom_info_dict['tx3power'] = dom_channel_monitor_data['data']['TX3Power']['value'] + transceiver_dom_info_dict['tx4power'] = dom_channel_monitor_data['data']['TX4Power']['value'] + + if dom_channel_monitor_raw: + transceiver_dom_info_dict['rx1power'] = dom_channel_monitor_data['data']['RX1Power']['value'] + transceiver_dom_info_dict['rx2power'] = dom_channel_monitor_data['data']['RX2Power']['value'] + transceiver_dom_info_dict['rx3power'] = dom_channel_monitor_data['data']['RX3Power']['value'] + transceiver_dom_info_dict['rx4power'] = dom_channel_monitor_data['data']['RX4Power']['value'] + transceiver_dom_info_dict['tx1bias'] = dom_channel_monitor_data['data']['TX1Bias']['value'] + transceiver_dom_info_dict['tx2bias'] = dom_channel_monitor_data['data']['TX2Bias']['value'] + transceiver_dom_info_dict['tx3bias'] = dom_channel_monitor_data['data']['TX3Bias']['value'] + transceiver_dom_info_dict['tx4bias'] = dom_channel_monitor_data['data']['TX4Bias']['value'] + + for key in transceiver_dom_info_dict: + transceiver_dom_info_dict[key] = self.__convert_string_to_num(transceiver_dom_info_dict[key]) + + transceiver_dom_info_dict['rx_los'] = self.get_rx_los() + transceiver_dom_info_dict['tx_fault'] = self.get_tx_fault() + transceiver_dom_info_dict['reset_status'] = self.get_reset_status() + transceiver_dom_info_dict['tx_disable'] = self.get_tx_disable() + transceiver_dom_info_dict['tx_disable_channel'] = self.get_tx_disable_channel() + transceiver_dom_info_dict['lp_mode'] = self.get_lpmode() + + + return transceiver_dom_info_dict + + def get_transceiver_threshold_info(self): + """ + Retrieves transceiver threshold info of this SFP + + Returns: + A dict which contains following keys/values : + ======================================================================== + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + temphighalarm |FLOAT |High Alarm Threshold value of temperature in Celsius. + templowalarm |FLOAT |Low Alarm Threshold value of temperature in Celsius. + temphighwarning |FLOAT |High Warning Threshold value of temperature in Celsius. + templowwarning |FLOAT |Low Warning Threshold value of temperature in Celsius. + vcchighalarm |FLOAT |High Alarm Threshold value of supply voltage in mV. + vcclowalarm |FLOAT |Low Alarm Threshold value of supply voltage in mV. + vcchighwarning |FLOAT |High Warning Threshold value of supply voltage in mV. + vcclowwarning |FLOAT |Low Warning Threshold value of supply voltage in mV. + rxpowerhighalarm |FLOAT |High Alarm Threshold value of received power in dBm. + rxpowerlowalarm |FLOAT |Low Alarm Threshold value of received power in dBm. + rxpowerhighwarning |FLOAT |High Warning Threshold value of received power in dBm. + rxpowerlowwarning |FLOAT |Low Warning Threshold value of received power in dBm. + txpowerhighalarm |FLOAT |High Alarm Threshold value of transmit power in dBm. + txpowerlowalarm |FLOAT |Low Alarm Threshold value of transmit power in dBm. + txpowerhighwarning |FLOAT |High Warning Threshold value of transmit power in dBm. + txpowerlowwarning |FLOAT |Low Warning Threshold value of transmit power in dBm. + txbiashighalarm |FLOAT |High Alarm Threshold value of tx Bias Current in mA. + txbiaslowalarm |FLOAT |Low Alarm Threshold value of tx Bias Current in mA. + txbiashighwarning |FLOAT |High Warning Threshold value of tx Bias Current in mA. + txbiaslowwarning |FLOAT |Low Warning Threshold value of tx Bias Current in mA. + ======================================================================== + """ + transceiver_dom_threshold_info_dict_keys = ['temphighalarm', 'temphighwarning', 'templowalarm', 'templowwarning', + 'vcchighalarm', 'vcchighwarning', 'vcclowalarm', 'vcclowwarning', + 'rxpowerhighalarm', 'rxpowerhighwarning', 'rxpowerlowalarm', 'rxpowerlowwarning', + 'txpowerhighalarm', 'txpowerhighwarning', 'txpowerlowalarm', 'txpowerlowwarning', + 'txbiashighalarm', 'txbiashighwarning', 'txbiaslowalarm', 'txbiaslowwarning'] + + sfpd_obj = sff8436Dom() + if not self.get_presence() or not sfpd_obj: + return {} + + transceiver_dom_threshold_dict = dict.fromkeys(transceiver_dom_threshold_info_dict_keys, 'N/A') + offset = THRE_OFFSET + + dom_module_threshold_raw = self.__read_eeprom_specific_bytes((offset + QSFP_MODULE_THRESHOLD_OFFSET), QSFP_MODULE_THRESHOLD_WIDTH) + if dom_module_threshold_raw: + module_threshold_values = sfpd_obj.parse_module_threshold_values(dom_module_threshold_raw, 0) + module_threshold_data = module_threshold_values.get('data') + if module_threshold_data: + transceiver_dom_threshold_dict['temphighalarm'] = module_threshold_data['TempHighAlarm']['value'] + transceiver_dom_threshold_dict['templowalarm'] = module_threshold_data['TempLowAlarm']['value'] + transceiver_dom_threshold_dict['temphighwarning'] = module_threshold_data['TempHighWarning']['value'] + transceiver_dom_threshold_dict['templowwarning'] = module_threshold_data['TempLowWarning']['value'] + transceiver_dom_threshold_dict['vcchighalarm'] = module_threshold_data['VccHighAlarm']['value'] + transceiver_dom_threshold_dict['vcclowalarm'] = module_threshold_data['VccLowAlarm']['value'] + transceiver_dom_threshold_dict['vcchighwarning'] = module_threshold_data['VccHighWarning']['value'] + transceiver_dom_threshold_dict['vcclowwarning'] = module_threshold_data['VccLowWarning']['value'] + + dom_channel_thres_raw = self.__read_eeprom_specific_bytes((offset + QSFP_CHANNEL_THRESHOLD_OFFSET), QSFP_CHANNEL_THRESHOLD_WIDTH) + channel_threshold_values = sfpd_obj.parse_channel_threshold_values(dom_channel_thres_raw, 0) + channel_threshold_data = channel_threshold_values.get('data') + if channel_threshold_data: + transceiver_dom_threshold_dict['rxpowerhighalarm'] = channel_threshold_data['RxPowerHighAlarm']['value'] + transceiver_dom_threshold_dict['rxpowerlowalarm'] = channel_threshold_data['RxPowerLowAlarm']['value'] + transceiver_dom_threshold_dict['rxpowerhighwarning'] = channel_threshold_data['RxPowerHighWarning']['value'] + transceiver_dom_threshold_dict['rxpowerlowwarning'] = channel_threshold_data['RxPowerLowWarning']['value'] + transceiver_dom_threshold_dict['txpowerhighalarm'] = "0.0dBm" + transceiver_dom_threshold_dict['txpowerlowalarm'] = "0.0dBm" + transceiver_dom_threshold_dict['txpowerhighwarning'] = "0.0dBm" + transceiver_dom_threshold_dict['txpowerlowwarning'] = "0.0dBm" + transceiver_dom_threshold_dict['txbiashighalarm'] = channel_threshold_data['TxBiasHighAlarm']['value'] + transceiver_dom_threshold_dict['txbiaslowalarm'] = channel_threshold_data['TxBiasLowAlarm']['value'] + transceiver_dom_threshold_dict['txbiashighwarning'] = channel_threshold_data['TxBiasHighWarning']['value'] + transceiver_dom_threshold_dict['txbiaslowwarning'] = channel_threshold_data['TxBiasLowWarning']['value'] + + for key in transceiver_dom_threshold_dict: + transceiver_dom_threshold_dict[key] = self.__convert_string_to_num(transceiver_dom_threshold_dict[key]) + + return transceiver_dom_threshold_dict + + def get_reset_status(self): + """ + Retrieves the reset status of SFP + + Returns: + A Boolean, True if reset enabled, False if disabled + """ + reset_status = False + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'reset') + if (attr_rv != None): + attr_rv = int(attr_rv, 16) + if ((attr_rv & 0x1) == 1): + reset_status = True + return reset_status + + def get_rx_los(self): + """ + Retrieves the RX LOS (lost-of-signal) status of SFP + + Returns: + A Boolean, True if SFP has RX LOS, False if not. + Note : RX LOS status is latched until a call to get_rx_los or a reset. + """ + rx_los_list = [] + + dom_channel_monitor_raw = self.__read_eeprom_specific_bytes(QSFP_CHANNL_RX_LOS_STATUS_OFFSET, QSFP_CHANNL_RX_LOS_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + rx_los_data = int(dom_channel_monitor_raw[0], 16) + rx_los_list.append(rx_los_data & 0x01 != 0) + rx_los_list.append(rx_los_data & 0x02 != 0) + rx_los_list.append(rx_los_data & 0x04 != 0) + rx_los_list.append(rx_los_data & 0x08 != 0) + + return rx_los_list + + def get_tx_fault(self): + """ + Retrieves the TX fault status of SFP + + Returns: + A Boolean, True if SFP has TX fault, False if not + Note : TX fault status is lached until a call to get_tx_fault or a reset. + """ + tx_fault_list = [] + + dom_channel_monitor_raw = self.__read_eeprom_specific_bytes(QSFP_CHANNL_TX_FAULT_STATUS_OFFSET, QSFP_CHANNL_TX_FAULT_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + tx_fault_data = int(dom_channel_monitor_raw[0], 16) + tx_fault_list.append(tx_fault_data & 0x01 != 0) + tx_fault_list.append(tx_fault_data & 0x02 != 0) + tx_fault_list.append(tx_fault_data & 0x04 != 0) + tx_fault_list.append(tx_fault_data & 0x08 != 0) + + return tx_fault_list + + def get_tx_disable(self): + """ + Retrieves the tx_disable status of this SFP + + Returns: + A Boolean, True if tx_disable is enabled, False if disabled + """ + tx_disable = False + tx_disable_list = [] + + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return tx_disable + + dom_control_raw = self.__read_eeprom_specific_bytes(QSFP_CONTROL_OFFSET, QSFP_CONTROL_WIDTH) + if dom_control_raw is not None: + dom_control_data = sfpd_obj.parse_control_bytes(dom_control_raw, 0) + tx_disable_list.append('On' == dom_control_data['data']['TX1Disable']['value']) + tx_disable_list.append('On' == dom_control_data['data']['TX2Disable']['value']) + tx_disable_list.append('On' == dom_control_data['data']['TX3Disable']['value']) + tx_disable_list.append('On' == dom_control_data['data']['TX4Disable']['value']) + tx_disable = tx_disable_list[0] or tx_disable_list[1] or tx_disable_list[2] or tx_disable_list[3] + + return tx_disable + + def get_tx_disable_channel(self): + """ + Retrieves the TX disabled channels in this SFP + + Returns: + A hex of 4 bits (bit 0 to bit 3 as channel 0 to channel 3) to represent + TX channels which have been disabled in this SFP. + As an example, a returned value of 0x5 indicates that channel 0 + and channel 2 have been disabled. + """ + tx_disable_channel = 0 + tx_disable_list = [] + + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return tx_disable_channel + + dom_control_raw = self.__read_eeprom_specific_bytes(QSFP_CONTROL_OFFSET, QSFP_CONTROL_WIDTH) + if dom_control_raw is not None: + dom_control_data = sfpd_obj.parse_control_bytes(dom_control_raw, 0) + tx_disable_list.append('On' == dom_control_data['data']['TX1Disable']['value']) + tx_disable_list.append('On' == dom_control_data['data']['TX2Disable']['value']) + tx_disable_list.append('On' == dom_control_data['data']['TX3Disable']['value']) + tx_disable_list.append('On' == dom_control_data['data']['TX4Disable']['value']) + + for i in range(len(tx_disable_list)): + if tx_disable_list[i]: + tx_disable_channel |= 1 << i + + return tx_disable_channel + + def get_lpmode(self): + """ + Retrieves the lpmode (low power mode) status of this SFP + + Returns: + A Boolean, True if lpmode is enabled, False if disabled + """ + lpmode = False + attr_path = self.__lpmode_attr + + attr_rv = self.__get_attr_value(attr_path) + if (attr_rv != 'ERR'): + if (int(attr_rv,16) == 1): + lpmode = True + + return lpmode + + def get_power_override(self): + """ + Retrieves the power-override status of this SFP + + Returns: + A Boolean, True if power-override is enabled, False if disabled + """ + power_override = False + + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return power_override + + dom_control_raw = self.__read_eeprom_specific_bytes(QSFP_CONTROL_OFFSET, QSFP_CONTROL_WIDTH) + if dom_control_raw is not None: + dom_control_data = sfpd_obj.parse_control_bytes(dom_control_raw, 0) + power_override = ('On' == dom_control_data['data']['PowerOverride']['value']) + + return power_override + + def get_temperature(self): + """ + Retrieves the temperature of this SFP + + Returns: + An integer number of current temperature in Celsius + """ + temp = "N/A" + sfpd_obj = sff8436Dom() + offset = DOM_OFFSET + + if not self.get_presence() or not sfpd_obj: + return temp + + dom_temperature_raw = self.__read_eeprom_specific_bytes((offset + QSFP_TEMPE_OFFSET), QSFP_TEMPE_WIDTH) + if dom_temperature_raw is not None: + dom_temperature_data = sfpd_obj.parse_temperature(dom_temperature_raw, 0) + temp = self.__convert_string_to_num(dom_temperature_data['data']['Temperature']['value']) + + return temp + + def get_voltage(self): + """ + Retrieves the supply voltage of this SFP + + Returns: + An integer number of supply voltage in mV + """ + voltage = "N/A" + sfpd_obj = sff8436Dom() + offset = DOM_OFFSET + + if not self.get_presence() or not sfpd_obj: + return voltage + + dom_voltage_raw = self.__read_eeprom_specific_bytes((offset + QSFP_VOLT_OFFSET), QSFP_VOLT_WIDTH) + if dom_voltage_raw is not None: + dom_voltage_data = sfpd_obj.parse_voltage(dom_voltage_raw, 0) + voltage = self.__convert_string_to_num(dom_voltage_data['data']['Vcc']['value']) + + return voltage + + def get_tx_bias(self): + """ + Retrieves the TX bias current of this SFP + + Returns: + A list of four integer numbers, representing TX bias in mA + for channel 0 to channel 4. + Ex. ['110.09', '111.12', '108.21', '112.09'] + """ + tx_bias_list = [] + sfpd_obj = sff8436Dom() + sfpi_obj = sff8436InterfaceId() + offset = DOM_OFFSET + offset_xcvr = INFO_OFFSET + + if not self.get_presence() or not sfpd_obj: + return [] + + qsfp_dom_capability_raw = self.__read_eeprom_specific_bytes((offset_xcvr + XCVR_DOM_CAPABILITY_OFFSET), XCVR_DOM_CAPABILITY_WIDTH) + if qsfp_dom_capability_raw is not None: + qspf_dom_capability_data = sfpi_obj.parse_dom_capability(qsfp_dom_capability_raw, 0) + else: + return None + + qsfp_dom_rev_raw = self.__read_eeprom_specific_bytes((offset + QSFP_DOM_REV_OFFSET), QSFP_DOM_REV_WIDTH) + if qsfp_dom_rev_raw is not None: + qsfp_dom_rev_data = sfpd_obj.parse_sfp_dom_rev(qsfp_dom_rev_raw, 0) + qsfp_dom_rev = qsfp_dom_rev_data['data']['dom_rev']['value'] + + dom_channel_monitor_data = {} + dom_channel_monitor_raw = None + qsfp_tx_power_support = qspf_dom_capability_data['data']['Tx_power_support']['value'] + if (qsfp_dom_rev[0:8] != 'SFF-8636' or (qsfp_dom_rev[0:8] == 'SFF-8636' and qsfp_tx_power_support != 'on')): + dom_channel_monitor_raw = self.__read_eeprom_specific_bytes((offset + QSFP_CHANNL_MON_OFFSET), QSFP_CHANNL_MON_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params(dom_channel_monitor_raw, 0) + + else: + dom_channel_monitor_raw = self.__read_eeprom_specific_bytes((offset + QSFP_CHANNL_MON_OFFSET), QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power(dom_channel_monitor_raw, 0) + + + if dom_channel_monitor_raw is not None: + tx_bias_list.append(self.__convert_string_to_num(dom_channel_monitor_data['data']['TX1Bias']['value'])) + tx_bias_list.append(self.__convert_string_to_num(dom_channel_monitor_data['data']['TX2Bias']['value'])) + tx_bias_list.append(self.__convert_string_to_num(dom_channel_monitor_data['data']['TX3Bias']['value'])) + tx_bias_list.append(self.__convert_string_to_num(dom_channel_monitor_data['data']['TX4Bias']['value'])) + + return tx_bias_list + + def get_rx_power(self): + """ + Retrieves the received optical power for this SFP + + Returns: + A list of four integer numbers, representing received optical + power in mW for channel 0 to channel 4. + Ex. ['1.77', '1.71', '1.68', '1.70'] + """ + rx_power_list = [] + sfpd_obj = sff8436Dom() + sfpi_obj = sff8436InterfaceId() + offset = DOM_OFFSET + offset_xcvr = INFO_OFFSET + + if not self.get_presence() or not sfpd_obj: + return [] + + qsfp_dom_capability_raw = self.__read_eeprom_specific_bytes((offset_xcvr + XCVR_DOM_CAPABILITY_OFFSET), XCVR_DOM_CAPABILITY_WIDTH) + if qsfp_dom_capability_raw is not None: + qspf_dom_capability_data = sfpi_obj.parse_dom_capability(qsfp_dom_capability_raw, 0) + else: + return None + + qsfp_dom_rev_raw = self.__read_eeprom_specific_bytes((offset + QSFP_DOM_REV_OFFSET), QSFP_DOM_REV_WIDTH) + if qsfp_dom_rev_raw is not None: + qsfp_dom_rev_data = sfpd_obj.parse_sfp_dom_rev(qsfp_dom_rev_raw, 0) + qsfp_dom_rev = qsfp_dom_rev_data['data']['dom_rev']['value'] + + dom_channel_monitor_data = {} + dom_channel_monitor_raw = None + qsfp_tx_power_support = qspf_dom_capability_data['data']['Tx_power_support']['value'] + if (qsfp_dom_rev[0:8] != 'SFF-8636' or (qsfp_dom_rev[0:8] == 'SFF-8636' and qsfp_tx_power_support != 'on')): + dom_channel_monitor_raw = self.__read_eeprom_specific_bytes((offset + QSFP_CHANNL_MON_OFFSET), QSFP_CHANNL_MON_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params(dom_channel_monitor_raw, 0) + + else: + dom_channel_monitor_raw = self.__read_eeprom_specific_bytes((offset + QSFP_CHANNL_MON_OFFSET), QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power(dom_channel_monitor_raw, 0) + + + if dom_channel_monitor_raw is not None: + rx_power_list.append(self.__convert_string_to_num(dom_channel_monitor_data['data']['RX1Power']['value'])) + rx_power_list.append(self.__convert_string_to_num(dom_channel_monitor_data['data']['RX2Power']['value'])) + rx_power_list.append(self.__convert_string_to_num(dom_channel_monitor_data['data']['RX3Power']['value'])) + rx_power_list.append(self.__convert_string_to_num(dom_channel_monitor_data['data']['RX4Power']['value'])) + + return rx_power_list + + def get_tx_power(self): + """ + Retrieves the TX power of this SFP + + Returns: + A list of four integer numbers, representing TX power in mW + for channel 0 to channel 4. + Ex. ['1.86', '1.86', '1.86', '1.86'] + """ + tx_power_list = [] + sfpd_obj = sff8436Dom() + sfpi_obj = sff8436InterfaceId() + offset = DOM_OFFSET + offset_xcvr = INFO_OFFSET + + if not self.get_presence() or not sfpd_obj: + return [] + + qsfp_dom_capability_raw = self.__read_eeprom_specific_bytes((offset_xcvr + XCVR_DOM_CAPABILITY_OFFSET), XCVR_DOM_CAPABILITY_WIDTH) + if qsfp_dom_capability_raw is not None: + qspf_dom_capability_data = sfpi_obj.parse_dom_capability(qsfp_dom_capability_raw, 0) + else: + return None + + qsfp_dom_rev_raw = self.__read_eeprom_specific_bytes((offset + QSFP_DOM_REV_OFFSET), QSFP_DOM_REV_WIDTH) + if qsfp_dom_rev_raw is not None: + qsfp_dom_rev_data = sfpd_obj.parse_sfp_dom_rev(qsfp_dom_rev_raw, 0) + qsfp_dom_rev = qsfp_dom_rev_data['data']['dom_rev']['value'] + + dom_channel_monitor_data = {} + dom_channel_monitor_raw = None + qsfp_tx_power_support = qspf_dom_capability_data['data']['Tx_power_support']['value'] + if (qsfp_dom_rev[0:8] != 'SFF-8636' or (qsfp_dom_rev[0:8] == 'SFF-8636' and qsfp_tx_power_support != 'on')): + pass + + else: + dom_channel_monitor_raw = self.__read_eeprom_specific_bytes((offset + QSFP_CHANNL_MON_OFFSET), QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power(dom_channel_monitor_raw, 0) + tx_power_list.append(self._convert_string_to_num(dom_channel_monitor_data['data']['TX1Power']['value'])) + tx_power_list.append(self._convert_string_to_num(dom_channel_monitor_data['data']['TX2Power']['value'])) + tx_power_list.append(self._convert_string_to_num(dom_channel_monitor_data['data']['TX3Power']['value'])) + tx_power_list.append(self._convert_string_to_num(dom_channel_monitor_data['data']['TX4Power']['value'])) + + return tx_power_list + + def reset(self): + """ + Reset SFP and return all user module settings to their default srate. + + Returns: + A boolean, True if successful, False if not + """ + + try: + reg_file = open(self.__reset_attr, "r+") + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False + + reg_value = 1 + reg_file.write(hex(reg_value)) + reg_file.close() + + try: + reg_file = open(self.__reset_attr, "r+") + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False + + reg_value = 0 + reg_file.write(hex(reg_value)) + reg_file.close() + + return True + + def tx_disable(self, tx_disable): + """ + Disable SFP TX for all channels + + Args: + tx_disable : A Boolean, True to enable tx_disable mode, False to disable + tx_disable mode. + + Returns: + A boolean, True if tx_disable is set successfully, False if not + """ + tx_disable_ctl = 0xf if tx_disable else 0x0 + buffer = create_string_buffer(1) + buffer[0] = chr(tx_disable_ctl) + + return self.__write_eeprom_specific_bytes(QSFP_CONTROL_OFFSET, buffer) + + def tx_disable_channel(self, channel, disable): + """ + Sets the tx_disable for specified SFP channels + + Args: + channel : A hex of 4 bits (bit 0 to bit 3) which represent channel 0 to 3, + e.g. 0x5 for channel 0 and channel 2. + disable : A boolean, True to disable TX channels specified in channel, + False to enable + + Returns: + A boolean, True if successful, False if not + """ + channel_state = self.get_tx_disable_channel() + if disable: + tx_disable_ctl = channel_state | channel + else: + tx_disable_ctl = channel_state & (~channel & 0xf) + + buffer = create_string_buffer(1) + buffer[0] = chr(tx_disable_ctl) + + return self.__write_eeprom_specific_bytes(QSFP_CONTROL_OFFSET, buffer) + + def set_lpmode(self, lpmode): + """ + Sets the lpmode (low power mode) of SFP + + Args: + lpmode: A Boolean, True to enable lpmode, False to disable it + Note : lpmode can be overridden by set_power_override + + Returns: + A boolean, True if lpmode is set successfully, False if not + """ + try: + reg_file = open(self.__lpmode_attr, "r+") + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False + + #reg_value = int(reg_file.readline().rstrip()) + + if lpmode is True: + reg_value = 1 + else: + reg_value = 0 + + reg_file.write(hex(reg_value)) + reg_file.close() + + return True + + def set_power_override(self, power_override, power_set): + """ + Sets SFP power level using power_override and power_set + + Args: + power_override : + A Boolean, True to override set_lpmode and use power_set + to control SFP power, False to disable SFP power control + through power_override/power_set and use set_lpmode + to control SFP power. + power_set : + Only valid when power_override is True. + A Boolean, True to set SFP to low power mode, False to set + SFP to high power mode. + + Returns: + A boolean, True if power-override and power_set are set successfully, + False if not + """ + power_override_bit = 0 + if power_override: + power_override_bit |= 1 << 0 + + power_set_bit = 0 + if power_set: + power_set_bit |= 1 << 1 + + buffer = create_string_buffer(1) + buffer[0] = chr(power_override_bit | power_set_bit) + + return self.__write_eeprom_specific_bytes(QSFP_POWEROVERRIDE_OFFSET, buffer) diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/qsfp_cmis.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/qsfp_cmis.py new file mode 100755 index 000000000000..9644b8a51d71 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/qsfp_cmis.py @@ -0,0 +1,1782 @@ +#!/usr/bin/env python +# +# Name: qsfp.py, version: 1.0 +# +# Description: Module contains the definitions of SONiC platform APIs +# + +import os +import time +import logging +import struct +import datetime +from ctypes import create_string_buffer + +try: + from sonic_platform_base.sfp_base import SfpBase + from sonic_platform_base.sonic_sfp.qsfp_dd import qsfp_dd_InterfaceId + from sonic_platform_base.sonic_sfp.qsfp_dd import qsfp_dd_Dom + from .helper import APIHelper +except ImportError as e: + raise ImportError (str(e) + "- required module not found") + +# definitions of the offset and width for values in XCVR info eeprom +XCVR_INTFACE_BULK_OFFSET = 0 +XCVR_INTFACE_BULK_WIDTH_QSFP = 20 +XCVR_INTFACE_BULK_WIDTH_SFP = 21 +XCVR_TYPE_OFFSET = 0 +XCVR_TYPE_WIDTH = 1 +XCVR_EXT_TYPE_OFFSET = 1 +XCVR_EXT_TYPE_WIDTH = 1 +XCVR_CONNECTOR_OFFSET = 2 +XCVR_CONNECTOR_WIDTH = 1 +XCVR_COMPLIANCE_CODE_OFFSET = 3 +XCVR_COMPLIANCE_CODE_WIDTH = 8 +XCVR_ENCODING_OFFSET = 11 +XCVR_ENCODING_WIDTH = 1 +XCVR_NBR_OFFSET = 12 +XCVR_NBR_WIDTH = 1 +XCVR_EXT_RATE_SEL_OFFSET = 13 +XCVR_EXT_RATE_SEL_WIDTH = 1 +XCVR_CABLE_LENGTH_OFFSET = 14 +XCVR_CABLE_LENGTH_WIDTH_QSFP = 5 +XCVR_CABLE_LENGTH_WIDTH_SFP = 6 +XCVR_VENDOR_NAME_OFFSET = 20 +XCVR_VENDOR_NAME_WIDTH = 16 +XCVR_VENDOR_OUI_OFFSET = 37 +XCVR_VENDOR_OUI_WIDTH = 3 +XCVR_VENDOR_PN_OFFSET = 40 +XCVR_VENDOR_PN_WIDTH = 16 +XCVR_HW_REV_OFFSET = 56 +XCVR_HW_REV_WIDTH_OSFP = 2 +XCVR_HW_REV_WIDTH_QSFP = 2 +XCVR_HW_REV_WIDTH_SFP = 4 +XCVR_EXT_SPECIFICATION_COMPLIANCE_OFFSET = 64 +XCVR_EXT_SPECIFICATION_COMPLIANCE_WIDTH = 1 +XCVR_VENDOR_SN_OFFSET = 68 +XCVR_VENDOR_SN_WIDTH = 16 +XCVR_VENDOR_DATE_OFFSET = 84 +XCVR_VENDOR_DATE_WIDTH = 8 +XCVR_DOM_CAPABILITY_OFFSET = 92 +XCVR_DOM_CAPABILITY_WIDTH = 2 + +# definitions of the offset and width for values in XCVR_QSFP_DD info eeprom +XCVR_EXT_TYPE_OFFSET_QSFP_DD = 72 +XCVR_EXT_TYPE_WIDTH_QSFP_DD = 2 +XCVR_CONNECTOR_OFFSET_QSFP_DD = 75 +XCVR_CONNECTOR_WIDTH_QSFP_DD = 1 +XCVR_CABLE_LENGTH_OFFSET_QSFP_DD = 74 +XCVR_CABLE_LENGTH_WIDTH_QSFP_DD = 1 +XCVR_HW_REV_OFFSET_QSFP_DD = 36 +XCVR_HW_REV_WIDTH_QSFP_DD = 2 +XCVR_VENDOR_DATE_OFFSET_QSFP_DD = 54 +XCVR_VENDOR_DATE_WIDTH_QSFP_DD = 8 +XCVR_DOM_CAPABILITY_OFFSET_QSFP_DD = 2 +XCVR_DOM_CAPABILITY_WIDTH_QSFP_DD = 1 +XCVR_MEDIA_TYPE_OFFSET_QSFP_DD = 85 +XCVR_MEDIA_TYPE_WIDTH_QSFP_DD = 1 +XCVR_FIRST_APPLICATION_LIST_OFFSET_QSFP_DD = 86 +XCVR_FIRST_APPLICATION_LIST_WIDTH_QSFP_DD = 32 +XCVR_SECOND_APPLICATION_LIST_OFFSET_QSFP_DD = 351 +XCVR_SECOND_APPLICATION_LIST_WIDTH_QSFP_DD = 28 + +# to improve performance we retrieve all eeprom data via a single ethtool command +# in function get_transceiver_info and get_transceiver_bulk_status +# XCVR_INTERFACE_DATA_SIZE stands for the max size to be read +# this variable is only used by get_transceiver_info. +# please be noted that each time some new value added to the function +# we should make sure that it falls into the area +# [XCVR_INTERFACE_DATA_START, XCVR_INTERFACE_DATA_SIZE] or +# adjust XCVR_INTERFACE_MAX_SIZE to contain the new data +# It's same for [QSFP_DOM_BULK_DATA_START, QSFP_DOM_BULK_DATA_SIZE] and +# [SFP_DOM_BULK_DATA_START, SFP_DOM_BULK_DATA_SIZE] which are used by +# get_transceiver_bulk_status +XCVR_INTERFACE_DATA_START = 0 +XCVR_INTERFACE_DATA_SIZE = 92 +SFP_MODULE_ADDRA2_OFFSET = 256 +SFP_MODULE_THRESHOLD_OFFSET = 0 +SFP_MODULE_THRESHOLD_WIDTH = 56 + +QSFP_DOM_BULK_DATA_START = 22 +QSFP_DOM_BULK_DATA_SIZE = 36 +SFP_DOM_BULK_DATA_START = 96 +SFP_DOM_BULK_DATA_SIZE = 10 + +QSFP_DD_DOM_BULK_DATA_START = 14 +QSFP_DD_DOM_BULK_DATA_SIZE = 4 + +# definitions of the offset for values in OSFP info eeprom +OSFP_TYPE_OFFSET = 0 +OSFP_VENDOR_NAME_OFFSET = 129 +OSFP_VENDOR_PN_OFFSET = 148 +OSFP_HW_REV_OFFSET = 164 +OSFP_VENDOR_SN_OFFSET = 166 + +# definitions of the offset for values in QSFP_DD info eeprom +QSFP_DD_TYPE_OFFSET = 0 +QSFP_DD_VENDOR_NAME_OFFSET = 1 +QSFP_DD_VENDOR_PN_OFFSET = 20 +QSFP_DD_VENDOR_SN_OFFSET = 38 +QSFP_DD_VENDOR_OUI_OFFSET = 17 + +#definitions of the offset and width for values in DOM info eeprom +QSFP_DOM_REV_OFFSET = 1 +QSFP_DOM_REV_WIDTH = 1 +QSFP_TEMPE_OFFSET = 22 +QSFP_TEMPE_WIDTH = 2 +QSFP_VOLT_OFFSET = 26 +QSFP_VOLT_WIDTH = 2 +QSFP_VERSION_COMPLIANCE_OFFSET = 1 +QSFP_VERSION_COMPLIANCE_WIDTH = 2 +QSFP_CHANNL_MON_OFFSET = 34 +QSFP_CHANNL_MON_WIDTH = 16 +QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH = 24 +QSFP_CHANNL_DISABLE_STATUS_OFFSET = 86 +QSFP_CHANNL_DISABLE_STATUS_WIDTH = 1 +QSFP_CHANNL_RX_LOS_STATUS_OFFSET = 3 +QSFP_CHANNL_RX_LOS_STATUS_WIDTH = 1 +QSFP_CHANNL_TX_FAULT_STATUS_OFFSET = 4 +QSFP_CHANNL_TX_FAULT_STATUS_WIDTH = 1 +QSFP_CONTROL_OFFSET = 86 +QSFP_CONTROL_WIDTH = 8 +QSFP_MODULE_MONITOR_OFFSET = 0 +QSFP_MODULE_MONITOR_WIDTH = 9 +QSFP_POWEROVERRIDE_OFFSET = 93 +QSFP_POWEROVERRIDE_WIDTH = 1 +QSFP_POWEROVERRIDE_BIT = 0 +QSFP_POWERSET_BIT = 1 +QSFP_OPTION_VALUE_OFFSET = 192 +QSFP_OPTION_VALUE_WIDTH = 4 + +QSFP_MODULE_THRESHOLD_OFFSET = 128 +QSFP_MODULE_THRESHOLD_WIDTH = 24 +QSFP_CHANNL_THRESHOLD_OFFSET = 176 +QSFP_CHANNL_THRESHOLD_WIDTH = 24 + +CMIS_LOWER_PAGE_START = 0 +CMIS_LOWER_PAGE_REVISION_COMPLIANCE_OFFSET = 1 +CMIS_LOWER_PAGE_FLAT_MEM = 2 +CMIS_LOWER_PAGE_MODULE_STATE_OFFSET = 3 + +CMIS_UPPER_PAGE01_START = 128 +CMIS_UPPER_PAGE01_TX_DISABLE_IMPLEMENTED = 155 + + +CMIS_UPPER_PAGE02_START = 384 +CMIS_UPPER_PAGE10_START = 2048 +CMIS_UPPER_PAGE10_DATA_PATH_DEINIT_OFFSET = 128 +CMIS_UPPER_PAGE10_TX_DISABLE_OFFSET = 130 +CMIS_UPPER_PAGE10_STAGED0_APPLY_DATA_PATH_INIT_OFFSET = 143 +CMIS_UPPER_PAGE10_STAGED0_APSEL_CONTROL_OFFSET = 145 + +CMIS_UPPER_PAGE11_START = 2176 +CMIS_UPPER_PAGE11_DATA_PATH_STATE_OFFSET = 128 +CMIS_UPPER_PAGE11_CONFIG_ERROR_CODE_OFFSET = 202 + +SFP_TEMPE_OFFSET = 96 +SFP_TEMPE_WIDTH = 2 +SFP_VOLT_OFFSET = 98 +SFP_VOLT_WIDTH = 2 +SFP_CHANNL_MON_OFFSET = 100 +SFP_CHANNL_MON_WIDTH = 6 + + +SFP_CHANNL_STATUS_OFFSET = 110 +SFP_CHANNL_STATUS_WIDTH = 1 + +SFP_CHANNL_THRESHOLD_OFFSET = 112 +SFP_CHANNL_THRESHOLD_WIDTH = 2 +SFP_STATUS_CONTROL_OFFSET = 110 +SFP_STATUS_CONTROL_WIDTH = 1 +SFP_TX_DISABLE_HARD_BIT = 7 +SFP_TX_DISABLE_SOFT_BIT = 6 + +QSFP_DD_TEMPE_OFFSET = 14 +QSFP_DD_TEMPE_WIDTH = 2 +QSFP_DD_VOLT_OFFSET = 16 +QSFP_DD_VOLT_WIDTH = 2 +QSFP_DD_TX_BIAS_OFFSET = 170 +QSFP_DD_TX_BIAS_WIDTH = 16 +QSFP_DD_RX_POWER_OFFSET = 186 +QSFP_DD_RX_POWER_WIDTH = 16 +QSFP_DD_TX_POWER_OFFSET = 26 +QSFP_DD_TX_POWER_WIDTH = 16 +QSFP_DD_CHANNL_MON_OFFSET = 154 +QSFP_DD_CHANNL_MON_WIDTH = 48 +QSFP_DD_CHANNL_DISABLE_STATUS_OFFSET = 86 +QSFP_DD_CHANNL_DISABLE_STATUS_WIDTH = 1 +QSFP_DD_CHANNL_RX_LOS_STATUS_OFFSET = 19 +QSFP_DD_CHANNL_RX_LOS_STATUS_WIDTH = 1 +QSFP_DD_CHANNL_TX_FAULT_STATUS_OFFSET = 7 +QSFP_DD_CHANNL_TX_FAULT_STATUS_WIDTH = 1 +QSFP_DD_MODULE_THRESHOLD_OFFSET = 128 +QSFP_DD_MODULE_THRESHOLD_WIDTH = 72 +QSFP_DD_CHANNL_STATUS_OFFSET = 26 +QSFP_DD_CHANNL_STATUS_WIDTH = 1 +DOM_OFFSET = 0 +# identifier value of xSFP module which is in the first byte of the EEPROM +# if the identifier value falls into SFP_TYPE_CODE_LIST the module is treated as a SFP module and parsed according to 8472 +# for QSFP_TYPE_CODE_LIST the module is treated as a QSFP module and parsed according to 8436/8636 +# Originally the type (SFP/QSFP) of each module is determined according to the SKU dictionary +# where the type of each FP port is defined. The content of EEPROM is parsed according to its type. +# However, sometimes the SFP module can be fit in an adapter and then pluged into a QSFP port. +# In this case the EEPROM content is in format of SFP but parsed as QSFP, causing failure. +# To resolve that issue the type field of the xSFP module is also fetched so that we can know exectly what type the +# module is. Currently only the following types are recognized as SFP/QSFP module. +# Meanwhile, if the a module's identifier value can't be recognized, it will be parsed according to the SKU dictionary. +# This is because in the future it's possible that some new identifier value which is not regonized but backward compatible +# with the current format and by doing so it can be parsed as much as possible. +SFP_TYPE_CODE_LIST = [ + '03' # SFP/SFP+/SFP28 +] +QSFP_TYPE_CODE_LIST = [ + '0d', # QSFP+ or later + '11' # QSFP28 or later +] +QSFP_DD_TYPE_CODE_LIST = [ + '18' # QSFP-DD Double Density 8X Pluggable Transceiver + '1b' # QSFP-DD Double Density 8X Pluggable Transceiver +] + +qsfp_cable_length_tup = ('Length(km)', 'Length OM3(2m)', + 'Length OM2(m)', 'Length OM1(m)', + 'Length Cable Assembly(m)') + +sfp_cable_length_tup = ('LengthSMFkm-UnitsOfKm', 'LengthSMF(UnitsOf100m)', + 'Length50um(UnitsOf10m)', 'Length62.5um(UnitsOfm)', + 'LengthCable(UnitsOfm)', 'LengthOM3(UnitsOf10m)') + +sfp_compliance_code_tup = ('10GEthernetComplianceCode', 'InfinibandComplianceCode', + 'ESCONComplianceCodes', 'SONETComplianceCodes', + 'EthernetComplianceCodes','FibreChannelLinkLength', + 'FibreChannelTechnology', 'SFP+CableTechnology', + 'FibreChannelTransmissionMedia','FibreChannelSpeed') + +qsfp_compliance_code_tup = ('10/40G Ethernet Compliance Code', 'SONET Compliance codes', + 'SAS/SATA compliance codes', 'Gigabit Ethernet Compliant codes', + 'Fibre Channel link length/Transmitter Technology', + 'Fibre Channel transmission media', 'Fibre Channel Speed') + +SFP_TYPE = "SFP" +QSFP_TYPE = "QSFP" +OSFP_TYPE = "OSFP" +QSFP_DD_TYPE = "QSFP_DD" + +TRANSCEIVER_PATH = '/sys/switch/transceiver/' + +MODULE_STATE = { + 1: 'ModuleLowPwr', + 2: 'ModulePwrUp', + 3: 'ModuleReady', + 4: 'ModulePwrDn', + 5: 'ModuleFault' +} +DATAPATH_STATE = { + 1: 'DataPathDeactivated', + 2: 'DataPathInit', + 3: 'DataPathDeinit', + 4: 'DataPathActivated', + 5: 'DataPathTxTurnOn', + 6: 'DataPathTxTurnOff', + 7: 'DataPathInitialized', +} +CONFIG_STATUS = { + 0: 'ConfigUndefined', + 1: 'ConfigSuccess', + 2: 'ConfigRejected', + 3: 'ConfigRejectedInvalidAppSel', + 4: 'ConfigRejectedInvalidDataPath', + 5: 'ConfigRejectedInvalidSI', + 6: 'ConfigRejectedLaneInUse', + 7: 'ConfigRejectedPartialDataPath', + 12: 'ConfigInProgress', +} + +CMIS_STATE_UNKNOWN = 'UNKNOWN' +CMIS_STATE_INSERTED = 'INSERTED' +CMIS_STATE_DP_DEINIT = 'DP_DEINIT' +CMIS_STATE_AP_CONF = 'AP_CONFIGURED' +CMIS_STATE_DP_INIT = 'DP_INIT' +CMIS_STATE_DP_TXON = 'DP_TXON' +CMIS_STATE_READY = 'READY' +CMIS_STATE_REMOVED = 'REMOVED' +CMIS_STATE_FAILED = 'FAILED' + +CMIS_MAX_RETRIES = 3 +CMIS_DEF_EXPIRED = 1 # seconds, default expiration time +class QSfp_CMIS(SfpBase): + + port_start = 0 + port_end = 55 + NUM_CHANNELS = 8 + + dom_supported = True + dom_temp_supported = True + dom_volt_supported = True + dom_rx_power_supported = True + dom_tx_power_supported = True + dom_tx_disable_supported = True + calibration = 1 + + def __init__(self, index,qsfp_conf): + self.__index = index + self.__conf = qsfp_conf + self.__api_helper = APIHelper() + + self.__attr_path_prefix = '/sys/switch/transceiver/eth{}/'.format(self.__index+1) + + self.__platform = "x86_64-pegatron_fn8656-bnf-r0" + self.__hwsku = "fn8656-bnf" + + self.__presence_attr = None + self.__eeprom_path = None + + if self.__index in range(0, self.port_end + 1): + self.__eeprom_path = TRANSCEIVER_PATH+'eth{}/'.format(self.__index+1)+'eeprom' + self.__presence_attr = TRANSCEIVER_PATH+'eth{}/'.format(self.__index+1)+'present' + self.__reset_attr = TRANSCEIVER_PATH+'eth{}/'.format(self.__index+1)+'reset' + self.__lpmode_attr = TRANSCEIVER_PATH+'eth{}/'.format(self.__index+1)+'lpmode' + SfpBase.__init__(self) + + self.cmis_state = CMIS_STATE_UNKNOWN + self.cmis_retries = 0 + self.cmis_expired = None + + self._detect_sfp_type(QSFP_DD_TYPE) + self.info_dict_keys = ['type', 'hardware_rev', 'serial', 'manufacturer', + 'model', 'connector', 'encoding', 'ext_identifier', + 'ext_rateselect_compliance', 'cable_type', 'cable_length', + 'nominal_bit_rate', 'specification_compliance', 'vendor_date', 'vendor_oui', 'application_advertisement'] + + def _detect_sfp_type(self, sfp_type): + sfp_type = QSFP_DD_TYPE + + if not self.get_presence(): + self.sfp_type = sfp_type + return + + eeprom_raw = [] + + eeprom_raw = self._read_eeprom_specific_bytes( + XCVR_TYPE_OFFSET, XCVR_TYPE_WIDTH) + if eeprom_raw: + if eeprom_raw[0] in SFP_TYPE_CODE_LIST: + self.sfp_type = SFP_TYPE + elif eeprom_raw[0] in QSFP_TYPE_CODE_LIST: + self.sfp_type = QSFP_TYPE + elif eeprom_raw[0] in QSFP_DD_TYPE_CODE_LIST: + self.sfp_type = QSFP_DD_TYPE + else: + self.sfp_type = sfp_type + else: + self.sfp_type = sfp_type + + def __get_attr_value(self, attr_path): + + retval = 'ERR' + if (not os.path.isfile(attr_path)): + return retval + + try: + with open(attr_path, 'r') as fd: + retval = fd.read() + except Exception as error: + logging.error("Unable to open ", attr_path, " file !") + + retval = retval.rstrip(' \t\n\r') + return retval + + def __is_host(self): + return os.system("docker > /dev/null 2>&1") == 0 + + def __get_path_to_port_config_file(self): + host_platform_root_path = '/usr/share/sonic/device' + docker_hwsku_path = '/usr/share/sonic/hwsku' + + host_platform_path = "/".join([host_platform_root_path, self.__platform]) + hwsku_path = "/".join([host_platform_path, self.__hwsku]) if self.__is_host() else docker_hwsku_path + + return "/".join([hwsku_path, "port_config.ini"]) + + def _read_eeprom_specific_bytes(self, offset, num_bytes): + """ + read register from qsfp eeprom + + Args: + offset: + Integer, lower page : 0 - 127,upper page: page*128 + upper offset + num_bytes: + Interger + Returns: + a character list + """ + sysfsfile_eeprom = None + eeprom_raw = [] + + for i in range(0, num_bytes): + eeprom_raw.append("0x00") + + sysfs_eeprom_path = self.__eeprom_path + try: + sysfsfile_eeprom = open(sysfs_eeprom_path, mode="rb", buffering=0) + sysfsfile_eeprom.seek(offset) + raw = sysfsfile_eeprom.read(num_bytes) + raw_len = len(raw) + for n in range(0, raw_len): + eeprom_raw[n] = hex(raw[n])[2:].zfill(2) + except BaseException: + pass + finally: + if sysfsfile_eeprom: + sysfsfile_eeprom.close() + + return eeprom_raw + + def _write_eeprom_specific_bytes(self, offset, data): + """ + write one byte to qsfp eeprom + + Args: + offset: + Integer, lower page : 0 - 127,upper page: page*128 + upper offset + data: + Interger + + Returns: + Boolean, true if success otherwise false + """ + sysfs_eeprom_path = self.__eeprom_path + + buffer = create_string_buffer(1) + buffer[0] = struct.pack('B',data) + try: + sysfsfile_eeprom = open(sysfs_eeprom_path, mode='rb+', buffering=0) + sysfsfile_eeprom.seek(offset) + sysfsfile_eeprom.write(buffer[0]) + sysfsfile_eeprom.close() + except OSError as e: + print(" %s" % str(e), True) + return False + return True + + def read_eeprom(self, offset, num_bytes): + sysfsfile_eeprom = None + eeprom_read_bytes = [] + eeprom_raw = [] + raw = [] + + for i in range(0, num_bytes): + eeprom_raw.append("0x00") + + sysfs_eeprom_path = self.__eeprom_path + try: + sysfsfile_eeprom = open(sysfs_eeprom_path, mode="rb", buffering=0) + sysfsfile_eeprom.seek(offset) + raw = sysfsfile_eeprom.read(num_bytes) + raw_len = len(raw) + eeprom_read_bytes = bytearray(raw) + for n in range(0, raw_len): + eeprom_raw[n] = hex(raw[n])[2:].zfill(2) + except Exception: + pass + finally: + if sysfsfile_eeprom: + sysfsfile_eeprom.close() + + return eeprom_read_bytes + + def write_eeprom(self, offset, num_bytes, write_buffer): + try: + with open(self.__eeprom_path, mode='r+b', buffering=0) as f: + f.seek(offset) + f.write(write_buffer[0:num_bytes]) + f.close() + except (OSError, IOError): + return False + return True + + def _convert_string_to_num(self, value_str): + if "-inf" in value_str: + return 'N/A' + elif "Unknown" in value_str: + return 'N/A' + elif 'dBm' in value_str: + t_str = value_str.rstrip('dBm') + return float(t_str) + elif 'mA' in value_str: + t_str = value_str.rstrip('mA') + return float(t_str) + elif 'C' in value_str: + t_str = value_str.rstrip('C') + return float(t_str) + elif 'Volts' in value_str: + t_str = value_str.rstrip('Volts') + return float(t_str) + else: + return 'N/A' + + def _dom_capability_detect(self): + if not self.get_presence(): + self.dom_supported = False + self.dom_temp_supported = False + self.dom_volt_supported = False + self.dom_rx_power_supported = False + self.dom_tx_bias_power_supported = False + self.dom_tx_power_supported = False + self.calibration = 0 + return + + sfpi_obj = qsfp_dd_InterfaceId() + if sfpi_obj is None: + self.dom_supported = False + + offset = 0 + # two types of QSFP-DD cable types supported: Copper and Optical. + qsfp_dom_capability_raw = self._read_eeprom_specific_bytes((offset + XCVR_DOM_CAPABILITY_OFFSET_QSFP_DD), XCVR_DOM_CAPABILITY_WIDTH_QSFP_DD) + if qsfp_dom_capability_raw is not None: + self.dom_temp_supported = True + self.dom_volt_supported = True + dom_capability = sfpi_obj.parse_dom_capability(qsfp_dom_capability_raw, 0) + if dom_capability['data']['Flat_MEM']['value'] == 'Off': + self.dom_supported = True + self.second_application_list = True + self.dom_rx_power_supported = True + self.dom_tx_power_supported = True + self.dom_tx_bias_power_supported = True + self.dom_thresholds_supported = True + self.dom_rx_tx_power_bias_supported = True + else: + self.dom_supported = False + self.second_application_list = False + self.dom_rx_power_supported = False + self.dom_tx_power_supported = False + self.dom_tx_bias_power_supported = False + self.dom_thresholds_supported = False + self.dom_rx_tx_power_bias_supported = False + else: + self.dom_supported = False + self.dom_temp_supported = False + self.dom_volt_supported = False + self.dom_rx_power_supported = False + self.dom_tx_power_supported = False + self.dom_tx_bias_power_supported = False + self.dom_thresholds_supported = False + self.dom_rx_tx_power_bias_supported = False + +############################################## +# Device methods +############################################## + + def get_name(self): + """ + Retrieves the name of the device + + Returns: + string: The name of the device + """ + return self.__conf[self.__index]['name'] + + def get_presence(self): + """ + Retrieves the presence of the device + + Returns: + bool: True if device is present, False if not + """ + presence = False + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'present') + if (attr_rv != None): + attr_rv = int(attr_rv, 16) + if ((attr_rv & 0x1) == 1): + presence = True + return presence + + def get_all_presence(self): + """ + Retrieves the presence of the device + + Returns: + bool: True if device is present, False if not + """ + + presence_bit = 0 + attr_rv = self.__api_helper.read_one_line_file(TRANSCEIVER_PATH + 'present') + if (attr_rv != None): + presence_bit = int(attr_rv, 16) + return presence_bit + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + + Returns: + string: Model/part number of device + """ + transceiver_info_dict = self.get_transceiver_info() + return transceiver_info_dict.get("model", "N/A") + + def get_serial(self): + """ + Retrieves the serial number of the device + + Returns: + string: Serial number of device + """ + transceiver_info_dict = self.get_transceiver_info() + return transceiver_info_dict.get("serial", "N/A") + + def get_status(self): + """ + Retrieves the operational status of the device + + Returns: + A boolean value, True if device is operating properly, False if not + """ + return self.get_presence() and not self.get_reset_status() + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return self.__index + + def is_replaceable(self): + """ + Indicate whether Chassis is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True +############################################## +# SFP methods +############################################## + + def get_transceiver_type(self,origin_transceiver_type): + transceiver_type = origin_transceiver_type + if transceiver_type != 'Unknown': + return transceiver_type + + type_of_transceiver = { + '00': 'Unknown or unspecified', + '01': 'GBIC', + '02': 'Module/connector soldered to motherboard', + '03': 'SFP/SFP+/SFP28', + '04': '300 pin XBI', + '05': 'XENPAK', + '06': 'XFP', + '07': 'XFF', + '08': 'XFP-E', + '09': 'XPAK', + '0a': 'X2', + '0b': 'DWDM-SFP/SFP+', + '0c': 'QSFP', + '0d': 'QSFP+ or later', + '0e': 'CXP or later', + '0f': 'Shielded Mini Multilane HD 4X', + '10': 'Shielded Mini Multilane HD 8X', + '11': 'QSFP28 or later', + '12': 'CXP2 (aka CXP28) or later', + '13': 'CDFP (Style 1/Style2)', + '14': 'Shielded Mini Multilane HD 4X Fanout Cable', + '15': 'Shielded Mini Multilane HD 8X Fanout Cable', + '16': 'CDFP (Style 3)', + '17': 'microQSFP', + '18': 'QSFP-DD Double Density 8X Pluggable Transceiver', + '19': 'OSFP 8X Pluggable Transceiver', + '1a': 'SFP-DD Double Density 2X Pluggable Transceiver', + '1b': 'DSFP Dual Small Form Factor Pluggable Transceiver', + '1c': 'x4 MiniLink/OcuLink', + '1d': 'x8 MiniLink', + '1e': 'QSFP+ or later with CMIS' + } + + offset = 128 + sfp_type_raw = self._read_eeprom_specific_bytes((offset + QSFP_DD_TYPE_OFFSET), XCVR_TYPE_WIDTH) + transceiver_type = type_of_transceiver.get(sfp_type_raw[0]) + return transceiver_type + + def get_transceiver_type_abbr_name(self,origin_transceiver_type): + transceiver_type_abbr_name = origin_transceiver_type + if transceiver_type_abbr_name != 'Unknown': + return transceiver_type_abbr_name + + type_abbrv_name = { + '00': 'Unknown', + '01': 'GBIC', + '02': 'Soldered', + '03': 'SFP', + '04': 'XBI300', + '05': 'XENPAK', + '06': 'XFP', + '07': 'XFF', + '08': 'XFP-E', + '09': 'XPAK', + '0a': 'X2', + '0b': 'DWDM-SFP', + '0c': 'QSFP', + '0d': 'QSFP+', + '0e': 'CXP', + '0f': 'HD4X', + '10': 'HD8X', + '11': 'QSFP28', + '12': 'CXP2', + '13': 'CDFP-1/2', + '14': 'HD4X-Fanout', + '15': 'HD8X-Fanout', + '16': 'CDFP-3', + '17': 'MicroQSFP', + '18': 'QSFP-DD', + '19': 'OSFP-8X', + '1a': 'SFP-DD', + '1b': 'DSFP', + '1c': 'Link-x4', + '1d': 'Link-x8', + '1e': 'QSFP+C' + } + offset = 128 + sfp_type_raw = self._read_eeprom_specific_bytes((offset + QSFP_DD_TYPE_OFFSET), XCVR_TYPE_WIDTH) + transceiver_type_abbr_name = type_abbrv_name.get(sfp_type_raw[0]) + return transceiver_type_abbr_name + + + + def get_transceiver_info(self): + """ + Retrieves transceiver info of this SFP + + Returns: + A dict which contains following keys/values : + ======================================================================== + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + type |1*255VCHAR |type of SFP + hardware_rev |1*255VCHAR |hardware version of SFP + serial |1*255VCHAR |serial number of the SFP + manufacturer |1*255VCHAR |SFP vendor name + model |1*255VCHAR |SFP model name + connector |1*255VCHAR |connector information + encoding |1*255VCHAR |encoding information + ext_identifier |1*255VCHAR |extend identifier + ext_rateselect_compliance |1*255VCHAR |extended rateSelect compliance + cable_length |INT |cable length in m + mominal_bit_rate |INT |nominal bit rate by 100Mbs + specification_compliance |1*255VCHAR |specification compliance + vendor_date |1*255VCHAR |vendor date + vendor_oui |1*255VCHAR |vendor OUI + ======================================================================== + """ + transceiver_info_dict = {} + transceiver_info_dict = dict.fromkeys(self.info_dict_keys, 'N/A') + transceiver_info_dict['specification_compliance'] = '{}' + + if not self.get_presence(): + return transceiver_info_dict + + self._detect_sfp_type(self.sfp_type) + self._dom_capability_detect() + + sfpi_obj = qsfp_dd_InterfaceId() + if not sfpi_obj: + return None + + offset = 128 + sfp_type_raw = self._read_eeprom_specific_bytes((offset + QSFP_DD_TYPE_OFFSET), XCVR_TYPE_WIDTH) + sfp_type_data = sfpi_obj.parse_sfp_type(sfp_type_raw, 0) + sfp_type_abbrv_name = sfpi_obj.parse_sfp_type_abbrv_name(sfp_type_raw, 0) + + sfp_vendor_name_raw = self._read_eeprom_specific_bytes((offset + QSFP_DD_VENDOR_NAME_OFFSET), XCVR_VENDOR_NAME_WIDTH) + sfp_vendor_name_data = sfpi_obj.parse_vendor_name(sfp_vendor_name_raw, 0) + + sfp_vendor_pn_raw = self._read_eeprom_specific_bytes((offset + QSFP_DD_VENDOR_PN_OFFSET), XCVR_VENDOR_PN_WIDTH) + sfp_vendor_pn_data = sfpi_obj.parse_vendor_pn(sfp_vendor_pn_raw, 0) + + sfp_vendor_rev_raw = self._read_eeprom_specific_bytes((offset + XCVR_HW_REV_OFFSET_QSFP_DD), XCVR_HW_REV_WIDTH_QSFP_DD) + sfp_vendor_rev_data = sfpi_obj.parse_vendor_rev(sfp_vendor_rev_raw, 0) + + sfp_vendor_sn_raw = self._read_eeprom_specific_bytes((offset + QSFP_DD_VENDOR_SN_OFFSET), XCVR_VENDOR_SN_WIDTH) + sfp_vendor_sn_data = sfpi_obj.parse_vendor_sn(sfp_vendor_sn_raw, 0) + + sfp_vendor_oui_raw = self._read_eeprom_specific_bytes((offset + QSFP_DD_VENDOR_OUI_OFFSET), XCVR_VENDOR_OUI_WIDTH) + sfp_vendor_oui_data = sfpi_obj.parse_vendor_oui(sfp_vendor_oui_raw, 0) + + sfp_vendor_date_raw = self._read_eeprom_specific_bytes((offset + XCVR_VENDOR_DATE_OFFSET_QSFP_DD), XCVR_VENDOR_DATE_WIDTH_QSFP_DD) + sfp_vendor_date_data = sfpi_obj.parse_vendor_date(sfp_vendor_date_raw, 0) + + sfp_connector_raw = self._read_eeprom_specific_bytes((offset + XCVR_CONNECTOR_OFFSET_QSFP_DD), XCVR_CONNECTOR_WIDTH_QSFP_DD) + sfp_connector_data = sfpi_obj.parse_connector(sfp_connector_raw, 0) + + sfp_ext_identifier_raw = self._read_eeprom_specific_bytes((offset + XCVR_EXT_TYPE_OFFSET_QSFP_DD), XCVR_EXT_TYPE_WIDTH_QSFP_DD) + sfp_ext_identifier_data = sfpi_obj.parse_ext_iden(sfp_ext_identifier_raw, 0) + + sfp_cable_len_raw = self._read_eeprom_specific_bytes((offset + XCVR_CABLE_LENGTH_OFFSET_QSFP_DD), XCVR_CABLE_LENGTH_WIDTH_QSFP_DD) + sfp_cable_len_data = sfpi_obj.parse_cable_len(sfp_cable_len_raw, 0) + + sfp_media_type_raw = self._read_eeprom_specific_bytes(XCVR_MEDIA_TYPE_OFFSET_QSFP_DD, XCVR_MEDIA_TYPE_WIDTH_QSFP_DD) + if sfp_media_type_raw is not None: + sfp_media_type_dict = sfpi_obj.parse_media_type(sfp_media_type_raw, 0) + if sfp_media_type_dict is None: + return None + + host_media_list = "" + sfp_application_type_first_list = self._read_eeprom_specific_bytes((XCVR_FIRST_APPLICATION_LIST_OFFSET_QSFP_DD), XCVR_FIRST_APPLICATION_LIST_WIDTH_QSFP_DD) + if self.second_application_list: + possible_application_count = 15 + sfp_application_type_second_list = self._read_eeprom_specific_bytes((XCVR_SECOND_APPLICATION_LIST_OFFSET_QSFP_DD), XCVR_SECOND_APPLICATION_LIST_WIDTH_QSFP_DD) + if sfp_application_type_first_list is not None and sfp_application_type_second_list is not None: + sfp_application_type_list = sfp_application_type_first_list + sfp_application_type_second_list + else: + return None + else: + possible_application_count = 8 + if sfp_application_type_first_list is not None: + sfp_application_type_list = sfp_application_type_first_list + else: + return None + + for i in range(0, possible_application_count): + if sfp_application_type_list[i * 4] == 'ff': + break + host_electrical, media_interface = sfpi_obj.parse_application(sfp_media_type_dict, sfp_application_type_list[i * 4], sfp_application_type_list[i * 4 + 1]) + host_media_list = host_media_list + host_electrical + ' - ' + media_interface + '\n\t\t\t\t ' + else: + return None + + transceiver_info_dict['manufacturer'] = str(sfp_vendor_name_data['data']['Vendor Name']['value']) + transceiver_info_dict['model'] = str(sfp_vendor_pn_data['data']['Vendor PN']['value']) + transceiver_info_dict['hardware_rev'] = str(sfp_vendor_rev_data['data']['Vendor Rev']['value']) + transceiver_info_dict['serial'] = sfp_vendor_sn_data['data']['Vendor SN']['value'] + transceiver_info_dict['vendor_oui'] = str(sfp_vendor_oui_data['data']['Vendor OUI']['value']) + transceiver_info_dict['vendor_date'] = str(sfp_vendor_date_data['data']['VendorDataCode(YYYY-MM-DD Lot)']['value']) + transceiver_info_dict['connector'] = str(sfp_connector_data['data']['Connector']['value']) + transceiver_info_dict['encoding'] = "Not supported for CMIS cables" + transceiver_info_dict['ext_identifier'] = str(sfp_ext_identifier_data['data']['Extended Identifier']['value']) + transceiver_info_dict['ext_rateselect_compliance'] = "Not supported for CMIS cables" + transceiver_info_dict['specification_compliance'] = "Not supported for CMIS cables" + transceiver_info_dict['cable_type'] = "Length Cable Assembly(m)" + transceiver_info_dict['cable_length'] = str(sfp_cable_len_data['data']['Length Cable Assembly(m)']['value']) + transceiver_info_dict['nominal_bit_rate'] = "Not supported for CMIS cables" + transceiver_info_dict['application_advertisement'] = host_media_list + transceiver_info_dict['type'] = self.get_transceiver_type(str(sfp_type_data['data']['type']['value'])) + transceiver_info_dict['type_abbrv_name'] = self.get_transceiver_type_abbr_name(str(sfp_type_abbrv_name['data']['type_abbrv_name']['value'])) + + #media_settings.json only match media_type + if sfp_media_type_raw[0] == '03': + transceiver_info_dict['specification_compliance'] = "{'media_interface':'DAC'}" + if transceiver_info_dict['type'] == "QSFP-DD Double Density 8X Pluggable Transceiver": + transceiver_info_dict['specification_compliance'] = "media_interface: DAC" + elif sfp_media_type_raw[0] == '04' or sfp_media_type_raw[0] == '01' or sfp_media_type_raw[0] == '02': + transceiver_info_dict['specification_compliance'] = "{'media_interface':'AOC'}" + if transceiver_info_dict['type'] == "QSFP-DD Double Density 8X Pluggable Transceiver": + transceiver_info_dict['specification_compliance'] = "media_interface: AOC" + + return transceiver_info_dict + + def get_transceiver_bulk_status(self): + """ + Retrieves transceiver bulk status of this SFP + + Returns: + A dict which contains following keys/values : + ======================================================================== + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + rx_los |BOOLEAN |RX loss-of-signal status, True if has RX los, False if not. + tx_fault |BOOLEAN |TX fault status, True if has TX fault, False if not. + reset_status |BOOLEAN |reset status, True if SFP in reset, False if not. + lp_mode |BOOLEAN |low power mode status, True in lp mode, False if not. + tx_disable |BOOLEAN |TX disable status, True TX disabled, False if not. + tx_disabled_channel |HEX |disabled TX channels in hex, bits 0 to 3 represent channel 0 + | |to channel 3. + temperature |INT |module temperature in Celsius + voltage |INT |supply voltage in mV + txbias |INT |TX Bias Current in mA, n is the channel number, + | |for example, tx2bias stands for tx bias of channel 2. + rxpower |INT |received optical power in mW, n is the channel number, + | |for example, rx2power stands for rx power of channel 2. + txpower |INT |TX output power in mW, n is the channel number, + | |for example, tx2power stands for tx power of channel 2. + ======================================================================== + """ + transceiver_dom_info_dict = {} + + dom_info_dict_keys = ['temperature', 'voltage', + 'rx1power', 'rx2power', + 'rx3power', 'rx4power', + 'rx5power', 'rx6power', + 'rx7power', 'rx8power', + 'tx1bias', 'tx2bias', + 'tx3bias', 'tx4bias', + 'tx5bias', 'tx6bias', + 'tx7bias', 'tx8bias', + 'tx1power', 'tx2power', + 'tx3power', 'tx4power', + 'tx5power', 'tx6power', + 'tx7power', 'tx8power' + ] + transceiver_dom_info_dict = dict.fromkeys(dom_info_dict_keys, 'N/A') + + if not self.get_presence(): + return {} + + self._dom_capability_detect() + + offset = 0 + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return transceiver_dom_info_dict + + dom_data_raw = self._read_eeprom_specific_bytes((offset + QSFP_DD_DOM_BULK_DATA_START), QSFP_DD_DOM_BULK_DATA_SIZE) + if dom_data_raw is None: + return transceiver_dom_info_dict + + if self.dom_temp_supported: + start = QSFP_DD_TEMPE_OFFSET - QSFP_DD_DOM_BULK_DATA_START + end = start + QSFP_DD_TEMPE_WIDTH + dom_temperature_data = sfpd_obj.parse_temperature(dom_data_raw[start : end], 0) + temp = self._convert_string_to_num(dom_temperature_data['data']['Temperature']['value']) + if temp is not None: + transceiver_dom_info_dict['temperature'] = temp + + if self.dom_volt_supported: + start = QSFP_DD_VOLT_OFFSET - QSFP_DD_DOM_BULK_DATA_START + end = start + QSFP_DD_VOLT_WIDTH + dom_voltage_data = sfpd_obj.parse_voltage(dom_data_raw[start : end], 0) + volt = self._convert_string_to_num(dom_voltage_data['data']['Vcc']['value']) + if volt is not None: + transceiver_dom_info_dict['voltage'] = volt + + if self.dom_rx_tx_power_bias_supported: + # page 11h + offset = CMIS_UPPER_PAGE11_START + dom_data_raw = self._read_eeprom_specific_bytes(offset + QSFP_DD_CHANNL_MON_OFFSET, QSFP_DD_CHANNL_MON_WIDTH) + if dom_data_raw is None: + return transceiver_dom_info_dict + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params(dom_data_raw, 0) + + if self.dom_tx_power_supported: + transceiver_dom_info_dict['tx1power'] = str(self._convert_string_to_num(dom_channel_monitor_data['data']['TX1Power']['value'])) + transceiver_dom_info_dict['tx2power'] = str(self._convert_string_to_num(dom_channel_monitor_data['data']['TX2Power']['value'])) + transceiver_dom_info_dict['tx3power'] = str(self._convert_string_to_num(dom_channel_monitor_data['data']['TX3Power']['value'])) + transceiver_dom_info_dict['tx4power'] = str(self._convert_string_to_num(dom_channel_monitor_data['data']['TX4Power']['value'])) + transceiver_dom_info_dict['tx5power'] = str(self._convert_string_to_num(dom_channel_monitor_data['data']['TX5Power']['value'])) + transceiver_dom_info_dict['tx6power'] = str(self._convert_string_to_num(dom_channel_monitor_data['data']['TX6Power']['value'])) + transceiver_dom_info_dict['tx7power'] = str(self._convert_string_to_num(dom_channel_monitor_data['data']['TX7Power']['value'])) + transceiver_dom_info_dict['tx8power'] = str(self._convert_string_to_num(dom_channel_monitor_data['data']['TX8Power']['value'])) + + if self.dom_rx_power_supported: + transceiver_dom_info_dict['rx1power'] = str(self._convert_string_to_num(dom_channel_monitor_data['data']['RX1Power']['value'])) + transceiver_dom_info_dict['rx2power'] = str(self._convert_string_to_num(dom_channel_monitor_data['data']['RX2Power']['value'])) + transceiver_dom_info_dict['rx3power'] = str(self._convert_string_to_num(dom_channel_monitor_data['data']['RX3Power']['value'])) + transceiver_dom_info_dict['rx4power'] = str(self._convert_string_to_num(dom_channel_monitor_data['data']['RX4Power']['value'])) + transceiver_dom_info_dict['rx5power'] = str(self._convert_string_to_num(dom_channel_monitor_data['data']['RX5Power']['value'])) + transceiver_dom_info_dict['rx6power'] = str(self._convert_string_to_num(dom_channel_monitor_data['data']['RX6Power']['value'])) + transceiver_dom_info_dict['rx7power'] = str(self._convert_string_to_num(dom_channel_monitor_data['data']['RX7Power']['value'])) + transceiver_dom_info_dict['rx8power'] = str(self._convert_string_to_num(dom_channel_monitor_data['data']['RX8Power']['value'])) + + if self.dom_tx_bias_power_supported: + transceiver_dom_info_dict['tx1bias'] = str(dom_channel_monitor_data['data']['TX1Bias']['value']) + transceiver_dom_info_dict['tx2bias'] = str(dom_channel_monitor_data['data']['TX2Bias']['value']) + transceiver_dom_info_dict['tx3bias'] = str(dom_channel_monitor_data['data']['TX3Bias']['value']) + transceiver_dom_info_dict['tx4bias'] = str(dom_channel_monitor_data['data']['TX4Bias']['value']) + transceiver_dom_info_dict['tx5bias'] = str(dom_channel_monitor_data['data']['TX5Bias']['value']) + transceiver_dom_info_dict['tx6bias'] = str(dom_channel_monitor_data['data']['TX6Bias']['value']) + transceiver_dom_info_dict['tx7bias'] = str(dom_channel_monitor_data['data']['TX7Bias']['value']) + transceiver_dom_info_dict['tx8bias'] = str(dom_channel_monitor_data['data']['TX8Bias']['value']) + + return transceiver_dom_info_dict + + def get_transceiver_threshold_info(self): + """ + Retrieves transceiver threshold info of this SFP + + Returns: + A dict which contains following keys/values : + ======================================================================== + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + temphighalarm |FLOAT |High Alarm Threshold value of temperature in Celsius. + templowalarm |FLOAT |Low Alarm Threshold value of temperature in Celsius. + temphighwarning |FLOAT |High Warning Threshold value of temperature in Celsius. + templowwarning |FLOAT |Low Warning Threshold value of temperature in Celsius. + vcchighalarm |FLOAT |High Alarm Threshold value of supply voltage in mV. + vcclowalarm |FLOAT |Low Alarm Threshold value of supply voltage in mV. + vcchighwarning |FLOAT |High Warning Threshold value of supply voltage in mV. + vcclowwarning |FLOAT |Low Warning Threshold value of supply voltage in mV. + rxpowerhighalarm |FLOAT |High Alarm Threshold value of received power in dBm. + rxpowerlowalarm |FLOAT |Low Alarm Threshold value of received power in dBm. + rxpowerhighwarning |FLOAT |High Warning Threshold value of received power in dBm. + rxpowerlowwarning |FLOAT |Low Warning Threshold value of received power in dBm. + txpowerhighalarm |FLOAT |High Alarm Threshold value of transmit power in dBm. + txpowerlowalarm |FLOAT |Low Alarm Threshold value of transmit power in dBm. + txpowerhighwarning |FLOAT |High Warning Threshold value of transmit power in dBm. + txpowerlowwarning |FLOAT |Low Warning Threshold value of transmit power in dBm. + txbiashighalarm |FLOAT |High Alarm Threshold value of tx Bias Current in mA. + txbiaslowalarm |FLOAT |Low Alarm Threshold value of tx Bias Current in mA. + txbiashighwarning |FLOAT |High Warning Threshold value of tx Bias Current in mA. + txbiaslowwarning |FLOAT |Low Warning Threshold value of tx Bias Current in mA. + ======================================================================== + """ + transceiver_dom_threshold_info_dict = {} + + dom_info_dict_keys = ['temphighalarm', 'temphighwarning', + 'templowalarm', 'templowwarning', + 'vcchighalarm', 'vcchighwarning', + 'vcclowalarm', 'vcclowwarning', + 'rxpowerhighalarm', 'rxpowerhighwarning', + 'rxpowerlowalarm', 'rxpowerlowwarning', + 'txpowerhighalarm', 'txpowerhighwarning', + 'txpowerlowalarm', 'txpowerlowwarning', + 'txbiashighalarm', 'txbiashighwarning', + 'txbiaslowalarm', 'txbiaslowwarning' + ] + transceiver_dom_threshold_info_dict = dict.fromkeys(dom_info_dict_keys, 'N/A') + if not self.get_presence(): + return {} + + if not self.dom_supported: + return transceiver_dom_threshold_info_dict + + if not self.dom_thresholds_supported: + return transceiver_dom_threshold_info_dict + + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return transceiver_dom_threshold_info_dict + + # page 02 + offset = CMIS_UPPER_PAGE02_START + dom_module_threshold_raw = self._read_eeprom_specific_bytes((offset + QSFP_DD_MODULE_THRESHOLD_OFFSET), QSFP_DD_MODULE_THRESHOLD_WIDTH) + if dom_module_threshold_raw is None: + return transceiver_dom_threshold_info_dict + + dom_module_threshold_data = sfpd_obj.parse_module_threshold_values(dom_module_threshold_raw, 0) + + # Threshold Data + transceiver_dom_threshold_info_dict['temphighalarm'] = dom_module_threshold_data['data']['TempHighAlarm']['value'] + transceiver_dom_threshold_info_dict['temphighwarning'] = dom_module_threshold_data['data']['TempHighWarning']['value'] + transceiver_dom_threshold_info_dict['templowalarm'] = dom_module_threshold_data['data']['TempLowAlarm']['value'] + transceiver_dom_threshold_info_dict['templowwarning'] = dom_module_threshold_data['data']['TempLowWarning']['value'] + transceiver_dom_threshold_info_dict['vcchighalarm'] = dom_module_threshold_data['data']['VccHighAlarm']['value'] + transceiver_dom_threshold_info_dict['vcchighwarning'] = dom_module_threshold_data['data']['VccHighWarning']['value'] + transceiver_dom_threshold_info_dict['vcclowalarm'] = dom_module_threshold_data['data']['VccLowAlarm']['value'] + transceiver_dom_threshold_info_dict['vcclowwarning'] = dom_module_threshold_data['data']['VccLowWarning']['value'] + transceiver_dom_threshold_info_dict['rxpowerhighalarm'] = dom_module_threshold_data['data']['RxPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerhighwarning'] = dom_module_threshold_data['data']['RxPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowalarm'] = dom_module_threshold_data['data']['RxPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowwarning'] = dom_module_threshold_data['data']['RxPowerLowWarning']['value'] + transceiver_dom_threshold_info_dict['txbiashighalarm'] = dom_module_threshold_data['data']['TxBiasHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiashighwarning'] = dom_module_threshold_data['data']['TxBiasHighWarning']['value'] + transceiver_dom_threshold_info_dict['txbiaslowalarm'] = dom_module_threshold_data['data']['TxBiasLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiaslowwarning'] = dom_module_threshold_data['data']['TxBiasLowWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerhighalarm'] = dom_module_threshold_data['data']['TxPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerhighwarning'] = dom_module_threshold_data['data']['TxPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerlowalarm'] = dom_module_threshold_data['data']['TxPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerlowwarning'] = dom_module_threshold_data['data']['TxPowerLowWarning']['value'] + + + return transceiver_dom_threshold_info_dict + + def get_reset_status(self): + """ + Retrieves the reset status of SFP + + Returns: + A Boolean, True if reset enabled, False if disabled + """ + reset_status = False + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'reset') + if (attr_rv != None): + attr_rv = int(attr_rv, 16) + if ((attr_rv & 0x1) == 1): + reset_status = True + return reset_status + + def get_rx_los(self): + """ + Retrieves the RX LOS (loss-of-signal) status of SFP + + Returns: + A list of boolean values, representing the RX LOS status + of each available channel, value is True if SFP channel + has RX LOS, False if not. + E.g., for a tranceiver with four channels: [False, False, True, False] + Note : RX LOS status is latched until a call to get_rx_los or a reset. + """ + if not self.dom_supported: + return None + + rx_los_list = [] + + if self.dom_rx_tx_power_bias_supported: + offset = 512 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes((offset + QSFP_DD_CHANNL_RX_LOS_STATUS_OFFSET), QSFP_DD_CHANNL_RX_LOS_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + rx_los_data = int(dom_channel_monitor_raw[0], 8) + rx_los_list.append(rx_los_data & 0x01 != 0) + rx_los_list.append(rx_los_data & 0x02 != 0) + rx_los_list.append(rx_los_data & 0x04 != 0) + rx_los_list.append(rx_los_data & 0x08 != 0) + rx_los_list.append(rx_los_data & 0x10 != 0) + rx_los_list.append(rx_los_data & 0x20 != 0) + rx_los_list.append(rx_los_data & 0x40 != 0) + rx_los_list.append(rx_los_data & 0x80 != 0) + + return rx_los_list + + def get_tx_fault(self): + """ + Retrieves the TX fault status of SFP + + Returns: + A list of boolean values, representing the TX fault status + of each available channel, value is True if SFP channel + has TX fault, False if not. + E.g., for a tranceiver with four channels: [False, False, True, False] + Note : TX fault status is lached until a call to get_tx_fault or a reset. + """ + if not self.dom_supported: + return None + + return None + + def get_lpmode(self): + """ + Retrieves the lpmode (low power mode) status of this SFP + + Returns: + A Boolean, True if lpmode is enabled, False if disabled + """ + lpmode = False + attr_path = self.__lpmode_attr + + attr_rv = self.__get_attr_value(attr_path) + if (attr_rv != 'ERR'): + if (int(attr_rv,16) == 1): + lpmode = True + + return lpmode + + def get_power_override(self): + """ + Retrieves the power-override status of this SFP + + Returns: + A Boolean, True if power-override is enabled, False if disabled + """ + return None + + def get_temperature(self): + """ + Retrieves the temperature of this SFP + + Returns: + An integer number of current temperature in Celsius + """ + offset = 0 + + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return None + + if self.dom_temp_supported: + dom_temperature_raw = self._read_eeprom_specific_bytes((offset + QSFP_DD_TEMPE_OFFSET), QSFP_DD_TEMPE_WIDTH) + if dom_temperature_raw is not None: + dom_temperature_data = sfpd_obj.parse_temperature(dom_temperature_raw, 0) + temp = self._convert_string_to_num(dom_temperature_data['data']['Temperature']['value']) + return temp + return None + + def get_voltage(self): + """ + Retrieves the supply voltage of this SFP + + Returns: + An integer number of supply voltage in mV + """ + offset = 128 + + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return None + + if self.dom_volt_supported: + dom_voltage_raw = self._read_eeprom_specific_bytes((offset + QSFP_DD_VOLT_OFFSET), QSFP_DD_VOLT_WIDTH) + if dom_voltage_raw is not None: + dom_voltage_data = sfpd_obj.parse_voltage(dom_voltage_raw, 0) + voltage = self._convert_string_to_num(dom_voltage_data['data']['Vcc']['value']) + return voltage + return None + + + def get_tx_bias(self): + """ + Retrieves the TX bias current of this SFP + + Returns: + A list of four integer numbers, representing TX bias in mA + for channel 0 to channel 4. + Ex. ['110.09', '111.12', '108.21', '112.09'] + """ + tx_bias_list = [] + # page 11h + if self.dom_rx_tx_power_bias_supported: + offset = CMIS_UPPER_PAGE11_START + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return None + + if self.dom_tx_bias_power_supported: + dom_tx_bias_raw = self._read_eeprom_specific_bytes((offset + QSFP_DD_TX_BIAS_OFFSET), QSFP_DD_TX_BIAS_WIDTH) + if dom_tx_bias_raw is not None: + dom_tx_bias_data = sfpd_obj.parse_dom_tx_bias(dom_tx_bias_raw, 0) + tx_bias_list.append(self._convert_string_to_num(dom_tx_bias_data['data']['TX1Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num(dom_tx_bias_data['data']['TX2Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num(dom_tx_bias_data['data']['TX3Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num(dom_tx_bias_data['data']['TX4Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num(dom_tx_bias_data['data']['TX5Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num(dom_tx_bias_data['data']['TX6Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num(dom_tx_bias_data['data']['TX7Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num(dom_tx_bias_data['data']['TX8Bias']['value'])) + return tx_bias_list + + def get_rx_power(self): + """ + Retrieves the received optical power for this SFP + + Returns: + A list of four integer numbers, representing received optical + power in mW for channel 0 to channel 4. + Ex. ['1.77', '1.71', '1.68', '1.70'] + """ + rx_power_list = [] + # page 11 + if self.dom_rx_tx_power_bias_supported: + offset = CMIS_UPPER_PAGE11_START + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return None + + if self.dom_rx_power_supported: + dom_rx_power_raw = self._read_eeprom_specific_bytes((offset + QSFP_DD_RX_POWER_OFFSET), QSFP_DD_RX_POWER_WIDTH) + if dom_rx_power_raw is not None: + dom_rx_power_data = sfpd_obj.parse_dom_rx_power(dom_rx_power_raw, 0) + rx_power_list.append(self._convert_string_to_num(dom_rx_power_data['data']['RX1Power']['value'])) + rx_power_list.append(self._convert_string_to_num(dom_rx_power_data['data']['RX2Power']['value'])) + rx_power_list.append(self._convert_string_to_num(dom_rx_power_data['data']['RX3Power']['value'])) + rx_power_list.append(self._convert_string_to_num(dom_rx_power_data['data']['RX4Power']['value'])) + rx_power_list.append(self._convert_string_to_num(dom_rx_power_data['data']['RX5Power']['value'])) + rx_power_list.append(self._convert_string_to_num(dom_rx_power_data['data']['RX6Power']['value'])) + rx_power_list.append(self._convert_string_to_num(dom_rx_power_data['data']['RX7Power']['value'])) + rx_power_list.append(self._convert_string_to_num(dom_rx_power_data['data']['RX8Power']['value'])) + return rx_power_list + + def get_tx_power(self): + """ + Retrieves the TX power of this SFP + + Returns: + A list of four integer numbers, representing TX power in mW + for channel 0 to channel 4. + Ex. ['1.86', '1.86', '1.86', '1.86'] + """ + return None + + def reset(self): + """ + Reset SFP and return all user module settings to their default srate. + + Returns: + A boolean, True if successful, False if not + """ + + try: + reg_file = open(self.__reset_attr, "r+") + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False + + reg_value = 1 + reg_file.write(hex(reg_value)) + reg_file.close() + + # Sleep 2 second to allow it to settle + time.sleep(2) + + try: + reg_file = open(self.__reset_attr, "r+") + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False + + reg_value = 0 + reg_file.write(hex(reg_value)) + reg_file.close() + + return True + + def is_mem_flat(self): + offset = CMIS_LOWER_PAGE_START + CMIS_LOWER_PAGE_FLAT_MEM + data = self._read_eeprom_specific_bytes(offset, 1) + is_flat_memory = (int(data[0],16) & 0xff) >> 7 + return is_flat_memory + + def get_tx_disable_support(self): + offset = CMIS_UPPER_PAGE01_START + CMIS_UPPER_PAGE01_TX_DISABLE_IMPLEMENTED + data = self._read_eeprom_specific_bytes(offset, 1) + tx_disable_implemented = ((int(data[0],16) & 0xff) & (0x1 << 1)) >> 1 + return not self.is_mem_flat() and tx_disable_implemented + + def get_tx_disable(self): + """ + Retrieves the tx_disable status of this SFP + + Returns: + A Boolean List + """ + tx_disable_support = self.get_tx_disable_support() + if tx_disable_support is None: + return None + if not tx_disable_support: + return ["N/A" for _ in range(self.NUM_CHANNELS)] + offset = CMIS_UPPER_PAGE10_START + CMIS_UPPER_PAGE10_TX_DISABLE_OFFSET + tx_disable = self._read_eeprom_specific_bytes(offset, 1) + + return [bool(int(tx_disable[0],16) & (1 << i)) for i in range(self.NUM_CHANNELS)] + + def get_tx_disable_channel(self): + """ + Retrieves the TX disabled channels in this SFP + + Returns: + A hex of 8 bits (bit 0 to bit 7 as channel 0 to channel 7) to represent + TX channels which have been disabled in this SFP. + As an example, a returned value of 0x5 indicates that channel 0 + and channel 2 have been disabled. + """ + tx_disable_support = self.get_tx_disable_support() + if tx_disable_support is None: + return None + if not tx_disable_support: + return 'N/A' + + offset = CMIS_UPPER_PAGE10_START + CMIS_UPPER_PAGE10_TX_DISABLE_OFFSET + tx_disable = self._read_eeprom_specific_bytes(offset, 1) + return int(tx_disable[0],16) + + def tx_disable(self, tx_disable): + """ + Disable SFP TX for all channels + + Args: + tx_disable : A Boolean, True to enable tx_disable mode, False to disable + tx_disable mode. + + Returns: + A boolean, True if tx_disable is set successfully, False if not + """ + if not self.get_presence(): + return False + + if self.dom_tx_disable_supported: + channel_mask = 0xff + if tx_disable: + return self.tx_disable_channel(channel_mask, True) + else: + return self.tx_disable_channel(channel_mask, False) + else: + return False + + + def tx_disable_channel(self, channel, disable): + """ + Sets the tx_disable for specified SFP channels + + Args: + channel : A hex of 4 bits (bit 0 to bit 3) which represent channel 0 to 3, + e.g. 0x5 for channel 0 and channel 2. + disable : A boolean, True to disable TX channels specified in channel, + False to enable + + Returns: + A boolean, True if successful, False if not + """ + channel_state = self.get_tx_disable_channel() + if channel_state is None or channel_state == 'N/A': + return False + + for i in range(self.NUM_CHANNELS): + mask = (1 << i) + if not (channel & mask): + continue + if disable: + channel_state |= mask + else: + channel_state &= ~mask + offset = CMIS_UPPER_PAGE10_START + CMIS_UPPER_PAGE10_TX_DISABLE_OFFSET + + return self._write_eeprom_specific_bytes(offset, channel_state) + + def set_datapath_deinit(self, channel): + """ + Put the CMIS datapath into the de-initialized state + + Args: + channel: + Integer, a bitmask of the lanes on the host side + e.g. 0x5 for lane 0 and lane 2. + + Returns: + Boolean, true if success otherwise false + """ + offset = CMIS_LOWER_PAGE_START + CMIS_LOWER_PAGE_REVISION_COMPLIANCE_OFFSET + cmis_major = self._read_eeprom_specific_bytes(offset, 1) + + offset = CMIS_UPPER_PAGE10_START + CMIS_UPPER_PAGE10_DATA_PATH_DEINIT_OFFSET + data = self._read_eeprom_specific_bytes(offset, 1) + deinit_value = int(data[0],16) + for lane in range(self.NUM_CHANNELS): + if ((1 << lane) & channel) == 0: + continue + if int(cmis_major[0],16) >= 4: # CMIS v4 onwards + deinit_value |= (1 << lane) + else: # CMIS v3 + deinit_value &= ~(1 << lane) + + return self._write_eeprom_specific_bytes(offset,deinit_value) + + def set_datapath_init(self, channel): + """ + Put the CMIS datapath into the de-initialized state + + Args: + channel: + Integer, a bitmask of the lanes on the host side + e.g. 0x5 for lane 0 and lane 2. + + Returns: + Boolean, true if success otherwise false + """ + offset = CMIS_LOWER_PAGE_START + CMIS_LOWER_PAGE_REVISION_COMPLIANCE_OFFSET + cmis_major = self._read_eeprom_specific_bytes(offset, 1) + + offset = CMIS_UPPER_PAGE10_START + CMIS_UPPER_PAGE10_DATA_PATH_DEINIT_OFFSET + data = self._read_eeprom_specific_bytes(offset, 1) + deinit_value = int(data[0],16) + for lane in range(self.NUM_CHANNELS): + if ((1 << lane) & channel) == 0: + continue + if int(cmis_major[0],16) >= 4: # CMIS v4 onwards + deinit_value &= ~(1 << lane) + else: # CMIS v3 + deinit_value |= (1 << lane) + + return self._write_eeprom_specific_bytes(offset,deinit_value) + + def get_module_state(self): + """ + get the module state + + Args: + Returns: + string in dict MODULE_STATE + """ + offset = CMIS_LOWER_PAGE_START + CMIS_LOWER_PAGE_MODULE_STATE_OFFSET + data = self._read_eeprom_specific_bytes(offset, 1) + state = (int(data[0],16) & 0xe) >> 1 + return MODULE_STATE[state] + + def test_module_state(self, states): + """ + Check if the CMIS module is in the specified state + + Args: + states: + List, a string list of states + + Returns: + Boolean, true if it's in the specified state, otherwise false + """ + return self.get_module_state() in states + + def get_datapath_state(self,channel): + """ + get the channel datapath state + + Args: + channel: + Integer, a bitmask of the lanes on the host side + e.g. 0x5 for lane 0 and lane 2. + Returns: + string in dict DATAPATH_STATE + """ + state = '' + for lane in range(self.NUM_CHANNELS): + if ((1 << lane) & channel) == 0: + continue + offset = CMIS_UPPER_PAGE11_START + CMIS_UPPER_PAGE11_DATA_PATH_STATE_OFFSET + (lane//2) + data = self._read_eeprom_specific_bytes(offset, 1) + tmp_data = int(data[0],16) + if tmp_data % 2: + state = tmp_data >> 4 + else: + state = tmp_data & 0xf + return DATAPATH_STATE[state] + + def test_datapath_state(self, channel, states): + """ + Check if the CMIS datapath states are in the specified state + + Args: + channel: + Integer, a bitmask of the lanes on the host side + e.g. 0x5 for lane 0 and lane 2. + states: + List, a string list of states + + Returns: + Boolean, true if all lanes are in the specified state, otherwise false + """ + return self.get_datapath_state(channel) in states + + def set_application(self, channel, appl_code): + """ + Update the selected application code to the specified lanes on the host side + + Args: + channel: + Integer, a bitmask of the lanes on the host side + e.g. 0x5 for lane 0 and lane 2. + appl_code: + Integer, the desired application code + + Returns: + Boolean, true if success otherwise false + """ + # Update the application selection + lane_first = -1 + for lane in range(self.NUM_CHANNELS): + if ((1 << lane) & channel) == 0: + continue + if lane_first < 0: + lane_first = lane + offset = CMIS_UPPER_PAGE10_START + CMIS_UPPER_PAGE10_STAGED0_APSEL_CONTROL_OFFSET + lane + data = (appl_code << 4) | (lane_first << 1) + self._write_eeprom_specific_bytes(offset, data) + + offset = CMIS_UPPER_PAGE10_START + CMIS_UPPER_PAGE10_STAGED0_APPLY_DATA_PATH_INIT_OFFSET + #data = (appl_code << 4) | (lane_first << 1) + # Apply DataPathInit + return self._write_eeprom_specific_bytes(offset, channel) + + def get_config_datapath_hostlane_status(self,channel): + """ + return configuration command execution result status for + the datapath of each host lane + + Args: + channel: + Integer, a bitmask of the lanes on the host side + e.g. 0x5 for lane 0 and lane 2. + Returns: + string in dict CONFIG_STATUS + """ + state = '' + for lane in range(self.NUM_CHANNELS): + if ((1 << lane) & channel) == 0: + continue + offset = CMIS_UPPER_PAGE11_START + CMIS_UPPER_PAGE11_CONFIG_ERROR_CODE_OFFSET + (lane//2) + data = self._read_eeprom_specific_bytes(offset, 1) + tmp_data = int(data[0],16) + if tmp_data % 2: + state = tmp_data >> 4 + else: + state = tmp_data & 0xf + return CONFIG_STATUS[state] + + def test_config_error(self, channel, states): + """ + Check if the CMIS configuration states are in the specified state + + Args: + channel: + Integer, a bitmask of the lanes on the host side + e.g. 0x5 for lane 0 and lane 2. + states: + List, a string list of states + + Returns: + Boolean, true if all lanes are in the specified state, otherwise false + """ + return self.get_config_datapath_hostlane_status(channel) in states + + def reset_cmis_init(self,retries): + self.cmis_state = CMIS_STATE_INSERTED + self.cmis_retries = retries + self.cmis_expired = None # No expiration + + def init_sequence(self): + host_lanes = 0xff + retries = self.cmis_retries + expired = self.cmis_expired + now = datetime.datetime.now() + self.cmis_state = CMIS_STATE_INSERTED + + while self.cmis_state is not CMIS_STATE_READY: + # CMIS state transitions + if self.cmis_state == CMIS_STATE_INSERTED: + """ + appl = self.get_cmis_application_desired( host_lanes, host_speed) + if appl < 1: + logging.errorr("port {}: no suitable app for the port".format(self.__index)) + self.cmis_state = CMIS_STATE_FAILED + continue + + has_update = self.is_cmis_application_update_required(host_lanes, host_speed) + if not has_update: + # No application updates + logging.error("port {}: READY".format(self.__index)) + self.cmis_state = CMIS_STATE_READY + continue + """ + # D.2.2 Software Deinitialization + self.set_datapath_deinit(host_lanes) + self.set_lpmode(True) + if not self.test_module_state(['ModuleReady', 'ModuleLowPwr']): + logging.error("port {}: unable to enter low-power mode".format(self.__index)) + self.cmis_retries = retries + 1 + continue + + # D.1.3 Software Configuration and Initialization + if not self.tx_disable_channel(host_lanes, True): + logging.error("port {}: unable to turn off tx power".format(self.__index)) + self.cmis_retries = retries + 1 + continue + self.set_lpmode(False) + + # TODO: Use fine grained time when the CMIS memory map is available + self.cmis_expired = now + datetime.timedelta(seconds=CMIS_DEF_EXPIRED) + self.cmis_state = CMIS_STATE_DP_DEINIT + elif self.cmis_state == CMIS_STATE_DP_DEINIT: + if not self.test_module_state(['ModuleReady']): + if (expired is not None) and (expired <= now): + logging.error("port {}: timeout for 'ModuleReady'".format(self.__index)) + self.reset_cmis_init(retries + 1) + continue + if not self.test_datapath_state(host_lanes, ['DataPathDeinit', 'DataPathDeactivated']): + if (expired is not None) and (expired <= now): + logging.error("port {}: timeout for 'DataPathDeinit'".format(self.__index)) + self.reset_cmis_init(retries + 1) + continue + + """ + # D.1.3 Software Configuration and Initialization + appl = self.get_cmis_application_desired(host_lanes, host_speed) + if appl < 1: + logging.error("port {}: no suitable app for the port".format(self.__index)) + self.cmis_state = CMIS_STATE_FAILED + continue + if not self.set_application(host_lanes, appl): + """ + if not self.set_application(host_lanes, 1): + logging.error("{}: unable to set application".format(self.__index)) + self.reset_cmis_init(retries + 1) + continue + + # TODO: Use fine grained time when the CMIS memory map is available + self.cmis_expired = now + datetime.timedelta(seconds=CMIS_DEF_EXPIRED) + self.cmis_state = CMIS_STATE_AP_CONF + elif self.cmis_state == CMIS_STATE_AP_CONF: + if not self.test_config_error(host_lanes, ['ConfigSuccess']): + if (expired is not None) and (expired <= now): + logging.error("port {}: timeout for 'ConfigSuccess'".format(self.__index)) + self.reset_cmis_init(retries + 1) + continue + + # D.1.3 Software Configuration and Initialization + self.set_datapath_init(host_lanes) + + # TODO: Use fine grained time when the CMIS memory map is available + self.cmis_expired = now + datetime.timedelta(seconds=CMIS_DEF_EXPIRED) + self.cmis_state = CMIS_STATE_DP_INIT + elif self.cmis_state == CMIS_STATE_DP_INIT: + if not self.test_datapath_state(host_lanes, ['DataPathInitialized']): + if (expired is not None) and (expired <= now): + logging.error("port {}: timeout for 'DataPathInitialized'".format(self.__index)) + self.reset_cmis_init(retries + 1) + continue + + # D.1.3 Software Configuration and Initialization + if not self.tx_disable_channel(host_lanes, False): + logging.error("port {}: unable to turn on tx power".format(self.__index)) + self.reset_cmis_init(retries + 1) + continue + + # TODO: Use fine grained timeout when the CMIS memory map is available + self.cmis_expired = now + datetime.timedelta(seconds=CMIS_DEF_EXPIRED) + self.cmis_state = CMIS_STATE_DP_TXON + elif self.cmis_state == CMIS_STATE_DP_TXON: + if not self.test_datapath_state(host_lanes, ['DataPathActivated']): + if (expired is not None) and (expired <= now): + logging.error("port {}: timeout for 'DataPathActivated'".format(self.__index)) + self.reset_cmis_init(retries + 1) + continue + logging.error("port {}: READY".format(self.__index)) + self.cmis_state = CMIS_STATE_READY + return self.cmis_state + + + def set_lpmode(self, lpmode): + """ + Sets the lpmode (low power mode) of SFP + + Args: + lpmode: A Boolean, True to enable lpmode, False to disable it + Note : lpmode can be overridden by set_power_override + + Returns: + A boolean, True if lpmode is set successfully, False if not + """ + try: + reg_file = open(self.__lpmode_attr, "r+") + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False + + #reg_value = int(reg_file.readline().rstrip(),16) + + if lpmode is True: + reg_value = 1 + else: + reg_value = 0 + + reg_file.write(hex(reg_value)) + reg_file.close() + + return True + + def set_power_override(self, power_override, power_set): + """ + Sets SFP power level using power_override and power_set + + Args: + power_override : + A Boolean, True to override set_lpmode and use power_set + to control SFP power, False to disable SFP power control + through power_override/power_set and use set_lpmode + to control SFP power. + power_set : + Only valid when power_override is True. + A Boolean, True to set SFP to low power mode, False to set + SFP to high power mode. + + Returns: + A boolean, True if power-override and power_set are set successfully, + False if not + """ + return True + diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/sfp.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/sfp.py new file mode 100755 index 000000000000..1fef3a759e2e --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/sfp.py @@ -0,0 +1,790 @@ +#!/usr/bin/env python +# +# Name: sfp.py, version: 1.0 +# +# Description: Module contains the definitions of SONiC platform APIs +# + +try: + import os + #import sys + from sonic_platform_base.sfp_base import SfpBase + from sonic_platform_base.sonic_sfp.sff8472 import sff8472Dom + from sonic_platform_base.sonic_sfp.sff8472 import sff8472InterfaceId + from sonic_platform_base.sonic_sfp.sff8472 import sffbase + from sonic_platform_base.sonic_sfp.sfputilhelper import SfpUtilHelper + from .helper import APIHelper +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +INFO_OFFSET = 0 +DOM_OFFSET = 256 + +XCVR_INTFACE_BULK_OFFSET = 0 +XCVR_INTFACE_BULK_WIDTH_SFP = 21 +XCVR_VENDOR_NAME_OFFSET = 20 +XCVR_VENDOR_NAME_WIDTH = 16 +XCVR_VENDOR_OUI_OFFSET = 37 +XCVR_VENDOR_OUI_WIDTH = 3 +XCVR_VENDOR_PN_OFFSET = 40 +XCVR_VENDOR_PN_WIDTH = 16 +XCVR_HW_REV_OFFSET = 56 +XCVR_HW_REV_WIDTH_SFP = 4 +XCVR_VENDOR_SN_OFFSET = 68 +XCVR_VENDOR_SN_WIDTH = 16 +XCVR_VENDOR_DATE_OFFSET = 84 +XCVR_VENDOR_DATE_WIDTH = 8 +XCVR_DOM_CAPABILITY_OFFSET = 92 +XCVR_DOM_CAPABILITY_WIDTH = 1 + +# Offset for values in SFP eeprom +SFP_TEMPE_OFFSET = 96 +SFP_TEMPE_WIDTH = 2 +SFP_VOLT_OFFSET = 98 +SFP_VOLT_WIDTH = 2 +SFP_CHANNL_MON_OFFSET = 100 +SFP_CHANNL_MON_WIDTH = 6 +SFP_MODULE_THRESHOLD_OFFSET = 0 +SFP_MODULE_THRESHOLD_WIDTH = 40 +SFP_CHANNL_THRESHOLD_OFFSET = 112 +SFP_CHANNL_THRESHOLD_WIDTH = 2 +SFP_STATUS_CONTROL_OFFSET = 110 +SFP_STATUS_CONTROL_WIDTH = 1 +SFP_TX_DISABLE_HARD_BIT = 7 +SFP_TX_DISABLE_SOFT_BIT = 6 + +TRANSCEIVER_PATH = '/sys/switch/transceiver/' + +class Sfp(SfpBase): + + port_start = 0 + port_end = 55 + + cplda_sfp_num = 0 + cpldb_sfp_num = 28 + cpldc_sfp_num = 28 + __port_to_i2c_mapping = {} + def __init__(self, index): + self.__index = index + + self.__platform = "x86_64-pegatron_fn8656-bnf-r0" + self.__hwsku = "fn8656-bnf" + self.__attr_path_prefix = '/sys/switch/transceiver/eth{}/'.format(self.__index+1) + + self.__api_helper = APIHelper() + self.__presence_attr = None + self.__eeprom_path = None + + if self.__index in range(0, self.port_end + 1): + self.__eeprom_path = TRANSCEIVER_PATH+'eth{}/'.format(self.__index+1)+'eeprom' + self.__presence_attr = TRANSCEIVER_PATH+'eth{}/'.format(self.__index+1)+'present' + self.__reset_attr = TRANSCEIVER_PATH+'eth{}/'.format(self.__index+1)+'reset' + self.__lpmode_attr = TRANSCEIVER_PATH+'eth{}/'.format(self.__index+1)+'lpmode' + + SfpBase.__init__(self) + + def __get_attr_value(self, attr_path): + + retval = 'ERR' + if (not os.path.isfile(attr_path)): + return retval + + try: + with open(attr_path, 'r') as fd: + retval = fd.read() + except Exception as error: + logging.error("Unable to open ", attr_path, " file !") + + retval = retval.rstrip(' \t\n\r') + return retval + + def __is_host(self): + return os.system("docker > /dev/null 2>&1") == 0 + + def __get_path_to_port_config_file(self): + host_platform_root_path = '/usr/share/sonic/device' + docker_hwsku_path = '/usr/share/sonic/hwsku' + + host_platform_path = "/".join([host_platform_root_path, self.__platform]) + hwsku_path = "/".join([host_platform_path, self.__hwsku]) if self.__is_host() else docker_hwsku_path + + return "/".join([hwsku_path, "port_config.ini"]) + + def __read_eeprom_specific_bytes(self, offset, num_bytes): + sysfsfile_eeprom = None + eeprom_raw = [] + + for i in range(0, num_bytes): + eeprom_raw.append("0x00") + + sysfs_eeprom_path = self.__eeprom_path + try: + sysfsfile_eeprom = open(sysfs_eeprom_path, mode="rb", buffering=0) + sysfsfile_eeprom.seek(offset) + raw = sysfsfile_eeprom.read(num_bytes) + raw_len = len(raw) + for n in range(0, raw_len): + eeprom_raw[n] = hex(raw[n])[2:].zfill(2) + except BaseException: + pass + finally: + if sysfsfile_eeprom: + sysfsfile_eeprom.close() + + return eeprom_raw + + def __convert_string_to_num(self, value_str): + if "-inf" in value_str: + return 'N/A' + elif "Unknown" in value_str: + return 'N/A' + elif 'dBm' in value_str: + t_str = value_str.rstrip('dBm') + return float(t_str) + elif 'mA' in value_str: + t_str = value_str.rstrip('mA') + return float(t_str) + elif 'C' in value_str: + t_str = value_str.rstrip('C') + return float(t_str) + elif 'Volts' in value_str: + t_str = value_str.rstrip('Volts') + return float(t_str) + else: + return 'N/A' + +############################################## +# Device methods +############################################## + + def get_name(self): + """ + Retrieves the name of the device + + Returns: + string: The name of the device + """ + name = None + + sfputil_helper = SfpUtilHelper() + sfputil_helper.read_porttab_mappings(self.__get_path_to_port_config_file()) + name = sfputil_helper.logical[self.__index] or "Unknown" + return name + + def get_presence(self): + """ + Retrieves the presence of the device + + Returns: + bool: True if device is present, False if not + """ + presence = False + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'present') + if (attr_rv != None): + attr_rv = int(attr_rv, 16) + if ((attr_rv & 0x1) == 1): + presence = True + return presence + + def get_all_presence(self): + """ + Retrieves the presence of the device + + Returns: + bool: True if device is present, False if not + """ + + attr_rv = self.__api_helper.read_one_line_file(TRANSCEIVER_PATH + 'present') + if (attr_rv != None): + attr_rv = int(attr_rv, 16) + return attr_rv + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + + Returns: + string: Model/part number of device + """ + transceiver_info_dict = self.get_transceiver_info() + return transceiver_info_dict.get("modelname", "N/A") + + def get_serial(self): + """ + Retrieves the serial number of the device + + Returns: + string: Serial number of device + """ + transceiver_info_dict = self.get_transceiver_info() + return transceiver_info_dict.get("serialnum", "N/A") + + def get_status(self): + """ + Retrieves the operational status of the device + + Returns: + A boolean value, True if device is operating properly, False if not + """ + return self.get_presence() and not self.get_reset_status() + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return self.__index + + def is_replaceable(self): + """ + Indicate whether Fan is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True + +############################################## +# SFP methods +############################################## + + def get_transceiver_info(self): + """ + Retrieves transceiver info of this SFP + + Returns: + A dict which contains following keys/values : + ======================================================================== + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + type |1*255VCHAR |type of SFP + hardware_rev |1*255VCHAR |hardware version of SFP + serialnum |1*255VCHAR |serial number of the SFP + manufacturename |1*255VCHAR |SFP vendor name + modelname |1*255VCHAR |SFP model name + Connector |1*255VCHAR |connector information + encoding |1*255VCHAR |encoding information + ext_identifier |1*255VCHAR |extend identifier + ext_rateselect_compliance |1*255VCHAR |extended rateSelect compliance + cable_length |INT |cable length in m + mominal_bit_rate |INT |nominal bit rate by 100Mbs + specification_compliance |1*255VCHAR |specification compliance + vendor_date |1*255VCHAR |vendor date + vendor_oui |1*255VCHAR |vendor OUI + ======================================================================== + """ + + transceiver_info_dict_keys = ['type', 'hardware_rev', + 'serial', 'manufacturer', + 'model', 'connector', + 'encoding', 'ext_identifier', + 'ext_rateselect_compliance', 'cable_type', + 'cable_length', 'nominal_bit_rate', + 'specification_compliance', 'vendor_date', + 'vendor_oui'] + + sfp_cable_length_tup = ('LengthSMFkm-UnitsOfKm', 'LengthSMF(UnitsOf100m)', + 'Length50um(UnitsOf10m)', 'Length62.5um(UnitsOfm)', + 'LengthCable(UnitsOfm)', 'LengthOM3(UnitsOf10m)') + + sfp_compliance_code_tup = ('10GEthernetComplianceCode', 'InfinibandComplianceCode', + 'ESCONComplianceCodes', 'SONETComplianceCodes', + 'EthernetComplianceCodes', 'FibreChannelLinkLength', + 'FibreChannelTechnology', 'SFP+CableTechnology', + 'FibreChannelTransmissionMedia', 'FibreChannelSpeed') + + sfpi_obj = sff8472InterfaceId() + if not self.get_presence() or not sfpi_obj: + return {} + + offset = INFO_OFFSET + + sfp_interface_bulk_raw = self.__read_eeprom_specific_bytes((offset + XCVR_INTFACE_BULK_OFFSET), XCVR_INTFACE_BULK_WIDTH_SFP) + sfp_interface_bulk_data = sfpi_obj.parse_sfp_info_bulk(sfp_interface_bulk_raw, 0) + + sfp_vendor_name_raw = self.__read_eeprom_specific_bytes((offset + XCVR_VENDOR_NAME_OFFSET), XCVR_VENDOR_NAME_WIDTH) + sfp_vendor_name_data = sfpi_obj.parse_vendor_name(sfp_vendor_name_raw, 0) + + sfp_vendor_pn_raw = self.__read_eeprom_specific_bytes((offset + XCVR_VENDOR_PN_OFFSET), XCVR_VENDOR_PN_WIDTH) + sfp_vendor_pn_data = sfpi_obj.parse_vendor_pn(sfp_vendor_pn_raw, 0) + + sfp_vendor_rev_raw = self.__read_eeprom_specific_bytes((offset + XCVR_HW_REV_OFFSET), XCVR_HW_REV_WIDTH_SFP) + sfp_vendor_rev_data = sfpi_obj.parse_vendor_rev(sfp_vendor_rev_raw, 0) + + sfp_vendor_sn_raw = self.__read_eeprom_specific_bytes((offset + XCVR_VENDOR_SN_OFFSET), XCVR_VENDOR_SN_WIDTH) + sfp_vendor_sn_data = sfpi_obj.parse_vendor_sn(sfp_vendor_sn_raw, 0) + + sfp_vendor_oui_raw = self.__read_eeprom_specific_bytes((offset + XCVR_VENDOR_OUI_OFFSET), XCVR_VENDOR_OUI_WIDTH) + if sfp_vendor_oui_raw is not None: + sfp_vendor_oui_data = sfpi_obj.parse_vendor_oui(sfp_vendor_oui_raw, 0) + + sfp_vendor_date_raw = self.__read_eeprom_specific_bytes((offset + XCVR_VENDOR_DATE_OFFSET), XCVR_VENDOR_DATE_WIDTH) + sfp_vendor_date_data = sfpi_obj.parse_vendor_date(sfp_vendor_date_raw, 0) + + transceiver_info_dict = dict.fromkeys(transceiver_info_dict_keys, 'N/A') + + if sfp_interface_bulk_data: + transceiver_info_dict['type'] = sfp_interface_bulk_data['data']['type']['value'] + transceiver_info_dict['connector'] = sfp_interface_bulk_data['data']['Connector']['value'] + transceiver_info_dict['encoding'] = sfp_interface_bulk_data['data']['EncodingCodes']['value'] + transceiver_info_dict['ext_identifier'] = sfp_interface_bulk_data['data']['Extended Identifier']['value'] + transceiver_info_dict['ext_rateselect_compliance'] = sfp_interface_bulk_data['data']['RateIdentifier']['value'] + transceiver_info_dict['type_abbrv_name'] = sfp_interface_bulk_data['data']['type_abbrv_name']['value'] + transceiver_info_dict['nominal_bit_rate'] = str(sfp_interface_bulk_data['data']['NominalSignallingRate(UnitsOf100Mbd)']['value']) + + transceiver_info_dict['manufacturer'] = sfp_vendor_name_data['data']['Vendor Name']['value'] if sfp_vendor_name_data else 'N/A' + transceiver_info_dict['model'] = sfp_vendor_pn_data['data']['Vendor PN']['value'] if sfp_vendor_pn_data else 'N/A' + transceiver_info_dict['hardware_rev'] = sfp_vendor_rev_data['data']['Vendor Rev']['value'] if sfp_vendor_rev_data else 'N/A' + transceiver_info_dict['serial'] = sfp_vendor_sn_data['data']['Vendor SN']['value'] if sfp_vendor_sn_data else 'N/A' + transceiver_info_dict['vendor_oui'] = sfp_vendor_oui_data['data']['Vendor OUI']['value'] if sfp_vendor_oui_data else 'N/A' + transceiver_info_dict['vendor_date'] = sfp_vendor_date_data['data']['VendorDataCode(YYYY-MM-DD Lot)']['value'] if sfp_vendor_date_data else 'N/A' + + transceiver_info_dict['cable_type'] = "Unknown" + transceiver_info_dict['cable_length'] = "Unknown" + for key in sfp_cable_length_tup: + if key in sfp_interface_bulk_data['data']: + transceiver_info_dict['cable_type'] = key + transceiver_info_dict['cable_length'] = str(sfp_interface_bulk_data['data'][key]['value']) + + compliance_code_dict = dict() + for key in sfp_compliance_code_tup: + if key in sfp_interface_bulk_data['data']['Specification compliance']['value']: + compliance_code_dict[key] = sfp_interface_bulk_data['data']['Specification compliance']['value'][key]['value'] + + transceiver_info_dict['specification_compliance'] = str(compliance_code_dict) + + return transceiver_info_dict + + def get_transceiver_bulk_status(self): + """ + Retrieves transceiver bulk status of this SFP + + Returns: + A dict which contains following keys/values : + ======================================================================== + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + rx_los |BOOLEAN |RX loss-of-signal status, True if has RX los, False if not. + tx_fault |BOOLEAN |TX fault status, True if has TX fault, False if not. + reset_status |BOOLEAN |reset status, True if SFP in reset, False if not. + lp_mode |BOOLEAN |low power mode status, True in lp mode, False if not. + tx_disable |BOOLEAN |TX disable status, True TX disabled, False if not. + tx_disabled_channel |HEX |disabled TX channels in hex, bits 0 to 3 represent channel 0 + | |to channel 3. + temperature |INT |module temperature in Celsius + voltage |INT |supply voltage in mV + txbias |INT |TX Bias Current in mA, n is the channel number, + | |for example, tx2bias stands for tx bias of channel 2. + rxpower |INT |received optical power in mW, n is the channel number, + | |for example, rx2power stands for rx power of channel 2. + txpower |INT |TX output power in mW, n is the channel number, + | |for example, tx2power stands for tx power of channel 2. + ======================================================================== + """ + transceiver_dom_info_dict_keys = ['rx_los', 'tx_fault', + 'reset_status', 'power_lpmode', + 'tx_disable', 'tx_disable_channel', + 'temperature', 'voltage', + 'rx1power', 'rx2power', + 'rx3power', 'rx4power', + 'tx1bias', 'tx2bias', + 'tx3bias', 'tx4bias', + 'tx1power', 'tx2power', + 'tx3power', 'tx4power'] + + sfpd_obj = sff8472Dom() + if not self.get_presence() or not sfpd_obj: + return {} + + sysfsfile_eeprom = None + #eeprom_raw = [] + bdata='' + sysfsfile_eeprom = open(self.__eeprom_path, mode="rb", buffering=0) + bdata = sysfsfile_eeprom.read() + if len(bdata) <= 256: + sysfsfile_eeprom.close() + return None + sysfsfile_eeprom.close() + + eeprom_ifraw = self.__read_eeprom_specific_bytes(0, DOM_OFFSET) + sfpi_obj = sff8472InterfaceId(eeprom_ifraw) + cal_type = sfpi_obj.get_calibration_type() + sfpd_obj._calibration_type = cal_type + + offset = DOM_OFFSET + transceiver_dom_info_dict = dict.fromkeys(transceiver_dom_info_dict_keys, 'N/A') + + dom_temperature_raw = self.__read_eeprom_specific_bytes((offset + SFP_TEMPE_OFFSET), SFP_TEMPE_WIDTH) + + if dom_temperature_raw is not None: + dom_temperature_data = sfpd_obj.parse_temperature(dom_temperature_raw, 0) + transceiver_dom_info_dict['temperature'] = dom_temperature_data['data']['Temperature']['value'] + + dom_voltage_raw = self.__read_eeprom_specific_bytes((offset + SFP_VOLT_OFFSET), SFP_VOLT_WIDTH) + if dom_voltage_raw is not None: + dom_voltage_data = sfpd_obj.parse_voltage(dom_voltage_raw, 0) + transceiver_dom_info_dict['voltage'] = dom_voltage_data['data']['Vcc']['value'] + + dom_channel_monitor_raw = self.__read_eeprom_specific_bytes((offset + SFP_CHANNL_MON_OFFSET), SFP_CHANNL_MON_WIDTH) + if dom_channel_monitor_raw is not None: + dom_voltage_data = sfpd_obj.parse_channel_monitor_params(dom_channel_monitor_raw, 0) + transceiver_dom_info_dict['tx1power'] = dom_voltage_data['data']['TXPower']['value'] + transceiver_dom_info_dict['rx1power'] = dom_voltage_data['data']['RXPower']['value'] + transceiver_dom_info_dict['tx1bias'] = dom_voltage_data['data']['TXBias']['value'] + + for key in transceiver_dom_info_dict: + transceiver_dom_info_dict[key] = self.__convert_string_to_num(transceiver_dom_info_dict[key]) + + transceiver_dom_info_dict['reset_status'] = self.get_reset_status() + transceiver_dom_info_dict['rx_los'] = self.get_rx_los() + transceiver_dom_info_dict['tx_fault'] = self.get_tx_fault() + transceiver_dom_info_dict['tx_disable'] = self.get_tx_disable() + transceiver_dom_info_dict['tx_disable_channel'] = self.get_tx_disable_channel() + transceiver_dom_info_dict['lp_mode'] = self.get_lpmode() + + return transceiver_dom_info_dict + + def get_transceiver_threshold_info(self): + """ + Retrieves transceiver threshold info of this SFP + + Returns: + A dict which contains following keys/values : + ======================================================================== + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + temphighalarm |FLOAT |High Alarm Threshold value of temperature in Celsius. + templowalarm |FLOAT |Low Alarm Threshold value of temperature in Celsius. + temphighwarning |FLOAT |High Warning Threshold value of temperature in Celsius. + templowwarning |FLOAT |Low Warning Threshold value of temperature in Celsius. + vcchighalarm |FLOAT |High Alarm Threshold value of supply voltage in mV. + vcclowalarm |FLOAT |Low Alarm Threshold value of supply voltage in mV. + vcchighwarning |FLOAT |High Warning Threshold value of supply voltage in mV. + vcclowwarning |FLOAT |Low Warning Threshold value of supply voltage in mV. + rxpowerhighalarm |FLOAT |High Alarm Threshold value of received power in dBm. + rxpowerlowalarm |FLOAT |Low Alarm Threshold value of received power in dBm. + rxpowerhighwarning |FLOAT |High Warning Threshold value of received power in dBm. + rxpowerlowwarning |FLOAT |Low Warning Threshold value of received power in dBm. + txpowerhighalarm |FLOAT |High Alarm Threshold value of transmit power in dBm. + txpowerlowalarm |FLOAT |Low Alarm Threshold value of transmit power in dBm. + txpowerhighwarning |FLOAT |High Warning Threshold value of transmit power in dBm. + txpowerlowwarning |FLOAT |Low Warning Threshold value of transmit power in dBm. + txbiashighalarm |FLOAT |High Alarm Threshold value of tx Bias Current in mA. + txbiaslowalarm |FLOAT |Low Alarm Threshold value of tx Bias Current in mA. + txbiashighwarning |FLOAT |High Warning Threshold value of tx Bias Current in mA. + txbiaslowwarning |FLOAT |Low Warning Threshold value of tx Bias Current in mA. + ======================================================================== + """ + transceiver_dom_threshold_info_dict_keys = ['temphighalarm', 'temphighwarning', 'templowalarm', 'templowwarning', + 'vcchighalarm', 'vcchighwarning', 'vcclowalarm', 'vcclowwarning', + 'rxpowerhighalarm', 'rxpowerhighwarning', 'rxpowerlowalarm', 'rxpowerlowwarning', + 'txpowerhighalarm', 'txpowerhighwarning', 'txpowerlowalarm', 'txpowerlowwarning', + 'txbiashighalarm', 'txbiashighwarning', 'txbiaslowalarm', 'txbiaslowwarning'] + + sfpd_obj = sff8472Dom() + + if not self.get_presence() and not sfpd_obj: + return {} + + sysfsfile_eeprom = None + #eeprom_raw = [] + bdata='' + sysfsfile_eeprom = open(self.__eeprom_path, mode="rb", buffering=0) + bdata = sysfsfile_eeprom.read() + if len(bdata) <= 256: + sysfsfile_eeprom.close() + return None + sysfsfile_eeprom.close() + + eeprom_ifraw = self.__read_eeprom_specific_bytes(0, DOM_OFFSET) + sfpi_obj = sff8472InterfaceId(eeprom_ifraw) + cal_type = sfpi_obj.get_calibration_type() + sfpd_obj._calibration_type = cal_type + + offset = DOM_OFFSET + transceiver_dom_threshold_info_dict = dict.fromkeys(transceiver_dom_threshold_info_dict_keys, 'N/A') + dom_module_threshold_raw = self.__read_eeprom_specific_bytes((offset + SFP_MODULE_THRESHOLD_OFFSET), SFP_MODULE_THRESHOLD_WIDTH) + if dom_module_threshold_raw is not None: + dom_module_threshold_data = sfpd_obj.parse_alarm_warning_threshold(dom_module_threshold_raw, 0) + + transceiver_dom_threshold_info_dict['temphighalarm'] = dom_module_threshold_data['data']['TempHighAlarm']['value'] + transceiver_dom_threshold_info_dict['templowalarm'] = dom_module_threshold_data['data']['TempLowAlarm']['value'] + transceiver_dom_threshold_info_dict['temphighwarning'] = dom_module_threshold_data['data']['TempHighWarning']['value'] + transceiver_dom_threshold_info_dict['templowwarning'] = dom_module_threshold_data['data']['TempLowWarning']['value'] + + transceiver_dom_threshold_info_dict['vcchighalarm'] = dom_module_threshold_data['data']['VoltageHighAlarm']['value'] + transceiver_dom_threshold_info_dict['vcclowalarm'] = dom_module_threshold_data['data']['VoltageLowAlarm']['value'] + transceiver_dom_threshold_info_dict['vcchighwarning'] = dom_module_threshold_data['data']['VoltageHighWarning']['value'] + transceiver_dom_threshold_info_dict['vcclowwarning'] = dom_module_threshold_data['data']['VoltageLowWarning']['value'] + + transceiver_dom_threshold_info_dict['txbiashighalarm'] = dom_module_threshold_data['data']['BiasHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiaslowalarm'] = dom_module_threshold_data['data']['BiasLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiashighwarning'] = dom_module_threshold_data['data']['BiasHighWarning']['value'] + transceiver_dom_threshold_info_dict['txbiaslowwarning'] = dom_module_threshold_data['data']['BiasLowWarning']['value'] + + transceiver_dom_threshold_info_dict['txpowerhighalarm'] = dom_module_threshold_data['data']['TXPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerlowalarm'] = dom_module_threshold_data['data']['TXPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerhighwarning'] = dom_module_threshold_data['data']['TXPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerlowwarning'] = dom_module_threshold_data['data']['TXPowerLowWarning']['value'] + + transceiver_dom_threshold_info_dict['rxpowerhighalarm'] = dom_module_threshold_data['data']['RXPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowalarm'] = dom_module_threshold_data['data']['RXPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerhighwarning'] = dom_module_threshold_data['data']['RXPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowwarning'] = dom_module_threshold_data['data']['RXPowerLowWarning']['value'] + + for key in transceiver_dom_threshold_info_dict: + transceiver_dom_threshold_info_dict[key] = self.__convert_string_to_num(transceiver_dom_threshold_info_dict[key]) + + return transceiver_dom_threshold_info_dict + + def get_reset_status(self): + """ + Retrieves the reset status of SFP + + Returns: + A Boolean, True if reset enabled, False if disabled + """ + # SFP doesn't support this feature + return False + + def get_rx_los(self): + """ + Retrieves the RX LOS (lost-of-signal) status of SFP + + Returns: + A Boolean, True if SFP has RX LOS, False if not. + Note : RX LOS status is latched until a call to get_rx_los or a reset. + """ + rx_los = False + status_control_raw = self.__read_eeprom_specific_bytes(SFP_STATUS_CONTROL_OFFSET, SFP_STATUS_CONTROL_WIDTH) + if status_control_raw: + data = int(status_control_raw[0], 16) + rx_los = (sffbase().test_bit(data, 1) != 0) + + return rx_los + + def get_tx_fault(self): + """ + Retrieves the TX fault status of SFP + + Returns: + A Boolean, True if SFP has TX fault, False if not + Note : TX fault status is lached until a call to get_tx_fault or a reset. + """ + tx_fault = False + status_control_raw = self.__read_eeprom_specific_bytes(SFP_STATUS_CONTROL_OFFSET, SFP_STATUS_CONTROL_WIDTH) + if status_control_raw: + data = int(status_control_raw[0], 16) + tx_fault = (sffbase().test_bit(data, 2) != 0) + + return tx_fault + + def get_tx_disable(self): + """ + Retrieves the tx_disable status of this SFP + + Returns: + A Boolean, True if tx_disable is enabled, False if disabled + """ + tx_disable = False + #tx_fault = False + status_control_raw = self.__read_eeprom_specific_bytes(SFP_STATUS_CONTROL_OFFSET, SFP_STATUS_CONTROL_WIDTH) + if status_control_raw: + data = int(status_control_raw[0], 16) + tx_disable_hard = (sffbase().test_bit(data, SFP_TX_DISABLE_HARD_BIT) != 0) + tx_disable_soft = (sffbase().test_bit(data, SFP_TX_DISABLE_SOFT_BIT) != 0) + tx_disable = tx_disable_hard | tx_disable_soft + + return tx_disable + + def get_tx_disable_channel(self): + """ + Retrieves the TX disabled channels in this SFP + + Returns: + A hex of 4 bits (bit 0 to bit 3 as channel 0 to channel 3) to represent + TX channels which have been disabled in this SFP. + As an example, a returned value of 0x5 indicates that channel 0 + and channel 2 have been disabled. + """ + # SFP doesn't support this feature + return 0 + + def get_lpmode(self): + """ + Retrieves the lpmode (low power mode) status of this SFP + + Returns: + A Boolean, True if lpmode is enabled, False if disabled + """ + # SFP doesn't support this feature + return False + + def get_power_override(self): + """ + Retrieves the power-override status of this SFP + + Returns: + A Boolean, True if power-override is enabled, False if disabled + """ + # SFP doesn't support this feature + return False + + def get_temperature(self): + """ + Retrieves the temperature of this SFP + + Returns: + An integer number of current temperature in Celsius + """ + transceiver_dom_info_dict = self.get_transceiver_bulk_status() + return transceiver_dom_info_dict.get("temperature", "N/A") + + + def get_voltage(self): + """ + Retrieves the supply voltage of this SFP + + Returns: + An integer number of supply voltage in mV + """ + transceiver_dom_info_dict = self.get_transceiver_bulk_status() + return transceiver_dom_info_dict.get("voltage", "N/A") + + def get_tx_bias(self): + """ + Retrieves the TX bias current of this SFP + + Returns: + A list of four integer numbers, representing TX bias in mA + for channel 0 to channel 4. + Ex. ['110.09', '111.12', '108.21', '112.09'] + """ + transceiver_dom_info_dict = self.get_transceiver_bulk_status() + tx1_bs = transceiver_dom_info_dict.get("tx1bias", "N/A") + return [tx1_bs, "N/A", "N/A", "N/A"] if transceiver_dom_info_dict else [] + + def get_rx_power(self): + """ + Retrieves the received optical power for this SFP + + Returns: + A list of four integer numbers, representing received optical + power in mW for channel 0 to channel 4. + Ex. ['1.77', '1.71', '1.68', '1.70'] + """ + transceiver_dom_info_dict = self.get_transceiver_bulk_status() + rx1_pw = transceiver_dom_info_dict.get("rx1power", "N/A") + return [rx1_pw, "N/A", "N/A", "N/A"] if transceiver_dom_info_dict else [] + + def get_tx_power(self): + """ + Retrieves the TX power of this SFP + + Returns: + A list of four integer numbers, representing TX power in mW + for channel 0 to channel 4. + Ex. ['1.86', '1.86', '1.86', '1.86'] + """ + transceiver_dom_info_dict = self.get_transceiver_bulk_status() + tx1_pw = transceiver_dom_info_dict.get("tx1power", "N/A") + return [tx1_pw, "N/A", "N/A", "N/A"] if transceiver_dom_info_dict else [] + + def reset(self): + """ + Reset SFP and return all user module settings to their default srate. + + Returns: + A boolean, True if successful, False if not + """ + # SFP doesn't support this feature + return False + + def tx_disable(self, tx_disable): + """ + Disable SFP TX for all channels + + Args: + tx_disable : A Boolean, True to enable tx_disable mode, False to disable + tx_disable mode. + + Returns: + A boolean, True if tx_disable is set successfully, False if not + """ + sysfs_eeprom_path = self.__eeprom_path + status_control_raw = self.__read_eeprom_specific_bytes(SFP_STATUS_CONTROL_OFFSET, SFP_STATUS_CONTROL_WIDTH) + if status_control_raw is not None: + # Set bit 6 for Soft TX Disable Select + # 01000000 = 64 and 10111111 = 191 + tx_disable_bit = 64 if tx_disable else 191 + status_control = int(status_control_raw[0], 16) + tx_disable_ctl = (status_control | tx_disable_bit) if tx_disable else (status_control & tx_disable_bit) + try: + sysfsfile_eeprom = open(sysfs_eeprom_path, mode="r+b", buffering=0) + buffer = create_string_buffer(1) + buffer[0] = chr(tx_disable_ctl) + # Write to eeprom + sysfsfile_eeprom.seek(SFP_STATUS_CONTROL_OFFSET) + sysfsfile_eeprom.write(buffer[0]) + except Exception: + return False + finally: + if sysfsfile_eeprom: + sysfsfile_eeprom.close() + time.sleep(0.01) + return True + return False + + def tx_disable_channel(self, channel, disable): + """ + Sets the tx_disable for specified SFP channels + + Args: + channel : A hex of 4 bits (bit 0 to bit 3) which represent channel 0 to 3, + e.g. 0x5 for channel 0 and channel 2. + disable : A boolean, True to disable TX channels specified in channel, + False to enable + + Returns: + A boolean, True if successful, False if not + """ + # SFP doesn't support this feature + return False + + def set_lpmode(self, lpmode): + """ + Sets the lpmode (low power mode) of SFP + + Args: + lpmode: A Boolean, True to enable lpmode, False to disable it + Note : lpmode can be overridden by set_power_override + + Returns: + A boolean, True if lpmode is set successfully, False if not + """ + # SFP doesn't support this feature + return False + + def set_power_override(self, power_override, power_set): + """ + Sets SFP power level using power_override and power_set + + Args: + power_override : + A Boolean, True to override set_lpmode and use power_set + to control SFP power, False to disable SFP power control + through power_override/power_set and use set_lpmode + to control SFP power. + power_set : + Only valid when power_override is True. + A Boolean, True to set SFP to low power mode, False to set + SFP to high power mode. + + Returns: + A boolean, True if power-override and power_set are set successfully, + False if not + """ + # SFP doesn't support this feature + return False diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/thermal.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/thermal.py new file mode 100755 index 000000000000..0f15fb91817f --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/thermal.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python +# +# Name: thermal.py, version: 1.0 +# +# Description: Module contains the definitions of SONiC platform APIs +# + +try: + import os + import os.path + from sonic_platform_base.thermal_base import ThermalBase + from .helper import APIHelper +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +NULL_VAL = "N/A" + +class Thermal(ThermalBase): + """Platform-specific Thermal class""" + + SS_CONFIG_PATH = "/etc/sensors.d/sensors.conf" + + def __init__(self, thermal_index,thermal_conf): + self.__index = thermal_index + self.__conf = thermal_conf + + if self.__conf[self.__index]['container'] == 'psu': + self.__attr_path_prefix = '/sys/switch/psu/psu{}/temp{}/'.format(self.__conf[self.__index]['container_index']+1,self.__index) + self.__test_path_prefix = '/sys/switch/psu/psu{}/temp{}/'.format(self.__conf[self.__index]['container_index']+1,self.__index) + else: + self.__attr_path_prefix = '/sys/switch/sensor/temp{}/'.format(self.__index) + self.__test_path_prefix = '/run/platform_cache/thermal_test/sensor/temp{}/'.format(self.__index) + + self.__api_helper = APIHelper() + + self.name = self.get_name() + + self.__minimum_thermal = self.get_temperature() + self.__maximum_thermal = self.get_temperature() + ThermalBase.__init__(self) + + def get_temperature(self): + """ + Retrieves current temperature reading from thermal + Returns: + A float number of current temperature in Celsius up to nearest thousandth + of one degree Celsius, e.g. 30.125 + """ + temperature = 0.0 + + #check whether test mode is set or not + if (os.path.isdir(self.__test_path_prefix)): + attr_rv = self.__api_helper.read_one_line_file(self.__test_path_prefix + 'temp_input') + else: + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'temp_input') + + if (attr_rv != None): + attr_rv = int(attr_rv, 10) + temperature = float(attr_rv/1000) + return temperature + + def get_high_threshold(self): + """ + Retrieves the high threshold temperature of thermal + Returns: + A float number, the high threshold temperature of thermal in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + high_threshold = 0.0 + #check whether test mode is set or not + if (os.path.isdir(self.__test_path_prefix)): + attr_rv = self.__api_helper.read_one_line_file(self.__test_path_prefix + 'temp_max_hyst') + else: + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'temp_max_hyst') + + if (attr_rv != None): + attr_rv = int(attr_rv, 10) + high_threshold = float(attr_rv/1000) + return high_threshold + + def get_low_threshold(self): + """ + Retrieves the low threshold temperature of thermal + Returns: + A float number, the low threshold temperature of thermal in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + return 0.001 + + def set_high_threshold(self, temperature): + """ + Sets the high threshold temperature of thermal + Args : + temperature: A float number up to nearest thousandth of one degree Celsius, + e.g. 30.125 + Returns: + A boolean, True if threshold is set successfully, False if not + """ + is_set = False + is_set = self.__api_helper.write_txt_file(self.__attr_path_prefix + 'temp_max_hyst',int(temperature*1000)) + return is_set + + def set_low_threshold(self, temperature): + """ + Sets the low threshold temperature of thermal + Args : + temperature: A float number up to nearest thousandth of one degree Celsius, + e.g. 30.125 + Returns: + A boolean, True if threshold is set successfully, False if not + """ + return False + + def get_high_critical_threshold(self): + """ + Retrieves the high critical threshold temperature of thermal + Returns: + A float number, the high critical threshold temperature of thermal in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + high_threshold = 0.0 + #check whether test mode is set or not + if (os.path.isdir(self.__test_path_prefix)): + attr_rv = self.__api_helper.read_one_line_file(self.__test_path_prefix + 'temp_max') + else: + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'temp_max') + + if (attr_rv != None): + attr_rv = int(attr_rv, 10) + high_threshold = float(attr_rv/1000) + return high_threshold + + def get_low_critical_threshold(self): + """ + Retrieves the low critical threshold temperature of thermal + Returns: + A float number, the low critical threshold temperature of thermal in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + return 0.001 + + 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 + """ + tmp = self.get_temperature() + if tmp < self.__minimum_thermal: + self.__minimum_thermal = tmp + return self.__minimum_thermal + + 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 + """ + tmp = self.get_temperature() + if tmp > self.__maximum_thermal: + self.__maximum_thermal = tmp + return self.__maximum_thermal + + ############################################################## + ###################### Device methods ######################## + ############################################################## + + def get_name(self): + """ + Retrieves the name of the thermal device + Returns: + string: The name of the thermal device + """ + return self.__conf[self.__index]['name'] + + def get_presence(self): + """ + Retrieves the presence of the PSU + Returns: + bool: True if PSU is present, False if not + """ + return os.path.isfile(self.__attr_path_prefix + 'temp_max_hyst') + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + return NULL_VAL + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + return NULL_VAL + + def get_status(self): + """ + Retrieves the operational status of the device + Returns: + A boolean value, True if device is operating properly, False if not + """ + return self.get_presence() + + def is_replaceable(self): + """ + Retrieves whether thermal module is replaceable + Returns: + A boolean value, True if replaceable, False if not + """ + return False + + def get_position_in_parent(self): + """ + Retrieves the thermal position information + Returns: + A int value, 0 represent ASIC thermal, 1 represent CPU thermal info + """ + if self.__conf[self.__index]['position'] == "cpu": + return 1 + return 0 diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/thermal_actions.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/thermal_actions.py new file mode 100644 index 000000000000..1af2bf6d44ea --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/thermal_actions.py @@ -0,0 +1,70 @@ +import os +#import sys +import time + +from sonic_platform_base.sonic_thermal_control.thermal_action_base import ThermalPolicyActionBase +from sonic_platform_base.sonic_thermal_control.thermal_json_object import thermal_json_object +from sonic_py_common import logger +#from sonic_platform.fault import Fault +from .helper import APIHelper + +SYSLOG_IDENTIFIER = 'thermalctld' +helper_logger = logger.Logger(SYSLOG_IDENTIFIER) + +PLATFORM_CAUSE_DIR = "/host/reboot-cause/platform" +#ADDITIONAL_FAULT_CAUSE_FILE = os.path.join(PLATFORM_CAUSE_DIR, "additional_fault_cause") +INT_STATUS_FILE = '/sys/devices/pci0000:00/0000:00:1f.3/i2c-0/i2c-1/i2c-6/6-0074/int_status' +ADDITIONAL_FAULT_CAUSE_FILE = "/usr/share/sonic/platform/api_files/reboot-cause/platform/additional_fault_cause" +THERMAL_OVERLOAD_POSITION_FILE = "/usr/share/sonic/platform/api_files/reboot-cause/platform/thermal_overload_position" + +@thermal_json_object('switch.power_cycling') +class SwitchPolicyAction(ThermalPolicyActionBase): + """ + Base class for thermal action. Once all thermal conditions in a thermal policy are matched, + all predefined thermal action will be executed. + """ + + def execute(self, thermal_info_dict): + """ + Take action when thermal condition matches. For example, power cycle the switch. + :param thermal_info_dict: A dictionary stores all thermal information. + :return: + """ + self.__api_helper = APIHelper() + helper_logger.log_error("Error: thermal overload !!!!!!!!!!!!!!!!!!Please reboot Now!!") + helper_logger.log_error("Error: thermal overload !!!!!!!!!!!!!!!!!!") + """ + helper_logger.log_error("recorded the fault cause begin...") + attr_rv = self.__api_helper.read_one_line_file(INT_STATUS_FILE) + attr_rv = int(attr_rv, 16) + REBOOT_FAN_FAULT = '' + REBOOT_PSU_FAULT = '' + #0 is fault and 1 is normal + if((attr_rv & 0x1) == 0): + REBOOT_FAN_FAULT = 'FAN' + if((attr_rv & 0x2) == 0): + REBOOT_PSU_FAULT = '3V3 SYSTEM POWER' + + #fault_status = Fault.get_fault_status() + # Write a new default reboot cause file for the next reboot check + if((attr_rv & 0x3) != 0): + REBOOT_FAULT_CAUSE = ' '.join([REBOOT_FAN_FAULT, REBOOT_PSU_FAULT]) + with open(ADDITIONAL_FAULT_CAUSE_FILE, "w") as additional_fault_cause_file: + additional_fault_cause_file.write(REBOOT_FAULT_CAUSE) + additional_fault_cause_file.close() + os.system('sync') + os.system('sync') + helper_logger.log_error("recorded the fault cause...done") + """ + #wait for all record actions done + wait_ms = 30 + while wait_ms > 0: + if os.path.isfile(THERMAL_OVERLOAD_POSITION_FILE): + thermal_overload_pos = self.__api_helper.read_one_line_file(THERMAL_OVERLOAD_POSITION_FILE) + if "critical threshold" in thermal_overload_pos: + break + time.sleep(1) + helper_logger.log_error("wait ############for recorded") + wait_ms = wait_ms - 1 + #system reset. + os.system('echo 1 > /sys/devices/pci0000:00/0000:00:1f.3/i2c-0/i2c-1/1-0018/sys_rst') diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/thermal_conditions.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/thermal_conditions.py new file mode 100644 index 000000000000..1f7eee35d187 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/thermal_conditions.py @@ -0,0 +1,20 @@ +from sonic_platform_base.sonic_thermal_control.thermal_condition_base import ThermalPolicyConditionBase +from sonic_platform_base.sonic_thermal_control.thermal_json_object import thermal_json_object + +class ThermalCondition(ThermalPolicyConditionBase): + def get_thermal_info(self, thermal_info_dict): + from .thermal_infos import ThermalInfo + if ThermalInfo.INFO_NAME in thermal_info_dict and isinstance(thermal_info_dict[ThermalInfo.INFO_NAME], ThermalInfo): + return thermal_info_dict[ThermalInfo.INFO_NAME] + else: + return None + + +@thermal_json_object('thermal.over.high_critical_threshold') +class ThermalOverHighCriticalCondition(ThermalCondition): + def is_match(self, thermal_info_dict): + thermal_info_obj = self.get_thermal_info(thermal_info_dict) + if thermal_info_obj: + return thermal_info_obj.is_over_high_critical_threshold() + else: + return False diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/thermal_infos.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/thermal_infos.py new file mode 100644 index 000000000000..f50e21283dff --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/thermal_infos.py @@ -0,0 +1,168 @@ +from sonic_platform_base.sonic_thermal_control.thermal_info_base import ThermalPolicyInfoBase +from sonic_platform_base.sonic_thermal_control.thermal_json_object import thermal_json_object +from .helper import APIHelper +import os +import time + + +@thermal_json_object('fan_info') +class FanInfo(ThermalPolicyInfoBase): + """ + Fan information needed by thermal policy + """ + + # Fan information name + INFO_NAME = 'fan_info' + + def __init__(self): + self._absence_fans = set() + self._presence_fans = set() + self._fault_fans = set() + self._status_changed = False + + def collect(self, chassis): + """ + Collect absence and presence fans. + :param chassis: The chassis object + :return: + """ + self._status_changed = False + for fan in chassis.get_all_fans(): + presence = fan.get_presence() + status = fan.get_status() + if presence and fan not in self._presence_fans: + self._presence_fans.add(fan) + self._status_changed = True + if fan in self._absence_fans: + self._absence_fans.remove(fan) + elif not presence and fan not in self._absence_fans: + self._absence_fans.add(fan) + self._status_changed = True + if fan in self._presence_fans: + self._presence_fans.remove(fan) + + if not status and fan not in self._fault_fans: + self._fault_fans.add(fan) + self._status_changed = True + + elif status and fan in self._fault_fans: + self._fault_fans.remove(fan) + self._status_changed = True + + def get_absence_fans(self): + """ + Retrieves absence fans + :return: A set of absence fans + """ + return self._absence_fans + + def get_presence_fans(self): + """ + Retrieves presence fans + :return: A set of presence fans + """ + return self._presence_fans + + def get_fault_fans(self): + """ + Retrieves fault fans + :return: A set of fault fans + """ + return self._fault_fans + + def is_status_changed(self): + """ + Retrieves if the status of fan information changed + :return: True if status changed else False + """ + return self._status_changed + + +@thermal_json_object('thermal_info') +class ThermalInfo(ThermalPolicyInfoBase): + """ + Thermal information needed by thermal policy + """ + + # Fan information name + INFO_NAME = 'thermal_info' + + def collect(self, chassis): + """ + Collect thermal sensor temperature change status + :param chassis: The chassis object + :return: + """ + self._over_high_threshold = False + self._over_high_critical_threshold = False + self._thermal_overload_position = 'cpu' + + # Calculate average temp within the device + temp = 0 + num_of_thermals = chassis.get_num_thermals() + for index in range(num_of_thermals): + thermal = chassis.get_thermal(index) + temp = thermal.get_temperature() + high_threshold = thermal.get_high_threshold() + high_critical_threshold = thermal.get_high_critical_threshold() + + if high_threshold and temp > high_threshold: + self._over_high_threshold = True + + if high_critical_threshold and temp > high_critical_threshold: + self._thermal_overload_position = thermal.name + self._over_high_critical_threshold = True + + def is_over_threshold(self): + """ + Retrieves if the temperature is over any threshold + :return: True if the temperature is over any threshold else False + """ + return self._over_high_threshold or self._over_high_critical_threshold + + def is_over_high_critical_threshold(self): + """ + Retrieves if the temperature is over high critical threshold + :return: True if the temperature is over high critical threshold else False + """ + thermal_overload_position_path = '/usr/share/sonic/platform/api_files/reboot-cause/platform/thermal_overload_position' + if self._over_high_critical_threshold: + APIHelper().write_txt_file(thermal_overload_position_path, + self._thermal_overload_position + ' temperature over critical threshold') + time.sleep(1) + os.system('sync') + os.system('sync') + return self._over_high_critical_threshold + + def is_over_high_threshold(self): + """ + Retrieves if the temperature is over high threshold + :return: True if the temperature is over high threshold else False + """ + return self._over_high_threshold + + +@thermal_json_object('chassis_info') +class ChassisInfo(ThermalPolicyInfoBase): + """ + Chassis information needed by thermal policy + """ + INFO_NAME = 'chassis_info' + + def __init__(self): + self._chassis = None + + def collect(self, chassis): + """ + Collect platform chassis. + :param chassis: The chassis object + :return: + """ + self._chassis = chassis + + def get_chassis(self): + """ + Retrieves platform chassis object + :return: A platform chassis object. + """ + return self._chassis diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/thermal_manager.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/thermal_manager.py new file mode 100644 index 000000000000..8fe160a0f800 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/thermal_manager.py @@ -0,0 +1,40 @@ +from sonic_platform_base.sonic_thermal_control.thermal_manager_base import ThermalManagerBase + +class ThermalManager(ThermalManagerBase): + + @classmethod + def start_thermal_control_algorithm(cls): + """ + Start vendor specific thermal control algorithm. The default behavior of this function is a no-op. + :return: + """ + return cls._enable_fancontrol_service(True) + + @classmethod + def stop_thermal_control_algorithm(cls): + """ + Stop thermal control algorithm + Returns: + bool: True if set success, False if fail. + """ + return cls._enable_fancontrol_service(False) + + @classmethod + def deinitialize(cls): + """ + Destroy thermal manager, including any vendor specific cleanup. The default behavior of this function + is a no-op. + :return: + """ + return cls._enable_fancontrol_service(True) + + @classmethod + def _enable_fancontrol_service(cls, enable): + """ + Control thermal by fcs algorithm + Args: + enable: Bool, indicate enable the algorithm or not + Returns: + bool: True if set success, False if fail. + """ + pass diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/watchdog.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/watchdog.py new file mode 100644 index 000000000000..ee3453234113 --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/sonic_platform/watchdog.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python + +from __future__ import print_function + +try: + from sonic_platform_base.watchdog_base import WatchdogBase + from .helper import APIHelper +except ImportError as e: + raise ImportError("%s - required module not found" % e) + +class Watchdog(WatchdogBase): + """ + Clounix watchdog class for interfacing with a hardware watchdog module + """ + + def __init__(self, watchdog_conf): + self.__conf=watchdog_conf + self.__attr_path_prefix = '/sys/switch/watchdog/' + self.__api_helper = APIHelper() + WatchdogBase.__init__(self) + + def get_name(self): + return self.__conf['name'] + + def get_model(self): + return "N/A" + + def get_presence(self): + return True + + def get_serial(self): + return "N/A" + + def get_status(self): + return True + + def get_position_in_parent(self): + return -1 + + def is_replaceable(self): + return False + + def get_identify(self): + identify = 'N/A' + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'identity') + if (attr_rv != None): + identify = attr_rv + return identify + + def get_timeout(self): + timeout = 0 + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'timeout') + if (attr_rv != None): + timeout = int(attr_rv) + return timeout + + def set_timeout(self,seconds): + timeout = 0 + + attr_rv = self.__api_helper.write_txt_file(self.__attr_path_prefix + 'timeout',seconds) + if (attr_rv != None): + timeout = int(attr_rv) + return timeout + + def arm(self, seconds): + """ + Arm the hardware watchdog with a timeout of seconds. + If the watchdog is currently armed, calling this function will + simply reset the timer to the provided value. If the underlying + hardware does not support the value provided in , this + method should arm the watchdog with the *next greater* available + value. + + Returns: + An integer specifying the *actual* number of seconds the watchdog + was armed with. On failure returns -1. + """ + arm_sec = -1 + self.__api_helper.write_txt_file(self.__attr_path_prefix + 'enable', '1') + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'timeout') + if (attr_rv != None): + arm_sec = int(attr_rv) + + return arm_sec + + def disarm(self): + """ + Disarm the hardware watchdog + + Returns: + A boolean, True if watchdog is disarmed successfully, False if not + """ + self.__api_helper.write_txt_file(self.__attr_path_prefix + 'enable', '0') + return True + + def get_remaining_time(self): + """ + If the watchdog is armed, retrieve the number of seconds remaining on + the watchdog timer + + Returns: + An integer specifying the number of seconds remaining on thei + watchdog timer. If the watchdog is not armed, returns -1. + """ + remaining_time = 0.0 + + attr_rv = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'timeleft') + if (attr_rv != None): + attr_rv = int(attr_rv, 10) + remaining_time = attr_rv + return remaining_time + + def is_armed(self): + """ + Retrieves the armed state of the hardware watchdog. + + Returns: + A boolean, True if watchdog is armed, False if not + """ + status = self.__api_helper.read_one_line_file(self.__attr_path_prefix + 'state') + status = status[0:3] + if (status == 'act'): + return True + else: + return False diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/utils/install_firmware.sh b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/utils/install_firmware.sh new file mode 100755 index 000000000000..12677e9df25b --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/utils/install_firmware.sh @@ -0,0 +1,192 @@ +#!/bin/sh + +firmware=$1 +firmware_file=$2 +bin_path=/usr/local/bin +log_file=/run/fw_updates_log +identify_file=/usr/local/bin/fw_updates_identify + +[ -n "${firmware_file}" ] || { + echo "ERROR: unable to read firmware Image" + exit 1 +} + +stop_plt_service() +{ + #stop service + systemctl stop pmon + systemctl stop system-health + modprobe -r i2c_mux_pca9641 + echo 0 > /proc/sys/kernel/panic_on_unrecovered_nmi +} + +start_plt_service() +{ + #auto restart sytem for firmware upgrade later + return + #restart platfrom servic + systemctl stop fn8656_bnf-platform-init.service + systemctl start fn8656_bnf-platform-init.service + systemctl start pmon + systemctl start system-health +} + +mcu_fw_install() +{ + _file_name=$firmware_file + + _mb_type=0 + if [ "$firmware" = "mcu-mb" ]; then + _mb_type=1 + fi + echo "${firmware} mcu_fw_install, please wait..." >> $log_file + stop_plt_service + #configuraton for programming + i2cset -y 0 0x71 0x3 0x00 + i2cset -y 0 0x71 0x1 0x7 + i2cset -y 0 0x72 0x0 0x7 + #GPIO17_UART1_SEL:0 is for upgrade MUC used and 1 is for i2c bridge used(default) + #GPIO54_MB_FCn_MCU_SEL:0 is for Fan control board MCU upgrade and 1 is for MB MCU upgrade + gpio_uart_sel=453 #436+17=453 + gpio_mcu_sel=490 #436+54=490 + echo $gpio_uart_sel > /sys/class/gpio/export + echo out > /sys/class/gpio/gpio453/direction + echo 0 > /sys/class/gpio/gpio453/value #pull low for GPIO-17 to UART selection + echo $gpio_mcu_sel > /sys/class/gpio/export + echo out > /sys/class/gpio/gpio490/direction + if [ $_mb_type = 1 ]; then + echo 1 > /sys/class/gpio/gpio490/value #pull high for GPIO-54 to UART selection MB + else + echo 0 > /sys/class/gpio/gpio490/value #pull low for GPIO-54 to UART selection FB + fi + if [ $_mb_type = 1 ]; then + #write 0xA5 to MB_FW_UG + i2cset -y 0 0x70 0x0 0xa5 + else + #write 0xA5 to FB_FW_UG + i2cset -y 0 0x70 0x1 0xa5 + fi + #i2cget -y 0 0x70 0x1 + #upgrade MCU FW + cd /usr/local/bin/mcu_upgrade + echo "${firmware} mcu_fw_install, please wait..." >> $log_file + python3 $bin_path/mcu_upgrade/efm8load.py -p /dev/ttyS1 $_file_name + echo "${firmware} mcu_fw_install, done" >> $log_file + cd - + #for store GPIOs used + echo $gpio_uart_sel > /sys/class/gpio/unexport + echo $gpio_mcu_sel > /sys/class/gpio/unexport + start_plt_service + +} + +#loading for main board CPLD A/B/C +cpld_mb_install() +{ + _file_name=$firmware_file + + stop_plt_service + #configuraton for GPIO + #GPIO6 GPIO6_CPLD_DTO JTAG_TDO + #GPIO24 GPIO24_SUS_CPLD_TCK JTAG_TCK + #GPIO32 GPIO32_CPLD_TDI JTAG_TDI + #GPIO50 GPIO50_CPLD_TM5 JTAG_TM5 + echo 442 > /sys/class/gpio/export + echo 460 > /sys/class/gpio/export + echo 468 > /sys/class/gpio/export + echo 486 > /sys/class/gpio/export + echo "out" > /sys/class/gpio/gpio486/direction + echo "out" > /sys/class/gpio/gpio468/direction + echo "out" > /sys/class/gpio/gpio460/direction + echo "in" > /sys/class/gpio/gpio442/direction + #must copy to same folder + cp $_file_name $bin_path/cpld_upgrade + tmp_file=$bin_path/cpld_upgrade/${_file_name##*/} + #upgrade CPLD load + $bin_path/cpld_upgrade/cpld_upgrade_bdxde_mb $tmp_file + rm $tmp_file + + #restore GPOIs + echo 442 > /sys/class/gpio/unexport + echo 460 > /sys/class/gpio/unexport + echo 468 > /sys/class/gpio/unexport + echo 486 > /sys/class/gpio/unexport + start_plt_service +} + +#loading for CPU board CPLD D +cpld_cpu_install() +{ + _file_name=$firmware_file + + stop_plt_service + #configuraton for GPIO + #GPIO27 GPIO27_CPLD_DTO JTAG_TDO + #GPIO61 GPIO61_SUS_CPLD_TCK JTAG_TCK + #GPIO68 GPIO68_CPLD_TDI JTAG_TDI + #GPIO12 GPIO12_CPLD_TM5 JTAG_TM5 + #GPIO25 GPIO25_SUS_CPLD_DOWNLOAD_SEL + echo 448 > /sys/class/gpio/export + echo 461 > /sys/class/gpio/export + echo 463 > /sys/class/gpio/export + echo 497 > /sys/class/gpio/export + echo 504 > /sys/class/gpio/export + echo "out" > /sys/class/gpio/gpio448/direction + echo "out" > /sys/class/gpio/gpio461/direction + echo "out" > /sys/class/gpio/gpio497/direction + echo "out" > /sys/class/gpio/gpio504/direction + echo "in" > /sys/class/gpio/gpio463/direction + echo 1 > /sys/class/gpio/gpio461/value + echo 1 > /sys/class/gpio/gpio448/value + echo 1 > /sys/class/gpio/gpio504/value + #About Upgrading CPU CPLD, due to JTAG-MUX set to BMC channel, + #so here need additional step to control CPLD(D) BMCR1 register bit [0] to CPU sid + i2cset -y 0 0x71 0x3 0x00 + i2cset -y 0 0x71 0x1 0x7 + i2cset -y 0 0x18 0x0B 0x29 + #must copy to same folder + cp $_file_name $bin_path/cpld_upgrade + tmp_file=$bin_path/cpld_upgrade/${_file_name##*/} + #upgrade CPLD load + $bin_path/cpld_upgrade/cpld_upgrade_bdxde_cpu $tmp_file + rm $tmp_file + + #resore GPIOs + echo 448 > /sys/class/gpio/unexport + echo 461 > /sys/class/gpio/unexport + echo 463 > /sys/class/gpio/unexport + echo 497 > /sys/class/gpio/unexport + echo 504 > /sys/class/gpio/unexport + start_plt_service +} + +echo "${firmware} loading, please wait..." +touch $log_file +touch $identify_file +echo "${firmware} loading, please wait..." >> $log_file +case "$firmware" in +mcu-fb | mcu-mb) + mcu_fw_install + ;; +cpld-mb) + cpld_mb_install + ;; +cpld-cpu) + cpld_cpu_install + ;; +fpga) + cd /usr/local/bin/fpga_bin + ./fpga_bin -f ${firmware_file} -b 3 + ;; +bios) + cd /usr/local/bin/bios_bin + ./afulnx_64 ${firmware_file} /p /b /n + ;; +*) + echo "Usage: $0 {cpld-mb|cpld-cpu|mcu-mb|mcu-fb|bios}" + exit 1 + ;; +esac +echo "${firmware} upgrade is done." +echo "${firmware} upgrade done." >> $log_file +echo "${firmware} upgrade done" >> $identify_file diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/utils/install_firmware_ext.sh b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/utils/install_firmware_ext.sh new file mode 100755 index 000000000000..6cfaf7401c9d --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/utils/install_firmware_ext.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +action=$1 + +[ -n "${action}" ] || { + echo "ERROR: no action is found." + exit 1 +} + +stop_plt_service() +{ + echo "service start" + #stop service + systemctl stop pmon + systemctl stop system-health + modprobe -r i2c_mux_pca9641 + echo 0 > /proc/sys/kernel/panic_on_unrecovered_nmi +} + +start_plt_service() +{ + echo "service start" + #restart platfrom servic + systemctl stop fn8656_bnf-platform-init.service + systemctl start fn8656_bnf-platform-init.service + systemctl start pmon + systemctl start system-health +} + + + +echo "start" +case "$action" in +start) + start_plt_service + ;; +stop) + stop_plt_service + ;; +*) + echo "Usage: $0 {cpld-mb|cpld-cpu|mcu-mb|mcu-fb|bios}" + exit 1 + ;; +esac +echo "done" diff --git a/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/utils/pegatron_fn8656_bnf_util.py b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/utils/pegatron_fn8656_bnf_util.py new file mode 100755 index 000000000000..fc9050cdf0ad --- /dev/null +++ b/platform/clounix/sonic-platform-modules-pegatron/fn8656-bnf/utils/pegatron_fn8656_bnf_util.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Pegatron, 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 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 . + +from pegaProcess import common + +#message handler +def doCommand(cmd): + """ + Command: + global : Set global config + device : Set device config + """ + if len(cmd[0:]) < 1 or cmd[0] not in common.CMD_TYPE: + print(doCommand.__doc__) + return + msg = ' '.join(str(data) for data in cmd) + result = common.doSend(msg, common.SOCKET_PORT) + print (result) + + return + +def main(): + """ + Command: + install : Install drivers + uninstall : Uninstall drivers + cmd : Commands + """ + args = common.sys.argv[1:] + + if len(args[0:]) < 1: + print(main.__doc__) + return + + if args[0] == 'uninstall': + doCommand(['global', 'uninstall']) + elif args[0] == 'cmd': + doCommand(args[1:]) + else: + print(main.__doc__) + common.sys.exit(0) + +if __name__ == "__main__": + main()