FILE
pci_insn.c
File preview with syntax highlighting.
linux-6.19.14/arch/s390/pci/pci_insn.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * s390 specific pci instructions 4 * 5 * Copyright IBM Corp. 2013 6 */ 7 8#include <linux/export.h> 9#include <linux/errno.h> 10#include <linux/delay.h> 11#include <linux/jump_label.h> 12#include <asm/asm-extable.h> 13#include <asm/facility.h> 14#include <asm/pci_insn.h> 15#include <asm/pci_debug.h> 16#include <asm/pci_io.h> 17#include <asm/processor.h> 18#include <asm/asm.h> 19 20#define ZPCI_INSN_BUSY_DELAY 1 /* 1 microsecond */ 21 22struct zpci_err_insn_data { 23 u8 insn; 24 u8 cc; 25 u8 status; 26 union { 27 struct { 28 u64 req; 29 u64 offset; 30 }; 31 struct { 32 u64 addr; 33 u64 len; 34 }; 35 }; 36} __packed; 37 38static inline void zpci_err_insn_req(int lvl, u8 insn, u8 cc, u8 status, 39 u64 req, u64 offset) 40{ 41 struct zpci_err_insn_data data = { 42 .insn = insn, .cc = cc, .status = status, 43 .req = req, .offset = offset}; 44 45 zpci_err_hex_level(lvl, &data, sizeof(data)); 46} 47 48static inline void zpci_err_insn_addr(int lvl, u8 insn, u8 cc, u8 status, 49 u64 addr, u64 len) 50{ 51 struct zpci_err_insn_data data = { 52 .insn = insn, .cc = cc, .status = status, 53 .addr = addr, .len = len}; 54 55 zpci_err_hex_level(lvl, &data, sizeof(data)); 56} 57 58/* Modify PCI Function Controls */ 59static inline u8 __mpcifc(u64 req, struct zpci_fib *fib, u8 *status) 60{ 61 int cc; 62 63 asm volatile ( 64 " .insn rxy,0xe300000000d0,%[req],%[fib]\n" 65 CC_IPM(cc) 66 : CC_OUT(cc, cc), [req] "+d" (req), [fib] "+Q" (*fib) 67 : 68 : CC_CLOBBER); 69 *status = req >> 24 & 0xff; 70 return CC_TRANSFORM(cc); 71} 72 73u8 zpci_mod_fc(u64 req, struct zpci_fib *fib, u8 *status) 74{ 75 bool retried = false; 76 u8 cc; 77 78 do { 79 cc = __mpcifc(req, fib, status); 80 if (cc == 2) { 81 msleep(ZPCI_INSN_BUSY_DELAY); 82 if (!retried) { 83 zpci_err_insn_req(1, 'M', cc, *status, req, 0); 84 retried = true; 85 } 86 } 87 } while (cc == 2); 88 89 if (cc) 90 zpci_err_insn_req(0, 'M', cc, *status, req, 0); 91 else if (retried) 92 zpci_err_insn_req(1, 'M', cc, *status, req, 0); 93 94 return cc; 95} 96EXPORT_SYMBOL_GPL(zpci_mod_fc); 97 98/* Refresh PCI Translations */ 99static inline u8 __rpcit(u64 fn, u64 addr, u64 range, u8 *status) 100{ 101 union register_pair addr_range = {.even = addr, .odd = range}; 102 int cc; 103 104 asm volatile ( 105 " .insn rre,0xb9d30000,%[fn],%[addr_range]\n" 106 CC_IPM(cc) 107 : CC_OUT(cc, cc), [fn] "+d" (fn) 108 : [addr_range] "d" (addr_range.pair) 109 : CC_CLOBBER); 110 *status = fn >> 24 & 0xff; 111 return CC_TRANSFORM(cc); 112} 113 114int zpci_refresh_trans(u64 fn, u64 addr, u64 range) 115{ 116 bool retried = false; 117 u8 cc, status; 118 119 do { 120 cc = __rpcit(fn, addr, range, &status); 121 if (cc == 2) { 122 udelay(ZPCI_INSN_BUSY_DELAY); 123 if (!retried) { 124 zpci_err_insn_addr(1, 'R', cc, status, addr, range); 125 retried = true; 126 } 127 } 128 } while (cc == 2); 129 130 if (cc) 131 zpci_err_insn_addr(0, 'R', cc, status, addr, range); 132 else if (retried) 133 zpci_err_insn_addr(1, 'R', cc, status, addr, range); 134 135 if (cc == 1 && (status == 4 || status == 16)) 136 return -ENOMEM; 137 138 return (cc) ? -EIO : 0; 139} 140 141/* Set Interruption Controls */ 142int zpci_set_irq_ctrl(u16 ctl, u8 isc, union zpci_sic_iib *iib) 143{ 144 if (!test_facility(72)) 145 return -EIO; 146 147 asm volatile( 148 ".insn rsy,0xeb00000000d1,%[ctl],%[isc],%[iib]" 149 : : [ctl] "d" (ctl), [isc] "d" (isc << 27), [iib] "Q" (*iib)); 150 151 return 0; 152} 153EXPORT_SYMBOL_GPL(zpci_set_irq_ctrl); 154 155/* PCI Load */ 156static inline int ____pcilg(u64 *data, u64 req, u64 offset, u8 *status) 157{ 158 union register_pair req_off = {.even = req, .odd = offset}; 159 int cc, exception; 160 u64 __data; 161 162 exception = 1; 163 asm_inline volatile ( 164 " .insn rre,0xb9d20000,%[data],%[req_off]\n" 165 "0: lhi %[exc],0\n" 166 "1:\n" 167 CC_IPM(cc) 168 EX_TABLE(0b, 1b) 169 : CC_OUT(cc, cc), [data] "=d" (__data), 170 [req_off] "+d" (req_off.pair), [exc] "+d" (exception) 171 : 172 : CC_CLOBBER); 173 *status = req_off.even >> 24 & 0xff; 174 *data = __data; 175 return exception ? -ENXIO : CC_TRANSFORM(cc); 176} 177 178static inline int __pcilg(u64 *data, u64 req, u64 offset, u8 *status) 179{ 180 u64 __data; 181 int cc; 182 183 cc = ____pcilg(&__data, req, offset, status); 184 if (!cc) 185 *data = __data; 186 187 return cc; 188} 189 190int __zpci_load(u64 *data, u64 req, u64 offset) 191{ 192 bool retried = false; 193 u8 status; 194 int cc; 195 196 do { 197 cc = __pcilg(data, req, offset, &status); 198 if (cc == 2) { 199 udelay(ZPCI_INSN_BUSY_DELAY); 200 if (!retried) { 201 zpci_err_insn_req(1, 'l', cc, status, req, offset); 202 retried = true; 203 } 204 } 205 } while (cc == 2); 206 207 if (cc) 208 zpci_err_insn_req(0, 'l', cc, status, req, offset); 209 else if (retried) 210 zpci_err_insn_req(1, 'l', cc, status, req, offset); 211 212 return (cc > 0) ? -EIO : cc; 213} 214EXPORT_SYMBOL_GPL(__zpci_load); 215 216static inline int zpci_load_fh(u64 *data, const volatile void __iomem *addr, 217 unsigned long len) 218{ 219 struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(addr)]; 220 u64 req = ZPCI_CREATE_REQ(READ_ONCE(entry->fh), entry->bar, len); 221 222 return __zpci_load(data, req, ZPCI_OFFSET(addr)); 223} 224 225static inline int __pcilg_mio(u64 *data, u64 ioaddr, u64 len, u8 *status) 226{ 227 union register_pair ioaddr_len = {.even = ioaddr, .odd = len}; 228 int cc, exception; 229 u64 __data; 230 231 exception = 1; 232 asm_inline volatile ( 233 " .insn rre,0xb9d60000,%[data],%[ioaddr_len]\n" 234 "0: lhi %[exc],0\n" 235 "1:\n" 236 CC_IPM(cc) 237 EX_TABLE(0b, 1b) 238 : CC_OUT(cc, cc), [data] "=d" (__data), 239 [ioaddr_len] "+d" (ioaddr_len.pair), [exc] "+d" (exception) 240 : 241 : CC_CLOBBER); 242 *status = ioaddr_len.odd >> 24 & 0xff; 243 *data = __data; 244 return exception ? -ENXIO : CC_TRANSFORM(cc); 245} 246 247int zpci_load(u64 *data, const volatile void __iomem *addr, unsigned long len) 248{ 249 u8 status; 250 int cc; 251 252 if (!static_branch_unlikely(&have_mio)) 253 return zpci_load_fh(data, addr, len); 254 255 cc = __pcilg_mio(data, (__force u64) addr, len, &status); 256 if (cc) 257 zpci_err_insn_addr(0, 'L', cc, status, (__force u64) addr, len); 258 259 return (cc > 0) ? -EIO : cc; 260} 261EXPORT_SYMBOL_GPL(zpci_load); 262 263/* PCI Store */ 264static inline int __pcistg(u64 data, u64 req, u64 offset, u8 *status) 265{ 266 union register_pair req_off = {.even = req, .odd = offset}; 267 int cc, exception; 268 269 exception = 1; 270 asm_inline volatile ( 271 " .insn rre,0xb9d00000,%[data],%[req_off]\n" 272 "0: lhi %[exc],0\n" 273 "1:\n" 274 CC_IPM(cc) 275 EX_TABLE(0b, 1b) 276 : CC_OUT(cc, cc), [req_off] "+d" (req_off.pair), [exc] "+d" (exception) 277 : [data] "d" (data) 278 : CC_CLOBBER); 279 *status = req_off.even >> 24 & 0xff; 280 return exception ? -ENXIO : CC_TRANSFORM(cc); 281} 282 283int __zpci_store(u64 data, u64 req, u64 offset) 284{ 285 bool retried = false; 286 u8 status; 287 int cc; 288 289 do { 290 cc = __pcistg(data, req, offset, &status); 291 if (cc == 2) { 292 udelay(ZPCI_INSN_BUSY_DELAY); 293 if (!retried) { 294 zpci_err_insn_req(1, 's', cc, status, req, offset); 295 retried = true; 296 } 297 } 298 } while (cc == 2); 299 300 if (cc) 301 zpci_err_insn_req(0, 's', cc, status, req, offset); 302 else if (retried) 303 zpci_err_insn_req(1, 's', cc, status, req, offset); 304 305 return (cc > 0) ? -EIO : cc; 306} 307EXPORT_SYMBOL_GPL(__zpci_store); 308 309static inline int zpci_store_fh(const volatile void __iomem *addr, u64 data, 310 unsigned long len) 311{ 312 struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(addr)]; 313 u64 req = ZPCI_CREATE_REQ(READ_ONCE(entry->fh), entry->bar, len); 314 315 return __zpci_store(data, req, ZPCI_OFFSET(addr)); 316} 317 318static inline int __pcistg_mio(u64 data, u64 ioaddr, u64 len, u8 *status) 319{ 320 union register_pair ioaddr_len = {.even = ioaddr, .odd = len}; 321 int cc, exception; 322 323 exception = 1; 324 asm_inline volatile ( 325 " .insn rre,0xb9d40000,%[data],%[ioaddr_len]\n" 326 "0: lhi %[exc],0\n" 327 "1:\n" 328 CC_IPM(cc) 329 EX_TABLE(0b, 1b) 330 : CC_OUT(cc, cc), [ioaddr_len] "+d" (ioaddr_len.pair), [exc] "+d" (exception) 331 : [data] "d" (data) 332 : CC_CLOBBER_LIST("memory")); 333 *status = ioaddr_len.odd >> 24 & 0xff; 334 return exception ? -ENXIO : CC_TRANSFORM(cc); 335} 336 337int zpci_store(const volatile void __iomem *addr, u64 data, unsigned long len) 338{ 339 u8 status; 340 int cc; 341 342 if (!static_branch_unlikely(&have_mio)) 343 return zpci_store_fh(addr, data, len); 344 345 cc = __pcistg_mio(data, (__force u64) addr, len, &status); 346 if (cc) 347 zpci_err_insn_addr(0, 'S', cc, status, (__force u64) addr, len); 348 349 return (cc > 0) ? -EIO : cc; 350} 351EXPORT_SYMBOL_GPL(zpci_store); 352 353/* PCI Store Block */ 354static inline int __pcistb(const u64 *data, u64 req, u64 offset, u8 *status) 355{ 356 int cc, exception; 357 358 exception = 1; 359 asm_inline volatile ( 360 " .insn rsy,0xeb00000000d0,%[req],%[offset],%[data]\n" 361 "0: lhi %[exc],0\n" 362 "1:\n" 363 CC_IPM(cc) 364 EX_TABLE(0b, 1b) 365 : CC_OUT(cc, cc), [req] "+d" (req), [exc] "+d" (exception) 366 : [offset] "d" (offset), [data] "Q" (*data) 367 : CC_CLOBBER); 368 *status = req >> 24 & 0xff; 369 return exception ? -ENXIO : CC_TRANSFORM(cc); 370} 371 372int __zpci_store_block(const u64 *data, u64 req, u64 offset) 373{ 374 bool retried = false; 375 u8 status; 376 int cc; 377 378 do { 379 cc = __pcistb(data, req, offset, &status); 380 if (cc == 2) { 381 udelay(ZPCI_INSN_BUSY_DELAY); 382 if (!retried) { 383 zpci_err_insn_req(0, 'b', cc, status, req, offset); 384 retried = true; 385 } 386 } 387 } while (cc == 2); 388 389 if (cc) 390 zpci_err_insn_req(0, 'b', cc, status, req, offset); 391 else if (retried) 392 zpci_err_insn_req(1, 'b', cc, status, req, offset); 393 394 return (cc > 0) ? -EIO : cc; 395} 396EXPORT_SYMBOL_GPL(__zpci_store_block); 397 398static inline int zpci_write_block_fh(volatile void __iomem *dst, 399 const void *src, unsigned long len) 400{ 401 struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(dst)]; 402 u64 req = ZPCI_CREATE_REQ(entry->fh, entry->bar, len); 403 u64 offset = ZPCI_OFFSET(dst); 404 405 return __zpci_store_block(src, req, offset); 406} 407 408static inline int __pcistb_mio(const u64 *data, u64 ioaddr, u64 len, u8 *status) 409{ 410 int cc, exception; 411 412 exception = 1; 413 asm_inline volatile ( 414 " .insn rsy,0xeb00000000d4,%[len],%[ioaddr],%[data]\n" 415 "0: lhi %[exc],0\n" 416 "1:\n" 417 CC_IPM(cc) 418 EX_TABLE(0b, 1b) 419 : CC_OUT(cc, cc), [len] "+d" (len), [exc] "+d" (exception) 420 : [ioaddr] "d" (ioaddr), [data] "Q" (*data) 421 : CC_CLOBBER); 422 *status = len >> 24 & 0xff; 423 return exception ? -ENXIO : CC_TRANSFORM(cc); 424} 425 426int zpci_write_block(volatile void __iomem *dst, 427 const void *src, unsigned long len) 428{ 429 u8 status; 430 int cc; 431 432 if (!static_branch_unlikely(&have_mio)) 433 return zpci_write_block_fh(dst, src, len); 434 435 cc = __pcistb_mio(src, (__force u64) dst, len, &status); 436 if (cc) 437 zpci_err_insn_addr(0, 'B', cc, status, (__force u64) dst, len); 438 439 return (cc > 0) ? -EIO : cc; 440} 441EXPORT_SYMBOL_GPL(zpci_write_block); 442 443static inline void __pciwb_mio(void) 444{ 445 asm volatile (".insn rre,0xb9d50000,0,0"); 446} 447 448void zpci_barrier(void) 449{ 450 if (static_branch_likely(&have_mio)) 451 __pciwb_mio(); 452} 453EXPORT_SYMBOL_GPL(zpci_barrier);
~/
Read-only file preview.