
From: Qunqin Zhao <zhaoqunqin@loongson.cn> LoongArch inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ICOFL4 CVE: NA -------------------------------- And clean up coding style. Signed-off-by: Qunqin Zhao <zhaoqunqin@loongson.cn> Change-Id: Ie31274fc663cbe63fcff7f7111c8f6e2afde531b Signed-off-by: Hongchen Zhang <zhanghongchen@loongson.cn> --- arch/loongarch/configs/loongson3_defconfig | 4 + drivers/char/Kconfig | 13 +- drivers/char/Makefile | 1 - drivers/char/loongson_se.c | 536 -------------------- drivers/char/lsse_sdf_cdev.c | 95 ++-- drivers/char/tpm/Kconfig | 2 +- drivers/char/tpm/tpm_loongson.c | 79 ++- drivers/crypto/Kconfig | 1 + drivers/crypto/Makefile | 1 + drivers/crypto/loongson/Kconfig | 31 ++ drivers/crypto/loongson/Makefile | 3 + drivers/crypto/loongson/loongson-hash.c | 312 ++++++++++++ drivers/crypto/loongson/loongson-rng.c | 209 ++++++++ drivers/crypto/loongson/loongson-skcipher.c | 248 +++++++++ drivers/mfd/Kconfig | 11 + drivers/mfd/Makefile | 2 + drivers/mfd/loongson-se.c | 256 ++++++++++ include/linux/mfd/loongson-se.h | 59 +++ 18 files changed, 1198 insertions(+), 665 deletions(-) delete mode 100644 drivers/char/loongson_se.c create mode 100644 drivers/crypto/loongson/Kconfig create mode 100644 drivers/crypto/loongson/Makefile create mode 100644 drivers/crypto/loongson/loongson-hash.c create mode 100644 drivers/crypto/loongson/loongson-rng.c create mode 100644 drivers/crypto/loongson/loongson-skcipher.c create mode 100644 drivers/mfd/loongson-se.c create mode 100644 include/linux/mfd/loongson-se.h diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index 0d78f6912fe9..aa8f4cc99e97 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -1308,6 +1308,7 @@ CONFIG_MFD_VIPERBOARD=m CONFIG_MFD_SM501=m CONFIG_MFD_SM501_GPIO=y CONFIG_MFD_VX855=m +CONFIG_MFD_LOONGSON_SE=m CONFIG_RC_CORE=m CONFIG_LIRC=y CONFIG_RC_DECODERS=y @@ -2189,6 +2190,9 @@ CONFIG_CRYPTO_CRC32_LOONGARCH=m CONFIG_CRYPTO_DEV_NITROX_CNN55XX=m CONFIG_CRYPTO_DEV_CHELSIO=m CONFIG_CRYPTO_DEV_VIRTIO=m +CONFIG_CRYPTO_DEV_LOONGSON_HASH=m +CONFIG_CRYPTO_DEV_LOONGSON_RNG=m +CONFIG_CRYPTO_DEV_LOONGSON_SKCIPHER=m CONFIG_SW_SE_CHIP=m CONFIG_SIGNED_PE_FILE_VERIFICATION=y CONFIG_SECONDARY_TRUSTED_KEYRING=y diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 1d2957bc100d..1548c7582df4 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -391,20 +391,9 @@ config UV_MMTIMER The uv_mmtimer device allows direct userspace access to the UV system timer. -config LOONGSON_SE - tristate "LOONGSON SECURITY MODULE Interface" - depends on LOONGARCH - select MFD_CORE - default m - help - If you have LOONGSON security module (SE) support say Yes and it - will be accessible from within Linux. - To compile this driver as a module, choose M here,the module will - be called loongson-se. - config LOONGSON_SE_SDF tristate "LOONGSON SECURITY MODULE SDF Interface" - depends on LOONGARCH && LOONGSON_SE + depends on LOONGARCH && MFD_LOONGSON_SE default m help If you want to use LOONGSON security module (SE) as SDF say Yes diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 109af71c5416..1242ca2ddd81 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -32,7 +32,6 @@ obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o obj-$(CONFIG_PC8736x_GPIO) += pc8736x_gpio.o obj-$(CONFIG_NSC_GPIO) += nsc_gpio.o obj-$(CONFIG_TELCLOCK) += tlclk.o -obj-$(CONFIG_LOONGSON_SE) += loongson_se.o obj-$(CONFIG_LOONGSON_SE_SDF) += lsse_sdf_cdev.o obj-$(CONFIG_MWAVE) += mwave/ diff --git a/drivers/char/loongson_se.c b/drivers/char/loongson_se.c deleted file mode 100644 index c9bd51adc792..000000000000 --- a/drivers/char/loongson_se.c +++ /dev/null @@ -1,536 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2024 Loongson Technology Corporation Limited - */ - -#include <linux/acpi.h> -#include <linux/cdev.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/dma-mapping.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/iopoll.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/mfd/core.h> -#include <linux/platform_device.h> -#include <soc/loongson/se.h> - -/* - * The Loongson Security Module provides the control for hardware - * encryption acceleration child devices. The SE framework is - * shown as follows: - * - * +------------+ - * | CPU | - * +------------+ - * ^ ^ - * DMA | | IRQ - * v v - * +-----------------------------------+ - * | Loongson Security Module | - * +-----------------------------------+ - * ^ ^ - * chnnel0 | channel1 | - * v v - * +-----------+ +----------+ - * | sub-dev0 | | sub-dev1 | ..... Max sub-dev31 - * +-----------+ +----------+ - * - * The CPU cannot directly communicate with SE's sub devices, - * but sends commands to SE, which processes the commands and - * sends them to the corresponding sub devices. - */ - -static inline u32 se_readl(struct loongson_se *se, u32 off) -{ - return readl(se->base + off); -} - -static inline void se_writel(struct loongson_se *se, u32 val, u32 off) -{ - writel(val, se->base + off); -} - -static inline bool se_ch_status(struct loongson_se *se, u32 int_bit) -{ - return !!(se->ch_status & int_bit); -} - -static void se_enable_int(struct loongson_se *se, u32 int_bit) -{ - unsigned long flag; - u32 tmp; - - spin_lock_irqsave(&se->dev_lock, flag); - - tmp = se_readl(se, SE_S2LINT_EN); - tmp |= int_bit; - se_writel(se, tmp, SE_S2LINT_EN); - - spin_unlock_irqrestore(&se->dev_lock, flag); -} - -static void se_disable_int(struct loongson_se *se, u32 int_bit) -{ - unsigned long flag; - u32 tmp; - - spin_lock_irqsave(&se->dev_lock, flag); - - tmp = se_readl(se, SE_S2LINT_EN); - tmp &= ~(int_bit); - se_writel(se, tmp, SE_S2LINT_EN); - - spin_unlock_irqrestore(&se->dev_lock, flag); -} - -static int se_send_requeset(struct loongson_se *se, struct se_data *req) -{ - unsigned long flag; - u32 status; - int err; - int i; - - if (!se || !req) - return -EINVAL; - - if (se_readl(se, SE_L2SINT_STAT) || - !(se_readl(se, SE_L2SINT_EN) & req->int_bit)) - return -EBUSY; - - spin_lock_irqsave(&se->cmd_lock, flag); - - for (i = 0; i < ARRAY_SIZE(req->u.data); i++) - se_writel(se, req->u.data[i], SE_DATA_S + i * 4); - se_writel(se, req->int_bit, SE_L2SINT_SET); - err = readl_relaxed_poll_timeout_atomic(se->base + SE_L2SINT_STAT, status, - !(status & req->int_bit), 10, 10000); - - spin_unlock_irqrestore(&se->cmd_lock, flag); - - return err; -} - -static int se_get_response(struct loongson_se *se, struct se_data *res) -{ - unsigned long flag; - int i; - - if (!se || !res) - return -EINVAL; - - if ((se_readl(se, SE_S2LINT_STAT) & res->int_bit) == 0) - return -EBUSY; - - spin_lock_irqsave(&se->cmd_lock, flag); - - for (i = 0; i < ARRAY_SIZE(res->u.data); i++) - res->u.data[i] = se_readl(se, SE_DATA_L + i * 4); - se_writel(se, res->int_bit, SE_S2LINT_CL); - - spin_unlock_irqrestore(&se->cmd_lock, flag); - - return 0; -} - -static int loongson_se_get_res(struct loongson_se *se, u32 int_bit, u32 cmd, - struct se_data *res) -{ - res->int_bit = int_bit; - - if (se_get_response(se, res)) { - dev_err(se->dev, "Int 0x%x get response fail.\n", int_bit); - return -EFAULT; - } - - /* Check response */ - if (res->u.res.cmd != cmd) { - dev_err(se->dev, "Response cmd is 0x%x, not expect cmd 0x%x.\n", - res->u.res.cmd, cmd); - return -EFAULT; - } - - return 0; -} - -static int se_send_genl_cmd(struct loongson_se *se, struct se_data *req, - struct se_data *res, int retry) -{ - int err, cnt = 0; - -try_again: - if (cnt++ >= retry) { - err = -ETIMEDOUT; - goto out; - } - - dev_dbg(se->dev, "%d time send cmd 0x%x\n", cnt, req->u.gcmd.cmd); - - err = se_send_requeset(se, req); - if (err) - goto try_again; - - if (!wait_for_completion_timeout(&se->cmd_completion, HZ)) { - se_enable_int(se, req->int_bit); - goto try_again; - } - err = loongson_se_get_res(se, req->int_bit, req->u.gcmd.cmd, res); - if (err || res->u.res.cmd_ret) { - se_enable_int(se, req->int_bit); - goto try_again; - } - -out: - se_enable_int(se, req->int_bit); - - return err; -} - -static int loongson_se_set_msg(struct lsse_ch *ch) -{ - struct loongson_se *se = ch->se; - struct se_data req = {0}; - struct se_data res = {0}; - int err; - - req.int_bit = SE_INT_SETUP; - req.u.gcmd.cmd = SE_CMD_SETMSG; - /* MSG off */ - req.u.gcmd.info[0] = ch->id; - req.u.gcmd.info[1] = ch->smsg - se->mem_base; - req.u.gcmd.info[2] = ch->msg_size; - - dev_dbg(se->dev, "Set Channel %d msg off 0x%x, msg size %d\n", - ch->id, req.u.gcmd.info[1], req.u.gcmd.info[2]); - - err = se_send_genl_cmd(se, &req, &res, 5); - if (res.u.res.cmd_ret) - return res.u.res.cmd_ret; - - return err; -} - -static irqreturn_t se_irq(int irq, void *dev_id) -{ - struct loongson_se *se = (struct loongson_se *)dev_id; - struct lsse_ch *ch; - u32 int_status; - - int_status = se_readl(se, SE_S2LINT_STAT); - - dev_dbg(se->dev, "%s int status is 0x%x\n", __func__, int_status); - - se_disable_int(se, int_status); - - if (int_status & SE_INT_SETUP) { - complete(&se->cmd_completion); - int_status &= ~SE_INT_SETUP; - } - - while (int_status) { - int id = __ffs(int_status); - - ch = &se->chs[id]; - if (ch->complete) - ch->complete(ch); - int_status &= ~BIT(id); - se_writel(se, BIT(id), SE_S2LINT_CL); - } - - return IRQ_HANDLED; -} - -static int se_init_hw(struct loongson_se *se, dma_addr_t addr, int size) -{ - struct se_data req; - struct se_data res; - int err, retry = 5; - - se_enable_int(se, SE_INT_SETUP); - - /* Start engine */ - memset(&req, 0, sizeof(struct se_data)); - memset(&res, 0, sizeof(struct se_data)); - req.int_bit = SE_INT_SETUP; - req.u.gcmd.cmd = SE_CMD_START; - err = se_send_genl_cmd(se, &req, &res, retry); - if (err) - return err; - - /* Get Version */ - memset(&req, 0, sizeof(struct se_data)); - memset(&res, 0, sizeof(struct se_data)); - req.int_bit = SE_INT_SETUP; - req.u.gcmd.cmd = SE_CMD_GETVER; - err = se_send_genl_cmd(se, &req, &res, retry); - if (err) - return err; - se->version = res.u.res.info[0]; - - /* Set shared mem */ - memset(&req, 0, sizeof(struct se_data)); - memset(&res, 0, sizeof(struct se_data)); - req.int_bit = SE_INT_SETUP; - req.u.gcmd.cmd = SE_CMD_SETBUF; - /* MMAP */ - req.u.gcmd.info[0] = addr & 0xffffffff; - req.u.gcmd.info[1] = addr >> 32; - /* MASK */ - req.u.gcmd.info[2] = ~(size - 1); - req.u.gcmd.info[3] = 0xffffffff; - err = se_send_genl_cmd(se, &req, &res, retry); - if (err) - return err; - pr_debug("Set win mmap 0x%llx, mask 0x%llx\n", - ((u64)req.u.gcmd.info[1] << 32) | req.u.gcmd.info[0], - ((u64)req.u.gcmd.info[3] << 32) | req.u.gcmd.info[2]); - - return err; -} - -static void se_disable_hw(struct loongson_se *se) -{ - struct se_data req = {0}; - struct se_data res = {0}; - - /* Stop engine */ - req.int_bit = SE_INT_SETUP; - req.u.gcmd.cmd = SE_CMD_STOP; - se_send_genl_cmd(se, &req, &res, 5); - se_disable_int(se, SE_INT_ALL); -} - -/* - * Called by SE's child device driver. - */ -int se_send_ch_requeset(struct lsse_ch *ch) -{ - struct loongson_se *se; - u32 status, int_bit; - int err; - - se = ch->se; - int_bit = ch->int_bit; - mutex_lock(&se->ch_init_lock); - if ((se_readl(se, SE_L2SINT_STAT) & int_bit) || - !(se_readl(se, SE_L2SINT_EN) & int_bit)) { - mutex_unlock(&se->ch_init_lock); - return -EBUSY; - } - - se_enable_int(se, int_bit); - se_writel(se, int_bit, SE_L2SINT_SET); - - err = readl_relaxed_poll_timeout_atomic(se->base + SE_L2SINT_STAT, status, - !(status & int_bit), 10, 10000); - mutex_unlock(&se->ch_init_lock); - - return err; -} -EXPORT_SYMBOL_GPL(se_send_ch_requeset); - -/* - * se_init_ch() - Init the channel used by child device. - * - * Allocate the shared memory agreed upon with SE on SE probe, - * and register the callback function when the data processing - * in this channel is completed. - */ -struct lsse_ch *se_init_ch(struct device *dev, int id, int data_size, int msg_size, - void *priv, void (*complete)(struct lsse_ch *se_ch)) -{ - struct loongson_se *se = dev_get_drvdata(dev); - struct lsse_ch *ch; - int data_first, data_nr; - int msg_first, msg_nr; - - mutex_lock(&se->ch_init_lock); - if (!se) { - pr_err("SE has bot been initialized\n"); - return NULL; - } - - if (id > SE_CH_MAX) { - dev_err(se->dev, "Channel number %d is invalid\n", id); - return NULL; - } - - if (se_ch_status(se, BIT(id))) { - dev_err(se->dev, "Channel number %d has been initialized\n", id); - return NULL; - } - - ch = &se->chs[id]; - ch->se = se; - ch->id = id; - ch->int_bit = BIT(id); - se->ch_status |= BIT(id); - - data_nr = round_up(data_size, PAGE_SIZE) / PAGE_SIZE; - data_first = bitmap_find_next_zero_area(se->mem_map, se->mem_map_pages, - 0, data_nr, 0); - if (data_first >= se->mem_map_pages) { - dev_err(se->dev, "Insufficient memory space\n"); - mutex_unlock(&se->ch_init_lock); - return NULL; - } - - bitmap_set(se->mem_map, data_first, data_nr); - ch->data_buffer = se->mem_base + data_first * PAGE_SIZE; - ch->data_addr = se->mem_addr + data_first * PAGE_SIZE; - ch->data_size = data_size; - - msg_nr = round_up(msg_size, PAGE_SIZE) / PAGE_SIZE; - msg_first = bitmap_find_next_zero_area(se->mem_map, se->mem_map_pages, - 0, msg_nr, 0); - if (msg_first >= se->mem_map_pages) { - dev_err(se->dev, "Insufficient memory space\n"); - bitmap_clear(se->mem_map, data_first, data_nr); - mutex_unlock(&se->ch_init_lock); - return NULL; - } - - bitmap_set(se->mem_map, msg_first, msg_nr); - ch->smsg = se->mem_base + msg_first * PAGE_SIZE; - ch->rmsg = ch->smsg + msg_size / 2; - ch->msg_size = msg_size; - ch->complete = complete; - ch->priv = priv; - spin_lock_init(&ch->ch_lock); - - - if (loongson_se_set_msg(ch)) { - dev_err(se->dev, "Channel %d setup message address failed\n", id); - mutex_unlock(&se->ch_init_lock); - return NULL; - } - - se_enable_int(se, ch->int_bit); - mutex_unlock(&se->ch_init_lock); - - return ch; -} -EXPORT_SYMBOL_GPL(se_init_ch); - -void se_deinit_ch(struct lsse_ch *ch) -{ - struct loongson_se *se = ch->se; - unsigned long flag; - int first, nr; - int id = ch->id; - - if (!se) { - pr_err("SE has bot been initialized\n"); - return; - } - - if (id > SE_CH_MAX) { - dev_err(se->dev, "Channel number %d is invalid\n", id); - return; - } - - if (!se_ch_status(se, BIT(id))) { - dev_err(se->dev, "Channel number %d has not been initialized\n", id); - return; - } - - spin_lock_irqsave(&se->dev_lock, flag); - se->ch_status &= ~BIT(ch->id); - - first = (ch->data_buffer - se->mem_base) / PAGE_SIZE; - nr = round_up(ch->data_size, PAGE_SIZE) / PAGE_SIZE; - bitmap_clear(se->mem_map, first, nr); - - first = (ch->smsg - se->mem_base) / PAGE_SIZE; - nr = round_up(ch->msg_size, PAGE_SIZE) / PAGE_SIZE; - bitmap_clear(se->mem_map, first, nr); - - se_disable_int(se, ch->int_bit); - spin_unlock_irqrestore(&se->dev_lock, flag); - -} -EXPORT_SYMBOL_GPL(se_deinit_ch); - -static const struct mfd_cell engines[] = { - { .name = "loongson-tpm" }, -}; - -static int loongson_se_probe(struct platform_device *pdev) -{ - struct loongson_se *se; - struct device *dev = &pdev->dev; - int nr_irq, irq, err, size; - - se = devm_kmalloc(dev, sizeof(*se), GFP_KERNEL); - if (!se) - return -ENOMEM; - se->dev = dev; - dev_set_drvdata(dev, se); - init_completion(&se->cmd_completion); - mutex_init(&se->ch_init_lock); - spin_lock_init(&se->cmd_lock); - spin_lock_init(&se->dev_lock); - /* Setup DMA buffer */ - dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); - if (device_property_read_u32(dev, "dmam_size", &size)) - return -ENODEV; - size = roundup_pow_of_two(size); - se->mem_base = dmam_alloc_coherent(dev, size, &se->mem_addr, GFP_KERNEL); - if (!se->mem_base) - return -ENOMEM; - se->mem_map_pages = size / PAGE_SIZE; - se->mem_map = devm_bitmap_zalloc(dev, se->mem_map_pages, GFP_KERNEL); - if (!se->mem_map) - return -ENOMEM; - - se->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(se->base)) - return PTR_ERR(se->base); - - nr_irq = platform_irq_count(pdev); - if (nr_irq <= 0) - return -ENODEV; - while (nr_irq) { - irq = platform_get_irq(pdev, --nr_irq); - if (irq < 0) - return -ENODEV; - /* Use the same interrupt handler address. - * Determine which irq it is accroding - * SE_S2LINT_STAT register. - */ - err = devm_request_irq(dev, irq, se_irq, 0, - "loongson-se", se); - if (err) - dev_err(dev, "failed to request irq: %d\n", err); - } - - err = se_init_hw(se, se->mem_addr, size); - if (err) { - se_disable_hw(se); - return err; - } - - return devm_mfd_add_devices(dev, 0, engines, ARRAY_SIZE(engines), NULL, 0, NULL); -} - -static const struct acpi_device_id loongson_se_acpi_match[] = { - {"LOON0011", 0}, - {} -}; -MODULE_DEVICE_TABLE(acpi, loongson_se_acpi_match); - -static struct platform_driver loongson_se_driver = { - .probe = loongson_se_probe, - .driver = { - .name = "loongson-se", - .acpi_match_table = loongson_se_acpi_match, - }, -}; -module_platform_driver(loongson_se_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Loongson Technology Corporation"); -MODULE_DESCRIPTION("Loongson Security Module driver"); diff --git a/drivers/char/lsse_sdf_cdev.c b/drivers/char/lsse_sdf_cdev.c index 3bbe3cbb35ae..7ffccede03a4 100644 --- a/drivers/char/lsse_sdf_cdev.c +++ b/drivers/char/lsse_sdf_cdev.c @@ -16,20 +16,17 @@ #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/wait.h> -#include <soc/loongson/se.h> +#include <linux/mfd/loongson-se.h> +#define SE_CMD_SDF 0x700 #define SE_SDF_BUFSIZE (PAGE_SIZE * 2) #define SDF_OPENSESSION (0x204) #define SDF_CLOSESESSION (0x205) struct sdf_dev { struct miscdevice miscdev; - struct lsse_ch *se_ch; + struct loongson_se_engine *se_ch; struct mutex data_lock; - bool processing_cmd; - - /* Synchronous CMD */ - wait_queue_head_t wq; }; struct se_sdf_msg { @@ -65,63 +62,36 @@ struct sdf_file_pvt_data { struct sdf_handle *ph; }; -static void sdf_complete(struct lsse_ch *ch) -{ - struct sdf_dev *se = (struct sdf_dev *)ch->priv; - - se->processing_cmd = false; - wake_up(&se->wq); -} - -static int se_send_sdf_cmd(struct sdf_dev *se, int len, int retry) +static int se_send_sdf_cmd(struct sdf_dev *se, int len) { - struct se_sdf_msg *smsg = (struct se_sdf_msg *)se->se_ch->smsg; - int err; - - spin_lock_irq(&se->se_ch->ch_lock); + struct se_sdf_msg *command = (struct se_sdf_msg *)se->se_ch->command; - smsg->cmd = SE_CMD_SDF; + command->cmd = SE_CMD_SDF; /* One time one cmd */ - smsg->data_off = se->se_ch->data_buffer - se->se_ch->se->mem_base; - smsg->data_len = len; + command->data_off = se->se_ch->buffer_off; + command->data_len = len; -try_again: - if (!retry--) - goto out; - - err = se_send_ch_requeset(se->se_ch); - if (err) { - udelay(5); - goto try_again; - } - -out: - spin_unlock_irq(&se->se_ch->ch_lock); - - return err; + return loongson_se_send_engine_cmd(se->se_ch); } static int sdf_recvu(struct sdf_file_pvt_data *pvt, char __user *buf, int *se_ret) { struct sdf_dev *se = pvt->se; struct sdf_kernel_command *skc; - struct se_sdf_msg *rmsg; + struct se_sdf_msg *command_ret; struct sdf_handle *ph; int ret; - if (!wait_event_timeout(se->wq, !se->processing_cmd, HZ*10)) - return -ETIME; - - rmsg = (struct se_sdf_msg *)se->se_ch->rmsg; - if (rmsg->cmd != SE_CMD_SDF) { + command_ret = (struct se_sdf_msg *)se->se_ch->command_ret; + if (command_ret->cmd != SE_CMD_SDF) { pr_err("se get wrong response\n"); return -EIO; } ret = copy_to_user((char __user *)buf, - se->se_ch->data_buffer + rmsg->data_off, rmsg->data_len); + se->se_ch->data_buffer + command_ret->data_off, command_ret->data_len); - skc = (struct sdf_kernel_command *)(se->se_ch->data_buffer + rmsg->data_off); + skc = (struct sdf_kernel_command *)(se->se_ch->data_buffer + command_ret->data_off); *se_ret = skc->header.u.ret; if (skc->header.command == SDF_OPENSESSION && !*se_ret) { ph = kmalloc(sizeof(*ph), GFP_KERNEL); @@ -165,10 +135,11 @@ static int sdf_sendu(struct sdf_file_pvt_data *pvt, if (skc->header.command == SDF_CLOSESESSION) ph = find_sdf_handle(skc->handle, pvt); - se->processing_cmd = true; - ret = se_send_sdf_cmd(se, count, 5); + ret = se_send_sdf_cmd(se, count); if (ret) { - pr_err("se_send_sdf_cmd failed\n"); + pr_debug("se_send_sdf_cmd failed u %d\n", ret); + udelay(LOONGSON_ENGINE_CMD_TIMEOUT_US); + reinit_completion(&se->se_ch->completion); goto out_unlock; } @@ -206,19 +177,14 @@ static ssize_t sdf_write(struct file *filp, const char __user *buf, static int sdf_recvk(struct sdf_file_pvt_data *pvt, char *buf) { struct sdf_dev *se = pvt->se; - struct se_sdf_msg *rmsg; - int time; - - time = wait_event_timeout(se->wq, !se->processing_cmd, HZ*10); - if (!time) - return -ETIME; + struct se_sdf_msg *command_ret; - rmsg = (struct se_sdf_msg *)se->se_ch->rmsg; - if (rmsg->cmd != SE_CMD_SDF) { + command_ret = (struct se_sdf_msg *)se->se_ch->command_ret; + if (command_ret->cmd != SE_CMD_SDF) { pr_err("se get wrong response\n"); return -EIO; } - memcpy(buf, se->se_ch->data_buffer + rmsg->data_off, rmsg->data_len); + memcpy(buf, se->se_ch->data_buffer + command_ret->data_off, command_ret->data_len); return 0; } @@ -231,10 +197,11 @@ static int sdf_sendk(struct sdf_file_pvt_data *pvt, char *buf, size_t count) mutex_lock(&se->data_lock); memcpy(se->se_ch->data_buffer, buf, count); - se->processing_cmd = true; - ret = se_send_sdf_cmd(se, count, 5); + ret = se_send_sdf_cmd(se, count); if (ret) { - pr_err("se_send_sdf_cmd failed\n"); + pr_debug("se_send_sdf_cmd failed k %d\n", ret); + udelay(LOONGSON_ENGINE_CMD_TIMEOUT_US); + reinit_completion(&se->se_ch->completion); goto out_unlock; } @@ -259,8 +226,8 @@ static int close_one_handle(struct sdf_file_pvt_data *pvt, struct sdf_handle *ph skc->header.param_len[0] = sizeof(skc->handle); /* close one session */ ret = sdf_sendk(pvt, (char *)&pvt->skc, sizeof(*skc)); - if (skc->header.u.ret) { - pr_err("Auto Close Session failed, session handle: %llx, ret: %d\n", + if (skc->header.u.ret || ret) { + pr_debug("Auto Close Session failed, session handle: %llx, ret: %d\n", (u64)ph->handle, skc->header.u.ret); return skc->header.u.ret; } @@ -327,15 +294,12 @@ static int sdf_probe(struct platform_device *pdev) if (!sdf) return -ENOMEM; mutex_init(&sdf->data_lock); - init_waitqueue_head(&sdf->wq); - sdf->processing_cmd = false; platform_set_drvdata(pdev, sdf); if (device_property_read_u32(&pdev->dev, "channel", &ch)) return -ENODEV; msg_size = 2 * sizeof(struct se_sdf_msg); - sdf->se_ch = se_init_ch(pdev->dev.parent, ch, SE_SDF_BUFSIZE, - msg_size, sdf, sdf_complete); + sdf->se_ch = loongson_se_init_engine(pdev->dev.parent, ch); sdf->miscdev.minor = MISC_DYNAMIC_MINOR; sdf->miscdev.name = "lsse_sdf"; @@ -352,7 +316,6 @@ static int sdf_remove(struct platform_device *pdev) struct sdf_dev *sdf = platform_get_drvdata(pdev); misc_deregister(&sdf->miscdev); - se_deinit_ch(sdf->se_ch); return 0; } diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 3524b2e6086e..a8046d1a17a1 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -176,7 +176,7 @@ config TCG_IBMVTPM config TCG_LOONGSON tristate "Loongson TPM Interface" - depends on LOONGSON_SE + depends on MFD_LOONGSON_SE help If you want to make Loongson TPM support available, say Yes and it will be accessible from within Linux. To compile this diff --git a/drivers/char/tpm/tpm_loongson.c b/drivers/char/tpm/tpm_loongson.c index 3c27d86b77ba..a4ec23639911 100644 --- a/drivers/char/tpm/tpm_loongson.c +++ b/drivers/char/tpm/tpm_loongson.c @@ -2,56 +2,44 @@ /* Copyright (c) 2025 Loongson Technology Corporation Limited. */ #include <linux/device.h> -#include <soc/loongson/se.h> +#include <linux/mfd/loongson-se.h> #include <linux/platform_device.h> #include <linux/wait.h> #include "tpm.h" -struct tpm_loongson_msg { - u32 cmd; +struct tpm_loongson_cmd { + u32 cmd_id; u32 data_off; u32 data_len; - u32 info[5]; + u32 pad[5]; }; -struct tpm_loongson_dev { - struct lsse_ch *se_ch; - struct completion tpm_loongson_completion; -}; - -static void tpm_loongson_complete(struct lsse_ch *ch) -{ - struct tpm_loongson_dev *td = ch->priv; - - complete(&td->tpm_loongson_completion); -} - static int tpm_loongson_recv(struct tpm_chip *chip, u8 *buf, size_t count) { - struct tpm_loongson_dev *td = dev_get_drvdata(&chip->dev); - struct tpm_loongson_msg *rmsg; - int sig; + struct loongson_se_engine *tpm_engine = dev_get_drvdata(&chip->dev); + struct tpm_loongson_cmd *cmd_ret = tpm_engine->command_ret; - sig = wait_for_completion_interruptible(&td->tpm_loongson_completion); - if (sig) - return sig; + if (cmd_ret->data_len > count) + return -EIO; - rmsg = td->se_ch->rmsg; - memcpy(buf, td->se_ch->data_buffer, rmsg->data_len); + memcpy(buf, tpm_engine->data_buffer, cmd_ret->data_len); - return rmsg->data_len; + return cmd_ret->data_len; } static int tpm_loongson_send(struct tpm_chip *chip, u8 *buf, size_t count) { - struct tpm_loongson_dev *td = dev_get_drvdata(&chip->dev); - struct tpm_loongson_msg *smsg = td->se_ch->smsg; + struct loongson_se_engine *tpm_engine = dev_get_drvdata(&chip->dev); + struct tpm_loongson_cmd *cmd = tpm_engine->command; - memcpy(td->se_ch->data_buffer, buf, count); - smsg->data_len = count; + if (count > tpm_engine->buffer_size) + return -E2BIG; - return se_send_ch_requeset(td->se_ch); + cmd->data_len = count; + memcpy(tpm_engine->data_buffer, buf, count); + + return loongson_se_send_engine_cmd(tpm_engine); } static const struct tpm_class_ops tpm_loongson_ops = { @@ -62,42 +50,35 @@ static const struct tpm_class_ops tpm_loongson_ops = { static int tpm_loongson_probe(struct platform_device *pdev) { + struct loongson_se_engine *tpm_engine; struct device *dev = &pdev->dev; - struct tpm_loongson_msg *smsg; - struct tpm_loongson_dev *td; + struct tpm_loongson_cmd *cmd; struct tpm_chip *chip; - td = devm_kzalloc(dev, sizeof(struct tpm_loongson_dev), GFP_KERNEL); - if (!td) - return -ENOMEM; - - init_completion(&td->tpm_loongson_completion); - td->se_ch = se_init_ch(dev->parent, SE_CH_TPM, PAGE_SIZE, - 2 * sizeof(struct tpm_loongson_msg), td, - tpm_loongson_complete); - if (!td->se_ch) + tpm_engine = loongson_se_init_engine(dev->parent, SE_ENGINE_TPM); + if (!tpm_engine) return -ENODEV; - smsg = td->se_ch->smsg; - smsg->cmd = SE_CMD_TPM; - smsg->data_off = td->se_ch->data_buffer - td->se_ch->se->mem_base; + cmd = tpm_engine->command; + cmd->cmd_id = SE_CMD_TPM; + cmd->data_off = tpm_engine->buffer_off; chip = tpmm_chip_alloc(dev, &tpm_loongson_ops); if (IS_ERR(chip)) return PTR_ERR(chip); chip->flags = TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_IRQ; - dev_set_drvdata(&chip->dev, td); + dev_set_drvdata(&chip->dev, tpm_engine); return tpm_chip_register(chip); } -static struct platform_driver tpm_loongson_driver = { +static struct platform_driver tpm_loongson = { .probe = tpm_loongson_probe, .driver = { - .name = "loongson-tpm", + .name = "tpm_loongson", }, }; -module_platform_driver(tpm_loongson_driver); +module_platform_driver(tpm_loongson); -MODULE_ALIAS("platform:loongson-tpm"); +MODULE_ALIAS("platform:tpm_loongson"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Loongson TPM driver"); diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index efd6a855bca3..cf5b5f9a3143 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -813,6 +813,7 @@ config CRYPTO_DEV_CCREE If unsure say Y. source "drivers/crypto/hisilicon/Kconfig" +source "drivers/crypto/loongson/Kconfig" source "drivers/crypto/amlogic/Kconfig" diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index 6ad337bad109..0ce1daed7dc9 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_CRYPTO_DEV_SAFEXCEL) += inside-secure/ obj-$(CONFIG_CRYPTO_DEV_ARTPEC6) += axis/ obj-y += xilinx/ obj-y += hisilicon/ +obj-y += loongson/ obj-$(CONFIG_CRYPTO_DEV_AMLOGIC_GXL) += amlogic/ obj-y += intel/ obj-y += starfive/ diff --git a/drivers/crypto/loongson/Kconfig b/drivers/crypto/loongson/Kconfig new file mode 100644 index 000000000000..115ed745f065 --- /dev/null +++ b/drivers/crypto/loongson/Kconfig @@ -0,0 +1,31 @@ +config CRYPTO_DEV_LOONGSON_HASH + tristate "Support for Loongson hash Driver" + depends on MFD_LOONGSON_SE + select CRYPTO_SM3_GENERIC + help + Support for Loongson hash Driver. + If you want to make Loongson hash support available, say Yes and + it will be accessible from within Linux. To compile this + driver as a module, choose M here; the module will be called + loongson_hash. + +config CRYPTO_DEV_LOONGSON_RNG + tristate "Support for Loongson RNG Driver" + depends on MFD_LOONGSON_SE + help + Support for Loongson RNG Driver. + If you want to make Loongson rng support available, say Yes and + it will be accessible from within Linux. To compile this + driver as a module, choose M here; the module will be called + loongson_rng. + +config CRYPTO_DEV_LOONGSON_SKCIPHER + tristate "Support for Loongson skcipher Driver" + depends on MFD_LOONGSON_SE + select CRYPTO_SM4_GENERIC + help + Support for Loongson skcipher Driver. + If you want to make Loongson hash support available, say Yes and + it will be accessible from within Linux. To compile this + driver as a module, choose M here; the module will be called + loongson_skcipher. diff --git a/drivers/crypto/loongson/Makefile b/drivers/crypto/loongson/Makefile new file mode 100644 index 000000000000..e583c500c250 --- /dev/null +++ b/drivers/crypto/loongson/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_CRYPTO_DEV_LOONGSON_HASH) += loongson-hash.o +obj-$(CONFIG_CRYPTO_DEV_LOONGSON_RNG) += loongson-rng.o +obj-$(CONFIG_CRYPTO_DEV_LOONGSON_SKCIPHER) += loongson-skcipher.o diff --git a/drivers/crypto/loongson/loongson-hash.c b/drivers/crypto/loongson/loongson-hash.c new file mode 100644 index 000000000000..ece990a53cec --- /dev/null +++ b/drivers/crypto/loongson/loongson-hash.c @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <crypto/engine.h> +#include <crypto/hmac.h> +#include <crypto/internal/hash.h> +#include <crypto/scatterwalk.h> +#include <crypto/sm3.h> +#include <linux/err.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mfd/loongson-se.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/scatterlist.h> + +#define LOONGSON_SM3_CTX_SIZE 64 + +#define LOONGSON_HASH_UPDATE 1 +#define LOONGSON_HASH_FINAL 2 + +struct loongson_hash_dev_list { + struct mutex lock; + struct list_head list; + int registered; +}; + +struct loongson_hash_dev { + struct loongson_se_engine *loongson_engine; + struct crypto_engine *crypto_engine; + struct list_head list; + u32 used; +}; + +struct loongson_hash_ctx { + struct loongson_hash_dev *hdev; + u8 sm3_ctx[LOONGSON_SM3_CTX_SIZE]; +}; + +struct loongson_hash_reqctx { + int op; +}; + +struct loongson_hash_cmd { + u32 cmd_id; + union { + u32 len; + u32 ret; + } u; + u32 block_off; + u32 digest_off; + u32 pad[4]; +}; + +static struct loongson_hash_dev_list hash_devices = { + .lock = __MUTEX_INITIALIZER(hash_devices.lock), + .list = LIST_HEAD_INIT(hash_devices.list), +}; + +static int loongson_sm3_init(struct ahash_request *req) +{ + struct loongson_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + + memset(ctx->sm3_ctx, 0, LOONGSON_SM3_CTX_SIZE); + + return 0; +} + +static int loongson_sm3_update(struct ahash_request *req) +{ + struct loongson_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + struct loongson_hash_reqctx *rctx = ahash_request_ctx(req); + + rctx->op = LOONGSON_HASH_UPDATE; + + return crypto_transfer_hash_request_to_engine(ctx->hdev->crypto_engine, req); +} + +static int loongson_sm3_final(struct ahash_request *req) +{ + struct loongson_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + struct loongson_hash_reqctx *rctx = ahash_request_ctx(req); + + rctx->op = LOONGSON_HASH_FINAL; + + return crypto_transfer_hash_request_to_engine(ctx->hdev->crypto_engine, req); +} + +static int loongson_sm3_finup(struct ahash_request *req) +{ + struct loongson_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + struct loongson_hash_reqctx *rctx = ahash_request_ctx(req); + + rctx->op = LOONGSON_HASH_UPDATE | LOONGSON_HASH_FINAL; + + return crypto_transfer_hash_request_to_engine(ctx->hdev->crypto_engine, req); +} + +static int loongson_sm3_do_update(struct ahash_request *req, + struct loongson_hash_ctx *ctx, + struct loongson_hash_reqctx *rctx) +{ + struct loongson_hash_cmd *cmd; + void *dma_buff = ctx->hdev->loongson_engine->data_buffer + LOONGSON_SM3_CTX_SIZE; + u32 dma_buff_size = ctx->hdev->loongson_engine->buffer_size - LOONGSON_SM3_CTX_SIZE; + int err = 0, skip = 0, copyed; + + /* Import */ + memcpy(ctx->hdev->loongson_engine->data_buffer, ctx->sm3_ctx, LOONGSON_SM3_CTX_SIZE); + + while (skip < req->nbytes) { + copyed = sg_pcopy_to_buffer(req->src, sg_nents(req->src), + dma_buff, min(dma_buff_size, req->nbytes), skip); + + cmd = ctx->hdev->loongson_engine->command; + cmd->cmd_id = SE_CMD_HASH | LOONGSON_HASH_UPDATE; + cmd->u.len = copyed; + err = loongson_se_send_engine_cmd(ctx->hdev->loongson_engine); + if (err) + break; + + cmd = ctx->hdev->loongson_engine->command_ret; + if (cmd->u.ret) { + err = -EIO; + break; + } + + skip += copyed; + } + + /* Export */ + memcpy(ctx->sm3_ctx, ctx->hdev->loongson_engine->data_buffer, LOONGSON_SM3_CTX_SIZE); + + return err; +} + +static int loongson_sm3_do_final(struct ahash_request *req, + struct loongson_hash_ctx *ctx, + struct loongson_hash_reqctx *rctx) +{ + struct loongson_hash_cmd *cmd = ctx->hdev->loongson_engine->command; + int err; + + cmd->cmd_id = SE_CMD_HASH | LOONGSON_HASH_FINAL; + cmd->u.len = SM3_DIGEST_SIZE; + err = loongson_se_send_engine_cmd(ctx->hdev->loongson_engine); + if (err) + goto out; + + cmd = ctx->hdev->loongson_engine->command_ret; + if (cmd->u.ret) + err = -EIO; + + memcpy(req->result, ctx->hdev->loongson_engine->data_buffer, SM3_DIGEST_SIZE); + /* Init */ + memset(ctx->sm3_ctx, 0, LOONGSON_SM3_CTX_SIZE); +out: + return err; +} + +static int loongson_sm3_do_one_request(struct crypto_engine *engine, void *areq) +{ + struct ahash_request *req = container_of(areq, struct ahash_request, base); + struct loongson_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + struct loongson_hash_reqctx *rctx = ahash_request_ctx(req); + int err; + + if (rctx->op & LOONGSON_HASH_UPDATE) { + err = loongson_sm3_do_update(req, ctx, rctx); + if (err) + goto out; + } + + if (rctx->op & LOONGSON_HASH_FINAL) + err = loongson_sm3_do_final(req, ctx, rctx); + +out: + crypto_finalize_hash_request(ctx->hdev->crypto_engine, req, err); + + return err; +} + +static int loongson_sm3_export(struct ahash_request *req, void *out) +{ + struct loongson_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + + memcpy(out, ctx->sm3_ctx, LOONGSON_SM3_CTX_SIZE); + + return 0; +} + +static int loongson_sm3_import(struct ahash_request *req, const void *in) +{ + struct loongson_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + + memcpy(ctx->sm3_ctx, in, LOONGSON_SM3_CTX_SIZE); + + return 0; +} + +static int loongson_ahash_init(struct crypto_tfm *tfm) +{ + struct loongson_hash_ctx *ctx = crypto_tfm_ctx(tfm); + struct loongson_hash_dev *hdev; + u32 min_used = U32_MAX; + + mutex_lock(&hash_devices.lock); + list_for_each_entry(hdev, &hash_devices.list, list) { + if (hdev->used < min_used) { + ctx->hdev = hdev; + min_used = hdev->used; + } + } + ctx->hdev->used++; + mutex_unlock(&hash_devices.lock); + + crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), + sizeof(struct loongson_hash_reqctx)); + + return 0; +} + +static void loongson_ahash_exit(struct crypto_tfm *tfm) +{ + struct loongson_hash_ctx *ctx = crypto_tfm_ctx(tfm); + + mutex_lock(&hash_devices.lock); + ctx->hdev->used--; + mutex_unlock(&hash_devices.lock); +} + +static struct ahash_engine_alg loongson_sm3 = { + .base = { + .init = loongson_sm3_init, + .update = loongson_sm3_update, + .final = loongson_sm3_final, + .finup = loongson_sm3_finup, + .digest = loongson_sm3_finup, + .export = loongson_sm3_export, + .import = loongson_sm3_import, + .halg.digestsize = SM3_DIGEST_SIZE, + .halg.statesize = LOONGSON_SM3_CTX_SIZE, + .halg.base = { + .cra_name = "sm3", + .cra_driver_name = "loongson-sm3", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = SM3_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct loongson_hash_ctx), + .cra_module = THIS_MODULE, + .cra_init = loongson_ahash_init, + .cra_exit = loongson_ahash_exit, + }, + }, + .op.do_one_request = loongson_sm3_do_one_request, +}; + +static int loongson_hash_probe(struct platform_device *pdev) +{ + struct loongson_hash_cmd *cmd; + struct loongson_hash_dev *hdev; + int ret = 0; + + hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL); + if (!hdev) + return -ENOMEM; + + hdev->loongson_engine = loongson_se_init_engine(pdev->dev.parent, SE_ENGINE_HASH); + if (!hdev->loongson_engine) + return -ENODEV; + + cmd = hdev->loongson_engine->command; + cmd->digest_off = hdev->loongson_engine->buffer_off; + cmd->block_off = hdev->loongson_engine->buffer_off + LOONGSON_SM3_CTX_SIZE; + + hdev->crypto_engine = crypto_engine_alloc_init(&pdev->dev, 1); + crypto_engine_start(hdev->crypto_engine); + + mutex_lock(&hash_devices.lock); + if (!hash_devices.registered) { + hash_devices.registered = 1; + list_add_tail(&hdev->list, &hash_devices.list); + mutex_unlock(&hash_devices.lock); + + ret = crypto_engine_register_ahash(&loongson_sm3); + if (ret) + dev_err(&pdev->dev, "failed to register crypto(%d)\n", ret); + + return ret; + } + + list_add_tail(&hdev->list, &hash_devices.list); + mutex_unlock(&hash_devices.lock); + + return ret; +} + +static struct platform_driver loongson_hash_driver = { + .probe = loongson_hash_probe, + .driver = { + .name = "loongson-hash", + }, +}; +module_platform_driver(loongson_hash_driver); + +MODULE_ALIAS("platform:loongson-hash"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yinggang Gu <guyinggang@loongson.cn>"); +MODULE_AUTHOR("Qunqin Zhao <zhaoqunqin@loongson.cn>"); +MODULE_DESCRIPTION("Loongson hash acceleration engine driver"); diff --git a/drivers/crypto/loongson/loongson-rng.c b/drivers/crypto/loongson/loongson-rng.c new file mode 100644 index 000000000000..3a4940260f9e --- /dev/null +++ b/drivers/crypto/loongson/loongson-rng.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2019 HiSilicon Limited. */ +/* Copyright (c) 2025 Loongson Technology Corporation Limited. */ + +#include <linux/crypto.h> +#include <linux/err.h> +#include <linux/hw_random.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/mfd/loongson-se.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/random.h> +#include <crypto/internal/rng.h> + +#define SE_SEED_SIZE 32 + +struct loongson_rng_list { + struct mutex lock; + struct list_head list; + int registered; +}; + +struct loongson_rng { + u32 used; + struct loongson_se_engine *engine; + struct list_head list; + struct mutex lock; +}; + +struct loongson_rng_ctx { + struct loongson_rng *rng; +}; + +struct loongson_rng_cmd { + u32 cmd_id; + union { + u32 len; + u32 ret; + } u; + u32 seed_off; + u32 out_off; + u32 pad[4]; +}; + +static struct loongson_rng_list rng_devices = { + .lock = __MUTEX_INITIALIZER(rng_devices.lock), + .list = LIST_HEAD_INIT(rng_devices.list), +}; + +static int loongson_rng_generate(struct crypto_rng *tfm, const u8 *src, + unsigned int slen, u8 *dstn, unsigned int dlen) +{ + struct loongson_rng_ctx *ctx = crypto_rng_ctx(tfm); + struct loongson_rng *rng = ctx->rng; + struct loongson_rng_cmd *cmd = rng->engine->command; + int err, len; + + mutex_lock(&rng->lock); + cmd->seed_off = 0; + do { + len = min(dlen, rng->engine->buffer_size); + cmd = rng->engine->command; + cmd->u.len = len; + err = loongson_se_send_engine_cmd(rng->engine); + if (err) + break; + + cmd = rng->engine->command_ret; + if (cmd->u.ret) { + err = -EIO; + break; + } + + memcpy(dstn, rng->engine->data_buffer, len); + dlen -= len; + dstn += len; + } while (dlen > 0); + mutex_unlock(&rng->lock); + + return err; +} + +static int loongson_rng_init(struct crypto_tfm *tfm) +{ + struct loongson_rng_ctx *ctx = crypto_tfm_ctx(tfm); + struct loongson_rng *rng; + u32 min_used = U32_MAX; + + mutex_lock(&rng_devices.lock); + list_for_each_entry(rng, &rng_devices.list, list) { + if (rng->used < min_used) { + ctx->rng = rng; + min_used = rng->used; + } + } + ctx->rng->used++; + mutex_unlock(&rng_devices.lock); + + return 0; +} + +static void loongson_rng_exit(struct crypto_tfm *tfm) +{ + struct loongson_rng_ctx *ctx = crypto_tfm_ctx(tfm); + + mutex_lock(&rng_devices.lock); + ctx->rng->used--; + mutex_unlock(&rng_devices.lock); +} + +static int loongson_rng_seed(struct crypto_rng *tfm, const u8 *seed, + unsigned int slen) +{ + struct loongson_rng_ctx *ctx = crypto_rng_ctx(tfm); + struct loongson_rng *rng = ctx->rng; + struct loongson_rng_cmd *cmd; + int err; + + if (slen < SE_SEED_SIZE) + return -EINVAL; + + slen = min(slen, rng->engine->buffer_size); + + mutex_lock(&rng->lock); + cmd = rng->engine->command; + cmd->u.len = slen; + cmd->seed_off = rng->engine->buffer_off; + memcpy(rng->engine->data_buffer, seed, slen); + err = loongson_se_send_engine_cmd(rng->engine); + if (err) + goto out; + + cmd = rng->engine->command_ret; + if (cmd->u.ret) + err = -EIO; +out: + mutex_unlock(&rng->lock); + + return err; +} + +static struct rng_alg loongson_rng_alg = { + .generate = loongson_rng_generate, + .seed = loongson_rng_seed, + .seedsize = SE_SEED_SIZE, + .base = { + .cra_name = "stdrng", + .cra_driver_name = "loongson_stdrng", + .cra_priority = 300, + .cra_ctxsize = sizeof(struct loongson_rng_ctx), + .cra_module = THIS_MODULE, + .cra_init = loongson_rng_init, + .cra_exit = loongson_rng_exit, + }, +}; + +static int loongson_rng_probe(struct platform_device *pdev) +{ + struct loongson_rng_cmd *cmd; + struct loongson_rng *rng; + int ret = 0; + + rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); + if (!rng) + return -ENOMEM; + + rng->engine = loongson_se_init_engine(pdev->dev.parent, SE_ENGINE_RNG); + if (!rng->engine) + return -ENODEV; + cmd = rng->engine->command; + cmd->cmd_id = SE_CMD_RNG; + cmd->out_off = rng->engine->buffer_off; + mutex_init(&rng->lock); + + mutex_lock(&rng_devices.lock); + + if (!rng_devices.registered) { + ret = crypto_register_rng(&loongson_rng_alg); + if (ret) { + dev_err(&pdev->dev, "failed to register crypto(%d)\n", ret); + goto out; + } + rng_devices.registered = 1; + } + + list_add_tail(&rng->list, &rng_devices.list); +out: + mutex_unlock(&rng_devices.lock); + + return ret; +} + +static struct platform_driver loongson_rng_driver = { + .probe = loongson_rng_probe, + .driver = { + .name = "loongson-rng", + }, +}; +module_platform_driver(loongson_rng_driver); + +MODULE_ALIAS("platform:loongson-rng"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yinggang Gu <guyinggang@loongson.cn>"); +MODULE_AUTHOR("Qunqin Zhao <zhaoqunqin@loongson.cn>"); +MODULE_DESCRIPTION("Loongson Random Number Generator driver"); diff --git a/drivers/crypto/loongson/loongson-skcipher.c b/drivers/crypto/loongson/loongson-skcipher.c new file mode 100644 index 000000000000..c7b6fad4e67f --- /dev/null +++ b/drivers/crypto/loongson/loongson-skcipher.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <crypto/engine.h> +#include <crypto/hmac.h> +#include <crypto/internal/skcipher.h> +#include <crypto/scatterwalk.h> +#include <crypto/sm4.h> +#include <linux/err.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mfd/loongson-se.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/scatterlist.h> + +#define LOONGSON_SM4_CTX_SIZE 64 + +#define LOONGSON_SKCIPHER_ENCRYPT 0 +#define LOONGSON_SKCIPHER_DECRYPT 1 + +struct loongson_skcipher_dev_list { + struct mutex lock; + struct list_head list; + int registered; +}; + +struct loongson_skcipher_dev { + struct loongson_se_engine *loongson_engine; + struct crypto_engine *crypto_engine; + struct list_head list; + u8 sm4_ctx[LOONGSON_SM4_CTX_SIZE]; + u32 used; +}; + +struct loongson_skcipher_ctx { + struct loongson_skcipher_dev *sdev; +}; + +struct loongson_skcipher_reqctx { + int op; +}; + +struct loongson_skcipher_cmd { + u32 cmd_id; + union { + u32 len; + u32 ret; + } u; + u32 in_off; + u32 out_off; + u32 key_off; + u32 pad[3]; +}; + +static struct loongson_skcipher_dev_list skcipher_devices = { + .lock = __MUTEX_INITIALIZER(skcipher_devices.lock), + .list = LIST_HEAD_INIT(skcipher_devices.list), +}; + +static int loongson_sm4_setkey(struct crypto_skcipher *tfm, const u8 *key, + unsigned int keylen) +{ + struct loongson_skcipher_ctx *ctx = crypto_skcipher_ctx(tfm); + + if (keylen != SM4_KEY_SIZE) + return -EINVAL; + + memcpy(ctx->sdev->sm4_ctx, key, keylen); + + return 0; +} + +static int loongson_sm4_encrypt(struct skcipher_request *req) +{ + struct loongson_skcipher_ctx *ctx = crypto_skcipher_ctx(crypto_skcipher_reqtfm(req)); + struct loongson_skcipher_reqctx *rctx = skcipher_request_ctx(req); + + if (req->cryptlen % SM4_BLOCK_SIZE) + return -EINVAL; + + rctx->op = LOONGSON_SKCIPHER_ENCRYPT; + + return crypto_transfer_skcipher_request_to_engine(ctx->sdev->crypto_engine, req); +} + +static int loongson_sm4_decrypt(struct skcipher_request *req) +{ + struct loongson_skcipher_ctx *ctx = crypto_skcipher_ctx(crypto_skcipher_reqtfm(req)); + struct loongson_skcipher_reqctx *rctx = skcipher_request_ctx(req); + + if (req->cryptlen % SM4_BLOCK_SIZE) + return -EINVAL; + + rctx->op = LOONGSON_SKCIPHER_DECRYPT; + + return crypto_transfer_skcipher_request_to_engine(ctx->sdev->crypto_engine, req); +} + +static int loongson_sm4_do_one_request(struct crypto_engine *engine, void *areq) +{ + struct skcipher_request *req = container_of(areq, struct skcipher_request, base); + struct loongson_skcipher_ctx *ctx = crypto_skcipher_ctx(crypto_skcipher_reqtfm(req)); + struct loongson_skcipher_reqctx *rctx = skcipher_request_ctx(req); + void *dma_buff = ctx->sdev->loongson_engine->data_buffer + LOONGSON_SM4_CTX_SIZE; + u32 dma_buff_size = ctx->sdev->loongson_engine->buffer_size - LOONGSON_SM4_CTX_SIZE; + struct loongson_skcipher_cmd *cmd; + int err = 0, skip = 0, copyed; + + memcpy(ctx->sdev->loongson_engine->data_buffer, ctx->sdev->sm4_ctx, LOONGSON_SM4_CTX_SIZE); + + while (skip < req->cryptlen) { + copyed = sg_pcopy_to_buffer(req->src, sg_nents(req->src), + dma_buff, min(dma_buff_size, req->cryptlen), skip); + + cmd = ctx->sdev->loongson_engine->command; + cmd->cmd_id = SE_CMD_SKCIPHER | rctx->op; + cmd->u.len = ALIGN(copyed, LOONGSON_SM4_CTX_SIZE); + err = loongson_se_send_engine_cmd(ctx->sdev->loongson_engine); + if (err) + break; + + cmd = ctx->sdev->loongson_engine->command_ret; + if (cmd->u.ret) { + err = -EIO; + break; + } + + sg_pcopy_from_buffer(req->dst, sg_nents(req->dst), + dma_buff, min(dma_buff_size, req->cryptlen), skip); + + skip += copyed; + } + + crypto_finalize_skcipher_request(ctx->sdev->crypto_engine, req, err); + + return err; +} + +static int loongson_skcipher_init(struct crypto_tfm *tfm) +{ + struct loongson_skcipher_ctx *ctx = crypto_tfm_ctx(tfm); + struct loongson_skcipher_dev *sdev; + u32 min_used = U32_MAX; + + mutex_lock(&skcipher_devices.lock); + list_for_each_entry(sdev, &skcipher_devices.list, list) { + if (sdev->used < min_used) { + ctx->sdev = sdev; + min_used = sdev->used; + } + } + ctx->sdev->used++; + mutex_unlock(&skcipher_devices.lock); + + crypto_skcipher_set_reqsize(__crypto_skcipher_cast(tfm), + sizeof(struct loongson_skcipher_reqctx)); + + return 0; +} + +static void loongson_skcipher_exit(struct crypto_tfm *tfm) +{ + struct loongson_skcipher_ctx *ctx = crypto_tfm_ctx(tfm); + + mutex_lock(&skcipher_devices.lock); + ctx->sdev->used--; + mutex_unlock(&skcipher_devices.lock); +} + +static struct skcipher_engine_alg loongson_sm4 = { + .base = { + .min_keysize = SM4_KEY_SIZE, + .max_keysize = SM4_KEY_SIZE, + .setkey = loongson_sm4_setkey, + .encrypt = loongson_sm4_encrypt, + .decrypt = loongson_sm4_decrypt, + .base = { + .cra_name = "ecb(sm4)", + .cra_driver_name = "loongson-ecb(sm4)", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = SM4_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct loongson_skcipher_ctx), + .cra_module = THIS_MODULE, + .cra_init = loongson_skcipher_init, + .cra_exit = loongson_skcipher_exit, + }, + }, + .op.do_one_request = loongson_sm4_do_one_request, +}; + +static int loongson_skcipher_probe(struct platform_device *pdev) +{ + struct loongson_skcipher_cmd *cmd; + struct loongson_skcipher_dev *sdev; + int ret = 0; + + sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL); + if (!sdev) + return -ENOMEM; + + sdev->loongson_engine = loongson_se_init_engine(pdev->dev.parent, SE_ENGINE_SKCIPHER); + if (!sdev->loongson_engine) + return -ENODEV; + + cmd = sdev->loongson_engine->command; + cmd->key_off = sdev->loongson_engine->buffer_off; + cmd->in_off = sdev->loongson_engine->buffer_off + LOONGSON_SM4_CTX_SIZE; + cmd->out_off = cmd->in_off; + + sdev->crypto_engine = crypto_engine_alloc_init(&pdev->dev, 1); + crypto_engine_start(sdev->crypto_engine); + + mutex_lock(&skcipher_devices.lock); + if (!skcipher_devices.registered) { + skcipher_devices.registered = 1; + list_add_tail(&sdev->list, &skcipher_devices.list); + mutex_unlock(&skcipher_devices.lock); + + ret = crypto_engine_register_skcipher(&loongson_sm4); + if (ret) + dev_err(&pdev->dev, "failed to register crypto(%d)\n", ret); + + return ret; + } + + list_add_tail(&sdev->list, &skcipher_devices.list); + mutex_unlock(&skcipher_devices.lock); + + return ret; +} + +static struct platform_driver loongson_skcipher_driver = { + .probe = loongson_skcipher_probe, + .driver = { + .name = "loongson-skcipher", + }, +}; +module_platform_driver(loongson_skcipher_driver); + +MODULE_ALIAS("platform:loongson-skcipher"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yinggang Gu <guyinggang@loongson.cn>"); +MODULE_AUTHOR("Qunqin Zhao <zhaoqunqin@loongson.cn>"); +MODULE_DESCRIPTION("Loongson skcipher acceleration engine driver"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 96633e8d4a9c..f5206b9a256a 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2321,6 +2321,17 @@ config MFD_INTEL_M10_BMC_PMCI additional drivers must be enabled in order to use the functionality of the device. +config MFD_LOONGSON_SE + tristate "Loongson Security Engine chip controller driver" + depends on LOONGARCH && ACPI + select MFD_CORE + help + The Loongson Security Engine chip supports RNG, SM2, SM3 and + SM4 accelerator engines. Each engine have its own DMA buffer + provided by the controller. The kernel cannot directly send + commands to the engine and must first send them to the controller, + which will forward them to the corresponding engine. + config MFD_RSMU_I2C tristate "Renesas Synchronization Management Unit with I2C" depends on I2C && OF diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 95867f732d2e..7787e84209d8 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -285,3 +285,5 @@ obj-$(CONFIG_MFD_ATC260X_I2C) += atc260x-i2c.o obj-$(CONFIG_MFD_RSMU_I2C) += rsmu_i2c.o rsmu_core.o obj-$(CONFIG_MFD_RSMU_SPI) += rsmu_spi.o rsmu_core.o + +obj-$(CONFIG_MFD_LOONGSON_SE) += loongson-se.o diff --git a/drivers/mfd/loongson-se.c b/drivers/mfd/loongson-se.c new file mode 100644 index 000000000000..2692a4e93a3a --- /dev/null +++ b/drivers/mfd/loongson-se.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Loongson Technology Corporation Limited + * + * Author: Yinggang Gu <guyinggang@loongson.cn> + * Author: Qunqin Zhao <zhaoqunqin@loongson.cn> + */ + +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/mfd/core.h> +#include <linux/mfd/loongson-se.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +struct loongson_se { + void __iomem *base; + spinlock_t dev_lock; + struct completion cmd_completion; + + void *dmam_base; + int dmam_size; + + struct mutex engine_init_lock; + struct loongson_se_engine engines[SE_ENGINE_MAX]; +}; + +struct loongson_se_controller_cmd { + u32 command_id; + u32 info[7]; +}; + +static int loongson_se_poll(struct loongson_se *se, u32 int_bit) +{ + u32 status; + int err; + + spin_lock_irq(&se->dev_lock); + + /* Notify the controller that the engine needs to be started */ + writel(int_bit, se->base + SE_L2SINT_SET); + + /* Polling until the controller has forwarded the engine command */ + err = readl_relaxed_poll_timeout_atomic(se->base + SE_L2SINT_STAT, status, + !(status & int_bit), + 1, LOONGSON_ENGINE_CMD_TIMEOUT_US); + + spin_unlock_irq(&se->dev_lock); + + return err; +} + +static int loongson_se_send_controller_cmd(struct loongson_se *se, + struct loongson_se_controller_cmd *cmd) +{ + u32 *send_cmd = (u32 *)cmd; + int err, i; + + for (i = 0; i < SE_SEND_CMD_REG_LEN; i++) + writel(send_cmd[i], se->base + SE_SEND_CMD_REG + i * 4); + + err = loongson_se_poll(se, SE_INT_CONTROLLER); + if (err) + return err; + + return wait_for_completion_interruptible(&se->cmd_completion); +} + +int loongson_se_send_engine_cmd(struct loongson_se_engine *engine) +{ + /* + * After engine initialization, the controller already knows + * where to obtain engine commands from. Now all we need to + * do is notify the controller that the engine needs to be started. + */ + int err = loongson_se_poll(engine->se, BIT(engine->id)); + + if (err) + return err; + + return wait_for_completion_interruptible(&engine->completion); +} +EXPORT_SYMBOL_GPL(loongson_se_send_engine_cmd); + +struct loongson_se_engine *loongson_se_init_engine(struct device *dev, int id) +{ + struct loongson_se *se = dev_get_drvdata(dev); + struct loongson_se_engine *engine = &se->engines[id]; + struct loongson_se_controller_cmd cmd; + + engine->se = se; + engine->id = id; + init_completion(&engine->completion); + + /* Divide DMA memory equally among all engines */ + engine->buffer_size = se->dmam_size / SE_ENGINE_MAX; + engine->buffer_off = (se->dmam_size / SE_ENGINE_MAX) * id; + engine->data_buffer = se->dmam_base + engine->buffer_off; + + /* + * There has no engine0, use its data buffer as command buffer for other + * engines. The DMA memory size is obtained from the ACPI table, which + * ensures that the data buffer size of engine0 is larger than the + * command buffer size of all engines. + */ + engine->command = se->dmam_base + id * (2 * SE_ENGINE_CMD_SIZE); + engine->command_ret = engine->command + SE_ENGINE_CMD_SIZE; + + mutex_lock(&se->engine_init_lock); + + /* Tell the controller where to find engine command */ + cmd.command_id = SE_CMD_SET_ENGINE_CMDBUF; + cmd.info[0] = id; + cmd.info[1] = engine->command - se->dmam_base; + cmd.info[2] = 2 * SE_ENGINE_CMD_SIZE; + + if (loongson_se_send_controller_cmd(se, &cmd)) + engine = NULL; + + mutex_unlock(&se->engine_init_lock); + + return engine; +} +EXPORT_SYMBOL_GPL(loongson_se_init_engine); + +static irqreturn_t se_irq_handler(int irq, void *dev_id) +{ + struct loongson_se *se = dev_id; + u32 int_status; + int id; + + spin_lock(&se->dev_lock); + + int_status = readl(se->base + SE_S2LINT_STAT); + + + /* For controller */ + if (int_status & SE_INT_CONTROLLER) { + complete(&se->cmd_completion); + int_status &= ~SE_INT_CONTROLLER; + writel(SE_INT_CONTROLLER, se->base + SE_S2LINT_CL); + } + + /* For engines */ + while (int_status) { + id = __ffs(int_status); + complete(&se->engines[id].completion); + int_status &= ~BIT(id); + writel(BIT(id), se->base + SE_S2LINT_CL); + } + + spin_unlock(&se->dev_lock); + + return IRQ_HANDLED; +} + +static int loongson_se_init(struct loongson_se *se, dma_addr_t addr, int size) +{ + struct loongson_se_controller_cmd cmd; + int err; + + cmd.command_id = SE_CMD_START; + err = loongson_se_send_controller_cmd(se, &cmd); + if (err) + return err; + + cmd.command_id = SE_CMD_SET_DMA; + cmd.info[0] = lower_32_bits(addr); + cmd.info[1] = upper_32_bits(addr); + cmd.info[2] = size; + + return loongson_se_send_controller_cmd(se, &cmd); +} + +static const struct mfd_cell engines[] = { + { .name = "loongson-hash" }, + { .name = "loongson-rng" }, + { .name = "loongson-skcipher" }, + { .name = "tpm_loongson" }, +}; + +static int loongson_se_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct loongson_se *se; + int nr_irq, irq, err, i; + dma_addr_t paddr; + + se = devm_kmalloc(dev, sizeof(*se), GFP_KERNEL); + if (!se) + return -ENOMEM; + + dev_set_drvdata(dev, se); + init_completion(&se->cmd_completion); + spin_lock_init(&se->dev_lock); + mutex_init(&se->engine_init_lock); + + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (device_property_read_u32(dev, "dmam_size", &se->dmam_size)) + return -ENODEV; + + se->dmam_base = dmam_alloc_coherent(dev, se->dmam_size, &paddr, GFP_KERNEL); + if (!se->dmam_base) + return -ENOMEM; + + se->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(se->base)) + return PTR_ERR(se->base); + + writel(SE_INT_ALL, se->base + SE_S2LINT_EN); + + nr_irq = platform_irq_count(pdev); + if (nr_irq <= 0) + return -ENODEV; + + for (i = 0; i < nr_irq; i++) { + irq = platform_get_irq(pdev, i); + err = devm_request_irq(dev, irq, se_irq_handler, 0, "loongson-se", se); + if (err) + dev_err(dev, "failed to request IRQ: %d\n", irq); + } + + err = loongson_se_init(se, paddr, se->dmam_size); + if (err) + return err; + + return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, engines, + ARRAY_SIZE(engines), NULL, 0, NULL); +} + +static const struct acpi_device_id loongson_se_acpi_match[] = { + { "LOON0011", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, loongson_se_acpi_match); + +static struct platform_driver loongson_se_driver = { + .probe = loongson_se_probe, + .driver = { + .name = "loongson-se", + .acpi_match_table = loongson_se_acpi_match, + }, +}; +module_platform_driver(loongson_se_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yinggang Gu <guyinggang@loongson.cn>"); +MODULE_AUTHOR("Qunqin Zhao <zhaoqunqin@loongson.cn>"); +MODULE_DESCRIPTION("Loongson Security Engine chip controller driver"); diff --git a/include/linux/mfd/loongson-se.h b/include/linux/mfd/loongson-se.h new file mode 100644 index 000000000000..48c69aa60dd4 --- /dev/null +++ b/include/linux/mfd/loongson-se.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (C) 2025 Loongson Technology Corporation Limited */ + +#ifndef __MFD_LOONGSON_SE_H__ +#define __MFD_LOONGSON_SE_H__ + +#define LOONGSON_ENGINE_CMD_TIMEOUT_US 10000 +#define SE_SEND_CMD_REG 0x0 +#define SE_SEND_CMD_REG_LEN 0x8 +/* Controller command ID */ +#define SE_CMD_START 0x0 +#define SE_CMD_SET_DMA 0x3 +#define SE_CMD_SET_ENGINE_CMDBUF 0x4 + +#define SE_S2LINT_STAT 0x88 +#define SE_S2LINT_EN 0x8c +#define SE_S2LINT_CL 0x94 +#define SE_L2SINT_STAT 0x98 +#define SE_L2SINT_SET 0xa0 + +#define SE_INT_ALL 0xffffffff +#define SE_INT_CONTROLLER BIT(0) + +#define SE_ENGINE_MAX 16 +#define SE_ENGINE_RNG 1 +#define SE_CMD_RNG 0x100 + +#define SE_ENGINE_HASH 3 +#define SE_CMD_HASH 0x300 + +#define SE_ENGINE_SKCIPHER 4 +#define SE_CMD_SKCIPHER 0x400 + +#define SE_ENGINE_TPM 5 +#define SE_CMD_TPM 0x500 + +#define SE_ENGINE_CMD_SIZE 32 + +struct loongson_se_engine { + struct loongson_se *se; + int id; + + /* Command buffer */ + void *command; + void *command_ret; + + void *data_buffer; + uint buffer_size; + /* Data buffer offset to DMA base */ + uint buffer_off; + + struct completion completion; + +}; + +struct loongson_se_engine *loongson_se_init_engine(struct device *dev, int id); +int loongson_se_send_engine_cmd(struct loongson_se_engine *engine); + +#endif -- 2.33.0