diff mbox series

[V2,RESEND] soc: loongson: add Loongson Security Module driver

Message ID 20241224072500.15669-1-zhaoqunqin@loongson.cn (mailing list archive)
State New
Headers show
Series [V2,RESEND] soc: loongson: add Loongson Security Module driver | expand

Commit Message

Zhao Qunqin Dec. 24, 2024, 7:25 a.m. UTC
This driver supports Loongson Security Module, which
provides the control for it's hardware encryption
acceleration child devices.

Only ACPI firmware is supported.

Signed-off-by: Qunqin Zhao <zhaoqunqin@loongson.cn>
---
Changes in v2:
	- Removed useless memset() in probe.
	- Cleaned up coding style.
	- Corrected the spelling.

 MAINTAINERS                        |   7 +
 drivers/soc/loongson/Kconfig       |   9 +
 drivers/soc/loongson/Makefile      |   1 +
 drivers/soc/loongson/loongson_se.c | 523 +++++++++++++++++++++++++++++
 include/soc/loongson/se.h          | 135 ++++++++
 5 files changed, 675 insertions(+)
 create mode 100644 drivers/soc/loongson/loongson_se.c
 create mode 100644 include/soc/loongson/se.h


base-commit: daa20223dee942ebea45bc72b517480af226c370

Comments

Krzysztof Kozlowski Dec. 28, 2024, 11:02 a.m. UTC | #1
On 24/12/2024 08:25, Qunqin Zhao wrote:
> This driver supports Loongson Security Module, which
> provides the control for it's hardware encryption
> acceleration child devices.

Please wrap commit message according to Linux coding style / submission
process (neither too early nor over the limit):
https://elixir.bootlin.com/linux/v6.4-rc1/source/Documentation/process/submitting-patches.rst#L597

> 
> Only ACPI firmware is supported.
> 
> Signed-off-by: Qunqin Zhao <zhaoqunqin@loongson.cn>

I don't get why you are sending this to soc. This is loongson specific
and is supposed to go via Loongson maintainers.

And why is this a resend?

> ---
> Changes in v2:
> 	- Removed useless memset() in probe.
> 	- Cleaned up coding style.
> 	- Corrected the spelling.
> 
>  MAINTAINERS                        |   7 +
>  drivers/soc/loongson/Kconfig       |   9 +
>  drivers/soc/loongson/Makefile      |   1 +
>  drivers/soc/loongson/loongson_se.c | 523 +++++++++++++++++++++++++++++
>  include/soc/loongson/se.h          | 135 ++++++++


include/linux/soc/... or just keep it private to the driver. Why this
has to be exposed to other users?

>  5 files changed, 675 insertions(+)
>  create mode 100644 drivers/soc/loongson/loongson_se.c
>  create mode 100644 include/soc/loongson/se.h
> 

...

