Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  *  Functions for assembling fcx enabled I/O control blocks.
0004  *
0005  *    Copyright IBM Corp. 2008
0006  *    Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
0007  */
0008 
0009 #include <linux/kernel.h>
0010 #include <linux/types.h>
0011 #include <linux/string.h>
0012 #include <linux/errno.h>
0013 #include <linux/err.h>
0014 #include <linux/module.h>
0015 #include <asm/fcx.h>
0016 #include "cio.h"
0017 
0018 /**
0019  * tcw_get_intrg - return pointer to associated interrogate tcw
0020  * @tcw: pointer to the original tcw
0021  *
0022  * Return a pointer to the interrogate tcw associated with the specified tcw
0023  * or %NULL if there is no associated interrogate tcw.
0024  */
0025 struct tcw *tcw_get_intrg(struct tcw *tcw)
0026 {
0027     return (struct tcw *) ((addr_t) tcw->intrg);
0028 }
0029 EXPORT_SYMBOL(tcw_get_intrg);
0030 
0031 /**
0032  * tcw_get_data - return pointer to input/output data associated with tcw
0033  * @tcw: pointer to the tcw
0034  *
0035  * Return the input or output data address specified in the tcw depending
0036  * on whether the r-bit or the w-bit is set. If neither bit is set, return
0037  * %NULL.
0038  */
0039 void *tcw_get_data(struct tcw *tcw)
0040 {
0041     if (tcw->r)
0042         return (void *) ((addr_t) tcw->input);
0043     if (tcw->w)
0044         return (void *) ((addr_t) tcw->output);
0045     return NULL;
0046 }
0047 EXPORT_SYMBOL(tcw_get_data);
0048 
0049 /**
0050  * tcw_get_tccb - return pointer to tccb associated with tcw
0051  * @tcw: pointer to the tcw
0052  *
0053  * Return pointer to the tccb associated with this tcw.
0054  */
0055 struct tccb *tcw_get_tccb(struct tcw *tcw)
0056 {
0057     return (struct tccb *) ((addr_t) tcw->tccb);
0058 }
0059 EXPORT_SYMBOL(tcw_get_tccb);
0060 
0061 /**
0062  * tcw_get_tsb - return pointer to tsb associated with tcw
0063  * @tcw: pointer to the tcw
0064  *
0065  * Return pointer to the tsb associated with this tcw.
0066  */
0067 struct tsb *tcw_get_tsb(struct tcw *tcw)
0068 {
0069     return (struct tsb *) ((addr_t) tcw->tsb);
0070 }
0071 EXPORT_SYMBOL(tcw_get_tsb);
0072 
0073 /**
0074  * tcw_init - initialize tcw data structure
0075  * @tcw: pointer to the tcw to be initialized
0076  * @r: initial value of the r-bit
0077  * @w: initial value of the w-bit
0078  *
0079  * Initialize all fields of the specified tcw data structure with zero and
0080  * fill in the format, flags, r and w fields.
0081  */
0082 void tcw_init(struct tcw *tcw, int r, int w)
0083 {
0084     memset(tcw, 0, sizeof(struct tcw));
0085     tcw->format = TCW_FORMAT_DEFAULT;
0086     tcw->flags = TCW_FLAGS_TIDAW_FORMAT(TCW_TIDAW_FORMAT_DEFAULT);
0087     if (r)
0088         tcw->r = 1;
0089     if (w)
0090         tcw->w = 1;
0091 }
0092 EXPORT_SYMBOL(tcw_init);
0093 
0094 static inline size_t tca_size(struct tccb *tccb)
0095 {
0096     return tccb->tcah.tcal - 12;
0097 }
0098 
0099 static u32 calc_dcw_count(struct tccb *tccb)
0100 {
0101     int offset;
0102     struct dcw *dcw;
0103     u32 count = 0;
0104     size_t size;
0105 
0106     size = tca_size(tccb);
0107     for (offset = 0; offset < size;) {
0108         dcw = (struct dcw *) &tccb->tca[offset];
0109         count += dcw->count;
0110         if (!(dcw->flags & DCW_FLAGS_CC))
0111             break;
0112         offset += sizeof(struct dcw) + ALIGN((int) dcw->cd_count, 4);
0113     }
0114     return count;
0115 }
0116 
0117 static u32 calc_cbc_size(struct tidaw *tidaw, int num)
0118 {
0119     int i;
0120     u32 cbc_data;
0121     u32 cbc_count = 0;
0122     u64 data_count = 0;
0123 
0124     for (i = 0; i < num; i++) {
0125         if (tidaw[i].flags & TIDAW_FLAGS_LAST)
0126             break;
0127         /* TODO: find out if padding applies to total of data
0128          * transferred or data transferred by this tidaw. Assumption:
0129          * applies to total. */
0130         data_count += tidaw[i].count;
0131         if (tidaw[i].flags & TIDAW_FLAGS_INSERT_CBC) {
0132             cbc_data = 4 + ALIGN(data_count, 4) - data_count;
0133             cbc_count += cbc_data;
0134             data_count += cbc_data;
0135         }
0136     }
0137     return cbc_count;
0138 }
0139 
0140 /**
0141  * tcw_finalize - finalize tcw length fields and tidaw list
0142  * @tcw: pointer to the tcw
0143  * @num_tidaws: the number of tidaws used to address input/output data or zero
0144  * if no tida is used
0145  *
0146  * Calculate the input-/output-count and tccbl field in the tcw, add a
0147  * tcat the tccb and terminate the data tidaw list if used.
0148  *
0149  * Note: in case input- or output-tida is used, the tidaw-list must be stored
0150  * in contiguous storage (no ttic). The tcal field in the tccb must be
0151  * up-to-date.
0152  */
0153 void tcw_finalize(struct tcw *tcw, int num_tidaws)
0154 {
0155     struct tidaw *tidaw;
0156     struct tccb *tccb;
0157     struct tccb_tcat *tcat;
0158     u32 count;
0159 
0160     /* Terminate tidaw list. */
0161     tidaw = tcw_get_data(tcw);
0162     if (num_tidaws > 0)
0163         tidaw[num_tidaws - 1].flags |= TIDAW_FLAGS_LAST;
0164     /* Add tcat to tccb. */
0165     tccb = tcw_get_tccb(tcw);
0166     tcat = (struct tccb_tcat *) &tccb->tca[tca_size(tccb)];
0167     memset(tcat, 0, sizeof(*tcat));
0168     /* Calculate tcw input/output count and tcat transport count. */
0169     count = calc_dcw_count(tccb);
0170     if (tcw->w && (tcw->flags & TCW_FLAGS_OUTPUT_TIDA))
0171         count += calc_cbc_size(tidaw, num_tidaws);
0172     if (tcw->r)
0173         tcw->input_count = count;
0174     else if (tcw->w)
0175         tcw->output_count = count;
0176     tcat->count = ALIGN(count, 4) + 4;
0177     /* Calculate tccbl. */
0178     tcw->tccbl = (sizeof(struct tccb) + tca_size(tccb) +
0179               sizeof(struct tccb_tcat) - 20) >> 2;
0180 }
0181 EXPORT_SYMBOL(tcw_finalize);
0182 
0183 /**
0184  * tcw_set_intrg - set the interrogate tcw address of a tcw
0185  * @tcw: the tcw address
0186  * @intrg_tcw: the address of the interrogate tcw
0187  *
0188  * Set the address of the interrogate tcw in the specified tcw.
0189  */
0190 void tcw_set_intrg(struct tcw *tcw, struct tcw *intrg_tcw)
0191 {
0192     tcw->intrg = (u32) ((addr_t) intrg_tcw);
0193 }
0194 EXPORT_SYMBOL(tcw_set_intrg);
0195 
0196 /**
0197  * tcw_set_data - set data address and tida flag of a tcw
0198  * @tcw: the tcw address
0199  * @data: the data address
0200  * @use_tidal: zero of the data address specifies a contiguous block of data,
0201  * non-zero if it specifies a list if tidaws.
0202  *
0203  * Set the input/output data address of a tcw (depending on the value of the
0204  * r-flag and w-flag). If @use_tidal is non-zero, the corresponding tida flag
0205  * is set as well.
0206  */
0207 void tcw_set_data(struct tcw *tcw, void *data, int use_tidal)
0208 {
0209     if (tcw->r) {
0210         tcw->input = (u64) ((addr_t) data);
0211         if (use_tidal)
0212             tcw->flags |= TCW_FLAGS_INPUT_TIDA;
0213     } else if (tcw->w) {
0214         tcw->output = (u64) ((addr_t) data);
0215         if (use_tidal)
0216             tcw->flags |= TCW_FLAGS_OUTPUT_TIDA;
0217     }
0218 }
0219 EXPORT_SYMBOL(tcw_set_data);
0220 
0221 /**
0222  * tcw_set_tccb - set tccb address of a tcw
0223  * @tcw: the tcw address
0224  * @tccb: the tccb address
0225  *
0226  * Set the address of the tccb in the specified tcw.
0227  */
0228 void tcw_set_tccb(struct tcw *tcw, struct tccb *tccb)
0229 {
0230     tcw->tccb = (u64) ((addr_t) tccb);
0231 }
0232 EXPORT_SYMBOL(tcw_set_tccb);
0233 
0234 /**
0235  * tcw_set_tsb - set tsb address of a tcw
0236  * @tcw: the tcw address
0237  * @tsb: the tsb address
0238  *
0239  * Set the address of the tsb in the specified tcw.
0240  */
0241 void tcw_set_tsb(struct tcw *tcw, struct tsb *tsb)
0242 {
0243     tcw->tsb = (u64) ((addr_t) tsb);
0244 }
0245 EXPORT_SYMBOL(tcw_set_tsb);
0246 
0247 /**
0248  * tccb_init - initialize tccb
0249  * @tccb: the tccb address
0250  * @size: the maximum size of the tccb
0251  * @sac: the service-action-code to be user
0252  *
0253  * Initialize the header of the specified tccb by resetting all values to zero
0254  * and filling in defaults for format, sac and initial tcal fields.
0255  */
0256 void tccb_init(struct tccb *tccb, size_t size, u32 sac)
0257 {
0258     memset(tccb, 0, size);
0259     tccb->tcah.format = TCCB_FORMAT_DEFAULT;
0260     tccb->tcah.sac = sac;
0261     tccb->tcah.tcal = 12;
0262 }
0263 EXPORT_SYMBOL(tccb_init);
0264 
0265 /**
0266  * tsb_init - initialize tsb
0267  * @tsb: the tsb address
0268  *
0269  * Initialize the specified tsb by resetting all values to zero.
0270  */
0271 void tsb_init(struct tsb *tsb)
0272 {
0273     memset(tsb, 0, sizeof(*tsb));
0274 }
0275 EXPORT_SYMBOL(tsb_init);
0276 
0277 /**
0278  * tccb_add_dcw - add a dcw to the tccb
0279  * @tccb: the tccb address
0280  * @tccb_size: the maximum tccb size
0281  * @cmd: the dcw command
0282  * @flags: flags for the dcw
0283  * @cd: pointer to control data for this dcw or NULL if none is required
0284  * @cd_count: number of control data bytes for this dcw
0285  * @count: number of data bytes for this dcw
0286  *
0287  * Add a new dcw to the specified tccb by writing the dcw information specified
0288  * by @cmd, @flags, @cd, @cd_count and @count to the tca of the tccb. Return
0289  * a pointer to the newly added dcw on success or -%ENOSPC if the new dcw
0290  * would exceed the available space as defined by @tccb_size.
0291  *
0292  * Note: the tcal field of the tccb header will be updates to reflect added
0293  * content.
0294  */
0295 struct dcw *tccb_add_dcw(struct tccb *tccb, size_t tccb_size, u8 cmd, u8 flags,
0296              void *cd, u8 cd_count, u32 count)
0297 {
0298     struct dcw *dcw;
0299     int size;
0300     int tca_offset;
0301 
0302     /* Check for space. */
0303     tca_offset = tca_size(tccb);
0304     size = ALIGN(sizeof(struct dcw) + cd_count, 4);
0305     if (sizeof(struct tccb_tcah) + tca_offset + size +
0306         sizeof(struct tccb_tcat) > tccb_size)
0307         return ERR_PTR(-ENOSPC);
0308     /* Add dcw to tca. */
0309     dcw = (struct dcw *) &tccb->tca[tca_offset];
0310     memset(dcw, 0, size);
0311     dcw->cmd = cmd;
0312     dcw->flags = flags;
0313     dcw->count = count;
0314     dcw->cd_count = cd_count;
0315     if (cd)
0316         memcpy(&dcw->cd[0], cd, cd_count);
0317     tccb->tcah.tcal += size;
0318     return dcw;
0319 }
0320 EXPORT_SYMBOL(tccb_add_dcw);
0321 
0322 /**
0323  * tcw_add_tidaw - add a tidaw to a tcw
0324  * @tcw: the tcw address
0325  * @num_tidaws: the current number of tidaws
0326  * @flags: flags for the new tidaw
0327  * @addr: address value for the new tidaw
0328  * @count: count value for the new tidaw
0329  *
0330  * Add a new tidaw to the input/output data tidaw-list of the specified tcw
0331  * (depending on the value of the r-flag and w-flag) and return a pointer to
0332  * the new tidaw.
0333  *
0334  * Note: the tidaw-list is assumed to be contiguous with no ttics. The caller
0335  * must ensure that there is enough space for the new tidaw. The last-tidaw
0336  * flag for the last tidaw in the list will be set by tcw_finalize.
0337  */
0338 struct tidaw *tcw_add_tidaw(struct tcw *tcw, int num_tidaws, u8 flags,
0339                 void *addr, u32 count)
0340 {
0341     struct tidaw *tidaw;
0342 
0343     /* Add tidaw to tidaw-list. */
0344     tidaw = ((struct tidaw *) tcw_get_data(tcw)) + num_tidaws;
0345     memset(tidaw, 0, sizeof(struct tidaw));
0346     tidaw->flags = flags;
0347     tidaw->count = count;
0348     tidaw->addr = (u64) ((addr_t) addr);
0349     return tidaw;
0350 }
0351 EXPORT_SYMBOL(tcw_add_tidaw);