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