diff --git a/MAINTAINERS b/MAINTAINERS index 61baf2cfc4e195c1e96a902d0a7034a09d574824..1b4bcc4e6744e8f67f3719a6d3280ff99cc5bb5d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7345,6 +7345,11 @@ F: Documentation/networking/net_dim.rst F: include/linux/dim.h F: lib/dim/ +LOONGSON TPM DRIVER +M: Qunqin Zhao +S: Maintained +F: drivers/char/tpm/tpm_loongson.c + DYNAMIC THERMAL POWER MANAGEMENT (DTPM) M: Daniel Lezcano L: linux-pm@vger.kernel.org @@ -12518,6 +12523,12 @@ S: Maintained F: Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml F: drivers/thermal/loongson2_thermal.c +LOONGSON EDAC DRIVER +M: Zhao Qunqin +L: linux-edac@vger.kernel.org +S: Maintained +F: drivers/edac/loongson_edac.c + LSILOGIC MPT FUSION DRIVERS (FC/SAS/SPI) M: Sathya Prakash M: Sreekanth Reddy diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 8739e15c137b491c536b69f7bd481bf7986a4d95..1a29cbc79f3105db231452004d6b3194ea55fccc 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -69,6 +69,7 @@ config LOONGARCH select BUILDTIME_TABLE_SORT select COMMON_CLK select CPU_PM + select EDAC_SUPPORT select EFI select GENERIC_CLOCKEVENTS select GENERIC_CMOS_UPDATE diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index 3d4c03c6b9acb0c2a39603933c292e3bf2c9d12d..3318fc5a3425a3a5a9ba09e2399aa8e85daeb9c7 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -1148,6 +1148,7 @@ CONFIG_TCG_TIS_I2C_INFINEON=m CONFIG_TCG_TIS_I2C_NUVOTON=m CONFIG_TCG_ATMEL=m CONFIG_TCG_INFINEON=m +CONFIG_TCG_LOONGSON=m CONFIG_TCG_TIS_ST33ZP24_I2C=m CONFIG_TCG_TIS_ST33ZP24_SPI=m CONFIG_I2C_CHARDEV=y @@ -1307,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 @@ -1848,6 +1850,8 @@ CONFIG_INFINIBAND_SRP=m CONFIG_INFINIBAND_SRPT=m CONFIG_INFINIBAND_ISER=m CONFIG_INFINIBAND_ISERT=m +CONFIG_EDAC=y +CONFIG_EDAC_LOONGSON=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_DS1307=m CONFIG_RTC_DRV_DS1374=m @@ -2188,6 +2192,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 ea7ace87a9df59ce8ee88dfa25ffd3b9c4d955d5..1548c7582df4466dee906d4afdd3a735b9eb772f 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -391,19 +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 - 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 109af71c54169d206a4d4665b697535f8351a091..1242ca2ddd8185b3965fb4f770c330d28d204fd3 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 853f7e10cd418686cdd9411f466e1573ff86b6b9..0000000000000000000000000000000000000000 --- a/drivers/char/loongson_se.c +++ /dev/null @@ -1,523 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2024 Loongson Technology Corporation Limited - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * 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; - - se = ch->se; - int_bit = ch->int_bit; - if ((se_readl(se, SE_L2SINT_STAT) & int_bit) || - !(se_readl(se, SE_L2SINT_EN) & int_bit)) - return -EBUSY; - - se_enable_int(se, int_bit); - se_writel(se, int_bit, SE_L2SINT_SET); - - return readl_relaxed_poll_timeout_atomic(se->base + SE_L2SINT_STAT, status, - !(status & int_bit), 10, 10000); - -} -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; - unsigned long flag; - int data_first, data_nr; - int msg_first, msg_nr; - - 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; - } - - spin_lock_irqsave(&se->dev_lock, flag); - - 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"); - spin_unlock_irqrestore(&se->dev_lock, flag); - 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); - spin_unlock_irqrestore(&se->dev_lock, flag); - 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); - - spin_unlock_irqrestore(&se->dev_lock, flag); - - if (loongson_se_set_msg(ch)) { - dev_err(se->dev, "Channel %d setup message address failed\n", id); - return NULL; - } - - se_enable_int(se, ch->int_bit); - - 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 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); - 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; -} - -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 3bbe3cbb35aea55aef9e8cb9cfd614aa9b28bf38..7ffccede03a418a1dcd11149a51cecc5b308f63f 100644 --- a/drivers/char/lsse_sdf_cdev.c +++ b/drivers/char/lsse_sdf_cdev.c @@ -16,20 +16,17 @@ #include #include #include -#include +#include +#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 301284e07603bb353c3c04d1128293f325b5d479..a8046d1a17a1809ec344af07e842a0ead94b6886 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -174,6 +174,15 @@ config TCG_IBMVTPM will be accessible from within Linux. To compile this driver as a module, choose M here; the module will be called tpm_ibmvtpm. +config TCG_LOONGSON + tristate "Loongson TPM Interface" + 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 + driver as a module, choose M here; the module will be called + tpm_loongson. + config TCG_XEN tristate "XEN TPM Interface" depends on TCG_TPM && XEN diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 8f868c9b9ce78f53ec54620bec947f099fd13624..ea2919ddbd6e18e28f3b80abb160fbdb37d5984d 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -44,3 +44,4 @@ obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o obj-$(CONFIG_TCG_FTPM_TEE) += tpm_ftpm_tee.o obj-$(CONFIG_TCG_HYGON) += tpm_hygon.o obj-$(CONFIG_TCM_HYGON) += tcm_hygon.o +obj-$(CONFIG_TCG_LOONGSON) += tpm_loongson.o diff --git a/drivers/char/tpm/tpm_loongson.c b/drivers/char/tpm/tpm_loongson.c new file mode 100644 index 0000000000000000000000000000000000000000..a4ec23639911ceb55dbe88b23365c066985858ee --- /dev/null +++ b/drivers/char/tpm/tpm_loongson.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Loongson Technology Corporation Limited. */ + +#include +#include +#include +#include + +#include "tpm.h" + +struct tpm_loongson_cmd { + u32 cmd_id; + u32 data_off; + u32 data_len; + u32 pad[5]; +}; + +static int tpm_loongson_recv(struct tpm_chip *chip, u8 *buf, size_t count) +{ + struct loongson_se_engine *tpm_engine = dev_get_drvdata(&chip->dev); + struct tpm_loongson_cmd *cmd_ret = tpm_engine->command_ret; + + if (cmd_ret->data_len > count) + return -EIO; + + memcpy(buf, tpm_engine->data_buffer, cmd_ret->data_len); + + return cmd_ret->data_len; +} + +static int tpm_loongson_send(struct tpm_chip *chip, u8 *buf, size_t count) +{ + struct loongson_se_engine *tpm_engine = dev_get_drvdata(&chip->dev); + struct tpm_loongson_cmd *cmd = tpm_engine->command; + + if (count > tpm_engine->buffer_size) + return -E2BIG; + + 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 = { + .flags = TPM_OPS_AUTO_STARTUP, + .recv = tpm_loongson_recv, + .send = tpm_loongson_send, +}; + +static int tpm_loongson_probe(struct platform_device *pdev) +{ + struct loongson_se_engine *tpm_engine; + struct device *dev = &pdev->dev; + struct tpm_loongson_cmd *cmd; + struct tpm_chip *chip; + + tpm_engine = loongson_se_init_engine(dev->parent, SE_ENGINE_TPM); + if (!tpm_engine) + return -ENODEV; + 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, tpm_engine); + + return tpm_chip_register(chip); +} + +static struct platform_driver tpm_loongson = { + .probe = tpm_loongson_probe, + .driver = { + .name = "tpm_loongson", + }, +}; +module_platform_driver(tpm_loongson); + +MODULE_ALIAS("platform:tpm_loongson"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Loongson TPM driver"); diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index efd6a855bca3004de1fbf92cb049418ba5575e0c..cf5b5f9a314312bfa85ee7ea31969887d968a8c1 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 6ad337bad10991fbdb6e4a29c96e71d2597564e8..0ce1daed7dc9d4237215dc387e4cb0f91b436d92 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 0000000000000000000000000000000000000000..115ed745f065e842cc824681c17c4c46ebf3458e --- /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 0000000000000000000000000000000000000000..e583c500c250bcdf82a8374edf147d1a4cbc8aa2 --- /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 0000000000000000000000000000000000000000..ece990a53cec7465ee80e5f8ab1d5672a985d639 --- /dev/null +++ b/drivers/crypto/loongson/loongson-hash.c @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_AUTHOR("Qunqin Zhao "); +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 0000000000000000000000000000000000000000..3a4940260f9e591d465878b8d4dc804e9c032172 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_AUTHOR("Qunqin Zhao "); +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 0000000000000000000000000000000000000000..c7b6fad4e67fa84119fe9edc468267b55e21a334 --- /dev/null +++ b/drivers/crypto/loongson/loongson-skcipher.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_AUTHOR("Qunqin Zhao "); +MODULE_DESCRIPTION("Loongson skcipher acceleration engine driver"); diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 87feeb24b3ff2c82db1e5b4e1a53b4810efa7344..2094459a64e9fd372b3a7e01b0688f23826e0277 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -562,4 +562,13 @@ config EDAC_NPCM error detection (in-line ECC in which a section 1/8th of the memory device used to store data is used for ECC storage). +config EDAC_LOONGSON + tristate "Loongson Memory Controller" + depends on LOONGARCH && ACPI + help + Support for error detection and correction on the Loongson + family memory controller. This driver reports single bit + errors (CE) only. Loongson-3A5000/3C5000/3D5000/3A6000/3C6000 + are compatible. + endif # EDAC diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 446364264e2b18944c9558c1836625585560e2eb..699b818ac7cbbd17b837bf341642db37a9adb358 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -88,3 +88,4 @@ obj-$(CONFIG_EDAC_BLUEFIELD) += bluefield_edac.o obj-$(CONFIG_EDAC_DMC520) += dmc520_edac.o obj-$(CONFIG_EDAC_NPCM) += npcm_edac.o obj-$(CONFIG_EDAC_ZYNQMP) += zynqmp_edac.o +obj-$(CONFIG_EDAC_LOONGSON) += loongson_edac.o diff --git a/drivers/edac/loongson_edac.c b/drivers/edac/loongson_edac.c new file mode 100644 index 0000000000000000000000000000000000000000..46d6a3ab2ce2ed0b00814cbe1e751b3bcf1aa841 --- /dev/null +++ b/drivers/edac/loongson_edac.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2024 Loongson Technology Corporation Limited. + */ + +#include +#include +#include +#include +#include +#include +#include "edac_module.h" + +#define ECC_CS_COUNT_REG 0x18 + +struct loongson_edac_pvt { + void __iomem *ecc_base; + int last_ce_count; +}; + +static int read_ecc(struct mem_ctl_info *mci) +{ + struct loongson_edac_pvt *pvt = mci->pvt_info; + u64 ecc; + int cs; + + ecc = readq(pvt->ecc_base + ECC_CS_COUNT_REG); + /* cs0 -- cs3 */ + cs = ecc & 0xff; + cs += (ecc >> 8) & 0xff; + cs += (ecc >> 16) & 0xff; + cs += (ecc >> 24) & 0xff; + + return cs; +} + +static void edac_check(struct mem_ctl_info *mci) +{ + struct loongson_edac_pvt *pvt = mci->pvt_info; + int new, add; + + new = read_ecc(mci); + add = new - pvt->last_ce_count; + pvt->last_ce_count = new; + if (add <= 0) + return; + + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add, + 0, 0, 0, 0, 0, -1, "error", ""); +} + +static void dimm_config_init(struct mem_ctl_info *mci) +{ + struct dimm_info *dimm; + u32 size, npages; + + /* size not used */ + size = -1; + npages = MiB_TO_PAGES(size); + + dimm = edac_get_dimm(mci, 0, 0, 0); + dimm->nr_pages = npages; + snprintf(dimm->label, sizeof(dimm->label), + "MC#%uChannel#%u_DIMM#%u", mci->mc_idx, 0, 0); + dimm->grain = 8; +} + +static void pvt_init(struct mem_ctl_info *mci, void __iomem *vbase) +{ + struct loongson_edac_pvt *pvt = mci->pvt_info; + + pvt->ecc_base = vbase; + pvt->last_ce_count = read_ecc(mci); +} + +static int edac_probe(struct platform_device *pdev) +{ + struct edac_mc_layer layers[2]; + struct mem_ctl_info *mci; + void __iomem *vbase; + int ret; + + vbase = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(vbase)) + return PTR_ERR(vbase); + + layers[0].type = EDAC_MC_LAYER_CHANNEL; + layers[0].size = 1; + layers[0].is_virt_csrow = false; + layers[1].type = EDAC_MC_LAYER_SLOT; + layers[1].size = 1; + layers[1].is_virt_csrow = true; + mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, + sizeof(struct loongson_edac_pvt)); + if (mci == NULL) + return -ENOMEM; + + mci->mc_idx = edac_device_alloc_index(); + mci->mtype_cap = MEM_FLAG_RDDR4; + mci->edac_ctl_cap = EDAC_FLAG_NONE; + mci->edac_cap = EDAC_FLAG_NONE; + mci->mod_name = "loongson_edac.c"; + mci->ctl_name = "loongson_edac_ctl"; + mci->dev_name = "loongson_edac_dev"; + mci->ctl_page_to_phys = NULL; + mci->pdev = &pdev->dev; + mci->error_desc.grain = 8; + mci->edac_check = edac_check; + + pvt_init(mci, vbase); + dimm_config_init(mci); + + ret = edac_mc_add_mc(mci); + if (ret) { + edac_dbg(0, "MC: failed edac_mc_add_mc()\n"); + edac_mc_free(mci); + return ret; + } + edac_op_state = EDAC_OPSTATE_POLL; + + return 0; +} + +static int edac_remove(struct platform_device *pdev) +{ + struct mem_ctl_info *mci = edac_mc_del_mc(&pdev->dev); + + if (mci) + edac_mc_free(mci); + + return 0; +} + +static const struct acpi_device_id loongson_edac_acpi_match[] = { + {"LOON0010", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, loongson_edac_acpi_match); + +static struct platform_driver loongson_edac_driver = { + .probe = edac_probe, + .remove = edac_remove, + .driver = { + .name = "loongson-mc-edac", + .acpi_match_table = loongson_edac_acpi_match, + }, +}; +module_platform_driver(loongson_edac_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Zhao Qunqin "); +MODULE_DESCRIPTION("EDAC driver for loongson memory controller"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 96633e8d4a9cd3a4ace98cd5f362049f442cfd28..f5206b9a256a154ea872748159df2a06bda334a6 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 95867f732d2e99784979f73133af738671e632a9..7787e84209d82ccf9b2c4b44ba67138b3c2872db 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 0000000000000000000000000000000000000000..2692a4e93a3aab837250f2b41b1c00a5075c34b5 --- /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 + * Author: Qunqin Zhao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 "); +MODULE_AUTHOR("Qunqin Zhao "); +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 0000000000000000000000000000000000000000..48c69aa60dd4cce784f611f2f2f4a5a364933579 --- /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 diff --git a/include/soc/loongson/se.h b/include/soc/loongson/se.h index d9864e3f3b85ae350a72257fb6b11b2c6e9c4ac6..22872f7d1a198781e1652e4d6d59e9be915095c5 100644 --- a/include/soc/loongson/se.h +++ b/include/soc/loongson/se.h @@ -109,6 +109,7 @@ struct loongson_se { void __iomem *base; u32 version; u32 ch_status; + struct mutex ch_init_lock; spinlock_t cmd_lock; spinlock_t dev_lock;