Add support for power supplies

Requires updating the kernel with a driver for the power supplies. The
current linux tree can be found in cesnet/2021-05-14.

Depends-on: https://cesnet-gerrit-czechlight/c/CzechLight/velia/+/4188
Depends-on: https://gerrit.cesnet.cz/c/CzechLight/velia/+/4188
Change-Id: I57f426feaf48a72b5b95c961e45f90323777d685
diff --git a/board/czechlight/clearfog/linux.fragment b/board/czechlight/clearfog/linux.fragment
index 825b98b..d939b71 100644
--- a/board/czechlight/clearfog/linux.fragment
+++ b/board/czechlight/clearfog/linux.fragment
@@ -11,6 +11,7 @@
 # PMBus for the PSUs
 CONFIG_PMBUS=y
 CONFIG_SENSORS_PMBUS=y
+CONFIG_SENSORS_FSP_3Y=y
 
 # SPI-to-Quad-UART
 CONFIG_SERIAL_MAX310X=y
diff --git a/board/czechlight/clearfog/patches/linux.patch b/board/czechlight/clearfog/patches/linux.patch
index da8f731..b54b573 100644
--- a/board/czechlight/clearfog/patches/linux.patch
+++ b/board/czechlight/clearfog/patches/linux.patch
@@ -1,5 +1,5 @@
 diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-mcp23s08.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-mcp23s08.txt
-index 8b94aa8f5971..2723e6143aa9 100644
+index 6ec3c8d79f49..80acd6849648 100644
 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-mcp23s08.txt
 +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-mcp23s08.txt
 @@ -144,3 +144,38 @@ gpio21: gpio@21 {
@@ -41,11 +41,57 @@
 +		gpio-line-names = ...
 +	};
 +};
+diff --git a/Documentation/hwmon/fsp-3y.rst b/Documentation/hwmon/fsp-3y.rst
+new file mode 100644
+index 000000000000..5693d83a2035
+--- /dev/null
++++ b/Documentation/hwmon/fsp-3y.rst
+@@ -0,0 +1,28 @@
++.. SPDX-License-Identifier: GPL-2.0
++
++Kernel driver fsp3y
++======================
++Supported devices:
++  * 3Y POWER YH-5151E
++  * 3Y POWER YM-2151E
++
++Author: Václav Kubernát <kubernat@cesnet.cz>
++
++Description
++-----------
++This driver implements limited support for two 3Y POWER devices.
++
++Sysfs entries
++-------------
++  * in1_input            input voltage
++  * in2_input            12V output voltage
++  * in3_input            5V output voltage
++  * curr1_input          input current
++  * curr2_input          12V output current
++  * curr3_input          5V output current
++  * fan1_input           fan rpm
++  * temp1_input          temperature 1
++  * temp2_input          temperature 2
++  * temp3_input          temperature 3
++  * power1_input         input power
++  * power2_input         output power
+diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
+index 8d5a2df1ecb6..ed32c0f200bb 100644
+--- a/Documentation/hwmon/index.rst
++++ b/Documentation/hwmon/index.rst
+@@ -62,6 +62,7 @@ Hardware Monitoring Kernel Drivers
+    f71805f
+    f71882fg
+    fam15h_power
++   fsp-3y
+    ftsteutates
+    g760a
+    g762
 diff --git a/Documentation/hwmon/max31790.rst b/Documentation/hwmon/max31790.rst
-index f301385d8cef..05ee1a3e5a01 100644
+index f301385d8cef..e70aae9f71a2 100644
 --- a/Documentation/hwmon/max31790.rst
 +++ b/Documentation/hwmon/max31790.rst
-@@ -30,14 +30,62 @@ monitoring and control of fan RPM as well as detection of fan failure.
+@@ -30,14 +30,58 @@ monitoring and control of fan RPM as well as detection of fan failure.
  Six pins are dedicated tachometer inputs. Any of the six PWM outputs can
  also be configured to serve as tachometer inputs.
  
@@ -80,11 +126,6 @@
 +The fan fault value latches, to reset it, set a value to pwm[1-6]
 +or fan[1-6]_target.
 +
-+About fan[1-12]_pulses
-+----------------------
-+You should set this depending on the fan you use. It affects what values
-+are in fan[1-12]_input and also what gets written to fan[1-6]_target.
-+
 +About fan[1-12]_div
 +-------------------
 +This value affects the measurable range of the chip. The driver sets this value
@@ -99,7 +140,6 @@
 -================== === =======================================================
 +================== === =============================================================
 +fan[1-12]_enable   RW  enable fan speed monitoring
-+fan[1-12]_pulses   RW  pulses per fan revolution (default: 2)
  fan[1-12]_input    RO  fan tachometer speed in RPM
  fan[1-12]_fault    RO  fan experienced fault
 -fan[1-6]_target    RW  desired fan speed in RPM
@@ -107,16 +147,18 @@
 +fan[1-12]_div      RW  set the measurable speed range, not available in RPM mode
 +fan[1-6]_target    RW  RPM mode = desired fan speed
 +                       PWM mode = minimum fan speed until fault
-+pwm[1-6]_enable    RW  regulator mode, 0=full speed, 1=manual (pwm) mode, 2=rpm mode
++pwm[1-6]_enable    RW  regulator mode, 0=no control, sets 0% PWM,
++				       1=manual (pwm) mode,
++				       2=rpm mode
 +                       setting rpm mode sets fan*_enable to 1
  pwm[1-6]           RW  fan target duty cycle (0-255)
 -================== === =======================================================
 +================== === =============================================================
 diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
-index 1ecf697d8d99..9f11d036c316 100644
+index 54f04e61fb83..c2ec57672c4e 100644
 --- a/drivers/hwmon/Kconfig
 +++ b/drivers/hwmon/Kconfig
-@@ -1095,6 +1095,7 @@ config SENSORS_MAX6697
+@@ -1092,6 +1092,7 @@ config SENSORS_MAX6697
  config SENSORS_MAX31790
  	tristate "Maxim MAX31790 sensor chip"
  	depends on I2C
@@ -125,7 +167,7 @@
  	  If you say yes here you get support for 6-Channel PWM-Output
  	  Fan RPM Controller.
 diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c
-index 86e6c71db685..3ef205e45dc1 100644
+index 86e6c71db685..8b04f4c01752 100644
 --- a/drivers/hwmon/max31790.c
 +++ b/drivers/hwmon/max31790.c
 @@ -12,6 +12,7 @@
@@ -136,22 +178,15 @@
  #include <linux/slab.h>
  
  /* MAX31790 registers */
-@@ -38,22 +39,65 @@
+@@ -29,6 +30,7 @@
+ #define MAX31790_FAN_CFG_RPM_MODE	0x80
+ #define MAX31790_FAN_CFG_TACH_INPUT_EN	0x08
+ #define MAX31790_FAN_CFG_TACH_INPUT	0x01
++#define MAX31790_FAN_CFG_CTRL_MON	0x10
  
