From 05c7606ac928ae90451e0c0b2517d13e40452b59 Mon Sep 17 00:00:00 2001 From: Suneel Garapati Date: Wed, 26 Aug 2020 14:37:22 +0200 Subject: mtd: nand: Add NAND controller driver for OcteonTX Adds support for NAND controllers found on OcteonTX or OcteonTX2 SoC platforms. Also includes driver to support Hardware ECC using BCH HW engine found on these platforms. Signed-off-by: Aaron Williams Signed-off-by: Suneel Garapati Signed-off-by: Stefan Roese --- drivers/mtd/nand/raw/Kconfig | 16 + drivers/mtd/nand/raw/Makefile | 2 + drivers/mtd/nand/raw/octeontx_bch.c | 425 ++++++ drivers/mtd/nand/raw/octeontx_bch.h | 131 ++ drivers/mtd/nand/raw/octeontx_bch_regs.h | 167 +++ drivers/mtd/nand/raw/octeontx_nand.c | 2257 ++++++++++++++++++++++++++++++ 6 files changed, 2998 insertions(+) create mode 100644 drivers/mtd/nand/raw/octeontx_bch.c create mode 100644 drivers/mtd/nand/raw/octeontx_bch.h create mode 100644 drivers/mtd/nand/raw/octeontx_bch_regs.h create mode 100644 drivers/mtd/nand/raw/octeontx_nand.c diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index df4cbd52cf..cd7e598aa8 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -291,6 +291,22 @@ config NAND_ZYNQ_USE_BOOTLOADER1_TIMINGS This flag prevent U-boot reconfigure NAND flash controller and reuse the NAND timing from 1st stage bootloader. +config NAND_OCTEONTX + bool "Support for OcteonTX NAND controller" + select SYS_NAND_SELF_INIT + imply CMD_NAND + help + This enables Nand flash controller hardware found on the OcteonTX + processors. + +config NAND_OCTEONTX_HW_ECC + bool "Support Hardware ECC for OcteonTX NAND controller" + depends on NAND_OCTEONTX + default y + help + This enables Hardware BCH engine found on the OcteonTX processors to + support ECC for NAND flash controller. + config NAND_STM32_FMC2 bool "Support for NAND controller on STM32MP SoCs" depends on ARCH_STM32MP diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index 9337f6482e..24c51b6924 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -58,6 +58,8 @@ obj-$(CONFIG_NAND_VF610_NFC) += vf610_nfc.o obj-$(CONFIG_NAND_MXC) += mxc_nand.o obj-$(CONFIG_NAND_MXS) += mxs_nand.o obj-$(CONFIG_NAND_MXS_DT) += mxs_nand_dt.o +obj-$(CONFIG_NAND_OCTEONTX) += octeontx_nand.o +obj-$(CONFIG_NAND_OCTEONTX_HW_ECC) += octeontx_bch.o obj-$(CONFIG_NAND_PXA3XX) += pxa3xx_nand.o obj-$(CONFIG_NAND_SPEAR) += spr_nand.o obj-$(CONFIG_TEGRA_NAND) += tegra_nand.o diff --git a/drivers/mtd/nand/raw/octeontx_bch.c b/drivers/mtd/nand/raw/octeontx_bch.c new file mode 100644 index 0000000000..693706257c --- /dev/null +++ b/drivers/mtd/nand/raw/octeontx_bch.c @@ -0,0 +1,425 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell International Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "octeontx_bch.h" + +#ifdef DEBUG +# undef CONFIG_LOGLEVEL +# define CONFIG_LOGLEVEL 8 +#endif + +LIST_HEAD(octeontx_bch_devices); +static unsigned int num_vfs = BCH_NR_VF; +static void *bch_pf; +static void *bch_vf; +static void *token; +static bool bch_pf_initialized; +static bool bch_vf_initialized; + +static int pci_enable_sriov(struct udevice *dev, int nr_virtfn) +{ + int ret; + + ret = pci_sriov_init(dev, nr_virtfn); + if (ret) + printf("%s(%s): pci_sriov_init returned %d\n", __func__, + dev->name, ret); + return ret; +} + +void *octeontx_bch_getv(void) +{ + if (!bch_vf) + return NULL; + if (bch_vf_initialized && bch_pf_initialized) + return bch_vf; + else + return NULL; +} + +void octeontx_bch_putv(void *token) +{ + bch_vf_initialized = !!token; + bch_vf = token; +} + +void *octeontx_bch_getp(void) +{ + return token; +} + +void octeontx_bch_putp(void *token) +{ + bch_pf = token; + bch_pf_initialized = !!token; +} + +static int do_bch_init(struct bch_device *bch) +{ + return 0; +} + +static void bch_reset(struct bch_device *bch) +{ + writeq(1, bch->reg_base + BCH_CTL); + mdelay(2); +} + +static void bch_disable(struct bch_device *bch) +{ + writeq(~0ull, bch->reg_base + BCH_ERR_INT_ENA_W1C); + writeq(~0ull, bch->reg_base + BCH_ERR_INT); + bch_reset(bch); +} + +static u32 bch_check_bist_status(struct bch_device *bch) +{ + return readq(bch->reg_base + BCH_BIST_RESULT); +} + +static int bch_device_init(struct bch_device *bch) +{ + u64 bist; + int rc; + + debug("%s: Resetting...\n", __func__); + /* Reset the PF when probed first */ + bch_reset(bch); + + debug("%s: Checking BIST...\n", __func__); + /* Check BIST status */ + bist = (u64)bch_check_bist_status(bch); + if (bist) { + dev_err(dev, "BCH BIST failed with code 0x%llx\n", bist); + return -ENODEV; + } + + /* Get max VQs/VFs supported by the device */ + + bch->max_vfs = pci_sriov_get_totalvfs(bch->dev); + debug("%s: %d vfs\n", __func__, bch->max_vfs); + if (num_vfs > bch->max_vfs) { + dev_warn(dev, "Num of VFs to enable %d is greater than max available. Enabling %d VFs.\n", + num_vfs, bch->max_vfs); + num_vfs = bch->max_vfs; + } + bch->vfs_enabled = bch->max_vfs; + /* Get number of VQs/VFs to be enabled */ + /* TODO: Get CLK frequency */ + /* Reset device parameters */ + + debug("%s: Doing initialization\n", __func__); + rc = do_bch_init(bch); + + return rc; +} + +static int bch_sriov_configure(struct udevice *dev, int numvfs) +{ + struct bch_device *bch = dev_get_priv(dev); + int ret = -EBUSY; + + debug("%s(%s, %d), bch: %p, vfs_in_use: %d, enabled: %d\n", __func__, + dev->name, numvfs, bch, bch->vfs_in_use, bch->vfs_enabled); + if (bch->vfs_in_use) + goto exit; + + ret = 0; + + if (numvfs > 0) { + debug("%s: Enabling sriov\n", __func__); + ret = pci_enable_sriov(dev, numvfs); + if (ret == 0) { + bch->flags |= BCH_FLAG_SRIOV_ENABLED; + ret = numvfs; + bch->vfs_enabled = numvfs; + } + } + + debug("VFs enabled: %d\n", ret); +exit: + debug("%s: Returning %d\n", __func__, ret); + return ret; +} + +static int octeontx_pci_bchpf_probe(struct udevice *dev) +{ + struct bch_device *bch; + int ret; + + debug("%s(%s)\n", __func__, dev->name); + bch = dev_get_priv(dev); + if (!bch) + return -ENOMEM; + + bch->reg_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, PCI_REGION_MEM); + bch->dev = dev; + + debug("%s: base address: %p\n", __func__, bch->reg_base); + ret = bch_device_init(bch); + if (ret) { + printf("%s(%s): init returned %d\n", __func__, dev->name, ret); + return ret; + } + INIT_LIST_HEAD(&bch->list); + list_add(&bch->list, &octeontx_bch_devices); + token = (void *)dev; + + debug("%s: Configuring SRIOV\n", __func__); + bch_sriov_configure(dev, num_vfs); + debug("%s: Done.\n", __func__); + octeontx_bch_putp(bch); + + return 0; +} + +static const struct pci_device_id octeontx_bchpf_pci_id_table[] = { + { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_BCH) }, + {}, +}; + +static const struct pci_device_id octeontx_bchvf_pci_id_table[] = { + { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_BCHVF)}, + {}, +}; + +/** + * Given a data block calculate the ecc data and fill in the response + * + * @param[in] block 8-byte aligned pointer to data block to calculate ECC + * @param block_size Size of block in bytes, must be a multiple of two. + * @param bch_level Number of errors that must be corrected. The number of + * parity bytes is equal to ((15 * bch_level) + 7) / 8. + * Must be 4, 8, 16, 24, 32, 40, 48, 56, 60 or 64. + * @param[out] ecc 8-byte aligned pointer to where ecc data should go + * @param[in] resp pointer to where responses will be written. + * + * @return Zero on success, negative on failure. + */ +int octeontx_bch_encode(struct bch_vf *vf, dma_addr_t block, u16 block_size, + u8 bch_level, dma_addr_t ecc, dma_addr_t resp) +{ + union bch_cmd cmd; + int rc; + + memset(&cmd, 0, sizeof(cmd)); + cmd.s.cword.ecc_gen = eg_gen; + cmd.s.cword.ecc_level = bch_level; + cmd.s.cword.size = block_size; + + cmd.s.oword.ptr = ecc; + cmd.s.iword.ptr = block; + cmd.s.rword.ptr = resp; + rc = octeontx_cmd_queue_write(QID_BCH, 1, + sizeof(cmd) / sizeof(uint64_t), cmd.u); + if (rc) + return -1; + + octeontx_bch_write_doorbell(1, vf); + + return 0; +} + +/** + * Given a data block and ecc data correct the data block + * + * @param[in] block_ecc_in 8-byte aligned pointer to data block with ECC + * data concatenated to the end to correct + * @param block_size Size of block in bytes, must be a multiple of + * two. + * @param bch_level Number of errors that must be corrected. The + * number of parity bytes is equal to + * ((15 * bch_level) + 7) / 8. + * Must be 4, 8, 16, 24, 32, 40, 48, 56, 60 or 64. + * @param[out] block_out 8-byte aligned pointer to corrected data buffer. + * This should not be the same as block_ecc_in. + * @param[in] resp pointer to where responses will be written. + * + * @return Zero on success, negative on failure. + */ + +int octeontx_bch_decode(struct bch_vf *vf, dma_addr_t block_ecc_in, + u16 block_size, u8 bch_level, + dma_addr_t block_out, dma_addr_t resp) +{ + union bch_cmd cmd; + int rc; + + memset(&cmd, 0, sizeof(cmd)); + cmd.s.cword.ecc_gen = eg_correct; + cmd.s.cword.ecc_level = bch_level; + cmd.s.cword.size = block_size; + + cmd.s.oword.ptr = block_out; + cmd.s.iword.ptr = block_ecc_in; + cmd.s.rword.ptr = resp; + rc = octeontx_cmd_queue_write(QID_BCH, 1, + sizeof(cmd) / sizeof(uint64_t), cmd.u); + if (rc) + return -1; + + octeontx_bch_write_doorbell(1, vf); + return 0; +} +EXPORT_SYMBOL(octeontx_bch_decode); + +int octeontx_bch_wait(struct bch_vf *vf, union bch_resp *resp, + dma_addr_t handle) +{ + ulong start = get_timer(0); + + __iormb(); /* HW is updating *resp */ + while (!resp->s.done && get_timer(start) < 10) + __iormb(); /* HW is updating *resp */ + + if (resp->s.done) + return 0; + + return -ETIMEDOUT; +} + +struct bch_q octeontx_bch_q[QID_MAX]; + +static int octeontx_cmd_queue_initialize(struct udevice *dev, int queue_id, + int max_depth, int fpa_pool, + int pool_size) +{ + /* some params are for later merge with CPT or cn83xx */ + struct bch_q *q = &octeontx_bch_q[queue_id]; + unsigned long paddr; + u64 *chunk_buffer; + int chunk = max_depth + 1; + int i, size; + + if ((unsigned int)queue_id >= QID_MAX) + return -EINVAL; + if (max_depth & chunk) /* must be 2^N - 1 */ + return -EINVAL; + + size = NQS * chunk * sizeof(u64); + chunk_buffer = dma_alloc_coherent(size, &paddr); + if (!chunk_buffer) + return -ENOMEM; + + q->base_paddr = paddr; + q->dev = dev; + q->index = 0; + q->max_depth = max_depth; + q->pool_size_m1 = pool_size; + q->base_vaddr = chunk_buffer; + + for (i = 0; i < NQS; i++) { + u64 *ixp; + int inext = (i + 1) * chunk - 1; + int j = (i + 1) % NQS; + int jnext = j * chunk; + dma_addr_t jbase = q->base_paddr + jnext * sizeof(u64); + + ixp = &chunk_buffer[inext]; + *ixp = jbase; + } + + return 0; +} + +static int octeontx_pci_bchvf_probe(struct udevice *dev) +{ + struct bch_vf *vf; + union bch_vqx_ctl ctl; + union bch_vqx_cmd_buf cbuf; + int err; + + debug("%s(%s)\n", __func__, dev->name); + vf = dev_get_priv(dev); + if (!vf) + return -ENOMEM; + + vf->dev = dev; + + /* Map PF's configuration registers */ + vf->reg_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, PCI_REGION_MEM); + debug("%s: reg base: %p\n", __func__, vf->reg_base); + + err = octeontx_cmd_queue_initialize(dev, QID_BCH, QDEPTH - 1, 0, + sizeof(union bch_cmd) * QDEPTH); + if (err) { + dev_err(dev, "octeontx_cmd_queue_initialize() failed\n"); + goto release; + } + + ctl.u = readq(vf->reg_base + BCH_VQX_CTL(0)); + + cbuf.u = 0; + cbuf.s.ldwb = 1; + cbuf.s.dfb = 1; + cbuf.s.size = QDEPTH; + writeq(cbuf.u, vf->reg_base + BCH_VQX_CMD_BUF(0)); + + writeq(ctl.u, vf->reg_base + BCH_VQX_CTL(0)); + + writeq(octeontx_bch_q[QID_BCH].base_paddr, + vf->reg_base + BCH_VQX_CMD_PTR(0)); + + octeontx_bch_putv(vf); + + debug("%s: bch vf initialization complete\n", __func__); + + if (octeontx_bch_getv()) + return octeontx_pci_nand_deferred_probe(); + + return -1; + +release: + return err; +} + +static int octeontx_pci_bchpf_remove(struct udevice *dev) +{ + struct bch_device *bch = dev_get_priv(dev); + + bch_disable(bch); + return 0; +} + +U_BOOT_DRIVER(octeontx_pci_bchpf) = { + .name = BCHPF_DRIVER_NAME, + .id = UCLASS_MISC, + .probe = octeontx_pci_bchpf_probe, + .remove = octeontx_pci_bchpf_remove, + .priv_auto_alloc_size = sizeof(struct bch_device), + .flags = DM_FLAG_OS_PREPARE, +}; + +U_BOOT_DRIVER(octeontx_pci_bchvf) = { + .name = BCHVF_DRIVER_NAME, + .id = UCLASS_MISC, + .probe = octeontx_pci_bchvf_probe, + .priv_auto_alloc_size = sizeof(struct bch_vf), +}; + +U_BOOT_PCI_DEVICE(octeontx_pci_bchpf, octeontx_bchpf_pci_id_table); +U_BOOT_PCI_DEVICE(octeontx_pci_bchvf, octeontx_bchvf_pci_id_table); diff --git a/drivers/mtd/nand/raw/octeontx_bch.h b/drivers/mtd/nand/raw/octeontx_bch.h new file mode 100644 index 0000000000..3aaa52c264 --- /dev/null +++ b/drivers/mtd/nand/raw/octeontx_bch.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Marvell International Ltd. + */ + +#ifndef __OCTEONTX_BCH_H__ +#define __OCTEONTX_BCH_H__ + +#include "octeontx_bch_regs.h" + +/* flags to indicate the features supported */ +#define BCH_FLAG_SRIOV_ENABLED BIT(1) + +/* + * BCH Registers map for 81xx + */ + +/* PF registers */ +#define BCH_CTL 0x0ull +#define BCH_ERR_CFG 0x10ull +#define BCH_BIST_RESULT 0x80ull +#define BCH_ERR_INT 0x88ull +#define BCH_ERR_INT_W1S 0x90ull +#define BCH_ERR_INT_ENA_W1C 0xA0ull +#define BCH_ERR_INT_ENA_W1S 0xA8ull + +/* VF registers */ +#define BCH_VQX_CTL(z) 0x0ull +#define BCH_VQX_CMD_BUF(z) 0x8ull +#define BCH_VQX_CMD_PTR(z) 0x20ull +#define BCH_VQX_DOORBELL(z) 0x800ull + +#define BCHPF_DRIVER_NAME "octeontx-bchpf" +#define BCHVF_DRIVER_NAME "octeontx-bchvf" + +struct bch_device { + struct list_head list; + u8 max_vfs; + u8 vfs_enabled; + u8 vfs_in_use; + u32 flags; + void __iomem *reg_base; + struct udevice *dev; +}; + +struct bch_vf { + u16 flags; + u8 vfid; + u8 node; + u8 priority; + struct udevice *dev; + void __iomem *reg_base; +}; + +struct buf_ptr { + u8 *vptr; + dma_addr_t dma_addr; + u16 size; +}; + +void *octeontx_bch_getv(void); +void octeontx_bch_putv(void *token); +void *octeontx_bch_getp(void); +void octeontx_bch_putp(void *token); +int octeontx_bch_wait(struct bch_vf *vf, union bch_resp *resp, + dma_addr_t handle); +/** + * Given a data block calculate the ecc data and fill in the response + * + * @param[in] block 8-byte aligned pointer to data block to calculate ECC + * @param block_size Size of block in bytes, must be a multiple of two. + * @param bch_level Number of errors that must be corrected. The number of + * parity bytes is equal to ((15 * bch_level) + 7) / 8. + * Must be 4, 8, 16, 24, 32, 40, 48, 56, 60 or 64. + * @param[out] ecc 8-byte aligned pointer to where ecc data should go + * @param[in] resp pointer to where responses will be written. + * + * @return Zero on success, negative on failure. + */ +int octeontx_bch_encode(struct bch_vf *vf, dma_addr_t block, u16 block_size, + u8 bch_level, dma_addr_t ecc, dma_addr_t resp); + +/** + * Given a data block and ecc data correct the data block + * + * @param[in] block_ecc_in 8-byte aligned pointer to data block with ECC + * data concatenated to the end to correct + * @param block_size Size of block in bytes, must be a multiple of + * two. + * @param bch_level Number of errors that must be corrected. The + * number of parity bytes is equal to + * ((15 * bch_level) + 7) / 8. + * Must be 4, 8, 16, 24, 32, 40, 48, 56, 60 or 64. + * @param[out] block_out 8-byte aligned pointer to corrected data buffer. + * This should not be the same as block_ecc_in. + * @param[in] resp pointer to where responses will be written. + * + * @return Zero on success, negative on failure. + */ + +int octeontx_bch_decode(struct bch_vf *vf, dma_addr_t block_ecc_in, + u16 block_size, u8 bch_level, + dma_addr_t block_out, dma_addr_t resp); + +/** + * Ring the BCH doorbell telling it that new commands are + * available. + * + * @param num_commands Number of new commands + * @param vf virtual function handle + */ +static inline void octeontx_bch_write_doorbell(u64 num_commands, + struct bch_vf *vf) +{ + u64 num_words = num_commands * sizeof(union bch_cmd) / sizeof(uint64_t); + + writeq(num_words, vf->reg_base + BCH_VQX_DOORBELL(0)); +} + +/** + * Since it's possible (and even likely) that the NAND device will be probed + * before the BCH device has been probed, we may need to defer the probing. + * + * In this case, the initial probe returns success but the actual probing + * is deferred until the BCH VF has been probed. + * + * @return 0 for success, otherwise error + */ +int octeontx_pci_nand_deferred_probe(void); + +#endif /* __OCTEONTX_BCH_H__ */ diff --git a/drivers/mtd/nand/raw/octeontx_bch_regs.h b/drivers/mtd/nand/raw/octeontx_bch_regs.h new file mode 100644 index 0000000000..7d34438fec --- /dev/null +++ b/drivers/mtd/nand/raw/octeontx_bch_regs.h @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Marvell International Ltd. + */ + +#ifndef __OCTEONTX_BCH_REGS_H__ +#define __OCTEONTX_BCH_REGS_H__ + +#define BCH_NR_VF 1 + +union bch_cmd { + u64 u[4]; + struct fields { + struct { + u64 size:12; + u64 reserved_12_31:20; + u64 ecc_level:4; + u64 reserved_36_61:26; + u64 ecc_gen:2; + } cword; + struct { + u64 ptr:49; + u64 reserved_49_55:7; + u64 nc:1; + u64 fw:1; + u64 reserved_58_63:6; + } oword; + struct { + u64 ptr:49; + u64 reserved_49_55:7; + u64 nc:1; + u64 reserved_57_63:7; + } iword; + struct { + u64 ptr:49; + u64 reserved_49_63:15; + } rword; + } s; +}; + +enum ecc_gen { + eg_correct, + eg_copy, + eg_gen, + eg_copy3, +}; + +/** Response from BCH instruction */ +union bch_resp { + u16 u16; + struct { + u16 num_errors:7; /** Number of errors in block */ + u16 zero:6; /** Always zero, ignore */ + u16 erased:1; /** Block is erased */ + u16 uncorrectable:1;/** too many bits flipped */ + u16 done:1; /** Block is done */ + } s; +}; + +union bch_vqx_ctl { + u64 u; + struct { + u64 reserved_0:1; + u64 cmd_be:1; + u64 max_read:4; + u64 reserved_6_15:10; + u64 erase_disable:1; + u64 one_cmd:1; + u64 early_term:4; + u64 reserved_22_63:42; + } s; +}; + +union bch_vqx_cmd_buf { + u64 u; + struct { + u64 reserved_0_32:33; + u64 size:13; + u64 dfb:1; + u64 ldwb:1; + u64 reserved_48_63:16; + } s; +}; + +/* keep queue state indexed, even though just one supported here, + * for later generalization to similarly-shaped queues on other Cavium devices + */ +enum { + QID_BCH, + QID_MAX +}; + +struct bch_q { + struct udevice *dev; + int index; + u16 max_depth; + u16 pool_size_m1; + u64 *base_vaddr; + dma_addr_t base_paddr; +}; + +extern struct bch_q octeontx_bch_q[QID_MAX]; + +/* with one dma-mapped area, virt<->phys conversions by +/- (vaddr-paddr) */ +static inline dma_addr_t qphys(int qid, void *v) +{ + struct bch_q *q = &octeontx_bch_q[qid]; + int off = (u8 *)v - (u8 *)q->base_vaddr; + + return q->base_paddr + off; +} + +#define octeontx_ptr_to_phys(v) qphys(QID_BCH, (v)) + +static inline void *qvirt(int qid, dma_addr_t p) +{ + struct bch_q *q = &octeontx_bch_q[qid]; + int off = p - q->base_paddr; + + return q->base_vaddr + off; +} + +#define octeontx_phys_to_ptr(p) qvirt(QID_BCH, (p)) + +/* plenty for interleaved r/w on two planes with 16k page, ecc_size 1k */ +/* QDEPTH >= 16, as successive chunks must align on 128-byte boundaries */ +#define QDEPTH 256 /* u64s in a command queue chunk, incl next-pointer */ +#define NQS 1 /* linked chunks in the chain */ + +/** + * Write an arbitrary number of command words to a command queue. + * This is a generic function; the fixed number of command word + * functions yield higher performance. + * + * Could merge with crypto version for FPA use on cn83xx + */ +static inline int octeontx_cmd_queue_write(int queue_id, bool use_locking, + int cmd_count, const u64 *cmds) +{ + int ret = 0; + u64 *cmd_ptr; + struct bch_q *qptr = &octeontx_bch_q[queue_id]; + + if (unlikely(cmd_count < 1 || cmd_count > 32)) + return -EINVAL; + if (unlikely(!cmds)) + return -EINVAL; + + cmd_ptr = qptr->base_vaddr; + + while (cmd_count > 0) { + int slot = qptr->index % (QDEPTH * NQS); + + if (slot % QDEPTH != QDEPTH - 1) { + cmd_ptr[slot] = *cmds++; + cmd_count--; + } + + qptr->index++; + } + + __iowmb(); /* flush commands before ringing bell */ + + return ret; +} + +#endif /* __OCTEONTX_BCH_REGS_H__ */ diff --git a/drivers/mtd/nand/raw/octeontx_nand.c b/drivers/mtd/nand/raw/octeontx_nand.c new file mode 100644 index 0000000000..ad219171e9 --- /dev/null +++ b/drivers/mtd/nand/raw/octeontx_nand.c @@ -0,0 +1,2257 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell International Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "octeontx_bch.h" + +#ifdef DEBUG +# undef CONFIG_LOGLEVEL +# define CONFIG_LOGLEVEL 8 +#endif + +/* + * The NDF_CMD queue takes commands between 16 - 128 bit. + * All commands must be 16 bit aligned and are little endian. + * WAIT_STATUS commands must be 64 bit aligned. + * Commands are selected by the 4 bit opcode. + * + * Available Commands: + * + * 16 Bit: + * NOP + * WAIT + * BUS_ACQ, BUS_REL + * CHIP_EN, CHIP_DIS + * + * 32 Bit: + * CLE_CMD + * RD_CMD, RD_EDO_CMD + * WR_CMD + * + * 64 Bit: + * SET_TM_PAR + * + * 96 Bit: + * ALE_CMD + * + * 128 Bit: + * WAIT_STATUS, WAIT_STATUS_ALE + */ + +/* NDF Register offsets */ +#define NDF_CMD 0x0 +#define NDF_MISC 0x8 +#define NDF_ECC_CNT 0x10 +#define NDF_DRBELL 0x30 +#define NDF_ST_REG 0x38 /* status */ +#define NDF_INT 0x40 +#define NDF_INT_W1S 0x48 +#define NDF_DMA_CFG 0x50 +#define NDF_DMA_ADR 0x58 +#define NDF_INT_ENA_W1C 0x60 +#define NDF_INT_ENA_W1S 0x68 + +/* NDF command opcodes */ +#define NDF_OP_NOP 0x0 +#define NDF_OP_SET_TM_PAR 0x1 +#define NDF_OP_WAIT 0x2 +#define NDF_OP_CHIP_EN_DIS 0x3 +#define NDF_OP_CLE_CMD 0x4 +#define NDF_OP_ALE_CMD 0x5 +#define NDF_OP_WR_CMD 0x8 +#define NDF_OP_RD_CMD 0x9 +#define NDF_OP_RD_EDO_CMD 0xa +#define NDF_OP_WAIT_STATUS 0xb /* same opcode for WAIT_STATUS_ALE */ +#define NDF_OP_BUS_ACQ_REL 0xf + +#define NDF_BUS_ACQUIRE 1 +#define NDF_BUS_RELEASE 0 + +#define DBGX_EDSCR(X) (0x87A008000088 + (X) * 0x80000) + +struct ndf_nop_cmd { + u16 opcode: 4; + u16 nop: 12; +}; + +struct ndf_wait_cmd { + u16 opcode:4; + u16 r_b:1; /* wait for one cycle or PBUS_WAIT deassert */ + u16:3; + u16 wlen:3; /* timing parameter select */ + u16:5; +}; + +struct ndf_bus_cmd { + u16 opcode:4; + u16 direction:4; /* 1 = acquire, 0 = release */ + u16:8; +}; + +struct ndf_chip_cmd { + u16 opcode:4; + u16 chip:3; /* select chip, 0 = disable */ + u16 enable:1; /* 1 = enable, 0 = disable */ + u16 bus_width:2; /* 10 = 16 bit, 01 = 8 bit */ + u16:6; +}; + +struct ndf_cle_cmd { + u32 opcode:4; + u32:4; + u32 cmd_data:8; /* command sent to the PBUS AD pins */ + u32 clen1:3; /* time between PBUS CLE and WE asserts */ + u32 clen2:3; /* time WE remains asserted */ + u32 clen3:3; /* time between WE deassert and CLE */ + u32:7; +}; + +/* RD_EDO_CMD uses the same layout as RD_CMD */ +struct ndf_rd_cmd { + u32 opcode:4; + u32 data:16; /* data bytes */ + u32 rlen1:3; + u32 rlen2:3; + u32 rlen3:3; + u32 rlen4:3; +}; + +struct ndf_wr_cmd { + u32 opcode:4; + u32 data:16; /* data bytes */ + u32:4; + u32 wlen1:3; + u32 wlen2:3; + u32:3; +}; + +struct ndf_set_tm_par_cmd { + u64 opcode:4; + u64 tim_mult:4; /* multiplier for the seven parameters */ + u64 tm_par1:8; /* --> Following are the 7 timing parameters that */ + u64 tm_par2:8; /* specify the number of coprocessor cycles. */ + u64 tm_par3:8; /* A value of zero means one cycle. */ + u64 tm_par4:8; /* All values are scaled by tim_mult */ + u64 tm_par5:8; /* using tim_par * (2 ^ tim_mult). */ + u64 tm_par6:8; + u64 tm_par7:8; +}; + +struct ndf_ale_cmd { + u32 opcode:4; + u32:4; + u32 adr_byte_num:4; /* number of address bytes to be sent */ + u32:4; + u32 alen1:3; + u32 alen2:3; + u32 alen3:3; + u32 alen4:3; + u32:4; + u8 adr_byt1; + u8 adr_byt2; + u8 adr_byt3; + u8 adr_byt4; + u8 adr_byt5; + u8 adr_byt6; + u8 adr_byt7; + u8 adr_byt8; +}; + +struct ndf_wait_status_cmd { + u32 opcode:4; + u32:4; + u32 data:8; /** data */ + u32 clen1:3; + u32 clen2:3; + u32 clen3:3; + u32:8; + /** set to 5 to select WAIT_STATUS_ALE command */ + u32 ale_ind:8; + /** ALE only: number of address bytes to be sent */ + u32 adr_byte_num:4; + u32:4; + u32 alen1:3; /* ALE only */ + u32 alen2:3; /* ALE only */ + u32 alen3:3; /* ALE only */ + u32 alen4:3; /* ALE only */ + u32:4; + u8 adr_byt[4]; /* ALE only */ + u32 nine:4; /* set to 9 */ + u32 and_mask:8; + u32 comp_byte:8; + u32 rlen1:3; + u32 rlen2:3; + u32 rlen3:3; + u32 rlen4:3; +}; + +union ndf_cmd { + u64 val[2]; + union { + struct ndf_nop_cmd nop; + struct ndf_wait_cmd wait; + struct ndf_bus_cmd bus_acq_rel; + struct ndf_chip_cmd chip_en_dis; + struct ndf_cle_cmd cle_cmd; + struct ndf_rd_cmd rd_cmd; + struct ndf_wr_cmd wr_cmd; + struct ndf_set_tm_par_cmd set_tm_par; + struct ndf_ale_cmd ale_cmd; + struct ndf_wait_status_cmd wait_status; + } u; +}; + +/** Disable multi-bit error hangs */ +#define NDF_MISC_MB_DIS BIT_ULL(27) +/** High watermark for NBR FIFO or load/store operations */ +#define NDF_MISC_NBR_HWM GENMASK_ULL(26, 24) +/** Wait input filter count */ +#define NDF_MISC_WAIT_CNT GENMASK_ULL(23, 18) +/** Unfilled NFD_CMD queue bytes */ +#define NDF_MISC_FR_BYTE GENMASK_ULL(17, 7) +/** Set by HW when it reads the last 8 bytes of NDF_CMD */ +#define NDF_MISC_RD_DONE BIT_ULL(6) +/** Set by HW when it reads. SW read of NDF_CMD clears it */ +#define NDF_MISC_RD_VAL BIT_ULL(5) +/** Let HW read NDF_CMD queue. Cleared on SW NDF_CMD write */ +#define NDF_MISC_RD_CMD BIT_ULL(4) +/** Boot disable */ +#define NDF_MISC_BT_DIS BIT_ULL(2) +/** Stop command execution after completing command queue */ +#define NDF_MISC_EX_DIS BIT_ULL(1) +/** Reset fifo */ +#define NDF_MISC_RST_FF BIT_ULL(0) + +/** DMA engine enable */ +#define NDF_DMA_CFG_EN BIT_ULL(63) +/** Read or write */ +#define NDF_DMA_CFG_RW BIT_ULL(62) +/** Terminates DMA and clears enable bit */ +#define NDF_DMA_CFG_CLR BIT_ULL(61) +/** 32-bit swap enable */ +#define NDF_DMA_CFG_SWAP32 BIT_ULL(59) +/** 16-bit swap enable */ +#define NDF_DMA_CFG_SWAP16 BIT_ULL(58) +/** 8-bit swap enable */ +#define NDF_DMA_CFG_SWAP8 BIT_ULL(57) +/** Endian mode */ +#define NDF_DMA_CFG_CMD_BE BIT_ULL(56) +/** Number of 64 bit transfers */ +#define NDF_DMA_CFG_SIZE GENMASK_ULL(55, 36) + +/** Command execution status idle */ +#define NDF_ST_REG_EXE_IDLE BIT_ULL(15) +/** Command execution SM states */ +#define NDF_ST_REG_EXE_SM GENMASK_ULL(14, 11) +/** DMA and load SM states */ +#define NDF_ST_REG_BT_SM GENMASK_ULL(10, 7) +/** Queue read-back SM bad state */ +#define NDF_ST_REG_RD_FF_BAD BIT_ULL(6) +/** Queue read-back SM states */ +#define NDF_ST_REG_RD_FF GENMASK_ULL(5, 4) +/** Main SM is in a bad state */ +#define NDF_ST_REG_MAIN_BAD BIT_ULL(3) +/** Main SM states */ +#define NDF_ST_REG_MAIN_SM GENMASK_ULL(2, 0) + +#define MAX_NAND_NAME_LEN 64 +#if (defined(NAND_MAX_PAGESIZE) && (NAND_MAX_PAGESIZE > 4096)) || \ + !defined(NAND_MAX_PAGESIZE) +# undef NAND_MAX_PAGESIZE +# define NAND_MAX_PAGESIZE 4096 +#endif +#if (defined(NAND_MAX_OOBSIZE) && (NAND_MAX_OOBSIZE > 256)) || \ + !defined(NAND_MAX_OOBSIZE) +# undef NAND_MAX_OOBSIZE +# define NAND_MAX_OOBSIZE 256 +#endif + +#define OCTEONTX_NAND_DRIVER_NAME "octeontx_nand" + +#define NDF_TIMEOUT 1000 /** Timeout in ms */ +#define USEC_PER_SEC 1000000 /** Linux compatibility */ +#ifndef NAND_MAX_CHIPS +# define NAND_MAX_CHIPS 8 /** Linux compatibility */ +#endif + +struct octeontx_nand_chip { + struct list_head node; + struct nand_chip nand; + struct ndf_set_tm_par_cmd timings; + int cs; + int selected_page; + int iface_mode; + int row_bytes; + int col_bytes; + bool oob_only; + bool iface_set; +}; + +struct octeontx_nand_buf { + u8 *dmabuf; + dma_addr_t dmaaddr; + int dmabuflen; + int data_len; + int data_index; +}; + +/** NAND flash controller (NDF) related information */ +struct octeontx_nfc { + struct nand_hw_control controller; + struct udevice *dev; + void __iomem *base; + struct list_head chips; + int selected_chip; /* Currently selected NAND chip number */ + + /* + * Status is separate from octeontx_nand_buf because + * it can be used in parallel and during init. + */ + u8 *stat; + dma_addr_t stat_addr; + bool use_status; + + struct octeontx_nand_buf buf; + union bch_resp *bch_resp; + dma_addr_t bch_rhandle; + + /* BCH of all-0xff, so erased pages read as error-free */ + unsigned char *eccmask; +}; + +/* settable timings - 0..7 select timing of alen1..4/clen1..3/etc */ +enum tm_idx { + t0, /* fixed at 4< __timeout_ms) { \ + (val) = readq(__addr); \ + break; \ + } \ + if (delay_us) \ + udelay(delay_us); \ + } while (1); \ + (cond) ? 0 : -ETIMEDOUT; \ +}) + +/** Ported from Linux 4.9.0 include/linux/of.h for compatibility */ +static inline int of_get_child_count(const ofnode node) +{ + return fdtdec_get_child_count(gd->fdt_blob, ofnode_to_offset(node)); +} + +/** + * Linux compatibility from Linux 4.9.0 drivers/mtd/nand/nand_base.c + */ +static int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (section || !ecc->total) + return -ERANGE; + + oobregion->length = ecc->total; + oobregion->offset = mtd->oobsize - oobregion->length; + + return 0; +} + +/** + * Linux compatibility from Linux 4.9.0 drivers/mtd/nand/nand_base.c + */ +static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (section) + return -ERANGE; + + oobregion->length = mtd->oobsize - ecc->total - 2; + oobregion->offset = 2; + + return 0; +} + +static const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = { + .ecc = nand_ooblayout_ecc_lp, + .rfree = nand_ooblayout_free_lp, +}; + +static inline struct octeontx_nand_chip *to_otx_nand(struct nand_chip *nand) +{ + return container_of(nand, struct octeontx_nand_chip, nand); +} + +static inline struct octeontx_nfc *to_otx_nfc(struct nand_hw_control *ctrl) +{ + return container_of(ctrl, struct octeontx_nfc, controller); +} + +static int octeontx_nand_calc_ecc_layout(struct nand_chip *nand) +{ + struct nand_ecclayout *layout = nand->ecc.layout; + struct octeontx_nfc *tn = to_otx_nfc(nand->controller); + struct mtd_info *mtd = &nand->mtd; + int oobsize = mtd->oobsize; + int i; + bool layout_alloc = false; + + if (!layout) { + layout = devm_kzalloc(tn->dev, sizeof(*layout), GFP_KERNEL); + if (!layout) + return -ENOMEM; + nand->ecc.layout = layout; + layout_alloc = true; + } + layout->eccbytes = nand->ecc.steps * nand->ecc.bytes; + /* Reserve 2 bytes for bad block marker */ + if (layout->eccbytes + 2 > oobsize) { + pr_err("No suitable oob scheme available for oobsize %d eccbytes %u\n", + oobsize, layout->eccbytes); + goto fail; + } + /* put ecc bytes at oob tail */ + for (i = 0; i < layout->eccbytes; i++) + layout->eccpos[i] = oobsize - layout->eccbytes + i; + layout->oobfree[0].offset = 2; + layout->oobfree[0].length = oobsize - 2 - layout->eccbytes; + nand->ecc.layout = layout; + return 0; + +fail: + if (layout_alloc) + kfree(layout); + return -1; +} + +/* + * Read a single byte from the temporary buffer. Used after READID + * to get the NAND information and for STATUS. + */ +static u8 octeontx_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct octeontx_nfc *tn = to_otx_nfc(nand->controller); + + if (tn->use_status) { + tn->use_status = false; + return *tn->stat; + } + + if (tn->buf.data_index < tn->buf.data_len) + return tn->buf.dmabuf[tn->buf.data_index++]; + + dev_err(tn->dev, "No data to read, idx: 0x%x, len: 0x%x\n", + tn->buf.data_index, tn->buf.data_len); + + return 0xff; +} + +/* + * Read a number of pending bytes from the temporary buffer. Used + * to get page and OOB data. + */ +static void octeontx_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct octeontx_nfc *tn = to_otx_nfc(nand->controller); + + if (len > tn->buf.data_len - tn->buf.data_index) { + dev_err(tn->dev, "Not enough data for read of %d bytes\n", len); + return; + } + + memcpy(buf, tn->buf.dmabuf + tn->buf.data_index, len); + tn->buf.data_index += len; +} + +static void octeontx_nand_write_buf(struct mtd_info *mtd, + const u8 *buf, int len) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct octeontx_nfc *tn = to_otx_nfc(nand->controller); + + memcpy(tn->buf.dmabuf + tn->buf.data_len, buf, len); + tn->buf.data_len += len; +} + +/* Overwrite default function to avoid sync abort on chip = -1. */ +static void octeontx_nand_select_chip(struct mtd_info *mtd, int chip) +{ +} + +static inline int timing_to_cycle(u32 psec, unsigned long clock) +{ + unsigned int ns; + int ticks; + + ns = DIV_ROUND_UP(psec, 1000); + ns += slew_ns; + + /* no rounding needed since clock is multiple of 1MHz */ + clock /= 1000000; + ns *= clock; + + ticks = DIV_ROUND_UP(ns, 1000); + + /* actual delay is (tm_parX+1)<tim_mult = 0; + s_wh = timing_to_cycle(timings->tWH_min, sclk); + s_cls = timing_to_cycle(timings->tCLS_min, sclk); + s_clh = timing_to_cycle(timings->tCLH_min, sclk); + s_rp = timing_to_cycle(timings->tRP_min, sclk); + s_wb = timing_to_cycle(timings->tWB_max, sclk); + s_wc = timing_to_cycle(timings->tWC_min, sclk); + + tp->tm_par1 = s_wh; + tp->tm_par2 = s_clh; + tp->tm_par3 = s_rp + 1; + tp->tm_par4 = s_cls - s_wh; + tp->tm_par5 = s_wc - s_wh + 1; + tp->tm_par6 = s_wb; + tp->tm_par7 = 0; + tp->tim_mult++; /* overcompensate for bad math */ + + /* TODO: comment parameter re-use */ + + pr_debug("%s: tim_par: mult: %d p1: %d p2: %d p3: %d\n", + __func__, tp->tim_mult, tp->tm_par1, tp->tm_par2, tp->tm_par3); + pr_debug(" p4: %d p5: %d p6: %d p7: %d\n", + tp->tm_par4, tp->tm_par5, tp->tm_par6, tp->tm_par7); +} + +static int set_default_timings(struct octeontx_nfc *tn, + const struct nand_sdr_timings *timings) +{ + unsigned long sclk = octeontx_get_io_clock(); + + set_timings(NULL, &default_timing_parms, timings, sclk); + return 0; +} + +static int octeontx_nfc_chip_set_timings(struct octeontx_nand_chip *chip, + const struct nand_sdr_timings *timings) +{ + /*struct octeontx_nfc *tn = to_otx_nfc(chip->nand.controller);*/ + unsigned long sclk = octeontx_get_io_clock(); + + set_timings(chip, &chip->timings, timings, sclk); + return 0; +} + +/* How many bytes are free in the NFD_CMD queue? */ +static int ndf_cmd_queue_free(struct octeontx_nfc *tn) +{ + u64 ndf_misc; + + ndf_misc = readq(tn->base + NDF_MISC); + return FIELD_GET(NDF_MISC_FR_BYTE, ndf_misc); +} + +/* Submit a command to the NAND command queue. */ +static int ndf_submit(struct octeontx_nfc *tn, union ndf_cmd *cmd) +{ + int opcode = cmd->val[0] & 0xf; + + switch (opcode) { + /* All these commands fit in one 64bit word */ + case NDF_OP_NOP: + case NDF_OP_SET_TM_PAR: + case NDF_OP_WAIT: + case NDF_OP_CHIP_EN_DIS: + case NDF_OP_CLE_CMD: + case NDF_OP_WR_CMD: + case NDF_OP_RD_CMD: + case NDF_OP_RD_EDO_CMD: + case NDF_OP_BUS_ACQ_REL: + if (ndf_cmd_queue_free(tn) < 8) + goto full; + writeq(cmd->val[0], tn->base + NDF_CMD); + break; + case NDF_OP_ALE_CMD: + /* ALE commands take either one or two 64bit words */ + if (cmd->u.ale_cmd.adr_byte_num < 5) { + if (ndf_cmd_queue_free(tn) < 8) + goto full; + writeq(cmd->val[0], tn->base + NDF_CMD); + } else { + if (ndf_cmd_queue_free(tn) < 16) + goto full; + writeq(cmd->val[0], tn->base + NDF_CMD); + writeq(cmd->val[1], tn->base + NDF_CMD); + } + break; + case NDF_OP_WAIT_STATUS: /* Wait status commands take two 64bit words */ + if (ndf_cmd_queue_free(tn) < 16) + goto full; + writeq(cmd->val[0], tn->base + NDF_CMD); + writeq(cmd->val[1], tn->base + NDF_CMD); + break; + default: + dev_err(tn->dev, "%s: unknown command: %u\n", __func__, opcode); + return -EINVAL; + } + return 0; + +full: + dev_err(tn->dev, "%s: no space left in command queue\n", __func__); + return -ENOMEM; +} + +/** + * Wait for the ready/busy signal. First wait for busy to be valid, + * then wait for busy to de-assert. + */ +static int ndf_build_wait_busy(struct octeontx_nfc *tn) +{ + union ndf_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.u.wait.opcode = NDF_OP_WAIT; + cmd.u.wait.r_b = 1; + cmd.u.wait.wlen = t6; + + if (ndf_submit(tn, &cmd)) + return -ENOMEM; + return 0; +} + +static bool ndf_dma_done(struct octeontx_nfc *tn) +{ + u64 dma_cfg; + + /* Enable bit should be clear after a transfer */ + dma_cfg = readq(tn->base + NDF_DMA_CFG); + if (!(dma_cfg & NDF_DMA_CFG_EN)) + return true; + + return false; +} + +static int ndf_wait(struct octeontx_nfc *tn) +{ + ulong start = get_timer(0); + bool done; + + while (!(done = ndf_dma_done(tn)) && get_timer(start) < NDF_TIMEOUT) + ; + + if (!done) { + dev_err(tn->dev, "%s: timeout error\n", __func__); + return -ETIMEDOUT; + } + return 0; +} + +static int ndf_wait_idle(struct octeontx_nfc *tn) +{ + u64 val; + u64 dval = 0; + int rc; + int pause = 100; + u64 tot_us = USEC_PER_SEC / 10; + + rc = readq_poll_timeout(tn->base + NDF_ST_REG, + val, val & NDF_ST_REG_EXE_IDLE, pause, tot_us); + if (!rc) + rc = readq_poll_timeout(tn->base + NDF_DMA_CFG, + dval, !(dval & NDF_DMA_CFG_EN), + pause, tot_us); + + return rc; +} + +/** Issue set timing parameters */ +static int ndf_queue_cmd_timing(struct octeontx_nfc *tn, + struct ndf_set_tm_par_cmd *timings) +{ + union ndf_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.u.set_tm_par.opcode = NDF_OP_SET_TM_PAR; + cmd.u.set_tm_par.tim_mult = timings->tim_mult; + cmd.u.set_tm_par.tm_par1 = timings->tm_par1; + cmd.u.set_tm_par.tm_par2 = timings->tm_par2; + cmd.u.set_tm_par.tm_par3 = timings->tm_par3; + cmd.u.set_tm_par.tm_par4 = timings->tm_par4; + cmd.u.set_tm_par.tm_par5 = timings->tm_par5; + cmd.u.set_tm_par.tm_par6 = timings->tm_par6; + cmd.u.set_tm_par.tm_par7 = timings->tm_par7; + return ndf_submit(tn, &cmd); +} + +/** Issue bus acquire or release */ +static int ndf_queue_cmd_bus(struct octeontx_nfc *tn, int direction) +{ + union ndf_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.u.bus_acq_rel.opcode = NDF_OP_BUS_ACQ_REL; + cmd.u.bus_acq_rel.direction = direction; + return ndf_submit(tn, &cmd); +} + +/* Issue chip select or deselect */ +static int ndf_queue_cmd_chip(struct octeontx_nfc *tn, int enable, int chip, + int width) +{ + union ndf_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.u.chip_en_dis.opcode = NDF_OP_CHIP_EN_DIS; + cmd.u.chip_en_dis.chip = chip; + cmd.u.chip_en_dis.enable = enable; + cmd.u.chip_en_dis.bus_width = width; + return ndf_submit(tn, &cmd); +} + +static int ndf_queue_cmd_wait(struct octeontx_nfc *tn, int t_delay) +{ + union ndf_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.u.wait.opcode = NDF_OP_WAIT; + cmd.u.wait.wlen = t_delay; + return ndf_submit(tn, &cmd); +} + +static int ndf_queue_cmd_cle(struct octeontx_nfc *tn, int command) +{ + union ndf_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.u.cle_cmd.opcode = NDF_OP_CLE_CMD; + cmd.u.cle_cmd.cmd_data = command; + cmd.u.cle_cmd.clen1 = t4; + cmd.u.cle_cmd.clen2 = t1; + cmd.u.cle_cmd.clen3 = t2; + return ndf_submit(tn, &cmd); +} + +static int ndf_queue_cmd_ale(struct octeontx_nfc *tn, int addr_bytes, + struct nand_chip *nand, u64 page, + u32 col, int page_size) +{ + struct octeontx_nand_chip *octeontx_nand = (nand) ? + to_otx_nand(nand) : NULL; + union ndf_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.u.ale_cmd.opcode = NDF_OP_ALE_CMD; + cmd.u.ale_cmd.adr_byte_num = addr_bytes; + + /* set column bit for OOB area, assume OOB follows page */ + if (octeontx_nand && octeontx_nand->oob_only) + col += page_size; + + /* page is u64 for this generality, even if cmdfunc() passes int */ + switch (addr_bytes) { + /* 4-8 bytes: page, then 2-byte col */ + case 8: + cmd.u.ale_cmd.adr_byt8 = (page >> 40) & 0xff; + fallthrough; + case 7: + cmd.u.ale_cmd.adr_byt7 = (page >> 32) & 0xff; + fallthrough; + case 6: + cmd.u.ale_cmd.adr_byt6 = (page >> 24) & 0xff; + fallthrough; + case 5: + cmd.u.ale_cmd.adr_byt5 = (page >> 16) & 0xff; + fallthrough; + case 4: + cmd.u.ale_cmd.adr_byt4 = (page >> 8) & 0xff; + cmd.u.ale_cmd.adr_byt3 = page & 0xff; + cmd.u.ale_cmd.adr_byt2 = (col >> 8) & 0xff; + cmd.u.ale_cmd.adr_byt1 = col & 0xff; + break; + /* 1-3 bytes: just the page address */ + case 3: + cmd.u.ale_cmd.adr_byt3 = (page >> 16) & 0xff; + fallthrough; + case 2: + cmd.u.ale_cmd.adr_byt2 = (page >> 8) & 0xff; + fallthrough; + case 1: + cmd.u.ale_cmd.adr_byt1 = page & 0xff; + break; + default: + break; + } + + cmd.u.ale_cmd.alen1 = t3; + cmd.u.ale_cmd.alen2 = t1; + cmd.u.ale_cmd.alen3 = t5; + cmd.u.ale_cmd.alen4 = t2; + return ndf_submit(tn, &cmd); +} + +static int ndf_queue_cmd_write(struct octeontx_nfc *tn, int len) +{ + union ndf_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.u.wr_cmd.opcode = NDF_OP_WR_CMD; + cmd.u.wr_cmd.data = len; + cmd.u.wr_cmd.wlen1 = t3; + cmd.u.wr_cmd.wlen2 = t1; + return ndf_submit(tn, &cmd); +} + +static int ndf_build_pre_cmd(struct octeontx_nfc *tn, int cmd1, + int addr_bytes, u64 page, u32 col, int cmd2) +{ + struct nand_chip *nand = tn->controller.active; + struct octeontx_nand_chip *octeontx_nand; + struct ndf_set_tm_par_cmd *timings; + int width, page_size, rc; + + /* Also called before chip probing is finished */ + if (!nand) { + timings = &default_timing_parms; + page_size = default_page_size; + width = default_width; + } else { + octeontx_nand = to_otx_nand(nand); + timings = &octeontx_nand->timings; + page_size = nand->mtd.writesize; + if (nand->options & NAND_BUSWIDTH_16) + width = 2; + else + width = 1; + } + rc = ndf_queue_cmd_timing(tn, timings); + if (rc) + return rc; + + rc = ndf_queue_cmd_bus(tn, NDF_BUS_ACQUIRE); + if (rc) + return rc; + + rc = ndf_queue_cmd_chip(tn, 1, tn->selected_chip, width); + if (rc) + return rc; + + rc = ndf_queue_cmd_wait(tn, t1); + if (rc) + return rc; + + rc = ndf_queue_cmd_cle(tn, cmd1); + if (rc) + return rc; + + if (addr_bytes) { + rc = ndf_build_wait_busy(tn); + if (rc) + return rc; + + rc = ndf_queue_cmd_ale(tn, addr_bytes, nand, + page, col, page_size); + if (rc) + return rc; + } + + /* CLE 2 */ + if (cmd2) { + rc = ndf_build_wait_busy(tn); + if (rc) + return rc; + + rc = ndf_queue_cmd_cle(tn, cmd2); + if (rc) + return rc; + } + return 0; +} + +static int ndf_build_post_cmd(struct octeontx_nfc *tn, int hold_time) +{ + int rc; + + /* Deselect chip */ + rc = ndf_queue_cmd_chip(tn, 0, 0, 0); + if (rc) + return rc; + + rc = ndf_queue_cmd_wait(tn, t2); + if (rc) + return rc; + + /* Release bus */ + rc = ndf_queue_cmd_bus(tn, 0); + if (rc) + return rc; + + rc = ndf_queue_cmd_wait(tn, hold_time); + if (rc) + return rc; + + /* + * Last action is ringing the doorbell with number of bus + * acquire-releases cycles (currently 1). + */ + writeq(1, tn->base + NDF_DRBELL); + return 0; +} + +/* Setup the NAND DMA engine for a transfer. */ +static void ndf_setup_dma(struct octeontx_nfc *tn, int is_write, + dma_addr_t bus_addr, int len) +{ + u64 dma_cfg; + + dma_cfg = FIELD_PREP(NDF_DMA_CFG_RW, is_write) | + FIELD_PREP(NDF_DMA_CFG_SIZE, (len >> 3) - 1); + dma_cfg |= NDF_DMA_CFG_EN; + writeq(bus_addr, tn->base + NDF_DMA_ADR); + writeq(dma_cfg, tn->base + NDF_DMA_CFG); +} + +static int octeontx_nand_reset(struct octeontx_nfc *tn) +{ + int rc; + + rc = ndf_build_pre_cmd(tn, NAND_CMD_RESET, 0, 0, 0, 0); + if (rc) + return rc; + + rc = ndf_build_wait_busy(tn); + if (rc) + return rc; + + rc = ndf_build_post_cmd(tn, t2); + if (rc) + return rc; + + return 0; +} + +static int ndf_read(struct octeontx_nfc *tn, int cmd1, int addr_bytes, + u64 page, u32 col, int cmd2, int len) +{ + dma_addr_t bus_addr = tn->use_status ? tn->stat_addr : tn->buf.dmaaddr; + struct nand_chip *nand = tn->controller.active; + int timing_mode, bytes, rc; + union ndf_cmd cmd; + u64 start, end; + + pr_debug("%s(%p, 0x%x, 0x%x, 0x%llx, 0x%x, 0x%x, 0x%x)\n", __func__, + tn, cmd1, addr_bytes, page, col, cmd2, len); + if (!nand) + timing_mode = default_onfi_timing; + else + timing_mode = nand->onfi_timing_mode_default; + + /* Build the command and address cycles */ + rc = ndf_build_pre_cmd(tn, cmd1, addr_bytes, page, col, cmd2); + if (rc) { + dev_err(tn->dev, "Build pre command failed\n"); + return rc; + } + + /* This waits for some time, then waits for busy to be de-asserted. */ + rc = ndf_build_wait_busy(tn); + if (rc) { + dev_err(tn->dev, "Wait timeout\n"); + return rc; + } + + memset(&cmd, 0, sizeof(cmd)); + + if (timing_mode < 4) + cmd.u.rd_cmd.opcode = NDF_OP_RD_CMD; + else + cmd.u.rd_cmd.opcode = NDF_OP_RD_EDO_CMD; + + cmd.u.rd_cmd.data = len; + cmd.u.rd_cmd.rlen1 = t7; + cmd.u.rd_cmd.rlen2 = t3; + cmd.u.rd_cmd.rlen3 = t1; + cmd.u.rd_cmd.rlen4 = t7; + rc = ndf_submit(tn, &cmd); + if (rc) { + dev_err(tn->dev, "Error submitting command\n"); + return rc; + } + + start = (u64)bus_addr; + ndf_setup_dma(tn, 0, bus_addr, len); + + rc = ndf_build_post_cmd(tn, t2); + if (rc) { + dev_err(tn->dev, "Build post command failed\n"); + return rc; + } + + /* Wait for the DMA to complete */ + rc = ndf_wait(tn); + if (rc) { + dev_err(tn->dev, "DMA timed out\n"); + return rc; + } + + end = readq(tn->base + NDF_DMA_ADR); + bytes = end - start; + + /* Make sure NDF is really done */ + rc = ndf_wait_idle(tn); + if (rc) { + dev_err(tn->dev, "poll idle failed\n"); + return rc; + } + + pr_debug("%s: Read %d bytes\n", __func__, bytes); + return bytes; +} + +static int octeontx_nand_get_features(struct mtd_info *mtd, + struct nand_chip *chip, int feature_addr, + u8 *subfeature_para) +{ + struct nand_chip *nand = chip; + struct octeontx_nfc *tn = to_otx_nfc(nand->controller); + int len = 8; + int rc; + + pr_debug("%s: feature addr: 0x%x\n", __func__, feature_addr); + memset(tn->buf.dmabuf, 0xff, len); + tn->buf.data_index = 0; + tn->buf.data_len = 0; + rc = ndf_read(tn, NAND_CMD_GET_FEATURES, 1, feature_addr, 0, 0, len); + if (rc) + return rc; + + memcpy(subfeature_para, tn->buf.dmabuf, ONFI_SUBFEATURE_PARAM_LEN); + + return 0; +} + +static int octeontx_nand_set_features(struct mtd_info *mtd, + struct nand_chip *chip, int feature_addr, + u8 *subfeature_para) +{ + struct nand_chip *nand = chip; + struct octeontx_nfc *tn = to_otx_nfc(nand->controller); + const int len = ONFI_SUBFEATURE_PARAM_LEN; + int rc; + + rc = ndf_build_pre_cmd(tn, NAND_CMD_SET_FEATURES, + 1, feature_addr, 0, 0); + if (rc) + return rc; + + memcpy(tn->buf.dmabuf, subfeature_para, len); + memset(tn->buf.dmabuf + len, 0, 8 - len); + + ndf_setup_dma(tn, 1, tn->buf.dmaaddr, 8); + + rc = ndf_queue_cmd_write(tn, 8); + if (rc) + return rc; + + rc = ndf_build_wait_busy(tn); + if (rc) + return rc; + + rc = ndf_build_post_cmd(tn, t2); + if (rc) + return rc; + + return 0; +} + +/* + * Read a page from NAND. If the buffer has room, the out of band + * data will be included. + */ +static int ndf_page_read(struct octeontx_nfc *tn, u64 page, int col, int len) +{ + debug("%s(%p, 0x%llx, 0x%x, 0x%x) active: %p\n", __func__, + tn, page, col, len, tn->controller.active); + struct nand_chip *nand = tn->controller.active; + struct octeontx_nand_chip *chip = to_otx_nand(nand); + int addr_bytes = chip->row_bytes + chip->col_bytes; + + memset(tn->buf.dmabuf, 0xff, len); + return ndf_read(tn, NAND_CMD_READ0, addr_bytes, + page, col, NAND_CMD_READSTART, len); +} + +/* Erase a NAND block */ +static int ndf_block_erase(struct octeontx_nfc *tn, u64 page_addr) +{ + struct nand_chip *nand = tn->controller.active; + struct octeontx_nand_chip *chip = to_otx_nand(nand); + int addr_bytes = chip->row_bytes; + int rc; + + rc = ndf_build_pre_cmd(tn, NAND_CMD_ERASE1, addr_bytes, + page_addr, 0, NAND_CMD_ERASE2); + if (rc) + return rc; + + /* Wait for R_B to signal erase is complete */ + rc = ndf_build_wait_busy(tn); + if (rc) + return rc; + + rc = ndf_build_post_cmd(tn, t2); + if (rc) + return rc; + + /* Wait until the command queue is idle */ + return ndf_wait_idle(tn); +} + +/* + * Write a page (or less) to NAND. + */ +static int ndf_page_write(struct octeontx_nfc *tn, int page) +{ + int len, rc; + struct nand_chip *nand = tn->controller.active; + struct octeontx_nand_chip *chip = to_otx_nand(nand); + int addr_bytes = chip->row_bytes + chip->col_bytes; + + len = tn->buf.data_len - tn->buf.data_index; + chip->oob_only = (tn->buf.data_index >= nand->mtd.writesize); + WARN_ON_ONCE(len & 0x7); + + ndf_setup_dma(tn, 1, tn->buf.dmaaddr + tn->buf.data_index, len); + rc = ndf_build_pre_cmd(tn, NAND_CMD_SEQIN, addr_bytes, page, 0, 0); + if (rc) + return rc; + + rc = ndf_queue_cmd_write(tn, len); + if (rc) + return rc; + + rc = ndf_queue_cmd_cle(tn, NAND_CMD_PAGEPROG); + if (rc) + return rc; + + /* Wait for R_B to signal program is complete */ + rc = ndf_build_wait_busy(tn); + if (rc) + return rc; + + rc = ndf_build_post_cmd(tn, t2); + if (rc) + return rc; + + /* Wait for the DMA to complete */ + rc = ndf_wait(tn); + if (rc) + return rc; + + /* Data transfer is done but NDF is not, it is waiting for R/B# */ + return ndf_wait_idle(tn); +} + +static void octeontx_nand_cmdfunc(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct octeontx_nand_chip *octeontx_nand = to_otx_nand(nand); + struct octeontx_nfc *tn = to_otx_nfc(nand->controller); + int rc; + + tn->selected_chip = octeontx_nand->cs; + if (tn->selected_chip < 0 || tn->selected_chip >= NAND_MAX_CHIPS) { + dev_err(tn->dev, "invalid chip select\n"); + return; + } + + tn->use_status = false; + + pr_debug("%s(%p, 0x%x, 0x%x, 0x%x) cs: %d\n", __func__, mtd, command, + column, page_addr, tn->selected_chip); + switch (command) { + case NAND_CMD_READID: + tn->buf.data_index = 0; + octeontx_nand->oob_only = false; + rc = ndf_read(tn, command, 1, column, 0, 0, 8); + if (rc < 0) + dev_err(tn->dev, "READID failed with %d\n", rc); + else + tn->buf.data_len = rc; + break; + + case NAND_CMD_READOOB: + octeontx_nand->oob_only = true; + tn->buf.data_index = 0; + tn->buf.data_len = 0; + rc = ndf_page_read(tn, page_addr, column, mtd->oobsize); + if (rc < mtd->oobsize) + dev_err(tn->dev, "READOOB failed with %d\n", + tn->buf.data_len); + else + tn->buf.data_len = rc; + break; + + case NAND_CMD_READ0: + octeontx_nand->oob_only = false; + tn->buf.data_index = 0; + tn->buf.data_len = 0; + rc = ndf_page_read(tn, page_addr, column, + mtd->writesize + mtd->oobsize); + + if (rc < mtd->writesize + mtd->oobsize) + dev_err(tn->dev, "READ0 failed with %d\n", rc); + else + tn->buf.data_len = rc; + break; + + case NAND_CMD_STATUS: + /* used in oob/not states */ + tn->use_status = true; + rc = ndf_read(tn, command, 0, 0, 0, 0, 8); + if (rc < 0) + dev_err(tn->dev, "STATUS failed with %d\n", rc); + break; + + case NAND_CMD_RESET: + /* used in oob/not states */ + rc = octeontx_nand_reset(tn); + if (rc < 0) + dev_err(tn->dev, "RESET failed with %d\n", rc); + break; + + case NAND_CMD_PARAM: + octeontx_nand->oob_only = false; + tn->buf.data_index = 0; + rc = ndf_read(tn, command, 1, 0, 0, 0, + min(tn->buf.dmabuflen, 3 * 512)); + if (rc < 0) + dev_err(tn->dev, "PARAM failed with %d\n", rc); + else + tn->buf.data_len = rc; + break; + + case NAND_CMD_RNDOUT: + tn->buf.data_index = column; + break; + + case NAND_CMD_ERASE1: + if (ndf_block_erase(tn, page_addr)) + dev_err(tn->dev, "ERASE1 failed\n"); + break; + + case NAND_CMD_ERASE2: + /* We do all erase processing in the first command, so ignore + * this one. + */ + break; + + case NAND_CMD_SEQIN: + octeontx_nand->oob_only = (column >= mtd->writesize); + tn->buf.data_index = column; + tn->buf.data_len = column; + + octeontx_nand->selected_page = page_addr; + break; + + case NAND_CMD_PAGEPROG: + rc = ndf_page_write(tn, octeontx_nand->selected_page); + if (rc) + dev_err(tn->dev, "PAGEPROG failed with %d\n", rc); + break; + + case NAND_CMD_SET_FEATURES: + octeontx_nand->oob_only = false; + /* assume tn->buf.data_len == 4 of data has been set there */ + rc = octeontx_nand_set_features(mtd, nand, + page_addr, tn->buf.dmabuf); + if (rc) + dev_err(tn->dev, "SET_FEATURES failed with %d\n", rc); + break; + + case NAND_CMD_GET_FEATURES: + octeontx_nand->oob_only = false; + rc = octeontx_nand_get_features(mtd, nand, + page_addr, tn->buf.dmabuf); + if (!rc) { + tn->buf.data_index = 0; + tn->buf.data_len = 4; + } else { + dev_err(tn->dev, "GET_FEATURES failed with %d\n", rc); + } + break; + + default: + WARN_ON_ONCE(1); + dev_err(tn->dev, "unhandled nand cmd: %x\n", command); + } +} + +static int octeontx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct octeontx_nfc *tn = to_otx_nfc(chip->controller); + int ret; + + ret = ndf_wait_idle(tn); + return (ret < 0) ? -EIO : 0; +} + +/* check compatibility with ONFI timing mode#N, and optionally apply */ +/* TODO: Implement chipnr support? */ +static int octeontx_nand_setup_dat_intf(struct mtd_info *mtd, int chipnr, + const struct nand_data_interface *conf) +{ + static const bool check_only; + struct nand_chip *nand = mtd_to_nand(mtd); + struct octeontx_nand_chip *chip = to_otx_nand(nand); + static u64 t_wc_n[MAX_ONFI_MODE + 2]; /* cache a mode signature */ + int mode; /* deduced mode number, for reporting and restricting */ + int rc; + + /* + * Cache timing modes for reporting, and reducing needless change. + * + * Challenge: caller does not pass ONFI mode#, but reporting the mode + * and restricting to a maximum, or a list, are useful for diagnosing + * new hardware. So use tWC_min, distinct and monotonic across modes, + * to discover the requested/accepted mode number + */ + for (mode = MAX_ONFI_MODE; mode >= 0 && !t_wc_n[0]; mode--) { + const struct nand_sdr_timings *t; + + t = onfi_async_timing_mode_to_sdr_timings(mode); + if (!t) + continue; + t_wc_n[mode] = t->tWC_min; + } + + if (!conf) { + rc = -EINVAL; + } else if (check_only) { + rc = 0; + } else if (nand->data_interface && + chip->iface_set && chip->iface_mode == mode) { + /* + * Cases: + * - called from nand_reset, which clears DDR timing + * mode back to SDR. BUT if we're already in SDR, + * timing mode persists over resets. + * While mtd/nand layer only supports SDR, + * this is always safe. And this driver only supports SDR. + * + * - called from post-power-event nand_reset (maybe + * NFC+flash power down, or system hibernate. + * Address this when CONFIG_PM support added + */ + rc = 0; + } else { + rc = octeontx_nfc_chip_set_timings(chip, &conf->timings.sdr); + if (!rc) { + chip->iface_mode = mode; + chip->iface_set = true; + } + } + return rc; +} + +static void octeontx_bch_reset(void) +{ +} + +/* + * Given a page, calculate the ECC code + * + * chip: Pointer to NAND chip data structure + * buf: Buffer to calculate ECC on + * code: Buffer to hold ECC data + * + * Return 0 on success or -1 on failure + */ +static int octeontx_nand_bch_calculate_ecc_internal(struct mtd_info *mtd, + dma_addr_t ihandle, + u8 *code) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct octeontx_nfc *tn = to_otx_nfc(nand->controller); + int rc; + int i; + static u8 *ecc_buffer; + static int ecc_size; + static unsigned long ecc_handle; + union bch_resp *r = tn->bch_resp; + + if (!ecc_buffer || ecc_size < nand->ecc.size) { + ecc_size = nand->ecc.size; + ecc_buffer = dma_alloc_coherent(ecc_size, + (unsigned long *)&ecc_handle); + } + + memset(ecc_buffer, 0, nand->ecc.bytes); + + r->u16 = 0; + __iowmb(); /* flush done=0 before making request */ + + rc = octeontx_bch_encode(bch_vf, ihandle, nand->ecc.size, + nand->ecc.strength, + (dma_addr_t)ecc_handle, tn->bch_rhandle); + + if (!rc) { + octeontx_bch_wait(bch_vf, r, tn->bch_rhandle); + } else { + dev_err(tn->dev, "octeontx_bch_encode failed\n"); + return -1; + } + + if (!r->s.done || r->s.uncorrectable) { + dev_err(tn->dev, + "%s timeout, done:%d uncorr:%d corr:%d erased:%d\n", + __func__, r->s.done, r->s.uncorrectable, + r->s.num_errors, r->s.erased); + octeontx_bch_reset(); + return -1; + } + + memcpy(code, ecc_buffer, nand->ecc.bytes); + + for (i = 0; i < nand->ecc.bytes; i++) + code[i] ^= tn->eccmask[i]; + + return tn->bch_resp->s.num_errors; +} + +/* + * Given a page, calculate the ECC code + * + * mtd: MTD block structure + * dat: raw data (unused) + * ecc_code: buffer for ECC + */ +static int octeontx_nand_bch_calculate(struct mtd_info *mtd, + const u8 *dat, u8 *ecc_code) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + dma_addr_t handle = dma_map_single((u8 *)dat, + nand->ecc.size, DMA_TO_DEVICE); + int ret; + + ret = octeontx_nand_bch_calculate_ecc_internal(mtd, handle, + (void *)ecc_code); + + return ret; +} + +/* + * Detect and correct multi-bit ECC for a page + * + * mtd: MTD block structure + * dat: raw data read from the chip + * read_ecc: ECC from the chip (unused) + * isnull: unused + * + * Returns number of bits corrected or -1 if unrecoverable + */ +static int octeontx_nand_bch_correct(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *isnull) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct octeontx_nfc *tn = to_otx_nfc(nand->controller); + int i = nand->ecc.size + nand->ecc.bytes; + static u8 *data_buffer; + static dma_addr_t ihandle; + static int buffer_size; + dma_addr_t ohandle; + union bch_resp *r = tn->bch_resp; + int rc; + + if (i > buffer_size) { + if (buffer_size) + free(data_buffer); + data_buffer = dma_alloc_coherent(i, + (unsigned long *)&ihandle); + if (!data_buffer) { + dev_err(tn->dev, + "%s: Could not allocate %d bytes for buffer\n", + __func__, i); + goto error; + } + buffer_size = i; + } + + memcpy(data_buffer, dat, nand->ecc.size); + memcpy(data_buffer + nand->ecc.size, read_ecc, nand->ecc.bytes); + + for (i = 0; i < nand->ecc.bytes; i++) + data_buffer[nand->ecc.size + i] ^= tn->eccmask[i]; + + r->u16 = 0; + __iowmb(); /* flush done=0 before making request */ + + ohandle = dma_map_single(dat, nand->ecc.size, DMA_FROM_DEVICE); + rc = octeontx_bch_decode(bch_vf, ihandle, nand->ecc.size, + nand->ecc.strength, ohandle, tn->bch_rhandle); + + if (!rc) + octeontx_bch_wait(bch_vf, r, tn->bch_rhandle); + + if (rc) { + dev_err(tn->dev, "octeontx_bch_decode failed\n"); + goto error; + } + + if (!r->s.done) { + dev_err(tn->dev, "Error: BCH engine timeout\n"); + octeontx_bch_reset(); + goto error; + } + + if (r->s.erased) { + debug("Info: BCH block is erased\n"); + return 0; + } + + if (r->s.uncorrectable) { + debug("Cannot correct NAND block, response: 0x%x\n", + r->u16); + goto error; + } + + return r->s.num_errors; + +error: + debug("Error performing bch correction\n"); + return -1; +} + +void octeontx_nand_bch_hwctl(struct mtd_info *mtd, int mode) +{ + /* Do nothing. */ +} + +static int octeontx_nand_hw_bch_read_page(struct mtd_info *mtd, + struct nand_chip *chip, u8 *buf, + int oob_required, int page) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct octeontx_nfc *tn = to_otx_nfc(nand->controller); + int i, eccsize = chip->ecc.size, ret; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + u8 *p; + u8 *ecc_code = chip->buffers->ecccode; + unsigned int max_bitflips = 0; + + /* chip->read_buf() insists on sequential order, we do OOB first */ + memcpy(chip->oob_poi, tn->buf.dmabuf + mtd->writesize, mtd->oobsize); + + /* Use private buffer as input for ECC correction */ + p = tn->buf.dmabuf; + + ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + debug("Correcting block offset %lx, ecc offset %x\n", + p - buf, i); + stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); + + if (stat < 0) { + mtd->ecc_stats.failed++; + debug("Cannot correct NAND page %d\n", page); + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + } + + /* Copy corrected data to caller's buffer now */ + memcpy(buf, tn->buf.dmabuf, mtd->writesize); + + return max_bitflips; +} + +static int octeontx_nand_hw_bch_write_page(struct mtd_info *mtd, + struct nand_chip *chip, + const u8 *buf, int oob_required, + int page) +{ + struct octeontx_nfc *tn = to_otx_nfc(chip->controller); + int i, eccsize = chip->ecc.size, ret; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + const u8 *p; + u8 *ecc_calc = chip->buffers->ecccalc; + + debug("%s(buf?%p, oob%d p%x)\n", + __func__, buf, oob_required, page); + for (i = 0; i < chip->ecc.total; i++) + ecc_calc[i] = 0xFF; + + /* Copy the page data from caller's buffers to private buffer */ + chip->write_buf(mtd, buf, mtd->writesize); + /* Use private date as source for ECC calculation */ + p = tn->buf.dmabuf; + + /* Hardware ECC calculation */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int ret; + + ret = chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + if (ret < 0) + debug("calculate(mtd, p?%p, &ecc_calc[%d]?%p) returned %d\n", + p, i, &ecc_calc[i], ret); + + debug("block offset %lx, ecc offset %x\n", p - buf, i); + } + + ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; + + /* Store resulting OOB into private buffer, will be sent to HW */ + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +/** + * nand_write_page_raw - [INTERN] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + * + * Not for syndrome calculating ECC controllers, which use a special oob layout. + */ +static int octeontx_nand_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, + const u8 *buf, int oob_required, + int page) +{ + chip->write_buf(mtd, buf, mtd->writesize); + if (oob_required) + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +/** + * octeontx_nand_write_oob_std - [REPLACEABLE] the most common OOB data write + * function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write + */ +static int octeontx_nand_write_oob_std(struct mtd_info *mtd, + struct nand_chip *chip, + int page) +{ + int status = 0; + const u8 *buf = chip->oob_poi; + int length = mtd->oobsize; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + chip->write_buf(mtd, buf, length); + /* Send command to program the OOB data */ + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +/** + * octeontx_nand_read_page_raw - [INTERN] read raw page data without ecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * Not for syndrome calculating ECC controllers, which use a special oob layout. + */ +static int octeontx_nand_read_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, + u8 *buf, int oob_required, int page) +{ + chip->read_buf(mtd, buf, mtd->writesize); + if (oob_required) + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return 0; +} + +static int octeontx_nand_read_oob_std(struct mtd_info *mtd, + struct nand_chip *chip, + int page) + +{ + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return 0; +} + +static int octeontx_nand_calc_bch_ecc_strength(struct nand_chip *nand) +{ + struct mtd_info *mtd = nand_to_mtd(nand); + struct nand_ecc_ctrl *ecc = &nand->ecc; + struct octeontx_nfc *tn = to_otx_nfc(nand->controller); + int nsteps = mtd->writesize / ecc->size; + int oobchunk = mtd->oobsize / nsteps; + + /* ecc->strength determines ecc_level and OOB's ecc_bytes. */ + const u8 strengths[] = {4, 8, 16, 24, 32, 40, 48, 56, 60, 64}; + /* first set the desired ecc_level to match strengths[] */ + int index = ARRAY_SIZE(strengths) - 1; + int need; + + while (index > 0 && !(ecc->options & NAND_ECC_MAXIMIZE) && + strengths[index - 1] >= ecc->strength) + index--; + + do { + need = DIV_ROUND_UP(15 * strengths[index], 8); + if (need <= oobchunk - 2) + break; + } while (index > 0); + + debug("%s: steps ds: %d, strength ds: %d\n", __func__, + nand->ecc_step_ds, nand->ecc_strength_ds); + ecc->strength = strengths[index]; + ecc->bytes = need; + debug("%s: strength: %d, bytes: %d\n", __func__, ecc->strength, + ecc->bytes); + + if (!tn->eccmask) + tn->eccmask = devm_kzalloc(tn->dev, ecc->bytes, GFP_KERNEL); + if (!tn->eccmask) + return -ENOMEM; + + return 0; +} + +/* sample the BCH signature of an erased (all 0xff) page, + * to XOR into all page traffic, so erased pages have no ECC errors + */ +static int octeontx_bch_save_empty_eccmask(struct nand_chip *nand) +{ + struct mtd_info *mtd = nand_to_mtd(nand); + struct octeontx_nfc *tn = to_otx_nfc(nand->controller); + unsigned int eccsize = nand->ecc.size; + unsigned int eccbytes = nand->ecc.bytes; + u8 erased_ecc[eccbytes]; + unsigned long erased_handle; + unsigned char *erased_page = dma_alloc_coherent(eccsize, + &erased_handle); + int i; + int rc = 0; + + if (!erased_page) + return -ENOMEM; + + memset(erased_page, 0xff, eccsize); + memset(erased_ecc, 0, eccbytes); + + rc = octeontx_nand_bch_calculate_ecc_internal(mtd, + (dma_addr_t)erased_handle, + erased_ecc); + + free(erased_page); + + for (i = 0; i < eccbytes; i++) + tn->eccmask[i] = erased_ecc[i] ^ 0xff; + + return rc; +} + +static void octeontx_nfc_chip_sizing(struct nand_chip *nand) +{ + struct octeontx_nand_chip *chip = to_otx_nand(nand); + struct mtd_info *mtd = nand_to_mtd(nand); + struct nand_ecc_ctrl *ecc = &nand->ecc; + + chip->row_bytes = nand->onfi_params.addr_cycles & 0xf; + chip->col_bytes = nand->onfi_params.addr_cycles >> 4; + debug("%s(%p) row bytes: %d, col bytes: %d, ecc mode: %d\n", + __func__, nand, chip->row_bytes, chip->col_bytes, ecc->mode); + + /* + * HW_BCH using OcteonTX BCH engine, or SOFT_BCH laid out in + * HW_BCH-compatible fashion, depending on devtree advice + * and kernel config. + * BCH/NFC hardware capable of subpage ops, not implemented. + */ + mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); + nand->options |= NAND_NO_SUBPAGE_WRITE; + debug("%s: start steps: %d, size: %d, bytes: %d\n", + __func__, ecc->steps, ecc->size, ecc->bytes); + debug("%s: step ds: %d, strength ds: %d\n", __func__, + nand->ecc_step_ds, nand->ecc_strength_ds); + + if (ecc->mode != NAND_ECC_NONE) { + int nsteps = ecc->steps ? ecc->steps : 1; + + if (ecc->size && ecc->size != mtd->writesize) + nsteps = mtd->writesize / ecc->size; + else if (mtd->writesize > def_ecc_size && + !(mtd->writesize & (def_ecc_size - 1))) + nsteps = mtd->writesize / def_ecc_size; + ecc->steps = nsteps; + ecc->size = mtd->writesize / nsteps; + ecc->bytes = mtd->oobsize / nsteps; + + if (nand->ecc_strength_ds) + ecc->strength = nand->ecc_strength_ds; + if (nand->ecc_step_ds) + ecc->size = nand->ecc_step_ds; + /* + * no subpage ops, but set subpage-shift to match ecc->steps + * so mtd_nandbiterrs tests appropriate boundaries + */ + if (!mtd->subpage_sft && !(ecc->steps & (ecc->steps - 1))) + mtd->subpage_sft = fls(ecc->steps) - 1; + + if (IS_ENABLED(CONFIG_NAND_OCTEONTX_HW_ECC)) { + debug("%s: ecc mode: %d\n", __func__, ecc->mode); + if (ecc->mode != NAND_ECC_SOFT && + !octeontx_nand_calc_bch_ecc_strength(nand)) { + struct octeontx_nfc *tn = + to_otx_nfc(nand->controller); + + debug("Using hardware BCH engine support\n"); + ecc->mode = NAND_ECC_HW_SYNDROME; + ecc->read_page = octeontx_nand_hw_bch_read_page; + ecc->write_page = + octeontx_nand_hw_bch_write_page; + ecc->read_page_raw = + octeontx_nand_read_page_raw; + ecc->write_page_raw = + octeontx_nand_write_page_raw; + ecc->read_oob = octeontx_nand_read_oob_std; + ecc->write_oob = octeontx_nand_write_oob_std; + + ecc->calculate = octeontx_nand_bch_calculate; + ecc->correct = octeontx_nand_bch_correct; + ecc->hwctl = octeontx_nand_bch_hwctl; + + debug("NAND chip %d using hw_bch\n", + tn->selected_chip); + debug(" %d bytes ECC per %d byte block\n", + ecc->bytes, ecc->size); + debug(" for %d bits of correction per block.", + ecc->strength); + octeontx_nand_calc_ecc_layout(nand); + octeontx_bch_save_empty_eccmask(nand); + } + } + } +} + +static int octeontx_nfc_chip_init(struct octeontx_nfc *tn, struct udevice *dev, + ofnode node) +{ + struct octeontx_nand_chip *chip; + struct nand_chip *nand; + struct mtd_info *mtd; + int ret; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + debug("%s: Getting chip select\n", __func__); + ret = ofnode_read_s32(node, "reg", &chip->cs); + if (ret) { + dev_err(dev, "could not retrieve reg property: %d\n", ret); + return ret; + } + + if (chip->cs >= NAND_MAX_CHIPS) { + dev_err(dev, "invalid reg value: %u (max CS = 7)\n", chip->cs); + return -EINVAL; + } + debug("%s: chip select: %d\n", __func__, chip->cs); + nand = &chip->nand; + nand->controller = &tn->controller; + if (!tn->controller.active) + tn->controller.active = nand; + + debug("%s: Setting flash node\n", __func__); + nand_set_flash_node(nand, node); + + nand->options = 0; + nand->select_chip = octeontx_nand_select_chip; + nand->cmdfunc = octeontx_nand_cmdfunc; + nand->waitfunc = octeontx_nand_waitfunc; + nand->read_byte = octeontx_nand_read_byte; + nand->read_buf = octeontx_nand_read_buf; + nand->write_buf = octeontx_nand_write_buf; + nand->onfi_set_features = octeontx_nand_set_features; + nand->onfi_get_features = octeontx_nand_get_features; + nand->setup_data_interface = octeontx_nand_setup_dat_intf; + + mtd = nand_to_mtd(nand); + debug("%s: mtd: %p\n", __func__, mtd); + mtd->dev->parent = dev; + + debug("%s: NDF_MISC: 0x%llx\n", __func__, + readq(tn->base + NDF_MISC)); + + /* TODO: support more then 1 chip */ + debug("%s: Scanning identification\n", __func__); + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + return ret; + + debug("%s: Sizing chip\n", __func__); + octeontx_nfc_chip_sizing(nand); + + debug("%s: Scanning tail\n", __func__); + ret = nand_scan_tail(mtd); + if (ret) { + dev_err(dev, "nand_scan_tail failed: %d\n", ret); + return ret; + } + + debug("%s: Registering mtd\n", __func__); + ret = nand_register(0, mtd); + + debug("%s: Adding tail\n", __func__); + list_add_tail(&chip->node, &tn->chips); + return 0; +} + +static int octeontx_nfc_chips_init(struct octeontx_nfc *tn) +{ + struct udevice *dev = tn->dev; + ofnode node = dev->node; + ofnode nand_node; + int nr_chips = of_get_child_count(node); + int ret; + + debug("%s: node: %s\n", __func__, ofnode_get_name(node)); + debug("%s: %d chips\n", __func__, nr_chips); + if (nr_chips > NAND_MAX_CHIPS) { + dev_err(dev, "too many NAND chips: %d\n", nr_chips); + return -EINVAL; + } + + if (!nr_chips) { + debug("no DT NAND chips found\n"); + return -ENODEV; + } + + pr_info("%s: scanning %d chips DTs\n", __func__, nr_chips); + + ofnode_for_each_subnode(nand_node, node) { + debug("%s: Calling octeontx_nfc_chip_init(%p, %s, %ld)\n", + __func__, tn, dev->name, nand_node.of_offset); + ret = octeontx_nfc_chip_init(tn, dev, nand_node); + if (ret) + return ret; + } + return 0; +} + +/* Reset NFC and initialize registers. */ +static int octeontx_nfc_init(struct octeontx_nfc *tn) +{ + const struct nand_sdr_timings *timings; + u64 ndf_misc; + int rc; + + /* Initialize values and reset the fifo */ + ndf_misc = readq(tn->base + NDF_MISC); + + ndf_misc &= ~NDF_MISC_EX_DIS; + ndf_misc |= (NDF_MISC_BT_DIS | NDF_MISC_RST_FF); + writeq(ndf_misc, tn->base + NDF_MISC); + debug("%s: NDF_MISC: 0x%llx\n", __func__, readq(tn->base + NDF_MISC)); + + /* Bring the fifo out of reset */ + ndf_misc &= ~(NDF_MISC_RST_FF); + + /* Maximum of co-processor cycles for glitch filtering */ + ndf_misc |= FIELD_PREP(NDF_MISC_WAIT_CNT, 0x3f); + + writeq(ndf_misc, tn->base + NDF_MISC); + + /* Set timing parameters to onfi mode 0 for probing */ + timings = onfi_async_timing_mode_to_sdr_timings(0); + if (IS_ERR(timings)) + return PTR_ERR(timings); + rc = set_default_timings(tn, timings); + if (rc) + return rc; + + return 0; +} + +static int octeontx_pci_nand_probe(struct udevice *dev) +{ + struct octeontx_nfc *tn = dev_get_priv(dev); + int ret; + static bool probe_done; + + debug("%s(%s) tn: %p\n", __func__, dev->name, tn); + if (probe_done) + return 0; + + if (IS_ENABLED(CONFIG_NAND_OCTEONTX_HW_ECC)) { + bch_vf = octeontx_bch_getv(); + if (!bch_vf) { + struct octeontx_probe_device *probe_dev; + + debug("%s: bch not yet initialized\n", __func__); + probe_dev = calloc(sizeof(*probe_dev), 1); + if (!probe_dev) { + printf("%s: Out of memory\n", __func__); + return -ENOMEM; + } + probe_dev->dev = dev; + INIT_LIST_HEAD(&probe_dev->list); + list_add_tail(&probe_dev->list, + &octeontx_pci_nand_deferred_devices); + debug("%s: Defering probe until after BCH initialization\n", + __func__); + return 0; + } + } + + tn->dev = dev; + INIT_LIST_HEAD(&tn->chips); + + tn->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, PCI_REGION_MEM); + if (!tn->base) { + ret = -EINVAL; + goto release; + } + debug("%s: bar at %p\n", __func__, tn->base); + tn->buf.dmabuflen = NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE; + tn->buf.dmabuf = dma_alloc_coherent(tn->buf.dmabuflen, + (unsigned long *)&tn->buf.dmaaddr); + if (!tn->buf.dmabuf) { + ret = -ENOMEM; + debug("%s: Could not allocate DMA buffer\n", __func__); + goto unclk; + } + + /* one hw-bch response, for one outstanding transaction */ + tn->bch_resp = dma_alloc_coherent(sizeof(*tn->bch_resp), + (unsigned long *)&tn->bch_rhandle); + + tn->stat = dma_alloc_coherent(8, (unsigned long *)&tn->stat_addr); + if (!tn->stat || !tn->bch_resp) { + debug("%s: Could not allocate bch status or response\n", + __func__); + ret = -ENOMEM; + goto unclk; + } + + debug("%s: Calling octeontx_nfc_init()\n", __func__); + octeontx_nfc_init(tn); + debug("%s: Initializing chips\n", __func__); + ret = octeontx_nfc_chips_init(tn); + debug("%s: init chips ret: %d\n", __func__, ret); + if (ret) { + if (ret != -ENODEV) + dev_err(dev, "failed to init nand chips\n"); + goto unclk; + } + dev_info(dev, "probed\n"); + return 0; + +unclk: +release: + return ret; +} + +int octeontx_pci_nand_disable(struct udevice *dev) +{ + struct octeontx_nfc *tn = dev_get_priv(dev); + u64 dma_cfg; + u64 ndf_misc; + + debug("%s: Disabling NAND device %s\n", __func__, dev->name); + dma_cfg = readq(tn->base + NDF_DMA_CFG); + dma_cfg &= ~NDF_DMA_CFG_EN; + dma_cfg |= NDF_DMA_CFG_CLR; + writeq(dma_cfg, tn->base + NDF_DMA_CFG); + + /* Disable execution and put FIFO in reset mode */ + ndf_misc = readq(tn->base + NDF_MISC); + ndf_misc |= NDF_MISC_EX_DIS | NDF_MISC_RST_FF; + writeq(ndf_misc, tn->base + NDF_MISC); + ndf_misc &= ~NDF_MISC_RST_FF; + writeq(ndf_misc, tn->base + NDF_MISC); +#ifdef DEBUG + printf("%s: NDF_MISC: 0x%llx\n", __func__, readq(tn->base + NDF_MISC)); +#endif + /* Clear any interrupts and enable bits */ + writeq(~0ull, tn->base + NDF_INT_ENA_W1C); + writeq(~0ull, tn->base + NDF_INT); + debug("%s: NDF_ST_REG: 0x%llx\n", __func__, + readq(tn->base + NDF_ST_REG)); + return 0; +} + +/** + * Since it's possible (and even likely) that the NAND device will be probed + * before the BCH device has been probed, we may need to defer the probing. + * + * In this case, the initial probe returns success but the actual probing + * is deferred until the BCH VF has been probed. + * + * @return 0 for success, otherwise error + */ +int octeontx_pci_nand_deferred_probe(void) +{ + int rc = 0; + struct octeontx_probe_device *pdev; + + debug("%s: Performing deferred probing\n", __func__); + list_for_each_entry(pdev, &octeontx_pci_nand_deferred_devices, list) { + debug("%s: Probing %s\n", __func__, pdev->dev->name); + pdev->dev->flags &= ~DM_FLAG_ACTIVATED; + rc = device_probe(pdev->dev); + if (rc && rc != -ENODEV) { + printf("%s: Error %d with deferred probe of %s\n", + __func__, rc, pdev->dev->name); + break; + } + } + return rc; +} + +static const struct pci_device_id octeontx_nfc_pci_id_table[] = { + { PCI_VDEVICE(CAVIUM, 0xA04F) }, + {} +}; + +static int octeontx_nand_ofdata_to_platdata(struct udevice *dev) +{ + return 0; +} + +static const struct udevice_id octeontx_nand_ids[] = { + { .compatible = "cavium,cn8130-nand" }, + { }, +}; + +U_BOOT_DRIVER(octeontx_pci_nand) = { + .name = OCTEONTX_NAND_DRIVER_NAME, + .id = UCLASS_MTD, + .of_match = of_match_ptr(octeontx_nand_ids), + .ofdata_to_platdata = octeontx_nand_ofdata_to_platdata, + .probe = octeontx_pci_nand_probe, + .priv_auto_alloc_size = sizeof(struct octeontx_nfc), + .remove = octeontx_pci_nand_disable, + .flags = DM_FLAG_OS_PREPARE, +}; + +U_BOOT_PCI_DEVICE(octeontx_pci_nand, octeontx_nfc_pci_id_table); + +void board_nand_init(void) +{ + struct udevice *dev; + int ret; + + if (IS_ENABLED(CONFIG_NAND_OCTEONTX_HW_ECC)) { + ret = uclass_get_device_by_driver(UCLASS_MISC, + DM_GET_DRIVER(octeontx_pci_bchpf), + &dev); + if (ret && ret != -ENODEV) { + pr_err("Failed to initialize OcteonTX BCH PF controller. (error %d)\n", + ret); + } + ret = uclass_get_device_by_driver(UCLASS_MISC, + DM_GET_DRIVER(octeontx_pci_bchvf), + &dev); + if (ret && ret != -ENODEV) { + pr_err("Failed to initialize OcteonTX BCH VF controller. (error %d)\n", + ret); + } + } + + ret = uclass_get_device_by_driver(UCLASS_MTD, + DM_GET_DRIVER(octeontx_pci_nand), + &dev); + if (ret && ret != -ENODEV) + pr_err("Failed to initialize OcteonTX NAND controller. (error %d)\n", + ret); +} -- cgit v1.2.1 From 0008e9a69da07a93d589b51922d80d9044233acb Mon Sep 17 00:00:00 2001 From: Suneel Garapati Date: Wed, 26 Aug 2020 14:37:33 +0200 Subject: net: Add NIC controller driver for OcteonTX Adds support for Network Interface controllers found on OcteonTX SoC platforms. Signed-off-by: Suneel Garapati Signed-off-by: Stefan Roese Cc: Joe Hershberger --- drivers/net/Kconfig | 14 + drivers/net/Makefile | 2 + drivers/net/octeontx/Makefile | 7 + drivers/net/octeontx/bgx.c | 1565 +++++++++++++++++++++++++++++++++++ drivers/net/octeontx/bgx.h | 259 ++++++ drivers/net/octeontx/nic.h | 508 ++++++++++++ drivers/net/octeontx/nic_main.c | 778 +++++++++++++++++ drivers/net/octeontx/nic_reg.h | 250 ++++++ drivers/net/octeontx/nicvf_main.c | 581 +++++++++++++ drivers/net/octeontx/nicvf_queues.c | 1140 +++++++++++++++++++++++++ drivers/net/octeontx/nicvf_queues.h | 353 ++++++++ drivers/net/octeontx/q_struct.h | 695 ++++++++++++++++ drivers/net/octeontx/smi.c | 380 +++++++++ drivers/net/octeontx/xcv.c | 124 +++ 14 files changed, 6656 insertions(+) create mode 100644 drivers/net/octeontx/Makefile create mode 100644 drivers/net/octeontx/bgx.c create mode 100644 drivers/net/octeontx/bgx.h create mode 100644 drivers/net/octeontx/nic.h create mode 100644 drivers/net/octeontx/nic_main.c create mode 100644 drivers/net/octeontx/nic_reg.h create mode 100644 drivers/net/octeontx/nicvf_main.c create mode 100644 drivers/net/octeontx/nicvf_queues.c create mode 100644 drivers/net/octeontx/nicvf_queues.h create mode 100644 drivers/net/octeontx/q_struct.h create mode 100644 drivers/net/octeontx/smi.c create mode 100644 drivers/net/octeontx/xcv.c diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index a0d2d21a55..96f23d3d7c 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -407,6 +407,20 @@ config MT7628_ETH The MediaTek MT7628 ethernet interface is used on MT7628 and MT7688 based boards. +config NET_OCTEONTX + bool "OcteonTX Ethernet support" + depends on ARCH_OCTEONTX + depends on PCI_SRIOV + help + You must select Y to enable network device support for + OcteonTX SoCs. If unsure, say n +config OCTEONTX_SMI + bool "OcteonTX SMI Device support" + depends on ARCH_OCTEONTX || ARCH_OCTEONTX2 + help + You must select Y to enable SMI controller support for + OcteonTX or OcteonTX2 SoCs. If unsure, say n + config PCH_GBE bool "Intel Platform Controller Hub EG20T GMAC driver" depends on DM_ETH && DM_PCI diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 03f01921ea..c193953b0e 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -65,6 +65,8 @@ obj-$(CONFIG_RENESAS_RAVB) += ravb.o obj-$(CONFIG_SMC91111) += smc91111.o obj-$(CONFIG_SMC911X) += smc911x.o obj-$(CONFIG_TSEC_ENET) += tsec.o fsl_mdio.o +obj-$(CONFIG_NET_OCTEONTX) += octeontx/ +obj-$(CONFIG_OCTEONTX_SMI) += octeontx/smi.o obj-$(CONFIG_FMAN_ENET) += fsl_mdio.o obj-$(CONFIG_ULI526X) += uli526x.o obj-$(CONFIG_VSC7385_ENET) += vsc7385.o diff --git a/drivers/net/octeontx/Makefile b/drivers/net/octeontx/Makefile new file mode 100644 index 0000000000..d4adb7cdba --- /dev/null +++ b/drivers/net/octeontx/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (C) 2018 Marvell International Ltd. +# + +obj-$(CONFIG_NET_OCTEONTX) += bgx.o nic_main.o nicvf_queues.o nicvf_main.o \ + xcv.o diff --git a/drivers/net/octeontx/bgx.c b/drivers/net/octeontx/bgx.c new file mode 100644 index 0000000000..fbe2e2c073 --- /dev/null +++ b/drivers/net/octeontx/bgx.c @@ -0,0 +1,1565 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell International Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nic_reg.h" +#include "nic.h" +#include "bgx.h" + +static const phy_interface_t if_mode[] = { + [QLM_MODE_SGMII] = PHY_INTERFACE_MODE_SGMII, + [QLM_MODE_RGMII] = PHY_INTERFACE_MODE_RGMII, + [QLM_MODE_QSGMII] = PHY_INTERFACE_MODE_QSGMII, + [QLM_MODE_XAUI] = PHY_INTERFACE_MODE_XAUI, + [QLM_MODE_RXAUI] = PHY_INTERFACE_MODE_RXAUI, +}; + +struct lmac { + struct bgx *bgx; + int dmac; + u8 mac[6]; + bool link_up; + bool init_pend; + int lmacid; /* ID within BGX */ + int phy_addr; /* ID on board */ + struct udevice *dev; + struct mii_dev *mii_bus; + struct phy_device *phydev; + unsigned int last_duplex; + unsigned int last_link; + unsigned int last_speed; + int lane_to_sds; + int use_training; + int lmac_type; + u8 qlm_mode; + int qlm; + bool is_1gx; +}; + +struct bgx { + u8 bgx_id; + int node; + struct lmac lmac[MAX_LMAC_PER_BGX]; + int lmac_count; + u8 max_lmac; + void __iomem *reg_base; + struct pci_dev *pdev; + bool is_rgx; +}; + +struct bgx_board_info bgx_board_info[MAX_BGX_PER_NODE]; + +struct bgx *bgx_vnic[MAX_BGX_PER_NODE]; + +/* APIs to read/write BGXX CSRs */ +static u64 bgx_reg_read(struct bgx *bgx, uint8_t lmac, u64 offset) +{ + u64 addr = (uintptr_t)bgx->reg_base + + ((uint32_t)lmac << 20) + offset; + + return readq((void *)addr); +} + +static void bgx_reg_write(struct bgx *bgx, uint8_t lmac, + u64 offset, u64 val) +{ + u64 addr = (uintptr_t)bgx->reg_base + + ((uint32_t)lmac << 20) + offset; + + writeq(val, (void *)addr); +} + +static void bgx_reg_modify(struct bgx *bgx, uint8_t lmac, + u64 offset, u64 val) +{ + u64 addr = (uintptr_t)bgx->reg_base + + ((uint32_t)lmac << 20) + offset; + + writeq(val | bgx_reg_read(bgx, lmac, offset), (void *)addr); +} + +static int bgx_poll_reg(struct bgx *bgx, uint8_t lmac, + u64 reg, u64 mask, bool zero) +{ + int timeout = 200; + u64 reg_val; + + while (timeout) { + reg_val = bgx_reg_read(bgx, lmac, reg); + if (zero && !(reg_val & mask)) + return 0; + if (!zero && (reg_val & mask)) + return 0; + mdelay(1); + timeout--; + } + return 1; +} + +static int gser_poll_reg(u64 reg, int bit, u64 mask, u64 expected_val, + int timeout) +{ + u64 reg_val; + + debug("%s reg = %#llx, mask = %#llx,", __func__, reg, mask); + debug(" expected_val = %#llx, bit = %d\n", expected_val, bit); + while (timeout) { + reg_val = readq(reg) >> bit; + if ((reg_val & mask) == (expected_val)) + return 0; + mdelay(1); + timeout--; + } + return 1; +} + +static bool is_bgx_port_valid(int bgx, int lmac) +{ + debug("%s bgx %d lmac %d valid %d\n", __func__, bgx, lmac, + bgx_board_info[bgx].lmac_reg[lmac]); + + if (bgx_board_info[bgx].lmac_reg[lmac]) + return 1; + else + return 0; +} + +struct lmac *bgx_get_lmac(int node, int bgx_idx, int lmacid) +{ + struct bgx *bgx = bgx_vnic[(node * MAX_BGX_PER_NODE) + bgx_idx]; + + if (bgx) + return &bgx->lmac[lmacid]; + + return NULL; +} + +const u8 *bgx_get_lmac_mac(int node, int bgx_idx, int lmacid) +{ + struct bgx *bgx = bgx_vnic[(node * MAX_BGX_PER_NODE) + bgx_idx]; + + if (bgx) + return bgx->lmac[lmacid].mac; + + return NULL; +} + +void bgx_set_lmac_mac(int node, int bgx_idx, int lmacid, const u8 *mac) +{ + struct bgx *bgx = bgx_vnic[(node * MAX_BGX_PER_NODE) + bgx_idx]; + + if (!bgx) + return; + + memcpy(bgx->lmac[lmacid].mac, mac, 6); +} + +/* Return number of BGX present in HW */ +void bgx_get_count(int node, int *bgx_count) +{ + int i; + struct bgx *bgx; + + *bgx_count = 0; + for (i = 0; i < MAX_BGX_PER_NODE; i++) { + bgx = bgx_vnic[node * MAX_BGX_PER_NODE + i]; + debug("bgx_vnic[%u]: %p\n", node * MAX_BGX_PER_NODE + i, + bgx); + if (bgx) + *bgx_count |= (1 << i); + } +} + +/* Return number of LMAC configured for this BGX */ +int bgx_get_lmac_count(int node, int bgx_idx) +{ + struct bgx *bgx; + + bgx = bgx_vnic[(node * MAX_BGX_PER_NODE) + bgx_idx]; + if (bgx) + return bgx->lmac_count; + + return 0; +} + +void bgx_lmac_rx_tx_enable(int node, int bgx_idx, int lmacid, bool enable) +{ + struct bgx *bgx = bgx_vnic[(node * MAX_BGX_PER_NODE) + bgx_idx]; + u64 cfg; + + if (!bgx) + return; + + cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG); + if (enable) + cfg |= CMR_PKT_RX_EN | CMR_PKT_TX_EN; + else + cfg &= ~(CMR_PKT_RX_EN | CMR_PKT_TX_EN); + bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg); +} + +static void bgx_flush_dmac_addrs(struct bgx *bgx, u64 lmac) +{ + u64 dmac = 0x00; + u64 offset, addr; + + while (bgx->lmac[lmac].dmac > 0) { + offset = ((bgx->lmac[lmac].dmac - 1) * sizeof(dmac)) + + (lmac * MAX_DMAC_PER_LMAC * sizeof(dmac)); + addr = (uintptr_t)bgx->reg_base + + BGX_CMR_RX_DMACX_CAM + offset; + writeq(dmac, (void *)addr); + bgx->lmac[lmac].dmac--; + } +} + +/* Configure BGX LMAC in internal loopback mode */ +void bgx_lmac_internal_loopback(int node, int bgx_idx, + int lmac_idx, bool enable) +{ + struct bgx *bgx; + struct lmac *lmac; + u64 cfg; + + bgx = bgx_vnic[(node * MAX_BGX_PER_NODE) + bgx_idx]; + if (!bgx) + return; + + lmac = &bgx->lmac[lmac_idx]; + if (lmac->qlm_mode == QLM_MODE_SGMII) { + cfg = bgx_reg_read(bgx, lmac_idx, BGX_GMP_PCS_MRX_CTL); + if (enable) + cfg |= PCS_MRX_CTL_LOOPBACK1; + else + cfg &= ~PCS_MRX_CTL_LOOPBACK1; + bgx_reg_write(bgx, lmac_idx, BGX_GMP_PCS_MRX_CTL, cfg); + } else { + cfg = bgx_reg_read(bgx, lmac_idx, BGX_SPUX_CONTROL1); + if (enable) + cfg |= SPU_CTL_LOOPBACK; + else + cfg &= ~SPU_CTL_LOOPBACK; + bgx_reg_write(bgx, lmac_idx, BGX_SPUX_CONTROL1, cfg); + } +} + +/* Return the DLM used for the BGX */ +static int get_qlm_for_bgx(int node, int bgx_id, int index) +{ + int qlm = 0; + u64 cfg; + + if (otx_is_soc(CN81XX)) { + qlm = (bgx_id) ? 2 : 0; + qlm += (index >= 2) ? 1 : 0; + } else if (otx_is_soc(CN83XX)) { + switch (bgx_id) { + case 0: + qlm = 2; + break; + case 1: + qlm = 3; + break; + case 2: + if (index >= 2) + qlm = 6; + else + qlm = 5; + break; + case 3: + qlm = 4; + break; + } + } + + cfg = readq(GSERX_CFG(qlm)) & GSERX_CFG_BGX; + debug("%s:qlm%d: cfg = %lld\n", __func__, qlm, cfg); + + /* Check if DLM is configured as BGX# */ + if (cfg) { + if (readq(GSERX_PHY_CTL(qlm))) + return -1; + return qlm; + } + return -1; +} + +static int bgx_lmac_sgmii_init(struct bgx *bgx, int lmacid) +{ + u64 cfg; + struct lmac *lmac; + + lmac = &bgx->lmac[lmacid]; + + debug("%s:bgx_id = %d, lmacid = %d\n", __func__, bgx->bgx_id, lmacid); + + bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_TXX_THRESH, 0x30); + /* max packet size */ + bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_RXX_JABBER, MAX_FRAME_SIZE); + + /* Disable frame alignment if using preamble */ + cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND); + if (cfg & 1) + bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_SGMII_CTL, 0); + + /* Enable lmac */ + bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, CMR_EN); + + /* PCS reset */ + bgx_reg_modify(bgx, lmacid, BGX_GMP_PCS_MRX_CTL, PCS_MRX_CTL_RESET); + if (bgx_poll_reg(bgx, lmacid, BGX_GMP_PCS_MRX_CTL, + PCS_MRX_CTL_RESET, true)) { + printf("BGX PCS reset not completed\n"); + return -1; + } + + /* power down, reset autoneg, autoneg enable */ + cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_PCS_MRX_CTL); + cfg &= ~PCS_MRX_CTL_PWR_DN; + + if (bgx_board_info[bgx->bgx_id].phy_info[lmacid].autoneg_dis) + cfg |= (PCS_MRX_CTL_RST_AN); + else + cfg |= (PCS_MRX_CTL_RST_AN | PCS_MRX_CTL_AN_EN); + bgx_reg_write(bgx, lmacid, BGX_GMP_PCS_MRX_CTL, cfg); + + /* Disable disparity for QSGMII mode, to prevent propogation across + * ports. + */ + + if (lmac->qlm_mode == QLM_MODE_QSGMII) { + cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_PCS_MISCX_CTL); + cfg &= ~PCS_MISCX_CTL_DISP_EN; + bgx_reg_write(bgx, lmacid, BGX_GMP_PCS_MISCX_CTL, cfg); + return 0; /* Skip checking AN_CPT */ + } + + if (lmac->is_1gx) { + cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_PCS_MISCX_CTL); + cfg |= PCS_MISC_CTL_MODE; + bgx_reg_write(bgx, lmacid, BGX_GMP_PCS_MISCX_CTL, cfg); + } + + if (lmac->qlm_mode == QLM_MODE_SGMII) { + if (bgx_poll_reg(bgx, lmacid, BGX_GMP_PCS_MRX_STATUS, + PCS_MRX_STATUS_AN_CPT, false)) { + printf("BGX AN_CPT not completed\n"); + return -1; + } + } + + return 0; +} + +static int bgx_lmac_sgmii_set_link_speed(struct lmac *lmac) +{ + u64 prtx_cfg; + u64 pcs_miscx_ctl; + u64 cfg; + struct bgx *bgx = lmac->bgx; + unsigned int lmacid = lmac->lmacid; + + debug("%s: lmacid %d\n", __func__, lmac->lmacid); + + /* Disable LMAC before setting up speed */ + cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG); + cfg &= ~CMR_EN; + bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg); + + /* Read GMX CFG */ + prtx_cfg = bgx_reg_read(bgx, lmacid, + BGX_GMP_GMI_PRTX_CFG); + /* Read PCS MISCS CTL */ + pcs_miscx_ctl = bgx_reg_read(bgx, lmacid, + BGX_GMP_PCS_MISCX_CTL); + + /* Use GMXENO to force the link down*/ + if (lmac->link_up) { + pcs_miscx_ctl &= ~PCS_MISC_CTL_GMX_ENO; + /* change the duplex setting if the link is up */ + prtx_cfg |= GMI_PORT_CFG_DUPLEX; + } else { + pcs_miscx_ctl |= PCS_MISC_CTL_GMX_ENO; + } + + /* speed based setting for GMX */ + switch (lmac->last_speed) { + case 10: + prtx_cfg &= ~GMI_PORT_CFG_SPEED; + prtx_cfg |= GMI_PORT_CFG_SPEED_MSB; + prtx_cfg &= ~GMI_PORT_CFG_SLOT_TIME; + pcs_miscx_ctl |= 50; /* sampling point */ + bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_SLOT, 0x40); + bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_BURST, 0); + break; + case 100: + prtx_cfg &= ~GMI_PORT_CFG_SPEED; + prtx_cfg &= ~GMI_PORT_CFG_SPEED_MSB; + prtx_cfg &= ~GMI_PORT_CFG_SLOT_TIME; + pcs_miscx_ctl |= 0x5; /* sampling point */ + bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_SLOT, 0x40); + bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_BURST, 0); + break; + case 1000: + prtx_cfg |= GMI_PORT_CFG_SPEED; + prtx_cfg &= ~GMI_PORT_CFG_SPEED_MSB; + prtx_cfg |= GMI_PORT_CFG_SLOT_TIME; + pcs_miscx_ctl |= 0x1; /* sampling point */ + bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_SLOT, 0x200); + if (lmac->last_duplex) + bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_BURST, 0); + else /* half duplex */ + bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_BURST, + 0x2000); + break; + default: + break; + } + + /* write back the new PCS misc and GMX settings */ + bgx_reg_write(bgx, lmacid, BGX_GMP_PCS_MISCX_CTL, pcs_miscx_ctl); + bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_PRTX_CFG, prtx_cfg); + + /* read back GMX CFG again to check config completion */ + bgx_reg_read(bgx, lmacid, BGX_GMP_GMI_PRTX_CFG); + + /* enable BGX back */ + cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG); + cfg |= CMR_EN; + bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg); + + return 0; +} + +static int bgx_lmac_xaui_init(struct bgx *bgx, int lmacid, int lmac_type) +{ + u64 cfg; + struct lmac *lmac; + + lmac = &bgx->lmac[lmacid]; + + /* Reset SPU */ + bgx_reg_modify(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_RESET); + if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_RESET, true)) { + printf("BGX SPU reset not completed\n"); + return -1; + } + + /* Disable LMAC */ + cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG); + cfg &= ~CMR_EN; + bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg); + + bgx_reg_modify(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_LOW_POWER); + /* Set interleaved running disparity for RXAUI */ + if (lmac->qlm_mode != QLM_MODE_RXAUI) + bgx_reg_modify(bgx, lmacid, + BGX_SPUX_MISC_CONTROL, SPU_MISC_CTL_RX_DIS); + else + bgx_reg_modify(bgx, lmacid, BGX_SPUX_MISC_CONTROL, + SPU_MISC_CTL_RX_DIS | SPU_MISC_CTL_INTLV_RDISP); + + /* clear all interrupts */ + cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_RX_INT); + bgx_reg_write(bgx, lmacid, BGX_SMUX_RX_INT, cfg); + cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_INT); + bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_INT, cfg); + cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT); + bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg); + + if (lmac->use_training) { + bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LP_CUP, 0x00); + bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LD_CUP, 0x00); + bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LD_REP, 0x00); + /* training enable */ + bgx_reg_modify(bgx, lmacid, + BGX_SPUX_BR_PMD_CRTL, SPU_PMD_CRTL_TRAIN_EN); + } + + /* Append FCS to each packet */ + bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_APPEND, SMU_TX_APPEND_FCS_D); + + /* Disable forward error correction */ + cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_FEC_CONTROL); + cfg &= ~SPU_FEC_CTL_FEC_EN; + bgx_reg_write(bgx, lmacid, BGX_SPUX_FEC_CONTROL, cfg); + + /* Disable autoneg */ + cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_AN_CONTROL); + cfg = cfg & ~(SPU_AN_CTL_XNP_EN); + if (lmac->use_training) + cfg = cfg | (SPU_AN_CTL_AN_EN); + else + cfg = cfg & ~(SPU_AN_CTL_AN_EN); + bgx_reg_write(bgx, lmacid, BGX_SPUX_AN_CONTROL, cfg); + + cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_AN_ADV); + /* Clear all KR bits, configure according to the mode */ + cfg &= ~((0xfULL << 22) | (1ULL << 12)); + if (lmac->qlm_mode == QLM_MODE_10G_KR) + cfg |= (1 << 23); + else if (lmac->qlm_mode == QLM_MODE_40G_KR4) + cfg |= (1 << 24); + bgx_reg_write(bgx, lmacid, BGX_SPUX_AN_ADV, cfg); + + cfg = bgx_reg_read(bgx, 0, BGX_SPU_DBG_CONTROL); + if (lmac->use_training) + cfg |= SPU_DBG_CTL_AN_ARB_LINK_CHK_EN; + else + cfg &= ~SPU_DBG_CTL_AN_ARB_LINK_CHK_EN; + bgx_reg_write(bgx, 0, BGX_SPU_DBG_CONTROL, cfg); + + /* Enable lmac */ + bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, CMR_EN); + + cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_CONTROL1); + cfg &= ~SPU_CTL_LOW_POWER; + bgx_reg_write(bgx, lmacid, BGX_SPUX_CONTROL1, cfg); + + cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_CTL); + cfg &= ~SMU_TX_CTL_UNI_EN; + cfg |= SMU_TX_CTL_DIC_EN; + bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_CTL, cfg); + + /* take lmac_count into account */ + bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_THRESH, (0x100 - 1)); + /* max packet size */ + bgx_reg_modify(bgx, lmacid, BGX_SMUX_RX_JABBER, MAX_FRAME_SIZE); + + debug("xaui_init: lmacid = %d, qlm = %d, qlm_mode = %d\n", + lmacid, lmac->qlm, lmac->qlm_mode); + /* RXAUI with Marvell PHY requires some tweaking */ + if (lmac->qlm_mode == QLM_MODE_RXAUI) { + char mii_name[20]; + struct phy_info *phy; + + phy = &bgx_board_info[bgx->bgx_id].phy_info[lmacid]; + snprintf(mii_name, sizeof(mii_name), "smi%d", phy->mdio_bus); + + debug("mii_name: %s\n", mii_name); + lmac->mii_bus = miiphy_get_dev_by_name(mii_name); + lmac->phy_addr = phy->phy_addr; + rxaui_phy_xs_init(lmac->mii_bus, lmac->phy_addr); + } + + return 0; +} + +/* Get max number of lanes present in a given QLM/DLM */ +static int get_qlm_lanes(int qlm) +{ + if (otx_is_soc(CN81XX)) + return 2; + else if (otx_is_soc(CN83XX)) + return (qlm >= 5) ? 2 : 4; + else + return -1; +} + +int __rx_equalization(int qlm, int lane) +{ + int max_lanes = get_qlm_lanes(qlm); + int l; + int fail = 0; + + /* Before completing Rx equalization wait for + * GSERx_RX_EIE_DETSTS[CDRLOCK] to be set + * This ensures the rx data is valid + */ + if (lane == -1) { + if (gser_poll_reg(GSER_RX_EIE_DETSTS(qlm), GSER_CDRLOCK, 0xf, + (1 << max_lanes) - 1, 100)) { + debug("ERROR: CDR Lock not detected"); + debug(" on DLM%d for 2 lanes\n", qlm); + return -1; + } + } else { + if (gser_poll_reg(GSER_RX_EIE_DETSTS(qlm), GSER_CDRLOCK, + (0xf & (1 << lane)), (1 << lane), 100)) { + debug("ERROR: DLM%d: CDR Lock not detected", qlm); + debug(" on %d lane\n", lane); + return -1; + } + } + + for (l = 0; l < max_lanes; l++) { + u64 rctl, reer; + + if (lane != -1 && lane != l) + continue; + + /* Enable software control */ + rctl = readq(GSER_BR_RXX_CTL(qlm, l)); + rctl |= GSER_BR_RXX_CTL_RXT_SWM; + writeq(rctl, GSER_BR_RXX_CTL(qlm, l)); + + /* Clear the completion flag and initiate a new request */ + reer = readq(GSER_BR_RXX_EER(qlm, l)); + reer &= ~GSER_BR_RXX_EER_RXT_ESV; + reer |= GSER_BR_RXX_EER_RXT_EER; + writeq(reer, GSER_BR_RXX_EER(qlm, l)); + } + + /* Wait for RX equalization to complete */ + for (l = 0; l < max_lanes; l++) { + u64 rctl, reer; + + if (lane != -1 && lane != l) + continue; + + gser_poll_reg(GSER_BR_RXX_EER(qlm, l), EER_RXT_ESV, 1, 1, 200); + reer = readq(GSER_BR_RXX_EER(qlm, l)); + + /* Switch back to hardware control */ + rctl = readq(GSER_BR_RXX_CTL(qlm, l)); + rctl &= ~GSER_BR_RXX_CTL_RXT_SWM; + writeq(rctl, GSER_BR_RXX_CTL(qlm, l)); + + if (reer & GSER_BR_RXX_EER_RXT_ESV) { + debug("Rx equalization completed on DLM%d", qlm); + debug(" QLM%d rxt_esm = 0x%llx\n", l, (reer & 0x3fff)); + } else { + debug("Rx equalization timedout on DLM%d", qlm); + debug(" lane %d\n", l); + fail = 1; + } + } + + return (fail) ? -1 : 0; +} + +static int bgx_xaui_check_link(struct lmac *lmac) +{ + struct bgx *bgx = lmac->bgx; + int lmacid = lmac->lmacid; + int lmac_type = lmac->lmac_type; + u64 cfg; + + bgx_reg_modify(bgx, lmacid, BGX_SPUX_MISC_CONTROL, SPU_MISC_CTL_RX_DIS); + + /* check if auto negotiation is complete */ + cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_AN_CONTROL); + if (cfg & SPU_AN_CTL_AN_EN) { + cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_AN_STATUS); + if (!(cfg & SPU_AN_STS_AN_COMPLETE)) { + /* Restart autonegotiation */ + debug("restarting auto-neg\n"); + bgx_reg_modify(bgx, lmacid, BGX_SPUX_AN_CONTROL, + SPU_AN_CTL_AN_RESTART); + return -1; + } + } + + debug("%s link use_training %d\n", __func__, lmac->use_training); + if (lmac->use_training) { + cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT); + if (!(cfg & (1ull << 13))) { + debug("waiting for link training\n"); + /* Clear the training interrupts (W1C) */ + cfg = (1ull << 13) | (1ull << 14); + bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg); + + udelay(2000); + /* Restart training */ + cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_BR_PMD_CRTL); + cfg |= (1ull << 0); + bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_CRTL, cfg); + return -1; + } + } + + /* Perform RX Equalization. Applies to non-KR interfaces for speeds + * >= 6.25Gbps. + */ + if (!lmac->use_training) { + int qlm; + bool use_dlm = 0; + + if (otx_is_soc(CN81XX) || (otx_is_soc(CN83XX) && + bgx->bgx_id == 2)) + use_dlm = 1; + switch (lmac->lmac_type) { + default: + case BGX_MODE_SGMII: + case BGX_MODE_RGMII: + case BGX_MODE_XAUI: + /* Nothing to do */ + break; + case BGX_MODE_XLAUI: + if (use_dlm) { + if (__rx_equalization(lmac->qlm, -1) || + __rx_equalization(lmac->qlm + 1, -1)) { + printf("BGX%d:%d", bgx->bgx_id, lmacid); + printf(" Waiting for RX Equalization"); + printf(" on DLM%d/DLM%d\n", + lmac->qlm, lmac->qlm + 1); + return -1; + } + } else { + if (__rx_equalization(lmac->qlm, -1)) { + printf("BGX%d:%d", bgx->bgx_id, lmacid); + printf(" Waiting for RX Equalization"); + printf(" on QLM%d\n", lmac->qlm); + return -1; + } + } + break; + case BGX_MODE_RXAUI: + /* RXAUI0 uses LMAC0:QLM0/QLM2 and RXAUI1 uses + * LMAC1:QLM1/QLM3 RXAUI requires 2 lanes + * for each interface + */ + qlm = lmac->qlm; + if (__rx_equalization(qlm, 0)) { + printf("BGX%d:%d", bgx->bgx_id, lmacid); + printf(" Waiting for RX Equalization"); + printf(" on QLM%d, Lane0\n", qlm); + return -1; + } + if (__rx_equalization(qlm, 1)) { + printf("BGX%d:%d", bgx->bgx_id, lmacid); + printf(" Waiting for RX Equalization"); + printf(" on QLM%d, Lane1\n", qlm); + return -1; + } + break; + case BGX_MODE_XFI: + { + int lid; + bool altpkg = otx_is_altpkg(); + + if (bgx->bgx_id == 0 && altpkg && lmacid) + lid = 0; + else if ((lmacid >= 2) && use_dlm) + lid = lmacid - 2; + else + lid = lmacid; + + if (__rx_equalization(lmac->qlm, lid)) { + printf("BGX%d:%d", bgx->bgx_id, lid); + printf(" Waiting for RX Equalization"); + printf(" on QLM%d\n", lmac->qlm); + } + } + break; + } + } + + /* wait for PCS to come out of reset */ + if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_RESET, true)) { + printf("BGX SPU reset not completed\n"); + return -1; + } + + if (lmac_type == 3 || lmac_type == 4) { + if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_BR_STATUS1, + SPU_BR_STATUS_BLK_LOCK, false)) { + printf("SPU_BR_STATUS_BLK_LOCK not completed\n"); + return -1; + } + } else { + if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_BX_STATUS, + SPU_BX_STATUS_RX_ALIGN, false)) { + printf("SPU_BX_STATUS_RX_ALIGN not completed\n"); + return -1; + } + } + + /* Clear rcvflt bit (latching high) and read it back */ + bgx_reg_modify(bgx, lmacid, BGX_SPUX_STATUS2, SPU_STATUS2_RCVFLT); + if (bgx_reg_read(bgx, lmacid, BGX_SPUX_STATUS2) & SPU_STATUS2_RCVFLT) { + printf("Receive fault, retry training\n"); + if (lmac->use_training) { + cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT); + if (!(cfg & (1ull << 13))) { + cfg = (1ull << 13) | (1ull << 14); + bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg); + cfg = bgx_reg_read(bgx, lmacid, + BGX_SPUX_BR_PMD_CRTL); + cfg |= (1ull << 0); + bgx_reg_write(bgx, lmacid, + BGX_SPUX_BR_PMD_CRTL, cfg); + return -1; + } + } + return -1; + } + + /* Wait for MAC RX to be ready */ + if (bgx_poll_reg(bgx, lmacid, BGX_SMUX_RX_CTL, + SMU_RX_CTL_STATUS, true)) { + printf("SMU RX link not okay\n"); + return -1; + } + + /* Wait for BGX RX to be idle */ + if (bgx_poll_reg(bgx, lmacid, BGX_SMUX_CTL, SMU_CTL_RX_IDLE, false)) { + printf("SMU RX not idle\n"); + return -1; + } + + /* Wait for BGX TX to be idle */ + if (bgx_poll_reg(bgx, lmacid, BGX_SMUX_CTL, SMU_CTL_TX_IDLE, false)) { + printf("SMU TX not idle\n"); + return -1; + } + + if (bgx_reg_read(bgx, lmacid, BGX_SPUX_STATUS2) & SPU_STATUS2_RCVFLT) { + printf("Receive fault\n"); + return -1; + } + + /* Receive link is latching low. Force it high and verify it */ + if (!(bgx_reg_read(bgx, lmacid, BGX_SPUX_STATUS1) & + SPU_STATUS1_RCV_LNK)) + bgx_reg_modify(bgx, lmacid, BGX_SPUX_STATUS1, + SPU_STATUS1_RCV_LNK); + if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_STATUS1, + SPU_STATUS1_RCV_LNK, false)) { + printf("SPU receive link down\n"); + return -1; + } + + cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_MISC_CONTROL); + cfg &= ~SPU_MISC_CTL_RX_DIS; + bgx_reg_write(bgx, lmacid, BGX_SPUX_MISC_CONTROL, cfg); + return 0; +} + +static int bgx_lmac_enable(struct bgx *bgx, int8_t lmacid) +{ + struct lmac *lmac; + u64 cfg; + + lmac = &bgx->lmac[lmacid]; + + debug("%s: lmac: %p, lmacid = %d\n", __func__, lmac, lmacid); + + if (lmac->qlm_mode == QLM_MODE_SGMII || + lmac->qlm_mode == QLM_MODE_RGMII || + lmac->qlm_mode == QLM_MODE_QSGMII) { + if (bgx_lmac_sgmii_init(bgx, lmacid)) { + debug("bgx_lmac_sgmii_init failed\n"); + return -1; + } + cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND); + cfg |= ((1ull << 2) | (1ull << 1)); /* FCS and PAD */ + bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND, cfg); + bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_MIN_PKT, 60 - 1); + } else { + if (bgx_lmac_xaui_init(bgx, lmacid, lmac->lmac_type)) + return -1; + cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_APPEND); + cfg |= ((1ull << 2) | (1ull << 1)); /* FCS and PAD */ + bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_APPEND, cfg); + bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_MIN_PKT, 60 + 4); + } + + /* Enable lmac */ + bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, + CMR_EN | CMR_PKT_RX_EN | CMR_PKT_TX_EN); + + return 0; +} + +int bgx_poll_for_link(int node, int bgx_idx, int lmacid) +{ + int ret; + struct lmac *lmac = bgx_get_lmac(node, bgx_idx, lmacid); + char mii_name[10]; + struct phy_info *phy; + + if (!lmac) { + printf("LMAC %d/%d/%d is disabled or doesn't exist\n", + node, bgx_idx, lmacid); + return 0; + } + + debug("%s: %d, lmac: %d/%d/%d %p\n", + __FILE__, __LINE__, + node, bgx_idx, lmacid, lmac); + if (lmac->init_pend) { + ret = bgx_lmac_enable(lmac->bgx, lmacid); + if (ret < 0) { + printf("BGX%d LMAC%d lmac_enable failed\n", bgx_idx, + lmacid); + return ret; + } + lmac->init_pend = 0; + mdelay(100); + } + if (lmac->qlm_mode == QLM_MODE_SGMII || + lmac->qlm_mode == QLM_MODE_RGMII || + lmac->qlm_mode == QLM_MODE_QSGMII) { + if (bgx_board_info[bgx_idx].phy_info[lmacid].phy_addr == -1) { + lmac->link_up = 1; + lmac->last_speed = 1000; + lmac->last_duplex = 1; + printf("BGX%d:LMAC %u link up\n", bgx_idx, lmacid); + return lmac->link_up; + } + snprintf(mii_name, sizeof(mii_name), "smi%d", + bgx_board_info[bgx_idx].phy_info[lmacid].mdio_bus); + + debug("mii_name: %s\n", mii_name); + + lmac->mii_bus = miiphy_get_dev_by_name(mii_name); + phy = &bgx_board_info[bgx_idx].phy_info[lmacid]; + lmac->phy_addr = phy->phy_addr; + + debug("lmac->mii_bus: %p\n", lmac->mii_bus); + if (!lmac->mii_bus) { + printf("MDIO device %s not found\n", mii_name); + ret = -ENODEV; + return ret; + } + + lmac->phydev = phy_connect(lmac->mii_bus, lmac->phy_addr, + lmac->dev, + if_mode[lmac->qlm_mode]); + + if (!lmac->phydev) { + printf("%s: No PHY device\n", __func__); + return -1; + } + + ret = phy_config(lmac->phydev); + if (ret) { + printf("%s: Could not initialize PHY %s\n", + __func__, lmac->phydev->dev->name); + return ret; + } + + ret = phy_startup(lmac->phydev); + debug("%s: %d\n", __FILE__, __LINE__); + if (ret) { + printf("%s: Could not initialize PHY %s\n", + __func__, lmac->phydev->dev->name); + } + +#ifdef OCTEONTX_XCV + if (lmac->qlm_mode == QLM_MODE_RGMII) + xcv_setup_link(lmac->phydev->link, lmac->phydev->speed); +#endif + + lmac->link_up = lmac->phydev->link; + lmac->last_speed = lmac->phydev->speed; + lmac->last_duplex = lmac->phydev->duplex; + + debug("%s qlm_mode %d phy link status 0x%x,last speed 0x%x,", + __func__, lmac->qlm_mode, lmac->link_up, + lmac->last_speed); + debug(" duplex 0x%x\n", lmac->last_duplex); + + if (lmac->qlm_mode != QLM_MODE_RGMII) + bgx_lmac_sgmii_set_link_speed(lmac); + + } else { + u64 status1; + u64 tx_ctl; + u64 rx_ctl; + + status1 = bgx_reg_read(lmac->bgx, lmac->lmacid, + BGX_SPUX_STATUS1); + tx_ctl = bgx_reg_read(lmac->bgx, lmac->lmacid, BGX_SMUX_TX_CTL); + rx_ctl = bgx_reg_read(lmac->bgx, lmac->lmacid, BGX_SMUX_RX_CTL); + + debug("BGX%d LMAC%d BGX_SPUX_STATUS2: %lx\n", bgx_idx, lmacid, + (unsigned long)bgx_reg_read(lmac->bgx, lmac->lmacid, + BGX_SPUX_STATUS2)); + debug("BGX%d LMAC%d BGX_SPUX_STATUS1: %lx\n", bgx_idx, lmacid, + (unsigned long)bgx_reg_read(lmac->bgx, lmac->lmacid, + BGX_SPUX_STATUS1)); + debug("BGX%d LMAC%d BGX_SMUX_RX_CTL: %lx\n", bgx_idx, lmacid, + (unsigned long)bgx_reg_read(lmac->bgx, lmac->lmacid, + BGX_SMUX_RX_CTL)); + debug("BGX%d LMAC%d BGX_SMUX_TX_CTL: %lx\n", bgx_idx, lmacid, + (unsigned long)bgx_reg_read(lmac->bgx, lmac->lmacid, + BGX_SMUX_TX_CTL)); + + if ((status1 & SPU_STATUS1_RCV_LNK) && + ((tx_ctl & SMU_TX_CTL_LNK_STATUS) == 0) && + ((rx_ctl & SMU_RX_CTL_STATUS) == 0)) { + lmac->link_up = 1; + if (lmac->lmac_type == 4) + lmac->last_speed = 40000; + else + lmac->last_speed = 10000; + lmac->last_duplex = 1; + } else { + lmac->link_up = 0; + lmac->last_speed = 0; + lmac->last_duplex = 0; + return bgx_xaui_check_link(lmac); + } + + lmac->last_link = lmac->link_up; + } + + printf("BGX%d:LMAC %u link %s\n", bgx_idx, lmacid, + (lmac->link_up) ? "up" : "down"); + + return lmac->link_up; +} + +void bgx_lmac_disable(struct bgx *bgx, uint8_t lmacid) +{ + struct lmac *lmac; + u64 cmrx_cfg; + + lmac = &bgx->lmac[lmacid]; + + cmrx_cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG); + cmrx_cfg &= ~(1 << 15); + bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cmrx_cfg); + bgx_flush_dmac_addrs(bgx, lmacid); + + if (lmac->phydev) + phy_shutdown(lmac->phydev); + + lmac->phydev = NULL; +} + +/* Program BGXX_CMRX_CONFIG.{lmac_type,lane_to_sds} for each interface. + * And the number of LMACs used by this interface. Each lmac can be in + * programmed in a different mode, so parse each lmac one at a time. + */ +static void bgx_init_hw(struct bgx *bgx) +{ + struct lmac *lmac; + int i, lmacid, count = 0, inc = 0; + char buf[40]; + static int qsgmii_configured; + + for (lmacid = 0; lmacid < MAX_LMAC_PER_BGX; lmacid++) { + struct lmac *tlmac; + + lmac = &bgx->lmac[lmacid]; + debug("%s: lmacid = %d, qlm = %d, mode = %d\n", + __func__, lmacid, lmac->qlm, lmac->qlm_mode); + /* If QLM is not programmed, skip */ + if (lmac->qlm == -1) + continue; + + switch (lmac->qlm_mode) { + case QLM_MODE_SGMII: + { + /* EBB8000 (alternative pkg) has only lane0 present on + * DLM0 and DLM1, skip configuring other lanes + */ + if (bgx->bgx_id == 0 && otx_is_altpkg()) { + if (lmacid % 2) + continue; + } + lmac->lane_to_sds = lmacid; + lmac->lmac_type = 0; + snprintf(buf, sizeof(buf), + "BGX%d QLM%d LMAC%d mode: %s\n", + bgx->bgx_id, lmac->qlm, lmacid, + lmac->is_1gx ? "1000Base-X" : "SGMII"); + break; + } + case QLM_MODE_XAUI: + if (lmacid != 0) + continue; + lmac->lmac_type = 1; + lmac->lane_to_sds = 0xE4; + snprintf(buf, sizeof(buf), + "BGX%d QLM%d LMAC%d mode: XAUI\n", + bgx->bgx_id, lmac->qlm, lmacid); + break; + case QLM_MODE_RXAUI: + if (lmacid == 0) { + lmac->lmac_type = 2; + lmac->lane_to_sds = 0x4; + } else if (lmacid == 1) { + struct lmac *tlmac; + + tlmac = &bgx->lmac[2]; + if (tlmac->qlm_mode == QLM_MODE_RXAUI) { + lmac->lmac_type = 2; + lmac->lane_to_sds = 0xe; + lmac->qlm = tlmac->qlm; + } + } else { + continue; + } + snprintf(buf, sizeof(buf), + "BGX%d QLM%d LMAC%d mode: RXAUI\n", + bgx->bgx_id, lmac->qlm, lmacid); + break; + case QLM_MODE_XFI: + /* EBB8000 (alternative pkg) has only lane0 present on + * DLM0 and DLM1, skip configuring other lanes + */ + if (bgx->bgx_id == 0 && otx_is_altpkg()) { + if (lmacid % 2) + continue; + } + lmac->lane_to_sds = lmacid; + lmac->lmac_type = 3; + snprintf(buf, sizeof(buf), + "BGX%d QLM%d LMAC%d mode: XFI\n", + bgx->bgx_id, lmac->qlm, lmacid); + break; + case QLM_MODE_XLAUI: + if (lmacid != 0) + continue; + lmac->lmac_type = 4; + lmac->lane_to_sds = 0xE4; + snprintf(buf, sizeof(buf), + "BGX%d QLM%d LMAC%d mode: XLAUI\n", + bgx->bgx_id, lmac->qlm, lmacid); + break; + case QLM_MODE_10G_KR: + /* EBB8000 (alternative pkg) has only lane0 present on + * DLM0 and DLM1, skip configuring other lanes + */ + if (bgx->bgx_id == 0 && otx_is_altpkg()) { + if (lmacid % 2) + continue; + } + lmac->lane_to_sds = lmacid; + lmac->lmac_type = 3; + lmac->use_training = 1; + snprintf(buf, sizeof(buf), + "BGX%d QLM%d LMAC%d mode: 10G-KR\n", + bgx->bgx_id, lmac->qlm, lmacid); + break; + case QLM_MODE_40G_KR4: + if (lmacid != 0) + continue; + lmac->lmac_type = 4; + lmac->lane_to_sds = 0xE4; + lmac->use_training = 1; + snprintf(buf, sizeof(buf), + "BGX%d QLM%d LMAC%d mode: 40G-KR4\n", + bgx->bgx_id, lmac->qlm, lmacid); + break; + case QLM_MODE_RGMII: + if (lmacid != 0) + continue; + lmac->lmac_type = 5; + lmac->lane_to_sds = 0xE4; + snprintf(buf, sizeof(buf), + "BGX%d LMAC%d mode: RGMII\n", + bgx->bgx_id, lmacid); + break; + case QLM_MODE_QSGMII: + if (qsgmii_configured) + continue; + if (lmacid == 0 || lmacid == 2) { + count = 4; + printf("BGX%d QLM%d LMAC%d mode: QSGMII\n", + bgx->bgx_id, lmac->qlm, lmacid); + for (i = 0; i < count; i++) { + struct lmac *l; + int type; + + l = &bgx->lmac[i]; + l->lmac_type = 6; + type = l->lmac_type; + l->qlm_mode = QLM_MODE_QSGMII; + l->lane_to_sds = lmacid + i; + if (is_bgx_port_valid(bgx->bgx_id, i)) + bgx_reg_write(bgx, i, + BGX_CMRX_CFG, + (type << 8) | + l->lane_to_sds); + } + qsgmii_configured = 1; + } + continue; + default: + continue; + } + + /* Reset lmac to the unused slot */ + if (is_bgx_port_valid(bgx->bgx_id, count) && + lmac->qlm_mode != QLM_MODE_QSGMII) { + int lmac_en = 0; + int tmp, idx; + + tlmac = &bgx->lmac[count]; + tlmac->lmac_type = lmac->lmac_type; + idx = bgx->bgx_id; + tmp = count + inc; + /* Adjust lane_to_sds based on BGX-ENABLE */ + for (; tmp < MAX_LMAC_PER_BGX; inc++) { + lmac_en = bgx_board_info[idx].lmac_enable[tmp]; + if (lmac_en) + break; + tmp = count + inc; + } + + if (inc != 0 && inc < MAX_LMAC_PER_BGX && + lmac_en && inc != count) + tlmac->lane_to_sds = + lmac->lane_to_sds + abs(inc - count); + else + tlmac->lane_to_sds = lmac->lane_to_sds; + tlmac->qlm = lmac->qlm; + tlmac->qlm_mode = lmac->qlm_mode; + + printf("%s", buf); + /* Initialize lmac_type and lane_to_sds */ + bgx_reg_write(bgx, count, BGX_CMRX_CFG, + (tlmac->lmac_type << 8) | + tlmac->lane_to_sds); + + if (tlmac->lmac_type == BGX_MODE_SGMII) { + if (tlmac->is_1gx) { + /* This is actually 1000BASE-X, so + * mark the LMAC as such. + */ + bgx_reg_modify(bgx, count, + BGX_GMP_PCS_MISCX_CTL, + PCS_MISC_CTL_MODE); + } + + if (!bgx_board_info[bgx->bgx_id].phy_info[lmacid].autoneg_dis) { + /* The Linux DTS does not disable + * autoneg for this LMAC (in SGMII or + * 1000BASE-X mode), so that means + * enable autoneg. + */ + bgx_reg_modify(bgx, count, + BGX_GMP_PCS_MRX_CTL, + PCS_MRX_CTL_AN_EN); + } + } + + count += 1; + } + } + + /* Done probing all 4 lmacs, now clear qsgmii_configured */ + qsgmii_configured = 0; + + printf("BGX%d LMACs: %d\n", bgx->bgx_id, count); + bgx->lmac_count = count; + bgx_reg_write(bgx, 0, BGX_CMR_RX_LMACS, count); + bgx_reg_write(bgx, 0, BGX_CMR_TX_LMACS, count); + + bgx_reg_modify(bgx, 0, BGX_CMR_GLOBAL_CFG, CMR_GLOBAL_CFG_FCS_STRIP); + if (bgx_reg_read(bgx, 0, BGX_CMR_BIST_STATUS)) + printf("BGX%d BIST failed\n", bgx->bgx_id); + + /* Set the backpressure AND mask */ + for (i = 0; i < bgx->lmac_count; i++) + bgx_reg_modify(bgx, 0, BGX_CMR_CHAN_MSK_AND, + ((1ULL << MAX_BGX_CHANS_PER_LMAC) - 1) << + (i * MAX_BGX_CHANS_PER_LMAC)); + + /* Disable all MAC filtering */ + for (i = 0; i < RX_DMAC_COUNT; i++) + bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM + (i * 8), 0x00); + + /* Disable MAC steering (NCSI traffic) */ + for (i = 0; i < RX_TRAFFIC_STEER_RULE_COUNT; i++) + bgx_reg_write(bgx, 0, BGX_CMR_RX_STREERING + (i * 8), 0x00); +} + +static void bgx_get_qlm_mode(struct bgx *bgx) +{ + struct lmac *lmac; + int lmacid; + + /* Read LMACx type to figure out QLM mode + * This is configured by low level firmware + */ + for (lmacid = 0; lmacid < MAX_LMAC_PER_BGX; lmacid++) { + int lmac_type; + int train_en; + int index = 0; + + if (otx_is_soc(CN81XX) || (otx_is_soc(CN83XX) && + bgx->bgx_id == 2)) + index = (lmacid < 2) ? 0 : 2; + + lmac = &bgx->lmac[lmacid]; + + /* check if QLM is programmed, if not, skip */ + if (lmac->qlm == -1) + continue; + + lmac_type = bgx_reg_read(bgx, index, BGX_CMRX_CFG); + lmac->lmac_type = (lmac_type >> 8) & 0x07; + debug("%s:%d:%d: lmac_type = %d, altpkg = %d\n", __func__, + bgx->bgx_id, lmacid, lmac->lmac_type, otx_is_altpkg()); + + train_en = (readq(GSERX_SCRATCH(lmac->qlm))) & 0xf; + lmac->is_1gx = bgx_reg_read(bgx, index, BGX_GMP_PCS_MISCX_CTL) + & (PCS_MISC_CTL_MODE) ? true : false; + + switch (lmac->lmac_type) { + case BGX_MODE_SGMII: + if (bgx->is_rgx) { + if (lmacid == 0) { + lmac->qlm_mode = QLM_MODE_RGMII; + debug("BGX%d LMAC%d mode: RGMII\n", + bgx->bgx_id, lmacid); + } + continue; + } else { + if (bgx->bgx_id == 0 && otx_is_altpkg()) { + if (lmacid % 2) + continue; + } + lmac->qlm_mode = QLM_MODE_SGMII; + debug("BGX%d QLM%d LMAC%d mode: %s\n", + bgx->bgx_id, lmac->qlm, lmacid, + lmac->is_1gx ? "1000Base-X" : "SGMII"); + } + break; + case BGX_MODE_XAUI: + if (bgx->bgx_id == 0 && otx_is_altpkg()) + continue; + lmac->qlm_mode = QLM_MODE_XAUI; + if (lmacid != 0) + continue; + debug("BGX%d QLM%d LMAC%d mode: XAUI\n", + bgx->bgx_id, lmac->qlm, lmacid); + break; + case BGX_MODE_RXAUI: + if (bgx->bgx_id == 0 && otx_is_altpkg()) + continue; + lmac->qlm_mode = QLM_MODE_RXAUI; + if (index == lmacid) { + debug("BGX%d QLM%d LMAC%d mode: RXAUI\n", + bgx->bgx_id, lmac->qlm, (index ? 1 : 0)); + } + break; + case BGX_MODE_XFI: + if (bgx->bgx_id == 0 && otx_is_altpkg()) { + if (lmacid % 2) + continue; + } + if ((lmacid < 2 && (train_en & (1 << lmacid))) || + (train_en & (1 << (lmacid - 2)))) { + lmac->qlm_mode = QLM_MODE_10G_KR; + debug("BGX%d QLM%d LMAC%d mode: 10G_KR\n", + bgx->bgx_id, lmac->qlm, lmacid); + } else { + lmac->qlm_mode = QLM_MODE_XFI; + debug("BGX%d QLM%d LMAC%d mode: XFI\n", + bgx->bgx_id, lmac->qlm, lmacid); + } + break; + case BGX_MODE_XLAUI: + if (bgx->bgx_id == 0 && otx_is_altpkg()) + continue; + if (train_en) { + lmac->qlm_mode = QLM_MODE_40G_KR4; + if (lmacid != 0) + break; + debug("BGX%d QLM%d LMAC%d mode: 40G_KR4\n", + bgx->bgx_id, lmac->qlm, lmacid); + } else { + lmac->qlm_mode = QLM_MODE_XLAUI; + if (lmacid != 0) + break; + debug("BGX%d QLM%d LMAC%d mode: XLAUI\n", + bgx->bgx_id, lmac->qlm, lmacid); + } + break; + case BGX_MODE_QSGMII: + /* If QLM is configured as QSGMII, use lmac0 */ + if (otx_is_soc(CN83XX) && lmacid == 2 && + bgx->bgx_id != 2) { + //lmac->qlm_mode = QLM_MODE_DISABLED; + continue; + } + + if (lmacid == 0 || lmacid == 2) { + lmac->qlm_mode = QLM_MODE_QSGMII; + debug("BGX%d QLM%d LMAC%d mode: QSGMII\n", + bgx->bgx_id, lmac->qlm, lmacid); + } + break; + default: + break; + } + } +} + +void bgx_set_board_info(int bgx_id, int *mdio_bus, + int *phy_addr, bool *autoneg_dis, bool *lmac_reg, + bool *lmac_enable) +{ + unsigned int i; + + for (i = 0; i < MAX_LMAC_PER_BGX; i++) { + bgx_board_info[bgx_id].phy_info[i].phy_addr = phy_addr[i]; + bgx_board_info[bgx_id].phy_info[i].mdio_bus = mdio_bus[i]; + bgx_board_info[bgx_id].phy_info[i].autoneg_dis = autoneg_dis[i]; + bgx_board_info[bgx_id].lmac_reg[i] = lmac_reg[i]; + bgx_board_info[bgx_id].lmac_enable[i] = lmac_enable[i]; + debug("%s bgx_id %d lmac %d\n", __func__, bgx_id, i); + debug("phy addr %x mdio bus %d autoneg_dis %d lmac_reg %d\n", + bgx_board_info[bgx_id].phy_info[i].phy_addr, + bgx_board_info[bgx_id].phy_info[i].mdio_bus, + bgx_board_info[bgx_id].phy_info[i].autoneg_dis, + bgx_board_info[bgx_id].lmac_reg[i]); + debug("lmac_enable = %x\n", + bgx_board_info[bgx_id].lmac_enable[i]); + } +} + +int octeontx_bgx_remove(struct udevice *dev) +{ + int lmacid; + u64 cfg; + int count = MAX_LMAC_PER_BGX; + struct bgx *bgx = dev_get_priv(dev); + + if (!bgx->reg_base) + return 0; + + if (bgx->is_rgx) + count = 1; + + for (lmacid = 0; lmacid < count; lmacid++) { + struct lmac *lmac; + + lmac = &bgx->lmac[lmacid]; + cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG); + cfg &= ~(CMR_PKT_RX_EN | CMR_PKT_TX_EN); + bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg); + + /* Disable PCS for 1G interface */ + if (lmac->lmac_type == BGX_MODE_SGMII || + lmac->lmac_type == BGX_MODE_QSGMII) { + cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_PCS_MRX_CTL); + cfg |= PCS_MRX_CTL_PWR_DN; + bgx_reg_write(bgx, lmacid, BGX_GMP_PCS_MRX_CTL, cfg); + } + + debug("%s disabling bgx%d lmacid%d\n", __func__, bgx->bgx_id, + lmacid); + bgx_lmac_disable(bgx, lmacid); + } + return 0; +} + +int octeontx_bgx_probe(struct udevice *dev) +{ + struct bgx *bgx = dev_get_priv(dev); + u8 lmac = 0; + int qlm[4] = {-1, -1, -1, -1}; + int bgx_idx, node; + int inc = 1; + + bgx->reg_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, + PCI_REGION_MEM); + if (!bgx->reg_base) { + debug("No PCI region found\n"); + return 0; + } + +#ifdef OCTEONTX_XCV + /* Use FAKE BGX2 for RGX interface */ + if ((((uintptr_t)bgx->reg_base >> 24) & 0xf) == 0x8) { + bgx->bgx_id = 2; + bgx->is_rgx = true; + for (lmac = 0; lmac < MAX_LMAC_PER_BGX; lmac++) { + if (lmac == 0) { + bgx->lmac[lmac].lmacid = 0; + bgx->lmac[lmac].qlm = 0; + } else { + bgx->lmac[lmac].qlm = -1; + } + } + xcv_init_hw(); + goto skip_qlm_config; + } +#endif + + node = node_id(bgx->reg_base); + bgx_idx = ((uintptr_t)bgx->reg_base >> 24) & 3; + bgx->bgx_id = (node * MAX_BGX_PER_NODE) + bgx_idx; + if (otx_is_soc(CN81XX)) + inc = 2; + else if (otx_is_soc(CN83XX) && (bgx_idx == 2)) + inc = 2; + + for (lmac = 0; lmac < MAX_LMAC_PER_BGX; lmac += inc) { + /* BGX3 (DLM4), has only 2 lanes */ + if (otx_is_soc(CN83XX) && bgx_idx == 3 && lmac >= 2) + continue; + qlm[lmac + 0] = get_qlm_for_bgx(node, bgx_idx, lmac); + /* Each DLM has 2 lanes, configure both lanes with + * same qlm configuration + */ + if (inc == 2) + qlm[lmac + 1] = qlm[lmac]; + debug("qlm[%d] = %d\n", lmac, qlm[lmac]); + } + + /* A BGX can take 1 or 2 DLMs, if both the DLMs are not configured + * as BGX, then return, nothing to initialize + */ + if (otx_is_soc(CN81XX)) + if ((qlm[0] == -1) && (qlm[2] == -1)) + return -ENODEV; + + /* MAP configuration registers */ + for (lmac = 0; lmac < MAX_LMAC_PER_BGX; lmac++) { + bgx->lmac[lmac].qlm = qlm[lmac]; + bgx->lmac[lmac].lmacid = lmac; + } + +#ifdef OCTEONTX_XCV +skip_qlm_config: +#endif + bgx_vnic[bgx->bgx_id] = bgx; + bgx_get_qlm_mode(bgx); + debug("bgx_vnic[%u]: %p\n", bgx->bgx_id, bgx); + + bgx_init_hw(bgx); + + /* Init LMACs */ + for (lmac = 0; lmac < bgx->lmac_count; lmac++) { + struct lmac *tlmac = &bgx->lmac[lmac]; + + tlmac->dev = dev; + tlmac->init_pend = 1; + tlmac->bgx = bgx; + } + + return 0; +} + +U_BOOT_DRIVER(octeontx_bgx) = { + .name = "octeontx_bgx", + .id = UCLASS_MISC, + .probe = octeontx_bgx_probe, + .remove = octeontx_bgx_remove, + .priv_auto_alloc_size = sizeof(struct bgx), + .flags = DM_FLAG_OS_PREPARE, +}; + +static struct pci_device_id octeontx_bgx_supported[] = { + { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_BGX) }, + { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_RGX) }, + {} +}; + +U_BOOT_PCI_DEVICE(octeontx_bgx, octeontx_bgx_supported); diff --git a/drivers/net/octeontx/bgx.h b/drivers/net/octeontx/bgx.h new file mode 100644 index 0000000000..8402630bf5 --- /dev/null +++ b/drivers/net/octeontx/bgx.h @@ -0,0 +1,259 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Marvell International Ltd. + */ + +#ifndef BGX_H +#define BGX_H + +#include + +/* PCI device IDs */ +#define PCI_DEVICE_ID_OCTEONTX_BGX 0xA026 +#define PCI_DEVICE_ID_OCTEONTX_RGX 0xA054 + +#define MAX_LMAC_PER_BGX 4 +#define MAX_BGX_CHANS_PER_LMAC 16 +#define MAX_DMAC_PER_LMAC 8 +#define MAX_FRAME_SIZE 9216 + +#define MAX_DMAC_PER_LMAC_TNS_BYPASS_MODE 2 + +#define MAX_LMAC (MAX_BGX_PER_NODE * MAX_LMAC_PER_BGX) + +#define NODE_ID_MASK 0x300000000000 +#define NODE_ID(x) (((x) & NODE_ID_MASK) >> 44) + +/* Registers */ +#define GSERX_CFG(x) (0x87E090000080ull + (x) * 0x1000000ull) +#define GSERX_SCRATCH(x) (0x87E090000020ull + (x) * 0x1000000ull) +#define GSERX_PHY_CTL(x) (0x87E090000000ull + (x) * 0x1000000ull) +#define GSERX_CFG_BGX BIT(2) +#define GSER_RX_EIE_DETSTS(x) (0x87E090000150ull + (x) * 0x1000000ull) +#define GSER_CDRLOCK (8) +#define GSER_BR_RXX_CTL(x, y) (0x87E090000400ull + (x) * 0x1000000ull + \ + (y) * 0x80) +#define GSER_BR_RXX_CTL_RXT_SWM BIT(2) +#define GSER_BR_RXX_EER(x, y) (0x87E090000418ull + (x) * 0x1000000ull + \ + (y) * 0x80) +#define GSER_BR_RXX_EER_RXT_ESV BIT(14) +#define GSER_BR_RXX_EER_RXT_EER BIT(15) +#define EER_RXT_ESV (14) + +#define BGX_CMRX_CFG 0x00 +#define CMR_PKT_TX_EN BIT_ULL(13) +#define CMR_PKT_RX_EN BIT_ULL(14) +#define CMR_EN BIT_ULL(15) +#define BGX_CMR_GLOBAL_CFG 0x08 +#define CMR_GLOBAL_CFG_FCS_STRIP BIT_ULL(6) +#define BGX_CMRX_RX_ID_MAP 0x60 +#define BGX_CMRX_RX_STAT0 0x70 +#define BGX_CMRX_RX_STAT1 0x78 +#define BGX_CMRX_RX_STAT2 0x80 +#define BGX_CMRX_RX_STAT3 0x88 +#define BGX_CMRX_RX_STAT4 0x90 +#define BGX_CMRX_RX_STAT5 0x98 +#define BGX_CMRX_RX_STAT6 0xA0 +#define BGX_CMRX_RX_STAT7 0xA8 +#define BGX_CMRX_RX_STAT8 0xB0 +#define BGX_CMRX_RX_STAT9 0xB8 +#define BGX_CMRX_RX_STAT10 0xC0 +#define BGX_CMRX_RX_BP_DROP 0xC8 +#define BGX_CMRX_RX_DMAC_CTL 0x0E8 +#define BGX_CMR_RX_DMACX_CAM 0x200 +#define RX_DMACX_CAM_EN BIT_ULL(48) +#define RX_DMACX_CAM_LMACID(x) ((x) << 49) +#define RX_DMAC_COUNT 32 +#define BGX_CMR_RX_STREERING 0x300 +#define RX_TRAFFIC_STEER_RULE_COUNT 8 +#define BGX_CMR_CHAN_MSK_AND 0x450 +#define BGX_CMR_BIST_STATUS 0x460 +#define BGX_CMR_RX_LMACS 0x468 +#define BGX_CMRX_TX_STAT0 0x600 +#define BGX_CMRX_TX_STAT1 0x608 +#define BGX_CMRX_TX_STAT2 0x610 +#define BGX_CMRX_TX_STAT3 0x618 +#define BGX_CMRX_TX_STAT4 0x620 +#define BGX_CMRX_TX_STAT5 0x628 +#define BGX_CMRX_TX_STAT6 0x630 +#define BGX_CMRX_TX_STAT7 0x638 +#define BGX_CMRX_TX_STAT8 0x640 +#define BGX_CMRX_TX_STAT9 0x648 +#define BGX_CMRX_TX_STAT10 0x650 +#define BGX_CMRX_TX_STAT11 0x658 +#define BGX_CMRX_TX_STAT12 0x660 +#define BGX_CMRX_TX_STAT13 0x668 +#define BGX_CMRX_TX_STAT14 0x670 +#define BGX_CMRX_TX_STAT15 0x678 +#define BGX_CMRX_TX_STAT16 0x680 +#define BGX_CMRX_TX_STAT17 0x688 +#define BGX_CMR_TX_LMACS 0x1000 + +#define BGX_SPUX_CONTROL1 0x10000 +#define SPU_CTL_LOW_POWER BIT_ULL(11) +#define SPU_CTL_LOOPBACK BIT_ULL(14) +#define SPU_CTL_RESET BIT_ULL(15) +#define BGX_SPUX_STATUS1 0x10008 +#define SPU_STATUS1_RCV_LNK BIT_ULL(2) +#define BGX_SPUX_STATUS2 0x10020 +#define SPU_STATUS2_RCVFLT BIT_ULL(10) +#define BGX_SPUX_BX_STATUS 0x10028 +#define SPU_BX_STATUS_RX_ALIGN BIT_ULL(12) +#define BGX_SPUX_BR_STATUS1 0x10030 +#define SPU_BR_STATUS_BLK_LOCK BIT_ULL(0) +#define SPU_BR_STATUS_RCV_LNK BIT_ULL(12) +#define BGX_SPUX_BR_PMD_CRTL 0x10068 +#define SPU_PMD_CRTL_TRAIN_EN BIT_ULL(1) +#define BGX_SPUX_BR_PMD_LP_CUP 0x10078 +#define BGX_SPUX_BR_PMD_LD_CUP 0x10088 +#define BGX_SPUX_BR_PMD_LD_REP 0x10090 +#define BGX_SPUX_FEC_CONTROL 0x100A0 +#define SPU_FEC_CTL_FEC_EN BIT_ULL(0) +#define SPU_FEC_CTL_ERR_EN BIT_ULL(1) +#define BGX_SPUX_AN_CONTROL 0x100C8 +#define SPU_AN_CTL_AN_EN BIT_ULL(12) +#define SPU_AN_CTL_XNP_EN BIT_ULL(13) +#define SPU_AN_CTL_AN_RESTART BIT_ULL(15) +#define BGX_SPUX_AN_STATUS 0x100D0 +#define SPU_AN_STS_AN_COMPLETE BIT_ULL(5) +#define BGX_SPUX_AN_ADV 0x100D8 +#define BGX_SPUX_MISC_CONTROL 0x10218 +#define SPU_MISC_CTL_INTLV_RDISP BIT_ULL(10) +#define SPU_MISC_CTL_RX_DIS BIT_ULL(12) +#define BGX_SPUX_INT 0x10220 /* +(0..3) << 20 */ +#define BGX_SPUX_INT_W1S 0x10228 +#define BGX_SPUX_INT_ENA_W1C 0x10230 +#define BGX_SPUX_INT_ENA_W1S 0x10238 +#define BGX_SPU_DBG_CONTROL 0x10300 +#define SPU_DBG_CTL_AN_ARB_LINK_CHK_EN BIT_ULL(18) +#define SPU_DBG_CTL_AN_NONCE_MCT_DIS BIT_ULL(29) + +#define BGX_SMUX_RX_INT 0x20000 +#define BGX_SMUX_RX_JABBER 0x20030 +#define BGX_SMUX_RX_CTL 0x20048 +#define SMU_RX_CTL_STATUS (3ull << 0) +#define BGX_SMUX_TX_APPEND 0x20100 +#define SMU_TX_APPEND_FCS_D BIT_ULL(2) +#define BGX_SMUX_TX_MIN_PKT 0x20118 +#define BGX_SMUX_TX_INT 0x20140 +#define BGX_SMUX_TX_CTL 0x20178 +#define SMU_TX_CTL_DIC_EN BIT_ULL(0) +#define SMU_TX_CTL_UNI_EN BIT_ULL(1) +#define SMU_TX_CTL_LNK_STATUS (3ull << 4) +#define BGX_SMUX_TX_THRESH 0x20180 +#define BGX_SMUX_CTL 0x20200 +#define SMU_CTL_RX_IDLE BIT_ULL(0) +#define SMU_CTL_TX_IDLE BIT_ULL(1) + +#define BGX_GMP_PCS_MRX_CTL 0x30000 +#define PCS_MRX_CTL_RST_AN BIT_ULL(9) +#define PCS_MRX_CTL_PWR_DN BIT_ULL(11) +#define PCS_MRX_CTL_AN_EN BIT_ULL(12) +#define PCS_MRX_CTL_LOOPBACK1 BIT_ULL(14) +#define PCS_MRX_CTL_RESET BIT_ULL(15) +#define BGX_GMP_PCS_MRX_STATUS 0x30008 +#define PCS_MRX_STATUS_AN_CPT BIT_ULL(5) +#define BGX_GMP_PCS_ANX_AN_RESULTS 0x30020 +#define BGX_GMP_PCS_SGM_AN_ADV 0x30068 +#define BGX_GMP_PCS_MISCX_CTL 0x30078 +#define PCS_MISCX_CTL_DISP_EN BIT_ULL(13) +#define PCS_MISC_CTL_GMX_ENO BIT_ULL(11) +#define PCS_MISC_CTL_SAMP_PT_MASK 0x7Full +#define PCS_MISC_CTL_MODE BIT_ULL(8) +#define BGX_GMP_GMI_PRTX_CFG 0x38020 +#define GMI_PORT_CFG_SPEED BIT_ULL(1) +#define GMI_PORT_CFG_DUPLEX BIT_ULL(2) +#define GMI_PORT_CFG_SLOT_TIME BIT_ULL(3) +#define GMI_PORT_CFG_SPEED_MSB BIT_ULL(8) +#define BGX_GMP_GMI_RXX_JABBER 0x38038 +#define BGX_GMP_GMI_TXX_THRESH 0x38210 +#define BGX_GMP_GMI_TXX_APPEND 0x38218 +#define BGX_GMP_GMI_TXX_SLOT 0x38220 +#define BGX_GMP_GMI_TXX_BURST 0x38228 +#define BGX_GMP_GMI_TXX_MIN_PKT 0x38240 +#define BGX_GMP_GMI_TXX_SGMII_CTL 0x38300 + +#define BGX_MSIX_VEC_0_29_ADDR 0x400000 /* +(0..29) << 4 */ +#define BGX_MSIX_VEC_0_29_CTL 0x400008 +#define BGX_MSIX_PBA_0 0x4F0000 + +/* MSI-X interrupts */ +#define BGX_MSIX_VECTORS 30 +#define BGX_LMAC_VEC_OFFSET 7 +#define BGX_MSIX_VEC_SHIFT 4 + +#define CMRX_INT 0 +#define SPUX_INT 1 +#define SMUX_RX_INT 2 +#define SMUX_TX_INT 3 +#define GMPX_PCS_INT 4 +#define GMPX_GMI_RX_INT 5 +#define GMPX_GMI_TX_INT 6 +#define CMR_MEM_INT 28 +#define SPU_MEM_INT 29 + +#define LMAC_INTR_LINK_UP BIT(0) +#define LMAC_INTR_LINK_DOWN BIT(1) + +/* RX_DMAC_CTL configuration*/ +enum MCAST_MODE { + MCAST_MODE_REJECT, + MCAST_MODE_ACCEPT, + MCAST_MODE_CAM_FILTER, + RSVD +}; + +#define BCAST_ACCEPT 1 +#define CAM_ACCEPT 1 + +int octeontx_bgx_initialize(unsigned int bgx_idx, unsigned int node); +void bgx_add_dmac_addr(u64 dmac, int node, int bgx_idx, int lmac); +void bgx_get_count(int node, int *bgx_count); +int bgx_get_lmac_count(int node, int bgx); +void bgx_print_stats(int bgx_idx, int lmac); +void xcv_init_hw(void); +void xcv_setup_link(bool link_up, int link_speed); + +#undef LINK_INTR_ENABLE + +enum qlm_mode { + QLM_MODE_SGMII, /* SGMII, each lane independent */ + QLM_MODE_XAUI, /* 1 XAUI or DXAUI, 4 lanes */ + QLM_MODE_RXAUI, /* 2 RXAUI, 2 lanes each */ + QLM_MODE_XFI, /* 4 XFI, 1 lane each */ + QLM_MODE_XLAUI, /* 1 XLAUI, 4 lanes each */ + QLM_MODE_10G_KR, /* 4 10GBASE-KR, 1 lane each */ + QLM_MODE_40G_KR4, /* 1 40GBASE-KR4, 4 lanes each */ + QLM_MODE_QSGMII, /* 4 QSGMII, each lane independent */ + QLM_MODE_RGMII, /* 1 RGX */ +}; + +struct phy_info { + int mdio_bus; + int phy_addr; + bool autoneg_dis; +}; + +struct bgx_board_info { + struct phy_info phy_info[MAX_LMAC_PER_BGX]; + bool lmac_reg[MAX_LMAC_PER_BGX]; + bool lmac_enable[MAX_LMAC_PER_BGX]; +}; + +enum LMAC_TYPE { + BGX_MODE_SGMII = 0, /* 1 lane, 1.250 Gbaud */ + BGX_MODE_XAUI = 1, /* 4 lanes, 3.125 Gbaud */ + BGX_MODE_DXAUI = 1, /* 4 lanes, 6.250 Gbaud */ + BGX_MODE_RXAUI = 2, /* 2 lanes, 6.250 Gbaud */ + BGX_MODE_XFI = 3, /* 1 lane, 10.3125 Gbaud */ + BGX_MODE_XLAUI = 4, /* 4 lanes, 10.3125 Gbaud */ + BGX_MODE_10G_KR = 3,/* 1 lane, 10.3125 Gbaud */ + BGX_MODE_40G_KR = 4,/* 4 lanes, 10.3125 Gbaud */ + BGX_MODE_RGMII = 5, + BGX_MODE_QSGMII = 6, + BGX_MODE_INVALID = 7, +}; + +int rxaui_phy_xs_init(struct mii_dev *bus, int phy_addr); + +#endif /* BGX_H */ diff --git a/drivers/net/octeontx/nic.h b/drivers/net/octeontx/nic.h new file mode 100644 index 0000000000..af3576cfbb --- /dev/null +++ b/drivers/net/octeontx/nic.h @@ -0,0 +1,508 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Marvell International Ltd. + */ + +#ifndef NIC_H +#define NIC_H + +#include +#include "bgx.h" + +#define PCI_DEVICE_ID_CAVIUM_NICVF_1 0x0011 + +/* Subsystem device IDs */ +#define PCI_SUBSYS_DEVID_88XX_NIC_PF 0xA11E +#define PCI_SUBSYS_DEVID_81XX_NIC_PF 0xA21E +#define PCI_SUBSYS_DEVID_83XX_NIC_PF 0xA31E + +#define PCI_SUBSYS_DEVID_88XX_PASS1_NIC_VF 0xA11E +#define PCI_SUBSYS_DEVID_88XX_NIC_VF 0xA134 +#define PCI_SUBSYS_DEVID_81XX_NIC_VF 0xA234 +#define PCI_SUBSYS_DEVID_83XX_NIC_VF 0xA334 + +#define NIC_INTF_COUNT 2 /* Interfaces btw VNIC and TNS/BGX */ +#define NIC_CHANS_PER_INF 128 +#define NIC_MAX_CHANS (NIC_INTF_COUNT * NIC_CHANS_PER_INF) + +/* PCI BAR nos */ +#define PCI_CFG_REG_BAR_NUM 0 +#define PCI_MSIX_REG_BAR_NUM 4 + +/* NIC SRIOV VF count */ +#define MAX_NUM_VFS_SUPPORTED 128 +#define DEFAULT_NUM_VF_ENABLED 8 + +#define NIC_TNS_BYPASS_MODE 0 +#define NIC_TNS_MODE 1 + +/* NIC priv flags */ +#define NIC_SRIOV_ENABLED BIT(0) +#define NIC_TNS_ENABLED BIT(1) + +/* VNIC HW optimiation features */ +#define VNIC_RX_CSUM_OFFLOAD_SUPPORT +#undef VNIC_TX_CSUM_OFFLOAD_SUPPORT +#undef VNIC_SG_SUPPORT +#undef VNIC_TSO_SUPPORT +#undef VNIC_LRO_SUPPORT +#undef VNIC_RSS_SUPPORT + +/* TSO not supported in Thunder pass1 */ +#ifdef VNIC_TSO_SUPPORT +#define VNIC_SW_TSO_SUPPORT +#undef VNIC_HW_TSO_SUPPORT +#endif + +/* ETHTOOL enable or disable, undef this to disable */ +#define NICVF_ETHTOOL_ENABLE + +/* Min/Max packet size */ +#define NIC_HW_MIN_FRS 64 +#define NIC_HW_MAX_FRS 9200 /* 9216 max packet including FCS */ + +/* Max pkinds */ +#define NIC_MAX_PKIND 16 + +/* Max when CPI_ALG is IP diffserv */ +#define NIC_MAX_CPI_PER_LMAC 64 + +/* NIC VF Interrupts */ +#define NICVF_INTR_CQ 0 +#define NICVF_INTR_SQ 1 +#define NICVF_INTR_RBDR 2 +#define NICVF_INTR_PKT_DROP 3 +#define NICVF_INTR_TCP_TIMER 4 +#define NICVF_INTR_MBOX 5 +#define NICVF_INTR_QS_ERR 6 + +#define NICVF_INTR_CQ_SHIFT 0 +#define NICVF_INTR_SQ_SHIFT 8 +#define NICVF_INTR_RBDR_SHIFT 16 +#define NICVF_INTR_PKT_DROP_SHIFT 20 +#define NICVF_INTR_TCP_TIMER_SHIFT 21 +#define NICVF_INTR_MBOX_SHIFT 22 +#define NICVF_INTR_QS_ERR_SHIFT 23 + +#define NICVF_INTR_CQ_MASK (0xFF << NICVF_INTR_CQ_SHIFT) +#define NICVF_INTR_SQ_MASK (0xFF << NICVF_INTR_SQ_SHIFT) +#define NICVF_INTR_RBDR_MASK (0x03 << NICVF_INTR_RBDR_SHIFT) +#define NICVF_INTR_PKT_DROP_MASK BIT(NICVF_INTR_PKT_DROP_SHIFT) +#define NICVF_INTR_TCP_TIMER_MASK BIT(NICVF_INTR_TCP_TIMER_SHIFT) +#define NICVF_INTR_MBOX_MASK BIT(NICVF_INTR_MBOX_SHIFT) +#define NICVF_INTR_QS_ERR_MASK BIT(NICVF_INTR_QS_ERR_SHIFT) + +/* MSI-X interrupts */ +#define NIC_PF_MSIX_VECTORS 10 +#define NIC_VF_MSIX_VECTORS 20 + +#define NIC_PF_INTR_ID_ECC0_SBE 0 +#define NIC_PF_INTR_ID_ECC0_DBE 1 +#define NIC_PF_INTR_ID_ECC1_SBE 2 +#define NIC_PF_INTR_ID_ECC1_DBE 3 +#define NIC_PF_INTR_ID_ECC2_SBE 4 +#define NIC_PF_INTR_ID_ECC2_DBE 5 +#define NIC_PF_INTR_ID_ECC3_SBE 6 +#define NIC_PF_INTR_ID_ECC3_DBE 7 +#define NIC_PF_INTR_ID_MBOX0 8 +#define NIC_PF_INTR_ID_MBOX1 9 + +/* Global timer for CQ timer thresh interrupts + * Calculated for SCLK of 700Mhz + * value written should be a 1/16thof what is expected + * + * 1 tick per ms + */ +#define NICPF_CLK_PER_INT_TICK 43750 + +struct nicvf_cq_poll { + u8 cq_idx; /* Completion queue index */ +}; + +#define NIC_MAX_RSS_HASH_BITS 8 +#define NIC_MAX_RSS_IDR_TBL_SIZE BIT(NIC_MAX_RSS_HASH_BITS) +#define RSS_HASH_KEY_SIZE 5 /* 320 bit key */ + +#ifdef VNIC_RSS_SUPPORT +struct nicvf_rss_info { + bool enable; +#define RSS_L2_EXTENDED_HASH_ENA BIT(0) +#define RSS_IP_HASH_ENA BIT(1) +#define RSS_TCP_HASH_ENA BIT(2) +#define RSS_TCP_SYN_DIS BIT(3) +#define RSS_UDP_HASH_ENA BIT(4) +#define RSS_L4_EXTENDED_HASH_ENA BIT(5) +#define RSS_ROCE_ENA BIT(6) +#define RSS_L3_BI_DIRECTION_ENA BIT(7) +#define RSS_L4_BI_DIRECTION_ENA BIT(8) + u64 cfg; + u8 hash_bits; + u16 rss_size; + u8 ind_tbl[NIC_MAX_RSS_IDR_TBL_SIZE]; + u64 key[RSS_HASH_KEY_SIZE]; +}; +#endif + +enum rx_stats_reg_offset { + RX_OCTS = 0x0, + RX_UCAST = 0x1, + RX_BCAST = 0x2, + RX_MCAST = 0x3, + RX_RED = 0x4, + RX_RED_OCTS = 0x5, + RX_ORUN = 0x6, + RX_ORUN_OCTS = 0x7, + RX_FCS = 0x8, + RX_L2ERR = 0x9, + RX_DRP_BCAST = 0xa, + RX_DRP_MCAST = 0xb, + RX_DRP_L3BCAST = 0xc, + RX_DRP_L3MCAST = 0xd, + RX_STATS_ENUM_LAST, +}; + +enum tx_stats_reg_offset { + TX_OCTS = 0x0, + TX_UCAST = 0x1, + TX_BCAST = 0x2, + TX_MCAST = 0x3, + TX_DROP = 0x4, + TX_STATS_ENUM_LAST, +}; + +struct nicvf_hw_stats { + u64 rx_bytes_ok; + u64 rx_ucast_frames_ok; + u64 rx_bcast_frames_ok; + u64 rx_mcast_frames_ok; + u64 rx_fcs_errors; + u64 rx_l2_errors; + u64 rx_drop_red; + u64 rx_drop_red_bytes; + u64 rx_drop_overrun; + u64 rx_drop_overrun_bytes; + u64 rx_drop_bcast; + u64 rx_drop_mcast; + u64 rx_drop_l3_bcast; + u64 rx_drop_l3_mcast; + u64 tx_bytes_ok; + u64 tx_ucast_frames_ok; + u64 tx_bcast_frames_ok; + u64 tx_mcast_frames_ok; + u64 tx_drops; +}; + +struct nicvf_drv_stats { + /* Rx */ + u64 rx_frames_ok; + u64 rx_frames_64; + u64 rx_frames_127; + u64 rx_frames_255; + u64 rx_frames_511; + u64 rx_frames_1023; + u64 rx_frames_1518; + u64 rx_frames_jumbo; + u64 rx_drops; + /* Tx */ + u64 tx_frames_ok; + u64 tx_drops; + u64 tx_busy; + u64 tx_tso; +}; + +struct hw_info { + u8 bgx_cnt; + u8 chans_per_lmac; + u8 chans_per_bgx; /* Rx/Tx chans */ + u8 chans_per_rgx; + u8 chans_per_lbk; + u16 cpi_cnt; + u16 rssi_cnt; + u16 rss_ind_tbl_size; + u16 tl4_cnt; + u16 tl3_cnt; + u8 tl2_cnt; + u8 tl1_cnt; + bool tl1_per_bgx; /* TL1 per BGX or per LMAC */ + u8 model_id; +}; + +struct nicvf { + struct udevice *dev; + u8 vf_id; + bool sqs_mode:1; + bool loopback_supported:1; + u8 tns_mode; + u8 node; + u16 mtu; + struct queue_set *qs; +#define MAX_SQS_PER_VF_SINGLE_NODE 5 +#define MAX_SQS_PER_VF 11 + u8 num_qs; + void *addnl_qs; + u16 vf_mtu; + void __iomem *reg_base; +#define MAX_QUEUES_PER_QSET 8 + struct nicvf_cq_poll *napi[8]; + + u8 cpi_alg; + + struct nicvf_hw_stats stats; + struct nicvf_drv_stats drv_stats; + + struct nicpf *nicpf; + + /* VF <-> PF mailbox communication */ + bool pf_acked; + bool pf_nacked; + bool set_mac_pending; + + bool link_up; + u8 duplex; + u32 speed; + u8 rev_id; + u8 rx_queues; + u8 tx_queues; + + bool open; + bool rb_alloc_fail; + void *rcv_buf; + bool hw_tso; +}; + +static inline int node_id(void *addr) +{ + return ((uintptr_t)addr >> 44) & 0x3; +} + +struct nicpf { + struct udevice *udev; + struct hw_info *hw; + u8 node; + unsigned int flags; + u16 total_vf_cnt; /* Total num of VF supported */ + u16 num_vf_en; /* No of VF enabled */ + void __iomem *reg_base; /* Register start address */ + u16 rss_ind_tbl_size; + u8 num_sqs_en; /* Secondary qsets enabled */ + u64 nicvf[MAX_NUM_VFS_SUPPORTED]; + u8 vf_sqs[MAX_NUM_VFS_SUPPORTED][MAX_SQS_PER_VF]; + u8 pqs_vf[MAX_NUM_VFS_SUPPORTED]; + bool sqs_used[MAX_NUM_VFS_SUPPORTED]; + struct pkind_cfg pkind; + u8 bgx_cnt; + u8 rev_id; +#define NIC_SET_VF_LMAC_MAP(bgx, lmac) ((((bgx) & 0xF) << 4) | ((lmac) & 0xF)) +#define NIC_GET_BGX_FROM_VF_LMAC_MAP(map) (((map) >> 4) & 0xF) +#define NIC_GET_LMAC_FROM_VF_LMAC_MAP(map) ((map) & 0xF) + u8 vf_lmac_map[MAX_LMAC]; + u16 cpi_base[MAX_NUM_VFS_SUPPORTED]; + u64 mac[MAX_NUM_VFS_SUPPORTED]; + bool mbx_lock[MAX_NUM_VFS_SUPPORTED]; + u8 link[MAX_LMAC]; + u8 duplex[MAX_LMAC]; + u32 speed[MAX_LMAC]; + bool vf_enabled[MAX_NUM_VFS_SUPPORTED]; + u16 rssi_base[MAX_NUM_VFS_SUPPORTED]; + u8 lmac_cnt; +}; + +/* PF <--> VF Mailbox communication + * Eight 64bit registers are shared between PF and VF. + * Separate set for each VF. + * Writing '1' into last register mbx7 means end of message. + */ + +/* PF <--> VF mailbox communication */ +#define NIC_PF_VF_MAILBOX_SIZE 2 +#define NIC_PF_VF_MBX_TIMEOUT 2000 /* ms */ + +/* Mailbox message types */ +#define NIC_MBOX_MSG_READY 0x01 /* Is PF ready to rcv msgs */ +#define NIC_MBOX_MSG_ACK 0x02 /* ACK the message received */ +#define NIC_MBOX_MSG_NACK 0x03 /* NACK the message received */ +#define NIC_MBOX_MSG_QS_CFG 0x04 /* Configure Qset */ +#define NIC_MBOX_MSG_RQ_CFG 0x05 /* Configure receive queue */ +#define NIC_MBOX_MSG_SQ_CFG 0x06 /* Configure Send queue */ +#define NIC_MBOX_MSG_RQ_DROP_CFG 0x07 /* Configure receive queue */ +#define NIC_MBOX_MSG_SET_MAC 0x08 /* Add MAC ID to DMAC filter */ +#define NIC_MBOX_MSG_SET_MAX_FRS 0x09 /* Set max frame size */ +#define NIC_MBOX_MSG_CPI_CFG 0x0A /* Config CPI, RSSI */ +#define NIC_MBOX_MSG_RSS_SIZE 0x0B /* Get RSS indir_tbl size */ +#define NIC_MBOX_MSG_RSS_CFG 0x0C /* Config RSS table */ +#define NIC_MBOX_MSG_RSS_CFG_CONT 0x0D /* RSS config continuation */ +#define NIC_MBOX_MSG_RQ_BP_CFG 0x0E /* RQ backpressure config */ +#define NIC_MBOX_MSG_RQ_SW_SYNC 0x0F /* Flush inflight pkts to RQ */ +#define NIC_MBOX_MSG_BGX_STATS 0x10 /* Get stats from BGX */ +#define NIC_MBOX_MSG_BGX_LINK_CHANGE 0x11 /* BGX:LMAC link status */ +#define NIC_MBOX_MSG_ALLOC_SQS 0x12 /* Allocate secondary Qset */ +#define NIC_MBOX_MSG_NICVF_PTR 0x13 /* Send nicvf ptr to PF */ +#define NIC_MBOX_MSG_PNICVF_PTR 0x14 /* Get primary qset nicvf ptr */ +#define NIC_MBOX_MSG_SNICVF_PTR 0x15 /* Send sqet nicvf ptr to PVF */ +#define NIC_MBOX_MSG_LOOPBACK 0x16 /* Set interface in loopback */ +#define NIC_MBOX_MSG_CFG_DONE 0xF0 /* VF configuration done */ +#define NIC_MBOX_MSG_SHUTDOWN 0xF1 /* VF is being shutdown */ + +struct nic_cfg_msg { + u8 msg; + u8 vf_id; + u8 node_id; + bool tns_mode:1; + bool sqs_mode:1; + bool loopback_supported:1; + u8 mac_addr[6]; +}; + +/* Qset configuration */ +struct qs_cfg_msg { + u8 msg; + u8 num; + u8 sqs_count; + u64 cfg; +}; + +/* Receive queue configuration */ +struct rq_cfg_msg { + u8 msg; + u8 qs_num; + u8 rq_num; + u64 cfg; +}; + +/* Send queue configuration */ +struct sq_cfg_msg { + u8 msg; + u8 qs_num; + u8 sq_num; + bool sqs_mode; + u64 cfg; +}; + +/* Set VF's MAC address */ +struct set_mac_msg { + u8 msg; + u8 vf_id; + u8 mac_addr[6]; +}; + +/* Set Maximum frame size */ +struct set_frs_msg { + u8 msg; + u8 vf_id; + u16 max_frs; +}; + +/* Set CPI algorithm type */ +struct cpi_cfg_msg { + u8 msg; + u8 vf_id; + u8 rq_cnt; + u8 cpi_alg; +}; + +/* Get RSS table size */ +struct rss_sz_msg { + u8 msg; + u8 vf_id; + u16 ind_tbl_size; +}; + +/* Set RSS configuration */ +struct rss_cfg_msg { + u8 msg; + u8 vf_id; + u8 hash_bits; + u8 tbl_len; + u8 tbl_offset; +#define RSS_IND_TBL_LEN_PER_MBX_MSG 8 + u8 ind_tbl[RSS_IND_TBL_LEN_PER_MBX_MSG]; +}; + +struct bgx_stats_msg { + u8 msg; + u8 vf_id; + u8 rx; + u8 idx; + u64 stats; +}; + +/* Physical interface link status */ +struct bgx_link_status { + u8 msg; + u8 link_up; + u8 duplex; + u32 speed; +}; + +#ifdef VNIC_MULTI_QSET_SUPPORT +/* Get Extra Qset IDs */ +struct sqs_alloc { + u8 msg; + u8 vf_id; + u8 qs_count; +}; + +struct nicvf_ptr { + u8 msg; + u8 vf_id; + bool sqs_mode; + u8 sqs_id; + u64 nicvf; +}; +#endif + +/* Set interface in loopback mode */ +struct set_loopback { + u8 msg; + u8 vf_id; + bool enable; +}; + +/* 128 bit shared memory between PF and each VF */ +union nic_mbx { + struct { u8 msg; } msg; + struct nic_cfg_msg nic_cfg; + struct qs_cfg_msg qs; + struct rq_cfg_msg rq; + struct sq_cfg_msg sq; + struct set_mac_msg mac; + struct set_frs_msg frs; + struct cpi_cfg_msg cpi_cfg; + struct rss_sz_msg rss_size; + struct rss_cfg_msg rss_cfg; + struct bgx_stats_msg bgx_stats; + struct bgx_link_status link_status; +#ifdef VNIC_MULTI_QSET_SUPPORT + struct sqs_alloc sqs_alloc; + struct nicvf_ptr nicvf; +#endif + struct set_loopback lbk; +}; + +int nicvf_set_real_num_queues(struct udevice *dev, + int tx_queues, int rx_queues); +int nicvf_open(struct udevice *dev); +void nicvf_stop(struct udevice *dev); +int nicvf_send_msg_to_pf(struct nicvf *vf, union nic_mbx *mbx); +void nicvf_update_stats(struct nicvf *nic); + +void nic_handle_mbx_intr(struct nicpf *nic, int vf); + +int bgx_poll_for_link(int node, int bgx_idx, int lmacid); +const u8 *bgx_get_lmac_mac(int node, int bgx_idx, int lmacid); +void bgx_set_lmac_mac(int node, int bgx_idx, int lmacid, const u8 *mac); +void bgx_lmac_rx_tx_enable(int node, int bgx_idx, int lmacid, bool enable); +void bgx_lmac_internal_loopback(int node, int bgx_idx, + int lmac_idx, bool enable); + +static inline bool pass1_silicon(unsigned int revision, int model_id) +{ + return ((revision < 8) && (model_id == 0x88)); +} + +static inline bool pass2_silicon(unsigned int revision, int model_id) +{ + return ((revision >= 8) && (model_id == 0x88)); +} + +#endif /* NIC_H */ diff --git a/drivers/net/octeontx/nic_main.c b/drivers/net/octeontx/nic_main.c new file mode 100644 index 0000000000..1a805f7a46 --- /dev/null +++ b/drivers/net/octeontx/nic_main.c @@ -0,0 +1,778 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell International Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nic_reg.h" +#include "nic.h" +#include "q_struct.h" + +unsigned long rounddown_pow_of_two(unsigned long n) +{ + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n |= n >> 32; + + return(n + 1); +} + +static void nic_config_cpi(struct nicpf *nic, struct cpi_cfg_msg *cfg); +static void nic_tx_channel_cfg(struct nicpf *nic, u8 vnic, + struct sq_cfg_msg *sq); +static int nic_update_hw_frs(struct nicpf *nic, int new_frs, int vf); +static int nic_rcv_queue_sw_sync(struct nicpf *nic); + +/* Register read/write APIs */ +static void nic_reg_write(struct nicpf *nic, u64 offset, u64 val) +{ + writeq(val, nic->reg_base + offset); +} + +static u64 nic_reg_read(struct nicpf *nic, u64 offset) +{ + return readq(nic->reg_base + offset); +} + +static u64 nic_get_mbx_addr(int vf) +{ + return NIC_PF_VF_0_127_MAILBOX_0_1 + (vf << NIC_VF_NUM_SHIFT); +} + +static void nic_send_msg_to_vf(struct nicpf *nic, int vf, union nic_mbx *mbx) +{ + void __iomem *mbx_addr = (void *)(nic->reg_base + nic_get_mbx_addr(vf)); + u64 *msg = (u64 *)mbx; + + /* In first revision HW, mbox interrupt is triggerred + * when PF writes to MBOX(1), in next revisions when + * PF writes to MBOX(0) + */ + if (pass1_silicon(nic->rev_id, nic->hw->model_id)) { + /* see the comment for nic_reg_write()/nic_reg_read() + * functions above + */ + writeq(msg[0], mbx_addr); + writeq(msg[1], mbx_addr + 8); + } else { + writeq(msg[1], mbx_addr + 8); + writeq(msg[0], mbx_addr); + } +} + +static void nic_mbx_send_ready(struct nicpf *nic, int vf) +{ + union nic_mbx mbx = {}; + int bgx_idx, lmac, timeout = 5, link = -1; + const u8 *mac; + + mbx.nic_cfg.msg = NIC_MBOX_MSG_READY; + mbx.nic_cfg.vf_id = vf; + + if (nic->flags & NIC_TNS_ENABLED) + mbx.nic_cfg.tns_mode = NIC_TNS_MODE; + else + mbx.nic_cfg.tns_mode = NIC_TNS_BYPASS_MODE; + + if (vf < nic->num_vf_en) { + bgx_idx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]); + + mac = bgx_get_lmac_mac(nic->node, bgx_idx, lmac); + if (mac) + memcpy((u8 *)&mbx.nic_cfg.mac_addr, mac, 6); + + while (timeout-- && (link <= 0)) { + link = bgx_poll_for_link(nic->node, bgx_idx, lmac); + debug("Link status: %d\n", link); + if (link <= 0) + mdelay(2000); + } + } +#ifdef VNIC_MULTI_QSET_SUPPORT + mbx.nic_cfg.sqs_mode = (vf >= nic->num_vf_en) ? true : false; +#endif + mbx.nic_cfg.node_id = nic->node; + + mbx.nic_cfg.loopback_supported = vf < nic->num_vf_en; + + nic_send_msg_to_vf(nic, vf, &mbx); +} + +/* ACKs VF's mailbox message + * @vf: VF to which ACK to be sent + */ +static void nic_mbx_send_ack(struct nicpf *nic, int vf) +{ + union nic_mbx mbx = {}; + + mbx.msg.msg = NIC_MBOX_MSG_ACK; + nic_send_msg_to_vf(nic, vf, &mbx); +} + +/* NACKs VF's mailbox message that PF is not able to + * complete the action + * @vf: VF to which ACK to be sent + */ +static void nic_mbx_send_nack(struct nicpf *nic, int vf) +{ + union nic_mbx mbx = {}; + + mbx.msg.msg = NIC_MBOX_MSG_NACK; + nic_send_msg_to_vf(nic, vf, &mbx); +} + +static int nic_config_loopback(struct nicpf *nic, struct set_loopback *lbk) +{ + int bgx_idx, lmac_idx; + + if (lbk->vf_id > nic->num_vf_en) + return -1; + + bgx_idx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[lbk->vf_id]); + lmac_idx = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[lbk->vf_id]); + + bgx_lmac_internal_loopback(nic->node, bgx_idx, lmac_idx, lbk->enable); + + return 0; +} + +/* Interrupt handler to handle mailbox messages from VFs */ +void nic_handle_mbx_intr(struct nicpf *nic, int vf) +{ + union nic_mbx mbx = {}; + u64 *mbx_data; + u64 mbx_addr; + u64 reg_addr; + u64 cfg; + int bgx, lmac; + int i; + int ret = 0; + + nic->mbx_lock[vf] = true; + + mbx_addr = nic_get_mbx_addr(vf); + mbx_data = (u64 *)&mbx; + + for (i = 0; i < NIC_PF_VF_MAILBOX_SIZE; i++) { + *mbx_data = nic_reg_read(nic, mbx_addr); + mbx_data++; + mbx_addr += sizeof(u64); + } + + debug("%s: Mailbox msg %d from VF%d\n", __func__, mbx.msg.msg, vf); + switch (mbx.msg.msg) { + case NIC_MBOX_MSG_READY: + nic_mbx_send_ready(nic, vf); + if (vf < nic->num_vf_en) { + nic->link[vf] = 0; + nic->duplex[vf] = 0; + nic->speed[vf] = 0; + } + ret = 1; + break; + case NIC_MBOX_MSG_QS_CFG: + reg_addr = NIC_PF_QSET_0_127_CFG | + (mbx.qs.num << NIC_QS_ID_SHIFT); + cfg = mbx.qs.cfg; +#ifdef VNIC_MULTI_QSET_SUPPORT + /* Check if its a secondary Qset */ + if (vf >= nic->num_vf_en) { + cfg = cfg & (~0x7FULL); + /* Assign this Qset to primary Qset's VF */ + cfg |= nic->pqs_vf[vf]; + } +#endif + nic_reg_write(nic, reg_addr, cfg); + break; + case NIC_MBOX_MSG_RQ_CFG: + reg_addr = NIC_PF_QSET_0_127_RQ_0_7_CFG | + (mbx.rq.qs_num << NIC_QS_ID_SHIFT) | + (mbx.rq.rq_num << NIC_Q_NUM_SHIFT); + nic_reg_write(nic, reg_addr, mbx.rq.cfg); + /* Enable CQE_RX2_S extension in CQE_RX descriptor. + * This gets appended by default on 81xx/83xx chips, + * for consistency enabling the same on 88xx pass2 + * where this is introduced. + */ + if (pass2_silicon(nic->rev_id, nic->hw->model_id)) + nic_reg_write(nic, NIC_PF_RX_CFG, 0x01); + break; + case NIC_MBOX_MSG_RQ_BP_CFG: + reg_addr = NIC_PF_QSET_0_127_RQ_0_7_BP_CFG | + (mbx.rq.qs_num << NIC_QS_ID_SHIFT) | + (mbx.rq.rq_num << NIC_Q_NUM_SHIFT); + nic_reg_write(nic, reg_addr, mbx.rq.cfg); + break; + case NIC_MBOX_MSG_RQ_SW_SYNC: + ret = nic_rcv_queue_sw_sync(nic); + break; + case NIC_MBOX_MSG_RQ_DROP_CFG: + reg_addr = NIC_PF_QSET_0_127_RQ_0_7_DROP_CFG | + (mbx.rq.qs_num << NIC_QS_ID_SHIFT) | + (mbx.rq.rq_num << NIC_Q_NUM_SHIFT); + nic_reg_write(nic, reg_addr, mbx.rq.cfg); + break; + case NIC_MBOX_MSG_SQ_CFG: + reg_addr = NIC_PF_QSET_0_127_SQ_0_7_CFG | + (mbx.sq.qs_num << NIC_QS_ID_SHIFT) | + (mbx.sq.sq_num << NIC_Q_NUM_SHIFT); + nic_reg_write(nic, reg_addr, mbx.sq.cfg); + nic_tx_channel_cfg(nic, mbx.qs.num, + (struct sq_cfg_msg *)&mbx.sq); + break; + case NIC_MBOX_MSG_SET_MAC: +#ifdef VNIC_MULTI_QSET_SUPPORT + if (vf >= nic->num_vf_en) + break; +#endif + lmac = mbx.mac.vf_id; + bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[lmac]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[lmac]); + bgx_set_lmac_mac(nic->node, bgx, lmac, mbx.mac.mac_addr); + break; + case NIC_MBOX_MSG_SET_MAX_FRS: + ret = nic_update_hw_frs(nic, mbx.frs.max_frs, + mbx.frs.vf_id); + break; + case NIC_MBOX_MSG_CPI_CFG: + nic_config_cpi(nic, &mbx.cpi_cfg); + break; +#ifdef VNIC_RSS_SUPPORT + case NIC_MBOX_MSG_RSS_SIZE: + nic_send_rss_size(nic, vf); + goto unlock; + case NIC_MBOX_MSG_RSS_CFG: + case NIC_MBOX_MSG_RSS_CFG_CONT: + nic_config_rss(nic, &mbx.rss_cfg); + break; +#endif + case NIC_MBOX_MSG_CFG_DONE: + /* Last message of VF config msg sequence */ + nic->vf_enabled[vf] = true; + if (vf >= nic->lmac_cnt) + goto unlock; + + bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]); + + bgx_lmac_rx_tx_enable(nic->node, bgx, lmac, true); + goto unlock; + case NIC_MBOX_MSG_SHUTDOWN: + /* First msg in VF teardown sequence */ + nic->vf_enabled[vf] = false; +#ifdef VNIC_MULTI_QSET_SUPPORT + if (vf >= nic->num_vf_en) + nic->sqs_used[vf - nic->num_vf_en] = false; + nic->pqs_vf[vf] = 0; +#endif + if (vf >= nic->lmac_cnt) + break; + + bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]); + + bgx_lmac_rx_tx_enable(nic->node, bgx, lmac, false); + break; +#ifdef VNIC_MULTI_QSET_SUPPORT + case NIC_MBOX_MSG_ALLOC_SQS: + nic_alloc_sqs(nic, &mbx.sqs_alloc); + goto unlock; + case NIC_MBOX_MSG_NICVF_PTR: + nic->nicvf[vf] = mbx.nicvf.nicvf; + break; + case NIC_MBOX_MSG_PNICVF_PTR: + nic_send_pnicvf(nic, vf); + goto unlock; + case NIC_MBOX_MSG_SNICVF_PTR: + nic_send_snicvf(nic, &mbx.nicvf); + goto unlock; +#endif + case NIC_MBOX_MSG_LOOPBACK: + ret = nic_config_loopback(nic, &mbx.lbk); + break; + default: + printf("Invalid msg from VF%d, msg 0x%x\n", vf, mbx.msg.msg); + break; + } + + if (!ret) + nic_mbx_send_ack(nic, vf); + else if (mbx.msg.msg != NIC_MBOX_MSG_READY) + nic_mbx_send_nack(nic, vf); +unlock: + nic->mbx_lock[vf] = false; +} + +static int nic_rcv_queue_sw_sync(struct nicpf *nic) +{ + int timeout = 20; + + nic_reg_write(nic, NIC_PF_SW_SYNC_RX, 0x01); + while (timeout) { + if (nic_reg_read(nic, NIC_PF_SW_SYNC_RX_DONE) & 0x1) + break; + udelay(2000); + timeout--; + } + nic_reg_write(nic, NIC_PF_SW_SYNC_RX, 0x00); + if (!timeout) { + printf("Recevie queue software sync failed"); + return 1; + } + return 0; +} + +static int nic_update_hw_frs(struct nicpf *nic, int new_frs, int vf) +{ + u64 *pkind = (u64 *)&nic->pkind; + + if (new_frs > NIC_HW_MAX_FRS || new_frs < NIC_HW_MIN_FRS) { + printf("Invalid MTU setting from VF%d rejected,", vf); + printf(" should be between %d and %d\n", NIC_HW_MIN_FRS, + NIC_HW_MAX_FRS); + return 1; + } + new_frs += ETH_HLEN; + if (new_frs <= nic->pkind.maxlen) + return 0; + + nic->pkind.maxlen = new_frs; + + nic_reg_write(nic, NIC_PF_PKIND_0_15_CFG, *pkind); + return 0; +} + +/* Set minimum transmit packet size */ +static void nic_set_tx_pkt_pad(struct nicpf *nic, int size) +{ + int lmac; + u64 lmac_cfg; + struct hw_info *hw = nic->hw; + int max_lmac = nic->hw->bgx_cnt * MAX_LMAC_PER_BGX; + + /* Max value that can be set is 60 */ + if (size > 52) + size = 52; + + /* CN81XX has RGX configured as FAKE BGX, adjust mac_lmac accordingly */ + if (hw->chans_per_rgx) + max_lmac = ((nic->hw->bgx_cnt - 1) * MAX_LMAC_PER_BGX) + 1; + + for (lmac = 0; lmac < max_lmac; lmac++) { + lmac_cfg = nic_reg_read(nic, NIC_PF_LMAC_0_7_CFG | (lmac << 3)); + lmac_cfg &= ~(0xF << 2); + lmac_cfg |= ((size / 4) << 2); + nic_reg_write(nic, NIC_PF_LMAC_0_7_CFG | (lmac << 3), lmac_cfg); + } +} + +/* Function to check number of LMACs present and set VF to LMAC mapping. + * Mapping will be used while initializing channels. + */ +static void nic_set_lmac_vf_mapping(struct nicpf *nic) +{ + int bgx, bgx_count, next_bgx_lmac = 0; + int lmac, lmac_cnt = 0; + u64 lmac_credit; + + nic->num_vf_en = 0; + if (nic->flags & NIC_TNS_ENABLED) { + nic->num_vf_en = DEFAULT_NUM_VF_ENABLED; + return; + } + + bgx_get_count(nic->node, &bgx_count); + debug("bgx_count: %d\n", bgx_count); + + for (bgx = 0; bgx < nic->hw->bgx_cnt; bgx++) { + if (!(bgx_count & (1 << bgx))) + continue; + nic->bgx_cnt++; + lmac_cnt = bgx_get_lmac_count(nic->node, bgx); + debug("lmac_cnt: %d for BGX%d\n", lmac_cnt, bgx); + for (lmac = 0; lmac < lmac_cnt; lmac++) + nic->vf_lmac_map[next_bgx_lmac++] = + NIC_SET_VF_LMAC_MAP(bgx, lmac); + nic->num_vf_en += lmac_cnt; + + /* Program LMAC credits */ + lmac_credit = (1ull << 1); /* chennel credit enable */ + lmac_credit |= (0x1ff << 2); + lmac_credit |= (((((48 * 1024) / lmac_cnt) - + NIC_HW_MAX_FRS) / 16) << 12); + lmac = bgx * MAX_LMAC_PER_BGX; + for (; lmac < lmac_cnt + (bgx * MAX_LMAC_PER_BGX); lmac++) + nic_reg_write(nic, NIC_PF_LMAC_0_7_CREDIT + (lmac * 8), + lmac_credit); + } +} + +static void nic_get_hw_info(struct nicpf *nic) +{ + u16 sdevid; + struct hw_info *hw = nic->hw; + + dm_pci_read_config16(nic->udev, PCI_SUBSYSTEM_ID, &sdevid); + + switch (sdevid) { + case PCI_SUBSYS_DEVID_88XX_NIC_PF: + hw->bgx_cnt = MAX_BGX_PER_NODE; + hw->chans_per_lmac = 16; + hw->chans_per_bgx = 128; + hw->cpi_cnt = 2048; + hw->rssi_cnt = 4096; + hw->rss_ind_tbl_size = NIC_MAX_RSS_IDR_TBL_SIZE; + hw->tl3_cnt = 256; + hw->tl2_cnt = 64; + hw->tl1_cnt = 2; + hw->tl1_per_bgx = true; + hw->model_id = 0x88; + break; + case PCI_SUBSYS_DEVID_81XX_NIC_PF: + hw->bgx_cnt = MAX_BGX_PER_NODE; + hw->chans_per_lmac = 8; + hw->chans_per_bgx = 32; + hw->chans_per_rgx = 8; + hw->chans_per_lbk = 24; + hw->cpi_cnt = 512; + hw->rssi_cnt = 256; + hw->rss_ind_tbl_size = 32; /* Max RSSI / Max interfaces */ + hw->tl3_cnt = 64; + hw->tl2_cnt = 16; + hw->tl1_cnt = 10; + hw->tl1_per_bgx = false; + hw->model_id = 0x81; + break; + case PCI_SUBSYS_DEVID_83XX_NIC_PF: + hw->bgx_cnt = MAX_BGX_PER_NODE; + hw->chans_per_lmac = 8; + hw->chans_per_bgx = 32; + hw->chans_per_lbk = 64; + hw->cpi_cnt = 2048; + hw->rssi_cnt = 1024; + hw->rss_ind_tbl_size = 64; /* Max RSSI / Max interfaces */ + hw->tl3_cnt = 256; + hw->tl2_cnt = 64; + hw->tl1_cnt = 18; + hw->tl1_per_bgx = false; + hw->model_id = 0x83; + break; + } + + hw->tl4_cnt = MAX_QUEUES_PER_QSET * pci_sriov_get_totalvfs(nic->udev); +} + +static void nic_init_hw(struct nicpf *nic) +{ + int i; + u64 reg; + u64 *pkind = (u64 *)&nic->pkind; + + /* Get HW capability info */ + nic_get_hw_info(nic); + + /* Enable NIC HW block */ + nic_reg_write(nic, NIC_PF_CFG, 0x3); + + /* Enable backpressure */ + nic_reg_write(nic, NIC_PF_BP_CFG, (1ULL << 6) | 0x03); + nic_reg_write(nic, NIC_PF_INTF_0_1_BP_CFG, (1ULL << 63) | 0x08); + nic_reg_write(nic, NIC_PF_INTF_0_1_BP_CFG + (1 << 8), + (1ULL << 63) | 0x09); + + for (i = 0; i < NIC_MAX_CHANS; i++) + nic_reg_write(nic, NIC_PF_CHAN_0_255_TX_CFG | (i << 3), 1); + + if (nic->flags & NIC_TNS_ENABLED) { + reg = NIC_TNS_MODE << 7; + reg |= 0x06; + nic_reg_write(nic, NIC_PF_INTF_0_1_SEND_CFG, reg); + reg &= ~0xFull; + reg |= 0x07; + nic_reg_write(nic, NIC_PF_INTF_0_1_SEND_CFG | (1 << 8), reg); + } else { + /* Disable TNS mode on both interfaces */ + reg = NIC_TNS_BYPASS_MODE << 7; + reg |= 0x08; /* Block identifier */ + nic_reg_write(nic, NIC_PF_INTF_0_1_SEND_CFG, reg); + reg &= ~0xFull; + reg |= 0x09; + nic_reg_write(nic, NIC_PF_INTF_0_1_SEND_CFG | (1 << 8), reg); + } + + /* PKIND configuration */ + nic->pkind.minlen = 0; + nic->pkind.maxlen = NIC_HW_MAX_FRS + ETH_HLEN; + nic->pkind.lenerr_en = 1; + nic->pkind.rx_hdr = 0; + nic->pkind.hdr_sl = 0; + + for (i = 0; i < NIC_MAX_PKIND; i++) + nic_reg_write(nic, NIC_PF_PKIND_0_15_CFG | (i << 3), *pkind); + + nic_set_tx_pkt_pad(nic, NIC_HW_MIN_FRS); + + /* Timer config */ + nic_reg_write(nic, NIC_PF_INTR_TIMER_CFG, NICPF_CLK_PER_INT_TICK); +} + +/* Channel parse index configuration */ +static void nic_config_cpi(struct nicpf *nic, struct cpi_cfg_msg *cfg) +{ + struct hw_info *hw = nic->hw; + u32 vnic, bgx, lmac, chan; + u32 padd, cpi_count = 0; + u64 cpi_base, cpi, rssi_base, rssi; + u8 qset, rq_idx = 0; + + vnic = cfg->vf_id; + bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vnic]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vnic]); + + chan = (lmac * hw->chans_per_lmac) + (bgx * hw->chans_per_bgx); + cpi_base = vnic * NIC_MAX_CPI_PER_LMAC; + rssi_base = vnic * hw->rss_ind_tbl_size; + + /* Rx channel configuration */ + nic_reg_write(nic, NIC_PF_CHAN_0_255_RX_BP_CFG | (chan << 3), + (1ull << 63) | (vnic << 0)); + nic_reg_write(nic, NIC_PF_CHAN_0_255_RX_CFG | (chan << 3), + ((u64)cfg->cpi_alg << 62) | (cpi_base << 48)); + + if (cfg->cpi_alg == CPI_ALG_NONE) + cpi_count = 1; + else if (cfg->cpi_alg == CPI_ALG_VLAN) /* 3 bits of PCP */ + cpi_count = 8; + else if (cfg->cpi_alg == CPI_ALG_VLAN16) /* 3 bits PCP + DEI */ + cpi_count = 16; + else if (cfg->cpi_alg == CPI_ALG_DIFF) /* 6bits DSCP */ + cpi_count = NIC_MAX_CPI_PER_LMAC; + + /* RSS Qset, Qidx mapping */ + qset = cfg->vf_id; + rssi = rssi_base; + for (; rssi < (rssi_base + cfg->rq_cnt); rssi++) { + nic_reg_write(nic, NIC_PF_RSSI_0_4097_RQ | (rssi << 3), + (qset << 3) | rq_idx); + rq_idx++; + } + + rssi = 0; + cpi = cpi_base; + for (; cpi < (cpi_base + cpi_count); cpi++) { + /* Determine port to channel adder */ + if (cfg->cpi_alg != CPI_ALG_DIFF) + padd = cpi % cpi_count; + else + padd = cpi % 8; /* 3 bits CS out of 6bits DSCP */ + + /* Leave RSS_SIZE as '0' to disable RSS */ + if (pass1_silicon(nic->rev_id, nic->hw->model_id)) { + nic_reg_write(nic, NIC_PF_CPI_0_2047_CFG | (cpi << 3), + (vnic << 24) | (padd << 16) | + (rssi_base + rssi)); + } else { + /* Set MPI_ALG to '0' to disable MCAM parsing */ + nic_reg_write(nic, NIC_PF_CPI_0_2047_CFG | (cpi << 3), + (padd << 16)); + /* MPI index is same as CPI if MPI_ALG is not enabled */ + nic_reg_write(nic, NIC_PF_MPI_0_2047_CFG | (cpi << 3), + (vnic << 24) | (rssi_base + rssi)); + } + + if ((rssi + 1) >= cfg->rq_cnt) + continue; + + if (cfg->cpi_alg == CPI_ALG_VLAN) + rssi++; + else if (cfg->cpi_alg == CPI_ALG_VLAN16) + rssi = ((cpi - cpi_base) & 0xe) >> 1; + else if (cfg->cpi_alg == CPI_ALG_DIFF) + rssi = ((cpi - cpi_base) & 0x38) >> 3; + } + nic->cpi_base[cfg->vf_id] = cpi_base; + nic->rssi_base[cfg->vf_id] = rssi_base; +} + +/* Transmit channel configuration (TL4 -> TL3 -> Chan) + * VNIC0-SQ0 -> TL4(0) -> TL4A(0) -> TL3[0] -> BGX0/LMAC0/Chan0 + * VNIC1-SQ0 -> TL4(8) -> TL4A(2) -> TL3[2] -> BGX0/LMAC1/Chan0 + * VNIC2-SQ0 -> TL4(16) -> TL4A(4) -> TL3[4] -> BGX0/LMAC2/Chan0 + * VNIC3-SQ0 -> TL4(32) -> TL4A(6) -> TL3[6] -> BGX0/LMAC3/Chan0 + * VNIC4-SQ0 -> TL4(512) -> TL4A(128) -> TL3[128] -> BGX1/LMAC0/Chan0 + * VNIC5-SQ0 -> TL4(520) -> TL4A(130) -> TL3[130] -> BGX1/LMAC1/Chan0 + * VNIC6-SQ0 -> TL4(528) -> TL4A(132) -> TL3[132] -> BGX1/LMAC2/Chan0 + * VNIC7-SQ0 -> TL4(536) -> TL4A(134) -> TL3[134] -> BGX1/LMAC3/Chan0 + */ +static void nic_tx_channel_cfg(struct nicpf *nic, u8 vnic, + struct sq_cfg_msg *sq) +{ + struct hw_info *hw = nic->hw; + u32 bgx, lmac, chan; + u32 tl2, tl3, tl4; + u32 rr_quantum; + u8 sq_idx = sq->sq_num; + u8 pqs_vnic = vnic; + int svf; + u16 sdevid; + + dm_pci_read_config16(nic->udev, PCI_SUBSYSTEM_ID, &sdevid); + + bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[pqs_vnic]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[pqs_vnic]); + + /* 24 bytes for FCS, IPG and preamble */ + rr_quantum = ((NIC_HW_MAX_FRS + 24) / 4); + + /* For 88xx 0-511 TL4 transmits via BGX0 and + * 512-1023 TL4s transmit via BGX1. + */ + if (hw->tl1_per_bgx) { + tl4 = bgx * (hw->tl4_cnt / hw->bgx_cnt); + if (!sq->sqs_mode) { + tl4 += (lmac * MAX_QUEUES_PER_QSET); + } else { + for (svf = 0; svf < MAX_SQS_PER_VF_SINGLE_NODE; svf++) { + if (nic->vf_sqs[pqs_vnic][svf] == vnic) + break; + } + tl4 += (MAX_LMAC_PER_BGX * MAX_QUEUES_PER_QSET); + tl4 += (lmac * MAX_QUEUES_PER_QSET * + MAX_SQS_PER_VF_SINGLE_NODE); + tl4 += (svf * MAX_QUEUES_PER_QSET); + } + } else { + tl4 = (vnic * MAX_QUEUES_PER_QSET); + } + + tl4 += sq_idx; + + tl3 = tl4 / (hw->tl4_cnt / hw->tl3_cnt); + nic_reg_write(nic, NIC_PF_QSET_0_127_SQ_0_7_CFG2 | + ((u64)vnic << NIC_QS_ID_SHIFT) | + ((u32)sq_idx << NIC_Q_NUM_SHIFT), tl4); + nic_reg_write(nic, NIC_PF_TL4_0_1023_CFG | (tl4 << 3), + ((u64)vnic << 27) | ((u32)sq_idx << 24) | rr_quantum); + + nic_reg_write(nic, NIC_PF_TL3_0_255_CFG | (tl3 << 3), rr_quantum); + + /* On 88xx 0-127 channels are for BGX0 and + * 127-255 channels for BGX1. + * + * On 81xx/83xx TL3_CHAN reg should be configured with channel + * within LMAC i.e 0-7 and not the actual channel number like on 88xx + */ + chan = (lmac * hw->chans_per_lmac) + (bgx * hw->chans_per_bgx); + if (hw->tl1_per_bgx) + nic_reg_write(nic, NIC_PF_TL3_0_255_CHAN | (tl3 << 3), chan); + else + nic_reg_write(nic, NIC_PF_TL3_0_255_CHAN | (tl3 << 3), 0); + + /* Enable backpressure on the channel */ + nic_reg_write(nic, NIC_PF_CHAN_0_255_TX_CFG | (chan << 3), 1); + + tl2 = tl3 >> 2; + nic_reg_write(nic, NIC_PF_TL3A_0_63_CFG | (tl2 << 3), tl2); + nic_reg_write(nic, NIC_PF_TL2_0_63_CFG | (tl2 << 3), rr_quantum); + /* No priorities as of now */ + nic_reg_write(nic, NIC_PF_TL2_0_63_PRI | (tl2 << 3), 0x00); + + /* Unlike 88xx where TL2s 0-31 transmits to TL1 '0' and rest to TL1 '1' + * on 81xx/83xx TL2 needs to be configured to transmit to one of the + * possible LMACs. + * + * This register doesn't exist on 88xx. + */ + if (!hw->tl1_per_bgx) + nic_reg_write(nic, NIC_PF_TL2_LMAC | (tl2 << 3), + lmac + (bgx * MAX_LMAC_PER_BGX)); +} + +int nic_initialize(struct udevice *dev) +{ + struct nicpf *nic = dev_get_priv(dev); + + nic->udev = dev; + nic->hw = calloc(1, sizeof(struct hw_info)); + if (!nic->hw) + return -ENOMEM; + + /* MAP PF's configuration registers */ + nic->reg_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, + PCI_REGION_MEM); + if (!nic->reg_base) { + printf("Cannot map config register space, aborting\n"); + goto exit; + } + + nic->node = node_id(nic->reg_base); + dm_pci_read_config8(dev, PCI_REVISION_ID, &nic->rev_id); + + /* By default set NIC in TNS bypass mode */ + nic->flags &= ~NIC_TNS_ENABLED; + + /* Initialize hardware */ + nic_init_hw(nic); + + nic_set_lmac_vf_mapping(nic); + + /* Set RSS TBL size for each VF */ + nic->rss_ind_tbl_size = NIC_MAX_RSS_IDR_TBL_SIZE; + + nic->rss_ind_tbl_size = rounddown_pow_of_two(nic->rss_ind_tbl_size); + + return 0; +exit: + free(nic->hw); + return -ENODEV; +} + +int octeontx_nic_probe(struct udevice *dev) +{ + int ret = 0; + struct nicpf *nicpf = dev_get_priv(dev); + + nicpf->udev = dev; + ret = nic_initialize(dev); + if (ret < 0) { + printf("couldn't initialize NIC PF\n"); + return ret; + } + + ret = pci_sriov_init(dev, nicpf->num_vf_en); + if (ret < 0) + printf("enabling SRIOV failed for num VFs %d\n", + nicpf->num_vf_en); + + return ret; +} + +U_BOOT_DRIVER(octeontx_nic) = { + .name = "octeontx_nic", + .id = UCLASS_MISC, + .probe = octeontx_nic_probe, + .priv_auto_alloc_size = sizeof(struct nicpf), +}; + +static struct pci_device_id octeontx_nic_supported[] = { + { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_NIC) }, + {} +}; + +U_BOOT_PCI_DEVICE(octeontx_nic, octeontx_nic_supported); + diff --git a/drivers/net/octeontx/nic_reg.h b/drivers/net/octeontx/nic_reg.h new file mode 100644 index 0000000000..c214ebb679 --- /dev/null +++ b/drivers/net/octeontx/nic_reg.h @@ -0,0 +1,250 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Marvell International Ltd. + */ + +#ifndef NIC_REG_H +#define NIC_REG_H + +#define NIC_PF_REG_COUNT 29573 +#define NIC_VF_REG_COUNT 249 + +/* Physical function register offsets */ +#define NIC_PF_CFG (0x0000) +#define NIC_PF_STATUS (0x0010) + +#define NIC_PF_INTR_TIMER_CFG (0x0030) +#define NIC_PF_BIST_STATUS (0x0040) +#define NIC_PF_SOFT_RESET (0x0050) + +#define NIC_PF_TCP_TIMER (0x0060) +#define NIC_PF_BP_CFG (0x0080) +#define NIC_PF_RRM_CFG (0x0088) +#define NIC_PF_CQM_CF (0x00A0) +#define NIC_PF_CNM_CF (0x00A8) +#define NIC_PF_CNM_STATUS (0x00B0) +#define NIC_PF_CQ_AVG_CFG (0x00C0) +#define NIC_PF_RRM_AVG_CFG (0x00C8) + +#define NIC_PF_INTF_0_1_SEND_CFG (0x0200) +#define NIC_PF_INTF_0_1_BP_CFG (0x0208) +#define NIC_PF_INTF_0_1_BP_DIS_0_1 (0x0210) +#define NIC_PF_INTF_0_1_BP_SW_0_1 (0x0220) +#define NIC_PF_RBDR_BP_STATE_0_3 (0x0240) + +#define NIC_PF_MAILBOX_INT (0x0410) +#define NIC_PF_MAILBOX_INT_W1S (0x0430) +#define NIC_PF_MAILBOX_ENA_W1C (0x0450) +#define NIC_PF_MAILBOX_ENA_W1S (0x0470) + +#define NIC_PF_RX_ETYPE_0_7 (0x0500) +#define NIC_PF_RX_CFG (0x05D0) +#define NIC_PF_PKIND_0_15_CFG (0x0600) + +#define NIC_PF_ECC0_FLIP0 (0x1000) +#define NIC_PF_ECC1_FLIP0 (0x1008) +#define NIC_PF_ECC2_FLIP0 (0x1010) +#define NIC_PF_ECC3_FLIP0 (0x1018) +#define NIC_PF_ECC0_FLIP1 (0x1080) +#define NIC_PF_ECC1_FLIP1 (0x1088) +#define NIC_PF_ECC2_FLIP1 (0x1090) +#define NIC_PF_ECC3_FLIP1 (0x1098) +#define NIC_PF_ECC0_CDIS (0x1100) +#define NIC_PF_ECC1_CDIS (0x1108) +#define NIC_PF_ECC2_CDIS (0x1110) +#define NIC_PF_ECC3_CDIS (0x1118) +#define NIC_PF_BIST0_STATUS (0x1280) +#define NIC_PF_BIST1_STATUS (0x1288) +#define NIC_PF_BIST2_STATUS (0x1290) +#define NIC_PF_BIST3_STATUS (0x1298) + +#define NIC_PF_ECC0_SBE_INT (0x2000) +#define NIC_PF_ECC0_SBE_INT_W1S (0x2008) +#define NIC_PF_ECC0_SBE_ENA_W1C (0x2010) +#define NIC_PF_ECC0_SBE_ENA_W1S (0x2018) +#define NIC_PF_ECC0_DBE_INT (0x2100) +#define NIC_PF_ECC0_DBE_INT_W1S (0x2108) +#define NIC_PF_ECC0_DBE_ENA_W1C (0x2110) +#define NIC_PF_ECC0_DBE_ENA_W1S (0x2118) + +#define NIC_PF_ECC1_SBE_INT (0x2200) +#define NIC_PF_ECC1_SBE_INT_W1S (0x2208) +#define NIC_PF_ECC1_SBE_ENA_W1C (0x2210) +#define NIC_PF_ECC1_SBE_ENA_W1S (0x2218) +#define NIC_PF_ECC1_DBE_INT (0x2300) +#define NIC_PF_ECC1_DBE_INT_W1S (0x2308) +#define NIC_PF_ECC1_DBE_ENA_W1C (0x2310) +#define NIC_PF_ECC1_DBE_ENA_W1S (0x2318) + +#define NIC_PF_ECC2_SBE_INT (0x2400) +#define NIC_PF_ECC2_SBE_INT_W1S (0x2408) +#define NIC_PF_ECC2_SBE_ENA_W1C (0x2410) +#define NIC_PF_ECC2_SBE_ENA_W1S (0x2418) +#define NIC_PF_ECC2_DBE_INT (0x2500) +#define NIC_PF_ECC2_DBE_INT_W1S (0x2508) +#define NIC_PF_ECC2_DBE_ENA_W1C (0x2510) +#define NIC_PF_ECC2_DBE_ENA_W1S (0x2518) + +#define NIC_PF_ECC3_SBE_INT (0x2600) +#define NIC_PF_ECC3_SBE_INT_W1S (0x2608) +#define NIC_PF_ECC3_SBE_ENA_W1C (0x2610) +#define NIC_PF_ECC3_SBE_ENA_W1S (0x2618) +#define NIC_PF_ECC3_DBE_INT (0x2700) +#define NIC_PF_ECC3_DBE_INT_W1S (0x2708) +#define NIC_PF_ECC3_DBE_ENA_W1C (0x2710) +#define NIC_PF_ECC3_DBE_ENA_W1S (0x2718) + +#define NIC_PF_CPI_0_2047_CFG (0x200000) +#define NIC_PF_MPI_0_2047_CFG (0x210000) +#define NIC_PF_RSSI_0_4097_RQ (0x220000) +#define NIC_PF_LMAC_0_7_CFG (0x240000) +#define NIC_PF_LMAC_0_7_SW_XOFF (0x242000) +#define NIC_PF_LMAC_0_7_CREDIT (0x244000) +#define NIC_PF_CHAN_0_255_TX_CFG (0x400000) +#define NIC_PF_CHAN_0_255_RX_CFG (0x420000) +#define NIC_PF_CHAN_0_255_SW_XOFF (0x440000) +#define NIC_PF_CHAN_0_255_CREDIT (0x460000) +#define NIC_PF_CHAN_0_255_RX_BP_CFG (0x480000) + +#define NIC_PF_SW_SYNC_RX (0x490000) + +#define NIC_PF_SW_SYNC_RX_DONE (0x490008) +#define NIC_PF_TL2_0_63_CFG (0x500000) +#define NIC_PF_TL2_0_63_PRI (0x520000) +#define NIC_PF_TL2_LMAC (0x540000) +#define NIC_PF_TL2_0_63_SH_STATUS (0x580000) +#define NIC_PF_TL3A_0_63_CFG (0x5F0000) +#define NIC_PF_TL3_0_255_CFG (0x600000) +#define NIC_PF_TL3_0_255_CHAN (0x620000) +#define NIC_PF_TL3_0_255_PIR (0x640000) +#define NIC_PF_TL3_0_255_SW_XOFF (0x660000) +#define NIC_PF_TL3_0_255_CNM_RATE (0x680000) +#define NIC_PF_TL3_0_255_SH_STATUS (0x6A0000) +#define NIC_PF_TL4A_0_255_CFG (0x6F0000) +#define NIC_PF_TL4_0_1023_CFG (0x800000) +#define NIC_PF_TL4_0_1023_SW_XOFF (0x820000) +#define NIC_PF_TL4_0_1023_SH_STATUS (0x840000) +#define NIC_PF_TL4A_0_1023_CNM_RATE (0x880000) +#define NIC_PF_TL4A_0_1023_CNM_STATUS (0x8A0000) + +#define NIC_PF_VF_0_127_MAILBOX_0_1 (0x20002030) +#define NIC_PF_VNIC_0_127_TX_STAT_0_4 (0x20004000) +#define NIC_PF_VNIC_0_127_RX_STAT_0_13 (0x20004100) +#define NIC_PF_QSET_0_127_LOCK_0_15 (0x20006000) +#define NIC_PF_QSET_0_127_CFG (0x20010000) +#define NIC_PF_QSET_0_127_RQ_0_7_CFG (0x20010400) +#define NIC_PF_QSET_0_127_RQ_0_7_DROP_CFG (0x20010420) +#define NIC_PF_QSET_0_127_RQ_0_7_BP_CFG (0x20010500) +#define NIC_PF_QSET_0_127_RQ_0_7_STAT_0_1 (0x20010600) +#define NIC_PF_QSET_0_127_SQ_0_7_CFG (0x20010C00) +#define NIC_PF_QSET_0_127_SQ_0_7_CFG2 (0x20010C08) +#define NIC_PF_QSET_0_127_SQ_0_7_STAT_0_1 (0x20010D00) + +#define NIC_PF_MSIX_VEC_0_18_ADDR (0x000000) +#define NIC_PF_MSIX_VEC_0_CTL (0x000008) +#define NIC_PF_MSIX_PBA_0 (0x0F0000) + +/* Virtual function register offsets */ +#define NIC_VNIC_CFG (0x000020) +#define NIC_VF_PF_MAILBOX_0_1 (0x000130) +#define NIC_VF_INT (0x000200) +#define NIC_VF_INT_W1S (0x000220) +#define NIC_VF_ENA_W1C (0x000240) +#define NIC_VF_ENA_W1S (0x000260) + +#define NIC_VNIC_RSS_CFG (0x0020E0) +#define NIC_VNIC_RSS_KEY_0_4 (0x002200) +#define NIC_VNIC_TX_STAT_0_4 (0x004000) +#define NIC_VNIC_RX_STAT_0_13 (0x004100) +#define NIC_QSET_RQ_GEN_CFG (0x010010) + +#define NIC_QSET_CQ_0_7_CFG (0x010400) +#define NIC_QSET_CQ_0_7_CFG2 (0x010408) +#define NIC_QSET_CQ_0_7_THRESH (0x010410) +#define NIC_QSET_CQ_0_7_BASE (0x010420) +#define NIC_QSET_CQ_0_7_HEAD (0x010428) +#define NIC_QSET_CQ_0_7_TAIL (0x010430) +#define NIC_QSET_CQ_0_7_DOOR (0x010438) +#define NIC_QSET_CQ_0_7_STATUS (0x010440) +#define NIC_QSET_CQ_0_7_STATUS2 (0x010448) +#define NIC_QSET_CQ_0_7_DEBUG (0x010450) + +#define NIC_QSET_RQ_0_7_CFG (0x010600) +#define NIC_QSET_RQ_0_7_STAT_0_1 (0x010700) + +#define NIC_QSET_SQ_0_7_CFG (0x010800) +#define NIC_QSET_SQ_0_7_THRESH (0x010810) +#define NIC_QSET_SQ_0_7_BASE (0x010820) +#define NIC_QSET_SQ_0_7_HEAD (0x010828) +#define NIC_QSET_SQ_0_7_TAIL (0x010830) +#define NIC_QSET_SQ_0_7_DOOR (0x010838) +#define NIC_QSET_SQ_0_7_STATUS (0x010840) +#define NIC_QSET_SQ_0_7_DEBUG (0x010848) +#define NIC_QSET_SQ_0_7_CNM_CHG (0x010860) +#define NIC_QSET_SQ_0_7_STAT_0_1 (0x010900) + +#define NIC_QSET_RBDR_0_1_CFG (0x010C00) +#define NIC_QSET_RBDR_0_1_THRESH (0x010C10) +#define NIC_QSET_RBDR_0_1_BASE (0x010C20) +#define NIC_QSET_RBDR_0_1_HEAD (0x010C28) +#define NIC_QSET_RBDR_0_1_TAIL (0x010C30) +#define NIC_QSET_RBDR_0_1_DOOR (0x010C38) +#define NIC_QSET_RBDR_0_1_STATUS0 (0x010C40) +#define NIC_QSET_RBDR_0_1_STATUS1 (0x010C48) +#define NIC_QSET_RBDR_0_1_PREFETCH_STATUS (0x010C50) + +#define NIC_VF_MSIX_VECTOR_0_19_ADDR (0x000000) +#define NIC_VF_MSIX_VECTOR_0_19_CTL (0x000008) +#define NIC_VF_MSIX_PBA (0x0F0000) + +/* Offsets within registers */ +#define NIC_MSIX_VEC_SHIFT 4 +#define NIC_Q_NUM_SHIFT 18 +#define NIC_QS_ID_SHIFT 21 +#define NIC_VF_NUM_SHIFT 21 + +/* Port kind configuration register */ +struct pkind_cfg { +#if defined(__BIG_ENDIAN_BITFIELD) + uint64_t reserved_42_63:22; + uint64_t hdr_sl:5; /* Header skip length */ + uint64_t rx_hdr:3; /* TNS Receive header present */ + uint64_t lenerr_en:1; /* L2 length error check enable */ + uint64_t reserved_32_32:1; + uint64_t maxlen:16; /* Max frame size */ + uint64_t minlen:16; /* Min frame size */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) + uint64_t minlen:16; + uint64_t maxlen:16; + uint64_t reserved_32_32:1; + uint64_t lenerr_en:1; + uint64_t rx_hdr:3; + uint64_t hdr_sl:5; + uint64_t reserved_42_63:22; +#endif +}; + +static inline uint64_t BGXX_PF_BAR0(unsigned long param1) + __attribute__ ((pure, always_inline)); +static inline uint64_t BGXX_PF_BAR0(unsigned long param1) +{ + assert(param1 <= 1); + return 0x87E0E0000000 + (param1 << 24); +} + +#define BGXX_PF_BAR0_SIZE 0x400000 +#define NIC_PF_BAR0 0x843000000000 +#define NIC_PF_BAR0_SIZE 0x40000000 + +static inline uint64_t NIC_VFX_BAR0(unsigned long param1) + __attribute__ ((pure, always_inline)); +static inline uint64_t NIC_VFX_BAR0(unsigned long param1) +{ + assert(param1 <= 127); + + return 0x8430A0000000 + (param1 << 21); +} + +#define NIC_VFX_BAR0_SIZE 0x200000 + +#endif /* NIC_REG_H */ diff --git a/drivers/net/octeontx/nicvf_main.c b/drivers/net/octeontx/nicvf_main.c new file mode 100644 index 0000000000..e13c8b9556 --- /dev/null +++ b/drivers/net/octeontx/nicvf_main.c @@ -0,0 +1,581 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell International Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nic_reg.h" +#include "nic.h" +#include "nicvf_queues.h" + +/* Register read/write APIs */ +void nicvf_reg_write(struct nicvf *nic, u64 offset, u64 val) +{ + writeq(val, nic->reg_base + offset); +} + +u64 nicvf_reg_read(struct nicvf *nic, u64 offset) +{ + return readq(nic->reg_base + offset); +} + +void nicvf_queue_reg_write(struct nicvf *nic, u64 offset, + u64 qidx, u64 val) +{ + void *addr = nic->reg_base + offset; + + writeq(val, (void *)(addr + (qidx << NIC_Q_NUM_SHIFT))); +} + +u64 nicvf_queue_reg_read(struct nicvf *nic, u64 offset, u64 qidx) +{ + void *addr = nic->reg_base + offset; + + return readq((void *)(addr + (qidx << NIC_Q_NUM_SHIFT))); +} + +static void nicvf_handle_mbx_intr(struct nicvf *nic); + +/* VF -> PF mailbox communication */ +static void nicvf_write_to_mbx(struct nicvf *nic, union nic_mbx *mbx) +{ + u64 *msg = (u64 *)mbx; + + nicvf_reg_write(nic, NIC_VF_PF_MAILBOX_0_1 + 0, msg[0]); + nicvf_reg_write(nic, NIC_VF_PF_MAILBOX_0_1 + 8, msg[1]); +} + +int nicvf_send_msg_to_pf(struct nicvf *nic, union nic_mbx *mbx) +{ + int timeout = NIC_PF_VF_MBX_TIMEOUT; + int sleep = 10; + + nic->pf_acked = false; + nic->pf_nacked = false; + + nicvf_write_to_mbx(nic, mbx); + + nic_handle_mbx_intr(nic->nicpf, nic->vf_id); + + /* Wait for previous message to be acked, timeout 2sec */ + while (!nic->pf_acked) { + if (nic->pf_nacked) + return -1; + mdelay(sleep); + nicvf_handle_mbx_intr(nic); + + if (nic->pf_acked) + break; + timeout -= sleep; + if (!timeout) { + printf("PF didn't ack to mbox msg %d from VF%d\n", + (mbx->msg.msg & 0xFF), nic->vf_id); + return -1; + } + } + + return 0; +} + +/* Checks if VF is able to comminicate with PF + * and also gets the VNIC number this VF is associated to. + */ +static int nicvf_check_pf_ready(struct nicvf *nic) +{ + union nic_mbx mbx = {}; + + mbx.msg.msg = NIC_MBOX_MSG_READY; + if (nicvf_send_msg_to_pf(nic, &mbx)) { + printf("PF didn't respond to READY msg\n"); + return 0; + } + + return 1; +} + +static void nicvf_handle_mbx_intr(struct nicvf *nic) +{ + union nic_mbx mbx = {}; + struct eth_pdata *pdata = dev_get_platdata(nic->dev); + u64 *mbx_data; + u64 mbx_addr; + int i; + + mbx_addr = NIC_VF_PF_MAILBOX_0_1; + mbx_data = (u64 *)&mbx; + + for (i = 0; i < NIC_PF_VF_MAILBOX_SIZE; i++) { + *mbx_data = nicvf_reg_read(nic, mbx_addr); + mbx_data++; + mbx_addr += sizeof(u64); + } + + debug("Mbox message: msg: 0x%x\n", mbx.msg.msg); + switch (mbx.msg.msg) { + case NIC_MBOX_MSG_READY: + nic->pf_acked = true; + nic->vf_id = mbx.nic_cfg.vf_id & 0x7F; + nic->tns_mode = mbx.nic_cfg.tns_mode & 0x7F; + nic->node = mbx.nic_cfg.node_id; + if (!nic->set_mac_pending) + memcpy(pdata->enetaddr, + mbx.nic_cfg.mac_addr, 6); + nic->loopback_supported = mbx.nic_cfg.loopback_supported; + nic->link_up = false; + nic->duplex = 0; + nic->speed = 0; + break; + case NIC_MBOX_MSG_ACK: + nic->pf_acked = true; + break; + case NIC_MBOX_MSG_NACK: + nic->pf_nacked = true; + break; + case NIC_MBOX_MSG_BGX_LINK_CHANGE: + nic->pf_acked = true; + nic->link_up = mbx.link_status.link_up; + nic->duplex = mbx.link_status.duplex; + nic->speed = mbx.link_status.speed; + if (nic->link_up) { + printf("%s: Link is Up %d Mbps %s\n", + nic->dev->name, nic->speed, + nic->duplex == 1 ? + "Full duplex" : "Half duplex"); + } else { + printf("%s: Link is Down\n", nic->dev->name); + } + break; + default: + printf("Invalid message from PF, msg 0x%x\n", mbx.msg.msg); + break; + } + + nicvf_clear_intr(nic, NICVF_INTR_MBOX, 0); +} + +static int nicvf_hw_set_mac_addr(struct nicvf *nic, struct udevice *dev) +{ + union nic_mbx mbx = {}; + struct eth_pdata *pdata = dev_get_platdata(dev); + + mbx.mac.msg = NIC_MBOX_MSG_SET_MAC; + mbx.mac.vf_id = nic->vf_id; + memcpy(mbx.mac.mac_addr, pdata->enetaddr, 6); + + return nicvf_send_msg_to_pf(nic, &mbx); +} + +static void nicvf_config_cpi(struct nicvf *nic) +{ + union nic_mbx mbx = {}; + + mbx.cpi_cfg.msg = NIC_MBOX_MSG_CPI_CFG; + mbx.cpi_cfg.vf_id = nic->vf_id; + mbx.cpi_cfg.cpi_alg = nic->cpi_alg; + mbx.cpi_cfg.rq_cnt = nic->qs->rq_cnt; + + nicvf_send_msg_to_pf(nic, &mbx); +} + +static int nicvf_init_resources(struct nicvf *nic) +{ + int err; + + nic->num_qs = 1; + + /* Enable Qset */ + nicvf_qset_config(nic, true); + + /* Initialize queues and HW for data transfer */ + err = nicvf_config_data_transfer(nic, true); + + if (err) { + printf("Failed to alloc/config VF's QSet resources\n"); + return err; + } + return 0; +} + +static void nicvf_snd_pkt_handler(struct nicvf *nic, + struct cmp_queue *cq, + void *cq_desc, int cqe_type) +{ + struct cqe_send_t *cqe_tx; + struct snd_queue *sq; + struct sq_hdr_subdesc *hdr; + + cqe_tx = (struct cqe_send_t *)cq_desc; + sq = &nic->qs->sq[cqe_tx->sq_idx]; + + hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, cqe_tx->sqe_ptr); + if (hdr->subdesc_type != SQ_DESC_TYPE_HEADER) + return; + + nicvf_check_cqe_tx_errs(nic, cq, cq_desc); + nicvf_put_sq_desc(sq, hdr->subdesc_cnt + 1); +} + +static int nicvf_rcv_pkt_handler(struct nicvf *nic, + struct cmp_queue *cq, void *cq_desc, + void **ppkt, int cqe_type) +{ + void *pkt; + + size_t pkt_len; + struct cqe_rx_t *cqe_rx = (struct cqe_rx_t *)cq_desc; + int err = 0; + + /* Check for errors */ + err = nicvf_check_cqe_rx_errs(nic, cq, cq_desc); + if (err && !cqe_rx->rb_cnt) + return -1; + + pkt = nicvf_get_rcv_pkt(nic, cq_desc, &pkt_len); + if (!pkt) { + debug("Packet not received\n"); + return -1; + } + + if (pkt) + *ppkt = pkt; + + return pkt_len; +} + +int nicvf_cq_handler(struct nicvf *nic, void **ppkt, int *pkt_len) +{ + int cq_qnum = 0; + int processed_sq_cqe = 0; + int processed_rq_cqe = 0; + int processed_cqe = 0; + + unsigned long cqe_count, cqe_head; + struct queue_set *qs = nic->qs; + struct cmp_queue *cq = &qs->cq[cq_qnum]; + struct cqe_rx_t *cq_desc; + + /* Get num of valid CQ entries expect next one to be SQ completion */ + cqe_count = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_STATUS, cq_qnum); + cqe_count &= 0xFFFF; + if (!cqe_count) + return 0; + + /* Get head of the valid CQ entries */ + cqe_head = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_HEAD, cq_qnum); + cqe_head >>= 9; + cqe_head &= 0xFFFF; + + if (cqe_count) { + /* Get the CQ descriptor */ + cq_desc = (struct cqe_rx_t *)GET_CQ_DESC(cq, cqe_head); + cqe_head++; + cqe_head &= (cq->dmem.q_len - 1); + /* Initiate prefetch for next descriptor */ + prefetch((struct cqe_rx_t *)GET_CQ_DESC(cq, cqe_head)); + + switch (cq_desc->cqe_type) { + case CQE_TYPE_RX: + debug("%s: Got Rx CQE\n", nic->dev->name); + *pkt_len = nicvf_rcv_pkt_handler(nic, cq, cq_desc, + ppkt, CQE_TYPE_RX); + processed_rq_cqe++; + break; + case CQE_TYPE_SEND: + debug("%s: Got Tx CQE\n", nic->dev->name); + nicvf_snd_pkt_handler(nic, cq, cq_desc, CQE_TYPE_SEND); + processed_sq_cqe++; + break; + default: + debug("%s: Got CQ type %u\n", nic->dev->name, + cq_desc->cqe_type); + break; + } + processed_cqe++; + } + + /* Dequeue CQE */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_DOOR, + cq_qnum, processed_cqe); + + asm volatile ("dsb sy"); + + return (processed_sq_cqe | processed_rq_cqe); +} + +/* Qset error interrupt handler + * + * As of now only CQ errors are handled + */ +void nicvf_handle_qs_err(struct nicvf *nic) +{ + struct queue_set *qs = nic->qs; + int qidx; + u64 status; + + /* Check if it is CQ err */ + for (qidx = 0; qidx < qs->cq_cnt; qidx++) { + status = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_STATUS, + qidx); + if (!(status & CQ_ERR_MASK)) + continue; + /* Process already queued CQEs and reconfig CQ */ + nicvf_sq_disable(nic, qidx); + nicvf_cmp_queue_config(nic, qs, qidx, true); + nicvf_sq_free_used_descs(nic->dev, &qs->sq[qidx], qidx); + nicvf_sq_enable(nic, &qs->sq[qidx], qidx); + } +} + +static int nicvf_free_pkt(struct udevice *dev, uchar *pkt, int pkt_len) +{ + struct nicvf *nic = dev_get_priv(dev); + + if (pkt && pkt_len) + free(pkt); + nicvf_refill_rbdr(nic); + return 0; +} + +static int nicvf_xmit(struct udevice *dev, void *pkt, int pkt_len) +{ + struct nicvf *nic = dev_get_priv(dev); + int ret = 0; + int rcv_len = 0; + unsigned int timeout = 5000; + void *rpkt = NULL; + + if (!nicvf_sq_append_pkt(nic, pkt, pkt_len)) { + printf("VF%d: TX ring full\n", nic->vf_id); + return -1; + } + + /* check and update CQ for pkt sent */ + while (!ret && timeout--) { + ret = nicvf_cq_handler(nic, &rpkt, &rcv_len); + if (!ret) { + debug("%s: %d, Not sent\n", __func__, __LINE__); + udelay(10); + } + } + + return 0; +} + +static int nicvf_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct nicvf *nic = dev_get_priv(dev); + void *pkt; + int pkt_len = 0; +#ifdef DEBUG + u8 *dpkt; + int i, j; +#endif + + nicvf_cq_handler(nic, &pkt, &pkt_len); + + if (pkt_len) { +#ifdef DEBUG + dpkt = pkt; + printf("RX packet contents:\n"); + for (i = 0; i < 8; i++) { + puts("\t"); + for (j = 0; j < 10; j++) + printf("%02x ", dpkt[i * 10 + j]); + puts("\n"); + } +#endif + *packetp = pkt; + } + + return pkt_len; +} + +void nicvf_stop(struct udevice *dev) +{ + struct nicvf *nic = dev_get_priv(dev); + + if (!nic->open) + return; + + /* Free resources */ + nicvf_config_data_transfer(nic, false); + + /* Disable HW Qset */ + nicvf_qset_config(nic, false); + + nic->open = false; +} + +int nicvf_open(struct udevice *dev) +{ + int err; + struct nicvf *nic = dev_get_priv(dev); + + nicvf_hw_set_mac_addr(nic, dev); + + /* Configure CPI alorithm */ + nic->cpi_alg = CPI_ALG_NONE; + nicvf_config_cpi(nic); + + /* Initialize the queues */ + err = nicvf_init_resources(nic); + if (err) + return -1; + + if (!nicvf_check_pf_ready(nic)) + return -1; + + nic->open = true; + + /* Make sure queue initialization is written */ + asm volatile("dsb sy"); + + return 0; +} + +int nicvf_write_hwaddr(struct udevice *dev) +{ + unsigned char ethaddr[ARP_HLEN]; + struct eth_pdata *pdata = dev_get_platdata(dev); + struct nicvf *nic = dev_get_priv(dev); + + /* If lower level firmware fails to set proper MAC + * u-boot framework updates MAC to random address. + * Use this hook to update mac address in environment. + */ + if (!eth_env_get_enetaddr_by_index("eth", dev->seq, ethaddr)) { + eth_env_set_enetaddr_by_index("eth", dev->seq, pdata->enetaddr); + debug("%s: pMAC %pM\n", __func__, pdata->enetaddr); + } + eth_env_get_enetaddr_by_index("eth", dev->seq, ethaddr); + if (memcmp(ethaddr, pdata->enetaddr, ARP_HLEN)) { + debug("%s: pMAC %pM\n", __func__, pdata->enetaddr); + nicvf_hw_set_mac_addr(nic, dev); + } + return 0; +} + +static void nicvf_probe_mdio_devices(void) +{ + struct udevice *pdev; + int err; + static int probed; + + if (probed) + return; + + err = dm_pci_find_device(PCI_VENDOR_ID_CAVIUM, + PCI_DEVICE_ID_CAVIUM_SMI, 0, + &pdev); + if (err) + debug("%s couldn't find SMI device\n", __func__); + probed = 1; +} + +int nicvf_initialize(struct udevice *dev) +{ + struct nicvf *nicvf = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + int ret = 0, bgx, lmac; + char name[16]; + unsigned char ethaddr[ARP_HLEN]; + struct udevice *pfdev; + struct nicpf *pf; + static int vfid; + + if (dm_pci_find_device(PCI_VENDOR_ID_CAVIUM, + PCI_DEVICE_ID_CAVIUM_NIC, 0, &pfdev)) { + printf("%s NIC PF device not found..VF probe failed\n", + __func__); + return -1; + } + pf = dev_get_priv(pfdev); + nicvf->vf_id = vfid++; + nicvf->dev = dev; + nicvf->nicpf = pf; + + nicvf_probe_mdio_devices(); + + /* Enable TSO support */ + nicvf->hw_tso = true; + + nicvf->reg_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, + PCI_REGION_MEM); + + debug("nicvf->reg_base: %p\n", nicvf->reg_base); + + if (!nicvf->reg_base) { + printf("Cannot map config register space, aborting\n"); + ret = -1; + goto fail; + } + + ret = nicvf_set_qset_resources(nicvf); + if (ret) + return -1; + + sprintf(name, "vnic%u", nicvf->vf_id); + debug("%s name %s\n", __func__, name); + device_set_name(dev, name); + + bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(pf->vf_lmac_map[nicvf->vf_id]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(pf->vf_lmac_map[nicvf->vf_id]); + debug("%s VF %d BGX %d LMAC %d\n", __func__, nicvf->vf_id, bgx, lmac); + debug("%s PF %p pfdev %p VF %p vfdev %p vf->pdata %p\n", + __func__, nicvf->nicpf, nicvf->nicpf->udev, nicvf, nicvf->dev, + pdata); + + fdt_board_get_ethaddr(bgx, lmac, ethaddr); + + debug("%s bgx %d lmac %d ethaddr %pM\n", __func__, bgx, lmac, ethaddr); + + if (is_valid_ethaddr(ethaddr)) { + memcpy(pdata->enetaddr, ethaddr, ARP_HLEN); + eth_env_set_enetaddr_by_index("eth", dev->seq, ethaddr); + } + debug("%s enetaddr %pM ethaddr %pM\n", __func__, + pdata->enetaddr, ethaddr); + +fail: + return ret; +} + +int octeontx_vnic_probe(struct udevice *dev) +{ + return nicvf_initialize(dev); +} + +static const struct eth_ops octeontx_vnic_ops = { + .start = nicvf_open, + .stop = nicvf_stop, + .send = nicvf_xmit, + .recv = nicvf_recv, + .free_pkt = nicvf_free_pkt, + .write_hwaddr = nicvf_write_hwaddr, +}; + +U_BOOT_DRIVER(octeontx_vnic) = { + .name = "vnic", + .id = UCLASS_ETH, + .probe = octeontx_vnic_probe, + .ops = &octeontx_vnic_ops, + .priv_auto_alloc_size = sizeof(struct nicvf), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), +}; + +static struct pci_device_id octeontx_vnic_supported[] = { + { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_NICVF) }, + { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_NICVF_1) }, + {} +}; + +U_BOOT_PCI_DEVICE(octeontx_vnic, octeontx_vnic_supported); diff --git a/drivers/net/octeontx/nicvf_queues.c b/drivers/net/octeontx/nicvf_queues.c new file mode 100644 index 0000000000..c7f262f440 --- /dev/null +++ b/drivers/net/octeontx/nicvf_queues.c @@ -0,0 +1,1140 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell International Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#include "nic_reg.h" +#include "nic.h" +#include "q_struct.h" +#include "nicvf_queues.h" + +static int nicvf_poll_reg(struct nicvf *nic, int qidx, + u64 reg, int bit_pos, int bits, int val) +{ + u64 bit_mask; + u64 reg_val; + int timeout = 10; + + bit_mask = (1ULL << bits) - 1; + bit_mask = (bit_mask << bit_pos); + + while (timeout) { + reg_val = nicvf_queue_reg_read(nic, reg, qidx); + if (((reg_val & bit_mask) >> bit_pos) == val) + return 0; + udelay(2000); + timeout--; + } + printf("Poll on reg 0x%llx failed\n", reg); + return 1; +} + +static int nicvf_alloc_q_desc_mem(struct nicvf *nic, struct q_desc_mem *dmem, + int q_len, int desc_size, int align_bytes) +{ + dmem->q_len = q_len; + dmem->size = (desc_size * q_len) + align_bytes; + /* Save address, need it while freeing */ + dmem->unalign_base = calloc(1, dmem->size); + dmem->dma = (uintptr_t)dmem->unalign_base; + + if (!dmem->unalign_base) + return -1; + + /* Align memory address for 'align_bytes' */ + dmem->phys_base = NICVF_ALIGNED_ADDR((u64)dmem->dma, align_bytes); + dmem->base = dmem->unalign_base + (dmem->phys_base - dmem->dma); + + return 0; +} + +static void nicvf_free_q_desc_mem(struct nicvf *nic, struct q_desc_mem *dmem) +{ + if (!dmem) + return; + + free(dmem->unalign_base); + + dmem->unalign_base = NULL; + dmem->base = NULL; +} + +static void *nicvf_rb_ptr_to_pkt(struct nicvf *nic, uintptr_t rb_ptr) +{ + return (void *)rb_ptr; +} + +static int nicvf_init_rbdr(struct nicvf *nic, struct rbdr *rbdr, + int ring_len, int buf_size) +{ + int idx; + uintptr_t rbuf; + struct rbdr_entry_t *desc; + + if (nicvf_alloc_q_desc_mem(nic, &rbdr->dmem, ring_len, + sizeof(struct rbdr_entry_t), + NICVF_RCV_BUF_ALIGN_BYTES)) { + printf("Unable to allocate memory for rcv buffer ring\n"); + return -1; + } + + rbdr->desc = rbdr->dmem.base; + /* Buffer size has to be in multiples of 128 bytes */ + rbdr->dma_size = buf_size; + rbdr->enable = true; + rbdr->thresh = RBDR_THRESH; + + debug("%s: %d: allocating %lld bytes for rcv buffers\n", + __func__, __LINE__, + ring_len * buf_size + NICVF_RCV_BUF_ALIGN_BYTES); + rbdr->buf_mem = (uintptr_t)calloc(1, ring_len * buf_size + + NICVF_RCV_BUF_ALIGN_BYTES); + + if (!rbdr->buf_mem) { + printf("Unable to allocate memory for rcv buffers\n"); + return -1; + } + + rbdr->buffers = NICVF_ALIGNED_ADDR(rbdr->buf_mem, + NICVF_RCV_BUF_ALIGN_BYTES); + + debug("%s: %d: rbdr->buf_mem: %lx, rbdr->buffers: %lx\n", + __func__, __LINE__, rbdr->buf_mem, rbdr->buffers); + + for (idx = 0; idx < ring_len; idx++) { + rbuf = rbdr->buffers + DMA_BUFFER_LEN * idx; + desc = GET_RBDR_DESC(rbdr, idx); + desc->buf_addr = rbuf >> NICVF_RCV_BUF_ALIGN; + flush_dcache_range((uintptr_t)desc, + (uintptr_t)desc + sizeof(desc)); + } + return 0; +} + +static void nicvf_free_rbdr(struct nicvf *nic, struct rbdr *rbdr) +{ + if (!rbdr) + return; + + rbdr->enable = false; + if (!rbdr->dmem.base) + return; + + debug("%s: %d: rbdr->buf_mem: %p\n", __func__, + __LINE__, (void *)rbdr->buf_mem); + free((void *)rbdr->buf_mem); + + /* Free RBDR ring */ + nicvf_free_q_desc_mem(nic, &rbdr->dmem); +} + +/* Refill receive buffer descriptors with new buffers. + * This runs in softirq context . + */ +void nicvf_refill_rbdr(struct nicvf *nic) +{ + struct queue_set *qs = nic->qs; + int rbdr_idx = qs->rbdr_cnt; + unsigned long qcount, head, tail, rb_cnt; + struct rbdr *rbdr; + + if (!rbdr_idx) + return; + rbdr_idx--; + rbdr = &qs->rbdr[rbdr_idx]; + /* Check if it's enabled */ + if (!rbdr->enable) { + printf("Receive queue %d is disabled\n", rbdr_idx); + return; + } + + /* check if valid descs reached or crossed threshold level */ + qcount = nicvf_queue_reg_read(nic, NIC_QSET_RBDR_0_1_STATUS0, rbdr_idx); + head = nicvf_queue_reg_read(nic, NIC_QSET_RBDR_0_1_HEAD, rbdr_idx); + tail = nicvf_queue_reg_read(nic, NIC_QSET_RBDR_0_1_TAIL, rbdr_idx); + + qcount &= 0x7FFFF; + + rb_cnt = qs->rbdr_len - qcount - 1; + + debug("%s: %d: qcount: %lu, head: %lx, tail: %lx, rb_cnt: %lu\n", + __func__, __LINE__, qcount, head, tail, rb_cnt); + + /* Notify HW */ + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_DOOR, rbdr_idx, rb_cnt); + + asm volatile ("dsb sy"); +} + +/* TBD: how to handle full packets received in CQ + * i.e conversion of buffers into SKBs + */ +static int nicvf_init_cmp_queue(struct nicvf *nic, + struct cmp_queue *cq, int q_len) +{ + if (nicvf_alloc_q_desc_mem(nic, &cq->dmem, q_len, + CMP_QUEUE_DESC_SIZE, + NICVF_CQ_BASE_ALIGN_BYTES)) { + printf("Unable to allocate memory for completion queue\n"); + return -1; + } + cq->desc = cq->dmem.base; + if (!pass1_silicon(nic->rev_id, nic->nicpf->hw->model_id)) + cq->thresh = CMP_QUEUE_CQE_THRESH; + else + cq->thresh = 0; + cq->intr_timer_thresh = CMP_QUEUE_TIMER_THRESH; + + return 0; +} + +static void nicvf_free_cmp_queue(struct nicvf *nic, struct cmp_queue *cq) +{ + if (!cq) + return; + if (!cq->dmem.base) + return; + + nicvf_free_q_desc_mem(nic, &cq->dmem); +} + +static int nicvf_init_snd_queue(struct nicvf *nic, + struct snd_queue *sq, int q_len) +{ + if (nicvf_alloc_q_desc_mem(nic, &sq->dmem, q_len, + SND_QUEUE_DESC_SIZE, + NICVF_SQ_BASE_ALIGN_BYTES)) { + printf("Unable to allocate memory for send queue\n"); + return -1; + } + + sq->desc = sq->dmem.base; + sq->skbuff = calloc(q_len, sizeof(u64)); + sq->head = 0; + sq->tail = 0; + sq->free_cnt = q_len - 1; + sq->thresh = SND_QUEUE_THRESH; + + return 0; +} + +static void nicvf_free_snd_queue(struct nicvf *nic, struct snd_queue *sq) +{ + if (!sq) + return; + if (!sq->dmem.base) + return; + + debug("%s: %d\n", __func__, __LINE__); + free(sq->skbuff); + + nicvf_free_q_desc_mem(nic, &sq->dmem); +} + +static void nicvf_reclaim_snd_queue(struct nicvf *nic, + struct queue_set *qs, int qidx) +{ + /* Disable send queue */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, 0); + /* Check if SQ is stopped */ + if (nicvf_poll_reg(nic, qidx, NIC_QSET_SQ_0_7_STATUS, 21, 1, 0x01)) + return; + /* Reset send queue */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, NICVF_SQ_RESET); +} + +static void nicvf_reclaim_rcv_queue(struct nicvf *nic, + struct queue_set *qs, int qidx) +{ + union nic_mbx mbx = {}; + + /* Make sure all packets in the pipeline are written back into mem */ + mbx.msg.msg = NIC_MBOX_MSG_RQ_SW_SYNC; + nicvf_send_msg_to_pf(nic, &mbx); +} + +static void nicvf_reclaim_cmp_queue(struct nicvf *nic, + struct queue_set *qs, int qidx) +{ + /* Disable timer threshold (doesn't get reset upon CQ reset */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG2, qidx, 0); + /* Disable completion queue */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG, qidx, 0); + /* Reset completion queue */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG, qidx, NICVF_CQ_RESET); +} + +static void nicvf_reclaim_rbdr(struct nicvf *nic, + struct rbdr *rbdr, int qidx) +{ + u64 tmp, fifo_state; + int timeout = 10; + + /* Save head and tail pointers for feeing up buffers */ + rbdr->head = nicvf_queue_reg_read(nic, + NIC_QSET_RBDR_0_1_HEAD, + qidx) >> 3; + rbdr->tail = nicvf_queue_reg_read(nic, + NIC_QSET_RBDR_0_1_TAIL, + qidx) >> 3; + + /* If RBDR FIFO is in 'FAIL' state then do a reset first + * before relaiming. + */ + fifo_state = nicvf_queue_reg_read(nic, NIC_QSET_RBDR_0_1_STATUS0, qidx); + if (((fifo_state >> 62) & 0x03) == 0x3) + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_CFG, + qidx, NICVF_RBDR_RESET); + + /* Disable RBDR */ + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_CFG, qidx, 0); + if (nicvf_poll_reg(nic, qidx, NIC_QSET_RBDR_0_1_STATUS0, 62, 2, 0x00)) + return; + while (1) { + tmp = nicvf_queue_reg_read(nic, + NIC_QSET_RBDR_0_1_PREFETCH_STATUS, + qidx); + if ((tmp & 0xFFFFFFFF) == ((tmp >> 32) & 0xFFFFFFFF)) + break; + mdelay(2000); + timeout--; + if (!timeout) { + printf("Failed polling on prefetch status\n"); + return; + } + } + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_CFG, + qidx, NICVF_RBDR_RESET); + + if (nicvf_poll_reg(nic, qidx, NIC_QSET_RBDR_0_1_STATUS0, 62, 2, 0x02)) + return; + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_CFG, qidx, 0x00); + if (nicvf_poll_reg(nic, qidx, NIC_QSET_RBDR_0_1_STATUS0, 62, 2, 0x00)) + return; +} + +/* Configures receive queue */ +static void nicvf_rcv_queue_config(struct nicvf *nic, struct queue_set *qs, + int qidx, bool enable) +{ + union nic_mbx mbx = {}; + struct rcv_queue *rq; + union { + struct rq_cfg s; + u64 u; + } rq_cfg; + + rq = &qs->rq[qidx]; + rq->enable = enable; + + /* Disable receive queue */ + nicvf_queue_reg_write(nic, NIC_QSET_RQ_0_7_CFG, qidx, 0); + + if (!rq->enable) { + nicvf_reclaim_rcv_queue(nic, qs, qidx); + return; + } + + rq->cq_qs = qs->vnic_id; + rq->cq_idx = qidx; + rq->start_rbdr_qs = qs->vnic_id; + rq->start_qs_rbdr_idx = qs->rbdr_cnt - 1; + rq->cont_rbdr_qs = qs->vnic_id; + rq->cont_qs_rbdr_idx = qs->rbdr_cnt - 1; + /* all writes of RBDR data to be loaded into L2 Cache as well*/ + rq->caching = 1; + + /* Send a mailbox msg to PF to config RQ */ + mbx.rq.msg = NIC_MBOX_MSG_RQ_CFG; + mbx.rq.qs_num = qs->vnic_id; + mbx.rq.rq_num = qidx; + mbx.rq.cfg = (rq->caching << 26) | (rq->cq_qs << 19) | + (rq->cq_idx << 16) | (rq->cont_rbdr_qs << 9) | + (rq->cont_qs_rbdr_idx << 8) | + (rq->start_rbdr_qs << 1) | (rq->start_qs_rbdr_idx); + nicvf_send_msg_to_pf(nic, &mbx); + + mbx.rq.msg = NIC_MBOX_MSG_RQ_BP_CFG; + mbx.rq.cfg = (1ULL << 63) | (1ULL << 62) | (qs->vnic_id << 0); + nicvf_send_msg_to_pf(nic, &mbx); + + /* RQ drop config + * Enable CQ drop to reserve sufficient CQEs for all tx packets + */ + mbx.rq.msg = NIC_MBOX_MSG_RQ_DROP_CFG; + mbx.rq.cfg = (1ULL << 62) | (RQ_CQ_DROP << 8); + nicvf_send_msg_to_pf(nic, &mbx); + nicvf_queue_reg_write(nic, NIC_QSET_RQ_GEN_CFG, 0, 0x00); + + /* Enable Receive queue */ + rq_cfg.s.ena = 1; + rq_cfg.s.tcp_ena = 0; + nicvf_queue_reg_write(nic, NIC_QSET_RQ_0_7_CFG, qidx, rq_cfg.u); +} + +void nicvf_cmp_queue_config(struct nicvf *nic, struct queue_set *qs, + int qidx, bool enable) +{ + struct cmp_queue *cq; + union { + u64 u; + struct cq_cfg s; + } cq_cfg; + + cq = &qs->cq[qidx]; + cq->enable = enable; + + if (!cq->enable) { + nicvf_reclaim_cmp_queue(nic, qs, qidx); + return; + } + + /* Reset completion queue */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG, qidx, NICVF_CQ_RESET); + + if (!cq->enable) + return; + + /* Set completion queue base address */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_BASE, + qidx, (u64)(cq->dmem.phys_base)); + + /* Enable Completion queue */ + cq_cfg.s.ena = 1; + cq_cfg.s.reset = 0; + cq_cfg.s.caching = 0; + cq_cfg.s.qsize = CMP_QSIZE; + cq_cfg.s.avg_con = 0; + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG, qidx, cq_cfg.u); + + /* Set threshold value for interrupt generation */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_THRESH, qidx, cq->thresh); + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG2, qidx, + cq->intr_timer_thresh); +} + +/* Configures transmit queue */ +static void nicvf_snd_queue_config(struct nicvf *nic, struct queue_set *qs, + int qidx, bool enable) +{ + union nic_mbx mbx = {}; + struct snd_queue *sq; + + union { + struct sq_cfg s; + u64 u; + } sq_cfg; + + sq = &qs->sq[qidx]; + sq->enable = enable; + + if (!sq->enable) { + nicvf_reclaim_snd_queue(nic, qs, qidx); + return; + } + + /* Reset send queue */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, NICVF_SQ_RESET); + + sq->cq_qs = qs->vnic_id; + sq->cq_idx = qidx; + + /* Send a mailbox msg to PF to config SQ */ + mbx.sq.msg = NIC_MBOX_MSG_SQ_CFG; + mbx.sq.qs_num = qs->vnic_id; + mbx.sq.sq_num = qidx; + mbx.sq.sqs_mode = nic->sqs_mode; + mbx.sq.cfg = (sq->cq_qs << 3) | sq->cq_idx; + nicvf_send_msg_to_pf(nic, &mbx); + + /* Set queue base address */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_BASE, + qidx, (u64)(sq->dmem.phys_base)); + + /* Enable send queue & set queue size */ + sq_cfg.s.ena = 1; + sq_cfg.s.reset = 0; + sq_cfg.s.ldwb = 0; + sq_cfg.s.qsize = SND_QSIZE; + sq_cfg.s.tstmp_bgx_intf = 0; + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, sq_cfg.u); + + /* Set threshold value for interrupt generation */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_THRESH, qidx, sq->thresh); +} + +/* Configures receive buffer descriptor ring */ +static void nicvf_rbdr_config(struct nicvf *nic, struct queue_set *qs, + int qidx, bool enable) +{ + struct rbdr *rbdr; + union { + struct rbdr_cfg s; + u64 u; + } rbdr_cfg; + + rbdr = &qs->rbdr[qidx]; + nicvf_reclaim_rbdr(nic, rbdr, qidx); + if (!enable) + return; + + /* Set descriptor base address */ + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_BASE, + qidx, (u64)(rbdr->dmem.phys_base)); + + /* Enable RBDR & set queue size */ + /* Buffer size should be in multiples of 128 bytes */ + rbdr_cfg.s.ena = 1; + rbdr_cfg.s.reset = 0; + rbdr_cfg.s.ldwb = 0; + rbdr_cfg.s.qsize = RBDR_SIZE; + rbdr_cfg.s.avg_con = 0; + rbdr_cfg.s.lines = rbdr->dma_size / 128; + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_CFG, + qidx, rbdr_cfg.u); + + /* Notify HW */ + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_DOOR, + qidx, qs->rbdr_len - 1); + + /* Set threshold value for interrupt generation */ + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_THRESH, + qidx, rbdr->thresh - 1); +} + +/* Requests PF to assign and enable Qset */ +void nicvf_qset_config(struct nicvf *nic, bool enable) +{ + union nic_mbx mbx = {}; + struct queue_set *qs = nic->qs; + struct qs_cfg *qs_cfg; + + if (!qs) { + printf("Qset is still not allocated, don't init queues\n"); + return; + } + + qs->enable = enable; + qs->vnic_id = nic->vf_id; + + /* Send a mailbox msg to PF to config Qset */ + mbx.qs.msg = NIC_MBOX_MSG_QS_CFG; + mbx.qs.num = qs->vnic_id; +#ifdef VNIC_MULTI_QSET_SUPPORT + mbx.qs.sqs_count = nic->sqs_count; +#endif + + mbx.qs.cfg = 0; + qs_cfg = (struct qs_cfg *)&mbx.qs.cfg; + if (qs->enable) { + qs_cfg->ena = 1; +#ifdef __BIG_ENDIAN + qs_cfg->be = 1; +#endif + qs_cfg->vnic = qs->vnic_id; + } + nicvf_send_msg_to_pf(nic, &mbx); +} + +static void nicvf_free_resources(struct nicvf *nic) +{ + int qidx; + struct queue_set *qs = nic->qs; + + /* Free receive buffer descriptor ring */ + for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) + nicvf_free_rbdr(nic, &qs->rbdr[qidx]); + + /* Free completion queue */ + for (qidx = 0; qidx < qs->cq_cnt; qidx++) + nicvf_free_cmp_queue(nic, &qs->cq[qidx]); + + /* Free send queue */ + for (qidx = 0; qidx < qs->sq_cnt; qidx++) + nicvf_free_snd_queue(nic, &qs->sq[qidx]); +} + +static int nicvf_alloc_resources(struct nicvf *nic) +{ + int qidx; + struct queue_set *qs = nic->qs; + + /* Alloc receive buffer descriptor ring */ + for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) { + if (nicvf_init_rbdr(nic, &qs->rbdr[qidx], qs->rbdr_len, + DMA_BUFFER_LEN)) + goto alloc_fail; + } + + /* Alloc send queue */ + for (qidx = 0; qidx < qs->sq_cnt; qidx++) { + if (nicvf_init_snd_queue(nic, &qs->sq[qidx], qs->sq_len)) + goto alloc_fail; + } + + /* Alloc completion queue */ + for (qidx = 0; qidx < qs->cq_cnt; qidx++) { + if (nicvf_init_cmp_queue(nic, &qs->cq[qidx], qs->cq_len)) + goto alloc_fail; + } + + return 0; +alloc_fail: + nicvf_free_resources(nic); + return -1; +} + +int nicvf_set_qset_resources(struct nicvf *nic) +{ + struct queue_set *qs; + + qs = calloc(1, sizeof(struct queue_set)); + if (!qs) + return -1; + nic->qs = qs; + + /* Set count of each queue */ + qs->rbdr_cnt = RBDR_CNT; + qs->rq_cnt = 1; + qs->sq_cnt = SND_QUEUE_CNT; + qs->cq_cnt = CMP_QUEUE_CNT; + + /* Set queue lengths */ + qs->rbdr_len = RCV_BUF_COUNT; + qs->sq_len = SND_QUEUE_LEN; + qs->cq_len = CMP_QUEUE_LEN; + + nic->rx_queues = qs->rq_cnt; + nic->tx_queues = qs->sq_cnt; + + return 0; +} + +int nicvf_config_data_transfer(struct nicvf *nic, bool enable) +{ + bool disable = false; + struct queue_set *qs = nic->qs; + int qidx; + + if (!qs) + return 0; + + if (enable) { + if (nicvf_alloc_resources(nic)) + return -1; + + for (qidx = 0; qidx < qs->sq_cnt; qidx++) + nicvf_snd_queue_config(nic, qs, qidx, enable); + for (qidx = 0; qidx < qs->cq_cnt; qidx++) + nicvf_cmp_queue_config(nic, qs, qidx, enable); + for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) + nicvf_rbdr_config(nic, qs, qidx, enable); + for (qidx = 0; qidx < qs->rq_cnt; qidx++) + nicvf_rcv_queue_config(nic, qs, qidx, enable); + } else { + for (qidx = 0; qidx < qs->rq_cnt; qidx++) + nicvf_rcv_queue_config(nic, qs, qidx, disable); + for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) + nicvf_rbdr_config(nic, qs, qidx, disable); + for (qidx = 0; qidx < qs->sq_cnt; qidx++) + nicvf_snd_queue_config(nic, qs, qidx, disable); + for (qidx = 0; qidx < qs->cq_cnt; qidx++) + nicvf_cmp_queue_config(nic, qs, qidx, disable); + + nicvf_free_resources(nic); + } + + return 0; +} + +/* Get a free desc from SQ + * returns descriptor ponter & descriptor number + */ +static int nicvf_get_sq_desc(struct snd_queue *sq, int desc_cnt) +{ + int qentry; + + qentry = sq->tail; + sq->free_cnt -= desc_cnt; + sq->tail += desc_cnt; + sq->tail &= (sq->dmem.q_len - 1); + + return qentry; +} + +/* Free descriptor back to SQ for future use */ +void nicvf_put_sq_desc(struct snd_queue *sq, int desc_cnt) +{ + sq->free_cnt += desc_cnt; + sq->head += desc_cnt; + sq->head &= (sq->dmem.q_len - 1); +} + +static int nicvf_get_nxt_sqentry(struct snd_queue *sq, int qentry) +{ + qentry++; + qentry &= (sq->dmem.q_len - 1); + return qentry; +} + +void nicvf_sq_enable(struct nicvf *nic, struct snd_queue *sq, int qidx) +{ + u64 sq_cfg; + + sq_cfg = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_CFG, qidx); + sq_cfg |= NICVF_SQ_EN; + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, sq_cfg); + /* Ring doorbell so that H/W restarts processing SQEs */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_DOOR, qidx, 0); +} + +void nicvf_sq_disable(struct nicvf *nic, int qidx) +{ + u64 sq_cfg; + + sq_cfg = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_CFG, qidx); + sq_cfg &= ~NICVF_SQ_EN; + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, sq_cfg); +} + +void nicvf_sq_free_used_descs(struct udevice *dev, struct snd_queue *sq, + int qidx) +{ + u64 head; + struct nicvf *nic = dev_get_priv(dev); + struct sq_hdr_subdesc *hdr; + + head = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_HEAD, qidx) >> 4; + + while (sq->head != head) { + hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, sq->head); + if (hdr->subdesc_type != SQ_DESC_TYPE_HEADER) { + nicvf_put_sq_desc(sq, 1); + continue; + } + nicvf_put_sq_desc(sq, hdr->subdesc_cnt + 1); + } +} + +/* Get the number of SQ descriptors needed to xmit this skb */ +static int nicvf_sq_subdesc_required(struct nicvf *nic) +{ + int subdesc_cnt = MIN_SQ_DESC_PER_PKT_XMIT; + + return subdesc_cnt; +} + +/* Add SQ HEADER subdescriptor. + * First subdescriptor for every send descriptor. + */ +static inline void +nicvf_sq_add_hdr_subdesc(struct nicvf *nic, struct snd_queue *sq, int qentry, + int subdesc_cnt, void *pkt, size_t pkt_len) +{ + struct sq_hdr_subdesc *hdr; + + hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, qentry); + sq->skbuff[qentry] = (uintptr_t)pkt; + + memset(hdr, 0, SND_QUEUE_DESC_SIZE); + hdr->subdesc_type = SQ_DESC_TYPE_HEADER; + /* Enable notification via CQE after processing SQE */ + hdr->post_cqe = 1; + /* No of subdescriptors following this */ + hdr->subdesc_cnt = subdesc_cnt; + hdr->tot_len = pkt_len; + + flush_dcache_range((uintptr_t)hdr, + (uintptr_t)hdr + sizeof(struct sq_hdr_subdesc)); +} + +/* SQ GATHER subdescriptor + * Must follow HDR descriptor + */ +static inline void nicvf_sq_add_gather_subdesc(struct snd_queue *sq, int qentry, + size_t size, uintptr_t data) +{ + struct sq_gather_subdesc *gather; + + qentry &= (sq->dmem.q_len - 1); + gather = (struct sq_gather_subdesc *)GET_SQ_DESC(sq, qentry); + + memset(gather, 0, SND_QUEUE_DESC_SIZE); + gather->subdesc_type = SQ_DESC_TYPE_GATHER; + gather->ld_type = NIC_SEND_LD_TYPE_E_LDD; + gather->size = size; + gather->addr = data; + + flush_dcache_range((uintptr_t)gather, + (uintptr_t)gather + sizeof(struct sq_hdr_subdesc)); +} + +/* Append an skb to a SQ for packet transfer. */ +int nicvf_sq_append_pkt(struct nicvf *nic, void *pkt, size_t pkt_size) +{ + int subdesc_cnt; + int sq_num = 0, qentry; + struct queue_set *qs; + struct snd_queue *sq; + + qs = nic->qs; + sq = &qs->sq[sq_num]; + + subdesc_cnt = nicvf_sq_subdesc_required(nic); + if (subdesc_cnt > sq->free_cnt) + goto append_fail; + + qentry = nicvf_get_sq_desc(sq, subdesc_cnt); + + /* Add SQ header subdesc */ + nicvf_sq_add_hdr_subdesc(nic, sq, qentry, subdesc_cnt - 1, + pkt, pkt_size); + + /* Add SQ gather subdescs */ + qentry = nicvf_get_nxt_sqentry(sq, qentry); + nicvf_sq_add_gather_subdesc(sq, qentry, pkt_size, (uintptr_t)(pkt)); + + flush_dcache_range((uintptr_t)pkt, + (uintptr_t)pkt + pkt_size); + + /* make sure all memory stores are done before ringing doorbell */ + asm volatile ("dsb sy"); + + /* Inform HW to xmit new packet */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_DOOR, + sq_num, subdesc_cnt); + return 1; + +append_fail: + printf("Not enough SQ descriptors to xmit pkt\n"); + return 0; +} + +static unsigned int frag_num(unsigned int i) +{ +#ifdef __BIG_ENDIAN + return (i & ~3) + 3 - (i & 3); +#else + return i; +#endif +} + +void *nicvf_get_rcv_pkt(struct nicvf *nic, void *cq_desc, size_t *pkt_len) +{ + int frag; + int payload_len = 0, tot_len; + void *pkt = NULL, *pkt_buf = NULL, *buffer; + struct cqe_rx_t *cqe_rx; + struct rbdr *rbdr; + struct rcv_queue *rq; + struct queue_set *qs = nic->qs; + u16 *rb_lens = NULL; + u64 *rb_ptrs = NULL; + + cqe_rx = (struct cqe_rx_t *)cq_desc; + + rq = &qs->rq[cqe_rx->rq_idx]; + rbdr = &qs->rbdr[rq->start_qs_rbdr_idx]; + rb_lens = cq_desc + (3 * sizeof(u64)); /* Use offsetof */ + /* Except 88xx pass1 on all other chips CQE_RX2_S is added to + * CQE_RX at word6, hence buffer pointers move by word + * + * Use existing 'hw_tso' flag which will be set for all chips + * except 88xx pass1 instead of a additional cache line + * access (or miss) by using pci dev's revision. + */ + if (!nic->hw_tso) + rb_ptrs = (void *)cqe_rx + (6 * sizeof(u64)); + else + rb_ptrs = (void *)cqe_rx + (7 * sizeof(u64)); + + /* + * Figure out packet length to create packet buffer + */ + for (frag = 0; frag < cqe_rx->rb_cnt; frag++) + payload_len += rb_lens[frag_num(frag)]; + *pkt_len = payload_len; + /* round up size to 8 byte multiple */ + tot_len = (payload_len & (~0x7)) + 8; + buffer = calloc(1, tot_len); + if (!buffer) { + printf("%s - Failed to allocate packet buffer\n", __func__); + return NULL; + } + pkt_buf = buffer; + debug("total pkt buf %p len %ld tot_len %d\n", pkt_buf, *pkt_len, + tot_len); + for (frag = 0; frag < cqe_rx->rb_cnt; frag++) { + payload_len = rb_lens[frag_num(frag)]; + + invalidate_dcache_range((uintptr_t)(*rb_ptrs), + (uintptr_t)(*rb_ptrs) + rbdr->dma_size); + + /* First fragment */ + *rb_ptrs = *rb_ptrs - cqe_rx->align_pad; + + pkt = nicvf_rb_ptr_to_pkt(nic, *rb_ptrs); + + invalidate_dcache_range((uintptr_t)pkt, + (uintptr_t)pkt + payload_len); + + if (cqe_rx->align_pad) + pkt += cqe_rx->align_pad; + debug("pkt_buf %p, pkt %p payload_len %d\n", pkt_buf, pkt, + payload_len); + memcpy(buffer, pkt, payload_len); + buffer += payload_len; + /* Next buffer pointer */ + rb_ptrs++; + } + return pkt_buf; +} + +/* Clear interrupt */ +void nicvf_clear_intr(struct nicvf *nic, int int_type, int q_idx) +{ + u64 reg_val = 0; + + switch (int_type) { + case NICVF_INTR_CQ: + reg_val = ((1ULL << q_idx) << NICVF_INTR_CQ_SHIFT); + break; + case NICVF_INTR_SQ: + reg_val = ((1ULL << q_idx) << NICVF_INTR_SQ_SHIFT); + break; + case NICVF_INTR_RBDR: + reg_val = ((1ULL << q_idx) << NICVF_INTR_RBDR_SHIFT); + break; + case NICVF_INTR_PKT_DROP: + reg_val = (1ULL << NICVF_INTR_PKT_DROP_SHIFT); + break; + case NICVF_INTR_TCP_TIMER: + reg_val = (1ULL << NICVF_INTR_TCP_TIMER_SHIFT); + break; + case NICVF_INTR_MBOX: + reg_val = (1ULL << NICVF_INTR_MBOX_SHIFT); + break; + case NICVF_INTR_QS_ERR: + reg_val |= (1ULL << NICVF_INTR_QS_ERR_SHIFT); + break; + default: + printf("Failed to clear interrupt: unknown type\n"); + break; + } + + nicvf_reg_write(nic, NIC_VF_INT, reg_val); +} + +void nicvf_update_rq_stats(struct nicvf *nic, int rq_idx) +{ + struct rcv_queue *rq; + +#define GET_RQ_STATS(reg) \ + nicvf_reg_read(nic, NIC_QSET_RQ_0_7_STAT_0_1 |\ + (rq_idx << NIC_Q_NUM_SHIFT) | ((reg) << 3)) + + rq = &nic->qs->rq[rq_idx]; + rq->stats.bytes = GET_RQ_STATS(RQ_SQ_STATS_OCTS); + rq->stats.pkts = GET_RQ_STATS(RQ_SQ_STATS_PKTS); +} + +void nicvf_update_sq_stats(struct nicvf *nic, int sq_idx) +{ + struct snd_queue *sq; + +#define GET_SQ_STATS(reg) \ + nicvf_reg_read(nic, NIC_QSET_SQ_0_7_STAT_0_1 |\ + (sq_idx << NIC_Q_NUM_SHIFT) | ((reg) << 3)) + + sq = &nic->qs->sq[sq_idx]; + sq->stats.bytes = GET_SQ_STATS(RQ_SQ_STATS_OCTS); + sq->stats.pkts = GET_SQ_STATS(RQ_SQ_STATS_PKTS); +} + +/* Check for errors in the receive cmp.queue entry */ +int nicvf_check_cqe_rx_errs(struct nicvf *nic, + struct cmp_queue *cq, void *cq_desc) +{ + struct cqe_rx_t *cqe_rx; + struct cmp_queue_stats *stats = &cq->stats; + + cqe_rx = (struct cqe_rx_t *)cq_desc; + if (!cqe_rx->err_level && !cqe_rx->err_opcode) { + stats->rx.errop.good++; + return 0; + } + + switch (cqe_rx->err_level) { + case CQ_ERRLVL_MAC: + stats->rx.errlvl.mac_errs++; + break; + case CQ_ERRLVL_L2: + stats->rx.errlvl.l2_errs++; + break; + case CQ_ERRLVL_L3: + stats->rx.errlvl.l3_errs++; + break; + case CQ_ERRLVL_L4: + stats->rx.errlvl.l4_errs++; + break; + } + + switch (cqe_rx->err_opcode) { + case CQ_RX_ERROP_RE_PARTIAL: + stats->rx.errop.partial_pkts++; + break; + case CQ_RX_ERROP_RE_JABBER: + stats->rx.errop.jabber_errs++; + break; + case CQ_RX_ERROP_RE_FCS: + stats->rx.errop.fcs_errs++; + break; + case CQ_RX_ERROP_RE_TERMINATE: + stats->rx.errop.terminate_errs++; + break; + case CQ_RX_ERROP_RE_RX_CTL: + stats->rx.errop.bgx_rx_errs++; + break; + case CQ_RX_ERROP_PREL2_ERR: + stats->rx.errop.prel2_errs++; + break; + case CQ_RX_ERROP_L2_FRAGMENT: + stats->rx.errop.l2_frags++; + break; + case CQ_RX_ERROP_L2_OVERRUN: + stats->rx.errop.l2_overruns++; + break; + case CQ_RX_ERROP_L2_PFCS: + stats->rx.errop.l2_pfcs++; + break; + case CQ_RX_ERROP_L2_PUNY: + stats->rx.errop.l2_puny++; + break; + case CQ_RX_ERROP_L2_MAL: + stats->rx.errop.l2_hdr_malformed++; + break; + case CQ_RX_ERROP_L2_OVERSIZE: + stats->rx.errop.l2_oversize++; + break; + case CQ_RX_ERROP_L2_UNDERSIZE: + stats->rx.errop.l2_undersize++; + break; + case CQ_RX_ERROP_L2_LENMISM: + stats->rx.errop.l2_len_mismatch++; + break; + case CQ_RX_ERROP_L2_PCLP: + stats->rx.errop.l2_pclp++; + break; + case CQ_RX_ERROP_IP_NOT: + stats->rx.errop.non_ip++; + break; + case CQ_RX_ERROP_IP_CSUM_ERR: + stats->rx.errop.ip_csum_err++; + break; + case CQ_RX_ERROP_IP_MAL: + stats->rx.errop.ip_hdr_malformed++; + break; + case CQ_RX_ERROP_IP_MALD: + stats->rx.errop.ip_payload_malformed++; + break; + case CQ_RX_ERROP_IP_HOP: + stats->rx.errop.ip_hop_errs++; + break; + case CQ_RX_ERROP_L3_ICRC: + stats->rx.errop.l3_icrc_errs++; + break; + case CQ_RX_ERROP_L3_PCLP: + stats->rx.errop.l3_pclp++; + break; + case CQ_RX_ERROP_L4_MAL: + stats->rx.errop.l4_malformed++; + break; + case CQ_RX_ERROP_L4_CHK: + stats->rx.errop.l4_csum_errs++; + break; + case CQ_RX_ERROP_UDP_LEN: + stats->rx.errop.udp_len_err++; + break; + case CQ_RX_ERROP_L4_PORT: + stats->rx.errop.bad_l4_port++; + break; + case CQ_RX_ERROP_TCP_FLAG: + stats->rx.errop.bad_tcp_flag++; + break; + case CQ_RX_ERROP_TCP_OFFSET: + stats->rx.errop.tcp_offset_errs++; + break; + case CQ_RX_ERROP_L4_PCLP: + stats->rx.errop.l4_pclp++; + break; + case CQ_RX_ERROP_RBDR_TRUNC: + stats->rx.errop.pkt_truncated++; + break; + } + + return 1; +} + +/* Check for errors in the send cmp.queue entry */ +int nicvf_check_cqe_tx_errs(struct nicvf *nic, + struct cmp_queue *cq, void *cq_desc) +{ + struct cqe_send_t *cqe_tx; + struct cmp_queue_stats *stats = &cq->stats; + + cqe_tx = (struct cqe_send_t *)cq_desc; + switch (cqe_tx->send_status) { + case CQ_TX_ERROP_GOOD: + stats->tx.good++; + return 0; + break; + case CQ_TX_ERROP_DESC_FAULT: + stats->tx.desc_fault++; + break; + case CQ_TX_ERROP_HDR_CONS_ERR: + stats->tx.hdr_cons_err++; + break; + case CQ_TX_ERROP_SUBDC_ERR: + stats->tx.subdesc_err++; + break; + case CQ_TX_ERROP_IMM_SIZE_OFLOW: + stats->tx.imm_size_oflow++; + break; + case CQ_TX_ERROP_DATA_SEQUENCE_ERR: + stats->tx.data_seq_err++; + break; + case CQ_TX_ERROP_MEM_SEQUENCE_ERR: + stats->tx.mem_seq_err++; + break; + case CQ_TX_ERROP_LOCK_VIOL: + stats->tx.lock_viol++; + break; + case CQ_TX_ERROP_DATA_FAULT: + stats->tx.data_fault++; + break; + case CQ_TX_ERROP_TSTMP_CONFLICT: + stats->tx.tstmp_conflict++; + break; + case CQ_TX_ERROP_TSTMP_TIMEOUT: + stats->tx.tstmp_timeout++; + break; + case CQ_TX_ERROP_MEM_FAULT: + stats->tx.mem_fault++; + break; + case CQ_TX_ERROP_CK_OVERLAP: + stats->tx.csum_overlap++; + break; + case CQ_TX_ERROP_CK_OFLOW: + stats->tx.csum_overflow++; + break; + } + + return 1; +} diff --git a/drivers/net/octeontx/nicvf_queues.h b/drivers/net/octeontx/nicvf_queues.h new file mode 100644 index 0000000000..833b2a1dd4 --- /dev/null +++ b/drivers/net/octeontx/nicvf_queues.h @@ -0,0 +1,353 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Marvell International Ltd. + */ + +#ifndef NICVF_QUEUES_H +#define NICVF_QUEUES_H + +#include "q_struct.h" + +#define MAX_QUEUE_SET 128 +#define MAX_RCV_QUEUES_PER_QS 8 +#define MAX_RCV_BUF_DESC_RINGS_PER_QS 2 +#define MAX_SND_QUEUES_PER_QS 8 +#define MAX_CMP_QUEUES_PER_QS 8 + +/* VF's queue interrupt ranges */ +#define NICVF_INTR_ID_CQ 0 +#define NICVF_INTR_ID_SQ 8 +#define NICVF_INTR_ID_RBDR 16 +#define NICVF_INTR_ID_MISC 18 +#define NICVF_INTR_ID_QS_ERR 19 + +#define RBDR_SIZE0 0ULL /* 8K entries */ +#define RBDR_SIZE1 1ULL /* 16K entries */ +#define RBDR_SIZE2 2ULL /* 32K entries */ +#define RBDR_SIZE3 3ULL /* 64K entries */ +#define RBDR_SIZE4 4ULL /* 126K entries */ +#define RBDR_SIZE5 5ULL /* 256K entries */ +#define RBDR_SIZE6 6ULL /* 512K entries */ + +#define SND_QUEUE_SIZE0 0ULL /* 1K entries */ +#define SND_QUEUE_SIZE1 1ULL /* 2K entries */ +#define SND_QUEUE_SIZE2 2ULL /* 4K entries */ +#define SND_QUEUE_SIZE3 3ULL /* 8K entries */ +#define SND_QUEUE_SIZE4 4ULL /* 16K entries */ +#define SND_QUEUE_SIZE5 5ULL /* 32K entries */ +#define SND_QUEUE_SIZE6 6ULL /* 64K entries */ + +#define CMP_QUEUE_SIZE0 0ULL /* 1K entries */ +#define CMP_QUEUE_SIZE1 1ULL /* 2K entries */ +#define CMP_QUEUE_SIZE2 2ULL /* 4K entries */ +#define CMP_QUEUE_SIZE3 3ULL /* 8K entries */ +#define CMP_QUEUE_SIZE4 4ULL /* 16K entries */ +#define CMP_QUEUE_SIZE5 5ULL /* 32K entries */ +#define CMP_QUEUE_SIZE6 6ULL /* 64K entries */ + +/* Default queue count per QS, its lengths and threshold values */ +#define RBDR_CNT 1 +#define RCV_QUEUE_CNT 1 +#define SND_QUEUE_CNT 1 +#define CMP_QUEUE_CNT 1 /* Max of RCV and SND qcount */ + +#define SND_QSIZE SND_QUEUE_SIZE0 +#define SND_QUEUE_LEN BIT_ULL((SND_QSIZE + 10)) +#define SND_QUEUE_THRESH 2ULL +#define MIN_SQ_DESC_PER_PKT_XMIT 2 +#define MAX_CQE_PER_PKT_XMIT 2 + +#define CMP_QSIZE CMP_QUEUE_SIZE0 +#define CMP_QUEUE_LEN BIT_ULL((CMP_QSIZE + 10)) +#define CMP_QUEUE_CQE_THRESH 0 +#define CMP_QUEUE_TIMER_THRESH 1 /* 1 ms */ + +#define RBDR_SIZE RBDR_SIZE0 +#define RCV_BUF_COUNT BIT_ULL((RBDR_SIZE + 13)) +#define RBDR_THRESH (RCV_BUF_COUNT / 2) +#define DMA_BUFFER_LEN 2048 /* In multiples of 128bytes */ +#define RCV_FRAG_LEN DMA_BUFFER_LEN + +#define MAX_CQES_FOR_TX ((SND_QUEUE_LEN / MIN_SQ_DESC_PER_PKT_XMIT) *\ + MAX_CQE_PER_PKT_XMIT) +#define RQ_CQ_DROP ((CMP_QUEUE_LEN - MAX_CQES_FOR_TX) / 256) + +/* Descriptor size */ +#define SND_QUEUE_DESC_SIZE 16 /* 128 bits */ +#define CMP_QUEUE_DESC_SIZE 512 + +/* Buffer / descriptor alignments */ +#define NICVF_RCV_BUF_ALIGN 7 +#define NICVF_RCV_BUF_ALIGN_BYTES BIT_ULL(NICVF_RCV_BUF_ALIGN) +#define NICVF_CQ_BASE_ALIGN_BYTES 512 /* 9 bits */ +#define NICVF_SQ_BASE_ALIGN_BYTES 128 /* 7 bits */ + +#define NICVF_ALIGNED_ADDR(ADDR, ALIGN_BYTES) ALIGN(ADDR, ALIGN_BYTES) + +/* Queue enable/disable */ +#define NICVF_SQ_EN BIT_ULL(19) + +/* Queue reset */ +#define NICVF_CQ_RESET BIT_ULL(41) +#define NICVF_SQ_RESET BIT_ULL(17) +#define NICVF_RBDR_RESET BIT_ULL(43) + +enum CQ_RX_ERRLVL_E { + CQ_ERRLVL_MAC, + CQ_ERRLVL_L2, + CQ_ERRLVL_L3, + CQ_ERRLVL_L4, +}; + +enum CQ_RX_ERROP_E { + CQ_RX_ERROP_RE_NONE = 0x0, + CQ_RX_ERROP_RE_PARTIAL = 0x1, + CQ_RX_ERROP_RE_JABBER = 0x2, + CQ_RX_ERROP_RE_FCS = 0x7, + CQ_RX_ERROP_RE_TERMINATE = 0x9, + CQ_RX_ERROP_RE_RX_CTL = 0xb, + CQ_RX_ERROP_PREL2_ERR = 0x1f, + CQ_RX_ERROP_L2_FRAGMENT = 0x20, + CQ_RX_ERROP_L2_OVERRUN = 0x21, + CQ_RX_ERROP_L2_PFCS = 0x22, + CQ_RX_ERROP_L2_PUNY = 0x23, + CQ_RX_ERROP_L2_MAL = 0x24, + CQ_RX_ERROP_L2_OVERSIZE = 0x25, + CQ_RX_ERROP_L2_UNDERSIZE = 0x26, + CQ_RX_ERROP_L2_LENMISM = 0x27, + CQ_RX_ERROP_L2_PCLP = 0x28, + CQ_RX_ERROP_IP_NOT = 0x41, + CQ_RX_ERROP_IP_CSUM_ERR = 0x42, + CQ_RX_ERROP_IP_MAL = 0x43, + CQ_RX_ERROP_IP_MALD = 0x44, + CQ_RX_ERROP_IP_HOP = 0x45, + CQ_RX_ERROP_L3_ICRC = 0x46, + CQ_RX_ERROP_L3_PCLP = 0x47, + CQ_RX_ERROP_L4_MAL = 0x61, + CQ_RX_ERROP_L4_CHK = 0x62, + CQ_RX_ERROP_UDP_LEN = 0x63, + CQ_RX_ERROP_L4_PORT = 0x64, + CQ_RX_ERROP_TCP_FLAG = 0x65, + CQ_RX_ERROP_TCP_OFFSET = 0x66, + CQ_RX_ERROP_L4_PCLP = 0x67, + CQ_RX_ERROP_RBDR_TRUNC = 0x70, +}; + +enum CQ_TX_ERROP_E { + CQ_TX_ERROP_GOOD = 0x0, + CQ_TX_ERROP_DESC_FAULT = 0x10, + CQ_TX_ERROP_HDR_CONS_ERR = 0x11, + CQ_TX_ERROP_SUBDC_ERR = 0x12, + CQ_TX_ERROP_IMM_SIZE_OFLOW = 0x80, + CQ_TX_ERROP_DATA_SEQUENCE_ERR = 0x81, + CQ_TX_ERROP_MEM_SEQUENCE_ERR = 0x82, + CQ_TX_ERROP_LOCK_VIOL = 0x83, + CQ_TX_ERROP_DATA_FAULT = 0x84, + CQ_TX_ERROP_TSTMP_CONFLICT = 0x85, + CQ_TX_ERROP_TSTMP_TIMEOUT = 0x86, + CQ_TX_ERROP_MEM_FAULT = 0x87, + CQ_TX_ERROP_CK_OVERLAP = 0x88, + CQ_TX_ERROP_CK_OFLOW = 0x89, + CQ_TX_ERROP_ENUM_LAST = 0x8a, +}; + +struct cmp_queue_stats { + struct rx_stats { + struct { + u64 mac_errs; + u64 l2_errs; + u64 l3_errs; + u64 l4_errs; + } errlvl; + struct { + u64 good; + u64 partial_pkts; + u64 jabber_errs; + u64 fcs_errs; + u64 terminate_errs; + u64 bgx_rx_errs; + u64 prel2_errs; + u64 l2_frags; + u64 l2_overruns; + u64 l2_pfcs; + u64 l2_puny; + u64 l2_hdr_malformed; + u64 l2_oversize; + u64 l2_undersize; + u64 l2_len_mismatch; + u64 l2_pclp; + u64 non_ip; + u64 ip_csum_err; + u64 ip_hdr_malformed; + u64 ip_payload_malformed; + u64 ip_hop_errs; + u64 l3_icrc_errs; + u64 l3_pclp; + u64 l4_malformed; + u64 l4_csum_errs; + u64 udp_len_err; + u64 bad_l4_port; + u64 bad_tcp_flag; + u64 tcp_offset_errs; + u64 l4_pclp; + u64 pkt_truncated; + } errop; + } rx; + struct tx_stats { + u64 good; + u64 desc_fault; + u64 hdr_cons_err; + u64 subdesc_err; + u64 imm_size_oflow; + u64 data_seq_err; + u64 mem_seq_err; + u64 lock_viol; + u64 data_fault; + u64 tstmp_conflict; + u64 tstmp_timeout; + u64 mem_fault; + u64 csum_overlap; + u64 csum_overflow; + } tx; +}; + +enum RQ_SQ_STATS { + RQ_SQ_STATS_OCTS, + RQ_SQ_STATS_PKTS, +}; + +struct rx_tx_queue_stats { + u64 bytes; + u64 pkts; +}; + +struct q_desc_mem { + uintptr_t dma; + u64 size; + u16 q_len; + uintptr_t phys_base; + void *base; + void *unalign_base; + bool allocated; +}; + +struct rbdr { + bool enable; + u32 dma_size; + u32 thresh; /* Threshold level for interrupt */ + void *desc; + u32 head; + u32 tail; + struct q_desc_mem dmem; + uintptr_t buf_mem; + uintptr_t buffers; +}; + +struct rcv_queue { + bool enable; + struct rbdr *rbdr_start; + struct rbdr *rbdr_cont; + bool en_tcp_reassembly; + u8 cq_qs; /* CQ's QS to which this RQ is assigned */ + u8 cq_idx; /* CQ index (0 to 7) in the QS */ + u8 cont_rbdr_qs; /* Continue buffer ptrs - QS num */ + u8 cont_qs_rbdr_idx; /* RBDR idx in the cont QS */ + u8 start_rbdr_qs; /* First buffer ptrs - QS num */ + u8 start_qs_rbdr_idx; /* RBDR idx in the above QS */ + u8 caching; + struct rx_tx_queue_stats stats; +}; + +struct cmp_queue { + bool enable; + u16 intr_timer_thresh; + u16 thresh; + void *desc; + struct q_desc_mem dmem; + struct cmp_queue_stats stats; +}; + +struct snd_queue { + bool enable; + u8 cq_qs; /* CQ's QS to which this SQ is pointing */ + u8 cq_idx; /* CQ index (0 to 7) in the above QS */ + u16 thresh; + u32 free_cnt; + u32 head; + u32 tail; + u64 *skbuff; + void *desc; + struct q_desc_mem dmem; + struct rx_tx_queue_stats stats; +}; + +struct queue_set { + bool enable; + bool be_en; + u8 vnic_id; + u8 rq_cnt; + u8 cq_cnt; + u64 cq_len; + u8 sq_cnt; + u64 sq_len; + u8 rbdr_cnt; + u64 rbdr_len; + struct rcv_queue rq[MAX_RCV_QUEUES_PER_QS]; + struct cmp_queue cq[MAX_CMP_QUEUES_PER_QS]; + struct snd_queue sq[MAX_SND_QUEUES_PER_QS]; + struct rbdr rbdr[MAX_RCV_BUF_DESC_RINGS_PER_QS]; +}; + +#define GET_RBDR_DESC(RING, idx)\ + (&(((struct rbdr_entry_t *)((RING)->desc))[idx])) +#define GET_SQ_DESC(RING, idx)\ + (&(((struct sq_hdr_subdesc *)((RING)->desc))[idx])) +#define GET_CQ_DESC(RING, idx)\ + (&(((union cq_desc_t *)((RING)->desc))[idx])) + +/* CQ status bits */ +#define CQ_WR_FULL BIT(26) +#define CQ_WR_DISABLE BIT(25) +#define CQ_WR_FAULT BIT(24) +#define CQ_CQE_COUNT (0xFFFF << 0) + +#define CQ_ERR_MASK (CQ_WR_FULL | CQ_WR_DISABLE | CQ_WR_FAULT) + +int nicvf_set_qset_resources(struct nicvf *nic); +int nicvf_config_data_transfer(struct nicvf *nic, bool enable); +void nicvf_qset_config(struct nicvf *nic, bool enable); +void nicvf_cmp_queue_config(struct nicvf *nic, struct queue_set *qs, + int qidx, bool enable); + +void nicvf_sq_enable(struct nicvf *nic, struct snd_queue *sq, int qidx); +void nicvf_sq_disable(struct nicvf *nic, int qidx); +void nicvf_put_sq_desc(struct snd_queue *sq, int desc_cnt); +void nicvf_sq_free_used_descs(struct udevice *dev, + struct snd_queue *sq, int qidx); +int nicvf_sq_append_pkt(struct nicvf *nic, void *pkt, size_t pkt_len); + +void *nicvf_get_rcv_pkt(struct nicvf *nic, void *cq_desc, size_t *pkt_len); +void nicvf_refill_rbdr(struct nicvf *nic); + +void nicvf_enable_intr(struct nicvf *nic, int int_type, int q_idx); +void nicvf_disable_intr(struct nicvf *nic, int int_type, int q_idx); +void nicvf_clear_intr(struct nicvf *nic, int int_type, int q_idx); +int nicvf_is_intr_enabled(struct nicvf *nic, int int_type, int q_idx); + +/* Register access APIs */ +void nicvf_reg_write(struct nicvf *nic, u64 offset, u64 val); +u64 nicvf_reg_read(struct nicvf *nic, u64 offset); +void nicvf_qset_reg_write(struct nicvf *nic, u64 offset, u64 val); +u64 nicvf_qset_reg_read(struct nicvf *nic, u64 offset); +void nicvf_queue_reg_write(struct nicvf *nic, u64 offset, + u64 qidx, u64 val); +u64 nicvf_queue_reg_read(struct nicvf *nic, u64 offset, u64 qidx); + +/* Stats */ +void nicvf_update_rq_stats(struct nicvf *nic, int rq_idx); +void nicvf_update_sq_stats(struct nicvf *nic, int sq_idx); +int nicvf_check_cqe_rx_errs(struct nicvf *nic, + struct cmp_queue *cq, void *cq_desc); +int nicvf_check_cqe_tx_errs(struct nicvf *nic, + struct cmp_queue *cq, void *cq_desc); +#endif /* NICVF_QUEUES_H */ diff --git a/drivers/net/octeontx/q_struct.h b/drivers/net/octeontx/q_struct.h new file mode 100644 index 0000000000..87abb132f5 --- /dev/null +++ b/drivers/net/octeontx/q_struct.h @@ -0,0 +1,695 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Marvell International Ltd. + */ + +#ifndef Q_STRUCT_H +#define Q_STRUCT_H + +/* Load transaction types for reading segment bytes specified by + * NIC_SEND_GATHER_S[LD_TYPE]. + */ +enum nic_send_ld_type_e { + NIC_SEND_LD_TYPE_E_LDD = 0x0, + NIC_SEND_LD_TYPE_E_LDT = 0x1, + NIC_SEND_LD_TYPE_E_LDWB = 0x2, + NIC_SEND_LD_TYPE_E_ENUM_LAST = 0x3, +}; + +enum ether_type_algorithm { + ETYPE_ALG_NONE = 0x0, + ETYPE_ALG_SKIP = 0x1, + ETYPE_ALG_ENDPARSE = 0x2, + ETYPE_ALG_VLAN = 0x3, + ETYPE_ALG_VLAN_STRIP = 0x4, +}; + +enum layer3_type { + L3TYPE_NONE = 0x00, + L3TYPE_GRH = 0x01, + L3TYPE_IPV4 = 0x04, + L3TYPE_IPV4_OPTIONS = 0x05, + L3TYPE_IPV6 = 0x06, + L3TYPE_IPV6_OPTIONS = 0x07, + L3TYPE_ET_STOP = 0x0D, + L3TYPE_OTHER = 0x0E, +}; + +enum layer4_type { + L4TYPE_NONE = 0x00, + L4TYPE_IPSEC_ESP = 0x01, + L4TYPE_IPFRAG = 0x02, + L4TYPE_IPCOMP = 0x03, + L4TYPE_TCP = 0x04, + L4TYPE_UDP = 0x05, + L4TYPE_SCTP = 0x06, + L4TYPE_GRE = 0x07, + L4TYPE_ROCE_BTH = 0x08, + L4TYPE_OTHER = 0x0E, +}; + +/* CPI and RSSI configuration */ +enum cpi_algorithm_type { + CPI_ALG_NONE = 0x0, + CPI_ALG_VLAN = 0x1, + CPI_ALG_VLAN16 = 0x2, + CPI_ALG_DIFF = 0x3, +}; + +enum rss_algorithm_type { + RSS_ALG_NONE = 0x00, + RSS_ALG_PORT = 0x01, + RSS_ALG_IP = 0x02, + RSS_ALG_TCP_IP = 0x03, + RSS_ALG_UDP_IP = 0x04, + RSS_ALG_SCTP_IP = 0x05, + RSS_ALG_GRE_IP = 0x06, + RSS_ALG_ROCE = 0x07, +}; + +enum rss_hash_cfg { + RSS_HASH_L2ETC = 0x00, + RSS_HASH_IP = 0x01, + RSS_HASH_TCP = 0x02, + RSS_TCP_SYN_DIS = 0x03, + RSS_HASH_UDP = 0x04, + RSS_HASH_L4ETC = 0x05, + RSS_HASH_ROCE = 0x06, + RSS_L3_BIDI = 0x07, + RSS_L4_BIDI = 0x08, +}; + +/* Completion queue entry types */ +enum cqe_type { + CQE_TYPE_INVALID = 0x0, + CQE_TYPE_RX = 0x2, + CQE_TYPE_RX_SPLIT = 0x3, + CQE_TYPE_RX_TCP = 0x4, + CQE_TYPE_SEND = 0x8, + CQE_TYPE_SEND_PTP = 0x9, +}; + +enum cqe_rx_tcp_status { + CQE_RX_STATUS_VALID_TCP_CNXT = 0x00, + CQE_RX_STATUS_INVALID_TCP_CNXT = 0x0F, +}; + +enum cqe_send_status { + CQE_SEND_STATUS_GOOD = 0x00, + CQE_SEND_STATUS_DESC_FAULT = 0x01, + CQE_SEND_STATUS_HDR_CONS_ERR = 0x11, + CQE_SEND_STATUS_SUBDESC_ERR = 0x12, + CQE_SEND_STATUS_IMM_SIZE_OFLOW = 0x80, + CQE_SEND_STATUS_CRC_SEQ_ERR = 0x81, + CQE_SEND_STATUS_DATA_SEQ_ERR = 0x82, + CQE_SEND_STATUS_MEM_SEQ_ERR = 0x83, + CQE_SEND_STATUS_LOCK_VIOL = 0x84, + CQE_SEND_STATUS_LOCK_UFLOW = 0x85, + CQE_SEND_STATUS_DATA_FAULT = 0x86, + CQE_SEND_STATUS_TSTMP_CONFLICT = 0x87, + CQE_SEND_STATUS_TSTMP_TIMEOUT = 0x88, + CQE_SEND_STATUS_MEM_FAULT = 0x89, + CQE_SEND_STATUS_CSUM_OVERLAP = 0x8A, + CQE_SEND_STATUS_CSUM_OVERFLOW = 0x8B, +}; + +enum cqe_rx_tcp_end_reason { + CQE_RX_TCP_END_FIN_FLAG_DET = 0, + CQE_RX_TCP_END_INVALID_FLAG = 1, + CQE_RX_TCP_END_TIMEOUT = 2, + CQE_RX_TCP_END_OUT_OF_SEQ = 3, + CQE_RX_TCP_END_PKT_ERR = 4, + CQE_RX_TCP_END_QS_DISABLED = 0x0F, +}; + +/* Packet protocol level error enumeration */ +enum cqe_rx_err_level { + CQE_RX_ERRLVL_RE = 0x0, + CQE_RX_ERRLVL_L2 = 0x1, + CQE_RX_ERRLVL_L3 = 0x2, + CQE_RX_ERRLVL_L4 = 0x3, +}; + +/* Packet protocol level error type enumeration */ +enum cqe_rx_err_opcode { + CQE_RX_ERR_RE_NONE = 0x0, + CQE_RX_ERR_RE_PARTIAL = 0x1, + CQE_RX_ERR_RE_JABBER = 0x2, + CQE_RX_ERR_RE_FCS = 0x7, + CQE_RX_ERR_RE_TERMINATE = 0x9, + CQE_RX_ERR_RE_RX_CTL = 0xb, + CQE_RX_ERR_PREL2_ERR = 0x1f, + CQE_RX_ERR_L2_FRAGMENT = 0x20, + CQE_RX_ERR_L2_OVERRUN = 0x21, + CQE_RX_ERR_L2_PFCS = 0x22, + CQE_RX_ERR_L2_PUNY = 0x23, + CQE_RX_ERR_L2_MAL = 0x24, + CQE_RX_ERR_L2_OVERSIZE = 0x25, + CQE_RX_ERR_L2_UNDERSIZE = 0x26, + CQE_RX_ERR_L2_LENMISM = 0x27, + CQE_RX_ERR_L2_PCLP = 0x28, + CQE_RX_ERR_IP_NOT = 0x41, + CQE_RX_ERR_IP_CHK = 0x42, + CQE_RX_ERR_IP_MAL = 0x43, + CQE_RX_ERR_IP_MALD = 0x44, + CQE_RX_ERR_IP_HOP = 0x45, + CQE_RX_ERR_L3_ICRC = 0x46, + CQE_RX_ERR_L3_PCLP = 0x47, + CQE_RX_ERR_L4_MAL = 0x61, + CQE_RX_ERR_L4_CHK = 0x62, + CQE_RX_ERR_UDP_LEN = 0x63, + CQE_RX_ERR_L4_PORT = 0x64, + CQE_RX_ERR_TCP_FLAG = 0x65, + CQE_RX_ERR_TCP_OFFSET = 0x66, + CQE_RX_ERR_L4_PCLP = 0x67, + CQE_RX_ERR_RBDR_TRUNC = 0x70, +}; + +struct cqe_rx_t { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 cqe_type:4; /* W0 */ + u64 stdn_fault:1; + u64 rsvd0:1; + u64 rq_qs:7; + u64 rq_idx:3; + u64 rsvd1:12; + u64 rss_alg:4; + u64 rsvd2:4; + u64 rb_cnt:4; + u64 vlan_found:1; + u64 vlan_stripped:1; + u64 vlan2_found:1; + u64 vlan2_stripped:1; + u64 l4_type:4; + u64 l3_type:4; + u64 l2_present:1; + u64 err_level:3; + u64 err_opcode:8; + + u64 pkt_len:16; /* W1 */ + u64 l2_ptr:8; + u64 l3_ptr:8; + u64 l4_ptr:8; + u64 cq_pkt_len:8; + u64 align_pad:3; + u64 rsvd3:1; + u64 chan:12; + + u64 rss_tag:32; /* W2 */ + u64 vlan_tci:16; + u64 vlan_ptr:8; + u64 vlan2_ptr:8; + + u64 rb3_sz:16; /* W3 */ + u64 rb2_sz:16; + u64 rb1_sz:16; + u64 rb0_sz:16; + + u64 rb7_sz:16; /* W4 */ + u64 rb6_sz:16; + u64 rb5_sz:16; + u64 rb4_sz:16; + + u64 rb11_sz:16; /* W5 */ + u64 rb10_sz:16; + u64 rb9_sz:16; + u64 rb8_sz:16; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 err_opcode:8; + u64 err_level:3; + u64 l2_present:1; + u64 l3_type:4; + u64 l4_type:4; + u64 vlan2_stripped:1; + u64 vlan2_found:1; + u64 vlan_stripped:1; + u64 vlan_found:1; + u64 rb_cnt:4; + u64 rsvd2:4; + u64 rss_alg:4; + u64 rsvd1:12; + u64 rq_idx:3; + u64 rq_qs:7; + u64 rsvd0:1; + u64 stdn_fault:1; + u64 cqe_type:4; /* W0 */ + u64 chan:12; + u64 rsvd3:1; + u64 align_pad:3; + u64 cq_pkt_len:8; + u64 l4_ptr:8; + u64 l3_ptr:8; + u64 l2_ptr:8; + u64 pkt_len:16; /* W1 */ + u64 vlan2_ptr:8; + u64 vlan_ptr:8; + u64 vlan_tci:16; + u64 rss_tag:32; /* W2 */ + u64 rb0_sz:16; + u64 rb1_sz:16; + u64 rb2_sz:16; + u64 rb3_sz:16; /* W3 */ + u64 rb4_sz:16; + u64 rb5_sz:16; + u64 rb6_sz:16; + u64 rb7_sz:16; /* W4 */ + u64 rb8_sz:16; + u64 rb9_sz:16; + u64 rb10_sz:16; + u64 rb11_sz:16; /* W5 */ +#endif + u64 rb0_ptr:64; + u64 rb1_ptr:64; + u64 rb2_ptr:64; + u64 rb3_ptr:64; + u64 rb4_ptr:64; + u64 rb5_ptr:64; + u64 rb6_ptr:64; + u64 rb7_ptr:64; + u64 rb8_ptr:64; + u64 rb9_ptr:64; + u64 rb10_ptr:64; + u64 rb11_ptr:64; +}; + +struct cqe_rx_tcp_err_t { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 cqe_type:4; /* W0 */ + u64 rsvd0:60; + + u64 rsvd1:4; /* W1 */ + u64 partial_first:1; + u64 rsvd2:27; + u64 rbdr_bytes:8; + u64 rsvd3:24; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 rsvd0:60; + u64 cqe_type:4; + + u64 rsvd3:24; + u64 rbdr_bytes:8; + u64 rsvd2:27; + u64 partial_first:1; + u64 rsvd1:4; +#endif +}; + +struct cqe_rx_tcp_t { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 cqe_type:4; /* W0 */ + u64 rsvd0:52; + u64 cq_tcp_status:8; + + u64 rsvd1:32; /* W1 */ + u64 tcp_cntx_bytes:8; + u64 rsvd2:8; + u64 tcp_err_bytes:16; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 cq_tcp_status:8; + u64 rsvd0:52; + u64 cqe_type:4; /* W0 */ + + u64 tcp_err_bytes:16; + u64 rsvd2:8; + u64 tcp_cntx_bytes:8; + u64 rsvd1:32; /* W1 */ +#endif +}; + +struct cqe_send_t { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 cqe_type:4; /* W0 */ + u64 rsvd0:4; + u64 sqe_ptr:16; + u64 rsvd1:4; + u64 rsvd2:10; + u64 sq_qs:7; + u64 sq_idx:3; + u64 rsvd3:8; + u64 send_status:8; + + u64 ptp_timestamp:64; /* W1 */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 send_status:8; + u64 rsvd3:8; + u64 sq_idx:3; + u64 sq_qs:7; + u64 rsvd2:10; + u64 rsvd1:4; + u64 sqe_ptr:16; + u64 rsvd0:4; + u64 cqe_type:4; /* W0 */ + + u64 ptp_timestamp:64; /* W1 */ +#endif +}; + +union cq_desc_t { + u64 u[64]; + struct cqe_send_t snd_hdr; + struct cqe_rx_t rx_hdr; + struct cqe_rx_tcp_t rx_tcp_hdr; + struct cqe_rx_tcp_err_t rx_tcp_err_hdr; +}; + +struct rbdr_entry_t { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 rsvd0:15; + u64 buf_addr:42; + u64 cache_align:7; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 cache_align:7; + u64 buf_addr:42; + u64 rsvd0:15; +#endif +}; + +/* TCP reassembly context */ +struct rbe_tcp_cnxt_t { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 tcp_pkt_cnt:12; + u64 rsvd1:4; + u64 align_hdr_bytes:4; + u64 align_ptr_bytes:4; + u64 ptr_bytes:16; + u64 rsvd2:24; + u64 cqe_type:4; + u64 rsvd0:54; + u64 tcp_end_reason:2; + u64 tcp_status:4; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 tcp_status:4; + u64 tcp_end_reason:2; + u64 rsvd0:54; + u64 cqe_type:4; + u64 rsvd2:24; + u64 ptr_bytes:16; + u64 align_ptr_bytes:4; + u64 align_hdr_bytes:4; + u64 rsvd1:4; + u64 tcp_pkt_cnt:12; +#endif +}; + +/* Always Big endian */ +struct rx_hdr_t { + u64 opaque:32; + u64 rss_flow:8; + u64 skip_length:6; + u64 disable_rss:1; + u64 disable_tcp_reassembly:1; + u64 nodrop:1; + u64 dest_alg:2; + u64 rsvd0:2; + u64 dest_rq:11; +}; + +enum send_l4_csum_type { + SEND_L4_CSUM_DISABLE = 0x00, + SEND_L4_CSUM_UDP = 0x01, + SEND_L4_CSUM_TCP = 0x02, + SEND_L4_CSUM_SCTP = 0x03, +}; + +enum send_crc_alg { + SEND_CRCALG_CRC32 = 0x00, + SEND_CRCALG_CRC32C = 0x01, + SEND_CRCALG_ICRC = 0x02, +}; + +enum send_load_type { + SEND_LD_TYPE_LDD = 0x00, + SEND_LD_TYPE_LDT = 0x01, + SEND_LD_TYPE_LDWB = 0x02, +}; + +enum send_mem_alg_type { + SEND_MEMALG_SET = 0x00, + SEND_MEMALG_ADD = 0x08, + SEND_MEMALG_SUB = 0x09, + SEND_MEMALG_ADDLEN = 0x0A, + SEND_MEMALG_SUBLEN = 0x0B, +}; + +enum send_mem_dsz_type { + SEND_MEMDSZ_B64 = 0x00, + SEND_MEMDSZ_B32 = 0x01, + SEND_MEMDSZ_B8 = 0x03, +}; + +enum sq_subdesc_type { + SQ_DESC_TYPE_INVALID = 0x00, + SQ_DESC_TYPE_HEADER = 0x01, + SQ_DESC_TYPE_CRC = 0x02, + SQ_DESC_TYPE_IMMEDIATE = 0x03, + SQ_DESC_TYPE_GATHER = 0x04, + SQ_DESC_TYPE_MEMORY = 0x05, +}; + +struct sq_crc_subdesc { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 rsvd1:32; + u64 crc_ival:32; + u64 subdesc_type:4; + u64 crc_alg:2; + u64 rsvd0:10; + u64 crc_insert_pos:16; + u64 hdr_start:16; + u64 crc_len:16; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 crc_len:16; + u64 hdr_start:16; + u64 crc_insert_pos:16; + u64 rsvd0:10; + u64 crc_alg:2; + u64 subdesc_type:4; + u64 crc_ival:32; + u64 rsvd1:32; +#endif +}; + +struct sq_gather_subdesc { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 subdesc_type:4; /* W0 */ + u64 ld_type:2; + u64 rsvd0:42; + u64 size:16; + + u64 rsvd1:15; /* W1 */ + u64 addr:49; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 size:16; + u64 rsvd0:42; + u64 ld_type:2; + u64 subdesc_type:4; /* W0 */ + + u64 addr:49; + u64 rsvd1:15; /* W1 */ +#endif +}; + +/* SQ immediate subdescriptor */ +struct sq_imm_subdesc { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 subdesc_type:4; /* W0 */ + u64 rsvd0:46; + u64 len:14; + + u64 data:64; /* W1 */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 len:14; + u64 rsvd0:46; + u64 subdesc_type:4; /* W0 */ + + u64 data:64; /* W1 */ +#endif +}; + +struct sq_mem_subdesc { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 subdesc_type:4; /* W0 */ + u64 mem_alg:4; + u64 mem_dsz:2; + u64 wmem:1; + u64 rsvd0:21; + u64 offset:32; + + u64 rsvd1:15; /* W1 */ + u64 addr:49; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 offset:32; + u64 rsvd0:21; + u64 wmem:1; + u64 mem_dsz:2; + u64 mem_alg:4; + u64 subdesc_type:4; /* W0 */ + + u64 addr:49; + u64 rsvd1:15; /* W1 */ +#endif +}; + +struct sq_hdr_subdesc { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 subdesc_type:4; + u64 tso:1; + u64 post_cqe:1; /* Post CQE on no error also */ + u64 dont_send:1; + u64 tstmp:1; + u64 subdesc_cnt:8; + u64 csum_l4:2; + u64 csum_l3:1; + u64 rsvd0:5; + u64 l4_offset:8; + u64 l3_offset:8; + u64 rsvd1:4; + u64 tot_len:20; /* W0 */ + + u64 tso_sdc_cont:8; + u64 tso_sdc_first:8; + u64 tso_l4_offset:8; + u64 tso_flags_last:12; + u64 tso_flags_first:12; + u64 rsvd2:2; + u64 tso_max_paysize:14; /* W1 */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 tot_len:20; + u64 rsvd1:4; + u64 l3_offset:8; + u64 l4_offset:8; + u64 rsvd0:5; + u64 csum_l3:1; + u64 csum_l4:2; + u64 subdesc_cnt:8; + u64 tstmp:1; + u64 dont_send:1; + u64 post_cqe:1; /* Post CQE on no error also */ + u64 tso:1; + u64 subdesc_type:4; /* W0 */ + + u64 tso_max_paysize:14; + u64 rsvd2:2; + u64 tso_flags_first:12; + u64 tso_flags_last:12; + u64 tso_l4_offset:8; + u64 tso_sdc_first:8; + u64 tso_sdc_cont:8; /* W1 */ +#endif +}; + +/* Queue config register formats */ +struct rq_cfg { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 reserved_2_63:62; + u64 ena:1; + u64 tcp_ena:1; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 tcp_ena:1; + u64 ena:1; + u64 reserved_2_63:62; +#endif +}; + +struct cq_cfg { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 reserved_43_63:21; + u64 ena:1; + u64 reset:1; + u64 caching:1; + u64 reserved_35_39:5; + u64 qsize:3; + u64 reserved_25_31:7; + u64 avg_con:9; + u64 reserved_0_15:16; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 reserved_0_15:16; + u64 avg_con:9; + u64 reserved_25_31:7; + u64 qsize:3; + u64 reserved_35_39:5; + u64 caching:1; + u64 reset:1; + u64 ena:1; + u64 reserved_43_63:21; +#endif +}; + +struct sq_cfg { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 reserved_20_63:44; + u64 ena:1; + u64 reserved_18_18:1; + u64 reset:1; + u64 ldwb:1; + u64 reserved_11_15:5; + u64 qsize:3; + u64 reserved_3_7:5; + u64 tstmp_bgx_intf:3; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 tstmp_bgx_intf:3; + u64 reserved_3_7:5; + u64 qsize:3; + u64 reserved_11_15:5; + u64 ldwb:1; + u64 reset:1; + u64 reserved_18_18:1; + u64 ena:1; + u64 reserved_20_63:44; +#endif +}; + +struct rbdr_cfg { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 reserved_45_63:19; + u64 ena:1; + u64 reset:1; + u64 ldwb:1; + u64 reserved_36_41:6; + u64 qsize:4; + u64 reserved_25_31:7; + u64 avg_con:9; + u64 reserved_12_15:4; + u64 lines:12; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 lines:12; + u64 reserved_12_15:4; + u64 avg_con:9; + u64 reserved_25_31:7; + u64 qsize:4; + u64 reserved_36_41:6; + u64 ldwb:1; + u64 reset:1; + u64 ena: 1; + u64 reserved_45_63:19; +#endif +}; + +struct qs_cfg { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 reserved_32_63:32; + u64 ena:1; + u64 reserved_27_30:4; + u64 sq_ins_ena:1; + u64 sq_ins_pos:6; + u64 lock_ena:1; + u64 lock_viol_cqe_ena:1; + u64 send_tstmp_ena:1; + u64 be:1; + u64 reserved_7_15:9; + u64 vnic:7; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 vnic:7; + u64 reserved_7_15:9; + u64 be:1; + u64 send_tstmp_ena:1; + u64 lock_viol_cqe_ena:1; + u64 lock_ena:1; + u64 sq_ins_pos:6; + u64 sq_ins_ena:1; + u64 reserved_27_30:4; + u64 ena:1; + u64 reserved_32_63:32; +#endif +}; + +#endif /* Q_STRUCT_H */ diff --git a/drivers/net/octeontx/smi.c b/drivers/net/octeontx/smi.c new file mode 100644 index 0000000000..8e2c3ca5a3 --- /dev/null +++ b/drivers/net/octeontx/smi.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell International Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCI_DEVICE_ID_OCTEONTX_SMI 0xA02B + +DECLARE_GLOBAL_DATA_PTR; + +enum octeontx_smi_mode { + CLAUSE22 = 0, + CLAUSE45 = 1, +}; + +enum { + SMI_OP_C22_WRITE = 0, + SMI_OP_C22_READ = 1, + + SMI_OP_C45_ADDR = 0, + SMI_OP_C45_WRITE = 1, + SMI_OP_C45_PRIA = 2, + SMI_OP_C45_READ = 3, +}; + +union smi_x_clk { + u64 u; + struct smi_x_clk_s { + int phase:8; + int sample:4; + int preamble:1; + int clk_idle:1; + int reserved_14_14:1; + int sample_mode:1; + int sample_hi:5; + int reserved_21_23:3; + int mode:1; + } s; +}; + +union smi_x_cmd { + u64 u; + struct smi_x_cmd_s { + int reg_adr:5; + int reserved_5_7:3; + int phy_adr:5; + int reserved_13_15:3; + int phy_op:2; + } s; +}; + +union smi_x_wr_dat { + u64 u; + struct smi_x_wr_dat_s { + unsigned int dat:16; + int val:1; + int pending:1; + } s; +}; + +union smi_x_rd_dat { + u64 u; + struct smi_x_rd_dat_s { + unsigned int dat:16; + int val:1; + int pending:1; + } s; +}; + +union smi_x_en { + u64 u; + struct smi_x_en_s { + int en:1; + } s; +}; + +#define SMI_X_RD_DAT 0x10ull +#define SMI_X_WR_DAT 0x08ull +#define SMI_X_CMD 0x00ull +#define SMI_X_CLK 0x18ull +#define SMI_X_EN 0x20ull + +struct octeontx_smi_priv { + void __iomem *baseaddr; + enum octeontx_smi_mode mode; +}; + +#define MDIO_TIMEOUT 10000 + +void octeontx_smi_setmode(struct mii_dev *bus, enum octeontx_smi_mode mode) +{ + struct octeontx_smi_priv *priv = bus->priv; + union smi_x_clk smix_clk; + + smix_clk.u = readq(priv->baseaddr + SMI_X_CLK); + smix_clk.s.mode = mode; + smix_clk.s.preamble = mode == CLAUSE45; + writeq(smix_clk.u, priv->baseaddr + SMI_X_CLK); + + priv->mode = mode; +} + +int octeontx_c45_addr(struct mii_dev *bus, int addr, int devad, int regnum) +{ + struct octeontx_smi_priv *priv = bus->priv; + + union smi_x_cmd smix_cmd; + union smi_x_wr_dat smix_wr_dat; + unsigned long timeout = MDIO_TIMEOUT; + + smix_wr_dat.u = 0; + smix_wr_dat.s.dat = regnum; + + writeq(smix_wr_dat.u, priv->baseaddr + SMI_X_WR_DAT); + + smix_cmd.u = 0; + smix_cmd.s.phy_op = SMI_OP_C45_ADDR; + smix_cmd.s.phy_adr = addr; + smix_cmd.s.reg_adr = devad; + + writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD); + + do { + smix_wr_dat.u = readq(priv->baseaddr + SMI_X_WR_DAT); + udelay(100); + timeout--; + } while (smix_wr_dat.s.pending && timeout); + + return timeout == 0; +} + +int octeontx_phy_read(struct mii_dev *bus, int addr, int devad, int regnum) +{ + struct octeontx_smi_priv *priv = bus->priv; + union smi_x_cmd smix_cmd; + union smi_x_rd_dat smix_rd_dat; + unsigned long timeout = MDIO_TIMEOUT; + int ret; + + enum octeontx_smi_mode mode = (devad < 0) ? CLAUSE22 : CLAUSE45; + + debug("RD: Mode: %u, baseaddr: %p, addr: %d, devad: %d, reg: %d\n", + mode, priv->baseaddr, addr, devad, regnum); + + octeontx_smi_setmode(bus, mode); + + if (mode == CLAUSE45) { + ret = octeontx_c45_addr(bus, addr, devad, regnum); + + debug("RD: ret: %u\n", ret); + + if (ret) + return 0; + } + + smix_cmd.u = 0; + smix_cmd.s.phy_adr = addr; + + if (mode == CLAUSE45) { + smix_cmd.s.reg_adr = devad; + smix_cmd.s.phy_op = SMI_OP_C45_READ; + } else { + smix_cmd.s.reg_adr = regnum; + smix_cmd.s.phy_op = SMI_OP_C22_READ; + } + + writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD); + + do { + smix_rd_dat.u = readq(priv->baseaddr + SMI_X_RD_DAT); + udelay(10); + timeout--; + } while (smix_rd_dat.s.pending && timeout); + + debug("SMIX_RD_DAT: %lx\n", (unsigned long)smix_rd_dat.u); + + return smix_rd_dat.s.dat; +} + +int octeontx_phy_write(struct mii_dev *bus, int addr, int devad, int regnum, + u16 value) +{ + struct octeontx_smi_priv *priv = bus->priv; + union smi_x_cmd smix_cmd; + union smi_x_wr_dat smix_wr_dat; + unsigned long timeout = MDIO_TIMEOUT; + int ret; + + enum octeontx_smi_mode mode = (devad < 0) ? CLAUSE22 : CLAUSE45; + + debug("WR: Mode: %u, baseaddr: %p, addr: %d, devad: %d, reg: %d\n", + mode, priv->baseaddr, addr, devad, regnum); + + if (mode == CLAUSE45) { + ret = octeontx_c45_addr(bus, addr, devad, regnum); + + debug("WR: ret: %u\n", ret); + + if (ret) + return ret; + } + + smix_wr_dat.u = 0; + smix_wr_dat.s.dat = value; + + writeq(smix_wr_dat.u, priv->baseaddr + SMI_X_WR_DAT); + + smix_cmd.u = 0; + smix_cmd.s.phy_adr = addr; + + if (mode == CLAUSE45) { + smix_cmd.s.reg_adr = devad; + smix_cmd.s.phy_op = SMI_OP_C45_WRITE; + } else { + smix_cmd.s.reg_adr = regnum; + smix_cmd.s.phy_op = SMI_OP_C22_WRITE; + } + + writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD); + + do { + smix_wr_dat.u = readq(priv->baseaddr + SMI_X_WR_DAT); + udelay(10); + timeout--; + } while (smix_wr_dat.s.pending && timeout); + + debug("SMIX_WR_DAT: %lx\n", (unsigned long)smix_wr_dat.u); + + return timeout == 0; +} + +int octeontx_smi_reset(struct mii_dev *bus) +{ + struct octeontx_smi_priv *priv = bus->priv; + + union smi_x_en smi_en; + + smi_en.s.en = 0; + writeq(smi_en.u, priv->baseaddr + SMI_X_EN); + + smi_en.s.en = 1; + writeq(smi_en.u, priv->baseaddr + SMI_X_EN); + + octeontx_smi_setmode(bus, CLAUSE22); + + return 0; +} + +/* PHY XS initialization, primarily for RXAUI + * + */ +int rxaui_phy_xs_init(struct mii_dev *bus, int phy_addr) +{ + int reg; + ulong start_time; + int phy_id1, phy_id2; + int oui, model_number; + + phy_id1 = octeontx_phy_read(bus, phy_addr, 1, 0x2); + phy_id2 = octeontx_phy_read(bus, phy_addr, 1, 0x3); + model_number = (phy_id2 >> 4) & 0x3F; + debug("%s model %x\n", __func__, model_number); + oui = phy_id1; + oui <<= 6; + oui |= (phy_id2 >> 10) & 0x3F; + debug("%s oui %x\n", __func__, oui); + switch (oui) { + case 0x5016: + if (model_number == 9) { + debug("%s +\n", __func__); + /* Perform hardware reset in XGXS control */ + reg = octeontx_phy_read(bus, phy_addr, 4, 0x0); + if ((reg & 0xffff) < 0) + goto read_error; + reg |= 0x8000; + octeontx_phy_write(bus, phy_addr, 4, 0x0, reg); + + start_time = get_timer(0); + do { + reg = octeontx_phy_read(bus, phy_addr, 4, 0x0); + if ((reg & 0xffff) < 0) + goto read_error; + } while ((reg & 0x8000) && get_timer(start_time) < 500); + if (reg & 0x8000) { + printf("HW reset for M88X3120 PHY failed"); + printf("MII_BMCR: 0x%x\n", reg); + return -1; + } + /* program 4.49155 with 0x5 */ + octeontx_phy_write(bus, phy_addr, 4, 0xc003, 0x5); + } + break; + default: + break; + } + + return 0; + +read_error: + debug("M88X3120 PHY config read failed\n"); + return -1; +} + +int octeontx_smi_probe(struct udevice *dev) +{ + int ret, subnode, cnt = 0, node = dev->node.of_offset; + struct mii_dev *bus; + struct octeontx_smi_priv *priv; + pci_dev_t bdf = dm_pci_get_bdf(dev); + + debug("SMI PCI device: %x\n", bdf); + dev->req_seq = PCI_FUNC(bdf); + if (!dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, PCI_REGION_MEM)) { + printf("Failed to map PCI region for bdf %x\n", bdf); + return -1; + } + + fdt_for_each_subnode(subnode, gd->fdt_blob, node) { + ret = fdt_node_check_compatible(gd->fdt_blob, subnode, + "cavium,thunder-8890-mdio"); + if (ret) + continue; + + bus = mdio_alloc(); + priv = malloc(sizeof(*priv)); + if (!bus || !priv) { + printf("Failed to allocate OcteonTX MDIO bus # %u\n", + dev->seq); + return -1; + } + + bus->read = octeontx_phy_read; + bus->write = octeontx_phy_write; + bus->reset = octeontx_smi_reset; + bus->priv = priv; + + priv->mode = CLAUSE22; + priv->baseaddr = (void __iomem *)fdtdec_get_addr(gd->fdt_blob, + subnode, + "reg"); + debug("mdio base addr %p\n", priv->baseaddr); + + /* use given name or generate its own unique name */ + snprintf(bus->name, MDIO_NAME_LEN, "smi%d", cnt++); + + ret = mdio_register(bus); + if (ret) + return ret; + } + return 0; +} + +static const struct udevice_id octeontx_smi_ids[] = { + { .compatible = "cavium,thunder-8890-mdio-nexus" }, + {} +}; + +U_BOOT_DRIVER(octeontx_smi) = { + .name = "octeontx_smi", + .id = UCLASS_MISC, + .probe = octeontx_smi_probe, + .of_match = octeontx_smi_ids, +}; + +static struct pci_device_id octeontx_smi_supported[] = { + { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_SMI) }, + {} +}; + +U_BOOT_PCI_DEVICE(octeontx_smi, octeontx_smi_supported); diff --git a/drivers/net/octeontx/xcv.c b/drivers/net/octeontx/xcv.c new file mode 100644 index 0000000000..8dd558b1bf --- /dev/null +++ b/drivers/net/octeontx/xcv.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell International Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define XCVX_BASE 0x87E0DB000000ULL + +/* Initialize XCV block */ +void xcv_init_hw(void) +{ + union xcvx_reset reset; + union xcvx_dll_ctl xcv_dll_ctl; + + /* Take the DLL out of reset */ + reset.u = readq(XCVX_BASE + XCVX_RESET(0)); + reset.s.dllrst = 0; + writeq(reset.u, XCVX_BASE + XCVX_RESET(0)); + + /* Take the clock tree out of reset */ + reset.u = readq(XCVX_BASE + XCVX_RESET(0)); + reset.s.clkrst = 0; + writeq(reset.u, XCVX_BASE + XCVX_RESET(0)); + + /* Once the 125MHz ref clock is stable, wait 10us for DLL to lock */ + udelay(10); + + /* Optionally, bypass the DLL setting */ + xcv_dll_ctl.u = readq(XCVX_BASE + XCVX_DLL_CTL(0)); + xcv_dll_ctl.s.clkrx_set = 0; + xcv_dll_ctl.s.clkrx_byp = 1; + xcv_dll_ctl.s.clktx_byp = 0; + writeq(xcv_dll_ctl.u, XCVX_BASE + XCVX_DLL_CTL(0)); + + /* Enable the compensation controller */ + reset.u = readq(XCVX_BASE + XCVX_RESET(0)); + reset.s.comp = 1; + writeq(reset.u, XCVX_BASE + XCVX_RESET(0)); + reset.u = readq(XCVX_BASE + XCVX_RESET(0)); + + /* Wait for 1040 reference clock cycles for the compensation state + * machine lock. + */ + udelay(100); + + /* Enable the XCV block */ + reset.u = readq(XCVX_BASE + XCVX_RESET(0)); + reset.s.enable = 1; + writeq(reset.u, XCVX_BASE + XCVX_RESET(0)); + + /* set XCV(0)_RESET[CLKRST] to 1 */ + reset.u = readq(XCVX_BASE + XCVX_RESET(0)); + reset.s.clkrst = 1; + writeq(reset.u, XCVX_BASE + XCVX_RESET(0)); +} + +/* + * Configure XCV link based on the speed + * link_up : Set to 1 when link is up otherwise 0 + * link_speed: The speed of the link. + */ +void xcv_setup_link(bool link_up, int link_speed) +{ + union xcvx_ctl xcv_ctl; + union xcvx_reset reset; + union xcvx_batch_crd_ret xcv_crd_ret; + int speed = 2; + + /* Check RGMII link */ + if (link_speed == 100) + speed = 1; + else if (link_speed == 10) + speed = 0; + + if (link_up) { + /* Set operating speed */ + xcv_ctl.u = readq(XCVX_BASE + XCVX_CTL(0)); + xcv_ctl.s.speed = speed; + writeq(xcv_ctl.u, XCVX_BASE + XCVX_CTL(0)); + + /* Datapaths come out of reset + * - The datapath resets will disengage BGX from the + * RGMII interface + * - XCV will continue to return TX credits for each tick + * that is sent on the TX data path + */ + reset.u = readq(XCVX_BASE + XCVX_RESET(0)); + reset.s.tx_dat_rst_n = 1; + reset.s.rx_dat_rst_n = 1; + writeq(reset.u, XCVX_BASE + XCVX_RESET(0)); + + /* Enable packet flow */ + reset.u = readq(XCVX_BASE + XCVX_RESET(0)); + reset.s.tx_pkt_rst_n = 1; + reset.s.rx_pkt_rst_n = 1; + writeq(reset.u, XCVX_BASE + XCVX_RESET(0)); + + xcv_crd_ret.u = readq(XCVX_BASE + XCVX_BATCH_CRD_RET(0)); + xcv_crd_ret.s.crd_ret = 1; + writeq(xcv_crd_ret.u, XCVX_BASE + XCVX_BATCH_CRD_RET(0)); + } else { + /* Enable packet flow */ + reset.u = readq(XCVX_BASE + XCVX_RESET(0)); + reset.s.tx_pkt_rst_n = 0; + reset.s.rx_pkt_rst_n = 0; + writeq(reset.u, XCVX_BASE + XCVX_RESET(0)); + reset.u = readq(XCVX_BASE + XCVX_RESET(0)); + } +} -- cgit v1.2.1 From 4684a7a43ad4c911bc810c243b2c58947c548fc5 Mon Sep 17 00:00:00 2001 From: Suneel Garapati Date: Wed, 26 Aug 2020 14:37:42 +0200 Subject: net: Add NIC controller driver for OcteonTX2 Adds support for Network Interface controllers found on OcteonTX2 SoC platforms. Signed-off-by: Suneel Garapati Signed-off-by: Stefan Roese Cc: Joe Hershberger --- drivers/net/Kconfig | 17 + drivers/net/Makefile | 2 + drivers/net/octeontx2/Makefile | 8 + drivers/net/octeontx2/cgx.c | 296 ++++++++++ drivers/net/octeontx2/cgx.h | 105 ++++ drivers/net/octeontx2/cgx_intf.c | 715 +++++++++++++++++++++++ drivers/net/octeontx2/cgx_intf.h | 448 +++++++++++++++ drivers/net/octeontx2/lmt.h | 49 ++ drivers/net/octeontx2/nix.c | 831 +++++++++++++++++++++++++++ drivers/net/octeontx2/nix.h | 353 ++++++++++++ drivers/net/octeontx2/nix_af.c | 1102 ++++++++++++++++++++++++++++++++++++ drivers/net/octeontx2/npc.h | 90 +++ drivers/net/octeontx2/rvu.h | 119 ++++ drivers/net/octeontx2/rvu_af.c | 171 ++++++ drivers/net/octeontx2/rvu_common.c | 71 +++ drivers/net/octeontx2/rvu_pf.c | 116 ++++ 16 files changed, 4493 insertions(+) create mode 100644 drivers/net/octeontx2/Makefile create mode 100644 drivers/net/octeontx2/cgx.c create mode 100644 drivers/net/octeontx2/cgx.h create mode 100644 drivers/net/octeontx2/cgx_intf.c create mode 100644 drivers/net/octeontx2/cgx_intf.h create mode 100644 drivers/net/octeontx2/lmt.h create mode 100644 drivers/net/octeontx2/nix.c create mode 100644 drivers/net/octeontx2/nix.h create mode 100644 drivers/net/octeontx2/nix_af.c create mode 100644 drivers/net/octeontx2/npc.h create mode 100644 drivers/net/octeontx2/rvu.h create mode 100644 drivers/net/octeontx2/rvu_af.c create mode 100644 drivers/net/octeontx2/rvu_common.c create mode 100644 drivers/net/octeontx2/rvu_pf.c diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 96f23d3d7c..2beec2da41 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -414,6 +414,15 @@ config NET_OCTEONTX help You must select Y to enable network device support for OcteonTX SoCs. If unsure, say n + +config NET_OCTEONTX2 + bool "OcteonTX2 Ethernet support" + depends on ARCH_OCTEONTX2 + select OCTEONTX2_CGX_INTF + help + You must select Y to enable network device support for + OcteonTX2 SoCs. If unsure, say n + config OCTEONTX_SMI bool "OcteonTX SMI Device support" depends on ARCH_OCTEONTX || ARCH_OCTEONTX2 @@ -421,6 +430,14 @@ config OCTEONTX_SMI You must select Y to enable SMI controller support for OcteonTX or OcteonTX2 SoCs. If unsure, say n +config OCTEONTX2_CGX_INTF + bool "OcteonTX2 CGX ATF interface support" + depends on ARCH_OCTEONTX2 + default y if ARCH_OCTEONTX2 + help + You must select Y to enable CGX ATF interface support for + OcteonTX2 SoCs. If unsure, say n + config PCH_GBE bool "Intel Platform Controller Hub EG20T GMAC driver" depends on DM_ETH && DM_PCI diff --git a/drivers/net/Makefile b/drivers/net/Makefile index c193953b0e..9f7a79ecb1 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -66,7 +66,9 @@ obj-$(CONFIG_SMC91111) += smc91111.o obj-$(CONFIG_SMC911X) += smc911x.o obj-$(CONFIG_TSEC_ENET) += tsec.o fsl_mdio.o obj-$(CONFIG_NET_OCTEONTX) += octeontx/ +obj-$(CONFIG_NET_OCTEONTX2) += octeontx2/ obj-$(CONFIG_OCTEONTX_SMI) += octeontx/smi.o +obj-$(CONFIG_OCTEONTX2_CGX_INTF) += octeontx2/cgx_intf.o obj-$(CONFIG_FMAN_ENET) += fsl_mdio.o obj-$(CONFIG_ULI526X) += uli526x.o obj-$(CONFIG_VSC7385_ENET) += vsc7385.o diff --git a/drivers/net/octeontx2/Makefile b/drivers/net/octeontx2/Makefile new file mode 100644 index 0000000000..c9300727ae --- /dev/null +++ b/drivers/net/octeontx2/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (C) 2018 Marvell International Ltd. +# + +obj-$(CONFIG_NET_OCTEONTX2) += cgx.o nix_af.o nix.o rvu_pf.o \ + rvu_af.o rvu_common.o + diff --git a/drivers/net/octeontx2/cgx.c b/drivers/net/octeontx2/cgx.c new file mode 100644 index 0000000000..ff2ebc25ce --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); diff --git a/drivers/net/octeontx2/cgx.h b/drivers/net/octeontx2/cgx.h new file mode 100644 index 0000000000..f287692712 --- /dev/null +++ b/drivers/net/octeontx2/cgx.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Marvell International Ltd. + */ + +#ifndef __CGX_H__ +#define __CGX_H__ + +#include "cgx_intf.h" + +#define PCI_DEVICE_ID_OCTEONTX2_CGX 0xA059 + +#define MAX_LMAC_PER_CGX 4 +#define CGX_PER_NODE 3 + +enum lmac_type { + LMAC_MODE_SGMII = 0, + LMAC_MODE_XAUI = 1, + LMAC_MODE_RXAUI = 2, + LMAC_MODE_10G_R = 3, + LMAC_MODE_40G_R = 4, + LMAC_MODE_QSGMII = 6, + LMAC_MODE_25G_R = 7, + LMAC_MODE_50G_R = 8, + LMAC_MODE_100G_R = 9, + LMAC_MODE_USXGMII = 10, +}; + +extern char lmac_type_to_str[][8]; + +extern char lmac_speed_to_str[][8]; + +struct lmac_priv { + u8 enable:1; + u8 full_duplex:1; + u8 speed:4; + u8 mode:1; + u8 rsvd:1; + u8 mac_addr[6]; +}; + +struct cgx; +struct nix; +struct nix_af; + +struct lmac { + struct cgx *cgx; + struct nix *nix; + char name[16]; + enum lmac_type lmac_type; + bool init_pend; + u8 instance; + u8 lmac_id; + u8 pknd; + u8 link_num; + u32 chan_num; + u8 mac_addr[6]; +}; + +struct cgx { + struct nix_af *nix_af; + void __iomem *reg_base; + struct udevice *dev; + struct lmac *lmac[MAX_LMAC_PER_CGX]; + u8 cgx_id; + u8 lmac_count; +}; + +static inline void cgx_write(struct cgx *cgx, u8 lmac, u64 offset, u64 val) +{ + writeq(val, cgx->reg_base + CMR_SHIFT(lmac) + offset); +} + +static inline u64 cgx_read(struct cgx *cgx, u8 lmac, u64 offset) +{ + return readq(cgx->reg_base + CMR_SHIFT(lmac) + offset); +} + +/** + * 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); + +int cgx_lmac_set_pkind(struct lmac *lmac, u8 lmac_id, int pkind); +int cgx_lmac_internal_loopback(struct lmac *lmac, int lmac_id, bool enable); +int cgx_lmac_rx_tx_enable(struct lmac *lmac, int lmac_id, bool enable); +int cgx_lmac_link_enable(struct lmac *lmac, int lmac_id, bool enable, + u64 *status); +int cgx_lmac_link_status(struct lmac *lmac, int lmac_id, u64 *status); +void cgx_lmac_mac_filter_setup(struct lmac *lmac); + +int cgx_intf_get_link_sts(u8 cgx, u8 lmac, u64 *lnk_sts); +int cgx_intf_link_up_dwn(u8 cgx, u8 lmac, u8 up_dwn, u64 *lnk_sts); +int cgx_intf_get_mac_addr(u8 cgx, u8 lmac, u8 *mac); +int cgx_intf_set_macaddr(struct udevice *dev); +int cgx_intf_prbs(u8 qlm, u8 mode, u32 time, u8 lane); +int cgx_intf_display_eye(u8 qlm, u8 lane); +int cgx_intf_display_serdes(u8 qlm, u8 lane); + +#endif /* __CGX_H__ */ diff --git a/drivers/net/octeontx2/cgx_intf.c b/drivers/net/octeontx2/cgx_intf.c new file mode 100644 index 0000000000..37d9a2bb73 --- /dev/null +++ b/drivers/net/octeontx2/cgx_intf.c @@ -0,0 +1,715 @@ +// 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; +} diff --git a/drivers/net/octeontx2/cgx_intf.h b/drivers/net/octeontx2/cgx_intf.h new file mode 100644 index 0000000000..62a7203ad8 --- /dev/null +++ b/drivers/net/octeontx2/cgx_intf.h @@ -0,0 +1,448 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Marvell International Ltd. + */ + +#ifndef __CGX_INTF_H__ +#define __CGX_INTF_H__ + +#define CGX_FIRMWARE_MAJOR_VER 1 +#define CGX_FIRMWARE_MINOR_VER 0 + +/* Register offsets */ +#define CGX_CMR_INT 0x87e0e0000040 +#define CGX_CMR_SCRATCH0 0x87e0e0001050 +#define CGX_CMR_SCRATCH1 0x87e0e0001058 + +#define CGX_SHIFT(x) (0x1000000 * ((x) & 0x3)) +#define CMR_SHIFT(x) (0x40000 * ((x) & 0x3)) + +/* CGX error types. set for cmd response status as CGX_STAT_FAIL */ +enum cgx_error_type { + CGX_ERR_NONE, + CGX_ERR_LMAC_NOT_ENABLED, + CGX_ERR_LMAC_MODE_INVALID, + CGX_ERR_REQUEST_ID_INVALID, + CGX_ERR_PREV_ACK_NOT_CLEAR, + CGX_ERR_PHY_LINK_DOWN, + CGX_ERR_PCS_RESET_FAIL, + CGX_ERR_AN_CPT_FAIL, + CGX_ERR_TX_NOT_IDLE, + CGX_ERR_RX_NOT_IDLE, + CGX_ERR_SPUX_BR_BLKLOCK_FAIL, + CGX_ERR_SPUX_RX_ALIGN_FAIL, + CGX_ERR_SPUX_TX_FAULT, + CGX_ERR_SPUX_RX_FAULT, + CGX_ERR_SPUX_RESET_FAIL, + CGX_ERR_SPUX_AN_RESET_FAIL, + CGX_ERR_SPUX_USX_AN_RESET_FAIL, + CGX_ERR_SMUX_RX_LINK_NOT_OK, + CGX_ERR_PCS_LINK_FAIL, + CGX_ERR_TRAINING_FAIL, + CGX_ERR_RX_EQU_FAIL, + CGX_ERR_SPUX_BER_FAIL, + CGX_ERR_SPUX_RSFEC_ALGN_FAIL, + CGX_ERR_SPUX_MARKER_LOCK_FAIL, + CGX_ERR_SET_FEC_INVALID, + CGX_ERR_SET_FEC_FAIL, + CGX_ERR_MODULE_INVALID, + CGX_ERR_MODULE_NOT_PRESENT, + CGX_ERR_SPEED_CHANGE_INVALID, /* = 28 */ + /* FIXME : add more error types when adding support for new modes */ +}; + +/* LINK speed types */ +enum cgx_link_speed { + CGX_LINK_NONE, + CGX_LINK_10M, + CGX_LINK_100M, + CGX_LINK_1G, + CGX_LINK_2HG, /* 2.5 Gbps */ + CGX_LINK_5G, + CGX_LINK_10G, + CGX_LINK_20G, + CGX_LINK_25G, + CGX_LINK_40G, + CGX_LINK_50G, + CGX_LINK_80G, + CGX_LINK_100G, + CGX_LINK_MAX, +}; + +/* REQUEST ID types. Input to firmware */ +enum cgx_cmd_id { + CGX_CMD_NONE = 0, + CGX_CMD_GET_FW_VER, + CGX_CMD_GET_MAC_ADDR, + CGX_CMD_SET_MTU, + CGX_CMD_GET_LINK_STS, /* optional to user */ + CGX_CMD_LINK_BRING_UP, /* = 5 */ + CGX_CMD_LINK_BRING_DOWN, + CGX_CMD_INTERNAL_LBK, + CGX_CMD_EXTERNAL_LBK, + CGX_CMD_HIGIG, + CGX_CMD_LINK_STAT_CHANGE, /* = 10 */ + CGX_CMD_MODE_CHANGE, /* hot plug support */ + CGX_CMD_INTF_SHUTDOWN, + CGX_CMD_GET_MKEX_SIZE, + CGX_CMD_GET_MKEX_PROFILE, + CGX_CMD_GET_FWD_BASE, /* get base address of shared FW data */ + CGX_CMD_GET_LINK_MODES, /* Supported Link Modes */ + CGX_CMD_SET_LINK_MODE, + CGX_CMD_GET_SUPPORTED_FEC, + CGX_CMD_SET_FEC, + CGX_CMD_GET_AN, /* = 20 */ + CGX_CMD_SET_AN, + CGX_CMD_GET_ADV_LINK_MODES, + CGX_CMD_GET_ADV_FEC, + CGX_CMD_GET_PHY_MOD_TYPE, /* line-side modulation type: NRZ or PAM4 */ + CGX_CMD_SET_PHY_MOD_TYPE, /* = 25 */ + CGX_CMD_PRBS, + CGX_CMD_DISPLAY_EYE, + CGX_CMD_GET_PHY_FEC_STATS, + CGX_CMD_DISPLAY_SERDES, + CGX_CMD_AN_LOOPBACK, /* = 30 */ + CGX_CMD_GET_PERSIST_IGNORE, + CGX_CMD_SET_PERSIST_IGNORE, + CGX_CMD_SET_MAC_ADDR, +}; + +/* async event ids */ +enum cgx_evt_id { + CGX_EVT_NONE, + CGX_EVT_LINK_CHANGE, +}; + +/* event types - cause of interrupt */ +enum cgx_evt_type { + CGX_EVT_ASYNC, + CGX_EVT_CMD_RESP +}; + +enum cgx_stat { + CGX_STAT_SUCCESS, + CGX_STAT_FAIL +}; + +enum cgx_cmd_own { + /* default ownership with kernel/uefi/u-boot */ + CGX_OWN_NON_SECURE_SW, + /* set by kernel/uefi/u-boot after posting a new request to ATF */ + CGX_OWN_FIRMWARE, +}; + +/* Supported LINK MODE enums + * Each link mode is a bit mask of these + * enums which are represented as bits + */ +enum cgx_mode_t { + CGX_MODE_SGMII_BIT = 0, + CGX_MODE_1000_BASEX_BIT, + CGX_MODE_QSGMII_BIT, + CGX_MODE_10G_C2C_BIT, + CGX_MODE_10G_C2M_BIT, + CGX_MODE_10G_KR_BIT, + CGX_MODE_20G_C2C_BIT, + CGX_MODE_25G_C2C_BIT, + CGX_MODE_25G_C2M_BIT, + CGX_MODE_25G_2_C2C_BIT, + CGX_MODE_25G_CR_BIT, + CGX_MODE_25G_KR_BIT, + CGX_MODE_40G_C2C_BIT, + CGX_MODE_40G_C2M_BIT, + CGX_MODE_40G_CR4_BIT, + CGX_MODE_40G_KR4_BIT, + CGX_MODE_40GAUI_C2C_BIT, + CGX_MODE_50G_C2C_BIT, + CGX_MODE_50G_C2M_BIT, + CGX_MODE_50G_4_C2C_BIT, + CGX_MODE_50G_CR_BIT, + CGX_MODE_50G_KR_BIT, + CGX_MODE_80GAUI_C2C_BIT, + CGX_MODE_100G_C2C_BIT, + CGX_MODE_100G_C2M_BIT, + CGX_MODE_100G_CR4_BIT, + CGX_MODE_100G_KR4_BIT, + CGX_MODE_MAX_BIT /* = 29 */ +}; + +/* scratchx(0) CSR used for ATF->non-secure SW communication. + * This acts as the status register + * Provides details on command ack/status, link status, error details + */ + +/* CAUTION : below structures are placed in order based on the bit positions + * For any updates/new bitfields, corresponding structures needs to be updated + */ +struct cgx_evt_sts_s { /* start from bit 0 */ + u64 ack:1; + u64 evt_type:1; /* cgx_evt_type */ + u64 stat:1; /* cgx_stat */ + u64 id:6; /* cgx_evt_id/cgx_cmd_id */ + u64 reserved:55; +}; + +/* all the below structures are in the same memory location of SCRATCHX(0) + * value can be read/written based on command ID + */ + +/* Resp to command IDs with command status as CGX_STAT_FAIL + * Not applicable for commands : + * CGX_CMD_LINK_BRING_UP/DOWN/CGX_EVT_LINK_CHANGE + * check struct cgx_lnk_sts_s comments + */ +struct cgx_err_sts_s { /* start from bit 9 */ + u64 reserved1:9; + u64 type:10; /* cgx_error_type */ + u64 reserved2:35; +}; + +/* Resp to cmd ID as CGX_CMD_GET_FW_VER with cmd status as CGX_STAT_SUCCESS */ +struct cgx_ver_s { /* start from bit 9 */ + u64 reserved1:9; + u64 major_ver:4; + u64 minor_ver:4; + u64 reserved2:47; +}; + +/* Resp to cmd ID as CGX_CMD_GET_MAC_ADDR with cmd status as CGX_STAT_SUCCESS + * Returns each byte of MAC address in a separate bit field + */ +struct cgx_mac_addr_s { /* start from bit 9 */ + u64 reserved1:9; + u64 addr_0:8; + u64 addr_1:8; + u64 addr_2:8; + u64 addr_3:8; + u64 addr_4:8; + u64 addr_5:8; + u64 reserved2:7; +}; + +/* Resp to cmd ID - CGX_CMD_LINK_BRING_UP/DOWN, event ID CGX_EVT_LINK_CHANGE + * status can be either CGX_STAT_FAIL or CGX_STAT_SUCCESS + * In case of CGX_STAT_FAIL, it indicates CGX configuration failed when + * processing link up/down/change command. Both err_type and current link status + * will be updated + * In case of CGX_STAT_SUCCESS, err_type will be CGX_ERR_NONE and current + * link status will be updated + */ +struct cgx_lnk_sts_s { + u64 reserved1:9; + u64 link_up:1; + u64 full_duplex:1; + u64 speed:4; /* cgx_link_speed */ + u64 err_type:10; + u64 an:1; /* Current AN state : enabled/disabled */ + u64 fec:2; /* Current FEC type if enabled, if not 0 */ + u64 port:8; /* Share the current port info if required */ + u64 mode:8; /* cgx_mode_t enum integer value */ + u64 reserved2:20; +}; + +struct sh_fwd_base_s { + u64 reserved1:9; + u64 addr:55; +}; + +struct cgx_link_modes_s { + u64 reserved1:9; + u64 modes:55; +}; + +/* Resp to cmd ID - CGX_CMD_GET_ADV_FEC/CGX_CMD_GET_SUPPORTED_FEC + * fec : 2 bits + * typedef enum cgx_fec_type { + * CGX_FEC_NONE, + * CGX_FEC_BASE_R, + * CGX_FEC_RS + * } fec_type_t; + */ +struct cgx_fec_types_s { + u64 reserved1:9; + u64 fec:2; + u64 reserved2:53; +}; + +/* Resp to cmd ID - CGX_CMD_GET_AN */ +struct cgx_get_an_s { + u64 reserved1:9; + u64 an:1; + u64 reserved2:54; +}; + +/* Resp to cmd ID - CGX_CMD_GET_PHY_MOD_TYPE */ +struct cgx_get_phy_mod_type_s { + u64 reserved1:9; + u64 mod:1; /* 0=NRZ, 1=PAM4 */ + u64 reserved2:54; +}; + +/* Resp to cmd ID - CGX_CMD_GET_PERSIST_IGNORE */ +struct cgx_get_flash_ignore_s { + uint64_t reserved1:9; + uint64_t ignore:1; + uint64_t reserved2:54; +}; + +union cgx_rsp_sts { + /* Fixed, applicable for all commands/events */ + struct cgx_evt_sts_s evt_sts; + /* response to CGX_CMD_LINK_BRINGUP/DOWN/LINK_CHANGE */ + struct cgx_lnk_sts_s link_sts; + /* response to CGX_CMD_GET_FW_VER */ + struct cgx_ver_s ver; + /* response to CGX_CMD_GET_MAC_ADDR */ + struct cgx_mac_addr_s mac_s; + /* response to CGX_CMD_GET_FWD_BASE */ + struct sh_fwd_base_s fwd_base_s; + /* response if evt_status = CMD_FAIL */ + struct cgx_err_sts_s err; + /* response to CGX_CMD_GET_SUPPORTED_FEC */ + struct cgx_fec_types_s supported_fec; + /* response to CGX_CMD_GET_LINK_MODES */ + struct cgx_link_modes_s supported_modes; + /* response to CGX_CMD_GET_ADV_LINK_MODES */ + struct cgx_link_modes_s adv_modes; + /* response to CGX_CMD_GET_ADV_FEC */ + struct cgx_fec_types_s adv_fec; + /* response to CGX_CMD_GET_AN */ + struct cgx_get_an_s an; + /* response to CGX_CMD_GET_PHY_MOD_TYPE */ + struct cgx_get_phy_mod_type_s phy_mod_type; + /* response to CGX_CMD_GET_PERSIST_IGNORE */ + struct cgx_get_flash_ignore_s persist; +#ifdef NT_FW_CONFIG + /* response to CGX_CMD_GET_MKEX_SIZE */ + struct cgx_mcam_profile_sz_s prfl_sz; + /* response to CGX_CMD_GET_MKEX_PROFILE */ + struct cgx_mcam_profile_addr_s prfl_addr; +#endif +}; + +union cgx_scratchx0 { + u64 u; + union cgx_rsp_sts s; +}; + +/* scratchx(1) CSR used for non-secure SW->ATF communication + * This CSR acts as a command register + */ +struct cgx_cmd { /* start from bit 2 */ + u64 reserved1:2; + u64 id:6; /* cgx_request_id */ + u64 reserved2:56; +}; + +/* all the below structures are in the same memory location of SCRATCHX(1) + * corresponding arguments for command Id needs to be updated + */ + +/* Any command using enable/disable as an argument need + * to pass the option via this structure. + * Ex: Loopback, HiGig... + */ +struct cgx_ctl_args { /* start from bit 8 */ + u64 reserved1:8; + u64 enable:1; + u64 reserved2:55; +}; + +/* command argument to be passed for cmd ID - CGX_CMD_SET_MTU */ +struct cgx_mtu_args { + u64 reserved1:8; + u64 size:16; + u64 reserved2:40; +}; + +/* command argument to be passed for cmd ID - CGX_CMD_MODE_CHANGE */ +struct cgx_mode_change_args { + uint64_t reserved1:8; + uint64_t speed:4; /* cgx_link_speed enum */ + uint64_t duplex:1; /* 0 - full duplex, 1 - half duplex */ + uint64_t an:1; /* 0 - disable AN, 1 - enable AN */ + uint64_t port:8; /* device port */ + uint64_t mode:42; +}; + +/* command argument to be passed for cmd ID - CGX_CMD_LINK_CHANGE */ +struct cgx_link_change_args { /* start from bit 8 */ + u64 reserved1:8; + u64 link_up:1; + u64 full_duplex:1; + u64 speed:4; /* cgx_link_speed */ + u64 reserved2:50; +}; + +/* command argument to be passed for cmd ID - CGX_CMD_SET_LINK_MODE */ +struct cgx_set_mode_args { + u64 reserved1:8; + u64 mode:56; +}; + +/* command argument to be passed for cmd ID - CGX_CMD_SET_FEC */ +struct cgx_set_fec_args { + u64 reserved1:8; + u64 fec:2; + u64 reserved2:54; +}; + +/* command argument to be passed for cmd ID - CGX_CMD_SET_PHY_MOD_TYPE */ +struct cgx_set_phy_mod_args { + u64 reserved1:8; + u64 mod:1; /* 0=NRZ, 1=PAM4 */ + u64 reserved2:55; +}; + +/* command argument to be passed for cmd ID - CGX_CMD_SET_PERSIST_IGNORE */ +struct cgx_set_flash_ignore_args { + uint64_t reserved1:8; + uint64_t ignore:1; + uint64_t reserved2:55; +}; + +/* command argument to be passed for cmd ID - CGX_CMD_SET_MAC_ADDR */ +struct cgx_mac_addr_args { + uint64_t reserved1:8; + uint64_t addr:48; + uint64_t pf_id:8; +}; + +struct cgx_prbs_args { + u64 reserved1:8; /* start from bit 8 */ + u64 lane:8; + u64 qlm:8; + u64 stop_on_error:1; + u64 mode:8; + u64 time:31; +}; + +struct cgx_display_eye_args { + u64 reserved1:8; /* start from bit 8 */ + u64 qlm:8; + u64 lane:47; +}; + +union cgx_cmd_s { + u64 own_status:2; /* cgx_cmd_own */ + struct cgx_cmd cmd; + struct cgx_ctl_args cmd_args; + struct cgx_mtu_args mtu_size; + struct cgx_link_change_args lnk_args; /* Input to CGX_CMD_LINK_CHANGE */ + struct cgx_set_mode_args mode_args; + struct cgx_mode_change_args mode_change_args; + struct cgx_set_fec_args fec_args; + struct cgx_set_phy_mod_args phy_mod_args; + struct cgx_set_flash_ignore_args persist_args; + struct cgx_mac_addr_args mac_args; + /* any other arg for command id * like : mtu, dmac filtering control */ + struct cgx_prbs_args prbs_args; + struct cgx_display_eye_args dsp_eye_args; +}; + +union cgx_scratchx1 { + u64 u; + union cgx_cmd_s s; +}; + +#endif /* __CGX_INTF_H__ */ diff --git a/drivers/net/octeontx2/lmt.h b/drivers/net/octeontx2/lmt.h new file mode 100644 index 0000000000..84a7eab814 --- /dev/null +++ b/drivers/net/octeontx2/lmt.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Marvell International Ltd. + */ + +/** + * Atomically adds a signed value to a 64 bit (aligned) memory location, + * and returns previous value. + * + * This version does not perform 'sync' operations to enforce memory + * operations. This should only be used when there are no memory operation + * ordering constraints. (This should NOT be used for reference counting - + * use the standard version instead.) + * + * @param ptr address in memory to add incr to + * @param incr amount to increment memory location by (signed) + * + * @return Value of memory location before increment + */ +static inline s64 atomic_fetch_and_add64_nosync(s64 *ptr, s64 incr) +{ + s64 result; + /* Atomic add with no ordering */ + asm volatile("ldadd %x[i], %x[r], [%[b]]" + : [r] "=r" (result), "+m" (*ptr) + : [i] "r" (incr), [b] "r" (ptr) + : "memory"); + return result; +} + +static inline void lmt_cancel(const struct nix *nix) +{ + writeq(0, nix->lmt_base + LMT_LF_LMTCANCEL()); +} + +static inline u64 *lmt_store_ptr(struct nix *nix) +{ + return (u64 *)((u8 *)(nix->lmt_base) + + LMT_LF_LMTLINEX(0)); +} + +static inline s64 lmt_submit(u64 io_address) +{ + s64 result = 0; + + asm volatile("ldeor xzr, %x[rf],[%[rs]]" + : [rf] "=r"(result) : [rs] "r"(io_address)); + return result; +} diff --git a/drivers/net/octeontx2/nix.c b/drivers/net/octeontx2/nix.c new file mode 100644 index 0000000000..0a3e8e4af0 --- /dev/null +++ b/drivers/net/octeontx2/nix.c @@ -0,0 +1,831 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell International Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "nix.h" +#include "lmt.h" +#include "cgx.h" + +/** + * NIX needs a lot of memory areas. Rather than handle all the failure cases, + * we'll use a wrapper around alloc that prints an error if a memory + * allocation fails. + * + * @param num_elements + * Number of elements to allocate + * @param elem_size Size of each element + * @param msg Text string to show when allocation fails + * + * @return A valid memory location or NULL on failure + */ +static void *nix_memalloc(int num_elements, size_t elem_size, const char *msg) +{ + size_t alloc_size = num_elements * elem_size; + void *base = memalign(CONFIG_SYS_CACHELINE_SIZE, alloc_size); + + if (!base) + printf("NIX: Mem alloc failed for %s (%d * %zu = %zu bytes)\n", + msg ? msg : __func__, num_elements, elem_size, + alloc_size); + else + memset(base, 0, alloc_size); + + debug("NIX: Memory alloc for %s (%d * %zu = %zu bytes) at %p\n", + msg ? msg : __func__, num_elements, elem_size, alloc_size, base); + return base; +} + +int npc_lf_setup(struct nix *nix) +{ + int err; + + err = npc_lf_admin_setup(nix); + if (err) { + printf("%s: Error setting up npc lf admin\n", __func__); + return err; + } + + return 0; +} + +static int npa_setup_pool(struct npa *npa, u32 pool_id, + size_t buffer_size, u32 queue_length, void *buffers[]) +{ + struct { + union npa_lf_aura_op_free0 f0; + union npa_lf_aura_op_free1 f1; + } aura_descr; + int index; + + for (index = 0; index < queue_length; index++) { + buffers[index] = memalign(CONFIG_SYS_CACHELINE_SIZE, + buffer_size); + if (!buffers[index]) { + printf("%s: Out of memory %d, size: %zu\n", + __func__, index, buffer_size); + return -ENOMEM; + } + debug("%s: allocating buffer %d, addr %p size: %zu\n", + __func__, index, buffers[index], buffer_size); + + /* Add the newly obtained pointer to the pool. 128 bit + * writes only. + */ + aura_descr.f0.s.addr = (u64)buffers[index]; + aura_descr.f1.u = 0; + aura_descr.f1.s.aura = pool_id; + st128(npa->npa_base + NPA_LF_AURA_OP_FREE0(), + aura_descr.f0.u, aura_descr.f1.u); + } + + return 0; +} + +int npa_lf_setup(struct nix *nix) +{ + struct rvu_pf *rvu = dev_get_priv(nix->dev); + struct nix_af *nix_af = nix->nix_af; + struct npa *npa; + union npa_af_const npa_af_const; + union npa_aura_s *aura; + union npa_pool_s *pool; + union rvu_func_addr_s block_addr; + int idx; + int stack_page_pointers; + int stack_page_bytes; + int err; + + npa = (struct npa *)calloc(1, sizeof(struct npa)); + if (!npa) { + printf("%s: out of memory for npa instance\n", __func__); + return -ENOMEM; + } + block_addr.u = 0; + block_addr.s.block = RVU_BLOCK_ADDR_E_NPA; + npa->npa_base = rvu->pf_base + block_addr.u; + npa->npa_af = nix_af->npa_af; + nix->npa = npa; + + npa_af_const.u = npa_af_reg_read(npa->npa_af, NPA_AF_CONST()); + stack_page_pointers = npa_af_const.s.stack_page_ptrs; + stack_page_bytes = npa_af_const.s.stack_page_bytes; + + npa->stack_pages[NPA_POOL_RX] = (RQ_QLEN + stack_page_pointers - 1) / + stack_page_pointers; + npa->stack_pages[NPA_POOL_TX] = (SQ_QLEN + stack_page_pointers - 1) / + stack_page_pointers; + npa->stack_pages[NPA_POOL_SQB] = (SQB_QLEN + stack_page_pointers - 1) / + stack_page_pointers; + npa->pool_stack_pointers = stack_page_pointers; + + npa->q_len[NPA_POOL_RX] = RQ_QLEN; + npa->q_len[NPA_POOL_TX] = SQ_QLEN; + npa->q_len[NPA_POOL_SQB] = SQB_QLEN; + + npa->buf_size[NPA_POOL_RX] = MAX_MTU + CONFIG_SYS_CACHELINE_SIZE; + npa->buf_size[NPA_POOL_TX] = MAX_MTU + CONFIG_SYS_CACHELINE_SIZE; + npa->buf_size[NPA_POOL_SQB] = nix_af->sqb_size; + + npa->aura_ctx = nix_memalloc(NPA_POOL_COUNT, + sizeof(union npa_aura_s), + "aura context"); + if (!npa->aura_ctx) { + printf("%s: Out of memory for aura context\n", __func__); + return -ENOMEM; + } + + for (idx = 0; idx < NPA_POOL_COUNT; idx++) { + npa->pool_ctx[idx] = nix_memalloc(1, + sizeof(union npa_pool_s), + "pool context"); + if (!npa->pool_ctx[idx]) { + printf("%s: Out of memory for pool context\n", + __func__); + return -ENOMEM; + } + npa->pool_stack[idx] = nix_memalloc(npa->stack_pages[idx], + stack_page_bytes, + "pool stack"); + if (!npa->pool_stack[idx]) { + printf("%s: Out of memory for pool stack\n", __func__); + return -ENOMEM; + } + } + + err = npa_lf_admin_setup(npa, nix->lf, (dma_addr_t)npa->aura_ctx); + if (err) { + printf("%s: Error setting up NPA LF admin for lf %d\n", + __func__, nix->lf); + return err; + } + + /* Set up the auras */ + for (idx = 0; idx < NPA_POOL_COUNT; idx++) { + aura = npa->aura_ctx + (idx * sizeof(union npa_aura_s)); + pool = npa->pool_ctx[idx]; + debug("%s aura %p pool %p\n", __func__, aura, pool); + memset(aura, 0, sizeof(union npa_aura_s)); + aura->s.fc_ena = 0; + aura->s.pool_addr = (u64)npa->pool_ctx[idx]; + debug("%s aura.s.pool_addr %llx pool_addr %p\n", __func__, + aura->s.pool_addr, npa->pool_ctx[idx]); + aura->s.shift = 64 - __builtin_clzll(npa->q_len[idx]) - 8; + aura->s.count = npa->q_len[idx]; + aura->s.limit = npa->q_len[idx]; + aura->s.ena = 1; + err = npa_attach_aura(nix_af, nix->lf, aura, idx); + if (err) + return err; + + memset(pool, 0, sizeof(*pool)); + pool->s.fc_ena = 0; + pool->s.nat_align = 1; + pool->s.stack_base = (u64)(npa->pool_stack[idx]); + debug("%s pool.s.stack_base %llx stack_base %p\n", __func__, + pool->s.stack_base, npa->pool_stack[idx]); + pool->s.buf_size = + npa->buf_size[idx] / CONFIG_SYS_CACHELINE_SIZE; + pool->s.stack_max_pages = npa->stack_pages[idx]; + pool->s.shift = + 64 - __builtin_clzll(npa->pool_stack_pointers) - 8; + pool->s.ptr_start = 0; + pool->s.ptr_end = (1ULL << 40) - 1; + pool->s.ena = 1; + err = npa_attach_pool(nix_af, nix->lf, pool, idx); + if (err) + return err; + } + + for (idx = 0; idx < NPA_POOL_COUNT; idx++) { + npa->buffers[idx] = nix_memalloc(npa->q_len[idx], + sizeof(void *), + "buffers"); + if (!npa->buffers[idx]) { + printf("%s: Out of memory\n", __func__); + return -ENOMEM; + } + } + + for (idx = 0; idx < NPA_POOL_COUNT; idx++) { + err = npa_setup_pool(npa, idx, npa->buf_size[idx], + npa->q_len[idx], npa->buffers[idx]); + if (err) { + printf("%s: Error setting up pool %d\n", + __func__, idx); + return err; + } + } + return 0; +} + +int npa_lf_shutdown(struct nix *nix) +{ + struct npa *npa = nix->npa; + int err; + int pool; + + err = npa_lf_admin_shutdown(nix->nix_af, nix->lf, NPA_POOL_COUNT); + if (err) { + printf("%s: Error %d shutting down NPA LF admin\n", + __func__, err); + return err; + } + free(npa->aura_ctx); + npa->aura_ctx = NULL; + + for (pool = 0; pool < NPA_POOL_COUNT; pool++) { + free(npa->pool_ctx[pool]); + npa->pool_ctx[pool] = NULL; + free(npa->pool_stack[pool]); + npa->pool_stack[pool] = NULL; + free(npa->buffers[pool]); + npa->buffers[pool] = NULL; + } + + return 0; +} + +int nix_lf_setup(struct nix *nix) +{ + struct nix_af *nix_af = nix->nix_af; + int idx; + int err = -1; + + /* Alloc NIX RQ HW context memory */ + nix->rq_ctx_base = nix_memalloc(nix->rq_cnt, nix_af->rq_ctx_sz, + "RQ CTX"); + if (!nix->rq_ctx_base) + goto error; + memset(nix->rq_ctx_base, 0, nix_af->rq_ctx_sz); + + /* Alloc NIX SQ HW context memory */ + nix->sq_ctx_base = nix_memalloc(nix->sq_cnt, nix_af->sq_ctx_sz, + "SQ CTX"); + if (!nix->sq_ctx_base) + goto error; + memset(nix->sq_ctx_base, 0, nix_af->sq_ctx_sz); + + /* Alloc NIX CQ HW context memory */ + nix->cq_ctx_base = nix_memalloc(nix->cq_cnt, nix_af->cq_ctx_sz, + "CQ CTX"); + if (!nix->cq_ctx_base) + goto error; + memset(nix->cq_ctx_base, 0, nix_af->cq_ctx_sz * NIX_CQ_COUNT); + /* Alloc NIX CQ Ring memory */ + for (idx = 0; idx < NIX_CQ_COUNT; idx++) { + err = qmem_alloc(&nix->cq[idx], CQ_ENTRIES, CQ_ENTRY_SIZE); + if (err) + goto error; + } + + /* Alloc memory for Qints HW contexts */ + nix->qint_base = nix_memalloc(nix_af->qints, nix_af->qint_ctx_sz, + "Qint CTX"); + if (!nix->qint_base) + goto error; + /* Alloc memory for CQints HW contexts */ + nix->cint_base = nix_memalloc(nix_af->cints, nix_af->cint_ctx_sz, + "Cint CTX"); + if (!nix->cint_base) + goto error; + /* Alloc NIX RSS HW context memory and config the base */ + nix->rss_base = nix_memalloc(nix->rss_grps, nix_af->rsse_ctx_sz, + "RSS CTX"); + if (!nix->rss_base) + goto error; + + err = nix_lf_admin_setup(nix); + if (err) { + printf("%s: Error setting up LF\n", __func__); + goto error; + } + + return 0; + +error: + if (nix->rq_ctx_base) + free(nix->rq_ctx_base); + nix->rq_ctx_base = NULL; + if (nix->rq_ctx_base) + free(nix->rq_ctx_base); + nix->rq_ctx_base = NULL; + if (nix->sq_ctx_base) + free(nix->sq_ctx_base); + nix->sq_ctx_base = NULL; + if (nix->cq_ctx_base) + free(nix->cq_ctx_base); + nix->cq_ctx_base = NULL; + + for (idx = 0; idx < NIX_CQ_COUNT; idx++) + qmem_free(&nix->cq[idx]); + + return err; +} + +int nix_lf_shutdown(struct nix *nix) +{ + struct nix_af *nix_af = nix->nix_af; + int index; + int err; + + err = nix_lf_admin_shutdown(nix_af, nix->lf, nix->cq_cnt, + nix->rq_cnt, nix->sq_cnt); + if (err) { + printf("%s: Error shutting down LF admin\n", __func__); + return err; + } + + if (nix->rq_ctx_base) + free(nix->rq_ctx_base); + nix->rq_ctx_base = NULL; + if (nix->rq_ctx_base) + free(nix->rq_ctx_base); + nix->rq_ctx_base = NULL; + if (nix->sq_ctx_base) + free(nix->sq_ctx_base); + nix->sq_ctx_base = NULL; + if (nix->cq_ctx_base) + free(nix->cq_ctx_base); + nix->cq_ctx_base = NULL; + + for (index = 0; index < NIX_CQ_COUNT; index++) + qmem_free(&nix->cq[index]); + + debug("%s: nix lf %d reset --\n", __func__, nix->lf); + return 0; +} + +struct nix *nix_lf_alloc(struct udevice *dev) +{ + union rvu_func_addr_s block_addr; + struct nix *nix; + struct rvu_pf *rvu = dev_get_priv(dev); + struct rvu_af *rvu_af = dev_get_priv(rvu->afdev); + union rvu_pf_func_s pf_func; + int err; + + debug("%s(%s )\n", __func__, dev->name); + + nix = (struct nix *)calloc(1, sizeof(*nix)); + if (!nix) { + printf("%s: Out of memory for nix instance\n", __func__); + return NULL; + } + nix->nix_af = rvu_af->nix_af; + + block_addr.u = 0; + block_addr.s.block = RVU_BLOCK_ADDR_E_NIXX(0); + nix->nix_base = rvu->pf_base + block_addr.u; + block_addr.u = 0; + block_addr.s.block = RVU_BLOCK_ADDR_E_NPC; + nix->npc_base = rvu->pf_base + block_addr.u; + block_addr.u = 0; + block_addr.s.block = RVU_BLOCK_ADDR_E_LMT; + nix->lmt_base = rvu->pf_base + block_addr.u; + + pf_func.u = 0; + pf_func.s.pf = rvu->pfid; + nix->pf_func = pf_func.u; + nix->lf = rvu->nix_lfid; + nix->pf = rvu->pfid; + nix->dev = dev; + nix->sq_cnt = 1; + nix->rq_cnt = 1; + nix->rss_grps = 1; + nix->cq_cnt = 2; + nix->xqe_sz = NIX_CQE_SIZE_W16; + + nix->lmac = nix_get_cgx_lmac(nix->pf); + if (!nix->lmac) { + printf("%s: Error: could not find lmac for pf %d\n", + __func__, nix->pf); + free(nix); + return NULL; + } + nix->lmac->link_num = + NIX_LINK_E_CGXX_LMACX(nix->lmac->cgx->cgx_id, + nix->lmac->lmac_id); + nix->lmac->chan_num = + NIX_CHAN_E_CGXX_LMACX_CHX(nix->lmac->cgx->cgx_id, + nix->lmac->lmac_id, 0); + /* This is rx pkind in 1:1 mapping to NIX_LINK_E */ + nix->lmac->pknd = nix->lmac->link_num; + + cgx_lmac_set_pkind(nix->lmac, nix->lmac->lmac_id, nix->lmac->pknd); + debug("%s(%s CGX%x LMAC%x)\n", __func__, dev->name, + nix->lmac->cgx->cgx_id, nix->lmac->lmac_id); + debug("%s(%s Link %x Chan %x Pknd %x)\n", __func__, dev->name, + nix->lmac->link_num, nix->lmac->chan_num, nix->lmac->pknd); + + err = npa_lf_setup(nix); + if (err) + return NULL; + + err = npc_lf_setup(nix); + if (err) + return NULL; + + err = nix_lf_setup(nix); + if (err) + return NULL; + + return nix; +} + +u64 npa_aura_op_alloc(struct npa *npa, u64 aura_id) +{ + union npa_lf_aura_op_allocx op_allocx; + + op_allocx.u = atomic_fetch_and_add64_nosync(npa->npa_base + + NPA_LF_AURA_OP_ALLOCX(0), aura_id); + return op_allocx.s.addr; +} + +u64 nix_cq_op_status(struct nix *nix, u64 cq_id) +{ + union nixx_lf_cq_op_status op_status; + s64 *reg = nix->nix_base + NIXX_LF_CQ_OP_STATUS(); + + op_status.u = atomic_fetch_and_add64_nosync(reg, cq_id << 32); + return op_status.u; +} + +/* TX */ +static inline void nix_write_lmt(struct nix *nix, void *buffer, + int num_words) +{ + int i; + + u64 *lmt_ptr = lmt_store_ptr(nix); + u64 *ptr = buffer; + + debug("%s lmt_ptr %p %p\n", __func__, nix->lmt_base, lmt_ptr); + for (i = 0; i < num_words; i++) { + debug("%s data %llx lmt_ptr %p\n", __func__, ptr[i], + lmt_ptr + i); + lmt_ptr[i] = ptr[i]; + } +} + +void nix_cqe_tx_pkt_handler(struct nix *nix, void *cqe) +{ + union nix_cqe_hdr_s *txcqe = (union nix_cqe_hdr_s *)cqe; + + debug("%s: txcqe: %p\n", __func__, txcqe); + + if (txcqe->s.cqe_type != NIX_XQE_TYPE_E_SEND) { + printf("%s: Error: Unsupported CQ header type %d\n", + __func__, txcqe->s.cqe_type); + return; + } + nix_pf_reg_write(nix, NIXX_LF_CQ_OP_DOOR(), + (NIX_CQ_TX << 32) | 1); +} + +void nix_lf_flush_tx(struct udevice *dev) +{ + struct rvu_pf *rvu = dev_get_priv(dev); + struct nix *nix = rvu->nix; + union nixx_lf_cq_op_status op_status; + u32 head, tail; + void *cq_tx_base = nix->cq[NIX_CQ_TX].base; + union nix_cqe_hdr_s *cqe; + + /* ack tx cqe entries */ + op_status.u = nix_cq_op_status(nix, NIX_CQ_TX); + head = op_status.s.head; + tail = op_status.s.tail; + head &= (nix->cq[NIX_CQ_TX].qsize - 1); + tail &= (nix->cq[NIX_CQ_TX].qsize - 1); + + debug("%s cq tx head %d tail %d\n", __func__, head, tail); + while (head != tail) { + cqe = cq_tx_base + head * nix->cq[NIX_CQ_TX].entry_sz; + nix_cqe_tx_pkt_handler(nix, cqe); + op_status.u = nix_cq_op_status(nix, NIX_CQ_TX); + head = op_status.s.head; + tail = op_status.s.tail; + head &= (nix->cq[NIX_CQ_TX].qsize - 1); + tail &= (nix->cq[NIX_CQ_TX].qsize - 1); + debug("%s cq tx head %d tail %d\n", __func__, head, tail); + } +} + +int nix_lf_xmit(struct udevice *dev, void *pkt, int pkt_len) +{ + struct rvu_pf *rvu = dev_get_priv(dev); + struct nix *nix = rvu->nix; + struct nix_tx_dr tx_dr; + int dr_sz = (sizeof(struct nix_tx_dr) + 15) / 16 - 1; + s64 result; + void *packet; + + nix_lf_flush_tx(dev); + memset((void *)&tx_dr, 0, sizeof(struct nix_tx_dr)); + /* Dump TX packet in to NPA buffer */ + packet = (void *)npa_aura_op_alloc(nix->npa, NPA_POOL_TX); + if (!packet) { + printf("%s TX buffers unavailable\n", __func__); + return -1; + } + memcpy(packet, pkt, pkt_len); + debug("%s TX buffer %p\n", __func__, packet); + + tx_dr.hdr.s.aura = NPA_POOL_TX; + tx_dr.hdr.s.df = 0; + tx_dr.hdr.s.pnc = 1; + tx_dr.hdr.s.sq = 0; + tx_dr.hdr.s.total = pkt_len; + tx_dr.hdr.s.sizem1 = dr_sz - 2; /* FIXME - for now hdr+sg+sg1addr */ + debug("%s dr_sz %d\n", __func__, dr_sz); + + tx_dr.tx_sg.s.segs = 1; + tx_dr.tx_sg.s.subdc = NIX_SUBDC_E_SG; + tx_dr.tx_sg.s.seg1_size = pkt_len; + tx_dr.tx_sg.s.ld_type = NIX_SENDLDTYPE_E_LDT; + tx_dr.sg1_addr = (dma_addr_t)packet; + +#define DEBUG_PKT +#ifdef DEBUG_PKT + debug("TX PKT Data\n"); + for (int i = 0; i < pkt_len; i++) { + if (i && (i % 8 == 0)) + debug("\n"); + debug("%02x ", *((u8 *)pkt + i)); + } + debug("\n"); +#endif + do { + nix_write_lmt(nix, &tx_dr, (dr_sz - 1) * 2); + __iowmb(); + result = lmt_submit((u64)(nix->nix_base + + NIXX_LF_OP_SENDX(0))); + WATCHDOG_RESET(); + } while (result == 0); + + return 0; +} + +/* RX */ +void nix_lf_flush_rx(struct udevice *dev) +{ + struct rvu_pf *rvu = dev_get_priv(dev); + struct nix *nix = rvu->nix; + union nixx_lf_cq_op_status op_status; + void *cq_rx_base = nix->cq[NIX_CQ_RX].base; + struct nix_rx_dr *rx_dr; + union nix_rx_parse_s *rxparse; + u32 head, tail; + u32 rx_cqe_sz = nix->cq[NIX_CQ_RX].entry_sz; + u64 *seg; + + /* flush rx cqe entries */ + op_status.u = nix_cq_op_status(nix, NIX_CQ_RX); + head = op_status.s.head; + tail = op_status.s.tail; + head &= (nix->cq[NIX_CQ_RX].qsize - 1); + tail &= (nix->cq[NIX_CQ_RX].qsize - 1); + + debug("%s cq rx head %d tail %d\n", __func__, head, tail); + while (head != tail) { + rx_dr = (struct nix_rx_dr *)cq_rx_base + head * rx_cqe_sz; + rxparse = &rx_dr->rx_parse; + + debug("%s: rx parse: %p\n", __func__, rxparse); + debug("%s: rx parse: desc_sizem1 %x pkt_lenm1 %x\n", + __func__, rxparse->s.desc_sizem1, rxparse->s.pkt_lenm1); + + seg = (dma_addr_t *)(&rx_dr->rx_sg + 1); + + st128(nix->npa->npa_base + NPA_LF_AURA_OP_FREE0(), + seg[0], (1ULL << 63) | NPA_POOL_RX); + + debug("%s return %llx to NPA\n", __func__, seg[0]); + nix_pf_reg_write(nix, NIXX_LF_CQ_OP_DOOR(), + (NIX_CQ_RX << 32) | 1); + + op_status.u = nix_cq_op_status(nix, NIX_CQ_RX); + head = op_status.s.head; + tail = op_status.s.tail; + head &= (nix->cq[NIX_CQ_RX].qsize - 1); + tail &= (nix->cq[NIX_CQ_RX].qsize - 1); + debug("%s cq rx head %d tail %d\n", __func__, head, tail); + } +} + +int nix_lf_free_pkt(struct udevice *dev, uchar *pkt, int pkt_len) +{ + struct rvu_pf *rvu = dev_get_priv(dev); + struct nix *nix = rvu->nix; + + /* Return rx packet to NPA */ + debug("%s return %p to NPA\n", __func__, pkt); + st128(nix->npa->npa_base + NPA_LF_AURA_OP_FREE0(), (u64)pkt, + (1ULL << 63) | NPA_POOL_RX); + nix_pf_reg_write(nix, NIXX_LF_CQ_OP_DOOR(), + (NIX_CQ_RX << 32) | 1); + + nix_lf_flush_tx(dev); + return 0; +} + +int nix_lf_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct rvu_pf *rvu = dev_get_priv(dev); + struct nix *nix = rvu->nix; + union nixx_lf_cq_op_status op_status; + void *cq_rx_base = nix->cq[NIX_CQ_RX].base; + struct nix_rx_dr *rx_dr; + union nix_rx_parse_s *rxparse; + void *pkt, *cqe; + int pkt_len = 0; + u64 *addr; + u32 head, tail; + + /* fetch rx cqe entries */ + op_status.u = nix_cq_op_status(nix, NIX_CQ_RX); + head = op_status.s.head; + tail = op_status.s.tail; + head &= (nix->cq[NIX_CQ_RX].qsize - 1); + tail &= (nix->cq[NIX_CQ_RX].qsize - 1); + debug("%s cq rx head %d tail %d\n", __func__, head, tail); + if (head == tail) + return -EAGAIN; + + debug("%s: rx_base %p head %d sz %d\n", __func__, cq_rx_base, head, + nix->cq[NIX_CQ_RX].entry_sz); + cqe = cq_rx_base + head * nix->cq[NIX_CQ_RX].entry_sz; + rx_dr = (struct nix_rx_dr *)cqe; + rxparse = &rx_dr->rx_parse; + + debug("%s: rx completion: %p\n", __func__, cqe); + debug("%s: rx dr: %p\n", __func__, rx_dr); + debug("%s: rx parse: %p\n", __func__, rxparse); + debug("%s: rx parse: desc_sizem1 %x pkt_lenm1 %x\n", + __func__, rxparse->s.desc_sizem1, rxparse->s.pkt_lenm1); + debug("%s: rx parse: pkind %x chan %x\n", + __func__, rxparse->s.pkind, rxparse->s.chan); + + if (rx_dr->hdr.s.cqe_type != NIX_XQE_TYPE_E_RX) { + printf("%s: Error: Unsupported CQ header type in Rx %d\n", + __func__, rx_dr->hdr.s.cqe_type); + return -1; + } + + pkt_len = rxparse->s.pkt_lenm1 + 1; + addr = (dma_addr_t *)(&rx_dr->rx_sg + 1); + pkt = (void *)addr[0]; + + debug("%s: segs: %d (%d@0x%llx, %d@0x%llx, %d@0x%llx)\n", __func__, + rx_dr->rx_sg.s.segs, rx_dr->rx_sg.s.seg1_size, addr[0], + rx_dr->rx_sg.s.seg2_size, addr[1], + rx_dr->rx_sg.s.seg3_size, addr[2]); + if (pkt_len < rx_dr->rx_sg.s.seg1_size + rx_dr->rx_sg.s.seg2_size + + rx_dr->rx_sg.s.seg3_size) { + debug("%s: Error: rx buffer size too small\n", __func__); + return -1; + } + + __iowmb(); +#define DEBUG_PKT +#ifdef DEBUG_PKT + debug("RX PKT Data\n"); + for (int i = 0; i < pkt_len; i++) { + if (i && (i % 8 == 0)) + debug("\n"); + debug("%02x ", *((u8 *)pkt + i)); + } + debug("\n"); +#endif + + *packetp = (uchar *)pkt; + + return pkt_len; +} + +int nix_lf_setup_mac(struct udevice *dev) +{ + struct rvu_pf *rvu = dev_get_priv(dev); + struct nix *nix = rvu->nix; + struct eth_pdata *pdata = dev_get_platdata(dev); + + /* If lower level firmware fails to set proper MAC + * u-boot framework updates MAC to random address. + * Use this hook to update mac address in cgx lmac + * and call mac filter setup to update new address. + */ + if (memcmp(nix->lmac->mac_addr, pdata->enetaddr, ARP_HLEN)) { + memcpy(nix->lmac->mac_addr, pdata->enetaddr, 6); + eth_env_set_enetaddr_by_index("eth", rvu->dev->seq, + pdata->enetaddr); + cgx_lmac_mac_filter_setup(nix->lmac); + /* Update user given MAC address to ATF for update + * in sh_fwdata to use in Linux. + */ + cgx_intf_set_macaddr(dev); + debug("%s: lMAC %pM\n", __func__, nix->lmac->mac_addr); + debug("%s: pMAC %pM\n", __func__, pdata->enetaddr); + } + debug("%s: setupMAC %pM\n", __func__, pdata->enetaddr); + return 0; +} + +void nix_lf_halt(struct udevice *dev) +{ + struct rvu_pf *rvu = dev_get_priv(dev); + struct nix *nix = rvu->nix; + + cgx_lmac_rx_tx_enable(nix->lmac, nix->lmac->lmac_id, false); + + mdelay(1); + + /* Flush tx and rx descriptors */ + nix_lf_flush_rx(dev); + nix_lf_flush_tx(dev); +} + +int nix_lf_init(struct udevice *dev) +{ + struct rvu_pf *rvu = dev_get_priv(dev); + struct nix *nix = rvu->nix; + struct lmac *lmac = nix->lmac; + int ret; + u64 link_sts; + u8 link, speed; + u16 errcode; + + printf("Waiting for CGX%d LMAC%d [%s] link status...", + lmac->cgx->cgx_id, lmac->lmac_id, + lmac_type_to_str[lmac->lmac_type]); + + if (lmac->init_pend) { + /* Bring up LMAC */ + ret = cgx_lmac_link_enable(lmac, lmac->lmac_id, + true, &link_sts); + lmac->init_pend = 0; + } else { + ret = cgx_lmac_link_status(lmac, lmac->lmac_id, &link_sts); + } + + if (ret) { + printf(" [Down]\n"); + return -1; + } + + link = link_sts & 0x1; + speed = (link_sts >> 2) & 0xf; + errcode = (link_sts >> 6) & 0x2ff; + debug("%s: link %x speed %x errcode %x\n", + __func__, link, speed, errcode); + + /* Print link status */ + printf(" [%s]\n", link ? lmac_speed_to_str[speed] : "Down"); + if (!link) + return -1; + + if (!lmac->init_pend) + cgx_lmac_rx_tx_enable(lmac, lmac->lmac_id, true); + + return 0; +} + +void nix_get_cgx_lmac_id(struct udevice *dev, int *cgxid, int *lmacid) +{ + struct rvu_pf *rvu = dev_get_priv(dev); + struct nix *nix = rvu->nix; + struct lmac *lmac = nix->lmac; + + *cgxid = lmac->cgx->cgx_id; + *lmacid = lmac->lmac_id; +} + +void nix_print_mac_info(struct udevice *dev) +{ + struct rvu_pf *rvu = dev_get_priv(dev); + struct nix *nix = rvu->nix; + struct lmac *lmac = nix->lmac; + + printf(" CGX%d LMAC%d [%s]", lmac->cgx->cgx_id, lmac->lmac_id, + lmac_type_to_str[lmac->lmac_type]); +} + diff --git a/drivers/net/octeontx2/nix.h b/drivers/net/octeontx2/nix.h new file mode 100644 index 0000000000..03260dddb3 --- /dev/null +++ b/drivers/net/octeontx2/nix.h @@ -0,0 +1,353 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Marvell International Ltd. + */ + +#ifndef __NIX_H__ +#define __NIX_H__ + +#include +#include +#include "rvu.h" + +/** Maximum number of LMACs supported */ +#define MAX_LMAC 12 + +/* NIX RX action operation*/ +#define NIX_RX_ACTIONOP_DROP (0x0ull) +#define NIX_RX_ACTIONOP_UCAST (0x1ull) +#define NIX_RX_ACTIONOP_UCAST_IPSEC (0x2ull) +#define NIX_RX_ACTIONOP_MCAST (0x3ull) +#define NIX_RX_ACTIONOP_RSS (0x4ull) + +/* NIX TX action operation*/ +#define NIX_TX_ACTIONOP_DROP (0x0ull) +#define NIX_TX_ACTIONOP_UCAST_DEFAULT (0x1ull) +#define NIX_TX_ACTIONOP_UCAST_CHAN (0x2ull) +#define NIX_TX_ACTIONOP_MCAST (0x3ull) +#define NIX_TX_ACTIONOP_DROP_VIOL (0x5ull) + +#define NIX_INTF_RX 0 +#define NIX_INTF_TX 1 + +#define NIX_INTF_TYPE_CGX 0 +#define NIX_INTF_TYPE_LBK 1 +#define NIX_MAX_HW_MTU 9212 +#define NIX_MIN_HW_MTU 40 +#define MAX_MTU 1536 + +#define NPA_POOL_COUNT 3 +#define NPA_AURA_COUNT(x) (1ULL << ((x) + 6)) +#define NPA_POOL_RX 0ULL +#define NPA_POOL_TX 1ULL +#define NPA_POOL_SQB 2ULL +#define RQ_QLEN Q_COUNT(Q_SIZE_1K) +#define SQ_QLEN Q_COUNT(Q_SIZE_1K) +#define SQB_QLEN Q_COUNT(Q_SIZE_16) + +#define NIX_CQ_RX 0ULL +#define NIX_CQ_TX 1ULL +#define NIX_CQ_COUNT 2ULL +#define NIX_CQE_SIZE_W16 (16 * sizeof(u64)) +#define NIX_CQE_SIZE_W64 (64 * sizeof(u64)) + +/** Size of aura hardware context */ +#define NPA_AURA_HW_CTX_SIZE 48 +/** Size of pool hardware context */ +#define NPA_POOL_HW_CTX_SIZE 64 + +#define NPA_DEFAULT_PF_FUNC 0xffff + +#define NIX_CHAN_CGX_LMAC_CHX(a, b, c) (0x800 + 0x100 * (a) + 0x10 * (b) + (c)) +#define NIX_LINK_CGX_LMAC(a, b) (0 + 4 * (a) + (b)) +#define NIX_LINK_LBK(a) (12 + (a)) +#define NIX_CHAN_LBK_CHX(a, b) (0 + 0x100 * (a) + (b)) +#define MAX_LMAC_PKIND 12 + +/** Number of Admin queue entries */ +#define AQ_RING_SIZE Q_COUNT(Q_SIZE_16) + +/** Each completion queue contains 256 entries, see NIC_CQ_CTX_S[qsize] */ +#define CQS_QSIZE Q_SIZE_256 +#define CQ_ENTRIES Q_COUNT(CQS_QSIZE) +/** + * Each completion queue entry contains 128 bytes, see + * NIXX_AF_LFX_CFG[xqe_size] + */ +#define CQ_ENTRY_SIZE NIX_CQE_SIZE_W16 + +enum npa_aura_size { + NPA_AURA_SZ_0, + NPA_AURA_SZ_128, + NPA_AURA_SZ_256, + NPA_AURA_SZ_512, + NPA_AURA_SZ_1K, + NPA_AURA_SZ_2K, + NPA_AURA_SZ_4K, + NPA_AURA_SZ_8K, + NPA_AURA_SZ_16K, + NPA_AURA_SZ_32K, + NPA_AURA_SZ_64K, + NPA_AURA_SZ_128K, + NPA_AURA_SZ_256K, + NPA_AURA_SZ_512K, + NPA_AURA_SZ_1M, + NPA_AURA_SZ_MAX, +}; + +#define NPA_AURA_SIZE_DEFAULT NPA_AURA_SZ_128 + +/* NIX Transmit schedulers */ +enum nix_scheduler { + NIX_TXSCH_LVL_SMQ = 0x0, + NIX_TXSCH_LVL_MDQ = 0x0, + NIX_TXSCH_LVL_TL4 = 0x1, + NIX_TXSCH_LVL_TL3 = 0x2, + NIX_TXSCH_LVL_TL2 = 0x3, + NIX_TXSCH_LVL_TL1 = 0x4, + NIX_TXSCH_LVL_CNT = 0x5, +}; + +struct cgx; + +struct nix_stats { + u64 num_packets; + u64 num_bytes; +}; + +struct nix; +struct lmac; + +struct npa_af { + void __iomem *npa_af_base; + struct admin_queue aq; + u32 aura; +}; + +struct npa { + struct npa_af *npa_af; + void __iomem *npa_base; + void __iomem *npc_base; + void __iomem *lmt_base; + /** Hardware aura context */ + void *aura_ctx; + /** Hardware pool context */ + void *pool_ctx[NPA_POOL_COUNT]; + void *pool_stack[NPA_POOL_COUNT]; + void **buffers[NPA_POOL_COUNT]; + u32 pool_stack_pages[NPA_POOL_COUNT]; + u32 pool_stack_pointers; + u32 q_len[NPA_POOL_COUNT]; + u32 buf_size[NPA_POOL_COUNT]; + u32 stack_pages[NPA_POOL_COUNT]; +}; + +struct nix_af { + struct udevice *dev; + struct nix *lmacs[MAX_LMAC]; + struct npa_af *npa_af; + void __iomem *nix_af_base; + void __iomem *npc_af_base; + struct admin_queue aq; + u8 num_lmacs; + s8 index; + u8 xqe_size; + u32 sqb_size; + u32 qints; + u32 cints; + u32 sq_ctx_sz; + u32 rq_ctx_sz; + u32 cq_ctx_sz; + u32 rsse_ctx_sz; + u32 cint_ctx_sz; + u32 qint_ctx_sz; +}; + +struct nix_tx_dr { + union nix_send_hdr_s hdr; + union nix_send_sg_s tx_sg; + dma_addr_t sg1_addr; + dma_addr_t sg2_addr; + dma_addr_t sg3_addr; + u64 in_use; +}; + +struct nix_rx_dr { + union nix_cqe_hdr_s hdr; + union nix_rx_parse_s rx_parse; + union nix_rx_sg_s rx_sg; +}; + +struct nix { + struct udevice *dev; + struct eth_device *netdev; + struct nix_af *nix_af; + struct npa *npa; + struct lmac *lmac; + union nix_cint_hw_s *cint_base; + union nix_cq_ctx_s *cq_ctx_base; + union nix_qint_hw_s *qint_base; + union nix_rq_ctx_s *rq_ctx_base; + union nix_rsse_s *rss_base; + union nix_sq_ctx_s *sq_ctx_base; + void *cqe_base; + struct qmem sq; + struct qmem cq[NIX_CQ_COUNT]; + struct qmem rq; + struct qmem rss; + struct qmem cq_ints; + struct qmem qints; + char name[16]; + void __iomem *nix_base; /** PF reg base */ + void __iomem *npc_base; + void __iomem *lmt_base; + struct nix_stats tx_stats; + struct nix_stats rx_stats; + u32 aura; + int pknd; + int lf; + int pf; + u16 pf_func; + u32 rq_cnt; /** receive queues count */ + u32 sq_cnt; /** send queues count */ + u32 cq_cnt; /** completion queues count */ + u16 rss_sz; + u16 sqb_size; + u8 rss_grps; + u8 xqe_sz; +}; + +struct nix_aq_cq_dis { + union nix_aq_res_s resp ALIGNED; + union nix_cq_ctx_s cq ALIGNED; + union nix_cq_ctx_s mcq ALIGNED; +}; + +struct nix_aq_rq_dis { + union nix_aq_res_s resp ALIGNED; + union nix_rq_ctx_s rq ALIGNED; + union nix_rq_ctx_s mrq ALIGNED; +}; + +struct nix_aq_sq_dis { + union nix_aq_res_s resp ALIGNED; + union nix_sq_ctx_s sq ALIGNED; + union nix_sq_ctx_s msq ALIGNED; +}; + +struct nix_aq_cq_request { + union nix_aq_res_s resp ALIGNED; + union nix_cq_ctx_s cq ALIGNED; +}; + +struct nix_aq_rq_request { + union nix_aq_res_s resp ALIGNED; + union nix_rq_ctx_s rq ALIGNED; +}; + +struct nix_aq_sq_request { + union nix_aq_res_s resp ALIGNED; + union nix_sq_ctx_s sq ALIGNED; +}; + +static inline u64 nix_af_reg_read(struct nix_af *nix_af, u64 offset) +{ + u64 val = readq(nix_af->nix_af_base + offset); + + debug("%s reg %p val %llx\n", __func__, nix_af->nix_af_base + offset, + val); + return val; +} + +static inline void nix_af_reg_write(struct nix_af *nix_af, u64 offset, + u64 val) +{ + debug("%s reg %p val %llx\n", __func__, nix_af->nix_af_base + offset, + val); + writeq(val, nix_af->nix_af_base + offset); +} + +static inline u64 nix_pf_reg_read(struct nix *nix, u64 offset) +{ + u64 val = readq(nix->nix_base + offset); + + debug("%s reg %p val %llx\n", __func__, nix->nix_base + offset, + val); + return val; +} + +static inline void nix_pf_reg_write(struct nix *nix, u64 offset, + u64 val) +{ + debug("%s reg %p val %llx\n", __func__, nix->nix_base + offset, + val); + writeq(val, nix->nix_base + offset); +} + +static inline u64 npa_af_reg_read(struct npa_af *npa_af, u64 offset) +{ + u64 val = readq(npa_af->npa_af_base + offset); + + debug("%s reg %p val %llx\n", __func__, npa_af->npa_af_base + offset, + val); + return val; +} + +static inline void npa_af_reg_write(struct npa_af *npa_af, u64 offset, + u64 val) +{ + debug("%s reg %p val %llx\n", __func__, npa_af->npa_af_base + offset, + val); + writeq(val, npa_af->npa_af_base + offset); +} + +static inline u64 npc_af_reg_read(struct nix_af *nix_af, u64 offset) +{ + u64 val = readq(nix_af->npc_af_base + offset); + + debug("%s reg %p val %llx\n", __func__, nix_af->npc_af_base + offset, + val); + return val; +} + +static inline void npc_af_reg_write(struct nix_af *nix_af, u64 offset, + u64 val) +{ + debug("%s reg %p val %llx\n", __func__, nix_af->npc_af_base + offset, + val); + writeq(val, nix_af->npc_af_base + offset); +} + +int npa_attach_aura(struct nix_af *nix_af, int lf, + const union npa_aura_s *desc, u32 aura_id); +int npa_attach_pool(struct nix_af *nix_af, int lf, + const union npa_pool_s *desc, u32 pool_id); +int npa_af_setup(struct npa_af *npa_af); +int npa_af_shutdown(struct npa_af *npa_af); +int npa_lf_setup(struct nix *nix); +int npa_lf_shutdown(struct nix *nix); +int npa_lf_admin_setup(struct npa *npa, int lf, dma_addr_t aura_base); +int npa_lf_admin_shutdown(struct nix_af *nix_af, int lf, u32 pool_count); + +int npc_lf_admin_setup(struct nix *nix); +int npc_af_shutdown(struct nix_af *nix_af); + +int nix_af_setup(struct nix_af *nix_af); +int nix_af_shutdown(struct nix_af *nix_af); +int nix_lf_setup(struct nix *nix); +int nix_lf_shutdown(struct nix *nix); +struct nix *nix_lf_alloc(struct udevice *dev); +int nix_lf_admin_setup(struct nix *nix); +int nix_lf_admin_shutdown(struct nix_af *nix_af, int lf, + u32 cq_count, u32 rq_count, u32 sq_count); +struct rvu_af *get_af(void); + +int nix_lf_setup_mac(struct udevice *dev); +int nix_lf_read_rom_mac(struct udevice *dev); +void nix_lf_halt(struct udevice *dev); +int nix_lf_free_pkt(struct udevice *dev, uchar *pkt, int pkt_len); +int nix_lf_recv(struct udevice *dev, int flags, uchar **packetp); +int nix_lf_init(struct udevice *dev); +int nix_lf_xmit(struct udevice *dev, void *pkt, int pkt_len); + +#endif /* __NIX_H__ */ diff --git a/drivers/net/octeontx2/nix_af.c b/drivers/net/octeontx2/nix_af.c new file mode 100644 index 0000000000..d513917ee7 --- /dev/null +++ b/drivers/net/octeontx2/nix_af.c @@ -0,0 +1,1102 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell International Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nix.h" +#include "lmt.h" +#include "cgx.h" + +static struct nix_aq_cq_dis cq_dis ALIGNED; +static struct nix_aq_rq_dis rq_dis ALIGNED; +static struct nix_aq_sq_dis sq_dis ALIGNED; + +/*************** + * NPA API + ***************/ +int npa_attach_aura(struct nix_af *nix_af, int lf, + const union npa_aura_s *desc, u32 aura_id) +{ + struct npa_af *npa = nix_af->npa_af; + union npa_aq_inst_s *inst; + union npa_aq_res_s *res; + union npa_af_aq_status aq_stat; + union npa_aura_s *context; + u64 head; + ulong start; + + debug("%s(%p, %d, %p, %u)\n", __func__, nix_af, lf, desc, aura_id); + aq_stat.u = npa_af_reg_read(npa, NPA_AF_AQ_STATUS()); + head = aq_stat.s.head_ptr; + inst = (union npa_aq_inst_s *)(npa->aq.inst.base) + head; + res = (union npa_aq_res_s *)(npa->aq.res.base); + + memset(inst, 0, sizeof(*inst)); + inst->s.lf = lf; + inst->s.doneint = 0; + inst->s.ctype = NPA_AQ_CTYPE_E_AURA; + inst->s.op = NPA_AQ_INSTOP_E_INIT; + inst->s.res_addr = npa->aq.res.iova; + inst->s.cindex = aura_id; + + context = (union npa_aura_s *)(npa->aq.res.base + + CONFIG_SYS_CACHELINE_SIZE); + memset(npa->aq.res.base, 0, npa->aq.res.entry_sz); + memcpy(context, desc, sizeof(union npa_aura_s)); + __iowmb(); + npa_af_reg_write(npa, NPA_AF_AQ_DOOR(), 1); + + start = get_timer(0); + while ((res->s.compcode == NPA_AQ_COMP_E_NOTDONE) && + (get_timer(start) < 1000)) + WATCHDOG_RESET(); + if (res->s.compcode != NPA_AQ_COMP_E_GOOD) { + printf("%s: Error: result 0x%x not good\n", + __func__, res->s.compcode); + return -1; + } + + return 0; +} + +int npa_attach_pool(struct nix_af *nix_af, int lf, + const union npa_pool_s *desc, u32 pool_id) +{ + union npa_aq_inst_s *inst; + union npa_aq_res_s *res; + union npa_af_aq_status aq_stat; + struct npa_af *npa = nix_af->npa_af; + union npa_aura_s *context; + u64 head; + ulong start; + + debug("%s(%p, %d, %p, %u)\n", __func__, nix_af, lf, desc, pool_id); + aq_stat.u = npa_af_reg_read(npa, NPA_AF_AQ_STATUS()); + head = aq_stat.s.head_ptr; + + inst = (union npa_aq_inst_s *)(npa->aq.inst.base) + head; + res = (union npa_aq_res_s *)(npa->aq.res.base); + + memset(inst, 0, sizeof(*inst)); + inst->s.cindex = pool_id; + inst->s.lf = lf; + inst->s.doneint = 0; + inst->s.ctype = NPA_AQ_CTYPE_E_POOL; + inst->s.op = NPA_AQ_INSTOP_E_INIT; + inst->s.res_addr = npa->aq.res.iova; + + context = (union npa_aura_s *)(npa->aq.res.base + + CONFIG_SYS_CACHELINE_SIZE); + memset(npa->aq.res.base, 0, npa->aq.res.entry_sz); + memcpy(context, desc, sizeof(union npa_aura_s)); + __iowmb(); + npa_af_reg_write(npa, NPA_AF_AQ_DOOR(), 1); + + start = get_timer(0); + while ((res->s.compcode == NPA_AQ_COMP_E_NOTDONE) && + (get_timer(start) < 1000)) + WATCHDOG_RESET(); + + if (res->s.compcode != NPA_AQ_COMP_E_GOOD) { + printf("%s: Error: result 0x%x not good\n", + __func__, res->s.compcode); + return -1; + } + + return 0; +} + +int npa_lf_admin_setup(struct npa *npa, int lf, dma_addr_t aura_base) +{ + union npa_af_lf_rst lf_rst; + union npa_af_lfx_auras_cfg auras_cfg; + struct npa_af *npa_af = npa->npa_af; + + debug("%s(%p, %d, 0x%llx)\n", __func__, npa_af, lf, aura_base); + lf_rst.u = 0; + lf_rst.s.exec = 1; + lf_rst.s.lf = lf; + npa_af_reg_write(npa_af, NPA_AF_LF_RST(), lf_rst.u); + + do { + lf_rst.u = npa_af_reg_read(npa_af, NPA_AF_LF_RST()); + WATCHDOG_RESET(); + } while (lf_rst.s.exec); + + /* Set Aura size and enable caching of contexts */ + auras_cfg.u = npa_af_reg_read(npa_af, NPA_AF_LFX_AURAS_CFG(lf)); + auras_cfg.s.loc_aura_size = NPA_AURA_SIZE_DEFAULT; //FIXME aura_size; + auras_cfg.s.caching = 1; + auras_cfg.s.rmt_aura_size = 0; + auras_cfg.s.rmt_aura_offset = 0; + auras_cfg.s.rmt_lf = 0; + npa_af_reg_write(npa_af, NPA_AF_LFX_AURAS_CFG(lf), auras_cfg.u); + /* Configure aura HW context base */ + npa_af_reg_write(npa_af, NPA_AF_LFX_LOC_AURAS_BASE(lf), + aura_base); + + return 0; +} + +int npa_lf_admin_shutdown(struct nix_af *nix_af, int lf, u32 pool_count) +{ + int pool_id; + u32 head; + union npa_aq_inst_s *inst; + union npa_aq_res_s *res; + struct npa_aq_pool_request { + union npa_aq_res_s resp ALIGNED; + union npa_pool_s p0 ALIGNED; + union npa_pool_s p1 ALIGNED; + } pool_req ALIGNED; + struct npa_aq_aura_request { + union npa_aq_res_s resp ALIGNED; + union npa_aura_s a0 ALIGNED; + union npa_aura_s a1 ALIGNED; + } aura_req ALIGNED; + union npa_af_aq_status aq_stat; + union npa_af_lf_rst lf_rst; + struct npa_af *npa = nix_af->npa_af; + ulong start; + + for (pool_id = 0; pool_id < pool_count; pool_id++) { + aq_stat.u = npa_af_reg_read(npa, NPA_AF_AQ_STATUS()); + head = aq_stat.s.head_ptr; + inst = (union npa_aq_inst_s *)(npa->aq.inst.base) + head; + res = &pool_req.resp; + + memset(inst, 0, sizeof(*inst)); + inst->s.cindex = pool_id; + inst->s.lf = lf; + inst->s.doneint = 0; + inst->s.ctype = NPA_AQ_CTYPE_E_POOL; + inst->s.op = NPA_AQ_INSTOP_E_WRITE; + inst->s.res_addr = (u64)&pool_req.resp; + + memset((void *)&pool_req, 0, sizeof(pool_req)); + pool_req.p0.s.ena = 0; + pool_req.p1.s.ena = 1; /* Write mask */ + __iowmb(); + + npa_af_reg_write(npa, NPA_AF_AQ_DOOR(), 1); + + start = get_timer(0); + while ((res->s.compcode == NPA_AQ_COMP_E_NOTDONE) && + (get_timer(start) < 1000)) + WATCHDOG_RESET(); + + if (res->s.compcode != NPA_AQ_COMP_E_GOOD) { + printf("%s: Error: result 0x%x not good for lf %d\n" + " aura id %d", __func__, res->s.compcode, lf, + pool_id); + return -1; + } + debug("%s(LF %d, pool id %d) disabled\n", __func__, lf, + pool_id); + } + + for (pool_id = 0; pool_id < pool_count; pool_id++) { + aq_stat.u = npa_af_reg_read(npa, NPA_AF_AQ_STATUS()); + head = aq_stat.s.head_ptr; + inst = (union npa_aq_inst_s *)(npa->aq.inst.base) + head; + res = &aura_req.resp; + + memset(inst, 0, sizeof(*inst)); + inst->s.cindex = pool_id; + inst->s.lf = lf; + inst->s.doneint = 0; + inst->s.ctype = NPA_AQ_CTYPE_E_AURA; + inst->s.op = NPA_AQ_INSTOP_E_WRITE; + inst->s.res_addr = (u64)&aura_req.resp; + + memset((void *)&aura_req, 0, sizeof(aura_req)); + aura_req.a0.s.ena = 0; + aura_req.a1.s.ena = 1; /* Write mask */ + __iowmb(); + + npa_af_reg_write(npa, NPA_AF_AQ_DOOR(), 1); + + start = get_timer(0); + while ((res->s.compcode == NPA_AQ_COMP_E_NOTDONE) && + (get_timer(start) < 1000)) + WATCHDOG_RESET(); + + if (res->s.compcode != NPA_AQ_COMP_E_GOOD) { + printf("%s: Error: result 0x%x not good for lf %d\n" + " aura id %d", __func__, res->s.compcode, lf, + pool_id); + return -1; + } + debug("%s(LF %d, aura id %d) disabled\n", __func__, lf, + pool_id); + } + + /* Reset the LF */ + lf_rst.u = 0; + lf_rst.s.exec = 1; + lf_rst.s.lf = lf; + npa_af_reg_write(npa, NPA_AF_LF_RST(), lf_rst.u); + + do { + lf_rst.u = npa_af_reg_read(npa, NPA_AF_LF_RST()); + WATCHDOG_RESET(); + } while (lf_rst.s.exec); + + return 0; +} + +int npa_af_setup(struct npa_af *npa_af) +{ + int err; + union npa_af_gen_cfg npa_cfg; + union npa_af_ndc_cfg ndc_cfg; + union npa_af_aq_cfg aq_cfg; + union npa_af_blk_rst blk_rst; + + err = rvu_aq_alloc(&npa_af->aq, Q_COUNT(AQ_SIZE), + sizeof(union npa_aq_inst_s), + sizeof(union npa_aq_res_s)); + if (err) { + printf("%s: Error %d allocating admin queue\n", __func__, err); + return err; + } + debug("%s: NPA admin queue allocated at %p %llx\n", __func__, + npa_af->aq.inst.base, npa_af->aq.inst.iova); + + blk_rst.u = 0; + blk_rst.s.rst = 1; + npa_af_reg_write(npa_af, NPA_AF_BLK_RST(), blk_rst.u); + + /* Wait for reset to complete */ + do { + blk_rst.u = npa_af_reg_read(npa_af, NPA_AF_BLK_RST()); + WATCHDOG_RESET(); + } while (blk_rst.s.busy); + + /* Set little Endian */ + npa_cfg.u = npa_af_reg_read(npa_af, NPA_AF_GEN_CFG()); + npa_cfg.s.af_be = 0; + npa_af_reg_write(npa_af, NPA_AF_GEN_CFG(), npa_cfg.u); + /* Enable NDC cache */ + ndc_cfg.u = npa_af_reg_read(npa_af, NPA_AF_NDC_CFG()); + ndc_cfg.s.ndc_bypass = 0; + npa_af_reg_write(npa_af, NPA_AF_NDC_CFG(), ndc_cfg.u); + /* Set up queue size */ + aq_cfg.u = npa_af_reg_read(npa_af, NPA_AF_AQ_CFG()); + aq_cfg.s.qsize = AQ_SIZE; + npa_af_reg_write(npa_af, NPA_AF_AQ_CFG(), aq_cfg.u); + /* Set up queue base address */ + npa_af_reg_write(npa_af, NPA_AF_AQ_BASE(), npa_af->aq.inst.iova); + + return 0; +} + +int npa_af_shutdown(struct npa_af *npa_af) +{ + union npa_af_blk_rst blk_rst; + + blk_rst.u = 0; + blk_rst.s.rst = 1; + npa_af_reg_write(npa_af, NPA_AF_BLK_RST(), blk_rst.u); + + /* Wait for reset to complete */ + do { + blk_rst.u = npa_af_reg_read(npa_af, NPA_AF_BLK_RST()); + WATCHDOG_RESET(); + } while (blk_rst.s.busy); + + rvu_aq_free(&npa_af->aq); + + debug("%s: npa af reset --\n", __func__); + + return 0; +} + +/*************** + * NIX API + ***************/ +/** + * Setup SMQ -> TL4 -> TL3 -> TL2 -> TL1 -> MAC mapping + * + * @param nix Handle to setup + * + * @return 0, or negative on failure + */ +static int nix_af_setup_sq(struct nix *nix) +{ + union nixx_af_tl1x_schedule tl1_sched; + union nixx_af_tl2x_parent tl2_parent; + union nixx_af_tl3x_parent tl3_parent; + union nixx_af_tl3_tl2x_cfg tl3_tl2_cfg; + union nixx_af_tl3_tl2x_linkx_cfg tl3_tl2_link_cfg; + union nixx_af_tl4x_parent tl4_parent; + union nixx_af_tl4x_sdp_link_cfg tl4_sdp_link_cfg; + union nixx_af_smqx_cfg smq_cfg; + union nixx_af_mdqx_schedule mdq_sched; + union nixx_af_mdqx_parent mdq_parent; + union nixx_af_rx_linkx_cfg link_cfg; + int tl1_index = nix->lmac->link_num; /* NIX_LINK_E enum */ + int tl2_index = tl1_index; + int tl3_index = tl2_index; + int tl4_index = tl3_index; + int smq_index = tl4_index; + struct nix_af *nix_af = nix->nix_af; + u64 offset = 0; + + tl1_sched.u = nix_af_reg_read(nix_af, + NIXX_AF_TL1X_SCHEDULE(tl1_index)); + tl1_sched.s.rr_quantum = MAX_MTU; + nix_af_reg_write(nix_af, NIXX_AF_TL1X_SCHEDULE(tl1_index), + tl1_sched.u); + + tl2_parent.u = nix_af_reg_read(nix_af, + NIXX_AF_TL2X_PARENT(tl2_index)); + tl2_parent.s.parent = tl1_index; + nix_af_reg_write(nix_af, NIXX_AF_TL2X_PARENT(tl2_index), + tl2_parent.u); + + tl3_parent.u = nix_af_reg_read(nix_af, + NIXX_AF_TL3X_PARENT(tl3_index)); + tl3_parent.s.parent = tl2_index; + nix_af_reg_write(nix_af, NIXX_AF_TL3X_PARENT(tl3_index), + tl3_parent.u); + tl3_tl2_cfg.u = nix_af_reg_read(nix_af, + NIXX_AF_TL3_TL2X_CFG(tl3_index)); + tl3_tl2_cfg.s.express = 0; + nix_af_reg_write(nix_af, NIXX_AF_TL3_TL2X_CFG(tl3_index), + tl3_tl2_cfg.u); + + offset = NIXX_AF_TL3_TL2X_LINKX_CFG(tl3_index, + nix->lmac->link_num); + tl3_tl2_link_cfg.u = nix_af_reg_read(nix_af, offset); + tl3_tl2_link_cfg.s.bp_ena = 1; + tl3_tl2_link_cfg.s.ena = 1; + tl3_tl2_link_cfg.s.relchan = 0; + offset = NIXX_AF_TL3_TL2X_LINKX_CFG(tl3_index, + nix->lmac->link_num); + nix_af_reg_write(nix_af, offset, tl3_tl2_link_cfg.u); + + tl4_parent.u = nix_af_reg_read(nix_af, + NIXX_AF_TL4X_PARENT(tl4_index)); + tl4_parent.s.parent = tl3_index; + nix_af_reg_write(nix_af, NIXX_AF_TL4X_PARENT(tl4_index), + tl4_parent.u); + + offset = NIXX_AF_TL4X_SDP_LINK_CFG(tl4_index); + tl4_sdp_link_cfg.u = nix_af_reg_read(nix_af, offset); + tl4_sdp_link_cfg.s.bp_ena = 0; + tl4_sdp_link_cfg.s.ena = 0; + tl4_sdp_link_cfg.s.relchan = 0; + offset = NIXX_AF_TL4X_SDP_LINK_CFG(tl4_index); + nix_af_reg_write(nix_af, offset, tl4_sdp_link_cfg.u); + + smq_cfg.u = nix_af_reg_read(nix_af, NIXX_AF_SMQX_CFG(smq_index)); + smq_cfg.s.express = 0; + smq_cfg.s.lf = nix->lf; + smq_cfg.s.desc_shp_ctl_dis = 1; + smq_cfg.s.maxlen = MAX_MTU; + smq_cfg.s.minlen = NIX_MIN_HW_MTU; + nix_af_reg_write(nix_af, NIXX_AF_SMQX_CFG(smq_index), smq_cfg.u); + + mdq_sched.u = nix_af_reg_read(nix_af, + NIXX_AF_MDQX_SCHEDULE(smq_index)); + mdq_sched.s.rr_quantum = MAX_MTU; + offset = NIXX_AF_MDQX_SCHEDULE(smq_index); + nix_af_reg_write(nix_af, offset, mdq_sched.u); + mdq_parent.u = nix_af_reg_read(nix_af, + NIXX_AF_MDQX_PARENT(smq_index)); + mdq_parent.s.parent = tl4_index; + nix_af_reg_write(nix_af, NIXX_AF_MDQX_PARENT(smq_index), + mdq_parent.u); + + link_cfg.u = 0; + link_cfg.s.maxlen = NIX_MAX_HW_MTU; + link_cfg.s.minlen = NIX_MIN_HW_MTU; + nix_af_reg_write(nix->nix_af, + NIXX_AF_RX_LINKX_CFG(nix->lmac->link_num), + link_cfg.u); + + return 0; +} + +/** + * Issue a command to the NIX AF Admin Queue + * + * @param nix nix handle + * @param lf Logical function number for command + * @param op Operation + * @param ctype Context type + * @param cindex Context index + * @param resp Result pointer + * + * @return 0 for success, -EBUSY on failure + */ +static int nix_aq_issue_command(struct nix_af *nix_af, + int lf, + int op, + int ctype, + int cindex, union nix_aq_res_s *resp) +{ + union nixx_af_aq_status aq_status; + union nix_aq_inst_s *aq_inst; + union nix_aq_res_s *result = resp; + ulong start; + + debug("%s(%p, 0x%x, 0x%x, 0x%x, 0x%x, %p)\n", __func__, nix_af, lf, + op, ctype, cindex, resp); + aq_status.u = nix_af_reg_read(nix_af, NIXX_AF_AQ_STATUS()); + aq_inst = (union nix_aq_inst_s *)(nix_af->aq.inst.base) + + aq_status.s.head_ptr; + aq_inst->u[0] = 0; + aq_inst->u[1] = 0; + aq_inst->s.op = op; + aq_inst->s.ctype = ctype; + aq_inst->s.lf = lf; + aq_inst->s.cindex = cindex; + aq_inst->s.doneint = 0; + aq_inst->s.res_addr = (u64)resp; + debug("%s: inst@%p: 0x%llx 0x%llx\n", __func__, aq_inst, + aq_inst->u[0], aq_inst->u[1]); + __iowmb(); + + /* Ring doorbell and wait for result */ + nix_af_reg_write(nix_af, NIXX_AF_AQ_DOOR(), 1); + + start = get_timer(0); + /* Wait for completion */ + do { + WATCHDOG_RESET(); + dsb(); + } while (result->s.compcode == 0 && get_timer(start) < 2); + + if (result->s.compcode != NIX_AQ_COMP_E_GOOD) { + printf("NIX:AQ fail or time out with code %d after %ld ms\n", + result->s.compcode, get_timer(start)); + return -EBUSY; + } + return 0; +} + +static int nix_attach_receive_queue(struct nix_af *nix_af, int lf) +{ + struct nix_aq_rq_request rq_req ALIGNED; + int err; + + debug("%s(%p, %d)\n", __func__, nix_af, lf); + + memset(&rq_req, 0, sizeof(struct nix_aq_rq_request)); + + rq_req.rq.s.ena = 1; + rq_req.rq.s.spb_ena = 1; + rq_req.rq.s.ipsech_ena = 0; + rq_req.rq.s.ena_wqwd = 0; + rq_req.rq.s.cq = NIX_CQ_RX; + rq_req.rq.s.substream = 0; /* FIXME: Substream IDs? */ + rq_req.rq.s.wqe_aura = -1; /* No WQE aura */ + rq_req.rq.s.spb_aura = NPA_POOL_RX; + rq_req.rq.s.lpb_aura = NPA_POOL_RX; + /* U-Boot doesn't use WQE group for anything */ + rq_req.rq.s.pb_caching = 1; + rq_req.rq.s.xqe_drop_ena = 0; /* Disable RED dropping */ + rq_req.rq.s.spb_drop_ena = 0; + rq_req.rq.s.lpb_drop_ena = 0; + rq_req.rq.s.spb_sizem1 = (MAX_MTU / (3 * 8)) - 1; /* 512 bytes */ + rq_req.rq.s.lpb_sizem1 = (MAX_MTU / 8) - 1; + rq_req.rq.s.first_skip = 0; + rq_req.rq.s.later_skip = 0; + rq_req.rq.s.xqe_imm_copy = 0; + rq_req.rq.s.xqe_hdr_split = 0; + rq_req.rq.s.xqe_drop = 0; + rq_req.rq.s.xqe_pass = 0; + rq_req.rq.s.wqe_pool_drop = 0; /* No WQE pool */ + rq_req.rq.s.wqe_pool_pass = 0; /* No WQE pool */ + rq_req.rq.s.spb_aura_drop = 255; + rq_req.rq.s.spb_aura_pass = 255; + rq_req.rq.s.spb_pool_drop = 0; + rq_req.rq.s.spb_pool_pass = 0; + rq_req.rq.s.lpb_aura_drop = 255; + rq_req.rq.s.lpb_aura_pass = 255; + rq_req.rq.s.lpb_pool_drop = 0; + rq_req.rq.s.lpb_pool_pass = 0; + rq_req.rq.s.qint_idx = 0; + + err = nix_aq_issue_command(nix_af, lf, + NIX_AQ_INSTOP_E_INIT, + NIX_AQ_CTYPE_E_RQ, + 0, &rq_req.resp); + if (err) { + printf("%s: Error requesting send queue\n", __func__); + return err; + } + + return 0; +} + +static int nix_attach_send_queue(struct nix *nix) +{ + struct nix_af *nix_af = nix->nix_af; + struct nix_aq_sq_request sq_req ALIGNED; + int err; + + debug("%s(%p)\n", __func__, nix_af); + err = nix_af_setup_sq(nix); + + memset(&sq_req, 0, sizeof(sq_req)); + + sq_req.sq.s.ena = 1; + sq_req.sq.s.cq_ena = 1; + sq_req.sq.s.max_sqe_size = NIX_MAXSQESZ_E_W16; + sq_req.sq.s.substream = 0; // FIXME: Substream IDs? + sq_req.sq.s.sdp_mcast = 0; + sq_req.sq.s.cq = NIX_CQ_TX; + sq_req.sq.s.cq_limit = 0; + sq_req.sq.s.smq = nix->lmac->link_num; // scheduling index + sq_req.sq.s.sso_ena = 0; + sq_req.sq.s.smq_rr_quantum = MAX_MTU / 4; + sq_req.sq.s.default_chan = nix->lmac->chan_num; + sq_req.sq.s.sqe_stype = NIX_STYPE_E_STP; + sq_req.sq.s.qint_idx = 0; + sq_req.sq.s.sqb_aura = NPA_POOL_SQB; + + err = nix_aq_issue_command(nix_af, nix->lf, + NIX_AQ_INSTOP_E_INIT, + NIX_AQ_CTYPE_E_SQ, + 0, &sq_req.resp); + if (err) { + printf("%s: Error requesting send queue\n", __func__); + return err; + } + + return 0; +} + +static int nix_attach_completion_queue(struct nix *nix, int cq_idx) +{ + struct nix_af *nix_af = nix->nix_af; + struct nix_aq_cq_request cq_req ALIGNED; + int err; + + debug("%s(%p)\n", __func__, nix_af); + memset(&cq_req, 0, sizeof(cq_req)); + cq_req.cq.s.ena = 1; + cq_req.cq.s.bpid = nix->lmac->pknd; + cq_req.cq.s.substream = 0; /* FIXME: Substream IDs? */ + cq_req.cq.s.drop_ena = 0; + cq_req.cq.s.caching = 1; + cq_req.cq.s.qsize = CQS_QSIZE; + cq_req.cq.s.drop = 255 * 7 / 8; + cq_req.cq.s.qint_idx = 0; + cq_req.cq.s.cint_idx = 0; + cq_req.cq.s.base = nix->cq[cq_idx].iova; + debug("%s: CQ(%d) base %p\n", __func__, cq_idx, + nix->cq[cq_idx].base); + + err = nix_aq_issue_command(nix_af, nix->lf, + NIX_AQ_INSTOP_E_INIT, + NIX_AQ_CTYPE_E_CQ, + cq_idx, &cq_req.resp); + if (err) { + printf("%s: Error requesting completion queue\n", __func__); + return err; + } + debug("%s: CQ(%d) allocated, base %p\n", __func__, cq_idx, + nix->cq[cq_idx].base); + + return 0; +} + +int nix_lf_admin_setup(struct nix *nix) +{ + union nixx_af_lfx_rqs_cfg rqs_cfg; + union nixx_af_lfx_sqs_cfg sqs_cfg; + union nixx_af_lfx_cqs_cfg cqs_cfg; + union nixx_af_lfx_rss_cfg rss_cfg; + union nixx_af_lfx_cints_cfg cints_cfg; + union nixx_af_lfx_qints_cfg qints_cfg; + union nixx_af_lfx_rss_grpx rss_grp; + union nixx_af_lfx_tx_cfg2 tx_cfg2; + union nixx_af_lfx_cfg lfx_cfg; + union nixx_af_lf_rst lf_rst; + u32 index; + struct nix_af *nix_af = nix->nix_af; + int err; + + /* Reset the LF */ + lf_rst.u = 0; + lf_rst.s.lf = nix->lf; + lf_rst.s.exec = 1; + nix_af_reg_write(nix_af, NIXX_AF_LF_RST(), lf_rst.u); + + do { + lf_rst.u = nix_af_reg_read(nix_af, NIXX_AF_LF_RST()); + WATCHDOG_RESET(); + } while (lf_rst.s.exec); + + /* Config NIX RQ HW context and base*/ + nix_af_reg_write(nix_af, NIXX_AF_LFX_RQS_BASE(nix->lf), + (u64)nix->rq_ctx_base); + /* Set caching and queue count in HW */ + rqs_cfg.u = nix_af_reg_read(nix_af, NIXX_AF_LFX_RQS_CFG(nix->lf)); + rqs_cfg.s.caching = 1; + rqs_cfg.s.max_queuesm1 = nix->rq_cnt - 1; + nix_af_reg_write(nix_af, NIXX_AF_LFX_RQS_CFG(nix->lf), rqs_cfg.u); + + /* Config NIX SQ HW context and base*/ + nix_af_reg_write(nix_af, NIXX_AF_LFX_SQS_BASE(nix->lf), + (u64)nix->sq_ctx_base); + sqs_cfg.u = nix_af_reg_read(nix_af, NIXX_AF_LFX_SQS_CFG(nix->lf)); + sqs_cfg.s.caching = 1; + sqs_cfg.s.max_queuesm1 = nix->sq_cnt - 1; + nix_af_reg_write(nix_af, NIXX_AF_LFX_SQS_CFG(nix->lf), sqs_cfg.u); + + /* Config NIX CQ HW context and base*/ + nix_af_reg_write(nix_af, NIXX_AF_LFX_CQS_BASE(nix->lf), + (u64)nix->cq_ctx_base); + cqs_cfg.u = nix_af_reg_read(nix_af, NIXX_AF_LFX_CQS_CFG(nix->lf)); + cqs_cfg.s.caching = 1; + cqs_cfg.s.max_queuesm1 = nix->cq_cnt - 1; + nix_af_reg_write(nix_af, NIXX_AF_LFX_CQS_CFG(nix->lf), cqs_cfg.u); + + /* Config NIX RSS HW context and base */ + nix_af_reg_write(nix_af, NIXX_AF_LFX_RSS_BASE(nix->lf), + (u64)nix->rss_base); + rss_cfg.u = nix_af_reg_read(nix_af, NIXX_AF_LFX_RSS_CFG(nix->lf)); + rss_cfg.s.ena = 1; + rss_cfg.s.size = ilog2(nix->rss_sz) / 256; + nix_af_reg_write(nix_af, NIXX_AF_LFX_RSS_CFG(nix->lf), rss_cfg.u); + + for (index = 0; index < nix->rss_grps; index++) { + rss_grp.u = 0; + rss_grp.s.sizem1 = 0x7; + rss_grp.s.offset = nix->rss_sz * index; + nix_af_reg_write(nix_af, + NIXX_AF_LFX_RSS_GRPX(nix->lf, index), + rss_grp.u); + } + + /* Config CQints HW contexts and base */ + nix_af_reg_write(nix_af, NIXX_AF_LFX_CINTS_BASE(nix->lf), + (u64)nix->cint_base); + cints_cfg.u = nix_af_reg_read(nix_af, + NIXX_AF_LFX_CINTS_CFG(nix->lf)); + cints_cfg.s.caching = 1; + nix_af_reg_write(nix_af, NIXX_AF_LFX_CINTS_CFG(nix->lf), + cints_cfg.u); + + /* Config Qints HW context and base */ + nix_af_reg_write(nix_af, NIXX_AF_LFX_QINTS_BASE(nix->lf), + (u64)nix->qint_base); + qints_cfg.u = nix_af_reg_read(nix_af, + NIXX_AF_LFX_QINTS_CFG(nix->lf)); + qints_cfg.s.caching = 1; + nix_af_reg_write(nix_af, NIXX_AF_LFX_QINTS_CFG(nix->lf), + qints_cfg.u); + + debug("%s(%p, %d, %d)\n", __func__, nix_af, nix->lf, nix->pf); + + /* Enable LMTST for this NIX LF */ + tx_cfg2.u = nix_af_reg_read(nix_af, NIXX_AF_LFX_TX_CFG2(nix->lf)); + tx_cfg2.s.lmt_ena = 1; + nix_af_reg_write(nix_af, NIXX_AF_LFX_TX_CFG2(nix->lf), tx_cfg2.u); + + /* Use 16-word XQEs, write the npa pf_func number only */ + lfx_cfg.u = nix_af_reg_read(nix_af, NIXX_AF_LFX_CFG(nix->lf)); + lfx_cfg.s.xqe_size = NIX_XQESZ_E_W16; + lfx_cfg.s.npa_pf_func = nix->pf_func; + nix_af_reg_write(nix_af, NIXX_AF_LFX_CFG(nix->lf), lfx_cfg.u); + + nix_af_reg_write(nix_af, NIXX_AF_LFX_RX_CFG(nix->lf), 0); + + for (index = 0; index < nix->cq_cnt; index++) { + err = nix_attach_completion_queue(nix, index); + if (err) { + printf("%s: Error attaching completion queue %d\n", + __func__, index); + return err; + } + } + + for (index = 0; index < nix->rq_cnt; index++) { + err = nix_attach_receive_queue(nix_af, nix->lf); + if (err) { + printf("%s: Error attaching receive queue %d\n", + __func__, index); + return err; + } + } + + for (index = 0; index < nix->sq_cnt; index++) { + err = nix_attach_send_queue(nix); + if (err) { + printf("%s: Error attaching send queue %d\n", + __func__, index); + return err; + } + } + + return 0; +} + +int nix_lf_admin_shutdown(struct nix_af *nix_af, int lf, + u32 cq_count, u32 rq_count, u32 sq_count) +{ + union nixx_af_rx_sw_sync sw_sync; + union nixx_af_lf_rst lf_rst; + int index, err; + + /* Flush all tx packets */ + sw_sync.u = 0; + sw_sync.s.ena = 1; + nix_af_reg_write(nix_af, NIXX_AF_RX_SW_SYNC(), sw_sync.u); + + do { + sw_sync.u = nix_af_reg_read(nix_af, NIXX_AF_RX_SW_SYNC()); + WATCHDOG_RESET(); + } while (sw_sync.s.ena); + + for (index = 0; index < rq_count; index++) { + memset((void *)&rq_dis, 0, sizeof(rq_dis)); + rq_dis.rq.s.ena = 0; /* Context */ + rq_dis.mrq.s.ena = 1; /* Mask */ + __iowmb(); + + err = nix_aq_issue_command(nix_af, lf, + NIX_AQ_INSTOP_E_WRITE, + NIX_AQ_CTYPE_E_RQ, + index, &rq_dis.resp); + if (err) { + printf("%s: Error disabling LF %d RQ(%d)\n", + __func__, lf, index); + return err; + } + debug("%s: LF %d RQ(%d) disabled\n", __func__, lf, index); + } + + for (index = 0; index < sq_count; index++) { + memset((void *)&sq_dis, 0, sizeof(sq_dis)); + sq_dis.sq.s.ena = 0; /* Context */ + sq_dis.msq.s.ena = 1; /* Mask */ + __iowmb(); + + err = nix_aq_issue_command(nix_af, lf, + NIX_AQ_INSTOP_E_WRITE, + NIX_AQ_CTYPE_E_SQ, + index, &sq_dis.resp); + if (err) { + printf("%s: Error disabling LF %d SQ(%d)\n", + __func__, lf, index); + return err; + } + debug("%s: LF %d SQ(%d) disabled\n", __func__, lf, index); + } + + for (index = 0; index < cq_count; index++) { + memset((void *)&cq_dis, 0, sizeof(cq_dis)); + cq_dis.cq.s.ena = 0; /* Context */ + cq_dis.mcq.s.ena = 1; /* Mask */ + __iowmb(); + + err = nix_aq_issue_command(nix_af, lf, + NIX_AQ_INSTOP_E_WRITE, + NIX_AQ_CTYPE_E_CQ, + index, &cq_dis.resp); + if (err) { + printf("%s: Error disabling LF %d CQ(%d)\n", + __func__, lf, index); + return err; + } + debug("%s: LF %d CQ(%d) disabled\n", __func__, lf, index); + } + + /* Reset the LF */ + lf_rst.u = 0; + lf_rst.s.lf = lf; + lf_rst.s.exec = 1; + nix_af_reg_write(nix_af, NIXX_AF_LF_RST(), lf_rst.u); + + do { + lf_rst.u = nix_af_reg_read(nix_af, NIXX_AF_LF_RST()); + WATCHDOG_RESET(); + } while (lf_rst.s.exec); + + return 0; +} + +int npc_lf_admin_setup(struct nix *nix) +{ + union npc_af_const af_const; + union npc_af_pkindx_action0 action0; + union npc_af_pkindx_action1 action1; + union npc_af_intfx_kex_cfg kex_cfg; + union npc_af_intfx_miss_stat_act intfx_stat_act; + union npc_af_mcamex_bankx_camx_intf camx_intf; + union npc_af_mcamex_bankx_camx_w0 camx_w0; + union npc_af_mcamex_bankx_cfg bankx_cfg; + union npc_af_mcamex_bankx_stat_act mcamex_stat_act; + + union nix_rx_action_s rx_action; + union nix_tx_action_s tx_action; + + struct nix_af *nix_af = nix->nix_af; + u32 kpus; + int pkind = nix->lmac->link_num; + int index; + u64 offset; + + debug("%s(%p, pkind 0x%x)\n", __func__, nix_af, pkind); + af_const.u = npc_af_reg_read(nix_af, NPC_AF_CONST()); + kpus = af_const.s.kpus; + + action0.u = 0; + action0.s.parse_done = 1; + npc_af_reg_write(nix_af, NPC_AF_PKINDX_ACTION0(pkind), action0.u); + + action1.u = 0; + npc_af_reg_write(nix_af, NPC_AF_PKINDX_ACTION1(pkind), action1.u); + + kex_cfg.u = 0; + kex_cfg.s.keyw = NPC_MCAMKEYW_E_X1; + kex_cfg.s.parse_nibble_ena = 0x7; + npc_af_reg_write(nix_af, + NPC_AF_INTFX_KEX_CFG(NPC_INTF_E_NIXX_RX(0)), + kex_cfg.u); + + /* HW Issue */ + kex_cfg.u = 0; + kex_cfg.s.parse_nibble_ena = 0x7; + npc_af_reg_write(nix_af, + NPC_AF_INTFX_KEX_CFG(NPC_INTF_E_NIXX_TX(0)), + kex_cfg.u); + + camx_intf.u = 0; + camx_intf.s.intf = ~NPC_INTF_E_NIXX_RX(0); + npc_af_reg_write(nix_af, + NPC_AF_MCAMEX_BANKX_CAMX_INTF(pkind, 0, 0), + camx_intf.u); + + camx_intf.u = 0; + camx_intf.s.intf = NPC_INTF_E_NIXX_RX(0); + npc_af_reg_write(nix_af, + NPC_AF_MCAMEX_BANKX_CAMX_INTF(pkind, 0, 1), + camx_intf.u); + + camx_w0.u = 0; + camx_w0.s.md = ~(nix->lmac->chan_num) & (~((~0x0ull) << 12)); + debug("NPC LF ADMIN camx_w0.u %llx\n", camx_w0.u); + npc_af_reg_write(nix_af, + NPC_AF_MCAMEX_BANKX_CAMX_W0(pkind, 0, 0), + camx_w0.u); + + camx_w0.u = 0; + camx_w0.s.md = nix->lmac->chan_num; + npc_af_reg_write(nix_af, + NPC_AF_MCAMEX_BANKX_CAMX_W0(pkind, 0, 1), + camx_w0.u); + + npc_af_reg_write(nix_af, NPC_AF_MCAMEX_BANKX_CAMX_W1(pkind, 0, 0), + 0); + + npc_af_reg_write(nix_af, NPC_AF_MCAMEX_BANKX_CAMX_W1(pkind, 0, 1), + 0); + + /* Enable stats for NPC INTF RX */ + mcamex_stat_act.u = 0; + mcamex_stat_act.s.ena = 1; + mcamex_stat_act.s.stat_sel = pkind; + npc_af_reg_write(nix_af, + NPC_AF_MCAMEX_BANKX_STAT_ACT(pkind, 0), + mcamex_stat_act.u); + intfx_stat_act.u = 0; + intfx_stat_act.s.ena = 1; + intfx_stat_act.s.stat_sel = 16; + offset = NPC_AF_INTFX_MISS_STAT_ACT(NPC_INTF_E_NIXX_RX(0)); + npc_af_reg_write(nix_af, offset, intfx_stat_act.u); + rx_action.u = 0; + rx_action.s.pf_func = nix->pf_func; + rx_action.s.op = NIX_RX_ACTIONOP_E_UCAST; + npc_af_reg_write(nix_af, NPC_AF_MCAMEX_BANKX_ACTION(pkind, 0), + rx_action.u); + + for (index = 0; index < kpus; index++) + npc_af_reg_write(nix_af, NPC_AF_KPUX_CFG(index), 0); + + rx_action.u = 0; + rx_action.s.pf_func = nix->pf_func; + rx_action.s.op = NIX_RX_ACTIONOP_E_DROP; + npc_af_reg_write(nix_af, + NPC_AF_INTFX_MISS_ACT(NPC_INTF_E_NIXX_RX(0)), + rx_action.u); + bankx_cfg.u = 0; + bankx_cfg.s.ena = 1; + npc_af_reg_write(nix_af, NPC_AF_MCAMEX_BANKX_CFG(pkind, 0), + bankx_cfg.u); + + tx_action.u = 0; + tx_action.s.op = NIX_TX_ACTIONOP_E_UCAST_DEFAULT; + npc_af_reg_write(nix_af, + NPC_AF_INTFX_MISS_ACT(NPC_INTF_E_NIXX_TX(0)), + tx_action.u); + +#ifdef DEBUG + /* Enable debug capture on RX intf */ + npc_af_reg_write(nix_af, NPC_AF_DBG_CTL(), 0x4); +#endif + + return 0; +} + +int npc_af_shutdown(struct nix_af *nix_af) +{ + union npc_af_blk_rst blk_rst; + + blk_rst.u = 0; + blk_rst.s.rst = 1; + npc_af_reg_write(nix_af, NPC_AF_BLK_RST(), blk_rst.u); + + /* Wait for reset to complete */ + do { + blk_rst.u = npc_af_reg_read(nix_af, NPC_AF_BLK_RST()); + WATCHDOG_RESET(); + } while (blk_rst.s.busy); + + debug("%s: npc af reset --\n", __func__); + + return 0; +} + +int nix_af_setup(struct nix_af *nix_af) +{ + int err; + union nixx_af_const2 af_const2; + union nixx_af_const3 af_const3; + union nixx_af_sq_const sq_const; + union nixx_af_cfg af_cfg; + union nixx_af_status af_status; + union nixx_af_ndc_cfg ndc_cfg; + union nixx_af_aq_cfg aq_cfg; + union nixx_af_blk_rst blk_rst; + + debug("%s(%p)\n", __func__, nix_af); + err = rvu_aq_alloc(&nix_af->aq, Q_COUNT(AQ_SIZE), + sizeof(union nix_aq_inst_s), + sizeof(union nix_aq_res_s)); + if (err) { + printf("%s: Error allocating nix admin queue\n", __func__); + return err; + } + + blk_rst.u = 0; + blk_rst.s.rst = 1; + nix_af_reg_write(nix_af, NIXX_AF_BLK_RST(), blk_rst.u); + + /* Wait for reset to complete */ + do { + blk_rst.u = nix_af_reg_read(nix_af, NIXX_AF_BLK_RST()); + WATCHDOG_RESET(); + } while (blk_rst.s.busy); + + /* Put in LE mode */ + af_cfg.u = nix_af_reg_read(nix_af, NIXX_AF_CFG()); + if (af_cfg.s.force_cond_clk_en || af_cfg.s.calibrate_x2p || + af_cfg.s.force_intf_clk_en) { + printf("%s: Error: Invalid NIX_AF_CFG value 0x%llx\n", + __func__, af_cfg.u); + return -1; + } + af_cfg.s.af_be = 0; + af_cfg.u |= 0x5E; /* HW Issue */ + nix_af_reg_write(nix_af, NIXX_AF_CFG(), af_cfg.u); + + /* Perform Calibration */ + af_cfg.u = nix_af_reg_read(nix_af, NIXX_AF_CFG()); + af_cfg.s.calibrate_x2p = 1; + nix_af_reg_write(nix_af, NIXX_AF_CFG(), af_cfg.u); + + /* Wait for calibration to complete */ + do { + af_status.u = nix_af_reg_read(nix_af, NIXX_AF_STATUS()); + WATCHDOG_RESET(); + } while (af_status.s.calibrate_done == 0); + + af_cfg.u = nix_af_reg_read(nix_af, NIXX_AF_CFG()); + af_cfg.s.calibrate_x2p = 0; + nix_af_reg_write(nix_af, NIXX_AF_CFG(), af_cfg.u); + + /* Enable NDC cache */ + ndc_cfg.u = nix_af_reg_read(nix_af, NIXX_AF_NDC_CFG()); + ndc_cfg.s.ndc_ign_pois = 0; + ndc_cfg.s.byp_sq = 0; + ndc_cfg.s.byp_sqb = 0; + ndc_cfg.s.byp_cqs = 0; + ndc_cfg.s.byp_cints = 0; + ndc_cfg.s.byp_dyno = 0; + ndc_cfg.s.byp_mce = 0; + ndc_cfg.s.byp_rqc = 0; + ndc_cfg.s.byp_rsse = 0; + ndc_cfg.s.byp_mc_data = 0; + ndc_cfg.s.byp_mc_wqe = 0; + ndc_cfg.s.byp_mr_data = 0; + ndc_cfg.s.byp_mr_wqe = 0; + ndc_cfg.s.byp_qints = 0; + nix_af_reg_write(nix_af, NIXX_AF_NDC_CFG(), ndc_cfg.u); + + /* Set up queue size */ + aq_cfg.u = 0; + aq_cfg.s.qsize = AQ_SIZE; + nix_af_reg_write(nix_af, NIXX_AF_AQ_CFG(), aq_cfg.u); + + /* Set up queue base address */ + nix_af_reg_write(nix_af, NIXX_AF_AQ_BASE(), nix_af->aq.inst.iova); + + af_const3.u = nix_af_reg_read(nix_af, NIXX_AF_CONST3()); + af_const2.u = nix_af_reg_read(nix_af, NIXX_AF_CONST2()); + sq_const.u = nix_af_reg_read(nix_af, NIXX_AF_SQ_CONST()); + nix_af->rq_ctx_sz = 1ULL << af_const3.s.rq_ctx_log2bytes; + nix_af->sq_ctx_sz = 1ULL << af_const3.s.sq_ctx_log2bytes; + nix_af->cq_ctx_sz = 1ULL << af_const3.s.cq_ctx_log2bytes; + nix_af->rsse_ctx_sz = 1ULL << af_const3.s.rsse_log2bytes; + nix_af->qints = af_const2.s.qints; + nix_af->cints = af_const2.s.cints; + nix_af->cint_ctx_sz = 1ULL << af_const3.s.cint_log2bytes; + nix_af->qint_ctx_sz = 1ULL << af_const3.s.qint_log2bytes; + nix_af->sqb_size = sq_const.s.sqb_size; + + return 0; +} + +int nix_af_shutdown(struct nix_af *nix_af) +{ + union nixx_af_blk_rst blk_rst; + + blk_rst.u = 0; + blk_rst.s.rst = 1; + nix_af_reg_write(nix_af, NIXX_AF_BLK_RST(), blk_rst.u); + + /* Wait for reset to complete */ + do { + blk_rst.u = nix_af_reg_read(nix_af, NIXX_AF_BLK_RST()); + WATCHDOG_RESET(); + } while (blk_rst.s.busy); + + rvu_aq_free(&nix_af->aq); + + debug("%s: nix af reset --\n", __func__); + + return 0; +} diff --git a/drivers/net/octeontx2/npc.h b/drivers/net/octeontx2/npc.h new file mode 100644 index 0000000000..6e645cd32e --- /dev/null +++ b/drivers/net/octeontx2/npc.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Marvell International Ltd. + */ + +#ifndef __NPC_H__ +#define __NPC_H__ + +#define RSVD_MCAM_ENTRIES_PER_PF 2 /** Ucast and Bcast */ +#define RSVD_MCAM_ENTRIES_PER_NIXLF 1 /** Ucast for VFs */ + +struct npc_kpu_profile_cam { + u8 state; + u8 state_mask; + u16 dp0; + u16 dp0_mask; + u16 dp1; + u16 dp1_mask; + u16 dp2; + u16 dp2_mask; +}; + +struct npc_kpu_profile_action { + u8 errlev; + u8 errcode; + u8 dp0_offset; + u8 dp1_offset; + u8 dp2_offset; + u8 bypass_count; + u8 parse_done; + u8 next_state; + u8 ptr_advance; + u8 cap_ena; + u8 lid; + u8 ltype; + u8 flags; + u8 offset; + u8 mask; + u8 right; + u8 shift; +}; + +struct npc_kpu_profile { + int cam_entries; + int action_entries; + struct npc_kpu_profile_cam *cam; + struct npc_kpu_profile_action *action; +}; + +struct npc_pkind { + struct rsrc_bmap rsrc; + u32 *pfchan_map; +}; + +struct npc_mcam { + struct rsrc_bmap rsrc; + u16 *pfvf_map; + u16 total_entries; /* Total number of MCAM entries */ + u16 entries; /* Total - reserved for NIX LFs */ + u8 banks_per_entry; /* Number of keywords in key */ + u8 keysize; + u8 banks; /* Number of MCAM banks */ + u16 banksize; /* Number of MCAM entries in each bank */ + u16 counters; /* Number of match counters */ + u16 nixlf_offset; + u16 pf_offset; +}; + +struct nix_af_handle; +struct nix_handle; +struct rvu_hwinfo; + +struct npc_af { + struct nix_af_handle *nix_af; + struct npc_pkind pkind; + void __iomem *npc_af_base; + u8 npc_kpus; /** Number of parser units */ + struct npc_mcam mcam; + struct rvu_block block; + struct rvu_hwinfo *hw; +}; + +struct npc { + struct npc_af *npc_af; + void __iomem *npc_base; + struct nix_handle *nix; +} + +#endif /* __NPC_H__ */ + diff --git a/drivers/net/octeontx2/rvu.h b/drivers/net/octeontx2/rvu.h new file mode 100644 index 0000000000..f455260a6f --- /dev/null +++ b/drivers/net/octeontx2/rvu.h @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Marvell International Ltd. + */ + +#ifndef __RVU_H__ +#define __RVU_H__ + +#include + +#define ALIGNED __aligned(CONFIG_SYS_CACHELINE_SIZE) + +#define Q_SIZE_16 0ULL /* 16 entries */ +#define Q_SIZE_64 1ULL /* 64 entries */ +#define Q_SIZE_256 2ULL +#define Q_SIZE_1K 3ULL +#define Q_SIZE_4K 4ULL +#define Q_SIZE_16K 5ULL +#define Q_SIZE_64K 6ULL +#define Q_SIZE_256K 7ULL +#define Q_SIZE_1M 8ULL /* Million entries */ +#define Q_SIZE_MIN Q_SIZE_16 +#define Q_SIZE_MAX Q_SIZE_1M + +#define Q_COUNT(x) (16ULL << (2 * (x))) +#define Q_SIZE(x, n) ((ilog2(x) - (n)) / 2) + +/* Admin queue info */ + +/* Since we intend to add only one instruction at a time, + * keep queue size to it's minimum. + */ +#define AQ_SIZE Q_SIZE_16 +/* HW head & tail pointer mask */ +#define AQ_PTR_MASK 0xFFFFF + +struct qmem { + void *base; + dma_addr_t iova; + size_t alloc_sz; + u32 qsize; + u8 entry_sz; +}; + +struct admin_queue { + struct qmem inst; + struct qmem res; +}; + +struct rvu_af { + struct udevice *dev; + void __iomem *af_base; + struct nix_af *nix_af; +}; + +struct rvu_pf { + struct udevice *dev; + struct udevice *afdev; + void __iomem *pf_base; + struct nix *nix; + u8 pfid; + int nix_lfid; + int npa_lfid; +}; + +/** + * Store 128 bit value + * + * @param[out] dest pointer to destination address + * @param val0 first 64 bits to write + * @param val1 second 64 bits to write + */ +static inline void st128(void *dest, u64 val0, u64 val1) +{ + __asm__ __volatile__("stp %x[x0], %x[x1], [%[pm]]" : + : [x0]"r"(val0), [x1]"r"(val1), [pm]"r"(dest) + : "memory"); +} + +/** + * Load 128 bit value + * + * @param[in] source pointer to 128 bits of data to load + * @param[out] val0 first 64 bits of data + * @param[out] val1 second 64 bits of data + */ +static inline void ld128(const u64 *src, u64 *val0, u64 *val1) +{ + __asm__ __volatile__ ("ldp %x[x0], %x[x1], [%[pm]]" : + : [x0]"r"(*val0), [x1]"r"(*val1), [pm]"r"(src)); +} + +void qmem_free(struct qmem *q); +int qmem_alloc(struct qmem *q, u32 qsize, size_t entry_sz); + +/** + * Allocates an admin queue for instructions and results + * + * @param aq admin queue to allocate for + * @param qsize Number of entries in the queue + * @param inst_size Size of each instruction + * @param res_size Size of each result + * + * @return -ENOMEM on error, 0 on success + */ +int rvu_aq_alloc(struct admin_queue *aq, unsigned int qsize, + size_t inst_size, size_t res_size); + +/** + * Frees an admin queue + * + * @param aq Admin queue to free + */ +void rvu_aq_free(struct admin_queue *aq); + +void rvu_get_lfid_for_pf(int pf, int *nixid, int *npaid); + +#endif /* __RVU_H__ */ + diff --git a/drivers/net/octeontx2/rvu_af.c b/drivers/net/octeontx2/rvu_af.c new file mode 100644 index 0000000000..7750089a20 --- /dev/null +++ b/drivers/net/octeontx2/rvu_af.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell International Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nix.h" + +struct udevice *rvu_af_dev; + +inline struct rvu_af *get_af(void) +{ + return rvu_af_dev ? dev_get_priv(rvu_af_dev) : NULL; +} + +void rvu_get_lfid_for_pf(int pf, int *nixid, int *npaid) +{ + union nixx_af_rvu_lf_cfg_debug nix_lf_dbg; + union npa_af_rvu_lf_cfg_debug npa_lf_dbg; + union rvu_pf_func_s pf_func; + struct rvu_af *af = dev_get_priv(rvu_af_dev); + struct nix_af *nix_af = af->nix_af; + + pf_func.u = 0; + pf_func.s.pf = pf; + + nix_lf_dbg.u = 0; + nix_lf_dbg.s.pf_func = pf_func.u & 0xFFFF; + nix_lf_dbg.s.exec = 1; + nix_af_reg_write(nix_af, NIXX_AF_RVU_LF_CFG_DEBUG(), + nix_lf_dbg.u); + do { + nix_lf_dbg.u = nix_af_reg_read(nix_af, + NIXX_AF_RVU_LF_CFG_DEBUG()); + } while (nix_lf_dbg.s.exec); + + if (nix_lf_dbg.s.lf_valid) + *nixid = nix_lf_dbg.s.lf; + + debug("%s: nix lf_valid %d lf %d nixid %d\n", __func__, + nix_lf_dbg.s.lf_valid, nix_lf_dbg.s.lf, *nixid); + + npa_lf_dbg.u = 0; + npa_lf_dbg.s.pf_func = pf_func.u & 0xFFFF; + npa_lf_dbg.s.exec = 1; + npa_af_reg_write(nix_af->npa_af, NPA_AF_RVU_LF_CFG_DEBUG(), + npa_lf_dbg.u); + do { + npa_lf_dbg.u = npa_af_reg_read(nix_af->npa_af, + NPA_AF_RVU_LF_CFG_DEBUG()); + } while (npa_lf_dbg.s.exec); + + if (npa_lf_dbg.s.lf_valid) + *npaid = npa_lf_dbg.s.lf; + debug("%s: npa lf_valid %d lf %d npaid %d\n", __func__, + npa_lf_dbg.s.lf_valid, npa_lf_dbg.s.lf, *npaid); +} + +struct nix_af *rvu_af_init(struct rvu_af *rvu_af) +{ + struct nix_af *nix_af; + union rvu_af_addr_s block_addr; + int err; + + nix_af = (struct nix_af *)calloc(1, sizeof(struct nix_af)); + if (!nix_af) { + printf("%s: out of memory\n", __func__); + goto error; + } + + nix_af->dev = rvu_af->dev; + + block_addr.u = 0; + block_addr.s.block = RVU_BLOCK_ADDR_E_NIXX(0); + nix_af->nix_af_base = rvu_af->af_base + block_addr.u; + + nix_af->npa_af = (struct npa_af *)calloc(1, sizeof(struct npa_af)); + if (!nix_af->npa_af) { + printf("%s: out of memory\n", __func__); + goto error; + } + + block_addr.u = 0; + block_addr.s.block = RVU_BLOCK_ADDR_E_NPA; + nix_af->npa_af->npa_af_base = rvu_af->af_base + block_addr.u; + + block_addr.u = 0; + block_addr.s.block = RVU_BLOCK_ADDR_E_NPC; + nix_af->npc_af_base = rvu_af->af_base + block_addr.u; + + debug("%s: Setting up npa admin\n", __func__); + err = npa_af_setup(nix_af->npa_af); + if (err) { + printf("%s: Error %d setting up NPA admin\n", __func__, err); + goto error; + } + debug("%s: Setting up nix af\n", __func__); + err = nix_af_setup(nix_af); + if (err) { + printf("%s: Error %d setting up NIX admin\n", __func__, err); + goto error; + } + debug("%s: nix_af: %p\n", __func__, nix_af); + return nix_af; + +error: + if (nix_af->npa_af) { + free(nix_af->npa_af); + memset(nix_af, 0, sizeof(*nix_af)); + } + if (nix_af) + free(nix_af); + return NULL; +} + +int rvu_af_probe(struct udevice *dev) +{ + struct rvu_af *af_ptr = dev_get_priv(dev); + + af_ptr->af_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, + PCI_REGION_MEM); + debug("%s RVU AF BAR %p\n", __func__, af_ptr->af_base); + af_ptr->dev = dev; + rvu_af_dev = dev; + + af_ptr->nix_af = rvu_af_init(af_ptr); + if (!af_ptr->nix_af) { + printf("%s: Error: could not initialize NIX AF\n", __func__); + return -1; + } + debug("%s: Done\n", __func__); + + return 0; +} + +int rvu_af_remove(struct udevice *dev) +{ + struct rvu_af *rvu_af = dev_get_priv(dev); + + nix_af_shutdown(rvu_af->nix_af); + npa_af_shutdown(rvu_af->nix_af->npa_af); + npc_af_shutdown(rvu_af->nix_af); + + debug("%s: rvu af down --\n", __func__); + return 0; +} + +U_BOOT_DRIVER(rvu_af) = { + .name = "rvu_af", + .id = UCLASS_MISC, + .probe = rvu_af_probe, + .remove = rvu_af_remove, + .priv_auto_alloc_size = sizeof(struct rvu_af), +}; + +static struct pci_device_id rvu_af_supported[] = { + { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_RVU_AF) }, + {} +}; + +U_BOOT_PCI_DEVICE(rvu_af, rvu_af_supported); diff --git a/drivers/net/octeontx2/rvu_common.c b/drivers/net/octeontx2/rvu_common.c new file mode 100644 index 0000000000..173b28ba4b --- /dev/null +++ b/drivers/net/octeontx2/rvu_common.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell International Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#include "rvu.h" + +int qmem_alloc(struct qmem *q, u32 qsize, size_t entry_sz) +{ + q->base = memalign(CONFIG_SYS_CACHELINE_SIZE, qsize * entry_sz); + if (!q->base) + return -ENOMEM; + q->entry_sz = entry_sz; + q->qsize = qsize; + q->alloc_sz = (size_t)qsize * entry_sz; + q->iova = (dma_addr_t)(q->base); + debug("NIX: qmem alloc for (%d * %d = %ld bytes) at %p\n", + q->qsize, q->entry_sz, q->alloc_sz, q->base); + return 0; +} + +void qmem_free(struct qmem *q) +{ + if (q->base) + free(q->base); + memset(q, 0, sizeof(*q)); +} + +/** + * Allocates an admin queue for instructions and results + * + * @param aq admin queue to allocate for + * @param qsize Number of entries in the queue + * @param inst_size Size of each instruction + * @param res_size Size of each result + * + * @return -ENOMEM on error, 0 on success + */ +int rvu_aq_alloc(struct admin_queue *aq, unsigned int qsize, + size_t inst_size, size_t res_size) +{ + int err; + + err = qmem_alloc(&aq->inst, qsize, inst_size); + if (err) + return err; + err = qmem_alloc(&aq->res, qsize, res_size); + if (err) + qmem_free(&aq->inst); + + return err; +} + +/** + * Frees an admin queue + * + * @param aq Admin queue to free + */ +void rvu_aq_free(struct admin_queue *aq) +{ + qmem_free(&aq->inst); + qmem_free(&aq->res); + memset(aq, 0, sizeof(*aq)); +} diff --git a/drivers/net/octeontx2/rvu_pf.c b/drivers/net/octeontx2/rvu_pf.c new file mode 100644 index 0000000000..201ecf2c16 --- /dev/null +++ b/drivers/net/octeontx2/rvu_pf.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell International Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cgx.h" +#include "nix.h" + +extern struct udevice *rvu_af_dev; + +int rvu_pf_init(struct rvu_pf *rvu) +{ + struct nix *nix; + struct eth_pdata *pdata = dev_get_platdata(rvu->dev); + + debug("%s: Allocating nix lf\n", __func__); + nix = nix_lf_alloc(rvu->dev); + if (!nix) { + printf("%s: Error allocating lf for pf %d\n", + __func__, rvu->pfid); + return -1; + } + rvu->nix = nix; + + /* to make post_probe happy */ + if (is_valid_ethaddr(nix->lmac->mac_addr)) { + memcpy(pdata->enetaddr, nix->lmac->mac_addr, 6); + eth_env_set_enetaddr_by_index("eth", rvu->dev->seq, + pdata->enetaddr); + } + + return 0; +} + +static const struct eth_ops nix_eth_ops = { + .start = nix_lf_init, + .send = nix_lf_xmit, + .recv = nix_lf_recv, + .free_pkt = nix_lf_free_pkt, + .stop = nix_lf_halt, + .write_hwaddr = nix_lf_setup_mac, +}; + +int rvu_pf_probe(struct udevice *dev) +{ + struct rvu_pf *rvu = dev_get_priv(dev); + int err; + char name[16]; + + debug("%s: name: %s\n", __func__, dev->name); + + rvu->pf_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_2, PCI_REGION_MEM); + rvu->pfid = dev->seq + 1; // RVU PF's start from 1; + rvu->dev = dev; + if (!rvu_af_dev) { + printf("%s: Error: Could not find RVU AF device\n", + __func__); + return -1; + } + rvu->afdev = rvu_af_dev; + + debug("RVU PF %u BAR2 %p\n", rvu->pfid, rvu->pf_base); + + rvu_get_lfid_for_pf(rvu->pfid, &rvu->nix_lfid, &rvu->npa_lfid); + + err = rvu_pf_init(rvu); + if (err) + printf("%s: Error %d adding nix\n", __func__, err); + + /* + * modify device name to include index/sequence number, + * for better readability, this is 1:1 mapping with eth0/1/2.. names. + */ + sprintf(name, "rvu_pf#%d", dev->seq); + device_set_name(dev, name); + debug("%s: name: %s\n", __func__, dev->name); + return err; +} + +int rvu_pf_remove(struct udevice *dev) +{ + struct rvu_pf *rvu = dev_get_priv(dev); + + nix_lf_shutdown(rvu->nix); + npa_lf_shutdown(rvu->nix); + + debug("%s: rvu pf%d down --\n", __func__, rvu->pfid); + + return 0; +} + +U_BOOT_DRIVER(rvu_pf) = { + .name = "rvu_pf", + .id = UCLASS_ETH, + .probe = rvu_pf_probe, + .remove = rvu_pf_remove, + .ops = &nix_eth_ops, + .priv_auto_alloc_size = sizeof(struct rvu_pf), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), +}; + +static struct pci_device_id rvu_pf_supported[] = { + { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_RVU_PF) }, + {} +}; + +U_BOOT_PCI_DEVICE(rvu_pf, rvu_pf_supported); -- cgit v1.2.1 From 2ae2b8a2f241e5b6ac493c5b2deff247b86a51b4 Mon Sep 17 00:00:00 2001 From: Luka Kovacic Date: Sat, 29 Aug 2020 00:35:50 +0200 Subject: arm: mvebu: Initial iEi Puzzle-M801 support Add initial U-Boot support for the iEi Puzzle-M801 board based on the Marvell Armada 88F8040 SoC. Currently supported hardware: 1x USB 3.0 4x Gigabit Ethernet 2x SFP+ (with NXP PCA9555 and NXP PCA9544) 1x SATA 3.0 1x M.2 type B 1x RJ45 UART 1x SPI flash 1x EPSON RX8010 RTC Signed-off-by: Luka Kovacic Cc: Luka Perkov Reviewed-by: Stefan Roese --- arch/arm/dts/Makefile | 1 + arch/arm/dts/armada-8040-puzzle-m801.dts | 389 ++++++++++++++++++++++++++++ board/Marvell/mvebu_armada-8k/MAINTAINERS | 6 + configs/mvebu_puzzle-m801-88f8040_defconfig | 91 +++++++ 4 files changed, 487 insertions(+) create mode 100644 arch/arm/dts/armada-8040-puzzle-m801.dts create mode 100644 configs/mvebu_puzzle-m801-88f8040_defconfig diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 72b6fe1a3e..0b9c4c5eb0 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -218,6 +218,7 @@ dtb-$(CONFIG_ARCH_MVEBU) += \ armada-8040-clearfog-gt-8k.dtb \ armada-8040-db.dtb \ armada-8040-mcbin.dtb \ + armada-8040-puzzle-m801.dtb \ armada-xp-crs305-1g-4s.dtb \ armada-xp-crs305-1g-4s-bit.dtb \ armada-xp-crs326-24g-2s.dtb \ diff --git a/arch/arm/dts/armada-8040-puzzle-m801.dts b/arch/arm/dts/armada-8040-puzzle-m801.dts new file mode 100644 index 0000000000..58edb5b3aa --- /dev/null +++ b/arch/arm/dts/armada-8040-puzzle-m801.dts @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016 Marvell International Ltd. + * Copyright (C) 2020 Sartura Ltd. + */ + +#include "armada-8040.dtsi" /* include SoC device tree */ + +/ { + model = "iEi-Puzzle-M801"; + compatible = "marvell,armada8040-puzzle-m801", + "marvell,armada8040"; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + aliases { + i2c0 = &i2c0; + i2c1 = &cpm_i2c0; + i2c2 = &cpm_i2c1; + i2c3 = &i2c_switch; + spi0 = &spi0; + gpio0 = &ap_gpio0; + gpio1 = &cpm_gpio0; + gpio2 = &cpm_gpio1; + gpio3 = &sfpplus_gpio; + }; + + memory@00000000 { + device_type = "memory"; + reg = <0x0 0x0 0x0 0x80000000>; + }; + + simple-bus { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + reg_usb3h0_vbus: usb3-vbus0 { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&cpm_xhci_vbus_pins>; + regulator-name = "reg-usb3h0-vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + startup-delay-us = <500000>; + enable-active-high; + regulator-always-on; + regulator-boot-on; + gpio = <&cpm_gpio1 15 GPIO_ACTIVE_HIGH>; /* GPIO[47] */ + }; + }; +}; + +&i2c0 { + status = "okay"; + clock-frequency = <100000>; + + rtc@32 { + compatible = "epson,rx8010"; + reg = <0x32>; + }; +}; + +&uart0 { + status = "okay"; +}; + +&ap_pinctl { + /* + * MPP Bus: + * AP SPI0 [0-3] + * AP I2C [4-5] + * AP GPIO [6] + * AP UART 1 RX/TX [7-8] + * AP GPIO [9-10] + * AP GPIO [12] + * UART0 [11,19] + */ + /* 0 1 2 3 4 5 6 7 8 9 */ + pin-func = < 3 3 3 3 3 3 3 3 3 0 + 0 3 0 0 0 0 0 0 0 3 >; +}; + +&cpm_pinctl { + /* + * MPP Bus: + * [0-31] = 0xff: Keep default CP0_shared_pins: + * [11] CLKOUT_MPP_11 (out) + * [23] LINK_RD_IN_CP2CP (in) + * [25] CLKOUT_MPP_25 (out) + * [29] AVS_FB_IN_CP2CP (in) + * [32,34] SMI + * [33] MSS power down + * [35-38] CP0 I2C1 and I2C0 + * [39] MSS CKE Enable + * [40,41] CP0 UART1 TX/RX + * [42,43] XSMI (controls two 10G phys) + * [47] USB VBUS EN + * [48] FAN PWM + * [49] 10G port 1 interrupt + * [50] 10G port 0 interrupt + * [51] 2.5G SFP TX fault + * [52] PCIe reset out + * [53] 2.5G SFP mode + * [54] 2.5G SFP LOS + * [55] Micro SD card detect + * [56-61] Micro SD + * [62] CP1 SFI SFP FAULT + */ + /* 0 1 2 3 4 5 6 7 8 9 */ + pin-func = < 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff + 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff + 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff + 0xff 0 7 0xa 7 2 2 2 2 0xa + 7 7 8 8 0 0 0 0 0 0 + 0 0 0 0 0 0 0xe 0xe 0xe 0xe + 0xe 0xe 0 >; + + cpm_xhci_vbus_pins: cpm-xhci-vbus-pins { + marvell,pins = < 47 >; + marvell,function = <0>; + }; + + cpm_pcie_reset_pins: cpm-pcie-reset-pins { + marvell,pins = < 52 >; + marvell,function = <0>; + }; +}; + +&cpm_sdhci0 { + pinctrl-names = "default"; + pinctrl-0 = <&cpm_sdhci_pins>; + bus-width= <4>; + status = "okay"; +}; + +&cpm_pcie0 { + num-lanes = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&cpm_pcie_reset_pins>; + marvell,reset-gpio = <&cpm_gpio1 20 GPIO_ACTIVE_LOW>; /* GPIO[52] */ + status = "okay"; +}; + +&cpm_i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&cpm_i2c0_pins>; + status = "okay"; + clock-frequency = <100000>; + + sfpplus_gpio: gpio@21 { + compatible = "nxp,pca9555"; + reg = <0x21>; + gpio-controller; + #gpio-cells = <2>; + }; +}; + +&cpm_i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&cpm_i2c1_pins>; + status = "okay"; + clock-frequency = <100000>; + + i2c_switch: i2c-switch@70 { + compatible = "nxp,pca9544"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x70>; + }; +}; + +&cpm_sata0 { + status = "okay"; +}; + +&cpm_ethernet { + pinctrl-names = "default"; + status = "okay"; +}; + +&cpm_mdio { + status = "okay"; + cpm_ge_phy0: ethernet-phy@1 { + reg = <0>; + }; + + cpm_ge_phy1: ethernet-phy@2 { + reg = <1>; + }; +}; + +&cpm_eth0 { + status = "okay"; + phy-mode = "sfi"; +}; + +&cpm_eth1 { + status = "okay"; + phy-mode = "sgmii"; + phy = <&cpm_ge_phy0>; +}; + +&cpm_eth2 { + status = "okay"; + phy-mode = "sgmii"; + phy = <&cpm_ge_phy1>; +}; + +&cpm_comphy { + /* + * CP0 Serdes Configuration: + * Lane 0: PCIe0 (x1) + * Lane 1: SGMII2 + * Lane 2: SATA0 + * Lane 3: SGMII1 + * Lane 4: SFI (10G) + * Lane 5: SATA1 + */ + phy0 { + phy-type = ; + }; + phy1 { + phy-type = ; + phy-speed = ; + }; + phy2 { + phy-type = ; + }; + phy3 { + phy-type = ; + phy-speed = ; + }; + phy4 { + phy-type = ; + }; + phy5 { + phy-type = ; + }; +}; + +&cps_mdio { + status = "okay"; + cps_ge_phy0: ethernet-phy@3 { + reg = <1>; + }; + + cps_ge_phy1: ethernet-phy@4 { + reg = <0>; + }; +}; + +&cps_pcie0 { + num-lanes = <2>; + pinctrl-names = "default"; + status = "okay"; +}; + +&cps_usb3_0 { + vbus-supply = <®_usb3h0_vbus>; + status = "okay"; +}; + +&cps_utmi0 { + status = "okay"; +}; + +&cps_ethernet { + status = "okay"; +}; + +&cps_eth0 { + status = "okay"; + phy-mode = "sfi"; +}; + +&cps_eth1 { + status = "okay"; + phy = <&cps_ge_phy0>; + phy-mode = "sgmii"; +}; + +&cps_eth2 { + status = "okay"; + phy = <&cps_ge_phy1>; + phy-mode = "sgmii"; +}; + +&cps_pinctl { + /* + * MPP Bus: + * [0-5] TDM + * [6,7] CP1_UART 0 + * [8] CP1 10G SFP LOS + * [9] CP1 10G PHY RESET + * [10] CP1 10G SFP TX Disable + * [11] CP1 10G SFP Mode + * [12] SPI1 CS1n + * [13] SPI1 MISO (TDM and SPI ROM shared) + * [14] SPI1 CS0n + * [15] SPI1 MOSI (TDM and SPI ROM shared) + * [16] SPI1 CLK (TDM and SPI ROM shared) + * [24] CP1 2.5G SFP TX Disable + * [26] CP0 10G SFP TX Fault + * [27] CP0 10G SFP Mode + * [28] CP0 10G SFP LOS + * [29] CP0 10G SFP TX Disable + * [30] USB Over current indication + * [31] 10G Port 0 phy reset + * [32-62] = 0xff: Keep default CP1_shared_pins: + */ + /* 0 1 2 3 4 5 6 7 8 9 */ + pin-func = < 0x4 0x4 0x4 0x4 0x4 0x4 0x8 0x8 0x0 0x0 + 0x0 0x0 0x3 0x3 0x3 0x3 0x3 0xff 0xff 0xff + 0xff 0xff 0xff 0xff 0x0 0xff 0x0 0x0 0x0 0x0 + 0x0 0x0 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff + 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff + 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff + 0xff 0xff 0xff>; +}; + +&spi0 { + status = "okay"; + + spi-flash@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <10000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@u-boot { + reg = <0x00000000 0x001f0000>; + label = "u-boot"; + }; + partition@u-boot-env { + reg = <0x001f0000 0x00010000>; + label = "u-boot-env"; + }; + partition@ubi1 { + reg = <0x00200000 0x03f00000>; + label = "ubi1"; + }; + partition@ubi2 { + reg = <0x04100000 0x03f00000>; + label = "ubi2"; + }; + }; + }; +}; + +&cps_comphy { + /* + * CP1 Serdes Configuration: + * Lane 0: PCIe0 (x2) + * Lane 1: PCIe0 (x2) + * Lane 2: USB HOST 0 + * Lane 3: SGMII1 + * Lane 4: SFI (10G) + * Lane 5: SGMII2 + */ + phy0 { + phy-type = ; + }; + phy1 { + phy-type = ; + }; + phy2 { + phy-type = ; + }; + phy3 { + phy-type = ; + phy-speed = ; + }; + phy4 { + phy-type = ; + }; + phy5 { + phy-type = ; + phy-speed = ; + }; +}; diff --git a/board/Marvell/mvebu_armada-8k/MAINTAINERS b/board/Marvell/mvebu_armada-8k/MAINTAINERS index 2551ed02c5..15660cd17d 100644 --- a/board/Marvell/mvebu_armada-8k/MAINTAINERS +++ b/board/Marvell/mvebu_armada-8k/MAINTAINERS @@ -10,3 +10,9 @@ MACCHIATOBin BOARD M: Konstantin Porotchkin S: Maintained F: configs/mvebu_mcbin-88f8040_defconfig + +Puzzle-M801 BOARD +M: Luka Kovacic +S: Maintained +F: configs/mvebu_puzzle-m801-88f8040_defconfig +F: arch/arm/dts/armada-8040-puzzle-m801.dts diff --git a/configs/mvebu_puzzle-m801-88f8040_defconfig b/configs/mvebu_puzzle-m801-88f8040_defconfig new file mode 100644 index 0000000000..d6fa829c2a --- /dev/null +++ b/configs/mvebu_puzzle-m801-88f8040_defconfig @@ -0,0 +1,91 @@ +CONFIG_ARM=y +CONFIG_ARCH_CPU_INIT=y +CONFIG_ARCH_MVEBU=y +CONFIG_SYS_TEXT_BASE=0x00000000 +CONFIG_SYS_MALLOC_F_LEN=0x2000 +CONFIG_TARGET_MVEBU_ARMADA_8K=y +CONFIG_ENV_SIZE=0x10000 +CONFIG_ENV_SECT_SIZE=0x10000 +CONFIG_ENV_OFFSET=0x1F0000 +CONFIG_NR_DRAM_BANKS=2 +CONFIG_DEBUG_UART_BASE=0xf0512000 +CONFIG_DEBUG_UART_CLOCK=200000000 +CONFIG_DEBUG_UART=y +CONFIG_AHCI=y +CONFIG_DISTRO_DEFAULTS=y +# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set +CONFIG_USE_PREBOOT=y +CONFIG_SYS_CONSOLE_INFO_QUIET=y +# CONFIG_DISPLAY_CPUINFO is not set +# CONFIG_DISPLAY_BOARDINFO is not set +CONFIG_DISPLAY_BOARDINFO_LATE=y +CONFIG_AUTOBOOT_KEYED=y +CONFIG_AUTOBOOT_PROMPT="Autoboot in %d seconds, to stop use 's' key\n" +CONFIG_AUTOBOOT_STOP_STR="s" +CONFIG_AUTOBOOT_KEYED_CTRLC=y +CONFIG_ARCH_EARLY_INIT_R=y +CONFIG_BOARD_EARLY_INIT_F=y +# CONFIG_EFI_LOADER is not set +# CONFIG_CMD_FLASH is not set +CONFIG_CMD_GPIO=y +CONFIG_CMD_I2C=y +CONFIG_CMD_MMC=y +CONFIG_CMD_PCI=y +CONFIG_CMD_SPI=y +CONFIG_CMD_USB=y +# CONFIG_CMD_SETEXPR is not set +CONFIG_CMD_TFTPPUT=y +CONFIG_CMD_CACHE=y +CONFIG_CMD_TIME=y +CONFIG_CMD_MVEBU_BUBT=y +CONFIG_CMD_REGULATOR=y +CONFIG_CMD_EXT4_WRITE=y +CONFIG_MAC_PARTITION=y +CONFIG_DEFAULT_DEVICE_TREE="armada-8040-puzzle-m801" +CONFIG_ENV_IS_IN_SPI_FLASH=y +CONFIG_SYS_RELOC_GD_ENV_ADDR=y +CONFIG_NET_RANDOM_ETHADDR=y +CONFIG_AHCI_MVEBU=y +CONFIG_DM_GPIO=y +CONFIG_DM_PCA953X=y +CONFIG_DM_I2C=y +CONFIG_SYS_I2C_MVTWSI=y +CONFIG_I2C_MUX=y +CONFIG_I2C_MUX_PCA954x=y +CONFIG_DM_RTC=y +CONFIG_RTC_RX8010SJ=y +CONFIG_MISC=y +CONFIG_DM_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_XENON=y +CONFIG_SF_DEFAULT_MODE=0 +CONFIG_SPI_FLASH_SFDP_SUPPORT=y +CONFIG_SPI_FLASH_BAR=y +CONFIG_SPI_FLASH_MACRONIX=y +# CONFIG_SPI_FLASH_SPANSION is not set +# CONFIG_SPI_FLASH_STMICRO is not set +# CONFIG_SPI_FLASH_WINBOND is not set +CONFIG_PHY_MARVELL=y +CONFIG_PHY_GIGE=y +CONFIG_MVPP2=y +CONFIG_NVME=y +CONFIG_PCI=y +CONFIG_DM_PCI=y +CONFIG_PCIE_DW_MVEBU=y +CONFIG_MVEBU_COMPHY_SUPPORT=y +CONFIG_PINCTRL=y +CONFIG_PINCTRL_ARMADA_8K=y +CONFIG_DM_REGULATOR_FIXED=y +CONFIG_DEBUG_UART_SHIFT=2 +CONFIG_DEBUG_UART_ANNOUNCE=y +CONFIG_SYS_NS16550=y +CONFIG_KIRKWOOD_SPI=y +CONFIG_USB=y +CONFIG_DM_USB=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_HOST_ETHER is not set +# CONFIG_USB_ETHER_ASIX is not set +# CONFIG_USB_ETHER_MCS7830 is not set +# CONFIG_USB_ETHER_RTL8152 is not set +# CONFIG_USB_ETHER_SMSC95XX is not set -- cgit v1.2.1 From 87c220d0419a6073bd20184cbb81e15214affda0 Mon Sep 17 00:00:00 2001 From: Luka Kovacic Date: Sat, 29 Aug 2020 00:35:51 +0200 Subject: arm: mvebu: mvebu_armada-8k: Add support for initializing iEi Puzzle-M801 networking Add support for the marvell,armada8040-puzzle-m801 compatible string in the board/Marvell/mvebu_armada-8k/board.c file to initialize the networking on iEi Puzzle-M801 board (2x CP1 1 Gb ports). Signed-off-by: Luka Kovacic Cc: Luka Perkov Reviewed-by: Stefan Roese --- board/Marvell/mvebu_armada-8k/board.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/board/Marvell/mvebu_armada-8k/board.c b/board/Marvell/mvebu_armada-8k/board.c index 60b0024630..bf8a929ec1 100644 --- a/board/Marvell/mvebu_armada-8k/board.c +++ b/board/Marvell/mvebu_armada-8k/board.c @@ -34,6 +34,17 @@ DECLARE_GLOBAL_DATA_PTR; #define I2C_IO_REG_CL ((1 << I2C_IO_REG_0_USB_H0_CL) | \ (1 << I2C_IO_REG_0_USB_H1_CL)) +/* + * Information specific to the iEi Puzzle-M801 board. + */ + +/* Internal configuration registers */ +#define CP1_CONF_REG_BASE 0xf4440000 +#define CONF_REG_MPP0 0x0 +#define CONF_REG_MPP1 0x4 +#define CONF_REG_MPP2 0x8 +#define CONF_REG_MPP3 0xC + static int usb_enabled = 0; /* Board specific xHCI dis-/enable code */ @@ -141,7 +152,14 @@ int board_xhci_enable(fdt_addr_t base) int board_early_init_f(void) { - /* Nothing to do (yet), perhaps later some pin-muxing etc */ + /* Initialize some platform specific memory locations */ + if (of_machine_is_compatible("marvell,armada8040-puzzle-m801")) { + /* MPP setup */ + writel(0x00444444, CP1_CONF_REG_BASE + CONF_REG_MPP0); + writel(0x00000000, CP1_CONF_REG_BASE + CONF_REG_MPP1); + writel(0x00000000, CP1_CONF_REG_BASE + CONF_REG_MPP2); + writel(0x08888000, CP1_CONF_REG_BASE + CONF_REG_MPP3); + } return 0; } -- cgit v1.2.1 From 2df286390a8009238ab9ccf5694ac088f3f90da3 Mon Sep 17 00:00:00 2001 From: George Hilliard Date: Wed, 30 Sep 2020 09:28:21 -0500 Subject: spi: mvebu_a3700_spi: add support for cs-gpios The device tree has a way to specify GPIO lines as chip selects. From the binding docs: So if for example the controller has 2 CS lines, and the cs-gpios property looks like this: cs-gpios = <&gpio1 0 0> <0> <&gpio1 1 0> <&gpio1 2 0>; Then it should be configured so that num_chipselect = 4 with the following mapping: cs0 : &gpio1 0 0 cs1 : native cs2 : &gpio1 1 0 cs3 : &gpio1 2 0 Add support for this, while retaining backward-compatibility with existing device trees; the driver will preserve existing behavior if a cs-gpios list is not given, or if a particular line is specified as <0> (native). This implementation is inspired by similar implementations in neighboring drivers for other platforms: atmega, mxc, etc. Signed-off-by: George Hilliard Reviewed-by: Stefan Roese --- drivers/spi/mvebu_a3700_spi.c | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/drivers/spi/mvebu_a3700_spi.c b/drivers/spi/mvebu_a3700_spi.c index e860b9ec64..eb13cf349e 100644 --- a/drivers/spi/mvebu_a3700_spi.c +++ b/drivers/spi/mvebu_a3700_spi.c @@ -15,6 +15,7 @@ #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -27,6 +28,7 @@ DECLARE_GLOBAL_DATA_PTR; #define MVEBU_SPI_A3700_SPI_EN_0 BIT(16) #define MVEBU_SPI_A3700_CLK_PRESCALE_MASK 0x1f +#define MAX_CS_COUNT 4 /* SPI registers */ struct spi_reg { @@ -39,16 +41,23 @@ struct spi_reg { struct mvebu_spi_platdata { struct spi_reg *spireg; struct clk clk; + struct gpio_desc cs_gpios[MAX_CS_COUNT]; }; -static void spi_cs_activate(struct spi_reg *reg, int cs) +static void spi_cs_activate(struct mvebu_spi_platdata *plat, int cs) { - setbits_le32(®->ctrl, MVEBU_SPI_A3700_SPI_EN_0 << cs); + if (CONFIG_IS_ENABLED(DM_GPIO) && dm_gpio_is_valid(&plat->cs_gpios[cs])) + dm_gpio_set_value(&plat->cs_gpios[cs], 1); + else + setbits_le32(&plat->spireg->ctrl, MVEBU_SPI_A3700_SPI_EN_0 << cs); } -static void spi_cs_deactivate(struct spi_reg *reg, int cs) +static void spi_cs_deactivate(struct mvebu_spi_platdata *plat, int cs) { - clrbits_le32(®->ctrl, MVEBU_SPI_A3700_SPI_EN_0 << cs); + if (CONFIG_IS_ENABLED(DM_GPIO) && dm_gpio_is_valid(&plat->cs_gpios[cs])) + dm_gpio_set_value(&plat->cs_gpios[cs], 0); + else + clrbits_le32(&plat->spireg->ctrl, MVEBU_SPI_A3700_SPI_EN_0 << cs); } /** @@ -150,7 +159,7 @@ static int mvebu_spi_xfer(struct udevice *dev, unsigned int bitlen, /* Activate CS */ if (flags & SPI_XFER_BEGIN) { debug("SPI: activate cs.\n"); - spi_cs_activate(reg, spi_chip_select(dev)); + spi_cs_activate(plat, spi_chip_select(dev)); } /* Send and/or receive */ @@ -169,7 +178,7 @@ static int mvebu_spi_xfer(struct udevice *dev, unsigned int bitlen, return ret; debug("SPI: deactivate cs.\n"); - spi_cs_deactivate(reg, spi_chip_select(dev)); + spi_cs_deactivate(plat, spi_chip_select(dev)); } return 0; @@ -247,6 +256,26 @@ static int mvebu_spi_probe(struct udevice *bus) writel(data, ®->cfg); + /* Set up CS GPIOs in device tree, if any */ + if (CONFIG_IS_ENABLED(DM_GPIO) && gpio_get_list_count(bus, "cs-gpios") > 0) { + int i; + + for (i = 0; i < ARRAY_SIZE(plat->cs_gpios); i++) { + ret = gpio_request_by_name(bus, "cs-gpios", i, &plat->cs_gpios[i], 0); + if (ret < 0 || !dm_gpio_is_valid(&plat->cs_gpios[i])) { + /* Use the native CS function for this line */ + continue; + } + + ret = dm_gpio_set_dir_flags(&plat->cs_gpios[i], + GPIOD_IS_OUT | GPIOD_ACTIVE_LOW); + if (ret) { + dev_err(bus, "Setting cs %d error\n", i); + return ret; + } + } + } + return 0; } -- cgit v1.2.1 From 559ae35c96794446a792d1927def38e517712278 Mon Sep 17 00:00:00 2001 From: Andre Heider Date: Fri, 2 Oct 2020 07:51:12 +0200 Subject: arm64: dts: armada-3720-espressobin: use Linux model/compatible strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the actual board vendor and ease synching dts files from Linux. Signed-off-by: Andre Heider Reviewed-by: Pali Rohár Reviewed-by: Stefan Roese --- arch/arm/dts/armada-3720-espressobin.dts | 4 ++-- board/Marvell/mvebu_armada-37xx/board.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/arm/dts/armada-3720-espressobin.dts b/arch/arm/dts/armada-3720-espressobin.dts index 4534f5ff29..be67a45870 100644 --- a/arch/arm/dts/armada-3720-espressobin.dts +++ b/arch/arm/dts/armada-3720-espressobin.dts @@ -50,8 +50,8 @@ #include "armada-372x.dtsi" / { - model = "Marvell Armada 3720 Community Board ESPRESSOBin"; - compatible = "marvell,armada-3720-espressobin", "marvell,armada3720", "marvell,armada3710"; + model = "Globalscale Marvell ESPRESSOBin Board"; + compatible = "globalscale,espressobin", "marvell,armada3720", "marvell,armada3710"; chosen { stdout-path = "serial0:115200n8"; diff --git a/board/Marvell/mvebu_armada-37xx/board.c b/board/Marvell/mvebu_armada-37xx/board.c index 2bfc7171c4..73d69e0388 100644 --- a/board/Marvell/mvebu_armada-37xx/board.c +++ b/board/Marvell/mvebu_armada-37xx/board.c @@ -88,14 +88,14 @@ int board_late_init(void) if (env_get("fdtfile")) return 0; - if (!of_machine_is_compatible("marvell,armada-3720-espressobin")) + if (!of_machine_is_compatible("globalscale,espressobin")) return 0; /* If the memory controller has been configured for DDR4, we're running on v7 */ ddr4 = ((readl(A3700_CH0_MC_CTRL2_REG) >> A3700_MC_CTRL2_SDRAM_TYPE_OFFS) & A3700_MC_CTRL2_SDRAM_TYPE_MASK) == A3700_MC_CTRL2_SDRAM_TYPE_DDR4; - emmc = of_machine_is_compatible("marvell,armada-3720-espressobin-emmc"); + emmc = of_machine_is_compatible("globalscale,espressobin-emmc"); if (ddr4 && emmc) env_set("fdtfile", "marvell/armada-3720-espressobin-v7-emmc.dtb"); @@ -248,7 +248,7 @@ static int mii_multi_chip_mode_write(struct mii_dev *bus, int dev_smi_addr, /* Bring-up board-specific network stuff */ int board_network_enable(struct mii_dev *bus) { - if (!of_machine_is_compatible("marvell,armada-3720-espressobin")) + if (!of_machine_is_compatible("globalscale,espressobin")) return 0; /* @@ -300,7 +300,7 @@ int ft_board_setup(void *blob, struct bd_info *bd) int part_off; /* Fill SPI MTD partitions for Linux kernel on Espressobin */ - if (!of_machine_is_compatible("marvell,armada-3720-espressobin")) + if (!of_machine_is_compatible("globalscale,espressobin")) return 0; spi_off = fdt_node_offset_by_compatible(blob, -1, "jedec,spi-nor"); -- cgit v1.2.1 From 03bb6a9b1ed7085794c5f167307273d15c99d3f0 Mon Sep 17 00:00:00 2001 From: Andre Heider Date: Fri, 4 Sep 2020 17:33:53 +0200 Subject: arm64: dts: armada-3720-espressobin: split common parts to .dtsi Move most of the dts to the new common armada-3720-espressobin.dtsi file, just like Linux, but keep the current, downstream based, version. The dts itself is imported from Linux. Signed-off-by: Andre Heider --- arch/arm/dts/armada-3720-espressobin.dts | 164 ++--------------------------- arch/arm/dts/armada-3720-espressobin.dtsi | 167 ++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+), 157 deletions(-) create mode 100644 arch/arm/dts/armada-3720-espressobin.dtsi diff --git a/arch/arm/dts/armada-3720-espressobin.dts b/arch/arm/dts/armada-3720-espressobin.dts index be67a45870..1542d836c0 100644 --- a/arch/arm/dts/armada-3720-espressobin.dts +++ b/arch/arm/dts/armada-3720-espressobin.dts @@ -1,170 +1,20 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* - * Device Tree file for Marvell Armada 3720 community board - * (ESPRESSOBin) + * Device Tree file for Globalscale Marvell ESPRESSOBin Board * Copyright (C) 2016 Marvell * - * Gregory CLEMENT - * Konstantin Porotchkin + * Romain Perier * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This file is distributed in the hope that it will be useful - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. + */ +/* + * Schematic available at http://espressobin.net/wp-content/uploads/2017/08/ESPRESSObin_V5_Schematics.pdf */ /dts-v1/; -#include "armada-372x.dtsi" +#include "armada-3720-espressobin.dtsi" / { model = "Globalscale Marvell ESPRESSOBin Board"; compatible = "globalscale,espressobin", "marvell,armada3720", "marvell,armada3710"; - - chosen { - stdout-path = "serial0:115200n8"; - }; - - aliases { - ethernet0 = ð0; - i2c0 = &i2c0; - spi0 = &spi0; - }; - - memory { - device_type = "memory"; - reg = <0x00000000 0x00000000 0x00000000 0x20000000>; - }; - - vcc_sd_reg0: regulator@0 { - compatible = "regulator-gpio"; - regulator-name = "vcc_sd0"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <3300000>; - regulator-type = "voltage"; - states = <1800000 0x1 - 3300000 0x0>; - gpios = <&gpionb 4 GPIO_ACTIVE_HIGH>; - }; -}; - -&comphy { - max-lanes = <3>; - phy0 { - phy-type = ; - phy-speed = ; - }; - - phy1 { - phy-type = ; - phy-speed = ; - }; - - phy2 { - phy-type = ; - phy-speed = ; - }; -}; - -ð0 { - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <&rgmii_pins>, <&smi_pins>; - phy-mode = "rgmii"; - phy_addr = <0x1>; - fixed-link { - speed = <1000>; - full-duplex; - }; -}; - -&i2c0 { - pinctrl-names = "default"; - pinctrl-0 = <&i2c1_pins>; - status = "okay"; -}; - -/* CON3 */ -&sata { - status = "okay"; -}; - -&sdhci0 { - pinctrl-names = "default"; - pinctrl-0 = <&sdio_pins>; - bus-width = <4>; - cd-gpios = <&gpionb 3 GPIO_ACTIVE_LOW>; - vqmmc-supply = <&vcc_sd_reg0>; - status = "okay"; -}; - -&spi0 { - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <&spi_quad_pins>; - - spi-flash@0 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "st,m25p128", "jedec,spi-nor"; - reg = <0>; /* Chip select 0 */ - spi-max-frequency = <50000000>; - m25p,fast-read; - }; -}; - -/* Exported on the micro USB connector CON32 through an FTDI */ -&uart0 { - pinctrl-names = "default"; - pinctrl-0 = <&uart1_pins>; - status = "okay"; -}; - -/* CON29 */ -&usb2 { - status = "okay"; -}; - -/* CON31 */ -&usb3 { - status = "okay"; -}; - -&pcie0 { - pinctrl-names = "default"; - pinctrl-0 = <&pcie_pins>; - reset-gpios = <&gpiosb 3 GPIO_ACTIVE_LOW>; - status = "okay"; }; diff --git a/arch/arm/dts/armada-3720-espressobin.dtsi b/arch/arm/dts/armada-3720-espressobin.dtsi new file mode 100644 index 0000000000..05dec89834 --- /dev/null +++ b/arch/arm/dts/armada-3720-espressobin.dtsi @@ -0,0 +1,167 @@ +/* + * Device Tree file for Marvell Armada 3720 community board + * (ESPRESSOBin) + * Copyright (C) 2016 Marvell + * + * Gregory CLEMENT + * Konstantin Porotchkin + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Or, alternatively + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/dts-v1/; + +#include "armada-372x.dtsi" + +/ { + chosen { + stdout-path = "serial0:115200n8"; + }; + + aliases { + ethernet0 = ð0; + i2c0 = &i2c0; + spi0 = &spi0; + }; + + memory { + device_type = "memory"; + reg = <0x00000000 0x00000000 0x00000000 0x20000000>; + }; + + vcc_sd_reg0: regulator@0 { + compatible = "regulator-gpio"; + regulator-name = "vcc_sd0"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-type = "voltage"; + states = <1800000 0x1 + 3300000 0x0>; + gpios = <&gpionb 4 GPIO_ACTIVE_HIGH>; + }; +}; + +&comphy { + max-lanes = <3>; + phy0 { + phy-type = ; + phy-speed = ; + }; + + phy1 { + phy-type = ; + phy-speed = ; + }; + + phy2 { + phy-type = ; + phy-speed = ; + }; +}; + +ð0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&rgmii_pins>, <&smi_pins>; + phy-mode = "rgmii"; + phy_addr = <0x1>; + fixed-link { + speed = <1000>; + full-duplex; + }; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + status = "okay"; +}; + +/* CON3 */ +&sata { + status = "okay"; +}; + +&sdhci0 { + pinctrl-names = "default"; + pinctrl-0 = <&sdio_pins>; + bus-width = <4>; + cd-gpios = <&gpionb 3 GPIO_ACTIVE_LOW>; + vqmmc-supply = <&vcc_sd_reg0>; + status = "okay"; +}; + +&spi0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&spi_quad_pins>; + + spi-flash@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "st,m25p128", "jedec,spi-nor"; + reg = <0>; /* Chip select 0 */ + spi-max-frequency = <50000000>; + m25p,fast-read; + }; +}; + +/* Exported on the micro USB connector CON32 through an FTDI */ +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "okay"; +}; + +/* CON29 */ +&usb2 { + status = "okay"; +}; + +/* CON31 */ +&usb3 { + status = "okay"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie_pins>; + reset-gpios = <&gpiosb 3 GPIO_ACTIVE_LOW>; + status = "okay"; +}; -- cgit v1.2.1 From f1a43c84a960265309fa8365759de271a70c5a7e Mon Sep 17 00:00:00 2001 From: Andre Heider Date: Fri, 4 Sep 2020 17:33:54 +0200 Subject: arm64: dts: a3720: add support for espressobin with populated emmc Import armada-3720-espressobin-emmc.dts from Linux, but use sdhc1 for emmc, since our dtsi is still based on downstream and sdhc0 is used for the sd card. Signed-off-by: Andre Heider --- arch/arm/dts/Makefile | 1 + arch/arm/dts/armada-3720-espressobin-emmc.dts | 44 +++++++++++++++++++++++++++ doc/README.marvell | 7 +++-- 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 arch/arm/dts/armada-3720-espressobin-emmc.dts diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 0b9c4c5eb0..b195723f16 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -202,6 +202,7 @@ dtb-$(CONFIG_ARCH_TEGRA) += tegra20-harmony.dtb \ dtb-$(CONFIG_ARCH_MVEBU) += \ armada-3720-db.dtb \ armada-3720-espressobin.dtb \ + armada-3720-espressobin-emmc.dtb \ armada-3720-turris-mox.dtb \ armada-3720-uDPU.dtb \ armada-375-db.dtb \ diff --git a/arch/arm/dts/armada-3720-espressobin-emmc.dts b/arch/arm/dts/armada-3720-espressobin-emmc.dts new file mode 100644 index 0000000000..29ccb6a573 --- /dev/null +++ b/arch/arm/dts/armada-3720-espressobin-emmc.dts @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Device Tree file for Globalscale Marvell ESPRESSOBin Board with eMMC + * Copyright (C) 2018 Marvell + * + * Romain Perier + * Konstantin Porotchkin + * + */ +/* + * Schematic available at http://espressobin.net/wp-content/uploads/2017/08/ESPRESSObin_V5_Schematics.pdf + */ + +/dts-v1/; + +#include "armada-3720-espressobin.dtsi" + +/ { + model = "Globalscale Marvell ESPRESSOBin Board (eMMC)"; + compatible = "globalscale,espressobin-emmc", "globalscale,espressobin", + "marvell,armada3720", "marvell,armada3710"; +}; + +/* U11 */ +&sdhci1 { + non-removable; + bus-width = <8>; + mmc-ddr-1_8v; + mmc-hs400-1_8v; + marvell,xenon-emmc; + marvell,xenon-tun-count = <9>; + marvell,pad-type = "fixed-1-8v"; + + pinctrl-names = "default"; + pinctrl-0 = <&mmc_pins>; + status = "okay"; + + #address-cells = <1>; + #size-cells = <0>; + mmccard: mmccard@0 { + compatible = "mmc-card"; + reg = <0>; + }; +}; diff --git a/doc/README.marvell b/doc/README.marvell index 6fc5ac8a40..6f05ad4cb1 100644 --- a/doc/README.marvell +++ b/doc/README.marvell @@ -43,8 +43,11 @@ Build Procedure In order to prevent this, the required device-tree MUST be set during compilation. All device-tree files are located in ./arch/arm/dts/ folder. - For other DB boards (MacchiatoBin, EspressoBin and 3700 DB board) compile u-boot with - just default device-tree from defconfig using: + For the EspressoBin board with populated eMMC device use + # make DEVICE_TREE=armada-3720-espressobin-emmc + + For other DB boards (MacchiatoBin, EspressoBin without soldered eMMC and 3700 DB board) + compile u-boot with just default device-tree from defconfig using: # make -- cgit v1.2.1 From 8369886b401d85e49d52ff5f877cf14b1619bd43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Mon, 5 Oct 2020 12:17:15 +0200 Subject: arm: mvebu: Remove old comments from configs/mvebu_armada-37xx.h file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These comments are relict for old, now removed config options. So remove these obsoleted comments too. Signed-off-by: Pali Rohár Reviewed-by: Stefan Roese --- include/configs/mvebu_armada-37xx.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/include/configs/mvebu_armada-37xx.h b/include/configs/mvebu_armada-37xx.h index 27428d5a0f..0d585606a7 100644 --- a/include/configs/mvebu_armada-37xx.h +++ b/include/configs/mvebu_armada-37xx.h @@ -17,8 +17,6 @@ #define CONFIG_SYS_BOOTM_LEN SZ_64M /* Increase max gunzip size */ -/* auto boot */ - #define CONFIG_SYS_BAUDRATE_TABLE { 9600, 19200, 38400, 57600, \ 115200, 230400, 460800, 921600 } @@ -57,11 +55,8 @@ /* * SPI Flash configuration */ - #define CONFIG_MTD_PARTITIONS /* required for UBI partition support */ -/* Environment in SPI NOR flash */ - /* * Ethernet Driver configuration */ @@ -70,8 +65,6 @@ #define CONFIG_USB_MAX_CONTROLLER_COUNT (3 + 3) -/* USB ethernet */ - /* * SATA/SCSI/AHCI configuration */ -- cgit v1.2.1