mirror of
https://github.com/torvalds/linux.git
synced 2025-11-03 10:10:33 +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