18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for IBM Power 842 compression accelerator 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) IBM Corporation, 2012 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: Robert Jennings <rcj@linux.vnet.ibm.com> 88c2ecf20Sopenharmony_ci * Seth Jennings <sjenning@linux.vnet.ibm.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <asm/vio.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "nx-842.h" 148c2ecf20Sopenharmony_ci#include "nx_csbcpb.h" /* struct nx_csbcpb */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 178c2ecf20Sopenharmony_ciMODULE_AUTHOR("Robert Jennings <rcj@linux.vnet.ibm.com>"); 188c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("842 H/W Compression driver for IBM Power processors"); 198c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("842"); 208c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("842-nx"); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic struct nx842_constraints nx842_pseries_constraints = { 238c2ecf20Sopenharmony_ci .alignment = DDE_BUFFER_ALIGN, 248c2ecf20Sopenharmony_ci .multiple = DDE_BUFFER_LAST_MULT, 258c2ecf20Sopenharmony_ci .minimum = DDE_BUFFER_LAST_MULT, 268c2ecf20Sopenharmony_ci .maximum = PAGE_SIZE, /* dynamic, max_sync_size */ 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int check_constraints(unsigned long buf, unsigned int *len, bool in) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci if (!IS_ALIGNED(buf, nx842_pseries_constraints.alignment)) { 328c2ecf20Sopenharmony_ci pr_debug("%s buffer 0x%lx not aligned to 0x%x\n", 338c2ecf20Sopenharmony_ci in ? "input" : "output", buf, 348c2ecf20Sopenharmony_ci nx842_pseries_constraints.alignment); 358c2ecf20Sopenharmony_ci return -EINVAL; 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci if (*len % nx842_pseries_constraints.multiple) { 388c2ecf20Sopenharmony_ci pr_debug("%s buffer len 0x%x not multiple of 0x%x\n", 398c2ecf20Sopenharmony_ci in ? "input" : "output", *len, 408c2ecf20Sopenharmony_ci nx842_pseries_constraints.multiple); 418c2ecf20Sopenharmony_ci if (in) 428c2ecf20Sopenharmony_ci return -EINVAL; 438c2ecf20Sopenharmony_ci *len = round_down(*len, nx842_pseries_constraints.multiple); 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci if (*len < nx842_pseries_constraints.minimum) { 468c2ecf20Sopenharmony_ci pr_debug("%s buffer len 0x%x under minimum 0x%x\n", 478c2ecf20Sopenharmony_ci in ? "input" : "output", *len, 488c2ecf20Sopenharmony_ci nx842_pseries_constraints.minimum); 498c2ecf20Sopenharmony_ci return -EINVAL; 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci if (*len > nx842_pseries_constraints.maximum) { 528c2ecf20Sopenharmony_ci pr_debug("%s buffer len 0x%x over maximum 0x%x\n", 538c2ecf20Sopenharmony_ci in ? "input" : "output", *len, 548c2ecf20Sopenharmony_ci nx842_pseries_constraints.maximum); 558c2ecf20Sopenharmony_ci if (in) 568c2ecf20Sopenharmony_ci return -EINVAL; 578c2ecf20Sopenharmony_ci *len = nx842_pseries_constraints.maximum; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci return 0; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* I assume we need to align the CSB? */ 638c2ecf20Sopenharmony_ci#define WORKMEM_ALIGN (256) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistruct nx842_workmem { 668c2ecf20Sopenharmony_ci /* scatterlist */ 678c2ecf20Sopenharmony_ci char slin[4096]; 688c2ecf20Sopenharmony_ci char slout[4096]; 698c2ecf20Sopenharmony_ci /* coprocessor status/parameter block */ 708c2ecf20Sopenharmony_ci struct nx_csbcpb csbcpb; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci char padding[WORKMEM_ALIGN]; 738c2ecf20Sopenharmony_ci} __aligned(WORKMEM_ALIGN); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* Macros for fields within nx_csbcpb */ 768c2ecf20Sopenharmony_ci/* Check the valid bit within the csbcpb valid field */ 778c2ecf20Sopenharmony_ci#define NX842_CSBCBP_VALID_CHK(x) (x & BIT_MASK(7)) 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* CE macros operate on the completion_extension field bits in the csbcpb. 808c2ecf20Sopenharmony_ci * CE0 0=full completion, 1=partial completion 818c2ecf20Sopenharmony_ci * CE1 0=CE0 indicates completion, 1=termination (output may be modified) 828c2ecf20Sopenharmony_ci * CE2 0=processed_bytes is source bytes, 1=processed_bytes is target bytes */ 838c2ecf20Sopenharmony_ci#define NX842_CSBCPB_CE0(x) (x & BIT_MASK(7)) 848c2ecf20Sopenharmony_ci#define NX842_CSBCPB_CE1(x) (x & BIT_MASK(6)) 858c2ecf20Sopenharmony_ci#define NX842_CSBCPB_CE2(x) (x & BIT_MASK(5)) 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* The NX unit accepts data only on 4K page boundaries */ 888c2ecf20Sopenharmony_ci#define NX842_HW_PAGE_SIZE (4096) 898c2ecf20Sopenharmony_ci#define NX842_HW_PAGE_MASK (~(NX842_HW_PAGE_SIZE-1)) 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistruct ibm_nx842_counters { 928c2ecf20Sopenharmony_ci atomic64_t comp_complete; 938c2ecf20Sopenharmony_ci atomic64_t comp_failed; 948c2ecf20Sopenharmony_ci atomic64_t decomp_complete; 958c2ecf20Sopenharmony_ci atomic64_t decomp_failed; 968c2ecf20Sopenharmony_ci atomic64_t swdecomp; 978c2ecf20Sopenharmony_ci atomic64_t comp_times[32]; 988c2ecf20Sopenharmony_ci atomic64_t decomp_times[32]; 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic struct nx842_devdata { 1028c2ecf20Sopenharmony_ci struct vio_dev *vdev; 1038c2ecf20Sopenharmony_ci struct device *dev; 1048c2ecf20Sopenharmony_ci struct ibm_nx842_counters *counters; 1058c2ecf20Sopenharmony_ci unsigned int max_sg_len; 1068c2ecf20Sopenharmony_ci unsigned int max_sync_size; 1078c2ecf20Sopenharmony_ci unsigned int max_sync_sg; 1088c2ecf20Sopenharmony_ci} __rcu *devdata; 1098c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(devdata_mutex); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#define NX842_COUNTER_INC(_x) \ 1128c2ecf20Sopenharmony_cistatic inline void nx842_inc_##_x( \ 1138c2ecf20Sopenharmony_ci const struct nx842_devdata *dev) { \ 1148c2ecf20Sopenharmony_ci if (dev) \ 1158c2ecf20Sopenharmony_ci atomic64_inc(&dev->counters->_x); \ 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ciNX842_COUNTER_INC(comp_complete); 1188c2ecf20Sopenharmony_ciNX842_COUNTER_INC(comp_failed); 1198c2ecf20Sopenharmony_ciNX842_COUNTER_INC(decomp_complete); 1208c2ecf20Sopenharmony_ciNX842_COUNTER_INC(decomp_failed); 1218c2ecf20Sopenharmony_ciNX842_COUNTER_INC(swdecomp); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci#define NX842_HIST_SLOTS 16 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void ibm_nx842_incr_hist(atomic64_t *times, unsigned int time) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci int bucket = fls(time); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (bucket) 1308c2ecf20Sopenharmony_ci bucket = min((NX842_HIST_SLOTS - 1), bucket - 1); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci atomic64_inc(×[bucket]); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* NX unit operation flags */ 1368c2ecf20Sopenharmony_ci#define NX842_OP_COMPRESS 0x0 1378c2ecf20Sopenharmony_ci#define NX842_OP_CRC 0x1 1388c2ecf20Sopenharmony_ci#define NX842_OP_DECOMPRESS 0x2 1398c2ecf20Sopenharmony_ci#define NX842_OP_COMPRESS_CRC (NX842_OP_COMPRESS | NX842_OP_CRC) 1408c2ecf20Sopenharmony_ci#define NX842_OP_DECOMPRESS_CRC (NX842_OP_DECOMPRESS | NX842_OP_CRC) 1418c2ecf20Sopenharmony_ci#define NX842_OP_ASYNC (1<<23) 1428c2ecf20Sopenharmony_ci#define NX842_OP_NOTIFY (1<<22) 1438c2ecf20Sopenharmony_ci#define NX842_OP_NOTIFY_INT(x) ((x & 0xff)<<8) 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic unsigned long nx842_get_desired_dma(struct vio_dev *viodev) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci /* No use of DMA mappings within the driver. */ 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistruct nx842_slentry { 1528c2ecf20Sopenharmony_ci __be64 ptr; /* Real address (use __pa()) */ 1538c2ecf20Sopenharmony_ci __be64 len; 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/* pHyp scatterlist entry */ 1578c2ecf20Sopenharmony_cistruct nx842_scatterlist { 1588c2ecf20Sopenharmony_ci int entry_nr; /* number of slentries */ 1598c2ecf20Sopenharmony_ci struct nx842_slentry *entries; /* ptr to array of slentries */ 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* Does not include sizeof(entry_nr) in the size */ 1638c2ecf20Sopenharmony_cistatic inline unsigned long nx842_get_scatterlist_size( 1648c2ecf20Sopenharmony_ci struct nx842_scatterlist *sl) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci return sl->entry_nr * sizeof(struct nx842_slentry); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic int nx842_build_scatterlist(unsigned long buf, int len, 1708c2ecf20Sopenharmony_ci struct nx842_scatterlist *sl) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci unsigned long entrylen; 1738c2ecf20Sopenharmony_ci struct nx842_slentry *entry; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci sl->entry_nr = 0; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci entry = sl->entries; 1788c2ecf20Sopenharmony_ci while (len) { 1798c2ecf20Sopenharmony_ci entry->ptr = cpu_to_be64(nx842_get_pa((void *)buf)); 1808c2ecf20Sopenharmony_ci entrylen = min_t(int, len, 1818c2ecf20Sopenharmony_ci LEN_ON_SIZE(buf, NX842_HW_PAGE_SIZE)); 1828c2ecf20Sopenharmony_ci entry->len = cpu_to_be64(entrylen); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci len -= entrylen; 1858c2ecf20Sopenharmony_ci buf += entrylen; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci sl->entry_nr++; 1888c2ecf20Sopenharmony_ci entry++; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic int nx842_validate_result(struct device *dev, 1958c2ecf20Sopenharmony_ci struct cop_status_block *csb) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci /* The csb must be valid after returning from vio_h_cop_sync */ 1988c2ecf20Sopenharmony_ci if (!NX842_CSBCBP_VALID_CHK(csb->valid)) { 1998c2ecf20Sopenharmony_ci dev_err(dev, "%s: cspcbp not valid upon completion.\n", 2008c2ecf20Sopenharmony_ci __func__); 2018c2ecf20Sopenharmony_ci dev_dbg(dev, "valid:0x%02x cs:0x%02x cc:0x%02x ce:0x%02x\n", 2028c2ecf20Sopenharmony_ci csb->valid, 2038c2ecf20Sopenharmony_ci csb->crb_seq_number, 2048c2ecf20Sopenharmony_ci csb->completion_code, 2058c2ecf20Sopenharmony_ci csb->completion_extension); 2068c2ecf20Sopenharmony_ci dev_dbg(dev, "processed_bytes:%d address:0x%016lx\n", 2078c2ecf20Sopenharmony_ci be32_to_cpu(csb->processed_byte_count), 2088c2ecf20Sopenharmony_ci (unsigned long)be64_to_cpu(csb->address)); 2098c2ecf20Sopenharmony_ci return -EIO; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* Check return values from the hardware in the CSB */ 2138c2ecf20Sopenharmony_ci switch (csb->completion_code) { 2148c2ecf20Sopenharmony_ci case 0: /* Completed without error */ 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci case 64: /* Compression ok, but output larger than input */ 2178c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: output size larger than input size\n", 2188c2ecf20Sopenharmony_ci __func__); 2198c2ecf20Sopenharmony_ci break; 2208c2ecf20Sopenharmony_ci case 13: /* Output buffer too small */ 2218c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: Out of space in output buffer\n", 2228c2ecf20Sopenharmony_ci __func__); 2238c2ecf20Sopenharmony_ci return -ENOSPC; 2248c2ecf20Sopenharmony_ci case 65: /* Calculated CRC doesn't match the passed value */ 2258c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: CRC mismatch for decompression\n", 2268c2ecf20Sopenharmony_ci __func__); 2278c2ecf20Sopenharmony_ci return -EINVAL; 2288c2ecf20Sopenharmony_ci case 66: /* Input data contains an illegal template field */ 2298c2ecf20Sopenharmony_ci case 67: /* Template indicates data past the end of the input stream */ 2308c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: Bad data for decompression (code:%d)\n", 2318c2ecf20Sopenharmony_ci __func__, csb->completion_code); 2328c2ecf20Sopenharmony_ci return -EINVAL; 2338c2ecf20Sopenharmony_ci default: 2348c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: Unspecified error (code:%d)\n", 2358c2ecf20Sopenharmony_ci __func__, csb->completion_code); 2368c2ecf20Sopenharmony_ci return -EIO; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* Hardware sanity check */ 2408c2ecf20Sopenharmony_ci if (!NX842_CSBCPB_CE2(csb->completion_extension)) { 2418c2ecf20Sopenharmony_ci dev_err(dev, "%s: No error returned by hardware, but " 2428c2ecf20Sopenharmony_ci "data returned is unusable, contact support.\n" 2438c2ecf20Sopenharmony_ci "(Additional info: csbcbp->processed bytes " 2448c2ecf20Sopenharmony_ci "does not specify processed bytes for the " 2458c2ecf20Sopenharmony_ci "target buffer.)\n", __func__); 2468c2ecf20Sopenharmony_ci return -EIO; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return 0; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci/** 2538c2ecf20Sopenharmony_ci * nx842_pseries_compress - Compress data using the 842 algorithm 2548c2ecf20Sopenharmony_ci * 2558c2ecf20Sopenharmony_ci * Compression provide by the NX842 coprocessor on IBM Power systems. 2568c2ecf20Sopenharmony_ci * The input buffer is compressed and the result is stored in the 2578c2ecf20Sopenharmony_ci * provided output buffer. 2588c2ecf20Sopenharmony_ci * 2598c2ecf20Sopenharmony_ci * Upon return from this function @outlen contains the length of the 2608c2ecf20Sopenharmony_ci * compressed data. If there is an error then @outlen will be 0 and an 2618c2ecf20Sopenharmony_ci * error will be specified by the return code from this function. 2628c2ecf20Sopenharmony_ci * 2638c2ecf20Sopenharmony_ci * @in: Pointer to input buffer 2648c2ecf20Sopenharmony_ci * @inlen: Length of input buffer 2658c2ecf20Sopenharmony_ci * @out: Pointer to output buffer 2668c2ecf20Sopenharmony_ci * @outlen: Length of output buffer 2678c2ecf20Sopenharmony_ci * @wrkmem: ptr to buffer for working memory, size determined by 2688c2ecf20Sopenharmony_ci * nx842_pseries_driver.workmem_size 2698c2ecf20Sopenharmony_ci * 2708c2ecf20Sopenharmony_ci * Returns: 2718c2ecf20Sopenharmony_ci * 0 Success, output of length @outlen stored in the buffer at @out 2728c2ecf20Sopenharmony_ci * -ENOMEM Unable to allocate internal buffers 2738c2ecf20Sopenharmony_ci * -ENOSPC Output buffer is to small 2748c2ecf20Sopenharmony_ci * -EIO Internal error 2758c2ecf20Sopenharmony_ci * -ENODEV Hardware unavailable 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_cistatic int nx842_pseries_compress(const unsigned char *in, unsigned int inlen, 2788c2ecf20Sopenharmony_ci unsigned char *out, unsigned int *outlen, 2798c2ecf20Sopenharmony_ci void *wmem) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct nx842_devdata *local_devdata; 2828c2ecf20Sopenharmony_ci struct device *dev = NULL; 2838c2ecf20Sopenharmony_ci struct nx842_workmem *workmem; 2848c2ecf20Sopenharmony_ci struct nx842_scatterlist slin, slout; 2858c2ecf20Sopenharmony_ci struct nx_csbcpb *csbcpb; 2868c2ecf20Sopenharmony_ci int ret = 0; 2878c2ecf20Sopenharmony_ci unsigned long inbuf, outbuf; 2888c2ecf20Sopenharmony_ci struct vio_pfo_op op = { 2898c2ecf20Sopenharmony_ci .done = NULL, 2908c2ecf20Sopenharmony_ci .handle = 0, 2918c2ecf20Sopenharmony_ci .timeout = 0, 2928c2ecf20Sopenharmony_ci }; 2938c2ecf20Sopenharmony_ci unsigned long start = get_tb(); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci inbuf = (unsigned long)in; 2968c2ecf20Sopenharmony_ci if (check_constraints(inbuf, &inlen, true)) 2978c2ecf20Sopenharmony_ci return -EINVAL; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci outbuf = (unsigned long)out; 3008c2ecf20Sopenharmony_ci if (check_constraints(outbuf, outlen, false)) 3018c2ecf20Sopenharmony_ci return -EINVAL; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci rcu_read_lock(); 3048c2ecf20Sopenharmony_ci local_devdata = rcu_dereference(devdata); 3058c2ecf20Sopenharmony_ci if (!local_devdata || !local_devdata->dev) { 3068c2ecf20Sopenharmony_ci rcu_read_unlock(); 3078c2ecf20Sopenharmony_ci return -ENODEV; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci dev = local_devdata->dev; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* Init scatterlist */ 3128c2ecf20Sopenharmony_ci workmem = PTR_ALIGN(wmem, WORKMEM_ALIGN); 3138c2ecf20Sopenharmony_ci slin.entries = (struct nx842_slentry *)workmem->slin; 3148c2ecf20Sopenharmony_ci slout.entries = (struct nx842_slentry *)workmem->slout; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* Init operation */ 3178c2ecf20Sopenharmony_ci op.flags = NX842_OP_COMPRESS_CRC; 3188c2ecf20Sopenharmony_ci csbcpb = &workmem->csbcpb; 3198c2ecf20Sopenharmony_ci memset(csbcpb, 0, sizeof(*csbcpb)); 3208c2ecf20Sopenharmony_ci op.csbcpb = nx842_get_pa(csbcpb); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if ((inbuf & NX842_HW_PAGE_MASK) == 3238c2ecf20Sopenharmony_ci ((inbuf + inlen - 1) & NX842_HW_PAGE_MASK)) { 3248c2ecf20Sopenharmony_ci /* Create direct DDE */ 3258c2ecf20Sopenharmony_ci op.in = nx842_get_pa((void *)inbuf); 3268c2ecf20Sopenharmony_ci op.inlen = inlen; 3278c2ecf20Sopenharmony_ci } else { 3288c2ecf20Sopenharmony_ci /* Create indirect DDE (scatterlist) */ 3298c2ecf20Sopenharmony_ci nx842_build_scatterlist(inbuf, inlen, &slin); 3308c2ecf20Sopenharmony_ci op.in = nx842_get_pa(slin.entries); 3318c2ecf20Sopenharmony_ci op.inlen = -nx842_get_scatterlist_size(&slin); 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if ((outbuf & NX842_HW_PAGE_MASK) == 3358c2ecf20Sopenharmony_ci ((outbuf + *outlen - 1) & NX842_HW_PAGE_MASK)) { 3368c2ecf20Sopenharmony_ci /* Create direct DDE */ 3378c2ecf20Sopenharmony_ci op.out = nx842_get_pa((void *)outbuf); 3388c2ecf20Sopenharmony_ci op.outlen = *outlen; 3398c2ecf20Sopenharmony_ci } else { 3408c2ecf20Sopenharmony_ci /* Create indirect DDE (scatterlist) */ 3418c2ecf20Sopenharmony_ci nx842_build_scatterlist(outbuf, *outlen, &slout); 3428c2ecf20Sopenharmony_ci op.out = nx842_get_pa(slout.entries); 3438c2ecf20Sopenharmony_ci op.outlen = -nx842_get_scatterlist_size(&slout); 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: op.in %lx op.inlen %ld op.out %lx op.outlen %ld\n", 3478c2ecf20Sopenharmony_ci __func__, (unsigned long)op.in, (long)op.inlen, 3488c2ecf20Sopenharmony_ci (unsigned long)op.out, (long)op.outlen); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* Send request to pHyp */ 3518c2ecf20Sopenharmony_ci ret = vio_h_cop_sync(local_devdata->vdev, &op); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* Check for pHyp error */ 3548c2ecf20Sopenharmony_ci if (ret) { 3558c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: vio_h_cop_sync error (ret=%d, hret=%ld)\n", 3568c2ecf20Sopenharmony_ci __func__, ret, op.hcall_err); 3578c2ecf20Sopenharmony_ci ret = -EIO; 3588c2ecf20Sopenharmony_ci goto unlock; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* Check for hardware error */ 3628c2ecf20Sopenharmony_ci ret = nx842_validate_result(dev, &csbcpb->csb); 3638c2ecf20Sopenharmony_ci if (ret) 3648c2ecf20Sopenharmony_ci goto unlock; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci *outlen = be32_to_cpu(csbcpb->csb.processed_byte_count); 3678c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: processed_bytes=%d\n", __func__, *outlen); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ciunlock: 3708c2ecf20Sopenharmony_ci if (ret) 3718c2ecf20Sopenharmony_ci nx842_inc_comp_failed(local_devdata); 3728c2ecf20Sopenharmony_ci else { 3738c2ecf20Sopenharmony_ci nx842_inc_comp_complete(local_devdata); 3748c2ecf20Sopenharmony_ci ibm_nx842_incr_hist(local_devdata->counters->comp_times, 3758c2ecf20Sopenharmony_ci (get_tb() - start) / tb_ticks_per_usec); 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci rcu_read_unlock(); 3788c2ecf20Sopenharmony_ci return ret; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci/** 3828c2ecf20Sopenharmony_ci * nx842_pseries_decompress - Decompress data using the 842 algorithm 3838c2ecf20Sopenharmony_ci * 3848c2ecf20Sopenharmony_ci * Decompression provide by the NX842 coprocessor on IBM Power systems. 3858c2ecf20Sopenharmony_ci * The input buffer is decompressed and the result is stored in the 3868c2ecf20Sopenharmony_ci * provided output buffer. The size allocated to the output buffer is 3878c2ecf20Sopenharmony_ci * provided by the caller of this function in @outlen. Upon return from 3888c2ecf20Sopenharmony_ci * this function @outlen contains the length of the decompressed data. 3898c2ecf20Sopenharmony_ci * If there is an error then @outlen will be 0 and an error will be 3908c2ecf20Sopenharmony_ci * specified by the return code from this function. 3918c2ecf20Sopenharmony_ci * 3928c2ecf20Sopenharmony_ci * @in: Pointer to input buffer 3938c2ecf20Sopenharmony_ci * @inlen: Length of input buffer 3948c2ecf20Sopenharmony_ci * @out: Pointer to output buffer 3958c2ecf20Sopenharmony_ci * @outlen: Length of output buffer 3968c2ecf20Sopenharmony_ci * @wrkmem: ptr to buffer for working memory, size determined by 3978c2ecf20Sopenharmony_ci * nx842_pseries_driver.workmem_size 3988c2ecf20Sopenharmony_ci * 3998c2ecf20Sopenharmony_ci * Returns: 4008c2ecf20Sopenharmony_ci * 0 Success, output of length @outlen stored in the buffer at @out 4018c2ecf20Sopenharmony_ci * -ENODEV Hardware decompression device is unavailable 4028c2ecf20Sopenharmony_ci * -ENOMEM Unable to allocate internal buffers 4038c2ecf20Sopenharmony_ci * -ENOSPC Output buffer is to small 4048c2ecf20Sopenharmony_ci * -EINVAL Bad input data encountered when attempting decompress 4058c2ecf20Sopenharmony_ci * -EIO Internal error 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_cistatic int nx842_pseries_decompress(const unsigned char *in, unsigned int inlen, 4088c2ecf20Sopenharmony_ci unsigned char *out, unsigned int *outlen, 4098c2ecf20Sopenharmony_ci void *wmem) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci struct nx842_devdata *local_devdata; 4128c2ecf20Sopenharmony_ci struct device *dev = NULL; 4138c2ecf20Sopenharmony_ci struct nx842_workmem *workmem; 4148c2ecf20Sopenharmony_ci struct nx842_scatterlist slin, slout; 4158c2ecf20Sopenharmony_ci struct nx_csbcpb *csbcpb; 4168c2ecf20Sopenharmony_ci int ret = 0; 4178c2ecf20Sopenharmony_ci unsigned long inbuf, outbuf; 4188c2ecf20Sopenharmony_ci struct vio_pfo_op op = { 4198c2ecf20Sopenharmony_ci .done = NULL, 4208c2ecf20Sopenharmony_ci .handle = 0, 4218c2ecf20Sopenharmony_ci .timeout = 0, 4228c2ecf20Sopenharmony_ci }; 4238c2ecf20Sopenharmony_ci unsigned long start = get_tb(); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* Ensure page alignment and size */ 4268c2ecf20Sopenharmony_ci inbuf = (unsigned long)in; 4278c2ecf20Sopenharmony_ci if (check_constraints(inbuf, &inlen, true)) 4288c2ecf20Sopenharmony_ci return -EINVAL; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci outbuf = (unsigned long)out; 4318c2ecf20Sopenharmony_ci if (check_constraints(outbuf, outlen, false)) 4328c2ecf20Sopenharmony_ci return -EINVAL; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci rcu_read_lock(); 4358c2ecf20Sopenharmony_ci local_devdata = rcu_dereference(devdata); 4368c2ecf20Sopenharmony_ci if (!local_devdata || !local_devdata->dev) { 4378c2ecf20Sopenharmony_ci rcu_read_unlock(); 4388c2ecf20Sopenharmony_ci return -ENODEV; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci dev = local_devdata->dev; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci workmem = PTR_ALIGN(wmem, WORKMEM_ALIGN); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* Init scatterlist */ 4458c2ecf20Sopenharmony_ci slin.entries = (struct nx842_slentry *)workmem->slin; 4468c2ecf20Sopenharmony_ci slout.entries = (struct nx842_slentry *)workmem->slout; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* Init operation */ 4498c2ecf20Sopenharmony_ci op.flags = NX842_OP_DECOMPRESS_CRC; 4508c2ecf20Sopenharmony_ci csbcpb = &workmem->csbcpb; 4518c2ecf20Sopenharmony_ci memset(csbcpb, 0, sizeof(*csbcpb)); 4528c2ecf20Sopenharmony_ci op.csbcpb = nx842_get_pa(csbcpb); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if ((inbuf & NX842_HW_PAGE_MASK) == 4558c2ecf20Sopenharmony_ci ((inbuf + inlen - 1) & NX842_HW_PAGE_MASK)) { 4568c2ecf20Sopenharmony_ci /* Create direct DDE */ 4578c2ecf20Sopenharmony_ci op.in = nx842_get_pa((void *)inbuf); 4588c2ecf20Sopenharmony_ci op.inlen = inlen; 4598c2ecf20Sopenharmony_ci } else { 4608c2ecf20Sopenharmony_ci /* Create indirect DDE (scatterlist) */ 4618c2ecf20Sopenharmony_ci nx842_build_scatterlist(inbuf, inlen, &slin); 4628c2ecf20Sopenharmony_ci op.in = nx842_get_pa(slin.entries); 4638c2ecf20Sopenharmony_ci op.inlen = -nx842_get_scatterlist_size(&slin); 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if ((outbuf & NX842_HW_PAGE_MASK) == 4678c2ecf20Sopenharmony_ci ((outbuf + *outlen - 1) & NX842_HW_PAGE_MASK)) { 4688c2ecf20Sopenharmony_ci /* Create direct DDE */ 4698c2ecf20Sopenharmony_ci op.out = nx842_get_pa((void *)outbuf); 4708c2ecf20Sopenharmony_ci op.outlen = *outlen; 4718c2ecf20Sopenharmony_ci } else { 4728c2ecf20Sopenharmony_ci /* Create indirect DDE (scatterlist) */ 4738c2ecf20Sopenharmony_ci nx842_build_scatterlist(outbuf, *outlen, &slout); 4748c2ecf20Sopenharmony_ci op.out = nx842_get_pa(slout.entries); 4758c2ecf20Sopenharmony_ci op.outlen = -nx842_get_scatterlist_size(&slout); 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: op.in %lx op.inlen %ld op.out %lx op.outlen %ld\n", 4798c2ecf20Sopenharmony_ci __func__, (unsigned long)op.in, (long)op.inlen, 4808c2ecf20Sopenharmony_ci (unsigned long)op.out, (long)op.outlen); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* Send request to pHyp */ 4838c2ecf20Sopenharmony_ci ret = vio_h_cop_sync(local_devdata->vdev, &op); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* Check for pHyp error */ 4868c2ecf20Sopenharmony_ci if (ret) { 4878c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: vio_h_cop_sync error (ret=%d, hret=%ld)\n", 4888c2ecf20Sopenharmony_ci __func__, ret, op.hcall_err); 4898c2ecf20Sopenharmony_ci goto unlock; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci /* Check for hardware error */ 4938c2ecf20Sopenharmony_ci ret = nx842_validate_result(dev, &csbcpb->csb); 4948c2ecf20Sopenharmony_ci if (ret) 4958c2ecf20Sopenharmony_ci goto unlock; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci *outlen = be32_to_cpu(csbcpb->csb.processed_byte_count); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ciunlock: 5008c2ecf20Sopenharmony_ci if (ret) 5018c2ecf20Sopenharmony_ci /* decompress fail */ 5028c2ecf20Sopenharmony_ci nx842_inc_decomp_failed(local_devdata); 5038c2ecf20Sopenharmony_ci else { 5048c2ecf20Sopenharmony_ci nx842_inc_decomp_complete(local_devdata); 5058c2ecf20Sopenharmony_ci ibm_nx842_incr_hist(local_devdata->counters->decomp_times, 5068c2ecf20Sopenharmony_ci (get_tb() - start) / tb_ticks_per_usec); 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci rcu_read_unlock(); 5108c2ecf20Sopenharmony_ci return ret; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci/** 5148c2ecf20Sopenharmony_ci * nx842_OF_set_defaults -- Set default (disabled) values for devdata 5158c2ecf20Sopenharmony_ci * 5168c2ecf20Sopenharmony_ci * @devdata - struct nx842_devdata to update 5178c2ecf20Sopenharmony_ci * 5188c2ecf20Sopenharmony_ci * Returns: 5198c2ecf20Sopenharmony_ci * 0 on success 5208c2ecf20Sopenharmony_ci * -ENOENT if @devdata ptr is NULL 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_cistatic int nx842_OF_set_defaults(struct nx842_devdata *devdata) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci if (devdata) { 5258c2ecf20Sopenharmony_ci devdata->max_sync_size = 0; 5268c2ecf20Sopenharmony_ci devdata->max_sync_sg = 0; 5278c2ecf20Sopenharmony_ci devdata->max_sg_len = 0; 5288c2ecf20Sopenharmony_ci return 0; 5298c2ecf20Sopenharmony_ci } else 5308c2ecf20Sopenharmony_ci return -ENOENT; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci/** 5348c2ecf20Sopenharmony_ci * nx842_OF_upd_status -- Check the device info from OF status prop 5358c2ecf20Sopenharmony_ci * 5368c2ecf20Sopenharmony_ci * The status property indicates if the accelerator is enabled. If the 5378c2ecf20Sopenharmony_ci * device is in the OF tree it indicates that the hardware is present. 5388c2ecf20Sopenharmony_ci * The status field indicates if the device is enabled when the status 5398c2ecf20Sopenharmony_ci * is 'okay'. Otherwise the device driver will be disabled. 5408c2ecf20Sopenharmony_ci * 5418c2ecf20Sopenharmony_ci * @devdata: struct nx842_devdata to use for dev_info 5428c2ecf20Sopenharmony_ci * @prop: struct property point containing the maxsyncop for the update 5438c2ecf20Sopenharmony_ci * 5448c2ecf20Sopenharmony_ci * Returns: 5458c2ecf20Sopenharmony_ci * 0 - Device is available 5468c2ecf20Sopenharmony_ci * -ENODEV - Device is not available 5478c2ecf20Sopenharmony_ci */ 5488c2ecf20Sopenharmony_cistatic int nx842_OF_upd_status(struct nx842_devdata *devdata, 5498c2ecf20Sopenharmony_ci struct property *prop) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci const char *status = (const char *)prop->value; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (!strncmp(status, "okay", (size_t)prop->length)) 5548c2ecf20Sopenharmony_ci return 0; 5558c2ecf20Sopenharmony_ci if (!strncmp(status, "disabled", (size_t)prop->length)) 5568c2ecf20Sopenharmony_ci return -ENODEV; 5578c2ecf20Sopenharmony_ci dev_info(devdata->dev, "%s: unknown status '%s'\n", __func__, status); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci return -EINVAL; 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci/** 5638c2ecf20Sopenharmony_ci * nx842_OF_upd_maxsglen -- Update the device info from OF maxsglen prop 5648c2ecf20Sopenharmony_ci * 5658c2ecf20Sopenharmony_ci * Definition of the 'ibm,max-sg-len' OF property: 5668c2ecf20Sopenharmony_ci * This field indicates the maximum byte length of a scatter list 5678c2ecf20Sopenharmony_ci * for the platform facility. It is a single cell encoded as with encode-int. 5688c2ecf20Sopenharmony_ci * 5698c2ecf20Sopenharmony_ci * Example: 5708c2ecf20Sopenharmony_ci * # od -x ibm,max-sg-len 5718c2ecf20Sopenharmony_ci * 0000000 0000 0ff0 5728c2ecf20Sopenharmony_ci * 5738c2ecf20Sopenharmony_ci * In this example, the maximum byte length of a scatter list is 5748c2ecf20Sopenharmony_ci * 0x0ff0 (4,080). 5758c2ecf20Sopenharmony_ci * 5768c2ecf20Sopenharmony_ci * @devdata - struct nx842_devdata to update 5778c2ecf20Sopenharmony_ci * @prop - struct property point containing the maxsyncop for the update 5788c2ecf20Sopenharmony_ci * 5798c2ecf20Sopenharmony_ci * Returns: 5808c2ecf20Sopenharmony_ci * 0 on success 5818c2ecf20Sopenharmony_ci * -EINVAL on failure 5828c2ecf20Sopenharmony_ci */ 5838c2ecf20Sopenharmony_cistatic int nx842_OF_upd_maxsglen(struct nx842_devdata *devdata, 5848c2ecf20Sopenharmony_ci struct property *prop) { 5858c2ecf20Sopenharmony_ci int ret = 0; 5868c2ecf20Sopenharmony_ci const unsigned int maxsglen = of_read_number(prop->value, 1); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (prop->length != sizeof(maxsglen)) { 5898c2ecf20Sopenharmony_ci dev_err(devdata->dev, "%s: unexpected format for ibm,max-sg-len property\n", __func__); 5908c2ecf20Sopenharmony_ci dev_dbg(devdata->dev, "%s: ibm,max-sg-len is %d bytes long, expected %lu bytes\n", __func__, 5918c2ecf20Sopenharmony_ci prop->length, sizeof(maxsglen)); 5928c2ecf20Sopenharmony_ci ret = -EINVAL; 5938c2ecf20Sopenharmony_ci } else { 5948c2ecf20Sopenharmony_ci devdata->max_sg_len = min_t(unsigned int, 5958c2ecf20Sopenharmony_ci maxsglen, NX842_HW_PAGE_SIZE); 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci return ret; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci/** 6028c2ecf20Sopenharmony_ci * nx842_OF_upd_maxsyncop -- Update the device info from OF maxsyncop prop 6038c2ecf20Sopenharmony_ci * 6048c2ecf20Sopenharmony_ci * Definition of the 'ibm,max-sync-cop' OF property: 6058c2ecf20Sopenharmony_ci * Two series of cells. The first series of cells represents the maximums 6068c2ecf20Sopenharmony_ci * that can be synchronously compressed. The second series of cells 6078c2ecf20Sopenharmony_ci * represents the maximums that can be synchronously decompressed. 6088c2ecf20Sopenharmony_ci * 1. The first cell in each series contains the count of the number of 6098c2ecf20Sopenharmony_ci * data length, scatter list elements pairs that follow – each being 6108c2ecf20Sopenharmony_ci * of the form 6118c2ecf20Sopenharmony_ci * a. One cell data byte length 6128c2ecf20Sopenharmony_ci * b. One cell total number of scatter list elements 6138c2ecf20Sopenharmony_ci * 6148c2ecf20Sopenharmony_ci * Example: 6158c2ecf20Sopenharmony_ci * # od -x ibm,max-sync-cop 6168c2ecf20Sopenharmony_ci * 0000000 0000 0001 0000 1000 0000 01fe 0000 0001 6178c2ecf20Sopenharmony_ci * 0000020 0000 1000 0000 01fe 6188c2ecf20Sopenharmony_ci * 6198c2ecf20Sopenharmony_ci * In this example, compression supports 0x1000 (4,096) data byte length 6208c2ecf20Sopenharmony_ci * and 0x1fe (510) total scatter list elements. Decompression supports 6218c2ecf20Sopenharmony_ci * 0x1000 (4,096) data byte length and 0x1f3 (510) total scatter list 6228c2ecf20Sopenharmony_ci * elements. 6238c2ecf20Sopenharmony_ci * 6248c2ecf20Sopenharmony_ci * @devdata - struct nx842_devdata to update 6258c2ecf20Sopenharmony_ci * @prop - struct property point containing the maxsyncop for the update 6268c2ecf20Sopenharmony_ci * 6278c2ecf20Sopenharmony_ci * Returns: 6288c2ecf20Sopenharmony_ci * 0 on success 6298c2ecf20Sopenharmony_ci * -EINVAL on failure 6308c2ecf20Sopenharmony_ci */ 6318c2ecf20Sopenharmony_cistatic int nx842_OF_upd_maxsyncop(struct nx842_devdata *devdata, 6328c2ecf20Sopenharmony_ci struct property *prop) { 6338c2ecf20Sopenharmony_ci int ret = 0; 6348c2ecf20Sopenharmony_ci unsigned int comp_data_limit, decomp_data_limit; 6358c2ecf20Sopenharmony_ci unsigned int comp_sg_limit, decomp_sg_limit; 6368c2ecf20Sopenharmony_ci const struct maxsynccop_t { 6378c2ecf20Sopenharmony_ci __be32 comp_elements; 6388c2ecf20Sopenharmony_ci __be32 comp_data_limit; 6398c2ecf20Sopenharmony_ci __be32 comp_sg_limit; 6408c2ecf20Sopenharmony_ci __be32 decomp_elements; 6418c2ecf20Sopenharmony_ci __be32 decomp_data_limit; 6428c2ecf20Sopenharmony_ci __be32 decomp_sg_limit; 6438c2ecf20Sopenharmony_ci } *maxsynccop; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci if (prop->length != sizeof(*maxsynccop)) { 6468c2ecf20Sopenharmony_ci dev_err(devdata->dev, "%s: unexpected format for ibm,max-sync-cop property\n", __func__); 6478c2ecf20Sopenharmony_ci dev_dbg(devdata->dev, "%s: ibm,max-sync-cop is %d bytes long, expected %lu bytes\n", __func__, prop->length, 6488c2ecf20Sopenharmony_ci sizeof(*maxsynccop)); 6498c2ecf20Sopenharmony_ci ret = -EINVAL; 6508c2ecf20Sopenharmony_ci goto out; 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci maxsynccop = (const struct maxsynccop_t *)prop->value; 6548c2ecf20Sopenharmony_ci comp_data_limit = be32_to_cpu(maxsynccop->comp_data_limit); 6558c2ecf20Sopenharmony_ci comp_sg_limit = be32_to_cpu(maxsynccop->comp_sg_limit); 6568c2ecf20Sopenharmony_ci decomp_data_limit = be32_to_cpu(maxsynccop->decomp_data_limit); 6578c2ecf20Sopenharmony_ci decomp_sg_limit = be32_to_cpu(maxsynccop->decomp_sg_limit); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci /* Use one limit rather than separate limits for compression and 6608c2ecf20Sopenharmony_ci * decompression. Set a maximum for this so as not to exceed the 6618c2ecf20Sopenharmony_ci * size that the header can support and round the value down to 6628c2ecf20Sopenharmony_ci * the hardware page size (4K) */ 6638c2ecf20Sopenharmony_ci devdata->max_sync_size = min(comp_data_limit, decomp_data_limit); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci devdata->max_sync_size = min_t(unsigned int, devdata->max_sync_size, 6668c2ecf20Sopenharmony_ci 65536); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci if (devdata->max_sync_size < 4096) { 6698c2ecf20Sopenharmony_ci dev_err(devdata->dev, "%s: hardware max data size (%u) is " 6708c2ecf20Sopenharmony_ci "less than the driver minimum, unable to use " 6718c2ecf20Sopenharmony_ci "the hardware device\n", 6728c2ecf20Sopenharmony_ci __func__, devdata->max_sync_size); 6738c2ecf20Sopenharmony_ci ret = -EINVAL; 6748c2ecf20Sopenharmony_ci goto out; 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci nx842_pseries_constraints.maximum = devdata->max_sync_size; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci devdata->max_sync_sg = min(comp_sg_limit, decomp_sg_limit); 6808c2ecf20Sopenharmony_ci if (devdata->max_sync_sg < 1) { 6818c2ecf20Sopenharmony_ci dev_err(devdata->dev, "%s: hardware max sg size (%u) is " 6828c2ecf20Sopenharmony_ci "less than the driver minimum, unable to use " 6838c2ecf20Sopenharmony_ci "the hardware device\n", 6848c2ecf20Sopenharmony_ci __func__, devdata->max_sync_sg); 6858c2ecf20Sopenharmony_ci ret = -EINVAL; 6868c2ecf20Sopenharmony_ci goto out; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ciout: 6908c2ecf20Sopenharmony_ci return ret; 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci/** 6948c2ecf20Sopenharmony_ci * 6958c2ecf20Sopenharmony_ci * nx842_OF_upd -- Handle OF properties updates for the device. 6968c2ecf20Sopenharmony_ci * 6978c2ecf20Sopenharmony_ci * Set all properties from the OF tree. Optionally, a new property 6988c2ecf20Sopenharmony_ci * can be provided by the @new_prop pointer to overwrite an existing value. 6998c2ecf20Sopenharmony_ci * The device will remain disabled until all values are valid, this function 7008c2ecf20Sopenharmony_ci * will return an error for updates unless all values are valid. 7018c2ecf20Sopenharmony_ci * 7028c2ecf20Sopenharmony_ci * @new_prop: If not NULL, this property is being updated. If NULL, update 7038c2ecf20Sopenharmony_ci * all properties from the current values in the OF tree. 7048c2ecf20Sopenharmony_ci * 7058c2ecf20Sopenharmony_ci * Returns: 7068c2ecf20Sopenharmony_ci * 0 - Success 7078c2ecf20Sopenharmony_ci * -ENOMEM - Could not allocate memory for new devdata structure 7088c2ecf20Sopenharmony_ci * -EINVAL - property value not found, new_prop is not a recognized 7098c2ecf20Sopenharmony_ci * property for the device or property value is not valid. 7108c2ecf20Sopenharmony_ci * -ENODEV - Device is not available 7118c2ecf20Sopenharmony_ci */ 7128c2ecf20Sopenharmony_cistatic int nx842_OF_upd(struct property *new_prop) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci struct nx842_devdata *old_devdata = NULL; 7158c2ecf20Sopenharmony_ci struct nx842_devdata *new_devdata = NULL; 7168c2ecf20Sopenharmony_ci struct device_node *of_node = NULL; 7178c2ecf20Sopenharmony_ci struct property *status = NULL; 7188c2ecf20Sopenharmony_ci struct property *maxsglen = NULL; 7198c2ecf20Sopenharmony_ci struct property *maxsyncop = NULL; 7208c2ecf20Sopenharmony_ci int ret = 0; 7218c2ecf20Sopenharmony_ci unsigned long flags; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci new_devdata = kzalloc(sizeof(*new_devdata), GFP_NOFS); 7248c2ecf20Sopenharmony_ci if (!new_devdata) 7258c2ecf20Sopenharmony_ci return -ENOMEM; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci spin_lock_irqsave(&devdata_mutex, flags); 7288c2ecf20Sopenharmony_ci old_devdata = rcu_dereference_check(devdata, 7298c2ecf20Sopenharmony_ci lockdep_is_held(&devdata_mutex)); 7308c2ecf20Sopenharmony_ci if (old_devdata) 7318c2ecf20Sopenharmony_ci of_node = old_devdata->dev->of_node; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci if (!old_devdata || !of_node) { 7348c2ecf20Sopenharmony_ci pr_err("%s: device is not available\n", __func__); 7358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&devdata_mutex, flags); 7368c2ecf20Sopenharmony_ci kfree(new_devdata); 7378c2ecf20Sopenharmony_ci return -ENODEV; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci memcpy(new_devdata, old_devdata, sizeof(*old_devdata)); 7418c2ecf20Sopenharmony_ci new_devdata->counters = old_devdata->counters; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci /* Set ptrs for existing properties */ 7448c2ecf20Sopenharmony_ci status = of_find_property(of_node, "status", NULL); 7458c2ecf20Sopenharmony_ci maxsglen = of_find_property(of_node, "ibm,max-sg-len", NULL); 7468c2ecf20Sopenharmony_ci maxsyncop = of_find_property(of_node, "ibm,max-sync-cop", NULL); 7478c2ecf20Sopenharmony_ci if (!status || !maxsglen || !maxsyncop) { 7488c2ecf20Sopenharmony_ci dev_err(old_devdata->dev, "%s: Could not locate device properties\n", __func__); 7498c2ecf20Sopenharmony_ci ret = -EINVAL; 7508c2ecf20Sopenharmony_ci goto error_out; 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci /* 7548c2ecf20Sopenharmony_ci * If this is a property update, there are only certain properties that 7558c2ecf20Sopenharmony_ci * we care about. Bail if it isn't in the below list 7568c2ecf20Sopenharmony_ci */ 7578c2ecf20Sopenharmony_ci if (new_prop && (strncmp(new_prop->name, "status", new_prop->length) || 7588c2ecf20Sopenharmony_ci strncmp(new_prop->name, "ibm,max-sg-len", new_prop->length) || 7598c2ecf20Sopenharmony_ci strncmp(new_prop->name, "ibm,max-sync-cop", new_prop->length))) 7608c2ecf20Sopenharmony_ci goto out; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci /* Perform property updates */ 7638c2ecf20Sopenharmony_ci ret = nx842_OF_upd_status(new_devdata, status); 7648c2ecf20Sopenharmony_ci if (ret) 7658c2ecf20Sopenharmony_ci goto error_out; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci ret = nx842_OF_upd_maxsglen(new_devdata, maxsglen); 7688c2ecf20Sopenharmony_ci if (ret) 7698c2ecf20Sopenharmony_ci goto error_out; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci ret = nx842_OF_upd_maxsyncop(new_devdata, maxsyncop); 7728c2ecf20Sopenharmony_ci if (ret) 7738c2ecf20Sopenharmony_ci goto error_out; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ciout: 7768c2ecf20Sopenharmony_ci dev_info(old_devdata->dev, "%s: max_sync_size new:%u old:%u\n", 7778c2ecf20Sopenharmony_ci __func__, new_devdata->max_sync_size, 7788c2ecf20Sopenharmony_ci old_devdata->max_sync_size); 7798c2ecf20Sopenharmony_ci dev_info(old_devdata->dev, "%s: max_sync_sg new:%u old:%u\n", 7808c2ecf20Sopenharmony_ci __func__, new_devdata->max_sync_sg, 7818c2ecf20Sopenharmony_ci old_devdata->max_sync_sg); 7828c2ecf20Sopenharmony_ci dev_info(old_devdata->dev, "%s: max_sg_len new:%u old:%u\n", 7838c2ecf20Sopenharmony_ci __func__, new_devdata->max_sg_len, 7848c2ecf20Sopenharmony_ci old_devdata->max_sg_len); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci rcu_assign_pointer(devdata, new_devdata); 7878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&devdata_mutex, flags); 7888c2ecf20Sopenharmony_ci synchronize_rcu(); 7898c2ecf20Sopenharmony_ci dev_set_drvdata(new_devdata->dev, new_devdata); 7908c2ecf20Sopenharmony_ci kfree(old_devdata); 7918c2ecf20Sopenharmony_ci return 0; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_cierror_out: 7948c2ecf20Sopenharmony_ci if (new_devdata) { 7958c2ecf20Sopenharmony_ci dev_info(old_devdata->dev, "%s: device disabled\n", __func__); 7968c2ecf20Sopenharmony_ci nx842_OF_set_defaults(new_devdata); 7978c2ecf20Sopenharmony_ci rcu_assign_pointer(devdata, new_devdata); 7988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&devdata_mutex, flags); 7998c2ecf20Sopenharmony_ci synchronize_rcu(); 8008c2ecf20Sopenharmony_ci dev_set_drvdata(new_devdata->dev, new_devdata); 8018c2ecf20Sopenharmony_ci kfree(old_devdata); 8028c2ecf20Sopenharmony_ci } else { 8038c2ecf20Sopenharmony_ci dev_err(old_devdata->dev, "%s: could not update driver from hardware\n", __func__); 8048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&devdata_mutex, flags); 8058c2ecf20Sopenharmony_ci } 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci if (!ret) 8088c2ecf20Sopenharmony_ci ret = -EINVAL; 8098c2ecf20Sopenharmony_ci return ret; 8108c2ecf20Sopenharmony_ci} 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci/** 8138c2ecf20Sopenharmony_ci * nx842_OF_notifier - Process updates to OF properties for the device 8148c2ecf20Sopenharmony_ci * 8158c2ecf20Sopenharmony_ci * @np: notifier block 8168c2ecf20Sopenharmony_ci * @action: notifier action 8178c2ecf20Sopenharmony_ci * @update: struct pSeries_reconfig_prop_update pointer if action is 8188c2ecf20Sopenharmony_ci * PSERIES_UPDATE_PROPERTY 8198c2ecf20Sopenharmony_ci * 8208c2ecf20Sopenharmony_ci * Returns: 8218c2ecf20Sopenharmony_ci * NOTIFY_OK on success 8228c2ecf20Sopenharmony_ci * NOTIFY_BAD encoded with error number on failure, use 8238c2ecf20Sopenharmony_ci * notifier_to_errno() to decode this value 8248c2ecf20Sopenharmony_ci */ 8258c2ecf20Sopenharmony_cistatic int nx842_OF_notifier(struct notifier_block *np, unsigned long action, 8268c2ecf20Sopenharmony_ci void *data) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci struct of_reconfig_data *upd = data; 8298c2ecf20Sopenharmony_ci struct nx842_devdata *local_devdata; 8308c2ecf20Sopenharmony_ci struct device_node *node = NULL; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci rcu_read_lock(); 8338c2ecf20Sopenharmony_ci local_devdata = rcu_dereference(devdata); 8348c2ecf20Sopenharmony_ci if (local_devdata) 8358c2ecf20Sopenharmony_ci node = local_devdata->dev->of_node; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if (local_devdata && 8388c2ecf20Sopenharmony_ci action == OF_RECONFIG_UPDATE_PROPERTY && 8398c2ecf20Sopenharmony_ci !strcmp(upd->dn->name, node->name)) { 8408c2ecf20Sopenharmony_ci rcu_read_unlock(); 8418c2ecf20Sopenharmony_ci nx842_OF_upd(upd->prop); 8428c2ecf20Sopenharmony_ci } else 8438c2ecf20Sopenharmony_ci rcu_read_unlock(); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci return NOTIFY_OK; 8468c2ecf20Sopenharmony_ci} 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_cistatic struct notifier_block nx842_of_nb = { 8498c2ecf20Sopenharmony_ci .notifier_call = nx842_OF_notifier, 8508c2ecf20Sopenharmony_ci}; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci#define nx842_counter_read(_name) \ 8538c2ecf20Sopenharmony_cistatic ssize_t nx842_##_name##_show(struct device *dev, \ 8548c2ecf20Sopenharmony_ci struct device_attribute *attr, \ 8558c2ecf20Sopenharmony_ci char *buf) { \ 8568c2ecf20Sopenharmony_ci struct nx842_devdata *local_devdata; \ 8578c2ecf20Sopenharmony_ci int p = 0; \ 8588c2ecf20Sopenharmony_ci rcu_read_lock(); \ 8598c2ecf20Sopenharmony_ci local_devdata = rcu_dereference(devdata); \ 8608c2ecf20Sopenharmony_ci if (local_devdata) \ 8618c2ecf20Sopenharmony_ci p = snprintf(buf, PAGE_SIZE, "%lld\n", \ 8628c2ecf20Sopenharmony_ci atomic64_read(&local_devdata->counters->_name)); \ 8638c2ecf20Sopenharmony_ci rcu_read_unlock(); \ 8648c2ecf20Sopenharmony_ci return p; \ 8658c2ecf20Sopenharmony_ci} 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci#define NX842DEV_COUNTER_ATTR_RO(_name) \ 8688c2ecf20Sopenharmony_ci nx842_counter_read(_name); \ 8698c2ecf20Sopenharmony_ci static struct device_attribute dev_attr_##_name = __ATTR(_name, \ 8708c2ecf20Sopenharmony_ci 0444, \ 8718c2ecf20Sopenharmony_ci nx842_##_name##_show,\ 8728c2ecf20Sopenharmony_ci NULL); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ciNX842DEV_COUNTER_ATTR_RO(comp_complete); 8758c2ecf20Sopenharmony_ciNX842DEV_COUNTER_ATTR_RO(comp_failed); 8768c2ecf20Sopenharmony_ciNX842DEV_COUNTER_ATTR_RO(decomp_complete); 8778c2ecf20Sopenharmony_ciNX842DEV_COUNTER_ATTR_RO(decomp_failed); 8788c2ecf20Sopenharmony_ciNX842DEV_COUNTER_ATTR_RO(swdecomp); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_cistatic ssize_t nx842_timehist_show(struct device *, 8818c2ecf20Sopenharmony_ci struct device_attribute *, char *); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_cistatic struct device_attribute dev_attr_comp_times = __ATTR(comp_times, 0444, 8848c2ecf20Sopenharmony_ci nx842_timehist_show, NULL); 8858c2ecf20Sopenharmony_cistatic struct device_attribute dev_attr_decomp_times = __ATTR(decomp_times, 8868c2ecf20Sopenharmony_ci 0444, nx842_timehist_show, NULL); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_cistatic ssize_t nx842_timehist_show(struct device *dev, 8898c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) { 8908c2ecf20Sopenharmony_ci char *p = buf; 8918c2ecf20Sopenharmony_ci struct nx842_devdata *local_devdata; 8928c2ecf20Sopenharmony_ci atomic64_t *times; 8938c2ecf20Sopenharmony_ci int bytes_remain = PAGE_SIZE; 8948c2ecf20Sopenharmony_ci int bytes; 8958c2ecf20Sopenharmony_ci int i; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci rcu_read_lock(); 8988c2ecf20Sopenharmony_ci local_devdata = rcu_dereference(devdata); 8998c2ecf20Sopenharmony_ci if (!local_devdata) { 9008c2ecf20Sopenharmony_ci rcu_read_unlock(); 9018c2ecf20Sopenharmony_ci return 0; 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci if (attr == &dev_attr_comp_times) 9058c2ecf20Sopenharmony_ci times = local_devdata->counters->comp_times; 9068c2ecf20Sopenharmony_ci else if (attr == &dev_attr_decomp_times) 9078c2ecf20Sopenharmony_ci times = local_devdata->counters->decomp_times; 9088c2ecf20Sopenharmony_ci else { 9098c2ecf20Sopenharmony_ci rcu_read_unlock(); 9108c2ecf20Sopenharmony_ci return 0; 9118c2ecf20Sopenharmony_ci } 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci for (i = 0; i < (NX842_HIST_SLOTS - 2); i++) { 9148c2ecf20Sopenharmony_ci bytes = snprintf(p, bytes_remain, "%u-%uus:\t%lld\n", 9158c2ecf20Sopenharmony_ci i ? (2<<(i-1)) : 0, (2<<i)-1, 9168c2ecf20Sopenharmony_ci atomic64_read(×[i])); 9178c2ecf20Sopenharmony_ci bytes_remain -= bytes; 9188c2ecf20Sopenharmony_ci p += bytes; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci /* The last bucket holds everything over 9218c2ecf20Sopenharmony_ci * 2<<(NX842_HIST_SLOTS - 2) us */ 9228c2ecf20Sopenharmony_ci bytes = snprintf(p, bytes_remain, "%uus - :\t%lld\n", 9238c2ecf20Sopenharmony_ci 2<<(NX842_HIST_SLOTS - 2), 9248c2ecf20Sopenharmony_ci atomic64_read(×[(NX842_HIST_SLOTS - 1)])); 9258c2ecf20Sopenharmony_ci p += bytes; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci rcu_read_unlock(); 9288c2ecf20Sopenharmony_ci return p - buf; 9298c2ecf20Sopenharmony_ci} 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_cistatic struct attribute *nx842_sysfs_entries[] = { 9328c2ecf20Sopenharmony_ci &dev_attr_comp_complete.attr, 9338c2ecf20Sopenharmony_ci &dev_attr_comp_failed.attr, 9348c2ecf20Sopenharmony_ci &dev_attr_decomp_complete.attr, 9358c2ecf20Sopenharmony_ci &dev_attr_decomp_failed.attr, 9368c2ecf20Sopenharmony_ci &dev_attr_swdecomp.attr, 9378c2ecf20Sopenharmony_ci &dev_attr_comp_times.attr, 9388c2ecf20Sopenharmony_ci &dev_attr_decomp_times.attr, 9398c2ecf20Sopenharmony_ci NULL, 9408c2ecf20Sopenharmony_ci}; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_cistatic struct attribute_group nx842_attribute_group = { 9438c2ecf20Sopenharmony_ci .name = NULL, /* put in device directory */ 9448c2ecf20Sopenharmony_ci .attrs = nx842_sysfs_entries, 9458c2ecf20Sopenharmony_ci}; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_cistatic struct nx842_driver nx842_pseries_driver = { 9488c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 9498c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 9508c2ecf20Sopenharmony_ci .workmem_size = sizeof(struct nx842_workmem), 9518c2ecf20Sopenharmony_ci .constraints = &nx842_pseries_constraints, 9528c2ecf20Sopenharmony_ci .compress = nx842_pseries_compress, 9538c2ecf20Sopenharmony_ci .decompress = nx842_pseries_decompress, 9548c2ecf20Sopenharmony_ci}; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_cistatic int nx842_pseries_crypto_init(struct crypto_tfm *tfm) 9578c2ecf20Sopenharmony_ci{ 9588c2ecf20Sopenharmony_ci return nx842_crypto_init(tfm, &nx842_pseries_driver); 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_cistatic struct crypto_alg nx842_pseries_alg = { 9628c2ecf20Sopenharmony_ci .cra_name = "842", 9638c2ecf20Sopenharmony_ci .cra_driver_name = "842-nx", 9648c2ecf20Sopenharmony_ci .cra_priority = 300, 9658c2ecf20Sopenharmony_ci .cra_flags = CRYPTO_ALG_TYPE_COMPRESS, 9668c2ecf20Sopenharmony_ci .cra_ctxsize = sizeof(struct nx842_crypto_ctx), 9678c2ecf20Sopenharmony_ci .cra_module = THIS_MODULE, 9688c2ecf20Sopenharmony_ci .cra_init = nx842_pseries_crypto_init, 9698c2ecf20Sopenharmony_ci .cra_exit = nx842_crypto_exit, 9708c2ecf20Sopenharmony_ci .cra_u = { .compress = { 9718c2ecf20Sopenharmony_ci .coa_compress = nx842_crypto_compress, 9728c2ecf20Sopenharmony_ci .coa_decompress = nx842_crypto_decompress } } 9738c2ecf20Sopenharmony_ci}; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_cistatic int nx842_probe(struct vio_dev *viodev, 9768c2ecf20Sopenharmony_ci const struct vio_device_id *id) 9778c2ecf20Sopenharmony_ci{ 9788c2ecf20Sopenharmony_ci struct nx842_devdata *old_devdata, *new_devdata = NULL; 9798c2ecf20Sopenharmony_ci unsigned long flags; 9808c2ecf20Sopenharmony_ci int ret = 0; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci new_devdata = kzalloc(sizeof(*new_devdata), GFP_NOFS); 9838c2ecf20Sopenharmony_ci if (!new_devdata) 9848c2ecf20Sopenharmony_ci return -ENOMEM; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci new_devdata->counters = kzalloc(sizeof(*new_devdata->counters), 9878c2ecf20Sopenharmony_ci GFP_NOFS); 9888c2ecf20Sopenharmony_ci if (!new_devdata->counters) { 9898c2ecf20Sopenharmony_ci kfree(new_devdata); 9908c2ecf20Sopenharmony_ci return -ENOMEM; 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci spin_lock_irqsave(&devdata_mutex, flags); 9948c2ecf20Sopenharmony_ci old_devdata = rcu_dereference_check(devdata, 9958c2ecf20Sopenharmony_ci lockdep_is_held(&devdata_mutex)); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci if (old_devdata && old_devdata->vdev != NULL) { 9988c2ecf20Sopenharmony_ci dev_err(&viodev->dev, "%s: Attempt to register more than one instance of the hardware\n", __func__); 9998c2ecf20Sopenharmony_ci ret = -1; 10008c2ecf20Sopenharmony_ci goto error_unlock; 10018c2ecf20Sopenharmony_ci } 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci dev_set_drvdata(&viodev->dev, NULL); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci new_devdata->vdev = viodev; 10068c2ecf20Sopenharmony_ci new_devdata->dev = &viodev->dev; 10078c2ecf20Sopenharmony_ci nx842_OF_set_defaults(new_devdata); 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci rcu_assign_pointer(devdata, new_devdata); 10108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&devdata_mutex, flags); 10118c2ecf20Sopenharmony_ci synchronize_rcu(); 10128c2ecf20Sopenharmony_ci kfree(old_devdata); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci of_reconfig_notifier_register(&nx842_of_nb); 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci ret = nx842_OF_upd(NULL); 10178c2ecf20Sopenharmony_ci if (ret) 10188c2ecf20Sopenharmony_ci goto error; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci ret = crypto_register_alg(&nx842_pseries_alg); 10218c2ecf20Sopenharmony_ci if (ret) { 10228c2ecf20Sopenharmony_ci dev_err(&viodev->dev, "could not register comp alg: %d\n", ret); 10238c2ecf20Sopenharmony_ci goto error; 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci rcu_read_lock(); 10278c2ecf20Sopenharmony_ci dev_set_drvdata(&viodev->dev, rcu_dereference(devdata)); 10288c2ecf20Sopenharmony_ci rcu_read_unlock(); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci if (sysfs_create_group(&viodev->dev.kobj, &nx842_attribute_group)) { 10318c2ecf20Sopenharmony_ci dev_err(&viodev->dev, "could not create sysfs device attributes\n"); 10328c2ecf20Sopenharmony_ci ret = -1; 10338c2ecf20Sopenharmony_ci goto error; 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci return 0; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_cierror_unlock: 10398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&devdata_mutex, flags); 10408c2ecf20Sopenharmony_ci if (new_devdata) 10418c2ecf20Sopenharmony_ci kfree(new_devdata->counters); 10428c2ecf20Sopenharmony_ci kfree(new_devdata); 10438c2ecf20Sopenharmony_cierror: 10448c2ecf20Sopenharmony_ci return ret; 10458c2ecf20Sopenharmony_ci} 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_cistatic int nx842_remove(struct vio_dev *viodev) 10488c2ecf20Sopenharmony_ci{ 10498c2ecf20Sopenharmony_ci struct nx842_devdata *old_devdata; 10508c2ecf20Sopenharmony_ci unsigned long flags; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci pr_info("Removing IBM Power 842 compression device\n"); 10538c2ecf20Sopenharmony_ci sysfs_remove_group(&viodev->dev.kobj, &nx842_attribute_group); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci crypto_unregister_alg(&nx842_pseries_alg); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci spin_lock_irqsave(&devdata_mutex, flags); 10588c2ecf20Sopenharmony_ci old_devdata = rcu_dereference_check(devdata, 10598c2ecf20Sopenharmony_ci lockdep_is_held(&devdata_mutex)); 10608c2ecf20Sopenharmony_ci of_reconfig_notifier_unregister(&nx842_of_nb); 10618c2ecf20Sopenharmony_ci RCU_INIT_POINTER(devdata, NULL); 10628c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&devdata_mutex, flags); 10638c2ecf20Sopenharmony_ci synchronize_rcu(); 10648c2ecf20Sopenharmony_ci dev_set_drvdata(&viodev->dev, NULL); 10658c2ecf20Sopenharmony_ci if (old_devdata) 10668c2ecf20Sopenharmony_ci kfree(old_devdata->counters); 10678c2ecf20Sopenharmony_ci kfree(old_devdata); 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci return 0; 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_cistatic const struct vio_device_id nx842_vio_driver_ids[] = { 10738c2ecf20Sopenharmony_ci {"ibm,compression-v1", "ibm,compression"}, 10748c2ecf20Sopenharmony_ci {"", ""}, 10758c2ecf20Sopenharmony_ci}; 10768c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(vio, nx842_vio_driver_ids); 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_cistatic struct vio_driver nx842_vio_driver = { 10798c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 10808c2ecf20Sopenharmony_ci .probe = nx842_probe, 10818c2ecf20Sopenharmony_ci .remove = nx842_remove, 10828c2ecf20Sopenharmony_ci .get_desired_dma = nx842_get_desired_dma, 10838c2ecf20Sopenharmony_ci .id_table = nx842_vio_driver_ids, 10848c2ecf20Sopenharmony_ci}; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_cistatic int __init nx842_pseries_init(void) 10878c2ecf20Sopenharmony_ci{ 10888c2ecf20Sopenharmony_ci struct nx842_devdata *new_devdata; 10898c2ecf20Sopenharmony_ci int ret; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci if (!of_find_compatible_node(NULL, NULL, "ibm,compression")) 10928c2ecf20Sopenharmony_ci return -ENODEV; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci RCU_INIT_POINTER(devdata, NULL); 10958c2ecf20Sopenharmony_ci new_devdata = kzalloc(sizeof(*new_devdata), GFP_KERNEL); 10968c2ecf20Sopenharmony_ci if (!new_devdata) 10978c2ecf20Sopenharmony_ci return -ENOMEM; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci RCU_INIT_POINTER(devdata, new_devdata); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci ret = vio_register_driver(&nx842_vio_driver); 11028c2ecf20Sopenharmony_ci if (ret) { 11038c2ecf20Sopenharmony_ci pr_err("Could not register VIO driver %d\n", ret); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci kfree(new_devdata); 11068c2ecf20Sopenharmony_ci return ret; 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci return 0; 11108c2ecf20Sopenharmony_ci} 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_cimodule_init(nx842_pseries_init); 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_cistatic void __exit nx842_pseries_exit(void) 11158c2ecf20Sopenharmony_ci{ 11168c2ecf20Sopenharmony_ci struct nx842_devdata *old_devdata; 11178c2ecf20Sopenharmony_ci unsigned long flags; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci crypto_unregister_alg(&nx842_pseries_alg); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci spin_lock_irqsave(&devdata_mutex, flags); 11228c2ecf20Sopenharmony_ci old_devdata = rcu_dereference_check(devdata, 11238c2ecf20Sopenharmony_ci lockdep_is_held(&devdata_mutex)); 11248c2ecf20Sopenharmony_ci RCU_INIT_POINTER(devdata, NULL); 11258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&devdata_mutex, flags); 11268c2ecf20Sopenharmony_ci synchronize_rcu(); 11278c2ecf20Sopenharmony_ci if (old_devdata && old_devdata->dev) 11288c2ecf20Sopenharmony_ci dev_set_drvdata(old_devdata->dev, NULL); 11298c2ecf20Sopenharmony_ci kfree(old_devdata); 11308c2ecf20Sopenharmony_ci vio_unregister_driver(&nx842_vio_driver); 11318c2ecf20Sopenharmony_ci} 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_cimodule_exit(nx842_pseries_exit); 11348c2ecf20Sopenharmony_ci 1135