xref: /linux/drivers/net/pse-pd/pd692x0.c (revision ae37dc574259f2750e9e2666591b752678fcd7cc)
19a993845SKory Maincent (Dent Project) // SPDX-License-Identifier: GPL-2.0-only
29a993845SKory Maincent (Dent Project) /*
39a993845SKory Maincent (Dent Project)  * Driver for the Microchip PD692X0 PoE PSE Controller driver (I2C bus)
49a993845SKory Maincent (Dent Project)  *
59a993845SKory Maincent (Dent Project)  * Copyright (c) 2023 Bootlin, Kory Maincent <kory.maincent@bootlin.com>
69a993845SKory Maincent (Dent Project)  */
79a993845SKory Maincent (Dent Project) 
89a993845SKory Maincent (Dent Project) #include <linux/delay.h>
99a993845SKory Maincent (Dent Project) #include <linux/firmware.h>
109a993845SKory Maincent (Dent Project) #include <linux/i2c.h>
119a993845SKory Maincent (Dent Project) #include <linux/module.h>
129a993845SKory Maincent (Dent Project) #include <linux/of.h>
139a993845SKory Maincent (Dent Project) #include <linux/platform_device.h>
149a993845SKory Maincent (Dent Project) #include <linux/pse-pd/pse.h>
159a993845SKory Maincent (Dent Project) 
169a993845SKory Maincent (Dent Project) #define PD692X0_PSE_NAME "pd692x0_pse"
179a993845SKory Maincent (Dent Project) 
189a993845SKory Maincent (Dent Project) #define PD692X0_MAX_PIS	48
199a993845SKory Maincent (Dent Project) #define PD692X0_MAX_MANAGERS		12
209a993845SKory Maincent (Dent Project) #define PD692X0_MAX_MANAGER_PORTS	8
219a993845SKory Maincent (Dent Project) #define PD692X0_MAX_HW_PORTS	(PD692X0_MAX_MANAGERS * PD692X0_MAX_MANAGER_PORTS)
229a993845SKory Maincent (Dent Project) 
239a993845SKory Maincent (Dent Project) #define PD69200_BT_PROD_VER	24
249a993845SKory Maincent (Dent Project) #define PD69210_BT_PROD_VER	26
259a993845SKory Maincent (Dent Project) #define PD69220_BT_PROD_VER	29
269a993845SKory Maincent (Dent Project) 
279a993845SKory Maincent (Dent Project) #define PD692X0_FW_MAJ_VER	3
289a993845SKory Maincent (Dent Project) #define PD692X0_FW_MIN_VER	5
299a993845SKory Maincent (Dent Project) #define PD692X0_FW_PATCH_VER	5
309a993845SKory Maincent (Dent Project) 
319a993845SKory Maincent (Dent Project) enum pd692x0_fw_state {
329a993845SKory Maincent (Dent Project) 	PD692X0_FW_UNKNOWN,
339a993845SKory Maincent (Dent Project) 	PD692X0_FW_OK,
349a993845SKory Maincent (Dent Project) 	PD692X0_FW_BROKEN,
359a993845SKory Maincent (Dent Project) 	PD692X0_FW_NEED_UPDATE,
369a993845SKory Maincent (Dent Project) 	PD692X0_FW_PREPARE,
379a993845SKory Maincent (Dent Project) 	PD692X0_FW_WRITE,
389a993845SKory Maincent (Dent Project) 	PD692X0_FW_COMPLETE,
399a993845SKory Maincent (Dent Project) };
409a993845SKory Maincent (Dent Project) 
419a993845SKory Maincent (Dent Project) struct pd692x0_msg {
429a993845SKory Maincent (Dent Project) 	u8 key;
439a993845SKory Maincent (Dent Project) 	u8 echo;
449a993845SKory Maincent (Dent Project) 	u8 sub[3];
459a993845SKory Maincent (Dent Project) 	u8 data[8];
469a993845SKory Maincent (Dent Project) 	__be16 chksum;
479a993845SKory Maincent (Dent Project) } __packed;
489a993845SKory Maincent (Dent Project) 
499a993845SKory Maincent (Dent Project) struct pd692x0_msg_ver {
509a993845SKory Maincent (Dent Project) 	u8 prod;
519a993845SKory Maincent (Dent Project) 	u8 maj_sw_ver;
529a993845SKory Maincent (Dent Project) 	u8 min_sw_ver;
539a993845SKory Maincent (Dent Project) 	u8 pa_sw_ver;
549a993845SKory Maincent (Dent Project) 	u8 param;
559a993845SKory Maincent (Dent Project) 	u8 build;
569a993845SKory Maincent (Dent Project) };
579a993845SKory Maincent (Dent Project) 
589a993845SKory Maincent (Dent Project) enum {
599a993845SKory Maincent (Dent Project) 	PD692X0_KEY_CMD,
609a993845SKory Maincent (Dent Project) 	PD692X0_KEY_PRG,
619a993845SKory Maincent (Dent Project) 	PD692X0_KEY_REQ,
629a993845SKory Maincent (Dent Project) 	PD692X0_KEY_TLM,
639a993845SKory Maincent (Dent Project) 	PD692X0_KEY_TEST,
649a993845SKory Maincent (Dent Project) 	PD692X0_KEY_REPORT = 0x52
659a993845SKory Maincent (Dent Project) };
669a993845SKory Maincent (Dent Project) 
679a993845SKory Maincent (Dent Project) enum {
689a993845SKory Maincent (Dent Project) 	PD692X0_MSG_RESET,
699a993845SKory Maincent (Dent Project) 	PD692X0_MSG_GET_SYS_STATUS,
709a993845SKory Maincent (Dent Project) 	PD692X0_MSG_GET_SW_VER,
719a993845SKory Maincent (Dent Project) 	PD692X0_MSG_SET_TMP_PORT_MATRIX,
729a993845SKory Maincent (Dent Project) 	PD692X0_MSG_PRG_PORT_MATRIX,
739a993845SKory Maincent (Dent Project) 	PD692X0_MSG_SET_PORT_PARAM,
749a993845SKory Maincent (Dent Project) 	PD692X0_MSG_GET_PORT_STATUS,
759a993845SKory Maincent (Dent Project) 	PD692X0_MSG_DOWNLOAD_CMD,
76*ae37dc57SKory Maincent (Dent Project) 	PD692X0_MSG_GET_PORT_CLASS,
779a993845SKory Maincent (Dent Project) 
789a993845SKory Maincent (Dent Project) 	/* add new message above here */
799a993845SKory Maincent (Dent Project) 	PD692X0_MSG_CNT
809a993845SKory Maincent (Dent Project) };
819a993845SKory Maincent (Dent Project) 
829a993845SKory Maincent (Dent Project) struct pd692x0_priv {
839a993845SKory Maincent (Dent Project) 	struct i2c_client *client;
849a993845SKory Maincent (Dent Project) 	struct pse_controller_dev pcdev;
859a993845SKory Maincent (Dent Project) 	struct device_node *np;
869a993845SKory Maincent (Dent Project) 
879a993845SKory Maincent (Dent Project) 	enum pd692x0_fw_state fw_state;
889a993845SKory Maincent (Dent Project) 	struct fw_upload *fwl;
899a993845SKory Maincent (Dent Project) 	bool cancel_request;
909a993845SKory Maincent (Dent Project) 
919a993845SKory Maincent (Dent Project) 	u8 msg_id;
929a993845SKory Maincent (Dent Project) 	bool last_cmd_key;
939a993845SKory Maincent (Dent Project) 	unsigned long last_cmd_key_time;
949a993845SKory Maincent (Dent Project) 
959a993845SKory Maincent (Dent Project) 	enum ethtool_c33_pse_admin_state admin_state[PD692X0_MAX_PIS];
969a993845SKory Maincent (Dent Project) };
979a993845SKory Maincent (Dent Project) 
989a993845SKory Maincent (Dent Project) /* Template list of communication messages. The non-null bytes defined here
999a993845SKory Maincent (Dent Project)  * constitute the fixed portion of the messages. The remaining bytes will
1009a993845SKory Maincent (Dent Project)  * be configured later within the functions. Refer to the "PD692x0 BT Serial
1019a993845SKory Maincent (Dent Project)  * Communication Protocol User Guide" for comprehensive details on messages
1029a993845SKory Maincent (Dent Project)  * content.
1039a993845SKory Maincent (Dent Project)  */
1049a993845SKory Maincent (Dent Project) static const struct pd692x0_msg pd692x0_msg_template_list[PD692X0_MSG_CNT] = {
1059a993845SKory Maincent (Dent Project) 	[PD692X0_MSG_RESET] = {
1069a993845SKory Maincent (Dent Project) 		.key = PD692X0_KEY_CMD,
1079a993845SKory Maincent (Dent Project) 		.sub = {0x07, 0x55, 0x00},
1089a993845SKory Maincent (Dent Project) 		.data = {0x55, 0x00, 0x55, 0x4e,
1099a993845SKory Maincent (Dent Project) 			 0x4e, 0x4e, 0x4e, 0x4e},
1109a993845SKory Maincent (Dent Project) 	},
1119a993845SKory Maincent (Dent Project) 	[PD692X0_MSG_GET_SYS_STATUS] = {
1129a993845SKory Maincent (Dent Project) 		.key = PD692X0_KEY_REQ,
1139a993845SKory Maincent (Dent Project) 		.sub = {0x07, 0xd0, 0x4e},
1149a993845SKory Maincent (Dent Project) 		.data = {0x4e, 0x4e, 0x4e, 0x4e,
1159a993845SKory Maincent (Dent Project) 			 0x4e, 0x4e, 0x4e, 0x4e},
1169a993845SKory Maincent (Dent Project) 	},
1179a993845SKory Maincent (Dent Project) 	[PD692X0_MSG_GET_SW_VER] = {
1189a993845SKory Maincent (Dent Project) 		.key = PD692X0_KEY_REQ,
1199a993845SKory Maincent (Dent Project) 		.sub = {0x07, 0x1e, 0x21},
1209a993845SKory Maincent (Dent Project) 		.data = {0x4e, 0x4e, 0x4e, 0x4e,
1219a993845SKory Maincent (Dent Project) 			 0x4e, 0x4e, 0x4e, 0x4e},
1229a993845SKory Maincent (Dent Project) 	},
1239a993845SKory Maincent (Dent Project) 	[PD692X0_MSG_SET_TMP_PORT_MATRIX] = {
1249a993845SKory Maincent (Dent Project) 		.key = PD692X0_KEY_CMD,
1259a993845SKory Maincent (Dent Project) 		.sub	 = {0x05, 0x43},
1269a993845SKory Maincent (Dent Project) 		.data = {   0, 0x4e, 0x4e, 0x4e,
1279a993845SKory Maincent (Dent Project) 			 0x4e, 0x4e, 0x4e, 0x4e},
1289a993845SKory Maincent (Dent Project) 	},
1299a993845SKory Maincent (Dent Project) 	[PD692X0_MSG_PRG_PORT_MATRIX] = {
1309a993845SKory Maincent (Dent Project) 		.key = PD692X0_KEY_CMD,
1319a993845SKory Maincent (Dent Project) 		.sub = {0x07, 0x43, 0x4e},
1329a993845SKory Maincent (Dent Project) 		.data = {0x4e, 0x4e, 0x4e, 0x4e,
1339a993845SKory Maincent (Dent Project) 			 0x4e, 0x4e, 0x4e, 0x4e},
1349a993845SKory Maincent (Dent Project) 	},
1359a993845SKory Maincent (Dent Project) 	[PD692X0_MSG_SET_PORT_PARAM] = {
1369a993845SKory Maincent (Dent Project) 		.key = PD692X0_KEY_CMD,
1379a993845SKory Maincent (Dent Project) 		.sub = {0x05, 0xc0},
1389a993845SKory Maincent (Dent Project) 		.data = {   0, 0xff, 0xff, 0xff,
1399a993845SKory Maincent (Dent Project) 			 0x4e, 0x4e, 0x4e, 0x4e},
1409a993845SKory Maincent (Dent Project) 	},
1419a993845SKory Maincent (Dent Project) 	[PD692X0_MSG_GET_PORT_STATUS] = {
1429a993845SKory Maincent (Dent Project) 		.key = PD692X0_KEY_REQ,
1439a993845SKory Maincent (Dent Project) 		.sub = {0x05, 0xc1},
1449a993845SKory Maincent (Dent Project) 		.data = {0x4e, 0x4e, 0x4e, 0x4e,
1459a993845SKory Maincent (Dent Project) 			 0x4e, 0x4e, 0x4e, 0x4e},
1469a993845SKory Maincent (Dent Project) 	},
1479a993845SKory Maincent (Dent Project) 	[PD692X0_MSG_DOWNLOAD_CMD] = {
1489a993845SKory Maincent (Dent Project) 		.key = PD692X0_KEY_PRG,
1499a993845SKory Maincent (Dent Project) 		.sub = {0xff, 0x99, 0x15},
1509a993845SKory Maincent (Dent Project) 		.data = {0x16, 0x16, 0x99, 0x4e,
1519a993845SKory Maincent (Dent Project) 			 0x4e, 0x4e, 0x4e, 0x4e},
1529a993845SKory Maincent (Dent Project) 	},
153*ae37dc57SKory Maincent (Dent Project) 	[PD692X0_MSG_GET_PORT_CLASS] = {
154*ae37dc57SKory Maincent (Dent Project) 		.key = PD692X0_KEY_REQ,
155*ae37dc57SKory Maincent (Dent Project) 		.sub = {0x05, 0xc4},
156*ae37dc57SKory Maincent (Dent Project) 		.data = {0x4e, 0x4e, 0x4e, 0x4e,
157*ae37dc57SKory Maincent (Dent Project) 			 0x4e, 0x4e, 0x4e, 0x4e},
158*ae37dc57SKory Maincent (Dent Project) 	},
1599a993845SKory Maincent (Dent Project) };
1609a993845SKory Maincent (Dent Project) 
1619a993845SKory Maincent (Dent Project) static u8 pd692x0_build_msg(struct pd692x0_msg *msg, u8 echo)
1629a993845SKory Maincent (Dent Project) {
1639a993845SKory Maincent (Dent Project) 	u8 *data = (u8 *)msg;
1649a993845SKory Maincent (Dent Project) 	u16 chksum = 0;
1659a993845SKory Maincent (Dent Project) 	int i;
1669a993845SKory Maincent (Dent Project) 
1679a993845SKory Maincent (Dent Project) 	msg->echo = echo++;
1689a993845SKory Maincent (Dent Project) 	if (echo == 0xff)
1699a993845SKory Maincent (Dent Project) 		echo = 0;
1709a993845SKory Maincent (Dent Project) 
1719a993845SKory Maincent (Dent Project) 	for (i = 0; i < sizeof(*msg) - sizeof(msg->chksum); i++)
1729a993845SKory Maincent (Dent Project) 		chksum += data[i];
1739a993845SKory Maincent (Dent Project) 
1749a993845SKory Maincent (Dent Project) 	msg->chksum = cpu_to_be16(chksum);
1759a993845SKory Maincent (Dent Project) 
1769a993845SKory Maincent (Dent Project) 	return echo;
1779a993845SKory Maincent (Dent Project) }
1789a993845SKory Maincent (Dent Project) 
1799a993845SKory Maincent (Dent Project) static int pd692x0_send_msg(struct pd692x0_priv *priv, struct pd692x0_msg *msg)
1809a993845SKory Maincent (Dent Project) {
1819a993845SKory Maincent (Dent Project) 	const struct i2c_client *client = priv->client;
1829a993845SKory Maincent (Dent Project) 	int ret;
1839a993845SKory Maincent (Dent Project) 
1849a993845SKory Maincent (Dent Project) 	if (msg->key == PD692X0_KEY_CMD && priv->last_cmd_key) {
1859a993845SKory Maincent (Dent Project) 		int cmd_msleep;
1869a993845SKory Maincent (Dent Project) 
1879a993845SKory Maincent (Dent Project) 		cmd_msleep = 30 - jiffies_to_msecs(jiffies - priv->last_cmd_key_time);
1889a993845SKory Maincent (Dent Project) 		if (cmd_msleep > 0)
1899a993845SKory Maincent (Dent Project) 			msleep(cmd_msleep);
1909a993845SKory Maincent (Dent Project) 	}
1919a993845SKory Maincent (Dent Project) 
1929a993845SKory Maincent (Dent Project) 	/* Add echo and checksum bytes to the message */
1939a993845SKory Maincent (Dent Project) 	priv->msg_id = pd692x0_build_msg(msg, priv->msg_id);
1949a993845SKory Maincent (Dent Project) 
1959a993845SKory Maincent (Dent Project) 	ret = i2c_master_send(client, (u8 *)msg, sizeof(*msg));
1969a993845SKory Maincent (Dent Project) 	if (ret != sizeof(*msg))
1979a993845SKory Maincent (Dent Project) 		return -EIO;
1989a993845SKory Maincent (Dent Project) 
1999a993845SKory Maincent (Dent Project) 	return 0;
2009a993845SKory Maincent (Dent Project) }
2019a993845SKory Maincent (Dent Project) 
2029a993845SKory Maincent (Dent Project) static int pd692x0_reset(struct pd692x0_priv *priv)
2039a993845SKory Maincent (Dent Project) {
2049a993845SKory Maincent (Dent Project) 	const struct i2c_client *client = priv->client;
2059a993845SKory Maincent (Dent Project) 	struct pd692x0_msg msg, buf = {0};
2069a993845SKory Maincent (Dent Project) 	int ret;
2079a993845SKory Maincent (Dent Project) 
2089a993845SKory Maincent (Dent Project) 	msg = pd692x0_msg_template_list[PD692X0_MSG_RESET];
2099a993845SKory Maincent (Dent Project) 	ret = pd692x0_send_msg(priv, &msg);
2109a993845SKory Maincent (Dent Project) 	if (ret) {
2119a993845SKory Maincent (Dent Project) 		dev_err(&client->dev,
2129a993845SKory Maincent (Dent Project) 			"Failed to reset the controller (%pe)\n", ERR_PTR(ret));
2139a993845SKory Maincent (Dent Project) 		return ret;
2149a993845SKory Maincent (Dent Project) 	}
2159a993845SKory Maincent (Dent Project) 
2169a993845SKory Maincent (Dent Project) 	msleep(30);
2179a993845SKory Maincent (Dent Project) 
2189a993845SKory Maincent (Dent Project) 	ret = i2c_master_recv(client, (u8 *)&buf, sizeof(buf));
2199a993845SKory Maincent (Dent Project) 	if (ret != sizeof(buf))
2209a993845SKory Maincent (Dent Project) 		return ret < 0 ? ret : -EIO;
2219a993845SKory Maincent (Dent Project) 
2229a993845SKory Maincent (Dent Project) 	/* Is the reply a successful report message */
2239a993845SKory Maincent (Dent Project) 	if (buf.key != PD692X0_KEY_REPORT || buf.sub[0] || buf.sub[1])
2249a993845SKory Maincent (Dent Project) 		return -EIO;
2259a993845SKory Maincent (Dent Project) 
2269a993845SKory Maincent (Dent Project) 	msleep(300);
2279a993845SKory Maincent (Dent Project) 
2289a993845SKory Maincent (Dent Project) 	ret = i2c_master_recv(client, (u8 *)&buf, sizeof(buf));
2299a993845SKory Maincent (Dent Project) 	if (ret != sizeof(buf))
2309a993845SKory Maincent (Dent Project) 		return ret < 0 ? ret : -EIO;
2319a993845SKory Maincent (Dent Project) 
2329a993845SKory Maincent (Dent Project) 	/* Is the boot status without error */
2339a993845SKory Maincent (Dent Project) 	if (buf.key != 0x03 || buf.echo != 0xff || buf.sub[0] & 0x1) {
2349a993845SKory Maincent (Dent Project) 		dev_err(&client->dev, "PSE controller error\n");
2359a993845SKory Maincent (Dent Project) 		return -EIO;
2369a993845SKory Maincent (Dent Project) 	}
2379a993845SKory Maincent (Dent Project) 
2389a993845SKory Maincent (Dent Project) 	return 0;
2399a993845SKory Maincent (Dent Project) }
2409a993845SKory Maincent (Dent Project) 
2419a993845SKory Maincent (Dent Project) static bool pd692x0_try_recv_msg(const struct i2c_client *client,
2429a993845SKory Maincent (Dent Project) 				 struct pd692x0_msg *msg,
2439a993845SKory Maincent (Dent Project) 				 struct pd692x0_msg *buf)
2449a993845SKory Maincent (Dent Project) {
2459a993845SKory Maincent (Dent Project) 	/* Wait 30ms before readback as mandated by the protocol */
2469a993845SKory Maincent (Dent Project) 	msleep(30);
2479a993845SKory Maincent (Dent Project) 
2489a993845SKory Maincent (Dent Project) 	memset(buf, 0, sizeof(*buf));
2499a993845SKory Maincent (Dent Project) 	i2c_master_recv(client, (u8 *)buf, sizeof(*buf));
2509a993845SKory Maincent (Dent Project) 	if (buf->key)
2519a993845SKory Maincent (Dent Project) 		return 0;
2529a993845SKory Maincent (Dent Project) 
2539a993845SKory Maincent (Dent Project) 	msleep(100);
2549a993845SKory Maincent (Dent Project) 
2559a993845SKory Maincent (Dent Project) 	memset(buf, 0, sizeof(*buf));
2569a993845SKory Maincent (Dent Project) 	i2c_master_recv(client, (u8 *)buf, sizeof(*buf));
2579a993845SKory Maincent (Dent Project) 	if (buf->key)
2589a993845SKory Maincent (Dent Project) 		return 0;
2599a993845SKory Maincent (Dent Project) 
2609a993845SKory Maincent (Dent Project) 	return 1;
2619a993845SKory Maincent (Dent Project) }
2629a993845SKory Maincent (Dent Project) 
2639a993845SKory Maincent (Dent Project) /* Implementation of I2C communication, specifically addressing scenarios
2649a993845SKory Maincent (Dent Project)  * involving communication loss. Refer to the "Synchronization During
2659a993845SKory Maincent (Dent Project)  * Communication Loss" section in the Communication Protocol document for
2669a993845SKory Maincent (Dent Project)  * further details.
2679a993845SKory Maincent (Dent Project)  */
2689a993845SKory Maincent (Dent Project) static int pd692x0_recv_msg(struct pd692x0_priv *priv,
2699a993845SKory Maincent (Dent Project) 			    struct pd692x0_msg *msg,
2709a993845SKory Maincent (Dent Project) 			    struct pd692x0_msg *buf)
2719a993845SKory Maincent (Dent Project) {
2729a993845SKory Maincent (Dent Project) 	const struct i2c_client *client = priv->client;
2739a993845SKory Maincent (Dent Project) 	int ret;
2749a993845SKory Maincent (Dent Project) 
2759a993845SKory Maincent (Dent Project) 	ret = pd692x0_try_recv_msg(client, msg, buf);
2769a993845SKory Maincent (Dent Project) 	if (!ret)
2779a993845SKory Maincent (Dent Project) 		goto out_success;
2789a993845SKory Maincent (Dent Project) 
2799a993845SKory Maincent (Dent Project) 	dev_warn(&client->dev,
2809a993845SKory Maincent (Dent Project) 		 "Communication lost, rtnl is locked until communication is back!");
2819a993845SKory Maincent (Dent Project) 
2829a993845SKory Maincent (Dent Project) 	ret = pd692x0_send_msg(priv, msg);
2839a993845SKory Maincent (Dent Project) 	if (ret)
2849a993845SKory Maincent (Dent Project) 		return ret;
2859a993845SKory Maincent (Dent Project) 
2869a993845SKory Maincent (Dent Project) 	ret = pd692x0_try_recv_msg(client, msg, buf);
2879a993845SKory Maincent (Dent Project) 	if (!ret)
2889a993845SKory Maincent (Dent Project) 		goto out_success2;
2899a993845SKory Maincent (Dent Project) 
2909a993845SKory Maincent (Dent Project) 	msleep(10000);
2919a993845SKory Maincent (Dent Project) 
2929a993845SKory Maincent (Dent Project) 	ret = pd692x0_send_msg(priv, msg);
2939a993845SKory Maincent (Dent Project) 	if (ret)
2949a993845SKory Maincent (Dent Project) 		return ret;
2959a993845SKory Maincent (Dent Project) 
2969a993845SKory Maincent (Dent Project) 	ret = pd692x0_try_recv_msg(client, msg, buf);
2979a993845SKory Maincent (Dent Project) 	if (!ret)
2989a993845SKory Maincent (Dent Project) 		goto out_success2;
2999a993845SKory Maincent (Dent Project) 
3009a993845SKory Maincent (Dent Project) 	return pd692x0_reset(priv);
3019a993845SKory Maincent (Dent Project) 
3029a993845SKory Maincent (Dent Project) out_success2:
3039a993845SKory Maincent (Dent Project) 	dev_warn(&client->dev, "Communication is back, rtnl is unlocked!");
3049a993845SKory Maincent (Dent Project) out_success:
3059a993845SKory Maincent (Dent Project) 	if (msg->key == PD692X0_KEY_CMD) {
3069a993845SKory Maincent (Dent Project) 		priv->last_cmd_key = true;
3079a993845SKory Maincent (Dent Project) 		priv->last_cmd_key_time = jiffies;
3089a993845SKory Maincent (Dent Project) 	} else {
3099a993845SKory Maincent (Dent Project) 		priv->last_cmd_key = false;
3109a993845SKory Maincent (Dent Project) 	}
3119a993845SKory Maincent (Dent Project) 
3129a993845SKory Maincent (Dent Project) 	return 0;
3139a993845SKory Maincent (Dent Project) }
3149a993845SKory Maincent (Dent Project) 
3159a993845SKory Maincent (Dent Project) static int pd692x0_sendrecv_msg(struct pd692x0_priv *priv,
3169a993845SKory Maincent (Dent Project) 				struct pd692x0_msg *msg,
3179a993845SKory Maincent (Dent Project) 				struct pd692x0_msg *buf)
3189a993845SKory Maincent (Dent Project) {
3199a993845SKory Maincent (Dent Project) 	struct device *dev = &priv->client->dev;
3209a993845SKory Maincent (Dent Project) 	int ret;
3219a993845SKory Maincent (Dent Project) 
3229a993845SKory Maincent (Dent Project) 	ret = pd692x0_send_msg(priv, msg);
3239a993845SKory Maincent (Dent Project) 	if (ret)
3249a993845SKory Maincent (Dent Project) 		return ret;
3259a993845SKory Maincent (Dent Project) 
3269a993845SKory Maincent (Dent Project) 	ret = pd692x0_recv_msg(priv, msg, buf);
3279a993845SKory Maincent (Dent Project) 	if (ret)
3289a993845SKory Maincent (Dent Project) 		return ret;
3299a993845SKory Maincent (Dent Project) 
3309a993845SKory Maincent (Dent Project) 	if (msg->echo != buf->echo) {
3319a993845SKory Maincent (Dent Project) 		dev_err(dev,
3329a993845SKory Maincent (Dent Project) 			"Wrong match in message ID, expect %d received %d.\n",
3339a993845SKory Maincent (Dent Project) 			msg->echo, buf->echo);
3349a993845SKory Maincent (Dent Project) 		return -EIO;
3359a993845SKory Maincent (Dent Project) 	}
3369a993845SKory Maincent (Dent Project) 
3379a993845SKory Maincent (Dent Project) 	/* If the reply is a report message is it successful */
3389a993845SKory Maincent (Dent Project) 	if (buf->key == PD692X0_KEY_REPORT &&
3399a993845SKory Maincent (Dent Project) 	    (buf->sub[0] || buf->sub[1])) {
3409a993845SKory Maincent (Dent Project) 		return -EIO;
3419a993845SKory Maincent (Dent Project) 	}
3429a993845SKory Maincent (Dent Project) 
3439a993845SKory Maincent (Dent Project) 	return 0;
3449a993845SKory Maincent (Dent Project) }
3459a993845SKory Maincent (Dent Project) 
3469a993845SKory Maincent (Dent Project) static struct pd692x0_priv *to_pd692x0_priv(struct pse_controller_dev *pcdev)
3479a993845SKory Maincent (Dent Project) {
3489a993845SKory Maincent (Dent Project) 	return container_of(pcdev, struct pd692x0_priv, pcdev);
3499a993845SKory Maincent (Dent Project) }
3509a993845SKory Maincent (Dent Project) 
3519a993845SKory Maincent (Dent Project) static int pd692x0_fw_unavailable(struct pd692x0_priv *priv)
3529a993845SKory Maincent (Dent Project) {
3539a993845SKory Maincent (Dent Project) 	switch (priv->fw_state) {
3549a993845SKory Maincent (Dent Project) 	case PD692X0_FW_OK:
3559a993845SKory Maincent (Dent Project) 		return 0;
3569a993845SKory Maincent (Dent Project) 	case PD692X0_FW_PREPARE:
3579a993845SKory Maincent (Dent Project) 	case PD692X0_FW_WRITE:
3589a993845SKory Maincent (Dent Project) 	case PD692X0_FW_COMPLETE:
3599a993845SKory Maincent (Dent Project) 		dev_err(&priv->client->dev, "Firmware update in progress!\n");
3609a993845SKory Maincent (Dent Project) 		return -EBUSY;
3619a993845SKory Maincent (Dent Project) 	case PD692X0_FW_BROKEN:
3629a993845SKory Maincent (Dent Project) 	case PD692X0_FW_NEED_UPDATE:
3639a993845SKory Maincent (Dent Project) 	default:
3649a993845SKory Maincent (Dent Project) 		dev_err(&priv->client->dev,
3659a993845SKory Maincent (Dent Project) 			"Firmware issue. Please update it!\n");
3669a993845SKory Maincent (Dent Project) 		return -EOPNOTSUPP;
3679a993845SKory Maincent (Dent Project) 	}
3689a993845SKory Maincent (Dent Project) }
3699a993845SKory Maincent (Dent Project) 
3709a993845SKory Maincent (Dent Project) static int pd692x0_pi_enable(struct pse_controller_dev *pcdev, int id)
3719a993845SKory Maincent (Dent Project) {
3729a993845SKory Maincent (Dent Project) 	struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
3739a993845SKory Maincent (Dent Project) 	struct pd692x0_msg msg, buf = {0};
3749a993845SKory Maincent (Dent Project) 	int ret;
3759a993845SKory Maincent (Dent Project) 
3769a993845SKory Maincent (Dent Project) 	ret = pd692x0_fw_unavailable(priv);
3779a993845SKory Maincent (Dent Project) 	if (ret)
3789a993845SKory Maincent (Dent Project) 		return ret;
3799a993845SKory Maincent (Dent Project) 
3809a993845SKory Maincent (Dent Project) 	if (priv->admin_state[id] == ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED)
3819a993845SKory Maincent (Dent Project) 		return 0;
3829a993845SKory Maincent (Dent Project) 
3839a993845SKory Maincent (Dent Project) 	msg = pd692x0_msg_template_list[PD692X0_MSG_SET_PORT_PARAM];
3849a993845SKory Maincent (Dent Project) 	msg.data[0] = 0x1;
3859a993845SKory Maincent (Dent Project) 	msg.sub[2] = id;
3869a993845SKory Maincent (Dent Project) 	ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
3879a993845SKory Maincent (Dent Project) 	if (ret < 0)
3889a993845SKory Maincent (Dent Project) 		return ret;
3899a993845SKory Maincent (Dent Project) 
3909a993845SKory Maincent (Dent Project) 	priv->admin_state[id] = ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED;
3919a993845SKory Maincent (Dent Project) 
3929a993845SKory Maincent (Dent Project) 	return 0;
3939a993845SKory Maincent (Dent Project) }
3949a993845SKory Maincent (Dent Project) 
3959a993845SKory Maincent (Dent Project) static int pd692x0_pi_disable(struct pse_controller_dev *pcdev, int id)
3969a993845SKory Maincent (Dent Project) {
3979a993845SKory Maincent (Dent Project) 	struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
3989a993845SKory Maincent (Dent Project) 	struct pd692x0_msg msg, buf = {0};
3999a993845SKory Maincent (Dent Project) 	int ret;
4009a993845SKory Maincent (Dent Project) 
4019a993845SKory Maincent (Dent Project) 	ret = pd692x0_fw_unavailable(priv);
4029a993845SKory Maincent (Dent Project) 	if (ret)
4039a993845SKory Maincent (Dent Project) 		return ret;
4049a993845SKory Maincent (Dent Project) 
4059a993845SKory Maincent (Dent Project) 	if (priv->admin_state[id] == ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED)
4069a993845SKory Maincent (Dent Project) 		return 0;
4079a993845SKory Maincent (Dent Project) 
4089a993845SKory Maincent (Dent Project) 	msg = pd692x0_msg_template_list[PD692X0_MSG_SET_PORT_PARAM];
4099a993845SKory Maincent (Dent Project) 	msg.data[0] = 0x0;
4109a993845SKory Maincent (Dent Project) 	msg.sub[2] = id;
4119a993845SKory Maincent (Dent Project) 	ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
4129a993845SKory Maincent (Dent Project) 	if (ret < 0)
4139a993845SKory Maincent (Dent Project) 		return ret;
4149a993845SKory Maincent (Dent Project) 
4159a993845SKory Maincent (Dent Project) 	priv->admin_state[id] = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED;
4169a993845SKory Maincent (Dent Project) 
4179a993845SKory Maincent (Dent Project) 	return 0;
4189a993845SKory Maincent (Dent Project) }
4199a993845SKory Maincent (Dent Project) 
4209a993845SKory Maincent (Dent Project) static int pd692x0_pi_is_enabled(struct pse_controller_dev *pcdev, int id)
4219a993845SKory Maincent (Dent Project) {
4229a993845SKory Maincent (Dent Project) 	struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
4239a993845SKory Maincent (Dent Project) 	struct pd692x0_msg msg, buf = {0};
4249a993845SKory Maincent (Dent Project) 	int ret;
4259a993845SKory Maincent (Dent Project) 
4269a993845SKory Maincent (Dent Project) 	ret = pd692x0_fw_unavailable(priv);
4279a993845SKory Maincent (Dent Project) 	if (ret)
4289a993845SKory Maincent (Dent Project) 		return ret;
4299a993845SKory Maincent (Dent Project) 
4309a993845SKory Maincent (Dent Project) 	msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_STATUS];
4319a993845SKory Maincent (Dent Project) 	msg.sub[2] = id;
4329a993845SKory Maincent (Dent Project) 	ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
4339a993845SKory Maincent (Dent Project) 	if (ret < 0)
4349a993845SKory Maincent (Dent Project) 		return ret;
4359a993845SKory Maincent (Dent Project) 
4369a993845SKory Maincent (Dent Project) 	if (buf.sub[1]) {
4379a993845SKory Maincent (Dent Project) 		priv->admin_state[id] = ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED;
4389a993845SKory Maincent (Dent Project) 		return 1;
4399a993845SKory Maincent (Dent Project) 	} else {
4409a993845SKory Maincent (Dent Project) 		priv->admin_state[id] = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED;
4419a993845SKory Maincent (Dent Project) 		return 0;
4429a993845SKory Maincent (Dent Project) 	}
4439a993845SKory Maincent (Dent Project) }
4449a993845SKory Maincent (Dent Project) 
445*ae37dc57SKory Maincent (Dent Project) struct pd692x0_pse_ext_state_mapping {
446*ae37dc57SKory Maincent (Dent Project) 	u32 status_code;
447*ae37dc57SKory Maincent (Dent Project) 	enum ethtool_c33_pse_ext_state pse_ext_state;
448*ae37dc57SKory Maincent (Dent Project) 	u32 pse_ext_substate;
449*ae37dc57SKory Maincent (Dent Project) };
450*ae37dc57SKory Maincent (Dent Project) 
451*ae37dc57SKory Maincent (Dent Project) static const struct pd692x0_pse_ext_state_mapping
452*ae37dc57SKory Maincent (Dent Project) pd692x0_pse_ext_state_map[] = {
453*ae37dc57SKory Maincent (Dent Project) 	{0x06, ETHTOOL_C33_PSE_EXT_STATE_OPTION_VPORT_LIM,
454*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_VPORT_LIM_HIGH_VOLTAGE},
455*ae37dc57SKory Maincent (Dent Project) 	{0x07, ETHTOOL_C33_PSE_EXT_STATE_OPTION_VPORT_LIM,
456*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_VPORT_LIM_LOW_VOLTAGE},
457*ae37dc57SKory Maincent (Dent Project) 	{0x08, ETHTOOL_C33_PSE_EXT_STATE_MR_PSE_ENABLE,
458*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_MR_PSE_ENABLE_DISABLE_PIN_ACTIVE},
459*ae37dc57SKory Maincent (Dent Project) 	{0x0C, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
460*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_NON_EXISTING_PORT},
461*ae37dc57SKory Maincent (Dent Project) 	{0x11, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
462*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_UNDEFINED_PORT},
463*ae37dc57SKory Maincent (Dent Project) 	{0x12, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
464*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_INTERNAL_HW_FAULT},
465*ae37dc57SKory Maincent (Dent Project) 	{0x1B, ETHTOOL_C33_PSE_EXT_STATE_OPTION_DETECT_TED,
466*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_DETECT_TED_DET_IN_PROCESS},
467*ae37dc57SKory Maincent (Dent Project) 	{0x1C, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
468*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_UNKNOWN_PORT_STATUS},
469*ae37dc57SKory Maincent (Dent Project) 	{0x1E, ETHTOOL_C33_PSE_EXT_STATE_MR_MPS_VALID,
470*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_MR_MPS_VALID_DETECTED_UNDERLOAD},
471*ae37dc57SKory Maincent (Dent Project) 	{0x1F, ETHTOOL_C33_PSE_EXT_STATE_OVLD_DETECTED,
472*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_OVLD_DETECTED_OVERLOAD},
473*ae37dc57SKory Maincent (Dent Project) 	{0x20, ETHTOOL_C33_PSE_EXT_STATE_POWER_NOT_AVAILABLE,
474*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_BUDGET_EXCEEDED},
475*ae37dc57SKory Maincent (Dent Project) 	{0x21, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
476*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_INTERNAL_HW_FAULT},
477*ae37dc57SKory Maincent (Dent Project) 	{0x22, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
478*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_CONFIG_CHANGE},
479*ae37dc57SKory Maincent (Dent Project) 	{0x24, ETHTOOL_C33_PSE_EXT_STATE_OPTION_VPORT_LIM,
480*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_VPORT_LIM_VOLTAGE_INJECTION},
481*ae37dc57SKory Maincent (Dent Project) 	{0x25, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
482*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_UNKNOWN_PORT_STATUS},
483*ae37dc57SKory Maincent (Dent Project) 	{0x34, ETHTOOL_C33_PSE_EXT_STATE_SHORT_DETECTED,
484*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_SHORT_DETECTED_SHORT_CONDITION},
485*ae37dc57SKory Maincent (Dent Project) 	{0x35, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
486*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_DETECTED_OVER_TEMP},
487*ae37dc57SKory Maincent (Dent Project) 	{0x36, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
488*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_DETECTED_OVER_TEMP},
489*ae37dc57SKory Maincent (Dent Project) 	{0x37, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
490*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_UNKNOWN_PORT_STATUS},
491*ae37dc57SKory Maincent (Dent Project) 	{0x3C, ETHTOOL_C33_PSE_EXT_STATE_POWER_NOT_AVAILABLE,
492*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_PORT_PW_LIMIT_EXCEEDS_CONTROLLER_BUDGET},
493*ae37dc57SKory Maincent (Dent Project) 	{0x3D, ETHTOOL_C33_PSE_EXT_STATE_POWER_NOT_AVAILABLE,
494*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_PD_REQUEST_EXCEEDS_PORT_LIMIT},
495*ae37dc57SKory Maincent (Dent Project) 	{0x41, ETHTOOL_C33_PSE_EXT_STATE_POWER_NOT_AVAILABLE,
496*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_HW_PW_LIMIT},
497*ae37dc57SKory Maincent (Dent Project) 	{0x43, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
498*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_UNKNOWN_PORT_STATUS},
499*ae37dc57SKory Maincent (Dent Project) 	{0xA7, ETHTOOL_C33_PSE_EXT_STATE_OPTION_DETECT_TED,
500*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_DETECT_TED_CONNECTION_CHECK_ERROR},
501*ae37dc57SKory Maincent (Dent Project) 	{0xA8, ETHTOOL_C33_PSE_EXT_STATE_MR_MPS_VALID,
502*ae37dc57SKory Maincent (Dent Project) 		ETHTOOL_C33_PSE_EXT_SUBSTATE_MR_MPS_VALID_CONNECTION_OPEN},
503*ae37dc57SKory Maincent (Dent Project) 	{ /* sentinel */ }
504*ae37dc57SKory Maincent (Dent Project) };
505*ae37dc57SKory Maincent (Dent Project) 
506*ae37dc57SKory Maincent (Dent Project) static void
507*ae37dc57SKory Maincent (Dent Project) pd692x0_get_ext_state(struct ethtool_c33_pse_ext_state_info *c33_ext_state_info,
508*ae37dc57SKory Maincent (Dent Project) 		      u32 status_code)
509*ae37dc57SKory Maincent (Dent Project) {
510*ae37dc57SKory Maincent (Dent Project) 	const struct pd692x0_pse_ext_state_mapping *ext_state_map;
511*ae37dc57SKory Maincent (Dent Project) 
512*ae37dc57SKory Maincent (Dent Project) 	ext_state_map = pd692x0_pse_ext_state_map;
513*ae37dc57SKory Maincent (Dent Project) 	while (ext_state_map->status_code) {
514*ae37dc57SKory Maincent (Dent Project) 		if (ext_state_map->status_code == status_code) {
515*ae37dc57SKory Maincent (Dent Project) 			c33_ext_state_info->c33_pse_ext_state = ext_state_map->pse_ext_state;
516*ae37dc57SKory Maincent (Dent Project) 			c33_ext_state_info->__c33_pse_ext_substate = ext_state_map->pse_ext_substate;
517*ae37dc57SKory Maincent (Dent Project) 			return;
518*ae37dc57SKory Maincent (Dent Project) 		}
519*ae37dc57SKory Maincent (Dent Project) 		ext_state_map++;
520*ae37dc57SKory Maincent (Dent Project) 	}
521*ae37dc57SKory Maincent (Dent Project) }
522*ae37dc57SKory Maincent (Dent Project) 
5239a993845SKory Maincent (Dent Project) static int pd692x0_ethtool_get_status(struct pse_controller_dev *pcdev,
5249a993845SKory Maincent (Dent Project) 				      unsigned long id,
5259a993845SKory Maincent (Dent Project) 				      struct netlink_ext_ack *extack,
5269a993845SKory Maincent (Dent Project) 				      struct pse_control_status *status)
5279a993845SKory Maincent (Dent Project) {
5289a993845SKory Maincent (Dent Project) 	struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
5299a993845SKory Maincent (Dent Project) 	struct pd692x0_msg msg, buf = {0};
530*ae37dc57SKory Maincent (Dent Project) 	u32 class;
5319a993845SKory Maincent (Dent Project) 	int ret;
5329a993845SKory Maincent (Dent Project) 
5339a993845SKory Maincent (Dent Project) 	ret = pd692x0_fw_unavailable(priv);
5349a993845SKory Maincent (Dent Project) 	if (ret)
5359a993845SKory Maincent (Dent Project) 		return ret;
5369a993845SKory Maincent (Dent Project) 
5379a993845SKory Maincent (Dent Project) 	msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_STATUS];
5389a993845SKory Maincent (Dent Project) 	msg.sub[2] = id;
5399a993845SKory Maincent (Dent Project) 	ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
5409a993845SKory Maincent (Dent Project) 	if (ret < 0)
5419a993845SKory Maincent (Dent Project) 		return ret;
5429a993845SKory Maincent (Dent Project) 
5439a993845SKory Maincent (Dent Project) 	/* Compare Port Status (Communication Protocol Document par. 7.1) */
5449a993845SKory Maincent (Dent Project) 	if ((buf.sub[0] & 0xf0) == 0x80 || (buf.sub[0] & 0xf0) == 0x90)
5459a993845SKory Maincent (Dent Project) 		status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING;
5469a993845SKory Maincent (Dent Project) 	else if (buf.sub[0] == 0x1b || buf.sub[0] == 0x22)
5479a993845SKory Maincent (Dent Project) 		status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_SEARCHING;
5489a993845SKory Maincent (Dent Project) 	else if (buf.sub[0] == 0x12)
5499a993845SKory Maincent (Dent Project) 		status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_FAULT;
5509a993845SKory Maincent (Dent Project) 	else
5519a993845SKory Maincent (Dent Project) 		status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED;
5529a993845SKory Maincent (Dent Project) 
5539a993845SKory Maincent (Dent Project) 	if (buf.sub[1])
5549a993845SKory Maincent (Dent Project) 		status->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED;
5559a993845SKory Maincent (Dent Project) 	else
5569a993845SKory Maincent (Dent Project) 		status->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED;
5579a993845SKory Maincent (Dent Project) 
5589a993845SKory Maincent (Dent Project) 	priv->admin_state[id] = status->c33_admin_state;
5599a993845SKory Maincent (Dent Project) 
560*ae37dc57SKory Maincent (Dent Project) 	pd692x0_get_ext_state(&status->c33_ext_state_info, buf.sub[0]);
561*ae37dc57SKory Maincent (Dent Project) 
562*ae37dc57SKory Maincent (Dent Project) 	status->c33_actual_pw = (buf.data[0] << 4 | buf.data[1]) * 100;
563*ae37dc57SKory Maincent (Dent Project) 
564*ae37dc57SKory Maincent (Dent Project) 	memset(&buf, 0, sizeof(buf));
565*ae37dc57SKory Maincent (Dent Project) 	msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_CLASS];
566*ae37dc57SKory Maincent (Dent Project) 	msg.sub[2] = id;
567*ae37dc57SKory Maincent (Dent Project) 	ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
568*ae37dc57SKory Maincent (Dent Project) 	if (ret < 0)
569*ae37dc57SKory Maincent (Dent Project) 		return ret;
570*ae37dc57SKory Maincent (Dent Project) 
571*ae37dc57SKory Maincent (Dent Project) 	class = buf.data[3] >> 4;
572*ae37dc57SKory Maincent (Dent Project) 	if (class <= 8)
573*ae37dc57SKory Maincent (Dent Project) 		status->c33_pw_class = class;
574*ae37dc57SKory Maincent (Dent Project) 
5759a993845SKory Maincent (Dent Project) 	return 0;
5769a993845SKory Maincent (Dent Project) }
5779a993845SKory Maincent (Dent Project) 
5789a993845SKory Maincent (Dent Project) static struct pd692x0_msg_ver pd692x0_get_sw_version(struct pd692x0_priv *priv)
5799a993845SKory Maincent (Dent Project) {
5809a993845SKory Maincent (Dent Project) 	struct device *dev = &priv->client->dev;
5819a993845SKory Maincent (Dent Project) 	struct pd692x0_msg msg, buf = {0};
5829a993845SKory Maincent (Dent Project) 	struct pd692x0_msg_ver ver = {0};
5839a993845SKory Maincent (Dent Project) 	int ret;
5849a993845SKory Maincent (Dent Project) 
5859a993845SKory Maincent (Dent Project) 	msg = pd692x0_msg_template_list[PD692X0_MSG_GET_SW_VER];
5869a993845SKory Maincent (Dent Project) 	ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
5879a993845SKory Maincent (Dent Project) 	if (ret < 0) {
5889a993845SKory Maincent (Dent Project) 		dev_err(dev, "Failed to get PSE version (%pe)\n", ERR_PTR(ret));
5899a993845SKory Maincent (Dent Project) 		return ver;
5909a993845SKory Maincent (Dent Project) 	}
5919a993845SKory Maincent (Dent Project) 
5929a993845SKory Maincent (Dent Project) 	/* Extract version from the message */
5939a993845SKory Maincent (Dent Project) 	ver.prod = buf.sub[2];
5949a993845SKory Maincent (Dent Project) 	ver.maj_sw_ver = (buf.data[0] << 8 | buf.data[1]) / 100;
5959a993845SKory Maincent (Dent Project) 	ver.min_sw_ver = ((buf.data[0] << 8 | buf.data[1]) / 10) % 10;
5969a993845SKory Maincent (Dent Project) 	ver.pa_sw_ver = (buf.data[0] << 8 | buf.data[1]) % 10;
5979a993845SKory Maincent (Dent Project) 	ver.param = buf.data[2];
5989a993845SKory Maincent (Dent Project) 	ver.build = buf.data[3];
5999a993845SKory Maincent (Dent Project) 
6009a993845SKory Maincent (Dent Project) 	return ver;
6019a993845SKory Maincent (Dent Project) }
6029a993845SKory Maincent (Dent Project) 
6039a993845SKory Maincent (Dent Project) struct pd692x0_manager {
6049a993845SKory Maincent (Dent Project) 	struct device_node *port_node[PD692X0_MAX_MANAGER_PORTS];
6059a993845SKory Maincent (Dent Project) 	int nports;
6069a993845SKory Maincent (Dent Project) };
6079a993845SKory Maincent (Dent Project) 
6089a993845SKory Maincent (Dent Project) struct pd692x0_matrix {
6099a993845SKory Maincent (Dent Project) 	u8 hw_port_a;
6109a993845SKory Maincent (Dent Project) 	u8 hw_port_b;
6119a993845SKory Maincent (Dent Project) };
6129a993845SKory Maincent (Dent Project) 
6139a993845SKory Maincent (Dent Project) static int
6149a993845SKory Maincent (Dent Project) pd692x0_of_get_ports_manager(struct pd692x0_priv *priv,
6159a993845SKory Maincent (Dent Project) 			     struct pd692x0_manager *manager,
6169a993845SKory Maincent (Dent Project) 			     struct device_node *np)
6179a993845SKory Maincent (Dent Project) {
6189a993845SKory Maincent (Dent Project) 	struct device_node *node;
6199a993845SKory Maincent (Dent Project) 	int ret, nports, i;
6209a993845SKory Maincent (Dent Project) 
6219a993845SKory Maincent (Dent Project) 	nports = 0;
6229a993845SKory Maincent (Dent Project) 	for_each_child_of_node(np, node) {
6239a993845SKory Maincent (Dent Project) 		u32 port;
6249a993845SKory Maincent (Dent Project) 
6259a993845SKory Maincent (Dent Project) 		if (!of_node_name_eq(node, "port"))
6269a993845SKory Maincent (Dent Project) 			continue;
6279a993845SKory Maincent (Dent Project) 
6289a993845SKory Maincent (Dent Project) 		ret = of_property_read_u32(node, "reg", &port);
6299a993845SKory Maincent (Dent Project) 		if (ret)
6309a993845SKory Maincent (Dent Project) 			goto out;
6319a993845SKory Maincent (Dent Project) 
6329a993845SKory Maincent (Dent Project) 		if (port >= PD692X0_MAX_MANAGER_PORTS || port != nports) {
6339a993845SKory Maincent (Dent Project) 			dev_err(&priv->client->dev,
6349a993845SKory Maincent (Dent Project) 				"wrong number or order of manager ports (%d)\n",
6359a993845SKory Maincent (Dent Project) 				port);
6369a993845SKory Maincent (Dent Project) 			ret = -EINVAL;
6379a993845SKory Maincent (Dent Project) 			goto out;
6389a993845SKory Maincent (Dent Project) 		}
6399a993845SKory Maincent (Dent Project) 
6409a993845SKory Maincent (Dent Project) 		of_node_get(node);
6419a993845SKory Maincent (Dent Project) 		manager->port_node[port] = node;
6429a993845SKory Maincent (Dent Project) 		nports++;
6439a993845SKory Maincent (Dent Project) 	}
6449a993845SKory Maincent (Dent Project) 
6459a993845SKory Maincent (Dent Project) 	manager->nports = nports;
6469a993845SKory Maincent (Dent Project) 	return 0;
6479a993845SKory Maincent (Dent Project) 
6489a993845SKory Maincent (Dent Project) out:
6499a993845SKory Maincent (Dent Project) 	for (i = 0; i < nports; i++) {
6509a993845SKory Maincent (Dent Project) 		of_node_put(manager->port_node[i]);
6519a993845SKory Maincent (Dent Project) 		manager->port_node[i] = NULL;
6529a993845SKory Maincent (Dent Project) 	}
6539a993845SKory Maincent (Dent Project) 	of_node_put(node);
6549a993845SKory Maincent (Dent Project) 	return ret;
6559a993845SKory Maincent (Dent Project) }
6569a993845SKory Maincent (Dent Project) 
6579a993845SKory Maincent (Dent Project) static int
6589a993845SKory Maincent (Dent Project) pd692x0_of_get_managers(struct pd692x0_priv *priv,
6599a993845SKory Maincent (Dent Project) 			struct pd692x0_manager manager[PD692X0_MAX_MANAGERS])
6609a993845SKory Maincent (Dent Project) {
6619a993845SKory Maincent (Dent Project) 	struct device_node *managers_node, *node;
6629a993845SKory Maincent (Dent Project) 	int ret, nmanagers, i, j;
6639a993845SKory Maincent (Dent Project) 
6649a993845SKory Maincent (Dent Project) 	if (!priv->np)
6659a993845SKory Maincent (Dent Project) 		return -EINVAL;
6669a993845SKory Maincent (Dent Project) 
6679a993845SKory Maincent (Dent Project) 	nmanagers = 0;
6689a993845SKory Maincent (Dent Project) 	managers_node = of_get_child_by_name(priv->np, "managers");
6699a993845SKory Maincent (Dent Project) 	if (!managers_node)
6709a993845SKory Maincent (Dent Project) 		return -EINVAL;
6719a993845SKory Maincent (Dent Project) 
6729a993845SKory Maincent (Dent Project) 	for_each_child_of_node(managers_node, node) {
6739a993845SKory Maincent (Dent Project) 		u32 manager_id;
6749a993845SKory Maincent (Dent Project) 
6759a993845SKory Maincent (Dent Project) 		if (!of_node_name_eq(node, "manager"))
6769a993845SKory Maincent (Dent Project) 			continue;
6779a993845SKory Maincent (Dent Project) 
6789a993845SKory Maincent (Dent Project) 		ret = of_property_read_u32(node, "reg", &manager_id);
6799a993845SKory Maincent (Dent Project) 		if (ret)
6809a993845SKory Maincent (Dent Project) 			goto out;
6819a993845SKory Maincent (Dent Project) 
6829a993845SKory Maincent (Dent Project) 		if (manager_id >= PD692X0_MAX_MANAGERS ||
6839a993845SKory Maincent (Dent Project) 		    manager_id != nmanagers) {
6849a993845SKory Maincent (Dent Project) 			dev_err(&priv->client->dev,
6859a993845SKory Maincent (Dent Project) 				"wrong number or order of managers (%d)\n",
6869a993845SKory Maincent (Dent Project) 				manager_id);
6879a993845SKory Maincent (Dent Project) 			ret = -EINVAL;
6889a993845SKory Maincent (Dent Project) 			goto out;
6899a993845SKory Maincent (Dent Project) 		}
6909a993845SKory Maincent (Dent Project) 
6919a993845SKory Maincent (Dent Project) 		ret = pd692x0_of_get_ports_manager(priv, &manager[manager_id],
6929a993845SKory Maincent (Dent Project) 						   node);
6939a993845SKory Maincent (Dent Project) 		if (ret)
6949a993845SKory Maincent (Dent Project) 			goto out;
6959a993845SKory Maincent (Dent Project) 
6969a993845SKory Maincent (Dent Project) 		nmanagers++;
6979a993845SKory Maincent (Dent Project) 	}
6989a993845SKory Maincent (Dent Project) 
6999a993845SKory Maincent (Dent Project) 	of_node_put(managers_node);
7009a993845SKory Maincent (Dent Project) 	return nmanagers;
7019a993845SKory Maincent (Dent Project) 
7029a993845SKory Maincent (Dent Project) out:
7039a993845SKory Maincent (Dent Project) 	for (i = 0; i < nmanagers; i++) {
7049a993845SKory Maincent (Dent Project) 		for (j = 0; j < manager[i].nports; j++) {
7059a993845SKory Maincent (Dent Project) 			of_node_put(manager[i].port_node[j]);
7069a993845SKory Maincent (Dent Project) 			manager[i].port_node[j] = NULL;
7079a993845SKory Maincent (Dent Project) 		}
7089a993845SKory Maincent (Dent Project) 	}
7099a993845SKory Maincent (Dent Project) 
7109a993845SKory Maincent (Dent Project) 	of_node_put(node);
7119a993845SKory Maincent (Dent Project) 	of_node_put(managers_node);
7129a993845SKory Maincent (Dent Project) 	return ret;
7139a993845SKory Maincent (Dent Project) }
7149a993845SKory Maincent (Dent Project) 
7159a993845SKory Maincent (Dent Project) static int
7169a993845SKory Maincent (Dent Project) pd692x0_set_port_matrix(const struct pse_pi_pairset *pairset,
7179a993845SKory Maincent (Dent Project) 			const struct pd692x0_manager *manager,
7189a993845SKory Maincent (Dent Project) 			int nmanagers, struct pd692x0_matrix *port_matrix)
7199a993845SKory Maincent (Dent Project) {
7209a993845SKory Maincent (Dent Project) 	int i, j, port_cnt;
7219a993845SKory Maincent (Dent Project) 	bool found = false;
7229a993845SKory Maincent (Dent Project) 
7239a993845SKory Maincent (Dent Project) 	if (!pairset->np)
7249a993845SKory Maincent (Dent Project) 		return 0;
7259a993845SKory Maincent (Dent Project) 
7269a993845SKory Maincent (Dent Project) 	/* Look on every managers */
7279a993845SKory Maincent (Dent Project) 	port_cnt = 0;
7289a993845SKory Maincent (Dent Project) 	for (i = 0; i < nmanagers; i++) {
7299a993845SKory Maincent (Dent Project) 		/* Look on every ports of the manager */
7309a993845SKory Maincent (Dent Project) 		for (j = 0; j < manager[i].nports; j++) {
7319a993845SKory Maincent (Dent Project) 			if (pairset->np == manager[i].port_node[j]) {
7329a993845SKory Maincent (Dent Project) 				found = true;
7339a993845SKory Maincent (Dent Project) 				break;
7349a993845SKory Maincent (Dent Project) 			}
7359a993845SKory Maincent (Dent Project) 		}
7369a993845SKory Maincent (Dent Project) 		port_cnt += j;
7379a993845SKory Maincent (Dent Project) 
7389a993845SKory Maincent (Dent Project) 		if (found)
7399a993845SKory Maincent (Dent Project) 			break;
7409a993845SKory Maincent (Dent Project) 	}
7419a993845SKory Maincent (Dent Project) 
7429a993845SKory Maincent (Dent Project) 	if (!found)
7439a993845SKory Maincent (Dent Project) 		return -ENODEV;
7449a993845SKory Maincent (Dent Project) 
7459a993845SKory Maincent (Dent Project) 	if (pairset->pinout == ALTERNATIVE_A)
7469a993845SKory Maincent (Dent Project) 		port_matrix->hw_port_a = port_cnt;
7479a993845SKory Maincent (Dent Project) 	else if (pairset->pinout == ALTERNATIVE_B)
7489a993845SKory Maincent (Dent Project) 		port_matrix->hw_port_b = port_cnt;
7499a993845SKory Maincent (Dent Project) 
7509a993845SKory Maincent (Dent Project) 	return 0;
7519a993845SKory Maincent (Dent Project) }
7529a993845SKory Maincent (Dent Project) 
7539a993845SKory Maincent (Dent Project) static int
7549a993845SKory Maincent (Dent Project) pd692x0_set_ports_matrix(struct pd692x0_priv *priv,
7559a993845SKory Maincent (Dent Project) 			 const struct pd692x0_manager *manager,
7569a993845SKory Maincent (Dent Project) 			 int nmanagers,
7579a993845SKory Maincent (Dent Project) 			 struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS])
7589a993845SKory Maincent (Dent Project) {
7599a993845SKory Maincent (Dent Project) 	struct pse_controller_dev *pcdev = &priv->pcdev;
7609a993845SKory Maincent (Dent Project) 	int i, ret;
7619a993845SKory Maincent (Dent Project) 
7629a993845SKory Maincent (Dent Project) 	/* Init Matrix */
7639a993845SKory Maincent (Dent Project) 	for (i = 0; i < PD692X0_MAX_PIS; i++) {
7649a993845SKory Maincent (Dent Project) 		port_matrix[i].hw_port_a = 0xff;
7659a993845SKory Maincent (Dent Project) 		port_matrix[i].hw_port_b = 0xff;
7669a993845SKory Maincent (Dent Project) 	}
7679a993845SKory Maincent (Dent Project) 
7689a993845SKory Maincent (Dent Project) 	/* Update with values for every PSE PIs */
7699a993845SKory Maincent (Dent Project) 	for (i = 0; i < pcdev->nr_lines; i++) {
7709a993845SKory Maincent (Dent Project) 		ret = pd692x0_set_port_matrix(&pcdev->pi[i].pairset[0],
7719a993845SKory Maincent (Dent Project) 					      manager, nmanagers,
7729a993845SKory Maincent (Dent Project) 					      &port_matrix[i]);
7739a993845SKory Maincent (Dent Project) 		if (ret) {
7749a993845SKory Maincent (Dent Project) 			dev_err(&priv->client->dev,
7759a993845SKory Maincent (Dent Project) 				"unable to configure pi %d pairset 0", i);
7769a993845SKory Maincent (Dent Project) 			return ret;
7779a993845SKory Maincent (Dent Project) 		}
7789a993845SKory Maincent (Dent Project) 
7799a993845SKory Maincent (Dent Project) 		ret = pd692x0_set_port_matrix(&pcdev->pi[i].pairset[1],
7809a993845SKory Maincent (Dent Project) 					      manager, nmanagers,
7819a993845SKory Maincent (Dent Project) 					      &port_matrix[i]);
7829a993845SKory Maincent (Dent Project) 		if (ret) {
7839a993845SKory Maincent (Dent Project) 			dev_err(&priv->client->dev,
7849a993845SKory Maincent (Dent Project) 				"unable to configure pi %d pairset 1", i);
7859a993845SKory Maincent (Dent Project) 			return ret;
7869a993845SKory Maincent (Dent Project) 		}
7879a993845SKory Maincent (Dent Project) 	}
7889a993845SKory Maincent (Dent Project) 
7899a993845SKory Maincent (Dent Project) 	return 0;
7909a993845SKory Maincent (Dent Project) }
7919a993845SKory Maincent (Dent Project) 
7929a993845SKory Maincent (Dent Project) static int
7939a993845SKory Maincent (Dent Project) pd692x0_write_ports_matrix(struct pd692x0_priv *priv,
7949a993845SKory Maincent (Dent Project) 			   const struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS])
7959a993845SKory Maincent (Dent Project) {
7969a993845SKory Maincent (Dent Project) 	struct pd692x0_msg msg, buf;
7979a993845SKory Maincent (Dent Project) 	int ret, i;
7989a993845SKory Maincent (Dent Project) 
7999a993845SKory Maincent (Dent Project) 	/* Write temporary Matrix */
8009a993845SKory Maincent (Dent Project) 	msg = pd692x0_msg_template_list[PD692X0_MSG_SET_TMP_PORT_MATRIX];
8019a993845SKory Maincent (Dent Project) 	for (i = 0; i < PD692X0_MAX_PIS; i++) {
8029a993845SKory Maincent (Dent Project) 		msg.sub[2] = i;
8039a993845SKory Maincent (Dent Project) 		msg.data[0] = port_matrix[i].hw_port_b;
8049a993845SKory Maincent (Dent Project) 		msg.data[1] = port_matrix[i].hw_port_a;
8059a993845SKory Maincent (Dent Project) 
8069a993845SKory Maincent (Dent Project) 		ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
8079a993845SKory Maincent (Dent Project) 		if (ret < 0)
8089a993845SKory Maincent (Dent Project) 			return ret;
8099a993845SKory Maincent (Dent Project) 	}
8109a993845SKory Maincent (Dent Project) 
8119a993845SKory Maincent (Dent Project) 	/* Program Matrix */
8129a993845SKory Maincent (Dent Project) 	msg = pd692x0_msg_template_list[PD692X0_MSG_PRG_PORT_MATRIX];
8139a993845SKory Maincent (Dent Project) 	ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
8149a993845SKory Maincent (Dent Project) 	if (ret < 0)
8159a993845SKory Maincent (Dent Project) 		return ret;
8169a993845SKory Maincent (Dent Project) 
8179a993845SKory Maincent (Dent Project) 	return 0;
8189a993845SKory Maincent (Dent Project) }
8199a993845SKory Maincent (Dent Project) 
8209a993845SKory Maincent (Dent Project) static int pd692x0_setup_pi_matrix(struct pse_controller_dev *pcdev)
8219a993845SKory Maincent (Dent Project) {
8229a993845SKory Maincent (Dent Project) 	struct pd692x0_manager manager[PD692X0_MAX_MANAGERS] = {0};
8239a993845SKory Maincent (Dent Project) 	struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
8249a993845SKory Maincent (Dent Project) 	struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS];
8259a993845SKory Maincent (Dent Project) 	int ret, i, j, nmanagers;
8269a993845SKory Maincent (Dent Project) 
8279a993845SKory Maincent (Dent Project) 	/* Should we flash the port matrix */
8289a993845SKory Maincent (Dent Project) 	if (priv->fw_state != PD692X0_FW_OK &&
8299a993845SKory Maincent (Dent Project) 	    priv->fw_state != PD692X0_FW_COMPLETE)
8309a993845SKory Maincent (Dent Project) 		return 0;
8319a993845SKory Maincent (Dent Project) 
8329a993845SKory Maincent (Dent Project) 	ret = pd692x0_of_get_managers(priv, manager);
8339a993845SKory Maincent (Dent Project) 	if (ret < 0)
8349a993845SKory Maincent (Dent Project) 		return ret;
8359a993845SKory Maincent (Dent Project) 
8369a993845SKory Maincent (Dent Project) 	nmanagers = ret;
8379a993845SKory Maincent (Dent Project) 	ret = pd692x0_set_ports_matrix(priv, manager, nmanagers, port_matrix);
8389a993845SKory Maincent (Dent Project) 	if (ret)
8399a993845SKory Maincent (Dent Project) 		goto out;
8409a993845SKory Maincent (Dent Project) 
8419a993845SKory Maincent (Dent Project) 	ret = pd692x0_write_ports_matrix(priv, port_matrix);
8429a993845SKory Maincent (Dent Project) 	if (ret)
8439a993845SKory Maincent (Dent Project) 		goto out;
8449a993845SKory Maincent (Dent Project) 
8459a993845SKory Maincent (Dent Project) out:
8469a993845SKory Maincent (Dent Project) 	for (i = 0; i < nmanagers; i++) {
8479a993845SKory Maincent (Dent Project) 		for (j = 0; j < manager[i].nports; j++)
8489a993845SKory Maincent (Dent Project) 			of_node_put(manager[i].port_node[j]);
8499a993845SKory Maincent (Dent Project) 	}
8509a993845SKory Maincent (Dent Project) 	return ret;
8519a993845SKory Maincent (Dent Project) }
8529a993845SKory Maincent (Dent Project) 
8539a993845SKory Maincent (Dent Project) static const struct pse_controller_ops pd692x0_ops = {
8549a993845SKory Maincent (Dent Project) 	.setup_pi_matrix = pd692x0_setup_pi_matrix,
8559a993845SKory Maincent (Dent Project) 	.ethtool_get_status = pd692x0_ethtool_get_status,
8569a993845SKory Maincent (Dent Project) 	.pi_enable = pd692x0_pi_enable,
8579a993845SKory Maincent (Dent Project) 	.pi_disable = pd692x0_pi_disable,
8589a993845SKory Maincent (Dent Project) 	.pi_is_enabled = pd692x0_pi_is_enabled,
8599a993845SKory Maincent (Dent Project) };
8609a993845SKory Maincent (Dent Project) 
8619a993845SKory Maincent (Dent Project) #define PD692X0_FW_LINE_MAX_SZ 0xff
8629a993845SKory Maincent (Dent Project) static int pd692x0_fw_get_next_line(const u8 *data,
8639a993845SKory Maincent (Dent Project) 				    char *line, size_t size)
8649a993845SKory Maincent (Dent Project) {
8659a993845SKory Maincent (Dent Project) 	size_t line_size;
8669a993845SKory Maincent (Dent Project) 	int i;
8679a993845SKory Maincent (Dent Project) 
8689a993845SKory Maincent (Dent Project) 	line_size = min_t(size_t, size, PD692X0_FW_LINE_MAX_SZ);
8699a993845SKory Maincent (Dent Project) 
8709a993845SKory Maincent (Dent Project) 	memset(line, 0, PD692X0_FW_LINE_MAX_SZ);
8719a993845SKory Maincent (Dent Project) 	for (i = 0; i < line_size - 1; i++) {
8729a993845SKory Maincent (Dent Project) 		if (*data == '\r' && *(data + 1) == '\n') {
8739a993845SKory Maincent (Dent Project) 			line[i] = '\r';
8749a993845SKory Maincent (Dent Project) 			line[i + 1] = '\n';
8759a993845SKory Maincent (Dent Project) 			return i + 2;
8769a993845SKory Maincent (Dent Project) 		}
8779a993845SKory Maincent (Dent Project) 		line[i] = *data;
8789a993845SKory Maincent (Dent Project) 		data++;
8799a993845SKory Maincent (Dent Project) 	}
8809a993845SKory Maincent (Dent Project) 
8819a993845SKory Maincent (Dent Project) 	return -EIO;
8829a993845SKory Maincent (Dent Project) }
8839a993845SKory Maincent (Dent Project) 
8849a993845SKory Maincent (Dent Project) static enum fw_upload_err
8859a993845SKory Maincent (Dent Project) pd692x0_fw_recv_resp(const struct i2c_client *client, unsigned long ms_timeout,
8869a993845SKory Maincent (Dent Project) 		     const char *msg_ok, unsigned int msg_size)
8879a993845SKory Maincent (Dent Project) {
8889a993845SKory Maincent (Dent Project) 	/* Maximum controller response size */
8899a993845SKory Maincent (Dent Project) 	char fw_msg_buf[5] = {0};
8909a993845SKory Maincent (Dent Project) 	unsigned long timeout;
8919a993845SKory Maincent (Dent Project) 	int ret;
8929a993845SKory Maincent (Dent Project) 
8939a993845SKory Maincent (Dent Project) 	if (msg_size > sizeof(fw_msg_buf))
8949a993845SKory Maincent (Dent Project) 		return FW_UPLOAD_ERR_RW_ERROR;
8959a993845SKory Maincent (Dent Project) 
8969a993845SKory Maincent (Dent Project) 	/* Read until we get something */
8979a993845SKory Maincent (Dent Project) 	timeout = msecs_to_jiffies(ms_timeout) + jiffies;
8989a993845SKory Maincent (Dent Project) 	while (true) {
8999a993845SKory Maincent (Dent Project) 		if (time_is_before_jiffies(timeout))
9009a993845SKory Maincent (Dent Project) 			return FW_UPLOAD_ERR_TIMEOUT;
9019a993845SKory Maincent (Dent Project) 
9029a993845SKory Maincent (Dent Project) 		ret = i2c_master_recv(client, fw_msg_buf, 1);
9039a993845SKory Maincent (Dent Project) 		if (ret < 0 || *fw_msg_buf == 0) {
9049a993845SKory Maincent (Dent Project) 			usleep_range(1000, 2000);
9059a993845SKory Maincent (Dent Project) 			continue;
9069a993845SKory Maincent (Dent Project) 		} else {
9079a993845SKory Maincent (Dent Project) 			break;
9089a993845SKory Maincent (Dent Project) 		}
9099a993845SKory Maincent (Dent Project) 	}
9109a993845SKory Maincent (Dent Project) 
9119a993845SKory Maincent (Dent Project) 	/* Read remaining characters */
9129a993845SKory Maincent (Dent Project) 	ret = i2c_master_recv(client, fw_msg_buf + 1, msg_size - 1);
9139a993845SKory Maincent (Dent Project) 	if (strncmp(fw_msg_buf, msg_ok, msg_size)) {
9149a993845SKory Maincent (Dent Project) 		dev_err(&client->dev,
9159a993845SKory Maincent (Dent Project) 			"Wrong FW download process answer (%*pE)\n",
9169a993845SKory Maincent (Dent Project) 			msg_size, fw_msg_buf);
9179a993845SKory Maincent (Dent Project) 		return FW_UPLOAD_ERR_HW_ERROR;
9189a993845SKory Maincent (Dent Project) 	}
9199a993845SKory Maincent (Dent Project) 
9209a993845SKory Maincent (Dent Project) 	return FW_UPLOAD_ERR_NONE;
9219a993845SKory Maincent (Dent Project) }
9229a993845SKory Maincent (Dent Project) 
9239a993845SKory Maincent (Dent Project) static int pd692x0_fw_write_line(const struct i2c_client *client,
9249a993845SKory Maincent (Dent Project) 				 const char line[PD692X0_FW_LINE_MAX_SZ],
9259a993845SKory Maincent (Dent Project) 				 const bool last_line)
9269a993845SKory Maincent (Dent Project) {
9279a993845SKory Maincent (Dent Project) 	int ret;
9289a993845SKory Maincent (Dent Project) 
9299a993845SKory Maincent (Dent Project) 	while (*line != 0) {
9309a993845SKory Maincent (Dent Project) 		ret = i2c_master_send(client, line, 1);
9319a993845SKory Maincent (Dent Project) 		if (ret < 0)
9329a993845SKory Maincent (Dent Project) 			return FW_UPLOAD_ERR_RW_ERROR;
9339a993845SKory Maincent (Dent Project) 		line++;
9349a993845SKory Maincent (Dent Project) 	}
9359a993845SKory Maincent (Dent Project) 
9369a993845SKory Maincent (Dent Project) 	if (last_line) {
9379a993845SKory Maincent (Dent Project) 		ret = pd692x0_fw_recv_resp(client, 100, "TP\r\n",
9389a993845SKory Maincent (Dent Project) 					   sizeof("TP\r\n") - 1);
9399a993845SKory Maincent (Dent Project) 		if (ret)
9409a993845SKory Maincent (Dent Project) 			return ret;
9419a993845SKory Maincent (Dent Project) 	} else {
9429a993845SKory Maincent (Dent Project) 		ret = pd692x0_fw_recv_resp(client, 100, "T*\r\n",
9439a993845SKory Maincent (Dent Project) 					   sizeof("T*\r\n") - 1);
9449a993845SKory Maincent (Dent Project) 		if (ret)
9459a993845SKory Maincent (Dent Project) 			return ret;
9469a993845SKory Maincent (Dent Project) 	}
9479a993845SKory Maincent (Dent Project) 
9489a993845SKory Maincent (Dent Project) 	return FW_UPLOAD_ERR_NONE;
9499a993845SKory Maincent (Dent Project) }
9509a993845SKory Maincent (Dent Project) 
9519a993845SKory Maincent (Dent Project) static enum fw_upload_err pd692x0_fw_reset(const struct i2c_client *client)
9529a993845SKory Maincent (Dent Project) {
9539a993845SKory Maincent (Dent Project) 	const struct pd692x0_msg zero = {0};
9549a993845SKory Maincent (Dent Project) 	struct pd692x0_msg buf = {0};
9559a993845SKory Maincent (Dent Project) 	unsigned long timeout;
9569a993845SKory Maincent (Dent Project) 	char cmd[] = "RST";
9579a993845SKory Maincent (Dent Project) 	int ret;
9589a993845SKory Maincent (Dent Project) 
9599a993845SKory Maincent (Dent Project) 	ret = i2c_master_send(client, cmd, strlen(cmd));
9609a993845SKory Maincent (Dent Project) 	if (ret < 0) {
9619a993845SKory Maincent (Dent Project) 		dev_err(&client->dev,
9629a993845SKory Maincent (Dent Project) 			"Failed to reset the controller (%pe)\n",
9639a993845SKory Maincent (Dent Project) 			ERR_PTR(ret));
9649a993845SKory Maincent (Dent Project) 		return ret;
9659a993845SKory Maincent (Dent Project) 	}
9669a993845SKory Maincent (Dent Project) 
9679a993845SKory Maincent (Dent Project) 	timeout = msecs_to_jiffies(10000) + jiffies;
9689a993845SKory Maincent (Dent Project) 	while (true) {
9699a993845SKory Maincent (Dent Project) 		if (time_is_before_jiffies(timeout))
9709a993845SKory Maincent (Dent Project) 			return FW_UPLOAD_ERR_TIMEOUT;
9719a993845SKory Maincent (Dent Project) 
9729a993845SKory Maincent (Dent Project) 		ret = i2c_master_recv(client, (u8 *)&buf, sizeof(buf));
9739a993845SKory Maincent (Dent Project) 		if (ret < 0 ||
9749a993845SKory Maincent (Dent Project) 		    !memcmp(&buf, &zero, sizeof(buf)))
9759a993845SKory Maincent (Dent Project) 			usleep_range(1000, 2000);
9769a993845SKory Maincent (Dent Project) 		else
9779a993845SKory Maincent (Dent Project) 			break;
9789a993845SKory Maincent (Dent Project) 	}
9799a993845SKory Maincent (Dent Project) 
9809a993845SKory Maincent (Dent Project) 	/* Is the reply a successful report message */
9819a993845SKory Maincent (Dent Project) 	if (buf.key != PD692X0_KEY_TLM || buf.echo != 0xff ||
9829a993845SKory Maincent (Dent Project) 	    buf.sub[0] & 0x01) {
9839a993845SKory Maincent (Dent Project) 		dev_err(&client->dev, "PSE controller error\n");
9849a993845SKory Maincent (Dent Project) 		return FW_UPLOAD_ERR_HW_ERROR;
9859a993845SKory Maincent (Dent Project) 	}
9869a993845SKory Maincent (Dent Project) 
9879a993845SKory Maincent (Dent Project) 	/* Is the firmware operational */
9889a993845SKory Maincent (Dent Project) 	if (buf.sub[0] & 0x02) {
9899a993845SKory Maincent (Dent Project) 		dev_err(&client->dev,
9909a993845SKory Maincent (Dent Project) 			"PSE firmware error. Please update it.\n");
9919a993845SKory Maincent (Dent Project) 		return FW_UPLOAD_ERR_HW_ERROR;
9929a993845SKory Maincent (Dent Project) 	}
9939a993845SKory Maincent (Dent Project) 
9949a993845SKory Maincent (Dent Project) 	return FW_UPLOAD_ERR_NONE;
9959a993845SKory Maincent (Dent Project) }
9969a993845SKory Maincent (Dent Project) 
9979a993845SKory Maincent (Dent Project) static enum fw_upload_err pd692x0_fw_prepare(struct fw_upload *fwl,
9989a993845SKory Maincent (Dent Project) 					     const u8 *data, u32 size)
9999a993845SKory Maincent (Dent Project) {
10009a993845SKory Maincent (Dent Project) 	struct pd692x0_priv *priv = fwl->dd_handle;
10019a993845SKory Maincent (Dent Project) 	const struct i2c_client *client = priv->client;
10029a993845SKory Maincent (Dent Project) 	enum pd692x0_fw_state last_fw_state;
10039a993845SKory Maincent (Dent Project) 	int ret;
10049a993845SKory Maincent (Dent Project) 
10059a993845SKory Maincent (Dent Project) 	priv->cancel_request = false;
10069a993845SKory Maincent (Dent Project) 	last_fw_state = priv->fw_state;
10079a993845SKory Maincent (Dent Project) 
10089a993845SKory Maincent (Dent Project) 	priv->fw_state = PD692X0_FW_PREPARE;
10099a993845SKory Maincent (Dent Project) 
10109a993845SKory Maincent (Dent Project) 	/* Enter program mode */
10119a993845SKory Maincent (Dent Project) 	if (last_fw_state == PD692X0_FW_BROKEN) {
10129a993845SKory Maincent (Dent Project) 		const char *msg = "ENTR";
10139a993845SKory Maincent (Dent Project) 		const char *c;
10149a993845SKory Maincent (Dent Project) 
10159a993845SKory Maincent (Dent Project) 		c = msg;
10169a993845SKory Maincent (Dent Project) 		do {
10179a993845SKory Maincent (Dent Project) 			ret = i2c_master_send(client, c, 1);
10189a993845SKory Maincent (Dent Project) 			if (ret < 0)
10199a993845SKory Maincent (Dent Project) 				return FW_UPLOAD_ERR_RW_ERROR;
10209a993845SKory Maincent (Dent Project) 			if (*(c + 1))
10219a993845SKory Maincent (Dent Project) 				usleep_range(10000, 20000);
10229a993845SKory Maincent (Dent Project) 		} while (*(++c));
10239a993845SKory Maincent (Dent Project) 	} else {
10249a993845SKory Maincent (Dent Project) 		struct pd692x0_msg msg, buf;
10259a993845SKory Maincent (Dent Project) 
10269a993845SKory Maincent (Dent Project) 		msg = pd692x0_msg_template_list[PD692X0_MSG_DOWNLOAD_CMD];
10279a993845SKory Maincent (Dent Project) 		ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
10289a993845SKory Maincent (Dent Project) 		if (ret < 0) {
10299a993845SKory Maincent (Dent Project) 			dev_err(&client->dev,
10309a993845SKory Maincent (Dent Project) 				"Failed to enter programming mode (%pe)\n",
10319a993845SKory Maincent (Dent Project) 				ERR_PTR(ret));
10329a993845SKory Maincent (Dent Project) 			return FW_UPLOAD_ERR_RW_ERROR;
10339a993845SKory Maincent (Dent Project) 		}
10349a993845SKory Maincent (Dent Project) 	}
10359a993845SKory Maincent (Dent Project) 
10369a993845SKory Maincent (Dent Project) 	ret = pd692x0_fw_recv_resp(client, 100, "TPE\r\n", sizeof("TPE\r\n") - 1);
10379a993845SKory Maincent (Dent Project) 	if (ret)
10389a993845SKory Maincent (Dent Project) 		goto err_out;
10399a993845SKory Maincent (Dent Project) 
10409a993845SKory Maincent (Dent Project) 	if (priv->cancel_request) {
10419a993845SKory Maincent (Dent Project) 		ret = FW_UPLOAD_ERR_CANCELED;
10429a993845SKory Maincent (Dent Project) 		goto err_out;
10439a993845SKory Maincent (Dent Project) 	}
10449a993845SKory Maincent (Dent Project) 
10459a993845SKory Maincent (Dent Project) 	return FW_UPLOAD_ERR_NONE;
10469a993845SKory Maincent (Dent Project) 
10479a993845SKory Maincent (Dent Project) err_out:
10489a993845SKory Maincent (Dent Project) 	pd692x0_fw_reset(priv->client);
10499a993845SKory Maincent (Dent Project) 	priv->fw_state = last_fw_state;
10509a993845SKory Maincent (Dent Project) 	return ret;
10519a993845SKory Maincent (Dent Project) }
10529a993845SKory Maincent (Dent Project) 
10539a993845SKory Maincent (Dent Project) static enum fw_upload_err pd692x0_fw_write(struct fw_upload *fwl,
10549a993845SKory Maincent (Dent Project) 					   const u8 *data, u32 offset,
10559a993845SKory Maincent (Dent Project) 					   u32 size, u32 *written)
10569a993845SKory Maincent (Dent Project) {
10579a993845SKory Maincent (Dent Project) 	struct pd692x0_priv *priv = fwl->dd_handle;
10589a993845SKory Maincent (Dent Project) 	char line[PD692X0_FW_LINE_MAX_SZ];
10599a993845SKory Maincent (Dent Project) 	const struct i2c_client *client;
10609a993845SKory Maincent (Dent Project) 	int ret, i;
10619a993845SKory Maincent (Dent Project) 	char cmd;
10629a993845SKory Maincent (Dent Project) 
10639a993845SKory Maincent (Dent Project) 	client = priv->client;
10649a993845SKory Maincent (Dent Project) 	priv->fw_state = PD692X0_FW_WRITE;
10659a993845SKory Maincent (Dent Project) 
10669a993845SKory Maincent (Dent Project) 	/* Erase */
10679a993845SKory Maincent (Dent Project) 	cmd = 'E';
10689a993845SKory Maincent (Dent Project) 	ret = i2c_master_send(client, &cmd, 1);
10699a993845SKory Maincent (Dent Project) 	if (ret < 0) {
10709a993845SKory Maincent (Dent Project) 		dev_err(&client->dev,
10719a993845SKory Maincent (Dent Project) 			"Failed to boot programming mode (%pe)\n",
10729a993845SKory Maincent (Dent Project) 			ERR_PTR(ret));
10739a993845SKory Maincent (Dent Project) 		return FW_UPLOAD_ERR_RW_ERROR;
10749a993845SKory Maincent (Dent Project) 	}
10759a993845SKory Maincent (Dent Project) 
10769a993845SKory Maincent (Dent Project) 	ret = pd692x0_fw_recv_resp(client, 100, "TOE\r\n", sizeof("TOE\r\n") - 1);
10779a993845SKory Maincent (Dent Project) 	if (ret)
10789a993845SKory Maincent (Dent Project) 		return ret;
10799a993845SKory Maincent (Dent Project) 
10809a993845SKory Maincent (Dent Project) 	ret = pd692x0_fw_recv_resp(client, 5000, "TE\r\n", sizeof("TE\r\n") - 1);
10819a993845SKory Maincent (Dent Project) 	if (ret)
10829a993845SKory Maincent (Dent Project) 		dev_warn(&client->dev,
10839a993845SKory Maincent (Dent Project) 			 "Failed to erase internal memory, however still try to write Firmware\n");
10849a993845SKory Maincent (Dent Project) 
10859a993845SKory Maincent (Dent Project) 	ret = pd692x0_fw_recv_resp(client, 100, "TPE\r\n", sizeof("TPE\r\n") - 1);
10869a993845SKory Maincent (Dent Project) 	if (ret)
10879a993845SKory Maincent (Dent Project) 		dev_warn(&client->dev,
10889a993845SKory Maincent (Dent Project) 			 "Failed to erase internal memory, however still try to write Firmware\n");
10899a993845SKory Maincent (Dent Project) 
10909a993845SKory Maincent (Dent Project) 	if (priv->cancel_request)
10919a993845SKory Maincent (Dent Project) 		return FW_UPLOAD_ERR_CANCELED;
10929a993845SKory Maincent (Dent Project) 
10939a993845SKory Maincent (Dent Project) 	/* Program */
10949a993845SKory Maincent (Dent Project) 	cmd = 'P';
10959a993845SKory Maincent (Dent Project) 	ret = i2c_master_send(client, &cmd, sizeof(char));
10969a993845SKory Maincent (Dent Project) 	if (ret < 0) {
10979a993845SKory Maincent (Dent Project) 		dev_err(&client->dev,
10989a993845SKory Maincent (Dent Project) 			"Failed to boot programming mode (%pe)\n",
10999a993845SKory Maincent (Dent Project) 			ERR_PTR(ret));
11009a993845SKory Maincent (Dent Project) 		return ret;
11019a993845SKory Maincent (Dent Project) 	}
11029a993845SKory Maincent (Dent Project) 
11039a993845SKory Maincent (Dent Project) 	ret = pd692x0_fw_recv_resp(client, 100, "TOP\r\n", sizeof("TOP\r\n") - 1);
11049a993845SKory Maincent (Dent Project) 	if (ret)
11059a993845SKory Maincent (Dent Project) 		return ret;
11069a993845SKory Maincent (Dent Project) 
11079a993845SKory Maincent (Dent Project) 	i = 0;
11089a993845SKory Maincent (Dent Project) 	while (i < size) {
11099a993845SKory Maincent (Dent Project) 		ret = pd692x0_fw_get_next_line(data, line, size - i);
11109a993845SKory Maincent (Dent Project) 		if (ret < 0) {
11119a993845SKory Maincent (Dent Project) 			ret = FW_UPLOAD_ERR_FW_INVALID;
11129a993845SKory Maincent (Dent Project) 			goto err;
11139a993845SKory Maincent (Dent Project) 		}
11149a993845SKory Maincent (Dent Project) 
11159a993845SKory Maincent (Dent Project) 		i += ret;
11169a993845SKory Maincent (Dent Project) 		data += ret;
11179a993845SKory Maincent (Dent Project) 		if (line[0] == 'S' && line[1] == '0') {
11189a993845SKory Maincent (Dent Project) 			continue;
11199a993845SKory Maincent (Dent Project) 		} else if (line[0] == 'S' && line[1] == '7') {
11209a993845SKory Maincent (Dent Project) 			ret = pd692x0_fw_write_line(client, line, true);
11219a993845SKory Maincent (Dent Project) 			if (ret)
11229a993845SKory Maincent (Dent Project) 				goto err;
11239a993845SKory Maincent (Dent Project) 		} else {
11249a993845SKory Maincent (Dent Project) 			ret = pd692x0_fw_write_line(client, line, false);
11259a993845SKory Maincent (Dent Project) 			if (ret)
11269a993845SKory Maincent (Dent Project) 				goto err;
11279a993845SKory Maincent (Dent Project) 		}
11289a993845SKory Maincent (Dent Project) 
11299a993845SKory Maincent (Dent Project) 		if (priv->cancel_request) {
11309a993845SKory Maincent (Dent Project) 			ret = FW_UPLOAD_ERR_CANCELED;
11319a993845SKory Maincent (Dent Project) 			goto err;
11329a993845SKory Maincent (Dent Project) 		}
11339a993845SKory Maincent (Dent Project) 	}
11349a993845SKory Maincent (Dent Project) 	*written = i;
11359a993845SKory Maincent (Dent Project) 
11369a993845SKory Maincent (Dent Project) 	msleep(400);
11379a993845SKory Maincent (Dent Project) 
11389a993845SKory Maincent (Dent Project) 	return FW_UPLOAD_ERR_NONE;
11399a993845SKory Maincent (Dent Project) 
11409a993845SKory Maincent (Dent Project) err:
11419a993845SKory Maincent (Dent Project) 	strscpy_pad(line, "S7\r\n", sizeof(line));
11429a993845SKory Maincent (Dent Project) 	pd692x0_fw_write_line(client, line, true);
11439a993845SKory Maincent (Dent Project) 	return ret;
11449a993845SKory Maincent (Dent Project) }
11459a993845SKory Maincent (Dent Project) 
11469a993845SKory Maincent (Dent Project) static enum fw_upload_err pd692x0_fw_poll_complete(struct fw_upload *fwl)
11479a993845SKory Maincent (Dent Project) {
11489a993845SKory Maincent (Dent Project) 	struct pd692x0_priv *priv = fwl->dd_handle;
11499a993845SKory Maincent (Dent Project) 	const struct i2c_client *client = priv->client;
11509a993845SKory Maincent (Dent Project) 	struct pd692x0_msg_ver ver;
11519a993845SKory Maincent (Dent Project) 	int ret;
11529a993845SKory Maincent (Dent Project) 
11539a993845SKory Maincent (Dent Project) 	priv->fw_state = PD692X0_FW_COMPLETE;
11549a993845SKory Maincent (Dent Project) 
11559a993845SKory Maincent (Dent Project) 	ret = pd692x0_fw_reset(client);
11569a993845SKory Maincent (Dent Project) 	if (ret)
11579a993845SKory Maincent (Dent Project) 		return ret;
11589a993845SKory Maincent (Dent Project) 
11599a993845SKory Maincent (Dent Project) 	ver = pd692x0_get_sw_version(priv);
11609a993845SKory Maincent (Dent Project) 	if (ver.maj_sw_ver < PD692X0_FW_MAJ_VER) {
11619a993845SKory Maincent (Dent Project) 		dev_err(&client->dev,
11629a993845SKory Maincent (Dent Project) 			"Too old firmware version. Please update it\n");
11639a993845SKory Maincent (Dent Project) 		priv->fw_state = PD692X0_FW_NEED_UPDATE;
11649a993845SKory Maincent (Dent Project) 		return FW_UPLOAD_ERR_FW_INVALID;
11659a993845SKory Maincent (Dent Project) 	}
11669a993845SKory Maincent (Dent Project) 
11679a993845SKory Maincent (Dent Project) 	ret = pd692x0_setup_pi_matrix(&priv->pcdev);
11689a993845SKory Maincent (Dent Project) 	if (ret < 0) {
11699a993845SKory Maincent (Dent Project) 		dev_err(&client->dev, "Error configuring ports matrix (%pe)\n",
11709a993845SKory Maincent (Dent Project) 			ERR_PTR(ret));
11719a993845SKory Maincent (Dent Project) 		priv->fw_state = PD692X0_FW_NEED_UPDATE;
11729a993845SKory Maincent (Dent Project) 		return FW_UPLOAD_ERR_HW_ERROR;
11739a993845SKory Maincent (Dent Project) 	}
11749a993845SKory Maincent (Dent Project) 
11759a993845SKory Maincent (Dent Project) 	priv->fw_state = PD692X0_FW_OK;
11769a993845SKory Maincent (Dent Project) 	return FW_UPLOAD_ERR_NONE;
11779a993845SKory Maincent (Dent Project) }
11789a993845SKory Maincent (Dent Project) 
11799a993845SKory Maincent (Dent Project) static void pd692x0_fw_cancel(struct fw_upload *fwl)
11809a993845SKory Maincent (Dent Project) {
11819a993845SKory Maincent (Dent Project) 	struct pd692x0_priv *priv = fwl->dd_handle;
11829a993845SKory Maincent (Dent Project) 
11839a993845SKory Maincent (Dent Project) 	priv->cancel_request = true;
11849a993845SKory Maincent (Dent Project) }
11859a993845SKory Maincent (Dent Project) 
11869a993845SKory Maincent (Dent Project) static void pd692x0_fw_cleanup(struct fw_upload *fwl)
11879a993845SKory Maincent (Dent Project) {
11889a993845SKory Maincent (Dent Project) 	struct pd692x0_priv *priv = fwl->dd_handle;
11899a993845SKory Maincent (Dent Project) 
11909a993845SKory Maincent (Dent Project) 	switch (priv->fw_state) {
11919a993845SKory Maincent (Dent Project) 	case PD692X0_FW_WRITE:
11929a993845SKory Maincent (Dent Project) 		pd692x0_fw_reset(priv->client);
11939a993845SKory Maincent (Dent Project) 		fallthrough;
11949a993845SKory Maincent (Dent Project) 	case PD692X0_FW_COMPLETE:
11959a993845SKory Maincent (Dent Project) 		priv->fw_state = PD692X0_FW_BROKEN;
11969a993845SKory Maincent (Dent Project) 		break;
11979a993845SKory Maincent (Dent Project) 	default:
11989a993845SKory Maincent (Dent Project) 		break;
11999a993845SKory Maincent (Dent Project) 	}
12009a993845SKory Maincent (Dent Project) }
12019a993845SKory Maincent (Dent Project) 
12029a993845SKory Maincent (Dent Project) static const struct fw_upload_ops pd692x0_fw_ops = {
12039a993845SKory Maincent (Dent Project) 	.prepare = pd692x0_fw_prepare,
12049a993845SKory Maincent (Dent Project) 	.write = pd692x0_fw_write,
12059a993845SKory Maincent (Dent Project) 	.poll_complete = pd692x0_fw_poll_complete,
12069a993845SKory Maincent (Dent Project) 	.cancel = pd692x0_fw_cancel,
12079a993845SKory Maincent (Dent Project) 	.cleanup = pd692x0_fw_cleanup,
12089a993845SKory Maincent (Dent Project) };
12099a993845SKory Maincent (Dent Project) 
12109a993845SKory Maincent (Dent Project) static int pd692x0_i2c_probe(struct i2c_client *client)
12119a993845SKory Maincent (Dent Project) {
12129a993845SKory Maincent (Dent Project) 	struct pd692x0_msg msg, buf = {0}, zero = {0};
12139a993845SKory Maincent (Dent Project) 	struct device *dev = &client->dev;
12149a993845SKory Maincent (Dent Project) 	struct pd692x0_msg_ver ver;
12159a993845SKory Maincent (Dent Project) 	struct pd692x0_priv *priv;
12169a993845SKory Maincent (Dent Project) 	struct fw_upload *fwl;
12179a993845SKory Maincent (Dent Project) 	int ret;
12189a993845SKory Maincent (Dent Project) 
12199a993845SKory Maincent (Dent Project) 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
12209a993845SKory Maincent (Dent Project) 		dev_err(dev, "i2c check functionality failed\n");
12219a993845SKory Maincent (Dent Project) 		return -ENXIO;
12229a993845SKory Maincent (Dent Project) 	}
12239a993845SKory Maincent (Dent Project) 
12249a993845SKory Maincent (Dent Project) 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
12259a993845SKory Maincent (Dent Project) 	if (!priv)
12269a993845SKory Maincent (Dent Project) 		return -ENOMEM;
12279a993845SKory Maincent (Dent Project) 
12289a993845SKory Maincent (Dent Project) 	priv->client = client;
12299a993845SKory Maincent (Dent Project) 	i2c_set_clientdata(client, priv);
12309a993845SKory Maincent (Dent Project) 
12319a993845SKory Maincent (Dent Project) 	ret = i2c_master_recv(client, (u8 *)&buf, sizeof(buf));
12329a993845SKory Maincent (Dent Project) 	if (ret != sizeof(buf)) {
12339a993845SKory Maincent (Dent Project) 		dev_err(dev, "Failed to get device status\n");
12349a993845SKory Maincent (Dent Project) 		return -EIO;
12359a993845SKory Maincent (Dent Project) 	}
12369a993845SKory Maincent (Dent Project) 
12379a993845SKory Maincent (Dent Project) 	/* Probe has been already run and the status dumped */
12389a993845SKory Maincent (Dent Project) 	if (!memcmp(&buf, &zero, sizeof(buf))) {
12399a993845SKory Maincent (Dent Project) 		/* Ask again the controller status */
12409a993845SKory Maincent (Dent Project) 		msg = pd692x0_msg_template_list[PD692X0_MSG_GET_SYS_STATUS];
12419a993845SKory Maincent (Dent Project) 		ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
12429a993845SKory Maincent (Dent Project) 		if (ret < 0) {
12439a993845SKory Maincent (Dent Project) 			dev_err(dev, "Failed to get device status\n");
12449a993845SKory Maincent (Dent Project) 			return ret;
12459a993845SKory Maincent (Dent Project) 		}
12469a993845SKory Maincent (Dent Project) 	}
12479a993845SKory Maincent (Dent Project) 
12489a993845SKory Maincent (Dent Project) 	if (buf.key != 0x03 || buf.sub[0] & 0x01) {
12499a993845SKory Maincent (Dent Project) 		dev_err(dev, "PSE controller error\n");
12509a993845SKory Maincent (Dent Project) 		return -EIO;
12519a993845SKory Maincent (Dent Project) 	}
12529a993845SKory Maincent (Dent Project) 	if (buf.sub[0] & 0x02) {
12539a993845SKory Maincent (Dent Project) 		dev_err(dev, "PSE firmware error. Please update it.\n");
12549a993845SKory Maincent (Dent Project) 		priv->fw_state = PD692X0_FW_BROKEN;
12559a993845SKory Maincent (Dent Project) 	} else {
12569a993845SKory Maincent (Dent Project) 		ver = pd692x0_get_sw_version(priv);
12579a993845SKory Maincent (Dent Project) 		dev_info(&client->dev, "Software version %d.%02d.%d.%d\n",
12589a993845SKory Maincent (Dent Project) 			 ver.prod, ver.maj_sw_ver, ver.min_sw_ver,
12599a993845SKory Maincent (Dent Project) 			 ver.pa_sw_ver);
12609a993845SKory Maincent (Dent Project) 
12619a993845SKory Maincent (Dent Project) 		if (ver.maj_sw_ver < PD692X0_FW_MAJ_VER) {
12629a993845SKory Maincent (Dent Project) 			dev_err(dev, "Too old firmware version. Please update it\n");
12639a993845SKory Maincent (Dent Project) 			priv->fw_state = PD692X0_FW_NEED_UPDATE;
12649a993845SKory Maincent (Dent Project) 		} else {
12659a993845SKory Maincent (Dent Project) 			priv->fw_state = PD692X0_FW_OK;
12669a993845SKory Maincent (Dent Project) 		}
12679a993845SKory Maincent (Dent Project) 	}
12689a993845SKory Maincent (Dent Project) 
12699a993845SKory Maincent (Dent Project) 	priv->np = dev->of_node;
12709a993845SKory Maincent (Dent Project) 	priv->pcdev.nr_lines = PD692X0_MAX_PIS;
12719a993845SKory Maincent (Dent Project) 	priv->pcdev.owner = THIS_MODULE;
12729a993845SKory Maincent (Dent Project) 	priv->pcdev.ops = &pd692x0_ops;
12739a993845SKory Maincent (Dent Project) 	priv->pcdev.dev = dev;
12749a993845SKory Maincent (Dent Project) 	priv->pcdev.types = ETHTOOL_PSE_C33;
12759a993845SKory Maincent (Dent Project) 	ret = devm_pse_controller_register(dev, &priv->pcdev);
12769a993845SKory Maincent (Dent Project) 	if (ret)
12779a993845SKory Maincent (Dent Project) 		return dev_err_probe(dev, ret,
12789a993845SKory Maincent (Dent Project) 				     "failed to register PSE controller\n");
12799a993845SKory Maincent (Dent Project) 
12809a993845SKory Maincent (Dent Project) 	fwl = firmware_upload_register(THIS_MODULE, dev, dev_name(dev),
12819a993845SKory Maincent (Dent Project) 				       &pd692x0_fw_ops, priv);
12829a993845SKory Maincent (Dent Project) 	if (IS_ERR(fwl))
12839a993845SKory Maincent (Dent Project) 		return dev_err_probe(dev, PTR_ERR(fwl),
12849a993845SKory Maincent (Dent Project) 				     "failed to register to the Firmware Upload API\n");
12859a993845SKory Maincent (Dent Project) 	priv->fwl = fwl;
12869a993845SKory Maincent (Dent Project) 
12879a993845SKory Maincent (Dent Project) 	return 0;
12889a993845SKory Maincent (Dent Project) }
12899a993845SKory Maincent (Dent Project) 
12909a993845SKory Maincent (Dent Project) static void pd692x0_i2c_remove(struct i2c_client *client)
12919a993845SKory Maincent (Dent Project) {
12929a993845SKory Maincent (Dent Project) 	struct pd692x0_priv *priv = i2c_get_clientdata(client);
12939a993845SKory Maincent (Dent Project) 
12949a993845SKory Maincent (Dent Project) 	firmware_upload_unregister(priv->fwl);
12959a993845SKory Maincent (Dent Project) }
12969a993845SKory Maincent (Dent Project) 
12979a993845SKory Maincent (Dent Project) static const struct i2c_device_id pd692x0_id[] = {
1298a6a6a980SUwe Kleine-König 	{ PD692X0_PSE_NAME },
1299a6a6a980SUwe Kleine-König 	{ }
13009a993845SKory Maincent (Dent Project) };
13019a993845SKory Maincent (Dent Project) MODULE_DEVICE_TABLE(i2c, pd692x0_id);
13029a993845SKory Maincent (Dent Project) 
13039a993845SKory Maincent (Dent Project) static const struct of_device_id pd692x0_of_match[] = {
13049a993845SKory Maincent (Dent Project) 	{ .compatible = "microchip,pd69200", },
13059a993845SKory Maincent (Dent Project) 	{ .compatible = "microchip,pd69210", },
13069a993845SKory Maincent (Dent Project) 	{ .compatible = "microchip,pd69220", },
13079a993845SKory Maincent (Dent Project) 	{ },
13089a993845SKory Maincent (Dent Project) };
13099a993845SKory Maincent (Dent Project) MODULE_DEVICE_TABLE(of, pd692x0_of_match);
13109a993845SKory Maincent (Dent Project) 
13119a993845SKory Maincent (Dent Project) static struct i2c_driver pd692x0_driver = {
13129a993845SKory Maincent (Dent Project) 	.probe		= pd692x0_i2c_probe,
13139a993845SKory Maincent (Dent Project) 	.remove		= pd692x0_i2c_remove,
13149a993845SKory Maincent (Dent Project) 	.id_table	= pd692x0_id,
13159a993845SKory Maincent (Dent Project) 	.driver		= {
13169a993845SKory Maincent (Dent Project) 		.name		= PD692X0_PSE_NAME,
13179a993845SKory Maincent (Dent Project) 		.of_match_table = pd692x0_of_match,
13189a993845SKory Maincent (Dent Project) 	},
13199a993845SKory Maincent (Dent Project) };
13209a993845SKory Maincent (Dent Project) module_i2c_driver(pd692x0_driver);
13219a993845SKory Maincent (Dent Project) 
13229a993845SKory Maincent (Dent Project) MODULE_AUTHOR("Kory Maincent <kory.maincent@bootlin.com>");
13239a993845SKory Maincent (Dent Project) MODULE_DESCRIPTION("Microchip PD692x0 PoE PSE Controller driver");
13249a993845SKory Maincent (Dent Project) MODULE_LICENSE("GPL");
1325