diff options
author | Luca Boccassi <luca.boccassi@gmail.com> | 2018-08-14 18:52:30 +0100 |
---|---|---|
committer | Luca Boccassi <luca.boccassi@gmail.com> | 2018-08-14 18:53:17 +0100 |
commit | b63264c8342e6a1b6971c79550d2af2024b6a4de (patch) | |
tree | 83114aac64286fe616506c0b3dfaec2ab86ef835 /drivers/net/nfp/nfpcore/nfp_cppcore.c | |
parent | ca33590b6af032bff57d9cc70455660466a654b2 (diff) |
New upstream version 18.08upstream/18.08
Change-Id: I32fdf5e5016556d9c0a6d88ddaf1fc468961790a
Signed-off-by: Luca Boccassi <luca.boccassi@gmail.com>
Diffstat (limited to 'drivers/net/nfp/nfpcore/nfp_cppcore.c')
-rw-r--r-- | drivers/net/nfp/nfpcore/nfp_cppcore.c | 858 |
1 files changed, 858 insertions, 0 deletions
diff --git a/drivers/net/nfp/nfpcore/nfp_cppcore.c b/drivers/net/nfp/nfpcore/nfp_cppcore.c new file mode 100644 index 00000000..75d3c974 --- /dev/null +++ b/drivers/net/nfp/nfpcore/nfp_cppcore.c @@ -0,0 +1,858 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Netronome Systems, Inc. + * All rights reserved. + */ + +#include <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> + +#include <rte_byteorder.h> +#include <rte_ethdev_pci.h> + +#include "nfp_cpp.h" +#include "nfp_target.h" +#include "nfp6000/nfp6000.h" +#include "nfp6000/nfp_xpb.h" +#include "nfp_nffw.h" + +#define NFP_PL_DEVICE_ID 0x00000004 +#define NFP_PL_DEVICE_ID_MASK 0xff + +#define NFP6000_ARM_GCSR_SOFTMODEL0 0x00400144 + +void +nfp_cpp_priv_set(struct nfp_cpp *cpp, void *priv) +{ + cpp->priv = priv; +} + +void * +nfp_cpp_priv(struct nfp_cpp *cpp) +{ + return cpp->priv; +} + +void +nfp_cpp_model_set(struct nfp_cpp *cpp, uint32_t model) +{ + cpp->model = model; +} + +uint32_t +nfp_cpp_model(struct nfp_cpp *cpp) +{ + if (!cpp) + return NFP_CPP_MODEL_INVALID; + + if (cpp->model == 0) + cpp->model = __nfp_cpp_model_autodetect(cpp); + + return cpp->model; +} + +void +nfp_cpp_interface_set(struct nfp_cpp *cpp, uint32_t interface) +{ + cpp->interface = interface; +} + +int +nfp_cpp_serial(struct nfp_cpp *cpp, const uint8_t **serial) +{ + *serial = cpp->serial; + return cpp->serial_len; +} + +int +nfp_cpp_serial_set(struct nfp_cpp *cpp, const uint8_t *serial, + size_t serial_len) +{ + if (cpp->serial_len) + free(cpp->serial); + + cpp->serial = malloc(serial_len); + if (!cpp->serial) + return -1; + + memcpy(cpp->serial, serial, serial_len); + cpp->serial_len = serial_len; + + return 0; +} + +uint16_t +nfp_cpp_interface(struct nfp_cpp *cpp) +{ + if (!cpp) + return NFP_CPP_INTERFACE(NFP_CPP_INTERFACE_TYPE_INVALID, 0, 0); + + return cpp->interface; +} + +void * +nfp_cpp_area_priv(struct nfp_cpp_area *cpp_area) +{ + return &cpp_area[1]; +} + +struct nfp_cpp * +nfp_cpp_area_cpp(struct nfp_cpp_area *cpp_area) +{ + return cpp_area->cpp; +} + +const char * +nfp_cpp_area_name(struct nfp_cpp_area *cpp_area) +{ + return cpp_area->name; +} + +/* + * nfp_cpp_area_alloc - allocate a new CPP area + * @cpp: CPP handle + * @dest: CPP id + * @address: start address on CPP target + * @size: size of area in bytes + * + * Allocate and initialize a CPP area structure. The area must later + * be locked down with an 'acquire' before it can be safely accessed. + * + * NOTE: @address and @size must be 32-bit aligned values. + */ +struct nfp_cpp_area * +nfp_cpp_area_alloc_with_name(struct nfp_cpp *cpp, uint32_t dest, + const char *name, unsigned long long address, + unsigned long size) +{ + struct nfp_cpp_area *area; + uint64_t tmp64 = (uint64_t)address; + int tmp, err; + + if (!cpp) + return NULL; + + /* CPP bus uses only a 40-bit address */ + if ((address + size) > (1ULL << 40)) + return NFP_ERRPTR(EFAULT); + + /* Remap from cpp_island to cpp_target */ + err = nfp_target_cpp(dest, tmp64, &dest, &tmp64, cpp->imb_cat_table); + if (err < 0) + return NULL; + + address = (unsigned long long)tmp64; + + if (!name) + name = ""; + + area = calloc(1, sizeof(*area) + cpp->op->area_priv_size + + strlen(name) + 1); + if (!area) + return NULL; + + area->cpp = cpp; + area->name = ((char *)area) + sizeof(*area) + cpp->op->area_priv_size; + memcpy(area->name, name, strlen(name) + 1); + + /* + * Preserve errno around the call to area_init, since most + * implementations will blindly call nfp_target_action_width()for both + * read or write modes, and that will set errno to EINVAL. + */ + tmp = errno; + + err = cpp->op->area_init(area, dest, address, size); + if (err < 0) { + free(area); + return NULL; + } + + /* Restore errno */ + errno = tmp; + + area->offset = address; + area->size = size; + + return area; +} + +struct nfp_cpp_area * +nfp_cpp_area_alloc(struct nfp_cpp *cpp, uint32_t dest, + unsigned long long address, unsigned long size) +{ + return nfp_cpp_area_alloc_with_name(cpp, dest, NULL, address, size); +} + +/* + * nfp_cpp_area_alloc_acquire - allocate a new CPP area and lock it down + * + * @cpp: CPP handle + * @dest: CPP id + * @address: start address on CPP target + * @size: size of area + * + * Allocate and initilizae a CPP area structure, and lock it down so + * that it can be accessed directly. + * + * NOTE: @address and @size must be 32-bit aligned values. + * + * NOTE: The area must also be 'released' when the structure is freed. + */ +struct nfp_cpp_area * +nfp_cpp_area_alloc_acquire(struct nfp_cpp *cpp, uint32_t destination, + unsigned long long address, unsigned long size) +{ + struct nfp_cpp_area *area; + + area = nfp_cpp_area_alloc(cpp, destination, address, size); + if (!area) + return NULL; + + if (nfp_cpp_area_acquire(area)) { + nfp_cpp_area_free(area); + return NULL; + } + + return area; +} + +/* + * nfp_cpp_area_free - free up the CPP area + * area: CPP area handle + * + * Frees up memory resources held by the CPP area. + */ +void +nfp_cpp_area_free(struct nfp_cpp_area *area) +{ + if (area->cpp->op->area_cleanup) + area->cpp->op->area_cleanup(area); + free(area); +} + +/* + * nfp_cpp_area_release_free - release CPP area and free it + * area: CPP area handle + * + * Releases CPP area and frees up memory resources held by the it. + */ +void +nfp_cpp_area_release_free(struct nfp_cpp_area *area) +{ + nfp_cpp_area_release(area); + nfp_cpp_area_free(area); +} + +/* + * nfp_cpp_area_acquire - lock down a CPP area for access + * @area: CPP area handle + * + * Locks down the CPP area for a potential long term activity. Area + * must always be locked down before being accessed. + */ +int +nfp_cpp_area_acquire(struct nfp_cpp_area *area) +{ + if (area->cpp->op->area_acquire) { + int err = area->cpp->op->area_acquire(area); + + if (err < 0) + return -1; + } + + return 0; +} + +/* + * nfp_cpp_area_release - release a locked down CPP area + * @area: CPP area handle + * + * Releases a previously locked down CPP area. + */ +void +nfp_cpp_area_release(struct nfp_cpp_area *area) +{ + if (area->cpp->op->area_release) + area->cpp->op->area_release(area); +} + +/* + * nfp_cpp_area_iomem() - get IOMEM region for CPP area + * + * @area: CPP area handle + * + * Returns an iomem pointer for use with readl()/writel() style operations. + * + * NOTE: Area must have been locked down with an 'acquire'. + * + * Return: pointer to the area, or NULL + */ +void * +nfp_cpp_area_iomem(struct nfp_cpp_area *area) +{ + void *iomem = NULL; + + if (area->cpp->op->area_iomem) + iomem = area->cpp->op->area_iomem(area); + + return iomem; +} + +/* + * nfp_cpp_area_read - read data from CPP area + * + * @area: CPP area handle + * @offset: offset into CPP area + * @kernel_vaddr: kernel address to put data into + * @length: number of bytes to read + * + * Read data from indicated CPP region. + * + * NOTE: @offset and @length must be 32-bit aligned values. + * + * NOTE: Area must have been locked down with an 'acquire'. + */ +int +nfp_cpp_area_read(struct nfp_cpp_area *area, unsigned long offset, + void *kernel_vaddr, size_t length) +{ + if ((offset + length) > area->size) + return NFP_ERRNO(EFAULT); + + return area->cpp->op->area_read(area, kernel_vaddr, offset, length); +} + +/* + * nfp_cpp_area_write - write data to CPP area + * + * @area: CPP area handle + * @offset: offset into CPP area + * @kernel_vaddr: kernel address to read data from + * @length: number of bytes to write + * + * Write data to indicated CPP region. + * + * NOTE: @offset and @length must be 32-bit aligned values. + * + * NOTE: Area must have been locked down with an 'acquire'. + */ +int +nfp_cpp_area_write(struct nfp_cpp_area *area, unsigned long offset, + const void *kernel_vaddr, size_t length) +{ + if ((offset + length) > area->size) + return NFP_ERRNO(EFAULT); + + return area->cpp->op->area_write(area, kernel_vaddr, offset, length); +} + +void * +nfp_cpp_area_mapped(struct nfp_cpp_area *area) +{ + if (area->cpp->op->area_mapped) + return area->cpp->op->area_mapped(area); + return NULL; +} + +/* + * nfp_cpp_area_check_range - check if address range fits in CPP area + * + * @area: CPP area handle + * @offset: offset into CPP area + * @length: size of address range in bytes + * + * Check if address range fits within CPP area. Return 0 if area fits + * or -1 on error. + */ +int +nfp_cpp_area_check_range(struct nfp_cpp_area *area, unsigned long long offset, + unsigned long length) +{ + if (((offset + length) > area->size)) + return NFP_ERRNO(EFAULT); + + return 0; +} + +/* + * Return the correct CPP address, and fixup xpb_addr as needed, + * based upon NFP model. + */ +static uint32_t +nfp_xpb_to_cpp(struct nfp_cpp *cpp, uint32_t *xpb_addr) +{ + uint32_t xpb; + int island; + + if (!NFP_CPP_MODEL_IS_6000(cpp->model)) + return 0; + + xpb = NFP_CPP_ID(14, NFP_CPP_ACTION_RW, 0); + + /* + * Ensure that non-local XPB accesses go out through the + * global XPBM bus. + */ + island = ((*xpb_addr) >> 24) & 0x3f; + + if (!island) + return xpb; + + if (island == 1) { + /* + * Accesses to the ARM Island overlay uses Island 0 + * Global Bit + */ + (*xpb_addr) &= ~0x7f000000; + if (*xpb_addr < 0x60000) + *xpb_addr |= (1 << 30); + else + /* And only non-ARM interfaces use island id = 1 */ + if (NFP_CPP_INTERFACE_TYPE_of(nfp_cpp_interface(cpp)) != + NFP_CPP_INTERFACE_TYPE_ARM) + *xpb_addr |= (1 << 24); + } else { + (*xpb_addr) |= (1 << 30); + } + + return xpb; +} + +int +nfp_cpp_area_readl(struct nfp_cpp_area *area, unsigned long offset, + uint32_t *value) +{ + int sz; + uint32_t tmp = 0; + + sz = nfp_cpp_area_read(area, offset, &tmp, sizeof(tmp)); + *value = rte_le_to_cpu_32(tmp); + + return (sz == sizeof(*value)) ? 0 : -1; +} + +int +nfp_cpp_area_writel(struct nfp_cpp_area *area, unsigned long offset, + uint32_t value) +{ + int sz; + + value = rte_cpu_to_le_32(value); + sz = nfp_cpp_area_write(area, offset, &value, sizeof(value)); + return (sz == sizeof(value)) ? 0 : -1; +} + +int +nfp_cpp_area_readq(struct nfp_cpp_area *area, unsigned long offset, + uint64_t *value) +{ + int sz; + uint64_t tmp = 0; + + sz = nfp_cpp_area_read(area, offset, &tmp, sizeof(tmp)); + *value = rte_le_to_cpu_64(tmp); + + return (sz == sizeof(*value)) ? 0 : -1; +} + +int +nfp_cpp_area_writeq(struct nfp_cpp_area *area, unsigned long offset, + uint64_t value) +{ + int sz; + + value = rte_cpu_to_le_64(value); + sz = nfp_cpp_area_write(area, offset, &value, sizeof(value)); + + return (sz == sizeof(value)) ? 0 : -1; +} + +int +nfp_cpp_readl(struct nfp_cpp *cpp, uint32_t cpp_id, unsigned long long address, + uint32_t *value) +{ + int sz; + uint32_t tmp; + + sz = nfp_cpp_read(cpp, cpp_id, address, &tmp, sizeof(tmp)); + *value = rte_le_to_cpu_32(tmp); + + return (sz == sizeof(*value)) ? 0 : -1; +} + +int +nfp_cpp_writel(struct nfp_cpp *cpp, uint32_t cpp_id, unsigned long long address, + uint32_t value) +{ + int sz; + + value = rte_cpu_to_le_32(value); + sz = nfp_cpp_write(cpp, cpp_id, address, &value, sizeof(value)); + + return (sz == sizeof(value)) ? 0 : -1; +} + +int +nfp_cpp_readq(struct nfp_cpp *cpp, uint32_t cpp_id, unsigned long long address, + uint64_t *value) +{ + int sz; + uint64_t tmp; + + sz = nfp_cpp_read(cpp, cpp_id, address, &tmp, sizeof(tmp)); + *value = rte_le_to_cpu_64(tmp); + + return (sz == sizeof(*value)) ? 0 : -1; +} + +int +nfp_cpp_writeq(struct nfp_cpp *cpp, uint32_t cpp_id, unsigned long long address, + uint64_t value) +{ + int sz; + + value = rte_cpu_to_le_64(value); + sz = nfp_cpp_write(cpp, cpp_id, address, &value, sizeof(value)); + + return (sz == sizeof(value)) ? 0 : -1; +} + +int +nfp_xpb_writel(struct nfp_cpp *cpp, uint32_t xpb_addr, uint32_t value) +{ + uint32_t cpp_dest; + + cpp_dest = nfp_xpb_to_cpp(cpp, &xpb_addr); + + return nfp_cpp_writel(cpp, cpp_dest, xpb_addr, value); +} + +int +nfp_xpb_readl(struct nfp_cpp *cpp, uint32_t xpb_addr, uint32_t *value) +{ + uint32_t cpp_dest; + + cpp_dest = nfp_xpb_to_cpp(cpp, &xpb_addr); + + return nfp_cpp_readl(cpp, cpp_dest, xpb_addr, value); +} + +static struct nfp_cpp * +nfp_cpp_alloc(struct rte_pci_device *dev, int driver_lock_needed) +{ + const struct nfp_cpp_operations *ops; + struct nfp_cpp *cpp; + int err; + + ops = nfp_cpp_transport_operations(); + + if (!ops || !ops->init) + return NFP_ERRPTR(EINVAL); + + cpp = calloc(1, sizeof(*cpp)); + if (!cpp) + return NULL; + + cpp->op = ops; + cpp->driver_lock_needed = driver_lock_needed; + + if (cpp->op->init) { + err = cpp->op->init(cpp, dev); + if (err < 0) { + free(cpp); + return NULL; + } + } + + if (NFP_CPP_MODEL_IS_6000(nfp_cpp_model(cpp))) { + uint32_t xpbaddr; + size_t tgt; + + for (tgt = 0; tgt < ARRAY_SIZE(cpp->imb_cat_table); tgt++) { + /* Hardcoded XPB IMB Base, island 0 */ + xpbaddr = 0x000a0000 + (tgt * 4); + err = nfp_xpb_readl(cpp, xpbaddr, + (uint32_t *)&cpp->imb_cat_table[tgt]); + if (err < 0) { + free(cpp); + return NULL; + } + } + } + + return cpp; +} + +/* + * nfp_cpp_free - free the CPP handle + * @cpp: CPP handle + */ +void +nfp_cpp_free(struct nfp_cpp *cpp) +{ + if (cpp->op && cpp->op->free) + cpp->op->free(cpp); + + if (cpp->serial_len) + free(cpp->serial); + + free(cpp); +} + +struct nfp_cpp * +nfp_cpp_from_device_name(struct rte_pci_device *dev, int driver_lock_needed) +{ + return nfp_cpp_alloc(dev, driver_lock_needed); +} + +/* + * Modify bits of a 32-bit value from the XPB bus + * + * @param cpp NFP CPP device handle + * @param xpb_tgt XPB target and address + * @param mask mask of bits to alter + * @param value value to modify + * + * @return 0 on success, or -1 on failure (and set errno accordingly). + */ +int +nfp_xpb_writelm(struct nfp_cpp *cpp, uint32_t xpb_tgt, uint32_t mask, + uint32_t value) +{ + int err; + uint32_t tmp; + + err = nfp_xpb_readl(cpp, xpb_tgt, &tmp); + if (err < 0) + return err; + + tmp &= ~mask; + tmp |= (mask & value); + return nfp_xpb_writel(cpp, xpb_tgt, tmp); +} + +/* + * Modify bits of a 32-bit value from the XPB bus + * + * @param cpp NFP CPP device handle + * @param xpb_tgt XPB target and address + * @param mask mask of bits to alter + * @param value value to monitor for + * @param timeout_us maximum number of us to wait (-1 for forever) + * + * @return >= 0 on success, or -1 on failure (and set errno accordingly). + */ +int +nfp_xpb_waitlm(struct nfp_cpp *cpp, uint32_t xpb_tgt, uint32_t mask, + uint32_t value, int timeout_us) +{ + uint32_t tmp; + int err; + + do { + err = nfp_xpb_readl(cpp, xpb_tgt, &tmp); + if (err < 0) + goto exit; + + if ((tmp & mask) == (value & mask)) { + if (timeout_us < 0) + timeout_us = 0; + break; + } + + if (timeout_us < 0) + continue; + + timeout_us -= 100; + usleep(100); + } while (timeout_us >= 0); + + if (timeout_us < 0) + err = NFP_ERRNO(ETIMEDOUT); + else + err = timeout_us; + +exit: + return err; +} + +/* + * nfp_cpp_read - read from CPP target + * @cpp: CPP handle + * @destination: CPP id + * @address: offset into CPP target + * @kernel_vaddr: kernel buffer for result + * @length: number of bytes to read + */ +int +nfp_cpp_read(struct nfp_cpp *cpp, uint32_t destination, + unsigned long long address, void *kernel_vaddr, size_t length) +{ + struct nfp_cpp_area *area; + int err; + + area = nfp_cpp_area_alloc_acquire(cpp, destination, address, length); + if (!area) { + printf("Area allocation/acquire failed\n"); + return -1; + } + + err = nfp_cpp_area_read(area, 0, kernel_vaddr, length); + + nfp_cpp_area_release_free(area); + return err; +} + +/* + * nfp_cpp_write - write to CPP target + * @cpp: CPP handle + * @destination: CPP id + * @address: offset into CPP target + * @kernel_vaddr: kernel buffer to read from + * @length: number of bytes to write + */ +int +nfp_cpp_write(struct nfp_cpp *cpp, uint32_t destination, + unsigned long long address, const void *kernel_vaddr, + size_t length) +{ + struct nfp_cpp_area *area; + int err; + + area = nfp_cpp_area_alloc_acquire(cpp, destination, address, length); + if (!area) + return -1; + + err = nfp_cpp_area_write(area, 0, kernel_vaddr, length); + + nfp_cpp_area_release_free(area); + return err; +} + +/* + * nfp_cpp_area_fill - fill a CPP area with a value + * @area: CPP area + * @offset: offset into CPP area + * @value: value to fill with + * @length: length of area to fill + */ +int +nfp_cpp_area_fill(struct nfp_cpp_area *area, unsigned long offset, + uint32_t value, size_t length) +{ + int err; + size_t i; + uint64_t value64; + + value = rte_cpu_to_le_32(value); + value64 = ((uint64_t)value << 32) | value; + + if ((offset + length) > area->size) + return NFP_ERRNO(EINVAL); + + if ((area->offset + offset) & 3) + return NFP_ERRNO(EINVAL); + + if (((area->offset + offset) & 7) == 4 && length >= 4) { + err = nfp_cpp_area_write(area, offset, &value, sizeof(value)); + if (err < 0) + return err; + if (err != sizeof(value)) + return NFP_ERRNO(ENOSPC); + offset += sizeof(value); + length -= sizeof(value); + } + + for (i = 0; (i + sizeof(value)) < length; i += sizeof(value64)) { + err = + nfp_cpp_area_write(area, offset + i, &value64, + sizeof(value64)); + if (err < 0) + return err; + if (err != sizeof(value64)) + return NFP_ERRNO(ENOSPC); + } + + if ((i + sizeof(value)) <= length) { + err = + nfp_cpp_area_write(area, offset + i, &value, sizeof(value)); + if (err < 0) + return err; + if (err != sizeof(value)) + return NFP_ERRNO(ENOSPC); + i += sizeof(value); + } + + return (int)i; +} + +/* + * NOTE: This code should not use nfp_xpb_* functions, + * as those are model-specific + */ +uint32_t +__nfp_cpp_model_autodetect(struct nfp_cpp *cpp) +{ + uint32_t arm_id = NFP_CPP_ID(NFP_CPP_TARGET_ARM, 0, 0); + uint32_t model = 0; + + nfp_cpp_readl(cpp, arm_id, NFP6000_ARM_GCSR_SOFTMODEL0, &model); + + if (NFP_CPP_MODEL_IS_6000(model)) { + uint32_t tmp; + + nfp_cpp_model_set(cpp, model); + + /* The PL's PluDeviceID revision code is authoratative */ + model &= ~0xff; + nfp_xpb_readl(cpp, NFP_XPB_DEVICE(1, 1, 16) + + NFP_PL_DEVICE_ID, &tmp); + model |= (NFP_PL_DEVICE_ID_MASK & tmp) - 0x10; + } + + return model; +} + +/* + * nfp_cpp_map_area() - Helper function to map an area + * @cpp: NFP CPP handler + * @domain: CPP domain + * @target: CPP target + * @addr: CPP address + * @size: Size of the area + * @area: Area handle (output) + * + * Map an area of IOMEM access. To undo the effect of this function call + * @nfp_cpp_area_release_free(*area). + * + * Return: Pointer to memory mapped area or ERR_PTR + */ +uint8_t * +nfp_cpp_map_area(struct nfp_cpp *cpp, int domain, int target, uint64_t addr, + unsigned long size, struct nfp_cpp_area **area) +{ + uint8_t *res; + uint32_t dest; + + dest = NFP_CPP_ISLAND_ID(target, NFP_CPP_ACTION_RW, 0, domain); + + *area = nfp_cpp_area_alloc_acquire(cpp, dest, addr, size); + if (!*area) + goto err_eio; + + res = nfp_cpp_area_iomem(*area); + if (!res) + goto err_release_free; + + return res; + +err_release_free: + nfp_cpp_area_release_free(*area); +err_eio: + return NULL; +} |