Re: [PATCH net-next 9/9] net: pse-pd: Add PD692x0 PSE controller driver

Andrew Lunn
2023-11-18T18:54:43Z

> +struct pd692x0_priv {
> + struct i2c_client *client;
> + struct pse_controller_dev pcdev;
> +
> + enum pd692x0_fw_state fw_state;
> + struct fw_upload *fwl;
> + bool cancel_request:1;
> +
> + u8 msg_id;
> + bool last_cmd_key:1;

Does a bool bit field of size 1 make any sense? I would also put the
two bitfields next to each other, and the compiler might then pack
them into the same word. The base type of a u8 would allow the compile
to put it next to the msg_id without any padding.

> + unsigned long last_cmd_key_time;
> +
> + enum ethtool_pse_admin_state admin_state[PD692X0_MAX_LOGICAL_PORTS];
> +};
> +
> +/* Template list of the fixed bytes of the communication messages */
> +static const struct pd692x0_msg pd692x0_msg_template_list[PD692X0_MSG_CNT] = {
> + [PD692X0_MSG_RESET] = {
> + .content = {
> + .key = PD692X0_KEY_CMD,
> + .sub = {0x07, 0x55, 0x00},
> + .data = {0x55, 0x00, 0x55, 0x4e,
> + 0x4e, 0x4e, 0x4e, 0x4e},
> + },
> + },

Is there any documentation about what all these magic number mean?

> +/* Implementation of the i2c communication in particular when there is
> + * a communication loss. See the "Synchronization During Communication Loss"
> + * paragraph of the Communication Protocol document.
> + */

Is this document public?

> +static int pd692x0_recv_msg(struct pd692x0_priv *priv,
> + struct pd692x0_msg *msg,
> + struct pd692x0_msg_content *buf)
> +{
> + const struct i2c_client *client = priv->client;
> + int ret;
> +
> + memset(buf, 0, sizeof(*buf));
> + if (msg->delay_recv)
> + msleep(msg->delay_recv);
> + else
> + msleep(30);
> +
> + i2c_master_recv(client, (u8 *)buf, sizeof(*buf));
> + if (buf->key)
> + goto out;

This is the first attempt to receive the message. I assume buf->key
not being 0 indicates something has been received?

> +
> + msleep(100);
> +
> + i2c_master_recv(client, (u8 *)buf, sizeof(*buf));
> + if (buf->key)
> + goto out;

So this is a second attempt. Should there be another memset? Could the
first failed transfer fill the buffer with random junk in the higher
bytes, and a successful read here could be a partial read and the end
of the buffer still contains the junk.

> +
> + ret = pd692x0_send_msg(priv, msg);
> + if (ret)
> + return ret;

So now we are re-transmitting the request.

> +
> + if (msg->delay_recv)
> + msleep(msg->delay_recv);
> + else
> + msleep(30);
> +
> + i2c_master_recv(client, (u8 *)buf, sizeof(*buf));
> + if (buf->key)
> + goto out;
> +
> + msleep(100);
> +
> + i2c_master_recv(client, (u8 *)buf, sizeof(*buf));
> + if (buf->key)
> + goto out;
> +
> + msleep(10000);

And two more attemps to receive it.

> +
> + ret = pd692x0_send_msg(priv, msg);
> + if (ret)
> + return ret;
> +
> + if (msg->delay_recv)
> + msleep(msg->delay_recv);
> + else
> + msleep(30);
> +
> + i2c_master_recv(client, (u8 *)buf, sizeof(*buf));
> + if (buf->key)
> + goto out;
> +
> + msleep(100);
> +
> + i2c_master_recv(client, (u8 *)buf, sizeof(*buf));
> + if (buf->key)
> + goto out;

Another resend and two more attempts to receive.

Is there a reason to not uses for loops here? And maybe put
send/receive/receive into a helper? And maybe make the first send part
of this, rather then separate? I think the code will be more readable
when restructured.

> +static int pd692x0_ethtool_set_config(struct pse_controller_dev *pcdev,
> + unsigned long id,
> + struct netlink_ext_ack *extack,
> + const struct pse_control_config *config)
> +{
> + struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
> + struct pd692x0_msg_content buf = {0};
> + struct pd692x0_msg msg;
> + int ret;
> +
> + ret = pd692x0_fw_unavailable(priv);
> + if (ret)
> + return ret;

It seems a bit late to check if the device has any firmware. I would
of expected probe to check that, and maybe attempt to download
firmware. If that fails, fail the probe, since the PSE is a brick.

> +static struct pd692x0_msg_ver pd692x0_get_sw_version(struct pd692x0_priv *priv)
> +{
> + struct pd692x0_msg msg = pd692x0_msg_template_list[PD692X0_MSG_GET_SW_VER];
> + struct device *dev = &priv->client->dev;
> + struct pd692x0_msg_content buf = {0};
> + struct pd692x0_msg_ver ver = {0};
> + int ret;
> +
> + ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
> + if (ret < 0) {
> + dev_err(dev, "Failed to get PSE version (%pe)\n", ERR_PTR(ret));
> + return ver;

I _think_ that return is wrong ???

> +static enum fw_upload_err pd692x0_fw_write(struct fw_upload *fwl,
> + const u8 *data, u32 offset,
> + u32 size, u32 *written)
> +{
> + struct pd692x0_priv *priv = fwl->dd_handle;
> + char line[PD692X0_FW_LINE_MAX_SZ];
> + const struct i2c_client *client;
> + int ret, i;
> + char cmd;
> +
> + client = priv->client;
> + priv->fw_state = PD692X0_FW_WRITE;
> +
> + /* Erase */
> + cmd = 'E';
> + ret = i2c_master_send(client, &cmd, 1);
> + if (ret < 0) {
> + dev_err(&client->dev,
> + "Failed to boot programming mode (%pe)\n",
> + ERR_PTR(ret));
> + return FW_UPLOAD_ERR_RW_ERROR;
> + }
> +
> + ret = pd692x0_fw_recv_resp(client, 100, "TOE\r\n", sizeof("TOE\r\n") - 1);
> + if (ret)
> + return ret;
> +
> + ret = pd692x0_fw_recv_resp(client, 5000, "TE\r\n", sizeof("TE\r\n") - 1);
> + if (ret)
> + dev_warn(&client->dev,
> + "Failed to erase internal memory, however still try to write Firmware\n");
> +
> + ret = pd692x0_fw_recv_resp(client, 100, "TPE\r\n", sizeof("TPE\r\n") - 1);
> + if (ret)
> + dev_warn(&client->dev,
> + "Failed to erase internal memory, however still try to write Firmware\n");
> +
> + if (priv->cancel_request)
> + return FW_UPLOAD_ERR_CANCELED;
> +
> + /* Program */
> + cmd = 'P';
> + ret = i2c_master_send(client, &cmd, sizeof(char));
> + if (ret < 0) {
> + dev_err(&client->dev,
> + "Failed to boot programming mode (%pe)\n",
> + ERR_PTR(ret));
> + return ret;
> + }
> +
> + ret = pd692x0_fw_recv_resp(client, 100, "TOP\r\n", sizeof("TOP\r\n") - 1);
> + if (ret)
> + return ret;
> +
> + i = 0;
> + while (i < size) {
> + ret = pd692x0_fw_get_next_line(data, line, size - i);
> + if (!ret) {
> + ret = FW_UPLOAD_ERR_FW_INVALID;
> + goto err;
> + }
> +
> + i += ret;
> + data += ret;
> + if (line[0] == 'S' && line[1] == '0') {
> + continue;
> + } else if (line[0] == 'S' && line[1] == '7') {
> + ret = pd692x0_fw_write_line(client, line, true);
> + if (ret)
> + goto err;

Is the firmware in Motorola SREC format? I thought the kernel had a
helper for that, but a quick search did not find it. So maybe i'm
remembering wrongly. But it seems silly for every driver to implement
an SREC parser.

Andrew