diff options
Diffstat (limited to 'roms/u-boot/drivers/net/octeontx2/cgx_intf.c')
-rw-r--r-- | roms/u-boot/drivers/net/octeontx2/cgx_intf.c | 715 |
1 files changed, 715 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/net/octeontx2/cgx_intf.c b/roms/u-boot/drivers/net/octeontx2/cgx_intf.c new file mode 100644 index 000000000..37d9a2bb7 --- /dev/null +++ b/roms/u-boot/drivers/net/octeontx2/cgx_intf.c @@ -0,0 +1,715 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell International Ltd. + */ + +#include <dm.h> +#include <errno.h> +#include <malloc.h> +#include <misc.h> +#include <net.h> + +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/list.h> + +#include <asm/arch/board.h> +#include <asm/io.h> + +#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; +} |