- #define FAN_RPM_MIN			120
- #define FAN_RPM_MAX			7864320
-+#define MAX_PWM				0XFF80
- 
--#define RPM_FROM_REG(reg, sr)		(((reg) >> 4) ? \
--					 ((60 * (sr) * 8192) / ((reg) >> 4)) : \
--					 FAN_RPM_MAX)
--#define RPM_TO_REG(rpm, sr)		((60 * (sr) * 8192) / ((rpm) * 2))
-+#define RPM_FROM_REG(reg, sr, pulses) \
-+	(((reg) >> 5) ? \
-+	 ((60 * (sr) * 8192) / ((reg) >> 5) / (pulses)) : \
-+	 FAN_RPM_MAX)
-+#define RPM_TO_REG(rpm, sr, pulses) \
-+	((60 * (sr) * 8192) / ((rpm) * (pulses)))
+ /* Fan Dynamics register bits */
+ #define MAX31790_FAN_DYN_SR_SHIFT	5
+@@ -46,92 +48,53 @@
  
  #define NR_CHANNEL			6
  
@@ -161,20 +196,6 @@
 +#define U16_MSB(num)			(((num) & 0xFF00) >> 8)
 +#define U16_LSB(num)			((num) & 0x00FF)
 +
-+#define FAN_INFO_1_TO_6 \
-+	(HWMON_F_DIV | \
-+	HWMON_F_PULSES | \
-+	HWMON_F_ENABLE | \
-+	HWMON_F_INPUT | \
-+	HWMON_F_TARGET | \
-+	HWMON_F_FAULT)
-+
-+#define FAN_INFO_7_TO_12 \
-+	(HWMON_F_PULSES | \
-+	HWMON_F_ENABLE | \
-+	HWMON_F_INPUT | \
-+	HWMON_F_FAULT)
-+
 +static const struct regmap_range max31790_ro_range = {
 +	.range_min = MAX31790_REG_TACH_COUNT(0),
 +	.range_max = MAX31790_REG_PWMOUT(0) - 1,
@@ -185,12 +206,24 @@
 +	.n_no_ranges = 1,
 +};
 +
++static const struct regmap_range max31790_volatile_ranges[] = {
++	regmap_reg_range(MAX31790_REG_TACH_COUNT(0), MAX31790_REG_TACH_COUNT(12)),
++	regmap_reg_range(MAX31790_REG_FAN_FAULT_STATUS2, MAX31790_REG_FAN_FAULT_STATUS1),
++};
++
++static const struct regmap_access_table max31790_volatile_table = {
++	.no_ranges = max31790_volatile_ranges,
++	.n_no_ranges = 2,
++	.n_yes_ranges = 0
++};
++
 +static const struct regmap_config max31790_regmap_config = {
 +	.reg_bits = 8,
 +	.val_bits = 8,
 +	.reg_stride = 1,
 +	.max_register = MAX31790_REG_USER_BYTE_67,
 +	.wr_table = &max31790_wr_table,
++	.volatile_table = &max31790_volatile_table
 +};
 +
  /*
@@ -199,112 +232,130 @@
  struct max31790_data {
 -	struct i2c_client *client;
 +	struct regmap *regmap;
++
  	struct mutex update_lock;
- 	bool valid; /* zero until following fields are valid */
- 	unsigned long last_updated; /* in jiffies */
-+	bool full_speed[NR_CHANNEL];
-+	u8 pulses[NR_CHANNEL];
- 
- 	/* register values */
+-	bool valid; /* zero until following fields are valid */
+-	unsigned long last_updated; /* in jiffies */
+-
+-	/* register values */
  	u8 fan_config[NR_CHANNEL];
-@@ -67,53 +111,72 @@ struct max31790_data {
- static struct max31790_data *max31790_update_device(struct device *dev)
- {
- 	struct max31790_data *data = dev_get_drvdata(dev);
+ 	u8 fan_dynamics[NR_CHANNEL];
+-	u16 fault_status;
+-	u16 tach[NR_CHANNEL * 2];
+-	u16 pwm[NR_CHANNEL];
+-	u16 target_count[NR_CHANNEL];
+ };
+ 
+-static struct max31790_data *max31790_update_device(struct device *dev)
+-{
+-	struct max31790_data *data = dev_get_drvdata(dev);
 -	struct i2c_client *client = data->client;
-+	struct regmap *regmap = data->regmap;
- 	struct max31790_data *ret = data;
- 	int i;
- 	int rv;
-+	int val;
-+	u8 val_bulk[2];
- 
- 	mutex_lock(&data->update_lock);
- 
- 	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+-	struct max31790_data *ret = data;
+-	int i;
+-	int rv;
+-
+-	mutex_lock(&data->update_lock);
+-
+-	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
 -		rv = i2c_smbus_read_byte_data(client,
 -				MAX31790_REG_FAN_FAULT_STATUS1);
-+		rv = regmap_read(regmap,
-+				 MAX31790_REG_FAN_FAULT_STATUS1,
-+				 &val);
- 		if (rv < 0)
- 			goto abort;
+-		if (rv < 0)
+-			goto abort;
 -		data->fault_status = rv & 0x3F;
-+		data->fault_status = val & 0x3F;
- 
+-
 -		rv = i2c_smbus_read_byte_data(client,
 -				MAX31790_REG_FAN_FAULT_STATUS2);
-+		rv = regmap_read(regmap,
-+				 MAX31790_REG_FAN_FAULT_STATUS2,
-+				 &val);
- 		if (rv < 0)
- 			goto abort;
+-		if (rv < 0)
+-			goto abort;
 -		data->fault_status |= (rv & 0x3F) << 6;
-+		data->fault_status |= (val & 0x3F) << 6;
- 
- 		for (i = 0; i < NR_CHANNEL; i++) {
+-
+-		for (i = 0; i < NR_CHANNEL; i++) {
 -			rv = i2c_smbus_read_word_swapped(client,
 -					MAX31790_REG_TACH_COUNT(i));
-+			rv = regmap_bulk_read(regmap,
-+					      MAX31790_REG_TACH_COUNT(i),
-+					      val_bulk,
-+					      2);
- 			if (rv < 0)
- 				goto abort;
+-			if (rv < 0)
+-				goto abort;
 -			data->tach[i] = rv;
-+			data->tach[i] = BULK_TO_U16(val_bulk[0],
-+						    val_bulk[1]);
- 
- 			if (data->fan_config[i]
- 			    & MAX31790_FAN_CFG_TACH_INPUT) {
+-
+-			if (data->fan_config[i]
+-			    & MAX31790_FAN_CFG_TACH_INPUT) {
 -				rv = i2c_smbus_read_word_swapped(client,
 -					MAX31790_REG_TACH_COUNT(NR_CHANNEL
 -								+ i));
-+				rv = regmap_bulk_read(regmap,
-+						      MAX31790_REG_TACH_COUNT(NR_CHANNEL
-+									      + i),
-+					      val_bulk,
-+					      2);
- 				if (rv < 0)
- 					goto abort;
+-				if (rv < 0)
+-					goto abort;
 -				data->tach[NR_CHANNEL + i] = rv;
-+
-+				data->tach[NR_CHANNEL + i] =
-+					BULK_TO_U16(val_bulk[0],
-+						    val_bulk[1]);
- 			} else {
+-			} else {
 -				rv = i2c_smbus_read_word_swapped(client,
 -						MAX31790_REG_PWMOUT(i));
-+				rv = regmap_bulk_read(regmap,
-+						      MAX31790_REG_PWMOUT(i),
-+						      val_bulk,
-+						      2);
- 				if (rv < 0)
- 					goto abort;
+-				if (rv < 0)
+-					goto abort;
 -				data->pwm[i] = rv;
-+				data->pwm[i] = BULK_TO_U16(val_bulk[0],
-+							   val_bulk[1]);
- 
+-
 -				rv = i2c_smbus_read_word_swapped(client,
 -						MAX31790_REG_TARGET_COUNT(i));
-+				rv = regmap_bulk_read(regmap,
-+						      MAX31790_REG_TARGET_COUNT(i),
-+						      val_bulk,
-+						      2);
- 				if (rv < 0)
- 					goto abort;
+-				if (rv < 0)
+-					goto abort;
 -				data->target_count[i] = rv;
-+				data->target_count[i] =
-+					BULK_TO_U16(val_bulk[0],
-+						    val_bulk[1]);
- 			}
- 		}
+-			}
+-		}
+-
+-		data->last_updated = jiffies;
+-		data->valid = true;
+-	}
+-	goto done;
+-
+-abort:
+-	data->valid = false;
+-	ret = ERR_PTR(rv);
+-
+-done:
+-	mutex_unlock(&data->update_lock);
+-
+-	return ret;
+-}
+-
+ static const u8 tach_period[8] = { 1, 2, 4, 8, 16, 32, 32, 32 };
  
