mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	net: pse-pd: tps23881: Add support for power limit and measurement features
Expand PSE callbacks to support the newly introduced pi_get/set_pw_limit() and pi_get_voltage() functions. These callbacks allow for power limit configuration in the TPS23881 controller. Additionally, the patch includes the pi_get_pw_class() the pi_get_actual_pw(), and the pi_get_pw_limit_ranges') callbacks providing more comprehensive PoE status reporting. Acked-by: Oleksij Rempel <o.rempel@pengutronix.de> Signed-off-by: Kory Maincent <kory.maincent@bootlin.com> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
		
							parent
							
								
									4640a1f0d8
								
							
						
					
					
						commit
						7f076ce3f1
					
				
					 1 changed files with 256 additions and 2 deletions
				
			
		| 
						 | 
				
			
			@ -25,20 +25,32 @@
 | 
			
		|||
#define TPS23881_REG_GEN_MASK	0x17
 | 
			
		||||
#define TPS23881_REG_NBITACC	BIT(5)
 | 
			
		||||
#define TPS23881_REG_PW_EN	0x19
 | 
			
		||||
#define TPS23881_REG_2PAIR_POL1	0x1e
 | 
			
		||||
#define TPS23881_REG_PORT_MAP	0x26
 | 
			
		||||
#define TPS23881_REG_PORT_POWER	0x29
 | 
			
		||||
#define TPS23881_REG_POEPLUS	0x40
 | 
			
		||||
#define TPS23881_REG_4PAIR_POL1	0x2a
 | 
			
		||||
#define TPS23881_REG_INPUT_V	0x2e
 | 
			
		||||
#define TPS23881_REG_CHAN1_A	0x30
 | 
			
		||||
#define TPS23881_REG_CHAN1_V	0x32
 | 
			
		||||
#define TPS23881_REG_FOLDBACK	0x40
 | 
			
		||||
#define TPS23881_REG_TPON	BIT(0)
 | 
			
		||||
#define TPS23881_REG_FWREV	0x41
 | 
			
		||||
#define TPS23881_REG_DEVID	0x43
 | 
			
		||||
#define TPS23881_REG_DEVID_MASK	0xF0
 | 
			
		||||
#define TPS23881_DEVICE_ID	0x02
 | 
			
		||||
#define TPS23881_REG_CHAN1_CLASS	0x4c
 | 
			
		||||
#define TPS23881_REG_SRAM_CTRL	0x60
 | 
			
		||||
#define TPS23881_REG_SRAM_DATA	0x61
 | 
			
		||||
 | 
			
		||||
#define TPS23881_UV_STEP	3662
 | 
			
		||||
#define TPS23881_NA_STEP	70190
 | 
			
		||||
#define TPS23881_MW_STEP	500
 | 
			
		||||
#define TPS23881_MIN_PI_PW_LIMIT_MW	2000
 | 
			
		||||
 | 
			
		||||
struct tps23881_port_desc {
 | 
			
		||||
	u8 chan[2];
 | 
			
		||||
	bool is_4p;
 | 
			
