Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 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> |
Sebastian Ott | 3d8258e | 2015-08-18 19:39:27 +0200 | [diff] [blame] | 11 | #include <asm/pci_debug.h> |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 12 | #include <asm/processor.h> |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 13 | |
| 14 | #define ZPCI_INSN_BUSY_DELAY 1 /* 1 microsecond */ |
| 15 | |
Sebastian Ott | 3d8258e | 2015-08-18 19:39:27 +0200 | [diff] [blame] | 16 | static inline void zpci_err_insn(u8 cc, u8 status, u64 req, u64 offset) |
| 17 | { |
| 18 | struct { |
Sebastian Ott | 3d8258e | 2015-08-18 19:39:27 +0200 | [diff] [blame] | 19 | u64 req; |
| 20 | u64 offset; |
Sebastian Ott | 7cc8944 | 2015-10-09 11:07:06 +0200 | [diff] [blame] | 21 | u8 cc; |
| 22 | u8 status; |
| 23 | } __packed data = {req, offset, cc, status}; |
Sebastian Ott | 3d8258e | 2015-08-18 19:39:27 +0200 | [diff] [blame] | 24 | |
| 25 | zpci_err_hex(&data, sizeof(data)); |
| 26 | } |
| 27 | |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 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 | |
Martin Schwidefsky | 9389339 | 2013-06-25 14:52:23 +0200 | [diff] [blame] | 43 | int zpci_mod_fc(u64 req, struct zpci_fib *fib) |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 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) |
Sebastian Ott | 3d8258e | 2015-08-18 19:39:27 +0200 | [diff] [blame] | 54 | zpci_err_insn(cc, status, req, 0); |
| 55 | |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 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 | |
Martin Schwidefsky | 9389339 | 2013-06-25 14:52:23 +0200 | [diff] [blame] | 77 | int zpci_refresh_trans(u64 fn, u64 addr, u64 range) |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 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) |
Sebastian Ott | 3d8258e | 2015-08-18 19:39:27 +0200 | [diff] [blame] | 88 | zpci_err_insn(cc, status, addr, range); |
| 89 | |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 90 | return (cc) ? -EIO : 0; |
| 91 | } |
| 92 | |
| 93 | /* Set Interruption Controls */ |
Martin Schwidefsky | 9389339 | 2013-06-25 14:52:23 +0200 | [diff] [blame] | 94 | void zpci_set_irq_ctrl(u16 ctl, char *unused, u8 isc) |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 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 */ |
Heiko Carstens | 7b411ac | 2016-06-20 14:03:25 +0200 | [diff] [blame] | 102 | static inline int ____pcilg(u64 *data, u64 req, u64 offset, u8 *status) |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 103 | { |
| 104 | register u64 __req asm("2") = req; |
| 105 | register u64 __offset asm("3") = offset; |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 106 | int cc = -ENXIO; |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 107 | u64 __data; |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 108 | |
| 109 | asm volatile ( |
| 110 | " .insn rre,0xb9d20000,%[data],%[req]\n" |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 111 | "0: ipm %[cc]\n" |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 112 | " srl %[cc],28\n" |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 113 | "1:\n" |
| 114 | EX_TABLE(0b, 1b) |
| 115 | : [cc] "+d" (cc), [data] "=d" (__data), [req] "+d" (__req) |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 116 | : "d" (__offset) |
| 117 | : "cc"); |
| 118 | *status = __req >> 24 & 0xff; |
Heiko Carstens | 7b411ac | 2016-06-20 14:03:25 +0200 | [diff] [blame] | 119 | *data = __data; |
| 120 | return cc; |
| 121 | } |
| 122 | |
| 123 | static inline int __pcilg(u64 *data, u64 req, u64 offset, u8 *status) |
| 124 | { |
| 125 | u64 __data; |
| 126 | int cc; |
| 127 | |
| 128 | cc = ____pcilg(&__data, req, offset, status); |
Sebastian Ott | b170bad | 2013-04-16 14:17:15 +0200 | [diff] [blame] | 129 | if (!cc) |
| 130 | *data = __data; |
| 131 | |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 132 | return cc; |
| 133 | } |
| 134 | |
Martin Schwidefsky | 9389339 | 2013-06-25 14:52:23 +0200 | [diff] [blame] | 135 | int zpci_load(u64 *data, u64 req, u64 offset) |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 136 | { |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 137 | u8 status; |
| 138 | int cc; |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 139 | |
| 140 | do { |
| 141 | cc = __pcilg(data, req, offset, &status); |
| 142 | if (cc == 2) |
| 143 | udelay(ZPCI_INSN_BUSY_DELAY); |
| 144 | } while (cc == 2); |
| 145 | |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 146 | if (cc) |
Sebastian Ott | 3d8258e | 2015-08-18 19:39:27 +0200 | [diff] [blame] | 147 | zpci_err_insn(cc, status, req, offset); |
| 148 | |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 149 | return (cc > 0) ? -EIO : cc; |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 150 | } |
Martin Schwidefsky | 9389339 | 2013-06-25 14:52:23 +0200 | [diff] [blame] | 151 | EXPORT_SYMBOL_GPL(zpci_load); |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 152 | |
| 153 | /* PCI Store */ |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 154 | static inline int __pcistg(u64 data, u64 req, u64 offset, u8 *status) |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 155 | { |
| 156 | register u64 __req asm("2") = req; |
| 157 | register u64 __offset asm("3") = offset; |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 158 | int cc = -ENXIO; |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 159 | |
| 160 | asm volatile ( |
| 161 | " .insn rre,0xb9d00000,%[data],%[req]\n" |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 162 | "0: ipm %[cc]\n" |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 163 | " srl %[cc],28\n" |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 164 | "1:\n" |
| 165 | EX_TABLE(0b, 1b) |
| 166 | : [cc] "+d" (cc), [req] "+d" (__req) |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 167 | : "d" (__offset), [data] "d" (data) |
| 168 | : "cc"); |
| 169 | *status = __req >> 24 & 0xff; |
| 170 | return cc; |
| 171 | } |
| 172 | |
Martin Schwidefsky | 9389339 | 2013-06-25 14:52:23 +0200 | [diff] [blame] | 173 | int zpci_store(u64 data, u64 req, u64 offset) |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 174 | { |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 175 | u8 status; |
| 176 | int cc; |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 177 | |
| 178 | do { |
| 179 | cc = __pcistg(data, req, offset, &status); |
| 180 | if (cc == 2) |
| 181 | udelay(ZPCI_INSN_BUSY_DELAY); |
| 182 | } while (cc == 2); |
| 183 | |
| 184 | if (cc) |
Sebastian Ott | 3d8258e | 2015-08-18 19:39:27 +0200 | [diff] [blame] | 185 | zpci_err_insn(cc, status, req, offset); |
| 186 | |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 187 | return (cc > 0) ? -EIO : cc; |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 188 | } |
Martin Schwidefsky | 9389339 | 2013-06-25 14:52:23 +0200 | [diff] [blame] | 189 | EXPORT_SYMBOL_GPL(zpci_store); |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 190 | |
| 191 | /* PCI Store Block */ |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 192 | static inline int __pcistb(const u64 *data, u64 req, u64 offset, u8 *status) |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 193 | { |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 194 | int cc = -ENXIO; |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 195 | |
| 196 | asm volatile ( |
| 197 | " .insn rsy,0xeb00000000d0,%[req],%[offset],%[data]\n" |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 198 | "0: ipm %[cc]\n" |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 199 | " srl %[cc],28\n" |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 200 | "1:\n" |
| 201 | EX_TABLE(0b, 1b) |
| 202 | : [cc] "+d" (cc), [req] "+d" (req) |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 203 | : [offset] "d" (offset), [data] "Q" (*data) |
| 204 | : "cc"); |
| 205 | *status = req >> 24 & 0xff; |
| 206 | return cc; |
| 207 | } |
| 208 | |
Martin Schwidefsky | 9389339 | 2013-06-25 14:52:23 +0200 | [diff] [blame] | 209 | int zpci_store_block(const u64 *data, u64 req, u64 offset) |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 210 | { |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 211 | u8 status; |
| 212 | int cc; |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 213 | |
| 214 | do { |
| 215 | cc = __pcistb(data, req, offset, &status); |
| 216 | if (cc == 2) |
| 217 | udelay(ZPCI_INSN_BUSY_DELAY); |
| 218 | } while (cc == 2); |
| 219 | |
| 220 | if (cc) |
Sebastian Ott | 3d8258e | 2015-08-18 19:39:27 +0200 | [diff] [blame] | 221 | zpci_err_insn(cc, status, req, offset); |
| 222 | |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 223 | return (cc > 0) ? -EIO : cc; |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 224 | } |
Martin Schwidefsky | 9389339 | 2013-06-25 14:52:23 +0200 | [diff] [blame] | 225 | EXPORT_SYMBOL_GPL(zpci_store_block); |