Add ONL based on Debian 9 and kernel 4.9.30 support for Mellanox platforms MSN2700, MSN2410, MSN2100.

Signed-off-by: Michael Shych <michaelsh@mellanox.com>
This commit is contained in:
Michael Shych
2017-11-13 12:51:57 +00:00
parent dfa47836d5
commit 2624a062b8
35 changed files with 6934 additions and 74 deletions

View File

@@ -41,6 +41,7 @@
- usbutils
- mtd-utils
- i2c-tools
- kmod
- isc-dhcp-client
- ntp
- wget

View File

@@ -9,7 +9,6 @@
- grub2
- onl-upgrade
- hw-management
- sx-kernel
- onl-kernel-3.16-lts-x86-64-all-modules
- onl-kernel-4.9-lts-x86-64-all-modules
- efibootmgr
- gdisk

View File

@@ -399,7 +399,6 @@ CONFIG_X86_EXTENDED_PLATFORM=y
# CONFIG_X86_VSMP is not set
# CONFIG_X86_GOLDFISH is not set
# CONFIG_X86_INTEL_MID is not set
# CONFIG_MLX_PLATFORM is not set
# CONFIG_X86_INTEL_LPSS is not set
# CONFIG_X86_AMD_PLATFORM_DEVICE is not set
# CONFIG_IOSF_MBI is not set
@@ -1143,6 +1142,7 @@ CONFIG_DEBUG_DEVRES=y
CONFIG_GENERIC_CPU_AUTOPROBE=y
CONFIG_REGMAP=y
CONFIG_REGMAP_I2C=y
CONFIG_REGMAP_SPI=y
CONFIG_DMA_SHARED_BUFFER=y
# CONFIG_FENCE_TRACE is not set
@@ -1207,17 +1207,21 @@ CONFIG_VIRTIO_BLK=y
# CONFIG_SENSORS_APDS990X is not set
# CONFIG_HMC6352 is not set
# CONFIG_DS1682 is not set
# CONFIG_TI_DAC7512 is not set
# CONFIG_USB_SWITCH_FSA9480 is not set
# CONFIG_LATTICE_ECP3_CONFIG is not set
# CONFIG_SRAM is not set
# CONFIG_C2PORT is not set
#
# EEPROM support
#
# CONFIG_EEPROM_AT24 is not set
CONFIG_EEPROM_AT24=m
# CONFIG_EEPROM_AT25 is not set
# CONFIG_EEPROM_LEGACY is not set
# CONFIG_EEPROM_MAX6875 is not set
# CONFIG_EEPROM_93CX6 is not set
# CONFIG_EEPROM_93XX46 is not set
# CONFIG_CB710_CORE is not set
#
@@ -1580,11 +1584,21 @@ CONFIG_NET_VENDOR_MELLANOX=y
# CONFIG_MLX4_EN is not set
# CONFIG_MLX4_CORE is not set
# CONFIG_MLX5_CORE is not set
# CONFIG_MLXSW_CORE is not set
CONFIG_MLXSW_CORE=y
CONFIG_MLXSW_CORE_HWMON=y
CONFIG_MLXSW_CORE_THERMAL=y
CONFIG_MLXSW_CORE_QSFP=y
# CONFIG_MLXSW_PCI is not set
CONFIG_MLXSW_I2C=y
CONFIG_MLXSW_MINIMAL=y
CONFIG_NET_VENDOR_MICREL=y
# CONFIG_KS8842 is not set
# CONFIG_KS8851 is not set
# CONFIG_KS8851_MLL is not set
# CONFIG_KSZ884X_PCI is not set
CONFIG_NET_VENDOR_MICROCHIP=y
# CONFIG_ENC28J60 is not set
# CONFIG_ENCX24J600 is not set
CONFIG_NET_VENDOR_MYRI=y
# CONFIG_MYRI10GE is not set
# CONFIG_FEALNX is not set
@@ -1704,6 +1718,7 @@ CONFIG_PHYLIB=y
# CONFIG_TERANETICS_PHY is not set
# CONFIG_VITESSE_PHY is not set
# CONFIG_XILINX_GMII2RGMII is not set
# CONFIG_MICREL_KS8995MA is not set
# CONFIG_PPP is not set
# CONFIG_SLIP is not set
CONFIG_USB_NET_DRIVERS=y
@@ -1827,6 +1842,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
@@ -1899,6 +1915,8 @@ CONFIG_INPUT_TABLET=y
# CONFIG_TABLET_SERIAL_WACOM4 is not set
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_PROPERTIES=y
# CONFIG_TOUCHSCREEN_ADS7846 is not set
# CONFIG_TOUCHSCREEN_AD7877 is not set
# CONFIG_TOUCHSCREEN_AD7879 is not set
# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set
# CONFIG_TOUCHSCREEN_BU21013 is not set
@@ -1933,6 +1951,7 @@ CONFIG_TOUCHSCREEN_PROPERTIES=y
# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
# CONFIG_TOUCHSCREEN_TSC_SERIO is not set
# CONFIG_TOUCHSCREEN_TSC2004 is not set
# CONFIG_TOUCHSCREEN_TSC2005 is not set
# CONFIG_TOUCHSCREEN_TSC2007 is not set
# CONFIG_TOUCHSCREEN_SILEAD is not set
# CONFIG_TOUCHSCREEN_ST1232 is not set
@@ -2038,6 +2057,8 @@ CONFIG_SERIAL_8250_MID=y
#
# Non-8250 serial port support
#
# CONFIG_SERIAL_MAX3100 is not set
# CONFIG_SERIAL_MAX310X is not set
# CONFIG_SERIAL_UARTLITE is not set
CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
@@ -2086,8 +2107,15 @@ CONFIG_I2C=y
CONFIG_ACPI_I2C_OPREGION=y
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_COMPAT=y
# CONFIG_I2C_CHARDEV is not set
# CONFIG_I2C_MUX is not set
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_MUX=y
#
# Multiplexer I2C Chip support
#
# CONFIG_I2C_MUX_PCA9541 is not set
CONFIG_I2C_MUX_REG=y
CONFIG_I2C_MUX_MLXCPLD=y
CONFIG_I2C_HELPER_AUTO=y
CONFIG_I2C_SMBUS=y
CONFIG_I2C_ALGOBIT=y
@@ -2142,12 +2170,38 @@ CONFIG_I2C_I801=y
#
# Other I2C/SMBus bus drivers
#
CONFIG_I2C_MLXCPLD=y
# CONFIG_I2C_STUB is not set
# CONFIG_I2C_SLAVE is not set
# CONFIG_I2C_DEBUG_CORE is not set
# CONFIG_I2C_DEBUG_ALGO is not set
# CONFIG_I2C_DEBUG_BUS is not set
# CONFIG_SPI is not set
CONFIG_SPI=y
# CONFIG_SPI_DEBUG is not set
CONFIG_SPI_MASTER=y
#
# SPI Master Controller Drivers
#
# CONFIG_SPI_ALTERA is not set
# CONFIG_SPI_AXI_SPI_ENGINE is not set
# CONFIG_SPI_BITBANG is not set
# CONFIG_SPI_CADENCE is not set
# CONFIG_SPI_DESIGNWARE is not set
# CONFIG_SPI_PXA2XX is not set
# CONFIG_SPI_PXA2XX_PCI is not set
# CONFIG_SPI_ROCKCHIP is not set
# CONFIG_SPI_SC18IS602 is not set
# CONFIG_SPI_XCOMM is not set
# CONFIG_SPI_XILINX is not set
# CONFIG_SPI_ZYNQMP_GQSPI is not set
#
# SPI Protocol Masters
#
# CONFIG_SPI_SPIDEV is not set
# CONFIG_SPI_LOOPBACK_TEST is not set
# CONFIG_SPI_TLE62X0 is not set
# CONFIG_SPMI is not set
# CONFIG_HSI is not set
@@ -2183,6 +2237,7 @@ CONFIG_PTP_1588_CLOCK=y
CONFIG_POWER_SUPPLY=y
# CONFIG_POWER_SUPPLY_DEBUG is not set
# CONFIG_PDA_POWER is not set
# CONFIG_GENERIC_ADC_BATTERY is not set
# CONFIG_TEST_POWER is not set
# CONFIG_BATTERY_DS2780 is not set
# CONFIG_BATTERY_DS2781 is not set
@@ -2205,6 +2260,7 @@ CONFIG_HWMON=y
#
# CONFIG_SENSORS_ABITUGURU is not set
# CONFIG_SENSORS_ABITUGURU3 is not set
# CONFIG_SENSORS_AD7314 is not set
# CONFIG_SENSORS_AD7414 is not set
# CONFIG_SENSORS_AD7418 is not set
# CONFIG_SENSORS_ADM1021 is not set
@@ -2213,6 +2269,7 @@ CONFIG_HWMON=y
# CONFIG_SENSORS_ADM1029 is not set
# CONFIG_SENSORS_ADM1031 is not set
# CONFIG_SENSORS_ADM9240 is not set
# CONFIG_SENSORS_ADT7310 is not set
# CONFIG_SENSORS_ADT7410 is not set
# CONFIG_SENSORS_ADT7411 is not set
# CONFIG_SENSORS_ADT7462 is not set
@@ -2239,8 +2296,9 @@ 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=y
# CONFIG_SENSORS_I5500 is not set
# CONFIG_SENSORS_CORETEMP is not set
CONFIG_SENSORS_CORETEMP=y
# CONFIG_SENSORS_IT87 is not set
# CONFIG_SENSORS_JC42 is not set
# CONFIG_SENSORS_POWR1220 is not set
@@ -2253,19 +2311,23 @@ CONFIG_HWMON=y
# CONFIG_SENSORS_LTC4245 is not set
# CONFIG_SENSORS_LTC4260 is not set
# CONFIG_SENSORS_LTC4261 is not set
# CONFIG_SENSORS_MAX1111 is not set
# CONFIG_SENSORS_MAX16065 is not set
# CONFIG_SENSORS_MAX1619 is not set
# CONFIG_SENSORS_MAX1668 is not set
# CONFIG_SENSORS_MAX197 is not set
# CONFIG_SENSORS_MAX31722 is not set
# CONFIG_SENSORS_MAX6639 is not set
# CONFIG_SENSORS_MAX6642 is not set
# CONFIG_SENSORS_MAX6650 is not set
# CONFIG_SENSORS_MAX6697 is not set
# CONFIG_SENSORS_MAX31790 is not set
# CONFIG_SENSORS_MCP3021 is not set
# CONFIG_SENSORS_ADCXX is not set
# CONFIG_SENSORS_LM63 is not set
# CONFIG_SENSORS_LM70 is not set
# CONFIG_SENSORS_LM73 is not set
# CONFIG_SENSORS_LM75 is not set
CONFIG_SENSORS_LM75=y
# CONFIG_SENSORS_LM77 is not set
# CONFIG_SENSORS_LM78 is not set
# CONFIG_SENSORS_LM80 is not set
@@ -2286,7 +2348,21 @@ CONFIG_HWMON=y
# CONFIG_SENSORS_NCT7802 is not set
# CONFIG_SENSORS_NCT7904 is not set
# CONFIG_SENSORS_PCF8591 is not set
# CONFIG_PMBUS is not set
CONFIG_PMBUS=y
CONFIG_SENSORS_PMBUS=y
# CONFIG_SENSORS_ADM1275 is not set
CONFIG_SENSORS_LM25066=y
# CONFIG_SENSORS_LTC2978 is not set
# CONFIG_SENSORS_LTC3815 is not set
# CONFIG_SENSORS_MAX16064 is not set
# CONFIG_SENSORS_MAX20751 is not set
# CONFIG_SENSORS_MAX34440 is not set
# CONFIG_SENSORS_MAX8688 is not set
# CONFIG_SENSORS_TPS40422 is not set
CONFIG_SENSORS_TPS53679=y
CONFIG_SENSORS_UCD9000=y
CONFIG_SENSORS_UCD9200=y
# CONFIG_SENSORS_ZL6100 is not set
# CONFIG_SENSORS_SHT21 is not set
# CONFIG_SENSORS_SHT3x is not set
# CONFIG_SENSORS_SHTC1 is not set
@@ -2305,13 +2381,14 @@ CONFIG_HWMON=y
# CONFIG_SENSORS_ADC128D818 is not set
# CONFIG_SENSORS_ADS1015 is not set
# CONFIG_SENSORS_ADS7828 is not set
# CONFIG_SENSORS_ADS7871 is not set
# CONFIG_SENSORS_AMC6821 is not set
# CONFIG_SENSORS_INA209 is not set
# CONFIG_SENSORS_INA2XX is not set
# CONFIG_SENSORS_INA3221 is not set
# CONFIG_SENSORS_TC74 is not set
# CONFIG_SENSORS_THMC50 is not set
# CONFIG_SENSORS_TMP102 is not set
CONFIG_SENSORS_TMP102=y
# CONFIG_SENSORS_TMP103 is not set
# CONFIG_SENSORS_TMP401 is not set
# CONFIG_SENSORS_TMP421 is not set
@@ -2356,6 +2433,7 @@ CONFIG_X86_PKG_TEMP_THERMAL=m
#
# CONFIG_INT340X_THERMAL is not set
# CONFIG_INTEL_PCH_THERMAL is not set
# CONFIG_GENERIC_ADC_THERMAL is not set
CONFIG_WATCHDOG=y
# CONFIG_WATCHDOG_CORE is not set
# CONFIG_WATCHDOG_NOWAYOUT is not set
@@ -2441,6 +2519,7 @@ CONFIG_BCMA_POSSIBLE=y
# CONFIG_MFD_AXP20X_I2C is not set
# CONFIG_MFD_CROS_EC is not set
# CONFIG_PMIC_DA903X is not set
# CONFIG_MFD_DA9052_SPI is not set
# CONFIG_MFD_DA9052_I2C is not set
# CONFIG_MFD_DA9055 is not set
# CONFIG_MFD_DA9062 is not set
@@ -2448,6 +2527,7 @@ CONFIG_BCMA_POSSIBLE=y
# CONFIG_MFD_DA9150 is not set
# CONFIG_MFD_DLN2 is not set
# CONFIG_MFD_EXYNOS_LPASS is not set
# CONFIG_MFD_MC13XXX_SPI is not set
# CONFIG_MFD_MC13XXX_I2C is not set
# CONFIG_HTC_PASIC3 is not set
# CONFIG_LPC_ICH is not set
@@ -2468,6 +2548,7 @@ CONFIG_BCMA_POSSIBLE=y
# CONFIG_MFD_MAX8998 is not set
# CONFIG_MFD_MT6397 is not set
# CONFIG_MFD_MENF21BMC is not set
# CONFIG_EZX_PCAP is not set
# CONFIG_MFD_VIPERBOARD is not set
# CONFIG_MFD_RETU is not set
# CONFIG_MFD_PCF50633 is not set
@@ -2496,6 +2577,7 @@ CONFIG_BCMA_POSSIBLE=y
# CONFIG_MFD_TPS65218 is not set
# CONFIG_MFD_TPS6586X is not set
# CONFIG_MFD_TPS65912_I2C is not set
# CONFIG_MFD_TPS65912_SPI is not set
# CONFIG_MFD_TPS80031 is not set
# CONFIG_TWL4030_CORE is not set
# CONFIG_TWL6040_CORE is not set
@@ -2504,8 +2586,10 @@ CONFIG_BCMA_POSSIBLE=y
# CONFIG_MFD_TMIO is not set
# CONFIG_MFD_VX855 is not set
# CONFIG_MFD_ARIZONA_I2C is not set
# CONFIG_MFD_ARIZONA_SPI is not set
# CONFIG_MFD_WM8400 is not set
# CONFIG_MFD_WM831X_I2C is not set
# CONFIG_MFD_WM831X_SPI is not set
# CONFIG_MFD_WM8350_I2C is not set
# CONFIG_MFD_WM8994 is not set
# CONFIG_REGULATOR is not set
@@ -2806,6 +2890,7 @@ CONFIG_SND_HDA_POWER_SAVE_DEFAULT=0
CONFIG_SND_HDA_CORE=y
CONFIG_SND_HDA_I915=y
CONFIG_SND_HDA_PREALLOC_SIZE=64
# CONFIG_SND_SPI is not set
CONFIG_SND_USB=y
# CONFIG_SND_USB_AUDIO is not set
# CONFIG_SND_USB_UA101 is not set
@@ -2961,6 +3046,7 @@ CONFIG_USB_EHCI_PCI=y
# CONFIG_USB_ISP116X_HCD is not set
# CONFIG_USB_ISP1362_HCD is not set
# CONFIG_USB_FOTG210_HCD is not set
# CONFIG_USB_MAX3421_HCD is not set
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_OHCI_HCD_PCI=y
# CONFIG_USB_OHCI_HCD_PLATFORM is not set
@@ -3079,6 +3165,7 @@ CONFIG_LEDS_CLASS=y
# CONFIG_LEDS_CLEVO_MAIL is not set
# CONFIG_LEDS_PCA955X is not set
# CONFIG_LEDS_PCA963X is not set
# CONFIG_LEDS_DAC124S085 is not set
# CONFIG_LEDS_BD2802 is not set
# CONFIG_LEDS_INTEL_SS4200 is not set
# CONFIG_LEDS_TCA6507 is not set
@@ -3090,12 +3177,15 @@ CONFIG_LEDS_CLASS=y
#
# CONFIG_LEDS_BLINKM is not set
# CONFIG_LEDS_MLXCPLD is not set
CONFIG_LEDS_MLXREG=y
# CONFIG_LEDS_USER is not set
# CONFIG_LEDS_NIC78BX is not set
#
# LED Triggers
#
CONFIG_LEDS_TRIGGERS=y
# CONFIG_LEDS_TRIGGER_TIMER is not set
CONFIG_LEDS_TRIGGER_TIMER=y
# CONFIG_LEDS_TRIGGER_ONESHOT is not set
# CONFIG_LEDS_TRIGGER_DISK is not set
# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set
@@ -3165,6 +3255,21 @@ CONFIG_RTC_INTF_DEV=y
#
# SPI RTC drivers
#
# CONFIG_RTC_DRV_M41T93 is not set
# CONFIG_RTC_DRV_M41T94 is not set
# CONFIG_RTC_DRV_DS1302 is not set
# CONFIG_RTC_DRV_DS1305 is not set
# CONFIG_RTC_DRV_DS1343 is not set
# CONFIG_RTC_DRV_DS1347 is not set
# CONFIG_RTC_DRV_DS1390 is not set
# CONFIG_RTC_DRV_MAX6916 is not set
# CONFIG_RTC_DRV_R9701 is not set
# CONFIG_RTC_DRV_RX4581 is not set
# CONFIG_RTC_DRV_RX6110 is not set
# CONFIG_RTC_DRV_RS5C348 is not set
# CONFIG_RTC_DRV_MAX6902 is not set
# CONFIG_RTC_DRV_PCF2123 is not set
# CONFIG_RTC_DRV_MCP795 is not set
CONFIG_RTC_I2C_AND_SPI=y
#
@@ -3289,7 +3394,10 @@ CONFIG_EEEPC_LAPTOP=y
# CONFIG_INTEL_PMC_IPC is not set
# CONFIG_SURFACE_PRO3_BUTTON is not set
# CONFIG_INTEL_PUNIT_IPC is not set
CONFIG_MLX_PLATFORM=y
# CONFIG_CHROME_PLATFORMS is not set
CONFIG_MELLANOX_PLATFORM=y
CONFIG_MLXREG_HOTPLUG=y
#
# Hardware Spinlock drivers
@@ -3344,7 +3452,269 @@ 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=y
CONFIG_IIO_BUFFER=y
# CONFIG_IIO_BUFFER_CB is not set
CONFIG_IIO_KFIFO_BUF=y
CONFIG_IIO_TRIGGERED_BUFFER=y
# 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_BMA180 is not set
# CONFIG_BMA220 is not set
# CONFIG_BMC150_ACCEL is not set
# CONFIG_DMARD09 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_MMA7455_SPI 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
#
# Analog to digital converters
#
# CONFIG_AD7266 is not set
# CONFIG_AD7291 is not set
# CONFIG_AD7298 is not set
# CONFIG_AD7476 is not set
# CONFIG_AD7791 is not set
# CONFIG_AD7793 is not set
# CONFIG_AD7887 is not set
# CONFIG_AD7923 is not set
# CONFIG_AD799X is not set
# CONFIG_HI8435 is not set
# CONFIG_INA2XX_ADC is not set
# CONFIG_LTC2485 is not set
# CONFIG_MAX1027 is not set
CONFIG_MAX1363=y
# CONFIG_MCP320X is not set
# CONFIG_MCP3422 is not set
# CONFIG_NAU7802 is not set
# CONFIG_TI_ADC081C is not set
# CONFIG_TI_ADC0832 is not set
# CONFIG_TI_ADC12138 is not set
# CONFIG_TI_ADC128S052 is not set
# CONFIG_TI_ADC161S626 is not set
# CONFIG_TI_ADS1015 is not set
#
# Amplifiers
#
# CONFIG_AD8366 is not set
#
# Chemical Sensors
#
# CONFIG_ATLAS_PH_SENSOR is not set
# CONFIG_IAQCORE is not set
# CONFIG_VZ89X is not set
#
# Hid Sensor IIO Common
#
#
# SSP Sensor Common
#
# CONFIG_IIO_SSP_SENSORHUB is not set
#
# Digital to analog converters
#
# CONFIG_AD5064 is not set
# CONFIG_AD5360 is not set
# CONFIG_AD5380 is not set
# CONFIG_AD5421 is not set
# CONFIG_AD5446 is not set
# CONFIG_AD5449 is not set
# CONFIG_AD5592R is not set
# CONFIG_AD5593R is not set
# CONFIG_AD5504 is not set
# CONFIG_AD5624R_SPI is not set
# CONFIG_AD5686 is not set
# CONFIG_AD5755 is not set
# CONFIG_AD5761 is not set
# CONFIG_AD5764 is not set
# CONFIG_AD5791 is not set
# CONFIG_AD7303 is not set
# CONFIG_AD8801 is not set
# CONFIG_M62332 is not set
# CONFIG_MAX517 is not set
# CONFIG_MCP4725 is not set
# CONFIG_MCP4922 is not set
#
# IIO dummy driver
#
#
# Frequency Synthesizers DDS/PLL
#
#
# Clock Generator/Distribution
#
# CONFIG_AD9523 is not set
#
# Phase-Locked Loop (PLL) frequency synthesizers
#
# CONFIG_ADF4350 is not set
#
# Digital gyroscope sensors
#
# CONFIG_ADIS16080 is not set
# CONFIG_ADIS16130 is not set
# CONFIG_ADIS16136 is not set
# CONFIG_ADIS16260 is not set
# CONFIG_ADXRS450 is not set
# CONFIG_BMG160 is not set
# CONFIG_IIO_ST_GYRO_3AXIS is not set
# CONFIG_ITG3200 is not set
#
# Health Sensors
#
#
# Heart Rate Monitors
#
# CONFIG_AFE4403 is not set
# CONFIG_AFE4404 is not set
# CONFIG_MAX30100 is not set
#
# Humidity sensors
#
# CONFIG_AM2315 is not set
# CONFIG_HDC100X is not set
# CONFIG_HTU21 is not set
# CONFIG_SI7005 is not set
# CONFIG_SI7020 is not set
#
# Inertial measurement units
#
# CONFIG_ADIS16400 is not set
# CONFIG_ADIS16480 is not set
# CONFIG_BMI160_I2C is not set
# CONFIG_BMI160_SPI is not set
# CONFIG_KMX61 is not set
# CONFIG_INV_MPU6050_I2C is not set
# CONFIG_INV_MPU6050_SPI is not set
#
# 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_ISL29125 is not set
# CONFIG_JSA1212 is not set
# CONFIG_RPR0521 is not set
# CONFIG_LTR501 is not set
# CONFIG_MAX44000 is not set
# CONFIG_OPT3001 is not set
# CONFIG_PA12203001 is not set
# CONFIG_SI1145 is not set
# CONFIG_STK3310 is not set
# CONFIG_TCS3414 is not set
# CONFIG_TCS3472 is not set
# CONFIG_SENSORS_TSL2563 is not set
# CONFIG_TSL4531 is not set
# CONFIG_US5182D is not set
# CONFIG_VCNL4000 is not set
# CONFIG_VEML6070 is not set
#
# Magnetometer sensors
#
# CONFIG_BMC150_MAGN_I2C is not set
# CONFIG_BMC150_MAGN_SPI 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_HMC5843_SPI is not set
#
# Inclinometer sensors
#
#
# Triggers - standalone
#
# CONFIG_IIO_INTERRUPT_TRIGGER is not set
# CONFIG_IIO_SYSFS_TRIGGER is not set
#
# Digital potentiometers
#
# CONFIG_DS1803 is not set
# CONFIG_MAX5487 is not set
# CONFIG_MCP4131 is not set
# CONFIG_MCP4531 is not set
# CONFIG_TPL0102 is not set
#
# Pressure sensors
#
# CONFIG_BMP280 is not set
# CONFIG_HP03 is not set
# CONFIG_MPL115_I2C is not set
# CONFIG_MPL115_SPI is not set
# CONFIG_MPL3115 is not set
# 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
#
# Lightning sensors
#
# CONFIG_AS3935 is not set
#
# Proximity sensors
#
# CONFIG_LIDAR_LITE_V2 is not set
# CONFIG_SX9500 is not set
#
# Temperature sensors
#
# CONFIG_MAXIM_THERMOCOUPLE is not set
# CONFIG_MLX90614 is not set
# CONFIG_TMP006 is not set
# CONFIG_TSYS01 is not set
# CONFIG_TSYS02D is not set
# CONFIG_NTB is not set
# CONFIG_VME_BUS is not set
# CONFIG_PWM is not set
@@ -3375,7 +3745,7 @@ CONFIG_RAS=y
#
# CONFIG_ANDROID is not set
# CONFIG_LIBNVDIMM is not set
# CONFIG_NVMEM is not set
CONFIG_NVMEM=m
# CONFIG_STM is not set
# CONFIG_INTEL_TH is not set

