Commit | Line | Data |
---|---|---|
cbcca5d0 SO |
1 | /* |
2 | * s390 specific pci instructions | |
3 | * | |
4 | * Copyright IBM Corp. 2013 | |
5 | */ | |
6 | ||
7 | #include <linux/export.h> | |
8 | #include <linux/errno.h> | |
9 | #include <linux/delay.h> | |
10 | #include <asm/pci_insn.h> | |
3d8258e4 | 11 | #include <asm/pci_debug.h> |
f0bacb7f | 12 | #include <asm/processor.h> |
cbcca5d0 SO |
13 | |
14 | #define ZPCI_INSN_BUSY_DELAY 1 /* 1 microsecond */ | |
15 | ||
3d8258e4 SO |
16 | static inline void zpci_err_insn(u8 cc, u8 status, u64 req, u64 offset) |
17 | { | |
18 | struct { | |
3d8258e4 SO |
19 | u64 req; |
20 | u64 offset; | |
7cc8944e SO |
21 | u8 cc; |
22 | u8 status; | |
23 | } __packed data = {req, offset, cc, status}; | |
3d8258e4 SO |
24 | |
25 | zpci_err_hex(&data, sizeof(data)); | |
26 | } | |
27 | ||
cbcca5d0 SO |
28 | /* Modify PCI Function Controls */ |
29 | static inline u8 __mpcifc(u64 req, struct zpci_fib *fib, u8 *status) | |
30 | { | |
31 | u8 cc; | |
32 | ||
33 | asm volatile ( | |
34 | " .insn rxy,0xe300000000d0,%[req],%[fib]\n" | |
35 | " ipm %[cc]\n" | |
36 | " srl %[cc],28\n" | |
37 | : [cc] "=d" (cc), [req] "+d" (req), [fib] "+Q" (*fib) | |
38 | : : "cc"); | |
39 | *status = req >> 24 & 0xff; | |
40 | return cc; | |
41 | } | |
42 | ||
9389339f | 43 | int zpci_mod_fc(u64 req, struct zpci_fib *fib) |
cbcca5d0 SO |
44 | { |
45 | u8 cc, status; | |
46 | ||
47 | do { | |
48 | cc = __mpcifc(req, fib, &status); | |
49 | if (cc == 2) | |
50 | msleep(ZPCI_INSN_BUSY_DELAY); | |
51 | } while (cc == 2); | |
52 | ||
53 | if (cc) | |
3d8258e4 SO |
54 | zpci_err_insn(cc, status, req, 0); |
55 | ||
cbcca5d0 SO |
56 | return (cc) ? -EIO : 0; |
57 | } | |
58 | ||
59 | /* Refresh PCI Translations */ | |
60 | static inline u8 __rpcit(u64 fn, u64 addr, u64 range, u8 *status) | |
61 | { | |
62 | register u64 __addr asm("2") = addr; | |
63 | register u64 __range asm("3") = range; | |
64 | u8 cc; | |
65 | ||
66 | asm volatile ( | |
67 | " .insn rre,0xb9d30000,%[fn],%[addr]\n" | |
68 | " ipm %[cc]\n" | |
69 | " srl %[cc],28\n" | |
70 | : [cc] "=d" (cc), [fn] "+d" (fn) | |
71 | : [addr] "d" (__addr), "d" (__range) | |
72 | : "cc"); | |
73 | *status = fn >> 24 & 0xff; | |
74 | return cc; | |
75 | } | |
76 | ||
9389339f | 77 | int zpci_refresh_trans(u64 fn, u64 addr, u64 range) |
cbcca5d0 SO |
78 | { |
79 | u8 cc, status; | |
80 | ||
81 | do { | |
82 | cc = __rpcit(fn, addr, range, &status); | |
83 | if (cc == 2) | |
84 | udelay(ZPCI_INSN_BUSY_DELAY); | |
85 | } while (cc == 2); | |
86 | ||
87 | if (cc) | |
3d8258e4 SO |
88 | zpci_err_insn(cc, status, addr, range); |
89 | ||
cbcca5d0 SO |
90 | return (cc) ? -EIO : 0; |
91 | } | |
92 | ||
93 | /* Set Interruption Controls */ | |
9389339f | 94 | void zpci_set_irq_ctrl(u16 ctl, char *unused, u8 isc) |
cbcca5d0 SO |
95 | { |
96 | asm volatile ( | |
97 | " .insn rsy,0xeb00000000d1,%[ctl],%[isc],%[u]\n" | |
98 | : : [ctl] "d" (ctl), [isc] "d" (isc << 27), [u] "Q" (*unused)); | |
99 | } | |
100 | ||
101 | /* PCI Load */ | |
f0bacb7f | 102 | static inline int __pcilg(u64 *data, u64 req, u64 offset, u8 *status) |
cbcca5d0 SO |
103 | { |
104 | register u64 __req asm("2") = req; | |
105 | register u64 __offset asm("3") = offset; | |
f0bacb7f | 106 | int cc = -ENXIO; |
cbcca5d0 | 107 | u64 __data; |
cbcca5d0 SO |
108 | |
109 | asm volatile ( | |
110 | " .insn rre,0xb9d20000,%[data],%[req]\n" | |
f0bacb7f | 111 | "0: ipm %[cc]\n" |
cbcca5d0 | 112 | " srl %[cc],28\n" |
f0bacb7f SO |
113 | "1:\n" |
114 | EX_TABLE(0b, 1b) | |
115 | : [cc] "+d" (cc), [data] "=d" (__data), [req] "+d" (__req) | |
cbcca5d0 SO |
116 | : "d" (__offset) |
117 | : "cc"); | |
118 | *status = __req >> 24 & 0xff; | |
b170bad4 SO |
119 | if (!cc) |
120 | *data = __data; | |
121 | ||
cbcca5d0 SO |
122 | return cc; |
123 | } | |
124 | ||
9389339f | 125 | int zpci_load(u64 *data, u64 req, u64 offset) |
cbcca5d0 | 126 | { |
f0bacb7f SO |
127 | u8 status; |
128 | int cc; | |
cbcca5d0 SO |
129 | |
130 | do { | |
131 | cc = __pcilg(data, req, offset, &status); | |
132 | if (cc == 2) | |
133 | udelay(ZPCI_INSN_BUSY_DELAY); | |
134 | } while (cc == 2); | |
135 | ||
f0bacb7f | 136 | if (cc) |
3d8258e4 SO |
137 | zpci_err_insn(cc, status, req, offset); |
138 | ||
f0bacb7f | 139 | return (cc > 0) ? -EIO : cc; |
cbcca5d0 | 140 | } |
9389339f | 141 | EXPORT_SYMBOL_GPL(zpci_load); |
cbcca5d0 SO |
142 | |
143 | /* PCI Store */ | |
f0bacb7f | 144 | static inline int __pcistg(u64 data, u64 req, u64 offset, u8 *status) |
cbcca5d0 SO |
145 | { |
146 | register u64 __req asm("2") = req; | |
147 | register u64 __offset asm("3") = offset; | |
f0bacb7f | 148 | int cc = -ENXIO; |
cbcca5d0 SO |
149 | |
150 | asm volatile ( | |
151 | " .insn rre,0xb9d00000,%[data],%[req]\n" | |
f0bacb7f | 152 | "0: ipm %[cc]\n" |
cbcca5d0 | 153 | " srl %[cc],28\n" |
f0bacb7f SO |
154 | "1:\n" |
155 | EX_TABLE(0b, 1b) | |
156 | : [cc] "+d" (cc), [req] "+d" (__req) | |
cbcca5d0 SO |
157 | : "d" (__offset), [data] "d" (data) |
158 | : "cc"); | |
159 | *status = __req >> 24 & 0xff; | |
160 | return cc; | |
161 | } | |
162 | ||
9389339f | 163 | int zpci_store(u64 data, u64 req, u64 offset) |
cbcca5d0 | 164 | { |
f0bacb7f SO |
165 | u8 status; |
166 | int cc; | |
cbcca5d0 SO |
167 | |
168 | do { | |
169 | cc = __pcistg(data, req, offset, &status); | |
170 | if (cc == 2) | |
171 | udelay(ZPCI_INSN_BUSY_DELAY); | |
172 | } while (cc == 2); | |
173 | ||
174 | if (cc) | |
3d8258e4 SO |
175 | zpci_err_insn(cc, status, req, offset); |
176 | ||
f0bacb7f | 177 | return (cc > 0) ? -EIO : cc; |
cbcca5d0 | 178 | } |
9389339f | 179 | EXPORT_SYMBOL_GPL(zpci_store); |
cbcca5d0 SO |
180 | |
181 | /* PCI Store Block */ | |
f0bacb7f | 182 | static inline int __pcistb(const u64 *data, u64 req, u64 offset, u8 *status) |
cbcca5d0 | 183 | { |
f0bacb7f | 184 | int cc = -ENXIO; |
cbcca5d0 SO |
185 | |
186 | asm volatile ( | |
187 | " .insn rsy,0xeb00000000d0,%[req],%[offset],%[data]\n" | |
f0bacb7f | 188 | "0: ipm %[cc]\n" |
cbcca5d0 | 189 | " srl %[cc],28\n" |
f0bacb7f SO |
190 | "1:\n" |
191 | EX_TABLE(0b, 1b) | |
192 | : [cc] "+d" (cc), [req] "+d" (req) | |
cbcca5d0 SO |
193 | : [offset] "d" (offset), [data] "Q" (*data) |
194 | : "cc"); | |
195 | *status = req >> 24 & 0xff; | |
196 | return cc; | |
197 | } | |
198 | ||
9389339f | 199 | int zpci_store_block(const u64 *data, u64 req, u64 offset) |
cbcca5d0 | 200 | { |
f0bacb7f SO |
201 | u8 status; |
202 | int cc; | |
cbcca5d0 SO |
203 | |
204 | do { | |
205 | cc = __pcistb(data, req, offset, &status); | |
206 | if (cc == 2) | |
207 | udelay(ZPCI_INSN_BUSY_DELAY); | |
208 | } while (cc == 2); | |
209 | ||
210 | if (cc) | |
3d8258e4 SO |
211 | zpci_err_insn(cc, status, req, offset); |
212 | ||
f0bacb7f | 213 | return (cc > 0) ? -EIO : cc; |
cbcca5d0 | 214 | } |
9389339f | 215 | EXPORT_SYMBOL_GPL(zpci_store_block); |