		||||
	int pw_pol;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct tps23881_priv {
 | 
			
		||||
| 
						 | 
				
			
			@ -102,6 +114,54 @@ static u16 tps23881_set_val(u16 reg_val, u8 chan, u8 field_offset,
 | 
			
		|||
	return reg_val;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
tps23881_pi_set_pw_pol_limit(struct tps23881_priv *priv, int id, u8 pw_pol,
 | 
			
		||||
			     bool is_4p)
 | 
			
		||||
{
 | 
			
		||||
	struct i2c_client *client = priv->client;
 | 
			
		||||
	int ret, reg;
 | 
			
		||||
	u16 val;
 | 
			
		||||
	u8 chan;
 | 
			
		||||
 | 
			
		||||
	chan = priv->port[id].chan[0];
 | 
			
		||||
	if (!is_4p) {
 | 
			
		||||
		reg = TPS23881_REG_2PAIR_POL1 + (chan % 4);
 | 
			
		||||
	} else {
 | 
			
		||||
		/* One chan is enough to configure the 4p PI power limit */
 | 
			
		||||
		if ((chan % 4) < 2)
 | 
			
		||||
			reg = TPS23881_REG_4PAIR_POL1;
 | 
			
		||||
		else
 | 
			
		||||
			reg = TPS23881_REG_4PAIR_POL1 + 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = i2c_smbus_read_word_data(client, reg);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	val = tps23881_set_val(ret, chan, 0, 0xff, pw_pol);
 | 
			
		||||
	return i2c_smbus_write_word_data(client, reg, val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int tps23881_pi_enable_manual_pol(struct tps23881_priv *priv, int id)
 | 
			
		||||
{
 | 
			
		||||
	struct i2c_client *client = priv->client;
 | 
			
		||||
	int ret;
 | 
			
		||||
	u8 chan;
 | 
			
		||||
	u16 val;
 | 
			
		||||
 | 
			
		||||
	ret = i2c_smbus_read_byte_data(client, TPS23881_REG_FOLDBACK);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	/* No need to test if the chan is PoE4 as setting either bit for a
 | 
			
		||||
	 * 4P configured port disables the automatic configuration on both
 | 
			
		||||
	 * channels.
 | 
			
		||||
	 */
 | 
			
		||||
	chan = priv->port[id].chan[0];
 | 
			
		||||
	val = tps23881_set_val(ret, chan, 0, BIT(chan % 4), BIT(chan % 4));
 | 
			
		||||
	return i2c_smbus_write_byte_data(client, TPS23881_REG_FOLDBACK, val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int tps23881_pi_enable(struct pse_controller_dev *pcdev, int id)
 | 
			
		||||
{
 | 
			
		||||
	struct tps23881_priv *priv = to_tps23881_priv(pcdev);
 | 
			
		||||
| 
						 | 
				
			
			@ -171,7 +231,21 @@ static int tps23881_pi_disable(struct pse_controller_dev *pcdev, int id)
 | 
			
		|||
				       BIT(chan % 4));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val);
 | 
			
		||||
	ret = i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	/* No power policy */
 | 
			
		||||
	if (priv->port[id].pw_pol < 0)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	ret = tps23881_pi_enable_manual_pol(priv, id);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	/* Set power policy */
 | 
			
		||||
	return tps23881_pi_set_pw_pol_limit(priv, id, priv->port[id].pw_pol,
 | 
			
		||||
					    priv->port[id].is_4p);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
| 
						 | 
				
			
			@ -246,6 +320,177 @@ tps23881_pi_get_pw_status(struct pse_controller_dev *pcdev, int id,
 | 
			
		|||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int tps23881_pi_get_voltage(struct pse_controller_dev *pcdev, int id)
 | 
			
		||||
{
 | 
			
		||||
	struct tps23881_priv *priv = to_tps23881_priv(pcdev);
 | 
			
		||||
	struct i2c_client *client = priv->client;
 | 
			
		||||
	int ret;
 | 
			
		||||
	u64 uV;
 | 
			
		||||
 | 
			
		||||
	ret = i2c_smbus_read_word_data(client, TPS23881_REG_INPUT_V);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	uV = ret & 0x3fff;
 | 
			
		||||
	uV *= TPS23881_UV_STEP;
 | 
			
		||||
 | 
			
		||||
	return (int)uV;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
tps23881_pi_get_chan_current(struct tps23881_priv *priv, u8 chan)
 | 
			
		||||
{
 | 
			
		||||
	struct i2c_client *client = priv->client;
 | 
			
		||||
	int reg, ret;
 | 
			
		||||
	u64 tmp_64;
 | 
			
		||||
 | 
			
		||||
	/* Registers 0x30 to 0x3d */
 | 
			
		||||
	reg = TPS23881_REG_CHAN1_A + (chan % 4) * 4 + (chan >= 4);
 | 
			
		||||
	ret = i2c_smbus_read_word_data(client, reg);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	tmp_64 = ret & 0x3fff;
 | 
			
		||||
	tmp_64 *= TPS23881_NA_STEP;
 | 
			
		||||
	/* uA = nA / 1000 */
 | 
			
		||||
	tmp_64 = DIV_ROUND_CLOSEST_ULL(tmp_64, 1000);
 | 
			
		||||
	return (int)tmp_64;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int tps23881_pi_get_pw_class(struct pse_controller_dev *pcdev,
 | 
			
		||||
				    int id)
 | 
			
		||||
{
 | 
			
		||||
	struct tps23881_priv *priv = to_tps23881_priv(pcdev);
 | 
			
		||||
	struct i2c_client *client = priv->client;
 | 
			
		||||
	int ret, reg;
 | 
			
		||||
	u8 chan;
 | 
			
		||||
 | 
			
		||||
	chan = priv->port[id].chan[0];
 | 
			
		||||
	reg = TPS23881_REG_CHAN1_CLASS + (chan % 4);
 | 
			
		||||
	ret = i2c_smbus_read_word_data(client, reg);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	return tps23881_calc_val(ret, chan, 4, 0x0f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
tps23881_pi_get_actual_pw(struct pse_controller_dev *pcdev, int id)
 | 
			
		||||
{
 | 
			
		||||
	struct tps23881_priv *priv = to_tps23881_priv(pcdev);
 | 
			
		||||
	int ret, uV, uA;
 | 
			
		||||
	u64 tmp_64;
 | 
			
		||||
	u8 chan;
 | 
			
		||||
 | 
			
		||||
	ret = tps23881_pi_get_voltage(&priv->pcdev, id);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
	uV = ret;
 | 
			
		||||
 | 
			
		||||
	chan = priv->port[id].chan[0];
 | 
			
		||||
	ret = tps23881_pi_get_chan_current(priv, chan);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
	uA = ret;
 | 
			
		||||
 | 
			
		||||
	if (priv->port[id].is_4p) {
 | 
			
		||||
		chan = priv->port[id].chan[1];
 | 
			
		||||
		ret = tps23881_pi_get_chan_current(priv, chan);
 | 
			
		||||
		if (ret < 0)
 | 
			
		||||
			return ret;
 | 
			
		||||
		uA += ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tmp_64 = uV;
 | 
			
		||||
	tmp_64 *= uA;
 | 
			
		||||
	/* mW = uV * uA / 1000000000 */
 | 
			
		||||
	return DIV_ROUND_CLOSEST_ULL(tmp_64, 1000000000);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
tps23881_pi_get_pw_limit_chan(struct tps23881_priv *priv, u8 chan)
 | 
			
		||||
{
 | 
			
		||||
	struct i2c_client *client = priv->client;
 | 
			
		||||
	int ret, reg;
 | 
			
		||||
	u16 val;
 | 
			
		||||
 | 
			
		||||
	reg = TPS23881_REG_2PAIR_POL1 + (chan % 4);
 | 
			
		||||
	ret = i2c_smbus_read_word_data(client, reg);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	val = tps23881_calc_val(ret, chan, 0, 0xff);
 | 
			
		||||
	return val * TPS23881_MW_STEP;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int tps23881_pi_get_pw_limit(struct pse_controller_dev *pcdev, int id)
 | 
			
		||||
{
 | 
			
		||||
	struct tps23881_priv *priv = to_tps23881_priv(pcdev);
 | 
			
		||||
	int ret, mW;
 | 
			
		||||
	u8 chan;
 | 
			
		||||
 | 
			
		||||
	chan = priv->port[id].chan[0];
 | 
			
		||||
	ret = tps23881_pi_get_pw_limit_chan(priv, chan);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	mW = ret;
 | 
			
		||||
	if (priv->port[id].is_4p) {
 | 
			
		||||
		chan = priv->port[id].chan[1];
 | 
			
		||||
		ret = tps23881_pi_get_pw_limit_chan(priv, chan);
 | 
			
		||||
		if (ret < 0)
 | 
			
		||||
			return ret;
 | 
			
		||||
		mW += ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return mW;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int tps23881_pi_set_pw_limit(struct pse_controller_dev *pcdev,
 | 
			
		||||
				    int id, int max_mW)
 | 
			
		||||
{
 | 
			
		||||
	struct tps23881_priv *priv = to_tps23881_priv(pcdev);
 | 
			
		||||
	u8 pw_pol;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (max_mW < TPS23881_MIN_PI_PW_LIMIT_MW || MAX_PI_PW < max_mW) {
 | 
			
		||||
		dev_err(&priv->client->dev,
 | 
			
		||||
			"power limit %d out of ranges [%d,%d]",
 | 
			
		||||
			max_mW, TPS23881_MIN_PI_PW_LIMIT_MW, MAX_PI_PW);
 | 
			
		||||
		return -ERANGE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = tps23881_pi_enable_manual_pol(priv, id);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	pw_pol = DIV_ROUND_CLOSEST_ULL(max_mW, TPS23881_MW_STEP);
 | 
			
		||||
 | 
			
		||||
	/* Save power policy to reconfigure it after a disabled call */
 | 
			
		||||
	priv->port[id].pw_pol = pw_pol;
 | 
			
		||||
	return tps23881_pi_set_pw_pol_limit(priv, id, pw_pol,
 | 
			
		||||
					    priv->port[id].is_4p);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
tps23881_pi_get_pw_limit_ranges(struct pse_controller_dev *pcdev, int id,
 | 
			
		||||
				struct pse_pw_limit_ranges *pw_limit_ranges)
 | 
			
		||||
{
 | 
			
		||||
	struct ethtool_c33_pse_pw_limit_range *c33_pw_limit_ranges;
 | 
			
		||||
 | 
			
		||||
	c33_pw_limit_ranges = kzalloc(sizeof(*c33_pw_limit_ranges),
 | 
			
		||||
				      GFP_KERNEL);
 | 
			
		||||
	if (!c33_pw_limit_ranges)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	c33_pw_limit_ranges->min = TPS23881_MIN_PI_PW_LIMIT_MW;
 | 
			
		||||
	c33_pw_limit_ranges->max = MAX_PI_PW;
 | 
			
		||||
	pw_limit_ranges->c33_pw_limit_ranges = c33_pw_limit_ranges;
 | 
			
		||||
 | 
			
		||||
	/* Return the number of ranges */
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Parse managers subnode into a array of device node */
 | 
			
		||||
static int
 | 
			
		||||
tps23881_get_of_channels(struct tps23881_priv *priv,
 | 
			
		||||
| 
						 | 
				
			
			@ -540,6 +785,9 @@ tps23881_write_port_matrix(struct tps23881_priv *priv,
 | 
			
		|||
		if (port_matrix[i].exist)
 | 
			
		||||
			priv->port[pi_id].chan[0] = lgcl_chan;
 | 
			
		||||
 | 
			
		||||
		/* Initialize power policy internal value */
 | 
			
		||||
		priv->port[pi_id].pw_pol = -1;
 | 
			
		||||
 | 
			
		||||
		/* Set hardware port matrix for all ports */
 | 
			
		||||
		val |= hw_chan << (lgcl_chan * 2);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -665,6 +913,12 @@ static const struct pse_controller_ops tps23881_ops = {
 | 
			
		|||
	.pi_disable = tps23881_pi_disable,
 | 
			
		||||
	.pi_get_admin_state = tps23881_pi_get_admin_state,
 | 
			
		||||
	.pi_get_pw_status = tps23881_pi_get_pw_status,
 | 
			
		||||
	.pi_get_pw_class = tps23881_pi_get_pw_class,
 | 
			
		||||
	.pi_get_actual_pw = tps23881_pi_get_actual_pw,
 | 
			
		||||
	.pi_get_voltage = tps23881_pi_get_voltage,
 | 
			
		||||
	.pi_get_pw_limit = tps23881_pi_get_pw_limit,
 | 
			
		||||
	.pi_set_pw_limit = tps23881_pi_set_pw_limit,
 | 
			
		||||
	.pi_get_pw_limit_ranges = tps23881_pi_get_pw_limit_ranges,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const char fw_parity_name[] = "ti/tps23881/tps23881-parity-14.bin";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue