[PATCH OLK-6.6 0/3] LoongArch: add TPM/SM3/SM4/EDAC support

Qunqin Zhao (3): tpm: Add a driver for Loongson TPM device crypto: loongson: add loongson sm3 and ecb(sm4) support EDAC: Add an EDAC driver for the Loongson memory controller MAINTAINERS | 11 + arch/loongarch/Kconfig | 1 + arch/loongarch/configs/loongson3_defconfig | 7 + drivers/char/Kconfig | 12 +- drivers/char/Makefile | 1 - drivers/char/loongson_se.c | 523 -------------------- drivers/char/lsse_sdf_cdev.c | 95 ++-- drivers/char/tpm/Kconfig | 9 + drivers/char/tpm/Makefile | 1 + drivers/char/tpm/tpm_loongson.c | 84 ++++ 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/edac/Kconfig | 9 + drivers/edac/Makefile | 1 + drivers/edac/loongson_edac.c | 152 ++++++ drivers/mfd/Kconfig | 11 + drivers/mfd/Makefile | 2 + drivers/mfd/loongson-se.c | 256 ++++++++++ include/linux/mfd/loongson-se.h | 59 +++ include/soc/loongson/se.h | 1 + 25 files changed, 1439 insertions(+), 601 deletions(-) delete mode 100644 drivers/char/loongson_se.c create mode 100644 drivers/char/tpm/tpm_loongson.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/edac/loongson_edac.c create mode 100644 drivers/mfd/loongson-se.c create mode 100644 include/linux/mfd/loongson-se.h -- 2.33.0

From: Qunqin Zhao <zhaoqunqin@loongson.cn> LoongArch inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ICATLF CVE: NA -------------------------------- Loongson security engine supports random number generation, hash, symmetric encryption and asymmetric encryption. Based on these encryption functions, TPM2 have been implemented in the Loongson security engine firmware. This driver is responsible for copying data into the memory visible to the firmware and receiving data from the firmware. Co-developed-by: Yinggang Gu <guyinggang@loongson.cn> Signed-off-by: Yinggang Gu <guyinggang@loongson.cn> Signed-off-by: Qunqin Zhao <zhaoqunqin@loongson.cn> Change-Id: I029740a822afd797d211a868b36e5ba8dd3b6b6a Signed-off-by: Hongchen Zhang <zhanghongchen@loongson.cn> --- MAINTAINERS | 5 + arch/loongarch/configs/loongson3_defconfig | 1 + drivers/char/Kconfig | 1 + drivers/char/loongson_se.c | 33 +++++-- drivers/char/tpm/Kconfig | 9 ++ drivers/char/tpm/Makefile | 1 + drivers/char/tpm/tpm_loongson.c | 103 +++++++++++++++++++++ include/soc/loongson/se.h | 1 + 8 files changed, 144 insertions(+), 10 deletions(-) create mode 100644 drivers/char/tpm/tpm_loongson.c diff --git a/MAINTAINERS b/MAINTAINERS index 61baf2cfc4e1..e46c80e9c550 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 <zhaoqunqin@loongson.cn> +S: Maintained +F: drivers/char/tpm/tpm_loongson.c + DYNAMIC THERMAL POWER MANAGEMENT (DTPM) M: Daniel Lezcano <daniel.lezcano@kernel.org> L: linux-pm@vger.kernel.org diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index 3d4c03c6b9ac..0d78f6912fe9 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 diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index ea7ace87a9df..1d2957bc100d 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -394,6 +394,7 @@ config UV_MMTIMER 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 diff --git a/drivers/char/loongson_se.c b/drivers/char/loongson_se.c index 853f7e10cd41..c9bd51adc792 100644 --- a/drivers/char/loongson_se.c +++ b/drivers/char/loongson_se.c @@ -14,6 +14,7 @@ #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> @@ -309,19 +310,25 @@ 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)) + !(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); - return readl_relaxed_poll_timeout_atomic(se->base + SE_L2SINT_STAT, status, + 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); @@ -337,10 +344,10 @@ struct lsse_ch *se_init_ch(struct device *dev, int id, int data_size, int msg_si { 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; + mutex_lock(&se->ch_init_lock); if (!se) { pr_err("SE has bot been initialized\n"); return NULL; @@ -356,8 +363,6 @@ struct lsse_ch *se_init_ch(struct device *dev, int id, int data_size, int msg_si return NULL; } - spin_lock_irqsave(&se->dev_lock, flag); - ch = &se->chs[id]; ch->se = se; ch->id = id; @@ -369,7 +374,7 @@ struct lsse_ch *se_init_ch(struct device *dev, int id, int data_size, int msg_si 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); + mutex_unlock(&se->ch_init_lock); return NULL; } @@ -384,7 +389,7 @@ struct lsse_ch *se_init_ch(struct device *dev, int id, int data_size, int msg_si 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); + mutex_unlock(&se->ch_init_lock); return NULL; } @@ -396,14 +401,15 @@ struct lsse_ch *se_init_ch(struct device *dev, int id, int data_size, int msg_si 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); + mutex_unlock(&se->ch_init_lock); return NULL; } se_enable_int(se, ch->int_bit); + mutex_unlock(&se->ch_init_lock); return ch; } @@ -448,6 +454,10 @@ void se_deinit_ch(struct lsse_ch *ch) } 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; @@ -460,6 +470,7 @@ static int loongson_se_probe(struct platform_device *pdev) 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 */ @@ -497,10 +508,12 @@ static int loongson_se_probe(struct platform_device *pdev) } err = se_init_hw(se, se->mem_addr, size); - if (err) + if (err) { se_disable_hw(se); + return err; + } - 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[] = { diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 301284e07603..3524b2e6086e 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 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 8f868c9b9ce7..ea2919ddbd6e 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 000000000000..3c27d86b77ba --- /dev/null +++ b/drivers/char/tpm/tpm_loongson.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Loongson Technology Corporation Limited. */ + +#include <linux/device.h> +#include <soc/loongson/se.h> +#include <linux/platform_device.h> +#include <linux/wait.h> + +#include "tpm.h" + +struct tpm_loongson_msg { + u32 cmd; + u32 data_off; + u32 data_len; + u32 info[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; + + sig = wait_for_completion_interruptible(&td->tpm_loongson_completion); + if (sig) + return sig; + + rmsg = td->se_ch->rmsg; + memcpy(buf, td->se_ch->data_buffer, rmsg->data_len); + + return rmsg->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; + + memcpy(td->se_ch->data_buffer, buf, count); + smsg->data_len = count; + + return se_send_ch_requeset(td->se_ch); +} + +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 device *dev = &pdev->dev; + struct tpm_loongson_msg *smsg; + struct tpm_loongson_dev *td; + 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) + 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; + + 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); + + return tpm_chip_register(chip); +} + +static struct platform_driver tpm_loongson_driver = { + .probe = tpm_loongson_probe, + .driver = { + .name = "loongson-tpm", + }, +}; +module_platform_driver(tpm_loongson_driver); + +MODULE_ALIAS("platform:loongson-tpm"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Loongson TPM driver"); diff --git a/include/soc/loongson/se.h b/include/soc/loongson/se.h index d9864e3f3b85..22872f7d1a19 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; -- 2.33.0

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

