Commit | Line | Data |
---|---|---|
83262d63 PO |
1 | /* |
2 | * Functions for assembling fcx enabled I/O control blocks. | |
3 | * | |
4 | * Copyright IBM Corp. 2008 | |
5 | * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com> | |
6 | */ | |
7 | ||
8 | #include <linux/kernel.h> | |
9 | #include <linux/types.h> | |
10 | #include <linux/string.h> | |
11 | #include <linux/errno.h> | |
12 | #include <linux/err.h> | |
13 | #include <linux/module.h> | |
14 | #include <asm/fcx.h> | |
15 | #include "cio.h" | |
16 | ||
17 | /** | |
18 | * tcw_get_intrg - return pointer to associated interrogate tcw | |
19 | * @tcw: pointer to the original tcw | |
20 | * | |
21 | * Return a pointer to the interrogate tcw associated with the specified tcw | |
22 | * or %NULL if there is no associated interrogate tcw. | |
23 | */ | |
24 | struct tcw *tcw_get_intrg(struct tcw *tcw) | |
25 | { | |
26 | return (struct tcw *) ((addr_t) tcw->intrg); | |
27 | } | |
28 | EXPORT_SYMBOL(tcw_get_intrg); | |
29 | ||
30 | /** | |
31 | * tcw_get_data - return pointer to input/output data associated with tcw | |
32 | * @tcw: pointer to the tcw | |
33 | * | |
34 | * Return the input or output data address specified in the tcw depending | |
35 | * on whether the r-bit or the w-bit is set. If neither bit is set, return | |
36 | * %NULL. | |
37 | */ | |
38 | void *tcw_get_data(struct tcw *tcw) | |
39 | { | |
40 | if (tcw->r) | |
41 | return (void *) ((addr_t) tcw->input); | |
42 | if (tcw->w) | |
43 | return (void *) ((addr_t) tcw->output); | |
44 | return NULL; | |
45 | } | |
46 | EXPORT_SYMBOL(tcw_get_data); | |
47 | ||
48 | /** | |
49 | * tcw_get_tccb - return pointer to tccb associated with tcw | |
50 | * @tcw: pointer to the tcw | |
51 | * | |
52 | * Return pointer to the tccb associated with this tcw. | |
53 | */ | |
54 | struct tccb *tcw_get_tccb(struct tcw *tcw) | |
55 | { | |
56 | return (struct tccb *) ((addr_t) tcw->tccb); | |
57 | } | |
58 | EXPORT_SYMBOL(tcw_get_tccb); | |
59 | ||
60 | /** | |
61 | * tcw_get_tsb - return pointer to tsb associated with tcw | |
62 | * @tcw: pointer to the tcw | |
63 | * | |
64 | * Return pointer to the tsb associated with this tcw. | |
65 | */ | |
66 | struct tsb *tcw_get_tsb(struct tcw *tcw) | |
67 | { | |
68 | return (struct tsb *) ((addr_t) tcw->tsb); | |
69 | } | |
70 | EXPORT_SYMBOL(tcw_get_tsb); | |
71 | ||
72 | /** | |
73 | * tcw_init - initialize tcw data structure | |
74 | * @tcw: pointer to the tcw to be initialized | |
75 | * @r: initial value of the r-bit | |
76 | * @w: initial value of the w-bit | |
77 | * | |
78 | * Initialize all fields of the specified tcw data structure with zero and | |
79 | * fill in the format, flags, r and w fields. | |
80 | */ | |
81 | void tcw_init(struct tcw *tcw, int r, int w) | |
82 | { | |
83 | memset(tcw, 0, sizeof(struct tcw)); | |
84 | tcw->format = TCW_FORMAT_DEFAULT; | |
85 | tcw->flags = TCW_FLAGS_TIDAW_FORMAT(TCW_TIDAW_FORMAT_DEFAULT); | |
86 | if (r) | |
87 | tcw->r = 1; | |
88 | if (w) | |
89 | tcw->w = 1; | |
90 | } | |
91 | EXPORT_SYMBOL(tcw_init); | |
92 | ||
93 | static inline size_t tca_size(struct tccb *tccb) | |
94 | { | |
95 | return tccb->tcah.tcal - 12; | |
96 | } | |
97 | ||
98 | static u32 calc_dcw_count(struct tccb *tccb) | |
99 | { | |
100 | int offset; | |
101 | struct dcw *dcw; | |
102 | u32 count = 0; | |
103 | size_t size; | |
104 | ||
105 | size = tca_size(tccb); | |
106 | for (offset = 0; offset < size;) { | |
107 | dcw = (struct dcw *) &tccb->tca[offset]; | |
108 | count += dcw->count; | |
109 | if (!(dcw->flags & DCW_FLAGS_CC)) | |
110 | break; | |
111 | offset += sizeof(struct dcw) + ALIGN((int) dcw->cd_count, 4); | |
112 | } | |
113 | return count; | |
114 | } | |
115 | ||
116 | static u32 calc_cbc_size(struct tidaw *tidaw, int num) | |
117 | { | |
118 | int i; | |
119 | u32 cbc_data; | |
120 | u32 cbc_count = 0; | |
121 | u64 data_count = 0; | |
122 | ||
123 | for (i = 0; i < num; i++) { | |
124 | if (tidaw[i].flags & TIDAW_FLAGS_LAST) | |
125 | break; | |
126 | /* TODO: find out if padding applies to total of data | |
127 | * transferred or data transferred by this tidaw. Assumption: | |
128 | * applies to total. */ | |
129 | data_count += tidaw[i].count; | |
130 | if (tidaw[i].flags & TIDAW_FLAGS_INSERT_CBC) { | |
131 | cbc_data = 4 + ALIGN(data_count, 4) - data_count; | |
132 | cbc_count += cbc_data; | |
133 | data_count += cbc_data; | |
134 | } | |
135 | } | |
136 | return cbc_count; | |
137 | } | |
138 | ||
139 | /** | |
140 | * tcw_finalize - finalize tcw length fields and tidaw list | |
141 | * @tcw: pointer to the tcw | |
142 | * @num_tidaws: the number of tidaws used to address input/output data or zero | |
143 | * if no tida is used | |
144 | * | |
145 | * Calculate the input-/output-count and tccbl field in the tcw, add a | |
146 | * tcat the tccb and terminate the data tidaw list if used. | |
147 | * | |
148 | * Note: in case input- or output-tida is used, the tidaw-list must be stored | |
149 | * in contiguous storage (no ttic). The tcal field in the tccb must be | |
150 | * up-to-date. | |
151 | */ | |
152 | void tcw_finalize(struct tcw *tcw, int num_tidaws) | |
153 | { | |
154 | struct tidaw *tidaw; | |
155 | struct tccb *tccb; | |
156 | struct tccb_tcat *tcat; | |
157 | u32 count; | |
158 | ||
159 | /* Terminate tidaw list. */ | |
160 | tidaw = tcw_get_data(tcw); | |
161 | if (num_tidaws > 0) | |
162 | tidaw[num_tidaws - 1].flags |= TIDAW_FLAGS_LAST; | |
163 | /* Add tcat to tccb. */ | |
164 | tccb = tcw_get_tccb(tcw); | |
165 | tcat = (struct tccb_tcat *) &tccb->tca[tca_size(tccb)]; | |
166 | memset(tcat, 0, sizeof(tcat)); | |
167 | /* Calculate tcw input/output count and tcat transport count. */ | |
168 | count = calc_dcw_count(tccb); | |
169 | if (tcw->w && (tcw->flags & TCW_FLAGS_OUTPUT_TIDA)) | |
170 | count += calc_cbc_size(tidaw, num_tidaws); | |
171 | if (tcw->r) | |
172 | tcw->input_count = count; | |
173 | else if (tcw->w) | |
174 | tcw->output_count = count; | |
175 | tcat->count = ALIGN(count, 4) + 4; | |
176 | /* Calculate tccbl. */ | |
177 | tcw->tccbl = (sizeof(struct tccb) + tca_size(tccb) + | |
178 | sizeof(struct tccb_tcat) - 20) >> 2; | |
179 | } | |
180 | EXPORT_SYMBOL(tcw_finalize); | |
181 | ||
182 | /** | |
183 | * tcw_set_intrg - set the interrogate tcw address of a tcw | |
184 | * @tcw: the tcw address | |
185 | * @intrg_tcw: the address of the interrogate tcw | |
186 | * | |
187 | * Set the address of the interrogate tcw in the specified tcw. | |
188 | */ | |
189 | void tcw_set_intrg(struct tcw *tcw, struct tcw *intrg_tcw) | |
190 | { | |
191 | tcw->intrg = (u32) ((addr_t) intrg_tcw); | |
192 | } | |
193 | EXPORT_SYMBOL(tcw_set_intrg); | |
194 | ||
195 | /** | |
196 | * tcw_set_data - set data address and tida flag of a tcw | |
197 | * @tcw: the tcw address | |
198 | * @data: the data address | |
199 | * @use_tidal: zero of the data address specifies a contiguous block of data, | |
200 | * non-zero if it specifies a list if tidaws. | |
201 | * | |
202 | * Set the input/output data address of a tcw (depending on the value of the | |
203 | * r-flag and w-flag). If @use_tidal is non-zero, the corresponding tida flag | |
204 | * is set as well. | |
205 | */ | |
206 | void tcw_set_data(struct tcw *tcw, void *data, int use_tidal) | |
207 | { | |
208 | if (tcw->r) { | |
209 | tcw->input = (u64) ((addr_t) data); | |
210 | if (use_tidal) | |
211 | tcw->flags |= TCW_FLAGS_INPUT_TIDA; | |
212 | } else if (tcw->w) { | |
213 | tcw->output = (u64) ((addr_t) data); | |
214 | if (use_tidal) | |
215 | tcw->flags |= TCW_FLAGS_OUTPUT_TIDA; | |
216 | } | |
217 | } | |
218 | EXPORT_SYMBOL(tcw_set_data); | |
219 | ||
220 | /** | |
221 | * tcw_set_tccb - set tccb address of a tcw | |
222 | * @tcw: the tcw address | |
223 | * @tccb: the tccb address | |
224 | * | |
225 | * Set the address of the tccb in the specified tcw. | |
226 | */ | |
227 | void tcw_set_tccb(struct tcw *tcw, struct tccb *tccb) | |
228 | { | |
229 | tcw->tccb = (u64) ((addr_t) tccb); | |
230 | } | |
231 | EXPORT_SYMBOL(tcw_set_tccb); | |
232 | ||
233 | /** | |
234 | * tcw_set_tsb - set tsb address of a tcw | |
235 | * @tcw: the tcw address | |
236 | * @tsb: the tsb address | |
237 | * | |
238 | * Set the address of the tsb in the specified tcw. | |
239 | */ | |
240 | void tcw_set_tsb(struct tcw *tcw, struct tsb *tsb) | |
241 | { | |
242 | tcw->tsb = (u64) ((addr_t) tsb); | |
243 | } | |
244 | EXPORT_SYMBOL(tcw_set_tsb); | |
245 | ||
246 | /** | |
247 | * tccb_init - initialize tccb | |
248 | * @tccb: the tccb address | |
249 | * @size: the maximum size of the tccb | |
250 | * @sac: the service-action-code to be user | |
251 | * | |
252 | * Initialize the header of the specified tccb by resetting all values to zero | |
253 | * and filling in defaults for format, sac and initial tcal fields. | |
254 | */ | |
255 | void tccb_init(struct tccb *tccb, size_t size, u32 sac) | |
256 | { | |
257 | memset(tccb, 0, size); | |
258 | tccb->tcah.format = TCCB_FORMAT_DEFAULT; | |
259 | tccb->tcah.sac = sac; | |
260 | tccb->tcah.tcal = 12; | |
261 | } | |
262 | EXPORT_SYMBOL(tccb_init); | |
263 | ||
264 | /** | |
265 | * tsb_init - initialize tsb | |
266 | * @tsb: the tsb address | |
267 | * | |
268 | * Initialize the specified tsb by resetting all values to zero. | |
269 | */ | |
270 | void tsb_init(struct tsb *tsb) | |
271 | { | |
272 | memset(tsb, 0, sizeof(tsb)); | |
273 | } | |
274 | EXPORT_SYMBOL(tsb_init); | |
275 | ||
276 | /** | |
277 | * tccb_add_dcw - add a dcw to the tccb | |
278 | * @tccb: the tccb address | |
279 | * @tccb_size: the maximum tccb size | |
280 | * @cmd: the dcw command | |
281 | * @flags: flags for the dcw | |
282 | * @cd: pointer to control data for this dcw or NULL if none is required | |
283 | * @cd_count: number of control data bytes for this dcw | |
284 | * @count: number of data bytes for this dcw | |
285 | * | |
286 | * Add a new dcw to the specified tccb by writing the dcw information specified | |
287 | * by @cmd, @flags, @cd, @cd_count and @count to the tca of the tccb. Return | |
288 | * a pointer to the newly added dcw on success or -%ENOSPC if the new dcw | |
289 | * would exceed the available space as defined by @tccb_size. | |
290 | * | |
291 | * Note: the tcal field of the tccb header will be updates to reflect added | |
292 | * content. | |
293 | */ | |
294 | struct dcw *tccb_add_dcw(struct tccb *tccb, size_t tccb_size, u8 cmd, u8 flags, | |
295 | void *cd, u8 cd_count, u32 count) | |
296 | { | |
297 | struct dcw *dcw; | |
298 | int size; | |
299 | int tca_offset; | |
300 | ||
301 | /* Check for space. */ | |
302 | tca_offset = tca_size(tccb); | |
303 | size = ALIGN(sizeof(struct dcw) + cd_count, 4); | |
304 | if (sizeof(struct tccb_tcah) + tca_offset + size + | |
305 | sizeof(struct tccb_tcat) > tccb_size) | |
306 | return ERR_PTR(-ENOSPC); | |
307 | /* Add dcw to tca. */ | |
308 | dcw = (struct dcw *) &tccb->tca[tca_offset]; | |
309 | memset(dcw, 0, size); | |
310 | dcw->cmd = cmd; | |
311 | dcw->flags = flags; | |
312 | dcw->count = count; | |
313 | dcw->cd_count = cd_count; | |
314 | if (cd) | |
315 | memcpy(&dcw->cd[0], cd, cd_count); | |
316 | tccb->tcah.tcal += size; | |
317 | return dcw; | |
318 | } | |
319 | EXPORT_SYMBOL(tccb_add_dcw); | |
320 | ||
321 | /** | |
322 | * tcw_add_tidaw - add a tidaw to a tcw | |
323 | * @tcw: the tcw address | |
324 | * @num_tidaws: the current number of tidaws | |
325 | * @flags: flags for the new tidaw | |
326 | * @addr: address value for the new tidaw | |
327 | * @count: count value for the new tidaw | |
328 | * | |
329 | * Add a new tidaw to the input/output data tidaw-list of the specified tcw | |
330 | * (depending on the value of the r-flag and w-flag) and return a pointer to | |
331 | * the new tidaw. | |
332 | * | |
333 | * Note: the tidaw-list is assumed to be contiguous with no ttics. The caller | |
334 | * must ensure that there is enough space for the new tidaw. The last-tidaw | |
335 | * flag for the last tidaw in the list will be set by tcw_finalize. | |
336 | */ | |
337 | struct tidaw *tcw_add_tidaw(struct tcw *tcw, int num_tidaws, u8 flags, | |
338 | void *addr, u32 count) | |
339 | { | |
340 | struct tidaw *tidaw; | |
341 | ||
342 | /* Add tidaw to tidaw-list. */ | |
343 | tidaw = ((struct tidaw *) tcw_get_data(tcw)) + num_tidaws; | |
344 | memset(tidaw, 0, sizeof(struct tidaw)); | |
345 | tidaw->flags = flags; | |
346 | tidaw->count = count; | |
347 | tidaw->addr = (u64) ((addr_t) addr); | |
348 | return tidaw; | |
349 | } | |
350 | EXPORT_SYMBOL(tcw_add_tidaw); |