1*9a993845SKory Maincent (Dent Project) // SPDX-License-Identifier: GPL-2.0-only 2*9a993845SKory Maincent (Dent Project) /* 3*9a993845SKory Maincent (Dent Project) * Driver for the Microchip PD692X0 PoE PSE Controller driver (I2C bus) 4*9a993845SKory Maincent (Dent Project) * 5*9a993845SKory Maincent (Dent Project) * Copyright (c) 2023 Bootlin, Kory Maincent <kory.maincent@bootlin.com> 6*9a993845SKory Maincent (Dent Project) */ 7*9a993845SKory Maincent (Dent Project) 8*9a993845SKory Maincent (Dent Project) #include <linux/delay.h> 9*9a993845SKory Maincent (Dent Project) #include <linux/firmware.h> 10*9a993845SKory Maincent (Dent Project) #include <linux/i2c.h> 11*9a993845SKory Maincent (Dent Project) #include <linux/module.h> 12*9a993845SKory Maincent (Dent Project) #include <linux/of.h> 13*9a993845SKory Maincent (Dent Project) #include <linux/platform_device.h> 14*9a993845SKory Maincent (Dent Project) #include <linux/pse-pd/pse.h> 15*9a993845SKory Maincent (Dent Project) 16*9a993845SKory Maincent (Dent Project) #define PD692X0_PSE_NAME "pd692x0_pse" 17*9a993845SKory Maincent (Dent Project) 18*9a993845SKory Maincent (Dent Project) #define PD692X0_MAX_PIS 48 19*9a993845SKory Maincent (Dent Project) #define PD692X0_MAX_MANAGERS 12 20*9a993845SKory Maincent (Dent Project) #define PD692X0_MAX_MANAGER_PORTS 8 21*9a993845SKory Maincent (Dent Project) #define PD692X0_MAX_HW_PORTS (PD692X0_MAX_MANAGERS * PD692X0_MAX_MANAGER_PORTS) 22*9a993845SKory Maincent (Dent Project) 23*9a993845SKory Maincent (Dent Project) #define PD69200_BT_PROD_VER 24 24*9a993845SKory Maincent (Dent Project) #define PD69210_BT_PROD_VER 26 25*9a993845SKory Maincent (Dent Project) #define PD69220_BT_PROD_VER 29 26*9a993845SKory Maincent (Dent Project) 27*9a993845SKory Maincent (Dent Project) #define PD692X0_FW_MAJ_VER 3 28*9a993845SKory Maincent (Dent Project) #define PD692X0_FW_MIN_VER 5 29*9a993845SKory Maincent (Dent Project) #define PD692X0_FW_PATCH_VER 5 30*9a993845SKory Maincent (Dent Project) 31*9a993845SKory Maincent (Dent Project) enum pd692x0_fw_state { 32*9a993845SKory Maincent (Dent Project) PD692X0_FW_UNKNOWN, 33*9a993845SKory Maincent (Dent Project) PD692X0_FW_OK, 34*9a993845SKory Maincent (Dent Project) PD692X0_FW_BROKEN, 35*9a993845SKory Maincent (Dent Project) PD692X0_FW_NEED_UPDATE, 36*9a993845SKory Maincent (Dent Project) PD692X0_FW_PREPARE, 37*9a993845SKory Maincent (Dent Project) PD692X0_FW_WRITE, 38*9a993845SKory Maincent (Dent Project) PD692X0_FW_COMPLETE, 39*9a993845SKory Maincent (Dent Project) }; 40*9a993845SKory Maincent (Dent Project) 41*9a993845SKory Maincent (Dent Project) struct pd692x0_msg { 42*9a993845SKory Maincent (Dent Project) u8 key; 43*9a993845SKory Maincent (Dent Project) u8 echo; 44*9a993845SKory Maincent (Dent Project) u8 sub[3]; 45*9a993845SKory Maincent (Dent Project) u8 data[8]; 46*9a993845SKory Maincent (Dent Project) __be16 chksum; 47*9a993845SKory Maincent (Dent Project) } __packed; 48*9a993845SKory Maincent (Dent Project) 49*9a993845SKory Maincent (Dent Project) struct pd692x0_msg_ver { 50*9a993845SKory Maincent (Dent Project) u8 prod; 51*9a993845SKory Maincent (Dent Project) u8 maj_sw_ver; 52*9a993845SKory Maincent (Dent Project) u8 min_sw_ver; 53*9a993845SKory Maincent (Dent Project) u8 pa_sw_ver; 54*9a993845SKory Maincent (Dent Project) u8 param; 55*9a993845SKory Maincent (Dent Project) u8 build; 56*9a993845SKory Maincent (Dent Project) }; 57*9a993845SKory Maincent (Dent Project) 58*9a993845SKory Maincent (Dent Project) enum { 59*9a993845SKory Maincent (Dent Project) PD692X0_KEY_CMD, 60*9a993845SKory Maincent (Dent Project) PD692X0_KEY_PRG, 61*9a993845SKory Maincent (Dent Project) PD692X0_KEY_REQ, 62*9a993845SKory Maincent (Dent Project) PD692X0_KEY_TLM, 63*9a993845SKory Maincent (Dent Project) PD692X0_KEY_TEST, 64*9a993845SKory Maincent (Dent Project) PD692X0_KEY_REPORT = 0x52 65*9a993845SKory Maincent (Dent Project) }; 66*9a993845SKory Maincent (Dent Project) 67*9a993845SKory Maincent (Dent Project) enum { 68*9a993845SKory Maincent (Dent Project) PD692X0_MSG_RESET, 69*9a993845SKory Maincent (Dent Project) PD692X0_MSG_GET_SYS_STATUS, 70*9a993845SKory Maincent (Dent Project) PD692X0_MSG_GET_SW_VER, 71*9a993845SKory Maincent (Dent Project) PD692X0_MSG_SET_TMP_PORT_MATRIX, 72*9a993845SKory Maincent (Dent Project) PD692X0_MSG_PRG_PORT_MATRIX, 73*9a993845SKory Maincent (Dent Project) PD692X0_MSG_SET_PORT_PARAM, 74*9a993845SKory Maincent (Dent Project) PD692X0_MSG_GET_PORT_STATUS, 75*9a993845SKory Maincent (Dent Project) PD692X0_MSG_DOWNLOAD_CMD, 76*9a993845SKory Maincent (Dent Project) 77*9a993845SKory Maincent (Dent Project) /* add new message above here */ 78*9a993845SKory Maincent (Dent Project) PD692X0_MSG_CNT 79*9a993845SKory Maincent (Dent Project) }; 80*9a993845SKory Maincent (Dent Project) 81*9a993845SKory Maincent (Dent Project) struct pd692x0_priv { 82*9a993845SKory Maincent (Dent Project) struct i2c_client *client; 83*9a993845SKory Maincent (Dent Project) struct pse_controller_dev pcdev; 84*9a993845SKory Maincent (Dent Project) struct device_node *np; 85*9a993845SKory Maincent (Dent Project) 86*9a993845SKory Maincent (Dent Project) enum pd692x0_fw_state fw_state; 87*9a993845SKory Maincent (Dent Project) struct fw_upload *fwl; 88*9a993845SKory Maincent (Dent Project) bool cancel_request; 89*9a993845SKory Maincent (Dent Project) 90*9a993845SKory Maincent (Dent Project) u8 msg_id; 91*9a993845SKory Maincent (Dent Project) bool last_cmd_key; 92*9a993845SKory Maincent (Dent Project) unsigned long last_cmd_key_time; 93*9a993845SKory Maincent (Dent Project) 94*9a993845SKory Maincent (Dent Project) enum ethtool_c33_pse_admin_state admin_state[PD692X0_MAX_PIS]; 95*9a993845SKory Maincent (Dent Project) }; 96*9a993845SKory Maincent (Dent Project) 97*9a993845SKory Maincent (Dent Project) /* Template list of communication messages. The non-null bytes defined here 98*9a993845SKory Maincent (Dent Project) * constitute the fixed portion of the messages. The remaining bytes will 99*9a993845SKory Maincent (Dent Project) * be configured later within the functions. Refer to the "PD692x0 BT Serial 100*9a993845SKory Maincent (Dent Project) * Communication Protocol User Guide" for comprehensive details on messages 101*9a993845SKory Maincent (Dent Project) * content. 102*9a993845SKory Maincent (Dent Project) */ 103*9a993845SKory Maincent (Dent Project) static const struct pd692x0_msg pd692x0_msg_template_list[PD692X0_MSG_CNT] = { 104*9a993845SKory Maincent (Dent Project) [PD692X0_MSG_RESET] = { 105*9a993845SKory Maincent (Dent Project) .key = PD692X0_KEY_CMD, 106*9a993845SKory Maincent (Dent Project) .sub = {0x07, 0x55, 0x00}, 107*9a993845SKory Maincent (Dent Project) .data = {0x55, 0x00, 0x55, 0x4e, 108*9a993845SKory Maincent (Dent Project) 0x4e, 0x4e, 0x4e, 0x4e}, 109*9a993845SKory Maincent (Dent Project) }, 110*9a993845SKory Maincent (Dent Project) [PD692X0_MSG_GET_SYS_STATUS] = { 111*9a993845SKory Maincent (Dent Project) .key = PD692X0_KEY_REQ, 112*9a993845SKory Maincent (Dent Project) .sub = {0x07, 0xd0, 0x4e}, 113*9a993845SKory Maincent (Dent Project) .data = {0x4e, 0x4e, 0x4e, 0x4e, 114*9a993845SKory Maincent (Dent Project) 0x4e, 0x4e, 0x4e, 0x4e}, 115*9a993845SKory Maincent (Dent Project) }, 116*9a993845SKory Maincent (Dent Project) [PD692X0_MSG_GET_SW_VER] = { 117*9a993845SKory Maincent (Dent Project) .key = PD692X0_KEY_REQ, 118*9a993845SKory Maincent (Dent Project) .sub = {0x07, 0x1e, 0x21}, 119*9a993845SKory Maincent (Dent Project) .data = {0x4e, 0x4e, 0x4e, 0x4e, 120*9a993845SKory Maincent (Dent Project) 0x4e, 0x4e, 0x4e, 0x4e}, 121*9a993845SKory Maincent (Dent Project) }, 122*9a993845SKory Maincent (Dent Project) [PD692X0_MSG_SET_TMP_PORT_MATRIX] = { 123*9a993845SKory Maincent (Dent Project) .key = PD692X0_KEY_CMD, 124*9a993845SKory Maincent (Dent Project) .sub = {0x05, 0x43}, 125*9a993845SKory Maincent (Dent Project) .data = { 0, 0x4e, 0x4e, 0x4e, 126*9a993845SKory Maincent (Dent Project) 0x4e, 0x4e, 0x4e, 0x4e}, 127*9a993845SKory Maincent (Dent Project) }, 128*9a993845SKory Maincent (Dent Project) [PD692X0_MSG_PRG_PORT_MATRIX] = { 129*9a993845SKory Maincent (Dent Project) .key = PD692X0_KEY_CMD, 130*9a993845SKory Maincent (Dent Project) .sub = {0x07, 0x43, 0x4e}, 131*9a993845SKory Maincent (Dent Project) .data = {0x4e, 0x4e, 0x4e, 0x4e, 132*9a993845SKory Maincent (Dent Project) 0x4e, 0x4e, 0x4e, 0x4e}, 133*9a993845SKory Maincent (Dent Project) }, 134*9a993845SKory Maincent (Dent Project) [PD692X0_MSG_SET_PORT_PARAM] = { 135*9a993845SKory Maincent (Dent Project) .key = PD692X0_KEY_CMD, 136*9a993845SKory Maincent (Dent Project) .sub = {0x05, 0xc0}, 137*9a993845SKory Maincent (Dent Project) .data = { 0, 0xff, 0xff, 0xff, 138*9a993845SKory Maincent (Dent Project) 0x4e, 0x4e, 0x4e, 0x4e}, 139*9a993845SKory Maincent (Dent Project) }, 140*9a993845SKory Maincent (Dent Project) [PD692X0_MSG_GET_PORT_STATUS] = { 141*9a993845SKory Maincent (Dent Project) .key = PD692X0_KEY_REQ, 142*9a993845SKory Maincent (Dent Project) .sub = {0x05, 0xc1}, 143*9a993845SKory Maincent (Dent Project) .data = {0x4e, 0x4e, 0x4e, 0x4e, 144*9a993845SKory Maincent (Dent Project) 0x4e, 0x4e, 0x4e, 0x4e}, 145*9a993845SKory Maincent (Dent Project) }, 146*9a993845SKory Maincent (Dent Project) [PD692X0_MSG_DOWNLOAD_CMD] = { 147*9a993845SKory Maincent (Dent Project) .key = PD692X0_KEY_PRG, 148*9a993845SKory Maincent (Dent Project) .sub = {0xff, 0x99, 0x15}, 149*9a993845SKory Maincent (Dent Project) .data = {0x16, 0x16, 0x99, 0x4e, 150*9a993845SKory Maincent (Dent Project) 0x4e, 0x4e, 0x4e, 0x4e}, 151*9a993845SKory Maincent (Dent Project) }, 152*9a993845SKory Maincent (Dent Project) }; 153*9a993845SKory Maincent (Dent Project) 154*9a993845SKory Maincent (Dent Project) static u8 pd692x0_build_msg(struct pd692x0_msg *msg, u8 echo) 155*9a993845SKory Maincent (Dent Project) { 156*9a993845SKory Maincent (Dent Project) u8 *data = (u8 *)msg; 157*9a993845SKory Maincent (Dent Project) u16 chksum = 0; 158*9a993845SKory Maincent (Dent Project) int i; 159*9a993845SKory Maincent (Dent Project) 160*9a993845SKory Maincent (Dent Project) msg->echo = echo++; 161*9a993845SKory Maincent (Dent Project) if (echo == 0xff) 162*9a993845SKory Maincent (Dent Project) echo = 0; 163*9a993845SKory Maincent (Dent Project) 164*9a993845SKory Maincent (Dent Project) for (i = 0; i < sizeof(*msg) - sizeof(msg->chksum); i++) 165*9a993845SKory Maincent (Dent Project) chksum += data[i]; 166*9a993845SKory Maincent (Dent Project) 167*9a993845SKory Maincent (Dent Project) msg->chksum = cpu_to_be16(chksum); 168*9a993845SKory Maincent (Dent Project) 169*9a993845SKory Maincent (Dent Project) return echo; 170*9a993845SKory Maincent (Dent Project) } 171*9a993845SKory Maincent (Dent Project) 172*9a993845SKory Maincent (Dent Project) static int pd692x0_send_msg(struct pd692x0_priv *priv, struct pd692x0_msg *msg) 173*9a993845SKory Maincent (Dent Project) { 174*9a993845SKory Maincent (Dent Project) const struct i2c_client *client = priv->client; 175*9a993845SKory Maincent (Dent Project) int ret; 176*9a993845SKory Maincent (Dent Project) 177*9a993845SKory Maincent (Dent Project) if (msg->key == PD692X0_KEY_CMD && priv->last_cmd_key) { 178*9a993845SKory Maincent (Dent Project) int cmd_msleep; 179*9a993845SKory Maincent (Dent Project) 180*9a993845SKory Maincent (Dent Project) cmd_msleep = 30 - jiffies_to_msecs(jiffies - priv->last_cmd_key_time); 181*9a993845SKory Maincent (Dent Project) if (cmd_msleep > 0) 182*9a993845SKory Maincent (Dent Project) msleep(cmd_msleep); 183*9a993845SKory Maincent (Dent Project) } 184*9a993845SKory Maincent (Dent Project) 185*9a993845SKory Maincent (Dent Project) /* Add echo and checksum bytes to the message */ 186*9a993845SKory Maincent (Dent Project) priv->msg_id = pd692x0_build_msg(msg, priv->msg_id); 187*9a993845SKory Maincent (Dent Project) 188*9a993845SKory Maincent (Dent Project) ret = i2c_master_send(client, (u8 *)msg, sizeof(*msg)); 189*9a993845SKory Maincent (Dent Project) if (ret != sizeof(*msg)) 190*9a993845SKory Maincent (Dent Project) return -EIO; 191*9a993845SKory Maincent (Dent Project) 192*9a993845SKory Maincent (Dent Project) return 0; 193*9a993845SKory Maincent (Dent Project) } 194*9a993845SKory Maincent (Dent Project) 195*9a993845SKory Maincent (Dent Project) static int pd692x0_reset(struct pd692x0_priv *priv) 196*9a993845SKory Maincent (Dent Project) { 197*9a993845SKory Maincent (Dent Project) const struct i2c_client *client = priv->client; 198*9a993845SKory Maincent (Dent Project) struct pd692x0_msg msg, buf = {0}; 199*9a993845SKory Maincent (Dent Project) int ret; 200*9a993845SKory Maincent (Dent Project) 201*9a993845SKory Maincent (Dent Project) msg = pd692x0_msg_template_list[PD692X0_MSG_RESET]; 202*9a993845SKory Maincent (Dent Project) ret = pd692x0_send_msg(priv, &msg); 203*9a993845SKory Maincent (Dent Project) if (ret) { 204*9a993845SKory Maincent (Dent Project) dev_err(&client->dev, 205*9a993845SKory Maincent (Dent Project) "Failed to reset the controller (%pe)\n", ERR_PTR(ret)); 206*9a993845SKory Maincent (Dent Project) return ret; 207*9a993845SKory Maincent (Dent Project) } 208*9a993845SKory Maincent (Dent Project) 209*9a993845SKory Maincent (Dent Project) msleep(30); 210*9a993845SKory Maincent (Dent Project) 211*9a993845SKory Maincent (Dent Project) ret = i2c_master_recv(client, (u8 *)&buf, sizeof(buf)); 212*9a993845SKory Maincent (Dent Project) if (ret != sizeof(buf)) 213*9a993845SKory Maincent (Dent Project) return ret < 0 ? ret : -EIO; 214*9a993845SKory Maincent (Dent Project) 215*9a993845SKory Maincent (Dent Project) /* Is the reply a successful report message */ 216*9a993845SKory Maincent (Dent Project) if (buf.key != PD692X0_KEY_REPORT || buf.sub[0] || buf.sub[1]) 217*9a993845SKory Maincent (Dent Project) return -EIO; 218*9a993845SKory Maincent (Dent Project) 219*9a993845SKory Maincent (Dent Project) msleep(300); 220*9a993845SKory Maincent (Dent Project) 221*9a993845SKory Maincent (Dent Project) ret = i2c_master_recv(client, (u8 *)&buf, sizeof(buf)); 222*9a993845SKory Maincent (Dent Project) if (ret != sizeof(buf)) 223*9a993845SKory Maincent (Dent Project) return ret < 0 ? ret : -EIO; 224*9a993845SKory Maincent (Dent Project) 225*9a993845SKory Maincent (Dent Project) /* Is the boot status without error */ 226*9a993845SKory Maincent (Dent Project) if (buf.key != 0x03 || buf.echo != 0xff || buf.sub[0] & 0x1) { 227*9a993845SKory Maincent (Dent Project) dev_err(&client->dev, "PSE controller error\n"); 228*9a993845SKory Maincent (Dent Project) return -EIO; 229*9a993845SKory Maincent (Dent Project) } 230*9a993845SKory Maincent (Dent Project) 231*9a993845SKory Maincent (Dent Project) return 0; 232*9a993845SKory Maincent (Dent Project) } 233*9a993845SKory Maincent (Dent Project) 234*9a993845SKory Maincent (Dent Project) static bool pd692x0_try_recv_msg(const struct i2c_client *client, 235*9a993845SKory Maincent (Dent Project) struct pd692x0_msg *msg, 236*9a993845SKory Maincent (Dent Project) struct pd692x0_msg *buf) 237*9a993845SKory Maincent (Dent Project) { 238*9a993845SKory Maincent (Dent Project) /* Wait 30ms before readback as mandated by the protocol */ 239*9a993845SKory Maincent (Dent Project) msleep(30); 240*9a993845SKory Maincent (Dent Project) 241*9a993845SKory Maincent (Dent Project) memset(buf, 0, sizeof(*buf)); 242*9a993845SKory Maincent (Dent Project) i2c_master_recv(client, (u8 *)buf, sizeof(*buf)); 243*9a993845SKory Maincent (Dent Project) if (buf->key) 244*9a993845SKory Maincent (Dent Project) return 0; 245*9a993845SKory Maincent (Dent Project) 246*9a993845SKory Maincent (Dent Project) msleep(100); 247*9a993845SKory Maincent (Dent Project) 248*9a993845SKory Maincent (Dent Project) memset(buf, 0, sizeof(*buf)); 249*9a993845SKory Maincent (Dent Project) i2c_master_recv(client, (u8 *)buf, sizeof(*buf)); 250*9a993845SKory Maincent (Dent Project) if (buf->key) 251*9a993845SKory Maincent (Dent Project) return 0; 252*9a993845SKory Maincent (Dent Project) 253*9a993845SKory Maincent (Dent Project) return 1; 254*9a993845SKory Maincent (Dent Project) } 255*9a993845SKory Maincent (Dent Project) 256*9a993845SKory Maincent (Dent Project) /* Implementation of I2C communication, specifically addressing scenarios 257*9a993845SKory Maincent (Dent Project) * involving communication loss. Refer to the "Synchronization During 258*9a993845SKory Maincent (Dent Project) * Communication Loss" section in the Communication Protocol document for 259*9a993845SKory Maincent (Dent Project) * further details. 260*9a993845SKory Maincent (Dent Project) */ 261*9a993845SKory Maincent (Dent Project) static int pd692x0_recv_msg(struct pd692x0_priv *priv, 262*9a993845SKory Maincent (Dent Project) struct pd692x0_msg *msg, 263*9a993845SKory Maincent (Dent Project) struct pd692x0_msg *buf) 264*9a993845SKory Maincent (Dent Project) { 265*9a993845SKory Maincent (Dent Project) const struct i2c_client *client = priv->client; 266*9a993845SKory Maincent (Dent Project) int ret; 267*9a993845SKory Maincent (Dent Project) 268*9a993845SKory Maincent (Dent Project) ret = pd692x0_try_recv_msg(client, msg, buf); 269*9a993845SKory Maincent (Dent Project) if (!ret) 270*9a993845SKory Maincent (Dent Project) goto out_success; 271*9a993845SKory Maincent (Dent Project) 272*9a993845SKory Maincent (Dent Project) dev_warn(&client->dev, 273*9a993845SKory Maincent (Dent Project) "Communication lost, rtnl is locked until communication is back!"); 274*9a993845SKory Maincent (Dent Project) 275*9a993845SKory Maincent (Dent Project) ret = pd692x0_send_msg(priv, msg); 276*9a993845SKory Maincent (Dent Project) if (ret) 277*9a993845SKory Maincent (Dent Project) return ret; 278*9a993845SKory Maincent (Dent Project) 279*9a993845SKory Maincent (Dent Project) ret = pd692x0_try_recv_msg(client, msg, buf); 280*9a993845SKory Maincent (Dent Project) if (!ret) 281*9a993845SKory Maincent (Dent Project) goto out_success2; 282*9a993845SKory Maincent (Dent Project) 283*9a993845SKory Maincent (Dent Project) msleep(10000); 284*9a993845SKory Maincent (Dent Project) 285*9a993845SKory Maincent (Dent Project) ret = pd692x0_send_msg(priv, msg); 286*9a993845SKory Maincent (Dent Project) if (ret) 287*9a993845SKory Maincent (Dent Project) return ret; 288*9a993845SKory Maincent (Dent Project) 289*9a993845SKory Maincent (Dent Project) ret = pd692x0_try_recv_msg(client, msg, buf); 290*9a993845SKory Maincent (Dent Project) if (!ret) 291*9a993845SKory Maincent (Dent Project) goto out_success2; 292*9a993845SKory Maincent (Dent Project) 293*9a993845SKory Maincent (Dent Project) return pd692x0_reset(priv); 294*9a993845SKory Maincent (Dent Project) 295*9a993845SKory Maincent (Dent Project) out_success2: 296*9a993845SKory Maincent (Dent Project) dev_warn(&client->dev, "Communication is back, rtnl is unlocked!"); 297*9a993845SKory Maincent (Dent Project) out_success: 298*9a993845SKory Maincent (Dent Project) if (msg->key == PD692X0_KEY_CMD) { 299*9a993845SKory Maincent (Dent Project) priv->last_cmd_key = true; 300*9a993845SKory Maincent (Dent Project) priv->last_cmd_key_time = jiffies; 301*9a993845SKory Maincent (Dent Project) } else { 302*9a993845SKory Maincent (Dent Project) priv->last_cmd_key = false; 303*9a993845SKory Maincent (Dent Project) } 304*9a993845SKory Maincent (Dent Project) 305*9a993845SKory Maincent (Dent Project) return 0; 306*9a993845SKory Maincent (Dent Project) } 307*9a993845SKory Maincent (Dent Project) 308*9a993845SKory Maincent (Dent Project) static int pd692x0_sendrecv_msg(struct pd692x0_priv *priv, 309*9a993845SKory Maincent (Dent Project) struct pd692x0_msg *msg, 310*9a993845SKory Maincent (Dent Project) struct pd692x0_msg *buf) 311*9a993845SKory Maincent (Dent Project) { 312*9a993845SKory Maincent (Dent Project) struct device *dev = &priv->client->dev; 313*9a993845SKory Maincent (Dent Project) int ret; 314*9a993845SKory Maincent (Dent Project) 315*9a993845SKory Maincent (Dent Project) ret = pd692x0_send_msg(priv, msg); 316*9a993845SKory Maincent (Dent Project) if (ret) 317*9a993845SKory Maincent (Dent Project) return ret; 318*9a993845SKory Maincent (Dent Project) 319*9a993845SKory Maincent (Dent Project) ret = pd692x0_recv_msg(priv, msg, buf); 320*9a993845SKory Maincent (Dent Project) if (ret) 321*9a993845SKory Maincent (Dent Project) return ret; 322*9a993845SKory Maincent (Dent Project) 323*9a993845SKory Maincent (Dent Project) if (msg->echo != buf->echo) { 324*9a993845SKory Maincent (Dent Project) dev_err(dev, 325*9a993845SKory Maincent (Dent Project) "Wrong match in message ID, expect %d received %d.\n", 326*9a993845SKory Maincent (Dent Project) msg->echo, buf->echo); 327*9a993845SKory Maincent (Dent Project) return -EIO; 328*9a993845SKory Maincent (Dent Project) } 329*9a993845SKory Maincent (Dent Project) 330*9a993845SKory Maincent (Dent Project) /* If the reply is a report message is it successful */ 331*9a993845SKory Maincent (Dent Project) if (buf->key == PD692X0_KEY_REPORT && 332*9a993845SKory Maincent (Dent Project) (buf->sub[0] || buf->sub[1])) { 333*9a993845SKory Maincent (Dent Project) return -EIO; 334*9a993845SKory Maincent (Dent Project) } 335*9a993845SKory Maincent (Dent Project) 336*9a993845SKory Maincent (Dent Project) return 0; 337*9a993845SKory Maincent (Dent Project) } 338*9a993845SKory Maincent (Dent Project) 339*9a993845SKory Maincent (Dent Project) static struct pd692x0_priv *to_pd692x0_priv(struct pse_controller_dev *pcdev) 340*9a993845SKory Maincent (Dent Project) { 341*9a993845SKory Maincent (Dent Project) return container_of(pcdev, struct pd692x0_priv, pcdev); 342*9a993845SKory Maincent (Dent Project) } 343*9a993845SKory Maincent (Dent Project) 344*9a993845SKory Maincent (Dent Project) static int pd692x0_fw_unavailable(struct pd692x0_priv *priv) 345*9a993845SKory Maincent (Dent Project) { 346*9a993845SKory Maincent (Dent Project) switch (priv->fw_state) { 347*9a993845SKory Maincent (Dent Project) case PD692X0_FW_OK: 348*9a993845SKory Maincent (Dent Project) return 0; 349*9a993845SKory Maincent (Dent Project) case PD692X0_FW_PREPARE: 350*9a993845SKory Maincent (Dent Project) case PD692X0_FW_WRITE: 351*9a993845SKory Maincent (Dent Project) case PD692X0_FW_COMPLETE: 352*9a993845SKory Maincent (Dent Project) dev_err(&priv->client->dev, "Firmware update in progress!\n"); 353*9a993845SKory Maincent (Dent Project) return -EBUSY; 354*9a993845SKory Maincent (Dent Project) case PD692X0_FW_BROKEN: 355*9a993845SKory Maincent (Dent Project) case PD692X0_FW_NEED_UPDATE: 356*9a993845SKory Maincent (Dent Project) default: 357*9a993845SKory Maincent (Dent Project) dev_err(&priv->client->dev, 358*9a993845SKory Maincent (Dent Project) "Firmware issue. Please update it!\n"); 359*9a993845SKory Maincent (Dent Project) return -EOPNOTSUPP; 360*9a993845SKory Maincent (Dent Project) } 361*9a993845SKory Maincent (Dent Project) } 362*9a993845SKory Maincent (Dent Project) 363*9a993845SKory Maincent (Dent Project) static int pd692x0_pi_enable(struct pse_controller_dev *pcdev, int id) 364*9a993845SKory Maincent (Dent Project) { 365*9a993845SKory Maincent (Dent Project) struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); 366*9a993845SKory Maincent (Dent Project) struct pd692x0_msg msg, buf = {0}; 367*9a993845SKory Maincent (Dent Project) int ret; 368*9a993845SKory Maincent (Dent Project) 369*9a993845SKory Maincent (Dent Project) ret = pd692x0_fw_unavailable(priv); 370*9a993845SKory Maincent (Dent Project) if (ret) 371*9a993845SKory Maincent (Dent Project) return ret; 372*9a993845SKory Maincent (Dent Project) 373*9a993845SKory Maincent (Dent Project) if (priv->admin_state[id] == ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED) 374*9a993845SKory Maincent (Dent Project) return 0; 375*9a993845SKory Maincent (Dent Project) 376*9a993845SKory Maincent (Dent Project) msg = pd692x0_msg_template_list[PD692X0_MSG_SET_PORT_PARAM]; 377*9a993845SKory Maincent (Dent Project) msg.data[0] = 0x1; 378*9a993845SKory Maincent (Dent Project) msg.sub[2] = id; 379*9a993845SKory Maincent (Dent Project) ret = pd692x0_sendrecv_msg(priv, &msg, &buf); 380*9a993845SKory Maincent (Dent Project) if (ret < 0) 381*9a993845SKory Maincent (Dent Project) return ret; 382*9a993845SKory Maincent (Dent Project) 383*9a993845SKory Maincent (Dent Project) priv->admin_state[id] = ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED; 384*9a993845SKory Maincent (Dent Project) 385*9a993845SKory Maincent (Dent Project) return 0; 386*9a993845SKory Maincent (Dent Project) } 387*9a993845SKory Maincent (Dent Project) 388*9a993845SKory Maincent (Dent Project) static int pd692x0_pi_disable(struct pse_controller_dev *pcdev, int id) 389*9a993845SKory Maincent (Dent Project) { 390*9a993845SKory Maincent (Dent Project) struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); 391*9a993845SKory Maincent (Dent Project) struct pd692x0_msg msg, buf = {0}; 392*9a993845SKory Maincent (Dent Project) int ret; 393*9a993845SKory Maincent (Dent Project) 394*9a993845SKory Maincent (Dent Project) ret = pd692x0_fw_unavailable(priv); 395*9a993845SKory Maincent (Dent Project) if (ret) 396*9a993845SKory Maincent (Dent Project) return ret; 397*9a993845SKory Maincent (Dent Project) 398*9a993845SKory Maincent (Dent Project) if (priv->admin_state[id] == ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED) 399*9a993845SKory Maincent (Dent Project) return 0; 400*9a993845SKory Maincent (Dent Project) 401*9a993845SKory Maincent (Dent Project) msg = pd692x0_msg_template_list[PD692X0_MSG_SET_PORT_PARAM]; 402*9a993845SKory Maincent (Dent Project) msg.data[0] = 0x0; 403*9a993845SKory Maincent (Dent Project) msg.sub[2] = id; 404*9a993845SKory Maincent (Dent Project) ret = pd692x0_sendrecv_msg(priv, &msg, &buf); 405*9a993845SKory Maincent (Dent Project) if (ret < 0) 406*9a993845SKory Maincent (Dent Project) return ret; 407*9a993845SKory Maincent (Dent Project) 408*9a993845SKory Maincent (Dent Project) priv->admin_state[id] = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED; 409*9a993845SKory Maincent (Dent Project) 410*9a993845SKory Maincent (Dent Project) return 0; 411*9a993845SKory Maincent (Dent Project) } 412*9a993845SKory Maincent (Dent Project) 413*9a993845SKory Maincent (Dent Project) static int pd692x0_pi_is_enabled(struct pse_controller_dev *pcdev, int id) 414*9a993845SKory Maincent (Dent Project) { 415*9a993845SKory Maincent (Dent Project) struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); 416*9a993845SKory Maincent (Dent Project) struct pd692x0_msg msg, buf = {0}; 417*9a993845SKory Maincent (Dent Project) int ret; 418*9a993845SKory Maincent (Dent Project) 419*9a993845SKory Maincent (Dent Project) ret = pd692x0_fw_unavailable(priv); 420*9a993845SKory Maincent (Dent Project) if (ret) 421*9a993845SKory Maincent (Dent Project) return ret; 422*9a993845SKory Maincent (Dent Project) 423*9a993845SKory Maincent (Dent Project) msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_STATUS]; 424*9a993845SKory Maincent (Dent Project) msg.sub[2] = id; 425*9a993845SKory Maincent (Dent Project) ret = pd692x0_sendrecv_msg(priv, &msg, &buf); 426*9a993845SKory Maincent (Dent Project) if (ret < 0) 427*9a993845SKory Maincent (Dent Project) return ret; 428*9a993845SKory Maincent (Dent Project) 429*9a993845SKory Maincent (Dent Project) if (buf.sub[1]) { 430*9a993845SKory Maincent (Dent Project) priv->admin_state[id] = ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED; 431*9a993845SKory Maincent (Dent Project) return 1; 432*9a993845SKory Maincent (Dent Project) } else { 433*9a993845SKory Maincent (Dent Project) priv->admin_state[id] = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED; 434*9a993845SKory Maincent (Dent Project) return 0; 435*9a993845SKory Maincent (Dent Project) } 436*9a993845SKory Maincent (Dent Project) } 437*9a993845SKory Maincent (Dent Project) 438*9a993845SKory Maincent (Dent Project) static int pd692x0_ethtool_get_status(struct pse_controller_dev *pcdev, 439*9a993845SKory Maincent (Dent Project) unsigned long id, 440*9a993845SKory Maincent (Dent Project) struct netlink_ext_ack *extack, 441*9a993845SKory Maincent (Dent Project) struct pse_control_status *status) 442*9a993845SKory Maincent (Dent Project) { 443*9a993845SKory Maincent (Dent Project) struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); 444*9a993845SKory Maincent (Dent Project) struct pd692x0_msg msg, buf = {0}; 445*9a993845SKory Maincent (Dent Project) int ret; 446*9a993845SKory Maincent (Dent Project) 447*9a993845SKory Maincent (Dent Project) ret = pd692x0_fw_unavailable(priv); 448*9a993845SKory Maincent (Dent Project) if (ret) 449*9a993845SKory Maincent (Dent Project) return ret; 450*9a993845SKory Maincent (Dent Project) 451*9a993845SKory Maincent (Dent Project) msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_STATUS]; 452*9a993845SKory Maincent (Dent Project) msg.sub[2] = id; 453*9a993845SKory Maincent (Dent Project) ret = pd692x0_sendrecv_msg(priv, &msg, &buf); 454*9a993845SKory Maincent (Dent Project) if (ret < 0) 455*9a993845SKory Maincent (Dent Project) return ret; 456*9a993845SKory Maincent (Dent Project) 457*9a993845SKory Maincent (Dent Project) /* Compare Port Status (Communication Protocol Document par. 7.1) */ 458*9a993845SKory Maincent (Dent Project) if ((buf.sub[0] & 0xf0) == 0x80 || (buf.sub[0] & 0xf0) == 0x90) 459*9a993845SKory Maincent (Dent Project) status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING; 460*9a993845SKory Maincent (Dent Project) else if (buf.sub[0] == 0x1b || buf.sub[0] == 0x22) 461*9a993845SKory Maincent (Dent Project) status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_SEARCHING; 462*9a993845SKory Maincent (Dent Project) else if (buf.sub[0] == 0x12) 463*9a993845SKory Maincent (Dent Project) status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_FAULT; 464*9a993845SKory Maincent (Dent Project) else 465*9a993845SKory Maincent (Dent Project) status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED; 466*9a993845SKory Maincent (Dent Project) 467*9a993845SKory Maincent (Dent Project) if (buf.sub[1]) 468*9a993845SKory Maincent (Dent Project) status->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED; 469*9a993845SKory Maincent (Dent Project) else 470*9a993845SKory Maincent (Dent Project) status->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED; 471*9a993845SKory Maincent (Dent Project) 472*9a993845SKory Maincent (Dent Project) priv->admin_state[id] = status->c33_admin_state; 473*9a993845SKory Maincent (Dent Project) 474*9a993845SKory Maincent (Dent Project) return 0; 475*9a993845SKory Maincent (Dent Project) } 476*9a993845SKory Maincent (Dent Project) 477*9a993845SKory Maincent (Dent Project) static struct pd692x0_msg_ver pd692x0_get_sw_version(struct pd692x0_priv *priv) 478*9a993845SKory Maincent (Dent Project) { 479*9a993845SKory Maincent (Dent Project) struct device *dev = &priv->client->dev; 480*9a993845SKory Maincent (Dent Project) struct pd692x0_msg msg, buf = {0}; 481*9a993845SKory Maincent (Dent Project) struct pd692x0_msg_ver ver = {0}; 482*9a993845SKory Maincent (Dent Project) int ret; 483*9a993845SKory Maincent (Dent Project) 484*9a993845SKory Maincent (Dent Project) msg = pd692x0_msg_template_list[PD692X0_MSG_GET_SW_VER]; 485*9a993845SKory Maincent (Dent Project) ret = pd692x0_sendrecv_msg(priv, &msg, &buf); 486*9a993845SKory Maincent (Dent Project) if (ret < 0) { 487*9a993845SKory Maincent (Dent Project) dev_err(dev, "Failed to get PSE version (%pe)\n", ERR_PTR(ret)); 488*9a993845SKory Maincent (Dent Project) return ver; 489*9a993845SKory Maincent (Dent Project) } 490*9a993845SKory Maincent (Dent Project) 491*9a993845SKory Maincent (Dent Project) /* Extract version from the message */ 492*9a993845SKory Maincent (Dent Project) ver.prod = buf.sub[2]; 493*9a993845SKory Maincent (Dent Project) ver.maj_sw_ver = (buf.data[0] << 8 | buf.data[1]) / 100; 494*9a993845SKory Maincent (Dent Project) ver.min_sw_ver = ((buf.data[0] << 8 | buf.data[1]) / 10) % 10; 495*9a993845SKory Maincent (Dent Project) ver.pa_sw_ver = (buf.data[0] << 8 | buf.data[1]) % 10; 496*9a993845SKory Maincent (Dent Project) ver.param = buf.data[2]; 497*9a993845SKory Maincent (Dent Project) ver.build = buf.data[3]; 498*9a993845SKory Maincent (Dent Project) 499*9a993845SKory Maincent (Dent Project) return ver; 500*9a993845SKory Maincent (Dent Project) } 501*9a993845SKory Maincent (Dent Project) 502*9a993845SKory Maincent (Dent Project) struct pd692x0_manager { 503*9a993845SKory Maincent (Dent Project) struct device_node *port_node[PD692X0_MAX_MANAGER_PORTS]; 504*9a993845SKory Maincent (Dent Project) int nports; 505*9a993845SKory Maincent (Dent Project) }; 506*9a993845SKory Maincent (Dent Project) 507*9a993845SKory Maincent (Dent Project) struct pd692x0_matrix { 508*9a993845SKory Maincent (Dent Project) u8 hw_port_a; 509*9a993845SKory Maincent (Dent Project) u8 hw_port_b; 510*9a993845SKory Maincent (Dent Project) }; 511*9a993845SKory Maincent (Dent Project) 512*9a993845SKory Maincent (Dent Project) static int 513*9a993845SKory Maincent (Dent Project) pd692x0_of_get_ports_manager(struct pd692x0_priv *priv, 514*9a993845SKory Maincent (Dent Project) struct pd692x0_manager *manager, 515*9a993845SKory Maincent (Dent Project) struct device_node *np) 516*9a993845SKory Maincent (Dent Project) { 517*9a993845SKory Maincent (Dent Project) struct device_node *node; 518*9a993845SKory Maincent (Dent Project) int ret, nports, i; 519*9a993845SKory Maincent (Dent Project) 520*9a993845SKory Maincent (Dent Project) nports = 0; 521*9a993845SKory Maincent (Dent Project) for_each_child_of_node(np, node) { 522*9a993845SKory Maincent (Dent Project) u32 port; 523*9a993845SKory Maincent (Dent Project) 524*9a993845SKory Maincent (Dent Project) if (!of_node_name_eq(node, "port")) 525*9a993845SKory Maincent (Dent Project) continue; 526*9a993845SKory Maincent (Dent Project) 527*9a993845SKory Maincent (Dent Project) ret = of_property_read_u32(node, "reg", &port); 528*9a993845SKory Maincent (Dent Project) if (ret) 529*9a993845SKory Maincent (Dent Project) goto out; 530*9a993845SKory Maincent (Dent Project) 531*9a993845SKory Maincent (Dent Project) if (port >= PD692X0_MAX_MANAGER_PORTS || port != nports) { 532*9a993845SKory Maincent (Dent Project) dev_err(&priv->client->dev, 533*9a993845SKory Maincent (Dent Project) "wrong number or order of manager ports (%d)\n", 534*9a993845SKory Maincent (Dent Project) port); 535*9a993845SKory Maincent (Dent Project) ret = -EINVAL; 536*9a993845SKory Maincent (Dent Project) goto out; 537*9a993845SKory Maincent (Dent Project) } 538*9a993845SKory Maincent (Dent Project) 539*9a993845SKory Maincent (Dent Project) of_node_get(node); 540*9a993845SKory Maincent (Dent Project) manager->port_node[port] = node; 541*9a993845SKory Maincent (Dent Project) nports++; 542*9a993845SKory Maincent (Dent Project) } 543*9a993845SKory Maincent (Dent Project) 544*9a993845SKory Maincent (Dent Project) manager->nports = nports; 545*9a993845SKory Maincent (Dent Project) return 0; 546*9a993845SKory Maincent (Dent Project) 547*9a993845SKory Maincent (Dent Project) out: 548*9a993845SKory Maincent (Dent Project) for (i = 0; i < nports; i++) { 549*9a993845SKory Maincent (Dent Project) of_node_put(manager->port_node[i]); 550*9a993845SKory Maincent (Dent Project) manager->port_node[i] = NULL; 551*9a993845SKory Maincent (Dent Project) } 552*9a993845SKory Maincent (Dent Project) of_node_put(node); 553*9a993845SKory Maincent (Dent Project) return ret; 554*9a993845SKory Maincent (Dent Project) } 555*9a993845SKory Maincent (Dent Project) 556*9a993845SKory Maincent (Dent Project) static int 557*9a993845SKory Maincent (Dent Project) pd692x0_of_get_managers(struct pd692x0_priv *priv, 558*9a993845SKory Maincent (Dent Project) struct pd692x0_manager manager[PD692X0_MAX_MANAGERS]) 559*9a993845SKory Maincent (Dent Project) { 560*9a993845SKory Maincent (Dent Project) struct device_node *managers_node, *node; 561*9a993845SKory Maincent (Dent Project) int ret, nmanagers, i, j; 562*9a993845SKory Maincent (Dent Project) 563*9a993845SKory Maincent (Dent Project) if (!priv->np) 564*9a993845SKory Maincent (Dent Project) return -EINVAL; 565*9a993845SKory Maincent (Dent Project) 566*9a993845SKory Maincent (Dent Project) nmanagers = 0; 567*9a993845SKory Maincent (Dent Project) managers_node = of_get_child_by_name(priv->np, "managers"); 568*9a993845SKory Maincent (Dent Project) if (!managers_node) 569*9a993845SKory Maincent (Dent Project) return -EINVAL; 570*9a993845SKory Maincent (Dent Project) 571*9a993845SKory Maincent (Dent Project) for_each_child_of_node(managers_node, node) { 572*9a993845SKory Maincent (Dent Project) u32 manager_id; 573*9a993845SKory Maincent (Dent Project) 574*9a993845SKory Maincent (Dent Project) if (!of_node_name_eq(node, "manager")) 575*9a993845SKory Maincent (Dent Project) continue; 576*9a993845SKory Maincent (Dent Project) 577*9a993845SKory Maincent (Dent Project) ret = of_property_read_u32(node, "reg", &manager_id); 578*9a993845SKory Maincent (Dent Project) if (ret) 579*9a993845SKory Maincent (Dent Project) goto out; 580*9a993845SKory Maincent (Dent Project) 581*9a993845SKory Maincent (Dent Project) if (manager_id >= PD692X0_MAX_MANAGERS || 582*9a993845SKory Maincent (Dent Project) manager_id != nmanagers) { 583*9a993845SKory Maincent (Dent Project) dev_err(&priv->client->dev, 584*9a993845SKory Maincent (Dent Project) "wrong number or order of managers (%d)\n", 585*9a993845SKory Maincent (Dent Project) manager_id); 586*9a993845SKory Maincent (Dent Project) ret = -EINVAL; 587*9a993845SKory Maincent (Dent Project) goto out; 588*9a993845SKory Maincent (Dent Project) } 589*9a993845SKory Maincent (Dent Project) 590*9a993845SKory Maincent (Dent Project) ret = pd692x0_of_get_ports_manager(priv, &manager[manager_id], 591*9a993845SKory Maincent (Dent Project) node); 592*9a993845SKory Maincent (Dent Project) if (ret) 593*9a993845SKory Maincent (Dent Project) goto out; 594*9a993845SKory Maincent (Dent Project) 595*9a993845SKory Maincent (Dent Project) nmanagers++; 596*9a993845SKory Maincent (Dent Project) } 597*9a993845SKory Maincent (Dent Project) 598*9a993845SKory Maincent (Dent Project) of_node_put(managers_node); 599*9a993845SKory Maincent (Dent Project) return nmanagers; 600*9a993845SKory Maincent (Dent Project) 601*9a993845SKory Maincent (Dent Project) out: 602*9a993845SKory Maincent (Dent Project) for (i = 0; i < nmanagers; i++) { 603*9a993845SKory Maincent (Dent Project) for (j = 0; j < manager[i].nports; j++) { 604*9a993845SKory Maincent (Dent Project) of_node_put(manager[i].port_node[j]); 605*9a993845SKory Maincent (Dent Project) manager[i].port_node[j] = NULL; 606*9a993845SKory Maincent (Dent Project) } 607*9a993845SKory Maincent (Dent Project) } 608*9a993845SKory Maincent (Dent Project) 609*9a993845SKory Maincent (Dent Project) of_node_put(node); 610*9a993845SKory Maincent (Dent Project) of_node_put(managers_node); 611*9a993845SKory Maincent (Dent Project) return ret; 612*9a993845SKory Maincent (Dent Project) } 613*9a993845SKory Maincent (Dent Project) 614*9a993845SKory Maincent (Dent Project) static int 615*9a993845SKory Maincent (Dent Project) pd692x0_set_port_matrix(const struct pse_pi_pairset *pairset, 616*9a993845SKory Maincent (Dent Project) const struct pd692x0_manager *manager, 617*9a993845SKory Maincent (Dent Project) int nmanagers, struct pd692x0_matrix *port_matrix) 618*9a993845SKory Maincent (Dent Project) { 619*9a993845SKory Maincent (Dent Project) int i, j, port_cnt; 620*9a993845SKory Maincent (Dent Project) bool found = false; 621*9a993845SKory Maincent (Dent Project) 622*9a993845SKory Maincent (Dent Project) if (!pairset->np) 623*9a993845SKory Maincent (Dent Project) return 0; 624*9a993845SKory Maincent (Dent Project) 625*9a993845SKory Maincent (Dent Project) /* Look on every managers */ 626*9a993845SKory Maincent (Dent Project) port_cnt = 0; 627*9a993845SKory Maincent (Dent Project) for (i = 0; i < nmanagers; i++) { 628*9a993845SKory Maincent (Dent Project) /* Look on every ports of the manager */ 629*9a993845SKory Maincent (Dent Project) for (j = 0; j < manager[i].nports; j++) { 630*9a993845SKory Maincent (Dent Project) if (pairset->np == manager[i].port_node[j]) { 631*9a993845SKory Maincent (Dent Project) found = true; 632*9a993845SKory Maincent (Dent Project) break; 633*9a993845SKory Maincent (Dent Project) } 634*9a993845SKory Maincent (Dent Project) } 635*9a993845SKory Maincent (Dent Project) port_cnt += j; 636*9a993845SKory Maincent (Dent Project) 637*9a993845SKory Maincent (Dent Project) if (found) 638*9a993845SKory Maincent (Dent Project) break; 639*9a993845SKory Maincent (Dent Project) } 640*9a993845SKory Maincent (Dent Project) 641*9a993845SKory Maincent (Dent Project) if (!found) 642*9a993845SKory Maincent (Dent Project) return -ENODEV; 643*9a993845SKory Maincent (Dent Project) 644*9a993845SKory Maincent (Dent Project) if (pairset->pinout == ALTERNATIVE_A) 645*9a993845SKory Maincent (Dent Project) port_matrix->hw_port_a = port_cnt; 646*9a993845SKory Maincent (Dent Project) else if (pairset->pinout == ALTERNATIVE_B) 647*9a993845SKory Maincent (Dent Project) port_matrix->hw_port_b = port_cnt; 648*9a993845SKory Maincent (Dent Project) 649*9a993845SKory Maincent (Dent Project) return 0; 650*9a993845SKory Maincent (Dent Project) } 651*9a993845SKory Maincent (Dent Project) 652*9a993845SKory Maincent (Dent Project) static int 653*9a993845SKory Maincent (Dent Project) pd692x0_set_ports_matrix(struct pd692x0_priv *priv, 654*9a993845SKory Maincent (Dent Project) const struct pd692x0_manager *manager, 655*9a993845SKory Maincent (Dent Project) int nmanagers, 656*9a993845SKory Maincent (Dent Project) struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS]) 657*9a993845SKory Maincent (Dent Project) { 658*9a993845SKory Maincent (Dent Project) struct pse_controller_dev *pcdev = &priv->pcdev; 659*9a993845SKory Maincent (Dent Project) int i, ret; 660*9a993845SKory Maincent (Dent Project) 661*9a993845SKory Maincent (Dent Project) /* Init Matrix */ 662*9a993845SKory Maincent (Dent Project) for (i = 0; i < PD692X0_MAX_PIS; i++) { 663*9a993845SKory Maincent (Dent Project) port_matrix[i].hw_port_a = 0xff; 664*9a993845SKory Maincent (Dent Project) port_matrix[i].hw_port_b = 0xff; 665*9a993845SKory Maincent (Dent Project) } 666*9a993845SKory Maincent (Dent Project) 667*9a993845SKory Maincent (Dent Project) /* Update with values for every PSE PIs */ 668*9a993845SKory Maincent (Dent Project) for (i = 0; i < pcdev->nr_lines; i++) { 669*9a993845SKory Maincent (Dent Project) ret = pd692x0_set_port_matrix(&pcdev->pi[i].pairset[0], 670*9a993845SKory Maincent (Dent Project) manager, nmanagers, 671*9a993845SKory Maincent (Dent Project) &port_matrix[i]); 672*9a993845SKory Maincent (Dent Project) if (ret) { 673*9a993845SKory Maincent (Dent Project) dev_err(&priv->client->dev, 674*9a993845SKory Maincent (Dent Project) "unable to configure pi %d pairset 0", i); 675*9a993845SKory Maincent (Dent Project) return ret; 676*9a993845SKory Maincent (Dent Project) } 677*9a993845SKory Maincent (Dent Project) 678*9a993845SKory Maincent (Dent Project) ret = pd692x0_set_port_matrix(&pcdev->pi[i].pairset[1], 679*9a993845SKory Maincent (Dent Project) manager, nmanagers, 680*9a993845SKory Maincent (Dent Project) &port_matrix[i]); 681*9a993845SKory Maincent (Dent Project) if (ret) { 682*9a993845SKory Maincent (Dent Project) dev_err(&priv->client->dev, 683*9a993845SKory Maincent (Dent Project) "unable to configure pi %d pairset 1", i); 684*9a993845SKory Maincent (Dent Project) return ret; 685*9a993845SKory Maincent (Dent Project) } 686*9a993845SKory Maincent (Dent Project) } 687*9a993845SKory Maincent (Dent Project) 688*9a993845SKory Maincent (Dent Project) return 0; 689*9a993845SKory Maincent (Dent Project) } 690*9a993845SKory Maincent (Dent Project) 691*9a993845SKory Maincent (Dent Project) static int 692*9a993845SKory Maincent (Dent Project) pd692x0_write_ports_matrix(struct pd692x0_priv *priv, 693*9a993845SKory Maincent (Dent Project) const struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS]) 694*9a993845SKory Maincent (Dent Project) { 695*9a993845SKory Maincent (Dent Project) struct pd692x0_msg msg, buf; 696*9a993845SKory Maincent (Dent Project) int ret, i; 697*9a993845SKory Maincent (Dent Project) 698*9a993845SKory Maincent (Dent Project) /* Write temporary Matrix */ 699*9a993845SKory Maincent (Dent Project) msg = pd692x0_msg_template_list[PD692X0_MSG_SET_TMP_PORT_MATRIX]; 700*9a993845SKory Maincent (Dent Project) for (i = 0; i < PD692X0_MAX_PIS; i++) { 701*9a993845SKory Maincent (Dent Project) msg.sub[2] = i; 702*9a993845SKory Maincent (Dent Project) msg.data[0] = port_matrix[i].hw_port_b; 703*9a993845SKory Maincent (Dent Project) msg.data[1] = port_matrix[i].hw_port_a; 704*9a993845SKory Maincent (Dent Project) 705*9a993845SKory Maincent (Dent Project) ret = pd692x0_sendrecv_msg(priv, &msg, &buf); 706*9a993845SKory Maincent (Dent Project) if (ret < 0) 707*9a993845SKory Maincent (Dent Project) return ret; 708*9a993845SKory Maincent (Dent Project) } 709*9a993845SKory Maincent (Dent Project) 710*9a993845SKory Maincent (Dent Project) /* Program Matrix */ 711*9a993845SKory Maincent (Dent Project) msg = pd692x0_msg_template_list[PD692X0_MSG_PRG_PORT_MATRIX]; 712*9a993845SKory Maincent (Dent Project) ret = pd692x0_sendrecv_msg(priv, &msg, &buf); 713*9a993845SKory Maincent (Dent Project) if (ret < 0) 714*9a993845SKory Maincent (Dent Project) return ret; 715*9a993845SKory Maincent (Dent Project) 716*9a993845SKory Maincent (Dent Project) return 0; 717*9a993845SKory Maincent (Dent Project) } 718*9a993845SKory Maincent (Dent Project) 719*9a993845SKory Maincent (Dent Project) static int pd692x0_setup_pi_matrix(struct pse_controller_dev *pcdev) 720*9a993845SKory Maincent (Dent Project) { 721*9a993845SKory Maincent (Dent Project) struct pd692x0_manager manager[PD692X0_MAX_MANAGERS] = {0}; 722*9a993845SKory Maincent (Dent Project) struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); 723*9a993845SKory Maincent (Dent Project) struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS]; 724*9a993845SKory Maincent (Dent Project) int ret, i, j, nmanagers; 725*9a993845SKory Maincent (Dent Project) 726*9a993845SKory Maincent (Dent Project) /* Should we flash the port matrix */ 727*9a993845SKory Maincent (Dent Project) if (priv->fw_state != PD692X0_FW_OK && 728*9a993845SKory Maincent (Dent Project) priv->fw_state != PD692X0_FW_COMPLETE) 729*9a993845SKory Maincent (Dent Project) return 0; 730*9a993845SKory Maincent (Dent Project) 731*9a993845SKory Maincent (Dent Project) ret = pd692x0_of_get_managers(priv, manager); 732*9a993845SKory Maincent (Dent Project) if (ret < 0) 733*9a993845SKory Maincent (Dent Project) return ret; 734*9a993845SKory Maincent (Dent Project) 735*9a993845SKory Maincent (Dent Project) nmanagers = ret; 736*9a993845SKory Maincent (Dent Project) ret = pd692x0_set_ports_matrix(priv, manager, nmanagers, port_matrix); 737*9a993845SKory Maincent (Dent Project) if (ret) 738*9a993845SKory Maincent (Dent Project) goto out; 739*9a993845SKory Maincent (Dent Project) 740*9a993845SKory Maincent (Dent Project) ret = pd692x0_write_ports_matrix(priv, port_matrix); 741*9a993845SKory Maincent (Dent Project) if (ret) 742*9a993845SKory Maincent (Dent Project) goto out; 743*9a993845SKory Maincent (Dent Project) 744*9a993845SKory Maincent (Dent Project) out: 745*9a993845SKory Maincent (Dent Project) for (i = 0; i < nmanagers; i++) { 746*9a993845SKory Maincent (Dent Project) for (j = 0; j < manager[i].nports; j++) 747*9a993845SKory Maincent (Dent Project) of_node_put(manager[i].port_node[j]); 748*9a993845SKory Maincent (Dent Project) } 749*9a993845SKory Maincent (Dent Project) return ret; 750*9a993845SKory Maincent (Dent Project) } 751*9a993845SKory Maincent (Dent Project) 752*9a993845SKory Maincent (Dent Project) static const struct pse_controller_ops pd692x0_ops = { 753*9a993845SKory Maincent (Dent Project) .setup_pi_matrix = pd692x0_setup_pi_matrix, 754*9a993845SKory Maincent (Dent Project) .ethtool_get_status = pd692x0_ethtool_get_status, 755*9a993845SKory Maincent (Dent Project) .pi_enable = pd692x0_pi_enable, 756*9a993845SKory Maincent (Dent Project) .pi_disable = pd692x0_pi_disable, 757*9a993845SKory Maincent (Dent Project) .pi_is_enabled = pd692x0_pi_is_enabled, 758*9a993845SKory Maincent (Dent Project) }; 759*9a993845SKory Maincent (Dent Project) 760*9a993845SKory Maincent (Dent Project) #define PD692X0_FW_LINE_MAX_SZ 0xff 761*9a993845SKory Maincent (Dent Project) static int pd692x0_fw_get_next_line(const u8 *data, 762*9a993845SKory Maincent (Dent Project) char *line, size_t size) 763*9a993845SKory Maincent (Dent Project) { 764*9a993845SKory Maincent (Dent Project) size_t line_size; 765*9a993845SKory Maincent (Dent Project) int i; 766*9a993845SKory Maincent (Dent Project) 767*9a993845SKory Maincent (Dent Project) line_size = min_t(size_t, size, PD692X0_FW_LINE_MAX_SZ); 768*9a993845SKory Maincent (Dent Project) 769*9a993845SKory Maincent (Dent Project) memset(line, 0, PD692X0_FW_LINE_MAX_SZ); 770*9a993845SKory Maincent (Dent Project) for (i = 0; i < line_size - 1; i++) { 771*9a993845SKory Maincent (Dent Project) if (*data == '\r' && *(data + 1) == '\n') { 772*9a993845SKory Maincent (Dent Project) line[i] = '\r'; 773*9a993845SKory Maincent (Dent Project) line[i + 1] = '\n'; 774*9a993845SKory Maincent (Dent Project) return i + 2; 775*9a993845SKory Maincent (Dent Project) } 776*9a993845SKory Maincent (Dent Project) line[i] = *data; 777*9a993845SKory Maincent (Dent Project) data++; 778*9a993845SKory Maincent (Dent Project) } 779*9a993845SKory Maincent (Dent Project) 780*9a993845SKory Maincent (Dent Project) return -EIO; 781*9a993845SKory Maincent (Dent Project) } 782*9a993845SKory Maincent (Dent Project) 783*9a993845SKory Maincent (Dent Project) static enum fw_upload_err 784*9a993845SKory Maincent (Dent Project) pd692x0_fw_recv_resp(const struct i2c_client *client, unsigned long ms_timeout, 785*9a993845SKory Maincent (Dent Project) const char *msg_ok, unsigned int msg_size) 786*9a993845SKory Maincent (Dent Project) { 787*9a993845SKory Maincent (Dent Project) /* Maximum controller response size */ 788*9a993845SKory Maincent (Dent Project) char fw_msg_buf[5] = {0}; 789*9a993845SKory Maincent (Dent Project) unsigned long timeout; 790*9a993845SKory Maincent (Dent Project) int ret; 791*9a993845SKory Maincent (Dent Project) 792*9a993845SKory Maincent (Dent Project) if (msg_size > sizeof(fw_msg_buf)) 793*9a993845SKory Maincent (Dent Project) return FW_UPLOAD_ERR_RW_ERROR; 794*9a993845SKory Maincent (Dent Project) 795*9a993845SKory Maincent (Dent Project) /* Read until we get something */ 796*9a993845SKory Maincent (Dent Project) timeout = msecs_to_jiffies(ms_timeout) + jiffies; 797*9a993845SKory Maincent (Dent Project) while (true) { 798*9a993845SKory Maincent (Dent Project) if (time_is_before_jiffies(timeout)) 799*9a993845SKory Maincent (Dent Project) return FW_UPLOAD_ERR_TIMEOUT; 800*9a993845SKory Maincent (Dent Project) 801*9a993845SKory Maincent (Dent Project) ret = i2c_master_recv(client, fw_msg_buf, 1); 802*9a993845SKory Maincent (Dent Project) if (ret < 0 || *fw_msg_buf == 0) { 803*9a993845SKory Maincent (Dent Project) usleep_range(1000, 2000); 804*9a993845SKory Maincent (Dent Project) continue; 805*9a993845SKory Maincent (Dent Project) } else { 806*9a993845SKory Maincent (Dent Project) break; 807*9a993845SKory Maincent (Dent Project) } 808*9a993845SKory Maincent (Dent Project) } 809*9a993845SKory Maincent (Dent Project) 810*9a993845SKory Maincent (Dent Project) /* Read remaining characters */ 811*9a993845SKory Maincent (Dent Project) ret = i2c_master_recv(client, fw_msg_buf + 1, msg_size - 1); 812*9a993845SKory Maincent (Dent Project) if (strncmp(fw_msg_buf, msg_ok, msg_size)) { 813*9a993845SKory Maincent (Dent Project) dev_err(&client->dev, 814*9a993845SKory Maincent (Dent Project) "Wrong FW download process answer (%*pE)\n", 815*9a993845SKory Maincent (Dent Project) msg_size, fw_msg_buf); 816*9a993845SKory Maincent (Dent Project) return FW_UPLOAD_ERR_HW_ERROR; 817*9a993845SKory Maincent (Dent Project) } 818*9a993845SKory Maincent (Dent Project) 819*9a993845SKory Maincent (Dent Project) return FW_UPLOAD_ERR_NONE; 820*9a993845SKory Maincent (Dent Project) } 821*9a993845SKory Maincent (Dent Project) 822*9a993845SKory Maincent (Dent Project) static int pd692x0_fw_write_line(const struct i2c_client *client, 823*9a993845SKory Maincent (Dent Project) const char line[PD692X0_FW_LINE_MAX_SZ], 824*9a993845SKory Maincent (Dent Project) const bool last_line) 825*9a993845SKory Maincent (Dent Project) { 826*9a993845SKory Maincent (Dent Project) int ret; 827*9a993845SKory Maincent (Dent Project) 828*9a993845SKory Maincent (Dent Project) while (*line != 0) { 829*9a993845SKory Maincent (Dent Project) ret = i2c_master_send(client, line, 1); 830*9a993845SKory Maincent (Dent Project) if (ret < 0) 831*9a993845SKory Maincent (Dent Project) return FW_UPLOAD_ERR_RW_ERROR; 832*9a993845SKory Maincent (Dent Project) line++; 833*9a993845SKory Maincent (Dent Project) } 834*9a993845SKory Maincent (Dent Project) 835*9a993845SKory Maincent (Dent Project) if (last_line) { 836*9a993845SKory Maincent (Dent Project) ret = pd692x0_fw_recv_resp(client, 100, "TP\r\n", 837*9a993845SKory Maincent (Dent Project) sizeof("TP\r\n") - 1); 838*9a993845SKory Maincent (Dent Project) if (ret) 839*9a993845SKory Maincent (Dent Project) return ret; 840*9a993845SKory Maincent (Dent Project) } else { 841*9a993845SKory Maincent (Dent Project) ret = pd692x0_fw_recv_resp(client, 100, "T*\r\n", 842*9a993845SKory Maincent (Dent Project) sizeof("T*\r\n") - 1); 843*9a993845SKory Maincent (Dent Project) if (ret) 844*9a993845SKory Maincent (Dent Project) return ret; 845*9a993845SKory Maincent (Dent Project) } 846*9a993845SKory Maincent (Dent Project) 847*9a993845SKory Maincent (Dent Project) return FW_UPLOAD_ERR_NONE; 848*9a993845SKory Maincent (Dent Project) } 849*9a993845SKory Maincent (Dent Project) 850*9a993845SKory Maincent (Dent Project) static enum fw_upload_err pd692x0_fw_reset(const struct i2c_client *client) 851*9a993845SKory Maincent (Dent Project) { 852*9a993845SKory Maincent (Dent Project) const struct pd692x0_msg zero = {0}; 853*9a993845SKory Maincent (Dent Project) struct pd692x0_msg buf = {0}; 854*9a993845SKory Maincent (Dent Project) unsigned long timeout; 855*9a993845SKory Maincent (Dent Project) char cmd[] = "RST"; 856*9a993845SKory Maincent (Dent Project) int ret; 857*9a993845SKory Maincent (Dent Project) 858*9a993845SKory Maincent (Dent Project) ret = i2c_master_send(client, cmd, strlen(cmd)); 859*9a993845SKory Maincent (Dent Project) if (ret < 0) { 860*9a993845SKory Maincent (Dent Project) dev_err(&client->dev, 861*9a993845SKory Maincent (Dent Project) "Failed to reset the controller (%pe)\n", 862*9a993845SKory Maincent (Dent Project) ERR_PTR(ret)); 863*9a993845SKory Maincent (Dent Project) return ret; 864*9a993845SKory Maincent (Dent Project) } 865*9a993845SKory Maincent (Dent Project) 866*9a993845SKory Maincent (Dent Project) timeout = msecs_to_jiffies(10000) + jiffies; 867*9a993845SKory Maincent (Dent Project) while (true) { 868*9a993845SKory Maincent (Dent Project) if (time_is_before_jiffies(timeout)) 869*9a993845SKory Maincent (Dent Project) return FW_UPLOAD_ERR_TIMEOUT; 870*9a993845SKory Maincent (Dent Project) 871*9a993845SKory Maincent (Dent Project) ret = i2c_master_recv(client, (u8 *)&buf, sizeof(buf)); 872*9a993845SKory Maincent (Dent Project) if (ret < 0 || 873*9a993845SKory Maincent (Dent Project) !memcmp(&buf, &zero, sizeof(buf))) 874*9a993845SKory Maincent (Dent Project) usleep_range(1000, 2000); 875*9a993845SKory Maincent (Dent Project) else 876*9a993845SKory Maincent (Dent Project) break; 877*9a993845SKory Maincent (Dent Project) } 878*9a993845SKory Maincent (Dent Project) 879*9a993845SKory Maincent (Dent Project) /* Is the reply a successful report message */ 880*9a993845SKory Maincent (Dent Project) if (buf.key != PD692X0_KEY_TLM || buf.echo != 0xff || 881*9a993845SKory Maincent (Dent Project) buf.sub[0] & 0x01) { 882*9a993845SKory Maincent (Dent Project) dev_err(&client->dev, "PSE controller error\n"); 883*9a993845SKory Maincent (Dent Project) return FW_UPLOAD_ERR_HW_ERROR; 884*9a993845SKory Maincent (Dent Project) } 885*9a993845SKory Maincent (Dent Project) 886*9a993845SKory Maincent (Dent Project) /* Is the firmware operational */ 887*9a993845SKory Maincent (Dent Project) if (buf.sub[0] & 0x02) { 888*9a993845SKory Maincent (Dent Project) dev_err(&client->dev, 889*9a993845SKory Maincent (Dent Project) "PSE firmware error. Please update it.\n"); 890*9a993845SKory Maincent (Dent Project) return FW_UPLOAD_ERR_HW_ERROR; 891*9a993845SKory Maincent (Dent Project) } 892*9a993845SKory Maincent (Dent Project) 893*9a993845SKory Maincent (Dent Project) return FW_UPLOAD_ERR_NONE; 894*9a993845SKory Maincent (Dent Project) } 895*9a993845SKory Maincent (Dent Project) 896*9a993845SKory Maincent (Dent Project) static enum fw_upload_err pd692x0_fw_prepare(struct fw_upload *fwl, 897*9a993845SKory Maincent (Dent Project) const u8 *data, u32 size) 898*9a993845SKory Maincent (Dent Project) { 899*9a993845SKory Maincent (Dent Project) struct pd692x0_priv *priv = fwl->dd_handle; 900*9a993845SKory Maincent (Dent Project) const struct i2c_client *client = priv->client; 901*9a993845SKory Maincent (Dent Project) enum pd692x0_fw_state last_fw_state; 902*9a993845SKory Maincent (Dent Project) int ret; 903*9a993845SKory Maincent (Dent Project) 904*9a993845SKory Maincent (Dent Project) priv->cancel_request = false; 905*9a993845SKory Maincent (Dent Project) last_fw_state = priv->fw_state; 906*9a993845SKory Maincent (Dent Project) 907*9a993845SKory Maincent (Dent Project) priv->fw_state = PD692X0_FW_PREPARE; 908*9a993845SKory Maincent (Dent Project) 909*9a993845SKory Maincent (Dent Project) /* Enter program mode */ 910*9a993845SKory Maincent (Dent Project) if (last_fw_state == PD692X0_FW_BROKEN) { 911*9a993845SKory Maincent (Dent Project) const char *msg = "ENTR"; 912*9a993845SKory Maincent (Dent Project) const char *c; 913*9a993845SKory Maincent (Dent Project) 914*9a993845SKory Maincent (Dent Project) c = msg; 915*9a993845SKory Maincent (Dent Project) do { 916*9a993845SKory Maincent (Dent Project) ret = i2c_master_send(client, c, 1); 917*9a993845SKory Maincent (Dent Project) if (ret < 0) 918*9a993845SKory Maincent (Dent Project) return FW_UPLOAD_ERR_RW_ERROR; 919*9a993845SKory Maincent (Dent Project) if (*(c + 1)) 920*9a993845SKory Maincent (Dent Project) usleep_range(10000, 20000); 921*9a993845SKory Maincent (Dent Project) } while (*(++c)); 922*9a993845SKory Maincent (Dent Project) } else { 923*9a993845SKory Maincent (Dent Project) struct pd692x0_msg msg, buf; 924*9a993845SKory Maincent (Dent Project) 925*9a993845SKory Maincent (Dent Project) msg = pd692x0_msg_template_list[PD692X0_MSG_DOWNLOAD_CMD]; 926*9a993845SKory Maincent (Dent Project) ret = pd692x0_sendrecv_msg(priv, &msg, &buf); 927*9a993845SKory Maincent (Dent Project) if (ret < 0) { 928*9a993845SKory Maincent (Dent Project) dev_err(&client->dev, 929*9a993845SKory Maincent (Dent Project) "Failed to enter programming mode (%pe)\n", 930*9a993845SKory Maincent (Dent Project) ERR_PTR(ret)); 931*9a993845SKory Maincent (Dent Project) return FW_UPLOAD_ERR_RW_ERROR; 932*9a993845SKory Maincent (Dent Project) } 933*9a993845SKory Maincent (Dent Project) } 934*9a993845SKory Maincent (Dent Project) 935*9a993845SKory Maincent (Dent Project) ret = pd692x0_fw_recv_resp(client, 100, "TPE\r\n", sizeof("TPE\r\n") - 1); 936*9a993845SKory Maincent (Dent Project) if (ret) 937*9a993845SKory Maincent (Dent Project) goto err_out; 938*9a993845SKory Maincent (Dent Project) 939*9a993845SKory Maincent (Dent Project) if (priv->cancel_request) { 940*9a993845SKory Maincent (Dent Project) ret = FW_UPLOAD_ERR_CANCELED; 941*9a993845SKory Maincent (Dent Project) goto err_out; 942*9a993845SKory Maincent (Dent Project) } 943*9a993845SKory Maincent (Dent Project) 944*9a993845SKory Maincent (Dent Project) return FW_UPLOAD_ERR_NONE; 945*9a993845SKory Maincent (Dent Project) 946*9a993845SKory Maincent (Dent Project) err_out: 947*9a993845SKory Maincent (Dent Project) pd692x0_fw_reset(priv->client); 948*9a993845SKory Maincent (Dent Project) priv->fw_state = last_fw_state; 949*9a993845SKory Maincent (Dent Project) return ret; 950*9a993845SKory Maincent (Dent Project) } 951*9a993845SKory Maincent (Dent Project) 952*9a993845SKory Maincent (Dent Project) static enum fw_upload_err pd692x0_fw_write(struct fw_upload *fwl, 953*9a993845SKory Maincent (Dent Project) const u8 *data, u32 offset, 954*9a993845SKory Maincent (Dent Project) u32 size, u32 *written) 955*9a993845SKory Maincent (Dent Project) { 956*9a993845SKory Maincent (Dent Project) struct pd692x0_priv *priv = fwl->dd_handle; 957*9a993845SKory Maincent (Dent Project) char line[PD692X0_FW_LINE_MAX_SZ]; 958*9a993845SKory Maincent (Dent Project) const struct i2c_client *client; 959*9a993845SKory Maincent (Dent Project) int ret, i; 960*9a993845SKory Maincent (Dent Project) char cmd; 961*9a993845SKory Maincent (Dent Project) 962*9a993845SKory Maincent (Dent Project) client = priv->client; 963*9a993845SKory Maincent (Dent Project) priv->fw_state = PD692X0_FW_WRITE; 964*9a993845SKory Maincent (Dent Project) 965*9a993845SKory Maincent (Dent Project) /* Erase */ 966*9a993845SKory Maincent (Dent Project) cmd = 'E'; 967*9a993845SKory Maincent (Dent Project) ret = i2c_master_send(client, &cmd, 1); 968*9a993845SKory Maincent (Dent Project) if (ret < 0) { 969*9a993845SKory Maincent (Dent Project) dev_err(&client->dev, 970*9a993845SKory Maincent (Dent Project) "Failed to boot programming mode (%pe)\n", 971*9a993845SKory Maincent (Dent Project) ERR_PTR(ret)); 972*9a993845SKory Maincent (Dent Project) return FW_UPLOAD_ERR_RW_ERROR; 973*9a993845SKory Maincent (Dent Project) } 974*9a993845SKory Maincent (Dent Project) 975*9a993845SKory Maincent (Dent Project) ret = pd692x0_fw_recv_resp(client, 100, "TOE\r\n", sizeof("TOE\r\n") - 1); 976*9a993845SKory Maincent (Dent Project) if (ret) 977*9a993845SKory Maincent (Dent Project) return ret; 978*9a993845SKory Maincent (Dent Project) 979*9a993845SKory Maincent (Dent Project) ret = pd692x0_fw_recv_resp(client, 5000, "TE\r\n", sizeof("TE\r\n") - 1); 980*9a993845SKory Maincent (Dent Project) if (ret) 981*9a993845SKory Maincent (Dent Project) dev_warn(&client->dev, 982*9a993845SKory Maincent (Dent Project) "Failed to erase internal memory, however still try to write Firmware\n"); 983*9a993845SKory Maincent (Dent Project) 984*9a993845SKory Maincent (Dent Project) ret = pd692x0_fw_recv_resp(client, 100, "TPE\r\n", sizeof("TPE\r\n") - 1); 985*9a993845SKory Maincent (Dent Project) if (ret) 986*9a993845SKory Maincent (Dent Project) dev_warn(&client->dev, 987*9a993845SKory Maincent (Dent Project) "Failed to erase internal memory, however still try to write Firmware\n"); 988*9a993845SKory Maincent (Dent Project) 989*9a993845SKory Maincent (Dent Project) if (priv->cancel_request) 990*9a993845SKory Maincent (Dent Project) return FW_UPLOAD_ERR_CANCELED; 991*9a993845SKory Maincent (Dent Project) 992*9a993845SKory Maincent (Dent Project) /* Program */ 993*9a993845SKory Maincent (Dent Project) cmd = 'P'; 994*9a993845SKory Maincent (Dent Project) ret = i2c_master_send(client, &cmd, sizeof(char)); 995*9a993845SKory Maincent (Dent Project) if (ret < 0) { 996*9a993845SKory Maincent (Dent Project) dev_err(&client->dev, 997*9a993845SKory Maincent (Dent Project) "Failed to boot programming mode (%pe)\n", 998*9a993845SKory Maincent (Dent Project) ERR_PTR(ret)); 999*9a993845SKory Maincent (Dent Project) return ret; 1000*9a993845SKory Maincent (Dent Project) } 1001*9a993845SKory Maincent (Dent Project) 1002*9a993845SKory Maincent (Dent Project) ret = pd692x0_fw_recv_resp(client, 100, "TOP\r\n", sizeof("TOP\r\n") - 1); 1003*9a993845SKory Maincent (Dent Project) if (ret) 1004*9a993845SKory Maincent (Dent Project) return ret; 1005*9a993845SKory Maincent (Dent Project) 1006*9a993845SKory Maincent (Dent Project) i = 0; 1007*9a993845SKory Maincent (Dent Project) while (i < size) { 1008*9a993845SKory Maincent (Dent Project) ret = pd692x0_fw_get_next_line(data, line, size - i); 1009*9a993845SKory Maincent (Dent Project) if (ret < 0) { 1010*9a993845SKory Maincent (Dent Project) ret = FW_UPLOAD_ERR_FW_INVALID; 1011*9a993845SKory Maincent (Dent Project) goto err; 1012*9a993845SKory Maincent (Dent Project) } 1013*9a993845SKory Maincent (Dent Project) 1014*9a993845SKory Maincent (Dent Project) i += ret; 1015*9a993845SKory Maincent (Dent Project) data += ret; 1016*9a993845SKory Maincent (Dent Project) if (line[0] == 'S' && line[1] == '0') { 1017*9a993845SKory Maincent (Dent Project) continue; 1018*9a993845SKory Maincent (Dent Project) } else if (line[0] == 'S' && line[1] == '7') { 1019*9a993845SKory Maincent (Dent Project) ret = pd692x0_fw_write_line(client, line, true); 1020*9a993845SKory Maincent (Dent Project) if (ret) 1021*9a993845SKory Maincent (Dent Project) goto err; 1022*9a993845SKory Maincent (Dent Project) } else { 1023*9a993845SKory Maincent (Dent Project) ret = pd692x0_fw_write_line(client, line, false); 1024*9a993845SKory Maincent (Dent Project) if (ret) 1025*9a993845SKory Maincent (Dent Project) goto err; 1026*9a993845SKory Maincent (Dent Project) } 1027*9a993845SKory Maincent (Dent Project) 1028*9a993845SKory Maincent (Dent Project) if (priv->cancel_request) { 1029*9a993845SKory Maincent (Dent Project) ret = FW_UPLOAD_ERR_CANCELED; 1030*9a993845SKory Maincent (Dent Project) goto err; 1031*9a993845SKory Maincent (Dent Project) } 1032*9a993845SKory Maincent (Dent Project) } 1033*9a993845SKory Maincent (Dent Project) *written = i; 1034*9a993845SKory Maincent (Dent Project) 1035*9a993845SKory Maincent (Dent Project) msleep(400); 1036*9a993845SKory Maincent (Dent Project) 1037*9a993845SKory Maincent (Dent Project) return FW_UPLOAD_ERR_NONE; 1038*9a993845SKory Maincent (Dent Project) 1039*9a993845SKory Maincent (Dent Project) err: 1040*9a993845SKory Maincent (Dent Project) strscpy_pad(line, "S7\r\n", sizeof(line)); 1041*9a993845SKory Maincent (Dent Project) pd692x0_fw_write_line(client, line, true); 1042*9a993845SKory Maincent (Dent Project) return ret; 1043*9a993845SKory Maincent (Dent Project) } 1044*9a993845SKory Maincent (Dent Project) 1045*9a993845SKory Maincent (Dent Project) static enum fw_upload_err pd692x0_fw_poll_complete(struct fw_upload *fwl) 1046*9a993845SKory Maincent (Dent Project) { 1047*9a993845SKory Maincent (Dent Project) struct pd692x0_priv *priv = fwl->dd_handle; 1048*9a993845SKory Maincent (Dent Project) const struct i2c_client *client = priv->client; 1049*9a993845SKory Maincent (Dent Project) struct pd692x0_msg_ver ver; 1050*9a993845SKory Maincent (Dent Project) int ret; 1051*9a993845SKory Maincent (Dent Project) 1052*9a993845SKory Maincent (Dent Project) priv->fw_state = PD692X0_FW_COMPLETE; 1053*9a993845SKory Maincent (Dent Project) 1054*9a993845SKory Maincent (Dent Project) ret = pd692x0_fw_reset(client); 1055*9a993845SKory Maincent (Dent Project) if (ret) 1056*9a993845SKory Maincent (Dent Project) return ret; 1057*9a993845SKory Maincent (Dent Project) 1058*9a993845SKory Maincent (Dent Project) ver = pd692x0_get_sw_version(priv); 1059*9a993845SKory Maincent (Dent Project) if (ver.maj_sw_ver < PD692X0_FW_MAJ_VER) { 1060*9a993845SKory Maincent (Dent Project) dev_err(&client->dev, 1061*9a993845SKory Maincent (Dent Project) "Too old firmware version. Please update it\n"); 1062*9a993845SKory Maincent (Dent Project) priv->fw_state = PD692X0_FW_NEED_UPDATE; 1063*9a993845SKory Maincent (Dent Project) return FW_UPLOAD_ERR_FW_INVALID; 1064*9a993845SKory Maincent (Dent Project) } 1065*9a993845SKory Maincent (Dent Project) 1066*9a993845SKory Maincent (Dent Project) ret = pd692x0_setup_pi_matrix(&priv->pcdev); 1067*9a993845SKory Maincent (Dent Project) if (ret < 0) { 1068*9a993845SKory Maincent (Dent Project) dev_err(&client->dev, "Error configuring ports matrix (%pe)\n", 1069*9a993845SKory Maincent (Dent Project) ERR_PTR(ret)); 1070*9a993845SKory Maincent (Dent Project) priv->fw_state = PD692X0_FW_NEED_UPDATE; 1071*9a993845SKory Maincent (Dent Project) return FW_UPLOAD_ERR_HW_ERROR; 1072*9a993845SKory Maincent (Dent Project) } 1073*9a993845SKory Maincent (Dent Project) 1074*9a993845SKory Maincent (Dent Project) priv->fw_state = PD692X0_FW_OK; 1075*9a993845SKory Maincent (Dent Project) return FW_UPLOAD_ERR_NONE; 1076*9a993845SKory Maincent (Dent Project) } 1077*9a993845SKory Maincent (Dent Project) 1078*9a993845SKory Maincent (Dent Project) static void pd692x0_fw_cancel(struct fw_upload *fwl) 1079*9a993845SKory Maincent (Dent Project) { 1080*9a993845SKory Maincent (Dent Project) struct pd692x0_priv *priv = fwl->dd_handle; 1081*9a993845SKory Maincent (Dent Project) 1082*9a993845SKory Maincent (Dent Project) priv->cancel_request = true; 1083*9a993845SKory Maincent (Dent Project) } 1084*9a993845SKory Maincent (Dent Project) 1085*9a993845SKory Maincent (Dent Project) static void pd692x0_fw_cleanup(struct fw_upload *fwl) 1086*9a993845SKory Maincent (Dent Project) { 1087*9a993845SKory Maincent (Dent Project) struct pd692x0_priv *priv = fwl->dd_handle; 1088*9a993845SKory Maincent (Dent Project) 1089*9a993845SKory Maincent (Dent Project) switch (priv->fw_state) { 1090*9a993845SKory Maincent (Dent Project) case PD692X0_FW_WRITE: 1091*9a993845SKory Maincent (Dent Project) pd692x0_fw_reset(priv->client); 1092*9a993845SKory Maincent (Dent Project) fallthrough; 1093*9a993845SKory Maincent (Dent Project) case PD692X0_FW_COMPLETE: 1094*9a993845SKory Maincent (Dent Project) priv->fw_state = PD692X0_FW_BROKEN; 1095*9a993845SKory Maincent (Dent Project) break; 1096*9a993845SKory Maincent (Dent Project) default: 1097*9a993845SKory Maincent (Dent Project) break; 1098*9a993845SKory Maincent (Dent Project) } 1099*9a993845SKory Maincent (Dent Project) } 1100*9a993845SKory Maincent (Dent Project) 1101*9a993845SKory Maincent (Dent Project) static const struct fw_upload_ops pd692x0_fw_ops = { 1102*9a993845SKory Maincent (Dent Project) .prepare = pd692x0_fw_prepare, 1103*9a993845SKory Maincent (Dent Project) .write = pd692x0_fw_write, 1104*9a993845SKory Maincent (Dent Project) .poll_complete = pd692x0_fw_poll_complete, 1105*9a993845SKory Maincent (Dent Project) .cancel = pd692x0_fw_cancel, 1106*9a993845SKory Maincent (Dent Project) .cleanup = pd692x0_fw_cleanup, 1107*9a993845SKory Maincent (Dent Project) }; 1108*9a993845SKory Maincent (Dent Project) 1109*9a993845SKory Maincent (Dent Project) static int pd692x0_i2c_probe(struct i2c_client *client) 1110*9a993845SKory Maincent (Dent Project) { 1111*9a993845SKory Maincent (Dent Project) struct pd692x0_msg msg, buf = {0}, zero = {0}; 1112*9a993845SKory Maincent (Dent Project) struct device *dev = &client->dev; 1113*9a993845SKory Maincent (Dent Project) struct pd692x0_msg_ver ver; 1114*9a993845SKory Maincent (Dent Project) struct pd692x0_priv *priv; 1115*9a993845SKory Maincent (Dent Project) struct fw_upload *fwl; 1116*9a993845SKory Maincent (Dent Project) int ret; 1117*9a993845SKory Maincent (Dent Project) 1118*9a993845SKory Maincent (Dent Project) if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { 1119*9a993845SKory Maincent (Dent Project) dev_err(dev, "i2c check functionality failed\n"); 1120*9a993845SKory Maincent (Dent Project) return -ENXIO; 1121*9a993845SKory Maincent (Dent Project) } 1122*9a993845SKory Maincent (Dent Project) 1123*9a993845SKory Maincent (Dent Project) priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 1124*9a993845SKory Maincent (Dent Project) if (!priv) 1125*9a993845SKory Maincent (Dent Project) return -ENOMEM; 1126*9a993845SKory Maincent (Dent Project) 1127*9a993845SKory Maincent (Dent Project) priv->client = client; 1128*9a993845SKory Maincent (Dent Project) i2c_set_clientdata(client, priv); 1129*9a993845SKory Maincent (Dent Project) 1130*9a993845SKory Maincent (Dent Project) ret = i2c_master_recv(client, (u8 *)&buf, sizeof(buf)); 1131*9a993845SKory Maincent (Dent Project) if (ret != sizeof(buf)) { 1132*9a993845SKory Maincent (Dent Project) dev_err(dev, "Failed to get device status\n"); 1133*9a993845SKory Maincent (Dent Project) return -EIO; 1134*9a993845SKory Maincent (Dent Project) } 1135*9a993845SKory Maincent (Dent Project) 1136*9a993845SKory Maincent (Dent Project) /* Probe has been already run and the status dumped */ 1137*9a993845SKory Maincent (Dent Project) if (!memcmp(&buf, &zero, sizeof(buf))) { 1138*9a993845SKory Maincent (Dent Project) /* Ask again the controller status */ 1139*9a993845SKory Maincent (Dent Project) msg = pd692x0_msg_template_list[PD692X0_MSG_GET_SYS_STATUS]; 1140*9a993845SKory Maincent (Dent Project) ret = pd692x0_sendrecv_msg(priv, &msg, &buf); 1141*9a993845SKory Maincent (Dent Project) if (ret < 0) { 1142*9a993845SKory Maincent (Dent Project) dev_err(dev, "Failed to get device status\n"); 1143*9a993845SKory Maincent (Dent Project) return ret; 1144*9a993845SKory Maincent (Dent Project) } 1145*9a993845SKory Maincent (Dent Project) } 1146*9a993845SKory Maincent (Dent Project) 1147*9a993845SKory Maincent (Dent Project) if (buf.key != 0x03 || buf.sub[0] & 0x01) { 1148*9a993845SKory Maincent (Dent Project) dev_err(dev, "PSE controller error\n"); 1149*9a993845SKory Maincent (Dent Project) return -EIO; 1150*9a993845SKory Maincent (Dent Project) } 1151*9a993845SKory Maincent (Dent Project) if (buf.sub[0] & 0x02) { 1152*9a993845SKory Maincent (Dent Project) dev_err(dev, "PSE firmware error. Please update it.\n"); 1153*9a993845SKory Maincent (Dent Project) priv->fw_state = PD692X0_FW_BROKEN; 1154*9a993845SKory Maincent (Dent Project) } else { 1155*9a993845SKory Maincent (Dent Project) ver = pd692x0_get_sw_version(priv); 1156*9a993845SKory Maincent (Dent Project) dev_info(&client->dev, "Software version %d.%02d.%d.%d\n", 1157*9a993845SKory Maincent (Dent Project) ver.prod, ver.maj_sw_ver, ver.min_sw_ver, 1158*9a993845SKory Maincent (Dent Project) ver.pa_sw_ver); 1159*9a993845SKory Maincent (Dent Project) 1160*9a993845SKory Maincent (Dent Project) if (ver.maj_sw_ver < PD692X0_FW_MAJ_VER) { 1161*9a993845SKory Maincent (Dent Project) dev_err(dev, "Too old firmware version. Please update it\n"); 1162*9a993845SKory Maincent (Dent Project) priv->fw_state = PD692X0_FW_NEED_UPDATE; 1163*9a993845SKory Maincent (Dent Project) } else { 1164*9a993845SKory Maincent (Dent Project) priv->fw_state = PD692X0_FW_OK; 1165*9a993845SKory Maincent (Dent Project) } 1166*9a993845SKory Maincent (Dent Project) } 1167*9a993845SKory Maincent (Dent Project) 1168*9a993845SKory Maincent (Dent Project) priv->np = dev->of_node; 1169*9a993845SKory Maincent (Dent Project) priv->pcdev.nr_lines = PD692X0_MAX_PIS; 1170*9a993845SKory Maincent (Dent Project) priv->pcdev.owner = THIS_MODULE; 1171*9a993845SKory Maincent (Dent Project) priv->pcdev.ops = &pd692x0_ops; 1172*9a993845SKory Maincent (Dent Project) priv->pcdev.dev = dev; 1173*9a993845SKory Maincent (Dent Project) priv->pcdev.types = ETHTOOL_PSE_C33; 1174*9a993845SKory Maincent (Dent Project) ret = devm_pse_controller_register(dev, &priv->pcdev); 1175*9a993845SKory Maincent (Dent Project) if (ret) 1176*9a993845SKory Maincent (Dent Project) return dev_err_probe(dev, ret, 1177*9a993845SKory Maincent (Dent Project) "failed to register PSE controller\n"); 1178*9a993845SKory Maincent (Dent Project) 1179*9a993845SKory Maincent (Dent Project) fwl = firmware_upload_register(THIS_MODULE, dev, dev_name(dev), 1180*9a993845SKory Maincent (Dent Project) &pd692x0_fw_ops, priv); 1181*9a993845SKory Maincent (Dent Project) if (IS_ERR(fwl)) 1182*9a993845SKory Maincent (Dent Project) return dev_err_probe(dev, PTR_ERR(fwl), 1183*9a993845SKory Maincent (Dent Project) "failed to register to the Firmware Upload API\n"); 1184*9a993845SKory Maincent (Dent Project) priv->fwl = fwl; 1185*9a993845SKory Maincent (Dent Project) 1186*9a993845SKory Maincent (Dent Project) return 0; 1187*9a993845SKory Maincent (Dent Project) } 1188*9a993845SKory Maincent (Dent Project) 1189*9a993845SKory Maincent (Dent Project) static void pd692x0_i2c_remove(struct i2c_client *client) 1190*9a993845SKory Maincent (Dent Project) { 1191*9a993845SKory Maincent (Dent Project) struct pd692x0_priv *priv = i2c_get_clientdata(client); 1192*9a993845SKory Maincent (Dent Project) 1193*9a993845SKory Maincent (Dent Project) firmware_upload_unregister(priv->fwl); 1194*9a993845SKory Maincent (Dent Project) } 1195*9a993845SKory Maincent (Dent Project) 1196*9a993845SKory Maincent (Dent Project) static const struct i2c_device_id pd692x0_id[] = { 1197*9a993845SKory Maincent (Dent Project) { PD692X0_PSE_NAME, 0 }, 1198*9a993845SKory Maincent (Dent Project) { }, 1199*9a993845SKory Maincent (Dent Project) }; 1200*9a993845SKory Maincent (Dent Project) MODULE_DEVICE_TABLE(i2c, pd692x0_id); 1201*9a993845SKory Maincent (Dent Project) 1202*9a993845SKory Maincent (Dent Project) static const struct of_device_id pd692x0_of_match[] = { 1203*9a993845SKory Maincent (Dent Project) { .compatible = "microchip,pd69200", }, 1204*9a993845SKory Maincent (Dent Project) { .compatible = "microchip,pd69210", }, 1205*9a993845SKory Maincent (Dent Project) { .compatible = "microchip,pd69220", }, 1206*9a993845SKory Maincent (Dent Project) { }, 1207*9a993845SKory Maincent (Dent Project) }; 1208*9a993845SKory Maincent (Dent Project) MODULE_DEVICE_TABLE(of, pd692x0_of_match); 1209*9a993845SKory Maincent (Dent Project) 1210*9a993845SKory Maincent (Dent Project) static struct i2c_driver pd692x0_driver = { 1211*9a993845SKory Maincent (Dent Project) .probe = pd692x0_i2c_probe, 1212*9a993845SKory Maincent (Dent Project) .remove = pd692x0_i2c_remove, 1213*9a993845SKory Maincent (Dent Project) .id_table = pd692x0_id, 1214*9a993845SKory Maincent (Dent Project) .driver = { 1215*9a993845SKory Maincent (Dent Project) .name = PD692X0_PSE_NAME, 1216*9a993845SKory Maincent (Dent Project) .of_match_table = pd692x0_of_match, 1217*9a993845SKory Maincent (Dent Project) }, 1218*9a993845SKory Maincent (Dent Project) }; 1219*9a993845SKory Maincent (Dent Project) module_i2c_driver(pd692x0_driver); 1220*9a993845SKory Maincent (Dent Project) 1221*9a993845SKory Maincent (Dent Project) MODULE_AUTHOR("Kory Maincent <kory.maincent@bootlin.com>"); 1222*9a993845SKory Maincent (Dent Project) MODULE_DESCRIPTION("Microchip PD692x0 PoE PSE Controller driver"); 1223*9a993845SKory Maincent (Dent Project) MODULE_LICENSE("GPL"); 1224