diff options
-rw-r--r-- | drivers/net/pse-pd/tps23881.c | 244 |
1 files changed, 228 insertions, 16 deletions
diff --git a/drivers/net/pse-pd/tps23881.c b/drivers/net/pse-pd/tps23881.c index 7a9a5dbe0cb1..63f8f43062bc 100644 --- a/drivers/net/pse-pd/tps23881.c +++ b/drivers/net/pse-pd/tps23881.c @@ -20,20 +20,30 @@ #define TPS23881_REG_IT 0x0 #define TPS23881_REG_IT_MASK 0x1 +#define TPS23881_REG_IT_DISF BIT(2) +#define TPS23881_REG_IT_DETC BIT(3) +#define TPS23881_REG_IT_CLASC BIT(4) #define TPS23881_REG_IT_IFAULT BIT(5) #define TPS23881_REG_IT_SUPF BIT(7) +#define TPS23881_REG_DET_EVENT 0x5 #define TPS23881_REG_FAULT 0x7 #define TPS23881_REG_SUPF_EVENT 0xb #define TPS23881_REG_TSD BIT(7) +#define TPS23881_REG_DISC 0xc #define TPS23881_REG_PW_STATUS 0x10 #define TPS23881_REG_OP_MODE 0x12 +#define TPS23881_REG_DISC_EN 0x13 #define TPS23881_OP_MODE_SEMIAUTO 0xaaaa #define TPS23881_REG_DIS_EN 0x13 #define TPS23881_REG_DET_CLA_EN 0x14 #define TPS23881_REG_GEN_MASK 0x17 +#define TPS23881_REG_CLCHE BIT(2) +#define TPS23881_REG_DECHE BIT(3) #define TPS23881_REG_NBITACC BIT(5) #define TPS23881_REG_INTEN BIT(7) #define TPS23881_REG_PW_EN 0x19 +#define TPS23881_REG_RESET 0x1a +#define TPS23881_REG_CLRAIN BIT(7) #define TPS23881_REG_2PAIR_POL1 0x1e #define TPS23881_REG_PORT_MAP 0x26 #define TPS23881_REG_PORT_POWER 0x29 @@ -178,6 +188,7 @@ static int tps23881_pi_enable(struct pse_controller_dev *pcdev, int id) struct i2c_client *client = priv->client; u8 chan; u16 val; + int ret; if (id >= TPS23881_MAX_CHANS) return -ERANGE; @@ -191,7 +202,22 @@ static int tps23881_pi_enable(struct pse_controller_dev *pcdev, int id) BIT(chan % 4)); } - return i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val); + ret = i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val); + if (ret) + return ret; + + /* Enable DC disconnect*/ + chan = priv->port[id].chan[0]; + ret = i2c_smbus_read_word_data(client, TPS23881_REG_DISC_EN); + if (ret < 0) + return ret; + + val = tps23881_set_val(ret, chan, 0, BIT(chan % 4), BIT(chan % 4)); + ret = i2c_smbus_write_word_data(client, TPS23881_REG_DISC_EN, val); + if (ret) + return ret; + + return 0; } static int tps23881_pi_disable(struct pse_controller_dev *pcdev, int id) @@ -224,6 +250,17 @@ static int tps23881_pi_disable(struct pse_controller_dev *pcdev, int id) */ mdelay(5); + /* Disable DC disconnect*/ + chan = priv->port[id].chan[0]; + ret = i2c_smbus_read_word_data(client, TPS23881_REG_DISC_EN); + if (ret < 0) + return ret; + + val = tps23881_set_val(ret, chan, 0, 0, BIT(chan % 4)); + ret = i2c_smbus_write_word_data(client, TPS23881_REG_DISC_EN, val); + if (ret) + return ret; + /* Enable detection and classification */ ret = i2c_smbus_read_word_data(client, TPS23881_REG_DET_CLA_EN); if (ret < 0) @@ -919,6 +956,47 @@ static int tps23881_setup_pi_matrix(struct pse_controller_dev *pcdev) return ret; } +static int tps23881_power_class_table[] = { + -ERANGE, + 4000, + 7000, + 15500, + 30000, + 15500, + 15500, + -ERANGE, + 45000, + 60000, + 75000, + 90000, + 15500, + 45000, + -ERANGE, + -ERANGE, +}; + +static int tps23881_pi_get_pw_req(struct pse_controller_dev *pcdev, int id) +{ + struct tps23881_priv *priv = to_tps23881_priv(pcdev); + struct i2c_client *client = priv->client; + u8 reg, chan; + int ret; + u16 val; + + /* For a 4-pair the classification need 5ms to be completed */ + if (priv->port[id].is_4p) + mdelay(5); + + chan = priv->port[id].chan[0]; + reg = TPS23881_REG_DISC + (chan % 4); + ret = i2c_smbus_read_word_data(client, reg); + if (ret < 0) + return ret; + + val = tps23881_calc_val(ret, chan, 4, 0xf); + return tps23881_power_class_table[val]; +} + static const struct pse_controller_ops tps23881_ops = { .setup_pi_matrix = tps23881_setup_pi_matrix, .pi_enable = tps23881_pi_enable, @@ -931,6 +1009,7 @@ static const struct pse_controller_ops tps23881_ops = { .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, + .pi_get_pw_req = tps23881_pi_get_pw_req, }; static const char fw_parity_name[] = "ti/tps23881/tps23881-parity-14.bin"; @@ -1088,17 +1167,113 @@ static void tps23881_irq_event_over_temp(struct tps23881_priv *priv, } } -static void tps23881_irq_event_over_current(struct tps23881_priv *priv, - u16 reg_val, - unsigned long *notifs, - unsigned long *notifs_mask) +static int tps23881_irq_event_over_current(struct tps23881_priv *priv, + u16 reg_val, + unsigned long *notifs, + unsigned long *notifs_mask) { + int i, ret; u8 chans; chans = tps23881_irq_export_chans_helper(reg_val, 0); + if (!chans) + return 0; + + tps23881_set_notifs_helper(priv, chans, notifs, notifs_mask, + ETHTOOL_PSE_EVENT_OVER_CURRENT | + ETHTOOL_C33_PSE_EVENT_DISCONNECTION); + + /* Over Current event resets the power limit registers so we need + * to configured it again. + */ + for_each_set_bit(i, notifs_mask, priv->pcdev.nr_lines) { + if (priv->port[i].pw_pol < 0) + continue; + + ret = tps23881_pi_enable_manual_pol(priv, i); + if (ret < 0) + return ret; + + /* Set power policy */ + ret = tps23881_pi_set_pw_pol_limit(priv, i, + priv->port[i].pw_pol, + priv->port[i].is_4p); + if (ret < 0) + return ret; + } + + return 0; +} + +static void tps23881_irq_event_disconnection(struct tps23881_priv *priv, + u16 reg_val, + unsigned long *notifs, + unsigned long *notifs_mask) +{ + u8 chans; + + chans = tps23881_irq_export_chans_helper(reg_val, 4); if (chans) tps23881_set_notifs_helper(priv, chans, notifs, notifs_mask, - ETHTOOL_PSE_EVENT_OVER_CURRENT); + ETHTOOL_C33_PSE_EVENT_DISCONNECTION); +} + +static int tps23881_irq_event_detection(struct tps23881_priv *priv, + u16 reg_val, + unsigned long *notifs, + unsigned long *notifs_mask) +{ + enum ethtool_pse_event event; + int reg, ret, i, val; + unsigned long chans; + + chans = tps23881_irq_export_chans_helper(reg_val, 0); + for_each_set_bit(i, &chans, TPS23881_MAX_CHANS) { + reg = TPS23881_REG_DISC + (i % 4); + ret = i2c_smbus_read_word_data(priv->client, reg); + if (ret < 0) + return ret; + + val = tps23881_calc_val(ret, i, 0, 0xf); + /* If detection valid */ + if (val == 0x4) + event = ETHTOOL_C33_PSE_EVENT_DETECTION; + else + event = ETHTOOL_C33_PSE_EVENT_DISCONNECTION; + + tps23881_set_notifs_helper(priv, BIT(i), notifs, + notifs_mask, event); + } + + return 0; +} + +static int tps23881_irq_event_classification(struct tps23881_priv *priv, + u16 reg_val, + unsigned long *notifs, + unsigned long *notifs_mask) +{ + int reg, ret, val, i; + unsigned long chans; + + chans = tps23881_irq_export_chans_helper(reg_val, 4); + for_each_set_bit(i, &chans, TPS23881_MAX_CHANS) { + reg = TPS23881_REG_DISC + (i % 4); + ret = i2c_smbus_read_word_data(priv->client, reg); + if (ret < 0) + return ret; + + val = tps23881_calc_val(ret, i, 4, 0xf); + /* Do not report classification event for unknown class */ + if (!val || val == 0x8 || val == 0xf) + continue; + + tps23881_set_notifs_helper(priv, BIT(i), notifs, + notifs_mask, + ETHTOOL_C33_PSE_EVENT_CLASSIFICATION); + } + + return 0; } static int tps23881_irq_event_handler(struct tps23881_priv *priv, u16 reg, @@ -1106,7 +1281,7 @@ static int tps23881_irq_event_handler(struct tps23881_priv *priv, u16 reg, unsigned long *notifs_mask) { struct i2c_client *client = priv->client; - int ret; + int ret, val; /* The Supply event bit is repeated twice so we only need to read * the one from the first byte. @@ -1118,13 +1293,36 @@ static int tps23881_irq_event_handler(struct tps23881_priv *priv, u16 reg, tps23881_irq_event_over_temp(priv, ret, notifs, notifs_mask); } - if (reg & (TPS23881_REG_IT_IFAULT | TPS23881_REG_IT_IFAULT << 8)) { + if (reg & (TPS23881_REG_IT_IFAULT | TPS23881_REG_IT_IFAULT << 8 | + TPS23881_REG_IT_DISF | TPS23881_REG_IT_DISF << 8)) { ret = i2c_smbus_read_word_data(client, TPS23881_REG_FAULT); if (ret < 0) return ret; - tps23881_irq_event_over_current(priv, ret, notifs, notifs_mask); + ret = tps23881_irq_event_over_current(priv, ret, notifs, + notifs_mask); + if (ret) + return ret; + + tps23881_irq_event_disconnection(priv, ret, notifs, notifs_mask); } + if (reg & (TPS23881_REG_IT_DETC | TPS23881_REG_IT_DETC << 8 | + TPS23881_REG_IT_CLASC | TPS23881_REG_IT_CLASC << 8)) { + ret = i2c_smbus_read_word_data(client, TPS23881_REG_DET_EVENT); + if (ret < 0) + return ret; + + val = ret; + ret = tps23881_irq_event_detection(priv, val, notifs, + notifs_mask); + if (ret) + return ret; + + ret = tps23881_irq_event_classification(priv, val, notifs, + notifs_mask); + if (ret) + return ret; + } return 0; } @@ -1178,7 +1376,14 @@ static int tps23881_setup_irq(struct tps23881_priv *priv, int irq) int ret; u16 val; - val = TPS23881_REG_IT_IFAULT | TPS23881_REG_IT_SUPF; + if (!irq) { + dev_err(&client->dev, "interrupt is missing"); + return -EINVAL; + } + + val = TPS23881_REG_IT_IFAULT | TPS23881_REG_IT_SUPF | + TPS23881_REG_IT_DETC | TPS23881_REG_IT_CLASC | + TPS23881_REG_IT_DISF; val |= val << 8; ret = i2c_smbus_write_word_data(client, TPS23881_REG_IT_MASK, val); if (ret) @@ -1188,11 +1393,19 @@ static int tps23881_setup_irq(struct tps23881_priv *priv, int irq) if (ret < 0) return ret; - val = (u16)(ret | TPS23881_REG_INTEN | TPS23881_REG_INTEN << 8); + val = TPS23881_REG_INTEN | TPS23881_REG_CLCHE | TPS23881_REG_DECHE; + val |= val << 8; + val |= (u16)ret; ret = i2c_smbus_write_word_data(client, TPS23881_REG_GEN_MASK, val); if (ret < 0) return ret; + /* Reset interrupts registers */ + ret = i2c_smbus_write_word_data(client, TPS23881_REG_RESET, + TPS23881_REG_CLRAIN); + if (ret < 0) + return ret; + return devm_pse_irq_helper(&priv->pcdev, irq, 0, &irq_desc); } @@ -1270,17 +1483,16 @@ static int tps23881_i2c_probe(struct i2c_client *client) priv->pcdev.dev = dev; priv->pcdev.types = ETHTOOL_PSE_C33; priv->pcdev.nr_lines = TPS23881_MAX_CHANS; + priv->pcdev.supp_budget_eval_strategies = PSE_BUDGET_EVAL_STRAT_STATIC; ret = devm_pse_controller_register(dev, &priv->pcdev); if (ret) { return dev_err_probe(dev, ret, "failed to register PSE controller\n"); } - if (client->irq) { - ret = tps23881_setup_irq(priv, client->irq); - if (ret) - return ret; - } + ret = tps23881_setup_irq(priv, client->irq); + if (ret) + return ret; return ret; } |