162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * s390 specific pci instructions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright IBM Corp. 2013 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/export.h> 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/jump_label.h> 1262306a36Sopenharmony_ci#include <asm/asm-extable.h> 1362306a36Sopenharmony_ci#include <asm/facility.h> 1462306a36Sopenharmony_ci#include <asm/pci_insn.h> 1562306a36Sopenharmony_ci#include <asm/pci_debug.h> 1662306a36Sopenharmony_ci#include <asm/pci_io.h> 1762306a36Sopenharmony_ci#include <asm/processor.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define ZPCI_INSN_BUSY_DELAY 1 /* 1 microsecond */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct zpci_err_insn_data { 2262306a36Sopenharmony_ci u8 insn; 2362306a36Sopenharmony_ci u8 cc; 2462306a36Sopenharmony_ci u8 status; 2562306a36Sopenharmony_ci union { 2662306a36Sopenharmony_ci struct { 2762306a36Sopenharmony_ci u64 req; 2862306a36Sopenharmony_ci u64 offset; 2962306a36Sopenharmony_ci }; 3062306a36Sopenharmony_ci struct { 3162306a36Sopenharmony_ci u64 addr; 3262306a36Sopenharmony_ci u64 len; 3362306a36Sopenharmony_ci }; 3462306a36Sopenharmony_ci }; 3562306a36Sopenharmony_ci} __packed; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic inline void zpci_err_insn_req(int lvl, u8 insn, u8 cc, u8 status, 3862306a36Sopenharmony_ci u64 req, u64 offset) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct zpci_err_insn_data data = { 4162306a36Sopenharmony_ci .insn = insn, .cc = cc, .status = status, 4262306a36Sopenharmony_ci .req = req, .offset = offset}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci zpci_err_hex_level(lvl, &data, sizeof(data)); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic inline void zpci_err_insn_addr(int lvl, u8 insn, u8 cc, u8 status, 4862306a36Sopenharmony_ci u64 addr, u64 len) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct zpci_err_insn_data data = { 5162306a36Sopenharmony_ci .insn = insn, .cc = cc, .status = status, 5262306a36Sopenharmony_ci .addr = addr, .len = len}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci zpci_err_hex_level(lvl, &data, sizeof(data)); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* Modify PCI Function Controls */ 5862306a36Sopenharmony_cistatic inline u8 __mpcifc(u64 req, struct zpci_fib *fib, u8 *status) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci u8 cc; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci asm volatile ( 6362306a36Sopenharmony_ci " .insn rxy,0xe300000000d0,%[req],%[fib]\n" 6462306a36Sopenharmony_ci " ipm %[cc]\n" 6562306a36Sopenharmony_ci " srl %[cc],28\n" 6662306a36Sopenharmony_ci : [cc] "=d" (cc), [req] "+d" (req), [fib] "+Q" (*fib) 6762306a36Sopenharmony_ci : : "cc"); 6862306a36Sopenharmony_ci *status = req >> 24 & 0xff; 6962306a36Sopenharmony_ci return cc; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ciu8 zpci_mod_fc(u64 req, struct zpci_fib *fib, u8 *status) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci bool retried = false; 7562306a36Sopenharmony_ci u8 cc; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci do { 7862306a36Sopenharmony_ci cc = __mpcifc(req, fib, status); 7962306a36Sopenharmony_ci if (cc == 2) { 8062306a36Sopenharmony_ci msleep(ZPCI_INSN_BUSY_DELAY); 8162306a36Sopenharmony_ci if (!retried) { 8262306a36Sopenharmony_ci zpci_err_insn_req(1, 'M', cc, *status, req, 0); 8362306a36Sopenharmony_ci retried = true; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci } while (cc == 2); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (cc) 8962306a36Sopenharmony_ci zpci_err_insn_req(0, 'M', cc, *status, req, 0); 9062306a36Sopenharmony_ci else if (retried) 9162306a36Sopenharmony_ci zpci_err_insn_req(1, 'M', cc, *status, req, 0); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci return cc; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(zpci_mod_fc); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* Refresh PCI Translations */ 9862306a36Sopenharmony_cistatic inline u8 __rpcit(u64 fn, u64 addr, u64 range, u8 *status) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci union register_pair addr_range = {.even = addr, .odd = range}; 10162306a36Sopenharmony_ci u8 cc; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci asm volatile ( 10462306a36Sopenharmony_ci " .insn rre,0xb9d30000,%[fn],%[addr_range]\n" 10562306a36Sopenharmony_ci " ipm %[cc]\n" 10662306a36Sopenharmony_ci " srl %[cc],28\n" 10762306a36Sopenharmony_ci : [cc] "=d" (cc), [fn] "+d" (fn) 10862306a36Sopenharmony_ci : [addr_range] "d" (addr_range.pair) 10962306a36Sopenharmony_ci : "cc"); 11062306a36Sopenharmony_ci *status = fn >> 24 & 0xff; 11162306a36Sopenharmony_ci return cc; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ciint zpci_refresh_trans(u64 fn, u64 addr, u64 range) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci bool retried = false; 11762306a36Sopenharmony_ci u8 cc, status; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci do { 12062306a36Sopenharmony_ci cc = __rpcit(fn, addr, range, &status); 12162306a36Sopenharmony_ci if (cc == 2) { 12262306a36Sopenharmony_ci udelay(ZPCI_INSN_BUSY_DELAY); 12362306a36Sopenharmony_ci if (!retried) { 12462306a36Sopenharmony_ci zpci_err_insn_addr(1, 'R', cc, status, addr, range); 12562306a36Sopenharmony_ci retried = true; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci } while (cc == 2); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (cc) 13162306a36Sopenharmony_ci zpci_err_insn_addr(0, 'R', cc, status, addr, range); 13262306a36Sopenharmony_ci else if (retried) 13362306a36Sopenharmony_ci zpci_err_insn_addr(1, 'R', cc, status, addr, range); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (cc == 1 && (status == 4 || status == 16)) 13662306a36Sopenharmony_ci return -ENOMEM; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return (cc) ? -EIO : 0; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* Set Interruption Controls */ 14262306a36Sopenharmony_ciint zpci_set_irq_ctrl(u16 ctl, u8 isc, union zpci_sic_iib *iib) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci if (!test_facility(72)) 14562306a36Sopenharmony_ci return -EIO; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci asm volatile( 14862306a36Sopenharmony_ci ".insn rsy,0xeb00000000d1,%[ctl],%[isc],%[iib]\n" 14962306a36Sopenharmony_ci : : [ctl] "d" (ctl), [isc] "d" (isc << 27), [iib] "Q" (*iib)); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(zpci_set_irq_ctrl); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci/* PCI Load */ 15662306a36Sopenharmony_cistatic inline int ____pcilg(u64 *data, u64 req, u64 offset, u8 *status) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci union register_pair req_off = {.even = req, .odd = offset}; 15962306a36Sopenharmony_ci int cc = -ENXIO; 16062306a36Sopenharmony_ci u64 __data; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci asm volatile ( 16362306a36Sopenharmony_ci " .insn rre,0xb9d20000,%[data],%[req_off]\n" 16462306a36Sopenharmony_ci "0: ipm %[cc]\n" 16562306a36Sopenharmony_ci " srl %[cc],28\n" 16662306a36Sopenharmony_ci "1:\n" 16762306a36Sopenharmony_ci EX_TABLE(0b, 1b) 16862306a36Sopenharmony_ci : [cc] "+d" (cc), [data] "=d" (__data), 16962306a36Sopenharmony_ci [req_off] "+&d" (req_off.pair) :: "cc"); 17062306a36Sopenharmony_ci *status = req_off.even >> 24 & 0xff; 17162306a36Sopenharmony_ci *data = __data; 17262306a36Sopenharmony_ci return cc; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic inline int __pcilg(u64 *data, u64 req, u64 offset, u8 *status) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci u64 __data; 17862306a36Sopenharmony_ci int cc; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci cc = ____pcilg(&__data, req, offset, status); 18162306a36Sopenharmony_ci if (!cc) 18262306a36Sopenharmony_ci *data = __data; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return cc; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ciint __zpci_load(u64 *data, u64 req, u64 offset) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci bool retried = false; 19062306a36Sopenharmony_ci u8 status; 19162306a36Sopenharmony_ci int cc; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci do { 19462306a36Sopenharmony_ci cc = __pcilg(data, req, offset, &status); 19562306a36Sopenharmony_ci if (cc == 2) { 19662306a36Sopenharmony_ci udelay(ZPCI_INSN_BUSY_DELAY); 19762306a36Sopenharmony_ci if (!retried) { 19862306a36Sopenharmony_ci zpci_err_insn_req(1, 'l', cc, status, req, offset); 19962306a36Sopenharmony_ci retried = true; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci } while (cc == 2); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (cc) 20562306a36Sopenharmony_ci zpci_err_insn_req(0, 'l', cc, status, req, offset); 20662306a36Sopenharmony_ci else if (retried) 20762306a36Sopenharmony_ci zpci_err_insn_req(1, 'l', cc, status, req, offset); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return (cc > 0) ? -EIO : cc; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__zpci_load); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic inline int zpci_load_fh(u64 *data, const volatile void __iomem *addr, 21462306a36Sopenharmony_ci unsigned long len) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(addr)]; 21762306a36Sopenharmony_ci u64 req = ZPCI_CREATE_REQ(READ_ONCE(entry->fh), entry->bar, len); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci return __zpci_load(data, req, ZPCI_OFFSET(addr)); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic inline int __pcilg_mio(u64 *data, u64 ioaddr, u64 len, u8 *status) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci union register_pair ioaddr_len = {.even = ioaddr, .odd = len}; 22562306a36Sopenharmony_ci int cc = -ENXIO; 22662306a36Sopenharmony_ci u64 __data; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci asm volatile ( 22962306a36Sopenharmony_ci " .insn rre,0xb9d60000,%[data],%[ioaddr_len]\n" 23062306a36Sopenharmony_ci "0: ipm %[cc]\n" 23162306a36Sopenharmony_ci " srl %[cc],28\n" 23262306a36Sopenharmony_ci "1:\n" 23362306a36Sopenharmony_ci EX_TABLE(0b, 1b) 23462306a36Sopenharmony_ci : [cc] "+d" (cc), [data] "=d" (__data), 23562306a36Sopenharmony_ci [ioaddr_len] "+&d" (ioaddr_len.pair) :: "cc"); 23662306a36Sopenharmony_ci *status = ioaddr_len.odd >> 24 & 0xff; 23762306a36Sopenharmony_ci *data = __data; 23862306a36Sopenharmony_ci return cc; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ciint zpci_load(u64 *data, const volatile void __iomem *addr, unsigned long len) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci u8 status; 24462306a36Sopenharmony_ci int cc; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (!static_branch_unlikely(&have_mio)) 24762306a36Sopenharmony_ci return zpci_load_fh(data, addr, len); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci cc = __pcilg_mio(data, (__force u64) addr, len, &status); 25062306a36Sopenharmony_ci if (cc) 25162306a36Sopenharmony_ci zpci_err_insn_addr(0, 'L', cc, status, (__force u64) addr, len); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return (cc > 0) ? -EIO : cc; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(zpci_load); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci/* PCI Store */ 25862306a36Sopenharmony_cistatic inline int __pcistg(u64 data, u64 req, u64 offset, u8 *status) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci union register_pair req_off = {.even = req, .odd = offset}; 26162306a36Sopenharmony_ci int cc = -ENXIO; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci asm volatile ( 26462306a36Sopenharmony_ci " .insn rre,0xb9d00000,%[data],%[req_off]\n" 26562306a36Sopenharmony_ci "0: ipm %[cc]\n" 26662306a36Sopenharmony_ci " srl %[cc],28\n" 26762306a36Sopenharmony_ci "1:\n" 26862306a36Sopenharmony_ci EX_TABLE(0b, 1b) 26962306a36Sopenharmony_ci : [cc] "+d" (cc), [req_off] "+&d" (req_off.pair) 27062306a36Sopenharmony_ci : [data] "d" (data) 27162306a36Sopenharmony_ci : "cc"); 27262306a36Sopenharmony_ci *status = req_off.even >> 24 & 0xff; 27362306a36Sopenharmony_ci return cc; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ciint __zpci_store(u64 data, u64 req, u64 offset) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci bool retried = false; 27962306a36Sopenharmony_ci u8 status; 28062306a36Sopenharmony_ci int cc; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci do { 28362306a36Sopenharmony_ci cc = __pcistg(data, req, offset, &status); 28462306a36Sopenharmony_ci if (cc == 2) { 28562306a36Sopenharmony_ci udelay(ZPCI_INSN_BUSY_DELAY); 28662306a36Sopenharmony_ci if (!retried) { 28762306a36Sopenharmony_ci zpci_err_insn_req(1, 's', cc, status, req, offset); 28862306a36Sopenharmony_ci retried = true; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci } while (cc == 2); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (cc) 29462306a36Sopenharmony_ci zpci_err_insn_req(0, 's', cc, status, req, offset); 29562306a36Sopenharmony_ci else if (retried) 29662306a36Sopenharmony_ci zpci_err_insn_req(1, 's', cc, status, req, offset); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return (cc > 0) ? -EIO : cc; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__zpci_store); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic inline int zpci_store_fh(const volatile void __iomem *addr, u64 data, 30362306a36Sopenharmony_ci unsigned long len) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(addr)]; 30662306a36Sopenharmony_ci u64 req = ZPCI_CREATE_REQ(READ_ONCE(entry->fh), entry->bar, len); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci return __zpci_store(data, req, ZPCI_OFFSET(addr)); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic inline int __pcistg_mio(u64 data, u64 ioaddr, u64 len, u8 *status) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci union register_pair ioaddr_len = {.even = ioaddr, .odd = len}; 31462306a36Sopenharmony_ci int cc = -ENXIO; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci asm volatile ( 31762306a36Sopenharmony_ci " .insn rre,0xb9d40000,%[data],%[ioaddr_len]\n" 31862306a36Sopenharmony_ci "0: ipm %[cc]\n" 31962306a36Sopenharmony_ci " srl %[cc],28\n" 32062306a36Sopenharmony_ci "1:\n" 32162306a36Sopenharmony_ci EX_TABLE(0b, 1b) 32262306a36Sopenharmony_ci : [cc] "+d" (cc), [ioaddr_len] "+&d" (ioaddr_len.pair) 32362306a36Sopenharmony_ci : [data] "d" (data) 32462306a36Sopenharmony_ci : "cc", "memory"); 32562306a36Sopenharmony_ci *status = ioaddr_len.odd >> 24 & 0xff; 32662306a36Sopenharmony_ci return cc; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ciint zpci_store(const volatile void __iomem *addr, u64 data, unsigned long len) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci u8 status; 33262306a36Sopenharmony_ci int cc; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (!static_branch_unlikely(&have_mio)) 33562306a36Sopenharmony_ci return zpci_store_fh(addr, data, len); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci cc = __pcistg_mio(data, (__force u64) addr, len, &status); 33862306a36Sopenharmony_ci if (cc) 33962306a36Sopenharmony_ci zpci_err_insn_addr(0, 'S', cc, status, (__force u64) addr, len); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return (cc > 0) ? -EIO : cc; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(zpci_store); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci/* PCI Store Block */ 34662306a36Sopenharmony_cistatic inline int __pcistb(const u64 *data, u64 req, u64 offset, u8 *status) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci int cc = -ENXIO; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci asm volatile ( 35162306a36Sopenharmony_ci " .insn rsy,0xeb00000000d0,%[req],%[offset],%[data]\n" 35262306a36Sopenharmony_ci "0: ipm %[cc]\n" 35362306a36Sopenharmony_ci " srl %[cc],28\n" 35462306a36Sopenharmony_ci "1:\n" 35562306a36Sopenharmony_ci EX_TABLE(0b, 1b) 35662306a36Sopenharmony_ci : [cc] "+d" (cc), [req] "+d" (req) 35762306a36Sopenharmony_ci : [offset] "d" (offset), [data] "Q" (*data) 35862306a36Sopenharmony_ci : "cc"); 35962306a36Sopenharmony_ci *status = req >> 24 & 0xff; 36062306a36Sopenharmony_ci return cc; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ciint __zpci_store_block(const u64 *data, u64 req, u64 offset) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci bool retried = false; 36662306a36Sopenharmony_ci u8 status; 36762306a36Sopenharmony_ci int cc; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci do { 37062306a36Sopenharmony_ci cc = __pcistb(data, req, offset, &status); 37162306a36Sopenharmony_ci if (cc == 2) { 37262306a36Sopenharmony_ci udelay(ZPCI_INSN_BUSY_DELAY); 37362306a36Sopenharmony_ci if (!retried) { 37462306a36Sopenharmony_ci zpci_err_insn_req(0, 'b', cc, status, req, offset); 37562306a36Sopenharmony_ci retried = true; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci } while (cc == 2); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (cc) 38162306a36Sopenharmony_ci zpci_err_insn_req(0, 'b', cc, status, req, offset); 38262306a36Sopenharmony_ci else if (retried) 38362306a36Sopenharmony_ci zpci_err_insn_req(1, 'b', cc, status, req, offset); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return (cc > 0) ? -EIO : cc; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__zpci_store_block); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic inline int zpci_write_block_fh(volatile void __iomem *dst, 39062306a36Sopenharmony_ci const void *src, unsigned long len) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(dst)]; 39362306a36Sopenharmony_ci u64 req = ZPCI_CREATE_REQ(entry->fh, entry->bar, len); 39462306a36Sopenharmony_ci u64 offset = ZPCI_OFFSET(dst); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci return __zpci_store_block(src, req, offset); 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic inline int __pcistb_mio(const u64 *data, u64 ioaddr, u64 len, u8 *status) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci int cc = -ENXIO; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci asm volatile ( 40462306a36Sopenharmony_ci " .insn rsy,0xeb00000000d4,%[len],%[ioaddr],%[data]\n" 40562306a36Sopenharmony_ci "0: ipm %[cc]\n" 40662306a36Sopenharmony_ci " srl %[cc],28\n" 40762306a36Sopenharmony_ci "1:\n" 40862306a36Sopenharmony_ci EX_TABLE(0b, 1b) 40962306a36Sopenharmony_ci : [cc] "+d" (cc), [len] "+d" (len) 41062306a36Sopenharmony_ci : [ioaddr] "d" (ioaddr), [data] "Q" (*data) 41162306a36Sopenharmony_ci : "cc"); 41262306a36Sopenharmony_ci *status = len >> 24 & 0xff; 41362306a36Sopenharmony_ci return cc; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ciint zpci_write_block(volatile void __iomem *dst, 41762306a36Sopenharmony_ci const void *src, unsigned long len) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci u8 status; 42062306a36Sopenharmony_ci int cc; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (!static_branch_unlikely(&have_mio)) 42362306a36Sopenharmony_ci return zpci_write_block_fh(dst, src, len); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci cc = __pcistb_mio(src, (__force u64) dst, len, &status); 42662306a36Sopenharmony_ci if (cc) 42762306a36Sopenharmony_ci zpci_err_insn_addr(0, 'B', cc, status, (__force u64) dst, len); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci return (cc > 0) ? -EIO : cc; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(zpci_write_block); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic inline void __pciwb_mio(void) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci asm volatile (".insn rre,0xb9d50000,0,0\n"); 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_civoid zpci_barrier(void) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci if (static_branch_likely(&have_mio)) 44162306a36Sopenharmony_ci __pciwb_mio(); 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(zpci_barrier); 444