Skip to content

Commit 7f076ce

Browse files
kmaincentPaolo Abeni
authored andcommitted
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 <[email protected]> Signed-off-by: Kory Maincent <[email protected]> Signed-off-by: Paolo Abeni <[email protected]>
1 parent 4640a1f commit 7f076ce

File tree

1 file changed

+256
-2
lines changed

1 file changed

+256
-2
lines changed

drivers/net/pse-pd/tps23881.c

Lines changed: 256 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,32 @@
2525
#define TPS23881_REG_GEN_MASK 0x17
2626
#define TPS23881_REG_NBITACC BIT(5)
2727
#define TPS23881_REG_PW_EN 0x19
28+
#define TPS23881_REG_2PAIR_POL1 0x1e
2829
#define TPS23881_REG_PORT_MAP 0x26
2930
#define TPS23881_REG_PORT_POWER 0x29
30-
#define TPS23881_REG_POEPLUS 0x40
31+
#define TPS23881_REG_4PAIR_POL1 0x2a
32+
#define TPS23881_REG_INPUT_V 0x2e
33+
#define TPS23881_REG_CHAN1_A 0x30
34+
#define TPS23881_REG_CHAN1_V 0x32
35+
#define TPS23881_REG_FOLDBACK 0x40
3136
#define TPS23881_REG_TPON BIT(0)
3237
#define TPS23881_REG_FWREV 0x41
3338
#define TPS23881_REG_DEVID 0x43
3439
#define TPS23881_REG_DEVID_MASK 0xF0
3540
#define TPS23881_DEVICE_ID 0x02
41+
#define TPS23881_REG_CHAN1_CLASS 0x4c
3642
#define TPS23881_REG_SRAM_CTRL 0x60
3743
#define TPS23881_REG_SRAM_DATA 0x61
3844

45+
#define TPS23881_UV_STEP 3662
46+
#define TPS23881_NA_STEP 70190
47+
#define TPS23881_MW_STEP 500
48+
#define TPS23881_MIN_PI_PW_LIMIT_MW 2000
49+
3950
struct tps23881_port_desc {
4051
u8 chan[2];
4152
bool is_4p;
53+
int pw_pol;
4254
};
4355

4456
struct tps23881_priv {
@@ -102,6 +114,54 @@ static u16 tps23881_set_val(u16 reg_val, u8 chan, u8 field_offset,
102114
return reg_val;
103115
}
104116

117+
static int
118+
tps23881_pi_set_pw_pol_limit(struct tps23881_priv *priv, int id, u8 pw_pol,
119+
bool is_4p)
120+
{
121+
struct i2c_client *client = priv->client;
122+
int ret, reg;
123+
u16 val;
124+
u8 chan;
125+
126+
chan = priv->port[id].chan[0];
127+
if (!is_4p) {
128+
reg = TPS23881_REG_2PAIR_POL1 + (chan % 4);
129+
} else {
130+
/* One chan is enough to configure the 4p PI power limit */
131+
if ((chan % 4) < 2)
132+
reg = TPS23881_REG_4PAIR_POL1;
133+
else
134+
reg = TPS23881_REG_4PAIR_POL1 + 1;
135+
}
136+
137+
ret = i2c_smbus_read_word_data(client, reg);
138+
if (ret < 0)
139+
return ret;
140+
141+
val = tps23881_set_val(ret, chan, 0, 0xff, pw_pol);
142+
return i2c_smbus_write_word_data(client, reg, val);
143+
}
144+
145+
static int tps23881_pi_enable_manual_pol(struct tps23881_priv *priv, int id)
146+
{
147+
struct i2c_client *client = priv->client;
148+
int ret;
149+
u8 chan;
150+
u16 val;
151+
152+
ret = i2c_smbus_read_byte_data(client, TPS23881_REG_FOLDBACK);
153+
if (ret < 0)
154+
return ret;
155+
156+
/* No need to test if the chan is PoE4 as setting either bit for a
157+
* 4P configured port disables the automatic configuration on both
158+
* channels.
159+
*/
160+
chan = priv->port[id].chan[0];
161+
val = tps23881_set_val(ret, chan, 0, BIT(chan % 4), BIT(chan % 4));
162+
return i2c_smbus_write_byte_data(client, TPS23881_REG_FOLDBACK, val);
163+
}
164+
105165
static int tps23881_pi_enable(struct pse_controller_dev *pcdev, int id)
106166
{
107167
struct tps23881_priv *priv = to_tps23881_priv(pcdev);
@@ -171,7 +231,21 @@ static int tps23881_pi_disable(struct pse_controller_dev *pcdev, int id)
171231
BIT(chan % 4));
172232
}
173233

