162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for IBM Power 842 compression accelerator 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) IBM Corporation, 2012 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Authors: Robert Jennings <rcj@linux.vnet.ibm.com> 862306a36Sopenharmony_ci * Seth Jennings <sjenning@linux.vnet.ibm.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <asm/vio.h> 1262306a36Sopenharmony_ci#include <asm/hvcall.h> 1362306a36Sopenharmony_ci#include <asm/vas.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "nx-842.h" 1662306a36Sopenharmony_ci#include "nx_csbcpb.h" /* struct nx_csbcpb */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1962306a36Sopenharmony_ciMODULE_AUTHOR("Robert Jennings <rcj@linux.vnet.ibm.com>"); 2062306a36Sopenharmony_ciMODULE_DESCRIPTION("842 H/W Compression driver for IBM Power processors"); 2162306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("842"); 2262306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("842-nx"); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * Coprocessor type specific capabilities from the hypervisor. 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_cistruct hv_nx_cop_caps { 2862306a36Sopenharmony_ci __be64 descriptor; 2962306a36Sopenharmony_ci __be64 req_max_processed_len; /* Max bytes in one GZIP request */ 3062306a36Sopenharmony_ci __be64 min_compress_len; /* Min compression size in bytes */ 3162306a36Sopenharmony_ci __be64 min_decompress_len; /* Min decompression size in bytes */ 3262306a36Sopenharmony_ci} __packed __aligned(0x1000); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * Coprocessor type specific capabilities. 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_cistruct nx_cop_caps { 3862306a36Sopenharmony_ci u64 descriptor; 3962306a36Sopenharmony_ci u64 req_max_processed_len; /* Max bytes in one GZIP request */ 4062306a36Sopenharmony_ci u64 min_compress_len; /* Min compression in bytes */ 4162306a36Sopenharmony_ci u64 min_decompress_len; /* Min decompression in bytes */ 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic u64 caps_feat; 4562306a36Sopenharmony_cistatic struct nx_cop_caps nx_cop_caps; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic struct nx842_constraints nx842_pseries_constraints = { 4862306a36Sopenharmony_ci .alignment = DDE_BUFFER_ALIGN, 4962306a36Sopenharmony_ci .multiple = DDE_BUFFER_LAST_MULT, 5062306a36Sopenharmony_ci .minimum = DDE_BUFFER_LAST_MULT, 5162306a36Sopenharmony_ci .maximum = PAGE_SIZE, /* dynamic, max_sync_size */ 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic int check_constraints(unsigned long buf, unsigned int *len, bool in) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci if (!IS_ALIGNED(buf, nx842_pseries_constraints.alignment)) { 5762306a36Sopenharmony_ci pr_debug("%s buffer 0x%lx not aligned to 0x%x\n", 5862306a36Sopenharmony_ci in ? "input" : "output", buf, 5962306a36Sopenharmony_ci nx842_pseries_constraints.alignment); 6062306a36Sopenharmony_ci return -EINVAL; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci if (*len % nx842_pseries_constraints.multiple) { 6362306a36Sopenharmony_ci pr_debug("%s buffer len 0x%x not multiple of 0x%x\n", 6462306a36Sopenharmony_ci in ? "input" : "output", *len, 6562306a36Sopenharmony_ci nx842_pseries_constraints.multiple); 6662306a36Sopenharmony_ci if (in) 6762306a36Sopenharmony_ci return -EINVAL; 6862306a36Sopenharmony_ci *len = round_down(*len, nx842_pseries_constraints.multiple); 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci if (*len < nx842_pseries_constraints.minimum) { 7162306a36Sopenharmony_ci pr_debug("%s buffer len 0x%x under minimum 0x%x\n", 7262306a36Sopenharmony_ci in ? "input" : "output", *len, 7362306a36Sopenharmony_ci nx842_pseries_constraints.minimum); 7462306a36Sopenharmony_ci return -EINVAL; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci if (*len > nx842_pseries_constraints.maximum) { 7762306a36Sopenharmony_ci pr_debug("%s buffer len 0x%x over maximum 0x%x\n", 7862306a36Sopenharmony_ci in ? "input" : "output", *len, 7962306a36Sopenharmony_ci nx842_pseries_constraints.maximum); 8062306a36Sopenharmony_ci if (in) 8162306a36Sopenharmony_ci return -EINVAL; 8262306a36Sopenharmony_ci *len = nx842_pseries_constraints.maximum; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* I assume we need to align the CSB? */ 8862306a36Sopenharmony_ci#define WORKMEM_ALIGN (256) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistruct nx842_workmem { 9162306a36Sopenharmony_ci /* scatterlist */ 9262306a36Sopenharmony_ci char slin[4096]; 9362306a36Sopenharmony_ci char slout[4096]; 9462306a36Sopenharmony_ci /* coprocessor status/parameter block */ 9562306a36Sopenharmony_ci struct nx_csbcpb csbcpb; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci char padding[WORKMEM_ALIGN]; 9862306a36Sopenharmony_ci} __aligned(WORKMEM_ALIGN); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* Macros for fields within nx_csbcpb */ 10162306a36Sopenharmony_ci/* Check the valid bit within the csbcpb valid field */ 10262306a36Sopenharmony_ci#define NX842_CSBCBP_VALID_CHK(x) (x & BIT_MASK(7)) 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* CE macros operate on the completion_extension field bits in the csbcpb. 10562306a36Sopenharmony_ci * CE0 0=full completion, 1=partial completion 10662306a36Sopenharmony_ci * CE1 0=CE0 indicates completion, 1=termination (output may be modified) 10762306a36Sopenharmony_ci * CE2 0=processed_bytes is source bytes, 1=processed_bytes is target bytes */ 10862306a36Sopenharmony_ci#define NX842_CSBCPB_CE0(x) (x & BIT_MASK(7)) 10962306a36Sopenharmony_ci#define NX842_CSBCPB_CE1(x) (x & BIT_MASK(6)) 11062306a36Sopenharmony_ci#define NX842_CSBCPB_CE2(x) (x & BIT_MASK(5)) 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* The NX unit accepts data only on 4K page boundaries */ 11362306a36Sopenharmony_ci#define NX842_HW_PAGE_SIZE (4096) 11462306a36Sopenharmony_ci#define NX842_HW_PAGE_MASK (~(NX842_HW_PAGE_SIZE-1)) 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistruct ibm_nx842_counters { 11762306a36Sopenharmony_ci atomic64_t comp_complete; 11862306a36Sopenharmony_ci atomic64_t comp_failed; 11962306a36Sopenharmony_ci atomic64_t decomp_complete; 12062306a36Sopenharmony_ci atomic64_t decomp_failed; 12162306a36Sopenharmony_ci atomic64_t swdecomp; 12262306a36Sopenharmony_ci atomic64_t comp_times[32]; 12362306a36Sopenharmony_ci atomic64_t decomp_times[32]; 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistruct nx842_devdata { 12762306a36Sopenharmony_ci struct vio_dev *vdev; 12862306a36Sopenharmony_ci struct device *dev; 12962306a36Sopenharmony_ci struct ibm_nx842_counters *counters; 13062306a36Sopenharmony_ci unsigned int max_sg_len; 13162306a36Sopenharmony_ci unsigned int max_sync_size; 13262306a36Sopenharmony_ci unsigned int max_sync_sg; 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic struct nx842_devdata __rcu *devdata; 13662306a36Sopenharmony_cistatic DEFINE_SPINLOCK(devdata_mutex); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci#define NX842_COUNTER_INC(_x) \ 13962306a36Sopenharmony_cistatic inline void nx842_inc_##_x( \ 14062306a36Sopenharmony_ci const struct nx842_devdata *dev) { \ 14162306a36Sopenharmony_ci if (dev) \ 14262306a36Sopenharmony_ci atomic64_inc(&dev->counters->_x); \ 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ciNX842_COUNTER_INC(comp_complete); 14562306a36Sopenharmony_ciNX842_COUNTER_INC(comp_failed); 14662306a36Sopenharmony_ciNX842_COUNTER_INC(decomp_complete); 14762306a36Sopenharmony_ciNX842_COUNTER_INC(decomp_failed); 14862306a36Sopenharmony_ciNX842_COUNTER_INC(swdecomp); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci#define NX842_HIST_SLOTS 16 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic void ibm_nx842_incr_hist(atomic64_t *times, unsigned int time) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci int bucket = fls(time); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (bucket) 15762306a36Sopenharmony_ci bucket = min((NX842_HIST_SLOTS - 1), bucket - 1); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci atomic64_inc(×[bucket]); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/* NX unit operation flags */ 16362306a36Sopenharmony_ci#define NX842_OP_COMPRESS 0x0 16462306a36Sopenharmony_ci#define NX842_OP_CRC 0x1 16562306a36Sopenharmony_ci#define NX842_OP_DECOMPRESS 0x2 16662306a36Sopenharmony_ci#define NX842_OP_COMPRESS_CRC (NX842_OP_COMPRESS | NX842_OP_CRC) 16762306a36Sopenharmony_ci#define NX842_OP_DECOMPRESS_CRC (NX842_OP_DECOMPRESS | NX842_OP_CRC) 16862306a36Sopenharmony_ci#define NX842_OP_ASYNC (1<<23) 16962306a36Sopenharmony_ci#define NX842_OP_NOTIFY (1<<22) 17062306a36Sopenharmony_ci#define NX842_OP_NOTIFY_INT(x) ((x & 0xff)<<8) 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic unsigned long nx842_get_desired_dma(struct vio_dev *viodev) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci /* No use of DMA mappings within the driver. */ 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistruct nx842_slentry { 17962306a36Sopenharmony_ci __be64 ptr; /* Real address (use __pa()) */ 18062306a36Sopenharmony_ci __be64 len; 18162306a36Sopenharmony_ci}; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/* pHyp scatterlist entry */ 18462306a36Sopenharmony_cistruct nx842_scatterlist { 18562306a36Sopenharmony_ci int entry_nr; /* number of slentries */ 18662306a36Sopenharmony_ci struct nx842_slentry *entries; /* ptr to array of slentries */ 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/* Does not include sizeof(entry_nr) in the size */ 19062306a36Sopenharmony_cistatic inline unsigned long nx842_get_scatterlist_size( 19162306a36Sopenharmony_ci struct nx842_scatterlist *sl) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci return sl->entry_nr * sizeof(struct nx842_slentry); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic int nx842_build_scatterlist(unsigned long buf, int len, 19762306a36Sopenharmony_ci struct nx842_scatterlist *sl) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci unsigned long entrylen; 20062306a36Sopenharmony_ci struct nx842_slentry *entry; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci sl->entry_nr = 0; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci entry = sl->entries; 20562306a36Sopenharmony_ci while (len) { 20662306a36Sopenharmony_ci entry->ptr = cpu_to_be64(nx842_get_pa((void *)buf)); 20762306a36Sopenharmony_ci entrylen = min_t(int, len, 20862306a36Sopenharmony_ci LEN_ON_SIZE(buf, NX842_HW_PAGE_SIZE)); 20962306a36Sopenharmony_ci entry->len = cpu_to_be64(entrylen); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci len -= entrylen; 21262306a36Sopenharmony_ci buf += entrylen; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci sl->entry_nr++; 21562306a36Sopenharmony_ci entry++; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic int nx842_validate_result(struct device *dev, 22262306a36Sopenharmony_ci struct cop_status_block *csb) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci /* The csb must be valid after returning from vio_h_cop_sync */ 22562306a36Sopenharmony_ci if (!NX842_CSBCBP_VALID_CHK(csb->valid)) { 22662306a36Sopenharmony_ci dev_err(dev, "%s: cspcbp not valid upon completion.\n", 22762306a36Sopenharmony_ci __func__); 22862306a36Sopenharmony_ci dev_dbg(dev, "valid:0x%02x cs:0x%02x cc:0x%02x ce:0x%02x\n", 22962306a36Sopenharmony_ci csb->valid, 23062306a36Sopenharmony_ci csb->crb_seq_number, 23162306a36Sopenharmony_ci csb->completion_code, 23262306a36Sopenharmony_ci csb->completion_extension); 23362306a36Sopenharmony_ci dev_dbg(dev, "processed_bytes:%d address:0x%016lx\n", 23462306a36Sopenharmony_ci be32_to_cpu(csb->processed_byte_count), 23562306a36Sopenharmony_ci (unsigned long)be64_to_cpu(csb->address)); 23662306a36Sopenharmony_ci return -EIO; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* Check return values from the hardware in the CSB */ 24062306a36Sopenharmony_ci switch (csb->completion_code) { 24162306a36Sopenharmony_ci case 0: /* Completed without error */ 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci case 64: /* Compression ok, but output larger than input */ 24462306a36Sopenharmony_ci dev_dbg(dev, "%s: output size larger than input size\n", 24562306a36Sopenharmony_ci __func__); 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci case 13: /* Output buffer too small */ 24862306a36Sopenharmony_ci dev_dbg(dev, "%s: Out of space in output buffer\n", 24962306a36Sopenharmony_ci __func__); 25062306a36Sopenharmony_ci return -ENOSPC; 25162306a36Sopenharmony_ci case 65: /* Calculated CRC doesn't match the passed value */ 25262306a36Sopenharmony_ci dev_dbg(dev, "%s: CRC mismatch for decompression\n", 25362306a36Sopenharmony_ci __func__); 25462306a36Sopenharmony_ci return -EINVAL; 25562306a36Sopenharmony_ci case 66: /* Input data contains an illegal template field */ 25662306a36Sopenharmony_ci case 67: /* Template indicates data past the end of the input stream */ 25762306a36Sopenharmony_ci dev_dbg(dev, "%s: Bad data for decompression (code:%d)\n", 25862306a36Sopenharmony_ci __func__, csb->completion_code); 25962306a36Sopenharmony_ci return -EINVAL; 26062306a36Sopenharmony_ci default: 26162306a36Sopenharmony_ci dev_dbg(dev, "%s: Unspecified error (code:%d)\n", 26262306a36Sopenharmony_ci __func__, csb->completion_code); 26362306a36Sopenharmony_ci return -EIO; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* Hardware sanity check */ 26762306a36Sopenharmony_ci if (!NX842_CSBCPB_CE2(csb->completion_extension)) { 26862306a36Sopenharmony_ci dev_err(dev, "%s: No error returned by hardware, but " 26962306a36Sopenharmony_ci "data returned is unusable, contact support.\n" 27062306a36Sopenharmony_ci "(Additional info: csbcbp->processed bytes " 27162306a36Sopenharmony_ci "does not specify processed bytes for the " 27262306a36Sopenharmony_ci "target buffer.)\n", __func__); 27362306a36Sopenharmony_ci return -EIO; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return 0; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/** 28062306a36Sopenharmony_ci * nx842_pseries_compress - Compress data using the 842 algorithm 28162306a36Sopenharmony_ci * 28262306a36Sopenharmony_ci * Compression provide by the NX842 coprocessor on IBM Power systems. 28362306a36Sopenharmony_ci * The input buffer is compressed and the result is stored in the 28462306a36Sopenharmony_ci * provided output buffer. 28562306a36Sopenharmony_ci * 28662306a36Sopenharmony_ci * Upon return from this function @outlen contains the length of the 28762306a36Sopenharmony_ci * compressed data. If there is an error then @outlen will be 0 and an 28862306a36Sopenharmony_ci * error will be specified by the return code from this function. 28962306a36Sopenharmony_ci * 29062306a36Sopenharmony_ci * @in: Pointer to input buffer 29162306a36Sopenharmony_ci * @inlen: Length of input buffer 29262306a36Sopenharmony_ci * @out: Pointer to output buffer 29362306a36Sopenharmony_ci * @outlen: Length of output buffer 29462306a36Sopenharmony_ci * @wmem: ptr to buffer for working memory, size determined by 29562306a36Sopenharmony_ci * nx842_pseries_driver.workmem_size 29662306a36Sopenharmony_ci * 29762306a36Sopenharmony_ci * Returns: 29862306a36Sopenharmony_ci * 0 Success, output of length @outlen stored in the buffer at @out 29962306a36Sopenharmony_ci * -ENOMEM Unable to allocate internal buffers 30062306a36Sopenharmony_ci * -ENOSPC Output buffer is to small 30162306a36Sopenharmony_ci * -EIO Internal error 30262306a36Sopenharmony_ci * -ENODEV Hardware unavailable 30362306a36Sopenharmony_ci */ 30462306a36Sopenharmony_cistatic int nx842_pseries_compress(const unsigned char *in, unsigned int inlen, 30562306a36Sopenharmony_ci unsigned char *out, unsigned int *outlen, 30662306a36Sopenharmony_ci void *wmem) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct nx842_devdata *local_devdata; 30962306a36Sopenharmony_ci struct device *dev = NULL; 31062306a36Sopenharmony_ci struct nx842_workmem *workmem; 31162306a36Sopenharmony_ci struct nx842_scatterlist slin, slout; 31262306a36Sopenharmony_ci struct nx_csbcpb *csbcpb; 31362306a36Sopenharmony_ci int ret = 0; 31462306a36Sopenharmony_ci unsigned long inbuf, outbuf; 31562306a36Sopenharmony_ci struct vio_pfo_op op = { 31662306a36Sopenharmony_ci .done = NULL, 31762306a36Sopenharmony_ci .handle = 0, 31862306a36Sopenharmony_ci .timeout = 0, 31962306a36Sopenharmony_ci }; 32062306a36Sopenharmony_ci unsigned long start = get_tb(); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci inbuf = (unsigned long)in; 32362306a36Sopenharmony_ci if (check_constraints(inbuf, &inlen, true)) 32462306a36Sopenharmony_ci return -EINVAL; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci outbuf = (unsigned long)out; 32762306a36Sopenharmony_ci if (check_constraints(outbuf, outlen, false)) 32862306a36Sopenharmony_ci return -EINVAL; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci rcu_read_lock(); 33162306a36Sopenharmony_ci local_devdata = rcu_dereference(devdata); 33262306a36Sopenharmony_ci if (!local_devdata || !local_devdata->dev) { 33362306a36Sopenharmony_ci rcu_read_unlock(); 33462306a36Sopenharmony_ci return -ENODEV; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci dev = local_devdata->dev; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* Init scatterlist */ 33962306a36Sopenharmony_ci workmem = PTR_ALIGN(wmem, WORKMEM_ALIGN); 34062306a36Sopenharmony_ci slin.entries = (struct nx842_slentry *)workmem->slin; 34162306a36Sopenharmony_ci slout.entries = (struct nx842_slentry *)workmem->slout; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* Init operation */ 34462306a36Sopenharmony_ci op.flags = NX842_OP_COMPRESS_CRC; 34562306a36Sopenharmony_ci csbcpb = &workmem->csbcpb; 34662306a36Sopenharmony_ci memset(csbcpb, 0, sizeof(*csbcpb)); 34762306a36Sopenharmony_ci op.csbcpb = nx842_get_pa(csbcpb); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if ((inbuf & NX842_HW_PAGE_MASK) == 35062306a36Sopenharmony_ci ((inbuf + inlen - 1) & NX842_HW_PAGE_MASK)) { 35162306a36Sopenharmony_ci /* Create direct DDE */ 35262306a36Sopenharmony_ci op.in = nx842_get_pa((void *)inbuf); 35362306a36Sopenharmony_ci op.inlen = inlen; 35462306a36Sopenharmony_ci } else { 35562306a36Sopenharmony_ci /* Create indirect DDE (scatterlist) */ 35662306a36Sopenharmony_ci nx842_build_scatterlist(inbuf, inlen, &slin); 35762306a36Sopenharmony_ci op.in = nx842_get_pa(slin.entries); 35862306a36Sopenharmony_ci op.inlen = -nx842_get_scatterlist_size(&slin); 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if ((outbuf & NX842_HW_PAGE_MASK) == 36262306a36Sopenharmony_ci ((outbuf + *outlen - 1) & NX842_HW_PAGE_MASK)) { 36362306a36Sopenharmony_ci /* Create direct DDE */ 36462306a36Sopenharmony_ci op.out = nx842_get_pa((void *)outbuf); 36562306a36Sopenharmony_ci op.outlen = *outlen; 36662306a36Sopenharmony_ci } else { 36762306a36Sopenharmony_ci /* Create indirect DDE (scatterlist) */ 36862306a36Sopenharmony_ci nx842_build_scatterlist(outbuf, *outlen, &slout); 36962306a36Sopenharmony_ci op.out = nx842_get_pa(slout.entries); 37062306a36Sopenharmony_ci op.outlen = -nx842_get_scatterlist_size(&slout); 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci dev_dbg(dev, "%s: op.in %lx op.inlen %ld op.out %lx op.outlen %ld\n", 37462306a36Sopenharmony_ci __func__, (unsigned long)op.in, (long)op.inlen, 37562306a36Sopenharmony_ci (unsigned long)op.out, (long)op.outlen); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* Send request to pHyp */ 37862306a36Sopenharmony_ci ret = vio_h_cop_sync(local_devdata->vdev, &op); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* Check for pHyp error */ 38162306a36Sopenharmony_ci if (ret) { 38262306a36Sopenharmony_ci dev_dbg(dev, "%s: vio_h_cop_sync error (ret=%d, hret=%ld)\n", 38362306a36Sopenharmony_ci __func__, ret, op.hcall_err); 38462306a36Sopenharmony_ci ret = -EIO; 38562306a36Sopenharmony_ci goto unlock; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* Check for hardware error */ 38962306a36Sopenharmony_ci ret = nx842_validate_result(dev, &csbcpb->csb); 39062306a36Sopenharmony_ci if (ret) 39162306a36Sopenharmony_ci goto unlock; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci *outlen = be32_to_cpu(csbcpb->csb.processed_byte_count); 39462306a36Sopenharmony_ci dev_dbg(dev, "%s: processed_bytes=%d\n", __func__, *outlen); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ciunlock: 39762306a36Sopenharmony_ci if (ret) 39862306a36Sopenharmony_ci nx842_inc_comp_failed(local_devdata); 39962306a36Sopenharmony_ci else { 40062306a36Sopenharmony_ci nx842_inc_comp_complete(local_devdata); 40162306a36Sopenharmony_ci ibm_nx842_incr_hist(local_devdata->counters->comp_times, 40262306a36Sopenharmony_ci (get_tb() - start) / tb_ticks_per_usec); 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci rcu_read_unlock(); 40562306a36Sopenharmony_ci return ret; 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci/** 40962306a36Sopenharmony_ci * nx842_pseries_decompress - Decompress data using the 842 algorithm 41062306a36Sopenharmony_ci * 41162306a36Sopenharmony_ci * Decompression provide by the NX842 coprocessor on IBM Power systems. 41262306a36Sopenharmony_ci * The input buffer is decompressed and the result is stored in the 41362306a36Sopenharmony_ci * provided output buffer. The size allocated to the output buffer is 41462306a36Sopenharmony_ci * provided by the caller of this function in @outlen. Upon return from 41562306a36Sopenharmony_ci * this function @outlen contains the length of the decompressed data. 41662306a36Sopenharmony_ci * If there is an error then @outlen will be 0 and an error will be 41762306a36Sopenharmony_ci * specified by the return code from this function. 41862306a36Sopenharmony_ci * 41962306a36Sopenharmony_ci * @in: Pointer to input buffer 42062306a36Sopenharmony_ci * @inlen: Length of input buffer 42162306a36Sopenharmony_ci * @out: Pointer to output buffer 42262306a36Sopenharmony_ci * @outlen: Length of output buffer 42362306a36Sopenharmony_ci * @wmem: ptr to buffer for working memory, size determined by 42462306a36Sopenharmony_ci * nx842_pseries_driver.workmem_size 42562306a36Sopenharmony_ci * 42662306a36Sopenharmony_ci * Returns: 42762306a36Sopenharmony_ci * 0 Success, output of length @outlen stored in the buffer at @out 42862306a36Sopenharmony_ci * -ENODEV Hardware decompression device is unavailable 42962306a36Sopenharmony_ci * -ENOMEM Unable to allocate internal buffers 43062306a36Sopenharmony_ci * -ENOSPC Output buffer is to small 43162306a36Sopenharmony_ci * -EINVAL Bad input data encountered when attempting decompress 43262306a36Sopenharmony_ci * -EIO Internal error 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_cistatic int nx842_pseries_decompress(const unsigned char *in, unsigned int inlen, 43562306a36Sopenharmony_ci unsigned char *out, unsigned int *outlen, 43662306a36Sopenharmony_ci void *wmem) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct nx842_devdata *local_devdata; 43962306a36Sopenharmony_ci struct device *dev = NULL; 44062306a36Sopenharmony_ci struct nx842_workmem *workmem; 44162306a36Sopenharmony_ci struct nx842_scatterlist slin, slout; 44262306a36Sopenharmony_ci struct nx_csbcpb *csbcpb; 44362306a36Sopenharmony_ci int ret = 0; 44462306a36Sopenharmony_ci unsigned long inbuf, outbuf; 44562306a36Sopenharmony_ci struct vio_pfo_op op = { 44662306a36Sopenharmony_ci .done = NULL, 44762306a36Sopenharmony_ci .handle = 0, 44862306a36Sopenharmony_ci .timeout = 0, 44962306a36Sopenharmony_ci }; 45062306a36Sopenharmony_ci unsigned long start = get_tb(); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* Ensure page alignment and size */ 45362306a36Sopenharmony_ci inbuf = (unsigned long)in; 45462306a36Sopenharmony_ci if (check_constraints(inbuf, &inlen, true)) 45562306a36Sopenharmony_ci return -EINVAL; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci outbuf = (unsigned long)out; 45862306a36Sopenharmony_ci if (check_constraints(outbuf, outlen, false)) 45962306a36Sopenharmony_ci return -EINVAL; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci rcu_read_lock(); 46262306a36Sopenharmony_ci local_devdata = rcu_dereference(devdata); 46362306a36Sopenharmony_ci if (!local_devdata || !local_devdata->dev) { 46462306a36Sopenharmony_ci rcu_read_unlock(); 46562306a36Sopenharmony_ci return -ENODEV; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci dev = local_devdata->dev; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci workmem = PTR_ALIGN(wmem, WORKMEM_ALIGN); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* Init scatterlist */ 47262306a36Sopenharmony_ci slin.entries = (struct nx842_slentry *)workmem->slin; 47362306a36Sopenharmony_ci slout.entries = (struct nx842_slentry *)workmem->slout; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* Init operation */ 47662306a36Sopenharmony_ci op.flags = NX842_OP_DECOMPRESS_CRC; 47762306a36Sopenharmony_ci csbcpb = &workmem->csbcpb; 47862306a36Sopenharmony_ci memset(csbcpb, 0, sizeof(*csbcpb)); 47962306a36Sopenharmony_ci op.csbcpb = nx842_get_pa(csbcpb); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if ((inbuf & NX842_HW_PAGE_MASK) == 48262306a36Sopenharmony_ci ((inbuf + inlen - 1) & NX842_HW_PAGE_MASK)) { 48362306a36Sopenharmony_ci /* Create direct DDE */ 48462306a36Sopenharmony_ci op.in = nx842_get_pa((void *)inbuf); 48562306a36Sopenharmony_ci op.inlen = inlen; 48662306a36Sopenharmony_ci } else { 48762306a36Sopenharmony_ci /* Create indirect DDE (scatterlist) */ 48862306a36Sopenharmony_ci nx842_build_scatterlist(inbuf, inlen, &slin); 48962306a36Sopenharmony_ci op.in = nx842_get_pa(slin.entries); 49062306a36Sopenharmony_ci op.inlen = -nx842_get_scatterlist_size(&slin); 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if ((outbuf & NX842_HW_PAGE_MASK) == 49462306a36Sopenharmony_ci ((outbuf + *outlen - 1) & NX842_HW_PAGE_MASK)) { 49562306a36Sopenharmony_ci /* Create direct DDE */ 49662306a36Sopenharmony_ci op.out = nx842_get_pa((void *)outbuf); 49762306a36Sopenharmony_ci op.outlen = *outlen; 49862306a36Sopenharmony_ci } else { 49962306a36Sopenharmony_ci /* Create indirect DDE (scatterlist) */ 50062306a36Sopenharmony_ci nx842_build_scatterlist(outbuf, *outlen, &slout); 50162306a36Sopenharmony_ci op.out = nx842_get_pa(slout.entries); 50262306a36Sopenharmony_ci op.outlen = -nx842_get_scatterlist_size(&slout); 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci dev_dbg(dev, "%s: op.in %lx op.inlen %ld op.out %lx op.outlen %ld\n", 50662306a36Sopenharmony_ci __func__, (unsigned long)op.in, (long)op.inlen, 50762306a36Sopenharmony_ci (unsigned long)op.out, (long)op.outlen); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* Send request to pHyp */ 51062306a36Sopenharmony_ci ret = vio_h_cop_sync(local_devdata->vdev, &op); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* Check for pHyp error */ 51362306a36Sopenharmony_ci if (ret) { 51462306a36Sopenharmony_ci dev_dbg(dev, "%s: vio_h_cop_sync error (ret=%d, hret=%ld)\n", 51562306a36Sopenharmony_ci __func__, ret, op.hcall_err); 51662306a36Sopenharmony_ci goto unlock; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* Check for hardware error */ 52062306a36Sopenharmony_ci ret = nx842_validate_result(dev, &csbcpb->csb); 52162306a36Sopenharmony_ci if (ret) 52262306a36Sopenharmony_ci goto unlock; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci *outlen = be32_to_cpu(csbcpb->csb.processed_byte_count); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ciunlock: 52762306a36Sopenharmony_ci if (ret) 52862306a36Sopenharmony_ci /* decompress fail */ 52962306a36Sopenharmony_ci nx842_inc_decomp_failed(local_devdata); 53062306a36Sopenharmony_ci else { 53162306a36Sopenharmony_ci nx842_inc_decomp_complete(local_devdata); 53262306a36Sopenharmony_ci ibm_nx842_incr_hist(local_devdata->counters->decomp_times, 53362306a36Sopenharmony_ci (get_tb() - start) / tb_ticks_per_usec); 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci rcu_read_unlock(); 53762306a36Sopenharmony_ci return ret; 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci/** 54162306a36Sopenharmony_ci * nx842_OF_set_defaults -- Set default (disabled) values for devdata 54262306a36Sopenharmony_ci * 54362306a36Sopenharmony_ci * @devdata: struct nx842_devdata to update 54462306a36Sopenharmony_ci * 54562306a36Sopenharmony_ci * Returns: 54662306a36Sopenharmony_ci * 0 on success 54762306a36Sopenharmony_ci * -ENOENT if @devdata ptr is NULL 54862306a36Sopenharmony_ci */ 54962306a36Sopenharmony_cistatic int nx842_OF_set_defaults(struct nx842_devdata *devdata) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci if (devdata) { 55262306a36Sopenharmony_ci devdata->max_sync_size = 0; 55362306a36Sopenharmony_ci devdata->max_sync_sg = 0; 55462306a36Sopenharmony_ci devdata->max_sg_len = 0; 55562306a36Sopenharmony_ci return 0; 55662306a36Sopenharmony_ci } else 55762306a36Sopenharmony_ci return -ENOENT; 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci/** 56162306a36Sopenharmony_ci * nx842_OF_upd_status -- Check the device info from OF status prop 56262306a36Sopenharmony_ci * 56362306a36Sopenharmony_ci * The status property indicates if the accelerator is enabled. If the 56462306a36Sopenharmony_ci * device is in the OF tree it indicates that the hardware is present. 56562306a36Sopenharmony_ci * The status field indicates if the device is enabled when the status 56662306a36Sopenharmony_ci * is 'okay'. Otherwise the device driver will be disabled. 56762306a36Sopenharmony_ci * 56862306a36Sopenharmony_ci * @devdata: struct nx842_devdata to use for dev_info 56962306a36Sopenharmony_ci * @prop: struct property point containing the maxsyncop for the update 57062306a36Sopenharmony_ci * 57162306a36Sopenharmony_ci * Returns: 57262306a36Sopenharmony_ci * 0 - Device is available 57362306a36Sopenharmony_ci * -ENODEV - Device is not available 57462306a36Sopenharmony_ci */ 57562306a36Sopenharmony_cistatic int nx842_OF_upd_status(struct nx842_devdata *devdata, 57662306a36Sopenharmony_ci struct property *prop) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci const char *status = (const char *)prop->value; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (!strncmp(status, "okay", (size_t)prop->length)) 58162306a36Sopenharmony_ci return 0; 58262306a36Sopenharmony_ci if (!strncmp(status, "disabled", (size_t)prop->length)) 58362306a36Sopenharmony_ci return -ENODEV; 58462306a36Sopenharmony_ci dev_info(devdata->dev, "%s: unknown status '%s'\n", __func__, status); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci return -EINVAL; 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci/** 59062306a36Sopenharmony_ci * nx842_OF_upd_maxsglen -- Update the device info from OF maxsglen prop 59162306a36Sopenharmony_ci * 59262306a36Sopenharmony_ci * Definition of the 'ibm,max-sg-len' OF property: 59362306a36Sopenharmony_ci * This field indicates the maximum byte length of a scatter list 59462306a36Sopenharmony_ci * for the platform facility. It is a single cell encoded as with encode-int. 59562306a36Sopenharmony_ci * 59662306a36Sopenharmony_ci * Example: 59762306a36Sopenharmony_ci * # od -x ibm,max-sg-len 59862306a36Sopenharmony_ci * 0000000 0000 0ff0 59962306a36Sopenharmony_ci * 60062306a36Sopenharmony_ci * In this example, the maximum byte length of a scatter list is 60162306a36Sopenharmony_ci * 0x0ff0 (4,080). 60262306a36Sopenharmony_ci * 60362306a36Sopenharmony_ci * @devdata: struct nx842_devdata to update 60462306a36Sopenharmony_ci * @prop: struct property point containing the maxsyncop for the update 60562306a36Sopenharmony_ci * 60662306a36Sopenharmony_ci * Returns: 60762306a36Sopenharmony_ci * 0 on success 60862306a36Sopenharmony_ci * -EINVAL on failure 60962306a36Sopenharmony_ci */ 61062306a36Sopenharmony_cistatic int nx842_OF_upd_maxsglen(struct nx842_devdata *devdata, 61162306a36Sopenharmony_ci struct property *prop) { 61262306a36Sopenharmony_ci int ret = 0; 61362306a36Sopenharmony_ci const unsigned int maxsglen = of_read_number(prop->value, 1); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (prop->length != sizeof(maxsglen)) { 61662306a36Sopenharmony_ci dev_err(devdata->dev, "%s: unexpected format for ibm,max-sg-len property\n", __func__); 61762306a36Sopenharmony_ci dev_dbg(devdata->dev, "%s: ibm,max-sg-len is %d bytes long, expected %lu bytes\n", __func__, 61862306a36Sopenharmony_ci prop->length, sizeof(maxsglen)); 61962306a36Sopenharmony_ci ret = -EINVAL; 62062306a36Sopenharmony_ci } else { 62162306a36Sopenharmony_ci devdata->max_sg_len = min_t(unsigned int, 62262306a36Sopenharmony_ci maxsglen, NX842_HW_PAGE_SIZE); 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci return ret; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci/** 62962306a36Sopenharmony_ci * nx842_OF_upd_maxsyncop -- Update the device info from OF maxsyncop prop 63062306a36Sopenharmony_ci * 63162306a36Sopenharmony_ci * Definition of the 'ibm,max-sync-cop' OF property: 63262306a36Sopenharmony_ci * Two series of cells. The first series of cells represents the maximums 63362306a36Sopenharmony_ci * that can be synchronously compressed. The second series of cells 63462306a36Sopenharmony_ci * represents the maximums that can be synchronously decompressed. 63562306a36Sopenharmony_ci * 1. The first cell in each series contains the count of the number of 63662306a36Sopenharmony_ci * data length, scatter list elements pairs that follow – each being 63762306a36Sopenharmony_ci * of the form 63862306a36Sopenharmony_ci * a. One cell data byte length 63962306a36Sopenharmony_ci * b. One cell total number of scatter list elements 64062306a36Sopenharmony_ci * 64162306a36Sopenharmony_ci * Example: 64262306a36Sopenharmony_ci * # od -x ibm,max-sync-cop 64362306a36Sopenharmony_ci * 0000000 0000 0001 0000 1000 0000 01fe 0000 0001 64462306a36Sopenharmony_ci * 0000020 0000 1000 0000 01fe 64562306a36Sopenharmony_ci * 64662306a36Sopenharmony_ci * In this example, compression supports 0x1000 (4,096) data byte length 64762306a36Sopenharmony_ci * and 0x1fe (510) total scatter list elements. Decompression supports 64862306a36Sopenharmony_ci * 0x1000 (4,096) data byte length and 0x1f3 (510) total scatter list 64962306a36Sopenharmony_ci * elements. 65062306a36Sopenharmony_ci * 65162306a36Sopenharmony_ci * @devdata: struct nx842_devdata to update 65262306a36Sopenharmony_ci * @prop: struct property point containing the maxsyncop for the update 65362306a36Sopenharmony_ci * 65462306a36Sopenharmony_ci * Returns: 65562306a36Sopenharmony_ci * 0 on success 65662306a36Sopenharmony_ci * -EINVAL on failure 65762306a36Sopenharmony_ci */ 65862306a36Sopenharmony_cistatic int nx842_OF_upd_maxsyncop(struct nx842_devdata *devdata, 65962306a36Sopenharmony_ci struct property *prop) { 66062306a36Sopenharmony_ci int ret = 0; 66162306a36Sopenharmony_ci unsigned int comp_data_limit, decomp_data_limit; 66262306a36Sopenharmony_ci unsigned int comp_sg_limit, decomp_sg_limit; 66362306a36Sopenharmony_ci const struct maxsynccop_t { 66462306a36Sopenharmony_ci __be32 comp_elements; 66562306a36Sopenharmony_ci __be32 comp_data_limit; 66662306a36Sopenharmony_ci __be32 comp_sg_limit; 66762306a36Sopenharmony_ci __be32 decomp_elements; 66862306a36Sopenharmony_ci __be32 decomp_data_limit; 66962306a36Sopenharmony_ci __be32 decomp_sg_limit; 67062306a36Sopenharmony_ci } *maxsynccop; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci if (prop->length != sizeof(*maxsynccop)) { 67362306a36Sopenharmony_ci dev_err(devdata->dev, "%s: unexpected format for ibm,max-sync-cop property\n", __func__); 67462306a36Sopenharmony_ci dev_dbg(devdata->dev, "%s: ibm,max-sync-cop is %d bytes long, expected %lu bytes\n", __func__, prop->length, 67562306a36Sopenharmony_ci sizeof(*maxsynccop)); 67662306a36Sopenharmony_ci ret = -EINVAL; 67762306a36Sopenharmony_ci goto out; 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci maxsynccop = (const struct maxsynccop_t *)prop->value; 68162306a36Sopenharmony_ci comp_data_limit = be32_to_cpu(maxsynccop->comp_data_limit); 68262306a36Sopenharmony_ci comp_sg_limit = be32_to_cpu(maxsynccop->comp_sg_limit); 68362306a36Sopenharmony_ci decomp_data_limit = be32_to_cpu(maxsynccop->decomp_data_limit); 68462306a36Sopenharmony_ci decomp_sg_limit = be32_to_cpu(maxsynccop->decomp_sg_limit); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* Use one limit rather than separate limits for compression and 68762306a36Sopenharmony_ci * decompression. Set a maximum for this so as not to exceed the 68862306a36Sopenharmony_ci * size that the header can support and round the value down to 68962306a36Sopenharmony_ci * the hardware page size (4K) */ 69062306a36Sopenharmony_ci devdata->max_sync_size = min(comp_data_limit, decomp_data_limit); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci devdata->max_sync_size = min_t(unsigned int, devdata->max_sync_size, 69362306a36Sopenharmony_ci 65536); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci if (devdata->max_sync_size < 4096) { 69662306a36Sopenharmony_ci dev_err(devdata->dev, "%s: hardware max data size (%u) is " 69762306a36Sopenharmony_ci "less than the driver minimum, unable to use " 69862306a36Sopenharmony_ci "the hardware device\n", 69962306a36Sopenharmony_ci __func__, devdata->max_sync_size); 70062306a36Sopenharmony_ci ret = -EINVAL; 70162306a36Sopenharmony_ci goto out; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci nx842_pseries_constraints.maximum = devdata->max_sync_size; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci devdata->max_sync_sg = min(comp_sg_limit, decomp_sg_limit); 70762306a36Sopenharmony_ci if (devdata->max_sync_sg < 1) { 70862306a36Sopenharmony_ci dev_err(devdata->dev, "%s: hardware max sg size (%u) is " 70962306a36Sopenharmony_ci "less than the driver minimum, unable to use " 71062306a36Sopenharmony_ci "the hardware device\n", 71162306a36Sopenharmony_ci __func__, devdata->max_sync_sg); 71262306a36Sopenharmony_ci ret = -EINVAL; 71362306a36Sopenharmony_ci goto out; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ciout: 71762306a36Sopenharmony_ci return ret; 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci/** 72162306a36Sopenharmony_ci * nx842_OF_upd -- Handle OF properties updates for the device. 72262306a36Sopenharmony_ci * 72362306a36Sopenharmony_ci * Set all properties from the OF tree. Optionally, a new property 72462306a36Sopenharmony_ci * can be provided by the @new_prop pointer to overwrite an existing value. 72562306a36Sopenharmony_ci * The device will remain disabled until all values are valid, this function 72662306a36Sopenharmony_ci * will return an error for updates unless all values are valid. 72762306a36Sopenharmony_ci * 72862306a36Sopenharmony_ci * @new_prop: If not NULL, this property is being updated. If NULL, update 72962306a36Sopenharmony_ci * all properties from the current values in the OF tree. 73062306a36Sopenharmony_ci * 73162306a36Sopenharmony_ci * Returns: 73262306a36Sopenharmony_ci * 0 - Success 73362306a36Sopenharmony_ci * -ENOMEM - Could not allocate memory for new devdata structure 73462306a36Sopenharmony_ci * -EINVAL - property value not found, new_prop is not a recognized 73562306a36Sopenharmony_ci * property for the device or property value is not valid. 73662306a36Sopenharmony_ci * -ENODEV - Device is not available 73762306a36Sopenharmony_ci */ 73862306a36Sopenharmony_cistatic int nx842_OF_upd(struct property *new_prop) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci struct nx842_devdata *old_devdata = NULL; 74162306a36Sopenharmony_ci struct nx842_devdata *new_devdata = NULL; 74262306a36Sopenharmony_ci struct device_node *of_node = NULL; 74362306a36Sopenharmony_ci struct property *status = NULL; 74462306a36Sopenharmony_ci struct property *maxsglen = NULL; 74562306a36Sopenharmony_ci struct property *maxsyncop = NULL; 74662306a36Sopenharmony_ci int ret = 0; 74762306a36Sopenharmony_ci unsigned long flags; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci new_devdata = kzalloc(sizeof(*new_devdata), GFP_NOFS); 75062306a36Sopenharmony_ci if (!new_devdata) 75162306a36Sopenharmony_ci return -ENOMEM; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci spin_lock_irqsave(&devdata_mutex, flags); 75462306a36Sopenharmony_ci old_devdata = rcu_dereference_check(devdata, 75562306a36Sopenharmony_ci lockdep_is_held(&devdata_mutex)); 75662306a36Sopenharmony_ci if (old_devdata) 75762306a36Sopenharmony_ci of_node = old_devdata->dev->of_node; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (!old_devdata || !of_node) { 76062306a36Sopenharmony_ci pr_err("%s: device is not available\n", __func__); 76162306a36Sopenharmony_ci spin_unlock_irqrestore(&devdata_mutex, flags); 76262306a36Sopenharmony_ci kfree(new_devdata); 76362306a36Sopenharmony_ci return -ENODEV; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci memcpy(new_devdata, old_devdata, sizeof(*old_devdata)); 76762306a36Sopenharmony_ci new_devdata->counters = old_devdata->counters; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* Set ptrs for existing properties */ 77062306a36Sopenharmony_ci status = of_find_property(of_node, "status", NULL); 77162306a36Sopenharmony_ci maxsglen = of_find_property(of_node, "ibm,max-sg-len", NULL); 77262306a36Sopenharmony_ci maxsyncop = of_find_property(of_node, "ibm,max-sync-cop", NULL); 77362306a36Sopenharmony_ci if (!status || !maxsglen || !maxsyncop) { 77462306a36Sopenharmony_ci dev_err(old_devdata->dev, "%s: Could not locate device properties\n", __func__); 77562306a36Sopenharmony_ci ret = -EINVAL; 77662306a36Sopenharmony_ci goto error_out; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci /* 78062306a36Sopenharmony_ci * If this is a property update, there are only certain properties that 78162306a36Sopenharmony_ci * we care about. Bail if it isn't in the below list 78262306a36Sopenharmony_ci */ 78362306a36Sopenharmony_ci if (new_prop && (strncmp(new_prop->name, "status", new_prop->length) || 78462306a36Sopenharmony_ci strncmp(new_prop->name, "ibm,max-sg-len", new_prop->length) || 78562306a36Sopenharmony_ci strncmp(new_prop->name, "ibm,max-sync-cop", new_prop->length))) 78662306a36Sopenharmony_ci goto out; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci /* Perform property updates */ 78962306a36Sopenharmony_ci ret = nx842_OF_upd_status(new_devdata, status); 79062306a36Sopenharmony_ci if (ret) 79162306a36Sopenharmony_ci goto error_out; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci ret = nx842_OF_upd_maxsglen(new_devdata, maxsglen); 79462306a36Sopenharmony_ci if (ret) 79562306a36Sopenharmony_ci goto error_out; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci ret = nx842_OF_upd_maxsyncop(new_devdata, maxsyncop); 79862306a36Sopenharmony_ci if (ret) 79962306a36Sopenharmony_ci goto error_out; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ciout: 80262306a36Sopenharmony_ci dev_info(old_devdata->dev, "%s: max_sync_size new:%u old:%u\n", 80362306a36Sopenharmony_ci __func__, new_devdata->max_sync_size, 80462306a36Sopenharmony_ci old_devdata->max_sync_size); 80562306a36Sopenharmony_ci dev_info(old_devdata->dev, "%s: max_sync_sg new:%u old:%u\n", 80662306a36Sopenharmony_ci __func__, new_devdata->max_sync_sg, 80762306a36Sopenharmony_ci old_devdata->max_sync_sg); 80862306a36Sopenharmony_ci dev_info(old_devdata->dev, "%s: max_sg_len new:%u old:%u\n", 80962306a36Sopenharmony_ci __func__, new_devdata->max_sg_len, 81062306a36Sopenharmony_ci old_devdata->max_sg_len); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci rcu_assign_pointer(devdata, new_devdata); 81362306a36Sopenharmony_ci spin_unlock_irqrestore(&devdata_mutex, flags); 81462306a36Sopenharmony_ci synchronize_rcu(); 81562306a36Sopenharmony_ci dev_set_drvdata(new_devdata->dev, new_devdata); 81662306a36Sopenharmony_ci kfree(old_devdata); 81762306a36Sopenharmony_ci return 0; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_cierror_out: 82062306a36Sopenharmony_ci if (new_devdata) { 82162306a36Sopenharmony_ci dev_info(old_devdata->dev, "%s: device disabled\n", __func__); 82262306a36Sopenharmony_ci nx842_OF_set_defaults(new_devdata); 82362306a36Sopenharmony_ci rcu_assign_pointer(devdata, new_devdata); 82462306a36Sopenharmony_ci spin_unlock_irqrestore(&devdata_mutex, flags); 82562306a36Sopenharmony_ci synchronize_rcu(); 82662306a36Sopenharmony_ci dev_set_drvdata(new_devdata->dev, new_devdata); 82762306a36Sopenharmony_ci kfree(old_devdata); 82862306a36Sopenharmony_ci } else { 82962306a36Sopenharmony_ci dev_err(old_devdata->dev, "%s: could not update driver from hardware\n", __func__); 83062306a36Sopenharmony_ci spin_unlock_irqrestore(&devdata_mutex, flags); 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (!ret) 83462306a36Sopenharmony_ci ret = -EINVAL; 83562306a36Sopenharmony_ci return ret; 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci/** 83962306a36Sopenharmony_ci * nx842_OF_notifier - Process updates to OF properties for the device 84062306a36Sopenharmony_ci * 84162306a36Sopenharmony_ci * @np: notifier block 84262306a36Sopenharmony_ci * @action: notifier action 84362306a36Sopenharmony_ci * @data: struct of_reconfig_data pointer 84462306a36Sopenharmony_ci * 84562306a36Sopenharmony_ci * Returns: 84662306a36Sopenharmony_ci * NOTIFY_OK on success 84762306a36Sopenharmony_ci * NOTIFY_BAD encoded with error number on failure, use 84862306a36Sopenharmony_ci * notifier_to_errno() to decode this value 84962306a36Sopenharmony_ci */ 85062306a36Sopenharmony_cistatic int nx842_OF_notifier(struct notifier_block *np, unsigned long action, 85162306a36Sopenharmony_ci void *data) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci struct of_reconfig_data *upd = data; 85462306a36Sopenharmony_ci struct nx842_devdata *local_devdata; 85562306a36Sopenharmony_ci struct device_node *node = NULL; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci rcu_read_lock(); 85862306a36Sopenharmony_ci local_devdata = rcu_dereference(devdata); 85962306a36Sopenharmony_ci if (local_devdata) 86062306a36Sopenharmony_ci node = local_devdata->dev->of_node; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci if (local_devdata && 86362306a36Sopenharmony_ci action == OF_RECONFIG_UPDATE_PROPERTY && 86462306a36Sopenharmony_ci !strcmp(upd->dn->name, node->name)) { 86562306a36Sopenharmony_ci rcu_read_unlock(); 86662306a36Sopenharmony_ci nx842_OF_upd(upd->prop); 86762306a36Sopenharmony_ci } else 86862306a36Sopenharmony_ci rcu_read_unlock(); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci return NOTIFY_OK; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic struct notifier_block nx842_of_nb = { 87462306a36Sopenharmony_ci .notifier_call = nx842_OF_notifier, 87562306a36Sopenharmony_ci}; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci#define nx842_counter_read(_name) \ 87862306a36Sopenharmony_cistatic ssize_t nx842_##_name##_show(struct device *dev, \ 87962306a36Sopenharmony_ci struct device_attribute *attr, \ 88062306a36Sopenharmony_ci char *buf) { \ 88162306a36Sopenharmony_ci struct nx842_devdata *local_devdata; \ 88262306a36Sopenharmony_ci int p = 0; \ 88362306a36Sopenharmony_ci rcu_read_lock(); \ 88462306a36Sopenharmony_ci local_devdata = rcu_dereference(devdata); \ 88562306a36Sopenharmony_ci if (local_devdata) \ 88662306a36Sopenharmony_ci p = snprintf(buf, PAGE_SIZE, "%lld\n", \ 88762306a36Sopenharmony_ci atomic64_read(&local_devdata->counters->_name)); \ 88862306a36Sopenharmony_ci rcu_read_unlock(); \ 88962306a36Sopenharmony_ci return p; \ 89062306a36Sopenharmony_ci} 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci#define NX842DEV_COUNTER_ATTR_RO(_name) \ 89362306a36Sopenharmony_ci nx842_counter_read(_name); \ 89462306a36Sopenharmony_ci static struct device_attribute dev_attr_##_name = __ATTR(_name, \ 89562306a36Sopenharmony_ci 0444, \ 89662306a36Sopenharmony_ci nx842_##_name##_show,\ 89762306a36Sopenharmony_ci NULL); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ciNX842DEV_COUNTER_ATTR_RO(comp_complete); 90062306a36Sopenharmony_ciNX842DEV_COUNTER_ATTR_RO(comp_failed); 90162306a36Sopenharmony_ciNX842DEV_COUNTER_ATTR_RO(decomp_complete); 90262306a36Sopenharmony_ciNX842DEV_COUNTER_ATTR_RO(decomp_failed); 90362306a36Sopenharmony_ciNX842DEV_COUNTER_ATTR_RO(swdecomp); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_cistatic ssize_t nx842_timehist_show(struct device *, 90662306a36Sopenharmony_ci struct device_attribute *, char *); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_cistatic struct device_attribute dev_attr_comp_times = __ATTR(comp_times, 0444, 90962306a36Sopenharmony_ci nx842_timehist_show, NULL); 91062306a36Sopenharmony_cistatic struct device_attribute dev_attr_decomp_times = __ATTR(decomp_times, 91162306a36Sopenharmony_ci 0444, nx842_timehist_show, NULL); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_cistatic ssize_t nx842_timehist_show(struct device *dev, 91462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) { 91562306a36Sopenharmony_ci char *p = buf; 91662306a36Sopenharmony_ci struct nx842_devdata *local_devdata; 91762306a36Sopenharmony_ci atomic64_t *times; 91862306a36Sopenharmony_ci int bytes_remain = PAGE_SIZE; 91962306a36Sopenharmony_ci int bytes; 92062306a36Sopenharmony_ci int i; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci rcu_read_lock(); 92362306a36Sopenharmony_ci local_devdata = rcu_dereference(devdata); 92462306a36Sopenharmony_ci if (!local_devdata) { 92562306a36Sopenharmony_ci rcu_read_unlock(); 92662306a36Sopenharmony_ci return 0; 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci if (attr == &dev_attr_comp_times) 93062306a36Sopenharmony_ci times = local_devdata->counters->comp_times; 93162306a36Sopenharmony_ci else if (attr == &dev_attr_decomp_times) 93262306a36Sopenharmony_ci times = local_devdata->counters->decomp_times; 93362306a36Sopenharmony_ci else { 93462306a36Sopenharmony_ci rcu_read_unlock(); 93562306a36Sopenharmony_ci return 0; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci for (i = 0; i < (NX842_HIST_SLOTS - 2); i++) { 93962306a36Sopenharmony_ci bytes = snprintf(p, bytes_remain, "%u-%uus:\t%lld\n", 94062306a36Sopenharmony_ci i ? (2<<(i-1)) : 0, (2<<i)-1, 94162306a36Sopenharmony_ci atomic64_read(×[i])); 94262306a36Sopenharmony_ci bytes_remain -= bytes; 94362306a36Sopenharmony_ci p += bytes; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci /* The last bucket holds everything over 94662306a36Sopenharmony_ci * 2<<(NX842_HIST_SLOTS - 2) us */ 94762306a36Sopenharmony_ci bytes = snprintf(p, bytes_remain, "%uus - :\t%lld\n", 94862306a36Sopenharmony_ci 2<<(NX842_HIST_SLOTS - 2), 94962306a36Sopenharmony_ci atomic64_read(×[(NX842_HIST_SLOTS - 1)])); 95062306a36Sopenharmony_ci p += bytes; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci rcu_read_unlock(); 95362306a36Sopenharmony_ci return p - buf; 95462306a36Sopenharmony_ci} 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_cistatic struct attribute *nx842_sysfs_entries[] = { 95762306a36Sopenharmony_ci &dev_attr_comp_complete.attr, 95862306a36Sopenharmony_ci &dev_attr_comp_failed.attr, 95962306a36Sopenharmony_ci &dev_attr_decomp_complete.attr, 96062306a36Sopenharmony_ci &dev_attr_decomp_failed.attr, 96162306a36Sopenharmony_ci &dev_attr_swdecomp.attr, 96262306a36Sopenharmony_ci &dev_attr_comp_times.attr, 96362306a36Sopenharmony_ci &dev_attr_decomp_times.attr, 96462306a36Sopenharmony_ci NULL, 96562306a36Sopenharmony_ci}; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_cistatic const struct attribute_group nx842_attribute_group = { 96862306a36Sopenharmony_ci .name = NULL, /* put in device directory */ 96962306a36Sopenharmony_ci .attrs = nx842_sysfs_entries, 97062306a36Sopenharmony_ci}; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci#define nxcop_caps_read(_name) \ 97362306a36Sopenharmony_cistatic ssize_t nxcop_##_name##_show(struct device *dev, \ 97462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) \ 97562306a36Sopenharmony_ci{ \ 97662306a36Sopenharmony_ci return sprintf(buf, "%lld\n", nx_cop_caps._name); \ 97762306a36Sopenharmony_ci} 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci#define NXCT_ATTR_RO(_name) \ 98062306a36Sopenharmony_ci nxcop_caps_read(_name); \ 98162306a36Sopenharmony_ci static struct device_attribute dev_attr_##_name = __ATTR(_name, \ 98262306a36Sopenharmony_ci 0444, \ 98362306a36Sopenharmony_ci nxcop_##_name##_show, \ 98462306a36Sopenharmony_ci NULL); 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ciNXCT_ATTR_RO(req_max_processed_len); 98762306a36Sopenharmony_ciNXCT_ATTR_RO(min_compress_len); 98862306a36Sopenharmony_ciNXCT_ATTR_RO(min_decompress_len); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cistatic struct attribute *nxcop_caps_sysfs_entries[] = { 99162306a36Sopenharmony_ci &dev_attr_req_max_processed_len.attr, 99262306a36Sopenharmony_ci &dev_attr_min_compress_len.attr, 99362306a36Sopenharmony_ci &dev_attr_min_decompress_len.attr, 99462306a36Sopenharmony_ci NULL, 99562306a36Sopenharmony_ci}; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_cistatic const struct attribute_group nxcop_caps_attr_group = { 99862306a36Sopenharmony_ci .name = "nx_gzip_caps", 99962306a36Sopenharmony_ci .attrs = nxcop_caps_sysfs_entries, 100062306a36Sopenharmony_ci}; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_cistatic struct nx842_driver nx842_pseries_driver = { 100362306a36Sopenharmony_ci .name = KBUILD_MODNAME, 100462306a36Sopenharmony_ci .owner = THIS_MODULE, 100562306a36Sopenharmony_ci .workmem_size = sizeof(struct nx842_workmem), 100662306a36Sopenharmony_ci .constraints = &nx842_pseries_constraints, 100762306a36Sopenharmony_ci .compress = nx842_pseries_compress, 100862306a36Sopenharmony_ci .decompress = nx842_pseries_decompress, 100962306a36Sopenharmony_ci}; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_cistatic int nx842_pseries_crypto_init(struct crypto_tfm *tfm) 101262306a36Sopenharmony_ci{ 101362306a36Sopenharmony_ci return nx842_crypto_init(tfm, &nx842_pseries_driver); 101462306a36Sopenharmony_ci} 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_cistatic struct crypto_alg nx842_pseries_alg = { 101762306a36Sopenharmony_ci .cra_name = "842", 101862306a36Sopenharmony_ci .cra_driver_name = "842-nx", 101962306a36Sopenharmony_ci .cra_priority = 300, 102062306a36Sopenharmony_ci .cra_flags = CRYPTO_ALG_TYPE_COMPRESS, 102162306a36Sopenharmony_ci .cra_ctxsize = sizeof(struct nx842_crypto_ctx), 102262306a36Sopenharmony_ci .cra_module = THIS_MODULE, 102362306a36Sopenharmony_ci .cra_init = nx842_pseries_crypto_init, 102462306a36Sopenharmony_ci .cra_exit = nx842_crypto_exit, 102562306a36Sopenharmony_ci .cra_u = { .compress = { 102662306a36Sopenharmony_ci .coa_compress = nx842_crypto_compress, 102762306a36Sopenharmony_ci .coa_decompress = nx842_crypto_decompress } } 102862306a36Sopenharmony_ci}; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_cistatic int nx842_probe(struct vio_dev *viodev, 103162306a36Sopenharmony_ci const struct vio_device_id *id) 103262306a36Sopenharmony_ci{ 103362306a36Sopenharmony_ci struct nx842_devdata *old_devdata, *new_devdata = NULL; 103462306a36Sopenharmony_ci unsigned long flags; 103562306a36Sopenharmony_ci int ret = 0; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci new_devdata = kzalloc(sizeof(*new_devdata), GFP_NOFS); 103862306a36Sopenharmony_ci if (!new_devdata) 103962306a36Sopenharmony_ci return -ENOMEM; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci new_devdata->counters = kzalloc(sizeof(*new_devdata->counters), 104262306a36Sopenharmony_ci GFP_NOFS); 104362306a36Sopenharmony_ci if (!new_devdata->counters) { 104462306a36Sopenharmony_ci kfree(new_devdata); 104562306a36Sopenharmony_ci return -ENOMEM; 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci spin_lock_irqsave(&devdata_mutex, flags); 104962306a36Sopenharmony_ci old_devdata = rcu_dereference_check(devdata, 105062306a36Sopenharmony_ci lockdep_is_held(&devdata_mutex)); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci if (old_devdata && old_devdata->vdev != NULL) { 105362306a36Sopenharmony_ci dev_err(&viodev->dev, "%s: Attempt to register more than one instance of the hardware\n", __func__); 105462306a36Sopenharmony_ci ret = -1; 105562306a36Sopenharmony_ci goto error_unlock; 105662306a36Sopenharmony_ci } 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci dev_set_drvdata(&viodev->dev, NULL); 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci new_devdata->vdev = viodev; 106162306a36Sopenharmony_ci new_devdata->dev = &viodev->dev; 106262306a36Sopenharmony_ci nx842_OF_set_defaults(new_devdata); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci rcu_assign_pointer(devdata, new_devdata); 106562306a36Sopenharmony_ci spin_unlock_irqrestore(&devdata_mutex, flags); 106662306a36Sopenharmony_ci synchronize_rcu(); 106762306a36Sopenharmony_ci kfree(old_devdata); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci of_reconfig_notifier_register(&nx842_of_nb); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci ret = nx842_OF_upd(NULL); 107262306a36Sopenharmony_ci if (ret) 107362306a36Sopenharmony_ci goto error; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci ret = crypto_register_alg(&nx842_pseries_alg); 107662306a36Sopenharmony_ci if (ret) { 107762306a36Sopenharmony_ci dev_err(&viodev->dev, "could not register comp alg: %d\n", ret); 107862306a36Sopenharmony_ci goto error; 107962306a36Sopenharmony_ci } 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci rcu_read_lock(); 108262306a36Sopenharmony_ci dev_set_drvdata(&viodev->dev, rcu_dereference(devdata)); 108362306a36Sopenharmony_ci rcu_read_unlock(); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci if (sysfs_create_group(&viodev->dev.kobj, &nx842_attribute_group)) { 108662306a36Sopenharmony_ci dev_err(&viodev->dev, "could not create sysfs device attributes\n"); 108762306a36Sopenharmony_ci ret = -1; 108862306a36Sopenharmony_ci goto error; 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci if (caps_feat) { 109262306a36Sopenharmony_ci if (sysfs_create_group(&viodev->dev.kobj, 109362306a36Sopenharmony_ci &nxcop_caps_attr_group)) { 109462306a36Sopenharmony_ci dev_err(&viodev->dev, 109562306a36Sopenharmony_ci "Could not create sysfs NX capability entries\n"); 109662306a36Sopenharmony_ci ret = -1; 109762306a36Sopenharmony_ci goto error; 109862306a36Sopenharmony_ci } 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci return 0; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_cierror_unlock: 110462306a36Sopenharmony_ci spin_unlock_irqrestore(&devdata_mutex, flags); 110562306a36Sopenharmony_ci if (new_devdata) 110662306a36Sopenharmony_ci kfree(new_devdata->counters); 110762306a36Sopenharmony_ci kfree(new_devdata); 110862306a36Sopenharmony_cierror: 110962306a36Sopenharmony_ci return ret; 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_cistatic void nx842_remove(struct vio_dev *viodev) 111362306a36Sopenharmony_ci{ 111462306a36Sopenharmony_ci struct nx842_devdata *old_devdata; 111562306a36Sopenharmony_ci unsigned long flags; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci pr_info("Removing IBM Power 842 compression device\n"); 111862306a36Sopenharmony_ci sysfs_remove_group(&viodev->dev.kobj, &nx842_attribute_group); 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci if (caps_feat) 112162306a36Sopenharmony_ci sysfs_remove_group(&viodev->dev.kobj, &nxcop_caps_attr_group); 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci crypto_unregister_alg(&nx842_pseries_alg); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci spin_lock_irqsave(&devdata_mutex, flags); 112662306a36Sopenharmony_ci old_devdata = rcu_dereference_check(devdata, 112762306a36Sopenharmony_ci lockdep_is_held(&devdata_mutex)); 112862306a36Sopenharmony_ci of_reconfig_notifier_unregister(&nx842_of_nb); 112962306a36Sopenharmony_ci RCU_INIT_POINTER(devdata, NULL); 113062306a36Sopenharmony_ci spin_unlock_irqrestore(&devdata_mutex, flags); 113162306a36Sopenharmony_ci synchronize_rcu(); 113262306a36Sopenharmony_ci dev_set_drvdata(&viodev->dev, NULL); 113362306a36Sopenharmony_ci if (old_devdata) 113462306a36Sopenharmony_ci kfree(old_devdata->counters); 113562306a36Sopenharmony_ci kfree(old_devdata); 113662306a36Sopenharmony_ci} 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci/* 113962306a36Sopenharmony_ci * Get NX capabilities from the hypervisor. 114062306a36Sopenharmony_ci * Only NXGZIP capabilities are provided by the hypersvisor right 114162306a36Sopenharmony_ci * now and these values are available to user space with sysfs. 114262306a36Sopenharmony_ci */ 114362306a36Sopenharmony_cistatic void __init nxcop_get_capabilities(void) 114462306a36Sopenharmony_ci{ 114562306a36Sopenharmony_ci struct hv_vas_all_caps *hv_caps; 114662306a36Sopenharmony_ci struct hv_nx_cop_caps *hv_nxc; 114762306a36Sopenharmony_ci int rc; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci hv_caps = kmalloc(sizeof(*hv_caps), GFP_KERNEL); 115062306a36Sopenharmony_ci if (!hv_caps) 115162306a36Sopenharmony_ci return; 115262306a36Sopenharmony_ci /* 115362306a36Sopenharmony_ci * Get NX overall capabilities with feature type=0 115462306a36Sopenharmony_ci */ 115562306a36Sopenharmony_ci rc = h_query_vas_capabilities(H_QUERY_NX_CAPABILITIES, 0, 115662306a36Sopenharmony_ci (u64)virt_to_phys(hv_caps)); 115762306a36Sopenharmony_ci if (rc) 115862306a36Sopenharmony_ci goto out; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci caps_feat = be64_to_cpu(hv_caps->feat_type); 116162306a36Sopenharmony_ci /* 116262306a36Sopenharmony_ci * NX-GZIP feature available 116362306a36Sopenharmony_ci */ 116462306a36Sopenharmony_ci if (caps_feat & VAS_NX_GZIP_FEAT_BIT) { 116562306a36Sopenharmony_ci hv_nxc = kmalloc(sizeof(*hv_nxc), GFP_KERNEL); 116662306a36Sopenharmony_ci if (!hv_nxc) 116762306a36Sopenharmony_ci goto out; 116862306a36Sopenharmony_ci /* 116962306a36Sopenharmony_ci * Get capabilities for NX-GZIP feature 117062306a36Sopenharmony_ci */ 117162306a36Sopenharmony_ci rc = h_query_vas_capabilities(H_QUERY_NX_CAPABILITIES, 117262306a36Sopenharmony_ci VAS_NX_GZIP_FEAT, 117362306a36Sopenharmony_ci (u64)virt_to_phys(hv_nxc)); 117462306a36Sopenharmony_ci } else { 117562306a36Sopenharmony_ci pr_err("NX-GZIP feature is not available\n"); 117662306a36Sopenharmony_ci rc = -EINVAL; 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci if (!rc) { 118062306a36Sopenharmony_ci nx_cop_caps.descriptor = be64_to_cpu(hv_nxc->descriptor); 118162306a36Sopenharmony_ci nx_cop_caps.req_max_processed_len = 118262306a36Sopenharmony_ci be64_to_cpu(hv_nxc->req_max_processed_len); 118362306a36Sopenharmony_ci nx_cop_caps.min_compress_len = 118462306a36Sopenharmony_ci be64_to_cpu(hv_nxc->min_compress_len); 118562306a36Sopenharmony_ci nx_cop_caps.min_decompress_len = 118662306a36Sopenharmony_ci be64_to_cpu(hv_nxc->min_decompress_len); 118762306a36Sopenharmony_ci } else { 118862306a36Sopenharmony_ci caps_feat = 0; 118962306a36Sopenharmony_ci } 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci kfree(hv_nxc); 119262306a36Sopenharmony_ciout: 119362306a36Sopenharmony_ci kfree(hv_caps); 119462306a36Sopenharmony_ci} 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_cistatic const struct vio_device_id nx842_vio_driver_ids[] = { 119762306a36Sopenharmony_ci {"ibm,compression-v1", "ibm,compression"}, 119862306a36Sopenharmony_ci {"", ""}, 119962306a36Sopenharmony_ci}; 120062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(vio, nx842_vio_driver_ids); 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_cistatic struct vio_driver nx842_vio_driver = { 120362306a36Sopenharmony_ci .name = KBUILD_MODNAME, 120462306a36Sopenharmony_ci .probe = nx842_probe, 120562306a36Sopenharmony_ci .remove = nx842_remove, 120662306a36Sopenharmony_ci .get_desired_dma = nx842_get_desired_dma, 120762306a36Sopenharmony_ci .id_table = nx842_vio_driver_ids, 120862306a36Sopenharmony_ci}; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_cistatic int __init nx842_pseries_init(void) 121162306a36Sopenharmony_ci{ 121262306a36Sopenharmony_ci struct nx842_devdata *new_devdata; 121362306a36Sopenharmony_ci struct device_node *np; 121462306a36Sopenharmony_ci int ret; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "ibm,compression"); 121762306a36Sopenharmony_ci if (!np) 121862306a36Sopenharmony_ci return -ENODEV; 121962306a36Sopenharmony_ci of_node_put(np); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci RCU_INIT_POINTER(devdata, NULL); 122262306a36Sopenharmony_ci new_devdata = kzalloc(sizeof(*new_devdata), GFP_KERNEL); 122362306a36Sopenharmony_ci if (!new_devdata) 122462306a36Sopenharmony_ci return -ENOMEM; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci RCU_INIT_POINTER(devdata, new_devdata); 122762306a36Sopenharmony_ci /* 122862306a36Sopenharmony_ci * Get NX capabilities from the hypervisor. 122962306a36Sopenharmony_ci */ 123062306a36Sopenharmony_ci nxcop_get_capabilities(); 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci ret = vio_register_driver(&nx842_vio_driver); 123362306a36Sopenharmony_ci if (ret) { 123462306a36Sopenharmony_ci pr_err("Could not register VIO driver %d\n", ret); 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci kfree(new_devdata); 123762306a36Sopenharmony_ci return ret; 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci ret = vas_register_api_pseries(THIS_MODULE, VAS_COP_TYPE_GZIP, 124162306a36Sopenharmony_ci "nx-gzip"); 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci if (ret) 124462306a36Sopenharmony_ci pr_err("NX-GZIP is not supported. Returned=%d\n", ret); 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci return 0; 124762306a36Sopenharmony_ci} 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_cimodule_init(nx842_pseries_init); 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_cistatic void __exit nx842_pseries_exit(void) 125262306a36Sopenharmony_ci{ 125362306a36Sopenharmony_ci struct nx842_devdata *old_devdata; 125462306a36Sopenharmony_ci unsigned long flags; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci vas_unregister_api_pseries(); 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci crypto_unregister_alg(&nx842_pseries_alg); 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci spin_lock_irqsave(&devdata_mutex, flags); 126162306a36Sopenharmony_ci old_devdata = rcu_dereference_check(devdata, 126262306a36Sopenharmony_ci lockdep_is_held(&devdata_mutex)); 126362306a36Sopenharmony_ci RCU_INIT_POINTER(devdata, NULL); 126462306a36Sopenharmony_ci spin_unlock_irqrestore(&devdata_mutex, flags); 126562306a36Sopenharmony_ci synchronize_rcu(); 126662306a36Sopenharmony_ci if (old_devdata && old_devdata->dev) 126762306a36Sopenharmony_ci dev_set_drvdata(old_devdata->dev, NULL); 126862306a36Sopenharmony_ci kfree(old_devdata); 126962306a36Sopenharmony_ci vio_unregister_driver(&nx842_vio_driver); 127062306a36Sopenharmony_ci} 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_cimodule_exit(nx842_pseries_exit); 127362306a36Sopenharmony_ci 1274