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