View File

@@ -0,0 +1,544 @@
Linux backport patch. Includes following commits:
899e11216e1c215b97f2f8f92c7b010a4e88f38e
diff -Nur a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
--- a/drivers/i2c/busses/Kconfig 2017-11-12 08:08:32.136039784 +0000
+++ b/drivers/i2c/busses/Kconfig 2017-11-12 08:08:40.776039899 +0000
@@ -1150,6 +1150,17 @@
This support is also available as a module. If so, the module
will be called i2c-elektor.
+config I2C_MLXCPLD
+ tristate "Mellanox I2C driver"
+ depends on X86_64
+ help
+ This exposes the Mellanox platform I2C busses to the linux I2C layer
+ for X86 based systems.
+ Controller is implemented as CPLD logic.
+
+ This driver can also be built as a module. If so, the module will be
+ called as i2c-mlxcpld.
+
config I2C_PCA_ISA
tristate "PCA9564/PCA9665 on an ISA bus"
depends on ISA
diff -Nur a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
--- a/drivers/i2c/busses/Makefile 2017-11-12 08:08:32.140039784 +0000
+++ b/drivers/i2c/busses/Makefile 2017-11-12 08:08:40.780039899 +0000
@@ -116,6 +116,7 @@
obj-$(CONFIG_I2C_BRCMSTB) += i2c-brcmstb.o
obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += i2c-cros-ec-tunnel.o
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
+obj-$(CONFIG_I2C_MLXCPLD) += i2c-mlxcpld.o
obj-$(CONFIG_I2C_OPAL) += i2c-opal.o
obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
diff -Nur a/drivers/i2c/busses/i2c-mlxcpld.c b/drivers/i2c/busses/i2c-mlxcpld.c
--- a/drivers/i2c/busses/i2c-mlxcpld.c 1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/i2c/busses/i2c-mlxcpld.c 2017-11-12 08:08:40.780039899 +0000
@@ -0,0 +1,504 @@
+/*
+ * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2016 Michael Shych <michaels@mellanox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+/* General defines */
+#define MLXPLAT_CPLD_LPC_I2C_BASE_ADDR 0x2000
+#define MLXCPLD_I2C_DEVICE_NAME "i2c_mlxcpld"
+#define MLXCPLD_I2C_VALID_FLAG (I2C_M_RECV_LEN | I2C_M_RD)
+#define MLXCPLD_I2C_BUS_NUM 1
+#define MLXCPLD_I2C_DATA_REG_SZ 36
+#define MLXCPLD_I2C_MAX_ADDR_LEN 4
+#define MLXCPLD_I2C_RETR_NUM 2
+#define MLXCPLD_I2C_XFER_TO 500000 /* usec */
+#define MLXCPLD_I2C_POLL_TIME 2000 /* usec */
+
+/* LPC I2C registers */
+#define MLXCPLD_LPCI2C_LPF_REG 0x0
+#define MLXCPLD_LPCI2C_CTRL_REG 0x1
+#define MLXCPLD_LPCI2C_HALF_CYC_REG 0x4
+#define MLXCPLD_LPCI2C_I2C_HOLD_REG 0x5
+#define MLXCPLD_LPCI2C_CMD_REG 0x6
+#define MLXCPLD_LPCI2C_NUM_DAT_REG 0x7
+#define MLXCPLD_LPCI2C_NUM_ADDR_REG 0x8
+#define MLXCPLD_LPCI2C_STATUS_REG 0x9
+#define MLXCPLD_LPCI2C_DATA_REG 0xa
+
+/* LPC I2C masks and parametres */
+#define MLXCPLD_LPCI2C_RST_SEL_MASK 0x1
+#define MLXCPLD_LPCI2C_TRANS_END 0x1
+#define MLXCPLD_LPCI2C_STATUS_NACK 0x10
+#define MLXCPLD_LPCI2C_NO_IND 0
+#define MLXCPLD_LPCI2C_ACK_IND 1
+#define MLXCPLD_LPCI2C_NACK_IND 2
+
+struct mlxcpld_i2c_curr_xfer {
+ u8 cmd;
+ u8 addr_width;
+ u8 data_len;
+ u8 msg_num;
+ struct i2c_msg *msg;
+};
+
+struct mlxcpld_i2c_priv {
+ struct i2c_adapter adap;
+ u32 base_addr;
+ struct mutex lock;
+ struct mlxcpld_i2c_curr_xfer xfer;
+ struct device *dev;
+};
+
+static void mlxcpld_i2c_lpc_write_buf(u8 *data, u8 len, u32 addr)
+{
+ int i;
+
+ for (i = 0; i < len - len % 4; i += 4)
+ outl(*(u32 *)(data + i), addr + i);
+ for (; i < len; ++i)
+ outb(*(data + i), addr + i);
+}
+
+static void mlxcpld_i2c_lpc_read_buf(u8 *data, u8 len, u32 addr)
+{
+ int i;
+
+ for (i = 0; i < len - len % 4; i += 4)
+ *(u32 *)(data + i) = inl(addr + i);
+ for (; i < len; ++i)
+ *(data + i) = inb(addr + i);
+}
+
+static void mlxcpld_i2c_read_comm(struct mlxcpld_i2c_priv *priv, u8 offs,
+ u8 *data, u8 datalen)
+{
+ u32 addr = priv->base_addr + offs;
+
+ switch (datalen) {
+ case 1:
+ *(data) = inb(addr);
+ break;
+ case 2:
+ *((u16 *)data) = inw(addr);
+ break;
+ case 3:
+ *((u16 *)data) = inw(addr);
+ *(data + 2) = inb(addr + 2);
+ break;
+ case 4:
+ *((u32 *)data) = inl(addr);
+ break;
+ default:
+ mlxcpld_i2c_lpc_read_buf(data, datalen, addr);
+ break;
+ }
+}
+
+static void mlxcpld_i2c_write_comm(struct mlxcpld_i2c_priv *priv, u8 offs,
+ u8 *data, u8 datalen)
+{
+ u32 addr = priv->base_addr + offs;
+
+ switch (datalen) {
+ case 1:
+ outb(*(data), addr);
+ break;
+ case 2:
+ outw(*((u16 *)data), addr);
+ break;
+ case 3:
+ outw(*((u16 *)data), addr);
+ outb(*(data + 2), addr + 2);
+ break;
+ case 4:
+ outl(*((u32 *)data), addr);
+ break;
+ default:
+ mlxcpld_i2c_lpc_write_buf(data, datalen, addr);
+ break;
+ }
+}
+
+/*
+ * Check validity of received i2c messages parameters.
+ * Returns 0 if OK, other - in case of invalid parameters.
+ */
+static int mlxcpld_i2c_check_msg_params(struct mlxcpld_i2c_priv *priv,
+ struct i2c_msg *msgs, int num)
+{
+ int i;
+
+ if (!num) {
+ dev_err(priv->dev, "Incorrect 0 num of messages\n");
+ return -EINVAL;
+ }
+
+ if (unlikely(msgs[0].addr > 0x7f)) {
+ dev_err(priv->dev, "Invalid address 0x%03x\n",
+ msgs[0].addr);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num; ++i) {
+ if (unlikely(!msgs[i].buf)) {
+ dev_err(priv->dev, "Invalid buf in msg[%d]\n",
+ i);
+ return -EINVAL;
+ }
+ if (unlikely(msgs[0].addr != msgs[i].addr)) {
+ dev_err(priv->dev, "Invalid addr in msg[%d]\n",
+ i);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Check if transfer is completed and status of operation.
+ * Returns 0 - transfer completed (both ACK or NACK),
+ * negative - transfer isn't finished.
+ */
+static int mlxcpld_i2c_check_status(struct mlxcpld_i2c_priv *priv, int *status)
+{
+ u8 val;
+
+ mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_STATUS_REG, &val, 1);
+
+ if (val & MLXCPLD_LPCI2C_TRANS_END) {
+ if (val & MLXCPLD_LPCI2C_STATUS_NACK)
+ /*
+ * The slave is unable to accept the data. No such
+ * slave, command not understood, or unable to accept
+ * any more data.
+ */
+ *status = MLXCPLD_LPCI2C_NACK_IND;
+ else
+ *status = MLXCPLD_LPCI2C_ACK_IND;
+ return 0;
+ }
+ *status = MLXCPLD_LPCI2C_NO_IND;
+
+ return -EIO;
+}
+
+static void mlxcpld_i2c_set_transf_data(struct mlxcpld_i2c_priv *priv,
+ struct i2c_msg *msgs, int num,
+ u8 comm_len)
+{
+ priv->xfer.msg = msgs;
+ priv->xfer.msg_num = num;
+
+ /*
+ * All upper layers currently are never use transfer with more than
+ * 2 messages. Actually, it's also not so relevant in Mellanox systems
+ * because of HW limitation. Max size of transfer is not more than 32
+ * bytes in the current x86 LPCI2C bridge.
+ */
+ priv->xfer.cmd = msgs[num - 1].flags & I2C_M_RD;
+
+ if (priv->xfer.cmd == I2C_M_RD && comm_len != msgs[0].len) {
+ priv->xfer.addr_width = msgs[0].len;
+ priv->xfer.data_len = comm_len - priv->xfer.addr_width;
+ } else {
+ priv->xfer.addr_width = 0;
+ priv->xfer.data_len = comm_len;
+ }
+}
+
+/* Reset CPLD LPCI2C block */
+static void mlxcpld_i2c_reset(struct mlxcpld_i2c_priv *priv)
+{
+ u8 val;
+
+ mutex_lock(&priv->lock);
+
+ mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_CTRL_REG, &val, 1);
+ val &= ~MLXCPLD_LPCI2C_RST_SEL_MASK;
+ mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_CTRL_REG, &val, 1);
+
+ mutex_unlock(&priv->lock);
+}
+
+/* Make sure the CPLD is ready to start transmitting. */
+static int mlxcpld_i2c_check_busy(struct mlxcpld_i2c_priv *priv)
+{
+ u8 val;
+
+ mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_STATUS_REG, &val, 1);
+
+ if (val & MLXCPLD_LPCI2C_TRANS_END)
+ return 0;
+
+ return -EIO;
+}
+
+static int mlxcpld_i2c_wait_for_free(struct mlxcpld_i2c_priv *priv)
+{
+ int timeout = 0;
+
+ do {
+ if (!mlxcpld_i2c_check_busy(priv))
+ break;
+ usleep_range(MLXCPLD_I2C_POLL_TIME / 2, MLXCPLD_I2C_POLL_TIME);
+ timeout += MLXCPLD_I2C_POLL_TIME;
+ } while (timeout <= MLXCPLD_I2C_XFER_TO);
+
+ if (timeout > MLXCPLD_I2C_XFER_TO)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+/*
+ * Wait for master transfer to complete.
+ * It puts current process to sleep until we get interrupt or timeout expires.
+ * Returns the number of transferred or read bytes or error (<0).
+ */
+static int mlxcpld_i2c_wait_for_tc(struct mlxcpld_i2c_priv *priv)
+{
+ int status, i, timeout = 0;
+ u8 datalen;
+
+ do {
+ usleep_range(MLXCPLD_I2C_POLL_TIME / 2, MLXCPLD_I2C_POLL_TIME);
+ if (!mlxcpld_i2c_check_status(priv, &status))
+ break;
+ timeout += MLXCPLD_I2C_POLL_TIME;
+ } while (status == 0 && timeout < MLXCPLD_I2C_XFER_TO);
+
+ switch (status) {
+ case MLXCPLD_LPCI2C_NO_IND:
+ return -ETIMEDOUT;
+
+ case MLXCPLD_LPCI2C_ACK_IND:
+ if (priv->xfer.cmd != I2C_M_RD)
+ return (priv->xfer.addr_width + priv->xfer.data_len);
+
+ if (priv->xfer.msg_num == 1)
+ i = 0;
+ else
+ i = 1;
+
+ if (!priv->xfer.msg[i].buf)
+ return -EINVAL;
+
+ /*
+ * Actual read data len will be always the same as
+ * requested len. 0xff (line pull-up) will be returned
+ * if slave has no data to return. Thus don't read
+ * MLXCPLD_LPCI2C_NUM_DAT_REG reg from CPLD.
+ */
+ datalen = priv->xfer.data_len;
+
+ mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_DATA_REG,
+ priv->xfer.msg[i].buf, datalen);
+
+ return datalen;
+
+ case MLXCPLD_LPCI2C_NACK_IND:
+ return -ENXIO;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static void mlxcpld_i2c_xfer_msg(struct mlxcpld_i2c_priv *priv)
+{
+ int i, len = 0;
+ u8 cmd;
+
+ mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_NUM_DAT_REG,
+ &priv->xfer.data_len, 1);
+ mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_NUM_ADDR_REG,
+ &priv->xfer.addr_width, 1);
+
+ for (i = 0; i < priv->xfer.msg_num; i++) {
+ if ((priv->xfer.msg[i].flags & I2C_M_RD) != I2C_M_RD) {
+ /* Don't write to CPLD buffer in read transaction */
+ mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_DATA_REG +
+ len, priv->xfer.msg[i].buf,
+ priv->xfer.msg[i].len);
+ len += priv->xfer.msg[i].len;
+ }
+ }
+
+ /*
+ * Set target slave address with command for master transfer.
+ * It should be latest executed function before CPLD transaction.
+ */
+ cmd = (priv->xfer.msg[0].addr << 1) | priv->xfer.cmd;
+ mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_CMD_REG, &cmd, 1);
+}
+
+/*
+ * Generic lpc-i2c transfer.
+ * Returns the number of processed messages or error (<0).
+ */
+static int mlxcpld_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct mlxcpld_i2c_priv *priv = i2c_get_adapdata(adap);
+ u8 comm_len = 0;
+ int i, err;
+
+ err = mlxcpld_i2c_check_msg_params(priv, msgs, num);
+ if (err) {
+ dev_err(priv->dev, "Incorrect message\n");
+ return err;
+ }
+
+ for (i = 0; i < num; ++i)
+ comm_len += msgs[i].len;
+
+ /* Check bus state */
+ if (mlxcpld_i2c_wait_for_free(priv)) {
+ dev_err(priv->dev, "LPCI2C bridge is busy\n");
+
+ /*
+ * Usually it means something serious has happened.
+ * We can not have unfinished previous transfer
+ * so it doesn't make any sense to try to stop it.
+ * Probably we were not able to recover from the
+ * previous error.
+ * The only reasonable thing - is soft reset.
+ */
+ mlxcpld_i2c_reset(priv);
+ if (mlxcpld_i2c_check_busy(priv)) {
+ dev_err(priv->dev, "LPCI2C bridge is busy after reset\n");
+ return -EIO;
+ }
+ }
+
+ mlxcpld_i2c_set_transf_data(priv, msgs, num, comm_len);
+
+ mutex_lock(&priv->lock);
+
+ /* Do real transfer. Can't fail */
+ mlxcpld_i2c_xfer_msg(priv);
+
+ /* Wait for transaction complete */
+ err = mlxcpld_i2c_wait_for_tc(priv);
+
+ mutex_unlock(&priv->lock);
+
+ return err < 0 ? err : num;
+}
+
+static u32 mlxcpld_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static const struct i2c_algorithm mlxcpld_i2c_algo = {
+ .master_xfer = mlxcpld_i2c_xfer,
+ .functionality = mlxcpld_i2c_func
+};
+
+static struct i2c_adapter_quirks mlxcpld_i2c_quirks = {
+ .flags = I2C_AQ_COMB_WRITE_THEN_READ,
+ .max_read_len = MLXCPLD_I2C_DATA_REG_SZ - MLXCPLD_I2C_MAX_ADDR_LEN,
+ .max_write_len = MLXCPLD_I2C_DATA_REG_SZ,
+ .max_comb_1st_msg_len = 4,
+};
+
+static struct i2c_adapter mlxcpld_i2c_adapter = {
+ .owner = THIS_MODULE,
+ .name = "i2c-mlxcpld",
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .algo = &mlxcpld_i2c_algo,
+ .quirks = &mlxcpld_i2c_quirks,
+ .retries = MLXCPLD_I2C_RETR_NUM,
+ .nr = MLXCPLD_I2C_BUS_NUM,
+};
+
+static int mlxcpld_i2c_probe(struct platform_device *pdev)
+{
+ struct mlxcpld_i2c_priv *priv;
+ int err;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mutex_init(&priv->lock);
+ platform_set_drvdata(pdev, priv);
+
+ priv->dev = &pdev->dev;
+
+ /* Register with i2c layer */
+ mlxcpld_i2c_adapter.timeout = usecs_to_jiffies(MLXCPLD_I2C_XFER_TO);
+ priv->adap = mlxcpld_i2c_adapter;
+ priv->adap.dev.parent = &pdev->dev;
+ priv->base_addr = MLXPLAT_CPLD_LPC_I2C_BASE_ADDR;
+ i2c_set_adapdata(&priv->adap, priv);
+
+ err = i2c_add_numbered_adapter(&priv->adap);
+ if (err)
+ mutex_destroy(&priv->lock);
+
+ return err;
+}
+
+static int mlxcpld_i2c_remove(struct platform_device *pdev)
+{
+ struct mlxcpld_i2c_priv *priv = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&priv->adap);
+ mutex_destroy(&priv->lock);
+
+ return 0;
+}
+
+static struct platform_driver mlxcpld_i2c_driver = {
+ .probe = mlxcpld_i2c_probe,
+ .remove = mlxcpld_i2c_remove,
+ .driver = {
+ .name = MLXCPLD_I2C_DEVICE_NAME,
+ },
+};
+
+module_platform_driver(mlxcpld_i2c_driver);
+
+MODULE_AUTHOR("Michael Shych <michaels@mellanox.com>");
+MODULE_DESCRIPTION("Mellanox I2C-CPLD controller driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("platform:i2c-mlxcpld");

View File

@@ -0,0 +1,317 @@
Linux backport patch. Includes following commits:
e3448e71adb1fdd7f403c568ef5c2ed5adf2b197
c3bb77620da428884807fb2f6f3485644e146f84
db5f807ee3dcc779b78f59982cc3e89863069e9c
diff -Nur a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
--- a/drivers/i2c/muxes/Kconfig 2017-11-12 08:13:59.176044126 +0000
+++ b/drivers/i2c/muxes/Kconfig 2017-11-12 08:14:27.992044509 +0000
@@ -82,4 +82,15 @@
demultiplexer that uses the pinctrl subsystem. This is useful if you
want to change the I2C master at run-time depending on features.
+config I2C_MUX_MLXCPLD
+ tristate "Mellanox CPLD based I2C multiplexer"
+ help
+ If you say yes to this option, support will be included for a
+ CPLD based I2C multiplexer. This driver provides access to
+ I2C busses connected through a MUX, which is controlled
+ by a CPLD register.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-mux-mlxcpld.
+
endmenu
diff -Nur a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
--- a/drivers/i2c/muxes/Makefile 2017-11-12 08:13:59.176044126 +0000
+++ b/drivers/i2c/muxes/Makefile 2017-11-12 08:14:27.992044509 +0000
@@ -6,6 +6,7 @@
obj-$(CONFIG_I2C_DEMUX_PINCTRL) += i2c-demux-pinctrl.o
obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
+obj-$(CONFIG_I2C_MUX_MLXCPLD) += i2c-mux-mlxcpld.o
obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o
obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o
diff -Nur a/drivers/i2c/muxes/i2c-mux-mlxcpld.c b/drivers/i2c/muxes/i2c-mux-mlxcpld.c
--- a/drivers/i2c/muxes/i2c-mux-mlxcpld.c 1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/i2c/muxes/i2c-mux-mlxcpld.c 2017-11-12 08:14:27.992044509 +0000
@@ -0,0 +1,221 @@
+/*
+ * drivers/i2c/muxes/i2c-mux-mlxcpld.c
+ * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2016 Michael Shych <michaels@mellanox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/i2c-mux.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/i2c/mlxcpld.h>
+
+#define CPLD_MUX_MAX_NCHANS 8
+
+/* mlxcpld_mux - mux control structure:
+ * @last_chan - last register value
+ * @client - I2C device client
+ */
+struct mlxcpld_mux {
+ u8 last_chan;
+ struct i2c_client *client;
+};
+
+/* MUX logic description.
+ * Driver can support different mux control logic, according to CPLD
+ * implementation.
+ *
+ * Connectivity schema.
+ *
+ * i2c-mlxcpld Digital Analog
+ * driver
+ * *--------* * -> mux1 (virt bus2) -> mux -> |
+ * | I2CLPC | i2c physical * -> mux2 (virt bus3) -> mux -> |
+ * | bridge | bus 1 *---------* |
+ * | logic |---------------------> * mux reg * |
+ * | in CPLD| *---------* |
+ * *--------* i2c-mux-mlxpcld ^ * -> muxn (virt busn) -> mux -> |
+ * | driver | |
+ * | *---------------* | Devices
+ * | * CPLD (i2c bus)* select |
+ * | * registers for *--------*
+ * | * mux selection * deselect
+ * | *---------------*
+ * | |
+ * <--------> <----------->
+ * i2c cntrl Board cntrl reg
+ * reg space space (mux select,
+ * IO, LED, WD, info)
+ *
+ */
+
+static const struct i2c_device_id mlxcpld_mux_id[] = {
+ { "mlxcpld_mux_module", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mlxcpld_mux_id);
+
+/* Write to mux register. Don't use i2c_transfer() and i2c_smbus_xfer()
+ * for this as they will try to lock adapter a second time.
+ */
+static int mlxcpld_mux_reg_write(struct i2c_adapter *adap,
+ struct i2c_client *client, u8 val)
+{
+ struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&client->dev);
+ int ret = -ENODEV;
+
+ if (adap->algo->master_xfer) {
+ struct i2c_msg msg;
+ u8 msgbuf[] = {pdata->sel_reg_addr, val};
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = 2;
+ msg.buf = msgbuf;
+ ret = __i2c_transfer(adap, &msg, 1);
+
+ if (ret >= 0 && ret != 1)
+ ret = -EREMOTEIO;
+ } else if (adap->algo->smbus_xfer) {
+ union i2c_smbus_data data;
+
+ data.byte = val;
+ ret = adap->algo->smbus_xfer(adap, client->addr,
+ client->flags, I2C_SMBUS_WRITE,
+ pdata->sel_reg_addr,
+ I2C_SMBUS_BYTE_DATA, &data);
+ }
+
+ return ret;
+}
+
+static int mlxcpld_mux_select_chan(struct i2c_mux_core *muxc, u32 chan)
+{
+ struct mlxcpld_mux *data = i2c_mux_priv(muxc);
+ struct i2c_client *client = data->client;
+ u8 regval = chan + 1;
+ int err = 0;
+
+ /* Only select the channel if its different from the last channel */
+ if (data->last_chan != regval) {
+ err = mlxcpld_mux_reg_write(muxc->parent, client, regval);
+ data->last_chan = err < 0 ? 0 : regval;
+ }
+
+ return err;
+}
+
+static int mlxcpld_mux_deselect(struct i2c_mux_core *muxc, u32 chan)
+{
+ struct mlxcpld_mux *data = i2c_mux_priv(muxc);
+ struct i2c_client *client = data->client;
+
+ /* Deselect active channel */
+ data->last_chan = 0;
+
+ return mlxcpld_mux_reg_write(muxc->parent, client, data->last_chan);
+}
+
+/* Probe/reomove functions */
+static int mlxcpld_mux_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
+ struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&client->dev);
+ struct i2c_mux_core *muxc;
+ int num, force;
+ struct mlxcpld_mux *data;
+ int err;
+
+ if (!pdata)
+ return -EINVAL;
+
+ if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+ return -ENODEV;
+
+ muxc = i2c_mux_alloc(adap, &client->dev, CPLD_MUX_MAX_NCHANS,
+ sizeof(*data), 0, mlxcpld_mux_select_chan,
+ mlxcpld_mux_deselect);
+ if (!muxc)
+ return -ENOMEM;
+
+ data = i2c_mux_priv(muxc);
+ i2c_set_clientdata(client, muxc);
+ data->client = client;
+ data->last_chan = 0; /* force the first selection */
+
+ /* Create an adapter for each channel. */
+ for (num = 0; num < CPLD_MUX_MAX_NCHANS; num++) {
+ if (num >= pdata->num_adaps)
+ /* discard unconfigured channels */
+ break;
+
+ force = pdata->adap_ids[num];
+
+ err = i2c_mux_add_adapter(muxc, force, num, 0);
+ if (err)
+ goto virt_reg_failed;
+ }
+
+ return 0;
+
+virt_reg_failed:
+ i2c_mux_del_adapters(muxc);
+ return err;
+}
+
+static int mlxcpld_mux_remove(struct i2c_client *client)
+{
+ struct i2c_mux_core *muxc = i2c_get_clientdata(client);
+
+ i2c_mux_del_adapters(muxc);
+ return 0;
+}
+
+static struct i2c_driver mlxcpld_mux_driver = {
+ .driver = {
+ .name = "mlxcpld-mux",
+ },
+ .probe = mlxcpld_mux_probe,
+ .remove = mlxcpld_mux_remove,
+ .id_table = mlxcpld_mux_id,
+};
+
+module_i2c_driver(mlxcpld_mux_driver);
+
+MODULE_AUTHOR("Michael Shych (michaels@mellanox.com)");
+MODULE_DESCRIPTION("Mellanox I2C-CPLD-MUX driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("platform:i2c-mux-mlxcpld");
diff -Nur a/include/linux/i2c/mlxcpld.h b/include/linux/i2c/mlxcpld.h
--- a/include/linux/i2c/mlxcpld.h 1970-01-01 00:00:00.000000000 +0000
+++ b/include/linux/i2c/mlxcpld.h 2017-11-12 08:17:03.032046568 +0000
@@ -0,0 +1,52 @@
+/*
+ * mlxcpld.h - Mellanox I2C multiplexer support in CPLD
+ *
+ * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2016 Michael Shych <michaels@mellanox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LINUX_I2C_MLXCPLD_H
+#define _LINUX_I2C_MLXCPLD_H
+
+/* Platform data for the CPLD I2C multiplexers */
+
+/* mlxcpld_mux_plat_data - per mux data, used with i2c_register_board_info
+ * @adap_ids - adapter array
+ * @num_adaps - number of adapters
+ * @sel_reg_addr - mux select register offset in CPLD space
+ */
+struct mlxcpld_mux_plat_data {
+ int *adap_ids;
+ int num_adaps;
+ int sel_reg_addr;
+};
+
+#endif /* _LINUX_I2C_MLXCPLD_H */

View File

@@ -0,0 +1,905 @@
Linux backport patch. Includes following commits:
2926024b5081fc8d4b086677bafa1ac55ea0b911
6124fdf76488681713f278f3fdf2ba2dfe760211
c84002d15210ca130263e23911cc399202124eb4
07b89c2b2a5e8ce30166b96f87b324c6b419f108
91973760712f350048a0fa8e0363e260bf874313
c2e714e56360e34f88e0a75ee74e467d8b82de75
af4779be0f2cec63f4cb15d3db78c5de3523756a
d53bc5dc941653f0ed93b11a647bd6ff40f40ef2
diff -Nur a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig
--- a/drivers/platform/mellanox/Kconfig 1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/platform/mellanox/Kconfig 2017-11-12 08:54:58.200076777 +0000
@@ -0,0 +1,25 @@
+#
+# Platform support for Mellanox hardware
+#
+
+menuconfig MELLANOX_PLATFORM
+ bool "Platform support for Mellanox hardware"
+ depends on X86 || ARM || COMPILE_TEST
+ ---help---
+ Say Y here to get to see options for platform support for
+ Mellanox systems. This option alone does not add any kernel code.
+
+ If you say N, all options in this submenu will be skipped and disabled.
+
+if MELLANOX_PLATFORM
+
+config MLXREG_HOTPLUG
+ tristate "Mellanox platform hotplug driver support"
+ depends on REGMAP
+ depends on HWMON
+ depends on I2C
+ ---help---
+ This driver handles hot-plug events for the power suppliers, power
+ cables and fans on the wide range Mellanox IB and Ethernet systems.
+
+endif # MELLANOX_PLATFORM
diff -Nur a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile
--- a/drivers/platform/mellanox/Makefile 1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/platform/mellanox/Makefile 2017-11-12 08:54:58.200076777 +0000
@@ -0,0 +1 @@
+obj-$(CONFIG_MLXREG_HOTPLUG) += mlxreg-hotplug.o
diff -Nur a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c
--- a/drivers/platform/mellanox/mlxreg-hotplug.c 1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/platform/mellanox/mlxreg-hotplug.c 2017-11-12 08:54:58.200076777 +0000
@@ -0,0 +1,710 @@
+/*
+ * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017 Vadim Pasternak <vadimp@mellanox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_data/mlxreg.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/workqueue.h>
+
+/* Offset of event and mask registers from status register. */
+#define MLXREG_HOTPLUG_EVENT_OFF 1
+#define MLXREG_HOTPLUG_MASK_OFF 2
+#define MLXREG_HOTPLUG_AGGR_MASK_OFF 1
+
+/* ASIC health parameters. */
+#define MLXREG_HOTPLUG_HEALTH_MASK 0x02
+#define MLXREG_HOTPLUG_RST_CNTR 3
+
+#define MLXREG_HOTPLUG_PROP_OKAY "okay"
+#define MLXREG_HOTPLUG_PROP_DISABLED "disabled"
+#define MLXREG_HOTPLUG_PROP_STATUS "status"
+
+#define MLXREG_HOTPLUG_ATTRS_MAX 24
+
+/**
+ * struct mlxreg_hotplug_priv_data - platform private data:
+ * @irq: platform device interrupt number;
+ * @pdev: platform device;
+ * @plat: platform data;
+ * @dwork: delayed work template;
+ * @lock: spin lock;
+ * @hwmon: hwmon device;
+ * @mlxreg_hotplug_attr: sysfs attributes array;
+ * @mlxreg_hotplug_dev_attr: sysfs sensor device attribute array;
+ * @group: sysfs attribute group;
+ * @groups: list of sysfs attribute group for hwmon registration;
+ * @cell: location of top aggregation interrupt register;
+ * @mask: top aggregation interrupt common mask;
+ * @aggr_cache: last value of aggregation register status;
+ */
+struct mlxreg_hotplug_priv_data {
+ int irq;
+ struct device *dev;
+ struct platform_device *pdev;
+ struct mlxreg_hotplug_platform_data *plat;
+ struct regmap *regmap;
+ struct delayed_work dwork_irq;
+ struct delayed_work dwork;
+ spinlock_t lock; /* sync with interrupt */
+ struct device *hwmon;
+ struct attribute *mlxreg_hotplug_attr[MLXREG_HOTPLUG_ATTRS_MAX + 1];
+ struct sensor_device_attribute_2
+ mlxreg_hotplug_dev_attr[MLXREG_HOTPLUG_ATTRS_MAX];
+ struct attribute_group group;
+ const struct attribute_group *groups[2];
+ u32 cell;
+ u32 mask;
+ u32 aggr_cache;
+ bool after_probe;
+};
+
+#if defined(CONFIG_OF_DYNAMIC)
+/**
+ * struct mlxreg_hotplug_device_en - Open Firmware property for enabling device
+ *
+ * @name - property name;
+ * @value - property value string;
+ * @length - length of proprty value string;
+ *
+ * The structure is used for the devices, which require some dynamic
+ * selection operation allowing access to them.
+ */
+static struct property mlxreg_hotplug_device_en = {
+ .name = MLXREG_HOTPLUG_PROP_STATUS,
+ .value = MLXREG_HOTPLUG_PROP_OKAY,
+ .length = sizeof(MLXREG_HOTPLUG_PROP_OKAY),
+};
+
+/**
+ * struct mlxreg_hotplug_device_dis - Open Firmware property for disabling
+ * device
+ *
+ * @name - property name;
+ * @value - property value string;
+ * @length - length of proprty value string;
+ *
+ * The structure is used for the devices, which require some dynamic
+ * selection operation disallowing access to them.
+ */
+static struct property mlxreg_hotplug_device_dis = {
+ .name = MLXREG_HOTPLUG_PROP_STATUS,
+ .value = MLXREG_HOTPLUG_PROP_DISABLED,
+ .length = sizeof(MLXREG_HOTPLUG_PROP_DISABLED),
+};
+
+static int mlxreg_hotplug_of_device_create(struct mlxreg_core_data *data)
+{
+ return of_update_property(data->np, &mlxreg_hotplug_device_en);
+}
+
+static void mlxreg_hotplug_of_device_destroy(struct mlxreg_core_data *data)
+{
+ of_update_property(data->np, &mlxreg_hotplug_device_dis);
+ of_node_clear_flag(data->np, OF_POPULATED);
+}
+#else
+static int mlxreg_hotplug_of_device_create(struct mlxreg_core_data *data)
+{
+ return 0;
+}
+
+static void mlxreg_hotplug_of_device_destroy(struct mlxreg_core_data *data)
+{
+}
+#endif
+
+static int mlxreg_hotplug_device_create(struct mlxreg_core_data *data)
+{
+ data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr);
+ if (!data->hpdev.adapter)
+ return -EFAULT;
+
+ data->hpdev.client = i2c_new_device(data->hpdev.adapter,
+ data->hpdev.brdinfo);
+ if (!data->hpdev.client) {
+ i2c_put_adapter(data->hpdev.adapter);
+ data->hpdev.adapter = NULL;
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static void mlxreg_hotplug_device_destroy(struct mlxreg_core_data *data)
+{
+ if (data->hpdev.client) {
+ i2c_unregister_device(data->hpdev.client);
+ data->hpdev.client = NULL;
+ }
+
+ if (data->hpdev.adapter) {
+ i2c_put_adapter(data->hpdev.adapter);
+ data->hpdev.adapter = NULL;
+ }
+}
+
+static int mlxreg_hotplug_dev_enable(struct mlxreg_core_data *data)
+{
+ int err;
+
+ /* Enable and create device. */
+ if (data->np)
+ err = mlxreg_hotplug_of_device_create(data);
+ else
+ err = mlxreg_hotplug_device_create(data);
+
+ return err;
+}
+
+static void mlxreg_hotplug_dev_disable(struct mlxreg_core_data *data)
+{
+ /* Disable and unregister platform device. */
+ if (data->np)
+ mlxreg_hotplug_of_device_destroy(data);
+ else
+ mlxreg_hotplug_device_destroy(data);
+}
+
+static ssize_t mlxreg_hotplug_attr_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct mlxreg_hotplug_priv_data *priv = dev_get_drvdata(dev);
+ struct mlxreg_core_hotplug_platform_data *pdata;
+ int index = to_sensor_dev_attr_2(attr)->index;
+ int nr = to_sensor_dev_attr_2(attr)->nr;
+ struct mlxreg_core_item *item;
+ struct mlxreg_core_data *data;
+ u32 regval;
+ int ret;
+
+ pdata = dev_get_platdata(&priv->pdev->dev);
+ item = pdata->items + nr;
+ data = item->data + index;
+
+ ret = regmap_read(priv->regmap, data->reg, &regval);
+ if (ret)
+ return ret;
+
+ if (item->health) {
+ regval &= data->mask;
+ } else {
+ /* Bit = 0 : functional if item->inversed is true. */
+ if (item->inversed)
+ regval = !(regval & data->mask);
+ else
+ regval = !!(regval & data->mask);
+ }
+
+ return sprintf(buf, "%u\n", regval);
+}
+
+#define PRIV_ATTR(i) priv->mlxreg_hotplug_attr[i]
+#define PRIV_DEV_ATTR(i) priv->mlxreg_hotplug_dev_attr[i]
+
+static int mlxreg_hotplug_attr_init(struct mlxreg_hotplug_priv_data *priv)
+{
+ struct mlxreg_core_hotplug_platform_data *pdata;
+ struct mlxreg_core_item *item;
+ struct mlxreg_core_data *data;
+ int num_attrs = 0, id = 0, i, j;
+
+ pdata = dev_get_platdata(&priv->pdev->dev);
+ item = pdata->items;
+
+ /* Go over all kinds of items - psu, pwr, fan. */
+ for (i = 0; i < pdata->counter; i++, item++) {
+ num_attrs += item->count;
+ data = item->data;
+ /* Go over all units within the item. */
+ for (j = 0; j < item->count; j++, data++, id++) {
+ PRIV_ATTR(id) = &PRIV_DEV_ATTR(id).dev_attr.attr;
+ PRIV_ATTR(id)->name = devm_kasprintf(&priv->pdev->dev,
+ GFP_KERNEL,
+ data->label);
+
+ if (!PRIV_ATTR(id)->name) {
+ dev_err(priv->dev, "Memory allocation failed for attr %d.\n",
+ id);
+ return -ENOMEM;
+ }
+
+ PRIV_DEV_ATTR(id).dev_attr.attr.name =
+ PRIV_ATTR(id)->name;
+ PRIV_DEV_ATTR(id).dev_attr.attr.mode = 0444;
+ PRIV_DEV_ATTR(id).dev_attr.show =
+ mlxreg_hotplug_attr_show;
+ PRIV_DEV_ATTR(id).nr = i;
+ PRIV_DEV_ATTR(id).index = j;
+ sysfs_attr_init(&PRIV_DEV_ATTR(id).dev_attr.attr);
+ }
+ }
+
+ priv->group.attrs = devm_kzalloc(&priv->pdev->dev, num_attrs *
+ sizeof(struct attribute *),
+ GFP_KERNEL);
+ if (!priv->group.attrs)
+ return -ENOMEM;
+
+ priv->group.attrs = priv->mlxreg_hotplug_attr;
+ priv->groups[0] = &priv->group;
+ priv->groups[1] = NULL;
+
+ return 0;
+}
+
+static void
+mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv,
+ struct mlxreg_core_item *item)
+{
+ struct mlxreg_core_data *data;
+ u32 asserted, regval, bit;
+ int ret;
+
+ /*
+ * Validate if item related to received signal type is valid.
+ * It should never happen, excepted the situation when some
+ * piece of hardware is broken. In such situation just produce
+ * error message and return. Caller must continue to handle the
+ * signals from other devices if any.
+ */
+ if (unlikely(!item)) {
+ dev_err(priv->dev, "False signal: at offset:mask 0x%02x:0x%02x.\n",
+ item->reg, item->mask);
+
+ return;
+ }
+
+ /* Mask event. */
+ ret = regmap_write(priv->regmap, item->reg + MLXREG_HOTPLUG_MASK_OFF,
+ 0);
+ if (ret)
+ goto access_error;
+
+ /* Read status. */
+ ret = regmap_read(priv->regmap, item->reg, &regval);
+ if (ret)
+ goto access_error;
+
+ /* Set asserted bits and save last status. */
+ regval &= item->mask;
+ asserted = item->cache ^ regval;
+ item->cache = regval;
+
+ for_each_set_bit(bit, (unsigned long *)&asserted, 8) {
+ data = item->data + bit;
+ if (regval & BIT(bit)) {
+ if (item->inversed)
+ mlxreg_hotplug_dev_disable(data);
+ else
+ mlxreg_hotplug_dev_enable(data);
+ } else {
+ if (item->inversed)
+ mlxreg_hotplug_dev_enable(data);
+ else
+ mlxreg_hotplug_dev_disable(data);
+ }
+ }
+
+ /* Acknowledge event. */
+ ret = regmap_write(priv->regmap, item->reg + MLXREG_HOTPLUG_EVENT_OFF,
+ 0);
+ if (ret)
+ goto access_error;
+
+ /* Unmask event. */
+ ret = regmap_write(priv->regmap, item->reg + MLXREG_HOTPLUG_MASK_OFF,
+ item->mask);
+ if (ret)
+ goto access_error;
+
+ return;
+
+access_error:
+ dev_err(priv->dev, "Failed to complete workqueue.\n");
+}
+
+static void
+mlxreg_hotplug_health_work_helper(struct mlxreg_hotplug_priv_data *priv,
+ struct mlxreg_core_item *item)
+{
+ struct mlxreg_core_data *data = item->data;
+ u32 regval;
+ int i, ret;
+
+ for (i = 0; i < item->count; i++, data++) {
+ /* Mask event. */
+ ret = regmap_write(priv->regmap, data->reg +
+ MLXREG_HOTPLUG_MASK_OFF, 0);
+ if (ret)
+ goto access_error;
+
+ /* Read status. */
+ ret = regmap_read(priv->regmap, data->reg, &regval);
+ if (ret)
+ goto access_error;
+
+ regval &= data->mask;
+ item->cache = regval;
+ if (regval == MLXREG_HOTPLUG_HEALTH_MASK) {
+ if ((data->health_cntr++ == MLXREG_HOTPLUG_RST_CNTR) ||
+ !priv->after_probe) {
+ mlxreg_hotplug_dev_enable(data);
+ data->attached = true;
+ }
+ } else {
+ if (data->attached) {
+ mlxreg_hotplug_dev_disable(data);
+ data->attached = false;
+ data->health_cntr = 0;
+ }
+ }
+
+ /* Acknowledge event. */
+ ret = regmap_write(priv->regmap, data->reg +
+ MLXREG_HOTPLUG_EVENT_OFF, 0);
+ if (ret)
+ goto access_error;
+
+ /* Unmask event. */
+ ret = regmap_write(priv->regmap, data->reg +
+ MLXREG_HOTPLUG_MASK_OFF, data->mask);
+ if (ret)
+ goto access_error;
+ }
+
+ return;
+
+access_error:
+ dev_err(priv->dev, "Failed to complete workqueue.\n");
+}
+
+/*
+ * mlxreg_hotplug_work_handler - performs traversing of device interrupt
+ * registers according to the below hierarchy schema:
+ *
+ * Aggregation registers (status/mask)
+ * PSU registers: *---*
+ * *-----------------* | |
+ * |status/event/mask|-----> | * |
+ * *-----------------* | |
+ * Power registers: | |
+ * *-----------------* | |
+ * |status/event/mask|-----> | * |
+ * *-----------------* | |
+ * FAN registers: | |--> CPU
+ * *-----------------* | |
+ * |status/event/mask|-----> | * |
+ * *-----------------* | |
+ * ASIC registers: | |
+ * *-----------------* | |
+ * |status/event/mask|-----> | * |
+ * *-----------------* | |
+ * *---*
+ *
+ * In case some system changed are detected: FAN in/out, PSU in/out, power
+ * cable attached/detached, ASIC helath good/bad, relevant device is created
+ * or destroyed.
+ */
+static void mlxreg_hotplug_work_handler(struct work_struct *work)
+{
+ struct mlxreg_hotplug_priv_data *priv = container_of(work,
+ struct mlxreg_hotplug_priv_data, dwork_irq.work);
+ struct mlxreg_core_hotplug_platform_data *pdata;
+ struct mlxreg_core_item *item;
+ unsigned long flags;
+ u32 regval, aggr_asserted;
+ int i;
+ int ret;
+
+ pdata = dev_get_platdata(&priv->pdev->dev);
+ item = pdata->items;
+ /* Mask aggregation event. */
+ ret = regmap_write(priv->regmap, pdata->cell +
+ MLXREG_HOTPLUG_AGGR_MASK_OFF, 0);
+ if (ret < 0)
+ goto access_error;
+
+ /* Read aggregation status. */
+ ret = regmap_read(priv->regmap, pdata->cell, &regval);
+ if (ret)
+ goto access_error;
+
+ regval &= pdata->mask;
+ aggr_asserted = priv->aggr_cache ^ regval;
+ priv->aggr_cache = regval;
+
+ /* Handle topology and health configuration changes. */
+ for (i = 0; i < pdata->counter; i++, item++) {
+ if (aggr_asserted & item->aggr_mask) {
+ if (item->health)
+ mlxreg_hotplug_health_work_helper(priv, item);
+ else
+ mlxreg_hotplug_work_helper(priv, item);
+ }
+ }
+
+ if (aggr_asserted) {
+ spin_lock_irqsave(&priv->lock, flags);
+
+ /*
+ * It is possible, that some signals have been inserted, while
+ * interrupt has been masked by mlxreg_hotplug_work_handler.
+ * In this case such signals will be missed. In order to handle
+ * these signals delayed work is canceled and work task
+ * re-scheduled for immediate execution. It allows to handle
+ * missed signals, if any. In other case work handler just
+ * validates that no new signals have been received during
+ * masking.
+ */
+ cancel_delayed_work(&priv->dwork_irq);
+ schedule_delayed_work(&priv->dwork_irq, 0);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return;
+ }
+
+ /* Unmask aggregation event (no need acknowledge). */
+ ret = regmap_write(priv->regmap, pdata->cell +
+ MLXREG_HOTPLUG_AGGR_MASK_OFF, pdata->mask);
+ if (ret)
+ goto access_error;
+
+ return;
+
+access_error:
+ dev_err(priv->dev, "Failed to complete workqueue.\n");
+}
+
+static int mlxreg_hotplug_set_irq(struct mlxreg_hotplug_priv_data *priv)
+{
+ struct mlxreg_core_hotplug_platform_data *pdata;
+ struct mlxreg_core_item *item;
+ int i;
+ int ret;
+
+ pdata = dev_get_platdata(&priv->pdev->dev);
+ item = pdata->items;
+
+ for (i = 0; i < pdata->counter; i++, item++) {
+ /* Clear group presense event. */
+ ret = regmap_write(priv->regmap, item->reg +
+ MLXREG_HOTPLUG_EVENT_OFF, 0);
+ if (ret)
+ goto access_error;
+
+ /* Set group initial status as mask and unmask group event. */
+ if (item->inversed) {
+ item->cache = item->mask;
+ ret = regmap_write(priv->regmap, item->reg +
+ MLXREG_HOTPLUG_MASK_OFF,
+ item->mask);
+ if (ret)
+ goto access_error;
+ }
+ }
+
+ /* Keep aggregation initial status as zero and unmask events. */
+ ret = regmap_write(priv->regmap, pdata->cell +
+ MLXREG_HOTPLUG_AGGR_MASK_OFF, pdata->mask);
+ if (ret)
+ goto access_error;
+
+ /* Keep low aggregation initial status as zero and unmask events. */
+ ret = regmap_write(priv->regmap, pdata->cell_low +
+ MLXREG_HOTPLUG_AGGR_MASK_OFF, pdata->mask_low);
+ if (ret)
+ goto access_error;
+
+ /* Invoke work handler for initializing hot plug devices setting. */
+ mlxreg_hotplug_work_handler(&priv->dwork_irq.work);
+
+ enable_irq(priv->irq);
+
+ return 0;
+
+access_error:
+ dev_err(priv->dev, "Failed to set interrupts.\n");
+
+ enable_irq(priv->irq);
+
+ return ret;
+}
+
+static void mlxreg_hotplug_unset_irq(struct mlxreg_hotplug_priv_data *priv)
+{
+ struct mlxreg_core_hotplug_platform_data *pdata;
+ struct mlxreg_core_item *item;
+ struct mlxreg_core_data *data;
+ int count, i, j;
+
+ pdata = dev_get_platdata(&priv->pdev->dev);
+ item = pdata->items;
+ disable_irq(priv->irq);
+ cancel_delayed_work_sync(&priv->dwork_irq);
+
+ /* Mask low aggregation event. */
+ regmap_write(priv->regmap, pdata->cell_low +
+ MLXREG_HOTPLUG_AGGR_MASK_OFF, 0);
+
+ /* Mask aggregation event. */
+ regmap_write(priv->regmap, pdata->cell + MLXREG_HOTPLUG_AGGR_MASK_OFF,
+ 0);
+
+ /* Clear topology configurations. */
+ for (i = 0; i < pdata->counter; i++, item++) {
+ data = item->data;
+ /* Mask group presense event. */
+ regmap_write(priv->regmap, data->reg + MLXREG_HOTPLUG_MASK_OFF,
+ 0);
+ /* Clear group presense event. */
+ regmap_write(priv->regmap, data->reg +
+ MLXREG_HOTPLUG_EVENT_OFF, 0);
+
+ /* Remove all the attached devices in group. */
+ count = item->count;
+ for (j = 0; j < count; j++, data++)
+ mlxreg_hotplug_dev_disable(data);
+ }
+}
+
+static irqreturn_t mlxreg_hotplug_irq_handler(int irq, void *dev)
+{
+ struct mlxreg_hotplug_priv_data *priv =
+ (struct mlxreg_hotplug_priv_data *)dev;
+
+ /* Schedule work task for immediate execution.*/
+ schedule_delayed_work(&priv->dwork_irq, 0);
+
+ return IRQ_HANDLED;
+}
+
+static int mlxreg_hotplug_probe(struct platform_device *pdev)
+{
+ struct mlxreg_core_hotplug_platform_data *pdata;
+ struct mlxreg_hotplug_priv_data *priv;
+ int err;
+
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata) {
+ dev_err(&pdev->dev, "Failed to get platform data.\n");
+ return -EINVAL;
+ }
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ if (pdata->irq) {
+ priv->irq = pdata->irq;
+ } else {
+ priv->irq = platform_get_irq(pdev, 0);
+ if (priv->irq < 0) {
+ dev_err(&pdev->dev, "Failed to get platform irq: %d\n",
+ priv->irq);
+ return priv->irq;
+ }
+ }
+
+ priv->regmap = pdata->regmap;
+ priv->dev = pdev->dev.parent;
+ priv->pdev = pdev;
+
+ err = devm_request_irq(&pdev->dev, priv->irq,
+ mlxreg_hotplug_irq_handler, IRQF_TRIGGER_FALLING
+ | IRQF_SHARED, "mlxreg-hotplug", priv);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to request irq: %d\n", err);
+ return err;
+ }
+
+ disable_irq(priv->irq);
+ spin_lock_init(&priv->lock);
+ INIT_DELAYED_WORK(&priv->dwork_irq, mlxreg_hotplug_work_handler);
+ /* Perform initial interrupts setup. */
+ mlxreg_hotplug_set_irq(priv);
+
+ priv->after_probe = true;
+ dev_set_drvdata(&pdev->dev, priv);
+
+ err = mlxreg_hotplug_attr_init(priv);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to allocate attributes: %d\n",
+ err);
+ return err;
+ }
+
+ priv->hwmon = devm_hwmon_device_register_with_groups(&pdev->dev,
+ "mlxreg_hotplug", priv, priv->groups);
+ if (IS_ERR(priv->hwmon)) {
+ dev_err(&pdev->dev, "Failed to register hwmon device %ld\n",
+ PTR_ERR(priv->hwmon));
+ return PTR_ERR(priv->hwmon);
+ }
+
+ return 0;
+}
+
+static int mlxreg_hotplug_remove(struct platform_device *pdev)
+{
+ struct mlxreg_hotplug_priv_data *priv = dev_get_drvdata(&pdev->dev);
+
+ /* Clean interrupts setup. */
+ mlxreg_hotplug_unset_irq(priv);
+
+ return 0;
+}
+
+static struct platform_driver mlxreg_hotplug_driver = {
+ .driver = {
+ .name = "mlxreg-hotplug",
+ },
+ .probe = mlxreg_hotplug_probe,
+ .remove = mlxreg_hotplug_remove,
+};
+
+module_platform_driver(mlxreg_hotplug_driver);
+
+MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
+MODULE_DESCRIPTION("Mellanox regmap hotplug platform driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("platform:mlxreg-hotplug");
diff -Nur a/include/linux/platform_data/mlxreg.h b/include/linux/platform_data/mlxreg.h
--- a/include/linux/platform_data/mlxreg.h 1970-01-01 00:00:00.000000000 +0000
+++ b/include/linux/platform_data/mlxreg.h 2017-11-12 09:04:09.796084101 +0000
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017 Vadim Pasternak <vadimp@mellanox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __LINUX_PLATFORM_DATA_MLXREG_H
+#define __LINUX_PLATFORM_DATA_MLXREG_H
+
+#define MLXREG_CORE_LABEL_MAX_SIZE 32
+
+/**
+ * struct mlxreg_hotplug_device - I2C device data:
+ *
+ * @adapter: I2C device adapter;
+ * @client: I2C device client;
+ * @brdinfo: device board information;
+ * @nr: I2C device adapter number, to which device is to be attached;
+ *
+ * Structure represents I2C hotplug device static data (board topology) and
+ * dynamic data (related kernel objects handles).
+ */
+struct mlxreg_hotplug_device {
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ struct i2c_board_info *brdinfo;
+ int nr;
+};
+
+/**
+ * struct mlxreg_core_data - attributes control data:
+ *
+ * @label: attribute label;
+ * @label: attribute register offset;
+ * @reg: attribute register;
+ * @mask: attribute access mask;
+ * @bit: attribute effective bit;
+ * @np - pointer to node platform associated with attribute;
+ * @hpdev - hotplug device data;
+ * @health_cntr: dynamic device health indication counter;
+ * @attached: true if device has been attached after good helath indication;
+ */
+struct mlxreg_core_data {
+ char label[MLXREG_CORE_LABEL_MAX_SIZE];
+ u32 reg;
+ u32 mask;
+ u32 bit;
+ struct device_node *np;
+ struct mlxreg_hotplug_device hpdev;
+ u8 health_cntr;
+ bool attached;
+};
+
+/**
+ * struct mlxreg_core_item - same type components controlled by the driver:
+ *
+ * @data: component data;
+ * @aggr_mask: group aggregation mask;
+ * @reg: group interrupt status register;
+ * @mask: group interrupt mask;
+ * @cache: last status value for elements fro the same group;
+ * @count: number of available elements in the group;
+ * @ind: element's index inside the group;
+ * @inversed: if 0: 0 for signal status is OK, if 1 - 1 is OK;
+ * @health: true if device has health indication, false in other case;
+ */
+struct mlxreg_core_item {
+ struct mlxreg_core_data *data;
+ u32 aggr_mask;
+ u32 reg;
+ u32 mask;
+ u32 cache;
+ u8 count;
+ u8 ind;
+ u8 inversed;
+ u8 health;
+};
+
+/**
+ * struct mlxreg_core_led_platform_data - led platform data:
+ *
+ * @led_data: led private data;
+ * @regmap: register map of parent device;
+ * @counter: number of led instances;
+ */
+struct mlxreg_core_led_platform_data {
+ struct mlxreg_core_data *data;
+ void *regmap;
+ int counter;
+};
+
+/**
+ * struct mlxreg_core_hotplug_platform_data - hotplug platform data:
+ *
+ * @items: same type components with the hotplug capability;
+ * @irq: platform interrupt number;
+ * @regmap: register map of parent device;
+ * @counter: number of the components with the hotplug capability;
+ * @cell: location of top aggregation interrupt register;
+ * @mask: top aggregation interrupt common mask;
+ * @cell_low: location of low aggregation interrupt register;
+ * @mask_low: low aggregation interrupt common mask;
+ */
+struct mlxreg_core_hotplug_platform_data {
+ struct mlxreg_core_item *items;
+ int irq;
+ void *regmap;
+ int counter;
+ u32 cell;
+ u32 mask;
+ u32 cell_low;
+ u32 mask_low;
+};
+
+#endif /* __LINUX_PLATFORM_DATA_MLXREG_H */

View File

@@ -0,0 +1,398 @@
Linux backport patch. Includes following commits:
7dc37aeb560416771cbdc286357157c7565dc1fe
9244ef4cb79a8411656cb8fc2366f32f2294a0c9
daf155fe70c9d69c28bba632b6a758ac8feab6e7
diff -Nur a/drivers/leds/Kconfig b/drivers/leds/Kconfig
--- a/drivers/leds/Kconfig 2017-11-12 09:08:40.740087699 +0000
+++ b/drivers/leds/Kconfig 2017-11-12 09:06:54.580086289 +0000
@@ -659,6 +659,35 @@
This option enabled support for the LEDs on the Mellanox
boards. Say Y to enabled these.
+config LEDS_MLXREG
+ tristate "LED support for the Mellanox BMC cards"
+ depends on LEDS_CLASS
+ help
+ This option enabled support for the LEDs on the Mellanox BMC cards.
+ The driver can be activated from the device tree or by the direct
+ platform device add call. Say Y to enabled these. To compile this
+ driver as a module, choose 'M' here: the module will be called
+ leds-mlxreg.
+
+config LEDS_USER
+ tristate "Userspace LED support"
+ depends on LEDS_CLASS
+ help
+ This option enables support for userspace LEDs. Say 'y' to enable this
+ support in kernel. To compile this driver as a module, choose 'm' here:
+ the module will be called uleds.
+
+config LEDS_NIC78BX
+ tristate "LED support for NI PXI NIC78bx devices"
+ depends on LEDS_CLASS
+ depends on X86 && ACPI
+ help
+ This option enables support for the User1 and User2 LEDs on NI
+ PXI NIC78bx devices.
+
+ To compile this driver as a module, choose M here: the module
+ will be called leds-nic78bx.
+
comment "LED Triggers"
source "drivers/leds/trigger/Kconfig"
diff -Nur a/drivers/leds/Makefile b/drivers/leds/Makefile
--- a/drivers/leds/Makefile 2017-11-12 09:08:40.740087699 +0000
+++ b/drivers/leds/Makefile 2017-11-12 09:06:54.580086289 +0000
@@ -71,6 +71,7 @@
obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o
obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o
obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o
+obj-$(CONFIG_LEDS_MLXREG) += leds-mlxreg.o
# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
diff -Nur a/drivers/leds/leds-mlxcpld.c b/drivers/leds/leds-mlxcpld.c
--- a/drivers/leds/leds-mlxcpld.c 2017-11-12 09:08:40.740087699 +0000
+++ b/drivers/leds/leds-mlxcpld.c 2017-11-12 09:08:05.620087233 +0000
@@ -400,6 +400,9 @@
struct platform_device *pdev;
int err;
+ if (!dmi_match(DMI_CHASSIS_VENDOR, "Mellanox Technologies Ltd."))
+ return -ENODEV;
+
pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
if (IS_ERR(pdev)) {
pr_err("Device allocation failed\n");
@@ -426,5 +429,5 @@
MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
MODULE_DESCRIPTION("Mellanox board LED driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("Dual BSD/GPL");
MODULE_ALIAS("platform:leds_mlxcpld");
diff -Nur a/drivers/leds/leds-mlxreg.c b/drivers/leds/leds-mlxreg.c
--- a/drivers/leds/leds-mlxreg.c 1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/leds/leds-mlxreg.c 2017-11-12 09:06:54.580086289 +0000
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017 Vadim Pasternak <vadimp@mellanox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_data/mlxreg.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/* Codes for LEDs. */
+#define MLXREG_LED_OFFSET_BLINK_3HZ 0x01 /* Offset from solid: 3Hz blink */
+#define MLXREG_LED_OFFSET_BLINK_6HZ 0x02 /* Offset from solid: 6Hz blink */
+#define MLXREG_LED_IS_OFF 0x00 /* Off */
+#define MLXREG_LED_RED_SOLID 0x05 /* Solid red */
+#define MLXREG_LED_GREEN_SOLID 0x0D /* Solid green */
+#define MLXREG_LED_AMBER_SOLID 0x09 /* Solid amber */
+#define MLXREG_LED_BLINK_3HZ 167 /* ~167 msec off/on - HW support */
+#define MLXREG_LED_BLINK_6HZ 83 /* ~83 msec off/on - HW support */
+
+/**
+ * struct mlxreg_led_data - led control data:
+ *
+ * @data: led configuration data;
+ * @led_classdev: led class data;
+ * @base_color: base led color (other colors have constant offset from base);
+ * @led_data: led data;
+ * @data_parent: pointer to private device control data of parent;
+ */
+struct mlxreg_led_data {
+ struct mlxreg_core_data *data;
+ struct led_classdev led_cdev;
+ u8 base_color;
+ void *data_parent;
+ char led_cdev_name[MLXREG_CORE_LABEL_MAX_SIZE];
+};
+
+#define cdev_to_priv(c) container_of(c, struct mlxreg_led_data, led_cdev)
+
+/**
+ * struct mlxreg_led_priv_data - platform private data:
+ *
+ * @pdev: platform device;
+ * @pdata: platform data;
+ * @access_lock: mutex for attribute IO access;
+ */
+struct mlxreg_led_priv_data {
+ struct platform_device *pdev;
+ struct mlxreg_core_led_platform_data *pdata;
+ struct mutex access_lock; /* protect IO operations */
+};
+
+static int
+mlxreg_led_store_hw(struct mlxreg_led_data *led_data, u8 vset)
+{
+ struct mlxreg_led_priv_data *priv = led_data->data_parent;
+ struct mlxreg_core_led_platform_data *led_pdata = priv->pdata;
+ struct mlxreg_core_data *data = led_data->data;
+ u32 regval;
+ u32 nib;
+ int ret;
+
+ /*
+ * Each LED is controlled through low or high nibble of the relevant
+ * register byte. Register offset is specified by off parameter.
+ * Parameter vset provides color code: 0x0 for off, 0x5 for solid red,
+ * 0x6 for 3Hz blink red, 0xd for solid green, 0xe for 3Hz blink
+ * green.
+ * Parameter mask specifies which nibble is used for specific LED: mask
+ * 0xf0 - lower nibble is to be used (bits from 0 to 3), mask 0x0f -
+ * higher nibble (bits from 4 to 7).
+ */
+ mutex_lock(&priv->access_lock);
+
+ ret = regmap_read(led_pdata->regmap, data->reg, &regval);
+ if (ret)
+ goto access_error;
+
+ nib = (ror32(data->mask, data->bit) == 0xf0) ? rol32(vset, data->bit) :
+ rol32(vset, data->bit + 4);
+ regval = (regval & data->mask) | nib;
+
+ ret = regmap_write(led_pdata->regmap, data->reg, regval);
+
+access_error:
+ mutex_unlock(&priv->access_lock);
+
+ return ret;
+}
+
+static enum led_brightness
+mlxreg_led_get_hw(struct mlxreg_led_data *led_data)
+{
+ struct mlxreg_led_priv_data *priv = led_data->data_parent;
+ struct mlxreg_core_led_platform_data *led_pdata = priv->pdata;
+ struct mlxreg_core_data *data = led_data->data;
+ u32 regval;
+ int ret;
+
+ /*
+ * Each LED is controlled through low or high nibble of the relevant
+ * register byte. Register offset is specified by off parameter.
+ * Parameter vset provides color code: 0x0 for off, 0x5 for solid red,
+ * 0x6 for 3Hz blink red, 0xd for solid green, 0xe for 3Hz blink
+ * green.
+ * Parameter mask specifies which nibble is used for specific LED: mask
+ * 0xf0 - lower nibble is to be used (bits from 0 to 3), mask 0x0f -
+ * higher nibble (bits from 4 to 7).
+ */
+ ret = regmap_read(led_pdata->regmap, data->reg, &regval);
+ if (ret < 0) {
+ dev_warn(led_data->led_cdev.dev, "Failed to get current brightness, error: %d\n",
+ ret);
+ /* Assume the LED is OFF */
+ return LED_OFF;
+ }
+
+ regval = regval & ~data->mask;
+ regval = (ror32(data->mask, data->bit) == 0xf0) ? ror32(regval,
+ data->bit) : ror32(regval, data->bit + 4);
+ if (regval >= led_data->base_color &&
+ regval <= (led_data->base_color + MLXREG_LED_OFFSET_BLINK_6HZ))
+ ret = LED_FULL;
+ else
+ ret = LED_OFF;
+
+ return ret;
+}
+
+static int
+mlxreg_led_brightness_set(struct led_classdev *cled, enum led_brightness value)
+{
+ struct mlxreg_led_data *led_data = cdev_to_priv(cled);
+
+ if (value)
+ return mlxreg_led_store_hw(led_data, led_data->base_color);
+ else
+ return mlxreg_led_store_hw(led_data, MLXREG_LED_IS_OFF);
+}
+
+static enum led_brightness
+mlxreg_led_brightness_get(struct led_classdev *cled)
+{
+ struct mlxreg_led_data *led_data = cdev_to_priv(cled);
+
+ return mlxreg_led_get_hw(led_data);
+}
+
+static int
+mlxreg_led_blink_set(struct led_classdev *cled, unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct mlxreg_led_data *led_data = cdev_to_priv(cled);
+ int err;
+
+ /*
+ * HW supports two types of blinking: full (6Hz) and half (3Hz).
+ * For delay on/off zero LED is setting to solid color. For others
+ * combination blinking is to be controlled by the software timer.
+ */
+ if (!(*delay_on == 0 && *delay_off == 0) &&
+ !(*delay_on == MLXREG_LED_BLINK_3HZ &&
+ *delay_off == MLXREG_LED_BLINK_3HZ) &&
+ !(*delay_on == MLXREG_LED_BLINK_6HZ &&
+ *delay_off == MLXREG_LED_BLINK_6HZ))
+ return -EINVAL;
+
+ if (*delay_on == MLXREG_LED_BLINK_6HZ)
+ err = mlxreg_led_store_hw(led_data, led_data->base_color +
+ MLXREG_LED_OFFSET_BLINK_6HZ);
+ else if (*delay_on == MLXREG_LED_BLINK_3HZ)
+ err = mlxreg_led_store_hw(led_data, led_data->base_color +
+ MLXREG_LED_OFFSET_BLINK_3HZ);
+ else
+ err = mlxreg_led_store_hw(led_data, led_data->base_color);
+
+ return err;
+}
+
+static int mlxreg_led_config(struct mlxreg_led_priv_data *priv)
+{
+ struct mlxreg_core_led_platform_data *led_pdata = priv->pdata;
+ struct mlxreg_core_data *data = led_pdata->data;
+ struct mlxreg_led_data *led_data;
+ struct led_classdev *led_cdev;
+ int brightness;
+ int i;
+ int err;
+
+ for (i = 0; i < led_pdata->counter; i++, data++) {
+ led_data = devm_kzalloc(&priv->pdev->dev, sizeof(*led_data),
+ GFP_KERNEL);
+ if (!led_data)
+ return -ENOMEM;
+
+ led_cdev = &led_data->led_cdev;
+ led_data->data_parent = priv;
+ if (strstr(data->label, "red") ||
+ strstr(data->label, "orange")) {
+ brightness = LED_OFF;
+ led_data->base_color = MLXREG_LED_RED_SOLID;
+ } else if (strstr(data->label, "amber")) {
+ brightness = LED_OFF;
+ led_data->base_color = MLXREG_LED_AMBER_SOLID;
+ } else {
+ brightness = LED_OFF;
+ led_data->base_color = MLXREG_LED_GREEN_SOLID;
+ }
+ sprintf(led_data->led_cdev_name, "%s:%s", "mlxreg",
+ data->label);
+ led_cdev->name = led_data->led_cdev_name;
+ led_cdev->brightness = brightness;
+ led_cdev->max_brightness = 1;
+ led_cdev->brightness_set_blocking =
+ mlxreg_led_brightness_set;
+ led_cdev->brightness_get = mlxreg_led_brightness_get;
+ led_cdev->blink_set = mlxreg_led_blink_set;
+ led_cdev->flags = LED_CORE_SUSPENDRESUME;
+ led_data->data = data;
+ err = devm_led_classdev_register(&priv->pdev->dev, led_cdev);
+ if (err)
+ return err;
+
+ if (led_cdev->brightness)
+ mlxreg_led_brightness_set(led_cdev,
+ led_cdev->brightness);
+ dev_info(led_cdev->dev, "label: %s, mask: 0x%02x, offset:0x%02x\n",
+ data->label, data->mask, data->reg);
+ }
+
+ return 0;
+}
+
+static int mlxreg_led_probe(struct platform_device *pdev)
+{
+ struct mlxreg_core_led_platform_data *led_pdata;
+ struct mlxreg_led_priv_data *priv;
+
+ led_pdata = dev_get_platdata(&pdev->dev);
+ if (!led_pdata) {
+ dev_err(&pdev->dev, "Failed to get platform data.\n");
+ return -EINVAL;
+ }
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mutex_init(&priv->access_lock);
+ priv->pdev = pdev;
+ priv->pdata = led_pdata;
+
+ return mlxreg_led_config(priv);
+}
+
+static int mlxreg_led_remove(struct platform_device *pdev)
+{
+ struct mlxreg_led_priv_data *priv = dev_get_drvdata(&pdev->dev);
+
+ mutex_destroy(&priv->access_lock);
+
+ return 0;
+}
+
+static const struct of_device_id mlxreg_led_dt_match[] = {
+ { .compatible = "mellanox,leds-mlxreg" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, mlxreg_led_dt_match);
+
+static struct platform_driver mlxreg_led_driver = {
+ .driver = {
+ .name = "leds-mlxreg",
+ .of_match_table = of_match_ptr(mlxreg_led_dt_match),
+ },
+ .probe = mlxreg_led_probe,
+ .remove = mlxreg_led_remove,
+};
+
+module_platform_driver(mlxreg_led_driver);
+
+MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
+MODULE_DESCRIPTION("Mellanox LED regmap driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("platform:leds-mlxreg");

View File

@@ -0,0 +1,30 @@
Linux backport patch. Includes following commits:
a4dffccb72a7fa46bb0d7f29e607375387e09956
diff -Nur a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
--- a/drivers/hwmon/pmbus/pmbus.h 2017-11-09 16:25:22.760993964 +0000
+++ b/drivers/hwmon/pmbus/pmbus.h 2017-11-09 16:26:02.568994492 +0000
@@ -341,7 +341,7 @@
#define PMBUS_HAVE_STATUS_VMON BIT(19)
enum pmbus_data_format { linear = 0, direct, vid };
-enum vrm_version { vr11 = 0, vr12 };
+enum vrm_version { vr11 = 0, vr12, vr13 };
struct pmbus_driver_info {
int pages; /* Total number of pages */
diff -Nur a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
--- a/drivers/hwmon/pmbus/pmbus_core.c 2017-11-09 16:25:22.760993964 +0000
+++ b/drivers/hwmon/pmbus/pmbus_core.c 2017-11-09 16:26:02.568994492 +0000
@@ -531,6 +531,10 @@
if (val >= 0x01)
rv = 250 + (val - 1) * 5;
break;
+ case vr13:
+ if (val >= 0x01)
+ rv = 500 + (val - 1) * 10;
+ break;
}
return rv;
}

View File

@@ -0,0 +1,151 @@
Linux backport patch. Includes following commits:
f7caf758e26ab84b2b9def9ec68235c85d645597
diff -Nur a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
--- a/drivers/hwmon/pmbus/Kconfig 2017-11-09 16:34:05.269000902 +0000
+++ b/drivers/hwmon/pmbus/Kconfig 2017-11-09 16:35:49.701002288 +0000
@@ -125,6 +125,15 @@
This driver can also be built as a module. If so, the module will
be called tps40422.
+config SENSORS_TPS53679
+ tristate "TI TPS53679"
+ help
+ If you say yes here you get hardware monitoring support for TI
+ TPS53679.
+
+ This driver can also be built as a module. If so, the module will
+ be called tps53679.
+
config SENSORS_UCD9000
tristate "TI UCD90120, UCD90124, UCD90160, UCD9090, UCD90910"
default n
diff -Nur a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
--- a/drivers/hwmon/pmbus/Makefile 2017-11-09 16:34:05.269000902 +0000
+++ b/drivers/hwmon/pmbus/Makefile 2017-11-09 16:35:49.701002288 +0000
@@ -13,6 +13,7 @@
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o
+obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o
obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o
diff -Nur a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c
--- a/drivers/hwmon/pmbus/tps53679.c 1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/hwmon/pmbus/tps53679.c 2017-11-09 16:35:49.701002288 +0000
@@ -0,0 +1,113 @@
+/*
+ * Hardware monitoring driver for Texas Instruments TPS53679
+ *
+ * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017 Vadim Pasternak <vadimp@mellanox.com>
+ *
+ * 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.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "pmbus.h"
+
+#define TPS53679_PROT_VR12_5MV 0x01 /* VR12.0 mode, 5-mV DAC */
+#define TPS53679_PROT_VR12_5_10MV 0x02 /* VR12.5 mode, 10-mV DAC */
+#define TPS53679_PROT_VR13_10MV 0x04 /* VR13.0 mode, 10-mV DAC */
+#define TPS53679_PROT_IMVP8_5MV 0x05 /* IMVP8 mode, 5-mV DAC */
+#define TPS53679_PROT_VR13_5MV 0x07 /* VR13.0 mode, 5-mV DAC */
+#define TPS53679_PAGE_NUM 2
+
+static int tps53679_identify(struct i2c_client *client,
+ struct pmbus_driver_info *info)
+{
+ u8 vout_params;
+ int ret;
+
+ /* Read the register with VOUT scaling value.*/
+ ret = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE);
+ if (ret < 0)
+ return ret;
+
+ vout_params = ret & GENMASK(4, 0);
+
+ switch (vout_params) {
+ case TPS53679_PROT_VR13_10MV:
+ case TPS53679_PROT_VR12_5_10MV:
+ info->vrm_version = vr13;
+ break;
+ case TPS53679_PROT_VR13_5MV:
+ case TPS53679_PROT_VR12_5MV:
+ case TPS53679_PROT_IMVP8_5MV:
+ info->vrm_version = vr12;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct pmbus_driver_info tps53679_info = {
+ .pages = TPS53679_PAGE_NUM,
+ .format[PSC_VOLTAGE_IN] = linear,
+ .format[PSC_VOLTAGE_OUT] = vid,
+ .format[PSC_TEMPERATURE] = linear,
+ .format[PSC_CURRENT_OUT] = linear,
+ .format[PSC_POWER] = linear,
+ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP |
+ PMBUS_HAVE_POUT,
+ .func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP |
+ PMBUS_HAVE_POUT,
+ .identify = tps53679_identify,
+};
+
+static int tps53679_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ return pmbus_do_probe(client, id, &tps53679_info);
+}
+
+static const struct i2c_device_id tps53679_id[] = {
+ {"tps53679", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, tps53679_id);
+
+static const struct of_device_id tps53679_of_match[] = {
+ {.compatible = "ti,tps53679"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, tps53679_of_match);
+
+static struct i2c_driver tps53679_driver = {
+ .driver = {
+ .name = "tps53679",
+ .of_match_table = of_match_ptr(tps53679_of_match),
+ },
+ .probe = tps53679_probe,
+ .remove = pmbus_do_remove,
+ .id_table = tps53679_id,
+};
+
+module_i2c_driver(tps53679_driver);
+
+MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
+MODULE_DESCRIPTION("PMBus driver for Texas Instruments TPS53679");
+MODULE_LICENSE("GPL");

View File

@@ -1 +1,9 @@
driver-support-intel-igb-bcm5461-phy.patch
0001-i2c-mlxcpld-add-master-driver-for-Mellanox-systems.patch
0002-i2c-mux-mlxcpld-add-driver-for-Mellanox-systems.patch
0003-platform-mellanox-Introduce-Mellanox-hardware-platfo.patch
0004-platform-x86-Introduce-support-for-Mellanox-hotplug-.patch
0005-leds-add-driver-for-support-Mellanox-regmap-LEDs-for.patch
0006-Mellanox-switch-drivers-changes.patch
0007-hwmon-pmbus-Add-support-for-Intel-VID-protocol-VR13.patch
0008-hwmon-pmbus-Add-support-for-Texas-Instruments-tps536.patch

View File

@@ -43,6 +43,12 @@
#define LED_MODE_BLUE_BLINK "blue_blink"
#define LED_MODE_AUTO "cpld_control"
#define LED_BLINK_PERIOD "100"
#define LED_ON "1"
#define LED_OFF "0"
#define LED_BLINK_PERIOD_LEN 3
#define LED_MODE_LEN 1
#define VALIDATE(_id) \
do { \
if(!ONLP_OID_IS_LED(_id)) { \
@@ -94,6 +100,20 @@ led_light_mode_map_t led_map[] = {
{LED_UID, LED_MODE_AUTO, ONLP_LED_MODE_AUTO},
};
typedef struct led_colors {
enum onlp_led_id id;
const char* color1;
const char* color2;
} led_colors_t;
static led_colors_t led_colors_map[] = {
{LED_SYSTEM, "green", "red"},
{LED_FAN, "green", "red"},
{LED_PSU1, "green", "red"},
{LED_PSU2, "green", "red"},
{LED_UID, "blue", NULL},
};
static char file_names[][10] = /* must map with onlp_led_id */
{
"reserved",
@@ -176,6 +196,57 @@ static char* onlp_to_driver_led_mode(enum onlp_led_id id, onlp_led_mode_t onlp_l
return LED_MODE_OFF;
}
static int led_set_mode(onlp_oid_t id, onlp_led_mode_t mode)
{
int local_id = ONLP_OID_ID_GET(id);
char color[10]={0};
int blinking = 0;
switch (mode) {
case ONLP_LED_MODE_RED_BLINKING:
strcpy(color, "red");
blinking = 1;
break;
case ONLP_LED_MODE_GREEN_BLINKING:
strcpy(color, "green");
blinking = 1;
break;
case ONLP_LED_MODE_BLUE_BLINKING:
strcpy(color, "blue");
blinking = 1;
break;
case ONLP_LED_MODE_YELLOW_BLINKING:
strcpy(color, "yellow");
blinking = 1;
break;
case ONLP_LED_MODE_RED:
strcpy(color, "red");
break;
case ONLP_LED_MODE_GREEN:
strcpy(color, "green");
break;
case ONLP_LED_MODE_BLUE:
strcpy(color, "blue");
break;
case ONLP_LED_MODE_YELLOW:
strcpy(color, "yellow");
break;
default:
return ONLP_STATUS_E_PARAM;
}
if (blinking) {
onlp_file_write((uint8_t*)LED_BLINK_PERIOD, LED_BLINK_PERIOD_LEN,
"%s%s_%s_delay_off", prefix_path, file_names[local_id], color);
onlp_file_write((uint8_t*)LED_BLINK_PERIOD, LED_BLINK_PERIOD_LEN,
"%s%s_%s_delay_on", prefix_path, file_names[local_id], color);
}
onlp_file_write((uint8_t*)LED_ON, LED_MODE_LEN,
"%s%s_%s", prefix_path, file_names[local_id], color);
return ONLP_STATUS_OK;
}
/*
* This function will be called prior to any other onlp_ledi_* functions.
*/
@@ -183,7 +254,7 @@ int
onlp_ledi_init(void)
{
/*
* TODO setting UI LED to off when it will be supported
* ONLPD calls it too early before all BSP insfrastructure is set
*/
return ONLP_STATUS_OK;
@@ -203,6 +274,15 @@ onlp_ledi_info_get(onlp_oid_t id, onlp_led_info_t* info)
*info = linfo[ONLP_OID_ID_GET(id)];
/* Get LED mode */
if (onlp_get_kernel_ver() >= KERNEL_VERSION(4,9,30)) {
char* cmd = aim_fstrdup("%s%s_state", prefix_path, file_names[local_id]);
if(system(cmd) != 0) {
aim_free(cmd);
return ONLP_STATUS_E_INTERNAL;
}
aim_free(cmd);
}
if (onlp_file_read(data, sizeof(data), &len, "%s%s",
prefix_path, file_names[local_id]) != 0) {
return ONLP_STATUS_E_INTERNAL;
@@ -233,7 +313,19 @@ onlp_ledi_set(onlp_oid_t id, int on_or_off)
VALIDATE(id);
if (!on_or_off) {
if (onlp_get_kernel_ver() < KERNEL_VERSION(4,9,30))
return onlp_ledi_mode_set(id, ONLP_LED_MODE_OFF);
else {
int i, nsize = sizeof(led_colors_map)/sizeof(led_colors_map[0]);
for (i = 0; i < nsize; i++)
{
if (id == led_colors_map[i].id)
break;
}
if (led_colors_map[i].color1)
onlp_file_write((uint8_t*)LED_OFF, LED_MODE_LEN,
"%s%s_%s", prefix_path, file_names[id], led_colors_map[i].color1);
}
}
return ONLP_STATUS_E_UNSUPPORTED;
@@ -249,14 +341,23 @@ int
onlp_ledi_mode_set(onlp_oid_t id, onlp_led_mode_t mode)
{
int local_id;
char* driver_led_mode;
int nbytes;
VALIDATE(id);
if (onlp_get_kernel_ver() < KERNEL_VERSION(4,9,30)) {
local_id = ONLP_OID_ID_GET(id);
if (onlp_file_write((uint8_t*)onlp_to_driver_led_mode(local_id, mode), driver_value_len,
driver_led_mode = onlp_to_driver_led_mode(local_id, mode);
nbytes = strnlen(driver_led_mode, driver_value_len);
if (onlp_file_write((uint8_t*)driver_led_mode, nbytes,
"%s%s", prefix_path, file_names[local_id]) != 0) {
return ONLP_STATUS_E_INTERNAL;
}
} else {
if (led_set_mode(id, mode) != 0) {
return ONLP_STATUS_E_INTERNAL;
}
}
return ONLP_STATUS_OK;

View File

@@ -27,9 +27,37 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/utsname.h>
#include <linux/version.h>
#include <AIM/aim.h>
#include <onlplib/file.h>
#include <sys/mman.h>
#include "platform_lib.h"
/* Nothing on this platform */
int
onlp_get_kernel_ver()
{
struct utsname buff;
char ver[4];
char *p;
int i = 0;
if (uname(&buff) != 0)
return ONLP_STATUS_E_INTERNAL;
p = buff.release;
while (*p) {
if (isdigit(*p)) {
ver[i] = strtol(p, &p, 10);
i++;
if (i >= 3)
break;
} else {
p++;
}
}
return KERNEL_VERSION(ver[0], ver[1], ver[2]);
}

View File

@@ -41,6 +41,10 @@
#define PSU_POWER_PREFIX "/bsp/power/psu%d_%s"
#define IDPROM_PATH "/bsp/eeprom/%s%d_info"
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
#endif
/* LED related data
*/
enum onlp_led_id
@@ -62,5 +66,6 @@ typedef enum psu_type {
psu_type_t get_psu_type(int id, char* modelname, int modelname_len);
int onlp_fani_get_min_rpm(int id);
int onlp_get_kernel_ver(void);
#endif /* __PLATFORM_LIB_H__ */

View File

@@ -135,7 +135,6 @@ int
onlp_psui_info_get(onlp_oid_t id, onlp_psu_info_t* info)
{
int val = 0;
int ret = ONLP_STATUS_OK;
int index = ONLP_OID_ID_GET(id);
const char psu_model[]=PSU_MODEL;
@@ -156,14 +155,14 @@ onlp_psui_info_get(onlp_oid_t id, onlp_psu_info_t* info)
if (val != PSU_CABLE_PRESENT) {
info->status |= ONLP_PSU_STATUS_UNPLUGGED;
return ONLP_STATUS_OK;
}
else {
info->status &= ~ONLP_PSU_STATUS_UNPLUGGED;
}
info->status |= ONLP_PSU_STATUS_PRESENT;
_psu_info_get(info);
ret = _psu_info_get(info);
return ret;
return ONLP_STATUS_OK;
}
int

View File

@@ -47,13 +47,23 @@ msn2100_sfp_node_read_int(char *node_path, int *value)
int data_len = 0, ret = 0;
char buf[SFP_SYSFS_VALUE_LEN] = {0};
*value = -1;
char sfp_present_status[16];
char sfp_not_present_status[16];
if (onlp_get_kernel_ver() >= KERNEL_VERSION(4,9,30)) {
strcpy(sfp_present_status, "1");
strcpy(sfp_not_present_status, "0");
} else {
strcpy(sfp_present_status, "good");
strcpy(sfp_not_present_status, "not_connected");
}
ret = onlp_file_read((uint8_t*)buf, sizeof(buf), &data_len, node_path);
if (ret == 0) {
if (!strncmp(buf, SFP_PRESENT_STATUS, strlen(SFP_PRESENT_STATUS))) {
if (!strncmp(buf, sfp_present_status, strlen(sfp_present_status))) {
*value = 1;
} else if (!strncmp(buf, SFP_NOT_PRESENT_STATUS, strlen(SFP_NOT_PRESENT_STATUS))) {
} else if (!strncmp(buf, sfp_not_present_status, strlen(sfp_not_present_status))) {
*value = 0;
}
}
@@ -64,7 +74,10 @@ msn2100_sfp_node_read_int(char *node_path, int *value)
static char*
msn2100_sfp_get_port_path(int port, char *node_name)
{
if (node_name)
sprintf(sfp_node_path, "/bsp/qsfp/qsfp%d%s", port, node_name);
else
sprintf(sfp_node_path, "/bsp/qsfp/qsfp%d", port);
return sfp_node_path;
}
@@ -136,7 +149,7 @@ onlp_sfpi_presence_bitmap_get(onlp_sfp_bitmap_t* dst)
int
onlp_sfpi_eeprom_read(int port, uint8_t data[256])
{
char* path = msn2100_sfp_get_port_path(port, "");
char* path = msn2100_sfp_get_port_path(port, NULL);
/*
* Read the SFP eeprom into data[]
@@ -194,12 +207,14 @@ onlp_sfpi_dev_readw(int port, uint8_t devaddr, uint8_t addr)
int fd;
int nrd;
if (!path)
if (!path){
return ONLP_STATUS_E_MISSING;
}
fd = open(path, O_RDONLY);
if (fd < 0)
if (fd < 0) {
return ONLP_STATUS_E_MISSING;
}
lseek(fd, addr, SEEK_SET);
nrd = read(fd, &data, 2);

View File

@@ -126,13 +126,13 @@ onlp_sysi_onie_info_get(onlp_onie_info_t* onie)
int
onlp_sysi_platform_manage_leds(void)
{
int fan_number;
onlp_led_mode_t mode;
int fan_number, psu_number;
onlp_led_mode_t mode, system_mode;
int min_fan_speed;
enum onlp_led_id fan_led_id = LED_FAN;
enum onlp_led_id psu_led_id[2] = { LED_PSU1, LED_PSU2 };
int fan_problem = 0;
int psu_problem = 0;
/* after reboot, status LED should blink green, SW set to solid green */
onlp_ledi_mode_set(ONLP_OID_TYPE_CREATE(ONLP_OID_TYPE_LED,LED_SYSTEM), ONLP_LED_MODE_GREEN);
/*
* FAN Indicators
*
@@ -142,7 +142,6 @@ onlp_sysi_platform_manage_leds(void)
*
*/
mode = ONLP_LED_MODE_GREEN;
for( fan_number = 1; fan_number<= CHASSIS_FAN_COUNT; fan_number+=2)
{
/* each 2 fans had same led_fan */
@@ -150,9 +149,11 @@ onlp_sysi_platform_manage_leds(void)
/* check fans */
if(onlp_fani_info_get(ONLP_FAN_ID_CREATE(fan_number), &fi) < 0) {
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
else if(fi.status & ONLP_FAN_STATUS_FAILED) {
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
else
{
@@ -160,6 +161,7 @@ onlp_sysi_platform_manage_leds(void)
if( fi.rpm < min_fan_speed)
{
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
}
/* check fan i+1 */
@@ -168,6 +170,7 @@ onlp_sysi_platform_manage_leds(void)
}
else if(fi.status & ONLP_FAN_STATUS_FAILED) {
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
else
{
@@ -175,10 +178,35 @@ onlp_sysi_platform_manage_leds(void)
if( fi.rpm < min_fan_speed)
{
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
}
}
onlp_ledi_mode_set(ONLP_OID_TYPE_CREATE(ONLP_OID_TYPE_LED,fan_led_id), mode);
onlp_ledi_mode_set(ONLP_OID_TYPE_CREATE(ONLP_OID_TYPE_LED, LED_FAN), mode);
for (psu_number = 1; psu_number <= CHASSIS_PSU_COUNT; psu_number++)
{
onlp_psu_info_t pi;
mode = ONLP_LED_MODE_GREEN;
if(onlp_psui_info_get(ONLP_PSU_ID_CREATE(psu_number), &pi) < 0) {
mode = ONLP_LED_MODE_RED;
psu_problem = 1;
}
/* Fixed system, PSU always in. Check only cable plugged. */
else if(pi.status & ONLP_PSU_STATUS_UNPLUGGED) {
mode = ONLP_LED_MODE_RED;
psu_problem = 1;
}
onlp_ledi_mode_set(ONLP_OID_TYPE_CREATE(ONLP_OID_TYPE_LED, psu_led_id[(psu_number-1)]), mode);
}
/* Set System status LED green if no problem in FANs or PSUs */
if (fan_problem || psu_problem)
system_mode = ONLP_LED_MODE_RED;
else
system_mode = ONLP_LED_MODE_GREEN;
onlp_ledi_mode_set(ONLP_OID_TYPE_CREATE(ONLP_OID_TYPE_LED, LED_SYSTEM), system_mode);
return ONLP_STATUS_OK;
}

View File

@@ -18,7 +18,7 @@ x86-64-mlnx-msn2100-r0:
--stop=1
kernel:
<<: *kernel-3-16
<<: *kernel-4-9
args: >-
nopat

View File

@@ -244,6 +244,7 @@ _onlp_fani_info_get_fan(int local_id, onlp_fan_info_t* info)
snprintf(fullpath, sizeof(fullpath), "%s%s", PREFIX_MODULE_PATH, fan_path[(int)fru_index].status);
OPEN_READ_FILE(fullpath, r_data, nbytes, len);
if (atoi(r_data) != FAN_STATUS_OK) {
info->status &= ~ONLP_FAN_STATUS_PRESENT;
return ONLP_STATUS_OK;
}
info->status |= ONLP_FAN_STATUS_PRESENT;
@@ -303,6 +304,7 @@ _onlp_fani_info_get_fan_on_psu(int local_id, int psu_id, onlp_fan_info_t* info)
snprintf(fullpath, sizeof(fullpath), "%s%s", PREFIX_MODULE_PATH, fan_path[local_id].status);
OPEN_READ_FILE(fullpath, r_data, nbytes, len);
if (atoi(r_data) != FAN_STATUS_OK) {
info->status &= ~ONLP_FAN_STATUS_PRESENT;
return ONLP_STATUS_OK;
}
info->status |= ONLP_FAN_STATUS_PRESENT;

View File

@@ -43,6 +43,12 @@
#define LED_MODE_BLUE_BLINK "blue_blink"
#define LED_MODE_AUTO "cpld_control"
#define LED_BLINK_PERIOD "100"
#define LED_ON "1"
#define LED_OFF "0"
#define LED_BLINK_PERIOD_LEN 3
#define LED_MODE_LEN 1
#define VALIDATE(_id) \
do { \
if(!ONLP_OID_IS_LED(_id)) { \
@@ -103,6 +109,21 @@ led_light_mode_map_t led_map[] = {
{LED_PSU, LED_MODE_AUTO, ONLP_LED_MODE_AUTO}
};
typedef struct led_colors {
enum onlp_led_id id;
const char* color1;
const char* color2;
} led_colors_t;
static led_colors_t led_colors_map[] = {
{LED_SYSTEM, "green", "red"},
{LED_FAN1, "green", "red"},
{LED_FAN2, "green", "red"},
{LED_FAN3, "green", "red"},
{LED_FAN4, "green", "red"},
{LED_PSU, "green", "red"},
};
static char file_names[][10] = /* must map with onlp_led_id */
{
"reserved",
@@ -192,6 +213,57 @@ static char* onlp_to_driver_led_mode(enum onlp_led_id id, onlp_led_mode_t onlp_l
return LED_MODE_OFF;
}
static int led_set_mode(onlp_oid_t id, onlp_led_mode_t mode)
{
int local_id = ONLP_OID_ID_GET(id);
char color[10]={0};
int blinking = 0;
switch (mode) {
case ONLP_LED_MODE_RED_BLINKING:
strcpy(color, "red");
blinking = 1;
break;
case ONLP_LED_MODE_GREEN_BLINKING:
strcpy(color, "green");
blinking = 1;
break;
case ONLP_LED_MODE_BLUE_BLINKING:
strcpy(color, "blue");
blinking = 1;
break;
case ONLP_LED_MODE_YELLOW_BLINKING:
strcpy(color, "yellow");
blinking = 1;
break;
case ONLP_LED_MODE_RED:
strcpy(color, "red");
break;
case ONLP_LED_MODE_GREEN:
strcpy(color, "green");
break;
case ONLP_LED_MODE_BLUE:
strcpy(color, "blue");
break;
case ONLP_LED_MODE_YELLOW:
strcpy(color, "yellow");
break;
default:
return ONLP_STATUS_E_PARAM;
}
if (blinking) {
onlp_file_write((uint8_t*)LED_BLINK_PERIOD, LED_BLINK_PERIOD_LEN,
"%s%s_%s_delay_off", prefix_path, file_names[local_id], color);
onlp_file_write((uint8_t*)LED_BLINK_PERIOD, LED_BLINK_PERIOD_LEN,
"%s%s_%s_delay_on", prefix_path, file_names[local_id], color);
}
onlp_file_write((uint8_t*)LED_ON, LED_MODE_LEN,
"%s%s_%s", prefix_path, file_names[local_id], color);
return ONLP_STATUS_OK;
}
/*
* This function will be called prior to any other onlp_ledi_* functions.
*/
@@ -199,7 +271,7 @@ int
onlp_ledi_init(void)
{
/*
* TODO setting UI LED to off when it will be supported on MSN2410
* ONLPD calls it too early before all BSP insfrastructure is set
*/
return ONLP_STATUS_OK;
@@ -219,6 +291,15 @@ onlp_ledi_info_get(onlp_oid_t id, onlp_led_info_t* info)
*info = linfo[ONLP_OID_ID_GET(id)];
/* Get LED mode */
if (onlp_get_kernel_ver() >= KERNEL_VERSION(4,9,30)) {
char* cmd = aim_fstrdup("%s%s_state", prefix_path, file_names[local_id]);
if(system(cmd) != 0) {
aim_free(cmd);
return ONLP_STATUS_E_INTERNAL;
}
aim_free(cmd);
}
if (onlp_file_read(data, sizeof(data), &len, "%s%s",
prefix_path, file_names[local_id]) != 0) {
return ONLP_STATUS_E_INTERNAL;
@@ -249,7 +330,19 @@ onlp_ledi_set(onlp_oid_t id, int on_or_off)
VALIDATE(id);
if (!on_or_off) {
if (onlp_get_kernel_ver() < KERNEL_VERSION(4,9,30))
return onlp_ledi_mode_set(id, ONLP_LED_MODE_OFF);
else {
int i, nsize = sizeof(led_colors_map)/sizeof(led_colors_map[0]);
for (i = 0; i < nsize; i++)
{
if (id == led_colors_map[i].id)
break;
}
if (led_colors_map[i].color1)
onlp_file_write((uint8_t*)LED_OFF, LED_MODE_LEN,
"%s%s_%s", prefix_path, file_names[id], led_colors_map[i].color1);
}
}
return ONLP_STATUS_E_UNSUPPORTED;
@@ -265,15 +358,23 @@ int
onlp_ledi_mode_set(onlp_oid_t id, onlp_led_mode_t mode)
{
int local_id;
char* driver_led_mode;
int nbytes;
VALIDATE(id);
if (onlp_get_kernel_ver() < KERNEL_VERSION(4,9,30)) {
local_id = ONLP_OID_ID_GET(id);
if (onlp_file_write((uint8_t*)onlp_to_driver_led_mode(local_id, mode), driver_value_len,
"%s%s", prefix_path, file_names[local_id]) != 0)
{
driver_led_mode = onlp_to_driver_led_mode(local_id, mode);
nbytes = strnlen(driver_led_mode, driver_value_len);
if (onlp_file_write((uint8_t*)driver_led_mode, nbytes,
"%s%s", prefix_path, file_names[local_id]) != 0) {
return ONLP_STATUS_E_INTERNAL;
}
} else {
if (led_set_mode(id, mode) != 0) {
return ONLP_STATUS_E_INTERNAL;
}
}
return ONLP_STATUS_OK;

View File

@@ -27,6 +27,9 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/utsname.h>
#include <linux/version.h>
#include <AIM/aim.h>
#include <onlplib/file.h>
#include <sys/mman.h>
@@ -77,3 +80,30 @@ psu_read_eeprom(int psu_index, onlp_psu_info_t* psu_info, onlp_fan_info_t* fan_i
return ONLP_STATUS_OK;
}
int
onlp_get_kernel_ver()
{
struct utsname buff;
char ver[4];
char *p;
int i = 0;
if (uname(&buff) != 0)
return ONLP_STATUS_E_INTERNAL;
p = buff.release;
while (*p) {
if (isdigit(*p)) {
ver[i] = strtol(p, &p, 10);
i++;
if (i >= 3)
break;
} else {
p++;
}
}
return KERNEL_VERSION(ver[0], ver[1], ver[2]);
}

View File

@@ -42,6 +42,10 @@
#define PSU_POWER_PREFIX "/bsp/power/psu%d_%s"
#define IDPROM_PATH "/bsp/eeprom/%s%d_info"
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
#endif
/* LED related data
*/
enum onlp_led_id
@@ -67,5 +71,6 @@ int psu_read_eeprom(int psu_index, onlp_psu_info_t* psu_info,
onlp_fan_info_t* fan_info);
int onlp_fani_get_min_rpm(int id);
int onlp_get_kernel_ver(void);
#endif /* __PLATFORM_LIB_H__ */

View File

@@ -174,6 +174,8 @@ onlp_psui_info_get(onlp_oid_t id, onlp_psu_info_t* info)
info->status |= ONLP_PSU_STATUS_UNPLUGGED;
return ONLP_STATUS_OK;
}
else
info->status |= ONLP_PSU_STATUS_PRESENT;
/* Get the cable preset state */
if (psu_module_info_get(index, "pwr_status", &val) != 0) {
@@ -185,8 +187,6 @@ onlp_psui_info_get(onlp_oid_t id, onlp_psu_info_t* info)
return ONLP_STATUS_OK;
}
info->status |= ONLP_PSU_STATUS_PRESENT;
ret = _psu_info_get(info);
return ret;

View File

@@ -47,13 +47,23 @@ msn2410_sfp_node_read_int(char *node_path, int *value)
int data_len = 0, ret = 0;
char buf[SFP_SYSFS_VALUE_LEN] = {0};
*value = -1;
char sfp_present_status[16];
char sfp_not_present_status[16];
if (onlp_get_kernel_ver() >= KERNEL_VERSION(4,9,30)) {
strcpy(sfp_present_status, "1");
strcpy(sfp_not_present_status, "0");
} else {
strcpy(sfp_present_status, "good");
strcpy(sfp_not_present_status, "not_connected");
}
ret = onlp_file_read((uint8_t*)buf, sizeof(buf), &data_len, node_path);
if (ret == 0) {
if (!strncmp(buf, SFP_PRESENT_STATUS, strlen(SFP_PRESENT_STATUS))) {
if (!strncmp(buf, sfp_present_status, strlen(sfp_present_status))) {
*value = 1;
} else if (!strncmp(buf, SFP_NOT_PRESENT_STATUS, strlen(SFP_NOT_PRESENT_STATUS))) {
} else if (!strncmp(buf, sfp_not_present_status, strlen(sfp_not_present_status))) {
*value = 0;
}
}
@@ -64,7 +74,10 @@ msn2410_sfp_node_read_int(char *node_path, int *value)
static char*
msn2410_sfp_get_port_path(int port, char *node_name)
{
if (node_name)
sprintf(sfp_node_path, "/bsp/qsfp/qsfp%d%s", port, node_name);
else
sprintf(sfp_node_path, "/bsp/qsfp/qsfp%d", port);
return sfp_node_path;
}
@@ -139,7 +152,7 @@ onlp_sfpi_presence_bitmap_get(onlp_sfp_bitmap_t* dst)
int
onlp_sfpi_eeprom_read(int port, uint8_t data[256])
{
char* path = msn2410_sfp_get_port_path(port, "");
char* path = msn2410_sfp_get_port_path(port, NULL);
/*
* Read the SFP eeprom into data[]

View File

@@ -136,13 +136,13 @@ onlp_sysi_onie_info_get(onlp_onie_info_t* onie)
int
onlp_sysi_platform_manage_leds(void)
{
int fan_number;
onlp_led_mode_t mode;
int fan_number, psu_number;
onlp_led_mode_t mode, system_mode;
int min_fan_speed;
enum onlp_led_id fan_led_id[4] = { LED_FAN1, LED_FAN2, LED_FAN3, LED_FAN4 };
int fan_problem = 0;
int psu_problem = 0;
/* after reboot, status LED should blink green, SW set to solid green */
onlp_ledi_mode_set(ONLP_OID_TYPE_CREATE(ONLP_OID_TYPE_LED,LED_SYSTEM), ONLP_LED_MODE_GREEN);
/*
* FAN Indicators
*
@@ -159,13 +159,16 @@ onlp_sysi_platform_manage_leds(void)
mode = ONLP_LED_MODE_GREEN;
if(onlp_fani_info_get(ONLP_FAN_ID_CREATE(fan_number), &fi) < 0) {
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
else if( (fi.status & 0x1) == 0) {
else if( (fi.status & ONLP_FAN_STATUS_PRESENT) == 0) {
/* Not present */
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
else if(fi.status & ONLP_FAN_STATUS_FAILED) {
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
else
{
@@ -173,18 +176,22 @@ onlp_sysi_platform_manage_leds(void)
if( fi.rpm < min_fan_speed)
{
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
}
/* check fan i+1 */
if(onlp_fani_info_get(ONLP_FAN_ID_CREATE(fan_number+1), &fi) < 0) {
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
else if( (fi.status & 0x1) == 0) {
/* Not present */
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
else if(fi.status & ONLP_FAN_STATUS_FAILED) {
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
else
{
@@ -192,10 +199,41 @@ onlp_sysi_platform_manage_leds(void)
if( fi.rpm < min_fan_speed)
{
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
}
onlp_ledi_mode_set(ONLP_OID_TYPE_CREATE(ONLP_OID_TYPE_LED,fan_led_id[fan_number/2]), mode);
}
for (psu_number = 1; psu_number <= CHASSIS_PSU_COUNT; psu_number++)
{
onlp_psu_info_t pi;
if(onlp_psui_info_get(ONLP_PSU_ID_CREATE(psu_number), &pi) < 0) {
psu_problem = 1;
}
else if((pi.status & ONLP_PSU_STATUS_PRESENT) == 0) {
/* Not present */
psu_problem = 1;
}
else if(pi.status & ONLP_PSU_STATUS_UNPLUGGED) {
psu_problem = 1;
}
}
if (psu_problem)
mode = ONLP_LED_MODE_RED;
else
mode = ONLP_LED_MODE_GREEN;
onlp_ledi_mode_set(ONLP_OID_TYPE_CREATE(ONLP_OID_TYPE_LED, LED_PSU), mode);
/* Set System status LED green if no problem in FANs or PSUs */
if (fan_problem || psu_problem)
system_mode = ONLP_LED_MODE_RED;
else
system_mode = ONLP_LED_MODE_GREEN;
onlp_ledi_mode_set(ONLP_OID_TYPE_CREATE(ONLP_OID_TYPE_LED,LED_SYSTEM), system_mode);
return ONLP_STATUS_OK;
}

View File

@@ -18,7 +18,7 @@ x86-64-mlnx-msn2410-r0:
--stop=1
kernel:
<<: *kernel-3-16
<<: *kernel-4-9
args: >-
nopat

View File

@@ -176,7 +176,8 @@ _onlp_fani_read_fan_eeprom(int local_id, onlp_fan_info_t* info)
local_id /= 2;
}
rv = onlp_file_read(data, sizeof(data), &len, IDPROM_PATH, "fan", local_id);
rv = onlp_file_read(data, sizeof(data), &len,
IDPROM_PATH, "fan", local_id);
if (rv < 0) {
return ONLP_STATUS_E_INTERNAL;
}
@@ -243,6 +244,7 @@ _onlp_fani_info_get_fan(int local_id, onlp_fan_info_t* info)
snprintf(fullpath, sizeof(fullpath), "%s%s", PREFIX_MODULE_PATH, fan_path[(int)fru_index].status);
OPEN_READ_FILE(fullpath, r_data, nbytes, len);
if (atoi(r_data) != FAN_STATUS_OK) {
info->status &= ~ONLP_FAN_STATUS_PRESENT;
return ONLP_STATUS_OK;
}
info->status |= ONLP_FAN_STATUS_PRESENT;
@@ -302,6 +304,7 @@ _onlp_fani_info_get_fan_on_psu(int local_id, int psu_id, onlp_fan_info_t* info)
snprintf(fullpath, sizeof(fullpath), "%s%s", PREFIX_MODULE_PATH, fan_path[local_id].status);
OPEN_READ_FILE(fullpath, r_data, nbytes, len);
if (atoi(r_data) != FAN_STATUS_OK) {
info->status &= ~ONLP_FAN_STATUS_PRESENT;
return ONLP_STATUS_OK;
}
info->status |= ONLP_FAN_STATUS_PRESENT;

View File

@@ -43,6 +43,12 @@
#define LED_MODE_BLUE_BLINK "blue_blink"
#define LED_MODE_AUTO "cpld_control"
#define LED_BLINK_PERIOD "100"
#define LED_ON "1"
#define LED_OFF "0"
#define LED_BLINK_PERIOD_LEN 3
#define LED_MODE_LEN 1
#define VALIDATE(_id) \
do { \
if(!ONLP_OID_IS_LED(_id)) { \
@@ -103,6 +109,21 @@ led_light_mode_map_t led_map[] = {
{LED_PSU, LED_MODE_AUTO, ONLP_LED_MODE_AUTO}
};
typedef struct led_colors {
enum onlp_led_id id;
const char* color1;
const char* color2;
} led_colors_t;
static led_colors_t led_colors_map[] = {
{LED_SYSTEM, "green", "red"},
{LED_FAN1, "green", "red"},
{LED_FAN2, "green", "red"},
{LED_FAN3, "green", "red"},
{LED_FAN4, "green", "red"},
{LED_PSU, "green", "red"},
};
static char file_names[][10] = /* must map with onlp_led_id */
{
"reserved",
@@ -192,6 +213,57 @@ static char* onlp_to_driver_led_mode(enum onlp_led_id id, onlp_led_mode_t onlp_l
return LED_MODE_OFF;
}
static int led_set_mode(onlp_oid_t id, onlp_led_mode_t mode)
{
int local_id = ONLP_OID_ID_GET(id);
char color[10]={0};
int blinking = 0;
switch (mode) {
case ONLP_LED_MODE_RED_BLINKING:
strcpy(color, "red");
blinking = 1;
break;
case ONLP_LED_MODE_GREEN_BLINKING:
strcpy(color, "green");
blinking = 1;
break;
case ONLP_LED_MODE_BLUE_BLINKING:
strcpy(color, "blue");
blinking = 1;
break;
case ONLP_LED_MODE_YELLOW_BLINKING:
strcpy(color, "yellow");
blinking = 1;
break;
case ONLP_LED_MODE_RED:
strcpy(color, "red");
break;
case ONLP_LED_MODE_GREEN:
strcpy(color, "green");
break;
case ONLP_LED_MODE_BLUE:
strcpy(color, "blue");
break;
case ONLP_LED_MODE_YELLOW:
strcpy(color, "yellow");
break;
default:
return ONLP_STATUS_E_PARAM;
}
if (blinking) {
onlp_file_write((uint8_t*)LED_BLINK_PERIOD, LED_BLINK_PERIOD_LEN,
"%s%s_%s_delay_off", prefix_path, file_names[local_id], color);
onlp_file_write((uint8_t*)LED_BLINK_PERIOD, LED_BLINK_PERIOD_LEN,
"%s%s_%s_delay_on", prefix_path, file_names[local_id], color);
}
onlp_file_write((uint8_t*)LED_ON, LED_MODE_LEN,
"%s%s_%s", prefix_path, file_names[local_id], color);
return ONLP_STATUS_OK;
}
/*
* This function will be called prior to any other onlp_ledi_* functions.
*/
@@ -199,7 +271,7 @@ int
onlp_ledi_init(void)
{
/*
* TODO setting UI LED to off when it will be supported on SN2700
* ONLPD calls it too early before all BSP insfrastructure is set
*/
return ONLP_STATUS_OK;
@@ -219,6 +291,15 @@ onlp_ledi_info_get(onlp_oid_t id, onlp_led_info_t* info)
*info = linfo[ONLP_OID_ID_GET(id)];
/* Get LED mode */
if (onlp_get_kernel_ver() >= KERNEL_VERSION(4,9,30)) {
char* cmd = aim_fstrdup("%s%s_state", prefix_path, file_names[local_id]);
if(system(cmd) != 0) {
aim_free(cmd);
return ONLP_STATUS_E_INTERNAL;
}
aim_free(cmd);
}
if (onlp_file_read(data, sizeof(data), &len, "%s%s",
prefix_path, file_names[local_id]) != 0) {
return ONLP_STATUS_E_INTERNAL;
@@ -249,7 +330,19 @@ onlp_ledi_set(onlp_oid_t id, int on_or_off)
VALIDATE(id);
if (!on_or_off) {
if (onlp_get_kernel_ver() < KERNEL_VERSION(4,9,30))
return onlp_ledi_mode_set(id, ONLP_LED_MODE_OFF);
else {
int i, nsize = sizeof(led_colors_map)/sizeof(led_colors_map[0]);
for (i = 0; i < nsize; i++)
{
if (id == led_colors_map[i].id)
break;
}
if (led_colors_map[i].color1)
onlp_file_write((uint8_t*)LED_OFF, LED_MODE_LEN,
"%s%s_%s", prefix_path, file_names[id], led_colors_map[i].color1);
}
}
return ONLP_STATUS_E_UNSUPPORTED;
@@ -265,15 +358,23 @@ int
onlp_ledi_mode_set(onlp_oid_t id, onlp_led_mode_t mode)
{
int local_id;
char* driver_led_mode;
int nbytes;
VALIDATE(id);
if (onlp_get_kernel_ver() < KERNEL_VERSION(4,9,30)) {
local_id = ONLP_OID_ID_GET(id);
if (onlp_file_write((uint8_t*)onlp_to_driver_led_mode(local_id, mode), driver_value_len,
"%s%s", prefix_path, file_names[local_id]) != 0)
{
driver_led_mode = onlp_to_driver_led_mode(local_id, mode);
nbytes = strnlen(driver_led_mode, driver_value_len);
if (onlp_file_write((uint8_t*)driver_led_mode, nbytes,
"%s%s", prefix_path, file_names[local_id]) != 0) {
return ONLP_STATUS_E_INTERNAL;
}
} else {
if (led_set_mode(id, mode) != 0) {
return ONLP_STATUS_E_INTERNAL;
}
}
return ONLP_STATUS_OK;

View File

@@ -27,6 +27,9 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/utsname.h>
#include <linux/version.h>
#include <AIM/aim.h>
#include <onlplib/file.h>
#include <sys/mman.h>
@@ -77,3 +80,30 @@ psu_read_eeprom(int psu_index, onlp_psu_info_t* psu_info, onlp_fan_info_t* fan_i
return ONLP_STATUS_OK;
}
int
onlp_get_kernel_ver()
{
struct utsname buff;
char ver[4];
char *p;
int i = 0;
if (uname(&buff) != 0)
return ONLP_STATUS_E_INTERNAL;
p = buff.release;
while (*p) {
if (isdigit(*p)) {
ver[i] = strtol(p, &p, 10);
i++;
if (i >= 3)
break;
} else {
p++;
}
}
return KERNEL_VERSION(ver[0], ver[1], ver[2]);
}

View File

@@ -42,6 +42,10 @@
#define PSU_POWER_PREFIX "/bsp/power/psu%d_%s"
#define IDPROM_PATH "/bsp/eeprom/%s%d_info"
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
#endif
/* LED related data
*/
enum onlp_led_id
@@ -67,5 +71,6 @@ int psu_read_eeprom(int psu_index, onlp_psu_info_t* psu_info,
onlp_fan_info_t* fan_info);
int onlp_fani_get_min_rpm(int id);
int onlp_get_kernel_ver(void);
#endif /* __PLATFORM_LIB_H__ */

View File

@@ -174,6 +174,8 @@ onlp_psui_info_get(onlp_oid_t id, onlp_psu_info_t* info)
info->status |= ONLP_PSU_STATUS_UNPLUGGED;
return ONLP_STATUS_OK;
}
else
info->status |= ONLP_PSU_STATUS_PRESENT;
/* Get the cable preset state */
if (psu_module_info_get(index, "pwr_status", &val) != 0) {
@@ -185,8 +187,6 @@ onlp_psui_info_get(onlp_oid_t id, onlp_psu_info_t* info)
return ONLP_STATUS_OK;
}
info->status |= ONLP_PSU_STATUS_PRESENT;
ret = _psu_info_get(info);
return ret;

View File

@@ -47,13 +47,23 @@ sn2700_sfp_node_read_int(char *node_path, int *value)
int data_len = 0, ret = 0;
char buf[SFP_SYSFS_VALUE_LEN] = {0};
*value = -1;
char sfp_present_status[16];
char sfp_not_present_status[16];
if (onlp_get_kernel_ver() >= KERNEL_VERSION(4,9,30)) {
strcpy(sfp_present_status, "1");
strcpy(sfp_not_present_status, "0");
} else {
strcpy(sfp_present_status, "good");
strcpy(sfp_not_present_status, "not_connected");
}
ret = onlp_file_read((uint8_t*)buf, sizeof(buf), &data_len, node_path);
if (ret == 0) {
if (!strncmp(buf, SFP_PRESENT_STATUS, strlen(SFP_PRESENT_STATUS))) {
if (!strncmp(buf, sfp_present_status, strlen(sfp_present_status))) {
*value = 1;
} else if (!strncmp(buf, SFP_NOT_PRESENT_STATUS, strlen(SFP_NOT_PRESENT_STATUS))) {
} else if (!strncmp(buf, sfp_not_present_status, strlen(sfp_not_present_status))) {
*value = 0;
}
}
@@ -64,7 +74,10 @@ sn2700_sfp_node_read_int(char *node_path, int *value)
static char*
sn2700_sfp_get_port_path(int port, char *node_name)
{
if (node_name)
sprintf(sfp_node_path, "/bsp/qsfp/qsfp%d%s", port, node_name);
else
sprintf(sfp_node_path, "/bsp/qsfp/qsfp%d", port);
return sfp_node_path;
}
@@ -139,7 +152,7 @@ onlp_sfpi_presence_bitmap_get(onlp_sfp_bitmap_t* dst)
int
onlp_sfpi_eeprom_read(int port, uint8_t data[256])
{
char* path = sn2700_sfp_get_port_path(port, "");
char* path = sn2700_sfp_get_port_path(port, NULL);
/*
* Read the SFP eeprom into data[]
@@ -197,12 +210,14 @@ onlp_sfpi_dev_readw(int port, uint8_t devaddr, uint8_t addr)
int fd;
int nrd;
if (!path)
if (!path){
return ONLP_STATUS_E_MISSING;
}
fd = open(path, O_RDONLY);
if (fd < 0)
if (fd < 0) {
return ONLP_STATUS_E_MISSING;
}
lseek(fd, addr, SEEK_SET);
nrd = read(fd, &data, 2);

View File

@@ -48,7 +48,6 @@
#define PREFIX_PATH_ON_CPLD_DEV "/bsp/cpld"
#define NUM_OF_CPLD 3
static char arr_cplddev_name[NUM_OF_CPLD][30] =
{
"cpld_brd_version",
@@ -137,13 +136,13 @@ onlp_sysi_onie_info_get(onlp_onie_info_t* onie)
int
onlp_sysi_platform_manage_leds(void)
{
int fan_number;
onlp_led_mode_t mode;
int fan_number, psu_number;
onlp_led_mode_t mode, system_mode;
int min_fan_speed;
enum onlp_led_id fan_led_id[4] = { LED_FAN1, LED_FAN2, LED_FAN3, LED_FAN4 };
int fan_problem = 0;
int psu_problem = 0;
/* after reboot, status LED should blink green, SW set to solid green */
onlp_ledi_mode_set(ONLP_OID_TYPE_CREATE(ONLP_OID_TYPE_LED,LED_SYSTEM), ONLP_LED_MODE_GREEN);
/*
* FAN Indicators
*
@@ -160,13 +159,16 @@ onlp_sysi_platform_manage_leds(void)
mode = ONLP_LED_MODE_GREEN;
if(onlp_fani_info_get(ONLP_FAN_ID_CREATE(fan_number), &fi) < 0) {
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
else if( (fi.status & 0x1) == 0) {
else if( (fi.status & ONLP_FAN_STATUS_PRESENT) == 0) {
/* Not present */
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
else if(fi.status & ONLP_FAN_STATUS_FAILED) {
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
else
{
@@ -174,18 +176,22 @@ onlp_sysi_platform_manage_leds(void)
if( fi.rpm < min_fan_speed)
{
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
}
/* check fan i+1 */
if(onlp_fani_info_get(ONLP_FAN_ID_CREATE(fan_number+1), &fi) < 0) {
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
else if( (fi.status & 0x1) == 0) {
/* Not present */
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
else if(fi.status & ONLP_FAN_STATUS_FAILED) {
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
else
{
@@ -193,10 +199,41 @@ onlp_sysi_platform_manage_leds(void)
if( fi.rpm < min_fan_speed)
{
mode = ONLP_LED_MODE_RED;
fan_problem = 1;
}
}
onlp_ledi_mode_set(ONLP_OID_TYPE_CREATE(ONLP_OID_TYPE_LED,fan_led_id[fan_number/2]), mode);
}
for (psu_number = 1; psu_number <= CHASSIS_PSU_COUNT; psu_number++)
{
onlp_psu_info_t pi;
if(onlp_psui_info_get(ONLP_PSU_ID_CREATE(psu_number), &pi) < 0) {
psu_problem = 1;
}
else if((pi.status & ONLP_PSU_STATUS_PRESENT) == 0) {
/* Not present */
psu_problem = 1;
}
else if(pi.status & ONLP_PSU_STATUS_UNPLUGGED) {
psu_problem = 1;
}
}
if (psu_problem)
mode = ONLP_LED_MODE_RED;
else
mode = ONLP_LED_MODE_GREEN;
onlp_ledi_mode_set(ONLP_OID_TYPE_CREATE(ONLP_OID_TYPE_LED, LED_PSU), mode);
/* Set System status LED green if no problem in FANs or PSUs */
if (fan_problem || psu_problem)
system_mode = ONLP_LED_MODE_RED;
else
system_mode = ONLP_LED_MODE_GREEN;
onlp_ledi_mode_set(ONLP_OID_TYPE_CREATE(ONLP_OID_TYPE_LED,LED_SYSTEM), system_mode);
return ONLP_STATUS_OK;
}

View File

@@ -18,7 +18,7 @@ x86-64-mlnx-msn2700-r0:
--stop=1
kernel:
<<: *kernel-3-16
<<: *kernel-4-9
args: >-
nopat