18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Functions for incremental construction of fcx enabled I/O control blocks. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2008 68c2ecf20Sopenharmony_ci * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/types.h> 118c2ecf20Sopenharmony_ci#include <linux/string.h> 128c2ecf20Sopenharmony_ci#include <linux/errno.h> 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <asm/fcx.h> 168c2ecf20Sopenharmony_ci#include <asm/itcw.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* 198c2ecf20Sopenharmony_ci * struct itcw - incremental tcw helper data type 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * This structure serves as a handle for the incremental construction of a 228c2ecf20Sopenharmony_ci * tcw and associated tccb, tsb, data tidaw-list plus an optional interrogate 238c2ecf20Sopenharmony_ci * tcw and associated data. The data structures are contained inside a single 248c2ecf20Sopenharmony_ci * contiguous buffer provided by the user. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * The itcw construction functions take care of overall data integrity: 278c2ecf20Sopenharmony_ci * - reset unused fields to zero 288c2ecf20Sopenharmony_ci * - fill in required pointers 298c2ecf20Sopenharmony_ci * - ensure required alignment for data structures 308c2ecf20Sopenharmony_ci * - prevent data structures to cross 4k-byte boundary where required 318c2ecf20Sopenharmony_ci * - calculate tccb-related length fields 328c2ecf20Sopenharmony_ci * - optionally provide ready-made interrogate tcw and associated structures 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * Restrictions apply to the itcws created with these construction functions: 358c2ecf20Sopenharmony_ci * - tida only supported for data address, not for tccb 368c2ecf20Sopenharmony_ci * - only contiguous tidaw-lists (no ttic) 378c2ecf20Sopenharmony_ci * - total number of bytes required per itcw may not exceed 4k bytes 388c2ecf20Sopenharmony_ci * - either read or write operation (may not work with r=0 and w=0) 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * Example: 418c2ecf20Sopenharmony_ci * struct itcw *itcw; 428c2ecf20Sopenharmony_ci * void *buffer; 438c2ecf20Sopenharmony_ci * size_t size; 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * size = itcw_calc_size(1, 2, 0); 468c2ecf20Sopenharmony_ci * buffer = kmalloc(size, GFP_KERNEL | GFP_DMA); 478c2ecf20Sopenharmony_ci * if (!buffer) 488c2ecf20Sopenharmony_ci * return -ENOMEM; 498c2ecf20Sopenharmony_ci * itcw = itcw_init(buffer, size, ITCW_OP_READ, 1, 2, 0); 508c2ecf20Sopenharmony_ci * if (IS_ERR(itcw)) 518c2ecf20Sopenharmony_ci * return PTR_ER(itcw); 528c2ecf20Sopenharmony_ci * itcw_add_dcw(itcw, 0x2, 0, NULL, 0, 72); 538c2ecf20Sopenharmony_ci * itcw_add_tidaw(itcw, 0, 0x30000, 20); 548c2ecf20Sopenharmony_ci * itcw_add_tidaw(itcw, 0, 0x40000, 52); 558c2ecf20Sopenharmony_ci * itcw_finalize(itcw); 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_cistruct itcw { 598c2ecf20Sopenharmony_ci struct tcw *tcw; 608c2ecf20Sopenharmony_ci struct tcw *intrg_tcw; 618c2ecf20Sopenharmony_ci int num_tidaws; 628c2ecf20Sopenharmony_ci int max_tidaws; 638c2ecf20Sopenharmony_ci int intrg_num_tidaws; 648c2ecf20Sopenharmony_ci int intrg_max_tidaws; 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/** 688c2ecf20Sopenharmony_ci * itcw_get_tcw - return pointer to tcw associated with the itcw 698c2ecf20Sopenharmony_ci * @itcw: address of the itcw 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * Return pointer to the tcw associated with the itcw. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_cistruct tcw *itcw_get_tcw(struct itcw *itcw) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci return itcw->tcw; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(itcw_get_tcw); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/** 808c2ecf20Sopenharmony_ci * itcw_calc_size - return the size of an itcw with the given parameters 818c2ecf20Sopenharmony_ci * @intrg: if non-zero, add an interrogate tcw 828c2ecf20Sopenharmony_ci * @max_tidaws: maximum number of tidaws to be used for data addressing or zero 838c2ecf20Sopenharmony_ci * if no tida is to be used. 848c2ecf20Sopenharmony_ci * @intrg_max_tidaws: maximum number of tidaws to be used for data addressing 858c2ecf20Sopenharmony_ci * by the interrogate tcw, if specified 868c2ecf20Sopenharmony_ci * 878c2ecf20Sopenharmony_ci * Calculate and return the number of bytes required to hold an itcw with the 888c2ecf20Sopenharmony_ci * given parameters and assuming tccbs with maximum size. 898c2ecf20Sopenharmony_ci * 908c2ecf20Sopenharmony_ci * Note that the resulting size also contains bytes needed for alignment 918c2ecf20Sopenharmony_ci * padding as well as padding to ensure that data structures don't cross a 928c2ecf20Sopenharmony_ci * 4k-boundary where required. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_cisize_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci size_t len; 978c2ecf20Sopenharmony_ci int cross_count; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* Main data. */ 1008c2ecf20Sopenharmony_ci len = sizeof(struct itcw); 1018c2ecf20Sopenharmony_ci len += /* TCW */ sizeof(struct tcw) + /* TCCB */ TCCB_MAX_SIZE + 1028c2ecf20Sopenharmony_ci /* TSB */ sizeof(struct tsb) + 1038c2ecf20Sopenharmony_ci /* TIDAL */ max_tidaws * sizeof(struct tidaw); 1048c2ecf20Sopenharmony_ci /* Interrogate data. */ 1058c2ecf20Sopenharmony_ci if (intrg) { 1068c2ecf20Sopenharmony_ci len += /* TCW */ sizeof(struct tcw) + /* TCCB */ TCCB_MAX_SIZE + 1078c2ecf20Sopenharmony_ci /* TSB */ sizeof(struct tsb) + 1088c2ecf20Sopenharmony_ci /* TIDAL */ intrg_max_tidaws * sizeof(struct tidaw); 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* Maximum required alignment padding. */ 1128c2ecf20Sopenharmony_ci len += /* Initial TCW */ 63 + /* Interrogate TCCB */ 7; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* TIDAW lists may not cross a 4k boundary. To cross a 1158c2ecf20Sopenharmony_ci * boundary we need to add a TTIC TIDAW. We need to reserve 1168c2ecf20Sopenharmony_ci * one additional TIDAW for a TTIC that we may need to add due 1178c2ecf20Sopenharmony_ci * to the placement of the data chunk in memory, and a further 1188c2ecf20Sopenharmony_ci * TIDAW for each page boundary that the TIDAW list may cross 1198c2ecf20Sopenharmony_ci * due to it's own size. 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_ci if (max_tidaws) { 1228c2ecf20Sopenharmony_ci cross_count = 1 + ((max_tidaws * sizeof(struct tidaw) - 1) 1238c2ecf20Sopenharmony_ci >> PAGE_SHIFT); 1248c2ecf20Sopenharmony_ci len += cross_count * sizeof(struct tidaw); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci if (intrg_max_tidaws) { 1278c2ecf20Sopenharmony_ci cross_count = 1 + ((intrg_max_tidaws * sizeof(struct tidaw) - 1) 1288c2ecf20Sopenharmony_ci >> PAGE_SHIFT); 1298c2ecf20Sopenharmony_ci len += cross_count * sizeof(struct tidaw); 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci return len; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(itcw_calc_size); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci#define CROSS4K(x, l) (((x) & ~4095) != ((x + l) & ~4095)) 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic inline void *fit_chunk(addr_t *start, addr_t end, size_t len, 1388c2ecf20Sopenharmony_ci int align, int check_4k) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci addr_t addr; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci addr = ALIGN(*start, align); 1438c2ecf20Sopenharmony_ci if (check_4k && CROSS4K(addr, len)) { 1448c2ecf20Sopenharmony_ci addr = ALIGN(addr, 4096); 1458c2ecf20Sopenharmony_ci addr = ALIGN(addr, align); 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci if (addr + len > end) 1488c2ecf20Sopenharmony_ci return ERR_PTR(-ENOSPC); 1498c2ecf20Sopenharmony_ci *start = addr + len; 1508c2ecf20Sopenharmony_ci return (void *) addr; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/** 1548c2ecf20Sopenharmony_ci * itcw_init - initialize incremental tcw data structure 1558c2ecf20Sopenharmony_ci * @buffer: address of buffer to use for data structures 1568c2ecf20Sopenharmony_ci * @size: number of bytes in buffer 1578c2ecf20Sopenharmony_ci * @op: %ITCW_OP_READ for a read operation tcw, %ITCW_OP_WRITE for a write 1588c2ecf20Sopenharmony_ci * operation tcw 1598c2ecf20Sopenharmony_ci * @intrg: if non-zero, add and initialize an interrogate tcw 1608c2ecf20Sopenharmony_ci * @max_tidaws: maximum number of tidaws to be used for data addressing or zero 1618c2ecf20Sopenharmony_ci * if no tida is to be used. 1628c2ecf20Sopenharmony_ci * @intrg_max_tidaws: maximum number of tidaws to be used for data addressing 1638c2ecf20Sopenharmony_ci * by the interrogate tcw, if specified 1648c2ecf20Sopenharmony_ci * 1658c2ecf20Sopenharmony_ci * Prepare the specified buffer to be used as an incremental tcw, i.e. a 1668c2ecf20Sopenharmony_ci * helper data structure that can be used to construct a valid tcw by 1678c2ecf20Sopenharmony_ci * successive calls to other helper functions. Note: the buffer needs to be 1688c2ecf20Sopenharmony_ci * located below the 2G address limit. The resulting tcw has the following 1698c2ecf20Sopenharmony_ci * restrictions: 1708c2ecf20Sopenharmony_ci * - no tccb tidal 1718c2ecf20Sopenharmony_ci * - input/output tidal is contiguous (no ttic) 1728c2ecf20Sopenharmony_ci * - total data should not exceed 4k 1738c2ecf20Sopenharmony_ci * - tcw specifies either read or write operation 1748c2ecf20Sopenharmony_ci * 1758c2ecf20Sopenharmony_ci * On success, return pointer to the resulting incremental tcw data structure, 1768c2ecf20Sopenharmony_ci * ERR_PTR otherwise. 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_cistruct itcw *itcw_init(void *buffer, size_t size, int op, int intrg, 1798c2ecf20Sopenharmony_ci int max_tidaws, int intrg_max_tidaws) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct itcw *itcw; 1828c2ecf20Sopenharmony_ci void *chunk; 1838c2ecf20Sopenharmony_ci addr_t start; 1848c2ecf20Sopenharmony_ci addr_t end; 1858c2ecf20Sopenharmony_ci int cross_count; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* Check for 2G limit. */ 1888c2ecf20Sopenharmony_ci start = (addr_t) buffer; 1898c2ecf20Sopenharmony_ci end = start + size; 1908c2ecf20Sopenharmony_ci if (end > (1 << 31)) 1918c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1928c2ecf20Sopenharmony_ci memset(buffer, 0, size); 1938c2ecf20Sopenharmony_ci /* ITCW. */ 1948c2ecf20Sopenharmony_ci chunk = fit_chunk(&start, end, sizeof(struct itcw), 1, 0); 1958c2ecf20Sopenharmony_ci if (IS_ERR(chunk)) 1968c2ecf20Sopenharmony_ci return chunk; 1978c2ecf20Sopenharmony_ci itcw = chunk; 1988c2ecf20Sopenharmony_ci /* allow for TTIC tidaws that may be needed to cross a page boundary */ 1998c2ecf20Sopenharmony_ci cross_count = 0; 2008c2ecf20Sopenharmony_ci if (max_tidaws) 2018c2ecf20Sopenharmony_ci cross_count = 1 + ((max_tidaws * sizeof(struct tidaw) - 1) 2028c2ecf20Sopenharmony_ci >> PAGE_SHIFT); 2038c2ecf20Sopenharmony_ci itcw->max_tidaws = max_tidaws + cross_count; 2048c2ecf20Sopenharmony_ci cross_count = 0; 2058c2ecf20Sopenharmony_ci if (intrg_max_tidaws) 2068c2ecf20Sopenharmony_ci cross_count = 1 + ((intrg_max_tidaws * sizeof(struct tidaw) - 1) 2078c2ecf20Sopenharmony_ci >> PAGE_SHIFT); 2088c2ecf20Sopenharmony_ci itcw->intrg_max_tidaws = intrg_max_tidaws + cross_count; 2098c2ecf20Sopenharmony_ci /* Main TCW. */ 2108c2ecf20Sopenharmony_ci chunk = fit_chunk(&start, end, sizeof(struct tcw), 64, 0); 2118c2ecf20Sopenharmony_ci if (IS_ERR(chunk)) 2128c2ecf20Sopenharmony_ci return chunk; 2138c2ecf20Sopenharmony_ci itcw->tcw = chunk; 2148c2ecf20Sopenharmony_ci tcw_init(itcw->tcw, (op == ITCW_OP_READ) ? 1 : 0, 2158c2ecf20Sopenharmony_ci (op == ITCW_OP_WRITE) ? 1 : 0); 2168c2ecf20Sopenharmony_ci /* Interrogate TCW. */ 2178c2ecf20Sopenharmony_ci if (intrg) { 2188c2ecf20Sopenharmony_ci chunk = fit_chunk(&start, end, sizeof(struct tcw), 64, 0); 2198c2ecf20Sopenharmony_ci if (IS_ERR(chunk)) 2208c2ecf20Sopenharmony_ci return chunk; 2218c2ecf20Sopenharmony_ci itcw->intrg_tcw = chunk; 2228c2ecf20Sopenharmony_ci tcw_init(itcw->intrg_tcw, 1, 0); 2238c2ecf20Sopenharmony_ci tcw_set_intrg(itcw->tcw, itcw->intrg_tcw); 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci /* Data TIDAL. */ 2268c2ecf20Sopenharmony_ci if (max_tidaws > 0) { 2278c2ecf20Sopenharmony_ci chunk = fit_chunk(&start, end, sizeof(struct tidaw) * 2288c2ecf20Sopenharmony_ci itcw->max_tidaws, 16, 0); 2298c2ecf20Sopenharmony_ci if (IS_ERR(chunk)) 2308c2ecf20Sopenharmony_ci return chunk; 2318c2ecf20Sopenharmony_ci tcw_set_data(itcw->tcw, chunk, 1); 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci /* Interrogate data TIDAL. */ 2348c2ecf20Sopenharmony_ci if (intrg && (intrg_max_tidaws > 0)) { 2358c2ecf20Sopenharmony_ci chunk = fit_chunk(&start, end, sizeof(struct tidaw) * 2368c2ecf20Sopenharmony_ci itcw->intrg_max_tidaws, 16, 0); 2378c2ecf20Sopenharmony_ci if (IS_ERR(chunk)) 2388c2ecf20Sopenharmony_ci return chunk; 2398c2ecf20Sopenharmony_ci tcw_set_data(itcw->intrg_tcw, chunk, 1); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci /* TSB. */ 2428c2ecf20Sopenharmony_ci chunk = fit_chunk(&start, end, sizeof(struct tsb), 8, 0); 2438c2ecf20Sopenharmony_ci if (IS_ERR(chunk)) 2448c2ecf20Sopenharmony_ci return chunk; 2458c2ecf20Sopenharmony_ci tsb_init(chunk); 2468c2ecf20Sopenharmony_ci tcw_set_tsb(itcw->tcw, chunk); 2478c2ecf20Sopenharmony_ci /* Interrogate TSB. */ 2488c2ecf20Sopenharmony_ci if (intrg) { 2498c2ecf20Sopenharmony_ci chunk = fit_chunk(&start, end, sizeof(struct tsb), 8, 0); 2508c2ecf20Sopenharmony_ci if (IS_ERR(chunk)) 2518c2ecf20Sopenharmony_ci return chunk; 2528c2ecf20Sopenharmony_ci tsb_init(chunk); 2538c2ecf20Sopenharmony_ci tcw_set_tsb(itcw->intrg_tcw, chunk); 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci /* TCCB. */ 2568c2ecf20Sopenharmony_ci chunk = fit_chunk(&start, end, TCCB_MAX_SIZE, 8, 0); 2578c2ecf20Sopenharmony_ci if (IS_ERR(chunk)) 2588c2ecf20Sopenharmony_ci return chunk; 2598c2ecf20Sopenharmony_ci tccb_init(chunk, TCCB_MAX_SIZE, TCCB_SAC_DEFAULT); 2608c2ecf20Sopenharmony_ci tcw_set_tccb(itcw->tcw, chunk); 2618c2ecf20Sopenharmony_ci /* Interrogate TCCB. */ 2628c2ecf20Sopenharmony_ci if (intrg) { 2638c2ecf20Sopenharmony_ci chunk = fit_chunk(&start, end, TCCB_MAX_SIZE, 8, 0); 2648c2ecf20Sopenharmony_ci if (IS_ERR(chunk)) 2658c2ecf20Sopenharmony_ci return chunk; 2668c2ecf20Sopenharmony_ci tccb_init(chunk, TCCB_MAX_SIZE, TCCB_SAC_INTRG); 2678c2ecf20Sopenharmony_ci tcw_set_tccb(itcw->intrg_tcw, chunk); 2688c2ecf20Sopenharmony_ci tccb_add_dcw(chunk, TCCB_MAX_SIZE, DCW_CMD_INTRG, 0, NULL, 2698c2ecf20Sopenharmony_ci sizeof(struct dcw_intrg_data), 0); 2708c2ecf20Sopenharmony_ci tcw_finalize(itcw->intrg_tcw, 0); 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci return itcw; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(itcw_init); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci/** 2778c2ecf20Sopenharmony_ci * itcw_add_dcw - add a dcw to the itcw 2788c2ecf20Sopenharmony_ci * @itcw: address of the itcw 2798c2ecf20Sopenharmony_ci * @cmd: the dcw command 2808c2ecf20Sopenharmony_ci * @flags: flags for the dcw 2818c2ecf20Sopenharmony_ci * @cd: address of control data for this dcw or NULL if none is required 2828c2ecf20Sopenharmony_ci * @cd_count: number of control data bytes for this dcw 2838c2ecf20Sopenharmony_ci * @count: number of data bytes for this dcw 2848c2ecf20Sopenharmony_ci * 2858c2ecf20Sopenharmony_ci * Add a new dcw to the specified itcw by writing the dcw information specified 2868c2ecf20Sopenharmony_ci * by @cmd, @flags, @cd, @cd_count and @count to the tca of the tccb. Return 2878c2ecf20Sopenharmony_ci * a pointer to the newly added dcw on success or -%ENOSPC if the new dcw 2888c2ecf20Sopenharmony_ci * would exceed the available space. 2898c2ecf20Sopenharmony_ci * 2908c2ecf20Sopenharmony_ci * Note: the tcal field of the tccb header will be updated to reflect added 2918c2ecf20Sopenharmony_ci * content. 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_cistruct dcw *itcw_add_dcw(struct itcw *itcw, u8 cmd, u8 flags, void *cd, 2948c2ecf20Sopenharmony_ci u8 cd_count, u32 count) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci return tccb_add_dcw(tcw_get_tccb(itcw->tcw), TCCB_MAX_SIZE, cmd, 2978c2ecf20Sopenharmony_ci flags, cd, cd_count, count); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(itcw_add_dcw); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/** 3028c2ecf20Sopenharmony_ci * itcw_add_tidaw - add a tidaw to the itcw 3038c2ecf20Sopenharmony_ci * @itcw: address of the itcw 3048c2ecf20Sopenharmony_ci * @flags: flags for the new tidaw 3058c2ecf20Sopenharmony_ci * @addr: address value for the new tidaw 3068c2ecf20Sopenharmony_ci * @count: count value for the new tidaw 3078c2ecf20Sopenharmony_ci * 3088c2ecf20Sopenharmony_ci * Add a new tidaw to the input/output data tidaw-list of the specified itcw 3098c2ecf20Sopenharmony_ci * (depending on the value of the r-flag and w-flag). Return a pointer to 3108c2ecf20Sopenharmony_ci * the new tidaw on success or -%ENOSPC if the new tidaw would exceed the 3118c2ecf20Sopenharmony_ci * available space. 3128c2ecf20Sopenharmony_ci * 3138c2ecf20Sopenharmony_ci * Note: TTIC tidaws are automatically added when needed, so explicitly calling 3148c2ecf20Sopenharmony_ci * this interface with the TTIC flag is not supported. The last-tidaw flag 3158c2ecf20Sopenharmony_ci * for the last tidaw in the list will be set by itcw_finalize. 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_cistruct tidaw *itcw_add_tidaw(struct itcw *itcw, u8 flags, void *addr, u32 count) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct tidaw *following; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (itcw->num_tidaws >= itcw->max_tidaws) 3228c2ecf20Sopenharmony_ci return ERR_PTR(-ENOSPC); 3238c2ecf20Sopenharmony_ci /* 3248c2ecf20Sopenharmony_ci * Is the tidaw, which follows the one we are about to fill, on the next 3258c2ecf20Sopenharmony_ci * page? Then we have to insert a TTIC tidaw first, that points to the 3268c2ecf20Sopenharmony_ci * tidaw on the new page. 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_ci following = ((struct tidaw *) tcw_get_data(itcw->tcw)) 3298c2ecf20Sopenharmony_ci + itcw->num_tidaws + 1; 3308c2ecf20Sopenharmony_ci if (itcw->num_tidaws && !((unsigned long) following & ~PAGE_MASK)) { 3318c2ecf20Sopenharmony_ci tcw_add_tidaw(itcw->tcw, itcw->num_tidaws++, 3328c2ecf20Sopenharmony_ci TIDAW_FLAGS_TTIC, following, 0); 3338c2ecf20Sopenharmony_ci if (itcw->num_tidaws >= itcw->max_tidaws) 3348c2ecf20Sopenharmony_ci return ERR_PTR(-ENOSPC); 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci return tcw_add_tidaw(itcw->tcw, itcw->num_tidaws++, flags, addr, count); 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(itcw_add_tidaw); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci/** 3418c2ecf20Sopenharmony_ci * itcw_set_data - set data address and tida flag of the itcw 3428c2ecf20Sopenharmony_ci * @itcw: address of the itcw 3438c2ecf20Sopenharmony_ci * @addr: the data address 3448c2ecf20Sopenharmony_ci * @use_tidal: zero of the data address specifies a contiguous block of data, 3458c2ecf20Sopenharmony_ci * non-zero if it specifies a list if tidaws. 3468c2ecf20Sopenharmony_ci * 3478c2ecf20Sopenharmony_ci * Set the input/output data address of the itcw (depending on the value of the 3488c2ecf20Sopenharmony_ci * r-flag and w-flag). If @use_tidal is non-zero, the corresponding tida flag 3498c2ecf20Sopenharmony_ci * is set as well. 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_civoid itcw_set_data(struct itcw *itcw, void *addr, int use_tidal) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci tcw_set_data(itcw->tcw, addr, use_tidal); 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(itcw_set_data); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci/** 3588c2ecf20Sopenharmony_ci * itcw_finalize - calculate length and count fields of the itcw 3598c2ecf20Sopenharmony_ci * @itcw: address of the itcw 3608c2ecf20Sopenharmony_ci * 3618c2ecf20Sopenharmony_ci * Calculate tcw input-/output-count and tccbl fields and add a tcat the tccb. 3628c2ecf20Sopenharmony_ci * In case input- or output-tida is used, the tidaw-list must be stored in 3638c2ecf20Sopenharmony_ci * continuous storage (no ttic). The tcal field in the tccb must be 3648c2ecf20Sopenharmony_ci * up-to-date. 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_civoid itcw_finalize(struct itcw *itcw) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci tcw_finalize(itcw->tcw, itcw->num_tidaws); 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(itcw_finalize); 371