> +
> +/*
> + * 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);

No, no users. You cannot export unused symbols.

> +
> +/*
> + * 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);

No, no users. You cannot export unused symbols.

> +
> +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);

No, no users. You cannot export unused symbols.

Best regards,
Krzysztof
Zhao Qunqin Jan. 3, 2025, 2:25 a.m. UTC | #2
在 2024/12/28 下午7:02, Krzysztof Kozlowski 写道:
> On 24/12/2024 08:25, Qunqin Zhao wrote:
>> This driver supports Loongson Security Module, which
>> provides the control for it's hardware encryption
>> acceleration child devices.
> Please wrap commit message according to Linux coding style / submission
> process (neither too early nor over the limit):
> https://elixir.bootlin.com/linux/v6.4-rc1/source/Documentation/process/submitting-patches.rst#L597
OK
>> Only ACPI firmware is supported.
>>
>> Signed-off-by: Qunqin Zhao <zhaoqunqin@loongson.cn>
> I don't get why you are sending this to soc. This is loongson specific
> and is supposed to go via Loongson maintainers.
>
> And why is this a resend?

There are no platform-specific maintainers, So I initially sent the 
patch to Arnd,

but didn't receive a response from Arnold, so I resent the patch and added

soc@kernel.org to the mailing list.

>> ---
>> Changes in v2:
>> 	- Removed useless memset() in probe.
>> 	- Cleaned up coding style.
>> 	- Corrected the spelling.
>>
>>   MAINTAINERS                        |   7 +
>>   drivers/soc/loongson/Kconfig       |   9 +
>>   drivers/soc/loongson/Makefile      |   1 +
>>   drivers/soc/loongson/loongson_se.c | 523 +++++++++++++++++++++++++++++
>>   include/soc/loongson/se.h          | 135 ++++++++
>
> include/linux/soc/... or just keep it private to the driver. Why this
> has to be exposed to other users?

Looks like  include/linux/soc... is more suitable than include/soc/...

This driver is the base of other users,  one user will be added in next 
revision.

And may I ask which one do you think is more suitable to place this 
basic driver under SOC or MFD ?


Thanks.

> Best regards,
> Krzysztof
Krzysztof Kozlowski Jan. 3, 2025, 7:26 a.m. UTC | #3
On 03/01/2025 03:25, Zhao Qunqin wrote:
> 
> 在 2024/12/28 下午7:02, Krzysztof Kozlowski 写道:
>> On 24/12/2024 08:25, Qunqin Zhao wrote:
>>> This driver supports Loongson Security Module, which
>>> provides the control for it's hardware encryption
>>> acceleration child devices.
>> Please wrap commit message according to Linux coding style / submission
>> process (neither too early nor over the limit):
>> https://elixir.bootlin.com/linux/v6.4-rc1/source/Documentation/process/submitting-patches.rst#L597
> OK
>>> Only ACPI firmware is supported.
>>>
>>> Signed-off-by: Qunqin Zhao <zhaoqunqin@loongson.cn>
>> I don't get why you are sending this to soc. This is loongson specific
>> and is supposed to go via Loongson maintainers.
>>
>> And why is this a resend?
> 
> There are no platform-specific maintainers, So I initially sent the 
> patch to Arnd,

What do you mean by "no platform-specific maintainers"? This
architecture and this platform is abandoned?

Then we should either drop it from the kernel or make it orphaned.


> 
> but didn't receive a response from Arnold, so I resent the patch and added
> 
> soc@kernel.org to the mailing list.

You shouldn't. This is patch for your maintainers to take. If you do not
have them: either fix that or abandon/orphan the platform.


> 
>>> ---
>>> Changes in v2:
>>> 	- Removed useless memset() in probe.
>>> 	- Cleaned up coding style.
>>> 	- Corrected the spelling.
>>>
>>>   MAINTAINERS                        |   7 +
>>>   drivers/soc/loongson/Kconfig       |   9 +
>>>   drivers/soc/loongson/Makefile      |   1 +
>>>   drivers/soc/loongson/loongson_se.c | 523 +++++++++++++++++++++++++++++
>>>   include/soc/loongson/se.h          | 135 ++++++++
>>
>> include/linux/soc/... or just keep it private to the driver. Why this
>> has to be exposed to other users?
> 
> Looks like  include/linux/soc... is more suitable than include/soc/...
> 
> This driver is the base of other users,  one user will be added in next 
> revision.

No. Keep it private to the driver.

Soc driver is usually not a "base for other users".

> 
> And may I ask which one do you think is more suitable to place this 
> basic driver under SOC or MFD ?

I was talking about the header.

Best regards,
Krzysztof
Zhao Qunqin Jan. 3, 2025, 8:35 a.m. UTC | #4
在 2025/1/3 下午3:26, Krzysztof Kozlowski 写道:
> On 03/01/2025 03:25, Zhao Qunqin wrote:
>> 在 2024/12/28 下午7:02, Krzysztof Kozlowski 写道:
>>>> Only ACPI firmware is supported.
>>>>
>>>> Signed-off-by: Qunqin Zhao <zhaoqunqin@loongson.cn>
>>> I don't get why you are sending this to soc. This is loongson specific
>>> and is supposed to go via Loongson maintainers.
>>>
>>> And why is this a resend?
>> There are no platform-specific maintainers, So I initially sent the
>> patch to Arnd,
> What do you mean by "no platform-specific maintainers"? This
> architecture and this platform is abandoned?

Sorry for my poor english. This architecture and this  platform is not 
ababdoned.

What I mean is that there are no Loongson staff to maintain 
drivers/soc/loongson.

The current maintainer  for drivers/soc/loongson is Arnd.


Thanks for your response.

> Then we should either drop it from the kernel or make it orphaned.
>
>
>> but didn't receive a response from Arnold, so I resent the patch and added
>>
>> soc@kernel.org to the mailing list.
> You shouldn't. This is patch for your maintainers to take. If you do not
> have them: either fix that or abandon/orphan the platform.
>
>
> Best regards,
> Krzysztof
Krzysztof Kozlowski Jan. 3, 2025, 8:46 a.m. UTC | #5
On 03/01/2025 09:35, Zhao Qunqin wrote:
> 
> 在 2025/1/3 下午3:26, Krzysztof Kozlowski 写道:
>> On 03/01/2025 03:25, Zhao Qunqin wrote:
>>> 在 2024/12/28 下午7:02, Krzysztof Kozlowski 写道:
>>>>> Only ACPI firmware is supported.
>>>>>
>>>>> Signed-off-by: Qunqin Zhao <zhaoqunqin@loongson.cn>
>>>> I don't get why you are sending this to soc. This is loongson specific
>>>> and is supposed to go via Loongson maintainers.
>>>>
>>>> And why is this a resend?
>>> There are no platform-specific maintainers, So I initially sent the
>>> patch to Arnd,
>> What do you mean by "no platform-specific maintainers"? This
>> architecture and this platform is abandoned?
> 
> Sorry for my poor english. This architecture and this  platform is not 
> ababdoned.
> 
> What I mean is that there are no Loongson staff to maintain 
> drivers/soc/loongson.

But there are maintainers of the platform, are they? Someone cares about
Loongson or not?

If someone cares, then Cc this person and this person is responsible for
handling your patches. This is called "maintainer".

If no one cares, then platform is abandoned and we should consider
calling it orphaned in Linux.

> 
> The current maintainer  for drivers/soc/loongson is Arnd.

I don't think so. First, it is unusual. Second, maintainers disagree
with you on above statement.

You keep disagreeing here with me and I am afraid we will end up in
endless discussion, so to reiterate because maybe I wrote my emails too
soft or too conditional, instead of stating the desired outcome:
Please send your Loongson SoC drivers to Loongson maintainers and they
are supposed to take it. If they do not take it, I will send patch for
making the platform orphaned.

Best regards,
Krzysztof
Krzysztof Kozlowski Jan. 3, 2025, 8:47 a.m. UTC | #6
On 03/01/2025 09:46, Krzysztof Kozlowski wrote:
> On 03/01/2025 09:35, Zhao Qunqin wrote:
>>
>> 在 2025/1/3 下午3:26, Krzysztof Kozlowski 写道:
>>> On 03/01/2025 03:25, Zhao Qunqin wrote:
>>>> 在 2024/12/28 下午7:02, Krzysztof Kozlowski 写道:
>>>>>> Only ACPI firmware is supported.
>>>>>>
>>>>>> Signed-off-by: Qunqin Zhao <zhaoqunqin@loongson.cn>
>>>>> I don't get why you are sending this to soc. This is loongson specific
>>>>> and is supposed to go via Loongson maintainers.
>>>>>
>>>>> And why is this a resend?
>>>> There are no platform-specific maintainers, So I initially sent the
>>>> patch to Arnd,
>>> What do you mean by "no platform-specific maintainers"? This
>>> architecture and this platform is abandoned?
>>
>> Sorry for my poor english. This architecture and this  platform is not 
>> ababdoned.
>>
>> What I mean is that there are no Loongson staff to maintain 
>> drivers/soc/loongson.
> 
> But there are maintainers of the platform, are they? Someone cares about
> Loongson or not?
> 
> If someone cares, then Cc this person and this person is responsible for
> handling your patches. This is called "maintainer".
> 
> If no one cares, then platform is abandoned and we should consider
> calling it orphaned in Linux.
> 
>>
>> The current maintainer  for drivers/soc/loongson is Arnd.
> 
> I don't think so. First, it is unusual. Second, maintainers disagree

^^^ By "maintainers" I meant here the "MAINTAINERS" file, so
scripts/get_maintainers.pl.

> with you on above statement.
> 
> You keep disagreeing here with me and I am afraid we will end up in
> endless discussion, so to reiterate because maybe I wrote my emails too
> soft or too conditional, instead of stating the desired outcome:
> Please send your Loongson SoC drivers to Loongson maintainers and they
> are supposed to take it. If they do not take it, I will send patch for
> making the platform orphaned.


Best regards,
Krzysztof
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index fdeb3d12c..85fff2eb7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13379,6 +13379,13 @@  S:	Maintained
 F:	Documentation/devicetree/bindings/hwinfo/loongson,ls2k-chipid.yaml
 F:	drivers/soc/loongson/loongson2_guts.c
 
+LOONGSON SECURITY MODULE DRIVER
+M:	Qunqin Zhao <zhaoqunqin@loongson.cn>
+L:	loongarch@lists.linux.dev
+S:	Maintained
+F:	drivers/soc/loongson/loongson_se.c
+F:	include/soc/loongson/se.h
+
 LOONGSON-2 SOC SERIES PM DRIVER
 M:	Yinbo Zhu <zhuyinbo@loongson.cn>
 L:	linux-pm@vger.kernel.org
diff --git a/drivers/soc/loongson/Kconfig b/drivers/soc/loongson/Kconfig
index 368344943..20a5d90c3 100644
--- a/drivers/soc/loongson/Kconfig
+++ b/drivers/soc/loongson/Kconfig
@@ -27,3 +27,12 @@  config LOONGSON2_PM
 	  Disk), ACPI S5 (Soft Shutdown) and supports multiple wake-up methods
 	  (USB, GMAC, PWRBTN, etc.). This driver was to add power management
 	  controller support that base on dts for Loongson-2 series SoCs.
+
+config LOONGSON_SE
+	tristate "Loongson Security Module Interface"
+	depends on LOONGARCH && ACPI
+	help
+	  The Loongson security module provides the control for hardware
+	  encryption acceleration devices. Each device uses at least one
+	  channel to interact with security module, and each channel may
+	  have its own buffer provided by security module.
diff --git a/drivers/soc/loongson/Makefile b/drivers/soc/loongson/Makefile
index 4118f50f5..503075042 100644
--- a/drivers/soc/loongson/Makefile
+++ b/drivers/soc/loongson/Makefile
@@ -5,3 +5,4 @@ 
 
 obj-$(CONFIG_LOONGSON2_GUTS)		+= loongson2_guts.o
 obj-$(CONFIG_LOONGSON2_PM)		+= loongson2_pm.o
+obj-$(CONFIG_LOONGSON_SE)		+= loongson_se.o
diff --git a/drivers/soc/loongson/loongson_se.c b/drivers/soc/loongson/loongson_se.c
new file mode 100644
index 000000000..442593a64
--- /dev/null
+++ b/drivers/soc/loongson/loongson_se.c
@@ -0,0 +1,523 @@ 
+// 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/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      |
+ *        +-----------------------------------+
+ *             ^                ^
+ *    channel0 |       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/include/soc/loongson/se.h b/include/soc/loongson/se.h
new file mode 100644
index 000000000..825d5b294
--- /dev/null
+++ b/include/soc/loongson/se.h
@@ -0,0 +1,135 @@ 
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (C) 2024 Loongson Technology Corporation Limited */
+
+#ifndef __LOONGSON_SE_H__
+#define __LOONGSON_SE_H__
+
+#define SE_DATA_S			0x0
+#define SE_DATA_L			0x20
+#define SE_S2LINT_STAT			0x88
+#define SE_S2LINT_EN			0x8c
+#define SE_S2LINT_SET			0x90
+#define SE_S2LINT_CL			0x94
+#define SE_L2SINT_STAT			0x98
+#define SE_L2SINT_EN			0x9c
+#define SE_L2SINT_SET			0xa0
+#define SE_L2SINT_CL			0xa4
+
+/* INT bit definition */
+#define SE_INT_SETUP			BIT(0)
+#define SE_INT_SM2			BIT(0)
+#define SE_INT_SM3			BIT(0)
+#define SE_INT_SM4			BIT(0)
+#define SE_INT_RNG			BIT(0)
+#define SE_INT_TPM			BIT(5)
+#define SE_INT_ALL			0xffffffff
+
+#define SE_CMD_START			0x0
+#define SE_CMD_STOP			0x1
+#define SE_CMD_GETVER			0x2
+#define SE_CMD_SETBUF			0x3
+#define SE_CMD_SETMSG			0x4
+
+#define SE_CMD_RNG			0x100
+
+#define SE_CMD_SM2_SIGN			0x200
+#define SE_CMD_SM2_VSIGN		0x201
+
+#define SE_CMD_SM3_DIGEST		0x300
+#define SE_CMD_SM3_UPDATE		0x301
+#define SE_CMD_SM3_FINISH		0x302
+
+#define SE_CMD_SM4_ECB_ENCRY		0x400
+#define SE_CMD_SM4_ECB_DECRY		0x401
+#define SE_CMD_SM4_CBC_ENCRY		0x402
+#define SE_CMD_SM4_CBC_DECRY		0x403
+#define SE_CMD_SM4_CTR			0x404
+
+#define SE_CMD_TPM			0x500
+#define SE_CMD_ZUC_INIT_READ		0x600
+#define SE_CMD_ZUC_READ			0x601
+
+#define SE_CMD_SDF			0x700
+
+#define SE_CH_MAX			32
+
+#define SE_CH_RNG			1
+#define SE_CH_SM2			2
+#define SE_CH_SM3			3
+#define SE_CH_SM4			4
+#define SE_CH_TPM			5
+#define SE_CH_ZUC			6
+#define SE_CH_SDF			7
+
+struct se_msg {
+	u32 cmd;
+	u32 data_off;
+	u32 data_len;
+	u32 info[5];
+};
+
+struct se_cmd {
+	u32 cmd;
+	u32 info[7];
+};
+
+struct se_res {
+	u32 cmd;
+	u32 cmd_ret;
+	u32 info[6];
+};
+
+struct se_data {
+	u32 int_bit;
+	union {
+		u32 data[8];
+		struct se_cmd gcmd;
+		struct se_res res;
+	} u;
+};
+
+struct lsse_ch {
+	u32 id;
+	u32 int_bit;
+	struct loongson_se *se;
+	void *priv;
+	spinlock_t ch_lock;
+	void *smsg;
+	void *rmsg;
+	int msg_size;
+	void *data_buffer;
+	dma_addr_t data_addr;
+	int data_size;
+
+	void (*complete)(struct lsse_ch *se_ch);
+};
+
+struct loongson_se {
+	struct device *dev;
+	void __iomem *base;
+	u32 version;
+	u32 ch_status;
+	spinlock_t cmd_lock;
+	spinlock_t dev_lock;
+
+	/* Interaction memory */
+	void *mem_base;
+	dma_addr_t mem_addr;
+	unsigned long *mem_map;
+	int mem_map_pages;
+	void *smsg;
+	void *rmsg;
+
+	/* Synchronous CMD */
+	struct completion cmd_completion;
+
+	/* Channel */
+	struct lsse_ch chs[SE_CH_MAX];
+};
+
+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));
+void se_deinit_ch(struct lsse_ch *ch);
+int se_send_ch_requeset(struct lsse_ch *ch);
+
+#endif