// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for IBM Power 842 compression accelerator
*
* Copyright (C) IBM Corporation, 2012
*
* Authors: Robert Jennings <rcj@linux.vnet.ibm.com>
* Seth Jennings <sjenning@linux.vnet.ibm.com>
*/
#include <asm/vio.h>
#include "nx-842.h"
#include "nx_csbcpb.h" /* struct nx_csbcpb */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Robert Jennings <rcj@linux.vnet.ibm.com>");
MODULE_DESCRIPTION("842 H/W Compression driver for IBM Power processors");
MODULE_ALIAS_CRYPTO("842");
MODULE_ALIAS_CRYPTO("842-nx");
static struct nx842_constraints nx842_pseries_constraints = {
.alignment = DDE_BUFFER_ALIGN,
.multiple = DDE_BUFFER_LAST_MULT,
.minimum = DDE_BUFFER_LAST_MULT,
.maximum = PAGE_SIZE, /* dynamic, max_sync_size */
};
static int check_constraints(unsigned long buf, unsigned int *len, bool in)
{
if (!IS_ALIGNED(buf, nx842_pseries_constraints.alignment)) {
pr_debug("%s buffer 0x%lx not aligned to 0x%x\n",
in ? "input" : "output", buf,
nx842_pseries_constraints.alignment);
return -EINVAL;
}
if (*len % nx842_pseries_constraints.multiple) {
pr_debug("%s buffer len 0x%x not multiple of 0x%x\n",
in ? "input" : "output", *len,
nx842_pseries_constraints.multiple);
if (in)
return -EINVAL;
*len = round_down(*len, nx842_pseries_constraints.multiple);
}
if (*len < nx842_pseries_constraints.minimum) {
pr_debug("%s buffer len 0x%x under minimum 0x%x\n",
in ? "input" : "output", *len,
nx842_pseries_constraints.minimum);
return -EINVAL;
}
if (*len > nx842_pseries_constraints.maximum) {
pr_debug("%s buffer len 0x%x over maximum 0x%x\n",
in ? "input" : "output", *len,
nx842_pseries_constraints.maximum);
if (in)
return -EINVAL;
*len = nx842_pseries_constraints.maximum;
}
return 0;
}
/* I assume we need to align the CSB? */
#define WORKMEM_ALIGN (256)
struct nx842_workmem {
/* scatterlist */
char slin[4096];
char slout[4096];
/* coprocessor status/parameter block */
struct nx_csbcpb csbcpb;
char padding[WORKMEM_ALIGN];
} __aligned(WORKMEM_ALIGN);
/* Macros for fields within nx_csbcpb */
/* Check the valid bit within the csbcpb valid field */
#define NX842_CSBCBP_VALID_CHK(x) (x & BIT_MASK(7))
/* CE macros operate on the completion_extension field bits in the csbcpb.
* CE0 0=full completion, 1=partial completion
* CE1 0=CE0 indicates completion, 1=termination (output may be modified)
* CE2 0=processed_bytes is source bytes, 1=processed_bytes is target bytes */
#define NX842_CSBCPB_CE0(x) (x & BIT_MASK(7))
#define NX842_CSBCPB_CE1(x) (x & BIT_MASK(6))
#define NX842_CSBCPB_CE2(x) (x & BIT_MASK(5))
/* The NX unit accepts data only on 4K page boundaries */
#define NX842_HW_PAGE_SIZE (4096)
#define NX842_HW_PAGE_MASK (~(NX842_HW_PAGE_SIZE-1))
struct ibm_nx842_counters {
atomic64_t comp_complete;
atomic64_t comp_failed;
atomic64_t decomp_complete;
atomic64_t decomp_failed;
atomic64_t swdecomp;
atomic64_t comp_times[32];
atomic64_t decomp_times[32];
};
static struct nx842_devdata {
struct vio_dev *vdev;
struct device *dev;
struct ibm_nx842_counters *counters;
unsigned int max_sg_len;
unsigned int max_sync_size;
unsigned int max_sync_sg;
} __rcu *devdata;
static DEFINE_SPINLOCK(devdata_mutex);
#define NX842_COUNTER_INC(_x) \
static inline void nx842_inc_##_x( \
const struct nx842_devdata *dev) { \
if (dev) \
atomic64_inc(&dev->counters->_x); \
}
NX842_COUNTER_INC(comp_complete);
NX842_COUNTER_INC(comp_failed);
NX842_COUNTER_INC(decomp_complete);
NX842_COUNTER_INC(decomp_failed);
NX842_COUNTER_INC(swdecomp);
#define NX842_HIST_SLOTS 16
static void ibm_nx842_incr_hist(atomic64_t *times, unsigned int time)
{
int bucket = fls(time);
if (bucket)
bucket = min((NX842_HIST_SLOTS - 1), bucket - 1);
atomic64_inc(×[bucket]);
}
/* NX unit operation flags */
#define NX842_OP_COMPRESS 0x0
#define NX842_OP_CRC 0x1
#define NX842_OP_DECOMPRESS 0x2
#define NX842_OP_COMPRESS_CRC (NX842_OP_COMPRESS | NX842_OP_CRC)
#define NX842_OP_DECOMPRESS_CRC (NX842_OP_DECOMPRESS | NX842_OP_CRC)
#define NX842_OP_ASYNC (1<<23)
#define NX842_OP_NOTIFY (1<<22)
#define NX842_OP_NOTIFY_INT(x) ((x & 0xff)<<8)
static unsigned long nx842_get_desired_dma(struct vio_dev *viodev)
{
/* No use of DMA mappings within the driver. */
return 0;
}
struct nx842_slentry {
__be64 ptr; /* Real address (use __pa()) */
__be64 len;
};
/* pHyp scatterlist entry */
struct nx842_scatterlist {
int entry_nr; /* number of slentries */
struct nx842_slentry *entries; /* ptr to array of slentries */
};
/* Does not include sizeof(entry_nr) in the size */
static inline unsigned long nx842_get_scatterlist_size(
struct nx842_scatterlist *sl)
{
return sl->entry_nr * sizeof(struct nx842_slentry);
}
static int nx842_build_scatterlist(unsigned long buf, int len,
struct nx842_scatterlist *sl)
{
unsigned long entrylen;
struct nx842