|
25 | 25 | #define TPS23881_REG_GEN_MASK 0x17
|
26 | 26 | #define TPS23881_REG_NBITACC BIT(5)
|
27 | 27 | #define TPS23881_REG_PW_EN 0x19
|
| 28 | +#define TPS23881_REG_2PAIR_POL1 0x1e |
28 | 29 | #define TPS23881_REG_PORT_MAP 0x26
|
29 | 30 | #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 |
31 | 36 | #define TPS23881_REG_TPON BIT(0)
|
32 | 37 | #define TPS23881_REG_FWREV 0x41
|
33 | 38 | #define TPS23881_REG_DEVID 0x43
|
34 | 39 | #define TPS23881_REG_DEVID_MASK 0xF0
|
35 | 40 | #define TPS23881_DEVICE_ID 0x02
|
| 41 | +#define TPS23881_REG_CHAN1_CLASS 0x4c |
36 | 42 | #define TPS23881_REG_SRAM_CTRL 0x60
|
37 | 43 | #define TPS23881_REG_SRAM_DATA 0x61
|
38 | 44 |
|
| 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 | + |
39 | 50 | struct tps23881_port_desc {
|
40 | 51 | u8 chan[2];
|
41 | 52 | bool is_4p;
|
| 53 | + int pw_pol; |
42 | 54 | };
|
43 | 55 |
|
44 | 56 | struct tps23881_priv {
|
@@ -102,6 +114,54 @@ static u16 tps23881_set_val(u16 reg_val, u8 chan, u8 field_offset,
|
102 | 114 | return reg_val;
|
103 | 115 | }
|
104 | 116 |
|
| 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 | + |
105 | 165 | static int tps23881_pi_enable(struct pse_controller_dev *pcdev, int id)
|
106 | 166 | {
|
107 | 167 | struct tps23881_priv *priv = to_tps23881_priv(pcdev);
|
@@ -171,7 +231,21 @@ static int tps23881_pi_disable(struct pse_controller_dev *pcdev, int id)
|
171 | 231 | BIT(chan % 4));
|
172 | 232 | }
|
173 | 233 |
|
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); |
175 | 249 | }
|
176 | 250 |
|
177 | 251 | static int
|
@@ -246,6 +320,177 @@ tps23881_pi_get_pw_status(struct pse_controller_dev *pcdev, int id,
|
246 | 320 | return 0;
|
247 | 321 | }
|
248 | 322 |
|
| 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 | + |
249 | 494 | /* Parse managers subnode into a array of device node */
|
250 | 495 | static int
|
251 | 496 | tps23881_get_of_channels(struct tps23881_priv *priv,
|
@@ -540,6 +785,9 @@ tps23881_write_port_matrix(struct tps23881_priv *priv,
|
540 | 785 | if (port_matrix[i].exist)
|
541 | 786 | priv->port[pi_id].chan[0] = lgcl_chan;
|
542 | 787 |
|
| 788 | + /* Initialize power policy internal value */ |
| 789 | + priv->port[pi_id].pw_pol = -1; |
| 790 | + |
543 | 791 | /* Set hardware port matrix for all ports */
|
544 | 792 | val |= hw_chan << (lgcl_chan * 2);
|
545 | 793 |
|
@@ -665,6 +913,12 @@ static const struct pse_controller_ops tps23881_ops = {
|
665 | 913 | .pi_disable = tps23881_pi_disable,
|
666 | 914 | .pi_get_admin_state = tps23881_pi_get_admin_state,
|
667 | 915 | .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, |
668 | 922 | };
|
669 | 923 |
|
670 | 924 | static const char fw_parity_name[] = "ti/tps23881/tps23881-parity-14.bin";
|
|
0 commit comments