|      parisc: | TODO |
     |     powerpc: |  ok  |
     |       riscv: | TODO |
-    |        s390: | TODO |
+    |        s390: |  ok  |
     |          sh: |  ok  |
     |       sparc: | TODO |
     |          um: | TODO |
 
 /* CLP common request & response block size */
 #define CLP_BLK_SIZE                   PAGE_SIZE
 
+/* Call Logical Processor - Command Code */
+#define CLP_SLPC               0x0001
+
 #define CLP_LPS_BASE   0
 #define CLP_LPS_PCI    2
 
 
 
 #include <linux/kernel.h>
 #include <asm/page.h>
+#include <asm/pgtable.h>
 #include <asm/pci_io.h>
 
 #define xlate_dev_mem_ptr xlate_dev_mem_ptr
 
 #define IO_SPACE_LIMIT 0
 
+void __iomem *ioremap_prot(phys_addr_t addr, size_t size, unsigned long prot);
 void __iomem *ioremap(phys_addr_t addr, size_t size);
+void __iomem *ioremap_wc(phys_addr_t addr, size_t size);
+void __iomem *ioremap_wt(phys_addr_t addr, size_t size);
 void iounmap(volatile void __iomem *addr);
 
 static inline void __iomem *ioport_map(unsigned long port, unsigned int nr)
 #define pci_iomap_wc pci_iomap_wc
 #define pci_iomap_wc_range pci_iomap_wc_range
 
+#define ioremap ioremap
+#define ioremap_wt ioremap_wt
+#define ioremap_wc ioremap_wc
+
 #define memcpy_fromio(dst, src, count) zpci_memcpy_fromio(dst, src, count)
 #define memcpy_toio(dst, src, count)   zpci_memcpy_toio(dst, src, count)
 #define memset_io(dst, val, count)     zpci_memset_io(dst, val, count)
 
 void zpci_remove_reserved_devices(void);
 
 /* CLP */
+int clp_setup_writeback_mio(void);
 int clp_scan_pci_devices(void);
 int clp_rescan_pci_devices(void);
 int clp_rescan_pci_devices_simple(u32 *fid);
 
 /*
  * Call Logical Processor - Command Codes
  */
+#define CLP_SLPC               0x0001
 #define CLP_LIST_PCI           0x0002
 #define CLP_QUERY_PCI_FN       0x0003
 #define CLP_QUERY_PCI_FNGRP    0x0004
 
 extern bool zpci_unique_uid;
 
+struct clp_rsp_slpc_pci {
+       struct clp_rsp_hdr hdr;
+       u32 reserved2[4];
+       u32 lpif[8];
+       u32 reserved3[4];
+       u32 vwb         :  1;
+       u32             :  1;
+       u32 mio_wb      :  6;
+       u32             : 24;
+       u32 reserved5[3];
+       u32 lpic[8];
+} __packed;
+
 /* List PCI functions request */
 struct clp_req_list_pci {
        struct clp_req_hdr hdr;
 } __packed;
 
 /* Combined request/response block structures used by clp insn */
+struct clp_req_rsp_slpc_pci {
+       struct clp_req_slpc request;
+       struct clp_rsp_slpc_pci response;
+} __packed;
+
 struct clp_req_rsp_list_pci {
        struct clp_req_list_pci request;
        struct clp_rsp_list_pci response;
 
 void gmap_pmdp_idte_local(struct mm_struct *mm, unsigned long vmaddr);
 void gmap_pmdp_idte_global(struct mm_struct *mm, unsigned long vmaddr);
 
+#define pgprot_writecombine    pgprot_writecombine
+pgprot_t pgprot_writecombine(pgprot_t prot);
+
+#define pgprot_writethrough    pgprot_writethrough
+pgprot_t pgprot_writethrough(pgprot_t prot);
+
 /*
  * Certain architectures need to do special things when PTEs
  * within a page table are directly modified.  Thus, the following
 static inline pte_t mk_pte_phys(unsigned long physpage, pgprot_t pgprot)
 {
        pte_t __pte;
-       pte_val(__pte) = physpage + pgprot_val(pgprot);
+
+       pte_val(__pte) = physpage | pgprot_val(pgprot);
        if (!MACHINE_HAS_NX)
                pte_val(__pte) &= ~_PAGE_NOEXEC;
        return pte_mkyoung(__pte);
 
 extern unsigned long max_physmem_end;
 extern unsigned long __swsusp_reset_dma;
 
+/* The Write Back bit position in the physaddr is given by the SLPC PCI */
+extern unsigned long mio_wb_bit_mask;
+
 #define MACHINE_IS_VM          (S390_lowcore.machine_flags & MACHINE_FLAG_VM)
 #define MACHINE_IS_KVM         (S390_lowcore.machine_flags & MACHINE_FLAG_KVM)
 #define MACHINE_IS_LPAR                (S390_lowcore.machine_flags & MACHINE_FLAG_LPAR)
 
 struct lowcore *lowcore_ptr[NR_CPUS];
 EXPORT_SYMBOL(lowcore_ptr);
 
+/*
+ * The Write Back bit position in the physaddr is given by the SLPC PCI.
+ * Leaving the mask zero always uses write through which is safe
+ */
+unsigned long mio_wb_bit_mask __ro_after_init;
+
 /*
  * This is set up by the setup-routine at boot-time
  * for S390 need to find out, what we have to setup
 
 #include <asm/mmu_context.h>
 #include <asm/page-states.h>
 
+pgprot_t pgprot_writecombine(pgprot_t prot)
+{
+       /*
+        * mio_wb_bit_mask may be set on a different CPU, but it is only set
+        * once at init and only read afterwards.
+        */
+       return __pgprot(pgprot_val(prot) | mio_wb_bit_mask);
+}
+EXPORT_SYMBOL_GPL(pgprot_writecombine);
+
+pgprot_t pgprot_writethrough(pgprot_t prot)
+{
+       /*
+        * mio_wb_bit_mask may be set on a different CPU, but it is only set
+        * once at init and only read afterwards.
+        */
+       return __pgprot(pgprot_val(prot) & ~mio_wb_bit_mask);
+}
+EXPORT_SYMBOL_GPL(pgprot_writethrough);
+
 static inline void ptep_ipte_local(struct mm_struct *mm, unsigned long addr,
                                   pte_t *ptep, int nodat)
 {
 
        zpci_memcpy_toio(to, from, count);
 }
 
