| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Driver for the TI TPS23881 PoE PSE Controller driver (I2C bus)
- *
- * Copyright (c) 2023 Bootlin, Kory Maincent <kory.maincent@bootlin.com>
- */
- #include <linux/bitfield.h>
- #include <linux/delay.h>
- #include <linux/firmware.h>
- #include <linux/gpio/consumer.h>
- #include <linux/i2c.h>
- #include <linux/module.h>
- #include <linux/of.h>
- #include <linux/platform_device.h>
- #include <linux/pse-pd/pse.h>
- #define TPS23881_MAX_CHANS 8
- #define TPS23881_REG_PW_STATUS 0x10
- #define TPS23881_REG_OP_MODE 0x12
- #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_NBITACC BIT(5)
- #define TPS23881_REG_PW_EN 0x19
- #define TPS23881_REG_PORT_MAP 0x26
- #define TPS23881_REG_PORT_POWER 0x29
- #define TPS23881_REG_POEPLUS 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_SRAM_CTRL 0x60
- #define TPS23881_REG_SRAM_DATA 0x61
- struct tps23881_port_desc {
- u8 chan[2];
- bool is_4p;
- };
- struct tps23881_priv {
- struct i2c_client *client;
- struct pse_controller_dev pcdev;
- struct device_node *np;
- struct tps23881_port_desc port[TPS23881_MAX_CHANS];
- };
- static struct tps23881_priv *to_tps23881_priv(struct pse_controller_dev *pcdev)
- {
- return container_of(pcdev, struct tps23881_priv, pcdev);
- }
- static int tps23881_pi_enable(struct pse_controller_dev *pcdev, int id)
- {
- struct tps23881_priv *priv = to_tps23881_priv(pcdev);
- struct i2c_client *client = priv->client;
- u8 chan;
- u16 val;
- int ret;
- if (id >= TPS23881_MAX_CHANS)
- return -ERANGE;
- chan = priv->port[id].chan[0];
- if (chan < 4)
- val = BIT(chan);
- else
- val = BIT(chan + 4);
- if (priv->port[id].is_4p) {
- chan = priv->port[id].chan[1];
- if (chan < 4)
- val |= BIT(chan);
- else
- val |= BIT(chan + 4);
- }
- ret = i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val);
- if (ret)
- return ret;
- return 0;
- }
- static int tps23881_pi_disable(struct pse_controller_dev *pcdev, int id)
- {
- struct tps23881_priv *priv = to_tps23881_priv(pcdev);
- struct i2c_client *client = priv->client;
- u8 chan;
- u16 val;
- int ret;
- if (id >= TPS23881_MAX_CHANS)
- return -ERANGE;
- chan = priv->port[id].chan[0];
- if (chan < 4)
- val = BIT(chan + 4);
- else
- val = BIT(chan + 8);
- if (priv->port[id].is_4p) {
- chan = priv->port[id].chan[1];
- if (chan < 4)
- val |= BIT(chan + 4);
- else
- val |= BIT(chan + 8);
- }
- ret = i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val);
- if (ret)
- return ret;
- return 0;
- }
- static int tps23881_pi_is_enabled(struct pse_controller_dev *pcdev, int id)
- {
- struct tps23881_priv *priv = to_tps23881_priv(pcdev);
- struct i2c_client *client = priv->client;
- bool enabled;
- u8 chan;
- int ret;
- ret = i2c_smbus_read_word_data(client, TPS23881_REG_PW_STATUS);
- if (ret < 0)
- return ret;
- chan = priv->port[id].chan[0];
- if (chan < 4)
- enabled = ret & BIT(chan);
- else
- enabled = ret & BIT(chan + 4);
- if (priv->port[id].is_4p) {
- chan = priv->port[id].chan[1];
- if (chan < 4)
- enabled &= !!(ret & BIT(chan));
- else
- enabled &= !!(ret & BIT(chan + 4));
- }
- /* Return enabled status only if both channel are on this state */
- return enabled;
- }
- static int tps23881_ethtool_get_status(struct pse_controller_dev *pcdev,
- unsigned long id,
- struct netlink_ext_ack *extack,
- struct pse_control_status *status)
- {
- struct tps23881_priv *priv = to_tps23881_priv(pcdev);
- struct i2c_client *client = priv->client;
- bool enabled, delivering;
- u8 chan;
- int ret;
- ret = i2c_smbus_read_word_data(client, TPS23881_REG_PW_STATUS);
- if (ret < 0)
- return ret;
- chan = priv->port[id].chan[0];
- if (chan < 4) {
- enabled = ret & BIT(chan);
- delivering = ret & BIT(chan + 4);
- } else {
- enabled = ret & BIT(chan + 4);
- delivering = ret & BIT(chan + 8);
- }
- if (priv->port[id].is_4p) {
- chan = priv->port[id].chan[1];
- if (chan < 4) {
- enabled &= !!(ret & BIT(chan));
- delivering &= !!(ret & BIT(chan + 4));
- } else {
- enabled &= !!(ret & BIT(chan + 4));
- delivering &= !!(ret & BIT(chan + 8));
- }
- }
- /* Return delivering status only if both channel are on this state */
- if (delivering)
- status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING;
- else
- status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED;
- /* Return enabled status only if both channel are on this state */
- if (enabled)
- status->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED;
- else
- status->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED;
- return 0;
- }
- /* Parse managers subnode into a array of device node */
- static int
- tps23881_get_of_channels(struct tps23881_priv *priv,
- struct device_node *chan_node[TPS23881_MAX_CHANS])
- {
- struct device_node *channels_node, *node;
- int i, ret;
- if (!priv->np)
- return -EINVAL;
- channels_node = of_find_node_by_name(priv->np, "channels");
- if (!channels_node)
- return -EINVAL;
- for_each_child_of_node(channels_node, node) {
- u32 chan_id;
- if (!of_node_name_eq(node, "channel"))
- continue;
- 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;
- }
- of_node_put(node);
- of_node_put(channels_node);
- return ret;
- }
- struct tps23881_port_matrix {
- u8 pi_id;
- u8 lgcl_chan[2];
- u8 hw_chan[2];
- bool is_4p;
- bool exist;
- };
- static int
- tps23881_match_channel(const struct 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;
- }
- static bool
- 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))
- return false;
- }
- return true;
- }
- /* Fill port matrix with the matching channels */
- static int
- 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;
- if (!tps23881_is_chan_free(port_matrix, ret)) {
- pr_err("tps23881: channel %d already used\n", ret);
- return -ENODEV;
- }
- port_matrix[pi_id].hw_chan[0] = ret;
- port_matrix[pi_id].exist = true;
- if (!pi->pairset[1].np)
- return 0;
- ret = tps23881_match_channel(&pi->pairset[1], chan_node);
- if (ret < 0)
- return ret;
- if (!tps23881_is_chan_free(port_matrix, ret)) {
- pr_err("tps23881: channel %d already used\n", ret);
- return -ENODEV;
- }
- if (port_matrix[pi_id].hw_chan[0] / 4 != ret / 4) {
- pr_err("tps23881: 4-pair PSE can only be set within the same 4 ports group");
- return -ENODEV;
- }
- port_matrix[pi_id].hw_chan[1] = ret;
- port_matrix[pi_id].is_4p = true;
- return 0;
- }
- static int
- 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.
- */
- static int
- 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;
- if (port_matrix[i].hw_chan[0] < 4)
- cnt = &cnt_4ch_grp1;
- else
- cnt = &cnt_4ch_grp2;
- tmp_port_matrix[port_cnt].exist = true;
- tmp_port_matrix[port_cnt].is_4p = true;
- tmp_port_matrix[port_cnt].pi_id = i;
- tmp_port_matrix[port_cnt].hw_chan[0] = port_matrix[i].hw_chan[0];
- tmp_port_matrix[port_cnt].hw_chan[1] = port_matrix[i].hw_chan[1];
- /* 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;
- if (port_matrix[i].hw_chan[0] < 4)
- cnt = &cnt_4ch_grp1;
- else
- cnt = &cnt_4ch_grp2;
- tmp_port_matrix[port_cnt].exist = true;
- tmp_port_matrix[port_cnt].pi_id = i;
- tmp_port_matrix[port_cnt].lgcl_chan[0] = (*cnt)++;
- tmp_port_matrix[port_cnt].hw_chan[0] = port_matrix[i].hw_chan[0];
- port_cnt++;
- }
- /* 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++;
- }
- memcpy(port_matrix, tmp_port_matrix, sizeof(tmp_port_matrix));
- return port_cnt;
- }
- /* Write port matrix to the hardware port matrix and the software port
- * matrix.
- */
- static int
- 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, ret;
- 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;
- /* Set hardware port matrix for all ports */
- val |= hw_chan << (lgcl_chan * 2);
- if (!port_matrix[i].is_4p)
- continue;
- lgcl_chan = port_matrix[i].lgcl_chan[1];
- hw_chan = port_matrix[i].hw_chan[1] % 4;
- /* Set software port matrix for existing ports */
- if (port_matrix[i].exist) {
- priv->port[pi_id].is_4p = true;
- priv->port[pi_id].chan[1] = lgcl_chan;
- }
- /* Set hardware port matrix for all ports */
- val |= hw_chan << (lgcl_chan * 2);
- }
- /* Write hardware ports matrix */
- ret = i2c_smbus_write_word_data(client, TPS23881_REG_PORT_MAP, val);
- if (ret)
- return ret;
- return 0;
- }
- static int
- 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);
- }
- ret = i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val);
- if (ret)
- return ret;
- return 0;
- }
- static int
- 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_set_ports_conf(priv, port_matrix);
- if (ret)
- return ret;
- return 0;
- }
- static int tps23881_setup_pi_matrix(struct pse_controller_dev *pcdev)
- {
- struct device_node *chan_node[TPS23881_MAX_CHANS] = {NULL};
- struct tps23881_priv *priv = to_tps23881_priv(pcdev);
- int ret, i;
- 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]);
- return ret;
- }
- static const struct pse_controller_ops tps23881_ops = {
- .setup_pi_matrix = tps23881_setup_pi_matrix,
- .pi_enable = tps23881_pi_enable,
- .pi_disable = tps23881_pi_disable,
- .pi_is_enabled = tps23881_pi_is_enabled,
- .ethtool_get_status = tps23881_ethtool_get_status,
- };
- static const char fw_parity_name[] = "ti/tps23881/tps23881-parity-14.bin";
- static const char fw_sram_name[] = "ti/tps23881/tps23881-sram-14.bin";
- struct tps23881_fw_conf {
- u8 reg;
- u8 val;
- };
- static const struct tps23881_fw_conf tps23881_fw_parity_conf[] = {
- {.reg = 0x60, .val = 0x01},
- {.reg = 0x62, .val = 0x00},
- {.reg = 0x63, .val = 0x80},
- {.reg = 0x60, .val = 0xC4},
- {.reg = 0x1D, .val = 0xBC},
- {.reg = 0xD7, .val = 0x02},
- {.reg = 0x91, .val = 0x00},
- {.reg = 0x90, .val = 0x00},
- {.reg = 0xD7, .val = 0x00},
- {.reg = 0x1D, .val = 0x00},
- { /* sentinel */ }
- };
- static const struct tps23881_fw_conf tps23881_fw_sram_conf[] = {
- {.reg = 0x60, .val = 0xC5},
- {.reg = 0x62, .val = 0x00},
- {.reg = 0x63, .val = 0x80},
- {.reg = 0x60, .val = 0xC0},
- {.reg = 0x1D, .val = 0xBC},
- {.reg = 0xD7, .val = 0x02},
- {.reg = 0x91, .val = 0x00},
- {.reg = 0x90, .val = 0x00},
- {.reg = 0xD7, .val = 0x00},
- {.reg = 0x1D, .val = 0x00},
- { /* sentinel */ }
- };
- static int tps23881_flash_sram_fw_part(struct i2c_client *client,
- const char *fw_name,
- const struct tps23881_fw_conf *fw_conf)
- {
- const struct 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;
- }
- static int 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;
- }
- static int tps23881_i2c_probe(struct i2c_client *client)
- {
- struct device *dev = &client->dev;
- struct tps23881_priv *priv;
- struct gpio_desc *reset;
- int ret;
- u8 val;
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
- dev_err(dev, "i2c check functionality failed\n");
- return -ENXIO;
- }
- 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");
- if (reset) {
- /* TPS23880 datasheet (Rev G) indicates minimum reset pulse is 5us */
- usleep_range(5, 10);
- gpiod_set_value_cansleep(reset, 0); /* De-assert reset */
- /* 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;
- if (FIELD_GET(TPS23881_REG_DEVID_MASK, ret) != TPS23881_DEVICE_ID) {
- dev_err(dev, "Wrong device ID\n");
- return -ENXIO;
- }
- ret = tps23881_flash_sram_fw(client);
- if (ret < 0)
- return ret;
- ret = i2c_smbus_read_byte_data(client, TPS23881_REG_FWREV);
- if (ret < 0)
- return ret;
- dev_info(&client->dev, "Firmware revision 0x%x\n", 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;
- priv->client = client;
- i2c_set_clientdata(client, priv);
- priv->np = dev->of_node;
- priv->pcdev.owner = THIS_MODULE;
- priv->pcdev.ops = &tps23881_ops;
- priv->pcdev.dev = dev;
- priv->pcdev.types = ETHTOOL_PSE_C33;
- priv->pcdev.nr_lines = TPS23881_MAX_CHANS;
- ret = devm_pse_controller_register(dev, &priv->pcdev);
- if (ret) {
- return dev_err_probe(dev, ret,
- "failed to register PSE controller\n");
- }
- return ret;
- }
- static const struct i2c_device_id tps23881_id[] = {
- { "tps23881" },
- { }
- };
- MODULE_DEVICE_TABLE(i2c, tps23881_id);
- static const struct of_device_id tps23881_of_match[] = {
- { .compatible = "ti,tps23881", },
- { },
- };
- MODULE_DEVICE_TABLE(of, tps23881_of_match);
- static struct i2c_driver tps23881_driver = {
- .probe = tps23881_i2c_probe,
- .id_table = tps23881_id,
- .driver = {
- .name = "tps23881",
- .of_match_table = tps23881_of_match,
- },
- };
- module_i2c_driver(tps23881_driver);
- MODULE_AUTHOR("Kory Maincent <kory.maincent@bootlin.com>");
- MODULE_DESCRIPTION("TI TPS23881 PoE PSE Controller driver");
- MODULE_LICENSE("GPL");
|