1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Based upon the MaxLinear SDK driver 4 * 5 * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org> 6 * Copyright (C) 2025 John Crispin <john@phrozen.org> 7 * Copyright (C) 2024 MaxLinear Inc. 8 */ 9 10 #include <linux/bits.h> 11 #include <linux/iopoll.h> 12 #include <linux/limits.h> 13 #include <net/dsa.h> 14 #include "mxl862xx.h" 15 #include "mxl862xx-host.h" 16 17 #define CTRL_BUSY_MASK BIT(15) 18 19 #define MXL862XX_MMD_REG_CTRL 0 20 #define MXL862XX_MMD_REG_LEN_RET 1 21 #define MXL862XX_MMD_REG_DATA_FIRST 2 22 #define MXL862XX_MMD_REG_DATA_LAST 95 23 #define MXL862XX_MMD_REG_DATA_MAX_SIZE \ 24 (MXL862XX_MMD_REG_DATA_LAST - MXL862XX_MMD_REG_DATA_FIRST + 1) 25 26 #define MMD_API_SET_DATA_0 2 27 #define MMD_API_GET_DATA_0 5 28 #define MMD_API_RST_DATA 8 29 30 #define MXL862XX_SWITCH_RESET 0x9907 31 32 static int mxl862xx_reg_read(struct mxl862xx_priv *priv, u32 addr) 33 { 34 return __mdiodev_c45_read(priv->mdiodev, MDIO_MMD_VEND1, addr); 35 } 36 37 static int mxl862xx_reg_write(struct mxl862xx_priv *priv, u32 addr, u16 data) 38 { 39 return __mdiodev_c45_write(priv->mdiodev, MDIO_MMD_VEND1, addr, data); 40 } 41 42 static int mxl862xx_ctrl_read(struct mxl862xx_priv *priv) 43 { 44 return mxl862xx_reg_read(priv, MXL862XX_MMD_REG_CTRL); 45 } 46 47 static int mxl862xx_busy_wait(struct mxl862xx_priv *priv) 48 { 49 int val; 50 51 return readx_poll_timeout(mxl862xx_ctrl_read, priv, val, 52 !(val & CTRL_BUSY_MASK), 15, 500000); 53 } 54 55 static int mxl862xx_set_data(struct mxl862xx_priv *priv, u16 words) 56 { 57 int ret; 58 u16 cmd; 59 60 ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, 61 MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16)); 62 if (ret < 0) 63 return ret; 64 65 cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE - 1; 66 if (!(cmd < 2)) 67 return -EINVAL; 68 69 cmd += MMD_API_SET_DATA_0; 70 ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, 71 cmd | CTRL_BUSY_MASK); 72 if (ret < 0) 73 return ret; 74 75 return mxl862xx_busy_wait(priv); 76 } 77 78 static int mxl862xx_get_data(struct mxl862xx_priv *priv, u16 words) 79 { 80 int ret; 81 u16 cmd; 82 83 ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, 84 MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16)); 85 if (ret < 0) 86 return ret; 87 88 cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE; 89 if (!(cmd > 0 && cmd < 3)) 90 return -EINVAL; 91 92 cmd += MMD_API_GET_DATA_0; 93 ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, 94 cmd | CTRL_BUSY_MASK); 95 if (ret < 0) 96 return ret; 97 98 return mxl862xx_busy_wait(priv); 99 } 100 101 static int mxl862xx_firmware_return(int ret) 102 { 103 /* Only 16-bit values are valid. */ 104 if (WARN_ON(ret & GENMASK(31, 16))) 105 return -EINVAL; 106 107 /* Interpret value as signed 16-bit integer. */ 108 return (s16)ret; 109 } 110 111 static int mxl862xx_send_cmd(struct mxl862xx_priv *priv, u16 cmd, u16 size, 112 bool quiet) 113 { 114 int ret; 115 116 ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, size); 117 if (ret) 118 return ret; 119 120 ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, 121 cmd | CTRL_BUSY_MASK); 122 if (ret) 123 return ret; 124 125 ret = mxl862xx_busy_wait(priv); 126 if (ret) 127 return ret; 128 129 ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_LEN_RET); 130 if (ret < 0) 131 return ret; 132 133 /* handle errors returned by the firmware as -EIO 134 * The firmware is based on Zephyr OS and uses the errors as 135 * defined in errno.h of Zephyr OS. See 136 * https://github.com/zephyrproject-rtos/zephyr/blob/v3.7.0/lib/libc/minimal/include/errno.h 137 */ 138 ret = mxl862xx_firmware_return(ret); 139 if (ret < 0) { 140 if (!quiet) 141 dev_err(&priv->mdiodev->dev, 142 "CMD %04x returned error %d\n", cmd, ret); 143 return -EIO; 144 } 145 146 return ret; 147 } 148 149 int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *_data, 150 u16 size, bool read, bool quiet) 151 { 152 __le16 *data = _data; 153 int ret, cmd_ret; 154 u16 max, i; 155 156 dev_dbg(&priv->mdiodev->dev, "CMD %04x DATA %*ph\n", cmd, size, data); 157 158 mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED); 159 160 max = (size + 1) / 2; 161 162 ret = mxl862xx_busy_wait(priv); 163 if (ret < 0) 164 goto out; 165 166 for (i = 0; i < max; i++) { 167 u16 off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE; 168 169 if (i && off == 0) { 170 /* Send command to set data when every 171 * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are written. 172 */ 173 ret = mxl862xx_set_data(priv, i); 174 if (ret < 0) 175 goto out; 176 } 177 178 ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_DATA_FIRST + off, 179 le16_to_cpu(data[i])); 180 if (ret < 0) 181 goto out; 182 } 183 184 ret = mxl862xx_send_cmd(priv, cmd, size, quiet); 185 if (ret < 0 || !read) 186 goto out; 187 188 /* store result of mxl862xx_send_cmd() */ 189 cmd_ret = ret; 190 191 for (i = 0; i < max; i++) { 192 u16 off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE; 193 194 if (i && off == 0) { 195 /* Send command to fetch next batch of data when every 196 * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are read. 197 */ 198 ret = mxl862xx_get_data(priv, i); 199 if (ret < 0) 200 goto out; 201 } 202 203 ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_DATA_FIRST + off); 204 if (ret < 0) 205 goto out; 206 207 if ((i * 2 + 1) == size) { 208 /* Special handling for last BYTE if it's not WORD 209 * aligned to avoid writing beyond the allocated data 210 * structure. 211 */ 212 *(uint8_t *)&data[i] = ret & 0xff; 213 } else { 214 data[i] = cpu_to_le16((u16)ret); 215 } 216 } 217 218 /* on success return the result of the mxl862xx_send_cmd() */ 219 ret = cmd_ret; 220 221 dev_dbg(&priv->mdiodev->dev, "RET %d DATA %*ph\n", ret, size, data); 222 223 out: 224 mutex_unlock(&priv->mdiodev->bus->mdio_lock); 225 226 return ret; 227 } 228 229 int mxl862xx_reset(struct mxl862xx_priv *priv) 230 { 231 int ret; 232 233 mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED); 234 235 /* Software reset */ 236 ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, 0); 237 if (ret) 238 goto out; 239 240 ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, MXL862XX_SWITCH_RESET); 241 out: 242 mutex_unlock(&priv->mdiodev->bus->mdio_lock); 243 244 return ret; 245 } 246