xref: /linux/arch/s390/pci/pci_insn.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1adbb3901SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2cbcca5d0SSebastian Ott /*
3cbcca5d0SSebastian Ott  * s390 specific pci instructions
4cbcca5d0SSebastian Ott  *
5cbcca5d0SSebastian Ott  * Copyright IBM Corp. 2013
6cbcca5d0SSebastian Ott  */
7cbcca5d0SSebastian Ott 
8cbcca5d0SSebastian Ott #include <linux/export.h>
9cbcca5d0SSebastian Ott #include <linux/errno.h>
10cbcca5d0SSebastian Ott #include <linux/delay.h>
1171ba41c9SSebastian Ott #include <linux/jump_label.h>
12d09a307fSHeiko Carstens #include <asm/asm-extable.h>
1348070c73SChristian Borntraeger #include <asm/facility.h>
14cbcca5d0SSebastian Ott #include <asm/pci_insn.h>
153d8258e4SSebastian Ott #include <asm/pci_debug.h>
1681deca12SSebastian Ott #include <asm/pci_io.h>
17f0bacb7fSSebastian Ott #include <asm/processor.h>
18cbcca5d0SSebastian Ott 
19cbcca5d0SSebastian Ott #define ZPCI_INSN_BUSY_DELAY	1	/* 1 microsecond */
20cbcca5d0SSebastian Ott 
21cde8833eSNiklas Schnelle struct zpci_err_insn_data {
22cde8833eSNiklas Schnelle 	u8 insn;
23cde8833eSNiklas Schnelle 	u8 cc;
24cde8833eSNiklas Schnelle 	u8 status;
25cde8833eSNiklas Schnelle 	union {
263d8258e4SSebastian Ott 		struct {
273d8258e4SSebastian Ott 			u64 req;
283d8258e4SSebastian Ott 			u64 offset;
29cde8833eSNiklas Schnelle 		};
30cde8833eSNiklas Schnelle 		struct {
31cde8833eSNiklas Schnelle 			u64 addr;
32cde8833eSNiklas Schnelle 			u64 len;
33cde8833eSNiklas Schnelle 		};
34cde8833eSNiklas Schnelle 	};
35cde8833eSNiklas Schnelle } __packed;
36cde8833eSNiklas Schnelle 
zpci_err_insn_req(int lvl,u8 insn,u8 cc,u8 status,u64 req,u64 offset)3734fb0e70SNiklas Schnelle static inline void zpci_err_insn_req(int lvl, u8 insn, u8 cc, u8 status,
38cde8833eSNiklas Schnelle 				     u64 req, u64 offset)
39cde8833eSNiklas Schnelle {
40cde8833eSNiklas Schnelle 	struct zpci_err_insn_data data = {
41cde8833eSNiklas Schnelle 		.insn = insn, .cc = cc, .status = status,
42cde8833eSNiklas Schnelle 		.req = req, .offset = offset};
43cde8833eSNiklas Schnelle 
4434fb0e70SNiklas Schnelle 	zpci_err_hex_level(lvl, &data, sizeof(data));
45cde8833eSNiklas Schnelle }
46cde8833eSNiklas Schnelle 
zpci_err_insn_addr(int lvl,u8 insn,u8 cc,u8 status,u64 addr,u64 len)4734fb0e70SNiklas Schnelle static inline void zpci_err_insn_addr(int lvl, u8 insn, u8 cc, u8 status,
48cde8833eSNiklas Schnelle 				      u64 addr, u64 len)
49cde8833eSNiklas Schnelle {
50cde8833eSNiklas Schnelle 	struct zpci_err_insn_data data = {
51cde8833eSNiklas Schnelle 		.insn = insn, .cc = cc, .status = status,
52cde8833eSNiklas Schnelle 		.addr = addr, .len = len};
533d8258e4SSebastian Ott 
5434fb0e70SNiklas Schnelle 	zpci_err_hex_level(lvl, &data, sizeof(data));
553d8258e4SSebastian Ott }
563d8258e4SSebastian Ott 
57cbcca5d0SSebastian Ott /* Modify PCI Function Controls */
__mpcifc(u64 req,struct zpci_fib * fib,u8 * status)58cbcca5d0SSebastian Ott static inline u8 __mpcifc(u64 req, struct zpci_fib *fib, u8 *status)
59cbcca5d0SSebastian Ott {
60cbcca5d0SSebastian Ott 	u8 cc;
61cbcca5d0SSebastian Ott 
62cbcca5d0SSebastian Ott 	asm volatile (
63cbcca5d0SSebastian Ott 		"	.insn	rxy,0xe300000000d0,%[req],%[fib]\n"
64cbcca5d0SSebastian Ott 		"	ipm	%[cc]\n"
65cbcca5d0SSebastian Ott 		"	srl	%[cc],28\n"
66cbcca5d0SSebastian Ott 		: [cc] "=d" (cc), [req] "+d" (req), [fib] "+Q" (*fib)
67cbcca5d0SSebastian Ott 		: : "cc");
68cbcca5d0SSebastian Ott 	*status = req >> 24 & 0xff;
69cbcca5d0SSebastian Ott 	return cc;
70cbcca5d0SSebastian Ott }
71cbcca5d0SSebastian Ott 
zpci_mod_fc(u64 req,struct zpci_fib * fib,u8 * status)724dfbd3efSSebastian Ott u8 zpci_mod_fc(u64 req, struct zpci_fib *fib, u8 *status)
73cbcca5d0SSebastian Ott {
7434fb0e70SNiklas Schnelle 	bool retried = false;
754dfbd3efSSebastian Ott 	u8 cc;
76cbcca5d0SSebastian Ott 
77cbcca5d0SSebastian Ott 	do {
784dfbd3efSSebastian Ott 		cc = __mpcifc(req, fib, status);
7934fb0e70SNiklas Schnelle 		if (cc == 2) {
80cbcca5d0SSebastian Ott 			msleep(ZPCI_INSN_BUSY_DELAY);
8134fb0e70SNiklas Schnelle 			if (!retried) {
8234fb0e70SNiklas Schnelle 				zpci_err_insn_req(1, 'M', cc, *status, req, 0);
8334fb0e70SNiklas Schnelle 				retried = true;
8434fb0e70SNiklas Schnelle 			}
8534fb0e70SNiklas Schnelle 		}
86cbcca5d0SSebastian Ott 	} while (cc == 2);
87cbcca5d0SSebastian Ott 
88cbcca5d0SSebastian Ott 	if (cc)
8934fb0e70SNiklas Schnelle 		zpci_err_insn_req(0, 'M', cc, *status, req, 0);
9034fb0e70SNiklas Schnelle 	else if (retried)
9134fb0e70SNiklas Schnelle 		zpci_err_insn_req(1, 'M', cc, *status, req, 0);
923d8258e4SSebastian Ott 
934dfbd3efSSebastian Ott 	return cc;
94cbcca5d0SSebastian Ott }
95*3c5a1b6fSMatthew Rosato EXPORT_SYMBOL_GPL(zpci_mod_fc);
96cbcca5d0SSebastian Ott 
97cbcca5d0SSebastian Ott /* Refresh PCI Translations */
__rpcit(u64 fn,u64 addr,u64 range,u8 * status)98cbcca5d0SSebastian Ott static inline u8 __rpcit(u64 fn, u64 addr, u64 range, u8 *status)
99cbcca5d0SSebastian Ott {
100d66a4c7fSNiklas Schnelle 	union register_pair addr_range = {.even = addr, .odd = range};
101cbcca5d0SSebastian Ott 	u8 cc;
102cbcca5d0SSebastian Ott 
103cbcca5d0SSebastian Ott 	asm volatile (
104d66a4c7fSNiklas Schnelle 		"	.insn	rre,0xb9d30000,%[fn],%[addr_range]\n"
105cbcca5d0SSebastian Ott 		"	ipm	%[cc]\n"
106cbcca5d0SSebastian Ott 		"	srl	%[cc],28\n"
107cbcca5d0SSebastian Ott 		: [cc] "=d" (cc), [fn] "+d" (fn)
108d66a4c7fSNiklas Schnelle 		: [addr_range] "d" (addr_range.pair)
109cbcca5d0SSebastian Ott 		: "cc");
110cbcca5d0SSebastian Ott 	*status = fn >> 24 & 0xff;
111cbcca5d0SSebastian Ott 	return cc;
112cbcca5d0SSebastian Ott }
113cbcca5d0SSebastian Ott 
zpci_refresh_trans(u64 fn,u64 addr,u64 range)1149389339fSMartin Schwidefsky int zpci_refresh_trans(u64 fn, u64 addr, u64 range)
115cbcca5d0SSebastian Ott {
11634fb0e70SNiklas Schnelle 	bool retried = false;
117cbcca5d0SSebastian Ott 	u8 cc, status;
118cbcca5d0SSebastian Ott 
119cbcca5d0SSebastian Ott 	do {
120cbcca5d0SSebastian Ott 		cc = __rpcit(fn, addr, range, &status);
12134fb0e70SNiklas Schnelle 		if (cc == 2) {
122cbcca5d0SSebastian Ott 			udelay(ZPCI_INSN_BUSY_DELAY);
12334fb0e70SNiklas Schnelle 			if (!retried) {
12434fb0e70SNiklas Schnelle 				zpci_err_insn_addr(1, 'R', cc, status, addr, range);
12534fb0e70SNiklas Schnelle 				retried = true;
12634fb0e70SNiklas Schnelle 			}
12734fb0e70SNiklas Schnelle 		}
128cbcca5d0SSebastian Ott 	} while (cc == 2);
129cbcca5d0SSebastian Ott 
130cbcca5d0SSebastian Ott 	if (cc)
13134fb0e70SNiklas Schnelle 		zpci_err_insn_addr(0, 'R', cc, status, addr, range);
13234fb0e70SNiklas Schnelle 	else if (retried)
13334fb0e70SNiklas Schnelle 		zpci_err_insn_addr(1, 'R', cc, status, addr, range);
1343d8258e4SSebastian Ott 
135a5f10055SSebastian Ott 	if (cc == 1 && (status == 4 || status == 16))
136a5f10055SSebastian Ott 		return -ENOMEM;
137a5f10055SSebastian Ott 
138cbcca5d0SSebastian Ott 	return (cc) ? -EIO : 0;
139cbcca5d0SSebastian Ott }
140cbcca5d0SSebastian Ott 
141cbcca5d0SSebastian Ott /* Set Interruption Controls */
zpci_set_irq_ctrl(u16 ctl,u8 isc,union zpci_sic_iib * iib)142062f0024SMatthew Rosato int zpci_set_irq_ctrl(u16 ctl, u8 isc, union zpci_sic_iib *iib)
143cbcca5d0SSebastian Ott {
14448070c73SChristian Borntraeger 	if (!test_facility(72))
14548070c73SChristian Borntraeger 		return -EIO;
146e979ce7bSSebastian Ott 
147cbcca5d0SSebastian Ott 	asm volatile(
148e979ce7bSSebastian Ott 		".insn	rsy,0xeb00000000d1,%[ctl],%[isc],%[iib]\n"
149e979ce7bSSebastian Ott 		: : [ctl] "d" (ctl), [isc] "d" (isc << 27), [iib] "Q" (*iib));
150e979ce7bSSebastian Ott 
15148070c73SChristian Borntraeger 	return 0;
152cbcca5d0SSebastian Ott }
153062f0024SMatthew Rosato EXPORT_SYMBOL_GPL(zpci_set_irq_ctrl);
154cbcca5d0SSebastian Ott 
155cbcca5d0SSebastian Ott /* PCI Load */
____pcilg(u64 * data,u64 req,u64 offset,u8 * status)1567b411ac6SHeiko Carstens static inline int ____pcilg(u64 *data, u64 req, u64 offset, u8 *status)
157cbcca5d0SSebastian Ott {
158d66a4c7fSNiklas Schnelle 	union register_pair req_off = {.even = req, .odd = offset};
159f0bacb7fSSebastian Ott 	int cc = -ENXIO;
160cbcca5d0SSebastian Ott 	u64 __data;
161cbcca5d0SSebastian Ott 
162cbcca5d0SSebastian Ott 	asm volatile (
163d66a4c7fSNiklas Schnelle 		"	.insn	rre,0xb9d20000,%[data],%[req_off]\n"
164f0bacb7fSSebastian Ott 		"0:	ipm	%[cc]\n"
165cbcca5d0SSebastian Ott 		"	srl	%[cc],28\n"
166f0bacb7fSSebastian Ott 		"1:\n"
167f0bacb7fSSebastian Ott 		EX_TABLE(0b, 1b)
168d66a4c7fSNiklas Schnelle 		: [cc] "+d" (cc), [data] "=d" (__data),
169d66a4c7fSNiklas Schnelle 		  [req_off] "+&d" (req_off.pair) :: "cc");
170d66a4c7fSNiklas Schnelle 	*status = req_off.even >> 24 & 0xff;
1717b411ac6SHeiko Carstens 	*data = __data;
1727b411ac6SHeiko Carstens 	return cc;
1737b411ac6SHeiko Carstens }
1747b411ac6SHeiko Carstens 
__pcilg(u64 * data,u64 req,u64 offset,u8 * status)1757b411ac6SHeiko Carstens static inline int __pcilg(u64 *data, u64 req, u64 offset, u8 *status)
1767b411ac6SHeiko Carstens {
1777b411ac6SHeiko Carstens 	u64 __data;
1787b411ac6SHeiko Carstens 	int cc;
1797b411ac6SHeiko Carstens 
1807b411ac6SHeiko Carstens 	cc = ____pcilg(&__data, req, offset, status);
181b170bad4SSebastian Ott 	if (!cc)
182cbcca5d0SSebastian Ott 		*data = __data;
183b170bad4SSebastian Ott 
184cbcca5d0SSebastian Ott 	return cc;
185cbcca5d0SSebastian Ott }
186cbcca5d0SSebastian Ott 
__zpci_load(u64 * data,u64 req,u64 offset)18781deca12SSebastian Ott int __zpci_load(u64 *data, u64 req, u64 offset)
188cbcca5d0SSebastian Ott {
18934fb0e70SNiklas Schnelle 	bool retried = false;
190f0bacb7fSSebastian Ott 	u8 status;
191f0bacb7fSSebastian Ott 	int cc;
192cbcca5d0SSebastian Ott 
193cbcca5d0SSebastian Ott 	do {
194cbcca5d0SSebastian Ott 		cc = __pcilg(data, req, offset, &status);
19534fb0e70SNiklas Schnelle 		if (cc == 2) {
196cbcca5d0SSebastian Ott 			udelay(ZPCI_INSN_BUSY_DELAY);
19734fb0e70SNiklas Schnelle 			if (!retried) {
19834fb0e70SNiklas Schnelle 				zpci_err_insn_req(1, 'l', cc, status, req, offset);
19934fb0e70SNiklas Schnelle 				retried = true;
20034fb0e70SNiklas Schnelle 			}
20134fb0e70SNiklas Schnelle 		}
202cbcca5d0SSebastian Ott 	} while (cc == 2);
203cbcca5d0SSebastian Ott 
204f0bacb7fSSebastian Ott 	if (cc)
20534fb0e70SNiklas Schnelle 		zpci_err_insn_req(0, 'l', cc, status, req, offset);
20634fb0e70SNiklas Schnelle 	else if (retried)
20734fb0e70SNiklas Schnelle 		zpci_err_insn_req(1, 'l', cc, status, req, offset);
2083d8258e4SSebastian Ott 
209f0bacb7fSSebastian Ott 	return (cc > 0) ? -EIO : cc;
210cbcca5d0SSebastian Ott }
21181deca12SSebastian Ott EXPORT_SYMBOL_GPL(__zpci_load);
21281deca12SSebastian Ott 
zpci_load_fh(u64 * data,const volatile void __iomem * addr,unsigned long len)21371ba41c9SSebastian Ott static inline int zpci_load_fh(u64 *data, const volatile void __iomem *addr,
21471ba41c9SSebastian Ott 			       unsigned long len)
21581deca12SSebastian Ott {
21681deca12SSebastian Ott 	struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(addr)];
2174fe20497SNiklas Schnelle 	u64 req = ZPCI_CREATE_REQ(READ_ONCE(entry->fh), entry->bar, len);
21881deca12SSebastian Ott 
21981deca12SSebastian Ott 	return __zpci_load(data, req, ZPCI_OFFSET(addr));
22081deca12SSebastian Ott }
22171ba41c9SSebastian Ott 
__pcilg_mio(u64 * data,u64 ioaddr,u64 len,u8 * status)22271ba41c9SSebastian Ott static inline int __pcilg_mio(u64 *data, u64 ioaddr, u64 len, u8 *status)
22371ba41c9SSebastian Ott {
224d66a4c7fSNiklas Schnelle 	union register_pair ioaddr_len = {.even = ioaddr, .odd = len};
22571ba41c9SSebastian Ott 	int cc = -ENXIO;
22671ba41c9SSebastian Ott 	u64 __data;
22771ba41c9SSebastian Ott 
22871ba41c9SSebastian Ott 	asm volatile (
229d66a4c7fSNiklas Schnelle 		"       .insn   rre,0xb9d60000,%[data],%[ioaddr_len]\n"
23071ba41c9SSebastian Ott 		"0:     ipm     %[cc]\n"
23171ba41c9SSebastian Ott 		"       srl     %[cc],28\n"
23271ba41c9SSebastian Ott 		"1:\n"
23371ba41c9SSebastian Ott 		EX_TABLE(0b, 1b)
234d66a4c7fSNiklas Schnelle 		: [cc] "+d" (cc), [data] "=d" (__data),
235d66a4c7fSNiklas Schnelle 		  [ioaddr_len] "+&d" (ioaddr_len.pair) :: "cc");
236d66a4c7fSNiklas Schnelle 	*status = ioaddr_len.odd >> 24 & 0xff;
23771ba41c9SSebastian Ott 	*data = __data;
23871ba41c9SSebastian Ott 	return cc;
23971ba41c9SSebastian Ott }
24071ba41c9SSebastian Ott 
zpci_load(u64 * data,const volatile void __iomem * addr,unsigned long len)24171ba41c9SSebastian Ott int zpci_load(u64 *data, const volatile void __iomem *addr, unsigned long len)
24271ba41c9SSebastian Ott {
24371ba41c9SSebastian Ott 	u8 status;
24471ba41c9SSebastian Ott 	int cc;
24571ba41c9SSebastian Ott 
24671ba41c9SSebastian Ott 	if (!static_branch_unlikely(&have_mio))
24771ba41c9SSebastian Ott 		return zpci_load_fh(data, addr, len);
24871ba41c9SSebastian Ott 
24971ba41c9SSebastian Ott 	cc = __pcilg_mio(data, (__force u64) addr, len, &status);
25071ba41c9SSebastian Ott 	if (cc)
25134fb0e70SNiklas Schnelle 		zpci_err_insn_addr(0, 'L', cc, status, (__force u64) addr, len);
25271ba41c9SSebastian Ott 
25371ba41c9SSebastian Ott 	return (cc > 0) ? -EIO : cc;
25471ba41c9SSebastian Ott }
2559389339fSMartin Schwidefsky EXPORT_SYMBOL_GPL(zpci_load);
256cbcca5d0SSebastian Ott 
257cbcca5d0SSebastian Ott /* PCI Store */
__pcistg(u64 data,u64 req,u64 offset,u8 * status)258f0bacb7fSSebastian Ott static inline int __pcistg(u64 data, u64 req, u64 offset, u8 *status)
259cbcca5d0SSebastian Ott {
260d66a4c7fSNiklas Schnelle 	union register_pair req_off = {.even = req, .odd = offset};
261f0bacb7fSSebastian Ott 	int cc = -ENXIO;
262cbcca5d0SSebastian Ott 
263cbcca5d0SSebastian Ott 	asm volatile (
264d66a4c7fSNiklas Schnelle 		"	.insn	rre,0xb9d00000,%[data],%[req_off]\n"
265f0bacb7fSSebastian Ott 		"0:	ipm	%[cc]\n"
266cbcca5d0SSebastian Ott 		"	srl	%[cc],28\n"
267f0bacb7fSSebastian Ott 		"1:\n"
268f0bacb7fSSebastian Ott 		EX_TABLE(0b, 1b)
269d66a4c7fSNiklas Schnelle 		: [cc] "+d" (cc), [req_off] "+&d" (req_off.pair)
270d66a4c7fSNiklas Schnelle 		: [data] "d" (data)
271cbcca5d0SSebastian Ott 		: "cc");
272d66a4c7fSNiklas Schnelle 	*status = req_off.even >> 24 & 0xff;
273cbcca5d0SSebastian Ott 	return cc;
274cbcca5d0SSebastian Ott }
275cbcca5d0SSebastian Ott 
__zpci_store(u64 data,u64 req,u64 offset)27681deca12SSebastian Ott int __zpci_store(u64 data, u64 req, u64 offset)
277cbcca5d0SSebastian Ott {
27834fb0e70SNiklas Schnelle 	bool retried = false;
279f0bacb7fSSebastian Ott 	u8 status;
280f0bacb7fSSebastian Ott 	int cc;
281cbcca5d0SSebastian Ott 
282cbcca5d0SSebastian Ott 	do {
283cbcca5d0SSebastian Ott 		cc = __pcistg(data, req, offset, &status);
28434fb0e70SNiklas Schnelle 		if (cc == 2) {
285cbcca5d0SSebastian Ott 			udelay(ZPCI_INSN_BUSY_DELAY);
28634fb0e70SNiklas Schnelle 			if (!retried) {
28734fb0e70SNiklas Schnelle 				zpci_err_insn_req(1, 's', cc, status, req, offset);
28834fb0e70SNiklas Schnelle 				retried = true;
28934fb0e70SNiklas Schnelle 			}
29034fb0e70SNiklas Schnelle 		}
291cbcca5d0SSebastian Ott 	} while (cc == 2);
292cbcca5d0SSebastian Ott 
293cbcca5d0SSebastian Ott 	if (cc)
29434fb0e70SNiklas Schnelle 		zpci_err_insn_req(0, 's', cc, status, req, offset);
29534fb0e70SNiklas Schnelle 	else if (retried)
29634fb0e70SNiklas Schnelle 		zpci_err_insn_req(1, 's', cc, status, req, offset);
2973d8258e4SSebastian Ott 
298f0bacb7fSSebastian Ott 	return (cc > 0) ? -EIO : cc;
299cbcca5d0SSebastian Ott }
30081deca12SSebastian Ott EXPORT_SYMBOL_GPL(__zpci_store);
30181deca12SSebastian Ott 
zpci_store_fh(const volatile void __iomem * addr,u64 data,unsigned long len)30271ba41c9SSebastian Ott static inline int zpci_store_fh(const volatile void __iomem *addr, u64 data,
30371ba41c9SSebastian Ott 				unsigned long len)
30481deca12SSebastian Ott {
30581deca12SSebastian Ott 	struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(addr)];
3064fe20497SNiklas Schnelle 	u64 req = ZPCI_CREATE_REQ(READ_ONCE(entry->fh), entry->bar, len);
30781deca12SSebastian Ott 
30881deca12SSebastian Ott 	return __zpci_store(data, req, ZPCI_OFFSET(addr));
30981deca12SSebastian Ott }
31071ba41c9SSebastian Ott 
__pcistg_mio(u64 data,u64 ioaddr,u64 len,u8 * status)31171ba41c9SSebastian Ott static inline int __pcistg_mio(u64 data, u64 ioaddr, u64 len, u8 *status)
31271ba41c9SSebastian Ott {
313d66a4c7fSNiklas Schnelle 	union register_pair ioaddr_len = {.even = ioaddr, .odd = len};
31471ba41c9SSebastian Ott 	int cc = -ENXIO;
31571ba41c9SSebastian Ott 
31671ba41c9SSebastian Ott 	asm volatile (
317d66a4c7fSNiklas Schnelle 		"       .insn   rre,0xb9d40000,%[data],%[ioaddr_len]\n"
31871ba41c9SSebastian Ott 		"0:     ipm     %[cc]\n"
31971ba41c9SSebastian Ott 		"       srl     %[cc],28\n"
32071ba41c9SSebastian Ott 		"1:\n"
32171ba41c9SSebastian Ott 		EX_TABLE(0b, 1b)
322d66a4c7fSNiklas Schnelle 		: [cc] "+d" (cc), [ioaddr_len] "+&d" (ioaddr_len.pair)
323d66a4c7fSNiklas Schnelle 		: [data] "d" (data)
324d66a4c7fSNiklas Schnelle 		: "cc", "memory");
325d66a4c7fSNiklas Schnelle 	*status = ioaddr_len.odd >> 24 & 0xff;
32671ba41c9SSebastian Ott 	return cc;
32771ba41c9SSebastian Ott }
32871ba41c9SSebastian Ott 
zpci_store(const volatile void __iomem * addr,u64 data,unsigned long len)32971ba41c9SSebastian Ott int zpci_store(const volatile void __iomem *addr, u64 data, unsigned long len)
33071ba41c9SSebastian Ott {
33171ba41c9SSebastian Ott 	u8 status;
33271ba41c9SSebastian Ott 	int cc;
33371ba41c9SSebastian Ott 
33471ba41c9SSebastian Ott 	if (!static_branch_unlikely(&have_mio))
33571ba41c9SSebastian Ott 		return zpci_store_fh(addr, data, len);
33671ba41c9SSebastian Ott 
33771ba41c9SSebastian Ott 	cc = __pcistg_mio(data, (__force u64) addr, len, &status);
33871ba41c9SSebastian Ott 	if (cc)
33934fb0e70SNiklas Schnelle 		zpci_err_insn_addr(0, 'S', cc, status, (__force u64) addr, len);
34071ba41c9SSebastian Ott 
34171ba41c9SSebastian Ott 	return (cc > 0) ? -EIO : cc;
34271ba41c9SSebastian Ott }
3439389339fSMartin Schwidefsky EXPORT_SYMBOL_GPL(zpci_store);
344cbcca5d0SSebastian Ott 
345cbcca5d0SSebastian Ott /* PCI Store Block */
__pcistb(const u64 * data,u64 req,u64 offset,u8 * status)346f0bacb7fSSebastian Ott static inline int __pcistb(const u64 *data, u64 req, u64 offset, u8 *status)
347cbcca5d0SSebastian Ott {
348f0bacb7fSSebastian Ott 	int cc = -ENXIO;
349cbcca5d0SSebastian Ott 
350cbcca5d0SSebastian Ott 	asm volatile (
351cbcca5d0SSebastian Ott 		"	.insn	rsy,0xeb00000000d0,%[req],%[offset],%[data]\n"
352f0bacb7fSSebastian Ott 		"0:	ipm	%[cc]\n"
353cbcca5d0SSebastian Ott 		"	srl	%[cc],28\n"
354f0bacb7fSSebastian Ott 		"1:\n"
355f0bacb7fSSebastian Ott 		EX_TABLE(0b, 1b)
356f0bacb7fSSebastian Ott 		: [cc] "+d" (cc), [req] "+d" (req)
357cbcca5d0SSebastian Ott 		: [offset] "d" (offset), [data] "Q" (*data)
358cbcca5d0SSebastian Ott 		: "cc");
359cbcca5d0SSebastian Ott 	*status = req >> 24 & 0xff;
360cbcca5d0SSebastian Ott 	return cc;
361cbcca5d0SSebastian Ott }
362cbcca5d0SSebastian Ott 
__zpci_store_block(const u64 * data,u64 req,u64 offset)36381deca12SSebastian Ott int __zpci_store_block(const u64 *data, u64 req, u64 offset)
364cbcca5d0SSebastian Ott {
36534fb0e70SNiklas Schnelle 	bool retried = false;
366f0bacb7fSSebastian Ott 	u8 status;
367f0bacb7fSSebastian Ott 	int cc;
368cbcca5d0SSebastian Ott 
369cbcca5d0SSebastian Ott 	do {
370cbcca5d0SSebastian Ott 		cc = __pcistb(data, req, offset, &status);
37134fb0e70SNiklas Schnelle 		if (cc == 2) {
372cbcca5d0SSebastian Ott 			udelay(ZPCI_INSN_BUSY_DELAY);
37334fb0e70SNiklas Schnelle 			if (!retried) {
37434fb0e70SNiklas Schnelle 				zpci_err_insn_req(0, 'b', cc, status, req, offset);
37534fb0e70SNiklas Schnelle 				retried = true;
37634fb0e70SNiklas Schnelle 			}
37734fb0e70SNiklas Schnelle 		}
378cbcca5d0SSebastian Ott 	} while (cc == 2);
379cbcca5d0SSebastian Ott 
380cbcca5d0SSebastian Ott 	if (cc)
38134fb0e70SNiklas Schnelle 		zpci_err_insn_req(0, 'b', cc, status, req, offset);
38234fb0e70SNiklas Schnelle 	else if (retried)
38334fb0e70SNiklas Schnelle 		zpci_err_insn_req(1, 'b', cc, status, req, offset);
3843d8258e4SSebastian Ott 
385f0bacb7fSSebastian Ott 	return (cc > 0) ? -EIO : cc;
386cbcca5d0SSebastian Ott }
38781deca12SSebastian Ott EXPORT_SYMBOL_GPL(__zpci_store_block);
38881deca12SSebastian Ott 
zpci_write_block_fh(volatile void __iomem * dst,const void * src,unsigned long len)38971ba41c9SSebastian Ott static inline int zpci_write_block_fh(volatile void __iomem *dst,
39081deca12SSebastian Ott 				      const void *src, unsigned long len)
39181deca12SSebastian Ott {
39281deca12SSebastian Ott 	struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(dst)];
39381deca12SSebastian Ott 	u64 req = ZPCI_CREATE_REQ(entry->fh, entry->bar, len);
39481deca12SSebastian Ott 	u64 offset = ZPCI_OFFSET(dst);
39581deca12SSebastian Ott 
39681deca12SSebastian Ott 	return __zpci_store_block(src, req, offset);
39781deca12SSebastian Ott }
39871ba41c9SSebastian Ott 
__pcistb_mio(const u64 * data,u64 ioaddr,u64 len,u8 * status)39971ba41c9SSebastian Ott static inline int __pcistb_mio(const u64 *data, u64 ioaddr, u64 len, u8 *status)
40071ba41c9SSebastian Ott {
40171ba41c9SSebastian Ott 	int cc = -ENXIO;
40271ba41c9SSebastian Ott 
40371ba41c9SSebastian Ott 	asm volatile (
40471ba41c9SSebastian Ott 		"       .insn   rsy,0xeb00000000d4,%[len],%[ioaddr],%[data]\n"
40571ba41c9SSebastian Ott 		"0:     ipm     %[cc]\n"
40671ba41c9SSebastian Ott 		"       srl     %[cc],28\n"
40771ba41c9SSebastian Ott 		"1:\n"
40871ba41c9SSebastian Ott 		EX_TABLE(0b, 1b)
40971ba41c9SSebastian Ott 		: [cc] "+d" (cc), [len] "+d" (len)
41071ba41c9SSebastian Ott 		: [ioaddr] "d" (ioaddr), [data] "Q" (*data)
41171ba41c9SSebastian Ott 		: "cc");
41271ba41c9SSebastian Ott 	*status = len >> 24 & 0xff;
41371ba41c9SSebastian Ott 	return cc;
41471ba41c9SSebastian Ott }
41571ba41c9SSebastian Ott 
zpci_write_block(volatile void __iomem * dst,const void * src,unsigned long len)41671ba41c9SSebastian Ott int zpci_write_block(volatile void __iomem *dst,
41771ba41c9SSebastian Ott 		     const void *src, unsigned long len)
41871ba41c9SSebastian Ott {
41971ba41c9SSebastian Ott 	u8 status;
42071ba41c9SSebastian Ott 	int cc;
42171ba41c9SSebastian Ott 
42271ba41c9SSebastian Ott 	if (!static_branch_unlikely(&have_mio))
42371ba41c9SSebastian Ott 		return zpci_write_block_fh(dst, src, len);
42471ba41c9SSebastian Ott 
42571ba41c9SSebastian Ott 	cc = __pcistb_mio(src, (__force u64) dst, len, &status);
42671ba41c9SSebastian Ott 	if (cc)
42734fb0e70SNiklas Schnelle 		zpci_err_insn_addr(0, 'B', cc, status, (__force u64) dst, len);
42871ba41c9SSebastian Ott 
42971ba41c9SSebastian Ott 	return (cc > 0) ? -EIO : cc;
43071ba41c9SSebastian Ott }
43181deca12SSebastian Ott EXPORT_SYMBOL_GPL(zpci_write_block);
43271ba41c9SSebastian Ott 
__pciwb_mio(void)43371ba41c9SSebastian Ott static inline void __pciwb_mio(void)
43471ba41c9SSebastian Ott {
4357b293216SNiklas Schnelle 	asm volatile (".insn    rre,0xb9d50000,0,0\n");
43671ba41c9SSebastian Ott }
43771ba41c9SSebastian Ott 
zpci_barrier(void)43871ba41c9SSebastian Ott void zpci_barrier(void)
43971ba41c9SSebastian Ott {
44071ba41c9SSebastian Ott 	if (static_branch_likely(&have_mio))
44171ba41c9SSebastian Ott 		__pciwb_mio();
44271ba41c9SSebastian Ott }
44371ba41c9SSebastian Ott EXPORT_SYMBOL_GPL(zpci_barrier);
444