diff options
Diffstat (limited to 'drivers/net/octeontx2/cgx.c')
-rw-r--r-- | drivers/net/octeontx2/cgx.c | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/drivers/net/octeontx2/cgx.c b/drivers/net/octeontx2/cgx.c new file mode 100644 index 00000000000..ff2ebc25ce1 --- /dev/null +++ b/drivers/net/octeontx2/cgx.c @@ -0,0 +1,296 @@ +// 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 <pci_ids.h> +#include <linux/list.h> +#include <asm/arch/board.h> +#include <asm/arch/csrs/csrs-cgx.h> +#include <asm/io.h> + +#include "cgx.h" + +char lmac_type_to_str[][8] = { + "SGMII", + "XAUI", + "RXAUI", + "10G_R", + "40G_R", + "RGMII", + "QSGMII", + "25G_R", + "50G_R", + "100G_R", + "USXGMII", +}; + +char lmac_speed_to_str[][8] = { + "0", + "10M", + "100M", + "1G", + "2.5G", + "5G", + "10G", + "20G", + "25G", + "40G", + "50G", + "80G", + "100G", +}; + +/** + * Given an LMAC/PF instance number, return the lmac + * Per design, each PF has only one LMAC mapped. + * + * @param instance instance to find + * + * @return pointer to lmac data structure or NULL if not found + */ +struct lmac *nix_get_cgx_lmac(int lmac_instance) +{ + struct cgx *cgx; + struct udevice *dev; + int i, idx, err; + + for (i = 0; i < CGX_PER_NODE; i++) { + err = dm_pci_find_device(PCI_VENDOR_ID_CAVIUM, + PCI_DEVICE_ID_OCTEONTX2_CGX, i, + &dev); + if (err) + continue; + + cgx = dev_get_priv(dev); + debug("%s udev %p cgx %p instance %d\n", __func__, dev, cgx, + lmac_instance); + for (idx = 0; idx < cgx->lmac_count; idx++) { + if (cgx->lmac[idx]->instance == lmac_instance) + return cgx->lmac[idx]; + } + } + return NULL; +} + +void cgx_lmac_mac_filter_clear(struct lmac *lmac) +{ + union cgxx_cmrx_rx_dmac_ctl0 dmac_ctl0; + union cgxx_cmr_rx_dmacx_cam0 dmac_cam0; + void *reg_addr; + + dmac_cam0.u = 0x0; + reg_addr = lmac->cgx->reg_base + + CGXX_CMR_RX_DMACX_CAM0(lmac->lmac_id * 8); + writeq(dmac_cam0.u, reg_addr); + debug("%s: reg %p dmac_cam0 %llx\n", __func__, reg_addr, dmac_cam0.u); + + dmac_ctl0.u = 0x0; + dmac_ctl0.s.bcst_accept = 1; + dmac_ctl0.s.mcst_mode = 1; + dmac_ctl0.s.cam_accept = 0; + reg_addr = lmac->cgx->reg_base + + CGXX_CMRX_RX_DMAC_CTL0(lmac->lmac_id); + writeq(dmac_ctl0.u, reg_addr); + debug("%s: reg %p dmac_ctl0 %llx\n", __func__, reg_addr, dmac_ctl0.u); +} + +void cgx_lmac_mac_filter_setup(struct lmac *lmac) +{ + union cgxx_cmrx_rx_dmac_ctl0 dmac_ctl0; + union cgxx_cmr_rx_dmacx_cam0 dmac_cam0; + u64 mac, tmp; + void *reg_addr; + + memcpy((void *)&tmp, lmac->mac_addr, 6); + debug("%s: tmp %llx\n", __func__, tmp); + debug("%s: swab tmp %llx\n", __func__, swab64(tmp)); + mac = swab64(tmp) >> 16; + debug("%s: mac %llx\n", __func__, mac); + dmac_cam0.u = 0x0; + dmac_cam0.s.id = lmac->lmac_id; + dmac_cam0.s.adr = mac; + dmac_cam0.s.en = 1; + reg_addr = lmac->cgx->reg_base + + CGXX_CMR_RX_DMACX_CAM0(lmac->lmac_id * 8); + writeq(dmac_cam0.u, reg_addr); + debug("%s: reg %p dmac_cam0 %llx\n", __func__, reg_addr, dmac_cam0.u); + dmac_ctl0.u = 0x0; + dmac_ctl0.s.bcst_accept = 1; + dmac_ctl0.s.mcst_mode = 0; + dmac_ctl0.s.cam_accept = 1; + reg_addr = lmac->cgx->reg_base + + CGXX_CMRX_RX_DMAC_CTL0(lmac->lmac_id); + writeq(dmac_ctl0.u, reg_addr); + debug("%s: reg %p dmac_ctl0 %llx\n", __func__, reg_addr, dmac_ctl0.u); +} + +int cgx_lmac_set_pkind(struct lmac *lmac, u8 lmac_id, int pkind) +{ + cgx_write(lmac->cgx, lmac_id, CGXX_CMRX_RX_ID_MAP(0), + (pkind & 0x3f)); + return 0; +} + +int cgx_lmac_link_status(struct lmac *lmac, int lmac_id, u64 *status) +{ + int ret = 0; + + ret = cgx_intf_get_link_sts(lmac->cgx->cgx_id, lmac_id, status); + if (ret) { + debug("%s request failed for cgx%d lmac%d\n", + __func__, lmac->cgx->cgx_id, lmac->lmac_id); + ret = -1; + } + return ret; +} + +int cgx_lmac_rx_tx_enable(struct lmac *lmac, int lmac_id, bool enable) +{ + struct cgx *cgx = lmac->cgx; + union cgxx_cmrx_config cmrx_config; + + if (!cgx || lmac_id >= cgx->lmac_count) + return -ENODEV; + + cmrx_config.u = cgx_read(cgx, lmac_id, CGXX_CMRX_CONFIG(0)); + cmrx_config.s.data_pkt_rx_en = + cmrx_config.s.data_pkt_tx_en = enable ? 1 : 0; + cgx_write(cgx, lmac_id, CGXX_CMRX_CONFIG(0), cmrx_config.u); + return 0; +} + +int cgx_lmac_link_enable(struct lmac *lmac, int lmac_id, bool enable, + u64 *status) +{ + int ret = 0; + + ret = cgx_intf_link_up_dwn(lmac->cgx->cgx_id, lmac_id, enable, + status); + if (ret) { + debug("%s request failed for cgx%d lmac%d\n", + __func__, lmac->cgx->cgx_id, lmac->lmac_id); + ret = -1; + } + return ret; +} + +int cgx_lmac_internal_loopback(struct lmac *lmac, int lmac_id, bool enable) +{ + struct cgx *cgx = lmac->cgx; + union cgxx_cmrx_config cmrx_cfg; + union cgxx_gmp_pcs_mrx_control mrx_control; + union cgxx_spux_control1 spux_control1; + enum lmac_type lmac_type; + + if (!cgx || lmac_id >= cgx->lmac_count) + return -ENODEV; + + cmrx_cfg.u = cgx_read(cgx, lmac_id, CGXX_CMRX_CONFIG(0)); + lmac_type = cmrx_cfg.s.lmac_type; + if (lmac_type == LMAC_MODE_SGMII || lmac_type == LMAC_MODE_QSGMII) { + mrx_control.u = cgx_read(cgx, lmac_id, + CGXX_GMP_PCS_MRX_CONTROL(0)); + mrx_control.s.loopbck1 = enable ? 1 : 0; + cgx_write(cgx, lmac_id, CGXX_GMP_PCS_MRX_CONTROL(0), + mrx_control.u); + } else { + spux_control1.u = cgx_read(cgx, lmac_id, + CGXX_SPUX_CONTROL1(0)); + spux_control1.s.loopbck = enable ? 1 : 0; + cgx_write(cgx, lmac_id, CGXX_SPUX_CONTROL1(0), + spux_control1.u); + } + return 0; +} + +static int cgx_lmac_init(struct cgx *cgx) +{ + struct lmac *lmac; + union cgxx_cmrx_config cmrx_cfg; + static int instance = 1; + int i; + + cgx->lmac_count = cgx_read(cgx, 0, CGXX_CMR_RX_LMACS()); + debug("%s: Found %d lmacs for cgx %d@%p\n", __func__, cgx->lmac_count, + cgx->cgx_id, cgx->reg_base); + + for (i = 0; i < cgx->lmac_count; i++) { + lmac = calloc(1, sizeof(*lmac)); + if (!lmac) + return -ENOMEM; + lmac->instance = instance++; + snprintf(lmac->name, sizeof(lmac->name), "cgx_fwi_%d_%d", + cgx->cgx_id, i); + /* Get LMAC type */ + cmrx_cfg.u = cgx_read(cgx, i, CGXX_CMRX_CONFIG(0)); + lmac->lmac_type = cmrx_cfg.s.lmac_type; + + lmac->lmac_id = i; + lmac->cgx = cgx; + cgx->lmac[i] = lmac; + debug("%s: map id %d to lmac %p (%s), type:%d instance %d\n", + __func__, i, lmac, lmac->name, lmac->lmac_type, + lmac->instance); + lmac->init_pend = 1; + printf("CGX%d LMAC%d [%s]\n", lmac->cgx->cgx_id, + lmac->lmac_id, lmac_type_to_str[lmac->lmac_type]); + octeontx2_board_get_mac_addr((lmac->instance - 1), + lmac->mac_addr); + debug("%s: MAC %pM\n", __func__, lmac->mac_addr); + cgx_lmac_mac_filter_setup(lmac); + } + return 0; +} + +int cgx_probe(struct udevice *dev) +{ + struct cgx *cgx = dev_get_priv(dev); + int err; + + cgx->reg_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, + PCI_REGION_MEM); + cgx->dev = dev; + cgx->cgx_id = ((u64)(cgx->reg_base) >> 24) & 0x7; + + debug("%s CGX BAR %p, id: %d\n", __func__, cgx->reg_base, + cgx->cgx_id); + debug("%s CGX %p, udev: %p\n", __func__, cgx, dev); + + err = cgx_lmac_init(cgx); + + return err; +} + +int cgx_remove(struct udevice *dev) +{ + struct cgx *cgx = dev_get_priv(dev); + int i; + + debug("%s: cgx remove reg_base %p cgx_id %d", + __func__, cgx->reg_base, cgx->cgx_id); + for (i = 0; i < cgx->lmac_count; i++) + cgx_lmac_mac_filter_clear(cgx->lmac[i]); + + return 0; +} + +U_BOOT_DRIVER(cgx) = { + .name = "cgx", + .id = UCLASS_MISC, + .probe = cgx_probe, + .remove = cgx_remove, + .priv_auto_alloc_size = sizeof(struct cgx), +}; + +static struct pci_device_id cgx_supported[] = { + {PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_CGX) }, + {} +}; + +U_BOOT_PCI_DEVICE(cgx, cgx_supported); |