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 { |
| 19 | u8 cc; |
| 20 | u8 status; |
| 21 | u64 req; |
| 22 | u64 offset; |
| 23 | } data = {cc, status, req, offset}; |
| 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 */ |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +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; |
Sebastian Ott | b170bad | 2013-04-16 14:17:15 +0200 | [diff] [blame] | 119 | if (!cc) |
| 120 | *data = __data; |
| 121 | |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 122 | return cc; |
| 123 | } |
| 124 | |
Martin Schwidefsky | 9389339 | 2013-06-25 14:52:23 +0200 | [diff] [blame] | 125 | int zpci_load(u64 *data, u64 req, u64 offset) |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 126 | { |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 127 | u8 status; |
| 128 | int cc; |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 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 | |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 136 | if (cc) |
Sebastian Ott | 3d8258e | 2015-08-18 19:39:27 +0200 | [diff] [blame^] | 137 | zpci_err_insn(cc, status, req, offset); |
| 138 | |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 139 | return (cc > 0) ? -EIO : cc; |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 140 | } |
Martin Schwidefsky | 9389339 | 2013-06-25 14:52:23 +0200 | [diff] [blame] | 141 | EXPORT_SYMBOL_GPL(zpci_load); |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 142 | |
| 143 | /* PCI Store */ |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 144 | static inline int __pcistg(u64 data, u64 req, u64 offset, u8 *status) |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 145 | { |
| 146 | register u64 __req asm("2") = req; |
| 147 | register u64 __offset asm("3") = offset; |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 148 | int cc = -ENXIO; |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 149 | |
| 150 | asm volatile ( |
| 151 | " .insn rre,0xb9d00000,%[data],%[req]\n" |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 152 | "0: ipm %[cc]\n" |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 153 | " srl %[cc],28\n" |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 154 | "1:\n" |
| 155 | EX_TABLE(0b, 1b) |
| 156 | : [cc] "+d" (cc), [req] "+d" (__req) |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 157 | : "d" (__offset), [data] "d" (data) |
| 158 | : "cc"); |
| 159 | *status = __req >> 24 & 0xff; |
| 160 | return cc; |
| 161 | } |
| 162 | |
Martin Schwidefsky | 9389339 | 2013-06-25 14:52:23 +0200 | [diff] [blame] | 163 | int zpci_store(u64 data, u64 req, u64 offset) |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 164 | { |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 165 | u8 status; |
| 166 | int cc; |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 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) |
Sebastian Ott | 3d8258e | 2015-08-18 19:39:27 +0200 | [diff] [blame^] | 175 | zpci_err_insn(cc, status, req, offset); |
| 176 | |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 177 | return (cc > 0) ? -EIO : cc; |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 178 | } |
Martin Schwidefsky | 9389339 | 2013-06-25 14:52:23 +0200 | [diff] [blame] | 179 | EXPORT_SYMBOL_GPL(zpci_store); |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 180 | |
| 181 | /* PCI Store Block */ |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 182 | 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] | 183 | { |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 184 | int cc = -ENXIO; |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 185 | |
| 186 | asm volatile ( |
| 187 | " .insn rsy,0xeb00000000d0,%[req],%[offset],%[data]\n" |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 188 | "0: ipm %[cc]\n" |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 189 | " srl %[cc],28\n" |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 190 | "1:\n" |
| 191 | EX_TABLE(0b, 1b) |
| 192 | : [cc] "+d" (cc), [req] "+d" (req) |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 193 | : [offset] "d" (offset), [data] "Q" (*data) |
| 194 | : "cc"); |
| 195 | *status = req >> 24 & 0xff; |
| 196 | return cc; |
| 197 | } |
| 198 | |
Martin Schwidefsky | 9389339 | 2013-06-25 14:52:23 +0200 | [diff] [blame] | 199 | int zpci_store_block(const u64 *data, u64 req, u64 offset) |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 200 | { |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 201 | u8 status; |
| 202 | int cc; |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 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) |
Sebastian Ott | 3d8258e | 2015-08-18 19:39:27 +0200 | [diff] [blame^] | 211 | zpci_err_insn(cc, status, req, offset); |
| 212 | |
Sebastian Ott | f0bacb7 | 2013-04-16 14:16:14 +0200 | [diff] [blame] | 213 | return (cc > 0) ? -EIO : cc; |
Sebastian Ott | cbcca5d | 2013-04-16 14:14:44 +0200 | [diff] [blame] | 214 | } |
Martin Schwidefsky | 9389339 | 2013-06-25 14:52:23 +0200 | [diff] [blame] | 215 | EXPORT_SYMBOL_GPL(zpci_store_block); |