New upstream version 18.05
[deb_dpdk.git] / drivers / net / nfp / nfpcore / nfp_cppcore.c
diff --git a/drivers/net/nfp/nfpcore/nfp_cppcore.c b/drivers/net/nfp/nfpcore/nfp_cppcore.c
new file mode 100644 (file)
index 0000000..f61143f
--- /dev/null
@@ -0,0 +1,857 @@
+/* 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 "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(const char *devname, 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, devname);
+               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(const char *devname, int driver_lock_needed)
+{
+       return nfp_cpp_alloc(devname, 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;
+}