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