Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
fcx.c
Go to the documentation of this file.
1 /*
2  * Functions for assembling fcx enabled I/O control blocks.
3  *
4  * Copyright IBM Corp. 2008
5  * Author(s): Peter Oberparleiter <[email protected]>
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 
24 struct tcw *tcw_get_intrg(struct tcw *tcw)
25 {
26  return (struct tcw *) ((addr_t) tcw->intrg);
27 }
29 
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 }
47 
54 struct tccb *tcw_get_tccb(struct tcw *tcw)
55 {
56  return (struct tccb *) ((addr_t) tcw->tccb);
57 }
59 
66 struct tsb *tcw_get_tsb(struct tcw *tcw)
67 {
68  return (struct tsb *) ((addr_t) tcw->tsb);
69 }
71 
81 void tcw_init(struct tcw *tcw, int r, int w)
82 {
83  memset(tcw, 0, sizeof(struct tcw));
86  if (r)
87  tcw->r = 1;
88  if (w)
89  tcw->w = 1;
90 }
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 
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 }
181 
189 void tcw_set_intrg(struct tcw *tcw, struct tcw *intrg_tcw)
190 {
191  tcw->intrg = (u32) ((addr_t) intrg_tcw);
192 }
194 
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)
216  }
217 }
219 
227 void tcw_set_tccb(struct tcw *tcw, struct tccb *tccb)
228 {
229  tcw->tccb = (u64) ((addr_t) tccb);
230 }
232 
240 void tcw_set_tsb(struct tcw *tcw, struct tsb *tsb)
241 {
242  tcw->tsb = (u64) ((addr_t) tsb);
243 }
245 
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 }
263 
270 void tsb_init(struct tsb *tsb)
271 {
272  memset(tsb, 0, sizeof(*tsb));
273 }
275 
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 }
320 
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 }