/* * Helper to extract a value from a u16 register value, which is made of two * u8 registers. The function calculates the bit offset based on the channel * and extracts the relevant bits using a provided field mask. * * @param reg_val: The u16 register value (composed of two u8 registers). * @param chan: The channel number (0-7). * @param field_offset: The base bit offset to apply (e.g., 0 or 4). * @param field_mask: The mask to apply to extract the required bits. * @return: The extracted value for the specific channel.
*/ static u16 tps23881_calc_val(u16 reg_val, u8 chan, u8 field_offset,
u16 field_mask)
{ if (chan >= 4)
reg_val >>= 8;
return (reg_val >> field_offset) & field_mask;
}
/* * Helper to combine individual channel values into a u16 register value. * The function sets the value for a specific channel in the appropriate * position. * * @param reg_val: The current u16 register value. * @param chan: The channel number (0-7). * @param field_offset: The base bit offset to apply (e.g., 0 or 4). * @param field_mask: The mask to apply for the field (e.g., 0x0F). * @param field_val: The value to set for the specific channel (masked by * field_mask). * @return: The updated u16 register value with the channel value set.
*/ static u16 tps23881_set_val(u16 reg_val, u8 chan, u8 field_offset,
u16 field_mask, u16 field_val)
{
field_val &= field_mask;
staticint 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);
}
if (priv->port[id].is_4p) {
chan = priv->port[id].chan[1];
val = tps23881_set_val(val, chan, 4, BIT(chan % 4),
BIT(chan % 4));
}
ret = i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val); if (ret) return ret;
/* PWOFF command resets lots of register which need to be * configured again. According to the datasheet "It may take upwards * of 5ms after PWOFFn command for all register values to be updated"
*/
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) return ret;
if (priv->port[id].is_4p) {
chan = priv->port[id].chan[1];
val = tps23881_calc_val(ret, chan, 0, BIT(chan % 4));
enabled &= !!(val);
}
/* Return enabled status only if both channel are on this state */ if (enabled)
admin_state->c33_admin_state =
ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED; else
admin_state->c33_admin_state =
ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED;
if (priv->port[id].is_4p) {
chan = priv->port[id].chan[1];
val = tps23881_calc_val(ret, chan, 4, BIT(chan % 4));
delivering &= !!(val);
}
/* Return delivering status only if both channel are on this state */ if (delivering)
pw_status->c33_pw_status =
ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING; else
pw_status->c33_pw_status =
ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED;
/* 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);
}
ret = of_property_read_u32(node, "reg", &chan_id); if (ret) {
ret = -EINVAL; goto out;
}
if (chan_id >= TPS23881_MAX_CHANS || chan_node[chan_id]) {
dev_err(&priv->client->dev, "wrong number of port (%d)\n", chan_id);
ret = -EINVAL; goto out;
}
of_node_get(node);
chan_node[chan_id] = node;
}
of_node_put(channels_node); return 0;
out: for (i = 0; i < TPS23881_MAX_CHANS; i++) {
of_node_put(chan_node[i]);
chan_node[i] = NULL;
}
staticint
tps23881_match_channel(conststruct pse_pi_pairset *pairset, struct device_node *chan_node[TPS23881_MAX_CHANS])
{ int i;
/* Look on every channels */ for (i = 0; i < TPS23881_MAX_CHANS; i++) { if (pairset->np == chan_node[i]) return i;
}
return -ENODEV;
}
staticbool
tps23881_is_chan_free(struct tps23881_port_matrix port_matrix[TPS23881_MAX_CHANS], int chan)
{ int i;
for (i = 0; i < TPS23881_MAX_CHANS; i++) { if (port_matrix[i].exist &&
(port_matrix[i].hw_chan[0] == chan ||
port_matrix[i].hw_chan[1] == chan)) returnfalse;
}
returntrue;
}
/* Fill port matrix with the matching channels */ staticint
tps23881_match_port_matrix(struct pse_pi *pi, int pi_id, struct device_node *chan_node[TPS23881_MAX_CHANS], struct tps23881_port_matrix port_matrix[TPS23881_MAX_CHANS])
{ int ret;
if (!pi->pairset[0].np) return 0;
ret = tps23881_match_channel(&pi->pairset[0], chan_node); if (ret < 0) return ret;
staticint
tps23881_get_unused_chan(struct tps23881_port_matrix port_matrix[TPS23881_MAX_CHANS], int port_cnt)
{ bool used; int i, j;
for (i = 0; i < TPS23881_MAX_CHANS; i++) {
used = false;
for (j = 0; j < port_cnt; j++) { if (port_matrix[j].hw_chan[0] == i) {
used = true; break;
}
if (port_matrix[j].is_4p &&
port_matrix[j].hw_chan[1] == i) {
used = true; break;
}
}
if (!used) return i;
}
return -ENODEV;
}
/* Sort the port matrix to following particular hardware ports matrix * specification of the tps23881. The device has two 4-ports groups and * each 4-pair powered device has to be configured to use two consecutive * logical channel in each 4 ports group (1 and 2 or 3 and 4). Also the * hardware matrix has to be fully configured even with unused chan to be * valid.
*/ staticint
tps23881_sort_port_matrix(struct tps23881_port_matrix port_matrix[TPS23881_MAX_CHANS])
{ struct tps23881_port_matrix tmp_port_matrix[TPS23881_MAX_CHANS] = {0}; int i, ret, port_cnt = 0, cnt_4ch_grp1 = 0, cnt_4ch_grp2 = 4;
/* Configure 4p port matrix */ for (i = 0; i < TPS23881_MAX_CHANS; i++) { int *cnt;
if (!port_matrix[i].exist || !port_matrix[i].is_4p) continue;
/* 4-pair ports have to be configured with consecutive * logical channels 0 and 1, 2 and 3.
*/
tmp_port_matrix[port_cnt].lgcl_chan[0] = (*cnt)++;
tmp_port_matrix[port_cnt].lgcl_chan[1] = (*cnt)++;
port_cnt++;
}
/* Configure 2p port matrix */ for (i = 0; i < TPS23881_MAX_CHANS; i++) { int *cnt;
if (!port_matrix[i].exist || port_matrix[i].is_4p) continue;
/* Complete the rest of the first 4 port group matrix even if * channels are unused
*/ while (cnt_4ch_grp1 < 4) {
ret = tps23881_get_unused_chan(tmp_port_matrix, port_cnt); if (ret < 0) {
pr_err("tps23881: port matrix issue, no chan available\n"); return ret;
}
if (port_cnt >= TPS23881_MAX_CHANS) {
pr_err("tps23881: wrong number of channels\n"); return -ENODEV;
}
tmp_port_matrix[port_cnt].lgcl_chan[0] = cnt_4ch_grp1;
tmp_port_matrix[port_cnt].hw_chan[0] = ret;
cnt_4ch_grp1++;
port_cnt++;
}
/* Complete the rest of the second 4 port group matrix even if * channels are unused
*/ while (cnt_4ch_grp2 < 8) {
ret = tps23881_get_unused_chan(tmp_port_matrix, port_cnt); if (ret < 0) {
pr_err("tps23881: port matrix issue, no chan available\n"); return -ENODEV;
}
if (port_cnt >= TPS23881_MAX_CHANS) {
pr_err("tps23881: wrong number of channels\n"); return -ENODEV;
}
tmp_port_matrix[port_cnt].lgcl_chan[0] = cnt_4ch_grp2;
tmp_port_matrix[port_cnt].hw_chan[0] = ret;
cnt_4ch_grp2++;
port_cnt++;
}
/* Write port matrix to the hardware port matrix and the software port * matrix.
*/ staticint
tps23881_write_port_matrix(struct tps23881_priv *priv, struct tps23881_port_matrix port_matrix[TPS23881_MAX_CHANS], int port_cnt)
{ struct i2c_client *client = priv->client;
u8 pi_id, lgcl_chan, hw_chan;
u16 val = 0; int i;
for (i = 0; i < port_cnt; i++) {
pi_id = port_matrix[i].pi_id;
lgcl_chan = port_matrix[i].lgcl_chan[0];
hw_chan = port_matrix[i].hw_chan[0] % 4;
/* Set software port matrix for existing ports */ if (port_matrix[i].exist) {
priv->port[pi_id].chan[0] = lgcl_chan;
priv->port[pi_id].exist = true;
}
/* 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);
staticint
tps23881_set_ports_conf(struct tps23881_priv *priv, struct tps23881_port_matrix port_matrix[TPS23881_MAX_CHANS])
{ struct i2c_client *client = priv->client; int i, ret;
u16 val;
/* Set operating mode */
ret = i2c_smbus_write_word_data(client, TPS23881_REG_OP_MODE,
TPS23881_OP_MODE_SEMIAUTO); if (ret) return ret;
/* Disable DC disconnect */
ret = i2c_smbus_write_word_data(client, TPS23881_REG_DIS_EN, 0x0); if (ret) return ret;
/* Set port power allocation */
val = 0; for (i = 0; i < TPS23881_MAX_CHANS; i++) { if (!port_matrix[i].exist) continue;
if (port_matrix[i].is_4p)
val |= 0xf << ((port_matrix[i].lgcl_chan[0] / 2) * 4); else
val |= 0x3 << ((port_matrix[i].lgcl_chan[0] / 2) * 4);
}
ret = i2c_smbus_write_word_data(client, TPS23881_REG_PORT_POWER, val); if (ret) return ret;
/* Enable detection and classification */
val = 0; for (i = 0; i < TPS23881_MAX_CHANS; i++) { if (!port_matrix[i].exist) continue;
val |= BIT(port_matrix[i].lgcl_chan[0]) |
BIT(port_matrix[i].lgcl_chan[0] + 4); if (port_matrix[i].is_4p)
val |= BIT(port_matrix[i].lgcl_chan[1]) |
BIT(port_matrix[i].lgcl_chan[1] + 4);
} return i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val);
}
staticint
tps23881_set_ports_matrix(struct tps23881_priv *priv, struct device_node *chan_node[TPS23881_MAX_CHANS])
{ struct tps23881_port_matrix port_matrix[TPS23881_MAX_CHANS] = {0}; int i, ret;
/* Update with values for every PSE PIs */ for (i = 0; i < TPS23881_MAX_CHANS; i++) {
ret = tps23881_match_port_matrix(&priv->pcdev.pi[i], i,
chan_node, port_matrix); if (ret) return ret;
}
ret = tps23881_sort_port_matrix(port_matrix); if (ret < 0) return ret;
ret = tps23881_write_port_matrix(priv, port_matrix, ret); if (ret) return ret;
ret = tps23881_get_of_channels(priv, chan_node); if (ret < 0) {
dev_warn(&priv->client->dev, "Unable to parse port-matrix, default matrix will be used\n"); return 0;
}
ret = tps23881_set_ports_matrix(priv, chan_node);
for (i = 0; i < TPS23881_MAX_CHANS; i++)
of_node_put(chan_node[i]);
staticint tps23881_flash_sram_fw_part(struct i2c_client *client, constchar *fw_name, conststruct tps23881_fw_conf *fw_conf)
{ conststruct firmware *fw = NULL; int i, ret;
ret = request_firmware(&fw, fw_name, &client->dev); if (ret) return ret;
dev_dbg(&client->dev, "Flashing %s\n", fw_name);
/* Prepare device for RAM download */ while (fw_conf->reg) {
ret = i2c_smbus_write_byte_data(client, fw_conf->reg,
fw_conf->val); if (ret) goto out;
fw_conf++;
}
/* Flash the firmware file */ for (i = 0; i < fw->size; i++) {
ret = i2c_smbus_write_byte_data(client,
TPS23881_REG_SRAM_DATA,
fw->data[i]); if (ret) goto out;
}
out:
release_firmware(fw); return ret;
}
staticint tps23881_flash_sram_fw(struct i2c_client *client)
{ int ret;
ret = tps23881_flash_sram_fw_part(client, fw_parity_name,
tps23881_fw_parity_conf); if (ret) return ret;
ret = tps23881_flash_sram_fw_part(client, fw_sram_name,
tps23881_fw_sram_conf); if (ret) return ret;
ret = i2c_smbus_write_byte_data(client, TPS23881_REG_SRAM_CTRL, 0x18); if (ret) return ret;
mdelay(12);
return 0;
}
/* Convert interrupt events to 0xff to be aligned with the chan * number.
*/ static u8 tps23881_irq_export_chans_helper(u16 reg_val, u8 field_offset)
{
u8 val;
/* Convert chan number to port number */ staticvoid tps23881_set_notifs_helper(struct tps23881_priv *priv,
u8 chans, unsignedlong *notifs, unsignedlong *notifs_mask, enum ethtool_pse_event event)
{
u8 chan; int i;
if (!chans) return;
for (i = 0; i < TPS23881_MAX_CHANS; i++) { if (!priv->port[i].exist) continue; /* No need to look at the 2nd channel in case of PoE4 as * both registers are set.
*/
chan = priv->port[i].chan[0];
/* 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;
}
/* The Supply event bit is repeated twice so we only need to read * the one from the first byte.
*/ if (reg & TPS23881_REG_IT_SUPF) {
ret = i2c_smbus_read_word_data(client, TPS23881_REG_SUPF_EVENT); if (ret < 0) return ret;
tps23881_irq_event_over_temp(priv, ret, notifs, notifs_mask);
}
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;
ret = tps23881_irq_event_over_current(priv, ret, notifs,
notifs_mask); if (ret) return ret;
/* Get interruption mask */
ret = i2c_smbus_read_word_data(client, TPS23881_REG_IT_MASK); if (ret < 0) return ret;
it_mask = ret;
/* Read interrupt register until it frees the interruption pin. */
retry = 0; while (true) { if (retry > TPS23881_MAX_IRQ_RETRIES) {
dev_err(&client->dev, "interrupt never freed"); return -ETIMEDOUT;
}
ret = i2c_smbus_read_word_data(client, TPS23881_REG_IT); if (ret < 0) return ret;
/* No more relevant interruption */ if (!(ret & it_mask)) return 0;
ret = tps23881_irq_event_handler(priv, (u16)ret, notifs,
notifs_mask); if (ret) return ret;
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) return ret;
ret = i2c_smbus_read_word_data(client, TPS23881_REG_GEN_MASK); if (ret < 0) return ret;
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;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM;
reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(reset)) return dev_err_probe(&client->dev, PTR_ERR(reset), "Failed to get reset GPIO\n");
/* TPS23880 datasheet indicates the minimum time after power on reset * should be 20ms, but the document describing how to load SRAM ("How * to Load TPS2388x SRAM and Parity Code over I2C" (Rev E)) * indicates we should delay that programming by at least 50ms. So * we'll wait the entire 50ms here to ensure we're safe to go to the * SRAM loading proceedure.
*/
msleep(50);
}
ret = i2c_smbus_read_byte_data(client, TPS23881_REG_DEVID); if (ret < 0) return ret;
/* Set configuration B, 16 bit access on a single device address */
ret = i2c_smbus_read_byte_data(client, TPS23881_REG_GEN_MASK); if (ret < 0) return ret;
val = ret | TPS23881_REG_NBITACC;
ret = i2c_smbus_write_byte_data(client, TPS23881_REG_GEN_MASK, val); if (ret) return ret;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.