Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
source "drivers/crypto/intel/keembay/Kconfig"
source "drivers/crypto/intel/ixp4xx/Kconfig"
source "drivers/crypto/intel/qat/Kconfig"
source "drivers/crypto/intel/iaa/Kconfig"
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-y += keembay/
obj-y += ixp4xx/
obj-$(CONFIG_CRYPTO_DEV_QAT) += qat/
obj-$(CONFIG_CRYPTO_DEV_IAA_CRYPTO) += iaa/
@@ -0,0 +1,19 @@
config CRYPTO_DEV_IAA_CRYPTO
tristate "Support for Intel(R) IAA Compression Accelerator"
depends on CRYPTO_DEFLATE
depends on INTEL_IDXD
default n
help
This driver supports acceleration for compression and
decompression with the Intel Analytics Accelerator (IAA)
hardware using the cryptographic API. If you choose 'M'
here, the module will be called iaa_crypto.
config CRYPTO_DEV_IAA_CRYPTO_STATS
bool "Enable Intel(R) IAA Compression Accelerator Statistics"
depends on CRYPTO_DEV_IAA_CRYPTO
default n
help
Enable statistics for the IAA compression accelerator.
These include per-device and per-workqueue statistics in
addition to global driver statistics.
@@ -0,0 +1,12 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for IAA crypto device drivers
#
ccflags-y += -I $(srctree)/drivers/dma/idxd -DDEFAULT_SYMBOL_NAMESPACE='"IDXD"'
obj-$(CONFIG_CRYPTO_DEV_IAA_CRYPTO) := iaa_crypto.o
iaa_crypto-y := iaa_crypto_main.o iaa_crypto_comp_fixed.o
iaa_crypto-$(CONFIG_CRYPTO_DEV_IAA_CRYPTO_STATS) += iaa_crypto_stats.o
@@ -0,0 +1,148 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2021 Intel Corporation. All rights rsvd. */
#ifndef __IAA_CRYPTO_H__
#define __IAA_CRYPTO_H__
#include <linux/crypto.h>
#include <linux/idxd.h>
#include <uapi/linux/idxd.h>
#define IDXD_SUBDRIVER_NAME "crypto"
#define IAA_DECOMP_ENABLE BIT(0)
#define IAA_DECOMP_FLUSH_OUTPUT BIT(1)
#define IAA_DECOMP_CHECK_FOR_EOB BIT(2)
#define IAA_DECOMP_STOP_ON_EOB BIT(3)
#define IAA_DECOMP_SUPPRESS_OUTPUT BIT(9)
#define IAA_COMP_FLUSH_OUTPUT BIT(1)
#define IAA_COMP_APPEND_EOB BIT(2)
#define IAA_COMPLETION_TIMEOUT 1000000
#define IAA_ANALYTICS_ERROR 0x0a
#define IAA_ERROR_DECOMP_BUF_OVERFLOW 0x0b
#define IAA_ERROR_COMP_BUF_OVERFLOW 0x19
#define IAA_ERROR_WATCHDOG_EXPIRED 0x24
#define IAA_COMP_MODES_MAX 2
#define FIXED_HDR 0x2
#define FIXED_HDR_SIZE 3
#define IAA_COMP_FLAGS (IAA_COMP_FLUSH_OUTPUT | \
IAA_COMP_APPEND_EOB)
#define IAA_DECOMP_FLAGS (IAA_DECOMP_ENABLE | \
IAA_DECOMP_FLUSH_OUTPUT | \
IAA_DECOMP_CHECK_FOR_EOB | \
IAA_DECOMP_STOP_ON_EOB)
/* Representation of IAA workqueue */
struct iaa_wq {
struct list_head list;
struct idxd_wq *wq;
int ref;
bool remove;
struct iaa_device *iaa_device;
atomic64_t comp_calls;
atomic64_t comp_bytes;
atomic64_t decomp_calls;
atomic64_t decomp_bytes;
};
struct iaa_device_compression_mode {
const char *name;
struct aecs_comp_table_record *aecs_comp_table;
dma_addr_t aecs_comp_table_dma_addr;
};
/* Representation of IAA device with wqs, populated by probe */
struct iaa_device {
struct list_head list;
struct idxd_device *idxd;
struct iaa_device_compression_mode *compression_modes[IAA_COMP_MODES_MAX];
int n_wq;
struct list_head wqs;
atomic64_t comp_calls;
atomic64_t comp_bytes;
atomic64_t decomp_calls;
atomic64_t decomp_bytes;
};
struct wq_table_entry {
struct idxd_wq **wqs;
int max_wqs;
int n_wqs;
int cur_wq;
};
#define IAA_AECS_ALIGN 32
/*
* Analytics Engine Configuration and State (AECS) contains parameters and
* internal state of the analytics engine.
*/
struct aecs_comp_table_record {
u32 crc;
u32 xor_checksum;
u32 reserved0[5];
u32 num_output_accum_bits;
u8 output_accum[256];
u32 ll_sym[286];
u32 reserved1;
u32 reserved2;
u32 d_sym[30];
u32 reserved_padding[2];
} __packed;
int iaa_aecs_init_fixed(void);
void iaa_aecs_cleanup_fixed(void);
typedef int (*iaa_dev_comp_init_fn_t) (struct iaa_device_compression_mode *mode);
typedef int (*iaa_dev_comp_free_fn_t) (struct iaa_device_compression_mode *mode);
struct iaa_compression_mode {
const char *name;
u32 *ll_table;
int ll_table_size;
u32 *d_table;
int d_table_size;
iaa_dev_comp_init_fn_t init;
iaa_dev_comp_free_fn_t free;
};
int add_iaa_compression_mode(const char *name,
const u32 *ll_table,
int ll_table_size,
const u32 *d_table,
int d_table_size,
iaa_dev_comp_init_fn_t init,
iaa_dev_comp_free_fn_t free);
void remove_iaa_compression_mode(const char *name);
enum iaa_mode {
IAA_MODE_FIXED,
};
struct iaa_compression_ctx {
enum iaa_mode mode;
bool verify_compress;
bool async_mode;
bool use_irq;
};
extern struct list_head iaa_devices;
extern struct mutex iaa_devices_lock;
#endif
@@ -0,0 +1,91 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2021 Intel Corporation. All rights rsvd. */
#include "idxd.h"
#include "iaa_crypto.h"
/*
* Fixed Huffman tables the IAA hardware requires to implement RFC-1951.
*/
static const u32 fixed_ll_sym[286] = {
0x40030, 0x40031, 0x40032, 0x40033, 0x40034, 0x40035, 0x40036, 0x40037,
0x40038, 0x40039, 0x4003A, 0x4003B, 0x4003C, 0x4003D, 0x4003E, 0x4003F,
0x40040, 0x40041, 0x40042, 0x40043, 0x40044, 0x40045, 0x40046, 0x40047,
0x40048, 0x40049, 0x4004A, 0x4004B, 0x4004C, 0x4004D, 0x4004E, 0x4004F,
0x40050, 0x40051, 0x40052, 0x40053, 0x40054, 0x40055, 0x40056, 0x40057,
0x40058, 0x40059, 0x4005A, 0x4005B, 0x4005C, 0x4005D, 0x4005E, 0x4005F,
0x40060, 0x40061, 0x40062, 0x40063, 0x40064, 0x40065, 0x40066, 0x40067,
0x40068, 0x40069, 0x4006A, 0x4006B, 0x4006C, 0x4006D, 0x4006E, 0x4006F,
0x40070, 0x40071, 0x40072, 0x40073, 0x40074, 0x40075, 0x40076, 0x40077,
0x40078, 0x40079, 0x4007A, 0x4007B, 0x4007C, 0x4007D, 0x4007E, 0x4007F,
0x40080, 0x40081, 0x40082, 0x40083, 0x40084, 0x40085, 0x40086, 0x40087,
0x40088, 0x40089, 0x4008A, 0x4008B, 0x4008C, 0x4008D, 0x4008E, 0x4008F,
0x40090, 0x40091, 0x40092, 0x40093, 0x40094, 0x40095, 0x40096, 0x40097,
0x40098, 0x40099, 0x4009A, 0x4009B, 0x4009C, 0x4009D, 0x4009E, 0x4009F,
0x400A0, 0x400A1, 0x400A2, 0x400A3, 0x400A4, 0x400A5, 0x400A6, 0x400A7,
0x400A8, 0x400A9, 0x400AA, 0x400AB, 0x400AC, 0x400AD, 0x400AE, 0x400AF,
0x400B0, 0x400B1, 0x400B2, 0x400B3, 0x400B4, 0x400B5, 0x400B6, 0x400B7,
0x400B8, 0x400B9, 0x400BA, 0x400BB, 0x400BC, 0x400BD, 0x400BE, 0x400BF,
0x48190, 0x48191, 0x48192, 0x48193, 0x48194, 0x48195, 0x48196, 0x48197,
0x48198, 0x48199, 0x4819A, 0x4819B, 0x4819C, 0x4819D, 0x4819E, 0x4819F,
0x481A0, 0x481A1, 0x481A2, 0x481A3, 0x481A4, 0x481A5, 0x481A6, 0x481A7,
0x481A8, 0x481A9, 0x481AA, 0x481AB, 0x481AC, 0x481AD, 0x481AE, 0x481AF,
0x481B0, 0x481B1, 0x481B2, 0x481B3, 0x481B4, 0x481B5, 0x481B6, 0x481B7,
0x481B8, 0x481B9, 0x481BA, 0x481BB, 0x481BC, 0x481BD, 0x481BE, 0x481BF,
0x481C0, 0x481C1, 0x481C2, 0x481C3, 0x481C4, 0x481C5, 0x481C6, 0x481C7,
0x481C8, 0x481C9, 0x481CA, 0x481CB, 0x481CC, 0x481CD, 0x481CE, 0x481CF,
0x481D0, 0x481D1, 0x481D2, 0x481D3, 0x481D4, 0x481D5, 0x481D6, 0x481D7,
0x481D8, 0x481D9, 0x481DA, 0x481DB, 0x481DC, 0x481DD, 0x481DE, 0x481DF,
0x481E0, 0x481E1, 0x481E2, 0x481E3, 0x481E4, 0x481E5, 0x481E6, 0x481E7,
0x481E8, 0x481E9, 0x481EA, 0x481EB, 0x481EC, 0x481ED, 0x481EE, 0x481EF,
0x481F0, 0x481F1, 0x481F2, 0x481F3, 0x481F4, 0x481F5, 0x481F6, 0x481F7,
0x481F8, 0x481F9, 0x481FA, 0x481FB, 0x481FC, 0x481FD, 0x481FE, 0x481FF,
0x38000, 0x38001, 0x38002, 0x38003, 0x38004, 0x38005, 0x38006, 0x38007,
0x38008, 0x38009, 0x3800A, 0x3800B, 0x3800C, 0x3800D, 0x3800E, 0x3800F,
0x38010, 0x38011, 0x38012, 0x38013, 0x38014, 0x38015, 0x38016, 0x38017,
0x400C0, 0x400C1, 0x400C2, 0x400C3, 0x400C4, 0x400C5
};
static const u32 fixed_d_sym[30] = {
0x28000, 0x28001, 0x28002, 0x28003, 0x28004, 0x28005, 0x28006, 0x28007,
0x28008, 0x28009, 0x2800A, 0x2800B, 0x2800C, 0x2800D, 0x2800E, 0x2800F,
0x28010, 0x28011, 0x28012, 0x28013, 0x28014, 0x28015, 0x28016, 0x28017,
0x28018, 0x28019, 0x2801A, 0x2801B, 0x2801C, 0x2801D
};
static int init_fixed_mode(struct iaa_device_compression_mode *mode)
{
struct aecs_comp_table_record *comp_table = mode->aecs_comp_table;
u32 bfinal = 1;
u32 offset;
/* Configure aecs table using fixed Huffman table */
comp_table->crc = 0;
comp_table->xor_checksum = 0;
offset = comp_table->num_output_accum_bits / 8;
comp_table->output_accum[offset] = FIXED_HDR | bfinal;
comp_table->num_output_accum_bits = FIXED_HDR_SIZE;
return 0;
}
int iaa_aecs_init_fixed(void)
{
int ret;
ret = add_iaa_compression_mode("fixed",
fixed_ll_sym,
sizeof(fixed_ll_sym),
fixed_d_sym,
sizeof(fixed_d_sym),
init_fixed_mode, NULL);
if (!ret)
pr_debug("IAA fixed compression mode initialized\n");
return ret;
}
void iaa_aecs_cleanup_fixed(void)
{
remove_iaa_compression_mode("fixed");
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,280 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2021 Intel Corporation. All rights rsvd. */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/highmem.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/smp.h>
#include <uapi/linux/idxd.h>
#include <linux/idxd.h>
#include <linux/dmaengine.h>
#include "../../dma/idxd/idxd.h"
#include <linux/debugfs.h>
#include <crypto/internal/acompress.h>
#include "iaa_crypto.h"
#include "iaa_crypto_stats.h"
static atomic64_t total_comp_calls;
static atomic64_t total_decomp_calls;
static atomic64_t total_sw_comp_calls;
static atomic64_t total_sw_decomp_calls;
static atomic64_t total_comp_bytes_out;
static atomic64_t total_decomp_bytes_in;
static atomic64_t total_completion_einval_errors;
static atomic64_t total_completion_timeout_errors;
static atomic64_t total_completion_comp_buf_overflow_errors;
static struct dentry *iaa_crypto_debugfs_root;
void update_total_comp_calls(void)
{
atomic64_inc(&total_comp_calls);
}
void update_total_comp_bytes_out(int n)
{
atomic64_add(n, &total_comp_bytes_out);
}
void update_total_decomp_calls(void)
{
atomic64_inc(&total_decomp_calls);
}
void update_total_sw_comp_calls(void)
{
atomic64_inc(&total_sw_comp_calls);
}
void update_total_sw_decomp_calls(void)
{
atomic64_inc(&total_sw_decomp_calls);
}
void update_total_decomp_bytes_in(int n)
{
atomic64_add(n, &total_decomp_bytes_in);
}
void update_completion_einval_errs(void)
{
atomic64_inc(&total_completion_einval_errors);
}
void update_completion_timeout_errs(void)
{
atomic64_inc(&total_completion_timeout_errors);
}
void update_completion_comp_buf_overflow_errs(void)
{
atomic64_inc(&total_completion_comp_buf_overflow_errors);
}
void update_wq_comp_calls(struct idxd_wq *idxd_wq)
{
struct iaa_wq *wq = idxd_wq_get_private(idxd_wq);
atomic64_inc(&wq->comp_calls);
atomic64_inc(&wq->iaa_device->comp_calls);
}
void update_wq_comp_bytes(struct idxd_wq *idxd_wq, int n)
{
struct iaa_wq *wq = idxd_wq_get_private(idxd_wq);
atomic64_add(n, &wq->comp_bytes);
atomic64_add(n, &wq->iaa_device->comp_bytes);
}
void update_wq_decomp_calls(struct idxd_wq *idxd_wq)
{
struct iaa_wq *wq = idxd_wq_get_private(idxd_wq);
atomic64_inc(&wq->decomp_calls);
atomic64_inc(&wq->iaa_device->decomp_calls);
}
void update_wq_decomp_bytes(struct idxd_wq *idxd_wq, int n)
{
struct iaa_wq *wq = idxd_wq_get_private(idxd_wq);
atomic64_add(n, &wq->decomp_bytes);
atomic64_add(n, &wq->iaa_device->decomp_bytes);
}
static void reset_iaa_crypto_stats(void)
{
atomic64_set(&total_comp_calls, 0);
atomic64_set(&total_decomp_calls, 0);
atomic64_set(&total_sw_comp_calls, 0);
atomic64_set(&total_sw_decomp_calls, 0);
atomic64_set(&total_comp_bytes_out, 0);
atomic64_set(&total_decomp_bytes_in, 0);
atomic64_set(&total_completion_einval_errors, 0);
atomic64_set(&total_completion_timeout_errors, 0);
atomic64_set(&total_completion_comp_buf_overflow_errors, 0);
}
static void reset_wq_stats(struct iaa_wq *wq)
{
atomic64_set(&wq->comp_calls, 0);
atomic64_set(&wq->comp_bytes, 0);
atomic64_set(&wq->decomp_calls, 0);
atomic64_set(&wq->decomp_bytes, 0);
}
static void reset_device_stats(struct iaa_device *iaa_device)
{
struct iaa_wq *iaa_wq;
atomic64_set(&iaa_device->comp_calls, 0);
atomic64_set(&iaa_device->comp_bytes, 0);
atomic64_set(&iaa_device->decomp_calls, 0);
atomic64_set(&iaa_device->decomp_bytes, 0);
list_for_each_entry(iaa_wq, &iaa_device->wqs, list)
reset_wq_stats(iaa_wq);
}
static void wq_show(struct seq_file *m, struct iaa_wq *iaa_wq)
{
seq_printf(m, " name: %s\n", iaa_wq->wq->name);
seq_printf(m, " comp_calls: %llu\n",
atomic64_read(&iaa_wq->comp_calls));
seq_printf(m, " comp_bytes: %llu\n",
atomic64_read(&iaa_wq->comp_bytes));
seq_printf(m, " decomp_calls: %llu\n",
atomic64_read(&iaa_wq->decomp_calls));
seq_printf(m, " decomp_bytes: %llu\n\n",
atomic64_read(&iaa_wq->decomp_bytes));
}
static void device_stats_show(struct seq_file *m, struct iaa_device *iaa_device)
{
struct iaa_wq *iaa_wq;
seq_puts(m, "iaa device:\n");
seq_printf(m, " id: %d\n", iaa_device->idxd->id);
seq_printf(m, " n_wqs: %d\n", iaa_device->n_wq);
seq_printf(m, " comp_calls: %llu\n",
atomic64_read(&iaa_device->comp_calls));
seq_printf(m, " comp_bytes: %llu\n",
atomic64_read(&iaa_device->comp_bytes));
seq_printf(m, " decomp_calls: %llu\n",
atomic64_read(&iaa_device->decomp_calls));
seq_printf(m, " decomp_bytes: %llu\n",
atomic64_read(&iaa_device->decomp_bytes));
seq_puts(m, " wqs:\n");
list_for_each_entry(iaa_wq, &iaa_device->wqs, list)
wq_show(m, iaa_wq);
}
static int global_stats_show(struct seq_file *m, void *v)
{
seq_puts(m, "global stats:\n");
seq_printf(m, " total_comp_calls: %llu\n",
atomic64_read(&total_comp_calls));
seq_printf(m, " total_decomp_calls: %llu\n",
atomic64_read(&total_decomp_calls));
seq_printf(m, " total_sw_comp_calls: %llu\n",
atomic64_read(&total_sw_comp_calls));
seq_printf(m, " total_sw_decomp_calls: %llu\n",
atomic64_read(&total_sw_decomp_calls));
seq_printf(m, " total_comp_bytes_out: %llu\n",
atomic64_read(&total_comp_bytes_out));
seq_printf(m, " total_decomp_bytes_in: %llu\n",
atomic64_read(&total_decomp_bytes_in));
seq_printf(m, " total_completion_einval_errors: %llu\n",
atomic64_read(&total_completion_einval_errors));
seq_printf(m, " total_completion_timeout_errors: %llu\n",
atomic64_read(&total_completion_timeout_errors));
seq_printf(m, " total_completion_comp_buf_overflow_errors: %llu\n\n",
atomic64_read(&total_completion_comp_buf_overflow_errors));
return 0;
}
static int wq_stats_show(struct seq_file *m, void *v)
{
struct iaa_device *iaa_device;
mutex_lock(&iaa_devices_lock);
list_for_each_entry(iaa_device, &iaa_devices, list)
device_stats_show(m, iaa_device);
mutex_unlock(&iaa_devices_lock);
return 0;
}
static int iaa_crypto_stats_reset(void *data, u64 value)
{
struct iaa_device *iaa_device;
reset_iaa_crypto_stats();
mutex_lock(&iaa_devices_lock);
list_for_each_entry(iaa_device, &iaa_devices, list)
reset_device_stats(iaa_device);
mutex_unlock(&iaa_devices_lock);
return 0;
}
static int wq_stats_open(struct inode *inode, struct file *file)
{
return single_open(file, wq_stats_show, file);
}
static const struct file_operations wq_stats_fops = {
.open = wq_stats_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int global_stats_open(struct inode *inode, struct file *file)
{
return single_open(file, global_stats_show, file);
}
static const struct file_operations global_stats_fops = {
.open = global_stats_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
DEFINE_DEBUGFS_ATTRIBUTE(wq_stats_reset_fops, NULL, iaa_crypto_stats_reset, "%llu\n");
int __init iaa_crypto_debugfs_init(void)
{
if (!debugfs_initialized())
return -ENODEV;
iaa_crypto_debugfs_root = debugfs_create_dir("iaa_crypto", NULL);
debugfs_create_file("global_stats", 0644, iaa_crypto_debugfs_root, NULL,
&global_stats_fops);
debugfs_create_file("wq_stats", 0644, iaa_crypto_debugfs_root, NULL,
&wq_stats_fops);
debugfs_create_file("stats_reset", 0644, iaa_crypto_debugfs_root, NULL,
&wq_stats_reset_fops);
return 0;
}
void __exit iaa_crypto_debugfs_cleanup(void)
{
debugfs_remove_recursive(iaa_crypto_debugfs_root);
}
MODULE_LICENSE("GPL");
@@ -0,0 +1,47 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2021 Intel Corporation. All rights rsvd. */
#ifndef __CRYPTO_DEV_IAA_CRYPTO_STATS_H__
#define __CRYPTO_DEV_IAA_CRYPTO_STATS_H__
#if defined(CONFIG_CRYPTO_DEV_IAA_CRYPTO_STATS)
int iaa_crypto_debugfs_init(void);
void iaa_crypto_debugfs_cleanup(void);
void update_total_comp_calls(void);
void update_total_comp_bytes_out(int n);
void update_total_decomp_calls(void);
void update_total_sw_comp_calls(void);
void update_total_sw_decomp_calls(void);
void update_total_decomp_bytes_in(int n);
void update_completion_einval_errs(void);
void update_completion_timeout_errs(void);
void update_completion_comp_buf_overflow_errs(void);
void update_wq_comp_calls(struct idxd_wq *idxd_wq);
void update_wq_comp_bytes(struct idxd_wq *idxd_wq, int n);
void update_wq_decomp_calls(struct idxd_wq *idxd_wq);
void update_wq_decomp_bytes(struct idxd_wq *idxd_wq, int n);
#else
static inline int iaa_crypto_debugfs_init(void) { return 0; }
static inline void iaa_crypto_debugfs_cleanup(void) {}
static inline void update_total_comp_calls(void) {}
static inline void update_total_comp_bytes_out(int n) {}
static inline void update_total_decomp_calls(void) {}
static inline void update_total_sw_comp_calls(void) {}
static inline void update_total_sw_decomp_calls(void) {}
static inline void update_total_decomp_bytes_in(int n) {}
static inline void update_completion_einval_errs(void) {}
static inline void update_completion_timeout_errs(void) {}
static inline void update_completion_comp_buf_overflow_errs(void) {}
static inline void update_wq_comp_calls(struct idxd_wq *idxd_wq) {}
static inline void update_wq_comp_bytes(struct idxd_wq *idxd_wq, int n) {}
static inline void update_wq_decomp_calls(struct idxd_wq *idxd_wq) {}
static inline void update_wq_decomp_bytes(struct idxd_wq *idxd_wq, int n) {}
#endif // CONFIG_CRYPTO_DEV_IAA_CRYPTO_STATS
#endif
@@ -0,0 +1,14 @@
config CRYPTO_DEV_IXP4XX
tristate "Driver for IXP4xx crypto hardware acceleration"
depends on (ARCH_IXP4XX || COMPILE_TEST) && IXP4XX_QMGR && IXP4XX_NPE
select CRYPTO_AES
select CRYPTO_DES
select CRYPTO_ECB
select CRYPTO_CBC
select CRYPTO_CTR
select CRYPTO_LIB_DES
select CRYPTO_AEAD
select CRYPTO_AUTHENC
select CRYPTO_SKCIPHER
help
Driver for the IXP4xx NPE crypto engine.
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,90 @@
config CRYPTO_DEV_KEEMBAY_OCS_AES_SM4
tristate "Support for Intel Keem Bay OCS AES/SM4 HW acceleration"
depends on HAS_IOMEM
depends on ARCH_KEEMBAY || COMPILE_TEST
select CRYPTO_SKCIPHER
select CRYPTO_AEAD
select CRYPTO_ENGINE
help
Support for Intel Keem Bay Offload and Crypto Subsystem (OCS) AES and
SM4 cipher hardware acceleration for use with Crypto API.
Provides HW acceleration for the following transformations:
cbc(aes), ctr(aes), ccm(aes), gcm(aes), cbc(sm4), ctr(sm4), ccm(sm4)
and gcm(sm4).
Optionally, support for the following transformations can also be
enabled: ecb(aes), cts(cbc(aes)), ecb(sm4) and cts(cbc(sm4)).
config CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_ECB
bool "Support for Intel Keem Bay OCS AES/SM4 ECB HW acceleration"
depends on CRYPTO_DEV_KEEMBAY_OCS_AES_SM4
help
Support for Intel Keem Bay Offload and Crypto Subsystem (OCS)
AES/SM4 ECB mode hardware acceleration for use with Crypto API.
Provides OCS version of ecb(aes) and ecb(sm4)
Intel does not recommend use of ECB mode with AES/SM4.
config CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_CTS
bool "Support for Intel Keem Bay OCS AES/SM4 CTS HW acceleration"
depends on CRYPTO_DEV_KEEMBAY_OCS_AES_SM4
help
Support for Intel Keem Bay Offload and Crypto Subsystem (OCS)
AES/SM4 CBC with CTS mode hardware acceleration for use with
Crypto API.
Provides OCS version of cts(cbc(aes)) and cts(cbc(sm4)).
Intel does not recommend use of CTS mode with AES/SM4.
config CRYPTO_DEV_KEEMBAY_OCS_ECC
tristate "Support for Intel Keem Bay OCS ECC HW acceleration"
depends on ARCH_KEEMBAY || COMPILE_TEST
depends on OF
depends on HAS_IOMEM
select CRYPTO_ECDH
select CRYPTO_ENGINE
help
Support for Intel Keem Bay Offload and Crypto Subsystem (OCS)
Elliptic Curve Cryptography (ECC) hardware acceleration for use with
Crypto API.
Provides OCS acceleration for ECDH-256 and ECDH-384.
Say Y or M if you are compiling for the Intel Keem Bay SoC. The
module will be called keembay-ocs-ecc.
If unsure, say N.
config CRYPTO_DEV_KEEMBAY_OCS_HCU
tristate "Support for Intel Keem Bay OCS HCU HW acceleration"
select CRYPTO_HASH
select CRYPTO_ENGINE
depends on HAS_IOMEM
depends on ARCH_KEEMBAY || COMPILE_TEST
depends on OF
help
Support for Intel Keem Bay Offload and Crypto Subsystem (OCS) Hash
Control Unit (HCU) hardware acceleration for use with Crypto API.
Provides OCS HCU hardware acceleration of sha256, sha384, sha512, and
sm3, as well as the HMAC variant of these algorithms.
Say Y or M if you're building for the Intel Keem Bay SoC. If compiled
as a module, the module will be called keembay-ocs-hcu.
If unsure, say N.
config CRYPTO_DEV_KEEMBAY_OCS_HCU_HMAC_SHA224
bool "Enable sha224 and hmac(sha224) support in Intel Keem Bay OCS HCU"
depends on CRYPTO_DEV_KEEMBAY_OCS_HCU
help
Enables support for sha224 and hmac(sha224) algorithms in the Intel
Keem Bay OCS HCU driver. Intel recommends not to use these
algorithms.
Provides OCS HCU hardware acceleration of sha224 and hmac(224).
If unsure, say N.
@@ -0,0 +1,10 @@
#
# Makefile for Intel Keem Bay OCS Crypto API Linux drivers
#
obj-$(CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4) += keembay-ocs-aes.o
keembay-ocs-aes-objs := keembay-ocs-aes-core.o ocs-aes.o
obj-$(CONFIG_CRYPTO_DEV_KEEMBAY_OCS_ECC) += keembay-ocs-ecc.o
obj-$(CONFIG_CRYPTO_DEV_KEEMBAY_OCS_HCU) += keembay-ocs-hcu.o
keembay-ocs-hcu-objs := keembay-ocs-hcu-core.o ocs-hcu.o
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,998 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel Keem Bay OCS ECC Crypto Driver.
*
* Copyright (C) 2019-2021 Intel Corporation
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <crypto/ecc_curve.h>
#include <crypto/ecdh.h>
#include <crypto/engine.h>
#include <crypto/internal/ecc.h>
#include <crypto/internal/kpp.h>
#include <crypto/kpp.h>
#include <crypto/rng.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/err.h>
#include <linux/fips.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/scatterlist.h>
#include <linux/string.h>
#define DRV_NAME "keembay-ocs-ecc"
#define KMB_OCS_ECC_PRIORITY 350
#define HW_OFFS_OCS_ECC_COMMAND 0x00000000
#define HW_OFFS_OCS_ECC_STATUS 0x00000004
#define HW_OFFS_OCS_ECC_DATA_IN 0x00000080
#define HW_OFFS_OCS_ECC_CX_DATA_OUT 0x00000100
#define HW_OFFS_OCS_ECC_CY_DATA_OUT 0x00000180
#define HW_OFFS_OCS_ECC_ISR 0x00000400
#define HW_OFFS_OCS_ECC_IER 0x00000404
#define HW_OCS_ECC_ISR_INT_STATUS_DONE BIT(0)
#define HW_OCS_ECC_COMMAND_INS_BP BIT(0)
#define HW_OCS_ECC_COMMAND_START_VAL BIT(0)
#define OCS_ECC_OP_SIZE_384 BIT(8)
#define OCS_ECC_OP_SIZE_256 0
/* ECC Instruction : for ECC_COMMAND */
#define OCS_ECC_INST_WRITE_AX (0x1 << HW_OCS_ECC_COMMAND_INS_BP)
#define OCS_ECC_INST_WRITE_AY (0x2 << HW_OCS_ECC_COMMAND_INS_BP)
#define OCS_ECC_INST_WRITE_BX_D (0x3 << HW_OCS_ECC_COMMAND_INS_BP)
#define OCS_ECC_INST_WRITE_BY_L (0x4 << HW_OCS_ECC_COMMAND_INS_BP)
#define OCS_ECC_INST_WRITE_P (0x5 << HW_OCS_ECC_COMMAND_INS_BP)
#define OCS_ECC_INST_WRITE_A (0x6 << HW_OCS_ECC_COMMAND_INS_BP)
#define OCS_ECC_INST_CALC_D_IDX_A (0x8 << HW_OCS_ECC_COMMAND_INS_BP)
#define OCS_ECC_INST_CALC_A_POW_B_MODP (0xB << HW_OCS_ECC_COMMAND_INS_BP)
#define OCS_ECC_INST_CALC_A_MUL_B_MODP (0xC << HW_OCS_ECC_COMMAND_INS_BP)
#define OCS_ECC_INST_CALC_A_ADD_B_MODP (0xD << HW_OCS_ECC_COMMAND_INS_BP)
#define ECC_ENABLE_INTR 1
#define POLL_USEC 100
#define TIMEOUT_USEC 10000
#define KMB_ECC_VLI_MAX_DIGITS ECC_CURVE_NIST_P384_DIGITS
#define KMB_ECC_VLI_MAX_BYTES (KMB_ECC_VLI_MAX_DIGITS \
<< ECC_DIGITS_TO_BYTES_SHIFT)
#define POW_CUBE 3
/**
* struct ocs_ecc_dev - ECC device context
* @list: List of device contexts
* @dev: OCS ECC device
* @base_reg: IO base address of OCS ECC
* @engine: Crypto engine for the device
* @irq_done: IRQ done completion.
* @irq: IRQ number
*/
struct ocs_ecc_dev {
struct list_head list;
struct device *dev;
void __iomem *base_reg;
struct crypto_engine *engine;
struct completion irq_done;
int irq;
};
/**
* struct ocs_ecc_ctx - Transformation context.
* @ecc_dev: The ECC driver associated with this context.
* @curve: The elliptic curve used by this transformation.
* @private_key: The private key.
*/
struct ocs_ecc_ctx {
struct ocs_ecc_dev *ecc_dev;
const struct ecc_curve *curve;
u64 private_key[KMB_ECC_VLI_MAX_DIGITS];
};
/* Driver data. */
struct ocs_ecc_drv {
struct list_head dev_list;
spinlock_t lock; /* Protects dev_list. */
};
/* Global variable holding the list of OCS ECC devices (only one expected). */
static struct ocs_ecc_drv ocs_ecc = {
.dev_list = LIST_HEAD_INIT(ocs_ecc.dev_list),
.lock = __SPIN_LOCK_UNLOCKED(ocs_ecc.lock),
};
/* Get OCS ECC tfm context from kpp_request. */
static inline struct ocs_ecc_ctx *kmb_ocs_ecc_tctx(struct kpp_request *req)
{
return kpp_tfm_ctx(crypto_kpp_reqtfm(req));
}
/* Converts number of digits to number of bytes. */
static inline unsigned int digits_to_bytes(unsigned int n)
{
return n << ECC_DIGITS_TO_BYTES_SHIFT;
}
/*
* Wait for ECC idle i.e when an operation (other than write operations)
* is done.
*/
static inline int ocs_ecc_wait_idle(struct ocs_ecc_dev *dev)
{
u32 value;
return readl_poll_timeout((dev->base_reg + HW_OFFS_OCS_ECC_STATUS),
value,
!(value & HW_OCS_ECC_ISR_INT_STATUS_DONE),
POLL_USEC, TIMEOUT_USEC);
}
static void ocs_ecc_cmd_start(struct ocs_ecc_dev *ecc_dev, u32 op_size)
{
iowrite32(op_size | HW_OCS_ECC_COMMAND_START_VAL,
ecc_dev->base_reg + HW_OFFS_OCS_ECC_COMMAND);
}
/* Direct write of u32 buffer to ECC engine with associated instruction. */
static void ocs_ecc_write_cmd_and_data(struct ocs_ecc_dev *dev,
u32 op_size,
u32 inst,
const void *data_in,
size_t data_size)
{
iowrite32(op_size | inst, dev->base_reg + HW_OFFS_OCS_ECC_COMMAND);
/* MMIO Write src uint32 to dst. */
memcpy_toio(dev->base_reg + HW_OFFS_OCS_ECC_DATA_IN, data_in,
data_size);
}
/* Start OCS ECC operation and wait for its completion. */
static int ocs_ecc_trigger_op(struct ocs_ecc_dev *ecc_dev, u32 op_size,
u32 inst)
{
reinit_completion(&ecc_dev->irq_done);
iowrite32(ECC_ENABLE_INTR, ecc_dev->base_reg + HW_OFFS_OCS_ECC_IER);
iowrite32(op_size | inst, ecc_dev->base_reg + HW_OFFS_OCS_ECC_COMMAND);
return wait_for_completion_interruptible(&ecc_dev->irq_done);
}
/**
* ocs_ecc_read_cx_out() - Read the CX data output buffer.
* @dev: The OCS ECC device to read from.
* @cx_out: The buffer where to store the CX value. Must be at least
* @byte_count byte long.
* @byte_count: The amount of data to read.
*/
static inline void ocs_ecc_read_cx_out(struct ocs_ecc_dev *dev, void *cx_out,
size_t byte_count)
{
memcpy_fromio(cx_out, dev->base_reg + HW_OFFS_OCS_ECC_CX_DATA_OUT,
byte_count);
}
/**
* ocs_ecc_read_cy_out() - Read the CX data output buffer.
* @dev: The OCS ECC device to read from.
* @cy_out: The buffer where to store the CY value. Must be at least
* @byte_count byte long.
* @byte_count: The amount of data to read.
*/
static inline void ocs_ecc_read_cy_out(struct ocs_ecc_dev *dev, void *cy_out,
size_t byte_count)
{
memcpy_fromio(cy_out, dev->base_reg + HW_OFFS_OCS_ECC_CY_DATA_OUT,
byte_count);
}
static struct ocs_ecc_dev *kmb_ocs_ecc_find_dev(struct ocs_ecc_ctx *tctx)
{
if (tctx->ecc_dev)
return tctx->ecc_dev;
spin_lock(&ocs_ecc.lock);
/* Only a single OCS device available. */
tctx->ecc_dev = list_first_entry(&ocs_ecc.dev_list, struct ocs_ecc_dev,
list);
spin_unlock(&ocs_ecc.lock);
return tctx->ecc_dev;
}
/* Do point multiplication using OCS ECC HW. */
static int kmb_ecc_point_mult(struct ocs_ecc_dev *ecc_dev,
struct ecc_point *result,
const struct ecc_point *point,
u64 *scalar,
const struct ecc_curve *curve)
{
u8 sca[KMB_ECC_VLI_MAX_BYTES]; /* Use the maximum data size. */
u32 op_size = (curve->g.ndigits > ECC_CURVE_NIST_P256_DIGITS) ?
OCS_ECC_OP_SIZE_384 : OCS_ECC_OP_SIZE_256;
size_t nbytes = digits_to_bytes(curve->g.ndigits);
int rc = 0;
/* Generate random nbytes for Simple and Differential SCA protection. */
rc = crypto_stdrng_get_bytes(sca, nbytes);
if (rc)
return rc;
/* Wait engine to be idle before starting new operation. */
rc = ocs_ecc_wait_idle(ecc_dev);
if (rc)
return rc;
/* Send ecc_start pulse as well as indicating operation size. */
ocs_ecc_cmd_start(ecc_dev, op_size);
/* Write ax param; Base point (Gx). */
ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_AX,
point->x, nbytes);
/* Write ay param; Base point (Gy). */
ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_AY,
point->y, nbytes);
/*
* Write the private key into DATA_IN reg.
*
* Since DATA_IN register is used to write different values during the
* computation private Key value is overwritten with
* side-channel-resistance value.
*/
ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_BX_D,
scalar, nbytes);
/* Write operand by/l. */
ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_BY_L,
sca, nbytes);
memzero_explicit(sca, sizeof(sca));
/* Write p = curve prime(GF modulus). */
ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_P,
curve->p, nbytes);
/* Write a = curve coefficient. */
ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_A,
curve->a, nbytes);
/* Make hardware perform the multiplication. */
rc = ocs_ecc_trigger_op(ecc_dev, op_size, OCS_ECC_INST_CALC_D_IDX_A);
if (rc)
return rc;
/* Read result. */
ocs_ecc_read_cx_out(ecc_dev, result->x, nbytes);
ocs_ecc_read_cy_out(ecc_dev, result->y, nbytes);
return 0;
}
/**
* kmb_ecc_do_scalar_op() - Perform Scalar operation using OCS ECC HW.
* @ecc_dev: The OCS ECC device to use.
* @scalar_out: Where to store the output scalar.
* @scalar_a: Input scalar operand 'a'.
* @scalar_b: Input scalar operand 'b'
* @curve: The curve on which the operation is performed.
* @ndigits: The size of the operands (in digits).
* @inst: The operation to perform (as an OCS ECC instruction).
*
* Return: 0 on success, negative error code otherwise.
*/
static int kmb_ecc_do_scalar_op(struct ocs_ecc_dev *ecc_dev, u64 *scalar_out,
const u64 *scalar_a, const u64 *scalar_b,
const struct ecc_curve *curve,
unsigned int ndigits, const u32 inst)
{
u32 op_size = (ndigits > ECC_CURVE_NIST_P256_DIGITS) ?
OCS_ECC_OP_SIZE_384 : OCS_ECC_OP_SIZE_256;
size_t nbytes = digits_to_bytes(ndigits);
int rc;
/* Wait engine to be idle before starting new operation. */
rc = ocs_ecc_wait_idle(ecc_dev);
if (rc)
return rc;
/* Send ecc_start pulse as well as indicating operation size. */
ocs_ecc_cmd_start(ecc_dev, op_size);
/* Write ax param (Base point (Gx).*/
ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_AX,
scalar_a, nbytes);
/* Write ay param Base point (Gy).*/
ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_AY,
scalar_b, nbytes);
/* Write p = curve prime(GF modulus).*/
ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_P,
curve->p, nbytes);
/* Give instruction A.B or A+B to ECC engine. */
rc = ocs_ecc_trigger_op(ecc_dev, op_size, inst);
if (rc)
return rc;
ocs_ecc_read_cx_out(ecc_dev, scalar_out, nbytes);
if (vli_is_zero(scalar_out, ndigits))
return -EINVAL;
return 0;
}
/* SP800-56A section 5.6.2.3.4 partial verification: ephemeral keys only */
static int kmb_ocs_ecc_is_pubkey_valid_partial(struct ocs_ecc_dev *ecc_dev,
const struct ecc_curve *curve,
struct ecc_point *pk)
{
u64 xxx[KMB_ECC_VLI_MAX_DIGITS] = { 0 };
u64 yy[KMB_ECC_VLI_MAX_DIGITS] = { 0 };
u64 w[KMB_ECC_VLI_MAX_DIGITS] = { 0 };
int rc;
if (WARN_ON(pk->ndigits != curve->g.ndigits))
return -EINVAL;
/* Check 1: Verify key is not the zero point. */
if (ecc_point_is_zero(pk))
return -EINVAL;
/* Check 2: Verify key is in the range [0, p-1]. */
if (vli_cmp(curve->p, pk->x, pk->ndigits) != 1)
return -EINVAL;
if (vli_cmp(curve->p, pk->y, pk->ndigits) != 1)
return -EINVAL;
/* Check 3: Verify that y^2 == (x^3 + a·x + b) mod p */
/* y^2 */
/* Compute y^2 -> store in yy */
rc = kmb_ecc_do_scalar_op(ecc_dev, yy, pk->y, pk->y, curve, pk->ndigits,
OCS_ECC_INST_CALC_A_MUL_B_MODP);
if (rc)
goto exit;
/* x^3 */
/* Assigning w = 3, used for calculating x^3. */
w[0] = POW_CUBE;
/* Load the next stage.*/
rc = kmb_ecc_do_scalar_op(ecc_dev, xxx, pk->x, w, curve, pk->ndigits,
OCS_ECC_INST_CALC_A_POW_B_MODP);
if (rc)
goto exit;
/* Do a*x -> store in w. */
rc = kmb_ecc_do_scalar_op(ecc_dev, w, curve->a, pk->x, curve,
pk->ndigits,
OCS_ECC_INST_CALC_A_MUL_B_MODP);
if (rc)
goto exit;
/* Do ax + b == w + b; store in w. */
rc = kmb_ecc_do_scalar_op(ecc_dev, w, w, curve->b, curve,
pk->ndigits,
OCS_ECC_INST_CALC_A_ADD_B_MODP);
if (rc)
goto exit;
/* x^3 + ax + b == x^3 + w -> store in w. */
rc = kmb_ecc_do_scalar_op(ecc_dev, w, xxx, w, curve, pk->ndigits,
OCS_ECC_INST_CALC_A_ADD_B_MODP);
if (rc)
goto exit;
/* Compare y^2 == x^3 + a·x + b. */
rc = vli_cmp(yy, w, pk->ndigits);
if (rc)
rc = -EINVAL;
exit:
memzero_explicit(xxx, sizeof(xxx));
memzero_explicit(yy, sizeof(yy));
memzero_explicit(w, sizeof(w));
return rc;
}
/* SP800-56A section 5.6.2.3.3 full verification */
static int kmb_ocs_ecc_is_pubkey_valid_full(struct ocs_ecc_dev *ecc_dev,
const struct ecc_curve *curve,
struct ecc_point *pk)
{
struct ecc_point *nQ;
int rc;
/* Checks 1 through 3 */
rc = kmb_ocs_ecc_is_pubkey_valid_partial(ecc_dev, curve, pk);
if (rc)
return rc;
/* Check 4: Verify that nQ is the zero point. */
nQ = ecc_alloc_point(pk->ndigits);
if (!nQ)
return -ENOMEM;
rc = kmb_ecc_point_mult(ecc_dev, nQ, pk, curve->n, curve);
if (rc)
goto exit;
if (!ecc_point_is_zero(nQ))
rc = -EINVAL;
exit:
ecc_free_point(nQ);
return rc;
}
static int kmb_ecc_is_key_valid(const struct ecc_curve *curve,
const u64 *private_key, size_t private_key_len)
{
size_t ndigits = curve->g.ndigits;
u64 one[KMB_ECC_VLI_MAX_DIGITS] = {1};
u64 res[KMB_ECC_VLI_MAX_DIGITS];
if (private_key_len != digits_to_bytes(ndigits))
return -EINVAL;
if (!private_key)
return -EINVAL;
/* Make sure the private key is in the range [2, n-3]. */
if (vli_cmp(one, private_key, ndigits) != -1)
return -EINVAL;
vli_sub(res, curve->n, one, ndigits);
vli_sub(res, res, one, ndigits);
if (vli_cmp(res, private_key, ndigits) != 1)
return -EINVAL;
return 0;
}
/*
* ECC private keys are generated using the method of extra random bits,
* equivalent to that described in FIPS 186-4, Appendix B.4.1.
*
* d = (c mod(n–1)) + 1 where c is a string of random bits, 64 bits longer
* than requested
* 0 <= c mod(n-1) <= n-2 and implies that
* 1 <= d <= n-1
*
* This method generates a private key uniformly distributed in the range
* [1, n-1].
*/
static int kmb_ecc_gen_privkey(const struct ecc_curve *curve, u64 *privkey)
{
size_t nbytes = digits_to_bytes(curve->g.ndigits);
u64 priv[KMB_ECC_VLI_MAX_DIGITS];
size_t nbits;
int rc;
nbits = vli_num_bits(curve->n, curve->g.ndigits);
/* Check that N is included in Table 1 of FIPS 186-4, section 6.1.1 */
if (nbits < 160 || curve->g.ndigits > ARRAY_SIZE(priv))
return -EINVAL;
/*
* FIPS 186-4 recommends that the private key should be obtained from a
* RBG with a security strength equal to or greater than the security
* strength associated with N.
*
* The maximum security strength identified by NIST SP800-57pt1r4 for
* ECC is 256 (N >= 512).
*
* This condition is met by stdrng because it selects a favored DRBG
* with a security strength of 256.
*/
rc = crypto_stdrng_get_bytes(priv, nbytes);
if (rc)
goto cleanup;
rc = kmb_ecc_is_key_valid(curve, priv, nbytes);
if (rc)
goto cleanup;
ecc_swap_digits(priv, privkey, curve->g.ndigits);
cleanup:
memzero_explicit(&priv, sizeof(priv));
return rc;
}
static int kmb_ocs_ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,
unsigned int len)
{
struct ocs_ecc_ctx *tctx = kpp_tfm_ctx(tfm);
struct ecdh params;
int rc = 0;
rc = crypto_ecdh_decode_key(buf, len, &params);
if (rc)
goto cleanup;
/* Ensure key size is not bigger then expected. */
if (params.key_size > digits_to_bytes(tctx->curve->g.ndigits)) {
rc = -EINVAL;
goto cleanup;
}
/* Auto-generate private key is not provided. */
if (!params.key || !params.key_size) {
rc = kmb_ecc_gen_privkey(tctx->curve, tctx->private_key);
goto cleanup;
}
rc = kmb_ecc_is_key_valid(tctx->curve, (const u64 *)params.key,
params.key_size);
if (rc)
goto cleanup;
ecc_swap_digits((const u64 *)params.key, tctx->private_key,
tctx->curve->g.ndigits);
cleanup:
memzero_explicit(&params, sizeof(params));
if (rc)
tctx->curve = NULL;
return rc;
}
/* Compute shared secret. */
static int kmb_ecc_do_shared_secret(struct ocs_ecc_ctx *tctx,
struct kpp_request *req)
{
struct ocs_ecc_dev *ecc_dev = tctx->ecc_dev;
const struct ecc_curve *curve = tctx->curve;
u64 shared_secret[KMB_ECC_VLI_MAX_DIGITS];
u64 pubk_buf[KMB_ECC_VLI_MAX_DIGITS * 2];
size_t copied, nbytes, pubk_len;
struct ecc_point *pk, *result;
int rc;
nbytes = digits_to_bytes(curve->g.ndigits);
/* Public key is a point, thus it has two coordinates */
pubk_len = 2 * nbytes;
/* Copy public key from SG list to pubk_buf. */
copied = sg_copy_to_buffer(req->src,
sg_nents_for_len(req->src, pubk_len),
pubk_buf, pubk_len);
if (copied != pubk_len)
return -EINVAL;
/* Allocate and initialize public key point. */
pk = ecc_alloc_point(curve->g.ndigits);
if (!pk)
return -ENOMEM;
ecc_swap_digits(pubk_buf, pk->x, curve->g.ndigits);
ecc_swap_digits(&pubk_buf[curve->g.ndigits], pk->y, curve->g.ndigits);
/*
* Check the public key for following
* Check 1: Verify key is not the zero point.
* Check 2: Verify key is in the range [1, p-1].
* Check 3: Verify that y^2 == (x^3 + a·x + b) mod p
*/
rc = kmb_ocs_ecc_is_pubkey_valid_partial(ecc_dev, curve, pk);
if (rc)
goto exit_free_pk;
/* Allocate point for storing computed shared secret. */
result = ecc_alloc_point(pk->ndigits);
if (!result) {
rc = -ENOMEM;
goto exit_free_pk;
}
/* Calculate the shared secret.*/
rc = kmb_ecc_point_mult(ecc_dev, result, pk, tctx->private_key, curve);
if (rc)
goto exit_free_result;
if (ecc_point_is_zero(result)) {
rc = -EFAULT;
goto exit_free_result;
}
/* Copy shared secret from point to buffer. */
ecc_swap_digits(result->x, shared_secret, result->ndigits);
/* Request might ask for less bytes than what we have. */
nbytes = min_t(size_t, nbytes, req->dst_len);
copied = sg_copy_from_buffer(req->dst,
sg_nents_for_len(req->dst, nbytes),
shared_secret, nbytes);
if (copied != nbytes)
rc = -EINVAL;
memzero_explicit(shared_secret, sizeof(shared_secret));
exit_free_result:
ecc_free_point(result);
exit_free_pk:
ecc_free_point(pk);
return rc;
}
/* Compute public key. */
static int kmb_ecc_do_public_key(struct ocs_ecc_ctx *tctx,
struct kpp_request *req)
{
const struct ecc_curve *curve = tctx->curve;
u64 pubk_buf[KMB_ECC_VLI_MAX_DIGITS * 2];
struct ecc_point *pk;
size_t pubk_len;
size_t copied;
int rc;
/* Public key is a point, so it has double the digits. */
pubk_len = 2 * digits_to_bytes(curve->g.ndigits);
pk = ecc_alloc_point(curve->g.ndigits);
if (!pk)
return -ENOMEM;
/* Public Key(pk) = priv * G. */
rc = kmb_ecc_point_mult(tctx->ecc_dev, pk, &curve->g, tctx->private_key,
curve);
if (rc)
goto exit;
/* SP800-56A rev 3 5.6.2.1.3 key check */
if (kmb_ocs_ecc_is_pubkey_valid_full(tctx->ecc_dev, curve, pk)) {
rc = -EAGAIN;
goto exit;
}
/* Copy public key from point to buffer. */
ecc_swap_digits(pk->x, pubk_buf, pk->ndigits);
ecc_swap_digits(pk->y, &pubk_buf[pk->ndigits], pk->ndigits);
/* Copy public key to req->dst. */
copied = sg_copy_from_buffer(req->dst,
sg_nents_for_len(req->dst, pubk_len),
pubk_buf, pubk_len);
if (copied != pubk_len)
rc = -EINVAL;
exit:
ecc_free_point(pk);
return rc;
}
static int kmb_ocs_ecc_do_one_request(struct crypto_engine *engine,
void *areq)
{
struct kpp_request *req = container_of(areq, struct kpp_request, base);
struct ocs_ecc_ctx *tctx = kmb_ocs_ecc_tctx(req);
struct ocs_ecc_dev *ecc_dev = tctx->ecc_dev;
int rc;
if (req->src)
rc = kmb_ecc_do_shared_secret(tctx, req);
else
rc = kmb_ecc_do_public_key(tctx, req);
crypto_finalize_kpp_request(ecc_dev->engine, req, rc);
return 0;
}
static int kmb_ocs_ecdh_generate_public_key(struct kpp_request *req)
{
struct ocs_ecc_ctx *tctx = kmb_ocs_ecc_tctx(req);
const struct ecc_curve *curve = tctx->curve;
/* Ensure kmb_ocs_ecdh_set_secret() has been successfully called. */
if (!tctx->curve)
return -EINVAL;
/* Ensure dst is present. */
if (!req->dst)
return -EINVAL;
/* Check the request dst is big enough to hold the public key. */
if (req->dst_len < (2 * digits_to_bytes(curve->g.ndigits)))
return -EINVAL;
/* 'src' is not supposed to be present when generate pubk is called. */
if (req->src)
return -EINVAL;
return crypto_transfer_kpp_request_to_engine(tctx->ecc_dev->engine,
req);
}
static int kmb_ocs_ecdh_compute_shared_secret(struct kpp_request *req)
{
struct ocs_ecc_ctx *tctx = kmb_ocs_ecc_tctx(req);
const struct ecc_curve *curve = tctx->curve;
/* Ensure kmb_ocs_ecdh_set_secret() has been successfully called. */
if (!tctx->curve)
return -EINVAL;
/* Ensure dst is present. */
if (!req->dst)
return -EINVAL;
/* Ensure src is present. */
if (!req->src)
return -EINVAL;
/*
* req->src is expected to the (other-side) public key, so its length
* must be 2 * coordinate size (in bytes).
*/
if (req->src_len != 2 * digits_to_bytes(curve->g.ndigits))
return -EINVAL;
return crypto_transfer_kpp_request_to_engine(tctx->ecc_dev->engine,
req);
}
static int kmb_ecc_tctx_init(struct ocs_ecc_ctx *tctx, unsigned int curve_id)
{
memset(tctx, 0, sizeof(*tctx));
tctx->ecc_dev = kmb_ocs_ecc_find_dev(tctx);
if (IS_ERR(tctx->ecc_dev)) {
pr_err("Failed to find the device : %ld\n",
PTR_ERR(tctx->ecc_dev));
return PTR_ERR(tctx->ecc_dev);
}
tctx->curve = ecc_get_curve(curve_id);
if (!tctx->curve)
return -EOPNOTSUPP;
return 0;
}
static int kmb_ocs_ecdh_nist_p256_init_tfm(struct crypto_kpp *tfm)
{
struct ocs_ecc_ctx *tctx = kpp_tfm_ctx(tfm);
return kmb_ecc_tctx_init(tctx, ECC_CURVE_NIST_P256);
}
static int kmb_ocs_ecdh_nist_p384_init_tfm(struct crypto_kpp *tfm)
{
struct ocs_ecc_ctx *tctx = kpp_tfm_ctx(tfm);
return kmb_ecc_tctx_init(tctx, ECC_CURVE_NIST_P384);
}
static void kmb_ocs_ecdh_exit_tfm(struct crypto_kpp *tfm)
{
struct ocs_ecc_ctx *tctx = kpp_tfm_ctx(tfm);
memzero_explicit(tctx->private_key, sizeof(*tctx->private_key));
}
static unsigned int kmb_ocs_ecdh_max_size(struct crypto_kpp *tfm)
{
struct ocs_ecc_ctx *tctx = kpp_tfm_ctx(tfm);
/* Public key is made of two coordinates, so double the digits. */
return digits_to_bytes(tctx->curve->g.ndigits) * 2;
}
static struct kpp_engine_alg ocs_ecdh_p256 = {
.base.set_secret = kmb_ocs_ecdh_set_secret,
.base.generate_public_key = kmb_ocs_ecdh_generate_public_key,
.base.compute_shared_secret = kmb_ocs_ecdh_compute_shared_secret,
.base.init = kmb_ocs_ecdh_nist_p256_init_tfm,
.base.exit = kmb_ocs_ecdh_exit_tfm,
.base.max_size = kmb_ocs_ecdh_max_size,
.base.base = {
.cra_name = "ecdh-nist-p256",
.cra_driver_name = "ecdh-nist-p256-keembay-ocs",
.cra_priority = KMB_OCS_ECC_PRIORITY,
.cra_module = THIS_MODULE,
.cra_ctxsize = sizeof(struct ocs_ecc_ctx),
},
.op.do_one_request = kmb_ocs_ecc_do_one_request,
};
static struct kpp_engine_alg ocs_ecdh_p384 = {
.base.set_secret = kmb_ocs_ecdh_set_secret,
.base.generate_public_key = kmb_ocs_ecdh_generate_public_key,
.base.compute_shared_secret = kmb_ocs_ecdh_compute_shared_secret,
.base.init = kmb_ocs_ecdh_nist_p384_init_tfm,
.base.exit = kmb_ocs_ecdh_exit_tfm,
.base.max_size = kmb_ocs_ecdh_max_size,
.base.base = {
.cra_name = "ecdh-nist-p384",
.cra_driver_name = "ecdh-nist-p384-keembay-ocs",
.cra_priority = KMB_OCS_ECC_PRIORITY,
.cra_module = THIS_MODULE,
.cra_ctxsize = sizeof(struct ocs_ecc_ctx),
},
.op.do_one_request = kmb_ocs_ecc_do_one_request,
};
static irqreturn_t ocs_ecc_irq_handler(int irq, void *dev_id)
{
struct ocs_ecc_dev *ecc_dev = dev_id;
u32 status;
/*
* Read the status register and write it back to clear the
* DONE_INT_STATUS bit.
*/
status = ioread32(ecc_dev->base_reg + HW_OFFS_OCS_ECC_ISR);
iowrite32(status, ecc_dev->base_reg + HW_OFFS_OCS_ECC_ISR);
if (!(status & HW_OCS_ECC_ISR_INT_STATUS_DONE))
return IRQ_NONE;
complete(&ecc_dev->irq_done);
return IRQ_HANDLED;
}
static int kmb_ocs_ecc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ocs_ecc_dev *ecc_dev;
int rc;
ecc_dev = devm_kzalloc(dev, sizeof(*ecc_dev), GFP_KERNEL);
if (!ecc_dev)
return -ENOMEM;
ecc_dev->dev = dev;
platform_set_drvdata(pdev, ecc_dev);
INIT_LIST_HEAD(&ecc_dev->list);
init_completion(&ecc_dev->irq_done);
/* Get base register address. */
ecc_dev->base_reg = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(ecc_dev->base_reg)) {
dev_err(dev, "Failed to get base address\n");
rc = PTR_ERR(ecc_dev->base_reg);
goto list_del;
}
/* Get and request IRQ */
ecc_dev->irq = platform_get_irq(pdev, 0);
if (ecc_dev->irq < 0) {
rc = ecc_dev->irq;
goto list_del;
}
rc = devm_request_threaded_irq(dev, ecc_dev->irq, ocs_ecc_irq_handler,
NULL, 0, "keembay-ocs-ecc", ecc_dev);
if (rc < 0) {
dev_err(dev, "Could not request IRQ\n");
goto list_del;
}
/* Add device to the list of OCS ECC devices. */
spin_lock(&ocs_ecc.lock);
list_add_tail(&ecc_dev->list, &ocs_ecc.dev_list);
spin_unlock(&ocs_ecc.lock);
/* Initialize crypto engine. */
ecc_dev->engine = crypto_engine_alloc_init(dev, 1);
if (!ecc_dev->engine) {
dev_err(dev, "Could not allocate crypto engine\n");
rc = -ENOMEM;
goto list_del;
}
rc = crypto_engine_start(ecc_dev->engine);
if (rc) {
dev_err(dev, "Could not start crypto engine\n");
goto cleanup;
}
/* Register the KPP algo. */
rc = crypto_engine_register_kpp(&ocs_ecdh_p256);
if (rc) {
dev_err(dev,
"Could not register OCS algorithms with Crypto API\n");
goto cleanup;
}
rc = crypto_engine_register_kpp(&ocs_ecdh_p384);
if (rc) {
dev_err(dev,
"Could not register OCS algorithms with Crypto API\n");
goto ocs_ecdh_p384_error;
}
return 0;
ocs_ecdh_p384_error:
crypto_engine_unregister_kpp(&ocs_ecdh_p256);
cleanup:
crypto_engine_exit(ecc_dev->engine);
list_del:
spin_lock(&ocs_ecc.lock);
list_del(&ecc_dev->list);
spin_unlock(&ocs_ecc.lock);
return rc;
}
static void kmb_ocs_ecc_remove(struct platform_device *pdev)
{
struct ocs_ecc_dev *ecc_dev;
ecc_dev = platform_get_drvdata(pdev);
crypto_engine_unregister_kpp(&ocs_ecdh_p384);
crypto_engine_unregister_kpp(&ocs_ecdh_p256);
spin_lock(&ocs_ecc.lock);
list_del(&ecc_dev->list);
spin_unlock(&ocs_ecc.lock);
crypto_engine_exit(ecc_dev->engine);
}
/* Device tree driver match. */
static const struct of_device_id kmb_ocs_ecc_of_match[] = {
{
.compatible = "intel,keembay-ocs-ecc",
},
{}
};
/* The OCS driver is a platform device. */
static struct platform_driver kmb_ocs_ecc_driver = {
.probe = kmb_ocs_ecc_probe,
.remove = kmb_ocs_ecc_remove,
.driver = {
.name = DRV_NAME,
.of_match_table = kmb_ocs_ecc_of_match,
},
};
module_platform_driver(kmb_ocs_ecc_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Intel Keem Bay OCS ECC Driver");
MODULE_ALIAS_CRYPTO("ecdh-nist-p256");
MODULE_ALIAS_CRYPTO("ecdh-nist-p384");
MODULE_ALIAS_CRYPTO("ecdh-nist-p256-keembay-ocs");
MODULE_ALIAS_CRYPTO("ecdh-nist-p384-keembay-ocs");
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,129 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Intel Keem Bay OCS AES Crypto Driver.
*
* Copyright (C) 2018-2020 Intel Corporation
*/
#ifndef _CRYPTO_OCS_AES_H
#define _CRYPTO_OCS_AES_H
#include <linux/dma-mapping.h>
enum ocs_cipher {
OCS_AES = 0,
OCS_SM4 = 1,
};
enum ocs_mode {
OCS_MODE_ECB = 0,
OCS_MODE_CBC = 1,
OCS_MODE_CTR = 2,
OCS_MODE_CCM = 6,
OCS_MODE_GCM = 7,
OCS_MODE_CTS = 9,
};
enum ocs_instruction {
OCS_ENCRYPT = 0,
OCS_DECRYPT = 1,
OCS_EXPAND = 2,
OCS_BYPASS = 3,
};
/**
* struct ocs_aes_dev - AES device context.
* @list: List head for insertion into device list hold
* by driver.
* @dev: OCS AES device.
* @irq: IRQ number.
* @base_reg: IO base address of OCS AES.
* @irq_copy_completion: Completion to indicate IRQ has been triggered.
* @dma_err_mask: Error reported by OCS DMA interrupts.
* @engine: Crypto engine for the device.
*/
struct ocs_aes_dev {
struct list_head list;
struct device *dev;
int irq;
void __iomem *base_reg;
struct completion irq_completion;
u32 dma_err_mask;
struct crypto_engine *engine;
};
/**
* struct ocs_dll_desc - Descriptor of an OCS DMA Linked List.
* @vaddr: Virtual address of the linked list head.
* @dma_addr: DMA address of the linked list head.
* @size: Size (in bytes) of the linked list.
*/
struct ocs_dll_desc {
void *vaddr;
dma_addr_t dma_addr;
size_t size;
};
int ocs_aes_set_key(struct ocs_aes_dev *aes_dev, const u32 key_size,
const u8 *key, const enum ocs_cipher cipher);
int ocs_aes_op(struct ocs_aes_dev *aes_dev,
enum ocs_mode mode,
enum ocs_cipher cipher,
enum ocs_instruction instruction,
dma_addr_t dst_dma_list,
dma_addr_t src_dma_list,
u32 src_size,
u8 *iv,
u32 iv_size);
/**
* ocs_aes_bypass_op() - Use OCS DMA to copy data.
* @aes_dev: The OCS AES device to use.
* @dst_dma_list: The OCS DMA list mapping the memory where input data
* will be copied to.
* @src_dma_list: The OCS DMA list mapping input data.
* @src_size: The amount of data to copy.
*/
static inline int ocs_aes_bypass_op(struct ocs_aes_dev *aes_dev,
dma_addr_t dst_dma_list,
dma_addr_t src_dma_list, u32 src_size)
{
return ocs_aes_op(aes_dev, OCS_MODE_ECB, OCS_AES, OCS_BYPASS,
dst_dma_list, src_dma_list, src_size, NULL, 0);
}
int ocs_aes_gcm_op(struct ocs_aes_dev *aes_dev,
enum ocs_cipher cipher,
enum ocs_instruction instruction,
dma_addr_t dst_dma_list,
dma_addr_t src_dma_list,
u32 src_size,
const u8 *iv,
dma_addr_t aad_dma_list,
u32 aad_size,
u8 *out_tag,
u32 tag_size);
int ocs_aes_ccm_op(struct ocs_aes_dev *aes_dev,
enum ocs_cipher cipher,
enum ocs_instruction instruction,
dma_addr_t dst_dma_list,
dma_addr_t src_dma_list,
u32 src_size,
u8 *iv,
dma_addr_t adata_dma_list,
u32 adata_size,
u8 *in_tag,
u32 tag_size);
int ocs_create_linked_list_from_sg(const struct ocs_aes_dev *aes_dev,
struct scatterlist *sg,
int sg_dma_count,
struct ocs_dll_desc *dll_desc,
size_t data_size,
size_t data_offset);
irqreturn_t ocs_aes_irq_handler(int irq, void *dev_id);
#endif
@@ -0,0 +1,841 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel Keem Bay OCS HCU Crypto Driver.
*
* Copyright (C) 2018-2020 Intel Corporation
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/iopoll.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <crypto/sha2.h>
#include "ocs-hcu.h"
/* Registers. */
#define OCS_HCU_MODE 0x00
#define OCS_HCU_CHAIN 0x04
#define OCS_HCU_OPERATION 0x08
#define OCS_HCU_KEY_0 0x0C
#define OCS_HCU_ISR 0x50
#define OCS_HCU_IER 0x54
#define OCS_HCU_STATUS 0x58
#define OCS_HCU_MSG_LEN_LO 0x60
#define OCS_HCU_MSG_LEN_HI 0x64
#define OCS_HCU_KEY_BYTE_ORDER_CFG 0x80
#define OCS_HCU_DMA_SRC_ADDR 0x400
#define OCS_HCU_DMA_SRC_SIZE 0x408
#define OCS_HCU_DMA_DST_SIZE 0x40C
#define OCS_HCU_DMA_DMA_MODE 0x410
#define OCS_HCU_DMA_NEXT_SRC_DESCR 0x418
#define OCS_HCU_DMA_MSI_ISR 0x480
#define OCS_HCU_DMA_MSI_IER 0x484
#define OCS_HCU_DMA_MSI_MASK 0x488
/* Register bit definitions. */
#define HCU_MODE_ALGO_SHIFT 16
#define HCU_MODE_HMAC_SHIFT 22
#define HCU_STATUS_BUSY BIT(0)
#define HCU_BYTE_ORDER_SWAP BIT(0)
#define HCU_IRQ_HASH_DONE BIT(2)
#define HCU_IRQ_HASH_ERR_MASK (BIT(3) | BIT(1) | BIT(0))
#define HCU_DMA_IRQ_SRC_DONE BIT(0)
#define HCU_DMA_IRQ_SAI_ERR BIT(2)
#define HCU_DMA_IRQ_BAD_COMP_ERR BIT(3)
#define HCU_DMA_IRQ_INBUF_RD_ERR BIT(4)
#define HCU_DMA_IRQ_INBUF_WD_ERR BIT(5)
#define HCU_DMA_IRQ_OUTBUF_WR_ERR BIT(6)
#define HCU_DMA_IRQ_OUTBUF_RD_ERR BIT(7)
#define HCU_DMA_IRQ_CRD_ERR BIT(8)
#define HCU_DMA_IRQ_ERR_MASK (HCU_DMA_IRQ_SAI_ERR | \
HCU_DMA_IRQ_BAD_COMP_ERR | \
HCU_DMA_IRQ_INBUF_RD_ERR | \
HCU_DMA_IRQ_INBUF_WD_ERR | \
HCU_DMA_IRQ_OUTBUF_WR_ERR | \
HCU_DMA_IRQ_OUTBUF_RD_ERR | \
HCU_DMA_IRQ_CRD_ERR)
#define HCU_DMA_SNOOP_MASK (0x7 << 28)
#define HCU_DMA_SRC_LL_EN BIT(25)
#define HCU_DMA_EN BIT(31)
#define OCS_HCU_ENDIANNESS_VALUE 0x2A
#define HCU_DMA_MSI_UNMASK BIT(0)
#define HCU_DMA_MSI_DISABLE 0
#define HCU_IRQ_DISABLE 0
#define OCS_HCU_START BIT(0)
#define OCS_HCU_TERMINATE BIT(1)
#define OCS_LL_DMA_FLAG_TERMINATE BIT(31)
#define OCS_HCU_HW_KEY_LEN_U32 (OCS_HCU_HW_KEY_LEN / sizeof(u32))
#define HCU_DATA_WRITE_ENDIANNESS_OFFSET 26
#define OCS_HCU_NUM_CHAINS_SHA256_224_SM3 (SHA256_DIGEST_SIZE / sizeof(u32))
#define OCS_HCU_NUM_CHAINS_SHA384_512 (SHA512_DIGEST_SIZE / sizeof(u32))
/*
* While polling on a busy HCU, wait maximum 200us between one check and the
* other.
*/
#define OCS_HCU_WAIT_BUSY_RETRY_DELAY_US 200
/* Wait on a busy HCU for maximum 1 second. */
#define OCS_HCU_WAIT_BUSY_TIMEOUT_US 1000000
/**
* struct ocs_hcu_dma_entry - An entry in an OCS DMA linked list.
* @src_addr: Source address of the data.
* @src_len: Length of data to be fetched.
* @nxt_desc: Next descriptor to fetch.
* @ll_flags: Flags (Freeze @ terminate) for the DMA engine.
*/
struct ocs_hcu_dma_entry {
u32 src_addr;
u32 src_len;
u32 nxt_desc;
u32 ll_flags;
};
/**
* struct ocs_hcu_dma_list - OCS-specific DMA linked list.
* @head: The head of the list (points to the array backing the list).
* @tail: The current tail of the list; NULL if the list is empty.
* @dma_addr: The DMA address of @head (i.e., the DMA address of the backing
* array).
* @max_nents: Maximum number of entries in the list (i.e., number of elements
* in the backing array).
*
* The OCS DMA list is an array-backed list of OCS DMA descriptors. The array
* backing the list is allocated with dma_alloc_coherent() and pointed by
* @head.
*/
struct ocs_hcu_dma_list {
struct ocs_hcu_dma_entry *head;
struct ocs_hcu_dma_entry *tail;
dma_addr_t dma_addr;
size_t max_nents;
};
static inline u32 ocs_hcu_num_chains(enum ocs_hcu_algo algo)
{
switch (algo) {
case OCS_HCU_ALGO_SHA224:
case OCS_HCU_ALGO_SHA256:
case OCS_HCU_ALGO_SM3:
return OCS_HCU_NUM_CHAINS_SHA256_224_SM3;
case OCS_HCU_ALGO_SHA384:
case OCS_HCU_ALGO_SHA512:
return OCS_HCU_NUM_CHAINS_SHA384_512;
default:
return 0;
};
}
static inline u32 ocs_hcu_digest_size(enum ocs_hcu_algo algo)
{
switch (algo) {
case OCS_HCU_ALGO_SHA224:
return SHA224_DIGEST_SIZE;
case OCS_HCU_ALGO_SHA256:
case OCS_HCU_ALGO_SM3:
/* SM3 shares the same block size. */
return SHA256_DIGEST_SIZE;
case OCS_HCU_ALGO_SHA384:
return SHA384_DIGEST_SIZE;
case OCS_HCU_ALGO_SHA512:
return SHA512_DIGEST_SIZE;
default:
return 0;
}
}
/**
* ocs_hcu_wait_busy() - Wait for HCU OCS hardware to became usable.
* @hcu_dev: OCS HCU device to wait for.
*
* Return: 0 if device free, -ETIMEOUT if device busy and internal timeout has
* expired.
*/
static int ocs_hcu_wait_busy(struct ocs_hcu_dev *hcu_dev)
{
long val;
return readl_poll_timeout(hcu_dev->io_base + OCS_HCU_STATUS, val,
!(val & HCU_STATUS_BUSY),
OCS_HCU_WAIT_BUSY_RETRY_DELAY_US,
OCS_HCU_WAIT_BUSY_TIMEOUT_US);
}
static void ocs_hcu_done_irq_en(struct ocs_hcu_dev *hcu_dev)
{
/* Clear any pending interrupts. */
writel(0xFFFFFFFF, hcu_dev->io_base + OCS_HCU_ISR);
hcu_dev->irq_err = false;
/* Enable error and HCU done interrupts. */
writel(HCU_IRQ_HASH_DONE | HCU_IRQ_HASH_ERR_MASK,
hcu_dev->io_base + OCS_HCU_IER);
}
static void ocs_hcu_dma_irq_en(struct ocs_hcu_dev *hcu_dev)
{
/* Clear any pending interrupts. */
writel(0xFFFFFFFF, hcu_dev->io_base + OCS_HCU_DMA_MSI_ISR);
hcu_dev->irq_err = false;
/* Only operating on DMA source completion and error interrupts. */
writel(HCU_DMA_IRQ_ERR_MASK | HCU_DMA_IRQ_SRC_DONE,
hcu_dev->io_base + OCS_HCU_DMA_MSI_IER);
/* Unmask */
writel(HCU_DMA_MSI_UNMASK, hcu_dev->io_base + OCS_HCU_DMA_MSI_MASK);
}
static void ocs_hcu_irq_dis(struct ocs_hcu_dev *hcu_dev)
{
writel(HCU_IRQ_DISABLE, hcu_dev->io_base + OCS_HCU_IER);
writel(HCU_DMA_MSI_DISABLE, hcu_dev->io_base + OCS_HCU_DMA_MSI_IER);
}
static int ocs_hcu_wait_and_disable_irq(struct ocs_hcu_dev *hcu_dev)
{
int rc;
rc = wait_for_completion_interruptible(&hcu_dev->irq_done);
if (rc)
goto exit;
if (hcu_dev->irq_err) {
/* Unset flag and return error. */
hcu_dev->irq_err = false;
rc = -EIO;
goto exit;
}
exit:
ocs_hcu_irq_dis(hcu_dev);
return rc;
}
/**
* ocs_hcu_get_intermediate_data() - Get intermediate data.
* @hcu_dev: The target HCU device.
* @data: Where to store the intermediate.
* @algo: The algorithm being used.
*
* This function is used to save the current hashing process state in order to
* continue it in the future.
*
* Note: once all data has been processed, the intermediate data actually
* contains the hashing result. So this function is also used to retrieve the
* final result of a hashing process.
*
* Return: 0 on success, negative error code otherwise.
*/
static int ocs_hcu_get_intermediate_data(struct ocs_hcu_dev *hcu_dev,
struct ocs_hcu_idata *data,
enum ocs_hcu_algo algo)
{
const int n = ocs_hcu_num_chains(algo);
u32 *chain;
int rc;
int i;
/* Data not requested. */
if (!data)
return -EINVAL;
chain = (u32 *)data->digest;
/* Ensure that the OCS is no longer busy before reading the chains. */
rc = ocs_hcu_wait_busy(hcu_dev);
if (rc)
return rc;
/*
* This loops is safe because data->digest is an array of
* SHA512_DIGEST_SIZE bytes and the maximum value returned by
* ocs_hcu_num_chains() is OCS_HCU_NUM_CHAINS_SHA384_512 which is equal
* to SHA512_DIGEST_SIZE / sizeof(u32).
*/
for (i = 0; i < n; i++)
chain[i] = readl(hcu_dev->io_base + OCS_HCU_CHAIN);
data->msg_len_lo = readl(hcu_dev->io_base + OCS_HCU_MSG_LEN_LO);
data->msg_len_hi = readl(hcu_dev->io_base + OCS_HCU_MSG_LEN_HI);
return 0;
}
/**
* ocs_hcu_set_intermediate_data() - Set intermediate data.
* @hcu_dev: The target HCU device.
* @data: The intermediate data to be set.
* @algo: The algorithm being used.
*
* This function is used to continue a previous hashing process.
*/
static void ocs_hcu_set_intermediate_data(struct ocs_hcu_dev *hcu_dev,
const struct ocs_hcu_idata *data,
enum ocs_hcu_algo algo)
{
const int n = ocs_hcu_num_chains(algo);
u32 *chain = (u32 *)data->digest;
int i;
/*
* This loops is safe because data->digest is an array of
* SHA512_DIGEST_SIZE bytes and the maximum value returned by
* ocs_hcu_num_chains() is OCS_HCU_NUM_CHAINS_SHA384_512 which is equal
* to SHA512_DIGEST_SIZE / sizeof(u32).
*/
for (i = 0; i < n; i++)
writel(chain[i], hcu_dev->io_base + OCS_HCU_CHAIN);
writel(data->msg_len_lo, hcu_dev->io_base + OCS_HCU_MSG_LEN_LO);
writel(data->msg_len_hi, hcu_dev->io_base + OCS_HCU_MSG_LEN_HI);
}
static int ocs_hcu_get_digest(struct ocs_hcu_dev *hcu_dev,
enum ocs_hcu_algo algo, u8 *dgst, size_t dgst_len)
{
u32 *chain;
int rc;
int i;
if (!dgst)
return -EINVAL;
/* Length of the output buffer must match the algo digest size. */
if (dgst_len != ocs_hcu_digest_size(algo))
return -EINVAL;
/* Ensure that the OCS is no longer busy before reading the chains. */
rc = ocs_hcu_wait_busy(hcu_dev);
if (rc)
return rc;
chain = (u32 *)dgst;
for (i = 0; i < dgst_len / sizeof(u32); i++)
chain[i] = readl(hcu_dev->io_base + OCS_HCU_CHAIN);
return 0;
}
/**
* ocs_hcu_hw_cfg() - Configure the HCU hardware.
* @hcu_dev: The HCU device to configure.
* @algo: The algorithm to be used by the HCU device.
* @use_hmac: Whether or not HW HMAC should be used.
*
* Return: 0 on success, negative error code otherwise.
*/
static int ocs_hcu_hw_cfg(struct ocs_hcu_dev *hcu_dev, enum ocs_hcu_algo algo,
bool use_hmac)
{
u32 cfg;
int rc;
if (algo != OCS_HCU_ALGO_SHA256 && algo != OCS_HCU_ALGO_SHA224 &&
algo != OCS_HCU_ALGO_SHA384 && algo != OCS_HCU_ALGO_SHA512 &&
algo != OCS_HCU_ALGO_SM3)
return -EINVAL;
rc = ocs_hcu_wait_busy(hcu_dev);
if (rc)
return rc;
/* Ensure interrupts are disabled. */
ocs_hcu_irq_dis(hcu_dev);
/* Configure endianness, hashing algorithm and HW HMAC (if needed) */
cfg = OCS_HCU_ENDIANNESS_VALUE << HCU_DATA_WRITE_ENDIANNESS_OFFSET;
cfg |= algo << HCU_MODE_ALGO_SHIFT;
if (use_hmac)
cfg |= BIT(HCU_MODE_HMAC_SHIFT);
writel(cfg, hcu_dev->io_base + OCS_HCU_MODE);
return 0;
}
/**
* ocs_hcu_clear_key() - Clear key stored in OCS HMAC KEY registers.
* @hcu_dev: The OCS HCU device whose key registers should be cleared.
*/
static void ocs_hcu_clear_key(struct ocs_hcu_dev *hcu_dev)
{
int reg_off;
/* Clear OCS_HCU_KEY_[0..15] */
for (reg_off = 0; reg_off < OCS_HCU_HW_KEY_LEN; reg_off += sizeof(u32))
writel(0, hcu_dev->io_base + OCS_HCU_KEY_0 + reg_off);
}
/**
* ocs_hcu_write_key() - Write key to OCS HMAC KEY registers.
* @hcu_dev: The OCS HCU device the key should be written to.
* @key: The key to be written.
* @len: The size of the key to write. It must be OCS_HCU_HW_KEY_LEN.
*
* Return: 0 on success, negative error code otherwise.
*/
static int ocs_hcu_write_key(struct ocs_hcu_dev *hcu_dev, const u8 *key, size_t len)
{
u32 key_u32[OCS_HCU_HW_KEY_LEN_U32];
int i;
if (len > OCS_HCU_HW_KEY_LEN)
return -EINVAL;
/* Copy key into temporary u32 array. */
memcpy(key_u32, key, len);
/*
* Hardware requires all the bytes of the HW Key vector to be
* written. So pad with zero until we reach OCS_HCU_HW_KEY_LEN.
*/
memzero_explicit((u8 *)key_u32 + len, OCS_HCU_HW_KEY_LEN - len);
/*
* OCS hardware expects the MSB of the key to be written at the highest
* address of the HCU Key vector; in other word, the key must be
* written in reverse order.
*
* Therefore, we first enable byte swapping for the HCU key vector;
* so that bytes of 32-bit word written to OCS_HCU_KEY_[0..15] will be
* swapped:
* 3 <---> 0, 2 <---> 1.
*/
writel(HCU_BYTE_ORDER_SWAP,
hcu_dev->io_base + OCS_HCU_KEY_BYTE_ORDER_CFG);
/*
* And then we write the 32-bit words composing the key starting from
* the end of the key.
*/
for (i = 0; i < OCS_HCU_HW_KEY_LEN_U32; i++)
writel(key_u32[OCS_HCU_HW_KEY_LEN_U32 - 1 - i],
hcu_dev->io_base + OCS_HCU_KEY_0 + (sizeof(u32) * i));
memzero_explicit(key_u32, OCS_HCU_HW_KEY_LEN);
return 0;
}
/**
* ocs_hcu_ll_dma_start() - Start OCS HCU hashing via DMA
* @hcu_dev: The OCS HCU device to use.
* @dma_list: The OCS DMA list mapping the data to hash.
* @finalize: Whether or not this is the last hashing operation and therefore
* the final hash should be compute even if data is not
* block-aligned.
*
* Return: 0 on success, negative error code otherwise.
*/
static int ocs_hcu_ll_dma_start(struct ocs_hcu_dev *hcu_dev,
const struct ocs_hcu_dma_list *dma_list,
bool finalize)
{
u32 cfg = HCU_DMA_SNOOP_MASK | HCU_DMA_SRC_LL_EN | HCU_DMA_EN;
int rc;
if (!dma_list)
return -EINVAL;
/*
* For final requests we use HCU_DONE IRQ to be notified when all input
* data has been processed by the HCU; however, we cannot do so for
* non-final requests, because we don't get a HCU_DONE IRQ when we
* don't terminate the operation.
*
* Therefore, for non-final requests, we use the DMA IRQ, which
* triggers when DMA has finishing feeding all the input data to the
* HCU, but the HCU may still be processing it. This is fine, since we
* will wait for the HCU processing to be completed when we try to read
* intermediate results, in ocs_hcu_get_intermediate_data().
*/
if (finalize)
ocs_hcu_done_irq_en(hcu_dev);
else
ocs_hcu_dma_irq_en(hcu_dev);
reinit_completion(&hcu_dev->irq_done);
writel(dma_list->dma_addr, hcu_dev->io_base + OCS_HCU_DMA_NEXT_SRC_DESCR);
writel(0, hcu_dev->io_base + OCS_HCU_DMA_SRC_SIZE);
writel(0, hcu_dev->io_base + OCS_HCU_DMA_DST_SIZE);
writel(OCS_HCU_START, hcu_dev->io_base + OCS_HCU_OPERATION);
writel(cfg, hcu_dev->io_base + OCS_HCU_DMA_DMA_MODE);
if (finalize)
writel(OCS_HCU_TERMINATE, hcu_dev->io_base + OCS_HCU_OPERATION);
rc = ocs_hcu_wait_and_disable_irq(hcu_dev);
if (rc)
return rc;
return 0;
}
struct ocs_hcu_dma_list *ocs_hcu_dma_list_alloc(struct ocs_hcu_dev *hcu_dev,
int max_nents)
{
struct ocs_hcu_dma_list *dma_list;
dma_list = kmalloc_obj(*dma_list);
if (!dma_list)
return NULL;
/* Total size of the DMA list to allocate. */
dma_list->head = dma_alloc_coherent(hcu_dev->dev,
sizeof(*dma_list->head) * max_nents,
&dma_list->dma_addr, GFP_KERNEL);
if (!dma_list->head) {
kfree(dma_list);
return NULL;
}
dma_list->max_nents = max_nents;
dma_list->tail = NULL;
return dma_list;
}
void ocs_hcu_dma_list_free(struct ocs_hcu_dev *hcu_dev,
struct ocs_hcu_dma_list *dma_list)
{
if (!dma_list)
return;
dma_free_coherent(hcu_dev->dev,
sizeof(*dma_list->head) * dma_list->max_nents,
dma_list->head, dma_list->dma_addr);
kfree(dma_list);
}
/* Add a new DMA entry at the end of the OCS DMA list. */
int ocs_hcu_dma_list_add_tail(struct ocs_hcu_dev *hcu_dev,
struct ocs_hcu_dma_list *dma_list,
dma_addr_t addr, u32 len)
{
struct device *dev = hcu_dev->dev;
struct ocs_hcu_dma_entry *old_tail;
struct ocs_hcu_dma_entry *new_tail;
if (!len)
return 0;
if (!dma_list)
return -EINVAL;
if (addr & ~OCS_HCU_DMA_BIT_MASK) {
dev_err(dev,
"Unexpected error: Invalid DMA address for OCS HCU\n");
return -EINVAL;
}
old_tail = dma_list->tail;
new_tail = old_tail ? old_tail + 1 : dma_list->head;
/* Check if list is full. */
if (new_tail - dma_list->head >= dma_list->max_nents)
return -ENOMEM;
/*
* If there was an old tail (i.e., this is not the first element we are
* adding), un-terminate the old tail and make it point to the new one.
*/
if (old_tail) {
old_tail->ll_flags &= ~OCS_LL_DMA_FLAG_TERMINATE;
/*
* The old tail 'nxt_desc' must point to the DMA address of the
* new tail.
*/
old_tail->nxt_desc = dma_list->dma_addr +
sizeof(*dma_list->tail) * (new_tail -
dma_list->head);
}
new_tail->src_addr = (u32)addr;
new_tail->src_len = (u32)len;
new_tail->ll_flags = OCS_LL_DMA_FLAG_TERMINATE;
new_tail->nxt_desc = 0;
/* Update list tail with new tail. */
dma_list->tail = new_tail;
return 0;
}
/**
* ocs_hcu_hash_init() - Initialize hash operation context.
* @ctx: The context to initialize.
* @algo: The hashing algorithm to use.
*
* Return: 0 on success, negative error code otherwise.
*/
int ocs_hcu_hash_init(struct ocs_hcu_hash_ctx *ctx, enum ocs_hcu_algo algo)
{
if (!ctx)
return -EINVAL;
ctx->algo = algo;
ctx->idata.msg_len_lo = 0;
ctx->idata.msg_len_hi = 0;
/* No need to set idata.digest to 0. */
return 0;
}
/**
* ocs_hcu_hash_update() - Perform a hashing iteration.
* @hcu_dev: The OCS HCU device to use.
* @ctx: The OCS HCU hashing context.
* @dma_list: The OCS DMA list mapping the input data to process.
*
* Return: 0 on success; negative error code otherwise.
*/
int ocs_hcu_hash_update(struct ocs_hcu_dev *hcu_dev,
struct ocs_hcu_hash_ctx *ctx,
const struct ocs_hcu_dma_list *dma_list)
{
int rc;
if (!hcu_dev || !ctx)
return -EINVAL;
/* Configure the hardware for the current request. */
rc = ocs_hcu_hw_cfg(hcu_dev, ctx->algo, false);
if (rc)
return rc;
/* If we already processed some data, idata needs to be set. */
if (ctx->idata.msg_len_lo || ctx->idata.msg_len_hi)
ocs_hcu_set_intermediate_data(hcu_dev, &ctx->idata, ctx->algo);
/* Start linked-list DMA hashing. */
rc = ocs_hcu_ll_dma_start(hcu_dev, dma_list, false);
if (rc)
return rc;
/* Update idata and return. */
return ocs_hcu_get_intermediate_data(hcu_dev, &ctx->idata, ctx->algo);
}
/**
* ocs_hcu_hash_finup() - Update and finalize hash computation.
* @hcu_dev: The OCS HCU device to use.
* @ctx: The OCS HCU hashing context.
* @dma_list: The OCS DMA list mapping the input data to process.
* @dgst: The buffer where to save the computed digest.
* @dgst_len: The length of @dgst.
*
* Return: 0 on success; negative error code otherwise.
*/
int ocs_hcu_hash_finup(struct ocs_hcu_dev *hcu_dev,
const struct ocs_hcu_hash_ctx *ctx,
const struct ocs_hcu_dma_list *dma_list,
u8 *dgst, size_t dgst_len)
{
int rc;
if (!hcu_dev || !ctx)
return -EINVAL;
/* Configure the hardware for the current request. */
rc = ocs_hcu_hw_cfg(hcu_dev, ctx->algo, false);
if (rc)
return rc;
/* If we already processed some data, idata needs to be set. */
if (ctx->idata.msg_len_lo || ctx->idata.msg_len_hi)
ocs_hcu_set_intermediate_data(hcu_dev, &ctx->idata, ctx->algo);
/* Start linked-list DMA hashing. */
rc = ocs_hcu_ll_dma_start(hcu_dev, dma_list, true);
if (rc)
return rc;
/* Get digest and return. */
return ocs_hcu_get_digest(hcu_dev, ctx->algo, dgst, dgst_len);
}
/**
* ocs_hcu_hash_final() - Finalize hash computation.
* @hcu_dev: The OCS HCU device to use.
* @ctx: The OCS HCU hashing context.
* @dgst: The buffer where to save the computed digest.
* @dgst_len: The length of @dgst.
*
* Return: 0 on success; negative error code otherwise.
*/
int ocs_hcu_hash_final(struct ocs_hcu_dev *hcu_dev,
const struct ocs_hcu_hash_ctx *ctx, u8 *dgst,
size_t dgst_len)
{
int rc;
if (!hcu_dev || !ctx)
return -EINVAL;
/* Configure the hardware for the current request. */
rc = ocs_hcu_hw_cfg(hcu_dev, ctx->algo, false);
if (rc)
return rc;
/* If we already processed some data, idata needs to be set. */
if (ctx->idata.msg_len_lo || ctx->idata.msg_len_hi)
ocs_hcu_set_intermediate_data(hcu_dev, &ctx->idata, ctx->algo);
/*
* Enable HCU interrupts, so that HCU_DONE will be triggered once the
* final hash is computed.
*/
ocs_hcu_done_irq_en(hcu_dev);
reinit_completion(&hcu_dev->irq_done);
writel(OCS_HCU_TERMINATE, hcu_dev->io_base + OCS_HCU_OPERATION);
rc = ocs_hcu_wait_and_disable_irq(hcu_dev);
if (rc)
return rc;
/* Get digest and return. */
return ocs_hcu_get_digest(hcu_dev, ctx->algo, dgst, dgst_len);
}
/**
* ocs_hcu_digest() - Compute hash digest.
* @hcu_dev: The OCS HCU device to use.
* @algo: The hash algorithm to use.
* @data: The input data to process.
* @data_len: The length of @data.
* @dgst: The buffer where to save the computed digest.
* @dgst_len: The length of @dgst.
*
* Return: 0 on success; negative error code otherwise.
*/
int ocs_hcu_digest(struct ocs_hcu_dev *hcu_dev, enum ocs_hcu_algo algo,
void *data, size_t data_len, u8 *dgst, size_t dgst_len)
{
struct device *dev = hcu_dev->dev;
dma_addr_t dma_handle;
u32 reg;
int rc;
/* Configure the hardware for the current request. */
rc = ocs_hcu_hw_cfg(hcu_dev, algo, false);
if (rc)
return rc;
dma_handle = dma_map_single(dev, data, data_len, DMA_TO_DEVICE);
if (dma_mapping_error(dev, dma_handle))
return -EIO;
reg = HCU_DMA_SNOOP_MASK | HCU_DMA_EN;
ocs_hcu_done_irq_en(hcu_dev);
reinit_completion(&hcu_dev->irq_done);
writel(dma_handle, hcu_dev->io_base + OCS_HCU_DMA_SRC_ADDR);
writel(data_len, hcu_dev->io_base + OCS_HCU_DMA_SRC_SIZE);
writel(OCS_HCU_START, hcu_dev->io_base + OCS_HCU_OPERATION);
writel(reg, hcu_dev->io_base + OCS_HCU_DMA_DMA_MODE);
writel(OCS_HCU_TERMINATE, hcu_dev->io_base + OCS_HCU_OPERATION);
rc = ocs_hcu_wait_and_disable_irq(hcu_dev);
if (rc)
return rc;
dma_unmap_single(dev, dma_handle, data_len, DMA_TO_DEVICE);
return ocs_hcu_get_digest(hcu_dev, algo, dgst, dgst_len);
}
/**
* ocs_hcu_hmac() - Compute HMAC.
* @hcu_dev: The OCS HCU device to use.
* @algo: The hash algorithm to use with HMAC.
* @key: The key to use.
* @dma_list: The OCS DMA list mapping the input data to process.
* @key_len: The length of @key.
* @dgst: The buffer where to save the computed HMAC.
* @dgst_len: The length of @dgst.
*
* Return: 0 on success; negative error code otherwise.
*/
int ocs_hcu_hmac(struct ocs_hcu_dev *hcu_dev, enum ocs_hcu_algo algo,
const u8 *key, size_t key_len,
const struct ocs_hcu_dma_list *dma_list,
u8 *dgst, size_t dgst_len)
{
int rc;
/* Ensure 'key' is not NULL. */
if (!key || key_len == 0)
return -EINVAL;
/* Configure the hardware for the current request. */
rc = ocs_hcu_hw_cfg(hcu_dev, algo, true);
if (rc)
return rc;
rc = ocs_hcu_write_key(hcu_dev, key, key_len);
if (rc)
return rc;
rc = ocs_hcu_ll_dma_start(hcu_dev, dma_list, true);
/* Clear HW key before processing return code. */
ocs_hcu_clear_key(hcu_dev);
if (rc)
return rc;
return ocs_hcu_get_digest(hcu_dev, algo, dgst, dgst_len);
}
irqreturn_t ocs_hcu_irq_handler(int irq, void *dev_id)
{
struct ocs_hcu_dev *hcu_dev = dev_id;
u32 hcu_irq;
u32 dma_irq;
/* Read and clear the HCU interrupt. */
hcu_irq = readl(hcu_dev->io_base + OCS_HCU_ISR);
writel(hcu_irq, hcu_dev->io_base + OCS_HCU_ISR);
/* Read and clear the HCU DMA interrupt. */
dma_irq = readl(hcu_dev->io_base + OCS_HCU_DMA_MSI_ISR);
writel(dma_irq, hcu_dev->io_base + OCS_HCU_DMA_MSI_ISR);
/* Check for errors. */
if (hcu_irq & HCU_IRQ_HASH_ERR_MASK || dma_irq & HCU_DMA_IRQ_ERR_MASK) {
hcu_dev->irq_err = true;
goto complete;
}
/* Check for DONE IRQs. */
if (hcu_irq & HCU_IRQ_HASH_DONE || dma_irq & HCU_DMA_IRQ_SRC_DONE)
goto complete;
return IRQ_NONE;
complete:
complete(&hcu_dev->irq_done);
return IRQ_HANDLED;
}
MODULE_DESCRIPTION("Intel Keem Bay OCS HCU Crypto Driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,106 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Intel Keem Bay OCS HCU Crypto Driver.
*
* Copyright (C) 2018-2020 Intel Corporation
*/
#include <linux/dma-mapping.h>
#ifndef _CRYPTO_OCS_HCU_H
#define _CRYPTO_OCS_HCU_H
#define OCS_HCU_DMA_BIT_MASK DMA_BIT_MASK(32)
#define OCS_HCU_HW_KEY_LEN 64
struct ocs_hcu_dma_list;
enum ocs_hcu_algo {
OCS_HCU_ALGO_SHA256 = 2,
OCS_HCU_ALGO_SHA224 = 3,
OCS_HCU_ALGO_SHA384 = 4,
OCS_HCU_ALGO_SHA512 = 5,
OCS_HCU_ALGO_SM3 = 6,
};
/**
* struct ocs_hcu_dev - OCS HCU device context.
* @list: List of device contexts.
* @dev: OCS HCU device.
* @io_base: Base address of OCS HCU registers.
* @engine: Crypto engine for the device.
* @irq: IRQ number.
* @irq_done: Completion for IRQ.
* @irq_err: Flag indicating an IRQ error has happened.
*/
struct ocs_hcu_dev {
struct list_head list;
struct device *dev;
void __iomem *io_base;
struct crypto_engine *engine;
int irq;
struct completion irq_done;
bool irq_err;
};
/**
* struct ocs_hcu_idata - Intermediate data generated by the HCU.
* @msg_len_lo: Length of data the HCU has operated on in bits, low 32b.
* @msg_len_hi: Length of data the HCU has operated on in bits, high 32b.
* @digest: The digest read from the HCU. If the HCU is terminated, it will
* contain the actual hash digest. Otherwise it is the intermediate
* state.
*/
struct ocs_hcu_idata {
u32 msg_len_lo;
u32 msg_len_hi;
u8 digest[SHA512_DIGEST_SIZE];
};
/**
* struct ocs_hcu_hash_ctx - Context for OCS HCU hashing operation.
* @algo: The hashing algorithm being used.
* @idata: The current intermediate data.
*/
struct ocs_hcu_hash_ctx {
enum ocs_hcu_algo algo;
struct ocs_hcu_idata idata;
};
irqreturn_t ocs_hcu_irq_handler(int irq, void *dev_id);
struct ocs_hcu_dma_list *ocs_hcu_dma_list_alloc(struct ocs_hcu_dev *hcu_dev,
int max_nents);
void ocs_hcu_dma_list_free(struct ocs_hcu_dev *hcu_dev,
struct ocs_hcu_dma_list *dma_list);
int ocs_hcu_dma_list_add_tail(struct ocs_hcu_dev *hcu_dev,
struct ocs_hcu_dma_list *dma_list,
dma_addr_t addr, u32 len);
int ocs_hcu_hash_init(struct ocs_hcu_hash_ctx *ctx, enum ocs_hcu_algo algo);
int ocs_hcu_hash_update(struct ocs_hcu_dev *hcu_dev,
struct ocs_hcu_hash_ctx *ctx,
const struct ocs_hcu_dma_list *dma_list);
int ocs_hcu_hash_finup(struct ocs_hcu_dev *hcu_dev,
const struct ocs_hcu_hash_ctx *ctx,
const struct ocs_hcu_dma_list *dma_list,
u8 *dgst, size_t dgst_len);
int ocs_hcu_hash_final(struct ocs_hcu_dev *hcu_dev,
const struct ocs_hcu_hash_ctx *ctx, u8 *dgst,
size_t dgst_len);
int ocs_hcu_digest(struct ocs_hcu_dev *hcu_dev, enum ocs_hcu_algo algo,
void *data, size_t data_len, u8 *dgst, size_t dgst_len);
int ocs_hcu_hmac(struct ocs_hcu_dev *hcu_dev, enum ocs_hcu_algo algo,
const u8 *key, size_t key_len,
const struct ocs_hcu_dma_list *dma_list,
u8 *dgst, size_t dgst_len);
#endif /* _CRYPTO_OCS_HCU_H */
@@ -0,0 +1,135 @@
# SPDX-License-Identifier: GPL-2.0-only
config CRYPTO_DEV_QAT
tristate
select CRYPTO_ACOMP
select CRYPTO_AEAD
select CRYPTO_AUTHENC
select CRYPTO_SKCIPHER
select CRYPTO_AKCIPHER
select CRYPTO_DH
select CRYPTO_RSA
select CRYPTO_LIB_AES
select CRYPTO_LIB_SHA1
select CRYPTO_LIB_SHA256
select CRYPTO_LIB_SHA512
select CRYPTO_ZSTD
select FW_LOADER
select CRC8
config CRYPTO_DEV_QAT_DH895xCC
tristate "Support for Intel(R) DH895xCC"
depends on PCI && (!CPU_BIG_ENDIAN || COMPILE_TEST)
select CRYPTO_DEV_QAT
help
Support for Intel(R) DH895xcc with Intel(R) QuickAssist Technology
for accelerating crypto and compression workloads.
To compile this as a module, choose M here: the module
will be called qat_dh895xcc.
config CRYPTO_DEV_QAT_C3XXX
tristate "Support for Intel(R) C3XXX"
depends on PCI && (!CPU_BIG_ENDIAN || COMPILE_TEST)
select CRYPTO_DEV_QAT
help
Support for Intel(R) C3xxx with Intel(R) QuickAssist Technology
for accelerating crypto and compression workloads.
To compile this as a module, choose M here: the module
will be called qat_c3xxx.
config CRYPTO_DEV_QAT_C62X
tristate "Support for Intel(R) C62X"
depends on PCI && (!CPU_BIG_ENDIAN || COMPILE_TEST)
select CRYPTO_DEV_QAT
help
Support for Intel(R) C62x with Intel(R) QuickAssist Technology
for accelerating crypto and compression workloads.
To compile this as a module, choose M here: the module
will be called qat_c62x.
config CRYPTO_DEV_QAT_4XXX
tristate "Support for Intel(R) QAT_4XXX"
depends on PCI && (!CPU_BIG_ENDIAN || COMPILE_TEST)
select CRYPTO_DEV_QAT
help
Support for Intel(R) QuickAssist Technology QAT_4xxx
for accelerating crypto and compression workloads.
To compile this as a module, choose M here: the module
will be called qat_4xxx.
config CRYPTO_DEV_QAT_420XX
tristate "Support for Intel(R) QAT_420XX"
depends on PCI && (!CPU_BIG_ENDIAN || COMPILE_TEST)
select CRYPTO_DEV_QAT
help
Support for Intel(R) QuickAssist Technology QAT_420xx
for accelerating crypto and compression workloads.
To compile this as a module, choose M here: the module
will be called qat_420xx.
config CRYPTO_DEV_QAT_6XXX
tristate "Support for Intel(R) QuickAssist Technology QAT_6XXX"
depends on (X86 || COMPILE_TEST)
depends on PCI
select CRYPTO_DEV_QAT
help
Support for Intel(R) QuickAssist Technology QAT_6xxx
for accelerating crypto and compression workloads.
To compile this as a module, choose M here: the module
will be called qat_6xxx.
config CRYPTO_DEV_QAT_DH895xCCVF
tristate "Support for Intel(R) DH895xCC Virtual Function"
depends on PCI && (!CPU_BIG_ENDIAN || COMPILE_TEST)
select PCI_IOV
select CRYPTO_DEV_QAT
help
Support for Intel(R) DH895xcc with Intel(R) QuickAssist Technology
Virtual Function for accelerating crypto and compression workloads.
To compile this as a module, choose M here: the module
will be called qat_dh895xccvf.
config CRYPTO_DEV_QAT_C3XXXVF
tristate "Support for Intel(R) C3XXX Virtual Function"
depends on PCI && (!CPU_BIG_ENDIAN || COMPILE_TEST)
select PCI_IOV
select CRYPTO_DEV_QAT
help
Support for Intel(R) C3xxx with Intel(R) QuickAssist Technology
Virtual Function for accelerating crypto and compression workloads.
To compile this as a module, choose M here: the module
will be called qat_c3xxxvf.
config CRYPTO_DEV_QAT_C62XVF
tristate "Support for Intel(R) C62X Virtual Function"
depends on PCI && (!CPU_BIG_ENDIAN || COMPILE_TEST)
select PCI_IOV
select CRYPTO_DEV_QAT
help
Support for Intel(R) C62x with Intel(R) QuickAssist Technology
Virtual Function for accelerating crypto and compression workloads.
To compile this as a module, choose M here: the module
will be called qat_c62xvf.
config CRYPTO_DEV_QAT_ERROR_INJECTION
bool "Support for Intel(R) QAT Devices Heartbeat Error Injection"
depends on CRYPTO_DEV_QAT
depends on DEBUG_FS
help
Enables a mechanism that allows to inject a heartbeat error on
Intel(R) QuickAssist devices for testing purposes.
This is intended for developer use only.
If unsure, say N.
This functionality is available via debugfs entry of the Intel(R)
QuickAssist device
@@ -0,0 +1,12 @@
# SPDX-License-Identifier: GPL-2.0
subdir-ccflags-y := -I$(src)/qat_common
obj-$(CONFIG_CRYPTO_DEV_QAT) += qat_common/
obj-$(CONFIG_CRYPTO_DEV_QAT_DH895xCC) += qat_dh895xcc/
obj-$(CONFIG_CRYPTO_DEV_QAT_C3XXX) += qat_c3xxx/
obj-$(CONFIG_CRYPTO_DEV_QAT_C62X) += qat_c62x/
obj-$(CONFIG_CRYPTO_DEV_QAT_4XXX) += qat_4xxx/
obj-$(CONFIG_CRYPTO_DEV_QAT_420XX) += qat_420xx/
obj-$(CONFIG_CRYPTO_DEV_QAT_6XXX) += qat_6xxx/
obj-$(CONFIG_CRYPTO_DEV_QAT_DH895xCCVF) += qat_dh895xccvf/
obj-$(CONFIG_CRYPTO_DEV_QAT_C3XXXVF) += qat_c3xxxvf/
obj-$(CONFIG_CRYPTO_DEV_QAT_C62XVF) += qat_c62xvf/
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_CRYPTO_DEV_QAT_420XX) += qat_420xx.o
qat_420xx-y := adf_drv.o adf_420xx_hw_data.o
@@ -0,0 +1,506 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2023 Intel Corporation */
#include <linux/iopoll.h>
#include <adf_accel_devices.h>
#include <adf_admin.h>
#include <adf_bank_state.h>
#include <adf_cfg.h>
#include <adf_cfg_services.h>
#include <adf_clock.h>
#include <adf_common_drv.h>
#include <adf_fw_config.h>
#include <adf_gen4_config.h>
#include <adf_gen4_hw_csr_data.h>
#include <adf_gen4_hw_data.h>
#include <adf_gen4_pfvf.h>
#include <adf_gen4_pm.h>
#include <adf_gen4_ras.h>
#include <adf_gen4_tl.h>
#include <adf_gen4_vf_mig.h>
#include <adf_timer.h>
#include "adf_420xx_hw_data.h"
#include "icp_qat_hw.h"
#define ADF_AE_GROUP_0 GENMASK(3, 0)
#define ADF_AE_GROUP_1 GENMASK(7, 4)
#define ADF_AE_GROUP_2 GENMASK(11, 8)
#define ADF_AE_GROUP_3 GENMASK(15, 12)
#define ADF_AE_GROUP_4 BIT(16)
#define ENA_THD_MASK_ASYM GENMASK(1, 0)
#define ENA_THD_MASK_SYM GENMASK(3, 0)
#define ENA_THD_MASK_DC GENMASK(1, 0)
static const char * const adf_420xx_fw_objs[] = {
[ADF_FW_SYM_OBJ] = ADF_420XX_SYM_OBJ,
[ADF_FW_ASYM_OBJ] = ADF_420XX_ASYM_OBJ,
[ADF_FW_DC_OBJ] = ADF_420XX_DC_OBJ,
[ADF_FW_ADMIN_OBJ] = ADF_420XX_ADMIN_OBJ,
};
static const struct adf_fw_config adf_fw_cy_config[] = {
{ADF_AE_GROUP_3, ADF_FW_SYM_OBJ},
{ADF_AE_GROUP_2, ADF_FW_ASYM_OBJ},
{ADF_AE_GROUP_1, ADF_FW_SYM_OBJ},
{ADF_AE_GROUP_0, ADF_FW_ASYM_OBJ},
{ADF_AE_GROUP_4, ADF_FW_ADMIN_OBJ},
};
static const struct adf_fw_config adf_fw_dc_config[] = {
{ADF_AE_GROUP_1, ADF_FW_DC_OBJ},
{ADF_AE_GROUP_0, ADF_FW_DC_OBJ},
{ADF_AE_GROUP_4, ADF_FW_ADMIN_OBJ},
};
static const struct adf_fw_config adf_fw_sym_config[] = {
{ADF_AE_GROUP_3, ADF_FW_SYM_OBJ},
{ADF_AE_GROUP_2, ADF_FW_SYM_OBJ},
{ADF_AE_GROUP_1, ADF_FW_SYM_OBJ},
{ADF_AE_GROUP_0, ADF_FW_SYM_OBJ},
{ADF_AE_GROUP_4, ADF_FW_ADMIN_OBJ},
};
static const struct adf_fw_config adf_fw_asym_config[] = {
{ADF_AE_GROUP_3, ADF_FW_ASYM_OBJ},
{ADF_AE_GROUP_2, ADF_FW_ASYM_OBJ},
{ADF_AE_GROUP_1, ADF_FW_ASYM_OBJ},
{ADF_AE_GROUP_0, ADF_FW_ASYM_OBJ},
{ADF_AE_GROUP_4, ADF_FW_ADMIN_OBJ},
};
static const struct adf_fw_config adf_fw_asym_dc_config[] = {
{ADF_AE_GROUP_3, ADF_FW_ASYM_OBJ},
{ADF_AE_GROUP_2, ADF_FW_ASYM_OBJ},
{ADF_AE_GROUP_1, ADF_FW_ASYM_OBJ},
{ADF_AE_GROUP_0, ADF_FW_DC_OBJ},
{ADF_AE_GROUP_4, ADF_FW_ADMIN_OBJ},
};
static const struct adf_fw_config adf_fw_sym_dc_config[] = {
{ADF_AE_GROUP_2, ADF_FW_SYM_OBJ},
{ADF_AE_GROUP_1, ADF_FW_SYM_OBJ},
{ADF_AE_GROUP_0, ADF_FW_DC_OBJ},
{ADF_AE_GROUP_4, ADF_FW_ADMIN_OBJ},
};
static const struct adf_fw_config adf_fw_dcc_config[] = {
{ADF_AE_GROUP_1, ADF_FW_DC_OBJ},
{ADF_AE_GROUP_0, ADF_FW_SYM_OBJ},
{ADF_AE_GROUP_4, ADF_FW_ADMIN_OBJ},
};
static struct adf_hw_device_class adf_420xx_class = {
.name = ADF_420XX_DEVICE_NAME,
.type = DEV_420XX,
};
static u32 get_ae_mask(struct adf_hw_device_data *self)
{
unsigned long fuses = self->fuses[ADF_FUSECTL4];
u32 mask = ADF_420XX_ACCELENGINES_MASK;
if (test_bit(0, &fuses))
mask &= ~ADF_AE_GROUP_0;
if (test_bit(4, &fuses))
mask &= ~ADF_AE_GROUP_1;
if (test_bit(8, &fuses))
mask &= ~ADF_AE_GROUP_2;
if (test_bit(12, &fuses))
mask &= ~ADF_AE_GROUP_3;
if (test_bit(16, &fuses))
mask &= ~ADF_AE_GROUP_4;
return mask;
}
static u32 uof_get_num_objs(struct adf_accel_dev *accel_dev)
{
switch (adf_get_service_enabled(accel_dev)) {
case SVC_SYM_ASYM:
return ARRAY_SIZE(adf_fw_cy_config);
case SVC_DC:
return ARRAY_SIZE(adf_fw_dc_config);
case SVC_DCC:
return ARRAY_SIZE(adf_fw_dcc_config);
case SVC_SYM:
return ARRAY_SIZE(adf_fw_sym_config);
case SVC_ASYM:
return ARRAY_SIZE(adf_fw_asym_config);
case SVC_ASYM_DC:
return ARRAY_SIZE(adf_fw_asym_dc_config);
case SVC_SYM_DC:
return ARRAY_SIZE(adf_fw_sym_dc_config);
default:
return 0;
}
}
static const struct adf_fw_config *get_fw_config(struct adf_accel_dev *accel_dev)
{
switch (adf_get_service_enabled(accel_dev)) {
case SVC_SYM_ASYM:
return adf_fw_cy_config;
case SVC_DC:
return adf_fw_dc_config;
case SVC_DCC:
return adf_fw_dcc_config;
case SVC_SYM:
return adf_fw_sym_config;
case SVC_ASYM:
return adf_fw_asym_config;
case SVC_ASYM_DC:
return adf_fw_asym_dc_config;
case SVC_SYM_DC:
return adf_fw_sym_dc_config;
default:
return NULL;
}
}
static void update_ae_mask(struct adf_accel_dev *accel_dev)
{
struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
const struct adf_fw_config *fw_config;
u32 config_ae_mask = 0;
u32 ae_mask, num_objs;
int i;
ae_mask = get_ae_mask(hw_data);
/* Modify the AE mask based on the firmware configuration loaded */
fw_config = get_fw_config(accel_dev);
num_objs = uof_get_num_objs(accel_dev);
config_ae_mask |= ADF_420XX_ADMIN_AE_MASK;
for (i = 0; i < num_objs; i++)
config_ae_mask |= fw_config[i].ae_mask;
hw_data->ae_mask = ae_mask & config_ae_mask;
}
static u32 get_accel_cap(struct adf_accel_dev *accel_dev)
{
u32 capabilities_sym, capabilities_asym, capabilities_dc;
struct pci_dev *pdev = accel_dev->accel_pci_dev.pci_dev;
u32 capabilities_dcc;
u32 fusectl1;
/* As a side effect, update ae_mask based on configuration */
update_ae_mask(accel_dev);
/* Read accelerator capabilities mask */
pci_read_config_dword(pdev, ADF_GEN4_FUSECTL1_OFFSET, &fusectl1);
capabilities_sym = ICP_ACCEL_CAPABILITIES_CRYPTO_SYMMETRIC |
ICP_ACCEL_CAPABILITIES_CIPHER |
ICP_ACCEL_CAPABILITIES_AUTHENTICATION |
ICP_ACCEL_CAPABILITIES_SHA3 |
ICP_ACCEL_CAPABILITIES_SHA3_EXT |
ICP_ACCEL_CAPABILITIES_HKDF |
ICP_ACCEL_CAPABILITIES_CHACHA_POLY |
ICP_ACCEL_CAPABILITIES_AESGCM_SPC |
ICP_ACCEL_CAPABILITIES_SM3 |
ICP_ACCEL_CAPABILITIES_SM4 |
ICP_ACCEL_CAPABILITIES_AES_V2 |
ICP_ACCEL_CAPABILITIES_ZUC |
ICP_ACCEL_CAPABILITIES_WIRELESS_CRYPTO_EXT |
ICP_ACCEL_CAPABILITIES_EXT_ALGCHAIN;
/* A set bit in fusectl1 means the feature is OFF in this SKU */
if (fusectl1 & ICP_ACCEL_GEN4_MASK_CIPHER_SLICE) {
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_CRYPTO_SYMMETRIC;
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_HKDF;
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_CIPHER;
}
if (fusectl1 & ICP_ACCEL_GEN4_MASK_UCS_SLICE) {
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_CHACHA_POLY;
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_AESGCM_SPC;
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_AES_V2;
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_CIPHER;
}
if (fusectl1 & ICP_ACCEL_GEN4_MASK_AUTH_SLICE) {
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_AUTHENTICATION;
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_SHA3;
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_SHA3_EXT;
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_CIPHER;
}
if (fusectl1 & ICP_ACCEL_GEN4_MASK_SMX_SLICE) {
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_SM3;
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_SM4;
}
if (fusectl1 & ICP_ACCEL_GEN4_MASK_WCP_WAT_SLICE) {
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_ZUC;
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_WIRELESS_CRYPTO_EXT;
}
if (fusectl1 & ICP_ACCEL_GEN4_MASK_EIA3_SLICE)
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_ZUC;
capabilities_asym = ICP_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC |
ICP_ACCEL_CAPABILITIES_SM2 |
ICP_ACCEL_CAPABILITIES_ECEDMONT;
if (fusectl1 & ICP_ACCEL_GEN4_MASK_PKE_SLICE) {
capabilities_asym &= ~ICP_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC;
capabilities_asym &= ~ICP_ACCEL_CAPABILITIES_SM2;
capabilities_asym &= ~ICP_ACCEL_CAPABILITIES_ECEDMONT;
}
capabilities_dc = ICP_ACCEL_CAPABILITIES_COMPRESSION |
ICP_ACCEL_CAPABILITIES_LZ4_COMPRESSION |
ICP_ACCEL_CAPABILITIES_LZ4S_COMPRESSION |
ICP_ACCEL_CAPABILITIES_CNV_INTEGRITY64;
if (fusectl1 & ICP_ACCEL_GEN4_MASK_COMPRESS_SLICE) {
capabilities_dc &= ~ICP_ACCEL_CAPABILITIES_COMPRESSION;
capabilities_dc &= ~ICP_ACCEL_CAPABILITIES_LZ4_COMPRESSION;
capabilities_dc &= ~ICP_ACCEL_CAPABILITIES_LZ4S_COMPRESSION;
capabilities_dc &= ~ICP_ACCEL_CAPABILITIES_CNV_INTEGRITY64;
}
switch (adf_get_service_enabled(accel_dev)) {
case SVC_SYM_ASYM:
return capabilities_sym | capabilities_asym;
case SVC_DC:
return capabilities_dc;
case SVC_DCC:
/*
* Sym capabilities are available for chaining operations,
* but sym crypto instances cannot be supported
*/
capabilities_dcc = capabilities_dc | capabilities_sym;
capabilities_dcc &= ~ICP_ACCEL_CAPABILITIES_CRYPTO_SYMMETRIC;
return capabilities_dcc;
case SVC_SYM:
return capabilities_sym;
case SVC_ASYM:
return capabilities_asym;
case SVC_ASYM_DC:
return capabilities_asym | capabilities_dc;
case SVC_SYM_DC:
return capabilities_sym | capabilities_dc;
default:
return 0;
}
}
static const u32 *adf_get_arbiter_mapping(struct adf_accel_dev *accel_dev)
{
if (adf_gen4_init_thd2arb_map(accel_dev))
dev_warn(&GET_DEV(accel_dev),
"Failed to generate thread to arbiter mapping");
return GET_HW_DATA(accel_dev)->thd_to_arb_map;
}
static void adf_init_rl_data(struct adf_rl_hw_data *rl_data)
{
rl_data->pciout_tb_offset = ADF_GEN4_RL_TOKEN_PCIEOUT_BUCKET_OFFSET;
rl_data->pciin_tb_offset = ADF_GEN4_RL_TOKEN_PCIEIN_BUCKET_OFFSET;
rl_data->r2l_offset = ADF_GEN4_RL_R2L_OFFSET;
rl_data->l2c_offset = ADF_GEN4_RL_L2C_OFFSET;
rl_data->c2s_offset = ADF_GEN4_RL_C2S_OFFSET;
rl_data->pcie_scale_div = ADF_420XX_RL_PCIE_SCALE_FACTOR_DIV;
rl_data->pcie_scale_mul = ADF_420XX_RL_PCIE_SCALE_FACTOR_MUL;
rl_data->dcpr_correction = ADF_420XX_RL_DCPR_CORRECTION;
rl_data->max_tp[SVC_ASYM] = ADF_420XX_RL_MAX_TP_ASYM;
rl_data->max_tp[SVC_SYM] = ADF_420XX_RL_MAX_TP_SYM;
rl_data->max_tp[SVC_DC] = ADF_420XX_RL_MAX_TP_DC;
rl_data->scan_interval = ADF_420XX_RL_SCANS_PER_SEC;
rl_data->scale_ref = ADF_420XX_RL_SLICE_REF;
adf_gen4_init_num_svc_aes(rl_data);
}
static int get_rp_group(struct adf_accel_dev *accel_dev, u32 ae_mask)
{
switch (ae_mask) {
case ADF_AE_GROUP_0:
return RP_GROUP_0;
case ADF_AE_GROUP_1:
case ADF_AE_GROUP_3:
return RP_GROUP_1;
case ADF_AE_GROUP_2:
if (get_fw_config(accel_dev) == adf_fw_cy_config)
return RP_GROUP_0;
else
return RP_GROUP_1;
default:
dev_dbg(&GET_DEV(accel_dev), "ae_mask not recognized");
return -EINVAL;
}
}
static u32 get_ena_thd_mask(struct adf_accel_dev *accel_dev, u32 obj_num)
{
const struct adf_fw_config *fw_config;
if (obj_num >= uof_get_num_objs(accel_dev))
return ADF_GEN4_ENA_THD_MASK_ERROR;
fw_config = get_fw_config(accel_dev);
if (!fw_config)
return ADF_GEN4_ENA_THD_MASK_ERROR;
switch (fw_config[obj_num].obj) {
case ADF_FW_ASYM_OBJ:
return ENA_THD_MASK_ASYM;
case ADF_FW_SYM_OBJ:
return ENA_THD_MASK_SYM;
case ADF_FW_DC_OBJ:
return ENA_THD_MASK_DC;
default:
return ADF_GEN4_ENA_THD_MASK_ERROR;
}
}
static const char *uof_get_name(struct adf_accel_dev *accel_dev, u32 obj_num,
const char * const fw_objs[], int num_objs)
{
const struct adf_fw_config *fw_config;
int id;
fw_config = get_fw_config(accel_dev);
if (fw_config)
id = fw_config[obj_num].obj;
else
id = -EINVAL;
if (id < 0 || id >= num_objs)
return NULL;
return fw_objs[id];
}
static const char *uof_get_name_420xx(struct adf_accel_dev *accel_dev, u32 obj_num)
{
int num_fw_objs = ARRAY_SIZE(adf_420xx_fw_objs);
return uof_get_name(accel_dev, obj_num, adf_420xx_fw_objs, num_fw_objs);
}
static int uof_get_obj_type(struct adf_accel_dev *accel_dev, u32 obj_num)
{
const struct adf_fw_config *fw_config;
if (obj_num >= uof_get_num_objs(accel_dev))
return -EINVAL;
fw_config = get_fw_config(accel_dev);
if (!fw_config)
return -EINVAL;
return fw_config[obj_num].obj;
}
static u32 uof_get_ae_mask(struct adf_accel_dev *accel_dev, u32 obj_num)
{
const struct adf_fw_config *fw_config;
fw_config = get_fw_config(accel_dev);
if (!fw_config)
return 0;
return fw_config[obj_num].ae_mask;
}
static void adf_gen4_set_err_mask(struct adf_dev_err_mask *dev_err_mask)
{
dev_err_mask->cppagentcmdpar_mask = ADF_420XX_HICPPAGENTCMDPARERRLOG_MASK;
dev_err_mask->parerr_ath_cph_mask = ADF_420XX_PARITYERRORMASK_ATH_CPH_MASK;
dev_err_mask->parerr_cpr_xlt_mask = ADF_420XX_PARITYERRORMASK_CPR_XLT_MASK;
dev_err_mask->parerr_dcpr_ucs_mask = ADF_420XX_PARITYERRORMASK_DCPR_UCS_MASK;
dev_err_mask->parerr_pke_mask = ADF_420XX_PARITYERRORMASK_PKE_MASK;
dev_err_mask->parerr_wat_wcp_mask = ADF_420XX_PARITYERRORMASK_WAT_WCP_MASK;
dev_err_mask->ssmfeatren_mask = ADF_420XX_SSMFEATREN_MASK;
}
void adf_init_hw_data_420xx(struct adf_hw_device_data *hw_data, u32 dev_id)
{
hw_data->dev_class = &adf_420xx_class;
hw_data->instance_id = adf_420xx_class.instances++;
hw_data->num_banks = ADF_GEN4_ETR_MAX_BANKS;
hw_data->num_banks_per_vf = ADF_GEN4_NUM_BANKS_PER_VF;
hw_data->num_rings_per_bank = ADF_GEN4_NUM_RINGS_PER_BANK;
hw_data->num_accel = ADF_GEN4_MAX_ACCELERATORS;
hw_data->num_engines = ADF_420XX_MAX_ACCELENGINES;
hw_data->num_logical_accel = 1;
hw_data->tx_rx_gap = ADF_GEN4_RX_RINGS_OFFSET;
hw_data->tx_rings_mask = ADF_GEN4_TX_RINGS_MASK;
hw_data->ring_to_svc_map = ADF_GEN4_DEFAULT_RING_TO_SRV_MAP;
hw_data->alloc_irq = adf_isr_resource_alloc;
hw_data->free_irq = adf_isr_resource_free;
hw_data->enable_error_correction = adf_gen4_enable_error_correction;
hw_data->get_accel_mask = adf_gen4_get_accel_mask;
hw_data->get_ae_mask = get_ae_mask;
hw_data->get_num_accels = adf_gen4_get_num_accels;
hw_data->get_num_aes = adf_gen4_get_num_aes;
hw_data->get_sram_bar_id = adf_gen4_get_sram_bar_id;
hw_data->get_etr_bar_id = adf_gen4_get_etr_bar_id;
hw_data->get_misc_bar_id = adf_gen4_get_misc_bar_id;
hw_data->get_arb_info = adf_gen4_get_arb_info;
hw_data->get_admin_info = adf_gen4_get_admin_info;
hw_data->get_accel_cap = get_accel_cap;
hw_data->get_sku = adf_gen4_get_sku;
hw_data->init_admin_comms = adf_init_admin_comms;
hw_data->exit_admin_comms = adf_exit_admin_comms;
hw_data->send_admin_init = adf_send_admin_init;
hw_data->init_arb = adf_init_arb;
hw_data->exit_arb = adf_exit_arb;
hw_data->get_arb_mapping = adf_get_arbiter_mapping;
hw_data->enable_ints = adf_gen4_enable_ints;
hw_data->init_device = adf_gen4_init_device;
hw_data->reset_device = adf_reset_flr;
hw_data->admin_ae_mask = ADF_420XX_ADMIN_AE_MASK;
hw_data->num_rps = ADF_GEN4_MAX_RPS;
hw_data->fw_name = ADF_420XX_FW;
hw_data->fw_mmp_name = ADF_420XX_MMP;
hw_data->uof_get_name = uof_get_name_420xx;
hw_data->uof_get_num_objs = uof_get_num_objs;
hw_data->uof_get_obj_type = uof_get_obj_type;
hw_data->uof_get_ae_mask = uof_get_ae_mask;
hw_data->get_rp_group = get_rp_group;
hw_data->get_ena_thd_mask = get_ena_thd_mask;
hw_data->set_msix_rttable = adf_gen4_set_msix_default_rttable;
hw_data->set_ssm_wdtimer = adf_gen4_set_ssm_wdtimer;
hw_data->get_ring_to_svc_map = adf_gen4_get_ring_to_svc_map;
hw_data->disable_iov = adf_disable_sriov;
hw_data->ring_pair_reset = adf_gen4_ring_pair_reset;
hw_data->bank_state_save = adf_bank_state_save;
hw_data->bank_state_restore = adf_bank_state_restore;
hw_data->enable_pm = adf_gen4_enable_pm;
hw_data->handle_pm_interrupt = adf_gen4_handle_pm_interrupt;
hw_data->dev_config = adf_gen4_dev_config;
hw_data->start_timer = adf_timer_start;
hw_data->stop_timer = adf_timer_stop;
hw_data->get_hb_clock = adf_gen4_get_heartbeat_clock;
hw_data->num_hb_ctrs = ADF_NUM_HB_CNT_PER_AE;
hw_data->clock_frequency = ADF_420XX_AE_FREQ;
hw_data->services_supported = adf_gen4_services_supported;
hw_data->get_svc_slice_cnt = adf_gen4_get_svc_slice_cnt;
hw_data->accel_capabilities_ext_mask = ADF_ACCEL_CAPABILITIES_EXT_ZSTD_LZ4S;
adf_gen4_set_err_mask(&hw_data->dev_err_mask);
adf_gen4_init_hw_csr_ops(&hw_data->csr_ops);
adf_gen4_init_pf_pfvf_ops(&hw_data->pfvf_ops);
adf_gen4_init_dc_ops(&hw_data->dc_ops);
adf_gen4_init_ras_ops(&hw_data->ras_ops);
adf_gen4_init_tl_data(&hw_data->tl_data);
adf_gen4_init_vf_mig_ops(&hw_data->vfmig_ops);
adf_init_rl_data(&hw_data->rl_data);
}
void adf_clean_hw_data_420xx(struct adf_hw_device_data *hw_data)
{
hw_data->dev_class->instances--;
}
@@ -0,0 +1,55 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright(c) 2023 Intel Corporation */
#ifndef ADF_420XX_HW_DATA_H_
#define ADF_420XX_HW_DATA_H_
#include <adf_accel_devices.h>
#define ADF_420XX_MAX_ACCELENGINES 17
#define ADF_420XX_ACCELENGINES_MASK 0x1FFFF
#define ADF_420XX_ADMIN_AE_MASK 0x10000
#define ADF_420XX_HICPPAGENTCMDPARERRLOG_MASK (0xFF)
#define ADF_420XX_PARITYERRORMASK_ATH_CPH_MASK (0xFF00FF)
#define ADF_420XX_PARITYERRORMASK_CPR_XLT_MASK (0x10001)
#define ADF_420XX_PARITYERRORMASK_DCPR_UCS_MASK (0xF0007)
#define ADF_420XX_PARITYERRORMASK_PKE_MASK (0xFFF)
#define ADF_420XX_PARITYERRORMASK_WAT_WCP_MASK (0x3FF03FF)
/*
* SSMFEATREN bit mask
* BIT(4) - enables parity detection on CPP
* BIT(12) - enables the logging of push/pull data errors
* in pperr register
* BIT(16) - BIT(27) - enable parity detection on SPPs
*/
#define ADF_420XX_SSMFEATREN_MASK \
(BIT(4) | BIT(12) | BIT(16) | BIT(17) | BIT(18) | BIT(19) | BIT(20) | \
BIT(21) | BIT(22) | BIT(23) | BIT(24) | BIT(25) | BIT(26) | BIT(27))
/* Firmware Binaries */
#define ADF_420XX_FW "qat_420xx.bin"
#define ADF_420XX_MMP "qat_420xx_mmp.bin"
#define ADF_420XX_SYM_OBJ "qat_420xx_sym.bin"
#define ADF_420XX_DC_OBJ "qat_420xx_dc.bin"
#define ADF_420XX_ASYM_OBJ "qat_420xx_asym.bin"
#define ADF_420XX_ADMIN_OBJ "qat_420xx_admin.bin"
/* RL constants */
#define ADF_420XX_RL_PCIE_SCALE_FACTOR_DIV 100
#define ADF_420XX_RL_PCIE_SCALE_FACTOR_MUL 102
#define ADF_420XX_RL_DCPR_CORRECTION 1
#define ADF_420XX_RL_SCANS_PER_SEC 954
#define ADF_420XX_RL_MAX_TP_ASYM 173750UL
#define ADF_420XX_RL_MAX_TP_SYM 95000UL
#define ADF_420XX_RL_MAX_TP_DC 40000UL
#define ADF_420XX_RL_SLICE_REF 1000UL
/* Clocks frequency */
#define ADF_420XX_AE_FREQ (1000 * HZ_PER_MHZ)
void adf_init_hw_data_420xx(struct adf_hw_device_data *hw_data, u32 dev_id);
void adf_clean_hw_data_420xx(struct adf_hw_device_data *hw_data);
#endif
@@ -0,0 +1,212 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2023 Intel Corporation */
#include <linux/device.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <adf_accel_devices.h>
#include <adf_gen4_hw_data.h>
#include <adf_gen4_config.h>
#include <adf_cfg.h>
#include <adf_common_drv.h>
#include <adf_dbgfs.h>
#include "adf_420xx_hw_data.h"
static const struct pci_device_id adf_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_QAT_420XX) },
{ }
};
MODULE_DEVICE_TABLE(pci, adf_pci_tbl);
static void adf_cleanup_accel(struct adf_accel_dev *accel_dev)
{
if (accel_dev->hw_device) {
adf_clean_hw_data_420xx(accel_dev->hw_device);
accel_dev->hw_device = NULL;
}
adf_dbgfs_exit(accel_dev);
adf_cfg_dev_remove(accel_dev);
adf_devmgr_rm_dev(accel_dev, NULL);
}
static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct adf_accel_dev *accel_dev;
struct adf_accel_pci *accel_pci_dev;
struct adf_hw_device_data *hw_data;
unsigned int i, bar_nr;
unsigned long bar_mask;
struct adf_bar *bar;
int ret;
if (num_possible_nodes() > 1 && dev_to_node(&pdev->dev) < 0) {
/*
* If the accelerator is connected to a node with no memory
* there is no point in using the accelerator since the remote
* memory transaction will be very slow.
*/
dev_err(&pdev->dev, "Invalid NUMA configuration.\n");
return -EINVAL;
}
accel_dev = devm_kzalloc(&pdev->dev, sizeof(*accel_dev), GFP_KERNEL);
if (!accel_dev)
return -ENOMEM;
INIT_LIST_HEAD(&accel_dev->crypto_list);
accel_pci_dev = &accel_dev->accel_pci_dev;
accel_pci_dev->pci_dev = pdev;
/*
* Add accel device to accel table
* This should be called before adf_cleanup_accel is called
*/
if (adf_devmgr_add_dev(accel_dev, NULL)) {
dev_err(&pdev->dev, "Failed to add new accelerator device.\n");
return -EFAULT;
}
accel_dev->owner = THIS_MODULE;
/* Allocate and initialise device hardware meta-data structure */
hw_data = devm_kzalloc(&pdev->dev, sizeof(*hw_data), GFP_KERNEL);
if (!hw_data) {
ret = -ENOMEM;
goto out_err;
}
accel_dev->hw_device = hw_data;
adf_init_hw_data_420xx(accel_dev->hw_device, ent->device);
pci_read_config_byte(pdev, PCI_REVISION_ID, &accel_pci_dev->revid);
pci_read_config_dword(pdev, ADF_GEN4_FUSECTL4_OFFSET, &hw_data->fuses[ADF_FUSECTL4]);
/* Get Accelerators and Accelerators Engines masks */
hw_data->accel_mask = hw_data->get_accel_mask(hw_data);
hw_data->ae_mask = hw_data->get_ae_mask(hw_data);
accel_pci_dev->sku = hw_data->get_sku(hw_data);
/* If the device has no acceleration engines then ignore it */
if (!hw_data->accel_mask || !hw_data->ae_mask ||
(~hw_data->ae_mask & 0x01)) {
dev_err(&pdev->dev, "No acceleration units found.\n");
ret = -EFAULT;
goto out_err;
}
/* Create device configuration table */
ret = adf_cfg_dev_add(accel_dev);
if (ret)
goto out_err;
/* Enable PCI device */
ret = pcim_enable_device(pdev);
if (ret) {
pci_err(pdev, "Can't enable PCI device.\n");
goto out_err;
}
/* Set DMA identifier */
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (ret) {
dev_err(&pdev->dev, "No usable DMA configuration.\n");
goto out_err;
}
ret = adf_gen4_cfg_dev_init(accel_dev);
if (ret) {
dev_err(&pdev->dev, "Failed to initialize configuration.\n");
goto out_err;
}
/* Get accelerator capabilities mask */
hw_data->accel_capabilities_mask = hw_data->get_accel_cap(accel_dev);
if (!hw_data->accel_capabilities_mask) {
dev_err(&pdev->dev, "Failed to get capabilities mask.\n");
ret = -EINVAL;
goto out_err;
}
/* Find and map all the device's BARS */
bar_mask = pci_select_bars(pdev, IORESOURCE_MEM) & ADF_GEN4_BAR_MASK;
ret = pcim_request_all_regions(pdev, pci_name(pdev));
if (ret) {
pci_err(pdev, "Failed to request PCI regions.\n");
goto out_err;
}
i = 0;
for_each_set_bit(bar_nr, &bar_mask, PCI_STD_NUM_BARS) {
bar = &accel_pci_dev->pci_bars[i++];
bar->virt_addr = pcim_iomap(pdev, bar_nr, 0);
if (!bar->virt_addr) {
pci_err(pdev, "Failed to ioremap PCI region.\n");
ret = -ENOMEM;
goto out_err;
}
}
if (pci_save_state(pdev)) {
pci_err(pdev, "Failed to save pci state.\n");
ret = -ENOMEM;
goto out_err;
}
accel_dev->ras_errors.enabled = true;
adf_dbgfs_init(accel_dev);
ret = adf_dev_up(accel_dev, true);
if (ret)
goto out_err_dev_stop;
ret = adf_sysfs_init(accel_dev);
if (ret)
goto out_err_dev_stop;
return ret;
out_err_dev_stop:
adf_dev_down(accel_dev);
out_err:
adf_cleanup_accel(accel_dev);
return ret;
}
static void adf_remove(struct pci_dev *pdev)
{
struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev);
if (!accel_dev) {
pr_err("QAT: Driver removal failed\n");
return;
}
adf_dev_down(accel_dev);
adf_cleanup_accel(accel_dev);
}
static void adf_shutdown(struct pci_dev *pdev)
{
struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev);
adf_dev_down(accel_dev);
}
static struct pci_driver adf_driver = {
.id_table = adf_pci_tbl,
.name = ADF_420XX_DEVICE_NAME,
.probe = adf_probe,
.remove = adf_remove,
.shutdown = adf_shutdown,
.sriov_configure = adf_sriov_configure,
.err_handler = &adf_err_handler,
};
module_pci_driver(adf_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Intel");
MODULE_FIRMWARE(ADF_420XX_FW);
MODULE_FIRMWARE(ADF_420XX_MMP);
MODULE_DESCRIPTION("Intel(R) QuickAssist Technology");
MODULE_SOFTDEP("pre: crypto-intel_qat");
MODULE_IMPORT_NS("CRYPTO_QAT");
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
obj-$(CONFIG_CRYPTO_DEV_QAT_4XXX) += qat_4xxx.o
qat_4xxx-y := adf_drv.o adf_4xxx_hw_data.o
@@ -0,0 +1,491 @@
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2020 - 2021 Intel Corporation */
#include <linux/iopoll.h>
#include <adf_accel_devices.h>
#include <adf_admin.h>
#include <adf_bank_state.h>
#include <adf_cfg.h>
#include <adf_cfg_services.h>
#include <adf_clock.h>
#include <adf_common_drv.h>
#include <adf_fw_config.h>
#include <adf_gen4_config.h>
#include <adf_gen4_hw_csr_data.h>
#include <adf_gen4_hw_data.h>
#include <adf_gen4_pfvf.h>
#include <adf_gen4_pm.h>
#include "adf_gen4_ras.h"
#include <adf_gen4_tl.h>
#include <adf_gen4_vf_mig.h>
#include <adf_timer.h>
#include "adf_4xxx_hw_data.h"
#include "icp_qat_hw.h"
#define ADF_AE_GROUP_0 GENMASK(3, 0)
#define ADF_AE_GROUP_1 GENMASK(7, 4)
#define ADF_AE_GROUP_2 BIT(8)
#define ENA_THD_MASK_ASYM GENMASK(1, 0)
#define ENA_THD_MASK_ASYM_401XX GENMASK(5, 0)
#define ENA_THD_MASK_SYM GENMASK(6, 0)
#define ENA_THD_MASK_DC GENMASK(1, 0)
static const char * const adf_4xxx_fw_objs[] = {
[ADF_FW_SYM_OBJ] = ADF_4XXX_SYM_OBJ,
[ADF_FW_ASYM_OBJ] = ADF_4XXX_ASYM_OBJ,
[ADF_FW_DC_OBJ] = ADF_4XXX_DC_OBJ,
[ADF_FW_ADMIN_OBJ] = ADF_4XXX_ADMIN_OBJ,
};
static const char * const adf_402xx_fw_objs[] = {
[ADF_FW_SYM_OBJ] = ADF_402XX_SYM_OBJ,
[ADF_FW_ASYM_OBJ] = ADF_402XX_ASYM_OBJ,
[ADF_FW_DC_OBJ] = ADF_402XX_DC_OBJ,
[ADF_FW_ADMIN_OBJ] = ADF_402XX_ADMIN_OBJ,
};
static const struct adf_fw_config adf_fw_cy_config[] = {
{ADF_AE_GROUP_1, ADF_FW_SYM_OBJ},
{ADF_AE_GROUP_0, ADF_FW_ASYM_OBJ},
{ADF_AE_GROUP_2, ADF_FW_ADMIN_OBJ},
};
static const struct adf_fw_config adf_fw_dc_config[] = {
{ADF_AE_GROUP_1, ADF_FW_DC_OBJ},
{ADF_AE_GROUP_0, ADF_FW_DC_OBJ},
{ADF_AE_GROUP_2, ADF_FW_ADMIN_OBJ},
};
static const struct adf_fw_config adf_fw_sym_config[] = {
{ADF_AE_GROUP_1, ADF_FW_SYM_OBJ},
{ADF_AE_GROUP_0, ADF_FW_SYM_OBJ},
{ADF_AE_GROUP_2, ADF_FW_ADMIN_OBJ},
};
static const struct adf_fw_config adf_fw_asym_config[] = {
{ADF_AE_GROUP_1, ADF_FW_ASYM_OBJ},
{ADF_AE_GROUP_0, ADF_FW_ASYM_OBJ},
{ADF_AE_GROUP_2, ADF_FW_ADMIN_OBJ},
};
static const struct adf_fw_config adf_fw_asym_dc_config[] = {
{ADF_AE_GROUP_1, ADF_FW_ASYM_OBJ},
{ADF_AE_GROUP_0, ADF_FW_DC_OBJ},
{ADF_AE_GROUP_2, ADF_FW_ADMIN_OBJ},
};
static const struct adf_fw_config adf_fw_sym_dc_config[] = {
{ADF_AE_GROUP_1, ADF_FW_SYM_OBJ},
{ADF_AE_GROUP_0, ADF_FW_DC_OBJ},
{ADF_AE_GROUP_2, ADF_FW_ADMIN_OBJ},
};
static const struct adf_fw_config adf_fw_dcc_config[] = {
{ADF_AE_GROUP_1, ADF_FW_DC_OBJ},
{ADF_AE_GROUP_0, ADF_FW_SYM_OBJ},
{ADF_AE_GROUP_2, ADF_FW_ADMIN_OBJ},
};
static_assert(ARRAY_SIZE(adf_fw_cy_config) == ARRAY_SIZE(adf_fw_dc_config));
static_assert(ARRAY_SIZE(adf_fw_cy_config) == ARRAY_SIZE(adf_fw_sym_config));
static_assert(ARRAY_SIZE(adf_fw_cy_config) == ARRAY_SIZE(adf_fw_asym_config));
static_assert(ARRAY_SIZE(adf_fw_cy_config) == ARRAY_SIZE(adf_fw_asym_dc_config));
static_assert(ARRAY_SIZE(adf_fw_cy_config) == ARRAY_SIZE(adf_fw_sym_dc_config));
static_assert(ARRAY_SIZE(adf_fw_cy_config) == ARRAY_SIZE(adf_fw_dcc_config));
static struct adf_hw_device_class adf_4xxx_class = {
.name = ADF_4XXX_DEVICE_NAME,
.type = DEV_4XXX,
};
static u32 get_ae_mask(struct adf_hw_device_data *self)
{
unsigned long fuses = self->fuses[ADF_FUSECTL4];
u32 mask = ADF_4XXX_ACCELENGINES_MASK;
if (test_bit(0, &fuses))
mask &= ~ADF_AE_GROUP_0;
if (test_bit(4, &fuses))
mask &= ~ADF_AE_GROUP_1;
if (test_bit(8, &fuses))
mask &= ~ADF_AE_GROUP_2;
return mask;
}
static u32 get_accel_cap(struct adf_accel_dev *accel_dev)
{
struct pci_dev *pdev = accel_dev->accel_pci_dev.pci_dev;
u32 capabilities_sym, capabilities_asym, capabilities_dc;
u32 capabilities_dcc;
u32 fusectl1;
/* Read accelerator capabilities mask */
pci_read_config_dword(pdev, ADF_GEN4_FUSECTL1_OFFSET, &fusectl1);
capabilities_sym = ICP_ACCEL_CAPABILITIES_CRYPTO_SYMMETRIC |
ICP_ACCEL_CAPABILITIES_CIPHER |
ICP_ACCEL_CAPABILITIES_AUTHENTICATION |
ICP_ACCEL_CAPABILITIES_SHA3 |
ICP_ACCEL_CAPABILITIES_SHA3_EXT |
ICP_ACCEL_CAPABILITIES_HKDF |
ICP_ACCEL_CAPABILITIES_CHACHA_POLY |
ICP_ACCEL_CAPABILITIES_AESGCM_SPC |
ICP_ACCEL_CAPABILITIES_SM3 |
ICP_ACCEL_CAPABILITIES_SM4 |
ICP_ACCEL_CAPABILITIES_AES_V2;
/* A set bit in fusectl1 means the feature is OFF in this SKU */
if (fusectl1 & ICP_ACCEL_GEN4_MASK_CIPHER_SLICE) {
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_CRYPTO_SYMMETRIC;
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_HKDF;
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_CIPHER;
}
if (fusectl1 & ICP_ACCEL_GEN4_MASK_UCS_SLICE) {
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_CHACHA_POLY;
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_AESGCM_SPC;
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_AES_V2;
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_CIPHER;
}
if (fusectl1 & ICP_ACCEL_GEN4_MASK_AUTH_SLICE) {
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_AUTHENTICATION;
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_SHA3;
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_SHA3_EXT;
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_CIPHER;
}
if (fusectl1 & ICP_ACCEL_GEN4_MASK_SMX_SLICE) {
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_SM3;
capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_SM4;
}
capabilities_asym = ICP_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC |
ICP_ACCEL_CAPABILITIES_CIPHER |
ICP_ACCEL_CAPABILITIES_SM2 |
ICP_ACCEL_CAPABILITIES_ECEDMONT;
if (fusectl1 & ICP_ACCEL_GEN4_MASK_PKE_SLICE) {
capabilities_asym &= ~ICP_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC;
capabilities_asym &= ~ICP_ACCEL_CAPABILITIES_SM2;
capabilities_asym &= ~ICP_ACCEL_CAPABILITIES_ECEDMONT;
}
capabilities_dc = ICP_ACCEL_CAPABILITIES_COMPRESSION |
ICP_ACCEL_CAPABILITIES_LZ4_COMPRESSION |
ICP_ACCEL_CAPABILITIES_LZ4S_COMPRESSION |
ICP_ACCEL_CAPABILITIES_CNV_INTEGRITY64;
if (fusectl1 & ICP_ACCEL_GEN4_MASK_COMPRESS_SLICE) {
capabilities_dc &= ~ICP_ACCEL_CAPABILITIES_COMPRESSION;
capabilities_dc &= ~ICP_ACCEL_CAPABILITIES_LZ4_COMPRESSION;
capabilities_dc &= ~ICP_ACCEL_CAPABILITIES_LZ4S_COMPRESSION;
capabilities_dc &= ~ICP_ACCEL_CAPABILITIES_CNV_INTEGRITY64;
}
switch (adf_get_service_enabled(accel_dev)) {
case SVC_SYM_ASYM:
return capabilities_sym | capabilities_asym;
case SVC_DC:
return capabilities_dc;
case SVC_DCC:
/*
* Sym capabilities are available for chaining operations,
* but sym crypto instances cannot be supported
*/
capabilities_dcc = capabilities_dc | capabilities_sym;
capabilities_dcc &= ~ICP_ACCEL_CAPABILITIES_CRYPTO_SYMMETRIC;
return capabilities_dcc;
case SVC_SYM:
return capabilities_sym;
case SVC_ASYM:
return capabilities_asym;
case SVC_ASYM_DC:
return capabilities_asym | capabilities_dc;
case SVC_SYM_DC:
return capabilities_sym | capabilities_dc;
default:
return 0;
}
}
static const u32 *adf_get_arbiter_mapping(struct adf_accel_dev *accel_dev)
{
if (adf_gen4_init_thd2arb_map(accel_dev))
dev_warn(&GET_DEV(accel_dev),
"Failed to generate thread to arbiter mapping");
return GET_HW_DATA(accel_dev)->thd_to_arb_map;
}
static void adf_init_rl_data(struct adf_rl_hw_data *rl_data)
{
rl_data->pciout_tb_offset = ADF_GEN4_RL_TOKEN_PCIEOUT_BUCKET_OFFSET;
rl_data->pciin_tb_offset = ADF_GEN4_RL_TOKEN_PCIEIN_BUCKET_OFFSET;
rl_data->r2l_offset = ADF_GEN4_RL_R2L_OFFSET;
rl_data->l2c_offset = ADF_GEN4_RL_L2C_OFFSET;
rl_data->c2s_offset = ADF_GEN4_RL_C2S_OFFSET;
rl_data->pcie_scale_div = ADF_4XXX_RL_PCIE_SCALE_FACTOR_DIV;
rl_data->pcie_scale_mul = ADF_4XXX_RL_PCIE_SCALE_FACTOR_MUL;
rl_data->dcpr_correction = ADF_4XXX_RL_DCPR_CORRECTION;
rl_data->max_tp[SVC_ASYM] = ADF_4XXX_RL_MAX_TP_ASYM;
rl_data->max_tp[SVC_SYM] = ADF_4XXX_RL_MAX_TP_SYM;
rl_data->max_tp[SVC_DC] = ADF_4XXX_RL_MAX_TP_DC;
rl_data->scan_interval = ADF_4XXX_RL_SCANS_PER_SEC;
rl_data->scale_ref = ADF_4XXX_RL_SLICE_REF;
adf_gen4_init_num_svc_aes(rl_data);
}
static u32 uof_get_num_objs(struct adf_accel_dev *accel_dev)
{
return ARRAY_SIZE(adf_fw_cy_config);
}
static const struct adf_fw_config *get_fw_config(struct adf_accel_dev *accel_dev)
{
switch (adf_get_service_enabled(accel_dev)) {
case SVC_SYM_ASYM:
return adf_fw_cy_config;
case SVC_DC:
return adf_fw_dc_config;
case SVC_DCC:
return adf_fw_dcc_config;
case SVC_SYM:
return adf_fw_sym_config;
case SVC_ASYM:
return adf_fw_asym_config;
case SVC_ASYM_DC:
return adf_fw_asym_dc_config;
case SVC_SYM_DC:
return adf_fw_sym_dc_config;
default:
return NULL;
}
}
static int get_rp_group(struct adf_accel_dev *accel_dev, u32 ae_mask)
{
switch (ae_mask) {
case ADF_AE_GROUP_0:
return RP_GROUP_0;
case ADF_AE_GROUP_1:
return RP_GROUP_1;
default:
dev_dbg(&GET_DEV(accel_dev), "ae_mask not recognized");
return -EINVAL;
}
}
static u32 get_ena_thd_mask(struct adf_accel_dev *accel_dev, u32 obj_num)
{
const struct adf_fw_config *fw_config;
if (obj_num >= uof_get_num_objs(accel_dev))
return ADF_GEN4_ENA_THD_MASK_ERROR;
fw_config = get_fw_config(accel_dev);
if (!fw_config)
return ADF_GEN4_ENA_THD_MASK_ERROR;
switch (fw_config[obj_num].obj) {
case ADF_FW_ASYM_OBJ:
return ENA_THD_MASK_ASYM;
case ADF_FW_SYM_OBJ:
return ENA_THD_MASK_SYM;
case ADF_FW_DC_OBJ:
return ENA_THD_MASK_DC;
default:
return ADF_GEN4_ENA_THD_MASK_ERROR;
}
}
static u32 get_ena_thd_mask_401xx(struct adf_accel_dev *accel_dev, u32 obj_num)
{
const struct adf_fw_config *fw_config;
if (obj_num >= uof_get_num_objs(accel_dev))
return ADF_GEN4_ENA_THD_MASK_ERROR;
fw_config = get_fw_config(accel_dev);
if (!fw_config)
return ADF_GEN4_ENA_THD_MASK_ERROR;
switch (fw_config[obj_num].obj) {
case ADF_FW_ASYM_OBJ:
return ENA_THD_MASK_ASYM_401XX;
case ADF_FW_SYM_OBJ:
return ENA_THD_MASK_SYM;
case ADF_FW_DC_OBJ:
return ENA_THD_MASK_DC;
default:
return ADF_GEN4_ENA_THD_MASK_ERROR;
}
}
static const char *uof_get_name(struct adf_accel_dev *accel_dev, u32 obj_num,
const char * const fw_objs[], int num_objs)
{
const struct adf_fw_config *fw_config;
int id;
fw_config = get_fw_config(accel_dev);
if (fw_config)
id = fw_config[obj_num].obj;
else
id = -EINVAL;
if (id < 0 || id >= num_objs)
return NULL;
return fw_objs[id];
}
static const char *uof_get_name_4xxx(struct adf_accel_dev *accel_dev, u32 obj_num)
{
int num_fw_objs = ARRAY_SIZE(adf_4xxx_fw_objs);
return uof_get_name(accel_dev, obj_num, adf_4xxx_fw_objs, num_fw_objs);
}
static const char *uof_get_name_402xx(struct adf_accel_dev *accel_dev, u32 obj_num)
{
int num_fw_objs = ARRAY_SIZE(adf_402xx_fw_objs);
return uof_get_name(accel_dev, obj_num, adf_402xx_fw_objs, num_fw_objs);
}
static int uof_get_obj_type(struct adf_accel_dev *accel_dev, u32 obj_num)
{
const struct adf_fw_config *fw_config;
if (obj_num >= uof_get_num_objs(accel_dev))
return -EINVAL;
fw_config = get_fw_config(accel_dev);
if (!fw_config)
return -EINVAL;
return fw_config[obj_num].obj;
}
static u32 uof_get_ae_mask(struct adf_accel_dev *accel_dev, u32 obj_num)
{
const struct adf_fw_config *fw_config;
fw_config = get_fw_config(accel_dev);
if (!fw_config)
return 0;
return fw_config[obj_num].ae_mask;
}
static void adf_gen4_set_err_mask(struct adf_dev_err_mask *dev_err_mask)
{
dev_err_mask->cppagentcmdpar_mask = ADF_4XXX_HICPPAGENTCMDPARERRLOG_MASK;
dev_err_mask->parerr_ath_cph_mask = ADF_4XXX_PARITYERRORMASK_ATH_CPH_MASK;
dev_err_mask->parerr_cpr_xlt_mask = ADF_4XXX_PARITYERRORMASK_CPR_XLT_MASK;
dev_err_mask->parerr_dcpr_ucs_mask = ADF_4XXX_PARITYERRORMASK_DCPR_UCS_MASK;
dev_err_mask->parerr_pke_mask = ADF_4XXX_PARITYERRORMASK_PKE_MASK;
dev_err_mask->ssmfeatren_mask = ADF_4XXX_SSMFEATREN_MASK;
}
void adf_init_hw_data_4xxx(struct adf_hw_device_data *hw_data, u32 dev_id)
{
hw_data->dev_class = &adf_4xxx_class;
hw_data->instance_id = adf_4xxx_class.instances++;
hw_data->num_banks = ADF_GEN4_ETR_MAX_BANKS;
hw_data->num_banks_per_vf = ADF_GEN4_NUM_BANKS_PER_VF;
hw_data->num_rings_per_bank = ADF_GEN4_NUM_RINGS_PER_BANK;
hw_data->num_accel = ADF_GEN4_MAX_ACCELERATORS;
hw_data->num_engines = ADF_4XXX_MAX_ACCELENGINES;
hw_data->num_logical_accel = 1;
hw_data->tx_rx_gap = ADF_GEN4_RX_RINGS_OFFSET;
hw_data->tx_rings_mask = ADF_GEN4_TX_RINGS_MASK;
hw_data->ring_to_svc_map = ADF_GEN4_DEFAULT_RING_TO_SRV_MAP;
hw_data->alloc_irq = adf_isr_resource_alloc;
hw_data->free_irq = adf_isr_resource_free;
hw_data->enable_error_correction = adf_gen4_enable_error_correction;
hw_data->get_accel_mask = adf_gen4_get_accel_mask;
hw_data->get_ae_mask = get_ae_mask;
hw_data->get_num_accels = adf_gen4_get_num_accels;
hw_data->get_num_aes = adf_gen4_get_num_aes;
hw_data->get_sram_bar_id = adf_gen4_get_sram_bar_id;
hw_data->get_etr_bar_id = adf_gen4_get_etr_bar_id;
hw_data->get_misc_bar_id = adf_gen4_get_misc_bar_id;
hw_data->get_arb_info = adf_gen4_get_arb_info;
hw_data->get_admin_info = adf_gen4_get_admin_info;
hw_data->get_accel_cap = get_accel_cap;
hw_data->get_sku = adf_gen4_get_sku;
hw_data->init_admin_comms = adf_init_admin_comms;
hw_data->exit_admin_comms = adf_exit_admin_comms;
hw_data->send_admin_init = adf_send_admin_init;
hw_data->init_arb = adf_init_arb;
hw_data->exit_arb = adf_exit_arb;
hw_data->get_arb_mapping = adf_get_arbiter_mapping;
hw_data->enable_ints = adf_gen4_enable_ints;
hw_data->init_device = adf_gen4_init_device;
hw_data->reset_device = adf_reset_flr;
hw_data->admin_ae_mask = ADF_4XXX_ADMIN_AE_MASK;
hw_data->num_rps = ADF_GEN4_MAX_RPS;
switch (dev_id) {
case PCI_DEVICE_ID_INTEL_QAT_402XX:
hw_data->fw_name = ADF_402XX_FW;
hw_data->fw_mmp_name = ADF_402XX_MMP;
hw_data->uof_get_name = uof_get_name_402xx;
hw_data->get_ena_thd_mask = get_ena_thd_mask;
break;
case PCI_DEVICE_ID_INTEL_QAT_401XX:
hw_data->fw_name = ADF_4XXX_FW;
hw_data->fw_mmp_name = ADF_4XXX_MMP;
hw_data->uof_get_name = uof_get_name_4xxx;
hw_data->get_ena_thd_mask = get_ena_thd_mask_401xx;
break;
default:
hw_data->fw_name = ADF_4XXX_FW;
hw_data->fw_mmp_name = ADF_4XXX_MMP;
hw_data->uof_get_name = uof_get_name_4xxx;
hw_data->get_ena_thd_mask = get_ena_thd_mask;
break;
}
hw_data->uof_get_num_objs = uof_get_num_objs;
hw_data->uof_get_obj_type = uof_get_obj_type;
hw_data->uof_get_ae_mask = uof_get_ae_mask;
hw_data->get_rp_group = get_rp_group;
hw_data->set_msix_rttable = adf_gen4_set_msix_default_rttable;
hw_data->set_ssm_wdtimer = adf_gen4_set_ssm_wdtimer;
hw_data->get_ring_to_svc_map = adf_gen4_get_ring_to_svc_map;
hw_data->disable_iov = adf_disable_sriov;
hw_data->ring_pair_reset = adf_gen4_ring_pair_reset;
hw_data->bank_state_save = adf_bank_state_save;
hw_data->bank_state_restore = adf_bank_state_restore;
hw_data->enable_pm = adf_gen4_enable_pm;
hw_data->handle_pm_interrupt = adf_gen4_handle_pm_interrupt;
hw_data->dev_config = adf_gen4_dev_config;
hw_data->start_timer = adf_timer_start;
hw_data->stop_timer = adf_timer_stop;
hw_data->get_hb_clock = adf_gen4_get_heartbeat_clock;
hw_data->num_hb_ctrs = ADF_NUM_HB_CNT_PER_AE;
hw_data->clock_frequency = ADF_4XXX_AE_FREQ;
hw_data->services_supported = adf_gen4_services_supported;
hw_data->get_svc_slice_cnt = adf_gen4_get_svc_slice_cnt;
hw_data->accel_capabilities_ext_mask = ADF_ACCEL_CAPABILITIES_EXT_ZSTD_LZ4S;
adf_gen4_set_err_mask(&hw_data->dev_err_mask);
adf_gen4_init_hw_csr_ops(&hw_data->csr_ops);
adf_gen4_init_pf_pfvf_ops(&hw_data->pfvf_ops);
adf_gen4_init_dc_ops(&hw_data->dc_ops);
adf_gen4_init_ras_ops(&hw_data->ras_ops);
adf_gen4_init_tl_data(&hw_data->tl_data);
adf_gen4_init_vf_mig_ops(&hw_data->vfmig_ops);
adf_init_rl_data(&hw_data->rl_data);
}
void adf_clean_hw_data_4xxx(struct adf_hw_device_data *hw_data)
{
hw_data->dev_class->instances--;
}
@@ -0,0 +1,62 @@
/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */
/* Copyright(c) 2014 - 2020 Intel Corporation */
#ifndef ADF_4XXX_HW_DATA_H_
#define ADF_4XXX_HW_DATA_H_
#include <linux/units.h>
#include <adf_accel_devices.h>
#define ADF_4XXX_MAX_ACCELENGINES 9
#define ADF_4XXX_ACCELENGINES_MASK (0x1FF)
#define ADF_4XXX_ADMIN_AE_MASK (0x100)
#define ADF_4XXX_HICPPAGENTCMDPARERRLOG_MASK 0x1F
#define ADF_4XXX_PARITYERRORMASK_ATH_CPH_MASK 0xF000F
#define ADF_4XXX_PARITYERRORMASK_CPR_XLT_MASK 0x10001
#define ADF_4XXX_PARITYERRORMASK_DCPR_UCS_MASK 0x30007
#define ADF_4XXX_PARITYERRORMASK_PKE_MASK 0x3F
/*
* SSMFEATREN bit mask
* BIT(4) - enables parity detection on CPP
* BIT(12) - enables the logging of push/pull data errors
* in pperr register
* BIT(16) - BIT(23) - enable parity detection on SPPs
*/
#define ADF_4XXX_SSMFEATREN_MASK \
(BIT(4) | BIT(12) | BIT(16) | BIT(17) | BIT(18) | \
BIT(19) | BIT(20) | BIT(21) | BIT(22) | BIT(23))
/* Firmware Binaries */
#define ADF_4XXX_FW "qat_4xxx.bin"
#define ADF_4XXX_MMP "qat_4xxx_mmp.bin"
#define ADF_4XXX_SYM_OBJ "qat_4xxx_sym.bin"
#define ADF_4XXX_DC_OBJ "qat_4xxx_dc.bin"
#define ADF_4XXX_ASYM_OBJ "qat_4xxx_asym.bin"
#define ADF_4XXX_ADMIN_OBJ "qat_4xxx_admin.bin"
/* Firmware for 402XXX */
#define ADF_402XX_FW "qat_402xx.bin"
#define ADF_402XX_MMP "qat_402xx_mmp.bin"
#define ADF_402XX_SYM_OBJ "qat_402xx_sym.bin"
#define ADF_402XX_DC_OBJ "qat_402xx_dc.bin"
#define ADF_402XX_ASYM_OBJ "qat_402xx_asym.bin"
#define ADF_402XX_ADMIN_OBJ "qat_402xx_admin.bin"
/* RL constants */
#define ADF_4XXX_RL_PCIE_SCALE_FACTOR_DIV 100
#define ADF_4XXX_RL_PCIE_SCALE_FACTOR_MUL 102
#define ADF_4XXX_RL_DCPR_CORRECTION 1
#define ADF_4XXX_RL_SCANS_PER_SEC 954
#define ADF_4XXX_RL_MAX_TP_ASYM 173750UL
#define ADF_4XXX_RL_MAX_TP_SYM 95000UL
#define ADF_4XXX_RL_MAX_TP_DC 45000UL
#define ADF_4XXX_RL_SLICE_REF 1000UL
/* Clocks frequency */
#define ADF_4XXX_AE_FREQ (1000 * HZ_PER_MHZ)
void adf_init_hw_data_4xxx(struct adf_hw_device_data *hw_data, u32 dev_id);
void adf_clean_hw_data_4xxx(struct adf_hw_device_data *hw_data);
#endif
@@ -0,0 +1,216 @@
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2020 Intel Corporation */
#include <linux/device.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <adf_accel_devices.h>
#include <adf_cfg.h>
#include <adf_common_drv.h>
#include <adf_dbgfs.h>
#include <adf_gen4_config.h>
#include <adf_gen4_hw_data.h>
#include "adf_4xxx_hw_data.h"
static const struct pci_device_id adf_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_QAT_4XXX) },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_QAT_401XX) },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_QAT_402XX) },
{ }
};
MODULE_DEVICE_TABLE(pci, adf_pci_tbl);
static void adf_cleanup_accel(struct adf_accel_dev *accel_dev)
{
if (accel_dev->hw_device) {
adf_clean_hw_data_4xxx(accel_dev->hw_device);
accel_dev->hw_device = NULL;
}
adf_dbgfs_exit(accel_dev);
adf_cfg_dev_remove(accel_dev);
adf_devmgr_rm_dev(accel_dev, NULL);
}
static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct adf_accel_dev *accel_dev;
struct adf_accel_pci *accel_pci_dev;
struct adf_hw_device_data *hw_data;
unsigned int i, bar_nr;
unsigned long bar_mask;
struct adf_bar *bar;
int ret;
if (num_possible_nodes() > 1 && dev_to_node(&pdev->dev) < 0) {
/*
* If the accelerator is connected to a node with no memory
* there is no point in using the accelerator since the remote
* memory transaction will be very slow.
*/
dev_err(&pdev->dev, "Invalid NUMA configuration.\n");
return -EINVAL;
}
accel_dev = devm_kzalloc(&pdev->dev, sizeof(*accel_dev), GFP_KERNEL);
if (!accel_dev)
return -ENOMEM;
INIT_LIST_HEAD(&accel_dev->crypto_list);
accel_pci_dev = &accel_dev->accel_pci_dev;
accel_pci_dev->pci_dev = pdev;
/*
* Add accel device to accel table
* This should be called before adf_cleanup_accel is called
*/
if (adf_devmgr_add_dev(accel_dev, NULL)) {
dev_err(&pdev->dev, "Failed to add new accelerator device.\n");
return -EFAULT;
}
accel_dev->owner = THIS_MODULE;
/* Allocate and initialise device hardware meta-data structure */
hw_data = devm_kzalloc(&pdev->dev, sizeof(*hw_data), GFP_KERNEL);
if (!hw_data) {
ret = -ENOMEM;
goto out_err;
}
accel_dev->hw_device = hw_data;
adf_init_hw_data_4xxx(accel_dev->hw_device, ent->device);
pci_read_config_byte(pdev, PCI_REVISION_ID, &accel_pci_dev->revid);
pci_read_config_dword(pdev, ADF_GEN4_FUSECTL4_OFFSET, &hw_data->fuses[ADF_FUSECTL4]);
/* Get Accelerators and Accelerators Engines masks */
hw_data->accel_mask = hw_data->get_accel_mask(hw_data);
hw_data->ae_mask = hw_data->get_ae_mask(hw_data);
accel_pci_dev->sku = hw_data->get_sku(hw_data);
/* If the device has no acceleration engines then ignore it */
if (!hw_data->accel_mask || !hw_data->ae_mask ||
(~hw_data->ae_mask & 0x01)) {
dev_err(&pdev->dev, "No acceleration units found.\n");
ret = -EFAULT;
goto out_err;
}
/* Create device configuration table */
ret = adf_cfg_dev_add(accel_dev);
if (ret)
goto out_err;
/* Enable PCI device */
ret = pcim_enable_device(pdev);
if (ret) {
pci_err(pdev, "Can't enable PCI device.\n");
goto out_err;
}
/* Set DMA identifier */
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (ret) {
dev_err(&pdev->dev, "No usable DMA configuration.\n");
goto out_err;
}
ret = adf_gen4_cfg_dev_init(accel_dev);
if (ret) {
dev_err(&pdev->dev, "Failed to initialize configuration.\n");
goto out_err;
}
/* Get accelerator capabilities mask */
hw_data->accel_capabilities_mask = hw_data->get_accel_cap(accel_dev);
if (!hw_data->accel_capabilities_mask) {
dev_err(&pdev->dev, "Failed to get capabilities mask.\n");
ret = -EINVAL;
goto out_err;
}
/* Find and map all the device's BARS */
bar_mask = pci_select_bars(pdev, IORESOURCE_MEM) & ADF_GEN4_BAR_MASK;
ret = pcim_request_all_regions(pdev, pci_name(pdev));
if (ret) {
pci_err(pdev, "Failed to request PCI regions.\n");
goto out_err;
}
i = 0;
for_each_set_bit(bar_nr, &bar_mask, PCI_STD_NUM_BARS) {
bar = &accel_pci_dev->pci_bars[i++];
bar->virt_addr = pcim_iomap(pdev, bar_nr, 0);
if (!bar->virt_addr) {
pci_err(pdev, "Failed to ioremap PCI region.\n");
ret = -ENOMEM;
goto out_err;
}
}
if (pci_save_state(pdev)) {
pci_err(pdev, "Failed to save pci state.\n");
ret = -ENOMEM;
goto out_err;
}
accel_dev->ras_errors.enabled = true;
adf_dbgfs_init(accel_dev);
ret = adf_dev_up(accel_dev, true);
if (ret)
goto out_err_dev_stop;
ret = adf_sysfs_init(accel_dev);
if (ret)
goto out_err_dev_stop;
return ret;
out_err_dev_stop:
adf_dev_down(accel_dev);
out_err:
adf_cleanup_accel(accel_dev);
return ret;
}
static void adf_remove(struct pci_dev *pdev)
{
struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev);
if (!accel_dev) {
pr_err("QAT: Driver removal failed\n");
return;
}
adf_dev_down(accel_dev);
adf_cleanup_accel(accel_dev);
}
static void adf_shutdown(struct pci_dev *pdev)
{
struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev);
adf_dev_down(accel_dev);
}
static struct pci_driver adf_driver = {
.id_table = adf_pci_tbl,
.name = ADF_4XXX_DEVICE_NAME,
.probe = adf_probe,
.remove = adf_remove,
.shutdown = adf_shutdown,
.sriov_configure = adf_sriov_configure,
.err_handler = &adf_err_handler,
};
module_pci_driver(adf_driver);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Intel");
MODULE_FIRMWARE(ADF_4XXX_FW);
MODULE_FIRMWARE(ADF_402XX_FW);
MODULE_FIRMWARE(ADF_4XXX_MMP);
MODULE_FIRMWARE(ADF_402XX_MMP);
MODULE_DESCRIPTION("Intel(R) QuickAssist Technology");
MODULE_SOFTDEP("pre: crypto-intel_qat");
MODULE_IMPORT_NS("CRYPTO_QAT");
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_CRYPTO_DEV_QAT_6XXX) += qat_6xxx.o
qat_6xxx-y := adf_drv.o adf_6xxx_hw_data.o
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,197 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright(c) 2025 Intel Corporation */
#ifndef ADF_6XXX_HW_DATA_H_
#define ADF_6XXX_HW_DATA_H_
#include <linux/bits.h>
#include <linux/time.h>
#include <linux/units.h>
#include "adf_accel_devices.h"
#include "adf_cfg_common.h"
#include "adf_dc.h"
/* PCIe configuration space */
#define ADF_GEN6_BAR_MASK (BIT(0) | BIT(2) | BIT(4))
#define ADF_GEN6_SRAM_BAR 0
#define ADF_GEN6_PMISC_BAR 1
#define ADF_GEN6_ETR_BAR 2
#define ADF_6XXX_MAX_ACCELENGINES 9
/* Clocks frequency */
#define ADF_GEN6_COUNTER_FREQ (100 * HZ_PER_MHZ)
/* Physical function fuses */
#define ADF_GEN6_FUSECTL0_OFFSET 0x2C8
#define ADF_GEN6_FUSECTL1_OFFSET 0x2CC
#define ADF_GEN6_FUSECTL4_OFFSET 0x2D8
/* Accelerators */
#define ADF_GEN6_ACCELERATORS_MASK 0x1
#define ADF_GEN6_MAX_ACCELERATORS 1
/* MSI-X interrupt */
#define ADF_GEN6_SMIAPF_RP_X0_MASK_OFFSET 0x41A040
#define ADF_GEN6_SMIAPF_RP_X1_MASK_OFFSET 0x41A044
#define ADF_GEN6_SMIAPF_MASK_OFFSET 0x41A084
#define ADF_GEN6_MSIX_RTTABLE_OFFSET(i) (0x409000 + ((i) * 4))
/* Bank and ring configuration */
#define ADF_GEN6_NUM_RINGS_PER_BANK 2
#define ADF_GEN6_NUM_BANKS_PER_VF 4
#define ADF_GEN6_ETR_MAX_BANKS 64
#define ADF_GEN6_RX_RINGS_OFFSET 1
#define ADF_GEN6_TX_RINGS_MASK 0x1
/* Arbiter configuration */
#define ADF_GEN6_ARB_CONFIG (BIT(31) | BIT(6) | BIT(0))
#define ADF_GEN6_ARB_OFFSET 0x000
#define ADF_GEN6_ARB_WRK_2_SER_MAP_OFFSET 0x400
/* Admin interface configuration */
#define ADF_GEN6_ADMINMSGUR_OFFSET 0x500574
#define ADF_GEN6_ADMINMSGLR_OFFSET 0x500578
#define ADF_GEN6_MAILBOX_BASE_OFFSET 0x600970
/* Anti-rollback */
#define ADF_GEN6_SVNCHECK_CSR_MSG 0x640004
/* Fuse bits */
#define ADF_GEN6_ANTI_RB_FUSE_BIT BIT(24)
#define ADF_GEN6_KPT_FUSE_BIT BIT(16)
/*
* Watchdog timers
* Timeout is in cycles. Clock speed may vary across products but this
* value should be a few milli-seconds.
*/
#define ADF_SSM_WDT_DEFAULT_VALUE 0x7000000ULL
#define ADF_SSM_WDT_PKE_DEFAULT_VALUE 0x8000000ULL
#define ADF_SSMWDTATHL_OFFSET 0x5208
#define ADF_SSMWDTATHH_OFFSET 0x520C
#define ADF_SSMWDTCNVL_OFFSET 0x5408
#define ADF_SSMWDTCNVH_OFFSET 0x540C
#define ADF_SSMWDTWCPL_OFFSET 0x5608
#define ADF_SSMWDTWCPH_OFFSET 0x560C
#define ADF_SSMWDTUCSL_OFFSET 0x5808
#define ADF_SSMWDTUCSH_OFFSET 0x580C
#define ADF_SSMWDTDCPRL_OFFSET 0x5A08
#define ADF_SSMWDTDCPRH_OFFSET 0x5A0C
#define ADF_SSMWDTWATL_OFFSET 0x5C08
#define ADF_SSMWDTWATH_OFFSET 0x5C0C
#define ADF_SSMWDTPKEL_OFFSET 0x5E08
#define ADF_SSMWDTPKEH_OFFSET 0x5E0C
/* Ring reset */
#define ADF_RPRESET_POLL_TIMEOUT_US (5 * USEC_PER_SEC)
#define ADF_RPRESET_POLL_DELAY_US 20
#define ADF_WQM_CSR_RPRESETCTL_RESET BIT(0)
#define ADF_WQM_CSR_RPRESETCTL(bank) (0x6000 + (bank) * 8)
#define ADF_WQM_CSR_RPRESETSTS_STATUS BIT(0)
#define ADF_WQM_CSR_RPRESETSTS(bank) (ADF_WQM_CSR_RPRESETCTL(bank) + 4)
/* Controls and sets up the corresponding ring mode of operation */
#define ADF_GEN6_CSR_RINGMODECTL(bank) (0x9000 + (bank) * 4)
/* Specifies the traffic class to use for the transactions to/from the ring */
#define ADF_GEN6_RINGMODECTL_TC_MASK GENMASK(18, 16)
#define ADF_GEN6_RINGMODECTL_TC_DEFAULT 0x7
/* Specifies usage of tc for the transactions to/from this ring */
#define ADF_GEN6_RINGMODECTL_TC_EN_MASK GENMASK(20, 19)
/*
* Use the value programmed in the tc field for request descriptor
* and metadata read transactions
*/
#define ADF_GEN6_RINGMODECTL_TC_EN_OP1 0x1
/* VC0 Resource Control Register */
#define ADF_GEN6_PVC0CTL_OFFSET 0x204
#define ADF_GEN6_PVC0CTL_TCVCMAP_OFFSET 1
#define ADF_GEN6_PVC0CTL_TCVCMAP_MASK GENMASK(7, 1)
#define ADF_GEN6_PVC0CTL_TCVCMAP_DEFAULT 0x3F
/* VC1 Resource Control Register */
#define ADF_GEN6_PVC1CTL_OFFSET 0x210
#define ADF_GEN6_PVC1CTL_TCVCMAP_OFFSET 1
#define ADF_GEN6_PVC1CTL_TCVCMAP_MASK GENMASK(7, 1)
#define ADF_GEN6_PVC1CTL_TCVCMAP_DEFAULT 0x40
#define ADF_GEN6_PVC1CTL_VCEN_OFFSET 31
#define ADF_GEN6_PVC1CTL_VCEN_MASK BIT(31)
/* RW bit: 0x1 - enables a Virtual Channel, 0x0 - disables */
#define ADF_GEN6_PVC1CTL_VCEN_ON 0x1
/* Error source mask registers */
#define ADF_GEN6_ERRMSK0 0x41A210
#define ADF_GEN6_ERRMSK1 0x41A214
#define ADF_GEN6_ERRMSK2 0x41A218
#define ADF_GEN6_ERRMSK3 0x41A21C
#define ADF_GEN6_VFLNOTIFY BIT(7)
/* Number of heartbeat counter pairs */
#define ADF_NUM_HB_CNT_PER_AE ADF_NUM_THREADS_PER_AE
/* Rate Limiting */
#define ADF_GEN6_RL_R2L_OFFSET 0x508000
#define ADF_GEN6_RL_L2C_OFFSET 0x509000
#define ADF_GEN6_RL_C2S_OFFSET 0x508818
#define ADF_GEN6_RL_TOKEN_PCIEIN_BUCKET_OFFSET 0x508800
#define ADF_GEN6_RL_TOKEN_PCIEOUT_BUCKET_OFFSET 0x508804
/* Physical function fuses */
#define ADF_6XXX_ACCELENGINES_MASK GENMASK(8, 0)
#define ADF_6XXX_ADMIN_AE_MASK GENMASK(8, 8)
/* Firmware binaries */
#define ADF_6XXX_FW "qat_6xxx.bin"
#define ADF_6XXX_MMP "qat_6xxx_mmp.bin"
#define ADF_6XXX_CY_OBJ "qat_6xxx_cy.bin"
#define ADF_6XXX_DC_OBJ "qat_6xxx_dc.bin"
#define ADF_6XXX_ADMIN_OBJ "qat_6xxx_admin.bin"
#define ADF_6XXX_WCY_OBJ "qat_6xxx_wcy.bin"
/* RL constants */
#define ADF_6XXX_RL_PCIE_SCALE_FACTOR_DIV 100
#define ADF_6XXX_RL_PCIE_SCALE_FACTOR_MUL 102
#define ADF_6XXX_RL_SCANS_PER_SEC 954
#define ADF_6XXX_RL_MAX_TP_ASYM 173750UL
#define ADF_6XXX_RL_MAX_TP_SYM 95000UL
#define ADF_6XXX_RL_MAX_TP_DC 40000UL
#define ADF_6XXX_RL_MAX_TP_DECOMP 40000UL
#define ADF_6XXX_RL_SLICE_REF 1000UL
/* Clock frequency */
#define ADF_6XXX_AE_FREQ (1000 * HZ_PER_MHZ)
/* KPT */
#define ADF_6XXX_KPT_MAX_SWK_COUNT_PER_FNPASID 128
#define ADF_6XXX_KPT_MAX_SWK_TTL 31536000
#define ADF_6XXX_KPT_DEFAULT_SWK_SHARED_MODE 1
#define ADF_6XXX_KPT_DEFAULT_SWK_TTL 0
#define ADF_6XXX_KPT_DEFAULT_SWK_CNT_PER_FN 0
#define ADF_6XXX_KPT_DEFAULT_SWK_CNT_PER_PASID 0
enum icp_qat_gen6_slice_mask {
ICP_ACCEL_GEN6_MASK_UCS_SLICE = BIT(0),
ICP_ACCEL_GEN6_MASK_AUTH_SLICE = BIT(1),
ICP_ACCEL_GEN6_MASK_PKE_SLICE = BIT(2),
ICP_ACCEL_GEN6_MASK_CPR_SLICE = BIT(3),
ICP_ACCEL_GEN6_MASK_DCPRZ_SLICE = BIT(4),
ICP_ACCEL_GEN6_MASK_EIA3_SLICE = BIT(5),
ICP_ACCEL_GEN6_MASK_WCP_WAT_SLICE = BIT(6),
ICP_ACCEL_GEN6_MASK_ZUC_256_SLICE = BIT(7),
ICP_ACCEL_GEN6_MASK_5G_SLICE = BIT(8),
};
/* Return true if the device is a wireless crypto (WCY) SKU */
static inline bool adf_6xxx_is_wcy(struct adf_hw_device_data *hw_data)
{
return !(hw_data->fuses[ADF_FUSECTL1] & ICP_ACCEL_GEN6_MASK_WCP_WAT_SLICE);
}
void adf_init_hw_data_6xxx(struct adf_hw_device_data *hw_data);
void adf_clean_hw_data_6xxx(struct adf_hw_device_data *hw_data);
#endif /* ADF_6XXX_HW_DATA_H_ */
@@ -0,0 +1,259 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2025 Intel Corporation */
#include <linux/array_size.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <adf_accel_devices.h>
#include <adf_cfg.h>
#include <adf_common_drv.h>
#include <adf_dbgfs.h>
#include <adf_sysfs_kpt.h>
#include "adf_gen6_shared.h"
#include "adf_6xxx_hw_data.h"
#include "adf_heartbeat.h"
static int bar_map[] = {
0, /* SRAM */
2, /* PMISC */
4, /* ETR */
};
static void adf_device_down(void *accel_dev)
{
adf_dev_down(accel_dev);
}
static void adf_dbgfs_cleanup(void *accel_dev)
{
adf_dbgfs_exit(accel_dev);
}
static void adf_cfg_device_remove(void *accel_dev)
{
adf_cfg_dev_remove(accel_dev);
}
static void adf_cleanup_hw_data(void *accel_dev)
{
struct adf_accel_dev *accel_device = accel_dev;
if (accel_device->hw_device) {
adf_clean_hw_data_6xxx(accel_device->hw_device);
accel_device->hw_device = NULL;
}
}
static void adf_devmgr_remove(void *accel_dev)
{
adf_devmgr_rm_dev(accel_dev, NULL);
}
static int adf_gen6_cfg_dev_init(struct adf_accel_dev *accel_dev)
{
const char *config;
int ret;
/*
* Wireless SKU - symmetric crypto service only
* Non-wireless SKU - crypto service for even devices and compression for odd devices
*/
if (adf_6xxx_is_wcy(GET_HW_DATA(accel_dev)))
config = ADF_CFG_SYM;
else
config = accel_dev->accel_id % 2 ? ADF_CFG_DC : ADF_CFG_CY;
ret = adf_cfg_section_add(accel_dev, ADF_GENERAL_SEC);
if (ret)
return ret;
ret = adf_cfg_add_key_value_param(accel_dev, ADF_GENERAL_SEC,
ADF_SERVICES_ENABLED, config,
ADF_STR);
if (ret)
return ret;
adf_heartbeat_save_cfg_param(accel_dev, ADF_CFG_HB_TIMER_MIN_MS);
return 0;
}
static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct adf_accel_pci *accel_pci_dev;
struct adf_hw_device_data *hw_data;
struct device *dev = &pdev->dev;
struct adf_accel_dev *accel_dev;
struct adf_bar *bar;
unsigned int i;
int ret;
if (num_possible_nodes() > 1 && dev_to_node(dev) < 0) {
/*
* If the accelerator is connected to a node with no memory
* there is no point in using the accelerator since the remote
* memory transaction will be very slow.
*/
return dev_err_probe(dev, -EINVAL, "Invalid NUMA configuration.\n");
}
accel_dev = devm_kzalloc(dev, sizeof(*accel_dev), GFP_KERNEL);
if (!accel_dev)
return -ENOMEM;
INIT_LIST_HEAD(&accel_dev->crypto_list);
INIT_LIST_HEAD(&accel_dev->list);
accel_pci_dev = &accel_dev->accel_pci_dev;
accel_pci_dev->pci_dev = pdev;
accel_dev->owner = THIS_MODULE;
hw_data = devm_kzalloc(dev, sizeof(*hw_data), GFP_KERNEL);
if (!hw_data)
return -ENOMEM;
pci_read_config_byte(pdev, PCI_REVISION_ID, &accel_pci_dev->revid);
pci_read_config_dword(pdev, ADF_GEN6_FUSECTL4_OFFSET, &hw_data->fuses[ADF_FUSECTL4]);
pci_read_config_dword(pdev, ADF_GEN6_FUSECTL0_OFFSET, &hw_data->fuses[ADF_FUSECTL0]);
pci_read_config_dword(pdev, ADF_GEN6_FUSECTL1_OFFSET, &hw_data->fuses[ADF_FUSECTL1]);
/* Enable PCI device */
ret = pcim_enable_device(pdev);
if (ret)
return dev_err_probe(dev, ret, "Cannot enable PCI device.\n");
ret = adf_devmgr_add_dev(accel_dev, NULL);
if (ret)
return dev_err_probe(dev, ret, "Failed to add new accelerator device.\n");
ret = devm_add_action_or_reset(dev, adf_devmgr_remove, accel_dev);
if (ret)
return ret;
accel_dev->hw_device = hw_data;
adf_init_hw_data_6xxx(accel_dev->hw_device);
ret = devm_add_action_or_reset(dev, adf_cleanup_hw_data, accel_dev);
if (ret)
return ret;
/* Get Accelerators and Accelerator Engine masks */
hw_data->accel_mask = hw_data->get_accel_mask(hw_data);
hw_data->ae_mask = hw_data->get_ae_mask(hw_data);
accel_pci_dev->sku = hw_data->get_sku(hw_data);
/* If the device has no acceleration engines then ignore it */
if (!hw_data->accel_mask || !hw_data->ae_mask ||
(~hw_data->ae_mask & ADF_GEN6_ACCELERATORS_MASK)) {
ret = -EFAULT;
return dev_err_probe(dev, ret, "No acceleration units were found.\n");
}
/* Create device configuration table */
ret = adf_cfg_dev_add(accel_dev);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, adf_cfg_device_remove, accel_dev);
if (ret)
return ret;
/* Set DMA identifier */
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
if (ret)
return dev_err_probe(dev, ret, "No usable DMA configuration.\n");
ret = adf_gen6_cfg_dev_init(accel_dev);
if (ret)
return dev_err_probe(dev, ret, "Failed to initialize configuration.\n");
/* Get accelerator capability mask */
hw_data->accel_capabilities_mask = hw_data->get_accel_cap(accel_dev);
if (!hw_data->accel_capabilities_mask) {
ret = -EINVAL;
return dev_err_probe(dev, ret, "Failed to get capabilities mask.\n");
}
for (i = 0; i < ARRAY_SIZE(bar_map); i++) {
bar = &accel_pci_dev->pci_bars[i];
/* Map 64-bit PCIe BAR */
bar->virt_addr = pcim_iomap_region(pdev, bar_map[i], pci_name(pdev));
if (IS_ERR(bar->virt_addr)) {
ret = PTR_ERR(bar->virt_addr);
return dev_err_probe(dev, ret, "Failed to ioremap PCI region.\n");
}
}
/*
* The PCI config space is saved at this point and will be restored
* after a Function Level Reset (FLR) as the FLR does not completely
* restore it.
*/
ret = pci_save_state(pdev);
if (ret)
return dev_err_probe(dev, ret, "Failed to save pci state.\n");
accel_dev->ras_errors.enabled = true;
adf_dbgfs_init(accel_dev);
ret = devm_add_action_or_reset(dev, adf_dbgfs_cleanup, accel_dev);
if (ret)
return ret;
ret = adf_dev_up(accel_dev, true);
if (ret) {
adf_dev_down(accel_dev);
return ret;
}
ret = devm_add_action_or_reset(dev, adf_device_down, accel_dev);
if (ret)
return ret;
ret = adf_sysfs_init(accel_dev);
if (ret)
return ret;
if (!(hw_data->fuses[ADF_FUSECTL0] & ADF_GEN6_KPT_FUSE_BIT))
ret = adf_sysfs_init_kpt(accel_dev);
return ret;
}
static void adf_shutdown(struct pci_dev *pdev)
{
struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev);
adf_dev_down(accel_dev);
}
static const struct pci_device_id adf_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_QAT_6XXX) },
{ }
};
MODULE_DEVICE_TABLE(pci, adf_pci_tbl);
static struct pci_driver adf_driver = {
.id_table = adf_pci_tbl,
.name = ADF_6XXX_DEVICE_NAME,
.probe = adf_probe,
.shutdown = adf_shutdown,
.sriov_configure = adf_sriov_configure,
.err_handler = &adf_err_handler,
};
module_pci_driver(adf_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Intel");
MODULE_FIRMWARE(ADF_6XXX_FW);
MODULE_FIRMWARE(ADF_6XXX_MMP);
MODULE_DESCRIPTION("Intel(R) QuickAssist Technology for GEN6 Devices");
MODULE_SOFTDEP("pre: crypto-intel_qat");
MODULE_IMPORT_NS("CRYPTO_QAT");
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_CRYPTO_DEV_QAT_C3XXX) += qat_c3xxx.o
qat_c3xxx-y := adf_drv.o adf_c3xxx_hw_data.o
@@ -0,0 +1,167 @@
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2014 - 2021 Intel Corporation */
#include <adf_accel_devices.h>
#include <adf_admin.h>
#include <adf_clock.h>
#include <adf_common_drv.h>
#include <adf_gen2_config.h>
#include <adf_gen2_hw_csr_data.h>
#include <adf_gen2_hw_data.h>
#include <adf_gen2_pfvf.h>
#include "adf_c3xxx_hw_data.h"
#include "adf_heartbeat.h"
#include "icp_qat_hw.h"
/* Worker thread to service arbiter mappings */
static const u32 thrd_to_arb_map[ADF_C3XXX_MAX_ACCELENGINES] = {
0x12222AAA, 0x11222AAA, 0x12222AAA,
0x11222AAA, 0x12222AAA, 0x11222AAA
};
static struct adf_hw_device_class c3xxx_class = {
.name = ADF_C3XXX_DEVICE_NAME,
.type = DEV_C3XXX,
};
static u32 get_accel_mask(struct adf_hw_device_data *self)
{
u32 fuses = self->fuses[ADF_FUSECTL0];
u32 straps = self->straps;
u32 accel;
accel = ~(fuses | straps) >> ADF_C3XXX_ACCELERATORS_REG_OFFSET;
accel &= ADF_C3XXX_ACCELERATORS_MASK;
return accel;
}
static u32 get_ae_mask(struct adf_hw_device_data *self)
{
u32 fuses = self->fuses[ADF_FUSECTL0];
u32 straps = self->straps;
unsigned long disabled;
u32 ae_disable;
int accel;
/* If an accel is disabled, then disable the corresponding two AEs */
disabled = ~get_accel_mask(self) & ADF_C3XXX_ACCELERATORS_MASK;
ae_disable = BIT(1) | BIT(0);
for_each_set_bit(accel, &disabled, ADF_C3XXX_MAX_ACCELERATORS)
straps |= ae_disable << (accel << 1);
return ~(fuses | straps) & ADF_C3XXX_ACCELENGINES_MASK;
}
static u32 get_ts_clock(struct adf_hw_device_data *self)
{
/*
* Timestamp update interval is 16 AE clock ticks for c3xxx.
*/
return self->clock_frequency / 16;
}
static int measure_clock(struct adf_accel_dev *accel_dev)
{
u32 frequency;
int ret;
ret = adf_dev_measure_clock(accel_dev, &frequency, ADF_C3XXX_MIN_AE_FREQ,
ADF_C3XXX_MAX_AE_FREQ);
if (ret)
return ret;
accel_dev->hw_device->clock_frequency = frequency;
return 0;
}
static u32 get_misc_bar_id(struct adf_hw_device_data *self)
{
return ADF_C3XXX_PMISC_BAR;
}
static u32 get_etr_bar_id(struct adf_hw_device_data *self)
{
return ADF_C3XXX_ETR_BAR;
}
static u32 get_sram_bar_id(struct adf_hw_device_data *self)
{
return ADF_C3XXX_SRAM_BAR;
}
static enum dev_sku_info get_sku(struct adf_hw_device_data *self)
{
int aes = self->get_num_aes(self);
if (aes == 6)
return DEV_SKU_4;
return DEV_SKU_UNKNOWN;
}
static const u32 *adf_get_arbiter_mapping(struct adf_accel_dev *accel_dev)
{
return thrd_to_arb_map;
}
static void configure_iov_threads(struct adf_accel_dev *accel_dev, bool enable)
{
adf_gen2_cfg_iov_thds(accel_dev, enable,
ADF_C3XXX_AE2FUNC_MAP_GRP_A_NUM_REGS,
ADF_C3XXX_AE2FUNC_MAP_GRP_B_NUM_REGS);
}
void adf_init_hw_data_c3xxx(struct adf_hw_device_data *hw_data)
{
hw_data->dev_class = &c3xxx_class;
hw_data->instance_id = c3xxx_class.instances++;
hw_data->num_banks = ADF_C3XXX_ETR_MAX_BANKS;
hw_data->num_rings_per_bank = ADF_ETR_MAX_RINGS_PER_BANK;
hw_data->num_accel = ADF_C3XXX_MAX_ACCELERATORS;
hw_data->num_logical_accel = 1;
hw_data->num_engines = ADF_C3XXX_MAX_ACCELENGINES;
hw_data->tx_rx_gap = ADF_GEN2_RX_RINGS_OFFSET;
hw_data->tx_rings_mask = ADF_GEN2_TX_RINGS_MASK;
hw_data->ring_to_svc_map = ADF_GEN2_DEFAULT_RING_TO_SRV_MAP;
hw_data->alloc_irq = adf_isr_resource_alloc;
hw_data->free_irq = adf_isr_resource_free;
hw_data->enable_error_correction = adf_gen2_enable_error_correction;
hw_data->get_accel_mask = get_accel_mask;
hw_data->get_ae_mask = get_ae_mask;
hw_data->get_accel_cap = adf_gen2_get_accel_cap;
hw_data->get_num_accels = adf_gen2_get_num_accels;
hw_data->get_num_aes = adf_gen2_get_num_aes;
hw_data->get_sram_bar_id = get_sram_bar_id;
hw_data->get_etr_bar_id = get_etr_bar_id;
hw_data->get_misc_bar_id = get_misc_bar_id;
hw_data->get_admin_info = adf_gen2_get_admin_info;
hw_data->get_arb_info = adf_gen2_get_arb_info;
hw_data->get_sku = get_sku;
hw_data->fw_name = ADF_C3XXX_FW;
hw_data->fw_mmp_name = ADF_C3XXX_MMP;
hw_data->init_admin_comms = adf_init_admin_comms;
hw_data->exit_admin_comms = adf_exit_admin_comms;
hw_data->configure_iov_threads = configure_iov_threads;
hw_data->send_admin_init = adf_send_admin_init;
hw_data->init_arb = adf_init_arb;
hw_data->exit_arb = adf_exit_arb;
hw_data->get_arb_mapping = adf_get_arbiter_mapping;
hw_data->enable_ints = adf_gen2_enable_ints;
hw_data->reset_device = adf_reset_flr;
hw_data->set_ssm_wdtimer = adf_gen2_set_ssm_wdtimer;
hw_data->disable_iov = adf_disable_sriov;
hw_data->dev_config = adf_gen2_dev_config;
hw_data->measure_clock = measure_clock;
hw_data->get_hb_clock = get_ts_clock;
hw_data->num_hb_ctrs = ADF_NUM_HB_CNT_PER_AE;
hw_data->check_hb_ctrs = adf_heartbeat_check_ctrs;
adf_gen2_init_pf_pfvf_ops(&hw_data->pfvf_ops);
adf_gen2_init_hw_csr_ops(&hw_data->csr_ops);
adf_gen2_init_dc_ops(&hw_data->dc_ops);
}
void adf_clean_hw_data_c3xxx(struct adf_hw_device_data *hw_data)
{
hw_data->dev_class->instances--;
}
@@ -0,0 +1,35 @@
/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */
/* Copyright(c) 2014 - 2020 Intel Corporation */
#ifndef ADF_C3XXX_HW_DATA_H_
#define ADF_C3XXX_HW_DATA_H_
#include <linux/units.h>
/* PCIe configuration space */
#define ADF_C3XXX_PMISC_BAR 0
#define ADF_C3XXX_ETR_BAR 1
#define ADF_C3XXX_SRAM_BAR 0
#define ADF_C3XXX_MAX_ACCELERATORS 3
#define ADF_C3XXX_MAX_ACCELENGINES 6
#define ADF_C3XXX_ACCELERATORS_REG_OFFSET 16
#define ADF_C3XXX_ACCELERATORS_MASK 0x7
#define ADF_C3XXX_ACCELENGINES_MASK 0x3F
#define ADF_C3XXX_ETR_MAX_BANKS 16
#define ADF_C3XXX_SOFTSTRAP_CSR_OFFSET 0x2EC
/* AE to function mapping */
#define ADF_C3XXX_AE2FUNC_MAP_GRP_A_NUM_REGS 48
#define ADF_C3XXX_AE2FUNC_MAP_GRP_B_NUM_REGS 6
/* Clocks frequency */
#define ADF_C3XXX_AE_FREQ (685 * HZ_PER_MHZ)
#define ADF_C3XXX_MIN_AE_FREQ (533 * HZ_PER_MHZ)
#define ADF_C3XXX_MAX_AE_FREQ (685 * HZ_PER_MHZ)
/* Firmware Binary */
#define ADF_C3XXX_FW "qat_c3xxx.bin"
#define ADF_C3XXX_MMP "qat_c3xxx_mmp.bin"
void adf_init_hw_data_c3xxx(struct adf_hw_device_data *hw_data);
void adf_clean_hw_data_c3xxx(struct adf_hw_device_data *hw_data);
#endif
@@ -0,0 +1,258 @@
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2014 - 2020 Intel Corporation */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/io.h>
#include <adf_accel_devices.h>
#include <adf_common_drv.h>
#include <adf_cfg.h>
#include <adf_dbgfs.h>
#include "adf_c3xxx_hw_data.h"
static void adf_cleanup_pci_dev(struct adf_accel_dev *accel_dev)
{
pci_release_regions(accel_dev->accel_pci_dev.pci_dev);
pci_disable_device(accel_dev->accel_pci_dev.pci_dev);
}
static void adf_cleanup_accel(struct adf_accel_dev *accel_dev)
{
struct adf_accel_pci *accel_pci_dev = &accel_dev->accel_pci_dev;
int i;
for (i = 0; i < ADF_PCI_MAX_BARS; i++) {
struct adf_bar *bar = &accel_pci_dev->pci_bars[i];
if (bar->virt_addr)
pci_iounmap(accel_pci_dev->pci_dev, bar->virt_addr);
}
if (accel_dev->hw_device) {
switch (accel_pci_dev->pci_dev->device) {
case PCI_DEVICE_ID_INTEL_QAT_C3XXX:
adf_clean_hw_data_c3xxx(accel_dev->hw_device);
break;
default:
break;
}
kfree(accel_dev->hw_device);
accel_dev->hw_device = NULL;
}
adf_dbgfs_exit(accel_dev);
adf_cfg_dev_remove(accel_dev);
adf_devmgr_rm_dev(accel_dev, NULL);
}
static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct adf_accel_dev *accel_dev;
struct adf_accel_pci *accel_pci_dev;
struct adf_hw_device_data *hw_data;
unsigned int i, bar_nr;
unsigned long bar_mask;
int ret;
switch (ent->device) {
case PCI_DEVICE_ID_INTEL_QAT_C3XXX:
break;
default:
dev_err(&pdev->dev, "Invalid device 0x%x.\n", ent->device);
return -ENODEV;
}
if (num_possible_nodes() > 1 && dev_to_node(&pdev->dev) < 0) {
/* If the accelerator is connected to a node with no memory
* there is no point in using the accelerator since the remote
* memory transaction will be very slow. */
dev_err(&pdev->dev, "Invalid NUMA configuration.\n");
return -EINVAL;
}
accel_dev = kzalloc_node(sizeof(*accel_dev), GFP_KERNEL,
dev_to_node(&pdev->dev));
if (!accel_dev)
return -ENOMEM;
INIT_LIST_HEAD(&accel_dev->crypto_list);
accel_pci_dev = &accel_dev->accel_pci_dev;
accel_pci_dev->pci_dev = pdev;
/* Add accel device to accel table.
* This should be called before adf_cleanup_accel is called */
if (adf_devmgr_add_dev(accel_dev, NULL)) {
dev_err(&pdev->dev, "Failed to add new accelerator device.\n");
kfree(accel_dev);
return -EFAULT;
}
accel_dev->owner = THIS_MODULE;
/* Allocate and configure device configuration structure */
hw_data = kzalloc_node(sizeof(*hw_data), GFP_KERNEL,
dev_to_node(&pdev->dev));
if (!hw_data) {
ret = -ENOMEM;
goto out_err;
}
accel_dev->hw_device = hw_data;
adf_init_hw_data_c3xxx(accel_dev->hw_device);
pci_read_config_byte(pdev, PCI_REVISION_ID, &accel_pci_dev->revid);
pci_read_config_dword(pdev, ADF_DEVICE_FUSECTL_OFFSET,
&hw_data->fuses[ADF_FUSECTL0]);
pci_read_config_dword(pdev, ADF_C3XXX_SOFTSTRAP_CSR_OFFSET,
&hw_data->straps);
/* Get Accelerators and Accelerators Engines masks */
hw_data->accel_mask = hw_data->get_accel_mask(hw_data);
hw_data->ae_mask = hw_data->get_ae_mask(hw_data);
accel_pci_dev->sku = hw_data->get_sku(hw_data);
/* If the device has no acceleration engines then ignore it. */
if (!hw_data->accel_mask || !hw_data->ae_mask ||
((~hw_data->ae_mask) & 0x01)) {
dev_err(&pdev->dev, "No acceleration units found");
ret = -EFAULT;
goto out_err;
}
/* Create device configuration table */
ret = adf_cfg_dev_add(accel_dev);
if (ret)
goto out_err;
/* enable PCI device */
if (pci_enable_device(pdev)) {
ret = -EFAULT;
goto out_err;
}
/* set dma identifier */
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(48));
if (ret) {
dev_err(&pdev->dev, "No usable DMA configuration\n");
goto out_err_disable;
}
if (pci_request_regions(pdev, ADF_C3XXX_DEVICE_NAME)) {
ret = -EFAULT;
goto out_err_disable;
}
/* Get accelerator capabilities mask */
hw_data->accel_capabilities_mask = hw_data->get_accel_cap(accel_dev);
/* Find and map all the device's BARS */
i = 0;
bar_mask = pci_select_bars(pdev, IORESOURCE_MEM);
for_each_set_bit(bar_nr, &bar_mask, ADF_PCI_MAX_BARS * 2) {
struct adf_bar *bar = &accel_pci_dev->pci_bars[i++];
bar->base_addr = pci_resource_start(pdev, bar_nr);
if (!bar->base_addr)
break;
bar->size = pci_resource_len(pdev, bar_nr);
bar->virt_addr = pci_iomap(accel_pci_dev->pci_dev, bar_nr, 0);
if (!bar->virt_addr) {
pci_err(pdev, "Failed to map BAR %d\n", bar_nr);
ret = -EFAULT;
goto out_err_free_reg;
}
}
if (pci_save_state(pdev)) {
pci_err(pdev, "Failed to save pci state\n");
ret = -ENOMEM;
goto out_err_free_reg;
}
adf_dbgfs_init(accel_dev);
ret = adf_dev_up(accel_dev, true);
if (ret)
goto out_err_dev_stop;
return ret;
out_err_dev_stop:
adf_dev_down(accel_dev);
out_err_free_reg:
pci_release_regions(accel_pci_dev->pci_dev);
out_err_disable:
pci_disable_device(accel_pci_dev->pci_dev);
out_err:
adf_cleanup_accel(accel_dev);
kfree(accel_dev);
return ret;
}
static void adf_remove(struct pci_dev *pdev)
{
struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev);
if (!accel_dev) {
pr_err("QAT: Driver removal failed\n");
return;
}
adf_dev_down(accel_dev);
adf_cleanup_accel(accel_dev);
adf_cleanup_pci_dev(accel_dev);
kfree(accel_dev);
}
static void adf_shutdown(struct pci_dev *pdev)
{
struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev);
adf_dev_down(accel_dev);
}
static const struct pci_device_id adf_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_QAT_C3XXX) },
{ }
};
MODULE_DEVICE_TABLE(pci, adf_pci_tbl);
static struct pci_driver adf_driver = {
.id_table = adf_pci_tbl,
.name = ADF_C3XXX_DEVICE_NAME,
.probe = adf_probe,
.remove = adf_remove,
.shutdown = adf_shutdown,
.sriov_configure = adf_sriov_configure,
.err_handler = &adf_err_handler,
};
static int __init adfdrv_init(void)
{
request_module("intel_qat");
if (pci_register_driver(&adf_driver)) {
pr_err("QAT: Driver initialization failed\n");
return -EFAULT;
}
return 0;
}
static void __exit adfdrv_release(void)
{
pci_unregister_driver(&adf_driver);
}
module_init(adfdrv_init);
module_exit(adfdrv_release);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Intel");
MODULE_FIRMWARE(ADF_C3XXX_FW);
MODULE_FIRMWARE(ADF_C3XXX_MMP);
MODULE_DESCRIPTION("Intel(R) QuickAssist Technology");
MODULE_IMPORT_NS("CRYPTO_QAT");
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_CRYPTO_DEV_QAT_C3XXXVF) += qat_c3xxxvf.o
qat_c3xxxvf-y := adf_drv.o adf_c3xxxvf_hw_data.o
@@ -0,0 +1,101 @@
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2015 - 2021 Intel Corporation */
#include <adf_accel_devices.h>
#include <adf_common_drv.h>
#include <adf_gen2_config.h>
#include <adf_gen2_hw_csr_data.h>
#include <adf_gen2_hw_data.h>
#include <adf_gen2_pfvf.h>
#include <adf_pfvf_vf_msg.h>
#include "adf_c3xxxvf_hw_data.h"
static struct adf_hw_device_class c3xxxiov_class = {
.name = ADF_C3XXXVF_DEVICE_NAME,
.type = DEV_C3XXXVF,
};
static u32 get_accel_mask(struct adf_hw_device_data *self)
{
return ADF_C3XXXIOV_ACCELERATORS_MASK;
}
static u32 get_ae_mask(struct adf_hw_device_data *self)
{
return ADF_C3XXXIOV_ACCELENGINES_MASK;
}
static u32 get_num_accels(struct adf_hw_device_data *self)
{
return ADF_C3XXXIOV_MAX_ACCELERATORS;
}
static u32 get_num_aes(struct adf_hw_device_data *self)
{
return ADF_C3XXXIOV_MAX_ACCELENGINES;
}
static u32 get_misc_bar_id(struct adf_hw_device_data *self)
{
return ADF_C3XXXIOV_PMISC_BAR;
}
static u32 get_etr_bar_id(struct adf_hw_device_data *self)
{
return ADF_C3XXXIOV_ETR_BAR;
}
static enum dev_sku_info get_sku(struct adf_hw_device_data *self)
{
return DEV_SKU_VF;
}
static int adf_vf_int_noop(struct adf_accel_dev *accel_dev)
{
return 0;
}
static void adf_vf_void_noop(struct adf_accel_dev *accel_dev)
{
}
void adf_init_hw_data_c3xxxiov(struct adf_hw_device_data *hw_data)
{
hw_data->dev_class = &c3xxxiov_class;
hw_data->num_banks = ADF_C3XXXIOV_ETR_MAX_BANKS;
hw_data->num_rings_per_bank = ADF_ETR_MAX_RINGS_PER_BANK;
hw_data->num_accel = ADF_C3XXXIOV_MAX_ACCELERATORS;
hw_data->num_logical_accel = 1;
hw_data->num_engines = ADF_C3XXXIOV_MAX_ACCELENGINES;
hw_data->tx_rx_gap = ADF_C3XXXIOV_RX_RINGS_OFFSET;
hw_data->tx_rings_mask = ADF_C3XXXIOV_TX_RINGS_MASK;
hw_data->ring_to_svc_map = ADF_GEN2_DEFAULT_RING_TO_SRV_MAP;
hw_data->alloc_irq = adf_vf_isr_resource_alloc;
hw_data->free_irq = adf_vf_isr_resource_free;
hw_data->enable_error_correction = adf_vf_void_noop;
hw_data->init_admin_comms = adf_vf_int_noop;
hw_data->exit_admin_comms = adf_vf_void_noop;
hw_data->send_admin_init = adf_vf2pf_notify_init;
hw_data->init_arb = adf_vf_int_noop;
hw_data->exit_arb = adf_vf_void_noop;
hw_data->disable_iov = adf_vf2pf_notify_shutdown;
hw_data->get_accel_mask = get_accel_mask;
hw_data->get_ae_mask = get_ae_mask;
hw_data->get_num_accels = get_num_accels;
hw_data->get_num_aes = get_num_aes;
hw_data->get_etr_bar_id = get_etr_bar_id;
hw_data->get_misc_bar_id = get_misc_bar_id;
hw_data->get_sku = get_sku;
hw_data->enable_ints = adf_vf_void_noop;
hw_data->dev_class->instances++;
hw_data->dev_config = adf_gen2_dev_config;
adf_devmgr_update_class_index(hw_data);
adf_gen2_init_vf_pfvf_ops(&hw_data->pfvf_ops);
adf_gen2_init_hw_csr_ops(&hw_data->csr_ops);
adf_gen2_init_dc_ops(&hw_data->dc_ops);
}
void adf_clean_hw_data_c3xxxiov(struct adf_hw_device_data *hw_data)
{
hw_data->dev_class->instances--;
adf_devmgr_update_class_index(hw_data);
}
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */
/* Copyright(c) 2015 - 2020 Intel Corporation */
#ifndef ADF_C3XXXVF_HW_DATA_H_
#define ADF_C3XXXVF_HW_DATA_H_
#define ADF_C3XXXIOV_PMISC_BAR 1
#define ADF_C3XXXIOV_ACCELERATORS_MASK 0x1
#define ADF_C3XXXIOV_ACCELENGINES_MASK 0x1
#define ADF_C3XXXIOV_MAX_ACCELERATORS 1
#define ADF_C3XXXIOV_MAX_ACCELENGINES 1
#define ADF_C3XXXIOV_RX_RINGS_OFFSET 8
#define ADF_C3XXXIOV_TX_RINGS_MASK 0xFF
#define ADF_C3XXXIOV_ETR_BAR 0
#define ADF_C3XXXIOV_ETR_MAX_BANKS 1
void adf_init_hw_data_c3xxxiov(struct adf_hw_device_data *hw_data);
void adf_clean_hw_data_c3xxxiov(struct adf_hw_device_data *hw_data);
#endif
@@ -0,0 +1,227 @@
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2014 - 2020 Intel Corporation */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/io.h>
#include <adf_accel_devices.h>
#include <adf_common_drv.h>
#include <adf_cfg.h>
#include <adf_dbgfs.h>
#include "adf_c3xxxvf_hw_data.h"
static const struct pci_device_id adf_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_QAT_C3XXX_VF), },
{ }
};
MODULE_DEVICE_TABLE(pci, adf_pci_tbl);
static int adf_probe(struct pci_dev *dev, const struct pci_device_id *ent);
static void adf_remove(struct pci_dev *dev);
static struct pci_driver adf_driver = {
.id_table = adf_pci_tbl,
.name = ADF_C3XXXVF_DEVICE_NAME,
.probe = adf_probe,
.remove = adf_remove,
};
static void adf_cleanup_pci_dev(struct adf_accel_dev *accel_dev)
{
pci_release_regions(accel_dev->accel_pci_dev.pci_dev);
pci_disable_device(accel_dev->accel_pci_dev.pci_dev);
}
static void adf_cleanup_accel(struct adf_accel_dev *accel_dev)
{
struct adf_accel_pci *accel_pci_dev = &accel_dev->accel_pci_dev;
struct adf_accel_dev *pf;
int i;
for (i = 0; i < ADF_PCI_MAX_BARS; i++) {
struct adf_bar *bar = &accel_pci_dev->pci_bars[i];
if (bar->virt_addr)
pci_iounmap(accel_pci_dev->pci_dev, bar->virt_addr);
}
if (accel_dev->hw_device) {
switch (accel_pci_dev->pci_dev->device) {
case PCI_DEVICE_ID_INTEL_QAT_C3XXX_VF:
adf_clean_hw_data_c3xxxiov(accel_dev->hw_device);
break;
default:
break;
}
kfree(accel_dev->hw_device);
accel_dev->hw_device = NULL;
}
adf_dbgfs_exit(accel_dev);
adf_cfg_dev_remove(accel_dev);
pf = adf_devmgr_pci_to_accel_dev(accel_pci_dev->pci_dev->physfn);
adf_devmgr_rm_dev(accel_dev, pf);
}
static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct adf_accel_dev *accel_dev;
struct adf_accel_dev *pf;
struct adf_accel_pci *accel_pci_dev;
struct adf_hw_device_data *hw_data;
unsigned int i, bar_nr;
unsigned long bar_mask;
int ret;
switch (ent->device) {
case PCI_DEVICE_ID_INTEL_QAT_C3XXX_VF:
break;
default:
dev_err(&pdev->dev, "Invalid device 0x%x.\n", ent->device);
return -ENODEV;
}
accel_dev = kzalloc_node(sizeof(*accel_dev), GFP_KERNEL,
dev_to_node(&pdev->dev));
if (!accel_dev)
return -ENOMEM;
accel_dev->is_vf = true;
pf = adf_devmgr_pci_to_accel_dev(pdev->physfn);
accel_pci_dev = &accel_dev->accel_pci_dev;
accel_pci_dev->pci_dev = pdev;
/* Add accel device to accel table */
if (adf_devmgr_add_dev(accel_dev, pf)) {
dev_err(&pdev->dev, "Failed to add new accelerator device.\n");
kfree(accel_dev);
return -EFAULT;
}
INIT_LIST_HEAD(&accel_dev->crypto_list);
accel_dev->owner = THIS_MODULE;
/* Allocate and configure device configuration structure */
hw_data = kzalloc_node(sizeof(*hw_data), GFP_KERNEL,
dev_to_node(&pdev->dev));
if (!hw_data) {
ret = -ENOMEM;
goto out_err;
}
accel_dev->hw_device = hw_data;
adf_init_hw_data_c3xxxiov(accel_dev->hw_device);
/* Get Accelerators and Accelerators Engines masks */
hw_data->accel_mask = hw_data->get_accel_mask(hw_data);
hw_data->ae_mask = hw_data->get_ae_mask(hw_data);
accel_pci_dev->sku = hw_data->get_sku(hw_data);
/* Create device configuration table */
ret = adf_cfg_dev_add(accel_dev);
if (ret)
goto out_err;
/* enable PCI device */
if (pci_enable_device(pdev)) {
ret = -EFAULT;
goto out_err;
}
/* set dma identifier */
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(48));
if (ret) {
dev_err(&pdev->dev, "No usable DMA configuration\n");
goto out_err_disable;
}
if (pci_request_regions(pdev, ADF_C3XXXVF_DEVICE_NAME)) {
ret = -EFAULT;
goto out_err_disable;
}
/* Find and map all the device's BARS */
i = 0;
bar_mask = pci_select_bars(pdev, IORESOURCE_MEM);
for_each_set_bit(bar_nr, &bar_mask, ADF_PCI_MAX_BARS * 2) {
struct adf_bar *bar = &accel_pci_dev->pci_bars[i++];
bar->base_addr = pci_resource_start(pdev, bar_nr);
if (!bar->base_addr)
break;
bar->size = pci_resource_len(pdev, bar_nr);
bar->virt_addr = pci_iomap(accel_pci_dev->pci_dev, bar_nr, 0);
if (!bar->virt_addr) {
pci_err(pdev, "Failed to map BAR %d\n", bar_nr);
ret = -EFAULT;
goto out_err_free_reg;
}
}
/* Completion for VF2PF request/response message exchange */
init_completion(&accel_dev->vf.msg_received);
adf_dbgfs_init(accel_dev);
ret = adf_dev_up(accel_dev, false);
if (ret)
goto out_err_dev_stop;
return ret;
out_err_dev_stop:
adf_dev_down(accel_dev);
out_err_free_reg:
pci_release_regions(accel_pci_dev->pci_dev);
out_err_disable:
pci_disable_device(accel_pci_dev->pci_dev);
out_err:
adf_cleanup_accel(accel_dev);
kfree(accel_dev);
return ret;
}
static void adf_remove(struct pci_dev *pdev)
{
struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev);
if (!accel_dev) {
pr_err("QAT: Driver removal failed\n");
return;
}
adf_flush_vf_wq(accel_dev);
adf_dev_down(accel_dev);
adf_cleanup_accel(accel_dev);
adf_cleanup_pci_dev(accel_dev);
kfree(accel_dev);
}
static int __init adfdrv_init(void)
{
request_module("intel_qat");
if (pci_register_driver(&adf_driver)) {
pr_err("QAT: Driver initialization failed\n");
return -EFAULT;
}
return 0;
}
static void __exit adfdrv_release(void)
{
pci_unregister_driver(&adf_driver);
adf_clean_vf_map(true);
}
module_init(adfdrv_init);
module_exit(adfdrv_release);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Intel");
MODULE_DESCRIPTION("Intel(R) QuickAssist Technology");
MODULE_IMPORT_NS("CRYPTO_QAT");
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_CRYPTO_DEV_QAT_C62X) += qat_c62x.o
qat_c62x-y := adf_drv.o adf_c62x_hw_data.o
@@ -0,0 +1,169 @@
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2014 - 2021 Intel Corporation */
#include <adf_accel_devices.h>
#include <adf_admin.h>
#include <adf_clock.h>
#include <adf_common_drv.h>
#include <adf_gen2_config.h>
#include <adf_gen2_hw_csr_data.h>
#include <adf_gen2_hw_data.h>
#include <adf_gen2_pfvf.h>
#include "adf_c62x_hw_data.h"
#include "adf_heartbeat.h"
#include "icp_qat_hw.h"
/* Worker thread to service arbiter mappings */
static const u32 thrd_to_arb_map[ADF_C62X_MAX_ACCELENGINES] = {
0x12222AAA, 0x11222AAA, 0x12222AAA, 0x11222AAA, 0x12222AAA,
0x11222AAA, 0x12222AAA, 0x11222AAA, 0x12222AAA, 0x11222AAA
};
static struct adf_hw_device_class c62x_class = {
.name = ADF_C62X_DEVICE_NAME,
.type = DEV_C62X,
};
static u32 get_accel_mask(struct adf_hw_device_data *self)
{
u32 fuses = self->fuses[ADF_FUSECTL0];
u32 straps = self->straps;
u32 accel;
accel = ~(fuses | straps) >> ADF_C62X_ACCELERATORS_REG_OFFSET;
accel &= ADF_C62X_ACCELERATORS_MASK;
return accel;
}
static u32 get_ae_mask(struct adf_hw_device_data *self)
{
u32 fuses = self->fuses[ADF_FUSECTL0];
u32 straps = self->straps;
unsigned long disabled;
u32 ae_disable;
int accel;
/* If an accel is disabled, then disable the corresponding two AEs */
disabled = ~get_accel_mask(self) & ADF_C62X_ACCELERATORS_MASK;
ae_disable = BIT(1) | BIT(0);
for_each_set_bit(accel, &disabled, ADF_C62X_MAX_ACCELERATORS)
straps |= ae_disable << (accel << 1);
return ~(fuses | straps) & ADF_C62X_ACCELENGINES_MASK;
}
static u32 get_ts_clock(struct adf_hw_device_data *self)
{
/*
* Timestamp update interval is 16 AE clock ticks for c62x.
*/
return self->clock_frequency / 16;
}
static int measure_clock(struct adf_accel_dev *accel_dev)
{
u32 frequency;
int ret;
ret = adf_dev_measure_clock(accel_dev, &frequency, ADF_C62X_MIN_AE_FREQ,
ADF_C62X_MAX_AE_FREQ);
if (ret)
return ret;
accel_dev->hw_device->clock_frequency = frequency;
return 0;
}
static u32 get_misc_bar_id(struct adf_hw_device_data *self)
{
return ADF_C62X_PMISC_BAR;
}
static u32 get_etr_bar_id(struct adf_hw_device_data *self)
{
return ADF_C62X_ETR_BAR;
}
static u32 get_sram_bar_id(struct adf_hw_device_data *self)
{
return ADF_C62X_SRAM_BAR;
}
static enum dev_sku_info get_sku(struct adf_hw_device_data *self)
{
int aes = self->get_num_aes(self);
if (aes == 8)
return DEV_SKU_2;
else if (aes == 10)
return DEV_SKU_4;
return DEV_SKU_UNKNOWN;
}
static const u32 *adf_get_arbiter_mapping(struct adf_accel_dev *accel_dev)
{
return thrd_to_arb_map;
}
static void configure_iov_threads(struct adf_accel_dev *accel_dev, bool enable)
{
adf_gen2_cfg_iov_thds(accel_dev, enable,
ADF_C62X_AE2FUNC_MAP_GRP_A_NUM_REGS,
ADF_C62X_AE2FUNC_MAP_GRP_B_NUM_REGS);
}
void adf_init_hw_data_c62x(struct adf_hw_device_data *hw_data)
{
hw_data->dev_class = &c62x_class;
hw_data->instance_id = c62x_class.instances++;
hw_data->num_banks = ADF_C62X_ETR_MAX_BANKS;
hw_data->num_rings_per_bank = ADF_ETR_MAX_RINGS_PER_BANK;
hw_data->num_accel = ADF_C62X_MAX_ACCELERATORS;
hw_data->num_logical_accel = 1;
hw_data->num_engines = ADF_C62X_MAX_ACCELENGINES;
hw_data->tx_rx_gap = ADF_GEN2_RX_RINGS_OFFSET;
hw_data->tx_rings_mask = ADF_GEN2_TX_RINGS_MASK;
hw_data->ring_to_svc_map = ADF_GEN2_DEFAULT_RING_TO_SRV_MAP;
hw_data->alloc_irq = adf_isr_resource_alloc;
hw_data->free_irq = adf_isr_resource_free;
hw_data->enable_error_correction = adf_gen2_enable_error_correction;
hw_data->get_accel_mask = get_accel_mask;
hw_data->get_ae_mask = get_ae_mask;
hw_data->get_accel_cap = adf_gen2_get_accel_cap;
hw_data->get_num_accels = adf_gen2_get_num_accels;
hw_data->get_num_aes = adf_gen2_get_num_aes;
hw_data->get_sram_bar_id = get_sram_bar_id;
hw_data->get_etr_bar_id = get_etr_bar_id;
hw_data->get_misc_bar_id = get_misc_bar_id;
hw_data->get_admin_info = adf_gen2_get_admin_info;
hw_data->get_arb_info = adf_gen2_get_arb_info;
hw_data->get_sku = get_sku;
hw_data->fw_name = ADF_C62X_FW;
hw_data->fw_mmp_name = ADF_C62X_MMP;
hw_data->init_admin_comms = adf_init_admin_comms;
hw_data->exit_admin_comms = adf_exit_admin_comms;
hw_data->configure_iov_threads = configure_iov_threads;
hw_data->send_admin_init = adf_send_admin_init;
hw_data->init_arb = adf_init_arb;
hw_data->exit_arb = adf_exit_arb;
hw_data->get_arb_mapping = adf_get_arbiter_mapping;
hw_data->enable_ints = adf_gen2_enable_ints;
hw_data->reset_device = adf_reset_flr;
hw_data->set_ssm_wdtimer = adf_gen2_set_ssm_wdtimer;
hw_data->disable_iov = adf_disable_sriov;
hw_data->dev_config = adf_gen2_dev_config;
hw_data->measure_clock = measure_clock;
hw_data->get_hb_clock = get_ts_clock;
hw_data->num_hb_ctrs = ADF_NUM_HB_CNT_PER_AE;
hw_data->check_hb_ctrs = adf_heartbeat_check_ctrs;
adf_gen2_init_pf_pfvf_ops(&hw_data->pfvf_ops);
adf_gen2_init_hw_csr_ops(&hw_data->csr_ops);
adf_gen2_init_dc_ops(&hw_data->dc_ops);
}
void adf_clean_hw_data_c62x(struct adf_hw_device_data *hw_data)
{
hw_data->dev_class->instances--;
}
@@ -0,0 +1,35 @@
/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */
/* Copyright(c) 2014 - 2020 Intel Corporation */
#ifndef ADF_C62X_HW_DATA_H_
#define ADF_C62X_HW_DATA_H_
#include <linux/units.h>
/* PCIe configuration space */
#define ADF_C62X_SRAM_BAR 0
#define ADF_C62X_PMISC_BAR 1
#define ADF_C62X_ETR_BAR 2
#define ADF_C62X_MAX_ACCELERATORS 5
#define ADF_C62X_MAX_ACCELENGINES 10
#define ADF_C62X_ACCELERATORS_REG_OFFSET 16
#define ADF_C62X_ACCELERATORS_MASK 0x1F
#define ADF_C62X_ACCELENGINES_MASK 0x3FF
#define ADF_C62X_ETR_MAX_BANKS 16
#define ADF_C62X_SOFTSTRAP_CSR_OFFSET 0x2EC
/* AE to function mapping */
#define ADF_C62X_AE2FUNC_MAP_GRP_A_NUM_REGS 80
#define ADF_C62X_AE2FUNC_MAP_GRP_B_NUM_REGS 10
/* Clocks frequency */
#define ADF_C62X_AE_FREQ (685 * HZ_PER_MHZ)
#define ADF_C62X_MIN_AE_FREQ (533 * HZ_PER_MHZ)
#define ADF_C62X_MAX_AE_FREQ (800 * HZ_PER_MHZ)
/* Firmware Binary */
#define ADF_C62X_FW "qat_c62x.bin"
#define ADF_C62X_MMP "qat_c62x_mmp.bin"
void adf_init_hw_data_c62x(struct adf_hw_device_data *hw_data);
void adf_clean_hw_data_c62x(struct adf_hw_device_data *hw_data);
#endif
@@ -0,0 +1,258 @@
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2014 - 2020 Intel Corporation */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/io.h>
#include <adf_accel_devices.h>
#include <adf_common_drv.h>
#include <adf_cfg.h>
#include <adf_dbgfs.h>
#include "adf_c62x_hw_data.h"
static void adf_cleanup_pci_dev(struct adf_accel_dev *accel_dev)
{
pci_release_regions(accel_dev->accel_pci_dev.pci_dev);
pci_disable_device(accel_dev->accel_pci_dev.pci_dev);
}
static void adf_cleanup_accel(struct adf_accel_dev *accel_dev)
{
struct adf_accel_pci *accel_pci_dev = &accel_dev->accel_pci_dev;
int i;
for (i = 0; i < ADF_PCI_MAX_BARS; i++) {
struct adf_bar *bar = &accel_pci_dev->pci_bars[i];
if (bar->virt_addr)
pci_iounmap(accel_pci_dev->pci_dev, bar->virt_addr);
}
if (accel_dev->hw_device) {
switch (accel_pci_dev->pci_dev->device) {
case PCI_DEVICE_ID_INTEL_QAT_C62X:
adf_clean_hw_data_c62x(accel_dev->hw_device);
break;
default:
break;
}
kfree(accel_dev->hw_device);
accel_dev->hw_device = NULL;
}
adf_dbgfs_exit(accel_dev);
adf_cfg_dev_remove(accel_dev);
adf_devmgr_rm_dev(accel_dev, NULL);
}
static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct adf_accel_dev *accel_dev;
struct adf_accel_pci *accel_pci_dev;
struct adf_hw_device_data *hw_data;
unsigned int i, bar_nr;
unsigned long bar_mask;
int ret;
switch (ent->device) {
case PCI_DEVICE_ID_INTEL_QAT_C62X:
break;
default:
dev_err(&pdev->dev, "Invalid device 0x%x.\n", ent->device);
return -ENODEV;
}
if (num_possible_nodes() > 1 && dev_to_node(&pdev->dev) < 0) {
/* If the accelerator is connected to a node with no memory
* there is no point in using the accelerator since the remote
* memory transaction will be very slow. */
dev_err(&pdev->dev, "Invalid NUMA configuration.\n");
return -EINVAL;
}
accel_dev = kzalloc_node(sizeof(*accel_dev), GFP_KERNEL,
dev_to_node(&pdev->dev));
if (!accel_dev)
return -ENOMEM;
INIT_LIST_HEAD(&accel_dev->crypto_list);
accel_pci_dev = &accel_dev->accel_pci_dev;
accel_pci_dev->pci_dev = pdev;
/* Add accel device to accel table.
* This should be called before adf_cleanup_accel is called */
if (adf_devmgr_add_dev(accel_dev, NULL)) {
dev_err(&pdev->dev, "Failed to add new accelerator device.\n");
kfree(accel_dev);
return -EFAULT;
}
accel_dev->owner = THIS_MODULE;
/* Allocate and configure device configuration structure */
hw_data = kzalloc_node(sizeof(*hw_data), GFP_KERNEL,
dev_to_node(&pdev->dev));
if (!hw_data) {
ret = -ENOMEM;
goto out_err;
}
accel_dev->hw_device = hw_data;
adf_init_hw_data_c62x(accel_dev->hw_device);
pci_read_config_byte(pdev, PCI_REVISION_ID, &accel_pci_dev->revid);
pci_read_config_dword(pdev, ADF_DEVICE_FUSECTL_OFFSET,
&hw_data->fuses[ADF_FUSECTL0]);
pci_read_config_dword(pdev, ADF_C62X_SOFTSTRAP_CSR_OFFSET,
&hw_data->straps);
/* Get Accelerators and Accelerators Engines masks */
hw_data->accel_mask = hw_data->get_accel_mask(hw_data);
hw_data->ae_mask = hw_data->get_ae_mask(hw_data);
accel_pci_dev->sku = hw_data->get_sku(hw_data);
/* If the device has no acceleration engines then ignore it. */
if (!hw_data->accel_mask || !hw_data->ae_mask ||
((~hw_data->ae_mask) & 0x01)) {
dev_err(&pdev->dev, "No acceleration units found");
ret = -EFAULT;
goto out_err;
}
/* Create device configuration table */
ret = adf_cfg_dev_add(accel_dev);
if (ret)
goto out_err;
/* enable PCI device */
if (pci_enable_device(pdev)) {
ret = -EFAULT;
goto out_err;
}
/* set dma identifier */
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(48));
if (ret) {
dev_err(&pdev->dev, "No usable DMA configuration\n");
goto out_err_disable;
}
if (pci_request_regions(pdev, ADF_C62X_DEVICE_NAME)) {
ret = -EFAULT;
goto out_err_disable;
}
/* Get accelerator capabilities mask */
hw_data->accel_capabilities_mask = hw_data->get_accel_cap(accel_dev);
/* Find and map all the device's BARS */
i = (hw_data->fuses[ADF_FUSECTL0] & ADF_DEVICE_FUSECTL_MASK) ? 1 : 0;
bar_mask = pci_select_bars(pdev, IORESOURCE_MEM);
for_each_set_bit(bar_nr, &bar_mask, ADF_PCI_MAX_BARS * 2) {
struct adf_bar *bar = &accel_pci_dev->pci_bars[i++];
bar->base_addr = pci_resource_start(pdev, bar_nr);
if (!bar->base_addr)
break;
bar->size = pci_resource_len(pdev, bar_nr);
bar->virt_addr = pci_iomap(accel_pci_dev->pci_dev, bar_nr, 0);
if (!bar->virt_addr) {
pci_err(pdev, "Failed to map BAR %d\n", bar_nr);
ret = -EFAULT;
goto out_err_free_reg;
}
}
if (pci_save_state(pdev)) {
pci_err(pdev, "Failed to save pci state\n");
ret = -ENOMEM;
goto out_err_free_reg;
}
adf_dbgfs_init(accel_dev);
ret = adf_dev_up(accel_dev, true);
if (ret)
goto out_err_dev_stop;
return ret;
out_err_dev_stop:
adf_dev_down(accel_dev);
out_err_free_reg:
pci_release_regions(accel_pci_dev->pci_dev);
out_err_disable:
pci_disable_device(accel_pci_dev->pci_dev);
out_err:
adf_cleanup_accel(accel_dev);
kfree(accel_dev);
return ret;
}
static void adf_remove(struct pci_dev *pdev)
{
struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev);
if (!accel_dev) {
pr_err("QAT: Driver removal failed\n");
return;
}
adf_dev_down(accel_dev);
adf_cleanup_accel(accel_dev);
adf_cleanup_pci_dev(accel_dev);
kfree(accel_dev);
}
static void adf_shutdown(struct pci_dev *pdev)
{
struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev);
adf_dev_down(accel_dev);
}
static const struct pci_device_id adf_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_QAT_C62X) },
{ }
};
MODULE_DEVICE_TABLE(pci, adf_pci_tbl);
static struct pci_driver adf_driver = {
.id_table = adf_pci_tbl,
.name = ADF_C62X_DEVICE_NAME,
.probe = adf_probe,
.remove = adf_remove,
.shutdown = adf_shutdown,
.sriov_configure = adf_sriov_configure,
.err_handler = &adf_err_handler,
};
static int __init adfdrv_init(void)
{
request_module("intel_qat");
if (pci_register_driver(&adf_driver)) {
pr_err("QAT: Driver initialization failed\n");
return -EFAULT;
}
return 0;
}
static void __exit adfdrv_release(void)
{
pci_unregister_driver(&adf_driver);
}
module_init(adfdrv_init);
module_exit(adfdrv_release);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Intel");
MODULE_FIRMWARE(ADF_C62X_FW);
MODULE_FIRMWARE(ADF_C62X_MMP);
MODULE_DESCRIPTION("Intel(R) QuickAssist Technology");
MODULE_IMPORT_NS("CRYPTO_QAT");
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_CRYPTO_DEV_QAT_C62XVF) += qat_c62xvf.o
qat_c62xvf-y := adf_drv.o adf_c62xvf_hw_data.o
@@ -0,0 +1,101 @@
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2015 - 2021 Intel Corporation */
#include <adf_accel_devices.h>
#include <adf_common_drv.h>
#include <adf_gen2_config.h>
#include <adf_gen2_hw_csr_data.h>
#include <adf_gen2_hw_data.h>
#include <adf_gen2_pfvf.h>
#include <adf_pfvf_vf_msg.h>
#include "adf_c62xvf_hw_data.h"
static struct adf_hw_device_class c62xiov_class = {
.name = ADF_C62XVF_DEVICE_NAME,
.type = DEV_C62XVF,
};
static u32 get_accel_mask(struct adf_hw_device_data *self)
{
return ADF_C62XIOV_ACCELERATORS_MASK;
}
static u32 get_ae_mask(struct adf_hw_device_data *self)
{
return ADF_C62XIOV_ACCELENGINES_MASK;
}
static u32 get_num_accels(struct adf_hw_device_data *self)
{
return ADF_C62XIOV_MAX_ACCELERATORS;
}
static u32 get_num_aes(struct adf_hw_device_data *self)
{
return ADF_C62XIOV_MAX_ACCELENGINES;
}
static u32 get_misc_bar_id(struct adf_hw_device_data *self)
{
return ADF_C62XIOV_PMISC_BAR;
}
static u32 get_etr_bar_id(struct adf_hw_device_data *self)
{
return ADF_C62XIOV_ETR_BAR;
}
static enum dev_sku_info get_sku(struct adf_hw_device_data *self)
{
return DEV_SKU_VF;
}
static int adf_vf_int_noop(struct adf_accel_dev *accel_dev)
{
return 0;
}
static void adf_vf_void_noop(struct adf_accel_dev *accel_dev)
{
}
void adf_init_hw_data_c62xiov(struct adf_hw_device_data *hw_data)
{
hw_data->dev_class = &c62xiov_class;
hw_data->num_banks = ADF_C62XIOV_ETR_MAX_BANKS;
hw_data->num_rings_per_bank = ADF_ETR_MAX_RINGS_PER_BANK;
hw_data->num_accel = ADF_C62XIOV_MAX_ACCELERATORS;
hw_data->num_logical_accel = 1;
hw_data->num_engines = ADF_C62XIOV_MAX_ACCELENGINES;
hw_data->tx_rx_gap = ADF_C62XIOV_RX_RINGS_OFFSET;
hw_data->tx_rings_mask = ADF_C62XIOV_TX_RINGS_MASK;
hw_data->ring_to_svc_map = ADF_GEN2_DEFAULT_RING_TO_SRV_MAP;
hw_data->alloc_irq = adf_vf_isr_resource_alloc;
hw_data->free_irq = adf_vf_isr_resource_free;
hw_data->enable_error_correction = adf_vf_void_noop;
hw_data->init_admin_comms = adf_vf_int_noop;
hw_data->exit_admin_comms = adf_vf_void_noop;
hw_data->send_admin_init = adf_vf2pf_notify_init;
hw_data->init_arb = adf_vf_int_noop;
hw_data->exit_arb = adf_vf_void_noop;
hw_data->disable_iov = adf_vf2pf_notify_shutdown;
hw_data->get_accel_mask = get_accel_mask;
hw_data->get_ae_mask = get_ae_mask;
hw_data->get_num_accels = get_num_accels;
hw_data->get_num_aes = get_num_aes;
hw_data->get_etr_bar_id = get_etr_bar_id;
hw_data->get_misc_bar_id = get_misc_bar_id;
hw_data->get_sku = get_sku;
hw_data->enable_ints = adf_vf_void_noop;
hw_data->dev_class->instances++;
hw_data->dev_config = adf_gen2_dev_config;
adf_devmgr_update_class_index(hw_data);
adf_gen2_init_vf_pfvf_ops(&hw_data->pfvf_ops);
adf_gen2_init_hw_csr_ops(&hw_data->csr_ops);
adf_gen2_init_dc_ops(&hw_data->dc_ops);
}
void adf_clean_hw_data_c62xiov(struct adf_hw_device_data *hw_data)
{
hw_data->dev_class->instances--;
adf_devmgr_update_class_index(hw_data);
}
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */
/* Copyright(c) 2015 - 2020 Intel Corporation */
#ifndef ADF_C62XVF_HW_DATA_H_
#define ADF_C62XVF_HW_DATA_H_
#define ADF_C62XIOV_PMISC_BAR 1
#define ADF_C62XIOV_ACCELERATORS_MASK 0x1
#define ADF_C62XIOV_ACCELENGINES_MASK 0x1
#define ADF_C62XIOV_MAX_ACCELERATORS 1
#define ADF_C62XIOV_MAX_ACCELENGINES 1
#define ADF_C62XIOV_RX_RINGS_OFFSET 8
#define ADF_C62XIOV_TX_RINGS_MASK 0xFF
#define ADF_C62XIOV_ETR_BAR 0
#define ADF_C62XIOV_ETR_MAX_BANKS 1
void adf_init_hw_data_c62xiov(struct adf_hw_device_data *hw_data);
void adf_clean_hw_data_c62xiov(struct adf_hw_device_data *hw_data);
#endif
@@ -0,0 +1,227 @@
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2014 - 2020 Intel Corporation */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/io.h>
#include <adf_accel_devices.h>
#include <adf_common_drv.h>
#include <adf_cfg.h>
#include <adf_dbgfs.h>
#include "adf_c62xvf_hw_data.h"
static const struct pci_device_id adf_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_QAT_C62X_VF), },
{ }
};
MODULE_DEVICE_TABLE(pci, adf_pci_tbl);
static int adf_probe(struct pci_dev *dev, const struct pci_device_id *ent);
static void adf_remove(struct pci_dev *dev);
static struct pci_driver adf_driver = {
.id_table = adf_pci_tbl,
.name = ADF_C62XVF_DEVICE_NAME,
.probe = adf_probe,
.remove = adf_remove,
};
static void adf_cleanup_pci_dev(struct adf_accel_dev *accel_dev)
{
pci_release_regions(accel_dev->accel_pci_dev.pci_dev);
pci_disable_device(accel_dev->accel_pci_dev.pci_dev);
}
static void adf_cleanup_accel(struct adf_accel_dev *accel_dev)
{
struct adf_accel_pci *accel_pci_dev = &accel_dev->accel_pci_dev;
struct adf_accel_dev *pf;
int i;
for (i = 0; i < ADF_PCI_MAX_BARS; i++) {
struct adf_bar *bar = &accel_pci_dev->pci_bars[i];
if (bar->virt_addr)
pci_iounmap(accel_pci_dev->pci_dev, bar->virt_addr);
}
if (accel_dev->hw_device) {
switch (accel_pci_dev->pci_dev->device) {
case PCI_DEVICE_ID_INTEL_QAT_C62X_VF:
adf_clean_hw_data_c62xiov(accel_dev->hw_device);
break;
default:
break;
}
kfree(accel_dev->hw_device);
accel_dev->hw_device = NULL;
}
adf_dbgfs_exit(accel_dev);
adf_cfg_dev_remove(accel_dev);
pf = adf_devmgr_pci_to_accel_dev(accel_pci_dev->pci_dev->physfn);
adf_devmgr_rm_dev(accel_dev, pf);
}
static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct adf_accel_dev *accel_dev;
struct adf_accel_dev *pf;
struct adf_accel_pci *accel_pci_dev;
struct adf_hw_device_data *hw_data;
unsigned int i, bar_nr;
unsigned long bar_mask;
int ret;
switch (ent->device) {
case PCI_DEVICE_ID_INTEL_QAT_C62X_VF:
break;
default:
dev_err(&pdev->dev, "Invalid device 0x%x.\n", ent->device);
return -ENODEV;
}
accel_dev = kzalloc_node(sizeof(*accel_dev), GFP_KERNEL,
dev_to_node(&pdev->dev));
if (!accel_dev)
return -ENOMEM;
accel_dev->is_vf = true;
pf = adf_devmgr_pci_to_accel_dev(pdev->physfn);
accel_pci_dev = &accel_dev->accel_pci_dev;
accel_pci_dev->pci_dev = pdev;
/* Add accel device to accel table */
if (adf_devmgr_add_dev(accel_dev, pf)) {
dev_err(&pdev->dev, "Failed to add new accelerator device.\n");
kfree(accel_dev);
return -EFAULT;
}
INIT_LIST_HEAD(&accel_dev->crypto_list);
accel_dev->owner = THIS_MODULE;
/* Allocate and configure device configuration structure */
hw_data = kzalloc_node(sizeof(*hw_data), GFP_KERNEL,
dev_to_node(&pdev->dev));
if (!hw_data) {
ret = -ENOMEM;
goto out_err;
}
accel_dev->hw_device = hw_data;
adf_init_hw_data_c62xiov(accel_dev->hw_device);
/* Get Accelerators and Accelerators Engines masks */
hw_data->accel_mask = hw_data->get_accel_mask(hw_data);
hw_data->ae_mask = hw_data->get_ae_mask(hw_data);
accel_pci_dev->sku = hw_data->get_sku(hw_data);
/* Create device configuration table */
ret = adf_cfg_dev_add(accel_dev);
if (ret)
goto out_err;
/* enable PCI device */
if (pci_enable_device(pdev)) {
ret = -EFAULT;
goto out_err;
}
/* set dma identifier */
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(48));
if (ret) {
dev_err(&pdev->dev, "No usable DMA configuration\n");
goto out_err_disable;
}
if (pci_request_regions(pdev, ADF_C62XVF_DEVICE_NAME)) {
ret = -EFAULT;
goto out_err_disable;
}
/* Find and map all the device's BARS */
i = 0;
bar_mask = pci_select_bars(pdev, IORESOURCE_MEM);
for_each_set_bit(bar_nr, &bar_mask, ADF_PCI_MAX_BARS * 2) {
struct adf_bar *bar = &accel_pci_dev->pci_bars[i++];
bar->base_addr = pci_resource_start(pdev, bar_nr);
if (!bar->base_addr)
break;
bar->size = pci_resource_len(pdev, bar_nr);
bar->virt_addr = pci_iomap(accel_pci_dev->pci_dev, bar_nr, 0);
if (!bar->virt_addr) {
pci_err(pdev, "Failed to map BAR %d\n", bar_nr);
ret = -EFAULT;
goto out_err_free_reg;
}
}
/* Completion for VF2PF request/response message exchange */
init_completion(&accel_dev->vf.msg_received);
adf_dbgfs_init(accel_dev);
ret = adf_dev_up(accel_dev, false);
if (ret)
goto out_err_dev_stop;
return ret;
out_err_dev_stop:
adf_dev_down(accel_dev);
out_err_free_reg:
pci_release_regions(accel_pci_dev->pci_dev);
out_err_disable:
pci_disable_device(accel_pci_dev->pci_dev);
out_err:
adf_cleanup_accel(accel_dev);
kfree(accel_dev);
return ret;
}
static void adf_remove(struct pci_dev *pdev)
{
struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev);
if (!accel_dev) {
pr_err("QAT: Driver removal failed\n");
return;
}
adf_flush_vf_wq(accel_dev);
adf_dev_down(accel_dev);
adf_cleanup_accel(accel_dev);
adf_cleanup_pci_dev(accel_dev);
kfree(accel_dev);
}
static int __init adfdrv_init(void)
{
request_module("intel_qat");
if (pci_register_driver(&adf_driver)) {
pr_err("QAT: Driver initialization failed\n");
return -EFAULT;
}
return 0;
}
static void __exit adfdrv_release(void)
{
pci_unregister_driver(&adf_driver);
adf_clean_vf_map(true);
}
module_init(adfdrv_init);
module_exit(adfdrv_release);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Intel");
MODULE_DESCRIPTION("Intel(R) QuickAssist Technology");
MODULE_IMPORT_NS("CRYPTO_QAT");
@@ -0,0 +1,72 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_CRYPTO_DEV_QAT) += intel_qat.o
ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE='"CRYPTO_QAT"'
intel_qat-y := adf_accel_engine.o \
adf_admin.o \
adf_aer.o \
adf_anti_rb.o \
adf_bank_state.o \
adf_cfg.o \
adf_cfg_services.o \
adf_clock.o \
adf_dc.o \
adf_dev_mgr.o \
adf_gen2_config.o \
adf_gen2_hw_csr_data.o \
adf_gen2_hw_data.o \
adf_gen4_config.o \
adf_gen4_hw_csr_data.o \
adf_gen4_hw_data.o \
adf_gen4_pm.o \
adf_gen4_ras.o \
adf_gen4_vf_mig.o \
adf_gen6_ras.o \
adf_gen6_shared.o \
adf_hw_arbiter.o \
adf_init.o \
adf_isr.o \
adf_kpt.o \
adf_module.o \
adf_mstate_mgr.o \
adf_rl_admin.o \
adf_rl.o \
adf_sysfs.o \
adf_sysfs_anti_rb.o \
adf_sysfs_kpt.o \
adf_sysfs_ras_counters.o \
adf_sysfs_rl.o \
adf_timer.o \
adf_transport.o \
qat_algs.o \
qat_algs_send.o \
qat_asym_algs.o \
qat_bl.o \
qat_comp_algs.o \
qat_compression.o \
qat_comp_zstd_utils.o \
qat_crypto.o \
qat_hal.o \
qat_mig_dev.o \
qat_uclo.o
intel_qat-$(CONFIG_DEBUG_FS) += adf_cnv_dbgfs.o \
adf_dbgfs.o \
adf_fw_counters.o \
adf_gen4_pm_debugfs.o \
adf_gen4_tl.o \
adf_gen6_pm_dbgfs.o \
adf_gen6_tl.o \
adf_heartbeat_dbgfs.o \
adf_heartbeat.o \
adf_pm_dbgfs.o \
adf_pm_dbgfs_utils.o \
adf_telemetry.o \
adf_tl_debugfs.o \
adf_transport_debug.o
intel_qat-$(CONFIG_PCI_IOV) += adf_gen2_pfvf.o adf_gen4_pfvf.o \
adf_pfvf_pf_msg.o adf_pfvf_pf_proto.o \
adf_pfvf_utils.o adf_pfvf_vf_msg.o \
adf_pfvf_vf_proto.o adf_sriov.o adf_vf_isr.o
intel_qat-$(CONFIG_CRYPTO_DEV_QAT_ERROR_INJECTION) += adf_heartbeat_inject.o
@@ -0,0 +1,501 @@
/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */
/* Copyright(c) 2014 - 2020 Intel Corporation */
#ifndef ADF_ACCEL_DEVICES_H_
#define ADF_ACCEL_DEVICES_H_
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/io.h>
#include <linux/pci.h>
#include <linux/ratelimit.h>
#include <linux/types.h>
#include <linux/qat/qat_mig_dev.h>
#include <linux/wordpart.h>
#include "adf_anti_rb.h"
#include "adf_cfg_common.h"
#include "adf_dc.h"
#include "adf_kpt.h"
#include "adf_rl.h"
#include "adf_telemetry.h"
#include "adf_pfvf_msg.h"
#include "icp_qat_hw.h"
#define ADF_DH895XCC_DEVICE_NAME "dh895xcc"
#define ADF_DH895XCCVF_DEVICE_NAME "dh895xccvf"
#define ADF_C62X_DEVICE_NAME "c6xx"
#define ADF_C62XVF_DEVICE_NAME "c6xxvf"
#define ADF_C3XXX_DEVICE_NAME "c3xxx"
#define ADF_C3XXXVF_DEVICE_NAME "c3xxxvf"
#define ADF_4XXX_DEVICE_NAME "4xxx"
#define ADF_420XX_DEVICE_NAME "420xx"
#define ADF_6XXX_DEVICE_NAME "6xxx"
#define PCI_DEVICE_ID_INTEL_QAT_4XXXIOV 0x4941
#define PCI_DEVICE_ID_INTEL_QAT_401XXIOV 0x4943
#define PCI_DEVICE_ID_INTEL_QAT_402XXIOV 0x4945
#define PCI_DEVICE_ID_INTEL_QAT_420XXIOV 0x4947
#define PCI_DEVICE_ID_INTEL_QAT_6XXX_IOV 0x4949
#define ADF_DEVICE_FUSECTL_OFFSET 0x40
#define ADF_DEVICE_LEGFUSE_OFFSET 0x4C
#define ADF_DEVICE_FUSECTL_MASK 0x80000000
#define ADF_PCI_MAX_BARS 3
#define ADF_DEVICE_NAME_LENGTH 32
#define ADF_ETR_MAX_RINGS_PER_BANK 16
#define ADF_MAX_MSIX_VECTOR_NAME 48
#define ADF_DEVICE_NAME_PREFIX "qat_"
enum adf_accel_capabilities {
ADF_ACCEL_CAPABILITIES_NULL = 0,
ADF_ACCEL_CAPABILITIES_CRYPTO_SYMMETRIC = 1,
ADF_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC = 2,
ADF_ACCEL_CAPABILITIES_CIPHER = 4,
ADF_ACCEL_CAPABILITIES_AUTHENTICATION = 8,
ADF_ACCEL_CAPABILITIES_COMPRESSION = 32,
ADF_ACCEL_CAPABILITIES_LZS_COMPRESSION = 64,
ADF_ACCEL_CAPABILITIES_RANDOM_NUMBER = 128
};
enum adf_accel_capabilities_ext {
ADF_ACCEL_CAPABILITIES_EXT_ZSTD_LZ4S = BIT(0),
ADF_ACCEL_CAPABILITIES_EXT_ZSTD = BIT(1),
};
enum adf_fuses {
ADF_FUSECTL0,
ADF_FUSECTL1,
ADF_FUSECTL2,
ADF_FUSECTL3,
ADF_FUSECTL4,
ADF_FUSECTL5,
ADF_MAX_FUSES
};
struct adf_bar {
resource_size_t base_addr;
void __iomem *virt_addr;
resource_size_t size;
};
struct adf_irq {
bool enabled;
char name[ADF_MAX_MSIX_VECTOR_NAME];
};
struct adf_accel_msix {
struct adf_irq *irqs;
u32 num_entries;
};
struct adf_accel_pci {
struct pci_dev *pci_dev;
struct adf_accel_msix msix_entries;
struct adf_bar pci_bars[ADF_PCI_MAX_BARS];
u8 revid;
u8 sku;
};
enum dev_state {
DEV_DOWN = 0,
DEV_UP
};
enum dev_sku_info {
DEV_SKU_1 = 0,
DEV_SKU_2,
DEV_SKU_3,
DEV_SKU_4,
DEV_SKU_VF,
DEV_SKU_UNKNOWN,
};
enum ras_errors {
ADF_RAS_CORR,
ADF_RAS_UNCORR,
ADF_RAS_FATAL,
ADF_RAS_ERRORS,
};
struct adf_error_counters {
atomic_t counter[ADF_RAS_ERRORS];
bool sysfs_added;
bool enabled;
};
static inline const char *get_sku_info(enum dev_sku_info info)
{
switch (info) {
case DEV_SKU_1:
return "SKU1";
case DEV_SKU_2:
return "SKU2";
case DEV_SKU_3:
return "SKU3";
case DEV_SKU_4:
return "SKU4";
case DEV_SKU_VF:
return "SKUVF";
case DEV_SKU_UNKNOWN:
default:
break;
}
return "Unknown SKU";
}
struct adf_hw_device_class {
const char *name;
const enum adf_device_type type;
u32 instances;
};
struct arb_info {
u32 arb_cfg;
u32 arb_offset;
u32 wt2sam_offset;
};
struct admin_info {
u32 admin_msg_ur;
u32 admin_msg_lr;
u32 mailbox_offset;
};
struct adf_bank_state;
struct adf_hw_csr_ops {
u64 (*build_csr_ring_base_addr)(dma_addr_t addr, u32 size);
u32 (*read_csr_ring_head)(void __iomem *csr_base_addr, u32 bank,
u32 ring);
void (*write_csr_ring_head)(void __iomem *csr_base_addr, u32 bank,
u32 ring, u32 value);
u32 (*read_csr_ring_tail)(void __iomem *csr_base_addr, u32 bank,
u32 ring);
void (*write_csr_ring_tail)(void __iomem *csr_base_addr, u32 bank,
u32 ring, u32 value);
u32 (*read_csr_stat)(void __iomem *csr_base_addr, u32 bank);
u32 (*read_csr_uo_stat)(void __iomem *csr_base_addr, u32 bank);
u32 (*read_csr_e_stat)(void __iomem *csr_base_addr, u32 bank);
u32 (*read_csr_ne_stat)(void __iomem *csr_base_addr, u32 bank);
u32 (*read_csr_nf_stat)(void __iomem *csr_base_addr, u32 bank);
u32 (*read_csr_f_stat)(void __iomem *csr_base_addr, u32 bank);
u32 (*read_csr_c_stat)(void __iomem *csr_base_addr, u32 bank);
u32 (*read_csr_exp_stat)(void __iomem *csr_base_addr, u32 bank);
u32 (*read_csr_exp_int_en)(void __iomem *csr_base_addr, u32 bank);
void (*write_csr_exp_int_en)(void __iomem *csr_base_addr, u32 bank,
u32 value);
u32 (*read_csr_ring_config)(void __iomem *csr_base_addr, u32 bank,
u32 ring);
void (*write_csr_ring_config)(void __iomem *csr_base_addr, u32 bank,
u32 ring, u32 value);
dma_addr_t (*read_csr_ring_base)(void __iomem *csr_base_addr, u32 bank,
u32 ring);
void (*write_csr_ring_base)(void __iomem *csr_base_addr, u32 bank,
u32 ring, dma_addr_t addr);
u32 (*read_csr_int_en)(void __iomem *csr_base_addr, u32 bank);
void (*write_csr_int_en)(void __iomem *csr_base_addr, u32 bank,
u32 value);
u32 (*read_csr_int_flag)(void __iomem *csr_base_addr, u32 bank);
void (*write_csr_int_flag)(void __iomem *csr_base_addr, u32 bank,
u32 value);
u32 (*read_csr_int_srcsel)(void __iomem *csr_base_addr, u32 bank);
void (*write_csr_int_srcsel)(void __iomem *csr_base_addr, u32 bank);
void (*write_csr_int_srcsel_w_val)(void __iomem *csr_base_addr,
u32 bank, u32 value);
u32 (*read_csr_int_col_en)(void __iomem *csr_base_addr, u32 bank);
void (*write_csr_int_col_en)(void __iomem *csr_base_addr, u32 bank,
u32 value);
u32 (*read_csr_int_col_ctl)(void __iomem *csr_base_addr, u32 bank);
void (*write_csr_int_col_ctl)(void __iomem *csr_base_addr, u32 bank,
u32 value);
u32 (*read_csr_int_flag_and_col)(void __iomem *csr_base_addr,
u32 bank);
void (*write_csr_int_flag_and_col)(void __iomem *csr_base_addr,
u32 bank, u32 value);
u32 (*read_csr_ring_srv_arb_en)(void __iomem *csr_base_addr, u32 bank);
void (*write_csr_ring_srv_arb_en)(void __iomem *csr_base_addr, u32 bank,
u32 value);
u32 (*get_int_col_ctl_enable_mask)(void);
};
struct adf_cfg_device_data;
struct adf_accel_dev;
struct adf_etr_data;
struct adf_etr_ring_data;
struct adf_ras_ops {
void (*enable_ras_errors)(struct adf_accel_dev *accel_dev);
void (*disable_ras_errors)(struct adf_accel_dev *accel_dev);
bool (*handle_interrupt)(struct adf_accel_dev *accel_dev,
bool *reset_required);
};
struct adf_pfvf_ops {
int (*enable_comms)(struct adf_accel_dev *accel_dev);
u32 (*get_pf2vf_offset)(u32 i);
u32 (*get_vf2pf_offset)(u32 i);
void (*enable_vf2pf_interrupts)(void __iomem *pmisc_addr, u32 vf_mask);
void (*disable_all_vf2pf_interrupts)(void __iomem *pmisc_addr);
u32 (*disable_pending_vf2pf_interrupts)(void __iomem *pmisc_addr);
int (*send_msg)(struct adf_accel_dev *accel_dev, struct pfvf_message msg,
u32 pfvf_offset, struct mutex *csr_lock);
struct pfvf_message (*recv_msg)(struct adf_accel_dev *accel_dev,
u32 pfvf_offset, u8 compat_ver);
};
struct adf_dc_ops {
int (*build_comp_block)(void *ctx, enum adf_dc_algo algo);
int (*build_decomp_block)(void *ctx, enum adf_dc_algo algo);
};
struct qat_migdev_ops {
int (*init)(struct qat_mig_dev *mdev);
void (*cleanup)(struct qat_mig_dev *mdev);
void (*reset)(struct qat_mig_dev *mdev);
int (*open)(struct qat_mig_dev *mdev);
void (*close)(struct qat_mig_dev *mdev);
int (*suspend)(struct qat_mig_dev *mdev);
int (*resume)(struct qat_mig_dev *mdev);
int (*save_state)(struct qat_mig_dev *mdev);
int (*save_setup)(struct qat_mig_dev *mdev);
int (*load_state)(struct qat_mig_dev *mdev);
int (*load_setup)(struct qat_mig_dev *mdev, int size);
};
struct adf_dev_err_mask {
u32 cppagentcmdpar_mask;
u32 parerr_ath_cph_mask;
u32 parerr_cpr_xlt_mask;
u32 parerr_dcpr_ucs_mask;
u32 parerr_pke_mask;
u32 parerr_wat_wcp_mask;
u32 ssmfeatren_mask;
};
struct adf_hw_device_data {
struct adf_hw_device_class *dev_class;
u32 (*get_accel_mask)(struct adf_hw_device_data *self);
u32 (*get_ae_mask)(struct adf_hw_device_data *self);
u32 (*get_accel_cap)(struct adf_accel_dev *accel_dev);
u32 (*get_sram_bar_id)(struct adf_hw_device_data *self);
u32 (*get_misc_bar_id)(struct adf_hw_device_data *self);
u32 (*get_etr_bar_id)(struct adf_hw_device_data *self);
u32 (*get_num_aes)(struct adf_hw_device_data *self);
u32 (*get_num_accels)(struct adf_hw_device_data *self);
void (*get_arb_info)(struct arb_info *arb_csrs_info);
void (*get_admin_info)(struct admin_info *admin_csrs_info);
enum dev_sku_info (*get_sku)(struct adf_hw_device_data *self);
u16 (*get_ring_to_svc_map)(struct adf_accel_dev *accel_dev);
int (*alloc_irq)(struct adf_accel_dev *accel_dev);
void (*free_irq)(struct adf_accel_dev *accel_dev);
void (*enable_error_correction)(struct adf_accel_dev *accel_dev);
int (*init_admin_comms)(struct adf_accel_dev *accel_dev);
void (*exit_admin_comms)(struct adf_accel_dev *accel_dev);
int (*send_admin_init)(struct adf_accel_dev *accel_dev);
int (*start_timer)(struct adf_accel_dev *accel_dev);
void (*stop_timer)(struct adf_accel_dev *accel_dev);
void (*check_hb_ctrs)(struct adf_accel_dev *accel_dev);
uint32_t (*get_hb_clock)(struct adf_hw_device_data *self);
int (*measure_clock)(struct adf_accel_dev *accel_dev);
int (*init_arb)(struct adf_accel_dev *accel_dev);
void (*exit_arb)(struct adf_accel_dev *accel_dev);
const u32 *(*get_arb_mapping)(struct adf_accel_dev *accel_dev);
int (*init_device)(struct adf_accel_dev *accel_dev);
int (*enable_pm)(struct adf_accel_dev *accel_dev);
bool (*handle_pm_interrupt)(struct adf_accel_dev *accel_dev);
void (*disable_iov)(struct adf_accel_dev *accel_dev);
void (*configure_iov_threads)(struct adf_accel_dev *accel_dev,
bool enable);
void (*enable_ints)(struct adf_accel_dev *accel_dev);
void (*set_ssm_wdtimer)(struct adf_accel_dev *accel_dev);
int (*ring_pair_reset)(struct adf_accel_dev *accel_dev, u32 bank_nr);
int (*bank_state_save)(struct adf_accel_dev *accel_dev, u32 bank_number,
struct adf_bank_state *state);
int (*bank_state_restore)(struct adf_accel_dev *accel_dev,
u32 bank_number, struct adf_bank_state *state);
void (*reset_device)(struct adf_accel_dev *accel_dev);
void (*set_msix_rttable)(struct adf_accel_dev *accel_dev);
const char *(*uof_get_name)(struct adf_accel_dev *accel_dev, u32 obj_num);
u32 (*uof_get_num_objs)(struct adf_accel_dev *accel_dev);
int (*uof_get_obj_type)(struct adf_accel_dev *accel_dev, u32 obj_num);
u32 (*uof_get_ae_mask)(struct adf_accel_dev *accel_dev, u32 obj_num);
int (*get_rp_group)(struct adf_accel_dev *accel_dev, u32 ae_mask);
u32 (*get_ena_thd_mask)(struct adf_accel_dev *accel_dev, u32 obj_num);
int (*dev_config)(struct adf_accel_dev *accel_dev);
bool (*services_supported)(unsigned long mask);
u32 (*get_svc_slice_cnt)(struct adf_accel_dev *accel_dev,
enum adf_base_services svc);
struct adf_pfvf_ops pfvf_ops;
struct adf_hw_csr_ops csr_ops;
struct adf_dc_ops dc_ops;
struct adf_ras_ops ras_ops;
struct adf_dev_err_mask dev_err_mask;
struct adf_rl_hw_data rl_data;
struct adf_tl_hw_data tl_data;
struct adf_anti_rb_hw_data anti_rb_data;
struct adf_kpt_hw_data kpt_data;
struct qat_migdev_ops vfmig_ops;
const char *fw_name;
const char *fw_mmp_name;
u32 fuses[ADF_MAX_FUSES];
u32 straps;
u32 accel_capabilities_mask;
u32 accel_capabilities_ext_mask;
u32 extended_dc_capabilities;
u16 fw_capabilities;
u32 clock_frequency;
u32 instance_id;
u16 accel_mask;
u32 ae_mask;
u32 admin_ae_mask;
u16 tx_rings_mask;
u16 ring_to_svc_map;
u32 thd_to_arb_map[ICP_QAT_HW_AE_DELIMITER];
u8 tx_rx_gap;
u8 num_banks;
u16 num_banks_per_vf;
u8 num_rings_per_bank;
u8 num_accel;
u8 num_logical_accel;
u8 num_engines;
u32 num_hb_ctrs;
u8 num_rps;
};
/* CSR write macro */
#define ADF_CSR_WR(csr_base, csr_offset, val) \
__raw_writel(val, csr_base + csr_offset)
/*
* CSR write macro to handle cases where the high and low
* offsets are sparsely located.
*/
#define ADF_CSR_WR64_LO_HI(csr_base, csr_low_offset, csr_high_offset, val) \
do { \
ADF_CSR_WR(csr_base, csr_low_offset, lower_32_bits(val)); \
ADF_CSR_WR(csr_base, csr_high_offset, upper_32_bits(val)); \
} while (0)
/* CSR read macro */
#define ADF_CSR_RD(csr_base, csr_offset) __raw_readl(csr_base + csr_offset)
#define ADF_CFG_NUM_SERVICES 4
#define ADF_SRV_TYPE_BIT_LEN 3
#define ADF_SRV_TYPE_MASK 0x7
#define ADF_AE_ADMIN_THREAD 7
#define ADF_NUM_THREADS_PER_AE 8
#define ADF_NUM_PKE_STRAND 2
#define ADF_AE_STRAND0_THREAD 8
#define ADF_AE_STRAND1_THREAD 9
#define GET_DEV(accel_dev) ((accel_dev)->accel_pci_dev.pci_dev->dev)
#define GET_BARS(accel_dev) ((accel_dev)->accel_pci_dev.pci_bars)
#define GET_HW_DATA(accel_dev) (accel_dev->hw_device)
#define GET_MAX_BANKS(accel_dev) (GET_HW_DATA(accel_dev)->num_banks)
#define GET_NUM_RINGS_PER_BANK(accel_dev) \
GET_HW_DATA(accel_dev)->num_rings_per_bank
#define GET_SRV_TYPE(accel_dev, idx) \
(((GET_HW_DATA(accel_dev)->ring_to_svc_map) >> (ADF_SRV_TYPE_BIT_LEN * (idx))) \
& ADF_SRV_TYPE_MASK)
#define GET_ERR_MASK(accel_dev) (&GET_HW_DATA(accel_dev)->dev_err_mask)
#define GET_MAX_ACCELENGINES(accel_dev) (GET_HW_DATA(accel_dev)->num_engines)
#define GET_CSR_OPS(accel_dev) (&(accel_dev)->hw_device->csr_ops)
#define GET_PFVF_OPS(accel_dev) (&(accel_dev)->hw_device->pfvf_ops)
#define GET_DC_OPS(accel_dev) (&(accel_dev)->hw_device->dc_ops)
#define GET_VFMIG_OPS(accel_dev) (&(accel_dev)->hw_device->vfmig_ops)
#define GET_TL_DATA(accel_dev) GET_HW_DATA(accel_dev)->tl_data
#define accel_to_pci_dev(accel_ptr) accel_ptr->accel_pci_dev.pci_dev
struct adf_admin_comms;
struct icp_qat_fw_loader_handle;
struct adf_fw_loader_data {
struct icp_qat_fw_loader_handle *fw_loader;
const struct firmware *uof_fw;
const struct firmware *mmp_fw;
};
struct adf_accel_vf_info {
struct adf_accel_dev *accel_dev;
struct mutex pf2vf_lock; /* protect CSR access for PF2VF messages */
struct mutex pfvf_mig_lock; /* protects PFVF state for migration */
struct ratelimit_state vf2pf_ratelimit;
u32 vf_nr;
bool init;
bool restarting;
u8 vf_compat_ver;
/*
* Private area used for device migration.
* Memory allocation and free is managed by migration driver.
*/
void *mig_priv;
};
struct adf_dc_data {
u8 *ovf_buff;
size_t ovf_buff_sz;
dma_addr_t ovf_buff_p;
};
struct adf_pm {
struct dentry *debugfs_pm_status;
bool present;
int idle_irq_counters;
int throttle_irq_counters;
int fw_irq_counters;
int host_ack_counter;
int host_nack_counter;
ssize_t (*print_pm_status)(struct adf_accel_dev *accel_dev,
char __user *buf, size_t count, loff_t *pos);
};
struct adf_sysfs {
int ring_num;
struct rw_semaphore lock; /* protects access to the fields in this struct */
};
struct adf_accel_dev {
struct adf_etr_data *transport;
struct adf_hw_device_data *hw_device;
struct adf_cfg_device_data *cfg;
struct adf_fw_loader_data *fw_loader;
struct adf_admin_comms *admin;
struct adf_telemetry *telemetry;
struct adf_dc_data *dc_data;
struct adf_pm power_management;
struct list_head crypto_list;
struct list_head compression_list;
unsigned long status;
atomic_t ref_count;
struct dentry *debugfs_dir;
struct dentry *fw_cntr_dbgfile;
struct dentry *cnv_dbgfile;
struct list_head list;
struct module *owner;
struct adf_accel_pci accel_pci_dev;
struct adf_timer *timer;
struct adf_heartbeat *heartbeat;
struct adf_rl *rate_limiting;
struct adf_sysfs sysfs;
union {
struct {
/* protects VF2PF interrupts access */
spinlock_t vf2pf_ints_lock;
/* prevents VF2PF handling from racing with VF state teardown */
bool vf2pf_disabled;
/* vf_info is non-zero when SR-IOV is init'ed */
struct adf_accel_vf_info *vf_info;
} pf;
struct {
bool irq_enabled;
char irq_name[ADF_MAX_MSIX_VECTOR_NAME];
struct tasklet_struct pf2vf_bh_tasklet;
struct mutex vf2pf_lock; /* protect CSR access */
struct completion msg_received;
struct pfvf_message response; /* temp field holding pf2vf response */
u8 pf_compat_ver;
} vf;
};
struct adf_error_counters ras_errors;
struct mutex state_lock; /* protect state of the device */
bool is_vf;
bool autoreset_on_error;
u32 accel_id;
};
#endif
@@ -0,0 +1,219 @@
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2014 - 2020 Intel Corporation */
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/pci.h>
#include "adf_cfg.h"
#include "adf_accel_devices.h"
#include "adf_common_drv.h"
#include "icp_qat_uclo.h"
static int adf_ae_fw_load_images(struct adf_accel_dev *accel_dev, void *fw_addr,
u32 fw_size)
{
struct adf_fw_loader_data *loader_data = accel_dev->fw_loader;
struct adf_hw_device_data *hw_device = accel_dev->hw_device;
struct icp_qat_fw_loader_handle *loader;
const char *obj_name;
u32 num_objs;
u32 ae_mask;
int i;
loader = loader_data->fw_loader;
num_objs = hw_device->uof_get_num_objs(accel_dev);
for (i = 0; i < num_objs; i++) {
obj_name = hw_device->uof_get_name(accel_dev, i);
ae_mask = hw_device->uof_get_ae_mask(accel_dev, i);
if (!obj_name || !ae_mask) {
dev_err(&GET_DEV(accel_dev), "Invalid UOF image\n");
goto out_err;
}
if (qat_uclo_set_cfg_ae_mask(loader, ae_mask)) {
dev_err(&GET_DEV(accel_dev),
"Invalid mask for UOF image\n");
goto out_err;
}
if (qat_uclo_map_obj(loader, fw_addr, fw_size, obj_name)) {
dev_err(&GET_DEV(accel_dev),
"Failed to map UOF firmware\n");
goto out_err;
}
if (qat_uclo_wr_all_uimage(loader)) {
dev_err(&GET_DEV(accel_dev),
"Failed to load UOF firmware\n");
goto out_err;
}
qat_uclo_del_obj(loader);
}
return 0;
out_err:
adf_ae_fw_release(accel_dev);
return -EFAULT;
}
int adf_ae_fw_load(struct adf_accel_dev *accel_dev)
{
struct adf_fw_loader_data *loader_data = accel_dev->fw_loader;
struct adf_hw_device_data *hw_device = accel_dev->hw_device;
void *fw_addr, *mmp_addr;
u32 fw_size, mmp_size;
if (!hw_device->fw_name)
return 0;
if (request_firmware(&loader_data->mmp_fw, hw_device->fw_mmp_name,
&accel_dev->accel_pci_dev.pci_dev->dev)) {
dev_err(&GET_DEV(accel_dev), "Failed to load MMP firmware %s\n",
hw_device->fw_mmp_name);
return -EFAULT;
}
if (request_firmware(&loader_data->uof_fw, hw_device->fw_name,
&accel_dev->accel_pci_dev.pci_dev->dev)) {
dev_err(&GET_DEV(accel_dev), "Failed to load UOF firmware %s\n",
hw_device->fw_name);
goto out_err;
}
fw_size = loader_data->uof_fw->size;
fw_addr = (void *)loader_data->uof_fw->data;
mmp_size = loader_data->mmp_fw->size;
mmp_addr = (void *)loader_data->mmp_fw->data;
if (qat_uclo_wr_mimage(loader_data->fw_loader, mmp_addr, mmp_size)) {
dev_err(&GET_DEV(accel_dev), "Failed to load MMP\n");
goto out_err;
}
if (hw_device->uof_get_num_objs)
return adf_ae_fw_load_images(accel_dev, fw_addr, fw_size);
if (qat_uclo_map_obj(loader_data->fw_loader, fw_addr, fw_size, NULL)) {
dev_err(&GET_DEV(accel_dev), "Failed to map FW\n");
goto out_err;
}
if (qat_uclo_wr_all_uimage(loader_data->fw_loader)) {
dev_err(&GET_DEV(accel_dev), "Failed to load UOF\n");
goto out_err;
}
return 0;
out_err:
adf_ae_fw_release(accel_dev);
return -EFAULT;
}
void adf_ae_fw_release(struct adf_accel_dev *accel_dev)
{
struct adf_fw_loader_data *loader_data = accel_dev->fw_loader;
struct adf_hw_device_data *hw_device = accel_dev->hw_device;
if (!hw_device->fw_name)
return;
qat_uclo_del_obj(loader_data->fw_loader);
qat_hal_deinit(loader_data->fw_loader);
release_firmware(loader_data->uof_fw);
release_firmware(loader_data->mmp_fw);
loader_data->uof_fw = NULL;
loader_data->mmp_fw = NULL;
loader_data->fw_loader = NULL;
}
int adf_ae_start(struct adf_accel_dev *accel_dev)
{
struct adf_fw_loader_data *loader_data = accel_dev->fw_loader;
struct adf_hw_device_data *hw_data = accel_dev->hw_device;
u32 ae_ctr;
if (!hw_data->fw_name)
return 0;
ae_ctr = qat_hal_start(loader_data->fw_loader);
dev_info(&GET_DEV(accel_dev),
"qat_dev%d started %d acceleration engines\n",
accel_dev->accel_id, ae_ctr);
return 0;
}
int adf_ae_stop(struct adf_accel_dev *accel_dev)
{
struct adf_fw_loader_data *loader_data = accel_dev->fw_loader;
struct adf_hw_device_data *hw_data = accel_dev->hw_device;
u32 ae_ctr, ae, max_aes = GET_MAX_ACCELENGINES(accel_dev);
if (!hw_data->fw_name)
return 0;
for (ae = 0, ae_ctr = 0; ae < max_aes; ae++) {
if (hw_data->ae_mask & (1 << ae)) {
qat_hal_stop(loader_data->fw_loader, ae, 0xFF);
ae_ctr++;
}
}
dev_info(&GET_DEV(accel_dev),
"qat_dev%d stopped %d acceleration engines\n",
accel_dev->accel_id, ae_ctr);
return 0;
}
static int adf_ae_reset(struct adf_accel_dev *accel_dev, int ae)
{
struct adf_fw_loader_data *loader_data = accel_dev->fw_loader;
unsigned long reset_delay;
qat_hal_reset(loader_data->fw_loader);
reset_delay = loader_data->fw_loader->chip_info->reset_delay_us;
if (reset_delay)
fsleep(reset_delay);
if (qat_hal_clr_reset(loader_data->fw_loader))
return -EFAULT;
return 0;
}
int adf_ae_init(struct adf_accel_dev *accel_dev)
{
struct adf_fw_loader_data *loader_data;
struct adf_hw_device_data *hw_device = accel_dev->hw_device;
if (!hw_device->fw_name)
return 0;
loader_data = kzalloc_obj(*loader_data);
if (!loader_data)
return -ENOMEM;
accel_dev->fw_loader = loader_data;
if (qat_hal_init(accel_dev)) {
dev_err(&GET_DEV(accel_dev), "Failed to init the AEs\n");
kfree(loader_data);
return -EFAULT;
}
if (adf_ae_reset(accel_dev, 0)) {
dev_err(&GET_DEV(accel_dev), "Failed to reset the AEs\n");
qat_hal_deinit(loader_data->fw_loader);
kfree(loader_data);
return -EFAULT;
}
return 0;
}
int adf_ae_shutdown(struct adf_accel_dev *accel_dev)
{
struct adf_fw_loader_data *loader_data = accel_dev->fw_loader;
struct adf_hw_device_data *hw_device = accel_dev->hw_device;
if (!hw_device->fw_name)
return 0;
qat_hal_deinit(loader_data->fw_loader);
kfree(accel_dev->fw_loader);
accel_dev->fw_loader = NULL;
return 0;
}
@@ -0,0 +1,719 @@
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2014 - 2020 Intel Corporation */
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/iopoll.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include "adf_accel_devices.h"
#include "adf_admin.h"
#include "adf_anti_rb.h"
#include "adf_common_drv.h"
#include "adf_cfg.h"
#include "adf_heartbeat.h"
#include "adf_kpt.h"
#include "icp_qat_fw_init_admin.h"
#define ADF_ADMIN_MAILBOX_STRIDE 0x1000
#define ADF_ADMINMSG_LEN 32
#define ADF_CONST_TABLE_SIZE 1024
#define ADF_ADMIN_POLL_DELAY_US 20
#define ADF_ADMIN_POLL_TIMEOUT_US (5 * USEC_PER_SEC)
#define ADF_ONE_AE 1
#define ADF_ADMIN_RETRY_MAX 60
static const u8 const_tab[1024] __aligned(1024) = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76,
0x54, 0x32, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x45, 0x23, 0x01, 0xef, 0xcd, 0xab,
0x89, 0x98, 0xba, 0xdc, 0xfe, 0x10, 0x32, 0x54, 0x76, 0xc3, 0xd2, 0xe1, 0xf0,
0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x05, 0x9e,
0xd8, 0x36, 0x7c, 0xd5, 0x07, 0x30, 0x70, 0xdd, 0x17, 0xf7, 0x0e, 0x59, 0x39,
0xff, 0xc0, 0x0b, 0x31, 0x68, 0x58, 0x15, 0x11, 0x64, 0xf9, 0x8f, 0xa7, 0xbe,
0xfa, 0x4f, 0xa4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x09, 0xe6, 0x67, 0xbb, 0x67, 0xae,
0x85, 0x3c, 0x6e, 0xf3, 0x72, 0xa5, 0x4f, 0xf5, 0x3a, 0x51, 0x0e, 0x52, 0x7f,
0x9b, 0x05, 0x68, 0x8c, 0x1f, 0x83, 0xd9, 0xab, 0x5b, 0xe0, 0xcd, 0x19, 0x05,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xcb, 0xbb, 0x9d, 0x5d, 0xc1, 0x05, 0x9e, 0xd8, 0x62, 0x9a, 0x29,
0x2a, 0x36, 0x7c, 0xd5, 0x07, 0x91, 0x59, 0x01, 0x5a, 0x30, 0x70, 0xdd, 0x17,
0x15, 0x2f, 0xec, 0xd8, 0xf7, 0x0e, 0x59, 0x39, 0x67, 0x33, 0x26, 0x67, 0xff,
0xc0, 0x0b, 0x31, 0x8e, 0xb4, 0x4a, 0x87, 0x68, 0x58, 0x15, 0x11, 0xdb, 0x0c,
0x2e, 0x0d, 0x64, 0xf9, 0x8f, 0xa7, 0x47, 0xb5, 0x48, 0x1d, 0xbe, 0xfa, 0x4f,
0xa4, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x6a, 0x09, 0xe6, 0x67, 0xf3, 0xbc, 0xc9, 0x08, 0xbb,
0x67, 0xae, 0x85, 0x84, 0xca, 0xa7, 0x3b, 0x3c, 0x6e, 0xf3, 0x72, 0xfe, 0x94,
0xf8, 0x2b, 0xa5, 0x4f, 0xf5, 0x3a, 0x5f, 0x1d, 0x36, 0xf1, 0x51, 0x0e, 0x52,
0x7f, 0xad, 0xe6, 0x82, 0xd1, 0x9b, 0x05, 0x68, 0x8c, 0x2b, 0x3e, 0x6c, 0x1f,
0x1f, 0x83, 0xd9, 0xab, 0xfb, 0x41, 0xbd, 0x6b, 0x5b, 0xe0, 0xcd, 0x19, 0x13,
0x7e, 0x21, 0x79, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x15, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x14, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x2B, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
struct adf_admin_comms {
dma_addr_t phy_addr;
dma_addr_t const_tbl_addr;
void *virt_addr;
void *virt_tbl_addr;
void __iomem *mailbox_addr;
struct mutex lock; /* protects adf_admin_comms struct */
};
static int adf_put_admin_msg_sync(struct adf_accel_dev *accel_dev, u32 ae,
void *in, void *out)
{
int ret;
u32 status;
struct adf_admin_comms *admin = accel_dev->admin;
int offset = ae * ADF_ADMINMSG_LEN * 2;
void __iomem *mailbox = admin->mailbox_addr;
int mb_offset = ae * ADF_ADMIN_MAILBOX_STRIDE;
struct icp_qat_fw_init_admin_req *request = in;
mutex_lock(&admin->lock);
if (ADF_CSR_RD(mailbox, mb_offset) == 1) {
mutex_unlock(&admin->lock);
return -EAGAIN;
}
memcpy(admin->virt_addr + offset, in, ADF_ADMINMSG_LEN);
ADF_CSR_WR(mailbox, mb_offset, 1);
ret = read_poll_timeout(ADF_CSR_RD, status, status == 0,
ADF_ADMIN_POLL_DELAY_US,
ADF_ADMIN_POLL_TIMEOUT_US, true,
mailbox, mb_offset);
if (ret < 0) {
/* Response timeout */
dev_err(&GET_DEV(accel_dev),
"Failed to send admin msg %d to accelerator %d\n",
request->cmd_id, ae);
} else {
/* Response received from admin message, we can now
* make response data available in "out" parameter.
*/
memcpy(out, admin->virt_addr + offset +
ADF_ADMINMSG_LEN, ADF_ADMINMSG_LEN);
}
mutex_unlock(&admin->lock);
return ret;
}
static int adf_send_admin(struct adf_accel_dev *accel_dev,
struct icp_qat_fw_init_admin_req *req,
struct icp_qat_fw_init_admin_resp *resp,
const unsigned long ae_mask)
{
u32 ae;
for_each_set_bit(ae, &ae_mask, ICP_QAT_HW_AE_DELIMITER)
if (adf_put_admin_msg_sync(accel_dev, ae, req, resp) ||
resp->status)
return -EFAULT;
return 0;
}
static int adf_init_ae(struct adf_accel_dev *accel_dev)
{
struct icp_qat_fw_init_admin_req req;
struct icp_qat_fw_init_admin_resp resp;
struct adf_hw_device_data *hw_device = accel_dev->hw_device;
u32 ae_mask = hw_device->ae_mask;
memset(&req, 0, sizeof(req));
memset(&resp, 0, sizeof(resp));
req.cmd_id = ICP_QAT_FW_INIT_AE;
return adf_send_admin(accel_dev, &req, &resp, ae_mask);
}
static int adf_set_fw_constants(struct adf_accel_dev *accel_dev)
{
struct icp_qat_fw_init_admin_req req;
struct icp_qat_fw_init_admin_resp resp;
struct adf_hw_device_data *hw_device = accel_dev->hw_device;
u32 ae_mask = hw_device->admin_ae_mask ?: hw_device->ae_mask;
memset(&req, 0, sizeof(req));
memset(&resp, 0, sizeof(resp));
req.cmd_id = ICP_QAT_FW_CONSTANTS_CFG;
req.init_cfg_sz = ADF_CONST_TABLE_SIZE;
req.init_cfg_ptr = accel_dev->admin->const_tbl_addr;
return adf_send_admin(accel_dev, &req, &resp, ae_mask);
}
int adf_get_fw_timestamp(struct adf_accel_dev *accel_dev, u64 *timestamp)
{
struct icp_qat_fw_init_admin_req req = { };
struct icp_qat_fw_init_admin_resp resp;
unsigned int ae_mask = ADF_ONE_AE;
int ret;
req.cmd_id = ICP_QAT_FW_TIMER_GET;
ret = adf_send_admin(accel_dev, &req, &resp, ae_mask);
if (ret)
return ret;
*timestamp = resp.timestamp;
return 0;
}
static int adf_set_chaining(struct adf_accel_dev *accel_dev)
{
u32 ae_mask = GET_HW_DATA(accel_dev)->ae_mask;
struct icp_qat_fw_init_admin_resp resp = { };
struct icp_qat_fw_init_admin_req req = { };
req.cmd_id = ICP_QAT_FW_DC_CHAIN_INIT;
return adf_send_admin(accel_dev, &req, &resp, ae_mask);
}
static int adf_get_dc_capabilities(struct adf_accel_dev *accel_dev,
u32 *capabilities)
{
struct adf_hw_device_data *hw_device = accel_dev->hw_device;
struct icp_qat_fw_init_admin_resp resp;
struct icp_qat_fw_init_admin_req req;
unsigned long ae_mask;
unsigned long ae;
int ret;
/* Target only service accelerator engines */
ae_mask = hw_device->ae_mask & ~hw_device->admin_ae_mask;
memset(&req, 0, sizeof(req));
memset(&resp, 0, sizeof(resp));
req.cmd_id = ICP_QAT_FW_COMP_CAPABILITY_GET;
*capabilities = 0;
for_each_set_bit(ae, &ae_mask, GET_MAX_ACCELENGINES(accel_dev)) {
ret = adf_send_admin(accel_dev, &req, &resp, 1ULL << ae);
if (ret)
return ret;
*capabilities |= resp.extended_features;
}
return 0;
}
int adf_get_ae_fw_counters(struct adf_accel_dev *accel_dev, u16 ae, u64 *reqs, u64 *resps)
{
struct icp_qat_fw_init_admin_resp resp = { };
struct icp_qat_fw_init_admin_req req = { };
int ret;
req.cmd_id = ICP_QAT_FW_COUNTERS_GET;
ret = adf_put_admin_msg_sync(accel_dev, ae, &req, &resp);
if (ret || resp.status)
return -EFAULT;
*reqs = resp.req_rec_count;
*resps = resp.resp_sent_count;
return 0;
}
int adf_send_admin_tim_sync(struct adf_accel_dev *accel_dev, u32 cnt)
{
u32 ae_mask = accel_dev->hw_device->ae_mask;
struct icp_qat_fw_init_admin_req req = { };
struct icp_qat_fw_init_admin_resp resp = { };
req.cmd_id = ICP_QAT_FW_SYNC;
req.int_timer_ticks = cnt;
return adf_send_admin(accel_dev, &req, &resp, ae_mask);
}
int adf_send_admin_hb_timer(struct adf_accel_dev *accel_dev, uint32_t ticks)
{
u32 ae_mask = accel_dev->hw_device->ae_mask;
struct icp_qat_fw_init_admin_req req = { };
struct icp_qat_fw_init_admin_resp resp;
req.cmd_id = ICP_QAT_FW_HEARTBEAT_TIMER_SET;
req.init_cfg_ptr = accel_dev->heartbeat->dma.phy_addr;
req.heartbeat_ticks = ticks;
return adf_send_admin(accel_dev, &req, &resp, ae_mask);
}
static bool is_dcc_enabled(struct adf_accel_dev *accel_dev)
{
char services[ADF_CFG_MAX_VAL_LEN_IN_BYTES] = {0};
int ret;
ret = adf_cfg_get_param_value(accel_dev, ADF_GENERAL_SEC,
ADF_SERVICES_ENABLED, services);
if (ret)
return false;
return !strcmp(services, "dcc");
}
static int adf_get_fw_capabilities(struct adf_accel_dev *accel_dev, u16 *caps)
{
u32 ae_mask = accel_dev->hw_device->admin_ae_mask;
struct icp_qat_fw_init_admin_resp resp = { };
struct icp_qat_fw_init_admin_req req = { };
int ret;
if (!ae_mask)
return 0;
req.cmd_id = ICP_QAT_FW_CAPABILITIES_GET;
ret = adf_send_admin(accel_dev, &req, &resp, ae_mask);
if (ret)
return ret;
*caps = resp.fw_capabilities;
return 0;
}
int adf_send_admin_rl_init(struct adf_accel_dev *accel_dev,
struct icp_qat_fw_init_admin_slice_cnt *slices)
{
u32 ae_mask = accel_dev->hw_device->admin_ae_mask;
struct icp_qat_fw_init_admin_resp resp = { };
struct icp_qat_fw_init_admin_req req = { };
int ret;
req.cmd_id = ICP_QAT_FW_RL_INIT;
ret = adf_send_admin(accel_dev, &req, &resp, ae_mask);
if (ret)
return ret;
memcpy(slices, &resp.slices, sizeof(*slices));
return 0;
}
int adf_send_admin_rl_add_update(struct adf_accel_dev *accel_dev,
struct icp_qat_fw_init_admin_req *req)
{
u32 ae_mask = accel_dev->hw_device->admin_ae_mask;
struct icp_qat_fw_init_admin_resp resp = { };
/*
* req struct filled in rl implementation. Used commands
* ICP_QAT_FW_RL_ADD for a new SLA
* ICP_QAT_FW_RL_UPDATE for update SLA
*/
return adf_send_admin(accel_dev, req, &resp, ae_mask);
}
int adf_send_admin_rl_delete(struct adf_accel_dev *accel_dev, u16 node_id,
u8 node_type)
{
u32 ae_mask = accel_dev->hw_device->admin_ae_mask;
struct icp_qat_fw_init_admin_resp resp = { };
struct icp_qat_fw_init_admin_req req = { };
req.cmd_id = ICP_QAT_FW_RL_REMOVE;
req.node_id = node_id;
req.node_type = node_type;
return adf_send_admin(accel_dev, &req, &resp, ae_mask);
}
/**
* adf_send_admin_init() - Function sends init message to FW
* @accel_dev: Pointer to acceleration device.
*
* Function sends admin init message to the FW
*
* Return: 0 on success, error code otherwise.
*/
int adf_send_admin_init(struct adf_accel_dev *accel_dev)
{
struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
u32 dc_capabilities = 0;
int ret;
ret = adf_set_fw_constants(accel_dev);
if (ret)
return ret;
if (is_dcc_enabled(accel_dev)) {
ret = adf_set_chaining(accel_dev);
if (ret)
return ret;
}
ret = adf_get_dc_capabilities(accel_dev, &dc_capabilities);
if (ret) {
dev_err(&GET_DEV(accel_dev), "Cannot get dc capabilities\n");
return ret;
}
accel_dev->hw_device->extended_dc_capabilities = dc_capabilities;
adf_get_fw_capabilities(accel_dev, &hw_data->fw_capabilities);
return adf_init_ae(accel_dev);
}
EXPORT_SYMBOL_GPL(adf_send_admin_init);
/**
* adf_init_admin_pm() - Function sends PM init message to FW
* @accel_dev: Pointer to acceleration device.
* @idle_delay: QAT HW idle time before power gating is initiated.
* 000 - 64us
* 001 - 128us
* 010 - 256us
* 011 - 512us
* 100 - 1ms
* 101 - 2ms
* 110 - 4ms
* 111 - 8ms
*
* Function sends to the FW the admin init message for the PM state
* configuration.
*
* Return: 0 on success, error code otherwise.
*/
int adf_init_admin_pm(struct adf_accel_dev *accel_dev, u32 idle_delay)
{
struct adf_hw_device_data *hw_data = accel_dev->hw_device;
struct icp_qat_fw_init_admin_resp resp = {0};
struct icp_qat_fw_init_admin_req req = {0};
u32 ae_mask = hw_data->admin_ae_mask;
if (!accel_dev->admin) {
dev_err(&GET_DEV(accel_dev), "adf_admin is not available\n");
return -EFAULT;
}
req.cmd_id = ICP_QAT_FW_PM_STATE_CONFIG;
req.idle_filter = idle_delay;
return adf_send_admin(accel_dev, &req, &resp, ae_mask);
}
EXPORT_SYMBOL_GPL(adf_init_admin_pm);
int adf_get_pm_info(struct adf_accel_dev *accel_dev, dma_addr_t p_state_addr,
size_t buff_size)
{
struct adf_hw_device_data *hw_data = accel_dev->hw_device;
struct icp_qat_fw_init_admin_req req = { };
struct icp_qat_fw_init_admin_resp resp;
u32 ae_mask = hw_data->admin_ae_mask;
int ret;
/* Query pm info via init/admin cmd */
if (!accel_dev->admin) {
dev_err(&GET_DEV(accel_dev), "adf_admin is not available\n");
return -EFAULT;
}
req.cmd_id = ICP_QAT_FW_PM_INFO;
req.init_cfg_sz = buff_size;
req.init_cfg_ptr = p_state_addr;
ret = adf_send_admin(accel_dev, &req, &resp, ae_mask);
if (ret)
dev_err(&GET_DEV(accel_dev),
"Failed to query power-management info\n");
return ret;
}
int adf_get_cnv_stats(struct adf_accel_dev *accel_dev, u16 ae, u16 *err_cnt,
u16 *latest_err)
{
struct icp_qat_fw_init_admin_req req = { };
struct icp_qat_fw_init_admin_resp resp;
int ret;
req.cmd_id = ICP_QAT_FW_CNV_STATS_GET;
ret = adf_put_admin_msg_sync(accel_dev, ae, &req, &resp);
if (ret)
return ret;
if (resp.status)
return -EPROTONOSUPPORT;
*err_cnt = resp.error_count;
*latest_err = resp.latest_error;
return ret;
}
int adf_send_admin_tl_start(struct adf_accel_dev *accel_dev,
dma_addr_t tl_dma_addr, size_t layout_sz, u8 *rp_indexes,
struct icp_qat_fw_init_admin_slice_cnt *slice_count)
{
u32 ae_mask = GET_HW_DATA(accel_dev)->admin_ae_mask;
struct icp_qat_fw_init_admin_resp resp = { };
struct icp_qat_fw_init_admin_req req = { };
int ret;
req.cmd_id = ICP_QAT_FW_TL_START;
req.init_cfg_ptr = tl_dma_addr;
req.init_cfg_sz = layout_sz;
if (rp_indexes)
memcpy(&req.rp_indexes, rp_indexes, sizeof(req.rp_indexes));
ret = adf_send_admin(accel_dev, &req, &resp, ae_mask);
if (ret)
return ret;
memcpy(slice_count, &resp.slices, sizeof(*slice_count));
return 0;
}
int adf_send_admin_tl_stop(struct adf_accel_dev *accel_dev)
{
struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
struct icp_qat_fw_init_admin_resp resp = { };
struct icp_qat_fw_init_admin_req req = { };
u32 ae_mask = hw_data->admin_ae_mask;
req.cmd_id = ICP_QAT_FW_TL_STOP;
return adf_send_admin(accel_dev, &req, &resp, ae_mask);
}
static int adf_send_admin_retry(struct adf_accel_dev *accel_dev, u8 cmd_id,
struct icp_qat_fw_init_admin_resp *resp,
unsigned int sleep_ms)
{
u32 admin_ae_mask = GET_HW_DATA(accel_dev)->admin_ae_mask;
struct icp_qat_fw_init_admin_req req = { };
unsigned int retries = ADF_ADMIN_RETRY_MAX;
int ret;
req.cmd_id = cmd_id;
do {
ret = adf_send_admin(accel_dev, &req, resp, admin_ae_mask);
if (!ret)
return 0;
if (resp->status != ICP_QAT_FW_INIT_RESP_STATUS_RETRY)
return ret;
msleep(sleep_ms);
} while (--retries);
return -ETIMEDOUT;
}
static int adf_send_admin_svn(struct adf_accel_dev *accel_dev, u8 cmd_id,
struct icp_qat_fw_init_admin_resp *resp)
{
return adf_send_admin_retry(accel_dev, cmd_id, resp, ADF_SVN_RETRY_MS);
}
int adf_send_admin_arb_query(struct adf_accel_dev *accel_dev, int cmd, u8 *svn)
{
struct icp_qat_fw_init_admin_resp resp = { };
int ret;
ret = adf_send_admin_svn(accel_dev, ICP_QAT_FW_SVN_READ, &resp);
if (ret)
return ret;
switch (cmd) {
case ARB_ENFORCED_MIN_SVN:
*svn = resp.enforced_min_svn;
break;
case ARB_PERMANENT_MIN_SVN:
*svn = resp.permanent_min_svn;
break;
case ARB_ACTIVE_SVN:
*svn = resp.active_svn;
break;
default:
*svn = 0;
dev_err(&GET_DEV(accel_dev),
"Unknown secure version number request\n");
ret = -EINVAL;
}
return ret;
}
int adf_send_admin_arb_commit(struct adf_accel_dev *accel_dev)
{
struct icp_qat_fw_init_admin_resp resp = { };
return adf_send_admin_svn(accel_dev, ICP_QAT_FW_SVN_COMMIT, &resp);
}
static_assert(sizeof(struct icp_qat_fw_init_admin_kpt_cfg) < PAGE_SIZE);
static void adf_cfg_kpt_config(struct adf_accel_dev *accel_dev,
struct icp_qat_fw_init_admin_kpt_cfg *kpt_config)
{
struct adf_kpt_interface_data *user_data = GET_KPT_USER_DATA(accel_dev);
kpt_config->swk_cnt_per_fn = user_data->swk_cnt_per_fn;
kpt_config->swk_cnt_per_pasid = user_data->swk_cnt_per_pasid;
kpt_config->swk_ttl_in_secs = user_data->swk_max_ttl;
kpt_config->swk_shared_disable = !user_data->swk_shared;
}
int adf_send_admin_kpt_init(struct adf_accel_dev *accel_dev, void *init_cfg,
size_t init_cfg_sz, dma_addr_t init_ptr)
{
struct icp_qat_fw_init_admin_kpt_cfg *kpt_config = init_cfg;
u32 ae_mask = GET_HW_DATA(accel_dev)->admin_ae_mask;
struct icp_qat_fw_init_admin_resp resp = { };
struct icp_qat_fw_init_admin_req req = { };
int ret;
if (!kpt_config || init_cfg_sz < sizeof(*kpt_config))
return -EINVAL;
adf_cfg_kpt_config(accel_dev, kpt_config);
req.cmd_id = ICP_QAT_FW_KPT_ENABLE;
req.init_cfg_ptr = init_ptr;
req.init_cfg_sz = sizeof(*kpt_config);
ret = adf_send_admin(accel_dev, &req, &resp, ae_mask);
if (ret)
dev_err(&GET_DEV(accel_dev), "Failed to send KPT init admin message\n");
return ret;
}
int adf_init_admin_comms(struct adf_accel_dev *accel_dev)
{
struct adf_admin_comms *admin;
struct adf_hw_device_data *hw_data = accel_dev->hw_device;
void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev);
struct admin_info admin_csrs_info;
u32 mailbox_offset, adminmsg_u, adminmsg_l;
void __iomem *mailbox;
u64 reg_val;
admin = kzalloc_node(sizeof(*accel_dev->admin), GFP_KERNEL,
dev_to_node(&GET_DEV(accel_dev)));
if (!admin)
return -ENOMEM;
admin->virt_addr = dma_alloc_coherent(&GET_DEV(accel_dev), PAGE_SIZE,
&admin->phy_addr, GFP_KERNEL);
if (!admin->virt_addr) {
dev_err(&GET_DEV(accel_dev), "Failed to allocate dma buff\n");
kfree(admin);
return -ENOMEM;
}
admin->virt_tbl_addr = dma_alloc_coherent(&GET_DEV(accel_dev),
PAGE_SIZE,
&admin->const_tbl_addr,
GFP_KERNEL);
if (!admin->virt_tbl_addr) {
dev_err(&GET_DEV(accel_dev), "Failed to allocate const_tbl\n");
dma_free_coherent(&GET_DEV(accel_dev), PAGE_SIZE,
admin->virt_addr, admin->phy_addr);
kfree(admin);
return -ENOMEM;
}
memcpy(admin->virt_tbl_addr, const_tab, sizeof(const_tab));
hw_data->get_admin_info(&admin_csrs_info);
mailbox_offset = admin_csrs_info.mailbox_offset;
mailbox = pmisc_addr + mailbox_offset;
adminmsg_u = admin_csrs_info.admin_msg_ur;
adminmsg_l = admin_csrs_info.admin_msg_lr;
reg_val = (u64)admin->phy_addr;
ADF_CSR_WR(pmisc_addr, adminmsg_u, upper_32_bits(reg_val));
ADF_CSR_WR(pmisc_addr, adminmsg_l, lower_32_bits(reg_val));
mutex_init(&admin->lock);
admin->mailbox_addr = mailbox;
accel_dev->admin = admin;
return 0;
}
EXPORT_SYMBOL_GPL(adf_init_admin_comms);
void adf_exit_admin_comms(struct adf_accel_dev *accel_dev)
{
struct adf_admin_comms *admin = accel_dev->admin;
if (!admin)
return;
if (admin->virt_addr)
dma_free_coherent(&GET_DEV(accel_dev), PAGE_SIZE,
admin->virt_addr, admin->phy_addr);
if (admin->virt_tbl_addr)
dma_free_coherent(&GET_DEV(accel_dev), PAGE_SIZE,
admin->virt_tbl_addr, admin->const_tbl_addr);
mutex_destroy(&admin->lock);
kfree(admin);
accel_dev->admin = NULL;
}
EXPORT_SYMBOL_GPL(adf_exit_admin_comms);
@@ -0,0 +1,35 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright(c) 2023 Intel Corporation */
#ifndef ADF_ADMIN
#define ADF_ADMIN
#include "icp_qat_fw_init_admin.h"
struct adf_accel_dev;
int adf_init_admin_comms(struct adf_accel_dev *accel_dev);
void adf_exit_admin_comms(struct adf_accel_dev *accel_dev);
int adf_send_admin_init(struct adf_accel_dev *accel_dev);
int adf_get_ae_fw_counters(struct adf_accel_dev *accel_dev, u16 ae, u64 *reqs, u64 *resps);
int adf_init_admin_pm(struct adf_accel_dev *accel_dev, u32 idle_delay);
int adf_send_admin_tim_sync(struct adf_accel_dev *accel_dev, u32 cnt);
int adf_send_admin_hb_timer(struct adf_accel_dev *accel_dev, uint32_t ticks);
int adf_send_admin_rl_init(struct adf_accel_dev *accel_dev,
struct icp_qat_fw_init_admin_slice_cnt *slices);
int adf_send_admin_rl_add_update(struct adf_accel_dev *accel_dev,
struct icp_qat_fw_init_admin_req *req);
int adf_send_admin_rl_delete(struct adf_accel_dev *accel_dev, u16 node_id,
u8 node_type);
int adf_get_fw_timestamp(struct adf_accel_dev *accel_dev, u64 *timestamp);
int adf_get_pm_info(struct adf_accel_dev *accel_dev, dma_addr_t p_state_addr, size_t buff_size);
int adf_get_cnv_stats(struct adf_accel_dev *accel_dev, u16 ae, u16 *err_cnt, u16 *latest_err);
int adf_send_admin_tl_start(struct adf_accel_dev *accel_dev,
dma_addr_t tl_dma_addr, size_t layout_sz, u8 *rp_indexes,
struct icp_qat_fw_init_admin_slice_cnt *slice_count);
int adf_send_admin_tl_stop(struct adf_accel_dev *accel_dev);
int adf_send_admin_arb_query(struct adf_accel_dev *accel_dev, int cmd, u8 *svn);
int adf_send_admin_arb_commit(struct adf_accel_dev *accel_dev);
int adf_send_admin_kpt_init(struct adf_accel_dev *accel_dev, void *init_cfg,
size_t init_cfg_sz, dma_addr_t init_ptr);
#endif
@@ -0,0 +1,342 @@
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2014 - 2020 Intel Corporation */
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/completion.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include "adf_accel_devices.h"
#include "adf_common_drv.h"
#include "adf_pfvf_pf_msg.h"
struct adf_fatal_error_data {
struct adf_accel_dev *accel_dev;
struct work_struct work;
};
static struct workqueue_struct *device_reset_wq;
static struct workqueue_struct *device_sriov_wq;
static pci_ers_result_t reset_prepare(struct pci_dev *pdev)
{
struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev);
if (!accel_dev) {
pci_err(pdev, "Can't find acceleration device\n");
return PCI_ERS_RESULT_DISCONNECT;
}
if (!adf_dev_started(accel_dev))
return PCI_ERS_RESULT_CAN_RECOVER;
set_bit(ADF_STATUS_RESTARTING, &accel_dev->status);
if (accel_dev->hw_device->exit_arb) {
dev_dbg(&pdev->dev, "Disabling arbitration\n");
accel_dev->hw_device->exit_arb(accel_dev);
}
adf_dev_restarting_notify(accel_dev);
adf_dev_down(accel_dev);
return PCI_ERS_RESULT_NEED_RESET;
}
static pci_ers_result_t reset_done(struct pci_dev *pdev)
{
struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev);
int res;
if (!accel_dev) {
pci_err(pdev, "Can't find acceleration device\n");
return PCI_ERS_RESULT_DISCONNECT;
}
if (!adf_devmgr_in_reset(accel_dev))
goto reset_complete;
pci_restore_state(pdev);
res = adf_dev_up(accel_dev, false);
if (res && res != -EALREADY)
return PCI_ERS_RESULT_DISCONNECT;
adf_reenable_sriov(accel_dev);
adf_pf2vf_notify_restarted(accel_dev);
adf_dev_restarted_notify(accel_dev);
clear_bit(ADF_STATUS_RESTARTING, &accel_dev->status);
reset_complete:
pci_info(pdev, "Device reset completed successfully\n");
return PCI_ERS_RESULT_RECOVERED;
}
static pci_ers_result_t adf_error_detected(struct pci_dev *pdev,
pci_channel_state_t state)
{
struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev);
pci_info(pdev, "Acceleration driver hardware error detected.\n");
if (!accel_dev) {
pci_err(pdev, "Can't find acceleration device\n");
return PCI_ERS_RESULT_DISCONNECT;
}
if (state == pci_channel_io_perm_failure) {
pci_err(pdev, "Can't recover from device error\n");
return PCI_ERS_RESULT_DISCONNECT;
}
adf_error_notifier(accel_dev);
adf_pf2vf_notify_fatal_error(accel_dev);
return reset_prepare(pdev);
}
/* reset dev data */
struct adf_reset_dev_data {
int mode;
struct adf_accel_dev *accel_dev;
struct completion compl;
struct work_struct reset_work;
};
/* sriov dev data */
struct adf_sriov_dev_data {
struct adf_accel_dev *accel_dev;
struct completion compl;
struct work_struct sriov_work;
};
void adf_reset_sbr(struct adf_accel_dev *accel_dev)
{
struct pci_dev *pdev = accel_to_pci_dev(accel_dev);
struct pci_dev *parent = pdev->bus->self;
u16 bridge_ctl = 0;
if (!parent)
parent = pdev;
if (!pci_wait_for_pending_transaction(pdev))
pci_info(pdev, "Transaction still in progress. Proceeding\n");
pci_info(pdev, "Secondary bus reset\n");
pci_read_config_word(parent, PCI_BRIDGE_CONTROL, &bridge_ctl);
bridge_ctl |= PCI_BRIDGE_CTL_BUS_RESET;
pci_write_config_word(parent, PCI_BRIDGE_CONTROL, bridge_ctl);
msleep(100);
bridge_ctl &= ~PCI_BRIDGE_CTL_BUS_RESET;
pci_write_config_word(parent, PCI_BRIDGE_CONTROL, bridge_ctl);
msleep(100);
}
EXPORT_SYMBOL_GPL(adf_reset_sbr);
void adf_reset_flr(struct adf_accel_dev *accel_dev)
{
pcie_flr(accel_to_pci_dev(accel_dev));
}
EXPORT_SYMBOL_GPL(adf_reset_flr);
void adf_dev_restore(struct adf_accel_dev *accel_dev)
{
struct adf_hw_device_data *hw_device = accel_dev->hw_device;
struct pci_dev *pdev = accel_to_pci_dev(accel_dev);
if (hw_device->reset_device) {
dev_info(&GET_DEV(accel_dev), "Resetting device qat_dev%d\n",
accel_dev->accel_id);
hw_device->reset_device(accel_dev);
pci_restore_state(pdev);
}
}
void adf_set_bme(struct adf_accel_dev *accel_dev)
{
struct pci_dev *pdev = accel_to_pci_dev(accel_dev);
pci_set_master(pdev);
}
static void adf_device_sriov_worker(struct work_struct *work)
{
struct adf_sriov_dev_data *sriov_data =
container_of(work, struct adf_sriov_dev_data, sriov_work);
adf_reenable_sriov(sriov_data->accel_dev);
complete(&sriov_data->compl);
}
static void adf_device_reset_worker(struct work_struct *work)
{
struct adf_reset_dev_data *reset_data =
container_of(work, struct adf_reset_dev_data, reset_work);
struct adf_accel_dev *accel_dev = reset_data->accel_dev;
unsigned long wait_jiffies = msecs_to_jiffies(10000);
struct adf_sriov_dev_data sriov_data;
adf_dev_restarting_notify(accel_dev);
if (adf_dev_restart(accel_dev)) {
/* The device hanged and we can't restart it so stop here */
dev_err(&GET_DEV(accel_dev), "Restart device failed\n");
if (reset_data->mode == ADF_DEV_RESET_ASYNC)
kfree(reset_data);
WARN(1, "QAT: device restart failed. Device is unusable\n");
return;
}
sriov_data.accel_dev = accel_dev;
init_completion(&sriov_data.compl);
INIT_WORK(&sriov_data.sriov_work, adf_device_sriov_worker);
queue_work(device_sriov_wq, &sriov_data.sriov_work);
if (wait_for_completion_timeout(&sriov_data.compl, wait_jiffies))
adf_pf2vf_notify_restarted(accel_dev);
adf_dev_restarted_notify(accel_dev);
clear_bit(ADF_STATUS_RESTARTING, &accel_dev->status);
/* The dev is back alive. Notify the caller if in sync mode */
if (reset_data->mode == ADF_DEV_RESET_ASYNC)
kfree(reset_data);
else
complete(&reset_data->compl);
}
static int adf_dev_aer_schedule_reset(struct adf_accel_dev *accel_dev,
enum adf_dev_reset_mode mode)
{
struct adf_reset_dev_data *reset_data;
if (!adf_dev_started(accel_dev) ||
test_and_set_bit(ADF_STATUS_RESTARTING, &accel_dev->status))
return 0;
reset_data = kzalloc_obj(*reset_data);
if (!reset_data) {
clear_bit(ADF_STATUS_RESTARTING, &accel_dev->status);
return -ENOMEM;
}
reset_data->accel_dev = accel_dev;
init_completion(&reset_data->compl);
reset_data->mode = mode;
INIT_WORK(&reset_data->reset_work, adf_device_reset_worker);
queue_work(device_reset_wq, &reset_data->reset_work);
/* If in sync mode wait for the result */
if (mode == ADF_DEV_RESET_SYNC) {
int ret = 0;
/* Maximum device reset time is 10 seconds */
unsigned long wait_jiffies = msecs_to_jiffies(10000);
unsigned long timeout = wait_for_completion_timeout(
&reset_data->compl, wait_jiffies);
if (!timeout) {
dev_err(&GET_DEV(accel_dev),
"Reset device timeout expired\n");
cancel_work_sync(&reset_data->reset_work);
ret = -EFAULT;
}
kfree(reset_data);
return ret;
}
return 0;
}
static pci_ers_result_t adf_slot_reset(struct pci_dev *pdev)
{
return reset_done(pdev);
}
static void adf_resume(struct pci_dev *pdev)
{
pci_info(pdev, "Acceleration driver reset completed\n");
pci_info(pdev, "Device is up and running\n");
}
static void adf_reset_prepare(struct pci_dev *pdev)
{
reset_prepare(pdev);
}
static void adf_reset_done(struct pci_dev *pdev)
{
reset_done(pdev);
}
const struct pci_error_handlers adf_err_handler = {
.error_detected = adf_error_detected,
.slot_reset = adf_slot_reset,
.resume = adf_resume,
.reset_prepare = adf_reset_prepare,
.reset_done = adf_reset_done,
};
EXPORT_SYMBOL_GPL(adf_err_handler);
static int adf_dev_autoreset(struct adf_accel_dev *accel_dev)
{
if (accel_dev->autoreset_on_error)
return adf_dev_aer_schedule_reset(accel_dev, ADF_DEV_RESET_ASYNC);
return 0;
}
static void adf_notify_fatal_error_worker(struct work_struct *work)
{
struct adf_fatal_error_data *wq_data =
container_of(work, struct adf_fatal_error_data, work);
struct adf_accel_dev *accel_dev = wq_data->accel_dev;
struct adf_hw_device_data *hw_device = accel_dev->hw_device;
adf_error_notifier(accel_dev);
if (!accel_dev->is_vf) {
/* Disable arbitration to stop processing of new requests */
if (accel_dev->autoreset_on_error && hw_device->exit_arb)
hw_device->exit_arb(accel_dev);
if (accel_dev->pf.vf_info)
adf_pf2vf_notify_fatal_error(accel_dev);
adf_dev_autoreset(accel_dev);
}
kfree(wq_data);
}
int adf_notify_fatal_error(struct adf_accel_dev *accel_dev)
{
struct adf_fatal_error_data *wq_data;
wq_data = kzalloc_obj(*wq_data, GFP_ATOMIC);
if (!wq_data)
return -ENOMEM;
wq_data->accel_dev = accel_dev;
INIT_WORK(&wq_data->work, adf_notify_fatal_error_worker);
adf_misc_wq_queue_work(&wq_data->work);
return 0;
}
int adf_init_aer(void)
{
device_reset_wq = alloc_workqueue("qat_device_reset_wq",
WQ_MEM_RECLAIM | WQ_PERCPU, 0);
if (!device_reset_wq)
return -EFAULT;
device_sriov_wq = alloc_workqueue("qat_device_sriov_wq", WQ_PERCPU, 0);
if (!device_sriov_wq) {
destroy_workqueue(device_reset_wq);
device_reset_wq = NULL;
return -EFAULT;
}
return 0;
}
void adf_exit_aer(void)
{
if (device_reset_wq)
destroy_workqueue(device_reset_wq);
device_reset_wq = NULL;
if (device_sriov_wq)
destroy_workqueue(device_sriov_wq);
device_sriov_wq = NULL;
}
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2026 Intel Corporation */
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/kstrtox.h>
#include "adf_accel_devices.h"
#include "adf_admin.h"
#include "adf_anti_rb.h"
#include "adf_common_drv.h"
#include "icp_qat_fw_init_admin.h"
#define ADF_SVN_RETRY_MAX 60
int adf_anti_rb_commit(struct adf_accel_dev *accel_dev)
{
return adf_send_admin_arb_commit(accel_dev);
}
int adf_anti_rb_query(struct adf_accel_dev *accel_dev, enum anti_rb cmd, u8 *svn)
{
return adf_send_admin_arb_query(accel_dev, cmd, svn);
}
int adf_anti_rb_check(struct pci_dev *pdev)
{
struct adf_anti_rb_hw_data *anti_rb;
u32 svncheck_sts, cfc_svncheck_sts;
struct adf_accel_dev *accel_dev;
void __iomem *pmisc_addr;
accel_dev = adf_devmgr_pci_to_accel_dev(pdev);
if (!accel_dev)
return -EINVAL;
anti_rb = GET_ANTI_RB_DATA(accel_dev);
if (!anti_rb->anti_rb_enabled || !anti_rb->anti_rb_enabled(accel_dev))
return 0;
pmisc_addr = adf_get_pmisc_base(accel_dev);
cfc_svncheck_sts = ADF_CSR_RD(pmisc_addr, anti_rb->svncheck_offset);
svncheck_sts = FIELD_GET(ADF_SVN_STS_MASK, cfc_svncheck_sts);
switch (svncheck_sts) {
case ADF_SVN_NO_STS:
return 0;
case ADF_SVN_PASS_STS:
anti_rb->svncheck_retry = 0;
return 0;
case ADF_SVN_FAIL_STS:
dev_err(&GET_DEV(accel_dev), "Security Version Number failure\n");
return -EIO;
case ADF_SVN_RETRY_STS:
if (anti_rb->svncheck_retry++ >= ADF_SVN_RETRY_MAX) {
anti_rb->svncheck_retry = 0;
return -ETIMEDOUT;
}
msleep(ADF_SVN_RETRY_MS);
return -EAGAIN;
default:
dev_err(&GET_DEV(accel_dev), "Invalid SVN check status\n");
return -EINVAL;
}
}
@@ -0,0 +1,37 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright(c) 2026 Intel Corporation */
#ifndef ADF_ANTI_RB_H_
#define ADF_ANTI_RB_H_
#include <linux/types.h>
#define GET_ANTI_RB_DATA(accel_dev) (&(accel_dev)->hw_device->anti_rb_data)
#define ADF_SVN_NO_STS 0x00
#define ADF_SVN_PASS_STS 0x01
#define ADF_SVN_RETRY_STS 0x02
#define ADF_SVN_FAIL_STS 0x03
#define ADF_SVN_RETRY_MS 250
#define ADF_SVN_STS_MASK GENMASK(7, 0)
enum anti_rb {
ARB_ENFORCED_MIN_SVN,
ARB_PERMANENT_MIN_SVN,
ARB_ACTIVE_SVN,
};
struct adf_accel_dev;
struct pci_dev;
struct adf_anti_rb_hw_data {
bool (*anti_rb_enabled)(struct adf_accel_dev *accel_dev);
u32 svncheck_offset;
u32 svncheck_retry;
bool sysfs_added;
};
int adf_anti_rb_commit(struct adf_accel_dev *accel_dev);
int adf_anti_rb_query(struct adf_accel_dev *accel_dev, enum anti_rb cmd, u8 *svn);
int adf_anti_rb_check(struct pci_dev *pdev);
#endif /* ADF_ANTI_RB_H_ */
@@ -0,0 +1,238 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2025 Intel Corporation */
#define pr_fmt(fmt) "QAT: " fmt
#include <linux/bits.h>
#include <linux/dev_printk.h>
#include <linux/printk.h>
#include "adf_accel_devices.h"
#include "adf_bank_state.h"
#include "adf_common_drv.h"
/* Ring interrupt masks */
#define ADF_RP_INT_SRC_SEL_F_RISE_MASK GENMASK(1, 0)
#define ADF_RP_INT_SRC_SEL_F_FALL_MASK GENMASK(2, 0)
#define ADF_RP_INT_SRC_SEL_RANGE_WIDTH 4
static inline int check_stat(u32 (*op)(void __iomem *, u32), u32 expect_val,
const char *name, void __iomem *base, u32 bank)
{
u32 actual_val = op(base, bank);
if (expect_val == actual_val)
return 0;
pr_err("Fail to restore %s register. Expected %#x, actual %#x\n",
name, expect_val, actual_val);
return -EINVAL;
}
static void bank_state_save(struct adf_hw_csr_ops *ops, void __iomem *base,
u32 bank, struct adf_bank_state *state, u32 num_rings)
{
u32 i;
state->ringstat0 = ops->read_csr_stat(base, bank);
state->ringuostat = ops->read_csr_uo_stat(base, bank);
state->ringestat = ops->read_csr_e_stat(base, bank);
state->ringnestat = ops->read_csr_ne_stat(base, bank);
state->ringnfstat = ops->read_csr_nf_stat(base, bank);
state->ringfstat = ops->read_csr_f_stat(base, bank);
state->ringcstat0 = ops->read_csr_c_stat(base, bank);
state->iaintflagen = ops->read_csr_int_en(base, bank);
state->iaintflagreg = ops->read_csr_int_flag(base, bank);
state->iaintflagsrcsel0 = ops->read_csr_int_srcsel(base, bank);
state->iaintcolen = ops->read_csr_int_col_en(base, bank);
state->iaintcolctl = ops->read_csr_int_col_ctl(base, bank);
state->iaintflagandcolen = ops->read_csr_int_flag_and_col(base, bank);
state->ringexpstat = ops->read_csr_exp_stat(base, bank);
state->ringexpintenable = ops->read_csr_exp_int_en(base, bank);
state->ringsrvarben = ops->read_csr_ring_srv_arb_en(base, bank);
for (i = 0; i < num_rings; i++) {
state->rings[i].head = ops->read_csr_ring_head(base, bank, i);
state->rings[i].tail = ops->read_csr_ring_tail(base, bank, i);
state->rings[i].config = ops->read_csr_ring_config(base, bank, i);
state->rings[i].base = ops->read_csr_ring_base(base, bank, i);
}
}
static int bank_state_restore(struct adf_hw_csr_ops *ops, void __iomem *base,
u32 bank, struct adf_bank_state *state, u32 num_rings,
int tx_rx_gap)
{
u32 val, tmp_val, i;
int ret;
for (i = 0; i < num_rings; i++)
ops->write_csr_ring_base(base, bank, i, state->rings[i].base);
for (i = 0; i < num_rings; i++)
ops->write_csr_ring_config(base, bank, i, state->rings[i].config);
for (i = 0; i < num_rings / 2; i++) {
int tx = i * (tx_rx_gap + 1);
int rx = tx + tx_rx_gap;
ops->write_csr_ring_head(base, bank, tx, state->rings[tx].head);
ops->write_csr_ring_tail(base, bank, tx, state->rings[tx].tail);
/*
* The TX ring head needs to be updated again to make sure that
* the HW will not consider the ring as full when it is empty
* and the correct state flags are set to match the recovered state.
*/
if (state->ringestat & BIT(tx)) {
val = ops->read_csr_int_srcsel(base, bank);
val |= ADF_RP_INT_SRC_SEL_F_RISE_MASK;
ops->write_csr_int_srcsel_w_val(base, bank, val);
ops->write_csr_ring_head(base, bank, tx, state->rings[tx].head);
}
ops->write_csr_ring_tail(base, bank, rx, state->rings[rx].tail);
val = ops->read_csr_int_srcsel(base, bank);
val |= ADF_RP_INT_SRC_SEL_F_RISE_MASK << ADF_RP_INT_SRC_SEL_RANGE_WIDTH;
ops->write_csr_int_srcsel_w_val(base, bank, val);
ops->write_csr_ring_head(base, bank, rx, state->rings[rx].head);
val = ops->read_csr_int_srcsel(base, bank);
val |= ADF_RP_INT_SRC_SEL_F_FALL_MASK << ADF_RP_INT_SRC_SEL_RANGE_WIDTH;
ops->write_csr_int_srcsel_w_val(base, bank, val);
/*
* The RX ring tail needs to be updated again to make sure that
* the HW will not consider the ring as empty when it is full
* and the correct state flags are set to match the recovered state.
*/
if (state->ringfstat & BIT(rx))
ops->write_csr_ring_tail(base, bank, rx, state->rings[rx].tail);
}
ops->write_csr_int_flag_and_col(base, bank, state->iaintflagandcolen);
ops->write_csr_int_en(base, bank, state->iaintflagen);
ops->write_csr_int_col_en(base, bank, state->iaintcolen);
ops->write_csr_int_srcsel_w_val(base, bank, state->iaintflagsrcsel0);
ops->write_csr_exp_int_en(base, bank, state->ringexpintenable);
ops->write_csr_int_col_ctl(base, bank, state->iaintcolctl);
/*
* Verify whether any exceptions were raised during the bank save process.
* If exceptions occurred, the status and exception registers cannot
* be directly restored. Consequently, further restoration is not
* feasible, and the current state of the ring should be maintained.
*/
val = state->ringexpstat;
if (val) {
pr_info("Bank %u state not fully restored due to exception in saved state (%#x)\n",
bank, val);
return 0;
}
/* Ensure that the restoration process completed without exceptions */
tmp_val = ops->read_csr_exp_stat(base, bank);
if (tmp_val) {
pr_err("Bank %u restored with exception: %#x\n", bank, tmp_val);
return -EFAULT;
}
ops->write_csr_ring_srv_arb_en(base, bank, state->ringsrvarben);
/* Check that all ring statuses match the saved state. */
ret = check_stat(ops->read_csr_stat, state->ringstat0, "ringstat",
base, bank);
if (ret)
return ret;
ret = check_stat(ops->read_csr_e_stat, state->ringestat, "ringestat",
base, bank);
if (ret)
return ret;
ret = check_stat(ops->read_csr_ne_stat, state->ringnestat, "ringnestat",
base, bank);
if (ret)
return ret;
ret = check_stat(ops->read_csr_nf_stat, state->ringnfstat, "ringnfstat",
base, bank);
if (ret)
return ret;
ret = check_stat(ops->read_csr_f_stat, state->ringfstat, "ringfstat",
base, bank);
if (ret)
return ret;
ret = check_stat(ops->read_csr_c_stat, state->ringcstat0, "ringcstat",
base, bank);
if (ret)
return ret;
return 0;
}
/**
* adf_bank_state_save() - save state of bank-related registers
* @accel_dev: Pointer to the device structure
* @bank_number: Bank number
* @state: Pointer to bank state structure
*
* This function saves the state of a bank by reading the bank CSRs and
* writing them in the @state structure.
*
* Returns 0 on success, error code otherwise
*/
int adf_bank_state_save(struct adf_accel_dev *accel_dev, u32 bank_number,
struct adf_bank_state *state)
{
struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(accel_dev);
void __iomem *csr_base = adf_get_etr_base(accel_dev);
if (bank_number >= hw_data->num_banks || !state)
return -EINVAL;
dev_dbg(&GET_DEV(accel_dev), "Saving state of bank %d\n", bank_number);
bank_state_save(csr_ops, csr_base, bank_number, state,
hw_data->num_rings_per_bank);
return 0;
}
EXPORT_SYMBOL_GPL(adf_bank_state_save);
/**
* adf_bank_state_restore() - restore state of bank-related registers
* @accel_dev: Pointer to the device structure
* @bank_number: Bank number
* @state: Pointer to bank state structure
*
* This function attempts to restore the state of a bank by writing the
* bank CSRs to the values in the state structure.
*
* Returns 0 on success, error code otherwise
*/
int adf_bank_state_restore(struct adf_accel_dev *accel_dev, u32 bank_number,
struct adf_bank_state *state)
{
struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(accel_dev);
void __iomem *csr_base = adf_get_etr_base(accel_dev);
int ret;
if (bank_number >= hw_data->num_banks || !state)
return -EINVAL;
dev_dbg(&GET_DEV(accel_dev), "Restoring state of bank %d\n", bank_number);
ret = bank_state_restore(csr_ops, csr_base, bank_number, state,
hw_data->num_rings_per_bank, hw_data->tx_rx_gap);
if (ret)
dev_err(&GET_DEV(accel_dev),
"Unable to restore state of bank %d\n", bank_number);
return ret;
}
EXPORT_SYMBOL_GPL(adf_bank_state_restore);
@@ -0,0 +1,49 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright(c) 2025 Intel Corporation */
#ifndef ADF_BANK_STATE_H_
#define ADF_BANK_STATE_H_
#include <linux/types.h>
struct adf_accel_dev;
struct ring_config {
u64 base;
u32 config;
u32 head;
u32 tail;
u32 reserved0;
};
struct adf_bank_state {
u32 ringstat0;
u32 ringstat1;
u32 ringuostat;
u32 ringestat;
u32 ringnestat;
u32 ringnfstat;
u32 ringfstat;
u32 ringcstat0;
u32 ringcstat1;
u32 ringcstat2;
u32 ringcstat3;
u32 iaintflagen;
u32 iaintflagreg;
u32 iaintflagsrcsel0;
u32 iaintflagsrcsel1;
u32 iaintcolen;
u32 iaintcolctl;
u32 iaintflagandcolen;
u32 ringexpstat;
u32 ringexpintenable;
u32 ringsrvarben;
u32 reserved0;
struct ring_config rings[ADF_ETR_MAX_RINGS_PER_BANK];
};
int adf_bank_state_restore(struct adf_accel_dev *accel_dev, u32 bank_number,
struct adf_bank_state *state);
int adf_bank_state_save(struct adf_accel_dev *accel_dev, u32 bank_number,
struct adf_bank_state *state);
#endif
@@ -0,0 +1,374 @@
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2014 - 2020 Intel Corporation */
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/seq_file.h>
#include "adf_accel_devices.h"
#include "adf_cfg.h"
#include "adf_common_drv.h"
static DEFINE_MUTEX(qat_cfg_read_lock);
static void *qat_dev_cfg_start(struct seq_file *sfile, loff_t *pos)
{
struct adf_cfg_device_data *dev_cfg = sfile->private;
mutex_lock(&qat_cfg_read_lock);
return seq_list_start(&dev_cfg->sec_list, *pos);
}
static int qat_dev_cfg_show(struct seq_file *sfile, void *v)
{
struct list_head *list;
struct adf_cfg_section *sec =
list_entry(v, struct adf_cfg_section, list);
seq_printf(sfile, "[%s]\n", sec->name);
list_for_each(list, &sec->param_head) {
struct adf_cfg_key_val *ptr =
list_entry(list, struct adf_cfg_key_val, list);
seq_printf(sfile, "%s = %s\n", ptr->key, ptr->val);
}
return 0;
}
static void *qat_dev_cfg_next(struct seq_file *sfile, void *v, loff_t *pos)
{
struct adf_cfg_device_data *dev_cfg = sfile->private;
return seq_list_next(v, &dev_cfg->sec_list, pos);
}
static void qat_dev_cfg_stop(struct seq_file *sfile, void *v)
{
mutex_unlock(&qat_cfg_read_lock);
}
static const struct seq_operations qat_dev_cfg_sops = {
.start = qat_dev_cfg_start,
.next = qat_dev_cfg_next,
.stop = qat_dev_cfg_stop,
.show = qat_dev_cfg_show
};
DEFINE_SEQ_ATTRIBUTE(qat_dev_cfg);
/**
* adf_cfg_dev_add() - Create an acceleration device configuration table.
* @accel_dev: Pointer to acceleration device.
*
* Function creates a configuration table for the given acceleration device.
* The table stores device specific config values.
* To be used by QAT device specific drivers.
*
* Return: 0 on success, error code otherwise.
*/
int adf_cfg_dev_add(struct adf_accel_dev *accel_dev)
{
struct adf_cfg_device_data *dev_cfg_data;
dev_cfg_data = kzalloc_obj(*dev_cfg_data);
if (!dev_cfg_data)
return -ENOMEM;
INIT_LIST_HEAD(&dev_cfg_data->sec_list);
init_rwsem(&dev_cfg_data->lock);
accel_dev->cfg = dev_cfg_data;
return 0;
}
EXPORT_SYMBOL_GPL(adf_cfg_dev_add);
void adf_cfg_dev_dbgfs_add(struct adf_accel_dev *accel_dev)
{
struct adf_cfg_device_data *dev_cfg_data = accel_dev->cfg;
dev_cfg_data->debug = debugfs_create_file("dev_cfg", 0400,
accel_dev->debugfs_dir,
dev_cfg_data,
&qat_dev_cfg_fops);
}
void adf_cfg_dev_dbgfs_rm(struct adf_accel_dev *accel_dev)
{
struct adf_cfg_device_data *dev_cfg_data = accel_dev->cfg;
if (!dev_cfg_data)
return;
debugfs_remove(dev_cfg_data->debug);
dev_cfg_data->debug = NULL;
}
static void adf_cfg_section_del_all(struct list_head *head);
static void adf_cfg_section_del_all_except(struct list_head *head,
const char *section_name);
void adf_cfg_del_all_except(struct adf_accel_dev *accel_dev,
const char *section_name)
{
struct adf_cfg_device_data *dev_cfg_data = accel_dev->cfg;
down_write(&dev_cfg_data->lock);
adf_cfg_section_del_all_except(&dev_cfg_data->sec_list, section_name);
up_write(&dev_cfg_data->lock);
clear_bit(ADF_STATUS_CONFIGURED, &accel_dev->status);
}
/**
* adf_cfg_dev_remove() - Clears acceleration device configuration table.
* @accel_dev: Pointer to acceleration device.
*
* Function removes configuration table from the given acceleration device
* and frees all allocated memory.
* To be used by QAT device specific drivers.
*
* Return: void
*/
void adf_cfg_dev_remove(struct adf_accel_dev *accel_dev)
{
struct adf_cfg_device_data *dev_cfg_data = accel_dev->cfg;
if (!dev_cfg_data)
return;
down_write(&dev_cfg_data->lock);
adf_cfg_section_del_all(&dev_cfg_data->sec_list);
up_write(&dev_cfg_data->lock);
kfree(dev_cfg_data);
accel_dev->cfg = NULL;
}
EXPORT_SYMBOL_GPL(adf_cfg_dev_remove);
static void adf_cfg_keyval_add(struct adf_cfg_key_val *new,
struct adf_cfg_section *sec)
{
list_add_tail(&new->list, &sec->param_head);
}
static void adf_cfg_keyval_remove(const char *key, struct adf_cfg_section *sec)
{
struct list_head *head = &sec->param_head;
struct list_head *list_ptr, *tmp;
list_for_each_prev_safe(list_ptr, tmp, head) {
struct adf_cfg_key_val *ptr =
list_entry(list_ptr, struct adf_cfg_key_val, list);
if (strncmp(ptr->key, key, sizeof(ptr->key)))
continue;
list_del(list_ptr);
kfree(ptr);
break;
}
}
static void adf_cfg_keyval_del_all(struct list_head *head)
{
struct list_head *list_ptr, *tmp;
list_for_each_prev_safe(list_ptr, tmp, head) {
struct adf_cfg_key_val *ptr =
list_entry(list_ptr, struct adf_cfg_key_val, list);
list_del(list_ptr);
kfree(ptr);
}
}
static void adf_cfg_section_del_all(struct list_head *head)
{
struct adf_cfg_section *ptr;
struct list_head *list, *tmp;
list_for_each_prev_safe(list, tmp, head) {
ptr = list_entry(list, struct adf_cfg_section, list);
adf_cfg_keyval_del_all(&ptr->param_head);
list_del(list);
kfree(ptr);
}
}
static void adf_cfg_section_del_all_except(struct list_head *head,
const char *section_name)
{
struct list_head *list, *tmp;
struct adf_cfg_section *ptr;
list_for_each_prev_safe(list, tmp, head) {
ptr = list_entry(list, struct adf_cfg_section, list);
if (!strcmp(ptr->name, section_name))
continue;
adf_cfg_keyval_del_all(&ptr->param_head);
list_del(list);
kfree(ptr);
}
}
static struct adf_cfg_key_val *adf_cfg_key_value_find(struct adf_cfg_section *s,
const char *key)
{
struct list_head *list;
list_for_each(list, &s->param_head) {
struct adf_cfg_key_val *ptr =
list_entry(list, struct adf_cfg_key_val, list);
if (!strcmp(ptr->key, key))
return ptr;
}
return NULL;
}
static struct adf_cfg_section *adf_cfg_sec_find(struct adf_accel_dev *accel_dev,
const char *sec_name)
{
struct adf_cfg_device_data *cfg = accel_dev->cfg;
struct list_head *list;
list_for_each(list, &cfg->sec_list) {
struct adf_cfg_section *ptr =
list_entry(list, struct adf_cfg_section, list);
if (!strcmp(ptr->name, sec_name))
return ptr;
}
return NULL;
}
static int adf_cfg_key_val_get(struct adf_accel_dev *accel_dev,
const char *sec_name,
const char *key_name,
char *val)
{
struct adf_cfg_section *sec = adf_cfg_sec_find(accel_dev, sec_name);
struct adf_cfg_key_val *keyval = NULL;
if (sec)
keyval = adf_cfg_key_value_find(sec, key_name);
if (keyval) {
memcpy(val, keyval->val, ADF_CFG_MAX_VAL_LEN_IN_BYTES);
return 0;
}
return -ENODATA;
}
/**
* adf_cfg_add_key_value_param() - Add key-value config entry to config table.
* @accel_dev: Pointer to acceleration device.
* @section_name: Name of the section where the param will be added
* @key: The key string
* @val: Value pain for the given @key
* @type: Type - string, int or address
*
* Function adds configuration key - value entry in the appropriate section
* in the given acceleration device. If the key exists already, the value
* is updated.
* To be used by QAT device specific drivers.
*
* Return: 0 on success, error code otherwise.
*/
int adf_cfg_add_key_value_param(struct adf_accel_dev *accel_dev,
const char *section_name,
const char *key, const void *val,
enum adf_cfg_val_type type)
{
struct adf_cfg_device_data *cfg = accel_dev->cfg;
struct adf_cfg_key_val *key_val;
struct adf_cfg_section *section = adf_cfg_sec_find(accel_dev,
section_name);
char temp_val[ADF_CFG_MAX_VAL_LEN_IN_BYTES];
if (!section)
return -EFAULT;
key_val = kzalloc_obj(*key_val);
if (!key_val)
return -ENOMEM;
INIT_LIST_HEAD(&key_val->list);
strscpy(key_val->key, key, sizeof(key_val->key));
if (type == ADF_DEC) {
snprintf(key_val->val, ADF_CFG_MAX_VAL_LEN_IN_BYTES,
"%ld", (*((long *)val)));
} else if (type == ADF_STR) {
strscpy(key_val->val, (char *)val, sizeof(key_val->val));
} else if (type == ADF_HEX) {
snprintf(key_val->val, ADF_CFG_MAX_VAL_LEN_IN_BYTES,
"0x%lx", (unsigned long)val);
} else {
dev_err(&GET_DEV(accel_dev), "Unknown type given.\n");
kfree(key_val);
return -EINVAL;
}
key_val->type = type;
/* Add the key-value pair as below policy:
* 1. if the key doesn't exist, add it;
* 2. if the key already exists with a different value then update it
* to the new value (the key is deleted and the newly created
* key_val containing the new value is added to the database);
* 3. if the key exists with the same value, then return without doing
* anything (the newly created key_val is freed).
*/
down_write(&cfg->lock);
if (!adf_cfg_key_val_get(accel_dev, section_name, key, temp_val)) {
if (strncmp(temp_val, key_val->val, sizeof(temp_val))) {
adf_cfg_keyval_remove(key, section);
} else {
kfree(key_val);
goto out;
}
}
adf_cfg_keyval_add(key_val, section);
out:
up_write(&cfg->lock);
return 0;
}
EXPORT_SYMBOL_GPL(adf_cfg_add_key_value_param);
/**
* adf_cfg_section_add() - Add config section entry to config table.
* @accel_dev: Pointer to acceleration device.
* @name: Name of the section
*
* Function adds configuration section where key - value entries
* will be stored.
* To be used by QAT device specific drivers.
*
* Return: 0 on success, error code otherwise.
*/
int adf_cfg_section_add(struct adf_accel_dev *accel_dev, const char *name)
{
struct adf_cfg_device_data *cfg = accel_dev->cfg;
struct adf_cfg_section *sec = adf_cfg_sec_find(accel_dev, name);
if (sec)
return 0;
sec = kzalloc_obj(*sec);
if (!sec)
return -ENOMEM;
strscpy(sec->name, name, sizeof(sec->name));
INIT_LIST_HEAD(&sec->param_head);
down_write(&cfg->lock);
list_add_tail(&sec->list, &cfg->sec_list);
up_write(&cfg->lock);
return 0;
}
EXPORT_SYMBOL_GPL(adf_cfg_section_add);
int adf_cfg_get_param_value(struct adf_accel_dev *accel_dev,
const char *section, const char *name,
char *value)
{
struct adf_cfg_device_data *cfg = accel_dev->cfg;
int ret;
down_read(&cfg->lock);
ret = adf_cfg_key_val_get(accel_dev, section, name, value);
up_read(&cfg->lock);
return ret;
}
EXPORT_SYMBOL_GPL(adf_cfg_get_param_value);
@@ -0,0 +1,46 @@
/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */
/* Copyright(c) 2014 - 2020 Intel Corporation */
#ifndef ADF_CFG_H_
#define ADF_CFG_H_
#include <linux/list.h>
#include <linux/rwsem.h>
#include <linux/debugfs.h>
#include "adf_accel_devices.h"
#include "adf_cfg_common.h"
#include "adf_cfg_strings.h"
struct adf_cfg_key_val {
char key[ADF_CFG_MAX_KEY_LEN_IN_BYTES];
char val[ADF_CFG_MAX_VAL_LEN_IN_BYTES];
enum adf_cfg_val_type type;
struct list_head list;
};
struct adf_cfg_section {
char name[ADF_CFG_MAX_SECTION_LEN_IN_BYTES];
struct list_head list;
struct list_head param_head;
};
struct adf_cfg_device_data {
struct list_head sec_list;
struct dentry *debug;
struct rw_semaphore lock;
};
int adf_cfg_dev_add(struct adf_accel_dev *accel_dev);
void adf_cfg_dev_remove(struct adf_accel_dev *accel_dev);
void adf_cfg_dev_dbgfs_add(struct adf_accel_dev *accel_dev);
void adf_cfg_dev_dbgfs_rm(struct adf_accel_dev *accel_dev);
int adf_cfg_section_add(struct adf_accel_dev *accel_dev, const char *name);
void adf_cfg_del_all_except(struct adf_accel_dev *accel_dev,
const char *section_name);
int adf_cfg_add_key_value_param(struct adf_accel_dev *accel_dev,
const char *section_name,
const char *key, const void *val,
enum adf_cfg_val_type type);
int adf_cfg_get_param_value(struct adf_accel_dev *accel_dev,
const char *section, const char *name, char *value);
#endif
@@ -0,0 +1,47 @@
/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */
/* Copyright(c) 2014 - 2020 Intel Corporation */
#ifndef ADF_CFG_COMMON_H_
#define ADF_CFG_COMMON_H_
#include <linux/types.h>
#define ADF_CFG_MAX_STR_LEN 64
#define ADF_CFG_MAX_KEY_LEN_IN_BYTES ADF_CFG_MAX_STR_LEN
#define ADF_CFG_MAX_VAL_LEN_IN_BYTES ADF_CFG_MAX_STR_LEN
#define ADF_CFG_MAX_SECTION_LEN_IN_BYTES ADF_CFG_MAX_STR_LEN
#define ADF_MAX_DEVICES (32 * 32)
#define ADF_DEVS_ARRAY_SIZE BITS_TO_LONGS(ADF_MAX_DEVICES)
#define ADF_CFG_SERV_RING_PAIR_0_SHIFT 0
#define ADF_CFG_SERV_RING_PAIR_1_SHIFT 3
#define ADF_CFG_SERV_RING_PAIR_2_SHIFT 6
#define ADF_CFG_SERV_RING_PAIR_3_SHIFT 9
enum adf_cfg_service_type {
UNUSED = 0,
CRYPTO,
COMP,
SYM,
ASYM,
DECOMP,
USED
};
enum adf_cfg_val_type {
ADF_DEC,
ADF_HEX,
ADF_STR
};
enum adf_device_type {
DEV_UNKNOWN = 0,
DEV_DH895XCC,
DEV_DH895XCCVF,
DEV_C62X,
DEV_C62XVF,
DEV_C3XXX,
DEV_C3XXXVF,
DEV_4XXX,
DEV_420XX,
DEV_6XXX,
};
#endif
@@ -0,0 +1,211 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2023 Intel Corporation */
#include <linux/array_size.h>
#include <linux/bitops.h>
#include <linux/export.h>
#include <linux/pci.h>
#include <linux/string.h>
#include "adf_cfg.h"
#include "adf_cfg_common.h"
#include "adf_cfg_services.h"
#include "adf_cfg_strings.h"
static const char *const adf_cfg_services[] = {
[SVC_ASYM] = ADF_CFG_ASYM,
[SVC_SYM] = ADF_CFG_SYM,
[SVC_DC] = ADF_CFG_DC,
[SVC_DCC] = ADF_CFG_DCC,
[SVC_DECOMP] = ADF_CFG_DECOMP,
};
/*
* Ensure that the size of the array matches the number of services,
* SVC_COUNT, that is used to size the bitmap.
*/
static_assert(ARRAY_SIZE(adf_cfg_services) == SVC_COUNT);
/*
* Ensure that the maximum number of concurrent services that can be
* enabled on a device is less than or equal to the number of total
* supported services.
*/
static_assert(ARRAY_SIZE(adf_cfg_services) >= MAX_NUM_CONCURR_SVC);
/*
* Ensure that the number of services fit a single unsigned long, as each
* service is represented by a bit in the mask.
*/
static_assert(BITS_PER_LONG >= SVC_COUNT);
/*
* Ensure that size of the concatenation of all service strings is smaller
* than the size of the buffer that will contain them.
*/
static_assert(sizeof(ADF_CFG_SYM ADF_SERVICES_DELIMITER
ADF_CFG_ASYM ADF_SERVICES_DELIMITER
ADF_CFG_DC ADF_SERVICES_DELIMITER
ADF_CFG_DECOMP ADF_SERVICES_DELIMITER
ADF_CFG_DCC) < ADF_CFG_MAX_VAL_LEN_IN_BYTES);
static int adf_service_string_to_mask(struct adf_accel_dev *accel_dev, const char *buf,
size_t len, unsigned long *out_mask)
{
struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
char services[ADF_CFG_MAX_VAL_LEN_IN_BYTES] = { };
unsigned long mask = 0;
char *substr, *token;
int id, num_svc = 0;
if (len > ADF_CFG_MAX_VAL_LEN_IN_BYTES - 1)
return -EINVAL;
strscpy(services, buf, ADF_CFG_MAX_VAL_LEN_IN_BYTES);
substr = services;
while ((token = strsep(&substr, ADF_SERVICES_DELIMITER))) {
id = sysfs_match_string(adf_cfg_services, token);
if (id < 0)
return id;
if (test_and_set_bit(id, &mask))
return -EINVAL;
if (num_svc++ == MAX_NUM_CONCURR_SVC)
return -EINVAL;
}
if (hw_data->services_supported && !hw_data->services_supported(mask))
return -EINVAL;
*out_mask = mask;
return 0;
}
static int adf_service_mask_to_string(unsigned long mask, char *buf, size_t len)
{
int offset = 0;
int bit;
if (len < ADF_CFG_MAX_VAL_LEN_IN_BYTES)
return -ENOSPC;
for_each_set_bit(bit, &mask, SVC_COUNT) {
if (offset)
offset += scnprintf(buf + offset, len - offset,
ADF_SERVICES_DELIMITER "%s", adf_cfg_services[bit]);
else
offset += scnprintf(buf, len, "%s", adf_cfg_services[bit]);
}
return 0;
}
int adf_parse_service_string(struct adf_accel_dev *accel_dev, const char *in,
size_t in_len, char *out, size_t out_len)
{
unsigned long mask;
int ret;
ret = adf_service_string_to_mask(accel_dev, in, in_len, &mask);
if (ret)
return ret;
if (!mask)
return -EINVAL;
return adf_service_mask_to_string(mask, out, out_len);
}
int adf_get_service_mask(struct adf_accel_dev *accel_dev, unsigned long *mask)
{
char services[ADF_CFG_MAX_VAL_LEN_IN_BYTES] = { };
size_t len;
int ret;
ret = adf_cfg_get_param_value(accel_dev, ADF_GENERAL_SEC,
ADF_SERVICES_ENABLED, services);
if (ret) {
dev_err(&GET_DEV(accel_dev), "%s param not found\n",
ADF_SERVICES_ENABLED);
return ret;
}
len = strnlen(services, ADF_CFG_MAX_VAL_LEN_IN_BYTES);
ret = adf_service_string_to_mask(accel_dev, services, len, mask);
if (ret)
dev_err(&GET_DEV(accel_dev), "Invalid value of %s param: %s\n",
ADF_SERVICES_ENABLED, services);
return ret;
}
EXPORT_SYMBOL_GPL(adf_get_service_mask);
int adf_get_service_enabled(struct adf_accel_dev *accel_dev)
{
unsigned long mask;
int ret;
ret = adf_get_service_mask(accel_dev, &mask);
if (ret)
return ret;
if (test_bit(SVC_SYM, &mask) && test_bit(SVC_ASYM, &mask))
return SVC_SYM_ASYM;
if (test_bit(SVC_SYM, &mask) && test_bit(SVC_DC, &mask))
return SVC_SYM_DC;
if (test_bit(SVC_ASYM, &mask) && test_bit(SVC_DC, &mask))
return SVC_ASYM_DC;
if (test_bit(SVC_SYM, &mask))
return SVC_SYM;
if (test_bit(SVC_ASYM, &mask))
return SVC_ASYM;
if (test_bit(SVC_DC, &mask))
return SVC_DC;
if (test_bit(SVC_DECOMP, &mask))
return SVC_DECOMP;
if (test_bit(SVC_DCC, &mask))
return SVC_DCC;
return -EINVAL;
}
EXPORT_SYMBOL_GPL(adf_get_service_enabled);
enum adf_cfg_service_type adf_srv_to_cfg_svc_type(enum adf_base_services svc)
{
switch (svc) {
case SVC_ASYM:
return ASYM;
case SVC_SYM:
return SYM;
case SVC_DC:
return COMP;
case SVC_DECOMP:
return DECOMP;
default:
return UNUSED;
}
}
bool adf_is_service_enabled(struct adf_accel_dev *accel_dev, enum adf_base_services svc)
{
enum adf_cfg_service_type arb_srv = adf_srv_to_cfg_svc_type(svc);
struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
u8 rps_per_bundle = hw_data->num_banks_per_vf;
int i;
for (i = 0; i < rps_per_bundle; i++) {
if (GET_SRV_TYPE(accel_dev, i) == arb_srv)
return true;
}
return false;
}
@@ -0,0 +1,44 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright(c) 2023 Intel Corporation */
#ifndef _ADF_CFG_SERVICES_H_
#define _ADF_CFG_SERVICES_H_
#include "adf_cfg_strings.h"
struct adf_accel_dev;
enum adf_base_services {
SVC_ASYM = 0,
SVC_SYM,
SVC_DC,
SVC_DECOMP,
SVC_BASE_COUNT
};
enum adf_extended_services {
SVC_DCC = SVC_BASE_COUNT,
SVC_COUNT
};
enum adf_composed_services {
SVC_SYM_ASYM = SVC_COUNT,
SVC_SYM_DC,
SVC_ASYM_DC,
};
enum {
ADF_ONE_SERVICE = 1,
ADF_TWO_SERVICES,
ADF_THREE_SERVICES,
};
#define MAX_NUM_CONCURR_SVC ADF_THREE_SERVICES
int adf_parse_service_string(struct adf_accel_dev *accel_dev, const char *in,
size_t in_len, char *out, size_t out_len);
int adf_get_service_enabled(struct adf_accel_dev *accel_dev);
int adf_get_service_mask(struct adf_accel_dev *accel_dev, unsigned long *mask);
enum adf_cfg_service_type adf_srv_to_cfg_svc_type(enum adf_base_services svc);
bool adf_is_service_enabled(struct adf_accel_dev *accel_dev, enum adf_base_services svc);
#endif
@@ -0,0 +1,51 @@
/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */
/* Copyright(c) 2014 - 2020 Intel Corporation */
#ifndef ADF_CFG_STRINGS_H_
#define ADF_CFG_STRINGS_H_
#define ADF_GENERAL_SEC "GENERAL"
#define ADF_KERNEL_SEC "KERNEL"
#define ADF_ACCEL_SEC "Accelerator"
#define ADF_NUM_CY "NumberCyInstances"
#define ADF_NUM_DC "NumberDcInstances"
#define ADF_RING_SYM_SIZE "NumConcurrentSymRequests"
#define ADF_RING_ASYM_SIZE "NumConcurrentAsymRequests"
#define ADF_RING_DC_SIZE "NumConcurrentRequests"
#define ADF_RING_ASYM_TX "RingAsymTx"
#define ADF_RING_SYM_TX "RingSymTx"
#define ADF_RING_ASYM_RX "RingAsymRx"
#define ADF_RING_SYM_RX "RingSymRx"
#define ADF_RING_DC_TX "RingTx"
#define ADF_RING_DC_RX "RingRx"
#define ADF_ETRMGR_BANK "Bank"
#define ADF_RING_SYM_BANK_NUM "BankSymNumber"
#define ADF_RING_ASYM_BANK_NUM "BankAsymNumber"
#define ADF_RING_DC_BANK_NUM "BankDcNumber"
#define ADF_CY "Cy"
#define ADF_DC "Dc"
#define ADF_CFG_DC "dc"
#define ADF_CFG_DECOMP "decomp"
#define ADF_CFG_CY "sym;asym"
#define ADF_CFG_SYM "sym"
#define ADF_CFG_ASYM "asym"
#define ADF_CFG_DCC "dcc"
#define ADF_SERVICES_ENABLED "ServicesEnabled"
#define ADF_SERVICES_DELIMITER ";"
#define ADF_PM_IDLE_SUPPORT "PmIdleSupport"
#define ADF_ETRMGR_COALESCING_ENABLED "InterruptCoalescingEnabled"
#define ADF_ETRMGR_COALESCING_ENABLED_FORMAT \
ADF_ETRMGR_BANK "%d" ADF_ETRMGR_COALESCING_ENABLED
#define ADF_ETRMGR_COALESCE_TIMER "InterruptCoalescingTimerNs"
#define ADF_ETRMGR_COALESCE_TIMER_FORMAT \
ADF_ETRMGR_BANK "%d" ADF_ETRMGR_COALESCE_TIMER
#define ADF_ETRMGR_COALESCING_MSG_ENABLED "InterruptCoalescingNumResponses"
#define ADF_ETRMGR_COALESCING_MSG_ENABLED_FORMAT \
ADF_ETRMGR_BANK "%d" ADF_ETRMGR_COALESCING_MSG_ENABLED
#define ADF_ETRMGR_CORE_AFFINITY "CoreAffinity"
#define ADF_ETRMGR_CORE_AFFINITY_FORMAT \
ADF_ETRMGR_BANK "%d" ADF_ETRMGR_CORE_AFFINITY
#define ADF_ACCEL_STR "Accelerator%d"
#define ADF_HEARTBEAT_TIMER "HeartbeatTimer"
#define ADF_SRIOV_ENABLED "SriovEnabled"
#endif
@@ -0,0 +1,135 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2023 Intel Corporation */
#include <linux/delay.h>
#include <linux/dev_printk.h>
#include <linux/export.h>
#include <linux/math.h>
#include <linux/minmax.h>
#include <linux/time64.h>
#include <linux/types.h>
#include <linux/units.h>
#include <asm/errno.h>
#include "adf_admin.h"
#include "adf_accel_devices.h"
#include "adf_clock.h"
#include "adf_common_drv.h"
#define MEASURE_CLOCK_RETRIES 10
#define MEASURE_CLOCK_DELAY_US 10000
#define ME_CLK_DIVIDER 16
#define MEASURE_CLOCK_DELTA_THRESHOLD_US 100
static inline u64 timespec_to_us(const struct timespec64 *ts)
{
return (u64)DIV_ROUND_CLOSEST_ULL(timespec64_to_ns(ts), NSEC_PER_USEC);
}
static inline u64 timespec_to_ms(const struct timespec64 *ts)
{
return (u64)DIV_ROUND_CLOSEST_ULL(timespec64_to_ns(ts), NSEC_PER_MSEC);
}
u64 adf_clock_get_current_time(void)
{
struct timespec64 ts;
ktime_get_real_ts64(&ts);
return timespec_to_ms(&ts);
}
static int measure_clock(struct adf_accel_dev *accel_dev, u32 *frequency)
{
struct timespec64 ts1, ts2, ts3, ts4;
u64 timestamp1, timestamp2, temp;
u32 delta_us, tries;
int ret;
tries = MEASURE_CLOCK_RETRIES;
do {
ktime_get_real_ts64(&ts1);
ret = adf_get_fw_timestamp(accel_dev, &timestamp1);
if (ret) {
dev_err(&GET_DEV(accel_dev),
"Failed to get fw timestamp\n");
return ret;
}
ktime_get_real_ts64(&ts2);
delta_us = timespec_to_us(&ts2) - timespec_to_us(&ts1);
} while (delta_us > MEASURE_CLOCK_DELTA_THRESHOLD_US && --tries);
if (!tries) {
dev_err(&GET_DEV(accel_dev), "Excessive clock measure delay\n");
return -ETIMEDOUT;
}
fsleep(MEASURE_CLOCK_DELAY_US);
tries = MEASURE_CLOCK_RETRIES;
do {
ktime_get_real_ts64(&ts3);
if (adf_get_fw_timestamp(accel_dev, &timestamp2)) {
dev_err(&GET_DEV(accel_dev),
"Failed to get fw timestamp\n");
return -EIO;
}
ktime_get_real_ts64(&ts4);
delta_us = timespec_to_us(&ts4) - timespec_to_us(&ts3);
} while (delta_us > MEASURE_CLOCK_DELTA_THRESHOLD_US && --tries);
if (!tries) {
dev_err(&GET_DEV(accel_dev), "Excessive clock measure delay\n");
return -ETIMEDOUT;
}
delta_us = timespec_to_us(&ts3) - timespec_to_us(&ts1);
if (!delta_us)
return -EINVAL;
temp = (timestamp2 - timestamp1) * ME_CLK_DIVIDER * 10;
temp = DIV_ROUND_CLOSEST_ULL(temp, delta_us);
/*
* Enclose the division to allow the preprocessor to precalculate it,
* and avoid promoting r-value to 64-bit before division.
*/
*frequency = temp * (HZ_PER_MHZ / 10);
return 0;
}
/**
* adf_dev_measure_clock() - measures device clock frequency
* @accel_dev: Pointer to acceleration device.
* @frequency: Pointer to variable where result will be stored
* @min: Minimal allowed frequency value
* @max: Maximal allowed frequency value
*
* If the measurement result will go beyond the min/max thresholds the value
* will take the value of the crossed threshold.
*
* This algorithm compares the device firmware timestamp with the kernel
* timestamp. So we can't expect too high accuracy from this measurement.
*
* Return:
* * 0 - measurement succeed
* * -ETIMEDOUT - measurement failed
*/
int adf_dev_measure_clock(struct adf_accel_dev *accel_dev,
u32 *frequency, u32 min, u32 max)
{
int ret;
u32 freq;
ret = measure_clock(accel_dev, &freq);
if (ret)
return ret;
*frequency = clamp(freq, min, max);
if (*frequency != freq)
dev_warn(&GET_DEV(accel_dev),
"Measured clock %d Hz is out of range, assuming %d\n",
freq, *frequency);
return 0;
}
EXPORT_SYMBOL_GPL(adf_dev_measure_clock);
@@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright(c) 2023 Intel Corporation */
#ifndef ADF_CLOCK_H
#define ADF_CLOCK_H
#include <linux/types.h>
struct adf_accel_dev;
int adf_dev_measure_clock(struct adf_accel_dev *accel_dev, u32 *frequency,
u32 min, u32 max);
u64 adf_clock_get_current_time(void);
#endif
@@ -0,0 +1,299 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2023 Intel Corporation */
#include <linux/bitfield.h>
#include <linux/debugfs.h>
#include <linux/kernel.h>
#include "adf_accel_devices.h"
#include "adf_admin.h"
#include "adf_common_drv.h"
#include "adf_cnv_dbgfs.h"
#include "qat_compression.h"
#define CNV_DEBUGFS_FILENAME "cnv_errors"
#define CNV_MIN_PADDING 16
#define CNV_ERR_INFO_MASK GENMASK(11, 0)
#define CNV_ERR_TYPE_MASK GENMASK(15, 12)
#define CNV_SLICE_ERR_SIGN_BIT_INDEX 7
#define CNV_DELTA_ERR_SIGN_BIT_INDEX 11
enum cnv_error_type {
CNV_ERR_TYPE_NONE,
CNV_ERR_TYPE_CHECKSUM,
CNV_ERR_TYPE_DECOMP_PRODUCED_LENGTH,
CNV_ERR_TYPE_DECOMPRESSION,
CNV_ERR_TYPE_TRANSLATION,
CNV_ERR_TYPE_DECOMP_CONSUMED_LENGTH,
CNV_ERR_TYPE_UNKNOWN,
CNV_ERR_TYPES_COUNT
};
#define CNV_ERROR_TYPE_GET(latest_err) \
min_t(u16, u16_get_bits(latest_err, CNV_ERR_TYPE_MASK), CNV_ERR_TYPE_UNKNOWN)
#define CNV_GET_DELTA_ERR_INFO(latest_error) \
sign_extend32(latest_error, CNV_DELTA_ERR_SIGN_BIT_INDEX)
#define CNV_GET_SLICE_ERR_INFO(latest_error) \
sign_extend32(latest_error, CNV_SLICE_ERR_SIGN_BIT_INDEX)
#define CNV_GET_DEFAULT_ERR_INFO(latest_error) \
u16_get_bits(latest_error, CNV_ERR_INFO_MASK)
enum cnv_fields {
CNV_ERR_COUNT,
CNV_LATEST_ERR,
CNV_FIELDS_COUNT
};
static const char * const cnv_field_names[CNV_FIELDS_COUNT] = {
[CNV_ERR_COUNT] = "Total Errors",
[CNV_LATEST_ERR] = "Last Error",
};
static const char * const cnv_error_names[CNV_ERR_TYPES_COUNT] = {
[CNV_ERR_TYPE_NONE] = "No Error",
[CNV_ERR_TYPE_CHECKSUM] = "Checksum Error",
[CNV_ERR_TYPE_DECOMP_PRODUCED_LENGTH] = "Length Error-P",
[CNV_ERR_TYPE_DECOMPRESSION] = "Decomp Error",
[CNV_ERR_TYPE_TRANSLATION] = "Xlat Error",
[CNV_ERR_TYPE_DECOMP_CONSUMED_LENGTH] = "Length Error-C",
[CNV_ERR_TYPE_UNKNOWN] = "Unknown Error",
};
struct ae_cnv_errors {
u16 ae;
u16 err_cnt;
u16 latest_err;
bool is_comp_ae;
};
struct cnv_err_stats {
u16 ae_count;
struct ae_cnv_errors ae_cnv_errors[];
};
static s16 get_err_info(u8 error_type, u16 latest)
{
switch (error_type) {
case CNV_ERR_TYPE_DECOMP_PRODUCED_LENGTH:
case CNV_ERR_TYPE_DECOMP_CONSUMED_LENGTH:
return CNV_GET_DELTA_ERR_INFO(latest);
case CNV_ERR_TYPE_DECOMPRESSION:
case CNV_ERR_TYPE_TRANSLATION:
return CNV_GET_SLICE_ERR_INFO(latest);
default:
return CNV_GET_DEFAULT_ERR_INFO(latest);
}
}
static void *qat_cnv_errors_seq_start(struct seq_file *sfile, loff_t *pos)
{
struct cnv_err_stats *err_stats = sfile->private;
if (*pos == 0)
return SEQ_START_TOKEN;
if (*pos > err_stats->ae_count)
return NULL;
return &err_stats->ae_cnv_errors[*pos - 1];
}
static void *qat_cnv_errors_seq_next(struct seq_file *sfile, void *v,
loff_t *pos)
{
struct cnv_err_stats *err_stats = sfile->private;
(*pos)++;
if (*pos > err_stats->ae_count)
return NULL;
return &err_stats->ae_cnv_errors[*pos - 1];
}
static void qat_cnv_errors_seq_stop(struct seq_file *sfile, void *v)
{
}
static int qat_cnv_errors_seq_show(struct seq_file *sfile, void *v)
{
struct ae_cnv_errors *ae_errors;
unsigned int i;
s16 err_info;
u8 err_type;
if (v == SEQ_START_TOKEN) {
seq_puts(sfile, "AE ");
for (i = 0; i < CNV_FIELDS_COUNT; ++i)
seq_printf(sfile, " %*s", CNV_MIN_PADDING,
cnv_field_names[i]);
} else {
ae_errors = v;
if (!ae_errors->is_comp_ae)
return 0;
err_type = CNV_ERROR_TYPE_GET(ae_errors->latest_err);
err_info = get_err_info(err_type, ae_errors->latest_err);
seq_printf(sfile, "%d:", ae_errors->ae);
seq_printf(sfile, " %*d", CNV_MIN_PADDING, ae_errors->err_cnt);
seq_printf(sfile, "%*s [%d]", CNV_MIN_PADDING,
cnv_error_names[err_type], err_info);
}
seq_putc(sfile, '\n');
return 0;
}
static const struct seq_operations qat_cnv_errors_sops = {
.start = qat_cnv_errors_seq_start,
.next = qat_cnv_errors_seq_next,
.stop = qat_cnv_errors_seq_stop,
.show = qat_cnv_errors_seq_show,
};
/**
* cnv_err_stats_alloc() - Get CNV stats for the provided device.
* @accel_dev: Pointer to a QAT acceleration device
*
* Allocates and populates table of CNV errors statistics for each non-admin AE
* available through the supplied acceleration device. The caller becomes the
* owner of such memory and is responsible for the deallocation through a call
* to kfree().
*
* Returns: a pointer to a dynamically allocated struct cnv_err_stats on success
* or a negative value on error.
*/
static struct cnv_err_stats *cnv_err_stats_alloc(struct adf_accel_dev *accel_dev)
{
struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
struct cnv_err_stats *err_stats;
unsigned long ae_count;
unsigned long ae_mask;
size_t err_stats_size;
unsigned long ae;
unsigned int i;
u16 latest_err;
u16 err_cnt;
int ret;
if (!adf_dev_started(accel_dev)) {
dev_err(&GET_DEV(accel_dev), "QAT Device not started\n");
return ERR_PTR(-EBUSY);
}
/* Ignore the admin AEs */
ae_mask = hw_data->ae_mask & ~hw_data->admin_ae_mask;
ae_count = hweight_long(ae_mask);
if (unlikely(!ae_count))
return ERR_PTR(-EINVAL);
err_stats_size = struct_size(err_stats, ae_cnv_errors, ae_count);
err_stats = kmalloc(err_stats_size, GFP_KERNEL);
if (!err_stats)
return ERR_PTR(-ENOMEM);
err_stats->ae_count = ae_count;
i = 0;
for_each_set_bit(ae, &ae_mask, GET_MAX_ACCELENGINES(accel_dev)) {
ret = adf_get_cnv_stats(accel_dev, ae, &err_cnt, &latest_err);
if (ret) {
dev_dbg(&GET_DEV(accel_dev),
"Failed to get CNV stats for ae %ld, [%d].\n",
ae, ret);
err_stats->ae_cnv_errors[i++].is_comp_ae = false;
continue;
}
err_stats->ae_cnv_errors[i].is_comp_ae = true;
err_stats->ae_cnv_errors[i].latest_err = latest_err;
err_stats->ae_cnv_errors[i].err_cnt = err_cnt;
err_stats->ae_cnv_errors[i].ae = ae;
i++;
}
return err_stats;
}
static int qat_cnv_errors_file_open(struct inode *inode, struct file *file)
{
struct adf_accel_dev *accel_dev = inode->i_private;
struct seq_file *cnv_errors_seq_file;
struct cnv_err_stats *cnv_err_stats;
int ret;
cnv_err_stats = cnv_err_stats_alloc(accel_dev);
if (IS_ERR(cnv_err_stats))
return PTR_ERR(cnv_err_stats);
ret = seq_open(file, &qat_cnv_errors_sops);
if (unlikely(ret)) {
kfree(cnv_err_stats);
return ret;
}
cnv_errors_seq_file = file->private_data;
cnv_errors_seq_file->private = cnv_err_stats;
return ret;
}
static int qat_cnv_errors_file_release(struct inode *inode, struct file *file)
{
struct seq_file *cnv_errors_seq_file = file->private_data;
kfree(cnv_errors_seq_file->private);
cnv_errors_seq_file->private = NULL;
return seq_release(inode, file);
}
static const struct file_operations qat_cnv_fops = {
.owner = THIS_MODULE,
.open = qat_cnv_errors_file_open,
.read = seq_read,
.llseek = seq_lseek,
.release = qat_cnv_errors_file_release,
};
static ssize_t no_comp_file_read(struct file *f, char __user *buf, size_t count,
loff_t *pos)
{
char *file_msg = "No engine configured for comp\n";
return simple_read_from_buffer(buf, count, pos, file_msg,
strlen(file_msg));
}
static const struct file_operations qat_cnv_no_comp_fops = {
.owner = THIS_MODULE,
.read = no_comp_file_read,
};
void adf_cnv_dbgfs_add(struct adf_accel_dev *accel_dev)
{
const struct file_operations *fops;
void *data;
if (adf_hw_dev_has_compression(accel_dev)) {
fops = &qat_cnv_fops;
data = accel_dev;
} else {
fops = &qat_cnv_no_comp_fops;
data = NULL;
}
accel_dev->cnv_dbgfile = debugfs_create_file(CNV_DEBUGFS_FILENAME, 0400,
accel_dev->debugfs_dir,
data, fops);
}
void adf_cnv_dbgfs_rm(struct adf_accel_dev *accel_dev)
{
debugfs_remove(accel_dev->cnv_dbgfile);
accel_dev->cnv_dbgfile = NULL;
}
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright(c) 2023 Intel Corporation */
#ifndef ADF_CNV_DBG_H
#define ADF_CNV_DBG_H
struct adf_accel_dev;
void adf_cnv_dbgfs_add(struct adf_accel_dev *accel_dev);
void adf_cnv_dbgfs_rm(struct adf_accel_dev *accel_dev);
#endif
@@ -0,0 +1,262 @@
/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */
/* Copyright(c) 2014 - 2021 Intel Corporation */
#ifndef ADF_DRV_H
#define ADF_DRV_H
#include <linux/list.h>
#include <linux/pci.h>
#include "adf_accel_devices.h"
#include "icp_qat_fw_loader_handle.h"
#include "icp_qat_hal.h"
#define ADF_STATUS_RESTARTING 0
#define ADF_STATUS_STARTING 1
#define ADF_STATUS_CONFIGURED 2
#define ADF_STATUS_STARTED 3
#define ADF_STATUS_AE_INITIALISED 4
#define ADF_STATUS_AE_UCODE_LOADED 5
#define ADF_STATUS_AE_STARTED 6
#define ADF_STATUS_PF_RUNNING 7
#define ADF_STATUS_IRQ_ALLOCATED 8
#define ADF_STATUS_CRYPTO_ALGS_REGISTERED 9
#define ADF_STATUS_COMP_ALGS_REGISTERED 10
enum adf_dev_reset_mode {
ADF_DEV_RESET_ASYNC = 0,
ADF_DEV_RESET_SYNC
};
enum adf_event {
ADF_EVENT_INIT = 0,
ADF_EVENT_START,
ADF_EVENT_STOP,
ADF_EVENT_SHUTDOWN,
ADF_EVENT_RESTARTING,
ADF_EVENT_RESTARTED,
ADF_EVENT_FATAL_ERROR,
};
struct service_hndl {
int (*event_hld)(struct adf_accel_dev *accel_dev,
enum adf_event event);
unsigned long init_status[ADF_DEVS_ARRAY_SIZE];
unsigned long start_status[ADF_DEVS_ARRAY_SIZE];
char *name;
struct list_head list;
};
int adf_service_register(struct service_hndl *service);
int adf_service_unregister(struct service_hndl *service);
int adf_dev_up(struct adf_accel_dev *accel_dev, bool init_config);
int adf_dev_down(struct adf_accel_dev *accel_dev);
int adf_dev_restart(struct adf_accel_dev *accel_dev);
void adf_devmgr_update_class_index(struct adf_hw_device_data *hw_data);
void adf_clean_vf_map(bool);
int adf_notify_fatal_error(struct adf_accel_dev *accel_dev);
void adf_error_notifier(struct adf_accel_dev *accel_dev);
int adf_devmgr_add_dev(struct adf_accel_dev *accel_dev,
struct adf_accel_dev *pf);
void adf_devmgr_rm_dev(struct adf_accel_dev *accel_dev,
struct adf_accel_dev *pf);
struct list_head *adf_devmgr_get_head(void);
struct adf_accel_dev *adf_devmgr_pci_to_accel_dev(struct pci_dev *pci_dev);
int adf_devmgr_in_reset(struct adf_accel_dev *accel_dev);
int adf_dev_started(struct adf_accel_dev *accel_dev);
int adf_dev_restarting_notify(struct adf_accel_dev *accel_dev);
int adf_dev_restarted_notify(struct adf_accel_dev *accel_dev);
int adf_ae_init(struct adf_accel_dev *accel_dev);
int adf_ae_shutdown(struct adf_accel_dev *accel_dev);
int adf_ae_fw_load(struct adf_accel_dev *accel_dev);
void adf_ae_fw_release(struct adf_accel_dev *accel_dev);
int adf_ae_start(struct adf_accel_dev *accel_dev);
int adf_ae_stop(struct adf_accel_dev *accel_dev);
extern const struct pci_error_handlers adf_err_handler;
void adf_reset_sbr(struct adf_accel_dev *accel_dev);
void adf_reset_flr(struct adf_accel_dev *accel_dev);
void adf_dev_restore(struct adf_accel_dev *accel_dev);
void adf_set_bme(struct adf_accel_dev *accel_dev);
int adf_init_aer(void);
void adf_exit_aer(void);
int adf_init_arb(struct adf_accel_dev *accel_dev);
void adf_exit_arb(struct adf_accel_dev *accel_dev);
void adf_update_ring_arb(struct adf_etr_ring_data *ring);
int adf_dev_get(struct adf_accel_dev *accel_dev);
void adf_dev_put(struct adf_accel_dev *accel_dev);
int adf_dev_in_use(struct adf_accel_dev *accel_dev);
int adf_init_etr_data(struct adf_accel_dev *accel_dev);
void adf_cleanup_etr_data(struct adf_accel_dev *accel_dev);
int qat_crypto_register(void);
int qat_crypto_unregister(void);
int qat_crypto_vf_dev_config(struct adf_accel_dev *accel_dev);
struct qat_crypto_instance *qat_crypto_get_instance_node(int node);
void qat_crypto_put_instance(struct qat_crypto_instance *inst);
void qat_alg_callback(void *resp);
void qat_alg_asym_callback(void *resp);
int qat_algs_register(void);
void qat_algs_unregister(void);
int qat_asym_algs_register(void);
void qat_asym_algs_unregister(void);
struct qat_compression_instance *qat_compression_get_instance_node(int node, int alg);
void qat_compression_put_instance(struct qat_compression_instance *inst);
int qat_compression_register(void);
int qat_compression_unregister(void);
int qat_comp_algs_register(u32 caps);
void qat_comp_algs_unregister(u32 caps);
void qat_comp_alg_callback(void *resp);
int adf_isr_resource_alloc(struct adf_accel_dev *accel_dev);
void adf_isr_resource_free(struct adf_accel_dev *accel_dev);
void adf_isr_sync_ae_cluster(struct adf_accel_dev *accel_dev);
int adf_vf_isr_resource_alloc(struct adf_accel_dev *accel_dev);
void adf_vf_isr_resource_free(struct adf_accel_dev *accel_dev);
int adf_pfvf_comms_disabled(struct adf_accel_dev *accel_dev);
int adf_sysfs_init(struct adf_accel_dev *accel_dev);
int qat_hal_init(struct adf_accel_dev *accel_dev);
void qat_hal_deinit(struct icp_qat_fw_loader_handle *handle);
int qat_hal_start(struct icp_qat_fw_loader_handle *handle);
void qat_hal_stop(struct icp_qat_fw_loader_handle *handle, unsigned char ae,
unsigned int ctx_mask);
void qat_hal_reset(struct icp_qat_fw_loader_handle *handle);
int qat_hal_clr_reset(struct icp_qat_fw_loader_handle *handle);
void qat_hal_set_live_ctx(struct icp_qat_fw_loader_handle *handle,
unsigned char ae, unsigned int ctx_mask);
int qat_hal_check_ae_active(struct icp_qat_fw_loader_handle *handle,
unsigned int ae);
int qat_hal_set_ae_lm_mode(struct icp_qat_fw_loader_handle *handle,
unsigned char ae, enum icp_qat_uof_regtype lm_type,
unsigned char mode);
int qat_hal_set_ae_ctx_mode(struct icp_qat_fw_loader_handle *handle,
unsigned char ae, unsigned char mode);
int qat_hal_set_ae_nn_mode(struct icp_qat_fw_loader_handle *handle,
unsigned char ae, unsigned char mode);
void qat_hal_set_pc(struct icp_qat_fw_loader_handle *handle,
unsigned char ae, unsigned int ctx_mask, unsigned int upc);
void qat_hal_wr_uwords(struct icp_qat_fw_loader_handle *handle,
unsigned char ae, unsigned int uaddr,
unsigned int words_num, u64 *uword);
void qat_hal_wr_umem(struct icp_qat_fw_loader_handle *handle, unsigned char ae,
unsigned int uword_addr, unsigned int words_num,
unsigned int *data);
int qat_hal_get_ins_num(void);
int qat_hal_batch_wr_lm(struct icp_qat_fw_loader_handle *handle,
unsigned char ae,
struct icp_qat_uof_batch_init *lm_init_header);
int qat_hal_init_gpr(struct icp_qat_fw_loader_handle *handle,
unsigned char ae, unsigned long ctx_mask,
enum icp_qat_uof_regtype reg_type,
unsigned short reg_num, unsigned int regdata);
int qat_hal_init_wr_xfer(struct icp_qat_fw_loader_handle *handle,
unsigned char ae, unsigned long ctx_mask,
enum icp_qat_uof_regtype reg_type,
unsigned short reg_num, unsigned int regdata);
int qat_hal_init_rd_xfer(struct icp_qat_fw_loader_handle *handle,
unsigned char ae, unsigned long ctx_mask,
enum icp_qat_uof_regtype reg_type,
unsigned short reg_num, unsigned int regdata);
int qat_hal_init_nn(struct icp_qat_fw_loader_handle *handle,
unsigned char ae, unsigned long ctx_mask,
unsigned short reg_num, unsigned int regdata);
void qat_hal_set_ae_tindex_mode(struct icp_qat_fw_loader_handle *handle,
unsigned char ae, unsigned char mode);
int qat_uclo_wr_all_uimage(struct icp_qat_fw_loader_handle *handle);
void qat_uclo_del_obj(struct icp_qat_fw_loader_handle *handle);
int qat_uclo_wr_mimage(struct icp_qat_fw_loader_handle *handle, void *addr_ptr,
int mem_size);
int qat_uclo_map_obj(struct icp_qat_fw_loader_handle *handle,
void *addr_ptr, u32 mem_size, const char *obj_name);
int qat_uclo_set_cfg_ae_mask(struct icp_qat_fw_loader_handle *handle,
unsigned int cfg_ae_mask);
int adf_init_misc_wq(void);
void adf_exit_misc_wq(void);
bool adf_misc_wq_queue_work(struct work_struct *work);
bool adf_misc_wq_queue_delayed_work(struct delayed_work *work,
unsigned long delay);
void adf_misc_wq_flush(void);
#if defined(CONFIG_PCI_IOV)
int adf_sriov_configure(struct pci_dev *pdev, int numvfs);
void adf_disable_sriov(struct adf_accel_dev *accel_dev);
void adf_reenable_sriov(struct adf_accel_dev *accel_dev);
void adf_enable_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 vf_mask);
void adf_enable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 num_vfs);
void adf_disable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev);
bool adf_recv_and_handle_pf2vf_msg(struct adf_accel_dev *accel_dev);
bool adf_recv_and_handle_vf2pf_msg(struct adf_accel_dev *accel_dev, u32 vf_nr);
int adf_pf2vf_handle_pf_restarting(struct adf_accel_dev *accel_dev);
void adf_enable_pf2vf_interrupts(struct adf_accel_dev *accel_dev);
void adf_disable_pf2vf_interrupts(struct adf_accel_dev *accel_dev);
void adf_schedule_vf2pf_handler(struct adf_accel_vf_info *vf_info);
int adf_init_pf_wq(void);
void adf_exit_pf_wq(void);
int adf_init_vf_wq(void);
void adf_exit_vf_wq(void);
void adf_flush_vf_wq(struct adf_accel_dev *accel_dev);
#else
#define adf_sriov_configure NULL
static inline void adf_disable_sriov(struct adf_accel_dev *accel_dev)
{
}
static inline void adf_reenable_sriov(struct adf_accel_dev *accel_dev)
{
}
static inline int adf_init_pf_wq(void)
{
return 0;
}
static inline void adf_exit_pf_wq(void)
{
}
static inline int adf_init_vf_wq(void)
{
return 0;
}
static inline void adf_exit_vf_wq(void)
{
}
#endif
static inline void __iomem *adf_get_pmisc_base(struct adf_accel_dev *accel_dev)
{
struct adf_hw_device_data *hw_data = accel_dev->hw_device;
struct adf_bar *pmisc;
pmisc = &GET_BARS(accel_dev)[hw_data->get_misc_bar_id(hw_data)];
return pmisc->virt_addr;
}
static inline void __iomem *adf_get_etr_base(struct adf_accel_dev *accel_dev)
{
struct adf_hw_device_data *hw_data = accel_dev->hw_device;
struct adf_bar *etr;
etr = &GET_BARS(accel_dev)[hw_data->get_etr_bar_id(hw_data)];
return etr->virt_addr;
}
static inline void __iomem *adf_get_aram_base(struct adf_accel_dev *accel_dev)
{
struct adf_hw_device_data *hw_data = accel_dev->hw_device;
struct adf_bar *param;
param = &GET_BARS(accel_dev)[hw_data->get_sram_bar_id(hw_data)];
return param->virt_addr;
}
#endif
@@ -0,0 +1,79 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2023 Intel Corporation */
#include <linux/debugfs.h>
#include "adf_accel_devices.h"
#include "adf_cfg.h"
#include "adf_common_drv.h"
#include "adf_cnv_dbgfs.h"
#include "adf_dbgfs.h"
#include "adf_fw_counters.h"
#include "adf_heartbeat_dbgfs.h"
#include "adf_pm_dbgfs.h"
#include "adf_tl_debugfs.h"
/**
* adf_dbgfs_init() - add persistent debugfs entries
* @accel_dev: Pointer to acceleration device.
*
* This function creates debugfs entries that are persistent through a device
* state change (from up to down or vice versa).
*/
void adf_dbgfs_init(struct adf_accel_dev *accel_dev)
{
char name[ADF_DEVICE_NAME_LENGTH];
/* Create dev top level debugfs entry */
snprintf(name, sizeof(name), "%s%s_%s", ADF_DEVICE_NAME_PREFIX,
accel_dev->hw_device->dev_class->name,
pci_name(accel_dev->accel_pci_dev.pci_dev));
accel_dev->debugfs_dir = debugfs_create_dir(name, NULL);
adf_cfg_dev_dbgfs_add(accel_dev);
}
EXPORT_SYMBOL_GPL(adf_dbgfs_init);
/**
* adf_dbgfs_exit() - remove persistent debugfs entries
* @accel_dev: Pointer to acceleration device.
*/
void adf_dbgfs_exit(struct adf_accel_dev *accel_dev)
{
adf_cfg_dev_dbgfs_rm(accel_dev);
debugfs_remove(accel_dev->debugfs_dir);
}
EXPORT_SYMBOL_GPL(adf_dbgfs_exit);
/**
* adf_dbgfs_add() - add non-persistent debugfs entries
* @accel_dev: Pointer to acceleration device.
*
* This function creates debugfs entries that are not persistent through
* a device state change (from up to down or vice versa).
*/
void adf_dbgfs_add(struct adf_accel_dev *accel_dev)
{
if (!accel_dev->is_vf) {
adf_fw_counters_dbgfs_add(accel_dev);
adf_heartbeat_dbgfs_add(accel_dev);
adf_pm_dbgfs_add(accel_dev);
adf_cnv_dbgfs_add(accel_dev);
adf_tl_dbgfs_add(accel_dev);
}
}
/**
* adf_dbgfs_rm() - remove non-persistent debugfs entries
* @accel_dev: Pointer to acceleration device.
*/
void adf_dbgfs_rm(struct adf_accel_dev *accel_dev)
{
if (!accel_dev->is_vf) {
adf_tl_dbgfs_rm(accel_dev);
adf_cnv_dbgfs_rm(accel_dev);
adf_pm_dbgfs_rm(accel_dev);
adf_heartbeat_dbgfs_rm(accel_dev);
adf_fw_counters_dbgfs_rm(accel_dev);
}
}
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright(c) 2023 Intel Corporation */
#ifndef ADF_DBGFS_H
#define ADF_DBGFS_H
#ifdef CONFIG_DEBUG_FS
void adf_dbgfs_init(struct adf_accel_dev *accel_dev);
void adf_dbgfs_add(struct adf_accel_dev *accel_dev);
void adf_dbgfs_rm(struct adf_accel_dev *accel_dev);
void adf_dbgfs_exit(struct adf_accel_dev *accel_dev);
#else
static inline void adf_dbgfs_init(struct adf_accel_dev *accel_dev)
{
}
static inline void adf_dbgfs_add(struct adf_accel_dev *accel_dev)
{
}
static inline void adf_dbgfs_rm(struct adf_accel_dev *accel_dev)
{
}
static inline void adf_dbgfs_exit(struct adf_accel_dev *accel_dev)
{
}
#endif
#endif
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2022 Intel Corporation */
#include "adf_accel_devices.h"
#include "adf_dc.h"
#include "icp_qat_fw_comp.h"
int qat_comp_build_ctx(struct adf_accel_dev *accel_dev, void *ctx, enum adf_dc_algo algo)
{
struct icp_qat_fw_comp_req *req_tmpl = ctx;
struct icp_qat_fw_comp_cd_hdr *comp_cd_ctrl = &req_tmpl->comp_cd_ctrl;
struct icp_qat_fw_comp_req_params *req_pars = &req_tmpl->comp_pars;
struct icp_qat_fw_comn_req_hdr *header = &req_tmpl->comn_hdr;
int ret;
memset(req_tmpl, 0, sizeof(*req_tmpl));
header->hdr_flags =
ICP_QAT_FW_COMN_HDR_FLAGS_BUILD(ICP_QAT_FW_COMN_REQ_FLAG_SET);
header->service_type = ICP_QAT_FW_COMN_REQ_CPM_FW_COMP;
header->comn_req_flags =
ICP_QAT_FW_COMN_FLAGS_BUILD(QAT_COMN_CD_FLD_TYPE_16BYTE_DATA,
QAT_COMN_PTR_TYPE_SGL);
header->serv_specif_flags =
ICP_QAT_FW_COMP_FLAGS_BUILD(ICP_QAT_FW_COMP_STATELESS_SESSION,
ICP_QAT_FW_COMP_NOT_AUTO_SELECT_BEST,
ICP_QAT_FW_COMP_NOT_ENH_AUTO_SELECT_BEST,
ICP_QAT_FW_COMP_NOT_DISABLE_TYPE0_ENH_AUTO_SELECT_BEST,
ICP_QAT_FW_COMP_ENABLE_SECURE_RAM_USED_AS_INTMD_BUF);
/* Build HW config block for compression */
ret = GET_DC_OPS(accel_dev)->build_comp_block(ctx, algo);
if (ret) {
dev_err(&GET_DEV(accel_dev), "Failed to build compression block\n");
return ret;
}
req_pars->crc.legacy.initial_adler = COMP_CPR_INITIAL_ADLER;
req_pars->crc.legacy.initial_crc32 = COMP_CPR_INITIAL_CRC;
req_pars->req_par_flags =
ICP_QAT_FW_COMP_REQ_PARAM_FLAGS_BUILD(ICP_QAT_FW_COMP_SOP,
ICP_QAT_FW_COMP_EOP,
ICP_QAT_FW_COMP_BFINAL,
ICP_QAT_FW_COMP_CNV,
ICP_QAT_FW_COMP_CNV_RECOVERY,
ICP_QAT_FW_COMP_NO_CNV_DFX,
ICP_QAT_FW_COMP_CRC_MODE_LEGACY,
ICP_QAT_FW_COMP_NO_XXHASH_ACC,
ICP_QAT_FW_COMP_CNV_ERROR_NONE,
ICP_QAT_FW_COMP_NO_APPEND_CRC,
ICP_QAT_FW_COMP_NO_DROP_DATA,
ICP_QAT_FW_COMP_NO_PARTIAL_DECOMPRESS);
ICP_QAT_FW_COMN_NEXT_ID_SET(comp_cd_ctrl, ICP_QAT_FW_SLICE_DRAM_WR);
ICP_QAT_FW_COMN_CURR_ID_SET(comp_cd_ctrl, ICP_QAT_FW_SLICE_COMP);
/* Fill second half of the template for decompression */
memcpy(req_tmpl + 1, req_tmpl, sizeof(*req_tmpl));
req_tmpl++;
/* Build HW config block for decompression */
ret = GET_DC_OPS(accel_dev)->build_decomp_block(req_tmpl, algo);
if (ret)
dev_err(&GET_DEV(accel_dev), "Failed to build decompression block\n");
return ret;
}
@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright(c) 2025 Intel Corporation */
#ifndef ADF_DC_H
#define ADF_DC_H
struct adf_accel_dev;
enum adf_dc_algo {
QAT_DEFLATE,
QAT_LZ4,
QAT_LZ4S,
QAT_ZSTD,
};
int qat_comp_build_ctx(struct adf_accel_dev *accel_dev, void *ctx, enum adf_dc_algo algo);
#endif /* ADF_DC_H */
@@ -0,0 +1,372 @@
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2014 - 2020 Intel Corporation */
#include <linux/mutex.h>
#include <linux/list.h>
#include "adf_cfg.h"
#include "adf_common_drv.h"
static LIST_HEAD(accel_table);
static LIST_HEAD(vfs_table);
static DEFINE_MUTEX(table_lock);
static u32 num_devices;
static u8 id_map[ADF_MAX_DEVICES];
struct vf_id_map {
u32 bdf;
u32 id;
u32 fake_id;
bool attached;
struct list_head list;
};
static int adf_get_vf_id(struct adf_accel_dev *vf)
{
return ((7 * (PCI_SLOT(accel_to_pci_dev(vf)->devfn) - 1)) +
PCI_FUNC(accel_to_pci_dev(vf)->devfn) +
(PCI_SLOT(accel_to_pci_dev(vf)->devfn) - 1));
}
static int adf_get_vf_num(struct adf_accel_dev *vf)
{
return (accel_to_pci_dev(vf)->bus->number << 8) | adf_get_vf_id(vf);
}
static struct vf_id_map *adf_find_vf(u32 bdf)
{
struct list_head *itr;
list_for_each(itr, &vfs_table) {
struct vf_id_map *ptr =
list_entry(itr, struct vf_id_map, list);
if (ptr->bdf == bdf)
return ptr;
}
return NULL;
}
/**
* adf_clean_vf_map() - Cleans VF id mappings
* @vf: flag indicating whether mappings is cleaned
* for vfs only or for vfs and pfs
*
* Function cleans internal ids for virtual functions.
*/
void adf_clean_vf_map(bool vf)
{
struct vf_id_map *map;
struct list_head *ptr, *tmp;
mutex_lock(&table_lock);
list_for_each_safe(ptr, tmp, &vfs_table) {
map = list_entry(ptr, struct vf_id_map, list);
if (map->bdf != -1) {
id_map[map->id] = 0;
num_devices--;
}
if (vf && map->bdf == -1)
continue;
list_del(ptr);
kfree(map);
}
mutex_unlock(&table_lock);
}
EXPORT_SYMBOL_GPL(adf_clean_vf_map);
/**
* adf_devmgr_update_class_index() - Update internal index
* @hw_data: Pointer to internal device data.
*
* Function updates internal dev index for VFs
*/
void adf_devmgr_update_class_index(struct adf_hw_device_data *hw_data)
{
struct adf_hw_device_class *class = hw_data->dev_class;
struct list_head *itr;
int i = 0;
list_for_each(itr, &accel_table) {
struct adf_accel_dev *ptr =
list_entry(itr, struct adf_accel_dev, list);
if (ptr->hw_device->dev_class == class)
ptr->hw_device->instance_id = i++;
if (i == class->instances)
break;
}
}
EXPORT_SYMBOL_GPL(adf_devmgr_update_class_index);
static unsigned int adf_find_free_id(void)
{
unsigned int i;
for (i = 0; i < ADF_MAX_DEVICES; i++) {
if (!id_map[i]) {
id_map[i] = 1;
return i;
}
}
return ADF_MAX_DEVICES + 1;
}
/**
* adf_devmgr_add_dev() - Add accel_dev to the acceleration framework
* @accel_dev: Pointer to acceleration device.
* @pf: Corresponding PF if the accel_dev is a VF
*
* Function adds acceleration device to the acceleration framework.
* To be used by QAT device specific drivers.
*
* Return: 0 on success, error code otherwise.
*/
int adf_devmgr_add_dev(struct adf_accel_dev *accel_dev,
struct adf_accel_dev *pf)
{
struct list_head *itr;
int ret = 0;
if (num_devices == ADF_MAX_DEVICES) {
dev_err(&GET_DEV(accel_dev), "Only support up to %d devices\n",
ADF_MAX_DEVICES);
return -EFAULT;
}
mutex_lock(&table_lock);
atomic_set(&accel_dev->ref_count, 0);
/* PF on host or VF on guest - optimized to remove redundant is_vf */
if (!accel_dev->is_vf || !pf) {
struct vf_id_map *map;
list_for_each(itr, &accel_table) {
struct adf_accel_dev *ptr =
list_entry(itr, struct adf_accel_dev, list);
if (ptr == accel_dev) {
ret = -EEXIST;
goto unlock;
}
}
list_add_tail(&accel_dev->list, &accel_table);
accel_dev->accel_id = adf_find_free_id();
if (accel_dev->accel_id > ADF_MAX_DEVICES) {
ret = -EFAULT;
goto unlock;
}
num_devices++;
map = kzalloc_obj(*map);
if (!map) {
ret = -ENOMEM;
goto unlock;
}
map->bdf = ~0;
map->id = accel_dev->accel_id;
map->fake_id = map->id;
map->attached = true;
list_add_tail(&map->list, &vfs_table);
} else if (accel_dev->is_vf && pf) {
/* VF on host */
struct vf_id_map *map;
map = adf_find_vf(adf_get_vf_num(accel_dev));
if (map) {
struct vf_id_map *next;
accel_dev->accel_id = map->id;
list_add_tail(&accel_dev->list, &accel_table);
map->fake_id++;
map->attached = true;
next = list_next_entry(map, list);
while (next && &next->list != &vfs_table) {
next->fake_id++;
next = list_next_entry(next, list);
}
ret = 0;
goto unlock;
}
map = kzalloc_obj(*map);
if (!map) {
ret = -ENOMEM;
goto unlock;
}
accel_dev->accel_id = adf_find_free_id();
if (accel_dev->accel_id > ADF_MAX_DEVICES) {
kfree(map);
ret = -EFAULT;
goto unlock;
}
num_devices++;
list_add_tail(&accel_dev->list, &accel_table);
map->bdf = adf_get_vf_num(accel_dev);
map->id = accel_dev->accel_id;
map->fake_id = map->id;
map->attached = true;
list_add_tail(&map->list, &vfs_table);
}
mutex_init(&accel_dev->state_lock);
unlock:
mutex_unlock(&table_lock);
return ret;
}
EXPORT_SYMBOL_GPL(adf_devmgr_add_dev);
struct list_head *adf_devmgr_get_head(void)
{
return &accel_table;
}
/**
* adf_devmgr_rm_dev() - Remove accel_dev from the acceleration framework.
* @accel_dev: Pointer to acceleration device.
* @pf: Corresponding PF if the accel_dev is a VF
*
* Function removes acceleration device from the acceleration framework.
* To be used by QAT device specific drivers.
*
* Return: void
*/
void adf_devmgr_rm_dev(struct adf_accel_dev *accel_dev,
struct adf_accel_dev *pf)
{
mutex_lock(&table_lock);
/* PF on host or VF on guest - optimized to remove redundant is_vf */
if (!accel_dev->is_vf || !pf) {
id_map[accel_dev->accel_id] = 0;
num_devices--;
} else if (accel_dev->is_vf && pf) {
struct vf_id_map *map, *next;
map = adf_find_vf(adf_get_vf_num(accel_dev));
if (!map) {
dev_err(&GET_DEV(accel_dev), "Failed to find VF map\n");
goto unlock;
}
map->fake_id--;
map->attached = false;
next = list_next_entry(map, list);
while (next && &next->list != &vfs_table) {
next->fake_id--;
next = list_next_entry(next, list);
}
}
unlock:
mutex_destroy(&accel_dev->state_lock);
list_del(&accel_dev->list);
mutex_unlock(&table_lock);
}
EXPORT_SYMBOL_GPL(adf_devmgr_rm_dev);
/**
* adf_devmgr_pci_to_accel_dev() - Get accel_dev associated with the pci_dev.
* @pci_dev: Pointer to PCI device.
*
* Function returns acceleration device associated with the given PCI device.
* To be used by QAT device specific drivers.
*
* Return: pointer to accel_dev or NULL if not found.
*/
struct adf_accel_dev *adf_devmgr_pci_to_accel_dev(struct pci_dev *pci_dev)
{
struct list_head *itr;
mutex_lock(&table_lock);
list_for_each(itr, &accel_table) {
struct adf_accel_dev *ptr =
list_entry(itr, struct adf_accel_dev, list);
if (ptr->accel_pci_dev.pci_dev == pci_dev) {
mutex_unlock(&table_lock);
return ptr;
}
}
mutex_unlock(&table_lock);
return NULL;
}
EXPORT_SYMBOL_GPL(adf_devmgr_pci_to_accel_dev);
/**
* adf_dev_in_use() - Check whether accel_dev is currently in use
* @accel_dev: Pointer to acceleration device.
*
* To be used by QAT device specific drivers.
*
* Return: 1 when device is in use, 0 otherwise.
*/
int adf_dev_in_use(struct adf_accel_dev *accel_dev)
{
return atomic_read(&accel_dev->ref_count) != 0;
}
EXPORT_SYMBOL_GPL(adf_dev_in_use);
/**
* adf_dev_get() - Increment accel_dev reference count
* @accel_dev: Pointer to acceleration device.
*
* Increment the accel_dev refcount and if this is the first time
* incrementing it during this period the accel_dev is in use,
* increment the module refcount too.
* To be used by QAT device specific drivers.
*
* Return: 0 when successful, EFAULT when fail to bump module refcount
*/
int adf_dev_get(struct adf_accel_dev *accel_dev)
{
if (atomic_add_return(1, &accel_dev->ref_count) == 1)
if (!try_module_get(accel_dev->owner))
return -EFAULT;
return 0;
}
EXPORT_SYMBOL_GPL(adf_dev_get);
/**
* adf_dev_put() - Decrement accel_dev reference count
* @accel_dev: Pointer to acceleration device.
*
* Decrement the accel_dev refcount and if this is the last time
* decrementing it during this period the accel_dev is in use,
* decrement the module refcount too.
* To be used by QAT device specific drivers.
*
* Return: void
*/
void adf_dev_put(struct adf_accel_dev *accel_dev)
{
if (atomic_sub_return(1, &accel_dev->ref_count) == 0)
module_put(accel_dev->owner);
}
EXPORT_SYMBOL_GPL(adf_dev_put);
/**
* adf_devmgr_in_reset() - Check whether device is in reset
* @accel_dev: Pointer to acceleration device.
*
* To be used by QAT device specific drivers.
*
* Return: 1 when the device is being reset, 0 otherwise.
*/
int adf_devmgr_in_reset(struct adf_accel_dev *accel_dev)
{
return test_bit(ADF_STATUS_RESTARTING, &accel_dev->status);
}
EXPORT_SYMBOL_GPL(adf_devmgr_in_reset);
/**
* adf_dev_started() - Check whether device has started
* @accel_dev: Pointer to acceleration device.
*
* To be used by QAT device specific drivers.
*
* Return: 1 when the device has started, 0 otherwise
*/
int adf_dev_started(struct adf_accel_dev *accel_dev)
{
return test_bit(ADF_STATUS_STARTED, &accel_dev->status);
}
EXPORT_SYMBOL_GPL(adf_dev_started);
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright(c) 2023 Intel Corporation */
#ifndef ADF_FW_CONFIG_H_
#define ADF_FW_CONFIG_H_
enum adf_fw_objs {
ADF_FW_SYM_OBJ,
ADF_FW_ASYM_OBJ,
ADF_FW_DC_OBJ,
ADF_FW_ADMIN_OBJ,
ADF_FW_CY_OBJ,
ADF_FW_WCY_OBJ,
};
struct adf_fw_config {
u32 ae_mask;
enum adf_fw_objs obj;
};
#endif
@@ -0,0 +1,265 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2023 Intel Corporation */
#include <linux/bitops.h>
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/seq_file.h>
#include <linux/types.h>
#include "adf_accel_devices.h"
#include "adf_admin.h"
#include "adf_common_drv.h"
#include "adf_fw_counters.h"
#define ADF_FW_COUNTERS_MAX_PADDING 16
enum adf_fw_counters_types {
ADF_FW_REQUESTS,
ADF_FW_RESPONSES,
ADF_FW_COUNTERS_COUNT
};
static const char * const adf_fw_counter_names[] = {
[ADF_FW_REQUESTS] = "Requests",
[ADF_FW_RESPONSES] = "Responses",
};
static_assert(ARRAY_SIZE(adf_fw_counter_names) == ADF_FW_COUNTERS_COUNT);
struct adf_ae_counters {
u16 ae;
u64 values[ADF_FW_COUNTERS_COUNT];
};
struct adf_fw_counters {
u16 ae_count;
struct adf_ae_counters ae_counters[] __counted_by(ae_count);
};
static void adf_fw_counters_parse_ae_values(struct adf_ae_counters *ae_counters, u32 ae,
u64 req_count, u64 resp_count)
{
ae_counters->ae = ae;
ae_counters->values[ADF_FW_REQUESTS] = req_count;
ae_counters->values[ADF_FW_RESPONSES] = resp_count;
}
static int adf_fw_counters_load_from_device(struct adf_accel_dev *accel_dev,
struct adf_fw_counters *fw_counters)
{
struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
unsigned long ae_mask;
unsigned int i;
unsigned long ae;
/* Ignore the admin AEs */
ae_mask = hw_data->ae_mask & ~hw_data->admin_ae_mask;
if (hweight_long(ae_mask) > fw_counters->ae_count)
return -EINVAL;
i = 0;
for_each_set_bit(ae, &ae_mask, GET_MAX_ACCELENGINES(accel_dev)) {
u64 req_count, resp_count;
int ret;
ret = adf_get_ae_fw_counters(accel_dev, ae, &req_count, &resp_count);
if (ret)
return ret;
adf_fw_counters_parse_ae_values(&fw_counters->ae_counters[i++], ae,
req_count, resp_count);
}
return 0;
}
static struct adf_fw_counters *adf_fw_counters_allocate(unsigned long ae_count)
{
struct adf_fw_counters *fw_counters;
if (unlikely(!ae_count))
return ERR_PTR(-EINVAL);
fw_counters = kmalloc_flex(*fw_counters, ae_counters, ae_count);
if (!fw_counters)
return ERR_PTR(-ENOMEM);
fw_counters->ae_count = ae_count;
return fw_counters;
}
/**
* adf_fw_counters_get() - Return FW counters for the provided device.
* @accel_dev: Pointer to a QAT acceleration device
*
* Allocates and returns a table of counters containing execution statistics
* for each non-admin AE available through the supplied acceleration device.
* The caller becomes the owner of such memory and is responsible for
* the deallocation through a call to kfree().
*
* Returns: a pointer to a dynamically allocated struct adf_fw_counters
* on success, or a negative value on error.
*/
static struct adf_fw_counters *adf_fw_counters_get(struct adf_accel_dev *accel_dev)
{
struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
struct adf_fw_counters *fw_counters;
unsigned long ae_count;
int ret;
if (!adf_dev_started(accel_dev)) {
dev_err(&GET_DEV(accel_dev), "QAT Device not started\n");
return ERR_PTR(-EFAULT);
}
/* Ignore the admin AEs */
ae_count = hweight_long(hw_data->ae_mask & ~hw_data->admin_ae_mask);
fw_counters = adf_fw_counters_allocate(ae_count);
if (IS_ERR(fw_counters))
return fw_counters;
ret = adf_fw_counters_load_from_device(accel_dev, fw_counters);
if (ret) {
kfree(fw_counters);
dev_err(&GET_DEV(accel_dev),
"Failed to create QAT fw_counters file table [%d].\n", ret);
return ERR_PTR(ret);
}
return fw_counters;
}
static void *qat_fw_counters_seq_start(struct seq_file *sfile, loff_t *pos)
{
struct adf_fw_counters *fw_counters = sfile->private;
if (*pos == 0)
return SEQ_START_TOKEN;
if (*pos > fw_counters->ae_count)
return NULL;
return &fw_counters->ae_counters[*pos - 1];
}
static void *qat_fw_counters_seq_next(struct seq_file *sfile, void *v, loff_t *pos)
{
struct adf_fw_counters *fw_counters = sfile->private;
(*pos)++;
if (*pos > fw_counters->ae_count)
return NULL;
return &fw_counters->ae_counters[*pos - 1];
}
static void qat_fw_counters_seq_stop(struct seq_file *sfile, void *v) {}
static int qat_fw_counters_seq_show(struct seq_file *sfile, void *v)
{
int i;
if (v == SEQ_START_TOKEN) {
seq_puts(sfile, "AE ");
for (i = 0; i < ADF_FW_COUNTERS_COUNT; ++i)
seq_printf(sfile, " %*s", ADF_FW_COUNTERS_MAX_PADDING,
adf_fw_counter_names[i]);
} else {
struct adf_ae_counters *ae_counters = (struct adf_ae_counters *)v;
seq_printf(sfile, "%2d:", ae_counters->ae);
for (i = 0; i < ADF_FW_COUNTERS_COUNT; ++i)
seq_printf(sfile, " %*llu", ADF_FW_COUNTERS_MAX_PADDING,
ae_counters->values[i]);
}
seq_putc(sfile, '\n');
return 0;
}
static const struct seq_operations qat_fw_counters_sops = {
.start = qat_fw_counters_seq_start,
.next = qat_fw_counters_seq_next,
.stop = qat_fw_counters_seq_stop,
.show = qat_fw_counters_seq_show,
};
static int qat_fw_counters_file_open(struct inode *inode, struct file *file)
{
struct adf_accel_dev *accel_dev = inode->i_private;
struct seq_file *fw_counters_seq_file;
struct adf_fw_counters *fw_counters;
int ret;
fw_counters = adf_fw_counters_get(accel_dev);
if (IS_ERR(fw_counters))
return PTR_ERR(fw_counters);
ret = seq_open(file, &qat_fw_counters_sops);
if (unlikely(ret)) {
kfree(fw_counters);
return ret;
}
fw_counters_seq_file = file->private_data;
fw_counters_seq_file->private = fw_counters;
return ret;
}
static int qat_fw_counters_file_release(struct inode *inode, struct file *file)
{
struct seq_file *seq = file->private_data;
kfree(seq->private);
seq->private = NULL;
return seq_release(inode, file); }
static const struct file_operations qat_fw_counters_fops = {
.owner = THIS_MODULE,
.open = qat_fw_counters_file_open,
.read = seq_read,
.llseek = seq_lseek,
.release = qat_fw_counters_file_release,
};
/**
* adf_fw_counters_dbgfs_add() - Create a debugfs file containing FW
* execution counters.
* @accel_dev: Pointer to a QAT acceleration device
*
* Function creates a file to display a table with statistics for the given
* QAT acceleration device. The table stores device specific execution values
* for each AE, such as the number of requests sent to the FW and responses
* received from the FW.
*
* Return: void
*/
void adf_fw_counters_dbgfs_add(struct adf_accel_dev *accel_dev)
{
accel_dev->fw_cntr_dbgfile = debugfs_create_file("fw_counters", 0400,
accel_dev->debugfs_dir,
accel_dev,
&qat_fw_counters_fops);
}
/**
* adf_fw_counters_dbgfs_rm() - Remove the debugfs file containing FW counters.
* @accel_dev: Pointer to a QAT acceleration device.
*
* Function removes the file providing the table of statistics for the given
* QAT acceleration device.
*
* Return: void
*/
void adf_fw_counters_dbgfs_rm(struct adf_accel_dev *accel_dev)
{
debugfs_remove(accel_dev->fw_cntr_dbgfile);
accel_dev->fw_cntr_dbgfile = NULL;
}
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright(c) 2023 Intel Corporation */
#ifndef ADF_FW_COUNTERS_H
#define ADF_FW_COUNTERS_H
struct adf_accel_dev;
void adf_fw_counters_dbgfs_add(struct adf_accel_dev *accel_dev);
void adf_fw_counters_dbgfs_rm(struct adf_accel_dev *accel_dev);
#endif
@@ -0,0 +1,213 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2022 Intel Corporation */
#include "adf_accel_devices.h"
#include "adf_cfg.h"
#include "adf_cfg_strings.h"
#include "adf_gen2_config.h"
#include "adf_common_drv.h"
#include "qat_crypto.h"
#include "qat_compression.h"
#include "adf_heartbeat.h"
#include "adf_transport_access_macros.h"
static int adf_gen2_crypto_dev_config(struct adf_accel_dev *accel_dev)
{
char key[ADF_CFG_MAX_KEY_LEN_IN_BYTES];
int banks = GET_MAX_BANKS(accel_dev);
int cpus = num_online_cpus();
unsigned long val;
int instances;
int ret;
int i;
if (adf_hw_dev_has_crypto(accel_dev))
instances = min(cpus, banks);
else
instances = 0;
for (i = 0; i < instances; i++) {
val = i;
snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_BANK_NUM, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_BANK_NUM, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
snprintf(key, sizeof(key), ADF_CY "%d" ADF_ETRMGR_CORE_AFFINITY,
i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_SIZE, i);
val = 128;
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
val = 512;
snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_SIZE, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
val = 0;
snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_TX, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
val = 2;
snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_TX, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
val = 8;
snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_RX, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
val = 10;
snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_RX, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
val = ADF_COALESCING_DEF_TIME;
snprintf(key, sizeof(key), ADF_ETRMGR_COALESCE_TIMER_FORMAT, i);
ret = adf_cfg_add_key_value_param(accel_dev, "Accelerator0",
key, &val, ADF_DEC);
if (ret)
goto err;
}
val = i;
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, ADF_NUM_CY,
&val, ADF_DEC);
if (ret)
goto err;
return ret;
err:
dev_err(&GET_DEV(accel_dev), "Failed to add configuration for crypto\n");
return ret;
}
static int adf_gen2_comp_dev_config(struct adf_accel_dev *accel_dev)
{
char key[ADF_CFG_MAX_KEY_LEN_IN_BYTES];
int banks = GET_MAX_BANKS(accel_dev);
int cpus = num_online_cpus();
unsigned long val;
int instances;
int ret;
int i;
if (adf_hw_dev_has_compression(accel_dev))
instances = min(cpus, banks);
else
instances = 0;
for (i = 0; i < instances; i++) {
val = i;
snprintf(key, sizeof(key), ADF_DC "%d" ADF_RING_DC_BANK_NUM, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
val = 512;
snprintf(key, sizeof(key), ADF_DC "%d" ADF_RING_DC_SIZE, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
val = 6;
snprintf(key, sizeof(key), ADF_DC "%d" ADF_RING_DC_TX, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
val = 14;
snprintf(key, sizeof(key), ADF_DC "%d" ADF_RING_DC_RX, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
}
val = i;
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, ADF_NUM_DC,
&val, ADF_DEC);
if (ret)
return ret;
return ret;
err:
dev_err(&GET_DEV(accel_dev), "Failed to add configuration for compression\n");
return ret;
}
/**
* adf_gen2_dev_config() - create dev config required to create instances
*
* @accel_dev: Pointer to acceleration device.
*
* Function creates device configuration required to create instances
*
* Return: 0 on success, error code otherwise.
*/
int adf_gen2_dev_config(struct adf_accel_dev *accel_dev)
{
int ret;
ret = adf_cfg_section_add(accel_dev, ADF_KERNEL_SEC);
if (ret)
goto err;
ret = adf_cfg_section_add(accel_dev, "Accelerator0");
if (ret)
goto err;
ret = adf_gen2_crypto_dev_config(accel_dev);
if (ret)
goto err;
ret = adf_gen2_comp_dev_config(accel_dev);
if (ret)
goto err;
ret = adf_cfg_section_add(accel_dev, ADF_GENERAL_SEC);
if (ret)
goto err;
adf_heartbeat_save_cfg_param(accel_dev, ADF_CFG_HB_TIMER_DEFAULT_MS);
set_bit(ADF_STATUS_CONFIGURED, &accel_dev->status);
return ret;
err:
dev_err(&GET_DEV(accel_dev), "Failed to configure QAT driver\n");
return ret;
}
EXPORT_SYMBOL_GPL(adf_gen2_dev_config);
@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright(c) 2022 Intel Corporation */
#ifndef ADF_GEN2_CONFIG_H_
#define ADF_GEN2_CONFIG_H_
#include "adf_accel_devices.h"
int adf_gen2_dev_config(struct adf_accel_dev *accel_dev);
#endif
@@ -0,0 +1,101 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2024 Intel Corporation */
#include <linux/types.h>
#include "adf_gen2_hw_csr_data.h"
static u64 build_csr_ring_base_addr(dma_addr_t addr, u32 size)
{
return BUILD_RING_BASE_ADDR(addr, size);
}
static u32 read_csr_ring_head(void __iomem *csr_base_addr, u32 bank, u32 ring)
{
return READ_CSR_RING_HEAD(csr_base_addr, bank, ring);
}
static void write_csr_ring_head(void __iomem *csr_base_addr, u32 bank, u32 ring,
u32 value)
{
WRITE_CSR_RING_HEAD(csr_base_addr, bank, ring, value);
}
static u32 read_csr_ring_tail(void __iomem *csr_base_addr, u32 bank, u32 ring)
{
return READ_CSR_RING_TAIL(csr_base_addr, bank, ring);
}
static void write_csr_ring_tail(void __iomem *csr_base_addr, u32 bank, u32 ring,
u32 value)
{
WRITE_CSR_RING_TAIL(csr_base_addr, bank, ring, value);
}
static u32 read_csr_e_stat(void __iomem *csr_base_addr, u32 bank)
{
return READ_CSR_E_STAT(csr_base_addr, bank);
}
static void write_csr_ring_config(void __iomem *csr_base_addr, u32 bank,
u32 ring, u32 value)
{
WRITE_CSR_RING_CONFIG(csr_base_addr, bank, ring, value);
}
static void write_csr_ring_base(void __iomem *csr_base_addr, u32 bank, u32 ring,
dma_addr_t addr)
{
WRITE_CSR_RING_BASE(csr_base_addr, bank, ring, addr);
}
static void write_csr_int_flag(void __iomem *csr_base_addr, u32 bank, u32 value)
{
WRITE_CSR_INT_FLAG(csr_base_addr, bank, value);
}
static void write_csr_int_srcsel(void __iomem *csr_base_addr, u32 bank)
{
WRITE_CSR_INT_SRCSEL(csr_base_addr, bank);
}
static void write_csr_int_col_en(void __iomem *csr_base_addr, u32 bank,
u32 value)
{
WRITE_CSR_INT_COL_EN(csr_base_addr, bank, value);
}
static void write_csr_int_col_ctl(void __iomem *csr_base_addr, u32 bank,
u32 value)
{
WRITE_CSR_INT_COL_CTL(csr_base_addr, bank, value);
}
static void write_csr_int_flag_and_col(void __iomem *csr_base_addr, u32 bank,
u32 value)
{
WRITE_CSR_INT_FLAG_AND_COL(csr_base_addr, bank, value);
}
static void write_csr_ring_srv_arb_en(void __iomem *csr_base_addr, u32 bank,
u32 value)
{
WRITE_CSR_RING_SRV_ARB_EN(csr_base_addr, bank, value);
}
void adf_gen2_init_hw_csr_ops(struct adf_hw_csr_ops *csr_ops)
{
csr_ops->build_csr_ring_base_addr = build_csr_ring_base_addr;
csr_ops->read_csr_ring_head = read_csr_ring_head;
csr_ops->write_csr_ring_head = write_csr_ring_head;
csr_ops->read_csr_ring_tail = read_csr_ring_tail;
csr_ops->write_csr_ring_tail = write_csr_ring_tail;
csr_ops->read_csr_e_stat = read_csr_e_stat;
csr_ops->write_csr_ring_config = write_csr_ring_config;
csr_ops->write_csr_ring_base = write_csr_ring_base;
csr_ops->write_csr_int_flag = write_csr_int_flag;
csr_ops->write_csr_int_srcsel = write_csr_int_srcsel;
csr_ops->write_csr_int_col_en = write_csr_int_col_en;
csr_ops->write_csr_int_col_ctl = write_csr_int_col_ctl;
csr_ops->write_csr_int_flag_and_col = write_csr_int_flag_and_col;
csr_ops->write_csr_ring_srv_arb_en = write_csr_ring_srv_arb_en;
}
EXPORT_SYMBOL_GPL(adf_gen2_init_hw_csr_ops);
@@ -0,0 +1,86 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright(c) 2024 Intel Corporation */
#ifndef ADF_GEN2_HW_CSR_DATA_H_
#define ADF_GEN2_HW_CSR_DATA_H_
#include <linux/bitops.h>
#include "adf_accel_devices.h"
#define ADF_BANK_INT_SRC_SEL_MASK_0 0x4444444CUL
#define ADF_BANK_INT_SRC_SEL_MASK_X 0x44444444UL
#define ADF_RING_CSR_RING_CONFIG 0x000
#define ADF_RING_CSR_RING_LBASE 0x040
#define ADF_RING_CSR_RING_UBASE 0x080
#define ADF_RING_CSR_RING_HEAD 0x0C0
#define ADF_RING_CSR_RING_TAIL 0x100
#define ADF_RING_CSR_E_STAT 0x14C
#define ADF_RING_CSR_INT_FLAG 0x170
#define ADF_RING_CSR_INT_SRCSEL 0x174
#define ADF_RING_CSR_INT_SRCSEL_2 0x178
#define ADF_RING_CSR_INT_COL_EN 0x17C
#define ADF_RING_CSR_INT_COL_CTL 0x180
#define ADF_RING_CSR_INT_FLAG_AND_COL 0x184
#define ADF_RING_CSR_INT_COL_CTL_ENABLE 0x80000000
#define ADF_RING_BUNDLE_SIZE 0x1000
#define ADF_ARB_REG_SLOT 0x1000
#define ADF_ARB_RINGSRVARBEN_OFFSET 0x19C
#define BUILD_RING_BASE_ADDR(addr, size) \
(((addr) >> 6) & (GENMASK_ULL(63, 0) << (size)))
#define READ_CSR_RING_HEAD(csr_base_addr, bank, ring) \
ADF_CSR_RD(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \
ADF_RING_CSR_RING_HEAD + ((ring) << 2))
#define READ_CSR_RING_TAIL(csr_base_addr, bank, ring) \
ADF_CSR_RD(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \
ADF_RING_CSR_RING_TAIL + ((ring) << 2))
#define READ_CSR_E_STAT(csr_base_addr, bank) \
ADF_CSR_RD(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \
ADF_RING_CSR_E_STAT)
#define WRITE_CSR_RING_CONFIG(csr_base_addr, bank, ring, value) \
ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \
ADF_RING_CSR_RING_CONFIG + ((ring) << 2), value)
#define WRITE_CSR_RING_BASE(csr_base_addr, bank, ring, value) \
do { \
u32 l_base = 0, u_base = 0; \
l_base = (u32)((value) & 0xFFFFFFFF); \
u_base = (u32)(((value) & 0xFFFFFFFF00000000ULL) >> 32); \
ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \
ADF_RING_CSR_RING_LBASE + ((ring) << 2), l_base); \
ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \
ADF_RING_CSR_RING_UBASE + ((ring) << 2), u_base); \
} while (0)
#define WRITE_CSR_RING_HEAD(csr_base_addr, bank, ring, value) \
ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \
ADF_RING_CSR_RING_HEAD + ((ring) << 2), value)
#define WRITE_CSR_RING_TAIL(csr_base_addr, bank, ring, value) \
ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \
ADF_RING_CSR_RING_TAIL + ((ring) << 2), value)
#define WRITE_CSR_INT_FLAG(csr_base_addr, bank, value) \
ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \
ADF_RING_CSR_INT_FLAG, value)
#define WRITE_CSR_INT_SRCSEL(csr_base_addr, bank) \
do { \
ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \
ADF_RING_CSR_INT_SRCSEL, ADF_BANK_INT_SRC_SEL_MASK_0); \
ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \
ADF_RING_CSR_INT_SRCSEL_2, ADF_BANK_INT_SRC_SEL_MASK_X); \
} while (0)
#define WRITE_CSR_INT_COL_EN(csr_base_addr, bank, value) \
ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \
ADF_RING_CSR_INT_COL_EN, value)
#define WRITE_CSR_INT_COL_CTL(csr_base_addr, bank, value) \
ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \
ADF_RING_CSR_INT_COL_CTL, \
ADF_RING_CSR_INT_COL_CTL_ENABLE | (value))
#define WRITE_CSR_INT_FLAG_AND_COL(csr_base_addr, bank, value) \
ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \
ADF_RING_CSR_INT_FLAG_AND_COL, value)
#define WRITE_CSR_RING_SRV_ARB_EN(csr_addr, index, value) \
ADF_CSR_WR(csr_addr, ADF_ARB_RINGSRVARBEN_OFFSET + \
(ADF_ARB_REG_SLOT * (index)), value)
void adf_gen2_init_hw_csr_ops(struct adf_hw_csr_ops *csr_ops);
#endif
@@ -0,0 +1,228 @@
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2020 Intel Corporation */
#include "adf_common_drv.h"
#include "adf_dc.h"
#include "adf_gen2_hw_data.h"
#include "icp_qat_fw_comp.h"
#include "icp_qat_hw.h"
#include <linux/pci.h>
u32 adf_gen2_get_num_accels(struct adf_hw_device_data *self)
{
if (!self || !self->accel_mask)
return 0;
return hweight16(self->accel_mask);
}
EXPORT_SYMBOL_GPL(adf_gen2_get_num_accels);
u32 adf_gen2_get_num_aes(struct adf_hw_device_data *self)
{
if (!self || !self->ae_mask)
return 0;
return hweight32(self->ae_mask);
}
EXPORT_SYMBOL_GPL(adf_gen2_get_num_aes);
void adf_gen2_enable_error_correction(struct adf_accel_dev *accel_dev)
{
struct adf_hw_device_data *hw_data = accel_dev->hw_device;
void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev);
unsigned long accel_mask = hw_data->accel_mask;
unsigned long ae_mask = hw_data->ae_mask;
unsigned int val, i;
/* Enable Accel Engine error detection & correction */
for_each_set_bit(i, &ae_mask, hw_data->num_engines) {
val = ADF_CSR_RD(pmisc_addr, ADF_GEN2_AE_CTX_ENABLES(i));
val |= ADF_GEN2_ENABLE_AE_ECC_ERR;
ADF_CSR_WR(pmisc_addr, ADF_GEN2_AE_CTX_ENABLES(i), val);
val = ADF_CSR_RD(pmisc_addr, ADF_GEN2_AE_MISC_CONTROL(i));
val |= ADF_GEN2_ENABLE_AE_ECC_PARITY_CORR;
ADF_CSR_WR(pmisc_addr, ADF_GEN2_AE_MISC_CONTROL(i), val);
}
/* Enable shared memory error detection & correction */
for_each_set_bit(i, &accel_mask, hw_data->num_accel) {
val = ADF_CSR_RD(pmisc_addr, ADF_GEN2_UERRSSMSH(i));
val |= ADF_GEN2_ERRSSMSH_EN;
ADF_CSR_WR(pmisc_addr, ADF_GEN2_UERRSSMSH(i), val);
val = ADF_CSR_RD(pmisc_addr, ADF_GEN2_CERRSSMSH(i));
val |= ADF_GEN2_ERRSSMSH_EN;
ADF_CSR_WR(pmisc_addr, ADF_GEN2_CERRSSMSH(i), val);
}
}
EXPORT_SYMBOL_GPL(adf_gen2_enable_error_correction);
void adf_gen2_cfg_iov_thds(struct adf_accel_dev *accel_dev, bool enable,
int num_a_regs, int num_b_regs)
{
void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev);
u32 reg;
int i;
/* Set/Unset Valid bit in AE Thread to PCIe Function Mapping Group A */
for (i = 0; i < num_a_regs; i++) {
reg = READ_CSR_AE2FUNCTION_MAP_A(pmisc_addr, i);
if (enable)
reg |= AE2FUNCTION_MAP_VALID;
else
reg &= ~AE2FUNCTION_MAP_VALID;
WRITE_CSR_AE2FUNCTION_MAP_A(pmisc_addr, i, reg);
}
/* Set/Unset Valid bit in AE Thread to PCIe Function Mapping Group B */
for (i = 0; i < num_b_regs; i++) {
reg = READ_CSR_AE2FUNCTION_MAP_B(pmisc_addr, i);
if (enable)
reg |= AE2FUNCTION_MAP_VALID;
else
reg &= ~AE2FUNCTION_MAP_VALID;
WRITE_CSR_AE2FUNCTION_MAP_B(pmisc_addr, i, reg);
}
}
EXPORT_SYMBOL_GPL(adf_gen2_cfg_iov_thds);
void adf_gen2_get_admin_info(struct admin_info *admin_csrs_info)
{
admin_csrs_info->mailbox_offset = ADF_MAILBOX_BASE_OFFSET;
admin_csrs_info->admin_msg_ur = ADF_ADMINMSGUR_OFFSET;
admin_csrs_info->admin_msg_lr = ADF_ADMINMSGLR_OFFSET;
}
EXPORT_SYMBOL_GPL(adf_gen2_get_admin_info);
void adf_gen2_get_arb_info(struct arb_info *arb_info)
{
arb_info->arb_cfg = ADF_ARB_CONFIG;
arb_info->arb_offset = ADF_ARB_OFFSET;
arb_info->wt2sam_offset = ADF_ARB_WRK_2_SER_MAP_OFFSET;
}
EXPORT_SYMBOL_GPL(adf_gen2_get_arb_info);
void adf_gen2_enable_ints(struct adf_accel_dev *accel_dev)
{
void __iomem *addr = adf_get_pmisc_base(accel_dev);
u32 val;
val = accel_dev->pf.vf_info ? 0 : BIT_ULL(GET_MAX_BANKS(accel_dev)) - 1;
/* Enable bundle and misc interrupts */
ADF_CSR_WR(addr, ADF_GEN2_SMIAPF0_MASK_OFFSET, val);
ADF_CSR_WR(addr, ADF_GEN2_SMIAPF1_MASK_OFFSET, ADF_GEN2_SMIA1_MASK);
}
EXPORT_SYMBOL_GPL(adf_gen2_enable_ints);
u32 adf_gen2_get_accel_cap(struct adf_accel_dev *accel_dev)
{
struct adf_hw_device_data *hw_data = accel_dev->hw_device;
struct pci_dev *pdev = accel_dev->accel_pci_dev.pci_dev;
u32 fuses = hw_data->fuses[ADF_FUSECTL0];
u32 straps = hw_data->straps;
u32 legfuses;
u32 capabilities = ICP_ACCEL_CAPABILITIES_CRYPTO_SYMMETRIC |
ICP_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC |
ICP_ACCEL_CAPABILITIES_AUTHENTICATION |
ICP_ACCEL_CAPABILITIES_CIPHER |
ICP_ACCEL_CAPABILITIES_COMPRESSION;
/* Read accelerator capabilities mask */
pci_read_config_dword(pdev, ADF_DEVICE_LEGFUSE_OFFSET, &legfuses);
/* A set bit in legfuses means the feature is OFF in this SKU */
if (legfuses & ICP_ACCEL_MASK_CIPHER_SLICE) {
capabilities &= ~ICP_ACCEL_CAPABILITIES_CRYPTO_SYMMETRIC;
capabilities &= ~ICP_ACCEL_CAPABILITIES_CIPHER;
}
if (legfuses & ICP_ACCEL_MASK_PKE_SLICE)
capabilities &= ~ICP_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC;
if (legfuses & ICP_ACCEL_MASK_AUTH_SLICE) {
capabilities &= ~ICP_ACCEL_CAPABILITIES_AUTHENTICATION;
capabilities &= ~ICP_ACCEL_CAPABILITIES_CIPHER;
}
if (legfuses & ICP_ACCEL_MASK_COMPRESS_SLICE)
capabilities &= ~ICP_ACCEL_CAPABILITIES_COMPRESSION;
if ((straps | fuses) & ADF_POWERGATE_PKE)
capabilities &= ~ICP_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC;
if ((straps | fuses) & ADF_POWERGATE_DC)
capabilities &= ~ICP_ACCEL_CAPABILITIES_COMPRESSION;
return capabilities;
}
EXPORT_SYMBOL_GPL(adf_gen2_get_accel_cap);
void adf_gen2_set_ssm_wdtimer(struct adf_accel_dev *accel_dev)
{
struct adf_hw_device_data *hw_data = accel_dev->hw_device;
void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev);
u32 timer_val_pke = ADF_SSM_WDT_PKE_DEFAULT_VALUE;
u32 timer_val = ADF_SSM_WDT_DEFAULT_VALUE;
unsigned long accel_mask = hw_data->accel_mask;
u32 i = 0;
/* Configures WDT timers */
for_each_set_bit(i, &accel_mask, hw_data->num_accel) {
/* Enable WDT for sym and dc */
ADF_CSR_WR(pmisc_addr, ADF_SSMWDT(i), timer_val);
/* Enable WDT for pke */
ADF_CSR_WR(pmisc_addr, ADF_SSMWDTPKE(i), timer_val_pke);
}
}
EXPORT_SYMBOL_GPL(adf_gen2_set_ssm_wdtimer);
static int adf_gen2_build_comp_block(void *ctx, enum adf_dc_algo algo)
{
struct icp_qat_fw_comp_req *req_tmpl = ctx;
struct icp_qat_fw_comp_req_hdr_cd_pars *cd_pars = &req_tmpl->cd_pars;
struct icp_qat_fw_comn_req_hdr *header = &req_tmpl->comn_hdr;
switch (algo) {
case QAT_DEFLATE:
header->service_cmd_id = ICP_QAT_FW_COMP_CMD_STATIC;
break;
default:
return -EINVAL;
}
cd_pars->u.sl.comp_slice_cfg_word[0] =
ICP_QAT_HW_COMPRESSION_CONFIG_BUILD(ICP_QAT_HW_COMPRESSION_DIR_COMPRESS,
ICP_QAT_HW_COMPRESSION_DELAYED_MATCH_DISABLED,
ICP_QAT_HW_COMPRESSION_ALGO_DEFLATE,
ICP_QAT_HW_COMPRESSION_DEPTH_1,
ICP_QAT_HW_COMPRESSION_FILE_TYPE_0);
return 0;
}
static int adf_gen2_build_decomp_block(void *ctx, enum adf_dc_algo algo)
{
struct icp_qat_fw_comp_req *req_tmpl = ctx;
struct icp_qat_fw_comp_req_hdr_cd_pars *cd_pars = &req_tmpl->cd_pars;
struct icp_qat_fw_comn_req_hdr *header = &req_tmpl->comn_hdr;
switch (algo) {
case QAT_DEFLATE:
header->service_cmd_id = ICP_QAT_FW_COMP_CMD_DECOMPRESS;
break;
default:
return -EINVAL;
}
cd_pars->u.sl.comp_slice_cfg_word[0] =
ICP_QAT_HW_COMPRESSION_CONFIG_BUILD(ICP_QAT_HW_COMPRESSION_DIR_DECOMPRESS,
ICP_QAT_HW_COMPRESSION_DELAYED_MATCH_DISABLED,
ICP_QAT_HW_COMPRESSION_ALGO_DEFLATE,
ICP_QAT_HW_COMPRESSION_DEPTH_1,
ICP_QAT_HW_COMPRESSION_FILE_TYPE_0);
return 0;
}
void adf_gen2_init_dc_ops(struct adf_dc_ops *dc_ops)
{
dc_ops->build_comp_block = adf_gen2_build_comp_block;
dc_ops->build_decomp_block = adf_gen2_build_decomp_block;
}
EXPORT_SYMBOL_GPL(adf_gen2_init_dc_ops);
@@ -0,0 +1,93 @@
/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */
/* Copyright(c) 2020 Intel Corporation */
#ifndef ADF_GEN2_HW_DATA_H_
#define ADF_GEN2_HW_DATA_H_
#include "adf_accel_devices.h"
#include "adf_cfg_common.h"
#define ADF_GEN2_RX_RINGS_OFFSET 8
#define ADF_GEN2_TX_RINGS_MASK 0xFF
/* AE to function map */
#define AE2FUNCTION_MAP_A_OFFSET (0x3A400 + 0x190)
#define AE2FUNCTION_MAP_B_OFFSET (0x3A400 + 0x310)
#define AE2FUNCTION_MAP_REG_SIZE 4
#define AE2FUNCTION_MAP_VALID BIT(7)
#define READ_CSR_AE2FUNCTION_MAP_A(pmisc_bar_addr, index) \
ADF_CSR_RD(pmisc_bar_addr, AE2FUNCTION_MAP_A_OFFSET + \
AE2FUNCTION_MAP_REG_SIZE * (index))
#define WRITE_CSR_AE2FUNCTION_MAP_A(pmisc_bar_addr, index, value) \
ADF_CSR_WR(pmisc_bar_addr, AE2FUNCTION_MAP_A_OFFSET + \
AE2FUNCTION_MAP_REG_SIZE * (index), value)
#define READ_CSR_AE2FUNCTION_MAP_B(pmisc_bar_addr, index) \
ADF_CSR_RD(pmisc_bar_addr, AE2FUNCTION_MAP_B_OFFSET + \
AE2FUNCTION_MAP_REG_SIZE * (index))
#define WRITE_CSR_AE2FUNCTION_MAP_B(pmisc_bar_addr, index, value) \
ADF_CSR_WR(pmisc_bar_addr, AE2FUNCTION_MAP_B_OFFSET + \
AE2FUNCTION_MAP_REG_SIZE * (index), value)
/* Admin Interface Offsets */
#define ADF_ADMINMSGUR_OFFSET (0x3A000 + 0x574)
#define ADF_ADMINMSGLR_OFFSET (0x3A000 + 0x578)
#define ADF_MAILBOX_BASE_OFFSET 0x20970
/* Arbiter configuration */
#define ADF_ARB_OFFSET 0x30000
#define ADF_ARB_WRK_2_SER_MAP_OFFSET 0x180
#define ADF_ARB_CONFIG (BIT(31) | BIT(6) | BIT(0))
/* Power gating */
#define ADF_POWERGATE_DC BIT(23)
#define ADF_POWERGATE_PKE BIT(24)
/* Default ring mapping */
#define ADF_GEN2_DEFAULT_RING_TO_SRV_MAP \
(CRYPTO << ADF_CFG_SERV_RING_PAIR_0_SHIFT | \
CRYPTO << ADF_CFG_SERV_RING_PAIR_1_SHIFT | \
UNUSED << ADF_CFG_SERV_RING_PAIR_2_SHIFT | \
COMP << ADF_CFG_SERV_RING_PAIR_3_SHIFT)
/* WDT timers
*
* Timeout is in cycles. Clock speed may vary across products but this
* value should be a few milli-seconds.
*/
#define ADF_SSM_WDT_DEFAULT_VALUE 0x200000
#define ADF_SSM_WDT_PKE_DEFAULT_VALUE 0x2000000
#define ADF_SSMWDT_OFFSET 0x54
#define ADF_SSMWDTPKE_OFFSET 0x58
#define ADF_SSMWDT(i) (ADF_SSMWDT_OFFSET + ((i) * 0x4000))
#define ADF_SSMWDTPKE(i) (ADF_SSMWDTPKE_OFFSET + ((i) * 0x4000))
/* Error detection and correction */
#define ADF_GEN2_AE_CTX_ENABLES(i) ((i) * 0x1000 + 0x20818)
#define ADF_GEN2_AE_MISC_CONTROL(i) ((i) * 0x1000 + 0x20960)
#define ADF_GEN2_ENABLE_AE_ECC_ERR BIT(28)
#define ADF_GEN2_ENABLE_AE_ECC_PARITY_CORR (BIT(24) | BIT(12))
#define ADF_GEN2_UERRSSMSH(i) ((i) * 0x4000 + 0x18)
#define ADF_GEN2_CERRSSMSH(i) ((i) * 0x4000 + 0x10)
#define ADF_GEN2_ERRSSMSH_EN BIT(3)
/* Number of heartbeat counter pairs */
#define ADF_NUM_HB_CNT_PER_AE ADF_NUM_THREADS_PER_AE
/* Interrupts */
#define ADF_GEN2_SMIAPF0_MASK_OFFSET (0x3A000 + 0x28)
#define ADF_GEN2_SMIAPF1_MASK_OFFSET (0x3A000 + 0x30)
#define ADF_GEN2_SMIA1_MASK 0x1
u32 adf_gen2_get_num_accels(struct adf_hw_device_data *self);
u32 adf_gen2_get_num_aes(struct adf_hw_device_data *self);
void adf_gen2_enable_error_correction(struct adf_accel_dev *accel_dev);
void adf_gen2_cfg_iov_thds(struct adf_accel_dev *accel_dev, bool enable,
int num_a_regs, int num_b_regs);
void adf_gen2_get_admin_info(struct admin_info *admin_csrs_info);
void adf_gen2_get_arb_info(struct arb_info *arb_info);
void adf_gen2_enable_ints(struct adf_accel_dev *accel_dev);
u32 adf_gen2_get_accel_cap(struct adf_accel_dev *accel_dev);
void adf_gen2_set_ssm_wdtimer(struct adf_accel_dev *accel_dev);
void adf_gen2_init_dc_ops(struct adf_dc_ops *dc_ops);
#endif
@@ -0,0 +1,401 @@
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2021 Intel Corporation */
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include "adf_accel_devices.h"
#include "adf_common_drv.h"
#include "adf_gen2_pfvf.h"
#include "adf_pfvf_msg.h"
#include "adf_pfvf_pf_proto.h"
#include "adf_pfvf_vf_proto.h"
#include "adf_pfvf_utils.h"
/* VF2PF interrupts */
#define ADF_GEN2_VF_MSK 0xFFFF
#define ADF_GEN2_ERR_REG_VF2PF(vf_src) (((vf_src) & 0x01FFFE00) >> 9)
#define ADF_GEN2_ERR_MSK_VF2PF(vf_mask) (((vf_mask) & ADF_GEN2_VF_MSK) << 9)
#define ADF_GEN2_PF_PF2VF_OFFSET(i) (0x3A000 + 0x280 + ((i) * 0x04))
#define ADF_GEN2_VF_PF2VF_OFFSET 0x200
#define ADF_GEN2_CSR_IN_USE 0x6AC2
#define ADF_GEN2_CSR_IN_USE_MASK 0xFFFE
enum gen2_csr_pos {
ADF_GEN2_CSR_PF2VF_OFFSET = 0,
ADF_GEN2_CSR_VF2PF_OFFSET = 16,
};
#define ADF_PFVF_GEN2_MSGTYPE_SHIFT 2
#define ADF_PFVF_GEN2_MSGTYPE_MASK 0x0F
#define ADF_PFVF_GEN2_MSGDATA_SHIFT 6
#define ADF_PFVF_GEN2_MSGDATA_MASK 0x3FF
static const struct pfvf_csr_format csr_gen2_fmt = {
{ ADF_PFVF_GEN2_MSGTYPE_SHIFT, ADF_PFVF_GEN2_MSGTYPE_MASK },
{ ADF_PFVF_GEN2_MSGDATA_SHIFT, ADF_PFVF_GEN2_MSGDATA_MASK },
};
#define ADF_PFVF_MSG_RETRY_DELAY 5
#define ADF_PFVF_MSG_MAX_RETRIES 3
static u32 adf_gen2_pf_get_pfvf_offset(u32 i)
{
return ADF_GEN2_PF_PF2VF_OFFSET(i);
}
static u32 adf_gen2_vf_get_pfvf_offset(u32 i)
{
return ADF_GEN2_VF_PF2VF_OFFSET;
}
static void adf_gen2_enable_vf2pf_interrupts(void __iomem *pmisc_addr, u32 vf_mask)
{
/* Enable VF2PF Messaging Ints - VFs 0 through 15 per vf_mask[15:0] */
if (vf_mask & ADF_GEN2_VF_MSK) {
u32 val = ADF_CSR_RD(pmisc_addr, ADF_GEN2_ERRMSK3)
& ~ADF_GEN2_ERR_MSK_VF2PF(vf_mask);
ADF_CSR_WR(pmisc_addr, ADF_GEN2_ERRMSK3, val);
}
}
static void adf_gen2_disable_all_vf2pf_interrupts(void __iomem *pmisc_addr)
{
/* Disable VF2PF interrupts for VFs 0 through 15 per vf_mask[15:0] */
u32 val = ADF_CSR_RD(pmisc_addr, ADF_GEN2_ERRMSK3)
| ADF_GEN2_ERR_MSK_VF2PF(ADF_GEN2_VF_MSK);
ADF_CSR_WR(pmisc_addr, ADF_GEN2_ERRMSK3, val);
}
static u32 adf_gen2_disable_pending_vf2pf_interrupts(void __iomem *pmisc_addr)
{
u32 sources, disabled, pending;
u32 errsou3, errmsk3;
/* Get the interrupt sources triggered by VFs */
errsou3 = ADF_CSR_RD(pmisc_addr, ADF_GEN2_ERRSOU3);
sources = ADF_GEN2_ERR_REG_VF2PF(errsou3);
if (!sources)
return 0;
/* Get the already disabled interrupts */
errmsk3 = ADF_CSR_RD(pmisc_addr, ADF_GEN2_ERRMSK3);
disabled = ADF_GEN2_ERR_REG_VF2PF(errmsk3);
pending = sources & ~disabled;
if (!pending)
return 0;
/* Due to HW limitations, when disabling the interrupts, we can't
* just disable the requested sources, as this would lead to missed
* interrupts if ERRSOU3 changes just before writing to ERRMSK3.
* To work around it, disable all and re-enable only the sources that
* are not in vf_mask and were not already disabled. Re-enabling will
* trigger a new interrupt for the sources that have changed in the
* meantime, if any.
*/
errmsk3 |= ADF_GEN2_ERR_MSK_VF2PF(ADF_GEN2_VF_MSK);
ADF_CSR_WR(pmisc_addr, ADF_GEN2_ERRMSK3, errmsk3);
/* Update only section of errmsk3 related to VF2PF */
errmsk3 &= ~ADF_GEN2_ERR_MSK_VF2PF(ADF_GEN2_VF_MSK);
errmsk3 |= ADF_GEN2_ERR_MSK_VF2PF(sources | disabled);
ADF_CSR_WR(pmisc_addr, ADF_GEN2_ERRMSK3, errmsk3);
/* Return the sources of the (new) interrupt(s) */
return pending;
}
static u32 gen2_csr_get_int_bit(enum gen2_csr_pos offset)
{
return ADF_PFVF_INT << offset;
}
static u32 gen2_csr_msg_to_position(u32 csr_msg, enum gen2_csr_pos offset)
{
return (csr_msg & 0xFFFF) << offset;
}
static u32 gen2_csr_msg_from_position(u32 csr_val, enum gen2_csr_pos offset)
{
return (csr_val >> offset) & 0xFFFF;
}
static bool gen2_csr_is_in_use(u32 msg, enum gen2_csr_pos offset)
{
return ((msg >> offset) & ADF_GEN2_CSR_IN_USE_MASK) == ADF_GEN2_CSR_IN_USE;
}
static void gen2_csr_clear_in_use(u32 *msg, enum gen2_csr_pos offset)
{
*msg &= ~(ADF_GEN2_CSR_IN_USE_MASK << offset);
}
static void gen2_csr_set_in_use(u32 *msg, enum gen2_csr_pos offset)
{
*msg |= (ADF_GEN2_CSR_IN_USE << offset);
}
static bool is_legacy_user_pfvf_message(u32 msg)
{
return !(msg & ADF_PFVF_MSGORIGIN_SYSTEM);
}
static bool is_pf2vf_notification(u8 msg_type)
{
switch (msg_type) {
case ADF_PF2VF_MSGTYPE_RESTARTING:
return true;
default:
return false;
}
}
static bool is_vf2pf_notification(u8 msg_type)
{
switch (msg_type) {
case ADF_VF2PF_MSGTYPE_INIT:
case ADF_VF2PF_MSGTYPE_SHUTDOWN:
return true;
default:
return false;
}
}
struct pfvf_gen2_params {
u32 pfvf_offset;
struct mutex *csr_lock; /* lock preventing concurrent access of CSR */
enum gen2_csr_pos local_offset;
enum gen2_csr_pos remote_offset;
bool (*is_notification_message)(u8 msg_type);
u8 compat_ver;
};
static int adf_gen2_pfvf_send(struct adf_accel_dev *accel_dev,
struct pfvf_message msg,
struct pfvf_gen2_params *params)
{
void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev);
enum gen2_csr_pos remote_offset = params->remote_offset;
enum gen2_csr_pos local_offset = params->local_offset;
unsigned int retries = ADF_PFVF_MSG_MAX_RETRIES;
struct mutex *lock = params->csr_lock;
u32 pfvf_offset = params->pfvf_offset;
u32 int_bit;
u32 csr_val;
u32 csr_msg;
int ret;
/* Gen2 messages, both PF->VF and VF->PF, are all 16 bits long. This
* allows us to build and read messages as if they where all 0 based.
* However, send and receive are in a single shared 32 bits register,
* so we need to shift and/or mask the message half before decoding
* it and after encoding it. Which one to shift depends on the
* direction.
*/
int_bit = gen2_csr_get_int_bit(local_offset);
csr_msg = adf_pfvf_csr_msg_of(accel_dev, msg, &csr_gen2_fmt);
if (unlikely(!csr_msg))
return -EINVAL;
/* Prepare for CSR format, shifting the wire message in place and
* setting the in use pattern
*/
csr_msg = gen2_csr_msg_to_position(csr_msg, local_offset);
gen2_csr_set_in_use(&csr_msg, remote_offset);
mutex_lock(lock);
start:
/* Check if the PFVF CSR is in use by remote function */
csr_val = ADF_CSR_RD(pmisc_addr, pfvf_offset);
if (gen2_csr_is_in_use(csr_val, local_offset)) {
dev_dbg(&GET_DEV(accel_dev),
"PFVF CSR in use by remote function\n");
goto retry;
}
/* Attempt to get ownership of the PFVF CSR */
ADF_CSR_WR(pmisc_addr, pfvf_offset, csr_msg | int_bit);
/* Wait for confirmation from remote func it received the message */
ret = read_poll_timeout(ADF_CSR_RD, csr_val, !(csr_val & int_bit),
ADF_PFVF_MSG_ACK_DELAY_US,
ADF_PFVF_MSG_ACK_MAX_DELAY_US,
true, pmisc_addr, pfvf_offset);
if (unlikely(ret < 0)) {
dev_dbg(&GET_DEV(accel_dev), "ACK not received from remote\n");
csr_val &= ~int_bit;
}
/* For fire-and-forget notifications, the receiver does not clear
* the in-use pattern. This is used to detect collisions.
*/
if (params->is_notification_message(msg.type) && csr_val != csr_msg) {
/* Collision must have overwritten the message */
dev_err(&GET_DEV(accel_dev),
"Collision on notification - PFVF CSR overwritten by remote function\n");
goto retry;
}
/* If the far side did not clear the in-use pattern it is either
* 1) Notification - message left intact to detect collision
* 2) Older protocol (compatibility version < 3) on the far side
* where the sender is responsible for clearing the in-use
* pattern after the received has acknowledged receipt.
* In either case, clear the in-use pattern now.
*/
if (gen2_csr_is_in_use(csr_val, remote_offset)) {
gen2_csr_clear_in_use(&csr_val, remote_offset);
ADF_CSR_WR(pmisc_addr, pfvf_offset, csr_val);
}
out:
mutex_unlock(lock);
return ret;
retry:
if (--retries) {
msleep(ADF_PFVF_MSG_RETRY_DELAY);
goto start;
} else {
ret = -EBUSY;
goto out;
}
}
static struct pfvf_message adf_gen2_pfvf_recv(struct adf_accel_dev *accel_dev,
struct pfvf_gen2_params *params)
{
void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev);
enum gen2_csr_pos remote_offset = params->remote_offset;
enum gen2_csr_pos local_offset = params->local_offset;
u32 pfvf_offset = params->pfvf_offset;
struct pfvf_message msg = { 0 };
u32 int_bit;
u32 csr_val;
u16 csr_msg;
int_bit = gen2_csr_get_int_bit(local_offset);
/* Read message */
csr_val = ADF_CSR_RD(pmisc_addr, pfvf_offset);
if (!(csr_val & int_bit)) {
dev_info(&GET_DEV(accel_dev),
"Spurious PFVF interrupt, msg 0x%.8x. Ignored\n", csr_val);
return msg;
}
/* Extract the message from the CSR */
csr_msg = gen2_csr_msg_from_position(csr_val, local_offset);
/* Ignore legacy non-system (non-kernel) messages */
if (unlikely(is_legacy_user_pfvf_message(csr_msg))) {
dev_dbg(&GET_DEV(accel_dev),
"Ignored non-system message (0x%.8x);\n", csr_val);
/* Because this must be a legacy message, the far side
* must clear the in-use pattern, so don't do it.
*/
return msg;
}
/* Return the pfvf_message format */
msg = adf_pfvf_message_of(accel_dev, csr_msg, &csr_gen2_fmt);
/* The in-use pattern is not cleared for notifications (so that
* it can be used for collision detection) or older implementations
*/
if (params->compat_ver >= ADF_PFVF_COMPAT_FAST_ACK &&
!params->is_notification_message(msg.type))
gen2_csr_clear_in_use(&csr_val, remote_offset);
/* To ACK, clear the INT bit */
csr_val &= ~int_bit;
ADF_CSR_WR(pmisc_addr, pfvf_offset, csr_val);
return msg;
}
static int adf_gen2_pf2vf_send(struct adf_accel_dev *accel_dev, struct pfvf_message msg,
u32 pfvf_offset, struct mutex *csr_lock)
{
struct pfvf_gen2_params params = {
.csr_lock = csr_lock,
.pfvf_offset = pfvf_offset,
.local_offset = ADF_GEN2_CSR_PF2VF_OFFSET,
.remote_offset = ADF_GEN2_CSR_VF2PF_OFFSET,
.is_notification_message = is_pf2vf_notification,
};
return adf_gen2_pfvf_send(accel_dev, msg, &params);
}
static int adf_gen2_vf2pf_send(struct adf_accel_dev *accel_dev, struct pfvf_message msg,
u32 pfvf_offset, struct mutex *csr_lock)
{
struct pfvf_gen2_params params = {
.csr_lock = csr_lock,
.pfvf_offset = pfvf_offset,
.local_offset = ADF_GEN2_CSR_VF2PF_OFFSET,
.remote_offset = ADF_GEN2_CSR_PF2VF_OFFSET,
.is_notification_message = is_vf2pf_notification,
};
return adf_gen2_pfvf_send(accel_dev, msg, &params);
}
static struct pfvf_message adf_gen2_pf2vf_recv(struct adf_accel_dev *accel_dev,
u32 pfvf_offset, u8 compat_ver)
{
struct pfvf_gen2_params params = {
.pfvf_offset = pfvf_offset,
.local_offset = ADF_GEN2_CSR_PF2VF_OFFSET,
.remote_offset = ADF_GEN2_CSR_VF2PF_OFFSET,
.is_notification_message = is_pf2vf_notification,
.compat_ver = compat_ver,
};
return adf_gen2_pfvf_recv(accel_dev, &params);
}
static struct pfvf_message adf_gen2_vf2pf_recv(struct adf_accel_dev *accel_dev,
u32 pfvf_offset, u8 compat_ver)
{
struct pfvf_gen2_params params = {
.pfvf_offset = pfvf_offset,
.local_offset = ADF_GEN2_CSR_VF2PF_OFFSET,
.remote_offset = ADF_GEN2_CSR_PF2VF_OFFSET,
.is_notification_message = is_vf2pf_notification,
.compat_ver = compat_ver,
};
return adf_gen2_pfvf_recv(accel_dev, &params);
}
void adf_gen2_init_pf_pfvf_ops(struct adf_pfvf_ops *pfvf_ops)
{
pfvf_ops->enable_comms = adf_enable_pf2vf_comms;
pfvf_ops->get_pf2vf_offset = adf_gen2_pf_get_pfvf_offset;
pfvf_ops->get_vf2pf_offset = adf_gen2_pf_get_pfvf_offset;
pfvf_ops->enable_vf2pf_interrupts = adf_gen2_enable_vf2pf_interrupts;
pfvf_ops->disable_all_vf2pf_interrupts = adf_gen2_disable_all_vf2pf_interrupts;
pfvf_ops->disable_pending_vf2pf_interrupts = adf_gen2_disable_pending_vf2pf_interrupts;
pfvf_ops->send_msg = adf_gen2_pf2vf_send;
pfvf_ops->recv_msg = adf_gen2_vf2pf_recv;
}
EXPORT_SYMBOL_GPL(adf_gen2_init_pf_pfvf_ops);
void adf_gen2_init_vf_pfvf_ops(struct adf_pfvf_ops *pfvf_ops)
{
pfvf_ops->enable_comms = adf_enable_vf2pf_comms;
pfvf_ops->get_pf2vf_offset = adf_gen2_vf_get_pfvf_offset;
pfvf_ops->get_vf2pf_offset = adf_gen2_vf_get_pfvf_offset;
pfvf_ops->send_msg = adf_gen2_vf2pf_send;
pfvf_ops->recv_msg = adf_gen2_pf2vf_recv;
}
EXPORT_SYMBOL_GPL(adf_gen2_init_vf_pfvf_ops);
@@ -0,0 +1,30 @@
/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */
/* Copyright(c) 2021 Intel Corporation */
#ifndef ADF_GEN2_PFVF_H
#define ADF_GEN2_PFVF_H
#include <linux/types.h>
#include "adf_accel_devices.h"
#include "adf_common_drv.h"
#define ADF_GEN2_ERRSOU3 (0x3A000 + 0x0C)
#define ADF_GEN2_ERRSOU5 (0x3A000 + 0xD8)
#define ADF_GEN2_ERRMSK3 (0x3A000 + 0x1C)
#define ADF_GEN2_ERRMSK5 (0x3A000 + 0xDC)
#if defined(CONFIG_PCI_IOV)
void adf_gen2_init_pf_pfvf_ops(struct adf_pfvf_ops *pfvf_ops);
void adf_gen2_init_vf_pfvf_ops(struct adf_pfvf_ops *pfvf_ops);
#else
static inline void adf_gen2_init_pf_pfvf_ops(struct adf_pfvf_ops *pfvf_ops)
{
pfvf_ops->enable_comms = adf_pfvf_comms_disabled;
}
static inline void adf_gen2_init_vf_pfvf_ops(struct adf_pfvf_ops *pfvf_ops)
{
pfvf_ops->enable_comms = adf_pfvf_comms_disabled;
}
#endif
#endif /* ADF_GEN2_PFVF_H */
@@ -0,0 +1,276 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2023 Intel Corporation */
#include "adf_accel_devices.h"
#include "adf_cfg.h"
#include "adf_cfg_services.h"
#include "adf_cfg_strings.h"
#include "adf_common_drv.h"
#include "adf_gen4_config.h"
#include "adf_heartbeat.h"
#include "adf_transport_access_macros.h"
#include "qat_compression.h"
#include "qat_crypto.h"
int adf_crypto_dev_config(struct adf_accel_dev *accel_dev)
{
char key[ADF_CFG_MAX_KEY_LEN_IN_BYTES];
int banks = GET_MAX_BANKS(accel_dev);
int cpus = num_online_cpus();
unsigned long bank, val;
int instances;
int ret;
int i;
if (adf_hw_dev_has_crypto(accel_dev))
instances = min(cpus, banks / 2);
else
instances = 0;
for (i = 0; i < instances; i++) {
val = i;
bank = i * 2;
snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_BANK_NUM, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &bank, ADF_DEC);
if (ret)
goto err;
bank += 1;
snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_BANK_NUM, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &bank, ADF_DEC);
if (ret)
goto err;
snprintf(key, sizeof(key), ADF_CY "%d" ADF_ETRMGR_CORE_AFFINITY,
i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_SIZE, i);
val = 128;
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
val = 512;
snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_SIZE, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
val = 0;
snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_TX, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
val = 0;
snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_TX, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
val = 1;
snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_RX, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
val = 1;
snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_RX, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
val = ADF_COALESCING_DEF_TIME;
snprintf(key, sizeof(key), ADF_ETRMGR_COALESCE_TIMER_FORMAT, i);
ret = adf_cfg_add_key_value_param(accel_dev, "Accelerator0",
key, &val, ADF_DEC);
if (ret)
goto err;
}
val = i;
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, ADF_NUM_CY,
&val, ADF_DEC);
if (ret)
goto err;
val = 0;
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, ADF_NUM_DC,
&val, ADF_DEC);
if (ret)
goto err;
return 0;
err:
dev_err(&GET_DEV(accel_dev), "Failed to add configuration for crypto\n");
return ret;
}
int adf_comp_dev_config(struct adf_accel_dev *accel_dev)
{
char key[ADF_CFG_MAX_KEY_LEN_IN_BYTES];
int banks = GET_MAX_BANKS(accel_dev);
int cpus = num_online_cpus();
unsigned long val;
int instances;
int ret;
int i;
if (adf_hw_dev_has_compression(accel_dev))
instances = min(cpus, banks);
else
instances = 0;
for (i = 0; i < instances; i++) {
val = i;
snprintf(key, sizeof(key), ADF_DC "%d" ADF_RING_DC_BANK_NUM, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
val = 512;
snprintf(key, sizeof(key), ADF_DC "%d" ADF_RING_DC_SIZE, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
val = 0;
snprintf(key, sizeof(key), ADF_DC "%d" ADF_RING_DC_TX, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
val = 1;
snprintf(key, sizeof(key), ADF_DC "%d" ADF_RING_DC_RX, i);
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
key, &val, ADF_DEC);
if (ret)
goto err;
val = ADF_COALESCING_DEF_TIME;
snprintf(key, sizeof(key), ADF_ETRMGR_COALESCE_TIMER_FORMAT, i);
ret = adf_cfg_add_key_value_param(accel_dev, "Accelerator0",
key, &val, ADF_DEC);
if (ret)
goto err;
}
val = i;
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, ADF_NUM_DC,
&val, ADF_DEC);
if (ret)
goto err;
val = 0;
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, ADF_NUM_CY,
&val, ADF_DEC);
if (ret)
goto err;
return 0;
err:
dev_err(&GET_DEV(accel_dev), "Failed to add configuration for compression\n");
return ret;
}
int adf_no_dev_config(struct adf_accel_dev *accel_dev)
{
unsigned long val;
int ret;
val = 0;
ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, ADF_NUM_DC,
&val, ADF_DEC);
if (ret)
return ret;
return adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, ADF_NUM_CY,
&val, ADF_DEC);
}
/**
* adf_gen4_dev_config() - create dev config required to create instances
*
* @accel_dev: Pointer to acceleration device.
*
* Function creates device configuration required to create instances
*
* Return: 0 on success, error code otherwise.
*/
int adf_gen4_dev_config(struct adf_accel_dev *accel_dev)
{
int ret;
ret = adf_cfg_section_add(accel_dev, ADF_KERNEL_SEC);
if (ret)
goto err;
ret = adf_cfg_section_add(accel_dev, "Accelerator0");
if (ret)
goto err;
switch (adf_get_service_enabled(accel_dev)) {
case SVC_SYM_ASYM:
ret = adf_crypto_dev_config(accel_dev);
break;
case SVC_DC:
case SVC_DCC:
ret = adf_comp_dev_config(accel_dev);
break;
default:
ret = adf_no_dev_config(accel_dev);
break;
}
if (ret)
goto err;
set_bit(ADF_STATUS_CONFIGURED, &accel_dev->status);
return ret;
err:
dev_err(&GET_DEV(accel_dev), "Failed to configure QAT driver\n");
return ret;
}
EXPORT_SYMBOL_GPL(adf_gen4_dev_config);
int adf_gen4_cfg_dev_init(struct adf_accel_dev *accel_dev)
{
const char *config;
int ret;
config = accel_dev->accel_id % 2 ? ADF_CFG_DC : ADF_CFG_CY;
ret = adf_cfg_section_add(accel_dev, ADF_GENERAL_SEC);
if (ret)
return ret;
/* Default configuration is crypto only for even devices
* and compression for odd devices
*/
ret = adf_cfg_add_key_value_param(accel_dev, ADF_GENERAL_SEC,
ADF_SERVICES_ENABLED, config,
ADF_STR);
if (ret)
return ret;
adf_heartbeat_save_cfg_param(accel_dev, ADF_CFG_HB_TIMER_MIN_MS);
return 0;
}
EXPORT_SYMBOL_GPL(adf_gen4_cfg_dev_init);
@@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright(c) 2023 Intel Corporation */
#ifndef ADF_GEN4_CONFIG_H_
#define ADF_GEN4_CONFIG_H_
#include "adf_accel_devices.h"
int adf_gen4_dev_config(struct adf_accel_dev *accel_dev);
int adf_gen4_cfg_dev_init(struct adf_accel_dev *accel_dev);
int adf_crypto_dev_config(struct adf_accel_dev *accel_dev);
int adf_comp_dev_config(struct adf_accel_dev *accel_dev);
int adf_no_dev_config(struct adf_accel_dev *accel_dev);
#endif
@@ -0,0 +1,231 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2024 Intel Corporation */
#include <linux/types.h>
#include "adf_gen4_hw_csr_data.h"
static u64 build_csr_ring_base_addr(dma_addr_t addr, u32 size)
{
return BUILD_RING_BASE_ADDR(addr, size);
}
static u32 read_csr_ring_head(void __iomem *csr_base_addr, u32 bank, u32 ring)
{
return READ_CSR_RING_HEAD(csr_base_addr, bank, ring);
}
static void write_csr_ring_head(void __iomem *csr_base_addr, u32 bank, u32 ring,
u32 value)
{
WRITE_CSR_RING_HEAD(csr_base_addr, bank, ring, value);
}
static u32 read_csr_ring_tail(void __iomem *csr_base_addr, u32 bank, u32 ring)
{
return READ_CSR_RING_TAIL(csr_base_addr, bank, ring);
}
static void write_csr_ring_tail(void __iomem *csr_base_addr, u32 bank, u32 ring,
u32 value)
{
WRITE_CSR_RING_TAIL(csr_base_addr, bank, ring, value);
}
static u32 read_csr_stat(void __iomem *csr_base_addr, u32 bank)
{
return READ_CSR_STAT(csr_base_addr, bank);
}
static u32 read_csr_uo_stat(void __iomem *csr_base_addr, u32 bank)
{
return READ_CSR_UO_STAT(csr_base_addr, bank);
}
static u32 read_csr_e_stat(void __iomem *csr_base_addr, u32 bank)
{
return READ_CSR_E_STAT(csr_base_addr, bank);
}
static u32 read_csr_ne_stat(void __iomem *csr_base_addr, u32 bank)
{
return READ_CSR_NE_STAT(csr_base_addr, bank);
}
static u32 read_csr_nf_stat(void __iomem *csr_base_addr, u32 bank)
{
return READ_CSR_NF_STAT(csr_base_addr, bank);
}
static u32 read_csr_f_stat(void __iomem *csr_base_addr, u32 bank)
{
return READ_CSR_F_STAT(csr_base_addr, bank);
}
static u32 read_csr_c_stat(void __iomem *csr_base_addr, u32 bank)
{
return READ_CSR_C_STAT(csr_base_addr, bank);
}
static u32 read_csr_exp_stat(void __iomem *csr_base_addr, u32 bank)
{
return READ_CSR_EXP_STAT(csr_base_addr, bank);
}
static u32 read_csr_exp_int_en(void __iomem *csr_base_addr, u32 bank)
{
return READ_CSR_EXP_INT_EN(csr_base_addr, bank);
}
static void write_csr_exp_int_en(void __iomem *csr_base_addr, u32 bank,
u32 value)
{
WRITE_CSR_EXP_INT_EN(csr_base_addr, bank, value);
}
static u32 read_csr_ring_config(void __iomem *csr_base_addr, u32 bank,
u32 ring)
{
return READ_CSR_RING_CONFIG(csr_base_addr, bank, ring);
}
static void write_csr_ring_config(void __iomem *csr_base_addr, u32 bank, u32 ring,
u32 value)
{
WRITE_CSR_RING_CONFIG(csr_base_addr, bank, ring, value);
}
static dma_addr_t read_csr_ring_base(void __iomem *csr_base_addr, u32 bank,
u32 ring)
{
return READ_CSR_RING_BASE(csr_base_addr, bank, ring);
}
static void write_csr_ring_base(void __iomem *csr_base_addr, u32 bank, u32 ring,
dma_addr_t addr)
{
WRITE_CSR_RING_BASE(csr_base_addr, bank, ring, addr);
}
static u32 read_csr_int_en(void __iomem *csr_base_addr, u32 bank)
{
return READ_CSR_INT_EN(csr_base_addr, bank);
}
static void write_csr_int_en(void __iomem *csr_base_addr, u32 bank, u32 value)
{
WRITE_CSR_INT_EN(csr_base_addr, bank, value);
}
static u32 read_csr_int_flag(void __iomem *csr_base_addr, u32 bank)
{
return READ_CSR_INT_FLAG(csr_base_addr, bank);
}
static void write_csr_int_flag(void __iomem *csr_base_addr, u32 bank,
u32 value)
{
WRITE_CSR_INT_FLAG(csr_base_addr, bank, value);
}
static u32 read_csr_int_srcsel(void __iomem *csr_base_addr, u32 bank)
{
return READ_CSR_INT_SRCSEL(csr_base_addr, bank);
}
static void write_csr_int_srcsel(void __iomem *csr_base_addr, u32 bank)
{
WRITE_CSR_INT_SRCSEL(csr_base_addr, bank);
}
static void write_csr_int_srcsel_w_val(void __iomem *csr_base_addr, u32 bank,
u32 value)
{
WRITE_CSR_INT_SRCSEL_W_VAL(csr_base_addr, bank, value);
}
static u32 read_csr_int_col_en(void __iomem *csr_base_addr, u32 bank)
{
return READ_CSR_INT_COL_EN(csr_base_addr, bank);
}
static void write_csr_int_col_en(void __iomem *csr_base_addr, u32 bank, u32 value)
{
WRITE_CSR_INT_COL_EN(csr_base_addr, bank, value);
}
static u32 read_csr_int_col_ctl(void __iomem *csr_base_addr, u32 bank)
{
return READ_CSR_INT_COL_CTL(csr_base_addr, bank);
}
static void write_csr_int_col_ctl(void __iomem *csr_base_addr, u32 bank,
u32 value)
{
WRITE_CSR_INT_COL_CTL(csr_base_addr, bank, value);
}
static u32 read_csr_int_flag_and_col(void __iomem *csr_base_addr, u32 bank)
{
return READ_CSR_INT_FLAG_AND_COL(csr_base_addr, bank);
}
static void write_csr_int_flag_and_col(void __iomem *csr_base_addr, u32 bank,
u32 value)
{
WRITE_CSR_INT_FLAG_AND_COL(csr_base_addr, bank, value);
}
static u32 read_csr_ring_srv_arb_en(void __iomem *csr_base_addr, u32 bank)
{
return READ_CSR_RING_SRV_ARB_EN(csr_base_addr, bank);
}
static void write_csr_ring_srv_arb_en(void __iomem *csr_base_addr, u32 bank,
u32 value)
{
WRITE_CSR_RING_SRV_ARB_EN(csr_base_addr, bank, value);
}
static u32 get_int_col_ctl_enable_mask(void)
{
return ADF_RING_CSR_INT_COL_CTL_ENABLE;
}
void adf_gen4_init_hw_csr_ops(struct adf_hw_csr_ops *csr_ops)
{
csr_ops->build_csr_ring_base_addr = build_csr_ring_base_addr;
csr_ops->read_csr_ring_head = read_csr_ring_head;
csr_ops->write_csr_ring_head = write_csr_ring_head;
csr_ops->read_csr_ring_tail = read_csr_ring_tail;
csr_ops->write_csr_ring_tail = write_csr_ring_tail;
csr_ops->read_csr_stat = read_csr_stat;
csr_ops->read_csr_uo_stat = read_csr_uo_stat;
csr_ops->read_csr_e_stat = read_csr_e_stat;
csr_ops->read_csr_ne_stat = read_csr_ne_stat;
csr_ops->read_csr_nf_stat = read_csr_nf_stat;
csr_ops->read_csr_f_stat = read_csr_f_stat;
csr_ops->read_csr_c_stat = read_csr_c_stat;
csr_ops->read_csr_exp_stat = read_csr_exp_stat;
csr_ops->read_csr_exp_int_en = read_csr_exp_int_en;
csr_ops->write_csr_exp_int_en = write_csr_exp_int_en;
csr_ops->read_csr_ring_config = read_csr_ring_config;
csr_ops->write_csr_ring_config = write_csr_ring_config;
csr_ops->read_csr_ring_base = read_csr_ring_base;
csr_ops->write_csr_ring_base = write_csr_ring_base;
csr_ops->read_csr_int_en = read_csr_int_en;
csr_ops->write_csr_int_en = write_csr_int_en;
csr_ops->read_csr_int_flag = read_csr_int_flag;
csr_ops->write_csr_int_flag = write_csr_int_flag;
csr_ops->read_csr_int_srcsel = read_csr_int_srcsel;
csr_ops->write_csr_int_srcsel = write_csr_int_srcsel;
csr_ops->write_csr_int_srcsel_w_val = write_csr_int_srcsel_w_val;
csr_ops->read_csr_int_col_en = read_csr_int_col_en;
csr_ops->write_csr_int_col_en = write_csr_int_col_en;
csr_ops->read_csr_int_col_ctl = read_csr_int_col_ctl;
csr_ops->write_csr_int_col_ctl = write_csr_int_col_ctl;
csr_ops->read_csr_int_flag_and_col = read_csr_int_flag_and_col;
csr_ops->write_csr_int_flag_and_col = write_csr_int_flag_and_col;
csr_ops->read_csr_ring_srv_arb_en = read_csr_ring_srv_arb_en;
csr_ops->write_csr_ring_srv_arb_en = write_csr_ring_srv_arb_en;
csr_ops->get_int_col_ctl_enable_mask = get_int_col_ctl_enable_mask;
}
EXPORT_SYMBOL_GPL(adf_gen4_init_hw_csr_ops);
@@ -0,0 +1,188 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright(c) 2024 Intel Corporation */
#ifndef ADF_GEN4_HW_CSR_DATA_H_
#define ADF_GEN4_HW_CSR_DATA_H_
#include <linux/bitops.h>
#include "adf_accel_devices.h"
#define ADF_BANK_INT_SRC_SEL_MASK 0x44UL
#define ADF_RING_CSR_RING_CONFIG 0x1000
#define ADF_RING_CSR_RING_LBASE 0x1040
#define ADF_RING_CSR_RING_UBASE 0x1080
#define ADF_RING_CSR_RING_HEAD 0x0C0
#define ADF_RING_CSR_RING_TAIL 0x100
#define ADF_RING_CSR_STAT 0x140
#define ADF_RING_CSR_UO_STAT 0x148
#define ADF_RING_CSR_E_STAT 0x14C
#define ADF_RING_CSR_NE_STAT 0x150
#define ADF_RING_CSR_NF_STAT 0x154
#define ADF_RING_CSR_F_STAT 0x158
#define ADF_RING_CSR_C_STAT 0x15C
#define ADF_RING_CSR_INT_FLAG_EN 0x16C
#define ADF_RING_CSR_INT_FLAG 0x170
#define ADF_RING_CSR_INT_SRCSEL 0x174
#define ADF_RING_CSR_INT_COL_EN 0x17C
#define ADF_RING_CSR_INT_COL_CTL 0x180
#define ADF_RING_CSR_INT_FLAG_AND_COL 0x184
#define ADF_RING_CSR_EXP_STAT 0x188
#define ADF_RING_CSR_EXP_INT_EN 0x18C
#define ADF_RING_CSR_INT_COL_CTL_ENABLE 0x80000000
#define ADF_RING_CSR_ADDR_OFFSET 0x100000
#define ADF_RING_BUNDLE_SIZE 0x2000
#define ADF_RING_CSR_RING_SRV_ARB_EN 0x19C
#define BUILD_RING_BASE_ADDR(addr, size) \
((((addr) >> 6) & (GENMASK_ULL(63, 0) << (size))) << 6)
#define READ_CSR_RING_HEAD(csr_base_addr, bank, ring) \
ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + \
ADF_RING_CSR_RING_HEAD + ((ring) << 2))
#define READ_CSR_RING_TAIL(csr_base_addr, bank, ring) \
ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + \
ADF_RING_CSR_RING_TAIL + ((ring) << 2))
#define READ_CSR_STAT(csr_base_addr, bank) \
ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + ADF_RING_CSR_STAT)
#define READ_CSR_UO_STAT(csr_base_addr, bank) \
ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + ADF_RING_CSR_UO_STAT)
#define READ_CSR_E_STAT(csr_base_addr, bank) \
ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + ADF_RING_CSR_E_STAT)
#define READ_CSR_NE_STAT(csr_base_addr, bank) \
ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + ADF_RING_CSR_NE_STAT)
#define READ_CSR_NF_STAT(csr_base_addr, bank) \
ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + ADF_RING_CSR_NF_STAT)
#define READ_CSR_F_STAT(csr_base_addr, bank) \
ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + ADF_RING_CSR_F_STAT)
#define READ_CSR_C_STAT(csr_base_addr, bank) \
ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + ADF_RING_CSR_C_STAT)
#define READ_CSR_EXP_STAT(csr_base_addr, bank) \
ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + ADF_RING_CSR_EXP_STAT)
#define READ_CSR_EXP_INT_EN(csr_base_addr, bank) \
ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + ADF_RING_CSR_EXP_INT_EN)
#define WRITE_CSR_EXP_INT_EN(csr_base_addr, bank, value) \
ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + \
ADF_RING_CSR_EXP_INT_EN, value)
#define READ_CSR_RING_CONFIG(csr_base_addr, bank, ring) \
ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + \
ADF_RING_CSR_RING_CONFIG + ((ring) << 2))
#define WRITE_CSR_RING_CONFIG(csr_base_addr, bank, ring, value) \
ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + \
ADF_RING_CSR_RING_CONFIG + ((ring) << 2), value)
#define WRITE_CSR_RING_BASE(csr_base_addr, bank, ring, value) \
do { \
void __iomem *_csr_base_addr = csr_base_addr; \
u32 _bank = bank; \
u32 _ring = ring; \
dma_addr_t _value = value; \
u32 l_base = 0, u_base = 0; \
l_base = lower_32_bits(_value); \
u_base = upper_32_bits(_value); \
ADF_CSR_WR((_csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (_bank) + \
ADF_RING_CSR_RING_LBASE + ((_ring) << 2), l_base); \
ADF_CSR_WR((_csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (_bank) + \
ADF_RING_CSR_RING_UBASE + ((_ring) << 2), u_base); \
} while (0)
static inline u64 read_base(void __iomem *csr_base_addr, u32 bank, u32 ring)
{
u32 l_base, u_base;
/*
* Use special IO wrapper for ring base as LBASE and UBASE are
* not physically contigious
*/
l_base = ADF_CSR_RD(csr_base_addr, (ADF_RING_BUNDLE_SIZE * bank) +
ADF_RING_CSR_RING_LBASE + (ring << 2));
u_base = ADF_CSR_RD(csr_base_addr, (ADF_RING_BUNDLE_SIZE * bank) +
ADF_RING_CSR_RING_UBASE + (ring << 2));
return (u64)u_base << 32 | (u64)l_base;
}
#define READ_CSR_RING_BASE(csr_base_addr, bank, ring) \
read_base((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, (bank), (ring))
#define WRITE_CSR_RING_HEAD(csr_base_addr, bank, ring, value) \
ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + \
ADF_RING_CSR_RING_HEAD + ((ring) << 2), value)
#define WRITE_CSR_RING_TAIL(csr_base_addr, bank, ring, value) \
ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + \
ADF_RING_CSR_RING_TAIL + ((ring) << 2), value)
#define READ_CSR_INT_EN(csr_base_addr, bank) \
ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + ADF_RING_CSR_INT_FLAG_EN)
#define WRITE_CSR_INT_EN(csr_base_addr, bank, value) \
ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + \
ADF_RING_CSR_INT_FLAG_EN, (value))
#define READ_CSR_INT_FLAG(csr_base_addr, bank) \
ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + ADF_RING_CSR_INT_FLAG)
#define WRITE_CSR_INT_FLAG(csr_base_addr, bank, value) \
ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + \
ADF_RING_CSR_INT_FLAG, (value))
#define READ_CSR_INT_SRCSEL(csr_base_addr, bank) \
ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + ADF_RING_CSR_INT_SRCSEL)
#define WRITE_CSR_INT_SRCSEL(csr_base_addr, bank) \
ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + \
ADF_RING_CSR_INT_SRCSEL, ADF_BANK_INT_SRC_SEL_MASK)
#define WRITE_CSR_INT_SRCSEL_W_VAL(csr_base_addr, bank, value) \
ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + \
ADF_RING_CSR_INT_SRCSEL, (value))
#define READ_CSR_INT_COL_EN(csr_base_addr, bank) \
ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + ADF_RING_CSR_INT_COL_EN)
#define WRITE_CSR_INT_COL_EN(csr_base_addr, bank, value) \
ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + \
ADF_RING_CSR_INT_COL_EN, (value))
#define READ_CSR_INT_COL_CTL(csr_base_addr, bank) \
ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + ADF_RING_CSR_INT_COL_CTL)
#define WRITE_CSR_INT_COL_CTL(csr_base_addr, bank, value) \
ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + \
ADF_RING_CSR_INT_COL_CTL, \
ADF_RING_CSR_INT_COL_CTL_ENABLE | (value))
#define READ_CSR_INT_FLAG_AND_COL(csr_base_addr, bank) \
ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + \
ADF_RING_CSR_INT_FLAG_AND_COL)
#define WRITE_CSR_INT_FLAG_AND_COL(csr_base_addr, bank, value) \
ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + \
ADF_RING_CSR_INT_FLAG_AND_COL, (value))
#define READ_CSR_RING_SRV_ARB_EN(csr_base_addr, bank) \
ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + \
ADF_RING_CSR_RING_SRV_ARB_EN)
#define WRITE_CSR_RING_SRV_ARB_EN(csr_base_addr, bank, value) \
ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \
ADF_RING_BUNDLE_SIZE * (bank) + \
ADF_RING_CSR_RING_SRV_ARB_EN, (value))
void adf_gen4_init_hw_csr_ops(struct adf_hw_csr_ops *csr_ops);
#endif
@@ -0,0 +1,610 @@
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2020 Intel Corporation */
#define pr_fmt(fmt) "QAT: " fmt
#include <linux/bitops.h>
#include <linux/iopoll.h>
#include <asm/div64.h>
#include "adf_accel_devices.h"
#include "adf_cfg_services.h"
#include "adf_common_drv.h"
#include "adf_fw_config.h"
#include "adf_gen4_hw_data.h"
#include "adf_gen4_pm.h"
#include "icp_qat_fw_comp.h"
#include "icp_qat_hw_20_comp.h"
u32 adf_gen4_get_accel_mask(struct adf_hw_device_data *self)
{
return ADF_GEN4_ACCELERATORS_MASK;
}
EXPORT_SYMBOL_GPL(adf_gen4_get_accel_mask);
u32 adf_gen4_get_num_accels(struct adf_hw_device_data *self)
{
return ADF_GEN4_MAX_ACCELERATORS;
}
EXPORT_SYMBOL_GPL(adf_gen4_get_num_accels);
u32 adf_gen4_get_num_aes(struct adf_hw_device_data *self)
{
if (!self || !self->ae_mask)
return 0;
return hweight32(self->ae_mask);
}
EXPORT_SYMBOL_GPL(adf_gen4_get_num_aes);
u32 adf_gen4_get_misc_bar_id(struct adf_hw_device_data *self)
{
return ADF_GEN4_PMISC_BAR;
}
EXPORT_SYMBOL_GPL(adf_gen4_get_misc_bar_id);
u32 adf_gen4_get_etr_bar_id(struct adf_hw_device_data *self)
{
return ADF_GEN4_ETR_BAR;
}
EXPORT_SYMBOL_GPL(adf_gen4_get_etr_bar_id);
u32 adf_gen4_get_sram_bar_id(struct adf_hw_device_data *self)
{
return ADF_GEN4_SRAM_BAR;
}
EXPORT_SYMBOL_GPL(adf_gen4_get_sram_bar_id);
enum dev_sku_info adf_gen4_get_sku(struct adf_hw_device_data *self)
{
return DEV_SKU_1;
}
EXPORT_SYMBOL_GPL(adf_gen4_get_sku);
void adf_gen4_get_arb_info(struct arb_info *arb_info)
{
arb_info->arb_cfg = ADF_GEN4_ARB_CONFIG;
arb_info->arb_offset = ADF_GEN4_ARB_OFFSET;
arb_info->wt2sam_offset = ADF_GEN4_ARB_WRK_2_SER_MAP_OFFSET;
}
EXPORT_SYMBOL_GPL(adf_gen4_get_arb_info);
void adf_gen4_get_admin_info(struct admin_info *admin_csrs_info)
{
admin_csrs_info->mailbox_offset = ADF_GEN4_MAILBOX_BASE_OFFSET;
admin_csrs_info->admin_msg_ur = ADF_GEN4_ADMINMSGUR_OFFSET;
admin_csrs_info->admin_msg_lr = ADF_GEN4_ADMINMSGLR_OFFSET;
}
EXPORT_SYMBOL_GPL(adf_gen4_get_admin_info);
u32 adf_gen4_get_heartbeat_clock(struct adf_hw_device_data *self)
{
/*
* GEN4 uses KPT counter for HB
*/
return ADF_GEN4_KPT_COUNTER_FREQ;
}
EXPORT_SYMBOL_GPL(adf_gen4_get_heartbeat_clock);
void adf_gen4_enable_error_correction(struct adf_accel_dev *accel_dev)
{
struct adf_bar *misc_bar = &GET_BARS(accel_dev)[ADF_GEN4_PMISC_BAR];
void __iomem *csr = misc_bar->virt_addr;
/* Enable all in errsou3 except VFLR notification on host */
ADF_CSR_WR(csr, ADF_GEN4_ERRMSK3, ADF_GEN4_VFLNOTIFY);
}
EXPORT_SYMBOL_GPL(adf_gen4_enable_error_correction);
void adf_gen4_enable_ints(struct adf_accel_dev *accel_dev)
{
void __iomem *addr;
addr = (&GET_BARS(accel_dev)[ADF_GEN4_PMISC_BAR])->virt_addr;
/* Enable bundle interrupts */
ADF_CSR_WR(addr, ADF_GEN4_SMIAPF_RP_X0_MASK_OFFSET, 0);
ADF_CSR_WR(addr, ADF_GEN4_SMIAPF_RP_X1_MASK_OFFSET, 0);
/* Enable misc interrupts */
ADF_CSR_WR(addr, ADF_GEN4_SMIAPF_MASK_OFFSET, 0);
}
EXPORT_SYMBOL_GPL(adf_gen4_enable_ints);
int adf_gen4_init_device(struct adf_accel_dev *accel_dev)
{
void __iomem *addr;
u32 status;
u32 csr;
int ret;
addr = (&GET_BARS(accel_dev)[ADF_GEN4_PMISC_BAR])->virt_addr;
/* Temporarily mask PM interrupt */
csr = ADF_CSR_RD(addr, ADF_GEN4_ERRMSK2);
csr |= ADF_GEN4_PM_SOU;
ADF_CSR_WR(addr, ADF_GEN4_ERRMSK2, csr);
/* Set DRV_ACTIVE bit to power up the device */
ADF_CSR_WR(addr, ADF_GEN4_PM_INTERRUPT, ADF_GEN4_PM_DRV_ACTIVE);
/* Poll status register to make sure the device is powered up */
ret = read_poll_timeout(ADF_CSR_RD, status,
status & ADF_GEN4_PM_INIT_STATE,
ADF_GEN4_PM_POLL_DELAY_US,
ADF_GEN4_PM_POLL_TIMEOUT_US, true, addr,
ADF_GEN4_PM_STATUS);
if (ret)
dev_err(&GET_DEV(accel_dev), "Failed to power up the device\n");
return ret;
}
EXPORT_SYMBOL_GPL(adf_gen4_init_device);
void adf_gen4_set_ssm_wdtimer(struct adf_accel_dev *accel_dev)
{
void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev);
u64 timer_val_pke = ADF_SSM_WDT_PKE_DEFAULT_VALUE;
u64 timer_val = ADF_SSM_WDT_DEFAULT_VALUE;
/* Enable watchdog timer for sym and dc */
ADF_CSR_WR64_LO_HI(pmisc_addr, ADF_SSMWDTL_OFFSET, ADF_SSMWDTH_OFFSET, timer_val);
/* Enable watchdog timer for pke */
ADF_CSR_WR64_LO_HI(pmisc_addr, ADF_SSMWDTPKEL_OFFSET, ADF_SSMWDTPKEH_OFFSET,
timer_val_pke);
}
EXPORT_SYMBOL_GPL(adf_gen4_set_ssm_wdtimer);
/*
* The vector routing table is used to select the MSI-X entry to use for each
* interrupt source.
* The first ADF_GEN4_ETR_MAX_BANKS entries correspond to ring interrupts.
* The final entry corresponds to VF2PF or error interrupts.
* This vector table could be used to configure one MSI-X entry to be shared
* between multiple interrupt sources.
*
* The default routing is set to have a one to one correspondence between the
* interrupt source and the MSI-X entry used.
*/
void adf_gen4_set_msix_default_rttable(struct adf_accel_dev *accel_dev)
{
void __iomem *csr;
int i;
csr = (&GET_BARS(accel_dev)[ADF_GEN4_PMISC_BAR])->virt_addr;
for (i = 0; i <= ADF_GEN4_ETR_MAX_BANKS; i++)
ADF_CSR_WR(csr, ADF_GEN4_MSIX_RTTABLE_OFFSET(i), i);
}
EXPORT_SYMBOL_GPL(adf_gen4_set_msix_default_rttable);
int adf_pfvf_comms_disabled(struct adf_accel_dev *accel_dev)
{
return 0;
}
EXPORT_SYMBOL_GPL(adf_pfvf_comms_disabled);
static int reset_ring_pair(void __iomem *csr, u32 bank_number)
{
u32 status;
int ret;
/* Write rpresetctl register BIT(0) as 1
* Since rpresetctl registers have no RW fields, no need to preserve
* values for other bits. Just write directly.
*/
ADF_CSR_WR(csr, ADF_WQM_CSR_RPRESETCTL(bank_number),
ADF_WQM_CSR_RPRESETCTL_RESET);
/* Read rpresetsts register and wait for rp reset to complete */
ret = read_poll_timeout(ADF_CSR_RD, status,
status & ADF_WQM_CSR_RPRESETSTS_STATUS,
ADF_RPRESET_POLL_DELAY_US,
ADF_RPRESET_POLL_TIMEOUT_US, true,
csr, ADF_WQM_CSR_RPRESETSTS(bank_number));
if (!ret) {
/* When rp reset is done, clear rpresetsts */
ADF_CSR_WR(csr, ADF_WQM_CSR_RPRESETSTS(bank_number),
ADF_WQM_CSR_RPRESETSTS_STATUS);
}
return ret;
}
int adf_gen4_ring_pair_reset(struct adf_accel_dev *accel_dev, u32 bank_number)
{
struct adf_hw_device_data *hw_data = accel_dev->hw_device;
void __iomem *csr = adf_get_etr_base(accel_dev);
int ret;
if (bank_number >= hw_data->num_banks)
return -EINVAL;
dev_dbg(&GET_DEV(accel_dev),
"ring pair reset for bank:%d\n", bank_number);
ret = reset_ring_pair(csr, bank_number);
if (ret)
dev_err(&GET_DEV(accel_dev),
"ring pair reset failed (timeout)\n");
else
dev_dbg(&GET_DEV(accel_dev), "ring pair reset successful\n");
return ret;
}
EXPORT_SYMBOL_GPL(adf_gen4_ring_pair_reset);
static const u32 thrd_to_arb_map_dcc[] = {
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x0000FFFF, 0x0000FFFF, 0x0000FFFF, 0x0000FFFF,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x0
};
static const u16 rp_group_to_arb_mask[] = {
[RP_GROUP_0] = 0x5,
[RP_GROUP_1] = 0xA,
};
static bool is_single_service(int service_id)
{
switch (service_id) {
case SVC_DC:
case SVC_SYM:
case SVC_ASYM:
return true;
default:
return false;
}
}
bool adf_gen4_services_supported(unsigned long mask)
{
unsigned long num_svc = hweight_long(mask);
if (mask >= BIT(SVC_COUNT))
return false;
if (test_bit(SVC_DECOMP, &mask))
return false;
switch (num_svc) {
case ADF_ONE_SERVICE:
return true;
case ADF_TWO_SERVICES:
return !test_bit(SVC_DCC, &mask);
default:
return false;
}
}
EXPORT_SYMBOL_GPL(adf_gen4_services_supported);
int adf_gen4_init_thd2arb_map(struct adf_accel_dev *accel_dev)
{
struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
u32 *thd2arb_map = hw_data->thd_to_arb_map;
unsigned int ae_cnt, worker_obj_cnt, i, j;
unsigned long ae_mask, thds_mask;
int srv_id, rp_group;
u32 thd2arb_map_base;
u16 arb_mask;
if (!hw_data->get_rp_group || !hw_data->get_ena_thd_mask ||
!hw_data->get_num_aes || !hw_data->uof_get_num_objs ||
!hw_data->uof_get_ae_mask)
return -EFAULT;
srv_id = adf_get_service_enabled(accel_dev);
if (srv_id < 0)
return srv_id;
ae_cnt = hw_data->get_num_aes(hw_data);
worker_obj_cnt = hw_data->uof_get_num_objs(accel_dev) -
ADF_GEN4_ADMIN_ACCELENGINES;
if (srv_id == SVC_DCC) {
if (ae_cnt > ICP_QAT_HW_AE_DELIMITER)
return -EINVAL;
memcpy(thd2arb_map, thrd_to_arb_map_dcc,
array_size(sizeof(*thd2arb_map), ae_cnt));
return 0;
}
for (i = 0; i < worker_obj_cnt; i++) {
ae_mask = hw_data->uof_get_ae_mask(accel_dev, i);
rp_group = hw_data->get_rp_group(accel_dev, ae_mask);
thds_mask = hw_data->get_ena_thd_mask(accel_dev, i);
thd2arb_map_base = 0;
if (rp_group >= RP_GROUP_COUNT || rp_group < RP_GROUP_0)
return -EINVAL;
if (thds_mask == ADF_GEN4_ENA_THD_MASK_ERROR)
return -EINVAL;
if (is_single_service(srv_id))
arb_mask = rp_group_to_arb_mask[RP_GROUP_0] |
rp_group_to_arb_mask[RP_GROUP_1];
else
arb_mask = rp_group_to_arb_mask[rp_group];
for_each_set_bit(j, &thds_mask, ADF_NUM_THREADS_PER_AE)
thd2arb_map_base |= arb_mask << (j * 4);
for_each_set_bit(j, &ae_mask, ae_cnt)
thd2arb_map[j] = thd2arb_map_base;
}
return 0;
}
EXPORT_SYMBOL_GPL(adf_gen4_init_thd2arb_map);
u16 adf_gen4_get_ring_to_svc_map(struct adf_accel_dev *accel_dev)
{
struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
enum adf_cfg_service_type rps[RP_GROUP_COUNT] = { };
unsigned int ae_mask, start_id, worker_obj_cnt, i;
u16 ring_to_svc_map;
int rp_group;
if (!hw_data->get_rp_group || !hw_data->uof_get_ae_mask ||
!hw_data->uof_get_obj_type || !hw_data->uof_get_num_objs)
return 0;
/* If dcc, all rings handle compression requests */
if (adf_get_service_enabled(accel_dev) == SVC_DCC) {
for (i = 0; i < RP_GROUP_COUNT; i++)
rps[i] = COMP;
goto set_mask;
}
worker_obj_cnt = hw_data->uof_get_num_objs(accel_dev) -
ADF_GEN4_ADMIN_ACCELENGINES;
start_id = worker_obj_cnt - RP_GROUP_COUNT;
for (i = start_id; i < worker_obj_cnt; i++) {
ae_mask = hw_data->uof_get_ae_mask(accel_dev, i);
rp_group = hw_data->get_rp_group(accel_dev, ae_mask);
if (rp_group >= RP_GROUP_COUNT || rp_group < RP_GROUP_0)
return 0;
switch (hw_data->uof_get_obj_type(accel_dev, i)) {
case ADF_FW_SYM_OBJ:
rps[rp_group] = SYM;
break;
case ADF_FW_ASYM_OBJ:
rps[rp_group] = ASYM;
break;
case ADF_FW_DC_OBJ:
rps[rp_group] = COMP;
break;
default:
rps[rp_group] = 0;
break;
}
}
set_mask:
ring_to_svc_map = rps[RP_GROUP_0] << ADF_CFG_SERV_RING_PAIR_0_SHIFT |
rps[RP_GROUP_1] << ADF_CFG_SERV_RING_PAIR_1_SHIFT |
rps[RP_GROUP_0] << ADF_CFG_SERV_RING_PAIR_2_SHIFT |
rps[RP_GROUP_1] << ADF_CFG_SERV_RING_PAIR_3_SHIFT;
return ring_to_svc_map;
}
EXPORT_SYMBOL_GPL(adf_gen4_get_ring_to_svc_map);
/*
* adf_gen4_bank_quiesce_coal_timer() - quiesce bank coalesced interrupt timer
* @accel_dev: Pointer to the device structure
* @bank_idx: Offset to the bank within this device
* @timeout_ms: Timeout in milliseconds for the operation
*
* This function tries to quiesce the coalesced interrupt timer of a bank if
* it has been enabled and triggered.
*
* Returns 0 on success, error code otherwise
*
*/
int adf_gen4_bank_quiesce_coal_timer(struct adf_accel_dev *accel_dev,
u32 bank_idx, int timeout_ms)
{
struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(accel_dev);
void __iomem *csr_misc = adf_get_pmisc_base(accel_dev);
void __iomem *csr_etr = adf_get_etr_base(accel_dev);
u32 int_col_ctl, int_col_mask, int_col_en;
u32 e_stat, intsrc;
u64 wait_us;
int ret;
if (timeout_ms < 0)
return -EINVAL;
int_col_ctl = csr_ops->read_csr_int_col_ctl(csr_etr, bank_idx);
int_col_mask = csr_ops->get_int_col_ctl_enable_mask();
if (!(int_col_ctl & int_col_mask))
return 0;
int_col_en = csr_ops->read_csr_int_col_en(csr_etr, bank_idx);
int_col_en &= BIT(ADF_WQM_CSR_RP_IDX_RX);
e_stat = csr_ops->read_csr_e_stat(csr_etr, bank_idx);
if (!(~e_stat & int_col_en))
return 0;
wait_us = 2 * ((int_col_ctl & ~int_col_mask) << 8) * USEC_PER_SEC;
do_div(wait_us, hw_data->clock_frequency);
wait_us = min(wait_us, (u64)timeout_ms * USEC_PER_MSEC);
dev_dbg(&GET_DEV(accel_dev),
"wait for bank %d - coalesced timer expires in %llu us (max=%u ms estat=0x%x intcolen=0x%x)\n",
bank_idx, wait_us, timeout_ms, e_stat, int_col_en);
ret = read_poll_timeout(ADF_CSR_RD, intsrc, intsrc,
ADF_COALESCED_POLL_DELAY_US, wait_us, true,
csr_misc, ADF_WQM_CSR_RPINTSOU(bank_idx));
if (ret)
dev_warn(&GET_DEV(accel_dev),
"coalesced timer for bank %d expired (%llu us)\n",
bank_idx, wait_us);
return ret;
}
EXPORT_SYMBOL_GPL(adf_gen4_bank_quiesce_coal_timer);
static int drain_bank(void __iomem *csr, u32 bank_number, int timeout_us)
{
u32 status;
ADF_CSR_WR(csr, ADF_WQM_CSR_RPRESETCTL(bank_number),
ADF_WQM_CSR_RPRESETCTL_DRAIN);
return read_poll_timeout(ADF_CSR_RD, status,
status & ADF_WQM_CSR_RPRESETSTS_STATUS,
ADF_RPRESET_POLL_DELAY_US, timeout_us, true,
csr, ADF_WQM_CSR_RPRESETSTS(bank_number));
}
void adf_gen4_bank_drain_finish(struct adf_accel_dev *accel_dev,
u32 bank_number)
{
void __iomem *csr = adf_get_etr_base(accel_dev);
ADF_CSR_WR(csr, ADF_WQM_CSR_RPRESETSTS(bank_number),
ADF_WQM_CSR_RPRESETSTS_STATUS);
}
int adf_gen4_bank_drain_start(struct adf_accel_dev *accel_dev,
u32 bank_number, int timeout_us)
{
void __iomem *csr = adf_get_etr_base(accel_dev);
int ret;
dev_dbg(&GET_DEV(accel_dev), "Drain bank %d\n", bank_number);
ret = drain_bank(csr, bank_number, timeout_us);
if (ret)
dev_err(&GET_DEV(accel_dev), "Bank drain failed (timeout)\n");
else
dev_dbg(&GET_DEV(accel_dev), "Bank drain successful\n");
return ret;
}
static int adf_gen4_build_comp_block(void *ctx, enum adf_dc_algo algo)
{
struct icp_qat_fw_comp_req *req_tmpl = ctx;
struct icp_qat_fw_comp_req_hdr_cd_pars *cd_pars = &req_tmpl->cd_pars;
struct icp_qat_hw_comp_20_config_csr_upper hw_comp_upper_csr = { };
struct icp_qat_hw_comp_20_config_csr_lower hw_comp_lower_csr = { };
struct icp_qat_fw_comn_req_hdr *header = &req_tmpl->comn_hdr;
u32 upper_val;
u32 lower_val;
switch (algo) {
case QAT_DEFLATE:
header->service_cmd_id = ICP_QAT_FW_COMP_CMD_DYNAMIC;
hw_comp_lower_csr.algo = ICP_QAT_HW_COMP_20_HW_COMP_FORMAT_ILZ77;
hw_comp_lower_csr.lllbd = ICP_QAT_HW_COMP_20_LLLBD_CTRL_LLLBD_ENABLED;
hw_comp_lower_csr.skip_ctrl = ICP_QAT_HW_COMP_20_BYTE_SKIP_3BYTE_LITERAL;
break;
case QAT_LZ4S:
header->service_cmd_id = ICP_QAT_FW_COMP_20_CMD_LZ4S_COMPRESS;
hw_comp_lower_csr.algo = ICP_QAT_HW_COMP_20_HW_COMP_FORMAT_LZ4S;
hw_comp_lower_csr.lllbd = ICP_QAT_HW_COMP_20_LLLBD_CTRL_LLLBD_DISABLED;
hw_comp_lower_csr.abd = ICP_QAT_HW_COMP_20_ABD_ABD_DISABLED;
break;
default:
return -EINVAL;
}
hw_comp_lower_csr.sd = ICP_QAT_HW_COMP_20_SEARCH_DEPTH_LEVEL_1;
hw_comp_lower_csr.hash_update = ICP_QAT_HW_COMP_20_SKIP_HASH_UPDATE_DONT_ALLOW;
hw_comp_lower_csr.edmm = ICP_QAT_HW_COMP_20_EXTENDED_DELAY_MATCH_MODE_EDMM_ENABLED;
hw_comp_upper_csr.nice = ICP_QAT_HW_COMP_20_CONFIG_CSR_NICE_PARAM_DEFAULT_VAL;
hw_comp_upper_csr.lazy = ICP_QAT_HW_COMP_20_CONFIG_CSR_LAZY_PARAM_DEFAULT_VAL;
upper_val = ICP_QAT_FW_COMP_20_BUILD_CONFIG_UPPER(hw_comp_upper_csr);
lower_val = ICP_QAT_FW_COMP_20_BUILD_CONFIG_LOWER(hw_comp_lower_csr);
cd_pars->u.sl.comp_slice_cfg_word[0] = lower_val;
cd_pars->u.sl.comp_slice_cfg_word[1] = upper_val;
return 0;
}
static int adf_gen4_build_decomp_block(void *ctx, enum adf_dc_algo algo)
{
struct icp_qat_fw_comp_req *req_tmpl = ctx;
struct icp_qat_hw_decomp_20_config_csr_lower hw_decomp_lower_csr = { };
struct icp_qat_fw_comp_req_hdr_cd_pars *cd_pars = &req_tmpl->cd_pars;
struct icp_qat_fw_comn_req_hdr *header = &req_tmpl->comn_hdr;
u32 lower_val;
switch (algo) {
case QAT_DEFLATE:
header->service_cmd_id = ICP_QAT_FW_COMP_CMD_DECOMPRESS;
hw_decomp_lower_csr.algo = ICP_QAT_HW_DECOMP_20_HW_DECOMP_FORMAT_DEFLATE;
break;
case QAT_LZ4S:
header->service_cmd_id = ICP_QAT_FW_COMP_20_CMD_LZ4S_DECOMPRESS;
hw_decomp_lower_csr.algo = ICP_QAT_HW_DECOMP_20_HW_DECOMP_FORMAT_LZ4S;
break;
default:
return -EINVAL;
}
lower_val = ICP_QAT_FW_DECOMP_20_BUILD_CONFIG_LOWER(hw_decomp_lower_csr);
cd_pars->u.sl.comp_slice_cfg_word[0] = lower_val;
cd_pars->u.sl.comp_slice_cfg_word[1] = 0;
return 0;
}
void adf_gen4_init_dc_ops(struct adf_dc_ops *dc_ops)
{
dc_ops->build_comp_block = adf_gen4_build_comp_block;
dc_ops->build_decomp_block = adf_gen4_build_decomp_block;
}
EXPORT_SYMBOL_GPL(adf_gen4_init_dc_ops);
void adf_gen4_init_num_svc_aes(struct adf_rl_hw_data *device_data)
{
struct adf_hw_device_data *hw_data;
unsigned int i;
u32 ae_cnt;
hw_data = container_of(device_data, struct adf_hw_device_data, rl_data);
ae_cnt = hweight32(hw_data->get_ae_mask(hw_data));
if (!ae_cnt)
return;
for (i = 0; i < SVC_BASE_COUNT; i++)
device_data->svc_ae_mask[i] = ae_cnt - 1;
/*
* The decompression service is not supported on QAT GEN4 devices.
* Therefore, set svc_ae_mask to 0.
*/
device_data->svc_ae_mask[SVC_DECOMP] = 0;
}
EXPORT_SYMBOL_GPL(adf_gen4_init_num_svc_aes);
u32 adf_gen4_get_svc_slice_cnt(struct adf_accel_dev *accel_dev,
enum adf_base_services svc)
{
struct adf_rl_hw_data *device_data = &accel_dev->hw_device->rl_data;
switch (svc) {
case SVC_SYM:
return device_data->slices.cph_cnt;
case SVC_ASYM:
return device_data->slices.pke_cnt;
case SVC_DC:
return device_data->slices.dcpr_cnt;
default:
return 0;
}
}
EXPORT_SYMBOL_GPL(adf_gen4_get_svc_slice_cnt);
@@ -0,0 +1,182 @@
/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */
/* Copyright(c) 2020 Intel Corporation */
#ifndef ADF_GEN4_HW_DATA_H_
#define ADF_GEN4_HW_DATA_H_
#include <linux/units.h>
#include "adf_accel_devices.h"
#include "adf_cfg_common.h"
#include "adf_dc.h"
/* PCIe configuration space */
#define ADF_GEN4_BAR_MASK (BIT(0) | BIT(2) | BIT(4))
#define ADF_GEN4_SRAM_BAR 0
#define ADF_GEN4_PMISC_BAR 1
#define ADF_GEN4_ETR_BAR 2
/* Clocks frequency */
#define ADF_GEN4_KPT_COUNTER_FREQ (100 * HZ_PER_MHZ)
/* Physical function fuses */
#define ADF_GEN4_FUSECTL0_OFFSET 0x2C8
#define ADF_GEN4_FUSECTL1_OFFSET 0x2CC
#define ADF_GEN4_FUSECTL2_OFFSET 0x2D0
#define ADF_GEN4_FUSECTL3_OFFSET 0x2D4
#define ADF_GEN4_FUSECTL4_OFFSET 0x2D8
#define ADF_GEN4_FUSECTL5_OFFSET 0x2DC
/* Accelerators */
#define ADF_GEN4_ACCELERATORS_MASK 0x1
#define ADF_GEN4_MAX_ACCELERATORS 1
#define ADF_GEN4_ADMIN_ACCELENGINES 1
/* MSIX interrupt */
#define ADF_GEN4_SMIAPF_RP_X0_MASK_OFFSET 0x41A040
#define ADF_GEN4_SMIAPF_RP_X1_MASK_OFFSET 0x41A044
#define ADF_GEN4_SMIAPF_MASK_OFFSET 0x41A084
#define ADF_GEN4_MSIX_RTTABLE_OFFSET(i) (0x409000 + ((i) * 0x04))
/* Bank and ring configuration */
#define ADF_GEN4_MAX_RPS 64
#define ADF_GEN4_NUM_RINGS_PER_BANK 2
#define ADF_GEN4_NUM_BANKS_PER_VF 4
#define ADF_GEN4_ETR_MAX_BANKS 64
#define ADF_GEN4_RX_RINGS_OFFSET 1
#define ADF_GEN4_TX_RINGS_MASK 0x1
/* Arbiter configuration */
#define ADF_GEN4_ARB_CONFIG (BIT(31) | BIT(6) | BIT(0))
#define ADF_GEN4_ARB_OFFSET 0x0
#define ADF_GEN4_ARB_WRK_2_SER_MAP_OFFSET 0x400
/* Admin Interface Reg Offset */
#define ADF_GEN4_ADMINMSGUR_OFFSET 0x500574
#define ADF_GEN4_ADMINMSGLR_OFFSET 0x500578
#define ADF_GEN4_MAILBOX_BASE_OFFSET 0x600970
/* Default ring mapping */
#define ADF_GEN4_DEFAULT_RING_TO_SRV_MAP \
(ASYM << ADF_CFG_SERV_RING_PAIR_0_SHIFT | \
SYM << ADF_CFG_SERV_RING_PAIR_1_SHIFT | \
ASYM << ADF_CFG_SERV_RING_PAIR_2_SHIFT | \
SYM << ADF_CFG_SERV_RING_PAIR_3_SHIFT)
/* WDT timers
*
* Timeout is in cycles. Clock speed may vary across products but this
* value should be a few milli-seconds.
*/
#define ADF_SSM_WDT_DEFAULT_VALUE 0x7000000ULL
#define ADF_SSM_WDT_PKE_DEFAULT_VALUE 0x8000000
#define ADF_SSMWDTL_OFFSET 0x54
#define ADF_SSMWDTH_OFFSET 0x5C
#define ADF_SSMWDTPKEL_OFFSET 0x58
#define ADF_SSMWDTPKEH_OFFSET 0x60
/* Ring reset */
#define ADF_RPRESET_POLL_TIMEOUT_US (5 * USEC_PER_SEC)
#define ADF_RPRESET_POLL_DELAY_US 20
#define ADF_WQM_CSR_RPRESETCTL_RESET BIT(0)
#define ADF_WQM_CSR_RPRESETCTL_DRAIN BIT(2)
#define ADF_WQM_CSR_RPRESETCTL(bank) (0x6000 + ((bank) << 3))
#define ADF_WQM_CSR_RPRESETSTS_STATUS BIT(0)
#define ADF_WQM_CSR_RPRESETSTS(bank) (ADF_WQM_CSR_RPRESETCTL(bank) + 4)
/* Ring interrupt */
#define ADF_COALESCED_POLL_TIMEOUT_US (1 * USEC_PER_SEC)
#define ADF_COALESCED_POLL_DELAY_US 1000
#define ADF_WQM_CSR_RPINTSOU(bank) (0x200000 + ((bank) << 12))
#define ADF_WQM_CSR_RP_IDX_RX 1
/* Error source registers */
#define ADF_GEN4_ERRSOU0 (0x41A200)
#define ADF_GEN4_ERRSOU1 (0x41A204)
#define ADF_GEN4_ERRSOU2 (0x41A208)
#define ADF_GEN4_ERRSOU3 (0x41A20C)
/* Error source mask registers */
#define ADF_GEN4_ERRMSK0 (0x41A210)
#define ADF_GEN4_ERRMSK1 (0x41A214)
#define ADF_GEN4_ERRMSK2 (0x41A218)
#define ADF_GEN4_ERRMSK3 (0x41A21C)
#define ADF_GEN4_VFLNOTIFY BIT(7)
/* Number of heartbeat counter pairs */
#define ADF_NUM_HB_CNT_PER_AE ADF_NUM_THREADS_PER_AE
/* Rate Limiting */
#define ADF_GEN4_RL_R2L_OFFSET 0x508000
#define ADF_GEN4_RL_L2C_OFFSET 0x509000
#define ADF_GEN4_RL_C2S_OFFSET 0x508818
#define ADF_GEN4_RL_TOKEN_PCIEIN_BUCKET_OFFSET 0x508800
#define ADF_GEN4_RL_TOKEN_PCIEOUT_BUCKET_OFFSET 0x508804
/* Arbiter threads mask with error value */
#define ADF_GEN4_ENA_THD_MASK_ERROR GENMASK(ADF_NUM_THREADS_PER_AE, 0)
/* PF2VM communication channel */
#define ADF_GEN4_PF2VM_OFFSET(i) (0x40B010 + (i) * 0x20)
#define ADF_GEN4_VM2PF_OFFSET(i) (0x40B014 + (i) * 0x20)
#define ADF_GEN4_VINTMSKPF2VM_OFFSET(i) (0x40B00C + (i) * 0x20)
#define ADF_GEN4_VINTSOUPF2VM_OFFSET(i) (0x40B008 + (i) * 0x20)
#define ADF_GEN4_VINTMSK_OFFSET(i) (0x40B004 + (i) * 0x20)
#define ADF_GEN4_VINTSOU_OFFSET(i) (0x40B000 + (i) * 0x20)
struct adf_gen4_vfmig {
struct adf_mstate_mgr *mstate_mgr;
bool bank_stopped[ADF_GEN4_NUM_BANKS_PER_VF];
};
void adf_gen4_set_ssm_wdtimer(struct adf_accel_dev *accel_dev);
enum icp_qat_gen4_slice_mask {
ICP_ACCEL_GEN4_MASK_CIPHER_SLICE = BIT(0),
ICP_ACCEL_GEN4_MASK_AUTH_SLICE = BIT(1),
ICP_ACCEL_GEN4_MASK_PKE_SLICE = BIT(2),
ICP_ACCEL_GEN4_MASK_COMPRESS_SLICE = BIT(3),
ICP_ACCEL_GEN4_MASK_UCS_SLICE = BIT(4),
ICP_ACCEL_GEN4_MASK_EIA3_SLICE = BIT(5),
ICP_ACCEL_GEN4_MASK_SMX_SLICE = BIT(7),
ICP_ACCEL_GEN4_MASK_WCP_WAT_SLICE = BIT(8),
ICP_ACCEL_GEN4_MASK_ZUC_256_SLICE = BIT(9),
};
enum adf_gen4_rp_groups {
RP_GROUP_0,
RP_GROUP_1,
RP_GROUP_COUNT
};
void adf_gen4_enable_error_correction(struct adf_accel_dev *accel_dev);
void adf_gen4_enable_ints(struct adf_accel_dev *accel_dev);
u32 adf_gen4_get_accel_mask(struct adf_hw_device_data *self);
void adf_gen4_get_admin_info(struct admin_info *admin_csrs_info);
void adf_gen4_get_arb_info(struct arb_info *arb_info);
u32 adf_gen4_get_etr_bar_id(struct adf_hw_device_data *self);
u32 adf_gen4_get_heartbeat_clock(struct adf_hw_device_data *self);
u32 adf_gen4_get_misc_bar_id(struct adf_hw_device_data *self);
u32 adf_gen4_get_num_accels(struct adf_hw_device_data *self);
u32 adf_gen4_get_num_aes(struct adf_hw_device_data *self);
enum dev_sku_info adf_gen4_get_sku(struct adf_hw_device_data *self);
u32 adf_gen4_get_sram_bar_id(struct adf_hw_device_data *self);
int adf_gen4_init_device(struct adf_accel_dev *accel_dev);
int adf_gen4_ring_pair_reset(struct adf_accel_dev *accel_dev, u32 bank_number);
void adf_gen4_set_msix_default_rttable(struct adf_accel_dev *accel_dev);
void adf_gen4_set_ssm_wdtimer(struct adf_accel_dev *accel_dev);
int adf_gen4_init_thd2arb_map(struct adf_accel_dev *accel_dev);
u16 adf_gen4_get_ring_to_svc_map(struct adf_accel_dev *accel_dev);
int adf_gen4_bank_quiesce_coal_timer(struct adf_accel_dev *accel_dev,
u32 bank_idx, int timeout_ms);
int adf_gen4_bank_drain_start(struct adf_accel_dev *accel_dev,
u32 bank_number, int timeout_us);
void adf_gen4_bank_drain_finish(struct adf_accel_dev *accel_dev,
u32 bank_number);
bool adf_gen4_services_supported(unsigned long service_mask);
void adf_gen4_init_dc_ops(struct adf_dc_ops *dc_ops);
void adf_gen4_init_num_svc_aes(struct adf_rl_hw_data *device_data);
u32 adf_gen4_get_svc_slice_cnt(struct adf_accel_dev *accel_dev,
enum adf_base_services svc);
#endif
@@ -0,0 +1,145 @@
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2021 Intel Corporation */
#include <linux/iopoll.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include "adf_accel_devices.h"
#include "adf_common_drv.h"
#include "adf_gen4_pfvf.h"
#include "adf_gen4_hw_data.h"
#include "adf_pfvf_pf_proto.h"
#include "adf_pfvf_utils.h"
/* VF2PF interrupt source registers */
#define ADF_4XXX_VM2PF_SOU 0x41A180
#define ADF_4XXX_VM2PF_MSK 0x41A1C0
#define ADF_GEN4_VF_MSK 0xFFFF
#define ADF_PFVF_GEN4_MSGTYPE_SHIFT 2
#define ADF_PFVF_GEN4_MSGTYPE_MASK 0x3F
#define ADF_PFVF_GEN4_MSGDATA_SHIFT 8
#define ADF_PFVF_GEN4_MSGDATA_MASK 0xFFFFFF
static const struct pfvf_csr_format csr_gen4_fmt = {
{ ADF_PFVF_GEN4_MSGTYPE_SHIFT, ADF_PFVF_GEN4_MSGTYPE_MASK },
{ ADF_PFVF_GEN4_MSGDATA_SHIFT, ADF_PFVF_GEN4_MSGDATA_MASK },
};
static u32 adf_gen4_pf_get_pf2vf_offset(u32 i)
{
return ADF_GEN4_PF2VM_OFFSET(i);
}
static u32 adf_gen4_pf_get_vf2pf_offset(u32 i)
{
return ADF_GEN4_VM2PF_OFFSET(i);
}
static void adf_gen4_enable_vf2pf_interrupts(void __iomem *pmisc_addr, u32 vf_mask)
{
u32 val;
val = ADF_CSR_RD(pmisc_addr, ADF_4XXX_VM2PF_MSK) & ~vf_mask;
ADF_CSR_WR(pmisc_addr, ADF_4XXX_VM2PF_MSK, val);
}
static void adf_gen4_disable_all_vf2pf_interrupts(void __iomem *pmisc_addr)
{
ADF_CSR_WR(pmisc_addr, ADF_4XXX_VM2PF_MSK, ADF_GEN4_VF_MSK);
}
static u32 adf_gen4_disable_pending_vf2pf_interrupts(void __iomem *pmisc_addr)
{
u32 sources, disabled, pending;
/* Get the interrupt sources triggered by VFs */
sources = ADF_CSR_RD(pmisc_addr, ADF_4XXX_VM2PF_SOU);
if (!sources)
return 0;
/* Get the already disabled interrupts */
disabled = ADF_CSR_RD(pmisc_addr, ADF_4XXX_VM2PF_MSK);
pending = sources & ~disabled;
if (!pending)
return 0;
/* Due to HW limitations, when disabling the interrupts, we can't
* just disable the requested sources, as this would lead to missed
* interrupts if VM2PF_SOU changes just before writing to VM2PF_MSK.
* To work around it, disable all and re-enable only the sources that
* are not in vf_mask and were not already disabled. Re-enabling will
* trigger a new interrupt for the sources that have changed in the
* meantime, if any.
*/
ADF_CSR_WR(pmisc_addr, ADF_4XXX_VM2PF_MSK, ADF_GEN4_VF_MSK);
ADF_CSR_WR(pmisc_addr, ADF_4XXX_VM2PF_MSK, disabled | sources);
/* Return the sources of the (new) interrupt(s) */
return pending;
}
static int adf_gen4_pfvf_send(struct adf_accel_dev *accel_dev,
struct pfvf_message msg, u32 pfvf_offset,
struct mutex *csr_lock)
{
void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev);
u32 csr_val;
int ret;
csr_val = adf_pfvf_csr_msg_of(accel_dev, msg, &csr_gen4_fmt);
if (unlikely(!csr_val))
return -EINVAL;
mutex_lock(csr_lock);
ADF_CSR_WR(pmisc_addr, pfvf_offset, csr_val | ADF_PFVF_INT);
/* Wait for confirmation from remote that it received the message */
ret = read_poll_timeout(ADF_CSR_RD, csr_val, !(csr_val & ADF_PFVF_INT),
ADF_PFVF_MSG_ACK_DELAY_US,
ADF_PFVF_MSG_ACK_MAX_DELAY_US,
true, pmisc_addr, pfvf_offset);
if (ret < 0)
dev_dbg(&GET_DEV(accel_dev), "ACK not received from remote\n");
mutex_unlock(csr_lock);
return ret;
}
static struct pfvf_message adf_gen4_pfvf_recv(struct adf_accel_dev *accel_dev,
u32 pfvf_offset, u8 compat_ver)
{
void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev);
struct pfvf_message msg = { 0 };
u32 csr_val;
/* Read message from the CSR */
csr_val = ADF_CSR_RD(pmisc_addr, pfvf_offset);
if (!(csr_val & ADF_PFVF_INT)) {
dev_info(&GET_DEV(accel_dev),
"Spurious PFVF interrupt, msg 0x%.8x. Ignored\n", csr_val);
return msg;
}
/* We can now acknowledge the message reception by clearing the
* interrupt bit
*/
ADF_CSR_WR(pmisc_addr, pfvf_offset, csr_val & ~ADF_PFVF_INT);
/* Return the pfvf_message format */
return adf_pfvf_message_of(accel_dev, csr_val, &csr_gen4_fmt);
}
void adf_gen4_init_pf_pfvf_ops(struct adf_pfvf_ops *pfvf_ops)
{
pfvf_ops->enable_comms = adf_enable_pf2vf_comms;
pfvf_ops->get_pf2vf_offset = adf_gen4_pf_get_pf2vf_offset;
pfvf_ops->get_vf2pf_offset = adf_gen4_pf_get_vf2pf_offset;
pfvf_ops->enable_vf2pf_interrupts = adf_gen4_enable_vf2pf_interrupts;
pfvf_ops->disable_all_vf2pf_interrupts = adf_gen4_disable_all_vf2pf_interrupts;
pfvf_ops->disable_pending_vf2pf_interrupts = adf_gen4_disable_pending_vf2pf_interrupts;
pfvf_ops->send_msg = adf_gen4_pfvf_send;
pfvf_ops->recv_msg = adf_gen4_pfvf_recv;
}
EXPORT_SYMBOL_GPL(adf_gen4_init_pf_pfvf_ops);
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */
/* Copyright(c) 2021 Intel Corporation */
#ifndef ADF_GEN4_PFVF_H
#define ADF_GEN4_PFVF_H
#include "adf_accel_devices.h"
#include "adf_common_drv.h"
#ifdef CONFIG_PCI_IOV
void adf_gen4_init_pf_pfvf_ops(struct adf_pfvf_ops *pfvf_ops);
#else
static inline void adf_gen4_init_pf_pfvf_ops(struct adf_pfvf_ops *pfvf_ops)
{
pfvf_ops->enable_comms = adf_pfvf_comms_disabled;
}
#endif
#endif /* ADF_GEN4_PFVF_H */
@@ -0,0 +1,164 @@
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2022 Intel Corporation */
#include <linux/bitfield.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include "adf_accel_devices.h"
#include "adf_admin.h"
#include "adf_common_drv.h"
#include "adf_gen4_pm.h"
#include "adf_cfg_strings.h"
#include "icp_qat_fw_init_admin.h"
#include "adf_gen4_hw_data.h"
#include "adf_cfg.h"
struct adf_gen4_pm_data {
struct work_struct pm_irq_work;
struct adf_accel_dev *accel_dev;
u32 pm_int_sts;
};
static int send_host_msg(struct adf_accel_dev *accel_dev)
{
char pm_idle_support_cfg[ADF_CFG_MAX_VAL_LEN_IN_BYTES] = {};
void __iomem *pmisc = adf_get_pmisc_base(accel_dev);
struct adf_pm *pm = &accel_dev->power_management;
bool pm_idle_support;
u32 msg;
int ret;
msg = ADF_CSR_RD(pmisc, ADF_GEN4_PM_HOST_MSG);
if (msg & ADF_GEN4_PM_MSG_PENDING)
return -EBUSY;
adf_cfg_get_param_value(accel_dev, ADF_GENERAL_SEC,
ADF_PM_IDLE_SUPPORT, pm_idle_support_cfg);
ret = kstrtobool(pm_idle_support_cfg, &pm_idle_support);
if (ret)
pm_idle_support = true;
if (pm_idle_support)
pm->host_ack_counter++;
else
pm->host_nack_counter++;
/* Send HOST_MSG */
msg = FIELD_PREP(ADF_GEN4_PM_MSG_PAYLOAD_BIT_MASK,
pm_idle_support ? PM_SET_MIN : PM_NO_CHANGE);
msg |= ADF_GEN4_PM_MSG_PENDING;
ADF_CSR_WR(pmisc, ADF_GEN4_PM_HOST_MSG, msg);
/* Poll status register to make sure the HOST_MSG has been processed */
return read_poll_timeout(ADF_CSR_RD, msg,
!(msg & ADF_GEN4_PM_MSG_PENDING),
ADF_GEN4_PM_MSG_POLL_DELAY_US,
ADF_GEN4_PM_POLL_TIMEOUT_US, true, pmisc,
ADF_GEN4_PM_HOST_MSG);
}
static void pm_bh_handler(struct work_struct *work)
{
struct adf_gen4_pm_data *pm_data =
container_of(work, struct adf_gen4_pm_data, pm_irq_work);
struct adf_accel_dev *accel_dev = pm_data->accel_dev;
void __iomem *pmisc = adf_get_pmisc_base(accel_dev);
struct adf_pm *pm = &accel_dev->power_management;
u32 pm_int_sts = pm_data->pm_int_sts;
u32 val;
/* PM Idle interrupt */
if (pm_int_sts & ADF_GEN4_PM_IDLE_STS) {
pm->idle_irq_counters++;
/* Issue host message to FW */
if (send_host_msg(accel_dev))
dev_warn_ratelimited(&GET_DEV(accel_dev),
"Failed to send host msg to FW\n");
}
/* PM throttle interrupt */
if (pm_int_sts & ADF_GEN4_PM_THR_STS)
pm->throttle_irq_counters++;
/* PM fw interrupt */
if (pm_int_sts & ADF_GEN4_PM_FW_INT_STS)
pm->fw_irq_counters++;
/* Clear interrupt status */
ADF_CSR_WR(pmisc, ADF_GEN4_PM_INTERRUPT, pm_int_sts);
/* Reenable PM interrupt */
val = ADF_CSR_RD(pmisc, ADF_GEN4_ERRMSK2);
val &= ~ADF_GEN4_PM_SOU;
ADF_CSR_WR(pmisc, ADF_GEN4_ERRMSK2, val);
kfree(pm_data);
}
bool adf_gen4_handle_pm_interrupt(struct adf_accel_dev *accel_dev)
{
void __iomem *pmisc = adf_get_pmisc_base(accel_dev);
struct adf_gen4_pm_data *pm_data = NULL;
u32 errsou2;
u32 errmsk2;
u32 val;
/* Only handle the interrupt triggered by PM */
errmsk2 = ADF_CSR_RD(pmisc, ADF_GEN4_ERRMSK2);
if (errmsk2 & ADF_GEN4_PM_SOU)
return false;
errsou2 = ADF_CSR_RD(pmisc, ADF_GEN4_ERRSOU2);
if (!(errsou2 & ADF_GEN4_PM_SOU))
return false;
/* Disable interrupt */
val = ADF_CSR_RD(pmisc, ADF_GEN4_ERRMSK2);
val |= ADF_GEN4_PM_SOU;
ADF_CSR_WR(pmisc, ADF_GEN4_ERRMSK2, val);
val = ADF_CSR_RD(pmisc, ADF_GEN4_PM_INTERRUPT);
pm_data = kzalloc_obj(*pm_data, GFP_ATOMIC);
if (!pm_data)
return false;
pm_data->pm_int_sts = val;
pm_data->accel_dev = accel_dev;
INIT_WORK(&pm_data->pm_irq_work, pm_bh_handler);
adf_misc_wq_queue_work(&pm_data->pm_irq_work);
return true;
}
EXPORT_SYMBOL_GPL(adf_gen4_handle_pm_interrupt);
int adf_gen4_enable_pm(struct adf_accel_dev *accel_dev)
{
void __iomem *pmisc = adf_get_pmisc_base(accel_dev);
int ret;
u32 val;
ret = adf_init_admin_pm(accel_dev, ADF_GEN4_PM_DEFAULT_IDLE_FILTER);
if (ret)
return ret;
/* Initialize PM internal data */
adf_gen4_init_dev_pm_data(accel_dev);
/* Enable default PM interrupts: IDLE, THROTTLE */
val = ADF_CSR_RD(pmisc, ADF_GEN4_PM_INTERRUPT);
val |= ADF_GEN4_PM_INT_EN_DEFAULT;
/* Clear interrupt status */
val |= ADF_GEN4_PM_INT_STS_MASK;
ADF_CSR_WR(pmisc, ADF_GEN4_PM_INTERRUPT, val);
/* Unmask PM Interrupt */
val = ADF_CSR_RD(pmisc, ADF_GEN4_ERRMSK2);
val &= ~ADF_GEN4_PM_SOU;
ADF_CSR_WR(pmisc, ADF_GEN4_ERRMSK2, val);
return 0;
}
EXPORT_SYMBOL_GPL(adf_gen4_enable_pm);
@@ -0,0 +1,93 @@
/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */
/* Copyright(c) 2022 Intel Corporation */
#ifndef ADF_GEN4_PM_H
#define ADF_GEN4_PM_H
#include <linux/bits.h>
struct adf_accel_dev;
enum qat_pm_host_msg {
PM_NO_CHANGE = 0,
PM_SET_MIN,
};
/* Power management registers */
#define ADF_GEN4_PM_HOST_MSG (0x50A01C)
/* Power management */
#define ADF_GEN4_PM_POLL_DELAY_US 20
#define ADF_GEN4_PM_POLL_TIMEOUT_US USEC_PER_SEC
#define ADF_GEN4_PM_MSG_POLL_DELAY_US (10 * USEC_PER_MSEC)
#define ADF_GEN4_PM_STATUS (0x50A00C)
#define ADF_GEN4_PM_INTERRUPT (0x50A028)
/* Power management source in ERRSOU2 and ERRMSK2 */
#define ADF_GEN4_PM_SOU BIT(18)
#define ADF_GEN4_PM_IDLE_INT_EN BIT(18)
#define ADF_GEN4_PM_THROTTLE_INT_EN BIT(19)
#define ADF_GEN4_PM_DRV_ACTIVE BIT(20)
#define ADF_GEN4_PM_INIT_STATE BIT(21)
#define ADF_GEN4_PM_INT_EN_DEFAULT (ADF_GEN4_PM_IDLE_INT_EN | \
ADF_GEN4_PM_THROTTLE_INT_EN)
#define ADF_GEN4_PM_THR_STS BIT(0)
#define ADF_GEN4_PM_IDLE_STS BIT(1)
#define ADF_GEN4_PM_FW_INT_STS BIT(2)
#define ADF_GEN4_PM_INT_STS_MASK (ADF_GEN4_PM_THR_STS | \
ADF_GEN4_PM_IDLE_STS | \
ADF_GEN4_PM_FW_INT_STS)
#define ADF_GEN4_PM_MSG_PENDING BIT(0)
#define ADF_GEN4_PM_MSG_PAYLOAD_BIT_MASK GENMASK(28, 1)
#define ADF_GEN4_PM_DEFAULT_IDLE_FILTER (0x6)
#define ADF_GEN4_PM_MAX_IDLE_FILTER (0x7)
#define ADF_GEN4_PM_DEFAULT_IDLE_SUPPORT (0x1)
/* PM CSRs fields masks */
#define ADF_GEN4_PM_DOMAIN_POWER_GATED_MASK GENMASK(15, 0)
#define ADF_GEN4_PM_SSM_PM_ENABLE_MASK GENMASK(15, 0)
#define ADF_GEN4_PM_IDLE_FILTER_MASK GENMASK(5, 3)
#define ADF_GEN4_PM_IDLE_ENABLE_MASK BIT(2)
#define ADF_GEN4_PM_ENABLE_PM_MASK BIT(21)
#define ADF_GEN4_PM_ENABLE_PM_IDLE_MASK BIT(22)
#define ADF_GEN4_PM_ENABLE_DEEP_PM_IDLE_MASK BIT(23)
#define ADF_GEN4_PM_CURRENT_WP_MASK GENMASK(19, 11)
#define ADF_GEN4_PM_CPM_PM_STATE_MASK GENMASK(22, 20)
#define ADF_GEN4_PM_PENDING_WP_MASK GENMASK(31, 23)
#define ADF_GEN4_PM_THR_VALUE_MASK GENMASK(6, 4)
#define ADF_GEN4_PM_MIN_PWR_ACK_MASK BIT(7)
#define ADF_GEN4_PM_MIN_PWR_ACK_PENDING_MASK BIT(17)
#define ADF_GEN4_PM_CPR_ACTIVE_COUNT_MASK BIT(0)
#define ADF_GEN4_PM_CPR_MANAGED_COUNT_MASK BIT(0)
#define ADF_GEN4_PM_XLT_ACTIVE_COUNT_MASK BIT(1)
#define ADF_GEN4_PM_XLT_MANAGED_COUNT_MASK BIT(1)
#define ADF_GEN4_PM_DCPR_ACTIVE_COUNT_MASK GENMASK(3, 2)
#define ADF_GEN4_PM_DCPR_MANAGED_COUNT_MASK GENMASK(3, 2)
#define ADF_GEN4_PM_PKE_ACTIVE_COUNT_MASK GENMASK(8, 4)
#define ADF_GEN4_PM_PKE_MANAGED_COUNT_MASK GENMASK(8, 4)
#define ADF_GEN4_PM_WAT_ACTIVE_COUNT_MASK GENMASK(13, 9)
#define ADF_GEN4_PM_WAT_MANAGED_COUNT_MASK GENMASK(13, 9)
#define ADF_GEN4_PM_WCP_ACTIVE_COUNT_MASK GENMASK(18, 14)
#define ADF_GEN4_PM_WCP_MANAGED_COUNT_MASK GENMASK(18, 14)
#define ADF_GEN4_PM_UCS_ACTIVE_COUNT_MASK GENMASK(20, 19)
#define ADF_GEN4_PM_UCS_MANAGED_COUNT_MASK GENMASK(20, 19)
#define ADF_GEN4_PM_CPH_ACTIVE_COUNT_MASK GENMASK(24, 21)
#define ADF_GEN4_PM_CPH_MANAGED_COUNT_MASK GENMASK(24, 21)
#define ADF_GEN4_PM_ATH_ACTIVE_COUNT_MASK GENMASK(28, 25)
#define ADF_GEN4_PM_ATH_MANAGED_COUNT_MASK GENMASK(28, 25)
int adf_gen4_enable_pm(struct adf_accel_dev *accel_dev);
bool adf_gen4_handle_pm_interrupt(struct adf_accel_dev *accel_dev);
#ifdef CONFIG_DEBUG_FS
void adf_gen4_init_dev_pm_data(struct adf_accel_dev *accel_dev);
#else
static inline void adf_gen4_init_dev_pm_data(struct adf_accel_dev *accel_dev)
{
}
#endif /* CONFIG_DEBUG_FS */
#endif
@@ -0,0 +1,199 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2023 Intel Corporation */
#include <linux/dma-mapping.h>
#include <linux/string_helpers.h>
#include "adf_accel_devices.h"
#include "adf_admin.h"
#include "adf_common_drv.h"
#include "adf_gen4_pm.h"
#include "adf_pm_dbgfs_utils.h"
#include "icp_qat_fw_init_admin.h"
#define PM_INFO_REGSET_ENTRY(_reg_, _field_) \
PM_INFO_REGSET_ENTRY_MASK(_reg_, _field_, ADF_GEN4_PM_##_field_##_MASK)
static const struct pm_status_row pm_fuse_rows[] = {
PM_INFO_REGSET_ENTRY(fusectl0, ENABLE_PM),
PM_INFO_REGSET_ENTRY(fusectl0, ENABLE_PM_IDLE),
PM_INFO_REGSET_ENTRY(fusectl0, ENABLE_DEEP_PM_IDLE),
};
static const struct pm_status_row pm_info_rows[] = {
PM_INFO_REGSET_ENTRY(pm.status, CPM_PM_STATE),
PM_INFO_REGSET_ENTRY(pm.status, PENDING_WP),
PM_INFO_REGSET_ENTRY(pm.status, CURRENT_WP),
PM_INFO_REGSET_ENTRY(pm.fw_init, IDLE_ENABLE),
PM_INFO_REGSET_ENTRY(pm.fw_init, IDLE_FILTER),
PM_INFO_REGSET_ENTRY(pm.main, MIN_PWR_ACK),
PM_INFO_REGSET_ENTRY(pm.thread, MIN_PWR_ACK_PENDING),
PM_INFO_REGSET_ENTRY(pm.main, THR_VALUE),
};
static const struct pm_status_row pm_ssm_rows[] = {
PM_INFO_REGSET_ENTRY(ssm.pm_enable, SSM_PM_ENABLE),
PM_INFO_REGSET_ENTRY32(ssm.active_constraint, ACTIVE_CONSTRAINT),
PM_INFO_REGSET_ENTRY(ssm.pm_domain_status, DOMAIN_POWER_GATED),
PM_INFO_REGSET_ENTRY(ssm.pm_active_status, ATH_ACTIVE_COUNT),
PM_INFO_REGSET_ENTRY(ssm.pm_active_status, CPH_ACTIVE_COUNT),
PM_INFO_REGSET_ENTRY(ssm.pm_active_status, PKE_ACTIVE_COUNT),
PM_INFO_REGSET_ENTRY(ssm.pm_active_status, CPR_ACTIVE_COUNT),
PM_INFO_REGSET_ENTRY(ssm.pm_active_status, DCPR_ACTIVE_COUNT),
PM_INFO_REGSET_ENTRY(ssm.pm_active_status, UCS_ACTIVE_COUNT),
PM_INFO_REGSET_ENTRY(ssm.pm_active_status, XLT_ACTIVE_COUNT),
PM_INFO_REGSET_ENTRY(ssm.pm_active_status, WAT_ACTIVE_COUNT),
PM_INFO_REGSET_ENTRY(ssm.pm_active_status, WCP_ACTIVE_COUNT),
PM_INFO_REGSET_ENTRY(ssm.pm_managed_status, ATH_MANAGED_COUNT),
PM_INFO_REGSET_ENTRY(ssm.pm_managed_status, CPH_MANAGED_COUNT),
PM_INFO_REGSET_ENTRY(ssm.pm_managed_status, PKE_MANAGED_COUNT),
PM_INFO_REGSET_ENTRY(ssm.pm_managed_status, CPR_MANAGED_COUNT),
PM_INFO_REGSET_ENTRY(ssm.pm_managed_status, DCPR_MANAGED_COUNT),
PM_INFO_REGSET_ENTRY(ssm.pm_managed_status, UCS_MANAGED_COUNT),
PM_INFO_REGSET_ENTRY(ssm.pm_managed_status, XLT_MANAGED_COUNT),
PM_INFO_REGSET_ENTRY(ssm.pm_managed_status, WAT_MANAGED_COUNT),
PM_INFO_REGSET_ENTRY(ssm.pm_managed_status, WCP_MANAGED_COUNT),
};
static const struct pm_status_row pm_log_rows[] = {
PM_INFO_REGSET_ENTRY32(event_counters.host_msg, HOST_MSG_EVENT_COUNT),
PM_INFO_REGSET_ENTRY32(event_counters.sys_pm, SYS_PM_EVENT_COUNT),
PM_INFO_REGSET_ENTRY32(event_counters.local_ssm, SSM_EVENT_COUNT),
PM_INFO_REGSET_ENTRY32(event_counters.timer, TIMER_EVENT_COUNT),
PM_INFO_REGSET_ENTRY32(event_counters.unknown, UNKNOWN_EVENT_COUNT),
};
static const struct pm_status_row pm_event_rows[ICP_QAT_NUMBER_OF_PM_EVENTS] = {
PM_INFO_REGSET_ENTRY32(event_log[0], EVENT0),
PM_INFO_REGSET_ENTRY32(event_log[1], EVENT1),
PM_INFO_REGSET_ENTRY32(event_log[2], EVENT2),
PM_INFO_REGSET_ENTRY32(event_log[3], EVENT3),
PM_INFO_REGSET_ENTRY32(event_log[4], EVENT4),
PM_INFO_REGSET_ENTRY32(event_log[5], EVENT5),
PM_INFO_REGSET_ENTRY32(event_log[6], EVENT6),
PM_INFO_REGSET_ENTRY32(event_log[7], EVENT7),
};
static const struct pm_status_row pm_csrs_rows[] = {
PM_INFO_REGSET_ENTRY32(pm.fw_init, CPM_PM_FW_INIT),
PM_INFO_REGSET_ENTRY32(pm.status, CPM_PM_STATUS),
PM_INFO_REGSET_ENTRY32(pm.main, CPM_PM_MASTER_FW),
PM_INFO_REGSET_ENTRY32(pm.pwrreq, CPM_PM_PWRREQ),
};
static_assert(sizeof(struct icp_qat_fw_init_admin_pm_info) < PAGE_SIZE);
static ssize_t adf_gen4_print_pm_status(struct adf_accel_dev *accel_dev,
char __user *buf, size_t count,
loff_t *pos)
{
void __iomem *pmisc = adf_get_pmisc_base(accel_dev);
struct adf_pm *pm = &accel_dev->power_management;
struct icp_qat_fw_init_admin_pm_info *pm_info;
dma_addr_t p_state_addr;
u32 *pm_info_regs;
char *pm_kv;
int len = 0;
u32 val;
int ret;
pm_info = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!pm_info)
return -ENOMEM;
pm_kv = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!pm_kv) {
ret = -ENOMEM;
goto out_free;
}
p_state_addr = dma_map_single(&GET_DEV(accel_dev), pm_info, PAGE_SIZE,
DMA_FROM_DEVICE);
ret = dma_mapping_error(&GET_DEV(accel_dev), p_state_addr);
if (ret)
goto out_free;
/* Query PM info from QAT FW */
ret = adf_get_pm_info(accel_dev, p_state_addr, PAGE_SIZE);
dma_unmap_single(&GET_DEV(accel_dev), p_state_addr, PAGE_SIZE,
DMA_FROM_DEVICE);
if (ret)
goto out_free;
pm_info_regs = (u32 *)pm_info;
/* Fusectl related */
len += scnprintf(&pm_kv[len], PAGE_SIZE - len,
"----------- PM Fuse info ---------\n");
len += adf_pm_scnprint_table_lower_keys(&pm_kv[len], pm_fuse_rows,
pm_info_regs, PAGE_SIZE - len,
ARRAY_SIZE(pm_fuse_rows));
len += scnprintf(&pm_kv[len], PAGE_SIZE - len, "max_pwrreq: %#x\n",
pm_info->max_pwrreq);
len += scnprintf(&pm_kv[len], PAGE_SIZE - len, "min_pwrreq: %#x\n",
pm_info->min_pwrreq);
/* PM related */
len += scnprintf(&pm_kv[len], PAGE_SIZE - len,
"------------ PM Info ------------\n");
len += scnprintf(&pm_kv[len], PAGE_SIZE - len, "power_level: %s\n",
pm_info->pwr_state == PM_SET_MIN ? "min" : "max");
len += adf_pm_scnprint_table_lower_keys(&pm_kv[len], pm_info_rows,
pm_info_regs, PAGE_SIZE - len,
ARRAY_SIZE(pm_info_rows));
len += scnprintf(&pm_kv[len], PAGE_SIZE - len, "pm_mode: STATIC\n");
/* SSM related */
len += scnprintf(&pm_kv[len], PAGE_SIZE - len,
"----------- SSM_PM Info ----------\n");
len += adf_pm_scnprint_table_lower_keys(&pm_kv[len], pm_ssm_rows,
pm_info_regs, PAGE_SIZE - len,
ARRAY_SIZE(pm_ssm_rows));
/* Log related */
len += scnprintf(&pm_kv[len], PAGE_SIZE - len,
"------------- PM Log -------------\n");
len += adf_pm_scnprint_table_lower_keys(&pm_kv[len], pm_log_rows,
pm_info_regs, PAGE_SIZE - len,
ARRAY_SIZE(pm_log_rows));
len += adf_pm_scnprint_table_lower_keys(&pm_kv[len], pm_event_rows,
pm_info_regs, PAGE_SIZE - len,
ARRAY_SIZE(pm_event_rows));
len += scnprintf(&pm_kv[len], PAGE_SIZE - len, "idle_irq_count: %#x\n",
pm->idle_irq_counters);
len += scnprintf(&pm_kv[len], PAGE_SIZE - len, "fw_irq_count: %#x\n",
pm->fw_irq_counters);
len += scnprintf(&pm_kv[len], PAGE_SIZE - len,
"throttle_irq_count: %#x\n", pm->throttle_irq_counters);
len += scnprintf(&pm_kv[len], PAGE_SIZE - len, "host_ack_count: %#x\n",
pm->host_ack_counter);
len += scnprintf(&pm_kv[len], PAGE_SIZE - len, "host_nack_count: %#x\n",
pm->host_nack_counter);
/* CSRs content */
len += scnprintf(&pm_kv[len], PAGE_SIZE - len,
"----------- HW PM CSRs -----------\n");
len += adf_pm_scnprint_table_upper_keys(&pm_kv[len], pm_csrs_rows,
pm_info_regs, PAGE_SIZE - len,
ARRAY_SIZE(pm_csrs_rows));
val = ADF_CSR_RD(pmisc, ADF_GEN4_PM_HOST_MSG);
len += scnprintf(&pm_kv[len], PAGE_SIZE - len,
"CPM_PM_HOST_MSG: %#x\n", val);
val = ADF_CSR_RD(pmisc, ADF_GEN4_PM_INTERRUPT);
len += scnprintf(&pm_kv[len], PAGE_SIZE - len,
"CPM_PM_INTERRUPT: %#x\n", val);
ret = simple_read_from_buffer(buf, count, pos, pm_kv, len);
out_free:
kfree(pm_info);
kfree(pm_kv);
return ret;
}
void adf_gen4_init_dev_pm_data(struct adf_accel_dev *accel_dev)
{
accel_dev->power_management.print_pm_status = adf_gen4_print_pm_status;
accel_dev->power_management.present = true;
}
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More