174-
return i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val);
234+
ret = i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val);
235+
if (ret)
236+
return ret;
237+
238+
/* No power policy */
239+
if (priv->port[id].pw_pol < 0)
240+
return 0;
241+
242+
ret = tps23881_pi_enable_manual_pol(priv, id);
243+
if (ret < 0)
244+
return ret;
245+
246+
/* Set power policy */
247+
return tps23881_pi_set_pw_pol_limit(priv, id, priv->port[id].pw_pol,
248+
priv->port[id].is_4p);
175249
}
176250

177251
static int
@@ -246,6 +320,177 @@ tps23881_pi_get_pw_status(struct pse_controller_dev *pcdev, int id,
246320
return 0;
247321
}
248322

323+
static int tps23881_pi_get_voltage(struct pse_controller_dev *pcdev, int id)
324+
{
325+
struct tps23881_priv *priv = to_tps23881_priv(pcdev);
326+
struct i2c_client *client = priv->client;
327+
int ret;
328+
u64 uV;
329+
330+
ret = i2c_smbus_read_word_data(client, TPS23881_REG_INPUT_V);
331+
if (ret < 0)
332+
return ret;
333+
334+
uV = ret & 0x3fff;
335+
uV *= TPS23881_UV_STEP;
336+
337+
return (int)uV;
338+
}
339+
340+
static int
341+
tps23881_pi_get_chan_current(struct tps23881_priv *priv, u8 chan)
342+
{
343+
struct i2c_client *client = priv->client;
344+
int reg, ret;
345+
u64 tmp_64;
346+
347+
/* Registers 0x30 to 0x3d */
348+
reg = TPS23881_REG_CHAN1_A + (chan % 4) * 4 + (chan >= 4);
349+
ret = i2c_smbus_read_word_data(client, reg);
350+
if (ret < 0)
351+
return ret;
352+
353+
tmp_64 = ret & 0x3fff;
354+
tmp_64 *= TPS23881_NA_STEP;
355+
/* uA = nA / 1000 */
356+
tmp_64 = DIV_ROUND_CLOSEST_ULL(tmp_64, 1000);
357+
return (int)tmp_64;
358+
}
359+
360+
static int tps23881_pi_get_pw_class(struct pse_controller_dev *pcdev,
361+
int id)
362+
{
363+
struct tps23881_priv *priv = to_tps23881_priv(pcdev);
364+
struct i2c_client *client = priv->client;
365+
int ret, reg;
366+
u8 chan;
367+
368+
chan = priv->port[id].chan[0];
369+
reg = TPS23881_REG_CHAN1_CLASS + (chan % 4);
370+
ret = i2c_smbus_read_word_data(client, reg);
371+
if (ret < 0)
372+
return ret;
373+
374+
return tps23881_calc_val(ret, chan, 4, 0x0f);
375+
}
376+
377+
static int
378+
tps23881_pi_get_actual_pw(struct pse_controller_dev *pcdev, int id)
379+
{
380+
struct tps23881_priv *priv = to_tps23881_priv(pcdev);
381+
int ret, uV, uA;
382+
u64 tmp_64;
383+
u8 chan;
384+
385+
ret = tps23881_pi_get_voltage(&priv->pcdev, id);
386+
if (ret < 0)
387+
return ret;
388+
uV = ret;
389+
390+
chan = priv->port[id].chan[0];
391+
ret = tps23881_pi_get_chan_current(priv, chan);
392+
if (ret < 0)
393+
return ret;
394+
uA = ret;
395+
396+
if (priv->port[id].is_4p) {
397+
chan = priv->port[id].chan[1];
398+
ret = tps23881_pi_get_chan_current(priv, chan);
399+
if (ret < 0)
400+
return ret;
401+
uA += ret;
402+
}
403+
404+
tmp_64 = uV;
405+
tmp_64 *= uA;
406+
/* mW = uV * uA / 1000000000 */
407+
return DIV_ROUND_CLOSEST_ULL(tmp_64, 1000000000);
408+
}
409+
410+
static int
411+
tps23881_pi_get_pw_limit_chan(struct tps23881_priv *priv, u8 chan)
412+
{
413+
struct i2c_client *client = priv->client;
414+
int ret, reg;
415+
u16 val;
416+
417+
reg = TPS23881_REG_2PAIR_POL1 + (chan % 4);
418+
ret = i2c_smbus_read_word_data(client, reg);
419+
if (ret < 0)
420+
return ret;
421+
422+
val = tps23881_calc_val(ret, chan, 0, 0xff);
423+
return val * TPS23881_MW_STEP;
424+
}
425+
426+
static int tps23881_pi_get_pw_limit(struct pse_controller_dev *pcdev, int id)
427+
{
428+
struct tps23881_priv *priv = to_tps23881_priv(pcdev);
429+
int ret, mW;
430+
u8 chan;
431+
432+
chan = priv->port[id].chan[0];
433+
ret = tps23881_pi_get_pw_limit_chan(priv, chan);
434+
if (ret < 0)
435+
return ret;
436+
437+
mW = ret;
438+
if (priv->port[id].is_4p) {
439+
chan = priv->port[id].chan[1];
440+
ret = tps23881_pi_get_pw_limit_chan(priv, chan);
441+
if (ret < 0)
442+
return ret;
443+
mW += ret;
444+
}
445+
446+
return mW;
447+
}
448+
449+
static int tps23881_pi_set_pw_limit(struct pse_controller_dev *pcdev,
450+
int id, int max_mW)
451+
{
452+
struct tps23881_priv *priv = to_tps23881_priv(pcdev);
453+
u8 pw_pol;
454+
int ret;
455+
456+
if (max_mW < TPS23881_MIN_PI_PW_LIMIT_MW || MAX_PI_PW < max_mW) {
457+
dev_err(&priv->client->dev,
458+
"power limit %d out of ranges [%d,%d]",
459+
max_mW, TPS23881_MIN_PI_PW_LIMIT_MW, MAX_PI_PW);
460+
return -ERANGE;
461+
}
462+
463+
ret = tps23881_pi_enable_manual_pol(priv, id);
464+
if (ret < 0)
465+
return ret;
466+
467+
pw_pol = DIV_ROUND_CLOSEST_ULL(max_mW, TPS23881_MW_STEP);
468+
469+
/* Save power policy to reconfigure it after a disabled call */
470+
priv->port[id].pw_pol = pw_pol;
471+
return tps23881_pi_set_pw_pol_limit(priv, id, pw_pol,
472+
priv->port[id].is_4p);
473+
}
474+
475+
static int
476+
tps23881_pi_get_pw_limit_ranges(struct pse_controller_dev *pcdev, int id,
477+
struct pse_pw_limit_ranges *pw_limit_ranges)
478+
{
479+
struct ethtool_c33_pse_pw_limit_range *c33_pw_limit_ranges;
480+
481+
c33_pw_limit_ranges = kzalloc(sizeof(*c33_pw_limit_ranges),
482+
GFP_KERNEL);
483+
if (!c33_pw_limit_ranges)
484+
return -ENOMEM;
485+
486+
c33_pw_limit_ranges->min = TPS23881_MIN_PI_PW_LIMIT_MW;
487+
c33_pw_limit_ranges->max = MAX_PI_PW;
488+
pw_limit_ranges->c33_pw_limit_ranges = c33_pw_limit_ranges;
489+
490+
/* Return the number of ranges */
491+
return 1;
492+
}
493+
249494
/* Parse managers subnode into a array of device node */
250495
static int
251496
tps23881_get_of_channels(struct tps23881_priv *priv,
@@ -540,6 +785,9 @@ tps23881_write_port_matrix(struct tps23881_priv *priv,
540785
if (port_matrix[i].exist)
541786
priv->port[pi_id].chan[0] = lgcl_chan;
542787

788+
/* Initialize power policy internal value */
789+
priv->port[pi_id].pw_pol = -1;
790+
543791
/* Set hardware port matrix for all ports */
544792
val |= hw_chan << (lgcl_chan * 2);
545793

@@ -665,6 +913,12 @@ static const struct pse_controller_ops tps23881_ops = {
665913
.pi_disable = tps23881_pi_disable,
666914
.pi_get_admin_state = tps23881_pi_get_admin_state,
667915
.pi_get_pw_status = tps23881_pi_get_pw_status,
916+
.pi_get_pw_class = tps23881_pi_get_pw_class,
917+
.pi_get_actual_pw = tps23881_pi_get_actual_pw,
918+
.pi_get_voltage = tps23881_pi_get_voltage,
919+
.pi_get_pw_limit = tps23881_pi_get_pw_limit,
920+
.pi_set_pw_limit = tps23881_pi_set_pw_limit,
921+
.pi_get_pw_limit_ranges = tps23881_pi_get_pw_limit_ranges,
668922
};
669923

670924
static const char fw_parity_name[] = "ti/tps23881/tps23881-parity-14.bin";

0 commit comments

Comments
 (0)