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:
Kory Maincent 2025-01-10 10:40:29 +01:00 committed by Paolo Abeni
parent 4640a1f0d8
commit 7f076ce3f1

View file

@ -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";