diff --git a/builds/any/rootfs/buster/common/all-base-packages.yml b/builds/any/rootfs/buster/common/all-base-packages.yml index b8e9df5f3a..36a5d200c9 100644 --- a/builds/any/rootfs/buster/common/all-base-packages.yml +++ b/builds/any/rootfs/buster/common/all-base-packages.yml @@ -85,3 +85,4 @@ - htop - tree - memtester +- ipmitool diff --git a/packages/base/all/vendor-config-onl/src/python/onl/platform/base.py b/packages/base/all/vendor-config-onl/src/python/onl/platform/base.py index 08bdb9839b..8183393b5c 100644 --- a/packages/base/all/vendor-config-onl/src/python/onl/platform/base.py +++ b/packages/base/all/vendor-config-onl/src/python/onl/platform/base.py @@ -650,3 +650,7 @@ class OnlPlatformPortConfig_40x100_13x400_2x10(object): class OnlPlatformPortConfig_64x800_2x25(object): PORT_COUNT=66 PORT_CONFIG="64x800 + 2x25" + +class OnlPlatformPortConfig_32x800_2x10(object): + PORT_COUNT=34 + PORT_CONFIG="32x800 + 2x10" diff --git a/packages/base/any/kernels/5.4-lts/configs/x86_64-all/x86_64-all.config b/packages/base/any/kernels/5.4-lts/configs/x86_64-all/x86_64-all.config index 08480880c6..54d93a8653 100644 --- a/packages/base/any/kernels/5.4-lts/configs/x86_64-all/x86_64-all.config +++ b/packages/base/any/kernels/5.4-lts/configs/x86_64-all/x86_64-all.config @@ -317,11 +317,11 @@ CONFIG_X86_LOCAL_APIC=y CONFIG_X86_IO_APIC=y CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS=y CONFIG_X86_MCE=y -# CONFIG_X86_MCELOG_LEGACY is not set +CONFIG_X86_MCELOG_LEGACY=y CONFIG_X86_MCE_INTEL=y CONFIG_X86_MCE_AMD=y CONFIG_X86_MCE_THRESHOLD=y -# CONFIG_X86_MCE_INJECT is not set +CONFIG_X86_MCE_INJECT=m CONFIG_X86_THERMAL_VECTOR=y # @@ -737,6 +737,7 @@ CONFIG_MODULE_FORCE_UNLOAD=y # CONFIG_MODVERSIONS is not set # CONFIG_MODULE_SRCVERSION_ALL is not set # CONFIG_MODULE_SIG is not set + # CONFIG_MODULE_COMPRESS is not set # CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS is not set # CONFIG_UNUSED_SYMBOLS is not set @@ -1975,7 +1976,35 @@ CONFIG_USB_NET_DRIVERS=y # CONFIG_USB_RTL8150 is not set # CONFIG_USB_RTL8152 is not set # CONFIG_USB_LAN78XX is not set -# CONFIG_USB_USBNET is not set +CONFIG_USB_USBNET=m +CONFIG_USB_NET_CDCETHER=m +# CONFIG_USB_NET_AX8817X is not set +# CONFIG_USB_NET_AX88179_178A is not set +# CONFIG_USB_NET_CDC_EEM is not set +# CONFIG_USB_NET_CDC_NCM is not set +# CONFIG_USB_NET_HUAWEI_CDC_NCM is not set +# CONFIG_USB_NET_CDC_MBIM is not set +# CONFIG_USB_NET_DM9601 is not set +# CONFIG_USB_NET_SR9700 is not set +# CONFIG_USB_NET_SR9800 is not set +# CONFIG_USB_NET_SMSC75XX is not set +# CONFIG_USB_NET_SMSC95XX is not set +# CONFIG_USB_NET_GL620A is not set +# CONFIG_USB_NET_NET1080 is not set +# CONFIG_USB_NET_PLUSB is not set +# CONFIG_USB_NET_MCS7830 is not set +# CONFIG_USB_NET_RNDIS_HOST is not set +# CONFIG_USB_NET_CDC_SUBSET is not set +# CONFIG_USB_NET_ZAURUS is not set +# CONFIG_USB_NET_CX82310_ETH is not set +# CONFIG_USB_NET_KALMIA is not set +# CONFIG_USB_NET_QMI_WWAN is not set +# CONFIG_USB_NET_INT51X1 is not set +# CONFIG_USB_SIERRA_NET is not set +# CONFIG_USB_VL600 is not set +# CONFIG_USB_NET_CH9200 is not set +# CONFIG_USB_NET_AQC111 is not set + # CONFIG_USB_HSO is not set # CONFIG_USB_IPHETH is not set CONFIG_WLAN=y @@ -2098,6 +2127,7 @@ CONFIG_INPUT_EVDEV=y # Input Device Drivers # CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADC is not set # CONFIG_KEYBOARD_ADP5588 is not set # CONFIG_KEYBOARD_ADP5589 is not set CONFIG_KEYBOARD_ATKBD=y @@ -2183,6 +2213,7 @@ CONFIG_INPUT_TABLET=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_PROPERTIES=y # CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_ADC is not set # CONFIG_TOUCHSCREEN_ATMEL_MXT is not set # CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set # CONFIG_TOUCHSCREEN_BU21013 is not set @@ -2396,7 +2427,19 @@ CONFIG_NVRAM=y CONFIG_HPET=y # CONFIG_HPET_MMAP is not set # CONFIG_HANGCHECK_TIMER is not set -# CONFIG_TCG_TPM is not set +CONFIG_TCG_TPM=m +# CONFIG_HW_RANDOM_TPM is not set +CONFIG_TCG_TIS_CORE=m +CONFIG_TCG_TIS=m +# CONFIG_TCG_TIS_I2C_ATMEL is not set +# CONFIG_TCG_TIS_I2C_INFINEON is not set +CONFIG_TCG_TIS_I2C_NUVOTON=m +# CONFIG_TCG_NSC is not set +# CONFIG_TCG_ATMEL is not set +# CONFIG_TCG_INFINEON is not set +# CONFIG_TCG_CRB is not set +# CONFIG_TCG_VTPM_PROXY is not set +# CONFIG_TCG_TIS_ST33ZP24_I2C is not set # CONFIG_TELCLOCK is not set CONFIG_DEVPORT=y # CONFIG_XILLYBUS is not set @@ -2563,6 +2606,7 @@ CONFIG_GPIOLIB_FASTPATH_LIMIT=512 # CONFIG_GPIO_MAX7300 is not set # CONFIG_GPIO_MAX732X is not set # CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCA953X_IRQ is not set # CONFIG_GPIO_PCF857X is not set # CONFIG_GPIO_TPIC2810 is not set # end of I2C GPIO expanders @@ -2596,6 +2640,7 @@ CONFIG_POWER_SUPPLY=y # CONFIG_POWER_SUPPLY_DEBUG is not set CONFIG_POWER_SUPPLY_HWMON=y # CONFIG_PDA_POWER is not set +# CONFIG_GENERIC_ADC_BATTERY is not set # CONFIG_TEST_POWER is not set # CONFIG_CHARGER_ADP5061 is not set # CONFIG_BATTERY_DS2780 is not set @@ -2662,6 +2707,7 @@ CONFIG_HWMON=y # CONFIG_SENSORS_G760A is not set # CONFIG_SENSORS_G762 is not set # CONFIG_SENSORS_HIH6130 is not set +# CONFIG_SENSORS_IIO_HWMON is not set # CONFIG_SENSORS_IBMAEM is not set # CONFIG_SENSORS_IBMPEX is not set # CONFIG_SENSORS_I5500 is not set @@ -2819,6 +2865,7 @@ CONFIG_X86_PKG_TEMP_THERMAL=m # CONFIG_INTEL_PCH_THERMAL is not set # end of Intel thermal drivers +# CONFIG_GENERIC_ADC_THERMAL is not set CONFIG_WATCHDOG=y # CONFIG_WATCHDOG_CORE is not set # CONFIG_WATCHDOG_NOWAYOUT is not set @@ -3993,7 +4040,318 @@ CONFIG_INTEL_IOMMU_FLOPPY_WA=y # CONFIG_PM_DEVFREQ is not set # CONFIG_EXTCON is not set # CONFIG_MEMORY is not set -# CONFIG_IIO is not set +CONFIG_IIO=m +CONFIG_IIO_BUFFER=y +# CONFIG_IIO_BUFFER_CB is not set +# CONFIG_IIO_BUFFER_HW_CONSUMER is not set +CONFIG_IIO_KFIFO_BUF=m +CONFIG_IIO_TRIGGERED_BUFFER=m +# CONFIG_IIO_CONFIGFS is not set +CONFIG_IIO_TRIGGER=y +CONFIG_IIO_CONSUMERS_PER_TRIGGER=2 +# CONFIG_IIO_SW_DEVICE is not set +# CONFIG_IIO_SW_TRIGGER is not set + +# +# Accelerometers +# +# CONFIG_ADXL345_I2C is not set +# CONFIG_ADXL372_I2C is not set +# CONFIG_BMA180 is not set +# CONFIG_BMC150_ACCEL is not set +# CONFIG_DA280 is not set +# CONFIG_DA311 is not set +# CONFIG_DMARD09 is not set +# CONFIG_DMARD10 is not set +# CONFIG_IIO_ST_ACCEL_3AXIS is not set +# CONFIG_KXSD9 is not set +# CONFIG_KXCJK1013 is not set +# CONFIG_MC3230 is not set +# CONFIG_MMA7455_I2C is not set +# CONFIG_MMA7660 is not set +# CONFIG_MMA8452 is not set +# CONFIG_MMA9551 is not set +# CONFIG_MMA9553 is not set +# CONFIG_MXC4005 is not set +# CONFIG_MXC6255 is not set +# CONFIG_STK8312 is not set +# CONFIG_STK8BA50 is not set +# end of Accelerometers + +# +# Analog to digital converters +# +# CONFIG_AD7291 is not set +# CONFIG_AD7606_IFACE_PARALLEL is not set +# CONFIG_AD799X is not set +# CONFIG_HX711 is not set +# CONFIG_INA2XX_ADC is not set +# CONFIG_LTC2471 is not set +# CONFIG_LTC2485 is not set +# CONFIG_LTC2497 is not set +# CONFIG_MAX1363 is not set +# CONFIG_MAX9611 is not set +# CONFIG_MCP3422 is not set +# CONFIG_NAU7802 is not set +# CONFIG_TI_ADC081C is not set +# CONFIG_TI_ADS1015 is not set +# CONFIG_XILINX_XADC is not set +# end of Analog to digital converters + +# +# Analog Front Ends +# +# end of Analog Front Ends + +# +# Amplifiers +# +# end of Amplifiers + +# +# Chemical Sensors +# +# CONFIG_ATLAS_PH_SENSOR is not set +# CONFIG_BME680 is not set +# CONFIG_CCS811 is not set +# CONFIG_IAQCORE is not set +# CONFIG_SENSIRION_SGP30 is not set +# CONFIG_SPS30 is not set +# CONFIG_VZ89X is not set +# end of Chemical Sensors + +# +# Hid Sensor IIO Common +# +# end of Hid Sensor IIO Common + +# +# SSP Sensor Common +# +# end of SSP Sensor Common + +# +# Digital to analog converters +# +# CONFIG_AD5064 is not set +# CONFIG_AD5380 is not set +# CONFIG_AD5446 is not set +# CONFIG_AD5593R is not set +# CONFIG_AD5696_I2C is not set +# CONFIG_DS4424 is not set +# CONFIG_M62332 is not set +# CONFIG_MAX517 is not set +# CONFIG_MCP4725 is not set +# CONFIG_TI_DAC5571 is not set +# end of Digital to analog converters + +# +# IIO dummy driver +# +# end of IIO dummy driver + +# +# Frequency Synthesizers DDS/PLL +# + +# +# Clock Generator/Distribution +# +# end of Clock Generator/Distribution + +# +# Phase-Locked Loop (PLL) frequency synthesizers +# +# end of Phase-Locked Loop (PLL) frequency synthesizers +# end of Frequency Synthesizers DDS/PLL + +# +# Digital gyroscope sensors +# +# CONFIG_BMG160 is not set +# CONFIG_FXAS21002C is not set +# CONFIG_MPU3050_I2C is not set +# CONFIG_IIO_ST_GYRO_3AXIS is not set +# CONFIG_ITG3200 is not set +# end of Digital gyroscope sensors + +# +# Health Sensors +# + +# +# Heart Rate Monitors +# +# CONFIG_AFE4404 is not set +# CONFIG_MAX30100 is not set +# CONFIG_MAX30102 is not set +# end of Heart Rate Monitors +# end of Health Sensors + +# +# Humidity sensors +# +# CONFIG_AM2315 is not set +# CONFIG_DHT11 is not set +# CONFIG_HDC100X is not set +# CONFIG_HTS221 is not set +# CONFIG_HTU21 is not set +# CONFIG_SI7005 is not set +# CONFIG_SI7020 is not set +# end of Humidity sensors + +# +# Inertial measurement units +# +# CONFIG_BMI160_I2C is not set +# CONFIG_KMX61 is not set +# CONFIG_INV_MPU6050_I2C is not set +# CONFIG_IIO_ST_LSM6DSX is not set +# end of Inertial measurement units + +# +# Light sensors +# +# CONFIG_ACPI_ALS is not set +# CONFIG_ADJD_S311 is not set +# CONFIG_AL3320A is not set +# CONFIG_APDS9300 is not set +# CONFIG_APDS9960 is not set +# CONFIG_BH1750 is not set +# CONFIG_BH1780 is not set +# CONFIG_CM32181 is not set +# CONFIG_CM3232 is not set +# CONFIG_CM3323 is not set +# CONFIG_CM36651 is not set +# CONFIG_GP2AP020A00F is not set +# CONFIG_SENSORS_ISL29018 is not set +# CONFIG_SENSORS_ISL29028 is not set +# CONFIG_ISL29125 is not set +# CONFIG_JSA1212 is not set +# CONFIG_RPR0521 is not set +# CONFIG_LTR501 is not set +# CONFIG_LV0104CS is not set +# CONFIG_MAX44000 is not set +# CONFIG_MAX44009 is not set +# CONFIG_NOA1305 is not set +# CONFIG_OPT3001 is not set +# CONFIG_PA12203001 is not set +# CONFIG_SI1133 is not set +# CONFIG_SI1145 is not set +# CONFIG_STK3310 is not set +# CONFIG_ST_UVIS25 is not set +# CONFIG_TCS3414 is not set +# CONFIG_TCS3472 is not set +# CONFIG_SENSORS_TSL2563 is not set +# CONFIG_TSL2583 is not set +# CONFIG_TSL2772 is not set +# CONFIG_TSL4531 is not set +# CONFIG_US5182D is not set +# CONFIG_VCNL4000 is not set +# CONFIG_VCNL4035 is not set +# CONFIG_VEML6070 is not set +# CONFIG_VL6180 is not set +# CONFIG_ZOPT2201 is not set +# end of Light sensors + +# +# Magnetometer sensors +# +# CONFIG_AK8975 is not set +# CONFIG_AK09911 is not set +# CONFIG_BMC150_MAGN_I2C is not set +# CONFIG_MAG3110 is not set +# CONFIG_MMC35240 is not set +# CONFIG_IIO_ST_MAGN_3AXIS is not set +# CONFIG_SENSORS_HMC5843_I2C is not set +# CONFIG_SENSORS_RM3100_I2C is not set +# end of Magnetometer sensors + +# +# Multiplexers +# +# end of Multiplexers + +# +# Inclinometer sensors +# +# end of Inclinometer sensors + +# +# Triggers - standalone +# +# CONFIG_IIO_INTERRUPT_TRIGGER is not set +# CONFIG_IIO_SYSFS_TRIGGER is not set +# end of Triggers - standalone + +# +# Digital potentiometers +# +# CONFIG_AD5272 is not set +# CONFIG_DS1803 is not set +# CONFIG_MAX5432 is not set +# CONFIG_MCP4018 is not set +# CONFIG_MCP4531 is not set +# CONFIG_TPL0102 is not set +# end of Digital potentiometers + +# +# Digital potentiostats +# +# CONFIG_LMP91000 is not set +# end of Digital potentiostats + +# +# Pressure sensors +# +# CONFIG_ABP060MG is not set +# CONFIG_BMP280 is not set +# CONFIG_DPS310 is not set +# CONFIG_HP03 is not set +# CONFIG_MPL115_I2C is not set +CONFIG_MPL3115=m +# CONFIG_MS5611 is not set +# CONFIG_MS5637 is not set +# CONFIG_IIO_ST_PRESS is not set +# CONFIG_T5403 is not set +# CONFIG_HP206C is not set +# CONFIG_ZPA2326 is not set +# end of Pressure sensors + +# +# Lightning sensors +# +# end of Lightning sensors + +# +# Proximity and distance sensors +# +# CONFIG_ISL29501 is not set +# CONFIG_LIDAR_LITE_V2 is not set +# CONFIG_MB1232 is not set +# CONFIG_RFD77402 is not set +# CONFIG_SRF04 is not set +# CONFIG_SX9500 is not set +# CONFIG_SRF08 is not set +# CONFIG_VL53L0X_I2C is not set +# end of Proximity and distance sensors + +# +# Resolver to digital converters +# +# end of Resolver to digital converters + +# +# Temperature sensors +# +# CONFIG_MLX90614 is not set +# CONFIG_MLX90632 is not set +# CONFIG_TMP006 is not set +# CONFIG_TMP007 is not set +# CONFIG_TSYS01 is not set +# CONFIG_TSYS02D is not set +# end of Temperature sensors + # CONFIG_NTB is not set # CONFIG_VME_BUS is not set # CONFIG_PWM is not set @@ -4013,6 +4371,7 @@ CONFIG_INTEL_IOMMU_FLOPPY_WA=y # CONFIG_BCM_KONA_USB2_PHY is not set # CONFIG_PHY_PXA_28NM_HSIC is not set # CONFIG_PHY_PXA_28NM_USB2 is not set +# CONFIG_PHY_CPCAP_USB is not set # end of PHY Subsystem # CONFIG_POWERCAP is not set @@ -4880,3 +5239,4 @@ CONFIG_X86_DEBUG_FPU=y CONFIG_UNWINDER_ORC=y # CONFIG_UNWINDER_FRAME_POINTER is not set # end of Kernel hacking +# CONFIG_TRUSTED_KEYS is not set diff --git a/packages/platforms/celestica/x86-64/ds4101/Makefile b/packages/platforms/celestica/x86-64/ds4101/Makefile new file mode 100644 index 0000000000..dc1e7b86f0 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk diff --git a/packages/platforms/celestica/x86-64/ds4101/modules/Makefile b/packages/platforms/celestica/x86-64/ds4101/modules/Makefile new file mode 100644 index 0000000000..003238cf6d --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/modules/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk \ No newline at end of file diff --git a/packages/platforms/celestica/x86-64/ds4101/modules/PKG.yml b/packages/platforms/celestica/x86-64/ds4101/modules/PKG.yml new file mode 100644 index 0000000000..b3a765409c --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/modules/PKG.yml @@ -0,0 +1 @@ +!include $ONL_TEMPLATES/platform-modules.yml ARCH=amd64 VENDOR=celestica BASENAME=x86-64-cls-ds4101 KERNELS="onl-kernel-5.4-lts-x86-64-all:amd64" diff --git a/packages/platforms/celestica/x86-64/ds4101/modules/builds/Makefile b/packages/platforms/celestica/x86-64/ds4101/modules/builds/Makefile new file mode 100644 index 0000000000..7eb25da6b4 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/modules/builds/Makefile @@ -0,0 +1,6 @@ +KERNELS := onl-kernel-5.4-lts-x86-64-all:amd64 +KMODULES := src +VENDOR := celestica +BASENAME := x86-64-cls-ds4101 +ARCH := x86_64 +include $(ONL)/make/kmodule.mk diff --git a/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/Makefile b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/Makefile new file mode 100644 index 0000000000..02d521fbd7 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/Makefile @@ -0,0 +1,2 @@ +KBUILD_CFLAGS+=-DDS4101 -Idrivers/hwmon/pmbus/ +obj-m := fpga-device.o fpga-i2c-xiic.o fpga-sys.o fpga-xcvr.o lpc-basecpld.o i2c-xcvr.o watch_dog.o mp2880.o mp5023.o mp2975.o platform-fan.o platform-psu.o at24.o diff --git a/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/at24.c b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/at24.c new file mode 100644 index 0000000000..170838953a --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/at24.c @@ -0,0 +1,770 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * at24.c - handle most I2C EEPROMs + * + * Copyright (C) 2005-2007 David Brownell + * Copyright (C) 2008 Wolfram Sang, Pengutronix + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Address pointer is 16 bit. */ +#define AT24_FLAG_ADDR16 BIT(7) +/* sysfs-entry will be read-only. */ +#define AT24_FLAG_READONLY BIT(6) +/* sysfs-entry will be world-readable. */ +#define AT24_FLAG_IRUGO BIT(5) +/* Take always 8 addresses (24c00). */ +#define AT24_FLAG_TAKE8ADDR BIT(4) +/* Factory-programmed serial number. */ +#define AT24_FLAG_SERIAL BIT(3) +/* Factory-programmed mac address. */ +#define AT24_FLAG_MAC BIT(2) +/* Does not auto-rollover reads to the next slave address. */ +#define AT24_FLAG_NO_RDROL BIT(1) + +/* + * I2C EEPROMs from most vendors are inexpensive and mostly interchangeable. + * Differences between different vendor product lines (like Atmel AT24C or + * MicroChip 24LC, etc) won't much matter for typical read/write access. + * There are also I2C RAM chips, likewise interchangeable. One example + * would be the PCF8570, which acts like a 24c02 EEPROM (256 bytes). + * + * However, misconfiguration can lose data. "Set 16-bit memory address" + * to a part with 8-bit addressing will overwrite data. Writing with too + * big a page size also loses data. And it's not safe to assume that the + * conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC + * uses 0x51, for just one example. + * + * Accordingly, explicit board-specific configuration data should be used + * in almost all cases. (One partial exception is an SMBus used to access + * "SPD" data for DRAM sticks. Those only use 24c02 EEPROMs.) + * + * So this driver uses "new style" I2C driver binding, expecting to be + * told what devices exist. That may be in arch/X/mach-Y/board-Z.c or + * similar kernel-resident tables; or, configuration data coming from + * a bootloader. + * + * Other than binding model, current differences from "eeprom" driver are + * that this one handles write access and isn't restricted to 24c02 devices. + * It also handles larger devices (32 kbit and up) with two-byte addresses, + * which won't work on pure SMBus systems. + */ + +struct at24_client { + struct i2c_client *client; + struct regmap *regmap; +}; + +struct at24_data { + /* + * Lock protects against activities from other Linux tasks, + * but not from changes by other I2C masters. + */ + struct mutex lock; + + unsigned int write_max; + unsigned int num_addresses; + unsigned int offset_adj; + + u32 byte_len; + u16 page_size; + u8 flags; + + struct nvmem_device *nvmem; + + struct gpio_desc *wp_gpio; + + /* + * Some chips tie up multiple I2C addresses; dummy devices reserve + * them for us, and we'll use them with SMBus calls. + */ + struct at24_client client[]; +}; + +/* + * This parameter is to help this driver avoid blocking other drivers out + * of I2C for potentially troublesome amounts of time. With a 100 kHz I2C + * clock, one 256 byte read takes about 1/43 second which is excessive; + * but the 1/170 second it takes at 400 kHz may be quite reasonable; and + * at 1 MHz (Fm+) a 1/430 second delay could easily be invisible. + * + * This value is forced to be a power of two so that writes align on pages. + */ +static unsigned int at24_io_limit = 128; +module_param_named(io_limit, at24_io_limit, uint, 0); +MODULE_PARM_DESC(at24_io_limit, "Maximum bytes per I/O (default 128)"); + +/* + * Specs often allow 5 msec for a page write, sometimes 20 msec; + * it's important to recover from write timeouts. + */ +static unsigned int at24_write_timeout = 25; +module_param_named(write_timeout, at24_write_timeout, uint, 0); +MODULE_PARM_DESC(at24_write_timeout, "Time (in ms) to try writes (default 25)"); + +struct at24_chip_data { + u32 byte_len; + u8 flags; +}; + +#define AT24_CHIP_DATA(_name, _len, _flags) \ + static const struct at24_chip_data _name = { \ + .byte_len = _len, .flags = _flags, \ + } + +/* needs 8 addresses as A0-A2 are ignored */ +AT24_CHIP_DATA(at24_data_24c00, 128 / 8, AT24_FLAG_TAKE8ADDR); +/* old variants can't be handled with this generic entry! */ +AT24_CHIP_DATA(at24_data_24c01, 1024 / 8, 0); +AT24_CHIP_DATA(at24_data_24cs01, 16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24c02, 2048 / 8, 0); +AT24_CHIP_DATA(at24_data_24cs02, 16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24mac402, 48 / 8, + AT24_FLAG_MAC | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24mac602, 64 / 8, + AT24_FLAG_MAC | AT24_FLAG_READONLY); +/* spd is a 24c02 in memory DIMMs */ +AT24_CHIP_DATA(at24_data_spd, 2048 / 8, + AT24_FLAG_READONLY | AT24_FLAG_IRUGO); +AT24_CHIP_DATA(at24_data_24c04, 4096 / 8, 0); +AT24_CHIP_DATA(at24_data_24cs04, 16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +/* 24rf08 quirk is handled at i2c-core */ +AT24_CHIP_DATA(at24_data_24c08, 8192 / 8, 0); +AT24_CHIP_DATA(at24_data_24cs08, 16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24c16, 16384 / 8, 0); +AT24_CHIP_DATA(at24_data_24cs16, 16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24c32, 32768 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24cs32, 16, + AT24_FLAG_ADDR16 | AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24c64, 65536 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24cs64, 16, + AT24_FLAG_ADDR16 | AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24c128, 131072 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24c256, 262144 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24c512, 524288 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24c1024, 1048576 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24c2048, 2097152 / 8, AT24_FLAG_ADDR16); +/* identical to 24c08 ? */ +AT24_CHIP_DATA(at24_data_INT3499, 8192 / 8, 0); + +static const struct i2c_device_id at24_ids[] = { + { "24c00", (kernel_ulong_t)&at24_data_24c00 }, + { "24c01", (kernel_ulong_t)&at24_data_24c01 }, + { "24cs01", (kernel_ulong_t)&at24_data_24cs01 }, + { "24c02", (kernel_ulong_t)&at24_data_24c02 }, + { "24cs02", (kernel_ulong_t)&at24_data_24cs02 }, + { "24mac402", (kernel_ulong_t)&at24_data_24mac402 }, + { "24mac602", (kernel_ulong_t)&at24_data_24mac602 }, + { "spd", (kernel_ulong_t)&at24_data_spd }, + { "24c04", (kernel_ulong_t)&at24_data_24c04 }, + { "24cs04", (kernel_ulong_t)&at24_data_24cs04 }, + { "24c08", (kernel_ulong_t)&at24_data_24c08 }, + { "24cs08", (kernel_ulong_t)&at24_data_24cs08 }, + { "24c16", (kernel_ulong_t)&at24_data_24c16 }, + { "24cs16", (kernel_ulong_t)&at24_data_24cs16 }, + { "24c32", (kernel_ulong_t)&at24_data_24c32 }, + { "24cs32", (kernel_ulong_t)&at24_data_24cs32 }, + { "24c64", (kernel_ulong_t)&at24_data_24c64 }, + { "24cs64", (kernel_ulong_t)&at24_data_24cs64 }, + { "24c128", (kernel_ulong_t)&at24_data_24c128 }, + { "24c256", (kernel_ulong_t)&at24_data_24c256 }, + { "24c512", (kernel_ulong_t)&at24_data_24c512 }, + { "24c1024", (kernel_ulong_t)&at24_data_24c1024 }, + { "24c2048", (kernel_ulong_t)&at24_data_24c2048 }, + { "at24", 0 }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(i2c, at24_ids); + +static const struct of_device_id at24_of_match[] = { + { .compatible = "atmel,24c00", .data = &at24_data_24c00 }, + { .compatible = "atmel,24c01", .data = &at24_data_24c01 }, + { .compatible = "atmel,24cs01", .data = &at24_data_24cs01 }, + { .compatible = "atmel,24c02", .data = &at24_data_24c02 }, + { .compatible = "atmel,24cs02", .data = &at24_data_24cs02 }, + { .compatible = "atmel,24mac402", .data = &at24_data_24mac402 }, + { .compatible = "atmel,24mac602", .data = &at24_data_24mac602 }, + { .compatible = "atmel,spd", .data = &at24_data_spd }, + { .compatible = "atmel,24c04", .data = &at24_data_24c04 }, + { .compatible = "atmel,24cs04", .data = &at24_data_24cs04 }, + { .compatible = "atmel,24c08", .data = &at24_data_24c08 }, + { .compatible = "atmel,24cs08", .data = &at24_data_24cs08 }, + { .compatible = "atmel,24c16", .data = &at24_data_24c16 }, + { .compatible = "atmel,24cs16", .data = &at24_data_24cs16 }, + { .compatible = "atmel,24c32", .data = &at24_data_24c32 }, + { .compatible = "atmel,24cs32", .data = &at24_data_24cs32 }, + { .compatible = "atmel,24c64", .data = &at24_data_24c64 }, + { .compatible = "atmel,24cs64", .data = &at24_data_24cs64 }, + { .compatible = "atmel,24c128", .data = &at24_data_24c128 }, + { .compatible = "atmel,24c256", .data = &at24_data_24c256 }, + { .compatible = "atmel,24c512", .data = &at24_data_24c512 }, + { .compatible = "atmel,24c1024", .data = &at24_data_24c1024 }, + { .compatible = "atmel,24c2048", .data = &at24_data_24c2048 }, + { /* END OF LIST */ }, +}; +MODULE_DEVICE_TABLE(of, at24_of_match); + +static const struct acpi_device_id at24_acpi_ids[] = { + { "INT3499", (kernel_ulong_t)&at24_data_INT3499 }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(acpi, at24_acpi_ids); + +/* + * This routine supports chips which consume multiple I2C addresses. It + * computes the addressing information to be used for a given r/w request. + * Assumes that sanity checks for offset happened at sysfs-layer. + * + * Slave address and byte offset derive from the offset. Always + * set the byte address; on a multi-master board, another master + * may have changed the chip's "current" address pointer. + */ +static struct at24_client *at24_translate_offset(struct at24_data *at24, + unsigned int *offset) +{ + unsigned int i; + + if (at24->flags & AT24_FLAG_ADDR16) { + i = *offset >> 16; + *offset &= 0xffff; + } else { + i = *offset >> 8; + *offset &= 0xff; + } + + return &at24->client[i]; +} + +static struct device *at24_base_client_dev(struct at24_data *at24) +{ + return &at24->client[0].client->dev; +} + +static size_t at24_adjust_read_count(struct at24_data *at24, + unsigned int offset, size_t count) +{ + unsigned int bits; + size_t remainder; + + /* + * In case of multi-address chips that don't rollover reads to + * the next slave address: truncate the count to the slave boundary, + * so that the read never straddles slaves. + */ + if (at24->flags & AT24_FLAG_NO_RDROL) { + bits = (at24->flags & AT24_FLAG_ADDR16) ? 16 : 8; + remainder = BIT(bits) - offset; + if (count > remainder) + count = remainder; + } + + if (count > at24_io_limit) + count = at24_io_limit; + + return count; +} + +static ssize_t at24_regmap_read(struct at24_data *at24, char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, read_time; + struct at24_client *at24_client; + struct i2c_client *client; + struct regmap *regmap; + int ret; + + at24_client = at24_translate_offset(at24, &offset); + regmap = at24_client->regmap; + client = at24_client->client; + count = at24_adjust_read_count(at24, offset, count); + + /* adjust offset for mac and serial read ops */ + offset += at24->offset_adj; + + timeout = jiffies + msecs_to_jiffies(at24_write_timeout); + do { + /* + * The timestamp shall be taken before the actual operation + * to avoid a premature timeout in case of high CPU load. + */ + read_time = jiffies; + + ret = regmap_bulk_read(regmap, offset, buf, count); + dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n", + count, offset, ret, jiffies); + if (!ret) + return count; + + usleep_range(1000, 1500); + } while (time_before(read_time, timeout)); + + return -ETIMEDOUT; +} + +/* + * Note that if the hardware write-protect pin is pulled high, the whole + * chip is normally write protected. But there are plenty of product + * variants here, including OTP fuses and partial chip protect. + * + * We only use page mode writes; the alternative is sloooow. These routines + * write at most one page. + */ + +static size_t at24_adjust_write_count(struct at24_data *at24, + unsigned int offset, size_t count) +{ + unsigned int next_page; + + /* write_max is at most a page */ + if (count > at24->write_max) + count = at24->write_max; + + /* Never roll over backwards, to the start of this page */ + next_page = roundup(offset + 1, at24->page_size); + if (offset + count > next_page) + count = next_page - offset; + + return count; +} + +static ssize_t at24_regmap_write(struct at24_data *at24, const char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, write_time; + struct at24_client *at24_client; + struct i2c_client *client; + struct regmap *regmap; + int ret; + + at24_client = at24_translate_offset(at24, &offset); + regmap = at24_client->regmap; + client = at24_client->client; + count = at24_adjust_write_count(at24, offset, count); + timeout = jiffies + msecs_to_jiffies(at24_write_timeout); + + do { + /* + * The timestamp shall be taken before the actual operation + * to avoid a premature timeout in case of high CPU load. + */ + write_time = jiffies; + + ret = regmap_bulk_write(regmap, offset, buf, count); + dev_dbg(&client->dev, "write %zu@%d --> %d (%ld)\n", + count, offset, ret, jiffies); + if (!ret) + return count; + + usleep_range(1000, 1500); + } while (time_before(write_time, timeout)); + + return -ETIMEDOUT; +} + +static int at24_read(void *priv, unsigned int off, void *val, size_t count) +{ + struct at24_data *at24; + struct device *dev; + char *buf = val; + int ret; + + at24 = priv; + dev = at24_base_client_dev(at24); + + if (unlikely(!count)) + return count; + + if (off + count > at24->byte_len) + return -EINVAL; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + return ret; + } + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&at24->lock); + + while (count) { + ret = at24_regmap_read(at24, buf, off, count); + if (ret < 0) { + mutex_unlock(&at24->lock); + pm_runtime_put(dev); + return ret; + } + buf += ret; + off += ret; + count -= ret; + } + + mutex_unlock(&at24->lock); + + pm_runtime_put(dev); + + return 0; +} + +static int at24_write(void *priv, unsigned int off, void *val, size_t count) +{ + struct at24_data *at24; + struct device *dev; + char *buf = val; + int ret; + + at24 = priv; + dev = at24_base_client_dev(at24); + + if (unlikely(!count)) + return -EINVAL; + + if (off + count > at24->byte_len) + return -EINVAL; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + return ret; + } + + /* + * Write data to chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&at24->lock); + gpiod_set_value_cansleep(at24->wp_gpio, 0); + + while (count) { + ret = at24_regmap_write(at24, buf, off, count); + if (ret < 0) { + gpiod_set_value_cansleep(at24->wp_gpio, 1); + mutex_unlock(&at24->lock); + pm_runtime_put(dev); + return ret; + } + /* + * EEPROM chip in ds4101 project doesn't support + * quick write, so add some delay here. + */ + msleep(5); + buf += ret; + off += ret; + count -= ret; + } + + gpiod_set_value_cansleep(at24->wp_gpio, 1); + mutex_unlock(&at24->lock); + + pm_runtime_put(dev); + + return 0; +} + +static const struct at24_chip_data *at24_get_chip_data(struct device *dev) +{ + struct device_node *of_node = dev->of_node; + const struct at24_chip_data *cdata; + const struct i2c_device_id *id; + + id = i2c_match_id(at24_ids, to_i2c_client(dev)); + + /* + * The I2C core allows OF nodes compatibles to match against the + * I2C device ID table as a fallback, so check not only if an OF + * node is present but also if it matches an OF device ID entry. + */ + if (of_node && of_match_device(at24_of_match, dev)) + cdata = of_device_get_match_data(dev); + else if (id) + cdata = (void *)id->driver_data; + else + cdata = acpi_device_get_match_data(dev); + + if (!cdata) + return ERR_PTR(-ENODEV); + + return cdata; +} + +static int at24_make_dummy_client(struct at24_data *at24, unsigned int index, + struct regmap_config *regmap_config) +{ + struct i2c_client *base_client, *dummy_client; + struct regmap *regmap; + struct device *dev; + + base_client = at24->client[0].client; + dev = &base_client->dev; + + dummy_client = devm_i2c_new_dummy_device(dev, base_client->adapter, + base_client->addr + index); + if (IS_ERR(dummy_client)) + return PTR_ERR(dummy_client); + + regmap = devm_regmap_init_i2c(dummy_client, regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + at24->client[index].client = dummy_client; + at24->client[index].regmap = regmap; + + return 0; +} + +static unsigned int at24_get_offset_adj(u8 flags, unsigned int byte_len) +{ + if (flags & AT24_FLAG_MAC) { + /* EUI-48 starts from 0x9a, EUI-64 from 0x98 */ + return 0xa0 - byte_len; + } else if (flags & AT24_FLAG_SERIAL && flags & AT24_FLAG_ADDR16) { + /* + * For 16 bit address pointers, the word address must contain + * a '10' sequence in bits 11 and 10 regardless of the + * intended position of the address pointer. + */ + return 0x0800; + } else if (flags & AT24_FLAG_SERIAL) { + /* + * Otherwise the word address must begin with a '10' sequence, + * regardless of the intended address. + */ + return 0x0080; + } else { + return 0; + } +} + +static int at24_probe(struct i2c_client *client) +{ + struct regmap_config regmap_config = { }; + struct nvmem_config nvmem_config = { }; + u32 byte_len, page_size, flags, addrw; + const struct at24_chip_data *cdata; + struct device *dev = &client->dev; + bool i2c_fn_i2c, i2c_fn_block; + unsigned int i, num_addresses; + struct at24_data *at24; + struct regmap *regmap; + bool writable; + u8 test_byte; + int err; + + i2c_fn_i2c = i2c_check_functionality(client->adapter, I2C_FUNC_I2C); + i2c_fn_block = i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK); + + cdata = at24_get_chip_data(dev); + if (IS_ERR(cdata)) + return PTR_ERR(cdata); + + err = device_property_read_u32(dev, "pagesize", &page_size); + if (err) + /* + * This is slow, but we can't know all eeproms, so we better + * play safe. Specifying custom eeprom-types via device tree + * or properties is recommended anyhow. + */ + page_size = 1; + + flags = cdata->flags; + if (device_property_present(dev, "read-only")) + flags |= AT24_FLAG_READONLY; + if (device_property_present(dev, "no-read-rollover")) + flags |= AT24_FLAG_NO_RDROL; + + err = device_property_read_u32(dev, "address-width", &addrw); + if (!err) { + switch (addrw) { + case 8: + if (flags & AT24_FLAG_ADDR16) + dev_warn(dev, + "Override address width to be 8, while default is 16\n"); + flags &= ~AT24_FLAG_ADDR16; + break; + case 16: + flags |= AT24_FLAG_ADDR16; + break; + default: + dev_warn(dev, "Bad \"address-width\" property: %u\n", + addrw); + } + } + + err = device_property_read_u32(dev, "size", &byte_len); + if (err) + byte_len = cdata->byte_len; + + if (!i2c_fn_i2c && !i2c_fn_block) + page_size = 1; + + if (!page_size) { + dev_err(dev, "page_size must not be 0!\n"); + return -EINVAL; + } + + if (!is_power_of_2(page_size)) + dev_warn(dev, "page_size looks suspicious (no power of 2)!\n"); + + err = device_property_read_u32(dev, "num-addresses", &num_addresses); + if (err) { + if (flags & AT24_FLAG_TAKE8ADDR) + num_addresses = 8; + else + num_addresses = DIV_ROUND_UP(byte_len, + (flags & AT24_FLAG_ADDR16) ? 65536 : 256); + } + + if ((flags & AT24_FLAG_SERIAL) && (flags & AT24_FLAG_MAC)) { + dev_err(dev, + "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC."); + return -EINVAL; + } + + regmap_config.val_bits = 8; + regmap_config.reg_bits = (flags & AT24_FLAG_ADDR16) ? 16 : 8; + regmap_config.disable_locking = true; + + regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + at24 = devm_kzalloc(dev, struct_size(at24, client, num_addresses), + GFP_KERNEL); + if (!at24) + return -ENOMEM; + + mutex_init(&at24->lock); + at24->byte_len = byte_len; + at24->page_size = page_size; + at24->flags = flags; + at24->num_addresses = num_addresses; + at24->offset_adj = at24_get_offset_adj(flags, byte_len); + at24->client[0].client = client; + at24->client[0].regmap = regmap; + + at24->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_HIGH); + if (IS_ERR(at24->wp_gpio)) + return PTR_ERR(at24->wp_gpio); + + writable = !(flags & AT24_FLAG_READONLY); + if (writable) { + at24->write_max = min_t(unsigned int, + page_size, at24_io_limit); + if (!i2c_fn_i2c && at24->write_max > I2C_SMBUS_BLOCK_MAX) + at24->write_max = I2C_SMBUS_BLOCK_MAX; + } + + /* use dummy devices for multiple-address chips */ + for (i = 1; i < num_addresses; i++) { + err = at24_make_dummy_client(at24, i, ®map_config); + if (err) + return err; + } + + nvmem_config.name = dev_name(dev); + nvmem_config.dev = dev; + nvmem_config.read_only = !writable; + nvmem_config.root_only = !(flags & AT24_FLAG_IRUGO); + nvmem_config.owner = THIS_MODULE; + nvmem_config.compat = true; + nvmem_config.base_dev = dev; + nvmem_config.reg_read = at24_read; + nvmem_config.reg_write = at24_write; + nvmem_config.priv = at24; + nvmem_config.stride = 1; + nvmem_config.word_size = 1; + nvmem_config.size = byte_len; + + at24->nvmem = devm_nvmem_register(dev, &nvmem_config); + if (IS_ERR(at24->nvmem)) + return PTR_ERR(at24->nvmem); + + i2c_set_clientdata(client, at24); + + /* enable runtime pm */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + /* + * Perform a one-byte test read to verify that the + * chip is functional. + */ + err = at24_read(at24, 0, &test_byte, 1); + pm_runtime_idle(dev); + if (err) { + pm_runtime_disable(dev); + return -ENODEV; + } + + dev_info(dev, "%u byte %s EEPROM, %s, %u bytes/write\n", + byte_len, client->name, + writable ? "writable" : "read-only", at24->write_max); + + return 0; +} + +static int at24_remove(struct i2c_client *client) +{ + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +static struct i2c_driver at24_driver = { + .driver = { + .name = "at24", + .of_match_table = at24_of_match, + .acpi_match_table = ACPI_PTR(at24_acpi_ids), + }, + .probe_new = at24_probe, + .remove = at24_remove, + .id_table = at24_ids, +}; + +static int __init at24_init(void) +{ + if (!at24_io_limit) { + pr_err("at24: at24_io_limit must not be 0!\n"); + return -EINVAL; + } + + at24_io_limit = rounddown_pow_of_two(at24_io_limit); + return i2c_add_driver(&at24_driver); +} +module_init(at24_init); + +static void __exit at24_exit(void) +{ + i2c_del_driver(&at24_driver); +} +module_exit(at24_exit); + +MODULE_DESCRIPTION("Driver for most I2C EEPROMs"); +MODULE_AUTHOR("David Brownell and Wolfram Sang"); +MODULE_LICENSE("GPL"); diff --git a/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/fpga-device.c b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/fpga-device.c new file mode 100644 index 0000000000..f1f42518f9 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/fpga-device.c @@ -0,0 +1,497 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * fpga-device.c - PCI device driver for DS4101 Switch board FPGA. + * + * Author: Nicholas Wu + * + * Copyright (C) 2022-2024 Celestica 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 Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fpga-xcvr.h" + +#define MOD_VERSION "0.0.2" +#define DRV_NAME "fpga" + +#define PCI_VENDOR_ID_CELESTICA 0x18d4 +#define FPGA_PCIE_DEVICE_ID 0x100a + +#define FPGA_OTHER_CR_ADDR 0x14 + +#define BMC_PRESENT_BIT 0x08 + +/* FPGA_OTHER_CR_ADDR bit8 0-bmc present 1-bmc absent*/ +#define BMC_PRESENT 0x00 + +#define MMIO_BAR 0 + +/* I2C ocore configurations */ +#define OCORE_REGSHIFT 2 +#define OCORE_IP_CLK_khz 62500 +#define OCORE_BUS_CLK_khz 100 +#define OCORE_REG_IO_WIDTH 1 + +/* Optical port xcvr configuration */ +#define XCVR_REG_SHIFT 2 +#define XCVR_NUM_PORT 32 +#define XCVR_PORT_REG_SIZE 0x10 + +/* i2c_bus_config - an i2c-core resource and platform data + * @id - I2C bus device ID, for identification. + * @res - resources for an i2c-core device. + * @num_res - size of the resources. + */ +struct i2c_bus_config { + int id; + struct resource *res; + ssize_t num_res; +}; + +/* switchbrd_priv - switchboard private data */ +struct switchbrd_priv { + void __iomem *iomem; + unsigned long base; + int num_i2c_bus; + const char *i2c_devname; + const char *xcvr_devname; + const char *fpga_devname; + struct platform_device **i2cbuses_pdev; + struct platform_device *regio_pdev; + struct platform_device *spiflash_pdev; + struct platform_device *xcvr_pdev; + struct platform_device *fpga_pdev; +}; + +/* RESOURCE SEPERATES BY FUNCTION */ +/* Resource IOMEM for FPGA extended i2c bus 0 */ +static struct resource cls_i2c_res_0[] = { + { + .start = 0x00010000, .end = 0x00010FFF, + .flags = IORESOURCE_MEM,}, +}; + +/* Resource IOMEM for FPGA extended i2c bus 1 */ +static struct resource cls_i2c_res_1[] = { + { + .start = 0x00011000, .end = 0x00011FFF, + .flags = IORESOURCE_MEM,}, +}; + +/* Resource IOMEM for FPGA extended i2c bus 2 */ +static struct resource cls_i2c_res_2[] = { + { + .start = 0x00012000, .end = 0x00012FFF, + .flags = IORESOURCE_MEM,}, +}; + +/* Resource IOMEM for FPGA extended i2c bus 3 */ +static struct resource cls_i2c_res_3[] = { + { + .start = 0x00013000, .end = 0x00013FFF, + .flags = IORESOURCE_MEM,}, +}; + +/* Resource IOMEM for FPGA extended i2c bus 4 */ +static struct resource cls_i2c_res_4[] = { + { + .start = 0x00014000, .end = 0x00014FFF, + .flags = IORESOURCE_MEM,}, +}; + +/* Resource IOMEM for FPGA extended i2c bus 5 */ +static struct resource cls_i2c_res_5[] = { + { + .start = 0x00015000, .end = 0x00015FFF, + .flags = IORESOURCE_MEM,}, +}; + +/* Resource IOMEM for FPGA extended i2c bus 6 */ +static struct resource cls_i2c_res_6[] = { + { + .start = 0x00016000, .end = 0x00016FFF, + .flags = IORESOURCE_MEM,}, +}; + +/* Resource IOMEM for FPGA extended i2c bus 7 */ +static struct resource cls_i2c_res_7[] = { + { + .start = 0x00017000, .end = 0x00017FFF, + .flags = IORESOURCE_MEM,}, +}; + +/* Resource IOMEM for FPGA extended i2c bus 8 */ +static struct resource cls_i2c_res_8[] = { + { + .start = 0x00018000, .end = 0x00018FFF, + .flags = IORESOURCE_MEM,}, +}; + +/* Resource IOMEM for FPGA extended i2c bus 9 */ +static struct resource cls_i2c_res_9[] = { + { + .start = 0x00019000, .end = 0x00019FFF, + .flags = IORESOURCE_MEM,}, +}; + +/* Resource IOMEM for FPGA extended i2c bus 10 */ +static struct resource cls_i2c_res_10[] = { + { + .start = 0x0001A000, .end = 0x0001AFFF, + .flags = IORESOURCE_MEM,}, +}; + +/* Resource IOMEM for FPGA extended i2c bus 11 */ +static struct resource cls_i2c_res_11[] = { + { + .start = 0x0001B000, .end = 0x0001BFFF, + .flags = IORESOURCE_MEM,}, +}; + +/* Resource IOMEM for FPGA extended i2c bus 12 */ +static struct resource cls_i2c_res_12[] = { + { + .start = 0x0001C000, .end = 0x0001CFFF, + .flags = IORESOURCE_MEM,}, +}; + +/* Resource IOMEM for FPGA extended i2c bus 13 */ +static struct resource cls_i2c_res_13[] = { + { + .start = 0x0001D000, .end = 0x0001DFFF, + .flags = IORESOURCE_MEM,}, +}; + +/* Resource IOMEM for front panel XCVR */ +static struct resource xcvr_res[] = { + { + .start = 0x00001000, .end = 0x00001FFF, + .flags = IORESOURCE_MEM,}, +}; + +/* Resource IOMEM for front panel XCVR */ +static struct resource fpga_res[] = { + { + .start = 0x00000000, .end = 0x01FFFFFF, + .flags = IORESOURCE_MEM,}, +}; + + +static struct i2c_bus_config i2c_bus_configs[] = { + { + .id = 0, + .res = cls_i2c_res_0, + .num_res = ARRAY_SIZE(cls_i2c_res_0), + }, + { + .id = 1, + .res = cls_i2c_res_1, + .num_res = ARRAY_SIZE(cls_i2c_res_1), + }, + { + .id = 2, + .res = cls_i2c_res_2, + .num_res = ARRAY_SIZE(cls_i2c_res_2), + }, + { + .id = 3, + .res = cls_i2c_res_3, + .num_res = ARRAY_SIZE(cls_i2c_res_3), + }, + { + .id = 4, + .res = cls_i2c_res_4, + .num_res = ARRAY_SIZE(cls_i2c_res_4), + }, + { + .id = 5, + .res = cls_i2c_res_5, + .num_res = ARRAY_SIZE(cls_i2c_res_5), + }, + { + .id = 6, + .res = cls_i2c_res_6, + .num_res = ARRAY_SIZE(cls_i2c_res_6), + }, + { + .id = 7, + .res = cls_i2c_res_7, + .num_res = ARRAY_SIZE(cls_i2c_res_7), + }, + { + .id = 8, + .res = cls_i2c_res_8, + .num_res = ARRAY_SIZE(cls_i2c_res_8), + }, + { + .id = 9, + .res = cls_i2c_res_9, + .num_res = ARRAY_SIZE(cls_i2c_res_9), + }, + { + .id = 10, + .res = cls_i2c_res_10, + .num_res = ARRAY_SIZE(cls_i2c_res_10), + }, + { + .id = 11, + .res = cls_i2c_res_11, + .num_res = ARRAY_SIZE(cls_i2c_res_11), + }, + { + .id = 12, + .res = cls_i2c_res_12, + .num_res = ARRAY_SIZE(cls_i2c_res_12), + }, + { + .id = 13, + .res = cls_i2c_res_13, + .num_res = ARRAY_SIZE(cls_i2c_res_13), + }, +}; + +/* xcvr front panel mapping */ +static struct port_info front_panel_ports[] = { + {"SFP1", 1, SFP}, + {"SFP2", 2, SFP}, + /* END OF LIST */ +}; + +static struct cls_xcvr_platform_data xcvr_data[] = { + { + .port_reg_size = 0x04, + .num_ports = ARRAY_SIZE(front_panel_ports), + .devices = front_panel_ports, + }, +}; + + +static int cls_fpga_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int err; + int num_i2c_bus, i = 0, ret; + int bmc_present = 0; /* 0-present 1-absent */ + unsigned long rstart; + void __iomem *base_addr; + struct switchbrd_priv *priv; + struct platform_device **i2cbuses_pdev; + struct platform_device *fpga_pdev; + struct platform_device *xcvr_pdev; + + err = pci_enable_device(dev); + if (err) { + dev_err(&dev->dev, "Failed to enable PCI device\n"); + goto err_exit; + } + pci_set_master(dev); + + /* Check for valid MMIO address */ + base_addr = pci_iomap(dev, MMIO_BAR, 0); + if (!base_addr) { + dev_err(&dev->dev, "Failed to map PCI device mem\n"); + err = -ENODEV; + goto err_disable_device; + } + + ret = pci_enable_msi(dev); + if (ret) { + dev_err(&dev->dev, "failed to allocate MSI entry\n"); + goto err_unmap; + } + + bmc_present = (ioread32(base_addr + FPGA_OTHER_CR_ADDR) + >> BMC_PRESENT_BIT) & 0x01; + + if (bmc_present == BMC_PRESENT) + dev_dbg(&dev->dev, "BMC present\n"); + else + dev_dbg(&dev->dev, "BMC absent\n"); + + rstart = pci_resource_start(dev, MMIO_BAR); + if (!rstart) { + dev_err(&dev->dev, + "Switchboard base address uninitialized, check FPGA\n"); + err = -ENODEV; + goto err_disable_msi; + } + + dev_dbg(&dev->dev, "BAR%d res: 0x%lx-0x%llx\n", MMIO_BAR, + rstart, pci_resource_end(dev, MMIO_BAR)); + + dev_dbg(&dev->dev, "BAR%d res: 0x%lx-0x%llx\n", MMIO_BAR, + rstart, pci_resource_end(dev, MMIO_BAR)); + + + priv = devm_kzalloc(&dev->dev, + sizeof(struct switchbrd_priv), + GFP_KERNEL); + + if (!priv) { + err = -ENOMEM; + goto err_disable_msi; + } + + pci_set_drvdata(dev, priv); + num_i2c_bus = ARRAY_SIZE(i2c_bus_configs); + i2cbuses_pdev = devm_kzalloc( + &dev->dev, + num_i2c_bus * sizeof(struct platform_device *), + GFP_KERNEL); + + fpga_res[0].start += rstart; + fpga_res[0].end += rstart; + xcvr_res[0].start += rstart; + xcvr_res[0].end += rstart; + + dev_dbg(&dev->dev, "num_i2c_bus = %x,fpga_res start/end %llx/%llx,restart=%lx\n", + num_i2c_bus, + fpga_res[0].start, + fpga_res[0].end, + rstart); + dev_dbg(&dev->dev, "num_i2c_bus = %x,xcvr_res start/end %llx/%llx,restart=%lx\n", + num_i2c_bus, + xcvr_res[0].start, + xcvr_res[0].end, + rstart); + + priv->i2c_devname = "fpga-xiic-i2c"; + priv->xcvr_devname = "fpga-xcvr"; + priv->fpga_devname = "fpga-sys"; + + fpga_pdev = platform_device_register_resndata( + NULL, + priv->fpga_devname, + -1, + fpga_res, + ARRAY_SIZE(fpga_res), + NULL, + 0); + if (IS_ERR(fpga_pdev)) { + dev_err(&dev->dev, "Failed to register fpga node\n"); + err = PTR_ERR(fpga_pdev); + goto err_unmap; + } + dev_dbg(&dev->dev, "register fpga node\n"); + + xcvr_pdev = platform_device_register_resndata( + NULL, + priv->xcvr_devname, + -1, + xcvr_res, + ARRAY_SIZE(xcvr_res), + &xcvr_data, + sizeof(xcvr_data)); + if (IS_ERR(xcvr_pdev)) { + dev_err(&dev->dev, "Failed to register xcvr node\n"); + err = PTR_ERR(xcvr_pdev); + goto err_unregister_fpga_dev; + } + dev_dbg(&dev->dev, "register xcvr node\n"); + + for (; i < num_i2c_bus; i++) { + /* override resource with MEM/IO resource offset */ + i2c_bus_configs[i].res[0].start += rstart; + i2c_bus_configs[i].res[0].end += rstart; + + dev_dbg(&dev->dev, "i2c-bus.%d: 0x%llx - 0x%llx\n", + i2c_bus_configs[i].id, + i2c_bus_configs[i].res[0].start, + i2c_bus_configs[i].res[0].end); + dev_dbg(&dev->dev, "bus id:%d, i2c_bus_configs[%d].res[0].start/end=%llx:%llx\n", + i2c_bus_configs[i].id, + i, + i2c_bus_configs[i].res[0].start, + i2c_bus_configs[i].res[0].end); + + i2cbuses_pdev[i] = platform_device_register_resndata( + &dev->dev, + priv->i2c_devname, + i2c_bus_configs[i].id, + i2c_bus_configs[i].res, + i2c_bus_configs[i].num_res, + NULL, + 0); + if (IS_ERR(i2cbuses_pdev[i])) { + dev_err(&dev->dev, "Failed to register cls-i2c-xiic.%d\n", + i2c_bus_configs[i].id); + err = PTR_ERR(i2cbuses_pdev[i]); + goto err_unregister_ocore; + } + } + + priv->iomem = base_addr; + priv->base = rstart; + priv->num_i2c_bus = num_i2c_bus; + priv->i2cbuses_pdev = i2cbuses_pdev; + priv->xcvr_pdev = xcvr_pdev; + priv->fpga_pdev = fpga_pdev; + return 0; + +err_unregister_ocore: + for (i = 0; i < num_i2c_bus; i++) { + if (priv->i2cbuses_pdev[i]) + platform_device_unregister(priv->i2cbuses_pdev[i]); + } + platform_device_unregister(xcvr_pdev); +err_unregister_fpga_dev: + platform_device_unregister(fpga_pdev); +err_disable_msi: + pci_disable_msi(dev); +err_unmap: + pci_iounmap(dev, base_addr); +err_disable_device: + pci_disable_device(dev); +err_exit: + return err; +} + +static void cls_fpga_remove(struct pci_dev *dev) +{ + int i; + struct switchbrd_priv *priv = pci_get_drvdata(dev); + + for (i = 0; i < priv->num_i2c_bus; i++) { + if (priv->i2cbuses_pdev[i]) + platform_device_unregister(priv->i2cbuses_pdev[i]); + } + platform_device_unregister(priv->xcvr_pdev); + platform_device_unregister(priv->fpga_pdev); + pci_disable_msi(dev); + pci_iounmap(dev, priv->iomem); + pci_disable_device(dev); + return; +}; + +static const struct pci_device_id pci_clsswbrd[] = { + { PCI_VDEVICE(CELESTICA, FPGA_PCIE_DEVICE_ID) }, + {0, } +}; + +MODULE_DEVICE_TABLE(pci, pci_clsswbrd); + +static struct pci_driver cls_pci_driver = { + .name = DRV_NAME, + .id_table = pci_clsswbrd, + .probe = cls_fpga_probe, + .remove = cls_fpga_remove, +}; + +module_pci_driver(cls_pci_driver); + +MODULE_AUTHOR("Nicholas Wu"); +MODULE_DESCRIPTION("Celestica DS4101 FPGA driver"); +MODULE_VERSION(MOD_VERSION); +MODULE_LICENSE("GPL"); + diff --git a/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/fpga-i2c-xiic.c b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/fpga-i2c-xiic.c new file mode 100644 index 0000000000..d494d80b3d --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/fpga-i2c-xiic.c @@ -0,0 +1,737 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * fpga-i2c-xiic.c + * Copyright (c) 2002-2007 Xilinx Inc. + * Copyright (c) 2009-2010 Intel Corporation + * Copyright (c) 2022-2024 Celestica Corporation + * + * This code was implemented by nicwu@celestica.com + * to port linux xiic driver to suit Celestica ds4101 platform. + * The copyright holder as seen in the header is Celestica corporation. + * + */ + +/* Supports: + * Xilinx IIC + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "fpga-xiic-i2c" + +enum xilinx_i2c_state { + STATE_DONE, + STATE_ERROR, + STATE_START, + STATE_WRITE, + STATE_READ, + STATE_BB, +}; + +enum xiic_endian { + LITTLE, + BIG +}; + +enum xiic_mode { + POLLING, + INT +}; +/** + * struct xiic_i2c - Internal representation of the XIIC I2C bus + * @dev: Pointer to device structure + * @base: Memory base of the HW registers + * @wait: Wait queue for callers + * @adap: Kernel adapter representation + * @tx_msg: Messages from above to be sent + * @lock: Mutual exclusion + * @tx_pos: Current pos in TX message + * @nmsgs: Number of messages in tx_msg + * @state: See STATE_ + * @rx_msg: Current RX message + * @rx_pos: Position within current RX message + * @endianness: big/little-endian byte order + * @clk: Pointer to struct clk + * @dynamic: Mode of controller + * @prev_msg_tx: Previous message is Tx + * @smbus_block_read: Flag to handle block read + */ +struct xiic_i2c { + struct device *dev; + int id; + void __iomem *base; + void __iomem *gl_int_base; + wait_queue_head_t wait; + struct i2c_adapter adap; + struct i2c_msg *tx_msg; + struct mutex lock; + unsigned int tx_pos; + unsigned int nmsgs; + enum xilinx_i2c_state state; + struct i2c_msg *rx_msg; + int rx_pos; + + enum xiic_endian endianness; + struct clk *clk; + bool dynamic; + bool prev_msg_tx; + bool smbus_block_read; + + /*polling variable*/ + unsigned int pos; + spinlock_t process_lock; + unsigned int flag; + int nack_retry; + struct i2c_msg *msg; +}; + +#define XIIC_MSB_OFFSET 0 +#define XIIC_REG_OFFSET (0x100 + XIIC_MSB_OFFSET) + +/* + * Register offsets in bytes from RegisterBase. Three is added to the + * base offset to access LSB (IBM style) of the word + */ +#define XIIC_CR_REG_OFFSET (0x00 + XIIC_REG_OFFSET) /* Control Register */ +#define XIIC_SR_REG_OFFSET (0x04 + XIIC_REG_OFFSET) /* Status Register */ +#define XIIC_DTR_REG_OFFSET (0x08 + XIIC_REG_OFFSET) /* Data Tx Register */ +#define XIIC_DRR_REG_OFFSET (0x0C + XIIC_REG_OFFSET) /* Data Rx Register */ +#define XIIC_ADR_REG_OFFSET (0x10 + XIIC_REG_OFFSET) /* Address Register */ +#define XIIC_TFO_REG_OFFSET (0x14 + XIIC_REG_OFFSET) /* Tx FIFO Occupancy */ +#define XIIC_RFO_REG_OFFSET (0x18 + XIIC_REG_OFFSET) /* Rx FIFO Occupancy */ +#define XIIC_TBA_REG_OFFSET (0x1C + XIIC_REG_OFFSET) /* 10 Bit Address reg */ +#define XIIC_RFD_REG_OFFSET (0x20 + XIIC_REG_OFFSET) /* Rx FIFO Depth reg */ +#define XIIC_GPO_REG_OFFSET (0x24 + XIIC_REG_OFFSET) /* Output Register */ + +/* Control Register masks */ +#define XIIC_CR_ENABLE_DEVICE_MASK 0x01 /* Device enable = 1 */ +#define XIIC_CR_TX_FIFO_RESET_MASK 0x02 /* Transmit FIFO reset=1 */ +#define XIIC_CR_MSMS_MASK 0x04 /* Master starts Txing=1 */ +#define XIIC_CR_DIR_IS_TX_MASK 0x08 /* Dir of tx. Txing=1 */ +#define XIIC_CR_NO_ACK_MASK 0x10 /* Tx Ack. NO ack = 1 */ +#define XIIC_CR_REPEATED_START_MASK 0x20 /* Repeated start = 1 */ +#define XIIC_CR_GENERAL_CALL_MASK 0x40 /* Gen Call enabled = 1 */ + +/* Status Register masks */ +#define XIIC_SR_GEN_CALL_MASK 0x01 /* 1=a mstr issued a GC */ +#define XIIC_SR_ADDR_AS_SLAVE_MASK 0x02 /* 1=when addr as slave */ +#define XIIC_SR_BUS_BUSY_MASK 0x04 /* 1 = bus is busy */ +#define XIIC_SR_MSTR_RDING_SLAVE_MASK 0x08 /* 1=Dir: mstr <-- slave */ +#define XIIC_SR_TX_FIFO_FULL_MASK 0x10 /* 1 = Tx FIFO full */ +#define XIIC_SR_RX_FIFO_FULL_MASK 0x20 /* 1 = Rx FIFO full */ +#define XIIC_SR_RX_FIFO_EMPTY_MASK 0x40 /* 1 = Rx FIFO empty */ +#define XIIC_SR_TX_FIFO_EMPTY_MASK 0x80 /* 1 = Tx FIFO empty */ + +/* Interrupt Status Register masks Interrupt occurs when... */ +#define XIIC_INTR_ARB_LOST_MASK 0x01 /* 1 = arbitration lost */ +#define XIIC_INTR_TX_ERROR_MASK 0x02 /* 1=Tx error/msg complete */ +#define XIIC_INTR_TX_EMPTY_MASK 0x04 /* 1 = Tx FIFO/reg empty */ +#define XIIC_INTR_RX_FULL_MASK 0x08 /* 1=Rx FIFO/reg=OCY level */ +#define XIIC_INTR_BNB_MASK 0x10 /* 1 = Bus not busy */ +#define XIIC_INTR_AAS_MASK 0x20 /* 1 = when addr as slave */ +#define XIIC_INTR_NAAS_MASK 0x40 /* 1 = not addr as slave */ +#define XIIC_INTR_TX_HALF_MASK 0x80 /* 1 = TX FIFO half empty */ + +/* The following constants specify the depth of the FIFOs */ +#define IIC_RX_FIFO_DEPTH 16 /* Rx fifo capacity */ +#define IIC_TX_FIFO_DEPTH 16 /* Tx fifo capacity */ + +/* + * Tx Fifo upper bit masks. + */ +#define XIIC_TX_DYN_START_MASK 0x0100 /* 1 = Set dynamic start */ +#define XIIC_TX_DYN_STOP_MASK 0x0200 /* 1 = Set dynamic stop */ + +/* + * The following constants define the register offsets for the Interrupt + * registers. There are some holes in the memory map for reserved addresses + * to allow other registers to be added and still match the memory map of the + * interrupt controller registers + */ +#define XIIC_IISR_OFFSET 0x20 /* Interrupt Status Register */ +#define XIIC_RESETR_OFFSET 0x40 /* Reset Register */ + +#define XIIC_RESET_MASK 0xAUL + +#define XIIC_PM_TIMEOUT 1000 /* ms */ +/* timeout waiting for the controller to respond */ +#define XIIC_I2C_TIMEOUT (msecs_to_jiffies(1000)) + +/* + * For the register read and write functions, a little-endian and big-endian + * version are necessary. Endianness is detected during the probe function. + * Only the least significant byte [doublet] of the register are ever + * accessed. This requires an offset of 3 [2] from the base address for + * big-endian systems. + */ + +static inline void xiic_setreg8(struct xiic_i2c *i2c, int reg, u8 value) +{ + if (i2c->endianness == LITTLE) + iowrite8(value, i2c->base + reg); + else + iowrite8(value, i2c->base + reg + 3); +} + +static inline u8 xiic_getreg8(struct xiic_i2c *i2c, int reg) +{ + u8 ret; + + if (i2c->endianness == LITTLE) + ret = ioread8(i2c->base + reg); + else + ret = ioread8(i2c->base + reg + 3); + return ret; +} + +static inline void xiic_setreg16(struct xiic_i2c *i2c, int reg, u16 value) +{ + if (i2c->endianness == LITTLE) + iowrite16(value, i2c->base + reg); + else + iowrite16be(value, i2c->base + reg + 2); +} + +static inline void xiic_setreg32(struct xiic_i2c *i2c, int reg, int value) +{ + if (i2c->endianness == LITTLE) + iowrite32(value, i2c->base + reg); + else + iowrite32be(value, i2c->base + reg); +} + +static inline int xiic_getreg32(struct xiic_i2c *i2c, int reg) +{ + u32 ret; + + if (i2c->endianness == LITTLE) + ret = ioread32(i2c->base + reg); + else + ret = ioread32be(i2c->base + reg); + + return ret; +} + +static inline void xiic_irq_clr(struct xiic_i2c *i2c, u32 mask) +{ + u32 isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET); + + xiic_setreg32(i2c, XIIC_IISR_OFFSET, isr & mask); +} + +static int xiic_clear_rx_fifo(struct xiic_i2c *i2c) +{ + u8 sr; + unsigned long timeout; + + timeout = jiffies + XIIC_I2C_TIMEOUT; + for (sr = xiic_getreg8(i2c, XIIC_SR_REG_OFFSET); + !(sr & XIIC_SR_RX_FIFO_EMPTY_MASK); + sr = xiic_getreg8(i2c, XIIC_SR_REG_OFFSET)) { + xiic_getreg8(i2c, XIIC_DRR_REG_OFFSET); + if (time_after(jiffies, timeout)) { + dev_err(i2c->dev, "Failed to clear rx fifo\n"); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int xiic_reinit(struct xiic_i2c *i2c) +{ + int ret; + + /* Soft reset IIC controller. */ + xiic_setreg32(i2c, XIIC_RESETR_OFFSET, XIIC_RESET_MASK); + + /* Set receive Fifo depth to maximum (zero based). */ + xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, IIC_RX_FIFO_DEPTH - 1); + + /* Reset Tx Fifo. */ + xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, XIIC_CR_TX_FIFO_RESET_MASK); + + /* Enable IIC Device, remove Tx Fifo reset & disable general call. */ + xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, XIIC_CR_ENABLE_DEVICE_MASK); + + /* make sure RX fifo is empty */ + ret = xiic_clear_rx_fifo(i2c); + if (ret) + return ret; + + return 0; +} + +static void xiic_deinit(struct xiic_i2c *i2c) +{ + u8 cr; + + xiic_setreg32(i2c, XIIC_RESETR_OFFSET, XIIC_RESET_MASK); + + /* Disable IIC Device. */ + cr = xiic_getreg8(i2c, XIIC_CR_REG_OFFSET); + xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, cr & ~XIIC_CR_ENABLE_DEVICE_MASK); +} + +static void ocores_process(struct xiic_i2c *i2c) +{ + struct i2c_msg *msg = i2c->msg; + u16 val; + + /* + * If we spin here because we are in timeout, so we are going + * to be in STATE_ERROR. See ocores_process_timeout() + */ + mutex_lock(&i2c->lock); + dev_dbg(&i2c->adap.dev, "STATE: %d\n", i2c->state); + + if (i2c->state == STATE_START) { + i2c->state = (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE; + if (i2c->state == STATE_READ) { + if (msg->flags & I2C_M_RECV_LEN) { + msg->len = I2C_SMBUS_BLOCK_MAX + 2; + } + /* Automatically add stop signal after receive msg->len bytes */ + val = msg->len | XIIC_TX_DYN_STOP_MASK; + xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, val); + goto out; + } + } + if (i2c->state == STATE_READ) { + while (!(xiic_getreg32(i2c, XIIC_SR_REG_OFFSET) & XIIC_SR_RX_FIFO_EMPTY_MASK)) + msg->buf[i2c->pos++] = xiic_getreg32(i2c, XIIC_DRR_REG_OFFSET); + } else if (i2c->state == STATE_WRITE) { + /* if it reaches the last byte data to be sent */ + if ((i2c->pos == msg->len - 1) && (i2c->nmsgs == 1)) { + val = msg->buf[i2c->pos++] | XIIC_TX_DYN_STOP_MASK; + xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, val); + i2c->state = STATE_DONE; + goto out; + /* if it is not the last byte data to be sent */ + } else if (i2c->pos < msg->len) { + xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, msg->buf[i2c->pos++]); + goto out; + } + } + + /* end of msg? */ + if (i2c->pos >= msg->len) { + /* suit for I2C_FUNC_SMBUS_BLOCK_DATA */ + if (i2c->state == STATE_READ && msg->flags & I2C_M_RECV_LEN) { + /* msg len includes 1 byte smbus block count and data bytes */ + msg->len = msg->buf[0] + 1; + if (msg->len > I2C_SMBUS_BLOCK_MAX + 2) { + dev_err(&i2c->adap.dev, + "Invalid block write size %d\n", + val); + goto out; + } + } + i2c->nmsgs--; + i2c->pos = 0; + if (i2c->nmsgs) { + i2c->msg++; + msg = i2c->msg; + if (!(msg->flags & I2C_M_NOSTART)) { /* send start? */ + i2c->state = STATE_START; + xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, + i2c_8bit_addr_from_msg(msg) + | XIIC_TX_DYN_START_MASK); + goto out; + } + } else { /* end? */ + i2c->state = STATE_DONE; + goto out; + } + } + +out: + mutex_unlock(&i2c->lock); +} + +/** + * Wait until something change in a given register + * @i2c: ocores I2C device instance + * @reg: register to query + * @mask: bitmask to apply on register value + * @val: expected result + * @timeout: timeout in jiffies + * + * Timeout is necessary to avoid to stay here forever when the chip + * does not answer correctly. + * + * Return: 0 on success, -ETIMEDOUT on timeout + */ +static int ocores_wait(struct xiic_i2c *i2c, + int reg, u8 mask, u8 val, + const unsigned long timeout) +{ + unsigned long j; + u8 status = 0; + + j = jiffies + timeout; + while (1) { + mutex_lock(&i2c->lock); + status = xiic_getreg32(i2c, reg); + mutex_unlock(&i2c->lock); + if ((status & mask) == val) + break; + if (time_after(jiffies, j)) + return -ETIMEDOUT; + cpu_relax(); + usleep_range(10, 20); + } + + return 0; +} + +/** + * Wait until is possible to process some data + * @i2c: ocores I2C device instance + * + * Used when the device is in polling mode (interrupts disabled). + * + * Return: 0 on success, -ETIMEDOUT on timeout + */ +static int ocores_poll_wait(struct xiic_i2c *i2c) +{ + u8 status = 0; + int err = 0; + + /* Transaction is completed */ + if (i2c->state == STATE_DONE) { + /* Wait for bus release after transaction is completed */ + err = ocores_wait(i2c, XIIC_SR_REG_OFFSET, XIIC_SR_BUS_BUSY_MASK, + 0, msecs_to_jiffies(50)); + if (err != 0) { + dev_dbg(i2c->adap.dev.parent, + "%s: bus busy after transaction is done\n", __func__); + return err; + } + + /* Write operation */ + } else if (i2c->state == STATE_WRITE || i2c->state == STATE_START) { + usleep_range(200, 300); + mutex_lock(&i2c->lock); + status = xiic_getreg32(i2c, XIIC_IISR_OFFSET); + mutex_unlock(&i2c->lock); + + /* Check NACK from slave address */ + if (status & XIIC_INTR_TX_ERROR_MASK) { + err = -ETIMEDOUT; + dev_dbg(i2c->adap.dev.parent, + "%s: isr: %#x, get NACK.\n", __func__, status); + return err; + } + /* Wait until TX FIFO is not full to do the next write operation */ + err = ocores_wait(i2c, XIIC_SR_REG_OFFSET, XIIC_SR_TX_FIFO_FULL_MASK, + 0, msecs_to_jiffies(50)); + if (err != 0) { + dev_dbg(i2c->adap.dev.parent, + "%s: TX FIFO is full after 50ms\n", __func__); + return err; + } + + /* Read operation */ + } else if (i2c->state == STATE_READ) { + /* Wait for write operation done */ + err = ocores_wait(i2c, XIIC_SR_REG_OFFSET, XIIC_SR_TX_FIFO_EMPTY_MASK, + XIIC_SR_TX_FIFO_EMPTY_MASK, msecs_to_jiffies(50)); + if (err != 0) { + dev_dbg(i2c->adap.dev.parent, + "%s: TX FIFO is not empty after 50ms\n", __func__); + return err; + } + /* Wait for data from slave address */ + err = ocores_wait(i2c, XIIC_SR_REG_OFFSET, XIIC_SR_RX_FIFO_EMPTY_MASK, + 0, msecs_to_jiffies(50)); + if (err != 0) { + dev_dbg(i2c->adap.dev.parent, + "%s: RX FIFO is empty after 50ms\n", __func__); + return err; + } + } + + mutex_lock(&i2c->lock); + status = xiic_getreg32(i2c, XIIC_IISR_OFFSET); + mutex_unlock(&i2c->lock); + + /* Check if arbitration lost */ + if (status & XIIC_INTR_ARB_LOST_MASK) { + err = -ETIMEDOUT; + dev_dbg(i2c->adap.dev.parent, + "%s: isr: %#x, arbitration lost.\n", __func__, status); + + /* Soft reset IIC controller. */ + xiic_setreg32(i2c, XIIC_RESETR_OFFSET, XIIC_RESET_MASK); + } + + return err; +} + +static void ocores_process_polling(struct xiic_i2c *i2c) +{ + while (1) { + int err; + + err = ocores_poll_wait(i2c); + if (err) { + i2c->state = STATE_ERROR; + break; + } else if (i2c->state == STATE_DONE) + break; + + ocores_process(i2c); + } +} + +static int ocores_xfer_core(struct xiic_i2c *i2c, + struct i2c_msg *msgs, int num) +{ + int ret = 0; + + mutex_lock(&i2c->lock); + /* Soft reset IIC controller. */ + xiic_setreg32(i2c, XIIC_RESETR_OFFSET, XIIC_RESET_MASK); + /* Set receive Fifo depth to maximum (zero based). */ + xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, IIC_RX_FIFO_DEPTH - 1); + + /* Reset Tx Fifo. */ + xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, XIIC_CR_TX_FIFO_RESET_MASK); + + /* Enable IIC Device, remove Tx Fifo reset & disable general call. */ + xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, XIIC_CR_ENABLE_DEVICE_MASK); + + /* set i2c clock as 100Hz. */ + //xiic_setreg8(i2c, 0x13c, 0x7C); + + /* make sure RX fifo is empty */ + ret = xiic_clear_rx_fifo(i2c); + if (ret) + return ret; + + /* Set minimum bus free time as 30us */ + usleep_range(30, 40); + + i2c->msg = msgs; + i2c->pos = 0; + i2c->nmsgs = num; + i2c->state = STATE_START; + + dev_dbg(&i2c->adap.dev, "STATE: %d\n", i2c->state); + + if (msgs->len == 0 && num == 1) { /* suit for i2cdetect time sequence */ + u8 status = xiic_getreg32(i2c, XIIC_IISR_OFFSET); + + xiic_irq_clr(i2c, status); + /* send out the 1st byte data and stop bit */ + xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, + i2c_8bit_addr_from_msg(msgs) + | XIIC_TX_DYN_START_MASK + | XIIC_TX_DYN_STOP_MASK); + } else + /* send out the 1st byte data */ + xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, + i2c_8bit_addr_from_msg(msgs) + | XIIC_TX_DYN_START_MASK); + + mutex_unlock(&i2c->lock); + ocores_process_polling(i2c); + + return (i2c->state == STATE_DONE) ? num : -EIO; +} + +/** + * interface for host computer calling on + * @adapter: ocores I2C adapter + * @msgs : messages to send out + * @num : numbers of messages to send out + * set iic working mode, call xiic_start_xfer to receive and send messages, + * wait for interrupt process waiting up queue and return process status. + * + * Return: 0 on bus idle, non 0 on bus busy + */ +static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct xiic_i2c *i2c = i2c_get_adapdata(adap); + int err = -EIO; + int retry = 0; + int max_retry = 0; + + dev_dbg(adap->dev.parent, "%s entry SR: 0x%x\n", __func__, + xiic_getreg8(i2c, XIIC_SR_REG_OFFSET)); + + /* + * polling mode + * quick to respond i2cdetect command, so not retry here, + * I2C_SMBUS_QUICK or I2C_SMBUS_BYTE + */ + if (((msgs->len == 1 && (msgs->flags & I2C_M_RD)) + || (msgs->len == 0 + && !(msgs->flags & I2C_M_RD))) + && num == 1) + max_retry = 1; + else + /* retry 5 times if receive a NACK or other errors */ + max_retry = 5; + //max_retry = 1; + + while ((-EIO == err) && (retry < max_retry)) { + err = ocores_xfer_core(i2c, msgs, num); + retry++; + } + + return err; +} + +static u32 xiic_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static const struct i2c_algorithm xiic_algorithm = { + .master_xfer = xiic_xfer, + .functionality = xiic_func, +}; + +static const struct i2c_adapter xiic_adapter = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .class = I2C_CLASS_DEPRECATED, + .algo = &xiic_algorithm, +}; + +static int xiic_i2c_probe(struct platform_device *pdev) +{ + struct xiic_i2c *i2c; + struct resource *res; + int ret; + u32 sr; + + dev_dbg(&pdev->dev, "probe xiic i2c\n"); + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + i2c->base = devm_ioremap_resource(&pdev->dev, res); + dev_dbg(&pdev->dev, "bus id: %d, i2c->base: %p\n", pdev->id, i2c->base); + + if (IS_ERR(i2c->base)) + return PTR_ERR(i2c->base); + + /* hook up driver to tree */ + platform_set_drvdata(pdev, i2c); + i2c->adap = xiic_adapter; + + /* + *set pdev id and transfer it to interrupt base + *using specific formula in isr function + */ + i2c->id = pdev->id; + + /* pass some flags or parameters to xiic_xfer to deal with*/ + i2c_set_adapdata(&i2c->adap, i2c); + + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.dev.of_node = pdev->dev.of_node; + + spin_lock_init(&i2c->process_lock); + mutex_init(&i2c->lock); + init_waitqueue_head(&i2c->wait); + + i2c->dev = &pdev->dev; + pm_runtime_set_autosuspend_delay(i2c->dev, XIIC_PM_TIMEOUT); + pm_runtime_use_autosuspend(i2c->dev); + pm_runtime_set_active(i2c->dev); + pm_runtime_enable(i2c->dev); + + ret = xiic_reinit(i2c); + if (ret < 0) { + dev_err(&pdev->dev, "Cannot xiic_reinit\n"); + goto err_clk_dis; + } + /* + * Detect endianness + * Try to reset the TX FIFO. Then check the EMPTY flag. If it is not + * set, assume that the endianness was wrong and swap. + */ + i2c->endianness = LITTLE; + xiic_setreg32(i2c, XIIC_CR_REG_OFFSET, XIIC_CR_TX_FIFO_RESET_MASK); + /* Reset is cleared in xiic_reinit */ + sr = xiic_getreg32(i2c, XIIC_SR_REG_OFFSET); + if (!(sr & XIIC_SR_TX_FIFO_EMPTY_MASK)) + i2c->endianness = BIG; + + /* add i2c adapter to i2c tree */ + ret = i2c_add_adapter(&i2c->adap); + if (ret) { + xiic_deinit(i2c); + goto err_clk_dis; + } + + return 0; + +err_clk_dis: + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_disable(&pdev->dev); + return ret; +} + +static int xiic_i2c_remove(struct platform_device *pdev) +{ + struct xiic_i2c *i2c = platform_get_drvdata(pdev); + int ret; + + /* remove adapter & data */ + i2c_del_adapter(&i2c->adap); + + ret = pm_runtime_get_sync(i2c->dev); + if (ret < 0) + return ret; + + xiic_deinit(i2c); + pm_runtime_put_sync(i2c->dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + + return 0; +} + +#if defined(CONFIG_OF) +static const struct of_device_id xiic_of_match[] = { + { .compatible = "xlnx,xps-iic-2.00.a", }, + {}, +}; +MODULE_DEVICE_TABLE(of, xiic_of_match); +#endif + +static struct platform_driver xiic_i2c_driver = { + .probe = xiic_i2c_probe, + .remove = xiic_i2c_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(xiic_of_match), + }, +}; + +module_platform_driver(xiic_i2c_driver); + +MODULE_AUTHOR("info@mocean-labs.com; nicwu@celestica.com"); +MODULE_VERSION("0.0.2"); +MODULE_DESCRIPTION("Celestica I2C bus driver based on Xilinx I2C bus driver"); +MODULE_LICENSE("GPL v2"); diff --git a/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/fpga-sys.c b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/fpga-sys.c new file mode 100644 index 0000000000..f5e5c8105f --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/fpga-sys.c @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * fpga-sys.c - driver to access FPGA registers. + * + * Pradchaya Phucharoen + * Nicholas Wu + * + * Copyright (C) 2022-2024 Celestica Corp. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define FPGA_VERSION 0x0000 +#define FPGA_SCRATCH 0x0004 +#define FPGA_SW_TEMP 0x002C +#define FPGA_REGISTER_SIZE 0x2000000 + + +/* + * fpga_priv - port fpga private data + * @dev: device for reference + * @base: virtual base address + * @num_ports: number of front panel ports + * @fp_devs: list of front panel port devices + */ +struct fpga_priv { + void __iomem *base; + struct mutex fpga_lock; + void __iomem *fpga_read_addr; +}; + + + +/** + * Show the value of the register set by 'set_fpga_reg_address' + * If the address is not set by 'set_fpga_reg_address' first, + * The version register is selected by default. + * @param buf register value in hextring + * @return number of bytes read, or an error code + */ +static ssize_t get_fpga_reg_value(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + uint32_t data; + struct fpga_priv *fpga = dev_get_drvdata(dev); + + data = ioread32(fpga->fpga_read_addr); + + return sprintf(buf, "0x%8.8x\n", data); +} +/** + * Store the register address + * @param buf address wanted to be read value of + * @return number of bytes stored, or an error code + */ +static ssize_t set_fpga_reg_address(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + int ret = 0; + unsigned long addr; + struct fpga_priv *fpga = dev_get_drvdata(dev); + + ret = kstrtoul(buf, 0, &addr); + if (ret != 0) + return ret; + fpga->fpga_read_addr = fpga->base + addr; + + return count; +} +/** + * Show value of fpga scratch register + * @param buf register value in hexstring + * @return number of bytes read, or an error code + */ +static ssize_t get_fpga_scratch(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct fpga_priv *fpga = dev_get_drvdata(dev); + + return sprintf(buf, "0x%8.8x\n", + ioread32(fpga->base + FPGA_SCRATCH) & 0xffffffff); +} +/** + * Store value of fpga scratch register + * @param buf scratch register value passing from user space + * @return number of bytes stored, or an error code + */ +static ssize_t set_fpga_scratch(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + int ret = 0; + unsigned long data; + struct fpga_priv *fpga = dev_get_drvdata(dev); + + ret = kstrtoul(buf, 0, &data); + if (ret != 0) + return ret; + iowrite32(data, fpga->base + FPGA_SCRATCH); + + return count; +} + +/** + * Show value of fpga version register + * @param buf register value in hexstring + * @return number of bytes read, or an error code + */ +static ssize_t get_fpga_version(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct fpga_priv *fpga = dev_get_drvdata(dev); + + return sprintf(buf, "0x%8.8x\n", + ioread32(fpga->base + FPGA_VERSION) & 0xffffffff); +} + +/** + * Show switch temperature + * @param buf register value in hexstring + * @return number of bytes read, or an error code + */ +static ssize_t get_temp_sw_internal(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct fpga_priv *fpga = dev_get_drvdata(dev); + uint32_t value = 0; + + value = ioread32(fpga->base + FPGA_SW_TEMP) & 0x3ffff; + + return sprintf(buf, "%d\n", value); +} + +/** + * Store a value in a specific register address + * @param buf the value and address in format '0xhhhh 0xhhhhhhhh' + * @return number of bytes sent by user space, or an error code + */ +static ssize_t set_fpga_reg_value(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + int ret = 0; + unsigned long addr; + unsigned long value; + unsigned long mode = 8; + char *tok; + char clone[20]; + char *pclone = clone; + struct fpga_priv *fpga = dev_get_drvdata(dev); + + strcpy(clone, buf); + mutex_lock(&fpga->fpga_lock); + tok = strsep((char **)&pclone, " "); + if (tok == NULL) { + mutex_unlock(&fpga->fpga_lock); + return -EINVAL; + } + ret = kstrtoul(tok, 0, &addr); + if (ret != 0) { + mutex_unlock(&fpga->fpga_lock); + return ret; + } + tok = strsep((char **)&pclone, " "); + if (tok == NULL) { + mutex_unlock(&fpga->fpga_lock); + return -EINVAL; + } + ret = kstrtoul(tok, 0, &value); + if (ret != 0) { + mutex_unlock(&fpga->fpga_lock); + return ret; + } + tok = strsep((char **)&pclone, " "); + if (tok == NULL) + mode = 32; + else { + ret = kstrtoul(tok, 10, &mode); + if (ret != 0) { + mutex_unlock(&fpga->fpga_lock); + return ret; + } + } + if (mode == 32) + iowrite32(value, fpga->base + addr); + else if (mode == 8) + iowrite8(value, fpga->base + addr); + else { + mutex_unlock(&fpga->fpga_lock); + return -EINVAL; + } + mutex_unlock(&fpga->fpga_lock); + + return count; +} + +/** + * Read all FPGA register in binary mode. + * @param buf Raw transceivers port startus and control register values + * @return number of bytes read, or an error code + */ +static ssize_t dump_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + unsigned long i = 0; + ssize_t status; + u8 read_reg; + struct device *dev = kobj_to_dev(kobj); + struct fpga_priv *fpga = dev_get_drvdata(dev); + + if (off + count > FPGA_REGISTER_SIZE) + return -EINVAL; + + mutex_lock(&fpga->fpga_lock); + while (i < count) { + read_reg = ioread8(fpga->base + off + i); + buf[i++] = read_reg; + } + status = count; + mutex_unlock(&fpga->fpga_lock); + + return status; +} + + +/* FPGA attributes */ +static DEVICE_ATTR(getreg, 0600, get_fpga_reg_value, set_fpga_reg_address); +static DEVICE_ATTR(setreg, 0200, NULL, set_fpga_reg_value); +static DEVICE_ATTR(scratch, 0600, get_fpga_scratch, set_fpga_scratch); +static DEVICE_ATTR(version, 0400, get_fpga_version, NULL); +static DEVICE_ATTR(temp_sw_internal, 0400, get_temp_sw_internal, NULL); +static BIN_ATTR_RO(dump, FPGA_REGISTER_SIZE); + +static struct bin_attribute *fpga_bin_attrs[] = { + &bin_attr_dump, + NULL, +}; + +static struct attribute *fpga_attrs[] = { + &dev_attr_getreg.attr, + &dev_attr_scratch.attr, + &dev_attr_version.attr, + &dev_attr_temp_sw_internal.attr, + &dev_attr_setreg.attr, + NULL, +}; + +static struct attribute_group fpga_attr_grp = { + .attrs = fpga_attrs, + .bin_attrs = fpga_bin_attrs, +}; + + +static int cls_fpga_probe(struct platform_device *pdev) +{ + + struct fpga_priv *fpga; + struct resource *res; + int ret; + + fpga = devm_kzalloc(&pdev->dev, sizeof(struct fpga_priv), GFP_KERNEL); + if (!fpga) { + ret = -ENOMEM; + goto err_exit; + } + mutex_init(&fpga->fpga_lock); + dev_set_drvdata(&pdev->dev, fpga); + + /* mmap resource */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) { + /* use devm_ioremap_resource to map overall FPGA resource + * will be conflict since it's been taken in ocores devm_ioremap + */ + fpga->base = ioremap_nocache(res->start, res->end - res->start); + if (IS_ERR(fpga->base)) { + ret = PTR_ERR(fpga->base); + goto mem_unmap; + } + } + dev_dbg(&pdev->dev, "FPGA version: 0x%x\n", + ioread32(fpga->base + FPGA_VERSION)); + + ret = sysfs_create_group(&pdev->dev.kobj, &fpga_attr_grp); + if (ret != 0) { + dev_err(&pdev->dev, "Cannot create FPGA system sysfs attributes\n"); + goto err_remove_fpga; + } + + return 0; + +err_remove_fpga: + sysfs_remove_group(&pdev->dev.kobj, &fpga_attr_grp); +mem_unmap: + iounmap(fpga->base); +err_exit: + return ret; + +} + +static int cls_fpga_remove(struct platform_device *pdev) +{ + struct fpga_priv *fpga = dev_get_drvdata(&pdev->dev); + + sysfs_remove_group(&pdev->dev.kobj, &fpga_attr_grp); + iounmap(fpga->base); + + return 0; +} + + +static struct platform_driver cls_fpga_driver = { + .probe = cls_fpga_probe, + .remove = cls_fpga_remove, + .driver = { + .name = "fpga-sys", + }, +}; + +static int __init drv_init(void) +{ + int rc = 0; + + rc = platform_driver_register(&cls_fpga_driver); + + return rc; +} + +static void __exit drv_exit(void) +{ + platform_driver_unregister(&cls_fpga_driver); +} + +module_init(drv_init); +module_exit(drv_exit); + +MODULE_AUTHOR("Nicholas Wu"); +MODULE_DESCRIPTION("Celestica fpga control driver"); +MODULE_VERSION("0.0.2"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:cls-fpga"); + diff --git a/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/fpga-xcvr.c b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/fpga-xcvr.c new file mode 100644 index 0000000000..cc13732a2b --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/fpga-xcvr.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * fpga-xcvr.c - front panel 2 SFP+ port control. + * + * Pradchaya Phucharoen + * Nicholas Wu + * + * Copyright (C) 2022-2024 Celestica Corp. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "fpga-xcvr.h" + +/* FPGA front panel */ +#define PORT_TEST 0 +#define PORT_CR_STATUS 0x4 + +/* + * Port control degister + * TXDIS : active high, RW + */ + +#define CTRL_TXDIS BIT(3) + +/* + * Port status register + * TXFAULT : active high, RO + * RXLOS : active high, RO + * MODABS : active high, RO, for SFP + */ +#define STAT_TXFAULT BIT(2) +#define STAT_MODABS BIT(1) +#define STAT_RXLOS BIT(0) + +/* + * port_data - optical port data + * @xcvr: xcvr memory accessor + * @name: port name + * @index: front panel port index starting from 1 + */ +struct port_data { + struct xcvr_priv *xcvr; + const char *name; + unsigned int index; +}; + +/* + * xcvr_priv - port xcvr private data + * @dev: device for reference + * @base: virtual base address + * @num_ports: number of front panel ports + * @fp_devs: list of front panel port devices + */ +struct xcvr_priv { + struct device *dev; + void __iomem *base; + int port_reg_size; + int num_ports; + struct device **fp_devs; +}; + +static inline void port_setreg(struct xcvr_priv *xcvr, + int reg, int index, u8 value) +{ + return iowrite8(value, xcvr->base + reg + + (index - 1) * xcvr->port_reg_size); +} + +static inline u8 port_getreg(struct xcvr_priv *xcvr, + int reg, int index) +{ + return ioread8(xcvr->base + reg + + (index - 1) * xcvr->port_reg_size); +} + +static ssize_t sfp_modabs_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data; + struct port_data *port_data = dev_get_drvdata(dev); + unsigned int index = port_data->index; + + data = port_getreg(port_data->xcvr, PORT_CR_STATUS, index); + + return sprintf(buf, "%d\n", (data & STAT_MODABS)?1:0); +} + +static ssize_t sfp_txfault_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data; + struct port_data *port_data = dev_get_drvdata(dev); + unsigned int index = port_data->index; + + data = port_getreg(port_data->xcvr, PORT_CR_STATUS, index); + + return sprintf(buf, "%d\n", (data & STAT_TXFAULT)?1:0); +} + +static ssize_t sfp_rxlos_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data; + struct port_data *port_data = dev_get_drvdata(dev); + unsigned int index = port_data->index; + + data = port_getreg(port_data->xcvr, PORT_CR_STATUS, index); + + return sprintf(buf, "%d\n", (data & STAT_RXLOS)?1:0); +} + +static ssize_t sfp_txdisable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data; + struct port_data *port_data = dev_get_drvdata(dev); + unsigned int index = port_data->index; + + data = port_getreg(port_data->xcvr, PORT_CR_STATUS, index); + + return sprintf(buf, "%d\n", (data & CTRL_TXDIS)?1:0); +} + +static ssize_t sfp_txdisable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + ssize_t status; + long value; + u8 data; + struct port_data *port_data = dev_get_drvdata(dev); + unsigned int index = port_data->index; + + status = kstrtol(buf, 0, &value); + if (status == 0) { + data = port_getreg(port_data->xcvr, PORT_CR_STATUS, index); + if (value == 0) + data &= ~CTRL_TXDIS; + else + data |= CTRL_TXDIS; + port_setreg(port_data->xcvr, PORT_CR_STATUS, index, data); + status = size; + } + + return status; +} + +DEVICE_ATTR_RO(sfp_modabs); +DEVICE_ATTR_RO(sfp_txfault); +DEVICE_ATTR_RO(sfp_rxlos); +DEVICE_ATTR_RW(sfp_txdisable); + +/* sfp_attrs */ +static struct attribute *sfp_attrs[] = { + &dev_attr_sfp_modabs.attr, + &dev_attr_sfp_txfault.attr, + &dev_attr_sfp_rxlos.attr, + &dev_attr_sfp_txdisable.attr, + NULL +}; + +ATTRIBUTE_GROUPS(sfp); + +/* A single port device init */ +static struct device *init_port(struct device *dev, + struct xcvr_priv *xcvr, + struct port_info info, + const struct attribute_group **groups) +{ + struct port_data *new_data; + + new_data = devm_kzalloc(dev, sizeof(struct port_data), GFP_KERNEL); + if (!new_data) + return ERR_PTR(-ENOMEM); + + new_data->index = info.index; + new_data->name = info.name; + new_data->xcvr = xcvr; + + return devm_hwmon_device_register_with_groups(dev, + info.name, + new_data, + groups); +} + +static void xcvr_cleanup(struct xcvr_priv *xcvr) +{ + struct device *dev; + struct port_data *data; + int i; + + for (i = 0; i < xcvr->num_ports; i++) { + dev = xcvr->fp_devs[i]; + if (dev == NULL) + continue; + data = dev_get_drvdata(dev); + sysfs_remove_link(&xcvr->dev->kobj, data->name); + } +} + +static int cls_xcvr_probe(struct platform_device *pdev) +{ + + struct xcvr_priv *xcvr; + struct cls_xcvr_platform_data *pdata; + struct resource *res; + int ret; + int i; + + struct device **port_devs; + + xcvr = devm_kzalloc(&pdev->dev, sizeof(struct xcvr_priv), GFP_KERNEL); + if (!xcvr) { + ret = -ENOMEM; + goto err_exit; + } + + dev_set_drvdata(&pdev->dev, xcvr); + + /* mmap resource */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) { + xcvr->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(xcvr->base)) { + ret = PTR_ERR(xcvr->base); + goto err_exit; + } + } + + pdata = dev_get_platdata(&pdev->dev); + xcvr->dev = &pdev->dev; + + if (pdata) { + /* assign pdata */ + xcvr->num_ports = pdata->num_ports; + xcvr->port_reg_size = pdata->port_reg_size; + } + + /* alloc front panel device list */ + port_devs = devm_kzalloc(&pdev->dev, + xcvr->num_ports * sizeof(struct device *), + GFP_KERNEL); + if (!port_devs) { + ret = -ENOMEM; + goto err_exit; + } + + + if (pdata) { + /* create each device attrs group determined by type */ + for (i = 0; i < pdata->num_ports; i++) { + struct device *fp_dev; + + fp_dev = init_port(&pdev->dev, + xcvr, + pdata->devices[i], + sfp_groups); + if (IS_ERR(fp_dev)) { + dev_err(&pdev->dev, + "Failed to init port %s\n", + pdata->devices[i].name); + ret = PTR_ERR(fp_dev); + goto dev_clean_up; + } + + dev_info(&pdev->dev, + "Register port %s\n", + pdata->devices[i].name); + + WARN(sysfs_create_link(&pdev->dev.kobj, + &fp_dev->kobj, + pdata->devices[i].name), + "can't create symlink to %s\n", pdata->devices[i].name); + port_devs[i] = fp_dev; + fp_dev = NULL; + } + xcvr->fp_devs = port_devs; + } + + return 0; + +dev_clean_up: + xcvr_cleanup(xcvr); +err_exit: + return ret; + +} + + +static int cls_xcvr_remove(struct platform_device *pdev) +{ + struct xcvr_priv *xcvr = dev_get_drvdata(&pdev->dev); + + xcvr_cleanup(xcvr); + + return 0; +} + +static struct platform_driver cls_xcvr_driver = { + .probe = cls_xcvr_probe, + .remove = cls_xcvr_remove, + .driver = { + .name = "fpga-xcvr", + }, +}; + +static int __init drv_init(void) +{ + int rc = 0; + + rc = platform_driver_register(&cls_xcvr_driver); + + return rc; +} + +static void __exit drv_exit(void) +{ + platform_driver_unregister(&cls_xcvr_driver); +} + +module_init(drv_init); +module_exit(drv_exit); + +MODULE_AUTHOR("Nicholas Wu"); +MODULE_DESCRIPTION("Celestica xcvr control driver"); +MODULE_VERSION("0.0.1"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:cls-xcvr"); + diff --git a/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/fpga-xcvr.h b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/fpga-xcvr.h new file mode 100644 index 0000000000..03b7e70beb --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/fpga-xcvr.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * fpga-xcvr.h + * + * Pradchaya Phucharoen + * Nicholas Wu + * + * Copyright (C) 2022-2024 Celestica Corp. + */ + +#ifndef _FPGA_XCVR_H_ +#define _FPGA_XCVR_H_ + +enum PORT_TYPE { + NONE = 0, + SFP, + QSFP +}; + +/* + * port_info - optical port info + * @index: front panel port index starting from 1 + * @typr: port type, see *PORT_TYPE* + */ +struct port_info { + const char *name; + unsigned int index; + enum PORT_TYPE type; +}; + +/* + * cls_xcvr_platform_data - port xcvr private data + * @port_reg_size: register range of each port + * @num_ports: number of front panel ports + * @devices: list of front panel port info + */ +struct cls_xcvr_platform_data { + unsigned int port_reg_size; + int num_ports; + struct port_info *devices; +}; + +#endif /* _LINUX_I2C_CLS_H */ diff --git a/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/i2c-xcvr.c b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/i2c-xcvr.c new file mode 100644 index 0000000000..a291f38a81 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/i2c-xcvr.c @@ -0,0 +1,385 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * i2c-xcvr.c - Celestica XCVR control/status driver + * based on PCA9535 i/o expander. + * Copyright (C) 2023 Celestica 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 Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* One PCA9535 manage 4 OSFP ports*/ +#define PCA9535_MAN_PORT_NUM 4 + +#define PCA9535_1_I2C_ADDR 0x20 +#define PCA9535_2_I2C_ADDR 0x21 +#define PCA9535_3_I2C_ADDR 0x22 +#define PCA9535_4_I2C_ADDR 0x23 +#define PCA9535_5_I2C_ADDR 0x24 +#define PCA9535_6_I2C_ADDR 0x25 +#define PCA9535_7_I2C_ADDR 0x26 +#define PCA9535_8_I2C_ADDR 0x27 +#define PCA9535_NUM 8 + + +#define INPUT_PORT_REG0 0x00 +#define INPUT_PORT_REG1 0x01 +#define OUTPUT_PORT_REG0 0x02 +#define OUTPUT_PORT_REG1 0x03 +#define POL_INVER_REG0 0x04 +#define POL_INVER_REG1 0x05 +#define CONFIG_REG0 0x06 +#define CONFIG_REG1 0x07 + +#define OUTPUT 0x00 +#define INPUT 0xff + +#define INPUT_PORT_INTIO_OFFSET 0 +#define INPUT_PORT_PRSIO_OFFSET 4 +#define OUTPUT_PORT_RSTIO_OFFSET 0 +#define OUTPUT_PORT_LPWIO_OFFSET 4 + +/* Private for all SFF devices */ +struct xcvr_data { + struct device *sff_devices[PCA9535_MAN_PORT_NUM]; + struct i2c_client *client; + struct class *class; +}; + +/* private data for individual SFF device */ +struct sff_dev_data { + int portid; + struct i2c_client *client; +}; + + +/* OSFP attributes */ +static ssize_t osfp_reset_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int value; + u8 portio = 0; + struct sff_dev_data *sff_data = dev_get_drvdata(dev); + struct i2c_client *client = sff_data->client; + + portio = sff_data->portid + OUTPUT_PORT_RSTIO_OFFSET; + value = i2c_smbus_read_byte_data(client, OUTPUT_PORT_REG0); + if (value < 0) + return value; + + return sprintf(buf, "%d\n", (value >> portio) & 0x01); + +} +static ssize_t osfp_reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + ssize_t status; + long value; + u8 data; + u8 portio = 0; + struct sff_dev_data *sff_data = dev_get_drvdata(dev); + struct i2c_client *client = sff_data->client; + + portio = sff_data->portid + OUTPUT_PORT_RSTIO_OFFSET; + status = kstrtol(buf, 0, &value); + if (status == 0) { + // if value is 0, reset signal is low + data = i2c_smbus_read_byte_data(client, OUTPUT_PORT_REG0); + if (!value) + data = data & ~((u8)0x1 << portio); + else + data = data | ((u8)0x1 << portio); + + i2c_smbus_write_byte_data(client, OUTPUT_PORT_REG0, data); + status = size; + } + + return status; + +} + +static ssize_t osfp_lpmode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int value; + u8 portio = 0; + struct sff_dev_data *sff_data = dev_get_drvdata(dev); + struct i2c_client *client = sff_data->client; + + portio = sff_data->portid + OUTPUT_PORT_LPWIO_OFFSET; + value = i2c_smbus_read_byte_data(client, OUTPUT_PORT_REG0); + if (value < 0) + return value; + + return sprintf(buf, "%d\n", (value >> portio) & 0x01); + +} +static ssize_t osfp_lpmode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + ssize_t status; + long value; + u8 data; + u8 portio = 0; + struct sff_dev_data *sff_data = dev_get_drvdata(dev); + struct i2c_client *client = sff_data->client; + + portio = sff_data->portid + OUTPUT_PORT_LPWIO_OFFSET; + + status = kstrtol(buf, 0, &value); + if (status == 0) { + data = i2c_smbus_read_byte_data(client, OUTPUT_PORT_REG0); + if (!value) + data = data & ~((u8)0x1 << portio); + else + data = data | ((u8)0x1 << portio); + + i2c_smbus_write_byte_data(client, OUTPUT_PORT_REG0, data); + status = size; + } + + return status; + +} + +static ssize_t osfp_modprs_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u32 data; + int len = 0; + u8 portio = 0; + struct sff_dev_data *sff_data = dev_get_drvdata(dev); + struct i2c_client *client = sff_data->client; + + portio = sff_data->portid + INPUT_PORT_PRSIO_OFFSET; + + data = i2c_smbus_read_byte_data(client, INPUT_PORT_REG1); + len = sprintf(buf, "%x\n", (data >> portio) & 0x01); + + return len; + +} + +static ssize_t osfp_modirq_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u32 data; + int len = 0; + u8 portio = 0; + struct sff_dev_data *sff_data = dev_get_drvdata(dev); + struct i2c_client *client = sff_data->client; + + portio = sff_data->portid + INPUT_PORT_INTIO_OFFSET; + data = i2c_smbus_read_byte_data(client, INPUT_PORT_REG1); + len = sprintf(buf, "%x\n", (data >> portio) & 0x01); + + return len; + +} + +DEVICE_ATTR_RW(osfp_reset); +DEVICE_ATTR_RW(osfp_lpmode); +DEVICE_ATTR_RO(osfp_modprs); +DEVICE_ATTR_RO(osfp_modirq); + +static struct attribute *sff_attrs[] = { + &dev_attr_osfp_modirq.attr, + &dev_attr_osfp_modprs.attr, + &dev_attr_osfp_lpmode.attr, + &dev_attr_osfp_reset.attr, + NULL, +}; + +static struct attribute_group sff_attr_grp = { + .attrs = sff_attrs, +}; +static const struct attribute_group *sff_attr_grps[] = { + &sff_attr_grp, + NULL +}; + + +static struct device *sff_dev_init(struct device *dev, int portid) +{ + struct xcvr_data *data = dev_get_drvdata(dev); + struct device *sff_device; + struct sff_dev_data *sff_data; + unsigned char pindex = 0; + + sff_data = kzalloc(sizeof(struct sff_dev_data), GFP_KERNEL); + if (!sff_data) { + dev_alert(dev, "Cannot allocate SFF device data @port%d", portid); + return NULL; + } + + /* The OSFP port ID start from 1 */ + sff_data->portid = portid; + sff_data->client = data->client; + + /* PCA9535 addressed are consistent from 0x20 to 0x27, + * so we can use the address offset + * and interval to calculate the port index + */ + pindex = portid + 1 + + (data->client->addr - PCA9535_1_I2C_ADDR) + * PCA9535_MAN_PORT_NUM; + + sff_device = device_create_with_groups(data->class, + NULL, + MKDEV(0, 0), + sff_data, + sff_attr_grps, + "%s%d", + "OSFP", pindex); + + if (IS_ERR(sff_device)) { + dev_alert(dev, "Cannot create SFF device @port%d", pindex); + kfree(sff_data); + return NULL; + } + + dev_info(dev, "Create SFF device @port%d", pindex); + return sff_device; +} + + +static int xcvr_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + u8 portid = 0; + int err = 0; + struct device *dev; + struct xcvr_data *data; + static struct class *class; + struct sff_dev_data *sff_data; + + dev = &client->dev; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + /* Set io0-7 as output and io8-15 as input */ + i2c_smbus_write_byte_data(client, CONFIG_REG0, OUTPUT); + i2c_smbus_write_byte_data(client, CONFIG_REG1, INPUT); + + /* Set Initial state */ + i2c_smbus_write_byte_data(client, OUTPUT_PORT_REG0, 0x00); + + data = devm_kzalloc(dev, sizeof(struct xcvr_data), + GFP_KERNEL); + + if (!data) { + err = -ENOMEM; + goto fail_alloc_xcvr_data; + } + + dev_set_drvdata(dev, data); + data->client = client; + + if (class == NULL) { + class = class_create(THIS_MODULE, "SFF"); + if (IS_ERR(data->class)) { + dev_alert(dev, "Failed to create class\n"); + err = PTR_ERR(data->class); + goto fail_alloc_xcvr_data; + } + } + + data->class = class; + + /* create 32 OSFP SYSFS */ + for (portid = 0; portid < PCA9535_MAN_PORT_NUM; portid++) { + data->sff_devices[portid] = sff_dev_init(dev, portid); + if (IS_ERR(data->sff_devices[portid])) { + dev_alert(dev, "Failed to register device\n"); + err = PTR_ERR(data->sff_devices[portid]); + goto fail_register_sff_device; + } + } + return 0; + +fail_register_sff_device: + for (portid = 0; portid < PCA9535_MAN_PORT_NUM; portid++) { + if (data->sff_devices[portid] != NULL) { + sff_data = dev_get_drvdata(data->sff_devices[portid]); + device_unregister(data->sff_devices[portid]); + put_device(data->sff_devices[portid]); + kfree(sff_data); + } + } + device_destroy(data->class, MKDEV(0, 0)); + class_unregister(data->class); + class_destroy(data->class); + +fail_alloc_xcvr_data: + return err; +} + +static int xcvr_remove(struct i2c_client *client) +{ + u8 portid = 0; + struct sff_dev_data *sff_data; + struct device *dev = &client->dev; + struct xcvr_data *data = dev_get_drvdata(dev); + static u8 index = 1; + + for (portid = 0; portid < PCA9535_MAN_PORT_NUM; portid++) { + if (data->sff_devices[portid] != NULL) { + sff_data = dev_get_drvdata(data->sff_devices[portid]); + device_unregister(data->sff_devices[portid]); + put_device(data->sff_devices[portid]); + kfree(sff_data); + } + } + if (index == PCA9535_NUM) { + device_destroy(data->class, MKDEV(0, 0)); + class_unregister(data->class); + class_destroy(data->class); + } + index++; + + return 0; +} + +static const struct i2c_device_id xcvr_ids[] = { + { "xcvr", 0 }, + { /* END OF List */ } +}; +MODULE_DEVICE_TABLE(i2c, xcvr_ids); + +struct i2c_driver xcvr_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "xcvr", + .owner = THIS_MODULE, + }, + .probe = xcvr_probe, + .remove = xcvr_remove, + .id_table = xcvr_ids, +}; + +module_i2c_driver(xcvr_driver); + +MODULE_AUTHOR("Celestica Inc."); +MODULE_DESCRIPTION("Celestica CPLD XCVR driver"); +MODULE_VERSION("0.0.2"); +MODULE_LICENSE("GPL"); + diff --git a/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/lpc-basecpld.c b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/lpc-basecpld.c new file mode 100644 index 0000000000..d4b37c6322 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/lpc-basecpld.c @@ -0,0 +1,700 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * lpc-basecpld.c - The CPLD driver for the Base Board of DS4101 + * The driver implement sysfs to access CPLD register on the baseboard of DS4101 via LPC bus. + * + * Author: Nicholas Wu + * Copyright (C) 2022-2024 Celestica 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 Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "sys_cpld" +/** + * CPLD register address for read and write. + */ +#define VERSION_ADDR 0xA100 +#define SCRATCH_ADDR 0xA101 +#define PSU_LED_ADDR 0xA161 +#define SYS_LED_ADDR 0xA162 +#define ALARM_LED_ADDR 0xA163 +#define FAN_LED_ADDR 0xA165 + +#define CPLD_REGISTER_SIZE 0xFF + +/* LED CTRL */ + +enum FAN_LED { + fan_led_amb = 1, + fan_led_grn, + fan_led_off, + fan_led_auto = 0x10 +} fan_led; + +enum SYS_LED { + sys_led_on = 0, + sys_led_grn, + sys_led_amb, + sys_led_off +} sys_led; + +enum PWR_LED { + pwr_led_amb = 1, + pwr_led_grn, + pwr_led_off, + pwr_led_auto = 0x10 +} pwr_led; + +enum ALARM_LED { + alarm_led_on = 0, + alarm_led_grn, + alarm_led_amb, + alarm_led_off +} alarm_led; + +enum LED_CTRL { + led_on = 0, + led_blk_1hz, + led_blk_4hz, + led_off +} led_ctrl; + +#define LED_OFF "off" +#define LED_GREEN "green" +#define LED_AMBER "amber" +#define LED_HZ_GBNK "grn_bnk_1hz" +#define LED_HZ_ABNK "amb_bnk_1hz" +#define LED_QHZ_GBNK "grn_bnk_4hz" +#define LED_QHZ_ABNK "amb_bnk_4hz" +#define LED_HZ_GABNK "grn_amb_1hz" +#define LED_QHZ_GABNK "grn_amb_4hz" + +struct cpld_b_data { + struct mutex cpld_lock; + uint16_t read_addr; +}; + +struct cpld_b_data *cpld_data; + +/** + * Read the value from scratch register as hex string. + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer for get value + * @return Hex string read from scratch register. + */ +static ssize_t scratch_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + unsigned char data = 0; + + mutex_lock(&cpld_data->cpld_lock); + data = inb(SCRATCH_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + + return sprintf(buf, "0x%2.2x\n", data); +} + +/** + * Set scratch register with specific hex string. + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer of set value + * @param count number of bytes in buffer + * @return number of bytes written, or error code < 0. + */ +static ssize_t scratch_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + int ret = 0; + unsigned long data; + + mutex_lock(&cpld_data->cpld_lock); + ret = kstrtoul(buf, 0, &data); + if (ret != 0) { + mutex_unlock(&cpld_data->cpld_lock); + return ret; + } + outb(data, SCRATCH_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + + return count; +} + +/* CPLD version attributes */ +static ssize_t version_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int len = 0; + unsigned char value = 0; + + // CPLD register is one byte + mutex_lock(&cpld_data->cpld_lock); + value = inb(VERSION_ADDR); + len = sprintf(buf, "%d.%d\n", value >> 4, value & 0x0F); + mutex_unlock(&cpld_data->cpld_lock); + + return len; +} + + +static ssize_t getreg_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + int ret = 0; + unsigned long addr; + + ret = kstrtoul(buf, 0, &addr); + if (ret != 0) + return ret; + cpld_data->read_addr = addr; + + return count; +} + +static ssize_t getreg_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int len = 0; + + // CPLD register is one byte + mutex_lock(&cpld_data->cpld_lock); + len = sprintf(buf, "0x%2.2x\n", inb(cpld_data->read_addr)); + mutex_unlock(&cpld_data->cpld_lock); + + return len; +} + +static ssize_t setreg_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + int ret = 0; + unsigned long addr; + unsigned long value; + char *tok; + char clone[20]; + char *pclone = clone; + + strcpy(clone, buf); + + mutex_lock(&cpld_data->cpld_lock); + tok = strsep((char **)&pclone, " "); + if (tok == NULL) { + mutex_unlock(&cpld_data->cpld_lock); + return -EINVAL; + } + ret = kstrtoul(tok, 0, &addr); + if (ret != 0) { + mutex_unlock(&cpld_data->cpld_lock); + return ret; + } + + tok = strsep((char **)&pclone, " "); + if (tok == NULL) { + mutex_unlock(&cpld_data->cpld_lock); + return -EINVAL; + } + ret = kstrtoul(tok, 0, &value); + if (ret != 0) { + mutex_unlock(&cpld_data->cpld_lock); + return ret; + } + outb(value, addr); + mutex_unlock(&cpld_data->cpld_lock); + + return count; +} + +/** + * Read all CPLD register in binary mode. + * @return number of byte read. + */ +static ssize_t dump_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + unsigned long i = 0; + ssize_t status; + + mutex_lock(&cpld_data->cpld_lock); +begin: + if (i < count) { + buf[i++] = inb(VERSION_ADDR + off); + off++; + msleep(1); + goto begin; + } + status = count; + mutex_unlock(&cpld_data->cpld_lock); + + return status; +} + +/** + * @brief Show status led + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer for get value + * @return led state - off/on/blink + */ +static ssize_t sys_led_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + unsigned char data = 0; + unsigned char color = 0; + unsigned char control = 0; + + mutex_lock(&cpld_data->cpld_lock); + data = inb(SYS_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + color = (data & 0x30) >> 4; + control = (data & 0x3); + + switch (color) { + case sys_led_on: + if (control == led_blk_1hz) + return sprintf(buf, "%s\n", LED_HZ_GABNK); + else if (control == led_blk_4hz) + return sprintf(buf, "%s\n", LED_QHZ_GABNK); + break; + case sys_led_amb: + if (control == led_blk_1hz) + return sprintf(buf, "%s\n", LED_HZ_ABNK); + else if (control == led_blk_4hz) + return sprintf(buf, "%s\n", LED_QHZ_ABNK); + else if (control == led_on) + return sprintf(buf, "%s\n", LED_AMBER); + break; + case sys_led_grn: + if (control == led_blk_1hz) + return sprintf(buf, "%s\n", LED_HZ_GBNK); + else if (control == led_blk_4hz) + return sprintf(buf, "%s\n", LED_QHZ_GBNK); + else if (control == led_on) + return sprintf(buf, "%s\n", LED_GREEN); + break; + default: + break; + } + + return sprintf(buf, "%s\n", LED_OFF); +} + +/** + * @brief Set the status led + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer of set value - off/on/blink + * @param count number of bytes in buffer + * @return number of bytes written, or error code < 0. + */ +static ssize_t sys_led_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + unsigned char data; + + mutex_lock(&cpld_data->cpld_lock); + data = inb(SYS_LED_ADDR); + if (sysfs_streq(buf, LED_OFF)) { + data &= 0xCF; + data = sys_led_off << 4; + data += led_off; + } else if (sysfs_streq(buf, LED_GREEN)) { + data &= 0xCF; + data = sys_led_grn << 4; + data &= 0xFC; + } else if (sysfs_streq(buf, LED_AMBER)) { + data &= 0xCF; + data = sys_led_amb << 4; + data &= 0xFC; + } else if (sysfs_streq(buf, LED_HZ_GBNK)) { + data &= 0xCF; + data = sys_led_grn << 4; + data += led_blk_1hz; + } else if (sysfs_streq(buf, LED_HZ_ABNK)) { + data &= 0xCF; + data = sys_led_amb << 4; + data += led_blk_1hz; + } else if (sysfs_streq(buf, LED_QHZ_GBNK)) { + data &= 0xCF; + data = sys_led_grn << 4; + data += led_blk_4hz; + } else if (sysfs_streq(buf, LED_QHZ_ABNK)) { + data &= 0xCF; + data = sys_led_amb << 4; + data += led_blk_4hz; + } else if (sysfs_streq(buf, LED_HZ_GABNK)) { + data &= 0xCF; + data = sys_led_on << 4; + data += led_blk_1hz; + } else if (sysfs_streq(buf, LED_QHZ_GABNK)) { + data &= 0xCF; + data = sys_led_on << 4; + data += led_blk_4hz; + } else + count = -EINVAL; + + outb(data, SYS_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + + return count; +} + +/** + * @brief Show alarm led + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer for get value + * @return led state - off/on/blink + */ +static ssize_t alarm_led_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + unsigned char data = 0; + unsigned char color = 0; + unsigned char control = 0; + + mutex_lock(&cpld_data->cpld_lock); + data = inb(ALARM_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + color = (data & 0x30) >> 4; + control = (data & 0x3); + + switch (color) { + case alarm_led_on: + if (control == led_blk_1hz) + return sprintf(buf, "%s\n", LED_HZ_GABNK); + else if (control == led_blk_4hz) + return sprintf(buf, "%s\n", LED_QHZ_GABNK); + break; + case alarm_led_amb: + if (control == led_blk_1hz) + return sprintf(buf, "%s\n", LED_HZ_ABNK); + else if (control == led_blk_4hz) + return sprintf(buf, "%s\n", LED_QHZ_ABNK); + else if (control == led_on) + return sprintf(buf, "%s\n", LED_AMBER); + break; + case alarm_led_grn: + if (control == led_blk_1hz) + return sprintf(buf, "%s\n", LED_HZ_GBNK); + else if (control == led_blk_4hz) + return sprintf(buf, "%s\n", LED_QHZ_GBNK); + else if (control == led_on) + return sprintf(buf, "%s\n", LED_GREEN); + break; + default: + break; + } + + return sprintf(buf, "%s\n", LED_OFF); +} + +/** + * @brief Set the alarm led + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer of set value - off/on/blink + * @param count number of bytes in buffer + * @return number of bytes written, or error code < 0. + */ +static ssize_t alarm_led_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + unsigned char data; + + mutex_lock(&cpld_data->cpld_lock); + data = inb(ALARM_LED_ADDR); + if (sysfs_streq(buf, LED_OFF)) { + data &= 0xCF; + data = alarm_led_off << 4; + data += led_off; + } else if (sysfs_streq(buf, LED_GREEN)) { + data &= 0xCF; + data = alarm_led_grn << 4; + data &= 0xFC; + } else if (sysfs_streq(buf, LED_AMBER)) { + data &= 0xCF; + data = alarm_led_amb << 4; + data &= 0xFC; + } else if (sysfs_streq(buf, LED_HZ_GBNK)) { + data &= 0xCF; + data = alarm_led_grn << 4; + data += led_blk_1hz; + } else if (sysfs_streq(buf, LED_HZ_ABNK)) { + data &= 0xCF; + data = alarm_led_amb << 4; + data += led_blk_1hz; + } else if (sysfs_streq(buf, LED_QHZ_GBNK)) { + data &= 0xCF; + data = alarm_led_grn << 4; + data += led_blk_4hz; + } else if (sysfs_streq(buf, LED_QHZ_ABNK)) { + data &= 0xCF; + data = alarm_led_amb << 4; + data += led_blk_4hz; + } else if (sysfs_streq(buf, LED_HZ_GABNK)) { + data &= 0xCF; + data = alarm_led_on << 4; + data += led_blk_1hz; + } else if (sysfs_streq(buf, LED_QHZ_GABNK)) { + data &= 0xCF; + data = alarm_led_on << 4; + data += led_blk_4hz; + } else + count = -EINVAL; + + outb(data, ALARM_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + + return count; +} + +static ssize_t pwr_led_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + unsigned char data = 0; + + mutex_lock(&cpld_data->cpld_lock); + data = inb(PSU_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + if ((data & 0x10) != 0) + return sprintf(buf, "%s\n", "auto"); + data = data & 0x3; + return sprintf(buf, "%s\n", data == pwr_led_grn ? "green" : + data == pwr_led_amb ? "amber" : "off"); +} + + +static ssize_t pwr_led_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + unsigned char led_status, data; + + if (sysfs_streq(buf, "off")) + led_status = pwr_led_off; + else if (sysfs_streq(buf, "green")) + led_status = pwr_led_grn; + else if (sysfs_streq(buf, "amber")) + led_status = pwr_led_amb; + else if (sysfs_streq(buf, "auto")) + led_status = pwr_led_auto; + else { + count = -EINVAL; + return count; + } + mutex_lock(&cpld_data->cpld_lock); + data = inb(PSU_LED_ADDR); + /* Set bit 4 as 0 to control pwrled by software */ + data = data & ~(0x13); + data = data | led_status; + outb(data, PSU_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + + return count; +} + +static ssize_t fan_led_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + unsigned char data = 0; + + mutex_lock(&cpld_data->cpld_lock); + data = inb(FAN_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + if ((data & 0x10) != 0) + return sprintf(buf, "%s\n", "auto"); + data = data & 0x3; + + return sprintf(buf, "%s\n", data == fan_led_grn ? "green" : + data == fan_led_amb ? "amber" : "off"); +} + + +static ssize_t fan_led_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + unsigned char led_status, data; + + if (sysfs_streq(buf, "off")) + led_status = fan_led_off; + else if (sysfs_streq(buf, "green")) + led_status = fan_led_grn; + else if (sysfs_streq(buf, "amber")) + led_status = fan_led_amb; + else if (sysfs_streq(buf, "auto")) + led_status = fan_led_auto; + else { + count = -EINVAL; + return count; + } + mutex_lock(&cpld_data->cpld_lock); + data = inb(FAN_LED_ADDR); + /* Set bit 4 as 0 to control fanled by software */ + data = data & ~(0x13); + data = data | led_status; + outb(data, FAN_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + + return count; +} + + +static BIN_ATTR_RO(dump, CPLD_REGISTER_SIZE); +static DEVICE_ATTR_RO(version); +static DEVICE_ATTR_RW(scratch); +static DEVICE_ATTR_RW(getreg); +static DEVICE_ATTR_WO(setreg); +static DEVICE_ATTR_RW(sys_led); +static DEVICE_ATTR_RW(alarm_led); +static DEVICE_ATTR_RW(pwr_led); +static DEVICE_ATTR_RW(fan_led); + +static struct attribute *cpld_b_attrs[] = { + &dev_attr_version.attr, + &dev_attr_scratch.attr, + &dev_attr_getreg.attr, + &dev_attr_setreg.attr, + &dev_attr_sys_led.attr, + &dev_attr_alarm_led.attr, + &dev_attr_pwr_led.attr, + &dev_attr_fan_led.attr, + NULL, +}; + +static struct bin_attribute *cpld_b_bin_attrs[] = { + &bin_attr_dump, + NULL, +}; + +static struct attribute_group cpld_b_attrs_grp = { + .attrs = cpld_b_attrs, + .bin_attrs = cpld_b_bin_attrs, +}; + +static struct resource cpld_b_resources[] = { + { + .start = 0xA100, + .end = 0xA1FF, + .flags = IORESOURCE_IO, + }, +}; + +static void cpld_b_dev_release(struct device *dev) +{ + return; +} + +static struct platform_device cpld_b_dev = { + .name = DRIVER_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(cpld_b_resources), + .resource = cpld_b_resources, + .dev = { + .release = cpld_b_dev_release, + } +}; + +static int cpld_b_drv_probe(struct platform_device *pdev) +{ + struct resource *res; + int err = 0; + + cpld_data = devm_kzalloc(&pdev->dev, sizeof(struct cpld_b_data), + GFP_KERNEL); + if (!cpld_data) + return -ENOMEM; + + mutex_init(&cpld_data->cpld_lock); + + cpld_data->read_addr = VERSION_ADDR; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (unlikely(!res)) { + dev_err(&pdev->dev, "Specified Resource Not Available...\n"); + return -ENODEV; + } + + err = sysfs_create_group(&pdev->dev.kobj, &cpld_b_attrs_grp); + if (err) { + dev_err(&pdev->dev, "Cannot create sysfs for baseboard CPLD\n"); + return err; + } + return 0; +} + +static int cpld_b_drv_remove(struct platform_device *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &cpld_b_attrs_grp); + + return 0; +} + +static struct platform_driver cpld_b_drv = { + .probe = cpld_b_drv_probe, + .remove = __exit_p(cpld_b_drv_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +int cpld_b_init(void) +{ + // Register platform device and platform driver + platform_device_register(&cpld_b_dev); + platform_driver_register(&cpld_b_drv); + + return 0; +} + +void cpld_b_exit(void) +{ + // Unregister platform device and platform driver + platform_driver_unregister(&cpld_b_drv); + platform_device_unregister(&cpld_b_dev); +} + +module_init(cpld_b_init); +module_exit(cpld_b_exit); + + +MODULE_AUTHOR("Celestica Inc."); +MODULE_DESCRIPTION("LPC CPLD baseboard driver"); +MODULE_VERSION("0.0.2"); +MODULE_LICENSE("GPL"); + diff --git a/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/mp2880.c b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/mp2880.c new file mode 100644 index 0000000000..dea2292e6f --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/mp2880.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for MPS MP2880 device + */ + +#include +#include +#include +#include "pmbus.h" + +static int mp2880_read_word_data(struct i2c_client *client, int page, int reg); + +static struct pmbus_driver_info mp2880_info = { + .pages = 1, + + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_POWER] = direct, + .format[PSC_TEMPERATURE] = direct, + + .m[PSC_VOLTAGE_OUT] = 512, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 0, + .m[PSC_CURRENT_OUT] = 2, + .b[PSC_CURRENT_OUT] = 0, + .R[PSC_CURRENT_OUT] = 0, + .m[PSC_POWER] = 4, + .b[PSC_POWER] = 0, + .R[PSC_POWER] = 0, + .m[PSC_TEMPERATURE] = 1, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 0, + + .func[0] = + PMBUS_HAVE_VOUT | PMBUS_HAVE_POUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT, + .read_word_data = &mp2880_read_word_data, +}; + +static int mp2880_read_word_data(struct i2c_client *client, int page, int reg) +{ + static char flag; + int ret = 0; + /* + * PSC_VOLTAGE_OUT format should be direct, but vout mode register is 0x00(linear). + * to avoid probe conflict in pmbus_probe, add this adaption here. + */ + if (2 != flag) { + if (0 == flag) + flag++; + else if (1 == flag) { + /* After pmbus_identify_common function, set the format as direct*/ + mp2880_info.format[PSC_VOLTAGE_OUT] = direct; + flag++; + } + } + + switch (reg) { + case PMBUS_READ_IOUT: + ret = pmbus_read_word_data(client, page, reg); + if (ret < 0) + return ret; + else + return ret & 0x7ff; + default: + return pmbus_read_word_data(client, page, reg); + } +} + +static int mp2880_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + return pmbus_do_probe(client, id, &mp2880_info); +} + +static const struct i2c_device_id mp2880_id[] = { + {"mp2880", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, mp2880_id); + +static const struct of_device_id __maybe_unused mp2880_of_match[] = { + { .compatible = "mps,mp2880", }, + {} +}; + +MODULE_DEVICE_TABLE(of, mp2880_of_match); + +static struct i2c_driver mp2880_driver = { + .driver = { + .name = "mp2880", + .of_match_table = of_match_ptr(mp2880_of_match), + }, + .probe = mp2880_probe, + .remove = pmbus_do_remove, + .id_table = mp2880_id, +}; + +module_i2c_driver(mp2880_driver); + +MODULE_AUTHOR("Nicholas Wu "); +MODULE_DESCRIPTION("PMBus driver for MPS MP2880"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/mp2975.c b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/mp2975.c new file mode 100644 index 0000000000..0d909e2197 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/mp2975.c @@ -0,0 +1,814 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers + * + * Copyright (c) 2020 Nvidia Technologies. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include "pmbus.h" + +/* Vendor specific registers. */ +#define MP2975_MFR_APS_HYS_R2 0x0d +#define MP2975_MFR_SLOPE_TRIM3 0x1d +#define MP2975_MFR_VR_MULTI_CONFIG_R1 0x0d +#define MP2975_MFR_VR_MULTI_CONFIG_R2 0x1d +#define MP2975_MFR_APS_DECAY_ADV 0x56 +#define MP2975_MFR_DC_LOOP_CTRL 0x59 +#define MP2975_MFR_OCP_UCP_PHASE_SET 0x65 +#define MP2975_MFR_VR_CONFIG1 0x68 +#define MP2975_MFR_READ_CS1_2 0x82 +#define MP2975_MFR_READ_CS3_4 0x83 +#define MP2975_MFR_READ_CS5_6 0x84 +#define MP2975_MFR_READ_CS7_8 0x85 +#define MP2975_MFR_READ_CS9_10 0x86 +#define MP2975_MFR_READ_CS11_12 0x87 +#define MP2975_MFR_READ_IOUT_PK 0x90 +#define MP2975_MFR_READ_POUT_PK 0x91 +#define MP2975_MFR_READ_VREF_R1 0xa1 +#define MP2975_MFR_READ_VREF_R2 0xa3 +#define MP2975_MFR_OVP_TH_SET 0xe5 +#define MP2975_MFR_UVP_SET 0xe6 + +#define MP2975_VOUT_FORMAT BIT(15) +#define MP2975_VID_STEP_SEL_R1 BIT(4) +#define MP2975_IMVP9_EN_R1 BIT(13) +#define MP2975_VID_STEP_SEL_R2 BIT(3) +#define MP2975_IMVP9_EN_R2 BIT(12) +#define MP2975_PRT_THRES_DIV_OV_EN BIT(14) +#define MP2975_DRMOS_KCS GENMASK(13, 12) +#define MP2975_PROT_DEV_OV_OFF 10 +#define MP2975_PROT_DEV_OV_ON 5 +#define MP2975_SENSE_AMPL BIT(11) +#define MP2975_SENSE_AMPL_UNIT 1 +#define MP2975_SENSE_AMPL_HALF 2 +#define MP2975_VIN_UV_LIMIT_UNIT 8 + +#define MP2975_PSC_VOLTAGE_OUT 0x40 +#define MP2975_MAX_PHASE_RAIL1 8 +#define MP2975_MAX_PHASE_RAIL2 4 +#define MP2975_PAGE_NUM 2 + +#define MP2975_RAIL2_FUNC (PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_POUT) + +struct mp2975_data { + struct pmbus_driver_info info; + int vout_scale; + int vid_step[MP2975_PAGE_NUM]; + int vref[MP2975_PAGE_NUM]; + int vref_off[MP2975_PAGE_NUM]; + int vout_max[MP2975_PAGE_NUM]; + int vout_ov_fixed[MP2975_PAGE_NUM]; + int vout_format[MP2975_PAGE_NUM]; + int curr_sense_gain[MP2975_PAGE_NUM]; +}; + +enum vrm_ver { imvp9 = 3, amd625mv }; +#define to_mp2975_data(x) container_of(x, struct mp2975_data, info) + +static int mp2975_read_byte_data(struct i2c_client *client, int page, int reg) +{ + switch (reg) { + case PMBUS_VOUT_MODE: + /* + * Enforce VOUT direct format, since device allows to set the + * different formats for the different rails. Conversion from + * VID to direct provided by driver internally, in case it is + * necessary. + */ + return MP2975_PSC_VOLTAGE_OUT; + default: + return -ENODATA; + } +} + +static int +mp2975_read_word_helper(struct i2c_client *client, int page, int phase, u8 reg, + u16 mask) +{ + int ret = pmbus_read_word_data(client, page, reg); + + return (ret > 0) ? ret & mask : ret; +} + +static int +mp2975_vid2direct(int vrf, int val) +{ + switch (vrf) { + case vr12: + if (val >= 0x01) + return 250 + (val - 1) * 5; + break; + case vr13: + if (val >= 0x01) + return 500 + (val - 1) * 10; + break; + case imvp9: + if (val >= 0x01) + return 200 + (val - 1) * 10; + break; + default: + return -EINVAL; + } + return 0; +} + +static int +mp2975_read_phase(struct i2c_client *client, struct mp2975_data *data, + int page, int phase, u8 reg) +{ + u16 mask; + int shift = 0, ret; + + if ((phase + 1) % MP2975_PAGE_NUM) { + mask = GENMASK(7, 0); + } else { + mask = GENMASK(15, 8); + shift = 8; + } + + ret = mp2975_read_word_helper(client, page, phase, reg, mask); + if (ret < 0) + return ret; + + ret >>= shift; + + /* + * Output value is calculated as: (READ_CSx / 80 – 1.23) / (Kcs * Rcs) + * where: + * - Kcs is the DrMOS current sense gain of power stage, which is + * obtained from the register MP2975_MFR_VR_CONFIG1, bits 13-12 with + * the following selection of DrMOS (data->curr_sense_gain[page]): + * 00b - 5µA/A, 01b - 8.5µA/A, 10b - 9.7µA/A, 11b - 10µA/A. + * - Rcs is the internal phase current sense resistor which is constant + * value 1kΩ. + */ + return DIV_ROUND_CLOSEST(DIV_ROUND_CLOSEST(ret * 100 - 9840, 100) * + 100, data->curr_sense_gain[page]); +} + +static int +mp2975_read_phases(struct i2c_client *client, struct mp2975_data *data, + int page, int phase) +{ + int ret; + + if (page) { + switch (phase) { + case 0 ... 1: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS7_8); + break; + case 2 ... 3: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS9_10); + break; + case 4 ... 5: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS11_12); + break; + default: + return -ENODATA; + } + } else { + switch (phase) { + case 0 ... 1: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS1_2); + break; + case 2 ... 3: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS3_4); + break; + case 4 ... 5: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS5_6); + break; + case 6 ... 7: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS7_8); + break; + case 8 ... 9: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS9_10); + break; + case 10 ... 11: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS11_12); + break; + default: + return -ENODATA; + } + } + return ret; +} + +static int mp2975_read_word_data(struct i2c_client *client, int page, + int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct mp2975_data *data = to_mp2975_data(info); + int phase = 255; + int ret; + + switch (reg) { + case PMBUS_OT_FAULT_LIMIT: + ret = mp2975_read_word_helper(client, page, phase, reg, + GENMASK(7, 0)); + break; + case PMBUS_VIN_OV_FAULT_LIMIT: + ret = mp2975_read_word_helper(client, page, phase, reg, + GENMASK(7, 0)); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST(ret, MP2975_VIN_UV_LIMIT_UNIT); + break; + case PMBUS_VOUT_OV_FAULT_LIMIT: + /* + * Register provides two values for over-voltage protection + * threshold for fixed (ovp2) and tracking (ovp1) modes. The + * minimum of these two values is provided as over-voltage + * fault alarm. + */ + ret = mp2975_read_word_helper(client, page, phase, + MP2975_MFR_OVP_TH_SET, + GENMASK(2, 0)); + if (ret < 0) + return ret; + + ret = min_t(int, data->vout_max[page] + 50 * (ret + 1), + data->vout_ov_fixed[page]); + break; + case PMBUS_VOUT_UV_FAULT_LIMIT: + ret = mp2975_read_word_helper(client, page, phase, + MP2975_MFR_UVP_SET, + GENMASK(2, 0)); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST(data->vref[page] * 10 - 50 * + (ret + 1) * data->vout_scale, 10); + break; + case PMBUS_READ_VOUT: + ret = mp2975_read_word_helper(client, page, phase, reg, + GENMASK(11, 0)); + if (ret < 0) + return ret; + + /* + * READ_VOUT can be provided in VID or direct format. The + * format type is specified by bit 15 of the register + * MP2975_MFR_DC_LOOP_CTRL. The driver enforces VOUT direct + * format, since device allows to set the different formats for + * the different rails and also all VOUT limits registers are + * provided in a direct format. In case format is VID - convert + * to direct. + */ + if (data->vout_format[page] == vid) + //ret = mp2975_vid2direct(info->vrm_version[page], ret); + ret = mp2975_vid2direct(info->vrm_version, ret); //to suit kernel v5.4.40 + break; + case PMBUS_VIRT_READ_POUT_MAX: + ret = mp2975_read_word_helper(client, page, phase, + MP2975_MFR_READ_POUT_PK, + GENMASK(12, 0)); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST(ret, 4); + break; + case PMBUS_VIRT_READ_IOUT_MAX: + ret = mp2975_read_word_helper(client, page, phase, + MP2975_MFR_READ_IOUT_PK, + GENMASK(12, 0)); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST(ret, 4); + break; + case PMBUS_READ_IOUT: + ret = mp2975_read_phases(client, data, page, phase); + if (ret < 0) + return ret; + + break; + case PMBUS_UT_WARN_LIMIT: + case PMBUS_UT_FAULT_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_VOUT_UV_WARN_LIMIT: + case PMBUS_VOUT_OV_WARN_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_IIN_OC_FAULT_LIMIT: + case PMBUS_IOUT_OC_LV_FAULT_LIMIT: + case PMBUS_IIN_OC_WARN_LIMIT: + case PMBUS_IOUT_OC_WARN_LIMIT: + case PMBUS_IOUT_OC_FAULT_LIMIT: + case PMBUS_IOUT_UC_FAULT_LIMIT: + case PMBUS_POUT_OP_FAULT_LIMIT: + case PMBUS_POUT_OP_WARN_LIMIT: + case PMBUS_PIN_OP_WARN_LIMIT: + return -ENXIO; + default: + return -ENODATA; + } + + return ret; +} + +static int mp2975_identify_multiphase_rail2(struct i2c_client *client) +{ + int ret; + + /* + * Identify multiphase for rail 2 - could be from 0 to 4. + * In case phase number is zero – only page zero is supported + */ + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); + if (ret < 0) + return ret; + + /* Identify multiphase for rail 2 - could be from 0 to 4. */ + ret = i2c_smbus_read_word_data(client, MP2975_MFR_VR_MULTI_CONFIG_R2); + if (ret < 0) + return ret; + + ret &= GENMASK(2, 0); + return (ret >= 4) ? 4 : ret; +} +#if 0 +static void mp2975_set_phase_rail1(struct pmbus_driver_info *info) +{ + int i; + + for (i = 0 ; i < info->phases[0]; i++) + info->pfunc[i] = PMBUS_HAVE_IOUT; +} + +static void mp2975_set_phase_rail2(struct pmbus_driver_info *info) +{ + int max_rail, i; + + /* Set phases for rail 2 from upper to lower. */ + max_rail = info->phases[1] % (MP2975_MAX_PHASE_RAIL2 - 1); + for (i = 1 ; i <= max_rail; i++) + info->pfunc[MP2975_MAX_PHASE_RAIL1 - i] = PMBUS_HAVE_IOUT; +} + +static int mp2975_set_multiphase_rail2(struct pmbus_driver_info *info) +{ + switch (info->phases[1]) { + case 1 ... 7: + mp2975_set_phase_rail2(info); + return 0; + default: + return -EINVAL; + } +} + +static int +mp2975_identify_multiphase(struct i2c_client *client, struct mp2975_data *data, + struct pmbus_driver_info *info) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); + if (ret < 0) + return ret; + + /* Identify multiphase for rail 1 - could be from 1 to 8. */ + ret = i2c_smbus_read_word_data(client, MP2975_MFR_VR_MULTI_CONFIG_R1); + if (ret > 0) + info->phases[0] = ret & GENMASK(3, 0); + else + return (ret) ? ret : -EINVAL; + + /* + * The device provides a total of 8 PWM pins, and can be configured + * to different phase count applications for rail 1 and rail 2. + * Rail 1 can be set to 8 phases, while rail 2 can only be set to 4 + * phases at most. When rail 1’s phase count is configured as 0, rail + * 1 operates with 1-phase DCM. When rail 2 phase count is configured + * as 0, rail 2 is disabled. + */ + switch (info->phases[0]) { + case 1 ... 4: + mp2975_set_phase_rail1(info); + return mp2975_set_multiphase_rail2(info); + case 5: + mp2975_set_phase_rail1(info); + switch (info->phases[1]) { + case 1 ... 3: + return mp2975_set_multiphase_rail2(info); + default: + return 0; + } + case 6: + mp2975_set_phase_rail1(info); + switch (info->phases[1]) { + case 1 ... 2: + return mp2975_set_multiphase_rail2(info); + default: + return 0; + } + case 7: + mp2975_set_phase_rail1(info); + switch (info->phases[1]) { + case 1: + return mp2975_set_multiphase_rail2(info); + default: + return 0; + } + case 8: + mp2975_set_phase_rail1(info); + return 0; + default: + return -EINVAL; + } +} +#endif +static int +mp2975_identify_vid(struct i2c_client *client, struct mp2975_data *data, + struct pmbus_driver_info *info, u32 reg, int page, + u32 imvp_bit, u32 vr_bit) +{ + int ret; + + /* Identify VID mode and step selection. */ + ret = i2c_smbus_read_word_data(client, reg); + if (ret < 0) + return ret; + + if (ret & imvp_bit) { + //info->vrm_version[page] = imvp9; + info->vrm_version = imvp9; //to suit kernel v5.4.40 + data->vid_step[page] = MP2975_PROT_DEV_OV_OFF; + } else if (ret & vr_bit) { + //info->vrm_version[page] = vr12; + info->vrm_version = vr12; //to suit kernel v5.4.40 + data->vid_step[page] = MP2975_PROT_DEV_OV_ON; + } else { + //info->vrm_version[page] = vr13; + info->vrm_version = vr13; //to suit kernel v5.4.40 + data->vid_step[page] = MP2975_PROT_DEV_OV_OFF; + } + + return 0; +} + +static int +mp2975_identify_rails_vid(struct i2c_client *client, struct mp2975_data *data, + struct pmbus_driver_info *info) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); + if (ret < 0) + return ret; + + /* Identify VID mode for rail 1. */ + ret = mp2975_identify_vid(client, data, info, + MP2975_MFR_VR_MULTI_CONFIG_R1, 0, + MP2975_IMVP9_EN_R1, MP2975_VID_STEP_SEL_R1); + if (ret < 0) + return ret; + + /* Identify VID mode for rail 2, if connected. */ + if (info->pages == MP2975_PAGE_NUM) + ret = mp2975_identify_vid(client, data, info, + MP2975_MFR_VR_MULTI_CONFIG_R2, 1, + MP2975_IMVP9_EN_R2, + MP2975_VID_STEP_SEL_R2); + return ret; +} + +static int +mp2975_current_sense_gain_get(struct i2c_client *client, + struct mp2975_data *data) +{ + int i, ret; + + /* + * Obtain DrMOS current sense gain of power stage from the register + * MP2975_MFR_VR_CONFIG1, bits 13-12. The value is selected as below: + * 00b - 5µA/A, 01b - 8.5µA/A, 10b - 9.7µA/A, 11b - 10µA/A. Other + * values are invalid. + */ + for (i = 0 ; i < data->info.pages; i++) { + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i); + if (ret < 0) + return ret; + ret = i2c_smbus_read_word_data(client, + MP2975_MFR_VR_CONFIG1); + if (ret < 0) + return ret; + + switch ((ret & MP2975_DRMOS_KCS) >> 12) { + case 0: + data->curr_sense_gain[i] = 50; + break; + case 1: + data->curr_sense_gain[i] = 85; + break; + case 2: + data->curr_sense_gain[i] = 97; + break; + default: + data->curr_sense_gain[i] = 100; + break; + } + } + + return 0; +} + +static int +mp2975_vref_get(struct i2c_client *client, struct mp2975_data *data, + struct pmbus_driver_info *info) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 3); + if (ret < 0) + return ret; + + /* Get voltage reference value for rail 1. */ + ret = i2c_smbus_read_word_data(client, MP2975_MFR_READ_VREF_R1); + if (ret < 0) + return ret; + + data->vref[0] = ret * data->vid_step[0]; + + /* Get voltage reference value for rail 2, if connected. */ + if (data->info.pages == MP2975_PAGE_NUM) { + ret = i2c_smbus_read_word_data(client, MP2975_MFR_READ_VREF_R2); + if (ret < 0) + return ret; + + data->vref[1] = ret * data->vid_step[1]; + } + return 0; +} + +static int +mp2975_vref_offset_get(struct i2c_client *client, struct mp2975_data *data, + int page) +{ + int ret; + + ret = i2c_smbus_read_word_data(client, MP2975_MFR_OVP_TH_SET); + if (ret < 0) + return ret; + + switch ((ret & GENMASK(5, 3)) >> 3) { + case 1: + data->vref_off[page] = 140; + break; + case 2: + data->vref_off[page] = 220; + break; + case 4: + data->vref_off[page] = 400; + break; + default: + return -EINVAL; + } + return 0; +} + +static int +mp2975_vout_max_get(struct i2c_client *client, struct mp2975_data *data, + struct pmbus_driver_info *info, int page) +{ + int ret; + + /* Get maximum reference voltage of VID-DAC in VID format. */ + ret = i2c_smbus_read_word_data(client, PMBUS_VOUT_MAX); + if (ret < 0) + return ret; + + //data->vout_max[page] = mp2975_vid2direct(info->vrm_version[page], ret & + // GENMASK(8, 0)); + data->vout_max[page] = mp2975_vid2direct(info->vrm_version, ret & + GENMASK(8, 0)); //to suit kernel v5.4.40 + return 0; +} + +static int +mp2975_identify_vout_format(struct i2c_client *client, + struct mp2975_data *data, int page) +{ + int ret; + + ret = i2c_smbus_read_word_data(client, MP2975_MFR_DC_LOOP_CTRL); + if (ret < 0) + return ret; + + if (ret & MP2975_VOUT_FORMAT) + data->vout_format[page] = vid; + else + data->vout_format[page] = direct; + return 0; +} + +static int +mp2975_vout_ov_scale_get(struct i2c_client *client, struct mp2975_data *data, + struct pmbus_driver_info *info) +{ + int thres_dev, sense_ampl, ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); + if (ret < 0) + return ret; + + /* + * Get divider for over- and under-voltage protection thresholds + * configuration from the Advanced Options of Auto Phase Shedding and + * decay register. + */ + ret = i2c_smbus_read_word_data(client, MP2975_MFR_APS_DECAY_ADV); + if (ret < 0) + return ret; + thres_dev = ret & MP2975_PRT_THRES_DIV_OV_EN ? MP2975_PROT_DEV_OV_ON : + MP2975_PROT_DEV_OV_OFF; + + /* Select the gain of remote sense amplifier. */ + ret = i2c_smbus_read_word_data(client, PMBUS_VOUT_SCALE_LOOP); + if (ret < 0) + return ret; + sense_ampl = ret & MP2975_SENSE_AMPL ? MP2975_SENSE_AMPL_HALF : + MP2975_SENSE_AMPL_UNIT; + + data->vout_scale = sense_ampl * thres_dev; + + return 0; +} + +static int +mp2975_vout_per_rail_config_get(struct i2c_client *client, + struct mp2975_data *data, + struct pmbus_driver_info *info) +{ + int i, ret; + + for (i = 0; i < data->info.pages; i++) { + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i); + if (ret < 0) + return ret; + + /* Obtain voltage reference offsets. */ + ret = mp2975_vref_offset_get(client, data, i); + if (ret < 0) + return ret; + + /* Obtain maximum voltage values. */ + ret = mp2975_vout_max_get(client, data, info, i); + if (ret < 0) + return ret; + + /* + * Get VOUT format for READ_VOUT command : VID or direct. + * Pages on same device can be configured with different + * formats. + */ + ret = mp2975_identify_vout_format(client, data, i); + if (ret < 0) + return ret; + + /* + * Set over-voltage fixed value. Thresholds are provided as + * fixed value, and tracking value. The minimum of them are + * exposed as over-voltage critical threshold. + */ + data->vout_ov_fixed[i] = data->vref[i] + + DIV_ROUND_CLOSEST(data->vref_off[i] * + data->vout_scale, + 10); + } + + return 0; +} + +static struct pmbus_driver_info mp2975_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_POWER] = direct, + .m[PSC_TEMPERATURE] = 1, + .m[PSC_VOLTAGE_OUT] = 1, + .R[PSC_VOLTAGE_OUT] = 3, + .m[PSC_CURRENT_OUT] = 1, + .m[PSC_POWER] = 1, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_POUT | + PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT, + .read_byte_data = mp2975_read_byte_data, + .read_word_data = mp2975_read_word_data, +}; + +static int mp2975_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pmbus_driver_info *info; + struct mp2975_data *data; + int ret; + + data = devm_kzalloc(&client->dev, sizeof(struct mp2975_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + memcpy(&data->info, &mp2975_info, sizeof(*info)); + info = &data->info; + + /* Identify multiphase configuration for rail 2. */ + ret = mp2975_identify_multiphase_rail2(client); + if (ret < 0) + return ret; + + if (ret) + data->info.pages = MP2975_PAGE_NUM; + + if (ret) { + /* Two rails are connected. */ + data->info.pages = MP2975_PAGE_NUM; +#if 0 + data->info.phases[1] = ret; +#endif + data->info.func[1] = MP2975_RAIL2_FUNC; + } +#if 0 + /* Identify multiphase configuration. */ + ret = mp2975_identify_multiphase(client, data, info); + if (ret) + return ret; +#endif + /* Identify VID setting per rail. */ + ret = mp2975_identify_rails_vid(client, data, info); + if (ret < 0) + return ret; + + /* Obtain current sense gain of power stage. */ + ret = mp2975_current_sense_gain_get(client, data); + if (ret) + return ret; + + /* Obtain voltage reference values. */ + ret = mp2975_vref_get(client, data, info); + if (ret) + return ret; + + /* Obtain vout over-voltage scales. */ + ret = mp2975_vout_ov_scale_get(client, data, info); + if (ret < 0) + return ret; + + /* Obtain offsets, maximum and format for vout. */ + ret = mp2975_vout_per_rail_config_get(client, data, info); + if (ret) + return ret; + + return pmbus_do_probe(client, id, info); +} + +static const struct i2c_device_id mp2975_id[] = { + {"mp2975", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, mp2975_id); + +static const struct of_device_id __maybe_unused mp2975_of_match[] = { + {.compatible = "mps,mp2975"}, + {} +}; +MODULE_DEVICE_TABLE(of, mp2975_of_match); + +static struct i2c_driver mp2975_driver = { + .driver = { + .name = "mp2975", + .of_match_table = of_match_ptr(mp2975_of_match), + }, + .probe = mp2975_probe, + .remove = pmbus_do_remove, + .id_table = mp2975_id, +}; + +module_i2c_driver(mp2975_driver); + +MODULE_AUTHOR("Vadim Pasternak "); +MODULE_DESCRIPTION("PMBus driver for MPS MP2975 device"); +MODULE_LICENSE("GPL"); + + diff --git a/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/mp5023.c b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/mp5023.c new file mode 100644 index 0000000000..84f16401f3 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/mp5023.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for MPS MP5023 Hot-Swap Controller + */ + +#include +#include +#include +#include "pmbus.h" + +static struct pmbus_driver_info mp5023_info = { + .pages = 1, + + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_POWER] = direct, + .format[PSC_TEMPERATURE] = direct, + + .m[PSC_VOLTAGE_IN] = 32, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = 0, + .m[PSC_VOLTAGE_OUT] = 32, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 0, + .m[PSC_CURRENT_OUT] = 16, + .b[PSC_CURRENT_OUT] = 0, + .R[PSC_CURRENT_OUT] = 0, + .m[PSC_POWER] = 1, + .b[PSC_POWER] = 0, + .R[PSC_POWER] = 0, + .m[PSC_TEMPERATURE] = 2, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 0, + + .func[0] = + PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_PIN | + PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT | + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP, +}; + +static int mp5023_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + return pmbus_do_probe(client, id, &mp5023_info); +} + +static const struct i2c_device_id mp5023_id[] = { + {"mp5023", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, mp5023_id); + +static const struct of_device_id __maybe_unused mp5023_of_match[] = { + { .compatible = "mps,mp5023", }, + {} +}; + +MODULE_DEVICE_TABLE(of, mp5023_of_match); + +static struct i2c_driver mp5023_driver = { + .driver = { + .name = "mp5023", + .of_match_table = of_match_ptr(mp5023_of_match), + }, + .probe = mp5023_probe, + .remove = pmbus_do_remove, + .id_table = mp5023_id, +}; + +module_i2c_driver(mp5023_driver); + +MODULE_AUTHOR("Howard Chiu "); +MODULE_DESCRIPTION("PMBus driver for MPS MP5023 HSC"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/platform-fan.c b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/platform-fan.c new file mode 100644 index 0000000000..07dbf7f498 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/platform-fan.c @@ -0,0 +1,681 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * A hwmon driver for the Celestica DS4101 fan + * + * Copyright (C) 2023 Celestica Inc. + * Nicholas Wu + * + * 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 + +#define DRVNAME "platform_fan" + +#define VAL_TO_RPM_FACTOR 157 + +#define FAN_LED_OFF 3 +#define FAN_LED_AMBER 2 +#define FAN_LED_GREEN 1 + +#define FANCPLD_VER_REG 0 + +static struct ds4101_fan_data *fan_update_device(struct device *dev); +static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); + +static ssize_t set_led_ctrl(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf); + +/* fan related data, the index should match sysfs_fan_attributes + */ +static const u8 ds4101_fan_reg[] = { + 0x22, /* FAN1 PWM Reg */ + 0x32, /* FAN2 PWM Reg */ + 0x42, /* FAN3 PWM Reg */ + 0x52, /* FAN4 PWM Reg */ + 0x62, /* FAN5 PWM Reg */ + 0x72, /* FAN6 PWM Reg */ + 0x82, /* FAN7 PWM Reg */ + 0x20, /* FAN1F Speed Reg */ + 0x21, /* FAN1R Speed Reg */ + 0x30, /* FAN2F Speed Reg */ + 0x31, /* FAN2R Speed Reg */ + 0x40, /* FAN3F Speed Reg */ + 0x41, /* FAN3R Speed Reg */ + 0x50, /* FAN4F Speed Reg */ + 0x51, /* FAN4R Speed Reg */ + 0x60, /* FAN5F Speed Reg */ + 0x61, /* FAN5R Speed Reg */ + 0x70, /* FAN6F Speed Reg */ + 0x71, /* FAN6R Speed Reg */ + 0x80, /* FAN7F Speed Reg */ + 0x81, /* FAN7R Speed Reg */ + 0x26, /* FAN1 Direction Reg */ + 0x36, /* FAN2 Direction Reg */ + 0x46, /* FAN3 Direction Reg */ + 0x56, /* FAN4 Direction Reg */ + 0x66, /* FAN5 Direction Reg */ + 0x76, /* FAN6 Direction Reg */ + 0x86, /* FAN7 Direction Reg */ + 0x26, /* FAN1 Present Reg */ + 0x36, /* FAN2 Present Reg */ + 0x46, /* FAN3 Present Reg */ + 0x56, /* FAN4 Present Reg */ + 0x66, /* FAN5 Present Reg */ + 0x76, /* FAN6 Present Reg */ + 0x86, /* FAN7 Present Reg */ + 0x24, /* FAN1 Led Control Reg */ + 0x34, /* FAN2 Led Control Reg */ + 0x44, /* FAN3 Led Control Reg */ + 0x54, /* FAN4 Led Control Reg */ + 0x64, /* FAN5 Led Control Reg */ + 0x74, /* FAN6 Led Control Reg */ + 0x84, /* FAN7 Led Control Reg */ + 0x20, /* FAN1F Speed Reg */ + 0x30, /* FAN2F Speed Reg */ + 0x40, /* FAN3F Speed Reg */ + 0x50, /* FAN4F Speed Reg */ + 0x60, /* FAN5F Speed Reg */ + 0x70, /* FAN6F Speed Reg */ + 0x80, /* FAN7F Speed Reg */ +}; + +/* Each client has this additional data */ +struct ds4101_fan_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 reg_val[ARRAY_SIZE(ds4101_fan_reg)]; /* Register value */ +}; + + +enum sysfs_fan_attributes { + FAN1_DUTY_CYCLE_PERCENTAGE, + FAN2_DUTY_CYCLE_PERCENTAGE, + FAN3_DUTY_CYCLE_PERCENTAGE, + FAN4_DUTY_CYCLE_PERCENTAGE, + FAN5_DUTY_CYCLE_PERCENTAGE, + FAN6_DUTY_CYCLE_PERCENTAGE, + FAN7_DUTY_CYCLE_PERCENTAGE, + FAN1_FRONT_SPEED_RPM, + FAN1_REAR_SPEED_RPM, + FAN2_FRONT_SPEED_RPM, + FAN2_REAR_SPEED_RPM, + FAN3_FRONT_SPEED_RPM, + FAN3_REAR_SPEED_RPM, + FAN4_FRONT_SPEED_RPM, + FAN4_REAR_SPEED_RPM, + FAN5_FRONT_SPEED_RPM, + FAN5_REAR_SPEED_RPM, + FAN6_FRONT_SPEED_RPM, + FAN6_REAR_SPEED_RPM, + FAN7_FRONT_SPEED_RPM, + FAN7_REAR_SPEED_RPM, + FAN1_DIRECTION, + FAN2_DIRECTION, + FAN3_DIRECTION, + FAN4_DIRECTION, + FAN5_DIRECTION, + FAN6_DIRECTION, + FAN7_DIRECTION, + FAN1_PRESENT, + FAN2_PRESENT, + FAN3_PRESENT, + FAN4_PRESENT, + FAN5_PRESENT, + FAN6_PRESENT, + FAN7_PRESENT, + FAN1_LED, + FAN2_LED, + FAN3_LED, + FAN4_LED, + FAN5_LED, + FAN6_LED, + FAN7_LED, + FAN1_FAULT, + FAN2_FAULT, + FAN3_FAULT, + FAN4_FAULT, + FAN5_FAULT, + FAN6_FAULT, + FAN7_FAULT, +}; + +/* Define attributes + */ +#define DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_fault, S_IRUGO, fan_show_value, NULL, FAN##index##_FAULT) + +#define DECLARE_FAN_FAULT_ATTR(index) &sensor_dev_attr_fan##index##_fault.dev_attr.attr + +#define DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_direction, S_IRUGO, fan_show_value, NULL, FAN##index##_DIRECTION) + +#define DECLARE_FAN_DIRECTION_ATTR(index) \ + &sensor_dev_attr_fan##index##_direction.dev_attr.attr + +#define DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(pwm##index, S_IWUSR | S_IRUGO, fan_show_value, set_duty_cycle, FAN##index##_DUTY_CYCLE_PERCENTAGE) + +#define DECLARE_FAN_DUTY_CYCLE_ATTR(index) &sensor_dev_attr_pwm##index.dev_attr.attr + +#define DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_present, S_IRUGO, fan_show_value, NULL, FAN##index##_PRESENT) + +#define DECLARE_FAN_PRESENT_ATTR(index) &sensor_dev_attr_fan##index##_present.dev_attr.attr + +#define DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_front_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_FRONT_SPEED_RPM);\ + static SENSOR_DEVICE_ATTR(fan##index##_rear_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_REAR_SPEED_RPM); + +#define DECLARE_FAN_SPEED_RPM_ATTR(index) &sensor_dev_attr_fan##index##_front_speed_rpm.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_rear_speed_rpm.dev_attr.attr + +#define DECLARE_FAN_LED_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_led, S_IRUGO | S_IWUSR, fan_show_value, set_led_ctrl, FAN##index##_LED) + +#define DECLARE_FAN_LED_ATTR(index) &sensor_dev_attr_fan##index##_led.dev_attr.attr + +DEVICE_ATTR_RO(version); + +/* 7 fan fault attributes in this platform */ +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(1); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(2); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(3); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(4); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(5); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(6); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(7); +/* 7 fan speed(rpm) attributes in this platform */ +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(1); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(2); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(3); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(4); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(5); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(6); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(7); +/* 7 fan present attributes in this platform */ +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(1); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(2); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(3); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(4); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(5); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(6); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(7); +/* 7 fan direction attribute in this platform */ +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(1); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(2); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(3); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(4); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(5); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(6); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(7); +/* 7 fan duty cycle attribute in this platform */ +DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(1); +DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(2); +DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(3); +DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(4); +DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(5); +DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(6); +DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(7); +/* 7 fan led attribute in this platform */ +DECLARE_FAN_LED_SENSOR_DEV_ATTR(1); +DECLARE_FAN_LED_SENSOR_DEV_ATTR(2); +DECLARE_FAN_LED_SENSOR_DEV_ATTR(3); +DECLARE_FAN_LED_SENSOR_DEV_ATTR(4); +DECLARE_FAN_LED_SENSOR_DEV_ATTR(5); +DECLARE_FAN_LED_SENSOR_DEV_ATTR(6); +DECLARE_FAN_LED_SENSOR_DEV_ATTR(7); + +static struct attribute *ds4101_fan_attributes[] = { + /* fan related attributes */ + DECLARE_FAN_DUTY_CYCLE_ATTR(1), + DECLARE_FAN_DUTY_CYCLE_ATTR(2), + DECLARE_FAN_DUTY_CYCLE_ATTR(3), + DECLARE_FAN_DUTY_CYCLE_ATTR(4), + DECLARE_FAN_DUTY_CYCLE_ATTR(5), + DECLARE_FAN_DUTY_CYCLE_ATTR(6), + DECLARE_FAN_DUTY_CYCLE_ATTR(7), + DECLARE_FAN_SPEED_RPM_ATTR(1), + DECLARE_FAN_SPEED_RPM_ATTR(2), + DECLARE_FAN_SPEED_RPM_ATTR(3), + DECLARE_FAN_SPEED_RPM_ATTR(4), + DECLARE_FAN_SPEED_RPM_ATTR(5), + DECLARE_FAN_SPEED_RPM_ATTR(6), + DECLARE_FAN_SPEED_RPM_ATTR(7), + DECLARE_FAN_DIRECTION_ATTR(1), + DECLARE_FAN_DIRECTION_ATTR(2), + DECLARE_FAN_DIRECTION_ATTR(3), + DECLARE_FAN_DIRECTION_ATTR(4), + DECLARE_FAN_DIRECTION_ATTR(5), + DECLARE_FAN_DIRECTION_ATTR(6), + DECLARE_FAN_DIRECTION_ATTR(7), + DECLARE_FAN_PRESENT_ATTR(1), + DECLARE_FAN_PRESENT_ATTR(2), + DECLARE_FAN_PRESENT_ATTR(3), + DECLARE_FAN_PRESENT_ATTR(4), + DECLARE_FAN_PRESENT_ATTR(5), + DECLARE_FAN_PRESENT_ATTR(6), + DECLARE_FAN_PRESENT_ATTR(7), + DECLARE_FAN_LED_ATTR(1), + DECLARE_FAN_LED_ATTR(2), + DECLARE_FAN_LED_ATTR(3), + DECLARE_FAN_LED_ATTR(4), + DECLARE_FAN_LED_ATTR(5), + DECLARE_FAN_LED_ATTR(6), + DECLARE_FAN_LED_ATTR(7), + DECLARE_FAN_FAULT_ATTR(1), + DECLARE_FAN_FAULT_ATTR(2), + DECLARE_FAN_FAULT_ATTR(3), + DECLARE_FAN_FAULT_ATTR(4), + DECLARE_FAN_FAULT_ATTR(5), + DECLARE_FAN_FAULT_ATTR(6), + DECLARE_FAN_FAULT_ATTR(7), + &dev_attr_version.attr, + NULL +}; + +#define FAN_DUTY_CYCLE_REG_MASK 0xFF +#define FAN_MAX_DUTY_CYCLE 100 + +/* CPLD version attributes */ +static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int len = 0; + unsigned char value = 0; + struct i2c_client *client = to_i2c_client(dev); + + value = i2c_smbus_read_byte_data(client, FANCPLD_VER_REG); + if (value < 0) + return value; + len = sprintf(buf, "%d.%d\n", value >> 4, value & 0x0F); + + return len; +} + +static int fan_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int fan_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* fan utility functions */ +static u8 reg_val_to_duty_cycle(u8 reg_val) +{ + u8 res = 0; + + reg_val &= FAN_DUTY_CYCLE_REG_MASK; + + if (!reg_val) + return 0; + + res = (u8)(reg_val); + + return res; +} + +static u8 duty_cycle_to_reg_val(u8 duty_cycle) +{ + if (duty_cycle >= FAN_DUTY_CYCLE_REG_MASK) + return FAN_DUTY_CYCLE_REG_MASK; + + return duty_cycle; +} +/*FAN speed: reg_val*150*/ +static u32 reg_val_to_speed_rpm(u8 reg_val) +{ + u64 f; + + if (reg_val == 0 || reg_val == 255) { + return 0; + } else { + f = VAL_TO_RPM_FACTOR*reg_val; + return (u32)f; + } +} +/*FAN direction:bit[1] 0-B2F 1-F2B*/ +static u8 reg_val_to_direction(u8 reg_val) +{ + u8 mask = (1 << 1); + + return ((reg_val & mask)>>1); +} +/*FAN color:bit[1][0] 11-off 10-amber 01-green*/ +static u8 reg_val_to_color(u8 reg_val, char **reg_color) +{ + u8 mask; + + mask = 0x3; + + switch (reg_val & mask) { + case FAN_LED_OFF: + return sprintf(*reg_color, "%s\n", "off"); + case FAN_LED_AMBER: + return sprintf(*reg_color, "%s\n", "amber"); + case FAN_LED_GREEN: + return sprintf(*reg_color, "%s\n", "green"); + default: + return sprintf(*reg_color, "invalid:%d\n", reg_val); + } +} +/*FAN presence:bit[0] 0-presence 1-absence*/ +static u8 reg_val_to_is_present(u8 reg_val) +{ + u8 mask = 0x01; + + return (reg_val & mask); + +} + +static u8 is_fan_fault(u8 reg_val) +{ + u8 ret = 1; + + /* Check if the speed of front or rear fan is ZERO, + */ + if (!reg_val_to_speed_rpm(reg_val)) + ret = 0; + + return ret; +} + +static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int error, value; + struct i2c_client *client = to_i2c_client(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + + error = kstrtoint(buf, 10, &value); + if (error) + return error; + + if (value < 0 || value > FAN_DUTY_CYCLE_REG_MASK) + return -EINVAL; + + if (attr->index > FAN7_DUTY_CYCLE_PERCENTAGE || attr->index < FAN1_DUTY_CYCLE_PERCENTAGE) + return -EINVAL; + + fan_write_value(client, ds4101_fan_reg[attr->index], duty_cycle_to_reg_val(value)); + + return count; +} + +static ssize_t set_led_ctrl(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int value; + struct i2c_client *client = to_i2c_client(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + + if (!buf) + return -EINVAL; + + if (sysfs_streq(buf, "amber")) + value = FAN_LED_AMBER; + else if (sysfs_streq(buf, "green")) + value = FAN_LED_GREEN; + else if (sysfs_streq(buf, "off")) + value = FAN_LED_OFF; + else + return -EINVAL; + + //fan_write_value(client, FAN_WDT_CTRL, 0); + fan_write_value(client, ds4101_fan_reg[attr->index], value); + + return count; +} + +static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ds4101_fan_data *data = fan_update_device(dev); + ssize_t ret = 0; + u8 duty_cycle = 0; + + if (data->valid) { + switch (attr->index) { + case FAN1_DUTY_CYCLE_PERCENTAGE: + case FAN2_DUTY_CYCLE_PERCENTAGE: + case FAN3_DUTY_CYCLE_PERCENTAGE: + case FAN4_DUTY_CYCLE_PERCENTAGE: + case FAN5_DUTY_CYCLE_PERCENTAGE: + case FAN6_DUTY_CYCLE_PERCENTAGE: + case FAN7_DUTY_CYCLE_PERCENTAGE: + duty_cycle = reg_val_to_duty_cycle(data->reg_val[attr->index]); + ret = sprintf(buf, "%u\n", duty_cycle); + break; + case FAN1_FRONT_SPEED_RPM: + case FAN1_REAR_SPEED_RPM: + case FAN2_FRONT_SPEED_RPM: + case FAN2_REAR_SPEED_RPM: + case FAN3_FRONT_SPEED_RPM: + case FAN3_REAR_SPEED_RPM: + case FAN4_FRONT_SPEED_RPM: + case FAN4_REAR_SPEED_RPM: + case FAN5_FRONT_SPEED_RPM: + case FAN5_REAR_SPEED_RPM: + case FAN6_FRONT_SPEED_RPM: + case FAN6_REAR_SPEED_RPM: + case FAN7_FRONT_SPEED_RPM: + case FAN7_REAR_SPEED_RPM: + ret = sprintf(buf, "%u\n", reg_val_to_speed_rpm(data->reg_val[attr->index])); + break; + /* 0: present 1: absent */ + case FAN1_PRESENT: + case FAN2_PRESENT: + case FAN3_PRESENT: + case FAN4_PRESENT: + case FAN5_PRESENT: + case FAN6_PRESENT: + case FAN7_PRESENT: + ret = sprintf(buf, "%d\n", + reg_val_to_is_present(data->reg_val[attr->index])); + break; + /*0: normal 1: fault */ + case FAN1_FAULT: + case FAN2_FAULT: + case FAN3_FAULT: + case FAN4_FAULT: + case FAN5_FAULT: + case FAN6_FAULT: + case FAN7_FAULT: + ret = sprintf(buf, "%d\n", is_fan_fault(data->reg_val[attr->index])); + break; + + /* 1: f2b 0: b2f*/ + case FAN1_DIRECTION: + case FAN2_DIRECTION: + case FAN3_DIRECTION: + case FAN4_DIRECTION: + case FAN5_DIRECTION: + case FAN6_DIRECTION: + case FAN7_DIRECTION: + ret = sprintf(buf, "%d\n", + reg_val_to_direction(data->reg_val[attr->index])); + break; + case FAN1_LED: + case FAN2_LED: + case FAN3_LED: + case FAN4_LED: + case FAN5_LED: + case FAN6_LED: + case FAN7_LED: + ret = reg_val_to_color(data->reg_val[attr->index], &buf); + break; + default: + break; + } + } + + return ret; +} + +static const struct attribute_group ds4101_fan_group = { + .attrs = ds4101_fan_attributes, +}; + +static struct ds4101_fan_data *fan_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds4101_fan_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || + !data->valid) { + int i; + + data->valid = 0; + + /* Update fan data + */ + for (i = 0; i < ARRAY_SIZE(data->reg_val); i++) { + int status = fan_read_value(client, ds4101_fan_reg[i]); + + if (status < 0) { + data->valid = 0; + mutex_unlock(&data->update_lock); + dev_info(&client->dev, "reg %d, err %d\n", ds4101_fan_reg[i], status); + return data; + } else + data->reg_val[i] = status; + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + return data; +} + +static int ds4101_fan_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct ds4101_fan_data *data; + int status; + + dev_err(&client->dev, "check i2c functionality\n"); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + status = -EIO; + goto exit; + } + + dev_err(&client->dev, "kzalloc the memory.\n"); + + data = kzalloc(sizeof(struct ds4101_fan_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + data->valid = 0; + mutex_init(&data->update_lock); + + dev_err(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &ds4101_fan_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + dev_err(&client->dev, "%s: fan '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &ds4101_fan_group); +exit_free: + kfree(data); +exit: + return status; +} + +static int ds4101_fan_remove(struct i2c_client *client) +{ + struct ds4101_fan_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &ds4101_fan_group); + + return 0; +} + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +static const struct i2c_device_id ds4101_fan_id[] = { + { "platform_fan", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ds4101_fan_id); + +static struct i2c_driver ds4101_fan_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = DRVNAME, + }, + .probe = ds4101_fan_probe, + .remove = ds4101_fan_remove, + .id_table = ds4101_fan_id, + .address_list = normal_i2c, +}; + +static int __init ds4101_fan_init(void) +{ + return i2c_add_driver(&ds4101_fan_driver); +} + +static void __exit ds4101_fan_exit(void) +{ + i2c_del_driver(&ds4101_fan_driver); +} + +module_init(ds4101_fan_init); +module_exit(ds4101_fan_exit); + +MODULE_AUTHOR("Nicholas Wu "); +MODULE_DESCRIPTION("ds4101 fan driver"); +MODULE_LICENSE("GPL"); + + diff --git a/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/platform-psu.c b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/platform-psu.c new file mode 100644 index 0000000000..1e9f238eb0 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/platform-psu.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for Delta TDPS-2000LB A + * + * (C) Copyright 2023, Celestica Inc. + * Author: Nicholas Wu + * + * 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 "pmbus.h" + +/* + * Form TDPS-2000 datasheet the supported sensors format defined as: + * all sensors: linear mode. + */ +static struct pmbus_driver_info tdps2000_info = { + .pages = 1, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT + | PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 + | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_STATUS_INPUT, +}; + +static int pmbus_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = -1; + + ret = pmbus_do_probe(client, id, &tdps2000_info); + + return ret; +} +/* user driver datat to pass the grpup */ +static const struct i2c_device_id tdps2000_id[] = { + {"tdps2000", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, tdps2000_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver tdps2000_driver = { + .driver = { + .name = "TDPS-2000LB A", + }, + .probe = pmbus_probe, + .remove = pmbus_do_remove, + .id_table = tdps2000_id, +}; + +module_i2c_driver(tdps2000_driver); + +MODULE_AUTHOR("Nicholas Wu "); +MODULE_DESCRIPTION("PMBus driver for Delta TDPS-2000LB A"); +MODULE_LICENSE("GPL"); diff --git a/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/watch_dog.c b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/watch_dog.c new file mode 100644 index 0000000000..7586730f9f --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/modules/builds/src/watch_dog.c @@ -0,0 +1,713 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*************************************************************************** + * Copyright (C) 2021 Celestica 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 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WDT_CONTROL_BASE 0xA100 +#define TEST_SCRATCH_REG 0xA101 +#define REBOOT_CAUSE_REG 0xA106 +#define WDT_SET_TIMER_H_BIT_REG 0xA181 +#define WDT_SET_TIMER_M_BIT_REG 0xA182 +#define WDT_SET_TIMER_L_BIT_REG 0xA183 +#define WDT_TIMER_H_BIT_REG 0xA184 +#define WDT_TIMER_M_BIT_REG 0xA185 +#define WDT_TIMER_L_BIT_REG 0xA186 +#define WDT_ENABLE_REG 0xA187 +#define WDT_FEED_REG 0xA188 +#define WDT_PUNCH_REG 0xA189 +#define WDT_START_FEED 0x01 +#define WDT_STOP_FEED 0x00 + + +#define POWER_ON_RESET 0x11 +#define SOFT_SET_WARM_RESET 0x22 +#define SOFT_SET_COLD_RESET 0x33 +#define CPU_WARM_RESET 0x44 +#define WDT_RESET 0x66 + + +#define MAX_TIMER_VALUE 0xffffff +#define DEFUALT_TIMER_VALUE 180000 /* 180s */ +#define WDT_ENABLE 0x01 +#define WDT_DISABLE 0x00 +#define WDT_RESTART 0x00 +#define DRV_NAME "cpld_wdt" +#define DRV_VERSION "1.0.0" +#define DEV_NAME "cpld_wdt" + +struct wdt_data { + unsigned long opened; + struct mutex lock; + char expect_close; + struct watchdog_info ident; + int timeout; + int timer_val; + char caused_reboot; /* last reboot was by the watchdog */ + struct resource *res; +}; + +struct cpld_wdt_private { + struct platform_device *pdev; + struct watchdog_device wddev; + struct cdev cdev; + struct miscdevice mdev; + bool suspended; + struct wdt_data wdat; +}; + +//struct class *cpld_wdt; +static const int max_timeout = MAX_TIMER_VALUE; + +static int timeout = DEFUALT_TIMER_VALUE; /* default 180s */ +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Start watchdog timer on module load with " + "given initial timeout(unit: ms). " + "Zero (default) disables this feature."); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0644); +MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close"); + +static unsigned int watchdog_get_timeleft(struct cpld_wdt_private *wdt) +{ + int time = 0; + + mutex_lock(&wdt->wdat.lock); + + time = inb(WDT_TIMER_H_BIT_REG); + time = time << 8 | inb(WDT_TIMER_M_BIT_REG); + time = time << 8 | inb(WDT_TIMER_L_BIT_REG); + time = time/1000; + mutex_unlock(&wdt->wdat.lock); + + return time; +} +static int watchdog_get_timeout(struct cpld_wdt_private *wdt) +{ + int timeout = 0; + + if (!wdt) + return -EINVAL; + + mutex_lock(&wdt->wdat.lock); + timeout = inb(WDT_SET_TIMER_H_BIT_REG); + timeout = timeout << 8 | inb(WDT_SET_TIMER_M_BIT_REG); + timeout = timeout << 8 | inb(WDT_SET_TIMER_L_BIT_REG); + timeout = timeout/1000; + mutex_unlock(&wdt->wdat.lock); + + return timeout; +} +static int watchdog_set_timeout(struct cpld_wdt_private *wdt, unsigned int timeout) +{ + if (!wdt) + return -EINVAL; + + if (timeout <= 0 || timeout > max_timeout) { + pr_err("watchdog timeout out of range\n"); + return -EINVAL; + } + + mutex_lock(&wdt->wdat.lock); + + wdt->wdat.timeout = timeout; + if (timeout > MAX_TIMER_VALUE) + wdt->wdat.timer_val = MAX_TIMER_VALUE; + else + wdt->wdat.timer_val = timeout; + /* Set timer value */ + //pr_crit("Watchdog Timeout:0x%06x\n", wdt->wdat.timer_val); + + outb((wdt->wdat.timer_val >> 16) & 0xff, WDT_SET_TIMER_H_BIT_REG); + outb((wdt->wdat.timer_val >> 8) & 0xff, WDT_SET_TIMER_M_BIT_REG); + outb(wdt->wdat.timer_val & 0xff, WDT_SET_TIMER_L_BIT_REG); + + mutex_unlock(&wdt->wdat.lock); + + return 0; +} + +static int watchdog_ping(struct cpld_wdt_private *wdt) +{ + if (!wdt) + return -EINVAL; + + mutex_lock(&wdt->wdat.lock); + + /* start feed watchdog */ + outb(WDT_START_FEED, WDT_FEED_REG); + /* stop feed watchdog */ + outb(WDT_STOP_FEED, WDT_FEED_REG); + + mutex_unlock(&wdt->wdat.lock); + + return 0; +} + +static void watchdog_keepalive(struct cpld_wdt_private *wdt) +{ + unsigned char val = 0; + + if (!wdt) + return; + + mutex_lock(&wdt->wdat.lock); + + val = inb(WDT_FEED_REG); + + val &= 0x1; + + val = ~val; + + val &= 0x1; + /* start feed watchdog */ + outb(val, WDT_FEED_REG); + + mutex_unlock(&wdt->wdat.lock); + + return; +} + +static int watchdog_start(struct cpld_wdt_private *wdt) +{ + if (!wdt) + return -EINVAL; + + /* Make sure we don't die as soon as the watchdog is enabled below */ + //watchdog_keepalive(); + mutex_lock(&wdt->wdat.lock); + outb(WDT_ENABLE, WDT_ENABLE_REG); + outb(WDT_RESTART, WDT_PUNCH_REG); + mutex_unlock(&wdt->wdat.lock); + + return 0; +} + +static int watchdog_stop(struct cpld_wdt_private *wdt) +{ + if (!wdt) + return -EINVAL; + + mutex_lock(&wdt->wdat.lock); + outb(WDT_DISABLE, WDT_ENABLE_REG); + mutex_unlock(&wdt->wdat.lock); + + return 0; +} + +static char watchdog_get_reason(struct cpld_wdt_private *p) +{ + char status = 0; + + if (!p) + return -1; + mutex_lock(&p->wdat.lock); + status = inb(REBOOT_CAUSE_REG); + mutex_unlock(&p->wdat.lock); + + return status; +} + +static bool watchdog_is_running(struct cpld_wdt_private *wdt) +{ + /* + * if we fail to determine the watchdog's status assume it to be + * running to be on the safe side + */ + bool is_running = true; + + mutex_lock(&wdt->wdat.lock); + is_running = inb(WDT_ENABLE_REG); + mutex_unlock(&wdt->wdat.lock); + + return is_running; +} + +static const struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, + .firmware_version = 0, + .identity = DRV_NAME, +}; + +static ssize_t identity_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct cpld_wdt_private *wdt = dev_get_drvdata(dev); + + if (!wdt) + return -EINVAL; + + return sprintf(buf, "%s\n", wdt->wdat.ident.identity); +} + +static DEVICE_ATTR_RO(identity); + + +static ssize_t state_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct cpld_wdt_private *wdt = dev_get_drvdata(dev); + bool state = watchdog_is_running(wdt); + + if (true == state) + return sprintf(buf, "active\n"); + else + return sprintf(buf, "inactive\n"); +} + +static DEVICE_ATTR_RO(state); + +static ssize_t status_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + unsigned int status = 0; + struct cpld_wdt_private *wdt = dev_get_drvdata(dev); + + if (!wdt) + return -EINVAL; + + return sprintf(buf, "0x%x\n", status); +} + +static DEVICE_ATTR_RO(status); + +static ssize_t reason_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char bootstatus; + struct cpld_wdt_private *wdt = dev_get_drvdata(dev); + + if (!wdt) + return -EINVAL; + bootstatus = watchdog_get_reason(wdt); + + return sprintf(buf, "0x%02x\n", bootstatus); +} + +static DEVICE_ATTR_RO(reason); + +static ssize_t timeleft_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + unsigned int timeleft; + struct cpld_wdt_private *wdt = dev_get_drvdata(dev); + + if (!wdt) + return -EINVAL; + + timeleft = watchdog_get_timeleft(wdt); + + return sprintf(buf, "%u\n", timeleft); + +} + +static DEVICE_ATTR_RO(timeleft); + + +static ssize_t timeout_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + unsigned int timeout; + struct cpld_wdt_private *wdt = dev_get_drvdata(dev); + + if (!wdt) + return -EINVAL; + + timeout = watchdog_get_timeout(wdt); + + return sprintf(buf, "%u\n", timeout); +} +static DEVICE_ATTR_RO(timeout); + + +static struct attribute *wdt_attrs[] = { + &dev_attr_state.attr, + &dev_attr_identity.attr, + &dev_attr_status.attr, + &dev_attr_reason.attr, + &dev_attr_timeleft.attr, + &dev_attr_timeout.attr, + NULL, +}; + +static const struct attribute_group wdt_group = { + .attrs = wdt_attrs, +}; + +static int watchdog_open(struct inode *inode, struct file *file) +{ + struct cpld_wdt_private *wdt; + + wdt = container_of(file->private_data, struct cpld_wdt_private, mdev); + + /* If the watchdog is alive we don't need to start it again */ + + if (test_and_set_bit(0, &wdt->wdat.opened)) + return -EBUSY; + + //watchdog_start(wdt); + + if (nowayout) + __module_get(THIS_MODULE); + + wdt->wdat.expect_close = 0; + + return nonseekable_open(inode, file); +} + +static int watchdog_release(struct inode *inode, struct file *file) +{ + struct cpld_wdt_private *p; + + p = container_of(file->private_data, struct cpld_wdt_private, mdev); + + if (!p) + return -EINVAL; + + clear_bit(0, &p->wdat.opened); + + if (p->wdat.expect_close && !nowayout) { + watchdog_stop(p); + } + + return 0; +} + +/* + * watchdog_write: + * @file: file handle to the watchdog + * @buf: buffer to write + * @count: count of bytes + * @ppos: pointer to the position to write. No seeks allowed + * + * A write to a watchdog device is defined as a keepalive signal. Any + * write of data will do, as we we don't define content meaning. + */ + +static ssize_t watchdog_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct cpld_wdt_private *p; + + p = container_of(file->private_data, struct cpld_wdt_private, mdev); + + if (!p) + return -EINVAL; + + + if (count) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + bool expect_close = false; + + for (i = 0; i != count; i++) { + char c; + + if (get_user(c, buf + i)) + return -EFAULT; + expect_close = (c == 'V'); + } + + /* Properly order writes across fork()ed processes */ + mutex_lock(&p->wdat.lock); + p->wdat.expect_close = expect_close; + mutex_unlock(&p->wdat.lock); + } + + /* someone wrote to us, we should restart timer */ + watchdog_keepalive(p); + } + + return count; +} + +/* + *watchdog_ioctl: + *@inode: inode of the device + *@file: file handle to the device + *@cmd: watchdog command + *@arg: argument pointer + * + *The watchdog API defines a common set of functions for all watchdogs + *according to their available features. + */ +static long watchdog_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int status; + int new_options; + int new_timeout; + unsigned int val; + struct cpld_wdt_private *p; + union { + struct watchdog_info __user *ident; + int __user *i; + } uarg; + + uarg.i = (int __user *)arg; + + p = container_of(file->private_data, struct cpld_wdt_private, mdev); + if (!p) + return -EINVAL; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user(uarg.ident, &p->wdat.ident, + sizeof(p->wdat.ident)) ? -EFAULT : 0; + case WDIOC_GETSTATUS: + status = watchdog_is_running(p); + return put_user(status, uarg.i); + case WDIOC_GETBOOTSTATUS: + //status = watchdog_get_bootstatus(p); + return put_user(status, uarg.i); + case WDIOC_SETOPTIONS: + if (get_user(new_options, uarg.i)) + return -EFAULT; + + if (new_options & WDIOS_DISABLECARD) + return watchdog_stop(p); + + if (new_options & WDIOS_ENABLECARD) + return watchdog_start(p); + + return 0; + case WDIOC_KEEPALIVE: + watchdog_keepalive(p); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_timeout, uarg.i)) + return -EFAULT; + new_timeout = new_timeout*1000; + if (watchdog_set_timeout(p, new_timeout)) + return -EINVAL; + val = watchdog_get_timeout(p); + return put_user(val, uarg.i); + case WDIOC_GETTIMEOUT: + val = watchdog_get_timeout(p); + return put_user(val, uarg.i); + case WDIOC_GETTIMELEFT: + val = watchdog_get_timeleft(p); + return put_user(val, uarg.i); + default: + return -ENOTTY; + + } +} + +static int watchdog_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + //watchdog_stop(p); + pr_info("Do nothing for SYS_DOWN or SYS_HALT\n"); + + pr_err("CPLD Watchdog did not Stop!\n"); + + return NOTIFY_DONE; +} + +static const struct file_operations watchdog_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = watchdog_open, + .release = watchdog_release, + .write = watchdog_write, + .unlocked_ioctl = watchdog_ioctl, +}; + +static struct miscdevice watchdog_miscdev = { + //.minor = WATCHDOG_MINOR, + .name = DEV_NAME, + .fops = &watchdog_fops, +}; + +static struct notifier_block watchdog_notifier = { + .notifier_call = watchdog_notify_sys, +}; + +static int cpld_wdt_probe(struct platform_device *pdev) +{ + int wdt_reboot_cause, err = 0; + unsigned char ver = 0; + struct device *dev = &pdev->dev; + + struct cpld_wdt_private *p; + + p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + mutex_init(&(p->wdat.lock)); + p->wdat.ident.options = WDIOC_SETTIMEOUT + | WDIOF_MAGICCLOSE + | WDIOF_KEEPALIVEPING + | WDIOC_GETTIMELEFT; + + snprintf(p->wdat.ident.identity, + sizeof(p->wdat.ident.identity), "%s", DRV_NAME); + + wdt_reboot_cause = inb(REBOOT_CAUSE_REG); // REBOOT_CAUSE + p->wdat.caused_reboot = wdt_reboot_cause; + ver = inb(WDT_CONTROL_BASE); + pr_info("Watchdog CPLD Version:0x%02x\n", + ver); + + if (timeout) { + if (timeout <= 0 || timeout > max_timeout) { + pr_err("starting timeout out of range\n"); + err = -EINVAL; + return err; + } + + //watchdog_start(p); + if (timeout > MAX_TIMER_VALUE) + watchdog_set_timeout(p, MAX_TIMER_VALUE); + else + watchdog_set_timeout(p, timeout); + + if (nowayout) + __module_get(THIS_MODULE); + + pr_info("watchdog started with initial timeout of %u Second(s)\n", timeout/1000); + } + + err = watchdog_set_timeout(p, timeout); + if (err) + return err; + + err = register_reboot_notifier(&watchdog_notifier); + if (err) + return err; + p->mdev = watchdog_miscdev; + err = misc_register(&p->mdev); + if (err) { + pr_err("cannot register miscdev on minor=%d\n", watchdog_miscdev.minor); + return err; + } + + err = sysfs_create_group(&pdev->dev.kobj, &wdt_group); + if (err) { + printk(KERN_ERR "Cannot create sysfs for cpld_wdt.\n"); + return err; + } + + platform_set_drvdata(pdev, p); + dev_set_drvdata(dev, p); + + pr_info("initialized. sec (nowayout=%d)\n", + nowayout); + + return 0; +} + +static int cpld_wdt_remove(struct platform_device *pdev) +{ + struct cpld_wdt_private *p = platform_get_drvdata(pdev); + + if (!p) + return 0; + + sysfs_remove_group(&pdev->dev.kobj, &wdt_group); + misc_deregister(&p->mdev); + unregister_reboot_notifier(&watchdog_notifier); + + return 0; + +} + +static struct platform_driver cpld_wdt_driver = { + .probe = cpld_wdt_probe, + .remove = cpld_wdt_remove, + .driver = { + .name = DRV_NAME, + }, +}; + +static struct resource cpld_wdt_resources[] = { + { + .start = 0xA100, + .end = 0xA1F2, + .flags = IORESOURCE_IO, + }, +}; + +static void wdt_dev_release(struct device *dev) +{ + return; +} + +static struct platform_device cpld_wdt_dev = { + .name = DRV_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(cpld_wdt_resources), + .resource = cpld_wdt_resources, + .dev = { + .release = wdt_dev_release, + } +}; + +static int __init cpld_wdt_init_module(void) +{ + int err = 0; + + err = platform_device_register(&cpld_wdt_dev); + err += platform_driver_register(&cpld_wdt_driver); + if (err < 0) + pr_info("Platform Device/Driver Register Failed. err:%d\n", err); + + pr_info("CPLD WatchDog Timer Driver v%s\n", DRV_VERSION); + + return err; +} + +static void __exit cpld_wdt_cleanup_module(void) +{ + platform_driver_unregister(&cpld_wdt_driver); + platform_device_unregister(&cpld_wdt_dev); + pr_info("Watchdog Module Unloaded\n"); +} + +module_init(cpld_wdt_init_module); +module_exit(cpld_wdt_cleanup_module); + + +MODULE_DESCRIPTION("Cpld Watchdog Driver"); +MODULE_VERSION(DRV_VERSION); +MODULE_AUTHOR("Nicholas "); +MODULE_LICENSE("GPL"); diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/Makefile b/packages/platforms/celestica/x86-64/ds4101/onlp/Makefile new file mode 100644 index 0000000000..003238cf6d --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk \ No newline at end of file diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/PKG.yml b/packages/platforms/celestica/x86-64/ds4101/onlp/PKG.yml new file mode 100644 index 0000000000..8c96fd2231 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/PKG.yml @@ -0,0 +1,2 @@ +!include $ONL/packages/platforms/celestica/x86-64/ds4101/onlp/onlp-platform-revision.yml REVISION=r0 PLATFORM=x86-64-cls-ds4101 ARCH=amd64 TOOLCHAIN=x86_64-linux-gnu + diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/Makefile b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/Makefile new file mode 100644 index 0000000000..5d5b5169ae --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/Makefile @@ -0,0 +1,2 @@ +FILTER=src +include $(ONL)/make/subdirs.mk diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/lib/Makefile b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/lib/Makefile new file mode 100644 index 0000000000..3044bb00b7 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/lib/Makefile @@ -0,0 +1,2 @@ +PLATFORM := x86-64-cls-ds4101 +include $(ONL)/packages/base/any/onlp/builds/platform/libonlp-platform.mk diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/onlpdump/Makefile b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/onlpdump/Makefile new file mode 100644 index 0000000000..ab7880d885 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/onlpdump/Makefile @@ -0,0 +1,2 @@ +PLATFORM := x86-64-cls-ds4101 +include $(ONL)/packages/base/any/onlp/builds/platform/onlps.mk diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/.module b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/.module new file mode 100644 index 0000000000..1e2d5192cf --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/.module @@ -0,0 +1 @@ +name: x86_64_cls_ds4101 diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/Makefile b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/Makefile new file mode 100644 index 0000000000..b539675edb --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/Makefile @@ -0,0 +1,9 @@ +############################################################################### +# +# +# +############################################################################### +include $(ONL)/make/config.mk +MODULE := x86_64_cls_ds4101 +AUTOMODULE := x86_64_cls_ds4101 +include $(BUILDER)/definemodule.mk diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/auto/make.mk b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/auto/make.mk new file mode 100644 index 0000000000..9d369bf090 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/auto/make.mk @@ -0,0 +1,9 @@ +############################################################################### +# +# x86_64_cls_ds4101 Autogeneration +# +############################################################################### +x86_64_cls_ds4101_AUTO_DEFS := module/auto/x86_64_cls_ds4101.yml +x86_64_cls_ds4101_AUTO_DIRS := module/inc/x86_64_cls_ds4101 module/src +include $(BUILDER)/auto.mk + diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/auto/x86_64_cls_ds4101.yml b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/auto/x86_64_cls_ds4101.yml new file mode 100644 index 0000000000..08660132de --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/auto/x86_64_cls_ds4101.yml @@ -0,0 +1,47 @@ +############################################################################### +# +# x86_64_cls_ds4101 Autogeneration Definitions. +# +############################################################################### + +cdefs: &cdefs +- X86_64_CLS_DS4101_CONFIG_INCLUDE_LOGGING: + doc: "Include or exclude logging." + default: 1 +- X86_64_CLS_DS4101_CONFIG_LOG_OPTIONS_DEFAULT: + doc: "Default enabled log options." + default: AIM_LOG_OPTIONS_DEFAULT +- X86_64_CLS_DS4101_CONFIG_LOG_BITS_DEFAULT: + doc: "Default enabled log bits." + default: AIM_LOG_BITS_DEFAULT +- X86_64_CLS_DS4101_CONFIG_LOG_CUSTOM_BITS_DEFAULT: + doc: "Default enabled custom log bits." + default: 0 +- X86_64_CLS_DS4101_CONFIG_PORTING_STDLIB: + doc: "Default all porting macros to use the C standard libraries." + default: 1 +- X86_64_CLS_DS4101_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS: + doc: "Include standard library headers for stdlib porting macros." + default: X86_64_CLS_DS4101_CONFIG_PORTING_STDLIB +- X86_64_CLS_DS4101_CONFIG_INCLUDE_UCLI: + doc: "Include generic uCli support." + default: 0 + + +definitions: + cdefs: + X86_64_CLS_DS4101_CONFIG_HEADER: + defs: *cdefs + basename: x86_64_cls_ds4101_config + + portingmacro: + X86_64_CLS_DS4101: + macros: + - malloc + - free + - memset + - memcpy + - strncpy + - vsnprintf + - snprintf + - strlen \ No newline at end of file diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/inc/x86_64_cls_ds4101/x86_64_cls_ds4101.x b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/inc/x86_64_cls_ds4101/x86_64_cls_ds4101.x new file mode 100644 index 0000000000..dbfe7c5709 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/inc/x86_64_cls_ds4101/x86_64_cls_ds4101.x @@ -0,0 +1,14 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +/* <--auto.start.xmacro(ALL).define> */ +/* */ + +/* <--auto.start.xenum(ALL).define> */ +/* */ + + diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/inc/x86_64_cls_ds4101/x86_64_cls_ds4101_config.h b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/inc/x86_64_cls_ds4101/x86_64_cls_ds4101_config.h new file mode 100644 index 0000000000..e14b7019fa --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/inc/x86_64_cls_ds4101/x86_64_cls_ds4101_config.h @@ -0,0 +1,127 @@ +/**************************************************************************//** + * + * @file + * @brief x86_64_cls_ds4101 Configuration Header + * + * @addtogroup x86_64_cls_ds4101-config + * @{ + * + *****************************************************************************/ +#ifndef __X86_64_CLS_DS4101_CONFIG_H__ +#define __X86_64_CLS_DS4101_CONFIG_H__ + +#ifdef GLOBAL_INCLUDE_CUSTOM_CONFIG +#include +#endif +#ifdef X86_64_CLS_DS4101_INCLUDE_CUSTOM_CONFIG +#include +#endif + +/* */ +#include +/** + * X86_64_CLS_DS4101_CONFIG_INCLUDE_LOGGING + * + * Include or exclude logging. */ + + +#ifndef X86_64_CLS_DS4101_CONFIG_INCLUDE_LOGGING +#define X86_64_CLS_DS4101_CONFIG_INCLUDE_LOGGING 1 +#endif + +/** + * X86_64_CLS_DS4101_CONFIG_LOG_OPTIONS_DEFAULT + * + * Default enabled log options. */ + + +#ifndef X86_64_CLS_DS4101_CONFIG_LOG_OPTIONS_DEFAULT +#define X86_64_CLS_DS4101_CONFIG_LOG_OPTIONS_DEFAULT AIM_LOG_OPTIONS_DEFAULT +#endif + +/** + * X86_64_CLS_DS4101_CONFIG_LOG_BITS_DEFAULT + * + * Default enabled log bits. */ + + +#ifndef X86_64_CLS_DS4101_CONFIG_LOG_BITS_DEFAULT +#define X86_64_CLS_DS4101_CONFIG_LOG_BITS_DEFAULT AIM_LOG_BITS_DEFAULT +#endif + +/** + * X86_64_CLS_DS4101_CONFIG_LOG_CUSTOM_BITS_DEFAULT + * + * Default enabled custom log bits. */ + + +#ifndef X86_64_CLS_DS4101_CONFIG_LOG_CUSTOM_BITS_DEFAULT +#define X86_64_CLS_DS4101_CONFIG_LOG_CUSTOM_BITS_DEFAULT 0 +#endif + +/** + * X86_64_CLS_DS4101_CONFIG_PORTING_STDLIB + * + * Default all porting macros to use the C standard libraries. */ + + +#ifndef X86_64_CLS_DS4101_CONFIG_PORTING_STDLIB +#define X86_64_CLS_DS4101_CONFIG_PORTING_STDLIB 1 +#endif + +/** + * X86_64_CLS_DS4101_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS + * + * Include standard library headers for stdlib porting macros. */ + + +#ifndef X86_64_CLS_DS4101_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS +#define X86_64_CLS_DS4101_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS X86_64_CLS_DS4101_CONFIG_PORTING_STDLIB +#endif + +/** + * X86_64_CLS_DS4101_CONFIG_INCLUDE_UCLI + * + * Include generic uCli support. */ + + +#ifndef X86_64_CLS_DS4101_CONFIG_INCLUDE_UCLI +#define X86_64_CLS_DS4101_CONFIG_INCLUDE_UCLI 0 +#endif + + + +/** + * All compile time options can be queried or displayed + */ + +/** Configuration settings structure. */ +typedef struct x86_64_cls_ds4101_config_settings_s { + /** name */ + const char* name; + /** value */ + const char* value; +} x86_64_cls_ds4101_config_settings_t; + +/** Configuration settings table. */ +/** x86_64_cls_ds4101_config_settings table. */ +extern x86_64_cls_ds4101_config_settings_t x86_64_cls_ds4101_config_settings[]; + +/** + * @brief Lookup a configuration setting. + * @param setting The name of the configuration option to lookup. + */ +const char* x86_64_cls_ds4101_config_lookup(const char* setting); + +/** + * @brief Show the compile-time configuration. + * @param pvs The output stream. + */ +int x86_64_cls_ds4101_config_show(struct aim_pvs_s* pvs); + +/* */ + +#include "x86_64_cls_ds4101_porting.h" + +#endif /* __X86_64_CLS_DS4101_CONFIG_H__ */ +/* @} */ diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/inc/x86_64_cls_ds4101/x86_64_cls_ds4101_dox.h b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/inc/x86_64_cls_ds4101/x86_64_cls_ds4101_dox.h new file mode 100644 index 0000000000..2888d21add --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/inc/x86_64_cls_ds4101/x86_64_cls_ds4101_dox.h @@ -0,0 +1,26 @@ +/**************************************************************************//** + * + * x86_64_cls_ds4101 Doxygen Header + * + *****************************************************************************/ +#ifndef __X86_64_CLS_DS4101_DOX_H__ +#define __X86_64_CLS_DS4101_DOX_H__ + +/** + * @defgroup x86_64_cls_ds4101 x86_64_cls_ds4101 - x86_64_cls_ds4101 Description + * + +The documentation overview for this module should go here. + + * + * @{ + * + * @defgroup x86_64_cls_ds4101-x86_64_cls_ds4101 Public Interface + * @defgroup x86_64_cls_ds4101-config Compile Time Configuration + * @defgroup x86_64_cls_ds4101-porting Porting Macros + * + * @} + * + */ + +#endif /* __X86_64_CLS_DS4101_DOX_H__ */ diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/inc/x86_64_cls_ds4101/x86_64_cls_ds4101_porting.h b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/inc/x86_64_cls_ds4101/x86_64_cls_ds4101_porting.h new file mode 100644 index 0000000000..aa64c012bf --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/inc/x86_64_cls_ds4101/x86_64_cls_ds4101_porting.h @@ -0,0 +1,107 @@ +/**************************************************************************//** + * + * @file + * @brief x86_64_cls_ds4101 Porting Macros. + * + * @addtogroup x86_64_cls_ds4101-porting + * @{ + * + *****************************************************************************/ +#ifndef __X86_64_CLS_DS4101_PORTING_H__ +#define __X86_64_CLS_DS4101_PORTING_H__ + + +/* */ +#if X86_64_CLS_DS4101_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS == 1 +#include +#include +#include +#include +#include +#endif + +#ifndef X86_64_CLS_DS4101_MALLOC + #if defined(GLOBAL_MALLOC) + #define X86_64_CLS_DS4101_MALLOC GLOBAL_MALLOC + #elif X86_64_CLS_DS4101_CONFIG_PORTING_STDLIB == 1 + #define X86_64_CLS_DS4101_MALLOC malloc + #else + #error The macro X86_64_CLS_DS4101_MALLOC is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_CLS_DS4101_FREE + #if defined(GLOBAL_FREE) + #define X86_64_CLS_DS4101_FREE GLOBAL_FREE + #elif X86_64_CLS_DS4101_CONFIG_PORTING_STDLIB == 1 + #define X86_64_CLS_DS4101_FREE free + #else + #error The macro X86_64_CLS_DS4101_FREE is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_CLS_DS4101_MEMSET + #if defined(GLOBAL_MEMSET) + #define X86_64_CLS_DS4101_MEMSET GLOBAL_MEMSET + #elif X86_64_CLS_DS4101_CONFIG_PORTING_STDLIB == 1 + #define X86_64_CLS_DS4101_MEMSET memset + #else + #error The macro X86_64_CLS_DS4101_MEMSET is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_CLS_DS4101_MEMCPY + #if defined(GLOBAL_MEMCPY) + #define X86_64_CLS_DS4101_MEMCPY GLOBAL_MEMCPY + #elif X86_64_CLS_DS4101_CONFIG_PORTING_STDLIB == 1 + #define X86_64_CLS_DS4101_MEMCPY memcpy + #else + #error The macro X86_64_CLS_DS4101_MEMCPY is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_CLS_DS4101_STRNCPY + #if defined(GLOBAL_STRNCPY) + #define X86_64_CLS_DS4101_STRNCPY GLOBAL_STRNCPY + #elif X86_64_CLS_DS4101_CONFIG_PORTING_STDLIB == 1 + #define X86_64_CLS_DS4101_STRNCPY strncpy + #else + #error The macro X86_64_CLS_DS4101_STRNCPY is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_CLS_DS4101_VSNPRINTF + #if defined(GLOBAL_VSNPRINTF) + #define X86_64_CLS_DS4101_VSNPRINTF GLOBAL_VSNPRINTF + #elif X86_64_CLS_DS4101_CONFIG_PORTING_STDLIB == 1 + #define X86_64_CLS_DS4101_VSNPRINTF vsnprintf + #else + #error The macro X86_64_CLS_DS4101_VSNPRINTF is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_CLS_DS4101_SNPRINTF + #if defined(GLOBAL_SNPRINTF) + #define X86_64_CLS_DS4101_SNPRINTF GLOBAL_SNPRINTF + #elif X86_64_CLS_DS4101_CONFIG_PORTING_STDLIB == 1 + #define X86_64_CLS_DS4101_SNPRINTF snprintf + #else + #error The macro X86_64_CLS_DS4101_SNPRINTF is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_CLS_DS4101_STRLEN + #if defined(GLOBAL_STRLEN) + #define X86_64_CLS_DS4101_STRLEN GLOBAL_STRLEN + #elif X86_64_CLS_DS4101_CONFIG_PORTING_STDLIB == 1 + #define X86_64_CLS_DS4101_STRLEN strlen + #else + #error The macro X86_64_CLS_DS4101_STRLEN is required but cannot be defined. + #endif +#endif + +/* */ + + +#endif /* __X86_64_CLS_DS4101_PORTING_H__ */ +/* @} */ diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/make.mk b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/make.mk new file mode 100644 index 0000000000..dea7d95770 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/make.mk @@ -0,0 +1,10 @@ +############################################################################### +# +# +# +############################################################################### +THIS_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +x86_64_cls_ds4101_INCLUDES := -I $(THIS_DIR)inc +x86_64_cls_ds4101_INTERNAL_INCLUDES := -I $(THIS_DIR)src +x86_64_cls_ds4101_DEPENDMODULE_ENTRIES := init:x86_64_cls_ds4101 ucli:x86_64_cls_ds4101 + diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/Makefile b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/Makefile new file mode 100644 index 0000000000..2a0f0d2eee --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/Makefile @@ -0,0 +1,9 @@ +############################################################################### +# +# Local source generation targets. +# +############################################################################### + +ucli: + @../../../../tools/uclihandlers.py x86_64_cls_ds4101_ucli.c + diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/fancontrol.c b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/fancontrol.c new file mode 100644 index 0000000000..e0fbe4adae --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/fancontrol.c @@ -0,0 +1,447 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "fancontrol.h" +#include "platform_wobmc.h" + +extern uint8_t BMC_Status; +extern struct fan Fan[FAN_COUNT + 1]; +extern struct temp Temp_B2F[THERMAL_COUNT + 1]; +extern struct temp Temp_F2B[THERMAL_COUNT + 1]; +extern uint8_t Sys_Airflow; + +linear_t U15LINEAR_F2B = { + 39, 52, 128, 255, 3, UNDEF, 0, 0, 0 +}; + +linear_t U16LINEAR_F2B = { + 39, 52, 128, 255, 3, UNDEF, 0, 0, 0 +}; + +/* temperature unit is mC, so setpoint multiple 1000 to be the same level */ +linear_t CPULINEAR_F2B = { + 67, 85, 66, 255, 3, UNDEF, 0, 0, 0 +}; + +pid_algo_t CPU_PID = { + UNDEF, 0, 96, 2.5, 0.15, 0.2, 0 +}; + +pid_algo_t SW_PID = { + UNDEF, 0, 94, 3, 0.2, 0.2, 0 +}; + + +static short get_temp(const char *path) +{ + short temp = 0; + long t = 0; + int ret = 0; + + ret = get_sysnode_value(path, (void *)&t); + if (ret) + { + return ERR; + } + temp = t / TEMP_CONV_FACTOR; + + return temp; +} + +static int set_fan(const char *path, uint8_t pwm) +{ + int ret = 0; + + ret = set_sysnode_value(path, pwm); + + return ret; +} + +static int set_all_fans(uint8_t pwm) +{ + int ret = 0, i = 0; + + for (i = 1; i <= CHASSIS_FAN_COUNT; i++) { + ret = set_fan(Fan[i].pwm_path, pwm); + if (ret) + { + printf("Can't set fan%d pwm\n", i); + return ERR; + } + } + + return 0; +} + + +static short get_valid_temp(char *path, uint8_t sec) +{ + uint8_t i = 0, cnt = 0; + int temp_sum = 0; + short temp = 0; + static short last_temp = 0; + + for (i = 0; i < 5; i++) { + temp = get_temp(path); + if (temp != -1) { + if (abs(last_temp - temp) > TEMP_DIFF_FAULT) { + temp_sum += temp; + cnt++; + } + else { + break; + } + } + sleep(sec); + } + + /* can not get temperture or reach maximum temperture for 5 times */ + if (0 == cnt && 5 == i) { + return ERR; + /* get valid temperture */ + } else { + if (5 == i) { // every temperture is larger than the maximum difference + temp = temp_sum / cnt; + } + last_temp = temp; + } + + return temp; +} + +static short upper_bound(char temp, uint8_t min_temp, uint8_t max_temp, + uint8_t min_pwm, uint8_t max_pwm, + uint8_t hysteresis, char temp_offset) +{ + short pwm = 0; + + pwm = (temp - min_temp + temp_offset) * (max_pwm - min_pwm) / + (max_temp - min_temp - hysteresis) + min_pwm; + DEBUG_PRINT("[linear] temp %d, min_temp %d, max_temp %d, min_pwm %d, " + "max_pwm %d, hysteresis %d, pwm %d, temp_offset %d\n", + temp, min_temp, max_temp, min_pwm, max_pwm, hysteresis, pwm, temp_offset); + + return pwm > 0 ? pwm : 0; +} + +static short lower_bound(char temp, uint8_t min_temp, uint8_t max_temp, + uint8_t min_pwm, uint8_t max_pwm, + uint8_t hysteresis, char temp_offset) +{ + short pwm = 0; + + pwm = (temp - min_temp + temp_offset - hysteresis) * (max_pwm - min_pwm) / + (max_temp - min_temp - hysteresis) + min_pwm; + DEBUG_PRINT("[linear] temp %d, min_temp %d, max_temp %d, min_pwm %d, " + "max_pwm %d, hysteresis %d, pwm %d, temp_offset %d\n", + temp, min_temp, max_temp, min_pwm, max_pwm, hysteresis, pwm, temp_offset); + + return pwm > 0 ? pwm : 0; +} + +static uint8_t linear_cal(uint8_t temp, linear_t *l, uint8_t altitude_effect) +{ + uint8_t pwm = 0; + short ub = 0, lb = 0; + int pressure_raw = 0; + float pressure_scale = 0.0; + double pressure = 0.0; + int altitude = 0; + int ret = 0; + char tmp[15]; + + if (altitude_effect) { + ret = read_device_node_string(PRESSURE_RAW_PATH, tmp, sizeof(tmp), 0); + if (ret) { + return ERR; + } + pressure_raw = atoi(tmp); + ret = read_device_node_string(PRESSURE_SCALE_PATH, tmp, sizeof(tmp), 0); + if (ret) { + return ERR; + } + pressure_scale = atof(tmp); + pressure = pressure_raw * pressure_scale * 1000; + DEBUG_PRINT("[linear] Pressure: %lf Pa\n", pressure); + // when height is less than default height 900 M(90970 Pa), set temp_offset as 0 + if (pressure < 90970 || DEBUG_MODE) { + altitude = 44330.77 * (1 - pow(pressure/101326, 0.1902632)) + OFF_H; + l->temp_offset = (altitude - 950) / 175; + DEBUG_PRINT("[linear] Altitude: %d M, temp offset: %d\n", altitude, l->temp_offset); + } else + l->temp_offset = 0; + } + + ub = upper_bound(temp, l->min_temp, l->max_temp, + l->min_pwm, l->max_pwm, + l->hysteresis, l->temp_offset); + lb = lower_bound(temp, l->min_temp, l->max_temp, + l->min_pwm, l->max_pwm, + l->hysteresis, l->temp_offset); + + if (UNDEF == l->lasttemp) { + l->lasttemp = temp; + l->lastpwm = lb; + DEBUG_PRINT("[linear] start lb pwm %d, preset initial pwm %d\n", lb, PWM_START); + return lb > PWM_START ? lb : PWM_START; + } + if (temp >= l->max_temp - l->temp_offset) { + l->lasttemp = temp; + l->lastpwm = PWM_MAX; + DEBUG_PRINT("[linear] set max pwm %d\n", PWM_MAX); + return PWM_MAX; + } else if (temp < l->min_temp - l->temp_offset) { + l->lasttemp = temp; + l->lastpwm = PWM_MIN; + DEBUG_PRINT("[linear] set min pwm %d\n", PWM_MIN); + return PWM_MIN; + } + + DEBUG_PRINT("[linear] temp %d, lasttemp %d\n", temp, l->lasttemp); + if (temp > l->lasttemp) + { + DEBUG_PRINT("[linear] temperature rises from %d to %d\n", l->lasttemp, temp); + if (lb < l->lastpwm) + { + pwm = l->lastpwm; + } + else + { + pwm = lb; + } + } + else if (temp < l->lasttemp) + { + DEBUG_PRINT("[linear] temperature declines from %d to %d\n", l->lasttemp, temp); + if (ub > l->lastpwm) + { + pwm = l->lastpwm; + } + else + { + pwm = ub; + } + } + else + { + DEBUG_PRINT("[linear] temperature keeps to %d\n", temp); + pwm = l->lastpwm; + } + if (pwm > l->max_pwm) + { + pwm = l->max_pwm; + } + else if (pwm < l->min_pwm) + { + pwm = l->min_pwm; + } + DEBUG_PRINT("[linear] last pwm: %d, new pwm: %d\n", l->lastpwm, pwm); + l->lasttemp = temp; + l->lastpwm = pwm; + + return pwm; +} + +static uint8_t pid_cal(uint8_t temp, pid_algo_t *pid) +{ + int pweight = 0; + int iweight = 0; + int dweight = 0; + uint8_t pwmpercent = 0; + uint8_t pwm = 0; + + if (UNDEF == pid->lasttemp) { + pid->lasttemp = temp; + pid->lastlasttemp = temp; + pid->lastpwmpercent = PWM_MIN * 100 / PWM_MAX; + return PWM_MIN; + } + + pweight = temp - pid->lasttemp; + iweight = temp - pid->setpoint; + dweight = temp - 2 * (pid->lasttemp) + pid->lastlasttemp; + DEBUG_PRINT("[pid] temp %d, lasttemp %d, lastlasttemp %d\n", + temp, pid->lasttemp, pid->lastlasttemp); + DEBUG_PRINT("[pid] setpoint %d, P %f, I %f, D %f, pweight %d, iweight %d, dweight %d\n", + pid->setpoint, pid->P, pid->I, pid->D, pweight, iweight, dweight); + + /* Add 0.5 pwm to support rounding */ + pwmpercent = (int)(pid->lastpwmpercent + + pid->P * pweight + pid->I * iweight + pid->D * dweight + 0.5); + DEBUG_PRINT("[pid] percent before cal %d\n", pwmpercent); + pwmpercent = pwmpercent < (PWM_MIN * 100 / PWM_MAX) ? (PWM_MIN * 100 / PWM_MAX) : pwmpercent; + pwmpercent = pwmpercent > 100 ? 100 : pwmpercent; + DEBUG_PRINT("[pid] percent after cal %d\n", pwmpercent); + pwm = pwmpercent * PWM_MAX / 100; + pid->lastlasttemp = pid->lasttemp; + pid->lasttemp = temp; + pid->lastpwmpercent = pwmpercent; + DEBUG_PRINT("[pid] set pwm %d\n", pwm); + + return pwm; +} + +int update_fan(void) +{ + int ret = 0, i = 0; + uint8_t pwm = 0, fan_full_speed_fault_num = 0; + static uint8_t fan_status = 0; + struct temp *temp_array; + + // pwm + uint8_t cpu_pwm = 0, sw_pwm = 0; + uint8_t u15_pwm = 0, u16_pwm = 0; + + // Configuration + uint8_t fan_out_full_speed_enable = 0; + uint8_t fan_in_duty_speed_enable = 0; + uint8_t psu_absent_full_speed_enable = 0; + uint8_t temp_fail_full_speed_enable = 0; + uint8_t fan_fault_full_speed_enable = 0; + uint8_t over_temp_full_speed_warn_enable = 0; + uint8_t fan_in_duty_speed_percent = 70; + uint8_t fan_in_duty_speed_duration = 20; // seconds + + fan_out_full_speed_enable = 1; + fan_in_duty_speed_enable = 1; + psu_absent_full_speed_enable = 1; + temp_fail_full_speed_enable = 1; + fan_fault_full_speed_enable = 1; + over_temp_full_speed_warn_enable = 1; + fan_full_speed_fault_num = 2; + fan_in_duty_speed_percent = 70; + fan_in_duty_speed_duration = 20; // seconds + + if (FAN_F2B == Sys_Airflow) { + temp_array = Temp_F2B; + } else { + temp_array = Temp_B2F; + } + + for (i = 1; i <= THERMAL_COUNT; i++) { + if (!strcmp(temp_array[i].descr, "TEMP_BMC") && ABSENT == BMC_Status) { + continue; + } + if (!strcmp(temp_array[i].descr, "TEMP_CPU") || !strcmp(temp_array[i].descr, "TEMP_SW_Internal")) { + /* Eliminate CPU/SW internal temperature jitter */ + temp_array[i].temp = get_valid_temp(temp_array[i].path, 0); + } else { + temp_array[i].temp = get_temp(temp_array[i].path); + } + /* + * Any accessing temperature failure execpt DIMM will cause full speed speed + * because DIMM number is configurable + */ + if (temp_fail_full_speed_enable && ERR == temp_array[i].temp && + NULL == strstr(temp_array[i].descr, "DIMM")) { + DEBUG_PRINT("fail to get %s, set full pwm: %d\n\n", temp_array[i].descr, PWM_MAX); + set_all_fans(PWM_MAX); + return 0; + } + + DEBUG_PRINT("%s: %d\n", temp_array[i].descr, temp_array[i].temp); + if (NOT_DEFINE != temp_array[i].hi_shutdown && + temp_array[i].temp > temp_array[i].hi_shutdown / TEMP_CONV_FACTOR) { + + /* soft shutdown */ + /*Record high temperature alarm */ + syslog(LOG_WARNING, "%s temperature %d is over than %d, machine should be shutdown !!!\n", + temp_array[i].descr, temp_array[i].temp, temp_array[i].hi_shutdown / TEMP_CONV_FACTOR); + closelog(); + system("sync"); + + /* switch board power off */ + ret = syscpld_setting(LPC_SWITCH_PWCTRL_REG, SWITCH_OFF); + if (ret) + { + perror("Fail to power off switch board !!!"); + } + + temp_array[i].is_ovshutdown_fault = 1; + /* wait for ledcontrol service to set alarm LED */ + sleep(5); + + system("reboot"); + } + + if (NOT_DEFINE != temp_array[i].hi_err && + temp_array[i].temp >= temp_array[i].hi_err / TEMP_CONV_FACTOR) { + /* warning if sensor value is higher than major alarm */ + if (0 == temp_array[i].is_overr_fault && over_temp_full_speed_warn_enable) + syslog(LOG_WARNING, "%s temperature %d is over than %d !!!\n", + temp_array[i].descr, temp_array[i].temp, temp_array[i].hi_err / TEMP_CONV_FACTOR); + + temp_array[i].is_overr_fault = 1; + temp_array[i].is_ovwarn_fault = 0; + } else if (NOT_DEFINE != temp_array[i].hi_warn && + temp_array[i].temp >= temp_array[i].hi_warn / TEMP_CONV_FACTOR) { + if (0 == temp_array[i].is_ovwarn_fault && over_temp_full_speed_warn_enable) + syslog(LOG_WARNING, "%s temperature %d is over than %d !!!\n", + temp_array[i].descr, temp_array[i].temp, temp_array[i].hi_warn / TEMP_CONV_FACTOR); + + temp_array[i].is_overr_fault = 0; + temp_array[i].is_ovwarn_fault = 1; + } else { + temp_array[i].is_overr_fault = 0; + temp_array[i].is_ovwarn_fault = 0; + } + } + + ret = check_psu_absent(); + if (0 != ret && psu_absent_full_speed_enable) { + DEBUG_PRINT("psu absent, set full pwm: %d\n\n", PWM_MAX); + set_all_fans(PWM_MAX); + return 0; + } + + ret = check_fan_absent(); + if (0 != ret && fan_out_full_speed_enable) { + DEBUG_PRINT("fan absent, set full pwm: %d\n\n", PWM_MAX); + set_all_fans(PWM_MAX); + fan_status = ABSENT; + return 0; + } else if (0 == ret && ABSENT == fan_status && fan_in_duty_speed_enable) { + DEBUG_PRINT("fan plug in, set duty pwm: %d\n\n", fan_in_duty_speed_percent * 255 / 100); + set_all_fans(fan_in_duty_speed_percent * 255 / 100); + fan_status = PRESENT; + sleep(fan_in_duty_speed_duration); + } + + ret = check_fan_fault(); + if (0 != ret && fan_full_speed_fault_num == ret && fan_fault_full_speed_enable) { + DEBUG_PRINT("fan fault, set full pwm: %d\n\n", PWM_MAX); + set_all_fans(PWM_MAX); + return 0; + } else { + /* calculate all PWMs */ + if (FAN_F2B == Sys_Airflow) { + DEBUG_PRINT("\n[pid] CPU temperature\n"); + cpu_pwm = pid_cal(temp_array[TEMP_CPU_ID].temp, &CPU_PID); + DEBUG_PRINT("\n[pid] SW temperature\n"); + sw_pwm = pid_cal(temp_array[TEMP_SW_Internal_ID].temp, &SW_PID); + DEBUG_PRINT("\n[linear] U15 temperature\n"); + u15_pwm = linear_cal(temp_array[TEMP_SW_U15_ID].temp, &U15LINEAR_F2B, 1); + DEBUG_PRINT("\n[linear] U16 temperature\n"); + u16_pwm = linear_cal(temp_array[TEMP_SW_U15_ID].temp, &U16LINEAR_F2B, 1); + } + /* get max PWM */ + DEBUG_PRINT("\n[final] cpu_pwm %d sw_pwm %d u15_pwm %d u16_pwm %d\n", + cpu_pwm, sw_pwm, u15_pwm, u16_pwm); + pwm = cpu_pwm > sw_pwm ? cpu_pwm : sw_pwm; + pwm = pwm > u15_pwm ? pwm : u15_pwm; + pwm = pwm > u16_pwm ? pwm : u16_pwm; + pwm = pwm > PWM_MAX ? PWM_MAX : pwm; + /* set max PWM to all fans */ + DEBUG_PRINT("[final] set normal pwm: %d\n\n", pwm); + set_all_fans(pwm); + } + + return 0; +} diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/fancontrol.h b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/fancontrol.h new file mode 100644 index 0000000000..826f2d782b --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/fancontrol.h @@ -0,0 +1,43 @@ +#ifndef _FANCONTROL_H_ +#define _FANCONTROL_H_ + +#include "platform_comm.h" + +#define INTERVAL 3 +#define TEMP_CONV_FACTOR 1000 +#define PWM_START 128 // 255 * 50% +#define PWM_MIN 128 +#define PWM_MAX 255 +#define RPM_FAULT_DEF 1000 // less than 1000rpm, regarded as fault +#define TEMP_DIFF_FAULT 15 //15 C +#define UNDEF -100 +#define OFF_H 0 // not add compensate + +typedef struct linear +{ + uint8_t min_temp; + uint8_t max_temp; + uint8_t min_pwm; + uint8_t max_pwm; + uint8_t hysteresis; + char lasttemp; + // effect by altitude, offset = (H-950)/175); + char temp_offset; + uint8_t lastpwm; + uint8_t lastpwmpercent; +} linear_t; + +typedef struct pid +{ + char lasttemp; + char lastlasttemp; + uint32_t setpoint; + float P; + float I; + float D; + float lastpwmpercent; +} pid_algo_t; + +int update_fan(void); + +#endif diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/fani.c b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/fani.c new file mode 100644 index 0000000000..d041081869 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/fani.c @@ -0,0 +1,105 @@ +#include +#include "platform_comm.h" + +onlp_fan_info_t f_info[FAN_COUNT + 1] = { + {}, + { + {ONLP_FAN_ID_CREATE(CHASSIS_FAN1_ID), "Chassis Fan 1", 0}, + 0, + ONLP_FAN_CAPS_B2F | ONLP_FAN_CAPS_F2B | ONLP_FAN_CAPS_GET_RPM | ONLP_FAN_CAPS_GET_PERCENTAGE, + }, + { + {ONLP_FAN_ID_CREATE(CHASSIS_FAN2_ID), "Chassis Fan 2", 0}, + 0, + ONLP_FAN_CAPS_B2F | ONLP_FAN_CAPS_F2B | ONLP_FAN_CAPS_GET_RPM | ONLP_FAN_CAPS_GET_PERCENTAGE, + }, + { + {ONLP_FAN_ID_CREATE(CHASSIS_FAN3_ID), "Chassis Fan 3", 0}, + 0, + ONLP_FAN_CAPS_B2F | ONLP_FAN_CAPS_F2B | ONLP_FAN_CAPS_GET_RPM | ONLP_FAN_CAPS_GET_PERCENTAGE, + }, + { + {ONLP_FAN_ID_CREATE(CHASSIS_FAN4_ID), "Chassis Fan 4", 0}, + 0, + ONLP_FAN_CAPS_B2F | ONLP_FAN_CAPS_F2B | ONLP_FAN_CAPS_GET_RPM | ONLP_FAN_CAPS_GET_PERCENTAGE, + }, + { + {ONLP_FAN_ID_CREATE(CHASSIS_FAN5_ID), "Chassis Fan 5", 0}, + 0, + ONLP_FAN_CAPS_B2F | ONLP_FAN_CAPS_F2B | ONLP_FAN_CAPS_GET_RPM | ONLP_FAN_CAPS_GET_PERCENTAGE, + }, + { + {ONLP_FAN_ID_CREATE(CHASSIS_FAN6_ID), "Chassis Fan 6", 0}, + 0, + ONLP_FAN_CAPS_B2F | ONLP_FAN_CAPS_F2B | ONLP_FAN_CAPS_GET_RPM | ONLP_FAN_CAPS_GET_PERCENTAGE, + }, + { + {ONLP_FAN_ID_CREATE(CHASSIS_FAN7_ID), "Chassis Fan 7", 0}, + 0, + ONLP_FAN_CAPS_B2F | ONLP_FAN_CAPS_F2B | ONLP_FAN_CAPS_GET_RPM | ONLP_FAN_CAPS_GET_PERCENTAGE, + }, + { + {ONLP_FAN_ID_CREATE(PSU1_FAN_ID), "PSU Fan 1", ONLP_PSU_ID_CREATE(1)}, + 0, + ONLP_FAN_CAPS_B2F | ONLP_FAN_CAPS_F2B | ONLP_FAN_CAPS_GET_RPM | ONLP_FAN_CAPS_GET_PERCENTAGE, + }, + { + {ONLP_FAN_ID_CREATE(PSU2_FAN_ID), "PSU Fan 2", ONLP_PSU_ID_CREATE(2)}, + 0, + ONLP_FAN_CAPS_B2F | ONLP_FAN_CAPS_F2B | ONLP_FAN_CAPS_GET_RPM | ONLP_FAN_CAPS_GET_PERCENTAGE, + }, +}; + +int onlp_fani_init(void) +{ + return ONLP_STATUS_OK; +} + +int onlp_fani_info_get(onlp_oid_t id, onlp_fan_info_t *info_p) +{ + int fan_id; + uint8_t spd_result; + int airflow = FAN_F2B; + + fan_id = ONLP_OID_ID_GET(id); + *info_p = f_info[fan_id]; + + if(fan_id <= CHASSIS_FAN_COUNT){ + get_fan_info(fan_id, info_p->model, info_p->serial, &airflow); + }else{ + int psu_id = 0; + if(fan_id == CHASSIS_FAN_COUNT + 1){ + psu_id = 1; + }else if(fan_id == CHASSIS_FAN_COUNT + 2){ + psu_id = 2; + } + get_psu_model_sn(psu_id, info_p->model, info_p->serial); + + if (!strcmp(info_p->model, PSU_F2B_AIRFLOW_FIELD)) { + airflow = FAN_F2B; + } else if (!strcmp(info_p->model, PSU_B2F_AIRFLOW_FIELD)) { + airflow = FAN_B2F; + } + } + + spd_result = get_fan_speed(fan_id, &(info_p->percentage), &(info_p->rpm)); + if(spd_result || 0 == info_p->rpm){ + return ONLP_FAN_STATUS_FAILED; + } + + info_p->status |= ONLP_FAN_STATUS_PRESENT; + + switch (airflow) + { + case FAN_F2B: + info_p->status |= ONLP_FAN_STATUS_F2B; + break; + case FAN_B2F: + info_p->status |= ONLP_FAN_STATUS_B2F; + break; + default: + break; + } + + return ONLP_STATUS_OK; +} diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/ledcontrol.c b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/ledcontrol.c new file mode 100644 index 0000000000..2b98bc95b5 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/ledcontrol.c @@ -0,0 +1,277 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "platform_comm.h" +#include "platform_wobmc.h" +#include "ledcontrol.h" + +extern struct fan Fan[FAN_COUNT + 1]; +extern struct temp Temp_B2F[THERMAL_COUNT + 1]; +extern struct temp Temp_F2B[THERMAL_COUNT + 1]; +extern struct vol_curr_pwr Vol_Curr_Pwr[POWER_COUNT + 1]; +extern struct psu Psu[PSU_COUNT + 1]; +extern uint8_t Sys_Airflow; + +static uint8_t set_panel_psu_led(uint8_t mode) +{ + int ret = 0; + + ret = syscpld_setting(LPC_LED_PSU_REG, mode); + if (ret) { + perror("Can't set PSU LED"); + return ERR; + } + + return OK; +} + +static uint8_t set_panel_fan_led(uint8_t mode) +{ + int ret = 0; + + ret = syscpld_setting(LPC_LED_FAN_REG, mode); + if (ret) { + perror("Can't set FAN LED"); + return ERR; + } + + return OK; +} + +static uint8_t set_panel_alarm_led(uint8_t mode) +{ + int ret = 0; + + ret = syscpld_setting(LPC_LED_ALARM_REG, mode); + if (ret) { + perror("Can't set alarm LED"); + return ERR; + } + + return OK; +} + +int update_led(void) +{ + int alarm_led = 0; + int ret = 0; + int i = 0, fault = 0; + int num_fan_absent = 0; + int num_psu_absent = 0; + int num_fan_fault = 0; + int num_psu_fault = 0; + int num_fan_airflow_fault = 0; + int num_psu_airflow_fault = 0; + FILE *fp = NULL; + char buffer[200] = {0}; + struct temp *temp_array; + + num_psu_absent = check_psu_absent(); + num_fan_absent = check_fan_absent(); + num_psu_fault = check_psu_fault(); + num_fan_fault = check_fan_fault(); + num_fan_airflow_fault = check_fan_airflow_fault(Sys_Airflow); + num_psu_airflow_fault = check_psu_airflow_fault(Sys_Airflow); + + /* Set panel PSU LED */ + if (0 == num_psu_absent && 0 == num_psu_fault) { + set_panel_psu_led(LED_PSU_GRN); + } + else { + set_panel_psu_led(LED_PSU_AMB); + } + + /* Set panel fan LED */ + if (CHASSIS_FAN_COUNT == num_fan_absent) { + set_panel_fan_led(LED_FAN_OFF); + } + else if (0 != num_fan_fault || num_fan_absent > 0) { + set_panel_fan_led(LED_FAN_AMB); + } else { + set_panel_fan_led(LED_FAN_GRN); + } + + /* Set panel alarm LED */ + fault = num_psu_absent + num_fan_absent + num_psu_fault + \ + num_fan_fault + num_fan_airflow_fault + num_psu_airflow_fault; + if (0 == fault) { + alarm_led = led_grn; + } + else if (1 == fault) { + alarm_led = led_amb_1hz; + } + else if (fault > 1) { + alarm_led = led_amb; + } + + /* temperature fault flags are set in fancontrol service */ + if (FAN_F2B == Sys_Airflow) { + temp_array = Temp_F2B; + } else { + temp_array = Temp_B2F; + } + for (i = 1; i <= THERMAL_COUNT; i++) { + if (temp_array[i].is_ovshutdown_fault) { + alarm_led += led_amb; + continue; + } else if (temp_array[i].is_overr_fault) { + alarm_led += led_amb_4hz; + continue; + } + else if (temp_array[i].is_ovwarn_fault) { + alarm_led += led_amb_1hz; + } else { + alarm_led += led_grn; + } + } + /* check power fault flags */ + for (i = 1; i <= POWER_COUNT; i++) { + if (NULL != strstr(Vol_Curr_Pwr[i].descr, "PSU1")) { + if (Psu[1].is_absent || Psu[1].is_ac_fault || + Psu[1].is_power_fault || Psu[1].is_alert) { + continue; + } + } + if (NULL != strstr(Vol_Curr_Pwr[i].descr, "PSU2")) { + if (Psu[2].is_absent || Psu[2].is_ac_fault || + Psu[2].is_power_fault || Psu[2].is_alert) { + continue; + } + } + ret = get_sysnode_value(Vol_Curr_Pwr[i].path, &Vol_Curr_Pwr[i].vol_curr_pwr); + if (ret < 0) + continue; + + if (NOT_DEFINE != Vol_Curr_Pwr[i].hi_shutdown && + Vol_Curr_Pwr[i].vol_curr_pwr >= Vol_Curr_Pwr[i].hi_shutdown) { + if (0 == Vol_Curr_Pwr[i].is_ovshutdown_fault) + syslog(LOG_WARNING, "%s voltage %ld is over than %ld !!!\n", + Vol_Curr_Pwr[i].descr, Vol_Curr_Pwr[i].vol_curr_pwr, + Vol_Curr_Pwr[i].hi_shutdown); + + Vol_Curr_Pwr[i].is_ovshutdown_fault = 1; + /** + * Need to count the number of errors to set the alarm LED, + * so here clear over warn error + */ + Vol_Curr_Pwr[i].is_overr_fault = 0; + Vol_Curr_Pwr[i].is_ovwarn_fault = 0; + Vol_Curr_Pwr[i].is_udshutdown_fault = 0; + Vol_Curr_Pwr[i].is_uderr_fault = 0; + Vol_Curr_Pwr[i].is_udwarn_fault = 0; + alarm_led += led_amb; + } else if (NOT_DEFINE != Vol_Curr_Pwr[i].hi_err && + Vol_Curr_Pwr[i].vol_curr_pwr >= Vol_Curr_Pwr[i].hi_err) { + if (0 == Vol_Curr_Pwr[i].is_overr_fault) + syslog(LOG_WARNING, "%s voltage %ld is over than %ld !!!\n", + Vol_Curr_Pwr[i].descr, Vol_Curr_Pwr[i].vol_curr_pwr, + Vol_Curr_Pwr[i].hi_err); + + Vol_Curr_Pwr[i].is_ovshutdown_fault = 0; + Vol_Curr_Pwr[i].is_overr_fault = 1; + Vol_Curr_Pwr[i].is_ovwarn_fault = 0; + Vol_Curr_Pwr[i].is_udshutdown_fault = 0; + Vol_Curr_Pwr[i].is_uderr_fault = 0; + Vol_Curr_Pwr[i].is_udwarn_fault = 0; + alarm_led += led_amb_4hz; + } else if (NOT_DEFINE != Vol_Curr_Pwr[i].hi_warn && + Vol_Curr_Pwr[i].vol_curr_pwr >= Vol_Curr_Pwr[i].hi_warn) { + if (0 == Vol_Curr_Pwr[i].is_ovwarn_fault) + syslog(LOG_WARNING, "%s voltage %ld is over than %ld !!!\n", + Vol_Curr_Pwr[i].descr, Vol_Curr_Pwr[i].vol_curr_pwr, + Vol_Curr_Pwr[i].hi_warn); + + Vol_Curr_Pwr[i].is_ovshutdown_fault = 0; + Vol_Curr_Pwr[i].is_overr_fault = 0; + Vol_Curr_Pwr[i].is_ovwarn_fault = 1; + Vol_Curr_Pwr[i].is_udshutdown_fault = 0; + Vol_Curr_Pwr[i].is_uderr_fault = 0; + Vol_Curr_Pwr[i].is_udwarn_fault = 0; + alarm_led += led_amb_1hz; + } else if (NOT_DEFINE != Vol_Curr_Pwr[i].lo_shutdown && + Vol_Curr_Pwr[i].vol_curr_pwr <= Vol_Curr_Pwr[i].lo_shutdown) { + if (0 == Vol_Curr_Pwr[i].is_udshutdown_fault) + syslog(LOG_WARNING, "%s voltage %ld is less than %ld !!!\n", + Vol_Curr_Pwr[i].descr, Vol_Curr_Pwr[i].vol_curr_pwr, + Vol_Curr_Pwr[i].lo_shutdown); + + Vol_Curr_Pwr[i].is_ovshutdown_fault = 0; + Vol_Curr_Pwr[i].is_overr_fault = 0; + Vol_Curr_Pwr[i].is_ovwarn_fault = 0; + Vol_Curr_Pwr[i].is_udshutdown_fault = 1; + Vol_Curr_Pwr[i].is_uderr_fault = 0; + Vol_Curr_Pwr[i].is_udwarn_fault = 0; + alarm_led += led_amb; + } else if (NOT_DEFINE != Vol_Curr_Pwr[i].lo_err && + Vol_Curr_Pwr[i].vol_curr_pwr <= Vol_Curr_Pwr[i].lo_err) { + if (0 == Vol_Curr_Pwr[i].is_uderr_fault) + syslog(LOG_WARNING, "%s voltage %ld is less than %ld !!!\n", + Vol_Curr_Pwr[i].descr, Vol_Curr_Pwr[i].vol_curr_pwr, + Vol_Curr_Pwr[i].lo_err); + + Vol_Curr_Pwr[i].is_ovshutdown_fault = 0; + Vol_Curr_Pwr[i].is_overr_fault = 0; + Vol_Curr_Pwr[i].is_ovwarn_fault = 0; + Vol_Curr_Pwr[i].is_udshutdown_fault = 0; + Vol_Curr_Pwr[i].is_uderr_fault = 1; + Vol_Curr_Pwr[i].is_udwarn_fault = 0; + alarm_led += led_amb_4hz; + } else if (NOT_DEFINE != Vol_Curr_Pwr[i].lo_warn && + Vol_Curr_Pwr[i].vol_curr_pwr <= Vol_Curr_Pwr[i].lo_warn) { + if (0 == Vol_Curr_Pwr[i].is_udwarn_fault) + syslog(LOG_WARNING, "%s voltage %ld is less than %ld !!!\n", + Vol_Curr_Pwr[i].descr, Vol_Curr_Pwr[i].vol_curr_pwr, + Vol_Curr_Pwr[i].lo_warn); + + Vol_Curr_Pwr[i].is_ovshutdown_fault = 0; + Vol_Curr_Pwr[i].is_overr_fault = 0; + Vol_Curr_Pwr[i].is_ovwarn_fault = 0; + Vol_Curr_Pwr[i].is_udshutdown_fault = 0; + Vol_Curr_Pwr[i].is_uderr_fault = 0; + Vol_Curr_Pwr[i].is_udwarn_fault = 1; + alarm_led += led_amb_1hz; + } else { + Vol_Curr_Pwr[i].is_ovshutdown_fault = 0; + Vol_Curr_Pwr[i].is_overr_fault = 0; + Vol_Curr_Pwr[i].is_ovwarn_fault = 0; + Vol_Curr_Pwr[i].is_udshutdown_fault = 0; + Vol_Curr_Pwr[i].is_uderr_fault = 0; + Vol_Curr_Pwr[i].is_udwarn_fault = 0; + alarm_led += led_grn; + } + } + + /* Check mce error */ + fp = popen("dmesg | grep 'Machine check events logged'", "r"); + if (fp == NULL) { + printf("call popen %s fail\n", "dmesg"); + return -1; + } + fscanf(fp, "%s", buffer); + if (0 != strlen(buffer)) { + alarm_led += led_amb; + syslog(LOG_WARNING, "MCE event is logged !!!\n"); + } else { + alarm_led += led_grn; + } + pclose(fp); + + if (alarm_led >= led_amb) { + set_panel_alarm_led(LED_ALARM_AMB); + } else if (alarm_led == led_amb_4hz) { + set_panel_alarm_led(LED_ALARM_AMB_4HZ); + } else if (alarm_led == led_amb_1hz) { + set_panel_alarm_led(LED_ALARM_AMB_1HZ); + } else { + set_panel_alarm_led(LED_ALARM_GRN); + } + + return 0; +} diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/ledcontrol.h b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/ledcontrol.h new file mode 100644 index 0000000000..e75574baff --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/ledcontrol.h @@ -0,0 +1,13 @@ +#ifndef _LEDCONTROL_H_ +#define _LEDCONTROL_H_ +#include "fancontrol.h" + +enum { + led_grn, + led_amb_1hz, + led_amb_4hz, + led_amb, +}; + +int update_led(void); +#endif diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/ledi.c b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/ledi.c new file mode 100644 index 0000000000..047f684b23 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/ledi.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include +#include "platform_comm.h" + +/* + * Get the information for the given LED OID. + */ +static onlp_led_info_t led_info[] = +{ + { }, + { + { ONLP_LED_ID_CREATE(LED_SYSTEM), "System LED (Front)", 0 }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_ON_OFF | ONLP_LED_CAPS_YELLOW | ONLP_LED_CAPS_YELLOW_BLINKING | ONLP_LED_CAPS_GREEN | + ONLP_LED_CAPS_GREEN_BLINKING, + }, + { + { ONLP_LED_ID_CREATE(LED_ALARM), "Alert LED (Front)", 0 }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_ON_OFF | ONLP_LED_CAPS_YELLOW | ONLP_LED_CAPS_YELLOW_BLINKING | ONLP_LED_CAPS_GREEN | + ONLP_LED_CAPS_GREEN_BLINKING, + }, + { + { ONLP_LED_ID_CREATE(LED_PSU), "PSU LED (Front)", 0 }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_ON_OFF | ONLP_LED_CAPS_AUTO | ONLP_LED_CAPS_YELLOW | ONLP_LED_CAPS_GREEN, + }, + { + { ONLP_LED_ID_CREATE(LED_FAN), "FAN LED (Front)", 0 }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_ON_OFF | ONLP_LED_CAPS_AUTO | ONLP_LED_CAPS_YELLOW | ONLP_LED_CAPS_GREEN, + } +}; + +int +onlp_ledi_init(void) +{ + return ONLP_STATUS_OK; +} + +int +onlp_ledi_info_get(onlp_oid_t id, onlp_led_info_t* info_p) +{ + int led_id; + uint8_t led_color = 0; + uint8_t blink_status = 0; + uint8_t hw_control_status = 0; + uint8_t result = 0; + + led_id = ONLP_OID_ID_GET(id); + *info_p = led_info[led_id]; + + result = get_led_status(led_id); + + if(result != 0xFF) + info_p->status |= ONLP_LED_STATUS_ON; + + switch(led_id){ + case LED_SYSTEM: + case LED_ALARM: + + led_color = (result >> 4)&0x3; + if(led_color == 0){ + info_p->mode |= ONLP_LED_MODE_BLINKING; + break; + } + if(led_color == 1){ + info_p->mode |= ONLP_LED_MODE_GREEN; + } + if(led_color == 2){ + info_p->mode |= ONLP_LED_MODE_YELLOW; + } + if(led_color == 3){ + info_p->mode |= ONLP_LED_MODE_OFF; + break; + } + + blink_status = result & 0x3; + if(blink_status == 1 || blink_status == 2){ + int current_mode = info_p->mode; + info_p->mode = current_mode+1; + } + + break; + case LED_PSU: + case LED_FAN: + hw_control_status = (result >> 4) & 0x1; + led_color = result & 0x3; + if(!hw_control_status) + { + if(led_color == 1){ + info_p->mode = ONLP_LED_MODE_YELLOW; + }else if(led_color == 2){ + info_p->mode = ONLP_LED_MODE_GREEN; + }else if(led_color == 3){ + info_p->mode = ONLP_LED_MODE_OFF; + } + }else{ + info_p->mode = ONLP_LED_MODE_AUTO; + } + break; + } + return ONLP_STATUS_OK; +} diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/make.mk b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/make.mk new file mode 100644 index 0000000000..f65e5c1fd5 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/make.mk @@ -0,0 +1,9 @@ +############################################################################### +# +# +# +############################################################################### + +LIBRARY := x86_64_cls_ds4101 +$(LIBRARY)_SUBDIR := $(dir $(lastword $(MAKEFILE_LIST))) +include $(BUILDER)/lib.mk diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/platform_comm.c b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/platform_comm.c new file mode 100644 index 0000000000..bfe1de70bd --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/platform_comm.c @@ -0,0 +1,404 @@ +////////////////////////////////////////////////////////////// +// PLATFORM FUNCTION TO INTERACT WITH SYS_CPLD AND BMC // +////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform_comm.h" +#include "platform_wbmc.h" +#include "platform_wobmc.h" + + +extern uint8_t BMC_Status; + +static const struct led_reg_mapper led_mapper[LED_COUNT + 1] = { + {}, + {"LED_SYSTEM", LED_SYSTEM, LED_SYSTEM_REG}, + {"LED_ALARM", LED_ALARM, LED_ALARM_REG}, + {"LED_PSU", LED_PSU, LED_PSU_REG}, + {"LED_FAN", LED_FAN, LED_FAN_REG} +}; + +const struct psu_reg_bit_mapper psu_mapper [PSU_COUNT + 1] = { + {}, + {PSU_STA_REG, 3, 5, 7, 1}, + {PSU_STA_REG, 2, 4, 6, 0}, +}; + +char* trim (char *s) +{ + int i; + + while (isspace (*s)) s++; // skip left side white spaces + for (i = strlen (s) - 1; (isspace (s[i])); i--) ; // skip right side white spaces + s[i + 1] = '\0'; + + return s; +} + +void array_trim(char *strIn, char *strOut) +{ + int i, j; + + i = 0; + j = strlen(strIn) - 1; + + while(strIn[i] == ' ') ++i; + while(strIn[j] == ' ') --j; + + /* in case all characters are spaces */ + if (-1 != j) { + strncpy(strOut, strIn + i , j - i + 1); + strOut[j - i + 1] = '\0'; + } +} + +int exec_cmd(char *cmd, char *retd) +{ + int ret = 0; + int i = 0; + char c; + FILE *pFd = NULL; + + pFd = popen(cmd, "r"); + if (pFd != NULL) { + c = fgetc(pFd); + while (c != EOF) { + retd[i] = c; + i++; + c = fgetc(pFd); + } + pclose(pFd); + } + + return ret; +} + +uint8_t read_register(uint16_t dev_reg) +{ + char command[256]; + uint8_t value; + int fd = 0; + int ret = 0; + + sprintf(command, "%sdump", SYS_CPLD_PATH); + fd = open(command, O_RDONLY); + if (fd < 0) { + printf("ERROR: Fail to open the file: %s \n", command); + return -1; + } + lseek(fd, dev_reg, SEEK_SET); + ret = read(fd, &value, sizeof(value)); + if (ret < 0) { + printf("ERROR: Fail to read register: 0x%x \n", value); + close(fd); + return -1; + } + close(fd); + + return value; +} + +int read_device_node_binary(char *filename, char *buffer, int buf_size, int data_len) +{ + int fd; + int len; + + if ((buffer == NULL) || (buf_size < 0)) + return -1; + + if ((fd = open(filename, O_RDONLY)) == -1) + return -1; + + if ((len = read(fd, buffer, buf_size)) < 0) + { + close(fd); + return -1; + } + + if ((close(fd) == -1)) + return -1; + + if ((len > buf_size) || (data_len != 0 && len != data_len)) + return -1; + + return 0; +} + +int read_device_node_string(char *filename, char *buffer, int buf_size, int data_len) +{ + int ret; + + if (data_len >= buf_size) + { + return -1; + } + + ret = read_device_node_binary(filename, buffer, buf_size - 1, data_len); + if (ret == 0) + { + buffer[buf_size - 1] = '\0'; + } + + return ret; +} + +int get_sysnode_value(const char *path, void *data) +{ + int fd; + int ret = 0; + FILE *fp = NULL; + char new_path[80] = {0}; + char buffer[20] = {0}; + long *value = (long *)data; + + /* replace hwmon* with hwmon[0-9] */ + sprintf(new_path, "ls %s", path); + fp = popen(new_path, "r"); + if (fp == NULL) + { + printf("call popen %s fail\n", new_path); + return -1; + } + fscanf(fp, "%s", new_path); + pclose(fp); + + fd = open(new_path, O_RDONLY); + if (fd < 0) { + printf("open %s fail\n", new_path); + ret = -1; + } else { + read(fd, buffer, sizeof(buffer)); + *value = atol(buffer); + close(fd); + } + + return ret; +} + +int set_sysnode_value(const char *path, int data) +{ + int fd, ret = 0; + FILE *fp = NULL; + char new_path[200] = {0}; + char buffer[50] = {0}; + + /* replace hwmon* with hwmon[0-9] */ + sprintf(new_path, "ls %s", path); + fp = popen(new_path, "r"); + if (fp == NULL) + { + printf("call popen fail\n"); + return -1; + } + fscanf(fp, "%s", new_path); + pclose(fp); + + fd = open(new_path, O_WRONLY); + if (fd < 0) { + ret = -1; + } else { + sprintf(buffer, "%d", data); + write(fd, buffer, strlen(buffer)); + close(fd); + } + + return ret; +} + +int syscpld_setting(uint32_t reg, uint8_t val) +{ + char data[30] = {0}; + char path[100] = {0}; + int fd, rc = 0; + + sprintf(path, "%s%s",SYS_CPLD_PATH, "setreg"); + sprintf(data, "0x%x 0x%x",reg, val); + + fd = open(path, O_WRONLY); + if (fd < 0) { + printf("Fail to open the file: %s \n", path); + return ERR; + } + + rc = write(fd, data, strlen(data)); + if (rc != strlen(data)) { + printf("Write failed. count=%lu, rc=%d \n", strlen(data), rc); + close(fd); + return ERR; + } + + close(fd); + + return OK; +} + +int read_smbus_block(int bus, int address, int bank, unsigned char *cblock) +{ + int res = 0, file; + char filename[20]; + + snprintf(filename, 19, "/dev/i2c-%d", bus); + file = open(filename, O_RDWR); + if (file < 0) { + return -1; + } + if (ioctl(file, I2C_SLAVE_FORCE, address) < 0) { + return -1; + } + res = i2c_smbus_read_block_data(file, bank, cblock); + close(file); + + return res; +} + + + +int read_eeprom(char* path, int offset, char* data, unsigned int *count) +{ + int ret = 0; + + int fd = open(path, O_RDONLY); + + if (fd < 0) { + printf("Fail to open the file: %s \n", path); + return -1; + } + if (offset > 0) { + lseek(fd, offset, SEEK_SET); + } + + size_t len = read(fd, data, *count); + + if (len == 0) { + DEBUG_PRINT("The file(%s) is empty, count=%d. \n", path, *count); + } else if (len < 0) { + perror("Can't read eeprom"); + return ret; + } + + *count = len; + close(fd); + + return ret; +} + + +uint8_t get_led_status(int id) +{ + uint8_t ret = 0xFF; + + if (id > LED_COUNT || id < 0) + return 0xFF; + + if (id <= LED_COUNT) + { + uint8_t result = 0; + uint16_t led_stat_reg; + + led_stat_reg = led_mapper[id].dev_reg; + result = read_register(led_stat_reg); + ret = result; + } + + return ret; +} + +uint8_t get_psu_status(int id) +{ + uint8_t ret = 0xFF; + uint16_t psu_stat_reg; + + if (id <= (PSU_COUNT)) + { + uint8_t result = 0; + + psu_stat_reg = psu_mapper[id].sta_reg; + result = read_register(psu_stat_reg); + ret = result; + } + + return ret; +} + +int get_psu_info(int id, int *mvin, int *mvout, int *mpin, int *mpout, int *miin, int *miout) +{ + int ret = 0; + + if (PRESENT == BMC_Status) { + ret = get_psu_info_wbmc(id, mvin, mvout, mpin, mpout, miin, miout); + } else { + ret = get_psu_info_wobmc(id, mvin, mvout, mpin, mpout, miin, miout); + } + + return ret; +} + +int get_psu_model_sn(int id, char *model, char *serial_number) +{ + int ret = 0; + + if (PRESENT == BMC_Status) { + ret = get_psu_model_sn_wbmc(id, model, serial_number); + } else { + ret = get_psu_model_sn_wobmc(id, model, serial_number); + } + + return ret; +} + +int get_fan_info(int id, char *model, char *serial, int *isfanb2f) +{ + int ret = 0; + + if (PRESENT == BMC_Status) { + ret = get_fan_info_wbmc(id, model, serial, isfanb2f); + } else { + ret = get_fan_info_wobmc(id, model, serial, isfanb2f); + } + + return ret; +} + +int get_sensor_info(int id, int *temp, int *warn, int *error, int *shutdown) +{ + int ret = 0; + + if (PRESENT == BMC_Status) { + ret = get_sensor_info_wbmc(id, temp, warn, error, shutdown); + } else { + ret = get_sensor_info_wobmc(id, temp, warn, error, shutdown); + } + + return ret; +} + +int get_fan_speed(int id,int *per, int *rpm) +{ + int ret = 0; + + if (PRESENT == BMC_Status) { + ret = get_fan_speed_wbmc(id, per, rpm); + } else { + ret = get_fan_speed_wobmc(id, per, rpm); + } + + return ret; +} diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/platform_comm.h b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/platform_comm.h new file mode 100644 index 0000000000..4c85331b7d --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/platform_comm.h @@ -0,0 +1,190 @@ +#ifndef _PLATFORM_COMMON_H_ +#define _PLATFORM_COMMON_H_ +#include +#include +#define PREFIX_PATH_LEN 100 + +//DEVICE NUMBERS +#define FAN_COUNT 9 +#define CHASSIS_FAN_COUNT 7 +#define PSU_COUNT 2 +#define THERMAL_COUNT 24 +#define CHASSIS_THERMAL_COUNT 18 +#define POWER_COUNT 40 +#define LED_COUNT 4 +#define OSFP_COUNT 32 +#define SFP_COUNT 2 + +//DEVICE REGISTERS +#define LED_PSU_REG 0x61 +#define LPC_LED_PSU_REG 0xA161 +#define LED_PSU_AMB 0x01 +#define LED_PSU_GRN 0x02 + +#define LED_ALARM_REG 0x63 +#define LPC_LED_ALARM_REG 0xA163 +#define LED_ALARM_AMB 0x20 +#define LED_ALARM_GRN 0x10 +#define LED_ALARM_AMB_1HZ 0x21 +#define LED_ALARM_AMB_4HZ 0x22 + +#define LED_FAN_REG 0x65 +#define LPC_LED_FAN_REG 0xA165 +#define LED_FAN_AMB 0x01 +#define LED_FAN_GRN 0x02 +#define LED_FAN_OFF 0x03 + +#define LED_SYSTEM_REG 0x62 + +#define PSU_STA_REG 0x60 +#define BMC_STATUS_REG 0x08 + +#define LPC_COME_BTNCTRL_REG 0xA124 +#define LPC_SWITCH_PWCTRL_REG 0xA126 +#define SWITCH_PWCTRL_REG 0x26 + +#define COME_BTNOFF_HOLD 0x00 +#define COME_BTNOFF_RELEASE 0x01 +#define COME_BTNOFF_HOLD_SEC 8 + +#define SWITCH_OFF 0x01 +#define SWITCH_ON 0x10 + + +//OFFSET +#define OSFP_I2C_START 15 +#define SFP_I2C_START 2 + +//PSU and chassis FANs maximum rpm +#define PSU_FAN_MAX 31000 +#define CHASSIS_FAN_FRONT_MAX 32000 +#define CHASSIS_FAN_REAR_MAX 28000 + +/* fan speed tolerant scope(duty), exclusive + * duty should regard as fan fault + */ +#define CHASIS_FAN_TOLERANT_PER 25 + +#define FAN_F2B 1 +#define FAN_B2F 0 +#define ABSENT 1 +#define PRESENT 0 +#define ERR -1 +#define OK 0 +#define FAULT 2 + +#define SYS_CPLD_PATH "/sys/devices/platform/sys_cpld/" +#define SYS_FPGA_PATH "/sys/devices/platform/fpga-sys/" +#define PLATFORM_SFP_PATH "/sys/devices/platform/fpga-xcvr" +#define PLATFORM_OSFP_PATH "/sys/class/SFF" +#define I2C_DEVICE_PATH "/sys/bus/i2c/devices/" +#define PREFIX_PATH_ON_SYS_EEPROM "/sys/bus/i2c/devices/i2c-0/0-0056/eeprom" + +#define PSU_F2B_AIRFLOW_FIELD "TDPS2000LB A " +#define PSU_B2F_AIRFLOW_FIELD "DPS-1300AB-36 " + +//COMMON USE +#define NELEMS(x) (sizeof(x) / sizeof((x)[0])) +#define DEBUG_MODE 0 + +#if (DEBUG_MODE==1) +#define DEBUG_PRINT(fmt, args...) fprintf(stderr, fmt, ## args) +#else +#define DEBUG_PRINT(fmt, args...) /* Don't do anything in release builds */ +#endif + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; + +enum led_id { + LED_RESERVED = 0, + LED_SYSTEM, + LED_ALARM, + LED_PSU, + LED_FAN +}; + +enum psu_id { + PSUL_ID = 1, + PSUR_ID, +}; + +enum thermal_id { + TEMP_CPU_ID = 1, + TEMP_DIMMA0_ID, + TEMP_DIMMB0_ID, + TEMP_SW_Internal_ID, + BMC_Temp_ID, + TEMP_BB_U3_ID, + TEMP_SW_U15_ID, + TEMP_SW_U17_ID, + TEMP_SW_U16_ID, + TEMP_SW_U13_ID, + TEMP_SW_U11_ID, + TEMP_SW_U12_ID, + TEMP_FCB_U52_ID, + TEMP_FCB_U17_ID, + MP5023_T_ID, + VDD_CORE_T_ID, + XP3R3V_LEFT_T_ID, + XP3R3V_RIGHT_T_ID, + PSU1_TEMP1_ID, + PSU1_TEMP2_ID, + PSU1_TEMP3_ID, + PSU2_TEMP1_ID, + PSU2_TEMP2_ID, + PSU2_TEMP3_ID, +}; + +enum fan_id { + CHASSIS_FAN1_ID = 1, + CHASSIS_FAN2_ID, + CHASSIS_FAN3_ID, + CHASSIS_FAN4_ID, + CHASSIS_FAN5_ID, + CHASSIS_FAN6_ID, + CHASSIS_FAN7_ID, + PSU1_FAN_ID, + PSU2_FAN_ID, +}; + + +struct led_reg_mapper{ + char *name; + uint16_t device; + uint16_t dev_reg; +}; + +struct psu_reg_bit_mapper{ + uint16_t sta_reg; + uint8_t bit_present; + uint8_t bit_alert; + uint8_t bit_ac_sta; + uint8_t bit_pow_sta; +}; + +int read_device_node_binary(char *filename, char *buffer, int buf_size, int data_len); +int read_device_node_string(char *filename, char *buffer, int buf_size, int data_len); +int set_sysnode_value(const char *path, int data); +int get_sysnode_value(const char *path, void *data); +int syscpld_setting(uint32_t reg, uint8_t val); +int read_eeprom(char* path, int offset, char* data, unsigned int *count); +int read_smbus_block(int bus, int address, int bank, unsigned char *cblock); +int exec_cmd(char *cmd, char *retd); + +uint8_t read_register(uint16_t dev_reg); +char* trim (char *s); +void array_trim(char *strIn, char *strOut); + +uint8_t get_led_status(int id); +int get_psu_model_sn(int id, char* model,char* serial_number); +int get_psu_info(int id,int *mvin,int *mvout,int *mpin,int *mpout,int *miin,int *miout); + +int get_fan_info(int id,char* model,char* serial,int *get_fan_info); +int get_sensor_info(int id, int *temp, int *warn, int *error, int *shutdown); +int get_fan_speed(int id,int* per,int* rpm); +uint8_t get_psu_status(int id); + + +#endif /* _PLATFORM_COMMON_H_ */ diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/platform_wbmc.c b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/platform_wbmc.c new file mode 100644 index 0000000000..f9bc5a5397 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/platform_wbmc.c @@ -0,0 +1,951 @@ +////////////////////////////////////////////////////////////// +// PLATFORM FUNCTION TO INTERACT WITH SYS_CPLD AND BMC // +////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "platform_comm.h" +#include "platform_wbmc.h" + +static struct device_info fan_information[FAN_COUNT + 1] = { + {"unknown", "unknown",1}, //check + {}, //Fan 1 + {}, //Fan 2 + {}, //Fan 3 + {}, //Fan 4 + {}, + {}, + {}, + {}, + {}, +}; + +static struct device_info psu_information[PSU_COUNT + 1] = { + {"unknown", "unknown"}, //check + {}, //PSU 1 + {}, //PSU 2 +}; + +void update_shm_mem(void) +{ + (void)fill_shared_memory(ONLP_SENSOR_CACHE_SHARED, ONLP_SENSOR_CACHE_SEM, ONLP_SENSOR_CACHE_FILE); + (void)fill_shared_memory(ONLP_FRU_CACHE_SHARED, ONLP_FRU_CACHE_SEM, ONLP_FRU_CACHE_FILE); + (void)fill_shared_memory(ONLP_SENSOR_LIST_CACHE_SHARED, ONLP_SENSOR_LIST_SEM, ONLP_SENSOR_LIST_FILE); +} + +int is_cache_exist() +{ + const char *sdr_cache_path="/tmp/onlp-sensor-list-cache.txt"; + const char *fru_cache_path="/tmp/onlp-fru-cache.txt"; + const char *time_setting_path="/var/opt/interval_time.txt"; + time_t current_time; + int interval_time = 30; //set default to 30 sec + double sdr_diff_time,fru_diff_time; + struct stat sdr_fst,fru_fst; + bzero(&sdr_fst,sizeof(sdr_fst)); + bzero(&fru_fst,sizeof(fru_fst)); + + //Read setting + if (access(time_setting_path, F_OK) == -1) { //Setting not exist + return -1; + } else { + FILE *fp; + + fp = fopen(time_setting_path, "r"); // read setting + + if (fp == NULL) { + perror("Error while opening the file.\n"); + exit(EXIT_FAILURE); + } + + fscanf(fp,"%d", &interval_time); + + fclose(fp); + } + + if ((access(sdr_cache_path, F_OK) == -1) && (access(fru_cache_path, F_OK) == -1)) //Cache not exist + return -1; + else { //Cache exist + current_time = time(NULL); + if (stat(sdr_cache_path,&sdr_fst) != 0) { + printf("stat() sdr_cache failed\n"); + return -1; + } + if (stat(fru_cache_path,&fru_fst) != 0) { + printf("stat() fru_cache failed\n"); + return -1; + } + + sdr_diff_time = difftime(current_time,sdr_fst.st_mtime); + fru_diff_time = difftime(current_time,fru_fst.st_mtime); + + if ((sdr_diff_time > interval_time)&&(fru_diff_time > interval_time)) + return -1; + + return 1; + } +} + +int is_shm_mem_ready() +{ + if (USE_SHM_METHOD) { + const char *sdr_cache_path="/run/shm/onlp-sensor-list-cache-shared"; + const char *fru_cache_path="/run/shm/onlp-fru-cache-shared"; + + if (access(fru_cache_path, F_OK) == -1 || access(sdr_cache_path, F_OK) == -1 ) //Shared cache files not exist + return 0; + + return 1; + } + + return 0; +} + +int create_cache() +{ + (void)system("ipmitool fru > /tmp/onlp-fru-cache.tmp; sync; rm -f /tmp/onlp-fru-cache.txt; mv /tmp/onlp-fru-cache.tmp /tmp/onlp-fru-cache.txt"); + (void)system("ipmitool sensor list > /tmp/onlp-sensor-list-cache.tmp; sync; rm -f /tmp/onlp-sensor-list-cache.txt; mv /tmp/onlp-sensor-list-cache.tmp /tmp/onlp-sensor-list-cache.txt"); + if (USE_SHM_METHOD) + update_shm_mem(); + + return 1; +} + +char *read_tmp_cache(char *cmd, char *cache_file_path) +{ + FILE* pFd = NULL; + char *str = NULL; + int round = 1; + + for (round = 1; round <= 10; round++) { + pFd = fopen(cache_file_path, "r"); + if (pFd != NULL ) { + + struct stat st; + + stat(cache_file_path, &st); + + int size = st.st_size; + + str = (char *)malloc(size + 1); + memset (str, 0, size+1); + fread(str, size+1, 1, pFd); + fclose(pFd); + break; + } else { + usleep(5000); //Sleep for 5 Microsec for waiting the file operation complete + } + } + + if (round >= 10 && str == NULL) { + str = (char *)malloc(1); + memset (str, 0, 1); + } + + return str; +} + +int get_psu_info_wbmc(int id, int *mvin, int *mvout, int *mpin, int *mpout, int *miin, int *miout) +{ + char *tmp = (char *)NULL; + int len = 0; + // int index = 0; + + int i = 0; + int ret = 0; + char strTmp[12][128] = {{0}, {0}}; + char command[256]; + char *token = NULL; + char *psu_sensor_name[12] = { + "PSU1_VIN", "PSU1_CIN", "PSU1_PIN", "PSU1_VOUT", + "PSU1_COUT", "PSU1_POUT", "PSU2_VIN", "PSU2_CIN", + "PSU2_PIN", "PSU2_VOUT","PSU2_COUT","PSU2_POUT"}; + + /* + String example: + root@localhost:~# ipmitool sensor list | grep PSU + PSU1_Status | 0x0 | discrete | 0x0180| na | na | na | na | na | na + PSU2_Status | 0x0 | discrete | 0x0180| na | na | na | na | na | na + PSU1_Fan | na | RPM | na | na | na | na | na | na | na + PSU2_Fan | 15800.000 | RPM | ok | na | na | na | na | na | na + PSU1_VIn | na | Volts | na | na 0 | na | na | 239.800 | 264.000 | na + PSU1_CIn | na | Amps | na | na 1 | na | na | na | 14.080 | na + PSU1_PIn | na | Watts | na | na 2 | na | na | na | 1500.000 | na + PSU1_Temp1 | na | degrees C | na | na | na | na | na | na | na + PSU1_Temp2 | na | degrees C | na | na | na | na | na | na | na + PSU1_VOut | na | Volts | na | na 3 | na | na | na | 13.500 | 15.600 + PSU1_COut | na | Amps | na | na 4 | na | na | na | 125.000 | na + PSU1_POut | na | Watts | na | na 5 | na | na | na | 1500.000 | na + PSU2_VIn | 228.800 | Volts | ok | na 6 | na | na | 239.800 | 264.000 | na + PSU2_CIn | 0.480 | Amps | ok | na 7 | na | na | na | 14.080 | na + PSU2_PIn | 114.000 | Watts | ok | na 8 | na | na | na | 1500.000 | na + PSU2_Temp1 | 26.000 | degrees C | ok | na | na | na | na | na | na + PSU2_Temp2 | 43.000 | degrees C | ok | na | na | na | na | na | na + PSU2_VOut | 12.000 | Volts | ok | na 9 | na | na | na | 13.500 | 15.600 + PSU2_COut | 7.500 | Amps | ok | na 10 | na | na | na | 125.000 | na + PSU2_POut | 90.000 | Watts | ok | na 11 | na | na | na | 1500.000 | na + root@localhost:~# + */ + if ((NULL == mvin) || (NULL == mvout) ||(NULL == mpin) || (NULL == mpout) || (NULL == miin) || (NULL == miout)) { + printf("%s null pointer!\n", __FUNCTION__); + return -1; + } + + if (is_shm_mem_ready()) { + ret = open_file(ONLP_SENSOR_LIST_CACHE_SHARED,ONLP_SENSOR_LIST_SEM, &tmp, &len); + if (ret < 0 || !tmp) { + printf("Failed - Failed to obtain system information\n"); + (void)free(tmp); + tmp = (char *)NULL; + return ret; + } + } else { + // use unsafe method to read the cache file. + sprintf(command, "cat %s",ONLP_SENSOR_LIST_FILE); + tmp = read_tmp_cache(command,ONLP_SENSOR_LIST_FILE); + } + + char *content, *temp_pointer; + int flag = 0; + content = strtok_r(tmp, "\n", &temp_pointer); + + int search_from = 0; + int search_to = 0; + + if (id == 1) { + search_from = 0; + search_to = 5; + } else { + search_from = 6; + search_to = 11; + } + + while (content != NULL && search_from <= search_to) { + if (strstr(content, psu_sensor_name[search_from])) + { + flag = 1; + // index++; + } + + if (flag == 1) { + i = 0; + /* the 1st token is PSU2_VIn */ + token = strtok(content, "|"); + while ( token != NULL ) + { + if (i == 1) { + /* get 228.800 as strTmp[search_from][1] from "PSU2_VIn | 228.800" */ + array_trim(token, &strTmp[search_from][0]); + search_from++; + } + i++; + if (i > 2) break; + /* the 2nd token is 228.800 and i is 1 */ + token = strtok(NULL, "|"); + } + } + + + flag = 0; + content = strtok_r(NULL, "\n", &temp_pointer); + } + + if (content) { + content = (char *)NULL; + } + if (temp_pointer) { + temp_pointer = (char *)NULL; + } + if (tmp) { + (void)free(tmp); + tmp = (char *)NULL; + } + + if (id == 1) + { + if (0 == strcmp(&strTmp[0][0], "na")) + *mvin = 0; + else + *mvin = atof(&strTmp[0][0]) * 1000.0; + + if (0 == strcmp(&strTmp[1][0], "na")) + *miin = 0; + else + *miin = atof(&strTmp[1][0]) * 1000.0; + + if (0 == strcmp(&strTmp[2][0], "na")) + *mpin = 0; + else + *mpin = atof(&strTmp[2][0]) * 1000.0; + + if (0 == strcmp(&strTmp[3][0], "na")) + *mvout = 0; + else + *mvout = atof(&strTmp[3][0]) * 1000.0; + + if (0 == strcmp(&strTmp[4][0], "na")) + *miout = 0; + else + *miout = atof(&strTmp[4][0]) * 1000.0; + + if (0 == strcmp(&strTmp[5][0], "na")) + *mpout = 0; + else + *mpout = atof(&strTmp[5][0]) * 1000.0; + } + else + { + if (0 == strcmp(&strTmp[6][0], "na")) + *mvin = 0; + else + *mvin = atof(&strTmp[6][0]) * 1000.0; + + if (0 == strcmp(&strTmp[7][0], "na")) + *miin = 0; + else + *miin = atof(&strTmp[7][0]) * 1000.0; + + if (0 == strcmp(&strTmp[8][0], "na")) + *mpin = 0; + else + *mpin = atof(&strTmp[8][0]) * 1000.0; + + if (0 == strcmp(&strTmp[9][0], "na")) + *mvout = 0; + else + *mvout = atof(&strTmp[9][0]) * 1000.0; + + if (0 == strcmp(&strTmp[10][0], "na")) + *miout = 0; + else + *miout = atof(&strTmp[10][0]) * 1000.0; + + if (0 == strcmp(&strTmp[11][0], "na")) + *mpout = 0; + else + *mpout = atof(&strTmp[11][0]) * 1000.0; + } + + return ret; +} + +int get_psu_model_sn_wbmc(int id, char *model, char *serial_number) +{ + int index; + char *token; + char *tmp = (char *)NULL; + int len = 0; + int ret = -1; + int search_psu_id = 1; + char command[256]; + + if (0 == strcasecmp(psu_information[0].model, "unknown")) { + + index = 0; + if (is_shm_mem_ready()) { + ret = open_file(ONLP_FRU_CACHE_SHARED,ONLP_FRU_CACHE_SEM, &tmp, &len); + if (ret < 0 || !tmp) { + printf("Failed - Failed to obtain system information\n"); + (void)free(tmp); + tmp = (char *)NULL; + return ret; + } + } else { + // use unsafe method to read the cache file. + sprintf(command, "cat %s",ONLP_FRU_CACHE_FILE); + tmp = read_tmp_cache(command,ONLP_FRU_CACHE_FILE); + } + + char *content, *temp_pointer; + int flag = 0; + /* + String example: + root@localhost:~# ipmitool fru (Pull out PSU1) + + FRU Device Description : FRU_PSU1 (ID 3) + Product Manufacturer : DELTA + Product Name : TDPS2000LB A + Product Part Number : TDPS2000LB A + Product Version : S2F + Product Serial : JJLT2248000182 + */ + content = strtok_r(tmp, "\n", &temp_pointer); + + while (content != NULL) { + if (strstr(content, "FRU Device Description : FRU_PSU")) { + flag = 1; + index++; + } + if (flag == 1) { + if (strstr(content, "Device not present")) { + flag=0; + } + else if (strstr(content, "Product Name")) { + strtok(content, ":"); + token = strtok(NULL, ":"); + char* trim_token = trim(token); + sprintf(psu_information[index].model,"%s",trim_token); + } + else if (strstr(content, "Product Serial")) { + strtok(content, ":"); + token = strtok(NULL, ":"); + char* trim_token = trim(token); + sprintf(psu_information[index].serial_number,"%s",trim_token); + flag = 0; + search_psu_id++; + } + } + if (search_psu_id > PSU_COUNT) { + content = NULL; + } else { + content = strtok_r(NULL, "\n", &temp_pointer); + } + } + + sprintf(psu_information[0].model,"pass"); //Mark as complete + + if (temp_pointer) { + temp_pointer = (char *)NULL; + } + if (tmp) { + (void)free(tmp); + tmp = (char *)NULL; + } + } + + strcpy(model, psu_information[id].model); + strcpy(serial_number, psu_information[id].serial_number); + + return 1; +} + +int get_fan_info_wbmc(int id, char *model, char *serial, int *airflow) +{ + int index; + char *token; + char *tmp = (char *)NULL; + int len = 0; + int ret = -1; + char command[256]; + + if (0 == strcasecmp(fan_information[0].model, "unknown")) { + index = 0; + if (is_shm_mem_ready()) { + ret = open_file(ONLP_FRU_CACHE_SHARED,ONLP_FRU_CACHE_SEM, &tmp, &len); + if (ret < 0 || !tmp) { + printf("Failed - Failed to obtain system information\n"); + (void)free(tmp); + tmp = (char *)NULL; + return ret; + } + } else { + // use unsafe method to read the cache file. + sprintf(command, "cat %s",ONLP_FRU_CACHE_FILE); + tmp = read_tmp_cache(command,ONLP_FRU_CACHE_FILE); + } + /* + String example: + root@localhost:~# ipmitool fru (Pull out FAN1) + + FRU Device Description : FRU_FAN1 (ID 6) + Device not present (Unknown (0x81)) + + FRU Device Description : FRU_FAN2 (ID 7) + Board Mfg Date : Sat Apr 6 10:26:00 2019 + Board Mfg : Celestica + Board Product : Fan Board 2 + Board Serial : R1141-F0018-01GD0119170163 + Board Part Number : R1141-F0018-01 + Board Extra : Mt.Echo-Fan + Board Extra : 02 + Board Extra : F2B + + . + . + + FRU Device Description : FRU_FAN7 (ID 12) + Board Mfg Date : Sat Apr 6 10:26:00 2019 + Board Mfg : Celestica + Board Product : Fan Board 7 + Board Serial : R1141-F0018-01GD0119170165 + Board Part Number : R1141-F0018-01 + Board Extra : Mt.Echo-Fan + Board Extra : 02 + Board Extra : F2B + + */ + char *content, *temp_pointer; + int flag = 0; + content = strtok_r(tmp, "\n", &temp_pointer); + + while (content != NULL) { + if (strstr(content, "FRU_FAN") && !strstr(content,"FRU_FANBRD")) { + flag = 1; + index++; + } + if (flag == 1) { + if (strstr(content, "Device not present")) { + flag=0; + } + else if (strstr(content, "Board Serial")) { + strtok(content, ":"); + token = strtok(NULL, ":"); + char* trim_token = trim(token); + sprintf(fan_information[index].serial_number,"%s",trim_token); + } + else if (strstr(content, "Board Part Number")) { + strtok(content, ":"); + token = strtok(NULL, ":"); + char* trim_token = trim(token); + sprintf(fan_information[index].model,"%s",trim_token); + + } else if (strstr(content, "Board Extra")) + { + strtok(content, ":"); + token = strtok(NULL, ":"); + char* trim_token = trim(token); + //Check until find B2F or F2B + if (strcmp(trim_token, "B2F") == 0) { + fan_information[index].airflow = FAN_B2F; + flag = 0; + } else if (strcmp(trim_token ,"F2B") == 0) { + fan_information[index].airflow = FAN_F2B; + flag = 0; + } + + } + + } + if (index > FAN_COUNT) { + content = NULL; + } else { + content = strtok_r(NULL, "\n", &temp_pointer); + } + } + + sprintf(fan_information[0].model,"pass"); //Mark as complete + + if (temp_pointer) { + temp_pointer = (char *)NULL; + } + if (tmp) { + (void)free(tmp); + tmp = (char *)NULL; + } + } + strcpy(model, fan_information[id].model); + strcpy(serial, fan_information[id].serial_number); + *airflow = fan_information[id].airflow; + + return 1; +} + +int get_sensor_info_wbmc(int id, int *temp, int *warn, int *error, int *shutdown) +{ + char *tmp = (char *)NULL; + int len = 0; + int index = 0; + int i = 0; + int ret = 0; + char strTmp[10][128] = {{0}, {0}}; + char *token = NULL; + char command[256]; + + char *Thermal_sensor_name[24] = { + "TEMP_CPU", "TEMP_DIMMA0", "TEMP_DIMMB0", "TEMP_SW_Internal", + "BMC_Temp", "TEMP_BB_U3", "TEMP_SW_U15", "TEMP_SW_U17", + "TEMP_SW_U16", "TEMP_SW_U13", "TEMP_SW_U11", "TEMP_SW_U12", + "TEMP_FCB_U52", "TEMP_FCB_U17", "MP5023_T", "VDD_CORE_T", + "XP3R3V_LEFT_T", "XP3R3V_RIGHT_T", "PSU1_TEMP1", "PSU1_TEMP2", + "PSU1_TEMP3", "PSU2_TEMP1", "PSU2_TEMP2", "PSU2_TEMP3" }; + + if ((NULL == temp) || (NULL == warn) || (NULL == error) || (NULL == shutdown)) + { + printf("%s null pointer!\n", __FUNCTION__); + return -1; + } + + /* + String example: + ipmitool sensor list + TEMP_CPU | 1.000 | degrees C | ok | 5.000 | 9.000 | 16.000 | 65.000 | 73.000 | 75.606 + TEMP_FCB_U52 | 32.000 | degrees C | ok | na | na | na | na | 70.000 | 75.000 + PSUR_Temp1 | na | degrees C | na | na | na | na | na | na | na + */ + if (is_shm_mem_ready()) { + ret = open_file(ONLP_SENSOR_LIST_CACHE_SHARED,ONLP_SENSOR_LIST_SEM, &tmp, &len); + if (ret < 0 || !tmp) { + printf("Failed - Failed to obtain system information\n"); + (void)free(tmp); + tmp = (char *)NULL; + return ret; + } + } else { + // use unsafe method to read the cache file. + sprintf(command, "cat %s",ONLP_SENSOR_LIST_FILE); + tmp = read_tmp_cache(command,ONLP_SENSOR_LIST_FILE); + } + + char *content, *temp_pointer; + int flag = 0; + content = strtok_r(tmp, "\n", &temp_pointer); + while (content != NULL) { + if (strstr(content, Thermal_sensor_name[id - 1])) { + flag = 1; + index++; + } + if (flag == 1) { + + i = 0; + token = strtok(content, "|"); + while ( token != NULL ) + { + /* strTmp[0]-"TEMP_CPU", strTmp[1]-1.000,... */ + array_trim(token, &strTmp[i][0]); + i++; + if (i > 10) + break; + token = strtok(NULL, "|"); + } + + flag = 3; + } + + if (flag == 3) { + content = NULL; + } else { + content = strtok_r(NULL, "\n", &temp_pointer); + } + } + + if (0 == strcmp(&strTmp[1][0], "na")) + *temp = 0; + else + *temp = atof(&strTmp[1][0]) * 1000.0; + + if (0 == strcmp(&strTmp[7][0], "na")) + *warn = 0; + else + *warn = atof(&strTmp[7][0]) * 1000.0; + + if (0 == strcmp(&strTmp[8][0], "na")) + *error = 0; + else + *error = atof(&strTmp[8][0]) * 1000.0; + + if (0 == strcmp(&strTmp[9][0], "na")) + *shutdown = 0; + else + *shutdown = atof(&strTmp[9][0]) * 1000.0; + + if (content) { + content = (char *)NULL; + } + if (temp_pointer) { + temp_pointer = (char *)NULL; + } + if (tmp) { + (void)free(tmp); + tmp = (char *)NULL; + } + + return 0; +} + +int get_fan_speed_wbmc(int id,int *per, int *rpm) +{ + char *tmp = (char *)NULL; + char retstr[5] = {0}; + int len = 0; + int i = 0; + int ret = 0; + char strTmp[2][128] = {{0}, {0}}; + char *token = NULL; + char command[256]; + + *per = *rpm = 0; + + char *Fan_sensor_name[9] = { + "Fan1_Front", "Fan2_Front", "Fan3_Front", "Fan4_Front", + "Fan5_Front", "Fan6_Front", "Fan7_Front","PSU1_FAN","PSU2_FAN"}; + + if ((NULL == per) || (NULL == rpm)) + { + printf("%s null pointer!\n", __FUNCTION__); + return -1; + } + + /* + String example: + ipmitool sensor list (Plug out FAN1 and PSU 1) + Fan1_Front | na | RPM | na | na | 1050.000 | na | na | na | na + Fan2_Front | 28650.000 | RPM | ok | na | 1050.000 | na | na | na | na + Fan3_Front | 29250.000 | RPM | ok | na | 1050.000 | na | na | na | na + Fan4_Front | 28650.000 | RPM | ok | na | 1050.000 | na | na | na | na + Fan5_Front | 29400.000 | RPM | ok | na | 1050.000 | na | na | na | na + Fan6_Front | 29100.000 | RPM | ok | na | 1050.000 | na | na | na | na + Fan7_Front | 29100.000 | RPM | ok | na | 1050.000 | na | na | na | na + PSU1_FAN | na | RPM | na | na | na | na | na | na | na + PSU2_FAN | 15800.000 | RPM | ok | na | na | na | na | na | na + */ + if (is_shm_mem_ready()) { + ret = open_file(ONLP_SENSOR_LIST_CACHE_SHARED,ONLP_SENSOR_LIST_SEM, &tmp, &len); + if (ret < 0 || !tmp) { + printf("Failed - Failed to obtain system information\n"); + (void)free(tmp); + tmp = (char *)NULL; + return ret; + } + } else { + // use unsafe method to read the cache file. + sprintf(command, "cat %s",ONLP_SENSOR_LIST_FILE); + tmp = read_tmp_cache(command,ONLP_SENSOR_LIST_FILE); + } + + char *content, *temp_pointer; + int flag = 0; + + content = strtok_r(tmp, "\n", &temp_pointer); + while (content != NULL) { + if (strstr(content, Fan_sensor_name[id - 1])) + flag = 1; + if (flag == 1) { + i = 0; + token = strtok(content, "|"); + while ( token != NULL ) { + array_trim(token, &strTmp[i][0]); + i++; + if (i > 2) break; + token = strtok(NULL, "|"); + } + + flag = 3; + } + + /* despite of FanX_Front, only use FanX_Rear as fan speed */ + if (flag == 3) + content = NULL; + else + content = strtok_r(NULL, "\n", &temp_pointer); + } + + if (0 == strcmp(&strTmp[1][0], "na")) { + *rpm = 0; + ret = -1; + } else + *rpm = atof(&strTmp[1][0]); + + if (0 == strcmp(&strTmp[1][0], "na")) + *per = 0; + else if (id == PSU1_FAN_ID) { + /** + * Greystone only supports F2B PSU TDPS2000LB A + * and it's speed is not linear, so obtain duty from mfr duty register 0xd1 + * while PMBUS duty register is 0x94 + */ + exec_cmd("ipmitool raw 0x3a 0x3e 16 0xB0 1 0xd1", retstr); + *per = strtol(retstr, NULL, 16); + } else if (id == PSU2_FAN_ID) { + exec_cmd("ipmitool raw 0x3a 0x3e 17 0xB2 1 0xd1", retstr); + *per = strtol(retstr, NULL, 16); + } else + *per = (atof(&strTmp[1][0]) * 100 )/ CHASSIS_FAN_FRONT_MAX; + + if (content) { + content = (char *)NULL; + } + if (temp_pointer) { + temp_pointer = (char *)NULL; + } + if (tmp) { + (void)free(tmp); + tmp = (char *)NULL; + } + + return ret; +} + +int fill_shared_memory(const char *shm_path, const char *sem_path, const char *cache_path) +{ + int seg_size = 0; + int shm_fd = -1; + struct shm_map_data * shm_map_ptr = (struct shm_map_data *)NULL; + + if (!shm_path || !sem_path || !cache_path) { + return -1; + } + + seg_size = sizeof(struct shm_map_data); + + shm_fd = shm_open(shm_path, O_CREAT|O_RDWR, S_IRWXU|S_IRWXG); + if (shm_fd < 0) { + printf("\nshm_path:%s. errno:%d\n", shm_path, errno); + return -1; + } + + ftruncate(shm_fd, seg_size); + + shm_map_ptr = (struct shm_map_data *)mmap(NULL, seg_size, PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd, 0); + if (shm_map_ptr == MAP_FAILED) { + printf("\nMAP_FAILED. errno:%d.\n", errno); + close(shm_fd); + return -1; + } + + if (access(cache_path, F_OK) == -1) { + munmap(shm_map_ptr, seg_size); + close(shm_fd); + return -1; + } + + struct stat sta; + stat(cache_path, &sta); + int st_size = sta.st_size; + if (st_size == 0) { + munmap(shm_map_ptr, seg_size); + close(shm_fd); + return -1; + } + + char *cache_buffer = (char *)malloc(st_size); + if (!cache_buffer) { + munmap(shm_map_ptr, seg_size); + close(shm_fd); + return -1; + } + + memset(cache_buffer, 0, st_size); + + FILE *cache_fp = fopen(cache_path, "r"); + if (!cache_fp) { + free(cache_buffer); + munmap(shm_map_ptr, seg_size); + close(shm_fd); + return -1; + } + + int cache_len = fread(cache_buffer, 1, st_size, cache_fp); + if (st_size != cache_len) { + munmap(shm_map_ptr, seg_size); + close(shm_fd); + free(cache_buffer); + fclose(cache_fp); + return -1; + } + + sem_t * sem_id = sem_open(sem_path, O_CREAT, S_IRUSR | S_IWUSR, 1); + if (sem_id == SEM_FAILED) { + munmap(shm_map_ptr, seg_size); + close(shm_fd); + free(cache_buffer); + fclose(cache_fp); + return -1; + } + + sem_wait(sem_id); + + memcpy(shm_map_ptr->data, cache_buffer, st_size); + + shm_map_ptr->size = st_size; + + sem_post(sem_id); + + (void)free(cache_buffer); + + sem_close(sem_id); + + munmap(shm_map_ptr, seg_size); + + close(shm_fd); + fclose(cache_fp); + + return 0; +} + +int dump_shared_memory(const char *shm_path, const char *sem_path, struct shm_map_data *shared_mem) +{ + sem_t *sem_id = (sem_t *)NULL; + struct shm_map_data *map_ptr = (struct shm_map_data *)NULL; + int seg_size = 0; + int shm_fd = -1; + + if (!shm_path || !sem_path || !shared_mem) { + return -1; + } + + seg_size = sizeof(struct shm_map_data); + + + shm_fd = shm_open(shm_path, O_RDONLY, 0666); + if (shm_fd < 0) { + return -1; + } + + map_ptr = (struct shm_map_data *)mmap(NULL, seg_size, PROT_READ, MAP_SHARED, shm_fd, 0); + if (map_ptr == MAP_FAILED) { + close(shm_fd); + return -1; + } + + sem_id = sem_open(sem_path, 0); + if (SEM_FAILED == sem_id) { + munmap(map_ptr, seg_size); + close(shm_fd); + return -1; + } + + sem_wait(sem_id); + + memcpy(shared_mem, map_ptr, sizeof(struct shm_map_data)); + + sem_post(sem_id); + + sem_close(sem_id); + + munmap(map_ptr, seg_size); + close(shm_fd); + + return 0; +} + +int open_file(const char *shm_path, const char *sem_path, char **cache_data, int *cache_size) +{ + int res = -1; + char *tmp_ptr = (char *)NULL; + struct shm_map_data shm_map_tmp; + + memset(&shm_map_tmp, 0, sizeof(struct shm_map_data)); + + res = dump_shared_memory(shm_path, sem_path, &shm_map_tmp); + if (!res) { + tmp_ptr = malloc(shm_map_tmp.size); + if (!tmp_ptr) { + res = -1; + return res; + } + + memset(tmp_ptr, 0, shm_map_tmp.size); + + memcpy(tmp_ptr, shm_map_tmp.data, shm_map_tmp.size); + + *cache_data = tmp_ptr; + + *cache_size = shm_map_tmp.size; + } + + return res; +} diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/platform_wbmc.h b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/platform_wbmc.h new file mode 100644 index 0000000000..e0b018bb69 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/platform_wbmc.h @@ -0,0 +1,41 @@ +#ifndef _PLATFORM_WBMC_H_ +#define _PLATFORM_WBMC_H_ + +#define ONLP_SENSOR_CACHE_SHARED "/onlp-sensor-cache-shared" +#define ONLP_FRU_CACHE_SHARED "/onlp-fru-cache-shared" +#define ONLP_SENSOR_LIST_CACHE_SHARED "/onlp-sensor-list-cache-shared" + +#define ONLP_SENSOR_CACHE_SEM "/onlp-sensor-cache-sem" +#define ONLP_FRU_CACHE_SEM "/onlp-fru-cache-sem" +#define ONLP_SENSOR_LIST_SEM "/onlp-sensor-list-cache-sem" + +#define ONLP_SENSOR_CACHE_FILE "/tmp/onlp-sensor-cache.txt" +#define ONLP_FRU_CACHE_FILE "/tmp/onlp-fru-cache.txt" +#define ONLP_SENSOR_LIST_FILE "/tmp/onlp-sensor-list-cache.txt" +#define USE_SHM_METHOD 0 + +struct device_info{ + char serial_number[256]; + char model[256]; + int airflow; +}; + +struct shm_map_data{ + char data[16384]; + int size; +}; + +int exec_ipmitool_cmd(char *cmd, char *retd); +int get_psu_model_sn_wbmc(int id,char* model,char* serial_number); +int get_psu_info_wbmc(int id,int *mvin,int *mvout,int *mpin,int *mpout,int *miin,int *miout); +int get_fan_info_wbmc(int id,char* model,char* serial,int *get_fan_info); +int get_sensor_info_wbmc(int id, int *temp, int *warn, int *error, int *shutdown); +int get_fan_speed_wbmc(int id,int* per,int* rpm); +int dump_shared_memory(const char *shm_path, const char *sem_path, struct shm_map_data *shared_mem); +int fill_shared_memory(const char *shm_path, const char *sem_path, const char *cache_path); +int open_file(const char *shm_path, const char *sem_path, char **cache_data, int *cache_size); +int create_cache(); +void update_shm_mem(void); +int is_cache_exist(); + +#endif /* _PLATFORM_WBMC_H_ */ diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/platform_wobmc.c b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/platform_wobmc.c new file mode 100644 index 0000000000..d45584a698 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/platform_wobmc.c @@ -0,0 +1,1580 @@ +////////////////////////////////////////////////////////////// +// PLATFORM FUNCTION TO INTERACT WITH SYS_CPLD AND OTHER DRIVERS // +////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform_comm.h" +#include "platform_wobmc.h" + +extern const struct psu_reg_bit_mapper psu_mapper [PSU_COUNT + 1]; +uint8_t Sys_Airflow = FAN_F2B; + +/* This array index should be the same as corresponding sensor ID */ +struct temp Temp_B2F[THERMAL_COUNT + 1]; + +struct temp Temp_F2B[THERMAL_COUNT + 1] = { + {}, + { + NOT_DEFINE, //CPU + NOT_DEFINE, + NOT_DEFINE, + 105000, + 105000, + NOT_DEFINE, + 0, + "TEMP_CPU", + "/sys/bus/platform/drivers/coretemp/coretemp.0/hwmon/hwmon*/temp1_input", + 0, + }, + { + NOT_DEFINE, //DIMMA0 + NOT_DEFINE, + NOT_DEFINE, + 85000, + 85000, + NOT_DEFINE, + 0, + "TEMP_DIMMA0", + "/sys/bus/i2c/devices/71-001a/hwmon/hwmon*/temp1_input", + 0, + }, + { + NOT_DEFINE, //DIMMB0 + NOT_DEFINE, + NOT_DEFINE, + 85000, + 85000, + NOT_DEFINE, + 0, + "TEMP_DIMMB0", + "/sys/bus/i2c/devices/71-0018/hwmon/hwmon*/temp1_input", + 0, + }, + { + NOT_DEFINE, //SWITCH INTERNAL + NOT_DEFINE, + NOT_DEFINE, + 105000, + 105000, + 110000, + 0, + "TEMP_SW_Internal", + "/sys/devices/platform/fpga-sys/temp_sw_internal", + 0, + }, + { + NOT_DEFINE, //BMC + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + 0, + "TEMP_BMC", + "na", + 0, + }, + { + NOT_DEFINE, //U3 + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + 0, + "TEMP_BB_U3", + "/sys/bus/i2c/devices/i2c-10/10-004e/hwmon/hwmon*/temp1_input", + 0, + }, + { + NOT_DEFINE, + NOT_DEFINE, //U15 + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + 0, + "TEMP_SW_U15", + "/sys/bus/i2c/devices/i2c-55/55-0048/hwmon/hwmon*/temp1_input", + 0, + }, + { + NOT_DEFINE, + NOT_DEFINE, //U17 + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + 0, + "TEMP_SW_U17", + "/sys/bus/i2c/devices/i2c-56/56-0049/hwmon/hwmon*/temp1_input", + 0, + }, + { + NOT_DEFINE, //U16 + NOT_DEFINE, + NOT_DEFINE, + 59000, + 59000, + NOT_DEFINE, + 0, + "TEMP_SW_U16", + "/sys/bus/i2c/devices/i2c-57/57-004a/hwmon/hwmon*/temp1_input", + 0, + }, + { + NOT_DEFINE, //U13 + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + 0, + "TEMP_SW_U13", + "/sys/bus/i2c/devices/i2c-58/58-004b/hwmon/hwmon*/temp1_input", + 0, + }, + { + NOT_DEFINE, //U11 + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + 0, + "TEMP_SW_U11", + "/sys/bus/i2c/devices/i2c-59/59-004c/hwmon/hwmon*/temp1_input", + 0, + }, + { + NOT_DEFINE, //U12 + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + 0, + "TEMP_SW_U12", + "/sys/bus/i2c/devices/i2c-60/60-004d/hwmon/hwmon*/temp1_input", + 0, + }, + { + NOT_DEFINE, //U52 + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + 0, + "TEMP_FB_U52", + "/sys/bus/i2c/devices/i2c-70/70-0048/hwmon/hwmon*/temp1_input", + 0, + }, + { + NOT_DEFINE, //U17 + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + 0, + "TEMP_FB_U17", + "/sys/bus/i2c/devices/i2c-70/70-0049/hwmon/hwmon*/temp1_input", + 0, + }, + { + NOT_DEFINE, //MP5023 + NOT_DEFINE, + NOT_DEFINE, + 135000, + 135000, + 135000, + 0, + "MP5023_T", + "/sys/bus/i2c/devices/i2c-8/8-0040/hwmon/hwmon*/temp1_input", + 0, + }, + { + NOT_DEFINE, //VDD_CORE + NOT_DEFINE, + NOT_DEFINE, + 135000, + 135000, + 135000, + 0, + "VDD_CORE_T", + "/sys/bus/i2c/devices/i2c-9/9-0020/hwmon/hwmon*/temp1_input", + 0, + }, + { + NOT_DEFINE, //XP3R3V_LEFT + NOT_DEFINE, + NOT_DEFINE, + 135000, + 135000, + 135000, + 0, + "XP3R3V_LEFT_T", + "/sys/bus/i2c/devices/i2c-9/9-0070/hwmon/hwmon*/temp1_input", + 0, + }, + { + NOT_DEFINE, //XP3R3V_RIGHT + NOT_DEFINE, + NOT_DEFINE, + 135000, + 135000, + 135000, + 0, + "XP3R3V_RIGHT_T", + "/sys/bus/i2c/devices/i2c-9/9-0076/hwmon/hwmon*/temp1_input", + 0, + }, + { + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + 60000, + 60000, + NOT_DEFINE, + 0, + "PSU1_TEMP1", + "/sys/bus/i2c/devices/i2c-47/47-0058/hwmon/hwmon*/temp1_input", + 0, + }, + { + NOT_DEFINE, //PSU1_TEMP2 + NOT_DEFINE, + NOT_DEFINE, + 79000, + 79000, + NOT_DEFINE, + 0, + "PSU1_TEMP2", + "/sys/bus/i2c/devices/i2c-47/47-0058/hwmon/hwmon*/temp2_input", + 0, + }, + { + NOT_DEFINE, //PSU1_TEMP3 + NOT_DEFINE, + NOT_DEFINE, + 78000, + 78000, + NOT_DEFINE, + 0, + "PSU1_TEMP3", + "/sys/bus/i2c/devices/i2c-47/47-0058/hwmon/hwmon*/temp3_input", + 0, + }, + { + NOT_DEFINE, //PSU2_TEMP1 + NOT_DEFINE, + NOT_DEFINE, + 60000, + 60000, + NOT_DEFINE, + 0, + "PSU2_TEMP1", + "/sys/bus/i2c/devices/i2c-48/48-0059/hwmon/hwmon*/temp1_input", + 0, + }, + { + NOT_DEFINE, //PSU2_TEMP2 + NOT_DEFINE, + NOT_DEFINE, + 79000, + 79000, + NOT_DEFINE, + 0, + "PSU2_TEMP2", + "/sys/bus/i2c/devices/i2c-48/48-0059/hwmon/hwmon*/temp2_input", + 0, + }, + { + NOT_DEFINE, //PSU2_TEMP3 + NOT_DEFINE, + NOT_DEFINE, + 78000, + 78000, + NOT_DEFINE, + 0, + "PSU2_TEMP3", + "/sys/bus/i2c/devices/i2c-48/48-0059/hwmon/hwmon*/temp3_input", + 0, + } +}; + + +struct psu Psu[PSU_COUNT + 1] = { + {}, + { //PSU1 + "/sys/bus/i2c/devices/i2c-47/47-0058/hwmon/hwmon*/in1_input", + "/sys/bus/i2c/devices/i2c-47/47-0058/hwmon/hwmon*/in2_input", + "/sys/bus/i2c/devices/i2c-47/47-0058/hwmon/hwmon*/curr1_input", + "/sys/bus/i2c/devices/i2c-47/47-0058/hwmon/hwmon*/curr2_input", + "/sys/bus/i2c/devices/i2c-47/47-0058/hwmon/hwmon*/power1_input", + "/sys/bus/i2c/devices/i2c-47/47-0058/hwmon/hwmon*/power2_input", + "/sys/bus/i2c/devices/i2c-47/47-0058/hwmon/hwmon*/fan1_input", + "PSU1", + 0, + }, + { //PSU2 + "/sys/bus/i2c/devices/i2c-48/48-0059/hwmon/hwmon*/in1_input", + "/sys/bus/i2c/devices/i2c-48/48-0059/hwmon/hwmon*/in2_input", + "/sys/bus/i2c/devices/i2c-48/48-0059/hwmon/hwmon*/curr1_input", + "/sys/bus/i2c/devices/i2c-48/48-0059/hwmon/hwmon*/curr2_input", + "/sys/bus/i2c/devices/i2c-48/48-0059/hwmon/hwmon*/power1_input", + "/sys/bus/i2c/devices/i2c-48/48-0059/hwmon/hwmon*/power2_input", + "/sys/bus/i2c/devices/i2c-48/48-0059/hwmon/hwmon*/fan1_input", + "PSU2", + 0, + }, +}; + +struct fan Fan[FAN_COUNT + 1] = { + {}, + { + "/sys/bus/i2c/devices/i2c-11/11-000d/fan1_present", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan1_front_speed_rpm", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan1_rear_speed_rpm", + "/sys/bus/i2c/devices/i2c-11/11-000d/pwm1", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan1_direction", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan1_led", + "/sys/bus/i2c/devices/i2c-67/67-0050/eeprom", + "FAN1_SPEED", + 0, + }, + { + "/sys/bus/i2c/devices/i2c-11/11-000d/fan2_present", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan2_front_speed_rpm", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan2_rear_speed_rpm", + "/sys/bus/i2c/devices/i2c-11/11-000d/pwm2", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan2_direction", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan2_led", + "/sys/bus/i2c/devices/i2c-68/68-0050/eeprom", + "FAN2_SPEED", + 0, + }, + { + "/sys/bus/i2c/devices/i2c-11/11-000d/fan3_present", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan3_front_speed_rpm", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan3_rear_speed_rpm", + "/sys/bus/i2c/devices/i2c-11/11-000d/pwm3", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan3_direction", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan3_led", + "/sys/bus/i2c/devices/i2c-69/69-0050/eeprom", + "FAN3_SPEED", + 0, + }, + { + "/sys/bus/i2c/devices/i2c-11/11-000d/fan4_present", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan4_front_speed_rpm", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan4_rear_speed_rpm", + "/sys/bus/i2c/devices/i2c-11/11-000d/pwm4", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan4_direction", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan4_led", + "/sys/bus/i2c/devices/i2c-66/66-0050/eeprom", + "FAN4_SPEED", + 0, + }, + { + "/sys/bus/i2c/devices/i2c-11/11-000d/fan5_present", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan5_front_speed_rpm", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan5_rear_speed_rpm", + "/sys/bus/i2c/devices/i2c-11/11-000d/pwm5", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan5_direction", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan5_led", + "/sys/bus/i2c/devices/i2c-63/63-0050/eeprom", + "FAN5_SPEED", + 0, + }, + { + "/sys/bus/i2c/devices/i2c-11/11-000d/fan6_present", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan6_front_speed_rpm", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan6_rear_speed_rpm", + "/sys/bus/i2c/devices/i2c-11/11-000d/pwm6", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan6_direction", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan6_led", + "/sys/bus/i2c/devices/i2c-64/64-0050/eeprom", + "FAN6_SPEED", + 0, + }, + { + "/sys/bus/i2c/devices/i2c-11/11-000d/fan7_present", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan7_front_speed_rpm", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan7_rear_speed_rpm", + "/sys/bus/i2c/devices/i2c-11/11-000d/pwm7", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan7_direction", + "/sys/bus/i2c/devices/i2c-11/11-000d/fan7_led", + "/sys/bus/i2c/devices/i2c-65/65-0050/eeprom", + "FAN7_SPEED", + 0, + }, + { + "na", + "/sys/bus/i2c/devices/i2c-47/47-0058/hwmon/hwmon*/fan1_input", + "na", + "/sys/bus/i2c/devices/i2c-47/47-0058/hwmon/hwmon*/fan1_target", + "na", + "na", + "/sys/bus/i2c/devices/i2c-47/47-0050/eeprom", + "PSU1_Fan", + 0, + }, + { + "na", + "/sys/bus/i2c/devices/i2c-48/48-0059/hwmon/hwmon*/fan1_input", + "na", + "/sys/bus/i2c/devices/i2c-48/48-0059/hwmon/hwmon*/fan1_target", + "na", + "na", + "/sys/bus/i2c/devices/i2c-48/48-0051/eeprom", + "PSU2_Fan", + 0, + }, +}; + +struct vol_curr_pwr Vol_Curr_Pwr[POWER_COUNT + 1] = { + {}, + { + 90000, + NOT_DEFINE, + NOT_DEFINE, + 264000, + NOT_DEFINE, + NOT_DEFINE, + 0, + "PSU1_VIN", + "/sys/bus/i2c/devices/i2c-47/47-0058/hwmon/hwmon*/in1_input", + 0, + }, + { + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + 12500, + NOT_DEFINE, + NOT_DEFINE, + 0, + "PSU1_CIN", + "/sys/bus/i2c/devices/i2c-47/47-0058/hwmon/hwmon*/curr1_input", + 0, + }, + { + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + 2244000000, + NOT_DEFINE, + NOT_DEFINE, + 0, + "PSU1_PIN", + "/sys/bus/i2c/devices/i2c-47/47-0058/hwmon/hwmon*/power1_input", + 0, + }, + { + 11400, + NOT_DEFINE, + NOT_DEFINE, + 12600, + NOT_DEFINE, + NOT_DEFINE, + 0, + "PSU1_VOUT", + "/sys/bus/i2c/devices/i2c-47/47-0058/hwmon/hwmon*/in2_input", + 0, + }, + { + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + 166000, + NOT_DEFINE, + NOT_DEFINE, + 0, + "PSU1_COUT", + "/sys/bus/i2c/devices/i2c-47/47-0058/hwmon/hwmon*/curr2_input", + 0, + }, + { + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + 2040000000, + NOT_DEFINE, + NOT_DEFINE, + 0, + "PSU1_POUT", + "/sys/bus/i2c/devices/i2c-47/47-0058/hwmon/hwmon*/power2_input", + 0, + }, + { + 90000, + NOT_DEFINE, + NOT_DEFINE, + 264000, + NOT_DEFINE, + NOT_DEFINE, + 0, + "PSU2_VIN", + "/sys/bus/i2c/devices/i2c-48/48-0059/hwmon/hwmon*/in1_input", + 0, + }, + { + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + 12500, + NOT_DEFINE, + NOT_DEFINE, + 0, + "PSU2_CIN", + "/sys/bus/i2c/devices/i2c-48/48-0059/hwmon/hwmon*/curr1_input", + 0, + }, + { + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + 2244000000, + NOT_DEFINE, + NOT_DEFINE, + 0, + "PSU2_PIN", + "/sys/bus/i2c/devices/i2c-48/48-0059/hwmon/hwmon*/power1_input", + 0, + }, + { + 11400, + NOT_DEFINE, + NOT_DEFINE, + 12600, + NOT_DEFINE, + NOT_DEFINE, + 0, + "PSU2_VOUT", + "/sys/bus/i2c/devices/i2c-48/48-0059/hwmon/hwmon*/in2_input", + 0, + }, + { + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + 166000, + NOT_DEFINE, + NOT_DEFINE, + 0, + "PSU2_COUT", + "/sys/bus/i2c/devices/i2c-48/48-0059/hwmon/hwmon*/curr2_input", + 0, + }, + { + NOT_DEFINE, + NOT_DEFINE, + NOT_DEFINE, + 2040000000, + NOT_DEFINE, + NOT_DEFINE, + 0, + "PSU2_POUT", + "/sys/bus/i2c/devices/i2c-48/48-0059/hwmon/hwmon*/power2_input", + 0, + }, + { + 10800, + 10200, + NOT_DEFINE, + 13200, + 13800, + NOT_DEFINE, + 0, + "BB_XP12R0V", + "/sys/bus/i2c/devices/i2c-8/8-0035/hwmon/hwmon*/in1_input", + 0, + }, + { + 4500, + 4230, + NOT_DEFINE, + 5490, + 5730, + NOT_DEFINE, + 0, + "BB_XP5R0V", + "/sys/bus/i2c/devices/i2c-8/8-0035/hwmon/hwmon*/in2_input", + 0, + }, + { + 2960, + 2800, + NOT_DEFINE, + 3620, + 3800, + NOT_DEFINE, + 0, + "COME_XP3R3V", + "/sys/bus/i2c/devices/i2c-8/8-0035/hwmon/hwmon*/in3_input", + 0, + }, + { + 1640, + 1550, + NOT_DEFINE, + 2000, + 2090, + NOT_DEFINE, + 0, + "COME_XP1R82V", + "/sys/bus/i2c/devices/i2c-8/8-0035/hwmon/hwmon*/in4_input", + 0, + }, + { + 950, + 890, + NOT_DEFINE, + 1160, + 1210, + NOT_DEFINE, + 0, + "COME_XP1R05V", + "/sys/bus/i2c/devices/i2c-8/8-0035/hwmon/hwmon*/in5_input", + 0, + }, + { + 1530, + 1450, + NOT_DEFINE, + 1870, + 1960, + NOT_DEFINE, + 0, + "COME_XP1R7V", + "/sys/bus/i2c/devices/i2c-8/8-0035/hwmon/hwmon*/in6_input", + 0, + }, + { + 1080, + 1020, + NOT_DEFINE, + 1320, + 1380, + NOT_DEFINE, + 0, + "COME_XP1R2V", + "/sys/bus/i2c/devices/i2c-8/8-0035/hwmon/hwmon*/in7_input", + 0, + }, + { + 1170, + 1110, + NOT_DEFINE, + 1430, + 1500, + NOT_DEFINE, + 0, + "COME_XP1R3V", + "/sys/bus/i2c/devices/i2c-8/8-0035/hwmon/hwmon*/in8_input", + 0, + }, + { + 1350, + 1280, + NOT_DEFINE, + 1650, + 1730, + NOT_DEFINE, + 0, + "COME_XP1R5V", + "/sys/bus/i2c/devices/i2c-8/8-0035/hwmon/hwmon*/in9_input", + 0, + }, + { + 2240, + 2120, + NOT_DEFINE, + 2740, + 2860, + NOT_DEFINE, + 0, + "COME_XP2R5V", + "/sys/bus/i2c/devices/i2c-8/8-0035/hwmon/hwmon*/in10_input", + 0, + }, + { + 10800, + 10200, + NOT_DEFINE, + 13200, + 13800, + NOT_DEFINE, + 0, + "COME_XP12R0V", + "/sys/bus/i2c/devices/i2c-8/8-0035/hwmon/hwmon*/in11_input", + 0, + }, + { + 2960, + 2800, + NOT_DEFINE, + 3620, + 3800, + NOT_DEFINE, + 0, + "SSD_XP3R3V", + "/sys/bus/i2c/devices/i2c-8/8-0035/hwmon/hwmon*/in12_input", + 0, + }, + { + 4500, + 4260, + NOT_DEFINE, + 5490, + 5760, + NOT_DEFINE, + 0, + "XP5R0V_C_MON", + "/sys/bus/i2c/devices/i2c-8/8-0034/hwmon/hwmon*/in1_input", + 0, + }, + { + NOT_DEFINE, + NOT_DEFINE, + //2980, + //2800, + NOT_DEFINE, + 3620, + 3800, + NOT_DEFINE, + 0, + "XP3R3V_LEFT_MON", + "/sys/bus/i2c/devices/i2c-8/8-0034/hwmon/hwmon*/in2_input", + 0, + }, + { + 2980, + 2800, + NOT_DEFINE, + 3620, + 3800, + NOT_DEFINE, + 0, + "XP3R3V_RIGHT_MON", + "/sys/bus/i2c/devices/i2c-8/8-0034/hwmon/hwmon*/in3_input", + 0, + }, + { + 2980, + 2800, + NOT_DEFINE, + 3620, + 3800, + NOT_DEFINE, + 0, + "XP3R3V_FPGA_MON", + "/sys/bus/i2c/devices/i2c-8/8-0034/hwmon/hwmon*/in4_input", + 0, + }, + { + 1620, + 1530, + NOT_DEFINE, + 1980, + 2070, + NOT_DEFINE, + 0, + "XP1R8V_FPGA_MON", + "/sys/bus/i2c/devices/i2c-8/8-0034/hwmon/hwmon*/in5_input", + 0, + }, + { + 1080, + 1020, + NOT_DEFINE, + 1320, + 1380, + NOT_DEFINE, + 0, + "XP1R2V_FPGA_MON", + "/sys/bus/i2c/devices/i2c-8/8-0034/hwmon/hwmon*/in6_input", + 0, + }, + { + 900, + 850, + NOT_DEFINE, + 1100, + 1150, + NOT_DEFINE, + 0, + "XP1R0V_FPGA_MON", + "/sys/bus/i2c/devices/i2c-8/8-0034/hwmon/hwmon*/in7_input", + 0, + }, + { + 1620, + 1530, + NOT_DEFINE, + 1980, + 2070, + NOT_DEFINE, + 0, + "XP1R8V_AVDD_MON", + "/sys/bus/i2c/devices/i2c-8/8-0034/hwmon/hwmon*/in8_input", + 0, + }, + { + 810, + 770, + NOT_DEFINE, + 990, + 1040, + NOT_DEFINE, + 0, + "XP0R9V_TRVDD0", + "/sys/bus/i2c/devices/i2c-8/8-0034/hwmon/hwmon*/in9_input", + 0, + }, + { + 810, + 770, + NOT_DEFINE, + 990, + 1040, + NOT_DEFINE, + 0, + "XP0R9V_TRVDD1", + "/sys/bus/i2c/devices/i2c-8/8-0034/hwmon/hwmon*/in10_input", + 0, + }, + { + 680, + 640, + NOT_DEFINE, + 830, + 860, + NOT_DEFINE, + 0, + "XP0R75V_TRVDD0", + "/sys/bus/i2c/devices/i2c-8/8-0034/hwmon/hwmon*/in11_input", + 0, + }, + { + 680, + 640, + NOT_DEFINE, + 830, + 860, + NOT_DEFINE, + 0, + "XP0R75V_TRVDD1", + "/sys/bus/i2c/devices/i2c-8/8-0034/hwmon/hwmon*/in12_input", + 0, + }, + { + 680, + 640, + NOT_DEFINE, + 830, + 860, + NOT_DEFINE, + 0, + "XP0R75V_AVDD_MON", + "/sys/bus/i2c/devices/i2c-8/8-0034/hwmon/hwmon*/in13_input", + 0, + }, + { + 680, + 650, + NOT_DEFINE, + 960, + 1010, + NOT_DEFINE, + 0, + "VDD_CORE_MON", + "/sys/bus/i2c/devices/i2c-8/8-0034/hwmon/hwmon*/in14_input", + 0, + }, + { + 1080, + 1020, + NOT_DEFINE, + 1320, + 1380, + NOT_DEFINE, + 0, + "XP1R2V_VDDO_MON", + "/sys/bus/i2c/devices/i2c-8/8-0034/hwmon/hwmon*/in15_input", + 0, + }, + { + 1620, + 1500, + NOT_DEFINE, + 1980, + 2040, + NOT_DEFINE, + 0, + "XP1R8V_VDDO_MON", + "/sys/bus/i2c/devices/i2c-8/8-0034/hwmon/hwmon*/in16_input", + 0, + } +}; + + +int get_psu_info_wobmc(int id, int *mvin, int *mvout, int *mpin, int *mpout, int *miin, int *miout) +{ + int ret = 0, fail = 0; + long value = 0; + *mvin = *mvout = *mpin = *mpout = *miin = *miout = 0; + + if ((NULL == mvin) || (NULL == mvout) ||(NULL == mpin) || (NULL == mpout) || (NULL == miin) || (NULL == miout)) { + printf("%s null pointer!\n", __FUNCTION__); + return -1; + } + + check_psu_absent(); + if (1 == Psu[id].is_absent) { + return 0; + } + + /* voltage in and out */ + ret = get_sysnode_value(Psu[id].vin_path, &value); + if (ret < 0) + fail++; + else + *mvin = (int)value; + + ret = get_sysnode_value(Psu[id].vout_path, &value); + if (ret < 0) + fail++; + else + *mvout = (int)value; + + /* power in and out */ + ret = get_sysnode_value(Psu[id].pin_path, &value); + if (ret < 0) + fail++; + else + *mpin = (int)value; + + *mpin = *mpin / 1000; + ret = get_sysnode_value(Psu[id].pout_path, &value); + if (ret < 0) + fail++; + else + *mpout = (int)value; + + *mpout = *mpout / 1000; + /* current in and out*/ + ret = get_sysnode_value(Psu[id].cin_path, &value); + if (ret < 0) + fail++; + else + *miin = (int)value; + + ret = get_sysnode_value(Psu[id].cout_path, &value); + if (ret < 0) + fail++; + else + *miout = (int)value; + + return fail; +} + +int get_psu_model_sn_wobmc(int id, char *model, char *serial_number) +{ + unsigned char cblock[288] = {0}; + int ret = 0; + int bus = 0, address = 0; + int bank = 0; + + if (id == 1) { + bus = PSU1_I2C_BUS; + address = PSU1_I2C_ADDR; + } else { + bus = PSU2_I2C_BUS; + address = PSU2_I2C_ADDR; + } + bank = PMBUS_MODEL_REG; + ret = read_smbus_block(bus, address, bank, cblock); + if (ret > 0) + strcpy(model, (char *)cblock); + else + return ERR; + + bank = PMBUS_SERIAL_REG; + ret = read_smbus_block(bus, address, bank, cblock); + if (ret > 0) + strcpy(serial_number, (char *)cblock); + else + return ERR; + + return 0; +} + +int get_fan_info_wobmc(int id, char *model, char *serial, int *airflow) +{ + uint16_t offset = FRU_COMM_HDR_LEN + FRU_BRDINFO_MFT_TLV_STRT, skip_len = 0; + uint32_t count = 1; + uint8_t ret = 0; + char path[100] = {0}, data[100] = {0}; + + check_fan_absent(); + check_psu_absent(); + + if (1 == Fan[id].is_absent) { + return 0; + } + + strcpy(path, Fan[id].eeprom_path); + + ret = read_eeprom(path, offset, data, &count); + if (ret < 0) + return ret; + + skip_len = data[0] & TLV_LEN_MASK; // skip Board Manufacturer bytes + offset += skip_len + 1; + count = 1; + ret = read_eeprom(path, offset, data, &count); + if (ret < 0) + return ret; + + skip_len = data[0] & TLV_LEN_MASK; // skip Board Product Name + offset += skip_len + 1; + count = 1; + ret = read_eeprom(path, offset, data, &count); + if (ret < 0) + return ret; + + count = data[0] & TLV_LEN_MASK; // Board Serial Number bytes length + offset++; + if (count > sizeof(data)) { + printf("read count %d is larger than buffer size %ld\n", count, sizeof(data)); + return -1; + } + ret = read_eeprom(path, offset, data, &count); + if (ret < 0) + return ret; + + strcpy(serial, data); + memset(data, 0, sizeof(data)); + offset += count; + count = 1; + ret = read_eeprom(path, offset, data, &count); + if (ret < 0) + return ret; + + count = data[0] & TLV_LEN_MASK; // Board Part Number bytes length + offset++; + if (count > sizeof(data)) { + printf("read count %d is larger than buffer size %ld\n", count, sizeof(data)); + return -1; + } + ret = read_eeprom(path, offset, data, &count); + if (ret < 0) + return ret; + + strcpy(model, data); + + /* 0-B2F,1-F2B*/ + get_sysnode_value(Fan[id].dir_path, airflow); + + return 0; +} + + +int get_sensor_info_wobmc(int id, int *temp, int *warn, int *error, int *shutdown) +{ + int ret = 0; + + *temp = *warn = *error = *shutdown = 0; + + if ((NULL == temp) || (NULL == warn) || (NULL == error) || (NULL == shutdown)) { + printf("%s null pointer!\n", __FUNCTION__); + return -1; + } + + *warn = Temp_F2B[id].hi_warn; + *error = Temp_F2B[id].hi_err; + *shutdown = Temp_F2B[id].hi_shutdown; + ret = get_sysnode_value(Temp_F2B[id].path, temp); + if (ret < 0 || *temp == 0) + return -1; + + return 0; +} + +int get_fan_speed_wobmc(int id, int *per, int *rpm) +{ + char retstr[10]={0}; + int ret = 0; + int max_speed; + + *rpm = *per = 0; + + if ((NULL == per) || (NULL == rpm)) { + printf("%s null pointer!\n", __FUNCTION__); + return -1; + } + + ret = get_sysnode_value(Fan[id].frpm_path, rpm); + if (ret < 0 || *rpm == 0) + return -1; + else { + /** + * Greystone only supports F2B PSU TDPS2000LB A + * and it's speed is not linear, so obtain duty from mfr duty register 0xd1 + * while PMBUS duty register is 0x94 + */ + if (PSU1_FAN_ID == id) { + exec_cmd("i2cget -y -f 47 0x58 0xd1", retstr); + *per = strtol(retstr, NULL, 16);; + } else if (PSU2_FAN_ID == id) { + exec_cmd("i2cget -y -f 48 0x59 0xd1", retstr); + *per = strtol(retstr, NULL, 16);; + } else { + max_speed = CHASSIS_FAN_FRONT_MAX; + *per = *rpm * 100 / max_speed; + } + } + + return 0; +} + +/** + * @brief Set replaceable fan module LED color + * @param id fan module ID + * @param color green/off/amber + * @retval ERR fail to set LED + * @retval OK succeed to set LED + */ +static uint8_t set_fan_led(uint8_t id, char *color) +{ + int fd, rc = 0; + + fd = open(Fan[id].led_path, O_WRONLY); + if (fd < 0) { + printf("Fail to open the file: %s \n", Fan[id].led_path); + return ERR; + } + + rc = write(fd, color, strlen(color)); + if (rc != strlen(color)) { + printf("Write failed. count=%lu, rc=%d \n", strlen(color), rc); + close(fd); + return ERR; + } + + close(fd); + + return OK; +} + +/** + * @brief Check replaceable fan module fault status + * @retval fault Numbers of failed fans + */ +int check_fan_fault(void) +{ + int err = 0; // single fan error + int fault = 0; // total fan error errors + int ret = 0, i = 0; + long speed = 0, speed_old = 0, pwm = 0; + long low_thr, high_thr = 0; + + check_fan_absent(); + for (i = 1; i <= CHASSIS_FAN_COUNT; i++) { + if (1 == Fan[i].is_absent) { + set_fan_led(i, "off"); + continue; + } + + /* if fan rpm duty is out of tolerant scope, set as fault */ + ret = get_sysnode_value(Fan[i].pwm_path, (void *)&pwm); + if (ret) { + fault++; + printf("Can't get sysnode: %s\n", Fan[i].pwm_path); + set_fan_led(i, "amber"); + continue; + } + + low_thr = CHASSIS_FAN_FRONT_MAX * pwm * (100 - CHASIS_FAN_TOLERANT_PER) / 25500; + high_thr = CHASSIS_FAN_FRONT_MAX * pwm * (100 + CHASIS_FAN_TOLERANT_PER) / 25500; + + ret = get_sysnode_value(Fan[i].frpm_path, (void *)&speed); + if (ret) { + fault++; + printf("Can't get sysnode: %s\n", Fan[i].frpm_path); + set_fan_led(i, "amber"); + continue; + } + + /* Eliminate jitter */ + if (speed < low_thr || speed > high_thr) { + sleep(5); + speed_old = speed; + ret = get_sysnode_value(Fan[i].frpm_path, (void *)&speed); + if (ret) { + fault++; + printf("Can't get sysnode: %s\n", Fan[i].frpm_path); + set_fan_led(i, "amber"); + continue; + } + } + + if (speed < RPM_FAULT_DEF) { + if (0 == Fan[i].is_udminspd_ffault) + syslog(LOG_WARNING, "Fan%d front rpm %ld is less than %d, fault !!!\n", + i, speed, RPM_FAULT_DEF); + Fan[i].is_udminspd_ffault = 1; + err++; + fault++; + } + else { + Fan[i].is_udminspd_ffault = 0; + if (speed < low_thr && speed <= speed_old) { + if (0 == Fan[i].is_udspd_ffault) + syslog(LOG_WARNING, "Fan%d front rpm %ld is less than %ld at pwm %ld!!!\n", + i, speed, low_thr, pwm); + Fan[i].is_udspd_ffault = 1; + err++; + fault++; + } else { + Fan[i].is_udspd_ffault = 0; + } + + if (speed > high_thr && speed >= speed_old) { + if (0 == Fan[i].is_ovspd_ffault) + syslog(LOG_WARNING, "Fan%d front rpm %ld is larger than %ld at pwm %ld!!!\n", + i, speed, high_thr, pwm); + Fan[i].is_ovspd_ffault = 1; + err++; + fault++; + } + else { + Fan[i].is_ovspd_ffault = 0; + } + } + + /* if fan rpm duty is out of tolerant scope, set as fault */ + low_thr = CHASSIS_FAN_REAR_MAX * pwm * (100 - CHASIS_FAN_TOLERANT_PER) / 25500; + high_thr = CHASSIS_FAN_REAR_MAX * pwm * (100 + CHASIS_FAN_TOLERANT_PER) / 25500; + + ret = get_sysnode_value(Fan[i].rrpm_path, (void *)&speed); + if (ret) { + fault++; + printf("Can't get sysnode: %s\n", Fan[i].rrpm_path); + set_fan_led(i, "amber"); + continue; + } + + /* Eliminate jitter */ + if (speed < low_thr || speed > high_thr) { + sleep(5); + speed_old = speed; + ret = get_sysnode_value(Fan[i].rrpm_path, (void *)&speed); + if (ret) { + fault++; + printf("Can't get sysnode: %s\n", Fan[i].rrpm_path); + set_fan_led(i, "amber"); + continue; + } + } + + if (speed < RPM_FAULT_DEF) { + if (0 == Fan[i].is_udminspd_rfault) + syslog(LOG_WARNING, "Fan%d rear rpm %ld is less than %d, fault !!!\n", + i, speed, RPM_FAULT_DEF); + Fan[i].is_udminspd_rfault = 1; + err++; + fault++; + } + else { + Fan[i].is_udminspd_rfault = 0; + if (speed < low_thr && speed <= speed_old) { + if (0 == Fan[i].is_udspd_rfault) + syslog(LOG_WARNING, "Fan%d rear rpm %ld is less than %ld at pwm %ld!!!\n", + i, speed, low_thr, pwm); + Fan[i].is_udspd_rfault = 1; + err++; + fault++; + } + else { + Fan[i].is_udspd_rfault = 0; + } + + if (speed > high_thr && speed >= speed_old) { + if (0 == Fan[i].is_ovspd_rfault) + syslog(LOG_WARNING, "Fan%d rear rpm %ld is larger than %ld at pwm %ld!!!\n", + i, speed, high_thr, pwm); + Fan[i].is_ovspd_rfault = 1; + err++; + fault++; + } + else { + Fan[i].is_ovspd_rfault = 0; + } + } + + if (err > 0) { + set_fan_led(i, "amber"); + err = 0; + } + else { + set_fan_led(i, "green"); + } + } + + return fault; +} + +/** + * @brief Check replaceable PSU module fault status + * @retval fault Numbers of failed PSUs + */ +int check_psu_fault(void) +{ + uint8_t i = 0; + uint8_t psu_status = 0; + + int present_status = 0, ac_status = 0, pow_status = 0, alert_status = 0; + int fault = 0; + + for (i = 1; i <= PSU_COUNT; i++) + { + psu_status = get_psu_status(i); + present_status = (psu_status >> psu_mapper[i].bit_present) & 0x01; + alert_status = (psu_status >> psu_mapper[i].bit_alert) & 0x01; + ac_status = (psu_status >> psu_mapper[i].bit_ac_sta) & 0x01; + pow_status = (psu_status >> psu_mapper[i].bit_pow_sta) & 0x01; + + if (0 == present_status) + { + if (0 == ac_status || 0 == pow_status || 0 == alert_status) { + fault++; + } + + if (0 == alert_status) { + if (0 == Psu[i].is_alert) + syslog(LOG_WARNING, "PSU%d alert is detected !!!\n", i); + Psu[i].is_alert = 1; + } + else { + Psu[i].is_alert = 0; + } + + if (0 == ac_status) { + if (0 == Psu[i].is_ac_fault) + syslog(LOG_WARNING, "PSU%d AC power is detected failed !!!\n", i); + Psu[i].is_ac_fault = 1; + } + else { + Psu[i].is_ac_fault = 0; + } + + if (0 == pow_status) { + if (0 == Psu[i].is_power_fault) + syslog(LOG_WARNING, "PSU%d AC power is detected failed !!!\n", i); + Psu[i].is_power_fault = 1; + } + else { + Psu[i].is_power_fault = 0; + } + } + } + + return fault; +} + +/** + * @brief Check system airflow + * @retvla <0 error + */ +int check_sys_airflow(void) +{ + int rv; + onlp_sys_info_t si; + list_links_t *cur, *next; + + rv = onlp_sys_info_get(&si); + if(rv < 0) { + printf("Can't get onie data\n"); + return rv; + } + + LIST_FOREACH_SAFE(&si.onie_info.vx_list, cur, next) { + onlp_onie_vx_t* vx = container_of(cur, links, onlp_onie_vx_t); + if (0x2f == vx->data[0] && 0xd4 == vx->data[1] && 0xfb == vx->data[2]) { + Sys_Airflow = FAN_F2B; + } else if (0x2f == vx->data[0] && 0xd4 == vx->data[1] && 0xbf == vx->data[2]) { + Sys_Airflow = FAN_B2F; + } + } + + return 0; +} + +/** + * @brief Check replaceable fan module airflow + * @param sys_airflow System airflow + * @retval ERR error + * @retval fault The number of fans in the opposite direction to the system airflow + */ +int check_fan_airflow_fault(uint8_t sys_airflow) +{ + int ret = 0, i = 0; + long fan_airflow = 0; + int fault = 0; + + for (i = 1; i <= CHASSIS_FAN_COUNT; i++) { + ret = get_sysnode_value(Fan[i].dir_path, (void *)&fan_airflow); + if (ret) { + return ERR; + } + if (sys_airflow != fan_airflow) { + if (0 == Fan[i].is_airflow_fault) + syslog(LOG_WARNING, "Fan%d airflow is fault !!!\n", i); + Fan[i].is_airflow_fault = 1; + fault++; + } else { + Fan[i].is_airflow_fault = 0; + } + } + + return fault; +} + +/** + * @brief Check replaceable PSU module airflow + * @param sys_airflow System airflow + * @retval fault The number of PSUs in the opposite direction to the system airflow + */ +int check_psu_airflow_fault(uint8_t sys_airflow) +{ + int ret = 0, i = 0, psu_airflow = -1; + int fault = 0; + char model[30] = {0}; + char serial_number[30] = {0}; + + check_psu_absent(); + + for (i = 1; i <= PSU_COUNT; i++) { + if (Psu[i].is_absent) { + continue; + } + + ret = get_psu_model_sn_wobmc(i, model, serial_number); + if (ret) { + fault++; + continue; + } + if (!strcmp(model, PSU_F2B_AIRFLOW_FIELD)) { + psu_airflow = FAN_F2B; + } else if (!strcmp(model, PSU_B2F_AIRFLOW_FIELD)) { + psu_airflow = FAN_B2F; + } + + if (sys_airflow != psu_airflow) { + if (0 == Psu[i].is_airflow_fault) + syslog(LOG_WARNING, "PSU%d airflow is fault !!!\n", i); + Psu[i].is_airflow_fault = 1; + fault++; + } else { + Psu[i].is_airflow_fault = 0; + } + } + + return fault; +} + +/** + * @brief Check replaceable fan module present status + * @retval ERR error + * @retval fault The number of absent fans + */ +int check_fan_absent(void) +{ + int ret = 0, fault = 0, i = 0; + long status = 0; + + for (i = 1; i <= CHASSIS_FAN_COUNT; i++) { + ret = get_sysnode_value(Fan[i].pres_path, (void *)&status); + if (ret) { + return ERR; + } + if (status == ABSENT) { + if (0 == Fan[i].is_absent) { + syslog(LOG_WARNING, "Fan%d is absent !!!\n", i); + } + Fan[i].is_absent = 1; + Fan[i].is_airflow_fault = 0; + Fan[i].is_udminspd_ffault = 0; + Fan[i].is_udminspd_rfault = 0; + Fan[i].is_udspd_ffault = 0; + Fan[i].is_udspd_rfault = 0; + Fan[i].is_ovspd_ffault = 0; + Fan[i].is_ovspd_rfault = 0; + fault++; + } + else { + if (1 == Fan[i].is_absent) + syslog(LOG_WARNING, "Fan%d is plugged in\n", i); + Fan[i].is_absent = 0; + } + } + + return fault; +} + +/** + * @brief Check replaceable PSU module present status + * @retval ERR error + * @retval fault The number of absent PSUs + */ +int check_psu_absent(void) +{ + uint8_t psu_status = 0, present_status = 0; + int fault = 0, i = 0; + + for (i = 1; i <= PSU_COUNT; i++) { + psu_status = get_psu_status(i); + present_status = (psu_status >> psu_mapper[i].bit_present) & 0x01; + if (ABSENT == present_status) { /* absent */ + if (0 == Psu[i].is_absent) + syslog(LOG_WARNING, "PSU%d is absent !!!\n", i); + Fan[PSU1_FAN_ID + i - 1].is_absent = 1; + Psu[i].is_absent = 1; + Psu[i].is_airflow_fault = 0; + Psu[i].is_ac_fault = 0; + Psu[i].is_power_fault = 0; + fault++; + } else { + if (1 == Psu[i].is_absent) + syslog(LOG_WARNING, "PSU%d is plugged in\n", i); + Fan[PSU1_FAN_ID + i - 1].is_absent = 0; + Psu[i].is_absent = 0; + } + } + + return fault; +} + diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/platform_wobmc.h b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/platform_wobmc.h new file mode 100644 index 0000000000..4945c05a16 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/platform_wobmc.h @@ -0,0 +1,112 @@ +#ifndef _PLATFORM_WOBMC_H_ +#define _PLATFORM_WOBMC_H_ +#include "platform_comm.h" +#include "fancontrol.h" + +#define PSU1_I2C_BUS 47 +#define PSU2_I2C_BUS 48 +#define PSU1_I2C_ADDR 0x58 +#define PSU2_I2C_ADDR 0x59 + +#define PMBUS_MODEL_REG 0x9a +#define PMBUS_SERIAL_REG 0x9e + +#define NOT_DEFINE 125000 +#define PRESSURE_RAW_PATH "/sys/bus/i2c/devices/i2c-61/61-0060/iio:device0/in_pressure_raw" +#define PRESSURE_SCALE_PATH "/sys/bus/i2c/devices/i2c-61/61-0060/iio:device0/in_pressure_scale" + +struct temp { + int lo_warn; + int lo_err; + int lo_shutdown; + int hi_warn; + int hi_err; + int hi_shutdown; + int temp; /* /1000 */ + const char *descr; + char *path; + uint8_t is_ovwarn_fault:4; + uint8_t is_overr_fault:2; + uint8_t is_ovshutdown_fault:2; +}; + +struct vol_curr_pwr { + long lo_warn; + long lo_err; + long lo_shutdown; + long hi_warn; + long hi_err; + long hi_shutdown; + long vol_curr_pwr; + const char *descr; + char *path; + uint8_t is_udwarn_fault:1; + uint8_t is_uderr_fault:1; + uint8_t is_udshutdown_fault:1; + uint8_t is_ovwarn_fault:1; + uint8_t is_overr_fault:1; + uint8_t is_ovshutdown_fault:3; +}; + +struct fan { + const char *pres_path; + const char *frpm_path; + const char *rrpm_path; + const char *pwm_path; + const char *dir_path; + const char *led_path; + const char *eeprom_path; + const char *descr; + uint8_t is_absent:1; + uint8_t is_airflow_fault:1; + uint8_t is_udminspd_ffault:1; + uint8_t is_udminspd_rfault:1; + uint8_t is_udspd_ffault:1; + uint8_t is_udspd_rfault:1; + uint8_t is_ovspd_ffault:1; + uint8_t is_ovspd_rfault:1; +}; + +struct psu { + const char *vin_path; + const char *vout_path; + const char *cin_path; + const char *cout_path; + const char *pin_path; + const char *pout_path; + const char *speed_path; + const char *descr; + uint8_t is_absent:1; + uint8_t is_alert:1; + uint8_t is_airflow_fault:2; + uint8_t is_ac_fault:2; + uint8_t is_power_fault:2; +}; + +#define BITS_PER_LONG_LONG 64 +#define GENMASK_ULL(h, l) \ + (((~0ULL) - (1ULL << (l)) + 1) & \ + (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h)))) + +#define FRU_COMM_HDR_LEN 8 +#define FRU_BRDINFO_MFT_TLV_STRT 6 //Board Manufacturer byte offset in "Board Info Area Format" field. +#define TLV_TYPE_MASK GENMASK_ULL(7,6) +#define TLV_LEN_MASK GENMASK_ULL(5,0) + + +int get_psu_model_sn_wobmc(int id,char* model,char* serial_number); +int get_psu_info_wobmc(int id,int *mvin,int *mvout,int *mpin,int *mpout,int *miin,int *miout); +int get_fan_info_wobmc(int id,char* model,char* serial,int *get_fan_info); +int get_sensor_info_wobmc(int id, int *temp, int *warn, int *error, int *shutdown); +int get_fan_speed_wobmc(int id,int* per,int* rpm); + +int check_sys_airflow(void); +int check_fan_airflow_fault(uint8_t sys_airflow); +int check_fan_absent(void); +int check_fan_fault(void); +int check_psu_airflow_fault(uint8_t sys_airflow); +int check_psu_absent(void); +int check_psu_fault(void); + +#endif /* _PLATFORM_WOBMC_H_ */ + diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/psui.c b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/psui.c new file mode 100644 index 0000000000..8eb26df51b --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/psui.c @@ -0,0 +1,74 @@ +#include +#include "platform_comm.h" + +static onlp_psu_info_t psu_info[] = + { + {}, + { + {ONLP_PSU_ID_CREATE(PSUL_ID), "PSU-Left", 0}, + "", + "", + 0, + ONLP_PSU_CAPS_AC | ONLP_PSU_CAPS_VIN | ONLP_PSU_CAPS_VOUT | ONLP_PSU_CAPS_IIN | ONLP_PSU_CAPS_IOUT | ONLP_PSU_CAPS_PIN | ONLP_PSU_CAPS_POUT, + }, + { + {ONLP_PSU_ID_CREATE(PSUR_ID), "PSU-Right", 0}, + "", + "", + 0, + ONLP_PSU_CAPS_AC | ONLP_PSU_CAPS_VIN | ONLP_PSU_CAPS_VOUT | ONLP_PSU_CAPS_IIN | ONLP_PSU_CAPS_IOUT | ONLP_PSU_CAPS_PIN | ONLP_PSU_CAPS_POUT, + }}; + +extern const struct psu_reg_bit_mapper psu_mapper[PSU_COUNT + 1]; + +int onlp_psui_init(void) +{ + return ONLP_STATUS_OK; +} + +int onlp_psui_info_get(onlp_oid_t id, onlp_psu_info_t *info_p) +{ + int psu_id,psu_offset=0;; + psu_id = ONLP_OID_ID_GET(id); + *info_p = psu_info[psu_id]; + uint8_t psu_status = 0; + + int present_status=0,ac_status=0,pow_status=0; + + psu_status = get_psu_status(psu_id); + present_status = (psu_status >> psu_mapper[psu_id].bit_present) & 0x01; + ac_status = (psu_status >> psu_mapper[psu_id].bit_ac_sta) & 0x01; + pow_status = (psu_status >> psu_mapper[psu_id].bit_pow_sta) & 0x01; + + if (0 == present_status) + { + info_p->status |= ONLP_PSU_STATUS_PRESENT; + if (0 == pow_status || 0 == ac_status) + info_p->status |= ONLP_PSU_STATUS_UNPLUGGED; + } + else + { + info_p->status = ONLP_PSU_STATUS_FAILED; + } + + get_psu_model_sn(psu_id,info_p->model,info_p->serial); + + get_psu_info(psu_id,&(info_p->mvin),&(info_p->mvout),&(info_p->mpin),&(info_p->mpout),&(info_p->miin),&(info_p->miout)); + + if((info_p->mvin == 0) && (info_p->mpin == 0) && (info_p->miin == 0)){ + info_p->status |= ONLP_PSU_STATUS_UNPLUGGED; + } + + if(psu_id == 1){ + psu_offset = 1; + }else if(psu_id == 2){ + psu_offset = 4; + } + + info_p->hdr.coids[0] = ONLP_THERMAL_ID_CREATE(psu_offset + CHASSIS_THERMAL_COUNT); + info_p->hdr.coids[1] = ONLP_THERMAL_ID_CREATE(psu_offset+1 + CHASSIS_THERMAL_COUNT); + info_p->hdr.coids[2] = ONLP_THERMAL_ID_CREATE(psu_offset+2 + CHASSIS_THERMAL_COUNT); + info_p->hdr.coids[3] = ONLP_FAN_ID_CREATE(psu_id + CHASSIS_FAN_COUNT); + + return ONLP_STATUS_OK; +} diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/sfpi.c b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/sfpi.c new file mode 100644 index 0000000000..cd420bf5b7 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/sfpi.c @@ -0,0 +1,206 @@ +/************************************************************ + * + * + * Copyright 2014, 2015 Big Switch Networks, Inc. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * + ********************************************************** */ +#include +#include +#include "x86_64_cls_ds4101_log.h" +#include "platform_comm.h" + +static char node_path[PREFIX_PATH_LEN] = {0}; + +static int cls_ds4101_qsfp_sfp_node_read_int(char *path, int *value, int data_len) +{ + int ret = 0; + char tmp[8]; + *value = 0; + + ret = read_device_node_string(path, tmp, sizeof(tmp), data_len); + if (ret == 0) { + int is_not_present = atoi(tmp); + if (!is_not_present) + { + *value = !is_not_present; + } + } + return ret; +} + +static char * cls_ds4101_sfp_qsfp_get_present_path(int port) +{ + if (port <= OSFP_COUNT + SFP_COUNT) + { + if (port <= OSFP_COUNT) + { + sprintf(node_path, "%s/OSFP%d/osfp_modprs", PLATFORM_OSFP_PATH, port); + } + else + { + sprintf(node_path, "%s/SFP%d/sfp_modabs", PLATFORM_SFP_PATH, port - OSFP_COUNT); + } + } + else + { + AIM_LOG_ERROR("Number of port config is mismatch port(%d)\r\n", port); + return ""; + } + + return node_path; +} + +static char * cls_ds4101_sfp_qsfp_get_eeprom_path(int port) +{ + if (port <= OSFP_COUNT + SFP_COUNT) + { + if (port <= OSFP_COUNT) + { + sprintf( node_path, "%s%d-0050/eeprom", I2C_DEVICE_PATH, OSFP_I2C_START + (port - 1) ); //QSFP 1 - 32 + } + else + { + sprintf( node_path, "%s%d-0050/eeprom", I2C_DEVICE_PATH, SFP_I2C_START + (port - OSFP_COUNT) - 1 ); //SFP 1 - 2 + } + } + else + { + AIM_LOG_ERROR("Number of port config is mismatch port(%d)\r\n", port); + return ""; + } + + return node_path; +} + +static uint64_t cls_ds4101_sfp_qsfp_get_all_ports_present(void) +{ + int i, ret; + uint64_t present = 0; + char *path; + + for (i = 0; i < (OSFP_COUNT + SFP_COUNT); i++) + { + path = cls_ds4101_sfp_qsfp_get_present_path(i + 1); + if (cls_ds4101_qsfp_sfp_node_read_int(path, &ret, 0) != 0) + { + ret = 0; + } + present |= ((uint64_t)ret << i); + } + + return present; +} + +int onlp_sfpi_init(void) +{ + return ONLP_STATUS_OK; +} + +int onlp_sfpi_bitmap_get(onlp_sfp_bitmap_t *bmap) +{ + int p; + AIM_BITMAP_CLR_ALL(bmap); + + for (p = 0; p < (OSFP_COUNT + SFP_COUNT); p++) + { + AIM_BITMAP_SET(bmap, p); + } + + return ONLP_STATUS_OK; +} + +/* +* Return 1 if present. +* Return 0 if not present. +* Return < 0 if error. +*/ +int onlp_sfpi_is_present(int port) +{ + int present; + char *path = cls_ds4101_sfp_qsfp_get_present_path(port + 1); + if (cls_ds4101_qsfp_sfp_node_read_int(path, &present, 0) != 0) + { + if (port <= OSFP_COUNT) + { + AIM_LOG_ERROR("Unable to read present status from qsfp port(%d)\r\n", port); + } + else + { + AIM_LOG_ERROR("Unable to read present status from sfp port(%d)\r\n", port - OSFP_COUNT); + } + + return ONLP_STATUS_E_INTERNAL; + } + return present; +} + +int onlp_sfpi_presence_bitmap_get(onlp_sfp_bitmap_t *dst) +{ + int i = 0; + uint64_t presence_all = 0; + + presence_all = cls_ds4101_sfp_qsfp_get_all_ports_present(); + + /* Populate bitmap */ + for (i = 0; presence_all; i++) + { + AIM_BITMAP_MOD(dst, i, (presence_all & 1)); + presence_all >>= 1; + } + return ONLP_STATUS_OK; +} + +/* + * This function reads the SFPs idrom and returns in + * in the data buffer provided. + */ +int onlp_sfpi_eeprom_read(int port, uint8_t data[256]) +{ + char *path; + + path = cls_ds4101_sfp_qsfp_get_eeprom_path(port + 1); + + /* + * Read the SFP eeprom into data[] + * + * Return MISSING if SFP is missing. + * Return OK if eeprom is read + */ + memset(data, 0, 256); + if (read_device_node_binary(path, (char*)data, 256, 256) != 0) { + AIM_LOG_ERROR("Unable to read eeprom from port(%d)\r\n", port); + return ONLP_STATUS_E_INTERNAL; + } + + return ONLP_STATUS_OK; +} + +int onlp_sfpi_rx_los_bitmap_get(onlp_sfp_bitmap_t *dst) +{ + return ONLP_STATUS_OK; +} + +/* + * De-initialize the SFPI subsystem. + */ +int onlp_sfpi_denit(void) +{ + return ONLP_STATUS_OK; +} diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/sysi.c b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/sysi.c new file mode 100644 index 0000000000..8d085d71c3 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/sysi.c @@ -0,0 +1,179 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//Below include add for support Cache system +#include +#include +#include +#include + +#include "x86_64_cls_ds4101_int.h" +#include "x86_64_cls_ds4101_log.h" +#include "platform_comm.h" +#include "platform_wbmc.h" +#include "platform_wobmc.h" +#include "fancontrol.h" +#include "ledcontrol.h" + + +uint8_t BMC_Status = ABSENT; + +const char *onlp_sysi_platform_get(void) +{ + return "x86-64-cls-ds4101-r0"; +} + +int onlp_sysi_init(void) +{ + BMC_Status = read_register(BMC_STATUS_REG) & 0x01; + + return ONLP_STATUS_OK; +} + +int onlp_sysi_platform_info_get(onlp_platform_info_t *pi) +{ + char r_data[15] = {0}; + char fullpath[PREFIX_PATH_LEN] = {0}; + + memset(fullpath, 0, PREFIX_PATH_LEN); + sprintf(fullpath, "%s%s", SYS_CPLD_PATH, "version"); + if (read_device_node_string(fullpath, r_data, sizeof(r_data), 0) != 0) + { + DEBUG_PRINT("%s(%d): read %s error\n", __FUNCTION__, __LINE__, fullpath); + return ONLP_STATUS_E_INTERNAL; + } + pi->cpld_versions = aim_fstrdup("CPLD_B=%s", r_data); + + sprintf(fullpath, "%s%s", SYS_FPGA_PATH, "version"); + if (read_device_node_string(fullpath, r_data, sizeof(r_data), 0) != 0) + { + DEBUG_PRINT("%s(%d): read %s error\n", __FUNCTION__, __LINE__, fullpath); + return ONLP_STATUS_E_INTERNAL; + } + pi->other_versions = aim_fstrdup("FPGA=%s", r_data); + + return 0; +} + +void onlp_sysi_platform_info_free(onlp_platform_info_t *pi) +{ + aim_free(pi->cpld_versions); +} + +int onlp_sysi_onie_data_get(uint8_t **data, int *size) +{ + uint8_t *rdata = aim_zmalloc(256); + + if (onlp_file_read(rdata, 256, size, PREFIX_PATH_ON_SYS_EEPROM) == ONLP_STATUS_OK) + { + if (*size == 256) + { + *data = rdata; + return ONLP_STATUS_OK; + } + } + aim_free(rdata); + rdata = NULL; + *size = 0; + DEBUG_PRINT("[Debug][%s][%d][Can't get onie data]\n", __FUNCTION__, __LINE__); + + return ONLP_STATUS_E_INTERNAL; +} + +void onlp_sysi_onie_data_free(uint8_t *data) +{ + aim_free(data); +} + +int onlp_sysi_platform_manage_init(void) +{ + uint8_t value = 0; + int ret = 0; + + check_sys_airflow(); + + /* power on the switch board if it's off because of high terperature */ + value = read_register(SWITCH_PWCTRL_REG); + if (SWITCH_OFF == (value & 0x03)) { + /* wait 30s for switch board cooling down */ + sleep(30); + ret = syscpld_setting(LPC_SWITCH_PWCTRL_REG, SWITCH_ON); + if (ret) + { + perror("Fail to power on switch board !!!"); + } + system("reboot"); + } + + + if (PRESENT == BMC_Status) + { + if (is_cache_exist() < 1) + { + create_cache(); + } + } + + return ONLP_STATUS_OK; +} + +int onlp_sysi_platform_manage_fans(void) +{ + if (PRESENT != BMC_Status) + { + update_fan(); + } + else + { + if (is_cache_exist() < 1) + create_cache(); + } + + return ONLP_STATUS_OK; +} + +int onlp_sysi_platform_manage_leds(void) +{ + if (PRESENT != BMC_Status) + { + update_led(); + } + + return ONLP_STATUS_OK; +} + +int onlp_sysi_oids_get(onlp_oid_t *table, int max) +{ + int i; + onlp_oid_t *e = table; + + memset(table, 0, max * sizeof(onlp_oid_t)); + + /* 2 PSUs */ + *e++ = ONLP_PSU_ID_CREATE(1); + *e++ = ONLP_PSU_ID_CREATE(2); + + // // /* LEDs Item */ + for (i = 1; i <= LED_COUNT; i++) + *e++ = ONLP_LED_ID_CREATE(i); + + // // /* THERMALs Item */ + for (i = 1; i <= CHASSIS_THERMAL_COUNT; i++) + *e++ = ONLP_THERMAL_ID_CREATE(i); + + // /* Fans Item */ + for (i = 1; i <= CHASSIS_FAN_COUNT; i++) + *e++ = ONLP_FAN_ID_CREATE(i); + + return ONLP_STATUS_OK; +} diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/thermali.c b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/thermali.c new file mode 100644 index 0000000000..4f51a92a54 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/thermali.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include "platform_comm.h" + +static onlp_thermal_info_t thermal_info[] = { + { }, + { { ONLP_THERMAL_ID_CREATE(TEMP_CPU_ID), "TEMP_CPU", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(TEMP_DIMMA0_ID), "TEMP_DIMMA0", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(TEMP_DIMMB0_ID), "TEMP_DIMMB0", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(TEMP_SW_Internal_ID), "TEMP_SW_Internal", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(BMC_Temp_ID), "BMC_Temp", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(TEMP_BB_U3_ID), "TEMP_BB_U3", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(TEMP_SW_U15_ID), "TEMP_SW_U15", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(TEMP_SW_U17_ID), "TEMP_SW_U17", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(TEMP_SW_U16_ID), "TEMP_SW_U16", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(TEMP_SW_U13_ID), "TEMP_SW_U13", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(TEMP_SW_U11_ID), "TEMP_SW_U11", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(TEMP_SW_U12_ID), "TEMP_SW_U12", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(TEMP_FCB_U52_ID), "TEMP_FCB_U52", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(TEMP_FCB_U17_ID), "TEMP_FCB_U17", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(MP5023_T_ID), "MP5023_T", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(VDD_CORE_T_ID), "VDD_CORE_T", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(XP3R3V_LEFT_T_ID), "XP3R3V_LEFT_T", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(XP3R3V_RIGHT_T_ID), "XP3R3V_RIGHT_T", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(PSU1_TEMP1_ID), "PSU1_TEMP1", ONLP_PSU_ID_CREATE(1)}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(PSU1_TEMP2_ID), "PSU1_TEMP2", ONLP_PSU_ID_CREATE(1)}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(PSU1_TEMP3_ID), "PSU1_TEMP3", ONLP_PSU_ID_CREATE(1)}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(PSU2_TEMP1_ID), "PSU2_TEMP1", ONLP_PSU_ID_CREATE(2)}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(PSU2_TEMP2_ID), "PSU2_TEMP2", ONLP_PSU_ID_CREATE(2)}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(PSU2_TEMP3_ID), "PSU2_TEMP3", ONLP_PSU_ID_CREATE(2)}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, +}; + +int onlp_thermali_init(void) +{ + return ONLP_STATUS_OK; +} + +int onlp_thermali_info_get(onlp_oid_t id, onlp_thermal_info_t *info_p) +{ + int thermal_id; + int thermal_status = 0; + int temp, warn, err, shutdown; + + thermal_id = ONLP_OID_ID_GET(id); + memcpy(info_p, &thermal_info[thermal_id], sizeof(onlp_thermal_info_t)); + + /* Get thermal temperature. */ + thermal_status = get_sensor_info(thermal_id, &temp, &warn, &err, &shutdown); + if (-1 == thermal_status) + { + info_p->status = ONLP_THERMAL_STATUS_FAILED; + } + else + { + info_p->status = ONLP_THERMAL_STATUS_PRESENT; + info_p->mcelsius = temp; + info_p->thresholds.warning = warn; + info_p->thresholds.error = err; + info_p->thresholds.shutdown = shutdown; + } + + return ONLP_STATUS_OK; +} diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_config.c b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_config.c new file mode 100644 index 0000000000..e955e5d0ec --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_config.c @@ -0,0 +1,76 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +/* */ +#define __x86_64_cls_ds4101_config_STRINGIFY_NAME(_x) #_x +#define __x86_64_cls_ds4101_config_STRINGIFY_VALUE(_x) __x86_64_cls_ds4101_config_STRINGIFY_NAME(_x) +x86_64_cls_ds4101_config_settings_t x86_64_cls_ds4101_config_settings[] = +{ +#ifdef X86_64_CLS_DS4101_CONFIG_INCLUDE_LOGGING + { __x86_64_cls_ds4101_config_STRINGIFY_NAME(X86_64_CLS_DS4101_CONFIG_INCLUDE_LOGGING), __x86_64_cls_ds4101_config_STRINGIFY_VALUE(X86_64_CLS_DS4101_CONFIG_INCLUDE_LOGGING) }, +#else +{ X86_64_CLS_DS4101_CONFIG_INCLUDE_LOGGING(__x86_64_cls_ds4101_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_CLS_DS4101_CONFIG_LOG_OPTIONS_DEFAULT + { __x86_64_cls_ds4101_config_STRINGIFY_NAME(X86_64_CLS_DS4101_CONFIG_LOG_OPTIONS_DEFAULT), __x86_64_cls_ds4101_config_STRINGIFY_VALUE(X86_64_CLS_DS4101_CONFIG_LOG_OPTIONS_DEFAULT) }, +#else +{ X86_64_CLS_DS4101_CONFIG_LOG_OPTIONS_DEFAULT(__x86_64_cls_ds4101_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_CLS_DS4101_CONFIG_LOG_BITS_DEFAULT + { __x86_64_cls_ds4101_config_STRINGIFY_NAME(X86_64_CLS_DS4101_CONFIG_LOG_BITS_DEFAULT), __x86_64_cls_ds4101_config_STRINGIFY_VALUE(X86_64_CLS_DS4101_CONFIG_LOG_BITS_DEFAULT) }, +#else +{ X86_64_CLS_DS4101_CONFIG_LOG_BITS_DEFAULT(__x86_64_cls_ds4101_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_CLS_DS4101_CONFIG_LOG_CUSTOM_BITS_DEFAULT + { __x86_64_cls_ds4101_config_STRINGIFY_NAME(X86_64_CLS_DS4101_CONFIG_LOG_CUSTOM_BITS_DEFAULT), __x86_64_cls_ds4101_config_STRINGIFY_VALUE(X86_64_CLS_DS4101_CONFIG_LOG_CUSTOM_BITS_DEFAULT) }, +#else +{ X86_64_CLS_DS4101_CONFIG_LOG_CUSTOM_BITS_DEFAULT(__x86_64_cls_ds4101_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_CLS_DS4101_CONFIG_PORTING_STDLIB + { __x86_64_cls_ds4101_config_STRINGIFY_NAME(X86_64_CLS_DS4101_CONFIG_PORTING_STDLIB), __x86_64_cls_ds4101_config_STRINGIFY_VALUE(X86_64_CLS_DS4101_CONFIG_PORTING_STDLIB) }, +#else +{ X86_64_CLS_DS4101_CONFIG_PORTING_STDLIB(__x86_64_cls_ds4101_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_CLS_DS4101_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS + { __x86_64_cls_ds4101_config_STRINGIFY_NAME(X86_64_CLS_DS4101_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS), __x86_64_cls_ds4101_config_STRINGIFY_VALUE(X86_64_CLS_DS4101_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS) }, +#else +{ X86_64_CLS_DS4101_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS(__x86_64_cls_ds4101_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_CLS_DS4101_CONFIG_INCLUDE_UCLI + { __x86_64_cls_ds4101_config_STRINGIFY_NAME(X86_64_CLS_DS4101_CONFIG_INCLUDE_UCLI), __x86_64_cls_ds4101_config_STRINGIFY_VALUE(X86_64_CLS_DS4101_CONFIG_INCLUDE_UCLI) }, +#else +{ X86_64_CLS_DS4101_CONFIG_INCLUDE_UCLI(__x86_64_cls_ds4101_config_STRINGIFY_NAME), "__undefined__" }, +#endif + { NULL, NULL } +}; +#undef __x86_64_cls_ds4101_config_STRINGIFY_VALUE +#undef __x86_64_cls_ds4101_config_STRINGIFY_NAME + +const char* +x86_64_cls_ds4101_config_lookup(const char* setting) +{ + int i; + for(i = 0; x86_64_cls_ds4101_config_settings[i].name; i++) { + if(strcmp(x86_64_cls_ds4101_config_settings[i].name, setting)) { + return x86_64_cls_ds4101_config_settings[i].value; + } + } + return NULL; +} + +int +x86_64_cls_ds4101_config_show(struct aim_pvs_s* pvs) +{ + int i; + for(i = 0; x86_64_cls_ds4101_config_settings[i].name; i++) { + aim_printf(pvs, "%s = %s\n", x86_64_cls_ds4101_config_settings[i].name, x86_64_cls_ds4101_config_settings[i].value); + } + return i; +} + +/* */ + diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_enums.c b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_enums.c new file mode 100644 index 0000000000..c3ef97c462 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_enums.c @@ -0,0 +1,6 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include \ No newline at end of file diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_int.h b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_int.h new file mode 100644 index 0000000000..66a768896f --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_int.h @@ -0,0 +1,11 @@ +/**************************************************************************//** + * + * x86_64_cls_ds4101 Internal Header + * + *****************************************************************************/ +#ifndef __X86_64_CLS_DS4101_INT_H__ +#define __X86_64_CLS_DS4101_INT_H__ + +#include + +#endif /* __X86_64_CLS_DS4101_INT_H__ */ \ No newline at end of file diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_log.c b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_log.c new file mode 100644 index 0000000000..7c07e76541 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_log.c @@ -0,0 +1,18 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +#include "x86_64_cls_ds4101_log.h" +/* + * x86_64_cls_ds4101 log struct. + */ +AIM_LOG_STRUCT_DEFINE( + X86_64_CLS_DS4101_CONFIG_LOG_OPTIONS_DEFAULT, + X86_64_CLS_DS4101_CONFIG_LOG_BITS_DEFAULT, + NULL, /* Custom log map */ + X86_64_CLS_DS4101_CONFIG_LOG_CUSTOM_BITS_DEFAULT + ); + diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_log.h b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_log.h new file mode 100644 index 0000000000..742f084576 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_log.h @@ -0,0 +1,12 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#ifndef __X86_64_CLS_DS4101_LOG_H__ +#define __X86_64_CLS_DS4101_LOG_H__ + +#define AIM_LOG_MODULE_NAME x86_64_cls_ds4101 +#include + +#endif /* __X86_64_CLS_DS4101_LOG_H__ */ diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_module.c b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_module.c new file mode 100644 index 0000000000..7cf6c50ce7 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_module.c @@ -0,0 +1,24 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +#include "x86_64_cls_ds4101_log.h" + +static int +datatypes_init__(void) +{ +#define X86_64_CLS_DS4101_ENUMERATION_ENTRY(_enum_name, _desc) AIM_DATATYPE_MAP_REGISTER(_enum_name, _enum_name##_map, _desc, AIM_LOG_INTERNAL); +#include + return 0; +} + +void __x86_64_cls_ds4101_module_init__(void) +{ + AIM_LOG_STRUCT_REGISTER(); + datatypes_init__(); +} + +int __onlp_platform_version__ = 1; diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_ucli.c b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_ucli.c new file mode 100644 index 0000000000..4704263b5a --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/builds/x86_64_cls_ds4101/module/src/x86_64_cls_ds4101_ucli.c @@ -0,0 +1,50 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +#if X86_64_CLS_DS4101_CONFIG_INCLUDE_UCLI == 1 + +#include +#include +#include + +static ucli_status_t +x86_64_cls_ds4101_ucli_ucli__config__(ucli_context_t* uc) +{ + UCLI_HANDLER_MACRO_MODULE_CONFIG(x86_64_cls_ds4101) +} + +/* */ +/* */ + +static ucli_module_t +x86_64_cls_ds4101_ucli_module__ = + { + "x86_64_cls_ds4101_ucli", + NULL, + x86_64_cls_ds4101_ucli_ucli_handlers__, + NULL, + NULL, + }; + +ucli_node_t* +x86_64_cls_ds4101_ucli_node_create(void) +{ + ucli_node_t* n; + ucli_module_init(&x86_64_cls_ds4101_ucli_module__); + n = ucli_node_create("x86_64_cls_ds4101", NULL, &x86_64_cls_ds4101_ucli_module__); + ucli_node_subnode_add(n, ucli_module_log_node_create("x86_64_cls_ds4101")); + return n; +} + +#else +void* +x86_64_cls_ds4101_ucli_node_create(void) +{ + return NULL; +} +#endif + diff --git a/packages/platforms/celestica/x86-64/ds4101/onlp/onlp-platform-revision.yml b/packages/platforms/celestica/x86-64/ds4101/onlp/onlp-platform-revision.yml new file mode 100644 index 0000000000..c898e73883 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/onlp/onlp-platform-revision.yml @@ -0,0 +1,28 @@ +############################################################ +# +# ONLP Platform Package Template +# +# Requires: PLATFORM, ARCH, REVISION +# +############################################################ + +variables: + install: /lib/platform-config/${PLATFORM}-${REVISION}/onl + +common: + version: 1.0.0 + arch: $ARCH + copyright: Copyright 2013, 2014, 2015 Big Switch Networks + maintainer: support@bigswitch.com + support: opennetworklinux@googlegroups.com + +packages: + - name: onlp-${PLATFORM}-${REVISION} + summary: ONLP Package for the ${PLATFORM}-${REVISION} platform. + platform-config: True + + files: + builds/lib/$BUILD_DIR/$TOOLCHAIN/bin/libonlp-${PLATFORM}.so : ${install}/lib/ + builds/onlpdump/$BUILD_DIR/$TOOLCHAIN/bin/onlps : ${install}/bin/onlpdump + + changelog: Change changes changes., diff --git a/packages/platforms/celestica/x86-64/ds4101/platform-config/Makefile b/packages/platforms/celestica/x86-64/ds4101/platform-config/Makefile new file mode 100644 index 0000000000..dc1e7b86f0 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/platform-config/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk diff --git a/packages/platforms/celestica/x86-64/ds4101/platform-config/r0/Makefile b/packages/platforms/celestica/x86-64/ds4101/platform-config/r0/Makefile new file mode 100644 index 0000000000..dc1e7b86f0 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/platform-config/r0/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk diff --git a/packages/platforms/celestica/x86-64/ds4101/platform-config/r0/PKG.yml b/packages/platforms/celestica/x86-64/ds4101/platform-config/r0/PKG.yml new file mode 100644 index 0000000000..85d3232792 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/platform-config/r0/PKG.yml @@ -0,0 +1 @@ +!include $ONL_TEMPLATES/platform-config-platform.yml ARCH=amd64 VENDOR=celestica BASENAME=x86-64-cls-ds4101 REVISION=r0 diff --git a/packages/platforms/celestica/x86-64/ds4101/platform-config/r0/src/lib/x86-64-cls-ds4101-r0.yml b/packages/platforms/celestica/x86-64/ds4101/platform-config/r0/src/lib/x86-64-cls-ds4101-r0.yml new file mode 100644 index 0000000000..574ccdcca4 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/platform-config/r0/src/lib/x86-64-cls-ds4101-r0.yml @@ -0,0 +1,26 @@ +--- + +###################################################################### +# +# platform-config for Celestica DS4101 +# +# +###################################################################### + +x86-64-cls-ds4101-r0: + + grub: + + serial: >- + --port=0x3f8 + --speed=115200 + --word=8 + --parity=no + --stop=1 + + kernel: + <<: *kernel-5-4 + + args: >- + nopat + console=ttyS0,115200n8 diff --git a/packages/platforms/celestica/x86-64/ds4101/platform-config/r0/src/python/x86_64_cls_ds4101_r0/__init__.py b/packages/platforms/celestica/x86-64/ds4101/platform-config/r0/src/python/x86_64_cls_ds4101_r0/__init__.py new file mode 100755 index 0000000000..21901b1670 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/platform-config/r0/src/python/x86_64_cls_ds4101_r0/__init__.py @@ -0,0 +1,213 @@ +import subprocess +import time +from onl.platform.base import * +from onl.platform.celestica import * +import tempfile +import subprocess +import re + +sfp_quantity = 2 +osfp_quantity = 32 +fan_quantity = 7 +sfp_i2c_start_bus = 2 +osfp_i2c_start_bus = 15 +fan_i2c_start_bus = 63 + + +class OnlPlatform_x86_64_cls_ds4101_r0( + OnlPlatformCelestica, OnlPlatformPortConfig_32x800_2x10 +): + PLATFORM = "x86-64-cls-ds4101-r0" + MODEL = "DS4101" + SYS_OBJECT_ID = ".2060.1" + + @staticmethod + def send_cmd(cmd): + """ + send command + :param cmd: command + :return: command status and response + """ + try: + data = subprocess.check_output(cmd, shell=True, + universal_newlines=True, stderr=subprocess.STDOUT) + sta = True + except subprocess.CalledProcessError as ex: + data = ex.output + sta = False + if data[-1:] == '\n': + data = data[:-1] + return sta, data + + def get_boot_partition(self): + """ + get the onl boot partition name. + If ssd interface protocol is NVME, partition name is /dev/nvme0n1p3 + If ssd interface protocol is sata, partition name is /dev/sda3 + :return: /dev/nvme0n1p3 or /dev/sda3 + """ + partition_status, partition = self.send_cmd("blkid | grep 'ONL-BOOT' | awk -F ':' '{print $1}'") + if partition_status: + partition = str(partition).strip() + else: + partition = "/dev/nvme0n1p3" if os.path.exists("/sys/block/nvme0n1/removable") else "/dev/sda3" + return partition + + def register_hwdevice_multi_times(self, driver, bus, addr, times, node): + """ + Register the hwmon device multiple times to fix loading issue + Param: + string: driver name like "tdps2000" + int: bus, decimal format like 11 + hex: addr, hex format like 0x59 + string: node like 'fan1_input' + Returns: + bool: true for success , false for fail + """ + count = 0 + while count < times: + self.new_i2c_device(driver, addr, bus) + ret = os.system("ls /sys/bus/i2c/devices/i2c-%d/%d-%4.4x/hwmon/hwmon*/ | grep %s > /dev/null" % (bus, bus, addr, node)) + if ret == 0: + return True + os.system("echo 0x%4.4x > /sys/bus/i2c/devices/i2c-%d/delete_device" % (addr, bus)) + count = count + 1 + return False + + def baseconfig(self): + # disable i2c-imc autoload because there's a conflict if BMC exists + os.system("echo %s > /etc/hostname" % self.MODEL) + os.system("echo 'blacklist i2c-imc' > /etc/modprobe.d/blacklist.conf") + + # Install platform drivers + print("Initialize and Install the driver here") + self.insmod("fpga-device.ko") + self.insmod("fpga-i2c-xiic.ko") + self.insmod("fpga-sys.ko") + self.insmod("fpga-xcvr.ko") + self.insmod("i2c-xcvr.ko") + self.insmod("lpc-basecpld.ko") + self.insmod("watch_dog.ko") + os.system("rmmod at24") + self.insmod("at24.ko") + + # Add devices + os.system("modprobe i2c-ismt") + os.system("modprobe tpm_i2c_nuvoton") + os.system("modprobe mpl3115") + os.system("modprobe optoe") + ## Support to use internal USB to update firmwares + os.system("modprobe usbnet") + os.system("modprobe cdc_ether") + os.system("modprobe mce-inject") + self.new_i2c_device("24c64", 0x56, 0) + self.new_i2c_device("pca9548", 0x71, 1) + self.new_i2c_device("pca9548", 0x73, 1) + self.new_i2c_device("pca9548", 0x70, 1) + self.new_i2c_device("pca9548", 0x72, 1) + os.system("echo -2 > /sys/bus/i2c/drivers/pca954x/1-0070/idle_state") + os.system("echo -2 > /sys/bus/i2c/drivers/pca954x/1-0071/idle_state") + os.system("echo -2 > /sys/bus/i2c/drivers/pca954x/1-0072/idle_state") + os.system("echo -2 > /sys/bus/i2c/drivers/pca954x/1-0073/idle_state") + + print("Running Watchdog") + os.system("python /usr/lib/python2.7/dist-packages/onl/platform/x86_64_cls_ds4101_r0/wdt/wdt_control.py &") + # check BMC present status + os.system("echo 0xA108 > /sys/bus/platform/devices/sys_cpld/getreg") + result = os.popen("cat /sys/bus/platform/devices/sys_cpld/getreg").read() + if "0x01" in result: + print("BMC is absent") + + self.insmod("platform-fan.ko") + self.insmod("platform-psu.ko") + + self.insmod("mp2975.ko") + self.insmod("mp2880.ko") + self.insmod("mp5023.ko") + + self.new_i2c_device("pca9548", 0x70, 7) + self.new_i2c_device("pca9548", 0x70, 10) + self.new_i2c_device("pca9548", 0x77, 11) + os.system("modprobe i2c-imc") + os.system("modprobe jc42") + + # deselect pca9548 channel. + os.system("echo -2 > /sys/bus/i2c/drivers/pca954x/7-0070/idle_state") + os.system("echo -2 > /sys/bus/i2c/drivers/pca954x/10-0070/idle_state") + os.system("echo -2 > /sys/bus/i2c/drivers/pca954x/11-0077/idle_state") + + self.new_i2c_device("platform_fan", 0x0d, 11) + + # reinstall tdps2000 driver since it may fail to create fanx_input node + # if there's error 0x80 in 0x7e register when driver probes + ret = self.register_hwdevice_multi_times('tdps2000', 47, 0x58, 6, 'fan1_input') + if ret is False: + print("*** # Fail to register tdps2000 on 47-0058, please check...") + ret = self.register_hwdevice_multi_times('tdps2000', 48, 0x59, 6, 'fan1_input') + if ret is False: + print("*** # Fail to register tdps2000 on 48-0059, please check...") + self.new_i2c_device("24c02", 0x50, 47) + self.new_i2c_device("24c02", 0x51, 48) + + self.new_i2c_device("ucd90120", 0x35, 8) + self.new_i2c_device("ucd90160", 0x34, 8) + + self.new_i2c_device("mp2975", 0x70, 9) + self.new_i2c_device("mp2975", 0x76, 9) + self.new_i2c_device("mp2880", 0x20, 9) + self.new_i2c_device("mp5023", 0x40, 8) + + # EEprom + self.new_i2c_device("24c64", 0x50, 4) + self.new_i2c_device("24c64", 0x50, 5) + self.new_i2c_device("24c64", 0x57, 5) + self.new_i2c_device("24c64", 0x50, 70) + + for fan_index in range(fan_quantity): + fan_bus = fan_index + fan_i2c_start_bus + self.new_i2c_device("24c64", 0x50, fan_bus) + + self.new_i2c_device("lm75b", 0x4e, 10) + self.new_i2c_device("lm75b", 0x48, 55) + self.new_i2c_device("lm75b", 0x49, 56) + self.new_i2c_device("lm75b", 0x4a, 57) + self.new_i2c_device("lm75b", 0x4b, 58) + self.new_i2c_device("lm75b", 0x4c, 59) + self.new_i2c_device("lm75b", 0x4d, 60) + self.new_i2c_device("mpl3115", 0x60, 61) + self.new_i2c_device("lm75", 0x48, 70) + self.new_i2c_device("lm75", 0x49, 70) + + # Instantiate XCVR devices; one device manage 4 ports, totally 32 ports from 0x20 to 0x27 + for xcvr in range(0x20, 0x24): + self.new_i2c_device("xcvr", xcvr, 13) + + for xcvr in range(0x24, 0x28): + self.new_i2c_device("xcvr", xcvr, 14) + + # Initialize SFPs & QSFPs name + for actual_i2c_port in range( + sfp_i2c_start_bus, sfp_i2c_start_bus + sfp_quantity + ): + self.new_i2c_device("optoe2", 0x50, actual_i2c_port) + port_number = actual_i2c_port - (sfp_i2c_start_bus - 1) + os.system( + "echo 'SFP{1}' > /sys/bus/i2c/devices/i2c-{0}/{0}-0050/port_name".format( + actual_i2c_port, port_number + ) + ) + + for actual_i2c_port in range( + osfp_i2c_start_bus, osfp_i2c_start_bus + osfp_quantity + ): + self.new_i2c_device("optoe1", 0x50, actual_i2c_port) + oport_number = actual_i2c_port - (osfp_i2c_start_bus - 1) + os.system( + "echo 'OSFP{1}' > /sys/bus/i2c/devices/i2c-{0}/{0}-0050/port_name".format( + actual_i2c_port, oport_number + ) + ) + + os.system("echo 4 > /proc/sys/kernel/printk") + + return True diff --git a/packages/platforms/celestica/x86-64/ds4101/platform-config/r0/src/python/x86_64_cls_ds4101_r0/wdt/__init__.py b/packages/platforms/celestica/x86-64/ds4101/platform-config/r0/src/python/x86_64_cls_ds4101_r0/wdt/__init__.py new file mode 100755 index 0000000000..e69de29bb2 diff --git a/packages/platforms/celestica/x86-64/ds4101/platform-config/r0/src/python/x86_64_cls_ds4101_r0/wdt/watch_dog.py b/packages/platforms/celestica/x86-64/ds4101/platform-config/r0/src/python/x86_64_cls_ds4101_r0/wdt/watch_dog.py new file mode 100755 index 0000000000..3952592dc8 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/platform-config/r0/src/python/x86_64_cls_ds4101_r0/wdt/watch_dog.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# @Time : 2023/8/17 15:44 +# @Mail : inhuang@celestica.com +# @Author : Ingrid Huang +# @Function: watchdog function + +import os +import subprocess +import sys +import fcntl +import array + +""" ioctl constants """ +IO_WRITE = 0x40000000 +IO_READ = 0x80000000 +IO_READ_WRITE = 0xC0000000 +IO_SIZE_INT = 0x00040000 +IO_SIZE_40 = 0x00280000 +IO_TYPE_WATCHDOG = ord('W') << 8 + +WDR_INT = IO_READ | IO_SIZE_INT | IO_TYPE_WATCHDOG +WDR_40 = IO_READ | IO_SIZE_40 | IO_TYPE_WATCHDOG +WDWR_INT = IO_READ_WRITE | IO_SIZE_INT | IO_TYPE_WATCHDOG + +""" Watchdog ioctl commands """ +WDIOC_GETSUPPORT = 0 | WDR_40 +WDIOC_GETSTATUS = 1 | WDR_INT +WDIOC_GETBOOTSTATUS = 2 | WDR_INT +WDIOC_GETTEMP = 3 | WDR_INT +WDIOC_SETOPTIONS = 4 | WDR_INT +WDIOC_KEEPALIVE = 5 | WDR_INT +WDIOC_SETTIMEOUT = 6 | WDWR_INT +WDIOC_GETTIMEOUT = 7 | WDR_INT +WDIOC_SETPRETIMEOUT = 8 | WDWR_INT +WDIOC_GETPRETIMEOUT = 9 | WDR_INT +WDIOC_GETTIMELEFT = 10 | WDR_INT + +""" Watchdog status constants """ +WDIOS_DISABLECARD = 0x0001 +WDIOS_ENABLECARD = 0x0002 + +STATUS_PATH = "/sys/devices/platform/cpld_wdt/status" +STATE_PATH = "/sys/devices/platform/cpld_wdt/state" +TIMEOUT_PATH = "/sys/devices/platform/cpld_wdt/timeout" +WDT_W_PATH = "/dev/cpld_wdt" +DEFAULT_TIMEOUT = 180 +watchdog = 0 +WDT_COMMON_ERROR = -1 + + +class Watchdog(): + watchdog = None + + def __init__(self): + global watchdog + # Set default value + state_cmd = "cat %s" % STATE_PATH + state = self.run_command(state_cmd) + self.armed = True if state == "active" else False + self.timeout = DEFAULT_TIMEOUT + if not watchdog: + watchdog = os.open(WDT_W_PATH, os.O_RDWR) + self.watchdog = watchdog + + @staticmethod + def run_command(cmd): + result = "" + try: + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err.decode("utf-8") == "": + result = raw_data.decode("utf-8").strip() + else: + result = err.decode("utf-8").strip() + except Exception: + pass + return result + + def _enable(self): + """ + Turn on the watchdog timer + """ + req = array.array('h', [WDIOS_ENABLECARD]) + fcntl.ioctl(self.watchdog, WDIOC_SETOPTIONS, req, False) + + def _disable(self): + """ + Turn off the watchdog timer + """ + req = array.array('h', [WDIOS_DISABLECARD]) + fcntl.ioctl(self.watchdog, WDIOC_SETOPTIONS, req, False) + + def arm(self, seconds): + ret = WDT_COMMON_ERROR + if seconds < 0: + return ret + + try: + self.timeout = self._set_timeout(seconds) + if self.armed: + self._keep_alive() + else: + self._enable() + self.armed = True + + ret = self.timeout + except IOError: + pass + return ret + + def _keep_alive(self): + """ + Keep alive watchdog timer + """ + fcntl.ioctl(self.watchdog, WDIOC_KEEPALIVE) + + def _set_timeout(self, seconds): + """ + Set watchdog timer timeout + @param seconds - timeout in seconds + @return is the actual set timeout + """ + req = array.array('I', [seconds]) + fcntl.ioctl(self.watchdog, WDIOC_SETTIMEOUT, req, True) + return int(req[0]) + + def get_time_left(self): + """ + Get time left before watchdog timer expires + @return time left in seconds + """ + req = array.array('I', [0]) + fcntl.ioctl(self.watchdog, WDIOC_GETTIMELEFT, req, True) + + return int(req[0]) + + def disarm(self): + """ + Disarm the hardware watchdog + Returns: + A boolean, True if watchdog is disarmed successfully, False if not + """ + disarmed = False + if self.is_armed(): + try: + self._disable() + self.armed = False + disarmed = True + except IOError: + pass + + if self.watchdog is not None: + os.write(self.watchdog, "V") + + return disarmed + + def is_armed(self): + """ + Retrieves the armed state of the hardware watchdog. + Returns: + A boolean, True if watchdog is armed, False if not + """ + + return self.armed + + def __del__(self): + """ + Close watchdog + """ + if self.watchdog is not None: + os.close(self.watchdog) + +def print_help(): + print(""" + Usage: ./watch_dog.py [OPTIONS] COMMAND [ARGS]... + + Commands: + arm Arm HW watchdog with default time + arm -s XX Arm HW watchdog with XX seconds, XX is number + disarm Disarm HW watchdog + status Check the watchdog status with remaining_time if it's armed + """) + + +def main(): + watchdog_object = Watchdog() + action = sys.argv[1:] + try: + if action[0] == "status": + if watchdog_object.armed: + print("\nStatus: Armed\nTime remaining: %s seconds\n" % watchdog_object.get_time_left()) + else: + print("\nStatus: Disarmed\n") + elif action[0] == "disarm": + wdt_disarm = watchdog_object.disarm() + if wdt_disarm: + print("\nDisarm the watchdog!\n") + else: + print("\nDisarm watchdog failed!\n") + elif action[0] == "arm": + if len(action) == 3: + if action[1] == "-s" and int(action[2]): + watchdog_object.arm(int(action[2])) + print("\nSet watchdog timeout: %s\n" % action[2]) + else: + print_help() + elif len(action) == 1: + watchdog_object.arm(DEFAULT_TIMEOUT) + print("\nSet watchdog default timeout: %s\n" % DEFAULT_TIMEOUT) + else: + print_help() + else: + print_help() + except Exception: + print_help() + + +if __name__ == '__main__': + main() diff --git a/packages/platforms/celestica/x86-64/ds4101/platform-config/r0/src/python/x86_64_cls_ds4101_r0/wdt/wdt_control.py b/packages/platforms/celestica/x86-64/ds4101/platform-config/r0/src/python/x86_64_cls_ds4101_r0/wdt/wdt_control.py new file mode 100755 index 0000000000..1c1d0ebf38 --- /dev/null +++ b/packages/platforms/celestica/x86-64/ds4101/platform-config/r0/src/python/x86_64_cls_ds4101_r0/wdt/wdt_control.py @@ -0,0 +1,10 @@ +import watch_dog +import time + +def arm_wdt(): + wdt_object = watch_dog.Watchdog() + while True: + wdt_object.arm(180) + time.sleep(160) + +arm_wdt()