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, ®map_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