* A more complete discussion of unaddressable memory may be found in
  * include/linux/hmm.h and Documentation/mm/hmm.rst.
  *
+ * MEMORY_DEVICE_COHERENT:
+ * Device memory that is cache coherent from device and CPU point of view. This
+ * is used on platforms that have an advanced system bus (like CAPI or CXL). A
+ * driver can hotplug the device memory using ZONE_DEVICE and with that memory
+ * type. Any page of a process can be migrated to such memory. However no one
+ * should be allowed to pin such memory so that it can always be evicted.
+ *
  * MEMORY_DEVICE_FS_DAX:
  * Host memory that has similar access semantics as System RAM i.e. DMA
  * coherent and supports page pinning. In support of coordinating page
 enum memory_type {
        /* 0 is reserved to catch uninitialized type fields */
        MEMORY_DEVICE_PRIVATE = 1,
+       MEMORY_DEVICE_COHERENT,
        MEMORY_DEVICE_FS_DAX,
        MEMORY_DEVICE_GENERIC,
        MEMORY_DEVICE_PCI_P2PDMA,
                page->pgmap->type == MEMORY_DEVICE_PCI_P2PDMA;
 }
 
+static inline bool is_device_coherent_page(const struct page *page)
+{
+       return is_zone_device_page(page) &&
+               page->pgmap->type == MEMORY_DEVICE_COHERENT;
+}
+
+static inline bool folio_is_device_coherent(const struct folio *folio)
+{
+       return is_device_coherent_page(&folio->page);
+}
+
 #ifdef CONFIG_ZONE_DEVICE
 void *memremap_pages(struct dev_pagemap *pgmap, int nid);
 void memunmap_pages(struct dev_pagemap *pgmap);
 
 #include <linux/sched.h>
 #include <linux/pgtable.h>
 #include <linux/kasan.h>
+#include <linux/memremap.h>
 
 struct mempolicy;
 struct anon_vma;
        if (mt == MIGRATE_CMA || mt == MIGRATE_ISOLATE)
                return false;
 #endif
-       return !is_zone_movable_page(page) || is_zero_pfn(page_to_pfn(page));
+       return !(is_device_coherent_page(page) ||
+                is_zone_movable_page(page) ||
+                is_zero_pfn(page_to_pfn(page)));
 }
 #else
 static inline bool is_longterm_pinnable_page(struct page *page)
 
  *   2(MC_TARGET_SWAP): if the swap entry corresponding to this pte is a
  *     target for charge migration. if @target is not NULL, the entry is stored
  *     in target->ent.
- *   3(MC_TARGET_DEVICE): like MC_TARGET_PAGE  but page is MEMORY_DEVICE_PRIVATE
- *     (so ZONE_DEVICE page and thus not on the lru).
+ *   3(MC_TARGET_DEVICE): like MC_TARGET_PAGE  but page is device memory and
+ *   thus not on the lru.
  *     For now we such page is charge like a regular page would be as for all
  *     intent and purposes it is just special memory taking the place of a
  *     regular page.
                 */
                if (page_memcg(page) == mc.from) {
                        ret = MC_TARGET_PAGE;
-                       if (is_device_private_page(page))
+                       if (is_device_private_page(page) ||
+                           is_device_coherent_page(page))
                                ret = MC_TARGET_DEVICE;
                        if (target)
                                target->page = page;
 
                goto unlock;
        }
 
-       if (pgmap->type == MEMORY_DEVICE_PRIVATE) {
+       switch (pgmap->type) {
+       case MEMORY_DEVICE_PRIVATE:
+       case MEMORY_DEVICE_COHERENT:
                /*
-                * TODO: Handle HMM pages which may need coordination
+                * TODO: Handle device pages which may need coordination
                 * with device-side memory.
                 */
                goto unlock;
+       default:
+               break;
        }
 
        /*
 
                        return ERR_PTR(-EINVAL);
                }
                break;
+       case MEMORY_DEVICE_COHERENT:
+               if (!pgmap->ops->page_free) {
+                       WARN(1, "Missing page_free method\n");
+                       return ERR_PTR(-EINVAL);
+               }
+               if (!pgmap->owner) {
+                       WARN(1, "Missing owner\n");
+                       return ERR_PTR(-EINVAL);
+               }
+               break;
        case MEMORY_DEVICE_FS_DAX:
                if (IS_ENABLED(CONFIG_FS_DAX_LIMITED)) {
                        WARN(1, "File system DAX not supported\n");
 
  *     handle_pte_fault()
  *       do_anonymous_page()
  * to map in an anonymous zero page but the struct page will be a ZONE_DEVICE
- * private page.
+ * private or coherent page.
  */
 static void migrate_vma_insert_page(struct migrate_vma *migrate,
                                    unsigned long addr,
                                                page_to_pfn(page));
                entry = swp_entry_to_pte(swp_entry);
        } else {
-               /*
-                * For now we only support migrating to un-addressable device
-                * memory.
-                */
-               if (is_zone_device_page(page)) {
+               if (is_zone_device_page(page) &&
+                   !is_device_coherent_page(page)) {
                        pr_warn_once("Unsupported ZONE_DEVICE page type.\n");
                        goto abort;
                }
 
                mapping = page_mapping(page);
 
-               if (is_device_private_page(newpage)) {
+               if (is_device_private_page(newpage) ||
+                   is_device_coherent_page(newpage)) {
                        /*
-                        * For now only support private anonymous when migrating
-                        * to un-addressable device memory.
+                        * For now only support anonymous memory migrating to
+                        * device private or coherent memory.
                         */
                        if (mapping) {
                                migrate->src[i] &= ~MIGRATE_PFN_MIGRATE;
 
                /* Update high watermark before we lower rss */
                update_hiwater_rss(mm);
 
-               if (folio_is_zone_device(folio)) {
+               if (folio_is_device_private(folio)) {
                        unsigned long pfn = folio_pfn(folio);
                        swp_entry_t entry;
                        pte_t swp_pte;
                                        TTU_SYNC)))
                return;
 
-       if (folio_is_zone_device(folio) && !folio_is_device_private(folio))
+       if (folio_is_zone_device(folio) &&
+           (!folio_is_device_private(folio) && !folio_is_device_coherent(folio)))
                return;
 
        /*