xref: /linux/drivers/net/ethernet/airoha/airoha_npu.c (revision b81e0f2b58be37628b2e12f8dffdd63c84573e75)
123290c7bSLorenzo Bianconi // SPDX-License-Identifier: GPL-2.0-only
223290c7bSLorenzo Bianconi /*
323290c7bSLorenzo Bianconi  * Copyright (c) 2025 AIROHA Inc
423290c7bSLorenzo Bianconi  * Author: Lorenzo Bianconi <lorenzo@kernel.org>
523290c7bSLorenzo Bianconi  */
623290c7bSLorenzo Bianconi 
723290c7bSLorenzo Bianconi #include <linux/devcoredump.h>
823290c7bSLorenzo Bianconi #include <linux/firmware.h>
923290c7bSLorenzo Bianconi #include <linux/platform_device.h>
1023290c7bSLorenzo Bianconi #include <linux/of_net.h>
1123290c7bSLorenzo Bianconi #include <linux/of_platform.h>
1223290c7bSLorenzo Bianconi #include <linux/of_reserved_mem.h>
1323290c7bSLorenzo Bianconi #include <linux/regmap.h>
1423290c7bSLorenzo Bianconi 
15*b81e0f2bSLorenzo Bianconi #include "airoha_eth.h"
1623290c7bSLorenzo Bianconi #include "airoha_npu.h"
1723290c7bSLorenzo Bianconi 
1823290c7bSLorenzo Bianconi #define NPU_EN7581_FIRMWARE_DATA		"airoha/en7581_npu_data.bin"
1923290c7bSLorenzo Bianconi #define NPU_EN7581_FIRMWARE_RV32		"airoha/en7581_npu_rv32.bin"
2023290c7bSLorenzo Bianconi #define NPU_EN7581_FIRMWARE_RV32_MAX_SIZE	0x200000
2123290c7bSLorenzo Bianconi #define NPU_EN7581_FIRMWARE_DATA_MAX_SIZE	0x10000
2223290c7bSLorenzo Bianconi #define NPU_DUMP_SIZE				512
2323290c7bSLorenzo Bianconi 
2423290c7bSLorenzo Bianconi #define REG_NPU_LOCAL_SRAM		0x0
2523290c7bSLorenzo Bianconi 
2623290c7bSLorenzo Bianconi #define NPU_PC_BASE_ADDR		0x305000
2723290c7bSLorenzo Bianconi #define REG_PC_DBG(_n)			(0x305000 + ((_n) * 0x100))
2823290c7bSLorenzo Bianconi 
2923290c7bSLorenzo Bianconi #define NPU_CLUSTER_BASE_ADDR		0x306000
3023290c7bSLorenzo Bianconi 
3123290c7bSLorenzo Bianconi #define REG_CR_BOOT_TRIGGER		(NPU_CLUSTER_BASE_ADDR + 0x000)
3223290c7bSLorenzo Bianconi #define REG_CR_BOOT_CONFIG		(NPU_CLUSTER_BASE_ADDR + 0x004)
3323290c7bSLorenzo Bianconi #define REG_CR_BOOT_BASE(_n)		(NPU_CLUSTER_BASE_ADDR + 0x020 + ((_n) << 2))
3423290c7bSLorenzo Bianconi 
3523290c7bSLorenzo Bianconi #define NPU_MBOX_BASE_ADDR		0x30c000
3623290c7bSLorenzo Bianconi 
3723290c7bSLorenzo Bianconi #define REG_CR_MBOX_INT_STATUS		(NPU_MBOX_BASE_ADDR + 0x000)
3823290c7bSLorenzo Bianconi #define MBOX_INT_STATUS_MASK		BIT(8)
3923290c7bSLorenzo Bianconi 
4023290c7bSLorenzo Bianconi #define REG_CR_MBOX_INT_MASK(_n)	(NPU_MBOX_BASE_ADDR + 0x004 + ((_n) << 2))
4123290c7bSLorenzo Bianconi #define REG_CR_MBQ0_CTRL(_n)		(NPU_MBOX_BASE_ADDR + 0x030 + ((_n) << 2))
4223290c7bSLorenzo Bianconi #define REG_CR_MBQ8_CTRL(_n)		(NPU_MBOX_BASE_ADDR + 0x0b0 + ((_n) << 2))
4323290c7bSLorenzo Bianconi #define REG_CR_NPU_MIB(_n)		(NPU_MBOX_BASE_ADDR + 0x140 + ((_n) << 2))
4423290c7bSLorenzo Bianconi 
4523290c7bSLorenzo Bianconi #define NPU_TIMER_BASE_ADDR		0x310100
4623290c7bSLorenzo Bianconi #define REG_WDT_TIMER_CTRL(_n)		(NPU_TIMER_BASE_ADDR + ((_n) * 0x100))
4723290c7bSLorenzo Bianconi #define WDT_EN_MASK			BIT(25)
4823290c7bSLorenzo Bianconi #define WDT_INTR_MASK			BIT(21)
4923290c7bSLorenzo Bianconi 
5023290c7bSLorenzo Bianconi enum {
5123290c7bSLorenzo Bianconi 	NPU_OP_SET = 1,
5223290c7bSLorenzo Bianconi 	NPU_OP_SET_NO_WAIT,
5323290c7bSLorenzo Bianconi 	NPU_OP_GET,
5423290c7bSLorenzo Bianconi 	NPU_OP_GET_NO_WAIT,
5523290c7bSLorenzo Bianconi };
5623290c7bSLorenzo Bianconi 
5723290c7bSLorenzo Bianconi enum {
5823290c7bSLorenzo Bianconi 	NPU_FUNC_WIFI,
5923290c7bSLorenzo Bianconi 	NPU_FUNC_TUNNEL,
6023290c7bSLorenzo Bianconi 	NPU_FUNC_NOTIFY,
6123290c7bSLorenzo Bianconi 	NPU_FUNC_DBA,
6223290c7bSLorenzo Bianconi 	NPU_FUNC_TR471,
6323290c7bSLorenzo Bianconi 	NPU_FUNC_PPE,
6423290c7bSLorenzo Bianconi };
6523290c7bSLorenzo Bianconi 
6623290c7bSLorenzo Bianconi enum {
6723290c7bSLorenzo Bianconi 	NPU_MBOX_ERROR,
6823290c7bSLorenzo Bianconi 	NPU_MBOX_SUCCESS,
6923290c7bSLorenzo Bianconi };
7023290c7bSLorenzo Bianconi 
7123290c7bSLorenzo Bianconi enum {
7223290c7bSLorenzo Bianconi 	PPE_FUNC_SET_WAIT,
7323290c7bSLorenzo Bianconi 	PPE_FUNC_SET_WAIT_HWNAT_INIT,
7423290c7bSLorenzo Bianconi 	PPE_FUNC_SET_WAIT_HWNAT_DEINIT,
7523290c7bSLorenzo Bianconi 	PPE_FUNC_SET_WAIT_API,
76*b81e0f2bSLorenzo Bianconi 	PPE_FUNC_SET_WAIT_FLOW_STATS_SETUP,
7723290c7bSLorenzo Bianconi };
7823290c7bSLorenzo Bianconi 
7923290c7bSLorenzo Bianconi enum {
8023290c7bSLorenzo Bianconi 	PPE2_SRAM_SET_ENTRY,
8123290c7bSLorenzo Bianconi 	PPE_SRAM_SET_ENTRY,
8223290c7bSLorenzo Bianconi 	PPE_SRAM_SET_VAL,
8323290c7bSLorenzo Bianconi 	PPE_SRAM_RESET_VAL,
8423290c7bSLorenzo Bianconi };
8523290c7bSLorenzo Bianconi 
8623290c7bSLorenzo Bianconi enum {
8723290c7bSLorenzo Bianconi 	QDMA_WAN_ETHER = 1,
8823290c7bSLorenzo Bianconi 	QDMA_WAN_PON_XDSL,
8923290c7bSLorenzo Bianconi };
9023290c7bSLorenzo Bianconi 
9123290c7bSLorenzo Bianconi #define MBOX_MSG_FUNC_ID	GENMASK(14, 11)
9223290c7bSLorenzo Bianconi #define MBOX_MSG_STATIC_BUF	BIT(5)
9323290c7bSLorenzo Bianconi #define MBOX_MSG_STATUS		GENMASK(4, 2)
9423290c7bSLorenzo Bianconi #define MBOX_MSG_DONE		BIT(1)
9523290c7bSLorenzo Bianconi #define MBOX_MSG_WAIT_RSP	BIT(0)
9623290c7bSLorenzo Bianconi 
9723290c7bSLorenzo Bianconi #define PPE_TYPE_L2B_IPV4	2
9823290c7bSLorenzo Bianconi #define PPE_TYPE_L2B_IPV4_IPV6	3
9923290c7bSLorenzo Bianconi 
10023290c7bSLorenzo Bianconi struct ppe_mbox_data {
10123290c7bSLorenzo Bianconi 	u32 func_type;
10223290c7bSLorenzo Bianconi 	u32 func_id;
10323290c7bSLorenzo Bianconi 	union {
10423290c7bSLorenzo Bianconi 		struct {
10523290c7bSLorenzo Bianconi 			u8 cds;
10623290c7bSLorenzo Bianconi 			u8 xpon_hal_api;
10723290c7bSLorenzo Bianconi 			u8 wan_xsi;
10823290c7bSLorenzo Bianconi 			u8 ct_joyme4;
1094a7843ccSLorenzo Bianconi 			u8 max_packet;
1104a7843ccSLorenzo Bianconi 			u8 rsv[3];
1114a7843ccSLorenzo Bianconi 			u32 ppe_type;
1124a7843ccSLorenzo Bianconi 			u32 wan_mode;
1134a7843ccSLorenzo Bianconi 			u32 wan_sel;
11423290c7bSLorenzo Bianconi 		} init_info;
11523290c7bSLorenzo Bianconi 		struct {
1164a7843ccSLorenzo Bianconi 			u32 func_id;
11723290c7bSLorenzo Bianconi 			u32 size;
11823290c7bSLorenzo Bianconi 			u32 data;
11923290c7bSLorenzo Bianconi 		} set_info;
120*b81e0f2bSLorenzo Bianconi 		struct {
121*b81e0f2bSLorenzo Bianconi 			u32 npu_stats_addr;
122*b81e0f2bSLorenzo Bianconi 			u32 foe_stats_addr;
123*b81e0f2bSLorenzo Bianconi 		} stats_info;
12423290c7bSLorenzo Bianconi 	};
12523290c7bSLorenzo Bianconi };
12623290c7bSLorenzo Bianconi 
12723290c7bSLorenzo Bianconi static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id,
12823290c7bSLorenzo Bianconi 			       void *p, int size)
12923290c7bSLorenzo Bianconi {
13023290c7bSLorenzo Bianconi 	u16 core = 0; /* FIXME */
13123290c7bSLorenzo Bianconi 	u32 val, offset = core << 4;
13223290c7bSLorenzo Bianconi 	dma_addr_t dma_addr;
13323290c7bSLorenzo Bianconi 	int ret;
13423290c7bSLorenzo Bianconi 
135c5291874SLorenzo Bianconi 	dma_addr = dma_map_single(npu->dev, p, size, DMA_TO_DEVICE);
13623290c7bSLorenzo Bianconi 	ret = dma_mapping_error(npu->dev, dma_addr);
13723290c7bSLorenzo Bianconi 	if (ret)
138c5291874SLorenzo Bianconi 		return ret;
13923290c7bSLorenzo Bianconi 
14023290c7bSLorenzo Bianconi 	spin_lock_bh(&npu->cores[core].lock);
14123290c7bSLorenzo Bianconi 
14223290c7bSLorenzo Bianconi 	regmap_write(npu->regmap, REG_CR_MBQ0_CTRL(0) + offset, dma_addr);
14323290c7bSLorenzo Bianconi 	regmap_write(npu->regmap, REG_CR_MBQ0_CTRL(1) + offset, size);
14423290c7bSLorenzo Bianconi 	regmap_read(npu->regmap, REG_CR_MBQ0_CTRL(2) + offset, &val);
14523290c7bSLorenzo Bianconi 	regmap_write(npu->regmap, REG_CR_MBQ0_CTRL(2) + offset, val + 1);
14623290c7bSLorenzo Bianconi 	val = FIELD_PREP(MBOX_MSG_FUNC_ID, func_id) | MBOX_MSG_WAIT_RSP;
14723290c7bSLorenzo Bianconi 	regmap_write(npu->regmap, REG_CR_MBQ0_CTRL(3) + offset, val);
14823290c7bSLorenzo Bianconi 
14923290c7bSLorenzo Bianconi 	ret = regmap_read_poll_timeout_atomic(npu->regmap,
15023290c7bSLorenzo Bianconi 					      REG_CR_MBQ0_CTRL(3) + offset,
15123290c7bSLorenzo Bianconi 					      val, (val & MBOX_MSG_DONE),
15223290c7bSLorenzo Bianconi 					      100, 100 * MSEC_PER_SEC);
15323290c7bSLorenzo Bianconi 	if (!ret && FIELD_GET(MBOX_MSG_STATUS, val) != NPU_MBOX_SUCCESS)
15423290c7bSLorenzo Bianconi 		ret = -EINVAL;
15523290c7bSLorenzo Bianconi 
15623290c7bSLorenzo Bianconi 	spin_unlock_bh(&npu->cores[core].lock);
15723290c7bSLorenzo Bianconi 
15823290c7bSLorenzo Bianconi 	dma_unmap_single(npu->dev, dma_addr, size, DMA_TO_DEVICE);
15923290c7bSLorenzo Bianconi 
16023290c7bSLorenzo Bianconi 	return ret;
16123290c7bSLorenzo Bianconi }
16223290c7bSLorenzo Bianconi 
16323290c7bSLorenzo Bianconi static int airoha_npu_run_firmware(struct device *dev, void __iomem *base,
16423290c7bSLorenzo Bianconi 				   struct reserved_mem *rmem)
16523290c7bSLorenzo Bianconi {
16623290c7bSLorenzo Bianconi 	const struct firmware *fw;
16723290c7bSLorenzo Bianconi 	void __iomem *addr;
16823290c7bSLorenzo Bianconi 	int ret;
16923290c7bSLorenzo Bianconi 
17023290c7bSLorenzo Bianconi 	ret = request_firmware(&fw, NPU_EN7581_FIRMWARE_RV32, dev);
17123290c7bSLorenzo Bianconi 	if (ret)
17223290c7bSLorenzo Bianconi 		return ret == -ENOENT ? -EPROBE_DEFER : ret;
17323290c7bSLorenzo Bianconi 
17423290c7bSLorenzo Bianconi 	if (fw->size > NPU_EN7581_FIRMWARE_RV32_MAX_SIZE) {
17523290c7bSLorenzo Bianconi 		dev_err(dev, "%s: fw size too overlimit (%zu)\n",
17623290c7bSLorenzo Bianconi 			NPU_EN7581_FIRMWARE_RV32, fw->size);
17723290c7bSLorenzo Bianconi 		ret = -E2BIG;
17823290c7bSLorenzo Bianconi 		goto out;
17923290c7bSLorenzo Bianconi 	}
18023290c7bSLorenzo Bianconi 
18123290c7bSLorenzo Bianconi 	addr = devm_ioremap(dev, rmem->base, rmem->size);
18223290c7bSLorenzo Bianconi 	if (!addr) {
18323290c7bSLorenzo Bianconi 		ret = -ENOMEM;
18423290c7bSLorenzo Bianconi 		goto out;
18523290c7bSLorenzo Bianconi 	}
18623290c7bSLorenzo Bianconi 
18723290c7bSLorenzo Bianconi 	memcpy_toio(addr, fw->data, fw->size);
18823290c7bSLorenzo Bianconi 	release_firmware(fw);
18923290c7bSLorenzo Bianconi 
19023290c7bSLorenzo Bianconi 	ret = request_firmware(&fw, NPU_EN7581_FIRMWARE_DATA, dev);
19123290c7bSLorenzo Bianconi 	if (ret)
19223290c7bSLorenzo Bianconi 		return ret == -ENOENT ? -EPROBE_DEFER : ret;
19323290c7bSLorenzo Bianconi 
19423290c7bSLorenzo Bianconi 	if (fw->size > NPU_EN7581_FIRMWARE_DATA_MAX_SIZE) {
19523290c7bSLorenzo Bianconi 		dev_err(dev, "%s: fw size too overlimit (%zu)\n",
19623290c7bSLorenzo Bianconi 			NPU_EN7581_FIRMWARE_DATA, fw->size);
19723290c7bSLorenzo Bianconi 		ret = -E2BIG;
19823290c7bSLorenzo Bianconi 		goto out;
19923290c7bSLorenzo Bianconi 	}
20023290c7bSLorenzo Bianconi 
20123290c7bSLorenzo Bianconi 	memcpy_toio(base + REG_NPU_LOCAL_SRAM, fw->data, fw->size);
20223290c7bSLorenzo Bianconi out:
20323290c7bSLorenzo Bianconi 	release_firmware(fw);
20423290c7bSLorenzo Bianconi 
20523290c7bSLorenzo Bianconi 	return ret;
20623290c7bSLorenzo Bianconi }
20723290c7bSLorenzo Bianconi 
20823290c7bSLorenzo Bianconi static irqreturn_t airoha_npu_mbox_handler(int irq, void *npu_instance)
20923290c7bSLorenzo Bianconi {
21023290c7bSLorenzo Bianconi 	struct airoha_npu *npu = npu_instance;
21123290c7bSLorenzo Bianconi 
21223290c7bSLorenzo Bianconi 	/* clear mbox interrupt status */
21323290c7bSLorenzo Bianconi 	regmap_write(npu->regmap, REG_CR_MBOX_INT_STATUS,
21423290c7bSLorenzo Bianconi 		     MBOX_INT_STATUS_MASK);
21523290c7bSLorenzo Bianconi 
21623290c7bSLorenzo Bianconi 	/* acknowledge npu */
21723290c7bSLorenzo Bianconi 	regmap_update_bits(npu->regmap, REG_CR_MBQ8_CTRL(3),
21823290c7bSLorenzo Bianconi 			   MBOX_MSG_STATUS | MBOX_MSG_DONE, MBOX_MSG_DONE);
21923290c7bSLorenzo Bianconi 
22023290c7bSLorenzo Bianconi 	return IRQ_HANDLED;
22123290c7bSLorenzo Bianconi }
22223290c7bSLorenzo Bianconi 
22323290c7bSLorenzo Bianconi static void airoha_npu_wdt_work(struct work_struct *work)
22423290c7bSLorenzo Bianconi {
22523290c7bSLorenzo Bianconi 	struct airoha_npu_core *core;
22623290c7bSLorenzo Bianconi 	struct airoha_npu *npu;
22723290c7bSLorenzo Bianconi 	void *dump;
22823290c7bSLorenzo Bianconi 	u32 val[3];
22923290c7bSLorenzo Bianconi 	int c;
23023290c7bSLorenzo Bianconi 
23123290c7bSLorenzo Bianconi 	core = container_of(work, struct airoha_npu_core, wdt_work);
23223290c7bSLorenzo Bianconi 	npu = core->npu;
23323290c7bSLorenzo Bianconi 
23423290c7bSLorenzo Bianconi 	dump = vzalloc(NPU_DUMP_SIZE);
23523290c7bSLorenzo Bianconi 	if (!dump)
23623290c7bSLorenzo Bianconi 		return;
23723290c7bSLorenzo Bianconi 
23823290c7bSLorenzo Bianconi 	c = core - &npu->cores[0];
23923290c7bSLorenzo Bianconi 	regmap_bulk_read(npu->regmap, REG_PC_DBG(c), val, ARRAY_SIZE(val));
24023290c7bSLorenzo Bianconi 	snprintf(dump, NPU_DUMP_SIZE, "PC: %08x SP: %08x LR: %08x\n",
24123290c7bSLorenzo Bianconi 		 val[0], val[1], val[2]);
24223290c7bSLorenzo Bianconi 
24323290c7bSLorenzo Bianconi 	dev_coredumpv(npu->dev, dump, NPU_DUMP_SIZE, GFP_KERNEL);
24423290c7bSLorenzo Bianconi }
24523290c7bSLorenzo Bianconi 
24623290c7bSLorenzo Bianconi static irqreturn_t airoha_npu_wdt_handler(int irq, void *core_instance)
24723290c7bSLorenzo Bianconi {
24823290c7bSLorenzo Bianconi 	struct airoha_npu_core *core = core_instance;
24923290c7bSLorenzo Bianconi 	struct airoha_npu *npu = core->npu;
25023290c7bSLorenzo Bianconi 	int c = core - &npu->cores[0];
25123290c7bSLorenzo Bianconi 	u32 val;
25223290c7bSLorenzo Bianconi 
25323290c7bSLorenzo Bianconi 	regmap_set_bits(npu->regmap, REG_WDT_TIMER_CTRL(c), WDT_INTR_MASK);
25423290c7bSLorenzo Bianconi 	if (!regmap_read(npu->regmap, REG_WDT_TIMER_CTRL(c), &val) &&
25523290c7bSLorenzo Bianconi 	    FIELD_GET(WDT_EN_MASK, val))
25623290c7bSLorenzo Bianconi 		schedule_work(&core->wdt_work);
25723290c7bSLorenzo Bianconi 
25823290c7bSLorenzo Bianconi 	return IRQ_HANDLED;
25923290c7bSLorenzo Bianconi }
26023290c7bSLorenzo Bianconi 
26123290c7bSLorenzo Bianconi static int airoha_npu_ppe_init(struct airoha_npu *npu)
26223290c7bSLorenzo Bianconi {
263c5291874SLorenzo Bianconi 	struct ppe_mbox_data *ppe_data;
264c5291874SLorenzo Bianconi 	int err;
26523290c7bSLorenzo Bianconi 
266c5291874SLorenzo Bianconi 	ppe_data = kzalloc(sizeof(*ppe_data), GFP_KERNEL);
267c5291874SLorenzo Bianconi 	if (!ppe_data)
268c5291874SLorenzo Bianconi 		return -ENOMEM;
269c5291874SLorenzo Bianconi 
270c5291874SLorenzo Bianconi 	ppe_data->func_type = NPU_OP_SET;
271c5291874SLorenzo Bianconi 	ppe_data->func_id = PPE_FUNC_SET_WAIT_HWNAT_INIT;
272c5291874SLorenzo Bianconi 	ppe_data->init_info.ppe_type = PPE_TYPE_L2B_IPV4_IPV6;
273c5291874SLorenzo Bianconi 	ppe_data->init_info.wan_mode = QDMA_WAN_ETHER;
274c5291874SLorenzo Bianconi 
275c5291874SLorenzo Bianconi 	err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data,
276c5291874SLorenzo Bianconi 				  sizeof(*ppe_data));
277c5291874SLorenzo Bianconi 	kfree(ppe_data);
278c5291874SLorenzo Bianconi 
279c5291874SLorenzo Bianconi 	return err;
28023290c7bSLorenzo Bianconi }
28123290c7bSLorenzo Bianconi 
28223290c7bSLorenzo Bianconi static int airoha_npu_ppe_deinit(struct airoha_npu *npu)
28323290c7bSLorenzo Bianconi {
284c5291874SLorenzo Bianconi 	struct ppe_mbox_data *ppe_data;
285c5291874SLorenzo Bianconi 	int err;
28623290c7bSLorenzo Bianconi 
287c5291874SLorenzo Bianconi 	ppe_data = kzalloc(sizeof(*ppe_data), GFP_KERNEL);
288c5291874SLorenzo Bianconi 	if (!ppe_data)
289c5291874SLorenzo Bianconi 		return -ENOMEM;
290c5291874SLorenzo Bianconi 
291c5291874SLorenzo Bianconi 	ppe_data->func_type = NPU_OP_SET;
292c5291874SLorenzo Bianconi 	ppe_data->func_id = PPE_FUNC_SET_WAIT_HWNAT_DEINIT;
293c5291874SLorenzo Bianconi 
294c5291874SLorenzo Bianconi 	err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data,
295c5291874SLorenzo Bianconi 				  sizeof(*ppe_data));
296c5291874SLorenzo Bianconi 	kfree(ppe_data);
297c5291874SLorenzo Bianconi 
298c5291874SLorenzo Bianconi 	return err;
29923290c7bSLorenzo Bianconi }
30023290c7bSLorenzo Bianconi 
30123290c7bSLorenzo Bianconi static int airoha_npu_ppe_flush_sram_entries(struct airoha_npu *npu,
30223290c7bSLorenzo Bianconi 					     dma_addr_t foe_addr,
30323290c7bSLorenzo Bianconi 					     int sram_num_entries)
30423290c7bSLorenzo Bianconi {
305c5291874SLorenzo Bianconi 	struct ppe_mbox_data *ppe_data;
306c5291874SLorenzo Bianconi 	int err;
30723290c7bSLorenzo Bianconi 
308c5291874SLorenzo Bianconi 	ppe_data = kzalloc(sizeof(*ppe_data), GFP_KERNEL);
309c5291874SLorenzo Bianconi 	if (!ppe_data)
310c5291874SLorenzo Bianconi 		return -ENOMEM;
311c5291874SLorenzo Bianconi 
312c5291874SLorenzo Bianconi 	ppe_data->func_type = NPU_OP_SET;
313c5291874SLorenzo Bianconi 	ppe_data->func_id = PPE_FUNC_SET_WAIT_API;
314c5291874SLorenzo Bianconi 	ppe_data->set_info.func_id = PPE_SRAM_RESET_VAL;
315c5291874SLorenzo Bianconi 	ppe_data->set_info.data = foe_addr;
316c5291874SLorenzo Bianconi 	ppe_data->set_info.size = sram_num_entries;
317c5291874SLorenzo Bianconi 
318c5291874SLorenzo Bianconi 	err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data,
319c5291874SLorenzo Bianconi 				  sizeof(*ppe_data));
320c5291874SLorenzo Bianconi 	kfree(ppe_data);
321c5291874SLorenzo Bianconi 
322c5291874SLorenzo Bianconi 	return err;
32323290c7bSLorenzo Bianconi }
32423290c7bSLorenzo Bianconi 
32523290c7bSLorenzo Bianconi static int airoha_npu_foe_commit_entry(struct airoha_npu *npu,
32623290c7bSLorenzo Bianconi 				       dma_addr_t foe_addr,
32723290c7bSLorenzo Bianconi 				       u32 entry_size, u32 hash, bool ppe2)
32823290c7bSLorenzo Bianconi {
329c5291874SLorenzo Bianconi 	struct ppe_mbox_data *ppe_data;
33023290c7bSLorenzo Bianconi 	int err;
33123290c7bSLorenzo Bianconi 
332c5291874SLorenzo Bianconi 	ppe_data = kzalloc(sizeof(*ppe_data), GFP_ATOMIC);
333c5291874SLorenzo Bianconi 	if (!ppe_data)
334c5291874SLorenzo Bianconi 		return -ENOMEM;
335c5291874SLorenzo Bianconi 
336c5291874SLorenzo Bianconi 	ppe_data->func_type = NPU_OP_SET;
337c5291874SLorenzo Bianconi 	ppe_data->func_id = PPE_FUNC_SET_WAIT_API;
338c5291874SLorenzo Bianconi 	ppe_data->set_info.data = foe_addr;
339c5291874SLorenzo Bianconi 	ppe_data->set_info.size = entry_size;
340c5291874SLorenzo Bianconi 	ppe_data->set_info.func_id = ppe2 ? PPE2_SRAM_SET_ENTRY
34123290c7bSLorenzo Bianconi 					  : PPE_SRAM_SET_ENTRY;
34223290c7bSLorenzo Bianconi 
343c5291874SLorenzo Bianconi 	err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data,
344c5291874SLorenzo Bianconi 				  sizeof(*ppe_data));
34523290c7bSLorenzo Bianconi 	if (err)
346c5291874SLorenzo Bianconi 		goto out;
347c5291874SLorenzo Bianconi 
348c5291874SLorenzo Bianconi 	ppe_data->set_info.func_id = PPE_SRAM_SET_VAL;
349c5291874SLorenzo Bianconi 	ppe_data->set_info.data = hash;
350c5291874SLorenzo Bianconi 	ppe_data->set_info.size = sizeof(u32);
351c5291874SLorenzo Bianconi 
352c5291874SLorenzo Bianconi 	err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data,
353c5291874SLorenzo Bianconi 				  sizeof(*ppe_data));
354c5291874SLorenzo Bianconi out:
355c5291874SLorenzo Bianconi 	kfree(ppe_data);
356c5291874SLorenzo Bianconi 
35723290c7bSLorenzo Bianconi 	return err;
35823290c7bSLorenzo Bianconi }
35923290c7bSLorenzo Bianconi 
360*b81e0f2bSLorenzo Bianconi static int airoha_npu_stats_setup(struct airoha_npu *npu,
361*b81e0f2bSLorenzo Bianconi 				  dma_addr_t foe_stats_addr)
362*b81e0f2bSLorenzo Bianconi {
363*b81e0f2bSLorenzo Bianconi 	int err, size = PPE_STATS_NUM_ENTRIES * sizeof(*npu->stats);
364*b81e0f2bSLorenzo Bianconi 	struct ppe_mbox_data *ppe_data;
365*b81e0f2bSLorenzo Bianconi 
366*b81e0f2bSLorenzo Bianconi 	if (!size) /* flow stats are disabled */
367*b81e0f2bSLorenzo Bianconi 		return 0;
368*b81e0f2bSLorenzo Bianconi 
369*b81e0f2bSLorenzo Bianconi 	ppe_data = kzalloc(sizeof(*ppe_data), GFP_ATOMIC);
370*b81e0f2bSLorenzo Bianconi 	if (!ppe_data)
371*b81e0f2bSLorenzo Bianconi 		return -ENOMEM;
372*b81e0f2bSLorenzo Bianconi 
373*b81e0f2bSLorenzo Bianconi 	ppe_data->func_type = NPU_OP_SET;
374*b81e0f2bSLorenzo Bianconi 	ppe_data->func_id = PPE_FUNC_SET_WAIT_FLOW_STATS_SETUP;
375*b81e0f2bSLorenzo Bianconi 	ppe_data->stats_info.foe_stats_addr = foe_stats_addr;
376*b81e0f2bSLorenzo Bianconi 
377*b81e0f2bSLorenzo Bianconi 	err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data,
378*b81e0f2bSLorenzo Bianconi 				  sizeof(*ppe_data));
379*b81e0f2bSLorenzo Bianconi 	if (err)
380*b81e0f2bSLorenzo Bianconi 		goto out;
381*b81e0f2bSLorenzo Bianconi 
382*b81e0f2bSLorenzo Bianconi 	npu->stats = devm_ioremap(npu->dev,
383*b81e0f2bSLorenzo Bianconi 				  ppe_data->stats_info.npu_stats_addr,
384*b81e0f2bSLorenzo Bianconi 				  size);
385*b81e0f2bSLorenzo Bianconi 	if (!npu->stats)
386*b81e0f2bSLorenzo Bianconi 		err = -ENOMEM;
387*b81e0f2bSLorenzo Bianconi out:
388*b81e0f2bSLorenzo Bianconi 	kfree(ppe_data);
389*b81e0f2bSLorenzo Bianconi 
390*b81e0f2bSLorenzo Bianconi 	return err;
391*b81e0f2bSLorenzo Bianconi }
392*b81e0f2bSLorenzo Bianconi 
393*b81e0f2bSLorenzo Bianconi struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr)
39423290c7bSLorenzo Bianconi {
39523290c7bSLorenzo Bianconi 	struct platform_device *pdev;
39623290c7bSLorenzo Bianconi 	struct device_node *np;
39723290c7bSLorenzo Bianconi 	struct airoha_npu *npu;
39823290c7bSLorenzo Bianconi 
39923290c7bSLorenzo Bianconi 	np = of_parse_phandle(dev->of_node, "airoha,npu", 0);
40023290c7bSLorenzo Bianconi 	if (!np)
40123290c7bSLorenzo Bianconi 		return ERR_PTR(-ENODEV);
40223290c7bSLorenzo Bianconi 
40323290c7bSLorenzo Bianconi 	pdev = of_find_device_by_node(np);
40423290c7bSLorenzo Bianconi 	of_node_put(np);
40523290c7bSLorenzo Bianconi 
40623290c7bSLorenzo Bianconi 	if (!pdev) {
40723290c7bSLorenzo Bianconi 		dev_err(dev, "cannot find device node %s\n", np->name);
40823290c7bSLorenzo Bianconi 		return ERR_PTR(-ENODEV);
40923290c7bSLorenzo Bianconi 	}
41023290c7bSLorenzo Bianconi 
41123290c7bSLorenzo Bianconi 	if (!try_module_get(THIS_MODULE)) {
41223290c7bSLorenzo Bianconi 		dev_err(dev, "failed to get the device driver module\n");
41323290c7bSLorenzo Bianconi 		npu = ERR_PTR(-ENODEV);
41423290c7bSLorenzo Bianconi 		goto error_pdev_put;
41523290c7bSLorenzo Bianconi 	}
41623290c7bSLorenzo Bianconi 
41723290c7bSLorenzo Bianconi 	npu = platform_get_drvdata(pdev);
41823290c7bSLorenzo Bianconi 	if (!npu) {
41923290c7bSLorenzo Bianconi 		npu = ERR_PTR(-ENODEV);
42023290c7bSLorenzo Bianconi 		goto error_module_put;
42123290c7bSLorenzo Bianconi 	}
42223290c7bSLorenzo Bianconi 
42323290c7bSLorenzo Bianconi 	if (!device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER)) {
42423290c7bSLorenzo Bianconi 		dev_err(&pdev->dev,
42523290c7bSLorenzo Bianconi 			"failed to create device link to consumer %s\n",
42623290c7bSLorenzo Bianconi 			dev_name(dev));
42723290c7bSLorenzo Bianconi 		npu = ERR_PTR(-EINVAL);
42823290c7bSLorenzo Bianconi 		goto error_module_put;
42923290c7bSLorenzo Bianconi 	}
43023290c7bSLorenzo Bianconi 
431*b81e0f2bSLorenzo Bianconi 	if (stats_addr) {
432*b81e0f2bSLorenzo Bianconi 		int err;
433*b81e0f2bSLorenzo Bianconi 
434*b81e0f2bSLorenzo Bianconi 		err = airoha_npu_stats_setup(npu, *stats_addr);
435*b81e0f2bSLorenzo Bianconi 		if (err) {
436*b81e0f2bSLorenzo Bianconi 			dev_err(dev, "failed to allocate npu stats buffer\n");
437*b81e0f2bSLorenzo Bianconi 			npu = ERR_PTR(err);
438*b81e0f2bSLorenzo Bianconi 			goto error_module_put;
439*b81e0f2bSLorenzo Bianconi 		}
440*b81e0f2bSLorenzo Bianconi 	}
441*b81e0f2bSLorenzo Bianconi 
44223290c7bSLorenzo Bianconi 	return npu;
44323290c7bSLorenzo Bianconi 
44423290c7bSLorenzo Bianconi error_module_put:
44523290c7bSLorenzo Bianconi 	module_put(THIS_MODULE);
44623290c7bSLorenzo Bianconi error_pdev_put:
44723290c7bSLorenzo Bianconi 	platform_device_put(pdev);
44823290c7bSLorenzo Bianconi 
44923290c7bSLorenzo Bianconi 	return npu;
45023290c7bSLorenzo Bianconi }
45123290c7bSLorenzo Bianconi EXPORT_SYMBOL_GPL(airoha_npu_get);
45223290c7bSLorenzo Bianconi 
45323290c7bSLorenzo Bianconi void airoha_npu_put(struct airoha_npu *npu)
45423290c7bSLorenzo Bianconi {
45523290c7bSLorenzo Bianconi 	module_put(THIS_MODULE);
45623290c7bSLorenzo Bianconi 	put_device(npu->dev);
45723290c7bSLorenzo Bianconi }
45823290c7bSLorenzo Bianconi EXPORT_SYMBOL_GPL(airoha_npu_put);
45923290c7bSLorenzo Bianconi 
46023290c7bSLorenzo Bianconi static const struct of_device_id of_airoha_npu_match[] = {
46123290c7bSLorenzo Bianconi 	{ .compatible = "airoha,en7581-npu" },
46223290c7bSLorenzo Bianconi 	{ /* sentinel */ }
46323290c7bSLorenzo Bianconi };
46423290c7bSLorenzo Bianconi MODULE_DEVICE_TABLE(of, of_airoha_npu_match);
46523290c7bSLorenzo Bianconi 
46623290c7bSLorenzo Bianconi static const struct regmap_config regmap_config = {
46723290c7bSLorenzo Bianconi 	.name			= "npu",
46823290c7bSLorenzo Bianconi 	.reg_bits		= 32,
46923290c7bSLorenzo Bianconi 	.val_bits		= 32,
47023290c7bSLorenzo Bianconi 	.reg_stride		= 4,
47123290c7bSLorenzo Bianconi 	.disable_locking	= true,
47223290c7bSLorenzo Bianconi };
47323290c7bSLorenzo Bianconi 
47423290c7bSLorenzo Bianconi static int airoha_npu_probe(struct platform_device *pdev)
47523290c7bSLorenzo Bianconi {
47623290c7bSLorenzo Bianconi 	struct device *dev = &pdev->dev;
47723290c7bSLorenzo Bianconi 	struct reserved_mem *rmem;
47823290c7bSLorenzo Bianconi 	struct airoha_npu *npu;
47923290c7bSLorenzo Bianconi 	struct device_node *np;
48023290c7bSLorenzo Bianconi 	void __iomem *base;
48123290c7bSLorenzo Bianconi 	int i, irq, err;
48223290c7bSLorenzo Bianconi 
48323290c7bSLorenzo Bianconi 	base = devm_platform_ioremap_resource(pdev, 0);
48423290c7bSLorenzo Bianconi 	if (IS_ERR(base))
48523290c7bSLorenzo Bianconi 		return PTR_ERR(base);
48623290c7bSLorenzo Bianconi 
48723290c7bSLorenzo Bianconi 	npu = devm_kzalloc(dev, sizeof(*npu), GFP_KERNEL);
48823290c7bSLorenzo Bianconi 	if (!npu)
48923290c7bSLorenzo Bianconi 		return -ENOMEM;
49023290c7bSLorenzo Bianconi 
49123290c7bSLorenzo Bianconi 	npu->dev = dev;
49223290c7bSLorenzo Bianconi 	npu->ops.ppe_init = airoha_npu_ppe_init;
49323290c7bSLorenzo Bianconi 	npu->ops.ppe_deinit = airoha_npu_ppe_deinit;
49423290c7bSLorenzo Bianconi 	npu->ops.ppe_flush_sram_entries = airoha_npu_ppe_flush_sram_entries;
49523290c7bSLorenzo Bianconi 	npu->ops.ppe_foe_commit_entry = airoha_npu_foe_commit_entry;
49623290c7bSLorenzo Bianconi 
49723290c7bSLorenzo Bianconi 	npu->regmap = devm_regmap_init_mmio(dev, base, &regmap_config);
49823290c7bSLorenzo Bianconi 	if (IS_ERR(npu->regmap))
49923290c7bSLorenzo Bianconi 		return PTR_ERR(npu->regmap);
50023290c7bSLorenzo Bianconi 
50123290c7bSLorenzo Bianconi 	np = of_parse_phandle(dev->of_node, "memory-region", 0);
50223290c7bSLorenzo Bianconi 	if (!np)
50323290c7bSLorenzo Bianconi 		return -ENODEV;
50423290c7bSLorenzo Bianconi 
50523290c7bSLorenzo Bianconi 	rmem = of_reserved_mem_lookup(np);
50623290c7bSLorenzo Bianconi 	of_node_put(np);
50723290c7bSLorenzo Bianconi 
50823290c7bSLorenzo Bianconi 	if (!rmem)
50923290c7bSLorenzo Bianconi 		return -ENODEV;
51023290c7bSLorenzo Bianconi 
51123290c7bSLorenzo Bianconi 	irq = platform_get_irq(pdev, 0);
51223290c7bSLorenzo Bianconi 	if (irq < 0)
51323290c7bSLorenzo Bianconi 		return irq;
51423290c7bSLorenzo Bianconi 
51523290c7bSLorenzo Bianconi 	err = devm_request_irq(dev, irq, airoha_npu_mbox_handler,
51623290c7bSLorenzo Bianconi 			       IRQF_SHARED, "airoha-npu-mbox", npu);
51723290c7bSLorenzo Bianconi 	if (err)
51823290c7bSLorenzo Bianconi 		return err;
51923290c7bSLorenzo Bianconi 
52023290c7bSLorenzo Bianconi 	for (i = 0; i < ARRAY_SIZE(npu->cores); i++) {
52123290c7bSLorenzo Bianconi 		struct airoha_npu_core *core = &npu->cores[i];
52223290c7bSLorenzo Bianconi 
52323290c7bSLorenzo Bianconi 		spin_lock_init(&core->lock);
52423290c7bSLorenzo Bianconi 		core->npu = npu;
52523290c7bSLorenzo Bianconi 
52623290c7bSLorenzo Bianconi 		irq = platform_get_irq(pdev, i + 1);
52723290c7bSLorenzo Bianconi 		if (irq < 0)
52823290c7bSLorenzo Bianconi 			return irq;
52923290c7bSLorenzo Bianconi 
53023290c7bSLorenzo Bianconi 		err = devm_request_irq(dev, irq, airoha_npu_wdt_handler,
53123290c7bSLorenzo Bianconi 				       IRQF_SHARED, "airoha-npu-wdt", core);
53223290c7bSLorenzo Bianconi 		if (err)
53323290c7bSLorenzo Bianconi 			return err;
53423290c7bSLorenzo Bianconi 
53523290c7bSLorenzo Bianconi 		INIT_WORK(&core->wdt_work, airoha_npu_wdt_work);
53623290c7bSLorenzo Bianconi 	}
53723290c7bSLorenzo Bianconi 
53823290c7bSLorenzo Bianconi 	err = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
53923290c7bSLorenzo Bianconi 	if (err)
54023290c7bSLorenzo Bianconi 		return err;
54123290c7bSLorenzo Bianconi 
54223290c7bSLorenzo Bianconi 	err = airoha_npu_run_firmware(dev, base, rmem);
54323290c7bSLorenzo Bianconi 	if (err)
54423290c7bSLorenzo Bianconi 		return dev_err_probe(dev, err, "failed to run npu firmware\n");
54523290c7bSLorenzo Bianconi 
54623290c7bSLorenzo Bianconi 	regmap_write(npu->regmap, REG_CR_NPU_MIB(10),
54723290c7bSLorenzo Bianconi 		     rmem->base + NPU_EN7581_FIRMWARE_RV32_MAX_SIZE);
54823290c7bSLorenzo Bianconi 	regmap_write(npu->regmap, REG_CR_NPU_MIB(11), 0x40000); /* SRAM 256K */
54923290c7bSLorenzo Bianconi 	regmap_write(npu->regmap, REG_CR_NPU_MIB(12), 0);
55023290c7bSLorenzo Bianconi 	regmap_write(npu->regmap, REG_CR_NPU_MIB(21), 1);
55123290c7bSLorenzo Bianconi 	msleep(100);
55223290c7bSLorenzo Bianconi 
55323290c7bSLorenzo Bianconi 	/* setting booting address */
55423290c7bSLorenzo Bianconi 	for (i = 0; i < NPU_NUM_CORES; i++)
55523290c7bSLorenzo Bianconi 		regmap_write(npu->regmap, REG_CR_BOOT_BASE(i), rmem->base);
55623290c7bSLorenzo Bianconi 	usleep_range(1000, 2000);
55723290c7bSLorenzo Bianconi 
55823290c7bSLorenzo Bianconi 	/* enable NPU cores */
55923290c7bSLorenzo Bianconi 	/* do not start core3 since it is used for WiFi offloading */
56023290c7bSLorenzo Bianconi 	regmap_write(npu->regmap, REG_CR_BOOT_CONFIG, 0xf7);
56123290c7bSLorenzo Bianconi 	regmap_write(npu->regmap, REG_CR_BOOT_TRIGGER, 0x1);
56223290c7bSLorenzo Bianconi 	msleep(100);
56323290c7bSLorenzo Bianconi 
56423290c7bSLorenzo Bianconi 	platform_set_drvdata(pdev, npu);
56523290c7bSLorenzo Bianconi 
56623290c7bSLorenzo Bianconi 	return 0;
56723290c7bSLorenzo Bianconi }
56823290c7bSLorenzo Bianconi 
56923290c7bSLorenzo Bianconi static void airoha_npu_remove(struct platform_device *pdev)
57023290c7bSLorenzo Bianconi {
57123290c7bSLorenzo Bianconi 	struct airoha_npu *npu = platform_get_drvdata(pdev);
57223290c7bSLorenzo Bianconi 	int i;
57323290c7bSLorenzo Bianconi 
57423290c7bSLorenzo Bianconi 	for (i = 0; i < ARRAY_SIZE(npu->cores); i++)
57523290c7bSLorenzo Bianconi 		cancel_work_sync(&npu->cores[i].wdt_work);
57623290c7bSLorenzo Bianconi }
57723290c7bSLorenzo Bianconi 
57823290c7bSLorenzo Bianconi static struct platform_driver airoha_npu_driver = {
57923290c7bSLorenzo Bianconi 	.probe = airoha_npu_probe,
58023290c7bSLorenzo Bianconi 	.remove = airoha_npu_remove,
58123290c7bSLorenzo Bianconi 	.driver = {
58223290c7bSLorenzo Bianconi 		.name = "airoha-npu",
58323290c7bSLorenzo Bianconi 		.of_match_table = of_airoha_npu_match,
58423290c7bSLorenzo Bianconi 	},
58523290c7bSLorenzo Bianconi };
58623290c7bSLorenzo Bianconi module_platform_driver(airoha_npu_driver);
58723290c7bSLorenzo Bianconi 
58823290c7bSLorenzo Bianconi MODULE_LICENSE("GPL");
58923290c7bSLorenzo Bianconi MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
59023290c7bSLorenzo Bianconi MODULE_DESCRIPTION("Airoha Network Processor Unit driver");
591