// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2018 Marvell International Ltd. */ #include #include #include #include #include #include #include #include #include #include #include "cgx_intf.h" #include "cgx.h" #include "nix.h" static u64 cgx_rd_scrx(u8 cgx, u8 lmac, u8 index) { u64 addr; addr = (index == 1) ? CGX_CMR_SCRATCH1 : CGX_CMR_SCRATCH0; addr += CGX_SHIFT(cgx) + CMR_SHIFT(lmac); return readq(addr); } static void cgx_wr_scrx(u8 cgx, u8 lmac, u8 index, u64 val) { u64 addr; addr = (index == 1) ? CGX_CMR_SCRATCH1 : CGX_CMR_SCRATCH0; addr += CGX_SHIFT(cgx) + CMR_SHIFT(lmac); writeq(val, addr); } static u64 cgx_rd_scr0(u8 cgx, u8 lmac) { return cgx_rd_scrx(cgx, lmac, 0); } static u64 cgx_rd_scr1(u8 cgx, u8 lmac) { return cgx_rd_scrx(cgx, lmac, 1); } static void cgx_wr_scr0(u8 cgx, u8 lmac, u64 val) { return cgx_wr_scrx(cgx, lmac, 0, val); } static void cgx_wr_scr1(u8 cgx, u8 lmac, u64 val) { return cgx_wr_scrx(cgx, lmac, 1, val); } static inline void set_ownership(u8 cgx, u8 lmac, u8 val) { union cgx_scratchx1 scr1; scr1.u = cgx_rd_scr1(cgx, lmac); scr1.s.own_status = val; cgx_wr_scr1(cgx, lmac, scr1.u); } static int wait_for_ownership(u8 cgx, u8 lmac) { union cgx_scratchx1 scr1; union cgx_scratchx0 scr0; u64 cmrx_int; int timeout = 5000; do { scr1.u = cgx_rd_scr1(cgx, lmac); scr0.u = cgx_rd_scr0(cgx, lmac); /* clear async events if any */ if (scr0.s.evt_sts.evt_type == CGX_EVT_ASYNC && scr0.s.evt_sts.ack) { /* clear interrupt */ cmrx_int = readq(CGX_CMR_INT + CGX_SHIFT(cgx) + CMR_SHIFT(lmac)); cmrx_int |= 0x2; // Overflw bit writeq(cmrx_int, CGX_CMR_INT + CGX_SHIFT(cgx) + CMR_SHIFT(lmac)); /* clear ack */ scr0.s.evt_sts.ack = 0; cgx_wr_scr0(cgx, lmac, scr0.u); } if (timeout-- < 0) { debug("timeout waiting for ownership\n"); return -ETIMEDOUT; } mdelay(1); } while ((scr1.s.own_status == CGX_OWN_FIRMWARE) && scr0.s.evt_sts.ack); return 0; } int cgx_intf_req(u8 cgx, u8 lmac, union cgx_cmd_s cmd_args, u64 *rsp, int use_cmd_id_only) { union cgx_scratchx1 scr1; union cgx_scratchx0 scr0; u64 cmrx_int; int timeout = 500; int err = 0; u8 cmd = cmd_args.cmd.id; if (wait_for_ownership(cgx, lmac)) { err = -ETIMEDOUT; goto error; } /* send command */ scr1.u = cgx_rd_scr1(cgx, lmac); if (use_cmd_id_only) { scr1.s.cmd.id = cmd; } else { cmd_args.own_status = scr1.s.own_status; scr1.s = cmd_args; } cgx_wr_scr1(cgx, lmac, scr1.u); set_ownership(cgx, lmac, CGX_OWN_FIRMWARE); /* wait for response and ownership */ do { scr0.u = cgx_rd_scr0(cgx, lmac); scr1.u = cgx_rd_scr1(cgx, lmac); mdelay(10); } while (timeout-- && (!scr0.s.evt_sts.ack) && (scr1.s.own_status == CGX_OWN_FIRMWARE)); if (timeout < 0) { debug("%s timeout waiting for ack\n", __func__); err = -ETIMEDOUT; goto error; } if (cmd == CGX_CMD_INTF_SHUTDOWN) goto error; if (scr0.s.evt_sts.evt_type != CGX_EVT_CMD_RESP) { debug("%s received async event instead of cmd resp event\n", __func__); err = -1; goto error; } if (scr0.s.evt_sts.id != cmd) { debug("%s received resp for cmd %d expected cmd %d\n", __func__, scr0.s.evt_sts.id, cmd); err = -1; goto error; } if (scr0.s.evt_sts.stat != CGX_STAT_SUCCESS) { debug("%s cmd%d failed on cgx%u lmac%u with errcode %d\n", __func__, cmd, cgx, lmac, scr0.s.link_sts.err_type); err = -1; } error: /* clear interrupt */ cmrx_int = readq(CGX_CMR_INT + CGX_SHIFT(cgx) + CMR_SHIFT(lmac)); cmrx_int |= 0x2; // Overflw bit writeq(cmrx_int, CGX_CMR_INT + CGX_SHIFT(cgx) + CMR_SHIFT(lmac)); /* clear ownership and ack */ scr0.s.evt_sts.ack = 0; cgx_wr_scr0(cgx, lmac, scr0.u); *rsp = err ? 0 : scr0.u; return err; } int cgx_intf_get_mac_addr(u8 cgx, u8 lmac, u8 *mac) { union cgx_scratchx0 scr0; int ret; union cgx_cmd_s cmd; cmd.cmd.id = CGX_CMD_GET_MAC_ADDR; ret = cgx_intf_req(cgx, lmac, cmd, &scr0.u, 1); if (ret) return -1; scr0.u >>= 9; memcpy(mac, &scr0.u, 6); return 0; } int cgx_intf_get_ver(u8 cgx, u8 lmac, u8 *ver) { union cgx_scratchx0 scr0; int ret; union cgx_cmd_s cmd; cmd.cmd.id = CGX_CMD_GET_FW_VER; ret = cgx_intf_req(cgx, lmac, cmd, &scr0.u, 1); if (ret) return -1; scr0.u >>= 9; *ver = scr0.u & 0xFFFF; return 0; } int cgx_intf_get_link_sts(u8 cgx, u8 lmac, u64 *lnk_sts) { union cgx_scratchx0 scr0; int ret; union cgx_cmd_s cmd; cmd.cmd.id = CGX_CMD_GET_LINK_STS; ret = cgx_intf_req(cgx, lmac, cmd, &scr0.u, 1); if (ret) return -1; scr0.u >>= 9; /* pass the same format as cgx_lnk_sts_s * err_type:10, speed:4, full_duplex:1, link_up:1 */ *lnk_sts = scr0.u & 0xFFFF; return 0; } int cgx_intf_link_up_dwn(u8 cgx, u8 lmac, u8 up_dwn, u64 *lnk_sts) { union cgx_scratchx0 scr0; int ret; union cgx_cmd_s cmd; cmd.cmd.id = up_dwn ? CGX_CMD_LINK_BRING_UP : CGX_CMD_LINK_BRING_DOWN; ret = cgx_intf_req(cgx, lmac, cmd, &scr0.u, 1); if (ret) return -1; scr0.u >>= 9; /* pass the same format as cgx_lnk_sts_s * err_type:10, speed:4, full_duplex:1, link_up:1 */ *lnk_sts = scr0.u & 0xFFFF; return 0; } void cgx_intf_shutdown(void) { union cgx_scratchx0 scr0; union cgx_cmd_s cmd; cmd.cmd.id = CGX_CMD_INTF_SHUTDOWN; cgx_intf_req(0, 0, cmd, &scr0.u, 1); } int cgx_intf_prbs(u8 qlm, u8 mode, u32 time, u8 lane) { union cgx_scratchx0 scr0; int ret; union cgx_cmd_s cmd; cmd.cmd.id = CGX_CMD_PRBS; cmd.prbs_args.qlm = qlm; cmd.prbs_args.mode = mode; cmd.prbs_args.time = time; cmd.prbs_args.lane = lane; ret = cgx_intf_req(0, 0, cmd, &scr0.u, 0); if (ret) return -1; return 0; } enum cgx_mode { MODE_10G_C2C, MODE_10G_C2M, MODE_10G_KR, MODE_25G_C2C, MODE_25G_2_C2C, MODE_50G_C2C, MODE_50G_4_C2C }; static char intf_speed_to_str[][8] = { "10M", "100M", "1G", "2.5G", "5G", "10G", "20G", "25G", "40G", "50G", "80G", "100G", }; static void mode_to_args(int mode, struct cgx_mode_change_args *args) { args->an = 0; args->duplex = 0; args->port = 0; switch (mode) { case MODE_10G_C2C: args->speed = CGX_LINK_10G; args->mode = BIT_ULL(CGX_MODE_10G_C2C_BIT); break; case MODE_10G_C2M: args->speed = CGX_LINK_10G; args->mode = BIT_ULL(CGX_MODE_10G_C2M_BIT); break; case MODE_10G_KR: args->speed = CGX_LINK_10G; args->mode = BIT_ULL(CGX_MODE_10G_KR_BIT); args->an = 1; break; case MODE_25G_C2C: args->speed = CGX_LINK_25G; args->mode = BIT_ULL(CGX_MODE_25G_C2C_BIT); break; case MODE_25G_2_C2C: args->speed = CGX_LINK_25G; args->mode = BIT_ULL(CGX_MODE_25G_2_C2C_BIT); break; case MODE_50G_C2C: args->speed = CGX_LINK_50G; args->mode = BIT_ULL(CGX_MODE_50G_C2C_BIT); break; case MODE_50G_4_C2C: args->speed = CGX_LINK_50G; args->mode = BIT_ULL(CGX_MODE_50G_4_C2C_BIT); } } int cgx_intf_set_mode(struct udevice *ethdev, int mode) { struct rvu_pf *rvu = dev_get_priv(ethdev); struct nix *nix = rvu->nix; union cgx_scratchx0 scr0; int ret; union cgx_cmd_s cmd; cmd.cmd.id = CGX_CMD_MODE_CHANGE; mode_to_args(mode, &cmd.mode_change_args); ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id, cmd, &scr0.u, 0); if (ret) { printf("Mode change command failed for %s\n", ethdev->name); return -1; } cmd.cmd.id = CGX_CMD_GET_LINK_STS; ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id, cmd, &scr0.u, 1); if (ret) { printf("Get Link Status failed for %s\n", ethdev->name); return -1; } printf("Current Link Status: "); if (scr0.s.link_sts.speed) { printf("%s\n", intf_speed_to_str[scr0.s.link_sts.speed]); switch (scr0.s.link_sts.fec) { case 0: printf("FEC_NONE\n"); break; case 1: printf("FEC_BASE_R\n"); break; case 2: printf("FEC_RS\n"); break; } printf("Auto Negotiation %sabled\n", scr0.s.link_sts.an ? "En" : "Dis"); printf("%s Duplex\n", scr0.s.link_sts.full_duplex ? "Full" : "Half"); } else { printf("Down\n"); } return 0; } int cgx_intf_get_mode(struct udevice *ethdev) { struct rvu_pf *rvu = dev_get_priv(ethdev); struct nix *nix = rvu->nix; union cgx_scratchx0 scr0; int ret; union cgx_cmd_s cmd; cmd.cmd.id = CGX_CMD_GET_LINK_STS; ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id, cmd, &scr0.u, 1); if (ret) { printf("Get link status failed for %s\n", ethdev->name); return -1; } printf("Current Interface Mode: "); switch (scr0.s.link_sts.mode) { case CGX_MODE_10G_C2C_BIT: printf("10G_C2C\n"); break; case CGX_MODE_10G_C2M_BIT: printf("10G_C2M\n"); break; case CGX_MODE_10G_KR_BIT: printf("10G_KR\n"); break; case CGX_MODE_25G_C2C_BIT: printf("25G_C2C\n"); break; case CGX_MODE_25G_2_C2C_BIT: printf("25G_2_C2C\n"); break; case CGX_MODE_50G_C2C_BIT: printf("50G_C2C\n"); break; case CGX_MODE_50G_4_C2C_BIT: printf("50G_4_C2C\n"); break; default: printf("Unknown\n"); break; } return 0; } int cgx_intf_get_fec(struct udevice *ethdev) { struct rvu_pf *rvu = dev_get_priv(ethdev); struct nix *nix = rvu->nix; union cgx_scratchx0 scr0; int ret; union cgx_cmd_s cmd; cmd.cmd.id = CGX_CMD_GET_SUPPORTED_FEC; ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id, cmd, &scr0.u, 1); if (ret) { printf("Get supported FEC failed for %s\n", ethdev->name); return -1; } printf("Supported FEC type: "); switch (scr0.s.supported_fec.fec) { case 0: printf("FEC_NONE\n"); break; case 1: printf("FEC_BASE_R\n"); break; case 2: printf("FEC_RS\n"); break; case 3: printf("FEC_BASE_R FEC_RS\n"); break; } cmd.cmd.id = CGX_CMD_GET_LINK_STS; ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id, cmd, &scr0.u, 1); if (ret) { printf("Get active fec failed for %s\n", ethdev->name); return -1; } printf("Active FEC type: "); switch (scr0.s.link_sts.fec) { case 0: printf("FEC_NONE\n"); break; case 1: printf("FEC_BASE_R\n"); break; case 2: printf("FEC_RS\n"); break; } return 0; } int cgx_intf_set_fec(struct udevice *ethdev, int type) { struct rvu_pf *rvu = dev_get_priv(ethdev); struct nix *nix = rvu->nix; union cgx_scratchx0 scr0; int ret; union cgx_cmd_s cmd; cmd.cmd.id = CGX_CMD_SET_FEC; cmd.fec_args.fec = type; ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id, cmd, &scr0.u, 0); if (ret) { printf("Set FEC type %d failed for %s\n", type, ethdev->name); return -1; } return 0; } int cgx_intf_get_phy_mod_type(struct udevice *ethdev) { struct rvu_pf *rvu = dev_get_priv(ethdev); struct nix *nix = rvu->nix; union cgx_scratchx0 scr0; int ret; union cgx_cmd_s cmd; cmd.cmd.id = CGX_CMD_GET_PHY_MOD_TYPE; ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id, cmd, &scr0.u, 1); if (ret) { printf("Get PHYMOD type failed for %s\n", ethdev->name); return -1; } printf("Current phy mod type %s\n", scr0.s.phy_mod_type.mod ? "PAM4" : "NRZ"); return 0; } int cgx_intf_set_phy_mod_type(struct udevice *ethdev, int type) { struct rvu_pf *rvu = dev_get_priv(ethdev); struct nix *nix = rvu->nix; union cgx_scratchx0 scr0; int ret; union cgx_cmd_s cmd; cmd.cmd.id = CGX_CMD_SET_PHY_MOD_TYPE; cmd.phy_mod_args.mod = type; ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id, cmd, &scr0.u, 0); if (ret) { printf("Set PHYMOD type %d failed for %s\n", type, ethdev->name); return -1; } return 0; } int cgx_intf_set_an_lbk(struct udevice *ethdev, int enable) { struct rvu_pf *rvu = dev_get_priv(ethdev); struct nix *nix = rvu->nix; union cgx_scratchx0 scr0; int ret; union cgx_cmd_s cmd; cmd.cmd.id = CGX_CMD_AN_LOOPBACK; cmd.cmd_args.enable = enable; ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id, cmd, &scr0.u, 0); if (ret) { printf("Set AN loopback command failed on %s\n", ethdev->name); return -1; } printf("AN loopback %s for %s\n", enable ? "set" : "clear", ethdev->name); return 0; } int cgx_intf_get_ignore(struct udevice *ethdev, int cgx, int lmac) { struct rvu_pf *rvu; struct nix *nix; union cgx_scratchx0 scr0; int ret, cgx_id = cgx, lmac_id = lmac; union cgx_cmd_s cmd; if (ethdev) { rvu = dev_get_priv(ethdev); nix = rvu->nix; cgx_id = nix->lmac->cgx->cgx_id; lmac_id = nix->lmac->lmac_id; } cmd.cmd.id = CGX_CMD_GET_PERSIST_IGNORE; ret = cgx_intf_req(cgx_id, lmac_id, cmd, &scr0.u, 1); if (ret) { if (ethdev) printf("Get ignore command failed for %s\n", ethdev->name); else printf("Get ignore command failed for CGX%d LMAC%d\n", cgx_id, lmac_id); return -1; } if (ethdev) printf("Persist settings %signored for %s\n", scr0.s.persist.ignore ? "" : "not ", ethdev->name); else printf("Persist settings %signored for CGX%d LMAC%d\n", scr0.s.persist.ignore ? "" : "not ", cgx_id, lmac_id); return 0; } int cgx_intf_set_ignore(struct udevice *ethdev, int cgx, int lmac, int ignore) { struct rvu_pf *rvu; struct nix *nix; union cgx_scratchx0 scr0; int ret, cgx_id = cgx, lmac_id = lmac; union cgx_cmd_s cmd; if (ethdev) { rvu = dev_get_priv(ethdev); nix = rvu->nix; cgx_id = nix->lmac->cgx->cgx_id; lmac_id = nix->lmac->lmac_id; } cmd.cmd.id = CGX_CMD_SET_PERSIST_IGNORE; cmd.persist_args.ignore = ignore; ret = cgx_intf_req(cgx_id, lmac_id, cmd, &scr0.u, 0); if (ret) { if (ethdev) printf("Set ignore command failed for %s\n", ethdev->name); else printf("Set ignore command failed for CGX%d LMAC%d\n", cgx_id, lmac_id); return -1; } return 0; } int cgx_intf_set_macaddr(struct udevice *ethdev) { struct rvu_pf *rvu = dev_get_priv(ethdev); struct nix *nix = rvu->nix; union cgx_scratchx0 scr0; int ret; union cgx_cmd_s cmd; u64 mac, tmp; memcpy((void *)&tmp, nix->lmac->mac_addr, 6); mac = swab64(tmp) >> 16; cmd.cmd.id = CGX_CMD_SET_MAC_ADDR; cmd.mac_args.addr = mac; cmd.mac_args.pf_id = rvu->pfid; ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id, cmd, &scr0.u, 0); if (ret) { printf("Set user mac addr failed for %s\n", ethdev->name); return -1; } return 0; } int cgx_intf_display_eye(u8 qlm, u8 lane) { union cgx_scratchx0 scr0; int ret; union cgx_cmd_s cmd; cmd.cmd.id = CGX_CMD_DISPLAY_EYE; cmd.dsp_eye_args.qlm = qlm; cmd.dsp_eye_args.lane = lane; ret = cgx_intf_req(0, 0, cmd, &scr0.u, 0); if (ret) return -1; return 0; } int cgx_intf_display_serdes(u8 qlm, u8 lane) { union cgx_scratchx0 scr0; int ret; union cgx_cmd_s cmd; cmd.cmd.id = CGX_CMD_DISPLAY_SERDES; cmd.dsp_eye_args.qlm = qlm; cmd.dsp_eye_args.lane = lane; ret = cgx_intf_req(0, 0, cmd, &scr0.u, 0); if (ret) return -1; return 0; }