-void __iomem *ioremap(phys_addr_t addr, size_t size)
+static void __iomem *__ioremap(phys_addr_t addr, size_t size, pgprot_t prot)
 {
        unsigned long offset, vaddr;
        struct vm_struct *area;
                return NULL;
 
        vaddr = (unsigned long) area->addr;
-       if (ioremap_page_range(vaddr, vaddr + size, addr, PAGE_KERNEL)) {
+       if (ioremap_page_range(vaddr, vaddr + size, addr, prot)) {
                free_vm_area(area);
                return NULL;
        }
        return (void __iomem *) ((unsigned long) area->addr + offset);
 }
+
+void __iomem *ioremap_prot(phys_addr_t addr, size_t size, unsigned long prot)
+{
+       return __ioremap(addr, size, __pgprot(prot));
+}
+EXPORT_SYMBOL(ioremap_prot);
+
+void __iomem *ioremap(phys_addr_t addr, size_t size)
+{
+       return __ioremap(addr, size, PAGE_KERNEL);
+}
 EXPORT_SYMBOL(ioremap);
 
+void __iomem *ioremap_wc(phys_addr_t addr, size_t size)
+{
+       return __ioremap(addr, size, pgprot_writecombine(PAGE_KERNEL));
+}
+EXPORT_SYMBOL(ioremap_wc);
+
+void __iomem *ioremap_wt(phys_addr_t addr, size_t size)
+{
+       return __ioremap(addr, size, pgprot_writethrough(PAGE_KERNEL));
+}
+EXPORT_SYMBOL(ioremap_wt);
+
 void iounmap(volatile void __iomem *addr)
 {
        if (static_branch_likely(&have_mio))
        if (!zpci_iomap_bitmap)
                goto error_iomap_bitmap;
 
+       if (static_branch_likely(&have_mio))
+               clp_setup_writeback_mio();
+
        return 0;
 error_iomap_bitmap:
        kfree(zpci_iomap_start);
 
        return rc;
 }
 
+int clp_setup_writeback_mio(void)
+{
+       struct clp_req_rsp_slpc_pci *rrb;
+       u8  wb_bit_pos;
+       int rc;
+
+       rrb = clp_alloc_block(GFP_KERNEL);
+       if (!rrb)
+               return -ENOMEM;
+
+       memset(rrb, 0, sizeof(*rrb));
+       rrb->request.hdr.len = sizeof(rrb->request);
+       rrb->request.hdr.cmd = CLP_SLPC;
+       rrb->response.hdr.len = sizeof(rrb->response);
+
+       rc = clp_req(rrb, CLP_LPS_PCI);
+       if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) {
+               if (rrb->response.vwb) {
+                       wb_bit_pos = rrb->response.mio_wb;
+                       set_bit_inv(wb_bit_pos, &mio_wb_bit_mask);
+                       zpci_dbg(3, "wb bit: %d\n", wb_bit_pos);
+               } else {
+                       zpci_dbg(3, "wb bit: n.a.\n");
+               }
+
+       } else {
+               zpci_err("SLPC PCI:\n");
+               zpci_err_clp(rrb->response.hdr.rsp, rc);
+               rc = -EIO;
+       }
+       clp_free_block(rrb);
+       return rc;
+}
+
 int clp_enable_fh(struct zpci_dev *zdev, u8 nr_dma_as)
 {
        int rc;
        }
 }
 
-static int clp_pci_slpc(struct clp_req *req, struct clp_req_rsp_slpc *lpcb)
+static int clp_pci_slpc(struct clp_req *req, struct clp_req_rsp_slpc_pci *lpcb)
 {
        unsigned long limit = PAGE_SIZE - sizeof(lpcb->request);