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(&times[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(&times[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(&times[(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