diff --git a/MAINTAINERS b/MAINTAINERS index 9872276959714462d011271d0ea4a45604ec35a1..2ad91e4ef85a6ce746e660c3d012684f33287499 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7343,6 +7343,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 diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index ea7ace87a9df59ce8ee88dfa25ffd3b9c4d955d5..1d2957bc100d696bdfdf3f8422cba037b65a581b 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 853f7e10cd418686cdd9411f466e1573ff86b6b9..c9bd51adc792fddf69dfa8ef46bed1d3d91f5cfd 100644 --- a/drivers/char/loongson_se.c +++ b/drivers/char/loongson_se.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -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 301284e07603bb353c3c04d1128293f325b5d479..3524b2e6086e574999d042b6e2c04b7ba09b7005 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 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..3c27d86b77ba07d8bdfde7da5df07f22783436aa --- /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 +#include +#include +#include + +#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 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;