diff options
author | Sebastian Ott <sebott@linux.ibm.com> | 2019-04-14 15:38:01 +0200 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2019-04-29 10:47:01 +0200 |
commit | 71ba41c9b1d91042960e9d92a5c8f52dc8531eda (patch) | |
tree | c0d4f5379684fb236526127730073d5fe1d44ea3 /arch/s390/pci/pci_insn.c | |
parent | c475f1770a5e062652a6a877afcbd4e22b18039f (diff) | |
download | linux-71ba41c9b1d91042960e9d92a5c8f52dc8531eda.tar.bz2 |
s390/pci: provide support for MIO instructions
Provide support for PCI I/O instructions that work on mapped IO addresses.
Signed-off-by: Sebastian Ott <sebott@linux.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/pci/pci_insn.c')
-rw-r--r-- | arch/s390/pci/pci_insn.c | 129 |
1 files changed, 125 insertions, 4 deletions
diff --git a/arch/s390/pci/pci_insn.c b/arch/s390/pci/pci_insn.c index 6dbd13bb7c1c..02f9505c99a8 100644 --- a/arch/s390/pci/pci_insn.c +++ b/arch/s390/pci/pci_insn.c @@ -8,6 +8,7 @@ #include <linux/export.h> #include <linux/errno.h> #include <linux/delay.h> +#include <linux/jump_label.h> #include <asm/facility.h> #include <asm/pci_insn.h> #include <asm/pci_debug.h> @@ -161,13 +162,50 @@ int __zpci_load(u64 *data, u64 req, u64 offset) } EXPORT_SYMBOL_GPL(__zpci_load); -int zpci_load(u64 *data, const volatile void __iomem *addr, unsigned long len) +static inline int zpci_load_fh(u64 *data, const volatile void __iomem *addr, + unsigned long len) { struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(addr)]; u64 req = ZPCI_CREATE_REQ(entry->fh, entry->bar, len); return __zpci_load(data, req, ZPCI_OFFSET(addr)); } + +static inline int __pcilg_mio(u64 *data, u64 ioaddr, u64 len, u8 *status) +{ + register u64 addr asm("2") = ioaddr; + register u64 r3 asm("3") = len; + int cc = -ENXIO; + u64 __data; + + asm volatile ( + " .insn rre,0xb9d60000,%[data],%[ioaddr]\n" + "0: ipm %[cc]\n" + " srl %[cc],28\n" + "1:\n" + EX_TABLE(0b, 1b) + : [cc] "+d" (cc), [data] "=d" (__data), "+d" (r3) + : [ioaddr] "d" (addr) + : "cc"); + *status = r3 >> 24 & 0xff; + *data = __data; + return cc; +} + +int zpci_load(u64 *data, const volatile void __iomem *addr, unsigned long len) +{ + u8 status; + int cc; + + if (!static_branch_unlikely(&have_mio)) + return zpci_load_fh(data, addr, len); + + cc = __pcilg_mio(data, (__force u64) addr, len, &status); + if (cc) + zpci_err_insn(cc, status, 0, (__force u64) addr); + + return (cc > 0) ? -EIO : cc; +} EXPORT_SYMBOL_GPL(zpci_load); /* PCI Store */ @@ -208,13 +246,48 @@ int __zpci_store(u64 data, u64 req, u64 offset) } EXPORT_SYMBOL_GPL(__zpci_store); -int zpci_store(const volatile void __iomem *addr, u64 data, unsigned long len) +static inline int zpci_store_fh(const volatile void __iomem *addr, u64 data, + unsigned long len) { struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(addr)]; u64 req = ZPCI_CREATE_REQ(entry->fh, entry->bar, len); return __zpci_store(data, req, ZPCI_OFFSET(addr)); } + +static inline int __pcistg_mio(u64 data, u64 ioaddr, u64 len, u8 *status) +{ + register u64 addr asm("2") = ioaddr; + register u64 r3 asm("3") = len; + int cc = -ENXIO; + + asm volatile ( + " .insn rre,0xb9d40000,%[data],%[ioaddr]\n" + "0: ipm %[cc]\n" + " srl %[cc],28\n" + "1:\n" + EX_TABLE(0b, 1b) + : [cc] "+d" (cc), "+d" (r3) + : [data] "d" (data), [ioaddr] "d" (addr) + : "cc"); + *status = r3 >> 24 & 0xff; + return cc; +} + +int zpci_store(const volatile void __iomem *addr, u64 data, unsigned long len) +{ + u8 status; + int cc; + + if (!static_branch_unlikely(&have_mio)) + return zpci_store_fh(addr, data, len); + + cc = __pcistg_mio(data, (__force u64) addr, len, &status); + if (cc) + zpci_err_insn(cc, status, 0, (__force u64) addr); + + return (cc > 0) ? -EIO : cc; +} EXPORT_SYMBOL_GPL(zpci_store); /* PCI Store Block */ @@ -253,8 +326,8 @@ int __zpci_store_block(const u64 *data, u64 req, u64 offset) } EXPORT_SYMBOL_GPL(__zpci_store_block); -int zpci_write_block(volatile void __iomem *dst, - const void *src, unsigned long len) +static inline int zpci_write_block_fh(volatile void __iomem *dst, + const void *src, unsigned long len) { struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(dst)]; u64 req = ZPCI_CREATE_REQ(entry->fh, entry->bar, len); @@ -262,4 +335,52 @@ int zpci_write_block(volatile void __iomem *dst, return __zpci_store_block(src, req, offset); } + +static inline int __pcistb_mio(const u64 *data, u64 ioaddr, u64 len, u8 *status) +{ + int cc = -ENXIO; + + asm volatile ( + " .insn rsy,0xeb00000000d4,%[len],%[ioaddr],%[data]\n" + "0: ipm %[cc]\n" + " srl %[cc],28\n" + "1:\n" + EX_TABLE(0b, 1b) + : [cc] "+d" (cc), [len] "+d" (len) + : [ioaddr] "d" (ioaddr), [data] "Q" (*data) + : "cc"); + *status = len >> 24 & 0xff; + return cc; +} + +int zpci_write_block(volatile void __iomem *dst, + const void *src, unsigned long len) +{ + u8 status; + int cc; + + if (!static_branch_unlikely(&have_mio)) + return zpci_write_block_fh(dst, src, len); + + cc = __pcistb_mio(src, (__force u64) dst, len, &status); + if (cc) + zpci_err_insn(cc, status, 0, (__force u64) dst); + + return (cc > 0) ? -EIO : cc; +} EXPORT_SYMBOL_GPL(zpci_write_block); + +static inline void __pciwb_mio(void) +{ + unsigned long unused = 0; + + asm volatile (".insn rre,0xb9d50000,%[op],%[op]\n" + : [op] "+d" (unused)); +} + +void zpci_barrier(void) +{ + if (static_branch_likely(&have_mio)) + __pciwb_mio(); +} +EXPORT_SYMBOL_GPL(zpci_barrier); |