-@@ -159,6 +222,26 @@ static u8 bits_for_tach_period(int rpm)
+ static u8 get_tach_period(u8 fan_dynamics)
+@@ -159,28 +122,109 @@ static u8 bits_for_tach_period(int rpm)
  	return bits;
  }
  
-+static int bits_for_speed_range(int speed_range)
++static int read_reg_byte(struct regmap *regmap, u8 reg)
++{
++	int rv;
++	int val;
++
++	rv = regmap_read(regmap, reg, &val);
++	if (rv < 0)
++		return rv;
++
++	return val;
++}
++
++static int read_reg_word(struct regmap *regmap, u8 reg)
++{
++	int rv;
++	u8 val_bulk[2];
++
++	rv = regmap_bulk_read(regmap, reg, val_bulk, 2);
++	if (rv < 0)
++		return rv;
++
++	return BULK_TO_U16(val_bulk[0], val_bulk[1]);
++}
++
++static int write_reg_word(struct regmap *regmap, u8 reg, u16 val)
++{
++	u8 bulk_val[2];
++
++	bulk_val[0] = U16_MSB(val);
++	bulk_val[1] = U16_LSB(val);
++
++	return regmap_bulk_write(regmap, reg, bulk_val, 2);
++}
++
++static int bits_for_speed_range(long speed_range)
 +{
 +	switch (speed_range) {
 +	case 1:
@@ -327,51 +378,68 @@
  static int max31790_read_fan(struct device *dev, u32 attr, int channel,
  			     long *val)
  {
-@@ -170,18 +253,41 @@ static int max31790_read_fan(struct device *dev, u32 attr, int channel,
+-	struct max31790_data *data = max31790_update_device(dev);
+-	int sr, rpm;
+-
+-	if (IS_ERR(data))
+-		return PTR_ERR(data);
++	struct max31790_data *data = dev_get_drvdata(dev);
++	struct regmap *regmap = data->regmap;
++	int tach, fault;
  
  	switch (attr) {
  	case hwmon_fan_input:
-+		if (!(data->fan_config[channel] &
-+		      MAX31790_FAN_CFG_TACH_INPUT_EN)) {
-+			*val = 0;
-+			return 0;
-+		}
- 		sr = get_tach_period(data->fan_dynamics[channel]);
+-		sr = get_tach_period(data->fan_dynamics[channel]);
 -		rpm = RPM_FROM_REG(data->tach[channel], sr);
-+		rpm = RPM_FROM_REG(data->tach[channel],
-+				   sr,
-+				   data->pulses[channel]);
- 		*val = rpm;
+-		*val = rpm;
++		if (!(data->fan_config[channel] & MAX31790_FAN_CFG_TACH_INPUT_EN))
++			return -ENODATA;
++
++		tach = read_reg_word(regmap, MAX31790_REG_TACH_COUNT(channel));
++		if (tach < 0)
++			return tach;
++
++		*val = RPM_FROM_REG(tach, get_tach_period(data->fan_dynamics[channel]));
  		return 0;
  	case hwmon_fan_target:
- 		sr = get_tach_period(data->fan_dynamics[channel]);
+-		sr = get_tach_period(data->fan_dynamics[channel]);
 -		rpm = RPM_FROM_REG(data->target_count[channel], sr);
-+		rpm = RPM_FROM_REG(data->target_count[channel],
-+				   sr,
-+				   data->pulses[channel]);
- 		*val = rpm;
+-		*val = rpm;
++		tach = read_reg_word(regmap, MAX31790_REG_TARGET_COUNT(channel));
++		if (tach < 0)
++			return tach;
++
++		*val = RPM_FROM_REG(tach, get_tach_period(data->fan_dynamics[channel]));
  		return 0;
  	case hwmon_fan_fault:
-+		if (!(data->fan_config[channel] &
-+		      MAX31790_FAN_CFG_TACH_INPUT_EN)) {
+-		*val = !!(data->fault_status & (1 << channel));
++		if (!(data->fan_config[channel] & MAX31790_FAN_CFG_TACH_INPUT_EN)) {
 +			*val = 0;
 +			return 0;
 +		}
- 		*val = !!(data->fault_status & (1 << channel));
- 		return 0;
++
++		if (channel > 6)
++			fault = read_reg_byte(regmap, MAX31790_REG_FAN_FAULT_STATUS2);
++		else
++			fault = read_reg_byte(regmap, MAX31790_REG_FAN_FAULT_STATUS1);
++
++		if (fault < 0)
++			return fault;
++
++		if (channel > 6)
++			*val = !!(fault & (1 << (channel - 6)));
++		else
++			*val = !!(fault & (1 << channel));
++		return 0;
 +	case hwmon_fan_enable:
 +		*val = !!(data->fan_config[channel] & MAX31790_FAN_CFG_TACH_INPUT_EN);
 +		return 0;
-+	case hwmon_fan_pulses:
-+		*val = data->pulses[channel];
-+		return 0;
 +	case hwmon_fan_div:
 +		*val = get_tach_period(data->fan_config[channel]);
-+		return 0;
+ 		return 0;
  	default:
  		return -EOPNOTSUPP;
- 	}
-@@ -191,11 +297,12 @@ static int max31790_write_fan(struct device *dev, u32 attr, int channel,
+@@ -191,7 +235,7 @@ static int max31790_write_fan(struct device *dev, u32 attr, int channel,
  			      long val)
  {
  	struct max31790_data *data = dev_get_drvdata(dev);
@@ -380,39 +448,33 @@
  	int target_count;
  	int err = 0;
  	u8 bits;
- 	int sr;
-+	u8 bulk_val[2];
- 
- 	mutex_lock(&data->update_lock);
- 
-@@ -207,21 +314,55 @@ static int max31790_write_fan(struct device *dev, u32 attr, int channel,
+@@ -207,9 +251,10 @@ static int max31790_write_fan(struct device *dev, u32 attr, int channel,
  			((data->fan_dynamics[channel] &
  			  ~MAX31790_FAN_DYN_SR_MASK) |
  			 (bits << MAX31790_FAN_DYN_SR_SHIFT));
 -		err = i2c_smbus_write_byte_data(client,
 -					MAX31790_REG_FAN_DYNAMICS(channel),
 -					data->fan_dynamics[channel]);
++
 +		err = regmap_write(regmap,
 +				   MAX31790_REG_FAN_DYNAMICS(channel),
 +				   data->fan_dynamics[channel]);
  		if (err < 0)
  			break;
  
- 		sr = get_tach_period(data->fan_dynamics[channel]);
--		target_count = RPM_TO_REG(val, sr);
-+		target_count = RPM_TO_REG(val, sr, data->pulses[channel]);
+@@ -217,11 +262,38 @@ static int max31790_write_fan(struct device *dev, u32 attr, int channel,
+ 		target_count = RPM_TO_REG(val, sr);
  		target_count = clamp_val(target_count, 0x1, 0x7FF);
  
- 		data->target_count[channel] = target_count << 5;
-+		bulk_val[0] = U16_MSB(data->target_count[channel]);
-+		bulk_val[1] = U16_LSB(data->target_count[channel]);
+-		data->target_count[channel] = target_count << 5;
++		target_count = target_count << 5;
  
 -		err = i2c_smbus_write_word_swapped(client,
-+		err = regmap_bulk_write(regmap,
- 					MAX31790_REG_TARGET_COUNT(channel),
+-					MAX31790_REG_TARGET_COUNT(channel),
 -					data->target_count[channel]);
-+					bulk_val,
-+					2);
++		err = write_reg_word(regmap,
++				     MAX31790_REG_TARGET_COUNT(channel),
++				     target_count);
 +		break;
 +	case hwmon_fan_enable:
 +		if (val == 0)
@@ -423,9 +485,6 @@
 +				   MAX31790_REG_FAN_CONFIG(channel),
 +				   data->fan_config[channel]);
 +		break;
-+	case hwmon_fan_pulses:
-+		data->pulses[channel] = val;
-+		break;
 +	case hwmon_fan_div:
 +		if (data->fan_config[channel] & MAX31790_FAN_CFG_RPM_MODE) {
 +			err = -EINVAL;
@@ -437,79 +496,91 @@
 +			break;
 +		}
 +
-+		data->fan_dynamics[channel] =
-+			((data->fan_dynamics[channel] &
-+			  ~MAX31790_FAN_DYN_SR_MASK) |
-+			 (sr << MAX31790_FAN_DYN_SR_SHIFT));
++		data->fan_dynamics[channel] = ((data->fan_dynamics[channel] &
++						~MAX31790_FAN_DYN_SR_MASK) |
++					       (sr << MAX31790_FAN_DYN_SR_SHIFT));
 +		err = regmap_write(regmap,
 +				   MAX31790_REG_FAN_DYNAMICS(channel),
 +				   data->fan_dynamics[channel]);
  		break;
  	default:
  		err = -EOPNOTSUPP;
-@@ -250,6 +391,16 @@ static umode_t max31790_fan_is_visible(const void *_data, u32 attr, int channel)
+@@ -250,6 +322,12 @@ static umode_t max31790_fan_is_visible(const void *_data, u32 attr, int channel)
  		    !(fan_config & MAX31790_FAN_CFG_TACH_INPUT))
  			return 0644;
  		return 0;
 +	case hwmon_fan_enable:
++	case hwmon_fan_div:
 +		if (channel < NR_CHANNEL ||
 +		    (fan_config & MAX31790_FAN_CFG_TACH_INPUT))
 +			return 0644;
 +		return 0;
-+	case hwmon_fan_div:
-+	case hwmon_fan_pulses:
-+		if (channel < NR_CHANNEL)
-+			return 0644;
-+		return 0;
  	default:
  		return 0;
  	}
-@@ -271,12 +422,12 @@ static int max31790_read_pwm(struct device *dev, u32 attr, int channel,
- 		*val = data->pwm[channel] >> 8;
+@@ -258,25 +336,25 @@ static umode_t max31790_fan_is_visible(const void *_data, u32 attr, int channel)
+ static int max31790_read_pwm(struct device *dev, u32 attr, int channel,
+ 			     long *val)
+ {
+-	struct max31790_data *data = max31790_update_device(dev);
+-	u8 fan_config;
+-
+-	if (IS_ERR(data))
+-		return PTR_ERR(data);
+-
+-	fan_config = data->fan_config[channel];
++	struct max31790_data *data = dev_get_drvdata(dev);
++	struct regmap *regmap = data->regmap;
++	int read;
+ 
+ 	switch (attr) {
+ 	case hwmon_pwm_input:
+-		*val = data->pwm[channel] >> 8;
++		read = read_reg_word(regmap, MAX31790_REG_PWMOUT(channel));
++		if (read < 0)
++			return read;
++
++		*val = read >> 8;
  		return 0;
  	case hwmon_pwm_enable:
 -		if (fan_config & MAX31790_FAN_CFG_RPM_MODE)
-+		if (data->full_speed[channel])
++		if (data->fan_config[channel] & MAX31790_FAN_CFG_CTRL_MON)
 +			*val = 0;
-+		else if (fan_config & MAX31790_FAN_CFG_RPM_MODE)
++		else if (data->fan_config[channel] & MAX31790_FAN_CFG_RPM_MODE)
  			*val = 2;
 -		else if (fan_config & MAX31790_FAN_CFG_TACH_INPUT_EN)
--			*val = 1;
- 		else
++		else
+ 			*val = 1;
+-		else
 -			*val = 0;
-+			*val = 1;
  		return 0;
  	default:
  		return -EOPNOTSUPP;
-@@ -287,43 +438,69 @@ static int max31790_write_pwm(struct device *dev, u32 attr, int channel,
+@@ -287,43 +365,46 @@ static int max31790_write_pwm(struct device *dev, u32 attr, int channel,
  			      long val)
  {
  	struct max31790_data *data = dev_get_drvdata(dev);
 -	struct i2c_client *client = data->client;
+-	u8 fan_config;
 +	struct regmap *regmap = data->regmap;
- 	u8 fan_config;
++	u8 fan_config = data->fan_config[channel];
  	int err = 0;
-+	u8 bulk_val[2];
  
  	mutex_lock(&data->update_lock);
  
  	switch (attr) {
  	case hwmon_pwm_input:
 -		if (val < 0 || val > 255) {
-+		if (data->full_speed[channel] || val < 0 || val > 255) {
++		if (fan_config & MAX31790_FAN_CFG_CTRL_MON || val < 0 || val > 255) {
  			err = -EINVAL;
  			break;
  		}
- 		data->pwm[channel] = val << 8;
+-		data->pwm[channel] = val << 8;
 -		err = i2c_smbus_write_word_swapped(client,
 -						   MAX31790_REG_PWMOUT(channel),
 -						   data->pwm[channel]);
-+		bulk_val[0] = U16_MSB(data->pwm[channel]);
-+		bulk_val[1] = U16_LSB(data->pwm[channel]);
-+		err = regmap_bulk_write(regmap,
-+					MAX31790_REG_PWMOUT(channel),
-+					bulk_val,
-+					2);
++
++		err = write_reg_word(regmap, MAX31790_REG_PWMOUT(channel), val << 8);
  		break;
  	case hwmon_pwm_enable:
  		fan_config = data->fan_config[channel];
@@ -520,16 +591,27 @@
 -			fan_config = (fan_config |
 -				      MAX31790_FAN_CFG_TACH_INPUT_EN) &
 -				     ~MAX31790_FAN_CFG_RPM_MODE;
-+		if (val == 0 || val == 1) {
-+			fan_config &= ~MAX31790_FAN_CFG_RPM_MODE;
++		if (val == 0)
++			fan_config |= MAX31790_FAN_CFG_CTRL_MON;
++		else if (val == 1) {
++			fan_config &= ~(MAX31790_FAN_CFG_RPM_MODE | MAX31790_FAN_CFG_CTRL_MON);
  		} else if (val == 2) {
 -			fan_config |= MAX31790_FAN_CFG_TACH_INPUT_EN |
 -				      MAX31790_FAN_CFG_RPM_MODE;
++			fan_config &= ~(MAX31790_FAN_CFG_CTRL_MON);
 +			fan_config |= MAX31790_FAN_CFG_RPM_MODE;
  		} else {
  			err = -EINVAL;
  			break;
  		}
++
++		/*
++		 * RPM mode implies enabled TACH input, so enable it in RPM
++		 * mode.
++		 */
++		if (val == 2)
++			fan_config |= MAX31790_FAN_CFG_TACH_INPUT_EN;
++
  		data->fan_config[channel] = fan_config;
 -		err = i2c_smbus_write_byte_data(client,
 -					MAX31790_REG_FAN_CONFIG(channel),
@@ -537,38 +619,10 @@
 +		err = regmap_write(regmap,
 +				   MAX31790_REG_FAN_CONFIG(channel),
 +				   fan_config);
-+
-+		/*
-+		 * The chip sets PWM to zero when using its "monitor only" mode
-+		 * and 0 means full speed.
-+		 */
-+		if (val == 0) {
-+			data->full_speed[channel] = true;
-+			data->pwm[channel] = MAX_PWM;
-+			bulk_val[0] = U16_MSB(data->pwm[channel]);
-+			bulk_val[1] = U16_LSB(data->pwm[channel]);
-+			err = regmap_bulk_write(regmap,
-+						MAX31790_REG_PWMOUT(channel),
-+						bulk_val,
-+						2);
-+		} else {
-+			data->full_speed[channel] = false;
-+		}
-+
-+		/*
-+		 * RPM mode implies enabled TACH input, so enable it in RPM
-+		 * mode.
-+		 */
-+		if (val == 2) {
-+			data->fan_config[channel] |= MAX31790_FAN_CFG_TACH_INPUT_EN;
-+			err = regmap_write(regmap,
-+					   MAX31790_REG_FAN_CONFIG(channel),
-+					   data->fan_config[channel]);
-+		}
  		break;
  	default:
  		err = -EOPNOTSUPP;
-@@ -393,25 +570,25 @@ static umode_t max31790_is_visible(const void *data,
+@@ -393,18 +474,18 @@ static umode_t max31790_is_visible(const void *data,
  
  static const struct hwmon_channel_info *max31790_info[] = {
  	HWMON_CHANNEL_INFO(fan,
@@ -584,35 +638,22 @@
 -			   HWMON_F_INPUT | HWMON_F_FAULT,
 -			   HWMON_F_INPUT | HWMON_F_FAULT,
 -			   HWMON_F_INPUT | HWMON_F_FAULT),
-+		FAN_INFO_1_TO_6,
-+		FAN_INFO_1_TO_6,
-+		FAN_INFO_1_TO_6,
-+		FAN_INFO_1_TO_6,
-+		FAN_INFO_1_TO_6,
-+		FAN_INFO_1_TO_6,
-+		FAN_INFO_7_TO_12,
-+		FAN_INFO_7_TO_12,
-+		FAN_INFO_7_TO_12,
-+		FAN_INFO_7_TO_12,
-+		FAN_INFO_7_TO_12,
-+		FAN_INFO_7_TO_12),
++			   HWMON_F_DIV | HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
++			   HWMON_F_DIV | HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
++			   HWMON_F_DIV | HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
++			   HWMON_F_DIV | HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
++			   HWMON_F_DIV | HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
++			   HWMON_F_DIV | HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
++			   HWMON_F_DIV | HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_FAULT,
++			   HWMON_F_DIV | HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_FAULT,
++			   HWMON_F_DIV | HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_FAULT,
++			   HWMON_F_DIV | HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_FAULT,
++			   HWMON_F_DIV | HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_FAULT,
++			   HWMON_F_DIV | HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_FAULT),
  	HWMON_CHANNEL_INFO(pwm,
--			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
--			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
--			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
--			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
--			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
--			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
-+		HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
-+		HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
-+		HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
-+		HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
-+		HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
-+		HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
- 	NULL
- };
- 
-@@ -426,23 +603,29 @@ static const struct hwmon_chip_info max31790_chip_info = {
+ 			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ 			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+@@ -426,20 +507,18 @@ static const struct hwmon_chip_info max31790_chip_info = {
  	.info = max31790_info,
  };
  
@@ -620,37 +661,23 @@
 +static int max31790_init_client(struct regmap *regmap,
  				struct max31790_data *data)
  {
--	int i, rv;
-+	int i, rv, val;
+ 	int i, rv;
  
  	for (i = 0; i < NR_CHANNEL; i++) {
 -		rv = i2c_smbus_read_byte_data(client,
 -				MAX31790_REG_FAN_CONFIG(i));
-+		rv = regmap_read(regmap,
-+				 MAX31790_REG_FAN_CONFIG(i),
-+				 &val);
++		rv = read_reg_byte(regmap, MAX31790_REG_FAN_CONFIG(i % NR_CHANNEL));
  		if (rv < 0)
  			return rv;
--		data->fan_config[i] = rv;
-+		data->fan_config[i] = val;
-+
-+		data->full_speed[i] = false;
+ 		data->fan_config[i] = rv;
  
 -		rv = i2c_smbus_read_byte_data(client,
 -				MAX31790_REG_FAN_DYNAMICS(i));
-+		data->pulses[i] = 2;
-+
-+		rv = regmap_read(regmap,
-+				 MAX31790_REG_FAN_DYNAMICS(i),
-+				 &val);
++		rv = read_reg_byte(regmap, MAX31790_REG_FAN_DYNAMICS(i));
  		if (rv < 0)
  			return rv;
--		data->fan_dynamics[i] = rv;
-+		data->fan_dynamics[i] = val;
- 	}
- 
- 	return 0;
-@@ -464,13 +647,19 @@ static int max31790_probe(struct i2c_client *client)
+ 		data->fan_dynamics[i] = rv;
+@@ -464,13 +543,18 @@ static int max31790_probe(struct i2c_client *client)
  	if (!data)
  		return -ENOMEM;
  
@@ -658,7 +685,6 @@
  	mutex_init(&data->update_lock);
  
 +	data->regmap = devm_regmap_init_i2c(client, &max31790_regmap_config);
-+
 +	if (IS_ERR(data->regmap)) {
 +		dev_err(dev, "failed to allocate register map\n");
 +		return PTR_ERR(data->regmap);
@@ -672,7 +698,7 @@
  	if (err)
  		return err;
  
-@@ -488,11 +677,18 @@ static const struct i2c_device_id max31790_id[] = {
+@@ -488,11 +572,18 @@ static const struct i2c_device_id max31790_id[] = {
  };
  MODULE_DEVICE_TABLE(i2c, max31790_id);
  
@@ -691,6 +717,339 @@
  	},
  	.id_table	= max31790_id,
  };
+diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
+index 32d2fc850621..5ec5b3bd1986 100644
+--- a/drivers/hwmon/pmbus/Kconfig
++++ b/drivers/hwmon/pmbus/Kconfig
+@@ -56,6 +56,16 @@ config SENSORS_BEL_PFE
+ 	  This driver can also be built as a module. If so, the module will
+ 	  be called bel-pfe.
+ 
++config SENSORS_FSP_3Y
++	tristate "FSP/3Y-Power power supplies"
++	help
++	  If you say yes here you get hardware monitoring support for
++	  FSP/3Y-Power hot-swap power supplies.
++	  Supported models: YH-5151E, YM-2151E
++
++	  This driver can also be built as a module. If so, the module will
++	  be called fsp-3y.
++
+ config SENSORS_IBM_CFFPS
+ 	tristate "IBM Common Form Factor Power Supply"
+ 	depends on LEDS_CLASS
+diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
+index 6a4ba0fdc1db..bfe218ad898f 100644
+--- a/drivers/hwmon/pmbus/Makefile
++++ b/drivers/hwmon/pmbus/Makefile
+@@ -8,6 +8,7 @@ obj-$(CONFIG_SENSORS_PMBUS)	+= pmbus.o
+ obj-$(CONFIG_SENSORS_ADM1266)	+= adm1266.o
+ obj-$(CONFIG_SENSORS_ADM1275)	+= adm1275.o
+ obj-$(CONFIG_SENSORS_BEL_PFE)	+= bel-pfe.o
++obj-$(CONFIG_SENSORS_FSP_3Y)	+= fsp-3y.o
+ obj-$(CONFIG_SENSORS_IBM_CFFPS)	+= ibm-cffps.o
+ obj-$(CONFIG_SENSORS_INSPUR_IPSPS) += inspur-ipsps.o
+ obj-$(CONFIG_SENSORS_IR35221)	+= ir35221.o
+diff --git a/drivers/hwmon/pmbus/fsp-3y.c b/drivers/hwmon/pmbus/fsp-3y.c
+new file mode 100644
+index 000000000000..073d960a0af5
+--- /dev/null
++++ b/drivers/hwmon/pmbus/fsp-3y.c
+@@ -0,0 +1,294 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Hardware monitoring driver for FSP 3Y-Power PSUs
++ *
++ * Copyright (c) 2021 Václav Kubernát, CESNET
++ *
++ * This driver is mostly reverse engineered with the help of a tool called pmbus_peek written by
++ * David Brownell (and later adopted by Jan Kundrát). The device has some sort of a timing issue
++ * when switching pages, details are explained in the code. The driver support is limited. It
++ * exposes only the values, that have been tested to work correctly. Unsupported values either
++ * aren't supported by the devices or their encondings are unknown.
++ */
++
++#include <linux/delay.h>
++#include <linux/i2c.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include "pmbus.h"
++
++#define YM2151_PAGE_12V_LOG	0x00
++#define YM2151_PAGE_12V_REAL	0x00
++#define YM2151_PAGE_5VSB_LOG	0x01
++#define YM2151_PAGE_5VSB_REAL	0x20
++#define YH5151E_PAGE_12V_LOG	0x00
++#define YH5151E_PAGE_12V_REAL	0x00
++#define YH5151E_PAGE_5V_LOG	0x01
++#define YH5151E_PAGE_5V_REAL	0x10
++#define YH5151E_PAGE_3V3_LOG	0x02
++#define YH5151E_PAGE_3V3_REAL	0x11
++
++enum chips {
++	ym2151e,
++	yh5151e
++};
++
++struct fsp3y_data {
++	struct pmbus_driver_info info;
++	int chip;
++	int page;
++
++	bool vout_linear_11;
++};
++
++#define to_fsp3y_data(x) container_of(x, struct fsp3y_data, info)
++
++static int page_log_to_page_real(int page_log, enum chips chip)
++{
++	switch (chip) {
++	case ym2151e:
++		switch (page_log) {
++		case YM2151_PAGE_12V_LOG:
++			return YM2151_PAGE_12V_REAL;
++		case YM2151_PAGE_5VSB_LOG:
++			return YM2151_PAGE_5VSB_REAL;
++		}
++		return -EINVAL;
++	case yh5151e:
++		switch (page_log) {
++		case YH5151E_PAGE_12V_LOG:
++			return YH5151E_PAGE_12V_REAL;
++		case YH5151E_PAGE_5V_LOG:
++			return YH5151E_PAGE_5V_REAL;
++		case YH5151E_PAGE_3V3_LOG:
++			return YH5151E_PAGE_3V3_REAL;
++		}
++		return -EINVAL;
++	}
++
++	return -EINVAL;
++}
++
++static int set_page(struct i2c_client *client, int page_log)
++{
++	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
++	struct fsp3y_data *data = to_fsp3y_data(info);
++	int rv;
++	int page_real;
++
++	if (page_log < 0)
++		return 0;
++
++	page_real = page_log_to_page_real(page_log, data->chip);
++	if (page_real < 0)
++		return page_real;
++
++	if (data->page != page_real) {
++		rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page_real);
++		if (rv < 0)
++			return rv;
++
++		data->page = page_real;
++
++		/*
++		 * Testing showed that the device has a timing issue. After
++		 * setting a page, it takes a while, before the device actually
++		 * gives the correct values from the correct page. 20 ms was
++		 * tested to be enough to not give wrong values (15 ms wasn't
++		 * enough).
++		 */
++		usleep_range(20000, 30000);
++	}
++
++	return 0;
++}
++
++static int fsp3y_read_byte_data(struct i2c_client *client, int page, int reg)
++{
++	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
++	struct fsp3y_data *data = to_fsp3y_data(info);
++	int rv;
++
++	/*
++	 * Inject an exponent for non-compliant YH5151-E.
++	 */
++	if (data->vout_linear_11 && reg == PMBUS_VOUT_MODE)
++		return 0x1A;
++
++	rv = set_page(client, page);
++	if (rv < 0)
++		return rv;
++
++	return i2c_smbus_read_byte_data(client, reg);
++}
++
++static int fsp3y_read_word_data(struct i2c_client *client, int page, int phase, int reg)
++{
++	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
++	struct fsp3y_data *data = to_fsp3y_data(info);
++	int rv;
++
++	/*
++	 * This masks commands which weren't tested to work correctly. Some of
++	 * the masked commands return 0xFFFF. These would probably get tagged as
++	 * invalid by pmbus_core. Other ones do return values which might be
++	 * useful (that is, they are not 0xFFFF), but their encoding is unknown,
++	 * and so they are unsupported.
++	 */
++	switch (reg) {
++	case PMBUS_READ_FAN_SPEED_1:
++	case PMBUS_READ_IIN:
++	case PMBUS_READ_IOUT:
++	case PMBUS_READ_PIN:
++	case PMBUS_READ_POUT:
++	case PMBUS_READ_TEMPERATURE_1:
++	case PMBUS_READ_TEMPERATURE_2:
++	case PMBUS_READ_TEMPERATURE_3:
++	case PMBUS_READ_VIN:
++	case PMBUS_READ_VOUT:
++	case PMBUS_STATUS_WORD:
++		break;
++	default:
++		return -ENXIO;
++	}
++
++	rv = set_page(client, page);
++	if (rv < 0)
++		return rv;
++
++	rv = i2c_smbus_read_word_data(client, reg);
++	if (rv < 0)
++		return rv;
++
++	/*
++	 * Handle YH-5151E non-compliant linear11 vout voltage.
++	 */
++	if (data->vout_linear_11 && reg == PMBUS_READ_VOUT)
++		rv = sign_extend32(rv, 10) & 0xffff;
++
++	return rv;
++}
++
++static struct pmbus_driver_info fsp3y_info[] = {
++	[ym2151e] = {
++		.pages = 2,
++		.func[YM2151_PAGE_12V_LOG] =
++			PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
++			PMBUS_HAVE_PIN | PMBUS_HAVE_POUT  |
++			PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 |
++			PMBUS_HAVE_VIN | PMBUS_HAVE_IIN |
++			PMBUS_HAVE_FAN12,
++		.func[YM2151_PAGE_5VSB_LOG] =
++			PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT,
++			PMBUS_HAVE_IIN,
++		.read_word_data = fsp3y_read_word_data,
++		.read_byte_data = fsp3y_read_byte_data,
++	},
++	[yh5151e] = {
++		.pages = 3,
++		.func[YH5151E_PAGE_12V_LOG] =
++			PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
++			PMBUS_HAVE_POUT  |
++			PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3,
++		.func[YH5151E_PAGE_5V_LOG] =
++			PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
++			PMBUS_HAVE_POUT,
++		.func[YH5151E_PAGE_3V3_LOG] =
++			PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
++			PMBUS_HAVE_POUT,
++		.read_word_data = fsp3y_read_word_data,
++		.read_byte_data = fsp3y_read_byte_data,
++	}
++};
++
++static int fsp3y_detect(struct i2c_client *client)
++{
++	int rv;
++	u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
++
++	rv = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
++	if (rv < 0)
++		return rv;
++
++	buf[rv] = '\0';
++
++	if (rv == 8) {
++		if (!strcmp(buf, "YM-2151E"))
++			return ym2151e;
++		else if (!strcmp(buf, "YH-5151E"))
++			return yh5151e;
++	}
++
++	dev_err(&client->dev, "Unsupported model %.*s\n", rv, buf);
++	return -ENODEV;
++}
++
++static const struct i2c_device_id fsp3y_id[] = {
++	{"ym2151e", ym2151e},
++	{"yh5151e", yh5151e},
++	{ }
++};
++
++static int fsp3y_probe(struct i2c_client *client)
++{
++	struct fsp3y_data *data;
++	const struct i2c_device_id *id;
++	int rv;
++
++	data = devm_kzalloc(&client->dev, sizeof(struct fsp3y_data), GFP_KERNEL);
++	if (!data)
++		return -ENOMEM;
++
++	data->chip = fsp3y_detect(client);
++	if (data->chip < 0)
++		return data->chip;
++
++	id = i2c_match_id(fsp3y_id, client);
++	if (data->chip != id->driver_data)
++		dev_warn(&client->dev, "Device mismatch: Configured %s (%d), detected %d\n",
++			 id->name, (int)id->driver_data, data->chip);
++
++	rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
++	if (rv < 0)
++		return rv;
++	data->page = rv;
++
++	data->info = fsp3y_info[data->chip];
++
++	/*
++	 * YH-5151E sometimes reports vout in linear11 and sometimes in
++	 * linear16. This depends on the exact individual piece of hardware. One
++	 * YH-5151E can use linear16 and another might use linear11 instead.
++	 *
++	 * The format can be recognized by reading VOUT_MODE - if it doesn't
++	 * report a valid exponent, then vout uses linear11. Otherwise, the
++	 * device is compliant and uses linear16.
++	 */
++	data->vout_linear_11 = false;
++	if (data->chip == yh5151e) {
++		rv = i2c_smbus_read_byte_data(client, PMBUS_VOUT_MODE);
++		if (rv < 0)
++			return rv;
++
++		if (rv == 0xFF)
++			data->vout_linear_11 = true;
++	}
++
++	return pmbus_do_probe(client, &data->info);
++}
++
++MODULE_DEVICE_TABLE(i2c, fsp3y_id);
++
++static struct i2c_driver fsp3y_driver = {
++	.driver = {
++		   .name = "fsp3y",
++		   },
++	.probe_new = fsp3y_probe,
++	.id_table = fsp3y_id
++};
++
++module_i2c_driver(fsp3y_driver);
++
++MODULE_AUTHOR("Václav Kubernát");
++MODULE_DESCRIPTION("PMBus driver for FSP/3Y-Power power supplies");
++MODULE_LICENSE("GPL");
 diff --git a/drivers/leds/leds-tlc591xx.c b/drivers/leds/leds-tlc591xx.c
 index 5b9dfdf743ec..26ea1164cb49 100644
 --- a/drivers/leds/leds-tlc591xx.c
@@ -751,7 +1110,7 @@
  	if (err < 0)
  		return err;
 diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
-index 03f78fdb0dcd..7e2e12f70d70 100644
+index a45cd2b416c8..aa4c38a0952d 100644
 --- a/drivers/net/ethernet/intel/igb/igb_main.c
 +++ b/drivers/net/ethernet/intel/igb/igb_main.c
 @@ -577,16 +577,15 @@ static void igb_set_i2c_data(void *data, int state)
@@ -875,7 +1234,7 @@
  	data->ngpio = ngpio;
  
 diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c
-index b57b8b3cc26e..056d107d8f32 100644
+index 68ed7fd64256..3fd04d5eeb14 100644
 --- a/drivers/spi/spi-orion.c
 +++ b/drivers/spi/spi-orion.c
 @@ -86,10 +86,6 @@ struct orion_direct_acc {
@@ -889,16 +1248,16 @@
  struct orion_spi {
  	struct spi_master	*master;
  	void __iomem		*base;
-@@ -97,7 +93,7 @@ struct orion_spi {
- 	struct clk              *axi_clk;
+@@ -98,7 +94,7 @@ struct orion_spi {
  	const struct orion_spi_dev *devdata;
+ 	struct device		*dev;
  
 -	struct orion_child_options	child[ORION_NUM_CHIPSELECTS];
 +	struct orion_direct_acc	direct_access[ORION_NUM_CHIPSELECTS];
  };
  
- static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg)
-@@ -434,7 +430,7 @@ orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
+ #ifdef CONFIG_PM
+@@ -463,7 +459,7 @@ orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
  	int cs = spi->chip_select;
  	void __iomem *vaddr;
  
@@ -907,16 +1266,16 @@
  	count = xfer->len;
  
  	orion_spi = spi_master_get_devdata(spi->master);
-@@ -443,7 +439,7 @@ orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
- 	 * Use SPI direct write mode if base address is available. Otherwise
- 	 * fall back to PIO mode for this transfer.
+@@ -473,7 +469,7 @@ orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
+ 	 * and SPI_CS_WORD flag is not set.
+ 	 * Otherwise fall back to PIO mode for this transfer.
  	 */
 -	vaddr = orion_spi->child[cs].direct_access.vaddr;
 +	vaddr = orion_spi->direct_access[cs].vaddr;
  
- 	if (vaddr && xfer->tx_buf && word_len == 8) {
+ 	if (vaddr && xfer->tx_buf && word_len == 8 && (spi->mode & SPI_CS_WORD) == 0) {
  		unsigned int cnt = count / 4;
-@@ -682,7 +678,6 @@ static int orion_spi_probe(struct platform_device *pdev)
+@@ -727,7 +723,6 @@ static int orion_spi_probe(struct platform_device *pdev)
  	}
  
  	for_each_available_child_of_node(pdev->dev.of_node, np) {
@@ -924,7 +1283,7 @@
  		u32 cs;
  
  		/* Get chip-select number from the "reg" property */
-@@ -711,13 +706,14 @@ static int orion_spi_probe(struct platform_device *pdev)
+@@ -756,13 +751,14 @@ static int orion_spi_probe(struct platform_device *pdev)
  		 * This needs to get extended for the direct SPI NOR / SPI NAND
  		 * support, once this gets implemented.
  		 */
@@ -944,7 +1303,7 @@
  		dev_info(&pdev->dev, "CS%d configured for direct access\n", cs);
  	}
 diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
-index 9795b2e8b0b2..67e7c310a7dc 100644
+index 1b61d26bb7af..e92f3c02c7f4 100644
 --- a/drivers/tty/serial/max310x.c
 +++ b/drivers/tty/serial/max310x.c
 @@ -235,6 +235,10 @@
@@ -977,7 +1336,7 @@
  		}
  	}
  
-@@ -1346,6 +1353,10 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty
+@@ -1327,6 +1334,10 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty
  	}
  
  	uartclk = max310x_set_ref_clk(dev, s, freq, xtal);
diff --git a/board/czechlight/clearfog/sdn-roadm-clearfog.dtsi b/board/czechlight/clearfog/sdn-roadm-clearfog.dtsi
index 32255a3..6a8715a 100644
--- a/board/czechlight/clearfog/sdn-roadm-clearfog.dtsi
+++ b/board/czechlight/clearfog/sdn-roadm-clearfog.dtsi
@@ -22,7 +22,7 @@
 		};
 	};
 
-	gpio_i2c {
+	gpio_i2c: gpio_i2c {
 		compatible = "i2c-gpio";
 		sda-gpios = <&gpio0 25 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
 		scl-gpios = <&gpio0 24 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
@@ -34,6 +34,13 @@
 	};
 };
 
+&gpio_i2c {
+	pdu@25 {
+		compatible = "fsp,yh5151e";
+		reg = <0x25>;
+	};
+};
+
 &pinctrl {
 	uart1_pins_i2c_bb: uart1-pins-i2c-bb {
 		marvell,pins = "mpp24", "mpp25";
diff --git a/configs/czechlight_clearfog_defconfig b/configs/czechlight_clearfog_defconfig
index 9a2f461..08eca82 100644
--- a/configs/czechlight_clearfog_defconfig
+++ b/configs/czechlight_clearfog_defconfig
@@ -28,7 +28,7 @@
 BR2_ROOTFS_POST_SCRIPT_ARGS="-c $(BR2_EXTERNAL_CZECHLIGHT_PATH)/board/czechlight/clearfog/genimage.cfg"
 BR2_LINUX_KERNEL=y
 BR2_LINUX_KERNEL_CUSTOM_VERSION=y
-BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="5.11.5"
+BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="5.12.4"
 BR2_LINUX_KERNEL_PATCH="$(BR2_EXTERNAL_CZECHLIGHT_PATH)/board/czechlight/clearfog/patches/linux.patch"
 BR2_LINUX_KERNEL_DEFCONFIG="mvebu_v7"
 BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES="$(BR2_EXTERNAL_CZECHLIGHT_PATH)/board/czechlight/clearfog/linux.fragment $(BR2_EXTERNAL_CZECHLIGHT_PATH)/board/czechlight/common/linux.fragment"
diff --git a/submodules/velia b/submodules/velia
index 63b2d65..281af6b 160000
--- a/submodules/velia
+++ b/submodules/velia
@@ -1 +1 @@
-Subproject commit 63b2d65d54f8ae14347a0c45764a2bf600b4ed08
+Subproject commit 281af6befffa10d0def0d2705418fe4e57e8cfd1