From: Qunqin Zhao <zhaoqunqin@loongson.cn> mainline inclusion from mainline-v6.14-rc1 commit 558aff7a63f67dc4723a4deed419a2dfd0fb14f2 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ICOFL4 CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- [ Upstream commit 558aff7a63f67dc4723a4deed419a2dfd0fb14f2 ] Add ECC support for Loongson SoC DDR controller. This driver reports single bit errors (CE) only. Only ACPI firmware is supported. Signed-off-by: Qunqin Zhao <zhaoqunqin@loongson.cn> Link: https://lore.kernel.org/r/20241219124846.1876-1-zhaoqunqin@loongson.cn Change-Id: Ic6e6c5f8623e6513ef30c75fb122df1e00f02845 Signed-off-by: Hongchen Zhang <zhanghongchen@loongson.cn> --- MAINTAINERS | 6 + arch/loongarch/Kconfig | 1 + arch/loongarch/configs/loongson3_defconfig | 2 + drivers/edac/Kconfig | 9 ++ drivers/edac/Makefile | 1 + drivers/edac/loongson_edac.c | 152 +++++++++++++++++++++ 6 files changed, 171 insertions(+) create mode 100644 drivers/edac/loongson_edac.c diff --git a/MAINTAINERS b/MAINTAINERS index e46c80e9c550..1b4bcc4e6744 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12523,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 <zhaoqunqin@loongson.cn> +L: linux-edac@vger.kernel.org +S: Maintained +F: drivers/edac/loongson_edac.c + LSILOGIC MPT FUSION DRIVERS (FC/SAS/SPI) M: Sathya Prakash <sathya.prakash@broadcom.com> M: Sreekanth Reddy <sreekanth.reddy@broadcom.com> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 8739e15c137b..1a29cbc79f31 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 aa8f4cc99e97..3318fc5a3425 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -1850,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 diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 87feeb24b3ff..2094459a64e9 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 446364264e2b..699b818ac7cb 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 000000000000..46d6a3ab2ce2 --- /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 <linux/acpi.h> +#include <linux/edac.h> +#include <linux/init.h> +#include <linux/io-64-nonatomic-lo-hi.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#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 <zhaoqunqin@loongson.cn>"); +MODULE_DESCRIPTION("EDAC driver for loongson memory controller"); -- 2.33.0

反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://gitee.com/openeuler/kernel/pulls/17265 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/6YX... FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://gitee.com/openeuler/kernel/pulls/17265 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/6YX...
participants (2)
-
Hongchen Zhang
-
patchwork bot