--- /dev/null
+/*
+ * Copyright 2015 IBM Corp.
+ *
+ * This program 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.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+
+#include "cxl.h"
+#include "hcalls.h"
+#include "trace.h"
+
+
+static irqreturn_t guest_handle_psl_slice_error(struct cxl_context *ctx, u64 dsisr,
+                                       u64 errstat)
+{
+       pr_devel("in %s\n", __func__);
+       dev_crit(&ctx->afu->dev, "PSL ERROR STATUS: 0x%.16llx\n", errstat);
+
+       return cxl_ops->ack_irq(ctx, 0, errstat);
+}
+
+static ssize_t guest_collect_vpd(struct cxl *adapter, struct cxl_afu *afu,
+                       void *buf, size_t len)
+{
+       unsigned int entries, mod;
+       unsigned long **vpd_buf = NULL;
+       struct sg_list *le;
+       int rc = 0, i, tocopy;
+       u64 out = 0;
+
+       if (buf == NULL)
+               return -EINVAL;
+
+       /* number of entries in the list */
+       entries = len / SG_BUFFER_SIZE;
+       mod = len % SG_BUFFER_SIZE;
+       if (mod)
+               entries++;
+
+       if (entries > SG_MAX_ENTRIES) {
+               entries = SG_MAX_ENTRIES;
+               len = SG_MAX_ENTRIES * SG_BUFFER_SIZE;
+               mod = 0;
+       }
+
+       vpd_buf = kzalloc(entries * sizeof(unsigned long *), GFP_KERNEL);
+       if (!vpd_buf)
+               return -ENOMEM;
+
+       le = (struct sg_list *)get_zeroed_page(GFP_KERNEL);
+       if (!le) {
+               rc = -ENOMEM;
+               goto err1;
+       }
+
+       for (i = 0; i < entries; i++) {
+               vpd_buf[i] = (unsigned long *)get_zeroed_page(GFP_KERNEL);
+               if (!vpd_buf[i]) {
+                       rc = -ENOMEM;
+                       goto err2;
+               }
+               le[i].phys_addr = cpu_to_be64(virt_to_phys(vpd_buf[i]));
+               le[i].len = cpu_to_be64(SG_BUFFER_SIZE);
+               if ((i == (entries - 1)) && mod)
+                       le[i].len = cpu_to_be64(mod);
+       }
+
+       if (adapter)
+               rc = cxl_h_collect_vpd_adapter(adapter->guest->handle,
+                                       virt_to_phys(le), entries, &out);
+       else
+               rc = cxl_h_collect_vpd(afu->guest->handle, 0,
+                               virt_to_phys(le), entries, &out);
+       pr_devel("length of available (entries: %i), vpd: %#llx\n",
+               entries, out);
+
+       if (!rc) {
+               /*
+                * hcall returns in 'out' the size of available VPDs.
+                * It fills the buffer with as much data as possible.
+                */
+               if (out < len)
+                       len = out;
+               rc = len;
+               if (out) {
+                       for (i = 0; i < entries; i++) {
+                               if (len < SG_BUFFER_SIZE)
+                                       tocopy = len;
+                               else
+                                       tocopy = SG_BUFFER_SIZE;
+                               memcpy(buf, vpd_buf[i], tocopy);
+                               buf += tocopy;
+                               len -= tocopy;
+                       }
+               }
+       }
+err2:
+       for (i = 0; i < entries; i++) {
+               if (vpd_buf[i])
+                       free_page((unsigned long) vpd_buf[i]);
+       }
+       free_page((unsigned long) le);
+err1:
+       kfree(vpd_buf);
+       return rc;
+}
+
+static int guest_get_irq_info(struct cxl_context *ctx, struct cxl_irq_info *info)
+{
+       return cxl_h_collect_int_info(ctx->afu->guest->handle, ctx->process_token, info);
+}
+
+static irqreturn_t guest_psl_irq(int irq, void *data)
+{
+       struct cxl_context *ctx = data;
+       struct cxl_irq_info irq_info;
+       int rc;
+
+       pr_devel("%d: received PSL interrupt %i\n", ctx->pe, irq);
+       rc = guest_get_irq_info(ctx, &irq_info);
+       if (rc) {
+               WARN(1, "Unable to get IRQ info: %i\n", rc);
+               return IRQ_HANDLED;
+       }
+
+       rc = cxl_irq(irq, ctx, &irq_info);
+       return rc;
+}
+
+static irqreturn_t guest_slice_irq_err(int irq, void *data)
+{
+       struct cxl_afu *afu = data;
+       int rc;
+       u64 serr;
+
+       WARN(irq, "CXL SLICE ERROR interrupt %i\n", irq);
+       rc = cxl_h_get_fn_error_interrupt(afu->guest->handle, &serr);
+       if (rc) {
+               dev_crit(&afu->dev, "Couldn't read PSL_SERR_An: %d\n", rc);
+               return IRQ_HANDLED;
+       }
+       dev_crit(&afu->dev, "PSL_SERR_An: 0x%.16llx\n", serr);
+
+       rc = cxl_h_ack_fn_error_interrupt(afu->guest->handle, serr);
+       if (rc)
+               dev_crit(&afu->dev, "Couldn't ack slice error interrupt: %d\n",
+                       rc);
+
+       return IRQ_HANDLED;
+}
+
+
+static int irq_alloc_range(struct cxl *adapter, int len, int *irq)
+{
+       int i, n;
+       struct irq_avail *cur;
+
+       for (i = 0; i < adapter->guest->irq_nranges; i++) {
+               cur = &adapter->guest->irq_avail[i];
+               n = bitmap_find_next_zero_area(cur->bitmap, cur->range,
+                                       0, len, 0);
+               if (n < cur->range) {
+                       bitmap_set(cur->bitmap, n, len);
+                       *irq = cur->offset + n;
+                       pr_devel("guest: allocate IRQs %#x->%#x\n",
+                               *irq, *irq + len - 1);
+
+                       return 0;
+               }
+       }
+       return -ENOSPC;
+}
+
+static int irq_free_range(struct cxl *adapter, int irq, int len)
+{
+       int i, n;
+       struct irq_avail *cur;
+
+       if (len == 0)
+               return -ENOENT;
+
+       for (i = 0; i < adapter->guest->irq_nranges; i++) {
+               cur = &adapter->guest->irq_avail[i];
+               if (irq >= cur->offset &&
+                       (irq + len) <= (cur->offset + cur->range)) {
+                       n = irq - cur->offset;
+                       bitmap_clear(cur->bitmap, n, len);
+                       pr_devel("guest: release IRQs %#x->%#x\n",
+                               irq, irq + len - 1);
+                       return 0;
+               }
+       }
+       return -ENOENT;
+}
+
+static int guest_reset(struct cxl *adapter)
+{
+       int rc;
+
+       pr_devel("Adapter reset request\n");
+       rc = cxl_h_reset_adapter(adapter->guest->handle);
+       return rc;
+}
+
+static int guest_alloc_one_irq(struct cxl *adapter)
+{
+       int irq;
+
+       spin_lock(&adapter->guest->irq_alloc_lock);
+       if (irq_alloc_range(adapter, 1, &irq))
+               irq = -ENOSPC;
+       spin_unlock(&adapter->guest->irq_alloc_lock);
+       return irq;
+}
+
+static void guest_release_one_irq(struct cxl *adapter, int irq)
+{
+       spin_lock(&adapter->guest->irq_alloc_lock);
+       irq_free_range(adapter, irq, 1);
+       spin_unlock(&adapter->guest->irq_alloc_lock);
+}
+
+static int guest_alloc_irq_ranges(struct cxl_irq_ranges *irqs,
+                               struct cxl *adapter, unsigned int num)
+{
+       int i, try, irq;
+
+       memset(irqs, 0, sizeof(struct cxl_irq_ranges));
+
+       spin_lock(&adapter->guest->irq_alloc_lock);
+       for (i = 0; i < CXL_IRQ_RANGES && num; i++) {
+               try = num;
+               while (try) {
+                       if (irq_alloc_range(adapter, try, &irq) == 0)
+                               break;
+                       try /= 2;
+               }
+               if (!try)
+                       goto error;
+               irqs->offset[i] = irq;
+               irqs->range[i] = try;
+               num -= try;
+       }
+       if (num)
+               goto error;
+       spin_unlock(&adapter->guest->irq_alloc_lock);
+       return 0;
+
+error:
+       for (i = 0; i < CXL_IRQ_RANGES; i++)
+               irq_free_range(adapter, irqs->offset[i], irqs->range[i]);
+       spin_unlock(&adapter->guest->irq_alloc_lock);
+       return -ENOSPC;
+}
+
+static void guest_release_irq_ranges(struct cxl_irq_ranges *irqs,
+                               struct cxl *adapter)
+{
+       int i;
+
+       spin_lock(&adapter->guest->irq_alloc_lock);
+       for (i = 0; i < CXL_IRQ_RANGES; i++)
+               irq_free_range(adapter, irqs->offset[i], irqs->range[i]);
+       spin_unlock(&adapter->guest->irq_alloc_lock);
+}
+
+static int guest_register_serr_irq(struct cxl_afu *afu)
+{
+       afu->err_irq_name = kasprintf(GFP_KERNEL, "cxl-%s-err",
+                                     dev_name(&afu->dev));
+       if (!afu->err_irq_name)
+               return -ENOMEM;
+
+       if (!(afu->serr_virq = cxl_map_irq(afu->adapter, afu->serr_hwirq,
+                                guest_slice_irq_err, afu, afu->err_irq_name))) {
+               kfree(afu->err_irq_name);
+               afu->err_irq_name = NULL;
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void guest_release_serr_irq(struct cxl_afu *afu)
+{
+       cxl_unmap_irq(afu->serr_virq, afu);
+       cxl_ops->release_one_irq(afu->adapter, afu->serr_hwirq);
+       kfree(afu->err_irq_name);
+}
+
+static int guest_ack_irq(struct cxl_context *ctx, u64 tfc, u64 psl_reset_mask)
+{
+       return cxl_h_control_faults(ctx->afu->guest->handle, ctx->process_token,
+                               tfc >> 32, (psl_reset_mask != 0));
+}
+
+static void disable_afu_irqs(struct cxl_context *ctx)
+{
+       irq_hw_number_t hwirq;
+       unsigned int virq;
+       int r, i;
+
+       pr_devel("Disabling AFU(%d) interrupts\n", ctx->afu->slice);
+       for (r = 0; r < CXL_IRQ_RANGES; r++) {
+               hwirq = ctx->irqs.offset[r];
+               for (i = 0; i < ctx->irqs.range[r]; hwirq++, i++) {
+                       virq = irq_find_mapping(NULL, hwirq);
+                       disable_irq(virq);
+               }
+       }
+}
+
+static void enable_afu_irqs(struct cxl_context *ctx)
+{
+       irq_hw_number_t hwirq;
+       unsigned int virq;
+       int r, i;
+
+       pr_devel("Enabling AFU(%d) interrupts\n", ctx->afu->slice);
+       for (r = 0; r < CXL_IRQ_RANGES; r++) {
+               hwirq = ctx->irqs.offset[r];
+               for (i = 0; i < ctx->irqs.range[r]; hwirq++, i++) {
+                       virq = irq_find_mapping(NULL, hwirq);
+                       enable_irq(virq);
+               }
+       }
+}
+
+static int _guest_afu_cr_readXX(int sz, struct cxl_afu *afu, int cr_idx,
+                       u64 offset, u64 *val)
+{
+       unsigned long cr;
+       char c;
+       int rc = 0;
+
+       if (afu->crs_len < sz)
+               return -ENOENT;
+
+       if (unlikely(offset >= afu->crs_len))
+               return -ERANGE;
+
+       cr = get_zeroed_page(GFP_KERNEL);
+       if (!cr)
+               return -ENOMEM;
+
+       rc = cxl_h_get_config(afu->guest->handle, cr_idx, offset,
+                       virt_to_phys((void *)cr), sz);
+       if (rc)
+               goto err;
+
+       switch (sz) {
+       case 1:
+               c = *((char *) cr);
+               *val = c;
+               break;
+       case 2:
+               *val = in_le16((u16 *)cr);
+               break;
+       case 4:
+               *val = in_le32((unsigned *)cr);
+               break;
+       case 8:
+               *val = in_le64((u64 *)cr);
+               break;
+       default:
+               WARN_ON(1);
+       }
+err:
+       free_page(cr);
+       return rc;
+}
+
+static int guest_afu_cr_read32(struct cxl_afu *afu, int cr_idx, u64 offset,
+                       u32 *out)
+{
+       int rc;
+       u64 val;
+
+       rc = _guest_afu_cr_readXX(4, afu, cr_idx, offset, &val);
+       if (!rc)
+               *out = (u32) val;
+       return rc;
+}
+
+static int guest_afu_cr_read16(struct cxl_afu *afu, int cr_idx, u64 offset,
+                       u16 *out)
+{
+       int rc;
+       u64 val;
+
+       rc = _guest_afu_cr_readXX(2, afu, cr_idx, offset, &val);
+       if (!rc)
+               *out = (u16) val;
+       return rc;
+}
+
+static int guest_afu_cr_read8(struct cxl_afu *afu, int cr_idx, u64 offset,
+                       u8 *out)
+{
+       int rc;
+       u64 val;
+
+       rc = _guest_afu_cr_readXX(1, afu, cr_idx, offset, &val);
+       if (!rc)
+               *out = (u8) val;
+       return rc;
+}
+
+static int guest_afu_cr_read64(struct cxl_afu *afu, int cr_idx, u64 offset,
+                       u64 *out)
+{
+       return _guest_afu_cr_readXX(8, afu, cr_idx, offset, out);
+}
+
+static int attach_afu_directed(struct cxl_context *ctx, u64 wed, u64 amr)
+{
+       struct cxl_process_element_hcall *elem;
+       struct cxl *adapter = ctx->afu->adapter;
+       const struct cred *cred;
+       u32 pid, idx;
+       int rc, r, i;
+       u64 mmio_addr, mmio_size;
+       __be64 flags = 0;
+
+       /* Must be 8 byte aligned and cannot cross a 4096 byte boundary */
+       if (!(elem = (struct cxl_process_element_hcall *)
+                       get_zeroed_page(GFP_KERNEL)))
+               return -ENOMEM;
+
+       elem->version = cpu_to_be64(CXL_PROCESS_ELEMENT_VERSION);
+       if (ctx->kernel) {
+               pid = 0;
+               flags |= CXL_PE_TRANSLATION_ENABLED;
+               flags |= CXL_PE_PRIVILEGED_PROCESS;
+               if (mfmsr() & MSR_SF)
+                       flags |= CXL_PE_64_BIT;
+       } else {
+               pid = current->pid;
+               flags |= CXL_PE_PROBLEM_STATE;
+               flags |= CXL_PE_TRANSLATION_ENABLED;
+               if (!test_tsk_thread_flag(current, TIF_32BIT))
+                       flags |= CXL_PE_64_BIT;
+               cred = get_current_cred();
+               if (uid_eq(cred->euid, GLOBAL_ROOT_UID))
+                       flags |= CXL_PE_PRIVILEGED_PROCESS;
+               put_cred(cred);
+       }
+       elem->flags         = cpu_to_be64(flags);
+       elem->common.tid    = cpu_to_be32(0); /* Unused */
+       elem->common.pid    = cpu_to_be32(pid);
+       elem->common.csrp   = cpu_to_be64(0); /* disable */
+       elem->common.aurp0  = cpu_to_be64(0); /* disable */
+       elem->common.aurp1  = cpu_to_be64(0); /* disable */
+
+       cxl_prefault(ctx, wed);
+
+       elem->common.sstp0  = cpu_to_be64(ctx->sstp0);
+       elem->common.sstp1  = cpu_to_be64(ctx->sstp1);
+       for (r = 0; r < CXL_IRQ_RANGES; r++) {
+               for (i = 0; i < ctx->irqs.range[r]; i++) {
+                       if (r == 0 && i == 0) {
+                               elem->pslVirtualIsn = cpu_to_be32(ctx->irqs.offset[0]);
+                       } else {
+                               idx = ctx->irqs.offset[r] + i - adapter->guest->irq_base_offset;
+                               elem->applicationVirtualIsnBitmap[idx / 8] |= 0x80 >> (idx % 8);
+                       }
+               }
+       }
+       elem->common.amr = cpu_to_be64(amr);
+       elem->common.wed = cpu_to_be64(wed);
+
+       disable_afu_irqs(ctx);
+
+       rc = cxl_h_attach_process(ctx->afu->guest->handle, elem,
+                               &ctx->process_token, &mmio_addr, &mmio_size);
+       if (rc == H_SUCCESS) {
+               if (ctx->master || !ctx->afu->pp_psa) {
+                       ctx->psn_phys = ctx->afu->psn_phys;
+                       ctx->psn_size = ctx->afu->adapter->ps_size;
+               } else {
+                       ctx->psn_phys = mmio_addr;
+                       ctx->psn_size = mmio_size;
+               }
+               if (ctx->afu->pp_psa && mmio_size &&
+                       ctx->afu->pp_size == 0) {
+                       /*
+                        * There's no property in the device tree to read the
+                        * pp_size. We only find out at the 1st attach.
+                        * Compared to bare-metal, it is too late and we
+                        * should really lock here. However, on powerVM,
+                        * pp_size is really only used to display in /sys.
+                        * Being discussed with pHyp for their next release.
+                        */
+                       ctx->afu->pp_size = mmio_size;
+               }
+               /* from PAPR: process element is bytes 4-7 of process token */
+               ctx->external_pe = ctx->process_token & 0xFFFFFFFF;
+               pr_devel("CXL pe=%i is known as %i for pHyp, mmio_size=%#llx",
+                       ctx->pe, ctx->external_pe, ctx->psn_size);
+               ctx->pe_inserted = true;
+               enable_afu_irqs(ctx);
+       }
+
+       free_page((u64)elem);
+       return rc;
+}
+
+static int guest_attach_process(struct cxl_context *ctx, bool kernel, u64 wed, u64 amr)
+{
+       pr_devel("in %s\n", __func__);
+
+       ctx->kernel = kernel;
+       if (ctx->afu->current_mode == CXL_MODE_DIRECTED)
+               return attach_afu_directed(ctx, wed, amr);
+
+       /* dedicated mode not supported on FW840 */
+
+       return -EINVAL;
+}
+
+static int detach_afu_directed(struct cxl_context *ctx)
+{
+       if (!ctx->pe_inserted)
+               return 0;
+       if (cxl_h_detach_process(ctx->afu->guest->handle, ctx->process_token))
+               return -1;
+       return 0;
+}
+
+static int guest_detach_process(struct cxl_context *ctx)
+{
+       pr_devel("in %s\n", __func__);
+       trace_cxl_detach(ctx);
+
+       if (!cxl_ops->link_ok(ctx->afu->adapter))
+               return -EIO;
+
+       if (ctx->afu->current_mode == CXL_MODE_DIRECTED)
+               return detach_afu_directed(ctx);
+
+       return -EINVAL;
+}
+
+static void guest_release_afu(struct device *dev)
+{
+       struct cxl_afu *afu = to_cxl_afu(dev);
+
+       pr_devel("%s\n", __func__);
+
+       idr_destroy(&afu->contexts_idr);
+
+       kfree(afu->guest);
+       kfree(afu);
+}
+
+ssize_t cxl_guest_read_afu_vpd(struct cxl_afu *afu, void *buf, size_t len)
+{
+       return guest_collect_vpd(NULL, afu, buf, len);
+}
+
+#define ERR_BUFF_MAX_COPY_SIZE PAGE_SIZE
+static ssize_t guest_afu_read_err_buffer(struct cxl_afu *afu, char *buf,
+                                       loff_t off, size_t count)
+{
+       void *tbuf = NULL;
+       int rc = 0;
+
+       tbuf = (void *) get_zeroed_page(GFP_KERNEL);
+       if (!tbuf)
+               return -ENOMEM;
+
+       rc = cxl_h_get_afu_err(afu->guest->handle,
+                              off & 0x7,
+                              virt_to_phys(tbuf),
+                              count);
+       if (rc)
+               goto err;
+
+       if (count > ERR_BUFF_MAX_COPY_SIZE)
+               count = ERR_BUFF_MAX_COPY_SIZE - (off & 0x7);
+       memcpy(buf, tbuf, count);
+err:
+       free_page((u64)tbuf);
+
+       return rc;
+}
+
+static int guest_afu_check_and_enable(struct cxl_afu *afu)
+{
+       return 0;
+}
+
+static int activate_afu_directed(struct cxl_afu *afu)
+{
+       int rc;
+
+       dev_info(&afu->dev, "Activating AFU(%d) directed mode\n", afu->slice);
+
+       afu->current_mode = CXL_MODE_DIRECTED;
+
+       afu->num_procs = afu->max_procs_virtualised;
+
+       if ((rc = cxl_chardev_m_afu_add(afu)))
+               return rc;
+
+       if ((rc = cxl_sysfs_afu_m_add(afu)))
+               goto err;
+
+       if ((rc = cxl_chardev_s_afu_add(afu)))
+               goto err1;
+
+       return 0;
+err1:
+       cxl_sysfs_afu_m_remove(afu);
+err:
+       cxl_chardev_afu_remove(afu);
+       return rc;
+}
+
+static int guest_afu_activate_mode(struct cxl_afu *afu, int mode)
+{
+       if (!mode)
+               return 0;
+       if (!(mode & afu->modes_supported))
+               return -EINVAL;
+
+       if (mode == CXL_MODE_DIRECTED)
+               return activate_afu_directed(afu);
+
+       if (mode == CXL_MODE_DEDICATED)
+               dev_err(&afu->dev, "Dedicated mode not supported\n");
+
+       return -EINVAL;
+}
+
+static int deactivate_afu_directed(struct cxl_afu *afu)
+{
+       dev_info(&afu->dev, "Deactivating AFU(%d) directed mode\n", afu->slice);
+
+       afu->current_mode = 0;
+       afu->num_procs = 0;
+
+       cxl_sysfs_afu_m_remove(afu);
+       cxl_chardev_afu_remove(afu);
+
+       cxl_ops->afu_reset(afu);
+
+       return 0;
+}
+
+static int guest_afu_deactivate_mode(struct cxl_afu *afu, int mode)
+{
+       if (!mode)
+               return 0;
+       if (!(mode & afu->modes_supported))
+               return -EINVAL;
+
+       if (mode == CXL_MODE_DIRECTED)
+               return deactivate_afu_directed(afu);
+       return 0;
+}
+
+static int guest_afu_reset(struct cxl_afu *afu)
+{
+       pr_devel("AFU(%d) reset request\n", afu->slice);
+       return cxl_h_reset_afu(afu->guest->handle);
+}
+
+static int guest_map_slice_regs(struct cxl_afu *afu)
+{
+       if (!(afu->p2n_mmio = ioremap(afu->guest->p2n_phys, afu->guest->p2n_size))) {
+               dev_err(&afu->dev, "Error mapping AFU(%d) MMIO regions\n",
+                       afu->slice);
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+static void guest_unmap_slice_regs(struct cxl_afu *afu)
+{
+       if (afu->p2n_mmio)
+               iounmap(afu->p2n_mmio);
+}
+
+static bool guest_link_ok(struct cxl *cxl)
+{
+       return true;
+}
+
+static int afu_properties_look_ok(struct cxl_afu *afu)
+{
+       if (afu->pp_irqs < 0) {
+               dev_err(&afu->dev, "Unexpected per-process minimum interrupt value\n");
+               return -EINVAL;
+       }
+
+       if (afu->max_procs_virtualised < 1) {
+               dev_err(&afu->dev, "Unexpected max number of processes virtualised value\n");
+               return -EINVAL;
+       }
+
+       if (afu->crs_len < 0) {
+               dev_err(&afu->dev, "Unexpected configuration record size value\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int cxl_guest_init_afu(struct cxl *adapter, int slice, struct device_node *afu_np)
+{
+       struct cxl_afu *afu;
+       bool free = true;
+       int rc;
+
+       pr_devel("in %s - AFU(%d)\n", __func__, slice);
+       if (!(afu = cxl_alloc_afu(adapter, slice)))
+               return -ENOMEM;
+
+       if (!(afu->guest = kzalloc(sizeof(struct cxl_afu_guest), GFP_KERNEL))) {
+               kfree(afu);
+               return -ENOMEM;
+       }
+
+       if ((rc = dev_set_name(&afu->dev, "afu%i.%i",
+                                         adapter->adapter_num,
+                                         slice)))
+               goto err1;
+
+       adapter->slices++;
+
+       if ((rc = cxl_of_read_afu_handle(afu, afu_np)))
+               goto err1;
+
+       if ((rc = cxl_ops->afu_reset(afu)))
+               goto err1;
+
+       if ((rc = cxl_of_read_afu_properties(afu, afu_np)))
+               goto err1;
+
+       if ((rc = afu_properties_look_ok(afu)))
+               goto err1;
+
+       if ((rc = guest_map_slice_regs(afu)))
+               goto err1;
+
+       if ((rc = guest_register_serr_irq(afu)))
+               goto err2;
+
+       /*
+        * After we call this function we must not free the afu directly, even
+        * if it returns an error!
+        */
+       if ((rc = cxl_register_afu(afu)))
+               goto err_put1;
+
+       if ((rc = cxl_sysfs_afu_add(afu)))
+               goto err_put1;
+
+       /*
+        * pHyp doesn't expose the programming models supported by the
+        * AFU. pHyp currently only supports directed mode. If it adds
+        * dedicated mode later, this version of cxl has no way to
+        * detect it. So we'll initialize the driver, but the first
+        * attach will fail.
+        * Being discussed with pHyp to do better (likely new property)
+        */
+       if (afu->max_procs_virtualised == 1)
+               afu->modes_supported = CXL_MODE_DEDICATED;
+       else
+               afu->modes_supported = CXL_MODE_DIRECTED;
+
+       if ((rc = cxl_afu_select_best_mode(afu)))
+               goto err_put2;
+
+       adapter->afu[afu->slice] = afu;
+
+       afu->enabled = true;
+
+       return 0;
+
+err_put2:
+       cxl_sysfs_afu_remove(afu);
+err_put1:
+       device_unregister(&afu->dev);
+       free = false;
+       guest_release_serr_irq(afu);
+err2:
+       guest_unmap_slice_regs(afu);
+err1:
+       if (free) {
+               kfree(afu->guest);
+               kfree(afu);
+       }
+       return rc;
+}
+
+void cxl_guest_remove_afu(struct cxl_afu *afu)
+{
+       pr_devel("in %s - AFU(%d)\n", __func__, afu->slice);
+
+       if (!afu)
+               return;
+
+       cxl_sysfs_afu_remove(afu);
+
+       spin_lock(&afu->adapter->afu_list_lock);
+       afu->adapter->afu[afu->slice] = NULL;
+       spin_unlock(&afu->adapter->afu_list_lock);
+
+       cxl_context_detach_all(afu);
+       cxl_ops->afu_deactivate_mode(afu, afu->current_mode);
+       guest_release_serr_irq(afu);
+       guest_unmap_slice_regs(afu);
+
+       device_unregister(&afu->dev);
+}
+
+static void free_adapter(struct cxl *adapter)
+{
+       struct irq_avail *cur;
+       int i;
+
+       if (adapter->guest->irq_avail) {
+               for (i = 0; i < adapter->guest->irq_nranges; i++) {
+                       cur = &adapter->guest->irq_avail[i];
+                       kfree(cur->bitmap);
+               }
+               kfree(adapter->guest->irq_avail);
+       }
+       kfree(adapter->guest->status);
+       cxl_remove_adapter_nr(adapter);
+       kfree(adapter->guest);
+       kfree(adapter);
+}
+
+static int properties_look_ok(struct cxl *adapter)
+{
+       /* The absence of this property means that the operational
+        * status is unknown or okay
+        */
+       if (strlen(adapter->guest->status) &&
+           strcmp(adapter->guest->status, "okay")) {
+               pr_err("ABORTING:Bad operational status of the device\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+ssize_t cxl_guest_read_adapter_vpd(struct cxl *adapter, void *buf, size_t len)
+{
+       return guest_collect_vpd(adapter, NULL, buf, len);
+}
+
+void cxl_guest_remove_adapter(struct cxl *adapter)
+{
+       pr_devel("in %s\n", __func__);
+
+       cxl_sysfs_adapter_remove(adapter);
+
+       device_unregister(&adapter->dev);
+}
+
+static void release_adapter(struct device *dev)
+{
+       free_adapter(to_cxl_adapter(dev));
+}
+
+struct cxl *cxl_guest_init_adapter(struct device_node *np, struct platform_device *pdev)
+{
+       struct cxl *adapter;
+       bool free = true;
+       int rc;
+
+       if (!(adapter = cxl_alloc_adapter()))
+               return ERR_PTR(-ENOMEM);
+
+       if (!(adapter->guest = kzalloc(sizeof(struct cxl_guest), GFP_KERNEL))) {
+               free_adapter(adapter);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       adapter->slices = 0;
+       adapter->guest->pdev = pdev;
+       adapter->dev.parent = &pdev->dev;
+       adapter->dev.release = release_adapter;
+       dev_set_drvdata(&pdev->dev, adapter);
+
+       if ((rc = cxl_of_read_adapter_handle(adapter, np)))
+               goto err1;
+
+       if ((rc = cxl_of_read_adapter_properties(adapter, np)))
+               goto err1;
+
+       if ((rc = properties_look_ok(adapter)))
+               goto err1;
+
+       /*
+        * After we call this function we must not free the adapter directly,
+        * even if it returns an error!
+        */
+       if ((rc = cxl_register_adapter(adapter)))
+               goto err_put1;
+
+       if ((rc = cxl_sysfs_adapter_add(adapter)))
+               goto err_put1;
+
+       return adapter;
+
+err_put1:
+       device_unregister(&adapter->dev);
+       free = false;
+err1:
+       if (free)
+               free_adapter(adapter);
+       return ERR_PTR(rc);
+}
+
+const struct cxl_backend_ops cxl_guest_ops = {
+       .module = THIS_MODULE,
+       .adapter_reset = guest_reset,
+       .alloc_one_irq = guest_alloc_one_irq,
+       .release_one_irq = guest_release_one_irq,
+       .alloc_irq_ranges = guest_alloc_irq_ranges,
+       .release_irq_ranges = guest_release_irq_ranges,
+       .setup_irq = NULL,
+       .handle_psl_slice_error = guest_handle_psl_slice_error,
+       .psl_interrupt = guest_psl_irq,
+       .ack_irq = guest_ack_irq,
+       .attach_process = guest_attach_process,
+       .detach_process = guest_detach_process,
+       .link_ok = guest_link_ok,
+       .release_afu = guest_release_afu,
+       .afu_read_err_buffer = guest_afu_read_err_buffer,
+       .afu_check_and_enable = guest_afu_check_and_enable,
+       .afu_activate_mode = guest_afu_activate_mode,
+       .afu_deactivate_mode = guest_afu_deactivate_mode,
+       .afu_reset = guest_afu_reset,
+       .afu_cr_read8 = guest_afu_cr_read8,
+       .afu_cr_read16 = guest_afu_cr_read16,
+       .afu_cr_read32 = guest_afu_cr_read32,
+       .afu_cr_read64 = guest_afu_cr_read64,
+};
 
--- /dev/null
+/*
+ * Copyright 2015 IBM Corp.
+ *
+ * This program 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+
+#include "cxl.h"
+
+
+static const __be32 *read_prop_string(const struct device_node *np,
+                               const char *prop_name)
+{
+       const __be32 *prop;
+
+       prop = of_get_property(np, prop_name, NULL);
+       if (cxl_verbose && prop)
+               pr_info("%s: %s\n", prop_name, (char *) prop);
+       return prop;
+}
+
+static const __be32 *read_prop_dword(const struct device_node *np,
+                               const char *prop_name, u32 *val)
+{
+       const __be32 *prop;
+
+       prop = of_get_property(np, prop_name, NULL);
+       if (prop)
+               *val = be32_to_cpu(prop[0]);
+       if (cxl_verbose && prop)
+               pr_info("%s: %#x (%u)\n", prop_name, *val, *val);
+       return prop;
+}
+
+static const __be64 *read_prop64_dword(const struct device_node *np,
+                               const char *prop_name, u64 *val)
+{
+       const __be64 *prop;
+
+       prop = of_get_property(np, prop_name, NULL);
+       if (prop)
+               *val = be64_to_cpu(prop[0]);
+       if (cxl_verbose && prop)
+               pr_info("%s: %#llx (%llu)\n", prop_name, *val, *val);
+       return prop;
+}
+
+
+static int read_handle(struct device_node *np, u64 *handle)
+{
+       const __be32 *prop;
+       u64 size;
+
+       /* Get address and size of the node */
+       prop = of_get_address(np, 0, &size, NULL);
+       if (size)
+               return -EINVAL;
+
+       /* Helper to read a big number; size is in cells (not bytes) */
+       *handle = of_read_number(prop, of_n_addr_cells(np));
+       return 0;
+}
+
+static int read_phys_addr(struct device_node *np, char *prop_name,
+                       struct cxl_afu *afu)
+{
+       int i, len, entry_size, naddr, nsize, type;
+       u64 addr, size;
+       const __be32 *prop;
+
+       naddr = of_n_addr_cells(np);
+       nsize = of_n_size_cells(np);
+
+       prop = of_get_property(np, prop_name, &len);
+       if (prop) {
+               entry_size = naddr + nsize;
+               for (i = 0; i < (len / 4); i += entry_size, prop += entry_size) {
+                       type = be32_to_cpu(prop[0]);
+                       addr = of_read_number(prop, naddr);
+                       size = of_read_number(&prop[naddr], nsize);
+                       switch (type) {
+                       case 0: /* unit address */
+                               afu->guest->handle = addr;
+                               break;
+                       case 1: /* p2 area */
+                               afu->guest->p2n_phys += addr;
+                               afu->guest->p2n_size = size;
+                               break;
+                       case 2: /* problem state area */
+                               afu->psn_phys += addr;
+                               afu->adapter->ps_size = size;
+                               break;
+                       default:
+                               pr_err("Invalid address type %d found in %s property of AFU\n",
+                                       type, prop_name);
+                               return -EINVAL;
+                       }
+                       if (cxl_verbose)
+                               pr_info("%s: %#x %#llx (size %#llx)\n",
+                                       prop_name, type, addr, size);
+               }
+       }
+       return 0;
+}
+
+static int read_vpd(struct cxl *adapter, struct cxl_afu *afu)
+{
+       char vpd[256];
+       int rc;
+       size_t len = sizeof(vpd);
+
+       memset(vpd, 0, len);
+
+       if (adapter)
+               rc = cxl_guest_read_adapter_vpd(adapter, vpd, len);
+       else
+               rc = cxl_guest_read_afu_vpd(afu, vpd, len);
+
+       if (rc > 0) {
+               cxl_dump_debug_buffer(vpd, rc);
+               rc = 0;
+       }
+       return rc;
+}
+
+int cxl_of_read_afu_handle(struct cxl_afu *afu, struct device_node *afu_np)
+{
+       if (read_handle(afu_np, &afu->guest->handle))
+               return -EINVAL;
+       pr_devel("AFU handle: 0x%.16llx\n", afu->guest->handle);
+
+       return 0;
+}
+
+int cxl_of_read_afu_properties(struct cxl_afu *afu, struct device_node *np)
+{
+       int i, len, rc;
+       char *p;
+       const __be32 *prop;
+       u16 device_id, vendor_id;
+       u32 val = 0, class_code;
+
+       /* Properties are read in the same order as listed in PAPR */
+
+       if (cxl_verbose) {
+               pr_info("Dump of the 'ibm,coherent-platform-function' node properties:\n");
+
+               prop = of_get_property(np, "compatible", &len);
+               i = 0;
+               while (i < len) {
+                       p = (char *) prop + i;
+                       pr_info("compatible: %s\n", p);
+                       i += strlen(p) + 1;
+               }
+               read_prop_string(np, "name");
+       }
+
+       rc = read_phys_addr(np, "reg", afu);
+       if (rc)
+               return rc;
+
+       rc = read_phys_addr(np, "assigned-addresses", afu);
+       if (rc)
+               return rc;
+
+       if (afu->psn_phys == 0)
+               afu->psa = false;
+       else
+               afu->psa = true;
+
+       if (cxl_verbose) {
+               read_prop_string(np, "ibm,loc-code");
+               read_prop_string(np, "device_type");
+       }
+
+       read_prop_dword(np, "ibm,#processes", &afu->max_procs_virtualised);
+
+       if (cxl_verbose) {
+               read_prop_dword(np, "ibm,scratchpad-size", &val);
+               read_prop_dword(np, "ibm,programmable", &val);
+               read_prop_string(np, "ibm,phandle");
+               read_vpd(NULL, afu);
+       }
+
+       read_prop_dword(np, "ibm,max-ints-per-process", &afu->guest->max_ints);
+       afu->irqs_max = afu->guest->max_ints;
+
+       prop = read_prop_dword(np, "ibm,min-ints-per-process", &afu->pp_irqs);
+       if (prop) {
+               /* One extra interrupt for the PSL interrupt is already
+                * included. Remove it now to keep only AFU interrupts and
+                * match the native case.
+                */
+               afu->pp_irqs--;
+       }
+
+       if (cxl_verbose) {
+               read_prop_dword(np, "ibm,max-ints", &val);
+               read_prop_dword(np, "ibm,vpd-size", &val);
+       }
+
+       read_prop64_dword(np, "ibm,error-buffer-size", &afu->eb_len);
+       afu->eb_offset = 0;
+
+       if (cxl_verbose)
+               read_prop_dword(np, "ibm,config-record-type", &val);
+
+       read_prop64_dword(np, "ibm,config-record-size", &afu->crs_len);
+       afu->crs_offset = 0;
+
+       read_prop_dword(np, "ibm,#config-records", &afu->crs_num);
+
+       if (cxl_verbose) {
+               for (i = 0; i < afu->crs_num; i++) {
+                       rc = cxl_ops->afu_cr_read16(afu, i, PCI_DEVICE_ID,
+                                               &device_id);
+                       if (!rc)
+                               pr_info("record %d - device-id: %#x\n",
+                                       i, device_id);
+                       rc = cxl_ops->afu_cr_read16(afu, i, PCI_VENDOR_ID,
+                                               &vendor_id);
+                       if (!rc)
+                               pr_info("record %d - vendor-id: %#x\n",
+                                       i, vendor_id);
+                       rc = cxl_ops->afu_cr_read32(afu, i, PCI_CLASS_REVISION,
+                                               &class_code);
+                       if (!rc) {
+                               class_code >>= 8;
+                               pr_info("record %d - class-code: %#x\n",
+                                       i, class_code);
+                       }
+               }
+
+               read_prop_dword(np, "ibm,function-number", &val);
+               read_prop_dword(np, "ibm,privileged-function", &val);
+               read_prop_dword(np, "vendor-id", &val);
+               read_prop_dword(np, "device-id", &val);
+               read_prop_dword(np, "revision-id", &val);
+               read_prop_dword(np, "class-code", &val);
+               read_prop_dword(np, "subsystem-vendor-id", &val);
+               read_prop_dword(np, "subsystem-id", &val);
+       }
+       /*
+        * if "ibm,process-mmio" doesn't exist then per-process mmio is
+        * not supported
+        */
+       val = 0;
+       prop = read_prop_dword(np, "ibm,process-mmio", &val);
+       if (prop && val == 1)
+               afu->pp_psa = true;
+       else
+               afu->pp_psa = false;
+
+       if (cxl_verbose) {
+               read_prop_dword(np, "ibm,supports-aur", &val);
+               read_prop_dword(np, "ibm,supports-csrp", &val);
+               read_prop_dword(np, "ibm,supports-prr", &val);
+       }
+
+       prop = read_prop_dword(np, "ibm,function-error-interrupt", &val);
+       if (prop)
+               afu->serr_hwirq = val;
+
+       pr_devel("AFU handle: %#llx\n", afu->guest->handle);
+       pr_devel("p2n_phys: %#llx (size %#llx)\n",
+               afu->guest->p2n_phys, afu->guest->p2n_size);
+       pr_devel("psn_phys: %#llx (size %#llx)\n",
+               afu->psn_phys, afu->adapter->ps_size);
+       pr_devel("Max number of processes virtualised=%i\n",
+               afu->max_procs_virtualised);
+       pr_devel("Per-process irqs min=%i, max=%i\n", afu->pp_irqs,
+                afu->irqs_max);
+       pr_devel("Slice error interrupt=%#lx\n", afu->serr_hwirq);
+
+       return 0;
+}
+
+static int read_adapter_irq_config(struct cxl *adapter, struct device_node *np)
+{
+       const __be32 *ranges;
+       int len, nranges, i;
+       struct irq_avail *cur;
+
+       ranges = of_get_property(np, "interrupt-ranges", &len);
+       if (ranges == NULL || len < (2 * sizeof(int)))
+               return -EINVAL;
+
+       /*
+        * encoded array of two cells per entry, each cell encoded as
+        * with encode-int
+        */
+       nranges = len / (2 * sizeof(int));
+       if (nranges == 0 || (nranges * 2 * sizeof(int)) != len)
+               return -EINVAL;
+
+       adapter->guest->irq_avail = kzalloc(nranges * sizeof(struct irq_avail),
+                                           GFP_KERNEL);
+       if (adapter->guest->irq_avail == NULL)
+               return -ENOMEM;
+
+       adapter->guest->irq_base_offset = be32_to_cpu(ranges[0]);
+       for (i = 0; i < nranges; i++) {
+               cur = &adapter->guest->irq_avail[i];
+               cur->offset = be32_to_cpu(ranges[i * 2]);
+               cur->range  = be32_to_cpu(ranges[i * 2 + 1]);
+               cur->bitmap = kcalloc(BITS_TO_LONGS(cur->range),
+                               sizeof(*cur->bitmap), GFP_KERNEL);
+               if (cur->bitmap == NULL)
+                       goto err;
+               if (cur->offset < adapter->guest->irq_base_offset)
+                       adapter->guest->irq_base_offset = cur->offset;
+               if (cxl_verbose)
+                       pr_info("available IRQ range: %#lx-%#lx (%lu)\n",
+                               cur->offset, cur->offset + cur->range - 1,
+                               cur->range);
+       }
+       adapter->guest->irq_nranges = nranges;
+       spin_lock_init(&adapter->guest->irq_alloc_lock);
+
+       return 0;
+err:
+       for (i--; i >= 0; i--) {
+               cur = &adapter->guest->irq_avail[i];
+               kfree(cur->bitmap);
+       }
+       kfree(adapter->guest->irq_avail);
+       adapter->guest->irq_avail = NULL;
+       return -ENOMEM;
+}
+
+int cxl_of_read_adapter_handle(struct cxl *adapter, struct device_node *np)
+{
+       if (read_handle(np, &adapter->guest->handle))
+               return -EINVAL;
+       pr_devel("Adapter handle: 0x%.16llx\n", adapter->guest->handle);
+
+       return 0;
+}
+
+int cxl_of_read_adapter_properties(struct cxl *adapter, struct device_node *np)
+{
+       int rc, len, naddr, i;
+       char *p;
+       const __be32 *prop;
+       u32 val = 0;
+
+       /* Properties are read in the same order as listed in PAPR */
+
+       naddr = of_n_addr_cells(np);
+
+       if (cxl_verbose) {
+               pr_info("Dump of the 'ibm,coherent-platform-facility' node properties:\n");
+
+               read_prop_dword(np, "#address-cells", &val);
+               read_prop_dword(np, "#size-cells", &val);
+
+               prop = of_get_property(np, "compatible", &len);
+               i = 0;
+               while (i < len) {
+                       p = (char *) prop + i;
+                       pr_info("compatible: %s\n", p);
+                       i += strlen(p) + 1;
+               }
+               read_prop_string(np, "name");
+               read_prop_string(np, "model");
+
+               prop = of_get_property(np, "reg", NULL);
+               if (prop) {
+                       pr_info("reg: addr:%#llx size:%#x\n",
+                               of_read_number(prop, naddr),
+                               be32_to_cpu(prop[naddr]));
+               }
+
+               read_prop_string(np, "ibm,loc-code");
+       }
+
+       if ((rc = read_adapter_irq_config(adapter, np)))
+               return rc;
+
+       if (cxl_verbose) {
+               read_prop_string(np, "device_type");
+               read_prop_string(np, "ibm,phandle");
+       }
+
+       prop = read_prop_dword(np, "ibm,caia-version", &val);
+       if (prop) {
+               adapter->caia_major = (val & 0xFF00) >> 8;
+               adapter->caia_minor = val & 0xFF;
+       }
+
+       prop = read_prop_dword(np, "ibm,psl-revision", &val);
+       if (prop)
+               adapter->psl_rev = val;
+
+       prop = read_prop_string(np, "status");
+       if (prop) {
+               adapter->guest->status = kasprintf(GFP_KERNEL, "%s", (char *) prop);
+               if (adapter->guest->status == NULL)
+                       return -ENOMEM;
+       }
+
+       prop = read_prop_dword(np, "vendor-id", &val);
+       if (prop)
+               adapter->guest->vendor = val;
+
+       prop = read_prop_dword(np, "device-id", &val);
+       if (prop)
+               adapter->guest->device = val;
+
+       if (cxl_verbose) {
+               read_prop_dword(np, "ibm,privileged-facility", &val);
+               read_prop_dword(np, "revision-id", &val);
+               read_prop_dword(np, "class-code", &val);
+       }
+
+       prop = read_prop_dword(np, "subsystem-vendor-id", &val);
+       if (prop)
+               adapter->guest->subsystem_vendor = val;
+
+       prop = read_prop_dword(np, "subsystem-id", &val);
+       if (prop)
+               adapter->guest->subsystem = val;
+
+       if (cxl_verbose)
+               read_vpd(adapter, NULL);
+
+       return 0;
+}
+
+static int cxl_of_remove(struct platform_device *pdev)
+{
+       struct cxl *adapter;
+       int afu;
+
+       adapter = dev_get_drvdata(&pdev->dev);
+       for (afu = 0; afu < adapter->slices; afu++)
+               cxl_guest_remove_afu(adapter->afu[afu]);
+
+       cxl_guest_remove_adapter(adapter);
+       return 0;
+}
+
+static void cxl_of_shutdown(struct platform_device *pdev)
+{
+       cxl_of_remove(pdev);
+}
+
+int cxl_of_probe(struct platform_device *pdev)
+{
+       struct device_node *np = NULL;
+       struct device_node *afu_np = NULL;
+       struct cxl *adapter = NULL;
+       int ret;
+       int slice, slice_ok;
+
+       pr_devel("in %s\n", __func__);
+
+       np = pdev->dev.of_node;
+       if (np == NULL)
+               return -ENODEV;
+
+       /* init adapter */
+       adapter = cxl_guest_init_adapter(np, pdev);
+       if (IS_ERR(adapter)) {
+               dev_err(&pdev->dev, "guest_init_adapter failed: %li\n", PTR_ERR(adapter));
+               return PTR_ERR(adapter);
+       }
+
+       /* init afu */
+       slice_ok = 0;
+       for (afu_np = NULL, slice = 0; (afu_np = of_get_next_child(np, afu_np)); slice++) {
+               if ((ret = cxl_guest_init_afu(adapter, slice, afu_np)))
+                       dev_err(&pdev->dev, "AFU %i failed to initialise: %i\n",
+                               slice, ret);
+               else
+                       slice_ok++;
+       }
+
+       if (slice_ok == 0) {
+               dev_info(&pdev->dev, "No active AFU");
+               adapter->slices = 0;
+       }
+
+       if (afu_np)
+               of_node_put(afu_np);
+       return 0;
+}
+
+static const struct of_device_id cxl_of_match[] = {
+       { .compatible = "ibm,coherent-platform-facility",},
+       {},
+};
+MODULE_DEVICE_TABLE(of, cxl_of_match);
+
+struct platform_driver cxl_of_driver = {
+       .driver = {
+               .name = "cxl_of",
+               .of_match_table = cxl_of_match,
+               .owner = THIS_MODULE
+       },
+       .probe = cxl_of_probe,
+       .remove = cxl_of_remove,
+       .shutdown = cxl_of_shutdown,
+};