From ffc170746cd67a244842014bd9f38fc37d352f88 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Thu, 18 Sep 2025 21:41:32 +0800 Subject: [PATCH 001/214] ub:ubus: Support for ub bus driver framework ANBZ: #45460 commit e42bc00975898c42b4b9f1dc88fbb3f0748de8a5 openEuler. Supports the UB bus driver framework, providing callback interfaces for match/probe/remove/uevent/shutdown, and supports device driver registration and unregistration interfaces. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- anolis/configs/L0-MANDATORY/arm64/CONFIG_UB | 1 + .../configs/L0-MANDATORY/arm64/CONFIG_UB_UBUS | 1 + .../L0-MANDATORY/arm64/CONFIG_UB_UBUS_BUS | 1 + drivers/Kconfig | 1 + drivers/Makefile | 1 + drivers/ub/Kconfig | 17 ++ drivers/ub/Makefile | 3 + drivers/ub/ubus/Kconfig | 30 ++ drivers/ub/ubus/Makefile | 7 + drivers/ub/ubus/ub-driver.c | 43 +++ drivers/ub/ubus/ubus.h | 13 + drivers/ub/ubus/ubus_driver.c | 269 +++++++++++++++++ drivers/ub/ubus/ubus_inner.h | 10 + include/linux/mod_devicetable.h | 22 ++ include/uapi/ub/ubus/ubus_regs.h | 279 ++++++++++++++++++ include/ub/ubus/ubus.h | 172 +++++++++++ include/ub/ubus/ubus_ids.h | 49 +++ scripts/mod/devicetable-offsets.c | 11 + scripts/mod/file2alias.c | 48 +++ 19 files changed, 978 insertions(+) create mode 100644 anolis/configs/L0-MANDATORY/arm64/CONFIG_UB create mode 100644 anolis/configs/L0-MANDATORY/arm64/CONFIG_UB_UBUS create mode 100644 anolis/configs/L0-MANDATORY/arm64/CONFIG_UB_UBUS_BUS create mode 100644 drivers/ub/Kconfig create mode 100644 drivers/ub/Makefile create mode 100644 drivers/ub/ubus/Kconfig create mode 100644 drivers/ub/ubus/Makefile create mode 100644 drivers/ub/ubus/ub-driver.c create mode 100644 drivers/ub/ubus/ubus.h create mode 100644 drivers/ub/ubus/ubus_driver.c create mode 100644 drivers/ub/ubus/ubus_inner.h create mode 100644 include/uapi/ub/ubus/ubus_regs.h create mode 100644 include/ub/ubus/ubus.h create mode 100644 include/ub/ubus/ubus_ids.h diff --git a/anolis/configs/L0-MANDATORY/arm64/CONFIG_UB b/anolis/configs/L0-MANDATORY/arm64/CONFIG_UB new file mode 100644 index 000000000000..0a01d9140d25 --- /dev/null +++ b/anolis/configs/L0-MANDATORY/arm64/CONFIG_UB @@ -0,0 +1 @@ +CONFIG_UB=y diff --git a/anolis/configs/L0-MANDATORY/arm64/CONFIG_UB_UBUS b/anolis/configs/L0-MANDATORY/arm64/CONFIG_UB_UBUS new file mode 100644 index 000000000000..44d0b3f6dbfb --- /dev/null +++ b/anolis/configs/L0-MANDATORY/arm64/CONFIG_UB_UBUS @@ -0,0 +1 @@ +CONFIG_UB_UBUS=y diff --git a/anolis/configs/L0-MANDATORY/arm64/CONFIG_UB_UBUS_BUS b/anolis/configs/L0-MANDATORY/arm64/CONFIG_UB_UBUS_BUS new file mode 100644 index 000000000000..96e2bfd67afd --- /dev/null +++ b/anolis/configs/L0-MANDATORY/arm64/CONFIG_UB_UBUS_BUS @@ -0,0 +1 @@ +CONFIG_UB_UBUS_BUS=m diff --git a/drivers/Kconfig b/drivers/Kconfig index 572436262798..c77f8bfbb020 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -7,6 +7,7 @@ source "drivers/amba/Kconfig" source "drivers/eisa/Kconfig" source "drivers/pci/Kconfig" source "drivers/cxl/Kconfig" +source "drivers/ub/Kconfig" source "drivers/pcmcia/Kconfig" source "drivers/rapidio/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 15f4df087e2e..7e5b459f5249 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_GPIOLIB) += gpio/ obj-y += pwm/ obj-y += pci/ +obj-y += ub/ obj-$(CONFIG_PARISC) += parisc/ obj-$(CONFIG_RAPIDIO) += rapidio/ diff --git a/drivers/ub/Kconfig b/drivers/ub/Kconfig new file mode 100644 index 000000000000..fc591be43597 --- /dev/null +++ b/drivers/ub/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# UnifiedBus configuration +# + +menuconfig UB + bool "UB (UnifiedBus) support" + depends on ARM64 + default n + help + Support for UB. + If you have a hardware that support UB protocol, + Say y here. By Default this option is closed. + +if UB +source "drivers/ub/ubus/Kconfig" +endif # UB diff --git a/drivers/ub/Makefile b/drivers/ub/Makefile new file mode 100644 index 000000000000..10428ed28774 --- /dev/null +++ b/drivers/ub/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-y += ubus/ diff --git a/drivers/ub/ubus/Kconfig b/drivers/ub/ubus/Kconfig new file mode 100644 index 000000000000..cf2f7e81b8c8 --- /dev/null +++ b/drivers/ub/ubus/Kconfig @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# UB bus driver configuration +# + +menuconfig UB_UBUS + default n + bool "UBUS support" + depends on UB + help + This option enables support for UB bus device management + functionality, providing fundamental capabilities such as + UB bus registration, device registration, and driver registration. + It configures and enables devices, offering device capabilities + to the upper layers. + Say 'Y' here unless you know what you are doing. + +if UB_UBUS + +config UB_UBUS_BUS + default n + tristate "UB Bus Driver Modular Section" + depends on UB_UBUS + help + This option enables the modular part of the UB bus driver, + which works in conjunction with the built-in part to implement + the UB bus device management functionality. + Say 'M' here unless you know what you are doing. + +endif diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile new file mode 100644 index 000000000000..b3cb5016bf2e --- /dev/null +++ b/drivers/ub/ubus/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_UB_UBUS) += ub-driver.o + +ubus-y := ubus_driver.o + +obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/ub-driver.c b/drivers/ub/ubus/ub-driver.c new file mode 100644 index 000000000000..516aec010e16 --- /dev/null +++ b/drivers/ub/ubus/ub-driver.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include + +#include "ubus_inner.h" + +struct bus_type ub_bus_type = { + .name = "ub", +}; +EXPORT_SYMBOL_GPL(ub_bus_type); + +int __ub_register_driver(struct ub_driver *drv, struct module *owner, + const char *mod_name) +{ + if (!drv) + return -EINVAL; + + drv->driver.name = drv->name; + drv->driver.bus = &ub_bus_type; + drv->driver.owner = owner; + drv->driver.mod_name = mod_name; + + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(__ub_register_driver); + +void ub_unregister_driver(struct ub_driver *drv) +{ + if (!drv) + return; + + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(ub_unregister_driver); + +static int __init ub_driver_init(void) +{ + return bus_register(&ub_bus_type); +} +postcore_initcall(ub_driver_init); diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h new file mode 100644 index 000000000000..b82922a63de4 --- /dev/null +++ b/drivers/ub/ubus/ubus.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ +#ifndef __UBUS_H__ +#define __UBUS_H__ + +#include + +int ub_host_probe(void); +void ub_host_remove(void); + +#endif /* __UBUS_H__ */ diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c new file mode 100644 index 000000000000..28303556807a --- /dev/null +++ b/drivers/ub/ubus/ubus_driver.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2002-2004, 2007 Greg Kroah-Hartman + * (C) Copyright 2007 Novell Inc. + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + * + * Thanks to Greg Kroah-Hartman and Novell Inc. for their previous work. + * + */ + +#define pr_fmt(fmt) "ubus driver: " fmt + +#include +#include + +#include "ubus.h" + +struct ub_entity *ub_entity_get(struct ub_entity *dev) +{ + if (dev) + get_device(&dev->dev); + return dev; +} +EXPORT_SYMBOL_GPL(ub_entity_get); + +void ub_entity_put(struct ub_entity *dev) +{ + if (dev) + put_device(&dev->dev); +} +EXPORT_SYMBOL_GPL(ub_entity_put); + +static const struct ub_device_id ub_entity_id_any = { + .vendor = (__u32)UB_ANY_ID, + .device = (__u32)UB_ANY_ID, + .mod_vendor = (__u32)UB_ANY_ID, + .module = (__u32)UB_ANY_ID, +}; + +static inline const struct ub_device_id * +ub_match_one_device(const struct ub_device_id *id, const struct ub_entity *dev) +{ + if ((id->vendor == UB_ANY_ID || id->vendor == uent_vendor(dev)) && + (id->device == UB_ANY_ID || id->device == uent_device(dev)) && + (id->mod_vendor == UB_ANY_ID || id->mod_vendor == dev->mod_vendor) && + (id->module == UB_ANY_ID || id->module == dev->module) && + !((id->class_code ^ uent_class(dev)) & id->class_mask)) + return id; + return NULL; +} + +const struct ub_device_id *ub_match_id(const struct ub_device_id *ids, + struct ub_entity *dev) +{ + if (ids && dev) { + while (ids->vendor || ids->mod_vendor || ids->class_mask) { + if (ub_match_one_device(ids, dev)) + return ids; + ids++; + } + } + return NULL; +} + +static const struct ub_device_id *ub_match_device(struct ub_driver *drv, + struct ub_entity *dev) +{ + const struct ub_device_id *found_id = NULL, *ids; + + /* When driver_override is set, only bind to the matching driver */ + if (dev->driver_override && strcmp(dev->driver_override, drv->name)) + return NULL; + + for (ids = drv->id_table; (found_id = ub_match_id(ids, dev)); + ids = found_id + 1) { + if (found_id->override_only) { + if (dev->driver_override) + return found_id; + } else { + return found_id; + } + } + + /* driver_override will always match, send a dummy id */ + if (dev->driver_override) + return &ub_entity_id_any; + + return NULL; +} + +static int ub_bus_match(struct device *dev, struct device_driver *drv) +{ + struct ub_driver *ub_drv = to_ub_driver(drv); + struct ub_entity *ub_entity = to_ub_entity(dev); + const struct ub_device_id *found_id; + + if (!ub_entity->match_driver) + return 0; + + found_id = ub_match_device(ub_drv, ub_entity); + if (found_id) + return 1; + + return 0; +} + +static int ub_call_probe(struct ub_driver *drv, struct ub_entity *dev, + const struct ub_device_id *id) +{ + int ret; + + dev->driver = drv; + /* + * Probe function should return < 0 for failure, 0 for success + * Treat values > 0 as success, but warn. + */ + ret = drv->probe(dev, id); + if (ret < 0) { + dev->driver = NULL; + return ret; + } else if (ret > 0) { + ub_warn(dev, "Driver probe function unexpectedly, ret=%d\n", + ret); + } + + return 0; +} + +static int __ub_entity_probe(struct ub_driver *drv, struct ub_entity *dev) +{ + const struct ub_device_id *id; + int ret = 0; + + if (drv->probe) { + ret = -ENODEV; + + id = ub_match_device(drv, dev); + if (id) + ret = ub_call_probe(drv, dev, id); + } + + return ret; +} + +static int ub_entity_probe(struct device *dev) +{ + struct ub_driver *drv = to_ub_driver(dev->driver); + struct ub_entity *ub_entity = to_ub_entity(dev); + int ret; + + ub_entity_get(ub_entity); + ret = __ub_entity_probe(drv, ub_entity); + if (ret) + ub_entity_put(ub_entity); + return ret; +} + +static void ub_entity_remove(struct device *dev) +{ + struct ub_entity *ub_entity = to_ub_entity(dev); + struct ub_driver *drv = ub_entity->driver; + + if (drv->remove) + drv->remove(ub_entity); + + ub_entity->driver = NULL; + + ub_entity_put(ub_entity); +} + +static void ub_entity_shutdown(struct device *dev) +{ + struct ub_entity *uent = to_ub_entity(dev); + struct ub_driver *drv = uent->driver; + + ub_dbg(uent, "come shutdown\n"); + + pm_runtime_resume(dev); + + if (drv && drv->shutdown) + drv->shutdown(uent); +} + +static int ub_uevent(const struct device *dev, struct kobj_uevent_env *env) +{ + struct ub_entity *uent; + + if (!dev) + return -ENODEV; + + uent = to_ub_entity(dev); + + if (add_uevent_var(env, "UB_ID=%04X:%04X", uent_vendor(uent), + uent_device(uent))) + return -ENOMEM; + + if (add_uevent_var(env, "UB_MODULE=%04X:%04X", uent->mod_vendor, + uent->module)) + return -ENOMEM; + + if (add_uevent_var(env, "UB_TYPE=%01X", uent_type(uent))) + return -ENOMEM; + + if (add_uevent_var(env, "UB_CLASS=%04X", uent_class(uent))) + return -ENOMEM; + + if (add_uevent_var(env, "UB_VERSION=%01X", uent_version(uent))) + return -ENOMEM; + + if (add_uevent_var(env, "UB_SEQ_NUM=%016llX", uent_seq(uent))) + return -ENOMEM; + + if (add_uevent_var(env, "UB_ENTITY_NAME=%s", ub_name(uent))) + return -ENOMEM; + + if (add_uevent_var(env, "MODALIAS=ub:v%04Xd%04Xmv%04Xm%04Xc%04X", + uent_vendor(uent), uent_device(uent), + uent->mod_vendor, uent->module, uent_class(uent))) + return -ENOMEM; + + return 0; +} + +void ub_bus_type_init(void) +{ + ub_bus_type.match = ub_bus_match; + ub_bus_type.uevent = ub_uevent; + ub_bus_type.probe = ub_entity_probe; + ub_bus_type.remove = ub_entity_remove; + ub_bus_type.shutdown = ub_entity_shutdown; +} + +void ub_bus_type_uninit(void) +{ + ub_bus_type.match = NULL; + ub_bus_type.uevent = NULL; + ub_bus_type.probe = NULL; + ub_bus_type.remove = NULL; + ub_bus_type.shutdown = NULL; +} + +int ub_host_probe(void) +{ + ub_bus_type_init(); + return 0; +} +EXPORT_SYMBOL_GPL(ub_host_probe); + +void ub_host_remove(void) +{ + ub_bus_type_uninit(); +} +EXPORT_SYMBOL_GPL(ub_host_remove); + +static int __init ubus_driver_init(void) +{ + pr_info("Ubus driver init successfully.\n"); + return 0; +} + +static void __exit ubus_driver_exit(void) +{ + pr_info("Ubus driver exit successfully.\n"); +} +module_init(ubus_driver_init); +module_exit(ubus_driver_exit); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("UB bus driver"); +MODULE_IMPORT_NS(UB_UBUS); diff --git a/drivers/ub/ubus/ubus_inner.h b/drivers/ub/ubus/ubus_inner.h new file mode 100644 index 000000000000..813cdcf70b16 --- /dev/null +++ b/drivers/ub/ubus/ubus_inner.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ +#ifndef __UBUS_INNER_H__ +#define __UBUS_INNER_H__ + +#include + +#endif /* __UBUS_INNER_H__ */ diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 0f51bc24ae59..de678c6b4348 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -948,4 +948,26 @@ struct cdx_device_id { __u32 override_only; }; +#define UB_ANY_ID (~0) + +/** + * struct ub_device_id - UB device identifier + * @vendor: Vendor ID + * @device: Device ID + * @mod_vendor: Module Vendor ID + * @module: Module ID + * @class_code: Device class base code and sub code to match. See + * include/ub/ubus/ubus_ids.h for a full list of classes. + * @class_mask: Limit which sub-fields of the class code field are + * compared. + * @override_only: Match only when dev->driver_override is this driver. + */ +struct ub_device_id { + __u32 vendor, device; /* Vendor and device ID or UB_ANY_ID*/ + __u32 mod_vendor, module; /* Module ID's or UB_ANY_ID */ + __u16 class_code, class_mask; /* Base code and sub code */ + unsigned long driver_data; /* Data private to the driver */ + __u32 override_only; +}; + #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/uapi/ub/ubus/ubus_regs.h b/include/uapi/ub/ubus/ubus_regs.h new file mode 100644 index 000000000000..9eed901fd205 --- /dev/null +++ b/include/uapi/ub/ubus/ubus_regs.h @@ -0,0 +1,279 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef _UAPI_UB_UBUS_UBUS_REGS_H_ +#define _UAPI_UB_UBUS_UBUS_REGS_H_ + +enum ub_cfg_cap_id { + UB_CFG0_BASIC_CAP = 0x000, + UB_SHP_CAP = 0x002, + UB_ERR_RECORD_CAP = 0x003, + UB_ERR_INFO_CAP = 0x004, + UB_EMQ_CAP = 0x005, + UB_CFG1_BASIC_CAP = 0x100, + UB_DECODER_CAP = 0x101, + UB_JETTY_CAP = 0x102, + UB_INT_TYPE1_CAP = 0x103, + UB_INT_TYPE2_CAP = 0x104, + UB_MEM_CAP = 0x106, +}; + +enum ub_port_cap_id { + UB_PORT_CAP15_QDLWS = 15, +}; + +#define UB_SLICE_SZ 0x00000400 +#define UB_CFG_BITMAP_SIZE 32 +#define UB_ROUTE_TABLE_SZ 0x40000000 +#define UB_CFG0_SUPPORT_FEATURE_SIZE 16 +#define UB_GUID_SIZE 16 +#define UB_EID_SIZE 16 +#define UB_FM_EID_SIZE 16 +#define UB_CFG1_SUPPORT_FEATURE_SIZE 16 +#define UB_PORT_SLICE_START 0x00080000 +#define UB_ROUTE_TABLE_SLICE_START 0x3c0000000 +#define UB_CFG_SAPCE_SLICE_END 0x400000000 +#define UB_ADDR_TO_POS(a) ((a) << 2) +#define UB_PORT_SLICE_SIZE UB_ADDR_TO_POS(0x10000) + +/* CFG0 BASIC */ +#define UB_CFG0_BASIC_SLICE 0x0 /* 0x0 */ +#define UB_TOTAL_NUMBER_PORT 0x4 /* 0x1 */ +#define UB_TOTAL_NUMBER_ENTITIES 0x6 +#define UB_CFG0_CAP_BITMAP 0x8 /* 0x2 */ +#define UB_FEATURE_SUPPORT_0 0x28 /* 0xA */ +#define UB_SW_SUPPORT BIT(7) +#define UB_CC_SUPPORT BIT(9) +#define UB_GUID 0x38 /* 0xE */ +#define UB_EID_0 0x48 /* 0x12 */ +#define UB_EID_3 0x54 /* 0x15 */ +#define UB_FM_EID_0 0x58 /* 0x16 */ +#define UB_PRIMARY_CNA 0x68 /* 0x1A */ +#define UB_PRIMARY_CNA_MASK GENMASK(23, 0) +#define UB_UPI 0x7c /* 0x1f */ +#define UB_MODULE 0x80 /* 0x20 */ +#define UB_MODULE_ID_MASK GENMASK(15, 0) +#define UB_ENTITY_RST 0x84 /* 0x21 */ +#define UB_ENTITY_RST_BIT 0x1 +#define UB_MTU_CFG 0x8c /* 0x23 */ +#define UB_MTU_CFG_BITS 0x7 +#define UB_CC_EN 0x90 /* 0x24 */ +#define UB_CC_EN_BIT 0x1 +#define UB_TH_EN 0x94 /* 0x25 */ +#define UB_TH_EN_BIT 0x1 +#define UB_FM_CNA 0x98 /* 0x26 */ +#define UB_FM_CNA_MASK GENMASK(23, 0) +#define UB_UEID_0 0x9c /* 0x27 */ +#define UB_UEID_1 0xa0 /* 0x28 */ +#define UB_UEID_2 0xa4 /* 0x29 */ +#define UB_UEID_3 0xa8 /* 0x2A */ +#define UB_UCNA 0xac /* 0x2B */ +#define UB_UCNA_MASK GENMASK(23, 0) + +/* SHP CAP */ +#define UB_SLOT_START UB_ADDR_TO_POS(0x200) +#define UB_SLOT_POS UB_ADDR_TO_POS(0x10) +#define UB_SLOT_NUM UB_ADDR_TO_POS(0x1) +#define UB_SLOT_CAP UB_ADDR_TO_POS(0x2) +#define UB_SLOT_PPS 0x1 +#define UB_SLOT_WLPS 0x2 +#define UB_SLOT_PLPS 0x4 +#define UB_SLOT_PDSS 0x8 +#define UB_SLOT_PORT UB_ADDR_TO_POS(0x3) +#define UB_SLOT_START_PORT 0x0000ffff +#define UB_SLOT_PP_CTRL UB_ADDR_TO_POS(0x4) +#define UB_SLOT_PP_CTRL_MASK BIT(0) +#define UB_SLOT_WL_CTRL UB_ADDR_TO_POS(0x5) +#define UB_SLOT_WL_CTRL_MASK GENMASK(1, 0) +#define UB_SLOT_PL_CTRL UB_ADDR_TO_POS(0x6) +#define UB_SLOT_PL_CTRL_MASK GENMASK(1, 0) +#define UB_SLOT_MS_CTRL UB_ADDR_TO_POS(0x7) +#define UB_SLOT_MS_CTRL_MASK BIT(0) +#define UB_SLOT_PD_CTRL UB_ADDR_TO_POS(0x8) +#define UB_SLOT_PD_CTRL_MASK BIT(0) +#define UB_SLOT_PDS_CTRL UB_ADDR_TO_POS(0x9) +#define UB_SLOT_PDS_CTRL_MASK BIT(0) +#define UB_SLOT_PW_CTRL UB_ADDR_TO_POS(0xA) +#define UB_SLOT_PP_STA UB_ADDR_TO_POS(0xB) +#define UB_SLOT_PP_STA_MASK BIT(0) +#define UB_SLOT_PD_STA UB_ADDR_TO_POS(0xC) +#define UB_SLOT_PD_STA_MASK BIT(0) +#define UB_SLOT_PDSC_STA UB_ADDR_TO_POS(0xD) +#define UB_SLOT_PDSC_STA_MASK BIT(0) + +/* CFG1 BASIC */ +#define UB_CFG1_BASIC 0x40000 +#define UB_CFG1_BASIC_SLICE (0x0 + UB_CFG1_BASIC) /* 0x0 */ +#define UB_CFG1_CAP_BITMAP (0x4 + UB_CFG1_BASIC) /* 0x1 */ +#define UB_DECODER_CAP_BIT 0x2 +#define UB_JETTY_CAP_BIT 0x4 +#define UB_INT_TYPE1_CAP_BIT 0x8 +#define UB_INT_TYPE2_CAP_BIT 0x10 +#define UB_MEM_CAP_BIT 0x40 +#define UB_CFG1_SUPPORT_FEATURE_L (0x24 + UB_CFG1_BASIC) /* 0x9 */ +#define UB_VDBS_SUPPORT 0x1 +#define UB_VDSS_SUPPORT 0x2 +#define UB_MGS_SUPPORT 0x4 +#define UB_UBBAS_SUPPORT 0x20 +#define UB_ERS0S_SUPPORT 0x40 +#define UB_ERS1S_SUPPORT 0x80 +#define UB_ERS2S_SUPPORT 0x100 +#define UB_CDMAS_SUPPORT 0x200 +#define UB_DECODER_JURIS 0x400 +#define UB_ERS0_SS (0x34 + UB_CFG1_BASIC) /* 0xD */ +#define UB_ERS1_SS (0x38 + UB_CFG1_BASIC) /* 0xE */ +#define UB_ERS2_SS (0x3c + UB_CFG1_BASIC) /* 0xF */ +#define UB_ERS0_SA_L (0x40 + UB_CFG1_BASIC) /* 0x10 */ +#define UB_ERS0_SA_H (0x44 + UB_CFG1_BASIC) /* 0x11 */ +#define UB_ERS1_SA_L (0x48 + UB_CFG1_BASIC) /* 0x12 */ +#define UB_ERS1_SA_H (0x4c + UB_CFG1_BASIC) /* 0x13 */ +#define UB_ERS2_SA_L (0x50 + UB_CFG1_BASIC) /* 0x14 */ +#define UB_ERS2_SA_H (0x54 + UB_CFG1_BASIC) /* 0x15 */ +#define UB_ERS0_UBBA_L (0x58 + UB_CFG1_BASIC) /* 0x16 */ +#define UB_ERS0_UBBA_H (0x5c + UB_CFG1_BASIC) /* 0x17 */ +#define UB_ERS1_UBBA_L (0x60 + UB_CFG1_BASIC) /* 0x18 */ +#define UB_ERS1_UBBA_H (0x64 + UB_CFG1_BASIC) /* 0x19 */ +#define UB_ERS2_UBBA_L (0x68 + UB_CFG1_BASIC) /* 0x1A */ +#define UB_ERS2_UBBA_H (0x6c + UB_CFG1_BASIC) /* 0x1B */ +#define UB_ELR (0x70 + UB_CFG1_BASIC) /* 0x1C */ +#define UB_ELR_BIT 0x1 +#define UB_ELR_DONE (0x74 + UB_CFG1_BASIC) /* 0x1D */ +#define UB_ELR_DONE_BIT 0x1 +#define UB_SYS_PGS (0x84 + UB_CFG1_BASIC) /* 0x21 */ +#define UB_SYS_PGS_SIZE 0x1 +#define UB_EU_TBA_L (0x88 + UB_CFG1_BASIC) /* 0x22 */ +#define UB_EU_TBA_H (0x8c + UB_CFG1_BASIC) /* 0x23 */ +#define UB_EU_TEN (0x90 + UB_CFG1_BASIC) /* 0x24 */ +#define UB_CLASS_CODE (0xa4 + UB_CFG1_BASIC) /* 0x29 */ +#define UB_BASE_CODE_MASK GENMASK(7, 0) +#define UB_SUB_CODE_MASK GENMASK(15, 8) +#define UB_ENTITY_TOKEN_ID (0xb4 + UB_CFG1_BASIC) /* 0x2D */ +#define UB_TOKEN_ID_MASK GENMASK(19, 0) +#define UB_BUS_ACCESS_EN (0xb8 + UB_CFG1_BASIC) /* 0x2E */ +#define UB_BUS_ACCESS_EN_BIT 0x1 +#define UB_ENTITY_RS_ACCESS_EN (0xbc + UB_CFG1_BASIC) /* 0x2F */ +#define UB_ENTITY_RS_ACCESS_EN_BIT 0x1 + +/* DECODER CAP */ +#define DECODER_CAP_START 0x00040400 +#define DECODER_POS(pos) (DECODER_CAP_START + ((pos) << 2)) +#define DECODER_CAP DECODER_POS(0x1) +#define DECODER_CTRL DECODER_POS(0x2) +#define DECODER_MATT_BA0 DECODER_POS(0x3) +#define DECODER_MATT_BA1 DECODER_POS(0x4) +#define DECODER_MMIO_BA0 DECODER_POS(0x5) +#define DECODER_MMIO_BA1 DECODER_POS(0x6) +#define DECODER_USI_IDX DECODER_POS(0x7) +#define DECODER_CMDQ_CFG DECODER_POS(0x10) +#define DECODER_CMDQ_PROD DECODER_POS(0x11) +#define DECODER_CMDQ_CONS DECODER_POS(0x12) +#define DECODER_CMDQ_BASE_ADDR0 DECODER_POS(0x13) +#define DECODER_CMDQ_BASE_ADDR1 DECODER_POS(0x14) +#define DECODER_EVENTQ_CFG DECODER_POS(0x20) +#define DECODER_EVENTQ_PROD DECODER_POS(0x21) +#define DECODER_EVENTQ_CONS DECODER_POS(0x22) +#define DECODER_EVENTQ_BASE_ADDR0 DECODER_POS(0x23) +#define DECODER_EVENTQ_BASE_ADDR1 DECODER_POS(0x24) + +/* INT_TYPE1_CAP */ +#define UB_CFG1_INT_TYPE1_CAP 0x40c00 +#define UB_INT_TYPE1_ENABLE (0x4 + UB_CFG1_INT_TYPE1_CAP) /* 0x1 */ +#define UB_INT_TYPE1_SUP_INT_NUM (0x8 + UB_CFG1_INT_TYPE1_CAP) /* 0x2 */ +#define UB_INT_TYPE1_EN_INT_NUM (0xC + UB_CFG1_INT_TYPE1_CAP) /* 0x3 */ +#define UB_INT_TYPE1_INT_DATA (0x10 + UB_CFG1_INT_TYPE1_CAP) /* 0x4 */ +#define UB_INT_TYPE1_INT_ADDR_L (0x14 + UB_CFG1_INT_TYPE1_CAP) /* 0x5 */ +#define UB_INT_TYPE1_INT_ADDR_H (0x18 + UB_CFG1_INT_TYPE1_CAP) /* 0x6 */ +#define UB_INT_TYPE1_INT_ID (0x1c + UB_CFG1_INT_TYPE1_CAP) /* 0x7 */ +#define UB_INT_TYPE1_INT_MASK (0x20 + UB_CFG1_INT_TYPE1_CAP) /* 0x8 */ +#define UB_INT_TYPE1_INT_PENDING (0x24 + UB_CFG1_INT_TYPE1_CAP) /* 0x9 */ + +/* INT_TYPE2_CAP */ +#define UB_CFG1_CAP_INT_CAP 0x41000 +#define UB_INT_CAP_SLICE (0x0 + UB_CFG1_CAP_INT_CAP) /* 0x0 */ +#define UB_INT_INFO (0x4 + UB_CFG1_CAP_INT_CAP) /* 0x1 */ +#define UB_NUM_OF_INTR_VECTOR_TBL (0x4 + UB_CFG1_CAP_INT_CAP) +#define UB_NUM_OF_INTR_ADDR_TBL (0x6 + UB_CFG1_CAP_INT_CAP) +#define UB_INT_VECTOR_TBL_SA_L (0x8 + UB_CFG1_CAP_INT_CAP) /* 0x2 */ +#define UB_INT_VECTOR_TBL_SA_H (0xc + UB_CFG1_CAP_INT_CAP) /* 0x3 */ +#define UB_INT_ADDR_TBL_SA_L (0x10 + UB_CFG1_CAP_INT_CAP) /* 0x4 */ +#define UB_INT_ADDR_TBL_SA_H (0x14 + UB_CFG1_CAP_INT_CAP) /* 0x5 */ +#define UB_INT_PENDING_TBL_SA_L (0x18 + UB_CFG1_CAP_INT_CAP) /* 0x6 */ +#define UB_INT_PENDING_TBL_SA_H (0x1c + UB_CFG1_CAP_INT_CAP) /* 0x7 */ +#define UB_INT_ID (0x20 + UB_CFG1_CAP_INT_CAP) /* 0x8 */ +#define UB_FUN_EN_BIT 0 +#define UB_FUN_MASK_BIT 1 +#define UB_INT_MASK (0x24 + UB_CFG1_CAP_INT_CAP) /* 0x9 */ +#define UB_INT_MASK_BIT 0x1 +#define UB_INT_EN (0x28 + UB_CFG1_CAP_INT_CAP) /* 0xA */ +#define UB_INT_EN_BIT 0x1 + +/* UB_MEM_CAP */ +#define UB_CFG1_CAP_UB_MEM 0x41800 +#define UB_MEM_USI_IDX (0x4 + UB_CFG1_CAP_UB_MEM) + +/* ub interrupt vector entry format */ +#define UB_INTR_VECTOR_ENTRY_SIZE 0x8 +#define UB_INTR_VECTOR_ID 0x0 +#define UB_INTR_VECTOR_ADDR_INDEX 0x4 +#define UB_INTR_VECTOR_ADDR_INDEX_MASK GENMASK(15, 0) +#define UB_INTR_VECTOR_MASK_MASK BIT(16) +/* ub interrupt addr entry format */ +#define UB_INTR_ADDR_ENTRY_SIZE 0x20 +#define UB_INTR_ADDR_ADDR_L 0x0 +#define UB_INTR_ADDR_ADDR_H 0x4 +#define UB_INTR_ADDR_TOKENID 0x8 +#define UB_INTR_ADDR_TOKENID_MASK GENMASK(19, 0) +#define UB_INTR_ADDR_VALID_BIT 20 +#define UB_INTR_ADDR_VALID_MASK BIT(20) +#define UB_INTR_ADDR_DSTEID_0 0xc +#define UB_INTR_ADDR_DSTEID_1 0x10 +#define UB_INTR_ADDR_DSTEID_2 0x14 +#define UB_INTR_ADDR_DSTEID_3 0x18 + +/* PORT BASIC */ +#define UB_CFG0_PORT_SLICE 0x0 +#define UB_CFG0_PORT_BITMAP 0x4 /* 0x1 */ +#define UB_PORT_INFO 0x24 /* 0x9 */ +#define UB_PORT_TYPE 0x26 /* 0x9 */ +#define UB_PORT_TYPE_MASK BIT(0) +#define UB_NEIGHBOR_PORT_INFO 0x28 /* 0xA */ +#define UB_UB_NEIGHBOR_PORT_INFO_SIZE 0x14 +#define UB_PORT_NET_ADDR 0x3c /* 0xF */ +#define UB_PORT_NET_ADDR_MASK GENMASK(23, 0) +#define UB_PORT_RST 0x40 /* 0x10 */ +#define UB_PORT_RST_BIT 0x1 + +/* PORT CAP1 LINK */ +/* Physical Port Link Status */ +#define UB_PORT_PHYSICAL_PORT_LINK_STATUS (0x1c0 << 2) +#define UB_PORT_LINK_STATE 0x1 + +/* PORT CAP15 QDLWS */ +#define PORT_CAP15_QDLWS_CAP (0xf01 << 2) +#define PORT_CAP15_QDLWS_CTRL (0xf02 << 2) +#define PORT_CAP15_QDLWS_STATE (0xf03 << 2) +#define ASY_LINK_WIDTH_MASK BIT(0) +#define GLB_QDLWS_MASK BIT(0) +#define GLB_QDLWS_ENABLE_MASK BIT(0) +#define GLB_QDLWS_DISABLE_MASK GENMASK(31, 1) +#define TX_QDLWS_MASK BIT(1) +#define TX_QDLWS_ENABLE_MASK BIT(1) +#define TX_QDLWS_DISABLE_MASK 0xFFFFFFFD +#define RX_QDLWS_MASK BIT(2) +#define RX_QDLWS_ENABLE_MASK BIT(2) +#define RX_QDLWS_DISABLE_MASK 0xFFFFFFFB +#define TX_WIDTH_MASK GENMASK(21, 16) +#define TX_WIDTH_OFFSET 16 +#define RX_WIDTH_MASK GENMASK(29, 24) +#define RX_WIDTH_OFFSET 24 +#define QDLWS_EXEC_STATUS_MASK GENMASK(2, 0) +#define QDLWS_EXEC_STATUS_MAX 4 + +/* Error Message Queue Capability */ +#define EMQ_CAP_START 0x00001400 +#define UB_CAP_ERR_MSG_QUE_CTL 0x8 +#define UB_CAP_INTERRUPT_GEN_ENA 0x100 + +#endif /* _UAPI_UB_UBUS_UBUS_REGS_H_ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h new file mode 100644 index 000000000000..68cbc445a853 --- /dev/null +++ b/include/ub/ubus/ubus.h @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef _UB_UBUS_UBUS_H_ +#define _UB_UBUS_UBUS_H_ + +#include +#include +#include +#include +#include +#include +#include + +#define UB_ENTITY(v, d) \ + .vendor = (v), .device = (d), \ + .mod_vendor = (u32)UB_ANY_ID, .module = (u32)UB_ANY_ID + +#define UB_ENTITY_MODULE(v, d, m_v, m) \ + .vendor = (v), .device = (d), \ + .mod_vendor = (m_v), .module = (m) + +#define UB_ENTITY_CLASS(dev_class, dev_class_mask) \ + .vendor = (u32)UB_ANY_ID, .device = (u32)UB_ANY_ID, \ + .mod_vendor = (u32)UB_ANY_ID, .module = (u32)UB_ANY_ID, \ + .class_code = (dev_class), .class_mask = (dev_class_mask) + +#define UB_GUID_DW_NUM SZ_4 + +struct ub_guid { + union { + struct { + unsigned long long seq_num; + unsigned int reserved : 24; + unsigned int type : 4; + unsigned int version : 4; + unsigned int device : 16; + unsigned int vendor : 16; + } bits; + + guid_t id; + + unsigned int dw[UB_GUID_DW_NUM]; + }; +}; + +#define uent_vendor(uent) ((uent)->guid.bits.vendor) +#define uent_type(uent) ((uent)->guid.bits.type) +#define uent_version(uent) ((uent)->guid.bits.version) +#define uent_device(uent) ((uent)->guid.bits.device) +#define uent_class(uent) ((uent)->class_code) +#define uent_base_code(uent) ((uent)->class_code & UB_BASE_CODE_MASK) +#define uent_seq(uent) ((uent)->guid.bits.seq_num) + +struct ub_entity { + /* Driver framework base info */ + struct device dev; + struct ub_driver *driver; + bool match_driver; /* Skip attaching driver before dev ready */ + const char *driver_override; /* Driver name to force a match */ + + /* entity base info */ + struct ub_guid guid; + u16 class_code; + u16 mod_vendor; /* entity's module vendor and module id */ + u16 module; +}; + +#define to_ub_entity(n) container_of(n, struct ub_entity, dev) + +/** + * struct ub_driver - UB driver structure + * @node: List of driver structures. + * @name: Driver name. + * @id_table: Pointer to table of device IDs the driver is + * interested in. Most drivers should export this + * table using MODULE_DEVICE_TABLE(ub,...). + * @probe: This probing function gets called (during execution + * of ub_register_driver() for already existing + * entities or later if a new entity gets inserted) for + * all UB entities which match the ID table and are not + * "owned" by the other drivers yet. This function gets + * passed a "struct ub_entity *" for each entity whose + * entry in the ID table matches the entity. The probe + * function returns zero when the driver chooses to + * take "ownership" of the entity or an error code + * (negative number) otherwise. + * The probe function always gets called from process + * context, so it can sleep. + * @remove: The remove() function gets called whenever an entity + * being handled by this driver is removed (either during + * deregistration of the driver or when it's manually + * removed from a hot-pluggable slot). + * The remove function always gets called from process + * context, so it can sleep. + * @shutdown: Hook into reboot_notifier_list (kernel/sys.c). + * Intended to stop any idling operations. + * @driver: Driver model structure. + */ +struct ub_driver { + struct list_head node; + const char *name; + const struct ub_device_id *id_table; /* Must be non-NULL for probe to be called */ + /* New entity inserted */ + int (*probe)(struct ub_entity *uent, const struct ub_device_id *id); + /* entity removed (NULL if not a hot-plug capable driver) */ + void (*remove)(struct ub_entity *uent); + void (*shutdown)(struct ub_entity *uent); + struct device_driver driver; +}; + +static inline struct ub_driver *to_ub_driver(struct device_driver *drv) +{ + return drv ? container_of(drv, struct ub_driver, driver) : NULL; +} + +#define ub_err(pue, fmt, arg...) dev_err(&(pue)->dev, fmt, ##arg) +#define ub_info(pue, fmt, arg...) dev_info(&(pue)->dev, fmt, ##arg) +#define ub_warn(pue, fmt, arg...) dev_warn(&(pue)->dev, fmt, ##arg) +#define ub_dbg(pue, fmt, arg...) dev_dbg(&(pue)->dev, fmt, ##arg) + +static inline const char *ub_name(const struct ub_entity *pue) +{ + return dev_name(&pue->dev); +} + +#ifdef CONFIG_UB_UBUS +extern struct bus_type ub_bus_type; +#define dev_is_ub(d) ((d)->bus == &ub_bus_type) + +/** + * ub_entity_get() - Atomically increment the reference count for the entity. + * @uent: UB entity pointer. + * + * Context: Any context. + * Return: uent, or NULL if @uent is NULL. + */ +struct ub_entity *ub_entity_get(struct ub_entity *uent); + +/** + * ub_entity_put() - decrement the reference count for the entity. + * @uent: UB entity pointer. + * + * Context: Any context. + */ +void ub_entity_put(struct ub_entity *uent); + +/* Proper probing supporting hot-pluggable entities */ +int __ub_register_driver(struct ub_driver *drv, struct module *owner, + const char *mod_name); +/* ub_register_driver() must be a macro so KBUILD_MODNAME can be expanded */ +#define ub_register_driver(driver) \ + __ub_register_driver(driver, THIS_MODULE, KBUILD_MODNAME) +void ub_unregister_driver(struct ub_driver *drv); + +#else /* CONFIG_UB_UBUS is not enabled */ +#define dev_is_ub(d) (false) +static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) +{ return NULL; } +static inline void ub_entity_put(struct ub_entity *uent) {} +static inline int +__ub_register_driver(struct ub_driver *drv, struct module *owner, + const char *mod_name) +{ return 0; } +static inline int ub_register_driver(struct ub_driver *drv) +{ return 0; } +static inline void ub_unregister_driver(struct ub_driver *drv) {} +#endif /* CONFIG_UB_UBUS */ + +#endif /* _UB_UBUS_UBUS_H_ */ diff --git a/include/ub/ubus/ubus_ids.h b/include/ub/ubus/ubus_ids.h new file mode 100644 index 000000000000..5e5158ba5527 --- /dev/null +++ b/include/ub/ubus/ubus_ids.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ +#ifndef _UB_UBUS_UBUS_IDS_H_ +#define _UB_UBUS_UBUS_IDS_H_ + +/* + * Device Class base code and sub code + * +----------+-----------+ + * | sub code | base code | + * | [15:8] | [7:0] | + * +----------+-----------+ + */ + +#define UB_BASE_CODE_BUS_CONTROLLER 0x00 +#define UB_CLASS_BUS_CONTROLLER 0x0000 + +#define UB_BASE_CODE_STORAGE 0x01 +#define UB_CLASS_STORAGE_LPC 0x0001 +#define UB_CLASS_STORAGE_LBC 0x0101 +#define UB_CLASS_STORAGE_RAID 0x0201 + +#define UB_BASE_CODE_NETWORK 0x02 +#define UB_CLASS_NETWORK_UB 0x0002 +#define UB_CLASS_NETWORK_ETH 0x0102 + +#define UB_BASE_CODE_DISPLAY 0x03 + +#define UB_BASE_CODE_SWITCH 0x04 +#define UB_CLASS_SWITCH_UB 0x0004 + +#define UB_BASE_CODE_VIRTIO 0x05 +#define UB_CLASS_LEGACY_VIRTIO_NETWORK 0x0005 +#define UB_CLASS_LEGACY_VIRTIO_BLOCK 0x0105 +#define UB_CLASS_LEGACY_VIRTIO_SCSI 0x0205 +#define UB_CLASS_LEGACY_VIRTIO_GRAPHIC 0x0305 +#define UB_CLASS_LEGACY_VIRTIO_SOCKET 0x0405 +#define UB_CLASS_LEGACY_VIRTIO_FS 0x0505 + +#define UB_BASE_CODE_VIRTUAL 0x06 + +#define UB_BASE_CODE_NPU 0x07 +#define UB_CLASS_NPU_UB 0x0007 + +#define UB_BASE_CODE_UNKNOWN 0xFF +#define UB_CLASS_UNKNOWN 0x00FF + +#endif /* _UB_UBUS_UBUS_IDS_H_ */ diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index abe65f8968dd..d445884800c8 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c @@ -267,5 +267,16 @@ int main(void) DEVID_FIELD(cdx_device_id, device); DEVID_FIELD(cdx_device_id, override_only); +#ifdef CONFIG_UB + DEVID(ub_device_id); + DEVID_FIELD(ub_device_id, vendor); + DEVID_FIELD(ub_device_id, device); + DEVID_FIELD(ub_device_id, mod_vendor); + DEVID_FIELD(ub_device_id, module); + DEVID_FIELD(ub_device_id, class_code); + DEVID_FIELD(ub_device_id, class_mask); + DEVID_FIELD(ub_device_id, override_only); +#endif + return 0; } diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index ea498eff1f2a..5acd60a148d1 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1475,6 +1475,51 @@ static int do_cdx_entry(const char *filename, void *symval, return 1; } +#ifdef CONFIG_UB +/* Looks like: ub:vNdNmvNmNcN. */ +static int do_ub_entry(const char *filename, void *symval, char *alias) +{ + /* Class code field can be divided into these two. */ + unsigned char basecode_mask, subcode_mask; + + DEF_FIELD(symval, ub_device_id, vendor); + DEF_FIELD(symval, ub_device_id, device); + DEF_FIELD(symval, ub_device_id, mod_vendor); + DEF_FIELD(symval, ub_device_id, module); + DEF_FIELD(symval, ub_device_id, class_code); + DEF_FIELD(symval, ub_device_id, class_mask); + DEF_FIELD(symval, ub_device_id, override_only); + + switch (override_only) { + case 0: + strcpy(alias, "ub:"); + break; + default: + warn("Unknown UB driver_override alias %08X\n", + override_only); + return 0; + } + + ADD(alias, "v", vendor != UB_ANY_ID, (__u16)vendor); + ADD(alias, "d", device != UB_ANY_ID, (__u16)device); + ADD(alias, "mv", mod_vendor != UB_ANY_ID, (__u16)mod_vendor); + ADD(alias, "m", module != UB_ANY_ID, (__u16)module); + + basecode_mask = class_mask; + subcode_mask = (class_mask) >> 8; + + if ((basecode_mask != 0 && basecode_mask != 0xFF) + || (subcode_mask != 0 && subcode_mask != 0xFF)) { + warn("Can't handle masks in %s:%04X\n", + filename, class_mask); + return 0; + } + + ADD(alias, "c", subcode_mask == 0xFF && basecode_mask == 0xFF, class_code); + return 1; +} +#endif + /* Does namelen bytes of name exactly match the symbol? */ static bool sym_is(const char *name, unsigned namelen, const char *symbol) { @@ -1555,6 +1600,9 @@ static const struct devtable devtable[] = { {"dfl", SIZE_dfl_device_id, do_dfl_entry}, {"ishtp", SIZE_ishtp_device_id, do_ishtp_entry}, {"cdx", SIZE_cdx_device_id, do_cdx_entry}, +#ifdef CONFIG_UB + {"ub", SIZE_ub_device_id, do_ub_entry}, +#endif }; /* Create MODULE_ALIAS() statements. -- Gitee From 25764cfe9d91fbc744cb5f360431c5416ae2af85 Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Thu, 18 Sep 2025 22:31:41 +0800 Subject: [PATCH 002/214] ub:ubfi: Support ub firmware interface basic functions ANBZ: #45460 commit 22672eaacb201846296cbfec7f1392cdf978b2d0 openEuler. Support UnifiedBus firmware interface basic functions, set bios mode, get ub root table by dts or acpi, handle dts-ubrt or acpi-ubrt Signed-off-by: Yuhao Xiang Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- .../configs/L1-RECOMMEND/arm64/CONFIG_UB_UBFI | 1 + drivers/ub/Kconfig | 1 + drivers/ub/Makefile | 1 + drivers/ub/ubfi/Kconfig | 18 +++ drivers/ub/ubfi/Makefile | 4 + drivers/ub/ubfi/ub_fi.c | 127 ++++++++++++++++ drivers/ub/ubfi/ub_fi.h | 17 +++ drivers/ub/ubfi/ubrt.c | 143 ++++++++++++++++++ drivers/ub/ubfi/ubrt.h | 63 ++++++++ drivers/ub/ubus/ubus_driver.c | 1 + 10 files changed, 376 insertions(+) create mode 100644 anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UBFI create mode 100644 drivers/ub/ubfi/Kconfig create mode 100644 drivers/ub/ubfi/Makefile create mode 100644 drivers/ub/ubfi/ub_fi.c create mode 100644 drivers/ub/ubfi/ub_fi.h create mode 100644 drivers/ub/ubfi/ubrt.c create mode 100644 drivers/ub/ubfi/ubrt.h diff --git a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UBFI b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UBFI new file mode 100644 index 000000000000..c2daa7a5232f --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UBFI @@ -0,0 +1 @@ +CONFIG_UB_UBFI=m diff --git a/drivers/ub/Kconfig b/drivers/ub/Kconfig index fc591be43597..d876a10ca180 100644 --- a/drivers/ub/Kconfig +++ b/drivers/ub/Kconfig @@ -14,4 +14,5 @@ menuconfig UB if UB source "drivers/ub/ubus/Kconfig" +source "drivers/ub/ubfi/Kconfig" endif # UB diff --git a/drivers/ub/Makefile b/drivers/ub/Makefile index 10428ed28774..7860f8e74a47 100644 --- a/drivers/ub/Makefile +++ b/drivers/ub/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0+ obj-y += ubus/ +obj-y += ubfi/ diff --git a/drivers/ub/ubfi/Kconfig b/drivers/ub/ubfi/Kconfig new file mode 100644 index 000000000000..4cb8a264fe9a --- /dev/null +++ b/drivers/ub/ubfi/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# UnifiedBus firmware interface driver configuration +# + +if UB_UBUS + +config UB_UBFI + tristate "UnifiedBus firmware interface driver" + depends on UB_UBUS + default n + help + This option support for UnifiedBus firmware interface driver. + If you have ub function say y and it will be accessible from + within Linux. To compile this driver as a module, choose M here. + Say 'M' here unless you know what you are doing + +endif diff --git a/drivers/ub/ubfi/Makefile b/drivers/ub/ubfi/Makefile new file mode 100644 index 000000000000..f06b3fbfddb1 --- /dev/null +++ b/drivers/ub/ubfi/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0+ + +ubfi-y := ub_fi.o ubrt.o +obj-$(CONFIG_UB_UBFI) += ubfi.o diff --git a/drivers/ub/ubfi/ub_fi.c b/drivers/ub/ubfi/ub_fi.c new file mode 100644 index 000000000000..50b359e52b7b --- /dev/null +++ b/drivers/ub/ubfi/ub_fi.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubfi driver: " fmt + +#include +#include +#include + +#include "ubrt.h" +#include "ub_fi.h" + +#define ACPI_SIG_UBRT "UBRT" /* UB Root Table */ +#define UBIOS_INFO_TABLE "linux,ubios-information-table" + +enum bios_report_mode bios_mode = UNKNOWN; + +static void ub_bios_mode_init(void) +{ + if (acpi_disabled) + bios_mode = DTS; + else + bios_mode = ACPI; + + pr_info("Starting with mode: %d\n", bios_mode); +} + +static int ubfi_get_acpi_ubrt(void) +{ + struct acpi_table_header *header; + acpi_status status; + + status = acpi_get_table(ACPI_SIG_UBRT, 0, &header); + if (ACPI_FAILURE(status)) { + pr_err("ACPI failed to get UBRT.\n"); + if (status != AE_NOT_FOUND) + pr_err("ACPI failed msg: %s\n", + acpi_format_exception(status)); + return -ENODEV; + } + acpi_table = (struct acpi_table_ubrt *)header; + pr_info("get ubrt by acpi success\n"); + return 0; +} + +static int ubfi_get_dts_ubrt(void) +{ + struct device_node *node; + u64 phys_addr; + + node = of_find_node_by_path("/chosen"); + if (!node) { + pr_err("Failed to get device tree chosen node\n"); + return -EINVAL; + } + + if (of_property_read_u64(node, UBIOS_INFO_TABLE, &phys_addr)) { + pr_err("Failed to get %s node\n", UBIOS_INFO_TABLE); + return -EINVAL; + } + + ubios_table = (struct ubios_root_table *)ub_table_get(phys_addr); + if (!ubios_table) + return -ENOMEM; + + pr_info("ubfi get ubrt by device tree success\n"); + return 0; +} + +static int ubfi_get_ubrt(void) +{ + if (bios_mode == ACPI) + return ubfi_get_acpi_ubrt(); + else if (bios_mode == DTS) + return ubfi_get_dts_ubrt(); + return -EINVAL; +} + +static int handle_ubrt(void) +{ + if (bios_mode == ACPI) + return handle_acpi_ubrt(); + else if (bios_mode == DTS) + return handle_dts_ubrt(); + + return -EINVAL; +} + +static void ubfi_put_ubrt(void) +{ + if (bios_mode == ACPI) { + acpi_put_table((struct acpi_table_header *)acpi_table); + acpi_table = NULL; + } else if (bios_mode == DTS) { + ub_table_put(ubios_table); + ubios_table = NULL; + } +} + +static int __init ubfi_init(void) +{ + int ret; + + ub_bios_mode_init(); + + ret = ubfi_get_ubrt(); + if (ret) { + pr_warn("can't get ub information from bios, ret=%d\n", ret); + return 0; + } + + return handle_ubrt(); +} + +static void __exit ubfi_exit(void) +{ + uninit_ub_nodes(); + ubfi_put_ubrt(); +} + +module_init(ubfi_init); +module_exit(ubfi_exit); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("UnifiedBus firmware interface driver"); +MODULE_IMPORT_NS(UB_UBFI); diff --git a/drivers/ub/ubfi/ub_fi.h b/drivers/ub/ubfi/ub_fi.h new file mode 100644 index 000000000000..3edf8dd6de4e --- /dev/null +++ b/drivers/ub/ubfi/ub_fi.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __UB_FI_H__ +#define __UB_FI_H__ + +enum bios_report_mode { + ACPI = 0, + DTS = 1, + UBIOS = 3, + UNKNOWN = 4, +}; + +extern enum bios_report_mode bios_mode; +#endif /* __UB_FI_H__ */ diff --git a/drivers/ub/ubfi/ubrt.c b/drivers/ub/ubfi/ubrt.c new file mode 100644 index 000000000000..d022ae96fbf9 --- /dev/null +++ b/drivers/ub/ubfi/ubrt.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubfi ubrt: " fmt + +#include +#include +#include + +#include "ubrt.h" + +#define UBIOS_SIG_UBC "ubc" + +struct acpi_table_ubrt *acpi_table; +struct ubios_root_table *ubios_table; + +/* + * ummu max count is 32, max size is 40 + 32 * 128 = 4640 + * ubc max count is 32, max size is 40 + 88 + 32 * 256 + 32 * 4 = 8448 + */ +#define UBIOS_TABLE_TOTLE_SIZE_MAX 8448 + +/* remember to use ub_table_put to release memory alloced by ub_table_get */ +void *ub_table_get(u64 pa) +{ + void __iomem *va; + u32 total_size; + void *ret; + + if (!pa) + return NULL; + + va = ioremap(pa, sizeof(struct ub_table_header)); + if (!va) { + pr_err("ioremap ub table header failed\n"); + return NULL; + } + + total_size = readl(va + UB_TABLE_HEADER_NAME_LEN); + pr_debug("ub table size is[0x%x]\n", total_size); + if (total_size == 0 || total_size > UBIOS_TABLE_TOTLE_SIZE_MAX) { + pr_err("ubios table size is invalid\n"); + iounmap(va); + return NULL; + } + iounmap(va); + + va = ioremap(pa, total_size); + if (!va) { + pr_err("ioremap full ub table failed\n"); + return NULL; + } + + ret = kzalloc(total_size, GFP_KERNEL); + if (!ret) { + iounmap(va); + return NULL; + } + + memcpy_fromio(ret, va, total_size); + iounmap(va); + return ret; +} + +void ub_table_put(void *va) +{ + kfree(va); +} + +void uninit_ub_nodes(void) +{ +} + +int handle_acpi_ubrt(void) +{ + struct ubrt_sub_table *sub_table; + int ret = 0; + u32 i; + + pr_info("acpi ubrt sub table count is %u\n", acpi_table->count); + + for (i = 0; i < acpi_table->count; i++) { + sub_table = &acpi_table->sub_table[i]; + switch (sub_table->type) { + case UB_BUS_CONTROLLER_TABLE: + break; + default: + pr_warn("Ignore sub table: type %u\n", sub_table->type); + break; + } + if (ret) { + pr_err("parse ubrt sub table type %u failed\n", + sub_table->type); + goto fail; + } + } + return ret; +fail: + uninit_ub_nodes(); + return ret; +} + +int handle_dts_ubrt(void) +{ + char name[UB_TABLE_HEADER_NAME_LEN] = {}; + struct ub_table_header *header; + int ret = 0, i; + + if (ubios_table->count == 0) { + pr_err("ubios root table has no sub tables.\n"); + return 0; + } + pr_info("ubios sub table count is %u\n", ubios_table->count); + + for (i = 0; i < ubios_table->count; i++) { + header = (struct ub_table_header *)ub_table_get( + ubios_table->sub_tables[i]); + if (!header) + continue; + + memcpy(name, header->name, UB_TABLE_HEADER_NAME_LEN - 1); + pr_info("ubrt sub table name is %s\n", name); + ub_table_put(header); + + if (!strncmp(name, UBIOS_SIG_UBC, strlen(UBIOS_SIG_UBC))) + ret = 0; + else + pr_warn("Ignore sub table: %s\n", name); + + if (ret) { + pr_err("Create %s device ret=%d\n", name, ret); + goto out; + } + } + + return 0; + +out: + uninit_ub_nodes(); + return ret; +} diff --git a/drivers/ub/ubfi/ubrt.h b/drivers/ub/ubfi/ubrt.h new file mode 100644 index 000000000000..0cbc5fe82368 --- /dev/null +++ b/drivers/ub/ubfi/ubrt.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __UBRT_H__ +#define __UBRT_H__ + +#include +#include + +#define UB_TABLE_HEADER_NAME_LEN 16 + +struct ub_table_header { + char name[UB_TABLE_HEADER_NAME_LEN]; + u32 total_size; + u8 version; + u8 reserved[3]; + u32 remaining_size; + u32 checksum; +}; + +struct ubios_root_table { + struct ub_table_header header; + u16 count; + u8 reserved[6]; + u64 sub_tables[]; +}; + +struct ubrt_sub_table { + u8 type; + u8 reserved[7]; + u64 pointer; +}; + +struct acpi_table_ubrt { + struct acpi_table_header head; + u32 count; + struct ubrt_sub_table sub_table[]; +}; + +enum ubrt_sub_table_type { + UB_BUS_CONTROLLER_TABLE = 0, + UMMU_TABLE = 1, + UB_RESERVED_MEMORY_TABLE = 2, + VIRTUAL_BUS_TABLE = 3, + CALL_ID_SERVICE_TABLE = 4, + UB_ENTITY_TABLE = 5, + UB_TOPOLOGY_TABLE = 6, +}; + +extern struct acpi_table_ubrt *acpi_table; +extern struct ubios_root_table *ubios_table; + +void *ub_table_get(u64 pa); +void ub_table_put(void *va); + +int handle_acpi_ubrt(void); +int handle_dts_ubrt(void); + +void uninit_ub_nodes(void); + +#endif /* __UBRT_H__ */ diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index 28303556807a..5448bbfa3717 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -266,4 +266,5 @@ module_init(ubus_driver_init); module_exit(ubus_driver_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("UB bus driver"); +MODULE_IMPORT_NS(UB_UBFI); MODULE_IMPORT_NS(UB_UBUS); -- Gitee From d91a17e0edd5f4975d7f75147f6b3fe4bb354c00 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Thu, 18 Sep 2025 22:35:59 +0800 Subject: [PATCH 003/214] ub:ubus: Support UB Bus Attribute Group ANBZ: #45460 commit 41aa93731e238255875991fa5d241be674d9ac3e openEuler. Add support for UB bus attribute groups, including device attribute groups, bus attribute groups, and driver attribute groups. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/sysfs.c | 145 +++++++++++++++++++++++++++++ drivers/ub/ubus/sysfs.h | 11 +++ drivers/ub/ubus/ub-driver.c | 22 +++++ drivers/ub/ubus/ubus_driver.c | 170 ++++++++++++++++++++++++++++++++++ include/ub/ubus/ubus.h | 20 ++++ 6 files changed, 369 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/sysfs.c create mode 100644 drivers/ub/ubus/sysfs.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index b3cb5016bf2e..0d05ad501ea1 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -2,6 +2,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o -ubus-y := ubus_driver.o +ubus-y := ubus_driver.o sysfs.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/sysfs.c b/drivers/ub/ubus/sysfs.c new file mode 100644 index 000000000000..84d32e36249b --- /dev/null +++ b/drivers/ub/ubus/sysfs.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus sysfs: " fmt + +#include "ubus.h" +#include "sysfs.h" + +#define ub_config_attr(field, format_string) \ +static ssize_t field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct ub_entity *uent; \ + \ + uent = to_ub_entity(dev); \ + return sysfs_emit(buf, format_string, uent->guid.bits.field); \ +} \ +static DEVICE_ATTR_RO(field) + +ub_config_attr(device, "%#06x\n"); +ub_config_attr(type, "%#x\n"); +ub_config_attr(vendor, "%#06x\n"); + +static ssize_t class_code_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ub_entity *uent; + + uent = to_ub_entity(dev); + return sysfs_emit(buf, "%#06x\n", uent->class_code); +} +static DEVICE_ATTR_RO(class_code); + +static ssize_t guid_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + int count; + + count = ub_show_guid(&uent->guid, buf); + + return count + sysfs_emit_at(buf, count, "\n"); +} +static DEVICE_ATTR_RO(guid); + +static ssize_t kref_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", kref_read(&dev->kobj.kref)); +} +static DEVICE_ATTR_RO(kref); + +static ssize_t driver_override_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ub_entity *uent = to_ub_entity(dev); + int ret; + + ret = driver_set_override(dev, &uent->driver_override, buf, count); + if (ret) + return ret; + + return count; +} + +static ssize_t driver_override_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + ssize_t len; + + device_lock(dev); + len = sysfs_emit(buf, "%s\n", uent->driver_override); + device_unlock(dev); + return len; +} +static DEVICE_ATTR_RW(driver_override); + +static ssize_t match_driver_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ub_entity *uent = to_ub_entity(dev); + unsigned long val; + ssize_t result; + + result = kstrtoul(buf, 0, &val); + if (result < 0) + return result; + if (!(val == 0 || val == 1)) + return -EINVAL; + + device_lock(dev); + uent->match_driver = !!val; + device_unlock(dev); + + return count; +} + +static ssize_t match_driver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + ssize_t len; + + device_lock(dev); + len = sysfs_emit(buf, "%s\n", uent->match_driver ? "true" : "false"); + device_unlock(dev); + + return len; +} +static DEVICE_ATTR_RW(match_driver); + +static struct attribute *ub_entity_attrs[] = { + &dev_attr_vendor.attr, + &dev_attr_device.attr, + &dev_attr_class_code.attr, + &dev_attr_type.attr, + &dev_attr_driver_override.attr, + &dev_attr_match_driver.attr, + &dev_attr_guid.attr, + &dev_attr_kref.attr, + NULL +}; + +static const struct attribute_group ub_entity_group = { + .attrs = ub_entity_attrs, +}; + +const struct attribute_group *ub_entity_groups[] = { + &ub_entity_group, + NULL +}; + +static struct attribute *ub_bus_attrs[] = { + NULL +}; + +static const struct attribute_group ub_bus_group = { + .attrs = ub_bus_attrs, +}; + +const struct attribute_group *ub_bus_groups[] = { + &ub_bus_group, + NULL +}; diff --git a/drivers/ub/ubus/sysfs.h b/drivers/ub/ubus/sysfs.h new file mode 100644 index 000000000000..8c8fc5d3fa72 --- /dev/null +++ b/drivers/ub/ubus/sysfs.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ +#ifndef __SYSFS_H__ +#define __SYSFS_H__ + +extern const struct attribute_group *ub_entity_groups[]; +extern const struct attribute_group *ub_bus_groups[]; + +#endif /* __SYSFS_H__ */ diff --git a/drivers/ub/ubus/ub-driver.c b/drivers/ub/ubus/ub-driver.c index 516aec010e16..1922b5062327 100644 --- a/drivers/ub/ubus/ub-driver.c +++ b/drivers/ub/ubus/ub-driver.c @@ -12,6 +12,23 @@ struct bus_type ub_bus_type = { }; EXPORT_SYMBOL_GPL(ub_bus_type); +struct ub_dynid { + struct list_head node; + struct ub_device_id id; +}; + +static void ub_free_dynids(struct ub_driver *drv) +{ + struct ub_dynid *dynid, *n; + + spin_lock(&drv->dynids.lock); + list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { + list_del(&dynid->node); + kfree(dynid); + } + spin_unlock(&drv->dynids.lock); +} + int __ub_register_driver(struct ub_driver *drv, struct module *owner, const char *mod_name) { @@ -22,6 +39,10 @@ int __ub_register_driver(struct ub_driver *drv, struct module *owner, drv->driver.bus = &ub_bus_type; drv->driver.owner = owner; drv->driver.mod_name = mod_name; + drv->driver.groups = drv->groups; + + spin_lock_init(&drv->dynids.lock); + INIT_LIST_HEAD(&drv->dynids.list); return driver_register(&drv->driver); } @@ -33,6 +54,7 @@ void ub_unregister_driver(struct ub_driver *drv) return; driver_unregister(&drv->driver); + ub_free_dynids(drv); } EXPORT_SYMBOL_GPL(ub_unregister_driver); diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index 5448bbfa3717..83f32f2a5bdf 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -13,6 +13,7 @@ #include #include +#include "sysfs.h" #include "ubus.h" struct ub_entity *ub_entity_get(struct ub_entity *dev) @@ -30,6 +31,11 @@ void ub_entity_put(struct ub_entity *dev) } EXPORT_SYMBOL_GPL(ub_entity_put); +struct ub_dynid { + struct list_head node; + struct ub_device_id id; +}; + static const struct ub_device_id ub_entity_id_any = { .vendor = (__u32)UB_ANY_ID, .device = (__u32)UB_ANY_ID, @@ -66,11 +72,25 @@ static const struct ub_device_id *ub_match_device(struct ub_driver *drv, struct ub_entity *dev) { const struct ub_device_id *found_id = NULL, *ids; + struct ub_dynid *dynid; /* When driver_override is set, only bind to the matching driver */ if (dev->driver_override && strcmp(dev->driver_override, drv->name)) return NULL; + /* Look at the dynamic ids first, before the static ones */ + spin_lock(&drv->dynids.lock); + list_for_each_entry(dynid, &drv->dynids.list, node) { + if (ub_match_one_device(&dynid->id, dev)) { + found_id = &dynid->id; + break; + } + } + spin_unlock(&drv->dynids.lock); + + if (found_id) + return found_id; + for (ids = drv->id_table; (found_id = ub_match_id(ids, dev)); ids = found_id + 1) { if (found_id->override_only) { @@ -221,6 +241,150 @@ static int ub_uevent(const struct device *dev, struct kobj_uevent_env *env) return 0; } +static int ub_add_dynid(struct ub_driver *drv, + unsigned int vendor, unsigned int device, + unsigned int mod_vendor, unsigned int module, + unsigned int class_code, unsigned int class_mask, + unsigned long driver_data) +{ + struct ub_dynid *dynid; + + dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); + if (!dynid) + return -ENOMEM; + + dynid->id.vendor = vendor; + dynid->id.device = device; + dynid->id.mod_vendor = mod_vendor; + dynid->id.module = module; + dynid->id.class_code = class_code; + dynid->id.class_mask = class_mask; + dynid->id.driver_data = driver_data; + + spin_lock(&drv->dynids.lock); + list_add_tail(&dynid->node, &drv->dynids.list); + spin_unlock(&drv->dynids.lock); + + return driver_attach(&drv->driver); +} + +static int ub_check_id_exist(struct ub_driver *udrv, u32 vendor, u32 device, + u32 mod_vendor, u32 module, u32 class_code) +{ + struct ub_entity *uent; + int ret = 0; + + uent = kzalloc(sizeof(struct ub_entity), GFP_KERNEL); + if (!uent) + return -ENOMEM; + + uent_vendor(uent) = vendor; + uent_device(uent) = device; + uent_class(uent) = class_code; + uent->mod_vendor = mod_vendor; + uent->module = module; + + if (ub_match_device(udrv, uent)) + ret = -EEXIST; + + kfree(uent); + + return ret; +} + +static int ub_check_driver_data_valid(const struct ub_device_id *ids, + unsigned long driver_data) +{ + /* Only accept driver_data values that match an exiting id_table entry */ + while (ids->vendor || ids->mod_vendor || ids->class_mask) { + if (driver_data == ids->driver_data) + return 0; + ids++; + } + + return -EINVAL; +} + +static ssize_t new_id_store(struct device_driver *driver, const char *buf, + size_t count) +{ + u32 vendor, device, class_code = 0, class_mask = 0; + u32 mod_vendor = UB_ANY_ID, module = UB_ANY_ID; + struct ub_driver *udrv = to_ub_driver(driver); + unsigned long driver_data = 0; + int fileds, ret; + + fileds = sscanf(buf, "%x %x %x %x %x %x %lx", &vendor, &device, + &mod_vendor, &module, + &class_code, &class_mask, &driver_data); + if (fileds < 2) /* 2 parameter for vender & device id */ + return -EINVAL; + + if (fileds != 7) { /* 7 for driver_data input */ + ret = ub_check_id_exist(udrv, vendor, device, mod_vendor, + module, class_code); + if (ret) + return ret; + } + + if (udrv->id_table) { + ret = ub_check_driver_data_valid(udrv->id_table, driver_data); + if (ret) + return ret; + } + + ret = ub_add_dynid(udrv, vendor, device, mod_vendor, module, + class_code, class_mask, driver_data); + if (ret) + return ret; + + return count; +} +static DRIVER_ATTR_WO(new_id); + +static ssize_t remove_id_store(struct device_driver *driver, const char *buf, + size_t count) +{ + u32 vendor, device, class_code = 0, class_mask = 0; + u32 mod_vendor = UB_ANY_ID, module = UB_ANY_ID; + struct ub_driver *udrv = to_ub_driver(driver); + struct ub_dynid *dynid, *n; + ssize_t ret = -ENODEV; + int fields; + + fields = sscanf(buf, "%x %x %x %x %x %x", &vendor, &device, &mod_vendor, + &module, &class_code, &class_mask); + if (fields < 2) /* 2 parameter for vender & device id */ + return -EINVAL; + + spin_lock(&udrv->dynids.lock); + list_for_each_entry_safe(dynid, n, &udrv->dynids.list, node) { + struct ub_device_id *id = &dynid->id; + + if ((id->vendor == vendor) && + (id->device == device) && + (mod_vendor == UB_ANY_ID || id->mod_vendor == mod_vendor) && + (module == UB_ANY_ID || id->module == module) && + !((id->class_code ^ class_code) & class_mask)) { + list_del(&dynid->node); + kfree(dynid); + ret = (ssize_t)count; + break; + } + } + spin_unlock(&udrv->dynids.lock); + + return ret; +} +static DRIVER_ATTR_WO(remove_id); + +static struct attribute *ub_drv_attrs[] = { + &driver_attr_new_id.attr, + &driver_attr_remove_id.attr, + NULL +}; +ATTRIBUTE_GROUPS(ub_drv); + void ub_bus_type_init(void) { ub_bus_type.match = ub_bus_match; @@ -228,6 +392,9 @@ void ub_bus_type_init(void) ub_bus_type.probe = ub_entity_probe; ub_bus_type.remove = ub_entity_remove; ub_bus_type.shutdown = ub_entity_shutdown; + ub_bus_type.dev_groups = ub_entity_groups; + ub_bus_type.bus_groups = ub_bus_groups; + ub_bus_type.drv_groups = ub_drv_groups; } void ub_bus_type_uninit(void) @@ -237,6 +404,9 @@ void ub_bus_type_uninit(void) ub_bus_type.probe = NULL; ub_bus_type.remove = NULL; ub_bus_type.shutdown = NULL; + ub_bus_type.dev_groups = NULL; + ub_bus_type.bus_groups = NULL; + ub_bus_type.drv_groups = NULL; } int ub_host_probe(void) diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 68cbc445a853..9910ce808647 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -54,6 +54,14 @@ struct ub_guid { #define uent_base_code(uent) ((uent)->class_code & UB_BASE_CODE_MASK) #define uent_seq(uent) ((uent)->guid.bits.seq_num) +static inline int ub_show_guid(struct ub_guid *guid, char *buf) +{ + return sprintf(buf, "%04x-%04x-%01x-%01x-%06x-%016llx", + guid->bits.vendor, guid->bits.device, + guid->bits.version, guid->bits.type, + guid->bits.reserved, guid->bits.seq_num); +} + struct ub_entity { /* Driver framework base info */ struct device dev; @@ -68,6 +76,11 @@ struct ub_entity { u16 module; }; +struct ub_dynids { + spinlock_t lock; /* Protects list, index */ + struct list_head list; /* For IDs added at runtime */ +}; + #define to_ub_entity(n) container_of(n, struct ub_entity, dev) /** @@ -97,7 +110,11 @@ struct ub_entity { * context, so it can sleep. * @shutdown: Hook into reboot_notifier_list (kernel/sys.c). * Intended to stop any idling operations. + * @groups: Sysfs attribute groups. + * @dev_groups: Attributes attached to the device that will be + * created once it is bound to the driver. * @driver: Driver model structure. + * @dynids: List of dynamically added device IDs. */ struct ub_driver { struct list_head node; @@ -108,7 +125,10 @@ struct ub_driver { /* entity removed (NULL if not a hot-plug capable driver) */ void (*remove)(struct ub_entity *uent); void (*shutdown)(struct ub_entity *uent); + const struct attribute_group **groups; + const struct attribute_group **dev_groups; struct device_driver driver; + struct ub_dynids dynids; }; static inline struct ub_driver *to_ub_driver(struct device_driver *drv) -- Gitee From cb11155192936276d10b626d34944c5fca36fd86 Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Thu, 16 Oct 2025 17:13:57 +0800 Subject: [PATCH 004/214] ub:ubfi: ubfi driver parse ubc information from ubrt ANBZ: #45460 commit 312a6b7fabe97e270b25bf322006c038feb51e11 openEuler. ubfi driver parse ubc information from ubrt table, by acpi upload or dts upload Signed-off-by: Yuhao Xiang Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubfi/Makefile | 2 +- drivers/ub/ubfi/ubc.c | 140 +++++++++++++++++++++++++++++++++++++++ drivers/ub/ubfi/ubc.h | 96 +++++++++++++++++++++++++++ drivers/ub/ubfi/ubrt.c | 5 +- include/ub/ubfi/ubfi.h | 18 +++++ include/ub/ubus/ubus.h | 5 ++ 6 files changed, 264 insertions(+), 2 deletions(-) create mode 100644 drivers/ub/ubfi/ubc.c create mode 100644 drivers/ub/ubfi/ubc.h create mode 100644 include/ub/ubfi/ubfi.h diff --git a/drivers/ub/ubfi/Makefile b/drivers/ub/ubfi/Makefile index f06b3fbfddb1..55bdaf0bb24f 100644 --- a/drivers/ub/ubfi/Makefile +++ b/drivers/ub/ubfi/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0+ -ubfi-y := ub_fi.o ubrt.o +ubfi-y := ub_fi.o ubrt.o ubc.o obj-$(CONFIG_UB_UBFI) += ubfi.o diff --git a/drivers/ub/ubfi/ubc.c b/drivers/ub/ubfi/ubc.c new file mode 100644 index 000000000000..40bd3c1f69b4 --- /dev/null +++ b/drivers/ub/ubfi/ubc.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubfi ubc: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ubrt.h" +#include "ub_fi.h" +#include "ubc.h" + +struct list_head ubc_list; +EXPORT_SYMBOL_GPL(ubc_list); + +u32 ubc_eid_start; +EXPORT_SYMBOL_GPL(ubc_eid_start); + +u32 ubc_eid_end; +EXPORT_SYMBOL_GPL(ubc_eid_end); + +u32 ubc_cna_start; +EXPORT_SYMBOL_GPL(ubc_cna_start); + +u32 ubc_cna_end; +EXPORT_SYMBOL_GPL(ubc_cna_end); + +u8 ubc_feature; +EXPORT_SYMBOL_GPL(ubc_feature); + +static bool cluster_mode; + +static int acpi_update_ubc_msi_domain(void) +{ + return 0; +} + +static int dts_update_ubc_msi_domain(void) +{ + return 0; +} + +static int create_ubc(struct ubc_node *node, u32 ctl_no) +{ + return 0; +} + +static int parse_ubc_table(void *info_node) +{ + struct ubrt_ubc_table *ubc_table = info_node; + struct ubc_node *node = ubc_table->ubcs; + u32 count, i; + int ret; + + count = ubc_table->ubc_count; + if (!count) { + pr_warn("ubc table has no ubc.\n"); + return 0; + } + + /* get ubc common attribute */ + ubc_cna_start = ubc_table->cna_start; + ubc_cna_end = ubc_table->cna_end; + ubc_eid_start = ubc_table->eid_start; + ubc_eid_end = ubc_table->eid_end; + ubc_feature = ubc_table->feature; + cluster_mode = ubc_table->cluster_mode; + + pr_info("cna_start=%u, cna_end=%u\n", ubc_cna_start, ubc_cna_end); + pr_info("eid_start=%u, eid_end=%u\n", ubc_eid_start, ubc_eid_end); + pr_info("ubc_count=%u, bios_cluster_mode=%u, feature=%u\n", count, + cluster_mode, ubc_feature); + if (ubc_cna_start > ubc_cna_end || ubc_eid_start > ubc_eid_end) { + pr_err("eid or cna range is incorrect\n"); + return -EINVAL; + } + + for (i = 0; i < count; i++) { + ret = create_ubc(node, i); + if (ret) { + pr_err("Create No.%u ubc failed, ret=%d\n", i, ret); + return ret; + } + node++; + } + + return 0; +} + +static void ub_destroy_bus_controllers(void) +{ +} + +void destroy_ubc(void) +{ + ub_destroy_bus_controllers(); +} + +int handle_ubc_table(u64 pointer) +{ + void *info_node = ub_table_get(pointer); + int ret; + + if (!info_node) + return -EINVAL; + + INIT_LIST_HEAD(&ubc_list); + + ret = parse_ubc_table(info_node); + if (ret) + goto err_handle; + + pr_info("Update msi domain for ub bus controller\n"); + /* Update msi domain for ub bus controller */ + if (bios_mode == ACPI) + ret = acpi_update_ubc_msi_domain(); + else + ret = dts_update_ubc_msi_domain(); + + if (ret) + goto err_handle; + + ub_table_put(info_node); + return 0; + +err_handle: + ub_table_put(info_node); + destroy_ubc(); + return ret; +} diff --git a/drivers/ub/ubfi/ubc.h b/drivers/ub/ubfi/ubc.h new file mode 100644 index 000000000000..966c1a152810 --- /dev/null +++ b/drivers/ub/ubfi/ubc.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __UBC_H__ +#define __UBC_H__ + +#include "ubrt.h" + +#define UBC_VENDOR_INFO_SIZE 256 + +/** + * struct ubc_node - Information about a single UBC(UB Controller) + * @int_id_start: Start value of the assignable Interrupt ID range + * @int_id_end: End value (inclusive) of the range of assignable Interrupt IDs + * @hpa_base: Available Host Physical Address Space Base Address + * @hpa_size: Size of available Host Physical Address space for allocation + * @mem_size_limit: Maximum addressable bit width + * @dma_cca: Indicates UBC DMA CCA (Cache Coherent Attribute) capability + * 0: Supports DMA but does not support CCA + * 1: Supports both DMA and CCA + * Others: Does not support DMA + * @ummu_mapping: Indicates the association between this UBC and UMMU, + * using the UMMU Index, which is represented by the UMMU + * serial number in the UMMU information table. + * @proximity_domain: Indicates the NUMA Domain number to which this UBC belongs. + * @msg_queue_base: UBC MSG Queue Register Base Address + * @msg_queue_size: UBC MSG Queue Register Space Size + * @msg_queue_depth: UBC MSG Queue Interrupt Depth + * @msg_int: UBC MSG Queue Interrupt Number + * @msg_int_attr: UBC MSG Queue Interrupt Attributes + * bit0: Level OR Edge Trigger + * 0: Level + * 1: Edge + * bit1: High level (rising edge) OR Low level (falling edge) + * 0: High level (rising edge) + * 1: Low level (falling edge) + * @vendor_info: Vendor-defined information + */ +struct ubc_node { + u32 int_id_start; + u32 int_id_end; + u64 hpa_base; + u64 hpa_size; + u8 mem_size_limit; + u8 dma_cca; + u16 ummu_mapping; + u16 proximity_domain; + u16 reserved; + u64 msg_queue_base; + u64 msg_queue_size; + u16 msg_queue_depth; + u16 msg_int; + u8 msg_int_attr; + u8 reserved2[59]; + u64 ubc_guid_low; + u64 ubc_guid_high; + u8 vendor_info[UBC_VENDOR_INFO_SIZE]; +}; + +/** + * struct ubrt_ubc_table - all ubcs in ubrt + * @header: Public header of the UBRT table + * @cna_start: Starting CNA number available for this UBPU + * @cna_end: End CNA number available for this UBPU (inclusive) + * @eid_start: Starting EID number available for this UBPU + * @eid_end: End EID number available for this UBPU (inclusive) + * @feature: Features enabled by UBCs: + * bit0: MMIO Token Value, a security feature for MMIO; + * 0 indicates disabled, 1 indicates enabled. + * bit1: MCTP over UB, an MCTP over UB feature; + * 0 indicates disabled, 1 indicates enabled. + * Other bits: reserved. + * @cluster_mode: System Operating Mode + * 0: Standalone mode, the OS can perform UB enumeration. + * 1: Super Node mode, the OS cannot perform active UB enumeration operations. + * @ubc_count: Number of UBCs + */ +struct ubrt_ubc_table { + struct ub_table_header header; + u32 cna_start; + u32 cna_end; + u32 eid_start; + u32 eid_end; + u8 feature; + u8 reserved[3]; + u16 cluster_mode; + u16 ubc_count; + struct ubc_node ubcs[]; +}; + +int handle_ubc_table(u64 pointer); +void destroy_ubc(void); + +#endif /* __UBC_H__ */ diff --git a/drivers/ub/ubfi/ubrt.c b/drivers/ub/ubfi/ubrt.c index d022ae96fbf9..ea95d66260cc 100644 --- a/drivers/ub/ubfi/ubrt.c +++ b/drivers/ub/ubfi/ubrt.c @@ -9,6 +9,7 @@ #include #include +#include "ubc.h" #include "ubrt.h" #define UBIOS_SIG_UBC "ubc" @@ -71,6 +72,7 @@ void ub_table_put(void *va) void uninit_ub_nodes(void) { + destroy_ubc(); } int handle_acpi_ubrt(void) @@ -85,6 +87,7 @@ int handle_acpi_ubrt(void) sub_table = &acpi_table->sub_table[i]; switch (sub_table->type) { case UB_BUS_CONTROLLER_TABLE: + ret = handle_ubc_table(sub_table->pointer); break; default: pr_warn("Ignore sub table: type %u\n", sub_table->type); @@ -125,7 +128,7 @@ int handle_dts_ubrt(void) ub_table_put(header); if (!strncmp(name, UBIOS_SIG_UBC, strlen(UBIOS_SIG_UBC))) - ret = 0; + ret = handle_ubc_table(ubios_table->sub_tables[i]); else pr_warn("Ignore sub table: %s\n", name); diff --git a/include/ub/ubfi/ubfi.h b/include/ub/ubfi/ubfi.h new file mode 100644 index 000000000000..9131e1da0888 --- /dev/null +++ b/include/ub/ubfi/ubfi.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef _UB_UBFI_UBFI_H_ +#define _UB_UBFI_UBFI_H_ + +#include + +extern struct list_head ubc_list; +extern u32 ubc_eid_start; +extern u32 ubc_eid_end; +extern u32 ubc_cna_start; +extern u32 ubc_cna_end; +extern u8 ubc_feature; + +#endif /* _UB_UBFI_UBFI_H_ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 9910ce808647..2e11e9f861cb 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -131,6 +131,11 @@ struct ub_driver { struct ub_dynids dynids; }; +struct ub_bus_controller { + struct device dev; + struct list_head node; +}; + static inline struct ub_driver *to_ub_driver(struct device_driver *drv) { return drv ? container_of(drv, struct ub_driver, driver) : NULL; -- Gitee From ee6fdde3eeef5c16a6b928f9a4c970a94c2cc6c2 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 19 Sep 2025 09:40:21 +0800 Subject: [PATCH 005/214] ub:ubus: add ubus controller framework ANBZ: #45460 commit 26a640c9d1b1f7764bd933b7bf6ffca8ddbc6efd openEuler. Add ubus controller framework, providing probe and remove controllers and find ubus controller by clan network address functions Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 4 +- drivers/ub/ubus/controller.c | 21 +++++++ drivers/ub/ubus/ubus.h | 10 ++++ drivers/ub/ubus/ubus_controller.c | 63 ++++++++++++++++++++ drivers/ub/ubus/ubus_controller.h | 17 ++++++ drivers/ub/ubus/ubus_driver.c | 96 +++++++++++++++++++++++++++++++ drivers/ub/ubus/ubus_inner.h | 3 + include/ub/ubus/ubus.h | 44 ++++++++++++++ 8 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 drivers/ub/ubus/controller.c create mode 100644 drivers/ub/ubus/ubus_controller.c create mode 100644 drivers/ub/ubus/ubus_controller.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 0d05ad501ea1..f5d3848ad60e 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ -obj-$(CONFIG_UB_UBUS) += ub-driver.o +obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o -ubus-y := ubus_driver.o sysfs.o +ubus-y := ubus_driver.o sysfs.o ubus_controller.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/controller.c b/drivers/ub/ubus/controller.c new file mode 100644 index 000000000000..72a7b9a0d12d --- /dev/null +++ b/drivers/ub/ubus/controller.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include "ubus_inner.h" + +struct ub_bus_controller *ub_ubc_get(struct ub_bus_controller *ubc) +{ + if (ubc) + get_device(&ubc->dev); + return ubc; +} +EXPORT_SYMBOL_GPL(ub_ubc_get); + +void ub_ubc_put(struct ub_bus_controller *ubc) +{ + if (ubc) + put_device(&ubc->dev); +} +EXPORT_SYMBOL_GPL(ub_ubc_put); diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index b82922a63de4..3f455ab68c11 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -5,9 +5,19 @@ #ifndef __UBUS_H__ #define __UBUS_H__ +#include #include int ub_host_probe(void); void ub_host_remove(void); +struct ub_manage_subsystem_ops { + u32 vendor; + int (*controller_probe)(struct ub_bus_controller *ubc); + void (*controller_remove)(struct ub_bus_controller *ubc); +}; +int register_ub_manage_subsystem_ops(const struct ub_manage_subsystem_ops *ops); +void unregister_ub_manage_subsystem_ops(const struct ub_manage_subsystem_ops *ops); +const struct ub_manage_subsystem_ops *get_ub_manage_subsystem_ops(void); + #endif /* __UBUS_H__ */ diff --git a/drivers/ub/ubus/ubus_controller.c b/drivers/ub/ubus/ubus_controller.c new file mode 100644 index 000000000000..5bd29cbbe79d --- /dev/null +++ b/drivers/ub/ubus/ubus_controller.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus controller: " fmt + +#include + +#include "ubus.h" +#include "ubus_controller.h" + +int ub_ubc_to_node(struct ub_bus_controller *ubc) +{ + return dev_to_node(&ubc->dev); +} + +void ub_bus_controllers_remove(void) +{ + const struct ub_manage_subsystem_ops *manage_subsystem_ops; + struct ub_bus_controller *ubc; + + manage_subsystem_ops = get_ub_manage_subsystem_ops(); + if (!manage_subsystem_ops || !manage_subsystem_ops->controller_remove) + return; + + list_for_each_entry_reverse(ubc, &ubc_list, node) + if (ubc->ops) + manage_subsystem_ops->controller_remove(ubc); +} + +int ub_bus_controllers_probe(void) +{ + const struct ub_manage_subsystem_ops *manage_subsystem_ops; + struct ub_bus_controller *ubc; + int ret; + + manage_subsystem_ops = get_ub_manage_subsystem_ops(); + if (!manage_subsystem_ops || !manage_subsystem_ops->controller_probe) + return -EINVAL; + + list_for_each_entry(ubc, &ubc_list, node) { + ret = manage_subsystem_ops->controller_probe(ubc); + if (ret) + goto out; + } + + return 0; +out: + ub_bus_controllers_remove(); + return ret; +} + +struct ub_bus_controller *ub_find_bus_controller_by_cna(u32 cna) +{ + struct ub_bus_controller *ubc; + + list_for_each_entry(ubc, &ubc_list, node) + if (ubc->uent->cna == cna) + return ubc; + + return NULL; +} diff --git a/drivers/ub/ubus/ubus_controller.h b/drivers/ub/ubus/ubus_controller.h new file mode 100644 index 000000000000..532307abbff0 --- /dev/null +++ b/drivers/ub/ubus/ubus_controller.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __UBUS_CONTROLLER_H__ +#define __UBUS_CONTROLLER_H__ + +struct ub_bus_controller; +struct ub_bus_controller_ops {}; + +struct ub_bus_controller *ub_find_bus_controller_by_cna(u32 cna); +void ub_bus_controllers_remove(void); +int ub_bus_controllers_probe(void); +int ub_ubc_to_node(struct ub_bus_controller *ubc); + +#endif /* __UBUS_CONTROLLER_H__ */ diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index 83f32f2a5bdf..fc39dd1cd534 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -15,6 +15,102 @@ #include "sysfs.h" #include "ubus.h" +#include "ubus_controller.h" + +static DEFINE_MUTEX(manage_subsystem_ops_mutex); +static const struct ub_manage_subsystem_ops *manage_subsystem_ops; + +int register_ub_manage_subsystem_ops(const struct ub_manage_subsystem_ops *ops) +{ + if (!ops) + return -EINVAL; + + mutex_lock(&manage_subsystem_ops_mutex); + if (!manage_subsystem_ops) { + manage_subsystem_ops = ops; + mutex_unlock(&manage_subsystem_ops_mutex); + pr_info("ub manage subsystem ops register successfully\n"); + return 0; + } + + pr_warn("ub manage subsystem ops has been registered\n"); + mutex_unlock(&manage_subsystem_ops_mutex); + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(register_ub_manage_subsystem_ops); + +void unregister_ub_manage_subsystem_ops(const struct ub_manage_subsystem_ops *ops) +{ + if (!ops) + return; + + mutex_lock(&manage_subsystem_ops_mutex); + if (manage_subsystem_ops == ops) { + manage_subsystem_ops = NULL; + pr_info("ub manage subsystem ops unregister successfully\n"); + } else { + pr_warn("ub manage subsystem ops is not registered by this vendor\n"); + } + mutex_unlock(&manage_subsystem_ops_mutex); +} +EXPORT_SYMBOL_GPL(unregister_ub_manage_subsystem_ops); + +const struct ub_manage_subsystem_ops *get_ub_manage_subsystem_ops(void) +{ + return manage_subsystem_ops; +} + +struct ub_bus_controller *ub_find_bus_controller(u32 ctl_no) +{ + struct ub_bus_controller *ubc; + + list_for_each_entry(ubc, &ubc_list, node) + if (ubc->ctl_no == ctl_no) + return ubc; + + return NULL; +} +EXPORT_SYMBOL_GPL(ub_find_bus_controller); + +int ub_get_bus_controller(struct ub_entity *ubc_dev[], unsigned int max_num, + unsigned int *real_num) +{ + struct ub_bus_controller *ubc; + unsigned int ubc_num = 0; + + if (!real_num || !ubc_dev) { + pr_err("%s: input parameters invalid\n", __func__); + return -EINVAL; + } + + list_for_each_entry(ubc, &ubc_list, node) { + if (ubc_num >= max_num) { + pr_err("ubc list num over max num %u\n", max_num); + ub_put_bus_controller(ubc_dev, max_num); + return -ENOMEM; + } + + ubc_dev[ubc_num] = ub_entity_get(ubc->uent); + ubc_num++; + } + *real_num = ubc_num; + + return 0; +} +EXPORT_SYMBOL_GPL(ub_get_bus_controller); + +void ub_put_bus_controller(struct ub_entity *ubc_dev[], unsigned int num) +{ + unsigned int i; + + if (ubc_dev) { + for (i = 0; i < num; i++) + ub_entity_put(ubc_dev[i]); + memset(ubc_dev, 0, sizeof(struct ub_entity *) * num); + } +} +EXPORT_SYMBOL_GPL(ub_put_bus_controller); struct ub_entity *ub_entity_get(struct ub_entity *dev) { diff --git a/drivers/ub/ubus/ubus_inner.h b/drivers/ub/ubus/ubus_inner.h index 813cdcf70b16..86a9c4130a1a 100644 --- a/drivers/ub/ubus/ubus_inner.h +++ b/drivers/ub/ubus/ubus_inner.h @@ -7,4 +7,7 @@ #include +struct ub_bus_controller *ub_ubc_get(struct ub_bus_controller *ubc); +void ub_ubc_put(struct ub_bus_controller *ubc); + #endif /* __UBUS_INNER_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 2e11e9f861cb..c3cc8818b32c 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -74,6 +74,10 @@ struct ub_entity { u16 class_code; u16 mod_vendor; /* entity's module vendor and module id */ u16 module; + unsigned int cna; + + /* entity topology info */ + struct ub_bus_controller *ubc; }; struct ub_dynids { @@ -133,7 +137,13 @@ struct ub_driver { struct ub_bus_controller { struct device dev; + struct ub_entity *uent; + + u32 ctl_no; + struct message_device *mdev; struct list_head node; + struct list_head devs; + struct ub_bus_controller_ops *ops; }; static inline struct ub_driver *to_ub_driver(struct device_driver *drv) @@ -172,6 +182,34 @@ struct ub_entity *ub_entity_get(struct ub_entity *uent); */ void ub_entity_put(struct ub_entity *uent); +/** + * ub_get_bus_controller() - Obtains the ub bus controller entity list. + * @uents: Output buffer for the UBC entities list. + * @max_num: Buffer size. + * @real_num: Real entities num. + * + * All ub bus controllers in the system are returned. Increase the reference + * counting of all entities by 1. Remember to call ub_put_bus_controller() after + * using it. + * + * Context: Any context. + * Return: 0 if success, or %-EINVAL if input parameter is NULL, + * or %-ENOMEM if buffer is too small. + */ +int ub_get_bus_controller(struct ub_entity *uents[], unsigned int max_num, + unsigned int *real_num); + +/** + * ub_put_bus_controller() - Free the ub bus controller device list. + * @uents: UBC entities list + * @num: entities num + * + * Decrement the reference counting of all entities by 1. + * + * Context: Any context. + */ +void ub_put_bus_controller(struct ub_entity *uents[], unsigned int num); + /* Proper probing supporting hot-pluggable entities */ int __ub_register_driver(struct ub_driver *drv, struct module *owner, const char *mod_name); @@ -185,6 +223,12 @@ void ub_unregister_driver(struct ub_driver *drv); static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) { return NULL; } static inline void ub_entity_put(struct ub_entity *uent) {} +static inline int ub_get_bus_controller(struct ub_entity *uents[], + unsigned int max_num, + unsigned int *real_num) +{ return -ENODEV; } +static inline void +ub_put_bus_controller(struct ub_entity *uents[], unsigned int num) {} static inline int __ub_register_driver(struct ub_driver *drv, struct module *owner, const char *mod_name) -- Gitee From 244c35a074df6c2308c0c2c078ea85fdcb40ecf8 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 19 Sep 2025 10:26:30 +0800 Subject: [PATCH 006/214] ub:ubus: Support for ubus messaging communication framework ANBZ: #45460 commit 8e23e297941bd98111ee67f825b590daa030e249 openEuler. Support for ubus messaging communication framework, create message device and add device probe and remove messages Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/msg.c | 118 +++++++++++++++++++ drivers/ub/ubus/msg.h | 244 +++++++++++++++++++++++++++++++++++++++ include/ub/ubus/ubus.h | 2 + 4 files changed, 365 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/msg.c create mode 100644 drivers/ub/ubus/msg.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index f5d3848ad60e..8f75a1860121 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -2,6 +2,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o -ubus-y := ubus_driver.o sysfs.o ubus_controller.o +ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/msg.c b/drivers/ub/ubus/msg.c new file mode 100644 index 000000000000..5bdad62050b1 --- /dev/null +++ b/drivers/ub/ubus/msg.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus msg: " fmt + +#include +#include +#include + +#include "msg.h" + +u8 err_to_msg_rsp(int err) +{ + if (err >= 0) + return err; + + switch (err) { + case -ENOMEM: + return UB_MSG_RSP_EXEC_ENOMEM; + case -EACCES: + return UB_MSG_RSP_EXEC_EACCES; + case -EFAULT: + return UB_MSG_RSP_EXEC_EFAULT; + case -EBUSY: + return UB_MSG_RSP_EXEC_EBUSY; + case -ENODEV: + return UB_MSG_RSP_EXEC_ENODEV; + case -EINVAL: + return UB_MSG_RSP_EXEC_EINVAL; + case -ENOEXEC: + return UB_MSG_RSP_EXEC_ENOEXEC; + default: + return UB_MSG_RSP_UNKNOWN; + } +} + +static LIST_HEAD(message_device_list); +static DEFINE_SPINLOCK(message_device_lock); + +int message_device_register(struct message_device *mdev) +{ + spin_lock(&message_device_lock); + list_add_tail(&mdev->list, &message_device_list); + spin_unlock(&message_device_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(message_device_register); + +void message_device_unregister(struct message_device *mdev) +{ + spin_lock(&message_device_lock); + list_del(&mdev->list); + spin_unlock(&message_device_lock); +} +EXPORT_SYMBOL_GPL(message_device_unregister); + +static struct dev_message *dev_message_get(struct ub_entity *uent) +{ + struct dev_message *msg = uent->message; + + if (msg) + return msg; + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return NULL; + + uent->message = msg; + + return msg; +} + +static void dev_message_put(struct ub_entity *uent) +{ + kfree(uent->message); + uent->message = NULL; +} + +int message_probe_device(struct ub_entity *uent) +{ + const struct message_ops *ops = uent->ubc->mdev->ops; + int ret; + + if (!dev_message_get(uent)) + return -ENOMEM; + + if (uent->message->mdev) + return 0; + + if (ops->probe_dev) { + ret = ops->probe_dev(uent); + if (ret) + goto err_probe; + } + + uent->message->mdev = uent->ubc->mdev; + + return 0; + +err_probe: + dev_message_put(uent); + return ret; +} + +void message_remove_device(struct ub_entity *uent) +{ + const struct message_ops *ops = uent->ubc->mdev->ops; + + if (!uent->message) + return; + + if (ops->remove_dev) + ops->remove_dev(uent); + dev_message_put(uent); +} diff --git a/drivers/ub/ubus/msg.h b/drivers/ub/ubus/msg.h new file mode 100644 index 000000000000..86e69abf3fb8 --- /dev/null +++ b/drivers/ub/ubus/msg.h @@ -0,0 +1,244 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __MSG_H__ +#define __MSG_H__ + +#include "ubus.h" + +struct ub_link_header { + u32 plen : 14; + u32 rt : 2; + u32 cfg : 4; + u32 rsvd1 : 1; + u32 vl : 4; + u32 rsvd0 : 1; + u32 crd_vl : 4; + u32 ack : 1; + u32 crd : 1; +}; +#define UB_COMPACT_LINK_CFG 6 + +struct compact_network_header { + /* DW0 */ + u32 dcna : 16; + u32 scna : 16; + /* DW1 */ +#define NTH_NLP_WITH_TPH 0 +#define NTH_NLP_WITHOUT_TPH 1 + u32 nth_nlp : 3; + u32 mgmt : 1; + u32 sl : 4; + u32 lb : 8; + u32 cc : 16; +}; + +/* message */ +#define seid_high(eid) (((eid) >> 12) & 0xff) +#define seid_low(eid) ((eid) & 0xfff) +#define eid_gen(eid_h, eid_l) ((eid_h) << 12 | (eid_l)) +#define msg_type(code) ((code) & 0x1) +#define msg_code(code) (((code) >> 1) & 0x7) +#define sub_msg_code(code) (((code) >> 4) & 0xf) +#define code_gen(msg, sub_msg, type) ((sub_msg) << 4 | ((msg) << 1 | (type))) +#define ubba_gen(ubba_h, ubba_l) ((u64)(ubba_h) << 32 | (ubba_l)) + +#define PLD_SIZE_MAX SZ_1K + +enum ub_msg_type { + MSG_REQ = 0, + MSG_RSP = 1 +}; + +enum ub_msg_code { + UB_MSG_CODE_RAS = 0, + UB_MSG_CODE_LINK = 1, + UB_MSG_CODE_CFG = 2, + UB_MSG_CODE_VDM = 3, + UB_MSG_CODE_EXCH = 4, + UB_MSG_CODE_SEC = 5, + UB_MSG_CODE_POOL = 6, + UB_MSG_CODE_MAX = 7 +}; + +#define UB_MSG_CODE_NUM 8 +#define UB_SUB_MSG_CODE_NUM 16 + +enum ub_sec_sub_msg_code { + UB_TOKEN_CHECK_CFG = 2, +}; + +enum ub_exch_sub_msg_code { + UB_OBTAIN_ENTITY_INFO = 2, + UB_LINK_NEIGHBOR_QUERY = 7, +}; + +enum ub_pool_sub_msg_code { + UB_BI_CREATE = 2, + UB_BI_DESTROY = 3, +}; + +enum ub_vdm_sub_msg_code { + UB_VENDOR_MSG = 0xf +}; + +enum ub_msg_rsp_status_code { + UB_MSG_RSP_SUCCESS = 0x00, + UB_MSG_RSP_CMD_EACCESS = 0x01, + UB_MSG_RSP_CMD_ENODEV = 0x02, + UB_MSG_RSP_CMD_CODE_ERR = 0x03, + UB_MSG_RSP_CMD_LEN_ERR = 0x04, + /* 0x05~0x1f reserved */ + UB_MSG_RSP_EXEC_ENOMEM = 0x20, + UB_MSG_RSP_EXEC_EACCES = 0x21, + UB_MSG_RSP_EXEC_EFAULT = 0x22, + UB_MSG_RSP_EXEC_EBUSY = 0x23, + UB_MSG_RSP_EXEC_EEXIST = 0x24, + UB_MSG_RSP_EXEC_ENODEV = 0x25, + UB_MSG_RSP_EXEC_EINVAL = 0x26, + UB_MSG_RSP_EXEC_ENOEXEC = 0x27, + /* 0x28~0xfe reserved */ + UB_MSG_RSP_UNKNOWN = 0xff +}; + +u8 err_to_msg_rsp(int err); + +struct msg_extended_header { + u32 plen : 12; + u32 rsvd : 4; + u32 rsp_status : 8; + union { + struct { + u8 type : 1; + u8 msg_code : 3; + u8 sub_msg_code : 4; + }; + u8 code; + }; +}; + +struct msg_pkt_header { + /* DW0 */ + struct ub_link_header ulh; + /* DW1-DW2 */ + struct compact_network_header nth; + /* DW3 */ + u32 seid_h : 8; + u32 upi : 16; +#define CTPH_NLP_UPI_40BITS_UEID 2 + u32 ctph_nlp : 4; /* tp header */ + u32 pad : 2; +#define CTPH_OPCODE_NOT_CNP 0 + u32 tp_opcode : 2; + /* DW4 */ + u32 deid : 20; + u32 seid_l : 12; + /* DW5 */ + u32 src_tassn : 16; + u32 taver : 3; + u32 tk_vld : 1; + u32 udf : 4; +#define TAH_OPCODE_MSG 0x10 + u32 ta_opcode : 8; + /* DW6 */ + u32 msgq_id : 8; + u32 rsvd0 : 24; + /* DW7 */ + struct msg_extended_header msgetah; + + /* DW8~DW11 */ + char payload[]; +}; +#define MSG_PKT_HEADER_SIZE 32 + +#define MSG_CFG_PKT_SIZE 48 /* header 32bytes, pld 16bytes */ + +/** + * struct msg_info - Collection of per message + * + * @ubc: This cfg request source ub bus controller + * @uent: This cfg request target ub_entity + * @req_packet: Request Message packet buffer + * @req_pkt_size: Size of the request packet + * @rsp_packet: Response Message packet buffer + * @rsp_pkt_size: Size of the rsponse packet + * @actual_rsp_size: Size of the actual rsponse packet + * + * vendor can define and parse structure in @req_packet and @rsp_packet + */ +struct msg_info { + struct ub_entity *ubc; + struct ub_entity *uent; + void *req_packet; + u16 req_pkt_size; + void *rsp_packet; + u16 rsp_pkt_size; + u16 actual_rsp_size; +}; +#define MSG_REQ_SIZE_OFFSET 16 + +enum message_tx_type { + MESSAGE_TX_TYPE_MSG, + MESSAGE_TX_TYPE_ENUM +}; + +#define msg_size_gen(req_size, rsp_size) ((u32)((req_size) << 16) | (rsp_size)) + +/** + * struct message_ops - message ops and capabilities + * @probe_dev: probe ub_entity to init message + * @remove_dev: remove ub_entity to uninit message + * @owner: Driver module providing these ops + */ +struct message_ops { + int (*probe_dev)(struct ub_entity *uent); + void (*remove_dev)(struct ub_entity *uent); + struct module *owner; +}; + +/** + * struct message_device - Message core representation of one message-dev + * hardware instance + * @list: Used by the message-core to keep a list of registered message-devs + * @ops: Message-ops for talking to this message-dev + * @fwnode: Firmware representation of one message-dev + */ +struct message_device { + struct list_head list; + const struct message_ops *ops; + struct fwnode_handle *fwnode; +}; + +/** + * struct dev_message - Collection of per-device Message-dev data + * + * @mdev: Message device this device is linked to + * @priv: Message Driver private data + */ +struct dev_message { + struct message_device *mdev; + void *priv; +}; + +static inline void message_info_init(struct msg_info *info, struct ub_entity *uent, + void *req, void *rsp, u32 size) +{ + if (uent) { + info->ubc = uent->ubc->uent; + info->uent = uent; + } + info->req_packet = req; + info->rsp_packet = rsp; + /* High 16bits for req size, low 16bits for rsp size */ + info->req_pkt_size = (u16)(size >> MSG_REQ_SIZE_OFFSET); + info->rsp_pkt_size = (u16)size; +} + +int message_device_register(struct message_device *mdev); +void message_device_unregister(struct message_device *mdev); +int message_probe_device(struct ub_entity *uent); +void message_remove_device(struct ub_entity *uent); + +#endif /* __MSG_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index c3cc8818b32c..b935c713e368 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -78,6 +78,8 @@ struct ub_entity { /* entity topology info */ struct ub_bus_controller *ubc; + + struct dev_message *message; }; struct ub_dynids { -- Gitee From a4fe237d62b82f7f0e43029b59189090f1afd241 Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Thu, 16 Oct 2025 17:16:27 +0800 Subject: [PATCH 007/214] ub:ubfi: ubfi driver create ubc device ANBZ: #45460 commit 6533f3af69cee90712f3481841901ab0a10f8339 openEuler. ubfi driver create ubc device, and initialize some ubc device resources(irq, mmio,...). Declare these resources to the kernel. Signed-off-by: Yuhao Xiang Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubfi/Makefile | 2 + drivers/ub/ubfi/irq.c | 43 ++++ drivers/ub/ubfi/ubc.c | 418 +++++++++++++++++++++++++++++++++++++++ include/ub/ubfi/ubfi.h | 25 +++ include/ub/ubus/ubus.h | 25 +++ 5 files changed, 513 insertions(+) create mode 100644 drivers/ub/ubfi/irq.c diff --git a/drivers/ub/ubfi/Makefile b/drivers/ub/ubfi/Makefile index 55bdaf0bb24f..966355af26fd 100644 --- a/drivers/ub/ubfi/Makefile +++ b/drivers/ub/ubfi/Makefile @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ +obj-y += irq.o + ubfi-y := ub_fi.o ubrt.o ubc.o obj-$(CONFIG_UB_UBFI) += ubfi.o diff --git a/drivers/ub/ubfi/irq.c b/drivers/ub/ubfi/irq.c new file mode 100644 index 000000000000..3c26ae362893 --- /dev/null +++ b/drivers/ub/ubfi/irq.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include +#include +#include +#include + +int ubrt_register_gsi(u32 hwirq, int trigger, int polarity, const char *name, + struct resource *res) +{ +#ifdef CONFIG_ACPI + int irq; + + if (!res) { + pr_err("ub register gsi, res is null\n"); + return -EINVAL; + } + + irq = acpi_register_gsi(NULL, hwirq, trigger, polarity); + if (irq <= 0) { + pr_err("could not register ub gsi hwirq %u\n", hwirq); + return -EINVAL; + } + + res->name = name; + res->start = irq; + res->end = irq; + res->flags = IORESOURCE_IRQ; +#endif + return 0; +} +EXPORT_SYMBOL_GPL(ubrt_register_gsi); + +void ubrt_unregister_gsi(u32 hwirq) +{ +#ifdef CONFIG_ACPI + acpi_unregister_gsi(hwirq); +#endif +} +EXPORT_SYMBOL_GPL(ubrt_unregister_gsi); diff --git a/drivers/ub/ubfi/ubc.c b/drivers/ub/ubfi/ubc.c index 40bd3c1f69b4..03fe0ead069a 100644 --- a/drivers/ub/ubfi/ubc.c +++ b/drivers/ub/ubfi/ubc.c @@ -14,12 +14,20 @@ #include #include #include +#include #include #include "ubrt.h" #include "ub_fi.h" #include "ubc.h" +#define UB_MSGQ_INT_TRIGGER_MASK BIT(0) +#define UB_MSGQ_INT_POLARITY_MASK BIT(1) + +#define ACPI_UBC_DEVICE_HID "HISI0541" + +#define to_ub_ubc(n) container_of(n, struct ub_bus_controller, dev) + struct list_head ubc_list; EXPORT_SYMBOL_GPL(ubc_list); @@ -40,19 +48,423 @@ EXPORT_SYMBOL_GPL(ubc_feature); static bool cluster_mode; +static acpi_status acpi_processor_ubc(acpi_handle handle, u32 lvl, + void *context, void **rv) +{ + return AE_OK; +} + static int acpi_update_ubc_msi_domain(void) { + struct ub_bus_controller *ubc; + acpi_status status; + + list_for_each_entry(ubc, &ubc_list, node) { + /* Get UB Bus Controller from DSDT */ + status = acpi_get_devices(ACPI_UBC_DEVICE_HID, + acpi_processor_ubc, + ubc, NULL); + if (ACPI_FAILURE(status)) + return -ENODEV; + } + return 0; } static int dts_update_ubc_msi_domain(void) { + struct ub_bus_controller *ubc; + struct device_node *np; + u32 ctl_no; + bool find; + int ret; + + for_each_compatible_node(np, NULL, "ub,ubc") { + ret = of_property_read_u32(np, "index", &ctl_no); + if (ret) { + pr_err("dts can't find ubc index\n"); + continue; + } + + find = false; + list_for_each_entry(ubc, &ubc_list, node) { + if (ubc->ctl_no == ctl_no) { + find = true; + break; + } + } + if (!find) { + pr_err("can't find ubc no=%u\n", ctl_no); + continue; + } + } return 0; } +static int parse_ubc_info(struct ubc_node *node, struct ub_bus_controller *ubc) +{ + ubc->attr.int_id_start = node->int_id_start; + ubc->attr.int_id_end = node->int_id_end; + ubc->attr.hpa_base = node->hpa_base; + ubc->attr.hpa_size = node->hpa_size; + ubc->attr.mem_size_limit = node->mem_size_limit; + ubc->attr.dma_cca = node->dma_cca; + ubc->attr.ummu_map = node->ummu_mapping; + ubc->attr.proximity_domain = node->proximity_domain; + ubc->attr.queue_addr = node->msg_queue_base; + ubc->attr.queue_size = node->msg_queue_size; + ubc->attr.queue_depth = node->msg_queue_depth; + ubc->attr.msg_int = node->msg_int; + ubc->attr.msg_int_attr = node->msg_int_attr; + ubc->attr.ubc_guid_low = node->ubc_guid_low; + ubc->attr.ubc_guid_high = node->ubc_guid_high; + pr_info("ubc interrupt id[0x%x-0x%x], ubc_guid[%llx-%llx], msg_int[0x%x]\n", + ubc->attr.int_id_start, ubc->attr.int_id_end, + ubc->attr.ubc_guid_high, ubc->attr.ubc_guid_low, + ubc->attr.msg_int); + + ubc->data = kzalloc(UBC_VENDOR_INFO_SIZE, GFP_KERNEL); + if (!ubc->data) + return -ENOMEM; + memcpy(ubc->data, node->vendor_info, UBC_VENDOR_INFO_SIZE); + + ubc->cluster = cluster_mode; + pr_info("ubc real cluster mode is %d\n", ubc->cluster); + INIT_LIST_HEAD(&ubc->resources); + (void)snprintf(ubc->name, sizeof(ubc->name), "UB_BUS_CTL%u", ubc->ctl_no); + pr_info("create ubc success, ubc name=%s\n", ubc->name); + return 0; +} + +static int ubc_dev_new_resource_entry(struct resource *res, + struct list_head *resources) +{ + struct resource_entry *rentry; + + rentry = resource_list_create_entry(NULL, 0); + if (!rentry) + return -ENOMEM; + + *rentry->res = *res; + rentry->offset = 0; + resource_list_add_tail(rentry, resources); + return 0; +} + +static int dts_register_irq(u32 ctl_no, int irq_type, const char *name, + struct resource *res) +{ + struct device_node *np; + u32 irq = 0, index; + int ret; + + for_each_compatible_node(np, NULL, "ub,ubc") { + ret = of_property_read_u32(np, "index", &index); + if (ret) { + pr_err("dts can't find ubc index\n"); + continue; + } + if (ctl_no != index) + continue; + + irq = irq_of_parse_and_map(np, irq_type); + if (!irq) + continue; + } + + if (!irq) { + pr_err("irq_type %d parse and map fail\n", irq_type); + return -EINVAL; + } + pr_info("irq_type[%d] register success, irq=%u\n", irq_type, irq); + + res->name = name; + res->start = irq; + res->end = irq; + res->flags = IORESOURCE_IRQ; + return 0; +} + +static void remove_ubc_resource(struct ub_bus_controller *ubc) +{ + struct resource_entry *entry; + struct resource *res; + + resource_list_for_each_entry(entry, &ubc->resources) { + res = entry->res; + if (res->parent && (res->flags & IORESOURCE_MEM)) + release_resource(res); + if ((res->flags & IORESOURCE_IRQ) + && !strcmp(res->name, "UBUS") + && !ubc->ctl_no) { + if (bios_mode == ACPI) + ubrt_unregister_gsi(ubc->attr.msg_int); + else if (bios_mode == DTS) + irq_dispose_mapping(ubc->queue_virq); + } + } + + resource_list_free(&ubc->resources); +} + +static int add_ubc_mmio_resource(struct ubc_node *node, + struct ub_bus_controller *ubc) +{ + struct resource res = {}; + int ret; + + res.start = node->hpa_base; + res.end = node->hpa_base + node->hpa_size - 1; + res.flags = IORESOURCE_MEM; + res.name = ubc->name; + + ret = ubc_dev_new_resource_entry(&res, &ubc->resources); + if (!ret) + pr_info("add %s resource success, %pR\n", res.name, &res); + + return ret; +} + +static int add_ubc_irq_resource(struct ubc_node *node, + struct ub_bus_controller *ubc) +{ + int hwirq, trigger, polarity, ret; + struct resource res = {}; + + hwirq = ubc->attr.msg_int; + trigger = !!(ubc->attr.msg_int_attr & UB_MSGQ_INT_TRIGGER_MASK); + polarity = !!(ubc->attr.msg_int_attr & UB_MSGQ_INT_POLARITY_MASK); + + if (bios_mode == ACPI) + ret = ubrt_register_gsi(hwirq, trigger, polarity, "UBUS", &res); + else if (bios_mode == DTS) + ret = dts_register_irq(ubc->ctl_no, 0, "UBUS", &res); + else + ret = -EINVAL; + + if (ret) { + pr_err("register irq fail, ret=%d\n", ret); + return ret; + } + + ret = ubc_dev_new_resource_entry(&res, &ubc->resources); + if (ret) + goto out; + + ubc->queue_virq = res.start; + + pr_info("ubc msgq irq register success\n"); + return 0; + +out: + if (bios_mode == ACPI) + ubrt_unregister_gsi(hwirq); + else if (bios_mode == DTS) + irq_dispose_mapping(res.start); + + return ret; +} + +static void ub_release_ubc_dev(struct device *dev) +{ + struct ub_bus_controller *ubc = to_ub_ubc(dev); + struct device_node *usi_np; + + pr_info("%s release ub bus controller device.\n", ubc->name); + + if (bios_mode == DTS) { + usi_np = irq_domain_get_of_node(dev->msi.domain); + if (usi_np) + of_node_put(usi_np); + } + + remove_ubc_resource(ubc); + kfree(ubc->data); + list_del(&ubc->node); + kfree(ubc); +} + +static int init_ubc(struct ub_bus_controller *ubc) +{ + struct device *dev = &ubc->dev; + int ret; + + INIT_LIST_HEAD(&ubc->devs); + device_initialize(dev); + set_dev_node(dev, pxm_to_node(ubc->attr.proximity_domain)); + + dev->release = ub_release_ubc_dev; + dev->coherent_dma_mask = GENMASK_ULL(31, 0); + dev_set_name(dev, "ub_bus_controller%u", ubc->ctl_no); + + ret = device_add(dev); + if (ret) { + pr_err("Add ub bus controller device failed.\n"); + put_device(&ubc->dev); + } + + return ret; +} + +static void ubc_validate_resources(struct list_head *resources, + unsigned long type) +{ + struct resource_entry *tmp, *entry, *entry2; + struct resource *res1, *res2, *root = NULL; + LIST_HEAD(list); + + WARN_ON((type & IORESOURCE_MEM) == 0); + root = &iomem_resource; + + list_splice_init(resources, &list); + resource_list_for_each_entry_safe(entry, tmp, &list) { + bool can_free = false; + resource_size_t end; + + res1 = entry->res; + if (!(type & res1->flags)) + goto next; + + end = min(res1->end, root->end); + if (end <= res1->start) { + pr_info("ub bus controller resources %pR (ignored, not CPU addressable)\n", + res1); + can_free = true; + goto next; + } else if (res1->end != end) { + pr_info("ub bus controller resources %pR ([%#llx-%#llx] ignored, not CPU addressable)\n", + res1, (unsigned long long)end + 1, + (unsigned long long)res1->end); + res1->end = end; + } + + resource_list_for_each_entry(entry2, resources) { + res2 = entry2->res; + if (!(type & res2->flags)) + continue; + + if (resource_overlaps(res1, res2)) { + res2->start = min(res1->start, res2->start); + res2->end = max(res1->end, res2->end); + pr_warn("ub bus controller resources expanded to %pR; %pR ignored\n", + res2, res1); + can_free = true; + goto next; + } + } + +next: + resource_list_del(entry); + if (can_free) + resource_list_free_entry(entry); + else + resource_list_add_tail(entry, resources); + } +} + +static void ubc_device_add_resources(struct list_head *resources) +{ + struct resource *res, *root = NULL; + struct resource_entry *entry, *tmp; + int ret; + + resource_list_for_each_entry_safe(entry, tmp, resources) { + res = entry->res; + if (!(res->flags & IORESOURCE_MEM)) + continue; + + root = &iomem_resource; + if (res == root) + continue; + + ret = insert_resource(root, res); + if (ret) { + pr_warn("conflict, ignoring controller resources %pR\n", + res); + resource_list_destroy_entry(entry); + } + } +} + +static void ubc_declare_resources(struct ub_bus_controller *ubc) +{ + struct resource_entry *window; + resource_size_t offset; + char addr[SZ_64], *fmt; + struct resource *res; + + /* Show ub bus controller's resources */ + resource_list_for_each_entry(window, &ubc->resources) { + offset = window->offset; + res = window->res; + if (offset) { + fmt = " (bus address [0x%#010llx-0x%#010llx])"; + snprintf(addr, sizeof(addr), fmt, + (unsigned long long)(res->start - offset), + (unsigned long long)(res->end - offset)); + } else { + addr[0] = '\0'; + } + + pr_info("ubc resource %pR%s\n", res, addr); + } +} + +static void ubc_check_and_add_resources(struct ub_bus_controller *ubc) +{ + struct resource_entry *entry, *tmp; + + resource_list_for_each_entry_safe(entry, tmp, &ubc->resources) + if (entry->res->flags & IORESOURCE_DISABLED) + resource_list_destroy_entry(entry); + + ubc_validate_resources(&ubc->resources, IORESOURCE_MEM); + + /* Insert resources into kernel */ + ubc_device_add_resources(&ubc->resources); + + ubc_declare_resources(ubc); +} + static int create_ubc(struct ubc_node *node, u32 ctl_no) { + struct ub_bus_controller *ubc; + int ret; + + ubc = kzalloc(sizeof(*ubc), GFP_KERNEL); + if (!ubc) + return -ENOMEM; + ubc->ctl_no = ctl_no; + + ret = parse_ubc_info(node, ubc); + if (ret) + goto free_ubc; + + ret = add_ubc_mmio_resource(node, ubc); + if (ret) + goto free_vendor; + + ret = add_ubc_irq_resource(node, ubc); + if (ret) + goto free_resource; + + /* after init_ubc, ubc resources will be released in the dev->release */ + ret = init_ubc(ubc); + if (ret) + return ret; + + ubc_check_and_add_resources(ubc); + list_add_tail(&ubc->node, &ubc_list); + return 0; + +free_resource: + remove_ubc_resource(ubc); +free_vendor: + kfree(ubc->data); +free_ubc: + kfree(ubc); + return ret; } static int parse_ubc_table(void *info_node) @@ -99,6 +511,12 @@ static int parse_ubc_table(void *info_node) static void ub_destroy_bus_controllers(void) { + struct ub_bus_controller *ubc, *tmp; + + list_for_each_entry_safe_reverse(ubc, tmp, &ubc_list, node) + device_unregister(&ubc->dev); + + pr_info("ubc destroy success\n"); } void destroy_ubc(void) diff --git a/include/ub/ubfi/ubfi.h b/include/ub/ubfi/ubfi.h index 9131e1da0888..cdaaa1e29bc3 100644 --- a/include/ub/ubfi/ubfi.h +++ b/include/ub/ubfi/ubfi.h @@ -6,13 +6,38 @@ #ifndef _UB_UBFI_UBFI_H_ #define _UB_UBFI_UBFI_H_ +#include +#include +#include +#include #include +/** + * ubrt_register_gsi() - Registering UBC's interrupt into the kernel via ACPI + * @hwirq: GSI IRQ number reported by BIOS + * @trigger: trigger type of the GSI number to be mapped + * @polarity: polarity of the GSI to be mapped + * @name: GSI IRQ name + * @res: Record the soft interrupt number obtained after registration into res + * + * Return: 0 if success or other if failed + */ +int ubrt_register_gsi(u32 hwirq, int trigger, int polarity, const char *name, + struct resource *res); + +/** + * ubrt_unregister_gsi() - Unregistering UBC's interrupt into the kernel via ACPI + * @hwirq: GSI IRQ number reported by BIOS + */ +void ubrt_unregister_gsi(u32 hwirq); + +#if IS_ENABLED(CONFIG_UB_UBFI) extern struct list_head ubc_list; extern u32 ubc_eid_start; extern u32 ubc_eid_end; extern u32 ubc_cna_start; extern u32 ubc_cna_end; extern u8 ubc_feature; +#endif /* CONFIG_UB_UBFI */ #endif /* _UB_UBFI_UBFI_H_ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index b935c713e368..55013d6f1ac8 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -137,15 +137,40 @@ struct ub_driver { struct ub_dynids dynids; }; +struct ubc_common_attr { + u32 int_id_start; + u32 int_id_end; + u64 hpa_base; + u64 hpa_size; + u8 mem_size_limit; + u8 dma_cca; + u16 ummu_map; + u16 proximity_domain; + u64 queue_addr; + u64 queue_size; + u16 queue_depth; + u16 msg_int; + u8 msg_int_attr; + u64 ubc_guid_low; + u64 ubc_guid_high; +}; + struct ub_bus_controller { struct device dev; struct ub_entity *uent; + struct ubc_common_attr attr; + u32 queue_virq; + char name[16]; u32 ctl_no; struct message_device *mdev; + struct list_head resources; struct list_head node; struct list_head devs; struct ub_bus_controller_ops *ops; + bool cluster; + + void *data; }; static inline struct ub_driver *to_ub_driver(struct device_driver *drv) -- Gitee From 7e88eeec0abb8eedef2310adbef787698ec2ea6f Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 09:51:10 +0800 Subject: [PATCH 008/214] ub:ubus: Support for UB Bus DMA Configuration Function ANBZ: #45460 commit fae045218cb429e88a3eb818974ed0fcdd822ea6 openEuler. Support for UB bus dma_configure and dma_cleanup callbacks, to enable UB bus device DMA data path. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/ub-driver.c | 163 ++++++++++++++++++++++++++++++++++++ include/ub/ubus/ubus.h | 15 ++++ 2 files changed, 178 insertions(+) diff --git a/drivers/ub/ubus/ub-driver.c b/drivers/ub/ubus/ub-driver.c index 1922b5062327..c0ebe83984fb 100644 --- a/drivers/ub/ubus/ub-driver.c +++ b/drivers/ub/ubus/ub-driver.c @@ -4,11 +4,174 @@ */ #include +#include +#include +#include #include "ubus_inner.h" +static struct fwnode_handle *iommu_fwnode; +void ub_iommu_fwnode_handle_set(const struct fwnode_handle *fwnode) +{ + iommu_fwnode = (struct fwnode_handle *)fwnode; +} +EXPORT_SYMBOL_GPL(ub_iommu_fwnode_handle_set); + +const struct fwnode_handle *ub_iommu_fwnode_handle_get(void) +{ + return iommu_fwnode; +} +EXPORT_SYMBOL_GPL(ub_iommu_fwnode_handle_get); + +static void ubct_dma_setup(struct device *dev) +{ + struct ub_entity *uent = to_ub_entity(dev); + u64 end, mask, size; + u8 addr_limit; + + addr_limit = uent->ubc->attr.mem_size_limit; + if (!addr_limit) { + pr_warn(FW_BUG "ub bus controller missing memory address limit\n"); + return; + } + + size = (addr_limit >= SZ_64) ? U64_MAX : (1ULL << addr_limit); + end = size - 1; + mask = DMA_BIT_MASK(ilog2(end) + 1); + dev->bus_dma_limit = end; + dev->coherent_dma_mask = min(dev->coherent_dma_mask, mask); + *dev->dma_mask = min(*dev->dma_mask, mask); +} + +static int ubct_iommu_fwspec_init(struct device *dev, u32 id, + struct fwnode_handle *fwnode, + bool *fwspec_exists) +{ + int ret; + + *fwspec_exists = dev_iommu_fwspec_get(dev) != NULL; + ret = iommu_fwspec_init(dev, fwnode); + if (ret) + return ret; + if (*fwspec_exists) + return 0; + + return iommu_fwspec_add_ids(dev, &id, 1); +} + +static int ubct_iommu_configure(struct device *dev, bool *fwspec_exists) +{ + if (!iommu_fwnode) { + dev_err(dev, "ubus's iommu_fwnode not ready\n"); + return -ENODEV; + } + + /* input id is 0, ummu not care */ + return ubct_iommu_fwspec_init(dev, 0, iommu_fwnode, fwspec_exists); +} + +static int handle_iommu_configure_error(struct device *dev, int err) +{ + if (err == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_warn(dev, "Adding to IOMMU failed: %d\n", err); + return 0; +} + +static int ub_hybrid_iommu_configure(struct device *dev) +{ + bool fwspec_exists; + int err; + + err = ubct_iommu_configure(dev, &fwspec_exists); + if (err) { + if (err != -EPROBE_DEFER) + dev_err(dev, "viot iommu configure: %d\n", err); + + return handle_iommu_configure_error(dev, err); + } + if (fwspec_exists) + return 0; + + if (dev->bus && !device_iommu_mapped(dev)) { + err = iommu_probe_device(dev); + if (err) + return handle_iommu_configure_error(dev, err); + } + + return 0; +} + +static int ub_hybrid_dma_configure(struct device *dev, enum dev_dma_attr attr) +{ + int ret; + + if (attr == DEV_DMA_NOT_SUPPORTED) { + set_dma_ops(dev, &dma_dummy_ops); + return 0; + } + + ubct_dma_setup(dev); + + ret = ub_hybrid_iommu_configure(dev); + if (ret == -EPROBE_DEFER) + return -EPROBE_DEFER; + + arch_setup_dma_ops(dev, attr == DEV_DMA_COHERENT); + + return 0; +} + +/* Translate from FW */ +static enum dev_dma_attr ub_dma_attr_trans(u16 dma_attr) +{ + switch (dma_attr) { + case 0: + return DEV_DMA_NON_COHERENT; + case 1: + return DEV_DMA_COHERENT; + default: + return DEV_DMA_NOT_SUPPORTED; + } +} + +static int ub_dma_configure(struct device *dev) +{ + struct ub_driver *udrv = to_ub_driver(dev->driver); + struct ub_entity *uent = to_ub_entity(dev); + struct ub_bus_controller *ubc; + int ret; + + ubc = ub_ubc_get(uent->ubc); + + ret = ub_hybrid_dma_configure(dev, + ub_dma_attr_trans(ubc->attr.dma_cca)); + if (!ret && !udrv->driver_managed_dma) { + ret = iommu_device_use_default_domain(dev); + if (ret) + arch_teardown_dma_ops(dev); + } + + ub_ubc_put(ubc); + dev_info(dev, "dma_configure ret = %d\n", ret); + return ret; +} + +static void ub_dma_cleanup(struct device *dev) +{ + struct ub_driver *driver = to_ub_driver(dev->driver); + + if (!driver->driver_managed_dma) + iommu_device_unuse_default_domain(dev); + + dev_dbg(dev, "dma_cleanup\n"); +} + struct bus_type ub_bus_type = { .name = "ub", + .dma_configure = ub_dma_configure, + .dma_cleanup = ub_dma_cleanup, }; EXPORT_SYMBOL_GPL(ub_bus_type); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 55013d6f1ac8..da072589b5e2 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -121,6 +122,13 @@ struct ub_dynids { * created once it is bound to the driver. * @driver: Driver model structure. * @dynids: List of dynamically added device IDs. + * @driver_managed_dma: Device driver doesn't use kernel DMA API for DMA. + * For most device drivers, no need to care about this flag + * as long as all DMAs are handled through the kernel DMA API. + * For some special ones, for example VFIO drivers, they know + * how to manage the DMA themselves and set this flag so that + * the IOMMU layer will allow them to setup and manage their + * own I/O address space. */ struct ub_driver { struct list_head node; @@ -135,6 +143,7 @@ struct ub_driver { const struct attribute_group **dev_groups; struct device_driver driver; struct ub_dynids dynids; + bool driver_managed_dma; }; struct ubc_common_attr { @@ -192,6 +201,9 @@ static inline const char *ub_name(const struct ub_entity *pue) extern struct bus_type ub_bus_type; #define dev_is_ub(d) ((d)->bus == &ub_bus_type) +void ub_iommu_fwnode_handle_set(const struct fwnode_handle *fwnode); +const struct fwnode_handle *ub_iommu_fwnode_handle_get(void); + /** * ub_entity_get() - Atomically increment the reference count for the entity. * @uent: UB entity pointer. @@ -256,6 +268,9 @@ static inline int ub_get_bus_controller(struct ub_entity *uents[], { return -ENODEV; } static inline void ub_put_bus_controller(struct ub_entity *uents[], unsigned int num) {} +static inline void ub_iommu_fwnode_handle_set(const struct fwnode_handle *fwnode) {} +static inline const struct fwnode_handle *ub_iommu_fwnode_handle_get(void) +{ return NULL; } static inline int __ub_register_driver(struct ub_driver *drv, struct module *owner, const char *mod_name) -- Gitee From d5fe458985e6290e18123cdebeff1ffb6630e594 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 19 Sep 2025 11:21:26 +0800 Subject: [PATCH 009/214] ub:ubus: Add Ubus setting configuration space function ANBZ: #45460 commit 8733e7995bd92d8159ba0df49df547f4b9c4862b openEuler. Add Ubus setting configuration space function, including basic function Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 4 +- drivers/ub/ubus/config.c | 103 +++++++++++++++++++++++++++++++++ drivers/ub/ubus/msg.c | 18 ++++++ drivers/ub/ubus/msg.h | 13 +++++ drivers/ub/ubus/ubus.h | 2 + drivers/ub/ubus/ubus_config.c | 105 ++++++++++++++++++++++++++++++++++ drivers/ub/ubus/ubus_config.h | 9 +++ drivers/ub/ubus/ubus_driver.c | 2 + drivers/ub/ubus/ubus_inner.h | 10 ++++ include/ub/ubus/ubus.h | 3 + 10 files changed, 267 insertions(+), 2 deletions(-) create mode 100644 drivers/ub/ubus/config.c create mode 100644 drivers/ub/ubus/ubus_config.c create mode 100644 drivers/ub/ubus/ubus_config.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 8f75a1860121..fb5edf76f9eb 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ -obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o +obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o -ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o +ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/config.c b/drivers/ub/ubus/config.c new file mode 100644 index 000000000000..721a9c62027b --- /dev/null +++ b/drivers/ub/ubus/config.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include "ubus_inner.h" + +static const struct cfg_ops { + read_byte_f cfg_read_byte; + read_word_f cfg_read_word; + read_dword_f cfg_read_dword; + write_byte_f cfg_write_byte; + write_word_f cfg_write_word; + write_dword_f cfg_write_dword; +} cfg_ops_t; + +static struct cfg_ops ub_cfg_ops = {}; + +int register_ub_cfg_read_ops(read_byte_f rb, read_word_f rw, read_dword_f rdw) +{ + if (rb && rw && rdw) { + ub_cfg_ops.cfg_read_byte = rb; + ub_cfg_ops.cfg_read_word = rw; + ub_cfg_ops.cfg_read_dword = rdw; + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(register_ub_cfg_read_ops); + +int register_ub_cfg_write_ops(write_byte_f wb, write_word_f ww, write_dword_f wdw) +{ + if (wb && ww && wdw) { + ub_cfg_ops.cfg_write_byte = wb; + ub_cfg_ops.cfg_write_word = ww; + ub_cfg_ops.cfg_write_dword = wdw; + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(register_ub_cfg_write_ops); + +void unregister_ub_cfg_ops(void) +{ + memset(&ub_cfg_ops, 0, sizeof(struct cfg_ops)); +} +EXPORT_SYMBOL_GPL(unregister_ub_cfg_ops); + +int ub_cfg_read_byte(struct ub_entity *uent, u64 pos, u8 *val) +{ + if (!ub_cfg_ops.cfg_read_byte) + return -ENODEV; + + return ub_cfg_ops.cfg_read_byte(uent, pos, val); +} +EXPORT_SYMBOL_GPL(ub_cfg_read_byte); + +int ub_cfg_read_word(struct ub_entity *uent, u64 pos, u16 *val) +{ + if (!ub_cfg_ops.cfg_read_word) + return -ENODEV; + + return ub_cfg_ops.cfg_read_word(uent, pos, val); +} +EXPORT_SYMBOL_GPL(ub_cfg_read_word); + +int ub_cfg_read_dword(struct ub_entity *uent, u64 pos, u32 *val) +{ + if (!ub_cfg_ops.cfg_read_dword) + return -ENODEV; + + return ub_cfg_ops.cfg_read_dword(uent, pos, val); +} +EXPORT_SYMBOL_GPL(ub_cfg_read_dword); + +int ub_cfg_write_byte(struct ub_entity *uent, u64 pos, u8 val) +{ + if (!ub_cfg_ops.cfg_write_byte) + return -ENODEV; + + return ub_cfg_ops.cfg_write_byte(uent, pos, val); +} +EXPORT_SYMBOL_GPL(ub_cfg_write_byte); + +int ub_cfg_write_word(struct ub_entity *uent, u64 pos, u16 val) +{ + if (!ub_cfg_ops.cfg_write_word) + return -ENODEV; + + return ub_cfg_ops.cfg_write_word(uent, pos, val); +} +EXPORT_SYMBOL_GPL(ub_cfg_write_word); + +int ub_cfg_write_dword(struct ub_entity *uent, u64 pos, u32 val) +{ + if (!ub_cfg_ops.cfg_write_dword) + return -ENODEV; + + return ub_cfg_ops.cfg_write_dword(uent, pos, val); +} +EXPORT_SYMBOL_GPL(ub_cfg_write_dword); diff --git a/drivers/ub/ubus/msg.c b/drivers/ub/ubus/msg.c index 5bdad62050b1..c7be05f350bb 100644 --- a/drivers/ub/ubus/msg.c +++ b/drivers/ub/ubus/msg.c @@ -116,3 +116,21 @@ void message_remove_device(struct ub_entity *uent) ops->remove_dev(uent); dev_message_put(uent); } + +int message_sync_request(struct message_device *mdev, struct msg_info *info, + u8 code) +{ + if (mdev->ops->sync_request) + return mdev->ops->sync_request(mdev, info, code); + + return -ENOTTY; +} + +int message_send(struct message_device *mdev, struct msg_info *info, + u8 code) +{ + if (mdev->ops->send) + return mdev->ops->send(mdev, info, code); + + return -ENOTTY; +} diff --git a/drivers/ub/ubus/msg.h b/drivers/ub/ubus/msg.h index 86e69abf3fb8..fd1d9df7d20c 100644 --- a/drivers/ub/ubus/msg.h +++ b/drivers/ub/ubus/msg.h @@ -190,11 +190,17 @@ enum message_tx_type { * struct message_ops - message ops and capabilities * @probe_dev: probe ub_entity to init message * @remove_dev: remove ub_entity to uninit message + * @sync_request: send message to target ub_entity and wait response + * @send: send message to target ub_entity but not wait response * @owner: Driver module providing these ops */ struct message_ops { int (*probe_dev)(struct ub_entity *uent); void (*remove_dev)(struct ub_entity *uent); + int (*sync_request)(struct message_device *mdev, struct msg_info *info, + u8 code); + int (*send)(struct message_device *mdev, struct msg_info *info, + u8 code); struct module *owner; }; @@ -236,9 +242,16 @@ static inline void message_info_init(struct msg_info *info, struct ub_entity *ue info->rsp_pkt_size = (u16)size; } +void ub_msg_pkt_header_init(struct msg_pkt_header *header, struct ub_entity *uent, + u16 plen, u8 code, bool flag); + int message_device_register(struct message_device *mdev); void message_device_unregister(struct message_device *mdev); int message_probe_device(struct ub_entity *uent); void message_remove_device(struct ub_entity *uent); +int message_sync_request(struct message_device *mdev, struct msg_info *info, + u8 code); +int message_send(struct message_device *mdev, struct msg_info *info, + u8 code); #endif /* __MSG_H__ */ diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index 3f455ab68c11..51828b3c9806 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -8,6 +8,8 @@ #include #include +#define UB_CP_UPI 0x7FFF + int ub_host_probe(void); void ub_host_remove(void); diff --git a/drivers/ub/ubus/ubus_config.c b/drivers/ub/ubus/ubus_config.c new file mode 100644 index 000000000000..698e465607aa --- /dev/null +++ b/drivers/ub/ubus/ubus_config.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus config: " fmt + +#include "ubus.h" +#include "msg.h" +#include "ubus_config.h" +#include "ubus_inner.h" + +struct cfg_msg_pld_req { + /* DW0 */ + u32 rsvd0 : 4; + u32 byte_enable : 4; + u32 rsvd1 : 8; + u32 entity_idx : 16; + /* DW1 */ + u32 req_addr; + /* DW2 */ + u32 rsvd2; + /* DW3 */ + u32 write_data; +}; + +struct cfg_msg_pld_rsp { + /* DW0 */ + u32 read_data; + /* DW1 */ + u32 rsvd1; + /* DW2 */ + u32 rsvd2; + /* DW3 */ + u32 rsvd3; +}; + +struct cfg_msg_req_pkt { + struct msg_pkt_header header; + struct cfg_msg_pld_req req_payload; +}; + +struct cfg_msg_rsp_pkt { + struct msg_pkt_header header; + struct cfg_msg_pld_rsp rsp_payload; +}; + +enum ub_cfg_sub_msg_code { + UB_CFG0_READ = 0, + UB_CFG0_WRITE = 1, + UB_CFG1_READ = 2, + UB_CFG1_WRITE = 3 +}; + +#define CFG_MSG_PLD_SIZE 16 + +struct ub_cfg { + u8 size; + u8 pos_mask; + u8 byte_mask; +}; + +static void ub_msg_extended_header_init(struct msg_extended_header *msgetah, + u16 plen, u8 code) +{ + msgetah->plen = plen; + msgetah->msg_code = msg_code(code); + msgetah->type = msg_type(code); + msgetah->sub_msg_code = sub_msg_code(code); +} + +void ub_msg_pkt_header_init(struct msg_pkt_header *header, struct ub_entity *uent, + u16 plen, u8 code, bool flag) +{ + struct compact_network_header *cnth = &header->nth; + struct ub_link_header *ulh = &header->ulh; + struct ub_entity *ubc_uent = uent->ubc->uent; + u32 seid = ubc_uent->eid; + u16 scna = (u16)ubc_uent->cna; + u32 deid = uent->eid; + u16 dcna = (u16)uent->cna; + + if (flag) { + dcna = scna; + deid = seid; + } + + ulh->cfg = UB_COMPACT_LINK_CFG; + + cnth->nth_nlp = NTH_NLP_WITH_TPH; + cnth->scna = scna; + cnth->dcna = dcna; + + header->ctph_nlp = CTPH_NLP_UPI_40BITS_UEID; + header->tp_opcode = CTPH_OPCODE_NOT_CNP; + header->pad = 0; + header->upi = uent->ubc->cluster ? uent->upi : UB_CP_UPI; + header->seid_h = seid_high(seid); + header->seid_l = seid_low(seid); + header->deid = deid; + header->ta_opcode = TAH_OPCODE_MSG; + + ub_msg_extended_header_init(&header->msgetah, plen, code); +} +EXPORT_SYMBOL_GPL(ub_msg_pkt_header_init); diff --git a/drivers/ub/ubus/ubus_config.h b/drivers/ub/ubus/ubus_config.h new file mode 100644 index 000000000000..b7f733ed6549 --- /dev/null +++ b/drivers/ub/ubus/ubus_config.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __UBUS_CONFIG_H__ +#define __UBUS_CONFIG_H__ + +#endif /* __UBUS_CONFIG_H__ */ diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index fc39dd1cd534..a14cadf75725 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -15,7 +15,9 @@ #include "sysfs.h" #include "ubus.h" +#include "ubus_config.h" #include "ubus_controller.h" +#include "ubus_inner.h" static DEFINE_MUTEX(manage_subsystem_ops_mutex); static const struct ub_manage_subsystem_ops *manage_subsystem_ops; diff --git a/drivers/ub/ubus/ubus_inner.h b/drivers/ub/ubus/ubus_inner.h index 86a9c4130a1a..dd576a6c41a3 100644 --- a/drivers/ub/ubus/ubus_inner.h +++ b/drivers/ub/ubus/ubus_inner.h @@ -7,6 +7,16 @@ #include +typedef int (*read_byte_f)(struct ub_entity *uent, u64 pos, u8 *val); +typedef int (*read_word_f)(struct ub_entity *uent, u64 pos, u16 *val); +typedef int (*read_dword_f)(struct ub_entity *uent, u64 pos, u32 *val); +typedef int (*write_byte_f)(struct ub_entity *uent, u64 pos, u8 val); +typedef int (*write_word_f)(struct ub_entity *uent, u64 pos, u16 val); +typedef int (*write_dword_f)(struct ub_entity *uent, u64 pos, u32 val); + +int register_ub_cfg_read_ops(read_byte_f rb, read_word_f rw, read_dword_f rdw); +int register_ub_cfg_write_ops(write_byte_f wb, write_word_f ww, write_dword_f wdw); +void unregister_ub_cfg_ops(void); struct ub_bus_controller *ub_ubc_get(struct ub_bus_controller *ubc); void ub_ubc_put(struct ub_bus_controller *ubc); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index da072589b5e2..d7f48cd0a430 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -76,11 +76,14 @@ struct ub_entity { u16 mod_vendor; /* entity's module vendor and module id */ u16 module; unsigned int cna; + unsigned int eid; /* entity topology info */ struct ub_bus_controller *ubc; struct dev_message *message; + + u16 upi; }; struct ub_dynids { -- Gitee From 122f244510b5388585d98f4c5c3c6899f264f212 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 10:24:55 +0800 Subject: [PATCH 010/214] ub:ubus: Support for UB Bus Port Management Framework ANBZ: #45460 commit 2fd0fb57502b20333189012551f5ee2fb1ce7b55 openEuler. Provide basic interface capabilities for UB port application, release, initialization, and connection. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/port.c | 199 +++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/port.h | 21 +++++ include/ub/ubus/ubus.h | 26 +++++ 4 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/port.c create mode 100644 drivers/ub/ubus/port.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index fb5edf76f9eb..90ab6925210e 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -2,6 +2,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o -ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o +ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/port.c b/drivers/ub/ubus/port.c new file mode 100644 index 000000000000..6b70c229463b --- /dev/null +++ b/drivers/ub/ubus/port.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus port: " fmt + +#include "ubus.h" +#include "port.h" + +struct ub_port_attribute { + struct attribute attr; + ssize_t (*show)(struct ub_port *port, char *buf); + ssize_t (*store)(struct ub_port *port, const char *buf, size_t count); +}; + +#define to_ub_port(o) container_of(o, struct ub_port, kobj) +#define to_ub_port_attr(a) container_of(a, struct ub_port_attribute, attr) + +static const struct attribute_group *ub_port_groups[] = { + NULL +}; + +static ssize_t ub_port_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct ub_port_attribute *attribute = to_ub_port_attr(attr); + struct ub_port *port = to_ub_port(kobj); + + if (!attribute->show) + return -EIO; + + return attribute->show(port, buf); +} + +static ssize_t ub_port_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct ub_port_attribute *attribute = to_ub_port_attr(attr); + struct ub_port *port = to_ub_port(kobj); + + if (!attribute->store) + return -EIO; + + return attribute->store(port, buf, count); +} + +static const struct sysfs_ops ub_port_sysfs_ops = { + .show = ub_port_attr_show, + .store = ub_port_attr_store, +}; + +static const struct kobj_type ub_port_ktype = { + .sysfs_ops = &ub_port_sysfs_ops, + .default_groups = ub_port_groups, +}; + +void ub_port_disconnect(struct ub_port *port) +{ + struct ub_port *r_port; + + if (!port || !port->r_uent) + return; + + r_port = port->r_uent->ports + port->r_index; + r_port->r_uent = NULL; + r_port->r_guid = guid_null; + + port->r_uent = NULL; + port->r_guid = guid_null; +} + +/* connect two ports, caller should make sure that the ports match */ +void ub_port_connect(struct ub_port *port, struct ub_port *r_port) +{ + if (!port || !r_port) + return; + + port->r_index = r_port->index; + port->r_uent = r_port->uent; + r_port->r_index = port->index; + r_port->r_uent = port->uent; +} + +bool ub_check_and_connect(struct ub_port *port, struct ub_entity *r_uent) +{ + struct ub_port *r_port; + + if (!port || !r_uent) + return false; + + if (port->r_index >= r_uent->port_nums) { + pr_err("port%u should connect to port%u, but remote device has only %u ports\n", + port->index, port->r_index, r_uent->port_nums); + return false; + } + + r_port = r_uent->ports + port->r_index; + + if (r_port->r_uent) { + pr_err("port%u should connect to port%u, which is already connected\n", + port->index, port->r_index); + return false; + } + + ub_port_connect(port, r_port); + + return true; +} + +static int ub_port_config(struct ub_port *port) +{ + return 0; +} + +int ub_ports_add(struct ub_entity *uent) +{ + struct ub_port *port; + int ret; + + if (!uent) + return -EINVAL; + + for_each_uent_port(port, uent) { + ret = ub_port_config(port); + if (ret) { + ub_err(uent, + "config port%u failed, stop adding ports\n", + port->index); + return ret; + } + + ret = kobject_add(&port->kobj, &uent->dev.kobj, "port%u", + port->index); + if (ret) { + ub_warn(uent, "cannot add port%u, stop adding ports\n", + port->index); + return ret; + } + } + + return 0; +} + +void ub_ports_del(struct ub_entity *uent) +{ + struct ub_port *port; + + if (!uent) + return; + + for_each_uent_port(port, uent) + kobject_put(&port->kobj); +} + +static void ub_port_init(struct ub_entity *uent, struct ub_port *port) +{ + port->uent = uent; + port->type = PHYSICAL; + port->cna = 0; + port->r_uent = NULL; + port->r_index = 0; + port->r_guid = guid_null; + bitmap_zero(port->cna_maps, UB_MAX_CNA_NUM); + bitmap_zero(port->cap_map, UB_PORT_CAP_NUM); + kobject_init(&port->kobj, &ub_port_ktype); +} + +int ub_ports_setup(struct ub_entity *uent) +{ + struct ub_port *port; + + if (!uent || !uent->port_nums) + return -EINVAL; + + if (uent->ports) + return 0; + + uent->ports = kvcalloc(uent->port_nums, sizeof(*port), GFP_KERNEL); + if (!uent->ports) + return -ENOMEM; + + for_each_uent_port(port, uent) { + port->index = port - uent->ports; + ub_port_init(uent, port); + } + + return 0; +} + +void ub_ports_unset(struct ub_entity *uent) +{ + if (!uent) + return; + + kvfree(uent->ports); + uent->ports = NULL; + pr_debug("port release\n"); +} diff --git a/drivers/ub/ubus/port.h b/drivers/ub/ubus/port.h new file mode 100644 index 000000000000..8f8c1d78c2be --- /dev/null +++ b/drivers/ub/ubus/port.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ +#ifndef __PORT_H__ +#define __PORT_H__ + +#define for_each_uent_port(p, d) \ + for ((p) = (d)->ports; ((p) - (d)->ports) < (d)->port_nums; (p)++) + +struct ub_port; +struct ub_entity; +void ub_port_disconnect(struct ub_port *port); +void ub_port_connect(struct ub_port *port, struct ub_port *r_port); +bool ub_check_and_connect(struct ub_port *port, struct ub_entity *r_uent); +int ub_ports_add(struct ub_entity *uent); +void ub_ports_del(struct ub_entity *uent); +int ub_ports_setup(struct ub_entity *uent); +void ub_ports_unset(struct ub_entity *uent); + +#endif /* __PORT_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index d7f48cd0a430..b1c7aba442ff 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -63,6 +63,28 @@ static inline int ub_show_guid(struct ub_guid *guid, char *buf) guid->bits.reserved, guid->bits.seq_num); } +enum ub_port_type { + PHYSICAL, + VIRTUAL, +}; + +#define UB_MAX_CNA_NUM SZ_64K +#define UB_PORT_CAP_NUM SZ_256 + +struct ub_port { + struct ub_entity *uent; + u16 index; + enum ub_port_type type; + u32 cna; + struct ub_entity *r_uent; /* If valid, also represent link up */ + u16 r_index; + guid_t r_guid; + struct kobject kobj; + DECLARE_BITMAP(cna_maps, UB_MAX_CNA_NUM); + /* cap cache */ + DECLARE_BITMAP(cap_map, UB_PORT_CAP_NUM); +}; + struct ub_entity { /* Driver framework base info */ struct device dev; @@ -81,6 +103,10 @@ struct ub_entity { /* entity topology info */ struct ub_bus_controller *ubc; + /* entity port info */ + u16 port_nums; + struct ub_port *ports; + struct dev_message *message; u16 upi; -- Gitee From 4bc347ee600988c66fcafd377e869d04ea5b931b Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 19 Sep 2025 13:58:00 +0800 Subject: [PATCH 011/214] ub:ubus: Support Ubus read/write configuration functions ANBZ: #45460 commit 280895301d3bc3507606cbd2e3cdccba75a8dcdc openEuler. Support Ubus read/write configuration functions, read/write by 1, 2, 4 byte. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/ubus_config.c | 281 ++++++++++++++++++++++++++++++++++ drivers/ub/ubus/ubus_config.h | 5 + drivers/ub/ubus/ubus_driver.c | 11 ++ include/ub/ubus/ubus.h | 44 ++++++ 4 files changed, 341 insertions(+) diff --git a/drivers/ub/ubus/ubus_config.c b/drivers/ub/ubus/ubus_config.c index 698e465607aa..c9b07b63e922 100644 --- a/drivers/ub/ubus/ubus_config.c +++ b/drivers/ub/ubus/ubus_config.c @@ -60,6 +60,27 @@ struct ub_cfg { u8 byte_mask; }; +static struct ub_cfg ub_cfg_param[] = { { 0x1, 0x0, 0x1 }, + { 0x2, 0x1, 0x3 }, + { 0x4, 0x3, 0xf } }; +#define UB_CFG_PARAM_CNT ARRAY_SIZE(ub_cfg_param) + +static bool pos_size_valid(u64 pos, u8 size) +{ + struct ub_cfg *cfg; + u8 i; + + if (pos >= UB_CFG_SAPCE_SLICE_END) + return false; + + for (i = 0; i < UB_CFG_PARAM_CNT; i++) { + cfg = &ub_cfg_param[i]; + if (cfg->size == size) + return !(pos & cfg->pos_mask); + } + return false; /* size invalid */ +} + static void ub_msg_extended_header_init(struct msg_extended_header *msgetah, u16 plen, u8 code) { @@ -103,3 +124,263 @@ void ub_msg_pkt_header_init(struct msg_pkt_header *header, struct ub_entity *uen ub_msg_extended_header_init(&header->msgetah, plen, code); } EXPORT_SYMBOL_GPL(ub_msg_pkt_header_init); + +int ub_check_cfg_msg_code(struct device *dev, u8 req_code, u8 rsp_code) +{ + u8 req_sub = sub_msg_code(req_code); + u8 rsp_sub = sub_msg_code(rsp_code); + u8 req = msg_code(req_code); + u8 rsp = msg_code(rsp_code); + + if (req != UB_MSG_CODE_CFG || rsp != UB_MSG_CODE_CFG) { + dev_err(dev, "The message code is incorrect, req message code=%#x, rsp message code=%#x\n", + req, rsp); + return -EIO; + } + + if (msg_type(req_code) == MSG_REQ && + msg_type(rsp_code) == MSG_RSP && req_sub == rsp_sub) + return 0; + + dev_err(dev, "The response code is incorrect, req code=%#x, rsp code=%#x\n", + req_code, rsp_code); + return -EIO; +} +EXPORT_SYMBOL_GPL(ub_check_cfg_msg_code); + +static int ub_sync_cfg_rsp_check(struct ub_entity *uent, + struct cfg_msg_req_pkt *req_pkt, + struct cfg_msg_rsp_pkt *rsp_pkt) +{ + struct msg_pkt_header *req_header = &req_pkt->header; + struct msg_pkt_header *rsp_header = &rsp_pkt->header; + u32 rsp_seid = eid_gen(rsp_header->seid_h, rsp_header->seid_l); + u32 req_seid = eid_gen(req_header->seid_h, req_header->seid_l); + u8 rsp_status = rsp_header->msgetah.rsp_status; + + if (rsp_status != UB_MSG_RSP_SUCCESS) { + ub_err(uent, "Message response error, rsp_status=%#x\n", + rsp_status); + return -EIO; + } + + if (rsp_header->nth.scna != req_header->nth.dcna || + rsp_header->nth.dcna != req_header->nth.scna) { + ub_err(uent, "CNA mismatch, req_scna=%#x req_dcna=%#x, rsp_scna=%#x rsp_dcna=%#x\n", + req_header->nth.scna, req_header->nth.dcna, + rsp_header->nth.scna, rsp_header->nth.dcna); + return -EIO; + } + + if (rsp_seid != req_header->deid || rsp_header->deid != req_seid) { + ub_err(uent, "EID mismatch, req_seid=%#x req_deid=%#x, rsp_seid=%#x rsp_deid=%#x\n", + req_seid, req_header->deid, rsp_seid, rsp_header->deid); + return -EIO; + } + + if (rsp_header->msgetah.plen != CFG_MSG_PLD_SIZE) { + ub_err(uent, "The packet length is incorrect, len=%#x\n", + rsp_header->msgetah.plen); + return -EIO; + } + + return ub_check_cfg_msg_code(&uent->dev, req_header->msgetah.code, + rsp_header->msgetah.code); +} + +void ub_sync_cfg_rsp_handle(struct cfg_msg_pld_rsp *rsp, u8 size, + u64 pos, bool write, u32 *val) +{ +#define UB_CFG_REG_SIZE 4 + u8 pos_in_reg = pos % UB_CFG_REG_SIZE; + u32 read_data; + + if (!write) { + read_data = rsp->read_data >> (pos_in_reg * BITS_PER_BYTE); + if (size == sizeof(u8)) + *(u8 *)val = read_data; + else if (size == sizeof(u16)) + *(u16 *)val = read_data; + else + *val = read_data; + } +} + +static u8 gen_cfg_sub_msg_code(bool is_write, u64 pos) +{ + if (pos >= UB_CFG1_BASIC_SLICE && pos < UB_PORT_SLICE_START) + return is_write ? UB_CFG1_WRITE : UB_CFG1_READ; + else + return is_write ? UB_CFG0_WRITE : UB_CFG0_READ; +} + +static void ub_msg_pkt_req_init(struct ub_entity *uent, u8 size, u64 pos, u32 *val, + struct cfg_msg_pld_req *req) +{ + u8 bt_mask = ub_cfg_param[size >> 1].byte_mask; + + if (val) { + if (size == sizeof(u8)) + req->write_data = *(u8 *)val; + else if (size == sizeof(u16)) + req->write_data = *(u16 *)val; + else + req->write_data = *val; + req->write_data <<= ((pos % sizeof(u32)) * BITS_PER_BYTE); + } + + req->byte_enable = bt_mask << (u8)(pos % sizeof(u32)); + req->entity_idx = uent->entity_idx; + /* The address is in four bytes. */ + req->req_addr = pos / sizeof(u32); +} + +static int ub_sync_cfg(struct ub_entity *uent, u8 size, u64 pos, bool iswrite, + u32 *val) +{ + struct cfg_msg_req_pkt req_pkt = {}; + struct cfg_msg_rsp_pkt rsp_pkt = {}; + struct msg_info info = {}; + u8 sub_msg_code; + int ret; + + if (!pos_size_valid(pos, size)) { + ub_err(uent, "pos or size invalid, pos=%#llx, size=%#x\n", pos, + size); + return -EINVAL; + } + + if (!iswrite) + memset(val, 0xFF, size); + + sub_msg_code = gen_cfg_sub_msg_code(iswrite, pos); + ub_msg_pkt_header_init(&req_pkt.header, uent, CFG_MSG_PLD_SIZE, + code_gen(UB_MSG_CODE_CFG, sub_msg_code, + MSG_REQ), false); + + ub_msg_pkt_req_init(uent, size, pos, (iswrite ? val : NULL), + &req_pkt.req_payload); + + message_info_init(&info, uent, &req_pkt, &rsp_pkt, + (MSG_CFG_PKT_SIZE << MSG_REQ_SIZE_OFFSET) | + MSG_CFG_PKT_SIZE); + ret = message_sync_request(uent->message->mdev, &info, + req_pkt.header.msgetah.code); + if (ret) + return ret; + + ret = ub_sync_cfg_rsp_check(uent, &req_pkt, &rsp_pkt); + if (!ret) + ub_sync_cfg_rsp_handle(&rsp_pkt.rsp_payload, size, pos, iswrite, val); + + return ret; +} + +int ub_send_cfg(struct ub_entity *uent, u8 size, u64 pos, u32 *val) +{ + struct cfg_msg_req_pkt req_pkt = {}; + struct msg_info info = {}; + u8 sub_msg_code; + + if (!uent || !uent->message || !uent->message->mdev) { + pr_err("uent or message or mdev is null\n"); + return -EINVAL; + } + + if (!pos_size_valid(pos, size)) { + pr_err("pos or size invalid, pos=%#llx, size=%u\n", pos, size); + return -EINVAL; + } + + sub_msg_code = gen_cfg_sub_msg_code(true, pos); + ub_msg_pkt_header_init(&req_pkt.header, uent, CFG_MSG_PLD_SIZE, + code_gen(UB_MSG_CODE_CFG, sub_msg_code, + MSG_REQ), false); + + ub_msg_pkt_req_init(uent, size, pos, val, + &req_pkt.req_payload); + + message_info_init(&info, uent, &req_pkt, NULL, + (MSG_CFG_PKT_SIZE << MSG_REQ_SIZE_OFFSET) | + MSG_CFG_PKT_SIZE); + return message_send(uent->message->mdev, &info, + req_pkt.header.msgetah.code); +} + +int __ub_cfg_read_byte(struct ub_entity *uent, u64 pos, u8 *val) +{ + if (!uent || !uent->message || !uent->message->mdev || !val) { + pr_err("uent or message or mdev is null\n"); + return -EINVAL; + } + + return ub_sync_cfg(uent, (u8)sizeof(u8), pos, false, (u32 *)val); +} + +int __ub_cfg_read_word(struct ub_entity *uent, u64 pos, u16 *val) +{ + if (!uent || !uent->message || !uent->message->mdev || !val) { + pr_err("uent or message or mdev is null\n"); + return -EINVAL; + } + + return ub_sync_cfg(uent, (u8)sizeof(u16), pos, false, (u32 *)val); +} + +int __ub_cfg_read_dword(struct ub_entity *uent, u64 pos, u32 *val) +{ + if (!uent || !uent->message || !uent->message->mdev || !val) { + pr_err("uent or message or mdev is null\n"); + return -EINVAL; + } + + return ub_sync_cfg(uent, (u8)sizeof(u32), pos, false, val); +} + +int __ub_cfg_write_byte(struct ub_entity *uent, u64 pos, u8 val) +{ + if (!uent || !uent->message || !uent->message->mdev) { + pr_err("uent or message or mdev is null\n"); + return -EINVAL; + } + + return ub_sync_cfg(uent, (u8)sizeof(u8), pos, true, (u32 *)&val); +} + +int __ub_cfg_write_word(struct ub_entity *uent, u64 pos, u16 val) +{ + if (!uent || !uent->message || !uent->message->mdev) { + pr_err("uent or message or mdev is null\n"); + return -EINVAL; + } + + return ub_sync_cfg(uent, (u8)sizeof(u16), pos, true, (u32 *)&val); +} + +int __ub_cfg_write_dword(struct ub_entity *uent, u64 pos, u32 val) +{ + if (!uent || !uent->message || !uent->message->mdev) { + pr_err("uent or message or mdev is null\n"); + return -EINVAL; + } + + return ub_sync_cfg(uent, (u8)sizeof(u32), pos, true, (u32 *)&val); +} + +int ub_cfg_ops_init(void) +{ + int ret; + + ret = register_ub_cfg_read_ops(__ub_cfg_read_byte, __ub_cfg_read_word, + __ub_cfg_read_dword); + if (ret) + return ret; + + ret = register_ub_cfg_write_ops(__ub_cfg_write_byte, + __ub_cfg_write_word, + __ub_cfg_write_dword); + if (ret) + unregister_ub_cfg_ops(); + + return ret; +} diff --git a/drivers/ub/ubus/ubus_config.h b/drivers/ub/ubus/ubus_config.h index b7f733ed6549..5971c87e481b 100644 --- a/drivers/ub/ubus/ubus_config.h +++ b/drivers/ub/ubus/ubus_config.h @@ -6,4 +6,9 @@ #ifndef __UBUS_CONFIG_H__ #define __UBUS_CONFIG_H__ +struct ub_entity; +int ub_cfg_ops_init(void); +int ub_check_cfg_msg_code(struct device *dev, u8 req_code, u8 rsp_code); +int ub_send_cfg(struct ub_entity *uent, u8 size, u64 pos, u32 *val); + #endif /* __UBUS_CONFIG_H__ */ diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index a14cadf75725..1ad798c2aa84 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -509,13 +509,24 @@ void ub_bus_type_uninit(void) int ub_host_probe(void) { + int ret; + ub_bus_type_init(); + ret = ub_cfg_ops_init(); + if (ret) + goto ub_cfg_ops_init_fail; + return 0; + +ub_cfg_ops_init_fail: + ub_bus_type_uninit(); + return ret; } EXPORT_SYMBOL_GPL(ub_host_probe); void ub_host_remove(void) { + unregister_ub_cfg_ops(); ub_bus_type_uninit(); } EXPORT_SYMBOL_GPL(ub_host_remove); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index b1c7aba442ff..b16ece64a22e 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -99,6 +99,7 @@ struct ub_entity { u16 module; unsigned int cna; unsigned int eid; + unsigned short entity_idx; /* entity topology info */ struct ub_bus_controller *ubc; @@ -233,6 +234,37 @@ extern struct bus_type ub_bus_type; void ub_iommu_fwnode_handle_set(const struct fwnode_handle *fwnode); const struct fwnode_handle *ub_iommu_fwnode_handle_get(void); +/** + * ub_cfg_read_byte() - 1 byte configuration access read. + * @uent: UB entity. + * @pos: Config space address. + * @val: Output buffer. + * + * Initiate configuration access to the specified address of the entity + * configuration space and read 1 byte. + * + * Context: Any context, It will take spin_lock_irqsave()/spin_unlock_restore() + * Return: 0 if success, or negative value if failed. + */ +int ub_cfg_read_byte(struct ub_entity *uent, u64 pos, u8 *val); +int ub_cfg_read_word(struct ub_entity *uent, u64 pos, u16 *val); +int ub_cfg_read_dword(struct ub_entity *uent, u64 pos, u32 *val); +/** + * ub_cfg_write_byte() - 1 byte configuration access write. + * @uent: UB entity. + * @pos: Config space address. + * @val: Data. + * + * Initiate configuration access to the specified address of the entity + * configuration space and write 1 byte. + * + * Context: Any context, It will take spin_lock_irqsave()/spin_unlock_restore() + * Return: 0 if success, or negative value if failed. + */ +int ub_cfg_write_byte(struct ub_entity *uent, u64 pos, u8 val); +int ub_cfg_write_word(struct ub_entity *uent, u64 pos, u16 val); +int ub_cfg_write_dword(struct ub_entity *uent, u64 pos, u32 val); + /** * ub_entity_get() - Atomically increment the reference count for the entity. * @uent: UB entity pointer. @@ -291,6 +323,18 @@ void ub_unregister_driver(struct ub_driver *drv); static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) { return NULL; } static inline void ub_entity_put(struct ub_entity *uent) {} +static inline int ub_cfg_read_byte(struct ub_entity *uent, u64 pos, u8 *val) +{ return -ENODEV; } +static inline int ub_cfg_read_word(struct ub_entity *uent, u64 pos, u16 *val) +{ return -ENODEV; } +static inline int ub_cfg_read_dword(struct ub_entity *uent, u64 pos, u32 *val) +{ return -ENODEV; } +static inline int ub_cfg_write_byte(struct ub_entity *uent, u64 pos, u8 val) +{ return -ENODEV; } +static inline int ub_cfg_write_word(struct ub_entity *uent, u64 pos, u16 val) +{ return -ENODEV; } +static inline int ub_cfg_write_dword(struct ub_entity *uent, u64 pos, u32 val) +{ return -ENODEV; } static inline int ub_get_bus_controller(struct ub_entity *uents[], unsigned int max_num, unsigned int *real_num) -- Gitee From ae8cfdcf76856384406e5d3c601d08c82e7922d9 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 11:33:52 +0800 Subject: [PATCH 012/214] ub:ubus: Support for UB port sysfs attribute files ANBZ: #45460 commit 210be966e79360fac509e7d66f9adb1712146c14 openEuler. Provide a list of basic sysfs attribute files for UB port, including port network address, neighbor information, link status, etc. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/port.c | 349 +++++++++++++++++++++++++++++++++++++++++ include/ub/ubus/ubus.h | 2 + 2 files changed, 351 insertions(+) diff --git a/drivers/ub/ubus/port.c b/drivers/ub/ubus/port.c index 6b70c229463b..81117aa1f9e9 100644 --- a/drivers/ub/ubus/port.c +++ b/drivers/ub/ubus/port.c @@ -5,6 +5,8 @@ #define pr_fmt(fmt) "ubus port: " fmt +#include + #include "ubus.h" #include "port.h" @@ -17,7 +19,335 @@ struct ub_port_attribute { #define to_ub_port(o) container_of(o, struct ub_port, kobj) #define to_ub_port_attr(a) container_of(a, struct ub_port_attribute, attr) +#define UB_PORT_ATTR_RO(field) \ + static struct ub_port_attribute ub_port_attr_##field = __ATTR_RO(field) + +#define UB_PORT_ATTR_RW(field) \ + static struct ub_port_attribute ub_port_attr_##field = __ATTR_RW(field) + +#define UB_PORT_ATTR_WO(field) \ + static struct ub_port_attribute ub_port_attr_##field = __ATTR_WO(field) + +int ub_port_read_byte(struct ub_port *port, u32 pos, u8 *val) +{ + u64 base = UB_PORT_SLICE_START + port->index * UB_PORT_SLICE_SIZE; + + return ub_cfg_read_byte(port->uent, base + pos, val); +} + +int ub_port_read_word(struct ub_port *port, u32 pos, u16 *val) +{ + u64 base = UB_PORT_SLICE_START + port->index * UB_PORT_SLICE_SIZE; + + return ub_cfg_read_word(port->uent, base + pos, val); +} + +int ub_port_read_dword(struct ub_port *port, u32 pos, u32 *val) +{ + u64 base = UB_PORT_SLICE_START + port->index * UB_PORT_SLICE_SIZE; + + return ub_cfg_read_dword(port->uent, base + pos, val); +} + +int ub_port_write_byte(struct ub_port *port, u32 pos, u8 val) +{ + u64 base = UB_PORT_SLICE_START + port->index * UB_PORT_SLICE_SIZE; + + return ub_cfg_write_byte(port->uent, base + pos, val); +} + +int ub_port_write_word(struct ub_port *port, u32 pos, u16 val) +{ + u64 base = UB_PORT_SLICE_START + port->index * UB_PORT_SLICE_SIZE; + + return ub_cfg_write_word(port->uent, base + pos, val); +} + +int ub_port_write_dword(struct ub_port *port, u32 pos, u32 val) +{ + u64 base = UB_PORT_SLICE_START + port->index * UB_PORT_SLICE_SIZE; + + return ub_cfg_write_dword(port->uent, base + pos, val); +} + +static ssize_t cna_show(struct ub_port *port, char *buf) +{ + return sysfs_emit(buf, "%#06x\n", port->cna); +} +UB_PORT_ATTR_RO(cna); + +static ssize_t boundary_show(struct ub_port *port, char *buf) +{ + return sysfs_emit(buf, "%#01x\n", port->domain_boundary); +} +UB_PORT_ATTR_RO(boundary); + +static ssize_t linkup_show(struct ub_port *port, char *buf) +{ + u8 val; + + if (port->type == VIRTUAL) + return sysfs_emit(buf, "Virtual port don't support\n"); + + if (ub_port_read_byte(port, UB_PORT_PHYSICAL_PORT_LINK_STATUS, &val)) { + ub_err(port->uent, "get port link cap fail\n"); + return -EIO; + } + + return sysfs_emit(buf, "%u\n", val & UB_PORT_LINK_STATE); +} +UB_PORT_ATTR_RO(linkup); + +static ssize_t neighbor_port_idx_show(struct ub_port *port, char *buf) +{ + if (!port->r_uent) + return sysfs_emit(buf, "No Neighbor\n"); + return sysfs_emit(buf, "%u\n", port->r_index); +} +UB_PORT_ATTR_RO(neighbor_port_idx); + +static ssize_t neighbor_guid_show(struct ub_port *port, char *buf) +{ + struct ub_guid *guid; + int count; + + if (!port->r_uent && guid_is_null(&port->r_guid)) + return sysfs_emit(buf, "No Neighbor\n"); + + guid = port->r_uent ? &port->r_uent->guid : + (struct ub_guid *)&port->r_guid; + + count = ub_show_guid(guid, buf); + + return count + sysfs_emit_at(buf, count, "\n"); +} +UB_PORT_ATTR_RO(neighbor_guid); + +static ssize_t neighbor_show(struct ub_port *port, char *buf) +{ + if (!port->r_uent) + return sysfs_emit(buf, "No Neighbor\n"); + + return sysfs_emit(buf, "%05x\n", port->r_uent->uent_num); +} +UB_PORT_ATTR_RO(neighbor); + +static ssize_t asy_link_width_show(struct ub_port *port, char *buf) +{ + u8 val; + + if (ub_port_read_byte(port, PORT_CAP15_QDLWS_CAP, &val)) { + ub_err(port->uent, "get port cap15 cap fail\n"); + return -EIO; + } + + if (val & ASY_LINK_WIDTH_MASK) + return sysfs_emit(buf, "Support\n"); + else + return sysfs_emit(buf, "Not Support\n"); +} +UB_PORT_ATTR_RO(asy_link_width); + +static ssize_t glb_qdlws_show(struct ub_port *port, char *buf) +{ + u8 val; + + if (ub_port_read_byte(port, PORT_CAP15_QDLWS_CTRL, &val)) { + ub_err(port->uent, "get global qdlws fail\n"); + return -EIO; + } + + return sysfs_emit(buf, "%s\n", str_enable_disable(val & GLB_QDLWS_MASK)); +} + +static ssize_t glb_qdlws_store(struct ub_port *port, const char *buf, + size_t count) +{ + unsigned long val; + int ret; + u8 cur; + + ret = kstrtoul(buf, 0, &val); + if (ret || (val != 0 && val != 1)) { + ub_err(port->uent, "Invalid val for global qdlws\n"); + return -EINVAL; + } + + if (ub_port_read_byte(port, PORT_CAP15_QDLWS_CTRL, &cur)) { + ub_err(port->uent, "get global qdlws fail\n"); + return -EIO; + } + + if (val) + cur = cur | GLB_QDLWS_ENABLE_MASK; + else + cur = cur & GLB_QDLWS_DISABLE_MASK; + + if (ub_port_write_byte(port, PORT_CAP15_QDLWS_CTRL, cur)) { + ub_err(port->uent, "set global qdlws fail\n"); + return -EIO; + } + + return count; +} +UB_PORT_ATTR_RW(glb_qdlws); + +static ssize_t tx_qdlws_show(struct ub_port *port, char *buf) +{ + u8 val; + + if (ub_port_read_byte(port, PORT_CAP15_QDLWS_CTRL, &val)) { + ub_err(port->uent, "get TX qdlws fail\n"); + return -EIO; + } + + return sysfs_emit(buf, "%s\n", str_enable_disable(val & TX_QDLWS_MASK)); +} + +static ssize_t tx_qdlws_store(struct ub_port *port, const char *buf, + size_t count) +{ + unsigned long val; + int ret; + u8 cur; + + ret = kstrtoul(buf, 0, &val); + if (ret || (val != 0 && val != 1)) { + ub_err(port->uent, "Invalid val for TX qdlws\n"); + return -EINVAL; + } + + if (ub_port_read_byte(port, PORT_CAP15_QDLWS_CTRL, &cur)) { + ub_err(port->uent, "get value for TX qdlws fail\n"); + return -EIO; + } + + if (val) + cur = cur | TX_QDLWS_ENABLE_MASK; + else + cur = cur & TX_QDLWS_DISABLE_MASK; + + if (ub_port_write_byte(port, PORT_CAP15_QDLWS_CTRL, cur)) { + ub_err(port->uent, "set TX qdlws fail\n"); + return -EIO; + } + + return count; +} +UB_PORT_ATTR_RW(tx_qdlws); + +static ssize_t rx_qdlws_show(struct ub_port *port, char *buf) +{ + u8 val; + + if (ub_port_read_byte(port, PORT_CAP15_QDLWS_CTRL, &val)) { + ub_err(port->uent, "get RX qdlws fail\n"); + return -EIO; + } + + return sysfs_emit(buf, "%s\n", str_enable_disable(val & RX_QDLWS_MASK)); +} + +static ssize_t rx_qdlws_store(struct ub_port *port, const char *buf, + size_t count) +{ + unsigned long val; + int ret; + u8 cur; + + ret = kstrtoul(buf, 0, &val); + if (ret || (val != 0 && val != 1)) { + ub_err(port->uent, "Invalid val for RX qdlws\n"); + return -EINVAL; + } + + if (ub_port_read_byte(port, PORT_CAP15_QDLWS_CTRL, &cur)) { + ub_err(port->uent, "get value for RX qdlws fail\n"); + return -EIO; + } + + if (val) + cur = cur | RX_QDLWS_ENABLE_MASK; + else + cur = cur & RX_QDLWS_DISABLE_MASK; + + if (ub_port_write_byte(port, PORT_CAP15_QDLWS_CTRL, cur)) { + ub_err(port->uent, "set RX qdlws fail\n"); + return -EIO; + } + + return count; +} +UB_PORT_ATTR_RW(rx_qdlws); + +static const char * const status[] = { "IDLE", "NAK", "In progress", "Timeout", + "Successful Done" }; + +static ssize_t qdlws_exec_state_show(struct ub_port *port, char *buf) +{ + u8 val; + + if (ub_port_read_byte(port, PORT_CAP15_QDLWS_STATE, &val)) { + ub_err(port->uent, "get qdlws exec state fail\n"); + return -EIO; + } + + val = val & QDLWS_EXEC_STATUS_MASK; + if (val > QDLWS_EXEC_STATUS_MAX) { + ub_err(port->uent, "get error state, value[%u]\n", val); + return sysfs_emit(buf, "Not support state\n"); + } + + return sysfs_emit(buf, "%s\n", status[val]); +} +UB_PORT_ATTR_RO(qdlws_exec_state); + +static struct attribute *ub_port_default_attrs[] = { + &ub_port_attr_cna.attr, + &ub_port_attr_boundary.attr, + &ub_port_attr_linkup.attr, + /* neighbor info */ + &ub_port_attr_neighbor_port_idx.attr, + &ub_port_attr_neighbor_guid.attr, + &ub_port_attr_neighbor.attr, + NULL +}; + +static const struct attribute_group ub_port_default_group = { + .attrs = ub_port_default_attrs, +}; + +static struct attribute *ub_port_qdlws_attrs[] = { + &ub_port_attr_asy_link_width.attr, + &ub_port_attr_glb_qdlws.attr, + &ub_port_attr_tx_qdlws.attr, + &ub_port_attr_rx_qdlws.attr, + &ub_port_attr_qdlws_exec_state.attr, + NULL +}; + +static umode_t ub_port_qdlws_is_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct ub_port *port = to_ub_port(kobj); + + if (port->type == VIRTUAL) + return 0; + + if (test_bit(UB_PORT_CAP15_QDLWS, port->cap_map)) + return a->mode; + + return 0; +} + +static const struct attribute_group ub_port_qdlws_group = { + .is_visible = ub_port_qdlws_is_visible, + .attrs = ub_port_qdlws_attrs, +}; + static const struct attribute_group *ub_port_groups[] = { + &ub_port_default_group, + &ub_port_qdlws_group, NULL }; @@ -110,6 +440,25 @@ bool ub_check_and_connect(struct ub_port *port, struct ub_entity *r_uent) static int ub_port_config(struct ub_port *port) { + u32 map[UB_PORT_CAP_NUM / SZ_32]; + int ret, i; + + /* virtual port doesn't have bitmap */ + if (port->type == VIRTUAL) + return 0; + + for (i = 0; i < UB_PORT_CAP_NUM / SZ_32; i++) { + ret = ub_port_read_dword( + port, UB_CFG0_PORT_BITMAP + i * sizeof(u32), &map[i]); + if (ret) { + ub_err(port->uent, + "failed to read port%u bitmap %d with %d\n", + port->index, i, ret); + return ret; + } + } + + memcpy((void *)port->cap_map, (void *)map, sizeof(map)); return 0; } diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index b16ece64a22e..d268a9d96f69 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -75,6 +75,7 @@ struct ub_port { struct ub_entity *uent; u16 index; enum ub_port_type type; + u8 domain_boundary; u32 cna; struct ub_entity *r_uent; /* If valid, also represent link up */ u16 r_index; @@ -100,6 +101,7 @@ struct ub_entity { unsigned int cna; unsigned int eid; unsigned short entity_idx; + u32 uent_num; /* ub dev number */ /* entity topology info */ struct ub_bus_controller *ubc; -- Gitee From 3979d8516a1d602d111087a429a2a096bde920ed Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 14:07:13 +0800 Subject: [PATCH 013/214] ub:ubus: Supporting the UB Shared Port Function ANBZ: #45460 commit 420be62f8a7ed365d011b97c5a4866553c3bfc13 openEuler. The UB Bus Controller and IDEV support shared ports, reusing port interfaces for external communication. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/port.c | 135 +++++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/port.h | 6 ++ drivers/ub/ubus/ubus.h | 23 +++++++ include/ub/ubus/ubus.h | 51 ++++++++++++++++ 4 files changed, 215 insertions(+) diff --git a/drivers/ub/ubus/port.c b/drivers/ub/ubus/port.c index 81117aa1f9e9..8aa7a7053cc0 100644 --- a/drivers/ub/ubus/port.c +++ b/drivers/ub/ubus/port.c @@ -546,3 +546,138 @@ void ub_ports_unset(struct ub_entity *uent) uent->ports = NULL; pr_debug("port release\n"); } + +static LIST_HEAD(ub_share_port_notify_list); +static DECLARE_RWSEM(ub_share_port_notify_list_rwsem); + +struct ub_share_port_notify_node { + struct ub_entity *parent; + struct ub_entity *idev; + u16 port_id; + struct ub_share_port_ops *ops; + struct list_head node; +}; + +int ub_register_share_port(struct ub_entity *idev, u16 port_id, + struct ub_share_port_ops *ops) +{ + struct ub_share_port_notify_node *notify_node; + struct ub_entity *parent; + struct ub_port *port; + + if (unlikely(!idev || !ops)) + return -EINVAL; + + if (!is_idev(idev)) { + ub_err(idev, "don't support non-idev device with type %u register share port\n", + uent_type(idev)); + return -EINVAL; + } + + /* get primary entity first */ + parent = idev; + while (!is_primary(parent)) + parent = parent->pue; + + /* check parent is controller */ + parent = to_ub_entity(parent->dev.parent); + if (!is_ibus_controller(parent)) { + ub_err(idev, "don't support register share port at non-controller device with type %u\n", + uent_type(parent)); + return -EINVAL; + } + + if (port_id >= parent->port_nums) { + ub_err(parent, "port id %u exceeds port num %u\n", port_id, + parent->port_nums); + return -EINVAL; + } + + port = parent->ports + port_id; + if (!port->shareable) { + ub_err(parent, "port%u isn't shareable\n", port_id); + return -EINVAL; + } + + notify_node = kzalloc(sizeof(*notify_node), GFP_KERNEL); + if (!notify_node) + return -ENOMEM; + + notify_node->parent = parent; + notify_node->idev = idev; + notify_node->port_id = port_id; + notify_node->ops = ops; + INIT_LIST_HEAD(¬ify_node->node); + + down_write(&ub_share_port_notify_list_rwsem); + list_add_tail(¬ify_node->node, &ub_share_port_notify_list); + up_write(&ub_share_port_notify_list_rwsem); + + ub_info(idev, "register share port at %u success\n", port_id); + return 0; +} +EXPORT_SYMBOL_GPL(ub_register_share_port); + +void ub_unregister_share_port(struct ub_entity *idev, u16 port_id, + struct ub_share_port_ops *ops) +{ + struct ub_share_port_notify_node *notify_node; + + if (unlikely(!idev)) + return; + + down_write(&ub_share_port_notify_list_rwsem); + + list_for_each_entry(notify_node, &ub_share_port_notify_list, node) { + if (notify_node->idev != idev || + notify_node->port_id != port_id || notify_node->ops != ops) + continue; + + list_del(¬ify_node->node); + kfree(notify_node); + ub_info(idev, "unregister share port at %u success\n", port_id); + goto unlock; + } + + ub_err(idev, "share port %u isn't registered, unregister failed\n", + port_id); +unlock: + up_write(&ub_share_port_notify_list_rwsem); +} +EXPORT_SYMBOL_GPL(ub_unregister_share_port); + +void ub_notify_share_port(struct ub_port *port, + enum ub_share_port_notify_type type) +{ + struct ub_share_port_notify_node *notify_node; + struct ub_share_port_ops *ops; + struct ub_entity *uent; + + if (!port || type >= NOTIFY_TYPE_MAX) + return; + + uent = port->uent; + down_read(&ub_share_port_notify_list_rwsem); + list_for_each_entry(notify_node, &ub_share_port_notify_list, node) { + if (notify_node->parent != uent || + notify_node->port_id != port->index) + continue; + + ops = notify_node->ops; + switch (type) { + case RESET_PREPARE: + if (ops->reset_prepare) + ops->reset_prepare(notify_node->idev, + notify_node->port_id); + break; + case RESET_DONE: + if (ops->reset_done) + ops->reset_done(notify_node->idev, + notify_node->port_id); + break; + default: + break; + } + } + up_read(&ub_share_port_notify_list_rwsem); +} diff --git a/drivers/ub/ubus/port.h b/drivers/ub/ubus/port.h index 8f8c1d78c2be..b8757fad7060 100644 --- a/drivers/ub/ubus/port.h +++ b/drivers/ub/ubus/port.h @@ -8,6 +8,12 @@ #define for_each_uent_port(p, d) \ for ((p) = (d)->ports; ((p) - (d)->ports) < (d)->port_nums; (p)++) +enum ub_share_port_notify_type { + RESET_PREPARE, + RESET_DONE, + NOTIFY_TYPE_MAX +}; + struct ub_port; struct ub_entity; void ub_port_disconnect(struct ub_port *port); diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index 51828b3c9806..cd2c2a91fa52 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -10,6 +10,29 @@ #define UB_CP_UPI 0x7FFF +enum ub_entity_type { + UB_ENT_BUS_CONTROLLER, + UB_ENT_IBUS_CONTROLLER, + UB_ENT_SWITCH, + UB_ENT_DEVICE, + UB_ENT_IDEVICE, + UB_ENT_P_DEVICE, + UB_ENT_P_IDEVICE, + UB_ENT_UNKNOWN +}; + +#define is_primary(uent) ((uent)->entity_idx == 0) +#define is_ibus_controller(uent) ((uent)->ent_type == UB_ENT_IBUS_CONTROLLER) +#define is_bus_controller(uent) ((uent)->ent_type == UB_ENT_BUS_CONTROLLER) +#define is_controller(uent) (is_ibus_controller(uent) || is_bus_controller(uent)) +#define is_switch(uent) ((uent)->ent_type == UB_ENT_SWITCH) +#define is_device(uent) ((uent)->ent_type == UB_ENT_DEVICE) +#define is_p_device(uent) ((uent)->ent_type == UB_ENT_P_DEVICE) +#define is_dev(uent) (is_device(uent) || is_p_device(uent)) +#define is_idevice(uent) ((uent)->ent_type == UB_ENT_IDEVICE) +#define is_p_idevice(uent) ((uent)->ent_type == UB_ENT_P_IDEVICE) +#define is_idev(uent) (is_idevice(uent) || is_p_idevice(uent)) + int ub_host_probe(void); void ub_host_remove(void); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index d268a9d96f69..137c1d0ef289 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -76,6 +76,7 @@ struct ub_port { u16 index; enum ub_port_type type; u8 domain_boundary; + bool shareable; u32 cna; struct ub_entity *r_uent; /* If valid, also represent link up */ u16 r_index; @@ -94,6 +95,7 @@ struct ub_entity { const char *driver_override; /* Driver name to force a match */ /* entity base info */ + int ent_type; struct ub_guid guid; u16 class_code; u16 mod_vendor; /* entity's module vendor and module id */ @@ -105,6 +107,7 @@ struct ub_entity { /* entity topology info */ struct ub_bus_controller *ubc; + struct ub_entity *pue; /* ue/mue connected to their mue */ /* entity port info */ u16 port_nums; @@ -229,6 +232,20 @@ static inline const char *ub_name(const struct ub_entity *pue) return dev_name(&pue->dev); } +/* interfaces for shareable port */ +enum ub_port_event { + UB_PORT_EVENT_LINK_DOWN, + UB_PORT_EVENT_LINK_UP, + UB_PORT_EVENT_RESET_PREPARE, + UB_PORT_EVENT_RESET_DONE +}; + +struct ub_share_port_ops { + void (*reset_prepare)(struct ub_entity *uent, u16 port_id); + void (*reset_done)(struct ub_entity *uent, u16 port_id); + void (*event_notify)(struct ub_entity *uent, u16 port_id, int event); +}; + #ifdef CONFIG_UB_UBUS extern struct bus_type ub_bus_type; #define dev_is_ub(d) ((d)->bus == &ub_bus_type) @@ -236,6 +253,35 @@ extern struct bus_type ub_bus_type; void ub_iommu_fwnode_handle_set(const struct fwnode_handle *fwnode); const struct fwnode_handle *ub_iommu_fwnode_handle_get(void); +/** + * ub_register_share_port() - Register a share port. + * @uent: UB entity. + * @port_id: UB Bus Controller port id. + * @ops: UB share port ops. + * + * The IDEV reuses the physical port of the ub bus controller. Record the + * callback function. + * + * Context: Any context + * Return: 0 if success, or %-ENOMEM if system out of memory, + * or %-EINVAL if parameters invalid. + */ +int ub_register_share_port(struct ub_entity *uent, u16 port_id, + struct ub_share_port_ops *ops); + +/** + * ub_unregister_share_port() - Unregister a share port. + * @uent: UB entity. + * @port_id: UB Bus Controller port id. + * @ops: UB share port ops. + * + * Clear the callback function. + * + * Context: Any context + */ +void ub_unregister_share_port(struct ub_entity *uent, u16 port_id, + struct ub_share_port_ops *ops); + /** * ub_cfg_read_byte() - 1 byte configuration access read. * @uent: UB entity. @@ -346,6 +392,11 @@ ub_put_bus_controller(struct ub_entity *uents[], unsigned int num) {} static inline void ub_iommu_fwnode_handle_set(const struct fwnode_handle *fwnode) {} static inline const struct fwnode_handle *ub_iommu_fwnode_handle_get(void) { return NULL; } +static inline int ub_register_share_port(struct ub_entity *uent, u16 port_id, + struct ub_share_port_ops *ops) +{ return -ENODEV; } +static inline void ub_unregister_share_port(struct ub_entity *uent, u16 port_id, + struct ub_share_port_ops *ops) {} static inline int __ub_register_driver(struct ub_driver *drv, struct module *owner, const char *mod_name) -- Gitee From f723be6b0576d56e518a47eb0ae0fdb440097d59 Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Fri, 19 Sep 2025 15:13:36 +0800 Subject: [PATCH 014/214] ub:ubus: Support cc configuration and query ANBZ: #45460 commit b1ce3f9db62e91531f60d3221e44e813207c88fe openEuler. Provide UB device congestion control switch configuration function. Provide the capability to query UB device congestion control functions. Signed-off-by: Yuhao Xiang Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/cc.c | 85 ++++++++++++++++++++++++++++++++++++++++ include/ub/ubus/ubus.h | 46 ++++++++++++++++++++++ 3 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/cc.c diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 90ab6925210e..aef65a34cbea 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -2,6 +2,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o -ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o +ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/cc.c b/drivers/ub/ubus/cc.c new file mode 100644 index 000000000000..5f20d047c2e4 --- /dev/null +++ b/drivers/ub/ubus/cc.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus cc: " fmt + +#include "ubus.h" + +static int ub_cc_safe_check(struct ub_entity *uent) +{ + if (!uent) + return -EINVAL; + + if (is_idev(uent)) { + if (!uent->ubc || !uent->ubc->uent) + return -EINVAL; + } + + return 0; +} + +bool ub_cc_supported(struct ub_entity *uent) +{ + if (ub_cc_safe_check(uent)) + return false; + + if (is_idev(uent)) { + /* Now, idev cc enable depends ub bus controller cc enable */ + if ((uent->ubc->uent->support_feature & UB_CC_SUPPORT) && + (uent->support_feature & UB_CC_SUPPORT)) + return true; + } else { + if (uent->support_feature & UB_CC_SUPPORT) + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(ub_cc_supported); + +/** + * ub_cc_set - Set congestion control for given ub entity + * @uent: ub entity + * @val: cc switch + * + * Returns: 0 on success, an error otherwise. + */ +static int ub_cc_set(struct ub_entity *uent, u8 val) +{ + if (!ub_cc_supported(uent)) + return -EPERM; + + return ub_cfg_write_byte(uent, UB_CC_EN, val); +} + +/** + * ub_cc_enable - Enable congestion control for given ub entity + * @uent: ub entity + * + * Returns: 0 on success, an error otherwise. + */ +int ub_cc_enable(struct ub_entity *uent) +{ + if (ub_cc_safe_check(uent)) + return -EINVAL; + + return ub_cc_set(uent, 1); +} +EXPORT_SYMBOL_GPL(ub_cc_enable); + +/** + * ub_cc_disable - Disable congestion control for given ub entity + * @uent: ub entity + * + * Returns: 0 on success, an error otherwise. + */ +int ub_cc_disable(struct ub_entity *uent) +{ + if (ub_cc_safe_check(uent)) + return -EINVAL; + + return ub_cc_set(uent, 0); +} +EXPORT_SYMBOL_GPL(ub_cc_disable); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 137c1d0ef289..4d9dead33810 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -55,6 +55,14 @@ struct ub_guid { #define uent_base_code(uent) ((uent)->class_code & UB_BASE_CODE_MASK) #define uent_seq(uent) ((uent)->guid.bits.seq_num) +enum ub_type { + UB_TYPE_BUS_INSTANCE = 0, + UB_TYPE_CONTROLLER = 1, + UB_TYPE_ICONTROLLER = 2, + UB_TYPE_SWITCH = 3, + UB_TYPE_ISWITCH = 4 +}; + static inline int ub_show_guid(struct ub_guid *guid, char *buf) { return sprintf(buf, "%04x-%04x-%01x-%01x-%06x-%016llx", @@ -114,6 +122,7 @@ struct ub_entity { struct ub_port *ports; struct dev_message *message; + u32 support_feature; u16 upi; }; @@ -358,6 +367,37 @@ int ub_get_bus_controller(struct ub_entity *uents[], unsigned int max_num, */ void ub_put_bus_controller(struct ub_entity *uents[], unsigned int num); +/** + * ub_cc_supported() - Congestion control capability query. + * @uent: UB entity. + * + * Context: Any context. + * Return: %true if the entity supports congestion control, %false otherwise. + */ +bool ub_cc_supported(struct ub_entity *uent); + +/** + * ub_cc_enable() - Enable the congestion control capability of the entity. + * @uent: UB entity. + * + * Context: Any context. + * Return: 0 if success, or %-EINVAL if input parameters are invalid, or + * %-EPERM if the entity does not support congestion control, or other + * failed negative values. + */ +int ub_cc_enable(struct ub_entity *uent); + +/** + * ub_cc_disable() - Disable the congestion control capability of the entity. + * @uent: UB entity. + * + * Context: Any context. + * Return: 0 if success, or %-EINVAL if input parameters are invalid, or + * %-EPERM if the entity does not support congestion control, or other + * failed negative values. + */ +int ub_cc_disable(struct ub_entity *uent); + /* Proper probing supporting hot-pluggable entities */ int __ub_register_driver(struct ub_driver *drv, struct module *owner, const char *mod_name); @@ -371,6 +411,12 @@ void ub_unregister_driver(struct ub_driver *drv); static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) { return NULL; } static inline void ub_entity_put(struct ub_entity *uent) {} +static inline bool ub_cc_supported(struct ub_entity *uent) +{ return false; } +static inline int ub_cc_enable(struct ub_entity *uent) +{ return -ENODEV; } +static inline int ub_cc_disable(struct ub_entity *uent) +{ return -ENODEV; } static inline int ub_cfg_read_byte(struct ub_entity *uent, u64 pos, u8 *val) { return -ENODEV; } static inline int ub_cfg_read_word(struct ub_entity *uent, u64 pos, u16 *val) -- Gitee From 461a32a31fe34beaeb83c57e2e7a376a59cd49e6 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 19 Sep 2025 15:06:42 +0800 Subject: [PATCH 015/214] ub:ubus: Add EID allocate and free interfaces by kernel ANBZ: #45460 commit 9c2051eafee2cffee27fad736d38091e31eaadbe openEuler. Add Entity index allocate and free interfaces by kernel. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/eid.c | 88 ++++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/eid.h | 16 ++++++++ drivers/ub/ubus/ubus.h | 2 + 4 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/eid.c create mode 100644 drivers/ub/ubus/eid.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index aef65a34cbea..ecd38e538b13 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -2,6 +2,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o -ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o +ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/eid.c b/drivers/ub/ubus/eid.c new file mode 100644 index 000000000000..74eeeeb9845b --- /dev/null +++ b/drivers/ub/ubus/eid.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus eid: " fmt + +#include "ubus.h" +#include "eid.h" + +static DEFINE_IDA(ub_eid_ida); + +int ub_eid_request(guid_t *id, u32 *eid) +{ + int ida; + + if (!id || !eid) + return -EINVAL; + + ida = ida_alloc_range(&ub_eid_ida, ubc_eid_start, ubc_eid_end, GFP_KERNEL); + if (ida < 0) + return ida; + + *eid = (u32)ida; + + return 0; +} + +void ub_eid_release(u32 eid) +{ + ida_free(&ub_eid_ida, eid); +} + +int ub_eid_alloc(struct ub_entity *uent) +{ + struct device *dev; + u32 eid = 0; + int ret; + + if (is_p_device(uent)) + return 0; + + if (uent->eid) { + ub_warn(uent, "uent eid not 0, eid=%#05x\n", uent->eid); + return -EPERM; + } + + if (is_ibus_controller(uent) && uent->ubc->cluster) { + dev = &uent->ubc->dev; + ret = ub_cfg_read_dword(uent, UB_EID_0, &eid); + if (ret) { + dev_err(dev, "query cluster ubc, ret=%d\n", ret); + return ret; + } + + eid &= UB_COMPACT_EID_MASK; + if (eid) + dev_info(dev, "update cluster ubc eid, eid=%#x\n", eid); + + uent->eid = eid; + return 0; + } + + ret = ub_eid_request(&uent->guid.id, &eid); + if (!ret) + uent->eid = eid; + + return ret; +} + +void ub_eid_free(struct ub_entity *uent) +{ + u32 eid = uent->eid; + + if (is_p_device(uent)) + return; + + if (!eid) { + ub_warn(uent, "eid free 0\n"); + return; + } + + if (is_ibus_controller(uent) && uent->ubc->cluster) + return; + + uent->eid = 0; + ub_eid_release(eid); +} diff --git a/drivers/ub/ubus/eid.h b/drivers/ub/ubus/eid.h new file mode 100644 index 000000000000..9d71ad5cf228 --- /dev/null +++ b/drivers/ub/ubus/eid.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __EID_H__ +#define __EID_H__ + +struct ub_entity; + +int ub_eid_request(guid_t *id, u32 *eid); +void ub_eid_release(u32 eid); +int ub_eid_alloc(struct ub_entity *uent); +void ub_eid_free(struct ub_entity *uent); + +#endif /* __EID_H__ */ diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index cd2c2a91fa52..33275db12ff2 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -33,6 +33,8 @@ enum ub_entity_type { #define is_p_idevice(uent) ((uent)->ent_type == UB_ENT_P_IDEVICE) #define is_idev(uent) (is_idevice(uent) || is_p_idevice(uent)) +#define UB_COMPACT_EID_MASK GENMASK(19, 0) + int ub_host_probe(void); void ub_host_remove(void); -- Gitee From 904420cd408b9e7bc431d5dfa8db9f922008ef94 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 19 Sep 2025 15:22:05 +0800 Subject: [PATCH 016/214] ub:ubus: Add CNA allocate and free interfaces by kernel ANBZ: #45460 commit b5e4bceddf35ee85cc831ed68ed1def7f662250e openEuler. Add Clan network address and free interfaces by kernel. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/cna.c | 86 ++++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/cna.h | 13 ++++++ drivers/ub/ubus/ubus.h | 2 + 4 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/cna.c create mode 100644 drivers/ub/ubus/cna.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index ecd38e538b13..caef4d64df35 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -2,6 +2,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o -ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o +ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/cna.c b/drivers/ub/ubus/cna.c new file mode 100644 index 000000000000..e435259b1411 --- /dev/null +++ b/drivers/ub/ubus/cna.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus cna: " fmt + +#include "ubus.h" +#include "port.h" +#include "cna.h" + +static DEFINE_IDA(ub_cna_ida); + +static int ub_alloc_cna_id(void) +{ + return ida_alloc_range(&ub_cna_ida, ubc_cna_start, ubc_cna_end, GFP_KERNEL); +} + +static void ub_free_cna_id(u32 cna) +{ + ida_free(&ub_cna_ida, cna); +} + +void ub_cna_free(struct ub_entity *uent) +{ + struct ub_port *port; + + if (!is_primary(uent)) { + ub_warn(uent, "only entity0 need to free cna\n"); + return; + } + + for_each_uent_port(port, uent) { + if (port->domain_boundary && is_ibus_controller(uent)) + continue; + + if (port->cna) { + if (!ONE_CNA(uent)) + ub_free_cna_id(port->cna); + port->cna = 0; + } + } + + if (is_ibus_controller(uent) && uent->ubc->cluster) + return; + + if (uent->cna) { + ub_free_cna_id(uent->cna); + uent->cna = 0; + } +} + +int ub_cna_alloc(struct ub_entity *uent) +{ + bool one_cna = ONE_CNA(uent); + struct ub_port *port; + int ret; + + if (!is_primary(uent)) { + ub_warn(uent, "only entity0 need to alloc cna\n"); + return 0; + } + + if (!(is_ibus_controller(uent) && uent->ubc->cluster)) { + ret = ub_alloc_cna_id(); + if (ret < 0) + return ret; + + uent->cna = (u32)ret; + } + + for_each_uent_port(port, uent) { + if (is_ibus_controller(uent) && port->domain_boundary) + continue; + if (one_cna) { + port->cna = uent->cna; + } else { + ret = ub_alloc_cna_id(); + if (ret < 0) + return ret; + port->cna = (u32)ret; + } + } + + return 0; +} diff --git a/drivers/ub/ubus/cna.h b/drivers/ub/ubus/cna.h new file mode 100644 index 000000000000..68d3d9a97fcc --- /dev/null +++ b/drivers/ub/ubus/cna.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __CNA_H__ +#define __CNA_H__ + +struct ub_entity; +int ub_cna_alloc(struct ub_entity *uent); +void ub_cna_free(struct ub_entity *uent); + +#endif /* __CNA_H__ */ diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index 33275db12ff2..e46611a1bdf0 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -33,6 +33,8 @@ enum ub_entity_type { #define is_p_idevice(uent) ((uent)->ent_type == UB_ENT_P_IDEVICE) #define is_idev(uent) (is_idevice(uent) || is_p_idevice(uent)) +#define ONE_CNA(uent) (is_switch(uent) || (uent)->port_nums == 1) + #define UB_COMPACT_EID_MASK GENMASK(19, 0) int ub_host_probe(void); -- Gitee From fca6c13e84ebe777b35ad6fbcda09af8f57889d6 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 14:45:05 +0800 Subject: [PATCH 017/214] ub:ubus: Support for UB routing table configuration function ANBZ: #45460 commit 9990ed08ad3b5049b5eb594c2e5ea53cb2cc6ffa openEuler. Support for device CNA routing node management and routing table configuration. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/route.c | 177 +++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/route.h | 15 ++++ drivers/ub/ubus/ubus.h | 13 +++ include/ub/ubus/ubus.h | 4 + 5 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/route.c create mode 100644 drivers/ub/ubus/route.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index caef4d64df35..9b0575e123be 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -2,6 +2,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o -ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o +ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/route.c b/drivers/ub/ubus/route.c new file mode 100644 index 000000000000..f4be537021d5 --- /dev/null +++ b/drivers/ub/ubus/route.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus route: " fmt + +#include "ubus.h" +#include "port.h" + +#define UB_ROUTE_TABLE_ENTRY_START (UB_ROUTE_TABLE_SLICE_START + (0x10 << 2)) +#define EBW(port_nums) ((((port_nums) - 1) >> 5) + 1) /* Entry Bit Width */ +#define UB_ROUTE_TABLE_ENTRY_BITS SZ_128 + +/* node to update routing table */ +struct cna_node { + u32 cna; + u16 distance; /* distance of path */ + u16 count; /* number of ports that have route to this cna */ + bool need_update; /* need to write to routing table */ + + struct kref ref; + struct list_head node; +}; + +static struct cna_node *ub_create_cna_node(u32 cna, short distance) +{ + struct cna_node *new_cna; + + new_cna = kzalloc(sizeof(*new_cna), GFP_KERNEL); + if (!new_cna) + return NULL; + + new_cna->cna = cna; + new_cna->need_update = true; + new_cna->distance = distance; + new_cna->count = 1; + kref_init(&new_cna->ref); + INIT_LIST_HEAD(&new_cna->node); + + return new_cna; +} + +static void ub_cna_node_release(struct kref *kref) +{ + struct cna_node *cna = container_of(kref, struct cna_node, ref); + + kfree(cna); +} + +static void ub_cna_node_put(struct cna_node *cna) +{ + kref_put(&cna->ref, ub_cna_node_release); +} + +static int ub_add_cna_node(u32 cna, short distance, struct list_head *cna_list) +{ + struct list_head *prev_head = cna_list; + struct cna_node *new_cna, *prev_cna; + + /** + * keep the cna list in ascending order to get the smallest cna quickly + * traverse from tail to end, insert new node when prev is smaller + */ + list_for_each_entry_reverse(prev_cna, cna_list, node) { + if (prev_cna->cna > cna) + continue; + + if (prev_cna->cna == cna) { + prev_cna->count++; + return 0; + } + + prev_head = &prev_cna->node; + break; + } + + new_cna = ub_create_cna_node(cna, distance); + if (!new_cna) + return -ENOMEM; + + list_add(&new_cna->node, prev_head); + return 0; +} + +static void ub_clear_cna_list(struct list_head *cna_list) +{ + struct cna_node *cna, *tmp; + + list_for_each_entry_safe(cna, tmp, cna_list, node) { + list_del(&cna->node); + ub_cna_node_put(cna); + } +} + +void ub_route_clear(struct ub_entity *uent) +{ + struct ub_port *port; + + if (!uent) + return; + + for_each_uent_port(port, uent) + bitmap_clear(port->cna_maps, 0, UB_ROUTE_TABLE_ENTRY_BITS); + + ub_clear_cna_list(&uent->cna_list); +} + +int ub_route_add_entry(struct ub_port *port, u32 cna, short distance) +{ + if (!port) + return -EINVAL; + + if (test_bit(cna, port->cna_maps)) + return -EEXIST; + + set_bit(cna, port->cna_maps); + return ub_add_cna_node(cna, distance, &port->uent->cna_list); +} + +static void ub_set_route_table_entry(struct ub_entity *uent, u32 dst_cna, + u32 *route_table_entry) +{ + int i; + u32 offset; + + /* Routing Table Block is not required for single-port devices. */ + if (uent->port_nums == 1) + return; + + pr_info("cna %#x uent set dstcna %#x route\n", uent->cna, dst_cna); + + for (i = 0; i < EBW(uent->port_nums); i++) { + offset = ((dst_cna + 1) * EBW(uent->port_nums) + i) << SZ_2; + ub_cfg_write_dword(uent, UB_ROUTE_TABLE_ENTRY_START + offset, + route_table_entry[i]); + } +} + +/** + * after updating routing table in software, this function must be called + * to sync the software routing table to config space + */ +void ub_route_sync_dev(struct ub_entity *uent) +{ + DECLARE_BITMAP(route_table_entry, UB_ROUTE_TABLE_ENTRY_BITS); + struct cna_node *cna_node, *tmp; + struct ub_port *port; + u32 cna; + + if (!uent || !ub_entity_test_priv_flag(uent, UB_ENTITY_ROUTE_UPDATED)) + return; + + list_for_each_entry_safe(cna_node, tmp, &uent->cna_list, node) { + if (!cna_node->need_update) + continue; + + cna = cna_node->cna; + bitmap_zero(route_table_entry, UB_ROUTE_TABLE_ENTRY_BITS); + + if (cna_node->count == 0) { + list_del(&cna_node->node); + kfree(cna_node); + goto set_entry; + } + + for_each_uent_port(port, uent) + if (test_bit(cna, port->cna_maps)) + set_bit(port->index, route_table_entry); + + cna_node->need_update = false; +set_entry: + ub_set_route_table_entry(uent, cna, (u32 *)route_table_entry); + } + + ub_entity_assign_priv_flag(uent, UB_ENTITY_ROUTE_UPDATED, false); +} diff --git a/drivers/ub/ubus/route.h b/drivers/ub/ubus/route.h new file mode 100644 index 000000000000..04bd052729a0 --- /dev/null +++ b/drivers/ub/ubus/route.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __ROUTE_H__ +#define __ROUTE_H__ + +struct ub_entity; +struct ub_port; +void ub_route_clear(struct ub_entity *uent); +int ub_route_add_entry(struct ub_port *port, u32 cna, short distance); +void ub_route_sync_dev(struct ub_entity *uent); + +#endif /* __ROUTE_H__ */ diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index e46611a1bdf0..3f0812e77212 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -37,6 +37,19 @@ enum ub_entity_type { #define UB_COMPACT_EID_MASK GENMASK(19, 0) +/* ub_entity priv_flags */ +#define UB_ENTITY_ROUTE_UPDATED 2 /* Flag indicate uent's route is updated */ +static inline void ub_entity_assign_priv_flag(struct ub_entity *uent, int bit, + bool flag) +{ + assign_bit(bit, &uent->priv_flags, flag); +} + +static inline bool ub_entity_test_priv_flag(struct ub_entity *uent, int bit) +{ + return test_bit(bit, &uent->priv_flags); +} + int ub_host_probe(void); void ub_host_remove(void); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 4d9dead33810..7fd6bccab0f2 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -101,6 +101,7 @@ struct ub_entity { struct ub_driver *driver; bool match_driver; /* Skip attaching driver before dev ready */ const char *driver_override; /* Driver name to force a match */ + unsigned long priv_flags; /* Private flags for the UB driver */ /* entity base info */ int ent_type; @@ -121,6 +122,9 @@ struct ub_entity { u16 port_nums; struct ub_port *ports; + /* entity route info */ + struct list_head cna_list; /* store distance for cna in route table */ + struct dev_message *message; u32 support_feature; -- Gitee From c34ada51799bb543a7c2f6c022eff4b440212a2e Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 15:37:23 +0800 Subject: [PATCH 018/214] ub:ubus: Support for UB bus enumeration message initialization ANBZ: #45460 commit 58e026216fffae4f8cf7b78c9d2e4c6468aa20a1 openEuler. Implement the UB protocol device enumeration message definition and initialize the corresponding message header section. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 1 + drivers/ub/ubus/enum.c | 197 +++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/enum.h | 186 ++++++++++++++++++++++++++++++++++++ include/ub/ubus/ubus.h | 1 + 4 files changed, 385 insertions(+) create mode 100644 drivers/ub/ubus/enum.c create mode 100644 drivers/ub/ubus/enum.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 9b0575e123be..fada110e4bf1 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -3,5 +3,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o +ubus-y += enum.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/enum.c b/drivers/ub/ubus/enum.c new file mode 100644 index 000000000000..b298402e2352 --- /dev/null +++ b/drivers/ub/ubus/enum.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus enum: " fmt + +#include "ubus.h" +#include "msg.h" +#include "port.h" +#include "enum.h" + +#define ENUM_MAX_HOPS 255 + +enum enum_topo_query_opcode { + ENUM_TOPO_QUERY_RESPONSE = 0, + ENUM_TOPO_QUERY_REQUEST +}; + +enum enum_na_cfg_opcode { + ENUM_NA_CFG_RESPONSE = 0, + ENUM_NA_CFG_PRIMARY = 2, + ENUM_NA_CFG_PORT = 3 +}; + +enum enum_hop_type { + ENUM_HOP_TYPE_4BIT = 0, + ENUM_HOP_TYPE_BYTE, + ENUM_HOP_TYPE_WORD +}; + +enum enum_na_query_opcode { + ENUM_NA_QUERY_RESPONSE = 0, + ENUM_NA_QUERY_SWITCH, + ENUM_NA_QUERY_DEVICE, + ENUM_NA_QUERY_PORT, +}; + +struct enum_na_cfg_req { + /* DW0~DW5 */ + struct enum_pld_scan_pdu_common common; + /* DW6 */ + u16 rsvd; + u16 port_idx; + /* DW7 */ + u32 cna : 24; + u32 rsvd1 : 8; +}; +#define ENUM_NA_CFG_REQ_SIZE 32 + +struct enum_na_cfg_rsp { + /* DW0~DW5 */ + struct enum_pld_scan_pdu_common common; +}; +#define ENUM_NA_CFG_RSP_SIZE 24 + +struct enum_na_query_req { + /* DW0~DW5 */ + struct enum_pld_scan_pdu_common common; + /* DW6 */ + u32 port_idx : 16; + u32 rsv : 16; +}; +#define ENUM_NA_QUERY_REQ_SIZE 28 + +struct enum_na_query_rsp { + /* DW0~DW5 */ + struct enum_pld_scan_pdu_common common; + /* DW6 */ + u32 cna : 24; + u32 rsvd : 8; +}; +#define ENUM_NA_QUERY_RSP_SIZE 28 + +static void ub_enum_pkt_header_init(void *buf) +{ + struct enum_pkt_header *header = (struct enum_pkt_header *)buf; + struct compact_network_header *cnth = &header->cnth; + struct ub_link_header *ulh = &header->ulh; + + ulh->cfg = UB_COMPACT_LINK_CFG; + cnth->nth_nlp = NTH_NLP_WITHOUT_TPH; + cnth->mgmt = 1; + header->upi = UB_CP_UPI; +} + +/* Attention: The caller must ensure that hop_type is valid */ +static size_t calc_forward_path_size(struct enum_pld_scan_header *header) +{ +#define FOUR_BITS_PER_DWORD 8 + u8 hop_bits[] = { SZ_4, SZ_8, SZ_16 }; + + /* Path size is hops * hop_bits[], then align it to 4byte */ + return ALIGN(hop_bits[header->bits.hop_type] * header->bits.hops / + hop_bits[0], FOUR_BITS_PER_DWORD) / SZ_2; +} + +static void set_hop_path(u8 *path, int type, u32 index, u32 val) +{ + u32 offset, mask; + u16 *p; + + switch (type) { + case ENUM_HOP_TYPE_4BIT: + offset = index / SZ_2; + mask = 0x0F << ((index % SZ_2) * SZ_4); + val = val << ((index % SZ_2) * SZ_4); + break; + case ENUM_HOP_TYPE_BYTE: + offset = index; + mask = GENMASK(7, 0); + break; + case ENUM_HOP_TYPE_WORD: + offset = index * SZ_2; + mask = GENMASK(15, 0); + break; + default: + return; + } + + p = (u16 *)(path + offset); + *p = (*p & ~mask) | (val & mask); +} + +static void ub_enum_pld_header_init(struct ub_entity *uent, void *buf) +{ + struct enum_pld_scan_header *header; + struct ub_entity *parent, *target; + struct ub_port *port; + u8 *path, *r_path; + size_t size; + int i, j; + + header = (struct enum_pld_scan_header *)(buf + ENUM_PKT_HEADER_SIZE); + header->bits.hop_type = ENUM_HOP_TYPE_BYTE; + header->bits.hops = (u32)uent->topo_rank; + header->bits.step = 0; + + if (header->bits.hops == 0) /* First hop doesn't need path */ + return; + + header->bits.r = 1; /* Use return path */ + + size = calc_forward_path_size(header); + + path = header->path; + r_path = path + size; + target = uent; + for (i = uent->topo_rank - 1; i >= 0; i--) { + parent = to_ub_entity(target->dev.parent); + for_each_uent_port(port, parent) { + if (port->r_uent == target) { + set_hop_path(path, header->bits.hop_type, + (u32)i, (u32)port->index); + j = uent->topo_rank - 1 - i; + set_hop_path(r_path, header->bits.hop_type, + (u32)j, (u32)port->r_index); + break; + } + } + target = parent; + } +} + +/* rsp should check return value, req is inside caller, don't need */ +size_t calc_enum_pld_header_size(struct enum_pld_scan_header *header, bool req) +{ + size_t bytes; + + if (!req && header->bits.hop_type > ENUM_HOP_TYPE_WORD) { + pr_err("rsp header hop_type error, type=%u\n", header->bits.hop_type); + return 0; + } + + bytes = calc_forward_path_size(header); + + if (req && header->bits.r) + bytes <<= 1; + + bytes += ENUM_PLD_SCAN_HEADER_BASE_SIZE; + + return bytes; +} +EXPORT_SYMBOL_GPL(calc_enum_pld_header_size); + +/* pkt header + scan header */ +static size_t ub_enum_calc_header_sz(void *buf, bool req) +{ + size_t bytes; + + bytes = calc_enum_pld_header_size((struct enum_pld_scan_header *)(buf + + ENUM_PKT_HEADER_SIZE), req); + if (bytes == 0) + return 0; + + return bytes + ENUM_PKT_HEADER_SIZE; +} diff --git a/drivers/ub/ubus/enum.h b/drivers/ub/ubus/enum.h new file mode 100644 index 000000000000..db80b7040286 --- /dev/null +++ b/drivers/ub/ubus/enum.h @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __ENUM_H__ +#define __ENUM_H__ + +enum enum_cmd { + ENUM_CMD_TOPO_QUERY = 0, + ENUM_CMD_NA_CFG, + ENUM_CMD_NA_QUERY +}; + +/* + * enum pkt : enum_pkt_header + enum_pld_scan_header + reqX + * reqX : enum_topo_query_req or enum_na_cfg_req or enum_na_query_req + */ +struct enum_pkt_header { + /* DW0 */ + struct ub_link_header ulh; + /* DW1-DW2 */ + struct compact_network_header cnth; + /* DW3 */ + u16 rsv; + u16 upi; + + /* DW4~ */ + char payload[]; +}; +#define ENUM_PKT_HEADER_SIZE 16 + +struct enum_pld_scan_header { + /* DW0 */ + union { + struct { + u32 step : 8; + u32 hops : 8; + u32 hop_type : 4; + u32 r : 1; + u32 rsv : 11; + } bits; + u32 dw0; + }; + /* DW1~ */ + u8 path[]; /* include forward & return, 4byte align */ +}; +#define ENUM_PLD_SCAN_HEADER_BASE_SIZE 4 /* exclusive path */ + +struct enum_pld_scan_pdu_common { + /* DW0 */ + union { + struct { + union { + u8 status; + u8 slice_id; + }; + u8 opcode; + u8 cmd; +#define UB_ENUM_MNG_VERSION 0x1 + u8 version; + } bits; + u32 dw0; + }; + /* DW1 */ + u32 msn : 16; + u32 pdu_len : 8; + u32 msgq_id : 8; + /* DW2~DW5 */ + guid_t guid; +}; +#define ENUM_PLD_SCAN_PDU_COMMON_SIZE 24 + +struct enum_topo_query_req { + /* DW0~DW5 */ + struct enum_pld_scan_pdu_common common; +}; +#define ENUM_TOPO_QUERY_REQ_SIZE 24 + +struct enum_tlv_port_info { + /* DW0 */ + union { + struct { + u32 rsvd : 8; + u32 s : 1; + u32 b : 1; + u32 w : 1; + u32 t : 1; + u32 rsvd1 : 4; + u32 len : 8; + u32 type : 8; + } bits0; + u32 dw0; + }; + /* DW1 */ + u16 remote_port_idx; + u16 local_port_idx; + + /* DW2 */ + u16 cur_rate; + u16 max_rate; + + /* DW3~DW6 */ + guid_t remote_guid; +}; +#define ENUM_TOPO_QUERY_RSP_PORT_SIZE 28 + +struct enum_topo_query_rsp { + /* DW0~DW5 */ + struct enum_pld_scan_pdu_common common; + /* DW6 */ + union { + struct { + u32 num_seg : 4; + u32 rsv0 : 4; + u32 lf : 1; + u32 lp : 1; + u32 rsv1 : 6; + u32 mtu : 3; + u32 rsv2 : 5; + u32 sup_mtu : 3; + u32 rsv3 : 5; + } bits; + u32 dw6; + }; + /* DW7 */ + u32 num_lf_entries; + /* DW8 */ + u32 num_lp_entries; + /* DW9 */ + u16 num_ports; /* Number of ports in the current message */ + u16 total_num_ports; /* Total number of ports on the target device */ + /* DW10~ */ + struct enum_tlv_port_info port_info[]; +}; +#define ENUM_TOPO_QUERY_RSP_BASE_SIZE 40 /* exclusive port_info */ + +enum enum_tlv_type { /* M: Mandatory , O: optional */ + TLV_SLICE_INFO = 0, /* M */ + TLV_PORT_NUM = 1, /* M */ + TLV_PORT_INFO = 2, /* M */ + TLV_CAP_INFO = 4 /* M */ +}; + +struct enum_tlv_common { + u32 value : 16; + u32 len : 8; + u32 type : 8; +}; + +struct enum_tlv_slice_info { + u32 slice_id : 8; + u32 total_slice : 8; + u32 len : 8; + u32 type : 8; +}; +#define ENUM_TLV_SLICE_INFO_SZ 4 + +struct enum_tlv_port_num { + u32 total_num_ports : 16; + u32 len : 8; + u32 type : 8; + + u32 rsvd : 16; + u32 num_port_tlv : 16; +}; +#define ENUM_TLV_PORT_NUM_SZ 8 + +struct enum_tlv_cap_info { + u32 da : 1; + u32 rsvd0 : 7; + u32 mtu : 3; + u32 rsvd1 : 1; + u32 sup_mtu : 3; + u32 rsvd2 : 1; + u32 len : 8; + u32 type : 8; + + u32 class_code : 16; + u32 rsvd3 : 16; +}; +#define ENUM_TLV_CAP_INFO_SZ 8 + +size_t calc_enum_pld_header_size(struct enum_pld_scan_header *header, bool req); + +#endif /* __ENUM_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 7fd6bccab0f2..58d8eeb0d2d3 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -117,6 +117,7 @@ struct ub_entity { /* entity topology info */ struct ub_bus_controller *ubc; struct ub_entity *pue; /* ue/mue connected to their mue */ + int topo_rank; /* The levels of Breadth-First Search */ /* entity port info */ u16 port_nums; -- Gitee From 999d1b54b9aeb0211d91ec4d3b9b338ffda65ec7 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 19 Sep 2025 16:01:00 +0800 Subject: [PATCH 019/214] ub:ubus: Add UBUS resource space framework ANBZ: #45460 commit 8ae417e11ba40080486c4a258a10295ecddf26d9 openEuler. Add basic framework for UBUS resource space, including reading/writing functions, and so on. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/resource.c | 174 +++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/resource.h | 13 +++ include/ub/ubus/ubus.h | 73 ++++++++++++++++ 4 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/resource.c create mode 100644 drivers/ub/ubus/resource.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index fada110e4bf1..6b61b611ee22 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -3,6 +3,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o -ubus-y += enum.o +ubus-y += enum.o resource.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/resource.c b/drivers/ub/ubus/resource.c new file mode 100644 index 000000000000..9ec78931a744 --- /dev/null +++ b/drivers/ub/ubus/resource.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus resource: " fmt + +#include +#include + +#include "ubus.h" +#include "msg.h" +#include "resource.h" + +static const struct vm_operations_struct ub_phys_vm_ops = { + .access = generic_access_phys, +}; + +int ub_mmap_resource_range(struct ub_entity *uent, unsigned long idx, + struct vm_area_struct *vma, int write_combine) +{ + resource_size_t size; + + size = ((ub_resource_len(uent, idx) - 1) >> PAGE_SHIFT) + 1; + if (vma->vm_pgoff + vma_pages(vma) > size) + return -EINVAL; + + if (write_combine) + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + else + vma->vm_page_prot = pgprot_device(vma->vm_page_prot); + + vma->vm_pgoff += (ub_resource_start(uent, idx) >> PAGE_SHIFT); + vma->vm_ops = &ub_phys_vm_ops; + + return io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); +} + +static void __iomem *ub_iomap_range(struct ub_entity *uent, int res_id, + unsigned long offset, unsigned long maxlen, + bool is_wc) +{ + resource_size_t start = ub_resource_start(uent, res_id); + resource_size_t len = ub_resource_len(uent, res_id); + unsigned long flags = ub_resource_flags(uent, res_id); + + if (len <= offset || !start) + return NULL; + len -= offset; + start += offset; + if (maxlen && len > maxlen) + len = maxlen; + if (!(flags & IORESOURCE_MEM)) + return NULL; + + if (is_wc) + return ioremap_wc(start, len); + else + return ioremap(start, len); +} + +void __iomem *ub_iomap(struct ub_entity *uent, int res_id, unsigned long maxlen) +{ + if (res_id >= MAX_UB_RES_NUM || !uent) + return NULL; + + return ub_iomap_range(uent, res_id, 0, maxlen, false); +} +EXPORT_SYMBOL_GPL(ub_iomap); + +void __iomem *ub_iomap_wc(struct ub_entity *uent, int res_id, unsigned long maxlen) +{ + if (res_id >= MAX_UB_RES_NUM || !uent) + return NULL; + + return ub_iomap_range(uent, res_id, 0, maxlen, true); +} +EXPORT_SYMBOL_GPL(ub_iomap_wc); + +void ub_iounmap(void __iomem *addr) +{ + iounmap(addr); +} +EXPORT_SYMBOL_GPL(ub_iounmap); + +static int ub_read_ubba_reg(struct ub_entity *uent, size_t pg_size, int idx) +{ + u32 addr_l_reg[MAX_UB_RES_NUM] = { UB_ERS0_UBBA_L, UB_ERS1_UBBA_L, + UB_ERS2_UBBA_L }; + u32 addr_h_reg[MAX_UB_RES_NUM] = { UB_ERS0_UBBA_H, UB_ERS1_UBBA_H, + UB_ERS2_UBBA_H }; + u32 ss_reg[MAX_UB_RES_NUM] = { UB_ERS0_SS, UB_ERS1_SS, UB_ERS2_SS }; + u32 hpa_l = 0, hpa_h = 0, size = 0; + int ret; + + ret = ub_cfg_read_dword(uent, addr_l_reg[idx], &hpa_l); + ret |= ub_cfg_read_dword(uent, addr_h_reg[idx], &hpa_h); + ret |= ub_cfg_read_dword(uent, ss_reg[idx], &size); + if (ret) { + ub_err(uent, "read reg failed when request HPA resource\n"); + return -EINVAL; + } else if (size == 0) { + ub_err(uent, "size is 0 when request HPA resource\n"); + return -EINVAL; + } + + uent->zone[idx].res.start = ubba_gen(hpa_h, hpa_l); + uent->zone[idx].res.end = + uent->zone[idx].res.start + ((u64)size * pg_size - 1); + + uent->zone[idx].res.flags = IORESOURCE_MEM; + uent->zone[idx].res.name = ub_name(uent); + uent->zone[idx].ubba_used = 1; + + ub_info(uent, "mmio idx %d, %pR\n", idx, &uent->zone[idx].res); + return 0; +} + +static int ub_read_sa_reg(struct ub_entity *uent, size_t pg_size, int idx) +{ + u32 sa_l_reg[MAX_UB_RES_NUM] = { UB_ERS0_SA_L, UB_ERS1_SA_L, + UB_ERS2_SA_L }; + u32 sa_h_reg[MAX_UB_RES_NUM] = { UB_ERS0_SA_H, UB_ERS1_SA_H, + UB_ERS2_SA_H }; + u32 ss_reg[MAX_UB_RES_NUM] = { UB_ERS0_SS, UB_ERS1_SS, UB_ERS2_SS }; + u32 sa_l = 0, sa_h = 0, size = 0; + int ret; + + ret = ub_cfg_read_dword(uent, sa_l_reg[idx], &sa_l); + ret |= ub_cfg_read_dword(uent, sa_h_reg[idx], &sa_h); + ret |= ub_cfg_read_dword(uent, ss_reg[idx], &size); + if (ret) { + ub_err(uent, "read reg failed when request SA resource\n"); + return -EINVAL; + } else if (size == 0) { + ub_err(uent, "size is 0 when request SA resource\n"); + return -EINVAL; + } + + /* Shift left 32 bits to get upper-bit address */ + uent->zone[idx].region.start = ((u64)sa_h << 32) | sa_l; + uent->zone[idx].region.size = size * pg_size; + uent->zone[idx].res.flags = IORESOURCE_MEM; + uent->zone[idx].sa_used = 1; + + return 0; +} + +static int ub_entity_read_mmio_idx(struct ub_entity *uent, int idx) +{ + struct ub_entity *pue; + size_t pg_size = 0; + + /* ue to its mue, mue to Entity0, Entity0 use itself */ + pue = uent->pue; + if (pue->entity_idx) /* to ensure Entity0 uent */ + pue = pue->pue; + + (void)ub_cfg_read_byte(uent, UB_SYS_PGS, (u8 *)&pg_size); + if ((u8)pg_size & UB_SYS_PGS_SIZE) + pg_size = SZ_64K; + else + pg_size = SZ_4K; + + if (pue->zone[idx].ubba_used) + return ub_read_ubba_reg(uent, pg_size, idx); + + if (pue->zone[idx].sa_used) + return ub_read_sa_reg(uent, pg_size, idx); + + return -EPERM; +} diff --git a/drivers/ub/ubus/resource.h b/drivers/ub/ubus/resource.h new file mode 100644 index 000000000000..70c72ef0bca4 --- /dev/null +++ b/drivers/ub/ubus/resource.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __RESOURCE_H__ +#define __RESOURCE_H__ + +int ub_mmap_resource_range(struct ub_entity *uent, unsigned long idx, + struct vm_area_struct *vma, int write_combine); +void ub_entity_free_mmio_idx(struct ub_entity *dev, int idx); + +#endif /* __RESOURCE_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 58d8eeb0d2d3..556ee7f39056 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -28,8 +28,32 @@ .mod_vendor = (u32)UB_ANY_ID, .module = (u32)UB_ANY_ID, \ .class_code = (dev_class), .class_mask = (dev_class_mask) +#define ub_resource_start(dev, resno) ((dev)->zone[(resno)].res.start) +#define ub_resource_end(dev, resno) ((dev)->zone[(resno)].res.end) +#define ub_resource_flags(dev, resno) ((dev)->zone[(resno)].res.flags) +#define ub_resource_len(dev, resno) \ + ((ub_resource_start((dev), (resno)) == 0 && \ + ub_resource_end((dev), (resno)) == \ + ub_resource_start((dev), (resno))) ? 0 : \ + (ub_resource_end((dev), (resno)) - \ + ub_resource_start((dev), (resno)) + 1)) + #define UB_GUID_DW_NUM SZ_4 +struct ub_bus_region { + unsigned long start; + unsigned long size; +}; + +struct mmio_zone { + struct resource res; + struct ub_bus_region region; + u8 ubba_used; + u8 sa_used; + u8 init_succ; + u8 decoder_mapped; +}; + struct ub_guid { union { struct { @@ -71,6 +95,8 @@ static inline int ub_show_guid(struct ub_guid *guid, char *buf) guid->bits.reserved, guid->bits.seq_num); } +#define MAX_UB_RES_NUM 3 + enum ub_port_type { PHYSICAL, VIRTUAL, @@ -113,6 +139,7 @@ struct ub_entity { unsigned int eid; unsigned short entity_idx; u32 uent_num; /* ub dev number */ + struct mmio_zone zone[MAX_UB_RES_NUM]; /* entity topology info */ struct ub_bus_controller *ubc; @@ -267,6 +294,45 @@ extern struct bus_type ub_bus_type; void ub_iommu_fwnode_handle_set(const struct fwnode_handle *fwnode); const struct fwnode_handle *ub_iommu_fwnode_handle_get(void); +/** + * ub_iomap() - Map the resource space of the entity. + * @uent: UB entity. + * @resno: Resource Number. + * @maxlen: Map size. + * + * Invoke ioremap() to map the entity resource space, if maxlen is 0, then map + * size is ub_resource_len(), else map size is min(maxlen, ub_resource_len()). + * + * Context: Any context. + * Return: The return value is the same as that of ioremap(). + */ +void __iomem *ub_iomap(struct ub_entity *uent, int resno, unsigned long maxlen); + +/** + * ub_iomap_wc() - Map the resource space of the entity. + * @uent: UB entity. + * @resno: Resource Number. + * @maxlen: Map size. + * + * Invoke ioremap_wc() to map the entity resource space, if maxlen is 0, + * then map size is ub_resource_len(), else map size is + * min(maxlen, ub_resource_len()). + * + * Context: Any context. + * Return: The return value is the same as that of ioremap_wc(). + */ +void __iomem *ub_iomap_wc(struct ub_entity *uent, int resno, unsigned long maxlen); + +/** + * ub_iounmap() - Unmap the resource space of the entity. + * @addr: Target resource address. + * + * Invoke iounmap() to unmap the entity resource space. + * + * Context: Any context. + */ +void ub_iounmap(void __iomem *addr); + /** * ub_register_share_port() - Register a share port. * @uent: UB entity. @@ -416,6 +482,13 @@ void ub_unregister_driver(struct ub_driver *drv); static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) { return NULL; } static inline void ub_entity_put(struct ub_entity *uent) {} +static inline void __iomem * +ub_iomap(struct ub_entity *uent, int resno, unsigned long maxlen) +{ return NULL; } +static inline void __iomem * +ub_iomap_wc(struct ub_entity *uent, int resno, unsigned long maxlen) +{ return NULL; } +static inline void ub_iounmap(void __iomem *addr) {} static inline bool ub_cc_supported(struct ub_entity *uent) { return false; } static inline int ub_cc_enable(struct ub_entity *uent) -- Gitee From dfe4b873488125fdcabaa9f6d6dffe7330803b8e Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 19 Sep 2025 16:30:54 +0800 Subject: [PATCH 020/214] ub:ubus: Add UBUS resource space basic functions ANBZ: #45460 commit f94773e30426af02371bc80aa047d1144f7b4291 openEuler. Add UBUS resource allocate/release and set-up and unset functions. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/resource.c | 285 +++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/resource.h | 5 + include/ub/ubus/ubus.h | 2 + 3 files changed, 292 insertions(+) diff --git a/drivers/ub/ubus/resource.c b/drivers/ub/ubus/resource.c index 9ec78931a744..ebb1a529af77 100644 --- a/drivers/ub/ubus/resource.c +++ b/drivers/ub/ubus/resource.c @@ -7,11 +7,38 @@ #include #include +#include #include "ubus.h" #include "msg.h" #include "resource.h" +struct query_token_msg_pld_req { + u32 token_enable : 1; + u32 reserved : 31; +}; +#define QUERY_TOKEN_MSG_REQ_PLD_SIZE 4 +#define QUERY_TOKEN_MSG_REQ_SIZE 36 + +struct query_token_msg_pld_rsp { + u32 token_id : 20; + u32 reserved : 12; + u32 token_value; +}; +#define QUERY_TOKEN_MSG_RSP_SIZE 40 + +struct query_token_msg_pld { + union { + struct query_token_msg_pld_req req; + struct query_token_msg_pld_rsp rsp; + }; +}; + +struct query_token_msg_pkt { + struct msg_pkt_header header; + struct query_token_msg_pld pld; +}; + static const struct vm_operations_struct ub_phys_vm_ops = { .access = generic_access_phys, }; @@ -38,6 +65,73 @@ int ub_mmap_resource_range(struct ub_entity *uent, unsigned long idx, vma->vm_page_prot); } +static int _ub_assign_resource(struct ub_entity *uent, int idx, + resource_size_t size, resource_size_t align) +{ + struct resource *res; + struct resource_entry *entry, *tmp; + struct resource *uent_res = &uent->zone[idx].res; + struct ub_bus_controller *ubc = uent->ubc; + int ret = -EINVAL; + + if (ubc == NULL) { + ub_err(uent, "ub assign resource failed, ubc is NULL.\n"); + return -EINVAL; + } + + resource_list_for_each_entry_safe(entry, tmp, &ubc->resources) { + res = entry->res; + + if (!(res->flags & IORESOURCE_MEM) || + !strstr(res->name, "UB_BUS_CTL")) + continue; + ret = allocate_resource(res, uent_res, size, 0, ~0, align, NULL, + NULL); + if (ret == 0) + return 0; + } + ub_err(uent, "ub assign resource failed, no res available, ret = %d\n", ret); + + return -ENOMEM; +} + +int ub_assign_resource(struct ub_entity *uent, int idx) +{ + struct resource *res = &uent->zone[idx].res; + resource_size_t size; + resource_size_t page_size = PAGE_SIZE; + /* decoder mapped address unit 1M */ + resource_size_t align = page_size > SZ_1M ? page_size : SZ_1M; + int ret; + + res->name = ub_name(uent); + res->flags |= IORESOURCE_UNSET | IORESOURCE_SIZEALIGN; + size = uent->zone[idx].region.size; + ret = _ub_assign_resource(uent, idx, size, align); + if (ret < 0) { + ub_err(uent, "RESOURCE %d: failed to assign, size=%#llx\n", + idx, size); + return ret; + } + + res->flags &= ~IORESOURCE_UNSET; + res->flags &= ~IORESOURCE_STARTALIGN; + ub_info(uent, "RESOURCE %d: assigned %pR\n", idx, res); + + return 0; +} + +void ub_release_resource(struct ub_entity *uent, int idx) +{ + int ret; + + if (uent->zone[idx].res.parent) { + ret = release_resource(&uent->zone[idx].res); + if (ret) + ub_err(uent, "resource release failed, ret=%d.\n", ret); + } +} + static void __iomem *ub_iomap_range(struct ub_entity *uent, int res_id, unsigned long offset, unsigned long maxlen, bool is_wc) @@ -172,3 +266,194 @@ static int ub_entity_read_mmio_idx(struct ub_entity *uent, int idx) return -EPERM; } + +int ub_insert_resource(struct ub_entity *dev, int idx) +{ + struct resource *res, *root = NULL; + int ret; + + if (idx >= MAX_UB_RES_NUM) + return -EINVAL; + + res = &dev->zone[idx].res; + if (!(res->flags & IORESOURCE_MEM)) + return -EINVAL; + + root = &iomem_resource; + + ret = insert_resource(root, res); + if (ret) { + ub_warn(dev, "failed to claim uent resource %pR\n", res); + return -ENOMEM; + } + + return 0; +} + +int ub_entity_alloc_mmio_idx(struct ub_entity *dev, int idx) +{ + if (is_ibus_controller(dev) || is_idev(dev)) + return ub_insert_resource(dev, idx); + return ub_assign_resource(dev, idx); +} + +void ub_entity_free_mmio_idx(struct ub_entity *dev, int idx) +{ + ub_release_resource(dev, idx); + dev->zone[idx].init_succ = 0; + dev->zone[idx].ubba_used = 0; + dev->zone[idx].sa_used = 0; +} + +static int ub_query_token_rsp_handle(struct ub_entity *uent, + struct query_token_msg_pkt *pkt) +{ + struct query_token_msg_pld_rsp *rsp = &pkt->pld.rsp; + + if (pkt->header.msgetah.rsp_status != UB_MSG_RSP_SUCCESS) { + ub_err(uent, "query token rsp, status=%#02x\n", + pkt->header.msgetah.rsp_status); + return -EINVAL; + } + + uent->token_id = rsp->token_id; + uent->token_value = rsp->token_value; + + return 0; +} + +static int ub_query_token_info(struct ub_entity *uent) +{ + struct message_device *mdev = uent->message->mdev; + struct query_token_msg_pkt req_pkt = {}; + struct query_token_msg_pkt rsp_pkt = {}; + struct msg_info info = {}; + int ret; + + ub_msg_pkt_header_init(&req_pkt.header, uent, + QUERY_TOKEN_MSG_REQ_PLD_SIZE, + code_gen(UB_MSG_CODE_SEC, UB_TOKEN_CHECK_CFG, + MSG_REQ), false); + + req_pkt.pld.req.token_enable = 1; + message_info_init(&info, uent, &req_pkt, &rsp_pkt, + (QUERY_TOKEN_MSG_REQ_SIZE << MSG_REQ_SIZE_OFFSET) | + QUERY_TOKEN_MSG_RSP_SIZE); + + ret = message_sync_request(mdev, &info, req_pkt.header.msgetah.code); + if (ret) + return ret; + + return ub_query_token_rsp_handle(uent, &rsp_pkt); +} + +static int ub_entity_writeback_addr(struct ub_entity *uent, int idx) +{ + u32 addr_l_reg[MAX_UB_RES_NUM] = { UB_ERS0_UBBA_L, UB_ERS1_UBBA_L, + UB_ERS2_UBBA_L }; + u32 addr_h_reg[MAX_UB_RES_NUM] = { UB_ERS0_UBBA_H, UB_ERS1_UBBA_H, + UB_ERS2_UBBA_H }; + u32 support_feature, ubba_l, ubba_h; + int ret; + + if (!uent->zone[idx].sa_used) + return 0; + + ret = ub_cfg_read_dword(uent, UB_CFG1_SUPPORT_FEATURE_L, + &support_feature); + if (ret) { + ub_err(uent, + "read reg failed when request sup feature, ret=%d\n", + ret); + return -EINVAL; + } + if (support_feature & UB_UBBAS_SUPPORT) { + ubba_l = (u32)(uent->zone[idx].res.start); + /* Shift right 32 bits to get upper-bit address */ + ubba_h = (u32)((uent->zone[idx].res.start) >> 32); + ub_cfg_write_dword(uent, addr_l_reg[idx], ubba_l); + ub_cfg_write_dword(uent, addr_h_reg[idx], ubba_h); + } + + return 0; +} + +int _ub_entity_setup_mmio(struct ub_entity *dev) +{ + int ret; + int i; + + if (is_ibus_controller(dev)) { + ub_info(dev, "now doesn't support ub bus controller mmio\n"); + return 0; + } + + if (is_device(dev) && !is_p_device(dev)) { + ret = ub_query_token_info(dev); + if (ret) { + ub_err(dev, "Token query failed\n"); + return ret; + } + ub_info(dev, "Token ID=%#x\n", dev->token_id); + } + + for (i = 0; i < MAX_UB_RES_NUM; i++) { + ret = ub_entity_read_mmio_idx(dev, i); + if (ret) + continue; + + ret = ub_entity_alloc_mmio_idx(dev, i); + if (ret) { + ub_err(dev, + "failed to alloc resource for mmio idx %d\n", i); + goto fail; + } + + /* Write back the address reg for the VM scenario capture. */ + ret = ub_entity_writeback_addr(dev, i); + if (ret) { + ub_err(dev, + "failed to write back address for mmio idx %d\n", + i); + ub_entity_free_mmio_idx(dev, i); + goto fail; + } + + ub_info(dev, "MMIO[%d]: ubba=%#lx, size=%#llx, hpa=%#llx, wr attr=%01u, prefetchable=%01u, order type=%01u,\n", + i, dev->zone[i].region.start, ub_resource_len(dev, i), + dev->zone[i].res.start, 0, 0, 0); + dev->zone[i].init_succ = 1; + } + + return 0; + +fail: + for (i -= 1; i >= 0; i--) + if (dev->zone[i].init_succ) + ub_entity_free_mmio_idx(dev, i); + + return ret; +} + +int ub_entity_setup_mmio(struct ub_entity *uent) +{ + int i; + + for (i = 0; i < MAX_UB_RES_NUM; i++) + if (!uent->zone[i].init_succ) + break; + + if (i == MAX_UB_RES_NUM) + return 0; + + return _ub_entity_setup_mmio(uent); +} + +void ub_entity_unset_mmio(struct ub_entity *dev) +{ + int i; + + for (i = 0; i < MAX_UB_RES_NUM; i++) + if (dev->zone[i].init_succ) + ub_entity_free_mmio_idx(dev, i); +} diff --git a/drivers/ub/ubus/resource.h b/drivers/ub/ubus/resource.h index 70c72ef0bca4..90da3c402dc0 100644 --- a/drivers/ub/ubus/resource.h +++ b/drivers/ub/ubus/resource.h @@ -6,8 +6,13 @@ #ifndef __RESOURCE_H__ #define __RESOURCE_H__ +struct ub_entity; +int _ub_entity_setup_mmio(struct ub_entity *uent); +int ub_entity_setup_mmio(struct ub_entity *dev); +void ub_entity_unset_mmio(struct ub_entity *dev); int ub_mmap_resource_range(struct ub_entity *uent, unsigned long idx, struct vm_area_struct *vma, int write_combine); +int ub_insert_resource(struct ub_entity *dev, int idx); void ub_entity_free_mmio_idx(struct ub_entity *dev, int idx); #endif /* __RESOURCE_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 556ee7f39056..0a3754380e95 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -140,6 +140,8 @@ struct ub_entity { unsigned short entity_idx; u32 uent_num; /* ub dev number */ struct mmio_zone zone[MAX_UB_RES_NUM]; + u32 token_id; + u32 token_value; /* entity topology info */ struct ub_bus_controller *ubc; -- Gitee From 752590d89c1eddbe1c746d9c4bcfac4afdfd923e Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 16:26:49 +0800 Subject: [PATCH 021/214] ub:ubus: Support enumeration topology query commands ANBZ: #45460 commit 1bed95293ba8482a16ea4e43171bb662c1fed5cb openEuler. Implement device topology query message transmission and reception, and parse response message information. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/enum.c | 336 +++++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/enum.h | 2 + drivers/ub/ubus/msg.c | 9 ++ drivers/ub/ubus/msg.h | 5 + include/ub/ubus/ubus.h | 1 + 5 files changed, 353 insertions(+) diff --git a/drivers/ub/ubus/enum.c b/drivers/ub/ubus/enum.c index b298402e2352..315077f184da 100644 --- a/drivers/ub/ubus/enum.c +++ b/drivers/ub/ubus/enum.c @@ -195,3 +195,339 @@ static size_t ub_enum_calc_header_sz(void *buf, bool req) return bytes + ENUM_PKT_HEADER_SIZE; } + +static struct topo_scan_info { +#define UB_TOPO_BUF_SZ SZ_4K + void *buf; + struct list_head dev_list; +} topo_scan; + +static int ub_enum_topo_scan_init(void) +{ + topo_scan.buf = kzalloc(UB_TOPO_BUF_SZ, GFP_KERNEL); + if (!topo_scan.buf) + return -ENOMEM; + + INIT_LIST_HEAD(&topo_scan.dev_list); + + return 0; +} + +static void ub_enum_topo_scan_uninit(void) +{ + kfree(topo_scan.buf); +} + +static inline void ub_enum_refresh_and_init_buffer_header(struct ub_entity *uent, + void *buf) +{ + memset(buf, 0, UB_TOPO_BUF_SZ); + ub_enum_pkt_header_init(buf); + ub_enum_pld_header_init(uent, buf); +} + +static void ub_enum_pld_common_setup(struct enum_pld_scan_pdu_common *common, + u8 cmd, u8 opcode, guid_t *guid, + int plen) +{ + common->bits.version = UB_ENUM_MNG_VERSION; + common->bits.cmd = cmd; + common->bits.opcode = opcode; + common->msn = 0; + common->guid = *guid; + common->pdu_len = (u8)(plen / SZ_4); +} + +static int ub_enum_topo_query(struct ub_entity *uent, void *buf, u16 start_idx, + u16 *actual_rsp_size) +{ + struct message_device *mdev = uent->message->mdev; + struct enum_topo_query_req *req; + struct msg_info info = {}; + size_t header_sz; + int ret; + + ub_enum_refresh_and_init_buffer_header(uent, buf); + + header_sz = ub_enum_calc_header_sz(buf, true); + req = (struct enum_topo_query_req *)(buf + header_sz); + ub_enum_pld_common_setup(&req->common, ENUM_CMD_TOPO_QUERY, + ENUM_TOPO_QUERY_REQUEST, &uent->guid.id, + ENUM_TOPO_QUERY_REQ_SIZE); + + req->common.bits.slice_id = start_idx; + + message_info_init(&info, uent, buf, buf, + ((header_sz + ENUM_TOPO_QUERY_REQ_SIZE) << + MSG_REQ_SIZE_OFFSET) | UB_TOPO_BUF_SZ); + + ret = message_sync_enum(mdev, &info, ENUM_CMD_TOPO_QUERY); + if (!ret) + *actual_rsp_size = info.actual_rsp_size; + + return ret; +} + +static struct enum_topo_query_rsp * +ub_enum_topo_query_and_check(struct ub_entity *uent, void *buf, u16 start_idx) +{ + struct device *dev = &uent->ubc->dev; + struct enum_topo_query_rsp *rsp; + u16 actual_rsp_size; + size_t header_sz; + int ret; + + ret = ub_enum_topo_query(uent, buf, start_idx, &actual_rsp_size); + if (ret) { + dev_err(dev, "enum topo query failed, ret=%d\n", ret); + return NULL; + } + + header_sz = ub_enum_calc_header_sz(buf, false); + if (header_sz == 0) { + dev_err(dev, "enum topo query rsp header sz 0\n"); + return NULL; + } + + if (header_sz + ENUM_TOPO_QUERY_RSP_BASE_SIZE > actual_rsp_size) { + dev_err(dev, "enum topo query rsp pkt size invalid\n"); + return NULL; + } + + rsp = (struct enum_topo_query_rsp *)(buf + header_sz); + ret = rsp->common.bits.status; + if (ret) { + dev_err(dev, "enum rsp has error, status=%d\n", ret); + return NULL; + } + + if (header_sz + rsp->common.pdu_len * SZ_4 != actual_rsp_size) { + dev_err(dev, "enum topo query rsp pdu_len invalid\n"); + return NULL; + } + + return rsp; +} + +static void ub_enum_parse_port(struct ub_entity *uent, + struct enum_tlv_port_info *pi, u16 num) +{ + struct enum_tlv_port_info *port_info; + struct device *dev = &uent->ubc->dev; + struct ub_port *port; + + for (u16 i = 0; i < num; i++) { + port_info = pi + i; + if (port_info->local_port_idx >= uent->port_nums) { + dev_err(dev, "local port idx %u exceeds uent port num %u\n", + port_info->local_port_idx, uent->port_nums); + continue; + } + + port = uent->ports + port_info->local_port_idx; + + port->domain_boundary = port_info->bits0.b; + port->type = port_info->bits0.t ? VIRTUAL : PHYSICAL; + port->shareable = (bool)port_info->bits0.w; + + /* skip link down port */ + if (!port_info->bits0.s) + continue; + + if (!is_device(uent) && !is_idev(uent) && port->domain_boundary) + continue; + + /* neighbor info of a boundary port shouldn't be stored */ + port->r_index = port_info->remote_port_idx; + guid_copy(&port->r_guid, &port_info->remote_guid); + } +} + +struct enum_tlv_info { + struct enum_tlv_slice_info *si; + struct enum_tlv_port_info *pi; + struct enum_tlv_port_num *pn; + struct enum_tlv_cap_info *ci; + u16 port_nums; +}; + +static int ub_enum_tlv_parse(void *tlv, int tlv_len, struct enum_tlv_info *info) +{ + struct enum_tlv_common *common; + u16 port_nums = 0; + int left_len = tlv_len; + + while ((left_len > 0) && (left_len % SZ_4 == 0)) { + common = (struct enum_tlv_common *)tlv; + + switch (common->type) { + case TLV_SLICE_INFO: + info->si = (struct enum_tlv_slice_info *)tlv; + break; + case TLV_PORT_NUM: + info->pn = (struct enum_tlv_port_num *)tlv; + break; + case TLV_PORT_INFO: + if (port_nums == 0) + info->pi = (struct enum_tlv_port_info *)tlv; + port_nums++; + break; + case TLV_CAP_INFO: + info->ci = (struct enum_tlv_cap_info *)tlv; + break; + default: + pr_warn("not support tlv type[%u]\n", common->type); + } + + if (common->len == 0) { + pr_err("enum tlv len is invalid\n"); + return -EINVAL; + } + + left_len -= (int)common->len; + tlv += common->len; + } + + info->port_nums = port_nums; + + return 0; +} + +/* only check guid itself here, repetition problem is not considered */ +bool ub_type_valid(struct ub_entity *uent, bool is_ctl) +{ + struct device *dev = &uent->ubc->dev; + u16 code = uent_class(uent); + u8 type = uent_type(uent); + + if (uent->ent_type == UB_ENT_UNKNOWN) { + dev_err(dev, "ent type unknown, type=%#x, class=%#x\n", + type, code); + return false; + } + + if (is_ctl && !is_ibus_controller(uent)) { + dev_err(dev, "ubc bad type, type=%#x, class=%#x\n", + type, code); + return false; + } + + if (!is_ctl && (is_ibus_controller(uent) || is_bus_controller(uent))) { + dev_err(dev, "peripheral bad type, type=%#x, class=%#x\n", + type, code); + return false; + } + + if (uent->pool && !(is_p_device(uent) || is_p_idevice(uent))) { + dev_err(dev, "pool bad type, type=%#x, class=%#x\n", + type, code); + return false; + } + + return true; +} + +void ub_entity_type_init(struct ub_entity *uent) +{ + u8 base = uent_base_code(uent); + + switch (uent_type(uent)) { + case UB_TYPE_CONTROLLER: + if (base == UB_BASE_CODE_BUS_CONTROLLER) { + if (uent->class_code == UB_CLASS_BUS_CONTROLLER) + uent->ent_type = UB_ENT_BUS_CONTROLLER; + else + uent->ent_type = UB_ENT_UNKNOWN; + } else { + if (!uent->pool) + uent->ent_type = UB_ENT_DEVICE; + else + uent->ent_type = UB_ENT_P_DEVICE; + } + break; + case UB_TYPE_ICONTROLLER: + if (base == UB_BASE_CODE_BUS_CONTROLLER) { + uent->ent_type = UB_ENT_IBUS_CONTROLLER; + } else { + if (!uent->pool) + uent->ent_type = UB_ENT_IDEVICE; + else + uent->ent_type = UB_ENT_P_IDEVICE; + } + break; + case UB_TYPE_SWITCH: + case UB_TYPE_ISWITCH: + uent->ent_type = UB_ENT_SWITCH; + break; + default: + uent->ent_type = UB_ENT_UNKNOWN; + } +} + +#define PORT_TOTAL_NUM_MAX 256 + +static int ub_enum_ent(struct ub_entity *uent, void *buf) +{ + struct device *dev = &uent->ubc->dev; + struct enum_pld_scan_pdu_common *pc; + int tlv_len, ret, total_num_ports; + struct enum_tlv_info info = {}; + u8 total_slice, slice_id = 0; + void *rsp, *tlv; + + do { + rsp = (void *)ub_enum_topo_query_and_check(uent, buf, slice_id); + if (!rsp) { + ret = -EBUSY; + goto err; + } + + pc = (struct enum_pld_scan_pdu_common *)rsp; + tlv_len = (int)pc->pdu_len * SZ_4 - ENUM_PLD_SCAN_PDU_COMMON_SIZE; + tlv = rsp + ENUM_PLD_SCAN_PDU_COMMON_SIZE; + + ret = ub_enum_tlv_parse(tlv, tlv_len, &info); + if (ret) + goto err; + + if (slice_id == 0) { + if (!info.si || !info.pi || !info.pn || !info.ci) { + dev_err(dev, "tlv si/pi/pn/ci NULL\n"); + goto err; + } + + uent->class_code = info.ci->class_code; + ub_entity_type_init(uent); + if (!ub_type_valid(uent, !uent->topo_rank)) + goto err; + + total_slice = info.si->total_slice; + total_num_ports = (int)info.pn->total_num_ports; + } + + if (!uent->ports) { + uent->port_nums = total_num_ports; + if (uent->port_nums > PORT_TOTAL_NUM_MAX) { + dev_err(dev, "Total num ports is over total num max(%d).\n", + PORT_TOTAL_NUM_MAX); + return -EINVAL; + } + + ret = ub_ports_setup(uent); + if (ret) { + dev_err(dev, "enum port setup fail, ret=%d\n", + ret); + return ret; + } + } + + ub_enum_parse_port(uent, info.pi, info.port_nums); + slice_id++; + } while (slice_id < total_slice); + + return 0; +err: + if (uent->ports) + ub_ports_unset(uent); + return ret; +} diff --git a/drivers/ub/ubus/enum.h b/drivers/ub/ubus/enum.h index db80b7040286..da4d293f38e8 100644 --- a/drivers/ub/ubus/enum.h +++ b/drivers/ub/ubus/enum.h @@ -182,5 +182,7 @@ struct enum_tlv_cap_info { #define ENUM_TLV_CAP_INFO_SZ 8 size_t calc_enum_pld_header_size(struct enum_pld_scan_header *header, bool req); +bool ub_type_valid(struct ub_entity *uent, bool is_ctl); +void ub_entity_type_init(struct ub_entity *uent); #endif /* __ENUM_H__ */ diff --git a/drivers/ub/ubus/msg.c b/drivers/ub/ubus/msg.c index c7be05f350bb..67dc56ab8ce3 100644 --- a/drivers/ub/ubus/msg.c +++ b/drivers/ub/ubus/msg.c @@ -134,3 +134,12 @@ int message_send(struct message_device *mdev, struct msg_info *info, return -ENOTTY; } + +int message_sync_enum(struct message_device *mdev, struct msg_info *info, + u8 cmd) +{ + if (mdev->ops->sync_enum) + return mdev->ops->sync_enum(mdev, info, cmd); + + return -ENOTTY; +} diff --git a/drivers/ub/ubus/msg.h b/drivers/ub/ubus/msg.h index fd1d9df7d20c..c980033803bc 100644 --- a/drivers/ub/ubus/msg.h +++ b/drivers/ub/ubus/msg.h @@ -192,6 +192,7 @@ enum message_tx_type { * @remove_dev: remove ub_entity to uninit message * @sync_request: send message to target ub_entity and wait response * @send: send message to target ub_entity but not wait response + * @sync_enum: send enum message to target ub_entity and wait response * @owner: Driver module providing these ops */ struct message_ops { @@ -201,6 +202,8 @@ struct message_ops { u8 code); int (*send)(struct message_device *mdev, struct msg_info *info, u8 code); + int (*sync_enum)(struct message_device *mdev, struct msg_info *info, + u8 cmd); struct module *owner; }; @@ -253,5 +256,7 @@ int message_sync_request(struct message_device *mdev, struct msg_info *info, u8 code); int message_send(struct message_device *mdev, struct msg_info *info, u8 code); +int message_sync_enum(struct message_device *mdev, struct msg_info *info, + u8 cmd); #endif /* __MSG_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 0a3754380e95..6e199a68c202 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -130,6 +130,7 @@ struct ub_entity { unsigned long priv_flags; /* Private flags for the UB driver */ /* entity base info */ + bool pool; int ent_type; struct ub_guid guid; u16 class_code; -- Gitee From 1e9d3b6712c48054a9079b4e5b87df90182b74d3 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 16:46:38 +0800 Subject: [PATCH 022/214] ub:ubus: Supports network address configuration and querying ANBZ: #45460 commit 6b215f25fcf1fc8f44fbb3cd7fc77ce96d934102 openEuler. Implement the functionality for configuring network addresses in enumeration messages and for sending and receiving network address query messages. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/enum.c | 272 +++++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/enum.h | 2 + 2 files changed, 274 insertions(+) diff --git a/drivers/ub/ubus/enum.c b/drivers/ub/ubus/enum.c index 315077f184da..466c7998c992 100644 --- a/drivers/ub/ubus/enum.c +++ b/drivers/ub/ubus/enum.c @@ -8,6 +8,7 @@ #include "ubus.h" #include "msg.h" #include "port.h" +#include "cna.h" #include "enum.h" #define ENUM_MAX_HOPS 255 @@ -531,3 +532,274 @@ static int ub_enum_ent(struct ub_entity *uent, void *buf) ub_ports_unset(uent); return ret; } + +static int ub_enum_na_query_rsp(void *buf, u32 *cna, u16 actual_rsp_size) +{ + struct enum_na_query_rsp *rsp; + size_t header_sz; + + header_sz = ub_enum_calc_header_sz(buf, false); + if (header_sz == 0) { + pr_err("na query rsp header sz 0\n"); + return -EINVAL; + } + + if (header_sz + ENUM_NA_QUERY_RSP_SIZE != actual_rsp_size) { + pr_err("na query rsp pld sz invalid\n"); + return -EINVAL; + } + + rsp = (struct enum_na_query_rsp *)(buf + header_sz); + + if (!rsp->common.bits.status) + *cna = rsp->cna; + + return rsp->common.bits.status; +} + +static size_t ub_enum_na_query_init(struct ub_entity *uent, void *buf, u8 opcode, + u16 port_idx) +{ + struct enum_na_query_req *req; + size_t header_sz; + + memset(buf, 0, UB_TOPO_BUF_SZ); + ub_enum_pkt_header_init(buf); + ub_enum_pld_header_init(uent, buf); + + header_sz = ub_enum_calc_header_sz(buf, true); + req = (struct enum_na_query_req *)(buf + header_sz); + + ub_enum_pld_common_setup(&req->common, ENUM_CMD_NA_QUERY, + opcode, &uent->guid.id, + ENUM_NA_QUERY_REQ_SIZE); + + if (opcode == ENUM_NA_QUERY_PORT) + req->port_idx = port_idx; + + return header_sz + ENUM_NA_QUERY_REQ_SIZE; +} + +int ub_query_port_na(struct ub_entity *uent, void *buf) +{ + struct device *dev = &uent->ubc->dev; + struct msg_info info = {0}; + size_t req_pkt_sz; + struct ub_port *p; + int ret; + u32 cna; + + for_each_uent_port(p, uent) { + if (!p->domain_boundary) + continue; + + req_pkt_sz = ub_enum_na_query_init(uent, buf, + ENUM_NA_QUERY_PORT, + p->index); + + message_info_init(&info, uent, buf, buf, + (req_pkt_sz << MSG_REQ_SIZE_OFFSET) | + UB_TOPO_BUF_SZ); + + ret = message_sync_enum(uent->ubc->mdev, &info, + ENUM_CMD_NA_QUERY); + if (ret) + goto out; + + ret = ub_enum_na_query_rsp(buf, &cna, info.actual_rsp_size); + if (ret) { + dev_err(dev, "port na query rsp, status=%d\n", ret); + ret = -EBUSY; + goto out; + } + + p->cna = cna; + if (p->cna) + dev_info(dev, "update boundary port%u cna to %#x\n", + p->index, p->cna); + } + + return 0; +out: + for_each_uent_port(p, uent) + if (p->domain_boundary) + p->cna = 0; + + dev_err(dev, "query fail and reset boundary port cna to 0.\n"); + + return ret; +} + +int ub_query_ent_na(struct ub_entity *uent, void *buf) +{ + struct device *dev = &uent->ubc->dev; + struct msg_info info = {}; + size_t req_pkt_sz; + int ret; + u32 cna; + + req_pkt_sz = ub_enum_na_query_init(uent, buf, ENUM_NA_QUERY_DEVICE, 0); + message_info_init(&info, uent, buf, buf, + (req_pkt_sz << MSG_REQ_SIZE_OFFSET) | + UB_TOPO_BUF_SZ); + + ret = message_sync_enum(uent->ubc->mdev, &info, + ENUM_CMD_NA_QUERY); + if (ret) + return ret; + + ret = ub_enum_na_query_rsp(buf, &cna, info.actual_rsp_size); + if (ret) { + dev_err(dev, "dev na query rsp, status=%d\n", ret); + return -EBUSY; + } + + uent->cna = cna; + if (uent->cna) + dev_info(dev, "update cluster ubc cna to %#x\n", uent->cna); + + return 0; +} + +static int ub_enum_cluster_query(struct ub_entity *uent, void *buf) +{ + int ret; + + ret = ub_query_ent_na(uent, buf); + if (ret) { + dev_err(&uent->ubc->dev, "query cluster ubc dev cna err\n"); + return ret; + } + + ret = ub_query_port_na(uent, buf); + if (ret) + dev_err(&uent->ubc->dev, "query cluster ubc port cna err\n"); + + return ret; +} + +static int ub_enum_na_cfg_rsp(void *buf, u16 actual_rsp_size) +{ + struct enum_na_cfg_rsp *rsp; + size_t header_sz; + + header_sz = ub_enum_calc_header_sz(buf, false); + if (header_sz == 0) { + pr_err("na cfg rsp header sz 0\n"); + return -EINVAL; + } + + if (header_sz + ENUM_NA_CFG_RSP_SIZE != actual_rsp_size) { + pr_err("na cfg rsp pld sz invalid\n"); + return -EINVAL; + } + + rsp = (struct enum_na_cfg_rsp *)(buf + header_sz); + + return rsp->common.bits.status; +} + +static size_t ub_enum_na_cfg_init(struct ub_entity *uent, void *buf, u32 cna, + u8 opcode, u16 port_idx) +{ + struct enum_na_cfg_req *req; + size_t header_sz; + + ub_enum_refresh_and_init_buffer_header(uent, buf); + + header_sz = ub_enum_calc_header_sz(buf, true); + req = (struct enum_na_cfg_req *)(buf + header_sz); + + ub_enum_pld_common_setup(&req->common, ENUM_CMD_NA_CFG, opcode, + &uent->guid.id, ENUM_NA_CFG_REQ_SIZE); + + req->cna = cna; + if (opcode == ENUM_NA_CFG_PORT) + req->port_idx = port_idx; + + return header_sz + ENUM_NA_CFG_REQ_SIZE; +} + +/* Configuring the CNA of the ub entity Port */ +static int ub_enum_port_na_cfg(struct ub_entity *uent, void *buf) +{ + struct message_device *mdev = uent->message->mdev; + struct msg_info info = {}; + size_t req_pkt_sz; + struct ub_port *p; + int ret; + + for_each_uent_port(p, uent) { + if (is_ibus_controller(uent) && p->domain_boundary) + continue; + + req_pkt_sz = ub_enum_na_cfg_init(uent, buf, p->cna, + ENUM_NA_CFG_PORT, p->index); + + message_info_init(&info, uent, buf, buf, + (req_pkt_sz << MSG_REQ_SIZE_OFFSET) | + UB_TOPO_BUF_SZ); + + ret = message_sync_enum(mdev, &info, ENUM_CMD_NA_CFG); + if (ret) + return ret; + + ret = ub_enum_na_cfg_rsp(buf, info.actual_rsp_size); + if (ret) { + dev_err(&uent->ubc->dev, "port na cfg rsp, status=%d\n", + ret); + return -EBUSY; + } + } + + return 0; +} + +static int ub_enum_na_cfg(struct ub_entity *uent, void *buf) +{ + struct message_device *mdev = uent->message->mdev; + struct device *dev = &uent->ubc->dev; + enum enum_na_cfg_opcode opcode; + struct msg_info info = {}; + size_t req_pkt_sz; + int ret; + + ret = ub_cna_alloc(uent); + if (ret) { + dev_err(dev, "cna alloc fail, ret=%d\n", ret); + goto na_failed; + } + + if (is_switch(uent)) /* Switch hasn't port cna register */ + goto cfg_dev; + + ret = ub_enum_port_na_cfg(uent, buf); + if (ret) { + dev_err(dev, "port na cfg fail, ret=%d\n", ret); + goto na_failed; + } + + if (is_ibus_controller(uent) && uent->ubc->cluster) { + ret = ub_enum_cluster_query(uent, buf); + if (ret) + goto na_failed; + return 0; + } + +cfg_dev: + opcode = ENUM_NA_CFG_PRIMARY; + req_pkt_sz = ub_enum_na_cfg_init(uent, buf, uent->cna, opcode, 0); + + message_info_init(&info, uent, buf, buf, + (req_pkt_sz << MSG_REQ_SIZE_OFFSET) | UB_TOPO_BUF_SZ); + + ret = message_sync_enum(mdev, &info, ENUM_CMD_NA_CFG); + if (ret) + goto na_failed; + + return 0; + +na_failed: + ub_cna_free(uent); + return ret; +} diff --git a/drivers/ub/ubus/enum.h b/drivers/ub/ubus/enum.h index da4d293f38e8..837197321ed3 100644 --- a/drivers/ub/ubus/enum.h +++ b/drivers/ub/ubus/enum.h @@ -182,6 +182,8 @@ struct enum_tlv_cap_info { #define ENUM_TLV_CAP_INFO_SZ 8 size_t calc_enum_pld_header_size(struct enum_pld_scan_header *header, bool req); +int ub_query_port_na(struct ub_entity *uent, void *buf); +int ub_query_ent_na(struct ub_entity *uent, void *buf); bool ub_type_valid(struct ub_entity *uent, bool is_ctl); void ub_entity_type_init(struct ub_entity *uent); -- Gitee From 6ee004b0ef5163a7d95b639434c3faca51955f6d Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 16:58:25 +0800 Subject: [PATCH 023/214] ub:ubus: Support for shortest path routing configuration ANBZ: #45460 commit 20e1e585cd94727b8820200af501edbe8d8a7e01 openEuler. Implement shortest path calculation using BFS to complete the basic routing configuration for the entire network. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/enum.c | 187 +++++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/enum.h | 1 + drivers/ub/ubus/ubus.h | 1 + include/ub/ubus/ubus.h | 1 + 4 files changed, 190 insertions(+) diff --git a/drivers/ub/ubus/enum.c b/drivers/ub/ubus/enum.c index 466c7998c992..50d00cbdf486 100644 --- a/drivers/ub/ubus/enum.c +++ b/drivers/ub/ubus/enum.c @@ -5,10 +5,13 @@ #define pr_fmt(fmt) "ubus enum: " fmt +#include + #include "ubus.h" #include "msg.h" #include "port.h" #include "cna.h" +#include "route.h" #include "enum.h" #define ENUM_MAX_HOPS 255 @@ -803,3 +806,187 @@ static int ub_enum_na_cfg(struct ub_entity *uent, void *buf) ub_cna_free(uent); return ret; } + + +/* + * Routing Algorithm: The BFS (Breadth First Search) algorithm + * calculates the shortest forwarding path. The steps are as follows: + * 1. Obtain the "nodes" and forwarding device information in the host + * scenario, and construct an interconnected network in the single host + * scenario. + * 2. Starting from each node in the interconnected network, sequentially + * visit the adjacent nodes that are directly connected with one hop, + * without considering weight/cost. + * 3. Visit the adjacent nodes of the adjacent nodes, traversing layer by + * layer. + * 4. Continue until reaching the destination or there are no unvisited nodes + * left, constructing the forwarding path with the minimum number of hops. + * 5. When the weights/costs of the links are not the same, the BFS algorithm + * may calculate non-shortest paths. + * 6. Based on the above mechanism, the BFS algorithm does not allow cycles. + */ + +struct bfs_port_path { + struct ub_port *s_port; /* source port for set cna_map */ + struct ub_entity *uent; /* BFS current ub entity */ +}; + +static struct bfs_route_info { + DECLARE_KFIFO_PTR(kfifo, struct bfs_port_path); + int *visited; /* 0: unvisited, 1: visited, 2: visiting */ + u32 cna_used; + u32 *cna_map; +} bfs_route; + +enum { BFS_UNVISITED, BFS_VISITED, BFS_VISITING }; + +/* Return: Whether to join the kfifo */ +static int bfs_route_test_and_put(struct ub_port *p, struct ub_entity *uent) +{ + struct bfs_port_path path; + + if (!is_switch(uent) && !is_ibus_controller(uent)) + return 0; + + if (uent->port_nums == 1) + return 0; + + path.s_port = p; + path.uent = uent; + + if (!kfifo_put(&bfs_route.kfifo, path)) { + pr_info("multiple paths join the kfifo, kfifo put failed\n"); + return 0; + } + + return 1; +} + +static int cna2index(int cna) +{ + int i; + + for (i = 0; i < bfs_route.cna_used; ++i) { + if (bfs_route.cna_map[i] == cna) + return i; + } + + return 0; +} + +static void ub_enum_bfs_layer_update(struct ub_entity *uent, int *curr, int *next, + int *layer) +{ + int i; + + *curr = *next; + *next = 0; + *layer += 1; + + for (i = 0; i < bfs_route.cna_used; ++i) + if (bfs_route.visited[i] == BFS_VISITING) + bfs_route.visited[i] = BFS_VISITED; +} + +static void ub_enum_bfs_route_dev(struct ub_entity *ldev) +{ + struct ub_entity *uent, *rdev; + struct ub_port *p, *rport, *s_port; + struct bfs_port_path path = { NULL, ldev }; + int curr_layer_devs = 1, next_layer_devs = 0, layer = 0; + + bfs_route.visited[cna2index(ldev->cna)] = BFS_VISITED; + kfifo_put(&bfs_route.kfifo, path); + while (kfifo_get(&bfs_route.kfifo, &path)) { + curr_layer_devs--; + uent = path.uent; + for_each_uent_port(p, uent) { + if (!p->cna || !p->r_uent || + bfs_route.visited[cna2index(p->r_uent->cna)] == + BFS_VISITED || + !ub_entity_test_priv_flag(p->r_uent, UB_ENTITY_DETACHED)) + continue; + + rdev = p->r_uent; + rport = rdev->ports + p->r_index; + s_port = path.s_port ? path.s_port : p; + + bfs_route.visited[cna2index(rdev->cna)] = BFS_VISITING; + ub_route_add_entry(s_port, rdev->cna, layer + 1); + if (!ONE_CNA(rdev)) + ub_route_add_entry(s_port, rport->cna, layer + 1); + ub_entity_assign_priv_flag(ldev, UB_ENTITY_ROUTE_UPDATED, true); + next_layer_devs += bfs_route_test_and_put(s_port, rdev); + } + + if (curr_layer_devs == 0) + ub_enum_bfs_layer_update(uent, &curr_layer_devs, + &next_layer_devs, &layer); + } +} + +static void ub_enum_bfs_route_core(struct list_head *dev_list) +{ + struct ub_entity *uent; + + list_for_each_entry(uent, dev_list, node) { + if (uent->port_nums == 1) + continue; + + memset(bfs_route.visited, 0, sizeof(int) * bfs_route.cna_used); + + ub_enum_bfs_route_dev(uent); + } +} + +static void ub_bfs_set_cna_map(struct list_head *dev_list) +{ + struct ub_port *port; + struct ub_entity *uent; + int i = 0; + + list_for_each_entry(uent, dev_list, node) { + bfs_route.cna_map[i++] = uent->cna; + if (ONE_CNA(uent)) + continue; + for_each_uent_port(port, uent) + bfs_route.cna_map[i++] = port->cna; + } +} + +int ub_enum_bfs_route_cal(struct list_head *dev_list) +{ + u16 max_port_num = 0; + struct ub_entity *uent; + int ret = -ENOMEM; + + if (kfifo_alloc(&bfs_route.kfifo, SZ_4K, GFP_KERNEL)) + goto kfifo_fail; + + bfs_route.cna_used = 0; + list_for_each_entry(uent, dev_list, node) { + if (max_port_num < uent->port_nums) + max_port_num = uent->port_nums; + bfs_route.cna_used += ONE_CNA(uent) ? 1 : uent->port_nums + 1; + } + + bfs_route.cna_map = kcalloc(bfs_route.cna_used, sizeof(u32), GFP_KERNEL); + if (!bfs_route.cna_map) + goto map_fail; + ub_bfs_set_cna_map(dev_list); + + bfs_route.visited = kcalloc(bfs_route.cna_used, sizeof(int), GFP_KERNEL); + if (!bfs_route.visited) + goto visited_fail; + + ub_enum_bfs_route_core(dev_list); + ret = 0; + + kfree(bfs_route.visited); +visited_fail: + kfree(bfs_route.cna_map); +map_fail: + kfifo_free(&bfs_route.kfifo); +kfifo_fail: + return ret; +} diff --git a/drivers/ub/ubus/enum.h b/drivers/ub/ubus/enum.h index 837197321ed3..2f5fec228f1a 100644 --- a/drivers/ub/ubus/enum.h +++ b/drivers/ub/ubus/enum.h @@ -182,6 +182,7 @@ struct enum_tlv_cap_info { #define ENUM_TLV_CAP_INFO_SZ 8 size_t calc_enum_pld_header_size(struct enum_pld_scan_header *header, bool req); +int ub_enum_bfs_route_cal(struct list_head *uent_list); int ub_query_port_na(struct ub_entity *uent, void *buf); int ub_query_ent_na(struct ub_entity *uent, void *buf); bool ub_type_valid(struct ub_entity *uent, bool is_ctl); diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index 3f0812e77212..3bd02c9ea4a3 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -38,6 +38,7 @@ enum ub_entity_type { #define UB_COMPACT_EID_MASK GENMASK(19, 0) /* ub_entity priv_flags */ +#define UB_ENTITY_DETACHED 1 /* Flag indicate uent is detached */ #define UB_ENTITY_ROUTE_UPDATED 2 /* Flag indicate uent's route is updated */ static inline void ub_entity_assign_priv_flag(struct ub_entity *uent, int bit, bool flag) diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 6e199a68c202..f16614ec7e78 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -145,6 +145,7 @@ struct ub_entity { u32 token_value; /* entity topology info */ + struct list_head node; struct ub_bus_controller *ubc; struct ub_entity *pue; /* ue/mue connected to their mue */ int topo_rank; /* The levels of Breadth-First Search */ -- Gitee From 7615de5e112b30ece9f5c05d3ecfe0a37b18f573 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 17:52:34 +0800 Subject: [PATCH 024/214] ub:ubus: Support for UB Bus Controller Enumeration ANBZ: #45460 commit 1a64cb7c7288cac262e7b3fd8d4dcd49b8a5bde5 openEuler. Perform a topological scan of the network using breadth-first search, and currently implement enumeration of the UB Bus Controller. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/enum.c | 222 ++++++++++++++++++++++++++++++++++ drivers/ub/ubus/ubus_entity.c | 14 +++ drivers/ub/ubus/ubus_entity.h | 11 ++ 4 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/ubus_entity.c create mode 100644 drivers/ub/ubus/ubus_entity.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 6b61b611ee22..906372a5be99 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -3,6 +3,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o -ubus-y += enum.o resource.o +ubus-y += enum.o resource.o ubus_entity.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/enum.c b/drivers/ub/ubus/enum.c index 50d00cbdf486..04e42b62e81f 100644 --- a/drivers/ub/ubus/enum.c +++ b/drivers/ub/ubus/enum.c @@ -8,6 +8,8 @@ #include #include "ubus.h" +#include "ubus_inner.h" +#include "ubus_entity.h" #include "msg.h" #include "port.h" #include "cna.h" @@ -990,3 +992,223 @@ int ub_enum_bfs_route_cal(struct list_head *dev_list) kfifo_fail: return ret; } + +int ub_cfg_read_guid(struct ub_entity *uent) +{ + u32 val = 0; + int i, ret; + + for (i = 0; i < UB_GUID_DW_NUM; i++) { + ret = ub_cfg_read_dword(uent, UB_GUID + i * sizeof(u32), &val); + if (ret) + return ret; + + uent->guid.dw[i] = val; + } + + return 0; +} + +static void ub_enum_destroy_entity(struct ub_entity *uent) +{ + message_remove_device(uent); + + if (is_primary(uent)) + ub_ubc_put(uent->ubc); + + kfree(uent); +} + +static struct ub_entity *ub_enum_create_uent(struct ub_bus_controller *ubc) +{ + struct ub_entity *uent; + int ret; + + uent = ub_alloc_ent(); + if (!uent) + return NULL; + + uent->pue = uent; + uent->entity_idx = 0; + uent->ubc = ub_ubc_get(ubc); + uent->upi = UB_CP_UPI; + ret = message_probe_device(uent); + if (ret) { + dev_err(&ubc->dev, "enum msg probe dev failed, ret=%d\n", ret); + goto err_out; + } + + return uent; +err_out: + ub_ubc_put(ubc); + kfree(uent); + return NULL; +} + +static struct ub_entity *ub_enum_create_bus_controller(struct ub_bus_controller *ubc) +{ + struct ub_entity *uent; + int ret; + + uent = ub_enum_create_uent(ubc); + if (!uent) + return (struct ub_entity *)ERR_PTR(-ENOMEM); + + ubc->uent = uent; + ret = ub_cfg_read_guid(uent); + if (ret) { + dev_err(&ubc->dev, "read guid failed, ret=%d\n", ret); + goto err_out; + } + + uent->topo_rank = 0; + uent->dev.parent = &ubc->dev; + + return uent; +err_out: + ub_enum_destroy_entity(uent); + return (struct ub_entity *)ERR_PTR(ret); +} + +static void ub_enum_topo_ent_uninit(struct ub_entity *uent) +{ + if (is_primary(uent)) { + ub_route_clear(uent); /* alloc in route_config */ + ub_cna_free(uent); /* alloc in na_cfg */ + ub_ports_unset(uent); /* alloc in scan_device */ + } +} + +static int ub_enum_and_configure_ent(struct ub_entity *uent, void *buf) +{ + int ret; + + ret = ub_enum_ent(uent, buf); + if (ret) + return ret; + + ret = ub_enum_na_cfg(uent, buf); + if (ret) + ub_ports_unset(uent); + + return ret; +} + +void ub_enum_clear_ent_list(struct list_head *dev_list) +{ + struct ub_entity *uent, *tmp; + struct ub_port *port; + + list_for_each_entry_safe_reverse(uent, tmp, dev_list, node) { + list_del(&uent->node); + for_each_uent_port(port, uent) + ub_port_disconnect(port); + + ub_cna_free(uent); + ub_ports_unset(uent); + ub_enum_destroy_entity(uent); + } +} + +static int ub_enum_bus_controllers(struct list_head *dev_list) +{ + struct ub_bus_controller *ubc; + struct ub_entity *uent; + int ret; + + list_for_each_entry(ubc, &ubc_list, node) { + uent = ub_enum_create_bus_controller(ubc); + if (IS_ERR(uent)) { + ret = PTR_ERR(uent); + dev_err(&ubc->dev, "create controller failed, ret=%d\n", + ret); + goto clear_list; + } + + ret = ub_enum_and_configure_ent(uent, topo_scan.buf); + if (ret) { + dev_err(&ubc->dev, "enum controller failed, ret=%d\n", + ret); + ub_enum_destroy_entity(uent); + goto clear_list; + } + + list_add_tail(&uent->node, dev_list); + } + + return 0; +clear_list: + ub_enum_clear_ent_list(dev_list); + return ret; +} + +/* + * During topo scan, just alloc ub_entity, alloc ub_port, alloc cna, + * so, when topo scan failed, just go to free devs in topo_scan.uents + */ +static int ub_enum_topo_scan(struct list_head *dev_list) +{ + int ret; + + ret = ub_enum_topo_scan_init(); + if (ret) { + pr_err("enum topo scan init failed, ret=%d\n", ret); + return ret; + } + + ret = ub_enum_bus_controllers(dev_list); + if (ret) { + pr_err("enum create controllers failed, ret=%d\n", ret); + goto out; + } + + if (list_empty(dev_list)) { + pr_warn("No ub bus controller exists in the current environment.\n"); + ret = -ENODEV; + goto out; + } + +out: + ub_enum_topo_scan_uninit(); + return ret; +} + +static void ub_enum_free_all(void) +{ + struct ub_entity *uent, *tmp; + + list_for_each_entry_safe_reverse(uent, tmp, &topo_scan.dev_list, node) { + list_del(&uent->node); + ub_enum_topo_ent_uninit(uent); + ub_enum_destroy_entity(uent); + } +} + +int ub_enum_probe(void) +{ + int ret; + + ret = ub_enum_topo_scan(&topo_scan.dev_list); + if (ret == -ENODEV) { + return 0; + } else if (ret) { + pr_err("topo_scan failed, ret=%d\n", ret); + return ret; + } + + ret = ub_enum_bfs_route_cal(&topo_scan.dev_list); + if (ret) { + pr_err("route cal failed, ret=%d\n", ret); + goto err_out; + } + + return 0; +err_out: + ub_enum_free_all(); + return ret; +} + +void ub_enum_remove(void) +{ + ub_enum_free_all(); +} diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c new file mode 100644 index 000000000000..eb773c8affeb --- /dev/null +++ b/drivers/ub/ubus/ubus_entity.c @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus entity: " fmt + +#include "ubus.h" + +struct ub_entity *ub_alloc_ent(void) +{ + return NULL; +} +EXPORT_SYMBOL_GPL(ub_alloc_ent); diff --git a/drivers/ub/ubus/ubus_entity.h b/drivers/ub/ubus/ubus_entity.h new file mode 100644 index 000000000000..b9e4e57ce1a7 --- /dev/null +++ b/drivers/ub/ubus/ubus_entity.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __UBUS_ENTITY_H__ +#define __UBUS_ENTITY_H__ + +struct ub_entity *ub_alloc_ent(void); + +#endif /* __UBUS_ENTITY_H__ */ -- Gitee From 0ca184ddcb48e35893ff7a9503f4e490c227e13e Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 18:01:13 +0800 Subject: [PATCH 025/214] ub:ubus: Supports device enumeration. ANBZ: #45460 commit e7af929fc0444491fa5f13a00c3f576ea5a4b923 openEuler. Enumerate all devices within the network, enable them, and add them to the system. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/enum.c | 178 +++++++++++++++++++++++++++++++++- drivers/ub/ubus/enum.h | 2 + drivers/ub/ubus/ubus_entity.c | 24 +++++ drivers/ub/ubus/ubus_entity.h | 5 + 4 files changed, 208 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubus/enum.c b/drivers/ub/ubus/enum.c index 04e42b62e81f..0fc76ac81502 100644 --- a/drivers/ub/ubus/enum.c +++ b/drivers/ub/ubus/enum.c @@ -1142,6 +1142,144 @@ static int ub_enum_bus_controllers(struct list_head *dev_list) return ret; } +static struct ub_entity *ub_enum_get_ent(guid_t *guid, struct list_head *dev_list) +{ + struct ub_entity *uent; + + list_for_each_entry(uent, dev_list, node) + if (guid_equal(guid, &uent->guid.id)) + return uent; + + return NULL; +} + +static bool ub_enum_port_recognise_check(struct ub_entity *root, + struct ub_port *port) +{ + struct ub_port *tmp; + + if (guid_equal(&port->r_guid, &root->guid.id)) { + tmp = root->ports + port->r_index; + if (guid_is_null(&tmp->r_guid)) { + port->r_index = 0; + guid_copy(&port->r_guid, &guid_null); + return true; + } + } + + return false; +} + +static bool port_need_scan(struct ub_entity *root, struct ub_port *port) +{ + if (port->r_uent) + return false; + + if (guid_is_null(&port->r_guid)) + return false; + + if (root && ub_enum_port_recognise_check(root, port)) + return false; + + return true; +} + +static struct ub_entity *ub_enum_create_entity(guid_t *guid, struct ub_entity *parent) +{ + struct ub_entity *uent; + + if (parent->topo_rank + 1 > ENUM_MAX_HOPS) { + dev_err(&parent->ubc->dev, "ub support max hops: %d\n", + ENUM_MAX_HOPS); + return (struct ub_entity *)ERR_PTR(-EINVAL); + } + + uent = ub_enum_create_uent(parent->ubc); + if (!uent) + return (struct ub_entity *)ERR_PTR(-ENOMEM); + + guid_copy(&uent->guid.id, guid); + + uent->topo_rank = parent->topo_rank + 1; + uent->dev.parent = &parent->dev; + + return uent; +} + +static int ub_enum_do_topo_scan(struct ub_entity *root, struct list_head *dev_list, + void *buf) +{ +#define UB_TOPO_KFIFO_DEPTH SZ_128 + DECLARE_KFIFO(kfifo, struct ub_entity *, UB_TOPO_KFIFO_DEPTH); + struct ub_entity *uent, *r_uent; + struct ub_port *port; + struct device *dev; + int ret = 0; + + INIT_KFIFO(kfifo); + + if (root) + kfifo_put(&kfifo, root); + + list_for_each_entry(uent, dev_list, node) + kfifo_put(&kfifo, uent); + + while (kfifo_get(&kfifo, &uent)) { + dev = &uent->ubc->dev; + + for_each_uent_port(port, uent) { + if (!port_need_scan(root, port)) + continue; + + r_uent = ub_enum_get_ent(&port->r_guid, dev_list); + if (r_uent) + goto connect_port; + + if (is_device(uent)) { + ret = -EINVAL; + dev_err(dev, "device connect to multi peer\n"); + goto clear_list; + } + + r_uent = ub_enum_create_entity(&port->r_guid, uent); + if (IS_ERR(r_uent)) { + ret = PTR_ERR(r_uent); + dev_err(dev, "enum create device failed, ret=%d\n", + ret); + goto clear_list; + } + + /* set here to help enum find path */ + port->r_uent = r_uent; + ret = ub_enum_and_configure_ent(r_uent, buf); + if (ret) { + dev_err(dev, "enum device failed, ret=%d\n", + ret); + ub_enum_destroy_entity(r_uent); + goto clear_port; + } + + list_add_tail(&r_uent->node, dev_list); + if (!kfifo_put(&kfifo, r_uent)) + dev_err(dev, "do topo scan kfifo put full\n"); + +connect_port: + if (!ub_check_and_connect(port, r_uent)) { + dev_err(dev, "port%u wrong topo\n", port->index); + ret = -EINVAL; + goto clear_port; + } + } + } + + return 0; +clear_port: + port->r_uent = NULL; +clear_list: + ub_enum_clear_ent_list(dev_list); + return ret; +} + /* * During topo scan, just alloc ub_entity, alloc ub_port, alloc cna, * so, when topo scan failed, just go to free devs in topo_scan.uents @@ -1168,6 +1306,10 @@ static int ub_enum_topo_scan(struct list_head *dev_list) goto out; } + ret = ub_enum_do_topo_scan(NULL, dev_list, topo_scan.buf); + if (ret) + pr_err("enum scan devices failed, ret=%d\n", ret); + out: ub_enum_topo_scan_uninit(); return ret; @@ -1182,6 +1324,33 @@ static void ub_enum_free_all(void) ub_enum_topo_ent_uninit(uent); ub_enum_destroy_entity(uent); } + + ub_stop_entities(); + ub_remove_entities(); +} + +int ub_enum_entities_active(struct list_head *dev_list) +{ + struct ub_entity *uent, *tmp; + int ret; + + list_for_each_entry_safe(uent, tmp, dev_list, node) { + if (uent->entity_idx != 0) + continue; + + ub_route_sync_dev(uent); + ret = ub_setup_ent(uent); + if (ret) { + pr_err("setup dev err, ret=%d\n", ret); + return ret; + } + + list_del(&uent->node); + ub_entity_add(uent, uent->ubc); + ub_start_ent(uent); + } + + return 0; } int ub_enum_probe(void) @@ -1202,6 +1371,12 @@ int ub_enum_probe(void) goto err_out; } + ret = ub_enum_entities_active(&topo_scan.dev_list); + if (ret) { + pr_err("devices start failed, ret=%d\n", ret); + goto err_out; + } + return 0; err_out: ub_enum_free_all(); @@ -1210,5 +1385,6 @@ int ub_enum_probe(void) void ub_enum_remove(void) { - ub_enum_free_all(); + ub_stop_entities(); + ub_remove_entities(); } diff --git a/drivers/ub/ubus/enum.h b/drivers/ub/ubus/enum.h index 2f5fec228f1a..554d503d5d51 100644 --- a/drivers/ub/ubus/enum.h +++ b/drivers/ub/ubus/enum.h @@ -185,6 +185,8 @@ size_t calc_enum_pld_header_size(struct enum_pld_scan_header *header, bool req); int ub_enum_bfs_route_cal(struct list_head *uent_list); int ub_query_port_na(struct ub_entity *uent, void *buf); int ub_query_ent_na(struct ub_entity *uent, void *buf); +int ub_enum_probe(void); +void ub_enum_remove(void); bool ub_type_valid(struct ub_entity *uent, bool is_ctl); void ub_entity_type_init(struct ub_entity *uent); diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index eb773c8affeb..258143c309e3 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -12,3 +12,27 @@ struct ub_entity *ub_alloc_ent(void) return NULL; } EXPORT_SYMBOL_GPL(ub_alloc_ent); + +int ub_setup_ent(struct ub_entity *uent) +{ + return -EINVAL; +} +EXPORT_SYMBOL_GPL(ub_setup_ent); + +void ub_entity_add(struct ub_entity *uent, void *ctx) +{ +} +EXPORT_SYMBOL_GPL(ub_entity_add); + +void ub_start_ent(struct ub_entity *uent) +{ +} +EXPORT_SYMBOL_GPL(ub_start_ent); + +void ub_stop_entities(void) +{ +} + +void ub_remove_entities(void) +{ +} diff --git a/drivers/ub/ubus/ubus_entity.h b/drivers/ub/ubus/ubus_entity.h index b9e4e57ce1a7..5cb3f020e4e6 100644 --- a/drivers/ub/ubus/ubus_entity.h +++ b/drivers/ub/ubus/ubus_entity.h @@ -7,5 +7,10 @@ #define __UBUS_ENTITY_H__ struct ub_entity *ub_alloc_ent(void); +int ub_setup_ent(struct ub_entity *uent); +void ub_entity_add(struct ub_entity *uent, void *ctx); +void ub_start_ent(struct ub_entity *uent); +void ub_stop_entities(void); +void ub_remove_entities(void); #endif /* __UBUS_ENTITY_H__ */ -- Gitee From b7555000a35546370608d674d7639143c93679dc Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 18:39:36 +0800 Subject: [PATCH 026/214] ub:ubus: Support UB device enabling basic interface ANBZ: #45460 commit 8ae16d21707c9299ad950c2511508a3d3407de7a openEuler. Implementing the basic configuration for entity0 initialization and de-initialization. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/enum.h | 1 + drivers/ub/ubus/sysfs.c | 4 + drivers/ub/ubus/sysfs.h | 1 + drivers/ub/ubus/ubus.h | 4 + drivers/ub/ubus/ubus_driver.c | 2 + drivers/ub/ubus/ubus_driver.h | 11 ++ drivers/ub/ubus/ubus_entity.c | 315 +++++++++++++++++++++++++++++++++- drivers/ub/ubus/ubus_entity.h | 1 + include/ub/ubus/ubus.h | 28 +++ 9 files changed, 365 insertions(+), 2 deletions(-) create mode 100644 drivers/ub/ubus/ubus_driver.h diff --git a/drivers/ub/ubus/enum.h b/drivers/ub/ubus/enum.h index 554d503d5d51..2ab04e0f705f 100644 --- a/drivers/ub/ubus/enum.h +++ b/drivers/ub/ubus/enum.h @@ -182,6 +182,7 @@ struct enum_tlv_cap_info { #define ENUM_TLV_CAP_INFO_SZ 8 size_t calc_enum_pld_header_size(struct enum_pld_scan_header *header, bool req); +int ub_cfg_read_guid(struct ub_entity *uent); int ub_enum_bfs_route_cal(struct list_head *uent_list); int ub_query_port_na(struct ub_entity *uent, void *buf); int ub_query_ent_na(struct ub_entity *uent, void *buf); diff --git a/drivers/ub/ubus/sysfs.c b/drivers/ub/ubus/sysfs.c index 84d32e36249b..eca8d3f7f58c 100644 --- a/drivers/ub/ubus/sysfs.c +++ b/drivers/ub/ubus/sysfs.c @@ -143,3 +143,7 @@ const struct attribute_group *ub_bus_groups[] = { &ub_bus_group, NULL }; + +const struct device_type ub_dev_type = { + .groups = NULL, +}; diff --git a/drivers/ub/ubus/sysfs.h b/drivers/ub/ubus/sysfs.h index 8c8fc5d3fa72..bbd4d1b49dbc 100644 --- a/drivers/ub/ubus/sysfs.h +++ b/drivers/ub/ubus/sysfs.h @@ -7,5 +7,6 @@ extern const struct attribute_group *ub_entity_groups[]; extern const struct attribute_group *ub_bus_groups[]; +extern const struct device_type ub_dev_type; #endif /* __SYSFS_H__ */ diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index 3bd02c9ea4a3..b2463fccee7a 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -36,10 +36,14 @@ enum ub_entity_type { #define ONE_CNA(uent) (is_switch(uent) || (uent)->port_nums == 1) #define UB_COMPACT_EID_MASK GENMASK(19, 0) +#define UB_UPI_MASK GENMASK(14, 0) /* ub_entity priv_flags */ +#define UB_ENTITY_START 0 /* Flag indicate uent can match with driver */ #define UB_ENTITY_DETACHED 1 /* Flag indicate uent is detached */ #define UB_ENTITY_ROUTE_UPDATED 2 /* Flag indicate uent's route is updated */ +#define UB_ENTITY_SETUP 4 /* Flag indicate uent is setup */ + static inline void ub_entity_assign_priv_flag(struct ub_entity *uent, int bit, bool flag) { diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index 1ad798c2aa84..9e504806fd33 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -19,6 +19,8 @@ #include "ubus_controller.h" #include "ubus_inner.h" +DECLARE_RWSEM(ub_bus_sem); + static DEFINE_MUTEX(manage_subsystem_ops_mutex); static const struct ub_manage_subsystem_ops *manage_subsystem_ops; diff --git a/drivers/ub/ubus/ubus_driver.h b/drivers/ub/ubus/ubus_driver.h new file mode 100644 index 000000000000..6a259d224942 --- /dev/null +++ b/drivers/ub/ubus/ubus_driver.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __UBUS_DRIVER_H__ +#define __UBUS_DRIVER_H__ + +extern struct rw_semaphore ub_bus_sem; + +#endif /* __UBUS_DRIVER_H__ */ diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index 258143c309e3..544252c790bf 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -5,30 +5,341 @@ #define pr_fmt(fmt) "ubus entity: " fmt +#include + #include "ubus.h" +#include "sysfs.h" +#include "msg.h" +#include "enum.h" +#include "route.h" +#include "port.h" +#include "eid.h" +#include "cna.h" +#include "resource.h" +#include "ubus_controller.h" +#include "ubus_driver.h" +#include "ubus_inner.h" + +/* + * Entity lifecycle + * + * init process: {ub_alloc_ent -> ub_setup_ent -> ub_entity_add -> ub_start_ent} + * + * uninit process: {ub_stop_ent -> ub_remove_ent} + */ + +#define UENT_NUM_START 1 +#define UENT_NUM_END 0xfffff +#define UB_DEFAULT_MAX_SEG_SIZE SZ_64K struct ub_entity *ub_alloc_ent(void) { - return NULL; + struct ub_entity *uent; + + uent = kzalloc(sizeof(*uent), GFP_KERNEL); + if (!uent) + return NULL; + + INIT_LIST_HEAD(&uent->node); + INIT_LIST_HEAD(&uent->cna_list); + + uent->dev.type = &ub_dev_type; + uent->cna = 0; + + ub_entity_assign_priv_flag(uent, UB_ENTITY_DETACHED, true); + + return uent; } EXPORT_SYMBOL_GPL(ub_alloc_ent); -int ub_setup_ent(struct ub_entity *uent) +static DEFINE_IDA(uent_num_ida); +void ub_entity_num_free(struct ub_entity *uent) +{ + ida_free(&uent_num_ida, uent->uent_num); +} + +static int ub_entity_num_alloc(void) +{ + return ida_alloc_range(&uent_num_ida, UENT_NUM_START, UENT_NUM_END, + GFP_KERNEL); +} + +static int ub_get_guid(struct ub_entity *uent) +{ + return 0; +} + +static void ub_config_upi(struct ub_entity *uent) +{ + struct device *dev; + int ret; + u16 upi; + + if (is_ibus_controller(uent) && uent->ubc->cluster) { + dev = &uent->ubc->dev; + ret = ub_cfg_read_word(uent, UB_UPI, &upi); + if (ret) { + dev_err(dev, "update cluster upi failed, ret=%d\n", ret); + return; + } + + upi &= UB_UPI_MASK; + if (upi) { + dev_info(dev, "update cluster ubc upi, upi=%#x\n", upi); + uent->upi = upi; + } + return; + } + + ret = ub_cfg_write_dword(uent, UB_UPI, uent->upi); + if (ret) + ub_err(uent, "cfg upi failed, ret=%d\n", ret); +} + +static int ub_setup_ent_primary(struct ub_entity *uent) { return -EINVAL; } + +static int ub_uent_cfg(struct ub_entity *uent, u32 uent_num) +{ + struct ub_guid *guid = &uent->guid; + char buf[SZ_64] = {}; + + dev_set_name(&uent->dev, "%05x", uent_num); + uent->uent_num = uent_num; + + (void)ub_show_guid(guid, buf); + ub_info(uent, "guid=%s, uent_num=%#05x\n", buf, uent_num); + + uent->dev.bus = &ub_bus_type; + /* Card driver set to 64bit if support */ + uent->dma_mask = GENMASK(31, 0); + + return ub_setup_ent_primary(uent); +} + +static void ub_config_eid(struct ub_entity *uent) +{ + if (is_ibus_controller(uent) && uent->ubc->cluster) + return; + + ub_cfg_write_dword(uent, UB_EID_0, uent->eid); +} + +static void ub_set_fm_info(struct ub_entity *uent) +{ + if (uent->entity_idx) + return; + + if (uent->ubc->cluster && !is_idev(uent)) + return; + + ub_cfg_write_dword(uent, UB_FM_CNA, uent->ubc->uent->cna); + ub_cfg_write_dword(uent, UB_FM_EID_0, uent->ubc->uent->eid); +} + +static void ub_get_module_id(struct ub_entity *uent) +{ + int ret; + u32 val; + + ret = ub_cfg_read_dword(uent, UB_MODULE, &val); + if (ret) { + ub_err(uent, "Get dev module failed %d\n", ret); + return; + } + + uent->mod_vendor = val >> SZ_16; + uent->module = val & UB_MODULE_ID_MASK; +} + +int ub_setup_ent(struct ub_entity *uent) +{ + int ret, uent_num; + + if (!uent) + return -EINVAL; + + ret = message_probe_device(uent); + if (ret) { + ub_err(uent, "probe message failed, ret=%d\n", ret); + return ret; + } + + ret = ub_get_guid(uent); + if (ret) { + ub_err(uent, "get guid failed, ret=%d\n", ret); + goto err_alloc; + } + + ub_config_upi(uent); + + /* common setup */ + ret = ub_eid_alloc(uent); + if (ret) { + ub_err(uent, "alloc eid failed, ret=%d\n", ret); + goto err_alloc; + } + + uent_num = ub_entity_num_alloc(); + if (uent_num < 0) { + ub_err(uent, "alloc dev uent_num failed, ret=%d\n", uent_num); + goto free_eid; + } + + ret = ub_uent_cfg(uent, (u32)uent_num); + if (ret) + goto free_uent_num; + + ub_config_eid(uent); + ub_set_fm_info(uent); + ub_get_module_id(uent); + + ub_entity_assign_priv_flag(uent, UB_ENTITY_SETUP, true); + return 0; +free_uent_num: + ub_entity_num_free(uent); +free_eid: + ub_eid_free(uent); +err_alloc: + message_remove_device(uent); + return ret; +} EXPORT_SYMBOL_GPL(ub_setup_ent); +static void ub_configure_ent(struct ub_entity *uent) +{ +} + +static void ub_unconfigure_ent(struct ub_entity *uent) +{ +} + +static void ub_release_ent(struct device *dev); void ub_entity_add(struct ub_entity *uent, void *ctx) { + struct ub_bus_controller *ubc; + struct list_head *list; + int ret, node; + + if (!uent || !ctx) + return; + + ret = ub_entity_setup_mmio(uent); + WARN_ON(ret); + + ub_configure_ent(uent); + + device_initialize(&uent->dev); + uent->dev.release = ub_release_ent; + + uent->dev.dma_mask = &uent->dma_mask; + uent->dev.dma_parms = &uent->dma_parms; + uent->dev.coherent_dma_mask = GENMASK_ULL(31, 0); + dma_set_max_seg_size(&uent->dev, UB_DEFAULT_MAX_SEG_SIZE); + dma_set_seg_boundary(&uent->dev, GENMASK(31, 0)); + + ubc = (struct ub_bus_controller *)ctx; + node = ub_ubc_to_node(ubc); + list = &ubc->devs; + + set_dev_node(&uent->dev, node); + ub_entity_assign_priv_flag(uent, UB_ENTITY_DETACHED, false); + + down_write(&ub_bus_sem); + list_add_tail(&uent->node, list); + up_write(&ub_bus_sem); + + uent->match_driver = false; + ret = device_add(&uent->dev); + WARN_ON(ret < 0); + + ret = ub_ports_add(uent); + WARN_ON(ret); } EXPORT_SYMBOL_GPL(ub_entity_add); void ub_start_ent(struct ub_entity *uent) { + int ret; + + if (!uent) + return; + + uent->match_driver = true; + ret = device_attach(&uent->dev); + if (ret < 0 && ret != -EPROBE_DEFER) + ub_err(uent, "device attach failed, ret=%d\n", ret); + + ub_entity_assign_priv_flag(uent, UB_ENTITY_START, true); } EXPORT_SYMBOL_GPL(ub_start_ent); +static void ub_release_ent(struct device *dev) +{ + struct ub_entity *uent; + + uent = to_ub_entity(dev); + + ub_route_clear(uent); + ub_cna_free(uent); + ub_ports_unset(uent); + + message_remove_device(uent); + ub_ubc_put(uent->ubc); + + kfree(uent->driver_override); + uent->token_value = 0; + kfree(uent); + pr_info("uent release\n"); +} + +void ub_stop_ent(struct ub_entity *uent) +{ + if (!uent) + return; + + if (!ub_entity_test_priv_flag(uent, UB_ENTITY_START)) + return; + ub_entity_assign_priv_flag(uent, UB_ENTITY_START, false); + + device_release_driver(&uent->dev); + uent->match_driver = false; +} +EXPORT_SYMBOL_GPL(ub_stop_ent); + +void ub_remove_ent(struct ub_entity *uent) +{ + if (!uent->dev.kobj.parent) + return; + + ub_ports_del(uent); + + device_del(&uent->dev); + down_write(&ub_bus_sem); + list_del(&uent->node); + up_write(&ub_bus_sem); + + ub_unconfigure_ent(uent); + ub_entity_unset_mmio(uent); + ub_entity_num_free(uent); + ub_eid_free(uent); + ub_entity_assign_priv_flag(uent, UB_ENTITY_SETUP, false); + + put_device(&uent->dev); +} + +void ub_stop_and_remove_ent(struct ub_entity *uent) +{ + if (!uent) + return; + + ub_stop_ent(uent); + ub_remove_ent(uent); +} +EXPORT_SYMBOL_GPL(ub_stop_and_remove_ent); + void ub_stop_entities(void) { } diff --git a/drivers/ub/ubus/ubus_entity.h b/drivers/ub/ubus/ubus_entity.h index 5cb3f020e4e6..811edacf6db8 100644 --- a/drivers/ub/ubus/ubus_entity.h +++ b/drivers/ub/ubus/ubus_entity.h @@ -10,6 +10,7 @@ struct ub_entity *ub_alloc_ent(void); int ub_setup_ent(struct ub_entity *uent); void ub_entity_add(struct ub_entity *uent, void *ctx); void ub_start_ent(struct ub_entity *uent); +void ub_remove_ent(struct ub_entity *uent); void ub_stop_entities(void); void ub_remove_entities(void); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index f16614ec7e78..5ecbb16224de 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -154,6 +154,10 @@ struct ub_entity { u16 port_nums; struct ub_port *ports; + /* entity DMA info */ + u64 dma_mask; + struct device_dma_parameters dma_parms; + /* entity route info */ struct list_head cna_list; /* store distance for cna in route table */ @@ -481,6 +485,28 @@ int __ub_register_driver(struct ub_driver *drv, struct module *owner, __ub_register_driver(driver, THIS_MODULE, KBUILD_MODNAME) void ub_unregister_driver(struct ub_driver *drv); +/** + * ub_stop_ent() - Stop the entity. + * @uent: UB entity. + * + * Call device_release_driver(), user can't use it again, if it's a mue, + * will stop all ues under it, if it's entity0, will stop all entity under it. + * + * Context: Any context. + */ +void ub_stop_ent(struct ub_entity *uent); + +/** + * ub_stop_and_remove_ent() - Stop and remove the entity from system. + * @uent: UB entity. + * + * Call device_release_driver() and device_unregister(), if it's a mue, + * will remove all ues under it, if it's entity0, will remove all entity under it. + * + * Context: Any context. + */ +void ub_stop_and_remove_ent(struct ub_entity *uent); + #else /* CONFIG_UB_UBUS is not enabled */ #define dev_is_ub(d) (false) static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) @@ -532,6 +558,8 @@ __ub_register_driver(struct ub_driver *drv, struct module *owner, static inline int ub_register_driver(struct ub_driver *drv) { return 0; } static inline void ub_unregister_driver(struct ub_driver *drv) {} +static inline void ub_stop_ent(struct ub_entity *uent) {} +static inline void ub_stop_and_remove_ent(struct ub_entity *uent) {} #endif /* CONFIG_UB_UBUS */ #endif /* _UB_UBUS_UBUS_H_ */ -- Gitee From 80c4efce4345aecd162ad2612f90e5fcc6dd2a6f Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 19:07:36 +0800 Subject: [PATCH 027/214] ub:ubus: Support for multiple mue enablement ANBZ: #45460 commit 4427c62157904b6816043ffe5fa811c16d7c91c4 openEuler. The UB device supports multiple entity models, and here we implement the enable/disable functionality for the mue. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/entity.c | 25 +++ drivers/ub/ubus/enum.c | 1 + drivers/ub/ubus/ubus_entity.c | 372 ++++++++++++++++++++++++++++++++-- drivers/ub/ubus/ubus_entity.h | 1 + include/ub/ubus/ubus.h | 33 +++ 6 files changed, 419 insertions(+), 15 deletions(-) create mode 100644 drivers/ub/ubus/entity.c diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 906372a5be99..cb195c0f5742 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ -obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o +obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o ubus-y += enum.o resource.o ubus_entity.o diff --git a/drivers/ub/ubus/entity.c b/drivers/ub/ubus/entity.c new file mode 100644 index 000000000000..5d91ee2c2c3e --- /dev/null +++ b/drivers/ub/ubus/entity.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include "ubus_inner.h" + +int ub_get_dst_eid(struct ub_entity *dev) +{ + struct ub_bus_controller *ubc; + u32 eid; + + if (!dev) + return -EINVAL; + + ubc = dev->ubc; + + if (!ubc) + return -ENODEV; + + eid = ubc->uent->eid; + + return eid; +} +EXPORT_SYMBOL_GPL(ub_get_dst_eid); diff --git a/drivers/ub/ubus/enum.c b/drivers/ub/ubus/enum.c index 0fc76ac81502..872bb0de1582 100644 --- a/drivers/ub/ubus/enum.c +++ b/drivers/ub/ubus/enum.c @@ -1030,6 +1030,7 @@ static struct ub_entity *ub_enum_create_uent(struct ub_bus_controller *ubc) uent->pue = uent; uent->entity_idx = 0; + uent->is_mue = 1; uent->ubc = ub_ubc_get(ubc); uent->upi = UB_CP_UPI; ret = message_probe_device(uent); diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index 544252c790bf..b1b41d10dbd2 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -41,11 +41,12 @@ struct ub_entity *ub_alloc_ent(void) return NULL; INIT_LIST_HEAD(&uent->node); + INIT_LIST_HEAD(&uent->mue_list); INIT_LIST_HEAD(&uent->cna_list); uent->dev.type = &ub_dev_type; uent->cna = 0; - + uent->tid = 0; /* default tid according to ummu */ ub_entity_assign_priv_flag(uent, UB_ENTITY_DETACHED, true); return uent; @@ -66,6 +67,27 @@ static int ub_entity_num_alloc(void) static int ub_get_guid(struct ub_entity *uent) { + u16 code; + int ret; + + if (is_primary(uent) && !uent->pool) + return 0; + + if (!uent->pool || uent_type(uent->pue) == UB_TYPE_ICONTROLLER) { + uent_type(uent) = uent_type(uent->pue); + ret = ub_cfg_read_guid(uent); + if (ret) + return ret; + } + + ret = ub_cfg_read_word(uent, UB_CLASS_CODE, &code); + if (ret) + return ret; + + uent->class_code = code; + ub_entity_type_init(uent); + if (!ub_type_valid(uent, false)) + return -EINVAL; return 0; } @@ -96,15 +118,82 @@ static void ub_config_upi(struct ub_entity *uent) ub_err(uent, "cfg upi failed, ret=%d\n", ret); } +static void ub_res_space_prepare(struct ub_entity *pue) +{ + u32 source_support_map[MAX_UB_RES_NUM] = { UB_ERS0S_SUPPORT, + UB_ERS1S_SUPPORT, + UB_ERS2S_SUPPORT }; + u32 support_feature = 0; + int i; + + if (ub_cfg_read_dword(pue, UB_CFG1_SUPPORT_FEATURE_L, &support_feature)) { + ub_err(pue, "read cfg1 support feature failed\n"); + return; + } + + if (is_idev(pue) || is_ibus_controller(pue)) { + if (!(support_feature & UB_UBBAS_SUPPORT)) + return; + + for (i = 0; i < MAX_UB_RES_NUM; i++) + if (support_feature & source_support_map[i]) + pue->zone[i].ubba_used = 1; + return; + } + + for (i = 0; i < MAX_UB_RES_NUM; i++) { + if (!(support_feature & source_support_map[i])) + continue; + + pue->zone[i].sa_used = 1; + if (is_p_device(pue)) + pue->zone[i].res.flags = IORESOURCE_MEM; + } +} + static int ub_setup_ent_primary(struct ub_entity *uent) { - return -EINVAL; + int ret; + + if (uent->ent_type == UB_ENT_UNKNOWN) { + ub_err(uent, "ignore unknown entity type, type=%#x, class=%#x\n", + uent_type(uent), uent_class(uent)); + return -EIO; + } + + ub_res_space_prepare(uent); + + if (is_p_device(uent)) + return 0; + + ret = ub_cfg_read_word(uent, UB_TOTAL_NUMBER_ENTITIES, + (u16 *)&uent->total_funcs); + if (ret) { + ub_err(uent, "get UB_TOTAL_NUMBER_ENTITIES failed, ret=%d\n", ret); + return ret; + } + + if (!uent->total_funcs) { + ub_err(uent, "total_funcs is zero\n"); + return -EINVAL; + } + /* Number of Entities presented to users, except Entity0. */ + uent->total_funcs -= 1; + + return 0; +} + +static int ub_setup_ent_normal(struct ub_entity *uent) +{ + uent->dev.parent = &uent->pue->dev; + return 0; } static int ub_uent_cfg(struct ub_entity *uent, u32 uent_num) { struct ub_guid *guid = &uent->guid; char buf[SZ_64] = {}; + int ret; dev_set_name(&uent->dev, "%05x", uent_num); uent->uent_num = uent_num; @@ -116,7 +205,12 @@ static int ub_uent_cfg(struct ub_entity *uent, u32 uent_num) /* Card driver set to 64bit if support */ uent->dma_mask = GENMASK(31, 0); - return ub_setup_ent_primary(uent); + if (is_primary(uent) || is_p_device(uent)) + ret = ub_setup_ent_primary(uent); + else + ret = ub_setup_ent_normal(uent); + + return ret; } static void ub_config_eid(struct ub_entity *uent) @@ -221,6 +315,7 @@ void ub_entity_add(struct ub_entity *uent, void *ctx) { struct ub_bus_controller *ubc; struct list_head *list; + struct ub_entity *pue; int ret, node; if (!uent || !ctx) @@ -240,9 +335,15 @@ void ub_entity_add(struct ub_entity *uent, void *ctx) dma_set_max_seg_size(&uent->dev, UB_DEFAULT_MAX_SEG_SIZE); dma_set_seg_boundary(&uent->dev, GENMASK(31, 0)); - ubc = (struct ub_bus_controller *)ctx; - node = ub_ubc_to_node(ubc); - list = &ubc->devs; + if (is_primary(uent) || is_p_device(uent)) { + ubc = (struct ub_bus_controller *)ctx; + node = ub_ubc_to_node(ubc); + list = &ubc->devs; + } else { + pue = (struct ub_entity *)ctx; + node = dev_to_node(&pue->dev); + list = &pue->mue_list; + } set_dev_node(&uent->dev, node); ub_entity_assign_priv_flag(uent, UB_ENTITY_DETACHED, false); @@ -255,11 +356,14 @@ void ub_entity_add(struct ub_entity *uent, void *ctx) ret = device_add(&uent->dev); WARN_ON(ret < 0); - ret = ub_ports_add(uent); - WARN_ON(ret); + if (is_primary(uent)) { + ret = ub_ports_add(uent); + WARN_ON(ret); + } } EXPORT_SYMBOL_GPL(ub_entity_add); +static int ub_mue_enable_and_map(struct ub_entity *uent); void ub_start_ent(struct ub_entity *uent) { int ret; @@ -272,6 +376,12 @@ void ub_start_ent(struct ub_entity *uent) if (ret < 0 && ret != -EPROBE_DEFER) ub_err(uent, "device attach failed, ret=%d\n", ret); + if (is_primary(uent) && !is_p_device(uent)) { + ret = ub_mue_enable_and_map(uent); + if (ret) + ub_err(uent, "enable pue failed, ret=%d\n", ret); + } + ub_entity_assign_priv_flag(uent, UB_ENTITY_START, true); } EXPORT_SYMBOL_GPL(ub_start_ent); @@ -281,13 +391,18 @@ static void ub_release_ent(struct device *dev) struct ub_entity *uent; uent = to_ub_entity(dev); - - ub_route_clear(uent); - ub_cna_free(uent); - ub_ports_unset(uent); + if (is_primary(uent) && !is_p_device(uent)) { + ub_route_clear(uent); + ub_cna_free(uent); + ub_ports_unset(uent); + } message_remove_device(uent); - ub_ubc_put(uent->ubc); + + if (is_primary(uent) || (is_p_device(uent) && uent->is_mue)) + ub_ubc_put(uent->ubc); + else + ub_entity_put(uent->pue); kfree(uent->driver_override); uent->token_value = 0; @@ -297,6 +412,8 @@ static void ub_release_ent(struct device *dev) void ub_stop_ent(struct ub_entity *uent) { + struct ub_entity *ent, *tmp; + if (!uent) return; @@ -304,6 +421,10 @@ void ub_stop_ent(struct ub_entity *uent) return; ub_entity_assign_priv_flag(uent, UB_ENTITY_START, false); + /* Stop mue in primary dev, when uent is entN, mue_list is NULL */ + list_for_each_entry_safe_reverse(ent, tmp, &uent->mue_list, node) + ub_stop_ent(ent); + device_release_driver(&uent->dev); uent->match_driver = false; } @@ -311,10 +432,17 @@ EXPORT_SYMBOL_GPL(ub_stop_ent); void ub_remove_ent(struct ub_entity *uent) { + struct ub_entity *ent, *tmp; + if (!uent->dev.kobj.parent) return; - ub_ports_del(uent); + /* Remove mue in primary dev, when uent is entN, mue_list is NULL */ + list_for_each_entry_safe_reverse(ent, tmp, &uent->mue_list, node) + ub_remove_ent(ent); + + if (is_primary(uent)) + ub_ports_del(uent); device_del(&uent->dev); down_write(&ub_bus_sem); @@ -342,8 +470,224 @@ EXPORT_SYMBOL_GPL(ub_stop_and_remove_ent); void ub_stop_entities(void) { + struct ub_bus_controller *ubc; + struct ub_entity *uent; + + list_for_each_entry_reverse(ubc, &ubc_list, node) + list_for_each_entry_reverse(uent, &ubc->devs, node) + ub_stop_ent(uent); } void ub_remove_entities(void) { + struct ub_bus_controller *ubc; + struct ub_entity *uent, *tmp; + + list_for_each_entry_reverse(ubc, &ubc_list, node) + list_for_each_entry_safe_reverse(uent, tmp, &ubc->devs, node) + ub_remove_ent(uent); +} + +struct entity_info_msg_pld_rsp { + u32 reserved; + u16 entity_nums; + u16 mue_nums; + struct ue_map map[]; +}; +#define ENTITY_INFO_BASE_PLD_SIZE 8 + +struct entity_info_msg_pld { + /* request payload is NULL */ + struct entity_info_msg_pld_rsp rsp; +}; + +struct entity_info_msg_pkt { + struct msg_pkt_header header; + struct entity_info_msg_pld pld; +}; + +static int ub_obtain_entity_info_rsp_handle(struct ub_entity *uent, + struct entity_info_msg_pkt *pkt, u16 *mue_nums, + struct ue_map *map) +{ + struct msg_extended_header *etah = &pkt->header.msgetah; + struct entity_info_msg_pld_rsp *rsp = &pkt->pld.rsp; + size_t size; + u16 i; + + if (etah->rsp_status != UB_MSG_RSP_SUCCESS) { + ub_err(uent, "obtain entity info rsp, status=%#02x\n", + etah->rsp_status); + return -EINVAL; + } + + if (etah->plen < ENTITY_INFO_BASE_PLD_SIZE) { + ub_err(uent, "obtain entity info plen=%#x invalid\n", + etah->plen); + return -EINVAL; + } + + uent->total_funcs = rsp->entity_nums; + *mue_nums = rsp->mue_nums; + + size = sizeof(struct ue_map) * (*mue_nums); + if (*mue_nums == 0 || size > SZ_1K) { + ub_err(uent, "mue_nums or size error, mue_nums=%#x, size=%#lx\n", + *mue_nums, size); + return -EINVAL; + } + + if (etah->plen != (*mue_nums * SZ_4 + ENTITY_INFO_BASE_PLD_SIZE)) { + ub_err(uent, "obtain entity info plen=%#x, mue_nums=%#x invalid\n", + etah->plen, *mue_nums); + return -EINVAL; + } + + for (i = 0; i < *mue_nums; i++) { + map[i].start_entity_idx = rsp->map[i].start_entity_idx; + map[i].end_entity_idx = rsp->map[i].end_entity_idx; + } + + return 0; +} + +static int ub_obtain_entity_info(struct ub_entity *uent, u16 *mue_nums, + struct ue_map *map) +{ + struct message_device *mdev = uent->message->mdev; + struct entity_info_msg_pkt req_pkt = {}; + struct entity_info_msg_pkt *rsp_pkt; + struct msg_info info = {}; + int ret; + + if (is_ibus_controller(uent)) { + *mue_nums = 1; + map->start_entity_idx = 0; + map->end_entity_idx = 0; + return 0; + } + + ub_msg_pkt_header_init(&req_pkt.header, uent, 0, + code_gen(UB_MSG_CODE_EXCH, UB_OBTAIN_ENTITY_INFO, + MSG_REQ), false); + + rsp_pkt = kzalloc(SZ_2K, GFP_KERNEL); + if (!rsp_pkt) + return -ENOMEM; + + message_info_init( + &info, uent, &req_pkt, rsp_pkt, + (sizeof(struct msg_pkt_header) << MSG_REQ_SIZE_OFFSET) | SZ_2K); + + ret = message_sync_request(mdev, &info, req_pkt.header.msgetah.code); + if (ret) + goto out; + + ret = ub_obtain_entity_info_rsp_handle(uent, rsp_pkt, mue_nums, map); +out: + kfree(rsp_pkt); + return ret; +} + +static int ub_enable_mues(struct ub_entity *pue, int nums, struct ue_map *map); +int ub_mue_enable_and_map(struct ub_entity *uent) +{ + struct ue_map *map; + u16 mue_nums; + int ret; + + map = kzalloc(SZ_1K, GFP_KERNEL); + if (!map) + return -ENOMEM; + + ret = ub_obtain_entity_info(uent, &mue_nums, map); + if (ret) + goto out; + + /* add map information to Entity0 */ + uent->uem.start_entity_idx = map[0].start_entity_idx; + uent->uem.end_entity_idx = map[0].end_entity_idx; + uent->total_ues = uent->uem.end_entity_idx - uent->uem.start_entity_idx + 1; + if (uent->entity_idx == map[0].start_entity_idx) + uent->total_ues = 0; + + ret = ub_enable_mues(uent, mue_nums - 1, map); + +out: + kfree(map); + return ret; +} + +static int ub_enable_ent(struct ub_entity *pue, int idx, u8 is_mue, + struct ue_map *map) +{ + int ret; + struct ub_entity *ue; + + ue = ub_alloc_ent(); + if (!ue) + return -ENOMEM; + + ue->entity_idx = idx; + ue->pue = pue; + ue->dev.parent = &pue->dev; + ue->ubc = pue->ubc; + ue->cna = pue->cna; + ue->upi = pue->upi; + + if (is_mue) { + ue->is_mue = is_mue; + ue->uem.start_entity_idx = map->start_entity_idx; + ue->uem.end_entity_idx = map->end_entity_idx; + ue->total_ues = map->end_entity_idx - map->start_entity_idx + 1; + if (ue->entity_idx == map->start_entity_idx) + ue->total_ues = 0; + } + + ret = ub_setup_ent(ue); + if (ret < 0) { + kfree(ue); + return ret; + } + + ub_entity_get(pue); + ub_entity_add(ue, pue); + ub_start_ent(ue); + + return 0; +} + +static void ub_disable_mues(struct ub_entity *pue); + +static int ub_enable_mues(struct ub_entity *pue, int nums, struct ue_map *map) +{ + int ret; + int i; + + /* Enable entities, excluding entity0 */ + for (i = 1; i <= nums; i++) { + ret = ub_enable_ent(pue, i, 1, &map[i]); + if (ret) + goto failed; + } + + return 0; +failed: + ub_disable_mues(pue); + return ret; +} + +void ub_disable_ent(struct ub_entity *uent) +{ + ub_info(uent, "disable entity, eid=%#05x\n", uent->eid); + ub_stop_and_remove_ent(uent); +} +EXPORT_SYMBOL_GPL(ub_disable_ent); + +static void ub_disable_mues(struct ub_entity *pue) +{ + struct ub_entity *mue, *tmp; + + list_for_each_entry_safe_reverse(mue, tmp, &pue->mue_list, node) + ub_disable_ent(mue); } diff --git a/drivers/ub/ubus/ubus_entity.h b/drivers/ub/ubus/ubus_entity.h index 811edacf6db8..d463269318fe 100644 --- a/drivers/ub/ubus/ubus_entity.h +++ b/drivers/ub/ubus/ubus_entity.h @@ -13,5 +13,6 @@ void ub_start_ent(struct ub_entity *uent); void ub_remove_ent(struct ub_entity *uent); void ub_stop_entities(void); void ub_remove_entities(void); +void ub_disable_ent(struct ub_entity *uent); #endif /* __UBUS_ENTITY_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 5ecbb16224de..59f83f32631d 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -121,6 +121,11 @@ struct ub_port { DECLARE_BITMAP(cap_map, UB_PORT_CAP_NUM); }; +struct ue_map { + u16 start_entity_idx; + u16 end_entity_idx; +}; + struct ub_entity { /* Driver framework base info */ struct device dev; @@ -141,9 +146,17 @@ struct ub_entity { unsigned short entity_idx; u32 uent_num; /* ub dev number */ struct mmio_zone zone[MAX_UB_RES_NUM]; + unsigned int total_funcs; u32 token_id; u32 token_value; + /* mue & ue info */ + u8 is_mue; + u16 total_ues; + u16 num_ues; + struct ue_map uem; + struct list_head mue_list; /* management ub entity list */ + /* entity topology info */ struct list_head node; struct ub_bus_controller *ubc; @@ -162,6 +175,10 @@ struct ub_entity { struct list_head cna_list; /* store distance for cna in route table */ struct dev_message *message; + + /* UB entity TID */ + u32 tid; + u32 support_feature; u16 upi; @@ -302,6 +319,20 @@ extern struct bus_type ub_bus_type; void ub_iommu_fwnode_handle_set(const struct fwnode_handle *fwnode); const struct fwnode_handle *ub_iommu_fwnode_handle_get(void); +/** + * ub_get_dst_eid() - Obtain the Dest EID of the entity. + * @uent: UB entity. + * + * Return the EID of bus instance if the entity has already been bound, + * or controller's EID. + * + * Context: Any context. + * Return: positive number if success, or %-EINVAL if @dev is %NULL, + * or %-ENODEV if entity's controller %NULL, or 0 if entity hasn't been + * initialized. + */ +int ub_get_dst_eid(struct ub_entity *uent); + /** * ub_iomap() - Map the resource space of the entity. * @uent: UB entity. @@ -512,6 +543,8 @@ void ub_stop_and_remove_ent(struct ub_entity *uent); static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) { return NULL; } static inline void ub_entity_put(struct ub_entity *uent) {} +static inline int ub_get_dst_eid(struct ub_entity *uent) +{ return -ENODEV; } static inline void __iomem * ub_iomap(struct ub_entity *uent, int resno, unsigned long maxlen) { return NULL; } -- Gitee From 54f9074621d538ea08c3fc999424fb14a9f065fb Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Mon, 13 Oct 2025 16:12:04 +0800 Subject: [PATCH 028/214] ub:ubus: Support device level and port level reset ANBZ: #45460 commit 0f0c155e2bb76b55e447e83da0226561c7f397b1 openEuler. Support device level and port level reset. Signed-off-by: Yahui Liu Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/port.c | 23 +++ drivers/ub/ubus/port.h | 4 + drivers/ub/ubus/reset.c | 326 ++++++++++++++++++++++++++++++++++ drivers/ub/ubus/reset.h | 16 ++ drivers/ub/ubus/ubus_entity.c | 2 + include/ub/ubus/ubus.h | 56 ++++++ 7 files changed, 428 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/reset.c create mode 100644 drivers/ub/ubus/reset.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index cb195c0f5742..37578637b453 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -3,6 +3,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o -ubus-y += enum.o resource.o ubus_entity.o +ubus-y += enum.o resource.o ubus_entity.o reset.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/port.c b/drivers/ub/ubus/port.c index 8aa7a7053cc0..74158f9b0d55 100644 --- a/drivers/ub/ubus/port.c +++ b/drivers/ub/ubus/port.c @@ -8,6 +8,7 @@ #include #include "ubus.h" +#include "reset.h" #include "port.h" struct ub_port_attribute { @@ -148,6 +149,26 @@ static ssize_t asy_link_width_show(struct ub_port *port, char *buf) } UB_PORT_ATTR_RO(asy_link_width); +static ssize_t port_reset_store(struct ub_port *port, const char *buf, + size_t count) +{ + unsigned long val; + int ret; + + ret = kstrtoul(buf, 0, &val); + if (ret || (val != 1)) { + ub_err(port->uent, "Invalid val for port reset\n"); + return -EINVAL; + } + + ret = ub_port_reset_function(port); + if (ret < 0) + return ret; + + return count; +} +UB_PORT_ATTR_WO(port_reset); + static ssize_t glb_qdlws_show(struct ub_port *port, char *buf) { u8 val; @@ -310,6 +331,7 @@ static struct attribute *ub_port_default_attrs[] = { &ub_port_attr_neighbor_port_idx.attr, &ub_port_attr_neighbor_guid.attr, &ub_port_attr_neighbor.attr, + &ub_port_attr_port_reset.attr, NULL }; @@ -508,6 +530,7 @@ static void ub_port_init(struct ub_entity *uent, struct ub_port *port) port->type = PHYSICAL; port->cna = 0; port->r_uent = NULL; + port->link_state = LINK_STATE_NORMAL; port->r_index = 0; port->r_guid = guid_null; bitmap_zero(port->cna_maps, UB_MAX_CNA_NUM); diff --git a/drivers/ub/ubus/port.h b/drivers/ub/ubus/port.h index b8757fad7060..3135d25ebdf4 100644 --- a/drivers/ub/ubus/port.h +++ b/drivers/ub/ubus/port.h @@ -23,5 +23,9 @@ int ub_ports_add(struct ub_entity *uent); void ub_ports_del(struct ub_entity *uent); int ub_ports_setup(struct ub_entity *uent); void ub_ports_unset(struct ub_entity *uent); +void ub_notify_share_port(struct ub_port *port, + enum ub_share_port_notify_type type); + +int ub_port_write_dword(struct ub_port *port, u32 pos, u32 val); #endif /* __PORT_H__ */ diff --git a/drivers/ub/ubus/reset.c b/drivers/ub/ubus/reset.c new file mode 100644 index 000000000000..1aaa787aec90 --- /dev/null +++ b/drivers/ub/ubus/reset.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus reset: " fmt + +#include + +#include "ubus.h" +#include "port.h" +#include "route.h" +#include "ubus_controller.h" +#include "ubus_config.h" + +enum elr_type { + ELR_PREPARE = 0, + ELR_DONE = 1, +}; + +enum SAVED_CFG_SPACE_NUM { + DEV_TOKEN_ID = 0, +}; + +static u32 saved_cfg_offset[] = { + [DEV_TOKEN_ID] = UB_CFG1_BASIC + 0xb4, +}; + +/** + * ub_elr - Initiate an UB entity level reset + * @dev: UB entity to reset + */ +int ub_elr(struct ub_entity *dev) +{ + u8 command; + u8 val = 0; + int ret; + + /* enable ELR */ + command = 0x01; + ret = ub_cfg_write_byte(dev, UB_ELR, command); + if (ret) { + ub_err(dev, "dev elr failed, write byte error, ret=%d", ret); + return ret; + } + + /* + * ub entity must complete an ELR within 100ms, + * but may silently discard requests while the ELR is in + * progress. Wait 100ms before trying to access the device. + */ + msleep(100); + + ret = ub_cfg_read_byte(dev, UB_ELR_DONE, &val); + if (ret || !val) { + ub_err(dev, "dev elr failed, ret=%d, ELR_DONE=%u\n", ret, val); + return -EINVAL; + } + + ub_info(dev, "dev elr success\n"); + + return 0; +} + +/** + * ub_device_reset - Initiate a UB entity reset + * @ent: UB entity. + */ +int ub_device_reset(struct ub_entity *ent) +{ + u16 command; + int ret; + + if (!ent) { + pr_err("device which will be reset is NULL\n"); + return -EINVAL; + } + + if (is_ibus_controller(ent)) { + ub_warn(ent, "ub bus controller do not support reset.\n"); + return -EINVAL; + } + + /* device reset only support Entity0 */ + if (ent->entity_idx) { + ub_err(ent, "device is not entity0, entity_idx=%u\n", ent->entity_idx); + return -EINVAL; + } + + device_lock(&ent->dev); + + /* enable device reset */ + command = 0x01; + ret = ub_send_cfg(ent, (u8)sizeof(u16), UB_ENTITY_RST, (u32 *)&command); + if (ret) { + ub_err(ent, "device reset failed, ret=%d\n", ret); + device_unlock(&ent->dev); + return -EIO; + } + + device_unlock(&ent->dev); + ub_info(ent, "device reset success\n"); + + return 0; +} +EXPORT_SYMBOL_GPL(ub_device_reset); + +static void ub_save_token_state(struct ub_entity *dev) +{ + int ret; + int i; + + for (i = DEV_TOKEN_ID; i <= DEV_TOKEN_ID; i++) { + ret = ub_cfg_read_dword(dev, saved_cfg_offset[i], + &dev->saved_config_space[i]); + if (ret) { + ub_err(dev, "ub cfg read dword failed, save cfg offset: %#x.\n", + saved_cfg_offset[i]); + continue; + } + ub_info(dev, "saving config space at address %#x (reading %#x)\n", + saved_cfg_offset[i], dev->saved_config_space[i]); + } +} + +static int ub_save_state(struct ub_entity *dev) +{ + const struct ub_error_handlers *err_handler = + dev->driver ? dev->driver->err_handler : NULL; + + if (err_handler && err_handler->ub_reset_prepare) + err_handler->ub_reset_prepare(dev); + + ub_save_token_state(dev); + + dev->state_saved = true; + + return 0; +} + +static void ub_restore_config_dword(struct ub_entity *dev, u32 pos, u32 saved_val) +{ + int retry = 10; + u32 val; + int ret; + + ret = ub_cfg_read_dword(dev, pos, &val); + if (ret || val == saved_val) + return; + while (retry-- > 0) { + ub_info(dev, "restoring config space at address %#x (was %#x, writing %#x)\n", + pos, val, saved_val); + ub_cfg_write_dword(dev, pos, saved_val); + + if (ub_cfg_read_dword(dev, pos, &val)) + continue; + + if (val == saved_val) + return; + +#define RESTORE_RETRY_SLEEP_MS 1 + msleep(RESTORE_RETRY_SLEEP_MS); + } +} + +static void ub_restore_state(struct ub_entity *dev) +{ + const struct ub_error_handlers *err_handler = + dev->driver ? dev->driver->err_handler : NULL; + int i; + + if (!dev->state_saved) + return; + + for (i = DEV_TOKEN_ID; i <= DEV_TOKEN_ID; i++) { + ub_restore_config_dword(dev, saved_cfg_offset[i], + dev->saved_config_space[i]); + } + + dev->state_saved = false; + + if (err_handler && err_handler->ub_reset_done) + err_handler->ub_reset_done(dev); +} + +static int ub_reset_check(struct ub_entity *dev) +{ + if (is_ibus_controller(dev)) { + ub_err(dev, "UB Bus Controller does not support ELR!\n"); + return -EINVAL; + } + + if (!dev->reset_fn) + return -ENOTTY; + + return 0; +} + +int ub_reset_entity(struct ub_entity *dev) +{ + int ret, rc; + + if (!dev) { + pr_err("device is NULL\n"); + return -EINVAL; + } + + rc = ub_reset_check(dev); + if (rc) + return rc; + + if (!device_trylock(&dev->dev)) + return -EBUSY; + + ret = ub_save_state(dev); + if (ret) { + device_unlock(&dev->dev); + return ret; + } + + rc = ub_elr(dev); + + ub_restore_state(dev); + + device_unlock(&dev->dev); + + return rc; +} +EXPORT_SYMBOL_GPL(ub_reset_entity); + +int ub_port_reset_check(struct ub_entity *dev, int port_id) +{ + struct ub_port *port = NULL; + + if (!dev) + return -EINVAL; + + if (is_idev(dev)) { + ub_err(dev, "IDEV does not support port reset!\n"); + return -EINVAL; + } + + if (port_id >= dev->port_nums) { + ub_err(dev, "Can't reset port because port(%d) is over port_nums(%u)!\n", + port_id, dev->port_nums); + return -EINVAL; + } + + port = dev->ports + port_id; + + if (port->type == VIRTUAL) { + ub_err(dev, "vport reset is not supported now!\n"); + return -EINVAL; + } + + return 0; +} + +static bool ub_wait_port_complete(struct ub_port *port) +{ + u64 timeout = 0; + +#define TOTAL_WAIT_CNT 150 /* wait 15s for port reset */ +#define WAIT_TIME 100 + + do { + if (port->link_state == LINK_STATE_DONE) + return true; + + msleep(WAIT_TIME); + timeout++; + } while (timeout < TOTAL_WAIT_CNT); + + return false; +} + +int ub_port_reset(struct ub_entity *dev, int port_id) +{ + struct ub_port *port = NULL; + int ret; + + if (ub_port_reset_check(dev, port_id)) + return -EINVAL; + + device_lock(&dev->dev); + port = dev->ports + port_id; + if (port->link_state != LINK_STATE_NORMAL) { + ub_err(dev, "port reset is not complete last time!\n"); + device_unlock(&dev->dev); + return -EINVAL; + } + + if (port->shareable) + ub_notify_share_port(port, RESET_PREPARE); + + /* enable port reset */ + ret = ub_port_write_dword(port, UB_PORT_RST, 0x01); + if (ret) { + ub_err(port->uent, "port reset failed, dev_eid=%u, port_id=%d, ret=%d\n", + dev->eid, port_id, ret); + device_unlock(&dev->dev); + return -EIO; + } + + port->link_state = LINK_STATE_RESETING; + + device_unlock(&dev->dev); + + if (ub_wait_port_complete(port)) { + if (port->shareable) + ub_notify_share_port(port, RESET_DONE); + port->link_state = LINK_STATE_NORMAL; + ub_info(dev, "port(%d) reset success!\n", port_id); + return ret; + } + + port->link_state = LINK_STATE_NORMAL; + ub_err(dev, "port(%d) reset timeout!\n", port_id); + return -ETIME; +} +EXPORT_SYMBOL_GPL(ub_port_reset); + +int ub_port_reset_function(struct ub_port *port) +{ + return ub_port_reset(port->uent, port->index); +} diff --git a/drivers/ub/ubus/reset.h b/drivers/ub/ubus/reset.h new file mode 100644 index 000000000000..f8ab56198c0a --- /dev/null +++ b/drivers/ub/ubus/reset.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __RESET_H__ +#define __RESET_H__ + +struct ub_port; +struct ub_entity; + +int ub_port_reset_function(struct ub_port *port); +int ub_port_reset(struct ub_entity *dev, int port_id); +int ub_port_reset_check(struct ub_entity *dev, int port_id); + +#endif /* __RESET_H__ */ diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index b1b41d10dbd2..7d0429d2d668 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -335,6 +335,8 @@ void ub_entity_add(struct ub_entity *uent, void *ctx) dma_set_max_seg_size(&uent->dev, UB_DEFAULT_MAX_SEG_SIZE); dma_set_seg_boundary(&uent->dev, GENMASK(31, 0)); + uent->state_saved = false; + if (is_primary(uent) || is_p_device(uent)) { ubc = (struct ub_bus_controller *)ctx; node = ub_ubc_to_node(ubc); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 59f83f32631d..0a2fdd3eb1ed 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -105,6 +105,12 @@ enum ub_port_type { #define UB_MAX_CNA_NUM SZ_64K #define UB_PORT_CAP_NUM SZ_256 +enum ub_link_state { + LINK_STATE_NORMAL = 0, + LINK_STATE_RESETING = 1, + LINK_STATE_DONE = 2, +}; + struct ub_port { struct ub_entity *uent; u16 index; @@ -119,6 +125,8 @@ struct ub_port { DECLARE_BITMAP(cna_maps, UB_MAX_CNA_NUM); /* cap cache */ DECLARE_BITMAP(cap_map, UB_PORT_CAP_NUM); + + enum ub_link_state link_state; }; struct ue_map { @@ -167,10 +175,15 @@ struct ub_entity { u16 port_nums; struct ub_port *ports; + unsigned int state_saved : 1; + /* entity DMA info */ u64 dma_mask; struct device_dma_parameters dma_parms; + /* UB reset info */ + unsigned int reset_fn : 1; + /* entity route info */ struct list_head cna_list; /* store distance for cna in route table */ @@ -179,11 +192,21 @@ struct ub_entity { /* UB entity TID */ u32 tid; + /* UB saved config space */ + u32 saved_config_space[24]; /* Config space saved at reset time */ + u32 support_feature; u16 upi; }; +/* UB bus error event callbacks */ +struct ub_error_handlers { + /* UB function reset prepare or completed */ + void (*ub_reset_prepare)(struct ub_entity *uent); + void (*ub_reset_done)(struct ub_entity *uent); +}; + struct ub_dynids { spinlock_t lock; /* Protects list, index */ struct list_head list; /* For IDs added at runtime */ @@ -218,6 +241,7 @@ struct ub_dynids { * context, so it can sleep. * @shutdown: Hook into reboot_notifier_list (kernel/sys.c). * Intended to stop any idling operations. + * @err_handler: Error handling callbacks. * @groups: Sysfs attribute groups. * @dev_groups: Attributes attached to the device that will be * created once it is bound to the driver. @@ -240,6 +264,7 @@ struct ub_driver { /* entity removed (NULL if not a hot-plug capable driver) */ void (*remove)(struct ub_entity *uent); void (*shutdown)(struct ub_entity *uent); + const struct ub_error_handlers *err_handler; const struct attribute_group **groups; const struct attribute_group **dev_groups; struct device_driver driver; @@ -401,6 +426,33 @@ int ub_register_share_port(struct ub_entity *uent, u16 port_id, void ub_unregister_share_port(struct ub_entity *uent, u16 port_id, struct ub_share_port_ops *ops); +/** + * ub_reset_entity() - Function entity level reset. + * @ent: UB entity. + * + * Reset a single entity without affecting other entities, If you want to reuse + * the entity after reset, you need to re-initialize it. + * + * Context: Any context + * Return: 0 if success, or %-EINVAL if entity not support elr, + * or %-ENOTTY if entity can't be reset safely, + * or -EBUSY if can't get device_trylock(), or other failed negative values. + */ +int ub_reset_entity(struct ub_entity *ent); + +/** + * ub_device_reset() - Device level reset. + * @ent: UB entity. + * + * Reset Device, include all entities under the device, If you want to reuse + * the device after reset, you need to re-initialize it. + * + * Context: Any context + * Return: 0 if success, or %-EINVAL if parameters invalid, + * or %-EIO if device can't reset now, can try later. + */ +int ub_device_reset(struct ub_entity *ent); + /** * ub_cfg_read_byte() - 1 byte configuration access read. * @uent: UB entity. @@ -584,6 +636,10 @@ static inline int ub_register_share_port(struct ub_entity *uent, u16 port_id, { return -ENODEV; } static inline void ub_unregister_share_port(struct ub_entity *uent, u16 port_id, struct ub_share_port_ops *ops) {} +static inline int ub_reset_entity(struct ub_entity *uent) +{ return -ENODEV; } +static inline int ub_device_reset(struct ub_entity *uent) +{ return -ENODEV; } static inline int __ub_register_driver(struct ub_driver *drv, struct module *owner, const char *mod_name) -- Gitee From d0ae02f95ed541a2e36f699418b7dd96ceea901a Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 19 Sep 2025 20:05:42 +0800 Subject: [PATCH 029/214] ub:ubus: Add UBUS capability interfaces ANBZ: #45460 commit c21b483526d766fd97d491f3951433f69fde3ac3 openEuler. Add some UBUS capability interfaces, including reading, writing, setting and clearing configuration space of UBUS capability. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/cap.c | 243 ++++++++++++++++++++++++++++++++++ drivers/ub/ubus/cap.h | 14 ++ drivers/ub/ubus/ubus_entity.c | 4 + include/ub/ubus/ubus.h | 6 + 5 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/cap.c create mode 100644 drivers/ub/ubus/cap.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 37578637b453..231f87ce48df 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -3,6 +3,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o -ubus-y += enum.o resource.o ubus_entity.o reset.o +ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/cap.c b/drivers/ub/ubus/cap.c new file mode 100644 index 000000000000..03f719422a2a --- /dev/null +++ b/drivers/ub/ubus/cap.c @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus cap: " fmt + +#include "ubus.h" + +#define DW_CHECK 3 + +void ub_set_cap_bitmap(struct ub_entity *uent) +{ + int ret; + u32 i; + + for (i = 0; i < SZ_8; i++) { + ret = ub_cfg_read_dword(uent, UB_CFG1_CAP_BITMAP + (i << SZ_2), + &uent->cfg1_bitmap[i]); + if (ret) + ub_err(uent, "Read cfg1 cap bitmap failed, ret=%d\n", + ret); + } + + if (is_p_device(uent)) + return; + + for (i = 0; i < SZ_8; i++) { + ret = ub_cfg_read_dword(uent, UB_CFG0_CAP_BITMAP + (i << SZ_2), + &uent->cfg0_bitmap[i]); + if (ret) + ub_err(uent, "Read cfg0 cap bitmap failed, ret=%d\n", + ret); + } +} + +/* Check whether the capbility register is implemented. */ +static bool ub_cap_reg_implemented(struct ub_entity *uent, u32 cap) +{ + u32 i = (cap & 0xFF) / SZ_32; + u32 val = (cap >> SZ_8) ? uent->cfg1_bitmap[i] : uent->cfg0_bitmap[i]; + + return val & BIT((cap & 0xFF) % SZ_32); +} + +/* find the start address of capability */ +u32 ub_find_capability(u32 cap) +{ + return (cap << BITS_PER_BYTE) << SZ_2; +} + +int ub_cap_read_byte(struct ub_entity *uent, u32 cap, u32 off, u8 *val) +{ + int ret; + + *val = 0; + if (off >= SZ_1K) + return -EFAULT; + + if (!ub_cap_reg_implemented(uent, cap)) + return -ENXIO; + + ret = ub_cfg_read_byte(uent, ub_find_capability(cap) + off, val); + if (ret) + *val = 0; + + return ret; +} + +int ub_cap_read_word(struct ub_entity *uent, u32 cap, u32 off, u16 *val) +{ + int ret; + + *val = 0; + if (off & 1 || off >= SZ_1K) + return -EFAULT; + + if (!ub_cap_reg_implemented(uent, cap)) + return -ENXIO; + + ret = ub_cfg_read_word(uent, ub_find_capability(cap) + off, val); + if (ret) + *val = 0; + + return ret; +} + +int ub_cap_read_dword(struct ub_entity *uent, u32 cap, u32 off, u32 *val) +{ + int ret; + + *val = 0; + if (off & DW_CHECK || off >= SZ_1K) + return -EFAULT; + + if (!ub_cap_reg_implemented(uent, cap)) + return -ENXIO; + + ret = ub_cfg_read_dword(uent, ub_find_capability(cap) + off, val); + if (ret) + *val = 0; + + return ret; +} + +int ub_cap_write_byte(struct ub_entity *uent, u32 cap, u32 off, u8 val) +{ + if (off >= SZ_1K) + return -EFAULT; + + if (!ub_cap_reg_implemented(uent, cap)) + return -ENXIO; + + return ub_cfg_write_byte(uent, ub_find_capability(cap) + off, val); +} + +int ub_cap_write_word(struct ub_entity *uent, u32 cap, u32 off, u16 val) +{ + if (off & 1 || off >= SZ_1K) + return -EFAULT; + + if (!ub_cap_reg_implemented(uent, cap)) + return -ENXIO; + + return ub_cfg_write_word(uent, ub_find_capability(cap) + off, val); +} + +int ub_cap_write_dword(struct ub_entity *uent, u32 cap, u32 off, u32 val) +{ + if (off & DW_CHECK || off >= SZ_1K) + return -EFAULT; + + if (!ub_cap_reg_implemented(uent, cap)) + return -ENXIO; + + return ub_cfg_write_dword(uent, ub_find_capability(cap) + off, val); +} + +int ub_cap_clear_and_set_word(struct ub_entity *dev, u32 cap, u32 off, + u16 clear, u16 set) +{ + u16 val; + int ret; + + ret = ub_cap_read_word(dev, cap, off, &val); + if (!ret) { + val &= ~clear; + val |= set; + ret = ub_cap_write_word(dev, cap, off, val); + } + + return ret; +} + +int ub_cap_clear_and_set_dword(struct ub_entity *dev, u32 cap, u32 off, + u32 clear, u32 set) +{ + u32 val; + int ret; + + ret = ub_cap_read_dword(dev, cap, off, &val); + if (!ret) { + val &= ~clear; + val |= set; + ret = ub_cap_write_dword(dev, cap, off, val); + } + + return ret; +} + +static inline int ub_cap_set_word(struct ub_entity *dev, u32 cap, u32 off, + u16 set) +{ + return ub_cap_clear_and_set_word(dev, cap, off, 0, set); +} + +static inline int ub_cap_set_dword(struct ub_entity *dev, u32 cap, u32 off, + u32 set) +{ + return ub_cap_clear_and_set_dword(dev, cap, off, 0, set); +} + +static inline int ub_cap_clear_word(struct ub_entity *dev, u32 cap, u32 off, + u16 clear) +{ + return ub_cap_clear_and_set_word(dev, cap, off, clear, 0); +} + +static inline int ub_cap_clear_dword(struct ub_entity *dev, u32 cap, u32 off, + u32 clear) +{ + return ub_cap_clear_and_set_dword(dev, cap, off, clear, 0); +} + +static int ub_sf_init(struct ub_entity *uent) +{ + u32 support_feature = 0; + int ret; + + ret = ub_cfg_read_dword(uent, UB_FEATURE_SUPPORT_0, &support_feature); + if (ret) { + ub_err(uent, "Read support feature0 failed, ret=%d\n", ret); + return ret; + } + + uent->support_feature = support_feature; + return 0; +} + +static void ub_sw_init(struct ub_entity *uent) +{ + if (!uent || !uent->ubc) + return; + + if (!is_ibus_controller(uent)) + return; + + if (uent->support_feature & UB_SW_SUPPORT) + uent->sw_cap = true; + else + uent->sw_cap = false; +} + +void ub_init_capabilities(struct ub_entity *uent) +{ + /* cfg0 caps */ + if (ub_sf_init(uent)) + goto init_cfg1_cap; + + ub_sw_init(uent); + +init_cfg1_cap: + /* cfg1 caps */ + + uent->reset_fn = 1; +} + +void ub_uninit_capabilities(struct ub_entity *uent) +{ + /* cfg1 cap */ + + uent->reset_fn = 0; +} diff --git a/drivers/ub/ubus/cap.h b/drivers/ub/ubus/cap.h new file mode 100644 index 000000000000..11507c646017 --- /dev/null +++ b/drivers/ub/ubus/cap.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __CAP_H__ +#define __CAP_H__ + +void ub_set_cap_bitmap(struct ub_entity *uent); +u32 ub_find_capability(u32 cap); +void ub_init_capabilities(struct ub_entity *uent); +void ub_uninit_capabilities(struct ub_entity *uent); + +#endif /* __CAP_H__ */ diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index 7d0429d2d668..c17a5e736eb0 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -19,6 +19,7 @@ #include "ubus_controller.h" #include "ubus_driver.h" #include "ubus_inner.h" +#include "cap.h" /* * Entity lifecycle @@ -287,6 +288,7 @@ int ub_setup_ent(struct ub_entity *uent) goto free_uent_num; ub_config_eid(uent); + ub_set_cap_bitmap(uent); ub_set_fm_info(uent); ub_get_module_id(uent); @@ -336,6 +338,7 @@ void ub_entity_add(struct ub_entity *uent, void *ctx) dma_set_seg_boundary(&uent->dev, GENMASK(31, 0)); uent->state_saved = false; + ub_init_capabilities(uent); if (is_primary(uent) || is_p_device(uent)) { ubc = (struct ub_bus_controller *)ctx; @@ -451,6 +454,7 @@ void ub_remove_ent(struct ub_entity *uent) list_del(&uent->node); up_write(&ub_bus_sem); + ub_uninit_capabilities(uent); ub_unconfigure_ent(uent); ub_entity_unset_mmio(uent); ub_entity_num_free(uent); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 0a2fdd3eb1ed..aff890265824 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -175,6 +175,10 @@ struct ub_entity { u16 port_nums; struct ub_port *ports; + /* entity capability info */ + unsigned int cfg0_bitmap[8]; + unsigned int cfg1_bitmap[8]; + unsigned int state_saved : 1; /* entity DMA info */ @@ -192,6 +196,8 @@ struct ub_entity { /* UB entity TID */ u32 tid; + bool sw_cap; + /* UB saved config space */ u32 saved_config_space[24]; /* Config space saved at reset time */ -- Gitee From 7dda0ab83a2b78c20423f68edf90af2807b13b45 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Sat, 20 Sep 2025 09:53:18 +0800 Subject: [PATCH 030/214] ub:ubus: Support UBUS Interrupt framework ANBZ: #45460 commit 81962d0ecc6e3e039f4df445d38650dd6109bc78 openEuler. Support UBUS Interrupt framework, add basic data structure of UBUS USI and create message signal interrupts domain. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- .../arm64/CONFIG_ARM_GIC_V3_ITS_UBUS | 1 + .../L1-RECOMMEND/arm64/CONFIG_UB_UBUS_USI | 1 + drivers/irqchip/Kconfig | 7 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-gic-v3-its-ub-msi.c | 156 ++++++++++++++++++ drivers/ub/ubus/Kconfig | 12 ++ drivers/ub/ubus/Makefile | 1 + drivers/ub/ubus/msi/Makefile | 3 + drivers/ub/ubus/msi/irqdomain.c | 114 +++++++++++++ drivers/ub/ubus/msi/msi.c | 155 +++++++++++++++++ include/linux/irqdomain_defs.h | 3 + include/linux/msi.h | 39 +++++ include/ub/ubus/ubus.h | 10 ++ kernel/irq/msi.c | 16 +- 14 files changed, 515 insertions(+), 4 deletions(-) create mode 100644 anolis/configs/L1-RECOMMEND/arm64/CONFIG_ARM_GIC_V3_ITS_UBUS create mode 100644 anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UBUS_USI create mode 100644 drivers/irqchip/irq-gic-v3-its-ub-msi.c create mode 100644 drivers/ub/ubus/msi/Makefile create mode 100644 drivers/ub/ubus/msi/irqdomain.c create mode 100644 drivers/ub/ubus/msi/msi.c diff --git a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_ARM_GIC_V3_ITS_UBUS b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_ARM_GIC_V3_ITS_UBUS new file mode 100644 index 000000000000..1b2aa127b3aa --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_ARM_GIC_V3_ITS_UBUS @@ -0,0 +1 @@ +CONFIG_ARM_GIC_V3_ITS_UBUS=y diff --git a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UBUS_USI b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UBUS_USI new file mode 100644 index 000000000000..fd03e3053c78 --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UBUS_USI @@ -0,0 +1 @@ +CONFIG_UB_UBUS_USI=y diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index a1dff2b166f0..da4cd3cef36e 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -102,6 +102,13 @@ config ARM_GIC_V3_ITS_PCI depends on PCI_MSI default ARM_GIC_V3_ITS +config ARM_GIC_V3_ITS_UBUS + bool + depends on ARM_GIC_V3_ITS + depends on UB_UBUS + depends on UB_UBUS_USI + default ARM_GIC_V3_ITS + config ARM_GIC_V3_ITS_FSL_MC bool depends on ARM_GIC_V3_ITS diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index a9f8fe849f8e..b58fe6bcdf24 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_IRQ_MSI_LIB) += irq-msi-lib.o obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-v3-mbi.o irq-gic-common.o obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v4.o irq-gic-v3-its-msi-parent.o +obj-$(CONFIG_ARM_GIC_V3_ITS_UBUS) += irq-gic-v3-its-ub-msi.o obj-$(CONFIG_ARM_GIC_V3_ITS_FSL_MC) += irq-gic-v3-its-fsl-mc-msi.o obj-$(CONFIG_ARM_GIC_PHYTIUM_2500) += irq-gic-phytium-2500.o irq-gic-phytium-2500-its.o obj-$(CONFIG_PARTITION_PERCPU) += irq-partition-percpu.o diff --git a/drivers/irqchip/irq-gic-v3-its-ub-msi.c b/drivers/irqchip/irq-gic-v3-its-ub-msi.c new file mode 100644 index 000000000000..4caccd12fdc4 --- /dev/null +++ b/drivers/irqchip/irq-gic-v3-its-ub-msi.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void its_mask_msi_irq(struct irq_data *d) +{ + ub_msi_mask_irq(d); + irq_chip_mask_parent(d); +} + +static void its_unmask_msi_irq(struct irq_data *d) +{ + ub_msi_unmask_irq(d); + irq_chip_unmask_parent(d); +} + +static struct irq_chip its_msi_irq_chip = { + .name = "ITS-MSI", + .irq_unmask = its_unmask_msi_irq, + .irq_mask = its_mask_msi_irq, + .irq_eoi = irq_chip_eoi_parent, +}; + +static int its_ub_msi_prepare(struct irq_domain *domain, struct device *dev, + int nvec, msi_alloc_info_t *info) +{ + struct msi_domain_info *msi_info; + int alias_count = 0, minnvec = 1; + struct ub_entity *uent = to_ub_entity(dev); + int cnt, ret; + + msi_info = msi_get_domain_info(domain->parent); + + ret = ub_interrupt_id_alloc(uent); + if (ret) { + dev_err(&uent->dev, "device id alloc failed, ret = %d.\n", ret); + return ret; + } + + info->scratchpad[0].ul = uent->intr_device_id; + dev_info(&uent->dev, "device id alloc success, id: %u.\n", uent->intr_device_id); + ub_write_interruptid(uent); + + cnt = max(nvec, alias_count); + cnt = max_t(int, minnvec, roundup_pow_of_two((u32)cnt)); + + return msi_info->ops->msi_prepare(domain->parent, dev, cnt, info); +} + +static struct msi_domain_ops its_ub_msi_ops = { + .msi_prepare = its_ub_msi_prepare, +}; + +static struct msi_domain_info its_ub_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_UB_INTR), + .ops = &its_ub_msi_ops, + .chip = &its_msi_irq_chip, +}; + +static int its_ub_msi_init_one(struct fwnode_handle *handle, + const char *name) +{ + struct irq_domain *parent; + + parent = irq_find_matching_fwnode(handle, DOMAIN_BUS_NEXUS); + if (!parent || !msi_get_domain_info(parent)) { + pr_err("%s: Unable to locate ITS domain\n", name); + return -ENXIO; + } + + if (!ub_msi_create_irq_domain(handle, &its_ub_msi_domain_info, parent)) { + pr_err("%s: Unable to create UB domain\n", name); + return -ENOMEM; + } + + return 0; +} + +static int its_ub_msi_parse_madt(union acpi_subtable_headers *header, + const unsigned long end) +{ + struct acpi_madt_generic_translator *its_entry; + struct fwnode_handle *dom_handle; + char *node_name; + int err = -ENXIO; + + its_entry = (struct acpi_madt_generic_translator *)header; + node_name = kasprintf(GFP_KERNEL, "ITS@0x%lx", + (long)its_entry->base_address); + + dom_handle = iort_find_domain_token(its_entry->translation_id); + if (!dom_handle) { + pr_err("%s: Unable to locate ITS domain handle\n", node_name); + goto out; + } + + err = its_ub_msi_init_one(dom_handle, (const char *)node_name); + if (!err) + pr_info("UB/USI: %s domain created\n", node_name); +out: + kfree(node_name); + return err; +} + +static int its_ub_acpi_msi_init(void) +{ + acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR, + its_ub_msi_parse_madt, 0); + return 0; +} + +static const struct of_device_id its_device_id[] = { + { .compatible = "arm,gic-v3-its", }, + {}, +}; + +static int its_ub_of_msi_init(void) +{ + struct device_node *np; + + for (np = of_find_matching_node(NULL, its_device_id); np; + np = of_find_matching_node(np, its_device_id)) { + if (!of_device_is_available(np)) + continue; + if (!of_property_read_bool(np, "msi-controller")) + continue; + + if (its_ub_msi_init_one(of_node_to_fwnode(np), np->full_name)) + continue; + + pr_info("UB/USI: %pOF domain created\n", np); + } + + return 0; +} + +int __init its_ub_msi_init(void) +{ + its_ub_of_msi_init(); + its_ub_acpi_msi_init(); + + return 0; +} +early_initcall(its_ub_msi_init); diff --git a/drivers/ub/ubus/Kconfig b/drivers/ub/ubus/Kconfig index cf2f7e81b8c8..fc509766b125 100644 --- a/drivers/ub/ubus/Kconfig +++ b/drivers/ub/ubus/Kconfig @@ -27,4 +27,16 @@ config UB_UBUS_BUS the UB bus device management functionality. Say 'M' here unless you know what you are doing. +config UB_UBUS_USI + default n + bool "UBUS Message Signaled Interrupts (USI)" + depends on UB_UBUS + select GENERIC_MSI_IRQ + help + This allows device drivers to enable MSI (Message Signaled + Interrupts). Message Signaled Interrupts enable a device to + generate an interrupt using an inbound Memory Write on its + UnifiedBus instead of asserting a device IRQ pin.If you + don't know what to do here, say Y. + endif diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 231f87ce48df..ac8ecfd3c0ce 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o +obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o diff --git a/drivers/ub/ubus/msi/Makefile b/drivers/ub/ubus/msi/Makefile new file mode 100644 index 000000000000..9fb340462fc6 --- /dev/null +++ b/drivers/ub/ubus/msi/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_UB_UBUS_USI) += irqdomain.o msi.o diff --git a/drivers/ub/ubus/msi/irqdomain.c b/drivers/ub/ubus/msi/irqdomain.c new file mode 100644 index 000000000000..5a01c40368bc --- /dev/null +++ b/drivers/ub/ubus/msi/irqdomain.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include +#include +#include +#include + +static irq_hw_number_t ub_msi_domain_calc_hwirq(struct msi_desc *desc) +{ + struct ub_entity *uent = msi_desc_to_ub_entity(desc); + + return (irq_hw_number_t)(desc->msi_index | (uent->intr_device_id << 11)); +} + +static void ub_msi_domain_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) +{ + arg->desc = desc; + arg->hwirq = ub_msi_domain_calc_hwirq(desc); + pr_info("%s, arg->hwirq: %lu.\n", __func__, arg->hwirq); +} + +static struct msi_domain_ops ub_msi_domain_ops_default = { + .set_desc = ub_msi_domain_set_desc, +}; + +static void ub_msi_domain_update_dom_ops(struct msi_domain_info *info) +{ + struct msi_domain_ops *ops = info->ops; + + if (ops == NULL) + info->ops = &ub_msi_domain_ops_default; + else if (!ops->set_desc) + ops->set_desc = ub_msi_domain_set_desc; +} + +static void ub_msi_domain_write_msg(struct irq_data *irq_data, struct msi_msg *msg) +{ + struct msi_desc *desc = irq_data_get_msi_desc(irq_data); + + if (desc->irq == irq_data->irq) + __ub_write_msi_msg(desc, msg); +} + +static void ub_msi_domain_update_chip_ops(struct msi_domain_info *info) +{ + struct irq_chip *chip = info->chip; + + WARN_ON_ONCE(!chip); + chip->irq_write_msi_msg = chip->irq_write_msi_msg ? + chip->irq_write_msi_msg : + ub_msi_domain_write_msg; + chip->irq_mask = chip->irq_mask ? chip->irq_mask : ub_msi_mask_irq; + chip->irq_unmask = chip->irq_unmask ? chip->irq_unmask : + ub_msi_unmask_irq; +} + +struct irq_domain *ub_msi_create_irq_domain(struct fwnode_handle *fwnode, + struct msi_domain_info *info, + struct irq_domain *parent) +{ + struct irq_domain *domain; + + if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS) + ub_msi_domain_update_dom_ops(info); + if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) + ub_msi_domain_update_chip_ops(info); + if (WARN_ON(info->flags & MSI_FLAG_LEVEL_CAPABLE)) + info->flags &= ~MSI_FLAG_LEVEL_CAPABLE; + + info->flags |= MSI_FLAG_FREE_MSI_DESCS | MSI_FLAG_ACTIVATE_EARLY | + MSI_FLAG_DEV_SYSFS; + + if (IS_ENABLED(CONFIG_GENERIC_IRQ_RESERVATION_MODE)) + info->flags |= MSI_FLAG_MUST_REACTIVATE; + + info->chip->flags |= IRQCHIP_ONESHOT_SAFE; + info->bus_token = DOMAIN_BUS_UB_MSI; + + domain = msi_create_irq_domain(fwnode, info, parent); + + return domain; +} +EXPORT_SYMBOL_GPL(ub_msi_create_irq_domain); + +static bool ub_create_device_domain(struct ub_entity *uent, + const struct msi_domain_template *tmpl, + unsigned int hwsize) +{ + struct irq_domain *domain = dev_get_msi_domain(&uent->dev); + + if (!domain || !irq_domain_is_msi_parent(domain)) + return true; + + if (WARN_ON_ONCE(1)) + pr_info("TODO: create device irq domain.\n"); + + return false; +} + +bool ub_setup_usi_device_domain(struct ub_entity *uent, unsigned int hwsize) +{ + if (WARN_ON_ONCE(uent->intr_enabled)) + return false; + + if (msi_match_device_irq_domain(&uent->dev, MSI_DEFAULT_DOMAIN, + DOMAIN_BUS_UB_MSI)) + return true; + + return ub_create_device_domain(uent, NULL, hwsize); +} +EXPORT_SYMBOL_GPL(ub_setup_usi_device_domain); diff --git a/drivers/ub/ubus/msi/msi.c b/drivers/ub/ubus/msi/msi.c new file mode 100644 index 000000000000..d0f3eb6e2731 --- /dev/null +++ b/drivers/ub/ubus/msi/msi.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Only support for irqdomain.c. + */ + +#include +#include +#include +#include +#include + +struct ub_device_id_manager { + struct idr device_id_idr; + spinlock_t lock; +}; + +static struct ub_device_id_manager ub_entity_idm = { + .device_id_idr = IDR_INIT(ub_entity_idm.device_id_idr), + .lock = __SPIN_LOCK_UNLOCKED(ub_entity_idm.lock), +}; + +int ub_interrupt_id_alloc(struct ub_entity *uent) +{ + int id; + + idr_preload(GFP_KERNEL); + spin_lock(&ub_entity_idm.lock); + id = idr_alloc(&ub_entity_idm.device_id_idr, uent, + uent->ubc->attr.int_id_start, uent->ubc->attr.int_id_end, + GFP_NOWAIT); + spin_unlock(&ub_entity_idm.lock); + idr_preload_end(); + + if (id < 0) + return id; + uent->intr_device_id = id; + + return 0; +} + +void ub_interrupt_id_free(struct ub_entity *uent) +{ + spin_lock(&ub_entity_idm.lock); + idr_remove(&ub_entity_idm.device_id_idr, uent->intr_device_id); + spin_unlock(&ub_entity_idm.lock); +} +EXPORT_SYMBOL_GPL(ub_interrupt_id_free); + +struct ub_entity *msi_desc_to_ub_entity(struct msi_desc *desc) +{ + return to_ub_entity(desc->dev); +} + +static void __iomem *ub_vector_desc_base_addr(struct msi_desc *desc) +{ + return desc->ub_intr.vector_base; +} + +void __iomem *ub_vector_desc_addr(struct msi_desc *desc) +{ + return desc->ub_intr.vector_base + (desc->ub_intr.intr_attrib.entry_nr * + UB_INTR_VECTOR_ENTRY_SIZE); +} + +static void __iomem *ub_addr_desc_base_addr(struct msi_desc *desc) +{ + return desc->ub_intr.addr_base; +} + +static void __iomem *ub_addr_desc_addr(struct msi_desc *desc) +{ + return desc->ub_intr.addr_base + (desc->ub_intr.intr_attrib.addr_index * + UB_INTR_ADDR_ENTRY_SIZE); +} + +static void ub_int_type1_update_mask(struct msi_desc *desc, u32 clear, u32 set) +{ + raw_spinlock_t *lock = &msi_desc_to_ub_entity(desc)->usi_lock; + unsigned long flags; + + raw_spin_lock_irqsave(lock, flags); + desc->ub_intr.intr_attrib.mask &= ~clear; + desc->ub_intr.intr_attrib.mask |= set; + ub_cfg_write_dword(msi_desc_to_ub_entity(desc), UB_INT_TYPE1_INT_MASK, + desc->ub_intr.intr_attrib.mask); + raw_spin_unlock_irqrestore(lock, flags); +} + +static void ub_int_type1_mask(struct msi_desc *desc, u32 mask) +{ + ub_int_type1_update_mask(desc, 0, mask); +} + +static void ub_int_type2_mask(struct msi_desc *desc) +{ + void __iomem *addr = ub_vector_desc_addr(desc); + u32 reg_val = readl(addr + UB_INTR_VECTOR_ADDR_INDEX) | UB_INTR_VECTOR_MASK_MASK; + + desc->ub_intr.intr_attrib.mask = 1; + writel(reg_val, addr + UB_INTR_VECTOR_ADDR_INDEX); +} + +static void ub_usi_desc_mask(struct msi_desc *desc, u32 nr) +{ + if (desc->ub_intr.intr_attrib.is_type1) + ub_int_type1_mask(desc, nr); + else + ub_int_type2_mask(desc); +} + +void ub_msi_mask_irq(struct irq_data *data) +{ + struct msi_desc *desc = irq_data_get_msi_desc(data); + + ub_usi_desc_mask(desc, (u32)BIT(data->irq - desc->irq)); +} + +static void ub_int_type1_unmask_irq(struct msi_desc *desc, u32 mask) +{ + ub_int_type1_update_mask(desc, mask, 0); +} + +static void ub_int_type2_unmask_irq(struct msi_desc *desc) +{ + void __iomem *addr = ub_vector_desc_addr(desc); + u32 reg_val = readl(addr + UB_INTR_VECTOR_ADDR_INDEX) & (~UB_INTR_VECTOR_MASK_MASK); + + desc->ub_intr.intr_attrib.mask = 0; + writel(reg_val, addr + UB_INTR_VECTOR_ADDR_INDEX); +} + +static void ub_usi_desc_unmask(struct msi_desc *desc, u32 nr) +{ + if (desc->ub_intr.intr_attrib.is_type1) + ub_int_type1_unmask_irq(desc, nr); + else + ub_int_type2_unmask_irq(desc); +} + +void ub_msi_unmask_irq(struct irq_data *data) +{ + struct msi_desc *desc = irq_data_get_msi_desc(data); + + ub_usi_desc_unmask(desc, (u32)BIT(data->irq - desc->irq)); +} + +void ub_write_interruptid(struct ub_entity *uent) +{ + if (!uent->intr_type1) + ub_cfg_write_dword(uent, UB_INT_ID, uent->intr_device_id); + else + ub_cfg_write_dword(uent, UB_INT_TYPE1_INT_ID, uent->intr_device_id); +} + +void __ub_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) {} diff --git a/include/linux/irqdomain_defs.h b/include/linux/irqdomain_defs.h index 5c1fe6f1fcde..1780c87e75eb 100644 --- a/include/linux/irqdomain_defs.h +++ b/include/linux/irqdomain_defs.h @@ -2,6 +2,8 @@ #ifndef _LINUX_IRQDOMAIN_DEFS_H #define _LINUX_IRQDOMAIN_DEFS_H +#include + /* * Should several domains have the same device node, but serve * different purposes (for example one domain is for PCI/MSI, and the @@ -28,6 +30,7 @@ enum irq_domain_bus_token { DOMAIN_BUS_PCI_DEVICE_IMS, DOMAIN_BUS_DEVICE_MSI, DOMAIN_BUS_WIRED_TO_MSI, + CK_KABI_EXTEND_ENUM(DOMAIN_BUS_UB_MSI) }; #endif /* _LINUX_IRQDOMAIN_DEFS_H */ diff --git a/include/linux/msi.h b/include/linux/msi.h index 7d0e717e2bde..eb60253e7330 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -159,6 +159,19 @@ struct msi_desc_data { #define MSI_MAX_INDEX ((unsigned int)USHRT_MAX) +struct ub_intr_desc { + struct { + u32 mask; + u8 is_type1 : 1; + u16 entry_nr; + u16 addr_index; + } intr_attrib; + u16 addr_num; + u16 vec_num; + void __iomem *vector_base; + void __iomem *addr_base; +}; + /** * struct msi_desc - Descriptor structure for MSI based interrupts * @irq: The base interrupt number @@ -202,6 +215,11 @@ struct msi_desc { union { struct pci_msi_desc pci; struct msi_desc_data data; +#ifndef __GENKSYMS__ + struct ub_intr_desc ub_intr; +#else + KABI_EXTEND_WITH_SIZE(KABI_RESERVE(1), 5) +#endif }; CK_KABI_RESERVE(1) @@ -578,6 +596,7 @@ enum { MSI_FLAG_PCI_MSIX_ALLOC_DYN = (1 << 20), /* Support for PCI/IMS */ MSI_FLAG_PCI_IMS = (1 << 21), + CK_KABI_EXTEND_ENUM(MSI_FLAG_UB_INTR = (1 << 22)) }; /** @@ -691,4 +710,24 @@ static inline struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev) static inline void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg) { } #endif /* !CONFIG_PCI_MSI */ +struct ub_usi_entry { + u32 vector; /* Kernel uses to write allocated vector */ + u16 entry; /* Driver uses to specify entry, OS writes */ +}; +struct ub_entity; +struct irq_affinity; +#ifdef CONFIG_UB_UBUS_USI +struct ub_entity *msi_desc_to_ub_entity(struct msi_desc *desc); +void ub_msi_mask_irq(struct irq_data *data); +void ub_msi_unmask_irq(struct irq_data *data); +void ub_write_interruptid(struct ub_entity *uent); +struct irq_domain *ub_msi_create_irq_domain(struct fwnode_handle *fwnode, + struct msi_domain_info *info, + struct irq_domain *parent); +void __ub_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); +int ub_interrupt_id_alloc(struct ub_entity *uent); +void ub_interrupt_id_free(struct ub_entity *uent); +bool ub_setup_usi_device_domain(struct ub_entity *uent, unsigned int hwsize); +#endif /* CONFIG_UB_UBUS_USI */ + #endif /* LINUX_MSI_H */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index aff890265824..d8af6aa007e7 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -185,6 +185,16 @@ struct ub_entity { u64 dma_mask; struct device_dma_parameters dma_parms; + /* UB interrupt info */ + raw_spinlock_t usi_lock; + unsigned int no_intr : 1; + unsigned int intr_enabled : 1; + unsigned int intr_type1 : 1; + void __iomem *intr_addr_base; + void __iomem *intr_vector_base; + u32 intr_device_id; + const struct attribute_group **msi_irq_groups; + /* UB reset info */ unsigned int reset_fn : 1; diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index cb5bcfbab43a..d5f01bdcf014 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include "internals.h" @@ -158,6 +160,7 @@ int msi_domain_insert_msi_desc(struct device *dev, unsigned int domid, /* Copy type specific data to the new descriptor. */ desc->pci = init_desc->pci; + desc->ub_intr = init_desc->ub_intr; return msi_insert_desc(dev, desc, domid, init_desc->msi_index); } @@ -448,7 +451,7 @@ unsigned int msi_domain_get_virq(struct device *dev, unsigned int domid, unsigne { struct msi_desc *desc; unsigned int ret = 0; - bool pcimsi = false; + bool devmsi = false; struct xarray *xa; if (!dev->msi.data) @@ -459,18 +462,23 @@ unsigned int msi_domain_get_virq(struct device *dev, unsigned int domid, unsigne /* This check is only valid for the PCI default MSI domain */ if (dev_is_pci(dev) && domid == MSI_DEFAULT_DOMAIN) - pcimsi = to_pci_dev(dev)->msi_enabled; + devmsi = to_pci_dev(dev)->msi_enabled; +#ifdef CONFIG_UB_UBUS + /* This check is only valid for the UBUS default MSI domain */ + if (dev_is_ub(dev) && domid == MSI_DEFAULT_DOMAIN) + devmsi = to_ub_entity(dev)->intr_type1; +#endif msi_lock_descs(dev); xa = &dev->msi.data->__domains[domid].store; - desc = xa_load(xa, pcimsi ? 0 : index); + desc = xa_load(xa, devmsi ? 0 : index); if (desc && desc->irq) { /* * PCI-MSI has only one descriptor for multiple interrupts. * PCI-MSIX and platform MSI use a descriptor per * interrupt. */ - if (pcimsi) { + if (devmsi) { if (index < desc->nvec_used) ret = desc->irq + index; } else { -- Gitee From 909be1485d486c429db3a124b2b3d589cfe07489 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Wed, 24 Sep 2025 17:15:50 +0800 Subject: [PATCH 031/214] ub:ubus: Add UBUS MSI basic functions ANBZ: #45460 commit 47b1d5f03a5975022ae7c08cd0fd80ecd5777553 openEuler. Add UBUS MSI basic functions, including set-up interrupt, count interrupt number, write and set msi message and disable UBUS interrupts. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/msi/msi.c | 398 +++++++++++++++++++++++++++++++++++++- include/linux/msi.h | 7 + include/ub/ubus/ubus.h | 37 ++++ 3 files changed, 441 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubus/msi/msi.c b/drivers/ub/ubus/msi/msi.c index d0f3eb6e2731..3ea005eb08ab 100644 --- a/drivers/ub/ubus/msi/msi.c +++ b/drivers/ub/ubus/msi/msi.c @@ -9,6 +9,14 @@ #include #include +unsigned int ub_irq_calc_affinity_vectors(unsigned int minvec, + unsigned int maxvec, + const struct irq_affinity *affd) +{ + return irq_calc_affinity_vectors(minvec, maxvec, affd); +} +EXPORT_SYMBOL_GPL(ub_irq_calc_affinity_vectors); + struct ub_device_id_manager { struct idr device_id_idr; spinlock_t lock; @@ -144,6 +152,169 @@ void ub_msi_unmask_irq(struct irq_data *data) ub_usi_desc_unmask(desc, (u32)BIT(data->irq - desc->irq)); } +static void __iomem *ub_intr_find_one_addr(struct msi_msg *msg, + struct msi_desc *entry, + u8 *is_new) +{ + void __iomem *first_unused_base = NULL; + void __iomem *base; + u32 addr_l, addr_h, valid, i; + u16 first_unused_id; + u32 addr_cnt = entry->ub_intr.addr_num + 1; + + base = ub_addr_desc_base_addr(entry); + + for (i = 0; i < addr_cnt; i++, base += UB_INTR_ADDR_ENTRY_SIZE) { + valid = readl(base + UB_INTR_ADDR_TOKENID) & UB_INTR_ADDR_VALID_MASK; + if (!valid) { + if (!first_unused_base) { + first_unused_base = base; + first_unused_id = i; + } + continue; + } + addr_l = readl(base + UB_INTR_ADDR_ADDR_L); + addr_h = readl(base + UB_INTR_ADDR_ADDR_H); + if (msg->address_lo == addr_l && msg->address_hi == addr_h) { + entry->ub_intr.intr_attrib.addr_index = i; + *is_new = 0; + return base; + } + } + + if (!first_unused_base) + return NULL; + *is_new = 1; + + entry->ub_intr.intr_attrib.addr_index = first_unused_id; + + return first_unused_base; +} + +static bool ub_intr_check_addr_used(struct msi_desc *entry) +{ + void __iomem *base, *vec_addr; + u16 addr_index; + u32 i, vec_cnt; + + base = ub_vector_desc_base_addr(entry); + vec_addr = ub_vector_desc_addr(entry); + vec_cnt = entry->ub_intr.vec_num + 1; + addr_index = entry->ub_intr.intr_attrib.addr_index; + + for (i = 0; i < vec_cnt; i++, base += UB_INTR_VECTOR_ENTRY_SIZE) { + if ((readl(base + UB_INTR_VECTOR_ADDR_INDEX) & UB_INTR_VECTOR_MASK_MASK) || + base == vec_addr) + continue; + + if ((readl(base + UB_INTR_VECTOR_ADDR_INDEX) & + UB_INTR_VECTOR_ADDR_INDEX_MASK) == addr_index) + return true; + } + + return false; +} + +static void ub_int_type1_clear_msg(struct msi_desc *entry, struct msi_msg *msg) +{ + struct ub_entity *uent = msi_desc_to_ub_entity(entry); + + ub_cfg_write_byte(uent, UB_INT_TYPE1_ENABLE, 0); + ub_cfg_write_dword(uent, UB_INT_TYPE1_EN_INT_NUM, 0); + ub_cfg_write_dword(uent, UB_INT_TYPE1_INT_DATA, 0); + ub_cfg_write_dword(uent, UB_INT_TYPE1_INT_ADDR_L, 0); + ub_cfg_write_dword(uent, UB_INT_TYPE1_INT_ADDR_H, 0); +} + +static void ub_int_type2_clear_msg(struct msi_desc *entry, struct msi_msg *msg) +{ + void __iomem *vector_base, *addr_base; + u32 reg_val; + + addr_base = ub_addr_desc_addr(entry); + vector_base = ub_vector_desc_addr(entry); + if (!vector_base || !addr_base) { + WARN_ON(1); + return; + } + + writel(0, vector_base + UB_INTR_VECTOR_ID); + reg_val = readl(vector_base + UB_INTR_VECTOR_ADDR_INDEX) & + ~UB_INTR_VECTOR_ADDR_INDEX_MASK; + writel(reg_val, vector_base + UB_INTR_VECTOR_ADDR_INDEX); + + if (!ub_intr_check_addr_used(entry)) { + writel(0, addr_base + UB_INTR_ADDR_TOKENID); + writel(0, addr_base + UB_INTR_ADDR_DSTEID_0); + writel(0, addr_base + UB_INTR_ADDR_ADDR_L); + writel(0, addr_base + UB_INTR_ADDR_ADDR_H); + } +} + +static void __ub_clear_msi_msg(struct msi_desc *entry, struct msi_msg *msg) +{ + if (entry->ub_intr.intr_attrib.is_type1) + ub_int_type1_clear_msg(entry, msg); + else + ub_int_type2_clear_msg(entry, msg); +} + +static void ub_int_type1_set_msg(struct msi_desc *entry, struct msi_msg *msg) +{ + struct ub_entity *uent = msi_desc_to_ub_entity(entry); + + ub_cfg_write_byte(uent, UB_INT_TYPE1_ENABLE, 1); + ub_cfg_write_dword(uent, UB_INT_TYPE1_EN_INT_NUM, + (u32)ilog2(__roundup_pow_of_two(entry->nvec_used))); + ub_cfg_write_dword(uent, UB_INT_TYPE1_INT_DATA, msg->data); + ub_cfg_write_dword(uent, UB_INT_TYPE1_INT_ADDR_L, msg->address_lo); + ub_cfg_write_dword(uent, UB_INT_TYPE1_INT_ADDR_H, msg->address_hi); +} + +static void ub_int_type2_set_msg(struct msi_desc *entry, struct msi_msg *msg) +{ + struct ub_entity *uent = msi_desc_to_ub_entity(entry); + void __iomem *vector_base, *addr_base; + unsigned long val; + u32 reg_val; + u8 is_new; + + vector_base = ub_vector_desc_addr(entry); + if (!vector_base) { + WARN_ON(1); + return; + } + + addr_base = ub_intr_find_one_addr(msg, entry, &is_new); + if (addr_base) { + writel(msg->data, vector_base + UB_INTR_VECTOR_ID); + reg_val = readl(vector_base + UB_INTR_VECTOR_ADDR_INDEX) & + ~UB_INTR_VECTOR_ADDR_INDEX_MASK; + reg_val |= entry->ub_intr.intr_attrib.addr_index; + writel(reg_val, vector_base + UB_INTR_VECTOR_ADDR_INDEX); + if (is_new) { + writel(msg->address_lo, addr_base + UB_INTR_ADDR_ADDR_L); + writel(msg->address_hi, addr_base + UB_INTR_ADDR_ADDR_H); + writel(ub_get_dst_eid(uent), addr_base + UB_INTR_ADDR_DSTEID_0); + val = uent->tid & UB_INTR_ADDR_TOKENID_MASK; + writel(val, addr_base + UB_INTR_ADDR_TOKENID); + /* valid bit must be placed at the end. */ + set_bit(UB_INTR_ADDR_VALID_BIT, &val); + writel(val, addr_base + UB_INTR_ADDR_TOKENID); + } + } else { + WARN_ON(1); + } +} + +static void __ub_set_msi_msg(struct msi_desc *entry, struct msi_msg *msg) +{ + if (entry->ub_intr.intr_attrib.is_type1) + ub_int_type1_set_msg(entry, msg); + else + ub_int_type2_set_msg(entry, msg); +} + void ub_write_interruptid(struct ub_entity *uent) { if (!uent->intr_type1) @@ -152,4 +323,229 @@ void ub_write_interruptid(struct ub_entity *uent) ub_cfg_write_dword(uent, UB_INT_TYPE1_INT_ID, uent->intr_device_id); } -void __ub_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) {} +void __ub_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) +{ + bool unmasked = false; + + if (!entry->ub_intr.intr_attrib.is_type1) + unmasked = !(entry->ub_intr.intr_attrib.mask); + + if (unmasked) + ub_usi_desc_mask(entry, 0); + + if (msg->address_lo == 0 && msg->address_hi == 0) + __ub_clear_msi_msg(entry, msg); + else + __ub_set_msi_msg(entry, msg); + + if (unmasked) + ub_usi_desc_unmask(entry, 0); + + entry->msg = *msg; + if (entry->write_msi_msg) + entry->write_msi_msg(entry, entry->write_msi_msg_data); +} + +u32 ub_intr_addr_count(struct ub_entity *uent) +{ + u32 addr_num = 0; + int ret; + + ret = ub_cfg_read_word(uent, UB_NUM_OF_INTR_ADDR_TBL, (u16 *)&addr_num); + if (ret) + return 0; + /* + * The value of this field is N, indicating that the interrupt address table + * supported by the function entity supports N+1 interrupt address. + */ + addr_num += 1; + + return addr_num; +} +EXPORT_SYMBOL_GPL(ub_intr_addr_count); + +u32 ub_intr_vec_count(struct ub_entity *uent) +{ + u32 vector_num = 0; + int ret; + + ret = ub_cfg_read_word(uent, UB_NUM_OF_INTR_VECTOR_TBL, (u16 *)&vector_num); + if (ret) + return 0; + /* + * The value of this field is N, indicating that the interrupt vector table + * supported by the function entity supports N+1 interrupt vectors. + */ + vector_num += 1; + + return vector_num; +} +EXPORT_SYMBOL_GPL(ub_intr_vec_count); + +static int ub_prepare_msi_desc(struct ub_entity *uent, struct msi_desc *desc) +{ + void __iomem *vec_desc_addr; + u32 addr_cnt, vec_cnt; + + addr_cnt = ub_intr_addr_count(uent); + if (addr_cnt == 0) + return -EINVAL; + + vec_cnt = ub_intr_vec_count(uent); + if (vec_cnt == 0) + return -EINVAL; + + desc->nvec_used = 1; + desc->ub_intr.intr_attrib.is_type1 = 0; + desc->ub_intr.addr_base = uent->intr_addr_base; + desc->ub_intr.vector_base = uent->intr_vector_base; + desc->ub_intr.addr_num = (u16)(addr_cnt - 1); + desc->ub_intr.vec_num = (u16)(vec_cnt - 1); + + vec_desc_addr = ub_vector_desc_addr(desc); + desc->ub_intr.intr_attrib.mask = !!(readl(vec_desc_addr + UB_INTR_VECTOR_ADDR_INDEX) & + UB_INTR_VECTOR_MASK_MASK); + + return 0; +} + +static int ub_setup_msi_descs(struct ub_entity *uent, + struct ub_usi_entry *entries, int nvec, + struct irq_affinity_desc *masks) +{ + struct irq_affinity_desc *curmsk; + struct msi_desc desc; + int ret = 0, i; + + memset(&desc, 0, sizeof(desc)); + + if (uent->intr_type1) { + desc.ub_intr.intr_attrib.is_type1 = 1; + desc.nvec_used = nvec; + desc.affinity = masks; + ret = ub_cfg_read_dword(uent, UB_INT_TYPE1_INT_MASK, + &desc.ub_intr.intr_attrib.mask); + if (ret) + return ret; + + return msi_insert_msi_desc(&uent->dev, &desc); + } + + for (i = 0, curmsk = masks; i < nvec; i++, curmsk++) { + desc.msi_index = entries ? entries[i].entry : i; + desc.ub_intr.intr_attrib.entry_nr = desc.msi_index; + desc.affinity = masks ? curmsk : NULL; + ret = ub_prepare_msi_desc(uent, &desc); + if (ret) + break; + ret = msi_insert_msi_desc(&uent->dev, &desc); + if (ret) + break; + } + return ret; +} + +static int ub_setup_intr_irqs(struct ub_entity *uent, int nvec) +{ + struct irq_domain *domain; + + domain = dev_get_msi_domain(&uent->dev); + if (domain && irq_domain_is_hierarchy(domain)) + return msi_domain_alloc_irqs_all_locked(&uent->dev, 0, nvec); + + pr_info("default irq alloc to be done...\n"); + + return -ENOSPC; +} + +static void ub_update_entries(struct ub_entity *uent, struct ub_usi_entry *entries) +{ + struct msi_desc *desc; + + if (entries) { + msi_for_each_desc(desc, &uent->dev, MSI_DESC_ALL) { + entries->vector = desc->irq; + entries++; + } + } +} + +static void ub_msi_teardown_msi_irqs(struct ub_entity *uent) +{ + struct irq_domain *domain; + + domain = dev_get_msi_domain(&uent->dev); + if (domain && irq_domain_is_hierarchy(domain)) + msi_domain_free_irqs_all_locked(&uent->dev, 0); + else + pr_info("default irq free to be done.\n"); +} + +static void free_msi_irqs(struct ub_entity *uent) +{ + ub_msi_teardown_msi_irqs(uent); + + if (uent->intr_addr_base) { + iounmap(uent->intr_addr_base); + uent->intr_addr_base = NULL; + } + if (uent->intr_vector_base) { + iounmap(uent->intr_vector_base); + uent->intr_vector_base = NULL; + } +} + +int usi_setup_interrupts(struct ub_entity *uent, struct ub_usi_entry *entries, + int nvec, struct irq_affinity *affd) +{ + struct irq_affinity_desc *masks = NULL; + int ret; + + if (affd) + masks = irq_create_affinity_masks(nvec, affd); + + msi_lock_descs(&uent->dev); + ret = ub_setup_msi_descs(uent, entries, nvec, masks); + if (ret) + goto out_free; + + ret = ub_setup_intr_irqs(uent, nvec); + if (ret) + goto out_free; + + ub_update_entries(uent, entries); + goto out_unlock; + +out_free: + free_msi_irqs(uent); +out_unlock: + msi_unlock_descs(&uent->dev); + kfree(masks); + return ret; +} +EXPORT_SYMBOL_GPL(usi_setup_interrupts); + +void ub_disable_intr(struct ub_entity *uent) +{ + if (!uent || !uent->intr_enabled) + return; + + if (uent->intr_type1) { + ub_cfg_write_byte(uent, UB_INT_TYPE1_ENABLE, 0); + ub_cfg_write_dword(uent, UB_INT_TYPE1_INT_MASK, GENMASK(31, 0)); + } else { + ub_cfg_write_byte(uent, UB_INT_MASK, 1); + ub_cfg_write_byte(uent, UB_INT_EN, 0); + } + + uent->intr_enabled = 0; + ub_interrupt_id_free(uent); + free_msi_irqs(uent); +} +EXPORT_SYMBOL_GPL(ub_disable_intr); + +int ub_setup_msi_context(struct ub_entity *uent) +{ + return msi_setup_device_data(&uent->dev); +} +EXPORT_SYMBOL_GPL(ub_setup_msi_context); diff --git a/include/linux/msi.h b/include/linux/msi.h index eb60253e7330..578fe61c6523 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -728,6 +728,13 @@ void __ub_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); int ub_interrupt_id_alloc(struct ub_entity *uent); void ub_interrupt_id_free(struct ub_entity *uent); bool ub_setup_usi_device_domain(struct ub_entity *uent, unsigned int hwsize); +void __iomem *ub_vector_desc_addr(struct msi_desc *desc); +u32 ub_intr_addr_count(struct ub_entity *uent); +u32 ub_intr_vec_count(struct ub_entity *uent); +int usi_setup_interrupts(struct ub_entity *uent, + struct ub_usi_entry *entries, int nvec, + struct irq_affinity *affd); +int ub_setup_msi_context(struct ub_entity *uent); #endif /* CONFIG_UB_UBUS_USI */ #endif /* LINUX_MSI_H */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index d8af6aa007e7..70b95e1141f4 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -14,6 +14,7 @@ #include #include #include +#include #define UB_ENTITY(v, d) \ .vendor = (v), .device = (d), \ @@ -469,6 +470,33 @@ int ub_reset_entity(struct ub_entity *ent); */ int ub_device_reset(struct ub_entity *ent); +/* Only for ubus module */ +unsigned int ub_irq_calc_affinity_vectors(unsigned int minvec, + unsigned int maxvec, + const struct irq_affinity *affd); + +/** + * ub_disable_intr() - Free entity interrupt vectors. + * @uent: UB entity. + * + * Free interrupt vectors of the device. + * + * Context: Any context. + */ +void ub_disable_intr(struct ub_entity *uent); + +/** + * ub_intr_vec_count() - Interrupt Vectors Supported by a entity. + * @uent: UB entity. + * + * Querying the Number of Interrupt Vectors Supported by a entity. + * For interrupt type 2. + * + * Context: Any context. + * Return: Number of Interrupts Supported if success, or 0 if failed. + */ +u32 ub_intr_vec_count(struct ub_entity *uent); + /** * ub_cfg_read_byte() - 1 byte configuration access read. * @uent: UB entity. @@ -656,6 +684,15 @@ static inline int ub_reset_entity(struct ub_entity *uent) { return -ENODEV; } static inline int ub_device_reset(struct ub_entity *uent) { return -ENODEV; } +static inline unsigned int +ub_irq_calc_affinity_vectors(unsigned int minvec, unsigned int maxvec, + const struct irq_affinity *affd) +{ + return maxvec; +} +static inline void ub_disable_intr(struct ub_entity *uent) {} +static inline u32 ub_intr_vec_count(struct ub_entity *uent) +{ return 0; } static inline int __ub_register_driver(struct ub_driver *drv, struct module *owner, const char *mod_name) -- Gitee From 1545792bfd6762df057e7cc4a4e242f0c6d31368 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Sat, 20 Sep 2025 10:53:37 +0800 Subject: [PATCH 032/214] ub:ubus: Add MSI capability for UBUS driver ANBZ: #45460 commit 1faf759d302206a8387d685808928c228c9aa306 openEuler. Add MSI capability for UBUS driver, providing type1 and type2 interrupts. Type1 interrupt is mainly used for ub bus controller, and type2 interrupt is used for ub device except ub bus controller. Finally, export some interfaces UBUS USI for UB kernel. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/cap.c | 2 + drivers/ub/ubus/interrupt.c | 316 ++++++++++++++++++++++++++++++++++ drivers/ub/ubus/interrupt.h | 11 ++ drivers/ub/ubus/ubus_entity.c | 2 + include/ub/ubus/ubus.h | 87 ++++++++++ 6 files changed, 419 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/interrupt.c create mode 100644 drivers/ub/ubus/interrupt.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index ac8ecfd3c0ce..d90034f0003f 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -4,6 +4,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o -ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o +ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/cap.c b/drivers/ub/ubus/cap.c index 03f719422a2a..a9073de22fbe 100644 --- a/drivers/ub/ubus/cap.c +++ b/drivers/ub/ubus/cap.c @@ -6,6 +6,7 @@ #define pr_fmt(fmt) "ubus cap: " fmt #include "ubus.h" +#include "interrupt.h" #define DW_CHECK 3 @@ -231,6 +232,7 @@ void ub_init_capabilities(struct ub_entity *uent) init_cfg1_cap: /* cfg1 caps */ + ub_intr_init(uent); uent->reset_fn = 1; } diff --git a/drivers/ub/ubus/interrupt.c b/drivers/ub/ubus/interrupt.c new file mode 100644 index 000000000000..d1ca667250fe --- /dev/null +++ b/drivers/ub/ubus/interrupt.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus interrupt: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ubus.h" + +enum ub_intr_tbl_type { + UB_INTR_VECTOR_TBL, + UB_INTR_ADDR_TBL +}; + +struct ub_intr_vector_tbl { + unsigned int vector; + unsigned int addr_index : 16; + unsigned int mask : 1; + unsigned int reserved : 15; +}; + +struct ub_intr_addr_tbl { + unsigned int intr_addr_l; + unsigned int intr_addr_h; + unsigned int token_id : 20; + unsigned int valid : 1; + unsigned int reserved0 : 11; + unsigned int dst_eid_0; + unsigned int dst_eid_1; + unsigned int dst_eid_2; + unsigned int dst_eid_3; +}; + +void ub_intr_init(struct ub_entity *uent) +{ + if (uent->cfg1_bitmap[0] & UB_INT_TYPE2_CAP_BIT) + uent->intr_type1 = 0; + else if (uent->cfg1_bitmap[0] & UB_INT_TYPE1_CAP_BIT) + uent->intr_type1 = 1; + else + uent->no_intr = 1; +} + +u32 ub_int_type1_vec_count(struct ub_entity *uent) +{ + u16 vector_num = 0; + int ret; + + ret = ub_cfg_read_word(uent, UB_INT_TYPE1_SUP_INT_NUM, &vector_num); + if (ret) + return 0; + + /* Mapping between the number of bitmaps and vectors defined in the protocol */ + vector_num = 1 << vector_num; + + return vector_num; +} +EXPORT_SYMBOL_GPL(ub_int_type1_vec_count); + +static void __iomem *ub_intr_map_region(struct ub_entity *uent, + enum ub_intr_tbl_type type, int nr_entries) +{ + u32 addr_reg_l, addr_reg_h, offset_l, offset_h; + resource_size_t tb_phys_addr; + unsigned long flags, sz; + u64 offset; + int ret; + + if (nr_entries <= 0) + return NULL; + if (type == UB_INTR_VECTOR_TBL) { + addr_reg_l = UB_INT_VECTOR_TBL_SA_L; + addr_reg_h = UB_INT_VECTOR_TBL_SA_H; + sz = (unsigned long)(nr_entries * UB_INTR_VECTOR_ENTRY_SIZE); + } else { + addr_reg_l = UB_INT_ADDR_TBL_SA_L; + addr_reg_h = UB_INT_ADDR_TBL_SA_H; + sz = (unsigned long)(nr_entries * UB_INTR_ADDR_ENTRY_SIZE); + } + + ret = ub_cfg_read_dword(uent, addr_reg_l, &offset_l); + ret |= ub_cfg_read_dword(uent, addr_reg_h, &offset_h); + if (ret) { + ub_err(uent, "read addr register failed, ret=%d\n", ret); + return NULL; + } + + offset = ((u64)offset_h << 32) | offset_l; + + flags = ub_resource_flags(uent, 0); + if (!flags || (flags & IORESOURCE_UNSET)) + return NULL; + + if (offset + sz > ub_resource_len(uent, 0)) { + ub_err(uent, "UB interrupt table(type:%u) off:%#llx + sz:%#lx is out of range!\n", + type, offset, sz); + return NULL; + } + tb_phys_addr = ub_resource_start(uent, 0) + offset; + + return ioremap(tb_phys_addr, sz); +} + +static int int_type1_capability_init(struct ub_entity *uent, + struct ub_usi_entry *entries, int nvec, + struct irq_affinity *affd) +{ + int ret; + + uent->intr_enabled = 1; + + ret = usi_setup_interrupts(uent, entries, nvec, affd); + if (ret) + goto out_disable; + + ub_cfg_write_byte(uent, UB_INT_TYPE1_ENABLE, 1); + return 0; + +out_disable: + uent->intr_enabled = 0; + + return ret; +} + +static int int_type2_capability_init(struct ub_entity *uent, + struct ub_usi_entry *entries, int nvec, + struct irq_affinity *affd) +{ + void __iomem *vector_base, *addr_base; + int vector_num, addr_num; + int ret; + + uent->intr_enabled = 1; + + vector_num = (int)ub_intr_vec_count(uent); + /* Request & Map UB interrupt table region */ + vector_base = ub_intr_map_region(uent, UB_INTR_VECTOR_TBL, vector_num); + if (!vector_base) { + ret = -ENOMEM; + goto out_disable; + } + addr_num = (int)ub_intr_addr_count(uent); + addr_base = ub_intr_map_region(uent, UB_INTR_ADDR_TBL, addr_num); + if (!addr_base) { + iounmap(vector_base); + ret = -ENOMEM; + goto out_disable; + } + uent->intr_vector_base = vector_base; + uent->intr_addr_base = addr_base; + + ret = usi_setup_interrupts(uent, entries, nvec, affd); + if (ret) + goto out_disable; + + ub_cfg_write_byte(uent, UB_INT_MASK, 0); + ub_cfg_write_byte(uent, UB_INT_EN, 1); + + return 0; + +out_disable: + uent->intr_enabled = 0; + + return ret; +} + +static int ub_msi_supported(struct ub_entity *uent, int nvec) +{ + if (!uent || uent->no_intr || nvec < 1) + return 0; + + return 1; +} + +static int ub_usi_entry_invalid_check(struct ub_usi_entry *ents, + int nvec, int nr_ents) +{ + int i, j; + + if (!ents) + return 0; + + for (i = 0; i < nvec; i++) { + if (ents[i].entry >= nr_ents) + return -EINVAL; + + for (j = i + 1; j < nvec; j++) + if (ents[i].entry == ents[j].entry) + return -EINVAL; + } + + return 0; +} + +static int __ub_enable_usi_range(struct ub_entity *uent, + struct ub_usi_entry *entries, int minvec, + int maxvec, struct irq_affinity *affd) +{ + int hwsize, nvec = maxvec; + int ret; + + if (maxvec < minvec) + return -ERANGE; + + if (!ub_msi_supported(uent, nvec)) + return -EINVAL; + + if (uent->intr_type1) + hwsize = (int)ub_int_type1_vec_count(uent); + else + hwsize = (int)ub_intr_vec_count(uent); + if (hwsize == 0) { + ub_err(uent, "int vector cnt is zero.\n"); + return -ENOSPC; + } + + ret = ub_usi_entry_invalid_check(entries, nvec, hwsize); + if (ret) + return ret; + + if (hwsize < nvec) + nvec = hwsize; + if (nvec < minvec) + return -ENOSPC; + + ret = ub_setup_msi_context(uent); + if (ret) + return ret; + + if (!ub_setup_usi_device_domain(uent, hwsize)) { + ub_err(uent, "ub setup device domain failed.\n"); + return -ENODEV; + } + + if (affd) { + nvec = ub_irq_calc_affinity_vectors(minvec, nvec, affd); + if (nvec < minvec) { + ub_err(uent, "irq calc affd failed.\n"); + return -ENOSPC; + } + } + + if (uent->intr_type1) + ret = int_type1_capability_init(uent, entries, nvec, affd); + else + ret = int_type2_capability_init(uent, entries, nvec, affd); + if (ret) + return ret; + + return nvec; +} + +int ub_alloc_irq_vectors_affinity(struct ub_entity *uent, unsigned int min_vecs, + unsigned int max_vecs, unsigned int flags, + struct irq_affinity *affd) +{ + struct irq_affinity msi_default_affd = {}; + + if (flags & UB_IRQ_AFFINITY) { + if (!affd) + affd = &msi_default_affd; + } else { + if (WARN_ON(affd)) + affd = NULL; + } + + return __ub_enable_usi_range(uent, NULL, min_vecs, max_vecs, affd); +} +EXPORT_SYMBOL_GPL(ub_alloc_irq_vectors_affinity); + +int ub_irq_vector(struct ub_entity *uent, unsigned int nr) +{ + unsigned int irq; + + if (!uent) + return -EINVAL; + + if (uent->intr_enabled) { + irq = msi_get_virq(&uent->dev, nr); + return irq ? (int)irq : -EINVAL; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(ub_irq_vector); + +const struct cpumask *ub_irq_get_affinity(struct ub_entity *dev, int nr) +{ + int idx, irq = ub_irq_vector(dev, nr); + struct msi_desc *desc; + + if (WARN_ON_ONCE(irq <= 0)) + return NULL; + + desc = irq_get_msi_desc(irq); + /* Interrupts can be allocated without affinity descriptor */ + if (!desc->affinity) + return NULL; + + /* + * INT_TYPE1 has a mask array in the descriptor. + * INT_TYPE2 has a single mask. + */ + idx = dev->intr_type1 ? nr : 0; + return &desc->affinity[idx].mask; +} +EXPORT_SYMBOL_GPL(ub_irq_get_affinity); diff --git a/drivers/ub/ubus/interrupt.h b/drivers/ub/ubus/interrupt.h new file mode 100644 index 000000000000..9e7a5173793a --- /dev/null +++ b/drivers/ub/ubus/interrupt.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __INTERRUPT_H__ +#define __INTERRUPT_H__ + +void ub_intr_init(struct ub_entity *uent); + +#endif /* __INTERRUPT_H__ */ diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index c17a5e736eb0..f6697426b332 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -357,6 +357,8 @@ void ub_entity_add(struct ub_entity *uent, void *ctx) list_add_tail(&uent->node, list); up_write(&ub_bus_sem); + dev_set_msi_domain(&uent->dev, uent->ubc->dev.msi.domain); + uent->match_driver = false; ret = device_add(&uent->dev); WARN_ON(ret < 0); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 70b95e1141f4..b58ee274e479 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -497,6 +497,78 @@ void ub_disable_intr(struct ub_entity *uent); */ u32 ub_intr_vec_count(struct ub_entity *uent); +/** + * ub_int_type1_vec_count() - Interrupt Vectors Supported by a entity. + * @uent: UB entity. + * + * Querying the Number of Interrupt Vectors Supported by a entity. + * For interrupt type 1. + * + * Context: Any context. + * Return: Number of Interrupts Supported if success, or 0 if failed. + */ +u32 ub_int_type1_vec_count(struct ub_entity *uent); + +#define UB_IRQ_AFFINITY (1 << 0) /* Auto-assign affinity */ +/** + * ub_alloc_irq_vectors_affinity() - Allocate multiple entity interrupt vectors. + * @uent: UB entity. + * @min_vecs: minimum required number of vectors (must be >= 1). + * @max_vecs: maximum desired number of vectors. + * + * @flags: allocation flags(can be 0): + * + * * %UB_IRQ_AFFINITY Auto-manage IRQs affinity by spreading + * the vectors around available CPUs + * + * @affd: affinity requirements (can be %NULL). + * + * Allocate interrupt vectors for the entity and set the affinity. + * + * Context: Any context. + * Return: the number of vectors allocated (which might be smaller than + * @max_vecs) if success, or a negative error code on error. If less than + * @min_vecs interrupt vectors are available for @uent the function will + * fail with -ENOSPC + */ +int ub_alloc_irq_vectors_affinity(struct ub_entity *uent, unsigned int min_vecs, + unsigned int max_vecs, unsigned int flags, + struct irq_affinity *affd); +static inline int ub_alloc_irq_vectors(struct ub_entity *uent, + unsigned int min_vecs, + unsigned int max_vecs) +{ + return ub_alloc_irq_vectors_affinity(uent, min_vecs, max_vecs, 0, NULL); +} + +/** + * ub_irq_vector() - Obtaining the Linux IRQ number. + * @uent: UB entity. + * @nr: Interrupt vector. + * + * Translate from Interrupt Vectors to Linux IRQ number. + * + * Context: Any context. + * Return: IRQ number if success, or %-EINVAL if failed. + */ +int ub_irq_vector(struct ub_entity *uent, unsigned int nr); + +/** + * ub_irq_get_affinity() - Get a entity interrupt vector affinity + * @uent: the UB entity to operate on + * @nr: entity-relative interrupt vector index (0-based); has different + * meanings, depending on interrupt mode: + * + * * INTR_TYPE2 the index in the USI vector table + * * INTR_TYPE1 the index of the enabled USI vectors + * + * Return: USI vector affinity, NULL if @nr is out of range or if + * the USI vector was allocated without explicit affinity + * requirements (e.g., by ub_alloc_irq_vectors(), or + * ub_alloc_irq_vectors_affinity() without the %UB_IRQ_AFFINITY flag). + */ +const struct cpumask *ub_irq_get_affinity(struct ub_entity *uent, int nr); + /** * ub_cfg_read_byte() - 1 byte configuration access read. * @uent: UB entity. @@ -693,6 +765,21 @@ ub_irq_calc_affinity_vectors(unsigned int minvec, unsigned int maxvec, static inline void ub_disable_intr(struct ub_entity *uent) {} static inline u32 ub_intr_vec_count(struct ub_entity *uent) { return 0; } +static inline u32 ub_int_type1_vec_count(struct ub_entity *uent) +{ return 0; } +static inline int +ub_alloc_irq_vectors_affinity(struct ub_entity *uent, unsigned int min_vecs, + unsigned int max_vecs, unsigned int flags, + struct irq_affinity *affd) +{ return -ENODEV; } +static inline int ub_alloc_irq_vectors(struct ub_entity *uent, + unsigned int min_vecs, + unsigned int max_vecs) +{ return -ENODEV; } +static inline int ub_irq_vector(struct ub_entity *uent, unsigned int nr) +{ return -EINVAL; } +static inline const struct cpumask * +ub_irq_get_affinity(struct ub_entity *uent, int nr) { return cpu_possible_mask; } static inline int __ub_register_driver(struct ub_driver *drv, struct module *owner, const char *mod_name) -- Gitee From 12c58e70c0c3fa2dfd1012b741028a403dcc8a81 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Sat, 20 Sep 2025 09:52:26 +0800 Subject: [PATCH 033/214] ub:ubus: Support for enabling and disabling ue ANBZ: #45460 commit 0f9e2dbe888d316edb6b5169220505a3c83b4d73 openEuler. Implement dynamic creation and destruction of ue. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/ubus_driver.c | 12 +++ drivers/ub/ubus/ubus_entity.c | 181 +++++++++++++++++++++++++++++++++- drivers/ub/ubus/ubus_entity.h | 3 + include/ub/ubus/ubus.h | 73 ++++++++++++++ 4 files changed, 268 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index 9e504806fd33..da0d5030ee5b 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -18,6 +18,11 @@ #include "ubus_config.h" #include "ubus_controller.h" #include "ubus_inner.h" +#include "ubus_entity.h" + +bool entity_flex_en; +module_param(entity_flex_en, bool, 0444); +MODULE_PARM_DESC(entity_flex_en, "Entity Flexible enable: default: 0"); DECLARE_RWSEM(ub_bus_sem); @@ -485,6 +490,11 @@ static struct attribute *ub_drv_attrs[] = { }; ATTRIBUTE_GROUPS(ub_drv); +static int ub_bus_num_ue(struct device *dev) +{ + return ub_num_ue(to_ub_entity(dev)); +} + void ub_bus_type_init(void) { ub_bus_type.match = ub_bus_match; @@ -495,6 +505,7 @@ void ub_bus_type_init(void) ub_bus_type.dev_groups = ub_entity_groups; ub_bus_type.bus_groups = ub_bus_groups; ub_bus_type.drv_groups = ub_drv_groups; + ub_bus_type.num_vf = ub_bus_num_ue; } void ub_bus_type_uninit(void) @@ -507,6 +518,7 @@ void ub_bus_type_uninit(void) ub_bus_type.dev_groups = NULL; ub_bus_type.bus_groups = NULL; ub_bus_type.drv_groups = NULL; + ub_bus_type.num_vf = NULL; } int ub_host_probe(void) diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index f6697426b332..43fb14040871 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -6,6 +6,7 @@ #define pr_fmt(fmt) "ubus entity: " fmt #include +#include #include "ubus.h" #include "sysfs.h" @@ -20,6 +21,7 @@ #include "ubus_driver.h" #include "ubus_inner.h" #include "cap.h" +#include "ubus_entity.h" /* * Entity lifecycle @@ -43,6 +45,7 @@ struct ub_entity *ub_alloc_ent(void) INIT_LIST_HEAD(&uent->node); INIT_LIST_HEAD(&uent->mue_list); + INIT_LIST_HEAD(&uent->ue_list); INIT_LIST_HEAD(&uent->cna_list); uent->dev.type = &ub_dev_type; @@ -312,12 +315,22 @@ static void ub_unconfigure_ent(struct ub_entity *uent) { } +static int ub_ue_sort_by_ent_idx(void *priv, const struct list_head *a, + const struct list_head *b) +{ + struct ub_entity *uent_a = container_of(a, struct ub_entity, node); + struct ub_entity *uent_b = container_of(b, struct ub_entity, node); + + return uent_a->entity_idx > uent_b->entity_idx; +} + static void ub_release_ent(struct device *dev); void ub_entity_add(struct ub_entity *uent, void *ctx) { struct ub_bus_controller *ubc; struct list_head *list; struct ub_entity *pue; + u8 need_sort = 0; int ret, node; if (!uent || !ctx) @@ -344,10 +357,15 @@ void ub_entity_add(struct ub_entity *uent, void *ctx) ubc = (struct ub_bus_controller *)ctx; node = ub_ubc_to_node(ubc); list = &ubc->devs; - } else { + } else if (uent->is_mue) { pue = (struct ub_entity *)ctx; node = dev_to_node(&pue->dev); list = &pue->mue_list; + } else { + pue = (struct ub_entity *)ctx; + node = dev_to_node(&pue->dev); + list = &pue->ue_list; + need_sort = 1; } set_dev_node(&uent->dev, node); @@ -355,6 +373,8 @@ void ub_entity_add(struct ub_entity *uent, void *ctx) down_write(&ub_bus_sem); list_add_tail(&uent->node, list); + if (need_sort) + list_sort(NULL, list, ub_ue_sort_by_ent_idx); up_write(&ub_bus_sem); dev_set_msi_domain(&uent->dev, uent->ubc->dev.msi.domain); @@ -428,6 +448,9 @@ void ub_stop_ent(struct ub_entity *uent) return; ub_entity_assign_priv_flag(uent, UB_ENTITY_START, false); + /* Stop ue in mue, when uent is not mue, ue_list is NULL */ + list_for_each_entry_safe_reverse(ent, tmp, &uent->ue_list, node) + ub_stop_ent(ent); /* Stop mue in primary dev, when uent is entN, mue_list is NULL */ list_for_each_entry_safe_reverse(ent, tmp, &uent->mue_list, node) ub_stop_ent(ent); @@ -444,6 +467,9 @@ void ub_remove_ent(struct ub_entity *uent) if (!uent->dev.kobj.parent) return; + /* Remove ue in mue, when uent is not mue, ue_list is NULL */ + list_for_each_entry_safe_reverse(ent, tmp, &uent->ue_list, node) + ub_remove_ent(ent); /* Remove mue in primary dev, when uent is entN, mue_list is NULL */ list_for_each_entry_safe_reverse(ent, tmp, &uent->mue_list, node) ub_remove_ent(ent); @@ -699,3 +725,156 @@ static void ub_disable_mues(struct ub_entity *pue) list_for_each_entry_safe_reverse(mue, tmp, &pue->mue_list, node) ub_disable_ent(mue); } + +void ub_disable_ues(struct ub_entity *mue); +static int ub_enable_ues(struct ub_entity *mue, int nums) +{ + int ret; + int i; + + if (nums > mue->total_ues) + return -EINVAL; + + for (i = 0; i < nums; i++) { + ret = ub_enable_ent(mue, mue->uem.start_entity_idx + i, 0, + NULL); + if (ret) + goto failed; + } + mue->num_ues = nums; + return 0; +failed: + ub_disable_ues(mue); + return ret; +} + +void ub_disable_ues(struct ub_entity *mue) +{ + struct ub_entity *ue, *tmp; + u16 pool_ues = 0; + + list_for_each_entry_safe_reverse(ue, tmp, &mue->ue_list, + node) { + if (is_p_device(ue) || is_p_idevice(ue)) + pool_ues++; + else + ub_disable_ent(ue); + } + + mue->num_ues = pool_ues; +} + +static int ub_check_ue_para(struct ub_entity *pue, int entity_idx) +{ + if (!pue->is_mue || !pue->total_ues) { + ub_err(pue, "It's not mue or ues 0.\n"); + return -EINVAL; + } + + if (entity_idx < pue->uem.start_entity_idx || entity_idx > pue->uem.end_entity_idx) { + ub_err(pue, "Entity idx is err, start=%d, pre=%d, end=%d.\n", + pue->uem.start_entity_idx, entity_idx, pue->uem.end_entity_idx); + return -EINVAL; + } + + return 0; +} + +/* ub_enable_ue does not support parallel execution */ +int ub_enable_ue(struct ub_entity *pue, int entity_idx) +{ + struct ub_entity *ue; + int ret; + + if (!pue) + return -EINVAL; + + ret = ub_check_ue_para(pue, entity_idx); + if (ret) + return ret; + + list_for_each_entry(ue, &pue->ue_list, node) + if (ue->entity_idx == entity_idx) { + ub_err(ue, "entity_idx[%d] already exists, eid=%#05x\n", + entity_idx, ue->eid); + return -EEXIST; + } + + ret = ub_enable_ent(pue, entity_idx, 0, NULL); + if (ret) + return ret; + + pue->num_ues += 1; + + return ret; +} +EXPORT_SYMBOL_GPL(ub_enable_ue); + +int ub_disable_ue(struct ub_entity *pue, int entity_idx) +{ + struct ub_entity *vd_dev; + int ret; + + if (!pue) + return -EINVAL; + + ret = ub_check_ue_para(pue, entity_idx); + if (ret) + return ret; + + list_for_each_entry(vd_dev, &pue->ue_list, node) + if (vd_dev->entity_idx == entity_idx) { + ub_disable_ent(vd_dev); + pue->num_ues -= 1; + return 0; + } + + ub_err(pue, "No matching entity_idx[%d] found\n", entity_idx); + + return -ENODEV; +} +EXPORT_SYMBOL_GPL(ub_disable_ue); + +bool ub_get_entity_flex_en(void) +{ + return entity_flex_en; +} +EXPORT_SYMBOL_GPL(ub_get_entity_flex_en); + +int ub_enable_entities(struct ub_entity *uent, int nums) +{ + if (!uent) + return -EINVAL; + + if (!uent->is_mue) { + ub_err(uent, "It's not mue.\n"); + return -EINVAL; + } + + return ub_enable_ues(uent, nums); +} +EXPORT_SYMBOL_GPL(ub_enable_entities); + +void ub_disable_entities(struct ub_entity *uent) +{ + if (!uent) + return; + + if (!uent->is_mue) + return; + + ub_disable_ues(uent); +} +EXPORT_SYMBOL_GPL(ub_disable_entities); + +int ub_num_ue(struct ub_entity *uent) +{ + if (!uent) + return -EINVAL; + + if (!uent->is_mue) + return 0; + + return uent->num_ues; +} +EXPORT_SYMBOL_GPL(ub_num_ue); diff --git a/drivers/ub/ubus/ubus_entity.h b/drivers/ub/ubus/ubus_entity.h index d463269318fe..8f4c0fb4bcc5 100644 --- a/drivers/ub/ubus/ubus_entity.h +++ b/drivers/ub/ubus/ubus_entity.h @@ -6,6 +6,8 @@ #ifndef __UBUS_ENTITY_H__ #define __UBUS_ENTITY_H__ +extern bool entity_flex_en; + struct ub_entity *ub_alloc_ent(void); int ub_setup_ent(struct ub_entity *uent); void ub_entity_add(struct ub_entity *uent, void *ctx); @@ -13,6 +15,7 @@ void ub_start_ent(struct ub_entity *uent); void ub_remove_ent(struct ub_entity *uent); void ub_stop_entities(void); void ub_remove_entities(void); +int ub_num_ue(struct ub_entity *uent); void ub_disable_ent(struct ub_entity *uent); #endif /* __UBUS_ENTITY_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index b58ee274e479..db608eebd9d7 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -165,6 +165,7 @@ struct ub_entity { u16 num_ues; struct ue_map uem; struct list_head mue_list; /* management ub entity list */ + struct list_head ue_list; /* entity list in management ub entity */ /* entity topology info */ struct list_head node; @@ -361,6 +362,69 @@ extern struct bus_type ub_bus_type; void ub_iommu_fwnode_handle_set(const struct fwnode_handle *fwnode); const struct fwnode_handle *ub_iommu_fwnode_handle_get(void); +/** + * ub_enable_entities() - Enable ues of mue in batches. + * @pue: UB mue. + * @nums: Number of enabled entities. + * + * Create ues in batches, initialize them, and add them to the system. + * + * Context: Any context. + * Return: 0 if success, or %-EINVAL if @pue type is not mue or nums over + * mue's total ue nums, or %-ENOMEM if the system is out of memory, + * or other failed negative values. + */ +int ub_enable_entities(struct ub_entity *pue, int nums); + +/** + * ub_disable_entities() - Disable ues of mue in batches. + * @pue: UB mue. + * + * Remove all enabled ues under the mue from the system. + * + * Context: Any context. + */ +void ub_disable_entities(struct ub_entity *pue); + +/** + * ub_enable_ue() - Enable a single ue. + * @pue: UB mue. + * @entity_idx: Number of the entity to be enabled. + * + * Create a specified ue under mue, initialize the ue, + * and add it to the system. + * + * Context: Any context. + * Return: 0 if success, or %-EINVAL if @pue type is not mue or @entity_idx + * is no longer in the ue range of mue, or %-EEXIST if entity has been + * enabled, or other failed negative values. + */ +int ub_enable_ue(struct ub_entity *pue, int entity_idx); + +/** + * ub_disable_ue() - Disable a single ue. + * @pue: UB mue. + * @entity_idx: Number of the entity to be disabled. + * + * Remove a specified ue. + * + * Context: Any context. + * Return: 0 if success, or %-EINVAL if @pue type is not mue or @entity_idx + * is no longer in the ue range of mue, or %-ENODEV if entity hasn't + * been enabled. + */ +int ub_disable_ue(struct ub_entity *pue, int entity_idx); + +/** + * ub_get_entity_flex_en() - Acquire the capability to enable flexible functionality. + * + * Return whether flexible enabling of entity is supported. + * + * Context: Any context. + * Return: true if support, or false if not support. + */ +bool ub_get_entity_flex_en(void); + /** * ub_get_dst_eid() - Obtain the Dest EID of the entity. * @uent: UB entity. @@ -711,6 +775,15 @@ void ub_stop_and_remove_ent(struct ub_entity *uent); static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) { return NULL; } static inline void ub_entity_put(struct ub_entity *uent) {} +static inline int ub_enable_entities(struct ub_entity *pue, int nums) +{ return -ENODEV; } +static inline void ub_disable_entities(struct ub_entity *pue) {} +static inline int ub_enable_ue(struct ub_entity *pue, int entity_idx) +{ return -ENODEV; } +static inline int ub_disable_ue(struct ub_entity *pue, int entity_idx) +{ return -ENODEV; } +static inline bool ub_get_entity_flex_en(void) +{ return false; } static inline int ub_get_dst_eid(struct ub_entity *uent) { return -ENODEV; } static inline void __iomem * -- Gitee From 3c4d43a8fc1430d122710c081422003e25fa8e4e Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Sat, 20 Sep 2025 10:23:15 +0800 Subject: [PATCH 034/214] ub:ubus: Support configuration of device information interface ANBZ: #45460 commit ce21d74812eca9693618fb52c84647b66f4889d8 openEuler. Provide entity enablement, host information configuration, and device stop flow interface. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/reset.c | 2 + drivers/ub/ubus/ubus.h | 1 + drivers/ub/ubus/ubus_controller.h | 4 +- drivers/ub/ubus/ubus_entity.c | 174 ++++++++++++++++++++++++++++++ include/ub/ubus/ubus.h | 69 ++++++++++++ 5 files changed, 249 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubus/reset.c b/drivers/ub/ubus/reset.c index 1aaa787aec90..4b7d86624e24 100644 --- a/drivers/ub/ubus/reset.c +++ b/drivers/ub/ubus/reset.c @@ -218,6 +218,8 @@ int ub_reset_entity(struct ub_entity *dev) return ret; } + ub_entity_enable(dev, 0); + rc = ub_elr(dev); ub_restore_state(dev); diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index b2463fccee7a..e25235f4003f 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -43,6 +43,7 @@ enum ub_entity_type { #define UB_ENTITY_DETACHED 1 /* Flag indicate uent is detached */ #define UB_ENTITY_ROUTE_UPDATED 2 /* Flag indicate uent's route is updated */ #define UB_ENTITY_SETUP 4 /* Flag indicate uent is setup */ +#define UB_ENTITY_ACTIVE 5 /* Flag indicate uent is in normal or disable state */ static inline void ub_entity_assign_priv_flag(struct ub_entity *uent, int bit, bool flag) diff --git a/drivers/ub/ubus/ubus_controller.h b/drivers/ub/ubus/ubus_controller.h index 532307abbff0..31a79840966d 100644 --- a/drivers/ub/ubus/ubus_controller.h +++ b/drivers/ub/ubus/ubus_controller.h @@ -7,7 +7,9 @@ #define __UBUS_CONTROLLER_H__ struct ub_bus_controller; -struct ub_bus_controller_ops {}; +struct ub_bus_controller_ops { + int (*entity_enable)(struct ub_entity *uent, u8 enable); +}; struct ub_bus_controller *ub_find_bus_controller_by_cna(u32 cna); void ub_bus_controllers_remove(void); diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index 43fb14040871..53842779275e 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -878,3 +878,177 @@ int ub_num_ue(struct ub_entity *uent) return uent->num_ues; } EXPORT_SYMBOL_GPL(ub_num_ue); + +void ub_entity_enable(struct ub_entity *uent, u8 enable) +{ + int ret; + + if (!uent) + return; + + ub_cfg_write_byte(uent, UB_BUS_ACCESS_EN, enable); + ub_cfg_write_byte(uent, UB_ENTITY_RS_ACCESS_EN, enable); + + if (!enable && !ub_entity_test_priv_flag(uent, UB_ENTITY_ACTIVE)) + return; + + if (uent->ubc && uent->ubc->ops && uent->ubc->ops->entity_enable) { + ret = uent->ubc->ops->entity_enable(uent, enable); + if (ret) { + ub_err(uent, "entity enable, ret=%d, enable=%u\n", + ret, enable); + return; + } + } + + ub_info(uent, "Change the entity status to %s\n", enable ? "normal" : "disable"); + + if (enable) + ub_entity_assign_priv_flag(uent, UB_ENTITY_ACTIVE, true); + else + ub_entity_assign_priv_flag(uent, UB_ENTITY_ACTIVE, false); +} +EXPORT_SYMBOL_GPL(ub_entity_enable); + +int ub_set_user_info(struct ub_entity *uent) +{ + if (!uent || !uent->ubc || !uent->ubc->uent) + return -EINVAL; + + u32 eid = uent->ubc->uent->eid; + + if (is_p_device(uent)) + goto cfg1; + + /* set dsteid to device */ + ub_cfg_write_dword(uent, UB_UEID_0, eid); + ub_cfg_write_dword(uent, UB_UEID_1, 0); + ub_cfg_write_dword(uent, UB_UEID_2, 0); + ub_cfg_write_dword(uent, UB_UEID_3, 0); + ub_cfg_write_dword(uent, UB_UCNA, uent->ubc->uent->cna); + +cfg1: + /* set tid to device */ + ub_cfg_write_dword(uent, UB_ENTITY_TOKEN_ID, uent->tid); + + return 0; +} +EXPORT_SYMBOL_GPL(ub_set_user_info); + +void ub_unset_user_info(struct ub_entity *uent) +{ + if (!uent) + return; + + if (is_p_device(uent)) + goto cfg1; + + ub_cfg_write_dword(uent, UB_UCNA, 0); + /* clear eid */ + ub_cfg_write_dword(uent, UB_UEID_0, 0); + ub_cfg_write_dword(uent, UB_UEID_1, 0); + ub_cfg_write_dword(uent, UB_UEID_2, 0); + ub_cfg_write_dword(uent, UB_UEID_3, 0); + +cfg1: + /* clear tid */ + ub_cfg_write_dword(uent, UB_ENTITY_TOKEN_ID, 0); +} +EXPORT_SYMBOL_GPL(ub_unset_user_info); + +static struct ub_entity *ub_get_ue_by_entity_idx(struct ub_entity *pue, u32 entity_idx) +{ + struct ub_entity *ue; + + if (ub_check_ue_para(pue, entity_idx)) + return NULL; + + list_for_each_entry(ue, &pue->ue_list, node) { + if (ue->entity_idx == entity_idx) + return ue; + } + + return NULL; +} + +int ub_activate_entity(struct ub_entity *uent, u32 entity_idx) +{ + struct ub_entity *target_dev; + struct ub_driver *udrv; + int ret; + + if (uent && uent->entity_idx != entity_idx && uent->is_mue) + target_dev = ub_get_ue_by_entity_idx(uent, entity_idx); + else + target_dev = uent; + if (!target_dev) + return -EINVAL; + + udrv = uent->driver; + if (!udrv || !udrv->activate) { + ub_err(uent, "udrv or activate is null\n"); + return -EINVAL; + } + + if (!device_trylock(&target_dev->dev)) + return -EBUSY; + + if (ub_entity_test_priv_flag(target_dev, UB_ENTITY_ACTIVE)) { + ub_warn(uent, "entity_idx[%u] is already in normal state\n", entity_idx); + device_unlock(&target_dev->dev); + return 0; + } + + ret = udrv->activate(uent, entity_idx); + if (ret) { + ub_err(uent, "udrv activate entity_idx[%u] failed, ret=%d\n", entity_idx, ret); + } else { + ub_entity_assign_priv_flag(target_dev, UB_ENTITY_ACTIVE, true); + ub_info(uent, "udrv activate entity_idx[%u] success\n", entity_idx); + } + + device_unlock(&target_dev->dev); + return ret; +} +EXPORT_SYMBOL_GPL(ub_activate_entity); + +int ub_deactivate_entity(struct ub_entity *uent, u32 entity_idx) +{ + struct ub_entity *target_dev; + struct ub_driver *udrv; + int ret; + + if (uent && uent->entity_idx != entity_idx && uent->is_mue) + target_dev = ub_get_ue_by_entity_idx(uent, entity_idx); + else + target_dev = uent; + if (!target_dev) + return -EINVAL; + + udrv = uent->driver; + if (!udrv || !udrv->deactivate) { + ub_err(uent, "udrv or deactivate is null\n"); + return -EINVAL; + } + + if (!device_trylock(&target_dev->dev)) + return -EBUSY; + + if (!ub_entity_test_priv_flag(target_dev, UB_ENTITY_ACTIVE)) { + ub_warn(uent, "entity_idx[%u] is already in disable state\n", entity_idx); + device_unlock(&target_dev->dev); + return 0; + } + + ret = udrv->deactivate(uent, entity_idx); + if (ret) { + ub_err(uent, "udrv deactivate entity_idx[%u] failed, ret=%d\n", entity_idx, ret); + } else { + ub_entity_assign_priv_flag(target_dev, UB_ENTITY_ACTIVE, false); + ub_info(uent, "udrv deactivate entity_idx[%u] success\n", entity_idx); + } + + device_unlock(&target_dev->dev); + return ret; +} +EXPORT_SYMBOL_GPL(ub_deactivate_entity); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index db608eebd9d7..9e3f041fb225 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -259,6 +259,10 @@ struct ub_dynids { * context, so it can sleep. * @shutdown: Hook into reboot_notifier_list (kernel/sys.c). * Intended to stop any idling operations. + * @activate: Activate a specific entity. This function is called to + * activate an entity by its index. + * @deactivate: Deactivate a specific entity. This function is called to + * deactivate an entity by its index. * @err_handler: Error handling callbacks. * @groups: Sysfs attribute groups. * @dev_groups: Attributes attached to the device that will be @@ -282,6 +286,8 @@ struct ub_driver { /* entity removed (NULL if not a hot-plug capable driver) */ void (*remove)(struct ub_entity *uent); void (*shutdown)(struct ub_entity *uent); + int (*activate)(struct ub_entity *uent, u32 entity_idx); + int (*deactivate)(struct ub_entity *uent, u32 entity_idx); const struct ub_error_handlers *err_handler; const struct attribute_group **groups; const struct attribute_group **dev_groups; @@ -362,6 +368,39 @@ extern struct bus_type ub_bus_type; void ub_iommu_fwnode_handle_set(const struct fwnode_handle *fwnode); const struct fwnode_handle *ub_iommu_fwnode_handle_get(void); +/** + * ub_entity_enable() - Enable or disable ub entity + * @uent: UB entity. + * @enable: Enable or disable. + * + * Enables or disables the entity access bus and the path through which + * the bus accesses the entity. + * + * Context: Any context. + */ +void ub_entity_enable(struct ub_entity *uent, u8 enable); + +/** + * ub_set_user_info() - Initialize host information for the entity. + * @uent: UB entity. + * + * Initialize necessary host information for the entity for communication + * purposes, such as the host EID, CNA, and token ID. + * + * Context: Any context. + */ +int ub_set_user_info(struct ub_entity *uent); + +/** + * ub_unset_user_info() - Deinitialize host information for the entity. + * @uent: UB entity. + * + * Clearing the Host Information of a entity. + * + * Context: Any context. + */ +void ub_unset_user_info(struct ub_entity *uent); + /** * ub_enable_entities() - Enable ues of mue in batches. * @pue: UB mue. @@ -633,6 +672,28 @@ int ub_irq_vector(struct ub_entity *uent, unsigned int nr); */ const struct cpumask *ub_irq_get_affinity(struct ub_entity *uent, int nr); +/** + * ub_activate_entity() - Activate entity. + * @uent: UB entity. + * @entity_idx: Number of the entity to be activated. + * + * Context: Any context, It will take device_trylock()/device_unlock() + * Return: 0 if success, or %-EINVAL if the device doesn't match the driver, + * or %-EBUSY if can't get device_trylock(), or other failed negative values. + */ +int ub_activate_entity(struct ub_entity *uent, u32 entity_idx); + +/** + * ub_deactivate_entity() - Deactivate entity. + * @uent: UB entity. + * @entity_idx: Number of the entity to be deactivated. + * + * Context: Any context, It will take device_trylock()/device_unlock() + * Return: 0 if success, or %-EINVAL if the entity doesn't match the driver, + * or %-EBUSY if can't get device_trylock(), or other failed negative values. + */ +int ub_deactivate_entity(struct ub_entity *uent, u32 entity_idx); + /** * ub_cfg_read_byte() - 1 byte configuration access read. * @uent: UB entity. @@ -775,6 +836,10 @@ void ub_stop_and_remove_ent(struct ub_entity *uent); static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) { return NULL; } static inline void ub_entity_put(struct ub_entity *uent) {} +static inline void ub_entity_enable(struct ub_entity *uent, u8 enable) {} +static inline int ub_set_user_info(struct ub_entity *uent) +{ return -ENODEV; } +static inline void ub_unset_user_info(struct ub_entity *uent) {} static inline int ub_enable_entities(struct ub_entity *pue, int nums) { return -ENODEV; } static inline void ub_disable_entities(struct ub_entity *pue) {} @@ -853,6 +918,10 @@ static inline int ub_irq_vector(struct ub_entity *uent, unsigned int nr) { return -EINVAL; } static inline const struct cpumask * ub_irq_get_affinity(struct ub_entity *uent, int nr) { return cpu_possible_mask; } +static inline int ub_activate_entity(struct ub_entity *uent, u32 entity_idx) +{ return -ENODEV; } +static inline int ub_deactivate_entity(struct ub_entity *uent, u32 entity_idx) +{ return -ENODEV; } static inline int __ub_register_driver(struct ub_driver *drv, struct module *owner, const char *mod_name) -- Gitee From 9ccea548e12102b01e7621eb650093f69ccf1383 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Sat, 20 Sep 2025 11:37:54 +0800 Subject: [PATCH 035/214] ub:ubus: Add UBUS RAS framework ANBZ: #45460 commit 3d59bca61a049e171e1f003b8a71fb8fe1dcb9c2 openEuler. Add UBUS Reliability, Availability and Serviceability framework, and add UBUS error handle in Generic Hardware Error Source of Linux. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/acpi/apei/ghes.c | 19 ++++++++++++++++--- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/ras.c | 31 +++++++++++++++++++++++++++++++ include/linux/cper.h | 18 ++++++++++++++++++ include/ub/ubus/uber.h | 24 ++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 drivers/ub/ubus/ras.c create mode 100644 include/ub/ubus/uber.h diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 3b921658bb00..a8ce2696ff24 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -48,7 +48,7 @@ #include #include #include - +#include #include #include #include @@ -682,6 +682,18 @@ static void ghes_handle_aer(struct acpi_hest_generic_data *gdata) #endif } +static void ghes_handle_ubus_err(struct acpi_hest_generic_data *gdata) +{ + struct cper_sec_ubus *ubus_err = acpi_hest_get_payload(gdata); + ub_ras_recover_func_t ras_recover_func; + + ras_recover_func = ub_ras_get_recover_func(); + if (ras_recover_func) + ras_recover_func(ubus_err, gdata->error_severity); + else + pr_warn(GHES_PFX "UBUS error occurs when the ras driver is not loaded.\n"); +} + static BLOCKING_NOTIFIER_HEAD(vendor_record_notify_list); int ghes_register_vendor_record_notifier(struct notifier_block *nb) @@ -866,8 +878,9 @@ static void ghes_do_proc(struct ghes *ghes, arch_apei_report_pcie_error(sec_sev, pcie_err); ghes_handle_aer(gdata); - } - else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) { + } else if (guid_equal(sec_type, &CPER_SEC_UBUS)) { + ghes_handle_ubus_err(gdata); + } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) { queued = ghes_handle_arm_hw_error(gdata, sev, sync); } else if (guid_equal(sec_type, &CPER_SEC_CXL_GEN_MEDIA_GUID)) { struct cxl_cper_event_rec *rec = acpi_hest_get_payload(gdata); diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index d90034f0003f..b7991115a3d3 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ -obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o +obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o ras.o obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o diff --git a/drivers/ub/ubus/ras.c b/drivers/ub/ubus/ras.c new file mode 100644 index 000000000000..a6553a661ac5 --- /dev/null +++ b/drivers/ub/ubus/ras.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include +#include + +static ub_ras_recover_func_t ub_ras_recover_func; +static DEFINE_SPINLOCK(ubus_ghes_lock); + +void ub_ras_register_recover_func(ub_ras_recover_func_t func) + +{ + spin_lock(&ubus_ghes_lock); + ub_ras_recover_func = func; + spin_unlock(&ubus_ghes_lock); +} +EXPORT_SYMBOL_GPL(ub_ras_register_recover_func); + +ub_ras_recover_func_t ub_ras_get_recover_func(void) +{ + ub_ras_recover_func_t tmp_func; + + spin_lock(&ubus_ghes_lock); + tmp_func = ub_ras_recover_func; + spin_unlock(&ubus_ghes_lock); + + return tmp_func; +} +EXPORT_SYMBOL_GPL(ub_ras_get_recover_func); diff --git a/include/linux/cper.h b/include/linux/cper.h index f78295bd5d18..2ff63ec2d394 100644 --- a/include/linux/cper.h +++ b/include/linux/cper.h @@ -220,6 +220,10 @@ enum { #define CPER_SEC_DMAR_IOMMU \ GUID_INIT(0x036F84E1, 0x7F37, 0x428c, 0xA7, 0x9E, 0x57, 0x5F, \ 0xDF, 0xAA, 0x84, 0xEC) +/* UBUS */ +#define CPER_SEC_UBUS \ + GUID_INIT(0x74D255B0, 0x73E8, 0x488A, 0xB8, 0x67, 0x90, 0x65, \ + 0x4A, 0x35, 0x86, 0x5E) #define CPER_PROC_VALID_TYPE 0x0001 #define CPER_PROC_VALID_ISA 0x0002 @@ -564,6 +568,20 @@ struct cper_sec_pcie { u8 aer_info[96]; }; +struct cper_sec_ubus { + u64 validation_bits; + u32 eid; + u32 cna; + u32 port; + u32 vendor; + u16 device; + u16 type; + u16 class_code; + u16 reserved; + u32 overflow_flag; + u32 err_info[72]; +}; + /* Firmware Error Record Reference, UEFI v2.7 sec N.2.10 */ struct cper_sec_fw_err_rec_ref { u8 record_type; diff --git a/include/ub/ubus/uber.h b/include/ub/ubus/uber.h new file mode 100644 index 000000000000..73a4418d5894 --- /dev/null +++ b/include/ub/ubus/uber.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef _UB_UBUS_UBER_H_ +#define _UB_UBUS_UBER_H_ + +#include + +typedef void (*ub_ras_recover_func_t)(struct cper_sec_ubus *err, int serverity); + +#ifdef CONFIG_UB_UBUS +void ub_ras_register_recover_func(ub_ras_recover_func_t func); +ub_ras_recover_func_t ub_ras_get_recover_func(void); +#else +static inline void ub_ras_register_recover_func(ub_ras_recover_func_t func) {} +static inline ub_ras_recover_func_t ub_ras_get_recover_func(void) +{ + return NULL; +} +#endif /* CONFIG_UB_UBUS */ + +#endif /* _UB_UBUS_UBER_H_ */ -- Gitee From a3e74de89ae0af53416d883ae4f9d57125384e8a Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Fri, 19 Sep 2025 17:04:58 +0800 Subject: [PATCH 036/214] ub:ubus: Enable the ub decoder device support ANBZ: #45460 commit 7dfab32c64b4c6f797756007331e6dcc2cb7cf4b openEuler. Configure the various registers required for the ub decoder hardware to function properly. Signed-off-by: Yuhao Xiang Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/cap.c | 3 + drivers/ub/ubus/decoder.c | 263 ++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/decoder.h | 47 +++++++ include/ub/ubus/ubus.h | 1 + 5 files changed, 315 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/decoder.c create mode 100644 drivers/ub/ubus/decoder.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index b7991115a3d3..8c35ed594e96 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -4,6 +4,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o ras.o obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o -ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o +ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/cap.c b/drivers/ub/ubus/cap.c index a9073de22fbe..3eca70059195 100644 --- a/drivers/ub/ubus/cap.c +++ b/drivers/ub/ubus/cap.c @@ -6,6 +6,7 @@ #define pr_fmt(fmt) "ubus cap: " fmt #include "ubus.h" +#include "decoder.h" #include "interrupt.h" #define DW_CHECK 3 @@ -232,6 +233,7 @@ void ub_init_capabilities(struct ub_entity *uent) init_cfg1_cap: /* cfg1 caps */ + ub_decoder_init(uent); ub_intr_init(uent); uent->reset_fn = 1; @@ -240,6 +242,7 @@ void ub_init_capabilities(struct ub_entity *uent) void ub_uninit_capabilities(struct ub_entity *uent) { /* cfg1 cap */ + ub_decoder_uninit(uent); uent->reset_fn = 0; } diff --git a/drivers/ub/ubus/decoder.c b/drivers/ub/ubus/decoder.c new file mode 100644 index 000000000000..e0b07707881f --- /dev/null +++ b/drivers/ub/ubus/decoder.c @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus decoder: " fmt + +#include +#include + +#include "ubus.h" +#include "decoder.h" + +#define MMIO_SIZE_MASK GENMASK_ULL(18, 16) +#define MMIO_SIZE_OFFSET 16 + +static void ub_decoder_uninit_queue(struct ub_decoder *decoder) +{} + +static int ub_decoder_init_queue(struct ub_bus_controller *ubc, + struct ub_decoder *decoder) +{ + return 0; +} + +static u32 set_mmio_base_reg(struct ub_decoder *decoder) +{ + u32 ret; + + ret = (u32)ub_cfg_write_dword(decoder->uent, DECODER_MMIO_BA0, + lower_32_bits(decoder->mmio_base_addr)); + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_MMIO_BA1, + upper_32_bits(decoder->mmio_base_addr)); + if (ret) + ub_err(decoder->uent, "set decoder mmio base failed\n"); + + return ret; +} + +static u32 set_page_table_reg(struct ub_decoder *decoder) +{ + u32 ret; + + ret = (u32)ub_cfg_write_dword(decoder->uent, DECODER_MATT_BA0, + lower_32_bits(decoder->pgtlb.pgtlb_dma)); + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_MATT_BA1, + upper_32_bits(decoder->pgtlb.pgtlb_dma)); + if (ret) + ub_err(decoder->uent, "set decoder page table reg failed\n"); + + return ret; +} + +static u32 set_queue_reg(struct ub_decoder *decoder) +{ + return 0; +} + +static u32 set_decoder_enable(struct ub_decoder *decoder) +{ + u32 ret = (u32)ub_cfg_write_dword(decoder->uent, DECODER_CTRL, 1); + + if (ret) + ub_err(decoder->uent, "set decoder enable failed\n"); + + return ret; +} + +static u32 ub_decoder_device_set(struct ub_decoder *decoder) +{ + u32 ret; + + ret = set_mmio_base_reg(decoder); + ret |= set_page_table_reg(decoder); + ret |= set_queue_reg(decoder); + ret |= set_decoder_enable(decoder); + + return ret; +} + +static int ub_decoder_create_page_table(struct ub_decoder *decoder) +{ + return 0; +} + +static void ub_decoder_free_page_table(struct ub_decoder *decoder) +{} + +static void ub_get_decoder_mmio_base(struct ub_bus_controller *ubc, + struct ub_decoder *decoder) +{ + struct resource_entry *entry; + + decoder->mmio_base_addr = -1; + resource_list_for_each_entry(entry, &ubc->resources) { + if (entry->res->flags == IORESOURCE_MEM && + strstr(entry->res->name, "UB_BUS_CTL") && + entry->res->start < decoder->mmio_base_addr) + decoder->mmio_base_addr = entry->res->start; + } + + ub_info(decoder->uent, "decoder mmio base is %#llx\n", + decoder->mmio_base_addr); +} + +static const char * const mmio_size_desc[] = { + "128Gbyte", "256Gbyte", "512Gbyte", "1Tbyte", + "2Tbyte", "4Tbyte", "8Tbyte", "16Tbyte" +}; + +static int ub_get_decoder_cap(struct ub_decoder *decoder) +{ + struct ub_entity *uent = decoder->uent; + u32 val; + int ret; + + ret = ub_cfg_read_dword(uent, DECODER_CAP, &val); + if (ret) { + ub_err(uent, "read decoder cap failed\n"); + return ret; + } + + decoder->mmio_size_sup = (val & MMIO_SIZE_MASK) >> MMIO_SIZE_OFFSET; + + return 0; +} + +static int ub_create_decoder(struct ub_bus_controller *ubc) +{ + struct ub_entity *uent = ubc->uent; + struct ub_decoder *decoder; + int ret; + + decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); + if (!decoder) + return -ENOMEM; + + decoder->dev = &ubc->dev; + decoder->uent = uent; + mutex_init(&decoder->table_lock); + + ub_get_decoder_mmio_base(ubc, decoder); + + ret = ub_get_decoder_cap(decoder); + if (ret) + goto release_decoder; + + ret = ub_decoder_init_queue(ubc, decoder); + if (ret) + goto release_decoder; + + ret = ub_decoder_create_page_table(decoder); + if (ret) { + ub_err(uent, "decoder create page table failed\n"); + goto release_queue; + } + + ret = ub_decoder_device_set(decoder); + if (ret) + goto release_page_table; + + ubc->decoder = decoder; + + decoder->irq_num = -1; + decoder->rg_size = SZ_4G; + + ub_info(uent, "decoder create success\n"); + return ret; + +release_page_table: + ub_decoder_free_page_table(decoder); +release_queue: + ub_decoder_uninit_queue(decoder); +release_decoder: + kfree(decoder); + return ret; +} + +static void unset_mmio_base_reg(struct ub_decoder *decoder) +{ + struct ub_entity *uent = decoder->uent; + u32 ret; + + ret = (u32)ub_cfg_write_dword(uent, DECODER_MMIO_BA0, 0); + ret |= (u32)ub_cfg_write_dword(uent, DECODER_MMIO_BA1, 0); + if (ret) + ub_err(uent, "unset mmio base reg failed\n"); +} + +static void unset_queue_reg(struct ub_decoder *decoder) +{} + +static void unset_page_table_reg(struct ub_decoder *decoder) +{ + struct ub_entity *uent = decoder->uent; + u32 ret; + + ret = (u32)ub_cfg_write_dword(uent, DECODER_MATT_BA0, 0); + ret |= (u32)ub_cfg_write_dword(uent, DECODER_MATT_BA1, 0); + if (ret) + ub_err(uent, "unset page table reg failed\n"); +} + +static void unset_decoder_enable(struct ub_decoder *decoder) +{ + struct ub_entity *uent = decoder->uent; + + if (ub_cfg_write_dword(uent, DECODER_CTRL, 0)) + ub_err(uent, "unset decoder enable failed\n"); +} + +static void ub_decoder_device_unset(struct ub_decoder *decoder) +{ + unset_decoder_enable(decoder); + unset_queue_reg(decoder); + unset_page_table_reg(decoder); + unset_mmio_base_reg(decoder); +} + +static void ub_remove_decoder(struct ub_bus_controller *ubc) +{ + struct ub_decoder *decoder = ubc->decoder; + + if (!decoder) { + ub_err(ubc->uent, "remove decoder, decoder is null\n"); + return; + } + + ub_decoder_device_unset(decoder); + + ub_decoder_free_page_table(decoder); + + ub_decoder_uninit_queue(decoder); + + kfree(decoder); + + ubc->decoder = NULL; +} + +void ub_decoder_init(struct ub_entity *uent) +{ + int ret; + + if (!uent || !uent->ubc) + return; + + if (!is_ibus_controller(uent)) + return; + + ret = ub_create_decoder(uent->ubc); + WARN_ON(ret); +} + +void ub_decoder_uninit(struct ub_entity *uent) +{ + if (!uent || !uent->ubc) + return; + + if (!is_ibus_controller(uent)) + return; + + ub_remove_decoder(uent->ubc); +} diff --git a/drivers/ub/ubus/decoder.h b/drivers/ub/ubus/decoder.h new file mode 100644 index 000000000000..1b9ea5dafd24 --- /dev/null +++ b/drivers/ub/ubus/decoder.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __DECODER_H__ +#define __DECODER_H__ + +#include + +struct reg_default_val { + u32 cmdq_cfg_val; + u32 evtq_cfg_val; +}; + +struct page_table_desc { + void *page_base; + dma_addr_t page_dma; + u32 count; +}; + +struct page_table { + void *pgtlb_base; + dma_addr_t pgtlb_dma; + struct page_table_desc *desc_base; +}; + +struct ub_decoder { + struct device *dev; + struct ub_entity *uent; + phys_addr_t mmio_base_addr; + u32 mmio_size_sup; + u64 rg_size; + struct page_table pgtlb; + struct reg_default_val vals; + int irq_num; + + struct page_table_desc invalid_desc; + u64 invalid_page_dma; + + struct mutex table_lock; +}; + +void ub_decoder_init(struct ub_entity *uent); +void ub_decoder_uninit(struct ub_entity *uent); + +#endif /* __DECODER_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 9e3f041fb225..fc63cfdbcba2 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -323,6 +323,7 @@ struct ub_bus_controller { u32 ctl_no; struct message_device *mdev; + struct ub_decoder *decoder; struct list_head resources; struct list_head node; struct list_head devs; -- Gitee From 7664a1cdf636aeec4ea9480bf2b58475c0279380 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Sat, 20 Sep 2025 11:01:15 +0800 Subject: [PATCH 037/214] ub:ubus: Adding a ue Creation Destruction Mechanism ANBZ: #45460 commit 9039065cf6d07af72879991553397629d8bd4e92 openEuler. Implement the ability to trigger the enabling of ue through a sysfs entry, adding ue change notification mechanism and add interfaces to access the device via different keys. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/sysfs.c | 260 ++++++++++++++++++++++++++++++++++ drivers/ub/ubus/ubus_driver.c | 98 +++++++++++++ drivers/ub/ubus/ubus_entity.c | 30 ++++ drivers/ub/ubus/ubus_entity.h | 1 + include/ub/ubus/ubus.h | 68 +++++++++ 5 files changed, 457 insertions(+) diff --git a/drivers/ub/ubus/sysfs.c b/drivers/ub/ubus/sysfs.c index eca8d3f7f58c..b816e82e4c0d 100644 --- a/drivers/ub/ubus/sysfs.c +++ b/drivers/ub/ubus/sysfs.c @@ -7,6 +7,7 @@ #include "ubus.h" #include "sysfs.h" +#include "ubus_entity.h" #define ub_config_attr(field, format_string) \ static ssize_t field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ @@ -147,3 +148,262 @@ const struct attribute_group *ub_bus_groups[] = { const struct device_type ub_dev_type = { .groups = NULL, }; + +static ssize_t ub_total_entities_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ub_entity *uent; + + uent = to_ub_entity(dev); + return sysfs_emit(buf, "%u\n", uent->total_funcs); +} +static DEVICE_ATTR_RO(ub_total_entities); + +static ssize_t ub_totalues_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ub_entity *uent; + + uent = to_ub_entity(dev); + return sysfs_emit(buf, "%u\n", uent->total_ues); +} +static DEVICE_ATTR_RO(ub_totalues); + +static ssize_t ub_numues_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ub_entity *uent; + + uent = to_ub_entity(dev); + return sysfs_emit(buf, "%u\n", uent->num_ues); +} + +static int ub_numues_store_driver_check(struct ub_entity *uent) +{ + if (!uent->driver) { + ub_err(uent, "no driver bound to device, enable failed\n"); + return -ENOENT; + } + + if (!uent->driver->virt_configure) { + ub_err(uent, "driver does not support virt_configure via sysfs\n"); + return -ENOENT; + } + + return 0; +} + +static void ub_numues_store_ues_process(struct ub_entity *uent, int nums) +{ + int ue_start_idx, ue_end_idx, i, cnt, ret; + struct ub_entity *ue; + + /* + * The software finds "cnt" disabled ues for "ue_start_idx" to + * "ue_end_idx" based on the ordered ue_list. "ue_start_idx" is + * the entity_idx of first ues and "ue_end_idx" is calculated based + * on the total number(that is, "nums") ues to be enabled. + */ + ue_start_idx = uent->uem.start_entity_idx; + ue_end_idx = ue_start_idx + nums - 1; + i = ue_start_idx; + cnt = nums - uent->num_ues; + list_for_each_entry(ue, &uent->ue_list, node) { + for (; i < ue->entity_idx; i++) { + /* + * The "ue_list" is sorted by entity_idx in ascending order. + * Ensure that others ues before this ue are enabled. + */ + ret = uent->driver->virt_configure(uent, i, 1); + if (ret) + ub_warn(uent, "driver virt_configure, ret=%d\n", ret); + if (--cnt == 0) + return; + } + /* Skip this enabled ue. */ + i++; + } + + /* Ensure that the remaining ues enabled. */ + for (; i <= ue_end_idx; i++) { + ret = uent->driver->virt_configure(uent, i, 1); + if (ret) + ub_warn(uent, "driver virt_configure, ret=%d\n", ret); + if (--cnt == 0) + return; + } +} + +static ssize_t ub_numues_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ub_entity *uent, *ue, *tmp; + int nums, ret; + + uent = to_ub_entity(dev); + + ret = kstrtoint(buf, 0, &nums); + if (ret < 0) + return ret; + + ub_info(uent, "enable %d ue, total_ue=%u\n", + nums, uent->total_ues); + + if (nums < 0 || nums > uent->total_ues) + return -EINVAL; + + device_lock(&uent->dev); + ret = ub_numues_store_driver_check(uent); + if (ret) + goto exit; + + if (nums == 0) { + list_for_each_entry_safe_reverse(ue, tmp, &uent->ue_list, node) + (void)uent->driver->virt_configure(uent, ue->entity_idx, 0); + goto exit; + } + + if (nums <= uent->num_ues || (!entity_flex_en && uent->num_ues)) { + ub_info(uent, "ue already enabled, num=%u.\n", uent->num_ues); + goto exit; + } + + ub_numues_store_ues_process(uent, nums); +exit: + device_unlock(&uent->dev); + if (ret < 0) + return ret; + + return count; +} +static DEVICE_ATTR_RW(ub_numues); + +static ssize_t ub_release_ue_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ub_entity *uent, *ue, *tmp; + unsigned long uent_num; + int ret; + +#define BASE_16 16 + ret = kstrtoul(buf, BASE_16, &uent_num); + if (ret < 0) + return ret; + + uent = to_ub_entity(dev); + device_lock(&uent->dev); + ret = ub_numues_store_driver_check(uent); + if (ret) + goto exit; + + list_for_each_entry_safe_reverse(ue, tmp, &uent->ue_list, node) + if (ue->uent_num == (u32)uent_num) { + (void)uent->driver->virt_configure(uent, ue->entity_idx, 0); + device_unlock(&uent->dev); + return count; + } + + ub_err(uent, "uent %#05lx does not belong to this pue\n", uent_num); +exit: + device_unlock(&uent->dev); + return -EIO; +} +static DEVICE_ATTR_WO(ub_release_ue); + +struct dev_attr_creat_group { + struct device_attribute *attr; + bool conditions; +}; + +static ssize_t ue_list_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + struct ub_entity *ent; + int cnt = 0; + + list_for_each_entry(ent, &uent->ue_list, node) + cnt += sysfs_emit_at(buf, cnt, "%s\n", dev_name(&ent->dev)); + + return cnt; +} +DEVICE_ATTR_RO(ue_list); + +static ssize_t mue_list_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + struct ub_entity *ent; + int cnt = 0; + + list_for_each_entry(ent, &uent->mue_list, node) + cnt += sysfs_emit_at(buf, cnt, "%s\n", dev_name(&ent->dev)); + + return cnt; +} +DEVICE_ATTR_RO(mue_list); + +#define CREATE_CONDITIONS(c) ((c) ? 1 : 0) + +static int ub_create_capabilities_sysfs(struct ub_entity *uent) +{ + struct dev_attr_creat_group grp[] = { + { &dev_attr_ub_total_entities, CREATE_CONDITIONS(uent->entity_idx == 0) }, + { &dev_attr_ub_totalues, CREATE_CONDITIONS(uent->is_mue) }, + { &dev_attr_ub_numues, CREATE_CONDITIONS(uent->is_mue) }, + { &dev_attr_ub_release_ue, CREATE_CONDITIONS(uent->is_mue && entity_flex_en) }, + { &dev_attr_mue_list, CREATE_CONDITIONS(uent->entity_idx == 0) }, + { &dev_attr_ue_list, CREATE_CONDITIONS(uent->is_mue) }, + }; + int retval, i; + + for (i = 0; i < ARRAY_SIZE(grp); i++) { + if (grp[i].conditions) { + retval = device_create_file(&uent->dev, grp[i].attr); + if (retval) + goto err; + } + } + + return 0; +err: + for (i = i - 1; i >= 0; i--) + if (grp[i].conditions) + device_remove_file(&uent->dev, grp[i].attr); + return retval; +} + +static void ub_remove_capabilities_sysfs(struct ub_entity *uent) +{ + struct dev_attr_creat_group grp[] = { + { &dev_attr_ub_total_entities, CREATE_CONDITIONS(uent->entity_idx == 0) }, + { &dev_attr_ub_totalues, CREATE_CONDITIONS(uent->is_mue) }, + { &dev_attr_ub_numues, CREATE_CONDITIONS(uent->is_mue) }, + { &dev_attr_ub_release_ue, CREATE_CONDITIONS(uent->is_mue && entity_flex_en) }, + { &dev_attr_mue_list, CREATE_CONDITIONS(uent->entity_idx == 0) }, + { &dev_attr_ue_list, CREATE_CONDITIONS(uent->is_mue) }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(grp); i++) + if (grp[i].conditions) + device_remove_file(&uent->dev, grp[i].attr); +} + +int ub_create_sysfs_dev_files(struct ub_entity *uent) +{ + int retval; + + retval = ub_create_capabilities_sysfs(uent); + if (retval) + return retval; + + return 0; +} + +void ub_remove_sysfs_ent_files(struct ub_entity *uent) +{ + ub_remove_capabilities_sysfs(uent); +} diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index da0d5030ee5b..fe5591c33999 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -173,6 +173,96 @@ const struct ub_device_id *ub_match_id(const struct ub_device_id *ids, return NULL; } +static struct ub_entity * +ub_get_dev_by_id_inner(struct ub_entity *from, const void *data, + int (*match)(struct device *dev, const void *data)) +{ + struct device *dev, *dev_start = NULL; + struct ub_entity *fdev = NULL; + + if (from) + dev_start = &from->dev; + + dev = bus_find_device(&ub_bus_type, dev_start, (void *)data, match); + if (dev) + fdev = to_ub_entity(dev); + + ub_entity_put(from); + return fdev; +} + +static int ub_entity_match_by_id(struct device *dev, const void *data) +{ + const struct ub_device_id *id = (const struct ub_device_id *)data; + struct ub_entity *pue = to_ub_entity(dev); + + if (ub_match_one_device(id, pue)) + return 1; + return 0; +} + +struct ub_entity *ub_get_entity(unsigned int vendor, unsigned int device, + struct ub_entity *from) +{ + struct ub_device_id id = { + .vendor = vendor, + .device = device, + }; + + return ub_get_dev_by_id_inner(from, &id, ub_entity_match_by_id); +} +EXPORT_SYMBOL_GPL(ub_get_entity); + +static int ub_entity_match_by_guid(struct device *dev, const void *data) +{ + const struct ub_guid *guid = (const struct ub_guid *)data; + struct ub_entity *uent = to_ub_entity(dev); + + if (guid_equal(&uent->guid.id, &guid->id)) + return 1; + return 0; +} + +struct ub_entity *ub_get_ent_by_guid(const struct ub_guid *guid) +{ + return ub_get_dev_by_id_inner(NULL, guid, ub_entity_match_by_guid); +} +EXPORT_SYMBOL_GPL(ub_get_ent_by_guid); + +static int ub_entity_match_by_eid(struct device *dev, const void *data) +{ + struct ub_entity *uent = to_ub_entity(dev); + const u32 eid = *((const u32 *)data); + + if (uent->eid == eid) + return 1; + + return 0; +} + +struct ub_entity *ub_get_ent_by_eid(unsigned int eid) +{ + return ub_get_dev_by_id_inner(NULL, &eid, ub_entity_match_by_eid); +} +EXPORT_SYMBOL_GPL(ub_get_ent_by_eid); + +static int ub_entity_match_by_uent_num(struct device *dev, const void *data) +{ + const u32 uent_num = *((const u32 *)data); + struct ub_entity *uent = to_ub_entity(dev); + + if (uent->uent_num == uent_num) + return 1; + + return 0; +} + +struct ub_entity *ub_get_ent_by_uent_num(unsigned int uent_num) +{ + return ub_get_dev_by_id_inner(NULL, &uent_num, ub_entity_match_by_uent_num); +} +EXPORT_SYMBOL_GPL(ub_get_ent_by_uent_num); + static const struct ub_device_id *ub_match_device(struct ub_driver *drv, struct ub_entity *dev) { @@ -271,9 +361,17 @@ static int ub_entity_probe(struct device *dev) { struct ub_driver *drv = to_ub_driver(dev->driver); struct ub_entity *ub_entity = to_ub_entity(dev); + struct ub_entity *pue; + u16 entity_idx; int ret; ub_entity_get(ub_entity); + if (!ub_entity->is_mue) { + pue = ub_entity->pue; + entity_idx = ub_entity->entity_idx; + ub_virt_notify(pue, entity_idx, true); + } + ret = __ub_entity_probe(drv, ub_entity); if (ret) ub_entity_put(ub_entity); diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index 53842779275e..f72f97bc06c6 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -711,10 +711,40 @@ static int ub_enable_mues(struct ub_entity *pue, int nums, struct ue_map *map) return ret; } +void ub_virt_notify(struct ub_entity *pue, u16 entity_idx, bool is_en) +{ + const char *operate = is_en ? "enable" : "disable"; + struct ub_driver *pdrv; + int ret; + + if (pue) { + pdrv = pue->driver; + if (pdrv && pdrv->virt_notify) { + ret = pdrv->virt_notify(pue, entity_idx, is_en); + if (ret) + ub_warn(pue, "drv virt notify %s ue with entity_idx %u failed, ret=%d\n", + operate, entity_idx, ret); + } + } +} + void ub_disable_ent(struct ub_entity *uent) { + struct ub_entity *pue; + bool ue_flag = false; + u16 entity_idx; + ub_info(uent, "disable entity, eid=%#05x\n", uent->eid); + if (!uent->is_mue) { + pue = uent->pue; + entity_idx = uent->entity_idx; + ue_flag = true; + } + ub_stop_and_remove_ent(uent); + + if (ue_flag) + ub_virt_notify(pue, entity_idx, false); } EXPORT_SYMBOL_GPL(ub_disable_ent); diff --git a/drivers/ub/ubus/ubus_entity.h b/drivers/ub/ubus/ubus_entity.h index 8f4c0fb4bcc5..aa535e6adf70 100644 --- a/drivers/ub/ubus/ubus_entity.h +++ b/drivers/ub/ubus/ubus_entity.h @@ -17,5 +17,6 @@ void ub_stop_entities(void); void ub_remove_entities(void); int ub_num_ue(struct ub_entity *uent); void ub_disable_ent(struct ub_entity *uent); +void ub_virt_notify(struct ub_entity *pue, u16 entity_idx, bool is_en); #endif /* __UBUS_ENTITY_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index fc63cfdbcba2..840c686a7c92 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -259,6 +259,11 @@ struct ub_dynids { * context, so it can sleep. * @shutdown: Hook into reboot_notifier_list (kernel/sys.c). * Intended to stop any idling operations. + * @virt_configure: Optional driver callback to allow configuration of + * ues. This function is called to enable or disable ues. + * @virt_notify: Optional driver callback to notify the driver about + * changes in ue status. This function is called + * when the status of a ue changes. * @activate: Activate a specific entity. This function is called to * activate an entity by its index. * @deactivate: Deactivate a specific entity. This function is called to @@ -286,6 +291,8 @@ struct ub_driver { /* entity removed (NULL if not a hot-plug capable driver) */ void (*remove)(struct ub_entity *uent); void (*shutdown)(struct ub_entity *uent); + int (*virt_configure)(struct ub_entity *uent, int entity_idx, bool is_en); + int (*virt_notify)(struct ub_entity *uent, int entity_idx, bool is_en); int (*activate)(struct ub_entity *uent, u32 entity_idx); int (*deactivate)(struct ub_entity *uent, u32 entity_idx); const struct ub_error_handlers *err_handler; @@ -369,6 +376,58 @@ extern struct bus_type ub_bus_type; void ub_iommu_fwnode_handle_set(const struct fwnode_handle *fwnode); const struct fwnode_handle *ub_iommu_fwnode_handle_get(void); +/** + * ub_get_ent_by_eid() - Searching for UB Devices by EID. + * @eid: entity EID. + * + * Traverse the UB bus device linked list and search for the device with + * the target EID. You need to call ub_entity_put() after using it. + * + * Context: Any context. + * Return: The device found, or NULL if not found. + */ +struct ub_entity *ub_get_ent_by_eid(unsigned int eid); + +/** + * ub_get_ent_by_uent_num() - Searching for UB entities by entity Num. + * @uent_num: entity Num. + * + * Traverse the UB bus device linked list and search for the entity with + * the target entity Num. You need to call ub_entity_put() after using it. + * + * Context: Any context. + * Return: The entity found, or NULL if not found. + */ +struct ub_entity *ub_get_ent_by_uent_num(unsigned int uent_num); + +/** + * ub_get_ent_by_guid() - Searching for UB entities by GUID. + * @guid: GUID. + * + * Traverse the UB bus entity linked list and search for the entity with + * the target GUID. You need to call ub_entity_put() after using it. + * + * Context: Any context. + * Return: The entity found, or NULL if not found. + */ +struct ub_entity *ub_get_ent_by_guid(const struct ub_guid *guid); + +/** + * ub_get_entity() - Searching for UB entities by Vendor and entity ID. + * @vendor: Vendor ID. + * @entity: entity ID. + * @from: Previous UB entity found in search, or %NULL for new search. + * + * Traverse the UB bus entity linked list and search for the entity with + * the target Vendor and entity ID. You need to call ub_entity_put() + * after using it. + * + * Context: Any context. + * Return: The entity found, or NULL if not found. + */ +struct ub_entity *ub_get_entity(unsigned int vendor, unsigned int entity, + struct ub_entity *from); + /** * ub_entity_enable() - Enable or disable ub entity * @uent: UB entity. @@ -834,6 +893,15 @@ void ub_stop_and_remove_ent(struct ub_entity *uent); #else /* CONFIG_UB_UBUS is not enabled */ #define dev_is_ub(d) (false) +static inline struct ub_entity *ub_get_ent_by_eid(unsigned int eid) +{ return NULL; } +static inline struct ub_entity *ub_get_ent_by_uent_num(unsigned int uent_num) +{ return NULL; } +static inline struct ub_entity *ub_get_ent_by_guid(const struct ub_guid *guid) +{ return NULL; } +static inline struct ub_entity * +ub_get_entity(unsigned int vendor, unsigned int entity, struct ub_entity *from) +{ return NULL; } static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) { return NULL; } static inline void ub_entity_put(struct ub_entity *uent) {} -- Gitee From 05ed20981cef29715afe2c784b40810d322c4c5d Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Fri, 19 Sep 2025 17:24:14 +0800 Subject: [PATCH 038/214] ub:ubus: Creating and deleting ub decoder page table mappings ANBZ: #45460 commit ddfa17bb149b3f68bc114b098e1d012705a9bc6c openEuler. Construct address mapping and populate the page table with information retrieved from the decoder hardware lookup. Clear the established mapping relationships. Signed-off-by: Yuhao Xiang Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/decoder.c | 61 ++++++++++- drivers/ub/ubus/decoder.h | 28 +++++ drivers/ub/ubus/omm.c | 209 ++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/omm.h | 28 +++++ 5 files changed, 326 insertions(+), 2 deletions(-) create mode 100644 drivers/ub/ubus/omm.c create mode 100644 drivers/ub/ubus/omm.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 8c35ed594e96..e0f036870acb 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -4,6 +4,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o ras.o obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o -ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o +ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o omm.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/decoder.c b/drivers/ub/ubus/decoder.c index e0b07707881f..9bb9dfb2f2a6 100644 --- a/drivers/ub/ubus/decoder.c +++ b/drivers/ub/ubus/decoder.c @@ -9,11 +9,14 @@ #include #include "ubus.h" +#include "omm.h" #include "decoder.h" #define MMIO_SIZE_MASK GENMASK_ULL(18, 16) #define MMIO_SIZE_OFFSET 16 +#define DECODER_PAGE_TABLE_ENTRY_SIZE 8 + static void ub_decoder_uninit_queue(struct ub_decoder *decoder) {} @@ -80,11 +83,67 @@ static u32 ub_decoder_device_set(struct ub_decoder *decoder) static int ub_decoder_create_page_table(struct ub_decoder *decoder) { + struct page_table_desc *invalid_desc = &decoder->invalid_desc; + struct ub_entity *uent = decoder->uent; + struct page_table *pgtlb; + void *pgtlb_base; + size_t size; + + size = DECODER_PAGE_TABLE_ENTRY_SIZE * DECODER_PAGE_TABLE_SIZE; + pgtlb = &decoder->pgtlb; + pgtlb_base = dmam_alloc_coherent(decoder->dev, size, + &pgtlb->pgtlb_dma, GFP_KERNEL); + if (!pgtlb_base) { + ub_err(uent, "allocate ub decoder page table fail\n"); + return -ENOMEM; + } + pgtlb->pgtlb_base = pgtlb_base; + + size = sizeof(*pgtlb->desc_base) * DECODER_PAGE_TABLE_SIZE; + pgtlb->desc_base = kzalloc(size, GFP_KERNEL); + if (!pgtlb->desc_base) { + ub_err(uent, "allocate ub decoder page table desc fail\n"); + goto release_pgtlb; + } + + invalid_desc->page_base = dmam_alloc_coherent(decoder->dev, + RANGE_TABLE_PAGE_SIZE, + &invalid_desc->page_dma, + GFP_KERNEL); + if (!invalid_desc->page_base) { + ub_err(uent, "decoder alloc free page fail\n"); + goto release_desc; + } + decoder->invalid_page_dma = (invalid_desc->page_dma & + DECODER_PGTBL_PGPRT_MASK) >> + DECODER_DMA_PAGE_ADDR_OFFSET; + + ub_decoder_init_page_table(decoder, pgtlb_base); + return 0; + +release_desc: + kfree(pgtlb->desc_base); + pgtlb->desc_base = NULL; +release_pgtlb: + size = DECODER_PAGE_TABLE_ENTRY_SIZE * DECODER_PAGE_TABLE_SIZE; + dmam_free_coherent(decoder->dev, size, pgtlb_base, pgtlb->pgtlb_dma); + return -ENOMEM; } static void ub_decoder_free_page_table(struct ub_decoder *decoder) -{} +{ + struct page_table_desc *invalid_desc = &decoder->invalid_desc; + size_t size; + + dmam_free_coherent(decoder->dev, RANGE_TABLE_PAGE_SIZE, + invalid_desc->page_base, invalid_desc->page_dma); + kfree(decoder->pgtlb.desc_base); + + size = DECODER_PAGE_TABLE_ENTRY_SIZE * DECODER_PAGE_TABLE_SIZE; + dmam_free_coherent(decoder->dev, size, decoder->pgtlb.pgtlb_base, + decoder->pgtlb.pgtlb_dma); +} static void ub_get_decoder_mmio_base(struct ub_bus_controller *ubc, struct ub_decoder *decoder) diff --git a/drivers/ub/ubus/decoder.h b/drivers/ub/ubus/decoder.h index 1b9ea5dafd24..6b0fcee2681e 100644 --- a/drivers/ub/ubus/decoder.h +++ b/drivers/ub/ubus/decoder.h @@ -41,6 +41,34 @@ struct ub_decoder { struct mutex table_lock; }; +#define DECODER_PGTBL_PGPRT_MASK GENMASK_ULL(47, 12) +#define DECODER_DMA_PAGE_ADDR_OFFSET 12 + +#define PGTLB_CACHE_IR_NC 0b00 +#define PGTLB_CACHE_IR_WBRA 0b01 +#define PGTLB_CACHE_IR_WT 0b10 +#define PGTLB_CACHE_IR_WB 0b11 +#define PGTLB_CACHE_OR_NC 0b0000 +#define PGTLB_CACHE_OR_WBRA 0b0100 +#define PGTLB_CACHE_OR_WT 0b1000 +#define PGTLB_CACHE_OR_WB 0b1100 +#define PGTLB_CACHE_SH_NSH 0b000000 +#define PGTLB_CACHE_SH_OSH 0b100000 +#define PGTLB_CACHE_SH_ISH 0b110000 + +#define PGTLB_ATTR_DEFAULT (PGTLB_CACHE_IR_WBRA | \ + PGTLB_CACHE_OR_WBRA | \ + PGTLB_CACHE_SH_ISH) + +#define RGTLB_TO_PGTLB 8 +#define DECODER_PAGE_ENTRY_SIZE 64 +#define DECODER_PAGE_SIZE (1 << 12) +#define DECODER_PAGE_TABLE_SIZE (1 << 12) +#define PAGE_TABLE_PAGE_SIZE (DECODER_PAGE_ENTRY_SIZE * DECODER_PAGE_SIZE) +#define RANGE_TABLE_PAGE_SIZE (DECODER_PAGE_ENTRY_SIZE * \ + DECODER_PAGE_SIZE * \ + RGTLB_TO_PGTLB) + void ub_decoder_init(struct ub_entity *uent); void ub_decoder_uninit(struct ub_entity *uent); diff --git a/drivers/ub/ubus/omm.c b/drivers/ub/ubus/omm.c new file mode 100644 index 000000000000..d4c84b585f93 --- /dev/null +++ b/drivers/ub/ubus/omm.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus omm: " fmt + +#include "decoder.h" +#include "omm.h" + +enum entry_type { + INVALID_ENTRY = 0x0, + PAGE_TABLE = 0x1, + PAGE = 0x2, + RANGE_TABLE = 0x3, +}; + +struct page_table_entry { + u64 entry_type : 2; + u64 reserve1 : 10; + u64 next_lv_addr : 36; + u64 reserve2 : 8; + u64 pgtlb_attr : 8; +}; + +struct page_entry { + /* dw0 ~ dw1 */ + u64 entry_type : 2; + u64 reserve0 : 6; + u64 token_vld : 1; + u64 reserve1 : 3; + u64 ub_addr : 52; + /* dw2 ~ dw3 */ + u64 reserve2 : 32; + u64 tpg_num : 20; + u64 order_type : 2; + u64 order_id : 8; + u64 reserve3 : 2; + /* dw4 ~ dw5 */ + u64 dst_eid_low; + /* dw6 ~ dw7 */ + u64 dst_eid_high; + /* dw8 ~ dw9 */ + u64 token_id : 20; + u64 reserve4 : 12; + u64 token_value : 32; + /* dw10 ~ dw11 */ + u64 upi : 16; + u64 reserve5 : 48; + /* dw12 ~ dw13 */ + u64 reserve6; + /* dw14 ~ dw15 */ + u64 reserve7 : 32; + u64 src_eid : 20; + u64 reserve8 : 12; +}; + +struct range_table_entry { + /* dw0 ~ dw1 */ + u64 entry_type : 2; + u64 reserve0 : 6; + u64 token_vld : 1; + u64 reserve1 : 3; + u64 next_lv_addr : 36; + u64 reserve2 : 8; + u64 page_table_attr : 8; + /* dw2 ~ dw3 */ + u64 mem_base : 15; + u64 reserve3 : 1; + u64 mem_limit : 15; + u64 reserve4 : 1; + u64 reserve5 : 32; + /* dw4 ~ dw5 */ + u64 reserve6 : 12; + u64 ub_addr : 52; + /* dw6 ~ dw7 */ + u64 reserve7 : 32; + u64 tpg_num : 20; + u64 order_type : 2; + u64 order_id : 8; + u64 reserve8 : 2; + /* dw8 ~ dw9 */ + u64 dst_eid_low; + /* dw10 ~ dw11 */ + u64 dst_eid_high; + /* dw12 ~ dw13 */ + u64 token_id : 20; + u64 reserve9 : 12; + u64 token_value : 32; + /* dw14 ~ dw15 */ + u64 upi : 16; + u64 reserve10 : 16; + u64 src_eid : 20; + u64 reserve11 : 12; +}; + +#define DECODER_PAGE_TABLE_LOC 35 +#define DECODER_PAGE_TABLE_MASK GENMASK_ULL(43, 35) + +#define get_pgtlb_idx(decoder, pa) ((((pa) - (decoder)->mmio_base_addr) & \ + DECODER_PAGE_TABLE_MASK) >> \ + DECODER_PAGE_TABLE_LOC) + +static int handle_range_table(struct ub_decoder *decoder, u64 *offset, + struct decoder_map_info *info, bool is_map) +{ + return 0; +} + +static int handle_page_table(struct ub_decoder *decoder, u64 *offset, + struct decoder_map_info *info, bool is_map) +{ + return 0; +} + +static int handle_pgrg_table(struct ub_decoder *decoder, u64 *offset, + struct decoder_map_info *info, bool is_map) +{ + return 0; +} + +static int handle_table(struct ub_decoder *decoder, + struct decoder_map_info *info, bool is_map) +{ + struct page_table *pgtlb = &decoder->pgtlb; + struct range_table_entry *range_tlb_entry; + u64 offset, rollback_size; + u32 table_idx; + int ret; + + for (offset = 0; offset < info->size;) { + rollback_size = offset; + + mutex_lock(&decoder->table_lock); + table_idx = get_pgtlb_idx(decoder, info->pa + offset); + range_tlb_entry = (struct range_table_entry *) + pgtlb->pgtlb_base + table_idx; + + if (range_tlb_entry->entry_type == RANGE_TABLE) + ret = handle_range_table(decoder, &offset, info, + is_map); + else if (range_tlb_entry->next_lv_addr == + decoder->invalid_page_dma) + ret = handle_pgrg_table(decoder, &offset, info, is_map); + else + ret = handle_page_table(decoder, &offset, info, is_map); + mutex_unlock(&decoder->table_lock); + + if (ret) { + ub_err(decoder->uent, "decoder table occur fatal error ret=%d\n", + ret); + /* if it is map operation, revert it. unmap operation can't revert */ + if (is_map) + (void)ub_decoder_unmap(decoder, info->pa, + rollback_size); + break; + } + } + return ret; +} + +int ub_decoder_unmap(struct ub_decoder *decoder, phys_addr_t addr, u64 size) +{ + int ret; + struct decoder_map_info info = { + .pa = addr, + .size = size, + }; + + if (!decoder) { + pr_err("unmap mmio decoder ptr is null\n"); + return -EINVAL; + } + if (size < SZ_1M) + size = SZ_1M; + ret = handle_table(decoder, &info, false); + + return ret; +} + +int ub_decoder_map(struct ub_decoder *decoder, struct decoder_map_info *info) +{ + if (!decoder || !info) { + pr_err("decoder or map info is null\n"); + return -EINVAL; + } + if (info->size < SZ_1M) + info->size = SZ_1M; + ub_info(decoder->uent, + "decoder map, pa=%#llx, uba=%#llx, size=%#llx, cna=%#x, orderid=%#x, ordertype=%#x, eid_l=%#llx, eid_h=%#llx, upi=%#x src_eid=%#x\n", + info->pa, info->uba, info->size, info->tpg_num, info->order_id, + info->order_type, info->eid_low, info->eid_high, info->upi, + info->src_eid); + + return handle_table(decoder, info, true); +} + +void ub_decoder_init_page_table(struct ub_decoder *decoder, void *pgtlb_base) +{ + struct page_table_entry *pgtlb_entry; + int i; + + for (i = 0; i < DECODER_PAGE_TABLE_SIZE; i++) { + pgtlb_entry = (struct page_table_entry *)pgtlb_base + i; + pgtlb_entry->entry_type = PAGE_TABLE; + pgtlb_entry->next_lv_addr = decoder->invalid_page_dma; + pgtlb_entry->pgtlb_attr = PGTLB_ATTR_DEFAULT; + } +} diff --git a/drivers/ub/ubus/omm.h b/drivers/ub/ubus/omm.h new file mode 100644 index 000000000000..4258a7609736 --- /dev/null +++ b/drivers/ub/ubus/omm.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __OMM_H__ +#define __OMM_H__ + +struct decoder_map_info { + phys_addr_t pa; + phys_addr_t uba; + u64 size; + u32 tpg_num; + u8 order_id; + u8 order_type; + u64 eid_low; + u64 eid_high; + u32 token_id; + u32 token_value; + u32 upi; + u32 src_eid; +}; + +void ub_decoder_init_page_table(struct ub_decoder *decoder, void *pgtlb_base); +int ub_decoder_unmap(struct ub_decoder *decoder, phys_addr_t addr, u64 size); +int ub_decoder_map(struct ub_decoder *decoder, struct decoder_map_info *info); + +#endif /* __OMM_H__ */ -- Gitee From 4011fd20abe2babad17578ee041879f965088559 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Sat, 20 Sep 2025 14:05:39 +0800 Subject: [PATCH 039/214] ub:ubus: Support UBUS RAS capability ANBZ: #45460 commit b16c9013e8adf3fbfb1f5a016785fd034c744a1c openEuler. Support UBUS Reliability, Availability and Serviceability capability, including error report, ras recover and so on. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 + drivers/ub/ubus/services/ras.c | 337 +++++++++++++++++++++++++++++++++ drivers/ub/ubus/services/ras.h | 40 ++++ include/ub/ubus/ubus.h | 19 ++ 4 files changed, 398 insertions(+) create mode 100644 drivers/ub/ubus/services/ras.c create mode 100644 drivers/ub/ubus/services/ras.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index e0f036870acb..b5731e2082b7 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -6,4 +6,6 @@ obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o omm.o +ubus-y += services/ras.o + obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/services/ras.c b/drivers/ub/ubus/services/ras.c new file mode 100644 index 000000000000..c38b300e1646 --- /dev/null +++ b/drivers/ub/ubus/services/ras.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus ras: " fmt + +#include +#include +#include + +#include "../ubus.h" +#include "../msg.h" +#include "../reset.h" +#include "ras.h" + +#define RAS_CE 0 +#define RAS_UC_NFE 1 +#define RAS_UC_FE 2 +#define RAS_RECOVER_RING_SIZE 16 +#define RAS_SEVERITY_STR(severity) \ + ((severity) == RAS_CE ? "CORRECTABLE" : \ + (severity) == RAS_UC_NFE ? "UNCORRECTABLE NONFATAL" : \ + (severity) == RAS_UC_FE ? "UNCORRECTABLE FATAL" : \ + "UNKNOWN") + +enum ras_err_level { + RAS_ERR_PORT_LEVEL, + RAS_ERR_DEVICE_LEVEL, +}; + +int cper_severity_to_ub_ras(int cper_severity) +{ + switch (cper_severity) { + case CPER_SEV_FATAL: + return RAS_UC_FE; + case CPER_SEV_RECOVERABLE: + return RAS_UC_NFE; + default: + return RAS_CE; + } +} + +static void print_ub_err_info(struct ub_entity *uent, u32 *spec_info, u32 *vendor_info) +{ +#define DW_EACH_ROUND 4 +#define DW_0 0 +#define DW_1 1 +#define DW_2 2 +#define DW_3 3 + for (int i = 0; i < RAS_SPEC_ERR_PKG_LEN; i += DW_EACH_ROUND) + ub_err(uent, "SPEC Err Pkg Header=%08x %08x %08x %08x\n", + spec_info[i], spec_info[i + DW_1], + spec_info[i + DW_2], spec_info[i + DW_3]); + + ub_err(uent, "VENDOR Err Pkg Header=%08x %08x\n", vendor_info[DW_0], vendor_info[DW_1]); +} + +static void cper_print_ub_err(struct ub_entity *uent, struct ras_recover_entry *entry) +{ + struct ras_cap_regs *reg = entry->regs; + + ub_err(uent, "UBUS ERR type=%s.\n", RAS_SEVERITY_STR(entry->severity)); + + if (entry->err_level == RAS_ERR_PORT_LEVEL) + ub_err(uent, "Error level: PORT ERR, port=%u.\n", entry->port); + else + ub_err(uent, "Error level: DEVICE ERR.\n"); + + if (!reg) { + ub_err(uent, "No error detail need to show.\n"); + return; + } + + ub_err(uent, "Uncorrectable ERR status=%#08llx, mask=%#08llx, severity=%#08llx\n", + reg->uncor_status, reg->uncor_mask, reg->uncor_severity); + ub_err(uent, "Correctable ERR status=%#08llx, mask=%#08llx, severity=%#08llx\n", + reg->cor_status, reg->cor_mask, reg->cor_severity); + + print_ub_err_info(uent, reg->spec_def_err_info, reg->vendor_def_err_info); +} + +static ub_ers_result_t ras_merge_result(ub_ers_result_t orig, + ub_ers_result_t new_one) +{ + ub_ers_result_t ret = orig; + + if (new_one == UB_ERS_RESULT_NO_ERR_DRIVER) + return UB_ERS_RESULT_NO_ERR_DRIVER; + + if (new_one == UB_ERS_RESULT_NONE) + return ret; + + switch (orig) { + case UB_ERS_RESULT_CAN_RECOVER: + case UB_ERS_RESULT_RECOVERED: + ret = new_one; + break; + case UB_ERS_RESULT_DISCONNECT: + if (new_one == UB_ERS_RESULT_NEED_RESET) + ret = new_one; + break; + default: + pr_err("unused ras error result %d\n", new_one); + break; + } + + return ret; +} + +static ub_ers_result_t report_resource_enabled(struct ub_entity *uent, + ub_ers_result_t orig) +{ + ub_ers_result_t result, new_one; + struct ub_driver *udrv; + + device_lock(&uent->dev); + udrv = uent->driver; + if (!udrv || !udrv->err_handler || !udrv->err_handler->ub_resource_enabled) { + result = orig; + goto out; + } + + new_one = udrv->err_handler->ub_resource_enabled(uent); + result = ras_merge_result(orig, new_one); +out: + device_unlock(&uent->dev); + return result; +} + +static void ub_uevent_ers(struct ub_entity *uent, ub_ers_result_t err_type) +{ +#define MAX_ENVP 3 + char *envp[MAX_ENVP]; + int idx = 0; + + switch (err_type) { + case UB_ERS_RESULT_NONE: + case UB_ERS_RESULT_CAN_RECOVER: + envp[idx++] = "ERROR_EVENT=BEGIN_RECOVERY"; + envp[idx++] = "DEVICE_ONLINE=0"; + break; + case UB_ERS_RESULT_RECOVERED: + envp[idx++] = "ERROR_EVENT=SUCCESSFUL_RECOVERY"; + envp[idx++] = "DEVICE_ONLINE=1"; + break; + case UB_ERS_RESULT_DISCONNECT: + envp[idx++] = "ERROR_EVENT=FAILED_RECOVERY"; + envp[idx++] = "DEVICE_ONLINE=0"; + break; + default: + pr_err("unused uevent ras err_type %d\n", err_type); + break; + } + + if (idx > 0) { + envp[idx++] = NULL; + kobject_uevent_env(&uent->dev.kobj, KOBJ_CHANGE, envp); + } +} + +static ub_ers_result_t ub_error_report(struct ub_entity *uent, ub_channel_state_t state) +{ + struct ub_driver *udrv; + ub_ers_result_t result; + + device_lock(&uent->dev); + udrv = uent->driver; + if (!udrv || !udrv->err_handler || !udrv->err_handler->ub_error_detected) { + ub_warn(uent, "No driver bounded or no handler provided.\n"); + result = UB_ERS_RESULT_NO_ERR_DRIVER; + goto exit; + } + + result = udrv->err_handler->ub_error_detected(uent, state); + ub_uevent_ers(uent, result); + result = ras_merge_result(UB_ERS_RESULT_CAN_RECOVER, result); +exit: + device_unlock(&uent->dev); + return result; +} + +static void ub_do_recovery(struct ub_entity *uent, ub_channel_state_t state, + struct ras_recover_entry *entry) +{ + u32 port_id = entry->port; + ub_ers_result_t ret; + int recovery; + + ret = ub_error_report(uent, state); + if (ret == UB_ERS_RESULT_NEED_RESET) { + if (entry->err_level == RAS_ERR_PORT_LEVEL) + recovery = ub_port_reset(uent, port_id); + else + recovery = is_ibus_controller(uent) ? 0 : ub_device_reset(uent); + + if (!recovery) + ret = UB_ERS_RESULT_RECOVERED; + else + goto failed; + } + + if (ret == UB_ERS_RESULT_CAN_RECOVER) + ret = report_resource_enabled(uent, UB_ERS_RESULT_RECOVERED); + + if (ret != UB_ERS_RESULT_RECOVERED) + goto failed; + + ub_info(uent, "uent ras recovery succeeded\n"); + return; +failed: + ub_uevent_ers(uent, UB_ERS_RESULT_DISCONNECT); + ub_err(uent, "uent ras recovery failed\n"); +} + +static inline void ras_cap_regs_free(struct ras_cap_regs **regs) +{ + if (!regs || !(*regs)) + return; + + kfree(*regs); + *regs = NULL; +} + +static DEFINE_KFIFO(ras_recover_ring, struct ras_recover_entry, + RAS_RECOVER_RING_SIZE); + +static void ub_ras_recover_work_func(struct work_struct *work) +{ + struct ras_recover_entry entry; + struct ub_entity *uent; + + while (kfifo_get(&ras_recover_ring, &entry)) { + uent = ub_get_ent_by_eid(entry.eid); + if (!uent) { + pr_err("can not find ub entity by eid=%#05x\n", + entry.eid); + ras_cap_regs_free(&entry.regs); + continue; + } + + cper_print_ub_err(uent, &entry); + if (uent->ubc->cluster) { + ub_err(uent, "UB RAS: no need to recover with cluster mode\n"); + } else { + if (entry.severity == RAS_UC_FE) + ub_do_recovery(uent, ub_channel_io_frozen, &entry); + else if (entry.severity == RAS_UC_NFE) + ub_do_recovery(uent, ub_channel_io_normal, &entry); + } + + ras_cap_regs_free(&entry.regs); + ub_entity_put(uent); + } +} + +static inline void ras_recover_entry_init(struct ras_recover_entry *entry, + struct cper_sec_ubus *ubus_err) +{ + entry->eid = ubus_err->eid; + entry->cna = ubus_err->cna; + entry->port = ubus_err->port; + entry->vendor = ubus_err->vendor; + entry->device = ubus_err->device; + entry->type = ubus_err->type; + entry->class_code = ubus_err->class_code; +} + +static DEFINE_SPINLOCK(ub_ras_recover_ring_lock); +static DECLARE_WORK(ub_ras_recover_work, ub_ras_recover_work_func); + +void ub_ras_recover_queue(struct cper_sec_ubus *ubus_err, int severity) +{ +#define PORT_VALID_BIT 0b100ULL +#define OVERFLOW_FLAG_BIT 0b10000ULL + int ub_severity = cper_severity_to_ub_ras(severity); + struct ras_cap_regs *ras_cap, *tmp_cap; + struct ras_recover_entry entry = {}; + u32 overflow = 0; + u32 err_status; + u32 err_level; + + if (ubus_err->validation_bits & OVERFLOW_FLAG_BIT) { + overflow = ubus_err->overflow_flag; + pr_info("severity %s is overflowed, flag=%#x\n", + RAS_SEVERITY_STR(ub_severity), overflow); + } + + tmp_cap = (struct ras_cap_regs *)ubus_err->err_info; + err_status = (ub_severity == RAS_CE) ? + tmp_cap->cor_status : tmp_cap->uncor_status; + if (err_status == 0 && overflow == 0) { + pr_info("no need to handle ras with err_status=0x%x, severity=%s\n", + err_status, RAS_SEVERITY_STR(ub_severity)); + return; + } + + ras_cap = kzalloc(sizeof(*ras_cap), GFP_ATOMIC); + if (!ras_cap) + return; + + memcpy(ras_cap, tmp_cap, sizeof(struct ras_cap_regs)); + err_level = (ubus_err->validation_bits & PORT_VALID_BIT) ? + RAS_ERR_PORT_LEVEL : RAS_ERR_DEVICE_LEVEL; + ras_recover_entry_init(&entry, ubus_err); + entry.err_level = err_level; + entry.severity = ub_severity; + entry.overflow = overflow; + entry.regs = ras_cap; + + if (kfifo_in_spinlocked(&ras_recover_ring, &entry, 1, + &ub_ras_recover_ring_lock)) { + schedule_work(&ub_ras_recover_work); + return; + } + + if (ub_severity == RAS_UC_FE) + pr_err("FIFO ring overflow when recovering error for eid:%05x, severity:%s\n", + ubus_err->eid, RAS_SEVERITY_STR(ub_severity)); + else + pr_err("FIFO ring overflow when recovering error for eid:%05x, port:%u, severity:%s\n", + ubus_err->eid, ubus_err->port, + RAS_SEVERITY_STR(ub_severity)); + + kfree(ras_cap); +} + +void ub_ras_init(void) +{ + ub_ras_register_recover_func(ub_ras_recover_queue); +} + +void ub_ras_uninit(void) +{ + ub_ras_register_recover_func(NULL); + (void)flush_work(&ub_ras_recover_work); +} diff --git a/drivers/ub/ubus/services/ras.h b/drivers/ub/ubus/services/ras.h new file mode 100644 index 000000000000..c68c2dce8471 --- /dev/null +++ b/drivers/ub/ubus/services/ras.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __SERVICE_RAS_H__ +#define __SERVICE_RAS_H__ + +#define RAS_SPEC_ERR_PKG_LEN 32 + +#pragma pack(4) +struct ras_cap_regs { + u32 err_ctrl_reg; + u64 uncor_status; + u64 uncor_mask; + u64 uncor_severity; + u64 cor_status; + u64 cor_mask; + u64 cor_severity; + u32 err_info_ctrl_reg; + u32 spec_def_err_info[RAS_SPEC_ERR_PKG_LEN]; + u32 vendor_def_err_info[2]; +}; +#pragma pack() + +struct ras_recover_entry { + u32 eid; + u32 cna; + u32 port; + u32 vendor; + u16 device; + u16 type; + u16 class_code; + u16 err_level; + int severity; + u32 overflow; + struct ras_cap_regs *regs; +}; + +#endif /* __SERVICE_RAS_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 840c686a7c92..60f5f8f09585 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -39,6 +39,23 @@ (ub_resource_end((dev), (resno)) - \ ub_resource_start((dev), (resno)) + 1)) +typedef unsigned int __bitwise ub_channel_state_t; +enum { + ub_channel_io_normal = (__force ub_channel_state_t) 1, + ub_channel_io_frozen = (__force ub_channel_state_t) 2, + ub_channel_io_perm_failure = (__force ub_channel_state_t) 3, +}; + +typedef unsigned int __bitwise ub_ers_result_t; +enum ub_ers_result { + UB_ERS_RESULT_NONE = (__force ub_ers_result_t) 1, + UB_ERS_RESULT_CAN_RECOVER = (__force ub_ers_result_t) 2, + UB_ERS_RESULT_NEED_RESET = (__force ub_ers_result_t) 3, + UB_ERS_RESULT_DISCONNECT = (__force ub_ers_result_t) 4, + UB_ERS_RESULT_RECOVERED = (__force ub_ers_result_t) 5, + UB_ERS_RESULT_NO_ERR_DRIVER = (__force ub_ers_result_t) 6, +}; + #define UB_GUID_DW_NUM SZ_4 struct ub_bus_region { @@ -223,6 +240,8 @@ struct ub_error_handlers { /* UB function reset prepare or completed */ void (*ub_reset_prepare)(struct ub_entity *uent); void (*ub_reset_done)(struct ub_entity *uent); + ub_ers_result_t (*ub_error_detected)(struct ub_entity *uent, ub_channel_state_t state); + ub_ers_result_t (*ub_resource_enabled)(struct ub_entity *uent); }; struct ub_dynids { -- Gitee From 12c98c3c488725154248c0c6e024cb6c0c294947 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Sat, 20 Sep 2025 13:45:02 +0800 Subject: [PATCH 040/214] ub:ubus: Support for UB Character Device Driver ANBZ: #45460 commit a4be501c749ab460697eed6d1235e9645fa08b30 openEuler. Implement the character device driver for UB, providing an ioctl operation entry for user space. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/ioctl.c | 103 ++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/ioctl.h | 12 +++++ include/uapi/ub/ubus/ubus.h | 31 +++++++++++ 4 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/ioctl.c create mode 100644 drivers/ub/ubus/ioctl.h create mode 100644 include/uapi/ub/ubus/ubus.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index b5731e2082b7..9419727937bb 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o ras.o obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o -ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o omm.o +ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o omm.o ioctl.o ubus-y += services/ras.o diff --git a/drivers/ub/ubus/ioctl.c b/drivers/ub/ubus/ioctl.c new file mode 100644 index 000000000000..88da1eccbb5c --- /dev/null +++ b/drivers/ub/ubus/ioctl.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus ioctl: " fmt + +#include +#include + +#include "ubus.h" + +#define UBUS_MAX_DEVICES 1 +#define UBUS_DEVICE_NAME "unified_bus" +#define UBUS_CLASS_NAME "unified_bus" + +struct unified_bus_ctx { + dev_t devno; + struct cdev cdev; + struct class *ub_class; + struct device *dev; +}; +static struct unified_bus_ctx ubus_ctx; + +static int ubus_fops_open(struct inode *inode, struct file *filep) +{ + return 0; +} + +static int ubus_fops_release(struct inode *inode, struct file *filep) +{ + return 0; +} + +static long ubus_fops_unl_ioctl(struct file *filep, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case UBUS_IOCTL_GET_API_VERSION: + return UBUS_API_VERSION; + default: + pr_err("ubus ioctl cmd %#x not support\n", cmd); + return -ENOTTY; + } +} + +static const struct file_operations ubus_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = ubus_fops_unl_ioctl, + .open = ubus_fops_open, + .release = ubus_fops_release, +}; + +int ub_cdev_init(void) +{ + struct cdev *cdev = &ubus_ctx.cdev; + struct device *dev; + struct class *cls; + dev_t devno; + int ret; + + ret = alloc_chrdev_region(&devno, 0, UBUS_MAX_DEVICES, UBUS_DEVICE_NAME); + if (ret) + return ret; + + cdev_init(cdev, &ubus_fops); + ret = cdev_add(cdev, devno, UBUS_MAX_DEVICES); + if (ret) + goto out_unregister; + + cls = class_create(UBUS_CLASS_NAME); + if (IS_ERR(cls)) { + ret = PTR_ERR(cls); + goto out_del; + } + + dev = device_create(cls, NULL, devno, NULL, UBUS_DEVICE_NAME); + if (IS_ERR(dev)) { + ret = PTR_ERR(dev); + goto out_destroy; + } + + ubus_ctx.ub_class = cls; + ubus_ctx.dev = dev; + ubus_ctx.devno = devno; + return 0; + +out_destroy: + class_destroy(cls); +out_del: + cdev_del(cdev); +out_unregister: + unregister_chrdev_region(devno, UBUS_MAX_DEVICES); + return ret; +} + +void ub_cdev_uninit(void) +{ + device_destroy(ubus_ctx.ub_class, ubus_ctx.devno); + class_destroy(ubus_ctx.ub_class); + cdev_del(&ubus_ctx.cdev); + unregister_chrdev_region(ubus_ctx.devno, UBUS_MAX_DEVICES); +} diff --git a/drivers/ub/ubus/ioctl.h b/drivers/ub/ubus/ioctl.h new file mode 100644 index 000000000000..139a5bfc0112 --- /dev/null +++ b/drivers/ub/ubus/ioctl.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __IOCTL_H__ +#define __IOCTL_H__ + +int ub_cdev_init(void); +void ub_cdev_uninit(void); + +#endif /* __IOCTL_H__ */ diff --git a/include/uapi/ub/ubus/ubus.h b/include/uapi/ub/ubus/ubus.h new file mode 100644 index 000000000000..c2de27e016db --- /dev/null +++ b/include/uapi/ub/ubus/ubus.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef _UAPI_UB_UBUS_UBUS_H_ +#define _UAPI_UB_UBUS_UBUS_H_ + +#include +#include + +#define UBUS_API_VERSION 0 + +/* Kernel & User level defines for UBUS IOCTLs. */ + +#define UBUS_TYPE ('U') + +/* -------- IOCTLs for UBUS file descriptor (/dev/unified_bus) -------- */ + +/** + * UBUS_GET_API_VERSION - _IO(UBUS_TYPE, 0) + * + * Report the version of the UBUS API. This allows us to bump the entire + * API version should we later need to add or change features in incompatible + * ways. + * Return: UBUS_API_VERSION + * Availability: Always + */ +#define UBUS_IOCTL_GET_API_VERSION _IO(UBUS_TYPE, 0) + +#endif /* _UAPI_UB_UBUS_UBUS_H_ */ -- Gitee From ecacd9c090e9ddb23b41c1e6d9ace587fdfc3a6f Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Sat, 20 Sep 2025 14:05:19 +0800 Subject: [PATCH 041/214] ub:ubus: Support for Bus EID-UPI Table Configuration ANBZ: #45460 commit 338de4f4c6b80c8e1c1957bde874548d8a3b5412 openEuler. Implement the initialization and deinitialization of the EID-UPI table, as well as the corresponding configuration table item interfaces. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/eu.c | 149 ++++++++++++++++++++++++++++++ drivers/ub/ubus/eu.h | 29 ++++++ drivers/ub/ubus/ubus_controller.h | 3 + drivers/ub/ubus/ubus_entity.c | 3 + include/ub/ubus/ubus.h | 3 + 6 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/eu.c create mode 100644 drivers/ub/ubus/eu.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 9419727937bb..36ab665f4f84 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o ras.o obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o -ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o omm.o ioctl.o +ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o omm.o ioctl.o eu.o ubus-y += services/ras.o diff --git a/drivers/ub/ubus/eu.c b/drivers/ub/ubus/eu.c new file mode 100644 index 000000000000..97e040eadd5e --- /dev/null +++ b/drivers/ub/ubus/eu.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus eu: " fmt + +#include + +#include "ubus.h" +#include "ubus_controller.h" +#include "eu.h" + +/* + * The ubus driver provides only the eid-upi table memory. + * The table configuration policy is developed by the vendor. + * Implemented in a unified manner in the future. + */ + +static int ub_eu_table_init_common(struct ub_entity *uent) +{ + struct ub_bus_controller *ubc = uent->ubc; + struct ub_eu_table *eu; + u32 entries, cfg_ret; + int ret; + + ret = ub_cfg_read_dword(uent, UB_EU_TEN, &entries); + if (ret) { + ub_err(uent, "EU TABLE get failed, ret=%d\n", ret); + return ret; + } + if (entries == 0) { + ub_err(uent, "EID-UPI Table Entry Num is 0\n"); + return -EINVAL; + } + + ub_info(uent, "EU TABLE entries num is %#x\n", entries); + + eu = kzalloc(sizeof(*eu), GFP_KERNEL); + if (!eu) + return -ENOMEM; + + eu->entries = entries; + eu->size = entries * EU_ENTRY_SIZE; + eu->addr = dma_alloc_coherent(&ubc->dev, eu->size, &eu->dma_addr, + GFP_KERNEL); + if (!eu->addr) { + ret = -ENOMEM; + goto free; + } + + cfg_ret = (u32)ub_cfg_write_dword(uent, UB_EU_TBA_L, + lower_32_bits(eu->dma_addr)); + cfg_ret |= (u32)ub_cfg_write_dword(uent, UB_EU_TBA_H, + upper_32_bits(eu->dma_addr)); + if (cfg_ret) { + ret = (int)cfg_ret; + ub_err(uent, "EU TABLE set failed, ret=%d\n", ret); + goto dma_free; + } + + memset(eu->addr, 0, eu->size); + uent->eu_table = eu; + + return 0; + +dma_free: + dma_free_coherent(&ubc->dev, eu->size, eu->addr, eu->dma_addr); +free: + kfree(eu); + return ret; +} + +static void ub_eu_table_uninit_common(struct ub_entity *uent) +{ + struct ub_eu_table *eu = uent->eu_table; + struct ub_bus_controller *ubc = uent->ubc; + u32 ret; + + uent->eu_table = NULL; + + ret = (u32)ub_cfg_write_dword(uent, UB_EU_TBA_L, 0); + ret |= (u32)ub_cfg_write_dword(uent, UB_EU_TBA_H, 0); + if (ret) + ub_err(uent, "EU TABLE unset failed, ret=%d\n", (int)ret); + + dma_free_coherent(&ubc->dev, eu->size, eu->addr, eu->dma_addr); + kfree(eu); +} + +void ub_eu_table_init(struct ub_entity *uent) +{ + struct ub_bus_controller *ubc = uent->ubc; + int ret; + + if (!is_ibus_controller(uent)) + return; + + ret = ub_eu_table_init_common(uent); + if (ret) + return; + + if (ubc->ops && ubc->ops->eu_table_init) { + ret = ubc->ops->eu_table_init(ubc); + if (ret) { + ub_err(uent, "EU TABLE private init failed, ret=%d\n", + ret); + goto uninit_common; + } + } + + ub_cfg_write_byte(uent, UB_TH_EN, 1); + + return; + +uninit_common: + ub_eu_table_uninit_common(uent); +} + +void ub_eu_table_uninit(struct ub_entity *uent) +{ + struct ub_bus_controller *ubc = uent->ubc; + + if (!uent->eu_table) + return; + + ub_cfg_write_byte(uent, UB_TH_EN, 0); + + if (ubc->ops && ubc->ops->eu_table_uninit) + ubc->ops->eu_table_uninit(ubc); + + ub_eu_table_uninit_common(uent); +} + +int ub_cfg_eu_table(struct ub_bus_controller *ubc, bool flag, u32 eid, u16 upi) +{ + struct ub_bus_controller_ops *ops = ubc->ops; + int ret; + + if (!ops || !ops->eu_cfg) + return -ENODEV; + + ret = ops->eu_cfg(ubc, flag, eid, upi); + if (ret) + dev_err(&ubc->dev, "eu %s fail, eid[%#05x]<->upi[%#04x]\n", + flag ? "add" : "del", eid, upi); + + return ret; +} diff --git a/drivers/ub/ubus/eu.h b/drivers/ub/ubus/eu.h new file mode 100644 index 000000000000..cc527feaacd5 --- /dev/null +++ b/drivers/ub/ubus/eu.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ +#ifndef __EU_H__ +#define __EU_H__ + +struct ub_eu_table { /* eid-upi table */ + dma_addr_t dma_addr; + void *addr; + size_t size; + u32 entries; + void *private_data; /* For ubc vendor private */ +}; + +#pragma pack(2) +struct ub_eu_entry { + u32 eid[SZ_4]; + u16 upi; +}; +#pragma pack() + +#define EU_ENTRY_SIZE 18 + +void ub_eu_table_init(struct ub_entity *uent); +void ub_eu_table_uninit(struct ub_entity *uent); +int ub_cfg_eu_table(struct ub_bus_controller *ubc, bool flag, u32 eid, u16 upi); + +#endif /* __EU_H__ */ diff --git a/drivers/ub/ubus/ubus_controller.h b/drivers/ub/ubus/ubus_controller.h index 31a79840966d..0e956a3c431e 100644 --- a/drivers/ub/ubus/ubus_controller.h +++ b/drivers/ub/ubus/ubus_controller.h @@ -8,6 +8,9 @@ struct ub_bus_controller; struct ub_bus_controller_ops { + int (*eu_table_init)(struct ub_bus_controller *ubc); + void (*eu_table_uninit)(struct ub_bus_controller *ubc); + int (*eu_cfg)(struct ub_bus_controller *ubc, bool flag, u32 eid, u16 upi); int (*entity_enable)(struct ub_entity *uent, u8 enable); }; diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index f72f97bc06c6..f0e45b496b2f 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -21,6 +21,7 @@ #include "ubus_driver.h" #include "ubus_inner.h" #include "cap.h" +#include "eu.h" #include "ubus_entity.h" /* @@ -309,10 +310,12 @@ EXPORT_SYMBOL_GPL(ub_setup_ent); static void ub_configure_ent(struct ub_entity *uent) { + ub_eu_table_init(uent); } static void ub_unconfigure_ent(struct ub_entity *uent) { + ub_eu_table_uninit(uent); } static int ub_ue_sort_by_ent_idx(void *priv, const struct list_head *a, diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 60f5f8f09585..b9d2623cd28b 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -230,6 +230,9 @@ struct ub_entity { /* UB saved config space */ u32 saved_config_space[24]; /* Config space saved at reset time */ + /* entity bus instance info */ + struct ub_eu_table *eu_table; + u32 support_feature; u16 upi; -- Gitee From 5b6c7616d3ced714beaa187e3a6f64b7222465af Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Tue, 26 Aug 2025 18:25:55 +0800 Subject: [PATCH 042/214] ub:ubus: Support for page-range table address mapping and unmapping ANBZ: #45460 commit 9c9c06bb4e5d08aa7b8a7202eb7c08b1ce917f06 openEuler. Construct address mapping and populate the page table with information retrieved from the decoder hardware lookup. Clear the established mapping relationships. Signed-off-by: Yuhao Xiang Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/omm.c | 228 +++++++++++++++++++++++++++++++++++++++++- drivers/ub/ubus/omm.h | 4 + 2 files changed, 229 insertions(+), 3 deletions(-) diff --git a/drivers/ub/ubus/omm.c b/drivers/ub/ubus/omm.c index d4c84b585f93..f536722d29a5 100644 --- a/drivers/ub/ubus/omm.c +++ b/drivers/ub/ubus/omm.c @@ -5,6 +5,7 @@ #define pr_fmt(fmt) "ubus omm: " fmt +#include #include "decoder.h" #include "omm.h" @@ -94,28 +95,249 @@ struct range_table_entry { u64 reserve11 : 12; }; -#define DECODER_PAGE_TABLE_LOC 35 -#define DECODER_PAGE_TABLE_MASK GENMASK_ULL(43, 35) +#define UBA_ADDR_OFFSET 12 + +#define DECODER_PAGE_INDEX_LOC 20 +#define DECODER_PAGE_TABLE_LOC 35 +#define DECODER_SUB_PAGE_TABLE_LOC 32 +#define DECODER_PAGE_TABLE_MASK GENMASK_ULL(43, 35) +#define DECODER_SUB_PAGE_TABLE_MASK GENMASK_ULL(34, 32) +#define DECODER_PGTLB_PAGE_MASK GENMASK_ULL(31, 20) +#define DECODER_RGTLB_PAGE_MASK GENMASK_ULL(34, 20) +#define DECODER_RGTLB_ADDRESS_MASK GENMASK_ULL(34, 20) +#define DECODER_RGTLB_ADDRESS_OFFSET 20 +#define TOKEN_VALID_MASK GENMASK(0, 0) #define get_pgtlb_idx(decoder, pa) ((((pa) - (decoder)->mmio_base_addr) & \ DECODER_PAGE_TABLE_MASK) >> \ DECODER_PAGE_TABLE_LOC) +#define get_rgtlb_addr(decoder, pa) ((((pa) - (decoder)->mmio_base_addr) & \ + DECODER_RGTLB_ADDRESS_MASK) >> \ + DECODER_RGTLB_ADDRESS_OFFSET) + +#define get_page_idx(decoder, pa) ((((pa) - (decoder)->mmio_base_addr) & \ + DECODER_PGTLB_PAGE_MASK) >> \ + DECODER_PAGE_INDEX_LOC) + +#define get_pgtlb_sub_idx(decoder, pa) ((((pa) - (decoder)->mmio_base_addr) & \ + DECODER_SUB_PAGE_TABLE_MASK) >> \ + DECODER_SUB_PAGE_TABLE_LOC) + +static void fill_page_entry(struct page_entry *page, + struct decoder_map_info *info, u64 offset) +{ + page->entry_type = PAGE; + page->token_vld = ubc_feature & TOKEN_VALID_MASK; + page->ub_addr = (info->uba + offset) >> UBA_ADDR_OFFSET; + page->tpg_num = info->tpg_num; + page->order_type = info->order_type; + page->order_id = info->order_id; + page->dst_eid_low = info->eid_low; + page->dst_eid_high = info->eid_high; + page->token_id = info->token_id; + page->token_value = info->token_value; + page->upi = info->upi; + page->src_eid = info->src_eid; +} + static int handle_range_table(struct ub_decoder *decoder, u64 *offset, struct decoder_map_info *info, bool is_map) { return 0; } +static int pgtlb_alloc_page(struct ub_decoder *decoder, + struct page_table_desc *desc, + struct page_table_entry *pgtlb_entry) +{ + void *page_base; + + page_base = dmam_alloc_coherent(decoder->dev, PAGE_TABLE_PAGE_SIZE, + &desc->page_dma, GFP_KERNEL); + if (!page_base) + return -ENOMEM; + + desc->page_base = page_base; + desc->count = 0; + pgtlb_entry->entry_type = PAGE_TABLE; + pgtlb_entry->next_lv_addr = (desc->page_dma & + DECODER_PGTBL_PGPRT_MASK) >> + DECODER_DMA_PAGE_ADDR_OFFSET; + pgtlb_entry->pgtlb_attr = PGTLB_ATTR_DEFAULT; + return 0; +} + +static void pgtlb_free_page(struct ub_decoder *decoder, + struct page_table_desc *desc, + struct page_table_entry *pgtlb_entry) +{ + dmam_free_coherent(decoder->dev, PAGE_TABLE_PAGE_SIZE, desc->page_base, + desc->page_dma); + pgtlb_entry->next_lv_addr = decoder->invalid_page_dma; + pgtlb_entry->entry_type = PAGE_TABLE; + desc->page_base = NULL; + desc->page_dma = 0; +} + +static int pgtlb_map_to_page(struct ub_decoder *decoder, + struct page_table_desc *desc, + struct page_table_entry *pgtlb_entry, + struct decoder_map_info *info, u64 offset) +{ + struct page_entry *page; + u32 index; + int ret; + + if (!desc->page_base) { + ret = pgtlb_alloc_page(decoder, desc, pgtlb_entry); + if (ret) + return ret; + } + + index = get_page_idx(decoder, info->pa + offset); + + page = (struct page_entry *)desc->page_base + index; + + /* + * If it's the first time to apply for a page table, + * the error branch will not be taken. + */ + if (page->entry_type != INVALID_ENTRY) { + ub_err(decoder->uent, "decoder mapping page from page table error.\n"); + return -EINVAL; + } + + fill_page_entry(page, info, offset); + desc->count++; + return 0; +} + +static int pgtlb_unmap_to_page(struct ub_decoder *decoder, + struct page_table_desc *desc, + struct page_table_entry *pgtlb_entry, + struct decoder_map_info *info, u64 offset) +{ + struct page_entry *page; + u32 index; + + index = get_page_idx(decoder, info->pa + offset); + page = (struct page_entry *)desc->page_base + index; + + if (pgtlb_entry->next_lv_addr == decoder->invalid_page_dma || + page->entry_type != PAGE) { + ub_err(decoder->uent, "decoder unmap page from page table error.\n"); + return -EINVAL; + } + + page->entry_type = INVALID_ENTRY; + desc->count--; + + if (desc->count == 0) + pgtlb_free_page(decoder, desc, pgtlb_entry); + return 0; +} + static int handle_page_table(struct ub_decoder *decoder, u64 *offset, struct decoder_map_info *info, bool is_map) { - return 0; + struct page_table_entry *pgtlb_entry; + struct page_table_desc *desc; + struct page_table *pgtlb; + u32 table_idx; + u32 sub_index; + int ret; + + pgtlb = &decoder->pgtlb; + table_idx = get_pgtlb_idx(decoder, info->pa + *offset); + sub_index = get_pgtlb_sub_idx(decoder, info->pa + *offset); + desc = pgtlb->desc_base + table_idx * RGTLB_TO_PGTLB + sub_index; + pgtlb_entry = (struct page_table_entry *)pgtlb->pgtlb_base + + table_idx * RGTLB_TO_PGTLB + sub_index; + + if (is_map) + ret = pgtlb_map_to_page(decoder, desc, pgtlb_entry, info, + *offset); + else + ret = pgtlb_unmap_to_page(decoder, desc, pgtlb_entry, info, + *offset); + *offset += SZ_1M; + return ret; +} + +#define MEM_LMT_MAX 0x7FFF +#define RANGE_UBA_LOW_MASK GENMASK_ULL(34, 20) +#define RANGE_UBA_HIGH_MASK GENMASK_ULL(63, 35) +#define UBA_CARRY 0x800000000 +#define UBA_NOCARRY 0x0 + +static void fill_range_table(struct ub_decoder *decoder, + struct range_table_entry *rg_entry, + struct decoder_map_info *info, u64 *offset) +{ + u64 mem_base, mem_limit; + u64 uba_high, uba_low; + u64 size; + + mem_base = get_rgtlb_addr(decoder, info->pa + *offset); + size = (MEM_LMT_MAX - mem_base + 1ULL) << DECODER_RGTLB_ADDRESS_OFFSET; + + uba_low = (info->uba + *offset) & RANGE_UBA_LOW_MASK; + uba_high = (info->uba + *offset) & RANGE_UBA_HIGH_MASK; + + if (info->size - *offset >= size) { + mem_limit = MEM_LMT_MAX; + *offset += size; + } else { + mem_limit = get_rgtlb_addr(decoder, info->pa + info->size); + mem_limit--; + *offset += (mem_limit - mem_base + 1) << + DECODER_RGTLB_ADDRESS_OFFSET; + } + + rg_entry->entry_type = RANGE_TABLE; + rg_entry->next_lv_addr = decoder->invalid_page_dma; + rg_entry->page_table_attr = PGTLB_ATTR_DEFAULT; + rg_entry->token_vld = ubc_feature & TOKEN_VALID_MASK; + rg_entry->mem_base = mem_base; + rg_entry->mem_limit = mem_limit; + rg_entry->ub_addr = (uba_high | uba_low) >> UBA_ADDR_OFFSET; + rg_entry->tpg_num = info->tpg_num; + rg_entry->order_id = info->order_id; + rg_entry->order_type = info->order_type; + rg_entry->dst_eid_low = info->eid_low; + rg_entry->dst_eid_high = info->eid_high; + rg_entry->token_id = info->token_id; + rg_entry->token_value = info->token_value; + rg_entry->upi = info->upi; + rg_entry->src_eid = info->src_eid; } static int handle_pgrg_table(struct ub_decoder *decoder, u64 *offset, struct decoder_map_info *info, bool is_map) { + struct range_table_entry *rg_entry; + struct page_table_entry *pg_entry; + u32 table_idx, sub_index; + int i; + + if (!is_map) + return handle_page_table(decoder, offset, info, is_map); + + table_idx = get_pgtlb_idx(decoder, info->pa + *offset); + sub_index = get_pgtlb_sub_idx(decoder, info->pa + *offset); + pg_entry = (struct page_table_entry *)decoder->pgtlb.pgtlb_base + + table_idx * RGTLB_TO_PGTLB; + if (sub_index != 0 || info->size - *offset < decoder->rg_size) + return handle_page_table(decoder, offset, info, is_map); + + for (i = 0; i < RGTLB_TO_PGTLB; i++) + if ((pg_entry + i)->next_lv_addr != decoder->invalid_page_dma) + return handle_page_table(decoder, offset, info, is_map); + + ub_dbg(decoder->uent, "create range table\n"); + rg_entry = (struct range_table_entry *)pg_entry; + fill_range_table(decoder, rg_entry, info, offset); return 0; } diff --git a/drivers/ub/ubus/omm.h b/drivers/ub/ubus/omm.h index 4258a7609736..e90ce9cb1f7f 100644 --- a/drivers/ub/ubus/omm.h +++ b/drivers/ub/ubus/omm.h @@ -6,6 +6,10 @@ #ifndef __OMM_H__ #define __OMM_H__ +#include + +extern u8 ubc_feature; + struct decoder_map_info { phys_addr_t pa; phys_addr_t uba; -- Gitee From 9897167e4ace785b9c9ce662a89c72d40373f585 Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Tue, 26 Aug 2025 14:49:52 +0800 Subject: [PATCH 043/214] ub:ubus: Support for range table mapping and unmapping functions ANBZ: #45460 commit bf214d70e73ffc660a806ccb025f87c680f346d7 openEuler. Construct address mapping and populate the range table with information retrieved from the decoder hardware lookup. Clear the established mapping relationships. Signed-off-by: Yuhao Xiang Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/omm.c | 222 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 221 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubus/omm.c b/drivers/ub/ubus/omm.c index f536722d29a5..7cc304950209 100644 --- a/drivers/ub/ubus/omm.c +++ b/drivers/ub/ubus/omm.c @@ -116,6 +116,10 @@ struct range_table_entry { DECODER_RGTLB_ADDRESS_MASK) >> \ DECODER_RGTLB_ADDRESS_OFFSET) +#define get_rgtlb_page_idx(decoder, pa) ((((pa) - (decoder)->mmio_base_addr) & \ + DECODER_RGTLB_PAGE_MASK) >> \ + DECODER_PAGE_INDEX_LOC) + #define get_page_idx(decoder, pa) ((((pa) - (decoder)->mmio_base_addr) & \ DECODER_PGTLB_PAGE_MASK) >> \ DECODER_PAGE_INDEX_LOC) @@ -141,10 +145,226 @@ static void fill_page_entry(struct page_entry *page, page->src_eid = info->src_eid; } +static int rgtlb_alloc_page(struct ub_decoder *decoder, + struct page_table_desc *desc, + struct range_table_entry *entry) +{ + void *page_base; + + page_base = dmam_alloc_coherent(decoder->dev, RANGE_TABLE_PAGE_SIZE, + &desc->page_dma, GFP_KERNEL); + if (!page_base) + return -ENOMEM; + + desc->page_base = page_base; + desc->count = 0; + entry->next_lv_addr = (desc->page_dma & DECODER_PGTBL_PGPRT_MASK) >> + DECODER_DMA_PAGE_ADDR_OFFSET; + entry->page_table_attr = PGTLB_ATTR_DEFAULT; + return 0; +} + +static void rgtlb_free_page(struct ub_decoder *decoder, + struct page_table_desc *desc, + struct range_table_entry *rgtlb_entry) +{ + dmam_free_coherent(decoder->dev, RANGE_TABLE_PAGE_SIZE, desc->page_base, + desc->page_dma); + rgtlb_entry->next_lv_addr = decoder->invalid_page_dma; + desc->page_base = NULL; + desc->page_dma = 0; +} + +static int rgtlb_map(struct ub_decoder *decoder, u32 table_idx, + struct decoder_map_info *info, u64 *offset) +{ + struct range_table_entry *rgtlb_entry; + struct ub_entity *uent = decoder->uent; + struct page_table_desc *desc; + struct page_entry *page; + u32 index; + u64 addr; + + rgtlb_entry = (struct range_table_entry *)decoder->pgtlb.pgtlb_base + + table_idx; + desc = decoder->pgtlb.desc_base + table_idx * RGTLB_TO_PGTLB; + addr = get_rgtlb_addr(decoder, info->pa + *offset); + index = get_rgtlb_page_idx(decoder, info->pa + *offset); + if (addr >= rgtlb_entry->mem_base && addr <= rgtlb_entry->mem_limit) { + ub_err(uent, "decoder mapping addr already in range.\n"); + *offset += SZ_1M; + return -EINVAL; + } + + if (!desc->page_base) + if (rgtlb_alloc_page(decoder, desc, rgtlb_entry)) { + *offset += SZ_1M; + return -ENOMEM; + } + + /* + * If the page table is applied for the first time, + * the error branch is not used. + */ + page = (struct page_entry *)desc->page_base + index; + if (page->entry_type != INVALID_ENTRY) { + *offset += SZ_1M; + ub_err(uent, "page entry has been used\n"); + return -EINVAL; + } + + fill_page_entry(page, info, *offset); + desc->count++; + *offset += SZ_1M; + return 0; +} + +static int copy_rg_to_pg(struct ub_decoder *decoder, + struct page_table_desc *desc_base, + struct range_table_entry *rgtlb_entry, + struct page_entry *rg_base, int idx) +{ + struct page_table_entry *pgtlb_entry; + struct page_entry *rentry, *pentry; + struct page_table_desc *desc; + void *page_base; + int i; + + desc = desc_base + idx; + pgtlb_entry = (struct page_table_entry *)rgtlb_entry + idx; + page_base = dmam_alloc_coherent(decoder->dev, PAGE_TABLE_PAGE_SIZE, + &desc->page_dma, GFP_KERNEL); + if (!page_base) + return -ENOMEM; + + desc->page_base = page_base; + desc->count = 0; + pgtlb_entry->entry_type = PAGE_TABLE; + pgtlb_entry->next_lv_addr = (desc->page_dma & + DECODER_PGTBL_PGPRT_MASK) >> + DECODER_DMA_PAGE_ADDR_OFFSET; + pgtlb_entry->pgtlb_attr = PGTLB_ATTR_DEFAULT; + + pentry = (struct page_entry *)page_base; + rentry = (struct page_entry *)rg_base + idx * DECODER_PAGE_SIZE; + for (i = 0; i < DECODER_PAGE_SIZE; i++) { + if ((rentry + i)->entry_type == PAGE) { + desc->count++; + memcpy(pentry + i, rentry + i, DECODER_PAGE_ENTRY_SIZE); + } + } + return 0; +} + +static int rgtlb_unmap_to_range(struct ub_decoder *decoder, + struct page_table_desc *desc, + struct range_table_entry *rgtlb_entry) +{ + struct page_table_entry *pgtlb_entry; + struct page_entry *rg_base; + struct page_entry *entry; + dma_addr_t rg_dma; + int i, j, ret; + bool flag; + + for (i = 0; i < RGTLB_TO_PGTLB; i++) { + pgtlb_entry = (struct page_table_entry *)rgtlb_entry + i; + pgtlb_entry->entry_type = PAGE_TABLE; + pgtlb_entry->next_lv_addr = decoder->invalid_page_dma; + pgtlb_entry->pgtlb_attr = PGTLB_ATTR_DEFAULT; + } + + if (!desc->page_base) + return 0; + /* + * If l2 table exists, + * convert l2 table of range table to l2 table of the page table. + */ + rg_base = (struct page_entry *)desc->page_base; + rg_dma = desc->page_dma; + memset(desc, 0, sizeof(struct page_table_desc)); + for (i = 0; i < RGTLB_TO_PGTLB; i++) { + flag = false; + for (j = 0; j < DECODER_PAGE_SIZE; j++) { + entry = rg_base + i * DECODER_PAGE_SIZE + j; + if (entry->entry_type != INVALID_ENTRY) { + flag = true; + break; + } + } + if (flag) { + ret = copy_rg_to_pg(decoder, desc, rgtlb_entry, + rg_base, i); + if (ret) + return ret; + } + } + + dmam_free_coherent(decoder->dev, RANGE_TABLE_PAGE_SIZE, rg_base, + rg_dma); + return 0; +} + +static int rgtlb_unmap_to_page(struct ub_decoder *decoder, + struct page_table_desc *desc, + struct range_table_entry *rgtlb_entry, + u32 index) +{ + struct page_entry *page; + + page = (struct page_entry *)desc->page_base + index; + + if (page->entry_type != PAGE) { + ub_err(decoder->uent, "decoder unmap page from range table error.\n"); + return -EINVAL; + } + page->entry_type = INVALID_ENTRY; + desc->count--; + + if (desc->count == 0) + rgtlb_free_page(decoder, desc, rgtlb_entry); + return 0; +} + +static int rgtlb_unmap(struct ub_decoder *decoder, u32 table_idx, + struct decoder_map_info *info, u64 *offset) +{ + struct range_table_entry *rgtlb_entry; + struct page_table_desc *desc; + u32 sub_idx; + u64 begin; + u64 size; + + rgtlb_entry = (struct range_table_entry *)decoder->pgtlb.pgtlb_base + + table_idx; + desc = decoder->pgtlb.desc_base + table_idx * RGTLB_TO_PGTLB; + + begin = get_rgtlb_addr(decoder, info->pa + *offset); + size = (rgtlb_entry->mem_limit - rgtlb_entry->mem_base + 1ULL) * SZ_1M; + + if (begin == rgtlb_entry->mem_base) { + *offset += size; + if (info->size >= *offset) + return rgtlb_unmap_to_range(decoder, desc, rgtlb_entry); + ub_err(decoder->uent, "can't unmap part of range\n!"); + return -EINVAL; + } + sub_idx = get_rgtlb_page_idx(decoder, info->pa + *offset); + *offset += SZ_1M; + return rgtlb_unmap_to_page(decoder, desc, rgtlb_entry, sub_idx); +} + static int handle_range_table(struct ub_decoder *decoder, u64 *offset, struct decoder_map_info *info, bool is_map) { - return 0; + u32 table_idx; + + table_idx = get_pgtlb_idx(decoder, info->pa + *offset); + + if (is_map) + return rgtlb_map(decoder, table_idx, info, offset); + else + return rgtlb_unmap(decoder, table_idx, info, offset); } static int pgtlb_alloc_page(struct ub_decoder *decoder, -- Gitee From 9477a4ad7f9674bcfd18eed1ab7bf911e39dea6b Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Tue, 26 Aug 2025 20:34:00 +0800 Subject: [PATCH 044/214] ub:ubus: Support for sending decoder commands ANBZ: #45460 commit f5c8a844a64b285d0bef0132dde13032d1ba5e0b openEuler. Provide an interface to support writing commands to the command queue for sending to the UB decoder device. Signed-off-by: Yuhao Xiang Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/decoder.c | 308 +++++++++++++++++++++++++++++- drivers/ub/ubus/decoder.h | 49 ++++- drivers/ub/ubus/omm.c | 5 +- drivers/ub/ubus/ubus_controller.h | 2 + 4 files changed, 356 insertions(+), 8 deletions(-) diff --git a/drivers/ub/ubus/decoder.c b/drivers/ub/ubus/decoder.c index 9bb9dfb2f2a6..0853bc4415e5 100644 --- a/drivers/ub/ubus/decoder.c +++ b/drivers/ub/ubus/decoder.c @@ -6,23 +6,57 @@ #define pr_fmt(fmt) "ubus decoder: " fmt #include +#include #include #include "ubus.h" +#include "ubus_controller.h" #include "omm.h" #include "decoder.h" -#define MMIO_SIZE_MASK GENMASK_ULL(18, 16) -#define MMIO_SIZE_OFFSET 16 +#define MMIO_SIZE_MASK GENMASK_ULL(18, 16) +#define CMDQ_SIZE_MASK GENMASK_ULL(15, 12) +#define MMIO_SIZE_OFFSET 16 +#define CMDQ_SIZE_OFFSET 12 + +#define CMDQ_SIZE_USE_MASK GENMASK(11, 8) +#define CMDQ_SIZE_USE_OFFSET 8 +#define CMDQ_ENABLE 0x1 +#define CMD_ENTRY_SIZE 16 #define DECODER_PAGE_TABLE_ENTRY_SIZE 8 +#define DECODER_QUEUE_TIMEOUT_US 1000000 /* 1s */ + static void ub_decoder_uninit_queue(struct ub_decoder *decoder) -{} +{ + iounmap(decoder->cmdq.qbase); +} static int ub_decoder_init_queue(struct ub_bus_controller *ubc, struct ub_decoder *decoder) { + struct ub_entity *uent = ubc->uent; + + if (ubc->ops->register_decoder_base_addr) { + ubc->ops->register_decoder_base_addr(ubc, &decoder->cmdq.base, + &decoder->evtq.base); + } else { + ub_err(uent, + "ub_bus_controller_ops does not provide register_decoder_base_addr func, exit\n"); + return -EINVAL; + } + + if (decoder->cmdq.qs == 0) { + ub_err(uent, "decoder cmdq qs is 0\n"); + return -EINVAL; + } + + decoder->cmdq.qbase = ioremap(decoder->cmdq.base, + (1 << decoder->cmdq.qs) * CMD_ENTRY_SIZE); + if (!decoder->cmdq.qbase) + return -ENOMEM; + return 0; } @@ -56,7 +90,28 @@ static u32 set_page_table_reg(struct ub_decoder *decoder) static u32 set_queue_reg(struct ub_decoder *decoder) { - return 0; + u32 val, ret; + + /* init decoder cmdq base addr and pi ci */ + ret = (u32)ub_cfg_write_dword(decoder->uent, DECODER_CMDQ_BASE_ADDR0, + lower_32_bits(decoder->cmdq.base)); + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_CMDQ_BASE_ADDR1, + upper_32_bits(decoder->cmdq.base)); + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_CMDQ_PROD, 0); + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_CMDQ_CONS, 0); + + /* set decoder cmdq conf */ + ret |= (u32)ub_cfg_read_dword(decoder->uent, DECODER_CMDQ_CFG, &val); + decoder->vals.cmdq_cfg_val = val; + val &= ~CMDQ_SIZE_USE_MASK; + val |= decoder->cmdq.qs << CMDQ_SIZE_USE_OFFSET; + val |= CMDQ_ENABLE; + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_CMDQ_CFG, val); + + if (ret) + ub_err(decoder->uent, "set decoder queue failed\n"); + + return ret; } static u32 set_decoder_enable(struct ub_decoder *decoder) @@ -180,6 +235,7 @@ static int ub_get_decoder_cap(struct ub_decoder *decoder) } decoder->mmio_size_sup = (val & MMIO_SIZE_MASK) >> MMIO_SIZE_OFFSET; + decoder->cmdq.qs = (val & CMDQ_SIZE_MASK) >> CMDQ_SIZE_OFFSET; return 0; } @@ -247,7 +303,19 @@ static void unset_mmio_base_reg(struct ub_decoder *decoder) } static void unset_queue_reg(struct ub_decoder *decoder) -{} +{ + struct ub_entity *uent = decoder->uent; + u32 ret; + + ret = (u32)ub_cfg_write_dword(uent, DECODER_CMDQ_CFG, + decoder->vals.cmdq_cfg_val); + + ret |= (u32)ub_cfg_write_dword(uent, DECODER_CMDQ_BASE_ADDR0, 0); + ret |= (u32)ub_cfg_write_dword(uent, DECODER_CMDQ_BASE_ADDR1, 0); + + if (ret) + ub_err(uent, "unset queue reg failed\n"); +} static void unset_page_table_reg(struct ub_decoder *decoder) { @@ -296,6 +364,236 @@ static void ub_remove_decoder(struct ub_bus_controller *ubc) ubc->decoder = NULL; } +struct sync_entry { + u64 op : 8; + u64 reserve0 : 4; + u64 cm : 2; + u64 ntf_sh : 2; + u64 ntf_attr : 4; + u64 reserve1 : 12; + u64 notify_data : 32; + u64 reserve2 : 2; + u64 notify_addr : 50; + u64 reserve3 : 12; +}; + +struct tlbi_all_entry { + u32 op : 8; + u32 reserve0 : 24; + u32 reserve1; + u32 reserve2; + u32 reserve3; +}; + +struct tlbi_partial_entry { + u32 op : 8; + u32 reserve0 : 24; + u32 tlbi_addr_base : 28; + u32 reserve1 : 4; + u32 tlbi_addr_limt : 28; + u32 reserve2 : 4; + u32 reserve3; +}; + +#define TLBI_ADDR_MASK GENMASK_ULL(43, 20) +#define TLBI_ADDR_OFFSET 20 +#define CMDQ_ENT_DWORDS 2 + +#define NTF_SH_NSH 0b00 +#define NTF_SH_OSH 0b10 +#define NTF_SH_ISH 0b11 +#define NTF_ATTR_IR_NC 0b00 +#define NTF_ATTR_IR_WBRA 0b01 +#define NTF_ATTR_IR_WT 0b10 +#define NTF_ATTR_IR_WB 0b11 +#define NTF_ATTR_OR_NC 0b0000 +#define NTF_ATTR_OR_WBRA 0b0100 +#define NTF_ATTR_OR_WT 0b1000 +#define NTF_ATTR_OR_WB 0b1100 + +#define Q_IDX(qs, p) ((p) & ((1 << (qs)) - 1)) +#define Q_WRP(qs, p) ((p) & (1 << (qs))) +#define Q_OVF(p) ((p) & Q_OVERFLOW_FLAG) + +enum NOTIFY_TYPE { + DISABLE_NOTIFY = 0, + ENABLE_NOTIFY = 1, +}; + +static bool queue_has_space(struct ub_decoder_queue *q, u32 n) +{ + u32 space, prod, cons; + + prod = Q_IDX(q->qs, q->prod.cmdq_wr_idx); + cons = Q_IDX(q->qs, q->cons.cmdq_rd_idx); + + if (Q_WRP(q->qs, q->prod.cmdq_wr_idx) == + Q_WRP(q->qs, q->cons.cmdq_rd_idx)) + space = (1 << q->qs) - (prod - cons); + else + space = cons - prod; + + return space >= n; +} + +static u32 queue_inc_prod_n(struct ub_decoder_queue *q, u32 n) +{ + u32 prod = (Q_WRP(q->qs, q->prod.cmdq_wr_idx) | + Q_IDX(q->qs, q->prod.cmdq_wr_idx)) + n; + return Q_WRP(q->qs, prod) | Q_IDX(q->qs, prod); +} + +#define CMD_0_OP GENMASK_ULL(7, 0) +#define CMD_0_ADDR_BASE GENMASK_ULL(59, 32) +#define CMD_1_ADDR_LIMT GENMASK_ULL(27, 0) + +static void decoder_cmdq_issue_cmd(struct ub_decoder *decoder, phys_addr_t addr, + u64 size, enum ub_cmd_op_type op) +{ + struct ub_decoder_queue *cmdq = &(decoder->cmdq); + struct tlbi_partial_entry entry = {}; + u64 cmd[CMDQ_ENT_DWORDS] = {}; + void *pi; + int i; + + entry.op = op; + entry.tlbi_addr_base = (addr & TLBI_ADDR_MASK) >> TLBI_ADDR_OFFSET; + entry.tlbi_addr_limt = ((addr + size - 1U) & TLBI_ADDR_MASK) >> + TLBI_ADDR_OFFSET; + + cmd[0] |= FIELD_PREP(CMD_0_OP, entry.op); + cmd[0] |= FIELD_PREP(CMD_0_ADDR_BASE, entry.tlbi_addr_base); + cmd[1] |= FIELD_PREP(CMD_1_ADDR_LIMT, entry.tlbi_addr_limt); + + pi = cmdq->qbase + Q_IDX(cmdq->qs, cmdq->prod.cmdq_wr_idx) * + sizeof(struct tlbi_partial_entry); + + for (i = 0; i < CMDQ_ENT_DWORDS; i++) + writeq(cmd[i], pi + i * sizeof(u64)); + + cmdq->prod.cmdq_wr_idx = queue_inc_prod_n(cmdq, 1); +} + +#define NTF_DMA_ADDR_OFFSERT 2 +#define SYNC_0_OP GENMASK_ULL(7, 0) +#define SYNC_0_CM GENMASK_ULL(13, 12) +#define SYNC_0_NTF_ISH GENMASK_ULL(15, 14) +#define SYNC_0_NTF_ATTR GENMASK_ULL(19, 16) +#define SYNC_0_NTF_DATA GENMASK_ULL(63, 32) +#define SYNC_1_NTF_ADDR GENMASK_ULL(51, 2) +#define SYNC_NTF_DATA 0xffffffff + +static void decoder_cmdq_issue_sync(struct ub_decoder *decoder) +{ + struct ub_decoder_queue *cmdq = &(decoder->cmdq); + u64 cmd[CMDQ_ENT_DWORDS] = {}; + struct sync_entry entry = {}; + phys_addr_t sync_dma; + void __iomem *pi; + int i; + + entry.op = SYNC; + entry.cm = ENABLE_NOTIFY; + sync_dma = cmdq->base + Q_IDX(cmdq->qs, cmdq->prod.cmdq_wr_idx) * + sizeof(struct sync_entry); + entry.ntf_sh = NTF_SH_NSH; + entry.ntf_attr = NTF_ATTR_IR_NC | NTF_ATTR_OR_NC; + entry.notify_data = SYNC_NTF_DATA; + entry.notify_addr = sync_dma >> NTF_DMA_ADDR_OFFSERT; + + cmd[0] |= FIELD_PREP(SYNC_0_OP, entry.op); + cmd[0] |= FIELD_PREP(SYNC_0_CM, entry.cm); + cmd[0] |= FIELD_PREP(SYNC_0_NTF_ISH, entry.ntf_sh); + cmd[0] |= FIELD_PREP(SYNC_0_NTF_ATTR, entry.ntf_attr); + cmd[0] |= FIELD_PREP(SYNC_0_NTF_DATA, entry.notify_data); + cmd[1] |= FIELD_PREP(SYNC_1_NTF_ADDR, entry.notify_addr); + + pi = cmdq->qbase + Q_IDX(cmdq->qs, cmdq->prod.cmdq_wr_idx) * + sizeof(struct sync_entry); + for (i = 0; i < CMDQ_ENT_DWORDS; i++) + writeq(cmd[i], pi + i * sizeof(u64)); + + decoder->notify = pi; + cmdq->prod.cmdq_wr_idx = queue_inc_prod_n(cmdq, 1); +} + +static void decoder_cmdq_update_prod(struct ub_decoder *decoder) +{ + struct ub_entity *uent = decoder->uent; + struct queue_idx q; + int ret; + + ret = ub_cfg_read_dword(uent, DECODER_CMDQ_PROD, &q.val); + if (ret) + ub_err(uent, "update pi, read decoder cmdq prod failed\n"); + + decoder->cmdq.prod.cmdq_err_resp = q.cmdq_err_resp; + ret = ub_cfg_write_dword(uent, DECODER_CMDQ_PROD, + decoder->cmdq.prod.val); + if (ret) + ub_err(uent, "update pi, write decoder cmdq prod failed\n"); +} + +static int wait_for_cmdq_free(struct ub_decoder *decoder, u32 n) +{ + ktime_t timeout = ktime_add_us(ktime_get(), DECODER_QUEUE_TIMEOUT_US); + struct ub_decoder_queue *cmdq = &(decoder->cmdq); + struct ub_entity *uent = decoder->uent; + int ret; + + while (true) { + ret = ub_cfg_read_dword(uent, DECODER_CMDQ_CONS, + &(cmdq->cons.val)); + if (ret) + return ret; + + if (queue_has_space(cmdq, n + 1)) + return 0; + + if (ktime_compare(ktime_get(), timeout) > 0) { + ub_err(uent, "decoder cmdq wait free entry timeout\n"); + return -ETIMEDOUT; + } + cpu_relax(); + } +} + +static int wait_for_cmdq_notify(struct ub_decoder *decoder) +{ + ktime_t timeout; + u32 val; + + timeout = ktime_add_us(ktime_get(), DECODER_QUEUE_TIMEOUT_US); + while (true) { + val = readl(decoder->notify); + if (val == SYNC_NTF_DATA) + return 0; + + if (ktime_compare(ktime_get(), timeout) > 0) { + ub_err(decoder->uent, "decoder cmdq wait notify timeout\n"); + return -ETIMEDOUT; + } + cpu_relax(); + } +} + +int ub_decoder_cmd_request(struct ub_decoder *decoder, phys_addr_t addr, + u64 size, enum ub_cmd_op_type op) +{ + int ret; + + ret = wait_for_cmdq_free(decoder, 1); + if (ret) + return ret; + + decoder_cmdq_issue_cmd(decoder, addr, size, op); + decoder_cmdq_issue_sync(decoder); + decoder_cmdq_update_prod(decoder); + + ret = wait_for_cmdq_notify(decoder); + return ret; +} + void ub_decoder_init(struct ub_entity *uent) { int ret; diff --git a/drivers/ub/ubus/decoder.h b/drivers/ub/ubus/decoder.h index 6b0fcee2681e..c2d623244bcc 100644 --- a/drivers/ub/ubus/decoder.h +++ b/drivers/ub/ubus/decoder.h @@ -8,6 +8,49 @@ #include +enum ub_cmd_op_type { + INVALID = 0x0, + SYNC = 0x1, + TLBI_ALL = 0x2, + TLBI_PARTIAL = 0x3, +}; + +struct queue_idx { + union { + u32 val; + struct { + u32 cmdq_wr_idx : 11; + u32 reserved1 : 5; + u32 cmdq_err_resp : 1; + u32 reserved2 : 15; + }; + struct { + u32 cmdq_rd_idx : 11; + u32 reserved3 : 5; + u32 cmdq_err : 1; + u32 cmdq_err_reason : 3; + u32 reserved4 : 12; + }; + struct { + u32 eventq_wr_idx : 11; + u32 reserved5 : 21; + }; + struct { + u32 eventq_rd_idx : 11; + u32 reserved6 : 20; + u32 eventq_ovfl_err_resp : 1; + }; + }; +}; + +struct ub_decoder_queue { + phys_addr_t base; + void __iomem *qbase; + u32 qs; + struct queue_idx prod; + struct queue_idx cons; +}; + struct reg_default_val { u32 cmdq_cfg_val; u32 evtq_cfg_val; @@ -31,8 +74,11 @@ struct ub_decoder { phys_addr_t mmio_base_addr; u32 mmio_size_sup; u64 rg_size; + struct ub_decoder_queue cmdq; + struct ub_decoder_queue evtq; struct page_table pgtlb; struct reg_default_val vals; + void __iomem *notify; int irq_num; struct page_table_desc invalid_desc; @@ -71,5 +117,6 @@ struct ub_decoder { void ub_decoder_init(struct ub_entity *uent); void ub_decoder_uninit(struct ub_entity *uent); - +int ub_decoder_cmd_request(struct ub_decoder *decoder, phys_addr_t addr, + u64 size, enum ub_cmd_op_type op); #endif /* __DECODER_H__ */ diff --git a/drivers/ub/ubus/omm.c b/drivers/ub/ubus/omm.c index 7cc304950209..b8b59a9da4f1 100644 --- a/drivers/ub/ubus/omm.c +++ b/drivers/ub/ubus/omm.c @@ -616,8 +616,9 @@ int ub_decoder_unmap(struct ub_decoder *decoder, phys_addr_t addr, u64 size) if (size < SZ_1M) size = SZ_1M; ret = handle_table(decoder, &info, false); - - return ret; + if (ret) + return ret; + return ub_decoder_cmd_request(decoder, addr, size, TLBI_PARTIAL); } int ub_decoder_map(struct ub_decoder *decoder, struct decoder_map_info *info) diff --git a/drivers/ub/ubus/ubus_controller.h b/drivers/ub/ubus/ubus_controller.h index 0e956a3c431e..8637da594ada 100644 --- a/drivers/ub/ubus/ubus_controller.h +++ b/drivers/ub/ubus/ubus_controller.h @@ -11,6 +11,8 @@ struct ub_bus_controller_ops { int (*eu_table_init)(struct ub_bus_controller *ubc); void (*eu_table_uninit)(struct ub_bus_controller *ubc); int (*eu_cfg)(struct ub_bus_controller *ubc, bool flag, u32 eid, u16 upi); + void (*register_decoder_base_addr)(struct ub_bus_controller *ubc, + u64 *cmd_queue, u64 *event_queue); int (*entity_enable)(struct ub_entity *uent, u8 enable); }; -- Gitee From 8b4d57ec18ffa158345cfbc9783265849b4baacf Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Tue, 26 Aug 2025 20:38:22 +0800 Subject: [PATCH 045/214] ub:ubus: Supports decoder event processing ANBZ: #45460 commit abc591c50df53d92014a5bcdd91b78d9ab248f74 openEuler. Support processing event notifications sent by the UB decoder device. Signed-off-by: Yuhao Xiang Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/decoder.c | 225 +++++++++++++++++++++++++++++++++++++- drivers/ub/ubus/decoder.h | 2 + 2 files changed, 225 insertions(+), 2 deletions(-) diff --git a/drivers/ub/ubus/decoder.c b/drivers/ub/ubus/decoder.c index 0853bc4415e5..f5a8e69fd1cf 100644 --- a/drivers/ub/ubus/decoder.c +++ b/drivers/ub/ubus/decoder.c @@ -6,6 +6,8 @@ #define pr_fmt(fmt) "ubus decoder: " fmt #include +#include +#include #include #include @@ -16,14 +18,21 @@ #define MMIO_SIZE_MASK GENMASK_ULL(18, 16) #define CMDQ_SIZE_MASK GENMASK_ULL(15, 12) +#define EVTQ_SIZE_MASK GENMASK_ULL(7, 4) #define MMIO_SIZE_OFFSET 16 #define CMDQ_SIZE_OFFSET 12 +#define EVTQ_SIZE_OFFSET 4 #define CMDQ_SIZE_USE_MASK GENMASK(11, 8) #define CMDQ_SIZE_USE_OFFSET 8 #define CMDQ_ENABLE 0x1 #define CMD_ENTRY_SIZE 16 +#define EVTQ_SIZE_USE_MASK GENMASK(11, 8) +#define EVTQ_SIZE_USE_OFFSET 8 +#define EVTQ_ENABLE 0x1 +#define EVT_ENTRY_SIZE 16 + #define DECODER_PAGE_TABLE_ENTRY_SIZE 8 #define DECODER_QUEUE_TIMEOUT_US 1000000 /* 1s */ @@ -31,6 +40,7 @@ static void ub_decoder_uninit_queue(struct ub_decoder *decoder) { iounmap(decoder->cmdq.qbase); + iounmap(decoder->evtq.qbase); } static int ub_decoder_init_queue(struct ub_bus_controller *ubc, @@ -47,8 +57,8 @@ static int ub_decoder_init_queue(struct ub_bus_controller *ubc, return -EINVAL; } - if (decoder->cmdq.qs == 0) { - ub_err(uent, "decoder cmdq qs is 0\n"); + if (decoder->cmdq.qs == 0 || decoder->evtq.qs == 0) { + ub_err(uent, "decoder cmdq or evtq qs is 0\n"); return -EINVAL; } @@ -57,6 +67,12 @@ static int ub_decoder_init_queue(struct ub_bus_controller *ubc, if (!decoder->cmdq.qbase) return -ENOMEM; + decoder->evtq.qbase = ioremap(decoder->evtq.base, + (1 << decoder->evtq.qs) * EVT_ENTRY_SIZE); + if (!decoder->evtq.qbase) { + iounmap(decoder->cmdq.qbase); + return -ENOMEM; + } return 0; } @@ -108,6 +124,24 @@ static u32 set_queue_reg(struct ub_decoder *decoder) val |= CMDQ_ENABLE; ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_CMDQ_CFG, val); + /* init decoder eventq base addr and pi ci */ + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_EVENTQ_BASE_ADDR0, + lower_32_bits(decoder->evtq.base)); + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_EVENTQ_BASE_ADDR1, + upper_32_bits(decoder->evtq.base)); + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_EVENTQ_PROD, 0); + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_EVENTQ_CONS, 0); + + /* set decoder eventq conf */ + ret |= (u32)ub_cfg_read_dword(decoder->uent, DECODER_EVENTQ_CFG, &val); + decoder->vals.evtq_cfg_val = val; + val &= ~EVTQ_SIZE_USE_MASK; + val |= decoder->evtq.qs << EVTQ_SIZE_USE_OFFSET; + val |= EVTQ_ENABLE; + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_EVENTQ_CFG, val); + + decoder->cmdq.prod.val = 0; + decoder->evtq.cons.val = 0; if (ret) ub_err(decoder->uent, "set decoder queue failed\n"); @@ -236,7 +270,11 @@ static int ub_get_decoder_cap(struct ub_decoder *decoder) decoder->mmio_size_sup = (val & MMIO_SIZE_MASK) >> MMIO_SIZE_OFFSET; decoder->cmdq.qs = (val & CMDQ_SIZE_MASK) >> CMDQ_SIZE_OFFSET; + decoder->evtq.qs = (val & EVTQ_SIZE_MASK) >> EVTQ_SIZE_OFFSET; + ub_dbg(uent, "cmdq_queue_size=%u, evtq_queue_size=%u, mmio_size=%s\n", + decoder->cmdq.qs, decoder->evtq.qs, + mmio_size_desc[decoder->mmio_size_sup]); return 0; } @@ -309,10 +347,14 @@ static void unset_queue_reg(struct ub_decoder *decoder) ret = (u32)ub_cfg_write_dword(uent, DECODER_CMDQ_CFG, decoder->vals.cmdq_cfg_val); + ret |= (u32)ub_cfg_write_dword(uent, DECODER_EVENTQ_CFG, + decoder->vals.evtq_cfg_val); ret |= (u32)ub_cfg_write_dword(uent, DECODER_CMDQ_BASE_ADDR0, 0); ret |= (u32)ub_cfg_write_dword(uent, DECODER_CMDQ_BASE_ADDR1, 0); + ret |= (u32)ub_cfg_write_dword(uent, DECODER_EVENTQ_BASE_ADDR0, 0); + ret |= (u32)ub_cfg_write_dword(uent, DECODER_EVENTQ_BASE_ADDR1, 0); if (ret) ub_err(uent, "unset queue reg failed\n"); } @@ -594,6 +636,185 @@ int ub_decoder_cmd_request(struct ub_decoder *decoder, phys_addr_t addr, return ret; } +static bool queue_empty(struct ub_decoder_queue *q) +{ + return (Q_IDX(q->qs, q->prod.eventq_wr_idx) == + Q_IDX(q->qs, q->cons.eventq_rd_idx)) && + (Q_WRP(q->qs, q->prod.eventq_wr_idx) == + Q_WRP(q->qs, q->cons.eventq_rd_idx)); +} + +static void queue_inc_cons(struct ub_decoder_queue *q) +{ + u32 cons = (Q_WRP(q->qs, q->cons.eventq_rd_idx) | + Q_IDX(q->qs, q->cons.eventq_rd_idx)) + 1; + q->cons.eventq_rd_idx = Q_WRP(q->qs, cons) | Q_IDX(q->qs, cons); +} + +enum event_op_type { + RESERVED = 0x00, + EVENT_ADDR_OUT_OF_RANGE = 0x01, + EVENT_ILLEGAL_CMD = 0x02, +}; + +#define EVTQ_0_ID GENMASK_ULL(7, 0) +#define EVTQ_0_ADDR GENMASK_ULL(59, 32) +#define EVTQ_0_CMD_OPCODE GENMASK_ULL(39, 32) +#define EVTQ_ENT_DWORDS 2 +#define MAX_REASON_NUM 3 + +static const char * const cmd_err_reason[MAX_REASON_NUM] = { + "no error", + "illegal command", + "abort error(read command with 2bit ecc)" +}; + +static void fix_err_cmd(struct ub_decoder *decoder) +{ + struct ub_decoder_queue *cmdq = &(decoder->cmdq); + struct ub_entity *uent = decoder->uent; + u64 cmd[CMDQ_ENT_DWORDS] = {}; + struct queue_idx prod, cons; + void *pi; + int i; + + if (ub_cfg_read_dword(uent, DECODER_CMDQ_CONS, &cons.val)) { + ub_err(uent, "decoder fix error cmd, read ci failed\n"); + return; + } + if (ub_cfg_read_dword(uent, DECODER_CMDQ_PROD, &prod.val)) { + ub_err(uent, "decoder fix error cmd, read pi failed\n"); + return; + } + + cmd[0] |= FIELD_PREP(CMD_0_OP, TLBI_ALL); + pi = cmdq->qbase + Q_IDX(cmdq->qs, cons.cmdq_rd_idx) * + sizeof(struct tlbi_partial_entry); + + for (i = 0; i < CMDQ_ENT_DWORDS; i++) + writeq(cmd[i], pi + i * sizeof(u64)); + + if (cons.cmdq_err_reason >= MAX_REASON_NUM) + ub_err(uent, "cmdq err reason is invalid, reason=%u\n", + cons.cmdq_err_reason); + else + ub_err(uent, "cmdq err reason is %s\n", cmd_err_reason[cons.cmdq_err_reason]); + + prod.cmdq_err_resp = cons.cmdq_err; + + if (ub_cfg_write_dword(uent, DECODER_CMDQ_PROD, prod.val)) + ub_err(uent, "decoder fix error cmd, write pi err resp failed\n"); +} + +static void handle_evt(struct ub_decoder *decoder, u64 *evt) +{ + struct ub_entity *uent = decoder->uent; + + switch (FIELD_GET(EVTQ_0_ID, evt[0])) { + case EVENT_ADDR_OUT_OF_RANGE: + ub_err(uent, "decoder event, input addr out of range, addr=%#.7x00000\n", + (u32)FIELD_GET(EVTQ_0_ADDR, evt[0])); + break; + case EVENT_ILLEGAL_CMD: + ub_err(uent, "decoder event, illegal cmd, cmd_opcode=%#x\n", + (u32)FIELD_GET(EVTQ_0_CMD_OPCODE, evt[0])); + fix_err_cmd(decoder); + break; + default: + ub_err(uent, "invalid event opcode, opcode=%#x\n", + (u32)FIELD_GET(EVTQ_0_ID, evt[0])); + } +} + +static void decoder_event_deal(struct ub_decoder *decoder) +{ + struct ub_decoder_queue *evtq = &decoder->evtq; + struct ub_entity *uent = decoder->uent; + u64 evt[EVTQ_ENT_DWORDS]; + void *ci; + int i; + + if (ub_cfg_read_dword(uent, DECODER_EVENTQ_PROD, &(evtq->prod.val))) { + ub_err(uent, "decoder handle event, read eventq pi failed\n"); + return; + } + + while (!queue_empty(evtq)) { + ci = evtq->qbase + Q_IDX(evtq->qs, evtq->cons.eventq_rd_idx) * + EVT_ENTRY_SIZE; + + for (i = 0; i < EVTQ_ENT_DWORDS; i++) + evt[i] = readq(ci + i * sizeof(u64)); + + handle_evt(decoder, evt); + queue_inc_cons(evtq); + + if (ub_cfg_write_dword(uent, DECODER_EVENTQ_CONS, + evtq->cons.val)) + ub_err(uent, "decoder handle event, write eventq ci failed\n"); + } +} + +static irqreturn_t decoder_event_deal_handle(int irq, void *data) +{ + struct ub_entity *uent = (struct ub_entity *)data; + struct ub_decoder *decoder = uent->ubc->decoder; + + if (!decoder) { + ub_err(uent, "decoder does not exist\n"); + return IRQ_HANDLED; + } + + decoder_event_deal(decoder); + return IRQ_HANDLED; +} + +void ub_init_decoder_usi(struct ub_entity *uent) +{ + int irq_num, ret; + u32 usi_idx; + + if (!uent->ubc->decoder) { + ub_err(uent, "decoder not exist, can't init usi\n"); + return; + } + + ret = ub_cfg_read_dword(uent, DECODER_USI_IDX, &usi_idx); + if (ret) { + ub_err(uent, "get decoder usi idx failed\n"); + return; + } + + irq_num = ub_irq_vector(uent, usi_idx); + if (irq_num < 0) { + ub_err(uent, "ub get irq vector failed, ret=%d\n", irq_num); + return; + } + + ret = request_irq((unsigned int)irq_num, decoder_event_deal_handle, + IRQF_SHARED, "decoder_event_handle", (void *)uent); + if (ret) + ub_err(uent, "decoder request_irq failed, ret=%d\n", ret); + else + uent->ubc->decoder->irq_num = irq_num; +} + +void ub_uninit_decoder_usi(struct ub_entity *uent) +{ + int irq_num; + + if (!uent->ubc->decoder) { + ub_err(uent, "decoder not exist, can't uninit usi\n"); + return; + } + + irq_num = uent->ubc->decoder->irq_num; + if (irq_num < 0) + return; + + free_irq((unsigned int)irq_num, (void *)uent); +} + void ub_decoder_init(struct ub_entity *uent) { int ret; diff --git a/drivers/ub/ubus/decoder.h b/drivers/ub/ubus/decoder.h index c2d623244bcc..47710ead71db 100644 --- a/drivers/ub/ubus/decoder.h +++ b/drivers/ub/ubus/decoder.h @@ -117,6 +117,8 @@ struct ub_decoder { void ub_decoder_init(struct ub_entity *uent); void ub_decoder_uninit(struct ub_entity *uent); +void ub_init_decoder_usi(struct ub_entity *uent); +void ub_uninit_decoder_usi(struct ub_entity *uent); int ub_decoder_cmd_request(struct ub_decoder *decoder, phys_addr_t addr, u64 size, enum ub_cmd_op_type op); #endif /* __DECODER_H__ */ -- Gitee From dc1dce3d0e15a6ba1acd5c0ffa3c41e68c3e0c81 Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Sat, 20 Sep 2025 15:58:32 +0800 Subject: [PATCH 046/214] ub:ubus: Support route modify/delete function ANBZ: #45460 commit 58d738ed498bae8866a43277d9350dc457c79751 openEuler. Support update routing table when linked port has been changed. When a device is removed, clear all route that pass through the port which the device linked to. Signed-off-by: Yahui Liu Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/route.c | 400 ++++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/route.h | 7 + 2 files changed, 407 insertions(+) diff --git a/drivers/ub/ubus/route.c b/drivers/ub/ubus/route.c index f4be537021d5..fffea1c491f1 100644 --- a/drivers/ub/ubus/route.c +++ b/drivers/ub/ubus/route.c @@ -5,12 +5,18 @@ #define pr_fmt(fmt) "ubus route: " fmt +#include + #include "ubus.h" +#include "msg.h" +#include "enum.h" #include "port.h" +#include "ubus_driver.h" #define UB_ROUTE_TABLE_ENTRY_START (UB_ROUTE_TABLE_SLICE_START + (0x10 << 2)) #define EBW(port_nums) ((((port_nums) - 1) >> 5) + 1) /* Entry Bit Width */ #define UB_ROUTE_TABLE_ENTRY_BITS SZ_128 +#define UB_ROUTE_KFIFO_DEPTH SZ_16 /* node to update routing table */ struct cna_node { @@ -48,6 +54,12 @@ static void ub_cna_node_release(struct kref *kref) kfree(cna); } +static struct cna_node *ub_cna_node_get(struct cna_node *cna) +{ + kref_get(&cna->ref); + return cna; +} + static void ub_cna_node_put(struct cna_node *cna) { kref_put(&cna->ref, ub_cna_node_release); @@ -83,6 +95,23 @@ static int ub_add_cna_node(u32 cna, short distance, struct list_head *cna_list) return 0; } +static struct cna_node *ub_find_cna_node(u32 cna, struct list_head *cna_list) +{ + struct cna_node *cna_node; + + list_for_each_entry(cna_node, cna_list, node) { + if (cna_node->cna < cna) + continue; + + if (cna_node->cna > cna) + break; + + return ub_cna_node_get(cna_node); + } + + return NULL; +} + static void ub_clear_cna_list(struct list_head *cna_list) { struct cna_node *cna, *tmp; @@ -93,6 +122,27 @@ static void ub_clear_cna_list(struct list_head *cna_list) } } +/* the cna_list had better be empty since it'll be clear when err comes */ +static int ub_entity_copy_cna_list(struct ub_entity *uent, struct list_head *cna_list, + bool update_only) +{ + struct cna_node *cna, *new_cna; + + list_for_each_entry(cna, &uent->cna_list, node) { + if (update_only && !cna->need_update) + continue; + + new_cna = ub_create_cna_node(cna->cna, cna->distance); + if (!new_cna) { + ub_clear_cna_list(cna_list); + return -ENOMEM; + } + list_add_tail(&new_cna->node, cna_list); + } + + return 0; +} + void ub_route_clear(struct ub_entity *uent) { struct ub_port *port; @@ -118,6 +168,332 @@ int ub_route_add_entry(struct ub_port *port, u32 cna, short distance) return ub_add_cna_node(cna, distance, &port->uent->cna_list); } +static void ub_route_del_entry(struct ub_port *port, u32 cna) +{ + struct cna_node *node; + + clear_bit(cna, port->cna_maps); + node = ub_find_cna_node(cna, &port->uent->cna_list); + if (!node) { + ub_err(port->uent, "can not find cna %#x from uent cna list\n", + cna); + return; + } + + node->need_update = true; + node->count--; + ub_cna_node_put(node); +} + +static bool ub_route_del_entries(struct ub_port *port, + struct list_head *cna_list) +{ + struct ub_entity *uent = port->uent; + bool route_updated = false; + struct cna_node *cna; + + list_for_each_entry(cna, cna_list, node) { + if (!test_bit(cna->cna, port->cna_maps)) + continue; + + ub_route_del_entry(port, cna->cna); + route_updated = true; + } + + if (route_updated) + ub_entity_assign_priv_flag(uent, UB_ENTITY_ROUTE_UPDATED, true); + + return route_updated; +} + +static bool ub_entity_has_cna(u32 cna, struct ub_entity *uent) +{ + struct ub_port *port; + + if (uent->cna == cna) + return true; + + for_each_uent_port(port, uent) + if (port->cna == cna) + return true; + + return false; +} + +/* try update routing table from cna_list, return true if updated */ +static bool ub_route_mod_entries(struct ub_port *port, + struct list_head *cna_list, short off) +{ + struct ub_entity *uent = port->uent; + struct cna_node *cna, *uent_cna; + bool route_updated = false; + short new_distance; + int ret; + + list_for_each_entry(cna, cna_list, node) { + if (test_bit(cna->cna, port->cna_maps)) + continue; + + if (ub_entity_has_cna(cna->cna, uent)) + continue; + + new_distance = cna->distance + off; + + uent_cna = ub_find_cna_node(cna->cna, &uent->cna_list); + if (!uent_cna) { + ret = ub_route_add_entry(port, cna->cna, new_distance); + if (!ret) + goto set_flag; + + ub_err(uent, "add route entry for %#x failed with %d\n", + cna->cna, ret); + return false; + } + + if (uent_cna->distance < new_distance) { + ub_cna_node_put(uent_cna); + continue; + } + + /* in bfs we will never encounter a shorter path */ + set_bit(cna->cna, port->cna_maps); + uent_cna->need_update = true; + uent_cna->count++; + ub_cna_node_put(uent_cna); +set_flag: + route_updated = true; + } + + if (route_updated) + ub_entity_assign_priv_flag(uent, UB_ENTITY_ROUTE_UPDATED, true); + + return route_updated; +} + +bool ub_entity_support_forward(struct ub_entity *uent) +{ + if (!uent) + return false; + + if (is_ibus_controller(uent)) + return true; + + if (is_switch(uent)) + return true; + + return false; +} + +static bool ub_check_path(struct ub_port *from, struct ub_port *to) +{ + if (to == from) + return false; + + if (!to->r_uent) + return false; + + if (to->r_uent->port_nums <= 1) + return false; + + return true; +} + +/** + * consider a given topo like + * +-------------+ +---------+ + * | controller0 |p0:---:p1| device0 | + * +-------------+ +---------+ + * when ub_route_mod_neighbor is called for p0, p1, device0's routing table + * will be updated with p0'cna and controller0's cna. If controller0 supports + * forward, device0 will also know all route in controller0's routing table + */ +int ub_route_mod_neighbor(struct ub_port *port, struct ub_port *r_port) +{ + struct list_head cna_list; + int ret; + + if (!port || !r_port) + return -EINVAL; + + INIT_LIST_HEAD(&cna_list); + + /* if uent can not forward, only need to update uent & port's cna */ + if (ub_entity_support_forward(port->uent)) { + ret = ub_entity_copy_cna_list(port->uent, &cna_list, false); + if (ret) + return ret; + } + + ret = ub_add_cna_node(port->uent->cna, 0, &cna_list); + if (ret) + goto clear_cna; + + ret = ub_add_cna_node(port->cna, 0, &cna_list); + if (ret) + goto clear_cna; + + ub_route_mod_entries(r_port, &cna_list, 1); + +clear_cna: + ub_clear_cna_list(&cna_list); + return ret; +} + +/** + * consider a given topo like + * +-------------+ +---------+ + * | controller0 |p0:---:p1| device0 | + * +-------------+ +---------+ + * when device0 is being removed, ub_route_clear_port is called for p0 + * 1. all route that pass though this p0 will be del from controller0's cna_list + * 2. p0's cna_maps will be clear + */ +void ub_route_clear_port(struct ub_port *port) +{ + struct list_head *cna_list; + struct cna_node *cna_node; + bool route_update = false; + struct ub_entity *uent; + u32 cna; /* use u32 to avoid wraparound */ + + if (!port) + return; + + uent = port->uent; + cna_list = &uent->cna_list; + cna_node = list_first_entry(cna_list, struct cna_node, node); + cna = find_next_bit(port->cna_maps, UB_MAX_CNA_NUM, 0); + + while (cna < UB_MAX_CNA_NUM) { + if (list_entry_is_head(cna_node, cna_list, node)) + goto clear_bit; + + while (cna_node->cna < cna) { + cna_node = list_next_entry(cna_node, node); + if (list_entry_is_head(cna_node, cna_list, node)) { + ub_warn(uent, "can't find cna %u in list\n", cna); + goto clear_bit; + } + } + + if (cna_node->cna == cna) { + route_update = true; + cna_node->count--; + cna_node->need_update = true; + } +clear_bit: + clear_bit(cna, port->cna_maps); + cna = find_next_bit(port->cna_maps, UB_MAX_CNA_NUM, cna + 1); + } + + if (route_update) + ub_entity_assign_priv_flag(uent, UB_ENTITY_ROUTE_UPDATED, true); +} + +struct bfs_node { + struct ub_port *from; + short off; +}; + +/** + * consider a given topo like + * +-------------+ +---------+ +--------+ + * | controller0 |p0:---:p0| switch0 |p1:---slot0---:p0| device0| + * +-------------+ +---------+ +--------+ + * after device0 is plugged in, switch0 updates route with p0 and device0's cna, + * ub_route_mod_bfs tries to use the updated route in switch0's routing + * table to update controller0 through p0. If success, then pass through + * controller0's other ports to update neighbor + */ +void ub_route_mod_bfs(struct ub_entity *uent) +{ + DECLARE_KFIFO(kfifo, struct bfs_node, UB_ROUTE_KFIFO_DEPTH); + struct bfs_node curr, next; + struct list_head cna_list; + struct ub_port *from, *to; + + if (!uent) + return; + + INIT_KFIFO(kfifo); + INIT_LIST_HEAD(&cna_list); + + if (ub_entity_copy_cna_list(uent, &cna_list, true)) + goto clear_cna; + + for_each_uent_port(to, uent) { + if (to->r_uent && to->r_uent->port_nums > 1) { + next.from = to->r_uent->ports + to->r_index; + next.off = 1; + kfifo_put(&kfifo, next); + } + } + + while (kfifo_get(&kfifo, &curr)) { + from = curr.from; + if (!ub_route_mod_entries(from, &cna_list, curr.off)) + continue; + + if (!ub_entity_support_forward(from->uent)) + continue; + + for_each_uent_port(to, from->uent) { + if (!ub_check_path(from, to)) + continue; + + next.from = to->r_uent->ports + to->r_index; + next.off = curr.off + 1; + if (!kfifo_put(&kfifo, next)) + ub_err(next.from->uent, + "%s kfifo put failed!\n", __func__); + } + } + +clear_cna: + ub_clear_cna_list(&cna_list); +} + +/* same as ub_route_mod_bfs, but used to del route instead of update route */ +void ub_route_del_bfs(struct ub_entity *uent) +{ + DECLARE_KFIFO(kfifo, struct ub_port *, UB_ROUTE_KFIFO_DEPTH); + struct ub_port *from, *to; + struct list_head cna_list; + + if (!uent) + return; + + INIT_KFIFO(kfifo); + INIT_LIST_HEAD(&cna_list); + + if (ub_entity_copy_cna_list(uent, &cna_list, true)) + goto clear_cna; + + for_each_uent_port(to, uent) + if (to->r_uent && to->r_uent->port_nums > 1) + kfifo_put(&kfifo, to->r_uent->ports + to->r_index); + + while (kfifo_get(&kfifo, &from)) { + if (!ub_route_del_entries(from, &cna_list)) + continue; + + if (!ub_entity_support_forward(from->uent)) + continue; + + for_each_uent_port(to, from->uent) { + if (!ub_check_path(from, to)) + continue; + + if (!kfifo_put(&kfifo, to->r_uent->ports + to->r_index)) + ub_err(to->r_uent, + "%s kfifo put failed!\n", __func__); + } + } + +clear_cna: + ub_clear_cna_list(&cna_list); +} + static void ub_set_route_table_entry(struct ub_entity *uent, u32 dst_cna, u32 *route_table_entry) { @@ -175,3 +551,27 @@ void ub_route_sync_dev(struct ub_entity *uent) ub_entity_assign_priv_flag(uent, UB_ENTITY_ROUTE_UPDATED, false); } + +/** + * this function sync all updated routing table entry to config space + * for devices that aren't in ubc dev list, this function will not update them + */ +void ub_route_sync_all(void) +{ + struct ub_bus_controller *ubc; + struct ub_entity *uent; + + down_read(&ub_bus_sem); + list_for_each_entry(ubc, &ubc_list, node) + list_for_each_entry(uent, &ubc->devs, node) + ub_route_sync_dev(uent); + up_read(&ub_bus_sem); +} + +int ub_route_entities(struct list_head *dev_list) +{ + if (!dev_list) + return -EINVAL; + + return ub_enum_bfs_route_cal(dev_list); +} diff --git a/drivers/ub/ubus/route.h b/drivers/ub/ubus/route.h index 04bd052729a0..1785b20427d7 100644 --- a/drivers/ub/ubus/route.h +++ b/drivers/ub/ubus/route.h @@ -10,6 +10,13 @@ struct ub_entity; struct ub_port; void ub_route_clear(struct ub_entity *uent); int ub_route_add_entry(struct ub_port *port, u32 cna, short distance); +bool ub_entity_support_forward(struct ub_entity *uent); +int ub_route_mod_neighbor(struct ub_port *port, struct ub_port *r_port); +void ub_route_clear_port(struct ub_port *port); +void ub_route_mod_bfs(struct ub_entity *uent); +void ub_route_del_bfs(struct ub_entity *uent); void ub_route_sync_dev(struct ub_entity *uent); +void ub_route_sync_all(void); +int ub_route_entities(struct list_head *dev_list); #endif /* __ROUTE_H__ */ -- Gitee From d7cb99726a451d8a71dfba7d24b0fd2a1a4a4822 Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Mon, 22 Sep 2025 10:38:41 +0800 Subject: [PATCH 047/214] ub:ubus: Support enumerate port and route set/clear for port ANBZ: #45460 commit 8abf17ce1d9f0ece70fc1ccdc8689ced0f0aa6ac openEuler. Support enumerate ports and set/clear route table for the new/deleted port. Signed-off-by: Yahui Liu Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/enum.c | 82 +++++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/enum.h | 5 +++ drivers/ub/ubus/route.c | 39 ++++++++++++++++++++ drivers/ub/ubus/route.h | 3 ++ 4 files changed, 129 insertions(+) diff --git a/drivers/ub/ubus/enum.c b/drivers/ub/ubus/enum.c index 872bb0de1582..48c37e30ca31 100644 --- a/drivers/ub/ubus/enum.c +++ b/drivers/ub/ubus/enum.c @@ -1080,6 +1080,57 @@ static void ub_enum_topo_ent_uninit(struct ub_entity *uent) } } +static int ub_enum_ports(struct ub_entity *uent, u16 start_port, u16 num, + void *buf) +{ + u16 left = num, start = start_port, count; + struct enum_pld_scan_pdu_common *pc; + struct enum_tlv_info info = {}; + u8 total_slice, slice_id = 0; + int tlv_len, ret, nums = 0; + void *rsp, *tlv; + + do { + rsp = (void *)ub_enum_topo_query_and_check(uent, buf, slice_id); + if (!rsp) + return -EBUSY; + + pc = rsp; + tlv_len = (int)pc->pdu_len * SZ_4 - ENUM_PLD_SCAN_PDU_COMMON_SIZE; + tlv = rsp + ENUM_PLD_SCAN_PDU_COMMON_SIZE; + + ret = ub_enum_tlv_parse(tlv, tlv_len, &info); + if (ret) + return ret; + + if (slice_id == 0) { + if (!info.si || !info.pi || !info.pn || !info.ci) { + dev_err(&uent->ubc->dev, "port tlv si/pi/pn/ci null\n"); + return -EINVAL; + } + + total_slice = info.si->total_slice; + } + + if (start >= nums + info.port_nums) + goto next; + + info.pi += start - nums; + count = min(left, nums + info.port_nums - start); + ub_enum_parse_port(uent, info.pi, count); + start += count; + left -= count; + + if (left == 0) + break; +next: + nums += info.port_nums; + slice_id++; + } while (slice_id < total_slice); + + return 0; +} + static int ub_enum_and_configure_ent(struct ub_entity *uent, void *buf) { int ret; @@ -1281,6 +1332,37 @@ static int ub_enum_do_topo_scan(struct ub_entity *root, struct list_head *dev_li return ret; } +int ub_enum_topo_scan_ports(struct ub_entity *uent, u16 start_port, u16 num, + struct list_head *dev_list, void *buf) +{ + int ret; + + if (!uent || !dev_list || !buf) + return -EINVAL; + + ret = ub_enum_ports(uent, start_port, num, buf); + if (ret) + return ret; + + return ub_enum_do_topo_scan(uent, dev_list, buf); +} + +struct ub_entity *ub_enum_get_port_r_uent(struct ub_port *port, void *buf) +{ + struct ub_bus_controller *ubc; + int ret; + + if (!port || !buf) + return NULL; + + ret = ub_enum_ports(port->uent, port->index, 1, buf); + if (ret) + return NULL; + + ubc = port->uent->ubc; + return ub_enum_get_ent(&port->r_guid, &ubc->devs); +} + /* * During topo scan, just alloc ub_entity, alloc ub_port, alloc cna, * so, when topo scan failed, just go to free devs in topo_scan.uents diff --git a/drivers/ub/ubus/enum.h b/drivers/ub/ubus/enum.h index 2ab04e0f705f..5c20eb68603a 100644 --- a/drivers/ub/ubus/enum.h +++ b/drivers/ub/ubus/enum.h @@ -181,6 +181,11 @@ struct enum_tlv_cap_info { }; #define ENUM_TLV_CAP_INFO_SZ 8 +void ub_enum_clear_ent_list(struct list_head *dev_list); +int ub_enum_entities_active(struct list_head *dev_list); +int ub_enum_topo_scan_ports(struct ub_entity *uent, u16 start_port, u16 num, + struct list_head *dev_list, void *buf); +struct ub_entity *ub_enum_get_port_r_uent(struct ub_port *port, void *buf); size_t calc_enum_pld_header_size(struct enum_pld_scan_header *header, bool req); int ub_cfg_read_guid(struct ub_entity *uent); int ub_enum_bfs_route_cal(struct list_head *uent_list); diff --git a/drivers/ub/ubus/route.c b/drivers/ub/ubus/route.c index fffea1c491f1..73e0df5436dc 100644 --- a/drivers/ub/ubus/route.c +++ b/drivers/ub/ubus/route.c @@ -575,3 +575,42 @@ int ub_route_entities(struct list_head *dev_list) return ub_enum_bfs_route_cal(dev_list); } + +void ub_route_table_clear_for_port(struct ub_port *port, struct ub_port *r_port) +{ + ub_route_clear_port(r_port); + + ub_route_clear_port(port); + + if (ub_entity_support_forward(r_port->uent)) + ub_route_del_bfs(r_port->uent); + + if (ub_entity_support_forward(port->uent)) + ub_route_del_bfs(port->uent); + + ub_route_sync_all(); +} + +int ub_route_table_set_for_port(struct ub_port *port, + struct ub_port *r_port) +{ + int ret; + + ret = ub_route_mod_neighbor(port, r_port); + if (ret) + return ret; + + ret = ub_route_mod_neighbor(r_port, port); + if (ret) + return ret; + + if (ub_entity_support_forward(port->uent)) + ub_route_mod_bfs(port->uent); + + if (ub_entity_support_forward(r_port->uent)) + ub_route_mod_bfs(r_port->uent); + + ub_route_sync_all(); + + return 0; +} diff --git a/drivers/ub/ubus/route.h b/drivers/ub/ubus/route.h index 1785b20427d7..d7d96d86d4de 100644 --- a/drivers/ub/ubus/route.h +++ b/drivers/ub/ubus/route.h @@ -18,5 +18,8 @@ void ub_route_del_bfs(struct ub_entity *uent); void ub_route_sync_dev(struct ub_entity *uent); void ub_route_sync_all(void); int ub_route_entities(struct list_head *dev_list); +void ub_route_table_clear_for_port(struct ub_port *port, + struct ub_port *r_port); +int ub_route_table_set_for_port(struct ub_port *port, struct ub_port *r_port); #endif /* __ROUTE_H__ */ -- Gitee From 8b5fa1495ba4170b879e36206ca5c01055f66aa2 Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Mon, 22 Sep 2025 11:31:11 +0800 Subject: [PATCH 048/214] ub:ubus: Support handle device link up and link down ANBZ: #45460 commit 95aa040fe0e7b8661e199e27cd2c10d875e0e3d5 openEuler. Support handle device link up and link down. Signed-off-by: Yahui Liu Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/link.c | 351 +++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/link.h | 18 ++ drivers/ub/ubus/port.c | 2 + include/ub/ubus/ubus.h | 2 + 5 files changed, 374 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/link.c create mode 100644 drivers/ub/ubus/link.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 36ab665f4f84..ce5639666b46 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o ras.o obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o -ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o omm.o ioctl.o eu.o +ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o omm.o ioctl.o eu.o link.o ubus-y += services/ras.o diff --git a/drivers/ub/ubus/link.c b/drivers/ub/ubus/link.c new file mode 100644 index 000000000000..37f3e01b30bb --- /dev/null +++ b/drivers/ub/ubus/link.c @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus link: " fmt + +#include + +#include "ubus.h" +#include "msg.h" +#include "enum.h" +#include "port.h" +#include "route.h" +#include "ubus_entity.h" +#include "ubus_driver.h" +#include "link.h" + +static struct ub_entity *ublc_get_port_r_uent(struct ub_port *port) +{ + struct ub_entity *r_uent = NULL; + void *buf; + +#define UB_TOPO_BUF_SZ SZ_4K + buf = kzalloc(UB_TOPO_BUF_SZ, GFP_KERNEL); + if (!buf) + return NULL; + + r_uent = ub_enum_get_port_r_uent(port, buf); + if (!r_uent) { + port->r_index = 0; + guid_copy(&port->r_guid, &guid_null); + } + + kfree(buf); + return r_uent; +} + +static void ublc_stop_devices(struct list_head *dev_list) +{ + struct ub_entity *uent; + + list_for_each_entry_reverse(uent, dev_list, node) + ub_stop_ent(uent); +} + +static void ublc_remove_devices(struct list_head *dev_list) +{ + struct ub_entity *uent, *tmp; + + list_for_each_entry_safe_reverse(uent, tmp, dev_list, node) + ub_remove_ent(uent); +} + +static void ublc_update_route_link_down(struct ub_port *port) +{ + struct ub_entity *uent = port->uent; + + ub_route_clear_port(port); + + if (ub_entity_support_forward(uent)) + ub_route_del_bfs(uent); + + ub_route_sync_all(); +} + +/** + * ublc_enum_at_port() - enum at port to find new devices + * @port: the port that has new device plugged in or all port down remove + * @dev_list: a list to store the new found devices + * + * this func use bfs to enum devices and put them into dev_list, + * which means the previous device in dev_list is enumerated previous + */ +static int ublc_enum_at_port(struct ub_port *port, struct list_head *dev_list) +{ + void *buf; + int ret; + +#define UB_TOPO_BUF_SZ SZ_4K + buf = kzalloc(UB_TOPO_BUF_SZ, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = ub_enum_topo_scan_ports(port->uent, port->index, 1, dev_list, buf); + if (ret) { + port->r_index = 0; + guid_copy(&port->r_guid, &guid_null); + } + + kfree(buf); + return ret; +} + +static int ublc_update_route_link_up(struct ub_port *port) +{ + struct ub_entity *uent = port->uent, *r_uent = port->r_uent; + struct ub_port *r_port; + int ret; + + r_port = port->r_uent->ports + port->r_index; + ret = ub_route_mod_neighbor(port, r_port); + if (ret) + return ret; + + ret = ub_route_mod_neighbor(r_port, port); + if (ret) + return ret; + + /* for uent that can't forward, there's no need to update other uent */ + if (ub_entity_support_forward(uent)) + ub_route_mod_bfs(uent); + + if (ub_entity_support_forward(r_uent)) + ub_route_mod_bfs(r_uent); + + ub_route_sync_all(); + return 0; +} + +/** + * a simple example for a new device link up, which is hotplug or reset + * for a given topo like + * +-------------+ +---------+ +---------+ +---------+ + * | controller0 |p0:--:p0| switch0 |p1:--slot0--:p0| switch1 |p1:--:p0| device0 | + * +-------------+ +---------+ +---------+ +---------+ + * when port0 is calling handle link up + * 1. enum at port0 to create switch1 and device0, put them in dev_list + * 2. route dev_list to set up route between these two devices + * 3. handle route link up at port0, add route of left(controller0 & switch0) + * into right(switch1 & device0) and route of right into left + * 4. start switch1 and device0 + */ +static int ublc_handle_new_device_link_up(struct ub_port *port) +{ + struct list_head dev_list; + int ret; + + INIT_LIST_HEAD(&dev_list); + + ret = ublc_enum_at_port(port, &dev_list); + if (ret) { + ub_err(port->uent, "link up enum at port%u failed, ret=%d\n", + port->index, ret); + return ret; + } + + if (list_empty(&dev_list)) { + ub_warn(port->uent, "link up without remote dev\n"); + if (port->link_state == LINK_STATE_RESETING) + port->link_state = LINK_STATE_DONE; + return -ENXIO; + } + + ret = ub_route_entities(&dev_list); + if (ret) { + ub_err(port->uent, "link up cal route failed, ret=%d\n", ret); + goto err_route; + } + + ret = ublc_update_route_link_up(port); + if (ret) { + ub_err(port->uent, "link up update route failed, ret=%d\n", ret); + goto err_link_up; + } + + ret = ub_enum_entities_active(&dev_list); + if (ret) { + ub_err(port->uent, "link up start devices failed, ret=%d\n", ret); + goto err_link_up; + } + + if (port->link_state == LINK_STATE_RESETING) + port->link_state = LINK_STATE_DONE; + + return 0; + +err_link_up: + ublc_update_route_link_down(port); + port->r_uent = NULL; +err_route: + ub_enum_clear_ent_list(&dev_list); + return ret; +} + +/** + * ublc_mark_detached_devices() - mark devices as detached and put them into dev_list + * @root: a device that needs to be removed + * @dev_list: a list to store the detached devices + * + * this func use bfs to mark devices and put them into dev_list, all devices + * that connected with root will be marked as detached + */ +static void ublc_mark_detached_devices(struct ub_entity *root, + struct list_head *dev_list) +{ +#define UB_LC_KFIFO_DEPTH SZ_16 + DECLARE_KFIFO(kfifo, struct ub_entity *, UB_LC_KFIFO_DEPTH); + struct ub_entity *uent, *r_uent; + struct ub_port *port; + + INIT_KFIFO(kfifo); + ub_entity_assign_priv_flag(root, UB_ENTITY_DETACHED, true); + kfifo_put(&kfifo, root); + + down_write(&ub_bus_sem); + while (kfifo_get(&kfifo, &uent)) { + for_each_uent_port(port, uent) { + if (!port->r_uent) + continue; + + r_uent = port->r_uent; + if (ub_entity_test_priv_flag(r_uent, UB_ENTITY_DETACHED)) + continue; + + ub_entity_assign_priv_flag(r_uent, UB_ENTITY_DETACHED, true); + if (!kfifo_put(&kfifo, r_uent)) + ub_err(r_uent, "%s kfifo put failed!\n", __func__); + } + + list_del(&uent->node); + list_add_tail(&uent->node, dev_list); + } + up_write(&ub_bus_sem); +} + +/** + * a simple example for link down + * for a given topo like + * +-------------+ +---------+ +---------+ +---------+ + * | controller0 |p0:--:p0| switch0 |p1:--slot0--:p0| switch1 |p1:--:p0| device0 | + * +-------------+ +---------+ +---------+ +---------+ + * when slot0's final port is calling handle link down + * 1. disconnect p1 and p0 so that p1 and p0 is not connected in software + * 2. put switch1 and device0 into dev_list, mark them as detached + * 3. stop switch1 and device0 + * 4. remove switch1 and device0 + * 5. handle route link down at p1, del route of right(switch1 & device0) + * from left(controller0 & switch0) + */ +static void ublc_handle_all_link_down(struct ub_port *port, struct ub_entity *r_uent) +{ + struct list_head dev_list; + + INIT_LIST_HEAD(&dev_list); + ub_port_disconnect(port); + ublc_mark_detached_devices(r_uent, &dev_list); + ublc_stop_devices(&dev_list); + ublc_remove_devices(&dev_list); + ublc_update_route_link_down(port); +} + +static bool ublc_device_is_down(struct ub_port *port) +{ + struct ub_entity *r_uent = port->r_uent; + struct ub_entity *uent = port->uent; + struct ub_port *tmp; + + for_each_uent_port(tmp, uent) + if (tmp->index != port->index && tmp->r_uent == r_uent) + return false; + + return true; +} + +static void port_link_state_change(struct ub_port *port, struct ub_port *r_port) +{ + if (port->link_state == LINK_STATE_RESETING) + port->link_state = LINK_STATE_DONE; + + if (r_port->link_state == LINK_STATE_RESETING) + r_port->link_state = LINK_STATE_DONE; +} + +void ublc_link_up_handle(struct ub_port *port) +{ + struct ub_entity *uent = port->uent; + struct ub_port *r_port; + struct ub_entity *r_uent; + int ret; + + if (port->r_uent) { + ub_err(uent, "port%u is already up\n", port->index); + return; + } + + device_lock(&uent->dev); + + r_uent = ublc_get_port_r_uent(port); + if (!r_uent) { + ublc_handle_new_device_link_up(port); + goto out; + } + + if (!ub_check_and_connect(port, r_uent)) + goto out; + + r_port = port->r_uent->ports + port->r_index; + ret = ub_route_table_set_for_port(port, r_port); + if (ret) { + ub_err(uent, "port%u up set route table failed! ret=%d\n", + port->index, ret); + goto out; + } + + port_link_state_change(port, r_port); + ub_info(uent, "port%u link up\n", port->index); +out: + device_unlock(&uent->dev); +} + +void ublc_link_down_handle(struct ub_port *port) +{ + struct ub_entity *uent = port->uent; + struct ub_port *r_port; + + if (!port->r_uent) { + ub_err(uent, "port%u is already down\n", port->index); + return; + } + + device_lock(&uent->dev); + + if (ublc_device_is_down(port)) { + ub_info(uent, "port%u link down\n", port->index); + ublc_handle_all_link_down(port, port->r_uent); + ub_info(uent, "all port link down and remove device\n"); + device_unlock(&uent->dev); + + return; + } + + r_port = port->r_uent->ports + port->r_index; + + ub_route_table_clear_for_port(port, r_port); + ub_port_disconnect(port); + + device_unlock(&uent->dev); + + ub_info(uent, "port%u link down\n", port->index); +} + +void ub_link_change_handler(struct work_struct *work) +{ + struct ub_port *port = container_of(work, struct ub_port, link_work); + + if (port->link_event == UB_LINK_UP) + ublc_link_up_handle(port); + else + ublc_link_down_handle(port); +} diff --git a/drivers/ub/ubus/link.h b/drivers/ub/ubus/link.h new file mode 100644 index 000000000000..1a548f1e6f1f --- /dev/null +++ b/drivers/ub/ubus/link.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __LINK_H__ +#define __LINK_H__ + +enum ub_link_event { + UB_LINK_UP, + UB_LINK_DOWN +}; + +void ub_link_change_handler(struct work_struct *work); +void ublc_link_up_handle(struct ub_port *port); +void ublc_link_down_handle(struct ub_port *port); + +#endif /* __LINK_H__ */ diff --git a/drivers/ub/ubus/port.c b/drivers/ub/ubus/port.c index 74158f9b0d55..c3be48b180af 100644 --- a/drivers/ub/ubus/port.c +++ b/drivers/ub/ubus/port.c @@ -9,6 +9,7 @@ #include "ubus.h" #include "reset.h" +#include "link.h" #include "port.h" struct ub_port_attribute { @@ -536,6 +537,7 @@ static void ub_port_init(struct ub_entity *uent, struct ub_port *port) bitmap_zero(port->cna_maps, UB_MAX_CNA_NUM); bitmap_zero(port->cap_map, UB_PORT_CAP_NUM); kobject_init(&port->kobj, &ub_port_ktype); + INIT_WORK(&port->link_work, ub_link_change_handler); } int ub_ports_setup(struct ub_entity *uent) diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index b9d2623cd28b..ac6abf1a9819 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -144,7 +144,9 @@ struct ub_port { /* cap cache */ DECLARE_BITMAP(cap_map, UB_PORT_CAP_NUM); + struct work_struct link_work; enum ub_link_state link_state; + u8 link_event; }; struct ue_map { -- Gitee From de1fa73ce890666620ae9c9d6323d8e648f689cd Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Mon, 22 Sep 2025 10:45:20 +0800 Subject: [PATCH 049/214] ub:ubus: Support for bus instance framework ANBZ: #45460 commit b06c272d7af102f2768d1963ce49902787981630 openEuler. Implement the basic framework for a bus instance, while initializing and deinitializing a static server-type bus instance. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 1 + drivers/ub/ubus/instance.c | 320 ++++++++++++++++++++++++++++++++++ drivers/ub/ubus/instance.h | 24 +++ drivers/ub/ubus/ubus.h | 1 + drivers/ub/ubus/ubus_entity.c | 9 + include/ub/ubus/ubus.h | 22 +++ 6 files changed, 377 insertions(+) create mode 100644 drivers/ub/ubus/instance.c create mode 100644 drivers/ub/ubus/instance.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index ce5639666b46..7736843bd353 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o omm.o ioctl.o eu.o link.o +ubus-y += instance.o ubus-y += services/ras.o diff --git a/drivers/ub/ubus/instance.c b/drivers/ub/ubus/instance.c new file mode 100644 index 000000000000..942cf52f34c0 --- /dev/null +++ b/drivers/ub/ubus/instance.c @@ -0,0 +1,320 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus instance: " fmt + +#include +#include + +#include "ubus.h" +#include "eid.h" +#include "eu.h" +#include "ubus_driver.h" +#include "instance.h" + +static LIST_HEAD(ubi_list); +static DEFINE_MUTEX(ubi_list_mutex); +static u32 instance_count; + +static void ub_unregister_bus_instance(struct ub_bus_instance *bi); +static void ub_bus_instance_destroy(struct ub_bus_instance *bi) +{ + guid_t *tar = (guid_t *)&guid_null; + + ummu_core_del_eid(tar, bi->info.eid, EID_BYPASS); + ub_unregister_bus_instance(bi); +} + +static void ub_release_bus_instance(struct kref *kref) +{ + struct ub_bus_instance *bi = + container_of(kref, struct ub_bus_instance, kref); + + if (bi->registered) + ub_bus_instance_destroy(bi); + + kfree(bi); +} + +static struct ub_bus_instance *ub_bus_instance_get(struct ub_bus_instance *bi) +{ + if (bi) + kref_get(&bi->kref); + + return bi; +} + +void ub_bus_instance_put(struct ub_bus_instance *bi) +{ + if (bi) + kref_put(&bi->kref, ub_release_bus_instance); +} +EXPORT_SYMBOL_GPL(ub_bus_instance_put); + +static struct ub_bus_instance *ub_alloc_bus_instance(void) +{ + struct ub_bus_instance *bi; + + bi = kzalloc(sizeof(*bi), GFP_KERNEL); + if (!bi) + return NULL; + + INIT_LIST_HEAD(&bi->node); + kref_init(&bi->kref); + INIT_LIST_HEAD(&bi->uents); + mutex_init(&bi->lock); + + return bi; +} + +static bool guid_match(struct ub_bus_instance *bi, void *arg) +{ + guid_t *guid = (guid_t *)arg; + + return guid_equal(&bi->info.guid.id, guid); +} + +struct ub_bus_instance *ub_find_bus_instance(instance_match match, void *arg) +{ + struct ub_bus_instance *bi; + + if (!match || !arg) { + pr_err("instance match or arg is null\n"); + return NULL; + } + + mutex_lock(&ubi_list_mutex); + list_for_each_entry(bi, &ubi_list, node) + if (match(bi, arg)) + goto out; + + bi = NULL; +out: + ub_bus_instance_get(bi); + mutex_unlock(&ubi_list_mutex); + + return bi; +} +EXPORT_SYMBOL_GPL(ub_find_bus_instance); + +static int bi_duplicate_check(struct ub_bus_instance_info *n) +{ + struct ub_bus_instance_info *old; + struct ub_bus_instance *bi; + int ret; + + bi = ub_find_bus_instance(guid_match, &n->guid.id); + if (!bi) + return 0; + + old = &bi->info; + + if (n->type != old->type) { + pr_err("bus instance guid exist with different type\n"); + ub_bus_instance_put(bi); + return -EINVAL; + } + + if (n->eid) { + if (n->upi) + ret = (n->eid == old->eid && n->upi == old->upi) ? + -EEXIST : + -EINVAL; + else + ret = (n->eid == old->eid) ? -EEXIST : -EINVAL; + } else { + if (n->upi) + ret = (n->upi == old->upi) ? -EEXIST : -EINVAL; + else + ret = -EEXIST; + } + + if (ret == -EEXIST) + pr_warn("bus instance guid exist\n"); + else + pr_err("bus instance guid exist, but eid/upi invalid\n"); + + ub_bus_instance_put(bi); + + return ret; +} + +static int bi_valid_check(struct ub_bus_instance *bi) +{ + struct ub_bus_instance_info *info = &bi->info; + + if (info->guid.bits.type != UB_TYPE_BUS_INSTANCE || + guid_is_null((const guid_t *)&info->guid)) { + pr_err("guid type is not bus instance or guid_null\n"); + return -EINVAL; + } + + if (is_server(bi) && info->eid && info->eid <= ubc_eid_end) { + pr_err("server bi eid within the local scope, eid=%#x\n", + info->eid); + return -EINVAL; + } + + return 0; +} + +static int ub_cfg_bus_instance_eid(struct ub_bus_instance *bi, bool alloc) +{ + struct ub_bus_instance_info *info = &bi->info; + struct ub_guid guid = info->guid; + u32 eid; + int ret; + + if (alloc) { + if (info->eid) + return 0; + + ret = ub_eid_request(&guid.id, &eid); + if (ret) { + pr_err("bus instance eid cfg failed, ret=%d\n", ret); + return ret; + } + + info->eid = eid; + } else { + if (info->eid > ubc_eid_end) + return 0; + + ub_eid_release(info->eid); + info->eid = 0; + } + + return 0; +} + +static void ub_cfg_bus_instance_upi(struct ub_bus_instance *bi) +{ + struct ub_bus_instance_info *info = &bi->info; + + if (info->upi) + return; + + info->upi = UB_CP_UPI; +} + +static int ub_register_bus_instance(struct ub_bus_instance *bi) +{ + struct ub_bus_instance_info *info = &bi->info; + int ret; + + ret = bi_duplicate_check(info); + if (ret) + return ret; + + ret = bi_valid_check(bi); + if (ret) + return ret; + + ret = ub_cfg_bus_instance_eid(bi, true); + if (ret) + return ret; + + ub_cfg_bus_instance_upi(bi); + + ret = ub_cfg_eu_table(bi->major, true, bi->info.eid, + bi->info.upi); + if (ret) + goto out; + + mutex_lock(&ubi_list_mutex); + bi->registered = true; + list_add_tail(&bi->node, &ubi_list); + instance_count++; + mutex_unlock(&ubi_list_mutex); + return 0; + +out: + /* upi no need unconfigure */ + (void)ub_cfg_bus_instance_eid(bi, false); + return ret; +} + +static void ub_unregister_bus_instance(struct ub_bus_instance *bi) +{ + mutex_lock(&ubi_list_mutex); + if (!bi->registered) { + mutex_unlock(&ubi_list_mutex); + return; + } + + instance_count--; + list_del(&bi->node); + bi->registered = false; + mutex_unlock(&ubi_list_mutex); + + (void)ub_cfg_eu_table(bi->major, false, bi->info.eid, + bi->info.upi); + + /* upi no need unconfigure */ + (void)ub_cfg_bus_instance_eid(bi, false); +} + +int ub_static_bus_instance_init(struct ub_bus_controller *ubc) +{ + struct ub_bus_instance *bi; + struct ub_bus_controller *tmp; + int ret; + + if (ubc->ctl_no != 0) { + tmp = ub_find_bus_controller(0); + if (!tmp || !tmp->bi) + return -EINVAL; + + ubc->bi = ub_bus_instance_get(tmp->bi); + + return ub_cfg_eu_table(ubc, true, ubc->bi->info.eid, + ubc->bi->info.upi); + } + + bi = ub_alloc_bus_instance(); + if (!bi) + return -ENOMEM; + + bi->info.type = UBUS_INSTANCE_STATIC_SERVER; + guid_copy(&bi->info.guid.id, &ubc->uent->guid.id); + bi->info.guid.bits.type = UB_TYPE_BUS_INSTANCE; + bi->major = ubc; + + ret = ub_register_bus_instance(bi); + if (ret) { + dev_err(&ubc->dev, "static server register bi failed ret=%d\n", + ret); + goto put; + } + + ret = ummu_core_add_eid((guid_t *)&guid_null, bi->info.eid, + EID_BYPASS); + if (ret) { + dev_err(&ubc->dev, "static server ummu core add eid, ret=%d\n", + ret); + goto unregister; + } + + ubc->bi = ub_bus_instance_get(bi); + + ub_bus_instance_put(bi); /* put the init one */ + + return 0; +unregister: + ub_unregister_bus_instance(bi); +put: + ub_bus_instance_put(bi); + return ret; +} + +void ub_static_bus_instance_uninit(struct ub_bus_controller *ubc) +{ + if (ubc->ctl_no != 0) + (void)ub_cfg_eu_table(ubc, false, ubc->bi->info.eid, + ubc->bi->info.upi); + + ub_bus_instance_put(ubc->bi); + ubc->bi = NULL; +} diff --git a/drivers/ub/ubus/instance.h b/drivers/ub/ubus/instance.h new file mode 100644 index 000000000000..9a835afd7b99 --- /dev/null +++ b/drivers/ub/ubus/instance.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __INSTANCE_H__ +#define __INSTANCE_H__ + +#include + +typedef bool (*instance_match)(struct ub_bus_instance *bi, void *arg); + +#define UBUS_INSTANCE_STATIC_SERVER 0 + +#define is_static_server(bi) ((bi)->info.type == UBUS_INSTANCE_STATIC_SERVER) +#define is_static(bi) (is_static_server(bi)) +#define is_server(bi) (is_static_server(bi)) + +int ub_static_bus_instance_init(struct ub_bus_controller *ubc); +void ub_static_bus_instance_uninit(struct ub_bus_controller *ubc); +void ub_bus_instance_put(struct ub_bus_instance *bi); +struct ub_bus_instance *ub_find_bus_instance(instance_match match, void *arg); + +#endif /* __INSTANCE_H__ */ diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index e25235f4003f..1367d8e87d9a 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -58,6 +58,7 @@ static inline bool ub_entity_test_priv_flag(struct ub_entity *uent, int bit) int ub_host_probe(void); void ub_host_remove(void); +struct ub_bus_controller *ub_find_bus_controller(u32 ctl_no); struct ub_manage_subsystem_ops { u32 vendor; diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index f0e45b496b2f..f515c8a2b490 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -22,6 +22,7 @@ #include "ubus_inner.h" #include "cap.h" #include "eu.h" +#include "instance.h" #include "ubus_entity.h" /* @@ -401,6 +402,11 @@ void ub_start_ent(struct ub_entity *uent) if (!uent) return; + if (is_ibus_controller(uent)) { + ret = ub_static_bus_instance_init(uent->ubc); + WARN_ON(ret); + } + uent->match_driver = true; ret = device_attach(&uent->dev); if (ret < 0 && ret != -EPROBE_DEFER) @@ -460,6 +466,9 @@ void ub_stop_ent(struct ub_entity *uent) device_release_driver(&uent->dev); uent->match_driver = false; + + if (is_ibus_controller(uent)) + ub_static_bus_instance_uninit(uent->ubc); } EXPORT_SYMBOL_GPL(ub_stop_ent); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index ac6abf1a9819..721fdca89eb6 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -360,10 +360,32 @@ struct ub_bus_controller { struct list_head devs; struct ub_bus_controller_ops *ops; bool cluster; + struct ub_bus_instance *bi; void *data; }; +struct ub_bus_instance_info { + u8 type; + u16 upi; + u32 eid : 20; + struct ub_guid guid; +}; + +struct ub_bus_instance { + bool registered; + bool destroy; + struct list_head node; + struct kref kref; + + struct ub_bus_instance_info info; + + struct ub_bus_controller *major; + + struct list_head uents; + struct mutex lock; +}; + static inline struct ub_driver *to_ub_driver(struct device_driver *drv) { return drv ? container_of(drv, struct ub_driver, driver) : NULL; -- Gitee From 76b64a043935ff7598420953bef4eccc27e45418 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Mon, 22 Sep 2025 11:28:03 +0800 Subject: [PATCH 050/214] ub:ubus: Support for dynamic bus instance ANBZ: #45460 commit ecf8e782249588c5f1522b8b28d6da295f076b98 openEuler. Implementing the creation and destruction of dynamically typed bus instances, the trigger path can be a user-space ioctl or a remote message. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/instance.c | 264 ++++++++++++++++++++++++++++++++++-- drivers/ub/ubus/instance.h | 12 +- drivers/ub/ubus/ioctl.c | 28 ++++ include/uapi/ub/ubus/ubus.h | 33 +++++ include/ub/ubus/ubus.h | 4 + 5 files changed, 332 insertions(+), 9 deletions(-) diff --git a/drivers/ub/ubus/instance.c b/drivers/ub/ubus/instance.c index 942cf52f34c0..ba7815bf1b02 100644 --- a/drivers/ub/ubus/instance.c +++ b/drivers/ub/ubus/instance.c @@ -14,8 +14,11 @@ #include "ubus_driver.h" #include "instance.h" +#define DYNAMIC_DEFAULT_UBC 0 + static LIST_HEAD(ubi_list); static DEFINE_MUTEX(ubi_list_mutex); +static DEFINE_MUTEX(dynamic_mutex); static u32 instance_count; static void ub_unregister_bus_instance(struct ub_bus_instance *bi); @@ -23,6 +26,9 @@ static void ub_bus_instance_destroy(struct ub_bus_instance *bi) { guid_t *tar = (guid_t *)&guid_null; + if (is_dynamic(bi)) + tar = &bi->info.guid.id; + ummu_core_del_eid(tar, bi->info.eid, EID_BYPASS); ub_unregister_bus_instance(bi); } @@ -151,6 +157,13 @@ static int bi_valid_check(struct ub_bus_instance *bi) return -EINVAL; } + if (is_cluster(bi) && (info->eid == 0 || info->upi == 0 || + info->eid <= ubc_eid_end)) { + pr_err("cluster bi eid or upi invalid, eid=%#x, upi=%#x\n", + info->eid, info->upi); + return -EINVAL; + } + if (is_server(bi) && info->eid && info->eid <= ubc_eid_end) { pr_err("server bi eid within the local scope, eid=%#x\n", info->eid); @@ -167,6 +180,9 @@ static int ub_cfg_bus_instance_eid(struct ub_bus_instance *bi, bool alloc) u32 eid; int ret; + if (is_cluster(bi)) + return 0; + if (alloc) { if (info->eid) return 0; @@ -193,7 +209,7 @@ static void ub_cfg_bus_instance_upi(struct ub_bus_instance *bi) { struct ub_bus_instance_info *info = &bi->info; - if (info->upi) + if (is_cluster(bi) || info->upi) return; info->upi = UB_CP_UPI; @@ -202,7 +218,8 @@ static void ub_cfg_bus_instance_upi(struct ub_bus_instance *bi) static int ub_register_bus_instance(struct ub_bus_instance *bi) { struct ub_bus_instance_info *info = &bi->info; - int ret; + struct ub_bus_controller *ubc; + int ret, i = 0, count = 0; ret = bi_duplicate_check(info); if (ret) @@ -218,10 +235,20 @@ static int ub_register_bus_instance(struct ub_bus_instance *bi) ub_cfg_bus_instance_upi(bi); - ret = ub_cfg_eu_table(bi->major, true, bi->info.eid, - bi->info.upi); - if (ret) - goto out; + if (is_static_server(bi)) { + ret = ub_cfg_eu_table(bi->major, true, bi->info.eid, + bi->info.upi); + if (ret) + goto out; + } else { + list_for_each_entry(ubc, &ubc_list, node) { + ret = ub_cfg_eu_table(ubc, true, bi->info.eid, + bi->info.upi); + if (ret) + goto cfg_fail; + count++; + } + } mutex_lock(&ubi_list_mutex); bi->registered = true; @@ -229,7 +256,14 @@ static int ub_register_bus_instance(struct ub_bus_instance *bi) instance_count++; mutex_unlock(&ubi_list_mutex); return 0; +cfg_fail: + list_for_each_entry(ubc, &ubc_list, node) { + if (i == count) + break; + (void)ub_cfg_eu_table(ubc, false, bi->info.eid, bi->info.upi); + i++; + } out: /* upi no need unconfigure */ (void)ub_cfg_bus_instance_eid(bi, false); @@ -238,6 +272,8 @@ static int ub_register_bus_instance(struct ub_bus_instance *bi) static void ub_unregister_bus_instance(struct ub_bus_instance *bi) { + struct ub_bus_controller *ubc; + mutex_lock(&ubi_list_mutex); if (!bi->registered) { mutex_unlock(&ubi_list_mutex); @@ -249,8 +285,14 @@ static void ub_unregister_bus_instance(struct ub_bus_instance *bi) bi->registered = false; mutex_unlock(&ubi_list_mutex); - (void)ub_cfg_eu_table(bi->major, false, bi->info.eid, - bi->info.upi); + if (is_static_server(bi)) { + (void)ub_cfg_eu_table(bi->major, false, bi->info.eid, + bi->info.upi); + } else { + list_for_each_entry(ubc, &ubc_list, node) + (void)ub_cfg_eu_table(ubc, false, bi->info.eid, + bi->info.upi); + } /* upi no need unconfigure */ (void)ub_cfg_bus_instance_eid(bi, false); @@ -318,3 +360,209 @@ void ub_static_bus_instance_uninit(struct ub_bus_controller *ubc) ub_bus_instance_put(ubc->bi); ubc->bi = NULL; } + +static struct ub_bus_instance * +ub_dynamic_bus_instance_create(struct ub_bus_instance_info *info, enum eid_type type) +{ + struct ub_bus_instance *bi; + struct ub_bus_controller *ubc; + int ret; + + ubc = ub_find_bus_controller(DYNAMIC_DEFAULT_UBC); + if (!ubc) { + pr_err("ubc 0 not exist\n"); + return (struct ub_bus_instance *)ERR_PTR(-ENODEV); + } + + bi = ub_alloc_bus_instance(); + if (!bi) + return (struct ub_bus_instance *)ERR_PTR(-ENOMEM); + + if (ubc->cluster) + info->type = UBUS_INSTANCE_DYNAMIC_CLUSTER; + else + info->type = UBUS_INSTANCE_DYNAMIC_SERVER; + + bi->info = *info; + bi->major = ubc; + + ret = ub_register_bus_instance(bi); + if (ret) + goto put; + + ret = ummu_core_add_eid(&bi->info.guid.id, bi->info.eid, type); + if (ret) { + pr_err("bus instance add eid, ret=%d\n", ret); + goto unregister; + } + + return bi; + +unregister: + ub_unregister_bus_instance(bi); +put: + ub_bus_instance_put(bi); + return (struct ub_bus_instance *)ERR_PTR(ret); +} + +static void bi_info_init(struct ub_bus_instance_info *info, + const struct ubus_cmd_bi_create *create) +{ + info->type = create->type; + info->upi = create->upi; + info->eid = create->eid; + guid_copy(&info->guid.id, (const guid_t *)create->guid); +} + +int ub_ioctl_bus_instance_create(void __user *uptr) +{ + size_t size = sizeof(struct ubus_cmd_bi_create); + struct ubus_cmd_bi_create create = {}; + struct ub_bus_instance_info info = {}; + struct ub_bus_instance *bi; + + if (copy_from_user(&create, uptr + UBUS_IOCTL_HEADER_SIZE, size)) + return -EFAULT; + + bi_info_init(&info, &create); + + mutex_lock(&dynamic_mutex); + bi = ub_dynamic_bus_instance_create(&info, EID_BYPASS); + if (IS_ERR(bi)) { + pr_err("bus instance create failed, ret=%ld\n", PTR_ERR(bi)); + mutex_unlock(&dynamic_mutex); + return PTR_ERR(bi); + } + + create.eid = bi->info.eid; + create.upi = bi->info.upi; + + if (copy_to_user(uptr + UBUS_IOCTL_HEADER_SIZE, &create, size)) { + ub_bus_instance_put(bi); + pr_err("bus instance copy to user failed\n"); + mutex_unlock(&dynamic_mutex); + return -EFAULT; + } + + mutex_unlock(&dynamic_mutex); + return 0; +} + +int ub_ioctl_bus_instance_destroy(void __user *uptr) +{ + size_t size = sizeof(struct ubus_cmd_bi_destroy); + struct ubus_cmd_bi_destroy destroy = {}; + struct ub_bus_instance *bi; + char b_str[SZ_64]; + + if (copy_from_user(&destroy, uptr + UBUS_IOCTL_HEADER_SIZE, size)) + return -EFAULT; + + (void)snprintf(b_str, SZ_64, "%#llx %llx", + *((u64 *)&destroy.guid[SZ_8]), + *((u64 *)&destroy.guid[0])); + + mutex_lock(&dynamic_mutex); + bi = ub_find_bus_instance(guid_match, destroy.guid); + if (!bi) { + pr_err("bus instance destroy invalid, guid=%s\n", b_str); + mutex_unlock(&dynamic_mutex); + return -ENODEV; + } + + if (!is_dynamic(bi)) { + pr_err("instance %s is not dynamic\n", b_str); + ub_bus_instance_put(bi); + mutex_unlock(&dynamic_mutex); + return -EINVAL; + } + + if (kref_read(&bi->kref) != 2) { /* 2 is original + find */ + pr_err("instance %s is still in use\n", b_str); + ub_bus_instance_put(bi); + mutex_unlock(&dynamic_mutex); + return -EBUSY; + } + + ub_bus_instance_put(bi); /* put find */ + ub_bus_instance_put(bi); /* real put */ + + mutex_unlock(&dynamic_mutex); + return 0; +} + +int ub_msg_bus_instance_create(struct ub_bus_controller *ubc, u32 *guid, u32 eid, + u16 upi, enum eid_type type) +{ + struct ub_bus_instance_info info = {}; + struct ub_bus_instance *bi; + int ret = 0; + + info.upi = upi; + info.eid = eid; + guid_copy(&info.guid.id, (const guid_t *)guid); + + mutex_lock(&dynamic_mutex); + bi = ub_dynamic_bus_instance_create(&info, type); + if (IS_ERR(bi)) { + ret = PTR_ERR(bi); + dev_err(&ubc->dev, "msg bus instance create failed, ret=%d\n", + ret); + } + + mutex_unlock(&dynamic_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(ub_msg_bus_instance_create); + +int ub_msg_bus_instance_destroy(struct ub_bus_controller *ubc, u32 *guid) +{ + struct ub_bus_instance *bi; + char b_str[SZ_64]; + + (void)snprintf(b_str, SZ_64, "%#llx %llx", *((u64 *)&guid[SZ_2]), + *((u64 *)&guid[0])); + + mutex_lock(&dynamic_mutex); + bi = ub_find_bus_instance(guid_match, guid); + if (!bi) { + dev_err(&ubc->dev, + "msg bus instance destroy invalid, guid=%s\n", b_str); + mutex_unlock(&dynamic_mutex); + return -ENODEV; + } + + if (!is_dynamic_cluster(bi)) { + dev_err(&ubc->dev, "msg destroy instance %s type invalid\n", + b_str); + ub_bus_instance_put(bi); + mutex_unlock(&dynamic_mutex); + return -EINVAL; + } + + if (kref_read(&bi->kref) != 2) { /* 2 is original + find */ + dev_err(&ubc->dev, "msg instance %s still in use\n", b_str); + ub_bus_instance_put(bi); + mutex_unlock(&dynamic_mutex); + return -EBUSY; + } + + ub_bus_instance_put(bi); /* put find */ + ub_bus_instance_put(bi); /* real put */ + + mutex_unlock(&dynamic_mutex); + return 0; +} + +void ub_dynamic_bus_instance_drain(void) +{ + struct ub_bus_instance *bi, *tmp; + + mutex_lock(&dynamic_mutex); + + list_for_each_entry_safe(bi, tmp, &ubi_list, node) + if (is_dynamic(bi)) + ub_bus_instance_put(bi); + + mutex_unlock(&dynamic_mutex); +} diff --git a/drivers/ub/ubus/instance.h b/drivers/ub/ubus/instance.h index 9a835afd7b99..ba2175aa6239 100644 --- a/drivers/ub/ubus/instance.h +++ b/drivers/ub/ubus/instance.h @@ -13,12 +13,22 @@ typedef bool (*instance_match)(struct ub_bus_instance *bi, void *arg); #define UBUS_INSTANCE_STATIC_SERVER 0 #define is_static_server(bi) ((bi)->info.type == UBUS_INSTANCE_STATIC_SERVER) +#define is_dynamic_server(bi) ((bi)->info.type == UBUS_INSTANCE_DYNAMIC_SERVER) +#define is_dynamic_cluster(bi) ((bi)->info.type == UBUS_INSTANCE_DYNAMIC_CLUSTER) #define is_static(bi) (is_static_server(bi)) -#define is_server(bi) (is_static_server(bi)) +#define is_dynamic(bi) (is_dynamic_server(bi) || is_dynamic_cluster(bi)) +#define is_server(bi) (is_static_server(bi) || is_dynamic_server(bi)) +#define is_cluster(bi) (is_dynamic_cluster(bi)) int ub_static_bus_instance_init(struct ub_bus_controller *ubc); void ub_static_bus_instance_uninit(struct ub_bus_controller *ubc); void ub_bus_instance_put(struct ub_bus_instance *bi); struct ub_bus_instance *ub_find_bus_instance(instance_match match, void *arg); +int ub_ioctl_bus_instance_create(void __user *uptr); +int ub_ioctl_bus_instance_destroy(void __user *uptr); +int ub_msg_bus_instance_create(struct ub_bus_controller *ubc, u32 *guid, u32 eid, + u16 upi, enum eid_type type); +int ub_msg_bus_instance_destroy(struct ub_bus_controller *ubc, u32 *guid); +void ub_dynamic_bus_instance_drain(void); #endif /* __INSTANCE_H__ */ diff --git a/drivers/ub/ubus/ioctl.c b/drivers/ub/ubus/ioctl.c index 88da1eccbb5c..c55b598f38e8 100644 --- a/drivers/ub/ubus/ioctl.c +++ b/drivers/ub/ubus/ioctl.c @@ -9,6 +9,7 @@ #include #include "ubus.h" +#include "instance.h" #define UBUS_MAX_DEVICES 1 #define UBUS_DEVICE_NAME "unified_bus" @@ -32,12 +33,39 @@ static int ubus_fops_release(struct inode *inode, struct file *filep) return 0; } +static int ub_ioctl_bus_instance(void __user *uptr) +{ + struct ubus_ioctl_bus_instance bi; + + if (copy_from_user(&bi, uptr, UBUS_IOCTL_HEADER_SIZE)) + return -EFAULT; + + pr_info("ub ioctl bus instance, sub_cmd=%#x\n", bi.sub_cmd); + switch (bi.sub_cmd) { + case UBUS_CMD_BI_CREATE: + if (bi.argsz != sizeof(struct ubus_cmd_bi_create)) + return -EINVAL; + return ub_ioctl_bus_instance_create(uptr); + case UBUS_CMD_BI_DESTROY: + if (bi.argsz != sizeof(struct ubus_cmd_bi_destroy)) + return -EINVAL; + return ub_ioctl_bus_instance_destroy(uptr); + default: + pr_err("ubus bi sub cmd not support, cmd=%#x\n", bi.sub_cmd); + return -EINVAL; + } +} + static long ubus_fops_unl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { + void __user *uptr = (void __user *)arg; + switch (cmd) { case UBUS_IOCTL_GET_API_VERSION: return UBUS_API_VERSION; + case UBUS_IOCTL_BUS_INSTANCE: + return ub_ioctl_bus_instance(uptr); default: pr_err("ubus ioctl cmd %#x not support\n", cmd); return -ENOTTY; diff --git a/include/uapi/ub/ubus/ubus.h b/include/uapi/ub/ubus/ubus.h index c2de27e016db..e6774aa7a8f1 100644 --- a/include/uapi/ub/ubus/ubus.h +++ b/include/uapi/ub/ubus/ubus.h @@ -28,4 +28,37 @@ */ #define UBUS_IOCTL_GET_API_VERSION _IO(UBUS_TYPE, 0) +#define GUID_SIZE 16 + +struct ubus_cmd_bi_create { +#define UBUS_INSTANCE_DYNAMIC_SERVER 2 +#define UBUS_INSTANCE_DYNAMIC_CLUSTER 3 + __u8 type; + __u16 upi; + __u32 eid; + __u8 guid[GUID_SIZE]; +}; + +struct ubus_cmd_bi_destroy { + __u8 guid[GUID_SIZE]; +}; + +#define UBUS_IOCTL_HEADER_SIZE 8 + +enum ubus_bi_cmd { + UBUS_CMD_BI_CREATE = 0, + UBUS_CMD_BI_DESTROY, + UBUS_CMD_BI_NUM +}; + +struct ubus_ioctl_bus_instance { + __u32 argsz; /* size of *union*->structure */ + __u32 sub_cmd; + union { + struct ubus_cmd_bi_create create; + struct ubus_cmd_bi_destroy destroy; + }; +}; +#define UBUS_IOCTL_BUS_INSTANCE _IOWR(UBUS_TYPE, 1, struct ubus_ioctl_bus_instance) + #endif /* _UAPI_UB_UBUS_UBUS_H_ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 721fdca89eb6..cccebd07a720 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -386,6 +387,9 @@ struct ub_bus_instance { struct mutex lock; }; +#define ub_bi_is_dynamic(bi) ((bi)->info.type == UBUS_INSTANCE_DYNAMIC_SERVER \ + || (bi)->info.type == UBUS_INSTANCE_DYNAMIC_CLUSTER) + static inline struct ub_driver *to_ub_driver(struct device_driver *drv) { return drv ? container_of(drv, struct ub_driver, driver) : NULL; -- Gitee From 7d1e33e1bd67631d5359e0d344269e1c93b62344 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Mon, 22 Sep 2025 11:53:17 +0800 Subject: [PATCH 051/214] ub:ubus: Support for static cluster bus instance ANBZ: #45460 commit eb87969321e251f0460434d21a4451b3573d2e17 openEuler. Implement the creation and destruction of bus instances of the static-cluster type, with the trigger originating from remote messages, and add a sysfs interface to view the list of bus instances in the system. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/instance.c | 160 +++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/instance.h | 11 ++- drivers/ub/ubus/sysfs.c | 2 + include/ub/ubus/ubus.h | 1 + 4 files changed, 172 insertions(+), 2 deletions(-) diff --git a/drivers/ub/ubus/instance.c b/drivers/ub/ubus/instance.c index ba7815bf1b02..47f76149e737 100644 --- a/drivers/ub/ubus/instance.c +++ b/drivers/ub/ubus/instance.c @@ -20,6 +20,69 @@ static LIST_HEAD(ubi_list); static DEFINE_MUTEX(ubi_list_mutex); static DEFINE_MUTEX(dynamic_mutex); static u32 instance_count; +static u32 instance_start; + +static ssize_t instance_store(const struct bus_type *bus, const char *buf, + size_t count) +{ + unsigned long val; + ssize_t ret; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + + mutex_lock(&ubi_list_mutex); + if (val >= instance_count) { + pr_err("store instance %#lx over %#x\n", val, instance_count); + mutex_unlock(&ubi_list_mutex); + return -EINVAL; + } + + instance_start = val; + mutex_unlock(&ubi_list_mutex); + return count; +} + +static ssize_t instance_show(const struct bus_type *bus, char *buf) +{ + struct ub_bus_instance_info *info; + struct ub_bus_instance *bi; + u32 show, left, end, i = 0; + struct ub_guid *guid; + ssize_t len; + +#define MAX_DUMP 50 + mutex_lock(&ubi_list_mutex); + left = instance_count - instance_start; + show = left > MAX_DUMP ? MAX_DUMP : left; + end = instance_start + show; + len = sysfs_emit(buf, "count %#x, from %#x, show %#x\n", instance_count, + instance_start, show); + + list_for_each_entry(bi, &ubi_list, node) { + if (i < instance_start) { + i++; + continue; + } + + if (i == end) + break; + + info = &bi->info; + guid = &info->guid; + len += sysfs_emit_at(buf, len, "guid:"); + len += ub_show_guid(guid, buf + len); + len += sysfs_emit_at(buf, len, " type:%01x eid:%05x upi:%04x\n", + info->type, info->eid, info->upi); + i++; + } + + instance_start = 0; + mutex_unlock(&ubi_list_mutex); + return len; +} +BUS_ATTR_RW(instance); static void ub_unregister_bus_instance(struct ub_bus_instance *bi); static void ub_bus_instance_destroy(struct ub_bus_instance *bi) @@ -566,3 +629,100 @@ void ub_dynamic_bus_instance_drain(void) mutex_unlock(&dynamic_mutex); } + +static int +ub_static_cluster_instance_create(struct ub_bus_controller *ubc, u32 *guid, + u32 eid, u16 upi) +{ + struct ub_bus_instance *bi; + int ret; + + bi = ub_alloc_bus_instance(); + if (!bi) + return -ENOMEM; + + bi->info.type = UBUS_INSTANCE_STATIC_CLUSTER; + guid_copy(&bi->info.guid.id, (guid_t *)guid); + bi->info.eid = eid; + bi->info.upi = upi; + bi->major = ubc; + + ret = ub_register_bus_instance(bi); + if (ret) + goto put; + + ret = ummu_core_add_eid((guid_t *)&guid_null, bi->info.eid, + EID_BYPASS); + if (ret) { + dev_err(&ubc->dev, "bus instance add eid failed, ret=%d\n", ret); + goto unregister; + } + + ubc->cluster_bi = bi; + return 0; + +unregister: + ub_unregister_bus_instance(bi); +put: + ub_bus_instance_put(bi); + return ret; +} + +int ub_notify_bus_instance_handle(struct ub_bus_controller *ubc, bool flag, + u32 *guid, u32 eid, u16 upi) +{ + struct ub_bus_instance *bi; + struct ub_guid *tmp; + int ret; + + if (!flag) { + bi = ub_find_bus_instance(guid_match, guid); + if (!bi) { + dev_err(&ubc->dev, "notify can't find bus instance\n"); + return -ENODEV; + } + + if (!is_static_cluster(bi) || bi->info.eid != eid || + bi->info.upi != upi) { + dev_err(&ubc->dev, "notify bus instance is invalid\n"); + ub_bus_instance_put(bi); + return -EINVAL; + } + + /* May receive the same UBC Notify message */ + if (ubc->cluster_bi) + ub_bus_instance_put(bi); + else + ubc->cluster_bi = bi; + + return 0; + } + + tmp = (struct ub_guid *)guid; + if (tmp->bits.type != UB_TYPE_BUS_INSTANCE) { + dev_err(&ubc->dev, "notify msg guid type is invalid\n"); + return -EINVAL; + } + + /* BI may have already been created in other UBC Notify messages */ + ret = ub_static_cluster_instance_create(ubc, guid, eid, upi); + if (ret && ret != -EEXIST) { + dev_err(&ubc->dev, "create static cluster instance failed\n"); + return ret; + } + + if (!ubc->cluster_bi) + ubc->cluster_bi = ub_find_bus_instance(guid_match, guid); + + return 0; +} + +void ub_static_cluster_instance_drain(void) +{ + struct ub_bus_controller *ubc; + + list_for_each_entry(ubc, &ubc_list, node) { + ub_bus_instance_put(ubc->cluster_bi); + ubc->cluster_bi = NULL; + } +} diff --git a/drivers/ub/ubus/instance.h b/drivers/ub/ubus/instance.h index ba2175aa6239..be09586e1b39 100644 --- a/drivers/ub/ubus/instance.h +++ b/drivers/ub/ubus/instance.h @@ -8,17 +8,21 @@ #include +extern struct bus_attribute bus_attr_instance; + typedef bool (*instance_match)(struct ub_bus_instance *bi, void *arg); #define UBUS_INSTANCE_STATIC_SERVER 0 +#define UBUS_INSTANCE_STATIC_CLUSTER 1 #define is_static_server(bi) ((bi)->info.type == UBUS_INSTANCE_STATIC_SERVER) +#define is_static_cluster(bi) ((bi)->info.type == UBUS_INSTANCE_STATIC_CLUSTER) #define is_dynamic_server(bi) ((bi)->info.type == UBUS_INSTANCE_DYNAMIC_SERVER) #define is_dynamic_cluster(bi) ((bi)->info.type == UBUS_INSTANCE_DYNAMIC_CLUSTER) -#define is_static(bi) (is_static_server(bi)) +#define is_static(bi) (is_static_server(bi) || is_static_cluster(bi)) #define is_dynamic(bi) (is_dynamic_server(bi) || is_dynamic_cluster(bi)) #define is_server(bi) (is_static_server(bi) || is_dynamic_server(bi)) -#define is_cluster(bi) (is_dynamic_cluster(bi)) +#define is_cluster(bi) (is_static_cluster(bi) || is_dynamic_cluster(bi)) int ub_static_bus_instance_init(struct ub_bus_controller *ubc); void ub_static_bus_instance_uninit(struct ub_bus_controller *ubc); @@ -30,5 +34,8 @@ int ub_msg_bus_instance_create(struct ub_bus_controller *ubc, u32 *guid, u32 eid u16 upi, enum eid_type type); int ub_msg_bus_instance_destroy(struct ub_bus_controller *ubc, u32 *guid); void ub_dynamic_bus_instance_drain(void); +int ub_notify_bus_instance_handle(struct ub_bus_controller *ubc, bool flag, + u32 *guid, u32 eid, u16 upi); +void ub_static_cluster_instance_drain(void); #endif /* __INSTANCE_H__ */ diff --git a/drivers/ub/ubus/sysfs.c b/drivers/ub/ubus/sysfs.c index b816e82e4c0d..91324fd8f150 100644 --- a/drivers/ub/ubus/sysfs.c +++ b/drivers/ub/ubus/sysfs.c @@ -8,6 +8,7 @@ #include "ubus.h" #include "sysfs.h" #include "ubus_entity.h" +#include "instance.h" #define ub_config_attr(field, format_string) \ static ssize_t field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ @@ -133,6 +134,7 @@ const struct attribute_group *ub_entity_groups[] = { }; static struct attribute *ub_bus_attrs[] = { + &bus_attr_instance.attr, NULL }; diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index cccebd07a720..57883bae6c07 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -362,6 +362,7 @@ struct ub_bus_controller { struct ub_bus_controller_ops *ops; bool cluster; struct ub_bus_instance *bi; + struct ub_bus_instance *cluster_bi; void *data; }; -- Gitee From ff3184157ffb186288cd35ce332b95007c9a7ad3 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Mon, 22 Sep 2025 14:17:58 +0800 Subject: [PATCH 052/214] ub:ubus: Support device binding and unbinding bus instance ANBZ: #45460 commit f3805d31c4db16e93b043326bcf21512af8d3074 openEuler. Initialize the default bus instance of the device and provide an interface for dynamically switching bus instances. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/entity.c | 2 + drivers/ub/ubus/instance.c | 196 ++++++++++++++++++++++++++++++++++ drivers/ub/ubus/instance.h | 4 + drivers/ub/ubus/ioctl.c | 8 ++ drivers/ub/ubus/sysfs.c | 14 +++ drivers/ub/ubus/ubus_entity.c | 8 ++ include/uapi/ub/ubus/ubus.h | 14 +++ include/ub/ubus/ubus.h | 3 + 8 files changed, 249 insertions(+) diff --git a/drivers/ub/ubus/entity.c b/drivers/ub/ubus/entity.c index 5d91ee2c2c3e..a288634f30c8 100644 --- a/drivers/ub/ubus/entity.c +++ b/drivers/ub/ubus/entity.c @@ -19,6 +19,8 @@ int ub_get_dst_eid(struct ub_entity *dev) return -ENODEV; eid = ubc->uent->eid; + if (dev->bi) + eid = dev->bi->info.eid; return eid; } diff --git a/drivers/ub/ubus/instance.c b/drivers/ub/ubus/instance.c index 47f76149e737..603cf517fad9 100644 --- a/drivers/ub/ubus/instance.c +++ b/drivers/ub/ubus/instance.c @@ -726,3 +726,199 @@ void ub_static_cluster_instance_drain(void) ubc->cluster_bi = NULL; } } + +/* Only for dynamic use */ +static int get_bus_instance_and_uent(u8 *b_guid, u8 *d_guid, + struct ub_bus_instance **p_bi, + struct ub_entity **p_uent) +{ + char b_str[SZ_64], d_str[SZ_64]; + struct ub_bus_instance *bi; + struct ub_entity *uent; + int ret = -ENODEV; + + (void)snprintf(b_str, SZ_64, "%#llx %llx", *((u64 *)&b_guid[SZ_8]), + *((u64 *)&b_guid[0])); + (void)snprintf(d_str, SZ_64, "%#llx %llx", *((u64 *)&d_guid[SZ_8]), + *((u64 *)&d_guid[0])); + + bi = ub_find_bus_instance(guid_match, b_guid); + if (!bi) + goto err; + + if (!is_dynamic(bi)) { + ret = -EINVAL; + goto put; + } + + uent = ub_get_ent_by_guid((struct ub_guid *)d_guid); + if (!uent) + goto put; + + *p_bi = bi; + *p_uent = uent; + return 0; +put: + ub_bus_instance_put(bi); +err: + pr_err("get bi and uent failed, bi_guid=%s, dev_guid=%s\n", b_str, d_str); + return ret; +} + +static int ub_bind_bus_instance(struct ub_entity *uent, struct ub_bus_instance *bi) +{ + if (!uent || uent->bi || !bi || !bi->registered) + return -EINVAL; + + uent->bi = ub_bus_instance_get(bi); + + mutex_lock(&bi->lock); + list_add_tail(&uent->instance_node, &bi->uents); + mutex_unlock(&bi->lock); + return 0; +} + +static void ub_unbind_bus_instance(struct ub_entity *uent) +{ + if (!uent || !uent->bi) + return; + + mutex_lock(&uent->bi->lock); + list_del(&uent->instance_node); + mutex_unlock(&uent->bi->lock); + + ub_bus_instance_put(uent->bi); + uent->bi = NULL; +} + +int ub_ioctl_bus_instance_bind(void __user *uptr) +{ + size_t size = sizeof(struct ubus_cmd_bi_bind); + struct ubus_cmd_bi_bind bind = {}; + struct ub_bus_instance *bi, *old; + struct ub_entity *uent; + char b_str[SZ_64]; + int ret; + + if (copy_from_user(&bind, uptr + UBUS_IOCTL_HEADER_SIZE, size)) + return -EFAULT; + + mutex_lock(&dynamic_mutex); + ret = get_bus_instance_and_uent(bind.instance_guid, bind.dev_guid, &bi, + &uent); + if (ret) { + mutex_unlock(&dynamic_mutex); + return ret; + } + + (void)snprintf(b_str, SZ_64, "%#llx %llx", + *((u64 *)&bind.instance_guid[SZ_8]), + *((u64 *)&bind.instance_guid[0])); + + mutex_lock(&uent->instance_lock); + old = uent->bi; + if (!old) { + ub_err(uent, "dev has no static instance\n"); + ret = -EINVAL; + goto out; + } + + if (is_static(old)) { + ub_unbind_bus_instance(uent); + ret = ub_bind_bus_instance(uent, bi); + if (ret) { + ub_err(uent, "bind instance failed, guid=%s\n", b_str); + (void)ub_bind_bus_instance(uent, uent->ubc->bi); + } + } else { + /* If bind the same again, do nothing */ + if (old != bi) { + ub_err(uent, "dev already bind instance\n"); + ret = -EBUSY; + } + } + +out: + ub_entity_put(uent); + ub_bus_instance_put(bi); + mutex_unlock(&uent->instance_lock); + mutex_unlock(&dynamic_mutex); + return ret; +} + +int ub_ioctl_bus_instance_unbind(void __user *uptr) +{ + size_t size = sizeof(struct ubus_cmd_bi_unbind); + struct ubus_cmd_bi_unbind unbind = {}; + struct ub_bus_instance *bi; + struct ub_entity *uent; + char b_str[SZ_64]; + int ret; + + if (copy_from_user(&unbind, uptr + UBUS_IOCTL_HEADER_SIZE, size)) + return -EFAULT; + + mutex_lock(&dynamic_mutex); + ret = get_bus_instance_and_uent(unbind.instance_guid, unbind.dev_guid, + &bi, &uent); + if (ret) { + mutex_unlock(&dynamic_mutex); + return ret; + } + + (void)snprintf(b_str, SZ_64, "%#llx %llx", + *((u64 *)&unbind.instance_guid[SZ_8]), + *((u64 *)&unbind.instance_guid[0])); + + mutex_lock(&uent->instance_lock); + if (uent->bi != bi) { + ub_err(uent, "dev not bind bus instance, guid=%s\n", b_str); + ret = -EINVAL; + goto out; + } + + ub_unbind_bus_instance(uent); + ret = ub_bind_bus_instance(uent, uent->ubc->bi); + if (ret) { + ub_err(uent, "unbind instance failed, guid=%s\n", b_str); + (void)ub_bind_bus_instance(uent, bi); + } + +out: + ub_entity_put(uent); + ub_bus_instance_put(bi); + mutex_unlock(&uent->instance_lock); + mutex_unlock(&dynamic_mutex); + return ret; +} + +int ub_default_bus_instance_init(struct ub_entity *uent) +{ + struct ub_bus_instance *bi; + int ret; + + if (is_switch(uent)) + return 0; + + bi = uent->ubc->bi; + if (!bi) { + ub_err(uent, "get default bi NULL\n"); + return -EINVAL; + } + + mutex_lock(&uent->instance_lock); + ret = ub_bind_bus_instance(uent, bi); + mutex_unlock(&uent->instance_lock); + + return ret; +} + +void ub_default_bus_instance_uninit(struct ub_entity *uent) +{ + if (is_switch(uent)) + return; + + mutex_lock(&uent->instance_lock); + ub_unbind_bus_instance(uent); + mutex_unlock(&uent->instance_lock); +} diff --git a/drivers/ub/ubus/instance.h b/drivers/ub/ubus/instance.h index be09586e1b39..2033c264e774 100644 --- a/drivers/ub/ubus/instance.h +++ b/drivers/ub/ubus/instance.h @@ -30,6 +30,8 @@ void ub_bus_instance_put(struct ub_bus_instance *bi); struct ub_bus_instance *ub_find_bus_instance(instance_match match, void *arg); int ub_ioctl_bus_instance_create(void __user *uptr); int ub_ioctl_bus_instance_destroy(void __user *uptr); +int ub_ioctl_bus_instance_bind(void __user *uptr); +int ub_ioctl_bus_instance_unbind(void __user *uptr); int ub_msg_bus_instance_create(struct ub_bus_controller *ubc, u32 *guid, u32 eid, u16 upi, enum eid_type type); int ub_msg_bus_instance_destroy(struct ub_bus_controller *ubc, u32 *guid); @@ -37,5 +39,7 @@ void ub_dynamic_bus_instance_drain(void); int ub_notify_bus_instance_handle(struct ub_bus_controller *ubc, bool flag, u32 *guid, u32 eid, u16 upi); void ub_static_cluster_instance_drain(void); +int ub_default_bus_instance_init(struct ub_entity *uent); +void ub_default_bus_instance_uninit(struct ub_entity *uent); #endif /* __INSTANCE_H__ */ diff --git a/drivers/ub/ubus/ioctl.c b/drivers/ub/ubus/ioctl.c index c55b598f38e8..abcd4e878755 100644 --- a/drivers/ub/ubus/ioctl.c +++ b/drivers/ub/ubus/ioctl.c @@ -50,6 +50,14 @@ static int ub_ioctl_bus_instance(void __user *uptr) if (bi.argsz != sizeof(struct ubus_cmd_bi_destroy)) return -EINVAL; return ub_ioctl_bus_instance_destroy(uptr); + case UBUS_CMD_BI_BIND: + if (bi.argsz != sizeof(struct ubus_cmd_bi_bind)) + return -EINVAL; + return ub_ioctl_bus_instance_bind(uptr); + case UBUS_CMD_BI_UNBIND: + if (bi.argsz != sizeof(struct ubus_cmd_bi_unbind)) + return -EINVAL; + return ub_ioctl_bus_instance_unbind(uptr); default: pr_err("ubus bi sub cmd not support, cmd=%#x\n", bi.sub_cmd); return -EINVAL; diff --git a/drivers/ub/ubus/sysfs.c b/drivers/ub/ubus/sysfs.c index 91324fd8f150..309d685d31c2 100644 --- a/drivers/ub/ubus/sysfs.c +++ b/drivers/ub/ubus/sysfs.c @@ -112,6 +112,19 @@ static ssize_t match_driver_show(struct device *dev, } static DEVICE_ATTR_RW(match_driver); +static ssize_t instance_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + u32 eid = 0; + + if (uent->bi) + eid = uent->bi->info.eid; + + return sysfs_emit(buf, "%#05x\n", eid); +} +DEVICE_ATTR_RO(instance); + static struct attribute *ub_entity_attrs[] = { &dev_attr_vendor.attr, &dev_attr_device.attr, @@ -120,6 +133,7 @@ static struct attribute *ub_entity_attrs[] = { &dev_attr_driver_override.attr, &dev_attr_match_driver.attr, &dev_attr_guid.attr, + &dev_attr_instance.attr, &dev_attr_kref.attr, NULL }; diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index f515c8a2b490..f79ac083e6a0 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -49,6 +49,7 @@ struct ub_entity *ub_alloc_ent(void) INIT_LIST_HEAD(&uent->mue_list); INIT_LIST_HEAD(&uent->ue_list); INIT_LIST_HEAD(&uent->cna_list); + INIT_LIST_HEAD(&uent->instance_node); uent->dev.type = &ub_dev_type; uent->cna = 0; @@ -407,6 +408,9 @@ void ub_start_ent(struct ub_entity *uent) WARN_ON(ret); } + ret = ub_default_bus_instance_init(uent); + WARN_ON(ret); + uent->match_driver = true; ret = device_attach(&uent->dev); if (ret < 0 && ret != -EPROBE_DEFER) @@ -467,6 +471,8 @@ void ub_stop_ent(struct ub_entity *uent) device_release_driver(&uent->dev); uent->match_driver = false; + ub_default_bus_instance_uninit(uent); + if (is_ibus_controller(uent)) ub_static_bus_instance_uninit(uent->ubc); } @@ -963,6 +969,8 @@ int ub_set_user_info(struct ub_entity *uent) goto cfg1; /* set dsteid to device */ + if (uent->bi) + eid = uent->bi->info.eid; ub_cfg_write_dword(uent, UB_UEID_0, eid); ub_cfg_write_dword(uent, UB_UEID_1, 0); ub_cfg_write_dword(uent, UB_UEID_2, 0); diff --git a/include/uapi/ub/ubus/ubus.h b/include/uapi/ub/ubus/ubus.h index e6774aa7a8f1..388a4cf043ce 100644 --- a/include/uapi/ub/ubus/ubus.h +++ b/include/uapi/ub/ubus/ubus.h @@ -43,11 +43,23 @@ struct ubus_cmd_bi_destroy { __u8 guid[GUID_SIZE]; }; +struct ubus_cmd_bi_bind { + __u8 instance_guid[GUID_SIZE]; + __u8 dev_guid[GUID_SIZE]; +}; + +struct ubus_cmd_bi_unbind { + __u8 instance_guid[GUID_SIZE]; + __u8 dev_guid[GUID_SIZE]; +}; + #define UBUS_IOCTL_HEADER_SIZE 8 enum ubus_bi_cmd { UBUS_CMD_BI_CREATE = 0, UBUS_CMD_BI_DESTROY, + UBUS_CMD_BI_BIND, + UBUS_CMD_BI_UNBIND, UBUS_CMD_BI_NUM }; @@ -57,6 +69,8 @@ struct ubus_ioctl_bus_instance { union { struct ubus_cmd_bi_create create; struct ubus_cmd_bi_destroy destroy; + struct ubus_cmd_bi_bind bind; + struct ubus_cmd_bi_unbind unbind; }; }; #define UBUS_IOCTL_BUS_INSTANCE _IOWR(UBUS_TYPE, 1, struct ubus_ioctl_bus_instance) diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 57883bae6c07..61bad7637d90 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -234,6 +234,9 @@ struct ub_entity { u32 saved_config_space[24]; /* Config space saved at reset time */ /* entity bus instance info */ + struct mutex instance_lock; + struct list_head instance_node; + struct ub_bus_instance *bi; struct ub_eu_table *eu_table; u32 support_feature; -- Gitee From f8dd82a48dc46ac672ef8bd2f314dd6dbe5168f7 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Mon, 22 Sep 2025 11:14:03 +0800 Subject: [PATCH 053/214] ub:ubus: Add UBUS receive message framework ANBZ: #45460 commit 63179ebb7e8e64d4a815144d79f56dc764cd84b6 openEuler. Add UBUS receive message framework, including message header, payload and handler interfaces Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/msg.c | 179 ++++++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/msg.h | 19 +++++ 2 files changed, 198 insertions(+) diff --git a/drivers/ub/ubus/msg.c b/drivers/ub/ubus/msg.c index 67dc56ab8ce3..d7bfca8eb725 100644 --- a/drivers/ub/ubus/msg.c +++ b/drivers/ub/ubus/msg.c @@ -135,6 +135,16 @@ int message_send(struct message_device *mdev, struct msg_info *info, return -ENOTTY; } +int message_response(struct message_device *mdev, struct msg_info *info, + u8 code) +{ + if (mdev->ops->response) + return mdev->ops->response(mdev, info, code); + + return -ENOTTY; +} +EXPORT_SYMBOL_GPL(message_response); + int message_sync_enum(struct message_device *mdev, struct msg_info *info, u8 cmd) { @@ -143,3 +153,172 @@ int message_sync_enum(struct message_device *mdev, struct msg_info *info, return -ENOTTY; } + +static struct workqueue_struct *rx_msg_wq[UB_MSG_CODE_NUM]; +struct workqueue_struct *get_rx_msg_wq(u8 msg_code) +{ + return rx_msg_wq[msg_code]; +} + +static bool msg_rx_flag; + +int message_rx_init(void) +{ + const char *msg_name[UB_MSG_CODE_NUM] = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL + }; + struct workqueue_struct *q; + int i; + + for (i = 0; i < UB_MSG_CODE_NUM; i++) { + if (!msg_name[i]) + continue; + q = create_singlethread_workqueue(msg_name[i]); + if (!q) { + pr_err("alloc workqueue[%d] failed\n", i); + message_rx_uninit(); + return -ENOMEM; + } + + rx_msg_wq[i] = q; + } + + msg_rx_flag = true; + + return 0; +} + +void message_rx_uninit(void) +{ +#define MSG_RX_WAIT_US 1000 + struct workqueue_struct *q; + int i; + + msg_rx_flag = false; + /* For cpus still handle rx msg in interrupt context */ + udelay(MSG_RX_WAIT_US); + + for (i = 0; i < UB_MSG_CODE_NUM; i++) { + q = rx_msg_wq[i]; + if (q) { + flush_workqueue(q); + destroy_workqueue(q); + rx_msg_wq[i] = NULL; + } + } +} + +static struct ub_rx_msg_task * +message_rx_task_alloc_and_init(struct ub_bus_controller *ubc, void *pkt, u16 len, + work_func_t func) +{ + struct ub_rx_msg_task *task; + + task = kzalloc(sizeof(*task), GFP_ATOMIC); + if (!task) + return (struct ub_rx_msg_task *)ERR_PTR(-ENOMEM); + + task->pkt = kzalloc(len, GFP_ATOMIC); + if (!task->pkt) { + kfree(task); + return (struct ub_rx_msg_task *)ERR_PTR(-ENOMEM); + } + + memcpy(task->pkt, pkt, len); + task->len = len; + task->ubc = ubc; + INIT_WORK(&task->work, func); + + return task; +} + +static void message_rx_task_free(struct ub_rx_msg_task *task) +{ + kfree(task->pkt); + kfree(task); +} + +static bool msg_rx_code_valid(struct ub_bus_controller *ubc, u8 code) +{ + u8 msg = msg_code(code); + + if (msg_type(code) == MSG_RSP) + return false; + + if (msg == UB_MSG_CODE_RAS || msg == UB_MSG_CODE_CFG || + msg == UB_MSG_CODE_EXCH || msg == UB_MSG_CODE_MAX) + return false; + + if (!ubc->cluster && msg == UB_MSG_CODE_POOL) + return false; + + return true; +} + +static rx_msg_handler_t rx_msg_handler[UB_MSG_CODE_NUM] = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +static void message_rx_work(struct work_struct *work) +{ + struct ub_rx_msg_task *task = container_of(work, struct ub_rx_msg_task, + work); + struct msg_pkt_header *header = (struct msg_pkt_header *)task->pkt; + u8 msg_code = header->msgetah.msg_code; + struct ub_bus_controller *ubc = task->ubc; + rx_msg_handler_t handler; + + handler = rx_msg_handler[msg_code]; + + if (handler) + handler(ubc, task->pkt, task->len); + else + dev_err(&ubc->dev, "rx msg code not support, code=%#x\n", + msg_code); + + message_rx_task_free(task); +} + +int message_rx_handler(struct ub_bus_controller *ubc, void *pkt, u16 len) +{ + struct msg_pkt_header *header = (struct msg_pkt_header *)pkt; + struct msg_extended_header *msgetah = &header->msgetah; + struct ub_rx_msg_task *task; + + if (!msg_rx_flag) + return -EBUSY; + + if (len < MSG_PKT_HEADER_SIZE) { + dev_err(&ubc->dev, "rx msg len invalid, len=%#x\n", len); + return -EINVAL; + } + + if (msgetah->plen != len - MSG_PKT_HEADER_SIZE) { + dev_err(&ubc->dev, "rx msg plen invalid, len=%#x, plen=%#x\n", + len, msgetah->plen); + return -EINVAL; + } + + if (!msg_rx_code_valid(ubc, msgetah->code)) { + dev_err(&ubc->dev, "rx msg code invalid, code=%#x\n", + msgetah->code); + return -EINVAL; + } + + dev_info(&ubc->dev, "rx msg coming, code=%#x\n", msgetah->code); + + task = message_rx_task_alloc_and_init(ubc, pkt, len, message_rx_work); + if (IS_ERR(task)) + return PTR_ERR(task); + + queue_work(rx_msg_wq[msgetah->msg_code], &task->work); + return 0; +} +EXPORT_SYMBOL_GPL(message_rx_handler); diff --git a/drivers/ub/ubus/msg.h b/drivers/ub/ubus/msg.h index c980033803bc..858782c2fead 100644 --- a/drivers/ub/ubus/msg.h +++ b/drivers/ub/ubus/msg.h @@ -186,12 +186,15 @@ enum message_tx_type { #define msg_size_gen(req_size, rsp_size) ((u32)((req_size) << 16) | (rsp_size)) +typedef void (*rx_msg_handler_t)(struct ub_bus_controller *ubc, void *pkt, u16 len); + /** * struct message_ops - message ops and capabilities * @probe_dev: probe ub_entity to init message * @remove_dev: remove ub_entity to uninit message * @sync_request: send message to target ub_entity and wait response * @send: send message to target ub_entity but not wait response + * @response: send response message to target * @sync_enum: send enum message to target ub_entity and wait response * @owner: Driver module providing these ops */ @@ -202,6 +205,8 @@ struct message_ops { u8 code); int (*send)(struct message_device *mdev, struct msg_info *info, u8 code); + int (*response)(struct message_device *mdev, struct msg_info *info, + u8 code); int (*sync_enum)(struct message_device *mdev, struct msg_info *info, u8 cmd); struct module *owner; @@ -256,7 +261,21 @@ int message_sync_request(struct message_device *mdev, struct msg_info *info, u8 code); int message_send(struct message_device *mdev, struct msg_info *info, u8 code); +int message_response(struct message_device *mdev, struct msg_info *info, + u8 code); int message_sync_enum(struct message_device *mdev, struct msg_info *info, u8 cmd); +struct ub_rx_msg_task { + struct ub_bus_controller *ubc; + void *pkt; + u16 len; + struct work_struct work; +}; + +struct workqueue_struct *get_rx_msg_wq(u8 msg_code); +int message_rx_handler(struct ub_bus_controller *ubc, void *pkt, u16 len); +int message_rx_init(void); +void message_rx_uninit(void); + #endif /* __MSG_H__ */ -- Gitee From 725781a21e1fd33cbeed45de90ae41508654b3a2 Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Mon, 22 Sep 2025 16:15:03 +0800 Subject: [PATCH 054/214] ub:ubus: Support for ub device resource space registration ANBZ: #45460 commit 389102091260b65d3df5ea5a1edc4f0399b14510 openEuler. register ub device mmio space to ub decoder Signed-off-by: Yuhao Xiang Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/instance.c | 4 ++ drivers/ub/ubus/resource.c | 94 ++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/resource.h | 2 + 3 files changed, 100 insertions(+) diff --git a/drivers/ub/ubus/instance.c b/drivers/ub/ubus/instance.c index 603cf517fad9..315af7f10c09 100644 --- a/drivers/ub/ubus/instance.c +++ b/drivers/ub/ubus/instance.c @@ -10,6 +10,7 @@ #include "ubus.h" #include "eid.h" +#include "resource.h" #include "eu.h" #include "ubus_driver.h" #include "instance.h" @@ -775,6 +776,8 @@ static int ub_bind_bus_instance(struct ub_entity *uent, struct ub_bus_instance * mutex_lock(&bi->lock); list_add_tail(&uent->instance_node, &bi->uents); mutex_unlock(&bi->lock); + + ub_entity_decoder_map_mmio(uent); return 0; } @@ -789,6 +792,7 @@ static void ub_unbind_bus_instance(struct ub_entity *uent) ub_bus_instance_put(uent->bi); uent->bi = NULL; + ub_entity_decoder_unmap_mmio(uent); } int ub_ioctl_bus_instance_bind(void __user *uptr) diff --git a/drivers/ub/ubus/resource.c b/drivers/ub/ubus/resource.c index ebb1a529af77..b57117e02415 100644 --- a/drivers/ub/ubus/resource.c +++ b/drivers/ub/ubus/resource.c @@ -11,6 +11,8 @@ #include "ubus.h" #include "msg.h" +#include "decoder.h" +#include "omm.h" #include "resource.h" struct query_token_msg_pld_req { @@ -305,6 +307,98 @@ void ub_entity_free_mmio_idx(struct ub_entity *dev, int idx) dev->zone[idx].sa_used = 0; } +static void fill_decoder_map_info(struct ub_entity *uent, int idx, + struct decoder_map_info *info) +{ + info->pa = uent->zone[idx].res.start; + info->uba = uent->zone[idx].region.start; + info->size = uent->zone[idx].region.size; + info->eid_low = uent->eid; + info->eid_high = 0; + info->tpg_num = is_primary(uent) ? uent->cna : uent->pue->cna; + info->order_id = 0; + info->order_type = 0; + info->token_id = uent->token_id; + info->token_value = uent->token_value; + info->upi = uent->bi ? uent->bi->info.upi : 0; + info->src_eid = uent->bi ? uent->bi->info.eid : 0; +} + +static int ub_entity_decoder_map_mmio_idx(struct ub_entity *uent, int idx) +{ + struct decoder_map_info info = {}; + struct ub_decoder *decoder; + int ret; + + fill_decoder_map_info(uent, idx, &info); + decoder = is_primary(uent) ? uent->ubc->decoder : + uent->pue->ubc->decoder; + + ret = ub_decoder_map(decoder, &info); + if (ret) + ub_err(uent, "resource%d decoder map failed.\n", idx); + + info.token_value = 0; + return ret; +} + +static void ub_entity_decoder_unmap_mmio_idx(struct ub_entity *uent, int idx) +{ + struct ub_decoder *decoder; + + if (!uent->zone[idx].sa_used) + return; + + decoder = is_primary(uent) ? uent->ubc->decoder : + uent->pue->ubc->decoder; + if (ub_decoder_unmap(decoder, uent->zone[idx].res.start, + uent->zone[idx].region.size)) + ub_warn(uent, "resource%d decoder unmap failed.\n", idx); +} + +void ub_entity_decoder_unmap_mmio(struct ub_entity *dev) +{ + int i; + + for (i = 0; i < MAX_UB_RES_NUM; i++) + if (dev->zone[i].sa_used && dev->zone[i].init_succ && + dev->zone[i].decoder_mapped) { + ub_entity_decoder_unmap_mmio_idx(dev, i); + dev->zone[i].decoder_mapped = 0; + } +} + +void ub_entity_decoder_map_mmio(struct ub_entity *dev) +{ + int i, ret; + + if (is_ibus_controller(dev)) + return; + + for (i = 0; i < MAX_UB_RES_NUM; i++) { + if (!dev->zone[i].sa_used || !dev->zone[i].init_succ) + continue; + if (dev->zone[i].decoder_mapped) { + ub_err(dev, "mmio idx[%d] has been mapped\n", i); + continue; + } + ret = ub_entity_decoder_map_mmio_idx(dev, i); + if (ret) { + ub_err(dev, "failed to establish ommu map, mmio_idx=%d, size=%#lx, error_code=%d\n", + i, dev->zone[i].region.size, ret); + goto fail; + } + dev->zone[i].decoder_mapped = 1; + } + + return; +fail: + for (i -= 1; i >= 0; i--) { + ub_entity_decoder_unmap_mmio_idx(dev, i); + dev->zone[i].decoder_mapped = 0; + } +} + static int ub_query_token_rsp_handle(struct ub_entity *uent, struct query_token_msg_pkt *pkt) { diff --git a/drivers/ub/ubus/resource.h b/drivers/ub/ubus/resource.h index 90da3c402dc0..da032502abe8 100644 --- a/drivers/ub/ubus/resource.h +++ b/drivers/ub/ubus/resource.h @@ -12,6 +12,8 @@ int ub_entity_setup_mmio(struct ub_entity *dev); void ub_entity_unset_mmio(struct ub_entity *dev); int ub_mmap_resource_range(struct ub_entity *uent, unsigned long idx, struct vm_area_struct *vma, int write_combine); +void ub_entity_decoder_map_mmio(struct ub_entity *dev); +void ub_entity_decoder_unmap_mmio(struct ub_entity *uent); int ub_insert_resource(struct ub_entity *dev, int idx); void ub_entity_free_mmio_idx(struct ub_entity *dev, int idx); -- Gitee From b0051b76d214036d178b3a7547a21e57b44bc7ae Mon Sep 17 00:00:00 2001 From: Lizhi He Date: Tue, 9 Sep 2025 13:22:31 +0800 Subject: [PATCH 055/214] drivers/perf: hisi: Implement ummu-pmu driver. ANBZ: #45460 commit 174f904d8b13360585da54538f4403eaa29392f1 openEuler. UMMU-PMU is a performance monitoring unit on the kunpeng ummu Soc. supports collection of performance statistics such as cache hit rate, latency, packet rate. UMMU of each SICL has one PMU device for it. Driver registers each PMU device to perf, and exports information of supported events, filter mode of each event, master_id, token_id, tect_tag and so on via sysfs. Each PMU device has its own registers of control, counters and interrupt, and it supports 8 hardware events, each hardware event has its own registers for configuration, counters. Key filter options contains: master_id - select master_id(packet source) token_id - select token_id(target page table of packets) tect_tag - select tect_tag Signed-off-by: LiuBin Shu Signed-off-by: Lizhi He Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- .../L1-RECOMMEND/arm64/CONFIG_UB_UMMU_PMU | 1 + drivers/perf/hisilicon/Kconfig | 10 + drivers/perf/hisilicon/Makefile | 2 + drivers/perf/hisilicon/ummu_pmu.c | 1035 +++++++++++++++++ 4 files changed, 1048 insertions(+) create mode 100644 anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UMMU_PMU create mode 100644 drivers/perf/hisilicon/ummu_pmu.c diff --git a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UMMU_PMU b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UMMU_PMU new file mode 100644 index 000000000000..f0a68ff59477 --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UMMU_PMU @@ -0,0 +1 @@ +CONFIG_UB_UMMU_PMU=m diff --git a/drivers/perf/hisilicon/Kconfig b/drivers/perf/hisilicon/Kconfig index 171bfc1b6bc2..b10c18dc1de7 100644 --- a/drivers/perf/hisilicon/Kconfig +++ b/drivers/perf/hisilicon/Kconfig @@ -24,3 +24,13 @@ config HNS3_PMU devices. Adds the HNS3 PMU into perf events system for monitoring latency, bandwidth etc. + +config UB_UMMU_PMU + tristate "HiSilicon UMMU PMU Support" + depends on ARM64 + default n + help + Provide support for hisilicon UMMU performance monitoring unit (PMU) + devices. + Adds the UMMU PMU into perf events system for cache hit rate, latency, + package rate etc. diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile index 186be3d02238..8cd87883cfba 100644 --- a/drivers/perf/hisilicon/Makefile +++ b/drivers/perf/hisilicon/Makefile @@ -6,3 +6,5 @@ obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c_pmu.o \ obj-$(CONFIG_HISI_PCIE_PMU) += hisi_pcie_pmu.o obj-$(CONFIG_HNS3_PMU) += hns3_pmu.o +obj-$(CONFIG_UB_UMMU_PMU) += ummu-pmu.o +ummu-pmu-y := ummu_pmu.o diff --git a/drivers/perf/hisilicon/ummu_pmu.c b/drivers/perf/hisilicon/ummu_pmu.c new file mode 100644 index 000000000000..420855a9575d --- /dev/null +++ b/drivers/perf/hisilicon/ummu_pmu.c @@ -0,0 +1,1035 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * Description: This driver adds support for perf events to use the Performance + * Monitor Counter Groups (PMCG) associated with an UMMU node to monitor that node. + */ + +#include +#include +#include +#include +#include + +#define UMMU_PMCG_MAX_COUNTERS 8 + +struct ummu_pmu { + struct pmu pmu; + struct hlist_node node; + struct perf_event *events[UMMU_PMCG_MAX_COUNTERS]; + DECLARE_BITMAP(used_counters, UMMU_PMCG_MAX_COUNTERS); + uint32_t irq; + uint32_t on_cpu; + uint32_t num_counters; + struct device *dev; + void __iomem *reg_base; +}; + +/* registers offset address */ +#define UMMU_PMCG_GLB_CTRL 0x0 +#define UMMU_PMCG_GLB_CTRL_COMMON_CLR BIT(4) +#define UMMU_PMCG_GLB_CTRL_COMMON_ENABLE BIT(0) +#define UMMU_PMCG_OVF_USI_ADDR0 0x08 +#define UMMU_PMCG_OVF_USI_ADDR1 0x0C +#define UMMU_PMCG_OVF_USI_ADDR_MASK GENMASK_ULL(51, 2) +#define UMMU_PMCG_OVF_USI_DATA 0x10 +#define UMMU_PMCG_OVF_USI_ATTR 0x14 + +#define UMMU_PMCG_INT_EN 0x18 +#define UMMU_PMCG_INT_OVF_EN BIT(0) +#define UMMU_PMCG_USI_MPAM 0x1C +#define UMMU_PMCG_USI_MPAM_NS BIT(24) +#define UMMU_PMCG_USI_MPAM_PMG GENMASK(23, 16) +#define UMMU_PMCG_USI_MPAM_PARTID GENMASK(15, 0) +#define UMMU_PMCG_USI_MPAM_PMG_MAX 0x03 +#define UMMU_PMCG_USI_MPAM_PARTID_MAX 0x0FF +#define UMMU_PMCG_USI_IDX 0x20 +#define UMMU_PMCG_ICG_EN 0x24 +#define UMMU_PMCG_ICG_EN_ICG BIT(0) + +#define UMMU_PMCG_COM_CTRL(n) (0x0 + (n)*0x40) +#define UMMU_PMCG_COM_CTRL_EVENT_CLR BIT(24) +#define UMMU_PMCG_COM_CTRL_EVENT_EN BIT(16) +#define UMMU_PMCG_COM_CTRL_EVENT_CODE GENMASK(7, 0) +#define UMMU_PMCG_OVF_INT_STS(n) (0x08 + (n)*0x40) +#define UMMU_PMCG_CNT1_OVERFLOW_INT_STS BIT(1) +#define UMMU_PMCG_CNT0_OVERFLOW_INT_STS BIT(0) +#define UMMU_PMCG_OVF_INT_MSK(n) (0x0C + (n)*0x40) +#define UMMU_PMCG_CNT1_OVERFLOW_INT_MSK BIT(1) +#define UMMU_PMCG_CNT0_OVERFLOW_INT_MSK BIT(0) +#define UMMU_PMCG_FILT_COND_L(n) (0x10 + (n)*0x40) +#define UMMU_PMCG_FILT_COND_L_MASTER_ID GENMASK(31, 20) +#define UMMU_PMCG_FILT_COND_L_TOKEN_ID GENMASK(19, 0) +#define UMMU_PMCG_FILT_COND_H(n) (0x14 + (n)*0x40) +#define UMMU_PMCG_FILT_COND_H_USER_DEF_ID GENMASK(31, 16) +#define UMMU_PMCG_FILT_COND_H_TECT_TAG GENMASK(15, 0) +#define UMMU_PMCG_FILT_MSK_L(n) (0x18 + (n)*0x40) +#define UMMU_PMCG_FILT_MSK_L_MASTER_ID GENMASK(31, 20) +#define UMMU_PMCG_FILT_MSK_L_TOKEN_ID GENMASK(19, 0) +#define UMMU_PMCG_FILT_MSK_H(n) (0x1C + (n)*0x40) +#define UMMU_PMCG_FILT_MSK_H_USER_DEF_ID GENMASK(31, 16) +#define UMMU_PMCG_FILT_MSK_H_TECT_TAG GENMASK(15, 0) +#define UMMU_PMCG_COM_CNT0_L(n) (0x20 + (n)*0x40) +#define UMMU_PMCG_COM_CNT0_H(n) (0x24 + (n)*0x40) +#define UMMU_PMCG_COM_CNT1_L(n) (0x28 + (n)*0x40) +#define UMMU_PMCG_COM_CNT1_H(n) (0x2C + (n)*0x40) + +/* define support event numbers. */ +#define UMMU_TCU_PPTW_REQ_NUM 0x00 +#define UMMU_TCU_CNTX_CACHE_MISS_NUM 0x01 +#define UMMU_TBU_TLB_CACHE_HIT_RATE 0x20 +#define UMMU_TBU_PLB_CACHE_HIT_RATE 0x21 +#define UMMU_TCU_TPTW_CACHE_HIT_RATE 0x22 +#define UMMU_TCU_PPTW_CACHE_HIT_RATE 0x23 +#define UMMU_REQ_RATE 0x4B +#define UMMU_RSP_RATE 0x4E +#define UMMU_REQ_AVERAGE_LATENCY 0x6B +#define UMMU_KV_TABLE_RD_AVERAGE_LATENCY 0x6F +#define UMMU_SWIF_CMD_SEND_NUM 0x04 +#define UMMU_SWIF_DVM_SYNC_LATENCY 0x64 +#define UMMU_SWIF_KCMD_S_SYNC_LATENCY 0x65 +#define UMMU_SWIF_KCMD_NS_SYNC_LATENCY 0x66 +#define UMMU_SWIF_UCMD_SYNC_LATENCY 0x67 +#define UMMU_UBIF_KV_CACHE_HIT_RATE 0x2F +#define UMMU_TCU_GPC_CACHE_HIT_RATE 0x2A +#define UMMU_TCU_TPTW_REQ_LATENCY 0x68 +#define UMMU_TCU_PPTW_REQ_LATENCY 0x69 +#define UMMU_TCU_GPC_REQ_LATENCY 0x6A +#define UMMU_TBU_PTW_PACK_RATE 0x4C +#define UMMU_TBU_PPTW_PACK_RATE 0x4D +#define UMMU_TBU_PTW_LATENCY 0x6C +#define UMMU_TBU_PPTW_LATENCY 0x6D +#define UMMU_TBU_RAB_BUF_USE_RATE 0x8E +#define UMMU_SWIF_KCMD_GPC_SYNC_LATENCY 0x70 +#define UMMU_SWIF_KCMD_REALM_SYNC_LATENCY 0x71 + +#define UMMU_PMCG_CFGR_SIZE 32 +#define UMMU_EVTYPE_SHIFT 5 +#define UMMU_PMCG_MASTER_ID_SHIFT 20 +#define UMMU_PMCG_USER_DEF_ID_SHIFT 16 +#define UMMU_PMCG_MASTER_ID_MSK_SHIFT 20 +#define UMMU_PMCG_USER_DEF_ID_MSK_SHIFT 16 +#define UMMU_PMCG_PA_SHIFT 12 +#define UMMU_USI_MEMATTR_DEVICE_nGnRE 0x01 +#define UMMU_COUNTER_LEN 31 + +#define UMMU_PMU_MULTIPLES_RATE 100 + +#define UMMU_PMU_DRV_NAME "ummu_pmu" + +#define to_ummu_pmu(p) (container_of(p, struct ummu_pmu, pmu)) + +#define UMMU_PMU_EVENT_ATTR_EXTRACTOR(_name, _config, _start, _end) \ + static inline uint32_t get_##_name(struct perf_event *event) \ + { \ + return FIELD_GET(GENMASK_ULL(_end, _start), \ + event->attr._config); \ + } + +UMMU_PMU_EVENT_ATTR_EXTRACTOR(event, config, 0, 7); +UMMU_PMU_EVENT_ATTR_EXTRACTOR(token_id, config1, 0, 19); +UMMU_PMU_EVENT_ATTR_EXTRACTOR(master_id, config1, 20, 31); +UMMU_PMU_EVENT_ATTR_EXTRACTOR(tect_tag, config1, 32, 47); +UMMU_PMU_EVENT_ATTR_EXTRACTOR(user_define_id, config1, 48, 63); +UMMU_PMU_EVENT_ATTR_EXTRACTOR(token_id_flt_msk, config2, 0, 19); +UMMU_PMU_EVENT_ATTR_EXTRACTOR(master_id_flt_msk, config2, 20, 31); +UMMU_PMU_EVENT_ATTR_EXTRACTOR(tect_tag_flt_msk, config2, 32, 47); +UMMU_PMU_EVENT_ATTR_EXTRACTOR(user_define_id_flt_msk, config2, 48, 63); + +enum ummu_pmu_event_type { + UMMU_PMU_EVENT_TYPE_NUM = 0x00, + UMMU_PMU_EVENT_TYPE_HIT_RATE = 0x01, + UMMU_PMU_EVENT_TYPE_FREQ = 0x02, + UMMU_PMU_EVENT_TYPE_AVER_LATENCY = 0x03, + UMMU_PMU_EVENT_TYPE_BUF_OCCUPY_RATE = 0x04, +}; + +static atomic_t ummu_pmu_index = ATOMIC_INIT(0); +static int ummu_pmu_cpuhp_state; + +/* events attributes */ +static ssize_t ummu_pmu_events_sysfs_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct perf_pmu_events_attr *ummu_pmu_attr; + + ummu_pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); + + return sysfs_emit(buf, "event=0x%08llx\n", ummu_pmu_attr->id); +} + +#define UMMU_EVENT_ATTR(name, config) \ + PMU_EVENT_ATTR_ID(name, ummu_pmu_events_sysfs_show, config) + +static struct attribute *ummu_pmu_events_attrs[] = { + UMMU_EVENT_ATTR(tcu_pptw_req_num, UMMU_TCU_PPTW_REQ_NUM), + UMMU_EVENT_ATTR(tcu_cntx_cache_miss_num, UMMU_TCU_CNTX_CACHE_MISS_NUM), + UMMU_EVENT_ATTR(tbu_tlb_cache_hit_rate, UMMU_TBU_TLB_CACHE_HIT_RATE), + UMMU_EVENT_ATTR(tbu_plb_cache_hit_rate, UMMU_TBU_PLB_CACHE_HIT_RATE), + UMMU_EVENT_ATTR(tcu_tptw_cache_hit_rate, UMMU_TCU_TPTW_CACHE_HIT_RATE), + UMMU_EVENT_ATTR(tcu_pptw_cache_hit_rate, UMMU_TCU_PPTW_CACHE_HIT_RATE), + UMMU_EVENT_ATTR(ummu_req_rate, UMMU_REQ_RATE), + UMMU_EVENT_ATTR(ummu_rsp_rate, UMMU_RSP_RATE), + UMMU_EVENT_ATTR(ummu_req_average_latency, UMMU_REQ_AVERAGE_LATENCY), + UMMU_EVENT_ATTR(kv_table_rd_average_latency, UMMU_KV_TABLE_RD_AVERAGE_LATENCY), + UMMU_EVENT_ATTR(swif_cmd_send_num, UMMU_SWIF_CMD_SEND_NUM), + UMMU_EVENT_ATTR(swif_dvm_sync_latency, UMMU_SWIF_DVM_SYNC_LATENCY), + UMMU_EVENT_ATTR(swif_kcmd_s_sync_latency, UMMU_SWIF_KCMD_S_SYNC_LATENCY), + UMMU_EVENT_ATTR(swif_kcmd_ns_sync_latency, UMMU_SWIF_KCMD_NS_SYNC_LATENCY), + UMMU_EVENT_ATTR(swif_ucmd_sync_latency, UMMU_SWIF_UCMD_SYNC_LATENCY), + UMMU_EVENT_ATTR(ubif_kv_cache_hit_rate, UMMU_UBIF_KV_CACHE_HIT_RATE), + UMMU_EVENT_ATTR(tcu_gpc_cache_hit_rate, UMMU_TCU_GPC_CACHE_HIT_RATE), + UMMU_EVENT_ATTR(tcu_tptw_req_latency, UMMU_TCU_TPTW_REQ_LATENCY), + UMMU_EVENT_ATTR(tcu_pptw_req_latency, UMMU_TCU_PPTW_REQ_LATENCY), + UMMU_EVENT_ATTR(tcu_gpc_req_latency, UMMU_TCU_GPC_REQ_LATENCY), + UMMU_EVENT_ATTR(tbu_ptw_pack_rate, UMMU_TBU_PTW_PACK_RATE), + UMMU_EVENT_ATTR(tbu_pptw_pack_rate, UMMU_TBU_PPTW_PACK_RATE), + UMMU_EVENT_ATTR(tbu_ptw_latency, UMMU_TBU_PTW_LATENCY), + UMMU_EVENT_ATTR(tbu_pptw_latency, UMMU_TBU_PPTW_LATENCY), + UMMU_EVENT_ATTR(tbu_rab_buf_use_rate, UMMU_TBU_RAB_BUF_USE_RATE), + UMMU_EVENT_ATTR(swif_kcmd_gpc_sync_latency, UMMU_SWIF_KCMD_GPC_SYNC_LATENCY), + UMMU_EVENT_ATTR(swif_kcmd_realm_sync_latency, UMMU_SWIF_KCMD_REALM_SYNC_LATENCY), + NULL +}; + +static const struct attribute_group ummu_pmu_events_attr_group = { + .name = "events", + .attrs = ummu_pmu_events_attrs +}; + +#define PRE_CNT1_MAX_IDX (ARRAY_SIZE(ummu_pmu_events_attrs) - 1) +static uint32_t pre_count1_idx[UMMU_PMCG_MAX_COUNTERS]; +static local64_t pre_count1[ARRAY_SIZE(ummu_pmu_events_attrs)]; +#define PRE_CNT1_ADDR(cnt_idx) (&pre_count1[pre_count1_idx[cnt_idx]]) + +static uint32_t get_local_pre_count_idx(uint32_t event_id) +{ + struct perf_pmu_events_attr *pmu_attr; + uint32_t idx; + + for (idx = 0; idx < ARRAY_SIZE(ummu_pmu_events_attrs) && + ummu_pmu_events_attrs[idx]; idx++) { + pmu_attr = container_of(container_of(ummu_pmu_events_attrs[idx], + struct device_attribute, + attr), + struct perf_pmu_events_attr, attr); + + if (pmu_attr && pmu_attr->id == event_id) + return idx; + } + + return PRE_CNT1_MAX_IDX; +} + +static irqreturn_t ummu_pmu_handle_irq(int irq, void *data) +{ + struct ummu_pmu *ummu_pmu = (struct ummu_pmu *)data; + u32 overflow; + int idx; + + /* + * Find the counter index which overflowed if the bit was set and handle it. + */ + for (idx = 0; idx < UMMU_PMCG_MAX_COUNTERS; idx++) { + overflow = readl_relaxed(ummu_pmu->reg_base + UMMU_PMCG_OVF_INT_STS(idx + 1)); + /* + * As each counter will restart from 0 when it is overflowed, + * extra processing is no need, just clear interrupt status. + */ + if (overflow) + writel_relaxed(0, ummu_pmu->reg_base + UMMU_PMCG_OVF_INT_STS(idx + 1)); + } + + return IRQ_HANDLED; +} + +static void ummu_pmu_free_msis(void *data) +{ + struct device *dev = (struct device *)data; + + platform_device_msi_free_irqs_all(dev); +} + +static void ummu_pmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) +{ + struct ummu_pmu *ummu_pmu; + phys_addr_t doorbell; + struct device *dev; + + dev = msi_desc_to_dev(desc); + ummu_pmu = (struct ummu_pmu *)dev_get_drvdata(dev); + /* 32-bit addresses are converted to 64-bit addresses. */ + doorbell = (((u64)msg->address_hi) << 32) | msg->address_lo; + doorbell &= UMMU_PMCG_OVF_USI_ADDR_MASK; + + writeq_relaxed(doorbell, ummu_pmu->reg_base + UMMU_PMCG_OVF_USI_ADDR0); + writel_relaxed(msg->data, ummu_pmu->reg_base + UMMU_PMCG_OVF_USI_DATA); + writel_relaxed(UMMU_USI_MEMATTR_DEVICE_nGnRE, ummu_pmu->reg_base + UMMU_PMCG_OVF_USI_ATTR); +} + +static int ummu_pmu_setup_irq(struct ummu_pmu *ummu_pmu) +{ + unsigned long flags = IRQF_NOBALANCING | IRQF_SHARED | IRQF_NO_THREAD; + struct device *dev = ummu_pmu->dev; + uint32_t val = UMMU_PMCG_INT_OVF_EN; + int ret; + uint32_t irq; + + /* Clear MSI address reg */ + writeq_relaxed(0, ummu_pmu->reg_base + UMMU_PMCG_OVF_USI_ADDR0); + + writel_relaxed(val, ummu_pmu->reg_base + UMMU_PMCG_INT_EN); + ret = platform_device_msi_init_and_alloc_irqs(dev, 1, ummu_pmu_write_msi_msg); + if (ret) { + dev_warn(dev, "failed to allocate MSIs, ret = %d\n", ret); + return ret; + } + + irq = msi_get_virq(dev, 0); + if (!irq) { + dev_err(ummu_pmu->dev, "msi get irq failed, irq = %u\n", irq); + platform_device_msi_free_irqs_all(dev); + return -ENXIO; + } + ummu_pmu->irq = irq; + + /* Add callback to free MSIs on teardown */ + ret = devm_add_action_or_reset(dev, ummu_pmu_free_msis, dev); + if (ret) { + dev_err(dev, "failed to add free msis action (%d).\n", ret); + return ret; + } + + return devm_request_irq(ummu_pmu->dev, irq, ummu_pmu_handle_irq, flags, + dev_name(ummu_pmu->dev), ummu_pmu); +} + +static int ummu_pmu_reset(struct ummu_pmu *ummu_pmu) +{ + uint32_t val; + int ret; + + val = readl_relaxed(ummu_pmu->reg_base + UMMU_PMCG_GLB_CTRL); + val |= UMMU_PMCG_GLB_CTRL_COMMON_CLR; + writel_relaxed(val, ummu_pmu->reg_base + UMMU_PMCG_GLB_CTRL); + + val &= ~UMMU_PMCG_GLB_CTRL_COMMON_CLR; + writel_relaxed(val, ummu_pmu->reg_base + UMMU_PMCG_GLB_CTRL); + + ummu_pmu->num_counters = UMMU_PMCG_MAX_COUNTERS; + ret = ummu_pmu_setup_irq(ummu_pmu); + if (ret) { + dev_err(ummu_pmu->dev, "error %d setup irq failed!\n", ret); + return ret; + } + + val |= UMMU_PMCG_ICG_EN_ICG; + writel_relaxed(val, ummu_pmu->reg_base + UMMU_PMCG_ICG_EN); + + /* Pick one CPU to be the preferred one to use */ + ummu_pmu->on_cpu = smp_processor_id(); + ret = cpuhp_state_add_instance_nocalls( + (enum cpuhp_state)ummu_pmu_cpuhp_state, &ummu_pmu->node); + if (ret) + dev_err(ummu_pmu->dev, "error %d registering hotplug!\n", ret); + + return ret; +} + +static void ummu_pmu_enable(struct pmu *pmu) +{ + struct ummu_pmu *ummu_pmu = to_ummu_pmu(pmu); + uint32_t val; + + val = readl_relaxed(ummu_pmu->reg_base + UMMU_PMCG_GLB_CTRL); + val |= UMMU_PMCG_GLB_CTRL_COMMON_ENABLE; + val &= ~UMMU_PMCG_GLB_CTRL_COMMON_CLR; + writel_relaxed(val, ummu_pmu->reg_base + UMMU_PMCG_GLB_CTRL); +} + +static void ummu_pmu_disable(struct pmu *pmu) +{ + struct ummu_pmu *ummu_pmu = to_ummu_pmu(pmu); + uint32_t val; + + val = readl_relaxed(ummu_pmu->reg_base + UMMU_PMCG_GLB_CTRL); + val &= ~UMMU_PMCG_GLB_CTRL_COMMON_ENABLE; + writel_relaxed(val, ummu_pmu->reg_base + UMMU_PMCG_GLB_CTRL); +} + +static void ummu_pmu_counter_get_value(struct ummu_pmu *ummu_pmu, int idx, + uint64_t *cnt0, uint64_t *cnt1) +{ + uint64_t val0_high, val0_low; + uint64_t val1_high, val1_low; + + val0_low = readl_relaxed(ummu_pmu->reg_base + UMMU_PMCG_COM_CNT0_L(idx + 1)); + val0_high = readl_relaxed(ummu_pmu->reg_base + UMMU_PMCG_COM_CNT0_H(idx + 1)); + *cnt0 = val0_high << UMMU_PMCG_CFGR_SIZE | val0_low; + + val1_low = readl_relaxed(ummu_pmu->reg_base + UMMU_PMCG_COM_CNT1_L(idx + 1)); + val1_high = readl_relaxed(ummu_pmu->reg_base + UMMU_PMCG_COM_CNT1_H(idx + 1)); + *cnt1 = val1_high << UMMU_PMCG_CFGR_SIZE | val1_low; +} + +static void ummu_pmu_counter_set_value(struct ummu_pmu *ummu_pmu, int idx, + uint64_t pre_cnt0, uint64_t pre_cnt1) +{ + uint64_t val0_high, val0_low; + uint64_t val1_high, val1_low; + + val0_low = pre_cnt0 & GENMASK_ULL(UMMU_COUNTER_LEN, 0); + val0_high = pre_cnt0 >> UMMU_PMCG_CFGR_SIZE; + val1_low = pre_cnt1 & GENMASK_ULL(UMMU_COUNTER_LEN, 0); + val1_high = pre_cnt1 >> UMMU_PMCG_CFGR_SIZE; + + writel_relaxed(val0_low, ummu_pmu->reg_base + UMMU_PMCG_COM_CNT0_L(idx + 1)); + writel_relaxed(val0_high, ummu_pmu->reg_base + UMMU_PMCG_COM_CNT0_H(idx + 1)); + writel_relaxed(val1_low, ummu_pmu->reg_base + UMMU_PMCG_COM_CNT1_L(idx + 1)); + writel_relaxed(val1_high, ummu_pmu->reg_base + UMMU_PMCG_COM_CNT1_H(idx + 1)); +} + +static void ummu_pmu_counter_enable(struct ummu_pmu *ummu_pmu, int idx) +{ + uint32_t val; + + val = readl_relaxed(ummu_pmu->reg_base + UMMU_PMCG_COM_CTRL(idx + 1)); + val |= UMMU_PMCG_COM_CTRL_EVENT_EN; + writel_relaxed(val, ummu_pmu->reg_base + UMMU_PMCG_COM_CTRL(idx + 1)); +} + +static void ummu_pmu_counter_disable(struct ummu_pmu *ummu_pmu, int idx) +{ + uint32_t val; + + val = readl_relaxed(ummu_pmu->reg_base + UMMU_PMCG_COM_CTRL(idx + 1)); + val &= ~UMMU_PMCG_COM_CTRL_EVENT_EN; + writel_relaxed(val, ummu_pmu->reg_base + UMMU_PMCG_COM_CTRL(idx + 1)); +} + +static void ummu_pmu_counter_clear_enable(struct ummu_pmu *ummu_pmu, int idx) +{ + uint32_t val; + + val = readl_relaxed(ummu_pmu->reg_base + UMMU_PMCG_COM_CTRL(idx + 1)); + val |= UMMU_PMCG_COM_CTRL_EVENT_CLR; + writel_relaxed(val, ummu_pmu->reg_base + UMMU_PMCG_COM_CTRL(idx + 1)); +} + +static void ummu_pmu_counter_clear_disable(struct ummu_pmu *ummu_pmu, int idx) +{ + uint32_t val; + + val = readl_relaxed(ummu_pmu->reg_base + UMMU_PMCG_COM_CTRL(idx + 1)); + val &= ~UMMU_PMCG_COM_CTRL_EVENT_CLR; + writel_relaxed(val, ummu_pmu->reg_base + UMMU_PMCG_COM_CTRL(idx + 1)); +} + +static bool ummu_pmu_validate_event_count(struct perf_event *event) +{ + struct ummu_pmu *ummu_pmu = to_ummu_pmu(event->pmu); + struct perf_cpu_pmu_context *cpc; + + cpc = per_cpu_ptr(event->pmu->cpu_pmu_context, ummu_pmu->on_cpu); + if (!cpc) { + dev_err(ummu_pmu->dev, "cpc is null.\n"); + return false; + } + + /* The events count must less than the counters in the HW */ + return cpc->epc.nr_events < ummu_pmu->num_counters; +} + +static int ummu_pmu_event_init(struct perf_event *event) +{ + struct ummu_pmu *ummu_pmu = to_ummu_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + struct device *dev = ummu_pmu->dev; + uint32_t pre_cnt1_idx; + + if (event->attr.type != event->pmu->type) + return -ENOENT; + + if (hwc->sample_period) { + dev_err(dev, "sampling not supported!\n"); + return -EOPNOTSUPP; + } + + if (event->cpu < 0) { + dev_err(dev, "per-task mode not supported!\n"); + return -EOPNOTSUPP; + } + + if (event->attach_state & PERF_ATTACH_TASK) { + dev_err(dev, "per-task counter cannot allocate!\n"); + return -EOPNOTSUPP; + } + + /* + * Validate if the events in group does not exceed the + * available counters in hardware. + */ + if (!ummu_pmu_validate_event_count(event)) { + dev_err(dev, "event count exceeds the available counters!\n"); + return -EINVAL; + } + + /* Clear the allocated counter */ + hwc->idx = -1; + + /* + * Ensure all events are on the same cpu so all events are in the + * same cpu context, to avoid races on pmu_enable etc. + */ + event->cpu = ummu_pmu->on_cpu; + + pre_cnt1_idx = get_local_pre_count_idx(get_event(event)); + if (pre_cnt1_idx >= PRE_CNT1_MAX_IDX) { + dev_err(dev, "pre counter1 index invalid, event maybe wrong.\n"); + return -EINVAL; + } + + local64_set(&hwc->prev_count, 0); + local64_set(&pre_count1[pre_cnt1_idx], 0); + + return 0; +} + +static void ummu_pmu_config_event_type(struct ummu_pmu *ummu_pmu, + struct perf_event *event, int idx) +{ + uint32_t type = get_event(event) & UMMU_PMCG_COM_CTRL_EVENT_CODE; + uint32_t val; + + val = readl_relaxed(ummu_pmu->reg_base + UMMU_PMCG_COM_CTRL(idx + 1)); + val &= ~UMMU_PMCG_COM_CTRL_EVENT_CODE; + val |= type; + writel_relaxed(val, ummu_pmu->reg_base + UMMU_PMCG_COM_CTRL(idx + 1)); +} + +static void ummu_pmu_config_event_filter(struct ummu_pmu *ummu_pmu, + struct perf_event *event, int idx) +{ + uint32_t user_def_id = get_user_define_id(event); + uint32_t master_id = get_master_id(event); + uint32_t tect_tag = get_tect_tag(event); + uint32_t tid = get_token_id(event); + uint32_t val; + + val = readl_relaxed(ummu_pmu->reg_base + UMMU_PMCG_FILT_COND_L(idx + 1)); + if (tid) + val |= tid; + if (master_id) + val |= master_id << UMMU_PMCG_MASTER_ID_SHIFT; + + writel_relaxed(val, ummu_pmu->reg_base + UMMU_PMCG_FILT_COND_L(idx + 1)); + + val = readl_relaxed(ummu_pmu->reg_base + UMMU_PMCG_FILT_COND_H(idx + 1)); + if (tect_tag) + val |= tect_tag; + if (user_def_id) + val |= user_def_id << UMMU_PMCG_USER_DEF_ID_SHIFT; + + writel_relaxed(val, ummu_pmu->reg_base + UMMU_PMCG_FILT_COND_H(idx + 1)); +} + +static void ummu_pmu_config_event_filter_mask(struct ummu_pmu *ummu_pmu, + struct perf_event *event, int idx) +{ + uint32_t val, tid_msk, master_id_msk, tect_tag_msk, user_def_id_msk; + + tid_msk = get_token_id_flt_msk(event); + master_id_msk = get_master_id_flt_msk(event); + tect_tag_msk = get_tect_tag_flt_msk(event); + user_def_id_msk = get_user_define_id_flt_msk(event); + + val = readl_relaxed(ummu_pmu->reg_base + UMMU_PMCG_FILT_MSK_L(idx + 1)); + + if (tid_msk) { + val &= ~UMMU_PMCG_FILT_MSK_L_TOKEN_ID; + val |= tid_msk; + } + if (master_id_msk) { + val &= ~UMMU_PMCG_FILT_MSK_L_MASTER_ID; + val |= master_id_msk << UMMU_PMCG_MASTER_ID_MSK_SHIFT; + } + + writel_relaxed(val, ummu_pmu->reg_base + UMMU_PMCG_FILT_MSK_L(idx + 1)); + + val = readl_relaxed(ummu_pmu->reg_base + UMMU_PMCG_FILT_MSK_H(idx + 1)); + + if (tect_tag_msk) { + val &= ~UMMU_PMCG_FILT_COND_H_TECT_TAG; + val |= tect_tag_msk; + } + if (user_def_id_msk) { + val &= ~UMMU_PMCG_FILT_COND_H_USER_DEF_ID; + val |= user_def_id_msk << UMMU_PMCG_USER_DEF_ID_MSK_SHIFT; + } + + writel_relaxed(val, ummu_pmu->reg_base + UMMU_PMCG_FILT_MSK_H(idx + 1)); +} + +static void ummu_pmu_config_event(struct ummu_pmu *ummu_pmu, + struct perf_event *event, int idx) +{ + ummu_pmu_config_event_type(ummu_pmu, event, idx); + ummu_pmu_config_event_filter(ummu_pmu, event, idx); + ummu_pmu_config_event_filter_mask(ummu_pmu, event, idx); +} + +static void ummu_pmu_config_overflow_int_mask(struct ummu_pmu *ummu_pmu, + struct perf_event *event, + int idx) +{ + uint32_t val; + + val = readl_relaxed(ummu_pmu->reg_base + UMMU_PMCG_OVF_INT_MSK(idx + 1)); + val &= ~UMMU_PMCG_CNT0_OVERFLOW_INT_MSK; + val &= ~UMMU_PMCG_CNT1_OVERFLOW_INT_MSK; + writel_relaxed(val, ummu_pmu->reg_base + UMMU_PMCG_OVF_INT_MSK(idx + 1)); +} + +static void ummu_pmu_event_start(struct perf_event *event, int flags) +{ + struct ummu_pmu *ummu_pmu = to_ummu_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + uint64_t prev_count0, prev_count1; + int idx = hwc->idx; + + if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED))) + return; + + WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE)); + hwc->state = 0; + + ummu_pmu_config_event(ummu_pmu, event, idx); + + if (flags & PERF_EF_RELOAD) { + prev_count0 = (uint64_t)local64_read(&hwc->prev_count); + prev_count1 = (uint64_t)local64_read(PRE_CNT1_ADDR(idx)); + ummu_pmu_counter_set_value(ummu_pmu, idx, prev_count0, prev_count1); + } + + ummu_pmu_counter_enable(ummu_pmu, idx); + ummu_pmu_config_overflow_int_mask(ummu_pmu, event, idx); + /* Propagate changes to the userspace mapping. */ + perf_event_update_userpage(event); +} + +static uint64_t ummu_pmu_calculate(uint8_t event_id, uint64_t cnt0, + uint64_t cnt1) +{ + uint8_t event_type = event_id >> UMMU_EVTYPE_SHIFT; + uint64_t result; + + switch (event_type) { + case UMMU_PMU_EVENT_TYPE_NUM: + result = cnt0; + break; + case UMMU_PMU_EVENT_TYPE_HIT_RATE: + case UMMU_PMU_EVENT_TYPE_FREQ: + case UMMU_PMU_EVENT_TYPE_AVER_LATENCY: + case UMMU_PMU_EVENT_TYPE_BUF_OCCUPY_RATE: + if (!cnt0 || !cnt1) + return 0; + result = cnt0 * UMMU_PMU_MULTIPLES_RATE / cnt1; + break; + default: + result = 0; + break; + } + + return result; +} + +static void ummu_pmu_event_update(struct perf_event *event) +{ + struct ummu_pmu *ummu_pmu = to_ummu_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + uint64_t new_cnt0, prev_cnt0, new_cnt1; + int idx = hwc->idx; + + do { + prev_cnt0 = local64_read(&hwc->prev_count); + ummu_pmu_counter_get_value(ummu_pmu, idx, &new_cnt0, &new_cnt1); + } while (local64_cmpxchg(&hwc->prev_count, prev_cnt0, new_cnt0) != + prev_cnt0); + + local64_set(PRE_CNT1_ADDR(idx), new_cnt1); + + local64_set(&event->count, + ummu_pmu_calculate(get_event(event), new_cnt0, new_cnt1)); +} + +static void ummu_pmu_reset_config_event_type(struct ummu_pmu *ummu_pmu, + struct perf_event *event, int idx) +{ + uint32_t val; + + val = readl_relaxed(ummu_pmu->reg_base + UMMU_PMCG_COM_CTRL(idx + 1)); + val &= ~UMMU_PMCG_COM_CTRL_EVENT_CODE; + writel_relaxed(val, ummu_pmu->reg_base + UMMU_PMCG_COM_CTRL(idx + 1)); +} + +static void ummu_pmu_reset_config_event_filter(struct ummu_pmu *ummu_pmu, + struct perf_event *event, + int idx) +{ + /* register default value equals to 0x0 */ + writel_relaxed(0, ummu_pmu->reg_base + UMMU_PMCG_FILT_COND_L(idx + 1)); + writel_relaxed(0, ummu_pmu->reg_base + UMMU_PMCG_FILT_COND_H(idx + 1)); +} + +static void ummu_pmu_reset_config_event_filter_mask(struct ummu_pmu *ummu_pmu, + struct perf_event *event, + int idx) +{ + /* register default value equals to 0x0000000000000000 */ + writel_relaxed(0, ummu_pmu->reg_base + UMMU_PMCG_FILT_MSK_L(idx + 1)); + + writel_relaxed(0, ummu_pmu->reg_base + UMMU_PMCG_FILT_MSK_H(idx + 1)); +} + +static void ummu_pmu_reset_config_event(struct ummu_pmu *ummu_pmu, + struct perf_event *event, int idx) +{ + ummu_pmu_reset_config_event_type(ummu_pmu, event, idx); + ummu_pmu_config_overflow_int_mask(ummu_pmu, event, idx); + ummu_pmu_reset_config_event_filter(ummu_pmu, event, idx); + ummu_pmu_reset_config_event_filter_mask(ummu_pmu, event, idx); +} + +static void ummu_pmu_event_stop(struct perf_event *event, int flags) +{ + struct ummu_pmu *ummu_pmu = to_ummu_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + int idx = hwc->idx; + + if (hwc->state & PERF_HES_STOPPED) + return; + + ummu_pmu_counter_disable(ummu_pmu, idx); + ummu_pmu_reset_config_event(ummu_pmu, event, idx); + + /* Read hardware counter and update the perf counter statistics */ + ummu_pmu_event_update(event); + hwc->state = hwc->state | PERF_HES_STOPPED | PERF_HES_UPTODATE; +} + +static int ummu_pmu_get_event_idx(struct ummu_pmu *ummu_pmu, + struct perf_event *event) +{ + uint32_t num_counts = ummu_pmu->num_counters; + int idx; + + idx = find_first_zero_bit(ummu_pmu->used_counters, num_counts); + if (idx == num_counts) + /* The counters are all in use. */ + return -EAGAIN; + + set_bit(idx, ummu_pmu->used_counters); + + return idx; +} + +static void ummu_pmu_clear_counter(struct ummu_pmu *ummu_pmu, int idx) +{ + ummu_pmu_counter_clear_enable(ummu_pmu, idx); + ummu_pmu_counter_clear_disable(ummu_pmu, idx); +} + +static int ummu_pmu_event_add(struct perf_event *event, int flags) +{ + struct ummu_pmu *ummu_pmu = to_ummu_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + int idx; + + /* Get an available counter index for counting */ + idx = ummu_pmu_get_event_idx(ummu_pmu, event); + if (idx < 0 || idx >= UMMU_PMCG_MAX_COUNTERS) + return -EINVAL; + + pre_count1_idx[idx] = get_local_pre_count_idx(get_event(event)); + if (pre_count1_idx[idx] >= PRE_CNT1_MAX_IDX) { + dev_err(ummu_pmu->dev, "pre counter1 [%u] index invalid, event maybe wrong.\n", + get_event(event)); + return -EINVAL; + } + + ummu_pmu_clear_counter(ummu_pmu, idx); + + ummu_pmu->events[idx] = event; + hwc->idx = idx; + hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE; + + if (flags & PERF_EF_START) + ummu_pmu_event_start(event, PERF_EF_RELOAD); + + dev_dbg(ummu_pmu->dev, "pmu event [0x%x] idx [%d] pre_cnt1 idx [%u] add finished.\n", + get_event(event), idx, pre_count1_idx[idx]); + + return 0; +} + +static void ummu_pmu_event_del(struct perf_event *event, int flags) +{ + struct ummu_pmu *ummu_pmu = to_ummu_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + int idx = hwc->idx; + + if (idx < 0) { + dev_err(ummu_pmu->dev, "event no exist!\n"); + return; + } + + ummu_pmu_event_stop(event, flags | PERF_EF_UPDATE); + ummu_pmu->events[idx] = NULL; + pre_count1_idx[idx] = PRE_CNT1_MAX_IDX; + clear_bit(idx, ummu_pmu->used_counters); + + perf_event_update_userpage(event); +} + +static void ummu_pmu_event_read(struct perf_event *event) +{ + ummu_pmu_event_update(event); +} + +/* cpumask attributes */ +static ssize_t ummu_pmu_cpumask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ummu_pmu *ummu_pmu = to_ummu_pmu(dev_get_drvdata(dev)); + + return cpumap_print_to_pagebuf(true, buf, cpumask_of(ummu_pmu->on_cpu)); +} + +static struct device_attribute ummu_pmu_cpumask_attr = + __ATTR(cpumask, 0444, ummu_pmu_cpumask_show, NULL); + +static struct attribute *ummu_pmu_cpumask_attrs[] = { + &ummu_pmu_cpumask_attr.attr, + NULL +}; + +static const struct attribute_group ummu_pmu_cpumask_attr_group = { + .attrs = ummu_pmu_cpumask_attrs +}; + +/* format attributes */ +static ssize_t ummu_pmu_format_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dev_ext_attribute *eattr; + + eattr = container_of(attr, struct dev_ext_attribute, attr); + return sysfs_emit(buf, "%s\n", (char *)eattr->var); +} + +#define UMMU_PMU_FORMAT(_name, _func, _config) \ + (&((struct dev_ext_attribute[]){ \ + { __ATTR(_name, 0444, _func, NULL), _config} })[0] \ + .attr.attr) + +#define UMMU_PMU_FORMAT_ATTR(_name, _format) \ + UMMU_PMU_FORMAT(_name, ummu_pmu_format_show, _format) + +static struct attribute *ummu_pmu_formats_attrs[] = { + UMMU_PMU_FORMAT_ATTR(event, "config:0-7"), + UMMU_PMU_FORMAT_ATTR(token_id, "config1:0-19"), + UMMU_PMU_FORMAT_ATTR(master_id, "config1:20-31"), + UMMU_PMU_FORMAT_ATTR(tect_tag, "config1:32-47"), + UMMU_PMU_FORMAT_ATTR(user_define_id, "config1:48-63"), + UMMU_PMU_FORMAT_ATTR(token_id_flt_msk, "config2:0-19"), + UMMU_PMU_FORMAT_ATTR(master_id_flt_msk, "config2:20-31"), + UMMU_PMU_FORMAT_ATTR(tect_tag_flt_msk, "config2:32-47"), + UMMU_PMU_FORMAT_ATTR(user_define_id_flt_msk, "config2:48-63"), + NULL +}; + +static const struct attribute_group ummu_pmu_format_attr_group = { + .name = "format", + .attrs = ummu_pmu_formats_attrs +}; + +static const struct attribute_group *ummu_pmu_attr_groups[] = { + &ummu_pmu_cpumask_attr_group, + &ummu_pmu_events_attr_group, + &ummu_pmu_format_attr_group, + NULL +}; + +static int ummu_pmu_offline_cpu(uint32_t cpu, struct hlist_node *node) +{ + struct ummu_pmu *ummu_pmu; + uint32_t target; + + ummu_pmu = hlist_entry_safe(node, struct ummu_pmu, node); + if (!ummu_pmu) + return -ENODEV; + + /* Nothing to do if this CPU doesn't own the PMU */ + if (cpu != ummu_pmu->on_cpu) + return 0; + + /* Choose a new CPU from all online cpus */ + target = cpumask_any_but(cpu_online_mask, cpu); + if (target >= nr_cpu_ids) + return 0; + + perf_pmu_migrate_context(&ummu_pmu->pmu, cpu, target); + /* Use this CPU for event counting */ + ummu_pmu->on_cpu = target; + + return 0; +} + +static void ummu_pmu_init_ops(struct ummu_pmu *ummu_pmu, struct module *module) +{ + ummu_pmu->pmu = (struct pmu) { + .module = module, + .attr_groups = ummu_pmu_attr_groups, + .capabilities = PERF_PMU_CAP_NO_EXCLUDE, + .task_ctx_nr = perf_invalid_context, + .pmu_enable = ummu_pmu_enable, + .pmu_disable = ummu_pmu_disable, + .event_init = ummu_pmu_event_init, + .add = ummu_pmu_event_add, + .del = ummu_pmu_event_del, + .start = ummu_pmu_event_start, + .stop = ummu_pmu_event_stop, + .read = ummu_pmu_event_read + }; +} + +static int ummu_pmu_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ummu_pmu *ummu_pmu; + struct resource *res; + char *name; + int ret; + + ummu_pmu = devm_kzalloc(dev, sizeof(*ummu_pmu), GFP_KERNEL); + if (!ummu_pmu) + return dev_err_probe(dev, -ENOMEM, "failed to kzalloc for pmu\n"); + + ummu_pmu->dev = dev; + platform_set_drvdata(pdev, ummu_pmu); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(res == NULL)) + return dev_err_probe(dev, -EINVAL, "IO resource is null\n"); + + ummu_pmu->reg_base = devm_ioremap_resource(dev, res); + if (IS_ERR(ummu_pmu->reg_base)) + return dev_err_probe(dev, PTR_ERR(ummu_pmu->reg_base), "Ioremap failed\n"); + + name = devm_kasprintf(dev, GFP_KERNEL, "ummu_pmcg_%d", + atomic_inc_return(&ummu_pmu_index) - 1); + if (!name) + return dev_err_probe(dev, -ENOMEM, "Create ummu pmu name failed\n"); + + ret = ummu_pmu_reset(ummu_pmu); + if (ret) + return dev_err_probe(dev, ret, "reset pmu failed\n"); + + ummu_pmu_init_ops(ummu_pmu, THIS_MODULE); + ret = perf_pmu_register(&ummu_pmu->pmu, name, -1); + if (ret) { + cpuhp_state_remove_instance_nocalls( + (enum cpuhp_state)ummu_pmu_cpuhp_state, + &ummu_pmu->node); + return dev_err_probe(dev, ret, "registering PMU failed\n"); + } + + return 0; +} + +static int ummu_pmu_remove(struct platform_device *pdev) +{ + struct ummu_pmu *ummu_pmu = platform_get_drvdata(pdev); + + if (!ummu_pmu) { + pr_err("pmu device remove get invalid platform device!\n"); + return -ENODEV; + } + + perf_pmu_unregister(&ummu_pmu->pmu); + cpuhp_state_remove_instance_nocalls( + (enum cpuhp_state)ummu_pmu_cpuhp_state, &ummu_pmu->node); + + return 0; +} + +static void ummu_pmu_shutdown(struct platform_device *pdev) +{ + struct ummu_pmu *ummu_pmu = platform_get_drvdata(pdev); + + if (!ummu_pmu) { + pr_err("pmu device shutdown get invalid platform device!\n"); + return; + } + + ummu_pmu_disable(&ummu_pmu->pmu); +} + +static const struct of_device_id hisi_ummu_pmu_of_match[] = { + { .compatible = "ub,ummu_pmu", }, + { } +}; +MODULE_DEVICE_TABLE(of, hisi_ummu_pmu_of_match); + +static const struct acpi_device_id hisi_ummu_pmu_acpi_match[] = { + {"HISI0571", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, hisi_ummu_pmu_acpi_match); + +static struct platform_driver ummu_pmu_driver = { + .driver = { + .name = UMMU_PMU_DRV_NAME, + .suppress_bind_attrs = true, + .of_match_table = hisi_ummu_pmu_of_match, + .acpi_match_table = hisi_ummu_pmu_acpi_match + }, + .probe = ummu_pmu_probe, + .remove = ummu_pmu_remove, + .shutdown = ummu_pmu_shutdown +}; + +static int __init ummu_pmu_module_init(void) +{ + int ret; + + ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, + "perf/ummu/pmcg:online", NULL, + ummu_pmu_offline_cpu); + if (ret < 0) { + pr_err("ummu pmu: setup hotplug failed, ret = %d\n", ret); + return ret; + } + ummu_pmu_cpuhp_state = ret; + + ret = platform_driver_register(&ummu_pmu_driver); + if (ret) + cpuhp_remove_multi_state( + (enum cpuhp_state)ummu_pmu_cpuhp_state); + + return ret; +} +module_init(ummu_pmu_module_init); + +static void __exit ummu_pmu_module_exit(void) +{ + platform_driver_unregister(&ummu_pmu_driver); + cpuhp_remove_multi_state((enum cpuhp_state)ummu_pmu_cpuhp_state); +} +module_exit(ummu_pmu_module_exit); + +MODULE_DESCRIPTION("PMU driver for UMMU Performance Monitors Extension"); +MODULE_LICENSE("GPL"); +MODULE_SOFTDEP("pre: ubfi"); +MODULE_ALIAS("platform:" UMMU_PMU_DRV_NAME); -- Gitee From 57a6ef635eb332d66537d47cb83d9c1902b872e0 Mon Sep 17 00:00:00 2001 From: Lizhi He Date: Tue, 9 Sep 2025 14:44:43 +0800 Subject: [PATCH 056/214] drivers/perf: hisi: Add the sysfs interface to configure pmu mpam ANBZ: #45460 commit 13f3fb8fcaf0329db411d5cfda70ed7dd113e022 openEuler. Allow users to configure partid and pmg through sysfs, as well as view the configured partid and pmg values. Signed-off-by: Jingbin Wu Signed-off-by: Lizhi He Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/perf/hisilicon/ummu_pmu.c | 154 +++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 1 deletion(-) diff --git a/drivers/perf/hisilicon/ummu_pmu.c b/drivers/perf/hisilicon/ummu_pmu.c index 420855a9575d..04c05921088e 100644 --- a/drivers/perf/hisilicon/ummu_pmu.c +++ b/drivers/perf/hisilicon/ummu_pmu.c @@ -113,6 +113,9 @@ struct ummu_pmu { #define UMMU_USI_MEMATTR_DEVICE_nGnRE 0x01 #define UMMU_COUNTER_LEN 31 +static int g_partid; +static int g_pmg; +#define NUMBER_BASE_DECIMAL 10 #define UMMU_PMU_MULTIPLES_RATE 100 #define UMMU_PMU_DRV_NAME "ummu_pmu" @@ -854,6 +857,23 @@ static const struct attribute_group ummu_pmu_format_attr_group = { .attrs = ummu_pmu_formats_attrs }; +static int ummu_pmu_check_usi_mpam_cap(struct ummu_pmu *pmu, int partid, int pmg) +{ + if (partid < 0 || partid > UMMU_PMCG_USI_MPAM_PARTID_MAX) { + dev_err(pmu->dev, "usi_mpam partid exceeds range: partid[0, %d]\n", + UMMU_PMCG_USI_MPAM_PARTID_MAX); + return -ERANGE; + } + + if (pmg < 0 || pmg > UMMU_PMCG_USI_MPAM_PMG_MAX) { + dev_err(pmu->dev, "usi_mpam pmg exceeds range: pmg[0, %d]\n", + UMMU_PMCG_USI_MPAM_PMG_MAX); + return -ERANGE; + } + + return 0; +} + static const struct attribute_group *ummu_pmu_attr_groups[] = { &ummu_pmu_cpumask_attr_group, &ummu_pmu_events_attr_group, @@ -861,6 +881,39 @@ static const struct attribute_group *ummu_pmu_attr_groups[] = { NULL }; +static int ummu_pmu_set_usi_mpam(struct ummu_pmu *pmu, int partid, int pmg) +{ + uint32_t reg; + int ret; + + if (!pmu) + return -EINVAL; + + ret = ummu_pmu_check_usi_mpam_cap(pmu, partid, pmg); + if (ret) + return ret; + + reg = UMMU_PMCG_USI_MPAM_NS; + reg |= FIELD_PREP(UMMU_PMCG_USI_MPAM_PMG, pmg); + reg |= FIELD_PREP(UMMU_PMCG_USI_MPAM_PARTID, partid); + writel_relaxed(reg, pmu->reg_base + UMMU_PMCG_USI_MPAM); + + return 0; +} + +static int ummu_pmu_get_usi_mpam(struct ummu_pmu *pmu, int *partid, int *pmg) +{ + uint32_t reg; + + if (!pmu) + return -EINVAL; + + reg = readl_relaxed(pmu->reg_base + UMMU_PMCG_USI_MPAM); + *partid = FIELD_GET(UMMU_PMCG_USI_MPAM_PARTID, reg); + *pmg = FIELD_GET(UMMU_PMCG_USI_MPAM_PMG, reg); + return 0; +} + static int ummu_pmu_offline_cpu(uint32_t cpu, struct hlist_node *node) { struct ummu_pmu *ummu_pmu; @@ -988,12 +1041,111 @@ static const struct acpi_device_id hisi_ummu_pmu_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, hisi_ummu_pmu_acpi_match); +static ssize_t partid_store(struct device *kobj, struct device_attribute *attr, + const char *buf, size_t count) +{ + int var, ret; + + ret = kstrtoint(buf, NUMBER_BASE_DECIMAL, &var); + if (ret < 0) + return ret; + + g_partid = var; + dev_info(kobj, "ummu mpam input partid = %d.\n", var); + return count; +} + +static ssize_t pmg_store(struct device *kobj, struct device_attribute *attr, + const char *buf, size_t count) +{ + int var, ret; + + ret = kstrtoint(buf, NUMBER_BASE_DECIMAL, &var); + if (ret < 0) + return ret; + + g_pmg = var; + dev_info(kobj, "ummu mpam input pmg = %d.\n", var); + return count; +} + +static int check_input_buf(const char *buf) +{ + int var, ret; + + ret = kstrtoint(buf, NUMBER_BASE_DECIMAL, &var); + if (ret) + return ret; + + if (var != 1) + return -EINVAL; + + return 0; +} + +static ssize_t run_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ummu_pmu *pmu; + int ret; + + ret = check_input_buf(buf); + if (ret) + return ret; + + pmu = (struct ummu_pmu *)dev_get_drvdata(dev); + ret = ummu_pmu_set_usi_mpam(pmu, g_partid, g_pmg); + if (ret) { + dev_err(dev, + "ummu_pmu set usi_mpam(partid[%d], pmg[%d]) failed, ret[%d]\n", + g_partid, g_pmg, ret); + return ret; + } + + return count; +} + +static ssize_t mpam_info_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ummu_pmu *pmu = (struct ummu_pmu *)dev_get_drvdata(dev); + int partid = 0, pmg = 0, ret; + + ret = ummu_pmu_get_usi_mpam(pmu, &partid, &pmg); + if (ret) + return ret; + return sysfs_emit(buf, "partid:%d\npmg:%d\n", partid, pmg); +} + +static DEVICE_ATTR_WO(partid); +static DEVICE_ATTR_WO(pmg); +static DEVICE_ATTR_WO(run); +static DEVICE_ATTR_RO(mpam_info); + +static struct attribute *ummu_pmu_usi_mpam_attrs[] = { + &dev_attr_partid.attr, + &dev_attr_pmg.attr, + &dev_attr_run.attr, + &dev_attr_mpam_info.attr, + NULL +}; + +static const struct attribute_group ummu_pmu_usi_mpam_attr_group = { + .name = "usi_mpam", + .attrs = ummu_pmu_usi_mpam_attrs +}; + +static const struct attribute_group *ummu_pmu_groups[] = { + &ummu_pmu_usi_mpam_attr_group, + NULL +}; + static struct platform_driver ummu_pmu_driver = { .driver = { .name = UMMU_PMU_DRV_NAME, .suppress_bind_attrs = true, .of_match_table = hisi_ummu_pmu_of_match, - .acpi_match_table = hisi_ummu_pmu_acpi_match + .acpi_match_table = hisi_ummu_pmu_acpi_match, + .dev_groups = ummu_pmu_groups }, .probe = ummu_pmu_probe, .remove = ummu_pmu_remove, -- Gitee From 70c815eb1911da9637e9d282bf9cb221271ad65c Mon Sep 17 00:00:00 2001 From: Liming An Date: Tue, 19 Aug 2025 13:31:17 +0800 Subject: [PATCH 057/214] ub: ubfi: Parsing ummu node in the ubrt table ANBZ: #45460 commit 010c6364261c263f01d3d7861a955f3b1c2c9156 openEuler. ubrt can be reported through two methods: ACPI and DTS. Here, the ummu sub-table is parsed from the ubrt table, and then the ummu node information is extracted from the ummu sub-table. Each ummu node contains hardware information for both ummu and pmu. There can be multiple ummu nodes. Finally, the parsed hardware information is configured for the ummu devices and pmu devices. Signed-off-by: Jingbin Wu Signed-off-by: Liming An Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- .../arm64/CONFIG_UB_UBRT_PLAT_DEV | 1 + drivers/ub/ubfi/Kconfig | 12 + drivers/ub/ubfi/Makefile | 4 +- drivers/ub/ubfi/irq.c | 31 ++ drivers/ub/ubfi/resv_mem.c | 91 ++++ drivers/ub/ubfi/ubrt-fwnode.c | 149 ++++++ drivers/ub/ubfi/ubrt.c | 9 + drivers/ub/ubfi/ummu.c | 435 ++++++++++++++++++ drivers/ub/ubfi/ummu.h | 12 + include/ub/ubfi/ubfi.h | 172 +++++++ 10 files changed, 914 insertions(+), 2 deletions(-) create mode 100644 anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UBRT_PLAT_DEV create mode 100644 drivers/ub/ubfi/resv_mem.c create mode 100644 drivers/ub/ubfi/ubrt-fwnode.c create mode 100644 drivers/ub/ubfi/ummu.c create mode 100644 drivers/ub/ubfi/ummu.h diff --git a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UBRT_PLAT_DEV b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UBRT_PLAT_DEV new file mode 100644 index 000000000000..905594d86436 --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UBRT_PLAT_DEV @@ -0,0 +1 @@ +CONFIG_UB_UBRT_PLAT_DEV=y diff --git a/drivers/ub/ubfi/Kconfig b/drivers/ub/ubfi/Kconfig index 4cb8a264fe9a..d3889afb2452 100644 --- a/drivers/ub/ubfi/Kconfig +++ b/drivers/ub/ubfi/Kconfig @@ -15,4 +15,16 @@ config UB_UBFI within Linux. To compile this driver as a module, choose M here. Say 'M' here unless you know what you are doing +config UB_UBRT_PLAT_DEV + bool "Enable UBRT platform device support" + depends on UB_UBUS + default n + help + This option enables the configuration of platform devices related to + the ub ubrt table. + If enabled, the UBRT-related platform device will obtain the + interrupt ID from the ubrt table instead of the IORT table. + The obtained interrupt ID will be used for the MSI interrupt of the + UBRT-related platform device. + endif diff --git a/drivers/ub/ubfi/Makefile b/drivers/ub/ubfi/Makefile index 966355af26fd..1fa9bb71b43d 100644 --- a/drivers/ub/ubfi/Makefile +++ b/drivers/ub/ubfi/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ -obj-y += irq.o +obj-y += irq.o ubrt-fwnode.o -ubfi-y := ub_fi.o ubrt.o ubc.o +ubfi-y := ub_fi.o ubrt.o ubc.o ummu.o resv_mem.o obj-$(CONFIG_UB_UBFI) += ubfi.o diff --git a/drivers/ub/ubfi/irq.c b/drivers/ub/ubfi/irq.c index 3c26ae362893..16b18db3af65 100644 --- a/drivers/ub/ubfi/irq.c +++ b/drivers/ub/ubfi/irq.c @@ -7,6 +7,7 @@ #include #include #include +#include int ubrt_register_gsi(u32 hwirq, int trigger, int polarity, const char *name, struct resource *res) @@ -41,3 +42,33 @@ void ubrt_unregister_gsi(u32 hwirq) #endif } EXPORT_SYMBOL_GPL(ubrt_unregister_gsi); + +#if IS_ENABLED(CONFIG_UB_UBRT_PLAT_DEV) +int ubrt_pmsi_get_interrupt_id(struct device *dev, u32 *interrupt_id) +{ + struct ubrt_fwnode *fw; + struct ummu_node *node; + + if (!dev->fwnode) + return -EINVAL; + + fw = ubrt_fwnode_get(dev->fwnode); + if (!fw) + return -ENODEV; + + switch (fw->type) { + case UBRT_UMMU: + node = (struct ummu_node *)fw->ubrt_node; + *interrupt_id = node->intr_id; + break; + case UBRT_UMMU_PMU: + node = (struct ummu_node *)fw->ubrt_node; + *interrupt_id = node->pmu_intr_id; + break; + default: + return -ENODEV; + } + dev_info(dev, "ubct pmsi successfully obtained interrupt id[0x%x].\n", *interrupt_id); + return 0; +} +#endif diff --git a/drivers/ub/ubfi/resv_mem.c b/drivers/ub/ubfi/resv_mem.c new file mode 100644 index 000000000000..ac9cc0f7da3c --- /dev/null +++ b/drivers/ub/ubfi/resv_mem.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + * Description: + * This file implements the parsing of the ub reserved memory node in the UBRT table. + */ + +#include +#include +#include +#include +#include +#include + +#include "ubrt.h" +#include "ub_fi.h" +#include "ubc.h" + +#define UBRT_RMR_DIRECT_REMAP BIT(0) + +struct ubrt_resv_mem_range { + u8 flags; + u8 reserved[7]; + u64 memory_base; + u64 memory_size; +}; + +struct ubrt_resv_mem_node { + struct ub_table_header header; + u16 count; + u8 reserved[6]; + u8 ranges[]; __counted_by(count) +}; + +static void parse_ummu_reserved_memory(void *info_node, struct list_head *list) +{ + struct ubrt_resv_mem_node *sub_table = info_node; + struct ubrt_resv_mem_range *mem_range; + struct iommu_resv_region *region; + int prot = IOMMU_READ | IOMMU_WRITE; + u32 i; + + if (!sub_table->count) { + pr_warn("resv mem table has no node.\n"); + return; + } + + mem_range = (struct ubrt_resv_mem_range *)sub_table->ranges; + for (i = 0; i < sub_table->count; i++) { + if (!(mem_range[i].flags & UBRT_RMR_DIRECT_REMAP)) + continue; + + prot |= IOMMU_MMIO; + region = iommu_alloc_resv_region(mem_range[i].memory_base, mem_range[i].memory_size, + prot, IOMMU_RESV_DIRECT, GFP_KERNEL); + if (!region) { + pr_err("Failed to allocate resv region for ummu.\n"); + return; + } + + list_add_tail(®ion->list, list); + } +} + +static void reserved_memory_parse(u64 pointer, struct list_head *list) +{ + void *info_node = ub_table_get(pointer); + + if (!info_node) + return; + + parse_ummu_reserved_memory(info_node, list); + ub_table_put(info_node); +} + +void ubrt_iommu_get_resv_regions(struct device *dev, struct list_head *list) +{ + struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); + struct ubrt_sub_table *sub_table; + u32 i; + + if (!fwspec || !acpi_table) + return; + + for (i = 0; i < acpi_table->count; i++) { + sub_table = &acpi_table->sub_table[i]; + if (sub_table->type == UB_RESERVED_MEMORY_TABLE) + reserved_memory_parse(sub_table->pointer, list); + } +} +EXPORT_SYMBOL_GPL(ubrt_iommu_get_resv_regions); diff --git a/drivers/ub/ubfi/ubrt-fwnode.c b/drivers/ub/ubfi/ubrt-fwnode.c new file mode 100644 index 000000000000..0b0b203b916b --- /dev/null +++ b/drivers/ub/ubfi/ubrt-fwnode.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + * Descriptor: Maintain ubrt information acquisition path + */ +#include +#include +#include +#include +#include + +static LIST_HEAD(ubrt_fwnode_list); +static DEFINE_SPINLOCK(ubrt_fwnode_lock); + +int ubrt_fwnode_set(u32 index, enum ubrt_node_type type, struct fwnode_handle *fwnode) +{ + struct ubrt_fwnode *curr; + + if (!fwnode) + return -ENODEV; + + guard(spinlock)(&ubrt_fwnode_lock); + list_for_each_entry(curr, &ubrt_fwnode_list, list) { + if (curr->type == type && curr->index == index) { + curr->fwnode = fwnode; + return 0; + } + } + + return -ENXIO; +} +EXPORT_SYMBOL_GPL(ubrt_fwnode_set); + +struct ubrt_fwnode *ubrt_fwnode_get_by_idx(u32 index, enum ubrt_node_type type) +{ + struct ubrt_fwnode *curr; + + guard(spinlock)(&ubrt_fwnode_lock); + list_for_each_entry(curr, &ubrt_fwnode_list, list) { + if (curr->type == type && curr->index == index) + return curr; + } + return NULL; +} +EXPORT_SYMBOL_GPL(ubrt_fwnode_get_by_idx); + +struct ubrt_fwnode *ubrt_fwnode_get(struct fwnode_handle *fwnode) +{ + struct ubrt_fwnode *curr; + + guard(spinlock)(&ubrt_fwnode_lock); + list_for_each_entry(curr, &ubrt_fwnode_list, list) { + if (curr->fwnode == fwnode) + return curr; + + } + return NULL; +} +EXPORT_SYMBOL_GPL(ubrt_fwnode_get); + +u32 ubrt_fwnode_get_count(enum ubrt_node_type type) +{ + struct ubrt_fwnode *curr; + u32 count = 0; + + guard(spinlock)(&ubrt_fwnode_lock); + list_for_each_entry(curr, &ubrt_fwnode_list, list) { + if (curr->type == type) + count++; + } + return count; +} +EXPORT_SYMBOL_GPL(ubrt_fwnode_get_count); + +int ubrt_fwnode_add(void *node, u32 index, int size, enum ubrt_node_type type) +{ + struct ubrt_fwnode *np; + void *tmp; + + if (!node) + return -EINVAL; + + np = kzalloc(sizeof(*np), GFP_KERNEL); + if (!np) + return -ENOMEM; + + tmp = kmemdup(node, size, GFP_KERNEL); + if (!tmp) { + kfree(np); + return -ENOMEM; + } + + INIT_LIST_HEAD(&np->list); + np->ubrt_node = tmp; + np->index = index; + np->type = type; + guard(spinlock)(&ubrt_fwnode_lock); + list_add_tail(&np->list, &ubrt_fwnode_list); + + return 0; +} +EXPORT_SYMBOL_GPL(ubrt_fwnode_add); + +void ubrt_fwnode_del(u32 index, enum ubrt_node_type type) +{ + struct ubrt_fwnode *curr, *tmp; + + guard(spinlock)(&ubrt_fwnode_lock); + list_for_each_entry_safe(curr, tmp, &ubrt_fwnode_list, list) { + if (curr->type == type && curr->index == index) { + kfree(curr->ubrt_node); + list_del(&curr->list); + kfree(curr); + break; + } + } +} +EXPORT_SYMBOL_GPL(ubrt_fwnode_del); + +void ubrt_fwnode_del_all(void) +{ + struct ubrt_fwnode *cur_fwnode, *tmp_fwnode; + + guard(spinlock)(&ubrt_fwnode_lock); + list_for_each_entry_safe(cur_fwnode, tmp_fwnode, &ubrt_fwnode_list, list) { + kfree(cur_fwnode->ubrt_node); + list_del(&cur_fwnode->list); + kfree(cur_fwnode); + } +} +EXPORT_SYMBOL_GPL(ubrt_fwnode_del_all); + +int ubrt_get_interrupt_id(u16 ummu_map, u32 *intr_id) +{ + struct ubrt_fwnode *curr; + struct ummu_node *node; + + guard(spinlock)(&ubrt_fwnode_lock); + list_for_each_entry(curr, &ubrt_fwnode_list, list) { + if (curr->type != UBRT_UMMU || curr->index != ummu_map) + continue; + + node = (struct ummu_node *)curr->ubrt_node; + *intr_id = node->intr_id; + return 0; + } + return -ENXIO; +} +EXPORT_SYMBOL_GPL(ubrt_get_interrupt_id); diff --git a/drivers/ub/ubfi/ubrt.c b/drivers/ub/ubfi/ubrt.c index ea95d66260cc..8908b2ae8edd 100644 --- a/drivers/ub/ubfi/ubrt.c +++ b/drivers/ub/ubfi/ubrt.c @@ -8,11 +8,14 @@ #include #include #include +#include +#include "ummu.h" #include "ubc.h" #include "ubrt.h" #define UBIOS_SIG_UBC "ubc" +#define UBIOS_SIG_UMMU "ummu" struct acpi_table_ubrt *acpi_table; struct ubios_root_table *ubios_table; @@ -72,6 +75,7 @@ void ub_table_put(void *va) void uninit_ub_nodes(void) { + ubrt_fwnode_del_all(); destroy_ubc(); } @@ -89,6 +93,9 @@ int handle_acpi_ubrt(void) case UB_BUS_CONTROLLER_TABLE: ret = handle_ubc_table(sub_table->pointer); break; + case UMMU_TABLE: + ret = handle_ummu_table(sub_table->pointer); + break; default: pr_warn("Ignore sub table: type %u\n", sub_table->type); break; @@ -129,6 +136,8 @@ int handle_dts_ubrt(void) if (!strncmp(name, UBIOS_SIG_UBC, strlen(UBIOS_SIG_UBC))) ret = handle_ubc_table(ubios_table->sub_tables[i]); + else if (!strncmp(name, UBIOS_SIG_UMMU, strlen(UBIOS_SIG_UMMU))) + ret = handle_ummu_table(ubios_table->sub_tables[i]); else pr_warn("Ignore sub table: %s\n", name); diff --git a/drivers/ub/ubfi/ummu.c b/drivers/ub/ubfi/ummu.c new file mode 100644 index 000000000000..93f6dcbf8aa6 --- /dev/null +++ b/drivers/ub/ubfi/ummu.c @@ -0,0 +1,435 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + * Descriptor: + * Parse and create ummu from UBRT(reported by ACPI) or UBIOS Info table (reported by DTS) + */ + +#define pr_fmt(fmt) "ubfi ummu: " fmt + +#include +#include +#include + +#include "ubrt.h" +#include "ub_fi.h" +#include "ubc.h" +#include "ummu.h" + +struct ummu_sub_table { + struct ub_table_header header; + u16 count; + u8 reserved[6]; + u8 node_data[]; __counted_by(count) +}; + +#define UBRT_UMMU_PXM_VALID 0xFFFF +#define ACPI_UMMU_DEVICE_HID "HISI0551" +#define ACPI_UMMU_PMU_DEVICE_HID "HISI0571" +#define UMMU_INDEX_MASK GENMASK(31, 0) +#define UMMU_TYPE_MASK GENMASK_ULL(63, 32) + +static int __init ummu_set_proximity(struct device *dev, struct ummu_node *node) +{ + int dev_node; + + if (node->pxm == UBRT_UMMU_PXM_VALID) + return 0; + + if (acpi_disabled) { + dev_node = node->pxm; + if (dev_node >= MAX_NUMNODES || !node_possible(dev_node)) + return -EINVAL; + } else { + dev_node = pxm_to_node(node->pxm); + if (dev_node != NUMA_NO_NODE && !node_online(dev_node)) + return -EINVAL; + } + + set_dev_node(dev, dev_node); + + dev_info(dev, "UMMU mapped to Proximity domain : %u dev_node : %d\n", + node->pxm, dev_node); + + return 0; +} + +static int __init ummu_count_resources(struct ummu_node *node) +{ + /* present mem resource only */ + return 1; +} + +static void __init ummu_pmu_dev_init_resources(struct resource *res, int cnt, + struct ummu_node *node) +{ + int num = 0; + + res[num].start = node->pmu_addr; + res[num].end = node->pmu_addr + node->pmu_size - 1; + res[num].flags = IORESOURCE_MEM; + num++; + + if (num != cnt) + pr_err("ummu pmu res num is not match!\n"); +} + +static void __init ummu_device_init_resources(struct resource *res, int cnt, + struct ummu_node *node) +{ + int num = 0; + + res[num].start = node->base_addr; + res[num].end = node->base_addr + node->addr_size - 1; + res[num].flags = IORESOURCE_MEM; + num++; + if (num != cnt) + pr_err("ummu res num is not match!\n"); +} + +static int __init ummu_add_resources(struct platform_device *pdev, + struct ummu_node *node, + enum ubrt_node_type type) +{ + struct resource *res __free(kfree); + int num_res; + + num_res = ummu_count_resources(node); + res = kcalloc(num_res, sizeof(*res), GFP_KERNEL); + if (!res) + return -ENOMEM; + + if (type == UBRT_UMMU) + ummu_device_init_resources(res, num_res, node); + else + ummu_pmu_dev_init_resources(res, num_res, node); + + return platform_device_add_resources(pdev, res, num_res); +} + +static int ummu_rename_device(struct platform_device *pdev, enum ubrt_node_type type) +{ + static int device_count; + char new_name[32]; + int ret; + + if (type == UBRT_UMMU) + ret = snprintf(new_name, sizeof(new_name), "ummu.%d", device_count); + else + ret = snprintf(new_name, sizeof(new_name), "ummu_pmu.%d", device_count); + + if (ret < 0 || ret >= sizeof(new_name)) { + dev_err(&pdev->dev, "failed to generate new device name\n"); + return -ENOENT; + } + + ret = device_rename(&pdev->dev, new_name); + if (ret) { + dev_err(&pdev->dev, "failed to rename device to %s: %d\n", new_name, ret); + return ret; + } + pdev->name = pdev->dev.kobj.name; + + device_count++; + + return 0; +} + +static int ummu_config_update(struct platform_device *pdev, + struct ummu_node *ummu_node, + enum ubrt_node_type type) +{ + int ret; + + if (!pdev->dev.msi.domain) + dev_warn(&pdev->dev, "can't find device msi domain.\n"); + + ret = ummu_rename_device(pdev, type); + if (ret) + return ret; + + ret = ummu_set_proximity(&pdev->dev, ummu_node); + if (ret) { + dev_err(&pdev->dev, "ummu set proximity failed ret[%d]\n", ret); + return ret; + } + + ret = ummu_add_resources(pdev, ummu_node, type); + if (ret) { + dev_err(&pdev->dev, "ummu add resources failed ret[%d]\n", ret); + return ret; + } + + if (type == UBRT_UMMU) { + ret = platform_device_add_data(pdev, ummu_node->vendor_info, + sizeof(ummu_node->vendor_info)); + if (ret) + return ret; + } + + dev_info(&pdev->dev, "interrupt_id[0x%x], pxm[%u], its_index[%u], vendor_id[0x%x]\n", + ((type == UBRT_UMMU) ? ummu_node->intr_id : ummu_node->pmu_intr_id), + ummu_node->pxm, ummu_node->its_index, ummu_node->vendor_id); + return 0; +} + +static acpi_status acpi_processor_ummu(acpi_handle handle, u32 lvl, + void *context, void **rv) +{ + struct platform_device *pdev; + struct acpi_device *adev; + enum ubrt_node_type type; + struct ummu_node *node; + struct ubrt_fwnode *fw; + unsigned long long uid; + acpi_status status; + struct device *dev; + u64 *node_flag; + u32 index; + int ret; + + node_flag = context; + index = FIELD_GET(UMMU_INDEX_MASK, *node_flag); + type = FIELD_GET(UMMU_TYPE_MASK, *node_flag); + fw = ubrt_fwnode_get_by_idx(index, type); + if (!fw) { + pr_err("can not get ubrt fwnode!\n"); + return AE_CTRL_FALSE; + } + + status = acpi_evaluate_integer(handle, "_UID", NULL, &uid); + if (ACPI_FAILURE(status)) { + pr_err("can not get dsdt uid. status[%u]\n", status); + return AE_CTRL_TERMINATE; + } + + if (index != (u32)uid) + return AE_OK; + + adev = acpi_get_acpi_dev(handle); + if (!adev) { + pr_err("acpi get device failed\n"); + return AE_CTRL_TERMINATE; + } + + dev = bus_find_device_by_acpi_dev(&platform_bus_type, adev); + if (!dev) { + pr_err("platform device not found\n"); + status = AE_CTRL_TERMINATE; + goto out; + } + pdev = to_platform_device(dev); + node = (struct ummu_node *)fw->ubrt_node; + + ret = ummu_config_update(pdev, node, type); + if (ret) { + dev_err(dev, "update config failed, ret[%d]\n", ret); + status = AE_CTRL_FALSE; + goto out; + } + + ret = ubrt_fwnode_set(index, type, dev->fwnode); + if (ret) { + dev_err(dev, "update fwnode failed, ret[%d]\n", ret); + status = AE_CTRL_FALSE; + } + +out: + acpi_put_acpi_dev(adev); + return status; +} + +#ifdef CONFIG_ACPI +static int acpi_update_ummu_config(struct ummu_node *ummu_node, u32 index) +{ + acpi_status status; + u64 node_flag; + int ret; + + ret = ubrt_fwnode_add(ummu_node, index, sizeof(*ummu_node), UBRT_UMMU); + if (ret) { + pr_err("failed to add ummu fwnode! ret[%d]\n", ret); + return ret; + } + + node_flag = index | (((u64)UBRT_UMMU) << 32); + + status = acpi_get_devices(ACPI_UMMU_DEVICE_HID, + acpi_processor_ummu, + &node_flag, NULL); + if (ACPI_FAILURE(status)) { + pr_err("acpi get devices err, status[%u]\n", status); + goto ummu_err; + } + + ret = ubrt_fwnode_add(ummu_node, index, sizeof(struct ummu_node), UBRT_UMMU_PMU); + if (ret) { + pr_err("failed to add pmu fwnode! ret[%d]\n", ret); + goto ummu_err; + } + + node_flag = index | (((u64)UBRT_UMMU_PMU) << 32); + + /* Get UB PMU from DSDT */ + status = acpi_get_devices(ACPI_UMMU_PMU_DEVICE_HID, + acpi_processor_ummu, + &node_flag, NULL); + if (ACPI_FAILURE(status)) { + pr_err("acpi get devices err, status[%u]\n", status); + goto pmu_err; + } + + return 0; + +pmu_err: + ubrt_fwnode_del(index, UBRT_UMMU_PMU); +ummu_err: + ubrt_fwnode_del(index, UBRT_UMMU); + return -ENODEV; +} +#else +static inline int acpi_update_ummu_config(struct ummu_node *ummu_node, u32 index) +{ + return -ENODEV; +} +#endif /* CONFIG_ACPI */ + +#ifdef CONFIG_OF +static struct platform_device *ummu_of_find_plat_dev(struct device_node *node, u32 index) +{ + struct platform_device *pdev; + const char *node_name; + u32 dts_index; + int ret; + + node_name = of_node_full_name(node); + + ret = of_property_read_u32(node, "index", &dts_index); + if (ret) { + pr_err("dts can't find ummu ctl-no\n"); + return NULL; + } + + if (dts_index != index) { + pr_debug("ummu dts_index %u != index %u\n", dts_index, index); + return NULL; + } + + pdev = of_find_device_by_node(node); + if (!pdev) { + pr_err("failed to find platform device for node: %s\n", node_name); + return NULL; + } + + return pdev; +} + +static int ummu_of_update_config(struct platform_device *pdev, + struct ummu_node *ummu_node, + u32 index, + enum ubrt_node_type type) +{ + int ret; + + ret = ubrt_fwnode_add(ummu_node, index, sizeof(struct ummu_node), type); + if (ret) { + dev_err(&pdev->dev, "failed to add ummu fwnode! ret[%d]\n", ret); + return ret; + } + + ret = ubrt_fwnode_set(index, type, pdev->dev.fwnode); + if (ret) { + dev_err(&pdev->dev, "update fwnode failed, ret[%d]\n", ret); + goto err; + } + + ret = ummu_config_update(pdev, ummu_node, type); + if (ret) { + dev_err(&pdev->dev, "update config failed, ret[%d]\n", ret); + goto err; + } + + return 0; +err: + ubrt_fwnode_del(index, type); + return ret; +} + +static int dts_update_ummu_config(struct ummu_node *ummu_node, u32 index) +{ + struct platform_device *pdev; + struct device_node *node; + int ret; + + for_each_compatible_node(node, NULL, "ub,ummu") { + pdev = ummu_of_find_plat_dev(node, index); + if (!pdev) + continue; + + ret = ummu_of_update_config(pdev, ummu_node, index, UBRT_UMMU); + if (ret) + return ret; + } + + for_each_compatible_node(node, NULL, "ub,ummu_pmu") { + pdev = ummu_of_find_plat_dev(node, index); + if (!pdev) + continue; + + ret = ummu_of_update_config(pdev, ummu_node, index, UBRT_UMMU_PMU); + if (ret) + return ret; + } + + return 0; +} +#else +static inline int dts_update_ummu_config(struct ummu_node *ummu_node, u32 index) +{ + return -ENODEV; +} +#endif /* CONFIG_OF */ + +static int parse_ummu(void *info_node) +{ + struct ummu_sub_table *sub_table; + struct ummu_node *ummu_node; + int ret; + u32 index; + + sub_table = (struct ummu_sub_table *)info_node; + if (!sub_table->count) { + pr_warn("info table has no ummu.\n"); + return 0; + } + + pr_info("ummu node num: %u\n", sub_table->count); + + ummu_node = (struct ummu_node *)sub_table->node_data; + for (index = 0; index < sub_table->count; index++, ummu_node++) { + if (acpi_disabled) + ret = dts_update_ummu_config(ummu_node, index); + else + ret = acpi_update_ummu_config(ummu_node, index); + + if (ret) { + pr_err("Create No.%u ummu failed, ret=%d\n", index, ret); + return ret; + } + } + + return 0; +} + +int handle_ummu_table(u64 pointer) +{ + void *info_node = ub_table_get(pointer); + int ret; + + if (!info_node) + return -EINVAL; + + ret = parse_ummu(info_node); + ub_table_put(info_node); + return ret; +} diff --git a/drivers/ub/ubfi/ummu.h b/drivers/ub/ubfi/ummu.h new file mode 100644 index 000000000000..e9c07c000a51 --- /dev/null +++ b/drivers/ub/ubfi/ummu.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + * Descriptor: Parse and create ummu from UBRT + */ + +#ifndef __UBFI_UMMU_H__ +#define __UBFI_UMMU_H__ + +int handle_ummu_table(u64 pointer); + +#endif /* __UBFI_UMMU_H__ */ diff --git a/include/ub/ubfi/ubfi.h b/include/ub/ubfi/ubfi.h index cdaaa1e29bc3..425d59102a1a 100644 --- a/include/ub/ubfi/ubfi.h +++ b/include/ub/ubfi/ubfi.h @@ -12,6 +12,74 @@ #include #include +#define UMMU_RSV_LEN 26 +#define UMMU_VEND_LEN 10 + +enum ubrt_node_type { + UBRT_UBC, + UBRT_UMMU, + UBRT_UMMU_PMU, +}; + +/* + * struct ubrt_fwnode - Structure to store nodes of the UBRT table reported by + * the BIOS. The UBRT nodes describe hardware information + * for UB system devices, such as UBC devices, UMMU + * devices, etc. + * + * @type: Type of the UBRT node. + * @index: Index of the UBRT node within its type. + * @fwnode: fwnode_handle of the device corresponding to the UBRT node. + * @ubrt_node: Pointer to the UBRT node, e.g., an ummu_node. + * @list: List node of ubrt_fwnode_list. + */ +struct ubrt_fwnode { + enum ubrt_node_type type; + u32 index; + struct fwnode_handle *fwnode; + void *ubrt_node; + struct list_head list; +}; + +/* + * struct ummu_node - Structure to store hardware information nodes reported + * by the BIOS to the UMMU device. + * + * @base_addr: Base address of the UMMU register space. + * @addr_size: Size of the UMMU register space. + * @intr_id: Interrupt ID for the UMMU device, used for device interrupt + * request in the OS. + * @pxm: Proximity Domain (PXM) identifier of the UMMU device. If the value is + * 0xFFFF, it indicates an invalid PXM. + * @its_index: Index of the Interrupt Translation Service (ITS) associated with + * the UMMU device. + * @pmu_addr: Base address of the PMU register space associated with the UMMU + * device. + * @pmu_size: Size of the PMU register space associated with the UMMU device. + * @pmu_intr_id: Interrupt ID for the PMU associated with the UMMU device. + * @min_tid: Minimum Token ID that can be allocated. + * @max_tid: Maximum Token ID that can be allocated. + * @reserved: Reserved bytes. + * @vendor_id: Vendor ID, reused from the Vendor ID definition in GUID, see + * "UB Base Specification 2.0". + * @vendor_info: Vendor-specific information. + */ +struct ummu_node { + u64 base_addr; + u64 addr_size; + u32 intr_id; + u16 pxm; + u16 its_index; + u64 pmu_addr; + u64 pmu_size; + u32 pmu_intr_id; + u32 min_tid; + u32 max_tid; + u8 reserved[UMMU_RSV_LEN]; + u16 vendor_id; + u64 vendor_info[UMMU_VEND_LEN]; +}; + /** * ubrt_register_gsi() - Registering UBC's interrupt into the kernel via ACPI * @hwirq: GSI IRQ number reported by BIOS @@ -31,6 +99,90 @@ int ubrt_register_gsi(u32 hwirq, int trigger, int polarity, const char *name, */ void ubrt_unregister_gsi(u32 hwirq); +/** + * ubrt_fwnode_set() - Associate a device's fwnode with an UBRT node + * @index: Index of the UBRT node within its type. + * @type: Type of the UBRT node. + * @fwnode: fwnode_handle associated with a device. + * + * Finds an UBRT node by index and type, and associates the device's + * fwnode with the UBRT node. This allows subsequent lookups of + * the UBRT node using the device's fwnode. + * + * Return: 0 if success, or error code if failed. + */ +int ubrt_fwnode_set(u32 index, enum ubrt_node_type type, + struct fwnode_handle *fwnode); + +/** + * ubrt_fwnode_get_by_idx() - Get an UBRT node by index and type + * @index: Index of the UBRT node within its type. + * @type: Type of the UBRT node. + * + * Finds an UBRT node by index and type. + * + * Return: Pointer to the struct ubrt_fwnode if found, or NULL if not found. + */ +struct ubrt_fwnode *ubrt_fwnode_get_by_idx(u32 index, + enum ubrt_node_type type); + +/** + * ubrt_fwnode_get() - Get an UBRT node by fwnode_handle + * @fwnode: fwnode_handle associated with a device. + * + * Finds an UBRT node by the device's fwnode. + * + * Return: Pointer to struct ubrt_fwnode if found, or NULL if not found. + */ +struct ubrt_fwnode *ubrt_fwnode_get(struct fwnode_handle *fwnode); + +/** + * ubrt_fwnode_get_count() - Get the count of UBRT nodes of a specific type + * @type: Type of the UBRT node. + * + * Return: The count of UBRT nodes of the specified type. + */ +u32 ubrt_fwnode_get_count(enum ubrt_node_type type); + +/** + * ubrt_fwnode_add() - Add an UBRT node to the ubrt_fwnode_list + * @node: Pointer to the UBRT node. + * @index: Index of the UBRT node within its type. + * @size: Size of the UBRT node. + * @type: Type of the UBRT node. + * + * Adds an UBRT node to the ubrt_fwnode_list, associating it with the + * specified index and type. + * + * Return: 0 if success, or error code if failed. + */ +int ubrt_fwnode_add(void *node, u32 index, int size, enum ubrt_node_type type); + +/** + * ubrt_fwnode_del() - Delete an UBRT node from the ubrt_fwnode_list + * @index: Index of the UBRT node within its type. + * @type: Type of the UBRT node. + */ +void ubrt_fwnode_del(u32 index, enum ubrt_node_type type); + +/** + * ubrt_fwnode_del_all() - Clear the ubrt_fwnode_list + * + * Clears all UBRT nodes from the ubrt_fwnode_list. + */ +void ubrt_fwnode_del_all(void); + +/** + * ubrt_get_interrupt_id() - Get the interrupt ID of an UMMU node + * @ummu_map: Index of the UMMU device. + * @intr_id: Pointer to the interrupt ID. + * + * Obtain the intr_id of ummu_node through ummu_map. + * + * Return: 0 if success, or error code if failed. + */ +int ubrt_get_interrupt_id(u16 ummu_map, u32 *intr_id); + #if IS_ENABLED(CONFIG_UB_UBFI) extern struct list_head ubc_list; extern u32 ubc_eid_start; @@ -38,6 +190,26 @@ extern u32 ubc_eid_end; extern u32 ubc_cna_start; extern u32 ubc_cna_end; extern u8 ubc_feature; + +/** + * ubrt_iommu_get_resv_regions() - Get reserved memory regions from UBRT table. + * @dev: Pointer to the device. + * @list: List used to store reserved memory regions. + * + * In virtualization scenarios, the host OS should describe the available + * memory regions for UBC to the guest OS through the UB Reserved Memory + * information table in the UBRT table. This information table is reported by + * the VM BIOS; the physical machine BIOS does not need to report this + * information table. + */ +void ubrt_iommu_get_resv_regions(struct device *dev, struct list_head *list); #endif /* CONFIG_UB_UBFI */ +#if IS_ENABLED(CONFIG_UB_UBRT_PLAT_DEV) +int ubrt_pmsi_get_interrupt_id(struct device *dev, u32 *interrupt_id); +#else +static inline int ubrt_pmsi_get_interrupt_id(struct device *dev, u32 *interrupt_id) +{ return -ENODEV; } +#endif /* CONFIG_UB_UBRT_PLAT_DEV */ + #endif /* _UB_UBFI_UBFI_H_ */ -- Gitee From 0d86305d789288753fb2ef9928906942d1c8f8ae Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Mon, 22 Sep 2025 21:44:28 +0800 Subject: [PATCH 058/214] vfio:ubus: Support for vfio ubus driver framework ANBZ: #45460 commit f684adf1d836969a4c2f7189f12c8c1234dc1b99 openEuler. Support vfio ubus driver framework, when the vfio ub module driver is loaded, calls the ub bus driver framework's ub_register_driver() driver registration interface to attach it to the ub bus. During unbinding the driver, in the remove interface, call the unregistration interface to delete the vfio interface file and rlease resource. Signed-off-by: Yahui Liu Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- .../configs/L1-RECOMMEND/arm64/CONFIG_VFIO_UB | 1 + drivers/vfio/Kconfig | 1 + drivers/vfio/Makefile | 1 + drivers/vfio/ubus/Kconfig | 15 ++ drivers/vfio/ubus/Makefile | 4 + drivers/vfio/ubus/vfio_ub.c | 105 ++++++++++++ drivers/vfio/ubus/vfio_ub_core.c | 162 ++++++++++++++++++ drivers/vfio/ubus/vfio_ub_private.h | 46 +++++ include/linux/mod_devicetable.h | 4 + include/ub/ubus/ubus.h | 8 + scripts/mod/file2alias.c | 5 +- 11 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 anolis/configs/L1-RECOMMEND/arm64/CONFIG_VFIO_UB create mode 100644 drivers/vfio/ubus/Kconfig create mode 100644 drivers/vfio/ubus/Makefile create mode 100644 drivers/vfio/ubus/vfio_ub.c create mode 100644 drivers/vfio/ubus/vfio_ub_core.c create mode 100644 drivers/vfio/ubus/vfio_ub_private.h diff --git a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_VFIO_UB b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_VFIO_UB new file mode 100644 index 000000000000..81bd7c5986f8 --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_VFIO_UB @@ -0,0 +1 @@ +CONFIG_VFIO_UB=m diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig index d9984ee13c93..eb8f1756fd83 100644 --- a/drivers/vfio/Kconfig +++ b/drivers/vfio/Kconfig @@ -91,6 +91,7 @@ config VFIO_DEBUGFS entries as required. source "drivers/vfio/pci/Kconfig" +source "drivers/vfio/ubus/Kconfig" source "drivers/vfio/platform/Kconfig" source "drivers/vfio/mdev/Kconfig" source "drivers/vfio/fsl-mc/Kconfig" diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile index b2fc9fb499d8..b875e7f26e31 100644 --- a/drivers/vfio/Makefile +++ b/drivers/vfio/Makefile @@ -12,6 +12,7 @@ vfio-$(CONFIG_VFIO_DEBUGFS) += debugfs.o obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o obj-$(CONFIG_VFIO_PCI_CORE) += pci/ +obj-$(CONFIG_VFIO_UB) += ubus/ obj-$(CONFIG_VFIO_PLATFORM_BASE) += platform/ obj-$(CONFIG_VFIO_MDEV) += mdev/ obj-$(CONFIG_VFIO_FSL_MC) += fsl-mc/ diff --git a/drivers/vfio/ubus/Kconfig b/drivers/vfio/ubus/Kconfig new file mode 100644 index 000000000000..130d11e0c21a --- /dev/null +++ b/drivers/vfio/ubus/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0+ +menu "VFIO support for UBUS devices" + depends on UB_UBUS && MMU + +config VFIO_UB + tristate "Generic VFIO support for any UBUS device" + select VFIO_VIRQFD + select IRQ_BYPASS_MANAGER + help + Support for the generic UBUS VFIO bus driver which can connect any + UBUS device to the VFIO framework. + + If you don't know what to do here, say N. + +endmenu diff --git a/drivers/vfio/ubus/Makefile b/drivers/vfio/ubus/Makefile new file mode 100644 index 000000000000..d5835459b7a6 --- /dev/null +++ b/drivers/vfio/ubus/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0+ + +vfio-ub-y := vfio_ub.o vfio_ub_core.o +obj-$(CONFIG_VFIO_UB) += vfio-ub.o diff --git a/drivers/vfio/ubus/vfio_ub.c b/drivers/vfio/ubus/vfio_ub.c new file mode 100644 index 000000000000..09aae7a98aa6 --- /dev/null +++ b/drivers/vfio/ubus/vfio_ub.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * Derived from original vfio: + * Copyright 2010 Cisco Systems, Inc. All rights reserved. + * Author: Tom Lyon, pugs@cisco.com + * + * Thanks to Alex Williamson and Tom Lyon for their original + * vfio implementation. + * + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "vfio ub: " fmt + +#include +#include +#include + +#include "vfio_ub_private.h" + +static const struct vfio_device_ops vfio_ub_ops = { + .name = "vfio-ub", + .init = vfio_ub_core_init_dev, + .release = vfio_ub_core_release_dev, + .open_device = vfio_ub_core_open_device, + .close_device = vfio_ub_core_close_device, +}; + +static int vfio_ub_probe(struct ub_entity *uent, const struct ub_device_id *id) +{ + struct vfio_ub_core_device *vdev; + int ret; + + vdev = vfio_alloc_device(vfio_ub_core_device, vdev, &uent->dev, &vfio_ub_ops); + if (IS_ERR(vdev)) + return PTR_ERR(vdev); + + dev_set_drvdata(&uent->dev, vdev); + ret = vfio_ub_core_register_device(vdev); + if (ret) + goto out_put_vdev; + + ub_dbg(uent, "vfio-ub driver has matched\n"); + return 0; + +out_put_vdev: + dev_set_drvdata(&uent->dev, NULL); + vfio_put_device(&vdev->vdev); + return ret; +} + +static void vfio_ub_remove(struct ub_entity *uent) +{ + struct vfio_ub_core_device *vdev = + (struct vfio_ub_core_device *)dev_get_drvdata(&uent->dev); + + vfio_ub_core_disable_all(vdev); + vfio_ub_core_unregister_device(vdev); + dev_set_drvdata(&uent->dev, NULL); + vfio_put_device(&vdev->vdev); + + ub_dbg(uent, "vfio-ub driver has removed\n"); +} + +static const struct ub_device_id vfio_ub_table[] = { + { UB_DRIVER_OVERRIDE_ENTITY_VFIO(UB_ANY_ID, UB_ANY_ID) }, /* match all by default */ + {} +}; +MODULE_DEVICE_TABLE(ub, vfio_ub_table); + +static struct ub_driver vfio_ub_driver = { + .name = "vfio-ub", + .id_table = vfio_ub_table, + .probe = vfio_ub_probe, + .remove = vfio_ub_remove, + .driver_managed_dma = true, +}; + +static int __init vfio_ub_init(void) +{ + int ret; + + ret = ub_register_driver(&vfio_ub_driver); + if (ret) + goto out_driver; + + return 0; + +out_driver: + return ret; +} + +static void __exit vfio_ub_exit(void) +{ + ub_unregister_driver(&vfio_ub_driver); +} + +module_init(vfio_ub_init); +module_exit(vfio_ub_exit); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("VFIO UB - User Level meta-driver"); +MODULE_IMPORT_NS(UB_UBUS); diff --git a/drivers/vfio/ubus/vfio_ub_core.c b/drivers/vfio/ubus/vfio_ub_core.c new file mode 100644 index 000000000000..2445b39de0c1 --- /dev/null +++ b/drivers/vfio/ubus/vfio_ub_core.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * Derived from original vfio: + * Copyright 2010 Cisco Systems, Inc. All rights reserved. + * Author: Tom Lyon, pugs@cisco.com + * + * Thanks to Alex Williamson and Tom Lyon for their original + * vfio implementation. + * + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "vfio ub: " fmt + +#include +#include +#include +#include + +#include "vfio_ub_private.h" + +static bool is_ub_entity_support_used(struct ub_entity *uent) +{ + if (!((uent_type(uent) == UB_TYPE_CONTROLLER || + uent_type(uent) == UB_TYPE_ICONTROLLER) && + uent_base_code(uent) != UB_BASE_CODE_BUS_CONTROLLER)) + return false; + + if (!list_empty(&uent->ue_list)) + return false; + + return true; +} + +int vfio_ub_core_register_device(struct vfio_ub_core_device *vdev) +{ + struct ub_entity *uent = vdev->uent; + struct device *dev = &uent->dev; + + /* Drivers must set the vfio_ub_core_device to their drvdata */ + if (WARN_ON(vdev != dev_get_drvdata(dev))) + return -EINVAL; + + if (!is_ub_entity_support_used(uent)) { + ub_err(uent, "Cannot bind to vfio driver\n"); + return -EBUSY; + } + + return vfio_register_group_dev(&vdev->vdev); +} + +void vfio_ub_core_unregister_device(struct vfio_ub_core_device *vdev) +{ + vfio_unregister_group_dev(&vdev->vdev); +} + +static int vfio_usi_info_init(struct vfio_ub_core_device *vdev) +{ + struct ub_entity *uent = vdev->uent; + u32 low32; + u32 high32; + u16 num; + int ret; + + /* when device supports int type2, read usi info from cfgspace */ + if (uent->no_intr == 0 && uent->intr_type1 == 0) { + ret = ub_cfg_read_dword(vdev->uent, UB_INT_VECTOR_TBL_SA_L, &low32); + ret |= ub_cfg_read_dword(vdev->uent, UB_INT_VECTOR_TBL_SA_H, &high32); + ret |= ub_cfg_read_word(vdev->uent, UB_NUM_OF_INTR_VECTOR_TBL, &num); + if (ret) + return -EFAULT; + + vdev->usi_vector_offset = (u64)high32 << 32 | low32; + vdev->usi_vector_size = (num + 1) * UB_INTR_VECTOR_ENTRY_SIZE; + + ret = ub_cfg_read_dword(vdev->uent, UB_INT_ADDR_TBL_SA_L, &low32); + ret |= ub_cfg_read_dword(vdev->uent, UB_INT_ADDR_TBL_SA_H, &high32); + ret |= ub_cfg_read_word(vdev->uent, UB_NUM_OF_INTR_ADDR_TBL, &num); + if (ret) + return -EFAULT; + + vdev->usi_addr_offset = (u64)high32 << 32 | low32; + vdev->usi_addr_size = (num + 1) * UB_INTR_ADDR_ENTRY_SIZE; + } else { + vdev->usi_vector_offset = 0; + vdev->usi_vector_size = 0; + vdev->usi_addr_offset = 0; + vdev->usi_addr_size = 0; + } + + return 0; +} + +static int vfio_ub_enable(struct vfio_ub_core_device *vdev) +{ + vdev->num_ext_irqs = 0; + vdev->num_regions = 0; + vdev->num_vendor_irqs = 0; + vdev->num_vendor_regions = 0; + vdev->reset_works = 1; /* we assume ub support ELR for now. */ + + return vfio_usi_info_init(vdev); +} + +static void vfio_ub_disable(struct vfio_ub_core_device *vdev) +{ + /* clear device caches when vm exit or crash */ + if (vdev->reset_works) + ub_reset_entity(vdev->uent); +} + +int vfio_ub_core_open_device(struct vfio_device *core_vdev) +{ + struct vfio_ub_core_device *vdev = container_of(core_vdev, + struct vfio_ub_core_device, vdev); + + return vfio_ub_enable(vdev); +} + +void vfio_ub_core_close_device(struct vfio_device *core_vdev) +{ + struct vfio_ub_core_device *vdev = container_of(core_vdev, + struct vfio_ub_core_device, vdev); + + vfio_ub_disable(vdev); + + mutex_lock(&vdev->igate); + if (vdev->req_trigger) { + eventfd_ctx_put(vdev->req_trigger); + vdev->req_trigger = NULL; + } + mutex_unlock(&vdev->igate); +} + +int vfio_ub_core_init_dev(struct vfio_device *core_vdev) +{ + struct vfio_ub_core_device *vdev = + container_of(core_vdev, struct vfio_ub_core_device, vdev); + + vdev->uent = to_ub_entity(core_vdev->dev); + mutex_init(&vdev->igate); + return 0; +} + +void vfio_ub_core_release_dev(struct vfio_device *core_vdev) +{ + struct vfio_ub_core_device *vdev = + container_of(core_vdev, struct vfio_ub_core_device, vdev); + + mutex_destroy(&vdev->igate); +} + +void vfio_ub_core_disable_all(struct vfio_ub_core_device *vdev) +{ + struct ub_entity *uent = vdev->uent; + + if (!list_empty(&uent->ue_list)) + ub_disable_entities(uent); +} diff --git a/drivers/vfio/ubus/vfio_ub_private.h b/drivers/vfio/ubus/vfio_ub_private.h new file mode 100644 index 000000000000..8391e9716a62 --- /dev/null +++ b/drivers/vfio/ubus/vfio_ub_private.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * Derived from original vfio: + * Copyright 2010 Cisco Systems, Inc. All rights reserved. + * Author: Tom Lyon, pugs@cisco.com + * + * Thanks to Alex Williamson and Tom Lyon for their original + * vfio implementation. + * + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ +#ifndef __VFIO_UB_PRIVATE_H__ +#define __VFIO_UB_PRIVATE_H__ + +#include +#include +#include + +struct vfio_ub_core_device { + struct vfio_device vdev; + struct ub_entity *uent; + int num_regions; /* vfio-ub additional regions */ + int num_ext_irqs; /* vfio-ub additional extended irqs */ + int num_vendor_regions; /* vfio-ub additional vendor-defined regions */ + int num_vendor_irqs; /* vfio-ub additional vendor-defined irqs */ + bool reset_works; /* vfio-ub support reset flag */ + u64 usi_vector_offset; + u32 usi_vector_size; + u64 usi_addr_offset; + u32 usi_addr_size; + struct eventfd_ctx *req_trigger; + struct mutex igate; +}; + +int vfio_ub_core_register_device(struct vfio_ub_core_device *vdev); +void vfio_ub_core_unregister_device(struct vfio_ub_core_device *vdev); +int vfio_ub_core_open_device(struct vfio_device *core_vdev); +void vfio_ub_core_close_device(struct vfio_device *core_vdev); +int vfio_ub_core_init_dev(struct vfio_device *core_vdev); +void vfio_ub_core_release_dev(struct vfio_device *core_dev); +void vfio_ub_core_disable_all(struct vfio_ub_core_device *vdev); + +#endif /* __VFIO_UB_PRIVATE_H__ */ diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index de678c6b4348..44a9dd285997 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -950,6 +950,10 @@ struct cdx_device_id { #define UB_ANY_ID (~0) +enum { + UB_ID_F_VFIO_DRIVER_OVERRIDE = 1, +}; + /** * struct ub_device_id - UB device identifier * @vendor: Vendor ID diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 61bad7637d90..194f9556de39 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -21,6 +21,14 @@ .vendor = (v), .device = (d), \ .mod_vendor = (u32)UB_ANY_ID, .module = (u32)UB_ANY_ID +#define UB_ENTITY_DRIVER_OVERRIDE(v, d, driver_override) \ + .vendor = (v), .device = (d), \ + .mod_vendor = (u32)UB_ANY_ID, .module = (u32)UB_ANY_ID, \ + .override_only = (driver_override) + +#define UB_DRIVER_OVERRIDE_ENTITY_VFIO(v, d) \ + UB_ENTITY_DRIVER_OVERRIDE(v, d, UB_ID_F_VFIO_DRIVER_OVERRIDE) + #define UB_ENTITY_MODULE(v, d, m_v, m) \ .vendor = (v), .device = (d), \ .mod_vendor = (m_v), .module = (m) diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 5acd60a148d1..ac15eeecd8c0 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1476,7 +1476,7 @@ static int do_cdx_entry(const char *filename, void *symval, } #ifdef CONFIG_UB -/* Looks like: ub:vNdNmvNmNcN. */ +/* Looks like: ub:vNdNmvNmNcN or _ub:vNdNmvNmNcN. */ static int do_ub_entry(const char *filename, void *symval, char *alias) { /* Class code field can be divided into these two. */ @@ -1494,6 +1494,9 @@ static int do_ub_entry(const char *filename, void *symval, char *alias) case 0: strcpy(alias, "ub:"); break; + case UB_ID_F_VFIO_DRIVER_OVERRIDE: + strcpy(alias, "vfio_ub:"); + break; default: warn("Unknown UB driver_override alias %08X\n", override_only); -- Gitee From 1048740c8e938cac6ed19cdb62a555927f0ed8e7 Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Tue, 23 Sep 2025 10:12:23 +0800 Subject: [PATCH 059/214] vfio:ubus: Support ub vfio config read and write ANBZ: #45460 commit ec1fedc7332152ce3d045766504670f0990173bb openEuler. In user space, read and write operations to the configuration space of ub device can be performed through the vfio ub device files. Signed-off-by: Yahui Liu Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/vfio/ubus/Makefile | 2 +- drivers/vfio/ubus/vfio_ub_config.c | 387 ++++++++++++++++++++++++++++ drivers/vfio/ubus/vfio_ub_private.h | 38 +++ 3 files changed, 426 insertions(+), 1 deletion(-) create mode 100644 drivers/vfio/ubus/vfio_ub_config.c diff --git a/drivers/vfio/ubus/Makefile b/drivers/vfio/ubus/Makefile index d5835459b7a6..20c9886c534c 100644 --- a/drivers/vfio/ubus/Makefile +++ b/drivers/vfio/ubus/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0+ -vfio-ub-y := vfio_ub.o vfio_ub_core.o +vfio-ub-y := vfio_ub.o vfio_ub_core.o vfio_ub_config.o obj-$(CONFIG_VFIO_UB) += vfio-ub.o diff --git a/drivers/vfio/ubus/vfio_ub_config.c b/drivers/vfio/ubus/vfio_ub_config.c new file mode 100644 index 000000000000..4a902f5c2940 --- /dev/null +++ b/drivers/vfio/ubus/vfio_ub_config.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * Derived from original vfio: + * Copyright 2010 Cisco Systems, Inc. All rights reserved. + * Author: Tom Lyon, pugs@cisco.com + * + * Thanks to Alex Williamson and Tom Lyon for their original + * vfio implementation. + * + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "vfio ub: " fmt + +#include +#include + +#include "vfio_ub_private.h" + +#define NO_ENTITY0_EO 0 +#define ENTITY0_EO 0xFFFFFFFFU + +/* Now, this attr control just support at byte granularity */ +enum { + FUNC_EXIST = 0x00000000, /* idev and ub device have this register */ + IDEV_NO_EXIST = 0x01010101, /* idev do not has this register */ + UDEV_NO_EXIST = 0x02020202, /* ub device do not has have this register */ + FUNC_NO_EXIST = 0x03030303, /* idev and ub device do not have this register */ +}; + +struct attrs { + __le32 ent0eo; + __le32 exist; +}; + +#define CFG_MAP_INVALID (-1) + +#define UB_PORT_CAP_GAP 0x100 +#define UB_PORT0_BASIC_CAP 0x200 + +#define UB_CFG_POS_TO_CAP(pos) ((u64)(pos) >> 10) +#define UB_CFG_CAP_TO_POS(cap) ((u32)(cap) << 10) + +static const u8 zero_array[UB_SLICE_SZ] = {}; + +static int vfio_ub_do_user_config_read(struct ub_entity *uent, u64 pos, + __le32 *val, int count) +{ + int ret = -EINVAL; + u32 tmp_val = 0; + u8 tmp_byte; + u16 tmp_word; + + switch (count) { + case BYTE_SIZE: + ret = ub_cfg_read_byte(uent, pos, &tmp_byte); + tmp_val = tmp_byte; + break; + case WORD_SIZE: + ret = ub_cfg_read_word(uent, pos, &tmp_word); + tmp_val = tmp_word; + break; + case DWORD_SIZE: + ret = ub_cfg_read_dword(uent, pos, &tmp_val); + break; + default: + break; + } + + *val = cpu_to_le32(tmp_val); + + return ret; +} + +static bool vfio_ub_judge_type(struct ub_entity *uent, u8 exist) +{ + return ((((exist & IDEV_NO_EXIST) == 0) && (uent_type(uent) == UB_TYPE_ICONTROLLER && + uent_base_code(uent) != UB_BASE_CODE_BUS_CONTROLLER)) || + (((exist & UDEV_NO_EXIST) == 0) && (uent_type(uent) == UB_TYPE_CONTROLLER && + uent_base_code(uent) != UB_BASE_CODE_BUS_CONTROLLER))); +} + +static bool vfio_ub_judge_reg_read(struct ub_entity *uent, u8 ent0eo, u8 exist) +{ + if (vfio_ub_judge_type(uent, exist)) { + if (((ent0eo == (u8)ENTITY0_EO) && (uent->entity_idx == 0)) || + (ent0eo == NO_ENTITY0_EO)) { + return true; + } + } + + return false; +} + +static int vfio_ub_get_split_cfg_val(struct ub_entity *uent, u64 pos, + struct attrs *attr, __le32 *val, u32 count) +{ + u32 tmp_val = 0; + u8 byte; + u8 ent0eo_byte; + u8 exist_byte; + int ret; + + for (u32 i = 0; i < count; i++) { + ent0eo_byte = (attr->ent0eo >> i * BITS_PER_BYTE) & 0xff; + exist_byte = (attr->exist >> i * BITS_PER_BYTE) & 0xff; + if (vfio_ub_judge_reg_read(uent, ent0eo_byte, exist_byte)) { + ret = ub_cfg_read_byte(uent, pos + i, &byte); + if (!ret) + tmp_val = tmp_val | (((u32)byte) << i * BITS_PER_BYTE); + else + return ret; + } + } + + *val = tmp_val; + + return 0; +} + +static int vfio_ub_default_config_read(struct vfio_ub_core_device *vdev, + u64 pos, int count, __le32 *val); + +static struct perm_bits cap_perms[UB_CFG1_MAX_CAP + 1] = {}; + +static struct perm_bits port_basic_perms = {.readfn = vfio_ub_default_config_read }; + +/* when call this function, cap_id is always exist */ +static struct perm_bits *vfio_ub_find_cap_perm(u32 cap_id) +{ + if (cap_id <= UB_CFG1_MAX_CAP) + return &cap_perms[cap_id]; + + return &port_basic_perms; +} + +static int vfio_ub_user_config_read(struct ub_entity *uent, u64 pos, + __le32 *val, int count) +{ + struct perm_bits *perm; + struct attrs attr = {}; + u32 offset; + u32 cap_id; + + cap_id = UB_CFG_POS_TO_CAP(pos); + perm = vfio_ub_find_cap_perm(cap_id); + offset = pos - UB_CFG_CAP_TO_POS(cap_id); + + memcpy(&attr.ent0eo, perm->ent0eo + offset, count); + memcpy(&attr.exist, perm->exist + offset, count); + + /* quick processing for most situation */ + if (likely(attr.exist == FUNC_EXIST && attr.ent0eo == NO_ENTITY0_EO)) + return vfio_ub_do_user_config_read(uent, pos, val, count); + + /* read cfg at byte unit */ + return vfio_ub_get_split_cfg_val(uent, pos, &attr, val, count); +} + +static u8 *vfio_ub_find_cfg_buf(struct vfio_ub_core_device *vdev, u32 cap_id) +{ + int i; + int num; + + if (cap_id <= UB_CFG1_MAX_CAP) { + num = vdev->vconfig.map[cap_id]; + if (num != CFG_MAP_INVALID) + return vdev->vconfig.slice[num].cfg; + } else { + for (i = 0; i < vdev->vconfig.nums; i++) { + if (vdev->vconfig.slice[i].cap_id == cap_id) + return vdev->vconfig.slice[i].cfg; + } + } + + return NULL; +} + +static int vfio_ub_default_config_read(struct vfio_ub_core_device *vdev, + u64 pos, int count, __le32 *val) +{ + struct perm_bits *perm; + __le32 virt = 0; + u8 *buf; + u32 cap_id; + u32 offset; + + cap_id = UB_CFG_POS_TO_CAP(pos); + offset = pos - UB_CFG_CAP_TO_POS(cap_id); + perm = vfio_ub_find_cap_perm(cap_id); + buf = vfio_ub_find_cfg_buf(vdev, cap_id); + if (buf == NULL) + return -EINVAL; + + memcpy(val, buf + offset, count); + memcpy(&virt, perm->virt + offset, count); + + if (cpu_to_le32(~0U >> (DWORD_BITS - (count * BYTE_BITS))) != virt) { + struct ub_entity *uent = vdev->uent; + __le32 phys_val = 0; + int ret; + + ret = vfio_ub_user_config_read(uent, pos, &phys_val, count); + if (ret) + return ret; + + *val = (phys_val & ~virt) | (*val & virt); + } + + return count; +} + +static int vfio_ub_find_valid_cap(struct vfio_ub_core_device *vdev, u32 cap_id) +{ + int i; + + if (cap_id <= UB_CFG1_MAX_CAP) { + if (vdev->vconfig.map[cap_id] != CFG_MAP_INVALID) + return 0; + } else { + for (i = 0; i < vdev->vconfig.nums; i++) { + if (vdev->vconfig.slice[i].cap_id == cap_id) + return 0; + } + } + + return -ENOENT; +} + +static int vfio_ub_find_cap_used_size(struct vfio_ub_core_device *vdev, u32 cap_id) +{ + int i; + int num; + + if (cap_id <= UB_CFG1_MAX_CAP) { + num = vdev->vconfig.map[cap_id]; + return vdev->vconfig.slice[num].used_size; + } + + for (i = 0; i < vdev->vconfig.nums; i++) { + if (vdev->vconfig.slice[i].cap_id == cap_id) + return vdev->vconfig.slice[i].used_size; + } + + return -ENOENT; +} + +static ssize_t vfio_ub_config_rw_used_zone(struct vfio_ub_core_device *vdev, + char __user *buf, size_t count, + loff_t *ppos, bool iswrite) +{ + size_t cap_start_pos; + struct perm_bits *perm; + size_t left; + u32 cap_id; + __le32 val = 0; + int used_size; + int ret; + + cap_id = UB_CFG_POS_TO_CAP(*ppos); + cap_start_pos = UB_CFG_CAP_TO_POS(cap_id); + used_size = vfio_ub_find_cap_used_size(vdev, cap_id); + + left = cap_start_pos + used_size - *ppos; + + perm = vfio_ub_find_cap_perm(cap_id); + + count = min(count, left); + if (count >= DWORD_SIZE && !(*ppos % DWORD_SIZE)) + count = DWORD_SIZE; + else if (count >= WORD_SIZE && !(*ppos % WORD_SIZE)) + count = WORD_SIZE; + else + count = BYTE_SIZE; + + ret = count; + + if (iswrite) { + if (!perm->writefn) + return ret; + + if (copy_from_user(&val, buf, count)) + return -EFAULT; + + ret = perm->writefn(vdev, *ppos, count, val); + } else { + if (perm->readfn) { + ret = perm->readfn(vdev, *ppos, count, &val); + if (ret < 0) + return ret; + } + + if (copy_to_user(buf, &val, count)) + return -EFAULT; + } + + return ret; +} + +static ssize_t vfio_ub_config_rw_unused_zone(struct vfio_ub_core_device *vdev, + char __user *buf, size_t count, + loff_t *ppos, bool iswrite) +{ + size_t cap_start_pos; + size_t left; + u32 cap_id; + + cap_id = UB_CFG_POS_TO_CAP(*ppos); + cap_start_pos = UB_CFG_CAP_TO_POS(cap_id); + left = cap_start_pos + UB_SLICE_SZ - *ppos; + + count = min(count, left); + if (iswrite) + return count; + + /* read unused zone just return zero */ + if (copy_to_user(buf, zero_array, count)) + return -EFAULT; + + return count; +} + +static ssize_t vfio_ub_config_do_rw(struct vfio_ub_core_device *vdev, + char __user *buf, size_t count, + loff_t *ppos, bool iswrite) +{ + u32 cap_id; + ssize_t ret; + size_t cap_start_pos; + int used_size; + + if (*ppos < 0 || *ppos >= UB_ROUTE_TABLE_SLICE_START || + *ppos + count > UB_ROUTE_TABLE_SLICE_START || + count > UB_SLICE_SZ) + return -EFAULT; + + cap_id = UB_CFG_POS_TO_CAP(*ppos); + + ret = vfio_ub_find_valid_cap(vdev, cap_id); + if (ret < 0) { + ret = vfio_ub_config_rw_unused_zone(vdev, buf, + count, ppos, iswrite); + } else { + cap_start_pos = UB_CFG_CAP_TO_POS(cap_id); + used_size = vfio_ub_find_cap_used_size(vdev, cap_id); + if (used_size < 0) + return -EINVAL; + + if (*ppos < cap_start_pos + used_size) { + ret = vfio_ub_config_rw_used_zone(vdev, buf, + count, ppos, iswrite); + } else { + ret = vfio_ub_config_rw_unused_zone(vdev, buf, + count, ppos, iswrite); + } + } + + return ret; +} + +ssize_t vfio_ub_config_rw(struct vfio_ub_core_device *vdev, char __user *buf, + size_t count, loff_t *ppos, bool iswrite) +{ + size_t done = 0; + int ret = 0; + loff_t pos = *ppos; + + pos &= VFIO_UB_OFFSET_MASK; + + while (count) { + ret = vfio_ub_config_do_rw(vdev, buf, count, &pos, iswrite); + if (ret < 0) + return ret; + + buf += ret; + pos += ret; + count -= ret; + done += ret; + } + + *ppos += done; + + return done; +} diff --git a/drivers/vfio/ubus/vfio_ub_private.h b/drivers/vfio/ubus/vfio_ub_private.h index 8391e9716a62..0758b0134e32 100644 --- a/drivers/vfio/ubus/vfio_ub_private.h +++ b/drivers/vfio/ubus/vfio_ub_private.h @@ -19,9 +19,36 @@ #include #include +#define UB_CFG1_MAX_CAP 511 /* cfg1 cap start from 256 */ + +struct vfio_ub_core_device; +struct perm_bits { + u8 *virt; + u8 *write; + u8 *ent0eo; + u8 *exist; + int (*readfn)(struct vfio_ub_core_device *vdev, u64 pos, int count, + __le32 *val); + int (*writefn)(struct vfio_ub_core_device *vdev, u64 pos, int count, + __le32 val); +}; + +struct vfio_ub_slice { + u8 *cfg; + u32 cap_id; + int used_size; +}; + +struct vfio_ub_config { + struct vfio_ub_slice *slice; + int nums; + int map[UB_CFG1_MAX_CAP + 1]; +}; + struct vfio_ub_core_device { struct vfio_device vdev; struct ub_entity *uent; + struct vfio_ub_config vconfig; int num_regions; /* vfio-ub additional regions */ int num_ext_irqs; /* vfio-ub additional extended irqs */ int num_vendor_regions; /* vfio-ub additional vendor-defined regions */ @@ -35,6 +62,17 @@ struct vfio_ub_core_device { struct mutex igate; }; +#define VFIO_UB_OFFSET_SHIFT 40 +#define VFIO_UB_OFFSET_MASK (((u64)(1) << VFIO_UB_OFFSET_SHIFT) - 1) + +#define BYTE_SIZE 1 +#define WORD_SIZE 2 +#define DWORD_SIZE 4 +#define DWORD_BITS 32 +#define BYTE_BITS 8 + +ssize_t vfio_ub_config_rw(struct vfio_ub_core_device *vdev, char __user *buf, + size_t count, loff_t *ppos, bool iswrite); int vfio_ub_core_register_device(struct vfio_ub_core_device *vdev); void vfio_ub_core_unregister_device(struct vfio_ub_core_device *vdev); int vfio_ub_core_open_device(struct vfio_device *core_vdev); -- Gitee From 604545160e4b4be87351cea24e494e2de5a908a3 Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Tue, 23 Sep 2025 10:41:55 +0800 Subject: [PATCH 060/214] vfio:ubus: Init vfio ub configuration space ANBZ: #45460 commit 67f2a0ec3a608a74b2bad13e4822b1cbbf86e4c0 openEuler. Init vfio ub device's configuration space and init vfio ub's own buffer. Signed-off-by: Yahui Liu Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/vfio/ubus/vfio_ub_config.c | 411 ++++++++++++++++++++++++++++ drivers/vfio/ubus/vfio_ub_core.c | 10 +- drivers/vfio/ubus/vfio_ub_private.h | 4 + 3 files changed, 424 insertions(+), 1 deletion(-) diff --git a/drivers/vfio/ubus/vfio_ub_config.c b/drivers/vfio/ubus/vfio_ub_config.c index 4a902f5c2940..d34eb2d99ebc 100644 --- a/drivers/vfio/ubus/vfio_ub_config.c +++ b/drivers/vfio/ubus/vfio_ub_config.c @@ -16,6 +16,7 @@ #define pr_fmt(fmt) "vfio ub: " fmt #include +#include #include #include "vfio_ub_private.h" @@ -213,6 +214,416 @@ static int vfio_ub_default_config_read(struct vfio_ub_core_device *vdev, return count; } +static u8 vfio_slice_supported[UB_CFG1_MAX_CAP + 1]; + +static int vfio_ub_fill_slice_vconfig(struct vfio_ub_core_device *vdev, + int num, u32 pos, u32 size) +{ + __le32 *dwordp; + u32 val = 0; + int ret; + u32 cap_id; + u32 start = 0; + + cap_id = UB_CFG_POS_TO_CAP(pos); + + for (u32 i = 0; i < size; i += DWORD_SIZE) { + ret = vfio_ub_user_config_read(vdev->uent, pos + i, &val, DWORD_SIZE); + if (ret) { + ub_info(vdev->uent, "read cfgspace pos[%#x] failed, used default val 0\n", + pos + i); + val = 0; + } + dwordp = (__le32 *)(&vdev->vconfig.slice[num].cfg[i + start]); + *dwordp = cpu_to_le32(val); + } + + /* vfio-ub support no port caps for userspace */ + if (cap_id >= UB_PORT0_BASIC_CAP) + memset(&vdev->vconfig.slice[num].cfg[UB_CFG0_PORT_BITMAP], 0, UB_CFG_BITMAP_SIZE); + + return 0; +} + +static struct ub_entity *ub_get_primary_ent(struct ub_entity *uent) +{ + if (uent->entity_idx == 0) + return uent; + + return uent->is_mue ? uent->pue : uent->pue->pue; +} + +static int vfio_ub_init_slice_config_info(struct vfio_ub_core_device *vdev, + u32 cap_id) +{ + struct vfio_ub_slice *pslice, *realloc_slice, *cur_slice; + struct ub_entity *primary_dev; + int used_size; + u32 pos; + int ret; + u32 val; + + pos = UB_CFG_CAP_TO_POS(cap_id); + if (cap_id != UB_PORT0_BASIC_CAP) { + ret = ub_cfg_read_dword(vdev->uent, pos, &val); + } else { + primary_dev = ub_get_primary_ent(vdev->uent); + ret = ub_cfg_read_dword(primary_dev, pos, &val); + } + + if (ret) { + ub_err(vdev->uent, "get cap[%#x] slice header failed\n", cap_id); + return ret; + } + used_size = (val >> SZ_4) << SZ_2; + + if (used_size == 0 || used_size > UB_SLICE_SZ) { + ub_err(vdev->uent, "invalid cap_id[%#x] used_size[%#x]\n", cap_id, used_size); + return -EINVAL; + } + + pslice = vdev->vconfig.slice; + realloc_slice = (struct vfio_ub_slice *)krealloc(vdev->vconfig.slice, + (vdev->vconfig.nums + 1) * + sizeof(struct vfio_ub_slice), GFP_KERNEL); + if (realloc_slice == NULL) + return -ENOMEM; + + vdev->vconfig.slice = realloc_slice; + + cur_slice = vdev->vconfig.slice + vdev->vconfig.nums; + cur_slice->cfg = kzalloc(used_size, GFP_KERNEL); + if (cur_slice->cfg == NULL) { + if (pslice == NULL) { + kfree(vdev->vconfig.slice); + vdev->vconfig.slice = NULL; + } + return -ENOMEM; + } + + cur_slice->used_size = used_size; + cur_slice->cap_id = cap_id; + ret = vfio_ub_fill_slice_vconfig(vdev, vdev->vconfig.nums, pos, used_size); + if (ret) { + kfree(cur_slice->cfg); + if (pslice == NULL) { + kfree(vdev->vconfig.slice); + vdev->vconfig.slice = NULL; + } + return ret; + } + + if (cap_id <= UB_CFG1_MAX_CAP) + vdev->vconfig.map[cap_id] = vdev->vconfig.nums; + vdev->vconfig.nums++; + + return 0; +} + +static int vfio_ub_find_cfg_cap(struct vfio_ub_core_device *vdev, u32 cap_id) +{ + int idx; + + for (idx = 0; idx < vdev->vconfig.nums; idx++) { + if (vdev->vconfig.slice[idx].cap_id == cap_id) + return idx; + } + + return CFG_MAP_INVALID; +} + +static void vfio_ub_uninit_slice_config_info(struct vfio_ub_core_device *vdev, + u32 cap_id) +{ + u32 tmp_cap; + int num; + + if (cap_id <= UB_CFG1_MAX_CAP) { + num = vdev->vconfig.map[cap_id]; + vdev->vconfig.map[cap_id] = CFG_MAP_INVALID; + } else { + num = vfio_ub_find_cfg_cap(vdev, cap_id); + } + + if (num == CFG_MAP_INVALID) + return; + + kfree(vdev->vconfig.slice[num].cfg); + for (int i = num; i < vdev->vconfig.nums - 1; i++) { + memcpy(vdev->vconfig.slice + i, vdev->vconfig.slice + i + 1, + sizeof(struct vfio_ub_slice)); + tmp_cap = vdev->vconfig.slice[i].cap_id; + if (tmp_cap <= UB_CFG1_MAX_CAP) + vdev->vconfig.map[tmp_cap] = i; + } + + vdev->vconfig.nums--; +} + +static int vfio_ub_init_cfg0_basic_info(struct vfio_ub_core_device *vdev) +{ + u8 *cfg0_bitmap_buf; + int cfg0_basic_idx; + int i, j, cap_idx = 0; + int ret; + + ret = vfio_ub_init_slice_config_info(vdev, UB_CFG0_BASIC_CAP); + if (ret) + return ret; + + cfg0_basic_idx = vdev->vconfig.map[UB_CFG0_BASIC_CAP]; + cfg0_bitmap_buf = vdev->vconfig.slice[cfg0_basic_idx].cfg + + UB_CFG0_CAP_BITMAP; + + for (i = 0; i < UB_CFG_BITMAP_SIZE; i++) { + for (j = 0; j < BITS_PER_BYTE; j++) { + if (((cfg0_bitmap_buf[i] >> j) & 1) && + vfio_slice_supported[cap_idx]) { + vdev->vconfig.final_slice_supported[cap_idx] = 1; + } else if (((cfg0_bitmap_buf[i] >> j) & 1) && + vfio_slice_supported[cap_idx] == 0) { + cfg0_bitmap_buf[i] = cfg0_bitmap_buf[i] & + ~(1 << j); + } + cap_idx++; + } + } + + return 0; +} + +static void vfio_ub_uninit_cfg0_basic_info(struct vfio_ub_core_device *vdev) +{ + u32 cap_id; + + for (cap_id = 0; cap_id <= UB_CFG0_MAX_CAP; cap_id++) { + if (vdev->vconfig.final_slice_supported[cap_id] == 1) + vdev->vconfig.final_slice_supported[cap_id] = 0; + } + + vfio_ub_uninit_slice_config_info(vdev, UB_CFG0_BASIC_CAP); +} + +static int vfio_ub_init_cfg0_cap_info(struct vfio_ub_core_device *vdev) +{ + int cap_id; + int ret; + + for (cap_id = 1; cap_id <= UB_CFG0_MAX_CAP; cap_id++) { + if (vdev->vconfig.final_slice_supported[cap_id]) { + ret = vfio_ub_init_slice_config_info(vdev, (u32)cap_id); + if (ret) + goto cfg0_cap_init_err; + } + } + + return 0; + +cfg0_cap_init_err: + for (cap_id = cap_id - 1; cap_id >= 1; cap_id--) + vfio_ub_uninit_slice_config_info(vdev, cap_id); + return ret; +} + +static void vfio_ub_uninit_cfg0_cap_info(struct vfio_ub_core_device *vdev) +{ + u32 cap_id; + + for (cap_id = 1; cap_id <= UB_CFG0_MAX_CAP; cap_id++) { + if (vdev->vconfig.final_slice_supported[cap_id]) + vfio_ub_uninit_slice_config_info(vdev, cap_id); + } +} + +static int vfio_ub_init_cfg1_basic_info(struct vfio_ub_core_device *vdev) +{ + u32 cap_idx = UB_CFG1_BASIC_CAP; + u8 *cfg1_bitmap_buf; + int cfg1_basic_idx; + int i, j; + int ret; + + ret = vfio_ub_init_slice_config_info(vdev, UB_CFG1_BASIC_CAP); + if (ret) + return ret; + + cfg1_basic_idx = vdev->vconfig.map[UB_CFG1_BASIC_CAP]; + cfg1_bitmap_buf = vdev->vconfig.slice[cfg1_basic_idx].cfg + + UB_CFG1_CAP_BITMAP - UB_CFG1_BASIC; + + for (i = 0; i < UB_CFG_BITMAP_SIZE; i++) { + for (j = 0; j < BITS_PER_BYTE; j++) { + if (((cfg1_bitmap_buf[i] >> j) & 1) && + vfio_slice_supported[cap_idx]) { + vdev->vconfig.final_slice_supported[cap_idx] = 1; + } else if (((cfg1_bitmap_buf[i] >> j) & 1) && + vfio_slice_supported[cap_idx] == 0) { + cfg1_bitmap_buf[i] = cfg1_bitmap_buf[i] & + ~(1 << j); + } + cap_idx++; + } + } + + return 0; +} + +static void vfio_ub_uninit_cfg1_basic_info(struct vfio_ub_core_device *vdev) +{ + u32 cap_id; + + for (cap_id = UB_DECODER_CAP; cap_id <= UB_CFG1_MAX_CAP; cap_id++) { + if (vdev->vconfig.final_slice_supported[cap_id] == 1) + vdev->vconfig.final_slice_supported[cap_id] = 0; + } + + vfio_ub_uninit_slice_config_info(vdev, UB_CFG1_BASIC_CAP); +} + +static int vfio_ub_init_cfg1_cap_info(struct vfio_ub_core_device *vdev) +{ + u32 cap_id; + int ret; + + for (cap_id = UB_DECODER_CAP; cap_id <= UB_CFG1_MAX_CAP; cap_id++) { + if (vdev->vconfig.final_slice_supported[cap_id]) { + ret = vfio_ub_init_slice_config_info(vdev, cap_id); + if (ret) + goto cfg1_cap_init_err; + } + } + + return 0; + +cfg1_cap_init_err: + for (cap_id = cap_id - 1; cap_id >= UB_DECODER_CAP; cap_id--) + vfio_ub_uninit_slice_config_info(vdev, cap_id); + return ret; +} + +static void vfio_ub_uninit_cfg1_cap_info(struct vfio_ub_core_device *vdev) +{ + u32 cap_id; + + for (cap_id = UB_DECODER_CAP; cap_id <= UB_CFG1_MAX_CAP; cap_id++) { + if (vdev->vconfig.final_slice_supported[cap_id]) + vfio_ub_uninit_slice_config_info(vdev, cap_id); + } +} + +static void vfio_ub_init_port_cap_bitmap(struct vfio_ub_core_device *vdev, int idx) +{ + /* now we do not support any port cap */ + memset(vdev->vconfig.slice[idx].cfg + UB_CFG0_PORT_BITMAP, + 0, UB_CFG_BITMAP_SIZE); +} + +static int vfio_ub_init_port_basic_info(struct vfio_ub_core_device *vdev) +{ + int cfg0_basic_idx = vdev->vconfig.map[UB_CFG0_BASIC_CAP]; + u16 total_port_nums; + __le16 *wordp; + int ret; + int idx; + + wordp = (__le16 *)(vdev->vconfig.slice[cfg0_basic_idx].cfg + + UB_TOTAL_NUMBER_PORT); + total_port_nums = le16_to_cpu(*wordp); + + for (idx = 0; idx < total_port_nums; idx++) { + ret = vfio_ub_init_slice_config_info(vdev, UB_PORT0_BASIC_CAP + + idx * UB_PORT_CAP_GAP); + if (ret) + goto port_basic_init_err; + vfio_ub_init_port_cap_bitmap(vdev, vdev->vconfig.nums - 1); + } + + return 0; + +port_basic_init_err: + for (idx = idx - 1; idx >= 0; idx--) + vfio_ub_uninit_slice_config_info(vdev, UB_PORT0_BASIC_CAP + + idx * UB_PORT_CAP_GAP); + return ret; +} + +static void vfio_ub_uninit_port_basic_info(struct vfio_ub_core_device *vdev) +{ + int cfg0_basic_idx = vdev->vconfig.map[UB_CFG0_BASIC_CAP]; + u16 total_port_nums; + u16 idx; + __le16 *wordp; + + wordp = (__le16 *)(vdev->vconfig.slice[cfg0_basic_idx].cfg + + UB_TOTAL_NUMBER_PORT); + total_port_nums = le16_to_cpu(*wordp); + for (idx = 0; idx < total_port_nums; idx++) + vfio_ub_uninit_slice_config_info(vdev, UB_PORT0_BASIC_CAP + + idx * UB_PORT_CAP_GAP); +} + +int vfio_ub_config_init(struct vfio_ub_core_device *vdev) +{ + int ret; + + memset(vdev->vconfig.map, CFG_MAP_INVALID, + (UB_CFG1_MAX_CAP + 1) * sizeof(int)); + + ret = vfio_ub_init_cfg0_basic_info(vdev); + if (ret) + return ret; + + ret = vfio_ub_init_cfg0_cap_info(vdev); + if (ret) + goto out_uninit_cfg0_basic; + + ret = vfio_ub_init_cfg1_basic_info(vdev); + if (ret) + goto out_uninit_cfg0_cap; + + ret = vfio_ub_init_cfg1_cap_info(vdev); + if (ret) + goto out_uninit_cfg1_basic; + + ret = vfio_ub_init_port_basic_info(vdev); + if (ret) + goto out_uninit_cfg1_cap; + + return 0; + +out_uninit_cfg1_cap: + vfio_ub_uninit_cfg1_cap_info(vdev); +out_uninit_cfg1_basic: + vfio_ub_uninit_cfg1_basic_info(vdev); +out_uninit_cfg0_cap: + vfio_ub_uninit_cfg0_cap_info(vdev); +out_uninit_cfg0_basic: + vfio_ub_uninit_cfg0_basic_info(vdev); + + memset(vdev->vconfig.map, CFG_MAP_INVALID, (UB_CFG1_MAX_CAP + 1) * sizeof(int)); + kfree(vdev->vconfig.slice); + vdev->vconfig.slice = NULL; + vdev->vconfig.nums = 0; + return ret; +} + +void vfio_ub_config_uninit(struct vfio_ub_core_device *vdev) +{ + if (vdev->vconfig.slice == NULL) + return; + + vfio_ub_uninit_port_basic_info(vdev); + vfio_ub_uninit_cfg1_cap_info(vdev); + vfio_ub_uninit_cfg1_basic_info(vdev); + vfio_ub_uninit_cfg0_cap_info(vdev); + vfio_ub_uninit_cfg0_basic_info(vdev); + + memset(vdev->vconfig.map, CFG_MAP_INVALID, (UB_CFG1_MAX_CAP + 1) * sizeof(int)); + kfree(vdev->vconfig.slice); + vdev->vconfig.slice = NULL; + vdev->vconfig.nums = 0; +} + static int vfio_ub_find_valid_cap(struct vfio_ub_core_device *vdev, u32 cap_id) { int i; diff --git a/drivers/vfio/ubus/vfio_ub_core.c b/drivers/vfio/ubus/vfio_ub_core.c index 2445b39de0c1..621d50b70c5b 100644 --- a/drivers/vfio/ubus/vfio_ub_core.c +++ b/drivers/vfio/ubus/vfio_ub_core.c @@ -96,17 +96,25 @@ static int vfio_usi_info_init(struct vfio_ub_core_device *vdev) static int vfio_ub_enable(struct vfio_ub_core_device *vdev) { + int ret; + vdev->num_ext_irqs = 0; vdev->num_regions = 0; vdev->num_vendor_irqs = 0; vdev->num_vendor_regions = 0; vdev->reset_works = 1; /* we assume ub support ELR for now. */ - return vfio_usi_info_init(vdev); + ret = vfio_usi_info_init(vdev); + if (ret) + return ret; + + return vfio_ub_config_init(vdev); } static void vfio_ub_disable(struct vfio_ub_core_device *vdev) { + vfio_ub_config_uninit(vdev); + /* clear device caches when vm exit or crash */ if (vdev->reset_works) ub_reset_entity(vdev->uent); diff --git a/drivers/vfio/ubus/vfio_ub_private.h b/drivers/vfio/ubus/vfio_ub_private.h index 0758b0134e32..56d358e0d7ed 100644 --- a/drivers/vfio/ubus/vfio_ub_private.h +++ b/drivers/vfio/ubus/vfio_ub_private.h @@ -19,6 +19,7 @@ #include #include +#define UB_CFG0_MAX_CAP 255 #define UB_CFG1_MAX_CAP 511 /* cfg1 cap start from 256 */ struct vfio_ub_core_device; @@ -42,6 +43,7 @@ struct vfio_ub_slice { struct vfio_ub_config { struct vfio_ub_slice *slice; int nums; + u8 final_slice_supported[UB_CFG1_MAX_CAP + 1]; /* do not incude port basic && port cap */ int map[UB_CFG1_MAX_CAP + 1]; }; @@ -71,6 +73,8 @@ struct vfio_ub_core_device { #define DWORD_BITS 32 #define BYTE_BITS 8 +int vfio_ub_config_init(struct vfio_ub_core_device *vdev); +void vfio_ub_config_uninit(struct vfio_ub_core_device *vdev); ssize_t vfio_ub_config_rw(struct vfio_ub_core_device *vdev, char __user *buf, size_t count, loff_t *ppos, bool iswrite); int vfio_ub_core_register_device(struct vfio_ub_core_device *vdev); -- Gitee From a317e525e53f50c80a9b942613ebd737f4108228 Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Mon, 22 Sep 2025 16:20:05 +0800 Subject: [PATCH 061/214] ub:ubfi: register ubc usi domain ANBZ: #45460 commit 028a4161a77d2e15d331a13599a6f06cdd7490b6 openEuler. get usi domain from acpi or dts, and register it to ubc device Signed-off-by: Yuhao Xiang Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubfi/irq.c | 31 +++++++++++++++++++++ drivers/ub/ubfi/ubc.c | 63 +++++++++++++++++++++++++++++++++++++++++- include/ub/ubfi/ubfi.h | 14 ++++++++++ 3 files changed, 107 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubfi/irq.c b/drivers/ub/ubfi/irq.c index 16b18db3af65..d47e4ce67bd6 100644 --- a/drivers/ub/ubfi/irq.c +++ b/drivers/ub/ubfi/irq.c @@ -9,6 +9,37 @@ #include #include +int ub_update_msi_domain(struct device *dev, + enum irq_domain_bus_token bus_token) +{ + struct fwnode_handle *fwnode; + struct irq_domain *domain; + + domain = dev->msi.domain; + if (!domain) { + dev_err(dev, "find base irq domain failed!\n"); + return -ENODEV; + } + + fwnode = domain->fwnode; + if (!fwnode) { + dev_err(dev, "find fwnode failed!\n"); + return -ENODEV; + } + + domain = irq_find_matching_fwnode(fwnode, bus_token); + if (!domain) { + dev_err(dev, "find irq domain failed!\n"); + return -ENODEV; + } + + /* Update msi domain with new bus_token */ + dev_set_msi_domain(dev, domain); + + return 0; +} +EXPORT_SYMBOL_GPL(ub_update_msi_domain); + int ubrt_register_gsi(u32 hwirq, int trigger, int polarity, const char *name, struct resource *res) { diff --git a/drivers/ub/ubfi/ubc.c b/drivers/ub/ubfi/ubc.c index 03fe0ead069a..e89aeeafb913 100644 --- a/drivers/ub/ubfi/ubc.c +++ b/drivers/ub/ubfi/ubc.c @@ -51,7 +51,41 @@ static bool cluster_mode; static acpi_status acpi_processor_ubc(acpi_handle handle, u32 lvl, void *context, void **rv) { - return AE_OK; + struct ub_bus_controller *ubc = context; + struct acpi_device *adev; + unsigned long long uid; + acpi_status status; + struct device *dev; + int ret; + + status = acpi_evaluate_integer(handle, "_UID", NULL, &uid); + if (ACPI_FAILURE(status)) + return AE_CTRL_TERMINATE; + + pr_info("ubc acpi ubc->ctl_no %u, uid: %llu\n", ubc->ctl_no, uid); + if (ubc->ctl_no != (u32)uid) + return AE_OK; + + adev = acpi_get_acpi_dev(handle); + if (!adev) + return AE_CTRL_TERMINATE; + + dev = bus_find_device_by_acpi_dev(&platform_bus_type, adev); + if (!dev) { + status = AE_CTRL_TERMINATE; + goto out; + } + ret = ub_update_msi_domain(dev, DOMAIN_BUS_UB_MSI); + if (ret) { + status = AE_CTRL_TERMINATE; + goto out; + } + dev_set_msi_domain(&ubc->dev, dev->msi.domain); + pr_debug("set ubc msi domain success by acpi\n"); + +out: + acpi_put_acpi_dev(adev); + return status; } static int acpi_update_ubc_msi_domain(void) @@ -71,10 +105,28 @@ static int acpi_update_ubc_msi_domain(void) return 0; } +static struct irq_domain *of_usi_get_domain(struct device_node *np, + enum irq_domain_bus_token token) +{ + struct device_node *usi_np; + struct irq_domain *d; + + usi_np = of_parse_phandle(np, "msi-parent", 0); + if (!usi_np) + return NULL; + + d = irq_find_matching_host(usi_np, token); + if (!d) + of_node_put(usi_np); + + return d; +} + static int dts_update_ubc_msi_domain(void) { struct ub_bus_controller *ubc; struct device_node *np; + struct irq_domain *d; u32 ctl_no; bool find; int ret; @@ -97,6 +149,15 @@ static int dts_update_ubc_msi_domain(void) pr_err("can't find ubc no=%u\n", ctl_no); continue; } + + d = of_usi_get_domain(np, DOMAIN_BUS_UB_MSI); + if (!d) { + pr_err("can't find ub irq domain\n"); + continue; + } + + dev_set_msi_domain(&ubc->dev, d); + pr_debug("set ubc[%u] msi domain success\n", ctl_no); } return 0; } diff --git a/include/ub/ubfi/ubfi.h b/include/ub/ubfi/ubfi.h index 425d59102a1a..5867311b10a6 100644 --- a/include/ub/ubfi/ubfi.h +++ b/include/ub/ubfi/ubfi.h @@ -99,6 +99,20 @@ int ubrt_register_gsi(u32 hwirq, int trigger, int polarity, const char *name, */ void ubrt_unregister_gsi(u32 hwirq); +/** + * ub_update_msi_domain() - Update the MSI domain of UBC + * @dev: device with ub msi domain + * @bus_token: DOMAIN_BUS_UB_MSI + * + * Used when booting via ACPI. The MSI domain of the UB is reported by a + * platform device to the driver, and this function passes the MSI domain of the + * platform device to the UBC. + * + * Return: 0 if success or other if failed + */ +int ub_update_msi_domain(struct device *dev, + enum irq_domain_bus_token bus_token); + /** * ubrt_fwnode_set() - Associate a device's fwnode with an UBRT node * @index: Index of the UBRT node within its type. -- Gitee From bca05cf7a6a07485a777625da1f53afc217776fc Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Tue, 23 Sep 2025 11:53:17 +0800 Subject: [PATCH 062/214] vfio:ubus: Init vfio ub config space 0 perm bits during module init ANBZ: #45460 commit 61a1ca5cb417a3e8517c4bc7ff9a412fa463eb0e openEuler. Init vfio ub config space 0 perm bits during module init. For the configuration space registers supported by vfio ub, the read and write permissions for each register are controlled through the virt and write fields in the struct perm_bits structure. Signed-off-by: Yahui Liu Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/vfio/ubus/vfio_ub.c | 6 + drivers/vfio/ubus/vfio_ub_config.c | 386 ++++++++++++++++++++++++++++ drivers/vfio/ubus/vfio_ub_private.h | 3 + 3 files changed, 395 insertions(+) diff --git a/drivers/vfio/ubus/vfio_ub.c b/drivers/vfio/ubus/vfio_ub.c index 09aae7a98aa6..22cf01765e0b 100644 --- a/drivers/vfio/ubus/vfio_ub.c +++ b/drivers/vfio/ubus/vfio_ub.c @@ -83,6 +83,10 @@ static int __init vfio_ub_init(void) { int ret; + ret = vfio_ub_init_perm_bits(); + if (ret) + return ret; + ret = ub_register_driver(&vfio_ub_driver); if (ret) goto out_driver; @@ -90,12 +94,14 @@ static int __init vfio_ub_init(void) return 0; out_driver: + vfio_ub_uninit_perm_bits(); return ret; } static void __exit vfio_ub_exit(void) { ub_unregister_driver(&vfio_ub_driver); + vfio_ub_uninit_perm_bits(); } module_init(vfio_ub_init); diff --git a/drivers/vfio/ubus/vfio_ub_config.c b/drivers/vfio/ubus/vfio_ub_config.c index d34eb2d99ebc..34b5674cfe3f 100644 --- a/drivers/vfio/ubus/vfio_ub_config.c +++ b/drivers/vfio/ubus/vfio_ub_config.c @@ -16,13 +16,20 @@ #define pr_fmt(fmt) "vfio ub: " fmt #include +#include #include #include #include "vfio_ub_private.h" +#define NO_VIRT 0 +#define ALL_VIRT 0xFFFFFFFFU +#define NO_WRITE 0 +#define ALL_WRITE 0xFFFFFFFFU #define NO_ENTITY0_EO 0 #define ENTITY0_EO 0xFFFFFFFFU +#define NO_ENTITYN_RO 0 +#define ENTITYN_RO 0xFFFFFFFFU /* Now, this attr control just support at byte granularity */ enum { @@ -34,6 +41,7 @@ enum { struct attrs { __le32 ent0eo; + __le32 entnro; __le32 exist; }; @@ -47,6 +55,48 @@ struct attrs { static const u8 zero_array[UB_SLICE_SZ] = {}; +/* Handle endian-ness - ub and tables are little-endian */ +static inline void p_setb(struct perm_bits *p, int off, u8 virt, u8 write) +{ + p->virt[off] = virt; + p->write[off] = write; +} + +/* Handle endian-ness - ub and tables are little-endian */ +static inline void p_setw(struct perm_bits *p, int off, u16 virt, u16 write) +{ + *(__le16 *)(&p->virt[off]) = cpu_to_le16(virt); + *(__le16 *)(&p->write[off]) = cpu_to_le16(write); +} + +/* Handle endian-ness - ub and tables are little-endian */ +static inline void p_setd(struct perm_bits *p, int off, u32 virt, u32 write) +{ + *(__le32 *)(&p->virt[off]) = cpu_to_le32(virt); + *(__le32 *)(&p->write[off]) = cpu_to_le32(write); +} + +static inline void p_attr_setb(struct perm_bits *p, int off, u8 ent0eo, u8 entnro, u8 exist) +{ + p->ent0eo[off] = ent0eo; + p->entnro[off] = entnro; + p->exist[off] = exist; +} + +static inline void p_attr_setw(struct perm_bits *p, int off, u16 ent0eo, u16 entnro, u16 exist) +{ + *(__le16 *)(&p->ent0eo[off]) = cpu_to_le16(ent0eo); + *(__le16 *)(&p->entnro[off]) = cpu_to_le16(entnro); + *(__le16 *)(&p->exist[off]) = cpu_to_le16(exist); +} + +static inline void p_attr_setd(struct perm_bits *p, int off, u32 ent0eo, u32 entnro, u32 exist) +{ + *(__le32 *)(&p->ent0eo[off]) = cpu_to_le32(ent0eo); + *(__le32 *)(&p->entnro[off]) = cpu_to_le32(entnro); + *(__le32 *)(&p->exist[off]) = cpu_to_le32(exist); +} + static int vfio_ub_do_user_config_read(struct ub_entity *uent, u64 pos, __le32 *val, int count) { @@ -96,6 +146,18 @@ static bool vfio_ub_judge_reg_read(struct ub_entity *uent, u8 ent0eo, u8 exist) return false; } +static bool vfio_ub_judge_reg_write(struct ub_entity *uent, u8 ent0eo, u8 exist, u8 entnro) +{ + if (vfio_ub_judge_type(uent, exist)) { + if (((uent->entity_idx != 0) && (ent0eo == NO_ENTITY0_EO) && + (entnro == NO_ENTITYN_RO)) || uent->entity_idx == 0) { + return true; + } + } + + return false; +} + static int vfio_ub_get_split_cfg_val(struct ub_entity *uent, u64 pos, struct attrs *attr, __le32 *val, u32 count) { @@ -122,6 +184,31 @@ static int vfio_ub_get_split_cfg_val(struct ub_entity *uent, u64 pos, return 0; } +static int vfio_ub_set_split_cfg_val(struct ub_entity *uent, u64 pos, + struct attrs *attr, __le32 val, u32 count) +{ + u8 byte; + u8 ent0eo_byte; + u8 exist_byte; + u8 entnro_byte; + int ret; + + for (int i = 0; i < count; i++) { + ent0eo_byte = (attr->ent0eo >> i * BITS_PER_BYTE) & 0xff; + exist_byte = (attr->exist >> i * BITS_PER_BYTE) & 0xff; + entnro_byte = (attr->entnro >> i * BITS_PER_BYTE) & 0xff; + if (vfio_ub_judge_reg_write(uent, ent0eo_byte, exist_byte, + entnro_byte)) { + byte = val >> i * BITS_PER_BYTE; + ret = ub_cfg_write_byte(uent, pos + i, byte); + if (ret) + return ret; + } + } + + return 0; +} + static int vfio_ub_default_config_read(struct vfio_ub_core_device *vdev, u64 pos, int count, __le32 *val); @@ -161,6 +248,53 @@ static int vfio_ub_user_config_read(struct ub_entity *uent, u64 pos, return vfio_ub_get_split_cfg_val(uent, pos, &attr, val, count); } +static int vfio_ub_do_user_config_write(struct ub_entity *uent, u64 pos, + __le32 val, int count) +{ + int ret = -EINVAL; + u32 tmp_val = le32_to_cpu(val); + + switch (count) { + case BYTE_SIZE: + ret = ub_cfg_write_byte(uent, pos, tmp_val); + break; + case WORD_SIZE: + ret = ub_cfg_write_word(uent, pos, tmp_val); + break; + case DWORD_SIZE: + ret = ub_cfg_write_dword(uent, pos, tmp_val); + break; + default: + break; + } + + return ret; +} + +static int vfio_ub_user_config_write(struct ub_entity *uent, u64 pos, + __le32 val, int count) +{ + struct perm_bits *perm; + struct attrs attr = {}; + u32 offset; + u32 cap_id; + + cap_id = UB_CFG_POS_TO_CAP(pos); + perm = vfio_ub_find_cap_perm(cap_id); + offset = pos - UB_CFG_CAP_TO_POS(cap_id); + + memcpy(&attr.ent0eo, perm->ent0eo + offset, count); + memcpy(&attr.exist, perm->exist + offset, count); + memcpy(&attr.entnro, perm->entnro + offset, count); + + /* quick processing for most situation */ + if (likely(attr.exist == FUNC_EXIST && attr.ent0eo == NO_ENTITY0_EO + && attr.entnro == NO_ENTITYN_RO)) + return vfio_ub_do_user_config_write(uent, pos, val, count); + + return vfio_ub_set_split_cfg_val(uent, pos, &attr, val, count); +} + static u8 *vfio_ub_find_cfg_buf(struct vfio_ub_core_device *vdev, u32 cap_id) { int i; @@ -214,7 +348,259 @@ static int vfio_ub_default_config_read(struct vfio_ub_core_device *vdev, return count; } +static __le32 vfio_ub_get_virt_write_mask(struct ub_entity *uent, u64 pos, int count) +{ + u8 ent0eo_byte, exist_byte, entnro_byte; + struct perm_bits *perm; + __le32 tmp_val = 0; + u32 cap_id; + u32 offset; + + cap_id = UB_CFG_POS_TO_CAP(pos); + offset = pos - UB_CFG_CAP_TO_POS(cap_id); + perm = vfio_ub_find_cap_perm(cap_id); + + for (int i = 0; i < count; i++) { + /* register attrs control at byte unit */ + ent0eo_byte = perm->ent0eo[offset + i]; + exist_byte = perm->exist[offset + i]; + entnro_byte = perm->entnro[offset + i]; + + if (vfio_ub_judge_reg_write(uent, ent0eo_byte, exist_byte, + entnro_byte)) + tmp_val |= 0xff << (i * BITS_PER_BYTE); + } + + return tmp_val; +} + +static int vfio_ub_default_config_write(struct vfio_ub_core_device *vdev, + u64 pos, int count, __le32 val) +{ + __le32 virt = 0, write = 0, mask = 0; + struct perm_bits *perm; + u8 *buf; + u32 cap_id; + u32 offset; + + cap_id = UB_CFG_POS_TO_CAP(pos); + offset = pos - UB_CFG_CAP_TO_POS(cap_id); + perm = vfio_ub_find_cap_perm(cap_id); + buf = vfio_ub_find_cfg_buf(vdev, cap_id); + if (buf == NULL) + return -EINVAL; + + memcpy(&write, perm->write + offset, count); + + if (!write) + return count; /* just drop it, no writable bits */ + + memcpy(&virt, perm->virt + offset, count); + + if (write & virt) { + __le32 virt_val = 0; + + mask = vfio_ub_get_virt_write_mask(vdev->uent, pos, count); + memcpy(&virt_val, buf + offset, count); + virt_val &= ~(write & virt & mask); + virt_val |= (val & (write & virt & mask)); + + memcpy(buf + offset, &virt_val, count); + } + + /* Non-virtualzed and writable bits go to hardware */ + if (write & ~virt) { + struct ub_entity *uent = vdev->uent; + __le32 phys_val = 0; + int ret; + + ret = vfio_ub_user_config_read(uent, pos, &phys_val, count); + if (ret) + return ret; + + phys_val &= ~(write & ~virt); + phys_val |= (val & (write & ~virt)); + + ret = vfio_ub_user_config_write(uent, pos, phys_val, count); + if (ret) + return ret; + } + + return count; +} + +static void free_perm_bits(struct perm_bits *perm) +{ + vfree(perm->exist); + vfree(perm->entnro); + vfree(perm->ent0eo); + vfree(perm->write); + vfree(perm->virt); + perm->exist = NULL; + perm->entnro = NULL; + perm->ent0eo = NULL; + perm->virt = NULL; + perm->write = NULL; +} + +static int alloc_perm_bits(struct perm_bits *perm, int size) +{ + int tmp; + + tmp = round_up(size, DWORD_SIZE); + + /* + * Zero state is + * - All Readable, None Writeable, None Virtualized + */ + perm->virt = vzalloc(tmp); + perm->write = vzalloc(tmp); + perm->ent0eo = vzalloc(tmp); + perm->entnro = vzalloc(tmp); + perm->exist = vzalloc(tmp); + if (!perm->virt || !perm->write || !perm->ent0eo || + !perm->entnro || !perm->exist) { + free_perm_bits(perm); + return -ENOMEM; + } + + perm->readfn = vfio_ub_default_config_read; + perm->writefn = vfio_ub_default_config_write; + + return 0; +} + +static void init_ub_cfg_chunk_perms(struct perm_bits *perm, u32 pos, + u32 size, u32 virt, u32 write) +{ + for (u32 idx = 0; idx < size; idx++) + p_setd(perm, pos + idx * DWORD_SIZE, virt, write); +} + +static void init_ub_cfg_chunk_attrs(struct perm_bits *perm, u32 pos, u32 size, + u32 ent0eo, u32 entnro, u32 exist) +{ + for (u32 idx = 0; idx < size; idx++) + p_attr_setd(perm, pos + idx * DWORD_SIZE, ent0eo, entnro, exist); +} + +static int vfio_ub_cfg0_basic_read(struct vfio_ub_core_device *vdev, u64 pos, + int count, __le32 *val) +{ + count = vfio_ub_default_config_read(vdev, pos, count, val); + + return count; +} + +static int vfio_ub_cfg0_basic_write(struct vfio_ub_core_device *vdev, u64 pos, + int count, __le32 val) +{ + count = vfio_ub_default_config_write(vdev, pos, count, val); + + return count; +} + +static void init_ub_cfg0_basic_operation_perm(struct perm_bits *perm) +{ + p_setd(perm, UB_CFG0_BASIC_SLICE, ALL_VIRT, NO_WRITE); + p_setd(perm, UB_TOTAL_NUMBER_PORT, ALL_VIRT, NO_WRITE); + + init_ub_cfg_chunk_perms(perm, UB_CFG0_CAP_BITMAP, + UB_CFG_BITMAP_SIZE / DWORD_SIZE, + ALL_VIRT, NO_WRITE); + init_ub_cfg_chunk_perms(perm, UB_FEATURE_SUPPORT_0, + UB_CFG0_SUPPORT_FEATURE_SIZE / DWORD_SIZE, + ALL_VIRT, NO_WRITE); + init_ub_cfg_chunk_perms(perm, UB_GUID, UB_GUID_SIZE / DWORD_SIZE, + ALL_VIRT, NO_WRITE); + init_ub_cfg_chunk_perms(perm, UB_EID_0, UB_EID_SIZE / DWORD_SIZE, + ALL_VIRT, ALL_WRITE); + init_ub_cfg_chunk_perms(perm, UB_FM_EID_0, UB_FM_EID_SIZE / DWORD_SIZE, + ALL_VIRT, ALL_WRITE); + + p_setd(perm, UB_PRIMARY_CNA, UB_PRIMARY_CNA_MASK, UB_PRIMARY_CNA_MASK); + p_setd(perm, UB_UEID_0, ALL_VIRT, ALL_WRITE); + p_setd(perm, UB_UEID_1, ALL_VIRT, ALL_WRITE); + p_setd(perm, UB_UEID_2, ALL_VIRT, ALL_WRITE); + p_setd(perm, UB_UEID_3, ALL_VIRT, ALL_WRITE); + p_setd(perm, UB_UCNA, UB_UCNA_MASK, UB_UCNA_MASK); + + p_setw(perm, UB_UPI, (u16)ALL_VIRT, (u16)ALL_WRITE); + p_setd(perm, UB_MODULE, ALL_VIRT, NO_WRITE); + p_setb(perm, UB_ENTITY_RST, (u8)ALL_VIRT, UB_ENTITY_RST_BIT); + p_setb(perm, UB_MTU_CFG, (u8)ALL_VIRT, UB_MTU_CFG_BITS); + p_setb(perm, UB_CC_EN, (u8)ALL_VIRT, UB_CC_EN_BIT); + p_setb(perm, UB_TH_EN, (u8)ALL_VIRT, UB_TH_EN_BIT); + p_setd(perm, UB_FM_CNA, UB_FM_CNA_MASK, UB_FM_CNA_MASK); +} + +static void init_ub_cfg0_basic_attr_perm(struct perm_bits *perm) +{ + p_attr_setw(perm, UB_TOTAL_NUMBER_ENTITIES, (u16)ENTITY0_EO, NO_ENTITYN_RO, FUNC_EXIST); + init_ub_cfg_chunk_attrs(perm, UB_FM_EID_0, UB_FM_EID_SIZE / DWORD_SIZE, + ENTITY0_EO, NO_ENTITYN_RO, FUNC_EXIST); + + p_attr_setd(perm, UB_PRIMARY_CNA, NO_ENTITY0_EO, ENTITYN_RO, FUNC_EXIST); + p_attr_setd(perm, UB_PRIMARY_CNA + 0x4, NO_ENTITY0_EO, ENTITYN_RO, FUNC_NO_EXIST); + p_attr_setd(perm, UB_PRIMARY_CNA + 0x8, NO_ENTITY0_EO, ENTITYN_RO, FUNC_NO_EXIST); + p_attr_setd(perm, UB_PRIMARY_CNA + 0xc, NO_ENTITY0_EO, ENTITYN_RO, FUNC_NO_EXIST); + p_attr_setd(perm, UB_PRIMARY_CNA + 0x10, NO_ENTITY0_EO, ENTITYN_RO, FUNC_NO_EXIST); + + p_attr_setd(perm, UB_ENTITY_RST, ENTITY0_EO, NO_ENTITYN_RO, FUNC_EXIST); + p_attr_setb(perm, UB_MTU_CFG, (u8)NO_ENTITY0_EO, (u8)ENTITYN_RO, (u8)FUNC_EXIST); + p_attr_setb(perm, UB_CC_EN, (u8)NO_ENTITY0_EO, (u8)ENTITYN_RO, (u8)FUNC_EXIST); + p_attr_setd(perm, UB_TH_EN, ENTITY0_EO, NO_ENTITYN_RO, FUNC_EXIST); + p_attr_setd(perm, UB_FM_CNA, ENTITY0_EO, NO_ENTITYN_RO, FUNC_EXIST); +} + +static int init_ub_cfg0_basic_perm(struct perm_bits *perm) +{ + if (alloc_perm_bits(perm, UB_SLICE_SZ)) + return -ENOMEM; + + perm->readfn = vfio_ub_cfg0_basic_read; + perm->writefn = vfio_ub_cfg0_basic_write; + + init_ub_cfg0_basic_operation_perm(perm); + init_ub_cfg0_basic_attr_perm(perm); + return 0; +} + +static int init_ub_cfg0_cap_perm(struct perm_bits *perm) +{ + return 0; +} + static u8 vfio_slice_supported[UB_CFG1_MAX_CAP + 1]; +static void vfio_ub_init_slice_supported(void) +{ + vfio_slice_supported[UB_CFG0_BASIC_CAP] = 1; + vfio_slice_supported[UB_CFG1_BASIC_CAP] = 1; + vfio_slice_supported[UB_INT_TYPE2_CAP] = 1; +} + +int __init vfio_ub_init_perm_bits(void) +{ + int ret; + + vfio_ub_init_slice_supported(); + + ret = init_ub_cfg0_basic_perm(&cap_perms[UB_CFG0_BASIC_CAP]); + ret |= init_ub_cfg0_cap_perm(cap_perms); + + if (ret) + vfio_ub_uninit_perm_bits(); + + return ret; +} + +void vfio_ub_uninit_perm_bits(void) +{ + free_perm_bits(&port_basic_perms); + free_perm_bits(&cap_perms[UB_INT_TYPE2_CAP]); + free_perm_bits(&cap_perms[UB_CFG1_BASIC_CAP]); + free_perm_bits(&cap_perms[UB_CFG0_BASIC_CAP]); +} static int vfio_ub_fill_slice_vconfig(struct vfio_ub_core_device *vdev, int num, u32 pos, u32 size) diff --git a/drivers/vfio/ubus/vfio_ub_private.h b/drivers/vfio/ubus/vfio_ub_private.h index 56d358e0d7ed..8219e1a96bba 100644 --- a/drivers/vfio/ubus/vfio_ub_private.h +++ b/drivers/vfio/ubus/vfio_ub_private.h @@ -26,6 +26,7 @@ struct vfio_ub_core_device; struct perm_bits { u8 *virt; u8 *write; + u8 *entnro; u8 *ent0eo; u8 *exist; int (*readfn)(struct vfio_ub_core_device *vdev, u64 pos, int count, @@ -73,6 +74,8 @@ struct vfio_ub_core_device { #define DWORD_BITS 32 #define BYTE_BITS 8 +int vfio_ub_init_perm_bits(void); +void vfio_ub_uninit_perm_bits(void); int vfio_ub_config_init(struct vfio_ub_core_device *vdev); void vfio_ub_config_uninit(struct vfio_ub_core_device *vdev); ssize_t vfio_ub_config_rw(struct vfio_ub_core_device *vdev, char __user *buf, -- Gitee From c837f7061340cb4d13975451ba6ba3112b6bc357 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Mon, 22 Sep 2025 16:10:46 +0800 Subject: [PATCH 063/214] ub:ubus: Support UBUS pool devices register ANBZ: #45460 commit e00041ecb77a9f16a6f72afdfaf7af7f7b31ce63 openEuler. Support UBUS pool devices register, and get pool device register message, add pool devices and remove response to Clan Fabric Manager Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/instance.c | 34 +++- drivers/ub/ubus/instance.h | 1 + drivers/ub/ubus/msg.c | 5 +- drivers/ub/ubus/pool.c | 316 ++++++++++++++++++++++++++++++++++ drivers/ub/ubus/pool.h | 57 ++++++ drivers/ub/ubus/ubus_config.c | 1 + drivers/ub/ubus/ubus_entity.c | 35 +++- include/ub/ubus/ubus.h | 1 + 9 files changed, 444 insertions(+), 8 deletions(-) create mode 100644 drivers/ub/ubus/pool.c create mode 100644 drivers/ub/ubus/pool.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 7736843bd353..03a8128a6473 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o omm.o ioctl.o eu.o link.o -ubus-y += instance.o +ubus-y += instance.o pool.o ubus-y += services/ras.o diff --git a/drivers/ub/ubus/instance.c b/drivers/ub/ubus/instance.c index 315af7f10c09..8c49c04388e3 100644 --- a/drivers/ub/ubus/instance.c +++ b/drivers/ub/ubus/instance.c @@ -123,6 +123,17 @@ void ub_bus_instance_put(struct ub_bus_instance *bi) } EXPORT_SYMBOL_GPL(ub_bus_instance_put); +bool eid_match(struct ub_bus_instance *bi, void *arg) +{ + if (bi == NULL || arg == NULL) + return false; + + u32 *eid = (u32 *)arg; + + return bi->info.eid == *eid; +} +EXPORT_SYMBOL_GPL(eid_match); + static struct ub_bus_instance *ub_alloc_bus_instance(void) { struct ub_bus_instance *bi; @@ -756,6 +767,12 @@ static int get_bus_instance_and_uent(u8 *b_guid, u8 *d_guid, if (!uent) goto put; + if (is_p_device(uent)) { + pr_err("fad dev not support bind again\n"); + ub_entity_put(uent); + goto put; + } + *p_bi = bi; *p_uent = uent; return 0; @@ -898,14 +915,24 @@ int ub_ioctl_bus_instance_unbind(void __user *uptr) int ub_default_bus_instance_init(struct ub_entity *uent) { + bool m_idev = is_p_idevice(uent); + bool fad = is_p_device(uent); struct ub_bus_instance *bi; int ret; if (is_switch(uent)) return 0; - bi = uent->ubc->bi; + if (fad || m_idev) { + mutex_lock(&dynamic_mutex); + bi = ub_find_bus_instance(eid_match, &uent->user_eid); + } else { + bi = uent->ubc->bi; + } + if (!bi) { + if (fad || m_idev) + mutex_unlock(&dynamic_mutex); ub_err(uent, "get default bi NULL\n"); return -EINVAL; } @@ -914,6 +941,11 @@ int ub_default_bus_instance_init(struct ub_entity *uent) ret = ub_bind_bus_instance(uent, bi); mutex_unlock(&uent->instance_lock); + if (fad || m_idev) { + ub_bus_instance_put(bi); + mutex_unlock(&dynamic_mutex); + } + return ret; } diff --git a/drivers/ub/ubus/instance.h b/drivers/ub/ubus/instance.h index 2033c264e774..6aac58df77bc 100644 --- a/drivers/ub/ubus/instance.h +++ b/drivers/ub/ubus/instance.h @@ -41,5 +41,6 @@ int ub_notify_bus_instance_handle(struct ub_bus_controller *ubc, bool flag, void ub_static_cluster_instance_drain(void); int ub_default_bus_instance_init(struct ub_entity *uent); void ub_default_bus_instance_uninit(struct ub_entity *uent); +bool eid_match(struct ub_bus_instance *bi, void *arg); #endif /* __INSTANCE_H__ */ diff --git a/drivers/ub/ubus/msg.c b/drivers/ub/ubus/msg.c index d7bfca8eb725..64ba8b91628a 100644 --- a/drivers/ub/ubus/msg.c +++ b/drivers/ub/ubus/msg.c @@ -9,6 +9,7 @@ #include #include +#include "pool.h" #include "msg.h" u8 err_to_msg_rsp(int err) @@ -166,7 +167,7 @@ int message_rx_init(void) { const char *msg_name[UB_MSG_CODE_NUM] = { NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL + NULL, NULL, "pool wq", NULL }; struct workqueue_struct *q; int i; @@ -263,7 +264,7 @@ static rx_msg_handler_t rx_msg_handler[UB_MSG_CODE_NUM] = { NULL, NULL, NULL, - NULL, + ub_pool_rx_msg_handler, }; static void message_rx_work(struct work_struct *work) diff --git a/drivers/ub/ubus/pool.c b/drivers/ub/ubus/pool.c new file mode 100644 index 000000000000..0c69e1e72c88 --- /dev/null +++ b/drivers/ub/ubus/pool.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus pool: " fmt + +#include "ubus.h" +#include "ubus_controller.h" +#include "ubus_entity.h" +#include "resource.h" +#include "msg.h" +#include "enum.h" +#include "port.h" +#include "reset.h" +#include "instance.h" +#include "ubus_inner.h" +#include "pool.h" + +struct pool_msg_pkt { + struct msg_pkt_header header; + union { + struct entity_reg_msg_pld reg; + }; +}; + +#define MSG_ENTITY_REG_SIZE (MSG_PKT_HEADER_SIZE + ENTITY_REG_PLD_SIZE) + +static DEFINE_SPINLOCK(ub_fad_lock); + +struct ub_fad_collection { + struct list_head list; + struct list_head node; +}; + +static struct ub_fad_collection ub_fad = { + .list = LIST_HEAD_INIT(ub_fad.list), + .node = LIST_HEAD_INIT(ub_fad.node), +}; + +static int ub_fad_res_init(struct pool_fad *fad, struct ub_entity *uent) +{ + u32 source_support_map[MAX_UB_RES_NUM] = { UB_ERS0S_SUPPORT, + UB_ERS1S_SUPPORT, + UB_ERS2S_SUPPORT }; + u32 support_feature = 0; + size_t pg_size; + u8 sys_pg = 0; + int ret, i; + + if (!fad->ers_valid) + return 0; + + ret = ub_cfg_read_byte(uent, UB_SYS_PGS, &sys_pg); + if (ret) { + ub_err(uent, "entity reg read UB_SYS_PGS failed, ret=%d\n", ret); + return ret; + } + pg_size = (sys_pg & UB_SYS_PGS_SIZE) ? SZ_64K : SZ_4K; + + ret = ub_cfg_read_dword(uent, UB_CFG1_SUPPORT_FEATURE_L, &support_feature); + if (ret) { + ub_err(uent, "read cfg1 feature_l failed, ret=%d\n", ret); + return ret; + } + + for (i = 0; i < MAX_UB_RES_NUM; i++) { + if (!(support_feature & source_support_map[i])) + continue; + + uent->zone[i].res.start = ubba_gen(fad->ers[i].sa_h, + fad->ers[i].sa_l); + uent->zone[i].res.end = uent->zone[i].res.start + + ((u64)fad->ers[i].ss * pg_size - 1); + uent->zone[i].res.flags = IORESOURCE_MEM; + uent->zone[i].res.name = ub_name(uent); + uent->zone[i].sa_used = 1; + uent->zone[i].ubba_used = 0; + ub_info(uent, "mmio_idx=%d, res=%pR\n", i, &uent->zone[i].res); + + ret = ub_insert_resource(uent, i); + if (ret) { + ub_err(uent, "mmio[%d] insert res failed, ret=%d\n", i, + ret); + goto fail; + } + + ub_info(uent, "MMIO[%d]: ubba=%dx, size=%#llx, hpa=%#llx, wr_attr=%01u, prefetchable=%01u, order_type=%01u\n", + i, 0, ub_resource_len(uent, i), + uent->zone[i].res.start, 0, 0, 0); + uent->zone[i].init_succ = 1; + } + + return 0; + +fail: + for (i -= 1; i >= 0; i--) + if (uent->zone[i].init_succ) + ub_entity_free_mmio_idx(uent, i); + return ret; +} + +static void ub_fad_uent_init(struct pool_fad *fad, struct ub_entity *uent) +{ + uent->pool = true; + uent->eid = fad->base.eid[0]; + uent->user_eid = fad->base.ueid[0]; + uent->cna = fad->base.cna; + uent->upi = fad->base.upi; + uent->entity_idx = fad->base.entity_idx; + uent->ubc = ub_ubc_get(fad->ubc); + memcpy(uent->guid.dw, fad->base.guid, UB_GUID_SIZE); + uent->pue = uent; + uent->is_mue = 1; +} + +static int ub_fad_attach(struct pool_fad *fad) +{ + struct ub_entity *uent; + int ret; + + if (fad->attach) + return 0; + + uent = ub_alloc_ent(); + if (!uent) + return -ENOMEM; + + ub_fad_uent_init(fad, uent); + + ret = ub_setup_ent(uent); + if (ret) + goto fail; + + ret = ub_fad_res_init(fad, uent); + WARN_ON(ret); + + ub_entity_add(uent, uent->ubc); + + fad->uent = uent; + fad->attach = true; + return 0; +fail: + ub_ubc_put(uent->ubc); + kfree(uent); + return ret; +} + +bool ub_rsp_msg_init(struct msg_pkt_header *header, u8 status, u32 plen) +{ + struct compact_network_header *cnth = &header->nth; + u32 seid = header->deid; + u32 deid = eid_gen(header->seid_h, header->seid_l); + u16 dcna = cnth->scna; + u16 scna = cnth->dcna; + + cnth->scna = scna; + cnth->dcna = dcna; + + header->seid_h = seid_high(seid); + header->seid_l = seid_low(seid); + header->deid = deid; + + header->msgetah.plen = plen; + header->msgetah.rsp_status = status; + header->msgetah.type = MSG_RSP; + + return (scna == dcna); +} +EXPORT_SYMBOL_GPL(ub_rsp_msg_init); + +static void ub_fad_para_init(struct pool_fad *fad, struct entity_reg_msg_pld *pld) +{ + struct ub_guid *guid = (struct ub_guid *)fad->base.guid; + char buf[SZ_64] = {}; + + memcpy(&fad->base, &pld->base, sizeof(struct entity_base_info)); + + (void)ub_show_guid(guid, buf); + + dev_info(&fad->ubc->dev, + "fad_add_idx=%u, eid=%#x, cna=%#x, upi=%#x, u_eid=%#05x, guid=%s\n", + fad->base.entity_idx, fad->base.eid[0], fad->base.cna, + fad->base.upi, fad->base.ueid[0], buf); + + if (fad->ers_valid) + memcpy(fad->ers, pld->ers, ENTITY_RS_PLD_SIZE); +} + +static int ub_entity_reg_check(struct ub_bus_controller *ubc, + struct entity_reg_msg_pld *pld, bool ers_valid) +{ + struct entity_rs_info *ers; + struct device *dev = &ubc->dev; + int i; + + if (pld->base.eid[0] == 0 || pld->base.ueid[0] == 0) { + dev_err(dev, "entity reg eid=%#x u_eid=%#x is invalid\n", + pld->base.eid[0], pld->base.ueid[0]); + return -EINVAL; + } + + if (!ers_valid) + return 0; + + ers = (struct entity_rs_info *)pld->ers; + for (i = 0; i < MAX_UB_RES_NUM; i++) { + if (ers[i].ss == 0) { + dev_err(dev, "entity reg ers[%d] size is 0\n", i); + return -EINVAL; + } + } + + return 0; +} + +static u8 ub_entity_reg_handle(struct ub_bus_controller *ubc, + struct pool_msg_pkt *pkt, bool ers_valid, + struct pool_fad **start_fad) +{ + struct entity_reg_msg_pld *pld = &pkt->reg; + struct pool_fad *fad; + struct ub_entity *uent; + int ret; + + uent = ub_get_ent_by_eid(pld->base.eid[0]); + if (uent) { + ub_entity_put(uent); + return UB_MSG_RSP_EXEC_EEXIST; + } + + ret = ub_entity_reg_check(ubc, pld, ers_valid); + if (ret) + return UB_MSG_RSP_EXEC_EINVAL; + + fad = kzalloc(sizeof(*fad), GFP_KERNEL); + if (!fad) + return UB_MSG_RSP_EXEC_ENOMEM; + + fad->ubc = ubc; + fad->ers_valid = ers_valid; + ub_fad_para_init(fad, pld); + + ret = ub_fad_attach(fad); + if (ret) { + dev_err(&ubc->dev, "fad idx[%u] add failed\n", fad->base.entity_idx); + kfree(fad); + return UB_MSG_RSP_EXEC_ENOEXEC; + } + + spin_lock(&ub_fad_lock); + list_add_tail(&fad->node, &ub_fad.node); + spin_unlock(&ub_fad_lock); + *start_fad = fad; + + return UB_MSG_RSP_SUCCESS; +} + +static void +ub_entity_reg_msg_handler(struct ub_bus_controller *ubc, void *msg, u16 p_len) +{ + struct pool_msg_pkt *pkt = (struct pool_msg_pkt *)msg; + struct msg_pkt_header *header = &pkt->header; + struct msg_info info = {}; + struct pool_fad *start_fad; + bool local, flag; + u32 feature = 0; + u8 status; + int ret; + + if (p_len != MSG_ENTITY_REG_SIZE) { + dev_err(&ubc->dev, "entity reg len err, len=%#x\n", p_len); + status = UB_MSG_RSP_CMD_LEN_ERR; + goto rsp; + } + + ret = ub_cfg_read_dword(ubc->uent, UB_CFG1_SUPPORT_FEATURE_L, &feature); + if (ret) { + dev_err(&ubc->dev, "entity reg feature read failed, ret=%d\n", ret); + status = UB_MSG_RSP_EXEC_ENOEXEC; + goto rsp; + } + + flag = !!(feature & UB_DECODER_JURIS); + status = ub_entity_reg_handle(ubc, pkt, flag, &start_fad); + +rsp: + local = ub_rsp_msg_init(header, status, 0); + message_info_init(&info, local ? ubc->uent : NULL, pkt, NULL, + (MSG_PKT_HEADER_SIZE << MSG_REQ_SIZE_OFFSET)); + + ret = message_response(ubc->mdev, &info, header->msgetah.code); + if (ret) + dev_err(&ubc->dev, "send pool rsp msg, ret=%d\n", ret); + + if (status == UB_MSG_RSP_SUCCESS) + ub_start_ent(start_fad->uent); +} + +static rx_msg_handler_t pool_rx_msg_handler[UB_SUB_MSG_CODE_NUM] = { + ub_entity_reg_msg_handler, +}; + +void ub_pool_rx_msg_handler(struct ub_bus_controller *ubc, void *pkt, u16 len) +{ + struct msg_pkt_header *header = (struct msg_pkt_header *)pkt; + u8 sub_msg_code = header->msgetah.sub_msg_code; + rx_msg_handler_t handler; + + handler = pool_rx_msg_handler[sub_msg_code]; + if (handler) + handler(ubc, pkt, len); + else + dev_err(&ubc->dev, "pool sub msg code not support, code=%#x\n", + sub_msg_code); +} diff --git a/drivers/ub/ubus/pool.h b/drivers/ub/ubus/pool.h new file mode 100644 index 000000000000..8178fa2a6862 --- /dev/null +++ b/drivers/ub/ubus/pool.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __POOL_H__ +#define __POOL_H__ + +#include "msg.h" + +struct entity_base_info { + /* DW0 */ + u32 entity_idx : 16; + u32 upi : 15; + u32 rsvd0 : 1; + /* DW1~DW4 */ + u32 eid[4]; + /* DW5~DW8 */ + u32 guid[UB_GUID_DW_NUM]; + /* DW9 */ + u32 cna : 24; + u32 rsvd2 : 8; + /* DW10~DW13 */ + u32 ueid[4]; +}; + +struct entity_rs_info { + u32 ss; + u32 sa_l; + u32 sa_h; +}; + +struct pool_fad { + struct entity_base_info base; + struct entity_rs_info ers[MAX_UB_RES_NUM]; + + bool attach; + bool ers_valid; + struct ub_entity *uent; + struct list_head node; + struct ub_bus_controller *ubc; +}; + +struct entity_reg_msg_pld { + /* DW0~DW13 */ + struct entity_base_info base; + /* DW14~DW22 */ + struct entity_rs_info ers[MAX_UB_RES_NUM]; +}; +#define ENTITY_BASE_PLD_SIZE 56 +#define ENTITY_RS_PLD_SIZE 36 +#define ENTITY_REG_PLD_SIZE (ENTITY_BASE_PLD_SIZE + ENTITY_RS_PLD_SIZE) + +bool ub_rsp_msg_init(struct msg_pkt_header *header, u8 status, u32 plen); +void ub_pool_rx_msg_handler(struct ub_bus_controller *ubc, void *pkt, u16 len); + +#endif /* __POOL_H__ */ diff --git a/drivers/ub/ubus/ubus_config.c b/drivers/ub/ubus/ubus_config.c index c9b07b63e922..a2d285da998a 100644 --- a/drivers/ub/ubus/ubus_config.c +++ b/drivers/ub/ubus/ubus_config.c @@ -111,6 +111,7 @@ void ub_msg_pkt_header_init(struct msg_pkt_header *header, struct ub_entity *uen cnth->nth_nlp = NTH_NLP_WITH_TPH; cnth->scna = scna; cnth->dcna = dcna; + cnth->mgmt = is_p_device(uent) ? 0 : 1; header->ctph_nlp = CTPH_NLP_UPI_40BITS_UEID; header->tp_opcode = CTPH_OPCODE_NOT_CNP; diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index f79ac083e6a0..7c95303f4e81 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -104,6 +104,9 @@ static void ub_config_upi(struct ub_entity *uent) int ret; u16 upi; + if (is_p_device(uent)) + return; + if (is_ibus_controller(uent) && uent->ubc->cluster) { dev = &uent->ubc->dev; ret = ub_cfg_read_word(uent, UB_UPI, &upi); @@ -196,6 +199,22 @@ static int ub_setup_ent_normal(struct ub_entity *uent) return 0; } +static int ub_fad_cfg_access_check(struct ub_entity *uent) +{ + u32 feature; + int ret = 0; + + if (is_p_device(uent)) { + ret = ub_cfg_read_dword(uent, UB_CFG1_SUPPORT_FEATURE_L, + &feature); + if (ret) + ub_err(uent, "fad cfg access failed, eid=%#x, ret=%d\n", + uent->eid, ret); + } + + return ret; +} + static int ub_uent_cfg(struct ub_entity *uent, u32 uent_num) { struct ub_guid *guid = &uent->guid; @@ -222,6 +241,9 @@ static int ub_uent_cfg(struct ub_entity *uent, u32 uent_num) static void ub_config_eid(struct ub_entity *uent) { + if (is_p_device(uent)) + return; + if (is_ibus_controller(uent) && uent->ubc->cluster) return; @@ -275,6 +297,9 @@ int ub_setup_ent(struct ub_entity *uent) } ub_config_upi(uent); + ret = ub_fad_cfg_access_check(uent); + if (ret) + goto err_alloc; /* common setup */ ret = ub_eid_alloc(uent); @@ -411,10 +436,12 @@ void ub_start_ent(struct ub_entity *uent) ret = ub_default_bus_instance_init(uent); WARN_ON(ret); - uent->match_driver = true; - ret = device_attach(&uent->dev); - if (ret < 0 && ret != -EPROBE_DEFER) - ub_err(uent, "device attach failed, ret=%d\n", ret); + if (!((is_p_device(uent) || is_p_idevice(uent)) && is_dynamic(uent->bi))) { + uent->match_driver = true; + ret = device_attach(&uent->dev); + if (ret < 0 && ret != -EPROBE_DEFER) + ub_err(uent, "device attach failed, ret=%d\n", ret); + } if (is_primary(uent) && !is_p_device(uent)) { ret = ub_mue_enable_and_map(uent); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 194f9556de39..e94bbc2d8c50 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -245,6 +245,7 @@ struct ub_entity { struct mutex instance_lock; struct list_head instance_node; struct ub_bus_instance *bi; + u32 user_eid; struct ub_eu_table *eu_table; u32 support_feature; -- Gitee From 20a51920aa57a2cc473ea956571b9bc025a40157 Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Tue, 23 Sep 2025 12:04:01 +0800 Subject: [PATCH 064/214] vfio:ubus: Init vfio ub config space 1 perm and port perm bits during module init ANBZ: #45460 commit 0679b8c24bb8387db0dadd6c82bef9af69d9bdf5 openEuler. Init vfio ub config space 1 perm and port perm bits during module init. For the configuration space registers supported by vfio ub, the read and write permissions for each register are controlled through the virt and write fields in the struct perm_bits structure. Signed-off-by: Yahui Liu Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/vfio/ubus/vfio_ub_config.c | 237 +++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+) diff --git a/drivers/vfio/ubus/vfio_ub_config.c b/drivers/vfio/ubus/vfio_ub_config.c index 34b5674cfe3f..44e8e470d233 100644 --- a/drivers/vfio/ubus/vfio_ub_config.c +++ b/drivers/vfio/ubus/vfio_ub_config.c @@ -571,6 +571,238 @@ static int init_ub_cfg0_cap_perm(struct perm_bits *perm) return 0; } +static int vfio_ub_cfg1_basic_read(struct vfio_ub_core_device *vdev, u64 pos, + int count, __le32 *val) +{ + count = vfio_ub_default_config_read(vdev, pos, count, val); + + return count; +} + +static int vfio_ub_cfg1_basic_write(struct vfio_ub_core_device *vdev, u64 pos, + int count, __le32 val) +{ + u8 *elr; + u8 *buf; + + count = vfio_ub_default_config_write(vdev, pos, count, val); + if (count < 0) + return count; + + buf = vfio_ub_find_cfg_buf(vdev, UB_CFG1_BASIC_CAP); + if (!buf) + return -EFAULT; + + elr = buf + UB_ELR - UB_CFG1_BASIC; + if (*elr & UB_ELR_BIT) { + *elr = *elr & (u8)~UB_ELR_BIT; + if (ub_reset_entity(vdev->uent)) + ub_warn(vdev->uent, "do elr reset failed\n"); + } + + return count; +} + +static void init_ub_cfg1_basic_operation_perm(struct perm_bits *perm) +{ + p_setd(perm, UB_CFG1_BASIC_SLICE - UB_CFG1_BASIC, ALL_VIRT, NO_WRITE); + init_ub_cfg_chunk_perms(perm, UB_CFG1_CAP_BITMAP - UB_CFG1_BASIC, + UB_CFG_BITMAP_SIZE / DWORD_SIZE, + ALL_VIRT, NO_WRITE); + init_ub_cfg_chunk_perms(perm, UB_CFG1_SUPPORT_FEATURE_L - UB_CFG1_BASIC, + UB_CFG1_SUPPORT_FEATURE_SIZE / DWORD_SIZE, + ALL_VIRT, NO_WRITE); + + p_setd(perm, UB_ERS0_SS - UB_CFG1_BASIC, ALL_VIRT, NO_WRITE); + p_setd(perm, UB_ERS1_SS - UB_CFG1_BASIC, ALL_VIRT, NO_WRITE); + p_setd(perm, UB_ERS2_SS - UB_CFG1_BASIC, ALL_VIRT, NO_WRITE); + p_setd(perm, UB_ERS0_SA_L - UB_CFG1_BASIC, ALL_VIRT, NO_WRITE); + p_setd(perm, UB_ERS0_SA_H - UB_CFG1_BASIC, ALL_VIRT, NO_WRITE); + p_setd(perm, UB_ERS1_SA_L - UB_CFG1_BASIC, ALL_VIRT, NO_WRITE); + p_setd(perm, UB_ERS1_SA_H - UB_CFG1_BASIC, ALL_VIRT, NO_WRITE); + p_setd(perm, UB_ERS2_SA_L - UB_CFG1_BASIC, ALL_VIRT, NO_WRITE); + p_setd(perm, UB_ERS2_SA_H - UB_CFG1_BASIC, ALL_VIRT, NO_WRITE); + p_setd(perm, UB_ERS0_UBBA_L - UB_CFG1_BASIC, ALL_VIRT, ALL_WRITE); + p_setd(perm, UB_ERS0_UBBA_H - UB_CFG1_BASIC, ALL_VIRT, ALL_WRITE); + p_setd(perm, UB_ERS1_UBBA_L - UB_CFG1_BASIC, ALL_VIRT, ALL_WRITE); + p_setd(perm, UB_ERS1_UBBA_H - UB_CFG1_BASIC, ALL_VIRT, ALL_WRITE); + p_setd(perm, UB_ERS2_UBBA_L - UB_CFG1_BASIC, ALL_VIRT, ALL_WRITE); + p_setd(perm, UB_ERS2_UBBA_H - UB_CFG1_BASIC, ALL_VIRT, ALL_WRITE); + + p_setd(perm, UB_ELR - UB_CFG1_BASIC, ALL_VIRT, UB_ELR_BIT); + p_setd(perm, UB_ELR_DONE - UB_CFG1_BASIC, ~UB_ELR_DONE_BIT, NO_WRITE); + p_setd(perm, UB_SYS_PGS - UB_CFG1_BASIC, ALL_VIRT, UB_SYS_PGS_SIZE); + + p_setd(perm, UB_ENTITY_TOKEN_ID - UB_CFG1_BASIC, NO_VIRT, UB_TOKEN_ID_MASK); + p_setd(perm, UB_BUS_ACCESS_EN - UB_CFG1_BASIC, NO_VIRT, + UB_BUS_ACCESS_EN_BIT); + p_setd(perm, UB_ENTITY_RS_ACCESS_EN - UB_CFG1_BASIC, NO_VIRT, + UB_BUS_ACCESS_EN_BIT); +} + +static void init_ub_cfg1_basic_attr_perm(struct perm_bits *perm) +{ + p_attr_setd(perm, UB_ERS0_SA_L - UB_CFG1_BASIC, NO_ENTITY0_EO, NO_ENTITYN_RO, + IDEV_NO_EXIST); + p_attr_setd(perm, UB_ERS0_SA_H - UB_CFG1_BASIC, NO_ENTITY0_EO, NO_ENTITYN_RO, + IDEV_NO_EXIST); + + p_attr_setd(perm, UB_ERS1_SA_L - UB_CFG1_BASIC, NO_ENTITY0_EO, NO_ENTITYN_RO, + IDEV_NO_EXIST); + p_attr_setd(perm, UB_ERS1_SA_H - UB_CFG1_BASIC, NO_ENTITY0_EO, NO_ENTITYN_RO, + IDEV_NO_EXIST); + + p_attr_setd(perm, UB_ERS2_SA_L - UB_CFG1_BASIC, NO_ENTITY0_EO, NO_ENTITYN_RO, + IDEV_NO_EXIST); + p_attr_setd(perm, UB_ERS2_SA_H - UB_CFG1_BASIC, NO_ENTITY0_EO, NO_ENTITYN_RO, + IDEV_NO_EXIST); + + p_attr_setd(perm, UB_ERS0_UBBA_L - UB_CFG1_BASIC, NO_ENTITY0_EO, ENTITYN_RO, + UDEV_NO_EXIST); + p_attr_setd(perm, UB_ERS0_UBBA_H - UB_CFG1_BASIC, NO_ENTITY0_EO, ENTITYN_RO, + UDEV_NO_EXIST); + + p_attr_setd(perm, UB_ERS1_UBBA_L - UB_CFG1_BASIC, NO_ENTITY0_EO, ENTITYN_RO, + UDEV_NO_EXIST); + p_attr_setd(perm, UB_ERS1_UBBA_H - UB_CFG1_BASIC, NO_ENTITY0_EO, ENTITYN_RO, + UDEV_NO_EXIST); + + p_attr_setd(perm, UB_ERS2_UBBA_L - UB_CFG1_BASIC, NO_ENTITY0_EO, ENTITYN_RO, + UDEV_NO_EXIST); + p_attr_setd(perm, UB_ERS2_UBBA_H - UB_CFG1_BASIC, NO_ENTITY0_EO, ENTITYN_RO, + UDEV_NO_EXIST); + + p_attr_setd(perm, UB_SYS_PGS - UB_CFG1_BASIC, NO_ENTITY0_EO, NO_ENTITYN_RO, + FUNC_NO_EXIST); + p_attr_setd(perm, UB_EU_TBA_L - UB_CFG1_BASIC, NO_ENTITY0_EO, NO_ENTITYN_RO, + FUNC_NO_EXIST); + p_attr_setd(perm, UB_EU_TBA_H - UB_CFG1_BASIC, NO_ENTITY0_EO, NO_ENTITYN_RO, + FUNC_NO_EXIST); + p_attr_setd(perm, UB_EU_TEN - UB_CFG1_BASIC, NO_ENTITY0_EO, NO_ENTITYN_RO, + FUNC_NO_EXIST); + p_attr_setd(perm, UB_CLASS_CODE - UB_CFG1_BASIC, NO_ENTITY0_EO, NO_ENTITYN_RO, + FUNC_EXIST); +} + +static int init_ub_cfg1_basic_perm(struct perm_bits *perm) +{ + if (alloc_perm_bits(perm, UB_SLICE_SZ)) + return -ENOMEM; + + perm->readfn = vfio_ub_cfg1_basic_read; + perm->writefn = vfio_ub_cfg1_basic_write; + + init_ub_cfg1_basic_operation_perm(perm); + init_ub_cfg1_basic_attr_perm(perm); + + return 0; +} + +static int vfio_ub_int_type2_cap_read(struct vfio_ub_core_device *vdev, u64 pos, + int count, __le32 *val) +{ + count = vfio_ub_default_config_read(vdev, pos, count, val); + + return count; +} + +static int vfio_ub_int_type2_cap_write(struct vfio_ub_core_device *vdev, u64 pos, + int count, __le32 val) +{ + count = vfio_ub_default_config_write(vdev, pos, count, val); + + return count; +} + +static int init_ub_cfg1_int_cap_perm(struct perm_bits *perm) +{ + if (alloc_perm_bits(perm, UB_SLICE_SZ)) + return -ENOMEM; + + perm->readfn = vfio_ub_int_type2_cap_read; + perm->writefn = vfio_ub_int_type2_cap_write; + + p_setd(perm, UB_INT_CAP_SLICE - UB_CFG1_CAP_INT_CAP, ALL_VIRT, NO_WRITE); + p_setd(perm, UB_INT_INFO - UB_CFG1_CAP_INT_CAP, ALL_VIRT, NO_WRITE); + p_setd(perm, UB_INT_VECTOR_TBL_SA_L - UB_CFG1_CAP_INT_CAP, + ALL_VIRT, NO_WRITE); + p_setd(perm, UB_INT_VECTOR_TBL_SA_H - UB_CFG1_CAP_INT_CAP, + ALL_VIRT, NO_WRITE); + p_setd(perm, UB_INT_ADDR_TBL_SA_L - UB_CFG1_CAP_INT_CAP, + ALL_VIRT, NO_WRITE); + p_setd(perm, UB_INT_ADDR_TBL_SA_H - UB_CFG1_CAP_INT_CAP, + ALL_VIRT, NO_WRITE); + p_setd(perm, UB_INT_PENDING_TBL_SA_L - UB_CFG1_CAP_INT_CAP, + ALL_VIRT, NO_WRITE); + p_setd(perm, UB_INT_PENDING_TBL_SA_H - UB_CFG1_CAP_INT_CAP, + ALL_VIRT, NO_WRITE); + p_setd(perm, UB_INT_ID - UB_CFG1_CAP_INT_CAP, ALL_VIRT, ALL_WRITE); + p_setd(perm, UB_INT_MASK - UB_CFG1_CAP_INT_CAP, UB_INT_MASK_BIT, + UB_INT_MASK_BIT); + + p_setd(perm, UB_INT_EN - UB_CFG1_CAP_INT_CAP, UB_INT_EN_BIT, + UB_INT_EN_BIT); + + return 0; +} + +static int init_ub_cfg1_cap_perm(struct perm_bits *perm) +{ + return init_ub_cfg1_int_cap_perm(&perm[UB_INT_TYPE2_CAP]); +} + +static int vfio_ub_port_basic_read(struct vfio_ub_core_device *vdev, u64 pos, + int count, __le32 *val) +{ + count = vfio_ub_default_config_read(vdev, pos, count, val); + + return count; +} + +static int vfio_ub_port_basic_write(struct vfio_ub_core_device *vdev, u64 pos, + int count, __le32 val) +{ + count = vfio_ub_default_config_write(vdev, pos, count, val); + + return count; +} + +static int init_ub_port_basic_perm(struct perm_bits *perm) +{ + if (alloc_perm_bits(perm, UB_SLICE_SZ)) + return -ENOMEM; + + perm->readfn = vfio_ub_port_basic_read; + perm->writefn = vfio_ub_port_basic_write; + + p_setd(perm, UB_CFG0_PORT_SLICE, ALL_VIRT, NO_WRITE); + p_attr_setd(perm, UB_CFG0_PORT_SLICE, ENTITY0_EO, NO_ENTITYN_RO, FUNC_EXIST); + + init_ub_cfg_chunk_perms(perm, UB_CFG0_PORT_BITMAP, + UB_CFG_BITMAP_SIZE / DWORD_SIZE, + ALL_VIRT, NO_WRITE); + init_ub_cfg_chunk_attrs(perm, UB_CFG0_PORT_BITMAP, + UB_CFG_BITMAP_SIZE / DWORD_SIZE, + ENTITY0_EO, NO_ENTITYN_RO, FUNC_EXIST); + + p_setd(perm, UB_PORT_INFO, ALL_VIRT, NO_WRITE); + p_attr_setd(perm, UB_PORT_INFO, ENTITY0_EO, NO_ENTITYN_RO, FUNC_EXIST); + + init_ub_cfg_chunk_perms(perm, UB_NEIGHBOR_PORT_INFO, + UB_UB_NEIGHBOR_PORT_INFO_SIZE / DWORD_SIZE, + ALL_VIRT, NO_WRITE); + init_ub_cfg_chunk_attrs(perm, UB_NEIGHBOR_PORT_INFO, + UB_UB_NEIGHBOR_PORT_INFO_SIZE / DWORD_SIZE, + ENTITY0_EO, NO_ENTITYN_RO, FUNC_EXIST); + + p_setd(perm, UB_PORT_NET_ADDR, UB_PORT_NET_ADDR_MASK, UB_PORT_NET_ADDR_MASK); + p_attr_setd(perm, UB_PORT_NET_ADDR, ENTITY0_EO, NO_ENTITYN_RO, FUNC_EXIST); + p_setd(perm, UB_PORT_RST, ALL_VIRT, UB_PORT_RST_BIT); + p_attr_setd(perm, UB_PORT_RST, ENTITY0_EO, NO_ENTITYN_RO, FUNC_EXIST); + + return 0; +} + static u8 vfio_slice_supported[UB_CFG1_MAX_CAP + 1]; static void vfio_ub_init_slice_supported(void) { @@ -588,6 +820,11 @@ int __init vfio_ub_init_perm_bits(void) ret = init_ub_cfg0_basic_perm(&cap_perms[UB_CFG0_BASIC_CAP]); ret |= init_ub_cfg0_cap_perm(cap_perms); + ret |= init_ub_cfg1_basic_perm(&cap_perms[UB_CFG1_BASIC_CAP]); + ret |= init_ub_cfg1_cap_perm(cap_perms); + + ret |= init_ub_port_basic_perm(&port_basic_perms); + if (ret) vfio_ub_uninit_perm_bits(); -- Gitee From 58b8b94b44cea8a4362510003d0590d6903fb523 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Mon, 22 Sep 2025 16:48:12 +0800 Subject: [PATCH 065/214] ub:ubus: Add new UBUS pool device interfaces ANBZ: #45460 commit 3ba2ea6f540df963f195e45f1a0cc5110cd67cee openEuler. Add new UBUS pool devices interfaces, including device release, bus instance create and destroy, port reset notify and config cpl notify.When complete, send response to FM. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/pool.c | 331 +++++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/pool.h | 17 +++ include/ub/ubus/ubus.h | 1 + 3 files changed, 349 insertions(+) diff --git a/drivers/ub/ubus/pool.c b/drivers/ub/ubus/pool.c index 0c69e1e72c88..2aeb8d57ee9a 100644 --- a/drivers/ub/ubus/pool.c +++ b/drivers/ub/ubus/pool.c @@ -17,14 +17,45 @@ #include "ubus_inner.h" #include "pool.h" +struct cfg_cpl_notify_pld { + u32 flag : 1; + u32 rsvd : 31; + u32 guid[UB_GUID_DW_NUM]; + u32 eid[4]; +}; +#define CFG_CPL_NOTIFY_PLD_SIZE 36 + +struct bi_create_pld { + u32 guid[UB_GUID_DW_NUM]; + u32 eid[4]; + u32 upi : 15; + u32 rsvd1 : 17; +}; +#define BI_CREATE_PLD_SIZE 36 + +struct bi_destroy_pld { + u32 guid[UB_GUID_DW_NUM]; +}; +#define BI_DESTROY_PLD_SIZE 16 + struct pool_msg_pkt { struct msg_pkt_header header; union { struct entity_reg_msg_pld reg; + struct entity_rls_msg_pld rls; + struct cfg_cpl_notify_pld notify; + struct bi_create_pld create; + struct bi_destroy_pld destroy; + struct port_reset_notify_pld port_reset; }; }; #define MSG_ENTITY_REG_SIZE (MSG_PKT_HEADER_SIZE + ENTITY_REG_PLD_SIZE) +#define MSG_ENTITY_RLS_SIZE (MSG_PKT_HEADER_SIZE + ENTITY_RLS_PLD_SIZE) +#define MSG_BI_CREATE_SIZE (MSG_PKT_HEADER_SIZE + BI_CREATE_PLD_SIZE) +#define MSG_BI_DESTROY_SIZE (MSG_PKT_HEADER_SIZE + BI_DESTROY_PLD_SIZE) +#define MSG_CFG_CPL_NOTIFY_SIZE (MSG_PKT_HEADER_SIZE + CFG_CPL_NOTIFY_PLD_SIZE) +#define MSG_PORT_RESET_SIZE (MSG_PKT_HEADER_SIZE + PORT_RESET_NOTIFY_PLD_SIZE) static DEFINE_SPINLOCK(ub_fad_lock); @@ -38,6 +69,33 @@ static struct ub_fad_collection ub_fad = { .node = LIST_HEAD_INIT(ub_fad.node), }; +static struct pool_fad *ub_get_fad(u32 eid) +{ + struct pool_fad *fad; + unsigned long flags; + + spin_lock_irqsave(&ub_fad_lock, flags); + list_for_each_entry(fad, &ub_fad.node, node) + if (fad->base.eid[0] == eid) + goto out; + + fad = NULL; +out: + spin_unlock_irqrestore(&ub_fad_lock, flags); + return fad; +} + +struct ub_entity *ub_get_fad_ent_by_eid(unsigned int eid) +{ + struct pool_fad *fad; + + fad = ub_get_fad(eid); + if (fad) + return fad->uent; + + return NULL; +} + static int ub_fad_res_init(struct pool_fad *fad, struct ub_entity *uent) { u32 source_support_map[MAX_UB_RES_NUM] = { UB_ERS0S_SUPPORT, @@ -146,6 +204,12 @@ static int ub_fad_attach(struct pool_fad *fad) return ret; } +static void ub_fad_detach(struct pool_fad *fad) +{ + ub_stop_and_remove_ent(fad->uent); + fad->uent = NULL; +} + bool ub_rsp_msg_init(struct msg_pkt_header *header, u8 status, u32 plen) { struct compact_network_header *cnth = &header->nth; @@ -256,6 +320,61 @@ static u8 ub_entity_reg_handle(struct ub_bus_controller *ubc, return UB_MSG_RSP_SUCCESS; } +static u8 +ub_entity_rls_handle(struct ub_bus_controller *ubc, struct pool_msg_pkt *pkt) +{ + struct entity_rls_msg_pld *pld = &pkt->rls; + struct pool_fad *fad; + + fad = ub_get_fad(pld->eid[0]); + if (!fad) + return UB_MSG_RSP_EXEC_ENODEV; + + spin_lock(&ub_fad_lock); + list_del(&fad->node); + spin_unlock(&ub_fad_lock); + + if (fad->attach) { + ub_info(fad->uent, "fad detach, eid=%#x, reason=%#x\n", + fad->uent->eid, pld->reason); + ub_fad_detach(fad); + fad->attach = false; + } + + kfree(fad); + + return UB_MSG_RSP_SUCCESS; +} + +static void +ub_entity_rls_msg_handler(struct ub_bus_controller *ubc, void *msg, u16 p_len) +{ + struct pool_msg_pkt *pkt = (struct pool_msg_pkt *)msg; + struct msg_pkt_header *header = &pkt->header; + struct msg_info info = {}; + bool local; + u8 status; + int ret; + + if (p_len != MSG_ENTITY_RLS_SIZE) { + dev_err(&ubc->dev, "entity rls msg len is wrong, len=%#x\n", + p_len); + status = UB_MSG_RSP_CMD_LEN_ERR; + goto rsp; + } + + status = ub_entity_rls_handle(ubc, pkt); + +rsp: + local = ub_rsp_msg_init(header, status, 0); + message_info_init(&info, local ? ubc->uent : NULL, pkt, NULL, + (MSG_PKT_HEADER_SIZE << MSG_REQ_SIZE_OFFSET)); + + ret = message_response(ubc->mdev, &info, header->msgetah.code); + if (ret) + dev_err(&ubc->dev, "send entity rls rsp, ret=%d\n", ret); +} + static void ub_entity_reg_msg_handler(struct ub_bus_controller *ubc, void *msg, u16 p_len) { @@ -297,8 +416,220 @@ ub_entity_reg_msg_handler(struct ub_bus_controller *ubc, void *msg, u16 p_len) ub_start_ent(start_fad->uent); } +static void ub_cfg_cpl_notify_msg_rsp(struct ub_bus_controller *ubc, + struct msg_pkt_header *header) +{ + struct msg_info info = {}; + u32 tmp_eid, tmp_cna; + bool local; + int ret; + + tmp_eid = header->deid; + header->deid = eid_gen(header->seid_h, header->seid_l); + header->seid_h = seid_high(tmp_eid); + header->seid_l = seid_low(tmp_eid); + + tmp_cna = header->nth.scna; + header->nth.scna = header->nth.dcna; + header->nth.dcna = tmp_cna; + header->msgetah.type = MSG_RSP; + header->msgetah.plen = 0; + + local = (header->nth.scna == header->nth.dcna); + message_info_init(&info, local ? ubc->uent : NULL, header, NULL, + (MSG_PKT_HEADER_SIZE << MSG_REQ_SIZE_OFFSET)); + + ret = message_response(ubc->mdev, &info, header->msgetah.code); + if (ret) + dev_err(&ubc->dev, "send notify rsp failed, ret=%d\n", ret); +} + +int ub_fm_flush_ubc_info(struct ub_bus_controller *ubc) +{ + struct device *dev = &ubc->dev; + int ret = -ENOMEM; + u32 eid, fm_cna; + char *buf; + u16 upi; + + buf = kzalloc(SZ_4K, GFP_KERNEL); + if (!buf) + goto out; + + ret = ub_cfg_read_word(ubc->uent, UB_UPI, &upi); + if (ret) { + dev_err(dev, "update cluster upi failed, ret=%d\n", ret); + goto free_buf; + } + + ubc->uent->upi = upi & UB_UPI_MASK; + dev_info(dev, "update cluster ubc upi to %#x\n", ubc->uent->upi); + + ret = ub_cfg_read_dword(ubc->uent, UB_EID_0, &eid); + if (ret) { + dev_err(dev, "update cluster ubc eid failed, ret=%d\n", ret); + goto free_buf; + } + + if (eid <= ubc_eid_end) { + dev_err(dev, "update cluster ubc wrong, eid=%#x\n", eid); + ret = -EINVAL; + goto free_buf; + } + + ubc->uent->eid = eid & UB_COMPACT_EID_MASK; + dev_info(dev, "update cluster ubc eid to %#x\n", ubc->uent->eid); + + ret = ub_cfg_read_dword(ubc->uent, UB_FM_CNA, &fm_cna); + if (ret) { + dev_err(dev, "read fm cna failed, ret=%d\n", ret); + goto free_buf; + } + + ubc->uent->fm_cna = fm_cna & UB_FM_CNA_MASK; + dev_info(dev, "update cluster ubc fm cna to %#x\n", ubc->uent->fm_cna); + + ret = ub_query_ent_na(ubc->uent, buf); + if (ret) { + dev_err(dev, "update cluster ubc cna failed, ret=%d\n", ret); + goto free_buf; + } + + ret = ub_query_port_na(ubc->uent, buf); + +free_buf: + kfree(buf); +out: + return ret; +} + +static void ub_cfg_cpl_notify_handler(struct ub_bus_controller *ubc, void *msg, + u16 p_len) +{ + struct pool_msg_pkt *pkt = (struct pool_msg_pkt *)msg; + struct cfg_cpl_notify_pld *notify = &pkt->notify; + struct msg_pkt_header *header = &pkt->header; + u8 rsp_status = UB_MSG_RSP_SUCCESS; + int ret; + + if (p_len != MSG_CFG_CPL_NOTIFY_SIZE) { + dev_err(&ubc->dev, "notify msg len is wrong, len=%#x\n", p_len); + rsp_status = UB_MSG_RSP_CMD_LEN_ERR; + goto rsp; + } + + ret = ub_fm_flush_ubc_info(ubc); + if (ret) { + rsp_status = err_to_msg_rsp(ret); + goto rsp; + } + + ret = ub_notify_bus_instance_handle(ubc, notify->flag, notify->guid, + notify->eid[0], ubc->uent->upi); + if (ret) { + dev_err(&ubc->dev, "handle notify bi failed, ret=%d\n", ret); + rsp_status = err_to_msg_rsp(ret); + } +rsp: + header->msgetah.rsp_status = rsp_status; + ub_cfg_cpl_notify_msg_rsp(ubc, header); +} + +static void ub_pool_bi_handler(struct ub_bus_controller *ubc, void *msg, u16 p_len) +{ + struct pool_msg_pkt *pkt = (struct pool_msg_pkt *)msg; + u32 size = MSG_PKT_HEADER_SIZE << MSG_REQ_SIZE_OFFSET; + struct msg_pkt_header *header = &pkt->header; + struct bi_create_pld *pld = &pkt->create; + u8 status = UB_MSG_RSP_SUCCESS; + struct device *dev = &ubc->dev; + struct msg_info info = {}; + bool local; + int ret; + + if (header->msgetah.sub_msg_code == UB_BI_CREATE) { + if (p_len != MSG_BI_CREATE_SIZE) { + dev_err(dev, "bi create msg len is wrong, len=%#x\n", + p_len); + status = UB_MSG_RSP_CMD_LEN_ERR; + goto rsp; + } + + ret = ub_msg_bus_instance_create(ubc, pld->guid, pld->eid[0], + pld->upi, EID_BYPASS); + } else { + if (p_len != MSG_BI_DESTROY_SIZE) { + dev_err(dev, "bi destroy msg len is wrong, len=%#x\n", + p_len); + status = UB_MSG_RSP_CMD_LEN_ERR; + goto rsp; + } + ret = ub_msg_bus_instance_destroy(ubc, pld->guid); + } + + if (ret) + status = UB_MSG_RSP_EXEC_ENOEXEC; + +rsp: + local = ub_rsp_msg_init(header, status, 0); + message_info_init(&info, local ? ubc->uent : NULL, pkt, pkt, size); + + ret = message_response(ubc->mdev, &info, header->msgetah.code); + if (ret) + dev_err(dev, "send bi rsp msg, ret=%d\n", ret); +} + +static void ub_port_reset_notify_handler(struct ub_bus_controller *ubc, void *msg, + u16 p_len) +{ + u32 size = MSG_PKT_HEADER_SIZE << MSG_REQ_SIZE_OFFSET; + struct pool_msg_pkt *pkt = (struct pool_msg_pkt *)msg; + struct msg_pkt_header *header = &pkt->header; + struct port_reset_notify_pld *pld; + u8 status = UB_MSG_RSP_SUCCESS; + struct msg_info info = {}; + struct ub_port *port = NULL; + bool local; + int ret; + + pld = &pkt->port_reset; + if (p_len != MSG_PORT_RESET_SIZE) { + dev_err(&ubc->dev, + "ub fm port reset notify msg len is wrong, len=%#x\n", + p_len); + status = UB_MSG_RSP_CMD_LEN_ERR; + goto rsp; + } + + if (ub_port_reset_check(ubc->uent, pld->port_index)) { + status = UB_MSG_RSP_EXEC_ENOEXEC; + goto rsp; + } + + port = ubc->uent->ports + pld->port_index; + if (port->shareable && port->domain_boundary) { + if (pld->type == RESET_PREPARE) + ub_notify_share_port(port, RESET_PREPARE); + else if (pld->type == RESET_DONE) + ub_notify_share_port(port, RESET_DONE); + } + +rsp: + local = ub_rsp_msg_init(header, status, 0); + message_info_init(&info, local ? ubc->uent : NULL, header, NULL, size); + ret = message_response(ubc->mdev, &info, header->msgetah.code); + if (ret) + dev_err(&ubc->dev, + "send ub fm port reset notify error, ret=%d\n", ret); +} + static rx_msg_handler_t pool_rx_msg_handler[UB_SUB_MSG_CODE_NUM] = { ub_entity_reg_msg_handler, + ub_entity_rls_msg_handler, + ub_pool_bi_handler, + ub_pool_bi_handler, + ub_cfg_cpl_notify_handler, + ub_port_reset_notify_handler, }; void ub_pool_rx_msg_handler(struct ub_bus_controller *ubc, void *pkt, u16 len) diff --git a/drivers/ub/ubus/pool.h b/drivers/ub/ubus/pool.h index 8178fa2a6862..6d9e68c982f9 100644 --- a/drivers/ub/ubus/pool.h +++ b/drivers/ub/ubus/pool.h @@ -51,6 +51,23 @@ struct entity_reg_msg_pld { #define ENTITY_RS_PLD_SIZE 36 #define ENTITY_REG_PLD_SIZE (ENTITY_BASE_PLD_SIZE + ENTITY_RS_PLD_SIZE) +struct entity_rls_msg_pld { + /* DW0~DW3 */ + u32 eid[4]; + /* DW4 */ + u32 reason : 8; + u32 rsvd1 : 24; +}; +#define ENTITY_RLS_PLD_SIZE 20 + +struct port_reset_notify_pld { + u32 type : 1; + u32 rsvd0 : 15; + u32 port_index : 16; +}; +#define PORT_RESET_NOTIFY_PLD_SIZE 4 + +struct ub_entity *ub_get_fad_ent_by_eid(unsigned int eid); bool ub_rsp_msg_init(struct msg_pkt_header *header, u8 status, u32 plen); void ub_pool_rx_msg_handler(struct ub_bus_controller *ubc, void *pkt, u16 len); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index e94bbc2d8c50..80bc8c02b2d8 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -182,6 +182,7 @@ struct ub_entity { unsigned int eid; unsigned short entity_idx; u32 uent_num; /* ub dev number */ + u32 fm_cna; struct mmio_zone zone[MAX_UB_RES_NUM]; unsigned int total_funcs; u32 token_id; -- Gitee From b1291cba1e5e227d3a7d4c88df6cdc0407f964a2 Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Tue, 23 Sep 2025 14:41:03 +0800 Subject: [PATCH 066/214] vfio:ubus: Support access to function entity resource space ANBZ: #45460 commit 1b2f6e155873505d794a16a0eae149de7ff4d652 openEuler. Support both direct and indirect access to function entity resource space. Vfio ub allows mapping device resource space in user space using mmap, obtaining a virtual address available in user space, and then using this virtual address to access the resource space in user space. Write protection will be applied to the interrput vector table section and the interrupt address table section of resource space. Signed-off-by: Yahui Liu Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/vfio/ubus/Makefile | 2 +- drivers/vfio/ubus/vfio_ub.c | 3 + drivers/vfio/ubus/vfio_ub_core.c | 87 +++++++++ drivers/vfio/ubus/vfio_ub_private.h | 10 + drivers/vfio/ubus/vfio_ub_rdwr.c | 271 ++++++++++++++++++++++++++++ include/uapi/linux/vfio.h | 9 + 6 files changed, 381 insertions(+), 1 deletion(-) create mode 100644 drivers/vfio/ubus/vfio_ub_rdwr.c diff --git a/drivers/vfio/ubus/Makefile b/drivers/vfio/ubus/Makefile index 20c9886c534c..54330ab7685e 100644 --- a/drivers/vfio/ubus/Makefile +++ b/drivers/vfio/ubus/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0+ -vfio-ub-y := vfio_ub.o vfio_ub_core.o vfio_ub_config.o +vfio-ub-y := vfio_ub.o vfio_ub_core.o vfio_ub_config.o vfio_ub_rdwr.o obj-$(CONFIG_VFIO_UB) += vfio-ub.o diff --git a/drivers/vfio/ubus/vfio_ub.c b/drivers/vfio/ubus/vfio_ub.c index 22cf01765e0b..2bc018adb7fe 100644 --- a/drivers/vfio/ubus/vfio_ub.c +++ b/drivers/vfio/ubus/vfio_ub.c @@ -27,6 +27,9 @@ static const struct vfio_device_ops vfio_ub_ops = { .release = vfio_ub_core_release_dev, .open_device = vfio_ub_core_open_device, .close_device = vfio_ub_core_close_device, + .read = vfio_ub_core_read, + .write = vfio_ub_core_write, + .mmap = vfio_ub_core_mmap, }; static int vfio_ub_probe(struct ub_entity *uent, const struct ub_device_id *id) diff --git a/drivers/vfio/ubus/vfio_ub_core.c b/drivers/vfio/ubus/vfio_ub_core.c index 621d50b70c5b..a6e3861dbd0d 100644 --- a/drivers/vfio/ubus/vfio_ub_core.c +++ b/drivers/vfio/ubus/vfio_ub_core.c @@ -134,6 +134,7 @@ void vfio_ub_core_close_device(struct vfio_device *core_vdev) struct vfio_ub_core_device, vdev); vfio_ub_disable(vdev); + vfio_ub_unset_resmap(vdev); mutex_lock(&vdev->igate); if (vdev->req_trigger) { @@ -143,6 +144,92 @@ void vfio_ub_core_close_device(struct vfio_device *core_vdev) mutex_unlock(&vdev->igate); } +static ssize_t vfio_ub_rw(struct vfio_ub_core_device *vdev, char __user *buf, size_t count, + loff_t *ppos, bool iswrite) +{ + unsigned int index = VFIO_UB_OFFSET_TO_INDEX(*ppos); + + if (index >= (VFIO_UB_NUM_REGIONS + vdev->num_regions)) + return -EINVAL; + + switch (index) { + case VFIO_UB_CONFIG_REGION_INDEX: + return vfio_ub_config_rw(vdev, buf, count, ppos, iswrite); + case VFIO_UB_REGION0_INDEX: + case VFIO_UB_REGION1_INDEX: + case VFIO_UB_REGION2_INDEX: + return vfio_ub_res_rw(vdev, buf, count, ppos, iswrite); + default: + return -EINVAL; + } +} + +ssize_t vfio_ub_core_read(struct vfio_device *core_vdev, char __user *buf, size_t count, + loff_t *ppos) +{ + struct vfio_ub_core_device *vdev = + container_of(core_vdev, struct vfio_ub_core_device, vdev); + + if (!count) + return 0; + + return vfio_ub_rw(vdev, buf, count, ppos, false); +} + +ssize_t vfio_ub_core_write(struct vfio_device *core_vdev, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct vfio_ub_core_device *vdev = + container_of(core_vdev, struct vfio_ub_core_device, vdev); + + if (!count) + return 0; + + return vfio_ub_rw(vdev, (char __user *)buf, count, ppos, true); +} + +int vfio_ub_core_mmap(struct vfio_device *core_vdev, struct vm_area_struct *vma) +{ + struct vfio_ub_core_device *vdev = + container_of(core_vdev, struct vfio_ub_core_device, vdev); + u64 phys_len, req_len, pgoff, req_start; + struct ub_entity *uent = vdev->uent; + unsigned int index; + + if (vma->vm_end < vma->vm_start) + return -EINVAL; + + /* + * VM_SHARED means user's changing to mmio can immediately arrive ub device, + * or it is unspecified. + */ + if ((vma->vm_flags & VM_SHARED) == 0) + return -EINVAL; + + index = vma->vm_pgoff >> (VFIO_UB_OFFSET_SHIFT - PAGE_SHIFT); + + if (index > VFIO_UB_REGION2_INDEX) + return -EINVAL; + + phys_len = PAGE_ALIGN(ub_resource_len(uent, index)); + req_len = vma->vm_end - vma->vm_start; + pgoff = vma->vm_pgoff & ((1U << (VFIO_UB_OFFSET_SHIFT - PAGE_SHIFT)) - 1); + req_start = pgoff << PAGE_SHIFT; + + if (req_start + req_len > phys_len) + return -EINVAL; + + vm_flags_set(vma, VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP | + VM_PFNMAP | VM_WIPEONFORK); + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + vma->vm_pgoff = (ub_resource_start(uent, index) >> PAGE_SHIFT) + pgoff; + if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} + int vfio_ub_core_init_dev(struct vfio_device *core_vdev) { struct vfio_ub_core_device *vdev = diff --git a/drivers/vfio/ubus/vfio_ub_private.h b/drivers/vfio/ubus/vfio_ub_private.h index 8219e1a96bba..648c44fd5015 100644 --- a/drivers/vfio/ubus/vfio_ub_private.h +++ b/drivers/vfio/ubus/vfio_ub_private.h @@ -52,6 +52,7 @@ struct vfio_ub_core_device { struct vfio_device vdev; struct ub_entity *uent; struct vfio_ub_config vconfig; + void __iomem *resmap[MAX_UB_RES_NUM]; int num_regions; /* vfio-ub additional regions */ int num_ext_irqs; /* vfio-ub additional extended irqs */ int num_vendor_regions; /* vfio-ub additional vendor-defined regions */ @@ -66,6 +67,7 @@ struct vfio_ub_core_device { }; #define VFIO_UB_OFFSET_SHIFT 40 +#define VFIO_UB_OFFSET_TO_INDEX(off) ((off) >> VFIO_UB_OFFSET_SHIFT) #define VFIO_UB_OFFSET_MASK (((u64)(1) << VFIO_UB_OFFSET_SHIFT) - 1) #define BYTE_SIZE 1 @@ -80,12 +82,20 @@ int vfio_ub_config_init(struct vfio_ub_core_device *vdev); void vfio_ub_config_uninit(struct vfio_ub_core_device *vdev); ssize_t vfio_ub_config_rw(struct vfio_ub_core_device *vdev, char __user *buf, size_t count, loff_t *ppos, bool iswrite); +ssize_t vfio_ub_res_rw(struct vfio_ub_core_device *vdev, char __user *buf, + size_t count, loff_t *ppos, bool iswrite); int vfio_ub_core_register_device(struct vfio_ub_core_device *vdev); void vfio_ub_core_unregister_device(struct vfio_ub_core_device *vdev); int vfio_ub_core_open_device(struct vfio_device *core_vdev); void vfio_ub_core_close_device(struct vfio_device *core_vdev); +ssize_t vfio_ub_core_write(struct vfio_device *core_vdev, const char __user *buf, + size_t count, loff_t *ppos); +ssize_t vfio_ub_core_read(struct vfio_device *core_vdev, char __user *buf, size_t count, + loff_t *ppos); +int vfio_ub_core_mmap(struct vfio_device *core_vdev, struct vm_area_struct *vma); int vfio_ub_core_init_dev(struct vfio_device *core_vdev); void vfio_ub_core_release_dev(struct vfio_device *core_dev); void vfio_ub_core_disable_all(struct vfio_ub_core_device *vdev); +void vfio_ub_unset_resmap(struct vfio_ub_core_device *vdev); #endif /* __VFIO_UB_PRIVATE_H__ */ diff --git a/drivers/vfio/ubus/vfio_ub_rdwr.c b/drivers/vfio/ubus/vfio_ub_rdwr.c new file mode 100644 index 000000000000..3fc8082e22f7 --- /dev/null +++ b/drivers/vfio/ubus/vfio_ub_rdwr.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * Derived from original vfio: + * Copyright 2010 Cisco Systems, Inc. All rights reserved. + * Author: Tom Lyon, pugs@cisco.com + * + * Thanks to Alex Williamson and Tom Lyon for their original + * vfio implementation. + * + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "vfio ub: " fmt + +#include +#include + +#include "vfio_ub_private.h" + +#ifdef __LITTLE_ENDIAN +#define vfio_mmio_read32 ioread32 +#define vfio_mmio_write32 iowrite32 +#define vfio_mmio_read16 ioread16 +#define vfio_mmio_write16 iowrite16 +#else +#define vfio_mmio_read32 ioread32be +#define vfio_mmio_write32 iowrite32be +#define vfio_mmio_read16 ioread16be +#define vfio_mmio_write16 iowrite16be +#endif +#define vfio_mmio_read8 ioread8 +#define vfio_mmio_write8 iowrite8 + +#define VFIO_UB_IOWRITE(size) \ +static void vfio_ub_iowrite##size(struct vfio_ub_core_device *vdev, \ + u##size val, void __iomem *io) \ +{ \ + vfio_mmio_write##size(val, io); \ +} + +VFIO_UB_IOWRITE(8) +VFIO_UB_IOWRITE(16) +VFIO_UB_IOWRITE(32) + +#define VFIO_UB_IOREAD(size) \ +static void vfio_ub_ioread##size(struct vfio_ub_core_device *vdev, \ + u##size * val, void __iomem *io) \ +{ \ + *val = vfio_mmio_read##size(io); \ +} + +VFIO_UB_IOREAD(8) +VFIO_UB_IOREAD(16) +VFIO_UB_IOREAD(32) + +static int vfio_ub_setup_resmap(struct vfio_ub_core_device *vdev, int res) +{ + struct ub_entity *uent = vdev->uent; + void __iomem *io_addr; + + if (vdev->resmap[res]) + return 0; + + io_addr = ub_iomap(uent, res, 0); + if (!io_addr) + return -ENOMEM; + + vdev->resmap[res] = io_addr; + + return 0; +} + +void vfio_ub_unset_resmap(struct vfio_ub_core_device *vdev) +{ + int res; + + for (res = 0; res < MAX_UB_RES_NUM; res++) { + if (!vdev->resmap[res]) + continue; + + ub_iounmap(vdev->resmap[res]); + vdev->resmap[res] = NULL; + } +} + +static int do_io_rw32(struct vfio_ub_core_device *vdev, char __user *buf, + void __iomem *io, loff_t off, bool iswrite) +{ + u32 val; + + if (iswrite) { + if (copy_from_user(&val, buf, DWORD_SIZE)) + return -EFAULT; + + vfio_ub_iowrite32(vdev, val, io + off); + } else { + vfio_ub_ioread32(vdev, &val, io + off); + if (copy_to_user(buf, &val, DWORD_SIZE)) + return -EFAULT; + } + + return 0; +} + +static int do_io_rw16(struct vfio_ub_core_device *vdev, char __user *buf, + void __iomem *io, loff_t off, bool iswrite) +{ + u16 val; + + if (iswrite) { + if (copy_from_user(&val, buf, WORD_SIZE)) + return -EFAULT; + + vfio_ub_iowrite16(vdev, val, io + off); + } else { + vfio_ub_ioread16(vdev, &val, io + off); + if (copy_to_user(buf, &val, WORD_SIZE)) + return -EFAULT; + } + + return 0; +} + +static int do_io_rw8(struct vfio_ub_core_device *vdev, char __user *buf, + void __iomem *io, loff_t off, bool iswrite) +{ + u8 val; + + if (iswrite) { + if (copy_from_user(&val, buf, BYTE_SIZE)) + return -EFAULT; + + vfio_ub_iowrite8(vdev, val, io + off); + } else { + vfio_ub_ioread8(vdev, &val, io + off); + if (copy_to_user(buf, &val, BYTE_SIZE)) + return -EFAULT; + } + + return 0; +} + +static size_t cal_io_rw_cnt(struct vfio_ub_core_device *vdev, int res, loff_t off, size_t count) +{ + size_t cnt = 0; + u64 left_start; + u32 left_size; + u64 right_start; + u32 right_size; + + /* Now usi info was installed at resource0 */ + if (res != VFIO_UB_REGION0_INDEX) + return count; + + if (vdev->usi_vector_offset < vdev->usi_addr_offset) { + left_start = vdev->usi_vector_offset; + left_size = vdev->usi_vector_size; + right_start = vdev->usi_addr_offset; + right_size = vdev->usi_addr_size; + } else { + left_start = vdev->usi_addr_offset; + left_size = vdev->usi_addr_size; + right_start = vdev->usi_vector_offset; + right_size = vdev->usi_vector_size; + } + + if (off < left_start) + cnt = min(count, (size_t)(left_start - off)); + else if (off >= left_start + left_size && off < right_start) + cnt = min(count, (size_t)(right_start - off)); + else if (off >= right_start + right_size) + cnt = count; + + return cnt; +} + +static ssize_t do_io_rw(struct vfio_ub_core_device *vdev, void __iomem *io, + char __user *buf, int res, loff_t off, size_t count, bool iswrite) +{ + ssize_t done = 0; + u64 tmp_addr; + u64 tmp_vec; + int ret; + + while (count) { + size_t fillable, filled; + + fillable = cal_io_rw_cnt(vdev, res, off, count); + if (fillable >= DWORD_SIZE && !(off % DWORD_SIZE)) { + ret = do_io_rw32(vdev, buf, io, off, iswrite); + if (ret) + return ret; + + filled = DWORD_SIZE; + } else if (fillable >= WORD_SIZE && !(off % WORD_SIZE)) { + ret = do_io_rw16(vdev, buf, io, off, iswrite); + if (ret) + return ret; + + filled = WORD_SIZE; + } else if (fillable) { + ret = do_io_rw8(vdev, buf, io, off, iswrite); + if (ret) + return ret; + + filled = BYTE_SIZE; + } else { + tmp_vec = vdev->usi_vector_offset + vdev->usi_vector_size; + tmp_addr = vdev->usi_addr_offset + vdev->usi_addr_size; + if (off >= vdev->usi_vector_offset && off < tmp_vec) + filled = min(count, (size_t)(tmp_vec - off)); + else if (off >= vdev->usi_addr_offset && off < tmp_addr) + filled = min(count, (size_t)(tmp_addr - off)); + else + return -EFAULT; + + if (!iswrite) { + u8 val = 0xff; + + for (size_t i = 0; i < filled; i++) { + if (copy_to_user(buf + i, &val, 1)) + return -EFAULT; + } + } + } + + count -= filled; + done += filled; + off += filled; + buf += filled; + } + + return done; +} + +ssize_t vfio_ub_res_rw(struct vfio_ub_core_device *vdev, char __user *buf, + size_t count, loff_t *ppos, bool iswrite) +{ + struct ub_entity *uent = vdev->uent; + loff_t pos = *ppos & VFIO_UB_OFFSET_MASK; + int res = VFIO_UB_OFFSET_TO_INDEX(*ppos); + resource_size_t end; + void __iomem *io_addr; + int ret; + ssize_t done; + + if (ub_resource_start(uent, res)) + end = ub_resource_len(uent, res); + else + return -EINVAL; + + if (pos >= end) + return -EINVAL; + + count = min(count, (size_t)(end - pos)); + + ret = vfio_ub_setup_resmap(vdev, res); + if (ret) + return ret; + + io_addr = vdev->resmap[res]; + + done = do_io_rw(vdev, io_addr, buf, res, pos, count, iswrite); + if (done > 0) + *ppos += done; + + return done; +} diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h index f906875fc314..2172a2052f38 100644 --- a/include/uapi/linux/vfio.h +++ b/include/uapi/linux/vfio.h @@ -687,6 +687,15 @@ enum { VFIO_PCI_NUM_IRQS }; +/* UB regions types */ +enum { + VFIO_UB_REGION0_INDEX = 0, + VFIO_UB_REGION1_INDEX, + VFIO_UB_REGION2_INDEX, + VFIO_UB_CONFIG_REGION_INDEX, + VFIO_UB_NUM_REGIONS +}; + /* * The vfio-ccw bus driver makes use of the following fixed region and * IRQ index mapping. Unimplemented regions return a size of zero. -- Gitee From aaab7f4d6daf5d8b96524dc1316e33daad21e7f5 Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Mon, 22 Sep 2025 15:04:18 +0800 Subject: [PATCH 067/214] ub:ubus: Add ub service driver framework ANBZ: #45460 commit 7533da4017f88c73338c39b0263ce42475d63b6e openEuler. Add ub service bus type and register it to the bus framework. Add interfaces to register and unregister ub service driver. Signed-off-by: Yahui Liu Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/services/service.c | 70 ++++++++++++++++++++++++++++++ drivers/ub/ubus/services/service.h | 34 +++++++++++++++ drivers/ub/ubus/ubus_driver.c | 30 +++++++++++++ drivers/ub/ubus/ubus_driver.h | 1 + 5 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/services/service.c create mode 100644 drivers/ub/ubus/services/service.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 03a8128a6473..129cf606dfd1 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -7,6 +7,6 @@ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc. ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o omm.o ioctl.o eu.o link.o ubus-y += instance.o pool.o -ubus-y += services/ras.o +ubus-y += services/ras.o services/service.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/services/service.c b/drivers/ub/ubus/services/service.c new file mode 100644 index 000000000000..b925da25cec7 --- /dev/null +++ b/drivers/ub/ubus/services/service.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus service: " fmt + +#include "../ubus.h" +#include "../ubus_driver.h" +#include "service.h" + +static int ub_service_probe(struct device *dev) +{ + struct ub_service_device *sdev; + struct ub_service_driver *sdrv; + int status; + + if (!dev || !dev->driver) + return -ENODEV; + + sdrv = to_ub_service_driver(dev->driver); + if (!sdrv || !sdrv->probe) + return -ENODEV; + + sdev = to_ub_service_device(dev); + status = sdrv->probe(sdev); + if (status) + return status; + + get_device(dev); + return 0; +} + +static int ub_service_remove(struct device *dev) +{ + struct ub_service_device *sdev; + struct ub_service_driver *sdrv; + + if (!dev || !dev->driver) + return 0; + + sdev = to_ub_service_device(dev); + sdrv = to_ub_service_driver(dev->driver); + if (sdrv && sdrv->remove) { + sdrv->remove(sdev); + put_device(dev); + } + + return 0; +} + +static void ub_service_shutdown(struct device *dev) +{ +} + +int ub_service_driver_register(struct ub_service_driver *drv) +{ + drv->driver.name = drv->name; + drv->driver.bus = &ub_service_bus_type; + drv->driver.probe = ub_service_probe; + drv->driver.remove = ub_service_remove; + drv->driver.shutdown = ub_service_shutdown; + + return driver_register(&drv->driver); +} + +void ub_service_driver_unregister(struct ub_service_driver *drv) +{ + driver_unregister(&drv->driver); +} diff --git a/drivers/ub/ubus/services/service.h b/drivers/ub/ubus/services/service.h new file mode 100644 index 000000000000..260f1a808746 --- /dev/null +++ b/drivers/ub/ubus/services/service.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ +#ifndef __SERVICES_SERVICE_H__ +#define __SERVICES_SERVICE_H__ + +struct ub_service_device { + int irq; + struct ub_entity *uent; + u32 service; + void *priv_data; + struct device device; +}; + +struct ub_service_driver { + const char *name; + int (*probe)(struct ub_service_device *dev); + void (*remove)(struct ub_service_device *dev); + + u32 service; + + struct device_driver driver; +}; + +#define to_ub_service_device(d) \ + container_of(d, struct ub_service_device, device) +#define to_ub_service_driver(d) \ + container_of(d, struct ub_service_driver, driver) + +int ub_service_driver_register(struct ub_service_driver *drv); +void ub_service_driver_unregister(struct ub_service_driver *drv); + +#endif /* __SERVICES_SERVICE_H__ */ diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index fe5591c33999..b5cb2dcc37c8 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -19,6 +19,8 @@ #include "ubus_controller.h" #include "ubus_inner.h" #include "ubus_entity.h" +#include "services/service.h" +#include "ubus_driver.h" bool entity_flex_en; module_param(entity_flex_en, bool, 0444); @@ -619,6 +621,27 @@ void ub_bus_type_uninit(void) ub_bus_type.num_vf = NULL; } +static int ub_service_bus_match(struct device *dev, struct device_driver *drv) +{ + struct ub_service_device *sdev; + struct ub_service_driver *sdrv; + + if (drv->bus != &ub_service_bus_type || dev->bus != &ub_service_bus_type) + return 0; + + sdev = to_ub_service_device(dev); + sdrv = to_ub_service_driver(drv); + if (sdrv->service != sdev->service) + return 0; + + return 1; +} + +struct bus_type ub_service_bus_type = { + .name = "ub_service", + .match = ub_service_bus_match, +}; + int ub_host_probe(void) { int ret; @@ -628,8 +651,14 @@ int ub_host_probe(void) if (ret) goto ub_cfg_ops_init_fail; + ret = bus_register(&ub_service_bus_type); + if (ret) + goto bus_register_fail; + return 0; +bus_register_fail: + unregister_ub_cfg_ops(); ub_cfg_ops_init_fail: ub_bus_type_uninit(); return ret; @@ -638,6 +667,7 @@ EXPORT_SYMBOL_GPL(ub_host_probe); void ub_host_remove(void) { + bus_unregister(&ub_service_bus_type); unregister_ub_cfg_ops(); ub_bus_type_uninit(); } diff --git a/drivers/ub/ubus/ubus_driver.h b/drivers/ub/ubus/ubus_driver.h index 6a259d224942..b2eab906fa31 100644 --- a/drivers/ub/ubus/ubus_driver.h +++ b/drivers/ub/ubus/ubus_driver.h @@ -7,5 +7,6 @@ #define __UBUS_DRIVER_H__ extern struct rw_semaphore ub_bus_sem; +extern struct bus_type ub_service_bus_type; #endif /* __UBUS_DRIVER_H__ */ -- Gitee From 92bfc8c194e517cbb9d7334aada2dbda1862d9ee Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Tue, 23 Sep 2025 15:10:50 +0800 Subject: [PATCH 068/214] vfio:ubus: Support configuration for function entity interrupt ANBZ: #45460 commit 491f4f7f7d116dc82dcc36e5b23152620daf2ed4 openEuler. Support configuration for function entity interrupt. In user space, device interrupts can be enabled through vfio. Vfio ub invokes the interrupt callback interface of the ubus driver to request interrupts and registers a user-space interrupt handler for these interrupts. When an interrupt occurs, the corresponding kernel interrupt callback function of vfio-ub sends a message to user space, notifying it of the occurrence of the corresponding interrupt. Signed-off-by: Yahui Liu Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/vfio/ubus/Makefile | 2 +- drivers/vfio/ubus/vfio_ub_core.c | 3 + drivers/vfio/ubus/vfio_ub_intrs.c | 317 ++++++++++++++++++++++++++++ drivers/vfio/ubus/vfio_ub_private.h | 14 ++ include/uapi/linux/vfio.h | 7 + 5 files changed, 342 insertions(+), 1 deletion(-) create mode 100644 drivers/vfio/ubus/vfio_ub_intrs.c diff --git a/drivers/vfio/ubus/Makefile b/drivers/vfio/ubus/Makefile index 54330ab7685e..e575a11b39b4 100644 --- a/drivers/vfio/ubus/Makefile +++ b/drivers/vfio/ubus/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0+ -vfio-ub-y := vfio_ub.o vfio_ub_core.o vfio_ub_config.o vfio_ub_rdwr.o +vfio-ub-y := vfio_ub.o vfio_ub_core.o vfio_ub_config.o vfio_ub_rdwr.o vfio_ub_intrs.o obj-$(CONFIG_VFIO_UB) += vfio-ub.o diff --git a/drivers/vfio/ubus/vfio_ub_core.c b/drivers/vfio/ubus/vfio_ub_core.c index a6e3861dbd0d..0d1db20a44b5 100644 --- a/drivers/vfio/ubus/vfio_ub_core.c +++ b/drivers/vfio/ubus/vfio_ub_core.c @@ -114,6 +114,8 @@ static int vfio_ub_enable(struct vfio_ub_core_device *vdev) static void vfio_ub_disable(struct vfio_ub_core_device *vdev) { vfio_ub_config_uninit(vdev); + vfio_ub_set_irqs_ioctl(vdev, VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER, + vdev->irq_type, 0, 0, NULL); /* clear device caches when vm exit or crash */ if (vdev->reset_works) @@ -236,6 +238,7 @@ int vfio_ub_core_init_dev(struct vfio_device *core_vdev) container_of(core_vdev, struct vfio_ub_core_device, vdev); vdev->uent = to_ub_entity(core_vdev->dev); + vdev->irq_type = VFIO_UB_NUM_IRQS; mutex_init(&vdev->igate); return 0; } diff --git a/drivers/vfio/ubus/vfio_ub_intrs.c b/drivers/vfio/ubus/vfio_ub_intrs.c new file mode 100644 index 000000000000..6b0ce3ccd068 --- /dev/null +++ b/drivers/vfio/ubus/vfio_ub_intrs.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * Derived from original vfio: + * Copyright 2010 Cisco Systems, Inc. All rights reserved. + * Author: Tom Lyon, pugs@cisco.com + * + * Thanks to Alex Williamson and Tom Lyon for their original + * vfio implementation. + * + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "vfio ub: " fmt + +#include +#include +#include +#include +#include + +#include "vfio_ub_private.h" + +static const char *ub_get_name(const struct ub_entity *uent) +{ + return dev_name(&uent->dev); +} + +static irqreturn_t vfio_ub_intr_handler(int irq, void *arg) +{ + struct eventfd_ctx *trigger = (struct eventfd_ctx *)arg; + + eventfd_signal(trigger, 1); + return IRQ_HANDLED; +} + +static int vfio_ub_intr_tid_update(struct vfio_ub_core_device *vdev, int vector) +{ + void __iomem *vaddr; + u32 tid, addr_num; + u16 addr_idx; + int ret; + + vaddr = ub_iomap(vdev->uent, 0, 0); + if (!vaddr) + return -ENOMEM; + + ret = ub_cfg_read_dword(vdev->uent, UB_ENTITY_TOKEN_ID, &tid); + if (ret) { + ub_iounmap(vaddr); + return ret; + } + + addr_num = ub_intr_addr_count(vdev->uent); + addr_idx = readl(vaddr + vdev->usi_vector_offset + + vector * UB_INTR_VECTOR_ENTRY_SIZE + + UB_INTR_VECTOR_ADDR_INDEX) & GENMASK(15, 0); + if (addr_idx >= addr_num) { + ub_iounmap(vaddr); + ub_err(vdev->uent, "invalid usi addr table info, addr_idx=%#x, addr_num=%#x\n", + addr_idx, addr_num); + return -EFAULT; + } + + writel(0, vaddr + vdev->usi_addr_offset + addr_idx * UB_INTR_ADDR_ENTRY_SIZE + + UB_INTR_ADDR_TOKENID); + writel((tid & UB_INTR_ADDR_TOKENID_MASK) | (0x1 << UB_INTR_ADDR_VALID_BIT), + vaddr + vdev->usi_addr_offset + addr_idx * UB_INTR_ADDR_ENTRY_SIZE + + UB_INTR_ADDR_TOKENID); + + ub_iounmap(vaddr); + return 0; +} + +static int vfio_ub_intr_set_vector_signal(struct vfio_ub_core_device *vdev, + int vector, int eventfd) +{ + struct ub_entity *uent = vdev->uent; + struct eventfd_ctx *trigger; + int irq_vec, ret; + + irq_vec = ub_irq_vector(uent, vector); + + /* if already assigned interrupts, just free it and reassigned it later. */ + if (vdev->ctx[vector].trigger != NULL) { + irq_bypass_unregister_producer(&vdev->ctx[vector].producer); + free_irq(irq_vec, vdev->ctx[vector].trigger); + kfree(vdev->ctx[vector].name); + eventfd_ctx_put(vdev->ctx[vector].trigger); + vdev->ctx[vector].trigger = NULL; + } + + if (eventfd < 0) + return 0; + + vdev->ctx[vector].name = kasprintf(GFP_KERNEL, "vfio-ub-intr[%d](%s)", + vector, ub_get_name(uent)); + if (!vdev->ctx[vector].name) + return -ENOMEM; + + trigger = eventfd_ctx_fdget(eventfd); + if (IS_ERR(trigger)) { + ret = PTR_ERR(trigger); + goto err_kasprintf; + } + + ret = request_irq(irq_vec, vfio_ub_intr_handler, 0, + vdev->ctx[vector].name, trigger); + if (ret) + goto err_eventfd; + + ret = vfio_ub_intr_tid_update(vdev, vector); + if (ret) + goto err_irq; + + vdev->ctx[vector].producer.irq = irq_vec; + vdev->ctx[vector].producer.token = trigger; + ret = irq_bypass_register_producer(&vdev->ctx[vector].producer); + if (unlikely(ret)) { + ub_info(uent, + "irq bypass producer registration failed, ret=%d\n", ret); + vdev->ctx[vector].producer.token = NULL; + } + vdev->ctx[vector].trigger = trigger; + + return 0; + +err_irq: + free_irq(irq_vec, trigger); +err_eventfd: + eventfd_ctx_put(trigger); +err_kasprintf: + kfree(vdev->ctx[vector].name); + vdev->ctx[vector].name = NULL; + return ret; +} + +static int vfio_ub_intr_set_block(struct vfio_ub_core_device *vdev, unsigned int start, + unsigned int count, int32_t *fds) +{ + int i, j, ret = 0; + + if ((start >= vdev->num_ctx) || ((start + count) > vdev->num_ctx)) + return -EINVAL; + + for (i = 0, j = start; i < count; i++, j++) { + int fd = fds ? fds[i] : -1; + + ret = vfio_ub_intr_set_vector_signal(vdev, j, fd); + if (ret) + break; + } + + if (ret) { + for (--j; j >= (int)start; j--) + vfio_ub_intr_set_vector_signal(vdev, j, -1); + } + + return ret; +} + +static void vfio_ub_intr_disable(struct vfio_ub_core_device *vdev) +{ + struct ub_entity *uent = vdev->uent; + + vfio_ub_intr_set_block(vdev, 0, vdev->num_ctx, NULL); + + ub_disable_intr(uent); + + vdev->irq_type = VFIO_UB_NUM_IRQS; + vdev->num_ctx = 0; + kfree(vdev->ctx); +} + +static int vfio_ub_intr_enable(struct vfio_ub_core_device *vdev, int nvec) +{ + struct ub_entity *uent = vdev->uent; + int ret; + + vdev->ctx = kcalloc(nvec, sizeof(struct vfio_ub_irq_ctx), GFP_KERNEL); + if (!vdev->ctx) + return -ENOMEM; + + ret = ub_alloc_irq_vectors(uent, 1, nvec); + if (ret < nvec) { + if (ret > 0) + ub_disable_intr(uent); + kfree(vdev->ctx); + return ret; + } + + vdev->num_ctx = nvec; + vdev->irq_type = VFIO_UB_INTR_IRQ_INDEX; + + return 0; +} + +static int vfio_ub_intr_irq_trigger(struct vfio_ub_core_device *vdev, + unsigned int index, unsigned int start, + unsigned int count, uint32_t flags, void *data) +{ + int ret; + unsigned int i; + + if (irq_is(vdev, index) && !count && (flags & VFIO_IRQ_SET_DATA_NONE)) { + vfio_ub_intr_disable(vdev); + return 0; + } + + /* DATA_EVENTFD with ACTION_TRIGGER means set irqs */ + if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { + int32_t *fds = (int32_t *)data; + + /* if ub device irq has setted, then update irq eventfd handler */ + if (irq_is(vdev, index)) + return vfio_ub_intr_set_block(vdev, start, count, fds); + + ret = vfio_ub_intr_enable(vdev, start + count); + if (ret) + return ret; + + ret = vfio_ub_intr_set_block(vdev, start, count, fds); + if (ret) + vfio_ub_intr_disable(vdev); + + return ret; + } + + if (!irq_is(vdev, index) || ((start + count) > vdev->num_ctx)) + return -EINVAL; + + for (i = start; i < (start + count); i++) { + if (!vdev->ctx[i].trigger) + continue; + /* DATA_NONE with ACTION_TRIGGER means trigger all irqs */ + if (flags & VFIO_IRQ_SET_DATA_NONE) { + eventfd_signal(vdev->ctx[i].trigger, 1); + } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { + /* DATA_BOOL with ACTION_TRIGGER means trigger sparse irqs */ + uint8_t *bools = (uint8_t *)data; + + if (bools[i - start]) + eventfd_signal(vdev->ctx[i].trigger, 1); + } + } + + return 0; +} + +static int vfio_ub_set_ctx_trigger_single(struct eventfd_ctx **ctx_pptr, + unsigned int num, uint32_t flags, + void *data) +{ + struct eventfd_ctx *efdctx; + int32_t file_desc; + + if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { + if (!num) + return -EINVAL; + + file_desc = *(int32_t *)data; + if (file_desc == -1) { + if (*ctx_pptr) + eventfd_ctx_put(*ctx_pptr); + *ctx_pptr = NULL; + } else if (file_desc >= 0) { + efdctx = eventfd_ctx_fdget(file_desc); + if (IS_ERR(efdctx)) + return PTR_ERR(efdctx); + + if (*ctx_pptr) + eventfd_ctx_put(*ctx_pptr); + + *ctx_pptr = efdctx; + } + return 0; + } + + return -EINVAL; +} + +static int vfio_ub_set_req_trigger(struct vfio_ub_core_device *vdev, + unsigned int index, unsigned int start, + unsigned int count, uint32_t flags, void *data) +{ + if (index != VFIO_UB_REQ_IRQ_INDEX || start != 0 || count > 1) + return -EINVAL; + + return vfio_ub_set_ctx_trigger_single(&vdev->req_trigger, count, flags, data); +} + +int vfio_ub_set_irqs_ioctl(struct vfio_ub_core_device *vdev, uint32_t flags, + unsigned int index, unsigned int start, + unsigned int count, void *data) +{ + int ret = 0; + + switch (index) { + case VFIO_UB_INTR_IRQ_INDEX: + if (flags & VFIO_IRQ_SET_ACTION_TRIGGER) + ret = vfio_ub_intr_irq_trigger(vdev, index, start, + count, flags, data); + break; + case VFIO_UB_REQ_IRQ_INDEX: + if (flags & VFIO_IRQ_SET_ACTION_TRIGGER) + ret = vfio_ub_set_req_trigger(vdev, index, start, + count, flags, data); + break; + default: + ret = -ENOTTY; + break; + } + + return ret; +} diff --git a/drivers/vfio/ubus/vfio_ub_private.h b/drivers/vfio/ubus/vfio_ub_private.h index 648c44fd5015..3b3ae05e2228 100644 --- a/drivers/vfio/ubus/vfio_ub_private.h +++ b/drivers/vfio/ubus/vfio_ub_private.h @@ -22,6 +22,13 @@ #define UB_CFG0_MAX_CAP 255 #define UB_CFG1_MAX_CAP 511 /* cfg1 cap start from 256 */ +struct vfio_ub_irq_ctx { + struct eventfd_ctx *trigger; + char *name; + bool masked; + struct irq_bypass_producer producer; +}; + struct vfio_ub_core_device; struct perm_bits { u8 *virt; @@ -53,6 +60,9 @@ struct vfio_ub_core_device { struct ub_entity *uent; struct vfio_ub_config vconfig; void __iomem *resmap[MAX_UB_RES_NUM]; + int num_ctx; /* num of enabled irqs */ + int irq_type; /* interrupt type of this uent */ + struct vfio_ub_irq_ctx *ctx; int num_regions; /* vfio-ub additional regions */ int num_ext_irqs; /* vfio-ub additional extended irqs */ int num_vendor_regions; /* vfio-ub additional vendor-defined regions */ @@ -70,6 +80,7 @@ struct vfio_ub_core_device { #define VFIO_UB_OFFSET_TO_INDEX(off) ((off) >> VFIO_UB_OFFSET_SHIFT) #define VFIO_UB_OFFSET_MASK (((u64)(1) << VFIO_UB_OFFSET_SHIFT) - 1) +#define irq_is(vdev, type) ((vdev)->irq_type == (type)) #define BYTE_SIZE 1 #define WORD_SIZE 2 #define DWORD_SIZE 4 @@ -84,6 +95,9 @@ ssize_t vfio_ub_config_rw(struct vfio_ub_core_device *vdev, char __user *buf, size_t count, loff_t *ppos, bool iswrite); ssize_t vfio_ub_res_rw(struct vfio_ub_core_device *vdev, char __user *buf, size_t count, loff_t *ppos, bool iswrite); +int vfio_ub_set_irqs_ioctl(struct vfio_ub_core_device *vdev, uint32_t flags, + unsigned int index, unsigned int start, + unsigned int count, void *data); int vfio_ub_core_register_device(struct vfio_ub_core_device *vdev); void vfio_ub_core_unregister_device(struct vfio_ub_core_device *vdev); int vfio_ub_core_open_device(struct vfio_device *core_vdev); diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h index 2172a2052f38..c6c5d8aa5c1f 100644 --- a/include/uapi/linux/vfio.h +++ b/include/uapi/linux/vfio.h @@ -687,6 +687,13 @@ enum { VFIO_PCI_NUM_IRQS }; +/* UB irq types */ +enum { + VFIO_UB_INTR_IRQ_INDEX, + VFIO_UB_REQ_IRQ_INDEX, + VFIO_UB_NUM_IRQS +}; + /* UB regions types */ enum { VFIO_UB_REGION0_INDEX = 0, -- Gitee From 23b7ab052280c3c20678306e45213f9773cbbb38 Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Mon, 22 Sep 2025 15:57:11 +0800 Subject: [PATCH 069/214] ub:ubus: Add generic ub component driver ANBZ: #45460 commit 4885c5bba9c1b11faff1754a6e323b40872e82ad openEuler. Add generic ub component driver to match with ub bus controller and ub switch. During device probe, register all capable service devices also. Signed-off-by: Yahui Liu Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/services.h | 11 ++ drivers/ub/ubus/services/gucd.c | 209 +++++++++++++++++++++++++++++ drivers/ub/ubus/services/service.h | 5 + drivers/ub/ubus/ubus_driver.c | 8 ++ 5 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/services.h create mode 100644 drivers/ub/ubus/services/gucd.c diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 129cf606dfd1..2ac8389ed26c 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -7,6 +7,6 @@ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc. ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o omm.o ioctl.o eu.o link.o ubus-y += instance.o pool.o -ubus-y += services/ras.o services/service.o +ubus-y += services/ras.o services/service.o services/gucd.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/services.h b/drivers/ub/ubus/services.h new file mode 100644 index 000000000000..71fe6aa57357 --- /dev/null +++ b/drivers/ub/ubus/services.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ +#ifndef __SERVICES_H__ +#define __SERVICES_H__ + +int ub_services_init(void); +void ub_services_exit(void); + +#endif /* __SERVICES_H__ */ diff --git a/drivers/ub/ubus/services/gucd.c b/drivers/ub/ubus/services/gucd.c new file mode 100644 index 000000000000..7d67b8b809ba --- /dev/null +++ b/drivers/ub/ubus/services/gucd.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus gucd: " fmt + +#include "../ubus.h" +#include "../decoder.h" +#include "../ubus_driver.h" +#include "service.h" + +static const struct ub_device_id component_device_ids[] = { + { UB_ENTITY_CLASS(UB_CLASS_BUS_CONTROLLER, (u16)~0)}, + { UB_ENTITY_CLASS(UB_CLASS_SWITCH_UB, (u16)~0)}, + {} +}; +MODULE_DEVICE_TABLE(ub, component_device_ids); + +static int get_component_service_capability(struct ub_entity *uent) +{ + int services = 0; + + return services; +} + +static void release_service_device(struct device *dev) +{ + kfree(to_ub_service_device(dev)); +} + +static int ub_component_service_init(struct ub_entity *uent, int service) +{ + struct ub_service_device *sdev; + struct device *dev; + int ret; + + sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); + if (!sdev) + return -ENOMEM; + + sdev->uent = uent; + sdev->service = service; + + dev = &sdev->device; + dev->bus = &ub_service_bus_type; + dev->release = release_service_device; + dev_set_name(dev, "%s:service%03x", ub_name(uent), service); + dev->parent = &uent->dev; + + ret = device_register(dev); + if (ret) + put_device(dev); + + return ret; +} + +static int ub_component_service_register(struct ub_entity *uent) +{ + int nr_service = 0; + int capabilities; + int i; + + /* Get and check component services */ + capabilities = get_component_service_capability(uent); + if (!capabilities) + return 0; + + for (i = 0; i < UB_MAXSERIVCES; i++) { + int service = 1 << i; + + if (!(capabilities & service)) + continue; + if (!ub_component_service_init(uent, service)) + nr_service++; + } + + if (!nr_service) + return -ENODEV; + + return 0; +} + +static void ub_enable_err_msq_ctrl(struct ub_entity *uent) +{ + int ret; + + ret = ub_cfg_write_dword(uent, EMQ_CAP_START + UB_CAP_ERR_MSG_QUE_CTL, + UB_CAP_INTERRUPT_GEN_ENA); + if (ret) + ub_err(uent, "enable error msq controller failed\n"); +} + +static void ub_disable_err_msq_ctrl(struct ub_entity *uent) +{ + int ret; + + ret = ub_cfg_write_dword(uent, EMQ_CAP_START + UB_CAP_ERR_MSG_QUE_CTL, + 0x0); + if (ret) + ub_err(uent, "disable error msq controller failed\n"); +} + +static void ub_setup_bus_controller(struct ub_entity *uent) +{ + u32 vec_num_max; + int usi_count; + + if (ub_cc_supported(uent)) + ub_cc_enable(uent); + + ub_set_user_info(uent); + ub_enable_err_msq_ctrl(uent); + vec_num_max = ub_int_type1_vec_count(uent); + usi_count = ub_alloc_irq_vectors(uent, vec_num_max, vec_num_max); + if (usi_count < 0) { + ub_err(uent, "alloc irq for ub bus controller failed, usi_count=%d\n", + usi_count); + return; + } + + if ((u32)usi_count < vec_num_max) + ub_err(uent, "alloc irq vectors failed, usi count=%d, vec_num_max=%u\n", + usi_count, vec_num_max); + else + ub_init_decoder_usi(uent); +} + +static void ub_unset_bus_controller(struct ub_entity *uent) +{ + ub_uninit_decoder_usi(uent); + ub_disable_intr(uent); + ub_disable_err_msq_ctrl(uent); + ub_unset_user_info(uent); + + if (ub_cc_supported(uent)) + ub_cc_disable(uent); +} + +static ub_ers_result_t ub_gucd_err_detected(struct ub_entity *uent, ub_channel_state_t state) +{ + if (state == ub_channel_io_normal) + return UB_ERS_RESULT_NEED_RESET; + + if (state == ub_channel_io_frozen) + return UB_ERS_RESULT_NEED_RESET; + + return UB_ERS_RESULT_CAN_RECOVER; +} + +static const struct ub_error_handlers gucd_err_handler = { + .ub_error_detected = ub_gucd_err_detected, +}; + +static int ub_component_probe(struct ub_entity *uent, + const struct ub_device_id *id) +{ + if (is_ibus_controller(uent)) + ub_setup_bus_controller(uent); + + return ub_component_service_register(uent); +} + +static int remove_iter(struct device *dev, void *data) +{ + if (dev->bus == &ub_service_bus_type) + device_unregister(dev); + + return 0; +} + +static void ub_component_remove(struct ub_entity *uent) +{ + device_for_each_child(&uent->dev, NULL, remove_iter); + if (is_ibus_controller(uent)) + ub_unset_bus_controller(uent); +} + +static struct ub_driver ub_component_device_driver = { + .name = "ub_generic_component", + .id_table = component_device_ids, + .probe = ub_component_probe, + .remove = ub_component_remove, + .err_handler = &gucd_err_handler, + .driver_managed_dma = true, +}; + +static void ub_init_services(void) +{ + ub_ras_init(); +} + +static void ub_uninit_services(void) +{ + ub_ras_uninit(); +} + +int ub_services_init(void) +{ + ub_init_services(); + + return ub_register_driver(&ub_component_device_driver); +} + +void ub_services_exit(void) +{ + ub_unregister_driver(&ub_component_device_driver); + ub_uninit_services(); +} diff --git a/drivers/ub/ubus/services/service.h b/drivers/ub/ubus/services/service.h index 260f1a808746..2e10f7293dbe 100644 --- a/drivers/ub/ubus/services/service.h +++ b/drivers/ub/ubus/services/service.h @@ -5,6 +5,8 @@ #ifndef __SERVICES_SERVICE_H__ #define __SERVICES_SERVICE_H__ +#define UB_MAXSERIVCES 3 + struct ub_service_device { int irq; struct ub_entity *uent; @@ -28,6 +30,9 @@ struct ub_service_driver { #define to_ub_service_driver(d) \ container_of(d, struct ub_service_driver, driver) +void ub_ras_init(void); +void ub_ras_uninit(void); + int ub_service_driver_register(struct ub_service_driver *drv); void ub_service_driver_unregister(struct ub_service_driver *drv); diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index b5cb2dcc37c8..ed6a0e353d65 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -13,6 +13,7 @@ #include #include +#include "services.h" #include "sysfs.h" #include "ubus.h" #include "ubus_config.h" @@ -655,8 +656,14 @@ int ub_host_probe(void) if (ret) goto bus_register_fail; + ret = ub_services_init(); + if (ret) + goto ub_services_init_fail; + return 0; +ub_services_init_fail: + bus_unregister(&ub_service_bus_type); bus_register_fail: unregister_ub_cfg_ops(); ub_cfg_ops_init_fail: @@ -667,6 +674,7 @@ EXPORT_SYMBOL_GPL(ub_host_probe); void ub_host_remove(void) { + ub_services_exit(); bus_unregister(&ub_service_bus_type); unregister_ub_cfg_ops(); ub_bus_type_uninit(); -- Gitee From c34d1b740c07510ab7c13b775ce87389b286b40e Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Tue, 23 Sep 2025 15:47:29 +0800 Subject: [PATCH 070/214] vfio:ubus: support obtaining and configuring function entity information in user mode ANBZ: #45460 commit f3de0a5a7a7981307380fd70bcceb609ba413abe openEuler. Support obtaining and configuring function entity information in user mode. Vfio ub provides ioctl command to retrieve the corresponding information from the ubus driver's device structure to return to user-space. Signed-off-by: Yahui Liu Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/vfio/ubus/vfio_ub.c | 6 + drivers/vfio/ubus/vfio_ub_core.c | 233 ++++++++++++++++++++++++++++ drivers/vfio/ubus/vfio_ub_private.h | 6 + include/uapi/linux/vfio.h | 1 + 4 files changed, 246 insertions(+) diff --git a/drivers/vfio/ubus/vfio_ub.c b/drivers/vfio/ubus/vfio_ub.c index 2bc018adb7fe..8e89cadc1847 100644 --- a/drivers/vfio/ubus/vfio_ub.c +++ b/drivers/vfio/ubus/vfio_ub.c @@ -29,7 +29,13 @@ static const struct vfio_device_ops vfio_ub_ops = { .close_device = vfio_ub_core_close_device, .read = vfio_ub_core_read, .write = vfio_ub_core_write, + .ioctl = vfio_ub_core_ioctl, .mmap = vfio_ub_core_mmap, + .request = vfio_ub_core_request, + .bind_iommufd = vfio_iommufd_physical_bind, + .unbind_iommufd = vfio_ub_iommufd_physical_unbind, + .attach_ioas = vfio_ub_iommufd_physical_attach_ioas, + .detach_ioas = vfio_ub_iommufd_physical_detach_ioas, }; static int vfio_ub_probe(struct ub_entity *uent, const struct ub_device_id *id) diff --git a/drivers/vfio/ubus/vfio_ub_core.c b/drivers/vfio/ubus/vfio_ub_core.c index 0d1db20a44b5..7c42ff90ed16 100644 --- a/drivers/vfio/ubus/vfio_ub_core.c +++ b/drivers/vfio/ubus/vfio_ub_core.c @@ -146,6 +146,187 @@ void vfio_ub_core_close_device(struct vfio_device *core_vdev) mutex_unlock(&vdev->igate); } +static int vfio_ub_get_device_info(struct vfio_ub_core_device *vdev, unsigned long arg) +{ + unsigned long minsz = offsetofend(struct vfio_device_info, num_irqs); + struct vfio_device_info info; + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + info.flags = VFIO_DEVICE_FLAGS_UB; + + if (vdev->reset_works) + info.flags |= VFIO_DEVICE_FLAGS_RESET; + + info.num_regions = VFIO_UB_NUM_REGIONS + vdev->num_regions + + vdev->num_vendor_regions; + info.num_irqs = VFIO_UB_NUM_IRQS + vdev->num_ext_irqs + + vdev->num_vendor_irqs; + + return copy_to_user((void __user *)arg, &info, minsz) ? + -EFAULT : 0; +} + +static int vfio_ub_get_region_info(struct vfio_ub_core_device *vdev, unsigned long arg) +{ + unsigned long minsz = offsetofend(struct vfio_region_info, offset); + struct ub_entity *uent = vdev->uent; + struct vfio_region_info info; + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if ((info.argsz < minsz) || + (info.index >= (VFIO_UB_NUM_REGIONS + vdev->num_regions))) + return -EINVAL; + + switch (info.index) { + case VFIO_UB_REGION0_INDEX: + case VFIO_UB_REGION1_INDEX: + case VFIO_UB_REGION2_INDEX: + info.offset = VFIO_UB_INDEX_TO_OFFSET(info.index); + info.size = ub_resource_len(uent, info.index); + if (!info.size) { + info.flags = 0; + break; + } + + info.flags = VFIO_REGION_INFO_FLAG_READ | + VFIO_REGION_INFO_FLAG_WRITE | + VFIO_REGION_INFO_FLAG_MMAP; + break; + case VFIO_UB_CONFIG_REGION_INDEX: + info.offset = VFIO_UB_INDEX_TO_OFFSET(info.index); + info.flags = VFIO_REGION_INFO_FLAG_READ | + VFIO_REGION_INFO_FLAG_WRITE; + info.size = 0; + break; + default: + return -EINVAL; + } + + return copy_to_user((void __user *)arg, &info, minsz) ? + -EFAULT : 0; +} + +static int vfio_ub_entity_reset(struct vfio_ub_core_device *vdev) +{ + if (!vdev->reset_works) + return -EINVAL; + + return ub_reset_entity(vdev->uent); +} + +static int vfio_ub_get_irq_count(struct vfio_ub_core_device *vdev, int irq_type) +{ + struct ub_entity *uent = vdev->uent; + + if (irq_type == VFIO_UB_INTR_IRQ_INDEX) { + if (!(uent->no_intr == 0 && uent->intr_type1 == 0)) + return 0; + return ub_intr_vec_count(vdev->uent); + } else if (irq_type == VFIO_UB_REQ_IRQ_INDEX) { + return 1; + } + + return 0; +} + +static int vfio_ub_get_irq_info(struct vfio_ub_core_device *vdev, unsigned long arg) +{ + unsigned long minsz = offsetofend(struct vfio_irq_info, count); + struct vfio_irq_info info; + int ret; + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if ((info.argsz < minsz) || + (info.index >= (VFIO_UB_NUM_IRQS + vdev->num_ext_irqs))) + return -EINVAL; + + info.flags = VFIO_IRQ_INFO_EVENTFD | VFIO_IRQ_INFO_NORESIZE; + + switch (info.index) { + case VFIO_UB_INTR_IRQ_INDEX: + ret = vfio_ub_get_irq_count(vdev, info.index); + if (ret < 0) + return -EFAULT; + + info.count = (u32)ret; + break; + case VFIO_UB_REQ_IRQ_INDEX: + info.count = (u32)vfio_ub_get_irq_count(vdev, info.index); + break; + default: + return -EINVAL; + } + + return copy_to_user((void __user *)arg, &info, minsz) ? + -EFAULT : 0; +} + +static int vfio_ub_set_irqs(struct vfio_ub_core_device *vdev, unsigned long arg) +{ + struct vfio_irq_set hdr; + u32 sz = sizeof(hdr); + size_t data_size = 0; + u8 *data = NULL; + int max; + int ret; + + if (copy_from_user(&hdr, (void __user *)arg, sz)) + return -EFAULT; + + max = vfio_ub_get_irq_count(vdev, hdr.index); + if (max <= 0) + return -EINVAL; + + ret = vfio_set_irqs_validate_and_prepare(&hdr, max, + VFIO_UB_NUM_IRQS + vdev->num_ext_irqs, &data_size); + if (ret) + return ret; + + if (data_size) { + data = (u8 *)memdup_user((void __user *)(arg + sz), data_size); + if (IS_ERR(data)) + return PTR_ERR(data); + } + + mutex_lock(&vdev->igate); + ret = vfio_ub_set_irqs_ioctl(vdev, hdr.flags, hdr.index, hdr.start, + hdr.count, data); + mutex_unlock(&vdev->igate); + kfree(data); + + return ret; +} + +long vfio_ub_core_ioctl(struct vfio_device *core_vdev, unsigned int cmd, unsigned long arg) +{ + struct vfio_ub_core_device *vdev = container_of(core_vdev, + struct vfio_ub_core_device, vdev); + + switch (cmd) { + case VFIO_DEVICE_GET_INFO: + return vfio_ub_get_device_info(vdev, arg); + case VFIO_DEVICE_GET_REGION_INFO: + return vfio_ub_get_region_info(vdev, arg); + case VFIO_DEVICE_GET_IRQ_INFO: + return vfio_ub_get_irq_info(vdev, arg); + case VFIO_DEVICE_SET_IRQS: + return vfio_ub_set_irqs(vdev, arg); + case VFIO_DEVICE_RESET: + return vfio_ub_entity_reset(vdev); + default: + return -ENOTTY; + } +} + static ssize_t vfio_ub_rw(struct vfio_ub_core_device *vdev, char __user *buf, size_t count, loff_t *ppos, bool iswrite) { @@ -251,6 +432,40 @@ void vfio_ub_core_release_dev(struct vfio_device *core_vdev) mutex_destroy(&vdev->igate); } +void vfio_ub_core_request(struct vfio_device *core_vdev, unsigned int count) +{ + struct vfio_ub_core_device *vdev = + container_of(core_vdev, struct vfio_ub_core_device, vdev); + + mutex_lock(&vdev->igate); + + if (vdev->req_trigger) { + if (!(count % 10)) + dev_notice_ratelimited(&vdev->uent->dev, + "Relaying device request to user #%u\n", count); + eventfd_signal(vdev->req_trigger, 1); + } else if (count == 0) { + ub_warn(vdev->uent, + "No device request channel registered, blocked until released by user\n"); + } + + mutex_unlock(&vdev->igate); +} + +void vfio_ub_iommufd_physical_unbind(struct vfio_device *core_vdev) +{ + struct vfio_ub_core_device *vdev = + container_of(core_vdev, struct vfio_ub_core_device, vdev); + + vfio_iommufd_physical_unbind(core_vdev); + + /* Now, valid tid are allocated at attach_ioas interface, + * but tid free at this unbind interface. + * detach_ioas interface are not called at vfio_compt mode. + */ + ub_unset_user_info(vdev->uent); +} + void vfio_ub_core_disable_all(struct vfio_ub_core_device *vdev) { struct ub_entity *uent = vdev->uent; @@ -258,3 +473,21 @@ void vfio_ub_core_disable_all(struct vfio_ub_core_device *vdev) if (!list_empty(&uent->ue_list)) ub_disable_entities(uent); } + +int vfio_ub_iommufd_physical_attach_ioas(struct vfio_device *core_vdev, u32 *pt_id) +{ + struct vfio_ub_core_device *vdev = + container_of(core_vdev, struct vfio_ub_core_device, vdev); + int ret; + + ret = vfio_iommufd_physical_attach_ioas(core_vdev, pt_id); + if (!ret) + ub_set_user_info(vdev->uent); + + return ret; +} + +void vfio_ub_iommufd_physical_detach_ioas(struct vfio_device *core_vdev) +{ + vfio_iommufd_physical_detach_ioas(core_vdev); +} diff --git a/drivers/vfio/ubus/vfio_ub_private.h b/drivers/vfio/ubus/vfio_ub_private.h index 3b3ae05e2228..dada03612cf5 100644 --- a/drivers/vfio/ubus/vfio_ub_private.h +++ b/drivers/vfio/ubus/vfio_ub_private.h @@ -78,6 +78,7 @@ struct vfio_ub_core_device { #define VFIO_UB_OFFSET_SHIFT 40 #define VFIO_UB_OFFSET_TO_INDEX(off) ((off) >> VFIO_UB_OFFSET_SHIFT) +#define VFIO_UB_INDEX_TO_OFFSET(index) ((u64)(index) << VFIO_UB_OFFSET_SHIFT) #define VFIO_UB_OFFSET_MASK (((u64)(1) << VFIO_UB_OFFSET_SHIFT) - 1) #define irq_is(vdev, type) ((vdev)->irq_type == (type)) @@ -102,6 +103,7 @@ int vfio_ub_core_register_device(struct vfio_ub_core_device *vdev); void vfio_ub_core_unregister_device(struct vfio_ub_core_device *vdev); int vfio_ub_core_open_device(struct vfio_device *core_vdev); void vfio_ub_core_close_device(struct vfio_device *core_vdev); +long vfio_ub_core_ioctl(struct vfio_device *core_vdev, unsigned int cmd, unsigned long arg); ssize_t vfio_ub_core_write(struct vfio_device *core_vdev, const char __user *buf, size_t count, loff_t *ppos); ssize_t vfio_ub_core_read(struct vfio_device *core_vdev, char __user *buf, size_t count, @@ -109,7 +111,11 @@ ssize_t vfio_ub_core_read(struct vfio_device *core_vdev, char __user *buf, size_ int vfio_ub_core_mmap(struct vfio_device *core_vdev, struct vm_area_struct *vma); int vfio_ub_core_init_dev(struct vfio_device *core_vdev); void vfio_ub_core_release_dev(struct vfio_device *core_dev); +void vfio_ub_core_request(struct vfio_device *core_vdev, unsigned int count); void vfio_ub_core_disable_all(struct vfio_ub_core_device *vdev); +int vfio_ub_iommufd_physical_attach_ioas(struct vfio_device *vdev, u32 *pt_id); +void vfio_ub_iommufd_physical_detach_ioas(struct vfio_device *vdev); +void vfio_ub_iommufd_physical_unbind(struct vfio_device *core_vdev); void vfio_ub_unset_resmap(struct vfio_ub_core_device *vdev); #endif /* __VFIO_UB_PRIVATE_H__ */ diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h index c6c5d8aa5c1f..18f489206204 100644 --- a/include/uapi/linux/vfio.h +++ b/include/uapi/linux/vfio.h @@ -254,6 +254,7 @@ struct vfio_device_info { #define VFIO_DEVICE_FLAGS_FSL_MC (1 << 6) /* vfio-fsl-mc device */ #define VFIO_DEVICE_FLAGS_CAPS (1 << 7) /* Info supports caps */ #define VFIO_DEVICE_FLAGS_CDX (1 << 8) /* vfio-cdx device */ +#define VFIO_DEVICE_FLAGS_UB (1 << 9) /* vfio-ub device */ __u32 num_regions; /* Max region index + 1 */ __u32 num_irqs; /* Max IRQ index + 1 */ __u32 cap_offset; /* Offset within info struct of first cap */ -- Gitee From 40555429acaff5ed3ed60fc5b3a5cfdfafa05bf8 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Mon, 22 Sep 2025 15:07:05 +0800 Subject: [PATCH 071/214] ub:ubus: Support hotplug service driver ANBZ: #45460 commit 8f4f082b147efc17e5c4692921dee2e6d09057ed openEuler. Register the hotplug service driver to implement basic initialization and deinitialization functions for the slot. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 1 + drivers/ub/ubus/services/gucd.c | 11 + drivers/ub/ubus/services/hotplug/hotplug.h | 50 ++++ .../ub/ubus/services/hotplug/hotplug_core.c | 226 ++++++++++++++++++ .../ub/ubus/services/hotplug/hotplug_ctrl.c | 62 +++++ drivers/ub/ubus/services/service.h | 5 + drivers/ub/ubus/ubus_entity.c | 1 + include/ub/ubus/ubus.h | 5 + 8 files changed, 361 insertions(+) create mode 100644 drivers/ub/ubus/services/hotplug/hotplug.h create mode 100644 drivers/ub/ubus/services/hotplug/hotplug_core.c create mode 100644 drivers/ub/ubus/services/hotplug/hotplug_ctrl.c diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 2ac8389ed26c..a120bb7c38bd 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -8,5 +8,6 @@ ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o om ubus-y += instance.o pool.o ubus-y += services/ras.o services/service.o services/gucd.o +ubus-y += services/hotplug/hotplug_core.o services/hotplug/hotplug_ctrl.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/services/gucd.c b/drivers/ub/ubus/services/gucd.c index 7d67b8b809ba..35a0cf35e20a 100644 --- a/drivers/ub/ubus/services/gucd.c +++ b/drivers/ub/ubus/services/gucd.c @@ -20,6 +20,15 @@ MODULE_DEVICE_TABLE(ub, component_device_ids); static int get_component_service_capability(struct ub_entity *uent) { int services = 0; + u32 val; + int ret; + + ret = ub_cfg_read_dword(uent, UB_CFG0_CAP_BITMAP, &val); + if (ret) + return services; + + if (val & (1 << UB_SHP_CAP)) + services |= UB_SERVICE_HP; return services; } @@ -188,10 +197,12 @@ static struct ub_driver ub_component_device_driver = { static void ub_init_services(void) { ub_ras_init(); + ubhp_service_init(); } static void ub_uninit_services(void) { + ubhp_service_uninit(); ub_ras_uninit(); } diff --git a/drivers/ub/ubus/services/hotplug/hotplug.h b/drivers/ub/ubus/services/hotplug/hotplug.h new file mode 100644 index 000000000000..e8c75543b2af --- /dev/null +++ b/drivers/ub/ubus/services/hotplug/hotplug.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __HOTPLUG_H__ +#define __HOTPLUG_H__ + +/** + * struct ub_slot - UB hotplug slot + * @uent: pointer to the ub dev who has this slot + * @r_uent: pointer to the ub dev who is plugged in this slot + * @ports: port array slice of uent's port array + * @kobj: kobject of slot to provide sysfs + * @node: node of slot in uent slot_list; + * + * @slot_id: id of this slot + * @port_start: start port_idx of ports belong to this slot + * @port_num: number of ports belong to this slot + */ +struct ub_slot { + /* slot info */ + struct ub_entity *uent; + struct ub_entity *r_uent; + struct ub_port *ports; + struct kobject kobj; + struct list_head node; + + /* cap info */ + u8 slot_id; + u16 port_start; + u16 port_num; + u32 slot_cap; +}; + +#define for_each_slot_port(p, s) \ + for ((p) = (s)->ports; ((p) - (s)->ports) < (s)->port_num; (p)++) + +static inline void ubhp_put_slot(struct ub_slot *slot) +{ + if (slot) + kobject_put(&slot->kobj); +} + +/* ctrl */ +int ub_slot_read_dword(struct ub_slot *slot, u32 pos, u32 *val); +void ubhp_start_slots(struct ub_entity *uent); +void ubhp_stop_slots(struct ub_entity *uent); + +#endif /* __HOTPLUG_H__ */ diff --git a/drivers/ub/ubus/services/hotplug/hotplug_core.c b/drivers/ub/ubus/services/hotplug/hotplug_core.c new file mode 100644 index 000000000000..0dd165517d53 --- /dev/null +++ b/drivers/ub/ubus/services/hotplug/hotplug_core.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include "../../ubus.h" +#include "../service.h" +#include "hotplug.h" + +#define to_ub_slot(s) container_of(s, struct ub_slot, kobj) + +static void ubhp_destory_slot(struct ub_slot *slot) +{ + kfree(slot); +} + +static void ubhp_slot_release(struct kobject *kobj) +{ + struct ub_slot *slot = to_ub_slot(kobj); + + ubhp_destory_slot(slot); +} + +static const struct kobj_type ub_slot_ktype = { + .release = ubhp_slot_release, +}; + +static struct ub_slot *ubhp_create_slot(void) +{ + struct ub_slot *slot; + + slot = kzalloc(sizeof(*slot), GFP_KERNEL); + if (!slot) + return NULL; + + slot->ports = NULL; + INIT_LIST_HEAD(&slot->node); + + return slot; +} + +static int ubhp_slot_port_check(struct ub_entity *uent, int idx, u32 val) +{ + struct ub_port *ports, *tmp; + u16 port_start, port_end; + u16 port_num; + + port_start = (u16)(val & UB_SLOT_START_PORT); + port_end = (u16)(val >> SZ_16); + + if (port_start > port_end || port_end >= uent->port_nums) { + ub_err(uent, "slot%d port range err, start=%u, end=%u, total=%u\n", + idx, port_start, port_end, uent->port_nums); + return -EINVAL; + } + + port_num = port_end - port_start + 1; + ports = uent->ports + port_start; + for (tmp = ports; (tmp - ports) < port_num; tmp++) { + if (tmp->type == VIRTUAL) { + ub_err(uent, "slot%d port%u type is virtual\n", + idx, tmp->index); + return -EINVAL; + } + + if (tmp->slot) { + ub_err(uent, "slot%d port%u already in slot%u\n", + idx, tmp->index, tmp->slot->slot_id); + return -EINVAL; + } + } + + return 0; +} + +static int ubhp_setup_slot(struct ub_slot *slot, struct ub_entity *uent, int idx) +{ + u16 port_start, port_end; + u32 val, cap; + int ret; + + slot->uent = uent; + slot->slot_id = idx; + + ret = ub_slot_read_dword(slot, UB_SLOT_PORT, &val); + if (ret) + return ret; + + ret = ubhp_slot_port_check(uent, idx, val); + if (ret) + return ret; + + ret = ub_slot_read_dword(slot, UB_SLOT_CAP, &cap); + if (ret) + return ret; + + port_start = (u16)(val & UB_SLOT_START_PORT); + port_end = (u16)(val >> SZ_16); + + slot->port_start = port_start; + slot->port_num = port_end - port_start + 1; + slot->ports = uent->ports + port_start; + slot->slot_cap = cap; + + ub_info(uent, "slot%u port[%u:%u] cap[%#x] setup\n", + slot->slot_id, slot->port_start, port_end, slot->slot_cap); + + return 0; +} + +static void ubhp_del_slot(struct ub_slot *slot) +{ + struct ub_port *port; + + list_del(&slot->node); + + for_each_slot_port(port, slot) + port->slot = NULL; + + kobject_del(&slot->kobj); +} + +static int ubhp_add_slot(struct ub_slot *slot) +{ + struct ub_entity *uent = slot->uent; + struct ub_port *port; + int ret; + + ret = kobject_init_and_add(&slot->kobj, &ub_slot_ktype, &uent->dev.kobj, + "slot%d", slot->slot_id); + if (ret) + return ret; + + if (slot->ports->r_uent) + slot->r_uent = slot->ports->r_uent; + + for_each_slot_port(port, slot) + port->slot = slot; + + list_add(&slot->node, &uent->slot_list); + + return ret; +} + +static int ubhp_probe(struct ub_service_device *sdev) +{ + struct ub_entity *uent = sdev->uent; + struct ub_slot *slot, *tmp; + u16 slot_num; + int i, ret; + + if (sdev->service != UB_SERVICE_HP) + return -ENODEV; + + ret = ub_cfg_read_word(uent, UB_SLOT_START + UB_SLOT_NUM, &slot_num); + if (ret) + return ret; + + if (!slot_num) { + ub_err(uent, "hotplug probe without slot, ignoring\n"); + return -ENODEV; + } + + for (i = 0; i < slot_num; i++) { + slot = ubhp_create_slot(); + if (!slot) { + ret = -ENOMEM; + goto free_slots; + } + + ret = ubhp_setup_slot(slot, uent, i); + if (ret) { + ubhp_destory_slot(slot); + goto free_slots; + } + + ret = ubhp_add_slot(slot); + if (ret) { + ubhp_put_slot(slot); + goto free_slots; + } + } + + ubhp_start_slots(uent); + + return 0; +free_slots: + list_for_each_entry_safe_reverse(slot, tmp, &uent->slot_list, node) { + ubhp_del_slot(slot); + ubhp_put_slot(slot); + } + + return ret; +} + +static void ubhp_remove(struct ub_service_device *sdev) +{ + struct ub_entity *uent = sdev->uent; + struct ub_slot *slot, *tmp; + + ubhp_stop_slots(uent); + + list_for_each_entry_safe(slot, tmp, &uent->slot_list, node) { + ubhp_del_slot(slot); + ubhp_put_slot(slot); + } +} + +static struct ub_service_driver ubhp_service_driver = { + .name = "ub-hotplug", + + .probe = ubhp_probe, + .remove = ubhp_remove, + + .service = UB_SERVICE_HP, +}; + +void ubhp_service_init(void) +{ + ub_service_driver_register(&ubhp_service_driver); +} + +void ubhp_service_uninit(void) +{ + ub_service_driver_unregister(&ubhp_service_driver); +} diff --git a/drivers/ub/ubus/services/hotplug/hotplug_ctrl.c b/drivers/ub/ubus/services/hotplug/hotplug_ctrl.c new file mode 100644 index 000000000000..6e822619fc90 --- /dev/null +++ b/drivers/ub/ubus/services/hotplug/hotplug_ctrl.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include "../../ubus.h" +#include "hotplug.h" + +/** + * arrange of SHP cap + * + * +-------------+---------------+---------------+ + * | reg(s) name | start address | end address | + * | slice header| 0x00 | 0x00 | + * | slot number | 0x01 | 0x01 | + * | slot1 regs | 0x02 | 0x0F | + * | slot2 regs | 0x12 | 0x1F | + * ... + * | slotN regs | 0x2 + 10(N-1) | 0xF + 10(N-1) | + */ + +/** + * for any given reg in slot regs of slotN, it's pos can be get by: + * SHP cap pos + 10(N-1) + reg pos in slot regs + * the fronter two elements is represented as following UB_SLOT_BASE marco + */ +#define UB_SLOT_BASE(slot) (UB_SLOT_START + (slot)->slot_id * UB_SLOT_POS) + +int ub_slot_read_dword(struct ub_slot *slot, u32 pos, u32 *val) +{ + int ret = ub_cfg_read_dword(slot->uent, UB_SLOT_BASE(slot) + pos, val); + + if (unlikely(ret)) + ub_err(slot->uent, "ub slot%u read %#x failed, ret=%d\n", + slot->slot_id, pos, ret); + + return ret; +} + +static void ubhp_start_slot(struct ub_slot *slot) +{ +} + +static void ubhp_stop_slot(struct ub_slot *slot) +{ +} + +void ubhp_start_slots(struct ub_entity *uent) +{ + struct ub_slot *slot; + + list_for_each_entry(slot, &uent->slot_list, node) + ubhp_start_slot(slot); +} + +void ubhp_stop_slots(struct ub_entity *uent) +{ + struct ub_slot *slot; + + list_for_each_entry(slot, &uent->slot_list, node) + ubhp_stop_slot(slot); +} diff --git a/drivers/ub/ubus/services/service.h b/drivers/ub/ubus/services/service.h index 2e10f7293dbe..85ac7654f912 100644 --- a/drivers/ub/ubus/services/service.h +++ b/drivers/ub/ubus/services/service.h @@ -5,6 +5,9 @@ #ifndef __SERVICES_SERVICE_H__ #define __SERVICES_SERVICE_H__ +#define UB_SERVICE_HP_SHIFT 1 +#define UB_SERVICE_HP (1 << UB_SERVICE_HP_SHIFT) + #define UB_MAXSERIVCES 3 struct ub_service_device { @@ -32,6 +35,8 @@ struct ub_service_driver { void ub_ras_init(void); void ub_ras_uninit(void); +void ubhp_service_init(void); +void ubhp_service_uninit(void); int ub_service_driver_register(struct ub_service_driver *drv); void ub_service_driver_unregister(struct ub_service_driver *drv); diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index 7c95303f4e81..c13cf5b312d9 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -49,6 +49,7 @@ struct ub_entity *ub_alloc_ent(void) INIT_LIST_HEAD(&uent->mue_list); INIT_LIST_HEAD(&uent->ue_list); INIT_LIST_HEAD(&uent->cna_list); + INIT_LIST_HEAD(&uent->slot_list); INIT_LIST_HEAD(&uent->instance_node); uent->dev.type = &ub_dev_type; diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 80bc8c02b2d8..80268750356b 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -150,6 +150,8 @@ struct ub_port { guid_t r_guid; struct kobject kobj; DECLARE_BITMAP(cna_maps, UB_MAX_CNA_NUM); + /* hotplug info */ + struct ub_slot *slot; /* cap cache */ DECLARE_BITMAP(cap_map, UB_PORT_CAP_NUM); @@ -232,6 +234,9 @@ struct ub_entity { /* entity route info */ struct list_head cna_list; /* store distance for cna in route table */ + /* entity slot info */ + struct list_head slot_list; /* store slots under this dev */ + struct dev_message *message; /* UB entity TID */ -- Gitee From 9d7a1dfd8de942595acd86b074ae38b66a38e7c0 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Mon, 22 Sep 2025 15:44:09 +0800 Subject: [PATCH 072/214] ub:ubus: Support for slot register configuration ANBZ: #45460 commit a7b1f6f057ef30346b864a83d1f9fa3f0b8e101f openEuler. Implement read and write operations for each slot register in the device configuration space, providing fundamental capabilities for hardware manipulation. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/port.c | 13 ++ drivers/ub/ubus/port.h | 1 + drivers/ub/ubus/services/hotplug/hotplug.h | 28 ++++ .../ub/ubus/services/hotplug/hotplug_ctrl.c | 158 ++++++++++++++++++ 4 files changed, 200 insertions(+) diff --git a/drivers/ub/ubus/port.c b/drivers/ub/ubus/port.c index c3be48b180af..36950c24e344 100644 --- a/drivers/ub/ubus/port.c +++ b/drivers/ub/ubus/port.c @@ -706,3 +706,16 @@ void ub_notify_share_port(struct ub_port *port, } up_read(&ub_share_port_notify_list_rwsem); } + +bool ub_port_check_link_up(struct ub_port *port) +{ + u8 val; + + if (!port) + return false; + + if (ub_port_read_byte(port, UB_PORT_PHYSICAL_PORT_LINK_STATUS, &val)) + return false; + + return !!(val & UB_PORT_LINK_STATE); +} diff --git a/drivers/ub/ubus/port.h b/drivers/ub/ubus/port.h index 3135d25ebdf4..0350908c531a 100644 --- a/drivers/ub/ubus/port.h +++ b/drivers/ub/ubus/port.h @@ -27,5 +27,6 @@ void ub_notify_share_port(struct ub_port *port, enum ub_share_port_notify_type type); int ub_port_write_dword(struct ub_port *port, u32 pos, u32 val); +bool ub_port_check_link_up(struct ub_port *port); #endif /* __PORT_H__ */ diff --git a/drivers/ub/ubus/services/hotplug/hotplug.h b/drivers/ub/ubus/services/hotplug/hotplug.h index e8c75543b2af..0a431e8c6c0c 100644 --- a/drivers/ub/ubus/services/hotplug/hotplug.h +++ b/drivers/ub/ubus/services/hotplug/hotplug.h @@ -36,6 +36,29 @@ struct ub_slot { #define for_each_slot_port(p, s) \ for ((p) = (s)->ports; ((p) - (s)->ports) < (s)->port_num; (p)++) +#define BUTTON(slot) ((slot)->slot_cap & UB_SLOT_PPS) +#define WORK_LED(slot) ((slot)->slot_cap & UB_SLOT_WLPS) +#define PWR_LED(slot) ((slot)->slot_cap & UB_SLOT_PLPS) +#define PRESENT(slot) ((slot)->slot_cap & UB_SLOT_PDSS) + +enum power_state { + POWER_OFF, + POWER_ON +}; + +enum indicator_state { + INDICATOR_OFF, /* set indicator off */ + INDICATOR_ON, /* set indicator on */ + INDICATOR_BLINKING, /* set indicator blinking */ + INDICATOR_NOOP /* left indicator unchanged */ +}; + +enum hotplug_event { + HPE_BUTTON_PRESSED = 2, + HPE_PRESENCE_DETECT, + HPE_OTHER +}; + static inline void ubhp_put_slot(struct ub_slot *slot) { if (slot) @@ -44,6 +67,11 @@ static inline void ubhp_put_slot(struct ub_slot *slot) /* ctrl */ int ub_slot_read_dword(struct ub_slot *slot, u32 pos, u32 *val); +void ubhp_set_indicators(struct ub_slot *slot, u8 power, u8 work); +void ubhp_set_slot_power(struct ub_slot *slot, enum power_state power); +bool ubhp_confirm_event(struct ub_slot *slot, enum hotplug_event event); +bool ubhp_wait_linkup(struct ub_slot *slot); +bool ubhp_card_present(struct ub_slot *slot); void ubhp_start_slots(struct ub_entity *uent); void ubhp_stop_slots(struct ub_entity *uent); diff --git a/drivers/ub/ubus/services/hotplug/hotplug_ctrl.c b/drivers/ub/ubus/services/hotplug/hotplug_ctrl.c index 6e822619fc90..28753cc2501c 100644 --- a/drivers/ub/ubus/services/hotplug/hotplug_ctrl.c +++ b/drivers/ub/ubus/services/hotplug/hotplug_ctrl.c @@ -4,6 +4,7 @@ */ #include "../../ubus.h" +#include "../../port.h" #include "hotplug.h" /** @@ -26,6 +27,17 @@ */ #define UB_SLOT_BASE(slot) (UB_SLOT_START + (slot)->slot_id * UB_SLOT_POS) +static void ub_slot_read_byte(struct ub_slot *slot, u32 pos, u8 *val) +{ + int ret = ub_cfg_read_byte(slot->uent, UB_SLOT_BASE(slot) + pos, val); + + if (unlikely(ret)) { + ub_err(slot->uent, "ub slot%u read %#x failed, ret=%d\n", + slot->slot_id, pos, ret); + *val = 0; + } +} + int ub_slot_read_dword(struct ub_slot *slot, u32 pos, u32 *val) { int ret = ub_cfg_read_dword(slot->uent, UB_SLOT_BASE(slot) + pos, val); @@ -37,12 +49,158 @@ int ub_slot_read_dword(struct ub_slot *slot, u32 pos, u32 *val) return ret; } +static void ub_slot_write_byte(struct ub_slot *slot, u32 pos, u8 val) +{ + int ret = ub_cfg_write_byte(slot->uent, UB_SLOT_BASE(slot) + pos, val); + + if (unlikely(ret)) + ub_err(slot->uent, "ub slot%u write %#x failed, ret=%d\n", + slot->slot_id, pos, ret); +} + +void ubhp_set_indicators(struct ub_slot *slot, u8 power, u8 work) +{ + u8 val; + + /** + * power state indicator: + * OFF: slot is power off, card can be inserted or removed + * ON: slot is power on, card can't be inserted or removed + * BLINKING: slot is under operation performed by user, + * card can't be inserted or removed + */ + if (power >= INDICATOR_NOOP || !PWR_LED(slot)) + goto set_wl; + + ub_slot_read_byte(slot, UB_SLOT_PL_CTRL, &val); + val &= ~UB_SLOT_PL_CTRL_MASK; + val |= power; + ub_slot_write_byte(slot, UB_SLOT_PL_CTRL, val); + +set_wl: + /** + * card work state indicator: + * OFF: card is running normally + * ON: card is out of service or faulty. + * BLINKING: card is under operation performed by user + */ + if (work >= INDICATOR_NOOP || !WORK_LED(slot)) + return; + + ub_slot_read_byte(slot, UB_SLOT_WL_CTRL, &val); + val &= ~UB_SLOT_WL_CTRL_MASK; + val |= work; + ub_slot_write_byte(slot, UB_SLOT_WL_CTRL, val); +} + +void ubhp_set_slot_power(struct ub_slot *slot, enum power_state power) +{ + ub_slot_write_byte(slot, UB_SLOT_PW_CTRL, power); +} + +bool ubhp_card_present(struct ub_slot *slot) +{ + u8 val; + + ub_slot_read_byte(slot, UB_SLOT_PD_STA, &val); + + return !!(val & UB_SLOT_PD_STA_MASK); +} + +bool ubhp_wait_linkup(struct ub_slot *slot) +{ +#define TOTAL_WAIT_TIME 100 /* wait 100ms for linkup */ + u64 timeout = get_jiffies_64() + msecs_to_jiffies(TOTAL_WAIT_TIME); + struct ub_port *port; + + do { + /* only need one port link up */ + for_each_slot_port(port, slot) + if (ub_port_check_link_up(port)) + return true; + } while (time_is_after_jiffies64(timeout)); + + return false; +} + +/* check config space and clear status regs */ +bool ubhp_confirm_event(struct ub_slot *slot, enum hotplug_event event) +{ + u32 pos, mask; + u8 val; + + switch (event) { + case HPE_BUTTON_PRESSED: + pos = UB_SLOT_PP_STA; + mask = UB_SLOT_PP_STA_MASK; + break; + case HPE_PRESENCE_DETECT: + pos = UB_SLOT_PDSC_STA; + mask = UB_SLOT_PDSC_STA_MASK; + break; + default: + return false; + } + + ub_slot_read_byte(slot, pos, &val); + if (!(val & mask)) { + ub_err(slot->uent, "confirm hotplug event %d failed\n", event); + return false; + } + + val &= ~mask; + ub_slot_write_byte(slot, pos, val); + return true; +} + static void ubhp_start_slot(struct ub_slot *slot) { + u8 val; + + /* enable PP */ + ub_slot_read_byte(slot, UB_SLOT_PP_CTRL, &val); + val |= UB_SLOT_PP_CTRL_MASK; + ub_slot_write_byte(slot, UB_SLOT_PP_CTRL, val); + + /* enable PD */ + ub_slot_read_byte(slot, UB_SLOT_PD_CTRL, &val); + val |= UB_SLOT_PD_CTRL_MASK; + ub_slot_write_byte(slot, UB_SLOT_PD_CTRL, val); + + /* enable PDS */ + ub_slot_read_byte(slot, UB_SLOT_PDS_CTRL, &val); + val |= UB_SLOT_PDS_CTRL_MASK; + ub_slot_write_byte(slot, UB_SLOT_PDS_CTRL, val); + + /* enable MS */ + ub_slot_read_byte(slot, UB_SLOT_MS_CTRL, &val); + val |= UB_SLOT_MS_CTRL_MASK; + ub_slot_write_byte(slot, UB_SLOT_MS_CTRL, val); } static void ubhp_stop_slot(struct ub_slot *slot) { + u8 val; + + /* disable MS */ + ub_slot_read_byte(slot, UB_SLOT_MS_CTRL, &val); + val &= ~UB_SLOT_MS_CTRL_MASK; + ub_slot_write_byte(slot, UB_SLOT_MS_CTRL, val); + + /* disable PDS */ + ub_slot_read_byte(slot, UB_SLOT_PDS_CTRL, &val); + val &= ~UB_SLOT_PDS_CTRL_MASK; + ub_slot_write_byte(slot, UB_SLOT_PDS_CTRL, val); + + /* disable PD */ + ub_slot_read_byte(slot, UB_SLOT_PD_CTRL, &val); + val &= ~UB_SLOT_PD_CTRL_MASK; + ub_slot_write_byte(slot, UB_SLOT_PD_CTRL, val); + + /* disable PP */ + ub_slot_read_byte(slot, UB_SLOT_PP_CTRL, &val); + val &= ~UB_SLOT_PP_CTRL_MASK; + ub_slot_write_byte(slot, UB_SLOT_PP_CTRL, val); } void ubhp_start_slots(struct ub_entity *uent) -- Gitee From dfeec1ce674d23a97b7689828dd2cf6bfec44672 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Mon, 22 Sep 2025 16:15:50 +0800 Subject: [PATCH 073/214] ub:ubus: Support for device enumeration during the hotplug phase ANBZ: #45460 commit 3acf8553990971040d75c0d8e6e5885e04302984 openEuler. Implement device creation and destruction during the hotplug phase, while refreshing the routing tables of all devices in the network. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 1 + drivers/ub/ubus/services/hotplug/hotplug.h | 7 + .../ub/ubus/services/hotplug/hotplug_core.c | 48 +++++++ .../ub/ubus/services/hotplug/hotplug_route.c | 123 ++++++++++++++++++ 4 files changed, 179 insertions(+) create mode 100644 drivers/ub/ubus/services/hotplug/hotplug_route.c diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index a120bb7c38bd..c3fb1b5d5e32 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -9,5 +9,6 @@ ubus-y += instance.o pool.o ubus-y += services/ras.o services/service.o services/gucd.o ubus-y += services/hotplug/hotplug_core.o services/hotplug/hotplug_ctrl.o +ubus-y += services/hotplug/hotplug_route.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/services/hotplug/hotplug.h b/drivers/ub/ubus/services/hotplug/hotplug.h index 0a431e8c6c0c..d4df22808d1c 100644 --- a/drivers/ub/ubus/services/hotplug/hotplug.h +++ b/drivers/ub/ubus/services/hotplug/hotplug.h @@ -75,4 +75,11 @@ bool ubhp_card_present(struct ub_slot *slot); void ubhp_start_slots(struct ub_entity *uent); void ubhp_stop_slots(struct ub_entity *uent); +/* route */ +int ubhp_update_route_link_up(struct ub_slot *slot); +void ubhp_update_route_link_down(struct ub_slot *slot); +void ubhp_mark_detached_entities(struct ub_entity *root, struct list_head *dev_list); +void ubhp_stop_entities(struct list_head *dev_list); +void ubhp_remove_entities(struct list_head *dev_list); + #endif /* __HOTPLUG_H__ */ diff --git a/drivers/ub/ubus/services/hotplug/hotplug_core.c b/drivers/ub/ubus/services/hotplug/hotplug_core.c index 0dd165517d53..017ce5d0a350 100644 --- a/drivers/ub/ubus/services/hotplug/hotplug_core.c +++ b/drivers/ub/ubus/services/hotplug/hotplug_core.c @@ -4,6 +4,10 @@ */ #include "../../ubus.h" +#include "../../msg.h" +#include "../../enum.h" +#include "../../route.h" +#include "../../port.h" #include "../service.h" #include "hotplug.h" @@ -224,3 +228,47 @@ void ubhp_service_uninit(void) { ub_service_driver_unregister(&ubhp_service_driver); } + +static void ubhp_disconnect_slot(struct ub_slot *slot) +{ + struct ub_port *port; + + for_each_slot_port(port, slot) + ub_port_disconnect(port); + + slot->r_uent = NULL; +} + +/** + * a simple example for link down + * for a given topo like + * +-------------+ +---------+ +---------+ +--------+ + * | controller0 |p0:---:p0| switch0 |p1:---slot0---:p0| switch1 |p1:---:p0| device0| + * +-------------+ +---------+ +---------+ +--------+ + * when slot0 is calling handle link down + * 1. disconnect slot0 so that p1 and p0 is not connected in software + * 2. put switch1 and device0 into dev_list, mark them as detached + * 3. stop switch1 and device0 + * 4. remove switch1 and device0 + * 5. handle route link down at slot0, del route of right(switch1 & device0) + * from left(controller0 & switch0) + */ +static void ubhp_handle_link_down(struct ub_slot *slot) +{ + struct list_head dev_list; + struct ub_entity *r_uent; + + INIT_LIST_HEAD(&dev_list); + + r_uent = slot->r_uent; + if (!r_uent) { + ub_warn(slot->uent, "link down without remote dev\n"); + return; + } + + ubhp_disconnect_slot(slot); + ubhp_mark_detached_entities(r_uent, &dev_list); + ubhp_stop_entities(&dev_list); + ubhp_remove_entities(&dev_list); + ubhp_update_route_link_down(slot); +} diff --git a/drivers/ub/ubus/services/hotplug/hotplug_route.c b/drivers/ub/ubus/services/hotplug/hotplug_route.c new file mode 100644 index 000000000000..76341766e362 --- /dev/null +++ b/drivers/ub/ubus/services/hotplug/hotplug_route.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include + +#include "../../ubus.h" +#include "../../ubus_entity.h" +#include "../../ubus_driver.h" +#include "../../port.h" +#include "../../route.h" +#include "hotplug.h" + +int ubhp_update_route_link_up(struct ub_slot *slot) +{ + struct ub_entity *uent = slot->uent, *r_uent = slot->r_uent; + struct ub_port *port, *r_port; + int ret; + + for_each_slot_port(port, slot) { + if (!port->r_uent) + continue; + + r_port = port->r_uent->ports + port->r_index; + ret = ub_route_mod_neighbor(port, r_port); + if (ret) + return ret; + + ret = ub_route_mod_neighbor(r_port, port); + if (ret) + return ret; + } + + /* for uent that can't forward, there's no need to update other uent */ + if (ub_entity_support_forward(uent)) + ub_route_mod_bfs(uent); + + if (ub_entity_support_forward(r_uent)) + ub_route_mod_bfs(r_uent); + + ub_route_sync_all(); + return 0; +} + +void ubhp_update_route_link_down(struct ub_slot *slot) +{ + struct ub_entity *uent = slot->uent; + struct ub_port *port; + + for_each_slot_port(port, slot) + ub_route_clear_port(port); + + /* for uent that can't forward, there's no need to update other uent */ + if (ub_entity_support_forward(uent)) + ub_route_del_bfs(uent); + + ub_route_sync_all(); +} + +/** + * ubhp_mark_detached_entities() - mark devices as detached and put them into dev_list + * @root: a device that needs to be removed + * @dev_list: a list to store the detached devices + * + * this func use bfs to mark devices and put them into dev_list, all devices + * that connected with root will be marked as detached + */ +void ubhp_mark_detached_entities(struct ub_entity *root, struct list_head *dev_list) +{ +#define UBHP_KFIFO_DEPTH SZ_16 + DECLARE_KFIFO(kfifo, struct ub_entity *, UBHP_KFIFO_DEPTH); + struct ub_port *port; + struct ub_entity *uent, *r_uent; + + INIT_KFIFO(kfifo); + ub_entity_assign_priv_flag(root, UB_ENTITY_DETACHED, true); + kfifo_put(&kfifo, root); + + down_write(&ub_bus_sem); + while (kfifo_get(&kfifo, &uent)) { + for_each_uent_port(port, uent) { + if (!port->r_uent) + continue; + + r_uent = port->r_uent; + if (ub_entity_test_priv_flag(r_uent, UB_ENTITY_DETACHED)) + continue; + + ub_entity_assign_priv_flag(r_uent, UB_ENTITY_DETACHED, true); + if (!kfifo_put(&kfifo, r_uent)) + ub_err(r_uent, "hp detached entity kfifo put failed!\n"); + } + + list_del(&uent->node); + list_add_tail(&uent->node, dev_list); + } + up_write(&ub_bus_sem); +} + +/** + * ubhp_stop_entities() - stop devices in dev_list + * @dev_list: a list to store devices + */ +void ubhp_stop_entities(struct list_head *dev_list) +{ + struct ub_entity *uent; + + list_for_each_entry_reverse(uent, dev_list, node) + ub_stop_ent(uent); +} + +/** + * ubhp_remove_entities() - remove devices in dev_list + * @dev_list: a list to store devices + */ +void ubhp_remove_entities(struct list_head *dev_list) +{ + struct ub_entity *uent, *tmp; + + list_for_each_entry_safe_reverse(uent, tmp, dev_list, node) + ub_remove_ent(uent); +} -- Gitee From fc76bafed9601722daad826814f020f783568225 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Mon, 22 Sep 2025 16:51:41 +0800 Subject: [PATCH 074/214] ub:ubus: Support hotplug processing main workflow ANBZ: #45460 commit 1064860850145840a31283fe40d5d1d92bec5590 openEuler. The main process for implementing hotplug handling is achieved through three workers that manage the callback processing for buttons, in-place operations, and power. Additionally, a user-space sysfs entry is added to trigger notification-based hotplug. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/services/hotplug/hotplug.h | 29 ++ .../ub/ubus/services/hotplug/hotplug_core.c | 308 +++++++++++++++++- 2 files changed, 336 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubus/services/hotplug/hotplug.h b/drivers/ub/ubus/services/hotplug/hotplug.h index d4df22808d1c..87a4e4fcdf16 100644 --- a/drivers/ub/ubus/services/hotplug/hotplug.h +++ b/drivers/ub/ubus/services/hotplug/hotplug.h @@ -17,6 +17,12 @@ * @slot_id: id of this slot * @port_start: start port_idx of ports belong to this slot * @port_num: number of ports belong to this slot + * + * @button_work: work to queue for button pressed event + * @present_work: work to queue for card present event + * @power_work: work to queue for power state update + * @state: current state machine position + * @state_lock: mutex lock for slot state */ struct ub_slot { /* slot info */ @@ -31,6 +37,13 @@ struct ub_slot { u16 port_start; u16 port_num; u32 slot_cap; + + /* hotplug info */ + struct work_struct button_work; + struct delayed_work present_work; + struct delayed_work power_work; + u8 state; + struct mutex state_lock; }; #define for_each_slot_port(p, s) \ @@ -59,6 +72,19 @@ enum hotplug_event { HPE_OTHER }; +/** + * a slot must be get before any of its work is queued into workqueue + * and must be put after the work is end + * so that when a slot is released, its works are idle and don't need to cancel + */ +static inline struct ub_slot *ubhp_get_slot(struct ub_slot *slot) +{ + if (slot) + kobject_get(&slot->kobj); + + return slot; +} + static inline void ubhp_put_slot(struct ub_slot *slot) { if (slot) @@ -75,6 +101,9 @@ bool ubhp_card_present(struct ub_slot *slot); void ubhp_start_slots(struct ub_entity *uent); void ubhp_stop_slots(struct ub_entity *uent); +/* core */ +void ubhp_handle_power(struct ub_slot *slot, bool power_on); + /* route */ int ubhp_update_route_link_up(struct ub_slot *slot); void ubhp_update_route_link_down(struct ub_slot *slot); diff --git a/drivers/ub/ubus/services/hotplug/hotplug_core.c b/drivers/ub/ubus/services/hotplug/hotplug_core.c index 017ce5d0a350..157e7b1d4396 100644 --- a/drivers/ub/ubus/services/hotplug/hotplug_core.c +++ b/drivers/ub/ubus/services/hotplug/hotplug_core.c @@ -15,6 +15,7 @@ static void ubhp_destory_slot(struct ub_slot *slot) { + mutex_destroy(&slot->state_lock); kfree(slot); } @@ -25,10 +26,155 @@ static void ubhp_slot_release(struct kobject *kobj) ubhp_destory_slot(slot); } +enum slot_state { + SLOT_ON, /* slot on, device running */ + SLOT_POWERON, /* from slot off to slot on */ + SLOT_POWEROFF, /* from slot on to slot off */ + SLOT_OFF, /* slot off, device not present */ +}; + +struct ub_slot_attribute { + struct attribute attr; + ssize_t (*show)(struct ub_slot *slot, char *buf); + ssize_t (*store)(struct ub_slot *slot, const char *buf, size_t count); +}; + +#define to_ub_slot_attr(s) container_of(s, struct ub_slot_attribute, attr) + +static void ubhp_enable_slot(struct ub_slot *slot) +{ + bool queued = false; + + ubhp_get_slot(slot); + mutex_lock(&slot->state_lock); + + if (slot->state == SLOT_OFF) + queued = queue_work(get_rx_msg_wq(UB_MSG_CODE_LINK), + &slot->button_work); + else + ub_info(slot->uent, "ignore slot poweron\n"); + + mutex_unlock(&slot->state_lock); + if (!queued) + ubhp_put_slot(slot); +} + +static void ubhp_disable_slot(struct ub_slot *slot) +{ + bool queued = false; + + ubhp_get_slot(slot); + mutex_lock(&slot->state_lock); + + if (slot->state == SLOT_ON) + queued = queue_work(get_rx_msg_wq(UB_MSG_CODE_LINK), + &slot->button_work); + else + ub_info(slot->uent, "ignore slot poweroff\n"); + + mutex_unlock(&slot->state_lock); + if (!queued) + ubhp_put_slot(slot); +} + +static ssize_t power_show(struct ub_slot *slot, char *buf) +{ + ssize_t ret; + + mutex_lock(&slot->state_lock); + switch (slot->state) { + case SLOT_ON: + ret = sysfs_emit(buf, "slot is %s\n", "on"); + break; + case SLOT_POWERON: + ret = sysfs_emit(buf, "slot is %s\n", "poweron"); + break; + case SLOT_POWEROFF: + ret = sysfs_emit(buf, "slot is %s\n", "poweroff"); + break; + case SLOT_OFF: + ret = sysfs_emit(buf, "slot is %s\n", "off"); + break; + default: + ret = sysfs_emit(buf, "unknown state %u\n", slot->state); + break; + } + mutex_unlock(&slot->state_lock); + + return ret; +} + +static ssize_t power_store(struct ub_slot *slot, const char *buf, size_t count) +{ + unsigned long power; + int ret; + + ret = kstrtoul(buf, 0, &power); + if (ret) { + ub_err(slot->uent, "Invalid val for power\n"); + return ret; + } + + switch (power) { + case 0: + ubhp_disable_slot(slot); + break; + case 1: + ubhp_enable_slot(slot); + break; + default: + ub_err(slot->uent, "Invalid val %lu for power\n", power); + return -EINVAL; + } + + return count; +} +static struct ub_slot_attribute ub_slot_attr_power = __ATTR_RW(power); + +static struct attribute *ub_slot_default_attrs[] = { + &ub_slot_attr_power.attr, + NULL +}; +ATTRIBUTE_GROUPS(ub_slot_default); + +static ssize_t ub_slot_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct ub_slot_attribute *attribute = to_ub_slot_attr(attr); + struct ub_slot *slot = to_ub_slot(kobj); + + if (!attribute->show) + return -EIO; + + return attribute->show(slot, buf); +} + +static ssize_t ub_slot_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct ub_slot_attribute *attribute = to_ub_slot_attr(attr); + struct ub_slot *slot = to_ub_slot(kobj); + + if (!attribute->store) + return -EIO; + + return attribute->store(slot, buf, count); +} + +static const struct sysfs_ops ub_slot_sysfs_ops = { + .show = ub_slot_attr_show, + .store = ub_slot_attr_store, +}; + static const struct kobj_type ub_slot_ktype = { .release = ubhp_slot_release, + .sysfs_ops = &ub_slot_sysfs_ops, + .default_groups = ub_slot_default_groups, }; +static void ubhp_button_handler(struct work_struct *work); +static void ubhp_present_handler(struct work_struct *work); +static void ubhp_power_handler(struct work_struct *work); static struct ub_slot *ubhp_create_slot(void) { struct ub_slot *slot; @@ -39,6 +185,10 @@ static struct ub_slot *ubhp_create_slot(void) slot->ports = NULL; INIT_LIST_HEAD(&slot->node); + INIT_WORK(&slot->button_work, ubhp_button_handler); + INIT_DELAYED_WORK(&slot->present_work, ubhp_present_handler); + INIT_DELAYED_WORK(&slot->power_work, ubhp_power_handler); + mutex_init(&slot->state_lock); return slot; } @@ -116,6 +266,10 @@ static void ubhp_del_slot(struct ub_slot *slot) { struct ub_port *port; + cancel_work_sync(&slot->button_work); + cancel_delayed_work_sync(&slot->power_work); + cancel_delayed_work_sync(&slot->present_work); + list_del(&slot->node); for_each_slot_port(port, slot) @@ -135,8 +289,12 @@ static int ubhp_add_slot(struct ub_slot *slot) if (ret) return ret; - if (slot->ports->r_uent) + if (slot->ports->r_uent) { + slot->state = SLOT_ON; slot->r_uent = slot->ports->r_uent; + } else { + slot->state = SLOT_OFF; + } for_each_slot_port(port, slot) port->slot = slot; @@ -272,3 +430,151 @@ static void ubhp_handle_link_down(struct ub_slot *slot) ubhp_remove_entities(&dev_list); ubhp_update_route_link_down(slot); } + +static void ubhp_handle_button_press(struct ub_slot *slot) +{ +#define POWER_ON_WAIT 5 + bool queued = false; + + ubhp_get_slot(slot); + mutex_lock(&slot->state_lock); + + if (slot->state == SLOT_ON) { + slot->state = SLOT_POWEROFF; + ub_info(slot->uent, "slot%u poweroff\n", slot->slot_id); + ubhp_set_indicators(slot, INDICATOR_BLINKING, INDICATOR_NOOP); + /* for power off, issued the present work immediately */ + queued = queue_delayed_work(get_rx_msg_wq(UB_MSG_CODE_LINK), + &slot->present_work, 0); + } else if (slot->state == SLOT_OFF) { + slot->state = SLOT_POWERON; + ub_info(slot->uent, "slot%u poweron\n", slot->slot_id); + ubhp_set_indicators(slot, INDICATOR_BLINKING, INDICATOR_NOOP); + /* for power on, left 5s for possible card insertion */ + queued = queue_delayed_work(get_rx_msg_wq(UB_MSG_CODE_LINK), + &slot->present_work, + POWER_ON_WAIT * HZ); + } + + mutex_unlock(&slot->state_lock); + if (!queued) + ubhp_put_slot(slot); +} + +static void ubhp_button_handler(struct work_struct *work) +{ + struct ub_slot *slot = container_of(work, struct ub_slot, button_work); + + ubhp_handle_button_press(slot); + ubhp_put_slot(slot); +} + +void ubhp_handle_power(struct ub_slot *slot, bool power_on) +{ + if (!slot) + return; + + mutex_lock(&slot->state_lock); + + if (slot->state != SLOT_POWERON) + goto out; + + if (power_on) { + ubhp_set_indicators(slot, INDICATOR_ON, INDICATOR_NOOP); + slot->state = SLOT_ON; + ub_info(slot->uent, "slot%u on\n", slot->slot_id); + ub_info(slot->uent, + "slot%u handle hotplug success\n", slot->slot_id); + if (cancel_work(&slot->button_work)) + ubhp_put_slot(slot); + } else { + ubhp_set_slot_power(slot, POWER_OFF); + slot->state = SLOT_OFF; + ubhp_set_indicators(slot, INDICATOR_OFF, INDICATOR_NOOP); + ub_info(slot->uent, "slot%u off\n", slot->slot_id); + ub_info(slot->uent, + "slot%u handle hotplug unsuccess\n", slot->slot_id); + } + +out: + mutex_unlock(&slot->state_lock); +} + +static void ubhp_power_handler(struct work_struct *work) +{ + struct delayed_work *power_work; + struct ub_slot *slot; + + power_work = to_delayed_work(work); + slot = container_of(power_work, struct ub_slot, power_work); + + ubhp_handle_power(slot, false); + ubhp_put_slot(slot); +} + +static void ubhp_handle_present(struct ub_slot *slot) +{ +#define HP_LINK_WAIT_DELAY 10 + mutex_lock(&slot->state_lock); + + if (slot->state == SLOT_POWEROFF || slot->state == SLOT_ON) { + ubhp_handle_link_down(slot); + ubhp_set_slot_power(slot, POWER_OFF); + ubhp_set_indicators(slot, INDICATOR_OFF, INDICATOR_NOOP); + slot->state = SLOT_OFF; + ub_info(slot->uent, "slot%u off\n", slot->slot_id); + goto out; + } + + if (!ubhp_card_present(slot)) + goto clear_state; + + ubhp_set_slot_power(slot, POWER_ON); + + mutex_unlock(&slot->state_lock); + ubhp_get_slot(slot); + queue_delayed_work(get_rx_msg_wq(UB_MSG_CODE_LINK), + &slot->power_work, HP_LINK_WAIT_DELAY * HZ); + return; +out: + /** + * why cancel button work here: + * 1. for a slot with many ports, it's possible that every port will send + * msg when hotplug event occurred, it's possible some of this msgs + * is handled after present work is triggered, if these msgs are + * handled as usually, may come into error like slot power off + * immediately after power on, so button work is banned during + * handling present work + * 2. when the user pressed button repeatedly, some msgs come when + * handling present work, it's reasonable to ignore them + * + * by holding the state_lock both during button work and present work, + * it's guaranteed that button work and present work can't be handled + * at the same time. So cancel button work before present work ends + * makes sure that button work issued during present work is ignored + */ + if (cancel_work(&slot->button_work)) + ubhp_put_slot(slot); + mutex_unlock(&slot->state_lock); + + ub_info(slot->uent, "slot%u handle hotplug succeeded\n", slot->slot_id); + return; +clear_state: + slot->state = SLOT_OFF; + ubhp_set_indicators(slot, INDICATOR_OFF, INDICATOR_NOOP); + ub_info(slot->uent, "slot%u off\n", slot->slot_id); + mutex_unlock(&slot->state_lock); + ub_info(slot->uent, "slot%u handle hotplug failed\n", slot->slot_id); +} + +static void ubhp_present_handler(struct work_struct *work) +{ + struct delayed_work *present_work; + struct ub_slot *slot; + + present_work = to_delayed_work(work); + slot = container_of(present_work, struct ub_slot, present_work); + + ubhp_handle_present(slot); + ubhp_put_slot(slot); +} -- Gitee From 2720b8fa2bf21381007e32a9f73cef01064cde09 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Mon, 22 Sep 2025 18:49:55 +0800 Subject: [PATCH 075/214] ub:ubus: Support processing protocol link messages ANBZ: #45460 commit 03f481a88c6b72336d8e6cba6f2b71d6f28e2027 openEuler. Implement the protocol-defined link up, link down, slot button press events, and slot presence detection event messages. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/link.c | 104 +++++++++++++++++- drivers/ub/ubus/link.h | 12 ++ drivers/ub/ubus/msg.c | 5 +- drivers/ub/ubus/services/hotplug/hotplug.h | 11 ++ .../ub/ubus/services/hotplug/hotplug_msg.c | 92 ++++++++++++++++ 6 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 drivers/ub/ubus/services/hotplug/hotplug_msg.c diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index c3fb1b5d5e32..76cfef6f30e1 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -9,6 +9,6 @@ ubus-y += instance.o pool.o ubus-y += services/ras.o services/service.o services/gucd.o ubus-y += services/hotplug/hotplug_core.o services/hotplug/hotplug_ctrl.o -ubus-y += services/hotplug/hotplug_route.o +ubus-y += services/hotplug/hotplug_route.o services/hotplug/hotplug_msg.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/link.c b/drivers/ub/ubus/link.c index 37f3e01b30bb..266d8e828143 100644 --- a/drivers/ub/ubus/link.c +++ b/drivers/ub/ubus/link.c @@ -14,6 +14,7 @@ #include "route.h" #include "ubus_entity.h" #include "ubus_driver.h" +#include "services/hotplug/hotplug.h" #include "link.h" static struct ub_entity *ublc_get_port_r_uent(struct ub_port *port) @@ -158,6 +159,9 @@ static int ublc_handle_new_device_link_up(struct ub_port *port) goto err_route; } + if (port->slot) + port->slot->r_uent = port->r_uent; + ret = ublc_update_route_link_up(port); if (ret) { ub_err(port->uent, "link up update route failed, ret=%d\n", ret); @@ -178,6 +182,8 @@ static int ublc_handle_new_device_link_up(struct ub_port *port) err_link_up: ublc_update_route_link_down(port); port->r_uent = NULL; + if (port->slot) + port->slot->r_uent = NULL; err_route: ub_enum_clear_ent_list(&dev_list); return ret; @@ -244,6 +250,10 @@ static void ublc_handle_all_link_down(struct ub_port *port, struct ub_entity *r_ INIT_LIST_HEAD(&dev_list); ub_port_disconnect(port); + + if (port->slot) + port->slot->r_uent = NULL; + ublc_mark_detached_devices(r_uent, &dev_list); ublc_stop_devices(&dev_list); ublc_remove_devices(&dev_list); @@ -288,7 +298,14 @@ void ublc_link_up_handle(struct ub_port *port) r_uent = ublc_get_port_r_uent(port); if (!r_uent) { - ublc_handle_new_device_link_up(port); + ret = ublc_handle_new_device_link_up(port); + if (ret) { + ubhp_handle_power(port->slot, false); + } else { + ub_info(uent, "port%u link up and create device\n", + port->index); + ubhp_handle_power(port->slot, true); + } goto out; } @@ -349,3 +366,88 @@ void ub_link_change_handler(struct work_struct *work) else ublc_link_down_handle(port); } + +static void ub_link_handle_event(struct ub_port *port, enum ub_link_event event) +{ + if (event == UB_LINK_UP) + ublc_link_up_handle(port); + else + ublc_link_down_handle(port); +} + +static struct ub_port *ub_link_get_port_from_msg(void *pkt) +{ + struct msg_pkt_header *header = (struct msg_pkt_header *)pkt; + struct link_msg_payload *payload; + struct ub_port *port = NULL; + struct ub_entity *uent; + u32 seid; + + seid = eid_gen(header->seid_h, header->seid_l); + uent = ub_get_ent_by_eid(seid); + if (!uent) { + pr_warn("get no device by eid %u\n", seid); + return NULL; + } + + payload = (struct link_msg_payload *)header->payload; + if (payload->port_idx >= uent->port_nums) { + pr_err("link port idx %u exceeds uent port num %u\n", + payload->port_idx, uent->port_nums); + goto out; + } + + port = uent->ports + payload->port_idx; + +out: + ub_entity_put(uent); + return port; +} + +static void ub_link_event_handler(struct ub_bus_controller *ubc, void *pkt, u16 len) +{ + struct msg_pkt_header *header = (struct msg_pkt_header *)pkt; + struct ub_port *port; + + if (len < UB_LINK_MSG_SIZE) { + dev_err(&ubc->dev, "lc msg len[%#x] invalid\n", len); + return; + } + + port = ub_link_get_port_from_msg(pkt); + if (!port) + return; + + ub_link_handle_event(port, + (enum ub_link_event)header->msgetah.sub_msg_code); +} + +/** + * for an incoming msg, the following procedure is performed: + * 1. parse relating link event from msg + * 2. parse slot/port that has this event + * 3. if hotplug event is button pressed, queue a button work; if hotplug event + * is card presence, queue a present work with 0s delay, if the work + * already exists, modify it's delay time to 0s + */ + +static rx_msg_handler_t link_msg_handler[UB_SUB_MSG_CODE_NUM] = { + ub_link_event_handler, + ub_link_event_handler, + ubhp_event_handler, + ubhp_event_handler, +}; + +void ub_link_msg_handler(struct ub_bus_controller *ubc, void *pkt, u16 len) +{ + struct msg_pkt_header *header = (struct msg_pkt_header *)pkt; + u8 sub_msg_code = header->msgetah.sub_msg_code; + rx_msg_handler_t handler; + + handler = link_msg_handler[sub_msg_code]; + if (handler) + handler(ubc, pkt, len); + else + dev_err(&ubc->dev, "link sub msg code[%#x] not support\n", + sub_msg_code); +} diff --git a/drivers/ub/ubus/link.h b/drivers/ub/ubus/link.h index 1a548f1e6f1f..004d06b948e2 100644 --- a/drivers/ub/ubus/link.h +++ b/drivers/ub/ubus/link.h @@ -6,6 +6,17 @@ #ifndef __LINK_H__ #define __LINK_H__ +struct link_msg_payload { + /* DW0 */ + u32 scna : 24; + u32 rsv0 : 8; + /* DW1 */ + u32 port_idx : 16; + u32 rsv1 : 16; +}; + +#define UB_LINK_MSG_SIZE 40 + enum ub_link_event { UB_LINK_UP, UB_LINK_DOWN @@ -14,5 +25,6 @@ enum ub_link_event { void ub_link_change_handler(struct work_struct *work); void ublc_link_up_handle(struct ub_port *port); void ublc_link_down_handle(struct ub_port *port); +void ub_link_msg_handler(struct ub_bus_controller *ubc, void *pkt, u16 len); #endif /* __LINK_H__ */ diff --git a/drivers/ub/ubus/msg.c b/drivers/ub/ubus/msg.c index 64ba8b91628a..5b0e31e670c7 100644 --- a/drivers/ub/ubus/msg.c +++ b/drivers/ub/ubus/msg.c @@ -10,6 +10,7 @@ #include #include "pool.h" +#include "link.h" #include "msg.h" u8 err_to_msg_rsp(int err) @@ -166,7 +167,7 @@ static bool msg_rx_flag; int message_rx_init(void) { const char *msg_name[UB_MSG_CODE_NUM] = { - NULL, NULL, NULL, NULL, + NULL, "link wq", NULL, NULL, NULL, NULL, "pool wq", NULL }; struct workqueue_struct *q; @@ -259,7 +260,7 @@ static bool msg_rx_code_valid(struct ub_bus_controller *ubc, u8 code) static rx_msg_handler_t rx_msg_handler[UB_MSG_CODE_NUM] = { NULL, - NULL, + ub_link_msg_handler, NULL, NULL, NULL, diff --git a/drivers/ub/ubus/services/hotplug/hotplug.h b/drivers/ub/ubus/services/hotplug/hotplug.h index 87a4e4fcdf16..93c8e3c798b9 100644 --- a/drivers/ub/ubus/services/hotplug/hotplug.h +++ b/drivers/ub/ubus/services/hotplug/hotplug.h @@ -54,6 +54,14 @@ struct ub_slot { #define PWR_LED(slot) ((slot)->slot_cap & UB_SLOT_PLPS) #define PRESENT(slot) ((slot)->slot_cap & UB_SLOT_PDSS) +struct ubhp_msg_payload { + u16 slot_id; + u16 rsv0; + u32 rsv1; +}; + +#define UB_HP_MSG_SIZE 40 + enum power_state { POWER_OFF, POWER_ON @@ -101,6 +109,9 @@ bool ubhp_card_present(struct ub_slot *slot); void ubhp_start_slots(struct ub_entity *uent); void ubhp_stop_slots(struct ub_entity *uent); +/* msg */ +void ubhp_event_handler(struct ub_bus_controller *ubc, void *pkt, u16 len); + /* core */ void ubhp_handle_power(struct ub_slot *slot, bool power_on); diff --git a/drivers/ub/ubus/services/hotplug/hotplug_msg.c b/drivers/ub/ubus/services/hotplug/hotplug_msg.c new file mode 100644 index 000000000000..53cbe832c3cb --- /dev/null +++ b/drivers/ub/ubus/services/hotplug/hotplug_msg.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus hp msg: " fmt + +#include "../../ubus.h" +#include "../../msg.h" +#include "../../link.h" +#include "hotplug.h" + +static struct ub_slot *ub_find_slot(struct ub_entity *uent, u16 slot_id) +{ + struct ub_slot *slot; + + list_for_each_entry(slot, &uent->slot_list, node) + if (slot->slot_id == slot_id) + return slot; + + return NULL; +} + +static struct ub_slot *ubhp_get_slot_from_msg(void *pkt) +{ + struct msg_pkt_header *header = (struct msg_pkt_header *)pkt; + struct ubhp_msg_payload *payload; + struct ub_slot *slot; + struct ub_entity *uent; + u32 seid; + + seid = eid_gen(header->seid_h, header->seid_l); + uent = ub_get_ent_by_eid(seid); + if (!uent) { + pr_warn("get no device by eid %#05x\n", seid); + return NULL; + } + + payload = (struct ubhp_msg_payload *)header->payload; + slot = ubhp_get_slot(ub_find_slot(uent, payload->slot_id)); + if (!slot) + pr_err("can not find slot with id %u\n", payload->slot_id); + + ub_entity_put(uent); + return slot; +} + +void ubhp_handle_event(struct ub_slot *slot, enum hotplug_event event) +{ + bool queued = false; + u32 flag; + + if (!ubhp_confirm_event(slot, event)) + return; + + /** + * get slot if work is queued + * queue_work: return false if work already exists + * mod_delayed_work: return true if work exists, with its timer modified + */ + if (event == HPE_BUTTON_PRESSED) { + queued = queue_work(get_rx_msg_wq(UB_MSG_CODE_LINK), + &slot->button_work); + } else if (event == HPE_PRESENCE_DETECT) { + flag = work_busy(&slot->present_work.work); + if (!(flag & WORK_BUSY_RUNNING)) + queued = !mod_delayed_work( + get_rx_msg_wq(UB_MSG_CODE_LINK), + &slot->present_work, 0); + } + if (queued) + ubhp_get_slot(slot); +} + +void ubhp_event_handler(struct ub_bus_controller *ubc, void *pkt, u16 len) +{ + struct msg_pkt_header *header = (struct msg_pkt_header *)pkt; + struct ub_slot *slot; + + if (len < UB_HP_MSG_SIZE) { + dev_err(&ubc->dev, "hp msg len[%#x] invalid\n", len); + return; + } + + slot = ubhp_get_slot_from_msg(pkt); + if (!slot) + return; + + ubhp_handle_event(slot, + (enum hotplug_event)header->msgetah.sub_msg_code); + ubhp_put_slot(slot); +} -- Gitee From 52f463ba04269ed4d13c927969395c78f9636cd4 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Mon, 22 Sep 2025 18:40:37 +0800 Subject: [PATCH 076/214] ub:ubus: Add basic System Filesystem attributes ANBZ: #45460 commit 9c1715dd13010d00e644abe937f867d1e1b8f7ed openEuler. Add some new basic System Filesystem attributes, including reset, device_reset, software capability, upi, eid, ummu_map and other attributes. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/sysfs.c | 196 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) diff --git a/drivers/ub/ubus/sysfs.c b/drivers/ub/ubus/sysfs.c index 309d685d31c2..6dd8ba5bb200 100644 --- a/drivers/ub/ubus/sysfs.c +++ b/drivers/ub/ubus/sysfs.c @@ -9,6 +9,16 @@ #include "sysfs.h" #include "ubus_entity.h" #include "instance.h" +#include "port.h" + +static inline void ub_resource_to_user(const struct ub_entity *dev, int res_id, + const struct resource *rsrc, + resource_size_t *start, + resource_size_t *end) +{ + *start = rsrc->start; + *end = rsrc->end; +} #define ub_config_attr(field, format_string) \ static ssize_t field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ @@ -24,6 +34,15 @@ ub_config_attr(device, "%#06x\n"); ub_config_attr(type, "%#x\n"); ub_config_attr(vendor, "%#06x\n"); +static ssize_t ubc_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ub_entity *uent; + + uent = to_ub_entity(dev); + return sysfs_emit(buf, "%#05x\n", uent->ubc->uent->uent_num); +} +static DEVICE_ATTR_RO(ubc); + static ssize_t class_code_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ub_entity *uent; @@ -44,12 +63,56 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr, char } static DEVICE_ATTR_RO(guid); +static ssize_t entity_idx_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + + return sysfs_emit(buf, "%u\n", uent->entity_idx); +} +static DEVICE_ATTR_RO(entity_idx); + +static ssize_t eid_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + + return sysfs_emit(buf, "%u\n", uent->eid); +} +static DEVICE_ATTR_RO(eid); + +static ssize_t tid_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + + return sysfs_emit(buf, "%u\n", uent->tid); +} +static DEVICE_ATTR_RO(tid); + static ssize_t kref_show(struct device *dev, struct device_attribute *attr, char *buf) { return sysfs_emit(buf, "%u\n", kref_read(&dev->kobj.kref)); } static DEVICE_ATTR_RO(kref); +static ssize_t resource_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + resource_size_t start, end; + int i, cnt = 0; + + for (i = 0; i < MAX_UB_RES_NUM; i++) { + struct resource *res = &uent->zone[i].res; + + ub_resource_to_user(uent, i, res, &start, &end); + cnt += sysfs_emit_at(buf, cnt, "%#016llx %#016llx %#016llx\n", + (unsigned long long)start, + (unsigned long long)end, + (unsigned long long)res->flags); + } + return cnt; +} +static DEVICE_ATTR_RO(resource); + static ssize_t driver_override_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -112,6 +175,34 @@ static ssize_t match_driver_show(struct device *dev, } static DEVICE_ATTR_RW(match_driver); +static ssize_t direct_link_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + struct ub_port *port; + int cnt = 0; + + for_each_uent_port(port, uent) { + if (!port->r_uent) + continue; + cnt += sysfs_emit_at(buf, cnt, "%#04x : %#04x [%#05x]\n", + port->index, port->r_index, + port->r_uent->uent_num); + } + + return cnt; +} +DEVICE_ATTR_RO(direct_link); + +static ssize_t primary_cna_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + + return sysfs_emit(buf, "%#06x\n", uent->cna); +} +DEVICE_ATTR_RO(primary_cna); + static ssize_t instance_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -125,15 +216,60 @@ static ssize_t instance_show(struct device *dev, struct device_attribute *attr, } DEVICE_ATTR_RO(instance); +static ssize_t upi_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + + return sysfs_emit(buf, "%#04x\n", uent->upi); +} +DEVICE_ATTR_RO(upi); + +static ssize_t numa_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + + return sysfs_emit(buf, "%#04x\n", uent->ubc->attr.proximity_domain); +} +DEVICE_ATTR_RO(numa); + +static ssize_t primary_entity_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + + if (uent->entity_idx == 0) /* this device is primary entity */ + goto pue_print; + else if (uent->is_mue) /* find primary entity for mue */ + uent = uent->pue; + else /* find primary entity for ue */ + uent = uent->pue->pue; + +pue_print: + return sysfs_emit(buf, "%#05x\n", uent->uent_num); +} +DEVICE_ATTR_RO(primary_entity); + static struct attribute *ub_entity_attrs[] = { + &dev_attr_resource.attr, &dev_attr_vendor.attr, &dev_attr_device.attr, &dev_attr_class_code.attr, &dev_attr_type.attr, &dev_attr_driver_override.attr, &dev_attr_match_driver.attr, + &dev_attr_direct_link.attr, &dev_attr_guid.attr, + &dev_attr_ubc.attr, + &dev_attr_primary_cna.attr, &dev_attr_instance.attr, + &dev_attr_upi.attr, + &dev_attr_entity_idx.attr, + &dev_attr_numa.attr, + &dev_attr_eid.attr, + &dev_attr_tid.attr, + &dev_attr_primary_entity.attr, &dev_attr_kref.attr, NULL }; @@ -165,6 +301,57 @@ const struct device_type ub_dev_type = { .groups = NULL, }; +static ssize_t reset_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ub_entity *uent = to_ub_entity(dev); + unsigned long val; + ssize_t result; + + result = kstrtoul(buf, 0, &val); + if (result < 0) + return result; + + if (val != 1) + return -EINVAL; + + result = ub_reset_entity(uent); + if (result < 0) + return result; + + return count; +} +static DEVICE_ATTR_WO(reset); + +static ssize_t device_reset_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ub_entity *uent = to_ub_entity(dev); + unsigned long val; + ssize_t result; + + result = kstrtoul(buf, 0, &val); + if (result < 0 || val != 1) + return -EINVAL; + + result = ub_device_reset(uent); + if (result < 0) + return result; + + return count; +} +static DEVICE_ATTR_WO(device_reset); + +static ssize_t sw_cap_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ub_entity *uent; + + uent = to_ub_entity(dev); + return sysfs_emit(buf, "%d\n", uent->sw_cap); +} +static DEVICE_ATTR_RO(sw_cap); + static ssize_t ub_total_entities_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -363,12 +550,17 @@ DEVICE_ATTR_RO(mue_list); #define CREATE_CONDITIONS(c) ((c) ? 1 : 0) +/* When adding sysfs, remember to add it in remove function. */ static int ub_create_capabilities_sysfs(struct ub_entity *uent) { struct dev_attr_creat_group grp[] = { + { &dev_attr_reset, CREATE_CONDITIONS(uent->reset_fn) }, + { &dev_attr_device_reset, + CREATE_CONDITIONS(uent->entity_idx == 0 && !is_ibus_controller(uent)) }, { &dev_attr_ub_total_entities, CREATE_CONDITIONS(uent->entity_idx == 0) }, { &dev_attr_ub_totalues, CREATE_CONDITIONS(uent->is_mue) }, { &dev_attr_ub_numues, CREATE_CONDITIONS(uent->is_mue) }, + { &dev_attr_sw_cap, CREATE_CONDITIONS(is_ibus_controller(uent)) }, { &dev_attr_ub_release_ue, CREATE_CONDITIONS(uent->is_mue && entity_flex_en) }, { &dev_attr_mue_list, CREATE_CONDITIONS(uent->entity_idx == 0) }, { &dev_attr_ue_list, CREATE_CONDITIONS(uent->is_mue) }, @@ -394,9 +586,13 @@ static int ub_create_capabilities_sysfs(struct ub_entity *uent) static void ub_remove_capabilities_sysfs(struct ub_entity *uent) { struct dev_attr_creat_group grp[] = { + { &dev_attr_reset, CREATE_CONDITIONS(uent->reset_fn) }, + { &dev_attr_device_reset, + CREATE_CONDITIONS(uent->entity_idx == 0 && !is_ibus_controller(uent)) }, { &dev_attr_ub_total_entities, CREATE_CONDITIONS(uent->entity_idx == 0) }, { &dev_attr_ub_totalues, CREATE_CONDITIONS(uent->is_mue) }, { &dev_attr_ub_numues, CREATE_CONDITIONS(uent->is_mue) }, + { &dev_attr_sw_cap, CREATE_CONDITIONS(is_ibus_controller(uent)) }, { &dev_attr_ub_release_ue, CREATE_CONDITIONS(uent->is_mue && entity_flex_en) }, { &dev_attr_mue_list, CREATE_CONDITIONS(uent->entity_idx == 0) }, { &dev_attr_ue_list, CREATE_CONDITIONS(uent->is_mue) }, -- Gitee From d880219fcf8bd50a28a5c085c1fdcb095b225337 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Mon, 22 Sep 2025 20:43:38 +0800 Subject: [PATCH 077/214] ub:ubus: Add configuration and resource sysfs interfaces ANBZ: #45460 commit 7b11279fcde45d86c90b0ed3c9456b823100b655 openEuler. Add configuration and resource System Filesystem interfaces, including creating and moving files. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/sysfs.c | 311 +++++++++++++++++++++++++++++++++- drivers/ub/ubus/sysfs.h | 5 + drivers/ub/ubus/ubus_entity.c | 3 + include/ub/ubus/ubus.h | 5 + 4 files changed, 323 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubus/sysfs.c b/drivers/ub/ubus/sysfs.c index 6dd8ba5bb200..c4899152ba6f 100644 --- a/drivers/ub/ubus/sysfs.c +++ b/drivers/ub/ubus/sysfs.c @@ -10,6 +10,7 @@ #include "ubus_entity.h" #include "instance.h" #include "port.h" +#include "resource.h" static inline void ub_resource_to_user(const struct ub_entity *dev, int res_id, const struct resource *rsrc, @@ -93,6 +94,127 @@ static ssize_t kref_show(struct device *dev, struct device_attribute *attr, char } static DEVICE_ATTR_RO(kref); +#define DATA_POS (cur_pos - usr_pos) +#define REMAIN_BYTE (len - DATA_POS) + +static ssize_t ub_read_config(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t usr_pos, size_t count) +{ + struct ub_entity *uent = to_ub_entity(kobj_to_dev(kobj)); + u64 cur_pos = usr_pos; + u8 *data = (u8 *)buf; + size_t len = count >> 1; + u32 val; + + if (count == PAGE_SIZE || len == 0) + return -EINVAL; + + memset(buf, 0, count); + + if (cur_pos & 1) { + if (ub_cfg_read_byte(uent, cur_pos, (u8 *)&val)) + memcpy(data + DATA_POS + len, &val, 1); + memcpy(data + DATA_POS, &val, 1); + cur_pos++; + } + + if ((cur_pos & SZ_2) && (REMAIN_BYTE >= SZ_2)) { + if (ub_cfg_read_word(uent, cur_pos, (u16 *)&val)) + memcpy(data + DATA_POS + len, &val, SZ_2); + memcpy(data + DATA_POS, &val, SZ_2); + cur_pos += SZ_2; + } + + while (REMAIN_BYTE >= SZ_4) { + if (ub_cfg_read_dword(uent, cur_pos, &val)) + memcpy(data + DATA_POS + len, &val, SZ_4); + memcpy(data + DATA_POS, &val, SZ_4); + cur_pos += SZ_4; + } + + if (REMAIN_BYTE >= SZ_2) { + if (ub_cfg_read_word(uent, cur_pos, (u16 *)&val)) + memcpy(data + DATA_POS + len, &val, SZ_2); + memcpy(data + DATA_POS, &val, SZ_2); + cur_pos += SZ_2; + } + + if (REMAIN_BYTE) { + if (ub_cfg_read_byte(uent, cur_pos, (u8 *)&val)) + memcpy(data + DATA_POS + len, &val, 1); + memcpy(data + DATA_POS, &val, 1); + cur_pos++; + } + + return count; +} + +static ssize_t ub_write_config(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t usr_pos, size_t count) +{ + struct ub_entity *uent = to_ub_entity(kobj_to_dev(kobj)); + u64 cur_pos = usr_pos; + u8 *data = (u8 *)buf; + size_t len = count; + + if (cur_pos & 1) { + u8 val; + + memcpy(&val, data + DATA_POS, 1); + ub_cfg_write_byte(uent, cur_pos, val); + cur_pos++; + } + + if ((cur_pos & SZ_2) && (REMAIN_BYTE >= SZ_2)) { + u16 val; + + memcpy(&val, data + DATA_POS, SZ_2); + ub_cfg_write_word(uent, cur_pos, val); + cur_pos += SZ_2; + } + + while (REMAIN_BYTE >= SZ_4) { + u32 val; + + memcpy(&val, data + DATA_POS, SZ_4); + ub_cfg_write_dword(uent, cur_pos, val); + cur_pos += SZ_4; + } + + if (REMAIN_BYTE >= SZ_2) { + u16 val; + + memcpy(&val, data + DATA_POS, SZ_2); + ub_cfg_write_word(uent, cur_pos, val); + cur_pos += SZ_2; + } + + if (REMAIN_BYTE) { + u8 val; + + memcpy(&val, data + DATA_POS, 1); + ub_cfg_write_byte(uent, cur_pos, val); + cur_pos++; + } + + return count; +} + +#undef REMAIN_BYTE +#undef DATA_POS + +static const struct bin_attribute ub_config_bin_attr = { + .attr = { + .name = "config", + .mode = 0644, + }, + .size = UB_CFG_SAPCE_SLICE_END, + .read = ub_read_config, + .write = ub_write_config, +}; + static ssize_t resource_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -283,8 +405,22 @@ const struct attribute_group *ub_entity_groups[] = { NULL }; +static ssize_t cluster_show(const struct bus_type *bus, char *buf) +{ + struct ub_bus_controller *ubc; + + if (list_empty(&ubc_list)) + return 0; + + ubc = list_first_entry(&ubc_list, struct ub_bus_controller, node); + return sysfs_emit(buf, "%d\n", ubc->cluster); +} +static BUS_ATTR_RO(cluster); + + static struct attribute *ub_bus_attrs[] = { &bus_attr_instance.attr, + &bus_attr_cluster.attr, NULL }; @@ -604,18 +740,191 @@ static void ub_remove_capabilities_sysfs(struct ub_entity *uent) device_remove_file(&uent->dev, grp[i].attr); } +static bool ub_mmap_fits(struct ub_entity *uent, int idx, + struct vm_area_struct *vma) +{ + resource_size_t len, start, size; + + if (ub_resource_len(uent, idx) == 0) + return false; + + len = vma_pages(vma); + start = vma->vm_pgoff; + size = ((ub_resource_len(uent, idx) - 1) >> PAGE_SHIFT) + 1; + if (start < size && (start + len <= size)) + return true; + + return false; +} + +static int ub_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, + struct vm_area_struct *vma, int write_combine) +{ + struct ub_entity *uent = to_ub_entity(kobj_to_dev(kobj)); + unsigned long idx = (unsigned long)attr->private; + + if (idx >= MAX_UB_RES_NUM) + return -EINVAL; + + if (!ub_mmap_fits(uent, idx, vma)) + return -EINVAL; + + return ub_mmap_resource_range(uent, idx, vma, write_combine); +} + +static int ub_mmap_resource_wc(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + struct vm_area_struct *vma) +{ + return ub_mmap_resource(kobj, attr, vma, 1); +} + +static int ub_mmap_resource_uc(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + struct vm_area_struct *vma) +{ + return ub_mmap_resource(kobj, attr, vma, 0); +} + +static int ub_create_attr(struct ub_entity *uent, int num, int write_combine) +{ + /* allocate attribute structure, piggyback attribute name */ + int name_len = write_combine ? 13 : 10; + struct bin_attribute *attr; + char *res_attr_name; + int retval; + + attr = kzalloc(sizeof(*attr) + name_len, GFP_ATOMIC); + if (!attr) + return -ENOMEM; + + res_attr_name = (char *)(attr + 1); + + sysfs_bin_attr_init(attr); + if (write_combine) { + uent->res_attr_wc[num] = attr; + sprintf(res_attr_name, "resource%d_wc", num); + attr->mmap = ub_mmap_resource_wc; + } else { + uent->res_attr[num] = attr; + sprintf(res_attr_name, "resource%d", num); + attr->mmap = ub_mmap_resource_uc; + } + attr->attr.name = res_attr_name; + attr->attr.mode = 0600; + attr->size = ub_resource_len(uent, num); + attr->private = (void *)(unsigned long)num; + retval = sysfs_create_bin_file(&uent->dev.kobj, attr); + if (retval) { + kfree(attr); + write_combine ? (uent->res_attr_wc[num] = NULL) : + (uent->res_attr[num] = NULL); + } + + return retval; +} + +static void ub_remove_resource_files(struct ub_entity *uent) +{ + int i; + + for (i = 0; i < MAX_UB_RES_NUM; i++) { + struct bin_attribute *res_attr; + + res_attr = uent->res_attr[i]; + if (res_attr) { + sysfs_remove_bin_file(&uent->dev.kobj, res_attr); + kfree(res_attr); + uent->res_attr[i] = NULL; + } + + res_attr = uent->res_attr_wc[i]; + if (res_attr) { + sysfs_remove_bin_file(&uent->dev.kobj, res_attr); + kfree(res_attr); + uent->res_attr_wc[i] = NULL; + } + } +} + +static int ub_create_resource_files(struct ub_entity *uent) +{ + int i; + int retval; + + for (i = 0; i < MAX_UB_RES_NUM; i++) { + /* skip empty resources */ + if (!ub_resource_len(uent, i)) + continue; + + retval = ub_create_attr(uent, i, 0); + if (retval) { + ub_remove_resource_files(uent); + return retval; + } + + retval = ub_create_attr(uent, i, 1); + if (retval) { + ub_remove_resource_files(uent); + return retval; + } + } + return 0; +} + int ub_create_sysfs_dev_files(struct ub_entity *uent) { int retval; + /* config interface */ + retval = sysfs_create_bin_file(&uent->dev.kobj, &ub_config_bin_attr); + if (retval) + goto err; + + retval = ub_create_resource_files(uent); + if (retval) + goto err_config_file; + retval = ub_create_capabilities_sysfs(uent); if (retval) - return retval; + goto err_resource_files; return 0; + +err_resource_files: + ub_remove_resource_files(uent); +err_config_file: + sysfs_remove_bin_file(&uent->dev.kobj, &ub_config_bin_attr); +err: + return retval; } void ub_remove_sysfs_ent_files(struct ub_entity *uent) { ub_remove_capabilities_sysfs(uent); + + sysfs_remove_bin_file(&uent->dev.kobj, &ub_config_bin_attr); + + ub_remove_resource_files(uent); +} + +int ub_bus_attr_dynamic_init(void) +{ + int ret; + + ret = bus_create_file(&ub_bus_type, &bus_attr_instance); + if (ret) + return ret; + + ret = bus_create_file(&ub_bus_type, &bus_attr_cluster); + if (ret) + bus_remove_file(&ub_bus_type, &bus_attr_instance); + + return ret; +} + +void ub_bus_attr_dynamic_uninit(void) +{ + bus_remove_file(&ub_bus_type, &bus_attr_cluster); + bus_remove_file(&ub_bus_type, &bus_attr_instance); } diff --git a/drivers/ub/ubus/sysfs.h b/drivers/ub/ubus/sysfs.h index bbd4d1b49dbc..6716be59183e 100644 --- a/drivers/ub/ubus/sysfs.h +++ b/drivers/ub/ubus/sysfs.h @@ -5,8 +5,13 @@ #ifndef __SYSFS_H__ #define __SYSFS_H__ +struct ub_entity; extern const struct attribute_group *ub_entity_groups[]; extern const struct attribute_group *ub_bus_groups[]; extern const struct device_type ub_dev_type; +int ub_create_sysfs_dev_files(struct ub_entity *pue); +void ub_remove_sysfs_ent_files(struct ub_entity *pue); +int ub_bus_attr_dynamic_init(void); +void ub_bus_attr_dynamic_uninit(void); #endif /* __SYSFS_H__ */ diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index c13cf5b312d9..dc9bccff9044 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -437,6 +437,8 @@ void ub_start_ent(struct ub_entity *uent) ret = ub_default_bus_instance_init(uent); WARN_ON(ret); + ub_create_sysfs_dev_files(uent); + if (!((is_p_device(uent) || is_p_idevice(uent)) && is_dynamic(uent->bi))) { uent->match_driver = true; ret = device_attach(&uent->dev); @@ -498,6 +500,7 @@ void ub_stop_ent(struct ub_entity *uent) device_release_driver(&uent->dev); uent->match_driver = false; + ub_remove_sysfs_ent_files(uent); ub_default_bus_instance_uninit(uent); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 80268750356b..5a30b8ab466b 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -218,6 +218,11 @@ struct ub_entity { u64 dma_mask; struct device_dma_parameters dma_parms; + /* entity user interface */ + struct bin_attribute *res_attr[MAX_UB_RES_NUM]; /* sysfs file for resources */ + /* sysfs file for WC mapping of resources */ + struct bin_attribute *res_attr_wc[MAX_UB_RES_NUM]; + /* UB interrupt info */ raw_spinlock_t usi_lock; unsigned int no_intr : 1; -- Gitee From 2b135ed83abd4c66580f227c8938101c676c1b6f Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Mon, 22 Sep 2025 19:26:54 +0800 Subject: [PATCH 078/214] ub:ubus: Support UBUS driver host probe and remove ANBZ: #45460 commit a65171dd038b73d5944c8634069697824b16b995 openEuler. Support UBUS driver host probe and remove. Add controller device enum and cdev and receive message initialize in host probe. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/ubus_driver.c | 50 +++++++++++++++++++++++++++++++++++ drivers/ub/ubus/ubus_driver.h | 2 ++ 2 files changed, 52 insertions(+) diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index ed6a0e353d65..93c1dc5d662a 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -14,6 +14,10 @@ #include #include "services.h" +#include "msg.h" +#include "enum.h" +#include "instance.h" +#include "ioctl.h" #include "sysfs.h" #include "ubus.h" #include "ubus_config.h" @@ -643,6 +647,12 @@ struct bus_type ub_service_bus_type = { .match = ub_service_bus_match, }; +static void ubus_driver_resource_drain(void) +{ + ub_dynamic_bus_instance_drain(); + ub_static_cluster_instance_drain(); +} + int ub_host_probe(void) { int ret; @@ -652,6 +662,22 @@ int ub_host_probe(void) if (ret) goto ub_cfg_ops_init_fail; + ret = ub_bus_controllers_probe(); + if (ret) + goto ubcs_probe_fail; + + ret = ub_enum_probe(); + if (ret) + goto ub_enum_probe_fail; + + /* + * Now ub_bus_type build-in, bus_attr_groups will not created, + * so init it here. + */ + ret = ub_bus_attr_dynamic_init(); + if (ret) + goto ub_bus_attr_dynamic_init_fail; + ret = bus_register(&ub_service_bus_type); if (ret) goto bus_register_fail; @@ -660,11 +686,29 @@ int ub_host_probe(void) if (ret) goto ub_services_init_fail; + ret = ub_cdev_init(); + if (ret) + goto cdev_fail; + + ret = message_rx_init(); + if (ret) + goto message_init_fail; + return 0; +message_init_fail: + ub_cdev_uninit(); +cdev_fail: + ub_services_exit(); ub_services_init_fail: bus_unregister(&ub_service_bus_type); bus_register_fail: + ub_bus_attr_dynamic_uninit(); +ub_bus_attr_dynamic_init_fail: + ub_enum_remove(); +ub_enum_probe_fail: + ub_bus_controllers_remove(); +ubcs_probe_fail: unregister_ub_cfg_ops(); ub_cfg_ops_init_fail: ub_bus_type_uninit(); @@ -674,8 +718,14 @@ EXPORT_SYMBOL_GPL(ub_host_probe); void ub_host_remove(void) { + message_rx_uninit(); + ub_cdev_uninit(); ub_services_exit(); bus_unregister(&ub_service_bus_type); + ub_bus_attr_dynamic_uninit(); + ubus_driver_resource_drain(); + ub_enum_remove(); + ub_bus_controllers_remove(); unregister_ub_cfg_ops(); ub_bus_type_uninit(); } diff --git a/drivers/ub/ubus/ubus_driver.h b/drivers/ub/ubus/ubus_driver.h index b2eab906fa31..f2bff32bbee9 100644 --- a/drivers/ub/ubus/ubus_driver.h +++ b/drivers/ub/ubus/ubus_driver.h @@ -8,5 +8,7 @@ extern struct rw_semaphore ub_bus_sem; extern struct bus_type ub_service_bus_type; +int ub_host_probe(void); +void ub_host_remove(void); #endif /* __UBUS_DRIVER_H__ */ -- Gitee From 757a34825b9ed9eedac67ef31c7c902220888901 Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Mon, 22 Sep 2025 16:58:50 +0800 Subject: [PATCH 079/214] ub:hisi-ubus: Add HiSilicon ubus driver framework ANBZ: #45460 commit d1391fa1d2d754cffcc976f8ac1fab3635a81988 openEuler. Add HiSilicon ubus driver framework. And parse hisi private data. Signed-off-by: Yuhao Xiang Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- .../L1-RECOMMEND/arm64/CONFIG_UB_HISI_UBUS | 1 + drivers/ub/ubus/Kconfig | 2 + drivers/ub/ubus/Makefile | 1 + drivers/ub/ubus/vendor/Kconfig | 18 +++++ drivers/ub/ubus/vendor/Makefile | 3 + drivers/ub/ubus/vendor/hisilicon/Makefile | 5 ++ drivers/ub/ubus/vendor/hisilicon/controller.c | 54 +++++++++++++ drivers/ub/ubus/vendor/hisilicon/hisi-ubus.c | 76 +++++++++++++++++++ drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h | 41 ++++++++++ include/ub/ubus/ubus.h | 1 + 10 files changed, 202 insertions(+) create mode 100644 anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_HISI_UBUS create mode 100644 drivers/ub/ubus/vendor/Kconfig create mode 100644 drivers/ub/ubus/vendor/Makefile create mode 100644 drivers/ub/ubus/vendor/hisilicon/Makefile create mode 100644 drivers/ub/ubus/vendor/hisilicon/controller.c create mode 100644 drivers/ub/ubus/vendor/hisilicon/hisi-ubus.c create mode 100644 drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h diff --git a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_HISI_UBUS b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_HISI_UBUS new file mode 100644 index 000000000000..aee9022a4efb --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_HISI_UBUS @@ -0,0 +1 @@ +CONFIG_UB_HISI_UBUS=m \ No newline at end of file diff --git a/drivers/ub/ubus/Kconfig b/drivers/ub/ubus/Kconfig index fc509766b125..a754f554628a 100644 --- a/drivers/ub/ubus/Kconfig +++ b/drivers/ub/ubus/Kconfig @@ -39,4 +39,6 @@ config UB_UBUS_USI UnifiedBus instead of asserting a device IRQ pin.If you don't know what to do here, say Y. +source "drivers/ub/ubus/vendor/Kconfig" + endif diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 76cfef6f30e1..7456bdb0a787 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -12,3 +12,4 @@ ubus-y += services/hotplug/hotplug_core.o services/hotplug/hotplug_ctrl.o ubus-y += services/hotplug/hotplug_route.o services/hotplug/hotplug_msg.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o +obj-y += vendor/ diff --git a/drivers/ub/ubus/vendor/Kconfig b/drivers/ub/ubus/vendor/Kconfig new file mode 100644 index 000000000000..bd4c3448bf4c --- /dev/null +++ b/drivers/ub/ubus/vendor/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0+ + +menu "UB vendor drivers" + depends on UB_UBUS + +config UB_HISI_UBUS + tristate "hisilicon ubus" + depends on UB_UBUS_BUS + depends on UB_UBUS_USI + default n + help + This option enables the modular part of the hisilicon ubus driver. + If you choose the ubus function provided by HiSilicon say y + and it will be accessible from within Linux. + To compile this driver as a module, choose M here. + Say 'M' here unless you know what you are doing + +endmenu diff --git a/drivers/ub/ubus/vendor/Makefile b/drivers/ub/ubus/vendor/Makefile new file mode 100644 index 000000000000..0a8a19acb2fc --- /dev/null +++ b/drivers/ub/ubus/vendor/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-y += hisilicon/ diff --git a/drivers/ub/ubus/vendor/hisilicon/Makefile b/drivers/ub/ubus/vendor/hisilicon/Makefile new file mode 100644 index 000000000000..f7d163189acf --- /dev/null +++ b/drivers/ub/ubus/vendor/hisilicon/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0+ + +hisi_ubus-objs := hisi-ubus.o controller.o + +obj-$(CONFIG_UB_HISI_UBUS) += hisi_ubus.o diff --git a/drivers/ub/ubus/vendor/hisilicon/controller.c b/drivers/ub/ubus/vendor/hisilicon/controller.c new file mode 100644 index 000000000000..eeadb39d38c9 --- /dev/null +++ b/drivers/ub/ubus/vendor/hisilicon/controller.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus hisi ctl: " fmt + +#include +#include +#include + +#include "../../ubus_controller.h" +#include "hisi-ubus.h" + +static struct ub_bus_controller_ops hi_ubc_ops = { + .register_decoder_base_addr = hi_register_decoder_base_addr, +}; + +void hi_register_decoder_base_addr(struct ub_bus_controller *ubc, u64 *cmd_queue, + u64 *event_queue) +{ + struct hi_ubc_private_data *data = (struct hi_ubc_private_data *)ubc->data; + + *cmd_queue = data->io_decoder_cmdq; + *event_queue = data->io_decoder_evtq; +} + +static void ub_bus_controller_debugfs_init(struct ub_bus_controller *ubc) +{ + if (!debugfs_initialized()) + return; + + ubc->debug_root = debugfs_create_dir(ubc->name, NULL); +} + +static void ub_bus_controller_debugfs_uninit(struct ub_bus_controller *ubc) +{ + debugfs_remove_recursive(ubc->debug_root); + ubc->debug_root = NULL; +} + +int ub_bus_controller_probe(struct ub_bus_controller *ubc) +{ + ubc->ops = &hi_ubc_ops; + ub_bus_controller_debugfs_init(ubc); + + return 0; +} + +void ub_bus_controller_remove(struct ub_bus_controller *ubc) +{ + ub_bus_controller_debugfs_uninit(ubc); + ubc->ops = NULL; +} diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.c b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.c new file mode 100644 index 000000000000..f3e9c3097ffb --- /dev/null +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include +#include + +#include "../../ubus.h" +#include "hisi-ubus.h" + +#define HISI_VENDOR_ID 0xCC08 +#define HISI_UBUS_DRV_NAME "hisi_ubus" + +static const struct ub_manage_subsystem_ops hisi_ub_manage_subsystem_ops = { + .vendor = HISI_VENDOR_ID, + .controller_probe = ub_bus_controller_probe, + .controller_remove = ub_bus_controller_remove, +}; + +static const struct of_device_id hisi_ubus_of_match[] = { + { .compatible = "hisi,ubus", }, + {} +}; +MODULE_DEVICE_TABLE(of, hisi_ubus_of_match); + +static const struct acpi_device_id hisi_ubus_acpi_match[] = { + { "HISI0581", 0 }, + {} +}; +MODULE_DEVICE_TABLE(acpi, hisi_ubus_acpi_match); + +static struct platform_driver hisi_ubus_driver = { + .driver = { + .name = HISI_UBUS_DRV_NAME, + .of_match_table = hisi_ubus_of_match, + .acpi_match_table = hisi_ubus_acpi_match, + }, +}; + +static int __init hisi_ubus_driver_register(struct platform_driver *drv) +{ + int ret; + + ret = register_ub_manage_subsystem_ops(&hisi_ub_manage_subsystem_ops); + if (ret) + return ret; + + ret = ub_host_probe(); + if (ret) + goto host_probe_fail; + + ret = platform_driver_register(drv); + if (ret) + goto platform_driver_register_fail; + + return 0; + +platform_driver_register_fail: + ub_host_remove(); +host_probe_fail: + unregister_ub_manage_subsystem_ops(&hisi_ub_manage_subsystem_ops); + return ret; +} + +static void __exit hisi_ubus_driver_unregister(struct platform_driver *drv) +{ + platform_driver_unregister(drv); + ub_host_remove(); + unregister_ub_manage_subsystem_ops(&hisi_ub_manage_subsystem_ops); +} + +module_driver(hisi_ubus_driver, hisi_ubus_driver_register, hisi_ubus_driver_unregister); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Hisilicon UnifiedBus Manage Subsystem"); +MODULE_IMPORT_NS(UB_UBUS); diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h new file mode 100644 index 000000000000..f0e98a5abbe8 --- /dev/null +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __HISI_UBUS_H__ +#define __HISI_UBUS_H__ + +#include + +#define MEM_INFO_NUM 5 +#define MB_SIZE_OFFSET 20 +#define HI_UBC_PRIVATE_DATA_RESERVED 3 +#define HI_UBC_PRIVATE_DATA_RESERVED2 111 + +struct hi_mem_pa_info { + u64 decode_addr; + u32 cc_base_addr; + u32 cc_base_size; + u32 nc_base_addr; + u32 nc_base_size; +}; + +struct hi_ubc_private_data { + u32 ub_mem_version; + u8 max_addr_bits; + u8 reserved[HI_UBC_PRIVATE_DATA_RESERVED]; + struct hi_mem_pa_info mem_pa_info[MEM_INFO_NUM]; + u64 io_decoder_cmdq; + u64 io_decoder_evtq; + u8 features; + u8 reserved2[HI_UBC_PRIVATE_DATA_RESERVED2]; +}; + +void hi_register_decoder_base_addr(struct ub_bus_controller *ubc, u64 *cmd_queue, + u64 *event_queue); + +int ub_bus_controller_probe(struct ub_bus_controller *ubc); +void ub_bus_controller_remove(struct ub_bus_controller *ubc); + +#endif /* __HISI_UBUS_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 5a30b8ab466b..6da027092df1 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -388,6 +388,7 @@ struct ub_bus_controller { struct ub_bus_instance *cluster_bi; void *data; + struct dentry *debug_root; }; struct ub_bus_instance_info { -- Gitee From de5411bd01e06aaf2a2091f4f457a49edc30fe62 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Mon, 22 Sep 2025 19:53:03 +0800 Subject: [PATCH 080/214] ub:hisi-ubus: Support UBUS vendor defined message framework ANBZ: #45460 commit 422f1e2c82f5937b996167e688f04469d0ae88c3 openEuler. Support UBUS vendor defined message framework.Get receive messages and handle message header and payload, send response when complete. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/msg.c | 94 ++++++++++++++- drivers/ub/ubus/msg.h | 2 + drivers/ub/ubus/vendor/hisilicon/Makefile | 2 +- drivers/ub/ubus/vendor/hisilicon/vdm.c | 111 ++++++++++++++++++ drivers/ub/ubus/vendor/hisilicon/vdm.h | 132 ++++++++++++++++++++++ include/ub/ubus/ubus.h | 26 +++++ 6 files changed, 365 insertions(+), 2 deletions(-) create mode 100644 drivers/ub/ubus/vendor/hisilicon/vdm.c create mode 100644 drivers/ub/ubus/vendor/hisilicon/vdm.h diff --git a/drivers/ub/ubus/msg.c b/drivers/ub/ubus/msg.c index 5b0e31e670c7..1d1893a8f54d 100644 --- a/drivers/ub/ubus/msg.c +++ b/drivers/ub/ubus/msg.c @@ -167,7 +167,7 @@ static bool msg_rx_flag; int message_rx_init(void) { const char *msg_name[UB_MSG_CODE_NUM] = { - NULL, "link wq", NULL, NULL, + NULL, "link wq", NULL, "vdm wq", NULL, NULL, "pool wq", NULL }; struct workqueue_struct *q; @@ -279,6 +279,9 @@ static void message_rx_work(struct work_struct *work) handler = rx_msg_handler[msg_code]; + if (msg_code == UB_MSG_CODE_VDM) + handler = ubc->mdev->ops->vdm_rx_handler; + if (handler) handler(ubc, task->pkt, task->len); else @@ -324,3 +327,92 @@ int message_rx_handler(struct ub_bus_controller *ubc, void *pkt, u16 len) return 0; } EXPORT_SYMBOL_GPL(message_rx_handler); + +static int handle_vdm_rsp_msg(struct ub_entity *uent, struct ub_vdm_pld *vdm_pld, + struct msg_info *info) +{ + struct msg_pkt_header *header; + + header = (struct msg_pkt_header *)info->rsp_packet; + if (header->msgetah.plen > vdm_pld->rsp_buf_len) { + ub_err(uent, "rsp msg len error, plen=%u, buf_len=%u\n", + header->msgetah.plen, vdm_pld->rsp_buf_len); + return -ENOMEM; + } + + if (header->msgetah.rsp_status) { + ub_err(uent, " vdm rsp msg status error, status=%u\n", + header->msgetah.rsp_status); + return -EINVAL; + } + + vdm_pld->rsp_pld_len = header->msgetah.plen; + if (!vdm_pld->rsp_pld) { + if (!vdm_pld->rsp_pld_len) + return 0; + + ub_err(uent, "rsp_pld is NULL\n"); + return -EINVAL; + } + + memcpy(vdm_pld->rsp_pld, info->rsp_packet + MSG_PKT_HEADER_SIZE, + vdm_pld->rsp_pld_len); + + return 0; +} + +int ub_vdm_message(struct ub_entity *uent, struct ub_vdm_pld *vdm_pld) +{ + struct msg_pkt_header *header; + struct msg_info info = {}; + void *req_pkt; + void *rsp_pkt; + u32 pkt_size; + int ret; + u8 code; + + if (!uent || !uent->message || !uent->message->mdev || !vdm_pld || + !vdm_pld->req_pld || !vdm_pld->req_pld_len) { + pr_err("input data error\n"); + return -EINVAL; + } + + if (vdm_pld->req_pld_len > SZ_1K || + MSG_PKT_HEADER_SIZE + vdm_pld->rsp_buf_len > U16_MAX) { + ub_err(uent, "input vdm msg too long\n"); + return -EINVAL; + } + + req_pkt = kzalloc(MSG_PKT_HEADER_SIZE + vdm_pld->req_pld_len, GFP_KERNEL); + if (!req_pkt) + return -ENOMEM; + + rsp_pkt = kzalloc(MSG_PKT_HEADER_SIZE + vdm_pld->rsp_buf_len, GFP_KERNEL); + if (!rsp_pkt) { + kfree(req_pkt); + return -ENOMEM; + } + + code = code_gen(UB_MSG_CODE_VDM, vdm_pld->sub_msg_code, MSG_REQ); + header = (struct msg_pkt_header *)req_pkt; + pkt_size = msg_size_gen((vdm_pld->req_pld_len + MSG_PKT_HEADER_SIZE), + (vdm_pld->rsp_buf_len + MSG_PKT_HEADER_SIZE)); + ub_msg_pkt_header_init(header, uent, vdm_pld->req_pld_len, code, false); + memcpy(req_pkt + MSG_PKT_HEADER_SIZE, vdm_pld->req_pld, + vdm_pld->req_pld_len); + message_info_init(&info, uent, req_pkt, rsp_pkt, pkt_size); + ret = message_sync_request(uent->message->mdev, &info, code); + if (ret) { + ub_err(uent, "message sync execute error, ret=%d\n", ret); + goto out; + } + + ret = handle_vdm_rsp_msg(uent, vdm_pld, &info); + +out: + kfree(req_pkt); + kfree(rsp_pkt); + + return ret; +} +EXPORT_SYMBOL_GPL(ub_vdm_message); diff --git a/drivers/ub/ubus/msg.h b/drivers/ub/ubus/msg.h index 858782c2fead..5d520db132d5 100644 --- a/drivers/ub/ubus/msg.h +++ b/drivers/ub/ubus/msg.h @@ -196,6 +196,7 @@ typedef void (*rx_msg_handler_t)(struct ub_bus_controller *ubc, void *pkt, u16 l * @send: send message to target ub_entity but not wait response * @response: send response message to target * @sync_enum: send enum message to target ub_entity and wait response + * @vdm_rx_handler: send vdm response message to target * @owner: Driver module providing these ops */ struct message_ops { @@ -209,6 +210,7 @@ struct message_ops { u8 code); int (*sync_enum)(struct message_device *mdev, struct msg_info *info, u8 cmd); + rx_msg_handler_t vdm_rx_handler; struct module *owner; }; diff --git a/drivers/ub/ubus/vendor/hisilicon/Makefile b/drivers/ub/ubus/vendor/hisilicon/Makefile index f7d163189acf..1d3b4c35a670 100644 --- a/drivers/ub/ubus/vendor/hisilicon/Makefile +++ b/drivers/ub/ubus/vendor/hisilicon/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0+ -hisi_ubus-objs := hisi-ubus.o controller.o +hisi_ubus-objs := hisi-ubus.o controller.o vdm.o obj-$(CONFIG_UB_HISI_UBUS) += hisi_ubus.o diff --git a/drivers/ub/ubus/vendor/hisilicon/vdm.c b/drivers/ub/ubus/vendor/hisilicon/vdm.c new file mode 100644 index 000000000000..de50453c2381 --- /dev/null +++ b/drivers/ub/ubus/vendor/hisilicon/vdm.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus hisi vdm: " fmt + +#include "../../ubus.h" +#include "../../msg.h" +#include "../../pool.h" +#include "hisi-ubus.h" +#include "vdm.h" + +struct opcode_func_map { + u16 sub_opcode; + u16 idev_pkt_size; + char print_info[16]; + u8 (*idev_handler)(struct ub_bus_controller *ubc, struct vdm_msg_pkt *pkt); + int idev_pld_size; +}; + +static void ub_vdm_msg_rsp(struct ub_bus_controller *ubc, + struct vdm_msg_pkt *pkt, u8 status) +{ + struct msg_pkt_header *header = &pkt->header; + struct msg_info info = {}; + bool local; + u32 size; + int ret; + + size = (MSG_PKT_HEADER_SIZE + VENDOR_GUID_PLD_SIZE + + VDM_MSG_DW0_PLD_SIZE) << MSG_REQ_SIZE_OFFSET; + + local = ub_rsp_msg_init(header, status, + VENDOR_GUID_PLD_SIZE + VDM_MSG_DW0_PLD_SIZE); + + message_info_init(&info, local ? ubc->uent : NULL, pkt, NULL, size); + ret = message_response(ubc->mdev, &info, header->msgetah.code); + if (ret) + dev_err(&ubc->dev, "send vdm response message failed, ret=%d\n", ret); +} + +struct opcode_func_map idev_func_mapping[] = {}; + +static int ub_vdm_msg_info_handle(struct ub_bus_controller *ubc, + struct vdm_msg_pkt *pkt, u16 len, + u16 sub_opcode) +{ + int idx; + + for (idx = 0; idx < ARRAY_SIZE(idev_func_mapping); idx++) { + if (sub_opcode == idev_func_mapping[idx].sub_opcode) { + if (len < idev_func_mapping[idx].idev_pkt_size) { + dev_err(&ubc->dev, + "Message length[%#x] is wrong\n", len); + return UB_MSG_RSP_EXEC_EINVAL; + } + (void)idev_func_mapping[idx].idev_handler(ubc, pkt); + return UB_MSG_RSP_SUCCESS; + } + } + + dev_err(&ubc->dev, "vdm sub opvode is invalid, sub_opcode = %#x\n", sub_opcode); + return UB_MSG_RSP_EXEC_EINVAL; +} + +static int hi_vdm_vendor_handler(struct ub_bus_controller *ubc, void *pkt, u16 len) +{ + struct vdm_msg_pkt *vdm_pkt = (struct vdm_msg_pkt *)pkt; + struct msg_pkt_dw0 *pld_dw0 = &vdm_pkt->pld_dw0; + u16 sub_opcode; + u8 opcode; + + if (len < (MSG_PKT_HEADER_SIZE + VENDOR_GUID_PLD_SIZE + + VDM_MSG_DW0_PLD_SIZE)) { + dev_err(&ubc->dev, "vdm msg len[%#x] is wrong\n", len); + return UB_MSG_RSP_EXEC_EINVAL; + } + + opcode = pld_dw0->opcode; + sub_opcode = pld_dw0->sub_opcode; + switch (opcode) { + case VDM_OPCODE_FM2UB_COMM_MSG: + return ub_vdm_msg_info_handle(ubc, vdm_pkt, len, sub_opcode); + default: + dev_err(&ubc->dev, "vdm opcode type [%u] not support\n", + opcode); + } + + return UB_MSG_RSP_EXEC_EINVAL; +} + +void hi_vdm_rx_msg_handler(struct ub_bus_controller *ubc, void *pkt, u16 len) +{ + struct msg_pkt_header *header = (struct msg_pkt_header *)pkt; + u8 sub_msg_code = header->msgetah.sub_msg_code; + u8 status; + + switch (sub_msg_code) { + case UB_VENDOR_MSG: + status = hi_vdm_vendor_handler(ubc, pkt, len); + break; + default: + dev_err(&ubc->dev, "vdm sub msg code[%#x] not support\n", + sub_msg_code); + status = UB_MSG_RSP_EXEC_EINVAL; + } + + if (status) + ub_vdm_msg_rsp(ubc, (struct vdm_msg_pkt *)pkt, status); +} diff --git a/drivers/ub/ubus/vendor/hisilicon/vdm.h b/drivers/ub/ubus/vendor/hisilicon/vdm.h new file mode 100644 index 000000000000..183449aa082e --- /dev/null +++ b/drivers/ub/ubus/vendor/hisilicon/vdm.h @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __VDM_H__ +#define __VDM_H__ + +/* VDM message opcode */ +enum vdm_opcode { + VDM_OPCODE_FM2UB_COMM_MSG = 0, /* FM To UB node Communication Message */ + VDM_OPCODE_UB2FM_COMM_MSG = 0x1, /* UB node To FM Communication Message */ + VDM_OPCODE_UB2UB_COMM_MSG = 0x2, /* UB node To UB node Communication Message */ + VDM_OPCODE_FW_COMM_MSG = 0x3, /* Firmware Communication Message */ +}; + +enum vdm_fm2ub_sub_opcode { + VDM_SUB_OPCODE_MUE_REG = 0x1, + VDM_SUB_OPCODE_MUE_RLS = 0x2, + VDM_SUB_OPCODE_UE_REG = 0x3, + VDM_SUB_OPCODE_UE_RLS = 0x4, + VDM_BI_CREATE_BYPASS_UMMU = 0x5, +}; + +enum vdm_ub2fm_sub_opcode { + VDM_SUB_OPCODE_ENTITY_ENABLE = 0x1, +}; + +struct msg_pkt_dw0 { + /* DW0 */ + u32 rsvd0 : 16; + u32 opcode : 6; + u32 sub_opcode : 10; +}; +#define VDM_MSG_DW0_PLD_SIZE 4 + +/* send entity enable msg payload */ +struct entity_enable_pld { + /* DW1 */ + u32 entity_type : 1; + u32 enable : 1; + u32 rsvd1 : 30; + /* DW2~DW5 */ + u32 eid[4]; + /* DW6~DW9 */ + u32 guid[UB_GUID_DW_NUM]; + /* DW10 */ + u16 entity_idx; + u16 rsvd2; +}; +#define ENTITY_ENABLE_PLD_SIZE 52 + +#define ENTITY_ENABLE_MSG_PKT_SIZE (MSG_PKT_HEADER_SIZE + ENTITY_ENABLE_PLD_SIZE) + +enum entity_type { + ENTITY_TYPE_IDEV = 0, + ENTITY_TYPE_DEV = 1, +}; + +struct idev_pue_reg_pld { + /* DW1~DW4 */ + u32 guid[UB_GUID_DW_NUM]; + /* DW5 */ + u16 pue_entity_idx; + u16 ue_cnt; + /* DW6 */ + u16 start_ue_entity_idx; + u16 end_ue_entity_idx; + /* DW7~DW10 */ + u32 user_eid[4]; +}; +#define IDEV_MUE_REG_PLD_TOTAL_SIZE 52 + +struct idev_pue_rls_pld { + /* DW1~DW4 */ + u32 guid[UB_GUID_DW_NUM]; + /* DW5 */ + u16 pue_entity_idx; + u16 rls_reason; +}; +#define IDEV_MUE_RLS_PLD_TOTAL_SIZE 32 + +struct idev_ue_reg_pld { + /* DW1~DW4 */ + u32 guid[UB_GUID_DW_NUM]; + /* DW5 */ + u16 pue_entity_idx; + u16 ue_entity_idx; + /* DW6~DW9 */ + u32 user_eid[4]; +}; +#define IDEV_UE_REG_PLD_TOTAL_SIZE 48 + +struct idev_ue_rls_pld { + /* DW1~DW4 */ + u32 guid[UB_GUID_DW_NUM]; + /* DW5 */ + u16 pue_entity_idx; + u16 ue_entity_idx; + /* DW6 */ + u16 rls_reason; + u16 rsvd1; +}; +#define IDEV_UE_RLS_PLD_TOTAL_SIZE 36 + +#define MSG_IDEV_MUE_REG_SIZE \ + (MSG_PKT_HEADER_SIZE + IDEV_MUE_REG_PLD_TOTAL_SIZE) +#define MSG_IDEV_MUE_RLS_SIZE \ + (MSG_PKT_HEADER_SIZE + IDEV_MUE_RLS_PLD_TOTAL_SIZE) +#define MSG_IDEV_UE_REG_SIZE \ + (MSG_PKT_HEADER_SIZE + IDEV_UE_REG_PLD_TOTAL_SIZE) +#define MSG_IDEV_UE_RLS_SIZE \ + (MSG_PKT_HEADER_SIZE + IDEV_UE_RLS_PLD_TOTAL_SIZE) + +#define VENDOR_GUID_PLD_SIZE 8 + +struct vdm_msg_pkt { + struct msg_pkt_header header; + u64 guid_high; + struct msg_pkt_dw0 pld_dw0; + union { + struct entity_enable_pld enable_pld; + struct idev_pue_reg_pld pd_reg_pld; + struct idev_pue_rls_pld pd_rls_pld; + struct idev_ue_reg_pld vd_reg_pld; + struct idev_ue_rls_pld vd_rls_pld; + }; +}; + +void hi_vdm_rx_msg_handler(struct ub_bus_controller *ubc, void *pkt, u16 len); + +#endif /* __VDM_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 6da027092df1..d1f55e8d7440 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -444,6 +444,15 @@ struct ub_share_port_ops { void (*event_notify)(struct ub_entity *uent, u16 port_id, int event); }; +struct ub_vdm_pld { + void *req_pld; + u16 req_pld_len; + void *rsp_pld; + u16 rsp_pld_len; + u16 rsp_buf_len; + u8 sub_msg_code; +}; + #ifdef CONFIG_UB_UBUS extern struct bus_type ub_bus_type; #define dev_is_ub(d) ((d)->bus == &ub_bus_type) @@ -708,6 +717,20 @@ int ub_reset_entity(struct ub_entity *ent); */ int ub_device_reset(struct ub_entity *ent); +/** + * ub_vdm_message() - Send vendor private message. + * @uent: UB entity. + * @vdm_pld: Vendor private message payload context. + * + * Send a vendor private message to the entity. Response will put in + * vdm_pld->rsp_pld, and will fill in vdm->rsp_pld_len. + * + * Context: Any context, It will take spin_lock_irqsave()/spin_unlock_restore() + * Return: 0 if success, or %-EINVAL if parameters invalid, + * or %-ENOMEM if system out of memory, or other failed negative values. + */ +int ub_vdm_message(struct ub_entity *uent, struct ub_vdm_pld *vdm_pld); + /* Only for ubus module */ unsigned int ub_irq_calc_affinity_vectors(unsigned int minvec, unsigned int maxvec, @@ -1038,6 +1061,9 @@ static inline int ub_reset_entity(struct ub_entity *uent) { return -ENODEV; } static inline int ub_device_reset(struct ub_entity *uent) { return -ENODEV; } +static inline int ub_vdm_message(struct ub_entity *uent, + struct ub_vdm_pld *vdm_pld) +{ return -ENODEV; } static inline unsigned int ub_irq_calc_affinity_vectors(unsigned int minvec, unsigned int maxvec, const struct irq_affinity *affd) -- Gitee From 7bbd32643d1ac084619c3ce0ea4cd004ab13d692 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Mon, 22 Sep 2025 21:20:54 +0800 Subject: [PATCH 081/214] ub:hisi-ubus: Support UBUS vdm pue/ue register and release ANBZ: #45460 commit 50dfaf0810e3591f386add71324ff1b12f62f633 openEuler. Support UBUS vdm physical device and virtual device register and release. Send response to MAMI when finished Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/sysfs.c | 4 +- drivers/ub/ubus/vendor/hisilicon/vdm.c | 362 ++++++++++++++++++++++++- include/ub/ubus/ubus.h | 1 + 3 files changed, 364 insertions(+), 3 deletions(-) diff --git a/drivers/ub/ubus/sysfs.c b/drivers/ub/ubus/sysfs.c index c4899152ba6f..a583cf7efa97 100644 --- a/drivers/ub/ubus/sysfs.c +++ b/drivers/ub/ubus/sysfs.c @@ -695,7 +695,7 @@ static int ub_create_capabilities_sysfs(struct ub_entity *uent) CREATE_CONDITIONS(uent->entity_idx == 0 && !is_ibus_controller(uent)) }, { &dev_attr_ub_total_entities, CREATE_CONDITIONS(uent->entity_idx == 0) }, { &dev_attr_ub_totalues, CREATE_CONDITIONS(uent->is_mue) }, - { &dev_attr_ub_numues, CREATE_CONDITIONS(uent->is_mue) }, + { &dev_attr_ub_numues, CREATE_CONDITIONS(uent->is_mue && !uent->is_vdm_idev) }, { &dev_attr_sw_cap, CREATE_CONDITIONS(is_ibus_controller(uent)) }, { &dev_attr_ub_release_ue, CREATE_CONDITIONS(uent->is_mue && entity_flex_en) }, { &dev_attr_mue_list, CREATE_CONDITIONS(uent->entity_idx == 0) }, @@ -727,7 +727,7 @@ static void ub_remove_capabilities_sysfs(struct ub_entity *uent) CREATE_CONDITIONS(uent->entity_idx == 0 && !is_ibus_controller(uent)) }, { &dev_attr_ub_total_entities, CREATE_CONDITIONS(uent->entity_idx == 0) }, { &dev_attr_ub_totalues, CREATE_CONDITIONS(uent->is_mue) }, - { &dev_attr_ub_numues, CREATE_CONDITIONS(uent->is_mue) }, + { &dev_attr_ub_numues, CREATE_CONDITIONS(uent->is_mue && !uent->is_vdm_idev) }, { &dev_attr_sw_cap, CREATE_CONDITIONS(is_ibus_controller(uent)) }, { &dev_attr_ub_release_ue, CREATE_CONDITIONS(uent->is_mue && entity_flex_en) }, { &dev_attr_mue_list, CREATE_CONDITIONS(uent->entity_idx == 0) }, diff --git a/drivers/ub/ubus/vendor/hisilicon/vdm.c b/drivers/ub/ubus/vendor/hisilicon/vdm.c index de50453c2381..d3ea3ebe3301 100644 --- a/drivers/ub/ubus/vendor/hisilicon/vdm.c +++ b/drivers/ub/ubus/vendor/hisilicon/vdm.c @@ -8,9 +8,13 @@ #include "../../ubus.h" #include "../../msg.h" #include "../../pool.h" +#include "../../instance.h" +#include "../../ubus_entity.h" #include "hisi-ubus.h" #include "vdm.h" +static DEFINE_SPINLOCK(ub_vdm_lock); + struct opcode_func_map { u16 sub_opcode; u16 idev_pkt_size; @@ -19,6 +23,56 @@ struct opcode_func_map { int idev_pld_size; }; +/** + * inner_ub_get_ent_by_guid - search for device by guid in the idev message + * + * @guid: guid in idev message + * @dev_list: &ubc->devs + */ +static struct ub_entity *inner_ub_get_ent_by_guid(guid_t *guid, + struct list_head *dev_list) +{ + struct ub_entity *uent; + + list_for_each_entry(uent, dev_list, node) + if (guid_equal(guid, &uent->guid.id)) + return uent; + + return NULL; +} + +/* + * ub_get_pue - search for physical device by guid and mue_idx + * + * First search for entity0 in ubc->dev_list by guid, + * then search for mue_idx in entity0->mue_list by mue_idx + */ +static struct ub_entity *ub_get_pue(guid_t *guid, u16 mue_idx, + struct list_head *dev_list) +{ + struct ub_entity *uent = inner_ub_get_ent_by_guid(guid, dev_list); + struct ub_entity *pue; + + if (!uent) { + pr_err("find uent with the guid failed\n"); + return NULL; + } + + if (is_primary(uent) && mue_idx == 0) { + ub_info(uent, + "This uent is primary dev, return uent directly\n"); + return uent; + } + + list_for_each_entry(pue, &uent->mue_list, node) { + ub_info(pue, "the pue num=%#x\n", pue->uent_num); + if (pue->entity_idx == mue_idx) + return pue; + } + + return NULL; +} + static void ub_vdm_msg_rsp(struct ub_bus_controller *ubc, struct vdm_msg_pkt *pkt, u8 status) { @@ -40,7 +94,313 @@ static void ub_vdm_msg_rsp(struct ub_bus_controller *ubc, dev_err(&ubc->dev, "send vdm response message failed, ret=%d\n", ret); } -struct opcode_func_map idev_func_mapping[] = {}; +/** + * ub_idevice_enable_handle - physical/virtual device entity enable handle + * + * @pue: physical device + * @idx: index of device entity + * @is_mue: whether is physical device. 1 for yes, 0 for no + * @map: the mapping of virtual devices + * @alloc_dev: a temporary variable used to receive allocated dev and return the information + */ +static int ub_idevice_enable_handle(struct ub_entity *pue, u16 idx, u8 is_mue, + struct ue_map *map, + struct ub_entity **alloc_dev) +{ + struct ub_entity *dev, *uent; + int ret; + + list_for_each_entry(uent, &pue->mue_list, node) + if (uent->entity_idx == idx) + return -EEXIST; + + list_for_each_entry(uent, &pue->ue_list, node) + if (uent->entity_idx == idx) + return -EEXIST; + + dev = ub_alloc_ent(); + if (!dev) + return -ENOMEM; + + dev->pool = true; + dev->entity_idx = idx; + dev->pue = pue; + dev->dev.parent = &pue->dev; + dev->ubc = pue->ubc; + dev->cna = pue->cna; + dev->upi = pue->upi; + + if (is_mue) { + dev->is_mue = is_mue; + dev->uem.start_entity_idx = map->start_entity_idx; + dev->uem.end_entity_idx = map->end_entity_idx; + dev->total_ues = map->end_entity_idx - map->start_entity_idx + 1; + dev->is_vdm_idev = 1; + } + + ret = ub_setup_ent(dev); + if (ret < 0) { + kfree(dev); + return ret; + } + + ub_entity_get(pue); + ub_entity_add(dev, pue); + + *alloc_dev = dev; + + return 0; +} + +static int check_pld_ueid_valid(u32 user_eid) +{ + struct ub_bus_instance *bi; + + bi = ub_find_bus_instance(eid_match, &user_eid); + if (!bi) { + pr_err("vdm payload user eid is invalid, user_eid = 0x%x\n", + user_eid); + return -EINVAL; + } + + ub_bus_instance_put(bi); + + return 0; +} + +/** + * ub_idevice_pue_add_handler - add mue to the bus + * + * @ubc: ub bus controller + * @pkt: idev pue message packet, contains header and payload + */ +static u8 ub_idevice_pue_add_handler(struct ub_bus_controller *ubc, struct vdm_msg_pkt *pkt) +{ + struct idev_pue_reg_pld *pld = &pkt->pd_reg_pld; + struct ub_entity *pue, *alloc_dev = NULL; + struct ue_map map = {}; + u8 status; + int ret; + + /* search corresponding device by guid in message payload */ + pue = inner_ub_get_ent_by_guid((guid_t *)pld->guid, &ubc->devs); + if (!pue) { + dev_err(&ubc->dev, "find uent by guid in pue reg func failed\n"); + status = UB_MSG_RSP_EXEC_ENODEV; + goto pue_reg_rsp; + } + + map.start_entity_idx = pld->start_ue_entity_idx; + map.end_entity_idx = pld->end_ue_entity_idx; + + if (!pld->ue_cnt) { + map.start_entity_idx = pue->entity_idx; + map.end_entity_idx = pue->entity_idx; + } else if (pld->ue_cnt != map.end_entity_idx - map.start_entity_idx + 1) { + dev_err(&ubc->dev, "Invalid ue cnt: [%u]\n" + "The ue cnt must be equal to end: [%d] - start: [%d] + 1\n", + pld->ue_cnt, map.end_entity_idx, map.start_entity_idx); + status = UB_MSG_RSP_EXEC_EINVAL; + goto pue_reg_rsp; + } + + ret = check_pld_ueid_valid(pld->user_eid[0]); + if (ret) { + status = UB_MSG_RSP_EXEC_EINVAL; + goto pue_reg_rsp; + } + + /* enable pue */ + ret = ub_idevice_enable_handle(pue, pld->pue_entity_idx, 1, &map, + &alloc_dev); + if (ret == 0) { + alloc_dev->user_eid = pld->user_eid[0]; + ub_info(pue, "enable idev pue succeeded, user_eid=0x%x\n", + pld->user_eid[0]); + status = UB_MSG_RSP_SUCCESS; + } else if (ret == -EEXIST) { + ub_err(pue, "The pue idx[%u] is already exist\n", + pld->pue_entity_idx); + status = UB_MSG_RSP_EXEC_EEXIST; + } else { + ub_err(pue, "enable idev pue failed\n"); + status = UB_MSG_RSP_EXEC_ENOEXEC; + } + +pue_reg_rsp: + ub_vdm_msg_rsp(ubc, pkt, status); + if (status == UB_MSG_RSP_SUCCESS) + ub_start_ent(alloc_dev); + + return status; +} + +/** + * ub_idevice_pue_rls_handler - send message to the FM to release the idev_pue + * + * @ubc: ub_bus_controller + * @pkt: message packet + */ +static u8 ub_idevice_pue_rls_handler(struct ub_bus_controller *ubc, struct vdm_msg_pkt *pkt) +{ + struct idev_pue_rls_pld *pld = &pkt->pd_rls_pld; + struct ub_entity *uent; + u8 status; + + /* search device by guid and pue entity idx in message payload */ + uent = ub_get_pue((guid_t *)pld->guid, pld->pue_entity_idx, &ubc->devs); + if (!uent) { + dev_err(&ubc->dev, "find pue failed, pue entity_idx in mue rls pkt=%u\n", + pld->pue_entity_idx); + status = UB_MSG_RSP_EXEC_ENODEV; + } else { + status = UB_MSG_RSP_SUCCESS; + } + + if (status == UB_MSG_RSP_SUCCESS) + ub_disable_ent(uent); + + ub_vdm_msg_rsp(ubc, pkt, status); + return status; +} + +/* + * ub_idevice_ue_add_handler - add ue to the bus + */ +static u8 ub_idevice_ue_add_handler(struct ub_bus_controller *ubc, struct vdm_msg_pkt *pkt) +{ + struct idev_ue_reg_pld *pld = &pkt->vd_reg_pld; + struct ub_entity *pue, *alloc_dev = NULL; + u16 ue_entity_idx = pld->ue_entity_idx; + int start_idx, end_idx, ret; + u8 status; + + /* check whether pue is registered. */ + pue = ub_get_pue((guid_t *)pld->guid, pld->pue_entity_idx, &ubc->devs); + if (!pue) { + dev_err(&ubc->dev, "find pue failed, pue entity_idx in ue reg pkt=%u\n", + pld->pue_entity_idx); + status = UB_MSG_RSP_EXEC_ENODEV; + goto ue_reg_rsp; + } + + if (pue->is_vdm_idev) { + start_idx = pue->uem.start_entity_idx; + end_idx = pue->uem.end_entity_idx; + if (ue_entity_idx < start_idx || ue_entity_idx > end_idx) { + ub_err(pue, + "invalid ue entity_idx=%u, start_idx=%d, end_idx=%d\n", + ue_entity_idx, start_idx, end_idx); + status = UB_MSG_RSP_EXEC_EINVAL; + goto ue_reg_rsp; + } + } else { + ub_info(pue, + "The pue of this vdm ue to be enabled is normal\n"); + } + + ret = check_pld_ueid_valid(pld->user_eid[0]); + if (ret) { + status = UB_MSG_RSP_EXEC_EINVAL; + goto ue_reg_rsp; + } + + spin_lock(&ub_vdm_lock); + ret = ub_idevice_enable_handle(pue, ue_entity_idx, 0, NULL, &alloc_dev); + spin_unlock(&ub_vdm_lock); + + if (ret == 0) { + alloc_dev->user_eid = pld->user_eid[0]; + ub_info(pue, "enable idev ue succeeded, user_eid=0x%x\n", + pld->user_eid[0]); + status = UB_MSG_RSP_SUCCESS; + } else if (ret == -EEXIST) { + ub_err(pue, "The ue idx[%u] is already exist\n", + ue_entity_idx); + status = UB_MSG_RSP_EXEC_EEXIST; + } else { + ub_err(pue, "enable idev ue failed\n"); + status = UB_MSG_RSP_EXEC_ENOEXEC; + } + +ue_reg_rsp: + ub_vdm_msg_rsp(ubc, pkt, status); + if (status == UB_MSG_RSP_SUCCESS) { + ub_start_ent(alloc_dev); + pue->num_ues += 1; + } + + return status; +} + +/** + * ub_idevice_ue_rls_handler - send message to the FM to release the idev_pue + * + * @ubc: ub_bus_controller + * @pkt: message packet + */ +static u8 ub_idevice_ue_rls_handler(struct ub_bus_controller *ubc, struct vdm_msg_pkt *pkt) +{ + struct idev_ue_rls_pld *pld = &pkt->vd_rls_pld; + struct ub_entity *pue, *vd_dev, *tmp; + u16 ue_entity_idx = pld->ue_entity_idx; + u16 start_idx, end_idx; + u8 status; + + /* search for pue with guid. Return an error if pue does not exist */ + pue = ub_get_pue((guid_t *)pld->guid, pld->pue_entity_idx, &ubc->devs); + if (!pue) { + dev_err(&ubc->dev, "find pue failed, pue entity_idx in ue rls pkt=%u\n", + pld->pue_entity_idx); + status = UB_MSG_RSP_EXEC_ENODEV; + goto ue_rls_rsp; + } + + if (pue->is_vdm_idev) { + start_idx = pue->uem.start_entity_idx; + end_idx = pue->uem.end_entity_idx; + if (ue_entity_idx < start_idx || ue_entity_idx > end_idx) { + ub_err(pue, + "invalid ue entity_idx=%u, start_idx=%u, end_idx=%u\n", + ue_entity_idx, start_idx, end_idx); + status = UB_MSG_RSP_EXEC_EINVAL; + goto ue_rls_rsp; + } + } else { + ub_info(pue, + "The pue of this vdm ue to be disabled is normal\n"); + } + + status = UB_MSG_RSP_EXEC_ENODEV; + /* otherwise, delete this ue with ue idx in message payload */ + list_for_each_entry_safe(vd_dev, tmp, &pue->ue_list, node) + if (ue_entity_idx == vd_dev->entity_idx) { + status = UB_MSG_RSP_SUCCESS; + break; + } + if (status == UB_MSG_RSP_EXEC_ENODEV) + ub_err(pue, "find vd_dev with entity_idx %u failed\n", ue_entity_idx); + +ue_rls_rsp: + ub_vdm_msg_rsp(ubc, pkt, status); + if (status == UB_MSG_RSP_SUCCESS) { + ub_disable_ent(vd_dev); + pue->num_ues -= 1; + } + + return status; +} + +struct opcode_func_map idev_func_mapping[] = { + { VDM_SUB_OPCODE_MUE_REG, MSG_IDEV_MUE_REG_SIZE, "MUE register", + ub_idevice_pue_add_handler, IDEV_MUE_REG_PLD_TOTAL_SIZE }, + { VDM_SUB_OPCODE_MUE_RLS, MSG_IDEV_MUE_RLS_SIZE, "MUE release", + ub_idevice_pue_rls_handler, IDEV_MUE_RLS_PLD_TOTAL_SIZE }, + { VDM_SUB_OPCODE_UE_REG, MSG_IDEV_UE_REG_SIZE, "UE register", + ub_idevice_ue_add_handler, IDEV_UE_REG_PLD_TOTAL_SIZE }, + { VDM_SUB_OPCODE_UE_RLS, MSG_IDEV_UE_RLS_SIZE, "UE release", + ub_idevice_ue_rls_handler, IDEV_UE_RLS_PLD_TOTAL_SIZE }, +}; static int ub_vdm_msg_info_handle(struct ub_bus_controller *ubc, struct vdm_msg_pkt *pkt, u16 len, diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index d1f55e8d7440..728c592ba325 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -197,6 +197,7 @@ struct ub_entity { struct ue_map uem; struct list_head mue_list; /* management ub entity list */ struct list_head ue_list; /* entity list in management ub entity */ + u8 is_vdm_idev; /* entity topology info */ struct list_head node; -- Gitee From 75c2b38aef6e1ca636dfd90c3d80865dc4b62714 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Mon, 22 Sep 2025 21:29:05 +0800 Subject: [PATCH 082/214] ub:hisi-ubus: Support UBUS local RAS interfaces ANBZ: #45460 commit 789e8b975d32f7ddd2806eb3de77b949bc6feb8a openEuler. Support UBUS local Reliability, Availability and Serviceability interfaces.Print, handle and recover ubus ras. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/ubus.h | 2 + drivers/ub/ubus/ubus_driver.c | 12 + drivers/ub/ubus/vendor/hisilicon/Makefile | 2 +- drivers/ub/ubus/vendor/hisilicon/hisi-ubus.c | 3 + drivers/ub/ubus/vendor/hisilicon/local-ras.c | 341 +++++++++++++++++++ drivers/ub/ubus/vendor/hisilicon/local-ras.h | 73 ++++ 6 files changed, 432 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/vendor/hisilicon/local-ras.c create mode 100644 drivers/ub/ubus/vendor/hisilicon/local-ras.h diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index 1367d8e87d9a..89c6e1f179ff 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -64,6 +64,8 @@ struct ub_manage_subsystem_ops { u32 vendor; int (*controller_probe)(struct ub_bus_controller *ubc); void (*controller_remove)(struct ub_bus_controller *ubc); + int (*ras_handler_probe)(void); + void (*ras_handler_remove)(void); }; int register_ub_manage_subsystem_ops(const struct ub_manage_subsystem_ops *ops); void unregister_ub_manage_subsystem_ops(const struct ub_manage_subsystem_ops *ops); diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index 93c1dc5d662a..b4c6f0d3b760 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -690,6 +690,13 @@ int ub_host_probe(void) if (ret) goto cdev_fail; + if (!manage_subsystem_ops || !manage_subsystem_ops->ras_handler_probe) + goto error_register_fail; + + ret = manage_subsystem_ops->ras_handler_probe(); + if (ret) + goto error_register_fail; + ret = message_rx_init(); if (ret) goto message_init_fail; @@ -697,6 +704,9 @@ int ub_host_probe(void) return 0; message_init_fail: + if (manage_subsystem_ops && manage_subsystem_ops->ras_handler_remove) + manage_subsystem_ops->ras_handler_remove(); +error_register_fail: ub_cdev_uninit(); cdev_fail: ub_services_exit(); @@ -719,6 +729,8 @@ EXPORT_SYMBOL_GPL(ub_host_probe); void ub_host_remove(void) { message_rx_uninit(); + if (manage_subsystem_ops && manage_subsystem_ops->ras_handler_remove) + manage_subsystem_ops->ras_handler_remove(); ub_cdev_uninit(); ub_services_exit(); bus_unregister(&ub_service_bus_type); diff --git a/drivers/ub/ubus/vendor/hisilicon/Makefile b/drivers/ub/ubus/vendor/hisilicon/Makefile index 1d3b4c35a670..f4191cc288bf 100644 --- a/drivers/ub/ubus/vendor/hisilicon/Makefile +++ b/drivers/ub/ubus/vendor/hisilicon/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0+ -hisi_ubus-objs := hisi-ubus.o controller.o vdm.o +hisi_ubus-objs := hisi-ubus.o controller.o vdm.o local-ras.o obj-$(CONFIG_UB_HISI_UBUS) += hisi_ubus.o diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.c b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.c index f3e9c3097ffb..54b1f2f0c582 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.c +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.c @@ -7,6 +7,7 @@ #include #include "../../ubus.h" +#include "local-ras.h" #include "hisi-ubus.h" #define HISI_VENDOR_ID 0xCC08 @@ -16,6 +17,8 @@ static const struct ub_manage_subsystem_ops hisi_ub_manage_subsystem_ops = { .vendor = HISI_VENDOR_ID, .controller_probe = ub_bus_controller_probe, .controller_remove = ub_bus_controller_remove, + .ras_handler_probe = ub_ras_handler_probe, + .ras_handler_remove = ub_ras_handler_remove }; static const struct of_device_id hisi_ubus_of_match[] = { diff --git a/drivers/ub/ubus/vendor/hisilicon/local-ras.c b/drivers/ub/ubus/vendor/hisilicon/local-ras.c new file mode 100644 index 000000000000..9dc016555732 --- /dev/null +++ b/drivers/ub/ubus/vendor/hisilicon/local-ras.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus hisi local_ras: " fmt + +#include +#include "../../ubus.h" +#include "../../ubus_driver.h" +#include "../../reset.h" +#include "local-ras.h" + +struct sub_module_info { + u32 sub_module_id; + const char *sub_module_name; +}; + +static struct sub_module_info hisi_ubus_sub_module[] = { + {0x2, "MISC"}, + {0x3, "BA"}, + {0x4, "NL PORT"}, + {0x5, "NL DEVICE"}, + {0x6, "DLMAC"}, + {0x7, "MUXPCS"}, + {0x1D, "ETH"}, + {0x20, "TP_DAM"}, + {0x21, "TP_EUM"}, + {0x22, "TP_LRB"}, + {0x23, "TP_PPP"}, + {0x24, "TP_RQM"}, + {0x25, "TP_RXDMA_HEAD"}, + {0x26, "TP_RXP"}, + {0x27, "TP_SCC"}, + {0x28, "TP_TAI"}, + {0x29, "TP_TIMER"}, + {0x2a, "TP_TPCM"}, + {0x2b, "TP_TPGCM"}, + {0x2c, "TP_TPMM"}, + {0x2d, "TP_TPP"}, + {0x2e, "TP_TQEM"}, + {0x2f, "TP_TQS"}, + {0x30, "TP_UBOMMU"}, + {0x40, "TA_CQM"}, + {0x41, "TA_MRD"}, + {0x42, "TA_RSP"}, + {0x43, "TA_TM"}, + {0x44, "TA_TOM"}, + {0x45, "TA_TQC"}, + {0x46, "TA_TQEB"}, + {0x47, "TA_TQMS"}, + {0x48, "TA_USI"}, + {0x60, "ETH.CORE_TXMAC"}, + {0x61, "ETH.CORE_TDM"}, + {0x62, "ETH.CORE_TXPCS"}, + {0x63, "ETH.CORE_TXRSFEC"}, + {0x64, "ETH.CORE_MIB"}, + {0x65, "ETH.CORE_RXMAC"}, + {0x66, "ETH.CORE_RXPCS"}, + {0x67, "ETH.CORE_RXRSFEC"}, + {0x68, "ETH.CORE_RXPMACORE"}, + {0x69, "ETH.CORE_TXPMAL0"}, + {0x6a, "ETH.CORE_RXPMAL0"}, + {0x6b, "ETH.CORE_TXBRFEC"}, + {0x6c, "ETH.CORE_RXBRFEC"}, +}; + +static const char * const hisi_ubus_error_sev[] = { + [HISI_UBUS_ERR_SEV_RECOVERABLE] = "recoverable", + [HISI_UBUS_ERR_SEV_FATAL] = "fatal", + [HISI_UBUS_ERR_SEV_CORRECTED] = "corrected", + [HISI_UBUS_ERR_SEV_NONE] = "none", +}; + +struct ras_err_info { + u32 val_bit; + const char *err_msg; + u8 val; +}; + +static guid_t hisi_ubus_sec_guid = + GUID_INIT(0xC8B328A8, 0x9917, 0x4AF6, 0x9A, 0x13, 0x2E, + 0x08, 0xAB, 0x2E, 0x75, 0x86); + +static inline const char *hisi_ubus_get_string( + const char * const *array, size_t n, u32 id) +{ + return id < n && array[id] ? array[id] : "Unknown"; +} + +static const char *get_sub_module_name(u32 id) +{ + u8 i; + + for (i = 0; i < ARRAY_SIZE(hisi_ubus_sub_module); i++) { + if (hisi_ubus_sub_module[i].sub_module_id == id) + return hisi_ubus_sub_module[i].sub_module_name; + } + + return "Unknown"; +} + +#define HIP12_VERSION 8 +#define HIP12_SOC_ID 1 +#define ASCEND950_VERSION 2 +#define ASCEND950_SOC_ID 0x13 +static inline struct ub_bus_controller *find_bus_controller_by_errdata( + const struct hisi_ubus_error_data *edata) +{ + u32 ctl_no; + + /* only support on HIP12 and ASCEND950. */ + if (edata->version == HIP12_VERSION && edata->soc_id == HIP12_SOC_ID) + ctl_no = (u32)edata->socket_id; + else if (edata->version == ASCEND950_VERSION && edata->soc_id == ASCEND950_SOC_ID) + ctl_no = (u32)edata->socket_id; + else + return NULL; + + return ub_find_bus_controller(ctl_no); +} + +static void hisi_ubus_ras_print(struct ub_entity *uent, + const struct hisi_ubus_error_data *edata) +{ + struct ras_err_info ras_err_info[] = { + {HISI_UBUS_LOCAL_VALID_SOC_ID, + "SOC ID = %u\n", edata->soc_id}, + {HISI_UBUS_LOCAL_VALID_SOCKET_ID, + "Socket ID = %u\n", edata->socket_id}, + {HISI_UBUS_LOCAL_VALID_NIMBUS_ID, + "Nimbus ID = %u\n", edata->nimbus_id}, + {HISI_UBUS_LOCAL_VALID_CORE_ID, + "Core ID = core%u\n", edata->core_id}, + {HISI_UBUS_LOCAL_VALID_PORT_ID, + "Port ID = port%u\n", edata->port_id}, + }; + u8 i; + + ub_info(uent, "HISI : Ubus local RAS error\n"); + ub_info(uent, "Table version = %u\n", edata->version); + for (i = 0; i < ARRAY_SIZE(ras_err_info); i++) + if (edata->val_bits & ras_err_info[i].val_bit) + ub_info(uent, ras_err_info[i].err_msg, + ras_err_info[i].val); + + if (edata->val_bits & HISI_UBUS_LOCAL_VALID_SUB_MODULE_ID) + ub_info(uent, "Sub Module = %s\n", + get_sub_module_name((u32)edata->sub_module_id)); + + if (edata->val_bits & HISI_UBUS_LOCAL_VALID_ERR_SEVERITY) + ub_info(uent, "Error severity = %s\n", + hisi_ubus_get_string(hisi_ubus_error_sev, + ARRAY_SIZE(hisi_ubus_error_sev), + (u32)edata->err_severity)); + + if (edata->val_bits & HISI_UBUS_LOCAL_VALID_ERR_TYPE) + ub_info(uent, "Error type = %#x\n", edata->err_type); +} + +#define NL_PORT_MODULE_ID 0x4 +static inline bool is_nl_local_ras(u8 sub_module_id) +{ + return sub_module_id == NL_PORT_MODULE_ID; +} + +#define LQC_MOUDULE_ERR_BIT 7 +#define LQC_MOUDULE_ERR_MISC 1 +static inline bool is_nl_ssu_link_credi_overtime_err(const struct hisi_ubus_error_data *edata) +{ + if (DIV_ROUND_UP(edata->register_array_size, SZ_4) <= LQC_MOUDULE_ERR_MISC) + return false; + /* only supported device err */ + if (!!(edata->val_bits & HISI_UBUS_LOCAL_VALID_PORT_ID)) + return false; + + return !!(edata->err_misc[LQC_MOUDULE_ERR_MISC] & (1U << LQC_MOUDULE_ERR_BIT)); +} + +static bool ubus_need_recover(const struct hisi_ubus_error_data *edata) +{ + if (edata->err_severity != HISI_UBUS_ERR_SEV_RECOVERABLE) + return false; + + if (is_nl_local_ras(edata->sub_module_id)) + return is_nl_ssu_link_credi_overtime_err(edata); + + return !!(edata->val_bits & HISI_UBUS_LOCAL_VALID_PORT_ID); +} + +static int ubus_port_recover(struct ub_entity *uent, u16 port_id) +{ + if (port_id < uent->port_nums && uent->ports[port_id].type == PHYSICAL) + return ub_port_reset(uent, port_id); + + ub_info(uent, "port[%u] no need reset by ubus.\n", port_id); + return 0; +} + +static int nl_ssu_link_credi_overtime_recover(struct ub_entity *uent, u8 nl_id) +{ +#define NL_PORTS 2 + /* + * The shared ports will only be front of exclusive ports, + * so the shared port ind is begin of 0 and serially, + * each nl has 2 ports, recover port ind of this nl should begin of (nl_id)*2 + */ + u16 port_id = nl_id * NL_PORTS; + int ret = 0, i; + + for (i = 0; i < NL_PORTS; i++) { + port_id += i; + ret = ubus_port_recover(uent, port_id); + if (ret) { + ub_err(uent, "port[%u] recover failed, ret=%d.\n", port_id, ret); + return ret; + } + } + + ub_info(uent, "nl[%u] recover all ports succeeded.\n", nl_id); + return ret; +} + +static int ubus_recover(struct ub_entity *uent, + const struct hisi_ubus_error_data *edata) +{ + int port_id; + u8 nl_id; + + if (is_nl_local_ras(edata->sub_module_id) && + is_nl_ssu_link_credi_overtime_err(edata)) { + nl_id = edata->core_id; + return nl_ssu_link_credi_overtime_recover(uent, nl_id); + } + + port_id = (int)edata->port_id; + return ubus_port_recover(uent, port_id); +} + +static void hisi_ubus_handle_error(struct ub_entity *uent, + const struct hisi_ubus_error_data *edata) +{ + int ret; + u32 i; + + hisi_ubus_ras_print(uent, edata); + ub_info(uent, "Reg Dump:\n"); +#define REGISTER_ARRAY_MAX_SIZE 256 + for (i = 0; i < DIV_ROUND_UP( + min(edata->register_array_size, REGISTER_ARRAY_MAX_SIZE), SZ_4); i++) + ub_info(uent, "ERR_MISC_%u = %#x\n", i, edata->err_misc[i]); + if (edata->register_array_size > REGISTER_ARRAY_MAX_SIZE) + ub_warn(uent, "register array size is exceed max array size %d, only parts of data were printed.\n", + REGISTER_ARRAY_MAX_SIZE); + + if (!ubus_need_recover(edata) || uent->ubc->cluster) { + ub_info(uent, "ubus no need recover.\n"); + return; + } + + ret = ubus_recover(uent, edata); + if (ret) + ub_err(uent, "ubus recover failed, ret=%d\n", ret); +} + +static bool ubus_error_supported(const struct hisi_ubus_error_data *error_data) +{ +#define UNION_DIE 1 +#define HIP12_UB_MODULE_ID 0x2d +#define ASCEND950_UB_MODULE_ID 0x31 + if (!(HISI_UBUS_LOCAL_VALID_MODULE_ID & error_data->val_bits) || + !(HISI_UBUS_LOCAL_VALID_NIMBUS_ID & error_data->val_bits) || + !(HISI_UBUS_LOCAL_VALID_SOC_ID & error_data->soc_id)) + return false; + + if (error_data->soc_id == HIP12_SOC_ID) + return error_data->nimbus_id == UNION_DIE && + error_data->module_id == HIP12_UB_MODULE_ID; + + if (error_data->soc_id == ASCEND950_SOC_ID) + return error_data->module_id == ASCEND950_UB_MODULE_ID; + + return false; +} + +static int hisi_ubus_notify_error(struct notifier_block *nb, unsigned long event, void *data) +{ + const struct hisi_ubus_error_data *error_data; + struct acpi_hest_generic_data *gdata; + struct ub_bus_controller *ubc; + guid_t err_sec_guid; + + gdata = (struct acpi_hest_generic_data *)data; + import_guid(&err_sec_guid, gdata->section_type); + if (!guid_equal(&err_sec_guid, &hisi_ubus_sec_guid)) + return NOTIFY_DONE; + + error_data = (struct hisi_ubus_error_data *)acpi_hest_get_payload(gdata); + if (!ubus_error_supported(error_data)) + return NOTIFY_DONE; + + ubc = find_bus_controller_by_errdata(error_data); + if (!ubc) + return NOTIFY_DONE; + + hisi_ubus_handle_error(ubc->uent, error_data); + return NOTIFY_OK; +} + +static struct hisi_ubus_error_private *hisi_ubus_error_private_p; + +void ub_ras_handler_remove(void) +{ + if (hisi_ubus_error_private_p) { + ghes_unregister_vendor_record_notifier( + &hisi_ubus_error_private_p->nb); + kfree(hisi_ubus_error_private_p); + hisi_ubus_error_private_p = NULL; + } +} + +int ub_ras_handler_probe(void) +{ + int ret; + + hisi_ubus_error_private_p = kzalloc(sizeof(struct hisi_ubus_error_private), + GFP_KERNEL); + if (!hisi_ubus_error_private_p) + return -ENOMEM; + + hisi_ubus_error_private_p->nb.notifier_call = hisi_ubus_notify_error; + ret = ghes_register_vendor_record_notifier( + &hisi_ubus_error_private_p->nb); + if (ret) { + pr_err("register ubus error handler with apei failed\n"); + kfree(hisi_ubus_error_private_p); + hisi_ubus_error_private_p = NULL; + } + + return ret; +} diff --git a/drivers/ub/ubus/vendor/hisilicon/local-ras.h b/drivers/ub/ubus/vendor/hisilicon/local-ras.h new file mode 100644 index 000000000000..fe036069e3ab --- /dev/null +++ b/drivers/ub/ubus/vendor/hisilicon/local-ras.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __LOCAL_RAS_H__ +#define __LOCAL_RAS_H__ + +#define HISI_UBUS_ERR_MISC_REGS 64 + +#define HISI_UBUS_LOCAL_VALID_SOC_ID BIT(0) +#define HISI_UBUS_LOCAL_VALID_SOCKET_ID BIT(1) +#define HISI_UBUS_LOCAL_VALID_TOTEM_ID BIT(2) +#define HISI_UBUS_LOCAL_VALID_NIMBUS_ID BIT(3) +#define HISI_UBUS_LOCAL_VALID_SUB_SYSTEM_ID BIT(4) +#define HISI_UBUS_LOCAL_VALID_MODULE_ID BIT(5) +#define HISI_UBUS_LOCAL_VALID_SUB_MODULE_ID BIT(6) +#define HISI_UBUS_LOCAL_VALID_CORE_ID BIT(7) +#define HISI_UBUS_LOCAL_VALID_PORT_ID BIT(8) +#define HISI_UBUS_LOCAL_VALID_ERR_TYPE BIT(9) +#define HISI_UBUS_LOCAL_VALID_ERR_INFO BIT(10) +#define HISI_UBUS_LOCAL_VALID_ERR_SEVERITY BIT(11) +#define HISI_UBUS_LOCAL_VALID_REG_SIZE BIT(12) + +struct hisi_ubus_error_data { + u32 val_bits; + u8 version; + u8 soc_id; + u8 socket_id; + u8 totem_id; + u8 nimbus_id; + u8 sub_system_id; + u8 module_id; + u8 sub_module_id; + u8 core_id; + u8 port_id; + u16 err_type; + u64 rsvd; + u8 err_severity; + u8 reserv[3]; + u32 register_array_size; + u32 err_misc[]; +}; + +struct hisi_ubus_error_private { + struct notifier_block nb; +}; + +enum hisi_ubus_submodule_idx { + HISI_UBUS_SUB_MODULE_ID_TP, + HISI_UBUS_SUB_MODULE_ID_TA, + HISI_UBUS_SUB_MODULE_ID_MISC, + HISI_UBUS_SUB_MODULE_ID_BA, + HISI_UBUS_SUB_MODULE_ID_NL, + HISI_UBUS_SUB_MODULE_ID_DLMAC, + HISI_UBUS_SUB_MODULE_ID_MUXPCS, + HISI_UBUS_SUB_MODULE_ID_CCUM, + HISI_UBUS_SUB_MODULE_ID_CCUA, + HISI_UBUS_SUB_MODULE_ID_ETH_MAC, + HISI_UBUS_SUB_MODULE_ID_INVAILD, +}; + +enum hisi_ubus_err_severity { + HISI_UBUS_ERR_SEV_RECOVERABLE, + HISI_UBUS_ERR_SEV_FATAL, + HISI_UBUS_ERR_SEV_CORRECTED, + HISI_UBUS_ERR_SEV_NONE, +}; + +void ub_ras_handler_remove(void); +int ub_ras_handler_probe(void); + +#endif /* __LOCAL_RAS_H__ */ -- Gitee From 83eaccc2fe0393a144db2077c770058def9fb4a0 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Tue, 23 Sep 2025 09:35:22 +0800 Subject: [PATCH 083/214] ub:hisi-ubus: Support for hisi MSGQ framework ANBZ: #45460 commit defb3950a595e989772d51bb4524c39cb92e04d5 openEuler. The framework for implementing msgq, including initialization and deinitialization, provides the foundational pathway for subsequent message transmission and reception. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/msg.h | 19 +++ drivers/ub/ubus/vendor/hisilicon/Makefile | 2 +- drivers/ub/ubus/vendor/hisilicon/controller.c | 15 ++ drivers/ub/ubus/vendor/hisilicon/hisi-msg.h | 94 +++++++++++ drivers/ub/ubus/vendor/hisilicon/msg-core.c | 153 ++++++++++++++++++ drivers/ub/ubus/vendor/hisilicon/msg.c | 84 ++++++++++ 6 files changed, 366 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/vendor/hisilicon/hisi-msg.h create mode 100644 drivers/ub/ubus/vendor/hisilicon/msg-core.c create mode 100644 drivers/ub/ubus/vendor/hisilicon/msg.c diff --git a/drivers/ub/ubus/msg.h b/drivers/ub/ubus/msg.h index 5d520db132d5..92a126bdf471 100644 --- a/drivers/ub/ubus/msg.h +++ b/drivers/ub/ubus/msg.h @@ -252,6 +252,25 @@ static inline void message_info_init(struct msg_info *info, struct ub_entity *ue info->rsp_pkt_size = (u16)size; } +static inline void __message_device_set_ops(struct message_device *mdev, + const struct message_ops *ops) +{ + mdev->ops = ops; +} + +#define message_device_set_ops(mdev, ops) \ +do { \ + struct message_ops *__ops = (struct message_ops *)(ops); \ + __ops->owner = THIS_MODULE; \ + __message_device_set_ops(mdev, __ops); \ +} while (0) + +static inline void message_device_set_fwnode(struct message_device *mdev, + struct fwnode_handle *fwnode) +{ + mdev->fwnode = fwnode; +} + void ub_msg_pkt_header_init(struct msg_pkt_header *header, struct ub_entity *uent, u16 plen, u8 code, bool flag); diff --git a/drivers/ub/ubus/vendor/hisilicon/Makefile b/drivers/ub/ubus/vendor/hisilicon/Makefile index f4191cc288bf..6b4dac3a604e 100644 --- a/drivers/ub/ubus/vendor/hisilicon/Makefile +++ b/drivers/ub/ubus/vendor/hisilicon/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0+ -hisi_ubus-objs := hisi-ubus.o controller.o vdm.o local-ras.o +hisi_ubus-objs := hisi-ubus.o controller.o vdm.o local-ras.o msg.o msg-core.o obj-$(CONFIG_UB_HISI_UBUS) += hisi_ubus.o diff --git a/drivers/ub/ubus/vendor/hisilicon/controller.c b/drivers/ub/ubus/vendor/hisilicon/controller.c index eeadb39d38c9..d0bb7ed6fad7 100644 --- a/drivers/ub/ubus/vendor/hisilicon/controller.c +++ b/drivers/ub/ubus/vendor/hisilicon/controller.c @@ -11,6 +11,7 @@ #include "../../ubus_controller.h" #include "hisi-ubus.h" +#include "hisi-msg.h" static struct ub_bus_controller_ops hi_ubc_ops = { .register_decoder_base_addr = hi_register_decoder_base_addr, @@ -41,14 +42,28 @@ static void ub_bus_controller_debugfs_uninit(struct ub_bus_controller *ubc) int ub_bus_controller_probe(struct ub_bus_controller *ubc) { + int ret; + ubc->ops = &hi_ubc_ops; ub_bus_controller_debugfs_init(ubc); + ret = hi_msg_device_probe(ubc); + if (ret) + goto msg_fail; + return 0; + +msg_fail: + ub_bus_controller_debugfs_uninit(ubc); + ubc->ops = NULL; + return ret; } void ub_bus_controller_remove(struct ub_bus_controller *ubc) { + if (ubc->mdev) + hi_msg_device_remove(ubc); + ub_bus_controller_debugfs_uninit(ubc); ubc->ops = NULL; } diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h b/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h new file mode 100644 index 000000000000..8df625d7b12c --- /dev/null +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __HISI_MSG_H__ +#define __HISI_MSG_H__ + +#include +#include +#include + +#include "../../msg.h" + +/* Hardware register context */ +#define HI_MSGQ_SIZE 0x120 +#define SQ_ADDR_L 0x0 +#define SQ_ADDR_H 0x4 +#define SQ_PI 0x8 +#define SQ_CI 0xc +#define SQ_DEPTH 0x10 +#define SQ_STATUS 0x14 +#define RQ_ADDR_L 0x40 +#define RQ_ADDR_H 0x44 +#define RQ_PI 0x48 +#define RQ_CI 0x4c +#define RQ_DEPTH 0x50 +#define RQ_ENTRY_SIZE 0x54 +#define RQ_STATUS 0x58 +#define CQ_ADDR_L 0x70 +#define CQ_ADDR_H 0x74 +#define CQ_PI 0x78 +#define CQ_CI 0x7c +#define CQ_DEPTH 0x80 +#define CQ_STATUS 0x84 +#define MSGQ_RST 0xB0 + +#define HI_MSG_RQE_SIZE 0x800 /* 2K */ +#define HI_MSG_SQE_PLD_SIZE 0x800 /* 2K */ +#define HI_SQ_CFG_DEPTH 0x400 +#define HI_RQ_CFG_DEPTH 0x400 +#define HI_CQ_CFG_DEPTH 0x400 +#define HI_MSG_SQE_SIZE 16 +#define HI_MSG_CQE_SIZE 16 + +enum hi_msgq_user { + MSGQ_USER_BUS_DRV = 0, + MSGQ_USER_NUMS +}; + +enum hi_msgq_idx { + MSG_SQ = 0, + MSG_RQ = 1, + MSG_CQ = 2, + MSGQ_NUM +}; + +struct hi_msg_queue { + union { + struct hi_msg_sqe *sqe; + void *rqe; + struct hi_msg_cqe *cqe; + void *entry; + }; + + dma_addr_t dma_addr; + + u16 ci; + u16 pi; + u16 depth; + u16 entry_size; + size_t total_size; + + spinlock_t lock; +}; + +struct hi_msg_core { + struct device *dev; /* ubc->dev */ + phys_addr_t q_addr; /* MSGQ ctx's phy addr & size */ + size_t q_size; + void __iomem *reg_base; /* MSGQ context virtual address */ + + int user; + struct hi_msg_queue queue[MSGQ_NUM]; +}; + +u32 hi_msg_reg_read(struct hi_msg_core *hmc, u16 offset); +void hi_msg_reg_write(struct hi_msg_core *hmc, u16 offset, u32 val); +int hi_msg_core_init(struct hi_msg_core *hmc, int user); +void hi_msg_core_uninit(struct hi_msg_core *hmc); +int hi_msg_device_probe(struct ub_bus_controller *ubc); +void hi_msg_device_remove(struct ub_bus_controller *ubc); + +#endif /* __HISI_MSG_H__ */ diff --git a/drivers/ub/ubus/vendor/hisilicon/msg-core.c b/drivers/ub/ubus/vendor/hisilicon/msg-core.c new file mode 100644 index 000000000000..778212abb7d0 --- /dev/null +++ b/drivers/ub/ubus/vendor/hisilicon/msg-core.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include + +#include "hisi-msg.h" + +#define HI_MSG_RQE_SIZE_128 0x80 +#define hi_msg_rqe_size_hw(sz_sw) (ilog2((sz_sw) / (HI_MSG_RQE_SIZE_128))) + +struct hi_msgq_reg { + u32 pi; + u32 ci; + u32 depth; + u32 entry_size; + u32 addr_l; + u32 addr_h; +}; + +static struct hi_msgq_reg msgq_reg[MSGQ_NUM] = { + { SQ_PI, SQ_CI, SQ_DEPTH, 0, SQ_ADDR_L, SQ_ADDR_H }, + { RQ_PI, RQ_CI, RQ_DEPTH, RQ_ENTRY_SIZE, RQ_ADDR_L, RQ_ADDR_H }, + { CQ_PI, CQ_CI, CQ_DEPTH, 0, CQ_ADDR_L, CQ_ADDR_H } +}; + +static const char * const msgq_user_name[] = { "UBUS" }; + +struct hi_msgq_cfg { + u32 depth; + u32 entry_size; +}; + +static int hi_msg_queue_sw_init(struct hi_msg_core *hmc, int user, + int idx) +{ + struct hi_msgq_cfg msgq_cfg[MSGQ_USER_NUMS][MSGQ_NUM] = { + { { HI_SQ_CFG_DEPTH, HI_MSG_SQE_SIZE }, + { HI_RQ_CFG_DEPTH, HI_MSG_RQE_SIZE }, + { HI_CQ_CFG_DEPTH, HI_MSG_CQE_SIZE } } + }; + struct hi_msg_queue *q = &hmc->queue[idx]; + struct device *dev = hmc->dev; + + q->ci = 0; + q->pi = 0; + q->depth = msgq_cfg[user][idx].depth; + q->entry_size = msgq_cfg[user][idx].entry_size; + q->total_size = q->depth * q->entry_size; + spin_lock_init(&q->lock); + + if (idx == MSG_SQ) + q->total_size += HI_MSG_SQE_PLD_SIZE * q->depth; + + q->entry = dma_alloc_coherent(dev, q->total_size, &q->dma_addr, + GFP_KERNEL); + if (!q->entry) + return -ENOMEM; + + dev_info(dev, "%s queue=%d, ci=%#x, pi=%#x, depth=%#x, entry_size=%#x, size=%#lx\n", + msgq_user_name[user], idx, q->ci, q->pi, q->depth, + q->entry_size, q->total_size); + + return 0; +} + +static void hi_msg_queue_sw_uninit(struct hi_msg_core *hmc, int idx) +{ + struct hi_msg_queue *q = &hmc->queue[idx]; + + dma_free_coherent(hmc->dev, q->total_size, q->entry, q->dma_addr); +} + +u32 hi_msg_reg_read(struct hi_msg_core *hmc, u16 offset) +{ + return readl(hmc->reg_base + offset); +} + +void hi_msg_reg_write(struct hi_msg_core *hmc, u16 offset, u32 val) +{ + writel(val, hmc->reg_base + offset); +} + +static void hi_msg_queue_hw_init(struct hi_msg_core *hmc, int idx) +{ + struct hi_msg_queue *q = &hmc->queue[idx]; + + /* cfg depth pre */ + hi_msg_reg_write(hmc, msgq_reg[idx].depth, q->depth); + + if (idx == MSG_SQ) + hi_msg_reg_write(hmc, msgq_reg[idx].pi, q->pi); + else + hi_msg_reg_write(hmc, msgq_reg[idx].ci, q->ci); + + hi_msg_reg_write(hmc, msgq_reg[idx].addr_l, q->dma_addr & 0xffffffff); + hi_msg_reg_write(hmc, msgq_reg[idx].addr_h, + q->dma_addr >> BITS_PER_TYPE(u32)); + + if (idx == MSG_RQ) + hi_msg_reg_write(hmc, msgq_reg[idx].entry_size, + hi_msg_rqe_size_hw(HI_MSG_RQE_SIZE)); +} + +static void hi_msg_reset_queue(struct hi_msg_core *hmc) +{ + hi_msg_reg_write(hmc, MSGQ_RST, 1); + hi_msg_reg_write(hmc, MSGQ_RST, 0); +} + +int hi_msg_core_init(struct hi_msg_core *hmc, int user) +{ + int i, j, ret; + + hmc->user = user; + hmc->reg_base = ioremap(hmc->q_addr, hmc->q_size); + if (!hmc->reg_base) + return -ENOMEM; + + for (i = 0; i < MSGQ_NUM; i++) { + ret = hi_msg_queue_sw_init(hmc, user, i); + if (ret) + goto sw_uninit; + } + + hi_msg_reset_queue(hmc); + + for (i = 0; i < MSGQ_NUM; i++) + hi_msg_queue_hw_init(hmc, i); + + return 0; +sw_uninit: + hi_msg_reset_queue(hmc); + + for (j = 0; j < i; j++) + hi_msg_queue_sw_uninit(hmc, j); + + iounmap(hmc->reg_base); + return ret; +} + +void hi_msg_core_uninit(struct hi_msg_core *hmc) +{ + int i; + + hi_msg_reset_queue(hmc); + + for (i = 0; i < MSGQ_NUM; i++) + hi_msg_queue_sw_uninit(hmc, i); + + iounmap(hmc->reg_base); +} diff --git a/drivers/ub/ubus/vendor/hisilicon/msg.c b/drivers/ub/ubus/vendor/hisilicon/msg.c new file mode 100644 index 000000000000..b70fb17a420e --- /dev/null +++ b/drivers/ub/ubus/vendor/hisilicon/msg.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus hisi msg: " fmt + +#include "../../ubus.h" +#include "../../msg.h" +#include "hisi-msg.h" + +struct hi_message_device { + struct hi_msg_core hmc; + struct message_device mdev; +}; + +#define to_hi_message_device(dev) \ + container_of(dev, struct hi_message_device, mdev) + +static int hi_msg_queue_init(struct hi_message_device *hmd) +{ + struct hi_msg_core *hmc = &hmd->hmc; + + return hi_msg_core_init(hmc, MSGQ_USER_BUS_DRV); +} + +static void hi_msg_queue_uninit(struct hi_message_device *hmd) +{ + hi_msg_core_uninit(&hmd->hmc); +} + +static struct message_ops hi_message_ops = {}; + +int hi_msg_device_probe(struct ub_bus_controller *ubc) +{ + struct device *dev = &ubc->dev; + struct hi_message_device *hmd; + struct hi_msg_core *hmc; + int ret; + + hmd = kzalloc(sizeof(*hmd), GFP_KERNEL); + if (!hmd) + return -ENOMEM; + + hmc = &hmd->hmc; + hmc->dev = dev; + hmc->q_addr = ubc->attr.queue_addr; + hmc->q_size = HI_MSGQ_SIZE; + + ret = hi_msg_queue_init(hmd); + if (ret) { + dev_err(dev, "init message queue failed\n"); + goto queue_init_fail; + } + + message_device_set_ops(&hmd->mdev, &hi_message_ops); + message_device_set_fwnode(&hmd->mdev, dev->fwnode); + + ret = message_device_register(&hmd->mdev); + if (ret) { + dev_err(dev, "register message device failed\n"); + goto mdev_reg_fail; + } + + ubc->mdev = &hmd->mdev; + + return 0; + +mdev_reg_fail: + hi_msg_queue_uninit(hmd); +queue_init_fail: + kfree(hmd); + return ret; +} + +void hi_msg_device_remove(struct ub_bus_controller *ubc) +{ + struct hi_message_device *hmd = to_hi_message_device(ubc->mdev); + + ubc->mdev = NULL; + message_device_unregister(&hmd->mdev); + hi_msg_queue_uninit(hmd); + kfree(hmd); +} -- Gitee From 37ded655cb00f9b2f70cb391c0e50aeb09d5f3f5 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Tue, 23 Sep 2025 09:57:58 +0800 Subject: [PATCH 084/214] ub:hisi-ubus: Support for MSGQ channel processing in SQ and CQ ANBZ: #45460 commit af49a86594b8913602a3d140f00a90ba7d016509 openEuler. Implement the SQ message sending functionality of the MSGQ queue and the CQ message receiving functionality, enabling interaction between the driver and link messages. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/vendor/hisilicon/hisi-msg.h | 74 ++++++++ drivers/ub/ubus/vendor/hisilicon/hisi-ubus.c | 4 + drivers/ub/ubus/vendor/hisilicon/msg-core.c | 122 +++++++++++++ drivers/ub/ubus/vendor/hisilicon/msg.c | 179 ++++++++++++++++++- 4 files changed, 378 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h b/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h index 8df625d7b12c..083e17f4e1fb 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h @@ -12,6 +12,8 @@ #include "../../msg.h" +extern int msg_wait; + /* Hardware register context */ #define HI_MSGQ_SIZE 0x120 #define SQ_ADDR_L 0x0 @@ -43,6 +45,13 @@ #define HI_MSG_SQE_SIZE 16 #define HI_MSG_CQE_SIZE 16 +enum hi_task_type { + PROTOCOL_MSG = 0, + PROTOCOL_ENUM = 1, + HISI_PRIVATE = 2, + TASK_TYPE_NUM +}; + enum hi_msgq_user { MSGQ_USER_BUS_DRV = 0, MSGQ_USER_NUMS @@ -55,6 +64,58 @@ enum hi_msgq_idx { MSGQ_NUM }; +struct hi_msg_sqe { + /* DW0 */ + u32 task_type : 2; + u32 rsvd0 : 2; + u32 local : 1; + u32 dev_type : 2; + u32 icrc : 1; + union { + struct { + u8 type : 1; + u8 msg_code : 3; + u8 sub_msg_code : 4; + }; + u8 opcode; + }; + u32 p_len : 12; + u32 rsvd1 : 4; + /* DW1 */ + u32 msn : 16; + u32 rsvd3 : 16; + /* DW2 */ + u32 p_addr; + /* DW3 */ + u32 rsvd2; +}; + +struct hi_msg_cqe { + /* DW0 */ + u32 task_type : 2; + u32 rsvd0 : 6; + union { + struct { + u8 type : 1; + u8 msg_code : 3; + u8 sub_msg_code : 4; + }; + u8 opcode; + }; + u32 p_len : 12; + u32 rsvd1 : 4; + /* DW1 */ + u32 msn : 16; + u32 rsvd5 : 16; + /* DW2 */ + u32 rq_pi : 10; + u32 rsvd2 : 6; + u32 status : 8; + u32 rsvd3 : 8; + /* DW3 */ + u32 rsvd4; +}; + struct hi_msg_queue { union { struct hi_msg_sqe *sqe; @@ -84,6 +145,19 @@ struct hi_msg_core { struct hi_msg_queue queue[MSGQ_NUM]; }; +#define q_used_cnt(q) (((q)->pi + (q)->depth - (q)->ci) % (q)->depth) +#define q_ptr_idx(q, p, i) (((q)->p + (i)) % (q)->depth) +#define cq_entry(hmc, idx) (&((hmc)->queue[MSG_CQ].cqe[idx])) +#define rq_entry(hmc, idx) \ + ((hmc)->queue[MSG_RQ].rqe + (HI_MSG_RQE_SIZE * (idx))) + +int hi_msg_cq_poll(struct hi_msg_core *hmc, int task_type, u16 msn); +void hi_msg_rq_update(struct hi_msg_core *hmc, int cq_idx); +void hi_msg_sqe_init(struct hi_msg_sqe *sqe, int msn, struct msg_info *info, + int task_type, u8 code); +int hi_message_cqe_check(struct device *dev, struct hi_msg_sqe *sqe, + struct hi_msg_cqe *cqe, u16 rsp_pkt_size); +void hi_msg_rqe_get(struct hi_msg_core *hmc, void *buf, struct hi_msg_cqe *cqe); u32 hi_msg_reg_read(struct hi_msg_core *hmc, u16 offset); void hi_msg_reg_write(struct hi_msg_core *hmc, u16 offset, u32 val); int hi_msg_core_init(struct hi_msg_core *hmc, int user); diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.c b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.c index 54b1f2f0c582..be86f055cb34 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.c +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.c @@ -10,6 +10,10 @@ #include "local-ras.h" #include "hisi-ubus.h" +int msg_wait = 1024; +module_param(msg_wait, int, 0444); +MODULE_PARM_DESC(msg_wait, "message wait timeout, default: 1024ms"); + #define HISI_VENDOR_ID 0xCC08 #define HISI_UBUS_DRV_NAME "hisi_ubus" diff --git a/drivers/ub/ubus/vendor/hisilicon/msg-core.c b/drivers/ub/ubus/vendor/hisilicon/msg-core.c index 778212abb7d0..2ac2ad0b6ef0 100644 --- a/drivers/ub/ubus/vendor/hisilicon/msg-core.c +++ b/drivers/ub/ubus/vendor/hisilicon/msg-core.c @@ -5,11 +5,15 @@ #include +#include "../../ubus.h" #include "hisi-msg.h" #define HI_MSG_RQE_SIZE_128 0x80 #define hi_msg_rqe_size_hw(sz_sw) (ilog2((sz_sw) / (HI_MSG_RQE_SIZE_128))) +enum hi_sqe_ent_type { TYPE_BUS_CONTROLLER = 0, TYPE_IDEV = 1 }; +enum hi_cqe_status { CQE_SUCCESS, CQE_FAIL }; + struct hi_msgq_reg { u32 pi; u32 ci; @@ -151,3 +155,121 @@ void hi_msg_core_uninit(struct hi_msg_core *hmc) iounmap(hmc->reg_base); } + +void hi_msg_sqe_init(struct hi_msg_sqe *sqe, int msn, struct msg_info *info, + int task_type, u8 code) +{ + bool idev_flag = false; + + sqe->task_type = (u8)task_type; + /* ub bus controller & idev 's local is 1 */ + if (info->uent) { + if (info->ubc != info->uent && + uent_type(info->uent) == UB_TYPE_ICONTROLLER) + idev_flag = true; + + sqe->local = (info->ubc == info->uent || idev_flag) ? 1 : 0; + if (sqe->local) + sqe->dev_type = idev_flag ? TYPE_IDEV : TYPE_BUS_CONTROLLER; + } + + /* only msg need icrc */ + sqe->icrc = (task_type == PROTOCOL_MSG) ? 1 : 0; + sqe->opcode = code; + sqe->p_len = info->req_pkt_size; + sqe->msn = (u16)msn; + /* p_addr init inside submit */ +} + +int hi_msg_cq_poll(struct hi_msg_core *hmc, int task_type, u16 msn) +{ + struct hi_msg_queue *cq = &hmc->queue[MSG_CQ]; + struct hi_msg_cqe *cqe; + unsigned long flags; + int i, cnt, idx; + + spin_lock_irqsave(&cq->lock, flags); + cq->pi = hi_msg_reg_read(hmc, CQ_PI); + if (cq->pi == cq->ci) { + spin_unlock_irqrestore(&cq->lock, flags); + return -EAGAIN; + } + + cnt = q_used_cnt(cq); + for (i = 0; i < cnt; i++) { + idx = q_ptr_idx(cq, ci, i); + cqe = cq_entry(hmc, idx); + if (cqe->msn == msn && cqe->task_type == (u8)task_type && + (task_type != PROTOCOL_MSG || cqe->type == MSG_RSP)) { + spin_unlock_irqrestore(&cq->lock, flags); + return idx; + } + } + spin_unlock_irqrestore(&cq->lock, flags); + return -EAGAIN; +} + +int hi_message_cqe_check(struct device *dev, struct hi_msg_sqe *sqe, + struct hi_msg_cqe *cqe, u16 rsp_pkt_size) +{ + if (cqe->status != CQE_SUCCESS) { + dev_err(dev, "cqe fail status=%u\n", cqe->status); + return -EINVAL; + } + + if (cqe->p_len > rsp_pkt_size || cqe->p_len > HI_MSG_RQE_SIZE) { + dev_err(dev, "cqe p_len %#x invalid, rsp_pkt_size %#x\n", + cqe->p_len, rsp_pkt_size); + return -EINVAL; + } + + if (sqe->task_type == PROTOCOL_MSG) { + if (cqe->p_len < MSG_PKT_HEADER_SIZE) { + dev_err(dev, "cqe p_len %u is less than msg pkt header size.\n", + cqe->p_len); + return -EINVAL; + } + if ((sqe->opcode >> 1) == (cqe->opcode >> 1)) + return 0; + } else { + if (sqe->opcode == cqe->opcode) + return 0; + } + + dev_err(dev, "opcode sqe %#x != cqe %#x\n", sqe->opcode, cqe->opcode); + return -EINVAL; +} + +void hi_msg_rqe_get(struct hi_msg_core *hmc, void *buf, struct hi_msg_cqe *cqe) +{ + u16 rqe_end_index, rqe_high_size, rqe_low_size, pi, depth; + u32 p_len; + + depth = hmc->queue[MSG_RQ].depth; + pi = cqe->rq_pi; + p_len = cqe->p_len; + + rqe_end_index = pi + DIV_ROUND_UP(p_len, HI_MSG_RQE_SIZE); + if (rqe_end_index <= depth) { + memcpy(buf, rq_entry(hmc, pi), p_len); + return; + } + + /* overlap scenario */ + rqe_high_size = (depth - pi) * HI_MSG_RQE_SIZE; + rqe_low_size = p_len - rqe_high_size; + memcpy(buf, rq_entry(hmc, pi), rqe_high_size); + memcpy(buf + rqe_high_size, rq_entry(hmc, 0), rqe_low_size); +} + +void hi_msg_rq_update(struct hi_msg_core *hmc, int cq_idx) +{ + struct hi_msg_queue *rq = &hmc->queue[MSG_RQ]; + struct hi_msg_cqe *cqe; + + cqe = cq_entry(hmc, cq_idx); + rq->ci = (cqe->rq_pi + DIV_ROUND_UP(cqe->p_len, HI_MSG_RQE_SIZE)) % + rq->depth; + wmb(); /* Ensure the register is written correctly. */ + hi_msg_reg_write(hmc, RQ_CI, rq->ci); +} diff --git a/drivers/ub/ubus/vendor/hisilicon/msg.c b/drivers/ub/ubus/vendor/hisilicon/msg.c index b70fb17a420e..eed78aa397d2 100644 --- a/drivers/ub/ubus/vendor/hisilicon/msg.c +++ b/drivers/ub/ubus/vendor/hisilicon/msg.c @@ -5,27 +5,204 @@ #define pr_fmt(fmt) "ubus hisi msg: " fmt +#include + #include "../../ubus.h" #include "../../msg.h" #include "hisi-msg.h" +struct hi_msg_sqe_pld { + char packet[HI_MSG_SQE_PLD_SIZE]; +}; + struct hi_message_device { struct hi_msg_core hmc; + struct hi_cqe_state *cqe_state; + atomic_t msg_in_flight_cnt; struct message_device mdev; }; #define to_hi_message_device(dev) \ container_of(dev, struct hi_message_device, mdev) +enum hi_cq_sw_state { CQ_SW_INIT, CQ_SW_HANDLED }; + +struct hi_cqe_state { + u8 state; +}; +#define HI_CQE_STATE_SZ sizeof(struct hi_cqe_state) + +static inline void cqe_state_set(struct hi_message_device *hmd, int idx, + u8 state) +{ + hmd->cqe_state[idx].state = state; +} +#define cqe_state_get(hmd, idx) ((hmd)->cqe_state[idx].state) + +#define MSG_MAX (HI_SQ_CFG_DEPTH - 1) +#define q_left_cnt(q) ((q)->depth - q_used_cnt((q)) - 1) +#define sq_sw_left(hmd) (MSG_MAX - atomic_read(&(hmd)->msg_in_flight_cnt)) +#define sq_pld_entry_off(hmd, idx) \ + ((HI_MSG_SQE_SIZE * HI_SQ_CFG_DEPTH) + (HI_MSG_SQE_PLD_SIZE * (idx))) +#define sq_pld_entry_sw(hmd, idx) \ + ((void *)&((hmd)->hmc.queue[MSG_SQ].sqe[HI_SQ_CFG_DEPTH]) + \ + (HI_MSG_SQE_PLD_SIZE * (idx))) + +#define MSGQ_TIMEOUT (HZ * msg_wait / 1000) + +static int hi_msg_get_sq_idle_num(struct hi_message_device *hmd, int num, + bool tx) +{ + struct hi_msg_queue *sq = &hmd->hmc.queue[MSG_SQ]; + int real, sw_left, hw_left; + + hw_left = q_left_cnt(sq); + sw_left = sq_sw_left(hmd); + real = num; + + /* Assure message in flight not overflow, just tx need check */ + if (tx && real > sw_left) + real = sw_left; + + /* If not enough, update ci by read hardware */ + if (real > hw_left) { + sq->ci = hi_msg_reg_read(&hmd->hmc, SQ_CI); + hw_left = q_left_cnt(sq); + if (real > hw_left) + real = hw_left; + } + + return real; +} + +static int hi_msg_sq_submit(struct hi_message_device *hmd, + struct hi_msg_sqe *sqe, struct hi_msg_sqe_pld *pld, + int num, bool wait) +{ + struct hi_msg_queue *sq = &hmd->hmc.queue[MSG_SQ]; + struct hi_msg_core *hmc = &hmd->hmc; + struct hi_msg_sqe *tar_sqe; + unsigned long flags; + int i, real; + u16 sqe_idx; + + spin_lock_irqsave(&sq->lock, flags); + + real = hi_msg_get_sq_idle_num(hmd, num, wait); + if (!real) { + spin_unlock_irqrestore(&sq->lock, flags); + return 0; + } + + for (i = 0; i < real; i++) { + sqe_idx = q_ptr_idx(sq, pi, i); + tar_sqe = sq->sqe + sqe_idx; + (sqe + i)->p_addr = sq_pld_entry_off(hmd, sqe_idx); + memcpy(tar_sqe, sqe + i, HI_MSG_SQE_SIZE); + memcpy(sq_pld_entry_sw(hmd, sqe_idx), pld[i].packet, + (sqe + i)->p_len); + } + + sq->pi = q_ptr_idx(sq, pi, real); + wmb(); /* Ensure the register is written correctly. */ + hi_msg_reg_write(hmc, SQ_PI, sq->pi); + if (wait) + atomic_add(real, &hmd->msg_in_flight_cnt); + + spin_unlock_irqrestore(&sq->lock, flags); + + return real; +} + +static int hi_msg_sync_wait(struct hi_message_device *hmd, int task_type, + u16 msn, u64 duration, bool flag) +{ +#define SLEEP_MIN_US 1000 +#define SLEEP_MAX_US 2000 + u64 end_time = get_jiffies_64() + duration; + struct hi_msg_core *hmc = &hmd->hmc; + int idx; + + while (!time_after64(get_jiffies_64(), end_time)) { + idx = hi_msg_cq_poll(hmc, task_type, msn); + if (idx >= 0) + return idx; + + if (flag) + usleep_range(SLEEP_MIN_US, SLEEP_MAX_US); + } + + dev_err(hmc->dev, "task %d msn %#x wait cqe timeout\n", task_type, msn); + return -ETIMEDOUT; +} + +static void hi_msg_cq_update(struct hi_message_device *hmd) +{ + struct hi_msg_queue *cq = &hmd->hmc.queue[MSG_CQ]; + struct hi_msg_core *hmc = &hmd->hmc; + int i, cnt, idx, release_cnt = 0; + struct hi_msg_cqe *cqe; + unsigned long flags; + + spin_lock_irqsave(&cq->lock, flags); + if (cq->ci == cq->pi) { + spin_unlock_irqrestore(&cq->lock, flags); + return; + } + + cnt = q_used_cnt(cq); + for (i = 0; i < cnt; i++) { + idx = q_ptr_idx(cq, ci, i); + if (cqe_state_get(hmd, idx) != CQ_SW_HANDLED) + break; + + cqe = cq_entry(hmc, idx); + if (cqe->task_type != PROTOCOL_MSG || cqe->type == MSG_RSP) + release_cnt++; + + cqe_state_set(hmd, idx, CQ_SW_INIT); + } + if (i == 0) { + spin_unlock_irqrestore(&cq->lock, flags); + return; + } + + hi_msg_rq_update(hmc, q_ptr_idx(cq, ci, i - 1)); + + cq->ci = q_ptr_idx(cq, ci, i); + wmb(); /* Ensure the register is written correctly. */ + hi_msg_reg_write(hmc, CQ_CI, cq->ci); + atomic_sub(release_cnt, &hmd->msg_in_flight_cnt); + spin_unlock_irqrestore(&cq->lock, flags); +} + static int hi_msg_queue_init(struct hi_message_device *hmd) { struct hi_msg_core *hmc = &hmd->hmc; + size_t size; + int ret; - return hi_msg_core_init(hmc, MSGQ_USER_BUS_DRV); + ret = hi_msg_core_init(hmc, MSGQ_USER_BUS_DRV); + if (ret) + return ret; + + size = HI_CQE_STATE_SZ * hmc->queue[MSG_CQ].depth; + hmd->cqe_state = kzalloc(size, GFP_KERNEL); + if (!hmd->cqe_state) { + dev_err(hmc->dev, "cqe state memory failed\n"); + ret = -ENOMEM; + goto cqe_state_fail; + } + return 0; + +cqe_state_fail: + hi_msg_core_uninit(hmc); + return ret; } static void hi_msg_queue_uninit(struct hi_message_device *hmd) { + kfree(hmd->cqe_state); hi_msg_core_uninit(&hmd->hmc); } -- Gitee From 7ffbab52cebf1fb166d0abeb70178e390d33c018 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Tue, 23 Sep 2025 10:23:01 +0800 Subject: [PATCH 085/214] ub:hisi-ubus: Provide callback functions for message devices ANBZ: #45460 commit 438efb9dce5ece7698a3636eddb4717d980d6a49 openEuler. Implement interfaces for sending and receiving protocol messages, enumeration messages, vendor messages, and private messages. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/vendor/hisilicon/hisi-msg.h | 8 + drivers/ub/ubus/vendor/hisilicon/msg-core.c | 31 +++ drivers/ub/ubus/vendor/hisilicon/msg.c | 262 +++++++++++++++++++- 3 files changed, 299 insertions(+), 2 deletions(-) diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h b/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h index 083e17f4e1fb..f286994b1ab0 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h @@ -155,6 +155,8 @@ int hi_msg_cq_poll(struct hi_msg_core *hmc, int task_type, u16 msn); void hi_msg_rq_update(struct hi_msg_core *hmc, int cq_idx); void hi_msg_sqe_init(struct hi_msg_sqe *sqe, int msn, struct msg_info *info, int task_type, u8 code); +void hi_msg_set_pkt_msn(struct msg_info *info, int task_type, u16 msn, + u8 msgq_id); int hi_message_cqe_check(struct device *dev, struct hi_msg_sqe *sqe, struct hi_msg_cqe *cqe, u16 rsp_pkt_size); void hi_msg_rqe_get(struct hi_msg_core *hmc, void *buf, struct hi_msg_cqe *cqe); @@ -164,5 +166,11 @@ int hi_msg_core_init(struct hi_msg_core *hmc, int user); void hi_msg_core_uninit(struct hi_msg_core *hmc); int hi_msg_device_probe(struct ub_bus_controller *ubc); void hi_msg_device_remove(struct ub_bus_controller *ubc); +int hi_message_sync_request(struct message_device *mdev, + struct msg_info *info, u8 opcode); +int hi_message_sync_request_sched(struct message_device *mdev, + struct msg_info *info, u8 opcode); +int hi_message_private(struct message_device *mdev, struct msg_info *info, + u8 opcode); #endif /* __HISI_MSG_H__ */ diff --git a/drivers/ub/ubus/vendor/hisilicon/msg-core.c b/drivers/ub/ubus/vendor/hisilicon/msg-core.c index 2ac2ad0b6ef0..2a0331269004 100644 --- a/drivers/ub/ubus/vendor/hisilicon/msg-core.c +++ b/drivers/ub/ubus/vendor/hisilicon/msg-core.c @@ -6,6 +6,8 @@ #include #include "../../ubus.h" +#include "../../msg.h" +#include "../../enum.h" #include "hisi-msg.h" #define HI_MSG_RQE_SIZE_128 0x80 @@ -273,3 +275,32 @@ void hi_msg_rq_update(struct hi_msg_core *hmc, int cq_idx) wmb(); /* Ensure the register is written correctly. */ hi_msg_reg_write(hmc, RQ_CI, rq->ci); } + +void hi_msg_set_pkt_msn(struct msg_info *info, int task_type, u16 msn, + u8 msgq_id) +{ + struct enum_topo_query_req *req; + struct msg_pkt_header *header; + void *pkt = info->req_packet; + size_t header_sz; + + switch (task_type) { + case PROTOCOL_ENUM: + header_sz = ENUM_PKT_HEADER_SIZE + + calc_enum_pld_header_size( + (struct enum_pld_scan_header + *)(pkt + ENUM_PKT_HEADER_SIZE), + true); + req = (struct enum_topo_query_req *)(pkt + header_sz); + req->common.msn = msn; + req->common.msgq_id = msgq_id; + break; + case PROTOCOL_MSG: + header = (struct msg_pkt_header *)pkt; + header->src_tassn = msn; + header->msgq_id = msgq_id; + break; + default: + return; + } +} diff --git a/drivers/ub/ubus/vendor/hisilicon/msg.c b/drivers/ub/ubus/vendor/hisilicon/msg.c index eed78aa397d2..b12c29e6f44a 100644 --- a/drivers/ub/ubus/vendor/hisilicon/msg.c +++ b/drivers/ub/ubus/vendor/hisilicon/msg.c @@ -9,6 +9,8 @@ #include "../../ubus.h" #include "../../msg.h" +#include "../../enum.h" +#include "vdm.h" #include "hisi-msg.h" struct hi_msg_sqe_pld { @@ -50,6 +52,84 @@ static inline void cqe_state_set(struct hi_message_device *hmd, int idx, #define MSGQ_TIMEOUT (HZ * msg_wait / 1000) +#define MSN_SIZE 65535 +DEFINE_SPINLOCK(msn_msg_lock); +DECLARE_BITMAP(msn_msg, MSN_SIZE); +DEFINE_SPINLOCK(msn_enum_lock); +DECLARE_BITMAP(msn_enum, MSN_SIZE); +DEFINE_SPINLOCK(msn_private_lock); +DECLARE_BITMAP(msn_private, MSN_SIZE); + +/* Return 0 represents failure, return 1-65535 represents success */ +static u16 hi_msn_get(int type) +{ + unsigned long flags, find, tmp, size, *addr; + static u16 msn[TASK_TYPE_NUM] = {}; + spinlock_t *lock; + u16 ret; + + find = msn[type]; + + if (type == PROTOCOL_MSG) { + addr = msn_msg; + lock = &msn_msg_lock; + } else if (type == PROTOCOL_ENUM) { + addr = msn_enum; + lock = &msn_enum_lock; + } else { /* hisi private */ + addr = msn_private; + lock = &msn_private_lock; + } + + spin_lock_irqsave(lock, flags); + if (unlikely(test_bit(find, addr))) { + size = MSN_SIZE; + tmp = find; + find = find_next_zero_bit(addr, size, find); + if (unlikely(find == size)) { + size = tmp; + find = find_next_zero_bit(addr, size, 0); + if (unlikely(find == size)) { + ret = 0; + goto out; + } + } + } + + set_bit(find, addr); + msn[type] = (find + 1) % MSN_SIZE; + ret = find + 1; /* find bit is 0~65534, ret use 1~65535 */ +out: + spin_unlock_irqrestore(lock, flags); + return ret; +} + +static void hi_msn_put(int type, u16 msn) +{ + unsigned long flags, *addr; + spinlock_t *lock; + + if (msn == 0 || type >= TASK_TYPE_NUM) { + pr_warn("invalid msn or type\n"); + return; + } + + if (type == PROTOCOL_MSG) { + addr = msn_msg; + lock = &msn_msg_lock; + } else if (type == PROTOCOL_ENUM) { + addr = msn_enum; + lock = &msn_enum_lock; + } else { /* hisi private */ + addr = msn_private; + lock = &msn_private_lock; + } + + spin_lock_irqsave(lock, flags); + clear_bit(msn - 1, addr); + spin_unlock_irqrestore(lock, flags); +} + static int hi_msg_get_sq_idle_num(struct hi_message_device *hmd, int num, bool tx) { @@ -157,8 +237,10 @@ static void hi_msg_cq_update(struct hi_message_device *hmd) break; cqe = cq_entry(hmc, idx); - if (cqe->task_type != PROTOCOL_MSG || cqe->type == MSG_RSP) + if (cqe->task_type != PROTOCOL_MSG || cqe->type == MSG_RSP) { + hi_msn_put(cqe->task_type, cqe->msn); release_cnt++; + } cqe_state_set(hmd, idx, CQ_SW_INIT); } @@ -206,7 +288,183 @@ static void hi_msg_queue_uninit(struct hi_message_device *hmd) hi_msg_core_uninit(&hmd->hmc); } -static struct message_ops hi_message_ops = {}; +static int hi_message_probe_dev(struct ub_entity *uent) +{ + return 0; +} + +static void hi_message_remove_dev(struct ub_entity *uent) +{ +} + +static bool pkt_plen_valid(void *pkt, u16 pkt_size, int task_type) +{ + struct msg_pkt_header *header = (struct msg_pkt_header *)pkt; + + if (pkt_size > HI_MSG_SQE_PLD_SIZE) { + pr_err("pkt_size %#x over\n", pkt_size); + return false; + } + + if (task_type == PROTOCOL_MSG && + ((pkt_size < MSG_PKT_HEADER_SIZE) || + (header->msgetah.plen > PLD_SIZE_MAX) || + (header->msgetah.plen != (pkt_size - MSG_PKT_HEADER_SIZE)))) { + pr_err("msg plen %#x, pkt_size %#x invalid\n", + header->msgetah.plen, pkt_size); + return false; + } + + if (task_type == PROTOCOL_ENUM && + ((pkt_size < ENUM_PKT_HEADER_SIZE) || + (pkt_size - ENUM_PKT_HEADER_SIZE > PLD_SIZE_MAX))) { + pr_err("enum pkt_size %#x invalid\n", pkt_size); + return false; + } + + return true; +} + +static int hi_message_sync(struct message_device *mdev, struct msg_info *info, + int task_type, u8 code, bool flag) +{ + struct hi_message_device *hmd = to_hi_message_device(mdev); + struct hi_msg_core *hmc = &hmd->hmc; + struct hi_msg_sqe sqe = {}; + struct hi_msg_cqe *cqe; + int ret, msn, cnt; + int cq_idx; + + if (!pkt_plen_valid(info->req_packet, info->req_pkt_size, task_type)) + return -EINVAL; + + msn = hi_msn_get(task_type); + if (msn == 0) { + dev_err(hmc->dev, "task type %d alloc msn failed\n", task_type); + return -ENOSPC; + } + + hi_msg_sqe_init(&sqe, msn, info, task_type, code); + hi_msg_set_pkt_msn(info, task_type, msn, hmc->user); + + cnt = hi_msg_sq_submit( + hmd, &sqe, (struct hi_msg_sqe_pld *)info->req_packet, 1, true); + if (cnt != 1) { + dev_err(hmc->dev, "sq submit failed\n"); + hi_msn_put(task_type, msn); + return -ENOSPC; + } + + cq_idx = hi_msg_sync_wait(hmd, task_type, msn, MSGQ_TIMEOUT, flag); + if (cq_idx < 0) { + if (cq_idx == -ENOMEM) + hi_msn_put(task_type, msn); + return cq_idx; + } + + cqe = cq_entry(hmc, cq_idx); + ret = hi_message_cqe_check(hmc->dev, &sqe, cqe, info->rsp_pkt_size); + if (!ret) { + hi_msg_rqe_get(hmc, info->rsp_packet, cqe); + info->actual_rsp_size = cqe->p_len; + } + + cqe_state_set(hmd, cq_idx, CQ_SW_HANDLED); + hi_msg_cq_update(hmd); + return ret; +} + +int hi_message_sync_request(struct message_device *mdev, struct msg_info *info, + u8 code) +{ + return hi_message_sync(mdev, info, PROTOCOL_MSG, code, false); +} + +static int hi_message_sync_enum(struct message_device *mdev, + struct msg_info *info, u8 cmd) +{ + return hi_message_sync(mdev, info, PROTOCOL_ENUM, cmd, false); +} + +static int hi_message_response(struct message_device *mdev, + struct msg_info *info, u8 code) +{ + struct hi_message_device *hmd = to_hi_message_device(mdev); + struct hi_msg_sqe sqe = {}; + struct msg_pkt_header *header; + void *pkt = info->req_packet; + int cnt; + + if (!pkt_plen_valid(info->req_packet, info->req_pkt_size, PROTOCOL_MSG)) + return -EINVAL; + + header = (struct msg_pkt_header *)pkt; + hi_msg_sqe_init(&sqe, header->src_tassn, info, PROTOCOL_MSG, code); + + cnt = hi_msg_sq_submit( + hmd, &sqe, (struct hi_msg_sqe_pld *)info->req_packet, 1, false); + if (cnt != 1) { + dev_err(hmd->hmc.dev, "async sq submit failed\n"); + return -ENOSPC; + } + + return 0; +} + +static int hi_message_send(struct message_device *mdev, struct msg_info *info, + u8 code) +{ + struct hi_message_device *hmd = to_hi_message_device(mdev); + struct hi_msg_core *hmc = &hmd->hmc; + struct hi_msg_sqe sqe = {}; + int msn, cnt; + int ret = 0; + + if (!pkt_plen_valid(info->req_packet, info->req_pkt_size, PROTOCOL_MSG)) + return -EINVAL; + + msn = hi_msn_get(PROTOCOL_MSG); + if (msn == 0) { + dev_err(hmc->dev, "alloc msn failed\n"); + return -ENOSPC; + } + + hi_msg_sqe_init(&sqe, msn, info, PROTOCOL_MSG, code); + hi_msg_set_pkt_msn(info, PROTOCOL_MSG, msn, hmc->user); + + cnt = hi_msg_sq_submit( + hmd, &sqe, (struct hi_msg_sqe_pld *)info->req_packet, 1, false); + if (cnt != 1) { + dev_err(hmc->dev, "sq submit failed\n"); + ret = -ENOSPC; + } + + hi_msn_put(PROTOCOL_MSG, msn); + return ret; +} + +/* For some special message need sleep, work in non-atomic context */ +int hi_message_sync_request_sched(struct message_device *mdev, + struct msg_info *info, u8 code) +{ + return hi_message_sync(mdev, info, PROTOCOL_MSG, code, true); +} + +int hi_message_private(struct message_device *mdev, struct msg_info *info, + u8 opcode) +{ + return hi_message_sync(mdev, info, HISI_PRIVATE, opcode, false); +} + +static struct message_ops hi_message_ops = { + .probe_dev = hi_message_probe_dev, + .remove_dev = hi_message_remove_dev, + .sync_request = hi_message_sync_request, + .response = hi_message_response, + .sync_enum = hi_message_sync_enum, + .vdm_rx_handler = hi_vdm_rx_msg_handler, + .send = hi_message_send, +}; int hi_msg_device_probe(struct ub_bus_controller *ubc) { -- Gitee From 557d70679b9f3bad762dd1c1772fc6e3a784d6bc Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Tue, 23 Sep 2025 10:43:33 +0800 Subject: [PATCH 086/214] ub:hisi-ubus: Support for processing CQ request messages ANBZ: #45460 commit d7b599123e51f32e3a4e072787034478f58c1158 openEuler. Implement two modes of interrupt and timer polling to handle request messages in the CQ queue. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/vendor/hisilicon/hisi-msg.h | 12 ++ drivers/ub/ubus/vendor/hisilicon/msg-core.c | 88 +++++++++++++ drivers/ub/ubus/vendor/hisilicon/msg.c | 137 ++++++++++++++++++++ 3 files changed, 237 insertions(+) diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h b/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h index f286994b1ab0..a08163c5198d 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h @@ -22,6 +22,7 @@ extern int msg_wait; #define SQ_CI 0xc #define SQ_DEPTH 0x10 #define SQ_STATUS 0x14 +#define SQ_INT_MSK 0x18 #define RQ_ADDR_L 0x40 #define RQ_ADDR_H 0x44 #define RQ_PI 0x48 @@ -35,7 +36,11 @@ extern int msg_wait; #define CQ_CI 0x7c #define CQ_DEPTH 0x80 #define CQ_STATUS 0x84 +#define CQ_INT_MASK 0x88 +#define CQ_INT_STATUS 0x8c +#define CQ_INT_RO 0x90 #define MSGQ_RST 0xB0 +#define MSGQ_INT_SEL 0xC0 #define HI_MSG_RQE_SIZE 0x800 /* 2K */ #define HI_MSG_SQE_PLD_SIZE 0x800 /* 2K */ @@ -135,12 +140,19 @@ struct hi_msg_queue { spinlock_t lock; }; +#define HI_MSG_INT_NAME_LEN 32 struct hi_msg_core { struct device *dev; /* ubc->dev */ phys_addr_t q_addr; /* MSGQ ctx's phy addr & size */ size_t q_size; void __iomem *reg_base; /* MSGQ context virtual address */ + u32 virq; /* if virq == 0, then use poll mode, else int mode */ + u32 intx; + void (*irq_handler)(struct hi_msg_core *hmc); + void (*isr_handler)(struct hi_msg_core *hmc); + char queue_name[HI_MSG_INT_NAME_LEN]; + atomic_t cq_int_mask; int user; struct hi_msg_queue queue[MSGQ_NUM]; }; diff --git a/drivers/ub/ubus/vendor/hisilicon/msg-core.c b/drivers/ub/ubus/vendor/hisilicon/msg-core.c index 2a0331269004..5a1af8fe6e31 100644 --- a/drivers/ub/ubus/vendor/hisilicon/msg-core.c +++ b/drivers/ub/ubus/vendor/hisilicon/msg-core.c @@ -4,6 +4,7 @@ */ #include +#include #include "../../ubus.h" #include "../../msg.h" @@ -115,6 +116,88 @@ static void hi_msg_reset_queue(struct hi_msg_core *hmc) hi_msg_reg_write(hmc, MSGQ_RST, 0); } +static irqreturn_t hi_msg_irq(int irq, void *context) +{ + struct hi_msg_core *hmc = (struct hi_msg_core *)context; + struct hi_msg_queue *cq = &hmc->queue[MSG_CQ]; + unsigned long flags; + u32 ro; + + ro = hi_msg_reg_read(hmc, CQ_INT_RO); + if (!ro) + return IRQ_NONE; + + hi_msg_reg_write(hmc, CQ_INT_MASK, 0x1); + + if (hmc->irq_handler) + hmc->irq_handler(hmc); + + if (hmc->isr_handler) + return IRQ_WAKE_THREAD; + + hi_msg_reg_write(hmc, CQ_INT_STATUS, 0x1); + + spin_lock_irqsave(&cq->lock, flags); + if (cq->pi == cq->ci) { + hi_msg_reg_write(hmc, CQ_INT_STATUS, 0x1); + hi_msg_reg_write(hmc, CQ_INT_MASK, 0x0); + } else { + atomic_set(&hmc->cq_int_mask, 1); + } + spin_unlock_irqrestore(&cq->lock, flags); + + return IRQ_HANDLED; +} + +static irqreturn_t hi_msg_isr(int irq, void *context) +{ + struct hi_msg_core *hmc = (struct hi_msg_core *)context; + + hmc->isr_handler(hmc); + + hi_msg_reg_write(hmc, CQ_INT_STATUS, 0x1); + hi_msg_reg_write(hmc, CQ_INT_MASK, 0x0); + + return IRQ_HANDLED; +} + +static int hi_msg_queue_irq_init(struct hi_msg_core *hmc) +{ + int ret; + + /* Config sq int mask for imp */ + hi_msg_reg_write(hmc, SQ_INT_MSK, 0x0); + + if (!hmc->virq) + return 0; + + ret = request_threaded_irq(hmc->virq, hi_msg_irq, hi_msg_isr, + IRQF_ONESHOT | IRQF_SHARED, + hmc->queue_name, hmc); + if (ret) { + dev_err(hmc->dev, "request msgq irq failed\n"); + return ret; + } + dev_info(hmc->dev, "request irq[%u] succeeded\n", hmc->virq); + hi_msg_reg_write(hmc, MSGQ_INT_SEL, hmc->intx); + hi_msg_reg_write(hmc, CQ_INT_STATUS, 0x1); + hi_msg_reg_write(hmc, CQ_INT_MASK, 0x0); + + return 0; +} + +static void hi_msg_queue_irq_uninit(struct hi_msg_core *hmc) +{ + if (hmc->virq) { + hi_msg_reg_write(hmc, CQ_INT_MASK, 0x1); + hi_msg_reg_write(hmc, CQ_INT_STATUS, 0x1); + + free_irq(hmc->virq, hmc); + } + + hi_msg_reg_write(hmc, SQ_INT_MSK, 0x1); +} + int hi_msg_core_init(struct hi_msg_core *hmc, int user) { int i, j, ret; @@ -135,6 +218,10 @@ int hi_msg_core_init(struct hi_msg_core *hmc, int user) for (i = 0; i < MSGQ_NUM; i++) hi_msg_queue_hw_init(hmc, i); + ret = hi_msg_queue_irq_init(hmc); + if (ret) + goto sw_uninit; /* Now i = MSGQ_NUM */ + return 0; sw_uninit: hi_msg_reset_queue(hmc); @@ -150,6 +237,7 @@ void hi_msg_core_uninit(struct hi_msg_core *hmc) { int i; + hi_msg_queue_irq_uninit(hmc); hi_msg_reset_queue(hmc); for (i = 0; i < MSGQ_NUM; i++) diff --git a/drivers/ub/ubus/vendor/hisilicon/msg.c b/drivers/ub/ubus/vendor/hisilicon/msg.c index b12c29e6f44a..7ace5a281e64 100644 --- a/drivers/ub/ubus/vendor/hisilicon/msg.c +++ b/drivers/ub/ubus/vendor/hisilicon/msg.c @@ -13,12 +13,16 @@ #include "vdm.h" #include "hisi-msg.h" +#define MSGQ_INT1 1 +#define to_ub_bus_controller(d) container_of(d, struct ub_bus_controller, dev) + struct hi_msg_sqe_pld { char packet[HI_MSG_SQE_PLD_SIZE]; }; struct hi_message_device { struct hi_msg_core hmc; + struct timer_list poll_timer; struct hi_cqe_state *cqe_state; atomic_t msg_in_flight_cnt; struct message_device mdev; @@ -41,6 +45,8 @@ static inline void cqe_state_set(struct hi_message_device *hmd, int idx, } #define cqe_state_get(hmd, idx) ((hmd)->cqe_state[idx].state) +#define HI_MSG_CQ_POLL_PERIOD 100 + #define MSG_MAX (HI_SQ_CFG_DEPTH - 1) #define q_left_cnt(q) ((q)->depth - q_used_cnt((q)) - 1) #define sq_sw_left(hmd) (MSG_MAX - atomic_read(&(hmd)->msg_in_flight_cnt)) @@ -216,6 +222,27 @@ static int hi_msg_sync_wait(struct hi_message_device *hmd, int task_type, return -ETIMEDOUT; } +static bool hi_msg_has_rx(struct hi_message_device *hmd, int ci, int pi) +{ + struct hi_msg_queue *cq = &hmd->hmc.queue[MSG_CQ]; + struct hi_msg_core *hmc = &hmd->hmc; + struct hi_msg_cqe *cqe; + int depth, cnt, i, idx; + + depth = hmc->queue[MSG_CQ].depth; + cnt = (pi + depth - ci) % depth; + + for (i = 0; i < cnt; i++) { + idx = q_ptr_idx(cq, ci, i); + cqe = cq_entry(hmc, idx); + if (cqe_state_get(hmd, idx) != CQ_SW_HANDLED && + cqe->task_type == PROTOCOL_MSG && cqe->type == MSG_REQ) + return true; + } + + return false; +} + static void hi_msg_cq_update(struct hi_message_device *hmd) { struct hi_msg_queue *cq = &hmd->hmc.queue[MSG_CQ]; @@ -223,6 +250,7 @@ static void hi_msg_cq_update(struct hi_message_device *hmd) int i, cnt, idx, release_cnt = 0; struct hi_msg_cqe *cqe; unsigned long flags; + u32 cq_pi; spin_lock_irqsave(&cq->lock, flags); if (cq->ci == cq->pi) { @@ -255,7 +283,98 @@ static void hi_msg_cq_update(struct hi_message_device *hmd) wmb(); /* Ensure the register is written correctly. */ hi_msg_reg_write(hmc, CQ_CI, cq->ci); atomic_sub(release_cnt, &hmd->msg_in_flight_cnt); + + if (atomic_read(&hmc->cq_int_mask)) { + /* Get newest pi, but not store in cq->pi */ + cq_pi = hi_msg_reg_read(hmc, CQ_PI); + /* Check really need open interrupt */ + if (cq_pi == cq->ci || hi_msg_has_rx(hmd, cq->ci, cq_pi)) { + hi_msg_reg_write(hmc, CQ_INT_STATUS, 0x1); + /* spi line interrupt, unmask during _irqsave no impact */ + hi_msg_reg_write(hmc, CQ_INT_MASK, 0x0); + atomic_set(&hmc->cq_int_mask, 0); + } + } + + spin_unlock_irqrestore(&cq->lock, flags); +} + +static int hi_msg_cq_poller(struct hi_message_device *hmd) +{ + struct ub_bus_controller *ubc = to_ub_bus_controller(hmd->hmc.dev); + struct hi_msg_queue *cq = &hmd->hmc.queue[MSG_CQ]; + int cqe_cnt, i, idx, ret, handled_cnt = 0; + struct hi_msg_core *hmc = &hmd->hmc; + struct hi_msg_cqe *cqe; + unsigned long flags; + + spin_lock_irqsave(&cq->lock, flags); + cq->pi = hi_msg_reg_read(hmc, CQ_PI); + if (cq->pi == cq->ci) { + spin_unlock_irqrestore(&cq->lock, flags); + return -EAGAIN; + } + + cqe_cnt = q_used_cnt(cq); + for (i = 0; i < cqe_cnt; i++) { + idx = q_ptr_idx(cq, ci, i); + cqe = cq_entry(hmc, idx); + + if (cqe_state_get(hmd, idx) == CQ_SW_HANDLED) + continue; + + /* Now, just msg type has rx */ + if (cqe->task_type != PROTOCOL_MSG || cqe->type != MSG_REQ) + continue; + + if (cqe->p_len > HI_MSG_RQE_SIZE) { + dev_err(hmc->dev, "poller cqe p_len invalid\n"); + goto handled; + } + + ret = message_rx_handler(ubc, rq_entry(hmc, cqe->rq_pi), + cqe->p_len); + if (ret) + dev_err(hmc->dev, "poller rx msg failed, ret=%d\n", ret); +handled: + cqe_state_set(hmd, idx, CQ_SW_HANDLED); + handled_cnt++; + } + spin_unlock_irqrestore(&cq->lock, flags); + return handled_cnt; +} + +static void hi_msg_cq_handle_poll_timer(struct timer_list *timer) +{ + struct hi_message_device *hmd; + + hmd = container_of(timer, struct hi_message_device, poll_timer); + if (hi_msg_cq_poller(hmd) > 0) + hi_msg_cq_update(hmd); + mod_timer(timer, jiffies + msecs_to_jiffies(HI_MSG_CQ_POLL_PERIOD)); +} + +static void hi_msg_cq_poller_init(struct hi_message_device *hmd) +{ + if (hmd->hmc.virq) + return; + + struct timer_list *poll_timer = &hmd->poll_timer; + + timer_setup(poll_timer, hi_msg_cq_handle_poll_timer, 0); + poll_timer->expires = jiffies + msecs_to_jiffies(HI_MSG_CQ_POLL_PERIOD); + add_timer(poll_timer); +} + +static void hi_msg_cq_poller_uninit(struct hi_message_device *hmd) +{ + if (hmd->hmc.virq) + return; + + struct timer_list *poll_timer = &hmd->poll_timer; + + timer_shutdown_sync(poll_timer); } static int hi_msg_queue_init(struct hi_message_device *hmd) @@ -275,6 +394,9 @@ static int hi_msg_queue_init(struct hi_message_device *hmd) ret = -ENOMEM; goto cqe_state_fail; } + + hi_msg_cq_poller_init(hmd); + return 0; cqe_state_fail: @@ -284,6 +406,7 @@ static int hi_msg_queue_init(struct hi_message_device *hmd) static void hi_msg_queue_uninit(struct hi_message_device *hmd) { + hi_msg_cq_poller_uninit(hmd); kfree(hmd->cqe_state); hi_msg_core_uninit(&hmd->hmc); } @@ -466,6 +589,14 @@ static struct message_ops hi_message_ops = { .send = hi_message_send, }; +static void hi_bus_drv_msg_irq_handler(struct hi_msg_core *hmc) +{ + struct hi_message_device *hmd = + container_of(hmc, struct hi_message_device, hmc); + if (hi_msg_cq_poller(hmd) > 0) + hi_msg_cq_update(hmd); +} + int hi_msg_device_probe(struct ub_bus_controller *ubc) { struct device *dev = &ubc->dev; @@ -481,6 +612,12 @@ int hi_msg_device_probe(struct ub_bus_controller *ubc) hmc->dev = dev; hmc->q_addr = ubc->attr.queue_addr; hmc->q_size = HI_MSGQ_SIZE; + hmc->virq = ubc->queue_virq; + hmc->intx = MSGQ_INT1; + hmc->irq_handler = hi_bus_drv_msg_irq_handler; + snprintf(hmc->queue_name, HI_MSG_INT_NAME_LEN, "hi_msgq%u-%d", + ubc->ctl_no, MSGQ_USER_BUS_DRV); + ret = hi_msg_queue_init(hmd); if (ret) { -- Gitee From 139da155a4ad496883df450c5bda683eaa080a2e Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Tue, 23 Sep 2025 10:55:36 +0800 Subject: [PATCH 087/214] ub:hisi-ubus: Support MSGQ queue exception handling ANBZ: #45460 commit 9940dcf73ef4d37611fb4f12f4774acc8004381a openEuler. Implementing timeout message handling by utilizing a timer mechanism to address various timeout message scenarios. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/vendor/hisilicon/msg.c | 184 ++++++++++++++++++++++++- 1 file changed, 183 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubus/vendor/hisilicon/msg.c b/drivers/ub/ubus/vendor/hisilicon/msg.c index 7ace5a281e64..a550d96296d8 100644 --- a/drivers/ub/ubus/vendor/hisilicon/msg.c +++ b/drivers/ub/ubus/vendor/hisilicon/msg.c @@ -20,9 +20,20 @@ struct hi_msg_sqe_pld { char packet[HI_MSG_SQE_PLD_SIZE]; }; +struct timeout_msg_info { + struct list_head node; + u16 msn; + u8 task_type; + u8 age; +}; +#define TIMEOUT_MSG_INFO_SZ sizeof(struct timeout_msg_info) + struct hi_message_device { struct hi_msg_core hmc; struct timer_list poll_timer; + struct timer_list timeout_msg_timer; + struct list_head timeout_msg_list; + spinlock_t timeout_msg_lock; struct hi_cqe_state *cqe_state; atomic_t msg_in_flight_cnt; struct message_device mdev; @@ -35,6 +46,7 @@ enum hi_cq_sw_state { CQ_SW_INIT, CQ_SW_HANDLED }; struct hi_cqe_state { u8 state; + u8 age; }; #define HI_CQE_STATE_SZ sizeof(struct hi_cqe_state) @@ -42,10 +54,15 @@ static inline void cqe_state_set(struct hi_message_device *hmd, int idx, u8 state) { hmd->cqe_state[idx].state = state; + hmd->cqe_state[idx].age = 0; } #define cqe_state_get(hmd, idx) ((hmd)->cqe_state[idx].state) +#define cqe_age_inc(hmd, idx) (((hmd)->cqe_state[idx].age)++) +#define cqe_age_get(hmd, idx) ((hmd)->cqe_state[idx].age) -#define HI_MSG_CQ_POLL_PERIOD 100 +#define HI_MSG_CQ_POLL_PERIOD 100 +#define HI_TIMEOUT_MSG_POLL_PERIOD 500 +#define HI_MSG_AGING_PERIOD 2 #define MSG_MAX (HI_SQ_CFG_DEPTH - 1) #define q_left_cnt(q) ((q)->depth - q_used_cnt((q)) - 1) @@ -206,7 +223,9 @@ static int hi_msg_sync_wait(struct hi_message_device *hmd, int task_type, #define SLEEP_MIN_US 1000 #define SLEEP_MAX_US 2000 u64 end_time = get_jiffies_64() + duration; + struct timeout_msg_info *timeout_msg; struct hi_msg_core *hmc = &hmd->hmc; + unsigned long flags; int idx; while (!time_after64(get_jiffies_64(), end_time)) { @@ -218,6 +237,15 @@ static int hi_msg_sync_wait(struct hi_message_device *hmd, int task_type, usleep_range(SLEEP_MIN_US, SLEEP_MAX_US); } + timeout_msg = kzalloc(TIMEOUT_MSG_INFO_SZ, GFP_ATOMIC); + if (!timeout_msg) + return -ENOMEM; + + timeout_msg->task_type = task_type; + timeout_msg->msn = msn; + spin_lock_irqsave(&hmd->timeout_msg_lock, flags); + list_add_tail(&timeout_msg->node, &hmd->timeout_msg_list); + spin_unlock_irqrestore(&hmd->timeout_msg_lock, flags); dev_err(hmc->dev, "task %d msn %#x wait cqe timeout\n", task_type, msn); return -ETIMEDOUT; } @@ -597,6 +625,156 @@ static void hi_bus_drv_msg_irq_handler(struct hi_msg_core *hmc) hi_msg_cq_update(hmd); } +static void hi_timeout_msg_ageing(struct hi_message_device *hmd) +{ + struct timeout_msg_info *msg, *tmp; + + list_for_each_entry_safe(msg, tmp, &hmd->timeout_msg_list, node) { + if (msg->age >= HI_MSG_AGING_PERIOD) { + hi_msn_put(msg->task_type, msg->msn); + atomic_sub(1, &hmd->msg_in_flight_cnt); + list_del(&msg->node); + dev_err(hmd->hmc.dev, + "release aged timeout type=%u msn=%#x\n", + msg->task_type, msg->msn); + kfree(msg); + continue; + } + msg->age++; + } +} + +static bool hi_cqe_ageing(struct hi_message_device *hmd, int idx) +{ + struct hi_msg_core *hmc = &hmd->hmc; + struct ub_bus_controller *ubc; + struct hi_msg_cqe *cqe; + int ret; + + if (cqe_state_get(hmd, idx) == CQ_SW_HANDLED) + return false; + + if (cqe_age_get(hmd, idx) < HI_MSG_AGING_PERIOD) { + cqe_age_inc(hmd, idx); + return false; + } + + cqe = cq_entry(hmc, idx); + if (cqe->task_type == PROTOCOL_MSG && cqe->type == MSG_REQ) { + ubc = to_ub_bus_controller(hmc->dev); + + if (cqe->p_len > HI_MSG_RQE_SIZE) { + dev_err(hmc->dev, "ageing cqe p_len invalid\n"); + return true; + } + + ret = message_rx_handler(ubc, rq_entry(hmc, cqe->rq_pi), + cqe->p_len); + if (ret) + dev_err(hmc->dev, "rx msg failed, ret=%d\n", ret); + + dev_warn(hmc->dev, "interrupt dont up, process unhandled cqe, idx=%d type=%u msn=%#x opcode=%#x\n", + idx, cqe->task_type, cqe->msn, cqe->opcode); + } else { + dev_err(hmc->dev, "reset unhandled cqe, idx=%d type=%u msn=%#x\n", + idx, cqe->task_type, cqe->msn); + } + + return true; +} + +static bool hi_is_timeout_msg(struct hi_message_device *hmd, int idx) +{ + struct timeout_msg_info *msg, *tmp; + struct hi_msg_core *hmc = &hmd->hmc; + struct hi_msg_cqe *cqe; + + cqe = cq_entry(hmc, idx); + + list_for_each_entry_safe(msg, tmp, &hmd->timeout_msg_list, node) { + if (cqe->msn == msg->msn && cqe->task_type == msg->task_type && + (cqe->task_type != PROTOCOL_MSG || cqe->type == MSG_RSP)) { + list_del(&msg->node); + dev_err(hmc->dev, + "Timeout Message Processed, task=%u msn=%#x\n", + msg->task_type, msg->msn); + kfree(msg); + return true; + } + } + return false; +} + +static int hi_msg_timeout_poller(struct hi_message_device *hmd) +{ + unsigned long cq_flags, timeout_msg_flags; + struct hi_msg_queue *cq; + int handled_cnt = 0; + int cqe_cnt, i, idx; + + cq = &hmd->hmc.queue[MSG_CQ]; + spin_lock_irqsave(&cq->lock, cq_flags); + spin_lock_irqsave(&hmd->timeout_msg_lock, timeout_msg_flags); + cq->pi = hi_msg_reg_read(&hmd->hmc, CQ_PI); + + hi_timeout_msg_ageing(hmd); + + if (cq->pi == cq->ci) { + spin_unlock_irqrestore(&hmd->timeout_msg_lock, + timeout_msg_flags); + spin_unlock_irqrestore(&cq->lock, cq_flags); + return 0; + } + + cqe_cnt = q_used_cnt(cq); + for (i = 0; i < cqe_cnt; i++) { + idx = q_ptr_idx(cq, ci, i); + if (hi_cqe_ageing(hmd, idx) || hi_is_timeout_msg(hmd, idx)) { + cqe_state_set(hmd, idx, CQ_SW_HANDLED); + handled_cnt++; + } + } + + spin_unlock_irqrestore(&hmd->timeout_msg_lock, timeout_msg_flags); + spin_unlock_irqrestore(&cq->lock, cq_flags); + return handled_cnt; +} + +static void hi_timeout_msg_handle(struct timer_list *timer) +{ + struct hi_message_device *hmd; + + hmd = container_of(timer, struct hi_message_device, timeout_msg_timer); + if (hi_msg_timeout_poller(hmd) > 0) + hi_msg_cq_update(hmd); + mod_timer(timer, + jiffies + msecs_to_jiffies(HI_TIMEOUT_MSG_POLL_PERIOD)); +} + +static void hi_timeout_msg_poller_init(struct hi_message_device *hmd) +{ + struct timer_list *timer = &hmd->timeout_msg_timer; + + INIT_LIST_HEAD(&hmd->timeout_msg_list); + spin_lock_init(&hmd->timeout_msg_lock); + timer_setup(timer, hi_timeout_msg_handle, 0); + timer->expires = jiffies + msecs_to_jiffies(HI_TIMEOUT_MSG_POLL_PERIOD); + add_timer(timer); +} + +static void hi_timeout_msg_poller_uninit(struct hi_message_device *hmd) +{ + struct timer_list *timer = &hmd->timeout_msg_timer; + struct timeout_msg_info *time_out_msg, *tmp; + + timer_shutdown_sync(timer); + list_for_each_entry_safe(time_out_msg, tmp, &hmd->timeout_msg_list, + node) { + list_del(&time_out_msg->node); + kfree(time_out_msg); + } +} + int hi_msg_device_probe(struct ub_bus_controller *ubc) { struct device *dev = &ubc->dev; @@ -625,6 +803,8 @@ int hi_msg_device_probe(struct ub_bus_controller *ubc) goto queue_init_fail; } + hi_timeout_msg_poller_init(hmd); + message_device_set_ops(&hmd->mdev, &hi_message_ops); message_device_set_fwnode(&hmd->mdev, dev->fwnode); @@ -639,6 +819,7 @@ int hi_msg_device_probe(struct ub_bus_controller *ubc) return 0; mdev_reg_fail: + hi_timeout_msg_poller_uninit(hmd); hi_msg_queue_uninit(hmd); queue_init_fail: kfree(hmd); @@ -651,6 +832,7 @@ void hi_msg_device_remove(struct ub_bus_controller *ubc) ubc->mdev = NULL; message_device_unregister(&hmd->mdev); + hi_timeout_msg_poller_uninit(hmd); hi_msg_queue_uninit(hmd); kfree(hmd); } -- Gitee From 3f0ce719c1db7fd213424006a6ccb72a737adf9e Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Tue, 23 Sep 2025 11:09:51 +0800 Subject: [PATCH 088/214] ub:hisi-ubus: Support for MSGQ's debugging functionality ANBZ: #45460 commit 850eb602b102d0288abae898a31cc9f36ea80c6b openEuler. Implementing MSGQ's debugfs, queue pointer status, exception log, and other DFX functionalities. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/vendor/hisilicon/Makefile | 1 + drivers/ub/ubus/vendor/hisilicon/hisi-msg.h | 4 + drivers/ub/ubus/vendor/hisilicon/msg-core.c | 3 + .../ub/ubus/vendor/hisilicon/msg-debugfs.c | 296 ++++++++++++++++++ drivers/ub/ubus/vendor/hisilicon/msg.c | 40 ++- 5 files changed, 341 insertions(+), 3 deletions(-) create mode 100644 drivers/ub/ubus/vendor/hisilicon/msg-debugfs.c diff --git a/drivers/ub/ubus/vendor/hisilicon/Makefile b/drivers/ub/ubus/vendor/hisilicon/Makefile index 6b4dac3a604e..255cb0ddfa8e 100644 --- a/drivers/ub/ubus/vendor/hisilicon/Makefile +++ b/drivers/ub/ubus/vendor/hisilicon/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ hisi_ubus-objs := hisi-ubus.o controller.o vdm.o local-ras.o msg.o msg-core.o +hisi_ubus-objs += msg-debugfs.o obj-$(CONFIG_UB_HISI_UBUS) += hisi_ubus.o diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h b/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h index a08163c5198d..c0bc9a043796 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h @@ -184,5 +184,9 @@ int hi_message_sync_request_sched(struct message_device *mdev, struct msg_info *info, u8 opcode); int hi_message_private(struct message_device *mdev, struct msg_info *info, u8 opcode); +void hi_msg_debugfs_init(struct hi_msg_core *hmc); +void hi_msg_debugfs_uninit(struct hi_msg_core *hmc); +void ub_msg_dump_sq(struct hi_msg_sqe *sqe, void *sq_pld); +void ub_msg_dump_cq(struct hi_msg_cqe *cqe, void *rqe); #endif /* __HISI_MSG_H__ */ diff --git a/drivers/ub/ubus/vendor/hisilicon/msg-core.c b/drivers/ub/ubus/vendor/hisilicon/msg-core.c index 5a1af8fe6e31..36b7b5cfa560 100644 --- a/drivers/ub/ubus/vendor/hisilicon/msg-core.c +++ b/drivers/ub/ubus/vendor/hisilicon/msg-core.c @@ -222,6 +222,8 @@ int hi_msg_core_init(struct hi_msg_core *hmc, int user) if (ret) goto sw_uninit; /* Now i = MSGQ_NUM */ + hi_msg_debugfs_init(hmc); + return 0; sw_uninit: hi_msg_reset_queue(hmc); @@ -237,6 +239,7 @@ void hi_msg_core_uninit(struct hi_msg_core *hmc) { int i; + hi_msg_debugfs_uninit(hmc); hi_msg_queue_irq_uninit(hmc); hi_msg_reset_queue(hmc); diff --git a/drivers/ub/ubus/vendor/hisilicon/msg-debugfs.c b/drivers/ub/ubus/vendor/hisilicon/msg-debugfs.c new file mode 100644 index 000000000000..495493188730 --- /dev/null +++ b/drivers/ub/ubus/vendor/hisilicon/msg-debugfs.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus hisi msg-d: " fmt + +#include +#include "../../ubus.h" +#include "../../msg.h" +#include "../../enum.h" +#include "hisi-msg.h" +#include "hisi-ubus.h" + +#define REG_NAME_LEN 16 +struct hi_msg_reg_info_s { + char name[REG_NAME_LEN]; + u16 offset; +}; + +static struct hi_msg_reg_info_s hi_msg_reg_info[] = { + {"sq_pi", SQ_PI}, + {"sq_ci", SQ_CI}, + {"sq_depth", SQ_DEPTH}, + {"sq_int_mask", SQ_INT_MSK}, + {"rq_pi", RQ_PI}, + {"rq_ci", RQ_CI}, + {"rq_depth", RQ_DEPTH}, + {"rq_entry_size", RQ_ENTRY_SIZE}, + {"cq_pi", CQ_PI}, + {"cq_ci", CQ_CI}, + {"cq_depth", CQ_DEPTH}, + {"cq_int_mask", CQ_INT_MASK}, + {"cq_int_status", CQ_INT_STATUS}, + {"cq_int_ro", CQ_INT_RO}, + {"q_int_sel", MSGQ_INT_SEL} +}; + +static ssize_t hi_msg_reg_info_read(struct file *file, char __user *ubuf, + size_t size, loff_t *loff) +{ + struct hi_msg_core *hmc = + (struct hi_msg_core *)file->f_inode->i_private; + struct hi_msg_reg_info_s *reg; + size_t len = 0; + int ret, i; + char *buf; + + buf = kzalloc(SZ_4K, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + reg = hi_msg_reg_info; + for (i = 0; i < ARRAY_SIZE(hi_msg_reg_info); i++) { + len += (size_t)scnprintf(buf + len, SZ_4K - len, "%s:%#x\n", + reg->name, + hi_msg_reg_read(hmc, reg->offset)); + reg++; + } + + ret = simple_read_from_buffer(ubuf, size, loff, buf, len); + kfree(buf); + return ret; +} + +static const struct file_operations hi_msg_reg_info_ops = { + .read = hi_msg_reg_info_read, +}; + +static int q_num; +static u32 q_idx; +static DEFINE_MUTEX(q_lock); + +static size_t hi_msg_buf_assemble(char *buf, size_t size, u32 *addr, int count) +{ + int i, len = 0; + +#define DW_EACH_LINE 4 + for (i = 0; i < count; i++) { + if (i % DW_EACH_LINE == 0) + len += scnprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, " %08x", *(addr + i)); + } + + len += scnprintf(buf + len, size - len, "\n"); + + return len; +} + +static ssize_t hi_msg_q_entry_info_read(struct file *file, char __user *ubuf, + size_t size, loff_t *loff) +{ + struct hi_msg_core *hmc = + (struct hi_msg_core *)file->f_inode->i_private; + int ret, num, entry_size; + struct hi_msg_sqe *sqe; + struct hi_msg_cqe *cqe; + u32 *entry, idx; + size_t len = 0; + char *buf; + + buf = kzalloc(SZ_8K, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&q_lock); + num = q_num; + idx = q_idx; + mutex_unlock(&q_lock); + + switch (num) { + case MSG_SQ: + sqe = &hmc->queue[num].sqe[idx]; + + entry = (u32 *)sqe; + entry_size = HI_MSG_SQE_SIZE; + break; + case MSG_CQ: + default: + cqe = &hmc->queue[num].cqe[idx]; + + entry = (u32 *)cqe; + entry_size = HI_MSG_CQE_SIZE; + break; + } + + len += (size_t)scnprintf(buf + len, SZ_8K - len, "%s[%#x]:", + (num == MSG_CQ) ? "cq" : "sq", idx); + len += hi_msg_buf_assemble(buf + len, SZ_8K - len, entry, + entry_size / sizeof(u32)); + + ret = simple_read_from_buffer(ubuf, size, loff, buf, len); + kfree(buf); + return ret; +} + +static ssize_t hi_msg_q_entry_info_write(struct file *file, + const char __user *ubuf, + size_t size, loff_t *loff) +{ + struct hi_msg_core *hmc = + (struct hi_msg_core *)file->f_inode->i_private; +#define BUF_SIZE 32 + int len, num, fields; + char buf[BUF_SIZE]; + u32 idx; + + if (*loff != 0) + return 0; + + if (size >= BUF_SIZE) + return -ENOSPC; + + len = simple_write_to_buffer(buf, BUF_SIZE - 1, loff, ubuf, size); + if (len < 0) + return len; + + buf[len] = '\0'; + fields = sscanf(buf, "%d %x", &num, &idx); + if (fields != 2) + return -EINVAL; + + if ((num != MSG_SQ && num != MSG_CQ) || idx >= hmc->queue[num].depth) { + pr_err("msgq num %d or queue entry idx %#x is invalid\n", + num, idx); + return -EINVAL; + } + + mutex_lock(&q_lock); + q_num = num; + q_idx = idx; + mutex_unlock(&q_lock); + return size; +} + +static const struct file_operations hi_msg_q_entry_info_ops = { + .read = hi_msg_q_entry_info_read, + .write = hi_msg_q_entry_info_write, +}; + +void hi_msg_debugfs_init(struct hi_msg_core *hmc) +{ + struct ub_bus_controller *ubc = container_of(hmc->dev, struct ub_bus_controller, + dev); + struct dentry *dir; + + dir = debugfs_create_dir(hmc->queue_name, ubc->debug_root); + debugfs_create_file("reg_info", 0400, dir, hmc, + &hi_msg_reg_info_ops); + debugfs_create_file("q_entry_info", 0600, dir, + hmc, &hi_msg_q_entry_info_ops); +} + +void hi_msg_debugfs_uninit(struct hi_msg_core *hmc) +{ + struct ub_bus_controller *ubc = container_of(hmc->dev, struct ub_bus_controller, + dev); + + debugfs_lookup_and_remove(hmc->queue_name, ubc->debug_root); +} + +static void hi_msg_sqe_dump(struct hi_msg_sqe *sqe) +{ + pr_err("sqe:\n"); + pr_err("type=%u local=%u dev=%u icrc=%u len=%#x msn=%#x off=%#x\n", + sqe->task_type, sqe->local, sqe->dev_type, sqe->icrc, + sqe->p_len, sqe->msn, sqe->p_addr); + + if (sqe->task_type == PROTOCOL_MSG) + pr_err("code=%#x sub=%#x rsp=%u\n", sqe->msg_code, + sqe->sub_msg_code, sqe->type); + else + pr_err("opcode=%#x\n", sqe->opcode); +} + +static void hi_msg_cqe_dump(struct hi_msg_cqe *cqe) +{ + pr_err("cqe:\n"); + pr_err("type=%u plen=%#x msn=%#x pi=%#x s=%u\n", cqe->task_type, + cqe->p_len, cqe->msn, cqe->rq_pi, cqe->status); + + if (cqe->task_type == PROTOCOL_MSG) + pr_err("code=%#x sub=%#x rsp=%u\n", cqe->msg_code, + cqe->sub_msg_code, cqe->type); + else + pr_err("opcode=%#x\n", cqe->opcode); +} + +static void hi_msg_header_dump(void *buf) +{ + struct msg_pkt_header *h = (struct msg_pkt_header *)buf; + struct msg_extended_header *eh = &h->msgetah; + struct compact_network_header *nh = &h->nth; + struct ub_link_header *lh = &h->ulh; + + pr_err("msg header:\n"); + pr_err("cfg=%u dcna=%#x scna=%#x nlp=%u mgmt=%u\n", + lh->cfg, nh->dcna, nh->scna, nh->nth_nlp, nh->mgmt); + pr_err("tp_opcode=%u tp_nlp=%u upi=%#x seid=%#x deid=%#x\n", + h->tp_opcode, + h->ctph_nlp, h->upi, eid_gen(h->seid_h, h->seid_l), h->deid); + pr_err("msn=%#x ta_opcode=%#x msgq_id=%u\n", h->src_tassn, h->ta_opcode, + h->msgq_id); + pr_err("code=%#x sub=%#x rsp=%u plen=%#x s=%u\n", eh->msg_code, + eh->sub_msg_code, eh->type, eh->plen, eh->rsp_status); +} + +static void hi_enum_header_dump(void *buf) +{ + struct enum_pkt_header *h = (struct enum_pkt_header *)buf; + struct compact_network_header *nh = &h->cnth; + struct ub_link_header *lh = &h->ulh; + + pr_err("enum header:\n"); + pr_err("cfg=%u dcna=%#x scna=%#x nlp=%u mgmt=%u upi=%#x\n", + lh->cfg, nh->dcna, nh->scna, nh->nth_nlp, nh->mgmt, h->upi); +} + +static void hi_msg_pld_dump(void *buf, int task_type, int len) +{ + switch (task_type) { + case PROTOCOL_MSG: + if (len < MSG_PKT_HEADER_SIZE) + return; + hi_msg_header_dump(buf); + break; + case PROTOCOL_ENUM: + if (len < ENUM_PKT_HEADER_SIZE) + return; + hi_enum_header_dump(buf); + break; + case HISI_PRIVATE: + /* Don't print */ + break; + default: + pr_err("bad task_type:%d\n", task_type); + } +} + +void ub_msg_dump_sq(struct hi_msg_sqe *sqe, void *sq_pld) +{ + if (sqe) + hi_msg_sqe_dump(sqe); + + if (sq_pld && sqe) + hi_msg_pld_dump(sq_pld, sqe->task_type, sqe->p_len); +} + +void ub_msg_dump_cq(struct hi_msg_cqe *cqe, void *rqe) +{ + if (cqe) + hi_msg_cqe_dump(cqe); + + if (rqe && cqe) + hi_msg_pld_dump(rqe, cqe->task_type, cqe->p_len); +} diff --git a/drivers/ub/ubus/vendor/hisilicon/msg.c b/drivers/ub/ubus/vendor/hisilicon/msg.c index a550d96296d8..178682aa5cd6 100644 --- a/drivers/ub/ubus/vendor/hisilicon/msg.c +++ b/drivers/ub/ubus/vendor/hisilicon/msg.c @@ -153,6 +153,27 @@ static void hi_msn_put(int type, u16 msn) spin_unlock_irqrestore(lock, flags); } +static void hi_msgq_hw_status(struct hi_msg_core *hmc) +{ + dev_err_ratelimited( + hmc->dev, "sq pi=%#04x, ci=%#04x\ncq pi=%#04x, ci=%#04x\nrq pi=%#04x, ci=%#04x\n", + hi_msg_reg_read(hmc, SQ_PI), hi_msg_reg_read(hmc, SQ_CI), + hi_msg_reg_read(hmc, CQ_PI), hi_msg_reg_read(hmc, CQ_CI), + hi_msg_reg_read(hmc, RQ_PI), hi_msg_reg_read(hmc, RQ_CI)); +} + +static void hi_msgq_sw_status(struct hi_message_device *hmd) +{ + dev_err_ratelimited(hmd->hmc.dev, "sq in flight msg:%d\n", + atomic_read(&hmd->msg_in_flight_cnt)); +} + +static void hi_msgq_status(struct hi_message_device *hmd) +{ + hi_msgq_hw_status(&hmd->hmc); + hi_msgq_sw_status(hmd); +} + static int hi_msg_get_sq_idle_num(struct hi_message_device *hmd, int num, bool tx) { @@ -357,13 +378,16 @@ static int hi_msg_cq_poller(struct hi_message_device *hmd) if (cqe->p_len > HI_MSG_RQE_SIZE) { dev_err(hmc->dev, "poller cqe p_len invalid\n"); + ub_msg_dump_cq(cqe, NULL); goto handled; } ret = message_rx_handler(ubc, rq_entry(hmc, cqe->rq_pi), cqe->p_len); - if (ret) + if (ret) { dev_err(hmc->dev, "poller rx msg failed, ret=%d\n", ret); + ub_msg_dump_cq(cqe, rq_entry(hmc, cqe->rq_pi)); + } handled: cqe_state_set(hmd, idx, CQ_SW_HANDLED); handled_cnt++; @@ -502,6 +526,7 @@ static int hi_message_sync(struct message_device *mdev, struct msg_info *info, hmd, &sqe, (struct hi_msg_sqe_pld *)info->req_packet, 1, true); if (cnt != 1) { dev_err(hmc->dev, "sq submit failed\n"); + hi_msgq_status(hmd); hi_msn_put(task_type, msn); return -ENOSPC; } @@ -510,6 +535,7 @@ static int hi_message_sync(struct message_device *mdev, struct msg_info *info, if (cq_idx < 0) { if (cq_idx == -ENOMEM) hi_msn_put(task_type, msn); + ub_msg_dump_sq(&sqe, info->req_packet); return cq_idx; } @@ -518,6 +544,9 @@ static int hi_message_sync(struct message_device *mdev, struct msg_info *info, if (!ret) { hi_msg_rqe_get(hmc, info->rsp_packet, cqe); info->actual_rsp_size = cqe->p_len; + } else { + ub_msg_dump_sq(&sqe, info->req_packet); + ub_msg_dump_cq(cqe, NULL); } cqe_state_set(hmd, cq_idx, CQ_SW_HANDLED); @@ -555,7 +584,7 @@ static int hi_message_response(struct message_device *mdev, cnt = hi_msg_sq_submit( hmd, &sqe, (struct hi_msg_sqe_pld *)info->req_packet, 1, false); if (cnt != 1) { - dev_err(hmd->hmc.dev, "async sq submit failed\n"); + dev_err(hmd->hmc.dev, "rsp sq submit failed\n"); return -ENOSPC; } @@ -587,6 +616,7 @@ static int hi_message_send(struct message_device *mdev, struct msg_info *info, hmd, &sqe, (struct hi_msg_sqe_pld *)info->req_packet, 1, false); if (cnt != 1) { dev_err(hmc->dev, "sq submit failed\n"); + hi_msgq_status(hmd); ret = -ENOSPC; } @@ -665,19 +695,23 @@ static bool hi_cqe_ageing(struct hi_message_device *hmd, int idx) if (cqe->p_len > HI_MSG_RQE_SIZE) { dev_err(hmc->dev, "ageing cqe p_len invalid\n"); + ub_msg_dump_cq(cqe, NULL); return true; } ret = message_rx_handler(ubc, rq_entry(hmc, cqe->rq_pi), cqe->p_len); - if (ret) + if (ret) { dev_err(hmc->dev, "rx msg failed, ret=%d\n", ret); + ub_msg_dump_cq(cqe, rq_entry(hmc, cqe->rq_pi)); + } dev_warn(hmc->dev, "interrupt dont up, process unhandled cqe, idx=%d type=%u msn=%#x opcode=%#x\n", idx, cqe->task_type, cqe->msn, cqe->opcode); } else { dev_err(hmc->dev, "reset unhandled cqe, idx=%d type=%u msn=%#x\n", idx, cqe->task_type, cqe->msn); + ub_msg_dump_cq(cqe, rq_entry(hmc, cqe->rq_pi)); } return true; -- Gitee From b9ca01703ac5fe2357fb4034464b85651c979ba0 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Tue, 23 Sep 2025 11:36:01 +0800 Subject: [PATCH 089/214] ub:hisi-ubus: Support UBUS vdm entity enable message ANBZ: #45460 commit 86fec00cb73a21ef8b033be42774133508ce95b8 openEuler. Support UBUS to send entity enable vendor defined message. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/vendor/hisilicon/controller.c | 1 + drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h | 1 + drivers/ub/ubus/vendor/hisilicon/vdm.c | 68 +++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/drivers/ub/ubus/vendor/hisilicon/controller.c b/drivers/ub/ubus/vendor/hisilicon/controller.c index d0bb7ed6fad7..be59baa71da8 100644 --- a/drivers/ub/ubus/vendor/hisilicon/controller.c +++ b/drivers/ub/ubus/vendor/hisilicon/controller.c @@ -15,6 +15,7 @@ static struct ub_bus_controller_ops hi_ubc_ops = { .register_decoder_base_addr = hi_register_decoder_base_addr, + .entity_enable = hi_send_entity_enable_msg, }; void hi_register_decoder_base_addr(struct ub_bus_controller *ubc, u64 *cmd_queue, diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h index f0e98a5abbe8..90a157e4b8d2 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h @@ -34,6 +34,7 @@ struct hi_ubc_private_data { void hi_register_decoder_base_addr(struct ub_bus_controller *ubc, u64 *cmd_queue, u64 *event_queue); +int hi_send_entity_enable_msg(struct ub_entity *uent, u8 enable); int ub_bus_controller_probe(struct ub_bus_controller *ubc); void ub_bus_controller_remove(struct ub_bus_controller *ubc); diff --git a/drivers/ub/ubus/vendor/hisilicon/vdm.c b/drivers/ub/ubus/vendor/hisilicon/vdm.c index d3ea3ebe3301..2d0444a585c9 100644 --- a/drivers/ub/ubus/vendor/hisilicon/vdm.c +++ b/drivers/ub/ubus/vendor/hisilicon/vdm.c @@ -10,6 +10,7 @@ #include "../../pool.h" #include "../../instance.h" #include "../../ubus_entity.h" +#include "hisi-msg.h" #include "hisi-ubus.h" #include "vdm.h" @@ -469,3 +470,70 @@ void hi_vdm_rx_msg_handler(struct ub_bus_controller *ubc, void *pkt, u16 len) if (status) ub_vdm_msg_rsp(ubc, (struct vdm_msg_pkt *)pkt, status); } + +static struct ub_entity *ub_get_primary_ent(struct ub_entity *uent) +{ + if (is_primary(uent)) + return uent; + + return uent->is_mue ? uent->pue : uent->pue->pue; +} + +/* + * hi_send_entity_enable_msg - send entity enable message + */ +int hi_send_entity_enable_msg(struct ub_entity *uent, u8 enable) +{ + struct entity_enable_pld *enable_pld; + struct vdm_msg_pkt pkt = {}; + struct msg_info info = {}; + struct msg_pkt_dw0 *pld_dw0; + struct ub_entity *pue; + u8 status; + int ret; + + if (!uent->ubc->cluster) + return 0; + + ub_msg_pkt_header_init(&pkt.header, uent, ENTITY_ENABLE_PLD_SIZE, + code_gen(UB_MSG_CODE_VDM, UB_VENDOR_MSG, + MSG_REQ), true); + + pkt.guid_high = *(u64 *)(&uent->ubc->uent->guid.dw[SZ_2]); + pld_dw0 = &pkt.pld_dw0; + pld_dw0->opcode = VDM_OPCODE_UB2FM_COMM_MSG; + pld_dw0->sub_opcode = VDM_SUB_OPCODE_ENTITY_ENABLE; + enable_pld = &pkt.enable_pld; + enable_pld->enable = enable; + if (is_device(uent)) { + enable_pld->entity_type = ENTITY_TYPE_DEV; + enable_pld->eid[0] = uent->eid; + } else { /* idev */ + enable_pld->entity_type = ENTITY_TYPE_IDEV; + pue = ub_get_primary_ent(uent); + memcpy(enable_pld->guid, pue->guid.dw, + sizeof(u32) * UB_GUID_DW_NUM); + enable_pld->entity_idx = uent->entity_idx; + } + + message_info_init(&info, uent->ubc->uent, &pkt, &pkt, + (ENTITY_ENABLE_MSG_PKT_SIZE << MSG_REQ_SIZE_OFFSET) | + ENTITY_ENABLE_MSG_PKT_SIZE); + + ub_info(uent, "Sync request entity enable msg, enable=%u\n", enable); + + ret = hi_message_sync_request_sched(uent->message->mdev, &info, + pkt.header.msgetah.code); + if (ret) { + ub_err(uent, "msg sync request ret=%d\n", ret); + return ret; + } + + status = pkt.header.msgetah.rsp_status; + if (status != UB_MSG_RSP_SUCCESS) { + ub_err(uent, "msg rsp status=%#02x\n", status); + return -EINVAL; + } + + return 0; +} -- Gitee From 9a05adce5a57e031e451b487d801821c204d5325 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Tue, 23 Sep 2025 11:53:13 +0800 Subject: [PATCH 090/214] ub:hisi-ubus: Support UBUS configure EID-UPI table ANBZ: #45460 commit 9324afe6a19e40f423950d613c6c8d9851cc4cf7 openEuler. Support UBUS configure EID-UPI table. Initialize and release EID-UPI table, and read/write info to EID-UPI table. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/vendor/hisilicon/Makefile | 2 +- drivers/ub/ubus/vendor/hisilicon/controller.c | 3 + drivers/ub/ubus/vendor/hisilicon/eu-table.c | 330 ++++++++++++++++++ drivers/ub/ubus/vendor/hisilicon/hisi-msg.h | 4 + drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h | 3 + 5 files changed, 341 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/vendor/hisilicon/eu-table.c diff --git a/drivers/ub/ubus/vendor/hisilicon/Makefile b/drivers/ub/ubus/vendor/hisilicon/Makefile index 255cb0ddfa8e..653de75f2539 100644 --- a/drivers/ub/ubus/vendor/hisilicon/Makefile +++ b/drivers/ub/ubus/vendor/hisilicon/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ hisi_ubus-objs := hisi-ubus.o controller.o vdm.o local-ras.o msg.o msg-core.o -hisi_ubus-objs += msg-debugfs.o +hisi_ubus-objs += msg-debugfs.o eu-table.o obj-$(CONFIG_UB_HISI_UBUS) += hisi_ubus.o diff --git a/drivers/ub/ubus/vendor/hisilicon/controller.c b/drivers/ub/ubus/vendor/hisilicon/controller.c index be59baa71da8..52d4086d39b2 100644 --- a/drivers/ub/ubus/vendor/hisilicon/controller.c +++ b/drivers/ub/ubus/vendor/hisilicon/controller.c @@ -14,6 +14,9 @@ #include "hisi-msg.h" static struct ub_bus_controller_ops hi_ubc_ops = { + .eu_table_init = hi_eu_table_init, + .eu_table_uninit = hi_eu_table_uninit, + .eu_cfg = hi_eu_cfg, .register_decoder_base_addr = hi_register_decoder_base_addr, .entity_enable = hi_send_entity_enable_msg, }; diff --git a/drivers/ub/ubus/vendor/hisilicon/eu-table.c b/drivers/ub/ubus/vendor/hisilicon/eu-table.c new file mode 100644 index 000000000000..6bbdfb0e0bf7 --- /dev/null +++ b/drivers/ub/ubus/vendor/hisilicon/eu-table.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus hisi eu: " fmt + +#include +#include "../../ubus.h" +#include "../../msg.h" +#include "../../eu.h" +#include "hisi-msg.h" +#include "hisi-ubus.h" + +enum hi_eu_cfg_msg_code { + EU_CFG_READ, + EU_CFG_WRITE +}; + +enum hi_eu_cfg_mode { + EU_CFG_SERIAL, + EU_CFG_DISCRETE +}; + +enum hi_eu_cfg_status { + EU_CFG_FAIL, + EU_CFG_SUCCESS +}; + +struct hi_eu_cfg_req { + u32 eu_msg_code:4; + u32 cfg_entry_num:10; + u32 tbl_cfg_mode:1; + u32 tbl_cfg_status:1; + u32 entry_start_id:16; + + u32 eid:20; + u32 rsv0:12; + + u32 upi:16; + u32 rsv1:16; +}; +#define HI_EU_CFG_REQ_SIZE 12 + +struct hi_eu_cfg_rsp { + u32 eu_msg_code:4; + u32 cfg_entry_num:10; + u32 tbl_cfg_mode:1; + u32 tbl_cfg_status:1; + u32 entry_start_id:16; +}; +#define HI_EU_CFG_RSP_SIZE 4 + +struct hi_eu_cfg_pld { + union { + struct hi_eu_cfg_req req; + struct hi_eu_cfg_rsp rsp; + }; +}; + +struct hi_eu_table_private { + u32 entries; + unsigned long *bitmap; + u32 *map; + struct mutex lock; +}; + +static int query_bit; + +static int hi_eu_query(struct ub_bus_controller *ubc, int bit, u32 *eid, u16 *upi) +{ + struct hi_eu_cfg_pld pld = {}; + struct msg_info info = {}; + int ret; + + /* + * The format of the query response packet is the same as that of the + * configuration request packet. The format of the query request packet + * is the same as that of the configuration response packet. Therefore, + * the data structure is directly reused. + */ + + pld.req.eu_msg_code = EU_CFG_READ; + pld.req.cfg_entry_num = 1; + pld.req.tbl_cfg_mode = EU_CFG_SERIAL; + pld.req.entry_start_id = (u16)bit; + + message_info_init(&info, ubc->uent, &pld, &pld, + (HI_EU_CFG_RSP_SIZE << MSG_REQ_SIZE_OFFSET) | + HI_EU_CFG_REQ_SIZE); + + ret = hi_message_private(ubc->mdev, &info, EU_TABLE_CFG_CMD); + if (ret || pld.rsp.tbl_cfg_status != EU_CFG_SUCCESS) { + dev_err(&ubc->dev, "eu query bit(%d) failed, ret=%d, status=%u\n", + bit, ret, pld.rsp.tbl_cfg_status); + return ret ? ret : -EBUSY; + } + + *eid = pld.req.eid; + *upi = pld.req.upi; + + return 0; +} + +static ssize_t hi_eu_table_info_read(struct file *file, char __user *ubuf, + size_t size, loff_t *loff) +{ + struct ub_bus_controller *ubc = + (struct ub_bus_controller *)file->f_inode->i_private; + char buf[SZ_64]; + size_t len; + u32 eid; + u16 upi; + int ret; + + ret = hi_eu_query(ubc, query_bit, &eid, &upi); + if (ret) + return ret; + + len = (size_t)scnprintf(buf, sizeof(buf), + "eu query bit %d eid %#05x upi %#04x\n", + query_bit, eid, upi); + + return simple_read_from_buffer(ubuf, size, loff, buf, len); +} + +static ssize_t hi_eu_table_info_write(struct file *file, + const char __user *ubuf, + size_t size, loff_t *loff) +{ + struct ub_bus_controller *ubc = + (struct ub_bus_controller *)file->f_inode->i_private; +#define BUF_SIZE 8 + int len, bit, fields, ret; + char buf[BUF_SIZE]; + + if (*loff != 0) + return 0; + + if (size >= BUF_SIZE) + return -ENOSPC; + + len = simple_write_to_buffer(buf, BUF_SIZE - 1, loff, ubuf, size); + if (len < 0) + return len; + + buf[len] = '\0'; + ret = kstrtoint(buf, 10, &bit); + if (fields != 1) + return -EINVAL; + + if (bit >= (int)ubc->uent->eu_table->entries) { + pr_err("eu table query bit(%d) invalid\n", bit); + return -EINVAL; + } + + query_bit = bit; + return size; +} + +static const struct file_operations hi_eu_table_info_ops = { + .read = hi_eu_table_info_read, + .write = hi_eu_table_info_write, +}; + +static void hi_eu_table_debugfs_init(struct ub_bus_controller *ubc) +{ + debugfs_create_file("eu_table", 0600, ubc->debug_root, + ubc, &hi_eu_table_info_ops); +} + +static void hi_eu_table_debugfs_uninit(struct ub_bus_controller *ubc) +{ + debugfs_lookup_and_remove("eu_table", ubc->debug_root); +} + +int hi_eu_table_init(struct ub_bus_controller *ubc) +{ + struct ub_eu_table *t = ubc->uent->eu_table; + struct hi_eu_table_private *p; + size_t size; + + if (t->entries >= INT_MAX) + return -EINVAL; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + + p->entries = t->entries; + + size = BITS_TO_LONGS(p->entries) * sizeof(unsigned long); + p->bitmap = kzalloc(size, GFP_KERNEL); + if (!p->bitmap) + goto free; + + p->map = kcalloc(p->entries, sizeof(u32), GFP_KERNEL); + if (!p->map) + goto free_bitmap; + + mutex_init(&p->lock); + t->private_data = p; + + hi_eu_table_debugfs_init(ubc); + return 0; + +free_bitmap: + kfree(p->bitmap); +free: + kfree(p); + return -ENOMEM; +} + +void hi_eu_table_uninit(struct ub_bus_controller *ubc) +{ + struct ub_eu_table *t = ubc->uent->eu_table; + struct hi_eu_table_private *p; + + hi_eu_table_debugfs_uninit(ubc); + + p = (struct hi_eu_table_private *)t->private_data; + kfree(p->map); + kfree(p->bitmap); + kfree(p); + t->private_data = NULL; +} + +static int hi_eu_get_bit(struct hi_eu_table_private *p) +{ + int bit, ret; + + mutex_lock(&p->lock); + bit = (int)find_first_zero_bit(p->bitmap, p->entries); + if (bit != (int)p->entries) { + set_bit(bit, p->bitmap); + ret = bit; + } else { + ret = -ENOSPC; + } + mutex_unlock(&p->lock); + + return ret; +} + +static void hi_eu_put_bit(struct hi_eu_table_private *p, int bit) +{ + mutex_lock(&p->lock); + clear_bit(bit, p->bitmap); + mutex_unlock(&p->lock); +} + +static int hi_eu_find_bit_and_mask(struct hi_eu_table_private *p, u32 eid) +{ + u32 bit; + + mutex_lock(&p->lock); + for_each_set_bit(bit, p->bitmap, p->entries) + if (p->map[bit] == eid) { + p->map[bit] = 0; + break; + } + mutex_unlock(&p->lock); + + if (bit == p->entries) + return -EINVAL; + + return bit; +} + +int hi_eu_cfg(struct ub_bus_controller *ubc, bool add, u32 eid, u16 upi) +{ + struct ub_eu_table *eu = ubc->uent->eu_table; + struct hi_eu_cfg_pld pld = {}; + struct hi_eu_table_private *p; + struct msg_info info = {}; + struct ub_eu_entry *entry; + int ret, bit; + + if (!eu) + return -EINVAL; + + p = (struct hi_eu_table_private *)eu->private_data; + + if (add) + bit = hi_eu_get_bit(p); + else + bit = hi_eu_find_bit_and_mask(p, eid); + + if (bit < 0) + return bit; + + pld.req.eu_msg_code = EU_CFG_WRITE; + pld.req.cfg_entry_num = 1; + pld.req.tbl_cfg_mode = EU_CFG_SERIAL; + pld.req.entry_start_id = (u16)bit; + pld.req.eid = add ? eid : 0; + pld.req.upi = add ? upi : 0; + + message_info_init(&info, ubc->uent, &pld, &pld, + (HI_EU_CFG_REQ_SIZE << MSG_REQ_SIZE_OFFSET) | + HI_EU_CFG_RSP_SIZE); + + ret = hi_message_private(ubc->mdev, &info, EU_TABLE_CFG_CMD); + if (ret || pld.rsp.tbl_cfg_status != EU_CFG_SUCCESS) { + dev_err(&ubc->dev, "eu cfg failed, eid %#05x upi %#04x, ret=%d, status=%u\n", + eid, upi, ret, pld.rsp.tbl_cfg_status); + if (add) + hi_eu_put_bit(p, bit); + else + p->map[bit] = eid; + return ret ? ret : -EBUSY; + } + + entry = (struct ub_eu_entry *)eu->addr + bit; + + if (add) { + p->map[bit] = eid; + entry->eid[0] = eid; + entry->upi = upi; + } else { + hi_eu_put_bit(p, bit); + entry->eid[0] = 0; + entry->upi = 0; + } + + dev_info(&ubc->dev, "eu cfg bit(%d) type(%d) eid %#05x upi %#04x\n", + bit, add, eid, upi); + + return 0; +} diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h b/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h index c0bc9a043796..78476423bdfb 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h @@ -57,6 +57,10 @@ enum hi_task_type { TASK_TYPE_NUM }; +enum hi_msgq_private_opcode { + EU_TABLE_CFG_CMD = 2, +}; + enum hi_msgq_user { MSGQ_USER_BUS_DRV = 0, MSGQ_USER_NUMS diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h index 90a157e4b8d2..ae86a0e22ff1 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h @@ -32,6 +32,9 @@ struct hi_ubc_private_data { u8 reserved2[HI_UBC_PRIVATE_DATA_RESERVED2]; }; +int hi_eu_table_init(struct ub_bus_controller *ubc); +void hi_eu_table_uninit(struct ub_bus_controller *ubc); +int hi_eu_cfg(struct ub_bus_controller *ubc, bool add, u32 eid, u16 upi); void hi_register_decoder_base_addr(struct ub_bus_controller *ubc, u64 *cmd_queue, u64 *event_queue); int hi_send_entity_enable_msg(struct ub_entity *uent, u8 enable); -- Gitee From d2595eb7043f29d5731c994954362766ef5b9edf Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Thu, 25 Sep 2025 11:13:36 +0800 Subject: [PATCH 091/214] ub:ubus: reserve space for ub subsystem related structure ANBZ: #45460 commit f1ef7b8acf16eabcba12f32776244ba4e4dd0e22 openEuler. Reserve space for ub subsystem related structure. Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/ubus.h | 5 ++++ drivers/ub/ubus/ubus_controller.h | 9 ++++++ include/ub/ubus/ubus.h | 46 +++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index 89c6e1f179ff..e0956b136bb9 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -66,6 +66,11 @@ struct ub_manage_subsystem_ops { void (*controller_remove)(struct ub_bus_controller *ubc); int (*ras_handler_probe)(void); void (*ras_handler_remove)(void); + + CK_KABI_RESERVE(1) + CK_KABI_RESERVE(2) + CK_KABI_RESERVE(3) + CK_KABI_RESERVE(4) }; int register_ub_manage_subsystem_ops(const struct ub_manage_subsystem_ops *ops); void unregister_ub_manage_subsystem_ops(const struct ub_manage_subsystem_ops *ops); diff --git a/drivers/ub/ubus/ubus_controller.h b/drivers/ub/ubus/ubus_controller.h index 8637da594ada..483d9d8b9ec0 100644 --- a/drivers/ub/ubus/ubus_controller.h +++ b/drivers/ub/ubus/ubus_controller.h @@ -14,6 +14,15 @@ struct ub_bus_controller_ops { void (*register_decoder_base_addr)(struct ub_bus_controller *ubc, u64 *cmd_queue, u64 *event_queue); int (*entity_enable)(struct ub_entity *uent, u8 enable); + + CK_KABI_RESERVE(1) + CK_KABI_RESERVE(2) + CK_KABI_RESERVE(3) + CK_KABI_RESERVE(4) + CK_KABI_RESERVE(5) + CK_KABI_RESERVE(6) + CK_KABI_RESERVE(7) + CK_KABI_RESERVE(8) }; struct ub_bus_controller *ub_find_bus_controller_by_cna(u32 cna); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 728c592ba325..ddc53104819d 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -6,6 +6,7 @@ #ifndef _UB_UBUS_UBUS_H_ #define _UB_UBUS_UBUS_H_ +#include #include #include #include @@ -158,6 +159,9 @@ struct ub_port { struct work_struct link_work; enum ub_link_state link_state; u8 link_event; + + CK_KABI_RESERVE(1) + CK_KABI_RESERVE(2) }; struct ue_map { @@ -263,6 +267,23 @@ struct ub_entity { u32 support_feature; u16 upi; + + CK_KABI_RESERVE(1) + CK_KABI_RESERVE(2) + CK_KABI_RESERVE(3) + CK_KABI_RESERVE(4) + CK_KABI_RESERVE(5) + CK_KABI_RESERVE(6) + CK_KABI_RESERVE(7) + CK_KABI_RESERVE(8) + CK_KABI_RESERVE(9) + CK_KABI_RESERVE(10) + CK_KABI_RESERVE(11) + CK_KABI_RESERVE(12) + CK_KABI_RESERVE(13) + CK_KABI_RESERVE(14) + CK_KABI_RESERVE(15) + CK_KABI_RESERVE(16) }; /* UB bus error event callbacks */ @@ -272,6 +293,11 @@ struct ub_error_handlers { void (*ub_reset_done)(struct ub_entity *uent); ub_ers_result_t (*ub_error_detected)(struct ub_entity *uent, ub_channel_state_t state); ub_ers_result_t (*ub_resource_enabled)(struct ub_entity *uent); + + CK_KABI_RESERVE(1) + CK_KABI_RESERVE(2) + CK_KABI_RESERVE(3) + CK_KABI_RESERVE(4) }; struct ub_dynids { @@ -350,6 +376,15 @@ struct ub_driver { struct device_driver driver; struct ub_dynids dynids; bool driver_managed_dma; + + CK_KABI_RESERVE(1) + CK_KABI_RESERVE(2) + CK_KABI_RESERVE(3) + CK_KABI_RESERVE(4) + CK_KABI_RESERVE(5) + CK_KABI_RESERVE(6) + CK_KABI_RESERVE(7) + CK_KABI_RESERVE(8) }; struct ubc_common_attr { @@ -390,6 +425,11 @@ struct ub_bus_controller { void *data; struct dentry *debug_root; + + CK_KABI_RESERVE(1) + CK_KABI_RESERVE(2) + CK_KABI_RESERVE(3) + CK_KABI_RESERVE(4) }; struct ub_bus_instance_info { @@ -411,6 +451,9 @@ struct ub_bus_instance { struct list_head uents; struct mutex lock; + + CK_KABI_RESERVE(1) + CK_KABI_RESERVE(2) }; #define ub_bi_is_dynamic(bi) ((bi)->info.type == UBUS_INSTANCE_DYNAMIC_SERVER \ @@ -443,6 +486,9 @@ struct ub_share_port_ops { void (*reset_prepare)(struct ub_entity *uent, u16 port_id); void (*reset_done)(struct ub_entity *uent, u16 port_id); void (*event_notify)(struct ub_entity *uent, u16 port_id, int event); + + CK_KABI_RESERVE(1) + CK_KABI_RESERVE(2) }; struct ub_vdm_pld { -- Gitee From e9a5473652c244e938f4e0c7b8066b4ec7cf291b Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 14 Nov 2025 11:54:54 +0800 Subject: [PATCH 092/214] ub:ubus: Bugfix of ubus and ubfi ANBZ: #45460 commit 7e992348e2c3a9064e165fbf5a075a2945790756 openEuler. 1. Add the CONFIG_UB option to the ub directory in Makefile; 2. Add static for inner functions; 3. fix ubfi's ubc_list init bug. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/Makefile | 2 +- drivers/irqchip/irq-gic-v3-its-ub-msi.c | 2 +- drivers/ub/ubfi/ubc.c | 4 +--- drivers/ub/ubus/ioctl.c | 1 + drivers/ub/ubus/reset.c | 3 ++- drivers/ub/ubus/resource.c | 6 +++--- drivers/ub/ubus/route.c | 1 + drivers/ub/ubus/services/ras.c | 4 ++-- drivers/ub/ubus/ubus_config.c | 12 ++++++------ drivers/ub/ubus/ubus_driver.c | 6 +++--- drivers/ub/ubus/ubus_entity.c | 2 +- 11 files changed, 22 insertions(+), 21 deletions(-) diff --git a/drivers/Makefile b/drivers/Makefile index 7e5b459f5249..c51310899c33 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -23,7 +23,7 @@ obj-$(CONFIG_GPIOLIB) += gpio/ obj-y += pwm/ obj-y += pci/ -obj-y += ub/ +obj-$(CONFIG_UB) += ub/ obj-$(CONFIG_PARISC) += parisc/ obj-$(CONFIG_RAPIDIO) += rapidio/ diff --git a/drivers/irqchip/irq-gic-v3-its-ub-msi.c b/drivers/irqchip/irq-gic-v3-its-ub-msi.c index 4caccd12fdc4..08274a57c5d5 100644 --- a/drivers/irqchip/irq-gic-v3-its-ub-msi.c +++ b/drivers/irqchip/irq-gic-v3-its-ub-msi.c @@ -146,7 +146,7 @@ static int its_ub_of_msi_init(void) return 0; } -int __init its_ub_msi_init(void) +static int __init its_ub_msi_init(void) { its_ub_of_msi_init(); its_ub_acpi_msi_init(); diff --git a/drivers/ub/ubfi/ubc.c b/drivers/ub/ubfi/ubc.c index e89aeeafb913..a3f7bab8863f 100644 --- a/drivers/ub/ubfi/ubc.c +++ b/drivers/ub/ubfi/ubc.c @@ -28,7 +28,7 @@ #define to_ub_ubc(n) container_of(n, struct ub_bus_controller, dev) -struct list_head ubc_list; +LIST_HEAD(ubc_list); EXPORT_SYMBOL_GPL(ubc_list); u32 ubc_eid_start; @@ -593,8 +593,6 @@ int handle_ubc_table(u64 pointer) if (!info_node) return -EINVAL; - INIT_LIST_HEAD(&ubc_list); - ret = parse_ubc_table(info_node); if (ret) goto err_handle; diff --git a/drivers/ub/ubus/ioctl.c b/drivers/ub/ubus/ioctl.c index abcd4e878755..0825a87d8b81 100644 --- a/drivers/ub/ubus/ioctl.c +++ b/drivers/ub/ubus/ioctl.c @@ -10,6 +10,7 @@ #include "ubus.h" #include "instance.h" +#include "ioctl.h" #define UBUS_MAX_DEVICES 1 #define UBUS_DEVICE_NAME "unified_bus" diff --git a/drivers/ub/ubus/reset.c b/drivers/ub/ubus/reset.c index 4b7d86624e24..596d848c2c11 100644 --- a/drivers/ub/ubus/reset.c +++ b/drivers/ub/ubus/reset.c @@ -12,6 +12,7 @@ #include "route.h" #include "ubus_controller.h" #include "ubus_config.h" +#include "reset.h" enum elr_type { ELR_PREPARE = 0, @@ -30,7 +31,7 @@ static u32 saved_cfg_offset[] = { * ub_elr - Initiate an UB entity level reset * @dev: UB entity to reset */ -int ub_elr(struct ub_entity *dev) +static int ub_elr(struct ub_entity *dev) { u8 command; u8 val = 0; diff --git a/drivers/ub/ubus/resource.c b/drivers/ub/ubus/resource.c index b57117e02415..d4516d672bb5 100644 --- a/drivers/ub/ubus/resource.c +++ b/drivers/ub/ubus/resource.c @@ -97,7 +97,7 @@ static int _ub_assign_resource(struct ub_entity *uent, int idx, return -ENOMEM; } -int ub_assign_resource(struct ub_entity *uent, int idx) +static int ub_assign_resource(struct ub_entity *uent, int idx) { struct resource *res = &uent->zone[idx].res; resource_size_t size; @@ -123,7 +123,7 @@ int ub_assign_resource(struct ub_entity *uent, int idx) return 0; } -void ub_release_resource(struct ub_entity *uent, int idx) +static void ub_release_resource(struct ub_entity *uent, int idx) { int ret; @@ -292,7 +292,7 @@ int ub_insert_resource(struct ub_entity *dev, int idx) return 0; } -int ub_entity_alloc_mmio_idx(struct ub_entity *dev, int idx) +static int ub_entity_alloc_mmio_idx(struct ub_entity *dev, int idx) { if (is_ibus_controller(dev) || is_idev(dev)) return ub_insert_resource(dev, idx); diff --git a/drivers/ub/ubus/route.c b/drivers/ub/ubus/route.c index 73e0df5436dc..364bf78d93c3 100644 --- a/drivers/ub/ubus/route.c +++ b/drivers/ub/ubus/route.c @@ -12,6 +12,7 @@ #include "enum.h" #include "port.h" #include "ubus_driver.h" +#include "route.h" #define UB_ROUTE_TABLE_ENTRY_START (UB_ROUTE_TABLE_SLICE_START + (0x10 << 2)) #define EBW(port_nums) ((((port_nums) - 1) >> 5) + 1) /* Entry Bit Width */ diff --git a/drivers/ub/ubus/services/ras.c b/drivers/ub/ubus/services/ras.c index c38b300e1646..fc4365f819a3 100644 --- a/drivers/ub/ubus/services/ras.c +++ b/drivers/ub/ubus/services/ras.c @@ -29,7 +29,7 @@ enum ras_err_level { RAS_ERR_DEVICE_LEVEL, }; -int cper_severity_to_ub_ras(int cper_severity) +static int cper_severity_to_ub_ras(int cper_severity) { switch (cper_severity) { case CPER_SEV_FATAL: @@ -269,7 +269,7 @@ static inline void ras_recover_entry_init(struct ras_recover_entry *entry, static DEFINE_SPINLOCK(ub_ras_recover_ring_lock); static DECLARE_WORK(ub_ras_recover_work, ub_ras_recover_work_func); -void ub_ras_recover_queue(struct cper_sec_ubus *ubus_err, int severity) +static void ub_ras_recover_queue(struct cper_sec_ubus *ubus_err, int severity) { #define PORT_VALID_BIT 0b100ULL #define OVERFLOW_FLAG_BIT 0b10000ULL diff --git a/drivers/ub/ubus/ubus_config.c b/drivers/ub/ubus/ubus_config.c index a2d285da998a..2c1f068dc11a 100644 --- a/drivers/ub/ubus/ubus_config.c +++ b/drivers/ub/ubus/ubus_config.c @@ -308,7 +308,7 @@ int ub_send_cfg(struct ub_entity *uent, u8 size, u64 pos, u32 *val) req_pkt.header.msgetah.code); } -int __ub_cfg_read_byte(struct ub_entity *uent, u64 pos, u8 *val) +static int __ub_cfg_read_byte(struct ub_entity *uent, u64 pos, u8 *val) { if (!uent || !uent->message || !uent->message->mdev || !val) { pr_err("uent or message or mdev is null\n"); @@ -318,7 +318,7 @@ int __ub_cfg_read_byte(struct ub_entity *uent, u64 pos, u8 *val) return ub_sync_cfg(uent, (u8)sizeof(u8), pos, false, (u32 *)val); } -int __ub_cfg_read_word(struct ub_entity *uent, u64 pos, u16 *val) +static int __ub_cfg_read_word(struct ub_entity *uent, u64 pos, u16 *val) { if (!uent || !uent->message || !uent->message->mdev || !val) { pr_err("uent or message or mdev is null\n"); @@ -328,7 +328,7 @@ int __ub_cfg_read_word(struct ub_entity *uent, u64 pos, u16 *val) return ub_sync_cfg(uent, (u8)sizeof(u16), pos, false, (u32 *)val); } -int __ub_cfg_read_dword(struct ub_entity *uent, u64 pos, u32 *val) +static int __ub_cfg_read_dword(struct ub_entity *uent, u64 pos, u32 *val) { if (!uent || !uent->message || !uent->message->mdev || !val) { pr_err("uent or message or mdev is null\n"); @@ -338,7 +338,7 @@ int __ub_cfg_read_dword(struct ub_entity *uent, u64 pos, u32 *val) return ub_sync_cfg(uent, (u8)sizeof(u32), pos, false, val); } -int __ub_cfg_write_byte(struct ub_entity *uent, u64 pos, u8 val) +static int __ub_cfg_write_byte(struct ub_entity *uent, u64 pos, u8 val) { if (!uent || !uent->message || !uent->message->mdev) { pr_err("uent or message or mdev is null\n"); @@ -348,7 +348,7 @@ int __ub_cfg_write_byte(struct ub_entity *uent, u64 pos, u8 val) return ub_sync_cfg(uent, (u8)sizeof(u8), pos, true, (u32 *)&val); } -int __ub_cfg_write_word(struct ub_entity *uent, u64 pos, u16 val) +static int __ub_cfg_write_word(struct ub_entity *uent, u64 pos, u16 val) { if (!uent || !uent->message || !uent->message->mdev) { pr_err("uent or message or mdev is null\n"); @@ -358,7 +358,7 @@ int __ub_cfg_write_word(struct ub_entity *uent, u64 pos, u16 val) return ub_sync_cfg(uent, (u8)sizeof(u16), pos, true, (u32 *)&val); } -int __ub_cfg_write_dword(struct ub_entity *uent, u64 pos, u32 val) +static int __ub_cfg_write_dword(struct ub_entity *uent, u64 pos, u32 val) { if (!uent || !uent->message || !uent->message->mdev) { pr_err("uent or message or mdev is null\n"); diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index b4c6f0d3b760..91f0a062153f 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -167,7 +167,7 @@ ub_match_one_device(const struct ub_device_id *id, const struct ub_entity *dev) return NULL; } -const struct ub_device_id *ub_match_id(const struct ub_device_id *ids, +static const struct ub_device_id *ub_match_id(const struct ub_device_id *ids, struct ub_entity *dev) { if (ids && dev) { @@ -600,7 +600,7 @@ static int ub_bus_num_ue(struct device *dev) return ub_num_ue(to_ub_entity(dev)); } -void ub_bus_type_init(void) +static void ub_bus_type_init(void) { ub_bus_type.match = ub_bus_match; ub_bus_type.uevent = ub_uevent; @@ -613,7 +613,7 @@ void ub_bus_type_init(void) ub_bus_type.num_vf = ub_bus_num_ue; } -void ub_bus_type_uninit(void) +static void ub_bus_type_uninit(void) { ub_bus_type.match = NULL; ub_bus_type.uevent = NULL; diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index dc9bccff9044..ebc9d764f670 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -62,7 +62,7 @@ struct ub_entity *ub_alloc_ent(void) EXPORT_SYMBOL_GPL(ub_alloc_ent); static DEFINE_IDA(uent_num_ida); -void ub_entity_num_free(struct ub_entity *uent) +static void ub_entity_num_free(struct ub_entity *uent) { ida_free(&uent_num_ida, uent->uent_num); } -- Gitee From bbc7fad419ce8de44b2fa76991d2e9a9a3318a70 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Mon, 17 Nov 2025 14:25:58 +0800 Subject: [PATCH 093/214] ub:ubus: Add ummu_map attribute in sysfs ANBZ: #45460 commit d9875bf05b18a022f352201fec2e517e72236ca4 openEuler. Add ummu_map attribute in sysfs Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/sysfs.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/ub/ubus/sysfs.c b/drivers/ub/ubus/sysfs.c index a583cf7efa97..2e997c5c5f1a 100644 --- a/drivers/ub/ubus/sysfs.c +++ b/drivers/ub/ubus/sysfs.c @@ -325,6 +325,15 @@ static ssize_t primary_cna_show(struct device *dev, } DEVICE_ATTR_RO(primary_cna); +static ssize_t ummu_map_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + + return sysfs_emit(buf, "%#04x\n", uent->ubc->attr.ummu_map); +} +DEVICE_ATTR_RO(ummu_map); + static ssize_t instance_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -393,6 +402,7 @@ static struct attribute *ub_entity_attrs[] = { &dev_attr_tid.attr, &dev_attr_primary_entity.attr, &dev_attr_kref.attr, + &dev_attr_ummu_map.attr, NULL }; -- Gitee From 66f4c227cb7d0650b0784af1bdf05f2266698f8f Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Wed, 19 Nov 2025 13:38:29 +0800 Subject: [PATCH 094/214] ub:ubus: Fix X86 build error ANBZ: #45460 commit d742069465ca1953abb2591d392686fbf1d69714 openEuler. Fix X86 build error.When CONFIG_GENERIC_MSI_IRQ is close and CONFIG_UB is on, msi.domain cannot be found. Add CONFIG_GENERIC_MSI_IRQ in function ub_update_msi_domain Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubfi/irq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubfi/irq.c b/drivers/ub/ubfi/irq.c index d47e4ce67bd6..eb716af13088 100644 --- a/drivers/ub/ubfi/irq.c +++ b/drivers/ub/ubfi/irq.c @@ -12,6 +12,7 @@ int ub_update_msi_domain(struct device *dev, enum irq_domain_bus_token bus_token) { +#ifdef CONFIG_GENERIC_MSI_IRQ struct fwnode_handle *fwnode; struct irq_domain *domain; @@ -35,7 +36,7 @@ int ub_update_msi_domain(struct device *dev, /* Update msi domain with new bus_token */ dev_set_msi_domain(dev, domain); - +#endif return 0; } EXPORT_SYMBOL_GPL(ub_update_msi_domain); -- Gitee From 2d47ffe46d0382d74d97f2560020426cec0b312d Mon Sep 17 00:00:00 2001 From: Lizhi He Date: Tue, 2 Sep 2025 14:14:15 +0800 Subject: [PATCH 095/214] dma-mapping: benchmark: add support for UB devices ANBZ: #45460 commit 72204766a0326d01d1c357357038bd74c1e73825 openEuler. The current dma_map benchmark only supports platform devices and PCI devices. This patch adds support for UB devices. Signed-off-by: Xiaofeng Liu Signed-off-by: Li Wentao Signed-off-by: Lizhi He Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- kernel/dma/map_benchmark.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/kernel/dma/map_benchmark.c b/kernel/dma/map_benchmark.c index cc19a3efea89..23aeba267cfa 100644 --- a/kernel/dma/map_benchmark.c +++ b/kernel/dma/map_benchmark.c @@ -18,6 +18,9 @@ #include #include #include +#ifdef CONFIG_UB_UBUS +#include +#endif struct map_benchmark_data { struct map_benchmark bparam; @@ -351,6 +354,19 @@ static struct pci_driver map_benchmark_pci_driver = { .probe = map_benchmark_pci_probe, }; +#ifdef CONFIG_UB_UBUS +static int map_benchmark_ub_probe(struct ub_entity *uent, + const struct ub_device_id *id) +{ + return __map_benchmark_probe(&uent->dev); +} + +static struct ub_driver map_benchmark_ub_driver = { + .name = "dma_map_benchmark", + .probe = map_benchmark_ub_probe, +}; +#endif + static int __init map_benchmark_init(void) { int ret; @@ -360,16 +376,30 @@ static int __init map_benchmark_init(void) return ret; ret = platform_driver_register(&map_benchmark_platform_driver); - if (ret) { - pci_unregister_driver(&map_benchmark_pci_driver); - return ret; - } + if (ret) + goto err_reg_platform; + +#ifdef CONFIG_UB_UBUS + ret = ub_register_driver(&map_benchmark_ub_driver); + if (ret) + goto err_reg_ub; +#endif return 0; +#ifdef CONFIG_UB_UBUS +err_reg_ub: + platform_driver_unregister(&map_benchmark_platform_driver); +#endif +err_reg_platform: + pci_unregister_driver(&map_benchmark_pci_driver); + return ret; } static void __exit map_benchmark_cleanup(void) { +#ifdef CONFIG_UB_UBUS + ub_unregister_driver(&map_benchmark_ub_driver); +#endif platform_driver_unregister(&map_benchmark_platform_driver); pci_unregister_driver(&map_benchmark_pci_driver); } -- Gitee From a5c8d838e7b5c148d138065b2ff05ec00364b296 Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Fri, 14 Nov 2025 14:18:53 +0800 Subject: [PATCH 096/214] ub:ubus: call ub_host_probe inside register_ub_manage_subsystem_ops ANBZ: #45460 commit 59de5029039dc2a046518ce1cdf5e3bf7bc3ac02 openEuler. Call ub_host_probe inside register_ub_manage_subsystem_ops so that ub_host_probe can become static function, same to ub_host_remove. Query entity and port na first, then do the config read in ub_fm_flush_ubc_info because config read will check na. Fix some comment description issues. Signed-off-by: Yahui Liu Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/pool.c | 20 ++-- drivers/ub/ubus/ubus.h | 2 - drivers/ub/ubus/ubus_driver.c | 100 +++++++++++-------- drivers/ub/ubus/ubus_driver.h | 2 - drivers/ub/ubus/vendor/hisilicon/hisi-ubus.c | 7 -- include/ub/ubus/ubus.h | 98 +++++++++--------- 6 files changed, 121 insertions(+), 108 deletions(-) diff --git a/drivers/ub/ubus/pool.c b/drivers/ub/ubus/pool.c index 2aeb8d57ee9a..e86b19b58f63 100644 --- a/drivers/ub/ubus/pool.c +++ b/drivers/ub/ubus/pool.c @@ -456,6 +456,18 @@ int ub_fm_flush_ubc_info(struct ub_bus_controller *ubc) if (!buf) goto out; + ret = ub_query_ent_na(ubc->uent, buf); + if (ret) { + dev_err(dev, "update cluster ubc cna failed, ret=%d\n", ret); + goto free_buf; + } + + ret = ub_query_port_na(ubc->uent, buf); + if (ret) { + dev_err(dev, "update cluster ubc port cna failed, ret=%d\n", ret); + goto free_buf; + } + ret = ub_cfg_read_word(ubc->uent, UB_UPI, &upi); if (ret) { dev_err(dev, "update cluster upi failed, ret=%d\n", ret); @@ -489,14 +501,6 @@ int ub_fm_flush_ubc_info(struct ub_bus_controller *ubc) ubc->uent->fm_cna = fm_cna & UB_FM_CNA_MASK; dev_info(dev, "update cluster ubc fm cna to %#x\n", ubc->uent->fm_cna); - ret = ub_query_ent_na(ubc->uent, buf); - if (ret) { - dev_err(dev, "update cluster ubc cna failed, ret=%d\n", ret); - goto free_buf; - } - - ret = ub_query_port_na(ubc->uent, buf); - free_buf: kfree(buf); out: diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index e0956b136bb9..790778f339cc 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -56,8 +56,6 @@ static inline bool ub_entity_test_priv_flag(struct ub_entity *uent, int bit) return test_bit(bit, &uent->priv_flags); } -int ub_host_probe(void); -void ub_host_remove(void); struct ub_bus_controller *ub_find_bus_controller(u32 ctl_no); struct ub_manage_subsystem_ops { diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index 91f0a062153f..974020bf3c38 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -33,45 +33,12 @@ MODULE_PARM_DESC(entity_flex_en, "Entity Flexible enable: default: 0"); DECLARE_RWSEM(ub_bus_sem); +#define UBC_GUID_VENDOR_SHIFT 48 +#define UBC_GUID_VENDOR_MASK GENMASK(15, 0) + static DEFINE_MUTEX(manage_subsystem_ops_mutex); static const struct ub_manage_subsystem_ops *manage_subsystem_ops; -int register_ub_manage_subsystem_ops(const struct ub_manage_subsystem_ops *ops) -{ - if (!ops) - return -EINVAL; - - mutex_lock(&manage_subsystem_ops_mutex); - if (!manage_subsystem_ops) { - manage_subsystem_ops = ops; - mutex_unlock(&manage_subsystem_ops_mutex); - pr_info("ub manage subsystem ops register successfully\n"); - return 0; - } - - pr_warn("ub manage subsystem ops has been registered\n"); - mutex_unlock(&manage_subsystem_ops_mutex); - - return -EINVAL; -} -EXPORT_SYMBOL_GPL(register_ub_manage_subsystem_ops); - -void unregister_ub_manage_subsystem_ops(const struct ub_manage_subsystem_ops *ops) -{ - if (!ops) - return; - - mutex_lock(&manage_subsystem_ops_mutex); - if (manage_subsystem_ops == ops) { - manage_subsystem_ops = NULL; - pr_info("ub manage subsystem ops unregister successfully\n"); - } else { - pr_warn("ub manage subsystem ops is not registered by this vendor\n"); - } - mutex_unlock(&manage_subsystem_ops_mutex); -} -EXPORT_SYMBOL_GPL(unregister_ub_manage_subsystem_ops); - const struct ub_manage_subsystem_ops *get_ub_manage_subsystem_ops(void) { return manage_subsystem_ops; @@ -653,7 +620,7 @@ static void ubus_driver_resource_drain(void) ub_static_cluster_instance_drain(); } -int ub_host_probe(void) +static int ub_host_probe(void) { int ret; @@ -724,9 +691,8 @@ int ub_host_probe(void) ub_bus_type_uninit(); return ret; } -EXPORT_SYMBOL_GPL(ub_host_probe); -void ub_host_remove(void) +static void ub_host_remove(void) { message_rx_uninit(); if (manage_subsystem_ops && manage_subsystem_ops->ras_handler_remove) @@ -741,7 +707,61 @@ void ub_host_remove(void) unregister_ub_cfg_ops(); ub_bus_type_uninit(); } -EXPORT_SYMBOL_GPL(ub_host_remove); + +int register_ub_manage_subsystem_ops(const struct ub_manage_subsystem_ops *ops) +{ + struct ub_bus_controller *ubc; + int ret; + + if (!ops) { + pr_err("ub manage subsystem ops is NULL\n"); + return -EINVAL; + } + + mutex_lock(&manage_subsystem_ops_mutex); + if (!manage_subsystem_ops) { + list_for_each_entry(ubc, &ubc_list, node) { + if (((ubc->attr.ubc_guid_high >> UBC_GUID_VENDOR_SHIFT) & + UBC_GUID_VENDOR_MASK) == ops->vendor) { + manage_subsystem_ops = ops; + ret = ub_host_probe(); + if (ret) + manage_subsystem_ops = NULL; + else + pr_info("ub manage subsystem ops register successfully\n"); + + mutex_unlock(&manage_subsystem_ops_mutex); + return ret; + } + } + pr_warn("ub manage subsystem ops is not match with any of ub controller\n"); + } else { + pr_warn("ub manage subsystem ops has been registered\n"); + } + mutex_unlock(&manage_subsystem_ops_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(register_ub_manage_subsystem_ops); + +void unregister_ub_manage_subsystem_ops(const struct ub_manage_subsystem_ops *ops) +{ + if (!ops) { + pr_err("ub manage subsystem ops is NULL\n"); + return; + } + + mutex_lock(&manage_subsystem_ops_mutex); + if (manage_subsystem_ops == ops) { + ub_host_remove(); + manage_subsystem_ops = NULL; + pr_info("ub manage subsystem ops unregister successfully\n"); + } else { + pr_warn("ub manage subsystem ops is not registered by this vendor\n"); + } + mutex_unlock(&manage_subsystem_ops_mutex); +} +EXPORT_SYMBOL_GPL(unregister_ub_manage_subsystem_ops); static int __init ubus_driver_init(void) { diff --git a/drivers/ub/ubus/ubus_driver.h b/drivers/ub/ubus/ubus_driver.h index f2bff32bbee9..b2eab906fa31 100644 --- a/drivers/ub/ubus/ubus_driver.h +++ b/drivers/ub/ubus/ubus_driver.h @@ -8,7 +8,5 @@ extern struct rw_semaphore ub_bus_sem; extern struct bus_type ub_service_bus_type; -int ub_host_probe(void); -void ub_host_remove(void); #endif /* __UBUS_DRIVER_H__ */ diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.c b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.c index be86f055cb34..3627b0e8f018 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.c +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.c @@ -53,10 +53,6 @@ static int __init hisi_ubus_driver_register(struct platform_driver *drv) if (ret) return ret; - ret = ub_host_probe(); - if (ret) - goto host_probe_fail; - ret = platform_driver_register(drv); if (ret) goto platform_driver_register_fail; @@ -64,8 +60,6 @@ static int __init hisi_ubus_driver_register(struct platform_driver *drv) return 0; platform_driver_register_fail: - ub_host_remove(); -host_probe_fail: unregister_ub_manage_subsystem_ops(&hisi_ub_manage_subsystem_ops); return ret; } @@ -73,7 +67,6 @@ static int __init hisi_ubus_driver_register(struct platform_driver *drv) static void __exit hisi_ubus_driver_unregister(struct platform_driver *drv) { platform_driver_unregister(drv); - ub_host_remove(); unregister_ub_manage_subsystem_ops(&hisi_ub_manage_subsystem_ops); } diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index ddc53104819d..c33542b11bcb 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -194,7 +194,7 @@ struct ub_entity { u32 token_id; u32 token_value; - /* mue & ue info */ + /* MUE & UE info */ u8 is_mue; u16 total_ues; u16 num_ues; @@ -206,7 +206,7 @@ struct ub_entity { /* entity topology info */ struct list_head node; struct ub_bus_controller *ubc; - struct ub_entity *pue; /* ue/mue connected to their mue */ + struct ub_entity *pue; /* UE/MUE connected to their MUE */ int topo_rank; /* The levels of Breadth-First Search */ /* entity port info */ @@ -335,10 +335,10 @@ struct ub_dynids { * @shutdown: Hook into reboot_notifier_list (kernel/sys.c). * Intended to stop any idling operations. * @virt_configure: Optional driver callback to allow configuration of - * ues. This function is called to enable or disable ues. + * UEs. This function is called to enable or disable UEs. * @virt_notify: Optional driver callback to notify the driver about - * changes in ue status. This function is called - * when the status of a ue changes. + * changes in UE status. This function is called + * when the status of a UE changes. * @activate: Activate a specific entity. This function is called to * activate an entity by its index. * @deactivate: Deactivate a specific entity. This function is called to @@ -508,14 +508,14 @@ void ub_iommu_fwnode_handle_set(const struct fwnode_handle *fwnode); const struct fwnode_handle *ub_iommu_fwnode_handle_get(void); /** - * ub_get_ent_by_eid() - Searching for UB Devices by EID. + * ub_get_ent_by_eid() - Searching for UB entity by EID. * @eid: entity EID. * * Traverse the UB bus device linked list and search for the device with * the target EID. You need to call ub_entity_put() after using it. * * Context: Any context. - * Return: The device found, or NULL if not found. + * Return: The entity found, or NULL if not found. */ struct ub_entity *ub_get_ent_by_eid(unsigned int eid); @@ -564,8 +564,7 @@ struct ub_entity *ub_get_entity(unsigned int vendor, unsigned int entity, * @uent: UB entity. * @enable: Enable or disable. * - * Enables or disables the entity access bus and the path through which - * the bus accesses the entity. + * Enable or disable the communication channel between entity and user host. * * Context: Any context. */ @@ -586,31 +585,31 @@ int ub_set_user_info(struct ub_entity *uent); * ub_unset_user_info() - Deinitialize host information for the entity. * @uent: UB entity. * - * Clearing the Host Information of a entity. + * Clearing the host information of an entity. * * Context: Any context. */ void ub_unset_user_info(struct ub_entity *uent); /** - * ub_enable_entities() - Enable ues of mue in batches. - * @pue: UB mue. + * ub_enable_entities() - Enable UEs of MUE in batches. + * @pue: UB MUE. * @nums: Number of enabled entities. * - * Create ues in batches, initialize them, and add them to the system. + * Create and initialize UEs in batches and add to the system. * * Context: Any context. - * Return: 0 if success, or %-EINVAL if @pue type is not mue or nums over - * mue's total ue nums, or %-ENOMEM if the system is out of memory, + * Return: 0 if success, or %-EINVAL if @pue type is not MUE or nums over + * MUE's total UE nums, or %-ENOMEM if the system is out of memory, * or other failed negative values. */ int ub_enable_entities(struct ub_entity *pue, int nums); /** - * ub_disable_entities() - Disable ues of mue in batches. - * @pue: UB mue. + * ub_disable_entities() - Disable UEs of MUE in batches. + * @pue: UB MUE. * - * Remove all enabled ues under the mue from the system. + * Remove all enabled UEs under the MUE from the system. * * Context: Any context. */ @@ -618,29 +617,29 @@ void ub_disable_entities(struct ub_entity *pue); /** * ub_enable_ue() - Enable a single ue. - * @pue: UB mue. + * @pue: UB MUE. * @entity_idx: Number of the entity to be enabled. * - * Create a specified ue under mue, initialize the ue, + * Create a specified UE under MUE, initialize the ue, * and add it to the system. * * Context: Any context. - * Return: 0 if success, or %-EINVAL if @pue type is not mue or @entity_idx - * is no longer in the ue range of mue, or %-EEXIST if entity has been + * Return: 0 if success, or %-EINVAL if @pue type is not MUE or @entity_idx + * is no longer in the UE range of MUE, or %-EEXIST if entity has been * enabled, or other failed negative values. */ int ub_enable_ue(struct ub_entity *pue, int entity_idx); /** * ub_disable_ue() - Disable a single ue. - * @pue: UB mue. + * @pue: UB MUE. * @entity_idx: Number of the entity to be disabled. * - * Remove a specified ue. + * Remove a specified UE. * * Context: Any context. - * Return: 0 if success, or %-EINVAL if @pue type is not mue or @entity_idx - * is no longer in the ue range of mue, or %-ENODEV if entity hasn't + * Return: 0 if success, or %-EINVAL if @pue type is not MUE or @entity_idx + * is no longer in the UE range of MUE, or %-ENODEV if entity hasn't * been enabled. */ int ub_disable_ue(struct ub_entity *pue, int entity_idx); @@ -660,7 +659,7 @@ bool ub_get_entity_flex_en(void); * @uent: UB entity. * * Return the EID of bus instance if the entity has already been bound, - * or controller's EID. + * otherwise return controller's EID. * * Context: Any context. * Return: positive number if success, or %-EINVAL if @dev is %NULL, @@ -741,7 +740,7 @@ void ub_unregister_share_port(struct ub_entity *uent, u16 port_id, * ub_reset_entity() - Function entity level reset. * @ent: UB entity. * - * Reset a single entity without affecting other entities, If you want to reuse + * Reset a single entity without affecting other entities. If you want to reuse * the entity after reset, you need to re-initialize it. * * Context: Any context @@ -755,7 +754,7 @@ int ub_reset_entity(struct ub_entity *ent); * ub_device_reset() - Device level reset. * @ent: UB entity. * - * Reset Device, include all entities under the device, If you want to reuse + * Reset device, including all entities under the device. If you want to reuse * the device after reset, you need to re-initialize it. * * Context: Any context @@ -769,10 +768,10 @@ int ub_device_reset(struct ub_entity *ent); * @uent: UB entity. * @vdm_pld: Vendor private message payload context. * - * Send a vendor private message to the entity. Response will put in - * vdm_pld->rsp_pld, and will fill in vdm->rsp_pld_len. + * Send a vendor private message to the entity. Response will be put in + * vdm_pld->rsp_pld, and response length is stored in vdm->rsp_pld_len. * - * Context: Any context, It will take spin_lock_irqsave()/spin_unlock_restore() + * Context: Any context, it will take spin_lock_irqsave()/spin_unlock_restore() * Return: 0 if success, or %-EINVAL if parameters invalid, * or %-ENOMEM if system out of memory, or other failed negative values. */ @@ -794,10 +793,10 @@ unsigned int ub_irq_calc_affinity_vectors(unsigned int minvec, void ub_disable_intr(struct ub_entity *uent); /** - * ub_intr_vec_count() - Interrupt Vectors Supported by a entity. + * ub_intr_vec_count() - Interrupt Vectors Supported by an entity. * @uent: UB entity. * - * Querying the Number of Interrupt Vectors Supported by a entity. + * Querying the Number of Interrupt Vectors Supported by an entity. * For interrupt type 2. * * Context: Any context. @@ -806,10 +805,10 @@ void ub_disable_intr(struct ub_entity *uent); u32 ub_intr_vec_count(struct ub_entity *uent); /** - * ub_int_type1_vec_count() - Interrupt Vectors Supported by a entity. + * ub_int_type1_vec_count() - Interrupt Vectors Supported by an entity. * @uent: UB entity. * - * Querying the Number of Interrupt Vectors Supported by a entity. + * Querying the Number of Interrupt Vectors Supported by an entity. * For interrupt type 1. * * Context: Any context. @@ -862,7 +861,7 @@ static inline int ub_alloc_irq_vectors(struct ub_entity *uent, int ub_irq_vector(struct ub_entity *uent, unsigned int nr); /** - * ub_irq_get_affinity() - Get a entity interrupt vector affinity + * ub_irq_get_affinity() - Get an entity interrupt vector affinity * @uent: the UB entity to operate on * @nr: entity-relative interrupt vector index (0-based); has different * meanings, depending on interrupt mode: @@ -882,7 +881,7 @@ const struct cpumask *ub_irq_get_affinity(struct ub_entity *uent, int nr); * @uent: UB entity. * @entity_idx: Number of the entity to be activated. * - * Context: Any context, It will take device_trylock()/device_unlock() + * Context: Any context, it will take device_trylock()/device_unlock() * Return: 0 if success, or %-EINVAL if the device doesn't match the driver, * or %-EBUSY if can't get device_trylock(), or other failed negative values. */ @@ -893,7 +892,7 @@ int ub_activate_entity(struct ub_entity *uent, u32 entity_idx); * @uent: UB entity. * @entity_idx: Number of the entity to be deactivated. * - * Context: Any context, It will take device_trylock()/device_unlock() + * Context: Any context, it will take device_trylock()/device_unlock() * Return: 0 if success, or %-EINVAL if the entity doesn't match the driver, * or %-EBUSY if can't get device_trylock(), or other failed negative values. */ @@ -908,7 +907,7 @@ int ub_deactivate_entity(struct ub_entity *uent, u32 entity_idx); * Initiate configuration access to the specified address of the entity * configuration space and read 1 byte. * - * Context: Any context, It will take spin_lock_irqsave()/spin_unlock_restore() + * Context: Any context, it will take spin_lock_irqsave()/spin_unlock_restore() * Return: 0 if success, or negative value if failed. */ int ub_cfg_read_byte(struct ub_entity *uent, u64 pos, u8 *val); @@ -923,7 +922,7 @@ int ub_cfg_read_dword(struct ub_entity *uent, u64 pos, u32 *val); * Initiate configuration access to the specified address of the entity * configuration space and write 1 byte. * - * Context: Any context, It will take spin_lock_irqsave()/spin_unlock_restore() + * Context: Any context, it will take spin_lock_irqsave()/spin_unlock_restore() * Return: 0 if success, or negative value if failed. */ int ub_cfg_write_byte(struct ub_entity *uent, u64 pos, u8 val); @@ -935,7 +934,7 @@ int ub_cfg_write_dword(struct ub_entity *uent, u64 pos, u32 val); * @uent: UB entity pointer. * * Context: Any context. - * Return: uent, or NULL if @uent is NULL. + * Return: @uent itself, or NULL if @uent is NULL. */ struct ub_entity *ub_entity_get(struct ub_entity *uent); @@ -953,9 +952,9 @@ void ub_entity_put(struct ub_entity *uent); * @max_num: Buffer size. * @real_num: Real entities num. * - * All ub bus controllers in the system are returned. Increase the reference - * counting of all entities by 1. Remember to call ub_put_bus_controller() after - * using it. + * All ub bus controllers in the system are collected in @uents. Increase the + * reference counting of all entities by 1. Remember to call + * ub_put_bus_controller() after using it. * * Context: Any context. * Return: 0 if success, or %-EINVAL if input parameter is NULL, @@ -1018,8 +1017,8 @@ void ub_unregister_driver(struct ub_driver *drv); * ub_stop_ent() - Stop the entity. * @uent: UB entity. * - * Call device_release_driver(), user can't use it again, if it's a mue, - * will stop all ues under it, if it's entity0, will stop all entity under it. + * Call device_release_driver(), user can't use it again. If it's a MUE, + * will stop all UEs under it. If it's entity0, will stop all entities under it. * * Context: Any context. */ @@ -1029,8 +1028,9 @@ void ub_stop_ent(struct ub_entity *uent); * ub_stop_and_remove_ent() - Stop and remove the entity from system. * @uent: UB entity. * - * Call device_release_driver() and device_unregister(), if it's a mue, - * will remove all ues under it, if it's entity0, will remove all entity under it. + * Call device_release_driver() and device_unregister(). If it's a MUE, + * will remove all UEs under it. If it's entity0, will remove all entities + * under it. * * Context: Any context. */ -- Gitee From 5c67c99685a4817ca445b928dae3a460bd6cf0ae Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Tue, 28 Oct 2025 19:14:38 +0800 Subject: [PATCH 097/214] ub:ubus: Support for UB Memory Decoder general layer implementation ANBZ: #45460 commit fa547b73756dd4755d0258824a230b31f9ea7b4f openEuler. Supports triggering the memory decoder's flush function via ubus driver and implements RAS processing. Signed-off-by: Yuhao Xiang Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/memory.c | 186 ++++++++++++++++++++++++++++++ drivers/ub/ubus/memory.h | 59 ++++++++++ drivers/ub/ubus/ubus_controller.h | 4 + drivers/ub/ubus/ubus_entity.c | 3 + include/ub/ubus/ub-mem-decoder.h | 100 ++++++++++++++++ include/ub/ubus/ubus.h | 3 + 7 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/memory.c create mode 100644 drivers/ub/ubus/memory.h create mode 100644 include/ub/ubus/ub-mem-decoder.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 7456bdb0a787..59505977dd2f 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o omm.o ioctl.o eu.o link.o -ubus-y += instance.o pool.o +ubus-y += instance.o pool.o memory.o ubus-y += services/ras.o services/service.o services/gucd.o ubus-y += services/hotplug/hotplug_core.o services/hotplug/hotplug_ctrl.o diff --git a/drivers/ub/ubus/memory.c b/drivers/ub/ubus/memory.c new file mode 100644 index 000000000000..e7b3144db4bd --- /dev/null +++ b/drivers/ub/ubus/memory.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus memory: " fmt + +#include +#include "ubus.h" +#include "ubus_controller.h" +#include "memory.h" + +static ubmem_ras_handler handler; + +static bool ub_mem_uent_valid(struct ub_entity *uent) +{ + if (!uent || !uent->ubc) + return false; + + return is_ibus_controller(uent); +} + +void ub_mem_decoder_init(struct ub_entity *uent) +{ + struct ub_bus_controller *ubc; + int ret; + + if (!ub_mem_uent_valid(uent)) + return; + + ubc = uent->ubc; + if (ubc->ops && ubc->ops->mem_decoder_create) { + ret = ubc->ops->mem_decoder_create(ubc); + WARN_ON(ret); + } else { + dev_warn(&ubc->dev, + "ubc ops or ubc ops mem_decoder_create is null.\n"); + } +} + +void ub_mem_decoder_uninit(struct ub_entity *uent) +{ + struct ub_bus_controller *ubc; + + if (!ub_mem_uent_valid(uent)) + return; + + ubc = uent->ubc; + if (ubc->ops && ubc->ops->mem_decoder_remove) + ubc->ops->mem_decoder_remove(ubc); + else + dev_warn(&ubc->dev, "ubc ops mem_decoder_remove is null.\n"); +} + +void ub_mem_ras_handler_register(ubmem_ras_handler rh) +{ + handler = rh; +} +EXPORT_SYMBOL_GPL(ub_mem_ras_handler_register); + +void ub_mem_ras_handler_unregister(void) +{ + handler = NULL; +} +EXPORT_SYMBOL_GPL(ub_mem_ras_handler_unregister); + +ubmem_ras_handler ub_mem_ras_handler_get(void) +{ + return handler; +} +EXPORT_SYMBOL_GPL(ub_mem_ras_handler_get); + +void ub_mem_init_usi(struct ub_entity *uent) +{ + if (!uent->ubc) { + pr_err("ubc not exist, can't init usi\n"); + return; + } + + if (uent->ubc->ops && uent->ubc->ops->register_ubmem_irq) + uent->ubc->ops->register_ubmem_irq(uent->ubc); + else + dev_warn(&uent->ubc->dev, "ubc ops register_ubmem_irq is null.\n"); +} + +void ub_mem_uninit_usi(struct ub_entity *uent) +{ + if (!uent->ubc) { + pr_err("ubc not exist, can't uninit usi\n"); + return; + } + + if (uent->ubc->ops && uent->ubc->ops->unregister_ubmem_irq) + uent->ubc->ops->unregister_ubmem_irq(uent->ubc); + else + dev_warn(&uent->ubc->dev, "ubc ops unregister_ubmem_irq is null.\n"); +} + +void ub_mem_drain_start(u32 scna) +{ + struct ub_mem_device *mem_device; + struct ub_bus_controller *ubc; + + ubc = ub_find_bus_controller_by_cna(scna); + if (!ubc) { + pr_err("No ubc has cna of %u\n", scna); + return; + } + + mem_device = ubc->mem_device; + if (!mem_device) { + dev_err(&ubc->dev, "ubc mem_device is null.\n"); + return; + } + + if (mem_device->ops && mem_device->ops->mem_drain_start) + mem_device->ops->mem_drain_start(mem_device); + else + dev_warn(mem_device->dev, "ub mem_device ops mem_drain_start is null.\n"); +} +EXPORT_SYMBOL_GPL(ub_mem_drain_start); + +int ub_mem_drain_state(u32 scna) +{ + struct ub_mem_device *mem_device; + struct ub_bus_controller *ubc; + + ubc = ub_find_bus_controller_by_cna(scna); + if (!ubc) { + pr_err("No ubc has cna of %u\n", scna); + return -ENODEV; + } + + mem_device = ubc->mem_device; + if (!mem_device) { + dev_err(&ubc->dev, "ubc mem_device is null.\n"); + return -EINVAL; + } + + if (mem_device->ops && mem_device->ops->mem_drain_state) + return mem_device->ops->mem_drain_state(mem_device); + + dev_warn(mem_device->dev, "ub memory decoder ops mem_drain_state is null.\n"); + return 0; +} +EXPORT_SYMBOL_GPL(ub_mem_drain_state); + +int ub_mem_get_numa_id(u32 scna) +{ + struct ub_bus_controller *ubc; + + ubc = ub_find_bus_controller_by_cna(scna); + if (!ubc) { + pr_err("No ubc has cna of %u\n", scna); + return NUMA_NO_NODE; + } + + return pxm_to_node(ubc->attr.proximity_domain); +} +EXPORT_SYMBOL_GPL(ub_mem_get_numa_id); + +bool ub_memory_validate_pa(u32 scna, u64 pa_start, u64 pa_end, bool cacheable) +{ + struct ub_mem_device *mem_device; + struct ub_bus_controller *ubc; + + ubc = ub_find_bus_controller_by_cna(scna); + if (!ubc) { + pr_err("No ubc has cna of %u\n", scna); + return false; + } + + mem_device = ubc->mem_device; + if (!mem_device) { + dev_err(&ubc->dev, "ubc mem_device is null.\n"); + return false; + } + + if (mem_device->ops && mem_device->ops->mem_validate_pa) + return mem_device->ops->mem_validate_pa(ubc, pa_start, pa_end, + cacheable); + + dev_warn(mem_device->dev, "ub memory decoder ops mem_drain_state is null.\n"); + return false; +} +EXPORT_SYMBOL_GPL(ub_memory_validate_pa); diff --git a/drivers/ub/ubus/memory.h b/drivers/ub/ubus/memory.h new file mode 100644 index 000000000000..af32951937ba --- /dev/null +++ b/drivers/ub/ubus/memory.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __MEMORY_H__ +#define __MEMORY_H__ + +#include +#include +#include +#include + +#define MAX_RAS_ERROR_SOURCES_CNT 256 + +void ub_mem_decoder_init(struct ub_entity *uent); +void ub_mem_decoder_uninit(struct ub_entity *uent); +void ub_mem_init_usi(struct ub_entity *uent); +void ub_mem_uninit_usi(struct ub_entity *uent); + +struct ub_mem_ras_err_info { + enum ras_err_type type; + u64 hpa; +}; + +struct ub_mem_ras_ctx { + DECLARE_KFIFO(ras_fifo, struct ub_mem_ras_err_info, + MAX_RAS_ERROR_SOURCES_CNT); +}; + +struct ub_mem_device_ops { + void (*mem_drain_start)(struct ub_mem_device *mem_device); + int (*mem_drain_state)(struct ub_mem_device *mem_device); + bool (*mem_validate_pa)(struct ub_bus_controller *ubc, u64 pa_start, + u64 pa_end, bool cacheable); + + CK_KABI_RESERVE(1) + CK_KABI_RESERVE(2) + CK_KABI_RESERVE(3) + CK_KABI_RESERVE(4) + CK_KABI_RESERVE(5) + CK_KABI_RESERVE(6) + CK_KABI_RESERVE(7) + CK_KABI_RESERVE(8) +}; + +struct ub_mem_device { + struct device *dev; + struct ub_entity *uent; + struct ub_mem_ras_ctx ras_ctx; + int ubmem_irq_num; + const struct ub_mem_device_ops *ops; + void *priv_data; + + CK_KABI_RESERVE(1) + CK_KABI_RESERVE(2) +}; + +#endif /* __MEMORY_H__ */ diff --git a/drivers/ub/ubus/ubus_controller.h b/drivers/ub/ubus/ubus_controller.h index 483d9d8b9ec0..d30911399dcc 100644 --- a/drivers/ub/ubus/ubus_controller.h +++ b/drivers/ub/ubus/ubus_controller.h @@ -11,6 +11,10 @@ struct ub_bus_controller_ops { int (*eu_table_init)(struct ub_bus_controller *ubc); void (*eu_table_uninit)(struct ub_bus_controller *ubc); int (*eu_cfg)(struct ub_bus_controller *ubc, bool flag, u32 eid, u16 upi); + int (*mem_decoder_create)(struct ub_bus_controller *ubc); + void (*mem_decoder_remove)(struct ub_bus_controller *ubc); + void (*register_ubmem_irq)(struct ub_bus_controller *ubc); + void (*unregister_ubmem_irq)(struct ub_bus_controller *ubc); void (*register_decoder_base_addr)(struct ub_bus_controller *ubc, u64 *cmd_queue, u64 *event_queue); int (*entity_enable)(struct ub_entity *uent, u8 enable); diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index ebc9d764f670..6031469a27b9 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -17,6 +17,7 @@ #include "eid.h" #include "cna.h" #include "resource.h" +#include "memory.h" #include "ubus_controller.h" #include "ubus_driver.h" #include "ubus_inner.h" @@ -438,6 +439,7 @@ void ub_start_ent(struct ub_entity *uent) WARN_ON(ret); ub_create_sysfs_dev_files(uent); + ub_mem_decoder_init(uent); if (!((is_p_device(uent) || is_p_idevice(uent)) && is_dynamic(uent->bi))) { uent->match_driver = true; @@ -531,6 +533,7 @@ void ub_remove_ent(struct ub_entity *uent) list_del(&uent->node); up_write(&ub_bus_sem); + ub_mem_decoder_uninit(uent); ub_uninit_capabilities(uent); ub_unconfigure_ent(uent); ub_entity_unset_mmio(uent); diff --git a/include/ub/ubus/ub-mem-decoder.h b/include/ub/ubus/ub-mem-decoder.h new file mode 100644 index 000000000000..56ba2bed34b0 --- /dev/null +++ b/include/ub/ubus/ub-mem-decoder.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef _UB_UBUS_UB_MEM_DECODER_H_ +#define _UB_UBUS_UB_MEM_DECODER_H_ + +#include + +enum ras_err_type { + UB_MEM_ATOMIC_DATA_ERR = 0, + UB_MEM_READ_DATA_ERR, + UB_MEM_FLOW_POISON, + UB_MEM_FLOW_READ_AUTH_POISON, + UB_MEM_FLOW_READ_AUTH_RESPERR, + UB_MEM_TIMEOUT_POISON, + UB_MEM_TIMEOUT_RESPERR, + UB_MEM_READ_DATA_POISON, + UB_MEM_READ_DATA_RESPERR, + MAR_NOPORT_VLD_INT_ERR, + MAR_FLUX_INT_ERR, + MAR_WITHOUT_CXT_ERR, + RSP_BKPRE_OVER_TIMEOUT_ERR, + MAR_NEAR_AUTH_FAIL_ERR, + MAR_FAR_AUTH_FAIL_ERR, + MAR_TIMEOUT_ERR, + MAR_ILLEGAL_ACCESS_ERR, + REMOTE_READ_DATA_ERR_OR_WRITE_RESPONSE_ERR, +}; + +typedef int (*ubmem_ras_handler)(u64, enum ras_err_type); + +#ifdef CONFIG_UB_UBUS + +/* + * ub_mem_ras_handler_register - register ub memory ras handler for OBMM + * @handler: OBMM ras handler + */ +void ub_mem_ras_handler_register(ubmem_ras_handler handler); + +/* + * ub_mem_ras_handler_unregister - unregister ub memory ras handler for OBMM + */ +void ub_mem_ras_handler_unregister(void); + +/* + * ub_mem_ras_handler_get - get ub memory ras handler + * RETURN VALUE: ubmem_ras_handler + */ +ubmem_ras_handler ub_mem_ras_handler_get(void); + +/* + * ub_mem_drain_start - start ub memory drain + * @scna: source cna + */ +void ub_mem_drain_start(u32 scna); + +/* + * ub_mem_drain_state - whether ub memory drain has been finished + * @scna: source cna + * RETURN VALUE: + * 0 if drain not finish; 1 if drain finish + * other if failed. + */ +int ub_mem_drain_state(u32 scna); + +/* + * ub_mem_get_numa_id - get ubc numa id from scna + * @scna: source cna + * RETURN VALUE: + * numa id + */ +int ub_mem_get_numa_id(u32 scna); + +/* + * ub_memory_validate_pa - Determine whether hpa is valid + * @scna: source cna + * @pa_start: hpa start address + * @pa_end: hpa end address + * @cacheable: cacheable flag + * RETURN VALUE: + * true if hpa is valid + * false if hpa is invalid + */ +bool ub_memory_validate_pa(u32 scna, u64 pa_start, u64 pa_end, bool cacheable); + +#else /* CONFIG_UB_UBUS is not enabled */ +static inline void ub_mem_ras_handler_register(ubmem_ras_handler handler) {} +static inline void ub_mem_ras_handler_unregister(void) {} +static inline ubmem_ras_handler ub_mem_ras_handler_get(void) { return NULL; } +static inline void ub_mem_drain_start(u32 scna) {} +static inline int ub_mem_drain_state(u32 scna) { return -EINVAL; } +static inline int ub_mem_get_numa_id(u32 scna) { return NUMA_NO_NODE; } +static inline bool ub_memory_validate_pa(u32 scna, u64 pa_start, u64 pa_end, + bool cacheable) +{ return false; } +#endif /* CONFIG_UB_UBUS */ + +#endif /* _UB_UBUS_UB_MEM_DECODER_H_ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index c33542b11bcb..3bf0bd47cb24 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -423,6 +423,9 @@ struct ub_bus_controller { struct ub_bus_instance *bi; struct ub_bus_instance *cluster_bi; + /* ub memory decoder */ + struct ub_mem_device *mem_device; + void *data; struct dentry *debug_root; -- Gitee From 5e263ef13b3dff025d39d29f483fe7802aac2f85 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Wed, 29 Oct 2025 09:34:25 +0800 Subject: [PATCH 098/214] ub:hisi-ubus: Support for UB Memory Decoder Initialization ANBZ: #45460 commit d392ae30c331be4fde2c2f9aa5e649aa1071c28c openEuler. Initialize the UB memory decoder, and implement the corresponding flush trigger, flush status query, and address validity verification interfaces. Signed-off-by: Yuhao Xiang Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/vendor/hisilicon/Makefile | 2 +- drivers/ub/ubus/vendor/hisilicon/controller.c | 2 + drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h | 2 + drivers/ub/ubus/vendor/hisilicon/memory.c | 196 ++++++++++++++++++ 4 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/vendor/hisilicon/memory.c diff --git a/drivers/ub/ubus/vendor/hisilicon/Makefile b/drivers/ub/ubus/vendor/hisilicon/Makefile index 653de75f2539..998c0e09aeef 100644 --- a/drivers/ub/ubus/vendor/hisilicon/Makefile +++ b/drivers/ub/ubus/vendor/hisilicon/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ hisi_ubus-objs := hisi-ubus.o controller.o vdm.o local-ras.o msg.o msg-core.o -hisi_ubus-objs += msg-debugfs.o eu-table.o +hisi_ubus-objs += msg-debugfs.o eu-table.o memory.o obj-$(CONFIG_UB_HISI_UBUS) += hisi_ubus.o diff --git a/drivers/ub/ubus/vendor/hisilicon/controller.c b/drivers/ub/ubus/vendor/hisilicon/controller.c index 52d4086d39b2..e5f806abc0e9 100644 --- a/drivers/ub/ubus/vendor/hisilicon/controller.c +++ b/drivers/ub/ubus/vendor/hisilicon/controller.c @@ -17,6 +17,8 @@ static struct ub_bus_controller_ops hi_ubc_ops = { .eu_table_init = hi_eu_table_init, .eu_table_uninit = hi_eu_table_uninit, .eu_cfg = hi_eu_cfg, + .mem_decoder_create = hi_mem_decoder_create, + .mem_decoder_remove = hi_mem_decoder_remove, .register_decoder_base_addr = hi_register_decoder_base_addr, .entity_enable = hi_send_entity_enable_msg, }; diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h index ae86a0e22ff1..119efd37b407 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h @@ -35,6 +35,8 @@ struct hi_ubc_private_data { int hi_eu_table_init(struct ub_bus_controller *ubc); void hi_eu_table_uninit(struct ub_bus_controller *ubc); int hi_eu_cfg(struct ub_bus_controller *ubc, bool add, u32 eid, u16 upi); +int hi_mem_decoder_create(struct ub_bus_controller *ubc); +void hi_mem_decoder_remove(struct ub_bus_controller *ubc); void hi_register_decoder_base_addr(struct ub_bus_controller *ubc, u64 *cmd_queue, u64 *event_queue); int hi_send_entity_enable_msg(struct ub_entity *uent, u8 enable); diff --git a/drivers/ub/ubus/vendor/hisilicon/memory.c b/drivers/ub/ubus/vendor/hisilicon/memory.c new file mode 100644 index 000000000000..505ba7dca4fb --- /dev/null +++ b/drivers/ub/ubus/vendor/hisilicon/memory.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus hisi memory: " fmt + +#include +#include + +#include "../../ubus.h" +#include "../../memory.h" +#include "hisi-ubus.h" + +#define DRAIN_ENABLE_REG_OFFSET 0x24 +#define DRAIN_STATE_REG_OFFSET 0x28 + +#define hpa_gen(addr_h, addr_l) (((u64)(addr_h) << 32) | (addr_l)) + +struct ub_mem_decoder { + struct device *dev; + struct ub_entity *uent; + void *base_reg; +}; + +static bool hi_mem_validate_pa(struct ub_bus_controller *ubc, + u64 pa_start, u64 pa_end, bool cacheable); + +static void hi_mem_drain_start(struct ub_mem_device *mem_device) +{ + struct ub_mem_decoder *decoder, *data = mem_device->priv_data; + + if (!data) { + dev_err(mem_device->dev, "ubc mem_decoder is null.\n"); + return; + } + + for (int i = 0; i < MEM_INFO_NUM; i++) { + decoder = &data[i]; + writel(0, decoder->base_reg + DRAIN_ENABLE_REG_OFFSET); + writel(1, decoder->base_reg + DRAIN_ENABLE_REG_OFFSET); + } +} + +static int hi_mem_drain_state(struct ub_mem_device *mem_device) +{ + struct ub_mem_decoder *decoder, *data = mem_device->priv_data; + int val = 0; + + if (!data) { + dev_err(mem_device->dev, "ubc mem_decoder is null.\n"); + return 0; + } + + for (int i = 0; i < MEM_INFO_NUM; i++) { + decoder = &data[i]; + val = readb(decoder->base_reg + DRAIN_STATE_REG_OFFSET) & 0x1; + dev_info_ratelimited(decoder->dev, "ub memory decoder[%d] drain state, val=%d\n", + i, val); + if (!val) + return val; + } + + return val; +} + +static const struct ub_mem_device_ops device_ops = { + .mem_drain_start = hi_mem_drain_start, + .mem_drain_state = hi_mem_drain_state, + .mem_validate_pa = hi_mem_validate_pa, +}; + +static int hi_mem_decoder_create_one(struct ub_bus_controller *ubc, int mar_id) +{ + struct hi_ubc_private_data *data = (struct hi_ubc_private_data *)ubc->data; + struct ub_mem_decoder *decoder, *priv_data = ubc->mem_device->priv_data; + + decoder = &priv_data[mar_id]; + decoder->dev = &ubc->dev; + decoder->uent = ubc->uent; + + decoder->base_reg = ioremap(data->mem_pa_info[mar_id].decode_addr, + SZ_64); + if (!decoder->base_reg) { + dev_err(decoder->dev, "ub mem decoder base reg ioremap failed.\n"); + return -ENOMEM; + } + + return 0; +} + +static void hi_mem_decoder_remove_one(struct ub_bus_controller *ubc, int mar_id) +{ + struct ub_mem_decoder *priv_data = ubc->mem_device->priv_data; + + iounmap(priv_data[mar_id].base_reg); +} + +int hi_mem_decoder_create(struct ub_bus_controller *ubc) +{ + struct ub_mem_device *mem_device; + void *priv_data; + int ret; + + mem_device = kzalloc(sizeof(*mem_device), GFP_KERNEL); + if (!mem_device) + return -ENOMEM; + + priv_data = kcalloc(MEM_INFO_NUM, sizeof(struct ub_mem_decoder), + GFP_KERNEL); + if (!priv_data) { + kfree(mem_device); + return -ENOMEM; + } + + mem_device->dev = &ubc->dev; + mem_device->uent = ubc->uent; + mem_device->ubmem_irq_num = -1; + mem_device->ops = &device_ops; + mem_device->priv_data = priv_data; + ubc->mem_device = mem_device; + + for (int i = 0; i < MEM_INFO_NUM; i++) { + ret = hi_mem_decoder_create_one(ubc, i); + if (ret) { + dev_err(&ubc->dev, "hi mem create decoder %d failed\n", i); + for (int j = i - 1; j >= 0; j--) + hi_mem_decoder_remove_one(ubc, j); + + kfree(mem_device->priv_data); + kfree(mem_device); + ubc->mem_device = NULL; + return ret; + } + } + + return ret; +} + +void hi_mem_decoder_remove(struct ub_bus_controller *ubc) +{ + if (!ubc->mem_device) + return; + + for (int i = 0; i < MEM_INFO_NUM; i++) + hi_mem_decoder_remove_one(ubc, i); + + kfree(ubc->mem_device->priv_data); + kfree(ubc->mem_device); + ubc->mem_device = NULL; +} + +#define MB_SIZE_OFFSET 20 + +static bool ub_hpa_valid(u64 pa_start, u64 pa_end, u32 base_addr, u32 size) +{ + if (pa_start >= ((u64)base_addr << MB_SIZE_OFFSET) && + pa_end < (((u64)base_addr + (u64)size) << MB_SIZE_OFFSET)) + return true; + + return false; +} + +static bool hi_mem_validate_pa(struct ub_bus_controller *ubc, + u64 pa_start, u64 pa_end, bool cacheable) +{ + struct hi_ubc_private_data *data; + + if (!ubc->data) { + dev_err(&ubc->dev, "Ubc data is null.\n"); + return false; + } + + if (pa_end < pa_start) { + dev_err(&ubc->dev, "pa_start is over pa_end.\n"); + return false; + } + + data = (struct hi_ubc_private_data *)ubc->data; + for (u16 i = 0; i < MEM_INFO_NUM; i++) { + if (ub_hpa_valid(pa_start, pa_end, + data->mem_pa_info[i].cc_base_addr, + data->mem_pa_info[i].cc_base_size) && + cacheable) + return true; + + if (ub_hpa_valid(pa_start, pa_end, + data->mem_pa_info[i].nc_base_addr, + data->mem_pa_info[i].nc_base_size) && + !cacheable) + return true; + } + + dev_err(&ubc->dev, "pa_start-pa_end is invalid.\n"); + return false; +} -- Gitee From 6f93d3d39649eaa6d57e9e92cb803b0654590b36 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Wed, 29 Oct 2025 09:46:02 +0800 Subject: [PATCH 099/214] ub:hisi-ubus: Support for ub memory decoder ras handle ANBZ: #45460 commit 82b774b2b67a64127583ba5aed012dc32bee0ae7 openEuler. Implement RAS exception handling for the ub memory decoder, including interrupt callback handling functions, error handling interfaces, etc. Signed-off-by: Yuhao Xiang Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/services/gucd.c | 8 +- drivers/ub/ubus/vendor/hisilicon/controller.c | 2 + drivers/ub/ubus/vendor/hisilicon/hisi-msg.h | 1 + drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h | 2 + drivers/ub/ubus/vendor/hisilicon/memory.c | 228 ++++++++++++++++++ .../ub/ubus/vendor/hisilicon/memory_trace.h | 46 ++++ 6 files changed, 285 insertions(+), 2 deletions(-) create mode 100644 drivers/ub/ubus/vendor/hisilicon/memory_trace.h diff --git a/drivers/ub/ubus/services/gucd.c b/drivers/ub/ubus/services/gucd.c index 35a0cf35e20a..ca5f0a3578e8 100644 --- a/drivers/ub/ubus/services/gucd.c +++ b/drivers/ub/ubus/services/gucd.c @@ -8,6 +8,7 @@ #include "../ubus.h" #include "../decoder.h" #include "../ubus_driver.h" +#include "../memory.h" #include "service.h" static const struct ub_device_id component_device_ids[] = { @@ -128,15 +129,18 @@ static void ub_setup_bus_controller(struct ub_entity *uent) return; } - if ((u32)usi_count < vec_num_max) + if ((u32)usi_count < vec_num_max) { ub_err(uent, "alloc irq vectors failed, usi count=%d, vec_num_max=%u\n", usi_count, vec_num_max); - else + } else { ub_init_decoder_usi(uent); + ub_mem_init_usi(uent); + } } static void ub_unset_bus_controller(struct ub_entity *uent) { + ub_mem_uninit_usi(uent); ub_uninit_decoder_usi(uent); ub_disable_intr(uent); ub_disable_err_msq_ctrl(uent); diff --git a/drivers/ub/ubus/vendor/hisilicon/controller.c b/drivers/ub/ubus/vendor/hisilicon/controller.c index e5f806abc0e9..d7ea5c118d32 100644 --- a/drivers/ub/ubus/vendor/hisilicon/controller.c +++ b/drivers/ub/ubus/vendor/hisilicon/controller.c @@ -19,6 +19,8 @@ static struct ub_bus_controller_ops hi_ubc_ops = { .eu_cfg = hi_eu_cfg, .mem_decoder_create = hi_mem_decoder_create, .mem_decoder_remove = hi_mem_decoder_remove, + .register_ubmem_irq = hi_register_ubmem_irq, + .unregister_ubmem_irq = hi_unregister_ubmem_irq, .register_decoder_base_addr = hi_register_decoder_base_addr, .entity_enable = hi_send_entity_enable_msg, }; diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h b/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h index 78476423bdfb..d1a68934d174 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-msg.h @@ -59,6 +59,7 @@ enum hi_task_type { enum hi_msgq_private_opcode { EU_TABLE_CFG_CMD = 2, + GET_UBMEM_EVENT_CMD = 4 }; enum hi_msgq_user { diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h index 119efd37b407..9aa3ba5521c1 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h @@ -37,6 +37,8 @@ void hi_eu_table_uninit(struct ub_bus_controller *ubc); int hi_eu_cfg(struct ub_bus_controller *ubc, bool add, u32 eid, u16 upi); int hi_mem_decoder_create(struct ub_bus_controller *ubc); void hi_mem_decoder_remove(struct ub_bus_controller *ubc); +void hi_register_ubmem_irq(struct ub_bus_controller *ubc); +void hi_unregister_ubmem_irq(struct ub_bus_controller *ubc); void hi_register_decoder_base_addr(struct ub_bus_controller *ubc, u64 *cmd_queue, u64 *event_queue); int hi_send_entity_enable_msg(struct ub_entity *uent, u8 enable); diff --git a/drivers/ub/ubus/vendor/hisilicon/memory.c b/drivers/ub/ubus/vendor/hisilicon/memory.c index 505ba7dca4fb..4d4f80f847fc 100644 --- a/drivers/ub/ubus/vendor/hisilicon/memory.c +++ b/drivers/ub/ubus/vendor/hisilicon/memory.c @@ -9,12 +9,22 @@ #include #include "../../ubus.h" +#include "../../msg.h" #include "../../memory.h" +#include "hisi-msg.h" #include "hisi-ubus.h" +#define CREATE_TRACE_POINTS +#include "memory_trace.h" #define DRAIN_ENABLE_REG_OFFSET 0x24 #define DRAIN_STATE_REG_OFFSET 0x28 +#define HI_GET_UBMEM_EVENT_REQ_SIZE 4 +#define HI_GET_UBMEM_EVENT_RSP_SIZE 772 +#define MEM_EVENT_MAX_NUM 16 +#define MAR_ERR_ADDR_COUNT 10 +#define MAR_ERR_ADDR_SIZE 2 + #define hpa_gen(addr_h, addr_l) (((u64)(addr_h) << 32) | (addr_l)) struct ub_mem_decoder { @@ -23,6 +33,28 @@ struct ub_mem_decoder { void *base_reg; }; +struct hi_ubmem_event { + u32 device_ras_status3; + u32 device_ras_status4; + u32 err_addr[MAR_ERR_ADDR_COUNT]; +}; + +struct hi_get_ubmem_event_rsp { + u32 event_num; + struct hi_ubmem_event event_info[MEM_EVENT_MAX_NUM]; +}; + +struct hi_get_ubmem_event_req { + u32 rsv0; +}; + +struct hi_get_ubmem_event_pld { + union { + struct hi_get_ubmem_event_req req; + struct hi_get_ubmem_event_rsp rsp; + }; +}; + static bool hi_mem_validate_pa(struct ub_bus_controller *ubc, u64 pa_start, u64 pa_end, bool cacheable); @@ -70,6 +102,150 @@ static const struct ub_mem_device_ops device_ops = { .mem_validate_pa = hi_mem_validate_pa, }; +static int save_ras_err_info(struct ub_mem_device *mem_device, + enum ras_err_type type, u64 hpa) +{ + struct ub_mem_ras_err_info err_info = { + .type = type, + .hpa = hpa, + }; + + if (!kfifo_put(&mem_device->ras_ctx.ras_fifo, err_info)) { + dev_err(mem_device->dev, "kfifo put failed!\n"); + return -ENOMEM; + } + + return 0; +} + +static irqreturn_t hi_mem_ras_isr(int irq, void *context) +{ + struct ub_bus_controller *ubc = (struct ub_bus_controller *)context; + struct ub_mem_ras_ctx *ras_ctx = &ubc->mem_device->ras_ctx; + struct ub_mem_ras_err_info err_info; + ubmem_ras_handler handler; + int ret; + + handler = ub_mem_ras_handler_get(); + while (kfifo_get(&ras_ctx->ras_fifo, &err_info)) { + trace_mem_ras_event(ubc->mem_device, &err_info); + pr_info("ras: type=%u\n", err_info.type); + if (handler) { + ret = handler(err_info.hpa, err_info.type); + WARN_ON(ret); + } + } + + return IRQ_HANDLED; +} + +static int err_type_bitmap[] = { + /* DEVICE_RAS_STATUS_3 */ + [UB_MEM_ATOMIC_DATA_ERR] = 31, + [UB_MEM_READ_DATA_ERR] = 28, + [UB_MEM_FLOW_POISON] = 27, + [UB_MEM_FLOW_READ_AUTH_POISON] = 23, + [UB_MEM_FLOW_READ_AUTH_RESPERR] = 22, + [UB_MEM_TIMEOUT_POISON] = 21, + [UB_MEM_TIMEOUT_RESPERR] = 20, + [UB_MEM_READ_DATA_POISON] = 19, + [UB_MEM_READ_DATA_RESPERR] = 18, + /* DEVICE_RAS_STATUS_4 */ + [MAR_NOPORT_VLD_INT_ERR] = 26, + [MAR_FLUX_INT_ERR] = 25, + [MAR_WITHOUT_CXT_ERR] = 24, + [RSP_BKPRE_OVER_TIMEOUT_ERR] = 10, + /* DEVICE_RAS_STATUS_4 need save addr */ + [MAR_NEAR_AUTH_FAIL_ERR] = 21, + [MAR_FAR_AUTH_FAIL_ERR] = 22, + [MAR_TIMEOUT_ERR] = 23, + [MAR_ILLEGAL_ACCESS_ERR] = 9, + [REMOTE_READ_DATA_ERR_OR_WRITE_RESPONSE_ERR] = 11, +}; + +static int save_ras_err_info_all(struct ub_bus_controller *ubc, struct hi_ubmem_event *info) +{ + unsigned long status3_bitmap = (unsigned long)info->device_ras_status3; + unsigned long status4_bitmap = (unsigned long)info->device_ras_status4; + u32 addr_h, addr_l; + int ret = -EINVAL; + u64 hpa = 0; + int index; + int i; + + for (i = UB_MEM_ATOMIC_DATA_ERR; i <= UB_MEM_READ_DATA_RESPERR; i++) { + if (test_bit(err_type_bitmap[i], &status3_bitmap)) { + ret = save_ras_err_info(ubc->mem_device, (enum ras_err_type)i, hpa); + if (ret) + return ret; + } + } + + for (i = MAR_FLUX_INT_ERR; i <= RSP_BKPRE_OVER_TIMEOUT_ERR; i++) { + if (test_bit(err_type_bitmap[i], &status4_bitmap)) { + ret = save_ras_err_info(ubc->mem_device, (enum ras_err_type)i, hpa); + if (ret) + return ret; + } + } + + for (i = MAR_NEAR_AUTH_FAIL_ERR; i <= REMOTE_READ_DATA_ERR_OR_WRITE_RESPONSE_ERR; i++) { + if (test_bit(err_type_bitmap[i], &status4_bitmap)) { + index = MAR_ERR_ADDR_SIZE * (i - MAR_NEAR_AUTH_FAIL_ERR); + addr_h = info->err_addr[index + 1]; + addr_l = info->err_addr[index]; + hpa = hpa_gen(addr_h, addr_l); + ret = save_ras_err_info(ubc->mem_device, (enum ras_err_type)i, hpa); + if (ret) + return ret; + } + } + + /* if no_port_vld and near_auth_fail report at the same time, ignore no_port_vld */ + if (test_bit(err_type_bitmap[MAR_NOPORT_VLD_INT_ERR], &status4_bitmap) && + !test_bit(err_type_bitmap[MAR_NEAR_AUTH_FAIL_ERR], &status4_bitmap)) { + i = MAR_NOPORT_VLD_INT_ERR; + ret = save_ras_err_info(ubc->mem_device, (enum ras_err_type)i, hpa); + } + + return ret; +} + +static irqreturn_t hi_mem_ras_irq(int irq, void *context) +{ + struct ub_bus_controller *ubc = (struct ub_bus_controller *)context; + struct hi_get_ubmem_event_pld pld = {}; + struct msg_info info = {}; + u32 event_cnt; + int ret; + + message_info_init(&info, ubc->uent, &pld, &pld, + (HI_GET_UBMEM_EVENT_REQ_SIZE << MSG_REQ_SIZE_OFFSET) | + HI_GET_UBMEM_EVENT_RSP_SIZE); + ret = hi_message_private(ubc->mdev, &info, GET_UBMEM_EVENT_CMD); + if (ret) { + dev_err(&ubc->dev, "get ubmem event failed, ret=%d\n", + ret); + return IRQ_HANDLED; + } + + event_cnt = pld.rsp.event_num; + if (event_cnt == 0 || event_cnt > MEM_EVENT_MAX_NUM) { + dev_err(&ubc->dev, "event_cnt [%u] is invalid\n", event_cnt); + return IRQ_HANDLED; + } + + for (u32 i = 0; i < event_cnt; i++) { + ret = save_ras_err_info_all(ubc, &pld.rsp.event_info[i]); + if (ret == -EINVAL) { + dev_err(&ubc->dev, "save_ras_err_info failed, ret=%d\n", ret); + return IRQ_HANDLED; + } + } + + return IRQ_WAKE_THREAD; +} + static int hi_mem_decoder_create_one(struct ub_bus_controller *ubc, int mar_id) { struct hi_ubc_private_data *data = (struct hi_ubc_private_data *)ubc->data; @@ -150,6 +326,58 @@ void hi_mem_decoder_remove(struct ub_bus_controller *ubc) ubc->mem_device = NULL; } +void hi_register_ubmem_irq(struct ub_bus_controller *ubc) +{ + struct ub_entity *uent = ubc->uent; + int irq_num, ret; + u32 usi_idx; + + if (!ubc->mem_device) { + pr_err("mem device is NULL!\n"); + return; + } + + ret = ub_cfg_read_dword(uent, UB_MEM_USI_IDX, &usi_idx); + if (ret) { + ub_err(uent, "get ubmem usi idx failed, ret=%d\n", ret); + return; + } + + irq_num = ub_irq_vector(uent, usi_idx); + if (irq_num < 0) { + ub_err(uent, "ub get irq vector failed, irq num=%d\n", irq_num); + return; + } + + INIT_KFIFO(ubc->mem_device->ras_ctx.ras_fifo); + + ret = request_threaded_irq(irq_num, hi_mem_ras_irq, + hi_mem_ras_isr, IRQF_SHARED, + "ub_mem_event", ubc); + if (ret) { + ub_err(uent, "ubmem request_irq failed, ret=%d\n", ret); + return; + } + + ubc->mem_device->ubmem_irq_num = irq_num; +} + +void hi_unregister_ubmem_irq(struct ub_bus_controller *ubc) +{ + int irq_num; + + if (!ubc->mem_device) { + dev_err(&ubc->dev, "mem device is NULL!\n"); + return; + } + + irq_num = ubc->mem_device->ubmem_irq_num; + if (irq_num < 0) + return; + + free_irq((unsigned int)irq_num, (void *)ubc); +} + #define MB_SIZE_OFFSET 20 static bool ub_hpa_valid(u64 pa_start, u64 pa_end, u32 base_addr, u32 size) diff --git a/drivers/ub/ubus/vendor/hisilicon/memory_trace.h b/drivers/ub/ubus/vendor/hisilicon/memory_trace.h new file mode 100644 index 000000000000..9204d94aa6b8 --- /dev/null +++ b/drivers/ub/ubus/vendor/hisilicon/memory_trace.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +/* This must be outside ifdef __HISI_MEMORY_TRACE_H__ */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM ub_memory + +#if !defined(__HISI_MEMORY_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) +#define __HISI_MEMORY_TRACE_H__ + +#include + +TRACE_EVENT(mem_ras_event, + TP_PROTO(struct ub_mem_device *device, struct ub_mem_ras_err_info *info), + TP_ARGS(device, info), + + TP_STRUCT__entry( + __field(u32, eid) + __field(u32, cna) + __field(u8, type) + __field(u64, hpa) + ), + + TP_fast_assign( + __entry->eid = device->uent->eid; + __entry->cna = device->uent->cna; + __entry->type = (u8)info->type; + __entry->hpa = info->hpa; + ), + + TP_printk( + "%u-%u-%u-%llu", __entry->eid, __entry->cna, + __entry->type, __entry->hpa + ) +); + +#endif /* __HISI_MEMORY_TRACE_H__ */ + +/* This must be outside ifdef __HISI_MEMORY_TRACE_H__ */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH ../../drivers/ub/ubus/vendor/hisilicon +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE memory_trace +#include -- Gitee From b5ec9dfe3094fea9827b8b73b9deb27caadc8216 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Thu, 27 Nov 2025 09:27:24 +0800 Subject: [PATCH 100/214] ub:ubus: Add ubus and ubfi opensource document ANBZ: #45460 commit e40360b68c42fb79a086799873391e728c29c0de openEuler. Add ubus and ubfi opensource document to introduce ubus and ubfi related functions and capabilities for users in opensource society. Signed-off-by: Yahui Liu Signed-off-by: Yuhao Xiang Signed-off-by: Junlong Zheng Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- .../ABI/testing/debugfs-ub-hisi-ubus | 37 ++ Documentation/ABI/testing/sysfs-bus-ub | 427 ++++++++++++++++++ Documentation/driver-api/ub/index.rst | 18 + Documentation/driver-api/ub/ubfi.rst | 7 + Documentation/driver-api/ub/ubus.rst | 7 + Documentation/ub/index.rst | 15 + Documentation/ub/ubfi/index.rst | 11 + Documentation/ub/ubfi/ubfi.rst | 178 ++++++++ Documentation/ub/ubus/hisi_ubus.rst | 95 ++++ Documentation/ub/ubus/index.rst | 13 + Documentation/ub/ubus/ubus-service.rst | 60 +++ Documentation/ub/ubus/ubus.rst | 312 +++++++++++++ 12 files changed, 1180 insertions(+) create mode 100644 Documentation/ABI/testing/debugfs-ub-hisi-ubus create mode 100644 Documentation/ABI/testing/sysfs-bus-ub create mode 100644 Documentation/driver-api/ub/index.rst create mode 100644 Documentation/driver-api/ub/ubfi.rst create mode 100644 Documentation/driver-api/ub/ubus.rst create mode 100644 Documentation/ub/index.rst create mode 100644 Documentation/ub/ubfi/index.rst create mode 100644 Documentation/ub/ubfi/ubfi.rst create mode 100644 Documentation/ub/ubus/hisi_ubus.rst create mode 100644 Documentation/ub/ubus/index.rst create mode 100644 Documentation/ub/ubus/ubus-service.rst create mode 100644 Documentation/ub/ubus/ubus.rst diff --git a/Documentation/ABI/testing/debugfs-ub-hisi-ubus b/Documentation/ABI/testing/debugfs-ub-hisi-ubus new file mode 100644 index 000000000000..69ba558bfc02 --- /dev/null +++ b/Documentation/ABI/testing/debugfs-ub-hisi-ubus @@ -0,0 +1,37 @@ +What: /sys/kernel/debug/UB_BUS_CTL/eu_table +Date: Oct 2025 +Contact: Junlong Zheng +Description: Display the contents of the EID-UPI entry. + By default, the EID and UPI key-value pair for entry 0 is displayed. + By writing an entry index to the properties file, you can retrieve + the content of the corresponding entry. + + Example:: + + Display the content of entry5: + # echo 5 > /sys/kernel/debug/UB_BUS_CTL/eu_table + # cat /sys/kernel/debug/UB_BUS_CTL/eu_table + +What: /sys/kernel/debug/UB_BUS_CTL/hi_msgq-/reg_info +Date: Oct 2025 +Contact: Junlong Zheng +Description: Display the register information for the specified queue of the designated + UB Bus controller. + +What: /sys/kernel/debug/UB_BUS_CTL/hi_msgq-/q_entry_info +Date: Oct 2025 +Contact: Junlong Zheng +Description: Display the SQE and CQE contents of the specified MSQG for the designated + UB Bus controller. By default, the content of SQ entry 0 is displayed. + By writing the queue type and entry index to the properties file, you can + retrieve the content of the corresponding entry. + + Example:: + + Output the content of SQ entry3: + # echo 0 3 > /sys/kernel/debug/UB_BUS_CTL/hi_msgq-/q_entry_info + # cat /sys/kernel/debug/UB_BUS_CTL/hi_msgq-/q_entry_info + + Output the content of CQ entry5: + # echo 2 5 > /sys/kernel/debug/UB_BUS_CTL/hi_msgq-/q_entry_info + # cat /sys/kernel/debug/UB_BUS_CTL/hi_msgq-/q_entry_info \ No newline at end of file diff --git a/Documentation/ABI/testing/sysfs-bus-ub b/Documentation/ABI/testing/sysfs-bus-ub new file mode 100644 index 000000000000..f7b3193958b7 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-ub @@ -0,0 +1,427 @@ +What: /sys/bus/ub/cluster +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Indicates the current system operating mode: + 1 for cluster mode, 0 for Standalone mode. + +What: /sys/bus/ub/instance +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display the list of bus instances created in the current system. + By default, it starts from the first one. Writing numbers into + the file can change the starting position of the output bus + instance. + +What: /sys/bus/ub/drivers/.../bind +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Writing an entity number to this file will cause the driver + to attempt to bind to the entity. This is useful for + overriding default bindings. The entity number is + the same as found in /sys/bus/ub/devices/. + For example:: + + # echo 00002 > /sys/bus/ub/drivers/sample/bind + +What: /sys/bus/ub/drivers/.../unbind +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Writing an entity number to this file will cause the + driver to attempt to unbind from the entity. This may be + useful when overriding default bindings. The entity + number is the same as found in /sys/bus/ub/devices/. + For example:: + + # echo 00002 > /sys/bus/ub/drivers/sample/unbind + +What: /sys/bus/ub/drivers/.../new_id +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Writing a device ID to this file will attempt to + dynamically add a new device ID to a UB device driver. + This may allow the driver to support more hardware than + was included in the driver's static device ID support + table at compile time. The format for the device ID is: + VVVV DDDD MVVV MMMM CCCC MMMM PPPP. That is Vendor ID, + Device ID, Module Vendor ID, Module ID, Class, Class Mask + and Private Driver Data. The Vendor ID and Device ID fields + are required, the rest are optional. Upon successfully + adding an ID, the driver will probe for the device and + attempt to bind to it. + For example:: + + # echo cc08 a001 > /sys/bus/ub/drivers/sample/new_id + +What: /sys/bus/ub/drivers/.../remove_id +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Writing a device ID to this file will remove an ID + that was dynamically added via the new_id sysfs entry. + The format for the device ID is: + VVVV DDDD MVVV MMMM CCCC MMMM. That is Vendor ID, Device + ID, Module Vendor ID, Module ID, Class and Class Mask. + The Vendor ID and Device ID fields are required, the rest + are optional. After successfully removing an ID, + the driver will no longer support the device. + This is useful to ensure auto probing won't + match the driver to the device. + For example:: + + # echo cc08 a001 > /sys/bus/ub/drivers/sample/remove_id + +What: /sys/bus/ub/devices/.../class_code +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display the class code type of the entity. + +What: /sys/bus/ub/devices/.../config +Date: Oct 2025 +Contact: Junlong Zheng +Description: + A channel is provided for user-mode programs to access the + entity configuration space. User programs can open the file + using the open system call and then perform read/write + operations on the configuration space using the pread/pwrite + system calls. + For details, please refer to the implementation of ubutils + . + +What: /sys/bus/ub/devices/.../device +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display the Device ID of the entity. + +What: /sys/bus/ub/devices/.../device_reset +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Writing 1 to this file can trigger a device-level reset. All + entities below it will be reset. + Supported only by Entity0. + +What: /sys/bus/ub/devices/.../direct_link +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display the link connection relationships and the peer + information of the ports connected to this entity. + +What: /sys/bus/ub/devices/.../driver_override +Date: Oct 2025 +Contact: Junlong Zheng +Description: + This file allows the driver for a device to be specified which + will override standard static and dynamic ID matching. When + specified, only a driver with a name matching the value written + to driver_override will have an opportunity to bind to the + device. The override is specified by writing a string to the + driver_override file (echo sample > driver_override) and + may be cleared with an empty string (echo > driver_override). + This returns the device to standard matching rules binding. + Writing to driver_override does not automatically unbind the + device from its current driver or make any attempt to + automatically load the specified driver. If no driver with a + matching name is currently loaded in the kernel, the device + will not bind to any driver. This also allows devices to + opt-out of driver binding using a driver_override name such as + "none". Only a single driver may be specified in the override, + there is no support for parsing delimiters. + +What: /sys/bus/ub/devices/.../eid +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display the entity's EID. + +What: /sys/bus/ub/devices/.../entity_idx +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display the index of the entity, numbered starting from 0. + +What: /sys/bus/ub/devices/.../guid +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display the GUID of the entity. + +What: /sys/bus/ub/devices/.../instance +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display the EID of the bus instance bound to the entity. + +What: /sys/bus/ub/devices/.../kref +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display the current reference count of the entity. + +What: /sys/bus/ub/devices/.../match_driver +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Indicates whether the current entity is ready for driver + matching. Some entities require additional initialization work, + so this entry is provided to control the entity separately. + In this case, it is necessary to ensure a certain timing + sequence; For example, the driver should be loaded only after + this status of the entity is set to 1 to ensure that the driver + probe is correctly initiated. + +What: /sys/bus/ub/devices/.../numa +Date: Oct 2025 +Contact: Junlong Zheng +Description: + This file contains the NUMA node to which the UB Entity is + attached, or -1 if the node is unknown. The initial value + comes from UBRT table, defined in the UB firmware specification. + +What: /sys/bus/ub/devices/.../primary_cna +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display the primary compact network address of the entity. + +What: /sys/bus/ub/devices/.../primary_entity +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display the entity number of the entity0 to which this entity + belongs. + +What: /sys/bus/ub/devices/.../mue_list +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display a list of all MUEs under this entity, excluding itself. + Only Entity0 has this attribute file. + +What: /sys/bus/ub/devices/.../reset +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Writing 1 to this file can trigger an entity-level reset, only + reset this entity, it will not affect other entities. + +What: /sys/bus/ub/devices/.../resource +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Information about the resource space of the entity is displayed, + with a total of 3 entries, each consisting of the following + three components: start_address, end_address, flags. + If all values are 0, it indicates that the resource space is + not supported. + +What: /sys/bus/ub/devices/.../resource +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Provide attribute files to the user-mode driver. Through the + open and mmap system calls, the resource space of an entity can + be mapped into the process space for direct access, thereby + improving the efficiency of cross-mode resource space access. + The memory attribute mapped by this interface is the device + attribute. + +What: /sys/bus/ub/devices/.../resource_wc +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Provide attribute files to the user-mode driver. Through the + open and mmap system calls, the resource space of an entity can + be mapped into the process space for direct access, thereby + improving the efficiency of cross-mode resource space access. + The memory attribute mapped by this interface is the + write-combine attribute. + +What: /sys/bus/ub/devices/.../sw_cap +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display whether forwarding capability is supported. + Only UBC supports it. + +What: /sys/bus/ub/devices/.../tid +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display the Token ID of the entity. The entity uses this + Token ID to access system memory. + +What: /sys/bus/ub/devices/.../type +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display the type of the entity. The type is a subdomain segment + of GUID. + +What: /sys/bus/ub/devices/.../ubc +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display the entity number of the UB controller associated with + the entity. + +What: /sys/bus/ub/devices/.../ub_numues +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display the number of UEs that have been enabled for this + entity. Writing a value to the file enables the UEs. The written + value must be within the range of ub_totalues. Writing 0 + disables all UEs. + Only MUE supports this file. + +What: /sys/bus/ub/devices/.../ub_total_entities +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display the number of all entities supported by this entity. + Only Entity0 supports this file. + +What: /sys/bus/ub/devices/.../ub_totalues +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display the number of UEs owned by this entity. + Only MUE supports this file. + +What: /sys/bus/ub/devices/.../ue_list +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display a list of all UEs under this entity, excluding itself. + Only MUE has this attribute file. + +What: /sys/bus/ub/devices/.../upi +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display UPI of the entity. + +What: /sys/bus/ub/devices/.../vendor +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display Vendor ID of the entity. + +What: /sys/bus/ub/devices/.../msi_irqs/ +Date: Oct 2025 +Contact: Junlong Zheng +Description: + The /sys/bus/ub/devices/.../msi_irqs/ directory contains a + variable set of files, with each file being named after a + corresponding msi irq vector allocated to that entity. + +What: /sys/bus/ub/devices/.../msi_irqs/ +Date: Oct 2025 +Contact: Junlong Zheng +Description: + This attribute indicates the mode that the irq vector named by + the file is in (msi vs. msix) + +What: /sys/bus/ub/devices/.../port/asy_link_width +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Indicates whether the port supports asymmetric link width. + Supported only on physical port. + + +What: /sys/bus/ub/devices/.../port/boundary +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Indicates whether the port is a boundary port. + +What: /sys/bus/ub/devices/.../port/cna +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display the CNA of this port. + +What: /sys/bus/ub/devices/.../port/glb_qdlws +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Write 1 to enable global dynamic lane adjustment, + write 0 to disable this function. + Supported only on physical port. + +What: /sys/bus/ub/devices/.../port/linkup +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Display whether the port has established a connection. + +What: /sys/bus/ub/devices/.../port/neighbor +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Displays the entity number of the peer entity at the port. + If no link is established, it displays "No Neighbor". + +What: /sys/bus/ub/devices/.../port/neighbor_guid +Date: Oct 2025 +Contact: Junlong Zheng +Description: + If there is a peer entity, display the GUID of the peer entity. + Otherwise, display "No Neighbor". + +What: /sys/bus/ub/devices/.../port/neighbor_port_idx +Date: Oct 2025 +Contact: Junlong Zheng +Description: + If there is a peer entity, the port index of the peer entity is + displayed. Otherwise, display "No Neighbor". + +What: /sys/bus/ub/devices/.../port/port_reset +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Supports individual port reset, triggered by writing 1. + Supported only on physical port. + +What: /sys/bus/ub/devices/.../port/qdlws_exec_state +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Obtain the hardware execution status of the current dynamically + adjustable lane. + Supported only on physical port. + +What: /sys/bus/ub/devices/.../port/rx_qdlws +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Enable/Disable dynamic lane adjustment in the RX direction. + Supported only on physical port. + +What: /sys/bus/ub/devices/.../port/tx_qdlws +Date: Oct 2025 +Contact: Junlong Zheng +Description: + Enable/Disable dynamic lane adjustment in the TX direction. + Supported only on physical port. + +What: /sys/bus/ub/devices/.../slot/power +Date: Oct 2025 +Contact: Junlong Zheng +Description: + This feature supports hot-plug notification. + Display the current slot status, the value can be "on", + "poweron", "poweroff", "off" or "unknown state". And can + write 1 to enable power on the slot, and write 0 to + power it off. + This file is supported only by entities that support + hot-plug features. diff --git a/Documentation/driver-api/ub/index.rst b/Documentation/driver-api/ub/index.rst new file mode 100644 index 000000000000..d3a5969e6e94 --- /dev/null +++ b/Documentation/driver-api/ub/index.rst @@ -0,0 +1,18 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +:Copyright: |copy| 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + +============================================= +The Linux UnifiedBus implementer's API guide +============================================= + +.. class:: toc-title + + Table of contents + +.. toctree:: + :maxdepth: 2 + + ubfi + ubus + ubase \ No newline at end of file diff --git a/Documentation/driver-api/ub/ubfi.rst b/Documentation/driver-api/ub/ubfi.rst new file mode 100644 index 000000000000..a7b0466bdca4 --- /dev/null +++ b/Documentation/driver-api/ub/ubfi.rst @@ -0,0 +1,7 @@ +.. SPDX-License-Identifier: GPL-2.0 + +UBFI Driver Support Library +--------------------------- + +.. kernel-doc:: include/ub/ubfi/ubfi.h + :functions: diff --git a/Documentation/driver-api/ub/ubus.rst b/Documentation/driver-api/ub/ubus.rst new file mode 100644 index 000000000000..4e39b79e9e5a --- /dev/null +++ b/Documentation/driver-api/ub/ubus.rst @@ -0,0 +1,7 @@ +.. SPDX-License-Identifier: GPL-2.0 + +UBUS Driver Support Library +----------------------------- + +.. kernel-doc:: include/ub/ubus/ubus.h + :functions: diff --git a/Documentation/ub/index.rst b/Documentation/ub/index.rst new file mode 100644 index 000000000000..8e939a2ba8fe --- /dev/null +++ b/Documentation/ub/index.rst @@ -0,0 +1,15 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. include:: + +:Copyright: |copy| 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + +===================== +UnifiedBus Subsystem +===================== + +.. toctree:: + :maxdepth: 2 + + ubase/index + ubfi/index + ubus/index diff --git a/Documentation/ub/ubfi/index.rst b/Documentation/ub/ubfi/index.rst new file mode 100644 index 000000000000..2dd11600f4f7 --- /dev/null +++ b/Documentation/ub/ubfi/index.rst @@ -0,0 +1,11 @@ +.. SPDX-License-Identifier: GPL-2.0 + +======================= +UB Firmware Spec Driver +======================= + +.. toctree:: + :maxdepth: 2 + :numbered: + + ubfi diff --git a/Documentation/ub/ubfi/ubfi.rst b/Documentation/ub/ubfi/ubfi.rst new file mode 100644 index 000000000000..efea335726b8 --- /dev/null +++ b/Documentation/ub/ubfi/ubfi.rst @@ -0,0 +1,178 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=========== +UBFI Driver +=========== + +What is UBFI +============ + +When BIOS boots the OS with UB firmware, it should report the UB-related +information in the system so that the OS can obtain the UB-related information, +including UBC, UMMU, and all other information required for UB enabling. + +Startup information is related to chip specifications and is static information +that can be reported through a static information table. There are three +reporting methods: UBIOS, ACPI, and Device Tree. The only difference among these +three methods lies in the entry points for obtaining the UB-related information +tables. The contents of each information table remain consistent. + +UnifiedBus Firmware Interface (UBFI) driver supports obtaining UB-related +information from the BIOS via the ACPI table or device tree. And create software +instances of UBCs and UMMUs in the OS. + +UBFI driver is one of the fundamental drivers of UB. It has achieved the +aforementioned functions. + +.. code-block:: none + + +--------------------------------------------------------------+ + | BIOS | + +--------------------------------------------------------------+ + ^ ^ + |acpi of| + v v + +--------------------------------------------------------------+ + | kernel | + +--------------------------------------------------------------+ + ^ + | + v + +--------------------------------------------------------------+ + | ubfi | + +--------------------------------------------------------------+ + ^ ^ + | | + v v + +-----------------+ +-----------------+ + | ubus | | ummu | + +-----------------+ +-----------------+ + +What does UBFI do +================= + +When loading the ubfi driver, it detects the current OS boot mode and retrieves +the UBRT (UB root table) physical address from the BIOS. + + - ACPI (UBRT table) + - device tree (node: chosen: ubios-information-table) + +For the structure of UBRT, please refer to https://www.unifiedbus.com/ + +Create UBC +---------- + +BIOS may report information about multiple UBCs, some of which is shared among +multiple UBCs and is reported in ``struct ubrt_ubc_table`` + +.. kernel-doc:: drivers/ub/ubfi/ubc.h + :functions: ubrt_ubc_table + +As ``ubc_cna_start``, ``ubc_cna_end``, ``ubc_eid_start``, ``ubc_eid_end``, +``ubc_feature``, ``cluster_mode``, these attributes belong to the entire UBPU +node and are shared by all UBCs. + +For a single UBC, its information is reported in the ``struct ubc_node`` + +.. kernel-doc:: drivers/ub/ubfi/ubc.h + :functions: ubc_node + +We have performed the following work on a single UBC. + + - Create the UBC structure and record the UBC information + - Register the UBC irq with the kernel + - Initialize UBC and register the UBC device with the kernel + - Register the MMIO address space of UBC with the kernel + - Set the MSI domain for all UBCs + +After completing these steps, ``struct list_head ubc_list`` will be provided +externally, which records all UBCs within the node for subsequent +interconnection and communication purposes. + +Set MSI domain for UBC +~~~~~~~~~~~~~~~~~~~~~~ + +UBFI driver requests interrupts from the interrupt management subsystem on +behalf of the entity and delivers the interrupt configuration to the entity. +When reporting an interrupt, the entity writes the interrupt information into +the interrupt controller, which then calls back the interrupt management +subsystem. The interrupt management subsystem subsequently invokes the UB driver +to handle the corresponding interrupt. + +UB created a new Message Signaled Interrupt domain called USI (UB Signaled +Interrupt). + +UB will add a platform device in the DSDT and IORT tables to associate UBC +with the USI domain. If booting with device tree, we will add a new UBC node in +DTS for binding the USI domain. For each UBC, a corresponding number of platform +devices should be created. We will set the USI domain of these platform devices +to the USI domain of each UBC. + +Example in DTS for UBC:: + + ubc@N { + compatible = "ub,ubc"; + #interrupt-cells = <0x3>; + interrupt-parent = <0x01>; + interrupts = <0x0 0xa 0x4>; + index = <0x00>; + msi-parent = <0x1 0xabcd>; + }; + +Parse UMMU and PMU +------------------ + +Both UMMU and UMMU-PMU devices are platform devices and support creation via +ACPI and DTS. + +ACPI method: + - The device information for UMMU and UMMU-PMU has been added to DSDT and + IORT tables. + - When the OS enables ACPI functionality, the ACPI system will recognize + the device information in the DSDT and IORT tables and automatically + create platform devices for UMMU and UMMU-PMU. + - The number of platform devices for UMMU and UMMU-PMU depends on the + number of device information nodes described in the DSDT and IORT tables. + +DTS method: + - The DTB file has added device tree nodes for UMMU and UMMU-PMU. + - When the OS enables the device tree functionality, the DTS system will + recognize the device tree nodes for UMMU and UMMU-PMU, and then + automatically create platform devices for them. + - The number of platform devices for UMMU and UMMU-PMU depends on the + number of corresponding device tree nodes described in the device tree. + + Example in DTS for UMMU and UMMU-PMU:: + + ummu@N { + compatible = "ub,ummu"; + index = <0x0>; + msi-parent = <&its>; + }; + + ummu-pmu@N { + compatible = "ub,ummu_pmu"; + index = <0x0>; + msi-parent = <&its>; + }; + +Obtain UMMU nodes from the UBRT table: + - The UBRT table can be parsed to extract the UMMU sub-table, which contains + several UMMU nodes. Each UMMU node describes the hardware information of an + UMMU device and its corresponding UMMU-PMU device. The specific content of + UMMU nodes can be found in ``struct ummu_node``. + + - The number of UMMU platform devices created via ACPI or DTS should match the + number of UMMU nodes in the UBRT table, as they have a one-to-one + correspondence. The same one-to-one correspondence applies to UMMU-PMU + devices and UMMU nodes. + +Configure UMMU and PMU devices: + - For each UMMU node parsed from the UBRT table, the register information and + NUMA affinity described in the UMMU node can be configured for the + corresponding UMMU and UMMU-PMU devices. + - Each UMMU node's content is stored in the ``ubrt_fwnode_list`` linked list. + Subsequently, the corresponding UMMU node can be found by using the fwnode + property of the UMMU and UMMU-PMU devices, making it convenient to obtain the + hardware information during the initialization of the UMMU and UMMU-PMU + drivers. \ No newline at end of file diff --git a/Documentation/ub/ubus/hisi_ubus.rst b/Documentation/ub/ubus/hisi_ubus.rst new file mode 100644 index 000000000000..b384b058129f --- /dev/null +++ b/Documentation/ub/ubus/hisi_ubus.rst @@ -0,0 +1,95 @@ +.. SPDX-License-Identifier: GPL-2.0 + +===================== +Hisilicon UBUS Driver +===================== + +Hisilicon UBUS Driver (abbreviated as Hisi UBUS) is a UnifiedBus (UB) +specification management subsystem specifically implemented for Hisi chips. It +provides a subsystem operation interfaces implementation:: + + static const struct ub_manage_subsystem_ops hisi_ub_manage_subsystem_ops = { + .vendor = HISI_VENDOR_ID, + .controller_probe = ub_bus_controller_probe, + .controller_remove = ub_bus_controller_remove, + .ras_handler_probe = ub_ras_handler_probe, + .ras_handler_remove = ub_ras_handler_remove + }; + +including probe/remove methods for the UB bus controller and ub ras handler. +Each specification management subsystem has a unique vendor id to identify the +provider. This vendor id is set to the vendor field of +``ub_manage_subsystem_ops`` implementation. During UB bus controller probe, a +ub_bus_controller_ops will be set to the UB bus controller, message device and +debug file system will be initialized. During UB bus controller remove, ops +will be unset, message device will be removed and debug file system will be +uninitialized. + +During module init, hisi_ub_manage_subsystem_ops is registered to Ubus driver +via the ``register_ub_manage_subsystem_ops()`` method provided by Ubus driver:: + + int register_ub_manage_subsystem_ops(const struct ub_manage_subsystem_ops *ops) + +When module is being unloaded, Ubus driver's +``unregister_ub_manage_subsystem_ops()`` is called to unregister the subsystem +operation interfaces:: + + void unregister_ub_manage_subsystem_ops(const struct ub_manage_subsystem_ops *ops) + +Hisi UBUS Controller Driver +=========================== +Hisi UBUS provides a ub bus controller operation interfaces implementation:: + + static struct ub_bus_controller_ops hi_ubc_ops = { + .eu_table_init = hi_eu_table_init, + .eu_table_uninit = hi_eu_table_uninit, + .eu_cfg = hi_eu_cfg, + .mem_decoder_create = hi_mem_decoder_create, + .mem_decoder_remove = hi_mem_decoder_remove, + .register_ubmem_irq = hi_register_ubmem_irq, + .unregister_ubmem_irq = hi_unregister_ubmem_irq, + .register_decoder_base_addr = hi_register_decoder_base_addr, + .entity_enable = hi_send_entity_enable_msg, + }; + +including init/uninit method for EID/UPI table, create/remove method for UB +memory decoder, register/unregister method for UB memory decoder interrupts +and so on. + +UB Message Core Driver +====================== +Hisi UBUS implements a message device that provides a set of operations:: + + static struct message_ops hi_message_ops = { + .probe_dev = hi_message_probe_dev, + .remove_dev = hi_message_remove_dev, + .sync_request = hi_message_sync_request, + .response = hi_message_response, + .sync_enum = hi_message_sync_enum, + .vdm_rx_handler = hi_vdm_rx_msg_handler, + .send = hi_message_send, + }; + +including synchronous message sending, synchronous enumeration message +sending, response message sending, vendor-defined message reception handling +and so on. After device creation, ``message_device_register()`` method of Ubus +driver is called to register the device to the Ubus driver message framework:: + + int message_device_register(struct message_device *mdev) + +This framework provides a unified interface for message transmission and +reception externally. + +Hisi UBUS Local Ras Error Handler +================================= +Hisi UBUS provides a local RAS handling module to detect and process errors +reported on the UB bus. It offers error printing and registry dump, determines +whether recovery is needed based on error type and severity, and can reset +ports for port issues in cluster environment. + +UB Vendor-Defined Messages Manager +================================== +Hisi UBUS defines several vendor-defined messages, implements messages' +transmission and processing. These private messages are mainly used for +managing the registration, release, and state control of physical and +virtual devices. \ No newline at end of file diff --git a/Documentation/ub/ubus/index.rst b/Documentation/ub/ubus/index.rst new file mode 100644 index 000000000000..a4c2a58324cf --- /dev/null +++ b/Documentation/ub/ubus/index.rst @@ -0,0 +1,13 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============= +UB BUS Driver +============= + +.. toctree:: + :maxdepth: 2 + :numbered: + + ubus + ubus-service + hisi_ubus \ No newline at end of file diff --git a/Documentation/ub/ubus/ubus-service.rst b/Documentation/ub/ubus/ubus-service.rst new file mode 100644 index 000000000000..fd347fff959a --- /dev/null +++ b/Documentation/ub/ubus/ubus-service.rst @@ -0,0 +1,60 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=========================================== +UnifiedBus Bus Driver (UBUS Driver) Service +=========================================== + +The UnifiedBus (UB) specification describes RAS-related error handling and +notification-based hot-plug functionalities. The UBUS driver implements these +two types of functionalities as two independent services in software. This +article will separately introduce these two services. + +UB Device Driver Error Service +============================== +The UB specification defines three categories of protocol errors: A, B, and C. +Among these, A and B category protocol errors are directly handled by the +UB device driver, and thus will not be further discussed in this document. +C category protocol errors are reported to the UBUS Driver via the APEI +mechanism. The UBUS Driver provides a set of mechanisms for handling C category +protocol errors, which work in conjunction with the UB device driver to +complete the error handling process. + +The UBUS driver provides the ``struct ub_error_handlers`` structure, which +includes multiple callback functions related to error handling. The UB device +driver needs to implement these callback functions:: + + struct ub_error_handlers { + void (*ub_reset_prepare)(struct ub_entity *uent); + void (*ub_reset_done)(struct ub_entity *uent); + ub_ers_result_t (*ub_error_detected)(struct ub_entity *uent, ub_channel_state_t state); + ub_ers_result_t (*ub_resource_enabled)(struct ub_entity *uent); + }; + +For UB device driver: + + - ub_reset_prepare is called before ELR, serving to notify the device driver to + prepare for the work before ELR + - ub_reset_done is called after ELR, serving to notify the device driver that + ELR has completed and services can be resumed + - ub_error_detected is called when the UB bus driver detects an error, serving + to notify the UB device driver of the occurrence of an error + - ub_resource_enabled is called after the UB bus driver has completed error + handling, serving to notify the UB device driver that error handling has + completed + +Hot-Plug Service +================ +The UB specification defines the hot-plug functionality for devices, which +requires coordination between software and hardware. The UBUS driver implements +the hot removal and hot insertion of external devices on a per-slot basis. +For detailed procedures, please refer to the UB specification document. The main +functional points implemented by the UBUS driver include: + + - Button event handling, completing the processing of hot-plug and + hot-unplug button messages + - Indicator control, switching different knowledge points based on the + device status + - Power control, performing power on/off operations for slots based on the + device status + - Providing a user-space sysfs interface to simulate button effects + according to user commands diff --git a/Documentation/ub/ubus/ubus.rst b/Documentation/ub/ubus/ubus.rst new file mode 100644 index 000000000000..e7176e98732e --- /dev/null +++ b/Documentation/ub/ubus/ubus.rst @@ -0,0 +1,312 @@ +.. SPDX-License-Identifier: GPL-2.0 + +====================================== +How To Write Linux UB Device Drivers +====================================== + +UnifiedBus (abbreviated as UB) is an interconnection technology and +architecture designed for computing systems. It unifies the communication +between IO, memory access, and various processing units within the same +interconnection technology framework, enabling high-performance data transfer, +unified resource management, efficient collaboration, and effective programming +in computing systems. Resource management is one of its key features, +implemented through a combination of software and hardware. The UB Bus Driver +(referred to as the UBUS Driver) implements the software portion of this +feature. This document provides a brief overview of the components within the +UBUS Driver framework and how to develop UB device drivers within this driver +framework. See more on the UB spec . + +Composition of the UBUS Driver +============================== +The UBUS Driver consists of two parts. The first part is the common +implementation section, which will be developed according to the UB +specification requirements. The second part is the proprietary implementation by +each manufacturer, which is based on the specific circuit designs of each host +manufacturer. Each host manufacturer can provide differentiated functionalities +in this part of the code. + +If the UBUS subsystem is not configured (CONFIG_UB_UBUS is not set), most of +the UBUS functions described below are defined as inline functions either +completely empty or just returning an appropriate error codes to avoid +lots of ifdefs in the drivers. + +The figure below illustrates the internal composition and system boundaries of +the UBUS Driver. + +.. code-block:: none + + +----------------------------------------------------------+ + | ub device driver | + +----------------------------------------------------------+ + ^ + | + v + +----------------------------------------------------------+ + | ubus driver | + | | + | +--------------------------------------------------+ | + | | ubus driver vendor-specific | | + | +--------------------------------------------------+ | + | | + | +--------------------------------------------------+ | + | | ubus driver common | | + | | | | + | | +------+ +--------+ +------+ +-------+ +-----+ | | +---------+ + | | | enum | | config | | port | | route | | msg | | | <-> | GIC/ITS | + | | +------+ +--------+ +------+ +-------+ +-----+ | | +---------+ + | | +------------+ +--------+ +-----------+ | | + | | | controller | | entity | | interrupt | | | +------------+ + | | +------------+ +--------+ +-----------+ | | <-> | IOMMU/UMMU | + | | +---------+ +----------+ +------+ +----------+ | | +------------+ + | | | decoder | | resource | | pool | | instance | | | + | | +---------+ +----------+ +------+ +----------+ | | + | | +-----+ +------+ +-------+ +---------+ +-------+ | | + | | | ras | | link | | reset | | hotplug | | sysfs | | | + | | +-----+ +------+ +-------+ +---------+ +-------+ | | + | | +------+ +---------------+ | | + | | | ubfi | | bus framework | | | + | | +------+ +---------------+ | | + | +--------------------------------------------------+ | + +----------------------------------------------------------+ + ^ + | + v + +----------------------------------------------------------+ + | hardware/firmware | + +----------------------------------------------------------+ + +The following briefly describes the functions of each submodule within the +UBUS driver: + + - enum: implement network topology scanning and device enumeration + functionality + - config: enable access to the device configuration space + - port: manage device ports + - route: implement the configuration of the routing table + - msg: implement message assembly and transmission/reception processing + for management messages + - controller: initialization and de-initialization of the UB controller + - entity: enable device configuration, multi-entity management, and other + functionalities + - interrupt: implement USI interrupt functionality + - decoder: implement address decoding functionality for MMIO access to + device resource space + - resource: manage the MMIO address space allocated by the user host to + the device + - pool: implementation of pooled message processing + - instance: implement bus instance management + - ras: implement handling for RAS exceptions + - link: implement processing of link messages + - reset: implement the reset function + - hotplug: enable hot-plug functionality for the device + - sysfs: implement sysfs attribute files + - ubfi: implement parsing of the UBRT table + - bus framework: implementation of the Ubus Driver Framework + +Structure of UB device driver +============================= +In Linux, the ``ub_driver`` structure is used to describe a UB device driver. +The `struct ub_driver` is employed to represent a UB device driver, and +the structure definition is as follows. + +.. kernel-doc:: include/ub/ubus/ubus.h + :functions: ub_driver + +This structure includes a matchable device table (`id_table`), a probe function, +a remove function, a shutdown function, error handling, and other functionalities. +The following content will provide a reference for the implementation of these +features. + +Rules for Device and Driver Matching +------------------------------------ +The matching rules for UnifiedBus devices and drivers are relatively flexible, +allowing for any combination of the following five matching entries in the +`struct ub_device_id` within the device driver to achieve the target matching rule: + + - GUID's Vendor ID + - GUID's Device ID + - Configuration Space Module Vendor ID + - Configuration Space Module ID + - Configuration Space Class Code + +The ID table is an array of ``struct ub_device_id`` entries ending with an +all-zero entry. Definitions with static const are generally preferred. + +.. kernel-doc:: include/linux/mod_devicetable.h + :functions: ub_device_id + +Most drivers only need ``UB_ENTITY()`` or ``UB_ENTITY_MODULE`` or +``UB_ENTITY_CLASS()`` to set up a ub_device_id table. + +The following is an example:: + + static const struct ub_device_id sample_tbl[] = { + { 0xCC08, 0xA001, UB_ANY_ID, UB_ANY_ID, 0, 0 }, + { UB_ENTITY(0xCC08, 0xA001), 0, 0 }, + { UB_ENTITY_MODULE(0xCC08, 0xA001, 0xCC08, 0xA001), 0, 0 }, + { UB_ENTITY_CLASS(0x0200, 0xffff) }, + }; + +New UB IDs may be added to a device driver ub_ids table at runtime +as shown below:: + + echo "vendor device modulevendor moduleid class class_mask driver_data" > \ + /sys/bus/ub/drivers/sample/new_id + +All fields are passed in as hexadecimal values (no leading 0x). +The vendor and device fields are mandatory, the others are optional. Users +need pass only as many optional fields as necessary: + + - modulevendor and moduledevice fields default to UB_ANY_ID (FFFFFFFF) + - class and classmask fields default to 0 + - driver_data defaults to 0UL. + - override_only field defaults to 0. + +Note that driver_data must match the value used by any of the ub_device_id +entries defined in the driver. This makes the driver_data field mandatory +if all the ub_device_id entries have a non-zero driver_data value. + +Once added, the driver probe routine will be invoked for any unclaimed +UB devices listed in its (newly updated) ub_ids list. + +Register UB Device Driver +------------------------- +The UB device driver uses `ub_register_driver` to register the device driver. +During the registration process, the matching between the device and the +driver will be triggered, with the matching rules referenced in the previous +section. + +UB Device Driver Probe Process Reference +---------------------------------------- +- Call `ub_set_user_info` to configure the user host information into the entity + Each entity's configuration space has corresponding user register + information, such as user EID, token ID, etc. Before the device driver + starts using the device, it needs to configure the user host information + for the device. + +- Call `ub_entity_enable` to configure the access path between the host and the device + Before using the device, you need to enable the bidirectional channel + switch for accessing the device from the user host and vice versa. + This is achieved by configuring the device configuration space registers. + +- Set the DMA mask size + The device driver can reconfigure this field segment based on the + device's DMA addressing capability. The default configuration is 32-bit. + +- Call the kernel DMA interface to request DMA memory + The device driver requests DMA memory through the DMA interface provided + by the kernel to prepare for subsequent device DMA operations. + +- Call `ub_iomap` to complete the MMIO access mapping for the resource space + The device resource space stores private configurations related to device + driver capabilities. Before accessing the device resource space, you need + to call the ioremap interface to complete address mapping. The ub_iomap + interface uses the device attribute, while the ub_iomap_wc interface + uses the writecombine attribute. + +- Call `ub_alloc_irq_vectors` or `ub_alloc_irq_vectors_affinity` to complete + the interrupt request, and then call the kernel's interrupt registration API. + +- Initiate specific business functions + +UB Device Driver Removal Process Reference +------------------------------------------ +- Stop specific business functions +- Invoke the kernel's interrupt unregistration API, call ub_disable_intr, to + complete the unregistration of the interrupt handler and release the interrupt +- Call ub_iounmap to demap the MMIO access space +- Invoke the kernel's DMA interface to release DMA memory +- Call ub_entity_enable to close the access path between the host and the device +- Call ub_unset_user_info to clear the user host information configured to the + entity + +UB Device Driver Shutdown +------------------------- +The UB device shutdown is triggered during the system shutdown or restart +process, and the UB device driver needs to stop the service flow in the shutdown +interface. + +UB Device Driver Virtual configure +---------------------------------- + +If the MUE supports multiple UEs, the device driver needs to provide +`virt_configure` callback. the UEs can be enabled or disabled to facilitate +direct connection to virtual machines for use. The bus driver will cyclically +call the virt_configure callback of the device driver to enable and disable +each UE in sequence. Within the virt_configure function of the device driver, +it needs to call `ub_enable_ue` and `ub_disable_ue` provided by the bus driver +to create and destroy UEs, at the same time, private processing logic can +also be executed. + +UE can be enabled and disabled through sysfs. The process is as follows:: + + 1. Check the number of UEs currently supported by the MUE + # cat /sys/bus/ub/devices/.../ub_totalues + 2. Specify the number of enabled UEs within the maximum UE quantity range + # echo 3 > /sys/bus/ub/devices/.../ub_numues + 3. Disable UEs + # echo 0 > /sys/bus/ub/devices/.../ub_numues + +UB Device Driver Virtual notify +------------------------------- + +If the device supports multiple UEs and the MUE device driver wants to be +aware of UE state changes, `virt_notify` hook function can be implemented to +capture the UE state. + +UB Device Driver Activate and Deactivate +---------------------------------------- + +The bus driver supports maintaining the working status of entities, indicating +whether an entity is in operation. It also provides corresponding interfaces +for controlling devices to enter or exit the working state, such as +`ub_activate_entity` and `ub_deactivate_entity`. If the device driver needs +to perform any special procedures, it must implement the corresponding activate +and deactivate hook functions. + +UB Device Driver RAS handler +---------------------------- + +The bus driver provides a set of hooks for RAS processing, creating an +opportunity window to notify device drivers when handling events such as +resets and RAS, allowing them to execute corresponding processing measures. +Currently implemented hooks include `reset_prepare`, `reset_done`, +`error_detected`, and `resource_enabled`. Device drivers can optionally +provide corresponding implementations to execute their own private processing. + +Uninstall UB Device Driver +-------------------------- +The UB device driver uses `ub_unregister_driver` to unregister the driver. This +interface call will perform a remove operation on all devices matched by the +driver, ultimately removing the UB device driver from the system. + +How to find UB devices manually +=============================== + +UBUS provides several interfaces to obtain ub_entities. You can search for them +using keywords such as GUID, EID, or entity number. Or you can find an entire +class of devices using vendor ID and device ID. + +How to access UB Configuration space +==================================== + +You can use `ub_config_(read|write)_(byte|word|dword)` to access the config +space of an entity represented by `struct ub_entity *`. All these functions return +0 when successful or an error code. Most drivers expect that accesses to valid UB +entities don't fail. + +The macros for configuration space registers are defined in the header file +include/uapi/ub/ubus/ubus_regs.h. + +Vendor and device identifications +================================= + +Do not add new device or vendor IDs to include/ub/ubus/ubus_ids.h unless they +are shared across multiple drivers. You can add private definitions in +your driver if they're helpful, or just use plain hex constants. + +The device IDs are arbitrary hex numbers (vendor controlled) and normally used +only in a single location, the ub_device_id table. + +Please DO submit new vendor/device IDs to . +There's a mirror of the ub.ids file at https://gitee.com/openeuler/ubutils/ub.ids. -- Gitee From dd2683848665498c8f420f060e220f421f620441 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Thu, 27 Nov 2025 09:28:01 +0800 Subject: [PATCH 101/214] ub:ubus: Add ubus devicetree file ANBZ: #45460 commit 575bfc910e2219b710f60f49ea1fc22543e85b46 openEuler. Define the attributes and attribute values of device nodes such as UB Controller to ensure that the kernel can correctly parse and use these attributes. Signed-off-by: Yahui Liu Signed-off-by: Yuhao Xiang Signed-off-by: Junlong Zheng Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- .../devicetree/bindings/ub/hisi,ubc.yaml | 35 ++++++++++++ .../devicetree/bindings/ub/ub,ubc.yaml | 55 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 Documentation/devicetree/bindings/ub/hisi,ubc.yaml create mode 100644 Documentation/devicetree/bindings/ub/ub,ubc.yaml diff --git a/Documentation/devicetree/bindings/ub/hisi,ubc.yaml b/Documentation/devicetree/bindings/ub/hisi,ubc.yaml new file mode 100644 index 000000000000..2219dd7902c1 --- /dev/null +++ b/Documentation/devicetree/bindings/ub/hisi,ubc.yaml @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ub/hisi,ubc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: HiSilicon UBC (Unified Bus Controller) platform device + +maintainers: + - Yuhao Xiang + +description: |+ + This platform device was added to enable the automatic loading of the + hisi_ubus driver. If this feature is not needed, you can omit adding this + device and manually load the driver instead. + +properties: + $nodename: + pattern: "^hisi-ubc$" + description: | + The node name should be "hisi-ubc". + + compatible: + const: "hisi,ubc" + description: | + The compatible property should be "hisi,ubc" to identify the device as a + HiSilicon UBC platform device. + +unevaluatedProperties: false + +examples: + - |+ + hisi-ubc { + compatible = "hisi,ubc"; + }; diff --git a/Documentation/devicetree/bindings/ub/ub,ubc.yaml b/Documentation/devicetree/bindings/ub/ub,ubc.yaml new file mode 100644 index 000000000000..012a293cf9e2 --- /dev/null +++ b/Documentation/devicetree/bindings/ub/ub,ubc.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ub/ub,ubc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: UBC (Unified Bus Controller) platform device + +maintainers: + - Yuhao Xiang + +description: | + The UBC platform device reported UBC interrupt number and the association + between the ubc and the interrupt controller. + +properties: + $nodename: + pattern: "^ubc@[0-9a-f]*" + + compatible: + const: "ub,ubc" + + interrupts: + maxItems: 1 + description: | + The interrupt specifier for the UBC. Used by the msgq of the ub controller. + + index: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + The index of the UBC. This is used to identify the specific UBC + in a system with multiple UBC devices. Starts from 0. + + msi-parent: + description: The msi interrupt for the UBC. Used by the ub entity connected + to UBC. + +required: + - compatible + - interrupts + - index + - msi-parent + +unevaluatedProperties: true + +examples: + - |+ + ubc@0 { + compatible = "ub,ubc"; + #interrupt-cells = <0x3>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xcb 0x4>; + index = <0x00>; + msi-parent = <&its 0x54c0>; + }; -- Gitee From f50cc7f0082da63e1f92f08b601571f4dc843032 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Thu, 20 Nov 2025 11:25:40 +0800 Subject: [PATCH 102/214] ub:hisi-ubus: Adding compatibility Interfaces for ub memory ANBZ: #45460 commit 56bce93e4090d11862d320e1025418bad27d1f45 openEuler. Adding southbound and northbound compatibility Interfaces for ub memory Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/memory.c | 4 +- drivers/ub/ubus/memory.h | 4 +- drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h | 4 + drivers/ub/ubus/vendor/hisilicon/memory.c | 90 +++++++++++++++----- 4 files changed, 79 insertions(+), 23 deletions(-) diff --git a/drivers/ub/ubus/memory.c b/drivers/ub/ubus/memory.c index e7b3144db4bd..e7fcdf76f28e 100644 --- a/drivers/ub/ubus/memory.c +++ b/drivers/ub/ubus/memory.c @@ -114,7 +114,7 @@ void ub_mem_drain_start(u32 scna) } if (mem_device->ops && mem_device->ops->mem_drain_start) - mem_device->ops->mem_drain_start(mem_device); + mem_device->ops->mem_drain_start(ubc); else dev_warn(mem_device->dev, "ub mem_device ops mem_drain_start is null.\n"); } @@ -138,7 +138,7 @@ int ub_mem_drain_state(u32 scna) } if (mem_device->ops && mem_device->ops->mem_drain_state) - return mem_device->ops->mem_drain_state(mem_device); + return mem_device->ops->mem_drain_state(ubc); dev_warn(mem_device->dev, "ub memory decoder ops mem_drain_state is null.\n"); return 0; diff --git a/drivers/ub/ubus/memory.h b/drivers/ub/ubus/memory.h index af32951937ba..70cf2b53958f 100644 --- a/drivers/ub/ubus/memory.h +++ b/drivers/ub/ubus/memory.h @@ -29,8 +29,8 @@ struct ub_mem_ras_ctx { }; struct ub_mem_device_ops { - void (*mem_drain_start)(struct ub_mem_device *mem_device); - int (*mem_drain_state)(struct ub_mem_device *mem_device); + void (*mem_drain_start)(struct ub_bus_controller *ubc); + int (*mem_drain_state)(struct ub_bus_controller *ubc); bool (*mem_validate_pa)(struct ub_bus_controller *ubc, u64 pa_start, u64 pa_end, bool cacheable); diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h index 9aa3ba5521c1..092695e9d43c 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h @@ -12,6 +12,10 @@ #define MB_SIZE_OFFSET 20 #define HI_UBC_PRIVATE_DATA_RESERVED 3 #define HI_UBC_PRIVATE_DATA_RESERVED2 111 +#define UB_MEM_VERSION_INVALID 0xffffffff +#define UB_MEM_VERSION_0 0 +#define UB_MEM_VERSION_1 1 +#define UB_MEM_VERSION_2 2 struct hi_mem_pa_info { u64 decode_addr; diff --git a/drivers/ub/ubus/vendor/hisilicon/memory.c b/drivers/ub/ubus/vendor/hisilicon/memory.c index 4d4f80f847fc..fa9747171eea 100644 --- a/drivers/ub/ubus/vendor/hisilicon/memory.c +++ b/drivers/ub/ubus/vendor/hisilicon/memory.c @@ -24,9 +24,13 @@ #define MEM_EVENT_MAX_NUM 16 #define MAR_ERR_ADDR_COUNT 10 #define MAR_ERR_ADDR_SIZE 2 +#define MEM_DECODER_NUMBER_V1 5 +#define MEM_DECODER_NUMBER_V2 2 #define hpa_gen(addr_h, addr_l) (((u64)(addr_h) << 32) | (addr_l)) +static u8 ub_mem_num; + struct ub_mem_decoder { struct device *dev; struct ub_entity *uent; @@ -58,25 +62,26 @@ struct hi_get_ubmem_event_pld { static bool hi_mem_validate_pa(struct ub_bus_controller *ubc, u64 pa_start, u64 pa_end, bool cacheable); -static void hi_mem_drain_start(struct ub_mem_device *mem_device) +static void hi_mem_drain_start(struct ub_bus_controller *ubc) { - struct ub_mem_decoder *decoder, *data = mem_device->priv_data; + struct ub_mem_decoder *decoder, *data = ubc->mem_device->priv_data; if (!data) { - dev_err(mem_device->dev, "ubc mem_decoder is null.\n"); + dev_err(&ubc->dev, "ubc mem_decoder is null.\n"); return; } - for (int i = 0; i < MEM_INFO_NUM; i++) { + for (int i = 0; i < ub_mem_num; i++) { decoder = &data[i]; writel(0, decoder->base_reg + DRAIN_ENABLE_REG_OFFSET); writel(1, decoder->base_reg + DRAIN_ENABLE_REG_OFFSET); } } -static int hi_mem_drain_state(struct ub_mem_device *mem_device) +static int hi_mem_drain_state(struct ub_bus_controller *ubc) { - struct ub_mem_decoder *decoder, *data = mem_device->priv_data; + struct ub_mem_decoder *decoder, *data = ubc->mem_device->priv_data; + struct ub_mem_device *mem_device = ubc->mem_device; int val = 0; if (!data) { @@ -84,7 +89,7 @@ static int hi_mem_drain_state(struct ub_mem_device *mem_device) return 0; } - for (int i = 0; i < MEM_INFO_NUM; i++) { + for (int i = 0; i < ub_mem_num; i++) { decoder = &data[i]; val = readb(decoder->base_reg + DRAIN_STATE_REG_OFFSET) & 0x1; dev_info_ratelimited(decoder->dev, "ub memory decoder[%d] drain state, val=%d\n", @@ -246,16 +251,25 @@ static irqreturn_t hi_mem_ras_irq(int irq, void *context) return IRQ_WAKE_THREAD; } -static int hi_mem_decoder_create_one(struct ub_bus_controller *ubc, int mar_id) +static bool is_ub_mem_version_valid(struct ub_bus_controller *ubc) +{ + struct hi_ubc_private_data *data = ubc->data; + + if (!data || data->ub_mem_version == UB_MEM_VERSION_INVALID) + return false; + return true; +} + +static int hi_mem_decoder_create_one(struct ub_bus_controller *ubc, int index) { - struct hi_ubc_private_data *data = (struct hi_ubc_private_data *)ubc->data; struct ub_mem_decoder *decoder, *priv_data = ubc->mem_device->priv_data; + struct hi_ubc_private_data *data = ubc->data; - decoder = &priv_data[mar_id]; + decoder = &priv_data[index]; decoder->dev = &ubc->dev; decoder->uent = ubc->uent; - decoder->base_reg = ioremap(data->mem_pa_info[mar_id].decode_addr, + decoder->base_reg = ioremap(data->mem_pa_info[index].decode_addr, SZ_64); if (!decoder->base_reg) { dev_err(decoder->dev, "ub mem decoder base reg ioremap failed.\n"); @@ -265,24 +279,47 @@ static int hi_mem_decoder_create_one(struct ub_bus_controller *ubc, int mar_id) return 0; } -static void hi_mem_decoder_remove_one(struct ub_bus_controller *ubc, int mar_id) +static void hi_mem_decoder_remove_one(struct ub_bus_controller *ubc, int index) { struct ub_mem_decoder *priv_data = ubc->mem_device->priv_data; - iounmap(priv_data[mar_id].base_reg); + iounmap(priv_data[index].base_reg); +} + +static u8 get_mem_decoder_number(struct hi_ubc_private_data *data) +{ + switch (data->ub_mem_version) { + case UB_MEM_VERSION_0: + case UB_MEM_VERSION_1: + return MEM_DECODER_NUMBER_V1; + case UB_MEM_VERSION_2: + return MEM_DECODER_NUMBER_V2; + default: + return 0; + } } int hi_mem_decoder_create(struct ub_bus_controller *ubc) { struct ub_mem_device *mem_device; + struct hi_ubc_private_data *data; void *priv_data; int ret; + if (!is_ub_mem_version_valid(ubc)) { + dev_info(&ubc->dev, "Don't need to create mem decoder\n"); + return 0; + } + + ub_mem_num = get_mem_decoder_number(data); + if (!ub_mem_num) + return -EINVAL; + mem_device = kzalloc(sizeof(*mem_device), GFP_KERNEL); if (!mem_device) return -ENOMEM; - priv_data = kcalloc(MEM_INFO_NUM, sizeof(struct ub_mem_decoder), + priv_data = kcalloc(ub_mem_num, sizeof(struct ub_mem_decoder), GFP_KERNEL); if (!priv_data) { kfree(mem_device); @@ -296,7 +333,7 @@ int hi_mem_decoder_create(struct ub_bus_controller *ubc) mem_device->priv_data = priv_data; ubc->mem_device = mem_device; - for (int i = 0; i < MEM_INFO_NUM; i++) { + for (int i = 0; i < ub_mem_num; i++) { ret = hi_mem_decoder_create_one(ubc, i); if (ret) { dev_err(&ubc->dev, "hi mem create decoder %d failed\n", i); @@ -318,7 +355,12 @@ void hi_mem_decoder_remove(struct ub_bus_controller *ubc) if (!ubc->mem_device) return; - for (int i = 0; i < MEM_INFO_NUM; i++) + if (!is_ub_mem_version_valid(ubc)) { + dev_info(&ubc->dev, "Don't need to remove mem decoder\n"); + return; + } + + for (int i = 0; i < ub_mem_num; i++) hi_mem_decoder_remove_one(ubc, i); kfree(ubc->mem_device->priv_data); @@ -333,7 +375,12 @@ void hi_register_ubmem_irq(struct ub_bus_controller *ubc) u32 usi_idx; if (!ubc->mem_device) { - pr_err("mem device is NULL!\n"); + pr_err("register ubmem irq failed, mem device is NULL!\n"); + return; + } + + if (!is_ub_mem_version_valid(ubc)) { + dev_info(&ubc->dev, "Don't need to register_ubmem_irq\n"); return; } @@ -371,6 +418,11 @@ void hi_unregister_ubmem_irq(struct ub_bus_controller *ubc) return; } + if (!is_ub_mem_version_valid(ubc)) { + dev_info(&ubc->dev, "Don't need to unregister_ubmem_irq\n"); + return; + } + irq_num = ubc->mem_device->ubmem_irq_num; if (irq_num < 0) return; @@ -404,8 +456,8 @@ static bool hi_mem_validate_pa(struct ub_bus_controller *ubc, return false; } - data = (struct hi_ubc_private_data *)ubc->data; - for (u16 i = 0; i < MEM_INFO_NUM; i++) { + data = ubc->data; + for (u16 i = 0; i < ub_mem_num; i++) { if (ub_hpa_valid(pa_start, pa_end, data->mem_pa_info[i].cc_base_addr, data->mem_pa_info[i].cc_base_size) && -- Gitee From 41374d361e852594ebede51d94cb35841f1dc034 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Sat, 29 Nov 2025 10:43:16 +0800 Subject: [PATCH 103/214] ub: hisi-ubus: Fix ub memory decoder create ANBZ: #45460 commit 2f37e06abbc56da4affaa2620d07148052395b05 openEuler. Fix ub memory decoder create cannot get data Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/vendor/hisilicon/memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ub/ubus/vendor/hisilicon/memory.c b/drivers/ub/ubus/vendor/hisilicon/memory.c index fa9747171eea..e49a4a82322f 100644 --- a/drivers/ub/ubus/vendor/hisilicon/memory.c +++ b/drivers/ub/ubus/vendor/hisilicon/memory.c @@ -301,8 +301,8 @@ static u8 get_mem_decoder_number(struct hi_ubc_private_data *data) int hi_mem_decoder_create(struct ub_bus_controller *ubc) { + struct hi_ubc_private_data *data = ubc->data; struct ub_mem_device *mem_device; - struct hi_ubc_private_data *data; void *priv_data; int ret; -- Gitee From 57275c1c66ca9da9b2560def646fbe788350d4f1 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Wed, 26 Nov 2025 09:30:46 +0800 Subject: [PATCH 104/214] ub:hisi-ubus ub:hisi-ubus: Move the decoder's page table operations to hisi-ubus ANBZ: #45460 commit bd9e1c9e31192a1eabadca924fab339f91eec5ae openEuler. Move the decoder's page table operations to hisi-ubus Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubfi/irq.c | 4 +- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/decoder.c | 115 +++++++--------- drivers/ub/ubus/decoder.h | 43 +++--- drivers/ub/ubus/omm.h | 32 ----- drivers/ub/ubus/resource.c | 1 - drivers/ub/ubus/ubus_controller.h | 9 ++ drivers/ub/ubus/vendor/hisilicon/Makefile | 2 +- drivers/ub/ubus/vendor/hisilicon/controller.c | 14 +- .../hisilicon/hisi-decoder.c} | 128 +++++++++++++----- .../ub/ubus/vendor/hisilicon/hisi-decoder.h | 50 +++++++ drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h | 2 - 12 files changed, 232 insertions(+), 170 deletions(-) delete mode 100644 drivers/ub/ubus/omm.h rename drivers/ub/ubus/{omm.c => vendor/hisilicon/hisi-decoder.c} (86%) create mode 100644 drivers/ub/ubus/vendor/hisilicon/hisi-decoder.h diff --git a/drivers/ub/ubfi/irq.c b/drivers/ub/ubfi/irq.c index eb716af13088..5835bc8421b3 100644 --- a/drivers/ub/ubfi/irq.c +++ b/drivers/ub/ubfi/irq.c @@ -62,8 +62,10 @@ int ubrt_register_gsi(u32 hwirq, int trigger, int polarity, const char *name, res->start = irq; res->end = irq; res->flags = IORESOURCE_IRQ; -#endif return 0; +#else + return -EINVAL; +#endif } EXPORT_SYMBOL_GPL(ubrt_register_gsi); diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 59505977dd2f..96efd80d502f 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o ras.o obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o -ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o omm.o ioctl.o eu.o link.o +ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o ioctl.o eu.o link.o ubus-y += instance.o pool.o memory.o ubus-y += services/ras.o services/service.o services/gucd.o diff --git a/drivers/ub/ubus/decoder.c b/drivers/ub/ubus/decoder.c index f5a8e69fd1cf..288e33a96038 100644 --- a/drivers/ub/ubus/decoder.c +++ b/drivers/ub/ubus/decoder.c @@ -13,7 +13,6 @@ #include "ubus.h" #include "ubus_controller.h" -#include "omm.h" #include "decoder.h" #define MMIO_SIZE_MASK GENMASK_ULL(18, 16) @@ -33,8 +32,6 @@ #define EVTQ_ENABLE 0x1 #define EVT_ENTRY_SIZE 16 -#define DECODER_PAGE_TABLE_ENTRY_SIZE 8 - #define DECODER_QUEUE_TIMEOUT_US 1000000 /* 1s */ static void ub_decoder_uninit_queue(struct ub_decoder *decoder) @@ -170,68 +167,24 @@ static u32 ub_decoder_device_set(struct ub_decoder *decoder) return ret; } -static int ub_decoder_create_page_table(struct ub_decoder *decoder) +static int ub_decoder_create_page_table(struct ub_bus_controller *ubc, + struct ub_decoder *decoder) { - struct page_table_desc *invalid_desc = &decoder->invalid_desc; - struct ub_entity *uent = decoder->uent; - struct page_table *pgtlb; - void *pgtlb_base; - size_t size; - - size = DECODER_PAGE_TABLE_ENTRY_SIZE * DECODER_PAGE_TABLE_SIZE; - pgtlb = &decoder->pgtlb; - pgtlb_base = dmam_alloc_coherent(decoder->dev, size, - &pgtlb->pgtlb_dma, GFP_KERNEL); - if (!pgtlb_base) { - ub_err(uent, "allocate ub decoder page table fail\n"); - return -ENOMEM; - } - pgtlb->pgtlb_base = pgtlb_base; - - size = sizeof(*pgtlb->desc_base) * DECODER_PAGE_TABLE_SIZE; - pgtlb->desc_base = kzalloc(size, GFP_KERNEL); - if (!pgtlb->desc_base) { - ub_err(uent, "allocate ub decoder page table desc fail\n"); - goto release_pgtlb; - } - - invalid_desc->page_base = dmam_alloc_coherent(decoder->dev, - RANGE_TABLE_PAGE_SIZE, - &invalid_desc->page_dma, - GFP_KERNEL); - if (!invalid_desc->page_base) { - ub_err(uent, "decoder alloc free page fail\n"); - goto release_desc; - } - decoder->invalid_page_dma = (invalid_desc->page_dma & - DECODER_PGTBL_PGPRT_MASK) >> - DECODER_DMA_PAGE_ADDR_OFFSET; - - ub_decoder_init_page_table(decoder, pgtlb_base); + if (ubc->ops->create_decoder_table) + return ubc->ops->create_decoder_table(decoder); - return 0; - -release_desc: - kfree(pgtlb->desc_base); - pgtlb->desc_base = NULL; -release_pgtlb: - size = DECODER_PAGE_TABLE_ENTRY_SIZE * DECODER_PAGE_TABLE_SIZE; - dmam_free_coherent(decoder->dev, size, pgtlb_base, pgtlb->pgtlb_dma); - return -ENOMEM; + ub_err(decoder->uent, "ub bus controller can't create decoder table\n"); + return -EPERM; } -static void ub_decoder_free_page_table(struct ub_decoder *decoder) +static void ub_decoder_free_page_table(struct ub_bus_controller *ubc, + struct ub_decoder *decoder) { - struct page_table_desc *invalid_desc = &decoder->invalid_desc; - size_t size; - - dmam_free_coherent(decoder->dev, RANGE_TABLE_PAGE_SIZE, - invalid_desc->page_base, invalid_desc->page_dma); - kfree(decoder->pgtlb.desc_base); - - size = DECODER_PAGE_TABLE_ENTRY_SIZE * DECODER_PAGE_TABLE_SIZE; - dmam_free_coherent(decoder->dev, size, decoder->pgtlb.pgtlb_base, - decoder->pgtlb.pgtlb_dma); + if (ubc->ops->free_decoder_table) + ubc->ops->free_decoder_table(decoder); + else + ub_err(decoder->uent, + "ub bus controller can't free decoder table\n"); } static void ub_get_decoder_mmio_base(struct ub_bus_controller *ubc, @@ -302,7 +255,7 @@ static int ub_create_decoder(struct ub_bus_controller *ubc) if (ret) goto release_decoder; - ret = ub_decoder_create_page_table(decoder); + ret = ub_decoder_create_page_table(ubc, decoder); if (ret) { ub_err(uent, "decoder create page table failed\n"); goto release_queue; @@ -321,7 +274,7 @@ static int ub_create_decoder(struct ub_bus_controller *ubc) return ret; release_page_table: - ub_decoder_free_page_table(decoder); + ub_decoder_free_page_table(ubc, decoder); release_queue: ub_decoder_uninit_queue(decoder); release_decoder: @@ -397,7 +350,7 @@ static void ub_remove_decoder(struct ub_bus_controller *ubc) ub_decoder_device_unset(decoder); - ub_decoder_free_page_table(decoder); + ub_decoder_free_page_table(ubc, decoder); ub_decoder_uninit_queue(decoder); @@ -635,6 +588,7 @@ int ub_decoder_cmd_request(struct ub_decoder *decoder, phys_addr_t addr, ret = wait_for_cmdq_notify(decoder); return ret; } +EXPORT_SYMBOL_GPL(ub_decoder_cmd_request); static bool queue_empty(struct ub_decoder_queue *q) { @@ -839,3 +793,38 @@ void ub_decoder_uninit(struct ub_entity *uent) ub_remove_decoder(uent->ubc); } + +int ub_decoder_unmap(struct ub_decoder *decoder, phys_addr_t addr, u64 size) +{ + struct ub_bus_controller *ubc; + + if (!decoder) { + pr_err("unmap mmio decoder ptr is null\n"); + return -EINVAL; + } + + ubc = decoder->uent->ubc; + if (!ubc->ops->decoder_unmap) { + pr_err("decoder_unmap ops not exist\n"); + return -EINVAL; + } + return ubc->ops->decoder_unmap(ubc->decoder, addr, size); +} + +int ub_decoder_map(struct ub_decoder *decoder, struct decoder_map_info *info) +{ + struct ub_bus_controller *ubc; + + if (!decoder || !info) { + pr_err("decoder or map info is null\n"); + return -EINVAL; + } + + ubc = decoder->uent->ubc; + if (!ubc->ops->decoder_map) { + pr_err("decoder_map ops not exist\n"); + return -EINVAL; + } + + return ubc->ops->decoder_map(ubc->decoder, info); +} diff --git a/drivers/ub/ubus/decoder.h b/drivers/ub/ubus/decoder.h index 47710ead71db..37d628dc45e2 100644 --- a/drivers/ub/ubus/decoder.h +++ b/drivers/ub/ubus/decoder.h @@ -87,33 +87,20 @@ struct ub_decoder { struct mutex table_lock; }; -#define DECODER_PGTBL_PGPRT_MASK GENMASK_ULL(47, 12) -#define DECODER_DMA_PAGE_ADDR_OFFSET 12 - -#define PGTLB_CACHE_IR_NC 0b00 -#define PGTLB_CACHE_IR_WBRA 0b01 -#define PGTLB_CACHE_IR_WT 0b10 -#define PGTLB_CACHE_IR_WB 0b11 -#define PGTLB_CACHE_OR_NC 0b0000 -#define PGTLB_CACHE_OR_WBRA 0b0100 -#define PGTLB_CACHE_OR_WT 0b1000 -#define PGTLB_CACHE_OR_WB 0b1100 -#define PGTLB_CACHE_SH_NSH 0b000000 -#define PGTLB_CACHE_SH_OSH 0b100000 -#define PGTLB_CACHE_SH_ISH 0b110000 - -#define PGTLB_ATTR_DEFAULT (PGTLB_CACHE_IR_WBRA | \ - PGTLB_CACHE_OR_WBRA | \ - PGTLB_CACHE_SH_ISH) - -#define RGTLB_TO_PGTLB 8 -#define DECODER_PAGE_ENTRY_SIZE 64 -#define DECODER_PAGE_SIZE (1 << 12) -#define DECODER_PAGE_TABLE_SIZE (1 << 12) -#define PAGE_TABLE_PAGE_SIZE (DECODER_PAGE_ENTRY_SIZE * DECODER_PAGE_SIZE) -#define RANGE_TABLE_PAGE_SIZE (DECODER_PAGE_ENTRY_SIZE * \ - DECODER_PAGE_SIZE * \ - RGTLB_TO_PGTLB) +struct decoder_map_info { + phys_addr_t pa; + phys_addr_t uba; + u64 size; + u32 tpg_num; + u8 order_id; + u8 order_type; + u64 eid_low; + u64 eid_high; + u32 token_id; + u32 token_value; + u32 upi; + u32 src_eid; +}; void ub_decoder_init(struct ub_entity *uent); void ub_decoder_uninit(struct ub_entity *uent); @@ -121,4 +108,6 @@ void ub_init_decoder_usi(struct ub_entity *uent); void ub_uninit_decoder_usi(struct ub_entity *uent); int ub_decoder_cmd_request(struct ub_decoder *decoder, phys_addr_t addr, u64 size, enum ub_cmd_op_type op); +int ub_decoder_map(struct ub_decoder *decoder, struct decoder_map_info *info); +int ub_decoder_unmap(struct ub_decoder *decoder, phys_addr_t addr, u64 size); #endif /* __DECODER_H__ */ diff --git a/drivers/ub/ubus/omm.h b/drivers/ub/ubus/omm.h deleted file mode 100644 index e90ce9cb1f7f..000000000000 --- a/drivers/ub/ubus/omm.h +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. - */ - -#ifndef __OMM_H__ -#define __OMM_H__ - -#include - -extern u8 ubc_feature; - -struct decoder_map_info { - phys_addr_t pa; - phys_addr_t uba; - u64 size; - u32 tpg_num; - u8 order_id; - u8 order_type; - u64 eid_low; - u64 eid_high; - u32 token_id; - u32 token_value; - u32 upi; - u32 src_eid; -}; - -void ub_decoder_init_page_table(struct ub_decoder *decoder, void *pgtlb_base); -int ub_decoder_unmap(struct ub_decoder *decoder, phys_addr_t addr, u64 size); -int ub_decoder_map(struct ub_decoder *decoder, struct decoder_map_info *info); - -#endif /* __OMM_H__ */ diff --git a/drivers/ub/ubus/resource.c b/drivers/ub/ubus/resource.c index d4516d672bb5..6e8ceeb9fa93 100644 --- a/drivers/ub/ubus/resource.c +++ b/drivers/ub/ubus/resource.c @@ -12,7 +12,6 @@ #include "ubus.h" #include "msg.h" #include "decoder.h" -#include "omm.h" #include "resource.h" struct query_token_msg_pld_req { diff --git a/drivers/ub/ubus/ubus_controller.h b/drivers/ub/ubus/ubus_controller.h index d30911399dcc..03005965f24f 100644 --- a/drivers/ub/ubus/ubus_controller.h +++ b/drivers/ub/ubus/ubus_controller.h @@ -6,6 +6,9 @@ #ifndef __UBUS_CONTROLLER_H__ #define __UBUS_CONTROLLER_H__ +#include +#include "decoder.h" + struct ub_bus_controller; struct ub_bus_controller_ops { int (*eu_table_init)(struct ub_bus_controller *ubc); @@ -18,6 +21,12 @@ struct ub_bus_controller_ops { void (*register_decoder_base_addr)(struct ub_bus_controller *ubc, u64 *cmd_queue, u64 *event_queue); int (*entity_enable)(struct ub_entity *uent, u8 enable); + int (*create_decoder_table)(struct ub_decoder *decoder); + void (*free_decoder_table)(struct ub_decoder *decoder); + int (*decoder_map)(struct ub_decoder *decoder, + struct decoder_map_info *info); + int (*decoder_unmap)(struct ub_decoder *decoder, phys_addr_t addr, + u64 size); CK_KABI_RESERVE(1) CK_KABI_RESERVE(2) diff --git a/drivers/ub/ubus/vendor/hisilicon/Makefile b/drivers/ub/ubus/vendor/hisilicon/Makefile index 998c0e09aeef..fec1dbe15796 100644 --- a/drivers/ub/ubus/vendor/hisilicon/Makefile +++ b/drivers/ub/ubus/vendor/hisilicon/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ hisi_ubus-objs := hisi-ubus.o controller.o vdm.o local-ras.o msg.o msg-core.o -hisi_ubus-objs += msg-debugfs.o eu-table.o memory.o +hisi_ubus-objs += msg-debugfs.o eu-table.o memory.o hisi-decoder.o obj-$(CONFIG_UB_HISI_UBUS) += hisi_ubus.o diff --git a/drivers/ub/ubus/vendor/hisilicon/controller.c b/drivers/ub/ubus/vendor/hisilicon/controller.c index d7ea5c118d32..6c9c8e320479 100644 --- a/drivers/ub/ubus/vendor/hisilicon/controller.c +++ b/drivers/ub/ubus/vendor/hisilicon/controller.c @@ -10,6 +10,7 @@ #include #include "../../ubus_controller.h" +#include "hisi-decoder.h" #include "hisi-ubus.h" #include "hisi-msg.h" @@ -23,17 +24,12 @@ static struct ub_bus_controller_ops hi_ubc_ops = { .unregister_ubmem_irq = hi_unregister_ubmem_irq, .register_decoder_base_addr = hi_register_decoder_base_addr, .entity_enable = hi_send_entity_enable_msg, + .create_decoder_table = hi_create_decoder_table, + .free_decoder_table = hi_free_decoder_table, + .decoder_map = hi_decoder_map, + .decoder_unmap = hi_decoder_unmap, }; -void hi_register_decoder_base_addr(struct ub_bus_controller *ubc, u64 *cmd_queue, - u64 *event_queue) -{ - struct hi_ubc_private_data *data = (struct hi_ubc_private_data *)ubc->data; - - *cmd_queue = data->io_decoder_cmdq; - *event_queue = data->io_decoder_evtq; -} - static void ub_bus_controller_debugfs_init(struct ub_bus_controller *ubc) { if (!debugfs_initialized()) diff --git a/drivers/ub/ubus/omm.c b/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.c similarity index 86% rename from drivers/ub/ubus/omm.c rename to drivers/ub/ubus/vendor/hisilicon/hisi-decoder.c index b8b59a9da4f1..ac1fa0498ffc 100644 --- a/drivers/ub/ubus/omm.c +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.c @@ -3,11 +3,13 @@ * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. */ -#define pr_fmt(fmt) "ubus omm: " fmt +#define pr_fmt(fmt) "ubus hisi decoder: " fmt #include -#include "decoder.h" -#include "omm.h" +#include +#include "../../ubus.h" +#include "hisi-ubus.h" +#include "hisi-decoder.h" enum entry_type { INVALID_ENTRY = 0x0, @@ -95,6 +97,7 @@ struct range_table_entry { u64 reserve11 : 12; }; +#define DECODER_PAGE_TABLE_ENTRY_SIZE 8 #define UBA_ADDR_OFFSET 12 #define DECODER_PAGE_INDEX_LOC 20 @@ -107,6 +110,11 @@ struct range_table_entry { #define DECODER_RGTLB_ADDRESS_MASK GENMASK_ULL(34, 20) #define DECODER_RGTLB_ADDRESS_OFFSET 20 #define TOKEN_VALID_MASK GENMASK(0, 0) +#define MEM_LMT_MAX 0x7FFF +#define RANGE_UBA_LOW_MASK GENMASK_ULL(34, 20) +#define RANGE_UBA_HIGH_MASK GENMASK_ULL(63, 35) +#define UBA_CARRY 0x800000000 +#define UBA_NOCARRY 0x0 #define get_pgtlb_idx(decoder, pa) ((((pa) - (decoder)->mmio_base_addr) & \ DECODER_PAGE_TABLE_MASK) >> \ @@ -485,12 +493,6 @@ static int handle_page_table(struct ub_decoder *decoder, u64 *offset, return ret; } -#define MEM_LMT_MAX 0x7FFF -#define RANGE_UBA_LOW_MASK GENMASK_ULL(34, 20) -#define RANGE_UBA_HIGH_MASK GENMASK_ULL(63, 35) -#define UBA_CARRY 0x800000000 -#define UBA_NOCARRY 0x0 - static void fill_range_table(struct ub_decoder *decoder, struct range_table_entry *rg_entry, struct decoder_map_info *info, u64 *offset) @@ -593,7 +595,7 @@ static int handle_table(struct ub_decoder *decoder, ret); /* if it is map operation, revert it. unmap operation can't revert */ if (is_map) - (void)ub_decoder_unmap(decoder, info->pa, + (void)hi_decoder_unmap(decoder, info->pa, rollback_size); break; } @@ -601,7 +603,88 @@ static int handle_table(struct ub_decoder *decoder, return ret; } -int ub_decoder_unmap(struct ub_decoder *decoder, phys_addr_t addr, u64 size) +static void ub_decoder_init_page_table(struct ub_decoder *decoder, void *pgtlb_base) +{ + struct page_table_entry *pgtlb_entry; + int i; + + for (i = 0; i < DECODER_PAGE_TABLE_SIZE; i++) { + pgtlb_entry = (struct page_table_entry *)pgtlb_base + i; + pgtlb_entry->entry_type = PAGE_TABLE; + pgtlb_entry->next_lv_addr = decoder->invalid_page_dma; + pgtlb_entry->pgtlb_attr = PGTLB_ATTR_DEFAULT; + } +} + +void hi_register_decoder_base_addr(struct ub_bus_controller *ubc, + u64 *cmd_queue, u64 *event_queue) +{ + struct hi_ubc_private_data *data = (struct hi_ubc_private_data *)ubc->data; + + *cmd_queue = data->io_decoder_cmdq; + *event_queue = data->io_decoder_evtq; +} + +int hi_create_decoder_table(struct ub_decoder *decoder) +{ + struct page_table_desc *invalid_desc = &decoder->invalid_desc; + struct page_table *pgtlb; + void *pgtlb_base; + size_t size; + + size = DECODER_PAGE_TABLE_ENTRY_SIZE * DECODER_PAGE_TABLE_SIZE; + pgtlb = &decoder->pgtlb; + pgtlb_base = dmam_alloc_coherent(decoder->dev, size, + &pgtlb->pgtlb_dma, GFP_KERNEL); + if (!pgtlb_base) + return -ENOMEM; + + pgtlb->pgtlb_base = pgtlb_base; + + size = sizeof(*pgtlb->desc_base) * DECODER_PAGE_TABLE_SIZE; + pgtlb->desc_base = kzalloc(size, GFP_KERNEL); + if (!pgtlb->desc_base) + goto release_pgtlb; + + invalid_desc->page_base = dmam_alloc_coherent(decoder->dev, + RANGE_TABLE_PAGE_SIZE, + &invalid_desc->page_dma, + GFP_KERNEL); + if (!invalid_desc->page_base) + goto release_desc; + + decoder->invalid_page_dma = (invalid_desc->page_dma & + DECODER_PGTBL_PGPRT_MASK) >> + DECODER_DMA_PAGE_ADDR_OFFSET; + + ub_decoder_init_page_table(decoder, pgtlb_base); + + return 0; + +release_desc: + kfree(pgtlb->desc_base); + pgtlb->desc_base = NULL; +release_pgtlb: + size = DECODER_PAGE_TABLE_ENTRY_SIZE * DECODER_PAGE_TABLE_SIZE; + dmam_free_coherent(decoder->dev, size, pgtlb_base, pgtlb->pgtlb_dma); + return -ENOMEM; +} + +void hi_free_decoder_table(struct ub_decoder *decoder) +{ + struct page_table_desc *invalid_desc = &decoder->invalid_desc; + size_t size; + + dmam_free_coherent(decoder->dev, RANGE_TABLE_PAGE_SIZE, + invalid_desc->page_base, invalid_desc->page_dma); + kfree(decoder->pgtlb.desc_base); + + size = DECODER_PAGE_TABLE_ENTRY_SIZE * DECODER_PAGE_TABLE_SIZE; + dmam_free_coherent(decoder->dev, size, decoder->pgtlb.pgtlb_base, + decoder->pgtlb.pgtlb_dma); +} + +int hi_decoder_unmap(struct ub_decoder *decoder, phys_addr_t addr, u64 size) { int ret; struct decoder_map_info info = { @@ -609,10 +692,6 @@ int ub_decoder_unmap(struct ub_decoder *decoder, phys_addr_t addr, u64 size) .size = size, }; - if (!decoder) { - pr_err("unmap mmio decoder ptr is null\n"); - return -EINVAL; - } if (size < SZ_1M) size = SZ_1M; ret = handle_table(decoder, &info, false); @@ -621,12 +700,8 @@ int ub_decoder_unmap(struct ub_decoder *decoder, phys_addr_t addr, u64 size) return ub_decoder_cmd_request(decoder, addr, size, TLBI_PARTIAL); } -int ub_decoder_map(struct ub_decoder *decoder, struct decoder_map_info *info) +int hi_decoder_map(struct ub_decoder *decoder, struct decoder_map_info *info) { - if (!decoder || !info) { - pr_err("decoder or map info is null\n"); - return -EINVAL; - } if (info->size < SZ_1M) info->size = SZ_1M; ub_info(decoder->uent, @@ -637,16 +712,3 @@ int ub_decoder_map(struct ub_decoder *decoder, struct decoder_map_info *info) return handle_table(decoder, info, true); } - -void ub_decoder_init_page_table(struct ub_decoder *decoder, void *pgtlb_base) -{ - struct page_table_entry *pgtlb_entry; - int i; - - for (i = 0; i < DECODER_PAGE_TABLE_SIZE; i++) { - pgtlb_entry = (struct page_table_entry *)pgtlb_base + i; - pgtlb_entry->entry_type = PAGE_TABLE; - pgtlb_entry->next_lv_addr = decoder->invalid_page_dma; - pgtlb_entry->pgtlb_attr = PGTLB_ATTR_DEFAULT; - } -} diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.h b/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.h new file mode 100644 index 000000000000..50658ef7b9cb --- /dev/null +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __HISI_DECODER_H__ +#define __HISI_DECODER_H__ + +#include +#include +#include "../../decoder.h" + +#define DECODER_PGTBL_PGPRT_MASK GENMASK_ULL(47, 12) +#define DECODER_DMA_PAGE_ADDR_OFFSET 12 + +#define PGTLB_CACHE_IR_NC 0b00 +#define PGTLB_CACHE_IR_WBRA 0b01 +#define PGTLB_CACHE_IR_WT 0b10 +#define PGTLB_CACHE_IR_WB 0b11 +#define PGTLB_CACHE_OR_NC 0b0000 +#define PGTLB_CACHE_OR_WBRA 0b0100 +#define PGTLB_CACHE_OR_WT 0b1000 +#define PGTLB_CACHE_OR_WB 0b1100 +#define PGTLB_CACHE_SH_NSH 0b000000 +#define PGTLB_CACHE_SH_OSH 0b100000 +#define PGTLB_CACHE_SH_ISH 0b110000 + +#define PGTLB_ATTR_DEFAULT (PGTLB_CACHE_IR_WBRA | \ + PGTLB_CACHE_OR_WBRA | \ + PGTLB_CACHE_SH_ISH) + +#define RGTLB_TO_PGTLB 8 +#define DECODER_PAGE_ENTRY_SIZE 64 +#define DECODER_PAGE_SIZE (1 << 12) +#define DECODER_PAGE_TABLE_SIZE (1 << 12) +#define PAGE_TABLE_PAGE_SIZE (DECODER_PAGE_ENTRY_SIZE * DECODER_PAGE_SIZE) +#define RANGE_TABLE_PAGE_SIZE (DECODER_PAGE_ENTRY_SIZE * \ + DECODER_PAGE_SIZE * \ + RGTLB_TO_PGTLB) + +void hi_register_decoder_base_addr(struct ub_bus_controller *ubc, + u64 *cmd_queue, u64 *event_queue); + +int hi_create_decoder_table(struct ub_decoder *decoder); +void hi_free_decoder_table(struct ub_decoder *decoder); + +int hi_decoder_map(struct ub_decoder *decoder, struct decoder_map_info *info); +int hi_decoder_unmap(struct ub_decoder *decoder, phys_addr_t addr, u64 size); + +#endif /* __HISI_DECODER_H__ */ diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h index 092695e9d43c..92e97c257302 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h @@ -43,8 +43,6 @@ int hi_mem_decoder_create(struct ub_bus_controller *ubc); void hi_mem_decoder_remove(struct ub_bus_controller *ubc); void hi_register_ubmem_irq(struct ub_bus_controller *ubc); void hi_unregister_ubmem_irq(struct ub_bus_controller *ubc); -void hi_register_decoder_base_addr(struct ub_bus_controller *ubc, u64 *cmd_queue, - u64 *event_queue); int hi_send_entity_enable_msg(struct ub_entity *uent, u8 enable); int ub_bus_controller_probe(struct ub_bus_controller *ubc); -- Gitee From d0dca816db8565514330edf46b692a1df4ec0163 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Mon, 1 Dec 2025 15:37:47 +0800 Subject: [PATCH 105/214] ub:ubus: fix bug of msg workqueue null ANBZ: #45460 commit 08ec74f94b034b0face4f8f0509bb43e1a1cd3be openEuler. 1.fix bug of msg workqueue null 2.don't cfg ubc0 route table during cluster mode Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/msg.c | 12 +++++++----- drivers/ub/ubus/route.c | 4 ++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/ub/ubus/msg.c b/drivers/ub/ubus/msg.c index 1d1893a8f54d..54f77128ad2f 100644 --- a/drivers/ub/ubus/msg.c +++ b/drivers/ub/ubus/msg.c @@ -162,7 +162,7 @@ struct workqueue_struct *get_rx_msg_wq(u8 msg_code) return rx_msg_wq[msg_code]; } -static bool msg_rx_flag; +static atomic_t msg_rx_flag; int message_rx_init(void) { @@ -186,18 +186,20 @@ int message_rx_init(void) rx_msg_wq[i] = q; } - msg_rx_flag = true; + wmb(); /* Ensure the register is written correctly. */ + atomic_set(&msg_rx_flag, 1); return 0; } void message_rx_uninit(void) { -#define MSG_RX_WAIT_US 1000 +#define MSG_RX_WAIT_US 15000 struct workqueue_struct *q; int i; - msg_rx_flag = false; + atomic_set(&msg_rx_flag, 0); + wmb(); /* Ensure the register is written correctly. */ /* For cpus still handle rx msg in interrupt context */ udelay(MSG_RX_WAIT_US); @@ -297,7 +299,7 @@ int message_rx_handler(struct ub_bus_controller *ubc, void *pkt, u16 len) struct msg_extended_header *msgetah = &header->msgetah; struct ub_rx_msg_task *task; - if (!msg_rx_flag) + if (!atomic_read(&msg_rx_flag)) return -EBUSY; if (len < MSG_PKT_HEADER_SIZE) { diff --git a/drivers/ub/ubus/route.c b/drivers/ub/ubus/route.c index 364bf78d93c3..ef3d462a90ee 100644 --- a/drivers/ub/ubus/route.c +++ b/drivers/ub/ubus/route.c @@ -505,6 +505,10 @@ static void ub_set_route_table_entry(struct ub_entity *uent, u32 dst_cna, if (uent->port_nums == 1) return; + /* In a cluster scenario, do not configure the UBC routing table. */ + if (is_ibus_controller(uent) && uent->ubc->cluster) + return; + pr_info("cna %#x uent set dstcna %#x route\n", uent->cna, dst_cna); for (i = 0; i < EBW(uent->port_nums); i++) { -- Gitee From aca6d872615173a7c18d93329b24e3ace64aecf2 Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Fri, 14 Nov 2025 15:59:01 +0800 Subject: [PATCH 106/214] vfio:ubus vfio-ub support ub entity enable ANBZ: #45460 commit 49e93aebab5dc6a0f1901977aafdf4fca30939b9 openEuler. vfio-ub support ub entity enable. Signed-off-by: Yahui Liu Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/vfio/ubus/vfio_ub_config.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/vfio/ubus/vfio_ub_config.c b/drivers/vfio/ubus/vfio_ub_config.c index 44e8e470d233..4be3c099a32a 100644 --- a/drivers/vfio/ubus/vfio_ub_config.c +++ b/drivers/vfio/ubus/vfio_ub_config.c @@ -589,6 +589,9 @@ static int vfio_ub_cfg1_basic_write(struct vfio_ub_core_device *vdev, u64 pos, if (count < 0) return count; + if (pos == UB_ENTITY_RS_ACCESS_EN) + ub_entity_enable(vdev->uent, val & 0x1); + buf = vfio_ub_find_cfg_buf(vdev, UB_CFG1_BASIC_CAP); if (!buf) return -EFAULT; -- Gitee From 1da09c21b570126f4aea06f9aaa2dbd5e349f76d Mon Sep 17 00:00:00 2001 From: Anzhe Li Date: Sat, 29 Nov 2025 11:53:40 +0800 Subject: [PATCH 107/214] ub:ubus adapt port local ras ANBZ: #45460 commit 3764805d000a62144f97c87c2ecee5c918a0801e openEuler. Adapt port local ras, and add port reset function in cluster mode. Signed-off-by: Anzhe Li Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/link.c | 14 ++-- drivers/ub/ubus/pool.c | 4 +- drivers/ub/ubus/port.c | 79 ++++++++------------ drivers/ub/ubus/port.h | 7 +- drivers/ub/ubus/reset.c | 6 +- drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h | 2 +- drivers/ub/ubus/vendor/hisilicon/local-ras.c | 40 +++++++++- drivers/ub/ubus/vendor/hisilicon/vdm.c | 45 +++++++++++ drivers/ub/ubus/vendor/hisilicon/vdm.h | 11 +++ 9 files changed, 142 insertions(+), 66 deletions(-) diff --git a/drivers/ub/ubus/link.c b/drivers/ub/ubus/link.c index 266d8e828143..001139f3ad63 100644 --- a/drivers/ub/ubus/link.c +++ b/drivers/ub/ubus/link.c @@ -285,13 +285,13 @@ static void port_link_state_change(struct ub_port *port, struct ub_port *r_port) void ublc_link_up_handle(struct ub_port *port) { struct ub_entity *uent = port->uent; - struct ub_port *r_port; struct ub_entity *r_uent; + struct ub_port *r_port; int ret; if (port->r_uent) { - ub_err(uent, "port%u is already up\n", port->index); - return; + ub_warn(uent, "port%u is already up\n", port->index); + goto link_up_notify; } device_lock(&uent->dev); @@ -324,6 +324,8 @@ void ublc_link_up_handle(struct ub_port *port) ub_info(uent, "port%u link up\n", port->index); out: device_unlock(&uent->dev); +link_up_notify: + ub_notify_share_port(port, UB_PORT_EVENT_LINK_UP); } void ublc_link_down_handle(struct ub_port *port) @@ -332,8 +334,8 @@ void ublc_link_down_handle(struct ub_port *port) struct ub_port *r_port; if (!port->r_uent) { - ub_err(uent, "port%u is already down\n", port->index); - return; + ub_warn(uent, "port%u is already down\n", port->index); + goto link_down_notify; } device_lock(&uent->dev); @@ -355,6 +357,8 @@ void ublc_link_down_handle(struct ub_port *port) device_unlock(&uent->dev); ub_info(uent, "port%u link down\n", port->index); +link_down_notify: + ub_notify_share_port(port, UB_PORT_EVENT_LINK_DOWN); } void ub_link_change_handler(struct work_struct *work) diff --git a/drivers/ub/ubus/pool.c b/drivers/ub/ubus/pool.c index e86b19b58f63..414e9dba0c20 100644 --- a/drivers/ub/ubus/pool.c +++ b/drivers/ub/ubus/pool.c @@ -613,9 +613,9 @@ static void ub_port_reset_notify_handler(struct ub_bus_controller *ubc, void *ms port = ubc->uent->ports + pld->port_index; if (port->shareable && port->domain_boundary) { if (pld->type == RESET_PREPARE) - ub_notify_share_port(port, RESET_PREPARE); + ub_notify_share_port(port, UB_PORT_EVENT_RESET_PREPARE); else if (pld->type == RESET_DONE) - ub_notify_share_port(port, RESET_DONE); + ub_notify_share_port(port, UB_PORT_EVENT_RESET_DONE); } rsp: diff --git a/drivers/ub/ubus/port.c b/drivers/ub/ubus/port.c index 36950c24e344..a8a238df8cc4 100644 --- a/drivers/ub/ubus/port.c +++ b/drivers/ub/ubus/port.c @@ -577,49 +577,46 @@ static DECLARE_RWSEM(ub_share_port_notify_list_rwsem); struct ub_share_port_notify_node { struct ub_entity *parent; - struct ub_entity *idev; + struct ub_entity *entity; u16 port_id; struct ub_share_port_ops *ops; struct list_head node; }; -int ub_register_share_port(struct ub_entity *idev, u16 port_id, +int ub_register_share_port(struct ub_entity *entity, u16 port_id, struct ub_share_port_ops *ops) { struct ub_share_port_notify_node *notify_node; struct ub_entity *parent; struct ub_port *port; - if (unlikely(!idev || !ops)) + if (unlikely(!entity || !ops)) return -EINVAL; - if (!is_idev(idev)) { - ub_err(idev, "don't support non-idev device with type %u register share port\n", - uent_type(idev)); + if (!is_idev(entity) && !is_ibus_controller(entity)) { + ub_err(entity, + "don't support device with type %u register share port\n", + uent_type(entity)); return -EINVAL; } /* get primary entity first */ - parent = idev; - while (!is_primary(parent)) - parent = parent->pue; - - /* check parent is controller */ - parent = to_ub_entity(parent->dev.parent); - if (!is_ibus_controller(parent)) { - ub_err(idev, "don't support register share port at non-controller device with type %u\n", - uent_type(parent)); - return -EINVAL; - } - - if (port_id >= parent->port_nums) { - ub_err(parent, "port id %u exceeds port num %u\n", port_id, - parent->port_nums); - return -EINVAL; + parent = entity; + if (is_idev(parent)) { + while (!is_primary(parent)) + parent = parent->pue; + + /* check parent is controller */ + parent = to_ub_entity(parent->dev.parent); + if (!is_ibus_controller(parent)) { + ub_err(entity, "don't support register share port at non-controller device with type %u\n", + uent_type(parent)); + return -EINVAL; + } } port = parent->ports + port_id; - if (!port->shareable) { + if (is_idev(entity) && !port->shareable) { ub_err(parent, "port%u isn't shareable\n", port_id); return -EINVAL; } @@ -629,7 +626,7 @@ int ub_register_share_port(struct ub_entity *idev, u16 port_id, return -ENOMEM; notify_node->parent = parent; - notify_node->idev = idev; + notify_node->entity = entity; notify_node->port_id = port_id; notify_node->ops = ops; INIT_LIST_HEAD(¬ify_node->node); @@ -638,33 +635,33 @@ int ub_register_share_port(struct ub_entity *idev, u16 port_id, list_add_tail(¬ify_node->node, &ub_share_port_notify_list); up_write(&ub_share_port_notify_list_rwsem); - ub_info(idev, "register share port at %u success\n", port_id); + ub_info(entity, "register share port at %u success\n", port_id); return 0; } EXPORT_SYMBOL_GPL(ub_register_share_port); -void ub_unregister_share_port(struct ub_entity *idev, u16 port_id, +void ub_unregister_share_port(struct ub_entity *entity, u16 port_id, struct ub_share_port_ops *ops) { struct ub_share_port_notify_node *notify_node; - if (unlikely(!idev)) + if (unlikely(!entity)) return; down_write(&ub_share_port_notify_list_rwsem); list_for_each_entry(notify_node, &ub_share_port_notify_list, node) { - if (notify_node->idev != idev || + if (notify_node->entity != entity || notify_node->port_id != port_id || notify_node->ops != ops) continue; list_del(¬ify_node->node); kfree(notify_node); - ub_info(idev, "unregister share port at %u success\n", port_id); + ub_info(entity, "unregister share port at %u success\n", port_id); goto unlock; } - ub_err(idev, "share port %u isn't registered, unregister failed\n", + ub_err(entity, "share port %u isn't registered, unregister failed\n", port_id); unlock: up_write(&ub_share_port_notify_list_rwsem); @@ -672,13 +669,13 @@ void ub_unregister_share_port(struct ub_entity *idev, u16 port_id, EXPORT_SYMBOL_GPL(ub_unregister_share_port); void ub_notify_share_port(struct ub_port *port, - enum ub_share_port_notify_type type) + enum ub_port_event type) { struct ub_share_port_notify_node *notify_node; struct ub_share_port_ops *ops; struct ub_entity *uent; - if (!port || type >= NOTIFY_TYPE_MAX) + if (!port || type > UB_PORT_EVENT_RESET_DONE) return; uent = port->uent; @@ -689,23 +686,13 @@ void ub_notify_share_port(struct ub_port *port, continue; ops = notify_node->ops; - switch (type) { - case RESET_PREPARE: - if (ops->reset_prepare) - ops->reset_prepare(notify_node->idev, - notify_node->port_id); - break; - case RESET_DONE: - if (ops->reset_done) - ops->reset_done(notify_node->idev, - notify_node->port_id); - break; - default: - break; - } + if (ops->event_notify) + ops->event_notify(notify_node->entity, + notify_node->port_id, type); } up_read(&ub_share_port_notify_list_rwsem); } +EXPORT_SYMBOL_GPL(ub_notify_share_port); bool ub_port_check_link_up(struct ub_port *port) { diff --git a/drivers/ub/ubus/port.h b/drivers/ub/ubus/port.h index 0350908c531a..21a2c7d33299 100644 --- a/drivers/ub/ubus/port.h +++ b/drivers/ub/ubus/port.h @@ -8,10 +8,9 @@ #define for_each_uent_port(p, d) \ for ((p) = (d)->ports; ((p) - (d)->ports) < (d)->port_nums; (p)++) -enum ub_share_port_notify_type { +enum ub_port_reset_notify_type { RESET_PREPARE, - RESET_DONE, - NOTIFY_TYPE_MAX + RESET_DONE }; struct ub_port; @@ -24,7 +23,7 @@ void ub_ports_del(struct ub_entity *uent); int ub_ports_setup(struct ub_entity *uent); void ub_ports_unset(struct ub_entity *uent); void ub_notify_share_port(struct ub_port *port, - enum ub_share_port_notify_type type); + enum ub_port_event type); int ub_port_write_dword(struct ub_port *port, u32 pos, u32 val); bool ub_port_check_link_up(struct ub_port *port); diff --git a/drivers/ub/ubus/reset.c b/drivers/ub/ubus/reset.c index 596d848c2c11..51d984d25b8f 100644 --- a/drivers/ub/ubus/reset.c +++ b/drivers/ub/ubus/reset.c @@ -293,8 +293,7 @@ int ub_port_reset(struct ub_entity *dev, int port_id) return -EINVAL; } - if (port->shareable) - ub_notify_share_port(port, RESET_PREPARE); + ub_notify_share_port(port, UB_PORT_EVENT_RESET_PREPARE); /* enable port reset */ ret = ub_port_write_dword(port, UB_PORT_RST, 0x01); @@ -310,8 +309,7 @@ int ub_port_reset(struct ub_entity *dev, int port_id) device_unlock(&dev->dev); if (ub_wait_port_complete(port)) { - if (port->shareable) - ub_notify_share_port(port, RESET_DONE); + ub_notify_share_port(port, UB_PORT_EVENT_RESET_DONE); port->link_state = LINK_STATE_NORMAL; ub_info(dev, "port(%d) reset success!\n", port_id); return ret; diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h index 92e97c257302..44e5fc8165ba 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-ubus.h @@ -44,7 +44,7 @@ void hi_mem_decoder_remove(struct ub_bus_controller *ubc); void hi_register_ubmem_irq(struct ub_bus_controller *ubc); void hi_unregister_ubmem_irq(struct ub_bus_controller *ubc); int hi_send_entity_enable_msg(struct ub_entity *uent, u8 enable); - +int hi_send_port_reset_msg(struct ub_entity *uent, u16 port_idx); int ub_bus_controller_probe(struct ub_bus_controller *ubc); void ub_bus_controller_remove(struct ub_bus_controller *ubc); diff --git a/drivers/ub/ubus/vendor/hisilicon/local-ras.c b/drivers/ub/ubus/vendor/hisilicon/local-ras.c index 9dc016555732..cf65f06da93f 100644 --- a/drivers/ub/ubus/vendor/hisilicon/local-ras.c +++ b/drivers/ub/ubus/vendor/hisilicon/local-ras.c @@ -9,7 +9,9 @@ #include "../../ubus.h" #include "../../ubus_driver.h" #include "../../reset.h" +#include "../../port.h" #include "local-ras.h" +#include "hisi-ubus.h" struct sub_module_info { u32 sub_module_id; @@ -197,7 +199,31 @@ static int ubus_port_recover(struct ub_entity *uent, u16 port_id) return 0; } -static int nl_ssu_link_credi_overtime_recover(struct ub_entity *uent, u8 nl_id) +static int ubus_port_recover_cluster(struct ub_entity *uent, u16 port_id) +{ + struct ub_port *port; + int ret; + + if (port_id >= uent->port_nums || uent->ports[port_id].type != PHYSICAL) { + pr_err("port id is over port nums or port type is not physical\n"); + return -EINVAL; + } + + port = uent->ports + port_id; + ub_notify_share_port(port, UB_PORT_EVENT_RESET_PREPARE); + + ret = hi_send_port_reset_msg(uent, port_id); + if (ret) { + pr_err("ub vdm port reset failed, ret:%d\n", ret); + return ret; + } + + ub_notify_share_port(port, UB_PORT_EVENT_RESET_DONE); + + return 0; +} + +static int nl_ssu_link_credi_overtime_recover(struct ub_entity *uent, u8 nl_id, bool cluster) { #define NL_PORTS 2 /* @@ -210,7 +236,10 @@ static int nl_ssu_link_credi_overtime_recover(struct ub_entity *uent, u8 nl_id) for (i = 0; i < NL_PORTS; i++) { port_id += i; - ret = ubus_port_recover(uent, port_id); + if (!cluster) + ret = ubus_port_recover(uent, port_id); + else + ret = ubus_port_recover_cluster(uent, port_id); if (ret) { ub_err(uent, "port[%u] recover failed, ret=%d.\n", port_id, ret); return ret; @@ -230,11 +259,14 @@ static int ubus_recover(struct ub_entity *uent, if (is_nl_local_ras(edata->sub_module_id) && is_nl_ssu_link_credi_overtime_err(edata)) { nl_id = edata->core_id; - return nl_ssu_link_credi_overtime_recover(uent, nl_id); + return nl_ssu_link_credi_overtime_recover(uent, nl_id, uent->ubc->cluster); } port_id = (int)edata->port_id; - return ubus_port_recover(uent, port_id); + if (uent->ubc->cluster) + return ubus_port_recover_cluster(uent, port_id); + else + return ubus_port_recover(uent, port_id); } static void hisi_ubus_handle_error(struct ub_entity *uent, diff --git a/drivers/ub/ubus/vendor/hisilicon/vdm.c b/drivers/ub/ubus/vendor/hisilicon/vdm.c index 2d0444a585c9..f95da0843e26 100644 --- a/drivers/ub/ubus/vendor/hisilicon/vdm.c +++ b/drivers/ub/ubus/vendor/hisilicon/vdm.c @@ -537,3 +537,48 @@ int hi_send_entity_enable_msg(struct ub_entity *uent, u8 enable) return 0; } + +int hi_send_port_reset_msg(struct ub_entity *uent, u16 port_idx) +{ + struct port_reset_pld *rst_pld; + struct vdm_msg_pkt pkt = {}; + struct msg_info info = {}; + struct msg_pkt_dw0 *pld_dw0; + u8 status; + int ret; + + if (!uent->ubc->cluster) + return 0; + + ub_msg_pkt_header_init(&pkt.header, uent, VDM_PORT_RESET_PLD_SIZE, + code_gen(UB_MSG_CODE_VDM, UB_VENDOR_MSG, + MSG_REQ), true); + + pkt.guid_high = *(u64 *)(&uent->ubc->uent->guid.dw[SZ_2]); + pld_dw0 = &pkt.pld_dw0; + pld_dw0->opcode = VDM_OPCODE_UB2FM_COMM_MSG; + pld_dw0->sub_opcode = VDM_SUB_OPCODE_PORT_RESET; + rst_pld = &pkt.reset_pld; + rst_pld->port_idx = port_idx; + + message_info_init(&info, uent->ubc->uent, &pkt, &pkt, + (VDM_PORT_RESET_SIZE << MSG_REQ_SIZE_OFFSET) | + VDM_PORT_RESET_SIZE); + + ub_info(uent, "Sync request port reset msg\n"); + + ret = hi_message_sync_request(uent->message->mdev, &info, + pkt.header.msgetah.code); + if (ret) { + ub_err(uent, "msg sync request ret=%d\n", ret); + return ret; + } + + status = pkt.header.msgetah.rsp_status; + if (status != UB_MSG_RSP_SUCCESS) { + ub_err(uent, "msg rsp status=%#02x\n", status); + return -EINVAL; + } + + return 0; +} diff --git a/drivers/ub/ubus/vendor/hisilicon/vdm.h b/drivers/ub/ubus/vendor/hisilicon/vdm.h index 183449aa082e..725288d6abac 100644 --- a/drivers/ub/ubus/vendor/hisilicon/vdm.h +++ b/drivers/ub/ubus/vendor/hisilicon/vdm.h @@ -24,6 +24,7 @@ enum vdm_fm2ub_sub_opcode { enum vdm_ub2fm_sub_opcode { VDM_SUB_OPCODE_ENTITY_ENABLE = 0x1, + VDM_SUB_OPCODE_PORT_RESET = 0x2, }; struct msg_pkt_dw0 { @@ -103,6 +104,13 @@ struct idev_ue_rls_pld { }; #define IDEV_UE_RLS_PLD_TOTAL_SIZE 36 +struct port_reset_pld { + /* DW1 */ + u16 rsvd; + u16 port_idx; +}; +#define VDM_PORT_RESET_PLD_SIZE 16 + #define MSG_IDEV_MUE_REG_SIZE \ (MSG_PKT_HEADER_SIZE + IDEV_MUE_REG_PLD_TOTAL_SIZE) #define MSG_IDEV_MUE_RLS_SIZE \ @@ -111,6 +119,8 @@ struct idev_ue_rls_pld { (MSG_PKT_HEADER_SIZE + IDEV_UE_REG_PLD_TOTAL_SIZE) #define MSG_IDEV_UE_RLS_SIZE \ (MSG_PKT_HEADER_SIZE + IDEV_UE_RLS_PLD_TOTAL_SIZE) +#define VDM_PORT_RESET_SIZE \ + (MSG_PKT_HEADER_SIZE + VDM_PORT_RESET_PLD_SIZE) #define VENDOR_GUID_PLD_SIZE 8 @@ -119,6 +129,7 @@ struct vdm_msg_pkt { u64 guid_high; struct msg_pkt_dw0 pld_dw0; union { + struct port_reset_pld reset_pld; struct entity_enable_pld enable_pld; struct idev_pue_reg_pld pd_reg_pld; struct idev_pue_rls_pld pd_rls_pld; -- Gitee From 34316d5bef5db19208c00b30c55651633fe09118 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Tue, 25 Nov 2025 19:27:05 +0800 Subject: [PATCH 108/214] ub:ubus: add active_mutex in ub_entity ANBZ: #45460 commit f21f7d173065acedec14639e7522420e62baefde openEuler. Add mutex to the entity structure. Signed-off-by: Yiyu Liu Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/ubus_entity.c | 25 ++++++++++++++++--------- include/ub/ubus/ubus.h | 12 +++++++----- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index 6031469a27b9..4fa1a8533e71 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -53,6 +53,8 @@ struct ub_entity *ub_alloc_ent(void) INIT_LIST_HEAD(&uent->slot_list); INIT_LIST_HEAD(&uent->instance_node); + mutex_init(&uent->active_mutex); + uent->dev.type = &ub_dev_type; uent->cna = 0; uent->tid = 0; /* default tid according to ummu */ @@ -971,12 +973,17 @@ void ub_entity_enable(struct ub_entity *uent, u8 enable) ub_cfg_write_byte(uent, UB_BUS_ACCESS_EN, enable); ub_cfg_write_byte(uent, UB_ENTITY_RS_ACCESS_EN, enable); - if (!enable && !ub_entity_test_priv_flag(uent, UB_ENTITY_ACTIVE)) + mutex_lock(&uent->active_mutex); + + if (!enable && !ub_entity_test_priv_flag(uent, UB_ENTITY_ACTIVE)) { + mutex_unlock(&uent->active_mutex); return; + } if (uent->ubc && uent->ubc->ops && uent->ubc->ops->entity_enable) { ret = uent->ubc->ops->entity_enable(uent, enable); if (ret) { + mutex_unlock(&uent->active_mutex); ub_err(uent, "entity enable, ret=%d, enable=%u\n", ret, enable); return; @@ -989,6 +996,8 @@ void ub_entity_enable(struct ub_entity *uent, u8 enable) ub_entity_assign_priv_flag(uent, UB_ENTITY_ACTIVE, true); else ub_entity_assign_priv_flag(uent, UB_ENTITY_ACTIVE, false); + + mutex_unlock(&uent->active_mutex); } EXPORT_SYMBOL_GPL(ub_entity_enable); @@ -1074,12 +1083,11 @@ int ub_activate_entity(struct ub_entity *uent, u32 entity_idx) return -EINVAL; } - if (!device_trylock(&target_dev->dev)) - return -EBUSY; + mutex_lock(&target_dev->active_mutex); if (ub_entity_test_priv_flag(target_dev, UB_ENTITY_ACTIVE)) { ub_warn(uent, "entity_idx[%u] is already in normal state\n", entity_idx); - device_unlock(&target_dev->dev); + mutex_unlock(&target_dev->active_mutex); return 0; } @@ -1091,7 +1099,7 @@ int ub_activate_entity(struct ub_entity *uent, u32 entity_idx) ub_info(uent, "udrv activate entity_idx[%u] success\n", entity_idx); } - device_unlock(&target_dev->dev); + mutex_unlock(&target_dev->active_mutex); return ret; } EXPORT_SYMBOL_GPL(ub_activate_entity); @@ -1115,12 +1123,11 @@ int ub_deactivate_entity(struct ub_entity *uent, u32 entity_idx) return -EINVAL; } - if (!device_trylock(&target_dev->dev)) - return -EBUSY; + mutex_lock(&target_dev->active_mutex); if (!ub_entity_test_priv_flag(target_dev, UB_ENTITY_ACTIVE)) { ub_warn(uent, "entity_idx[%u] is already in disable state\n", entity_idx); - device_unlock(&target_dev->dev); + mutex_unlock(&target_dev->active_mutex); return 0; } @@ -1132,7 +1139,7 @@ int ub_deactivate_entity(struct ub_entity *uent, u32 entity_idx) ub_info(uent, "udrv deactivate entity_idx[%u] success\n", entity_idx); } - device_unlock(&target_dev->dev); + mutex_unlock(&target_dev->active_mutex); return ret; } EXPORT_SYMBOL_GPL(ub_deactivate_entity); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 3bf0bd47cb24..be2758aa4fc4 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -264,6 +264,8 @@ struct ub_entity { u32 user_eid; struct ub_eu_table *eu_table; + struct mutex active_mutex; + u32 support_feature; u16 upi; @@ -569,7 +571,7 @@ struct ub_entity *ub_get_entity(unsigned int vendor, unsigned int entity, * * Enable or disable the communication channel between entity and user host. * - * Context: Any context. + * Context: Any context, it will take mutex_lock()/mutex_unlock(). */ void ub_entity_enable(struct ub_entity *uent, u8 enable); @@ -884,9 +886,9 @@ const struct cpumask *ub_irq_get_affinity(struct ub_entity *uent, int nr); * @uent: UB entity. * @entity_idx: Number of the entity to be activated. * - * Context: Any context, it will take device_trylock()/device_unlock() + * Context: Any context, it will take mutex_lock()/mutex_unlock() * Return: 0 if success, or %-EINVAL if the device doesn't match the driver, - * or %-EBUSY if can't get device_trylock(), or other failed negative values. + * or other failed negative values. */ int ub_activate_entity(struct ub_entity *uent, u32 entity_idx); @@ -895,9 +897,9 @@ int ub_activate_entity(struct ub_entity *uent, u32 entity_idx); * @uent: UB entity. * @entity_idx: Number of the entity to be deactivated. * - * Context: Any context, it will take device_trylock()/device_unlock() + * Context: Any context, it will take mutex_lock()/mutex_unlock() * Return: 0 if success, or %-EINVAL if the entity doesn't match the driver, - * or %-EBUSY if can't get device_trylock(), or other failed negative values. + * or other failed negative values. */ int ub_deactivate_entity(struct ub_entity *uent, u32 entity_idx); -- Gitee From 66ea727cb9f3eb73ae3fa78ab1d7f5c51fbba6d9 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Tue, 9 Dec 2025 09:56:40 +0800 Subject: [PATCH 109/214] ub:hisi-ubus: Fix ue reg/unreg without lock bug ANBZ: #45460 commit d6926f5c7ee4c3f9dda78762a3f59a5afa867963 openEuler. Currently, UE registration and deregistration are not performed under the protection of the MUE device lock. When the MUE performs a reset, there is a risk of concurrent resource conflicts. Fixes: 86fec00cb73a ("ub:hisi-ubus: Support UBUS vdm entity enable message") Signed-off-by: Junlong Zheng Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/vendor/hisilicon/vdm.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/ub/ubus/vendor/hisilicon/vdm.c b/drivers/ub/ubus/vendor/hisilicon/vdm.c index f95da0843e26..4a19e0fb8d57 100644 --- a/drivers/ub/ubus/vendor/hisilicon/vdm.c +++ b/drivers/ub/ubus/vendor/hisilicon/vdm.c @@ -14,8 +14,6 @@ #include "hisi-ubus.h" #include "vdm.h" -static DEFINE_SPINLOCK(ub_vdm_lock); - struct opcode_func_map { u16 sub_opcode; u16 idev_pkt_size; @@ -274,6 +272,7 @@ static u8 ub_idevice_ue_add_handler(struct ub_bus_controller *ubc, struct vdm_ms struct ub_entity *pue, *alloc_dev = NULL; u16 ue_entity_idx = pld->ue_entity_idx; int start_idx, end_idx, ret; + int lock = 0; u8 status; /* check whether pue is registered. */ @@ -306,10 +305,13 @@ static u8 ub_idevice_ue_add_handler(struct ub_bus_controller *ubc, struct vdm_ms goto ue_reg_rsp; } - spin_lock(&ub_vdm_lock); - ret = ub_idevice_enable_handle(pue, ue_entity_idx, 0, NULL, &alloc_dev); - spin_unlock(&ub_vdm_lock); + lock = device_trylock(&pue->dev); + if (!lock) { + status = UB_MSG_RSP_EXEC_EBUSY; + goto ue_reg_rsp; + } + ret = ub_idevice_enable_handle(pue, ue_entity_idx, 0, NULL, &alloc_dev); if (ret == 0) { alloc_dev->user_eid = pld->user_eid[0]; ub_info(pue, "enable idev ue succeeded, user_eid=0x%x\n", @@ -331,6 +333,9 @@ static u8 ub_idevice_ue_add_handler(struct ub_bus_controller *ubc, struct vdm_ms pue->num_ues += 1; } + if (lock) + device_unlock(&pue->dev); + return status; } @@ -346,6 +351,7 @@ static u8 ub_idevice_ue_rls_handler(struct ub_bus_controller *ubc, struct vdm_ms struct ub_entity *pue, *vd_dev, *tmp; u16 ue_entity_idx = pld->ue_entity_idx; u16 start_idx, end_idx; + int lock = 0; u8 status; /* search for pue with guid. Return an error if pue does not exist */ @@ -372,6 +378,12 @@ static u8 ub_idevice_ue_rls_handler(struct ub_bus_controller *ubc, struct vdm_ms "The pue of this vdm ue to be disabled is normal\n"); } + lock = device_trylock(&pue->dev); + if (!lock) { + status = UB_MSG_RSP_EXEC_EBUSY; + goto ue_rls_rsp; + } + status = UB_MSG_RSP_EXEC_ENODEV; /* otherwise, delete this ue with ue idx in message payload */ list_for_each_entry_safe(vd_dev, tmp, &pue->ue_list, node) @@ -389,6 +401,9 @@ static u8 ub_idevice_ue_rls_handler(struct ub_bus_controller *ubc, struct vdm_ms pue->num_ues -= 1; } + if (lock) + device_unlock(&pue->dev); + return status; } -- Gitee From d8e2d7fa22293e57d3431036406536871ef7b780 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Tue, 9 Dec 2025 10:29:25 +0800 Subject: [PATCH 110/214] ub:ubus: Delete ubc cfg0 config during cluster mode ANBZ: #45460 commit 3ee6b2906e902998d3bcb476bd812e9d4db8c738 openEuler. In cluster mode, the management authority of UBC is primarily held by UBFM. Ubus cannot configure the configuration space of cfg0 except for the vport configuration space. Fixes: 280895301d3b ("ub:ubus: Support Ubus read/write configuration functions") Signed-off-by: Junlong Zheng Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/enum.c | 4 +++ drivers/ub/ubus/eu.c | 5 +++- drivers/ub/ubus/instance.c | 12 +++++---- drivers/ub/ubus/pool.c | 7 +++++ drivers/ub/ubus/port.c | 4 +++ drivers/ub/ubus/services/gucd.c | 29 ++++----------------- drivers/ub/ubus/ubus_entity.c | 22 +++++++++------- drivers/ub/ubus/vendor/hisilicon/eu-table.c | 6 +++++ include/uapi/ub/ubus/ubus_regs.h | 5 ---- 9 files changed, 49 insertions(+), 45 deletions(-) diff --git a/drivers/ub/ubus/enum.c b/drivers/ub/ubus/enum.c index 48c37e30ca31..67a87f9f23aa 100644 --- a/drivers/ub/ubus/enum.c +++ b/drivers/ub/ubus/enum.c @@ -1430,6 +1430,10 @@ int ub_enum_entities_active(struct list_head *dev_list) list_del(&uent->node); ub_entity_add(uent, uent->ubc); + + if (is_ibus_controller(uent) && uent->ubc->cluster) + continue; + ub_start_ent(uent); } diff --git a/drivers/ub/ubus/eu.c b/drivers/ub/ubus/eu.c index 97e040eadd5e..5c99d44918e5 100644 --- a/drivers/ub/ubus/eu.c +++ b/drivers/ub/ubus/eu.c @@ -93,7 +93,7 @@ void ub_eu_table_init(struct ub_entity *uent) struct ub_bus_controller *ubc = uent->ubc; int ret; - if (!is_ibus_controller(uent)) + if (!is_ibus_controller(uent) || ubc->cluster) return; ret = ub_eu_table_init_common(uent); @@ -137,6 +137,9 @@ int ub_cfg_eu_table(struct ub_bus_controller *ubc, bool flag, u32 eid, u16 upi) struct ub_bus_controller_ops *ops = ubc->ops; int ret; + if (ubc->cluster) + return 0; + if (!ops || !ops->eu_cfg) return -ENODEV; diff --git a/drivers/ub/ubus/instance.c b/drivers/ub/ubus/instance.c index 8c49c04388e3..342fc9960ef2 100644 --- a/drivers/ub/ubus/instance.c +++ b/drivers/ub/ubus/instance.c @@ -915,15 +915,17 @@ int ub_ioctl_bus_instance_unbind(void __user *uptr) int ub_default_bus_instance_init(struct ub_entity *uent) { - bool m_idev = is_p_idevice(uent); - bool fad = is_p_device(uent); struct ub_bus_instance *bi; + bool use_cluster; int ret; if (is_switch(uent)) return 0; - if (fad || m_idev) { + use_cluster = is_p_device(uent) || is_p_idevice(uent) || + (is_ibus_controller(uent) && uent->ubc->cluster); + + if (use_cluster) { mutex_lock(&dynamic_mutex); bi = ub_find_bus_instance(eid_match, &uent->user_eid); } else { @@ -931,7 +933,7 @@ int ub_default_bus_instance_init(struct ub_entity *uent) } if (!bi) { - if (fad || m_idev) + if (use_cluster) mutex_unlock(&dynamic_mutex); ub_err(uent, "get default bi NULL\n"); return -EINVAL; @@ -941,7 +943,7 @@ int ub_default_bus_instance_init(struct ub_entity *uent) ret = ub_bind_bus_instance(uent, bi); mutex_unlock(&uent->instance_lock); - if (fad || m_idev) { + if (use_cluster) { ub_bus_instance_put(bi); mutex_unlock(&dynamic_mutex); } diff --git a/drivers/ub/ubus/pool.c b/drivers/ub/ubus/pool.c index 414e9dba0c20..4fdd9aca922d 100644 --- a/drivers/ub/ubus/pool.c +++ b/drivers/ub/ubus/pool.c @@ -533,7 +533,14 @@ static void ub_cfg_cpl_notify_handler(struct ub_bus_controller *ubc, void *msg, if (ret) { dev_err(&ubc->dev, "handle notify bi failed, ret=%d\n", ret); rsp_status = err_to_msg_rsp(ret); + goto rsp; + } + + if (!ub_entity_test_priv_flag(ubc->uent, UB_ENTITY_START)) { + ubc->uent->user_eid = notify->eid[0]; + ub_start_ent(ubc->uent); } + rsp: header->msgetah.rsp_status = rsp_status; ub_cfg_cpl_notify_msg_rsp(ubc, header); diff --git a/drivers/ub/ubus/port.c b/drivers/ub/ubus/port.c index a8a238df8cc4..f2ec6e8b9f47 100644 --- a/drivers/ub/ubus/port.c +++ b/drivers/ub/ubus/port.c @@ -353,10 +353,14 @@ static umode_t ub_port_qdlws_is_visible(struct kobject *kobj, struct attribute *a, int n) { struct ub_port *port = to_ub_port(kobj); + struct ub_entity *uent = port->uent; if (port->type == VIRTUAL) return 0; + if (is_ibus_controller(uent) && uent->ubc->cluster) + return 0; + if (test_bit(UB_PORT_CAP15_QDLWS, port->cap_map)) return a->mode; diff --git a/drivers/ub/ubus/services/gucd.c b/drivers/ub/ubus/services/gucd.c index ca5f0a3578e8..a7796bcce6aa 100644 --- a/drivers/ub/ubus/services/gucd.c +++ b/drivers/ub/ubus/services/gucd.c @@ -71,6 +71,9 @@ static int ub_component_service_register(struct ub_entity *uent) int capabilities; int i; + if (is_ibus_controller(uent) && uent->ubc->cluster) + return 0; + /* Get and check component services */ capabilities = get_component_service_capability(uent); if (!capabilities) @@ -91,36 +94,15 @@ static int ub_component_service_register(struct ub_entity *uent) return 0; } -static void ub_enable_err_msq_ctrl(struct ub_entity *uent) -{ - int ret; - - ret = ub_cfg_write_dword(uent, EMQ_CAP_START + UB_CAP_ERR_MSG_QUE_CTL, - UB_CAP_INTERRUPT_GEN_ENA); - if (ret) - ub_err(uent, "enable error msq controller failed\n"); -} - -static void ub_disable_err_msq_ctrl(struct ub_entity *uent) -{ - int ret; - - ret = ub_cfg_write_dword(uent, EMQ_CAP_START + UB_CAP_ERR_MSG_QUE_CTL, - 0x0); - if (ret) - ub_err(uent, "disable error msq controller failed\n"); -} - static void ub_setup_bus_controller(struct ub_entity *uent) { u32 vec_num_max; int usi_count; - if (ub_cc_supported(uent)) + if (ub_cc_supported(uent) && !uent->ubc->cluster) ub_cc_enable(uent); ub_set_user_info(uent); - ub_enable_err_msq_ctrl(uent); vec_num_max = ub_int_type1_vec_count(uent); usi_count = ub_alloc_irq_vectors(uent, vec_num_max, vec_num_max); if (usi_count < 0) { @@ -143,10 +125,9 @@ static void ub_unset_bus_controller(struct ub_entity *uent) ub_mem_uninit_usi(uent); ub_uninit_decoder_usi(uent); ub_disable_intr(uent); - ub_disable_err_msq_ctrl(uent); ub_unset_user_info(uent); - if (ub_cc_supported(uent)) + if (ub_cc_supported(uent) && !uent->ubc->cluster) ub_cc_disable(uent); } diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index 4fa1a8533e71..b43d682ba3d8 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -421,6 +421,11 @@ void ub_entity_add(struct ub_entity *uent, void *ctx) ret = ub_ports_add(uent); WARN_ON(ret); } + + if (is_ibus_controller(uent)) { + ret = ub_static_bus_instance_init(uent->ubc); + WARN_ON(ret); + } } EXPORT_SYMBOL_GPL(ub_entity_add); @@ -432,11 +437,6 @@ void ub_start_ent(struct ub_entity *uent) if (!uent) return; - if (is_ibus_controller(uent)) { - ret = ub_static_bus_instance_init(uent->ubc); - WARN_ON(ret); - } - ret = ub_default_bus_instance_init(uent); WARN_ON(ret); @@ -507,9 +507,6 @@ void ub_stop_ent(struct ub_entity *uent) ub_remove_sysfs_ent_files(uent); ub_default_bus_instance_uninit(uent); - - if (is_ibus_controller(uent)) - ub_static_bus_instance_uninit(uent->ubc); } EXPORT_SYMBOL_GPL(ub_stop_ent); @@ -527,6 +524,9 @@ void ub_remove_ent(struct ub_entity *uent) list_for_each_entry_safe_reverse(ent, tmp, &uent->mue_list, node) ub_remove_ent(ent); + if (is_ibus_controller(uent)) + ub_static_bus_instance_uninit(uent->ubc); + if (is_primary(uent)) ub_ports_del(uent); @@ -1008,7 +1008,8 @@ int ub_set_user_info(struct ub_entity *uent) u32 eid = uent->ubc->uent->eid; - if (is_p_device(uent)) + if (is_p_device(uent) || + (uent->ubc->cluster && is_ibus_controller(uent))) goto cfg1; /* set dsteid to device */ @@ -1033,7 +1034,8 @@ void ub_unset_user_info(struct ub_entity *uent) if (!uent) return; - if (is_p_device(uent)) + if (is_p_device(uent) || + (uent->ubc->cluster && is_ibus_controller(uent))) goto cfg1; ub_cfg_write_dword(uent, UB_UCNA, 0); diff --git a/drivers/ub/ubus/vendor/hisilicon/eu-table.c b/drivers/ub/ubus/vendor/hisilicon/eu-table.c index 6bbdfb0e0bf7..3004093b52d8 100644 --- a/drivers/ub/ubus/vendor/hisilicon/eu-table.c +++ b/drivers/ub/ubus/vendor/hisilicon/eu-table.c @@ -165,12 +165,18 @@ static const struct file_operations hi_eu_table_info_ops = { static void hi_eu_table_debugfs_init(struct ub_bus_controller *ubc) { + if (ubc->cluster) + return; + debugfs_create_file("eu_table", 0600, ubc->debug_root, ubc, &hi_eu_table_info_ops); } static void hi_eu_table_debugfs_uninit(struct ub_bus_controller *ubc) { + if (ubc->cluster) + return; + debugfs_lookup_and_remove("eu_table", ubc->debug_root); } diff --git a/include/uapi/ub/ubus/ubus_regs.h b/include/uapi/ub/ubus/ubus_regs.h index 9eed901fd205..a4fe600f5459 100644 --- a/include/uapi/ub/ubus/ubus_regs.h +++ b/include/uapi/ub/ubus/ubus_regs.h @@ -271,9 +271,4 @@ enum ub_port_cap_id { #define QDLWS_EXEC_STATUS_MASK GENMASK(2, 0) #define QDLWS_EXEC_STATUS_MAX 4 -/* Error Message Queue Capability */ -#define EMQ_CAP_START 0x00001400 -#define UB_CAP_ERR_MSG_QUE_CTL 0x8 -#define UB_CAP_INTERRUPT_GEN_ENA 0x100 - #endif /* _UAPI_UB_UBUS_UBUS_REGS_H_ */ -- Gitee From 48a4305d274d2349e9b19ab55a15b4e7b0fcded1 Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Tue, 9 Dec 2025 14:31:20 +0800 Subject: [PATCH 111/214] ub:ubus: Matt and MMIO judgments are not performed in cluster ANBZ: #45460 commit 68ce3e1d69c339382cf0b8aa95a7e0957c9ce22d openEuler. Rollback after setting the decoder register fails. Matt and MMIO judgments are not performed in cluster true Fixes: abc591c50df5 ("ub:ubus: Supports decoder event processing") Signed-off-by: Yuhao Xiang Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/decoder.c | 153 +++++++++++++++++++++++++++----------- drivers/ub/ubus/decoder.h | 1 + 2 files changed, 109 insertions(+), 45 deletions(-) diff --git a/drivers/ub/ubus/decoder.c b/drivers/ub/ubus/decoder.c index 288e33a96038..dbd6191b693a 100644 --- a/drivers/ub/ubus/decoder.c +++ b/drivers/ub/ubus/decoder.c @@ -75,28 +75,68 @@ static int ub_decoder_init_queue(struct ub_bus_controller *ubc, static u32 set_mmio_base_reg(struct ub_decoder *decoder) { - u32 ret; + u32 mmio_high = upper_32_bits(decoder->mmio_base_addr); + u32 mmio_low = lower_32_bits(decoder->mmio_base_addr); + struct ub_entity *ent = decoder->uent; + u32 low_bit, high_bit, ret; + + if (!ent->ubc->cluster) { + ret = (u32)ub_cfg_write_dword(ent, DECODER_MMIO_BA0, + 0xffffffff); + ret |= (u32)ub_cfg_write_dword(ent, DECODER_MMIO_BA1, + 0xffffffff); + ret |= (u32)ub_cfg_read_dword(ent, DECODER_MMIO_BA0, &low_bit); + ret |= (u32)ub_cfg_read_dword(ent, DECODER_MMIO_BA1, &high_bit); + if (ret) { + ub_err(ent, "Failed to access decoder MMIO BA\n"); + return ret; + } + + if ((low_bit | mmio_low) != low_bit || + (high_bit | mmio_high) != high_bit) { + ub_err(ent, "decoder MMIO address does not match HW reg\n"); + return -EINVAL; + } + } ret = (u32)ub_cfg_write_dword(decoder->uent, DECODER_MMIO_BA0, lower_32_bits(decoder->mmio_base_addr)); ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_MMIO_BA1, upper_32_bits(decoder->mmio_base_addr)); - if (ret) - ub_err(decoder->uent, "set decoder mmio base failed\n"); return ret; } static u32 set_page_table_reg(struct ub_decoder *decoder) { - u32 ret; + u32 matt_high = upper_32_bits(decoder->pgtlb.pgtlb_dma); + u32 matt_low = lower_32_bits(decoder->pgtlb.pgtlb_dma); + struct ub_entity *ent = decoder->uent; + u32 low_bit, high_bit, ret; + + if (!ent->ubc->cluster) { + ret = (u32)ub_cfg_write_dword(ent, DECODER_MATT_BA0, + 0xffffffff); + ret |= (u32)ub_cfg_write_dword(ent, DECODER_MATT_BA1, + 0xffffffff); + ret |= (u32)ub_cfg_read_dword(ent, DECODER_MATT_BA0, &low_bit); + ret |= (u32)ub_cfg_read_dword(ent, DECODER_MATT_BA1, &high_bit); + if (ret) { + ub_err(ent, "Failed to access decoder MATT BA\n"); + return ret; + } + + if ((low_bit | matt_low) != low_bit || + (high_bit | matt_high) != high_bit) { + ub_err(ent, "decoder MATT address does not match HW reg\n"); + return -EINVAL; + } + } ret = (u32)ub_cfg_write_dword(decoder->uent, DECODER_MATT_BA0, lower_32_bits(decoder->pgtlb.pgtlb_dma)); ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_MATT_BA1, upper_32_bits(decoder->pgtlb.pgtlb_dma)); - if (ret) - ub_err(decoder->uent, "set decoder page table reg failed\n"); return ret; } @@ -145,6 +185,25 @@ static u32 set_queue_reg(struct ub_decoder *decoder) return ret; } +static void unset_queue_reg(struct ub_decoder *decoder) +{ + struct ub_entity *uent = decoder->uent; + u32 ret; + + ret = (u32)ub_cfg_write_dword(uent, DECODER_CMDQ_CFG, + decoder->vals.cmdq_cfg_val); + ret |= (u32)ub_cfg_write_dword(uent, DECODER_EVENTQ_CFG, + decoder->vals.evtq_cfg_val); + + ret |= (u32)ub_cfg_write_dword(uent, DECODER_CMDQ_BASE_ADDR0, 0); + ret |= (u32)ub_cfg_write_dword(uent, DECODER_CMDQ_BASE_ADDR1, 0); + + ret |= (u32)ub_cfg_write_dword(uent, DECODER_EVENTQ_BASE_ADDR0, 0); + ret |= (u32)ub_cfg_write_dword(uent, DECODER_EVENTQ_BASE_ADDR1, 0); + if (ret) + ub_err(uent, "unset queue reg fail\n"); +} + static u32 set_decoder_enable(struct ub_decoder *decoder) { u32 ret = (u32)ub_cfg_write_dword(decoder->uent, DECODER_CTRL, 1); @@ -155,6 +214,14 @@ static u32 set_decoder_enable(struct ub_decoder *decoder) return ret; } +static void unset_decoder_enable(struct ub_decoder *decoder) +{ + struct ub_entity *uent = decoder->uent; + + if (ub_cfg_write_dword(uent, DECODER_CTRL, 0)) + ub_err(uent, "unset decoder enable fail\n"); +} + static u32 ub_decoder_device_set(struct ub_decoder *decoder) { u32 ret; @@ -164,6 +231,11 @@ static u32 ub_decoder_device_set(struct ub_decoder *decoder) ret |= set_queue_reg(decoder); ret |= set_decoder_enable(decoder); + if (ret) { + unset_decoder_enable(decoder); + unset_queue_reg(decoder); + } + return ret; } @@ -186,22 +258,26 @@ static void ub_decoder_free_page_table(struct ub_bus_controller *ubc, ub_err(decoder->uent, "ub bus controller can't free decoder table\n"); } - -static void ub_get_decoder_mmio_base(struct ub_bus_controller *ubc, +static int ub_get_decoder_mmio_base(struct ub_bus_controller *ubc, struct ub_decoder *decoder) { struct resource_entry *entry; - decoder->mmio_base_addr = -1; resource_list_for_each_entry(entry, &ubc->resources) { if (entry->res->flags == IORESOURCE_MEM && - strstr(entry->res->name, "UB_BUS_CTL") && - entry->res->start < decoder->mmio_base_addr) + strstr(entry->res->name, "UB_BUS_CTL")) { decoder->mmio_base_addr = entry->res->start; + decoder->mmio_end_addr = entry->res->end; + break; + } } - ub_info(decoder->uent, "decoder mmio base is %#llx\n", - decoder->mmio_base_addr); + if (decoder->mmio_base_addr == 0) { + ub_err(decoder->uent, "get decoder mmio base failed\n"); + return -EINVAL; + } + + return 0; } static const char * const mmio_size_desc[] = { @@ -209,15 +285,21 @@ static const char * const mmio_size_desc[] = { "2Tbyte", "4Tbyte", "8Tbyte", "16Tbyte" }; +static const u64 mmio_size[] = { + 128ULL * SZ_1G, 256ULL * SZ_1G, 512ULL * SZ_1G, SZ_1T, + 2 * SZ_1T, 4 * SZ_1T, 8 * SZ_1T, 16 * SZ_1T +}; + static int ub_get_decoder_cap(struct ub_decoder *decoder) { struct ub_entity *uent = decoder->uent; + u64 size; u32 val; int ret; ret = ub_cfg_read_dword(uent, DECODER_CAP, &val); if (ret) { - ub_err(uent, "read decoder cap failed\n"); + ub_err(uent, "read decoder cap fail\n"); return ret; } @@ -225,9 +307,15 @@ static int ub_get_decoder_cap(struct ub_decoder *decoder) decoder->cmdq.qs = (val & CMDQ_SIZE_MASK) >> CMDQ_SIZE_OFFSET; decoder->evtq.qs = (val & EVTQ_SIZE_MASK) >> EVTQ_SIZE_OFFSET; - ub_dbg(uent, "cmdq_queue_size=%u, evtq_queue_size=%u, mmio_size=%s\n", - decoder->cmdq.qs, decoder->evtq.qs, - mmio_size_desc[decoder->mmio_size_sup]); + size = decoder->mmio_end_addr - decoder->mmio_base_addr + 1; + if (size > mmio_size[decoder->mmio_size_sup]) + decoder->mmio_end_addr = decoder->mmio_base_addr + + mmio_size[decoder->mmio_size_sup] - 1; + + ub_info(uent, "decoder mmio_addr[%#llx-%#llx], cmdq_queue_size=%u, evtq_queue_size=%u, mmio_size_sup=%s\n", + decoder->mmio_base_addr, decoder->mmio_end_addr, + decoder->cmdq.qs, decoder->evtq.qs, + mmio_size_desc[decoder->mmio_size_sup]); return 0; } @@ -245,7 +333,9 @@ static int ub_create_decoder(struct ub_bus_controller *ubc) decoder->uent = uent; mutex_init(&decoder->table_lock); - ub_get_decoder_mmio_base(ubc, decoder); + ret = ub_get_decoder_mmio_base(ubc, decoder); + if (ret) + goto release_decoder; ret = ub_get_decoder_cap(decoder); if (ret) @@ -293,25 +383,6 @@ static void unset_mmio_base_reg(struct ub_decoder *decoder) ub_err(uent, "unset mmio base reg failed\n"); } -static void unset_queue_reg(struct ub_decoder *decoder) -{ - struct ub_entity *uent = decoder->uent; - u32 ret; - - ret = (u32)ub_cfg_write_dword(uent, DECODER_CMDQ_CFG, - decoder->vals.cmdq_cfg_val); - ret |= (u32)ub_cfg_write_dword(uent, DECODER_EVENTQ_CFG, - decoder->vals.evtq_cfg_val); - - ret |= (u32)ub_cfg_write_dword(uent, DECODER_CMDQ_BASE_ADDR0, 0); - ret |= (u32)ub_cfg_write_dword(uent, DECODER_CMDQ_BASE_ADDR1, 0); - - ret |= (u32)ub_cfg_write_dword(uent, DECODER_EVENTQ_BASE_ADDR0, 0); - ret |= (u32)ub_cfg_write_dword(uent, DECODER_EVENTQ_BASE_ADDR1, 0); - if (ret) - ub_err(uent, "unset queue reg failed\n"); -} - static void unset_page_table_reg(struct ub_decoder *decoder) { struct ub_entity *uent = decoder->uent; @@ -323,14 +394,6 @@ static void unset_page_table_reg(struct ub_decoder *decoder) ub_err(uent, "unset page table reg failed\n"); } -static void unset_decoder_enable(struct ub_decoder *decoder) -{ - struct ub_entity *uent = decoder->uent; - - if (ub_cfg_write_dword(uent, DECODER_CTRL, 0)) - ub_err(uent, "unset decoder enable failed\n"); -} - static void ub_decoder_device_unset(struct ub_decoder *decoder) { unset_decoder_enable(decoder); diff --git a/drivers/ub/ubus/decoder.h b/drivers/ub/ubus/decoder.h index 37d628dc45e2..6667d07e9219 100644 --- a/drivers/ub/ubus/decoder.h +++ b/drivers/ub/ubus/decoder.h @@ -72,6 +72,7 @@ struct ub_decoder { struct device *dev; struct ub_entity *uent; phys_addr_t mmio_base_addr; + phys_addr_t mmio_end_addr; u32 mmio_size_sup; u64 rg_size; struct ub_decoder_queue cmdq; -- Gitee From 3d9838b04bdcb59c27cc66103c77d3ab895bbfd2 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Wed, 10 Dec 2025 20:01:02 +0800 Subject: [PATCH 112/214] ub:ubus: bugfix calltrace of killing qemu when rmmod hisi_ubus ANBZ: #45460 commit 0e8de1af5c2d288c7e6eb2fc702049bf2c3792ab openEuler. bugfix calltrace of killing qemu when rmmod hisi_ubus Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/msg.c | 23 ++--------------------- drivers/ub/ubus/msg.h | 4 ---- drivers/ub/ubus/vendor/hisilicon/msg.c | 11 ----------- 3 files changed, 2 insertions(+), 36 deletions(-) diff --git a/drivers/ub/ubus/msg.c b/drivers/ub/ubus/msg.c index 54f77128ad2f..34b2831bd8bf 100644 --- a/drivers/ub/ubus/msg.c +++ b/drivers/ub/ubus/msg.c @@ -83,39 +83,20 @@ static void dev_message_put(struct ub_entity *uent) int message_probe_device(struct ub_entity *uent) { - const struct message_ops *ops = uent->ubc->mdev->ops; - int ret; - if (!dev_message_get(uent)) return -ENOMEM; - if (uent->message->mdev) - return 0; - - if (ops->probe_dev) { - ret = ops->probe_dev(uent); - if (ret) - goto err_probe; - } - - uent->message->mdev = uent->ubc->mdev; + if (!uent->message->mdev) + uent->message->mdev = uent->ubc->mdev; return 0; - -err_probe: - dev_message_put(uent); - return ret; } void message_remove_device(struct ub_entity *uent) { - const struct message_ops *ops = uent->ubc->mdev->ops; - if (!uent->message) return; - if (ops->remove_dev) - ops->remove_dev(uent); dev_message_put(uent); } diff --git a/drivers/ub/ubus/msg.h b/drivers/ub/ubus/msg.h index 92a126bdf471..f714295fa0e9 100644 --- a/drivers/ub/ubus/msg.h +++ b/drivers/ub/ubus/msg.h @@ -190,8 +190,6 @@ typedef void (*rx_msg_handler_t)(struct ub_bus_controller *ubc, void *pkt, u16 l /** * struct message_ops - message ops and capabilities - * @probe_dev: probe ub_entity to init message - * @remove_dev: remove ub_entity to uninit message * @sync_request: send message to target ub_entity and wait response * @send: send message to target ub_entity but not wait response * @response: send response message to target @@ -200,8 +198,6 @@ typedef void (*rx_msg_handler_t)(struct ub_bus_controller *ubc, void *pkt, u16 l * @owner: Driver module providing these ops */ struct message_ops { - int (*probe_dev)(struct ub_entity *uent); - void (*remove_dev)(struct ub_entity *uent); int (*sync_request)(struct message_device *mdev, struct msg_info *info, u8 code); int (*send)(struct message_device *mdev, struct msg_info *info, diff --git a/drivers/ub/ubus/vendor/hisilicon/msg.c b/drivers/ub/ubus/vendor/hisilicon/msg.c index 178682aa5cd6..5c4e672aa55e 100644 --- a/drivers/ub/ubus/vendor/hisilicon/msg.c +++ b/drivers/ub/ubus/vendor/hisilicon/msg.c @@ -463,15 +463,6 @@ static void hi_msg_queue_uninit(struct hi_message_device *hmd) hi_msg_core_uninit(&hmd->hmc); } -static int hi_message_probe_dev(struct ub_entity *uent) -{ - return 0; -} - -static void hi_message_remove_dev(struct ub_entity *uent) -{ -} - static bool pkt_plen_valid(void *pkt, u16 pkt_size, int task_type) { struct msg_pkt_header *header = (struct msg_pkt_header *)pkt; @@ -638,8 +629,6 @@ int hi_message_private(struct message_device *mdev, struct msg_info *info, } static struct message_ops hi_message_ops = { - .probe_dev = hi_message_probe_dev, - .remove_dev = hi_message_remove_dev, .sync_request = hi_message_sync_request, .response = hi_message_response, .sync_enum = hi_message_sync_enum, -- Gitee From dcc2f72b194feb474beab97da5c4b0bc6729694f Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Wed, 10 Dec 2025 16:27:55 +0800 Subject: [PATCH 113/214] ub:ubus: add hotplug capability check ANBZ: #45460 commit b0aa44c0a884645c7a84a1ad65d01a7a3ab976a8 openEuler. HotPlug capability check added to determine whether power-on and power-off functions are supported. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/services/hotplug/hotplug.h | 1 + .../ub/ubus/services/hotplug/hotplug_core.c | 121 +++++++++++++++++- .../ub/ubus/services/hotplug/hotplug_ctrl.c | 71 +++++----- include/uapi/ub/ubus/ubus_regs.h | 1 + 4 files changed, 154 insertions(+), 40 deletions(-) diff --git a/drivers/ub/ubus/services/hotplug/hotplug.h b/drivers/ub/ubus/services/hotplug/hotplug.h index 93c8e3c798b9..81f731eb29be 100644 --- a/drivers/ub/ubus/services/hotplug/hotplug.h +++ b/drivers/ub/ubus/services/hotplug/hotplug.h @@ -53,6 +53,7 @@ struct ub_slot { #define WORK_LED(slot) ((slot)->slot_cap & UB_SLOT_WLPS) #define PWR_LED(slot) ((slot)->slot_cap & UB_SLOT_PLPS) #define PRESENT(slot) ((slot)->slot_cap & UB_SLOT_PDSS) +#define PWR(slot) ((slot)->slot_cap & UB_SLOT_PWCS) struct ubhp_msg_payload { u16 slot_id; diff --git a/drivers/ub/ubus/services/hotplug/hotplug_core.c b/drivers/ub/ubus/services/hotplug/hotplug_core.c index 157e7b1d4396..55531b6043f3 100644 --- a/drivers/ub/ubus/services/hotplug/hotplug_core.c +++ b/drivers/ub/ubus/services/hotplug/hotplug_core.c @@ -397,6 +397,102 @@ static void ubhp_disconnect_slot(struct ub_slot *slot) slot->r_uent = NULL; } +static void ubhp_clear_port(struct ub_slot *slot) +{ + struct ub_port *port; + + for_each_slot_port(port, slot) { + port->r_index = 0; + guid_copy(&port->r_guid, &guid_null); + } +} + +/** + * ubhp_enum_at_slot() - enum at slot to find new devices + * @slot: the slot that has new device plugged in + * @dev_list: a list to store the new found devices + * + * this func use bfs to enum devices and put them into dev_list, + * which means the previous device in dev_list is enumerated previous + */ +static int ubhp_enum_at_slot(struct ub_slot *slot, struct list_head *dev_list) +{ + void *buf; + int ret; + +#define UB_TOPO_BUF_SZ SZ_4K + buf = kzalloc(UB_TOPO_BUF_SZ, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = ub_enum_topo_scan_ports(slot->uent, slot->port_start, slot->port_num, + dev_list, buf); + if (ret) + ubhp_clear_port(slot); + + kfree(buf); + return ret; +} + +/** + * a simple example for link up + * for a given topo like + * +-------------+ +---------+ +---------+ +--------+ + * | controller0 |p0:---:p0| switch0 |p1:---slot0---:p0| switch1 |p1:---:p0| device0| + * +-------------+ +---------+ +---------+ +--------+ + * when slot0 is calling handle link up + * 1. enum at slot0 to create switch1 and device0, put them in dev_list + * 2. route dev_list to set up route between these two devices + * 3. handle route link up at slot0, add route of left(controller0 & switch0) + * into right(switch1 & device0) and route of right into left + * 4. start switch1 and device0 + */ +static int ubhp_handle_link_up(struct ub_slot *slot) +{ + struct list_head dev_list; + int ret; + + INIT_LIST_HEAD(&dev_list); + + ret = ubhp_enum_at_slot(slot, &dev_list); + if (ret) { + ub_err(slot->uent, "enum at slot%u failed, ret=%d\n", slot->slot_id, ret); + return ret; + } + + if (list_empty(&dev_list)) { + ub_warn(slot->uent, "link up without remote dev\n"); + return -ENXIO; + } + + ret = ub_route_entities(&dev_list); + if (ret) { + ub_err(slot->uent, "hotplug cal route failed, ret=%d\n", ret); + goto err_route; + } + + slot->r_uent = slot->ports->r_uent; + ret = ubhp_update_route_link_up(slot); + if (ret) { + ub_err(slot->uent, "hotplug update route failed, ret=%d\n", ret); + goto err_link_up; + } + + ret = ub_enum_entities_active(&dev_list); + if (ret) { + ub_err(slot->uent, "hotplug start devices failed, ret=%d\n", ret); + goto err_link_up; + } + + return 0; +err_link_up: + ubhp_update_route_link_down(slot); + slot->r_uent = NULL; +err_route: + ub_enum_clear_ent_list(&dev_list); + return ret; +} + /** * a simple example for link down * for a given topo like @@ -471,7 +567,7 @@ static void ubhp_button_handler(struct work_struct *work) void ubhp_handle_power(struct ub_slot *slot, bool power_on) { - if (!slot) + if (!slot || !PWR(slot)) return; mutex_lock(&slot->state_lock); @@ -531,11 +627,22 @@ static void ubhp_handle_present(struct ub_slot *slot) ubhp_set_slot_power(slot, POWER_ON); - mutex_unlock(&slot->state_lock); - ubhp_get_slot(slot); - queue_delayed_work(get_rx_msg_wq(UB_MSG_CODE_LINK), - &slot->power_work, HP_LINK_WAIT_DELAY * HZ); - return; + /* If support power ctrl, wait link up process */ + if (PWR(slot)) { + mutex_unlock(&slot->state_lock); + ubhp_get_slot(slot); + queue_delayed_work(get_rx_msg_wq(UB_MSG_CODE_LINK), + &slot->power_work, HP_LINK_WAIT_DELAY * HZ); + return; + } + + if (ubhp_handle_link_up(slot)) + goto poweroff; + + ubhp_set_indicators(slot, INDICATOR_ON, INDICATOR_NOOP); + slot->state = SLOT_ON; + ub_info(slot->uent, "slot%u on\n", slot->slot_id); + out: /** * why cancel button work here: @@ -559,6 +666,8 @@ static void ubhp_handle_present(struct ub_slot *slot) ub_info(slot->uent, "slot%u handle hotplug succeeded\n", slot->slot_id); return; +poweroff: + ubhp_set_slot_power(slot, POWER_OFF); clear_state: slot->state = SLOT_OFF; ubhp_set_indicators(slot, INDICATOR_OFF, INDICATOR_NOOP); diff --git a/drivers/ub/ubus/services/hotplug/hotplug_ctrl.c b/drivers/ub/ubus/services/hotplug/hotplug_ctrl.c index 28753cc2501c..73d32079bdd8 100644 --- a/drivers/ub/ubus/services/hotplug/hotplug_ctrl.c +++ b/drivers/ub/ubus/services/hotplug/hotplug_ctrl.c @@ -95,13 +95,18 @@ void ubhp_set_indicators(struct ub_slot *slot, u8 power, u8 work) void ubhp_set_slot_power(struct ub_slot *slot, enum power_state power) { - ub_slot_write_byte(slot, UB_SLOT_PW_CTRL, power); + if (PWR(slot)) + ub_slot_write_byte(slot, UB_SLOT_PW_CTRL, power); } bool ubhp_card_present(struct ub_slot *slot) { u8 val; + /* always present if no present ctrl */ + if (!PRESENT(slot)) + return true; + ub_slot_read_byte(slot, UB_SLOT_PD_STA, &val); return !!(val & UB_SLOT_PD_STA_MASK); @@ -153,54 +158,52 @@ bool ubhp_confirm_event(struct ub_slot *slot, enum hotplug_event event) return true; } -static void ubhp_start_slot(struct ub_slot *slot) +static void ubhp_enable(struct ub_slot *slot, u32 pos, u32 mask, bool flag) { u8 val; - /* enable PP */ - ub_slot_read_byte(slot, UB_SLOT_PP_CTRL, &val); - val |= UB_SLOT_PP_CTRL_MASK; - ub_slot_write_byte(slot, UB_SLOT_PP_CTRL, val); + if (!flag) + return; - /* enable PD */ - ub_slot_read_byte(slot, UB_SLOT_PD_CTRL, &val); - val |= UB_SLOT_PD_CTRL_MASK; - ub_slot_write_byte(slot, UB_SLOT_PD_CTRL, val); + ub_slot_read_byte(slot, pos, &val); + val |= mask; + ub_slot_write_byte(slot, pos, val); +} - /* enable PDS */ - ub_slot_read_byte(slot, UB_SLOT_PDS_CTRL, &val); - val |= UB_SLOT_PDS_CTRL_MASK; - ub_slot_write_byte(slot, UB_SLOT_PDS_CTRL, val); +static void ubhp_disable(struct ub_slot *slot, u32 pos, u32 mask, bool flag) +{ + u8 val; + + if (!flag) + return; + + ub_slot_read_byte(slot, pos, &val); + val &= ~mask; + ub_slot_write_byte(slot, pos, val); +} +static void ubhp_start_slot(struct ub_slot *slot) +{ + /* enable PP */ + ubhp_enable(slot, UB_SLOT_PP_CTRL, UB_SLOT_PP_CTRL_MASK, BUTTON(slot)); + /* enable PD */ + ubhp_enable(slot, UB_SLOT_PD_CTRL, UB_SLOT_PD_CTRL_MASK, PRESENT(slot)); + /* enable PDS */ + ubhp_enable(slot, UB_SLOT_PDS_CTRL, UB_SLOT_PDS_CTRL_MASK, PRESENT(slot)); /* enable MS */ - ub_slot_read_byte(slot, UB_SLOT_MS_CTRL, &val); - val |= UB_SLOT_MS_CTRL_MASK; - ub_slot_write_byte(slot, UB_SLOT_MS_CTRL, val); + ubhp_enable(slot, UB_SLOT_MS_CTRL, UB_SLOT_MS_CTRL_MASK, true); } static void ubhp_stop_slot(struct ub_slot *slot) { - u8 val; - /* disable MS */ - ub_slot_read_byte(slot, UB_SLOT_MS_CTRL, &val); - val &= ~UB_SLOT_MS_CTRL_MASK; - ub_slot_write_byte(slot, UB_SLOT_MS_CTRL, val); - + ubhp_disable(slot, UB_SLOT_MS_CTRL, UB_SLOT_MS_CTRL_MASK, true); /* disable PDS */ - ub_slot_read_byte(slot, UB_SLOT_PDS_CTRL, &val); - val &= ~UB_SLOT_PDS_CTRL_MASK; - ub_slot_write_byte(slot, UB_SLOT_PDS_CTRL, val); - + ubhp_disable(slot, UB_SLOT_PDS_CTRL, UB_SLOT_PDS_CTRL_MASK, PRESENT(slot)); /* disable PD */ - ub_slot_read_byte(slot, UB_SLOT_PD_CTRL, &val); - val &= ~UB_SLOT_PD_CTRL_MASK; - ub_slot_write_byte(slot, UB_SLOT_PD_CTRL, val); - + ubhp_disable(slot, UB_SLOT_PD_CTRL, UB_SLOT_PD_CTRL_MASK, PRESENT(slot)); /* disable PP */ - ub_slot_read_byte(slot, UB_SLOT_PP_CTRL, &val); - val &= ~UB_SLOT_PP_CTRL_MASK; - ub_slot_write_byte(slot, UB_SLOT_PP_CTRL, val); + ubhp_disable(slot, UB_SLOT_PP_CTRL, UB_SLOT_PP_CTRL_MASK, BUTTON(slot)); } void ubhp_start_slots(struct ub_entity *uent) diff --git a/include/uapi/ub/ubus/ubus_regs.h b/include/uapi/ub/ubus/ubus_regs.h index a4fe600f5459..47847be68e91 100644 --- a/include/uapi/ub/ubus/ubus_regs.h +++ b/include/uapi/ub/ubus/ubus_regs.h @@ -81,6 +81,7 @@ enum ub_port_cap_id { #define UB_SLOT_WLPS 0x2 #define UB_SLOT_PLPS 0x4 #define UB_SLOT_PDSS 0x8 +#define UB_SLOT_PWCS 0x10 #define UB_SLOT_PORT UB_ADDR_TO_POS(0x3) #define UB_SLOT_START_PORT 0x0000ffff #define UB_SLOT_PP_CTRL UB_ADDR_TO_POS(0x4) -- Gitee From 5cde54a22d5b134bb82078b67423c37240fd9e9c Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Wed, 10 Dec 2025 20:36:11 +0800 Subject: [PATCH 114/214] ub:ubus: bugfix port reset in cluster mode ANBZ: #45460 commit 246a059f3238542f9f9ada5fda550e7f8fb4f78a openEuler. bugfix port reset in cluster mode. Signed-off-by: Yahui Liu Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- Documentation/ub/ubus/hisi_ubus.rst | 2 -- drivers/ub/ubus/port.c | 5 +++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/ub/ubus/hisi_ubus.rst b/Documentation/ub/ubus/hisi_ubus.rst index b384b058129f..90e76d2e587f 100644 --- a/Documentation/ub/ubus/hisi_ubus.rst +++ b/Documentation/ub/ubus/hisi_ubus.rst @@ -61,8 +61,6 @@ UB Message Core Driver Hisi UBUS implements a message device that provides a set of operations:: static struct message_ops hi_message_ops = { - .probe_dev = hi_message_probe_dev, - .remove_dev = hi_message_remove_dev, .sync_request = hi_message_sync_request, .response = hi_message_response, .sync_enum = hi_message_sync_enum, diff --git a/drivers/ub/ubus/port.c b/drivers/ub/ubus/port.c index f2ec6e8b9f47..f4d91d0e9d99 100644 --- a/drivers/ub/ubus/port.c +++ b/drivers/ub/ubus/port.c @@ -162,6 +162,11 @@ static ssize_t port_reset_store(struct ub_port *port, const char *buf, return -EINVAL; } + if (port->uent->ubc->cluster) { + ub_err(port->uent, "Port reset is not supported by sysfs in cluster mode\n"); + return -EINVAL; + } + ret = ub_port_reset_function(port); if (ret < 0) return ret; -- Gitee From 347abe5aea35594ebd5ea220c38e433a109f6af7 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Wed, 10 Dec 2025 20:24:13 +0800 Subject: [PATCH 115/214] ub:hisi-ubus: fix MUE unreg msg rsp time ANBZ: #45460 commit f0942c09a5cf0eb52de419aeaf1067d7798fd60e openEuler. 1. During MUE deregistration processing, first send a response to the control plane, then proceed with the device destruction process. 2. Add entity number to the uent release log. Fixes: 86fec00cb73a ("ub:hisi-ubus: Support UBUS vdm entity enable message") Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/ubus_entity.c | 4 +++- drivers/ub/ubus/vendor/hisilicon/vdm.c | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index b43d682ba3d8..105c6f396b44 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -463,6 +463,7 @@ EXPORT_SYMBOL_GPL(ub_start_ent); static void ub_release_ent(struct device *dev) { struct ub_entity *uent; + u32 uent_num; uent = to_ub_entity(dev); if (is_primary(uent) && !is_p_device(uent)) { @@ -480,8 +481,9 @@ static void ub_release_ent(struct device *dev) kfree(uent->driver_override); uent->token_value = 0; + uent_num = uent->uent_num; kfree(uent); - pr_info("uent release\n"); + pr_info("uent[%#x] release\n", uent_num); } void ub_stop_ent(struct ub_entity *uent) diff --git a/drivers/ub/ubus/vendor/hisilicon/vdm.c b/drivers/ub/ubus/vendor/hisilicon/vdm.c index 4a19e0fb8d57..329bc51b5a0e 100644 --- a/drivers/ub/ubus/vendor/hisilicon/vdm.c +++ b/drivers/ub/ubus/vendor/hisilicon/vdm.c @@ -256,10 +256,11 @@ static u8 ub_idevice_pue_rls_handler(struct ub_bus_controller *ubc, struct vdm_m status = UB_MSG_RSP_SUCCESS; } + ub_vdm_msg_rsp(ubc, pkt, status); + if (status == UB_MSG_RSP_SUCCESS) ub_disable_ent(uent); - ub_vdm_msg_rsp(ubc, pkt, status); return status; } -- Gitee From 2bc42f4f8395ce042ae03295ee61aef198bda4c9 Mon Sep 17 00:00:00 2001 From: Jianquan-Lin Date: Tue, 16 Dec 2025 09:58:16 +0800 Subject: [PATCH 116/214] ub:ubus: Change create device irq domain debug info ANBZ: #45460 commit 9147e429cddb371664aeb08f8ed5af680cb6054a openEuler. Change create device irq domain debug info. Fixes: 81962d0ecc6e ("ub:ubus: Support UBUS Interrupt framework") Signed-off-by: Junlong Zheng Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/msi/irqdomain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ub/ubus/msi/irqdomain.c b/drivers/ub/ubus/msi/irqdomain.c index 5a01c40368bc..83da28e03fc7 100644 --- a/drivers/ub/ubus/msi/irqdomain.c +++ b/drivers/ub/ubus/msi/irqdomain.c @@ -95,7 +95,7 @@ static bool ub_create_device_domain(struct ub_entity *uent, return true; if (WARN_ON_ONCE(1)) - pr_info("TODO: create device irq domain.\n"); + pr_err("Create device irq domain failed.\n"); return false; } -- Gitee From 8bea9ed7293608410052dbe4b0e14565a419c533 Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Wed, 10 Dec 2025 10:09:59 +0800 Subject: [PATCH 117/214] ub:ubus: hi_msg_sync_wait first pull cq ANBZ: #45460 commit b20a84c09032b1d42389fccfbb2abfd170dc83f1 openEuler. In cluster mode, ub_get_ue_by_entity_idx should not check entity idx. Currently hi_msg_sync_wait() first check msg is not timeout, then pull the cq, which can be misjudged because of cpu not getting dispatch for long time but actually the msg cq already arrived. Change the order usingdo {} while() so that driver will pull cq at least once. Fixes: 0f9e2dbe888d ("ub:ubus: Support for enabling and disabling ue") Signed-off-by: Yahui Liu Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/ubus_entity.c | 3 --- drivers/ub/ubus/vendor/hisilicon/msg.c | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index 105c6f396b44..3dbc196d4da4 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -1057,9 +1057,6 @@ static struct ub_entity *ub_get_ue_by_entity_idx(struct ub_entity *pue, u32 enti { struct ub_entity *ue; - if (ub_check_ue_para(pue, entity_idx)) - return NULL; - list_for_each_entry(ue, &pue->ue_list, node) { if (ue->entity_idx == entity_idx) return ue; diff --git a/drivers/ub/ubus/vendor/hisilicon/msg.c b/drivers/ub/ubus/vendor/hisilicon/msg.c index 5c4e672aa55e..6d4e9d422c49 100644 --- a/drivers/ub/ubus/vendor/hisilicon/msg.c +++ b/drivers/ub/ubus/vendor/hisilicon/msg.c @@ -249,14 +249,14 @@ static int hi_msg_sync_wait(struct hi_message_device *hmd, int task_type, unsigned long flags; int idx; - while (!time_after64(get_jiffies_64(), end_time)) { + do { idx = hi_msg_cq_poll(hmc, task_type, msn); if (idx >= 0) return idx; if (flag) usleep_range(SLEEP_MIN_US, SLEEP_MAX_US); - } + } while (!time_after64(get_jiffies_64(), end_time)); timeout_msg = kzalloc(TIMEOUT_MSG_INFO_SZ, GFP_ATOMIC); if (!timeout_msg) -- Gitee From 1a309d41d1a43067edf7f5da08904efe9ff56092 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Tue, 23 Dec 2025 21:47:29 +0800 Subject: [PATCH 118/214] ub:ubus delete undefined class code ANBZ: #45460 commit a973d58a2eda583deb0b17287db58836aea11c3e openEuler. Delete undefined class code macro. Fixes: e42bc0097589 ("ub:ubus: Support for ub bus driver framework") Signed-off-by: Junlong Zheng Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- include/ub/ubus/ubus_ids.h | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/include/ub/ubus/ubus_ids.h b/include/ub/ubus/ubus_ids.h index 5e5158ba5527..62ead65e5061 100644 --- a/include/ub/ubus/ubus_ids.h +++ b/include/ub/ubus/ubus_ids.h @@ -19,29 +19,16 @@ #define UB_BASE_CODE_STORAGE 0x01 #define UB_CLASS_STORAGE_LPC 0x0001 #define UB_CLASS_STORAGE_LBC 0x0101 -#define UB_CLASS_STORAGE_RAID 0x0201 #define UB_BASE_CODE_NETWORK 0x02 #define UB_CLASS_NETWORK_UB 0x0002 #define UB_CLASS_NETWORK_ETH 0x0102 -#define UB_BASE_CODE_DISPLAY 0x03 +#define UB_BASE_CODE_SWITCH 0x03 +#define UB_CLASS_SWITCH_UB 0x0003 -#define UB_BASE_CODE_SWITCH 0x04 -#define UB_CLASS_SWITCH_UB 0x0004 - -#define UB_BASE_CODE_VIRTIO 0x05 -#define UB_CLASS_LEGACY_VIRTIO_NETWORK 0x0005 -#define UB_CLASS_LEGACY_VIRTIO_BLOCK 0x0105 -#define UB_CLASS_LEGACY_VIRTIO_SCSI 0x0205 -#define UB_CLASS_LEGACY_VIRTIO_GRAPHIC 0x0305 -#define UB_CLASS_LEGACY_VIRTIO_SOCKET 0x0405 -#define UB_CLASS_LEGACY_VIRTIO_FS 0x0505 - -#define UB_BASE_CODE_VIRTUAL 0x06 - -#define UB_BASE_CODE_NPU 0x07 -#define UB_CLASS_NPU_UB 0x0007 +#define UB_BASE_CODE_NPU 0x04 +#define UB_CLASS_NPU_UB 0x0004 #define UB_BASE_CODE_UNKNOWN 0xFF #define UB_CLASS_UNKNOWN 0x00FF -- Gitee From a2e6e67be5e4ef9da2f5429a81ba1c829ac35c83 Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Tue, 9 Dec 2025 14:30:33 +0800 Subject: [PATCH 119/214] ub:ubus: Move the decoder's queue operations to hisi-ubus ANBZ: #45460 commit 1038b7a186d8bbf3cc9d15fd16d1698a1eeff075 openEuler. Move the decoder's queue operations to hisi-ubus Fixes: abc591c50df5 ("ub:ubus: Supports decoder event processing") Signed-off-by: Yuhao Xiang Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/decoder.c | 421 ++---------------- drivers/ub/ubus/decoder.h | 2 - drivers/ub/ubus/ubus_controller.h | 5 +- drivers/ub/ubus/vendor/hisilicon/controller.c | 4 +- .../ub/ubus/vendor/hisilicon/hisi-decoder.c | 394 +++++++++++++++- .../ub/ubus/vendor/hisilicon/hisi-decoder.h | 8 +- 6 files changed, 428 insertions(+), 406 deletions(-) diff --git a/drivers/ub/ubus/decoder.c b/drivers/ub/ubus/decoder.c index dbd6191b693a..0a2a06114429 100644 --- a/drivers/ub/ubus/decoder.c +++ b/drivers/ub/ubus/decoder.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include "ubus.h" @@ -25,52 +24,28 @@ #define CMDQ_SIZE_USE_MASK GENMASK(11, 8) #define CMDQ_SIZE_USE_OFFSET 8 #define CMDQ_ENABLE 0x1 -#define CMD_ENTRY_SIZE 16 #define EVTQ_SIZE_USE_MASK GENMASK(11, 8) #define EVTQ_SIZE_USE_OFFSET 8 #define EVTQ_ENABLE 0x1 -#define EVT_ENTRY_SIZE 16 -#define DECODER_QUEUE_TIMEOUT_US 1000000 /* 1s */ - -static void ub_decoder_uninit_queue(struct ub_decoder *decoder) +static void ub_decoder_uninit_queue(struct ub_bus_controller *ubc, + struct ub_decoder *decoder) { - iounmap(decoder->cmdq.qbase); - iounmap(decoder->evtq.qbase); + if (ubc->ops->uninit_decoder_queue) + ubc->ops->uninit_decoder_queue(decoder); + else + ub_err(ubc->uent, "ub bus controller can't uninit decoder queue\n"); } static int ub_decoder_init_queue(struct ub_bus_controller *ubc, struct ub_decoder *decoder) { - struct ub_entity *uent = ubc->uent; + if (ubc->ops->init_decoder_queue && ubc->ops->uninit_decoder_queue) + return ubc->ops->init_decoder_queue(decoder); - if (ubc->ops->register_decoder_base_addr) { - ubc->ops->register_decoder_base_addr(ubc, &decoder->cmdq.base, - &decoder->evtq.base); - } else { - ub_err(uent, - "ub_bus_controller_ops does not provide register_decoder_base_addr func, exit\n"); - return -EINVAL; - } - - if (decoder->cmdq.qs == 0 || decoder->evtq.qs == 0) { - ub_err(uent, "decoder cmdq or evtq qs is 0\n"); - return -EINVAL; - } - - decoder->cmdq.qbase = ioremap(decoder->cmdq.base, - (1 << decoder->cmdq.qs) * CMD_ENTRY_SIZE); - if (!decoder->cmdq.qbase) - return -ENOMEM; - - decoder->evtq.qbase = ioremap(decoder->evtq.base, - (1 << decoder->evtq.qs) * EVT_ENTRY_SIZE); - if (!decoder->evtq.qbase) { - iounmap(decoder->cmdq.qbase); - return -ENOMEM; - } - return 0; + ub_err(ubc->uent, "ub bus controller can't init decoder queue\n"); + return -EINVAL; } static u32 set_mmio_base_reg(struct ub_decoder *decoder) @@ -242,11 +217,11 @@ static u32 ub_decoder_device_set(struct ub_decoder *decoder) static int ub_decoder_create_page_table(struct ub_bus_controller *ubc, struct ub_decoder *decoder) { - if (ubc->ops->create_decoder_table) + if (ubc->ops->create_decoder_table && ubc->ops->free_decoder_table) return ubc->ops->create_decoder_table(decoder); ub_err(decoder->uent, "ub bus controller can't create decoder table\n"); - return -EPERM; + return -EINVAL; } static void ub_decoder_free_page_table(struct ub_bus_controller *ubc, @@ -307,6 +282,11 @@ static int ub_get_decoder_cap(struct ub_decoder *decoder) decoder->cmdq.qs = (val & CMDQ_SIZE_MASK) >> CMDQ_SIZE_OFFSET; decoder->evtq.qs = (val & EVTQ_SIZE_MASK) >> EVTQ_SIZE_OFFSET; + if (decoder->cmdq.qs == 0 || decoder->evtq.qs == 0) { + ub_err(uent, "decoder cmdq or evtq qs is 0\n"); + return -EINVAL; + } + size = decoder->mmio_end_addr - decoder->mmio_base_addr + 1; if (size > mmio_size[decoder->mmio_size_sup]) decoder->mmio_end_addr = decoder->mmio_base_addr + @@ -366,7 +346,7 @@ static int ub_create_decoder(struct ub_bus_controller *ubc) release_page_table: ub_decoder_free_page_table(ubc, decoder); release_queue: - ub_decoder_uninit_queue(decoder); + ub_decoder_uninit_queue(ubc, decoder); release_decoder: kfree(decoder); return ret; @@ -415,374 +395,29 @@ static void ub_remove_decoder(struct ub_bus_controller *ubc) ub_decoder_free_page_table(ubc, decoder); - ub_decoder_uninit_queue(decoder); + ub_decoder_uninit_queue(ubc, decoder); kfree(decoder); ubc->decoder = NULL; } -struct sync_entry { - u64 op : 8; - u64 reserve0 : 4; - u64 cm : 2; - u64 ntf_sh : 2; - u64 ntf_attr : 4; - u64 reserve1 : 12; - u64 notify_data : 32; - u64 reserve2 : 2; - u64 notify_addr : 50; - u64 reserve3 : 12; -}; - -struct tlbi_all_entry { - u32 op : 8; - u32 reserve0 : 24; - u32 reserve1; - u32 reserve2; - u32 reserve3; -}; - -struct tlbi_partial_entry { - u32 op : 8; - u32 reserve0 : 24; - u32 tlbi_addr_base : 28; - u32 reserve1 : 4; - u32 tlbi_addr_limt : 28; - u32 reserve2 : 4; - u32 reserve3; -}; - -#define TLBI_ADDR_MASK GENMASK_ULL(43, 20) -#define TLBI_ADDR_OFFSET 20 -#define CMDQ_ENT_DWORDS 2 - -#define NTF_SH_NSH 0b00 -#define NTF_SH_OSH 0b10 -#define NTF_SH_ISH 0b11 -#define NTF_ATTR_IR_NC 0b00 -#define NTF_ATTR_IR_WBRA 0b01 -#define NTF_ATTR_IR_WT 0b10 -#define NTF_ATTR_IR_WB 0b11 -#define NTF_ATTR_OR_NC 0b0000 -#define NTF_ATTR_OR_WBRA 0b0100 -#define NTF_ATTR_OR_WT 0b1000 -#define NTF_ATTR_OR_WB 0b1100 - -#define Q_IDX(qs, p) ((p) & ((1 << (qs)) - 1)) -#define Q_WRP(qs, p) ((p) & (1 << (qs))) -#define Q_OVF(p) ((p) & Q_OVERFLOW_FLAG) - -enum NOTIFY_TYPE { - DISABLE_NOTIFY = 0, - ENABLE_NOTIFY = 1, -}; - -static bool queue_has_space(struct ub_decoder_queue *q, u32 n) -{ - u32 space, prod, cons; - - prod = Q_IDX(q->qs, q->prod.cmdq_wr_idx); - cons = Q_IDX(q->qs, q->cons.cmdq_rd_idx); - - if (Q_WRP(q->qs, q->prod.cmdq_wr_idx) == - Q_WRP(q->qs, q->cons.cmdq_rd_idx)) - space = (1 << q->qs) - (prod - cons); - else - space = cons - prod; - - return space >= n; -} - -static u32 queue_inc_prod_n(struct ub_decoder_queue *q, u32 n) -{ - u32 prod = (Q_WRP(q->qs, q->prod.cmdq_wr_idx) | - Q_IDX(q->qs, q->prod.cmdq_wr_idx)) + n; - return Q_WRP(q->qs, prod) | Q_IDX(q->qs, prod); -} - -#define CMD_0_OP GENMASK_ULL(7, 0) -#define CMD_0_ADDR_BASE GENMASK_ULL(59, 32) -#define CMD_1_ADDR_LIMT GENMASK_ULL(27, 0) - -static void decoder_cmdq_issue_cmd(struct ub_decoder *decoder, phys_addr_t addr, - u64 size, enum ub_cmd_op_type op) -{ - struct ub_decoder_queue *cmdq = &(decoder->cmdq); - struct tlbi_partial_entry entry = {}; - u64 cmd[CMDQ_ENT_DWORDS] = {}; - void *pi; - int i; - - entry.op = op; - entry.tlbi_addr_base = (addr & TLBI_ADDR_MASK) >> TLBI_ADDR_OFFSET; - entry.tlbi_addr_limt = ((addr + size - 1U) & TLBI_ADDR_MASK) >> - TLBI_ADDR_OFFSET; - - cmd[0] |= FIELD_PREP(CMD_0_OP, entry.op); - cmd[0] |= FIELD_PREP(CMD_0_ADDR_BASE, entry.tlbi_addr_base); - cmd[1] |= FIELD_PREP(CMD_1_ADDR_LIMT, entry.tlbi_addr_limt); - - pi = cmdq->qbase + Q_IDX(cmdq->qs, cmdq->prod.cmdq_wr_idx) * - sizeof(struct tlbi_partial_entry); - - for (i = 0; i < CMDQ_ENT_DWORDS; i++) - writeq(cmd[i], pi + i * sizeof(u64)); - - cmdq->prod.cmdq_wr_idx = queue_inc_prod_n(cmdq, 1); -} - -#define NTF_DMA_ADDR_OFFSERT 2 -#define SYNC_0_OP GENMASK_ULL(7, 0) -#define SYNC_0_CM GENMASK_ULL(13, 12) -#define SYNC_0_NTF_ISH GENMASK_ULL(15, 14) -#define SYNC_0_NTF_ATTR GENMASK_ULL(19, 16) -#define SYNC_0_NTF_DATA GENMASK_ULL(63, 32) -#define SYNC_1_NTF_ADDR GENMASK_ULL(51, 2) -#define SYNC_NTF_DATA 0xffffffff - -static void decoder_cmdq_issue_sync(struct ub_decoder *decoder) -{ - struct ub_decoder_queue *cmdq = &(decoder->cmdq); - u64 cmd[CMDQ_ENT_DWORDS] = {}; - struct sync_entry entry = {}; - phys_addr_t sync_dma; - void __iomem *pi; - int i; - - entry.op = SYNC; - entry.cm = ENABLE_NOTIFY; - sync_dma = cmdq->base + Q_IDX(cmdq->qs, cmdq->prod.cmdq_wr_idx) * - sizeof(struct sync_entry); - entry.ntf_sh = NTF_SH_NSH; - entry.ntf_attr = NTF_ATTR_IR_NC | NTF_ATTR_OR_NC; - entry.notify_data = SYNC_NTF_DATA; - entry.notify_addr = sync_dma >> NTF_DMA_ADDR_OFFSERT; - - cmd[0] |= FIELD_PREP(SYNC_0_OP, entry.op); - cmd[0] |= FIELD_PREP(SYNC_0_CM, entry.cm); - cmd[0] |= FIELD_PREP(SYNC_0_NTF_ISH, entry.ntf_sh); - cmd[0] |= FIELD_PREP(SYNC_0_NTF_ATTR, entry.ntf_attr); - cmd[0] |= FIELD_PREP(SYNC_0_NTF_DATA, entry.notify_data); - cmd[1] |= FIELD_PREP(SYNC_1_NTF_ADDR, entry.notify_addr); - - pi = cmdq->qbase + Q_IDX(cmdq->qs, cmdq->prod.cmdq_wr_idx) * - sizeof(struct sync_entry); - for (i = 0; i < CMDQ_ENT_DWORDS; i++) - writeq(cmd[i], pi + i * sizeof(u64)); - - decoder->notify = pi; - cmdq->prod.cmdq_wr_idx = queue_inc_prod_n(cmdq, 1); -} - -static void decoder_cmdq_update_prod(struct ub_decoder *decoder) -{ - struct ub_entity *uent = decoder->uent; - struct queue_idx q; - int ret; - - ret = ub_cfg_read_dword(uent, DECODER_CMDQ_PROD, &q.val); - if (ret) - ub_err(uent, "update pi, read decoder cmdq prod failed\n"); - - decoder->cmdq.prod.cmdq_err_resp = q.cmdq_err_resp; - ret = ub_cfg_write_dword(uent, DECODER_CMDQ_PROD, - decoder->cmdq.prod.val); - if (ret) - ub_err(uent, "update pi, write decoder cmdq prod failed\n"); -} - -static int wait_for_cmdq_free(struct ub_decoder *decoder, u32 n) -{ - ktime_t timeout = ktime_add_us(ktime_get(), DECODER_QUEUE_TIMEOUT_US); - struct ub_decoder_queue *cmdq = &(decoder->cmdq); - struct ub_entity *uent = decoder->uent; - int ret; - - while (true) { - ret = ub_cfg_read_dword(uent, DECODER_CMDQ_CONS, - &(cmdq->cons.val)); - if (ret) - return ret; - - if (queue_has_space(cmdq, n + 1)) - return 0; - - if (ktime_compare(ktime_get(), timeout) > 0) { - ub_err(uent, "decoder cmdq wait free entry timeout\n"); - return -ETIMEDOUT; - } - cpu_relax(); - } -} - -static int wait_for_cmdq_notify(struct ub_decoder *decoder) -{ - ktime_t timeout; - u32 val; - - timeout = ktime_add_us(ktime_get(), DECODER_QUEUE_TIMEOUT_US); - while (true) { - val = readl(decoder->notify); - if (val == SYNC_NTF_DATA) - return 0; - - if (ktime_compare(ktime_get(), timeout) > 0) { - ub_err(decoder->uent, "decoder cmdq wait notify timeout\n"); - return -ETIMEDOUT; - } - cpu_relax(); - } -} - -int ub_decoder_cmd_request(struct ub_decoder *decoder, phys_addr_t addr, - u64 size, enum ub_cmd_op_type op) -{ - int ret; - - ret = wait_for_cmdq_free(decoder, 1); - if (ret) - return ret; - - decoder_cmdq_issue_cmd(decoder, addr, size, op); - decoder_cmdq_issue_sync(decoder); - decoder_cmdq_update_prod(decoder); - - ret = wait_for_cmdq_notify(decoder); - return ret; -} -EXPORT_SYMBOL_GPL(ub_decoder_cmd_request); - -static bool queue_empty(struct ub_decoder_queue *q) -{ - return (Q_IDX(q->qs, q->prod.eventq_wr_idx) == - Q_IDX(q->qs, q->cons.eventq_rd_idx)) && - (Q_WRP(q->qs, q->prod.eventq_wr_idx) == - Q_WRP(q->qs, q->cons.eventq_rd_idx)); -} - -static void queue_inc_cons(struct ub_decoder_queue *q) -{ - u32 cons = (Q_WRP(q->qs, q->cons.eventq_rd_idx) | - Q_IDX(q->qs, q->cons.eventq_rd_idx)) + 1; - q->cons.eventq_rd_idx = Q_WRP(q->qs, cons) | Q_IDX(q->qs, cons); -} - -enum event_op_type { - RESERVED = 0x00, - EVENT_ADDR_OUT_OF_RANGE = 0x01, - EVENT_ILLEGAL_CMD = 0x02, -}; - -#define EVTQ_0_ID GENMASK_ULL(7, 0) -#define EVTQ_0_ADDR GENMASK_ULL(59, 32) -#define EVTQ_0_CMD_OPCODE GENMASK_ULL(39, 32) -#define EVTQ_ENT_DWORDS 2 -#define MAX_REASON_NUM 3 - -static const char * const cmd_err_reason[MAX_REASON_NUM] = { - "no error", - "illegal command", - "abort error(read command with 2bit ecc)" -}; - -static void fix_err_cmd(struct ub_decoder *decoder) -{ - struct ub_decoder_queue *cmdq = &(decoder->cmdq); - struct ub_entity *uent = decoder->uent; - u64 cmd[CMDQ_ENT_DWORDS] = {}; - struct queue_idx prod, cons; - void *pi; - int i; - - if (ub_cfg_read_dword(uent, DECODER_CMDQ_CONS, &cons.val)) { - ub_err(uent, "decoder fix error cmd, read ci failed\n"); - return; - } - if (ub_cfg_read_dword(uent, DECODER_CMDQ_PROD, &prod.val)) { - ub_err(uent, "decoder fix error cmd, read pi failed\n"); - return; - } - - cmd[0] |= FIELD_PREP(CMD_0_OP, TLBI_ALL); - pi = cmdq->qbase + Q_IDX(cmdq->qs, cons.cmdq_rd_idx) * - sizeof(struct tlbi_partial_entry); - - for (i = 0; i < CMDQ_ENT_DWORDS; i++) - writeq(cmd[i], pi + i * sizeof(u64)); - - if (cons.cmdq_err_reason >= MAX_REASON_NUM) - ub_err(uent, "cmdq err reason is invalid, reason=%u\n", - cons.cmdq_err_reason); - else - ub_err(uent, "cmdq err reason is %s\n", cmd_err_reason[cons.cmdq_err_reason]); - - prod.cmdq_err_resp = cons.cmdq_err; - - if (ub_cfg_write_dword(uent, DECODER_CMDQ_PROD, prod.val)) - ub_err(uent, "decoder fix error cmd, write pi err resp failed\n"); -} - -static void handle_evt(struct ub_decoder *decoder, u64 *evt) -{ - struct ub_entity *uent = decoder->uent; - - switch (FIELD_GET(EVTQ_0_ID, evt[0])) { - case EVENT_ADDR_OUT_OF_RANGE: - ub_err(uent, "decoder event, input addr out of range, addr=%#.7x00000\n", - (u32)FIELD_GET(EVTQ_0_ADDR, evt[0])); - break; - case EVENT_ILLEGAL_CMD: - ub_err(uent, "decoder event, illegal cmd, cmd_opcode=%#x\n", - (u32)FIELD_GET(EVTQ_0_CMD_OPCODE, evt[0])); - fix_err_cmd(decoder); - break; - default: - ub_err(uent, "invalid event opcode, opcode=%#x\n", - (u32)FIELD_GET(EVTQ_0_ID, evt[0])); - } -} - -static void decoder_event_deal(struct ub_decoder *decoder) -{ - struct ub_decoder_queue *evtq = &decoder->evtq; - struct ub_entity *uent = decoder->uent; - u64 evt[EVTQ_ENT_DWORDS]; - void *ci; - int i; - - if (ub_cfg_read_dword(uent, DECODER_EVENTQ_PROD, &(evtq->prod.val))) { - ub_err(uent, "decoder handle event, read eventq pi failed\n"); - return; - } - - while (!queue_empty(evtq)) { - ci = evtq->qbase + Q_IDX(evtq->qs, evtq->cons.eventq_rd_idx) * - EVT_ENTRY_SIZE; - - for (i = 0; i < EVTQ_ENT_DWORDS; i++) - evt[i] = readq(ci + i * sizeof(u64)); - - handle_evt(decoder, evt); - queue_inc_cons(evtq); - - if (ub_cfg_write_dword(uent, DECODER_EVENTQ_CONS, - evtq->cons.val)) - ub_err(uent, "decoder handle event, write eventq ci failed\n"); - } -} static irqreturn_t decoder_event_deal_handle(int irq, void *data) { struct ub_entity *uent = (struct ub_entity *)data; struct ub_decoder *decoder = uent->ubc->decoder; - if (!decoder) { ub_err(uent, "decoder does not exist\n"); - return IRQ_HANDLED; + return IRQ_NONE; + } + + if (!uent->ubc->ops->decoder_event_deal) { + ub_err(uent, "decoder event deal does not exist\n"); + return IRQ_NONE; } - decoder_event_deal(decoder); + uent->ubc->ops->decoder_event_deal(decoder); return IRQ_HANDLED; } @@ -884,8 +519,8 @@ int ub_decoder_map(struct ub_decoder *decoder, struct decoder_map_info *info) } ubc = decoder->uent->ubc; - if (!ubc->ops->decoder_map) { - pr_err("decoder_map ops not exist\n"); + if (!ubc->ops->decoder_map && !ubc->ops->decoder_unmap) { + pr_err("decoder_map or decoder_unmap ops not exist\n"); return -EINVAL; } diff --git a/drivers/ub/ubus/decoder.h b/drivers/ub/ubus/decoder.h index 6667d07e9219..48ffe9102a46 100644 --- a/drivers/ub/ubus/decoder.h +++ b/drivers/ub/ubus/decoder.h @@ -107,8 +107,6 @@ void ub_decoder_init(struct ub_entity *uent); void ub_decoder_uninit(struct ub_entity *uent); void ub_init_decoder_usi(struct ub_entity *uent); void ub_uninit_decoder_usi(struct ub_entity *uent); -int ub_decoder_cmd_request(struct ub_decoder *decoder, phys_addr_t addr, - u64 size, enum ub_cmd_op_type op); int ub_decoder_map(struct ub_decoder *decoder, struct decoder_map_info *info); int ub_decoder_unmap(struct ub_decoder *decoder, phys_addr_t addr, u64 size); #endif /* __DECODER_H__ */ diff --git a/drivers/ub/ubus/ubus_controller.h b/drivers/ub/ubus/ubus_controller.h index 03005965f24f..9ead5a1bae5a 100644 --- a/drivers/ub/ubus/ubus_controller.h +++ b/drivers/ub/ubus/ubus_controller.h @@ -18,8 +18,8 @@ struct ub_bus_controller_ops { void (*mem_decoder_remove)(struct ub_bus_controller *ubc); void (*register_ubmem_irq)(struct ub_bus_controller *ubc); void (*unregister_ubmem_irq)(struct ub_bus_controller *ubc); - void (*register_decoder_base_addr)(struct ub_bus_controller *ubc, - u64 *cmd_queue, u64 *event_queue); + int (*init_decoder_queue)(struct ub_decoder *decoder); + void (*uninit_decoder_queue)(struct ub_decoder *decoder); int (*entity_enable)(struct ub_entity *uent, u8 enable); int (*create_decoder_table)(struct ub_decoder *decoder); void (*free_decoder_table)(struct ub_decoder *decoder); @@ -27,6 +27,7 @@ struct ub_bus_controller_ops { struct decoder_map_info *info); int (*decoder_unmap)(struct ub_decoder *decoder, phys_addr_t addr, u64 size); + void (*decoder_event_deal)(struct ub_decoder *decoder); CK_KABI_RESERVE(1) CK_KABI_RESERVE(2) diff --git a/drivers/ub/ubus/vendor/hisilicon/controller.c b/drivers/ub/ubus/vendor/hisilicon/controller.c index 6c9c8e320479..b9a4e6dc02d0 100644 --- a/drivers/ub/ubus/vendor/hisilicon/controller.c +++ b/drivers/ub/ubus/vendor/hisilicon/controller.c @@ -22,12 +22,14 @@ static struct ub_bus_controller_ops hi_ubc_ops = { .mem_decoder_remove = hi_mem_decoder_remove, .register_ubmem_irq = hi_register_ubmem_irq, .unregister_ubmem_irq = hi_unregister_ubmem_irq, - .register_decoder_base_addr = hi_register_decoder_base_addr, + .init_decoder_queue = hi_init_decoder_queue, + .uninit_decoder_queue = hi_uninit_decoder_queue, .entity_enable = hi_send_entity_enable_msg, .create_decoder_table = hi_create_decoder_table, .free_decoder_table = hi_free_decoder_table, .decoder_map = hi_decoder_map, .decoder_unmap = hi_decoder_unmap, + .decoder_event_deal = hi_decoder_event_deal, }; static void ub_bus_controller_debugfs_init(struct ub_bus_controller *ubc) diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.c b/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.c index ac1fa0498ffc..00f958696b66 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.c +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.c @@ -6,6 +6,7 @@ #define pr_fmt(fmt) "ubus hisi decoder: " fmt #include +#include #include #include "../../ubus.h" #include "hisi-ubus.h" @@ -136,6 +137,10 @@ struct range_table_entry { DECODER_SUB_PAGE_TABLE_MASK) >> \ DECODER_SUB_PAGE_TABLE_LOC) +#define DECODER_QUEUE_TIMEOUT_US 1000000 /* 1s */ +#define CMD_ENTRY_SIZE 16 +#define EVT_ENTRY_SIZE 16 + static void fill_page_entry(struct page_entry *page, struct decoder_map_info *info, u64 offset) { @@ -616,13 +621,37 @@ static void ub_decoder_init_page_table(struct ub_decoder *decoder, void *pgtlb_b } } -void hi_register_decoder_base_addr(struct ub_bus_controller *ubc, - u64 *cmd_queue, u64 *event_queue) +int hi_init_decoder_queue(struct ub_decoder *decoder) { - struct hi_ubc_private_data *data = (struct hi_ubc_private_data *)ubc->data; + struct hi_ubc_private_data *data; + struct ub_bus_controller *ubc; + + if (!decoder) + return -EINVAL; + + ubc = decoder->uent->ubc; + data = (struct hi_ubc_private_data *)ubc->data; + decoder->cmdq.base = data->io_decoder_cmdq; + decoder->evtq.base = data->io_decoder_evtq; + + decoder->cmdq.qbase = ioremap(decoder->cmdq.base, + (1 << decoder->cmdq.qs) * CMD_ENTRY_SIZE); + if (!decoder->cmdq.qbase) + return -ENOMEM; + + decoder->evtq.qbase = ioremap(decoder->evtq.base, + (1 << decoder->evtq.qs) * EVT_ENTRY_SIZE); + if (!decoder->evtq.qbase) { + iounmap(decoder->cmdq.qbase); + return -ENOMEM; + } + return 0; +} - *cmd_queue = data->io_decoder_cmdq; - *event_queue = data->io_decoder_evtq; +void hi_uninit_decoder_queue(struct ub_decoder *decoder) +{ + iounmap(decoder->cmdq.qbase); + iounmap(decoder->evtq.qbase); } int hi_create_decoder_table(struct ub_decoder *decoder) @@ -697,7 +726,7 @@ int hi_decoder_unmap(struct ub_decoder *decoder, phys_addr_t addr, u64 size) ret = handle_table(decoder, &info, false); if (ret) return ret; - return ub_decoder_cmd_request(decoder, addr, size, TLBI_PARTIAL); + return hi_decoder_cmd_request(decoder, addr, size, TLBI_PARTIAL); } int hi_decoder_map(struct ub_decoder *decoder, struct decoder_map_info *info) @@ -712,3 +741,356 @@ int hi_decoder_map(struct ub_decoder *decoder, struct decoder_map_info *info) return handle_table(decoder, info, true); } + +struct sync_entry { + u64 op : 8; + u64 reserve0 : 4; + u64 cm : 2; + u64 ntf_sh : 2; + u64 ntf_attr : 4; + u64 reserve1 : 12; + u64 notify_data : 32; + u64 reserve2 : 2; + u64 notify_addr : 50; + u64 reserve3 : 12; +}; + +struct tlbi_all_entry { + u32 op : 8; + u32 reserve0 : 24; + u32 reserve1; + u32 reserve2; + u32 reserve3; +}; + +struct tlbi_partial_entry { + u32 op : 8; + u32 reserve0 : 24; + u32 tlbi_addr_base : 28; + u32 reserve1 : 4; + u32 tlbi_addr_limt : 28; + u32 reserve2 : 4; + u32 reserve3; +}; + +#define TLBI_ADDR_MASK GENMASK_ULL(43, 20) +#define TLBI_ADDR_OFFSET 20 +#define CMDQ_ENT_DWORDS 2 + +#define NTF_SH_NSH 0b00 +#define NTF_SH_OSH 0b10 +#define NTF_SH_ISH 0b11 + +#define NTF_ATTR_IR_NC 0b00 +#define NTF_ATTR_IR_WBRA 0b01 +#define NTF_ATTR_IR_WT 0b10 +#define NTF_ATTR_IR_WB 0b11 +#define NTF_ATTR_OR_NC 0b0000 +#define NTF_ATTR_OR_WBRA 0b0100 +#define NTF_ATTR_OR_WT 0b1000 +#define NTF_ATTR_OR_WB 0b1100 + +#define Q_IDX(qs, p) ((p) & ((1 << (qs)) - 1)) +#define Q_WRP(qs, p) ((p) & (1 << (qs))) +#define Q_OVF(p) ((p) & Q_OVERFLOW_FLAG) + +enum NOTIFY_TYPE { + DISABLE_NOTIFY = 0, + ENABLE_NOTIFY = 1, +}; + +static bool queue_has_space(struct ub_decoder_queue *q, u32 n) +{ + u32 space, prod, cons; + + prod = Q_IDX(q->qs, q->prod.cmdq_wr_idx); + cons = Q_IDX(q->qs, q->cons.cmdq_rd_idx); + + if (Q_WRP(q->qs, q->prod.cmdq_wr_idx) == + Q_WRP(q->qs, q->cons.cmdq_rd_idx)) + space = (1 << q->qs) - (prod - cons); + else + space = cons - prod; + + return space >= n; +} + +static u32 queue_inc_prod_n(struct ub_decoder_queue *q, u32 n) +{ + u32 prod = (Q_WRP(q->qs, q->prod.cmdq_wr_idx) | + Q_IDX(q->qs, q->prod.cmdq_wr_idx)) + n; + return Q_WRP(q->qs, prod) | Q_IDX(q->qs, prod); +} + +#define CMD_0_OP GENMASK_ULL(7, 0) +#define CMD_0_ADDR_BASE GENMASK_ULL(59, 32) +#define CMD_1_ADDR_LIMT GENMASK_ULL(27, 0) + +static void decoder_cmdq_issue_cmd(struct ub_decoder *decoder, phys_addr_t addr, + u64 size, enum ub_cmd_op_type op) +{ + struct ub_decoder_queue *cmdq = &(decoder->cmdq); + struct tlbi_partial_entry entry = {}; + u64 cmd[CMDQ_ENT_DWORDS] = {}; + void *pi; + int i; + + entry.op = op; + entry.tlbi_addr_base = (addr & TLBI_ADDR_MASK) >> TLBI_ADDR_OFFSET; + entry.tlbi_addr_limt = ((addr + size - 1U) & TLBI_ADDR_MASK) >> + TLBI_ADDR_OFFSET; + + cmd[0] |= FIELD_PREP(CMD_0_OP, entry.op); + cmd[0] |= FIELD_PREP(CMD_0_ADDR_BASE, entry.tlbi_addr_base); + cmd[1] |= FIELD_PREP(CMD_1_ADDR_LIMT, entry.tlbi_addr_limt); + + pi = cmdq->qbase + Q_IDX(cmdq->qs, cmdq->prod.cmdq_wr_idx) * + sizeof(struct tlbi_partial_entry); + + for (i = 0; i < CMDQ_ENT_DWORDS; i++) + writeq(cmd[i], pi + i * sizeof(u64)); + + cmdq->prod.cmdq_wr_idx = queue_inc_prod_n(cmdq, 1); +} + +#define NTF_DMA_ADDR_OFFSERT 2 +#define SYNC_0_OP GENMASK_ULL(7, 0) +#define SYNC_0_CM GENMASK_ULL(13, 12) +#define SYNC_0_NTF_ISH GENMASK_ULL(15, 14) +#define SYNC_0_NTF_ATTR GENMASK_ULL(19, 16) +#define SYNC_0_NTF_DATA GENMASK_ULL(63, 32) +#define SYNC_1_NTF_ADDR GENMASK_ULL(51, 2) +#define SYNC_NTF_DATA 0xffffffff + +static void decoder_cmdq_issue_sync(struct ub_decoder *decoder) +{ + struct ub_decoder_queue *cmdq = &(decoder->cmdq); + u64 cmd[CMDQ_ENT_DWORDS] = {}; + struct sync_entry entry = {}; + phys_addr_t sync_dma; + void __iomem *pi; + int i; + + entry.op = SYNC; + entry.cm = ENABLE_NOTIFY; + sync_dma = cmdq->base + Q_IDX(cmdq->qs, cmdq->prod.cmdq_wr_idx) * + sizeof(struct sync_entry); + entry.ntf_sh = NTF_SH_NSH; + entry.ntf_attr = NTF_ATTR_IR_NC | NTF_ATTR_OR_NC; + entry.notify_data = SYNC_NTF_DATA; + entry.notify_addr = sync_dma >> NTF_DMA_ADDR_OFFSERT; + + cmd[0] |= FIELD_PREP(SYNC_0_OP, entry.op); + cmd[0] |= FIELD_PREP(SYNC_0_CM, entry.cm); + cmd[0] |= FIELD_PREP(SYNC_0_NTF_ISH, entry.ntf_sh); + cmd[0] |= FIELD_PREP(SYNC_0_NTF_ATTR, entry.ntf_attr); + cmd[0] |= FIELD_PREP(SYNC_0_NTF_DATA, entry.notify_data); + cmd[1] |= FIELD_PREP(SYNC_1_NTF_ADDR, entry.notify_addr); + + pi = cmdq->qbase + Q_IDX(cmdq->qs, cmdq->prod.cmdq_wr_idx) * + sizeof(struct sync_entry); + for (i = 0; i < CMDQ_ENT_DWORDS; i++) + writeq(cmd[i], pi + i * sizeof(u64)); + + decoder->notify = pi; + cmdq->prod.cmdq_wr_idx = queue_inc_prod_n(cmdq, 1); +} + +static void decoder_cmdq_update_prod(struct ub_decoder *decoder) +{ + struct ub_entity *uent = decoder->uent; + struct queue_idx q; + int ret; + + ret = ub_cfg_read_dword(uent, DECODER_CMDQ_PROD, &q.val); + if (ret) + ub_err(uent, "update pi, read decoder cmdq prod fail\n"); + + decoder->cmdq.prod.cmdq_err_resp = q.cmdq_err_resp; + ret = ub_cfg_write_dword(uent, DECODER_CMDQ_PROD, + decoder->cmdq.prod.val); + if (ret) + ub_err(uent, "update pi, write decoder cmdq prod fail\n"); +} + +static int wait_for_cmdq_free(struct ub_decoder *decoder, u32 n) +{ + ktime_t timeout = ktime_add_us(ktime_get(), DECODER_QUEUE_TIMEOUT_US); + struct ub_decoder_queue *cmdq = &(decoder->cmdq); + struct ub_entity *uent = decoder->uent; + int ret; + + while (true) { + ret = ub_cfg_read_dword(uent, DECODER_CMDQ_CONS, + &(cmdq->cons.val)); + if (ret) + return ret; + + if (queue_has_space(cmdq, n + 1)) + return 0; + + if (ktime_compare(ktime_get(), timeout) > 0) { + ub_err(uent, "decoder cmdq wait free entry timeout\n"); + return -ETIMEDOUT; + } + cpu_relax(); + } +} + +static int wait_for_cmdq_notify(struct ub_decoder *decoder) +{ + ktime_t timeout; + u32 val; + + timeout = ktime_add_us(ktime_get(), DECODER_QUEUE_TIMEOUT_US); + while (true) { + val = readl(decoder->notify); + if (val == SYNC_NTF_DATA) + return 0; + + if (ktime_compare(ktime_get(), timeout) > 0) { + ub_err(decoder->uent, "decoder cmdq wait notify timeout\n"); + return -ETIMEDOUT; + } + cpu_relax(); + } +} + +int hi_decoder_cmd_request(struct ub_decoder *decoder, phys_addr_t addr, + u64 size, enum ub_cmd_op_type op) +{ + int ret; + + ret = wait_for_cmdq_free(decoder, 1); + if (ret) + return ret; + + decoder_cmdq_issue_cmd(decoder, addr, size, op); + decoder_cmdq_issue_sync(decoder); + decoder_cmdq_update_prod(decoder); + + ret = wait_for_cmdq_notify(decoder); + return ret; +} +#ifdef UBUS_KP_TOOL_STUB +EXPORT_SYMBOL_GPL(hi_decoder_cmd_request); +#endif + +static bool queue_empty(struct ub_decoder_queue *q) +{ + return (Q_IDX(q->qs, q->prod.eventq_wr_idx) == + Q_IDX(q->qs, q->cons.eventq_rd_idx)) && + (Q_WRP(q->qs, q->prod.eventq_wr_idx) == + Q_WRP(q->qs, q->cons.eventq_rd_idx)); +} + +static void queue_inc_cons(struct ub_decoder_queue *q) +{ + u32 cons = (Q_WRP(q->qs, q->cons.eventq_rd_idx) | + Q_IDX(q->qs, q->cons.eventq_rd_idx)) + 1; + q->cons.eventq_rd_idx = Q_WRP(q->qs, cons) | Q_IDX(q->qs, cons); +} + +enum event_op_type { + RESERVED = 0x0, + EVENT_ADDR_OUT_OF_RANGE = 0x01, + EVENT_ILLEGAL_CMD = 0x02, +}; + +#define EVTQ_0_ID GENMASK_ULL(7, 0) +#define EVTQ_0_ADDR GENMASK_ULL(59, 32) +#define EVTQ_0_CMD_OPCODE GENMASK_ULL(39, 32) +#define EVTQ_ENT_DWORDS 2 +#define MAX_REASON_NUM 3 + +static const char *cmd_err_reason[MAX_REASON_NUM] = { + "no error", + "illegal command", + "abort error(read command with 2bit ecc)" +}; + +static void fix_err_cmd(struct ub_decoder *decoder) +{ + struct ub_decoder_queue *cmdq = &(decoder->cmdq); + struct ub_entity *uent = decoder->uent; + u64 cmd[CMDQ_ENT_DWORDS] = {}; + struct queue_idx prod, cons; + void *pi; + int i; + + if (ub_cfg_read_dword(uent, DECODER_CMDQ_CONS, &cons.val)) { + ub_err(uent, "decoder fix error cmd, read ci failed\n"); + return; + } + if (ub_cfg_read_dword(uent, DECODER_CMDQ_PROD, &prod.val)) { + ub_err(uent, "decoder fix error cmd, read pi failed\n"); + return; + } + + cmd[0] |= FIELD_PREP(CMD_0_OP, TLBI_ALL); + pi = cmdq->qbase + Q_IDX(cmdq->qs, cons.cmdq_rd_idx) * + sizeof(struct tlbi_partial_entry); + + for (i = 0; i < CMDQ_ENT_DWORDS; i++) + writeq(cmd[i], pi + i * sizeof(u64)); + + if (cons.cmdq_err_reason >= MAX_REASON_NUM) + ub_err(uent, "cmdq err reason is invalid, reason=%u\n", + cons.cmdq_err_reason); + else + ub_err(uent, "cmdq err reason is %s\n", cmd_err_reason[cons.cmdq_err_reason]); + + prod.cmdq_err_resp = cons.cmdq_err; + + if (ub_cfg_write_dword(uent, DECODER_CMDQ_PROD, prod.val)) + ub_err(uent, "decoder fix error cmd, write pi err resp failed\n"); +} + +static void handle_evt(struct ub_decoder *decoder, u64 *evt) +{ + struct ub_entity *uent = decoder->uent; + + switch (FIELD_GET(EVTQ_0_ID, evt[0])) { + case EVENT_ADDR_OUT_OF_RANGE: + ub_err(uent, "decoder event, input addr out of range, addr=%#.7x00000\n", + (u32)FIELD_GET(EVTQ_0_ADDR, evt[0])); + break; + case EVENT_ILLEGAL_CMD: + ub_err(uent, "decoder event, illegal cmd, cmd_opcode=%#x\n", + (u32)FIELD_GET(EVTQ_0_CMD_OPCODE, evt[0])); + fix_err_cmd(decoder); + break; + default: + ub_err(uent, "invalid event opcode, opcode=%#x\n", + (u32)FIELD_GET(EVTQ_0_ID, evt[0])); + } +} + +void hi_decoder_event_deal(struct ub_decoder *decoder) +{ + struct ub_decoder_queue *evtq = &decoder->evtq; + struct ub_entity *uent = decoder->uent; + u64 evt[EVTQ_ENT_DWORDS]; + void *ci; + int i; + + if (ub_cfg_read_dword(uent, DECODER_EVENTQ_PROD, &(evtq->prod.val))) { + ub_err(uent, "decoder handle event, read eventq pi fail\n"); + return; + } + + while (!queue_empty(evtq)) { + ci = evtq->qbase + Q_IDX(evtq->qs, evtq->cons.eventq_rd_idx) * + EVT_ENTRY_SIZE; + + for (i = 0; i < EVTQ_ENT_DWORDS; i++) + evt[i] = readq(ci + i * sizeof(u64)); + + handle_evt(decoder, evt); + queue_inc_cons(evtq); + + if (ub_cfg_write_dword(uent, DECODER_EVENTQ_CONS, + evtq->cons.val)) + ub_err(uent, "decoder handle event, write eventq ci fail\n"); + } +} diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.h b/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.h index 50658ef7b9cb..fc49a25b80d6 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.h +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.h @@ -38,8 +38,8 @@ DECODER_PAGE_SIZE * \ RGTLB_TO_PGTLB) -void hi_register_decoder_base_addr(struct ub_bus_controller *ubc, - u64 *cmd_queue, u64 *event_queue); +int hi_init_decoder_queue(struct ub_decoder *decoder); +void hi_uninit_decoder_queue(struct ub_decoder *decoder); int hi_create_decoder_table(struct ub_decoder *decoder); void hi_free_decoder_table(struct ub_decoder *decoder); @@ -47,4 +47,8 @@ void hi_free_decoder_table(struct ub_decoder *decoder); int hi_decoder_map(struct ub_decoder *decoder, struct decoder_map_info *info); int hi_decoder_unmap(struct ub_decoder *decoder, phys_addr_t addr, u64 size); +int hi_decoder_cmd_request(struct ub_decoder *decoder, phys_addr_t addr, + u64 size, enum ub_cmd_op_type op); +void hi_decoder_event_deal(struct ub_decoder *decoder); + #endif /* __HISI_DECODER_H__ */ -- Gitee From 5aae6469f0697cca638c4866a23f1c6f3ddbaf0a Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 9 Jan 2026 11:07:47 +0800 Subject: [PATCH 120/214] ub:ubus:Fix ubus interrupt release issue ANBZ: #45460 commit 6fb61e4fb48c42edc56605d6e186e28e67628502 openEuler. Fix ubus interrupt release issue when the resource space is inaccessible. Fixes: 81962d0ecc6e ("ub:ubus: Support UBUS Interrupt framework") Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/msi/msi.c | 77 +++++++++++++-------------------------- 1 file changed, 25 insertions(+), 52 deletions(-) diff --git a/drivers/ub/ubus/msi/msi.c b/drivers/ub/ubus/msi/msi.c index 3ea005eb08ab..f505ad16dcaf 100644 --- a/drivers/ub/ubus/msi/msi.c +++ b/drivers/ub/ubus/msi/msi.c @@ -59,11 +59,6 @@ struct ub_entity *msi_desc_to_ub_entity(struct msi_desc *desc) return to_ub_entity(desc->dev); } -static void __iomem *ub_vector_desc_base_addr(struct msi_desc *desc) -{ - return desc->ub_intr.vector_base; -} - void __iomem *ub_vector_desc_addr(struct msi_desc *desc) { return desc->ub_intr.vector_base + (desc->ub_intr.intr_attrib.entry_nr * @@ -75,12 +70,6 @@ static void __iomem *ub_addr_desc_base_addr(struct msi_desc *desc) return desc->ub_intr.addr_base; } -static void __iomem *ub_addr_desc_addr(struct msi_desc *desc) -{ - return desc->ub_intr.addr_base + (desc->ub_intr.intr_attrib.addr_index * - UB_INTR_ADDR_ENTRY_SIZE); -} - static void ub_int_type1_update_mask(struct msi_desc *desc, u32 clear, u32 set) { raw_spinlock_t *lock = &msi_desc_to_ub_entity(desc)->usi_lock; @@ -102,9 +91,13 @@ static void ub_int_type1_mask(struct msi_desc *desc, u32 mask) static void ub_int_type2_mask(struct msi_desc *desc) { void __iomem *addr = ub_vector_desc_addr(desc); - u32 reg_val = readl(addr + UB_INTR_VECTOR_ADDR_INDEX) | UB_INTR_VECTOR_MASK_MASK; + u16 addr_index; + u32 reg_val; desc->ub_intr.intr_attrib.mask = 1; + addr_index = desc->ub_intr.intr_attrib.addr_index; + reg_val = UB_INTR_VECTOR_MASK_MASK | (u32)addr_index; + writel(reg_val, addr + UB_INTR_VECTOR_ADDR_INDEX); } @@ -131,10 +124,11 @@ static void ub_int_type1_unmask_irq(struct msi_desc *desc, u32 mask) static void ub_int_type2_unmask_irq(struct msi_desc *desc) { void __iomem *addr = ub_vector_desc_addr(desc); - u32 reg_val = readl(addr + UB_INTR_VECTOR_ADDR_INDEX) & (~UB_INTR_VECTOR_MASK_MASK); + u16 addr_index; desc->ub_intr.intr_attrib.mask = 0; - writel(reg_val, addr + UB_INTR_VECTOR_ADDR_INDEX); + addr_index = desc->ub_intr.intr_attrib.addr_index; + writel((u32)addr_index, addr + UB_INTR_VECTOR_ADDR_INDEX); } static void ub_usi_desc_unmask(struct msi_desc *desc, u32 nr) @@ -191,30 +185,6 @@ static void __iomem *ub_intr_find_one_addr(struct msi_msg *msg, return first_unused_base; } -static bool ub_intr_check_addr_used(struct msi_desc *entry) -{ - void __iomem *base, *vec_addr; - u16 addr_index; - u32 i, vec_cnt; - - base = ub_vector_desc_base_addr(entry); - vec_addr = ub_vector_desc_addr(entry); - vec_cnt = entry->ub_intr.vec_num + 1; - addr_index = entry->ub_intr.intr_attrib.addr_index; - - for (i = 0; i < vec_cnt; i++, base += UB_INTR_VECTOR_ENTRY_SIZE) { - if ((readl(base + UB_INTR_VECTOR_ADDR_INDEX) & UB_INTR_VECTOR_MASK_MASK) || - base == vec_addr) - continue; - - if ((readl(base + UB_INTR_VECTOR_ADDR_INDEX) & - UB_INTR_VECTOR_ADDR_INDEX_MASK) == addr_index) - return true; - } - - return false; -} - static void ub_int_type1_clear_msg(struct msi_desc *entry, struct msi_msg *msg) { struct ub_entity *uent = msi_desc_to_ub_entity(entry); @@ -228,27 +198,18 @@ static void ub_int_type1_clear_msg(struct msi_desc *entry, struct msi_msg *msg) static void ub_int_type2_clear_msg(struct msi_desc *entry, struct msi_msg *msg) { - void __iomem *vector_base, *addr_base; - u32 reg_val; + void __iomem *vector_base; - addr_base = ub_addr_desc_addr(entry); vector_base = ub_vector_desc_addr(entry); - if (!vector_base || !addr_base) { + if (!vector_base) { WARN_ON(1); return; } writel(0, vector_base + UB_INTR_VECTOR_ID); - reg_val = readl(vector_base + UB_INTR_VECTOR_ADDR_INDEX) & - ~UB_INTR_VECTOR_ADDR_INDEX_MASK; - writel(reg_val, vector_base + UB_INTR_VECTOR_ADDR_INDEX); - - if (!ub_intr_check_addr_used(entry)) { - writel(0, addr_base + UB_INTR_ADDR_TOKENID); - writel(0, addr_base + UB_INTR_ADDR_DSTEID_0); - writel(0, addr_base + UB_INTR_ADDR_ADDR_L); - writel(0, addr_base + UB_INTR_ADDR_ADDR_H); - } + /* clear interrupt index(bit 0~15), keep mask(bit 16) */ + writel(UB_INTR_VECTOR_MASK_MASK, + vector_base + UB_INTR_VECTOR_ADDR_INDEX); } static void __ub_clear_msi_msg(struct msi_desc *entry, struct msi_msg *msg) @@ -527,6 +488,9 @@ EXPORT_SYMBOL_GPL(usi_setup_interrupts); void ub_disable_intr(struct ub_entity *uent) { + u32 i, addr_num; + void *intr_addr; + if (!uent || !uent->intr_enabled) return; @@ -534,6 +498,15 @@ void ub_disable_intr(struct ub_entity *uent) ub_cfg_write_byte(uent, UB_INT_TYPE1_ENABLE, 0); ub_cfg_write_dword(uent, UB_INT_TYPE1_INT_MASK, GENMASK(31, 0)); } else { + addr_num = ub_intr_addr_count(uent); + for (i = 0; i < addr_num; i++) { + intr_addr = uent->intr_addr_base + i * UB_INTR_ADDR_ENTRY_SIZE; + writel(0, intr_addr + UB_INTR_ADDR_TOKENID); + writel(0, intr_addr + UB_INTR_ADDR_DSTEID_0); + writel(0, intr_addr + UB_INTR_ADDR_ADDR_L); + writel(0, intr_addr + UB_INTR_ADDR_ADDR_H); + } + ub_cfg_write_byte(uent, UB_INT_MASK, 1); ub_cfg_write_byte(uent, UB_INT_EN, 0); } -- Gitee From 35b97b107261697fe837cdba2d8cfe2760bf549b Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Tue, 23 Dec 2025 13:56:35 +0800 Subject: [PATCH 121/214] ub:ubus: fix ub_get_bus_controller getting null ANBZ: #45460 commit 539643475d9fa14c6e679642c1d0331e826f40ce openEuler. fix ub_get_bus_controller getting null Fixes: 26a640c9d1b1 ("ub:ubus: add ubus controller framework") Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/ubus_driver.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index 974020bf3c38..6d5594156c3d 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -61,6 +61,12 @@ int ub_get_bus_controller(struct ub_entity *ubc_dev[], unsigned int max_num, { struct ub_bus_controller *ubc; unsigned int ubc_num = 0; + int ret; + + if (!manage_subsystem_ops) { + pr_err("manage subsystem ops is null\n"); + return -EINVAL; + } if (!real_num || !ubc_dev) { pr_err("%s: input parameters invalid\n", __func__); @@ -70,16 +76,25 @@ int ub_get_bus_controller(struct ub_entity *ubc_dev[], unsigned int max_num, list_for_each_entry(ubc, &ubc_list, node) { if (ubc_num >= max_num) { pr_err("ubc list num over max num %u\n", max_num); - ub_put_bus_controller(ubc_dev, max_num); - return -ENOMEM; + ret = -ENOMEM; + goto ubc_put; } - ubc_dev[ubc_num] = ub_entity_get(ubc->uent); + if (!ub_entity_get(ubc->uent)) { + pr_err("The ub_entity of ubc is null\n"); + ret = -EINVAL; + goto ubc_put; + } + ubc_dev[ubc_num] = ubc->uent; ubc_num++; } *real_num = ubc_num; return 0; + +ubc_put: + ub_put_bus_controller(ubc_dev, max_num); + return ret; } EXPORT_SYMBOL_GPL(ub_get_bus_controller); -- Gitee From e8e97254f800edc0a082149d2e3f858925602952 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Thu, 15 Jan 2026 11:22:40 +0800 Subject: [PATCH 122/214] ub:hisi-ubus:Fix vdm message print error ANBZ: #45460 commit 30b08dce06f0b7f21cf80ab6a76efdb077276ece openEuler. Fix vdm message print error Fixes: 86fec00cb73a ("ub:hisi-ubus: Support UBUS vdm entity enable message") Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/vendor/hisilicon/vdm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ub/ubus/vendor/hisilicon/vdm.c b/drivers/ub/ubus/vendor/hisilicon/vdm.c index 329bc51b5a0e..39143a4dcaa9 100644 --- a/drivers/ub/ubus/vendor/hisilicon/vdm.c +++ b/drivers/ub/ubus/vendor/hisilicon/vdm.c @@ -437,7 +437,7 @@ static int ub_vdm_msg_info_handle(struct ub_bus_controller *ubc, } } - dev_err(&ubc->dev, "vdm sub opvode is invalid, sub_opcode = %#x\n", sub_opcode); + dev_err(&ubc->dev, "vdm sub opcode is invalid, sub_opcode = %#x\n", sub_opcode); return UB_MSG_RSP_EXEC_EINVAL; } -- Gitee From 9e9feb4ca4020a4e9f9bb0ce4a1746d28ceffec2 Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Thu, 15 Jan 2026 16:07:40 +0800 Subject: [PATCH 123/214] ub:hisi-ubus: cq poller maximum handle 64 cqe per time ANBZ: #45460 commit ec98563bd024c32ba6cd4f8954b81df2ebe2981b openEuler. Cq poller maximum handle 64 cqe per time, so that the time stay in irqsave state becomes short to prevent cpu hard lockup. Fixes: d7b599123e51 ("ub:hisi-ubus: Support for processing CQ request messages") Signed-off-by: Yahui Liu Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/vendor/hisilicon/msg.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/ub/ubus/vendor/hisilicon/msg.c b/drivers/ub/ubus/vendor/hisilicon/msg.c index 6d4e9d422c49..178e186c626b 100644 --- a/drivers/ub/ubus/vendor/hisilicon/msg.c +++ b/drivers/ub/ubus/vendor/hisilicon/msg.c @@ -63,6 +63,7 @@ static inline void cqe_state_set(struct hi_message_device *hmd, int idx, #define HI_MSG_CQ_POLL_PERIOD 100 #define HI_TIMEOUT_MSG_POLL_PERIOD 500 #define HI_MSG_AGING_PERIOD 2 +#define MAX_CQ_POLL_PER_TIME 64 #define MSG_MAX (HI_SQ_CFG_DEPTH - 1) #define q_left_cnt(q) ((q)->depth - q_used_cnt((q)) - 1) @@ -391,6 +392,8 @@ static int hi_msg_cq_poller(struct hi_message_device *hmd) handled: cqe_state_set(hmd, idx, CQ_SW_HANDLED); handled_cnt++; + if (handled_cnt == MAX_CQ_POLL_PER_TIME) + break; } spin_unlock_irqrestore(&cq->lock, flags); @@ -755,6 +758,8 @@ static int hi_msg_timeout_poller(struct hi_message_device *hmd) if (hi_cqe_ageing(hmd, idx) || hi_is_timeout_msg(hmd, idx)) { cqe_state_set(hmd, idx, CQ_SW_HANDLED); handled_cnt++; + if (handled_cnt == MAX_CQ_POLL_PER_TIME) + break; } } -- Gitee From d01b0d8a708541651ba7cb056d85151a97d26c5f Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Tue, 27 Jan 2026 14:22:55 +0800 Subject: [PATCH 124/214] ub:ubus: Fix ubus link message and pool device error ANBZ: #45460 commit 177750a35904a7b0b88df48ed3a21e05c2c57753 openEuler. 1.add flag UB_ENTITY_DISCONNECTED to stop cfg access to device when device all down or reset. 2.bugfix of msgq init, coming link msg. 3.add ueid check during entity reg. 4.modify mgmt val when pool device not init. 5.add pool device guid check with pld.guid. Fixes: e00041ecb77a ("ub:ubus: Support UBUS pool devices register") Signed-off-by: Junlong Zheng Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/enum.c | 13 ++++---- drivers/ub/ubus/enum.h | 2 +- drivers/ub/ubus/instance.c | 30 ++++++++++++------- drivers/ub/ubus/instance.h | 6 +--- drivers/ub/ubus/link.c | 1 + drivers/ub/ubus/pool.c | 17 +++++++++-- drivers/ub/ubus/reset.c | 2 ++ drivers/ub/ubus/ubus.h | 1 + drivers/ub/ubus/ubus_config.c | 20 ++++++++++++- drivers/ub/ubus/ubus_entity.c | 41 +++++++++++++------------- drivers/ub/ubus/vendor/hisilicon/msg.c | 22 +++++++------- drivers/ub/ubus/vendor/hisilicon/vdm.c | 26 ++++------------ 12 files changed, 102 insertions(+), 79 deletions(-) diff --git a/drivers/ub/ubus/enum.c b/drivers/ub/ubus/enum.c index 67a87f9f23aa..656dcf72d35b 100644 --- a/drivers/ub/ubus/enum.c +++ b/drivers/ub/ubus/enum.c @@ -993,19 +993,20 @@ int ub_enum_bfs_route_cal(struct list_head *dev_list) return ret; } -int ub_cfg_read_guid(struct ub_entity *uent) +int ub_cfg_read_guid(struct ub_entity *uent, u32 *dw) { - u32 val = 0; + u32 val[UB_GUID_DW_NUM]; int i, ret; for (i = 0; i < UB_GUID_DW_NUM; i++) { - ret = ub_cfg_read_dword(uent, UB_GUID + i * sizeof(u32), &val); + ret = ub_cfg_read_dword(uent, UB_GUID + i * sizeof(u32), &val[i]); if (ret) return ret; - - uent->guid.dw[i] = val; } + for (i = 0; i < UB_GUID_DW_NUM; i++) + *(dw + i) = val[i]; + return 0; } @@ -1056,7 +1057,7 @@ static struct ub_entity *ub_enum_create_bus_controller(struct ub_bus_controller return (struct ub_entity *)ERR_PTR(-ENOMEM); ubc->uent = uent; - ret = ub_cfg_read_guid(uent); + ret = ub_cfg_read_guid(uent, uent->guid.dw); if (ret) { dev_err(&ubc->dev, "read guid failed, ret=%d\n", ret); goto err_out; diff --git a/drivers/ub/ubus/enum.h b/drivers/ub/ubus/enum.h index 5c20eb68603a..32094ce41751 100644 --- a/drivers/ub/ubus/enum.h +++ b/drivers/ub/ubus/enum.h @@ -187,7 +187,7 @@ int ub_enum_topo_scan_ports(struct ub_entity *uent, u16 start_port, u16 num, struct list_head *dev_list, void *buf); struct ub_entity *ub_enum_get_port_r_uent(struct ub_port *port, void *buf); size_t calc_enum_pld_header_size(struct enum_pld_scan_header *header, bool req); -int ub_cfg_read_guid(struct ub_entity *uent); +int ub_cfg_read_guid(struct ub_entity *uent, u32 *dw); int ub_enum_bfs_route_cal(struct list_head *uent_list); int ub_query_port_na(struct ub_entity *uent, void *buf); int ub_query_ent_na(struct ub_entity *uent, void *buf); diff --git a/drivers/ub/ubus/instance.c b/drivers/ub/ubus/instance.c index 342fc9960ef2..c7829bff236a 100644 --- a/drivers/ub/ubus/instance.c +++ b/drivers/ub/ubus/instance.c @@ -23,6 +23,8 @@ static DEFINE_MUTEX(dynamic_mutex); static u32 instance_count; static u32 instance_start; +typedef bool (*instance_match)(struct ub_bus_instance *bi, void *arg); + static ssize_t instance_store(const struct bus_type *bus, const char *buf, size_t count) { @@ -116,14 +118,13 @@ static struct ub_bus_instance *ub_bus_instance_get(struct ub_bus_instance *bi) return bi; } -void ub_bus_instance_put(struct ub_bus_instance *bi) +static void ub_bus_instance_put(struct ub_bus_instance *bi) { if (bi) kref_put(&bi->kref, ub_release_bus_instance); } -EXPORT_SYMBOL_GPL(ub_bus_instance_put); -bool eid_match(struct ub_bus_instance *bi, void *arg) +static bool eid_match(struct ub_bus_instance *bi, void *arg) { if (bi == NULL || arg == NULL) return false; @@ -132,7 +133,6 @@ bool eid_match(struct ub_bus_instance *bi, void *arg) return bi->info.eid == *eid; } -EXPORT_SYMBOL_GPL(eid_match); static struct ub_bus_instance *ub_alloc_bus_instance(void) { @@ -157,15 +157,11 @@ static bool guid_match(struct ub_bus_instance *bi, void *arg) return guid_equal(&bi->info.guid.id, guid); } -struct ub_bus_instance *ub_find_bus_instance(instance_match match, void *arg) +static struct ub_bus_instance *ub_find_bus_instance(instance_match match, + void *arg) { struct ub_bus_instance *bi; - if (!match || !arg) { - pr_err("instance match or arg is null\n"); - return NULL; - } - mutex_lock(&ubi_list_mutex); list_for_each_entry(bi, &ubi_list, node) if (match(bi, arg)) @@ -178,7 +174,19 @@ struct ub_bus_instance *ub_find_bus_instance(instance_match match, void *arg) return bi; } -EXPORT_SYMBOL_GPL(ub_find_bus_instance); + +bool ub_bus_instance_exist(u32 eid) +{ + struct ub_bus_instance *bi; + + bi = ub_find_bus_instance(eid_match, &eid); + if (!bi) + return false; + + ub_bus_instance_put(bi); + return true; +} +EXPORT_SYMBOL_GPL(ub_bus_instance_exist); static int bi_duplicate_check(struct ub_bus_instance_info *n) { diff --git a/drivers/ub/ubus/instance.h b/drivers/ub/ubus/instance.h index 6aac58df77bc..02dcae1ab33b 100644 --- a/drivers/ub/ubus/instance.h +++ b/drivers/ub/ubus/instance.h @@ -10,8 +10,6 @@ extern struct bus_attribute bus_attr_instance; -typedef bool (*instance_match)(struct ub_bus_instance *bi, void *arg); - #define UBUS_INSTANCE_STATIC_SERVER 0 #define UBUS_INSTANCE_STATIC_CLUSTER 1 @@ -26,8 +24,6 @@ typedef bool (*instance_match)(struct ub_bus_instance *bi, void *arg); int ub_static_bus_instance_init(struct ub_bus_controller *ubc); void ub_static_bus_instance_uninit(struct ub_bus_controller *ubc); -void ub_bus_instance_put(struct ub_bus_instance *bi); -struct ub_bus_instance *ub_find_bus_instance(instance_match match, void *arg); int ub_ioctl_bus_instance_create(void __user *uptr); int ub_ioctl_bus_instance_destroy(void __user *uptr); int ub_ioctl_bus_instance_bind(void __user *uptr); @@ -41,6 +37,6 @@ int ub_notify_bus_instance_handle(struct ub_bus_controller *ubc, bool flag, void ub_static_cluster_instance_drain(void); int ub_default_bus_instance_init(struct ub_entity *uent); void ub_default_bus_instance_uninit(struct ub_entity *uent); -bool eid_match(struct ub_bus_instance *bi, void *arg); +bool ub_bus_instance_exist(u32 eid); #endif /* __INSTANCE_H__ */ diff --git a/drivers/ub/ubus/link.c b/drivers/ub/ubus/link.c index 001139f3ad63..7a3738a3741a 100644 --- a/drivers/ub/ubus/link.c +++ b/drivers/ub/ubus/link.c @@ -341,6 +341,7 @@ void ublc_link_down_handle(struct ub_port *port) device_lock(&uent->dev); if (ublc_device_is_down(port)) { + ub_entity_assign_priv_flag(port->r_uent, UB_ENTITY_DISCONNECTED, true); ub_info(uent, "port%u link down\n", port->index); ublc_handle_all_link_down(port, port->r_uent); ub_info(uent, "all port link down and remove device\n"); diff --git a/drivers/ub/ubus/pool.c b/drivers/ub/ubus/pool.c index 4fdd9aca922d..fce5be54705e 100644 --- a/drivers/ub/ubus/pool.c +++ b/drivers/ub/ubus/pool.c @@ -254,8 +254,9 @@ static void ub_fad_para_init(struct pool_fad *fad, struct entity_reg_msg_pld *pl static int ub_entity_reg_check(struct ub_bus_controller *ubc, struct entity_reg_msg_pld *pld, bool ers_valid) { - struct entity_rs_info *ers; + struct ub_guid *guid = (struct ub_guid *)pld->base.guid; struct device *dev = &ubc->dev; + struct entity_rs_info *ers; int i; if (pld->base.eid[0] == 0 || pld->base.ueid[0] == 0) { @@ -264,6 +265,18 @@ static int ub_entity_reg_check(struct ub_bus_controller *ubc, return -EINVAL; } + if (!ub_bus_instance_exist(pld->base.ueid[0])) { + dev_err(dev, "entity reg u_eid=%#x not exist\n", + pld->base.ueid[0]); + return -EINVAL; + } + + if (guid->bits.type != UB_TYPE_CONTROLLER) { + dev_err(dev, "entity reg guid type %u is invalid\n", + guid->bits.type); + return -EINVAL; + } + if (!ers_valid) return 0; @@ -309,7 +322,7 @@ static u8 ub_entity_reg_handle(struct ub_bus_controller *ubc, if (ret) { dev_err(&ubc->dev, "fad idx[%u] add failed\n", fad->base.entity_idx); kfree(fad); - return UB_MSG_RSP_EXEC_ENOEXEC; + return err_to_msg_rsp(ret); } spin_lock(&ub_fad_lock); diff --git a/drivers/ub/ubus/reset.c b/drivers/ub/ubus/reset.c index 51d984d25b8f..e1ecb04ebe31 100644 --- a/drivers/ub/ubus/reset.c +++ b/drivers/ub/ubus/reset.c @@ -99,6 +99,8 @@ int ub_device_reset(struct ub_entity *ent) return -EIO; } + ub_entity_assign_priv_flag(ent, UB_ENTITY_DISCONNECTED, true); + device_unlock(&ent->dev); ub_info(ent, "device reset success\n"); diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index 790778f339cc..229b027a8ef7 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -44,6 +44,7 @@ enum ub_entity_type { #define UB_ENTITY_ROUTE_UPDATED 2 /* Flag indicate uent's route is updated */ #define UB_ENTITY_SETUP 4 /* Flag indicate uent is setup */ #define UB_ENTITY_ACTIVE 5 /* Flag indicate uent is in normal or disable state */ +#define UB_ENTITY_DISCONNECTED 6 /* Flag indicate uent is disconnected */ static inline void ub_entity_assign_priv_flag(struct ub_entity *uent, int bit, bool flag) diff --git a/drivers/ub/ubus/ubus_config.c b/drivers/ub/ubus/ubus_config.c index 2c1f068dc11a..e15bfa8b5abc 100644 --- a/drivers/ub/ubus/ubus_config.c +++ b/drivers/ub/ubus/ubus_config.c @@ -111,7 +111,9 @@ void ub_msg_pkt_header_init(struct msg_pkt_header *header, struct ub_entity *uen cnth->nth_nlp = NTH_NLP_WITH_TPH; cnth->scna = scna; cnth->dcna = dcna; - cnth->mgmt = is_p_device(uent) ? 0 : 1; + cnth->mgmt = (is_p_device(uent) || + (uent->pool && uent_type(uent) == UB_TYPE_CONTROLLER)) + ? 0 : 1; header->ctph_nlp = CTPH_NLP_UPI_40BITS_UEID; header->tp_opcode = CTPH_OPCODE_NOT_CNP; @@ -236,6 +238,19 @@ static void ub_msg_pkt_req_init(struct ub_entity *uent, u8 size, u64 pos, u32 *v req->req_addr = pos / sizeof(u32); } +static bool ub_entity_is_disconnected(struct ub_entity *uent) +{ + struct ub_entity *pri = uent; + + if (uent->pool && uent_type(uent) == UB_TYPE_CONTROLLER) + return false; /* Pool entity not consider here. */ + + while (pri->entity_idx != 0) + pri = pri->pue; + + return ub_entity_test_priv_flag(pri, UB_ENTITY_DISCONNECTED); +} + static int ub_sync_cfg(struct ub_entity *uent, u8 size, u64 pos, bool iswrite, u32 *val) { @@ -245,6 +260,9 @@ static int ub_sync_cfg(struct ub_entity *uent, u8 size, u64 pos, bool iswrite, u8 sub_msg_code; int ret; + if (ub_entity_is_disconnected(uent)) + return -ETIMEDOUT; + if (!pos_size_valid(pos, size)) { ub_err(uent, "pos or size invalid, pos=%#llx, size=%#x\n", pos, size); diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index 3dbc196d4da4..e823cc6800f1 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -78,6 +78,8 @@ static int ub_entity_num_alloc(void) static int ub_get_guid(struct ub_entity *uent) { + char buf[SZ_64] = {}; + struct ub_guid guid; u16 code; int ret; @@ -86,11 +88,24 @@ static int ub_get_guid(struct ub_entity *uent) if (!uent->pool || uent_type(uent->pue) == UB_TYPE_ICONTROLLER) { uent_type(uent) = uent_type(uent->pue); - ret = ub_cfg_read_guid(uent); + ret = ub_cfg_read_guid(uent, uent->guid.dw); if (ret) return ret; } + if (uent->pool && uent_type(uent) == UB_TYPE_CONTROLLER) { + ret = ub_cfg_read_guid(uent, guid.dw); + if (ret) + return ret; + + if (!guid_equal(&uent->guid.id, &guid.id)) { + (void)ub_show_guid(&guid, buf); + ub_err(uent, "pool pld guid is not equal entity guid %s\n", + buf); + return -EINVAL; + } + } + ret = ub_cfg_read_word(uent, UB_CLASS_CODE, &code); if (ret) return ret; @@ -203,22 +218,6 @@ static int ub_setup_ent_normal(struct ub_entity *uent) return 0; } -static int ub_fad_cfg_access_check(struct ub_entity *uent) -{ - u32 feature; - int ret = 0; - - if (is_p_device(uent)) { - ret = ub_cfg_read_dword(uent, UB_CFG1_SUPPORT_FEATURE_L, - &feature); - if (ret) - ub_err(uent, "fad cfg access failed, eid=%#x, ret=%d\n", - uent->eid, ret); - } - - return ret; -} - static int ub_uent_cfg(struct ub_entity *uent, u32 uent_num) { struct ub_guid *guid = &uent->guid; @@ -301,9 +300,6 @@ int ub_setup_ent(struct ub_entity *uent) } ub_config_upi(uent); - ret = ub_fad_cfg_access_check(uent); - if (ret) - goto err_alloc; /* common setup */ ret = ub_eid_alloc(uent); @@ -438,7 +434,10 @@ void ub_start_ent(struct ub_entity *uent) return; ret = ub_default_bus_instance_init(uent); - WARN_ON(ret); + if (ret) { + ub_err(uent, "default bi init failed, ret=%d\n", ret); + return; + } ub_create_sysfs_dev_files(uent); ub_mem_decoder_init(uent); diff --git a/drivers/ub/ubus/vendor/hisilicon/msg.c b/drivers/ub/ubus/vendor/hisilicon/msg.c index 178e186c626b..ed43357556b7 100644 --- a/drivers/ub/ubus/vendor/hisilicon/msg.c +++ b/drivers/ub/ubus/vendor/hisilicon/msg.c @@ -438,32 +438,30 @@ static int hi_msg_queue_init(struct hi_message_device *hmd) size_t size; int ret; - ret = hi_msg_core_init(hmc, MSGQ_USER_BUS_DRV); - if (ret) - return ret; - - size = HI_CQE_STATE_SZ * hmc->queue[MSG_CQ].depth; + size = HI_CQE_STATE_SZ * HI_CQ_CFG_DEPTH; hmd->cqe_state = kzalloc(size, GFP_KERNEL); if (!hmd->cqe_state) { dev_err(hmc->dev, "cqe state memory failed\n"); - ret = -ENOMEM; - goto cqe_state_fail; + return -ENOMEM; + } + + ret = hi_msg_core_init(hmc, MSGQ_USER_BUS_DRV); + if (ret) { + kfree(hmd->cqe_state); + hmd->cqe_state = NULL; + return ret; } hi_msg_cq_poller_init(hmd); return 0; - -cqe_state_fail: - hi_msg_core_uninit(hmc); - return ret; } static void hi_msg_queue_uninit(struct hi_message_device *hmd) { hi_msg_cq_poller_uninit(hmd); - kfree(hmd->cqe_state); hi_msg_core_uninit(&hmd->hmc); + kfree(hmd->cqe_state); } static bool pkt_plen_valid(void *pkt, u16 pkt_size, int task_type) diff --git a/drivers/ub/ubus/vendor/hisilicon/vdm.c b/drivers/ub/ubus/vendor/hisilicon/vdm.c index 39143a4dcaa9..8abd4423a7e5 100644 --- a/drivers/ub/ubus/vendor/hisilicon/vdm.c +++ b/drivers/ub/ubus/vendor/hisilicon/vdm.c @@ -151,22 +151,6 @@ static int ub_idevice_enable_handle(struct ub_entity *pue, u16 idx, u8 is_mue, return 0; } -static int check_pld_ueid_valid(u32 user_eid) -{ - struct ub_bus_instance *bi; - - bi = ub_find_bus_instance(eid_match, &user_eid); - if (!bi) { - pr_err("vdm payload user eid is invalid, user_eid = 0x%x\n", - user_eid); - return -EINVAL; - } - - ub_bus_instance_put(bi); - - return 0; -} - /** * ub_idevice_pue_add_handler - add mue to the bus * @@ -203,8 +187,9 @@ static u8 ub_idevice_pue_add_handler(struct ub_bus_controller *ubc, struct vdm_m goto pue_reg_rsp; } - ret = check_pld_ueid_valid(pld->user_eid[0]); - if (ret) { + if (!ub_bus_instance_exist(pld->user_eid[0])) { + dev_err(&ubc->dev, "pue add msg user eid[%#x] not exist\n", + pld->user_eid[0]); status = UB_MSG_RSP_EXEC_EINVAL; goto pue_reg_rsp; } @@ -300,8 +285,9 @@ static u8 ub_idevice_ue_add_handler(struct ub_bus_controller *ubc, struct vdm_ms "The pue of this vdm ue to be enabled is normal\n"); } - ret = check_pld_ueid_valid(pld->user_eid[0]); - if (ret) { + if (!ub_bus_instance_exist(pld->user_eid[0])) { + dev_err(&ubc->dev, "ue add msg user eid[%#x] not exist\n", + pld->user_eid[0]); status = UB_MSG_RSP_EXEC_EINVAL; goto ue_reg_rsp; } -- Gitee From b01ab47a2e2003f0c25417e951a9c9bedb4fc13b Mon Sep 17 00:00:00 2001 From: Anzhe Li Date: Tue, 27 Jan 2026 15:35:48 +0800 Subject: [PATCH 125/214] ub:hisi-ubus: Delete validate_pa err print ANBZ: #45460 commit 1e88ff71a114fd9e53014f760298deac519f7c4a openEuler. Delete validate_pa err print Fixes: d392ae30c331 ("ub:hisi-ubus: Support for UB Memory Decoder Initialization") Signed-off-by: Anzhe Li Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/vendor/hisilicon/memory.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/ub/ubus/vendor/hisilicon/memory.c b/drivers/ub/ubus/vendor/hisilicon/memory.c index e49a4a82322f..0c1d06ca0735 100644 --- a/drivers/ub/ubus/vendor/hisilicon/memory.c +++ b/drivers/ub/ubus/vendor/hisilicon/memory.c @@ -471,6 +471,5 @@ static bool hi_mem_validate_pa(struct ub_bus_controller *ubc, return true; } - dev_err(&ubc->dev, "pa_start-pa_end is invalid.\n"); return false; } -- Gitee From d050d486a9cdc574a36cd8a74db2b469d03c6d68 Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Thu, 29 Jan 2026 16:44:38 +0800 Subject: [PATCH 126/214] ub:ubus: Not cfg access during surprise down ANBZ: #45460 commit 1e0844999878ec2b7cb990a0e1063cb630e277b1 openEuler. Not cfg access during surprise down Fixes: c21b483526d7 ("ub:ubus: Add UBUS capability interfaces") Signed-off-by: Yuhao Xiang Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/cap.c | 1 + drivers/ub/ubus/link.c | 45 ++++++++++++++++++++++++++++++ drivers/ub/ubus/pool.c | 5 ++-- drivers/ub/ubus/pool.h | 2 +- drivers/ub/ubus/port.h | 1 + drivers/ub/ubus/resource.c | 4 +-- drivers/ub/ubus/services/gucd.c | 2 +- drivers/ub/ubus/ubus_config.c | 4 +-- drivers/vfio/ubus/vfio_ub_config.c | 2 +- 9 files changed, 56 insertions(+), 10 deletions(-) diff --git a/drivers/ub/ubus/cap.c b/drivers/ub/ubus/cap.c index 3eca70059195..92d2069b4d5c 100644 --- a/drivers/ub/ubus/cap.c +++ b/drivers/ub/ubus/cap.c @@ -8,6 +8,7 @@ #include "ubus.h" #include "decoder.h" #include "interrupt.h" +#include "cap.h" #define DW_CHECK 3 diff --git a/drivers/ub/ubus/link.c b/drivers/ub/ubus/link.c index 7a3738a3741a..ac87736801ba 100644 --- a/drivers/ub/ubus/link.c +++ b/drivers/ub/ubus/link.c @@ -328,6 +328,49 @@ void ublc_link_up_handle(struct ub_port *port) ub_notify_share_port(port, UB_PORT_EVENT_LINK_UP); } +static void check_entity_disconnected(struct ub_port *port) +{ + struct ub_entity *ent_near = port->uent, *ent_far = port->r_uent, *tmp; + bool is_connected = false; + struct ub_port *port_iter; + u8 val; + + if (port->type == VIRTUAL) + return; + + /* Ensure topo_rank of ent_near is less than topo_rank of ent_far */ + if (ent_near->topo_rank > ent_far->topo_rank) { + tmp = ent_near; + ent_near = ent_far; + ent_far = tmp; + } + + if (ub_entity_test_priv_flag(ent_far, UB_ENTITY_DISCONNECTED)) + return; + + /* Traverse all ports of ent_near and check if they are connected to ent_far */ + for_each_uent_port(port_iter, ent_near) { + if (port_iter->r_uent != ent_far) + continue; + if (ub_port_read_byte(port_iter, + UB_PORT_PHYSICAL_PORT_LINK_STATUS, &val)) { + ub_err(ent_near, "get port[%u] link status failed\n", + port_iter->index); + return; + } + if (val) { + is_connected = true; + break; + } + } + + /* If no connection is detected, mark ent_far as disconnected */ + if (!is_connected) { + ub_info(ent_far, "ub entity is down\n"); + ub_entity_assign_priv_flag(ent_far, UB_ENTITY_DISCONNECTED, true); + } +} + void ublc_link_down_handle(struct ub_port *port) { struct ub_entity *uent = port->uent; @@ -338,6 +381,8 @@ void ublc_link_down_handle(struct ub_port *port) goto link_down_notify; } + check_entity_disconnected(port); + device_lock(&uent->dev); if (ublc_device_is_down(port)) { diff --git a/drivers/ub/ubus/pool.c b/drivers/ub/ubus/pool.c index fce5be54705e..276c854de867 100644 --- a/drivers/ub/ubus/pool.c +++ b/drivers/ub/ubus/pool.c @@ -143,9 +143,8 @@ static int ub_fad_res_init(struct pool_fad *fad, struct ub_entity *uent) goto fail; } - ub_info(uent, "MMIO[%d]: ubba=%dx, size=%#llx, hpa=%#llx, wr_attr=%01u, prefetchable=%01u, order_type=%01u\n", - i, 0, ub_resource_len(uent, i), - uent->zone[i].res.start, 0, 0, 0); + ub_info(uent, "MMIO[%d]: ubba=0x0, size=%#llx, hpa=%#llx, wr_attr=0, prefetchable=0, order_type=0\n", + i, ub_resource_len(uent, i), uent->zone[i].res.start); uent->zone[i].init_succ = 1; } diff --git a/drivers/ub/ubus/pool.h b/drivers/ub/ubus/pool.h index 6d9e68c982f9..2a593bcb0410 100644 --- a/drivers/ub/ubus/pool.h +++ b/drivers/ub/ubus/pool.h @@ -66,7 +66,7 @@ struct port_reset_notify_pld { u32 port_index : 16; }; #define PORT_RESET_NOTIFY_PLD_SIZE 4 - +struct msg_pkt_header; struct ub_entity *ub_get_fad_ent_by_eid(unsigned int eid); bool ub_rsp_msg_init(struct msg_pkt_header *header, u8 status, u32 plen); void ub_pool_rx_msg_handler(struct ub_bus_controller *ubc, void *pkt, u16 len); diff --git a/drivers/ub/ubus/port.h b/drivers/ub/ubus/port.h index 21a2c7d33299..2c07053fc830 100644 --- a/drivers/ub/ubus/port.h +++ b/drivers/ub/ubus/port.h @@ -25,6 +25,7 @@ void ub_ports_unset(struct ub_entity *uent); void ub_notify_share_port(struct ub_port *port, enum ub_port_event type); +int ub_port_read_byte(struct ub_port *port, u32 pos, u8 *val); int ub_port_write_dword(struct ub_port *port, u32 pos, u32 val); bool ub_port_check_link_up(struct ub_port *port); diff --git a/drivers/ub/ubus/resource.c b/drivers/ub/ubus/resource.c index 6e8ceeb9fa93..542b6e6135e8 100644 --- a/drivers/ub/ubus/resource.c +++ b/drivers/ub/ubus/resource.c @@ -512,9 +512,9 @@ int _ub_entity_setup_mmio(struct ub_entity *dev) goto fail; } - ub_info(dev, "MMIO[%d]: ubba=%#lx, size=%#llx, hpa=%#llx, wr attr=%01u, prefetchable=%01u, order type=%01u,\n", + ub_info(dev, "MMIO[%d]: ubba=%#lx, size=%#llx, hpa=%#llx, wr attr=0, prefetchable=0, order type=0,\n", i, dev->zone[i].region.start, ub_resource_len(dev, i), - dev->zone[i].res.start, 0, 0, 0); + dev->zone[i].res.start); dev->zone[i].init_succ = 1; } diff --git a/drivers/ub/ubus/services/gucd.c b/drivers/ub/ubus/services/gucd.c index a7796bcce6aa..4ed4f53b9b7d 100644 --- a/drivers/ub/ubus/services/gucd.c +++ b/drivers/ub/ubus/services/gucd.c @@ -55,7 +55,7 @@ static int ub_component_service_init(struct ub_entity *uent, int service) dev = &sdev->device; dev->bus = &ub_service_bus_type; dev->release = release_service_device; - dev_set_name(dev, "%s:service%03x", ub_name(uent), service); + dev_set_name(dev, "%s:service%03x", ub_name(uent), (u32)service); dev->parent = &uent->dev; ret = device_register(dev); diff --git a/drivers/ub/ubus/ubus_config.c b/drivers/ub/ubus/ubus_config.c index e15bfa8b5abc..a0dea1c39901 100644 --- a/drivers/ub/ubus/ubus_config.c +++ b/drivers/ub/ubus/ubus_config.c @@ -191,8 +191,8 @@ static int ub_sync_cfg_rsp_check(struct ub_entity *uent, rsp_header->msgetah.code); } -void ub_sync_cfg_rsp_handle(struct cfg_msg_pld_rsp *rsp, u8 size, - u64 pos, bool write, u32 *val) +static void ub_sync_cfg_rsp_handle(struct cfg_msg_pld_rsp *rsp, u8 size, + u64 pos, bool write, u32 *val) { #define UB_CFG_REG_SIZE 4 u8 pos_in_reg = pos % UB_CFG_REG_SIZE; diff --git a/drivers/vfio/ubus/vfio_ub_config.c b/drivers/vfio/ubus/vfio_ub_config.c index 4be3c099a32a..9b204d6300ad 100644 --- a/drivers/vfio/ubus/vfio_ub_config.c +++ b/drivers/vfio/ubus/vfio_ub_config.c @@ -904,7 +904,7 @@ static int vfio_ub_init_slice_config_info(struct vfio_ub_core_device *vdev, used_size = (val >> SZ_4) << SZ_2; if (used_size == 0 || used_size > UB_SLICE_SZ) { - ub_err(vdev->uent, "invalid cap_id[%#x] used_size[%#x]\n", cap_id, used_size); + ub_err(vdev->uent, "invalid cap_id[%#x] used_size[%d]\n", cap_id, used_size); return -EINVAL; } -- Gitee From bc23a2edb725d43dd1912d04fe1bfcb4dea8adeb Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Wed, 21 Jan 2026 15:59:47 +0800 Subject: [PATCH 127/214] ub:hisi-ubus: Add create bus instance ummu bypass ANBZ: #45460 commit b5b85a132c0802e3c6f79e09f9472ce94b7ed66d openEuler. Add create bus instance ummu bypass Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/vendor/hisilicon/vdm.c | 39 ++++++++++++++++++++++++++ drivers/ub/ubus/vendor/hisilicon/vdm.h | 15 ++++++++++ 2 files changed, 54 insertions(+) diff --git a/drivers/ub/ubus/vendor/hisilicon/vdm.c b/drivers/ub/ubus/vendor/hisilicon/vdm.c index 8abd4423a7e5..2f7425029b67 100644 --- a/drivers/ub/ubus/vendor/hisilicon/vdm.c +++ b/drivers/ub/ubus/vendor/hisilicon/vdm.c @@ -394,6 +394,43 @@ static u8 ub_idevice_ue_rls_handler(struct ub_bus_controller *ubc, struct vdm_ms return status; } +static u8 ub_vdm_create_bi_bypass_ummu(struct ub_bus_controller *ubc, struct vdm_msg_pkt *pkt) +{ + struct create_bi_bypass_pld *pld = &pkt->bi_bypass_pld; + struct msg_pkt_header *header = &pkt->header; + u8 status = UB_MSG_RSP_SUCCESS; + struct msg_info q_info = {}; + enum eid_type type; + bool local; + u32 size; + int ret; + + if (pld->m) + type = EID_NONE; + else + type = EID_BYPASS; + + ret = ub_msg_bus_instance_create(ubc, pld->guid, pld->eid[0], pld->upi, + type); + if (ret) { + dev_err(&ubc->dev, "bi create msg failed ret[%d]\n", ret); + status = UB_MSG_RSP_EXEC_ENOEXEC; + } + + size = (MSG_PKT_HEADER_SIZE + VENDOR_GUID_PLD_SIZE + + VDM_MSG_DW0_PLD_SIZE) << MSG_REQ_SIZE_OFFSET; + local = ub_rsp_msg_init(header, status, + VENDOR_GUID_PLD_SIZE + VDM_MSG_DW0_PLD_SIZE); + message_info_init(&q_info, local ? ubc->uent : NULL, pkt, NULL, size); + ret = message_response(ubc->mdev, &q_info, header->msgetah.code); + if (ret) { + dev_err(&ubc->dev, "create bi bypass rsp msg failed, ret=%d\n", ret); + status = UB_MSG_RSP_EXEC_ENOEXEC; + } + + return status; +} + struct opcode_func_map idev_func_mapping[] = { { VDM_SUB_OPCODE_MUE_REG, MSG_IDEV_MUE_REG_SIZE, "MUE register", ub_idevice_pue_add_handler, IDEV_MUE_REG_PLD_TOTAL_SIZE }, @@ -403,6 +440,8 @@ struct opcode_func_map idev_func_mapping[] = { ub_idevice_ue_add_handler, IDEV_UE_REG_PLD_TOTAL_SIZE }, { VDM_SUB_OPCODE_UE_RLS, MSG_IDEV_UE_RLS_SIZE, "UE release", ub_idevice_ue_rls_handler, IDEV_UE_RLS_PLD_TOTAL_SIZE }, + { VDM_BI_CREATE_BYPASS_UMMU, VDM_BI_CREATE_BYPASS_SIZE, "Vdm bi create", + ub_vdm_create_bi_bypass_ummu, VDM_BI_CREATE_BYPASS_UMMU_PLD_SIZE }, }; static int ub_vdm_msg_info_handle(struct ub_bus_controller *ubc, diff --git a/drivers/ub/ubus/vendor/hisilicon/vdm.h b/drivers/ub/ubus/vendor/hisilicon/vdm.h index 725288d6abac..8b3b4e4f616f 100644 --- a/drivers/ub/ubus/vendor/hisilicon/vdm.h +++ b/drivers/ub/ubus/vendor/hisilicon/vdm.h @@ -104,6 +104,18 @@ struct idev_ue_rls_pld { }; #define IDEV_UE_RLS_PLD_TOTAL_SIZE 36 +struct create_bi_bypass_pld { + /* DW1~DW4 */ + u32 guid[UB_GUID_DW_NUM]; + /* DW5~DW8 */ + u32 eid[4]; + /* DW9 */ + u32 upi : 15; + u32 m : 1; + u32 rsvd2 : 16; +}; +#define VDM_BI_CREATE_BYPASS_UMMU_PLD_SIZE 48 + struct port_reset_pld { /* DW1 */ u16 rsvd; @@ -119,6 +131,8 @@ struct port_reset_pld { (MSG_PKT_HEADER_SIZE + IDEV_UE_REG_PLD_TOTAL_SIZE) #define MSG_IDEV_UE_RLS_SIZE \ (MSG_PKT_HEADER_SIZE + IDEV_UE_RLS_PLD_TOTAL_SIZE) +#define VDM_BI_CREATE_BYPASS_SIZE \ + (MSG_PKT_HEADER_SIZE + VDM_BI_CREATE_BYPASS_UMMU_PLD_SIZE) #define VDM_PORT_RESET_SIZE \ (MSG_PKT_HEADER_SIZE + VDM_PORT_RESET_PLD_SIZE) @@ -135,6 +149,7 @@ struct vdm_msg_pkt { struct idev_pue_rls_pld pd_rls_pld; struct idev_ue_reg_pld vd_reg_pld; struct idev_ue_rls_pld vd_rls_pld; + struct create_bi_bypass_pld bi_bypass_pld; }; }; -- Gitee From e6a04c98e9e32fed17a66f1236f458478d5d928a Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Sat, 31 Jan 2026 11:45:28 +0800 Subject: [PATCH 128/214] ub:ubus: Delete unused functions ANBZ: #45460 commit a8f7b8affe71b5a91f320e07973e54e816496e3a openEuler. Delete unused functions Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/ubus_driver.c | 22 ---------------- drivers/ub/ubus/ubus_entity.c | 42 ------------------------------- include/ub/ubus/ubus.h | 47 ----------------------------------- 3 files changed, 111 deletions(-) diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index 6d5594156c3d..c57ebb10bef5 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -180,28 +180,6 @@ ub_get_dev_by_id_inner(struct ub_entity *from, const void *data, return fdev; } -static int ub_entity_match_by_id(struct device *dev, const void *data) -{ - const struct ub_device_id *id = (const struct ub_device_id *)data; - struct ub_entity *pue = to_ub_entity(dev); - - if (ub_match_one_device(id, pue)) - return 1; - return 0; -} - -struct ub_entity *ub_get_entity(unsigned int vendor, unsigned int device, - struct ub_entity *from) -{ - struct ub_device_id id = { - .vendor = vendor, - .device = device, - }; - - return ub_get_dev_by_id_inner(from, &id, ub_entity_match_by_id); -} -EXPORT_SYMBOL_GPL(ub_get_entity); - static int ub_entity_match_by_guid(struct device *dev, const void *data) { const struct ub_guid *guid = (const struct ub_guid *)data; diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index e823cc6800f1..1c4aba1a724b 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -811,28 +811,6 @@ static void ub_disable_mues(struct ub_entity *pue) ub_disable_ent(mue); } -void ub_disable_ues(struct ub_entity *mue); -static int ub_enable_ues(struct ub_entity *mue, int nums) -{ - int ret; - int i; - - if (nums > mue->total_ues) - return -EINVAL; - - for (i = 0; i < nums; i++) { - ret = ub_enable_ent(mue, mue->uem.start_entity_idx + i, 0, - NULL); - if (ret) - goto failed; - } - mue->num_ues = nums; - return 0; -failed: - ub_disable_ues(mue); - return ret; -} - void ub_disable_ues(struct ub_entity *mue) { struct ub_entity *ue, *tmp; @@ -920,26 +898,6 @@ int ub_disable_ue(struct ub_entity *pue, int entity_idx) } EXPORT_SYMBOL_GPL(ub_disable_ue); -bool ub_get_entity_flex_en(void) -{ - return entity_flex_en; -} -EXPORT_SYMBOL_GPL(ub_get_entity_flex_en); - -int ub_enable_entities(struct ub_entity *uent, int nums) -{ - if (!uent) - return -EINVAL; - - if (!uent->is_mue) { - ub_err(uent, "It's not mue.\n"); - return -EINVAL; - } - - return ub_enable_ues(uent, nums); -} -EXPORT_SYMBOL_GPL(ub_enable_entities); - void ub_disable_entities(struct ub_entity *uent) { if (!uent) diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index be2758aa4fc4..874eace9271c 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -548,22 +548,6 @@ struct ub_entity *ub_get_ent_by_uent_num(unsigned int uent_num); */ struct ub_entity *ub_get_ent_by_guid(const struct ub_guid *guid); -/** - * ub_get_entity() - Searching for UB entities by Vendor and entity ID. - * @vendor: Vendor ID. - * @entity: entity ID. - * @from: Previous UB entity found in search, or %NULL for new search. - * - * Traverse the UB bus entity linked list and search for the entity with - * the target Vendor and entity ID. You need to call ub_entity_put() - * after using it. - * - * Context: Any context. - * Return: The entity found, or NULL if not found. - */ -struct ub_entity *ub_get_entity(unsigned int vendor, unsigned int entity, - struct ub_entity *from); - /** * ub_entity_enable() - Enable or disable ub entity * @uent: UB entity. @@ -596,20 +580,6 @@ int ub_set_user_info(struct ub_entity *uent); */ void ub_unset_user_info(struct ub_entity *uent); -/** - * ub_enable_entities() - Enable UEs of MUE in batches. - * @pue: UB MUE. - * @nums: Number of enabled entities. - * - * Create and initialize UEs in batches and add to the system. - * - * Context: Any context. - * Return: 0 if success, or %-EINVAL if @pue type is not MUE or nums over - * MUE's total UE nums, or %-ENOMEM if the system is out of memory, - * or other failed negative values. - */ -int ub_enable_entities(struct ub_entity *pue, int nums); - /** * ub_disable_entities() - Disable UEs of MUE in batches. * @pue: UB MUE. @@ -649,16 +619,6 @@ int ub_enable_ue(struct ub_entity *pue, int entity_idx); */ int ub_disable_ue(struct ub_entity *pue, int entity_idx); -/** - * ub_get_entity_flex_en() - Acquire the capability to enable flexible functionality. - * - * Return whether flexible enabling of entity is supported. - * - * Context: Any context. - * Return: true if support, or false if not support. - */ -bool ub_get_entity_flex_en(void); - /** * ub_get_dst_eid() - Obtain the Dest EID of the entity. * @uent: UB entity. @@ -1049,9 +1009,6 @@ static inline struct ub_entity *ub_get_ent_by_uent_num(unsigned int uent_num) { return NULL; } static inline struct ub_entity *ub_get_ent_by_guid(const struct ub_guid *guid) { return NULL; } -static inline struct ub_entity * -ub_get_entity(unsigned int vendor, unsigned int entity, struct ub_entity *from) -{ return NULL; } static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) { return NULL; } static inline void ub_entity_put(struct ub_entity *uent) {} @@ -1059,15 +1016,11 @@ static inline void ub_entity_enable(struct ub_entity *uent, u8 enable) {} static inline int ub_set_user_info(struct ub_entity *uent) { return -ENODEV; } static inline void ub_unset_user_info(struct ub_entity *uent) {} -static inline int ub_enable_entities(struct ub_entity *pue, int nums) -{ return -ENODEV; } static inline void ub_disable_entities(struct ub_entity *pue) {} static inline int ub_enable_ue(struct ub_entity *pue, int entity_idx) { return -ENODEV; } static inline int ub_disable_ue(struct ub_entity *pue, int entity_idx) { return -ENODEV; } -static inline bool ub_get_entity_flex_en(void) -{ return false; } static inline int ub_get_dst_eid(struct ub_entity *uent) { return -ENODEV; } static inline void __iomem * -- Gitee From 4a50228d3f31fa12ff559c88343962d5808cbea0 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Wed, 25 Feb 2026 11:43:39 +0800 Subject: [PATCH 129/214] ub:ubus: Add port num check ANBZ: #45460 commit ad02ea4ea0d788b320de5b5fa20dc33ce0cbb706 openEuler. Add port num check Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/port.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/ub/ubus/port.c b/drivers/ub/ubus/port.c index f4d91d0e9d99..f46d7450df60 100644 --- a/drivers/ub/ubus/port.c +++ b/drivers/ub/ubus/port.c @@ -624,6 +624,12 @@ int ub_register_share_port(struct ub_entity *entity, u16 port_id, } } + if (port_id >= parent->port_nums) { + ub_err(parent, "port id %u exceeds port num %u\n", port_id, + parent->port_nums); + return -EINVAL; + } + port = parent->ports + port_id; if (is_idev(entity) && !port->shareable) { ub_err(parent, "port%u isn't shareable\n", port_id); -- Gitee From 60bbc7eb990cf11563ceef4eb8bc2f42e237e943 Mon Sep 17 00:00:00 2001 From: Yiyu Liu Date: Thu, 26 Feb 2026 10:34:46 +0800 Subject: [PATCH 130/214] ub:ubus: Black Box Development ANBZ: #45460 commit ab9a945e6625387beed9578a7530f373ec6383bb openEuler. Black Box Development Signed-off-by: Yiyu Liu Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/ub_black_box.c | 91 ++++++++++++++++++++++++++++++++++ drivers/ub/ubus/ub_black_box.h | 19 +++++++ 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/ub_black_box.c create mode 100644 drivers/ub/ubus/ub_black_box.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 96efd80d502f..569718ac74de 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o ioctl.o eu.o link.o -ubus-y += instance.o pool.o memory.o +ubus-y += instance.o pool.o memory.o ub_black_box.o ubus-y += services/ras.o services/service.o services/gucd.o ubus-y += services/hotplug/hotplug_core.o services/hotplug/hotplug_ctrl.o diff --git a/drivers/ub/ubus/ub_black_box.c b/drivers/ub/ubus/ub_black_box.c new file mode 100644 index 000000000000..ee064165d992 --- /dev/null +++ b/drivers/ub/ubus/ub_black_box.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2026. All rights reserved. + */ + +#include + +#include "ub_black_box.h" + +ub_fault_record ub_fault_record_func; +static DEFINE_MUTEX(fault_record_mutex); + +void ub_fault_log(struct ub_entity *uent, u32 event_id, void *data) +{ + int ret; + + /* The data can be a null pointer, the above interface may pass NULL */ + if (!uent) { + pr_err("uent is null\n"); + return; + } + + mutex_lock(&fault_record_mutex); + if (!ub_fault_record_func) { + ub_err(uent, "ub_fault_record_func is not registered\n"); + mutex_unlock(&fault_record_mutex); + return; + } + + ret = ub_fault_record_func(uent->ubc->ctl_no, uent_device(uent), + uent->entity_idx, event_id, data); + if (ret) + ub_err(uent, "ub_fault_record_func execute error, ret=%d\n", + ret); + + mutex_unlock(&fault_record_mutex); +} +EXPORT_SYMBOL_GPL(ub_fault_log); + +int ub_fault_register(ub_fault_record record) +{ + if (!record) + return -EINVAL; + + mutex_lock(&fault_record_mutex); + ub_fault_record_func = record; + mutex_unlock(&fault_record_mutex); + pr_info("ub ub_fault_record register successfully\n"); + + return 0; +} +EXPORT_SYMBOL_GPL(ub_fault_register); + +void ub_fault_unregister(void) +{ + mutex_lock(&fault_record_mutex); + ub_fault_record_func = NULL; + mutex_unlock(&fault_record_mutex); +} +EXPORT_SYMBOL_GPL(ub_fault_unregister); + +int ub_fault_vdm(u32 ctl_no, struct ub_vdm_pld *vdm_pld) +{ + struct ub_bus_controller *ubc = NULL; + struct ub_bus_controller *tmp; + int ret; + + if (!vdm_pld) { + pr_err("vdm_pld is null\n"); + return -EINVAL; + } + + list_for_each_entry(tmp, &ubc_list, node) { + if (tmp->ctl_no == ctl_no) { + ubc = tmp; + break; + } + } + + if (!ubc) { + pr_err("ctl_no %u not found in ubc_list\n", ctl_no); + return -ENODEV; + } + + ret = ub_vdm_message(ubc->uent, vdm_pld); + if (ret) + pr_err("ub_vdm_message execute error, ret=%d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(ub_fault_vdm); diff --git a/drivers/ub/ubus/ub_black_box.h b/drivers/ub/ubus/ub_black_box.h new file mode 100644 index 000000000000..3e515157ac1a --- /dev/null +++ b/drivers/ub/ubus/ub_black_box.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2026. All rights reserved. + */ + +#ifndef __UB_BLACK_BOX_H__ +#define __UB_BLACK_BOX_H__ + +#include "ubus.h" + +typedef int (*ub_fault_record)(u32 ctl_no, u32 device_id, u32 entity_idx, + u32 event_id, void *data); + +void ub_fault_log(struct ub_entity *uent, u32 event_id, void *data); +int ub_fault_register(ub_fault_record record); +void ub_fault_unregister(void); +int ub_fault_vdm(u32 ctl_no, struct ub_vdm_pld *vdm_pld); + +#endif /* __UB_BLACK_BOX_H__ */ -- Gitee From de3a84a4a8d889e1e47a8969fa4e7d175edb6e98 Mon Sep 17 00:00:00 2001 From: Ben Li Date: Thu, 26 Feb 2026 15:50:18 +0800 Subject: [PATCH 131/214] ub:ubus: injected route event ANBZ: #45460 commit 7e956e9ba359b238ad02c8bb8fd8bc39223819af openEuler. injected route event Signed-off-by: Ben Li Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/memory.h | 3 +- drivers/ub/ubus/vendor/hisilicon/memory.c | 100 +++++++++++++++--- .../ub/ubus/vendor/hisilicon/memory_trace.h | 10 +- 3 files changed, 94 insertions(+), 19 deletions(-) diff --git a/drivers/ub/ubus/memory.h b/drivers/ub/ubus/memory.h index 70cf2b53958f..ab6ad0e72ac8 100644 --- a/drivers/ub/ubus/memory.h +++ b/drivers/ub/ubus/memory.h @@ -20,7 +20,8 @@ void ub_mem_uninit_usi(struct ub_entity *uent); struct ub_mem_ras_err_info { enum ras_err_type type; - u64 hpa; + u64 val0; /* addr or transaction id + port bitmap or 0*/ + u64 val1; /* port bitmap or 0 */ }; struct ub_mem_ras_ctx { diff --git a/drivers/ub/ubus/vendor/hisilicon/memory.c b/drivers/ub/ubus/vendor/hisilicon/memory.c index 0c1d06ca0735..5f1bacb8a3a0 100644 --- a/drivers/ub/ubus/vendor/hisilicon/memory.c +++ b/drivers/ub/ubus/vendor/hisilicon/memory.c @@ -29,6 +29,21 @@ #define hpa_gen(addr_h, addr_l) (((u64)(addr_h) << 32) | (addr_l)) +#define RAS_VAL_COUNT 3 +#define RAS_VAL_IDX_VAL0 0 +#define RAS_VAL_IDX_VAL1 1 +#define RAS_VAL_IDX_VAL2 2 + +#define UB_MEM_PORT_WARNING 18 +#define UB_MEM_PORT_RECOVERY 19 + +#define WARNING_OFFSET_VAL0_LOW 0 +#define WARNING_OFFSET_VAL0_HIGH 1 +#define WARNING_OFFSET_VAL1_LOW 2 +#define WARNING_OFFSET_VAL1_HIGH 3 +#define RECOVERY_OFFSET_VAL0_LOW 4 +#define RECOVERY_OFFSET_VAL0_HIGH 5 + static u8 ub_mem_num; struct ub_mem_decoder { @@ -108,11 +123,12 @@ static const struct ub_mem_device_ops device_ops = { }; static int save_ras_err_info(struct ub_mem_device *mem_device, - enum ras_err_type type, u64 hpa) + enum ras_err_type type, u64 val0, u64 val1) { struct ub_mem_ras_err_info err_info = { .type = type, - .hpa = hpa, + .val0 = val0, + .val1 = val1, }; if (!kfifo_put(&mem_device->ras_ctx.ras_fifo, err_info)) { @@ -129,15 +145,28 @@ static irqreturn_t hi_mem_ras_isr(int irq, void *context) struct ub_mem_ras_ctx *ras_ctx = &ubc->mem_device->ras_ctx; struct ub_mem_ras_err_info err_info; ubmem_ras_handler handler; + u64 ctl_no = ubc->ctl_no; + u64 vals[RAS_VAL_COUNT]; int ret; handler = ub_mem_ras_handler_get(); while (kfifo_get(&ras_ctx->ras_fifo, &err_info)) { trace_mem_ras_event(ubc->mem_device, &err_info); pr_info("ras: type=%u\n", err_info.type); - if (handler) { - ret = handler(err_info.hpa, err_info.type); - WARN_ON(ret); + if (err_info.type == UB_MEM_PORT_WARNING || + err_info.type == UB_MEM_PORT_RECOVERY) { + vals[RAS_VAL_IDX_VAL0] = err_info.val0; + vals[RAS_VAL_IDX_VAL1] = err_info.val1; + vals[RAS_VAL_IDX_VAL2] = ctl_no; + if (handler) { + ret = handler((u64)vals, err_info.type); + WARN_ON(ret); + } + } else { + if (handler) { + ret = handler(err_info.val0, err_info.type); + WARN_ON(ret); + } } } @@ -166,21 +195,64 @@ static int err_type_bitmap[] = { [MAR_TIMEOUT_ERR] = 23, [MAR_ILLEGAL_ACCESS_ERR] = 9, [REMOTE_READ_DATA_ERR_OR_WRITE_RESPONSE_ERR] = 11, + /* DEVICE_RAS_STATUS_4 need save transaction id and port bitmap */ + [UB_MEM_PORT_WARNING] = 31, + /* DEVICE_RAS_STATUS_4 need save transaction id */ + [UB_MEM_PORT_RECOVERY] = 30, }; +static int save_injected_route_info(struct ub_bus_controller *ubc, + struct hi_ubmem_event *info, + unsigned long status4_bitmap) +{ + u32 addr_h, addr_l; + int i, index = 0; + u64 val0, val1; + int ret; + + if (test_bit(err_type_bitmap[UB_MEM_PORT_WARNING], &status4_bitmap)) { + i = UB_MEM_PORT_WARNING; + addr_h = info->err_addr[index + WARNING_OFFSET_VAL0_HIGH]; + addr_l = info->err_addr[index + WARNING_OFFSET_VAL0_LOW]; + val0 = hpa_gen(addr_h, addr_l); + addr_h = info->err_addr[index + WARNING_OFFSET_VAL1_HIGH]; + addr_l = info->err_addr[index + WARNING_OFFSET_VAL1_LOW]; + val1 = hpa_gen(addr_h, addr_l); + ret = save_ras_err_info(ubc->mem_device, (enum ras_err_type)i, val0, val1); + if (ret) + return ret; + } + + if (test_bit(err_type_bitmap[UB_MEM_PORT_RECOVERY], &status4_bitmap)) { + i = UB_MEM_PORT_RECOVERY; + addr_h = info->err_addr[index + RECOVERY_OFFSET_VAL0_HIGH]; + addr_l = info->err_addr[index + RECOVERY_OFFSET_VAL0_LOW]; + val0 = hpa_gen(addr_h, addr_l); + ret = save_ras_err_info(ubc->mem_device, (enum ras_err_type)i, val0, 0); + if (ret) + return ret; + } + + return 0; +} + static int save_ras_err_info_all(struct ub_bus_controller *ubc, struct hi_ubmem_event *info) { unsigned long status3_bitmap = (unsigned long)info->device_ras_status3; unsigned long status4_bitmap = (unsigned long)info->device_ras_status4; u32 addr_h, addr_l; int ret = -EINVAL; - u64 hpa = 0; - int index; - int i; + int index, i; + u64 val0 = 0; + + /* receives and processes one injected route event at a time */ + if (test_bit(err_type_bitmap[UB_MEM_PORT_WARNING], &status4_bitmap) || + test_bit(err_type_bitmap[UB_MEM_PORT_RECOVERY], &status4_bitmap)) + return save_injected_route_info(ubc, info, status4_bitmap); for (i = UB_MEM_ATOMIC_DATA_ERR; i <= UB_MEM_READ_DATA_RESPERR; i++) { if (test_bit(err_type_bitmap[i], &status3_bitmap)) { - ret = save_ras_err_info(ubc->mem_device, (enum ras_err_type)i, hpa); + ret = save_ras_err_info(ubc->mem_device, (enum ras_err_type)i, val0, 0); if (ret) return ret; } @@ -188,7 +260,7 @@ static int save_ras_err_info_all(struct ub_bus_controller *ubc, struct hi_ubmem_ for (i = MAR_FLUX_INT_ERR; i <= RSP_BKPRE_OVER_TIMEOUT_ERR; i++) { if (test_bit(err_type_bitmap[i], &status4_bitmap)) { - ret = save_ras_err_info(ubc->mem_device, (enum ras_err_type)i, hpa); + ret = save_ras_err_info(ubc->mem_device, (enum ras_err_type)i, val0, 0); if (ret) return ret; } @@ -199,8 +271,8 @@ static int save_ras_err_info_all(struct ub_bus_controller *ubc, struct hi_ubmem_ index = MAR_ERR_ADDR_SIZE * (i - MAR_NEAR_AUTH_FAIL_ERR); addr_h = info->err_addr[index + 1]; addr_l = info->err_addr[index]; - hpa = hpa_gen(addr_h, addr_l); - ret = save_ras_err_info(ubc->mem_device, (enum ras_err_type)i, hpa); + val0 = hpa_gen(addr_h, addr_l); + ret = save_ras_err_info(ubc->mem_device, (enum ras_err_type)i, val0, 0); if (ret) return ret; } @@ -210,7 +282,7 @@ static int save_ras_err_info_all(struct ub_bus_controller *ubc, struct hi_ubmem_ if (test_bit(err_type_bitmap[MAR_NOPORT_VLD_INT_ERR], &status4_bitmap) && !test_bit(err_type_bitmap[MAR_NEAR_AUTH_FAIL_ERR], &status4_bitmap)) { i = MAR_NOPORT_VLD_INT_ERR; - ret = save_ras_err_info(ubc->mem_device, (enum ras_err_type)i, hpa); + ret = save_ras_err_info(ubc->mem_device, (enum ras_err_type)i, val0, 0); } return ret; @@ -236,7 +308,7 @@ static irqreturn_t hi_mem_ras_irq(int irq, void *context) event_cnt = pld.rsp.event_num; if (event_cnt == 0 || event_cnt > MEM_EVENT_MAX_NUM) { - dev_err(&ubc->dev, "event_cnt [%u] is invalid\n", event_cnt); + dev_warn(&ubc->dev, "event_cnt [%u] is invalid\n", event_cnt); return IRQ_HANDLED; } diff --git a/drivers/ub/ubus/vendor/hisilicon/memory_trace.h b/drivers/ub/ubus/vendor/hisilicon/memory_trace.h index 9204d94aa6b8..1de776fdd97b 100644 --- a/drivers/ub/ubus/vendor/hisilicon/memory_trace.h +++ b/drivers/ub/ubus/vendor/hisilicon/memory_trace.h @@ -20,19 +20,21 @@ TRACE_EVENT(mem_ras_event, __field(u32, eid) __field(u32, cna) __field(u8, type) - __field(u64, hpa) + __field(u64, val0) + __field(u64, val1) ), TP_fast_assign( __entry->eid = device->uent->eid; __entry->cna = device->uent->cna; __entry->type = (u8)info->type; - __entry->hpa = info->hpa; + __entry->val0 = info->val0; + __entry->val1 = info->val1; ), TP_printk( - "%u-%u-%u-%llu", __entry->eid, __entry->cna, - __entry->type, __entry->hpa + "%u-%u-%u-%llu-%llu", __entry->eid, __entry->cna, + __entry->type, __entry->val0, __entry->val1 ) ); -- Gitee From f6a28b574810e04549756073c3e6e7e8feb02c6f Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Fri, 27 Feb 2026 14:41:21 +0800 Subject: [PATCH 132/214] ub:hisi-ubus: fix bug of security smatch ANBZ: #45460 commit b889080ef7b6035d229e0250725138b62f2a4102 openEuler. fix bug of security smatch Signed-off-by: Yuhao Xiang Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/decoder.c | 2 +- drivers/ub/ubus/enum.c | 5 ++++- drivers/ub/ubus/ubus_driver.c | 4 +++- drivers/ub/ubus/ubus_entity.c | 1 + drivers/ub/ubus/vendor/hisilicon/eu-table.c | 6 +++--- drivers/ub/ubus/vendor/hisilicon/hisi-decoder.c | 2 +- 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/ub/ubus/decoder.c b/drivers/ub/ubus/decoder.c index 0a2a06114429..f678641d84a5 100644 --- a/drivers/ub/ubus/decoder.c +++ b/drivers/ub/ubus/decoder.c @@ -519,7 +519,7 @@ int ub_decoder_map(struct ub_decoder *decoder, struct decoder_map_info *info) } ubc = decoder->uent->ubc; - if (!ubc->ops->decoder_map && !ubc->ops->decoder_unmap) { + if (!ubc->ops->decoder_map || !ubc->ops->decoder_unmap) { pr_err("decoder_map or decoder_unmap ops not exist\n"); return -EINVAL; } diff --git a/drivers/ub/ubus/enum.c b/drivers/ub/ubus/enum.c index 656dcf72d35b..6f9b04d1e186 100644 --- a/drivers/ub/ubus/enum.c +++ b/drivers/ub/ubus/enum.c @@ -499,13 +499,16 @@ static int ub_enum_ent(struct ub_entity *uent, void *buf) if (slice_id == 0) { if (!info.si || !info.pi || !info.pn || !info.ci) { dev_err(dev, "tlv si/pi/pn/ci NULL\n"); + ret = -EFAULT; goto err; } uent->class_code = info.ci->class_code; ub_entity_type_init(uent); - if (!ub_type_valid(uent, !uent->topo_rank)) + if (!ub_type_valid(uent, !uent->topo_rank)) { + ret = -EINVAL; goto err; + } total_slice = info.si->total_slice; total_num_ports = (int)info.pn->total_num_ports; diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index c57ebb10bef5..cbeefff5bf74 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -650,8 +650,10 @@ static int ub_host_probe(void) if (ret) goto cdev_fail; - if (!manage_subsystem_ops || !manage_subsystem_ops->ras_handler_probe) + if (!manage_subsystem_ops || !manage_subsystem_ops->ras_handler_probe) { + ret = -EFAULT; goto error_register_fail; + } ret = manage_subsystem_ops->ras_handler_probe(); if (ret) diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index 1c4aba1a724b..5cb47a88c147 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -311,6 +311,7 @@ int ub_setup_ent(struct ub_entity *uent) uent_num = ub_entity_num_alloc(); if (uent_num < 0) { ub_err(uent, "alloc dev uent_num failed, ret=%d\n", uent_num); + ret = -ENOSPC; goto free_eid; } diff --git a/drivers/ub/ubus/vendor/hisilicon/eu-table.c b/drivers/ub/ubus/vendor/hisilicon/eu-table.c index 3004093b52d8..b759c372afc0 100644 --- a/drivers/ub/ubus/vendor/hisilicon/eu-table.c +++ b/drivers/ub/ubus/vendor/hisilicon/eu-table.c @@ -131,7 +131,7 @@ static ssize_t hi_eu_table_info_write(struct file *file, struct ub_bus_controller *ubc = (struct ub_bus_controller *)file->f_inode->i_private; #define BUF_SIZE 8 - int len, bit, fields, ret; + int len, bit, ret; char buf[BUF_SIZE]; if (*loff != 0) @@ -146,8 +146,8 @@ static ssize_t hi_eu_table_info_write(struct file *file, buf[len] = '\0'; ret = kstrtoint(buf, 10, &bit); - if (fields != 1) - return -EINVAL; + if (ret < 0) + return ret; if (bit >= (int)ubc->uent->eu_table->entries) { pr_err("eu table query bit(%d) invalid\n", bit); diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.c b/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.c index 00f958696b66..b9eec4d8111a 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.c +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.c @@ -575,7 +575,7 @@ static int handle_table(struct ub_decoder *decoder, struct range_table_entry *range_tlb_entry; u64 offset, rollback_size; u32 table_idx; - int ret; + int ret = 0; for (offset = 0; offset < info->size;) { rollback_size = offset; -- Gitee From 8855941061533c774f7db5c19525ae9a78935b2a Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Thu, 15 Jan 2026 10:06:13 +0800 Subject: [PATCH 133/214] ub:hisi-ubus: Use GFP_ATOMIC when alloc memory ANBZ: #45460 commit e564f20a9c4d630ccfe8303f101c3e05e955e145 openEuler. When allocating memory, use GFP_ATOMIC instead of GFP_KERNEL. Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/msg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/ub/ubus/msg.c b/drivers/ub/ubus/msg.c index 34b2831bd8bf..72a89b522cd4 100644 --- a/drivers/ub/ubus/msg.c +++ b/drivers/ub/ubus/msg.c @@ -366,11 +366,11 @@ int ub_vdm_message(struct ub_entity *uent, struct ub_vdm_pld *vdm_pld) return -EINVAL; } - req_pkt = kzalloc(MSG_PKT_HEADER_SIZE + vdm_pld->req_pld_len, GFP_KERNEL); + req_pkt = kzalloc(MSG_PKT_HEADER_SIZE + vdm_pld->req_pld_len, GFP_ATOMIC); if (!req_pkt) return -ENOMEM; - rsp_pkt = kzalloc(MSG_PKT_HEADER_SIZE + vdm_pld->rsp_buf_len, GFP_KERNEL); + rsp_pkt = kzalloc(MSG_PKT_HEADER_SIZE + vdm_pld->rsp_buf_len, GFP_ATOMIC); if (!rsp_pkt) { kfree(req_pkt); return -ENOMEM; -- Gitee From 7878cbf009bb796128c1f31ac473287bcba23c17 Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Thu, 12 Feb 2026 16:53:35 +0800 Subject: [PATCH 134/214] ub:ubus: Avoid getting empty uent from ubc ANBZ: #45460 commit 3eda85dfaa4b00c0444668bd8b30148172e9bdfc openEuler. Avoid getting empty uent from ubc Signed-off-by: Yuhao Xiang Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/ubus_controller.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/ub/ubus/ubus_controller.c b/drivers/ub/ubus/ubus_controller.c index 5bd29cbbe79d..007951070766 100644 --- a/drivers/ub/ubus/ubus_controller.c +++ b/drivers/ub/ubus/ubus_controller.c @@ -55,6 +55,11 @@ struct ub_bus_controller *ub_find_bus_controller_by_cna(u32 cna) { struct ub_bus_controller *ubc; + if (!get_ub_manage_subsystem_ops()) { + pr_err("manage subsystem ops is null\n"); + return NULL; + } + list_for_each_entry(ubc, &ubc_list, node) if (ubc->uent->cna == cna) return ubc; -- Gitee From 7337547a1e9728aa88baeffb0e5261ba15ba2b0b Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Mon, 2 Mar 2026 19:09:04 +0800 Subject: [PATCH 135/214] ub:ubus: protect iterator ue_list and mue_list with ub_bus_sem ANBZ: #45460 commit 874642f9fd02f00c1385dda268a938d621333238 openEuler. protect iterator ue_list and mue_list with ub_bus_sem. Signed-off-by: Yahui Liu Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/sysfs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubus/sysfs.c b/drivers/ub/ubus/sysfs.c index 2e997c5c5f1a..75e7b6c5a4c1 100644 --- a/drivers/ub/ubus/sysfs.c +++ b/drivers/ub/ubus/sysfs.c @@ -6,11 +6,12 @@ #define pr_fmt(fmt) "ubus sysfs: " fmt #include "ubus.h" -#include "sysfs.h" #include "ubus_entity.h" #include "instance.h" #include "port.h" #include "resource.h" +#include "ubus_driver.h" +#include "sysfs.h" static inline void ub_resource_to_user(const struct ub_entity *dev, int res_id, const struct resource *rsrc, @@ -673,8 +674,10 @@ static ssize_t ue_list_show(struct device *dev, struct device_attribute *attr, struct ub_entity *ent; int cnt = 0; + down_read(&ub_bus_sem); list_for_each_entry(ent, &uent->ue_list, node) cnt += sysfs_emit_at(buf, cnt, "%s\n", dev_name(&ent->dev)); + up_read(&ub_bus_sem); return cnt; } @@ -687,8 +690,10 @@ static ssize_t mue_list_show(struct device *dev, struct device_attribute *attr, struct ub_entity *ent; int cnt = 0; + down_read(&ub_bus_sem); list_for_each_entry(ent, &uent->mue_list, node) cnt += sysfs_emit_at(buf, cnt, "%s\n", dev_name(&ent->dev)); + up_read(&ub_bus_sem); return cnt; } -- Gitee From 8511f0f7febdbaa825f9b1c49fc9c20f3bf8def4 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Tue, 3 Mar 2026 10:07:13 +0800 Subject: [PATCH 136/214] change bus instance create EID_BYPASS ANBZ: #45460 commit 4cadf2b8a59c2025f767cd3812462b6a6e9d9ae3 openEuler. change bus instance create EID_BYPASS Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/instance.c | 8 ++++---- drivers/ub/ubus/pool.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/ub/ubus/instance.c b/drivers/ub/ubus/instance.c index c7829bff236a..2854765a5fae 100644 --- a/drivers/ub/ubus/instance.c +++ b/drivers/ub/ubus/instance.c @@ -95,7 +95,7 @@ static void ub_bus_instance_destroy(struct ub_bus_instance *bi) if (is_dynamic(bi)) tar = &bi->info.guid.id; - ummu_core_del_eid(tar, bi->info.eid, EID_BYPASS); + ummu_core_del_eid(tar, bi->info.eid, EID_NONE); ub_unregister_bus_instance(bi); } @@ -415,7 +415,7 @@ int ub_static_bus_instance_init(struct ub_bus_controller *ubc) } ret = ummu_core_add_eid((guid_t *)&guid_null, bi->info.eid, - EID_BYPASS); + EID_NONE); if (ret) { dev_err(&ubc->dev, "static server ummu core add eid, ret=%d\n", ret); @@ -510,7 +510,7 @@ int ub_ioctl_bus_instance_create(void __user *uptr) bi_info_init(&info, &create); mutex_lock(&dynamic_mutex); - bi = ub_dynamic_bus_instance_create(&info, EID_BYPASS); + bi = ub_dynamic_bus_instance_create(&info, EID_NONE); if (IS_ERR(bi)) { pr_err("bus instance create failed, ret=%ld\n", PTR_ERR(bi)); mutex_unlock(&dynamic_mutex); @@ -672,7 +672,7 @@ ub_static_cluster_instance_create(struct ub_bus_controller *ubc, u32 *guid, goto put; ret = ummu_core_add_eid((guid_t *)&guid_null, bi->info.eid, - EID_BYPASS); + EID_NONE); if (ret) { dev_err(&ubc->dev, "bus instance add eid failed, ret=%d\n", ret); goto unregister; diff --git a/drivers/ub/ubus/pool.c b/drivers/ub/ubus/pool.c index 276c854de867..38a8cf76c181 100644 --- a/drivers/ub/ubus/pool.c +++ b/drivers/ub/ubus/pool.c @@ -579,7 +579,7 @@ static void ub_pool_bi_handler(struct ub_bus_controller *ubc, void *msg, u16 p_l } ret = ub_msg_bus_instance_create(ubc, pld->guid, pld->eid[0], - pld->upi, EID_BYPASS); + pld->upi, EID_NONE); } else { if (p_len != MSG_BI_DESTROY_SIZE) { dev_err(dev, "bi destroy msg len is wrong, len=%#x\n", -- Gitee From 25ff248aae3379429cacc390ea82b19509b26960 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Sat, 7 Mar 2026 15:12:56 +0800 Subject: [PATCH 137/214] ub:ubus add some missing code to openeuler ANBZ: #45460 commit adb3d506316a0743444386aa9181d55a47d675db openEuler. add some missing code to openeuler Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/cap.c | 4 -- drivers/ub/ubus/decoder.c | 1 - drivers/ub/ubus/link.c | 10 ----- drivers/ub/ubus/link.h | 1 - drivers/ub/ubus/memory.c | 45 +++++++++++++++++++ drivers/ub/ubus/memory.h | 2 + drivers/ub/ubus/msg.c | 32 +++++++++++-- drivers/ub/ubus/msg.h | 3 ++ drivers/ub/ubus/port.c | 1 - .../ub/ubus/services/hotplug/hotplug_core.c | 6 +-- drivers/ub/ubus/ubus_driver.c | 5 +++ .../ub/ubus/vendor/hisilicon/hisi-decoder.c | 18 +++++--- drivers/ub/ubus/vendor/hisilicon/local-ras.h | 14 ------ drivers/ub/ubus/vendor/hisilicon/msg.c | 13 +++++- drivers/ub/ubus/vendor/hisilicon/vdm.c | 2 + drivers/vfio/ubus/vfio_ub_intrs.c | 2 + include/ub/ubus/ub-mem-decoder.h | 13 ++++++ 17 files changed, 127 insertions(+), 45 deletions(-) diff --git a/drivers/ub/ubus/cap.c b/drivers/ub/ubus/cap.c index 92d2069b4d5c..cd40c398b6f0 100644 --- a/drivers/ub/ubus/cap.c +++ b/drivers/ub/ubus/cap.c @@ -236,14 +236,10 @@ void ub_init_capabilities(struct ub_entity *uent) /* cfg1 caps */ ub_decoder_init(uent); ub_intr_init(uent); - - uent->reset_fn = 1; } void ub_uninit_capabilities(struct ub_entity *uent) { /* cfg1 cap */ ub_decoder_uninit(uent); - - uent->reset_fn = 0; } diff --git a/drivers/ub/ubus/decoder.c b/drivers/ub/ubus/decoder.c index f678641d84a5..93dca30e9c50 100644 --- a/drivers/ub/ubus/decoder.c +++ b/drivers/ub/ubus/decoder.c @@ -402,7 +402,6 @@ static void ub_remove_decoder(struct ub_bus_controller *ubc) ubc->decoder = NULL; } - static irqreturn_t decoder_event_deal_handle(int irq, void *data) { struct ub_entity *uent = (struct ub_entity *)data; diff --git a/drivers/ub/ubus/link.c b/drivers/ub/ubus/link.c index ac87736801ba..adfbc8dae2ed 100644 --- a/drivers/ub/ubus/link.c +++ b/drivers/ub/ubus/link.c @@ -407,16 +407,6 @@ void ublc_link_down_handle(struct ub_port *port) ub_notify_share_port(port, UB_PORT_EVENT_LINK_DOWN); } -void ub_link_change_handler(struct work_struct *work) -{ - struct ub_port *port = container_of(work, struct ub_port, link_work); - - if (port->link_event == UB_LINK_UP) - ublc_link_up_handle(port); - else - ublc_link_down_handle(port); -} - static void ub_link_handle_event(struct ub_port *port, enum ub_link_event event) { if (event == UB_LINK_UP) diff --git a/drivers/ub/ubus/link.h b/drivers/ub/ubus/link.h index 004d06b948e2..d5eaf9b87c2a 100644 --- a/drivers/ub/ubus/link.h +++ b/drivers/ub/ubus/link.h @@ -22,7 +22,6 @@ enum ub_link_event { UB_LINK_DOWN }; -void ub_link_change_handler(struct work_struct *work); void ublc_link_up_handle(struct ub_port *port); void ublc_link_down_handle(struct ub_port *port); void ub_link_msg_handler(struct ub_bus_controller *ubc, void *pkt, u16 len); diff --git a/drivers/ub/ubus/memory.c b/drivers/ub/ubus/memory.c index e7fcdf76f28e..a96934fc1190 100644 --- a/drivers/ub/ubus/memory.c +++ b/drivers/ub/ubus/memory.c @@ -145,6 +145,51 @@ int ub_mem_drain_state(u32 scna) } EXPORT_SYMBOL_GPL(ub_mem_drain_state); +void ub_mem_drain_start_enhanced(void) +{ + struct ub_mem_device *mem_device; + struct ub_bus_controller *ubc; + + list_for_each_entry(ubc, &ubc_list, node) { + mem_device = ubc->mem_device; + if (!mem_device) { + dev_warn(&ubc->dev, "ubc mem_device is null.\n"); + continue; + } + if (mem_device->ops && mem_device->ops->mem_drain_start) + mem_device->ops->mem_drain_start(ubc); + else + dev_warn(&ubc->dev, "ub mem_device ops mem_drain_start is null.\n"); + } +} +EXPORT_SYMBOL_GPL(ub_mem_drain_start_enhanced); + +int ub_mem_drain_state_enhanced(void) +{ + struct ub_mem_device *mem_device; + struct ub_bus_controller *ubc; + int ret = 0; + + list_for_each_entry(ubc, &ubc_list, node) { + mem_device = ubc->mem_device; + if (!mem_device) { + dev_warn(&ubc->dev, "ubc mem_device is null.\n"); + continue; + } + + if (mem_device->ops && mem_device->ops->mem_drain_state) { + ret = mem_device->ops->mem_drain_state(ubc); + if (!ret) + return ret; + } else { + dev_warn(&ubc->dev, "ub memory decoder ops mem_drain_state is null.\n"); + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(ub_mem_drain_state_enhanced); + int ub_mem_get_numa_id(u32 scna) { struct ub_bus_controller *ubc; diff --git a/drivers/ub/ubus/memory.h b/drivers/ub/ubus/memory.h index ab6ad0e72ac8..18960788831f 100644 --- a/drivers/ub/ubus/memory.h +++ b/drivers/ub/ubus/memory.h @@ -17,6 +17,8 @@ void ub_mem_decoder_init(struct ub_entity *uent); void ub_mem_decoder_uninit(struct ub_entity *uent); void ub_mem_init_usi(struct ub_entity *uent); void ub_mem_uninit_usi(struct ub_entity *uent); +void ub_mem_drain_start_enhanced(void); +int ub_mem_drain_state_enhanced(void); struct ub_mem_ras_err_info { enum ras_err_type type; diff --git a/drivers/ub/ubus/msg.c b/drivers/ub/ubus/msg.c index 72a89b522cd4..7e20083a9bb8 100644 --- a/drivers/ub/ubus/msg.c +++ b/drivers/ub/ubus/msg.c @@ -103,8 +103,20 @@ void message_remove_device(struct ub_entity *uent) int message_sync_request(struct message_device *mdev, struct msg_info *info, u8 code) { - if (mdev->ops->sync_request) - return mdev->ops->sync_request(mdev, info, code); + int ret, i = 1; + + if (mdev->ops->sync_request) { + while (1) { + ret = mdev->ops->sync_request(mdev, info, code); + if (ret != -ETIMEDOUT) + return ret; + + i++; + + if (!msg_retry || i > RETRY_COUNT) + return -ETIMEDOUT; + } + } return -ENOTTY; } @@ -131,8 +143,20 @@ EXPORT_SYMBOL_GPL(message_response); int message_sync_enum(struct message_device *mdev, struct msg_info *info, u8 cmd) { - if (mdev->ops->sync_enum) - return mdev->ops->sync_enum(mdev, info, cmd); + int ret, i = 1; + + if (mdev->ops->sync_enum) { + while (1) { + ret = mdev->ops->sync_enum(mdev, info, cmd); + if (ret != -ETIMEDOUT) + return ret; + + i++; + + if (!msg_retry || i > RETRY_COUNT) + return -ETIMEDOUT; + } + } return -ENOTTY; } diff --git a/drivers/ub/ubus/msg.h b/drivers/ub/ubus/msg.h index f714295fa0e9..088f4e73be6d 100644 --- a/drivers/ub/ubus/msg.h +++ b/drivers/ub/ubus/msg.h @@ -8,6 +8,8 @@ #include "ubus.h" +extern bool msg_retry; + struct ub_link_header { u32 plen : 14; u32 rt : 2; @@ -45,6 +47,7 @@ struct compact_network_header { #define code_gen(msg, sub_msg, type) ((sub_msg) << 4 | ((msg) << 1 | (type))) #define ubba_gen(ubba_h, ubba_l) ((u64)(ubba_h) << 32 | (ubba_l)) +#define RETRY_COUNT 3 #define PLD_SIZE_MAX SZ_1K enum ub_msg_type { diff --git a/drivers/ub/ubus/port.c b/drivers/ub/ubus/port.c index f46d7450df60..717a5507bd62 100644 --- a/drivers/ub/ubus/port.c +++ b/drivers/ub/ubus/port.c @@ -546,7 +546,6 @@ static void ub_port_init(struct ub_entity *uent, struct ub_port *port) bitmap_zero(port->cna_maps, UB_MAX_CNA_NUM); bitmap_zero(port->cap_map, UB_PORT_CAP_NUM); kobject_init(&port->kobj, &ub_port_ktype); - INIT_WORK(&port->link_work, ub_link_change_handler); } int ub_ports_setup(struct ub_entity *uent) diff --git a/drivers/ub/ubus/services/hotplug/hotplug_core.c b/drivers/ub/ubus/services/hotplug/hotplug_core.c index 55531b6043f3..eed9927802d2 100644 --- a/drivers/ub/ubus/services/hotplug/hotplug_core.c +++ b/drivers/ub/ubus/services/hotplug/hotplug_core.c @@ -13,7 +13,7 @@ #define to_ub_slot(s) container_of(s, struct ub_slot, kobj) -static void ubhp_destory_slot(struct ub_slot *slot) +static void ubhp_destroy_slot(struct ub_slot *slot) { mutex_destroy(&slot->state_lock); kfree(slot); @@ -23,7 +23,7 @@ static void ubhp_slot_release(struct kobject *kobj) { struct ub_slot *slot = to_ub_slot(kobj); - ubhp_destory_slot(slot); + ubhp_destroy_slot(slot); } enum slot_state { @@ -332,7 +332,7 @@ static int ubhp_probe(struct ub_service_device *sdev) ret = ubhp_setup_slot(slot, uent, i); if (ret) { - ubhp_destory_slot(slot); + ubhp_destroy_slot(slot); goto free_slots; } diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index cbeefff5bf74..7f7e7ac8d448 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -31,6 +31,11 @@ bool entity_flex_en; module_param(entity_flex_en, bool, 0444); MODULE_PARM_DESC(entity_flex_en, "Entity Flexible enable: default: 0"); +bool msg_retry; +EXPORT_SYMBOL_GPL(msg_retry); +module_param(msg_retry, bool, 0444); +MODULE_PARM_DESC(msg_retry, "support msg retry: 0(disable)"); + DECLARE_RWSEM(ub_bus_sem); #define UBC_GUID_VENDOR_SHIFT 48 diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.c b/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.c index b9eec4d8111a..7b04cc832cd5 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.c +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.c @@ -715,30 +715,36 @@ void hi_free_decoder_table(struct ub_decoder *decoder) int hi_decoder_unmap(struct ub_decoder *decoder, phys_addr_t addr, u64 size) { - int ret; struct decoder_map_info info = { .pa = addr, .size = size, }; + int ret; - if (size < SZ_1M) - size = SZ_1M; + info.size = ALIGN(info.size, SZ_1M); ret = handle_table(decoder, &info, false); if (ret) return ret; - return hi_decoder_cmd_request(decoder, addr, size, TLBI_PARTIAL); + return hi_decoder_cmd_request(decoder, info.pa, info.size, TLBI_PARTIAL); } int hi_decoder_map(struct ub_decoder *decoder, struct decoder_map_info *info) { - if (info->size < SZ_1M) - info->size = SZ_1M; + info->size = ALIGN(info->size, SZ_1M); + ub_info(decoder->uent, "decoder map, pa=%#llx, uba=%#llx, size=%#llx, cna=%#x, orderid=%#x, ordertype=%#x, eid_l=%#llx, eid_h=%#llx, upi=%#x src_eid=%#x\n", info->pa, info->uba, info->size, info->tpg_num, info->order_id, info->order_type, info->eid_low, info->eid_high, info->upi, info->src_eid); + if (info->pa < decoder->mmio_base_addr || + info->pa + info->size - 1 > decoder->mmio_end_addr || + info->pa > info->pa + info->size - 1) { + pr_err("decoder map range check error\n"); + return -EINVAL; + } + return handle_table(decoder, info, true); } diff --git a/drivers/ub/ubus/vendor/hisilicon/local-ras.h b/drivers/ub/ubus/vendor/hisilicon/local-ras.h index fe036069e3ab..b01712efb514 100644 --- a/drivers/ub/ubus/vendor/hisilicon/local-ras.h +++ b/drivers/ub/ubus/vendor/hisilicon/local-ras.h @@ -46,20 +46,6 @@ struct hisi_ubus_error_private { struct notifier_block nb; }; -enum hisi_ubus_submodule_idx { - HISI_UBUS_SUB_MODULE_ID_TP, - HISI_UBUS_SUB_MODULE_ID_TA, - HISI_UBUS_SUB_MODULE_ID_MISC, - HISI_UBUS_SUB_MODULE_ID_BA, - HISI_UBUS_SUB_MODULE_ID_NL, - HISI_UBUS_SUB_MODULE_ID_DLMAC, - HISI_UBUS_SUB_MODULE_ID_MUXPCS, - HISI_UBUS_SUB_MODULE_ID_CCUM, - HISI_UBUS_SUB_MODULE_ID_CCUA, - HISI_UBUS_SUB_MODULE_ID_ETH_MAC, - HISI_UBUS_SUB_MODULE_ID_INVAILD, -}; - enum hisi_ubus_err_severity { HISI_UBUS_ERR_SEV_RECOVERABLE, HISI_UBUS_ERR_SEV_FATAL, diff --git a/drivers/ub/ubus/vendor/hisilicon/msg.c b/drivers/ub/ubus/vendor/hisilicon/msg.c index ed43357556b7..53f3b1b7d660 100644 --- a/drivers/ub/ubus/vendor/hisilicon/msg.c +++ b/drivers/ub/ubus/vendor/hisilicon/msg.c @@ -626,7 +626,18 @@ int hi_message_sync_request_sched(struct message_device *mdev, int hi_message_private(struct message_device *mdev, struct msg_info *info, u8 opcode) { - return hi_message_sync(mdev, info, HISI_PRIVATE, opcode, false); + int ret, i = 1; + + while (1) { + ret = hi_message_sync(mdev, info, HISI_PRIVATE, opcode, false); + if (ret != -ETIMEDOUT) + return ret; + + i++; + + if (!msg_retry || i > RETRY_COUNT) + return -ETIMEDOUT; + } } static struct message_ops hi_message_ops = { diff --git a/drivers/ub/ubus/vendor/hisilicon/vdm.c b/drivers/ub/ubus/vendor/hisilicon/vdm.c index 2f7425029b67..be61f02846bc 100644 --- a/drivers/ub/ubus/vendor/hisilicon/vdm.c +++ b/drivers/ub/ubus/vendor/hisilicon/vdm.c @@ -162,12 +162,14 @@ static u8 ub_idevice_pue_add_handler(struct ub_bus_controller *ubc, struct vdm_m struct idev_pue_reg_pld *pld = &pkt->pd_reg_pld; struct ub_entity *pue, *alloc_dev = NULL; struct ue_map map = {}; + char buf[SZ_64] = {}; u8 status; int ret; /* search corresponding device by guid in message payload */ pue = inner_ub_get_ent_by_guid((guid_t *)pld->guid, &ubc->devs); if (!pue) { + (void)ub_show_guid((struct ub_guid *)pld->guid, buf); dev_err(&ubc->dev, "find uent by guid in pue reg func failed\n"); status = UB_MSG_RSP_EXEC_ENODEV; goto pue_reg_rsp; diff --git a/drivers/vfio/ubus/vfio_ub_intrs.c b/drivers/vfio/ubus/vfio_ub_intrs.c index 6b0ce3ccd068..8dfc13c39e45 100644 --- a/drivers/vfio/ubus/vfio_ub_intrs.c +++ b/drivers/vfio/ubus/vfio_ub_intrs.c @@ -172,6 +172,7 @@ static void vfio_ub_intr_disable(struct vfio_ub_core_device *vdev) vdev->irq_type = VFIO_UB_NUM_IRQS; vdev->num_ctx = 0; kfree(vdev->ctx); + vdev->ctx = NULL; } static int vfio_ub_intr_enable(struct vfio_ub_core_device *vdev, int nvec) @@ -188,6 +189,7 @@ static int vfio_ub_intr_enable(struct vfio_ub_core_device *vdev, int nvec) if (ret > 0) ub_disable_intr(uent); kfree(vdev->ctx); + vdev->ctx = NULL; return ret; } diff --git a/include/ub/ubus/ub-mem-decoder.h b/include/ub/ubus/ub-mem-decoder.h index 56ba2bed34b0..322fa7d7b01e 100644 --- a/include/ub/ubus/ub-mem-decoder.h +++ b/include/ub/ubus/ub-mem-decoder.h @@ -65,6 +65,19 @@ void ub_mem_drain_start(u32 scna); */ int ub_mem_drain_state(u32 scna); +/* + * ub_mem_drain_start_enhanced - start ub memory drain enhanced + */ +void ub_mem_drain_start_enhanced(void); + +/* + * ub_mem_drain_state_enhanced - whether ub memory drain enhanced has been finished + * RETURN VALUE: + * 0 if drain not finish; 1 if drain finish + * other if failed. + */ +int ub_mem_drain_state_enhanced(void); + /* * ub_mem_get_numa_id - get ubc numa id from scna * @scna: source cna -- Gitee From 1fa6afcdf49c47cd5c3ebb1a60f2932544c64777 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Thu, 22 Jan 2026 16:49:17 +0800 Subject: [PATCH 138/214] ub:ubus: alter nl ssu credit ras recover ANBZ: #45460 commit 2a93500880c00d17d46af96dcafe84ce52b4942b openEuler. alter nl ssu credit ras recover Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/vendor/hisilicon/local-ras.c | 48 +++----------------- 1 file changed, 7 insertions(+), 41 deletions(-) diff --git a/drivers/ub/ubus/vendor/hisilicon/local-ras.c b/drivers/ub/ubus/vendor/hisilicon/local-ras.c index cf65f06da93f..58e92d846c48 100644 --- a/drivers/ub/ubus/vendor/hisilicon/local-ras.c +++ b/drivers/ub/ubus/vendor/hisilicon/local-ras.c @@ -19,7 +19,8 @@ struct sub_module_info { }; static struct sub_module_info hisi_ubus_sub_module[] = { - {0x2, "MISC"}, + {0x1, "MISC.MISC_SLV"}, + {0x2, "MISC.IMP"}, {0x3, "BA"}, {0x4, "NL PORT"}, {0x5, "NL DEVICE"}, @@ -62,7 +63,6 @@ static struct sub_module_info hisi_ubus_sub_module[] = { {0x67, "ETH.CORE_RXRSFEC"}, {0x68, "ETH.CORE_RXPMACORE"}, {0x69, "ETH.CORE_TXPMAL0"}, - {0x6a, "ETH.CORE_RXPMAL0"}, {0x6b, "ETH.CORE_TXBRFEC"}, {0x6c, "ETH.CORE_RXBRFEC"}, }; @@ -172,9 +172,6 @@ static inline bool is_nl_ssu_link_credi_overtime_err(const struct hisi_ubus_erro { if (DIV_ROUND_UP(edata->register_array_size, SZ_4) <= LQC_MOUDULE_ERR_MISC) return false; - /* only supported device err */ - if (!!(edata->val_bits & HISI_UBUS_LOCAL_VALID_PORT_ID)) - return false; return !!(edata->err_misc[LQC_MOUDULE_ERR_MISC] & (1U << LQC_MOUDULE_ERR_BIT)); } @@ -184,10 +181,13 @@ static bool ubus_need_recover(const struct hisi_ubus_error_data *edata) if (edata->err_severity != HISI_UBUS_ERR_SEV_RECOVERABLE) return false; + if (!(edata->val_bits & HISI_UBUS_LOCAL_VALID_PORT_ID)) + return false; + if (is_nl_local_ras(edata->sub_module_id)) return is_nl_ssu_link_credi_overtime_err(edata); - return !!(edata->val_bits & HISI_UBUS_LOCAL_VALID_PORT_ID); + return true; } static int ubus_port_recover(struct ub_entity *uent, u16 port_id) @@ -223,44 +223,10 @@ static int ubus_port_recover_cluster(struct ub_entity *uent, u16 port_id) return 0; } -static int nl_ssu_link_credi_overtime_recover(struct ub_entity *uent, u8 nl_id, bool cluster) -{ -#define NL_PORTS 2 - /* - * The shared ports will only be front of exclusive ports, - * so the shared port ind is begin of 0 and serially, - * each nl has 2 ports, recover port ind of this nl should begin of (nl_id)*2 - */ - u16 port_id = nl_id * NL_PORTS; - int ret = 0, i; - - for (i = 0; i < NL_PORTS; i++) { - port_id += i; - if (!cluster) - ret = ubus_port_recover(uent, port_id); - else - ret = ubus_port_recover_cluster(uent, port_id); - if (ret) { - ub_err(uent, "port[%u] recover failed, ret=%d.\n", port_id, ret); - return ret; - } - } - - ub_info(uent, "nl[%u] recover all ports succeeded.\n", nl_id); - return ret; -} - static int ubus_recover(struct ub_entity *uent, const struct hisi_ubus_error_data *edata) { int port_id; - u8 nl_id; - - if (is_nl_local_ras(edata->sub_module_id) && - is_nl_ssu_link_credi_overtime_err(edata)) { - nl_id = edata->core_id; - return nl_ssu_link_credi_overtime_recover(uent, nl_id, uent->ubc->cluster); - } port_id = (int)edata->port_id; if (uent->ubc->cluster) @@ -285,7 +251,7 @@ static void hisi_ubus_handle_error(struct ub_entity *uent, ub_warn(uent, "register array size is exceed max array size %d, only parts of data were printed.\n", REGISTER_ARRAY_MAX_SIZE); - if (!ubus_need_recover(edata) || uent->ubc->cluster) { + if (!ubus_need_recover(edata)) { ub_info(uent, "ubus no need recover.\n"); return; } -- Gitee From 50203e7af07f9b07d0f57961d0ded6ba63dbc3cc Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 13 Mar 2026 09:41:17 +0800 Subject: [PATCH 139/214] ub:ubus: add reset function ANBZ: #45460 commit 827cce8a17551a59219d3ebf611b95a0b47d9516 openEuler. add reset function Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/cap.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/ub/ubus/cap.c b/drivers/ub/ubus/cap.c index cd40c398b6f0..92d2069b4d5c 100644 --- a/drivers/ub/ubus/cap.c +++ b/drivers/ub/ubus/cap.c @@ -236,10 +236,14 @@ void ub_init_capabilities(struct ub_entity *uent) /* cfg1 caps */ ub_decoder_init(uent); ub_intr_init(uent); + + uent->reset_fn = 1; } void ub_uninit_capabilities(struct ub_entity *uent) { /* cfg1 cap */ ub_decoder_uninit(uent); + + uent->reset_fn = 0; } -- Gitee From e488d80ec4db0fc9fd3dcb785b227eed38b3be38 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Mon, 9 Mar 2026 11:40:29 +0800 Subject: [PATCH 140/214] ub:hisi-ubus: Review of Readability Modifications ANBZ: #45460 commit 5d776f20343c8f158df3dd2ed7d7e27cdf1962db openEuler. Review of Readability Modifications Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/memory.h | 2 -- drivers/ub/ubus/vendor/hisilicon/memory.c | 35 +++++++---------------- 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/drivers/ub/ubus/memory.h b/drivers/ub/ubus/memory.h index 18960788831f..ab6ad0e72ac8 100644 --- a/drivers/ub/ubus/memory.h +++ b/drivers/ub/ubus/memory.h @@ -17,8 +17,6 @@ void ub_mem_decoder_init(struct ub_entity *uent); void ub_mem_decoder_uninit(struct ub_entity *uent); void ub_mem_init_usi(struct ub_entity *uent); void ub_mem_uninit_usi(struct ub_entity *uent); -void ub_mem_drain_start_enhanced(void); -int ub_mem_drain_state_enhanced(void); struct ub_mem_ras_err_info { enum ras_err_type type; diff --git a/drivers/ub/ubus/vendor/hisilicon/memory.c b/drivers/ub/ubus/vendor/hisilicon/memory.c index 5f1bacb8a3a0..e936d9ae79a4 100644 --- a/drivers/ub/ubus/vendor/hisilicon/memory.c +++ b/drivers/ub/ubus/vendor/hisilicon/memory.c @@ -29,21 +29,9 @@ #define hpa_gen(addr_h, addr_l) (((u64)(addr_h) << 32) | (addr_l)) -#define RAS_VAL_COUNT 3 -#define RAS_VAL_IDX_VAL0 0 -#define RAS_VAL_IDX_VAL1 1 -#define RAS_VAL_IDX_VAL2 2 - #define UB_MEM_PORT_WARNING 18 #define UB_MEM_PORT_RECOVERY 19 -#define WARNING_OFFSET_VAL0_LOW 0 -#define WARNING_OFFSET_VAL0_HIGH 1 -#define WARNING_OFFSET_VAL1_LOW 2 -#define WARNING_OFFSET_VAL1_HIGH 3 -#define RECOVERY_OFFSET_VAL0_LOW 4 -#define RECOVERY_OFFSET_VAL0_HIGH 5 - static u8 ub_mem_num; struct ub_mem_decoder { @@ -146,7 +134,7 @@ static irqreturn_t hi_mem_ras_isr(int irq, void *context) struct ub_mem_ras_err_info err_info; ubmem_ras_handler handler; u64 ctl_no = ubc->ctl_no; - u64 vals[RAS_VAL_COUNT]; + u64 vals[3]; int ret; handler = ub_mem_ras_handler_get(); @@ -155,9 +143,9 @@ static irqreturn_t hi_mem_ras_isr(int irq, void *context) pr_info("ras: type=%u\n", err_info.type); if (err_info.type == UB_MEM_PORT_WARNING || err_info.type == UB_MEM_PORT_RECOVERY) { - vals[RAS_VAL_IDX_VAL0] = err_info.val0; - vals[RAS_VAL_IDX_VAL1] = err_info.val1; - vals[RAS_VAL_IDX_VAL2] = ctl_no; + vals[0] = err_info.val0; + vals[1] = err_info.val1; + vals[2] = ctl_no; if (handler) { ret = handler((u64)vals, err_info.type); WARN_ON(ret); @@ -206,17 +194,16 @@ static int save_injected_route_info(struct ub_bus_controller *ubc, unsigned long status4_bitmap) { u32 addr_h, addr_l; - int i, index = 0; u64 val0, val1; - int ret; + int ret, i; if (test_bit(err_type_bitmap[UB_MEM_PORT_WARNING], &status4_bitmap)) { i = UB_MEM_PORT_WARNING; - addr_h = info->err_addr[index + WARNING_OFFSET_VAL0_HIGH]; - addr_l = info->err_addr[index + WARNING_OFFSET_VAL0_LOW]; + addr_h = info->err_addr[1]; + addr_l = info->err_addr[0]; val0 = hpa_gen(addr_h, addr_l); - addr_h = info->err_addr[index + WARNING_OFFSET_VAL1_HIGH]; - addr_l = info->err_addr[index + WARNING_OFFSET_VAL1_LOW]; + addr_h = info->err_addr[3]; + addr_l = info->err_addr[2]; val1 = hpa_gen(addr_h, addr_l); ret = save_ras_err_info(ubc->mem_device, (enum ras_err_type)i, val0, val1); if (ret) @@ -225,8 +212,8 @@ static int save_injected_route_info(struct ub_bus_controller *ubc, if (test_bit(err_type_bitmap[UB_MEM_PORT_RECOVERY], &status4_bitmap)) { i = UB_MEM_PORT_RECOVERY; - addr_h = info->err_addr[index + RECOVERY_OFFSET_VAL0_HIGH]; - addr_l = info->err_addr[index + RECOVERY_OFFSET_VAL0_LOW]; + addr_h = info->err_addr[5]; + addr_l = info->err_addr[4]; val0 = hpa_gen(addr_h, addr_l); ret = save_ras_err_info(ubc->mem_device, (enum ras_err_type)i, val0, 0); if (ret) -- Gitee From ee3c2c38569e9fe30dc34f7736aeaf6358ddf0c3 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 3 Apr 2026 17:33:26 +0800 Subject: [PATCH 141/214] ub:hisi-ubus: Synchronize the missing code ANBZ: #45460 commit 9b3f7b878ff5f585449a55761859fb8430c3c545 openEuler. 1. Fix the error in parsing messages bypassed by the UMMU. 2. Bugfix ubus AI reviews of security verification issue. 3. In a cluster scenario, pool devices are not registered in the decoder page table. 4. When ublc device is down, go to notify share port. Fixes: 0f9e2dbe888d ("ub:ubus: Support for enabling and disabling ue") Signed-off-by: Jianquan Lin Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/link.c | 2 +- drivers/ub/ubus/memory.c | 2 +- drivers/ub/ubus/resource.c | 18 ++++++++++++++++++ .../ub/ubus/vendor/hisilicon/hisi-decoder.c | 4 ++++ drivers/ub/ubus/vendor/hisilicon/vdm.c | 4 ++-- 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/ub/ubus/link.c b/drivers/ub/ubus/link.c index adfbc8dae2ed..9d8d579d8c01 100644 --- a/drivers/ub/ubus/link.c +++ b/drivers/ub/ubus/link.c @@ -392,7 +392,7 @@ void ublc_link_down_handle(struct ub_port *port) ub_info(uent, "all port link down and remove device\n"); device_unlock(&uent->dev); - return; + goto link_down_notify; } r_port = port->r_uent->ports + port->r_index; diff --git a/drivers/ub/ubus/memory.c b/drivers/ub/ubus/memory.c index a96934fc1190..9dc07f7047a2 100644 --- a/drivers/ub/ubus/memory.c +++ b/drivers/ub/ubus/memory.c @@ -225,7 +225,7 @@ bool ub_memory_validate_pa(u32 scna, u64 pa_start, u64 pa_end, bool cacheable) return mem_device->ops->mem_validate_pa(ubc, pa_start, pa_end, cacheable); - dev_warn(mem_device->dev, "ub memory decoder ops mem_drain_state is null.\n"); + dev_warn(mem_device->dev, "ub memory decoder ops memory_validate_pa is null.\n"); return false; } EXPORT_SYMBOL_GPL(ub_memory_validate_pa); diff --git a/drivers/ub/ubus/resource.c b/drivers/ub/ubus/resource.c index 542b6e6135e8..258288ba584f 100644 --- a/drivers/ub/ubus/resource.c +++ b/drivers/ub/ubus/resource.c @@ -355,6 +355,21 @@ static void ub_entity_decoder_unmap_mmio_idx(struct ub_entity *uent, int idx) ub_warn(uent, "resource%d decoder unmap failed.\n", idx); } +static bool map_by_ubus(struct ub_entity *ent) +{ + struct ub_bus_controller *ubc = ent->ubc; + u32 feature; + int ret; + + ret = ub_cfg_read_dword(ubc->uent, UB_CFG1_SUPPORT_FEATURE_L, &feature); + if (ret) { + ub_err(ubc->uent, "read ub cfg1 support feature failed, ret=%d\n", + ret); + return false; + } + return !(feature & UB_DECODER_JURIS); +} + void ub_entity_decoder_unmap_mmio(struct ub_entity *dev) { int i; @@ -374,6 +389,9 @@ void ub_entity_decoder_map_mmio(struct ub_entity *dev) if (is_ibus_controller(dev)) return; + if (!map_by_ubus(dev)) + return; + for (i = 0; i < MAX_UB_RES_NUM; i++) { if (!dev->zone[i].sa_used || !dev->zone[i].init_succ) continue; diff --git a/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.c b/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.c index 7b04cc832cd5..a9616b5195a0 100644 --- a/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.c +++ b/drivers/ub/ubus/vendor/hisilicon/hisi-decoder.c @@ -181,6 +181,7 @@ static void rgtlb_free_page(struct ub_decoder *decoder, struct page_table_desc *desc, struct range_table_entry *rgtlb_entry) { + memset(desc->page_base, 0, RANGE_TABLE_PAGE_SIZE); dmam_free_coherent(decoder->dev, RANGE_TABLE_PAGE_SIZE, desc->page_base, desc->page_dma); rgtlb_entry->next_lv_addr = decoder->invalid_page_dma; @@ -280,6 +281,7 @@ static int rgtlb_unmap_to_range(struct ub_decoder *decoder, int i, j, ret; bool flag; + rgtlb_entry->token_value = 0; for (i = 0; i < RGTLB_TO_PGTLB; i++) { pgtlb_entry = (struct page_table_entry *)rgtlb_entry + i; pgtlb_entry->entry_type = PAGE_TABLE; @@ -313,6 +315,7 @@ static int rgtlb_unmap_to_range(struct ub_decoder *decoder, } } + memset(rg_base, 0, RANGE_TABLE_PAGE_SIZE); dmam_free_coherent(decoder->dev, RANGE_TABLE_PAGE_SIZE, rg_base, rg_dma); return 0; @@ -405,6 +408,7 @@ static void pgtlb_free_page(struct ub_decoder *decoder, struct page_table_desc *desc, struct page_table_entry *pgtlb_entry) { + memset(desc->page_base, 0, PAGE_TABLE_PAGE_SIZE); dmam_free_coherent(decoder->dev, PAGE_TABLE_PAGE_SIZE, desc->page_base, desc->page_dma); pgtlb_entry->next_lv_addr = decoder->invalid_page_dma; diff --git a/drivers/ub/ubus/vendor/hisilicon/vdm.c b/drivers/ub/ubus/vendor/hisilicon/vdm.c index be61f02846bc..d52ae6ffe02b 100644 --- a/drivers/ub/ubus/vendor/hisilicon/vdm.c +++ b/drivers/ub/ubus/vendor/hisilicon/vdm.c @@ -408,9 +408,9 @@ static u8 ub_vdm_create_bi_bypass_ummu(struct ub_bus_controller *ubc, struct vdm int ret; if (pld->m) - type = EID_NONE; - else type = EID_BYPASS; + else + type = EID_NONE; ret = ub_msg_bus_instance_create(ubc, pld->guid, pld->eid[0], pld->upi, type); -- Gitee From 03565fd25b17824baa60cfa672968dd0600d646f Mon Sep 17 00:00:00 2001 From: Jinjie Cui Date: Mon, 30 Mar 2026 14:48:37 +0800 Subject: [PATCH 142/214] ub:ubus: Fix null message workqueue issue ANBZ: #45460 commit e505a7803615efd56ea9ab14e0afa64686939b8f openEuler. Check message workqueue is null or not before queue_work(), if the workqueue is empty, the kernel might dereference a null pointer, causing a kernel panic. Fixes: 63179ebb7e8e ("ub:ubus: Add UBUS receive message framework") Signed-off-by: Jinjie Cui Signed-off-by: qiushengming Signed-off-by: zhangxinghao --- drivers/ub/ubus/msg.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/ub/ubus/msg.c b/drivers/ub/ubus/msg.c index 7e20083a9bb8..f5a71844107e 100644 --- a/drivers/ub/ubus/msg.c +++ b/drivers/ub/ubus/msg.c @@ -255,12 +255,27 @@ static bool msg_rx_code_valid(struct ub_bus_controller *ubc, u8 code) if (msg_type(code) == MSG_RSP) return false; - if (msg == UB_MSG_CODE_RAS || msg == UB_MSG_CODE_CFG || - msg == UB_MSG_CODE_EXCH || msg == UB_MSG_CODE_MAX) + switch (msg) { + case UB_MSG_CODE_RAS: + case UB_MSG_CODE_CFG: + case UB_MSG_CODE_EXCH: + case UB_MSG_CODE_SEC: + case UB_MSG_CODE_MAX: return false; - if (!ubc->cluster && msg == UB_MSG_CODE_POOL) + case UB_MSG_CODE_POOL: + if (!ubc->cluster) + return false; + break; + + default: + break; + } + + if (!rx_msg_wq[msg]) { + dev_err(&ubc->dev, "no workqueue registered for msg %u\n", msg); return false; + } return true; } -- Gitee From f2a647537fc48d05f7213dc2ba6a96f23db26936 Mon Sep 17 00:00:00 2001 From: Fengyan Mu Date: Wed, 24 Sep 2025 16:39:23 +0800 Subject: [PATCH 143/214] net: ubl: add CONFIG_UB_UBL definition and UBL interface ANBZ: #45460 commit 5a325caafc55cf93314324d53386de09747ef457 openEuler. This patch adds UB link interfaces to support it. UB is a new interconnection protocol that defines its own Layer 2 protocol. The format of a complete UB packet is as follows. UBL HDR includes UB LINK, CC and NPI. UB LINK replaces Ether header in packets. The UB Network header consists of CC, NPI, and traditional Network packet headers. |<-------- UBL HDR --------->| +--------------+------+------+---------+-----------+---------+ | UB LINK | CC | NPI | Network | TPH / TAH | Payload | +--------------+------+------+---------+-----------+---------+ |<----- UB Network ---->| - UB LINK: Format specified by the data link layer of the UB LINK. - CC: Congestion Control. - NPI: Network Partition Identifier. - Network: Traditinal L3 Header. At network layer, the UB Network Header is encapsulated. However, the packet type cannot be known by parsing packet from user, which leads to restrictions on the use of socket. This patch adds sw_ctype field to indicate the packet type. As shown in the following figure, the sw_ctype field will be add at the header of packet before entering the dirver. +----------+----+-----+---------+-----------+---------+ | sw_ctype | CC | NPI | Network | TPH / TAH | Payload | +----------+----+-----+---------+-----------+---------+ -sw_ctype: L3 Header type. When packets are sent to hardware, the sw_ctype field will be deleted and the original packet will be restored. +----+-----+---------+-----------+---------+ | CC | NPI | Network | TPH / TAH | Payload | +----+-----+---------+-----------+---------+ Signed-off-by: Fengyan Mu Signed-off-by: Haiqing Fang Signed-off-by: Junxin Chen Signed-off-by: huwentao Signed-off-by: zhangxinghao --- .../configs/L1-RECOMMEND/arm64/CONFIG_UB_UBL | 1 + drivers/net/Kconfig | 2 + drivers/net/Makefile | 1 + drivers/net/ub/Kconfig | 13 ++ drivers/net/ub/Makefile | 7 + drivers/net/ub/dev/Makefile | 7 + drivers/net/ub/dev/ubl.c | 191 ++++++++++++++++++ include/net/ub/ubl.h | 90 +++++++++ include/uapi/linux/if_arp.h | 2 + include/uapi/linux/if_ether.h | 1 + 10 files changed, 315 insertions(+) create mode 100644 anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UBL create mode 100644 drivers/net/ub/Kconfig create mode 100644 drivers/net/ub/Makefile create mode 100644 drivers/net/ub/dev/Makefile create mode 100644 drivers/net/ub/dev/ubl.c create mode 100644 include/net/ub/ubl.h diff --git a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UBL b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UBL new file mode 100644 index 000000000000..d89b5424a231 --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UBL @@ -0,0 +1 @@ +CONFIG_UB_UBL=m diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 8aa6eccb56be..c803c39793f3 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -494,6 +494,8 @@ source "drivers/net/fddi/Kconfig" source "drivers/net/hippi/Kconfig" +source "drivers/net/ub/Kconfig" + source "drivers/net/ipa/Kconfig" config NET_SB1000 diff --git a/drivers/net/Makefile b/drivers/net/Makefile index e15939e77122..e089d6f44197 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -75,6 +75,7 @@ obj-$(CONFIG_WLAN) += wireless/ obj-$(CONFIG_IEEE802154) += ieee802154/ obj-$(CONFIG_WWAN) += wwan/ obj-$(CONFIG_MCTP) += mctp/ +obj-$(CONFIG_UB_UBL) += ub/ obj-$(CONFIG_VMXNET3) += vmxnet3/ obj-$(CONFIG_XEN_NETDEV_FRONTEND) += xen-netfront.o diff --git a/drivers/net/ub/Kconfig b/drivers/net/ub/Kconfig new file mode 100644 index 000000000000..82b4267bd5c3 --- /dev/null +++ b/drivers/net/ub/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# UBL configuration +# + +config UB_UBL + default n + tristate "UB link support" + help + This enables UB link support which is + used to replace traditional ethernet layer. + Say 'Y' or 'm' if your device works + in UB mode. \ No newline at end of file diff --git a/drivers/net/ub/Makefile b/drivers/net/ub/Makefile new file mode 100644 index 000000000000..98c1cc30bc13 --- /dev/null +++ b/drivers/net/ub/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Makefile for the HISILICON network device drivers. +# + +#### compile ubl +obj-$(CONFIG_UB_UBL) += dev/ \ No newline at end of file diff --git a/drivers/net/ub/dev/Makefile b/drivers/net/ub/dev/Makefile new file mode 100644 index 000000000000..f7130b0da68e --- /dev/null +++ b/drivers/net/ub/dev/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Makefile for the HISILICON network device drivers. +# + +#### compile ubl +obj-$(CONFIG_UB_UBL) += ubl.o \ No newline at end of file diff --git a/drivers/net/ub/dev/ubl.c b/drivers/net/ub/dev/ubl.c new file mode 100644 index 000000000000..b3637476b1fb --- /dev/null +++ b/drivers/net/ub/dev/ubl.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * UBL An implementation of the UB protocol suite for the LINUX + * operating system. + * + * UB link device handling. + * + * Version: @(#)ubl.c 1.0.1 25/09/24 + * + */ + +#include +#include +#include + +static __be16 ubl_type_to_proto(u8 type) +{ + __be16 proto; + + switch (type) { + case UB_IPV4_CFG_TYPE: + proto = htons(ETH_P_IP); + break; + case UB_IPV6_CFG_TYPE: + proto = htons(ETH_P_IPV6); + break; + case UB_NOIP_CFG_TYPE: + default: + proto = htons(ETH_P_UB); + break; + } + + return proto; +} + +/** + * ubl_add_sw_ctype - add software packet type for skb->data + * @skb: buffer to alter + * @ctype: indicates the packet type + * + * The packet type cannot be known by parsing packets from user, + * which leads to restrictions on the use of socket. + * Add sw_ctype field to indicate the packet type. And sw_ctype + * exists only during software prcessing. + * +----------+----+-----+-----------+ + * | sw_ctype | CC | NPI | L3 Packet | + * +----------+----+-----+-----------+ + */ +static int ubl_add_sw_ctype(struct sk_buff *skb, u8 ctype) +{ + u8 *pkt_cfg; + + if (skb_cow_head(skb, sizeof(u8))) + return -ENOMEM; + + pkt_cfg = (u8 *)skb_push(skb, sizeof(u8)); + *pkt_cfg = ctype; + + return 0; +} + +/** + * ubl_create_header - create the ubl header + * @skb: buffer to alter + * @dev: source device + * @type: ubl type field + * @daddr: not used in ubl + * @saddr: not used in ubl + * @len: packet length (<= skb->len) + * + */ +static int ubl_create_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, const void *daddr, + const void *saddr, unsigned int len) +{ + struct ublhdr *ubl; + + if (type == ETH_P_IP || type == ETH_P_IPV6) { + ubl = (struct ublhdr *)skb_push(skb, UBL_HLEN); + memset(ubl, 0, sizeof(struct ublhdr)); + ubl->h_npi = htonl(UB_DEFAULT_NPI); + ubl->cfg = (type == ETH_P_IP) ? UB_IPV4_CFG_TYPE : UB_IPV6_CFG_TYPE; + return UBL_HLEN; + } else if (type == ETH_P_UB) { + return 0; + } + + return -UBL_HLEN; +} + +/** + * ubl_header_parse_protocol - parse packets protocol before sending it to + * driver. + * @skb: buffer to alter + * + * parse packets based on packet data if skb->protocol is ETH_P_ALL or 0. + */ +static __be16 ubl_header_parse_protocol(const struct sk_buff *skb) +{ + return ubl_type_to_proto(skb->data[0]); +} + +static const struct header_ops ubl_header_ops ____cacheline_aligned = { + .create = ubl_create_header, + .parse_protocol = ubl_header_parse_protocol, +}; + +/** + * ubl_setup - setup ub link network device + * @dev: network device + * + * Fill in the fields of the device structure with ubl-generic values. + */ +void ubl_setup(struct net_device *dev) +{ + dev->header_ops = &ubl_header_ops; + dev->type = ARPHRD_UB; + dev->hard_header_len = UBL_HLEN; + dev->min_header_len = UBL_HLEN; + dev->mtu = UB_DATA_LEN; + dev->min_mtu = UB_MIN_MTU; + dev->max_mtu = UB_DATA_LEN; + dev->addr_len = UBL_ALEN; + dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; + dev->flags = (IFF_NOARP | IFF_POINTOPOINT); + dev->priv_flags |= IFF_TX_SKB_SHARING; +} +EXPORT_SYMBOL(ubl_setup); + +/** + * alloc_ubldev_mqs - Allocates and sets up a ub-link device + * @sizeof_priv: Size of additional driver-private structure to be allocated + * for this ubl device + * @txqs: The number of TX queues this device has. + * @rxqs: The number of RX queues this device has. + * + * Fill in the fields of the device structure with ubl-generic + * values. Basically done everything except registering the device. + * + * Constructs a new net device, completing with a private data area of + * size (sizeof_priv). A 32-byte (not bit) alignment is enforced for + * this private data area. + */ + +struct net_device *alloc_ubldev_mqs(int sizeof_priv, unsigned int txqs, + unsigned int rxqs) +{ + return alloc_netdev_mqs(sizeof_priv, "ubl%d", NET_NAME_UNKNOWN, + ubl_setup, txqs, rxqs); +} +EXPORT_SYMBOL(alloc_ubldev_mqs); + +/** + * ubl_type_trans - obtains skb->protocol and adds sw_ptype to the packet + * @skb: buffer to alter + * @dev: source device + * @type: packet type + * + * Obtains the packet type and translates it to skb->protocol and adds sw_ptype + * to the packet data. + */ +__be16 ubl_type_trans(struct sk_buff *skb, struct net_device *dev, u8 type) +{ + skb->dev = dev; + if (ubl_add_sw_ctype(skb, type)) + net_warn_ratelimited("add sw ctype failed\n"); + + skb_reset_mac_header(skb); + if (type == UB_IPV4_CFG_TYPE || type == UB_IPV6_CFG_TYPE) + skb_pull_inline(skb, UBL_HLEN); + else if (type != UB_NOIP_CFG_TYPE) + net_warn_ratelimited("An unknown packet is received by %s, type is %u\n", + dev->name, type); + + return ubl_type_to_proto(type); +} +EXPORT_SYMBOL(ubl_type_trans); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("UB link level"); +MODULE_VERSION(UBL_MOD_VERSION); diff --git a/include/net/ub/ubl.h b/include/net/ub/ubl.h new file mode 100644 index 000000000000..63582e147ab7 --- /dev/null +++ b/include/net/ub/ubl.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef _NET_UB_UBL_H_ +#define _NET_UB_UBL_H_ + +#include +#include +#include +#include + +#define UBL_MOD_VERSION "1.0.0" + +#define UBL_HARD_HLEN 4 +#define UBL_HLEN 7 +#define UBL_ALEN 16 + +#define UBL_LCRC_LEN 2 +#define UBL_BCRC_LEN 4 +#define UBL_LINK_LEN 2 + +#define UB_DEFAULT_NPI 1 + +#define UB_MAX_BLOCK_NUM 16 +#define UB_FLIT_NUM_OF_BLOCK 32 +#define UB_BYTE_OF_FLIT 20 +#define UB_BYTE_OF_BLK (UB_BYTE_OF_FLIT * UB_FLIT_NUM_OF_BLOCK) + +#define UB_BLOCK_CRC_LEN (UBL_BCRC_LEN + UBL_LCRC_LEN) +#define UB_MAX_MTU_PER_BLK (UB_BYTE_OF_BLK - UBL_LINK_LEN - UB_BLOCK_CRC_LEN) +#define UB_DATA_LEN 1500 +#define UB_MAX_MTU ((UB_MAX_BLOCK_NUM * UB_MAX_MTU_PER_BLK) - \ + UBL_HLEN - UBL_HARD_HLEN + UBL_LINK_LEN) +#define UB_MIN_MTU 68 + +#define UB_IPV4_CFG_TYPE 3 +#define UB_IPV6_CFG_TYPE 4 +#define UB_NOIP_CFG_TYPE 5 +#define UB_UNKNOWN_CFG_TYPE 255 + +/** + * struct ublhdr - ub link header + * @cfg: network type configuration + * @resv: reserved bit-field + * @h_cc: congestion control + * @h_npi: network partition identifier + */ +struct ublhdr { +#if defined(__LITTLE_ENDIAN_BITFIELD) + u8 cfg : 4; + u8 resv : 4; +#else + u8 resv : 4; + u8 cfg : 4; +#endif + __be16 h_cc; + __be32 h_npi; +} __packed; + +/** + * ubl_rmv_sw_ctype - delete software packet type for skb->data + * @skb: buffer to alter + * + * Before the packet is sent to the hardware, remove sw_ctype field + * and restore the original packet. + */ +static inline void *ubl_rmv_sw_ctype(struct sk_buff *skb) +{ + return pskb_pull(skb, sizeof(u8)); +} + +void ubl_setup(struct net_device *dev); +__be16 ubl_type_trans(struct sk_buff *skb, struct net_device *dev, u8 type); +struct net_device *alloc_ubldev_mqs(int sizeof_priv, unsigned int txqs, + unsigned int rxqs); +#define alloc_ubldev_mq(sizeof_priv, count) \ + alloc_ubldev_mqs((sizeof_priv), (count), (count)) + +#endif diff --git a/include/uapi/linux/if_arp.h b/include/uapi/linux/if_arp.h index 4783af9fe520..ed514ed01268 100644 --- a/include/uapi/linux/if_arp.h +++ b/include/uapi/linux/if_arp.h @@ -43,6 +43,8 @@ #define ARPHRD_EUI64 27 /* EUI-64 */ #define ARPHRD_INFINIBAND 32 /* InfiniBand */ +#define ARPHRD_UB 38 /* Unified bus */ + /* Dummy types for non ARP hardware */ #define ARPHRD_SLIP 256 #define ARPHRD_CSLIP 257 diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h index 69e0457eb200..065ca4e2d3c4 100644 --- a/include/uapi/linux/if_ether.h +++ b/include/uapi/linux/if_ether.h @@ -159,6 +159,7 @@ #define ETH_P_MCTP 0x00FA /* Management component transport * protocol packets */ +#define ETH_P_UB 0x0100 /* Network control packet of Unified Bus */ /* * This is an Ethernet frame header. -- Gitee From 000026ea3cc3a51bcfd6643387eb1cf80b688f7b Mon Sep 17 00:00:00 2001 From: Fengyan Mu Date: Tue, 28 Oct 2025 15:15:04 +0800 Subject: [PATCH 144/214] net: ubl: add depend to ARM64 and remove config in x86 ANBZ: #45460 commit c531d6313da388d4e925e903e4ded6b072329255 openEuler. This patch add the depends of ARM64 for config UB_UBL, because it can only be enabled in ARM64, and in x86 architecture all UB related config can't be enabled, so we remove the config settings in arch/x86/configs/openeuler_defconfig. Fixes: 5a325caafc55 ("net: ubl: add CONFIG_UB_UBL definition and UBL interface") Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ub/Kconfig b/drivers/net/ub/Kconfig index 82b4267bd5c3..6b32cc5cc49b 100644 --- a/drivers/net/ub/Kconfig +++ b/drivers/net/ub/Kconfig @@ -6,6 +6,7 @@ config UB_UBL default n tristate "UB link support" + depends on ARM64 help This enables UB link support which is used to replace traditional ethernet layer. -- Gitee From b86dfc91b02e39abc7134fdc02f1ff3ebe7c5cdd Mon Sep 17 00:00:00 2001 From: Fengyan Mu Date: Fri, 31 Oct 2025 22:29:57 +0800 Subject: [PATCH 145/214] ub: ubase: add support of ubase driver for ub network ANBZ: #45460 commit 3fb8b7d415394192b312b6b5746cff24e058d4c6 openEuler. UB is a new interconnection protocol, and ubase is the base driver for all network service. This patch adds the support of UBASE driver and also includes the headers and functions needed by ubase and other drivers like unic, udma, cdma, fwctl and pmu, which means its foundation use. UBASE driver is one of the base driver for ub network, which includes ubus device management, resource management, auxiliary device management, entity management, query the specific capabilities of the device and so on, which support the drivers listed as unic, udma, cdma, fwctl and pmu supposed to insmod and use its basic capabilities. In the same time, this patch support to compile ubase.ko with Makefile, Kconfig and Makefile under drivers/ub added. Signed-off-by: Haiqing Fang Signed-off-by: Haibin Lu Signed-off-by: Yixi Shen Signed-off-by: Xiaobo Zhang Signed-off-by: Jianqiang Li Signed-off-by: Chuan Wu Signed-off-by: Zihao Sheng Signed-off-by: Junxin Chen Signed-off-by: Fengyan Mu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- .../L1-RECOMMEND/arm64/CONFIG_UB_UBASE | 1 + drivers/ub/Kconfig | 1 + drivers/ub/Makefile | 1 + drivers/ub/ubase/Kconfig | 15 + drivers/ub/ubase/Makefile | 16 + drivers/ub/ubase/debugfs/ubase_debugfs.c | 165 +++++ drivers/ub/ubase/debugfs/ubase_debugfs.h | 28 + drivers/ub/ubase/ubase.h | 45 ++ drivers/ub/ubase/ubase_cmd.c | 87 +++ drivers/ub/ubase/ubase_cmd.h | 18 + drivers/ub/ubase/ubase_dev.c | 656 ++++++++++++++++++ drivers/ub/ubase/ubase_dev.h | 288 ++++++++ drivers/ub/ubase/ubase_eq.c | 158 +++++ drivers/ub/ubase/ubase_eq.h | 75 ++ drivers/ub/ubase/ubase_main.c | 39 ++ drivers/ub/ubase/ubase_reset.h | 14 + drivers/ub/ubase/ubase_ubus.c | 371 ++++++++++ drivers/ub/ubase/ubase_ubus.h | 46 ++ include/ub/ubase/ubase_comm_cmd.h | 23 + include/ub/ubase/ubase_comm_debugfs.h | 40 ++ include/ub/ubase/ubase_comm_dev.h | 224 ++++++ include/ub/ubase/ubase_comm_eq.h | 53 ++ include/ub/ubase/ubase_comm_hw.h | 53 ++ include/ub/ubase/ubase_comm_stats.h | 119 ++++ 24 files changed, 2536 insertions(+) create mode 100644 anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UBASE create mode 100644 drivers/ub/ubase/Kconfig create mode 100644 drivers/ub/ubase/Makefile create mode 100644 drivers/ub/ubase/debugfs/ubase_debugfs.c create mode 100644 drivers/ub/ubase/debugfs/ubase_debugfs.h create mode 100644 drivers/ub/ubase/ubase.h create mode 100644 drivers/ub/ubase/ubase_cmd.c create mode 100644 drivers/ub/ubase/ubase_cmd.h create mode 100644 drivers/ub/ubase/ubase_dev.c create mode 100644 drivers/ub/ubase/ubase_dev.h create mode 100644 drivers/ub/ubase/ubase_eq.c create mode 100644 drivers/ub/ubase/ubase_eq.h create mode 100644 drivers/ub/ubase/ubase_main.c create mode 100644 drivers/ub/ubase/ubase_reset.h create mode 100644 drivers/ub/ubase/ubase_ubus.c create mode 100644 drivers/ub/ubase/ubase_ubus.h create mode 100644 include/ub/ubase/ubase_comm_cmd.h create mode 100644 include/ub/ubase/ubase_comm_debugfs.h create mode 100644 include/ub/ubase/ubase_comm_dev.h create mode 100644 include/ub/ubase/ubase_comm_eq.h create mode 100644 include/ub/ubase/ubase_comm_hw.h create mode 100644 include/ub/ubase/ubase_comm_stats.h diff --git a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UBASE b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UBASE new file mode 100644 index 000000000000..a7b7b9a1f12a --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UBASE @@ -0,0 +1 @@ +CONFIG_UB_UBASE=m diff --git a/drivers/ub/Kconfig b/drivers/ub/Kconfig index d876a10ca180..9755ce29deea 100644 --- a/drivers/ub/Kconfig +++ b/drivers/ub/Kconfig @@ -15,4 +15,5 @@ menuconfig UB if UB source "drivers/ub/ubus/Kconfig" source "drivers/ub/ubfi/Kconfig" +source "drivers/ub/ubase/Kconfig" endif # UB diff --git a/drivers/ub/Makefile b/drivers/ub/Makefile index 7860f8e74a47..72eead96aa95 100644 --- a/drivers/ub/Makefile +++ b/drivers/ub/Makefile @@ -2,3 +2,4 @@ obj-y += ubus/ obj-y += ubfi/ +obj-$(CONFIG_UB_UBASE) += ubase/ diff --git a/drivers/ub/ubase/Kconfig b/drivers/ub/ubase/Kconfig new file mode 100644 index 000000000000..c9137f193a47 --- /dev/null +++ b/drivers/ub/ubase/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# ubase configuration +# + +menuconfig UB_UBASE + default n + tristate "UBASE support" + depends on UB_UBUS_BUS && UB_UBUS_USI + depends on UB_UMMU_CORE_DRIVER + help + This option enables support for the ubase module of Unifiedbus, + including support for getting resource and create auxiliary bus + for upper Unifiedbus modules like unic, udma and cdma. + Say 'Y' here if you want to enable Unifiedbus upper module. diff --git a/drivers/ub/ubase/Makefile b/drivers/ub/ubase/Makefile new file mode 100644 index 000000000000..17069b9f624e --- /dev/null +++ b/drivers/ub/ubase/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +## Makefile for the Hisilicon ubase drivers. +# +## + +ccflags-y += -I$(srctree)/$(src) +ccflags-y += -I$(srctree)/drivers/ub/ubase/debugfs + +MODULE_NAME := ubase + +UBASE_OBJS := ubase_main.o ubase_dev.o ubase_cmd.o \ + debugfs/ubase_debugfs.o ubase_eq.o ubase_ubus.o + +$(MODULE_NAME)-objs := $(UBASE_OBJS) +obj-$(CONFIG_UB_UBASE) := ubase.o diff --git a/drivers/ub/ubase/debugfs/ubase_debugfs.c b/drivers/ub/ubase/debugfs/ubase_debugfs.c new file mode 100644 index 000000000000..75bf029f17a5 --- /dev/null +++ b/drivers/ub/ubase/debugfs/ubase_debugfs.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include +#include +#include + +#include "ubase_dev.h" +#include "ubase_debugfs.h" + +static struct dentry *ubase_dbgfs_root; + +static bool __ubase_dbg_dentry_support(struct device *dev, u32 property) +{ + struct ubase_dev *udev = dev_get_drvdata(dev); + + if (((property & UBASE_SUP_UNIC) && ubase_dev_unic_supported(udev)) || + ((property & UBASE_SUP_UDMA) && ubase_dev_udma_supported(udev)) || + ((property & UBASE_SUP_CDMA) && ubase_dev_cdma_supported(udev)) || + ((property & UBASE_SUP_PMU) && ubase_dev_pmu_supported(udev))) { + if (((property & UBASE_SUP_UBL) && ubase_dev_ubl_supported(udev)) || + ((property & UBASE_SUP_ETH) && ubase_dev_eth_mac_supported(udev))) + return true; + } + + return false; +} + +static struct ubase_dbg_dentry_info ubase_dbg_dentry[] = { + /* ue debugfs top-level directory, + * "dev_name" refers to the ue name + */ + { + .name = "dev_name", + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_PMU | + UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + }, +}; + +static struct ubase_dbg_cmd_info ubase_dbg_cmd[] = { +}; + +static int ubase_dbg_create_dir(struct device *dev, + struct ubase_dbg_dentry_info *dirs, u32 root_idx) +{ + u32 i; + + for (i = 0; i < root_idx; i++) { + if (!dirs[i].support(dev, dirs[i].property)) + continue; + + dirs[i].dentry = debugfs_create_dir(dirs[i].name, + dirs[root_idx].dentry); + if (IS_ERR(dirs[i].dentry)) { + dev_err(dev, "failed to create %s dir.\n", dirs[i].name); + return PTR_ERR(dirs[i].dentry); + } + } + + return 0; +} + +static int ubase_dbg_create_file(struct device *dev, struct ubase_dbgfs *dbgfs, + struct ubase_dbg_dentry_info *dirs) +{ + struct ubase_dbg_cmd_info *cmd_info; + int i, ret; + + cmd_info = dbgfs->cmd_info; + for (i = 0; i < dbgfs->cmd_info_size; i++) { + if (!cmd_info[i].support(dev, cmd_info[i].property)) + continue; + + ret = cmd_info[i].init(dev, dirs, dbgfs, i); + if (ret) { + dev_err(dev, "failed to init cmd %s, ret = %d.\n", + cmd_info[i].name, ret); + return ret; + } + } + + return 0; +} + +int ubase_dbg_create_dentry(struct device *dev, struct ubase_dbgfs *dbgfs, + struct ubase_dbg_dentry_info *dirs, u32 root_idx) +{ + int ret; + + if (!dev || !dbgfs || !dirs || !dbgfs->cmd_info) + return -EINVAL; + + ret = ubase_dbg_create_dir(dev, dirs, root_idx); + if (ret) { + dev_err(dev, + "failed to create ubase debugfs dirs, ret = %d.\n", ret); + return ret; + } + + ret = ubase_dbg_create_file(dev, dbgfs, dirs); + if (ret) + dev_err(dev, + "failed to create ubase debugfs files, ret = %d.\n", ret); + + return ret; +} +EXPORT_SYMBOL(ubase_dbg_create_dentry); + +int ubase_dbg_init(struct ubase_dev *udev) +{ + const char *name = dev_name(udev->dev); + struct device *dev = udev->dev; + int ret; + + udev->dbgfs.dentry = debugfs_create_dir(name, ubase_dbgfs_root); + if (IS_ERR(udev->dbgfs.dentry)) { + ubase_err(udev, "failed to create ubase debugfs root dir.\n"); + return PTR_ERR(udev->dbgfs.dentry); + } + + ubase_dbg_dentry[UBASE_DBG_DENTRY_ROOT].dentry = udev->dbgfs.dentry; + udev->dbgfs.cmd_info = ubase_dbg_cmd; + udev->dbgfs.cmd_info_size = ARRAY_SIZE(ubase_dbg_cmd); + + ret = ubase_dbg_create_dentry(dev, &udev->dbgfs, ubase_dbg_dentry, + ARRAY_SIZE(ubase_dbg_dentry) - 1); + if (ret) { + ubase_err(udev, + "failed to create ubase debugfs dentry, ret = %d.\n", + ret); + goto create_dentry_err; + } + + return 0; + +create_dentry_err: + debugfs_remove_recursive(udev->dbgfs.dentry); + + return ret; +} + +void ubase_dbg_uninit(struct ubase_dev *udev) +{ + debugfs_remove_recursive(udev->dbgfs.dentry); +} + +int ubase_dbg_register_debugfs(void) +{ +#define UBASE_DBGFS_ROOT "ubase" + + ubase_dbgfs_root = debugfs_create_dir(UBASE_DBGFS_ROOT, NULL); + if (IS_ERR(ubase_dbgfs_root)) + return PTR_ERR(ubase_dbgfs_root); + + return 0; +} + +void ubase_dbg_unregister_debugfs(void) +{ + debugfs_remove_recursive(ubase_dbgfs_root); +} diff --git a/drivers/ub/ubase/debugfs/ubase_debugfs.h b/drivers/ub/ubase/debugfs/ubase_debugfs.h new file mode 100644 index 000000000000..8b68b75d4cfc --- /dev/null +++ b/drivers/ub/ubase/debugfs/ubase_debugfs.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UBASE_DEBUGFS_H__ +#define __UBASE_DEBUGFS_H__ + +#include +#include +#include + +#include "ubase_dev.h" + +enum ubase_dbg_dentry_type { + UBASE_DBG_DENTRY_CONTEXT = 0, + UBASE_DBG_DENTRY_QOS, + /* must be the last entry. */ + UBASE_DBG_DENTRY_ROOT, +}; + +int ubase_dbg_init(struct ubase_dev *udev); +void ubase_dbg_uninit(struct ubase_dev *udev); +int ubase_dbg_register_debugfs(void); +void ubase_dbg_unregister_debugfs(void); + +#endif diff --git a/drivers/ub/ubase/ubase.h b/drivers/ub/ubase/ubase.h new file mode 100644 index 000000000000..1f1a157d8a98 --- /dev/null +++ b/drivers/ub/ubase/ubase.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UBASE_H__ +#define __UBASE_H__ + +#include +#include + +#define UBASE_CAP_LEN 3 +#define UBASE_MAX_TCG_NUM (4) + +struct ubase_delay_work { + struct delayed_work service_task; + unsigned long state; +}; + +enum { + UBASE_SUPPORT_UBL_B = 0, + UBASE_SUPPORT_TA_EXTDB_BUF_B = 2, + UBASE_SUPPORT_TA_TIMER_BUF_B = 3, + UBASE_SUPPORT_ERR_HANDLE_B = 4, + UBASE_SUPPORT_CTRLQ_B = 5, + UBASE_SUPPORT_ETH_MAC_B = 6, + UBASE_SUPPORT_MAC_STATS_B = 10, + UBASE_SUPPORT_PRE_ALLOC_B = 13, + UBASE_SUPPORT_UDMA_DISABLE_B = 14, + UBASE_SUPPORT_UNIC_DISABLE_B = 15, + UBASE_SUPPORT_UVB_B = 16, + UBASE_SUPPORT_IP_OVER_URMA_B = 17, + UBASE_SUPPORT_IP_OVER_URMA_UTP_B = 18, + UBASE_SUPPORT_ACTIVATE_PROXY_B = 19, + UBASE_SUPPORT_UTP_B = 20, + + /* must be last entry and it should <= UBASE_CAP_LEN * 32 */ + UBASE_SUPPORT_MASK_NBITS +}; + +#define ubase_get_cap_bit(udev, nr) \ + test_bit(nr, (unsigned long *)((udev)->cap_bits)) + +#endif diff --git a/drivers/ub/ubase/ubase_cmd.c b/drivers/ub/ubase/ubase_cmd.c new file mode 100644 index 000000000000..ab47d8ae9021 --- /dev/null +++ b/drivers/ub/ubase/ubase_cmd.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include + +#include "ubase_cmd.h" + +int __ubase_register_crq_event(struct ubase_dev *udev, + struct ubase_crq_event_nb *nb) +{ + struct ubase_crq_event_nbs *nbs, *tmp, *new_nbs; + struct ubase_crq_table *crq_table; + int ret; + + crq_table = &udev->crq_table; + mutex_lock(&crq_table->lock); + list_for_each_entry_safe(nbs, tmp, &crq_table->nbs.list, list) { + if (unlikely(nbs->nb.opcode == nb->opcode)) { + ret = -EEXIST; + goto err_crq_register; + } + } + + new_nbs = kzalloc(sizeof(*new_nbs), GFP_KERNEL); + if (!new_nbs) { + ret = -ENOMEM; + goto err_crq_register; + } + + new_nbs->nb = *nb; + list_add_tail(&new_nbs->list, &crq_table->nbs.list); + mutex_unlock(&crq_table->lock); + + return 0; + +err_crq_register: + mutex_unlock(&crq_table->lock); + + ubase_err(udev, "failed to register crq event, opcode = 0x%x, ret = %d.\n", + nb->opcode, ret); + return ret; +} + +int ubase_register_crq_event(struct auxiliary_device *aux_dev, + struct ubase_crq_event_nb *nb) +{ + struct ubase_dev *udev; + + if (!aux_dev || !nb || !nb->crq_handler) + return -EINVAL; + + udev = __ubase_get_udev_by_adev(aux_dev); + return __ubase_register_crq_event(udev, nb); +} +EXPORT_SYMBOL(ubase_register_crq_event); + +void __ubase_unregister_crq_event(struct ubase_dev *udev, u16 opcode) +{ + struct ubase_crq_event_nbs *nbs, *tmp; + struct ubase_crq_table *crq_table; + + crq_table = &udev->crq_table; + mutex_lock(&crq_table->lock); + list_for_each_entry_safe(nbs, tmp, &crq_table->nbs.list, list) { + if (nbs->nb.opcode == opcode) { + list_del(&nbs->list); + kfree(nbs); + break; + } + } + mutex_unlock(&crq_table->lock); +} + +void ubase_unregister_crq_event(struct auxiliary_device *aux_dev, u16 opcode) +{ + struct ubase_dev *udev; + + if (!aux_dev) + return; + + udev = __ubase_get_udev_by_adev(aux_dev); + __ubase_unregister_crq_event(udev, opcode); +} +EXPORT_SYMBOL(ubase_unregister_crq_event); diff --git a/drivers/ub/ubase/ubase_cmd.h b/drivers/ub/ubase/ubase_cmd.h new file mode 100644 index 000000000000..0f1d92ebe511 --- /dev/null +++ b/drivers/ub/ubase/ubase_cmd.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UBASE_CMD_H__ +#define __UBASE_CMD_H__ + +#include + +#include "ubase_dev.h" + +int __ubase_register_crq_event(struct ubase_dev *udev, + struct ubase_crq_event_nb *nb); +void __ubase_unregister_crq_event(struct ubase_dev *udev, u16 opcode); + +#endif diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c new file mode 100644 index 000000000000..be052e9cad61 --- /dev/null +++ b/drivers/ub/ubase/ubase_dev.c @@ -0,0 +1,656 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include +#include + +#include "debugfs/ubase_debugfs.h" +#include "ubase_cmd.h" +#include "ubase_dev.h" + +#define UBASE_PERIOD_100MS 100 + +static int ubase_debug; +module_param_named(debug, ubase_debug, int, 0644); +MODULE_PARM_DESC(debug, "enable ubase debug log, 0:disable, others:enable, default:0"); + +static DEFINE_IDA(ubase_adev_ida); + +bool ubase_dev_urma_supported(struct ubase_dev *udev) +{ + struct ub_entity *ue = container_of(udev->dev, struct ub_entity, dev); + + switch (uent_device(ue)) { + case UBASE_DEV_ID_K_0_URMA_MUE: + case UBASE_DEV_ID_K_0_URMA_UE: + case UBASE_DEV_ID_A_0_URMA_MUE: + case UBASE_DEV_ID_A_0_URMA_UE: + case UBASE_DEV_ID_A_0_UBOE_MUE: + case UBASE_DEV_ID_A_0_UBOE_UE: + break; + default: + return false; + } + + return true; +} + +bool ubase_dev_unic_supported(struct ubase_dev *udev) +{ + struct ub_entity *ue = container_of(udev->dev, struct ub_entity, dev); + + switch (uent_device(ue)) { + case UBASE_DEV_ID_K_0_URMA_MUE: + case UBASE_DEV_ID_A_0_URMA_MUE: + case UBASE_DEV_ID_A_0_UBOE_MUE: + break; + default: + return false; + } + + return !ubase_get_cap_bit(udev, UBASE_SUPPORT_UNIC_DISABLE_B); +} + +bool ubase_dev_cdma_supported(struct ubase_dev *udev) +{ + struct ub_entity *ue = container_of(udev->dev, struct ub_entity, dev); + + switch (uent_device(ue)) { + case UBASE_DEV_ID_K_0_CDMA_MUE: + case UBASE_DEV_ID_K_0_CDMA_UE: + case UBASE_DEV_ID_A_0_CDMA_MUE: + case UBASE_DEV_ID_A_0_CDMA_UE: + break; + default: + return false; + } + + return true; +} + +bool ubase_dev_pmu_supported(struct ubase_dev *udev) +{ + struct ub_entity *ue = container_of(udev->dev, struct ub_entity, dev); + + switch (uent_device(ue)) { + case UBASE_DEV_ID_K_0_PMU_MUE: + case UBASE_DEV_ID_K_0_PMU_UE: + case UBASE_DEV_ID_A_0_PMU_MUE: + case UBASE_DEV_ID_A_0_PMU_UE: + break; + default: + return false; + } + + return true; +} + +bool ubase_dev_fwctl_supported(struct ubase_dev *udev) +{ + return ubase_dev_pmu_supported(udev); +} + +static struct ubase_adev_device { + const char *suffix; + bool (*is_supported)(struct ubase_dev *dev); +} ubase_adev_devices[UBASE_DRV_MAX] = { + [UBASE_DRV_UNIC] = { + .suffix = "unic", + .is_supported = &ubase_dev_unic_supported + }, + [UBASE_DRV_UDMA] = { + .suffix = "udma", + .is_supported = &ubase_dev_udma_supported + }, + [UBASE_DRV_CDMA] = { + .suffix = "cdma", + .is_supported = &ubase_dev_cdma_supported + }, + [UBASE_DRV_FWCTL] = { + .suffix = "fwctl", + .is_supported = &ubase_dev_fwctl_supported + }, + [UBASE_DRV_PMU] = { + .suffix = "pmu", + .is_supported = &ubase_dev_pmu_supported + }, + [UBASE_DRV_UVB] = { + .suffix = "uvb", + .is_supported = &ubase_dev_uvb_supported + }, +}; + +int ubase_adev_idx_alloc(void) +{ + return ida_alloc(&ubase_adev_ida, GFP_KERNEL); +} + +void ubase_adev_idx_free(int id) +{ + ida_free(&ubase_adev_ida, id); +} + +static void ubase_port_handler(struct ubase_dev *udev, bool link_up) +{ + struct ubase_adev *uadev; + int i; + + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits)) + return; + + mutex_lock(&udev->priv.uadev_lock); + for (i = 0; i < UBASE_DRV_MAX; i++) { + uadev = udev->priv.uadev[i]; + if (!uadev) + continue; + + mutex_lock(&uadev->port_lock); + if (uadev->port_handler) + uadev->port_handler(&uadev->adev, link_up); + mutex_unlock(&uadev->port_lock); + } + mutex_unlock(&udev->priv.uadev_lock); +} + +void ubase_port_down(struct ubase_dev *udev) +{ + ubase_port_handler(udev, 0); +} + +void ubase_port_up(struct ubase_dev *udev) +{ + ubase_port_handler(udev, 1); +} + +static void ubase_comm_adev_release(struct device *dev) +{ + struct ubase_adev *ubase_adev = + container_of(dev, struct ubase_adev, adev.dev); + + kfree(ubase_adev); +} + +static struct ubase_adev *ubase_add_one_adev(struct ubase_dev *udev, int idx) +{ + struct ubase_adev *uadev; + int ret; + + uadev = kzalloc(sizeof(struct ubase_adev), GFP_KERNEL); + if (!uadev) { + ubase_err(udev, "failed to alloc auxiliary device(%s.%d).\n", + ubase_adev_devices[idx].suffix, udev->dev_id); + return ERR_PTR(-ENOMEM); + } + + uadev->adev.name = ubase_adev_devices[idx].suffix; + uadev->adev.id = (u32)udev->dev_id; + uadev->adev.dev.parent = udev->dev; + uadev->adev.dev.release = ubase_comm_adev_release; + uadev->idx = idx; + uadev->udev = udev; + ATOMIC_INIT_NOTIFIER_HEAD(&uadev->comp_nh); + + ret = auxiliary_device_init(&uadev->adev); + if (ret) { + kfree(uadev); + ubase_err(udev, + "failed to init auxiliary device(%s.%d), ret = %d\n", + ubase_adev_devices[idx].suffix, udev->dev_id, ret); + return ERR_PTR(ret); + } + + ret = auxiliary_device_add(&uadev->adev); + if (ret) { + auxiliary_device_uninit(&uadev->adev); + ubase_err(udev, + "failed to add auxiliary device(%s.%d), ret = %d\n", + ubase_adev_devices[idx].suffix, udev->dev_id, ret); + return ERR_PTR(ret); + } + + mutex_init(&uadev->virt_lock); + mutex_init(&uadev->port_lock); + mutex_init(&uadev->reset_lock); + mutex_init(&uadev->activate_lock); + + return uadev; +} + +static void ubase_del_one_adev(struct ubase_dev *udev, int idx) +{ + struct ubase_priv *priv = &udev->priv; + struct ubase_adev *uadev; + + uadev = priv->uadev[idx]; + + mutex_destroy(&uadev->activate_lock); + mutex_destroy(&uadev->reset_lock); + mutex_destroy(&uadev->port_lock); + mutex_destroy(&uadev->virt_lock); + auxiliary_device_delete(&uadev->adev); + auxiliary_device_uninit(&uadev->adev); + priv->uadev[idx] = NULL; +} + +static int ubase_init_aux_devices(struct ubase_dev *udev) +{ + struct ubase_priv *priv = &udev->priv; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(ubase_adev_devices); i++) { + if (priv->uadev[i]) + continue; + + if (!ubase_adev_devices[i].is_supported || + !ubase_adev_devices[i].is_supported(udev)) + continue; + + priv->uadev[i] = ubase_add_one_adev(udev, i); + if (IS_ERR(priv->uadev[i])) { + ret = PTR_ERR(priv->uadev[i]); + priv->uadev[i] = NULL; + ubase_err(udev, + "failed to load auxiliary device(%s.%d)\n", + ubase_adev_devices[i].suffix, udev->dev_id); + goto err_add_aux_dev; + } + } + + mutex_init(&udev->priv.uadev_lock); + + return 0; + +err_add_aux_dev: + for (; i >= 0; i--) { + if (!priv->uadev[i]) + continue; + + ubase_del_one_adev(udev, i); + } + + return ret; +} + +static void ubase_uninit_aux_devices(struct ubase_dev *udev) +{ + struct ubase_priv *priv = &udev->priv; + int i; + + mutex_lock(&priv->uadev_lock); + for (i = ARRAY_SIZE(ubase_adev_devices) - 1; i >= 0; i--) { + if (!priv->uadev[i]) + continue; + + ubase_del_one_adev(udev, i); + } + mutex_unlock(&priv->uadev_lock); + + mutex_destroy(&udev->priv.uadev_lock); +} + +static void ubase_cancel_period_service_task(struct ubase_dev *udev) +{ + if (udev->period_service_task.service_task.work.func) + cancel_delayed_work_sync(&udev->period_service_task.service_task); +} + +static int ubase_enable_period_service_task(struct ubase_dev *udev) +{ + struct ubase_delay_work *period_work = &udev->period_service_task; + unsigned long delta; + + delta = round_jiffies_relative(msecs_to_jiffies(UBASE_PERIOD_100MS)); + mod_delayed_work(udev->ubase_period_wq, + &period_work->service_task, + delta); + + return 0; +} + +static void ubase_period_service_task(struct work_struct *work) +{ +#define UBASE_STATS_TIMER_INTERVAL (300000 / (UBASE_PERIOD_100MS)) +#define UBASE_QUERY_SL_TIMER_INTERVAL (1000 / (UBASE_PERIOD_100MS)) + + struct ubase_delay_work *ubase_work = + container_of(work, struct ubase_delay_work, service_task.work); + struct ubase_dev *udev = container_of(ubase_work, struct ubase_dev, + period_service_task); + + if (test_bit(UBASE_STATE_DISABLED_B, &udev->state_bits)) { + ubase_enable_period_service_task(udev); + return; + } + + udev->serv_proc_cnt++; + ubase_enable_period_service_task(udev); +} + +static void ubase_init_delayed_work(struct ubase_dev *udev) +{ + INIT_DELAYED_WORK(&udev->period_service_task.service_task, + ubase_period_service_task); +} + +static int ubase_wq_init(struct ubase_dev *udev) +{ +#define UBASE_ALLOC_WQ(name) alloc_workqueue("%s", WQ_UNBOUND, 0, name) + + udev->ubase_wq = UBASE_ALLOC_WQ("ubase"); + if (!udev->ubase_wq) { + ubase_err(udev, "failed to alloc ubase workqueue.\n"); + goto err_alloc_ubase_wq; + } + + udev->ubase_async_wq = UBASE_ALLOC_WQ("ubase_async_service"); + if (!udev->ubase_async_wq) { + ubase_err(udev, "failed to alloc ubase async workqueue.\n"); + goto err_alloc_ubase_async_wq; + } + + udev->ubase_reset_wq = UBASE_ALLOC_WQ("ubase_reset_service"); + if (!udev->ubase_reset_wq) { + ubase_err(udev, "failed to alloc ubase reset workqueue.\n"); + goto err_alloc_ubase_reset_wq; + } + + udev->ubase_period_wq = UBASE_ALLOC_WQ("ubase_period_service"); + if (!udev->ubase_period_wq) { + ubase_err(udev, "failed to alloc ubase period workqueue.\n"); + goto err_alloc_ubase_period_wq; + } + + udev->ubase_arq_wq = UBASE_ALLOC_WQ("ubase_arq_service"); + if (!udev->ubase_arq_wq) { + ubase_err(udev, "failed to alloc ubase arq workqueue.\n"); + goto err_alloc_ubase_arq_wq; + } + + ubase_init_delayed_work(udev); + return 0; + +err_alloc_ubase_arq_wq: + destroy_workqueue(udev->ubase_period_wq); +err_alloc_ubase_period_wq: + destroy_workqueue(udev->ubase_reset_wq); +err_alloc_ubase_reset_wq: + destroy_workqueue(udev->ubase_async_wq); +err_alloc_ubase_async_wq: + destroy_workqueue(udev->ubase_wq); +err_alloc_ubase_wq: + return -ENOMEM; +} + +static void ubase_wq_uninit(struct ubase_dev *udev) +{ + destroy_workqueue(udev->ubase_arq_wq); + destroy_workqueue(udev->ubase_period_wq); + destroy_workqueue(udev->ubase_reset_wq); + destroy_workqueue(udev->ubase_async_wq); + destroy_workqueue(udev->ubase_wq); +} + +static struct ubase_crq_event_nb ubase_crq_events[] = { +}; + +static void ubase_unregister_cmdq_crq_event(struct ubase_dev *udev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ubase_crq_events); i++) + __ubase_unregister_crq_event(udev, ubase_crq_events[i].opcode); +} + +static int ubase_register_cmdq_crq_event(struct ubase_dev *udev) +{ + int ret, i; + + for (i = 0; i < ARRAY_SIZE(ubase_crq_events); i++) { + ubase_crq_events[i].back = udev; + + ret = __ubase_register_crq_event(udev, &ubase_crq_events[i]); + if (ret) { + ubase_err(udev, + "failed to register crq event[%d], ret = %d.\n", + i, ret); + goto err_reg_event; + } + } + + return 0; + +err_reg_event: + for (i = i - 1; i >= 0; i--) + __ubase_unregister_crq_event(udev, ubase_crq_events[i].opcode); + return ret; +} + +static const struct ubase_init_function ubase_init_func_map[] = { + { + "init work queue", UBASE_SUP_ALL, 0, + ubase_wq_init, ubase_wq_uninit + }, + { + "init cmd queue", UBASE_SUP_ALL, 1, + NULL, NULL + }, + { + "query dev res", UBASE_SUP_ALL, 0, + NULL, NULL + }, + { + "init mailbox", UBASE_SUP_NO_PMU, 0, + NULL, NULL + }, + { + "query chip info", UBASE_SUP_ALL, 0, + NULL, NULL + }, + { + "query controller_info", UBASE_SUP_NO_PMU, 0, + NULL, NULL + }, + { + "query hw oor caps", UBASE_SUP_NO_PMU, 0, + NULL, NULL + }, + { + "init irq table", UBASE_SUP_NO_PMU, 1, + NULL, NULL + }, + { + "init ctrl queue", UBASE_SUP_NO_PMU, 1, + NULL, NULL + }, + { + "register aeq event", UBASE_SUP_NO_PMU, 0, + ubase_register_ae_event, ubase_unregister_ae_event + }, + { + "register cmdq crq event", UBASE_SUP_NO_PMU, 0, + ubase_register_cmdq_crq_event, ubase_unregister_cmdq_crq_event + }, + { + "register ctrlq crq event", UBASE_SUP_NO_PMU, 0, + NULL, NULL + }, + { + "init qos", UBASE_SUP_ALL, 0, + NULL, NULL + }, + { + "prealloc memory", UBASE_SUP_UDMA, 1, + NULL, NULL + }, + { + "init ue", UBASE_SUP_NO_PMU, 0, + NULL, NULL + }, + { + "init hw", UBASE_SUP_NO_PMU, 1, + NULL, NULL + }, + { + "init debugfs", UBASE_SUP_ALL, 0, + ubase_dbg_init, ubase_dbg_uninit + }, + { + "init auxiliary devices", UBASE_SUP_ALL, 0, + ubase_init_aux_devices, ubase_uninit_aux_devices + }, + { + "enable period service task", UBASE_SUP_NO_PMU, 0, + ubase_enable_period_service_task, ubase_cancel_period_service_task + }, + { + "enable ce irq", UBASE_SUP_NO_PMU, 1, + NULL, NULL + }, +}; + +static bool ubase_init_func_support(struct ubase_dev *udev, u32 support) +{ + return (((support & UBASE_SUP_UNIC) && ubase_dev_unic_supported(udev)) || + ((support & UBASE_SUP_UDMA) && ubase_dev_udma_supported(udev)) || + ((support & UBASE_SUP_CDMA) && ubase_dev_cdma_supported(udev)) || + ((support & UBASE_SUP_PMU) && ubase_dev_pmu_supported(udev))); +} + +int ubase_dev_init(struct ubase_dev *udev) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(ubase_init_func_map); i++) { + if (!ubase_init_func_support(udev, + ubase_init_func_map[i].support_devs)) + continue; + + if (ubase_init_func_map[i].init_func) { + ret = ubase_init_func_map[i].init_func(udev); + if (ret) { + ubase_err(udev, "failed to %s, ret = %d.\n", + ubase_init_func_map[i].err_msg, ret); + goto err_init; + } + } + } + + set_bit(UBASE_STATE_INITED_B, &udev->state_bits); + + return 0; + +err_init: + for (i -= 1; i >= 0; i--) { + if (!ubase_init_func_support(udev, + ubase_init_func_map[i].support_devs)) + continue; + + if (ubase_init_func_map[i].uninit_func) + ubase_init_func_map[i].uninit_func(udev); + } + + return ret; +} + +void ubase_dev_uninit(struct ubase_dev *udev) +{ + int i; + + if (udev->service_task.service_task.work.func) + cancel_delayed_work_sync(&udev->service_task.service_task); + flush_workqueue(udev->ubase_async_wq); + + for (i = ARRAY_SIZE(ubase_init_func_map) - 1; i >= 0; i--) { + if (!ubase_init_func_support(udev, + ubase_init_func_map[i].support_devs)) + continue; + + if (ubase_init_func_map[i].uninit_func) + ubase_init_func_map[i].uninit_func(udev); + } +} + +bool ubase_adev_ubl_supported(struct auxiliary_device *adev) +{ + if (!adev) + return false; + + return ubase_dev_ubl_supported(__ubase_get_udev_by_adev(adev)); +} +EXPORT_SYMBOL(ubase_adev_ubl_supported); + +bool ubase_adev_eth_mac_supported(struct auxiliary_device *adev) +{ + if (!adev) + return false; + + return ubase_dev_eth_mac_supported(__ubase_get_udev_by_adev(adev)); +} +EXPORT_SYMBOL(ubase_adev_eth_mac_supported); + +struct ubase_resource_space *ubase_get_io_base(struct auxiliary_device *adev) +{ + if (!adev) + return NULL; + + return &__ubase_get_udev_by_adev(adev)->hw.io_base; +} +EXPORT_SYMBOL(ubase_get_io_base); + +struct ubase_resource_space *ubase_get_mem_base(struct auxiliary_device *adev) +{ + if (!adev) + return NULL; + + return &__ubase_get_udev_by_adev(adev)->hw.mem_base; +} +EXPORT_SYMBOL(ubase_get_mem_base); + +struct ubase_caps *ubase_get_dev_caps(struct auxiliary_device *adev) +{ + if (!adev) + return NULL; + + return &__ubase_get_udev_by_adev(adev)->caps.dev_caps; +} +EXPORT_SYMBOL(ubase_get_dev_caps); + +struct ubase_adev_caps *ubase_get_udma_caps(struct auxiliary_device *adev) +{ + struct ubase_dev *udev; + + if (!adev) + return NULL; + + udev = __ubase_get_udev_by_adev(adev); + + return &udev->caps.udma_caps; +} +EXPORT_SYMBOL(ubase_get_udma_caps); + +struct ubase_adev_caps *ubase_get_cdma_caps(struct auxiliary_device *adev) +{ + return ubase_get_udma_caps(adev); +} +EXPORT_SYMBOL(ubase_get_cdma_caps); + +struct ubase_adev_caps *ubase_get_unic_caps(struct auxiliary_device *adev) +{ + struct ubase_dev *udev; + + if (!adev) + return NULL; + + udev = __ubase_get_udev_by_adev(adev); + + return &udev->caps.unic_caps; +} +EXPORT_SYMBOL(ubase_get_unic_caps); + +bool ubase_dbg_default(void) +{ + return ubase_debug; +} diff --git a/drivers/ub/ubase/ubase_dev.h b/drivers/ub/ubase/ubase_dev.h new file mode 100644 index 000000000000..fe587046b291 --- /dev/null +++ b/drivers/ub/ubase/ubase_dev.h @@ -0,0 +1,288 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UBASE_DEV_H__ +#define __UBASE_DEV_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ubase.h" +#include "ubase_eq.h" +#include "ubase_ubus.h" + +#define UBASE_MOD_VERSION "1.0" + +#define ubase_dbg(_udev, fmt, ...) do { \ + if (ubase_dbg_default()) \ + dev_info(_udev->dev, "(pid %d) " fmt, \ + current->pid, ##__VA_ARGS__); \ + } while (0) + +#define ubase_err(_udev, fmt, ...) \ + dev_err(_udev->dev, "(pid %d) " fmt, \ + current->pid, ##__VA_ARGS__) + +#define ubase_info(_udev, fmt, ...) \ + dev_info(_udev->dev, "(pid %d) " fmt, \ + current->pid, ##__VA_ARGS__) + +#define ubase_warn(_udev, fmt, ...) \ + dev_warn(_udev->dev, "(pid %d) " fmt, \ + current->pid, ##__VA_ARGS__) + +struct ubase_adev { + struct auxiliary_device adev; + struct ubase_dev *udev; + struct atomic_notifier_head comp_nh; + struct notifier_block comp_notifier; + int idx; + + struct mutex virt_lock; + void (*virt_handler)(struct auxiliary_device *adev, u16 bus_ue_id, + bool is_en); + struct mutex port_lock; + void (*port_handler)(struct auxiliary_device *adev, bool link_up); + struct mutex reset_lock; + void (*reset_handler)(struct auxiliary_device *adev, + enum ubase_reset_stage stage); + struct mutex activate_lock; + void (*activate_handler)(struct auxiliary_device *adev, bool activate); +}; + +struct ubase_priv { + struct ubase_adev *uadev[UBASE_DRV_MAX]; + struct mutex uadev_lock; /* protect uadev[] */ +}; + +struct ubase_dev_caps { + struct ubase_adev_caps udma_caps; + struct ubase_adev_caps unic_caps; + struct ubase_caps dev_caps; + struct ubase_ue_caps ue_caps; +}; + +struct ubase_mbox_cmd { + struct dma_pool *pool; + struct semaphore sem; + struct ubase_mbx_event_context ctx; +}; + +struct ubase_dma_buf { + void *addr; + dma_addr_t dma_addr; + size_t size; +}; + +struct ubase_ta_layer_ctx { + struct ubase_dma_buf extdb_buf; + struct ubase_dma_buf timer_buf; +}; + +struct ubase_tp_layer_ctx { + spinlock_t tpg_lock; + struct ubase_tpg *tpg; +}; + +struct ubase_reset_stat { + u32 reset_done_cnt; + u32 hw_reset_done_cnt; + u32 elr_reset_cnt; + u32 reset_fail_cnt; + u32 reset_retry_cnt; + u32 port_reset_cnt; + u32 himac_reset_cnt; +}; + +enum ubase_dev_state_bit { + UBASE_STATE_INITED_B, + UBASE_STATE_DISABLED_B, + UBASE_STATE_RST_HANDLING_B, + UBASE_STATE_IRQ_INVALID_B, + UBASE_STATE_PORT_RESETTING_B, + UBASE_STATE_HIMAC_RESETTING_B, + UBASE_STATE_CTX_READY_B, + UBASE_STATE_PREALLOC_OK_B, +}; + +struct ubase_crq_event_nbs { + struct list_head list; + struct ubase_crq_event_nb nb; +}; + +struct ubase_crq_table { + struct mutex lock; + unsigned long last_crq_scheduled; + struct ubase_crq_event_nbs nbs; +}; + +#define UBASE_ACT_STAT_MAX_NUM 10U +struct ubase_activate_dev_stats { + u64 act_cnt; + u64 deact_cnt; + struct { + bool activate; + int result; + time64_t time; + } stats[UBASE_ACT_STAT_MAX_NUM]; + struct mutex lock; +}; + +struct ubase_stats { + struct mutex stats_lock; + struct ubase_eth_mac_stats eth_stats; + struct ubase_activate_dev_stats activate_record; +}; + +struct ubase_act_info { + u16 wait_msn; + int result; + struct completion activate_done; +}; + +struct ubase_act_ctx { + u16 msn; + struct ubase_act_info self; + struct ubase_act_info other; + struct mutex lock; +}; + +struct ubase_arq_msg { + u16 opcode; + void *data; + u32 data_len; +}; + +#define MAX_ARQ_MSG_NUM 128 +struct ubase_arq_msg_ring { + u8 pi; + u8 ci; + atomic_t count; + struct ubase_arq_msg msg[MAX_ARQ_MSG_NUM]; +}; + +struct ubase_pmem_ctx { + u16 page_cnt; + struct page **pgs; + struct scatterlist *sg; + dma_addr_t dma_addr; +}; + +struct ubase_prealloc_mem_info { + struct ubase_pmem_ctx comm; + struct ubase_pmem_ctx udma; +}; + +struct ubase_dev { + struct device *dev; + int dev_id; + struct ubase_priv priv; + struct ubase_hw hw; + + struct ubase_dev_caps caps; + struct ubase_adev_qos qos; + struct ubase_dbgfs dbgfs; + struct ubase_ctx_buf ctx_buf; + struct ubase_ta_layer_ctx ta_ctx; + struct ubase_tp_layer_ctx tp_ctx; + u32 cap_bits[UBASE_CAP_LEN]; + struct ubase_irq_table irq_table; + struct ubase_mbox_cmd mb_cmd; + struct workqueue_struct *ubase_wq; + struct workqueue_struct *ubase_async_wq; + struct workqueue_struct *ubase_reset_wq; + struct workqueue_struct *ubase_period_wq; + struct workqueue_struct *ubase_arq_wq; + unsigned long serv_proc_cnt; + struct ubase_delay_work service_task; + struct ubase_delay_work reset_service_task; + struct ubase_delay_work period_service_task; + struct ubase_delay_work arq_service_task; + struct ubase_crq_table crq_table; + unsigned long state_bits; + struct list_head ue_list; + struct mutex ue_list_lock; + + struct ubase_reset_stat reset_stat; + enum ubase_reset_type reset_type; + unsigned long last_reset_scheduled; + enum ubase_reset_stage reset_stage; + struct ubase_stats stats; + struct ubase_act_ctx act_ctx; + struct ubase_arq_msg_ring arq; + struct ubase_prealloc_mem_info pmem_info; +}; + +#define UBASE_ERR_MSG_LEN 128 +struct ubase_init_function { + char err_msg[UBASE_ERR_MSG_LEN]; + u32 support_devs; + u8 need_reset; + int (*init_func)(struct ubase_dev *udev); + void (*uninit_func)(struct ubase_dev *udev); +}; + +bool ubase_dbg_default(void); +bool ubase_dev_urma_supported(struct ubase_dev *udev); +bool ubase_dev_unic_supported(struct ubase_dev *udev); +bool ubase_dev_cdma_supported(struct ubase_dev *udev); +bool ubase_dev_pmu_supported(struct ubase_dev *udev); +bool ubase_dev_fwctl_supported(struct ubase_dev *udev); + +static inline +struct ubase_dev *__ubase_get_udev_by_adev(struct auxiliary_device *adev) +{ + struct ubase_adev *uadev = container_of(adev, struct ubase_adev, adev); + + return uadev->udev; +} + +static inline bool ubase_dev_uvb_supported(struct ubase_dev *udev) +{ + return ubase_get_cap_bit(udev, UBASE_SUPPORT_UVB_B); +} + +static inline +struct ubase_dev *ubase_get_udev_by_adev(struct auxiliary_device *adev) +{ + if (!adev) + return NULL; + + return __ubase_get_udev_by_adev(adev); +} + +static inline bool ubase_dev_udma_supported(struct ubase_dev *udev) +{ + return ubase_dev_urma_supported(udev) && + !ubase_get_cap_bit(udev, UBASE_SUPPORT_UDMA_DISABLE_B); +} + +static inline bool ubase_dev_ubl_supported(struct ubase_dev *udev) +{ + return ubase_get_cap_bit(udev, UBASE_SUPPORT_UBL_B); +} + +static inline bool ubase_dev_eth_mac_supported(struct ubase_dev *udev) +{ + return ubase_get_cap_bit(udev, UBASE_SUPPORT_ETH_MAC_B); +} + +int ubase_adev_idx_alloc(void); +void ubase_adev_idx_free(int id); + +void ubase_port_down(struct ubase_dev *udev); +void ubase_port_up(struct ubase_dev *udev); + +int ubase_dev_init(struct ubase_dev *udev); +void ubase_dev_uninit(struct ubase_dev *udev); + +#endif diff --git a/drivers/ub/ubase/ubase_eq.c b/drivers/ub/ubase/ubase_eq.c new file mode 100644 index 000000000000..98e4bfe69ef6 --- /dev/null +++ b/drivers/ub/ubase/ubase_eq.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include + +#include "ubase_dev.h" +#include "ubase_eq.h" + +static int __ubase_event_register(struct ubase_dev *udev, + struct ubase_event_nb *cb) +{ + struct blocking_notifier_head *nh; + int ret; + + if (cb->drv_type >= UBASE_DRV_MAX) { + ubase_err(udev, "unsupported drv_type(%u).\n", cb->drv_type); + return -EINVAL; + } + + if (cb->event_type >= UBASE_EVENT_TYPE_MAX) { + ubase_err(udev, + "unsupported event type(%u).\n", cb->event_type); + return -EINVAL; + } + + nh = udev->irq_table.nh[cb->drv_type]; + ret = blocking_notifier_chain_register(&nh[cb->event_type], &cb->nb); + if (ret) + ubase_err(udev, + "failed to notifier chain register, type = %u, ret = %d.\n", + cb->event_type, ret); + + return ret; +} + +int ubase_event_register(struct auxiliary_device *adev, + struct ubase_event_nb *cb) +{ + if (!adev || !cb) + return -EINVAL; + + return __ubase_event_register(__ubase_get_udev_by_adev(adev), cb); +} +EXPORT_SYMBOL(ubase_event_register); + +static void __ubase_event_unregister(struct ubase_dev *udev, + struct ubase_event_nb *cb) +{ + struct blocking_notifier_head *nh; + int ret; + + if (cb->drv_type >= UBASE_DRV_MAX) { + ubase_err(udev, "unsupported drv_type(%u).\n", cb->drv_type); + return; + } + + if (cb->event_type >= UBASE_EVENT_TYPE_MAX) { + ubase_err(udev, + "unsupported event type(%u).\n", cb->event_type); + return; + } + + nh = udev->irq_table.nh[cb->drv_type]; + ret = blocking_notifier_chain_unregister(&nh[cb->event_type], &cb->nb); + if (ret) + ubase_err(udev, + "failed to unregister notifier chain, type = %u, ret = %d.\n", + cb->event_type, ret); +} + +void ubase_event_unregister(struct auxiliary_device *adev, + struct ubase_event_nb *cb) +{ + if (!adev || !cb) + return; + + __ubase_event_unregister(__ubase_get_udev_by_adev(adev), cb); +} +EXPORT_SYMBOL(ubase_event_unregister); + +int ubase_comp_register(struct auxiliary_device *adev, + int (*comp_handler)(struct notifier_block *nb, + unsigned long jfcn, void *data)) +{ + struct ubase_adev *uadev; + int ret; + + if (!adev || !comp_handler) + return -EINVAL; + + uadev = container_of(adev, struct ubase_adev, adev); + uadev->comp_notifier.notifier_call = comp_handler; + ret = atomic_notifier_chain_register(&uadev->comp_nh, + &uadev->comp_notifier); + if (ret) + ubase_err(uadev->udev, + "failed to register comp notifier chain, ret = %d.\n", + ret); + + return ret; +} +EXPORT_SYMBOL(ubase_comp_register); + +void ubase_comp_unregister(struct auxiliary_device *adev) +{ + struct ubase_adev *uadev; + int ret; + + if (!adev) + return; + + uadev = container_of(adev, struct ubase_adev, adev); + + ret = atomic_notifier_chain_unregister(&uadev->comp_nh, + &uadev->comp_notifier); + if (ret) + ubase_err(uadev->udev, + "failed to unregister comp notifier chain, ret = %d.\n", + ret); +} +EXPORT_SYMBOL(ubase_comp_unregister); + +static void __ubase_unregister_ae_event(struct ubase_dev *udev, int num) +{ + int i; + + for (i = 0; i < num; i++) + __ubase_event_unregister(udev, &udev->irq_table.aeq.cb[i]); +} + +void ubase_unregister_ae_event(struct ubase_dev *udev) +{ + __ubase_unregister_ae_event(udev, UBASE_AE_LEVEL_NUM); +} + +int ubase_register_ae_event(struct ubase_dev *udev) +{ + struct ubase_event_nb ubase_ae_nbs[UBASE_AE_LEVEL_NUM] = {}; + struct ubase_aeq *aeq = &udev->irq_table.aeq; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(ubase_ae_nbs); i++) { + aeq->cb[i] = ubase_ae_nbs[i]; + ret = __ubase_event_register(udev, &aeq->cb[i]); + if (ret) { + ubase_err(udev, + "failed to register asyn event[%u], ret = %d", + aeq->cb[i].event_type, ret); + __ubase_unregister_ae_event(udev, i); + return ret; + } + } + + return 0; +} diff --git a/drivers/ub/ubase/ubase_eq.h b/drivers/ub/ubase/ubase_eq.h new file mode 100644 index 000000000000..5602e166a82d --- /dev/null +++ b/drivers/ub/ubase/ubase_eq.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UBASE_EQ_H__ +#define __UBASE_EQ_H__ + +#include + +#include "ubase.h" + +#define UBASE_MIN_IRQ_NUM 3 /* for misc aeq ceq */ + +#define UBASE_INT_NAME_LEN 32 + +#define UBASE_AE_LEVEL_NUM 4 + +struct ubase_irq { + char name[UBASE_INT_NAME_LEN]; + int irqn; +}; + +struct ubase_eq_addr { + void *addr; + dma_addr_t dma_addr; + size_t size; +}; + +struct ubase_eq { + void __iomem *db_reg; + struct ubase_eq_addr addr; + u32 eqn; + u32 entries_num; + u32 state; + u32 arm_st; + u8 eqe_size; + u32 eq_period; + u32 coalesce_cnt; + int irqn; + u32 eqc_irqn; /* irqn for eqc */ + u32 cons_index; +}; + +struct ubase_ceq { + struct ubase_dev *udev; + struct ubase_eq eq; +}; + +struct ubase_aeq { + struct ubase_dev *udev; + struct ubase_eq eq; + struct ubase_event_nb cb[UBASE_AE_LEVEL_NUM]; +}; + +struct ubase_ceqs { + struct ubase_ceq *ceq; + u32 num; +}; + +struct ubase_irq_table { + struct mutex ceq_lock; + struct ubase_ceqs ceqs; + struct ubase_aeq aeq; + struct blocking_notifier_head nh[UBASE_DRV_MAX][UBASE_EVENT_TYPE_MAX]; + + struct ubase_irq **irqs; /* first one for misc */ + u32 irqs_num; +}; + +int ubase_register_ae_event(struct ubase_dev *udev); +void ubase_unregister_ae_event(struct ubase_dev *udev); + +#endif diff --git a/drivers/ub/ubase/ubase_main.c b/drivers/ub/ubase/ubase_main.c new file mode 100644 index 000000000000..2c97a842c3dd --- /dev/null +++ b/drivers/ub/ubase/ubase_main.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include + +#include "debugfs/ubase_debugfs.h" + +static int __init ubase_init(void) +{ + int ret; + + ret = ubase_dbg_register_debugfs(); + if (ret) + return ret; + + ret = ubase_ubus_register_driver(); + if (ret) + ubase_dbg_unregister_debugfs(); + + return ret; +} + +static void __exit ubase_exit(void) +{ + ubase_ubus_unregister_driver(); + ubase_dbg_unregister_debugfs(); +} + +module_init(ubase_init); +module_exit(ubase_exit); + +MODULE_DESCRIPTION("UBASE: Hisilicon Network Driver"); +MODULE_IMPORT_NS(UB_UBFI); +MODULE_IMPORT_NS(UB_UBUS); +MODULE_LICENSE("GPL"); +MODULE_VERSION(UBASE_MOD_VERSION); diff --git a/drivers/ub/ubase/ubase_reset.h b/drivers/ub/ubase/ubase_reset.h new file mode 100644 index 000000000000..ae1ae9b7fdc1 --- /dev/null +++ b/drivers/ub/ubase/ubase_reset.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UBASE_RESET_H__ +#define __UBASE_RESET_H__ + +#include "ubase_dev.h" + +#define UBASE_RST_WAIT_TIME 100 + +#endif diff --git a/drivers/ub/ubase/ubase_ubus.c b/drivers/ub/ubase/ubase_ubus.c new file mode 100644 index 000000000000..7b05707955e0 --- /dev/null +++ b/drivers/ub/ubase/ubase_ubus.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include +#include +#include + +#include "ubase_cmd.h" +#include "ubase_dev.h" +#include "ubase_reset.h" +#include "ubase_ubus.h" + +static const char ubase_ubus_driver_name[] = "ubase"; + +static const struct ub_device_id ubase_ubus_tbl[] = { + {UB_ENTITY(UBASE_VENDOR_ID, UBASE_DEV_ID_K_0_URMA_MUE), 0, 0}, + {UB_ENTITY(UBASE_VENDOR_ID, UBASE_DEV_ID_K_0_URMA_UE), 0, 0}, + {UB_ENTITY(UBASE_VENDOR_ID, UBASE_DEV_ID_K_0_CDMA_MUE), 0, 0}, + {UB_ENTITY(UBASE_VENDOR_ID, UBASE_DEV_ID_K_0_CDMA_UE), 0, 0}, + {UB_ENTITY(UBASE_VENDOR_ID, UBASE_DEV_ID_K_0_PMU_MUE), 0, 0}, + {UB_ENTITY(UBASE_VENDOR_ID, UBASE_DEV_ID_K_0_PMU_UE), 0, 0}, + {UB_ENTITY(UBASE_VENDOR_ID, UBASE_DEV_ID_A_0_URMA_MUE), 0, 0}, + {UB_ENTITY(UBASE_VENDOR_ID, UBASE_DEV_ID_A_0_URMA_UE), 0, 0}, + {UB_ENTITY(UBASE_VENDOR_ID, UBASE_DEV_ID_A_0_CDMA_MUE), 0, 0}, + {UB_ENTITY(UBASE_VENDOR_ID, UBASE_DEV_ID_A_0_CDMA_UE), 0, 0}, + {UB_ENTITY(UBASE_VENDOR_ID, UBASE_DEV_ID_A_0_PMU_MUE), 0, 0}, + {UB_ENTITY(UBASE_VENDOR_ID, UBASE_DEV_ID_A_0_PMU_UE), 0, 0}, + {UB_ENTITY(UBASE_VENDOR_ID, UBASE_DEV_ID_A_0_UBOE_MUE), 0, 0}, + {UB_ENTITY(UBASE_VENDOR_ID, UBASE_DEV_ID_A_0_UBOE_UE), 0, 0}, + /* required last entry */ + {0}, +}; +MODULE_DEVICE_TABLE(ub, ubase_ubus_tbl); + +static int ubase_ubus_init(struct ub_entity *ue) +{ +#define UBASE_UBUS_DMA_BIT 48 + + struct ubase_dev *udev = dev_get_drvdata(&ue->dev); + int ret; + + ub_entity_enable(ue, 1); + + ret = dma_set_mask_and_coherent(&ue->dev, + DMA_BIT_MASK(UBASE_UBUS_DMA_BIT)); + if (ret) { + ubase_err(udev, + "can't set consistent UBUS DMA, ret = %d.\n", ret); + goto err_enable_device; + } + + udev->hw.io_base.addr_unmapped = ub_resource_start(ue, UBASE_UBUS_IO_RESOURCE); + udev->hw.io_base.addr = ub_iomap(ue, UBASE_UBUS_IO_RESOURCE, 0); + if (!udev->hw.io_base.addr) { + ubase_err(udev, "failed to map io base.\n"); + ret = -ENOMEM; + goto err_enable_device; + } + + udev->hw.mem_base.addr_unmapped = ub_resource_start(ue, UBASE_UBUS_MEM_RESOURCE); + udev->hw.mem_base.addr = devm_ioremap_wc(&ue->dev, + ub_resource_start(ue, UBASE_UBUS_MEM_RESOURCE), + ub_resource_len(ue, UBASE_UBUS_MEM_RESOURCE)); + if (!udev->hw.mem_base.addr) { + ubase_err(udev, "failed to map memory base.\n"); + ret = -ENOMEM; + goto err_unmap_io_base; + } + + udev->hw.rs0_base.addr_unmapped = ub_resource_start(ue, UBASE_UBUS_RESOURCE_0); + udev->hw.rs0_base.addr = ub_iomap(ue, UBASE_UBUS_RESOURCE_0, 0); + if (!udev->hw.rs0_base.addr) { + ubase_err(udev, "failed to map resource0 addr.\n"); + ret = -ENOMEM; + goto err_unmap_mem_base; + } + + return 0; + +err_unmap_mem_base: + devm_iounmap(&ue->dev, udev->hw.mem_base.addr); + udev->hw.mem_base.addr = NULL; +err_unmap_io_base: + ub_iounmap(udev->hw.io_base.addr); + udev->hw.io_base.addr = NULL; +err_enable_device: + ub_entity_enable(ue, 0); + + return ret; +} + +static void ubase_ubus_uninit(struct ub_entity *ue) +{ + struct ubase_dev *udev = dev_get_drvdata(&ue->dev); + + if (udev->hw.io_base.addr) { + ub_iounmap(udev->hw.io_base.addr); + udev->hw.io_base.addr = NULL; + } + + if (udev->hw.mem_base.addr) { + devm_iounmap(&ue->dev, udev->hw.mem_base.addr); + udev->hw.mem_base.addr = NULL; + } + + if (udev->hw.rs0_base.addr) { + ub_iounmap(udev->hw.rs0_base.addr); + udev->hw.rs0_base.addr = NULL; + } + + ub_entity_enable(ue, 0); +} + +static void ubase_port_reset_prepare(struct ub_entity *ue, u16 port_id) +{ + struct ubase_dev *udev = dev_get_drvdata(&ue->dev); + + ubase_info(udev, "port %u reset prepare.\n", port_id); + ubase_port_down(udev); +} + +static void ubase_port_reset_done(struct ub_entity *ue, u16 port_id) +{ + struct ubase_dev *udev = dev_get_drvdata(&ue->dev); + + ubase_port_up(udev); + ubase_info(udev, "port %u reset done.\n", port_id); + udev->reset_stat.port_reset_cnt++; +} + +static struct ub_share_port_ops ubase_share_port_ops = { + .reset_prepare = ubase_port_reset_prepare, + .reset_done = ubase_port_reset_done +}; + +static int ubase_ubus_reg_share_port(struct ubase_dev *udev) +{ + struct ub_entity *ue = container_of(udev->dev, struct ub_entity, dev); + struct ubase_caps *caps = &udev->caps.dev_caps; + int ret; + + if (!ubase_dev_ubl_supported(udev)) + return 0; + + ret = ub_register_share_port(ue, caps->ub_port_logic_id, + &ubase_share_port_ops); + if (ret) + ubase_err(udev, + "failed to register share logical port %u, ret = %d.\n", + caps->ub_port_logic_id, ret); + + return ret; +} + +static void ubase_ubus_unreg_share_port(struct ubase_dev *udev) +{ + struct ub_entity *ue = container_of(udev->dev, struct ub_entity, dev); + struct ubase_caps *caps = &udev->caps.dev_caps; + + if (!ubase_dev_ubl_supported(udev)) + return; + + ub_unregister_share_port(ue, caps->ub_port_logic_id, + &ubase_share_port_ops); +} + +/* ubase_ubus_probe - Device initialization routine + * @ue: ub entity information struct + * @utbl_entry: entry in ubase_ubus_tbl + * + * ubase_ubus_probe initializes an UE identified by an ub_entity structure. + * + * Returns 0 on success, negative on failure + */ +static int ubase_ubus_probe(struct ub_entity *ue, + const struct ub_device_id *utbl_entry) +{ + struct ubase_dev *udev; + int ret; + + ub_set_user_info(ue); + + udev = devm_kzalloc(&ue->dev, sizeof(*udev), GFP_KERNEL); + if (!udev) { + dev_err(&ue->dev, "failed to alloc ubase dev.\n"); + return -ENOMEM; + } + + udev->dev = &ue->dev; + udev->dev_id = ubase_adev_idx_alloc(); + if (udev->dev_id < 0) { + ubase_err(udev, + "failed to alloc dev id(%d).\n", udev->dev_id); + devm_kfree(&ue->dev, udev); + return udev->dev_id; + } + + udev->caps.dev_caps.tid = ue->tid; + udev->caps.dev_caps.eid = ue->eid; + udev->caps.dev_caps.upi = ue->upi; + udev->caps.dev_caps.ctl_no = ue->ubc->ctl_no; + + dev_set_drvdata(&ue->dev, udev); + + ret = ubase_ubus_init(ue); + if (ret) { + ubase_err(udev, + "failed to init ubus, ret = %d.\n", ret); + goto err_ubus_init; + } + + ret = ubase_dev_init(udev); + if (ret) { + ubase_err(udev, + "failed to init ubase dev, ret = %d.\n", ret); + goto err_udev_init; + } + + ret = ubase_ubus_reg_share_port(udev); + if (ret) + goto err_register_share_port; + + return 0; + +err_register_share_port: + ubase_dev_uninit(udev); +err_udev_init: + ubase_ubus_uninit(ue); +err_ubus_init: + dev_set_drvdata(&ue->dev, NULL); + ubase_adev_idx_free(udev->dev_id); + devm_kfree(&ue->dev, udev); + ub_unset_user_info(ue); + + return ret; +} + +static void __ubase_ubus_remove(struct ub_entity *ue) +{ + struct ubase_dev *udev = dev_get_drvdata(&ue->dev); + + ubase_ubus_unreg_share_port(udev); + ubase_dev_uninit(udev); + ubase_ubus_uninit(ue); + ub_unset_user_info(ue); + ubase_adev_idx_free(udev->dev_id); + dev_set_drvdata(&ue->dev, NULL); + devm_kfree(&ue->dev, udev); +} + +/* ubase_remove - Device removal routine + * @ue: ub entity information struct + */ +static void ubase_ubus_remove(struct ub_entity *ue) +{ + struct ubase_dev *udev = dev_get_drvdata(&ue->dev); + + while (test_and_set_bit(UBASE_STATE_DISABLED_B, &udev->state_bits)) + msleep(UBASE_RST_WAIT_TIME); + + ub_disable_entities(ue); + __ubase_ubus_remove(ue); +} + +static void ubase_ubus_shutdown(struct ub_entity *ue) +{ + struct ubase_dev *udev = dev_get_drvdata(&ue->dev); + + while (test_and_set_bit(UBASE_STATE_DISABLED_B, &udev->state_bits)) + msleep(UBASE_RST_WAIT_TIME); + + ubase_dbg(udev, "ubase_shutdown start.\n"); + + __ubase_ubus_remove(ue); +} + +int ubase_ubus_irq_vectors_alloc(struct device *dev) +{ + struct ub_entity *ue = container_of(dev, struct ub_entity, dev); + struct ubase_dev *udev = dev_get_drvdata(&ue->dev); + int irqs_num; + + udev->irq_table.irqs_num = udev->caps.dev_caps.num_aeq_vectors + + udev->caps.dev_caps.num_ceq_vectors + + udev->caps.dev_caps.num_misc_vectors; + + irqs_num = ub_alloc_irq_vectors(ue, UBASE_MIN_IRQ_NUM, + udev->irq_table.irqs_num); + if (irqs_num == -ENOSPC) { + ubase_err(udev, + "bus is unable to provide sufficient number of interrupts.\n"); + goto out; + } + + if (irqs_num < 0) { + ubase_err(udev, + "failed to allocate USI vectors, irqs_num = %d.\n", + irqs_num); + goto out; + } + + if ((u32)irqs_num < udev->irq_table.irqs_num) { + ubase_warn(udev, + "need to allocate %u USI resource, but only allocated %d.\n", + udev->irq_table.irqs_num, irqs_num); + udev->irq_table.irqs_num = (u32)irqs_num; + } + + return 0; + +out: + ub_disable_intr(ue); + return -EFAULT; +} + +void ubase_ubus_irq_vectors_free(struct device *dev) +{ + struct ub_entity *ue = container_of(dev, struct ub_entity, dev); + + ub_disable_intr(ue); +} + +int ubase_ubus_irq_vector(struct device *dev, u32 idx) +{ + struct ub_entity *ue = container_of(dev, struct ub_entity, dev); + + return ub_irq_vector(ue, idx); +} + +static struct ub_driver ubase_ubus_driver = { + .name = ubase_ubus_driver_name, + .id_table = ubase_ubus_tbl, + .probe = ubase_ubus_probe, + .remove = ubase_ubus_remove, + .shutdown = ubase_ubus_shutdown, + .driver = {}, +}; + +int ubase_ubus_register_driver(void) +{ + return ub_register_driver(&ubase_ubus_driver); +} + +void ubase_ubus_unregister_driver(void) +{ + ub_unregister_driver(&ubase_ubus_driver); +} + +int ubase_ubus_reset_entry(struct device *dev) +{ + struct ub_entity *ue = container_of(dev, struct ub_entity, dev); + struct ubase_dev *udev = dev_get_drvdata(dev); + int ret; + + ret = ub_reset_entity(ue); + if (ret) + ubase_err(udev, "failed to trigger reset, ret = %d.\n", ret); + + return ret; +} + +void ubase_ubus_reinit(struct device *dev) +{ + struct ub_entity *ue = container_of(dev, struct ub_entity, dev); + + ub_set_user_info(ue); + ub_entity_enable(ue, 1); +} diff --git a/drivers/ub/ubase/ubase_ubus.h b/drivers/ub/ubase/ubase_ubus.h new file mode 100644 index 000000000000..3e85fe7bfaea --- /dev/null +++ b/drivers/ub/ubase/ubase_ubus.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UBASE_UBUS_H__ +#define __UBASE_UBUS_H__ + +#include + +#define UBASE_UBUS_IO_RESOURCE 2 +#define UBASE_UBUS_MEM_RESOURCE 1 +#define UBASE_UBUS_RESOURCE_0 0 + +#define UBASE_VENDOR_ID 0xCC08 + +#define UBASE_DEV_ID_K_0_URMA_MUE 0xA001 +#define UBASE_DEV_ID_K_0_URMA_UE 0xA002 +#define UBASE_DEV_ID_K_0_CDMA_MUE 0xA003 +#define UBASE_DEV_ID_K_0_CDMA_UE 0xA004 +#define UBASE_DEV_ID_K_0_PMU_MUE 0xA005 +#define UBASE_DEV_ID_K_0_PMU_UE 0xA006 + +#define UBASE_DEV_ID_A_0_URMA_MUE 0xD802 +#define UBASE_DEV_ID_A_0_URMA_UE 0xD803 +#define UBASE_DEV_ID_A_0_CDMA_MUE 0xD804 +#define UBASE_DEV_ID_A_0_CDMA_UE 0xD805 +#define UBASE_DEV_ID_A_0_PMU_MUE 0xD806 +#define UBASE_DEV_ID_A_0_PMU_UE 0xD807 +#define UBASE_DEV_ID_A_0_UBOE_MUE 0xD80B +#define UBASE_DEV_ID_A_0_UBOE_UE 0xD80C + +struct ubase_bus_eid; + +int ubase_ubus_register_driver(void); +void ubase_ubus_unregister_driver(void); + +int ubase_ubus_irq_vectors_alloc(struct device *dev); +void ubase_ubus_irq_vectors_free(struct device *dev); +int ubase_ubus_irq_vector(struct device *dev, u32 idx); + +int ubase_ubus_reset_entry(struct device *dev); +void ubase_ubus_reinit(struct device *dev); + +#endif diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h new file mode 100644 index 000000000000..e7089e59d1bf --- /dev/null +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef _UB_UBASE_COMM_CMD_H_ +#define _UB_UBASE_COMM_CMD_H_ + +#include +#include + +struct ubase_crq_event_nb { + u16 opcode; + void *back; + int (*crq_handler)(void *dev, void *data, u32 len); +}; + +int ubase_register_crq_event(struct auxiliary_device *aux_dev, + struct ubase_crq_event_nb *nb); +void ubase_unregister_crq_event(struct auxiliary_device *aux_dev, u16 opcode); + +#endif diff --git a/include/ub/ubase/ubase_comm_debugfs.h b/include/ub/ubase/ubase_comm_debugfs.h new file mode 100644 index 000000000000..c3d9c473d646 --- /dev/null +++ b/include/ub/ubase/ubase_comm_debugfs.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef _UB_UBASE_COMM_DEBUGFS_H_ +#define _UB_UBASE_COMM_DEBUGFS_H_ + +#include + +struct ubase_dbgfs; + +struct ubase_dbg_dentry_info { + const char *name; + struct dentry *dentry; + u32 property; + bool (*support)(struct device *dev, u32 property); +}; + +struct ubase_dbg_cmd_info { + const char *name; + int dentry_index; + u32 property; + bool (*support)(struct device *dev, u32 property); + int (*init)(struct device *dev, struct ubase_dbg_dentry_info *dirs, + struct ubase_dbgfs *dbgfs, u32 idx); + int (*read_func)(struct seq_file *s, void *data); +}; + +struct ubase_dbgfs { + struct dentry *dentry; /* dbgfs root path */ + struct ubase_dbg_cmd_info *cmd_info; + int cmd_info_size; +}; + +int ubase_dbg_create_dentry(struct device *dev, struct ubase_dbgfs *dbgfs, + struct ubase_dbg_dentry_info *dirs, u32 root_idx); + +#endif diff --git a/include/ub/ubase/ubase_comm_dev.h b/include/ub/ubase/ubase_comm_dev.h new file mode 100644 index 000000000000..e1d6a3d32d6c --- /dev/null +++ b/include/ub/ubase/ubase_comm_dev.h @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef _UB_UBASE_COMM_DEV_H_ +#define _UB_UBASE_COMM_DEV_H_ + +#include +#include +#include + +struct iova_slot; + +#define UBASE_ADEV_NAME "ubase" + +#define UBASE_MAX_DSCP (64) +#define UBASE_MAX_SL_NUM (16U) +#define UBASE_MAX_VL_NUM (16U) +#if UBASE_MAX_VL_NUM < IEEE_8021QAZ_MAX_TCS +#error "UBASE_MAX_VL_NUM can't less than IEEE_8021QAZ_MAX_TCS" +#endif + +#define UBASE_SUP_UBL BIT(0) +#define UBASE_SUP_ETH BIT(1) +#define UBASE_SUP_UNIC BIT(2) +#define UBASE_SUP_UDMA BIT(3) +#define UBASE_SUP_CDMA BIT(4) +#define UBASE_SUP_PMU BIT(5) +#define UBASE_SUP_URMA (UBASE_SUP_UNIC | UBASE_SUP_UDMA) +#define UBASE_SUP_UBL_ETH (UBASE_SUP_UBL | UBASE_SUP_ETH) +#define UBASE_SUP_ALL (UBASE_SUP_UNIC | UBASE_SUP_UDMA | \ + UBASE_SUP_CDMA | UBASE_SUP_PMU) +#define UBASE_SUP_NO_PMU (UBASE_SUP_ALL ^ UBASE_SUP_PMU) + +enum ubase_reset_type { + UBASE_NO_RESET, + UBASE_ELR_RESET, + UBASE_UE_RESET, + UBASE_MAX_RESET, +}; + +enum ubase_reset_stage { + UBASE_RESET_STAGE_NONE, + UBASE_RESET_STAGE_DOWN, + UBASE_RESET_STAGE_UNINIT, + UBASE_RESET_STAGE_INIT, + UBASE_RESET_STAGE_UP, +}; + +struct ubase_caps { + u16 num_ceq_vectors; + u16 num_aeq_vectors; + u16 num_misc_vectors; + + u32 aeqe_depth; + u32 ceqe_depth; + u16 aeqe_size; + u16 ceqe_size; + + u32 total_ue_num; + u32 public_jetty_cnt; + u8 vl_num; + u16 rsvd_jetty_cnt; + + u8 req_vl[UBASE_MAX_VL_NUM]; + u8 resp_vl[UBASE_MAX_VL_NUM]; + + u8 packet_pattern_mode; + u8 ack_queue_num; + u8 oor_en; + u8 reorder_queue_en; + u32 on_flight_size; + u8 reorder_cap; + u8 reorder_queue_shift; + u8 at_times; + u8 ue_num; + u16 mac_stats_num; + + u32 logic_port_bitmap; + u16 ub_port_logic_id; + u16 io_port_logic_id; + u16 io_port_id; + u16 nl_port_id; + u16 chip_id; + u16 die_id; + u16 ue_id; + u16 nl_id; + + u32 tid; + u32 eid; + u16 upi; + u32 ctl_no; + + u32 fw_version; +}; + +struct ubase_res_caps { + u32 max_cnt; + u32 start_idx; + u32 reserved_cnt; + u32 depth; +}; + +struct ubase_pmem_caps { + u64 dma_len; + dma_addr_t dma_addr; +}; + +struct ubase_adev_caps { + struct ubase_res_caps jfs; + struct ubase_res_caps jfr; + struct ubase_res_caps jfc; + struct ubase_res_caps tp; + struct ubase_res_caps tpg; + struct ubase_pmem_caps pmem; + u32 utp_port_bitmap; /* utp port bitmap */ + u32 jtg_max_cnt; + u32 rc_max_cnt; + u32 rc_que_depth; + u32 ccc_max_cnt; + u32 dest_addr_max_cnt; + u32 seid_upi_max_cnt; + u32 tpm_max_cnt; + u16 cqe_size; +}; + +struct ubase_ctx_buf_cap { + dma_addr_t dma_ctx_buf_ba; /* pass to hw */ + struct iova_slot *slot; + u32 entry_size; + u32 entry_cnt; + u32 cnt_per_page_shift; + struct xarray ctx_xa; + struct mutex ctx_mutex; +}; + +struct ubase_ctx_buf { + struct ubase_ctx_buf_cap jfs; + struct ubase_ctx_buf_cap jfr; + struct ubase_ctx_buf_cap jfc; + struct ubase_ctx_buf_cap jtg; + struct ubase_ctx_buf_cap rc; + + struct ubase_ctx_buf_cap tp; + struct ubase_ctx_buf_cap tpg; +}; + +struct net_device; +struct ubase_adev_com { + struct auxiliary_device *adev; + struct net_device *netdev; +}; + +struct ubase_resource_space { + resource_size_t addr_unmapped; + void __iomem *addr; +}; + +struct ubase_adev_qos { + /* udma/cdma resource */ + u8 sl_num; + u8 sl[UBASE_MAX_SL_NUM]; + u8 tp_sl_num; + u8 tp_sl[UBASE_MAX_SL_NUM]; + u8 ctp_sl_num; + u8 ctp_sl[UBASE_MAX_SL_NUM]; + + u8 vl_num; + u8 vl[UBASE_MAX_VL_NUM]; + u8 tp_vl_num; + u8 tp_resp_vl_offset; + u8 tp_req_vl[UBASE_MAX_VL_NUM]; + u8 ctp_vl_num; + u8 ctp_resp_vl_offset; + u8 ctp_req_vl[UBASE_MAX_VL_NUM]; + + u8 dscp_vl[UBASE_MAX_DSCP]; + + /* unic resource */ + u8 nic_sl_num; + u8 nic_sl[UBASE_MAX_SL_NUM]; + + u8 nic_vl_num; + u8 nic_vl[UBASE_MAX_VL_NUM]; + + /* common resource */ + u8 ue_max_vl_id; + u8 ue_sl_vl[UBASE_MAX_SL_NUM]; +}; + +struct ubase_ue_caps { + u8 ceq_vector_num; + u8 aeq_vector_num; + u32 aeqe_depth; + u32 ceqe_depth; + u32 jfs_max_cnt; + u32 jfs_depth; + u32 jfr_max_cnt; + u32 jfr_depth; + u32 jfc_max_cnt; + u32 jfc_depth; + u32 rc_max_cnt; + u32 rc_depth; + u32 jtg_max_cnt; +}; + +#define UBASE_BUS_EID_LEN 4 +struct ubase_bus_eid { + u32 eid[UBASE_BUS_EID_LEN]; +}; + +bool ubase_adev_ubl_supported(struct auxiliary_device *adev); +bool ubase_adev_eth_mac_supported(struct auxiliary_device *adev); + +struct ubase_resource_space *ubase_get_io_base(struct auxiliary_device *adev); +struct ubase_resource_space *ubase_get_mem_base(struct auxiliary_device *adev); +struct ubase_caps *ubase_get_dev_caps(struct auxiliary_device *adev); +struct ubase_adev_caps *ubase_get_unic_caps(struct auxiliary_device *adev); +struct ubase_adev_caps *ubase_get_udma_caps(struct auxiliary_device *adev); +struct ubase_adev_caps *ubase_get_cdma_caps(struct auxiliary_device *adev); + +#endif diff --git a/include/ub/ubase/ubase_comm_eq.h b/include/ub/ubase/ubase_comm_eq.h new file mode 100644 index 000000000000..2dabcc0537ed --- /dev/null +++ b/include/ub/ubase/ubase_comm_eq.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef _UB_UBASE_COMM_EQ_H_ +#define _UB_UBASE_COMM_EQ_H_ + +#include +#include + +enum ubase_drv_type { + UBASE_DRV_UNIC, + UBASE_DRV_UDMA, + UBASE_DRV_CDMA, + UBASE_DRV_FWCTL, + UBASE_DRV_PMU, + UBASE_DRV_UVB, + UBASE_DRV_MAX, +}; + +enum ubase_event_type { + UBASE_EVENT_TYPE_RESERVED = 0x00, + UBASE_EVENT_TYPE_JETTY_LEVEL_ERROR = 0x01, + UBASE_EVENT_TYPE_TP_LEVEL_ERROR = 0x02, + UBASE_EVENT_TYPE_ENTITY_LEVEL_ERROR = 0x03, + UBASE_EVENT_TYPE_TP_FLUSH_DONE = 0x10, + UBASE_EVENT_TYPE_ENTITY_FLUSH_DONE = 0x11, + UBASE_EVENT_TYPE_JFR_LIMIT_REACHED = 0x12, + UBASE_EVENT_TYPE_MB = 0x13, + UBASE_EVENT_TYPE_CHECK_TOKEN = 0x14, + UBASE_EVENT_TYPE_MAX +}; + +struct ubase_event_nb { + enum ubase_drv_type drv_type; + u8 event_type; + struct notifier_block nb; + void *back; +}; + +int ubase_event_register(struct auxiliary_device *adev, + struct ubase_event_nb *cb); +void ubase_event_unregister(struct auxiliary_device *adev, + struct ubase_event_nb *cb); + +int ubase_comp_register(struct auxiliary_device *adev, + int (*comp_handler)(struct notifier_block *nb, + unsigned long jfcn, void *data)); +void ubase_comp_unregister(struct auxiliary_device *adev); + +#endif diff --git a/include/ub/ubase/ubase_comm_hw.h b/include/ub/ubase/ubase_comm_hw.h new file mode 100644 index 000000000000..e212b4a7d2bc --- /dev/null +++ b/include/ub/ubase/ubase_comm_hw.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef _UB_UBASE_COMM_HW_H_ +#define _UB_UBASE_COMM_HW_H_ + +#include +#include + +#define UBASE_DESC_DATA_LEN 6 +struct ubase_cmdq_desc { + __le16 opcode; + u8 flag; + u8 bd_num; + __le16 ret; + __le16 rsv; + __le32 data[UBASE_DESC_DATA_LEN]; +}; + +struct ubase_cmdq_ring { + u32 ci; + u32 pi; + u32 desc_num; + u32 tx_timeout; + dma_addr_t desc_dma_addr; + struct ubase_cmdq_desc *desc; + spinlock_t lock; +}; + +struct ubase_cmdq { + struct ubase_cmdq_ring csq; + struct ubase_cmdq_ring crq; +}; + +struct ubase_hw { + struct ubase_resource_space rs0_base; + struct ubase_resource_space io_base; + struct ubase_resource_space mem_base; + struct ubase_cmdq cmdq; + unsigned long state; +}; + +struct ubase_mbx_event_context { + struct completion done; + int result; + u64 out_param; + u16 seq_num; +}; + +#endif diff --git a/include/ub/ubase/ubase_comm_stats.h b/include/ub/ubase/ubase_comm_stats.h new file mode 100644 index 000000000000..80d32bc9273b --- /dev/null +++ b/include/ub/ubase/ubase_comm_stats.h @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef _UB_UBASE_COMM_STATS_H_ +#define _UB_UBASE_COMM_STATS_H_ + +#include + +struct ubase_eth_mac_stats { + u64 tx_fragment_pkts; + u64 tx_undersize_pkts; + u64 tx_undermin_pkts; + + u64 tx_64_octets_pkts; + u64 tx_65_127_octets_pkts; + u64 tx_128_255_octets_pkts; + u64 tx_256_511_octets_pkts; + u64 tx_512_1023_octets_pkts; + u64 tx_1024_1518_octets_pkts; + u64 tx_1519_2047_octets_pkts; + u64 tx_2048_4095_octets_pkts; + u64 tx_4096_8191_octets_pkts; + u64 tx_8192_9216_octets_pkts; + u64 tx_9217_12287_octets_pkts; + u64 tx_12288_16383_octets_pkts; + u64 tx_1519_max_octets_bad_pkts; + u64 tx_1519_max_octets_good_pkts; + u64 tx_oversize_pkts; + u64 tx_jabber_pkts; + u64 tx_bad_pkts; + u64 tx_bad_octets; + u64 tx_good_pkts; + u64 tx_good_octets; + u64 tx_total_pkts; + u64 tx_total_octets; + u64 tx_unicast_pkts; + u64 tx_multicast_pkts; + u64 tx_broadcast_pkts; + + u64 tx_pause_pkts; + u64 tx_pfc_pkts; + u64 tx_pri0_pfc_pkts; + u64 tx_pri1_pfc_pkts; + u64 tx_pri2_pfc_pkts; + u64 tx_pri3_pfc_pkts; + u64 tx_pri4_pfc_pkts; + u64 tx_pri5_pfc_pkts; + u64 tx_pri6_pfc_pkts; + u64 tx_pri7_pfc_pkts; + + u64 tx_mac_ctrl_pkts; + u64 tx_unfilter_pkts; + u64 tx_1588_pkts; + u64 tx_err_all_pkts; + u64 tx_from_app_good_pkts; + u64 tx_from_app_bad_pkts; + + u64 rx_fragment_pkts; + u64 rx_undersize_pkts; + u64 rx_undermin_pkts; + + u64 rx_64_octets_pkts; + u64 rx_65_127_octets_pkts; + u64 rx_128_255_octets_pkts; + u64 rx_256_511_octets_pkts; + u64 rx_512_1023_octets_pkts; + u64 rx_1024_1518_octets_pkts; + u64 rx_1519_2047_octets_pkts; + u64 rx_2048_4095_octets_pkts; + u64 rx_4096_8191_octets_pkts; + u64 rx_8192_9216_octets_pkts; + u64 rx_9217_12287_octets_pkts; + u64 rx_12288_16383_octets_pkts; + u64 rx_1519_max_octets_bad_pkts; + u64 rx_1519_max_octets_good_pkts; + + u64 rx_oversize_pkts; + u64 rx_jabber_pkts; + u64 rx_bad_pkts; + u64 rx_bad_octets; + u64 rx_good_pkts; + u64 rx_good_octets; + u64 rx_total_pkts; + u64 rx_total_octets; + u64 rx_unicast_pkts; + u64 rx_multicast_pkts; + u64 rx_broadcast_pkts; + + u64 rx_pause_pkts; + u64 rx_pfc_pkts; + u64 rx_pri0_pfc_pkts; + u64 rx_pri1_pfc_pkts; + u64 rx_pri2_pfc_pkts; + u64 rx_pri3_pfc_pkts; + u64 rx_pri4_pfc_pkts; + u64 rx_pri5_pfc_pkts; + u64 rx_pri6_pfc_pkts; + u64 rx_pri7_pfc_pkts; + + u64 rx_mac_ctrl_pkts; + u64 rx_symbol_err_pkts; + u64 rx_fcs_err_pkts; + u64 rx_send_app_good_pkts; + u64 rx_send_app_bad_pkts; + u64 rx_unfilter_pkts; + + u64 tx_merge_frame_ass_error_pkts; + u64 tx_merge_frame_ass_ok_pkts; + u64 tx_merge_frame_frag_count; + u64 rx_merge_frame_ass_error_pkts; + u64 rx_merge_frame_ass_ok_pkts; + u64 rx_merge_frame_frag_count; + u64 rx_merge_frame_smd_error_pkts; +}; + +#endif /* _UBASE_COMM_STATS_H */ -- Gitee From af046e76a7693eef8f6932cf4a8ee73ca34db4c9 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Fri, 12 Sep 2025 16:07:48 +0800 Subject: [PATCH 146/214] ub: ubase: support for command process ANBZ: #45460 commit 8d68017f37faefe5f545dbfd994e1b5141ec06e1 openEuler. This patch support for command proccess by command queue and provides interfaces to other modules, such as udma and unic etc. The command queue is used by the driver to interact with the imp. Signed-off-by: Xiaobo Zhang Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/Makefile | 2 +- drivers/ub/ubase/ubase.h | 22 + drivers/ub/ubase/ubase_cmd.c | 858 ++++++++++++++++++++++++++++++ drivers/ub/ubase/ubase_cmd.h | 80 +++ drivers/ub/ubase/ubase_dev.c | 5 +- drivers/ub/ubase/ubase_dev.h | 7 +- drivers/ub/ubase/ubase_mailbox.c | 542 +++++++++++++++++++ drivers/ub/ubase/ubase_mailbox.h | 52 ++ include/ub/ubase/ubase_comm_cmd.h | 85 +++ include/ub/ubase/ubase_comm_dev.h | 1 + include/ub/ubase/ubase_comm_eq.h | 31 ++ include/ub/ubase/ubase_comm_mbx.h | 94 ++++ 12 files changed, 1775 insertions(+), 4 deletions(-) create mode 100644 drivers/ub/ubase/ubase_mailbox.c create mode 100644 drivers/ub/ubase/ubase_mailbox.h create mode 100644 include/ub/ubase/ubase_comm_mbx.h diff --git a/drivers/ub/ubase/Makefile b/drivers/ub/ubase/Makefile index 17069b9f624e..10adf2df0859 100644 --- a/drivers/ub/ubase/Makefile +++ b/drivers/ub/ubase/Makefile @@ -10,7 +10,7 @@ ccflags-y += -I$(srctree)/drivers/ub/ubase/debugfs MODULE_NAME := ubase UBASE_OBJS := ubase_main.o ubase_dev.o ubase_cmd.o \ - debugfs/ubase_debugfs.o ubase_eq.o ubase_ubus.o + debugfs/ubase_debugfs.o ubase_eq.o ubase_mailbox.o ubase_ubus.o $(MODULE_NAME)-objs := $(UBASE_OBJS) obj-$(CONFIG_UB_UBASE) := ubase.o diff --git a/drivers/ub/ubase/ubase.h b/drivers/ub/ubase/ubase.h index 1f1a157d8a98..dd684141890c 100644 --- a/drivers/ub/ubase/ubase.h +++ b/drivers/ub/ubase/ubase.h @@ -13,6 +13,11 @@ #define UBASE_CAP_LEN 3 #define UBASE_MAX_TCG_NUM (4) +enum ubase_service_state { + UBASE_STATE_CRQ_SERVICE_SCHED, + UBASE_STATE_CRQ_HANDLING, +}; + struct ubase_delay_work { struct delayed_work service_task; unsigned long state; @@ -42,4 +47,21 @@ enum { #define ubase_get_cap_bit(udev, nr) \ test_bit(nr, (unsigned long *)((udev)->cap_bits)) +static inline void ubase_write_reg(void __iomem *base, u32 reg, u32 value) +{ + writel(value, base + reg); +} + +static inline u32 ubase_read_reg(u8 __iomem *base, u32 reg) +{ + u8 __iomem *reg_addr = READ_ONCE(base); + + return readl(reg_addr + reg); +} + +#define ubase_write_dev(a, reg, value) \ + ubase_write_reg((a)->io_base.addr, reg, value) +#define ubase_read_dev(a, reg) \ + ubase_read_reg((a)->io_base.addr, reg) + #endif diff --git a/drivers/ub/ubase/ubase_cmd.c b/drivers/ub/ubase/ubase_cmd.c index ab47d8ae9021..81891811dfa3 100644 --- a/drivers/ub/ubase/ubase_cmd.c +++ b/drivers/ub/ubase/ubase_cmd.c @@ -8,6 +8,864 @@ #include "ubase_cmd.h" +static int ubase_alloc_cmd_queue(struct ubase_dev *udev, + struct ubase_cmdq_ring *ring) +{ + size_t size = ring->desc_num * sizeof(struct ubase_cmdq_desc); + + ring->desc = dma_alloc_coherent(udev->dev, size, &ring->desc_dma_addr, + GFP_KERNEL); + if (!ring->desc) { + ubase_err(udev, "failed to alloc cmdq dma addr.\n"); + return -ENOMEM; + } + + return 0; +} + +static void ubase_free_cmd_queue(struct ubase_dev *udev, + struct ubase_cmdq_ring *ring) +{ + size_t size = ring->desc_num * sizeof(struct ubase_cmdq_desc); + + if (!ring->desc) + return; + + dma_free_coherent(udev->dev, size, ring->desc, ring->desc_dma_addr); + ring->desc = NULL; +} + +static int ubase_cmd_queue_init(struct ubase_dev *udev) +{ + struct ubase_cmdq_ring *csq = &udev->hw.cmdq.csq; + struct ubase_cmdq_ring *crq = &udev->hw.cmdq.crq; + int ret; + + spin_lock_init(&csq->lock); + spin_lock_init(&crq->lock); + + csq->pi = 0; + csq->ci = 0; + crq->pi = 0; + crq->ci = 0; + csq->desc_num = UBASE_CMDQ_DESC_NUM; + crq->desc_num = UBASE_CMDQ_DESC_NUM; + csq->tx_timeout = UBASE_CMDQ_TX_TIMEOUT; + + ret = ubase_alloc_cmd_queue(udev, csq); + if (ret) { + ubase_err(udev, "failed to alloc csq, ret = %d.\n", ret); + return ret; + } + + ret = ubase_alloc_cmd_queue(udev, crq); + if (ret) { + ubase_err(udev, "failed to alloc crq, ret = %d.\n", ret); + goto err_csq; + } + + return 0; + +err_csq: + ubase_free_cmd_queue(udev, csq); + return ret; +} + +static void ubase_cmd_queue_uninit(struct ubase_dev *udev) +{ + struct ubase_cmdq_ring *csq = &udev->hw.cmdq.csq; + struct ubase_cmdq_ring *crq = &udev->hw.cmdq.crq; + + ubase_free_cmd_queue(udev, csq); + ubase_free_cmd_queue(udev, crq); +} + +static void ubase_cmd_init_regs(struct ubase_dev *udev) +{ + struct ubase_cmdq_ring *csq = &udev->hw.cmdq.csq; + struct ubase_cmdq_ring *crq = &udev->hw.cmdq.crq; + u32 reg_val; + + spin_lock_bh(&csq->lock); + spin_lock(&crq->lock); + + /* csq init */ + ubase_write_dev(&udev->hw, UBASE_CSQ_BASEADDR_L_REG, + lower_32_bits(csq->desc_dma_addr)); + ubase_write_dev(&udev->hw, UBASE_CSQ_BASEADDR_H_REG, + upper_32_bits(csq->desc_dma_addr)); + reg_val = csq->desc_num >> UBASE_CMDQ_DESC_NUM_S; + ubase_write_dev(&udev->hw, UBASE_CSQ_DEPTH_REG, reg_val); + ubase_write_dev(&udev->hw, UBASE_CSQ_HEAD_REG, 0); + ubase_write_dev(&udev->hw, UBASE_CSQ_TAIL_REG, 0); + + /* crq init */ + ubase_write_dev(&udev->hw, UBASE_CRQ_BASEADDR_L_REG, + lower_32_bits(crq->desc_dma_addr)); + ubase_write_dev(&udev->hw, UBASE_CRQ_BASEADDR_H_REG, + upper_32_bits(crq->desc_dma_addr)); + reg_val = crq->desc_num >> UBASE_CMDQ_DESC_NUM_S; + ubase_write_dev(&udev->hw, UBASE_CRQ_DEPTH_REG, reg_val); + ubase_write_dev(&udev->hw, UBASE_CRQ_HEAD_REG, 0); + ubase_write_dev(&udev->hw, UBASE_CRQ_TAIL_REG, 0); + + spin_unlock(&crq->lock); + spin_unlock_bh(&csq->lock); +} + +static void ubase_cmd_uninit_regs(struct ubase_dev *udev) +{ + struct ubase_cmdq_ring *csq = &udev->hw.cmdq.csq; + struct ubase_cmdq_ring *crq = &udev->hw.cmdq.crq; + + spin_lock_bh(&csq->lock); + spin_lock(&crq->lock); + + ubase_write_dev(&udev->hw, UBASE_CSQ_BASEADDR_L_REG, 0); + ubase_write_dev(&udev->hw, UBASE_CSQ_BASEADDR_H_REG, 0); + ubase_write_dev(&udev->hw, UBASE_CSQ_DEPTH_REG, 0); + ubase_write_dev(&udev->hw, UBASE_CSQ_HEAD_REG, 0); + ubase_write_dev(&udev->hw, UBASE_CSQ_TAIL_REG, 0); + + ubase_write_dev(&udev->hw, UBASE_CRQ_BASEADDR_L_REG, 0); + ubase_write_dev(&udev->hw, UBASE_CRQ_BASEADDR_H_REG, 0); + ubase_write_dev(&udev->hw, UBASE_CRQ_DEPTH_REG, 0); + ubase_write_dev(&udev->hw, UBASE_CRQ_HEAD_REG, 0); + ubase_write_dev(&udev->hw, UBASE_CRQ_TAIL_REG, 0); + + spin_unlock(&crq->lock); + spin_unlock_bh(&csq->lock); +} + +static void ubase_write_desc_to_cmdq(struct ubase_dev *udev, + struct ubase_cmdq_desc *desc, int num) +{ + struct ubase_cmdq_ring *csq = &udev->hw.cmdq.csq; + struct ubase_cmdq_desc *desc_to_use; + int cnt = 0; + + while (cnt < num) { + desc_to_use = &csq->desc[csq->pi]; + *desc_to_use = desc[cnt]; + (csq->pi)++; + if (csq->pi >= csq->desc_num) + csq->pi = 0; + cnt++; + } + + ubase_write_dev(&udev->hw, UBASE_CSQ_TAIL_REG, csq->pi); +} + +static int ubase_remain_cmdq_space(struct ubase_cmdq_ring *csq) +{ + u32 used = (csq->pi - csq->ci + csq->desc_num) % csq->desc_num; + + return csq->desc_num - used - 1; +} + +static bool ubase_wait_for_resp(struct ubase_dev *udev) +{ + struct ubase_cmdq_ring *csq = &udev->hw.cmdq.csq; + u32 timeout = 0; + u32 ci; + + do { + ci = ubase_read_dev(&udev->hw, UBASE_CSQ_HEAD_REG); + if (ci == csq->pi) + return true; + udelay(1); + timeout++; + } while (timeout < csq->tx_timeout); + + return false; +} + +static int ubase_get_cmd_result(struct ubase_dev *udev, + struct ubase_cmdq_desc *desc, + int num, u32 sw_pi) +{ + struct ubase_cmdq_ring *csq = &udev->hw.cmdq.csq; + u32 pi = sw_pi; + int handle; + u16 ret; + + for (handle = 0; handle < num; handle++) { + desc[handle] = csq->desc[pi]; + pi++; + if (pi >= csq->desc_num) + pi = 0; + } + + if (desc->flag & UBASE_CMD_FLAG_OUT) + ret = le16_to_cpu(desc->ret); + else + ret = ETIMEDOUT; + + return -ret; +} + +static int ubase_csq_data_is_valid(struct ubase_dev *udev, u32 hw_ci) +{ + struct ubase_cmdq_ring *csq = &udev->hw.cmdq.csq; + u32 sw_ci = csq->ci; + u32 sw_pi = csq->pi; + + if (sw_pi > sw_ci) + return hw_ci >= sw_ci && hw_ci <= sw_pi; + + return hw_ci >= sw_ci || hw_ci <= sw_pi; +} + +static int ubase_csq_clean(struct ubase_dev *udev) +{ + struct ubase_cmdq_ring *csq = &udev->hw.cmdq.csq; + s64 hw_ci; + int clean; + + hw_ci = ubase_read_dev(&udev->hw, UBASE_CSQ_HEAD_REG); + /* Make sure head is ready before touch any data */ + rmb(); + if (!ubase_csq_data_is_valid(udev, hw_ci)) { + ubase_warn(udev, + "the cmd head is incorrect! cmd head = (%lld, %u-%u).\n", + hw_ci, csq->pi, csq->ci); + ubase_warn(udev, + "any further commands to the firmware are disabled!\n"); + set_bit(UBASE_STATE_CMD_DISABLE, &udev->hw.state); + ubase_warn(udev, + "the firmware watchdog is expected to reset soon!\n"); + return -EIO; + } + + clean = (int)((hw_ci - (s64)(csq->ci) + (s64)(csq->desc_num)) % + (s64)(csq->desc_num)); + csq->ci = hw_ci; + + return clean; +} + +int ubase_send_cmd(struct ubase_dev *udev, + struct ubase_cmdq_desc *desc, int num) +{ + struct ubase_cmdq_ring *csq = &udev->hw.cmdq.csq; + bool is_completed = false; + int cleaned; + u32 sw_pi; + int ret; + + spin_lock_bh(&csq->lock); + if (test_bit(UBASE_STATE_CMD_DISABLE, &udev->hw.state)) { + ret = -EBUSY; + goto err_unlock; + } + + if (num > ubase_remain_cmdq_space(csq)) { + csq->ci = ubase_read_dev(&udev->hw, UBASE_CSQ_HEAD_REG); + ubase_warn(udev, + "the requested space exceeds the remaining space.\n"); + ret = -EBUSY; + goto err_unlock; + } + + /** + * Record the location of desc in the ring for this time + * which will be use for hardware to write back + */ + sw_pi = csq->pi; + + ubase_write_desc_to_cmdq(udev, desc, num); + is_completed = ubase_wait_for_resp(udev); + if (!is_completed) { + ret = -EBADE; + goto err_clr_cmdq; + } + ret = ubase_get_cmd_result(udev, desc, num, sw_pi); + +err_clr_cmdq: + cleaned = ubase_csq_clean(udev); + if (cleaned < 0) + ret = cleaned; + else if (cleaned != num) + ubase_warn(udev, + "cleaned %dBD, need to clean %dBD.\n", cleaned, num); +err_unlock: + spin_unlock_bh(&csq->lock); + + return ret; +} + +static int ubase_cmd_query_version(struct ubase_dev *udev, u32 *fw_version) +{ + struct ubase_query_version_cmd *resp; + struct ubase_cmdq_desc desc; + int ret; + + ubase_cmd_setup_basic_desc(&desc, UBASE_OPC_QUERY_FW_VER, true, 1); + ret = ubase_send_cmd(udev, &desc, 1); + if (ret) { + ubase_err(udev, "failed to query fw version, ret = %d.\n", + ret); + return ret; + } + + resp = (struct ubase_query_version_cmd *)desc.data; + *fw_version = le32_to_cpu(resp->firmware); + + ubase_info(udev, "The firmware version is %u.%u.%u.%u\n", + u32_get_bits(*fw_version, UBASE_FW_VERSION_BYTE3_MASK), + u32_get_bits(*fw_version, UBASE_FW_VERSION_BYTE2_MASK), + u32_get_bits(*fw_version, UBASE_FW_VERSION_BYTE1_MASK), + u32_get_bits(*fw_version, UBASE_FW_VERSION_BYTE0_MASK)); + + return 0; +} + +static inline void ubase_crq_table_init(struct ubase_dev *udev) +{ + struct ubase_crq_table *crq_table = &udev->crq_table; + + mutex_init(&crq_table->lock); + INIT_LIST_HEAD(&crq_table->nbs.list); +} + +static inline void ubase_crq_table_uninit(struct ubase_dev *udev) +{ + struct ubase_crq_table *crq_table = &udev->crq_table; + + mutex_destroy(&crq_table->lock); +} + +int ubase_cmd_init(struct ubase_dev *udev) +{ + int ret; + + if (!test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + ubase_crq_table_init(udev); + + ret = ubase_cmd_queue_init(udev); + if (ret) { + ubase_err(udev, "failed to init ubase cmd queue.\n"); + goto err_queue_init; + } + + ubase_cmd_init_regs(udev); + + clear_bit(UBASE_STATE_CMD_DISABLE, &udev->hw.state); + + ret = ubase_cmd_query_version(udev, &udev->caps.dev_caps.fw_version); + if (ret) + goto err_query_version; + + return 0; + +err_query_version: + set_bit(UBASE_STATE_CMD_DISABLE, &udev->hw.state); + ubase_cmd_uninit_regs(udev); + ubase_cmd_queue_uninit(udev); +err_queue_init: + if (!test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + ubase_crq_table_uninit(udev); + + return ret; +} + +void ubase_cmd_disable(struct ubase_dev *udev) +{ + __ubase_cmd_disable(udev); + /* wait to ensure the firmware completes csq commands. */ + msleep(UBASE_CMDQ_CLEAR_WAIT_TIME); + + ubase_cmd_uninit_regs(udev); +} + +void ubase_cmd_uninit(struct ubase_dev *udev) +{ + if (udev->reset_stage != UBASE_RESET_STAGE_UNINIT) { + ubase_cmd_disable(udev); + /* wait to ensure the firmware completes crq commands. */ + msleep(UBASE_CMDQ_CLEAR_WAIT_TIME); + } + + ubase_cmd_queue_uninit(udev); + + if (!test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + ubase_crq_table_uninit(udev); +} + +void ubase_cmd_setup_basic_desc(struct ubase_cmdq_desc *desc, + enum ubase_opcode_type opcode, bool is_read, + u8 num) +{ + memset(desc, 0, sizeof(*desc)); + desc->opcode = cpu_to_le16(opcode); + desc->flag = UBASE_CMD_FLAG_NO_INTR | UBASE_CMD_FLAG_IN; + desc->bd_num = num; + + if (is_read) + desc->flag |= cpu_to_le16(UBASE_CMD_FLAG_WR); +} + +static u16 ubase_calc_bd_num(struct ubase_cmd_buf *buf) +{ + u32 data_size = buf->data_size > UBASE_CMD_DATA_LENGTH ? + buf->data_size - UBASE_CMD_DATA_LENGTH : 0; + + return DIV_ROUND_UP(data_size, sizeof(struct ubase_cmdq_desc)) + 1; +} + +static int ubase_cmd_buf_check(struct ubase_dev *udev, struct ubase_cmd_buf *in, + struct ubase_cmd_buf *out, u16 *num) +{ + if (in->is_read && (!out || !out->data || out->data_size == 0)) { + ubase_err(udev, "output buffer is empty.\n"); + return -EINVAL; + } + + *num = max_t(u16, ubase_calc_bd_num(in), ubase_calc_bd_num(out)); + if (*num > UBASE_CMDQ_DESC_NUM) { + ubase_err(udev, + "requested space(%u) exceeds the maximum(%lu).\n", + max_t(u32, in->data_size, out->data_size), + UBASE_CMD_MAX_DESC_SIZE); + return -EINVAL; + } + + return 0; +} + +static void ubase_cmd_setup_desc_by_inbuf(struct ubase_dev *udev, + struct ubase_cmd_buf *in, + struct ubase_cmdq_desc *desc, + u16 num) +{ + ubase_cmd_setup_basic_desc(&desc[0], in->opcode, in->is_read, num); + if (in->data) + memcpy(desc->data, in->data, in->data_size); +} + +static void ubase_cmd_setup_desc_by_outbuf(struct ubase_dev *udev, + struct ubase_cmd_buf *out, + struct ubase_cmdq_desc *desc) +{ + if (!out || out->data_size == 0) + return; + + out->opcode = desc[0].opcode; + out->is_read = (desc[0].flag & UBASE_CMD_FLAG_WR) ? true : false; + memcpy(out->data, desc->data, out->data_size); +} + +static int ubase_cmd_send_inout_real(struct ubase_dev *udev, + struct ubase_cmd_buf *in, + struct ubase_cmd_buf *out) +{ + struct ubase_cmdq_desc *desc; + u16 num; + int ret; + + ret = ubase_cmd_buf_check(udev, in, out, &num); + if (ret) + return ret; + + desc = kcalloc(num, sizeof(struct ubase_cmdq_desc), GFP_ATOMIC); + if (!desc) { + ubase_err(udev, "failed to alloc desc, size = %lu.\n", + num * sizeof(struct ubase_cmdq_desc)); + return -ENOMEM; + } + + ubase_cmd_setup_desc_by_inbuf(udev, in, desc, num); + + ret = ubase_send_cmd(udev, desc, num); + if (ret) + goto err_send_cmd; + + ubase_cmd_setup_desc_by_outbuf(udev, out, desc); + +err_send_cmd: + kfree(desc); + + return ret; +} + +static void ubase_free_bd_data(void *msg_data, u32 bd_num) +{ + if (!msg_data || bd_num <= 1) + return; + + kfree(msg_data); +} + +static void ubase_cmd_exec_callback(struct ubase_dev *udev, u16 opcode, + void *msg_data, u32 msg_data_len) +{ + struct ubase_crq_table *crq_table = &udev->crq_table; + struct ubase_crq_event_nbs *nbs; + + if (!msg_data) + return; + + mutex_lock(&crq_table->lock); + list_for_each_entry(nbs, &crq_table->nbs.list, list) { + if (nbs->nb.opcode == opcode) { + nbs->nb.crq_handler(nbs->nb.back, msg_data, + msg_data_len); + break; + } + } + mutex_unlock(&crq_table->lock); +} + +static void ubase_gen_multi_bd_data(struct ubase_dev *udev, u32 bd_num, + void **msg_data, u32 msg_data_len) +{ + struct ubase_cmdq_ring *crq = &udev->hw.cmdq.crq; + struct ubase_cmdq_desc *desc; + u32 pos = 0; + u32 i; + + *msg_data = kzalloc(msg_data_len, GFP_KERNEL); + if (!(*msg_data)) { + ubase_err(udev, "failed to alloc crq msg data."); + return; + } + + for (i = 0; i < bd_num; i++) { + desc = &crq->desc[crq->ci]; + if (i == 0) { + memcpy(*msg_data + pos, + desc->data, UBASE_CMD_DATA_LENGTH); + pos += UBASE_CMD_DATA_LENGTH; + } else { + memcpy(*msg_data + pos, desc, sizeof(*desc)); + pos += sizeof(*desc); + } + + UBASE_MOVE_CRQ_RING_PTR(crq); + } +} + +static void ubase_gen_single_bd_data(struct ubase_dev *udev, void **msg_data) +{ + struct ubase_cmdq_ring *crq = &udev->hw.cmdq.crq; + struct ubase_cmdq_desc *desc; + + desc = &crq->desc[crq->ci]; + *msg_data = crq->desc[crq->ci].data; + UBASE_MOVE_CRQ_RING_PTR(crq); +} + +static void ubase_gen_bd_data(struct ubase_dev *udev, u32 bd_num, + void **msg_data, u32 msg_data_len) +{ + if (bd_num == 1) { + ubase_gen_single_bd_data(udev, msg_data); + return; + } + + ubase_gen_multi_bd_data(udev, bd_num, msg_data, msg_data_len); +} + +static bool ubase_cmd_crq_empty(struct ubase_dev *udev, struct ubase_hw *hw) +{ + hw->cmdq.crq.pi = ubase_read_dev(hw, UBASE_CRQ_TAIL_REG); + + return hw->cmdq.crq.pi == hw->cmdq.crq.ci; +} + +void ubase_cmd_crq_handler(struct ubase_dev *udev) +{ + struct ubase_cmdq_ring *crq = &udev->hw.cmdq.crq; + u32 msg_data_len; + void *msg_data; + u16 opcode; + u8 bd_num; + u8 flag; + + while (!ubase_cmd_crq_empty(udev, &udev->hw)) { + if (test_bit(UBASE_STATE_CMD_DISABLE, &udev->hw.state)) { + ubase_warn(udev, + "command queue needs re-initializing.\n"); + return; + } + + opcode = crq->desc[crq->ci].opcode; + bd_num = crq->desc[crq->ci].bd_num; + flag = crq->desc[crq->ci].flag; + msg_data_len = bd_num * sizeof(struct ubase_cmdq_desc) - + UBASE_CMD_HEADER_LENGTH; + + if (unlikely(!bd_num || !(flag & UBASE_CMD_FLAG_OUT))) { + ubase_err(udev, + "drop invalid crq message, opcode = 0x%x, bd_num = %u, flag = 0x%x.", + opcode, bd_num, flag); + UBASE_MOVE_CRQ_RING_PTR(crq); + continue; + } + + ubase_gen_bd_data(udev, bd_num, &msg_data, msg_data_len); + + ubase_cmd_exec_callback(udev, opcode, msg_data, + msg_data_len); + + ubase_free_bd_data(msg_data, bd_num); + } + + ubase_write_dev(&udev->hw, UBASE_CRQ_HEAD_REG, crq->ci); +} + +void ubase_crq_service_task(struct ubase_delay_work *ubase_work) +{ + struct ubase_dev *udev = container_of(ubase_work, struct ubase_dev, + service_task); + struct ubase_crq_table *crq_table = &udev->crq_table; + + if (!test_and_clear_bit(UBASE_STATE_CRQ_SERVICE_SCHED, + &udev->service_task.state) || + test_and_set_bit(UBASE_STATE_CRQ_HANDLING, + &udev->service_task.state)) + return; + + if (time_is_before_eq_jiffies(crq_table->last_crq_scheduled + + UBASE_CRQ_SCHED_TIMEOUT)) + ubase_warn(udev, + "crq service task is scheduled after %ums on cpu%d!\n", + jiffies_to_msecs(jiffies - crq_table->last_crq_scheduled), + smp_processor_id()); + + ubase_cmd_crq_handler(udev); + + clear_bit(UBASE_STATE_CRQ_HANDLING, &udev->service_task.state); +} + +static bool ubase_cmd_is_mbx_avail(struct ubase_dev *udev) +{ + return true; +} + +int ubase_cmd_mbx_event_cb(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct ubase_event_nb *ev_nb = container_of(nb, struct ubase_event_nb, nb); + struct ubase_aeq_notify_info *info = data; + struct ubase_aeqe *aeqe = info->aeqe; + struct ubase_dev *udev = ev_nb->back; + struct ubase_mbx_event_context *ctx; + + ctx = &udev->mb_cmd.ctx; + if (aeqe->event.cmd.seq_num != ctx->seq_num) { + ubase_err(udev, + "mbx seq num is different, cmd seq_num = %u, ctx seq_num = %u.\n", + aeqe->event.cmd.seq_num, ctx->seq_num); + return NOTIFY_DONE; + } + + ctx->result = aeqe->event.cmd.status == 0 ? 0 : -EIO; + ctx->out_param = aeqe->event.cmd.out_param; + + complete(&ctx->done); + + return NOTIFY_OK; +} + +static int ubase_cmd_wait_mbx_completed(struct ubase_dev *udev, + union ubase_mbox *mbx) +{ + struct ubase_mbx_event_context *ctx = &udev->mb_cmd.ctx; + int ret; + + if (!wait_for_completion_timeout(&ctx->done, + msecs_to_jiffies(UBASE_CMDQ_MBX_TX_TIMEOUT))) { + ubase_err(udev, + "cmd seq_num 0x%x mailbox cmd code 0x%x timeout.\n", + ctx->seq_num, mbx->cmd); + return -EBUSY; + } + + ret = ctx->result; + if (ret) + ubase_err(udev, + "cmd seq_num(0x%x) mailbox cmd code(0x%x) error, ret = %d.\n", + ctx->seq_num, mbx->cmd, ret); + + return ret; +} + +static void ubase_setup_mbx_info(struct ubase_dev *udev, union ubase_mbox *mbx) +{ + mbx->seq_num = ++udev->mb_cmd.ctx.seq_num; + mbx->event_en = 1; +} + +int ubase_post_mailbox_by_event(struct ubase_dev *udev, + struct ubase_cmd_buf *in, + struct ubase_cmd_buf *out) +{ + union ubase_mbox *mbx = (union ubase_mbox *)in->data; + unsigned long end; + int ret; + + if (!mbx) { + ubase_err(udev, "input mailbox data field is empty.\n"); + return -EINVAL; + } + + ubase_setup_mbx_info(udev, mbx); + + end = msecs_to_jiffies(UBASE_CMDQ_MBX_TX_TIMEOUT) + jiffies; + while (ubase_cmd_is_mbx_avail(udev)) { + ret = __ubase_cmd_send_inout(udev, in, out); + if (!ret) + break; + + if (time_after(jiffies, end)) { + dev_err_ratelimited(udev->dev, + "failed to wait mbox.\n"); + return -ETIMEDOUT; + } + + cond_resched(); + } + + return ubase_cmd_wait_mbx_completed(udev, mbx); +} + +int __ubase_cmd_send_inout(struct ubase_dev *udev, struct ubase_cmd_buf *in, + struct ubase_cmd_buf *out) +{ + if (!in) { + ubase_err(udev, "input buffer is empty.\n"); + return -EINVAL; + } + + if (udev->reset_stage == UBASE_RESET_STAGE_UNINIT) + return -EAGAIN; + + return ubase_cmd_send_inout_real(udev, in, out); +} + +int __ubase_cmd_send_in(struct ubase_dev *udev, struct ubase_cmd_buf *in) +{ + struct ubase_cmd_buf out; + + out.data_size = 0; + + return __ubase_cmd_send_inout(udev, in, &out); +} + +/** + * When uninstalling, cmdq needs to be successfully sended as much as possible, + * but the cmd may be disabled during reset, this interface attempts to send cmd + * when it is enabled. + */ +int ubase_cmd_send_inout_ex(struct auxiliary_device *aux_dev, + struct ubase_cmd_buf *in, struct ubase_cmd_buf *out, + u32 time_out) +{ + struct ubase_dev *udev; + u32 try_cnt = 0; + + if (!aux_dev || !in || !out) + return -EINVAL; + + udev = ubase_get_udev_by_adev(aux_dev); + if (!time_out) + return __ubase_cmd_send_inout(udev, in, out); + + while (test_bit(UBASE_STATE_CMD_DISABLE, &udev->hw.state) && + (try_cnt * UBASE_CMDQ_WAIT_TIME) < time_out) { + msleep(UBASE_CMDQ_WAIT_TIME); + try_cnt++; + } + + if ((try_cnt * UBASE_CMDQ_WAIT_TIME) >= time_out) { + ubase_warn(udev, + "cmd send timeout, due to cmd enter disable state for %ums.\n", + try_cnt * UBASE_CMDQ_WAIT_TIME); + return -EBUSY; + } + + return __ubase_cmd_send_inout(udev, in, out); +} +EXPORT_SYMBOL(ubase_cmd_send_inout_ex); + +int ubase_cmd_send_inout(struct auxiliary_device *aux_dev, + struct ubase_cmd_buf *in, + struct ubase_cmd_buf *out) +{ + if (!aux_dev || !in || !out) + return -EINVAL; + + return __ubase_cmd_send_inout(__ubase_get_udev_by_adev(aux_dev), in, out); +} +EXPORT_SYMBOL(ubase_cmd_send_inout); + +int ubase_cmd_send_in_ex(struct auxiliary_device *aux_dev, + struct ubase_cmd_buf *in, u32 time_out) +{ + struct ubase_cmd_buf out; + + if (!aux_dev || !in) + return -EINVAL; + + out.data_size = 0; + + return ubase_cmd_send_inout_ex(aux_dev, in, &out, time_out); +} +EXPORT_SYMBOL(ubase_cmd_send_in_ex); + +int ubase_cmd_send_in(struct auxiliary_device *aux_dev, + struct ubase_cmd_buf *in) +{ + if (!aux_dev || !in) + return -EINVAL; + + return __ubase_cmd_send_in(__ubase_get_udev_by_adev(aux_dev), in); +} +EXPORT_SYMBOL(ubase_cmd_send_in); + +static int __ubase_cmd_get_data_size(struct ubase_dev *udev, u16 opcode, + u16 *data_size) +{ + struct ubase_cmdq_desc desc; + u8 bd_num; + int ret; + + ubase_cmd_setup_basic_desc(&desc, opcode, true, 1); + desc.flag |= UBASE_CMD_FLAG_GET_BD_NUM; + + ret = ubase_send_cmd(udev, &desc, 1); + if (ret) { + ubase_err(udev, + "failed to send cmd in get cmd data size, opcode = 0x%x, ret = %d.\n", + opcode, ret); + return ret; + } + + bd_num = *(u8 *)desc.data; + if (unlikely(!bd_num)) { + ubase_err(udev, + "failed to get cmd data size, bd_num = %u, opcode = 0x%x.\n", + bd_num, opcode); + return -EIO; + } + + *data_size = bd_num * sizeof(desc) - UBASE_CMD_HEADER_LENGTH; + + return 0; +} + +int ubase_cmd_get_data_size(struct auxiliary_device *aux_dev, u16 opcode, + u16 *data_size) +{ + if (!aux_dev || !data_size) + return -EINVAL; + + return __ubase_cmd_get_data_size(__ubase_get_udev_by_adev(aux_dev), + opcode, data_size); +} +EXPORT_SYMBOL(ubase_cmd_get_data_size); + int __ubase_register_crq_event(struct ubase_dev *udev, struct ubase_crq_event_nb *nb) { diff --git a/drivers/ub/ubase/ubase_cmd.h b/drivers/ub/ubase/ubase_cmd.h index 0f1d92ebe511..70c3f4d00719 100644 --- a/drivers/ub/ubase/ubase_cmd.h +++ b/drivers/ub/ubase/ubase_cmd.h @@ -11,8 +11,88 @@ #include "ubase_dev.h" +#define UBASE_CMDQ_DESC_NUM_S 3 +#define UBASE_CMDQ_DESC_NUM 1024 +#define UBASE_CMDQ_TX_TIMEOUT 3000000 +#define UBASE_CMDQ_MBX_TX_TIMEOUT 30000 +#define UBASE_CMDQ_CLEAR_WAIT_TIME 200 +#define UBASE_CMDQ_WAIT_TIME 10 + +#define UBASE_CMD_FLAG_IN BIT(0) +#define UBASE_CMD_FLAG_OUT BIT(1) +#define UBASE_CMD_FLAG_NEXT BIT(2) +#define UBASE_CMD_FLAG_WR BIT(3) +#define UBASE_CMD_FLAG_NO_INTR BIT(4) +#define UBASE_CMD_FLAG_ERR_INTR BIT(5) +#define UBASE_CMD_FLAG_GET_BD_NUM BIT(6) + +#define UBASE_UE2UE_MSG_WAIT_TIME 3000 + +#define UBASE_CRQ_SCHED_TIMEOUT (HZ / 2) + +#define UBASE_CMD_HEADER_LENGTH 8 +#define UBASE_CMD_DATA_LENGTH (UBASE_DESC_DATA_LEN * sizeof(__le32)) +#define UBASE_CMD_MAX_DESC_SIZE \ + (UBASE_CMDQ_DESC_NUM * sizeof(struct ubase_cmdq_desc)) + +#define UBASE_MOVE_CRQ_RING_PTR(crq) \ + ((crq)->ci = ((crq)->ci + 1) % (crq)->desc_num) + +enum ubase_cmd_state { + UBASE_STATE_CMD_DISABLE +}; + +struct ubase_query_version_cmd { + __le32 firmware; + __le32 hardware; + __le32 rsv; + __le32 caps[UBASE_CAP_LEN]; +}; + +static inline void __ubase_fill_inout_buf(struct ubase_cmd_buf *buf, u16 opcode, + bool is_read, u32 data_size, void *data) +{ + buf->opcode = opcode; + buf->is_read = is_read; + buf->data_size = data_size; + buf->data = data; +} + +int ubase_cmd_init(struct ubase_dev *udev); +void ubase_cmd_uninit(struct ubase_dev *udev); + +static inline void __ubase_cmd_enable(struct ubase_dev *udev) +{ + clear_bit(UBASE_STATE_CMD_DISABLE, &udev->hw.state); +} + +static inline void __ubase_cmd_disable(struct ubase_dev *udev) +{ + set_bit(UBASE_STATE_CMD_DISABLE, &udev->hw.state); +} + +void ubase_cmd_disable(struct ubase_dev *udev); + +void ubase_cmd_setup_basic_desc(struct ubase_cmdq_desc *desc, + enum ubase_opcode_type opcode, bool is_read, + u8 num); +int ubase_send_cmd(struct ubase_dev *udev, + struct ubase_cmdq_desc *desc, int num); + +int ubase_post_mailbox_by_event(struct ubase_dev *udev, + struct ubase_cmd_buf *in, + struct ubase_cmd_buf *out); +int __ubase_cmd_send_in(struct ubase_dev *udev, struct ubase_cmd_buf *in); +int __ubase_cmd_send_inout(struct ubase_dev *udev, struct ubase_cmd_buf *in, + struct ubase_cmd_buf *out); + +int ubase_cmd_mbx_event_cb(struct notifier_block *nb, unsigned long action, + void *data); + int __ubase_register_crq_event(struct ubase_dev *udev, struct ubase_crq_event_nb *nb); void __ubase_unregister_crq_event(struct ubase_dev *udev, u16 opcode); +void ubase_crq_service_task(struct ubase_delay_work *ubase_work); + #endif diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index be052e9cad61..8c102000e7ef 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -9,6 +9,7 @@ #include "debugfs/ubase_debugfs.h" #include "ubase_cmd.h" +#include "ubase_mailbox.h" #include "ubase_dev.h" #define UBASE_PERIOD_100MS 100 @@ -435,7 +436,7 @@ static const struct ubase_init_function ubase_init_func_map[] = { }, { "init cmd queue", UBASE_SUP_ALL, 1, - NULL, NULL + ubase_cmd_init, ubase_cmd_uninit }, { "query dev res", UBASE_SUP_ALL, 0, @@ -443,7 +444,7 @@ static const struct ubase_init_function ubase_init_func_map[] = { }, { "init mailbox", UBASE_SUP_NO_PMU, 0, - NULL, NULL + ubase_mbox_cmd_init, ubase_mbox_cmd_uninit }, { "query chip info", UBASE_SUP_ALL, 0, diff --git a/drivers/ub/ubase/ubase_dev.h b/drivers/ub/ubase/ubase_dev.h index fe587046b291..ee59ac41c8be 100644 --- a/drivers/ub/ubase/ubase_dev.h +++ b/drivers/ub/ubase/ubase_dev.h @@ -83,6 +83,12 @@ struct ubase_dma_buf { size_t size; }; +struct ubase_ctx_page { + dma_addr_t iova; + u32 npage; + refcount_t refcount; +}; + struct ubase_ta_layer_ctx { struct ubase_dma_buf extdb_buf; struct ubase_dma_buf timer_buf; @@ -242,7 +248,6 @@ static inline struct ubase_dev *__ubase_get_udev_by_adev(struct auxiliary_device *adev) { struct ubase_adev *uadev = container_of(adev, struct ubase_adev, adev); - return uadev->udev; } diff --git a/drivers/ub/ubase/ubase_mailbox.c b/drivers/ub/ubase/ubase_mailbox.c new file mode 100644 index 000000000000..9951929a7f8d --- /dev/null +++ b/drivers/ub/ubase/ubase_mailbox.c @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include +#include +#include + +#include "ubase_cmd.h" +#include "ubase_mailbox.h" + +int ubase_mbox_cmd_init(struct ubase_dev *udev) +{ + struct ubase_mbx_event_context *ctx = &udev->mb_cmd.ctx; + + udev->mb_cmd.pool = dma_pool_create("ubase_mbox", udev->dev, + UBASE_MAILBOX_SIZE, + UBASE_MAILBOX_SIZE, 0); + if (!udev->mb_cmd.pool) + return -ENOMEM; + + sema_init(&udev->mb_cmd.sem, 1); + init_completion(&ctx->done); + + return 0; +} + +void ubase_mbox_cmd_uninit(struct ubase_dev *udev) +{ + if (!udev->mb_cmd.pool) + return; + + dma_pool_destroy(udev->mb_cmd.pool); + udev->mb_cmd.pool = NULL; +} + +struct ubase_cmd_mailbox *__ubase_alloc_cmd_mailbox(struct ubase_dev *udev) +{ + struct ubase_cmd_mailbox *mailbox; + + if (!udev->mb_cmd.pool) { + ubase_err(udev, "failed to alloc mailbox, pool is null.\n"); + return NULL; + } + + mailbox = kzalloc(sizeof(*mailbox), GFP_KERNEL); + if (!mailbox) { + ubase_err(udev, "failed to alloc mailbox.\n"); + goto failed_alloc_mailbox; + } + + mailbox->buf = dma_pool_zalloc(udev->mb_cmd.pool, GFP_KERNEL, + &mailbox->dma); + if (!mailbox->buf) { + ubase_err(udev, "failed to alloc buffer of mailbox.\n"); + goto failed_alloc_mailbox_buf; + } + + return mailbox; + +failed_alloc_mailbox_buf: + kfree(mailbox); +failed_alloc_mailbox: + return NULL; +} + +struct ubase_cmd_mailbox *ubase_alloc_cmd_mailbox(struct auxiliary_device *aux_dev) +{ + struct ubase_dev *udev; + + if (!aux_dev) + return NULL; + + udev = __ubase_get_udev_by_adev(aux_dev); + + return __ubase_alloc_cmd_mailbox(udev); +} +EXPORT_SYMBOL(ubase_alloc_cmd_mailbox); + +void __ubase_free_cmd_mailbox(struct ubase_dev *udev, + struct ubase_cmd_mailbox *mailbox) +{ + if (!mailbox) { + ubase_err(udev, "Invalid mailbox.\n"); + return; + } + + dma_pool_free(udev->mb_cmd.pool, mailbox->buf, mailbox->dma); + kfree(mailbox); +} + +void ubase_free_cmd_mailbox(struct auxiliary_device *aux_dev, + struct ubase_cmd_mailbox *mailbox) +{ + struct ubase_dev *udev; + + if (!aux_dev) + return; + + udev = __ubase_get_udev_by_adev(aux_dev); + + __ubase_free_cmd_mailbox(udev, mailbox); +} +EXPORT_SYMBOL(ubase_free_cmd_mailbox); + +static int ubase_post_mailbox(struct ubase_dev *udev, + struct ubase_cmdq_desc *desc, + u32 timeout, u8 *complete_status, bool is_read) +{ + union ubase_mbox *mb; + unsigned long end; + int ret; + + end = msecs_to_jiffies(timeout) + jiffies; + mb = (union ubase_mbox *)desc->data; + + while (1) { + desc->flag = cpu_to_le16(UBASE_CMD_FLAG_NO_INTR | UBASE_CMD_FLAG_IN); + if (is_read) + desc->flag |= cpu_to_le16(UBASE_CMD_FLAG_WR); + + ret = ubase_send_cmd(udev, desc, 1); + if (!ret && !(is_read ? mb->query_hw_run : mb->hw_run)) + break; + + if (time_after(jiffies, end)) { + dev_err_ratelimited(udev->dev, + "failed to wait mbox.\n"); + return -ETIMEDOUT; + } + + cond_resched(); + } + + if (!ret) + *complete_status = + (u8)(is_read ? mb->query_status : mb->status); + + return ret; +} + +static int ubase_poll_mbox_done(struct ubase_dev *udev, uint32_t timeout) +{ + struct ubase_cmdq_desc desc; + u8 status = 0; + int ret; + + ubase_cmd_setup_basic_desc(&desc, UBASE_OPC_QUERY_MB_ST, true, 1); + + ret = ubase_post_mailbox(udev, &desc, timeout, &status, true); + if (!ret) { + if (!status) { + ubase_info(udev, + "failed to query ubase mailbox, status = %u.\n", + status); + return -EBUSY; + } + } else + dev_err_ratelimited(udev->dev, + "failed to check mbox status = %u, ret = %d.\n", + status, ret); + + return ret; +} + +static void ubase_mbox_desc_init(struct ubase_dev *udev, union ubase_mbox *mb, + u64 in_param, struct ubase_mbx_attr *attr) +{ + memset(mb, 0, sizeof(*mb)); + mb->in_param_l = cpu_to_le32(lower_32_bits(in_param)); + mb->in_param_h = cpu_to_le32(upper_32_bits(in_param)); + mb->cmd = attr->op; + mb->tag = attr->tag; + mb->mbx_ue_id = attr->mbx_ue_id; +} + +static int ubase_cmd_mbox_poll(struct ubase_dev *udev, + struct ubase_mbx_attr *attr, + struct ubase_cmd_mailbox *mailbox) +{ + struct ubase_cmdq_desc desc; + union ubase_mbox *mb; + u8 status = 0; + int ret; + + if (udev->reset_stage == UBASE_RESET_STAGE_UNINIT) + return 0; + + mb = (union ubase_mbox *)desc.data; + + ubase_cmd_setup_basic_desc(&desc, UBASE_OPC_POST_MB, false, 1); + + ubase_mbox_desc_init(udev, mb, (u64)mailbox->dma, attr); + + mb->seq_num = CMD_MBX_POLL_VALUE; + + ret = ubase_post_mailbox(udev, &desc, UBASE_MBX_TX_TIMEOUT, &status, + false); + if (ret) { + ubase_err(udev, + "failed to post mailbox 0x%x in poll mode, status = %u, ret = %d.\n", + attr->op, status, ret); + return ret; + } + + return ubase_poll_mbox_done(udev, UBASE_MBX_TX_TIMEOUT); +} + +static int ubase_cmd_mbox_event(struct ubase_dev *udev, + struct ubase_mbx_attr *attr, + struct ubase_cmd_mailbox *mailbox) +{ + union ubase_mbox mb_out = {0}; + union ubase_mbox mb_in = {0}; + struct ubase_cmd_buf in, out; + int ret; + + if (udev->reset_stage == UBASE_RESET_STAGE_UNINIT) + return 0; + + ubase_mbox_desc_init(udev, &mb_in, (u64)mailbox->dma, attr); + + mb_in.event_en = 1; + __ubase_fill_inout_buf(&in, UBASE_OPC_POST_MB, false, + sizeof(union ubase_mbox), (void *)&mb_in); + __ubase_fill_inout_buf(&out, UBASE_OPC_POST_MB, false, + sizeof(union ubase_mbox), (void *)&mb_out); + + ret = ubase_post_mailbox_by_event(udev, &in, &out); + if (ret) + ubase_err(udev, + "failed to post mailbox 0x%x in event mode, ret = %d.\n", + attr->op, ret); + + return ret; +} + +int ubase_create_ctx_page(struct ubase_dev *udev, + struct ubase_ctx_buf_cap *ctx_buf, + struct ubase_ctx_page **ctx_page, u32 npage) +{ + int ret; + + *ctx_page = kzalloc(sizeof(struct ubase_ctx_page), GFP_KERNEL); + if (!(*ctx_page)) + return -ENOMEM; + + (*ctx_page)->iova = ctx_buf->dma_ctx_buf_ba + npage * PAGE_SIZE; + refcount_set(&(*ctx_page)->refcount, 1); + (*ctx_page)->npage = npage; + ret = ummu_fill_pages(ctx_buf->slot, (*ctx_page)->iova, + UBASE_IOVA_COMM_PFN_CNT); + if (ret) { + ubase_err(udev, "failed to fill pages in ummu, ret = %d\n", ret); + kfree(*ctx_page); + *ctx_page = NULL; + } + + return ret; +} + +void ubase_destroy_ctx_page(struct ubase_dev *udev, + struct ubase_ctx_page *ctx_page, + struct ubase_ctx_buf_cap *ctx_buf) +{ + int ret; + + ret = ummu_drain_pages(ctx_buf->slot, ctx_page->iova, + UBASE_IOVA_COMM_PFN_CNT); + if (ret) + ubase_err(udev, + "failed to drain pages in ummu, npage = %u, ret = %d.\n", + ctx_page->npage, ret); + + kfree(ctx_page); +} + +static int ubase_use_buf_ctx_page(struct ubase_dev *udev, + struct ubase_ctx_buf_cap *ctx_buf, u32 tag) +{ + u32 cnt_per_page_shift = ctx_buf->cnt_per_page_shift; + u32 npage = tag >> cnt_per_page_shift; + struct ubase_ctx_page *ctx_page; + int ret; + + mutex_lock(&ctx_buf->ctx_mutex); + + ctx_page = (struct ubase_ctx_page *)xa_load(&ctx_buf->ctx_xa, npage); + if (!ctx_page) { + ret = ubase_create_ctx_page(udev, ctx_buf, &ctx_page, npage); + if (ret) { + ubase_err(udev, "failed to create context page, ret = %d.\n", + ret); + goto err_create; + } + + ret = xa_err(xa_store(&ctx_buf->ctx_xa, npage, ctx_page, + GFP_KERNEL)); + if (ret) { + ubase_err(udev, "failed to store page, ret = %d.\n", + ret); + goto err_store; + } + } + + refcount_inc(&ctx_page->refcount); + mutex_unlock(&ctx_buf->ctx_mutex); + + return 0; +err_store: + ubase_destroy_ctx_page(udev, ctx_page, ctx_buf); +err_create: + mutex_unlock(&ctx_buf->ctx_mutex); + + return ret; +} + +static void ubase_free_buf_ctx_page(struct ubase_dev *udev, + struct ubase_ctx_buf_cap *ctx_buf, u32 tag) +{ + struct ubase_ctx_page *ctx_page; + u32 cnt_per_page_shift; + u32 npage; + + if (!ctx_buf) + return; + + cnt_per_page_shift = ctx_buf->cnt_per_page_shift; + npage = tag >> cnt_per_page_shift; + + mutex_lock(&ctx_buf->ctx_mutex); + + ctx_page = (struct ubase_ctx_page *)xa_load(&ctx_buf->ctx_xa, npage); + if (!ctx_page) { + ubase_err(udev, + "no find ctx page in free buf page, npage = %u.\n", + npage); + mutex_unlock(&ctx_buf->ctx_mutex); + return; + } + + refcount_dec(&ctx_page->refcount); + if (refcount_dec_if_one(&ctx_page->refcount)) { + ubase_info(udev, + "refcout of ctx page is equal to one and the ctx_page is going to erased.\n"); + xa_erase(&ctx_buf->ctx_xa, npage); + ubase_destroy_ctx_page(udev, ctx_page, ctx_buf); + } + + mutex_unlock(&ctx_buf->ctx_mutex); +} + +static struct ubase_ctx_buf_cap* +ubase_parse_common_buf(struct ubase_mbx_attr *attr, + struct mbx_op_match op_matches[], + enum ubase_mb_type *type, u32 size) +{ + u32 i; + + for (i = 0; i < size; i++) { + if (attr->op == op_matches[i].op) { + *type = op_matches[i].type; + return op_matches[i].ctx_caps; + } + } + + return NULL; +} + +static bool ubase_is_jfs_opcode(u8 op) +{ + switch (op) { + case UBASE_MB_CREATE_JFS_CONTEXT: + case UBASE_MB_MODIFY_JFS_CONTEXT: + case UBASE_MB_DESTROY_JFS_CONTEXT: + case UBASE_MB_QUERY_JFS_CONTEXT: + return true; + default: + return false; + } +} + +static struct ubase_ctx_buf_cap* +ubase_parse_ta_opcode_buf(struct ubase_dev *udev, struct ubase_mbx_attr *attr, + enum ubase_mb_type *type) +{ + struct mbx_op_match ta_matches[] = { + {UBASE_MB_CREATE_JFS_CONTEXT, UBASE_MB_CREATE, &udev->ctx_buf.jfs}, + {UBASE_MB_MODIFY_JFS_CONTEXT, UBASE_MB_MODIFY, &udev->ctx_buf.jfs}, + {UBASE_MB_DESTROY_JFS_CONTEXT, UBASE_MB_DESTROY, &udev->ctx_buf.jfs}, + {UBASE_MB_QUERY_JFS_CONTEXT, UBASE_MB_QUERY, &udev->ctx_buf.jfs}, + {UBASE_MB_CREATE_JFC_CONTEXT, UBASE_MB_CREATE, &udev->ctx_buf.jfc}, + {UBASE_MB_MODIFY_JFC_CONTEXT, UBASE_MB_MODIFY, &udev->ctx_buf.jfc}, + {UBASE_MB_DESTROY_JFC_CONTEXT, UBASE_MB_DESTROY, &udev->ctx_buf.jfc}, + {UBASE_MB_QUERY_JFC_CONTEXT, UBASE_MB_QUERY, &udev->ctx_buf.jfc}, + {UBASE_MB_CREATE_JFR_CONTEXT, UBASE_MB_CREATE, &udev->ctx_buf.jfr}, + {UBASE_MB_MODIFY_JFR_CONTEXT, UBASE_MB_MODIFY, &udev->ctx_buf.jfr}, + {UBASE_MB_DESTROY_JFR_CONTEXT, UBASE_MB_DESTROY, &udev->ctx_buf.jfr}, + {UBASE_MB_QUERY_JFR_CONTEXT, UBASE_MB_QUERY, &udev->ctx_buf.jfr}, + {UBASE_MB_CREATE_JETTY_GROUP_CONTEXT, UBASE_MB_CREATE, &udev->ctx_buf.jtg}, + {UBASE_MB_MODIFY_JETTY_GROUP_CONTEXT, UBASE_MB_MODIFY, &udev->ctx_buf.jtg}, + {UBASE_MB_DESTROY_JETTY_GROUP_CONTEXT, UBASE_MB_DESTROY, &udev->ctx_buf.jtg}, + {UBASE_MB_QUERY_JETTY_GROUP_CONTEXT, UBASE_MB_QUERY, &udev->ctx_buf.jtg}, + {UBASE_MB_CREATE_RC_CONTEXT, UBASE_MB_CREATE, &udev->ctx_buf.rc}, + {UBASE_MB_MODIFY_RC_CONTEXT, UBASE_MB_MODIFY, &udev->ctx_buf.rc}, + {UBASE_MB_DESTROY_RC_CONTEXT, UBASE_MB_DESTROY, &udev->ctx_buf.rc}, + {UBASE_MB_QUERY_RC_CONTEXT, UBASE_MB_QUERY, &udev->ctx_buf.rc}, + }; + u32 size = ARRAY_SIZE(ta_matches); + + return ubase_parse_common_buf(attr, ta_matches, type, size); +} + +static struct ubase_ctx_buf_cap* +ubase_parse_opcode_buf(struct ubase_dev *udev, struct ubase_mbx_attr *attr, + enum ubase_mb_type *type) +{ + if (ubase_is_jfs_opcode(attr->op) && + test_bit(UBASE_STATE_PREALLOC_OK_B, &udev->state_bits)) + return NULL; + + return ubase_parse_ta_opcode_buf(udev, attr, type); +} + +static int ubase_check_buf_ctx_page(struct ubase_dev *udev, + struct ubase_ctx_buf_cap *ctx_buf, u32 tag) +{ + u32 cnt_per_page_shift = ctx_buf->cnt_per_page_shift; + u32 npage = tag >> cnt_per_page_shift; + struct ubase_ctx_page *ctx_page; + + mutex_lock(&ctx_buf->ctx_mutex); + + ctx_page = (struct ubase_ctx_page *)xa_load(&ctx_buf->ctx_xa, npage); + if (!ctx_page) { + ubase_err(udev, + "failed to find ctx page in free buf page, npage = %u.\n", + npage); + mutex_unlock(&ctx_buf->ctx_mutex); + return -EINVAL; + } + + mutex_unlock(&ctx_buf->ctx_mutex); + + return 0; +} + +static int ubase_hw_upgrade_ctx_event(struct ubase_dev *udev, + struct ubase_mbx_attr *attr, + struct ubase_cmd_mailbox *mailbox) +{ + int ret; + + down(&udev->mb_cmd.sem); + ret = ubase_cmd_mbox_event(udev, attr, mailbox); + up(&udev->mb_cmd.sem); + + return ret; +} + +int ubase_hw_upgrade_ctx_poll(struct ubase_dev *udev, + struct ubase_mbx_attr *attr, + struct ubase_cmd_mailbox *mailbox) +{ + int ret; + + down(&udev->mb_cmd.sem); + ret = ubase_cmd_mbox_poll(udev, attr, mailbox); + up(&udev->mb_cmd.sem); + + return ret; +} + +int __ubase_hw_upgrade_ctx(struct ubase_dev *udev, + struct ubase_mbx_attr *attr, + struct ubase_cmd_mailbox *mailbox) +{ + return ubase_hw_upgrade_ctx_event(udev, attr, mailbox); +} + +int __ubase_hw_upgrade_ctx_ex(struct ubase_dev *udev, + struct ubase_mbx_attr *attr, + struct ubase_cmd_mailbox *mailbox) +{ + enum ubase_mb_type type = UBASE_MB_OTHER; + struct ubase_ctx_buf_cap *ctx_buf; + int ret; + + ctx_buf = ubase_parse_opcode_buf(udev, attr, &type); + if (ctx_buf) { + if (type == UBASE_MB_CREATE) { + ret = ubase_use_buf_ctx_page(udev, ctx_buf, attr->tag); + if (ret) { + ubase_err(udev, "failed to write context va, ret = %d.\n", + ret); + return ret; + } + } else if (type == UBASE_MB_QUERY) { + ret = ubase_check_buf_ctx_page(udev, ctx_buf, attr->tag); + if (ret) { + ubase_info(udev, + "A query operation is performed before the create operation, 0 is returned by default, op = 0x%x.\n", + attr->op); + return 0; + } + } else if (type == UBASE_MB_MODIFY || type == UBASE_MB_DESTROY) { + ret = ubase_check_buf_ctx_page(udev, ctx_buf, attr->tag); + if (ret) { + ubase_info(udev, + "An access operation is performed before the create operation, op = 0x%x.\n", + attr->op); + return ret; + } + } + } + + ret = __ubase_hw_upgrade_ctx(udev, attr, mailbox); + if ((ret && type == UBASE_MB_CREATE) || + (!ret && type == UBASE_MB_DESTROY)) + ubase_free_buf_ctx_page(udev, ctx_buf, attr->tag); + + return ret; +} + +int ubase_hw_upgrade_ctx_ex(struct auxiliary_device *aux_dev, + struct ubase_mbx_attr *attr, + struct ubase_cmd_mailbox *mailbox) +{ + struct ubase_dev *udev; + + if (!aux_dev || !attr || !mailbox) + return -EINVAL; + + udev = ubase_get_udev_by_adev(aux_dev); + if (!test_bit(UBASE_STATE_CTX_READY_B, &udev->state_bits)) + return -EAGAIN; + + return __ubase_hw_upgrade_ctx_ex(udev, attr, mailbox); +} +EXPORT_SYMBOL(ubase_hw_upgrade_ctx_ex); diff --git a/drivers/ub/ubase/ubase_mailbox.h b/drivers/ub/ubase/ubase_mailbox.h new file mode 100644 index 000000000000..18aa5babe5b8 --- /dev/null +++ b/drivers/ub/ubase/ubase_mailbox.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UBASE_MAILBOX_H__ +#define __UBASE_MAILBOX_H__ + +#include "ub/ubase/ubase_comm_mbx.h" +#include "ubase_dev.h" + +#define CMD_MBX_POLL_VALUE 0xffff +#define UBASE_MAILBOX_SIZE 4096 +#define UBASE_MBX_TX_TIMEOUT 30000 + +enum ubase_mb_type { + UBASE_MB_CREATE, + UBASE_MB_MODIFY, + UBASE_MB_DESTROY, + UBASE_MB_QUERY, + UBASE_MB_OTHER, +}; + +struct mbx_op_match { + u32 op; + enum ubase_mb_type type; + struct ubase_ctx_buf_cap *ctx_caps; +}; + +int ubase_mbox_cmd_init(struct ubase_dev *udev); +void ubase_mbox_cmd_uninit(struct ubase_dev *udev); +struct ubase_cmd_mailbox *__ubase_alloc_cmd_mailbox(struct ubase_dev *udev); +void __ubase_free_cmd_mailbox(struct ubase_dev *udev, + struct ubase_cmd_mailbox *mailbox); +void ubase_destroy_ctx_page(struct ubase_dev *udev, + struct ubase_ctx_page *ctx_page, + struct ubase_ctx_buf_cap *ctx_buf); +int ubase_hw_upgrade_ctx_poll(struct ubase_dev *udev, + struct ubase_mbx_attr *attr, + struct ubase_cmd_mailbox *mailbox); +int __ubase_hw_upgrade_ctx(struct ubase_dev *udev, + struct ubase_mbx_attr *attr, + struct ubase_cmd_mailbox *mailbox); +int __ubase_hw_upgrade_ctx_ex(struct ubase_dev *udev, + struct ubase_mbx_attr *attr, + struct ubase_cmd_mailbox *mailbox); +int ubase_create_ctx_page(struct ubase_dev *udev, + struct ubase_ctx_buf_cap *ctx_buf, + struct ubase_ctx_page **ctx_page, u32 npage); + +#endif diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index e7089e59d1bf..0514dff9f2b8 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -10,12 +10,97 @@ #include #include +#define UBASE_FW_VERSION_BYTE3_MASK GENMASK(31, 24) +#define UBASE_FW_VERSION_BYTE2_MASK GENMASK(23, 16) +#define UBASE_FW_VERSION_BYTE1_MASK GENMASK(15, 8) +#define UBASE_FW_VERSION_BYTE0_MASK GENMASK(7, 0) + +#define UBASE_CSQ_BASEADDR_L_REG 0x18400 +#define UBASE_CSQ_BASEADDR_H_REG 0x18404 +#define UBASE_CSQ_DEPTH_REG 0x18408 +#define UBASE_CSQ_TAIL_REG 0x18410 +#define UBASE_CSQ_HEAD_REG 0x18414 +#define UBASE_CRQ_BASEADDR_L_REG 0x18418 +#define UBASE_CRQ_BASEADDR_H_REG 0x1841c +#define UBASE_CRQ_DEPTH_REG 0x18420 +#define UBASE_CRQ_TAIL_REG 0x18424 +#define UBASE_CRQ_HEAD_REG 0x18428 + +enum ubase_opcode_type { + /* Generic commands */ + UBASE_OPC_QUERY_FW_VER = 0x0001, + + /* Mailbox commands */ + UBASE_OPC_POST_MB = 0x7000, + UBASE_OPC_QUERY_MB_ST = 0X7001, + + /* Software commands */ + UBASE_OPC_MUE_TO_UE = 0xF001, + UBASE_OPC_UE_TO_MUE = 0xF002, +}; + +union ubase_mbox { + struct { + /* MB 0 */ + __le32 in_param_l; + /* MB 1 */ + __le32 in_param_h; + /* MB 2 */ + __le32 cmd : 8; + __le32 tag : 24; + /* MB 3 */ + __le32 seq_num : 16; + __le32 event_en : 1; + __le32 mbx_ue_id : 8; + __le32 rsv : 7; + /* MB 4 */ + __le32 status : 1; + __le32 hw_run : 1; + __le32 rsv1 : 30; + }; + + struct { + __le32 query_status : 1; + __le32 query_hw_run : 1; + __le32 query_rsv : 30; + }; +}; + +struct ubase_cmd_buf { + u16 opcode; + bool is_read; + u32 data_size; + void *data; +}; + struct ubase_crq_event_nb { u16 opcode; void *back; int (*crq_handler)(void *dev, void *data, u32 len); }; +static inline void ubase_fill_inout_buf(struct ubase_cmd_buf *buf, u16 opcode, + bool is_read, u32 data_size, void *data) +{ + buf->opcode = opcode; + buf->is_read = is_read; + buf->data_size = data_size; + buf->data = data; +} + +int ubase_cmd_send_inout(struct auxiliary_device *aux_dev, + struct ubase_cmd_buf *in, struct ubase_cmd_buf *out); +int ubase_cmd_send_in(struct auxiliary_device *aux_dev, + struct ubase_cmd_buf *in); +int ubase_cmd_send_inout_ex(struct auxiliary_device *aux_dev, + struct ubase_cmd_buf *in, struct ubase_cmd_buf *out, + u32 time_out); +int ubase_cmd_send_in_ex(struct auxiliary_device *aux_dev, + struct ubase_cmd_buf *in, u32 time_out); + +int ubase_cmd_get_data_size(struct auxiliary_device *aux_dev, u16 opcode, + u16 *data_size); + int ubase_register_crq_event(struct auxiliary_device *aux_dev, struct ubase_crq_event_nb *nb); void ubase_unregister_crq_event(struct auxiliary_device *aux_dev, u16 opcode); diff --git a/include/ub/ubase/ubase_comm_dev.h b/include/ub/ubase/ubase_comm_dev.h index e1d6a3d32d6c..c07100c59a9f 100644 --- a/include/ub/ubase/ubase_comm_dev.h +++ b/include/ub/ubase/ubase_comm_dev.h @@ -15,6 +15,7 @@ struct iova_slot; #define UBASE_ADEV_NAME "ubase" +#define UBASE_IOVA_COMM_PFN_CNT 1 #define UBASE_MAX_DSCP (64) #define UBASE_MAX_SL_NUM (16U) #define UBASE_MAX_VL_NUM (16U) diff --git a/include/ub/ubase/ubase_comm_eq.h b/include/ub/ubase/ubase_comm_eq.h index 2dabcc0537ed..8e355b710025 100644 --- a/include/ub/ubase/ubase_comm_eq.h +++ b/include/ub/ubase/ubase_comm_eq.h @@ -40,6 +40,37 @@ struct ubase_event_nb { void *back; }; +struct ubase_aeqe { + u32 event_type : 8; + u32 sub_type : 8; + u32 rsv0 : 15; + u32 owner : 1; + + union { + struct { + u32 num; + u32 rsv0; + u32 rsv1; + } queue_event; + +#pragma pack(push, 1) + struct { + u64 out_param; + u16 seq_num; + u8 status; + u8 rsv0; + } cmd; +#pragma pack(pop) + } event; + u32 rsv[12]; +}; + +struct ubase_aeq_notify_info { + u8 event_type; + u8 sub_type; + struct ubase_aeqe *aeqe; +}; + int ubase_event_register(struct auxiliary_device *adev, struct ubase_event_nb *cb); void ubase_event_unregister(struct auxiliary_device *adev, diff --git a/include/ub/ubase/ubase_comm_mbx.h b/include/ub/ubase/ubase_comm_mbx.h new file mode 100644 index 000000000000..26c625f80a77 --- /dev/null +++ b/include/ub/ubase/ubase_comm_mbx.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef _UB_UBASE_COMM_MBX_H_ +#define _UB_UBASE_COMM_MBX_H_ + +#include +#include + +struct ubase_cmd_mailbox { + void *buf; + dma_addr_t dma; +}; + +struct ubase_mbx_attr { + __le32 tag : 24; + __le32 rsv : 8; + u8 op; + u8 mbx_ue_id; +}; + +enum ubase_mbox_opcode { + /* write/destroy jfs/jetty ctx buf */ + UBASE_MB_WRITE_JFS_CONTEXT_VA = 0x0, + UBASE_MB_CREATE_JFS_CONTEXT = 0x4, + UBASE_MB_MODIFY_JFS_CONTEXT = 0x5, + UBASE_MB_QUERY_JFS_CONTEXT = 0x6, + UBASE_MB_DESTROY_JFS_CONTEXT = 0x7, + + /* write/destroy rc ctx buf */ + UBASE_MB_WRITE_RC_CONTEXT_VA = 0x10, + UBASE_MB_CREATE_RC_CONTEXT = 0x14, + UBASE_MB_MODIFY_RC_CONTEXT = 0x15, + UBASE_MB_QUERY_RC_CONTEXT = 0x16, + UBASE_MB_DESTROY_RC_CONTEXT = 0x17, + + /* write/destroy jfc ctx buf */ + UBASE_MB_WRITE_JFC_CONTEXT_VA = 0x20, + UBASE_MB_CREATE_JFC_CONTEXT = 0x24, + UBASE_MB_MODIFY_JFC_CONTEXT = 0x25, + UBASE_MB_QUERY_JFC_CONTEXT = 0x26, + UBASE_MB_DESTROY_JFC_CONTEXT = 0x27, + + /* create/destroy aeq ctx */ + UBASE_MB_CREATE_AEQ_CONTEXT = 0x34, + UBASE_MB_QUERY_AEQ_CONTEXT = 0x36, + UBASE_MB_DESTROY_AEQ_CONTEXT = 0x37, + + /* create/destroy ceq ctx */ + UBASE_MB_CREATE_CEQ_CONTEXT = 0x44, + UBASE_MB_QUERY_CEQ_CONTEXT = 0x46, + UBASE_MB_DESTROY_CEQ_CONTEXT = 0x47, + + /* write/destroy jfr ctx buf */ + UBASE_MB_WRITE_JFR_CONTEXT_VA = 0x50, + UBASE_MB_CREATE_JFR_CONTEXT = 0x54, + UBASE_MB_MODIFY_JFR_CONTEXT = 0x55, + UBASE_MB_QUERY_JFR_CONTEXT = 0x56, + UBASE_MB_DESTROY_JFR_CONTEXT = 0x57, + + /* write/destroy jetty group ctx buf */ + UBASE_MB_WRITE_JETTY_GROUP_CONTEXT_VA = 0x60, + UBASE_MB_CREATE_JETTY_GROUP_CONTEXT = 0x64, + UBASE_MB_MODIFY_JETTY_GROUP_CONTEXT = 0x65, + UBASE_MB_QUERY_JETTY_GROUP_CONTEXT = 0x66, + UBASE_MB_DESTROY_JETTY_GROUP_CONTEXT = 0x67, + + /* query tpg ctx buf */ + UBASE_MB_QUERY_TPG_CONTEXT = 0x76, + + /* query tp ctx buf */ + UBASE_MB_QUERY_TP_CONTEXT = 0x86, +}; + +struct ubase_cmd_mailbox *ubase_alloc_cmd_mailbox(struct auxiliary_device *aux_dev); +void ubase_free_cmd_mailbox(struct auxiliary_device *aux_dev, + struct ubase_cmd_mailbox *mailbox); + +int ubase_hw_upgrade_ctx_ex(struct auxiliary_device *aux_dev, + struct ubase_mbx_attr *attr, + struct ubase_cmd_mailbox *mailbox); + +static inline void ubase_fill_mbx_attr(struct ubase_mbx_attr *attr, u32 tag, + u8 op, u8 mbx_ue_id) +{ + attr->tag = tag; + attr->op = op; + attr->mbx_ue_id = mbx_ue_id; +} + +#endif -- Gitee From 23ff7b78c090e9a65737a985a3a9970bb1175e0d Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Fri, 12 Sep 2025 17:11:27 +0800 Subject: [PATCH 147/214] ub: ubase: support debugfs public interface. ANBZ: #45460 commit a152802795763d0e27808bf239f5803c048e2802 openEuler. UBASE provides a set of debugfs framework for auxiliary devices. Auxiliary drivers (such as unic and cdma) use debugfs interfaces provided by UBASE to create their own debugfs files. This ensures that the hierarchy and dependencies between debugfs are maintained. This patch adds the debugfs export interface for UBASE. Signed-off-by: Jianqiang Li Signed-off-by: Fengyan Mu Signed-off-by: Haibin Lu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/Makefile | 3 +- drivers/ub/ubase/debugfs/ubase_ctx_debugfs.c | 31 ++++++++ drivers/ub/ubase/debugfs/ubase_debugfs.c | 75 +++++++++++++++++++- drivers/ub/ubase/ubase_mailbox.h | 3 +- include/ub/ubase/ubase_comm_debugfs.h | 7 ++ 5 files changed, 114 insertions(+), 5 deletions(-) create mode 100644 drivers/ub/ubase/debugfs/ubase_ctx_debugfs.c diff --git a/drivers/ub/ubase/Makefile b/drivers/ub/ubase/Makefile index 10adf2df0859..59032bf09ddf 100644 --- a/drivers/ub/ubase/Makefile +++ b/drivers/ub/ubase/Makefile @@ -10,7 +10,8 @@ ccflags-y += -I$(srctree)/drivers/ub/ubase/debugfs MODULE_NAME := ubase UBASE_OBJS := ubase_main.o ubase_dev.o ubase_cmd.o \ - debugfs/ubase_debugfs.o ubase_eq.o ubase_mailbox.o ubase_ubus.o + debugfs/ubase_debugfs.o ubase_eq.o ubase_mailbox.o ubase_ubus.o \ + debugfs/ubase_ctx_debugfs.o $(MODULE_NAME)-objs := $(UBASE_OBJS) obj-$(CONFIG_UB_UBASE) := ubase.o diff --git a/drivers/ub/ubase/debugfs/ubase_ctx_debugfs.c b/drivers/ub/ubase/debugfs/ubase_ctx_debugfs.c new file mode 100644 index 000000000000..1004033c1581 --- /dev/null +++ b/drivers/ub/ubase/debugfs/ubase_ctx_debugfs.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include + +#include "ubase_debugfs.h" + +static void __ubase_print_context_hw(struct seq_file *s, void *ctx_addr, + u32 ctx_len) +{ + __le32 *p = (__le32 *)ctx_addr; + u32 i; + + ctx_len = ctx_len / sizeof(u32); + for (i = 0; i < ctx_len; i++, p++) { + seq_printf(s, "%lu\t", (i + 1) * sizeof(u32)); + seq_printf(s, "%08x\n", le32_to_cpu(*p)); + } +} + +void ubase_print_context_hw(struct seq_file *s, void *ctx_addr, u32 ctx_len) +{ + if (!s || !ctx_addr) + return; + + __ubase_print_context_hw(s, ctx_addr, ctx_len); +} +EXPORT_SYMBOL(ubase_print_context_hw); diff --git a/drivers/ub/ubase/debugfs/ubase_debugfs.c b/drivers/ub/ubase/debugfs/ubase_debugfs.c index 75bf029f17a5..46a794d935da 100644 --- a/drivers/ub/ubase/debugfs/ubase_debugfs.c +++ b/drivers/ub/ubase/debugfs/ubase_debugfs.c @@ -29,6 +29,44 @@ static bool __ubase_dbg_dentry_support(struct device *dev, u32 property) return false; } +bool ubase_dbg_dentry_support(struct auxiliary_device *adev, u32 property) +{ + if (!adev) + return false; + + return __ubase_dbg_dentry_support(__ubase_get_udev_by_adev(adev)->dev, + property); +} +EXPORT_SYMBOL(ubase_dbg_dentry_support); + +static int __ubase_dbg_seq_file_init(struct device *dev, + struct ubase_dbg_dentry_info *dirs, + struct ubase_dbgfs *dbgfs, u32 idx) +{ + struct ubase_dbg_cmd_info *cmd_info = &dbgfs->cmd_info[idx]; + struct dentry *cur_dir; + + cur_dir = dirs[cmd_info->dentry_index].dentry; + if (!cmd_info->read_func) + return -EFAULT; + + debugfs_create_devm_seqfile(dev, cmd_info->name, cur_dir, + cmd_info->read_func); + + return 0; +} + +int ubase_dbg_seq_file_init(struct device *dev, + struct ubase_dbg_dentry_info *dirs, + struct ubase_dbgfs *dbgfs, u32 idx) +{ + if (!dev || !dirs || !dbgfs || !dbgfs->cmd_info) + return -EINVAL; + + return __ubase_dbg_seq_file_init(dev, dirs, dbgfs, idx); +} +EXPORT_SYMBOL(ubase_dbg_seq_file_init); + static struct ubase_dbg_dentry_info ubase_dbg_dentry[] = { /* ue debugfs top-level directory, * "dev_name" refers to the ue name @@ -112,6 +150,7 @@ EXPORT_SYMBOL(ubase_dbg_create_dentry); int ubase_dbg_init(struct ubase_dev *udev) { + struct ubase_dbg_dentry_info dentry[UBASE_DBG_DENTRY_ROOT + 1] = {0}; const char *name = dev_name(udev->dev); struct device *dev = udev->dev; int ret; @@ -122,12 +161,13 @@ int ubase_dbg_init(struct ubase_dev *udev) return PTR_ERR(udev->dbgfs.dentry); } - ubase_dbg_dentry[UBASE_DBG_DENTRY_ROOT].dentry = udev->dbgfs.dentry; + memcpy(dentry, ubase_dbg_dentry, sizeof(dentry)); + dentry[UBASE_DBG_DENTRY_ROOT].dentry = udev->dbgfs.dentry; udev->dbgfs.cmd_info = ubase_dbg_cmd; udev->dbgfs.cmd_info_size = ARRAY_SIZE(ubase_dbg_cmd); - ret = ubase_dbg_create_dentry(dev, &udev->dbgfs, ubase_dbg_dentry, - ARRAY_SIZE(ubase_dbg_dentry) - 1); + ret = ubase_dbg_create_dentry(dev, &udev->dbgfs, dentry, + ARRAY_SIZE(dentry) - 1); if (ret) { ubase_err(udev, "failed to create ubase debugfs dentry, ret = %d.\n", @@ -163,3 +203,32 @@ void ubase_dbg_unregister_debugfs(void) { debugfs_remove_recursive(ubase_dbgfs_root); } + +struct dentry *ubase_diag_debugfs_root(struct auxiliary_device *adev) +{ + if (!adev) + return NULL; + + return __ubase_get_udev_by_adev(adev)->dbgfs.dentry; +} +EXPORT_SYMBOL(ubase_diag_debugfs_root); + +int ubase_dbg_format_time(time64_t time, struct seq_file *s) +{ +#define YEAR_OFFSET 1900 + const char week[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + const char mouth[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + struct tm t; + + if (!s) + return -EINVAL; + + time64_to_tm(time, 0, &t); + + seq_printf(s, "%s %s %02d %02d:%02d:%02d %ld", week[t.tm_wday], + mouth[t.tm_mon], t.tm_mday, t.tm_hour, t.tm_min, + t.tm_sec, t.tm_year + YEAR_OFFSET); + return 0; +} +EXPORT_SYMBOL(ubase_dbg_format_time); diff --git a/drivers/ub/ubase/ubase_mailbox.h b/drivers/ub/ubase/ubase_mailbox.h index 18aa5babe5b8..33e72b7d15b3 100644 --- a/drivers/ub/ubase/ubase_mailbox.h +++ b/drivers/ub/ubase/ubase_mailbox.h @@ -7,7 +7,8 @@ #ifndef __UBASE_MAILBOX_H__ #define __UBASE_MAILBOX_H__ -#include "ub/ubase/ubase_comm_mbx.h" +#include + #include "ubase_dev.h" #define CMD_MBX_POLL_VALUE 0xffff diff --git a/include/ub/ubase/ubase_comm_debugfs.h b/include/ub/ubase/ubase_comm_debugfs.h index c3d9c473d646..dc0bd30aa93b 100644 --- a/include/ub/ubase/ubase_comm_debugfs.h +++ b/include/ub/ubase/ubase_comm_debugfs.h @@ -34,7 +34,14 @@ struct ubase_dbgfs { int cmd_info_size; }; +int ubase_dbg_seq_file_init(struct device *dev, + struct ubase_dbg_dentry_info *dirs, + struct ubase_dbgfs *dbgfs, u32 idx); int ubase_dbg_create_dentry(struct device *dev, struct ubase_dbgfs *dbgfs, struct ubase_dbg_dentry_info *dirs, u32 root_idx); +struct dentry *ubase_diag_debugfs_root(struct auxiliary_device *adev); +void ubase_print_context_hw(struct seq_file *s, void *ctx_addr, u32 ctx_len); +bool ubase_dbg_dentry_support(struct auxiliary_device *adev, u32 property); +int ubase_dbg_format_time(time64_t time, struct seq_file *s); #endif -- Gitee From 828b36101403a9b592e5e580ff0dc6e9c143e304 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Fri, 12 Sep 2025 17:46:15 +0800 Subject: [PATCH 148/214] ub: ubase: add the function that query ueid ANBZ: #45460 commit 2e855fc37dac44e0c535dfe828e82ed8fe1bcf6f openEuler. This patch adds the support of UBASE driver for querying bus eid for all entities. And supply the interface to get ueid for udma driver, which is a upper layer driver to get the hardware information needed. Signed-off-by: Wenlong Zhu Signed-off-by: Fengyan Mu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_cmd.h | 5 ++++ drivers/ub/ubase/ubase_dev.c | 40 +++++++++++++++++++++++++++++++ include/ub/ubase/ubase_comm_cmd.h | 1 + include/ub/ubase/ubase_comm_dev.h | 2 ++ 4 files changed, 48 insertions(+) diff --git a/drivers/ub/ubase/ubase_cmd.h b/drivers/ub/ubase/ubase_cmd.h index 70c3f4d00719..7acaed31ee37 100644 --- a/drivers/ub/ubase/ubase_cmd.h +++ b/drivers/ub/ubase/ubase_cmd.h @@ -49,6 +49,11 @@ struct ubase_query_version_cmd { __le32 caps[UBASE_CAP_LEN]; }; +struct ubase_query_ueid_cmd { + __le32 ueid[UBASE_BUS_EID_LEN]; + u32 rsv[2]; +}; + static inline void __ubase_fill_inout_buf(struct ubase_cmd_buf *buf, u16 opcode, bool is_read, u32 data_size, void *data) { diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index 8c102000e7ef..e3d770c9225e 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -655,3 +655,43 @@ bool ubase_dbg_default(void) { return ubase_debug; } + +static int ubase_query_bus_eid(struct ubase_dev *udev, struct ubase_bus_eid *eid) +{ + struct ubase_query_ueid_cmd resp = {0}; + struct ubase_cmd_buf in, out; + int i, ret; + + __ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_BUS_EID, true, 0, NULL); + __ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_BUS_EID, false, + sizeof(resp), &resp); + + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret) { + ubase_err(udev, "failed to query bus eid, ret = %d.\n", ret); + return ret; + } + + for (i = 0; i < UBASE_BUS_EID_LEN; ++i) + eid->eid[i] = le32_to_cpu(resp.ueid[i]); + + return 0; +} + +static int __ubase_get_bus_eid(struct ubase_dev *udev, struct ubase_bus_eid *eid) +{ + return ubase_query_bus_eid(udev, eid); +} + +int ubase_get_bus_eid(struct auxiliary_device *adev, struct ubase_bus_eid *eid) +{ + struct ubase_dev *udev; + + if (!adev || !eid) + return -EINVAL; + + udev = __ubase_get_udev_by_adev(adev); + + return __ubase_get_bus_eid(udev, eid); +} +EXPORT_SYMBOL(ubase_get_bus_eid); diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index 0514dff9f2b8..9d5c352999ec 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -29,6 +29,7 @@ enum ubase_opcode_type { /* Generic commands */ UBASE_OPC_QUERY_FW_VER = 0x0001, + UBASE_OPC_QUERY_BUS_EID = 0x0047, /* Mailbox commands */ UBASE_OPC_POST_MB = 0x7000, diff --git a/include/ub/ubase/ubase_comm_dev.h b/include/ub/ubase/ubase_comm_dev.h index c07100c59a9f..2060a17e400c 100644 --- a/include/ub/ubase/ubase_comm_dev.h +++ b/include/ub/ubase/ubase_comm_dev.h @@ -222,4 +222,6 @@ struct ubase_adev_caps *ubase_get_unic_caps(struct auxiliary_device *adev); struct ubase_adev_caps *ubase_get_udma_caps(struct auxiliary_device *adev); struct ubase_adev_caps *ubase_get_cdma_caps(struct auxiliary_device *adev); +int ubase_get_bus_eid(struct auxiliary_device *adev, struct ubase_bus_eid *eid); + #endif -- Gitee From dde048625a541490c0a50ad68e51afeb36883b37 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Fri, 12 Sep 2025 19:00:58 +0800 Subject: [PATCH 149/214] ub: ubase: add function to query device resource ANBZ: #45460 commit 59e37a82209a0d0fd47e0e9a46a7984424d38fb4 openEuler. This patch adds the function to query device resource, which not only includes the resource needed by ubase, and also includs which necessary for driver unic, udma and cdma. Based on this, ubase as the foundation driver can do the resource management, such as TA/TP context buf and reserved jetty and so on. Ubase driver can get the resource count such as jetty number, jetty depth and tp layer resource's count and depth from firmware through the designated cmdq operation code. Signed-off-by: Fengyan Mu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/Makefile | 2 +- drivers/ub/ubase/ubase_dev.c | 7 +- drivers/ub/ubase/ubase_hw.c | 269 ++++++++++++++++++++++++++++++ drivers/ub/ubase/ubase_hw.h | 137 +++++++++++++++ include/ub/ubase/ubase_comm_cmd.h | 5 + include/ub/ubase/ubase_comm_dev.h | 1 + 6 files changed, 417 insertions(+), 4 deletions(-) create mode 100644 drivers/ub/ubase/ubase_hw.c create mode 100644 drivers/ub/ubase/ubase_hw.h diff --git a/drivers/ub/ubase/Makefile b/drivers/ub/ubase/Makefile index 59032bf09ddf..64169ef5d796 100644 --- a/drivers/ub/ubase/Makefile +++ b/drivers/ub/ubase/Makefile @@ -9,7 +9,7 @@ ccflags-y += -I$(srctree)/drivers/ub/ubase/debugfs MODULE_NAME := ubase -UBASE_OBJS := ubase_main.o ubase_dev.o ubase_cmd.o \ +UBASE_OBJS := ubase_main.o ubase_dev.o ubase_hw.o ubase_cmd.o \ debugfs/ubase_debugfs.o ubase_eq.o ubase_mailbox.o ubase_ubus.o \ debugfs/ubase_ctx_debugfs.o diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index e3d770c9225e..e87a819cdd07 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -9,6 +9,7 @@ #include "debugfs/ubase_debugfs.h" #include "ubase_cmd.h" +#include "ubase_hw.h" #include "ubase_mailbox.h" #include "ubase_dev.h" @@ -440,7 +441,7 @@ static const struct ubase_init_function ubase_init_func_map[] = { }, { "query dev res", UBASE_SUP_ALL, 0, - NULL, NULL + ubase_query_dev_res, NULL }, { "init mailbox", UBASE_SUP_NO_PMU, 0, @@ -448,11 +449,11 @@ static const struct ubase_init_function ubase_init_func_map[] = { }, { "query chip info", UBASE_SUP_ALL, 0, - NULL, NULL + ubase_query_chip_info, NULL }, { "query controller_info", UBASE_SUP_NO_PMU, 0, - NULL, NULL + ubase_query_controller_info, NULL }, { "query hw oor caps", UBASE_SUP_NO_PMU, 0, diff --git a/drivers/ub/ubase/ubase_hw.c b/drivers/ub/ubase/ubase_hw.c new file mode 100644 index 000000000000..11e32da647dc --- /dev/null +++ b/drivers/ub/ubase/ubase_hw.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include "ubase_cmd.h" +#include "ubase_hw.h" + +struct ubase_dma_buf_desc { + struct ubase_dma_buf *buf; + u16 opc; + bool (*is_supported)(struct ubase_dev *dev); +}; + +#define UBASE_DEFINE_DMA_BUFS(udev) \ + struct ubase_dma_buf_desc bufs[] = { \ + } + +static void ubase_assign_addr_val(void *addr, u32 val, u8 size) +{ +#define VAL_SIZE_1 1 +#define VAL_SIZE_2 2 +#define VAL_SIZE_4 4 + + switch (size) { + case VAL_SIZE_1: + *(u8 *)addr = (u8)val; + break; + case VAL_SIZE_2: + *(u16 *)addr = (u16)val; + break; + case VAL_SIZE_4: + *(u32 *)addr = (u32)val; + break; + default: + break; + } +} + +static void ubase_check_dev_caps_comm(struct ubase_dev *udev) +{ + struct ubase_caps *caps = &udev->caps.dev_caps; + struct ubase_caps_item items[] = { + { + &caps->num_ceq_vectors, UBASE_DEF_CEQ_VECTOR_NUM, + sizeof(caps->num_ceq_vectors), "ceq vector num" + }, + { + &caps->num_aeq_vectors, UBASE_DEF_AEQ_VECTOR_NUM, + sizeof(caps->num_aeq_vectors), "aeq vector num" + }, + { + &caps->num_misc_vectors, UBASE_DEF_MISC_VERCTOR_NUM, + sizeof(caps->num_misc_vectors), "misc vector num" + }, + { + &caps->public_jetty_cnt, UBASE_DEF_PUBLIC_JETTY_CNT, + sizeof(caps->public_jetty_cnt), "public jetty count" + }, + { + &caps->aeqe_size, UBASE_DEF_EQE_SIZE, + sizeof(caps->aeqe_size), "aeqe size" + }, + { + &caps->ceqe_size, UBASE_DEF_EQE_SIZE, + sizeof(caps->ceqe_size), "ceqe size" + }, + { + &caps->aeqe_depth, UBASE_DEF_AEQ_DEPTH, + sizeof(caps->aeqe_depth), "aeqe depth" + }, + { + &caps->ceqe_depth, UBASE_DEF_CEQ_DEPTH, + sizeof(caps->ceqe_depth), "ceqe depth" + }, + }; + u32 i, items_size = ARRAY_SIZE(items); + u32 val; + + for (i = 0; i < items_size; i++) { + val = 0; + memcpy(&val, items[i].p, items[i].size); + if (!val) { + ubase_assign_addr_val(items[i].p, items[i].default_val, + items[i].size); + ubase_warn(udev, "using default %s(%u).\n", + items[i].name, items[i].default_val); + } + } +} + +static int ubase_check_dev_caps_extdb(struct ubase_dev *udev) +{ + UBASE_DEFINE_DMA_BUFS(udev); + int i; + + for (i = 0; i < ARRAY_SIZE(bufs); i++) { + if (bufs[i].is_supported(udev) && !bufs[i].buf->size) { + ubase_err(udev, + "failed to check caps: buf[%d] size=0.\n", i); + return -EINVAL; + } + } + + return 0; +} + +static int ubase_check_dev_caps(struct ubase_dev *udev) +{ + ubase_check_dev_caps_comm(udev); + + return ubase_check_dev_caps_extdb(udev); +} + +static void ubase_parse_dev_caps_comm(struct ubase_dev *udev, + const struct ubase_res_cmd_resp *resp) +{ + struct ubase_caps *dev_caps = &udev->caps.dev_caps; + + dev_caps->num_ceq_vectors = le16_to_cpu(resp->ceq_vector_num); + dev_caps->num_aeq_vectors = le16_to_cpu(resp->aeq_vector_num); + dev_caps->num_misc_vectors = le16_to_cpu(resp->misc_vector_num); + dev_caps->aeqe_size = le16_to_cpu(resp->aeqe_size); + dev_caps->ceqe_size = le16_to_cpu(resp->ceqe_size); + dev_caps->aeqe_depth = le32_to_cpu(resp->aeqe_depth); + dev_caps->ceqe_depth = le32_to_cpu(resp->ceqe_depth); + dev_caps->total_ue_num = le32_to_cpu(resp->total_ue_num); + dev_caps->public_jetty_cnt = le32_to_cpu(resp->public_jetty_cnt); + dev_caps->rsvd_jetty_cnt = le16_to_cpu(resp->rsvd_jetty_cnt); + dev_caps->ue_num = resp->ue_num; + dev_caps->mac_stats_num = le16_to_cpu(resp->mac_stats_num); + + udev->ta_ctx.extdb_buf.size = le32_to_cpu(resp->ta_extdb_buf_size); + udev->ta_ctx.timer_buf.size = le32_to_cpu(resp->ta_timer_buf_size); +} + +static void ubase_parse_dev_caps_unic(struct ubase_dev *udev, + const struct ubase_res_cmd_resp *resp) +{ + struct ubase_adev_caps *unic_caps = &udev->caps.unic_caps; + + unic_caps->jfs.max_cnt = le32_to_cpu(resp->nic_jfs_max_cnt); + unic_caps->jfs.reserved_cnt = le32_to_cpu(resp->nic_jfs_reserved_cnt); + unic_caps->jfs.depth = le32_to_cpu(resp->nic_jfs_depth); + unic_caps->jfr.max_cnt = le32_to_cpu(resp->nic_jfr_max_cnt); + unic_caps->jfr.reserved_cnt = le32_to_cpu(resp->nic_jfr_reserved_cnt); + unic_caps->jfr.depth = le32_to_cpu(resp->nic_jfr_depth); + unic_caps->jfc.max_cnt = le32_to_cpu(resp->nic_jfc_max_cnt); + unic_caps->jfc.reserved_cnt = le32_to_cpu(resp->nic_jfc_reserved_cnt); + unic_caps->jfc.depth = le32_to_cpu(resp->nic_jfc_depth); + unic_caps->cqe_size = le16_to_cpu(resp->nic_cqe_size); + unic_caps->utp_port_bitmap = le32_to_cpu(resp->port_bitmap); +} + +static void ubase_parse_dev_caps_udma(struct ubase_dev *udev, + const struct ubase_res_cmd_resp *resp) +{ + struct ubase_adev_caps *udma_caps = &udev->caps.udma_caps; + + udma_caps->jfs.max_cnt = le32_to_cpu(resp->udma_jfs_max_cnt); + udma_caps->jfs.reserved_cnt = le32_to_cpu(resp->udma_jfs_reserved_cnt); + udma_caps->jfs.depth = le32_to_cpu(resp->udma_jfs_depth); + udma_caps->jfr.max_cnt = le32_to_cpu(resp->udma_jfr_max_cnt); + udma_caps->jfr.reserved_cnt = le32_to_cpu(resp->udma_jfr_reserved_cnt); + udma_caps->jfr.depth = le32_to_cpu(resp->udma_jfr_depth); + udma_caps->jfc.max_cnt = le32_to_cpu(resp->udma_jfc_max_cnt); + udma_caps->jfc.reserved_cnt = le32_to_cpu(resp->udma_jfc_reserved_cnt); + udma_caps->jfc.depth = le32_to_cpu(resp->udma_jfc_depth); + udma_caps->cqe_size = le16_to_cpu(resp->udma_cqe_size); + udma_caps->jtg_max_cnt = le32_to_cpu(resp->jtg_max_cnt); + udma_caps->rc_max_cnt = le32_to_cpu(resp->rc_max_cnt_per_vl); + udma_caps->rc_que_depth = le32_to_cpu(resp->udma_rc_depth); +} + +static void ubase_parse_dev_caps(struct ubase_dev *udev, + const struct ubase_res_cmd_resp *resp) +{ + int i; + + for (i = 0; i < UBASE_CAP_LEN; i++) + udev->cap_bits[i] = le32_to_cpu(resp->cap_bits[i]); + + ubase_parse_dev_caps_comm(udev, resp); + ubase_parse_dev_caps_unic(udev, resp); + ubase_parse_dev_caps_udma(udev, resp); +} + +static int ubase_parse_dev_res(struct ubase_dev *udev, + struct ubase_res_cmd_resp *resp) +{ + ubase_parse_dev_caps(udev, resp); + return ubase_check_dev_caps(udev); +} + +int ubase_query_dev_res(struct ubase_dev *udev) +{ + struct ubase_res_cmd_resp resp = {0}; + struct ubase_cmd_buf out; + struct ubase_cmd_buf in; + int ret; + + __ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_COMM_RSRC_PARAM, true, 0, + NULL); + + __ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_COMM_RSRC_PARAM, false, + sizeof(resp), &resp); + + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret) { + ubase_err(udev, "failed to query ubase res, ret = %d.\n", ret); + return ret; + } + + return ubase_parse_dev_res(udev, &resp); +} + +int ubase_query_controller_info(struct ubase_dev *udev) +{ + struct ubase_caps *dev_caps = &udev->caps.dev_caps; + struct ubase_query_controller_info_resp resp = {0}; + struct ubase_cmd_buf in, out; + int ret; + + ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_CTL_INFO, true, 0, NULL); + ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_CTL_INFO, true, + sizeof(resp), &resp); + + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret) { + ubase_err(udev, + "failed to query controller info, ret = %d.\n", ret); + return ret; + } + + dev_caps->packet_pattern_mode = resp.packet_pattern_mode; + dev_caps->ack_queue_num = resp.ack_queue_num; + + return 0; +} + +int ubase_query_chip_info(struct ubase_dev *udev) +{ + struct ubase_caps *dev_caps = &udev->caps.dev_caps; + struct ubase_query_chip_die_cmd resp = {0}; + struct ubase_cmd_buf in, out; + int ret; + + __ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_CHIP_INFO, true, 0, NULL); + __ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_CHIP_INFO, true, + sizeof(resp), &resp); + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret) { + ubase_err(udev, + "failed to query ub chip info, ret = %d.\n", ret); + return ret; + } + + dev_caps->nl_port_id = le16_to_cpu(resp.nl_port_id); + dev_caps->chip_id = le16_to_cpu(resp.chip_id); + dev_caps->die_id = le16_to_cpu(resp.die_id); + dev_caps->io_port_id = le16_to_cpu(resp.io_port_id); + dev_caps->ue_id = le16_to_cpu(resp.ue_id); + dev_caps->ub_port_logic_id = le16_to_cpu(resp.ub_port_logic_id); + dev_caps->io_port_logic_id = le16_to_cpu(resp.io_port_logic_id); + dev_caps->nl_id = le16_to_cpu(resp.nl_id); + + return 0; +} diff --git a/drivers/ub/ubase/ubase_hw.h b/drivers/ub/ubase/ubase_hw.h new file mode 100644 index 000000000000..b96db6d5c126 --- /dev/null +++ b/drivers/ub/ubase/ubase_hw.h @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UBASE_HW_H__ +#define __UBASE_HW_H__ + +#include + +#define UBASE_DEF_CEQ_VECTOR_NUM 1 +#define UBASE_DEF_AEQ_VECTOR_NUM 1 +#define UBASE_DEF_MISC_VERCTOR_NUM 1 +#define UBASE_DEF_PUBLIC_JETTY_CNT 1024 +#define UBASE_DEF_EQE_SIZE 64 +#define UBASE_DEF_AEQ_DEPTH 512 +#define UBASE_DEF_CEQ_DEPTH 4096 + +struct ubase_caps_item { + void *p; + u32 default_val; + u8 size; + const char *name; +}; + +struct ubase_res_cmd_resp { + __le32 cap_bits[UBASE_CAP_LEN]; + __le32 rsvd[3]; + + u8 rsvd1[2]; + __le16 ceq_vector_num; + __le16 aeq_vector_num; + __le16 misc_vector_num; + __le16 aeqe_size; + __le16 ceqe_size; + __le16 udma_cqe_size; + __le16 nic_cqe_size; + __le32 aeqe_depth; + __le32 ceqe_depth; + __le32 udma_jfs_max_cnt; + __le32 udma_jfs_reserved_cnt; + + __le32 udma_jfs_depth; + __le32 udma_jfr_max_cnt; + __le32 udma_jfr_reserved_cnt; + __le32 udma_jfr_depth; + u8 nic_vl_num; + u8 rsvd2[3]; + u8 nic_vl[UBASE_MAX_REQ_VL_NUM]; + __le32 udma_jfc_max_cnt; + + __le32 udma_jfc_reserved_cnt; + __le32 udma_jfc_depth; + __le32 udma_tp_max_cnt; + __le32 udma_tp_reserved_cnt; + __le32 udma_tp_depth; + __le32 udma_tpg_max_cnt; + __le32 udma_tpg_reserved_cnt; + __le32 udma_tpg_depth; + + __le32 nic_jfs_max_cnt; + __le32 nic_jfs_reserved_cnt; + __le32 nic_jfs_depth; + __le32 nic_jfr_max_cnt; + __le32 nic_jfr_reserved_cnt; + __le32 nic_jfr_depth; + __le32 rsvd3[2]; + + __le32 rsvd4; + __le32 nic_jfc_max_cnt; + __le32 nic_jfc_reserved_cnt; + __le32 nic_jfc_depth; + __le32 nic_tp_max_cnt; + __le32 nic_tp_reserved_cnt; + __le32 nic_tp_depth; + __le32 nic_tpg_max_cnt; + + __le32 nic_tpg_reserved_cnt; + __le32 nic_tpg_depth; + __le32 total_ue_num; + __le32 jfs_ctx_size; + __le32 jfr_ctx_size; + __le32 jfc_ctx_size; + __le32 tp_ctx_size; + __le16 rsvd_jetty_cnt; + __le16 mac_stats_num; + + __le32 ta_extdb_buf_size; + __le32 ta_timer_buf_size; + __le32 public_jetty_cnt; + __le32 tp_extdb_buf_size; + __le32 tp_timer_buf_size; + u8 port_work_mode; + u8 udma_vl_num; + u8 udma_tp_resp_vl_offset; + u8 ue_num; + __le32 port_bitmap; + u8 rsvd5[4]; + + /* include udma tp and ctp req vl */ + u8 udma_req_vl[UBASE_MAX_REQ_VL_NUM]; + __le32 udma_rc_depth; + u8 rsvd6[4]; + __le32 jtg_max_cnt; + __le32 rc_max_cnt_per_vl; + __le32 dest_addr_max_cnt; + __le32 seid_upi_max_cnt; + + __le32 tpm_max_cnt; + __le32 ccc_max_cnt; +}; + +struct ubase_query_controller_info_resp { + __le32 rsvd0[2]; + u8 packet_pattern_mode : 1; + u8 ack_queue_num : 4; + u8 rsvd1 : 3; + u8 rsvd2[15]; +}; + +struct ubase_query_chip_die_cmd { + __le16 nl_port_id; + __le16 chip_id; + __le16 die_id; + __le16 io_port_id; + __le16 ue_id; + __le16 ub_port_logic_id; + __le16 nl_id; + __le16 io_port_logic_id; +}; + +int ubase_query_dev_res(struct ubase_dev *udev); +int ubase_query_chip_info(struct ubase_dev *udev); +int ubase_query_controller_info(struct ubase_dev *udev); + +#endif diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index 9d5c352999ec..b8aa23431003 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -29,8 +29,13 @@ enum ubase_opcode_type { /* Generic commands */ UBASE_OPC_QUERY_FW_VER = 0x0001, + UBASE_OPC_QUERY_CTL_INFO = 0x0003, + UBASE_OPC_QUERY_COMM_RSRC_PARAM = 0x0030, UBASE_OPC_QUERY_BUS_EID = 0x0047, + /* PHY commands */ + UBASE_OPC_QUERY_CHIP_INFO = 0x6201, + /* Mailbox commands */ UBASE_OPC_POST_MB = 0x7000, UBASE_OPC_QUERY_MB_ST = 0X7001, diff --git a/include/ub/ubase/ubase_comm_dev.h b/include/ub/ubase/ubase_comm_dev.h index 2060a17e400c..d8bcb9349b42 100644 --- a/include/ub/ubase/ubase_comm_dev.h +++ b/include/ub/ubase/ubase_comm_dev.h @@ -18,6 +18,7 @@ struct iova_slot; #define UBASE_IOVA_COMM_PFN_CNT 1 #define UBASE_MAX_DSCP (64) #define UBASE_MAX_SL_NUM (16U) +#define UBASE_MAX_REQ_VL_NUM (8U) #define UBASE_MAX_VL_NUM (16U) #if UBASE_MAX_VL_NUM < IEEE_8021QAZ_MAX_TCS #error "UBASE_MAX_VL_NUM can't less than IEEE_8021QAZ_MAX_TCS" -- Gitee From 4210941768075dc8fbac98758eb6c12bc692668e Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Fri, 12 Sep 2025 19:39:52 +0800 Subject: [PATCH 150/214] ub: ubase: add interrupt management framework and misc handler ANBZ: #45460 commit d6d63eaa720e3c8b388e7a3c2e563d086d1f384b openEuler. This patch implements a complete interrupt management framework for the ubase driver, primarily providing two interface functions, ubase_irq_table_init and ubase_irq_table_uninit, for centralized management of device interrupt resources. The framework supports the handling of miscellaneous interrupts (misc IRQ), including interrupt request, registration, enabling/disabling, and resource release. It integrates with the underlying hardware abstraction layer (ubus) to obtain interrupt vectors, offering devices a unified interrupt lifecycle management capability. Signed-off-by: Haibin Lu Signed-off-by: Xiaobo Zhang Signed-off-by: Junxin Chen Signed-off-by: Xiongchuan Zhou Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_dev.c | 2 +- drivers/ub/ubase/ubase_eq.c | 208 +++++++++++++++++++++++++++++++++++ drivers/ub/ubase/ubase_eq.h | 12 ++ 3 files changed, 221 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index e87a819cdd07..7e7df281940a 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -461,7 +461,7 @@ static const struct ubase_init_function ubase_init_func_map[] = { }, { "init irq table", UBASE_SUP_NO_PMU, 1, - NULL, NULL + ubase_irq_table_init, ubase_irq_table_uninit }, { "init ctrl queue", UBASE_SUP_NO_PMU, 1, diff --git a/drivers/ub/ubase/ubase_eq.c b/drivers/ub/ubase/ubase_eq.c index 98e4bfe69ef6..6d3bcb0fb404 100644 --- a/drivers/ub/ubase/ubase_eq.c +++ b/drivers/ub/ubase/ubase_eq.c @@ -4,11 +4,219 @@ * */ +#include #include #include "ubase_dev.h" #include "ubase_eq.h" +void ubase_enable_misc_vector(struct ubase_dev *udev, bool enable) +{ + ubase_write_dev(&udev->hw, UBASE_MISC_VECTOR_REG_OFFSET, + enable ? 0 : 1); +} + +static int ubase_reg_event_handler(struct ubase_dev *udev) +{ + int ret = IRQ_HANDLED; + + ubase_enable_misc_vector(udev, false); + + ubase_enable_misc_vector(udev, true); + + return ret; +} + +static irqreturn_t ubase_misc_int_handler(int irq, void *data) +{ + struct ubase_dev *udev = (struct ubase_dev *)data; + + return IRQ_RETVAL(ubase_reg_event_handler(udev)); +} + +static int ubase_request_misc_irq(struct ubase_dev *udev) +{ + struct ubase_irq_table *irq_table = &udev->irq_table; + struct ubase_irq *irq; + int ret; + + if (ubase_ubus_irq_vector(udev->dev, 0) == -EOPNOTSUPP) + return 0; + + irq = irq_table->irqs[UBASE_MISC_IRQ_INDEX]; + snprintf(irq->name, UBASE_INT_NAME_LEN, "ubase%d-%s-%d", udev->dev_id, + "misc", 0); + ret = request_irq(irq->irqn, ubase_misc_int_handler, 0, irq->name, udev); + if (ret) { + ubase_err(udev, + "failed to request misc irq, ret = %d.\n", ret); + return ret; + } + + ubase_enable_misc_vector(udev, true); + + return ret; +} + +static void ubase_free_misc_irq(struct ubase_dev *udev) +{ + struct ubase_irq_table *irq_table = &udev->irq_table; + struct ubase_irq *irq; + + if (!irq_table->irqs) + return; + + ubase_enable_misc_vector(udev, false); + + irq = irq_table->irqs[UBASE_MISC_IRQ_INDEX]; + if (ubase_ubus_irq_vector(udev->dev, 0) != -EOPNOTSUPP) + free_irq(irq->irqn, udev); +} + +static int ubase_irq_init(struct ubase_dev *udev) +{ + struct ubase_irq_table *irq_table = &udev->irq_table; + u32 irqs_num = irq_table->irqs_num; + struct ubase_irq **irqs; + int ret; + u32 i; + + irqs = kcalloc(irqs_num, sizeof(struct ubase_irq *), GFP_KERNEL); + if (!irqs) { + ubase_err(udev, "failed to alloc irqs.\n"); + return -ENOMEM; + } + + for (i = 0; i < irqs_num; i++) { + irqs[i] = kzalloc(sizeof(struct ubase_irq), GFP_KERNEL); + if (!irqs[i]) { + ubase_err(udev, "failed to alloc ubase irq[%u].\n", i); + ret = -ENOMEM; + goto err_alloc_ubase_irq; + } + + if (ubase_ubus_irq_vector(udev->dev, 0) == -EOPNOTSUPP) + continue; + + irqs[i]->irqn = ubase_ubus_irq_vector(udev->dev, i); + if (irqs[i]->irqn < 0) { + ubase_err(udev, + "failed to get irq[%u] num, err irq num = %d.\n", + i, irqs[i]->irqn); + ret = irqs[i]->irqn; + kfree(irqs[i]); + goto err_alloc_ubase_irq; + } + } + irq_table->irqs = irqs; + + return 0; + +err_alloc_ubase_irq: + for (; i > 0; i--) + kfree(irqs[i - 1]); + kfree(irqs); + irq_table->irqs = NULL; + + return ret; +} + +static void ubase_irq_uninit(struct ubase_dev *udev) +{ + struct ubase_irq_table *irq_table = &udev->irq_table; + u32 i; + + if (!irq_table->irqs) + return; + + for (i = 0; i < irq_table->irqs_num; i++) + kfree(irq_table->irqs[i]); + kfree(irq_table->irqs); + irq_table->irqs = NULL; +} + +static int ubase_request_irq(struct ubase_dev *udev) +{ + int ret; + + ret = ubase_request_misc_irq(udev); + if (ret) { + ubase_err(udev, + "failed to request ubase misc irq, ret = %d.\n", ret); + return ret; + } + + return 0; +} + +static void ubase_free_irq(struct ubase_dev *udev) +{ + ubase_free_misc_irq(udev); +} + +int ubase_irq_table_init(struct ubase_dev *udev) +{ + struct ubase_irq_table *irq_table = &udev->irq_table; + int i, j, ret; + + if (!test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) { + for (i = 0; i < UBASE_DRV_MAX; i++) { + for (j = 0; j < UBASE_EVENT_TYPE_MAX; j++) + BLOCKING_INIT_NOTIFIER_HEAD(&irq_table->nh[i][j]); + } + mutex_init(&udev->irq_table.ceq_lock); + } + + ret = ubase_ubus_irq_vectors_alloc(udev->dev); + if (ret) { + ubase_err(udev, "failed to alloc irq vectors, ret = %d.\n", + ret); + goto err_irq_res_init; + } + + ret = ubase_irq_init(udev); + if (ret) { + ubase_err(udev, "failed to init ubase irq, ret = %d.\n", ret); + goto err_irq_init; + } + + ret = ubase_request_irq(udev); + if (ret) { + ubase_err(udev, "failed to request ubase irq, ret = %d.\n", + ret); + goto err_request_irq; + } + + clear_bit(UBASE_STATE_IRQ_INVALID_B, &udev->state_bits); + + return 0; + +err_request_irq: + ubase_irq_uninit(udev); +err_irq_init: + ubase_ubus_irq_vectors_free(udev->dev); +err_irq_res_init: + if (!test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + mutex_destroy(&udev->irq_table.ceq_lock); + + return ret; +} + +void ubase_irq_table_free(struct ubase_dev *udev) +{ + if (test_and_set_bit(UBASE_STATE_IRQ_INVALID_B, &udev->state_bits)) + return; + + ubase_free_irq(udev); + ubase_irq_uninit(udev); + ubase_ubus_irq_vectors_free(udev->dev); +} + +void ubase_irq_table_uninit(struct ubase_dev *udev) +{ + ubase_irq_table_free(udev); +} + static int __ubase_event_register(struct ubase_dev *udev, struct ubase_event_nb *cb) { diff --git a/drivers/ub/ubase/ubase_eq.h b/drivers/ub/ubase/ubase_eq.h index 5602e166a82d..6d40ef2173bb 100644 --- a/drivers/ub/ubase/ubase_eq.h +++ b/drivers/ub/ubase/ubase_eq.h @@ -17,6 +17,13 @@ #define UBASE_AE_LEVEL_NUM 4 +/* Vector0 interrupt control register */ +#define UBASE_MISC_VECTOR_REG_OFFSET 0x18020 + +enum ubase_eqc_irqn { + UBASE_MISC_IRQ_INDEX, +}; + struct ubase_irq { char name[UBASE_INT_NAME_LEN]; int irqn; @@ -69,7 +76,12 @@ struct ubase_irq_table { u32 irqs_num; }; +int ubase_irq_table_init(struct ubase_dev *udev); +void ubase_irq_table_uninit(struct ubase_dev *udev); + int ubase_register_ae_event(struct ubase_dev *udev); void ubase_unregister_ae_event(struct ubase_dev *udev); +void ubase_enable_misc_vector(struct ubase_dev *udev, bool enable); + #endif -- Gitee From 0a3064ddd58039fd5b12895138ab39aebf4b153f Mon Sep 17 00:00:00 2001 From: Fengyan Mu Date: Tue, 4 Nov 2025 09:59:22 +0800 Subject: [PATCH 151/214] ub: ubase: support for async event process ANBZ: #45460 commit 9cc0f22b5d69364f95783a1766fe155b6b01493f openEuler. This patch implements a complete asynchronous event queue (AEQ) management function for the ubase driver, with major enhancements including: 1. AEQ Core Mechanism: Added AEQ creation/destruction functionality, supporting interaction with hardware to manage EQ contexts via mailbox. 2. Event Handling Optimization: Implemented the main logic for asynchronous event handling, supporting event classification processing and an asynchronous processing mechanism based on work queues. 3. Interrupt Handling Enhancement: Expanded interrupt handlers, added AEQ-specific interrupt handling, and improved event cause identification and clearing mechanisms. 4. Hardware Interaction Support: Implemented EQ database update mechanisms and AEQ element traversal functions, using DMA-coherent memory allocation for EQ memory space. 5. Event Registration Expansion: Default registration of mailbox event callbacks, supporting event notification mechanisms for various driver types. This implementation provides efficient event handling capabilities, reliably processing various asynchronous events generated by hardware, and offers a comprehensive event notification infrastructure for upper-layer drivers. Signed-off-by: Xiaobo Zhang Signed-off-by: Junxin Chen Signed-off-by: Xiongchuan Zhou Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_eq.c | 401 +++++++++++++++++++++++++++++++++++- drivers/ub/ubase/ubase_eq.h | 97 +++++++++ 2 files changed, 495 insertions(+), 3 deletions(-) diff --git a/drivers/ub/ubase/ubase_eq.c b/drivers/ub/ubase/ubase_eq.c index 6d3bcb0fb404..4c7add6ee24b 100644 --- a/drivers/ub/ubase/ubase_eq.c +++ b/drivers/ub/ubase/ubase_eq.c @@ -7,23 +7,207 @@ #include #include +#include "ubase_cmd.h" #include "ubase_dev.h" +#include "ubase_mailbox.h" #include "ubase_eq.h" +enum ubase_eq_type { + UBASE_EQ_TYPE_AEQ, +}; + +static void ubase_update_eq_db(struct ubase_eq *eq, enum ubase_eq_type eq_type) +{ + struct ubase_eq_db eq_db = {0}; + + eq_db.eqn = eq->eqn; + eq_db.ci = eq->cons_index; + + writeq(*(__le64 *)&eq_db, eq->db_reg); +} + +static struct ubase_aeqe *ubase_next_aeqe(struct ubase_dev *udev, + struct ubase_aeq *aeq) +{ + struct ubase_eq *eq = &aeq->eq; + struct ubase_aeqe *aeqe; + + aeqe = (struct ubase_aeqe *)(eq->addr.addr + + (eq->cons_index & (eq->entries_num - 1)) * + eq->eqe_size); + + return aeqe->owner ^ !!(eq->cons_index & eq->entries_num) ? aeqe : NULL; +} + void ubase_enable_misc_vector(struct ubase_dev *udev, bool enable) { ubase_write_dev(&udev->hw, UBASE_MISC_VECTOR_REG_OFFSET, enable ? 0 : 1); } +static unsigned long ubase_check_event_cause(struct ubase_dev *udev) +{ + unsigned long event_cause = 0; + u32 cmdq_src_reg; + + cmdq_src_reg = ubase_read_dev(&udev->hw, UBASE_VECTOR0_CMDQ_SRC_REG); + if (cmdq_src_reg & BIT(UBASE_VECTOR0_RX_CMDQ_INT_B)) + event_cause |= BIT(UBASE_ASYNC_EVENT_CRQ_B); + + return event_cause; +} + +static void ubase_clear_event_cause(struct ubase_dev *udev, + unsigned long event_cause) +{ + if (test_bit(UBASE_ASYNC_EVENT_CRQ_B, &event_cause)) + ubase_write_dev(&udev->hw, UBASE_VECTOR0_CMDQ_SRC_REG, + BIT(UBASE_VECTOR0_RX_CMDQ_INT_B)); +} + +static void ubase_clear_all_event_cause(struct ubase_dev *udev) +{ + ubase_clear_event_cause(udev, BIT(UBASE_ASYNC_EVENT_CRQ_B)); +} + +static void ubase_crq_task_schedule(struct ubase_dev *udev) +{ + if (!test_and_set_bit(UBASE_STATE_CRQ_SERVICE_SCHED, + &udev->service_task.state)) { + udev->crq_table.last_crq_scheduled = jiffies; + mod_delayed_work(udev->ubase_wq, + &udev->service_task.service_task, 0); + } +} + static int ubase_reg_event_handler(struct ubase_dev *udev) { - int ret = IRQ_HANDLED; + unsigned long event_cause; ubase_enable_misc_vector(udev, false); + event_cause = ubase_check_event_cause(udev); + if (test_bit(UBASE_ASYNC_EVENT_CRQ_B, &event_cause)) + ubase_crq_task_schedule(udev); + + ubase_clear_event_cause(udev, event_cause); ubase_enable_misc_vector(udev, true); + return event_cause ? IRQ_HANDLED : IRQ_NONE; +} + +static bool ubase_is_udma_event(struct ubase_dev *udev, u8 event_type, + u8 sub_type, struct ubase_aeqe *aeqe) +{ + switch (event_type) { + case UBASE_EVENT_TYPE_CHECK_TOKEN: + return true; + default: + break; + } + + return false; +} + +static bool ubase_is_comm_event(struct ubase_dev *udev, struct ubase_aeqe *aeqe) +{ + switch (aeqe->event_type) { + case UBASE_EVENT_TYPE_MB: + return true; + default: + return false; + } + + return false; +} + +static void ubase_aeq_event_handler(struct ubase_dev *udev, + struct ubase_aeqe *aeqe) +{ + struct ubase_aeq_notify_info info; + u8 event_type = aeqe->event_type; + u8 sub_type = aeqe->sub_type; + u8 idx; + + if (event_type >= UBASE_EVENT_TYPE_MAX) { + ubase_err(udev, "event type wrong, event_type = %u.\n", + event_type); + return; + } + + info.event_type = event_type; + info.sub_type = sub_type; + info.aeqe = aeqe; + + if (ubase_is_comm_event(udev, aeqe)) + idx = UBASE_DRV_UNIC; + else if (ubase_is_udma_event(udev, event_type, sub_type, aeqe)) + idx = UBASE_DRV_UDMA; + else + idx = UBASE_DRV_UNIC; + + ubase_dbg(udev, "ubase do async work, idx = %u, event_type = %u.\n", + idx, event_type); + + blocking_notifier_call_chain(&udev->irq_table.nh[idx][event_type], + event_type, (void *)&info); +} + +static void ubase_async_service_task(struct work_struct *work) +{ + struct ubase_aeq_work *aeq_work = + container_of(work, struct ubase_aeq_work, work); + struct ubase_aeqe *aeqe = &aeq_work->aeqe; + struct ubase_dev *udev = aeq_work->udev; + + ubase_aeq_event_handler(udev, aeqe); + + kfree(aeq_work); +} + +static void ubase_init_aeq_work(struct ubase_dev *udev, struct ubase_aeqe *aeqe) +{ + struct ubase_aeq_work *aeq_work; + + aeq_work = kzalloc(sizeof(struct ubase_aeq_work), GFP_ATOMIC); + if (!aeq_work) { + dev_err_ratelimited(udev->dev, "failed to alloc aeq work.\n"); + return; + } + + aeq_work->udev = udev; + memcpy(&aeq_work->aeqe, aeqe, sizeof(struct ubase_aeqe)); + INIT_WORK(&aeq_work->work, ubase_async_service_task); + + queue_work(udev->ubase_async_wq, &aeq_work->work); +} + +static int ubase_async_event_handler(struct ubase_dev *udev) +{ + struct ubase_aeq *aeq = &udev->irq_table.aeq; + struct ubase_eq *eq = &aeq->eq; + struct ubase_aeqe *aeqe; + int ret = IRQ_NONE; + + aeqe = ubase_next_aeqe(udev, aeq); + while (aeqe) { + dma_rmb(); + + ubase_dbg(udev, + "event_type = 0x%x, sub_type = 0x%x, owner = %u, seq_num = %u, cons_index = %u.\n", + aeqe->event_type, aeqe->sub_type, aeqe->owner, + aeqe->event.cmd.seq_num, eq->cons_index); + + ret = IRQ_HANDLED; + + ubase_init_aeq_work(udev, aeqe); + + ++aeq->eq.cons_index; + aeqe = ubase_next_aeqe(udev, aeq); + + ubase_update_eq_db(&aeq->eq, UBASE_EQ_TYPE_AEQ); + } + return ret; } @@ -34,6 +218,143 @@ static irqreturn_t ubase_misc_int_handler(int irq, void *data) return IRQ_RETVAL(ubase_reg_event_handler(udev)); } +static irqreturn_t ubase_aeq_int_handler(int irq, void *data) +{ + struct ubase_dev *udev = (struct ubase_dev *)data; + + ubase_dbg(udev, "ubase enter aeq handler.\n"); + + return IRQ_RETVAL(ubase_async_event_handler(udev)); +} + +static void ubase_construct_eq_ctx(struct ubase_eq *eq, + struct ubase_eq_ctx *ctx, u32 tid) +{ + ctx->state = eq->state; + ctx->arm_st = eq->arm_st; + ctx->eqe_size = eq->eqe_size == UBASE_DEFAULT_EQE_SIZE ? 1 : 0; + ctx->pi = UBASE_EQ_INIT_PROD_IDX; + ctx->shift = ilog2(eq->entries_num) - UBASE_CTX_SHIFT_BASE; + ctx->eqe_coalesce_period = eq->eq_period; + ctx->ci = UBASE_EQ_INIT_CONS_IDX; + ctx->eqe_coalesce_cnt = eq->coalesce_cnt; + ctx->eqe_base_addr_l = eq->addr.dma_addr >> + UBASE_EQE_BA_L_OFFSET & UBASE_EQE_BA_L_VALID_BIT; + ctx->eqe_base_addr_h = eq->addr.dma_addr >> + UBASE_EQE_BA_H_OFFSET & UBASE_EQE_BA_H_VALID_BIT; + ctx->eqe_token_id = tid; + ctx->irq_num = eq->eqc_irqn; + ctx->pi_bypass = UBASE_EQ_INIT_PROD_IDX; + ctx->state2 = eq->state; +} + +static int ubase_fill_eq_attribute(struct ubase_dev *udev, struct ubase_eq *eq, + u32 eqn, struct ubase_irq *irq, + enum ubase_eq_type eq_type) +{ + struct ubase_eq_addr *eq_addr = &eq->addr; + + if (eq_type == UBASE_EQ_TYPE_AEQ) { + eq->eqe_size = udev->caps.dev_caps.aeqe_size; + eq->entries_num = udev->caps.dev_caps.aeqe_depth; + eq->eq_period = EQC_EQ_MAX_PERIOD_INDX; + eq->eqc_irqn = eqn + udev->caps.dev_caps.num_misc_vectors; + } + + eq->cons_index = 0; + eq->db_reg = udev->hw.mem_base.addr; + eq->eqn = eqn; + eq->state = UBASE_EQ_STAT_VALID; + eq->arm_st = UBASE_EQ_ALWAYS_ARMED; + eq->coalesce_cnt = UBASE_EQ_COALESCE_0; + eq->irqn = irq->irqn; + + eq_addr->size = eq->entries_num * eq->eqe_size; + eq_addr->addr = dma_alloc_coherent(udev->dev, eq_addr->size, + &eq_addr->dma_addr, GFP_KERNEL); + if (!eq_addr->addr) { + ubase_err(udev, "failed to alloc eqe base addr.\n"); + return -ENOMEM; + } + + return 0; +} + +static int ubase_create_eq(struct ubase_dev *udev, struct ubase_eq *eq, u32 eqn, + struct ubase_irq *irq, enum ubase_eq_type eq_type) +{ + struct ubase_cmd_mailbox *mbx; + struct ubase_mbx_attr attr; + int mbx_cmd; + int ret; + + ret = ubase_fill_eq_attribute(udev, eq, eqn, irq, eq_type); + if (ret) { + ubase_err(udev, "failed to fill eq attribute.\n"); + return ret; + } + + mbx_cmd = eq_type == UBASE_EQ_TYPE_AEQ ? UBASE_MB_CREATE_AEQ_CONTEXT : + UBASE_MB_CREATE_CEQ_CONTEXT; + mbx = __ubase_alloc_cmd_mailbox(udev); + if (IS_ERR_OR_NULL(mbx)) { + ubase_err(udev, "failed to alloc mailbox for create EQC.\n"); + ret = -ENOMEM; + goto err_alloc_mailbox; + } + ubase_construct_eq_ctx(eq, (struct ubase_eq_ctx *)mbx->buf, + udev->caps.dev_caps.tid); + ubase_fill_mbx_attr(&attr, eq->eqn, mbx_cmd, 0); + ret = ubase_hw_upgrade_ctx_poll(udev, &attr, mbx); + if (ret) { + ubase_err(udev, "failed to create EQC, ret = %d.\n", ret); + goto err_upgrade_ctx; + } + + __ubase_free_cmd_mailbox(udev, mbx); + + return 0; + +err_upgrade_ctx: + __ubase_free_cmd_mailbox(udev, mbx); +err_alloc_mailbox: + dma_free_coherent(udev->dev, eq->addr.size, eq->addr.addr, + eq->addr.dma_addr); + eq->addr.addr = NULL; + + return ret; +} + +static int ubase_destroy_eq(struct ubase_dev *udev, struct ubase_eq *eq, + enum ubase_eq_type eq_type) +{ + struct ubase_cmd_mailbox *mbx; + struct ubase_mbx_attr attr; + int mbx_cmd; + int ret; + + mbx_cmd = eq_type == UBASE_EQ_TYPE_AEQ ? UBASE_MB_DESTROY_AEQ_CONTEXT : + UBASE_MB_DESTROY_CEQ_CONTEXT; + + mbx = __ubase_alloc_cmd_mailbox(udev); + if (IS_ERR_OR_NULL(mbx)) { + ubase_err(udev, + "failed to alloc mailbox for destroy EQC.\n"); + return -ENOMEM; + } + ubase_fill_mbx_attr(&attr, eq->eqn, mbx_cmd, 0); + ret = ubase_hw_upgrade_ctx_poll(udev, &attr, mbx); + if (ret) + ubase_err(udev, "failed to destroy EQC, ret = %d.\n", ret); + + __ubase_free_cmd_mailbox(udev, mbx); + dma_free_coherent(udev->dev, eq->addr.size, eq->addr.addr, + eq->addr.dma_addr); + eq->addr.addr = NULL; + + return ret; +} + static int ubase_request_misc_irq(struct ubase_dev *udev) { struct ubase_irq_table *irq_table = &udev->irq_table; @@ -58,6 +379,39 @@ static int ubase_request_misc_irq(struct ubase_dev *udev) return ret; } +static int ubase_request_aeq_irq(struct ubase_dev *udev) +{ + struct ubase_irq_table *irq_table = &udev->irq_table; + struct ubase_aeq *aeq = &irq_table->aeq; + struct ubase_irq *irq; + int ret; + + irq = irq_table->irqs[UBASE_AEQ_IRQ_INDEX]; + snprintf(irq->name, UBASE_INT_NAME_LEN, "ubase%d-%s-%d", udev->dev_id, + "aeq", 0); + + ret = ubase_create_eq(udev, &aeq->eq, 0, irq, UBASE_EQ_TYPE_AEQ); + if (ret) { + ubase_err(udev, "failed to create aeq, ret = %d.\n", ret); + return ret; + } + + if (ubase_ubus_irq_vector(udev->dev, 0) == -EOPNOTSUPP) + return 0; + + ret = request_irq(irq->irqn, ubase_aeq_int_handler, 0, irq->name, udev); + if (ret) { + ubase_err(udev, + "failed to request aeq irq, ret = %d.\n", ret); + + if (ubase_destroy_eq(udev, &irq_table->aeq.eq, UBASE_EQ_TYPE_AEQ)) + ubase_err(udev, "failed to destroy aeq.\n"); + return ret; + } + + return 0; +} + static void ubase_free_misc_irq(struct ubase_dev *udev) { struct ubase_irq_table *irq_table = &udev->irq_table; @@ -73,6 +427,25 @@ static void ubase_free_misc_irq(struct ubase_dev *udev) free_irq(irq->irqn, udev); } +static void ubase_free_aeq_irq(struct ubase_dev *udev) +{ + struct ubase_aeq *aeq = &udev->irq_table.aeq; + + if (ubase_ubus_irq_vector(udev->dev, 0) != -EOPNOTSUPP) + free_irq(aeq->eq.irqn, udev); +} + +static void ubase_destroy_aeq(struct ubase_dev *udev) +{ + struct ubase_aeq *aeq = &udev->irq_table.aeq; + + if (!aeq->eq.addr.addr) + return; + + if (ubase_destroy_eq(udev, &aeq->eq, UBASE_EQ_TYPE_AEQ)) + ubase_err(udev, "failed to destroy aeq.\n"); +} + static int ubase_irq_init(struct ubase_dev *udev) { struct ubase_irq_table *irq_table = &udev->irq_table; @@ -143,14 +516,26 @@ static int ubase_request_irq(struct ubase_dev *udev) if (ret) { ubase_err(udev, "failed to request ubase misc irq, ret = %d.\n", ret); - return ret; + goto err_misc_init; + } + + ret = ubase_request_aeq_irq(udev); + if (ret) { + ubase_err(udev, + "failed to request ubase aeq irq, ret = %d.\n", ret); + goto err_aeq_init; } return 0; +err_aeq_init: + ubase_free_misc_irq(udev); +err_misc_init: + return ret; } static void ubase_free_irq(struct ubase_dev *udev) { + ubase_free_aeq_irq(udev); ubase_free_misc_irq(udev); } @@ -180,6 +565,8 @@ int ubase_irq_table_init(struct ubase_dev *udev) goto err_irq_init; } + ubase_clear_all_event_cause(udev); + ret = ubase_request_irq(udev); if (ret) { ubase_err(udev, "failed to request ubase irq, ret = %d.\n", @@ -215,6 +602,7 @@ void ubase_irq_table_free(struct ubase_dev *udev) void ubase_irq_table_uninit(struct ubase_dev *udev) { ubase_irq_table_free(udev); + ubase_destroy_aeq(udev); } static int __ubase_event_register(struct ubase_dev *udev, @@ -346,7 +734,14 @@ void ubase_unregister_ae_event(struct ubase_dev *udev) int ubase_register_ae_event(struct ubase_dev *udev) { - struct ubase_event_nb ubase_ae_nbs[UBASE_AE_LEVEL_NUM] = {}; + struct ubase_event_nb ubase_ae_nbs[UBASE_AE_LEVEL_NUM] = { + { + UBASE_DRV_UNIC, + UBASE_EVENT_TYPE_MB, + { ubase_cmd_mbx_event_cb }, + udev + } + }; struct ubase_aeq *aeq = &udev->irq_table.aeq; int i, ret; diff --git a/drivers/ub/ubase/ubase_eq.h b/drivers/ub/ubase/ubase_eq.h index 6d40ef2173bb..326b80879929 100644 --- a/drivers/ub/ubase/ubase_eq.h +++ b/drivers/ub/ubase/ubase_eq.h @@ -13,15 +13,40 @@ #define UBASE_MIN_IRQ_NUM 3 /* for misc aeq ceq */ +#define UBASE_EQ_ALWAYS_ARMED 0x2 +#define UBASE_EQ_COALESCE_0 0 +#define UBASE_EQ_INIT_PROD_IDX 0 +#define UBASE_EQ_INIT_CONS_IDX 0 +#define UBASE_EQ_STAT_INVALID 0 +#define UBASE_EQ_STAT_VALID 1 + +#define UBASE_CTX_SHIFT_BASE 6 +#define UBASE_DEFAULT_EQE_SIZE 64 +#define UBASE_EQE_BA_L_OFFSET 12 +#define UBASE_EQE_BA_L_VALID_BIT GENMASK(19, 0) +#define UBASE_EQE_BA_H_OFFSET 32 +#define UBASE_EQE_BA_H_VALID_BIT GENMASK(31, 0) + +#define EQC_EQ_MAX_PERIOD_INDX 4U + #define UBASE_INT_NAME_LEN 32 #define UBASE_AE_LEVEL_NUM 4 +/* Vector0 interrupt CMDQ event source register(RW) */ +#define UBASE_VECTOR0_CMDQ_SRC_REG 0x18004 /* Vector0 interrupt control register */ #define UBASE_MISC_VECTOR_REG_OFFSET 0x18020 +/* CMDQ register bits for RX event */ +#define UBASE_VECTOR0_RX_CMDQ_INT_B 1 + +enum ubase_async_event_cause_bit { + UBASE_ASYNC_EVENT_CRQ_B, +}; enum ubase_eqc_irqn { UBASE_MISC_IRQ_INDEX, + UBASE_AEQ_IRQ_INDEX, }; struct ubase_irq { @@ -29,6 +54,16 @@ struct ubase_irq { int irqn; }; +struct ubase_eq_db { + u8 eqn; + u8 rsv0; + __le16 type : 2; + __le16 rsv1 : 14; + + __le32 ci : 24; + __le32 rsv2 : 8; +}; + struct ubase_eq_addr { void *addr; dma_addr_t dma_addr; @@ -50,6 +85,62 @@ struct ubase_eq { u32 cons_index; }; +struct ubase_eq_ctx { + /* DW0 */ + u32 state : 2; + u32 arm_st : 2; + u32 eqe_size : 1; + u32 rsv0 : 3; + u32 pi : 24; + + /* DW1 */ + u32 shift : 5; + u32 eqe_coalesce_period : 3; + u32 ci : 24; + + /* DW2 */ + u32 eqe_coalesce_cnt : 10; + u32 rsv1 : 2; + u32 eqe_base_addr_l : 20; + + /* DW3 */ + u32 eqe_base_addr_h; + + /* DW4 */ + u32 eqe_token_id : 20; + u32 rsv2 : 12; + + /* DW5 */ + u32 eqe_token_value; + + /* DW6 */ + u32 irq_num : 16; + u32 rsv3_1 : 16; + + /* DW7 */ + u32 rsv3_2; + + /* DW8 */ + u32 eqn : 8; + u32 eqe_cnt : 10; + u32 rsv4 : 12; + u32 eqe_report_timer_l : 2; + + /* DW9 */ + u32 eqe_report_timer_h; + + /* DW10 */ + u32 funid : 8; + u32 pi_bypass : 24; + + /* DW11 */ + u32 state2 : 2; + u32 rsv5_1 : 30; + + /* DW12~DW15 */ + u32 rsv5_2[4]; +}; + struct ubase_ceq { struct ubase_dev *udev; struct ubase_eq eq; @@ -76,6 +167,12 @@ struct ubase_irq_table { u32 irqs_num; }; +struct ubase_aeq_work { + struct ubase_dev *udev; + struct work_struct work; + struct ubase_aeqe aeqe; +}; + int ubase_irq_table_init(struct ubase_dev *udev); void ubase_irq_table_uninit(struct ubase_dev *udev); -- Gitee From 70a3cfb0d5595c2f556d29b7ea5b08b54571a3f2 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Fri, 12 Sep 2025 20:24:21 +0800 Subject: [PATCH 152/214] ub: ubase: support for complete event process ANBZ: #45460 commit 21b9293cf5f6721b80be259a6b5660b377f8e815 openEuler. This patch implements a completion event queue (CEQ) management framework for the ubase driver, with the main features including: 1. Core Processing Mechanism: Adds a CEQ interrupt handler (ubase_ceq_int_handler) and completion event handling logic (ubase_comp_handler), supporting batch processing of completion events. 2. Lifecycle Management: Implements a complete management process for the creation, destruction, interrupt request, and release of CEQs, supporting dynamic creation of multiple CEQ vectors. 3. Interrupt Control: Provides interfaces for enabling/disabling CEQ interrupts (ubase_enable_ce_irqs/ubase_disable_ce_irqs) and sets a flag to prevent automatic interrupt enabling. 4. Resource Management: Uses device-managed memory allocation for CEQ structures, ensures thread safety through a mutex (ceq_lock), and improves error handling mechanisms. 5. Hardware Integration: Configures CEQ parameters based on device capabilities, supports interaction with hardware via mailboxes, and routes completion events to driver instances. This implementation provides efficient completion event handling capabilities for ubase devices, enhances the event handling framework, and complements the existing AEQ functionality. Signed-off-by: Haibin Lu Signed-off-by: Xiaobo Zhang Signed-off-by: Junxin Chen Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_dev.c | 2 +- drivers/ub/ubase/ubase_eq.c | 218 +++++++++++++++++++++++++++++++++++ drivers/ub/ubase/ubase_eq.h | 11 ++ 3 files changed, 230 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index 7e7df281940a..26b80b517702 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -509,7 +509,7 @@ static const struct ubase_init_function ubase_init_func_map[] = { }, { "enable ce irq", UBASE_SUP_NO_PMU, 1, - NULL, NULL + ubase_enable_ce_irqs, ubase_disable_ce_irqs }, }; diff --git a/drivers/ub/ubase/ubase_eq.c b/drivers/ub/ubase/ubase_eq.c index 4c7add6ee24b..7c9c5a2a7cd6 100644 --- a/drivers/ub/ubase/ubase_eq.c +++ b/drivers/ub/ubase/ubase_eq.c @@ -14,8 +14,42 @@ enum ubase_eq_type { UBASE_EQ_TYPE_AEQ, + UBASE_EQ_TYPE_CEQ, }; +static struct ubase_ceqe *ubase_next_ceqe(struct ubase_ceq *ceq) +{ + struct ubase_eq *eq = &ceq->eq; + struct ubase_ceqe *ceqe; + + ceqe = (struct ubase_ceqe *)(ceq->eq.addr.addr + + (eq->cons_index & (eq->entries_num - 1)) * + eq->eqe_size); + + return !!u32_get_bits(ceqe->comp, UBASE_CEQ_CEQE_OWNER_BIT) ^ + !!(eq->cons_index & eq->entries_num) ? ceqe : NULL; +} + +static void ubase_comp_handler(struct ubase_dev *udev, u32 jfcn) +{ + struct ubase_res_caps *unic_jfc_caps = &udev->caps.unic_caps.jfc; + u32 jfcn_end = unic_jfc_caps->start_idx + unic_jfc_caps->max_cnt; + u32 jfcn_begin = unic_jfc_caps->start_idx; + struct ubase_adev *uadev; + u8 idx; + + if (jfcn_begin <= jfcn && jfcn < jfcn_end) + idx = UBASE_DRV_UNIC; + else + idx = UBASE_DRV_UDMA; + + uadev = udev->priv.uadev[idx]; + if (unlikely(!uadev)) + return; + + atomic_notifier_call_chain(&uadev->comp_nh, jfcn, (void *)&uadev->adev); +} + static void ubase_update_eq_db(struct ubase_eq *eq, enum ubase_eq_type eq_type) { struct ubase_eq_db eq_db = {0}; @@ -23,9 +57,44 @@ static void ubase_update_eq_db(struct ubase_eq *eq, enum ubase_eq_type eq_type) eq_db.eqn = eq->eqn; eq_db.ci = eq->cons_index; + if (eq_type == UBASE_EQ_TYPE_CEQ) + eq_db.type = UBASE_EQ_DB_CMD_CEQ; + writeq(*(__le64 *)&eq_db, eq->db_reg); } +static irqreturn_t ubase_ceq_int_handler(int irq, void *data) +{ +#define UBASE_CEQ_POLLING_BUDGET 128 + + struct ubase_ceq *ceq = (struct ubase_ceq *)data; + struct ubase_ceqe *ceqe = ubase_next_ceqe(ceq); + struct ubase_dev *udev = ceq->udev; + bool ceqe_found = false; + u8 cnt = 0; + u32 jfcn; + + while (cnt++ < UBASE_CEQ_POLLING_BUDGET && ceqe) { + /* Make sure we read CEQ entry after we have checked the + * ownership bit + */ + dma_rmb(); + + jfcn = u32_get_bits(ceqe->comp, UBASE_CEQE_COMP_CQN_M); + + ubase_comp_handler(udev, jfcn); + + ++ceq->eq.cons_index; + ceqe_found = true; + ceqe = ubase_next_ceqe(ceq); + } + + if (ceqe_found) + ubase_update_eq_db(&ceq->eq, UBASE_EQ_TYPE_CEQ); + + return IRQ_RETVAL(ceqe_found); +} + static struct ubase_aeqe *ubase_next_aeqe(struct ubase_dev *udev, struct ubase_aeq *aeq) { @@ -259,6 +328,12 @@ static int ubase_fill_eq_attribute(struct ubase_dev *udev, struct ubase_eq *eq, eq->entries_num = udev->caps.dev_caps.aeqe_depth; eq->eq_period = EQC_EQ_MAX_PERIOD_INDX; eq->eqc_irqn = eqn + udev->caps.dev_caps.num_misc_vectors; + } else { + eq->eqe_size = udev->caps.dev_caps.ceqe_size; + eq->entries_num = udev->caps.dev_caps.ceqe_depth; + eq->eqc_irqn = eqn + udev->caps.dev_caps.num_misc_vectors + + udev->caps.dev_caps.num_aeq_vectors; + eq->eq_period = EQC_EQ_MAX_PERIOD_INDX; } eq->cons_index = 0; @@ -427,6 +502,17 @@ static void ubase_free_misc_irq(struct ubase_dev *udev) free_irq(irq->irqn, udev); } +static void ubase_free_ceq_irqs(struct ubase_dev *udev) +{ + struct ubase_ceqs *ceqs = &udev->irq_table.ceqs; + u32 i; + + for (i = 0; i < ceqs->num; i++) { + if (ubase_ubus_irq_vector(udev->dev, 0) != -EOPNOTSUPP) + free_irq(ceqs->ceq[i].eq.irqn, &ceqs->ceq[i]); + } +} + static void ubase_free_aeq_irq(struct ubase_dev *udev) { struct ubase_aeq *aeq = &udev->irq_table.aeq; @@ -435,6 +521,24 @@ static void ubase_free_aeq_irq(struct ubase_dev *udev) free_irq(aeq->eq.irqn, udev); } +static void ubase_destroy_ceqs(struct ubase_dev *udev) +{ + struct ubase_ceqs *ceqs = &udev->irq_table.ceqs; + u32 i; + + if (!ceqs->ceq) + return; + + for (i = 0; i < ceqs->num; i++) { + if (ubase_destroy_eq(udev, &ceqs->ceq[i].eq, UBASE_EQ_TYPE_CEQ)) + ubase_err(udev, "failed to destroy ceq[%u].\n", i); + } + mutex_lock(&udev->irq_table.ceq_lock); + devm_kfree(udev->dev, ceqs->ceq); + ceqs->ceq = NULL; + mutex_unlock(&udev->irq_table.ceq_lock); +} + static void ubase_destroy_aeq(struct ubase_dev *udev) { struct ubase_aeq *aeq = &udev->irq_table.aeq; @@ -446,6 +550,78 @@ static void ubase_destroy_aeq(struct ubase_dev *udev) ubase_err(udev, "failed to destroy aeq.\n"); } +static int ubase_request_ceq_irq(struct ubase_dev *udev, struct ubase_ceq *ceq, + u32 index) +{ + struct ubase_irq_table *irq_table = &udev->irq_table; + struct ubase_irq *irq; + int ret; + + irq = irq_table->irqs[index + UBASE_CEQ_IRQ_INDEX]; + snprintf(irq->name, UBASE_INT_NAME_LEN, "ubase%d-%s-%u", udev->dev_id, + "ceq", index); + ceq->udev = udev; + ret = ubase_create_eq(udev, &ceq->eq, index, irq, UBASE_EQ_TYPE_CEQ); + if (ret) { + ubase_err(udev, + "failed to create ceq[%u], ret = %d.\n", index, ret); + return ret; + } + + if (ubase_ubus_irq_vector(udev->dev, 0) == -EOPNOTSUPP) + return 0; + + irq_set_status_flags(irq->irqn, IRQ_NOAUTOEN); + ret = request_irq(irq->irqn, ubase_ceq_int_handler, 0, irq->name, ceq); + if (ret) { + ubase_err(udev, "failed to request ceq[%u], ret = %d.\n", + index, ret); + if (ubase_destroy_eq(udev, &ceq->eq, UBASE_EQ_TYPE_CEQ)) + ubase_err(udev, "failed to destroy ceq.\n"); + } + + return ret; +} + +static int ubase_request_ceq_irqs(struct ubase_dev *udev) +{ + struct ubase_irq_table *irq_table = &udev->irq_table; + struct ubase_ceqs *ceqs = &irq_table->ceqs; + u32 ceq_irq_num, i; + int ret; + + mutex_lock(&udev->irq_table.ceq_lock); + ceq_irq_num = udev->caps.dev_caps.num_ceq_vectors; + ceqs->ceq = devm_kcalloc(udev->dev, ceq_irq_num, + sizeof(struct ubase_ceq), GFP_KERNEL); + if (!ceqs->ceq) { + mutex_unlock(&udev->irq_table.ceq_lock); + return -ENOMEM; + } + mutex_unlock(&udev->irq_table.ceq_lock); + + ceqs->num = ceq_irq_num; + + for (i = 0; i < ceq_irq_num; i++) { + ret = ubase_request_ceq_irq(udev, &ceqs->ceq[i], i); + if (ret) { + ubase_err(udev, + "failed to request ceq[%u] irq, ret = %d.\n", + i, ret); + ceqs->num = i; + goto err_alloc_ceq; + } + } + + return 0; + +err_alloc_ceq: + ubase_free_ceq_irqs(udev); + ubase_destroy_ceqs(udev); + + return ret; +} + static int ubase_irq_init(struct ubase_dev *udev) { struct ubase_irq_table *irq_table = &udev->irq_table; @@ -526,7 +702,18 @@ static int ubase_request_irq(struct ubase_dev *udev) goto err_aeq_init; } + ret = ubase_request_ceq_irqs(udev); + if (ret) { + ubase_err(udev, + "failed to request ubase ceq irqs, ret = %d.\n", ret); + goto err_ceq_init; + } + return 0; + +err_ceq_init: + ubase_free_aeq_irq(udev); + ubase_destroy_aeq(udev); err_aeq_init: ubase_free_misc_irq(udev); err_misc_init: @@ -535,6 +722,7 @@ static int ubase_request_irq(struct ubase_dev *udev) static void ubase_free_irq(struct ubase_dev *udev) { + ubase_free_ceq_irqs(udev); ubase_free_aeq_irq(udev); ubase_free_misc_irq(udev); } @@ -602,7 +790,37 @@ void ubase_irq_table_free(struct ubase_dev *udev) void ubase_irq_table_uninit(struct ubase_dev *udev) { ubase_irq_table_free(udev); + ubase_destroy_ceqs(udev); ubase_destroy_aeq(udev); + + if (!test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + mutex_destroy(&udev->irq_table.ceq_lock); +} + +void ubase_disable_ce_irqs(struct ubase_dev *udev) +{ + struct ubase_ceqs *ceqs = &udev->irq_table.ceqs; + u32 i; + + if (test_bit(UBASE_STATE_IRQ_INVALID_B, &udev->state_bits)) + return; + + for (i = 0; i < ceqs->num; i++) + disable_irq(ceqs->ceq[i].eq.irqn); +} + +int ubase_enable_ce_irqs(struct ubase_dev *udev) +{ + struct ubase_ceqs *ceqs = &udev->irq_table.ceqs; + u32 i; + + if (test_bit(UBASE_STATE_IRQ_INVALID_B, &udev->state_bits)) + return 0; + + for (i = 0; i < ceqs->num; i++) + enable_irq(ceqs->ceq[i].eq.irqn); + + return 0; } static int __ubase_event_register(struct ubase_dev *udev, diff --git a/drivers/ub/ubase/ubase_eq.h b/drivers/ub/ubase/ubase_eq.h index 326b80879929..9b7b04911e92 100644 --- a/drivers/ub/ubase/ubase_eq.h +++ b/drivers/ub/ubase/ubase_eq.h @@ -27,6 +27,9 @@ #define UBASE_EQE_BA_H_OFFSET 32 #define UBASE_EQE_BA_H_VALID_BIT GENMASK(31, 0) +#define UBASE_CEQ_CEQE_OWNER_BIT BIT(31) +#define UBASE_CEQE_COMP_CQN_M GENMASK(19, 0) +#define UBASE_EQ_DB_CMD_CEQ 0x2 #define EQC_EQ_MAX_PERIOD_INDX 4U #define UBASE_INT_NAME_LEN 32 @@ -47,6 +50,7 @@ enum ubase_async_event_cause_bit { enum ubase_eqc_irqn { UBASE_MISC_IRQ_INDEX, UBASE_AEQ_IRQ_INDEX, + UBASE_CEQ_IRQ_INDEX, }; struct ubase_irq { @@ -64,6 +68,11 @@ struct ubase_eq_db { __le32 rsv2 : 8; }; +struct ubase_ceqe { + u32 comp; + u32 rsv[15]; +}; + struct ubase_eq_addr { void *addr; dma_addr_t dma_addr; @@ -180,5 +189,7 @@ int ubase_register_ae_event(struct ubase_dev *udev); void ubase_unregister_ae_event(struct ubase_dev *udev); void ubase_enable_misc_vector(struct ubase_dev *udev, bool enable); +void ubase_disable_ce_irqs(struct ubase_dev *udev); +int ubase_enable_ce_irqs(struct ubase_dev *udev); #endif -- Gitee From 45799dbc85c8f1f8196438319773459d8159454c Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Tue, 16 Sep 2025 11:38:45 +0800 Subject: [PATCH 153/214] ub: ubase: support for cdma auxiliary device ANBZ: #45460 commit a5587f20a0e17e90b854065efbd6f7b76d97dc9b openEuler. This patch support for cdma auxiliary device. The following steps are completed: 1.add device id and cdma auxiliary device type to the auxiliary bus. 2.add aeq and ceq notification chain process of the cdma. Signed-off-by: Zesong Li Signed-off-by: Xiaobo Zhang Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_eq.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubase/ubase_eq.c b/drivers/ub/ubase/ubase_eq.c index 7c9c5a2a7cd6..e82de4fa9069 100644 --- a/drivers/ub/ubase/ubase_eq.c +++ b/drivers/ub/ubase/ubase_eq.c @@ -38,7 +38,10 @@ static void ubase_comp_handler(struct ubase_dev *udev, u32 jfcn) struct ubase_adev *uadev; u8 idx; - if (jfcn_begin <= jfcn && jfcn < jfcn_end) + /* CDMA exists independently, UNIC/UDMA are distinguished by jfcn. */ + if (udev->priv.uadev[UBASE_DRV_CDMA]) + idx = UBASE_DRV_CDMA; + else if (jfcn_begin <= jfcn && jfcn < jfcn_end) idx = UBASE_DRV_UNIC; else idx = UBASE_DRV_UDMA; @@ -210,6 +213,8 @@ static void ubase_aeq_event_handler(struct ubase_dev *udev, if (ubase_is_comm_event(udev, aeqe)) idx = UBASE_DRV_UNIC; + else if (ubase_dev_cdma_supported(udev)) + idx = UBASE_DRV_CDMA; else if (ubase_is_udma_event(udev, event_type, sub_type, aeqe)) idx = UBASE_DRV_UDMA; else -- Gitee From d9361aa1e96ad2142778db2c9782f409515e7c50 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Tue, 16 Sep 2025 15:32:51 +0800 Subject: [PATCH 154/214] ub: ubase: Support for public Context memory allocation and release. ANBZ: #45460 commit a748088829be770a59028c9b9c3f6763a8e48cf3 openEuler. This patch adds comprehensive management functionality for hardware context buffers to the ubase driver, primarily implementing the following: 1. Core Initialization Mechanism: New functions `ubase_hw_init` and `ubase_hw_uninit` have been added and integrated into the device initialization process, responsible for the creation and destruction of hardware contexts. 2. Context Buffer Management: - Supports multiple context types (JFS, JFR, JFC, JTG, RC). - Implements mechanisms for context allocation, population, and release. - Utilizes XArray for efficient management of context pages. 3. Hardware Resource Configuration: - Dynamically calculates the number and size of context entries based on device capabilities. - Supports configuration of contexts through mailbox communication with hardware. - Adds resource destruction commands and waiting mechanisms. 4. Memory Management Optimization: - Uses DMA IOVA allocator to manage context buffers. - Supports pre-allocated memory (PMEM) optimization for JFS context configuration. This implementation provides ubase devices with complete hardware context management capabilities, laying the foundation for subsequent data transfer and queue operations. Signed-off-by: Fengyan Mu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_dev.c | 2 +- drivers/ub/ubase/ubase_dev.h | 15 ++ drivers/ub/ubase/ubase_hw.c | 343 ++++++++++++++++++++++++++++++ drivers/ub/ubase/ubase_hw.h | 9 + include/ub/ubase/ubase_comm_cmd.h | 1 + include/ub/ubase/ubase_comm_hw.h | 8 + 6 files changed, 377 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index 26b80b517702..c0ee8be61660 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -493,7 +493,7 @@ static const struct ubase_init_function ubase_init_func_map[] = { }, { "init hw", UBASE_SUP_NO_PMU, 1, - NULL, NULL + ubase_hw_init, ubase_hw_uninit }, { "init debugfs", UBASE_SUP_ALL, 0, diff --git a/drivers/ub/ubase/ubase_dev.h b/drivers/ub/ubase/ubase_dev.h index ee59ac41c8be..e53462115b3d 100644 --- a/drivers/ub/ubase/ubase_dev.h +++ b/drivers/ub/ubase/ubase_dev.h @@ -77,6 +77,11 @@ struct ubase_mbox_cmd { struct ubase_mbx_event_context ctx; }; +struct ubase_destroy_res_cmd { + u8 destroy_done; + u8 rsv[23]; +}; + struct ubase_dma_buf { void *addr; dma_addr_t dma_addr; @@ -281,6 +286,16 @@ static inline bool ubase_dev_eth_mac_supported(struct ubase_dev *udev) return ubase_get_cap_bit(udev, UBASE_SUPPORT_ETH_MAC_B); } +static inline u32 ubase_jfs_num(struct ubase_dev *udev) +{ + struct ubase_adev_caps *unic_caps = &udev->caps.unic_caps; + struct ubase_adev_caps *udma_caps = &udev->caps.udma_caps; + struct ubase_caps *dev_caps = &udev->caps.dev_caps; + + return unic_caps->jfs.max_cnt + udma_caps->jfs.max_cnt + + dev_caps->public_jetty_cnt + dev_caps->rsvd_jetty_cnt; +} + int ubase_adev_idx_alloc(void); void ubase_adev_idx_free(int id); diff --git a/drivers/ub/ubase/ubase_hw.c b/drivers/ub/ubase/ubase_hw.c index 11e32da647dc..332178813766 100644 --- a/drivers/ub/ubase/ubase_hw.c +++ b/drivers/ub/ubase/ubase_hw.c @@ -4,9 +4,17 @@ * */ +#include +#include +#include +#include + #include "ubase_cmd.h" +#include "ubase_mailbox.h" #include "ubase_hw.h" +#define UBASE_CTX_REMOVE_ALL (-2) + struct ubase_dma_buf_desc { struct ubase_dma_buf *buf; u16 opc; @@ -215,6 +223,272 @@ int ubase_query_dev_res(struct ubase_dev *udev) return ubase_parse_dev_res(udev, &resp); } +static int ubase_config_ctx_buf_to_hw(struct ubase_dev *udev, + struct ubase_ctx_buf_cap *ctx_buf, + struct ubase_mbx_attr *attr) +{ + struct ubase_cmd_mailbox mailbox; + int ret; + + mailbox.dma = ctx_buf->dma_ctx_buf_ba; + ret = __ubase_hw_upgrade_ctx(udev, attr, &mailbox); + if (ret) + ubase_err(udev, + "failed to config ctx_buf to hw, cmd = 0x%x, ret = %d.\n", + attr->op, ret); + return ret; +} + +static void ubase_free_and_clear_ctx_buf(struct ubase_dev *udev, + struct ubase_ctx_buf_cap *ctx_buf) +{ + struct ubase_ctx_page *ctx_page; + size_t npage; + + if (!xa_empty(&ctx_buf->ctx_xa)) { + xa_for_each(&ctx_buf->ctx_xa, npage, ctx_page) + ubase_destroy_ctx_page(udev, ctx_page, ctx_buf); + } + dma_free_iova(ctx_buf->slot); + + ctx_buf->slot = NULL; + ctx_buf->dma_ctx_buf_ba = 0; +} + +static void ubase_cmd_ctx_buf_free(struct ubase_dev *udev, + struct ubase_ctx_buf_cap *ctx_buf) +{ + size_t size; + + if (!ctx_buf || !ctx_buf->slot) + return; + + size = ctx_buf->entry_cnt * ctx_buf->entry_size; + if (!size) + return; + + ubase_free_and_clear_ctx_buf(udev, ctx_buf); + xa_destroy(&ctx_buf->ctx_xa); +} + +static void ubase_ctx_free(struct ubase_dev *udev, + struct ubase_ctx_buf *ctx_buf, int idx) +{ + struct ubase_ctx_buf_map map[] = { + { &ctx_buf->jfs, UBASE_MB_WRITE_JFS_CONTEXT_VA }, + { &ctx_buf->jfr, UBASE_MB_WRITE_JFR_CONTEXT_VA }, + { &ctx_buf->jfc, UBASE_MB_WRITE_JFC_CONTEXT_VA }, + { &ctx_buf->jtg, UBASE_MB_WRITE_JETTY_GROUP_CONTEXT_VA }, + { &ctx_buf->rc, UBASE_MB_WRITE_RC_CONTEXT_VA }, + }; + int i, end_idx = ARRAY_SIZE(map) - 1; + + i = (idx == UBASE_CTX_REMOVE_ALL) ? end_idx : idx; + for (; i >= 0; i--) + ubase_cmd_ctx_buf_free(udev, map[i].ctx); +} + +static int ubase_fill_common_ctx_buf(struct ubase_dev *udev, + struct ubase_ctx_buf_cap *ctx_buf, + u32 start_pos, u32 size) +{ + struct ubase_ctx_page *ctx_page; + size_t npage; + int ret; + u32 i; + + mutex_lock(&ctx_buf->ctx_mutex); + + for (i = start_pos; i < start_pos + size; i++) { + ctx_page = (struct ubase_ctx_page *)xa_load(&ctx_buf->ctx_xa, i); + if (ctx_page) + continue; + + ret = ubase_create_ctx_page(udev, ctx_buf, &ctx_page, i); + if (ret) { + ubase_err(udev, "failed to create context page, ret = %d.\n", + ret); + goto err_fill_ctx_page; + } + + ret = xa_err(xa_store(&ctx_buf->ctx_xa, i, ctx_page, + GFP_KERNEL)); + if (ret) { + ubase_err(udev, "failed to store page, ret = %d.\n", + ret); + goto err_fill_ctx_page; + } + } + + mutex_unlock(&ctx_buf->ctx_mutex); + + return 0; + +err_fill_ctx_page: + xa_for_each(&ctx_buf->ctx_xa, npage, ctx_page) + ubase_destroy_ctx_page(udev, ctx_page, ctx_buf); + mutex_unlock(&ctx_buf->ctx_mutex); + + return ret; +} + +static int ubase_fill_ctx_inherent_buf(struct ubase_dev *udev, + struct ubase_ctx_buf_cap *ctx_buf, + struct ubase_mbx_attr *attr) +{ + struct ubase_adev_caps *unic_caps = &udev->caps.unic_caps; + u32 buf_size, page_cnt; + u32 start_pos = 0; + + switch (attr->op) { + case UBASE_MB_WRITE_JFS_CONTEXT_VA: + start_pos = udev->caps.unic_caps.jfs.start_idx >> + ctx_buf->cnt_per_page_shift; + buf_size = unic_caps->jfs.max_cnt * ctx_buf->entry_size; + break; + case UBASE_MB_WRITE_JFR_CONTEXT_VA: + buf_size = unic_caps->jfr.max_cnt * ctx_buf->entry_size; + break; + case UBASE_MB_WRITE_JFC_CONTEXT_VA: + buf_size = unic_caps->jfc.max_cnt * ctx_buf->entry_size; + break; + default: + return 0; + } + + page_cnt = DIV_ROUND_UP(buf_size, PAGE_SIZE); + return ubase_fill_common_ctx_buf(udev, ctx_buf, start_pos, page_cnt); +} + +static int ubase_alloc_and_fill_ctx_buf(struct ubase_dev *udev, + struct ubase_ctx_buf_cap *ctx_buf, + struct ubase_mbx_attr *attr, + size_t size) +{ + size_t sizep; + int ret; + + ctx_buf->cnt_per_page_shift = + ilog2(roundup_pow_of_two(PAGE_SIZE / ctx_buf->entry_size)); + ctx_buf->slot = dma_alloc_iova(udev->dev, size, 0, + &ctx_buf->dma_ctx_buf_ba, &sizep); + if (IS_ERR(ctx_buf->slot)) { + ubase_err(udev, + "failed to alloc iova slot, cmd = 0x%x, size = %lu.\n", + attr->op, size); + return -ENOMEM; + } + + ret = ubase_fill_ctx_inherent_buf(udev, ctx_buf, attr); + if (ret) { + ubase_err(udev, + "failed to fill inherent ctx buf, cmd = 0x%x, ret = %d.\n", + attr->op, ret); + dma_free_iova(ctx_buf->slot); + } + + return ret; +} + +static int ubase_cmd_ctx_buf_alloc(struct ubase_dev *udev, + struct ubase_ctx_buf_cap *ctx_buf, + struct ubase_mbx_attr *attr) +{ + size_t size = ctx_buf->entry_cnt * ctx_buf->entry_size; + int ret; + + if (!size) + return 0; + + xa_init(&ctx_buf->ctx_xa); + ret = ubase_alloc_and_fill_ctx_buf(udev, ctx_buf, attr, size); + if (ret) + goto err_ctx_alloc; + + ret = ubase_config_ctx_buf_to_hw(udev, ctx_buf, attr); + if (ret) + goto err_ctx_to_hw; + + return 0; + +err_ctx_to_hw: + ubase_free_and_clear_ctx_buf(udev, ctx_buf); + +err_ctx_alloc: + xa_destroy(&ctx_buf->ctx_xa); + + return ret; +} + +static int ubase_ctx_alloc(struct ubase_dev *udev, + struct ubase_ctx_buf *ctx_buf) +{ + struct ubase_ctx_buf_map map[] = { + { &ctx_buf->jfs, UBASE_MB_WRITE_JFS_CONTEXT_VA }, + { &ctx_buf->jfr, UBASE_MB_WRITE_JFR_CONTEXT_VA }, + { &ctx_buf->jfc, UBASE_MB_WRITE_JFC_CONTEXT_VA }, + { &ctx_buf->jtg, UBASE_MB_WRITE_JETTY_GROUP_CONTEXT_VA }, + { &ctx_buf->rc, UBASE_MB_WRITE_RC_CONTEXT_VA }, + }; + struct ubase_mbx_attr attr = {0}; + int ret, i; + + for (i = 0; i < ARRAY_SIZE(map); i++) { + memset(&attr, 0, sizeof(attr)); + attr.op = map[i].mb_cmd; + ret = ubase_cmd_ctx_buf_alloc(udev, map[i].ctx, &attr); + if (ret) { + ubase_err(udev, + "failed to alloc ctx buf, mb_cmd = 0x%x, ret = %d.\n", + map[i].mb_cmd, ret); + goto err_alloc_ctx_buf; + } + } + + return 0; + +err_alloc_ctx_buf: + ubase_ctx_free(udev, ctx_buf, --i); + return ret; +} + +static void ubase_get_ctx_entry_cnt(struct ubase_dev *udev) +{ + struct ubase_adev_caps *unic_caps = &udev->caps.unic_caps; + struct ubase_adev_caps *udma_caps = &udev->caps.udma_caps; + struct ubase_ctx_buf *ubase_ctx_buf = &udev->ctx_buf; + + ubase_ctx_buf->jfs.entry_cnt = ubase_jfs_num(udev); + ubase_ctx_buf->jfr.entry_cnt = unic_caps->jfr.max_cnt; + ubase_ctx_buf->jfc.entry_cnt = unic_caps->jfc.max_cnt; + ubase_ctx_buf->jtg.entry_cnt = udma_caps->jtg_max_cnt; + ubase_ctx_buf->rc.entry_cnt = udma_caps->rc_max_cnt; + + ubase_ctx_buf->jfr.entry_cnt += udma_caps->jfr.max_cnt; + ubase_ctx_buf->jfc.entry_cnt += udma_caps->jfc.max_cnt; +} + +static void ubase_get_ctx_entry_size(struct ubase_dev *udev) +{ + struct ubase_ctx_buf *ubase_ctx_buf = &udev->ctx_buf; + + ubase_ctx_buf->jfs.entry_size = UBASE_JFS_CTX_SIZE; + ubase_ctx_buf->jfr.entry_size = UBASE_JFR_CTX_SIZE; + ubase_ctx_buf->jfc.entry_size = UBASE_JFC_CTX_SIZE; + ubase_ctx_buf->jtg.entry_size = UBASE_JTG_CTX_SIZE; + ubase_ctx_buf->rc.entry_size = UBASE_RC_CTX_SIZE; +} + +static int ubase_ctx_buf_alloc(struct ubase_dev *udev) +{ + struct ubase_ctx_buf *ctx_buf = &udev->ctx_buf; + + ubase_get_ctx_entry_cnt(udev); + ubase_get_ctx_entry_size(udev); + + return ubase_ctx_alloc(udev, ctx_buf); +} + int ubase_query_controller_info(struct ubase_dev *udev) { struct ubase_caps *dev_caps = &udev->caps.dev_caps; @@ -267,3 +541,72 @@ int ubase_query_chip_info(struct ubase_dev *udev) return 0; } + +static void ubase_destroy_ctx_res(struct ubase_dev *udev) +{ +#define UBASE_DESTROY_RES_WAIT_TIME 20 +#define UBASE_DESTROY_RES_WAIT_COUNT 5 + + struct ubase_destroy_res_cmd resp; + struct ubase_cmd_buf in, out; + int try_cnt = 0; + int ret; + + __ubase_fill_inout_buf(&in, UBASE_OPC_DESTROY_CTX_RESOURCE, false, 0, NULL); + ret = __ubase_cmd_send_in(udev, &in); + if (ret) { + ubase_err(udev, "failed to send destroy resource, ret = %d.\n", + ret); + return; + } + + __ubase_fill_inout_buf(&in, UBASE_OPC_DESTROY_CTX_RESOURCE, true, 0, NULL); + __ubase_fill_inout_buf(&out, UBASE_OPC_DESTROY_CTX_RESOURCE, false, + sizeof(resp), &resp); + do { + memset(&resp, 0, sizeof(resp)); + msleep(UBASE_DESTROY_RES_WAIT_TIME); + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret) { + ubase_err(udev, + "failed to query destroy resource, ret = %d.\n", + ret); + return; + } + + try_cnt++; + } while (!resp.destroy_done && try_cnt < UBASE_DESTROY_RES_WAIT_COUNT); + + if (!resp.destroy_done) + ubase_warn(udev, "wait ue destroy res timeout!\n"); +} + +static inline void ubase_uninit_ctx_buf(struct ubase_dev *udev) +{ + ubase_ctx_free(udev, &udev->ctx_buf, UBASE_CTX_REMOVE_ALL); +} + +int ubase_hw_init(struct ubase_dev *udev) +{ + int ret; + + ret = ubase_ctx_buf_alloc(udev); + if (ret) { + ubase_err(udev, "failed to init ctx buf, ret = %d.\n", ret); + return ret; + } + + set_bit(UBASE_STATE_CTX_READY_B, &udev->state_bits); + + return 0; +} + +void ubase_hw_uninit(struct ubase_dev *udev) +{ + clear_bit(UBASE_STATE_CTX_READY_B, &udev->state_bits); + + if (!test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + ubase_destroy_ctx_res(udev); + + ubase_uninit_ctx_buf(udev); +} diff --git a/drivers/ub/ubase/ubase_hw.h b/drivers/ub/ubase/ubase_hw.h index b96db6d5c126..b70097555a3e 100644 --- a/drivers/ub/ubase/ubase_hw.h +++ b/drivers/ub/ubase/ubase_hw.h @@ -9,6 +9,8 @@ #include +#define UBASE_CTX_REMOVE_ALL (-2) + #define UBASE_DEF_CEQ_VECTOR_NUM 1 #define UBASE_DEF_AEQ_VECTOR_NUM 1 #define UBASE_DEF_MISC_VERCTOR_NUM 1 @@ -130,6 +132,13 @@ struct ubase_query_chip_die_cmd { __le16 io_port_logic_id; }; +struct ubase_ctx_buf_map { + struct ubase_ctx_buf_cap *ctx; + u16 mb_cmd; +}; + +int ubase_hw_init(struct ubase_dev *udev); +void ubase_hw_uninit(struct ubase_dev *udev); int ubase_query_dev_res(struct ubase_dev *udev); int ubase_query_chip_info(struct ubase_dev *udev); int ubase_query_controller_info(struct ubase_dev *udev); diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index b8aa23431003..38f327a5bb8c 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -43,6 +43,7 @@ enum ubase_opcode_type { /* Software commands */ UBASE_OPC_MUE_TO_UE = 0xF001, UBASE_OPC_UE_TO_MUE = 0xF002, + UBASE_OPC_DESTROY_CTX_RESOURCE = 0xF00D, }; union ubase_mbox { diff --git a/include/ub/ubase/ubase_comm_hw.h b/include/ub/ubase/ubase_comm_hw.h index e212b4a7d2bc..ba3717fb16b3 100644 --- a/include/ub/ubase/ubase_comm_hw.h +++ b/include/ub/ubase/ubase_comm_hw.h @@ -10,6 +10,14 @@ #include #include +#define UBASE_AEQ_CTX_SIZE 64 +#define UBASE_CEQ_CTX_SIZE 64 +#define UBASE_JFS_CTX_SIZE 256 +#define UBASE_JFR_CTX_SIZE 64 +#define UBASE_JFC_CTX_SIZE 128 +#define UBASE_RC_CTX_SIZE 256 +#define UBASE_JTG_CTX_SIZE 8 + #define UBASE_DESC_DATA_LEN 6 struct ubase_cmdq_desc { __le16 opcode; -- Gitee From a973cea3901a3c6e2d21503c097903710a6498b4 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Tue, 16 Sep 2025 16:19:22 +0800 Subject: [PATCH 155/214] ub: ubase: Support for timer/ext_dtb buffer management. ANBZ: #45460 commit d226e3ba6b60e091e54ff29d04395bc654ed466a openEuler. This patch add management of the TP timer/extdb buffer and refactored the existed code for the TA timer/extdb buffer. Same as the TA layer, the TP layer also need a DMA buffer to cache extdb and timer in hardware, these buffers are also allocated by the driver, and the buffer sizes are also got from the firmware. Because these buffers are processed in the same way, so we refactored the existed code by using a buffer array. Signed-off-by: Chuan Wu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_dev.h | 10 ++++ drivers/ub/ubase/ubase_hw.c | 98 +++++++++++++++++++++++++++++++ drivers/ub/ubase/ubase_hw.h | 7 +++ include/ub/ubase/ubase_comm_cmd.h | 8 +++ 4 files changed, 123 insertions(+) diff --git a/drivers/ub/ubase/ubase_dev.h b/drivers/ub/ubase/ubase_dev.h index e53462115b3d..7acf89644c6d 100644 --- a/drivers/ub/ubase/ubase_dev.h +++ b/drivers/ub/ubase/ubase_dev.h @@ -281,6 +281,16 @@ static inline bool ubase_dev_ubl_supported(struct ubase_dev *udev) return ubase_get_cap_bit(udev, UBASE_SUPPORT_UBL_B); } +static inline bool ubase_dev_ta_extdb_buf_supported(struct ubase_dev *udev) +{ + return ubase_get_cap_bit(udev, UBASE_SUPPORT_TA_EXTDB_BUF_B); +} + +static inline bool ubase_dev_ta_timer_buf_supported(struct ubase_dev *udev) +{ + return ubase_get_cap_bit(udev, UBASE_SUPPORT_TA_TIMER_BUF_B); +} + static inline bool ubase_dev_eth_mac_supported(struct ubase_dev *udev) { return ubase_get_cap_bit(udev, UBASE_SUPPORT_ETH_MAC_B); diff --git a/drivers/ub/ubase/ubase_hw.c b/drivers/ub/ubase/ubase_hw.c index 332178813766..863f909a435b 100644 --- a/drivers/ub/ubase/ubase_hw.c +++ b/drivers/ub/ubase/ubase_hw.c @@ -10,6 +10,7 @@ #include #include "ubase_cmd.h" +#include "ubase_dev.h" #include "ubase_mailbox.h" #include "ubase_hw.h" @@ -23,6 +24,10 @@ struct ubase_dma_buf_desc { #define UBASE_DEFINE_DMA_BUFS(udev) \ struct ubase_dma_buf_desc bufs[] = { \ + { &(udev)->ta_ctx.extdb_buf, UBASE_OPC_TA_EXTDB_VA_CONFIG, \ + &ubase_dev_ta_extdb_buf_supported }, \ + { &(udev)->ta_ctx.timer_buf, UBASE_OPC_TA_TIMER_VA_CONFIG, \ + &ubase_dev_ta_timer_buf_supported } \ } static void ubase_assign_addr_val(void *addr, u32 val, u8 size) @@ -489,6 +494,88 @@ static int ubase_ctx_buf_alloc(struct ubase_dev *udev) return ubase_ctx_alloc(udev, ctx_buf); } +static int ubase_config_dma_buf(struct ubase_dev *udev, u16 opc, u64 dma_addr) +{ + struct ubase_cfg_dma_buf_req req = {0}; + struct ubase_cmd_buf in; + int ret; + + req.addr_l = cpu_to_le32(lower_32_bits(dma_addr)); + req.addr_h = cpu_to_le32(upper_32_bits(dma_addr)); + + __ubase_fill_inout_buf(&in, opc, false, sizeof(req), &req); + + ret = __ubase_cmd_send_in(udev, &in); + if (ret) + ubase_err(udev, + "failed to send cmd in cfg dma buf, ret = %d.\n", ret); + + return ret; +} + +static int ubase_init_dma_buf(struct ubase_dev *udev, struct ubase_dma_buf *buf, + u16 opc) +{ + int ret; + + buf->addr = dma_alloc_coherent(udev->dev, buf->size, &buf->dma_addr, + GFP_KERNEL); + if (!buf->addr) + return -ENOMEM; + + ret = ubase_config_dma_buf(udev, opc, buf->dma_addr); + if (ret) + dma_free_coherent(udev->dev, buf->size, buf->addr, + buf->dma_addr); + + return ret; +} + +static void ubase_uninit_dma_buf(struct ubase_dev *udev, + struct ubase_dma_buf *buf) +{ + if (!buf->addr) + return; + + dma_free_coherent(udev->dev, buf->size, buf->addr, buf->dma_addr); + buf->addr = NULL; +} + +static int ubase_init_ta_tp_ext_buf(struct ubase_dev *udev) +{ + UBASE_DEFINE_DMA_BUFS(udev); + int i, ret; + + for (i = 0; i < ARRAY_SIZE(bufs); i++) { + ret = bufs[i].is_supported(udev) ? + ubase_init_dma_buf(udev, bufs[i].buf, bufs[i].opc) : 0; + if (ret) { + ubase_err(udev, + "failed to init buf[%d], ret = %d.\n", i, ret); + goto err_out; + } + } + + return 0; +err_out: + for (i -= 1; i >= 0; i--) { + if (bufs[i].is_supported(udev)) + ubase_uninit_dma_buf(udev, bufs[i].buf); + } + return ret; +} + +static void ubase_uninit_ta_tp_ext_buf(struct ubase_dev *udev) +{ + UBASE_DEFINE_DMA_BUFS(udev); + int i; + + for (i = 0; i < ARRAY_SIZE(bufs); i++) { + if (bufs[i].is_supported(udev)) + ubase_uninit_dma_buf(udev, bufs[i].buf); + } +} + int ubase_query_controller_info(struct ubase_dev *udev) { struct ubase_caps *dev_caps = &udev->caps.dev_caps; @@ -596,15 +683,26 @@ int ubase_hw_init(struct ubase_dev *udev) return ret; } + ret = ubase_init_ta_tp_ext_buf(udev); + if (ret) + goto err_init_ta_tp_ext_buf; + set_bit(UBASE_STATE_CTX_READY_B, &udev->state_bits); return 0; + +err_init_ta_tp_ext_buf: + ubase_uninit_ctx_buf(udev); + + return ret; } void ubase_hw_uninit(struct ubase_dev *udev) { clear_bit(UBASE_STATE_CTX_READY_B, &udev->state_bits); + ubase_uninit_ta_tp_ext_buf(udev); + if (!test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) ubase_destroy_ctx_res(udev); diff --git a/drivers/ub/ubase/ubase_hw.h b/drivers/ub/ubase/ubase_hw.h index b70097555a3e..dff37099b719 100644 --- a/drivers/ub/ubase/ubase_hw.h +++ b/drivers/ub/ubase/ubase_hw.h @@ -121,6 +121,13 @@ struct ubase_query_controller_info_resp { u8 rsvd2[15]; }; +struct ubase_cfg_dma_buf_req { + __le32 addr_l; + __le32 addr_h; + __le32 tp_num; /* only used when cfg TP extdb buf */ + __le32 resv[3]; +}; + struct ubase_query_chip_die_cmd { __le16 nl_port_id; __le16 chip_id; diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index 38f327a5bb8c..98875d5c6076 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -33,6 +33,14 @@ enum ubase_opcode_type { UBASE_OPC_QUERY_COMM_RSRC_PARAM = 0x0030, UBASE_OPC_QUERY_BUS_EID = 0x0047, + /* TP commands */ + UBASE_OPC_TP_TIMER_VA_CONFIG = 0x3007, + UBASE_OPC_TP_EXTDB_VA_CONFIG = 0x3008, + + /* TA commands */ + UBASE_OPC_TA_EXTDB_VA_CONFIG = 0x4000, + UBASE_OPC_TA_TIMER_VA_CONFIG = 0x4001, + /* PHY commands */ UBASE_OPC_QUERY_CHIP_INFO = 0x6201, -- Gitee From 6c425c854d71b7f76af6d387254a793fac7893b2 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Tue, 16 Sep 2025 21:00:51 +0800 Subject: [PATCH 156/214] ub: ubase: Add the cmdq trace event print at the ubase layer. ANBZ: #45460 commit d4ecd932869fdec56eaa997001b258b34ded198e openEuler. This patch is used to add the cmdq trace event print at the ubase layer. Therefore, a tracing event framework is defined, and three tracing events of ubase_csq_tx/ubase_csq_rx/ubase_crq are defined according to the framework. The following changes are made: implement the ubase_trace.h header file, which is mainly used to provide interface functions for tracing events. invoke the trace event interface prefixed with trace_ in the interface for sending and receiving csq commands and processing crq datas. Signed-off-by: Xiaobo Zhang Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_cmd.c | 47 +++++++++++++ drivers/ub/ubase/ubase_cmd.h | 2 + drivers/ub/ubase/ubase_trace.h | 105 ++++++++++++++++++++++++++++++ include/ub/ubase/ubase_comm_cmd.h | 1 + 4 files changed, 155 insertions(+) create mode 100644 drivers/ub/ubase/ubase_trace.h diff --git a/drivers/ub/ubase/ubase_cmd.c b/drivers/ub/ubase/ubase_cmd.c index 81891811dfa3..6a16d521d736 100644 --- a/drivers/ub/ubase/ubase_cmd.c +++ b/drivers/ub/ubase/ubase_cmd.c @@ -7,6 +7,15 @@ #include #include "ubase_cmd.h" +#include "ubase_hw.h" + +/* When use tracepoint, must define "CREATE_TRACE_POINTS" before include the + * trace header file. + * If many source file need include the same header file, the + * "CREATE_TRACE_POINTS" only define once. + */ +#define CREATE_TRACE_POINTS +#include "ubase_trace.h" static int ubase_alloc_cmd_queue(struct ubase_dev *udev, struct ubase_cmdq_ring *ring) @@ -147,6 +156,7 @@ static void ubase_write_desc_to_cmdq(struct ubase_dev *udev, while (cnt < num) { desc_to_use = &csq->desc[csq->pi]; *desc_to_use = desc[cnt]; + trace_ubase_csq_tx(udev->dev, cnt, csq->pi, csq->ci, desc); (csq->pi)++; if (csq->pi >= csq->desc_num) csq->pi = 0; @@ -191,6 +201,7 @@ static int ubase_get_cmd_result(struct ubase_dev *udev, for (handle = 0; handle < num; handle++) { desc[handle] = csq->desc[pi]; + trace_ubase_csq_rx(udev->dev, handle, pi, csq->ci, desc); pi++; if (pi >= csq->desc_num) pi = 0; @@ -532,6 +543,7 @@ static void ubase_gen_multi_bd_data(struct ubase_dev *udev, u32 bd_num, for (i = 0; i < bd_num; i++) { desc = &crq->desc[crq->ci]; + trace_ubase_crq(udev->dev, i, crq->pi, crq->ci, desc); if (i == 0) { memcpy(*msg_data + pos, desc->data, UBASE_CMD_DATA_LENGTH); @@ -551,6 +563,7 @@ static void ubase_gen_single_bd_data(struct ubase_dev *udev, void **msg_data) struct ubase_cmdq_desc *desc; desc = &crq->desc[crq->ci]; + trace_ubase_crq(udev->dev, 1, crq->pi, crq->ci, desc); *msg_data = crq->desc[crq->ci].data; UBASE_MOVE_CRQ_RING_PTR(crq); } @@ -943,3 +956,37 @@ void ubase_unregister_crq_event(struct auxiliary_device *aux_dev, u16 opcode) __ubase_unregister_crq_event(udev, opcode); } EXPORT_SYMBOL(ubase_unregister_crq_event); + +void ubase_mask_key_words(struct ubase_cmdq_desc *desc, u16 opc, int idx) +{ + struct ubase_cfg_dma_buf_req *req; + union ubase_mbox *mb; + + switch (opc) { + case UBASE_OPC_TP_TIMER_VA_CONFIG: + case UBASE_OPC_TP_EXTDB_VA_CONFIG: + case UBASE_OPC_TA_EXTDB_VA_CONFIG: + case UBASE_OPC_TA_TIMER_VA_CONFIG: + if (idx) + return; + req = (struct ubase_cfg_dma_buf_req *)desc->data; + req->addr_l = 0; + req->addr_h = 0; + break; + case UBASE_OPC_POST_MB: + if (idx) + return; + mb = (union ubase_mbox *)desc->data; + mb->in_param_l = 0; + mb->in_param_h = 0; + break; + case UBASE_OPC_CFG_VPORT_BUF: + if (idx) + memset(desc, 0, sizeof(struct ubase_cmdq_desc)); + else + memset(desc->data, 0, sizeof(desc->data)); + break; + default: + return; + } +} diff --git a/drivers/ub/ubase/ubase_cmd.h b/drivers/ub/ubase/ubase_cmd.h index 7acaed31ee37..dc65f83e5d82 100644 --- a/drivers/ub/ubase/ubase_cmd.h +++ b/drivers/ub/ubase/ubase_cmd.h @@ -100,4 +100,6 @@ void __ubase_unregister_crq_event(struct ubase_dev *udev, u16 opcode); void ubase_crq_service_task(struct ubase_delay_work *ubase_work); +void ubase_mask_key_words(struct ubase_cmdq_desc *desc, u16 opc, int idx); + #endif diff --git a/drivers/ub/ubase/ubase_trace.h b/drivers/ub/ubase/ubase_trace.h new file mode 100644 index 000000000000..3a4c27f272aa --- /dev/null +++ b/drivers/ub/ubase/ubase_trace.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +/* This must be outside ifdef UBASE_TRACE_H_ */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM ubase + +#if !defined(__UBASE_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) +#define __UBASE_TRACE_H__ + +#include + +#define TRACE_TWO_BYTES_BITS 16 +#define TRACE_THREE_BYTES_BITS 24 +#define TRACE_CMDQ_DESC_SIZE (sizeof(struct ubase_cmdq_desc) / sizeof(u32)) + +#define TRACE_DEV_NAME_MAX_LEN 16 + +DECLARE_EVENT_CLASS(ubase_cmdq_template, + TP_PROTO(const struct device *dev, int idx, u32 pi, u32 ci, + struct ubase_cmdq_desc *desc), + TP_ARGS(dev, idx, pi, ci, desc), + + TP_STRUCT__entry( + __field(int, idx) + __field(u32, pi) + __field(u32, ci) + __array(u32, data, TRACE_CMDQ_DESC_SIZE) + __dynamic_array(char, devname, TRACE_DEV_NAME_MAX_LEN) + ), + + TP_fast_assign( + __entry->idx = idx; + __entry->pi = pi; + __entry->ci = ci; + memcpy(&__entry->data, &desc[idx], + sizeof(u32) * TRACE_CMDQ_DESC_SIZE); + if (dev) { + snprintf(__get_str(devname), TRACE_DEV_NAME_MAX_LEN, + "%s %s", dev_driver_string(dev), dev_name(dev)); + } + ubase_mask_key_words((struct ubase_cmdq_desc *)&__entry->data, + desc[0].opcode, idx); + ), + + TP_printk( + "%s %d-%u-%u data: %s", __get_str(devname), + __entry->idx, __entry->pi, __entry->ci, + __print_array(__entry->data, TRACE_CMDQ_DESC_SIZE, sizeof(u32)) + ) +); + +DEFINE_EVENT(ubase_cmdq_template, ubase_csq_tx, + TP_PROTO(const struct device *dev, int idx, u32 pi, u32 ci, + struct ubase_cmdq_desc *desc), + TP_ARGS(dev, idx, pi, ci, desc)); + +DEFINE_EVENT(ubase_cmdq_template, ubase_csq_rx, + TP_PROTO(const struct device *dev, int idx, u32 pi, u32 ci, + struct ubase_cmdq_desc *desc), + TP_ARGS(dev, idx, pi, ci, desc)); + +TRACE_EVENT(ubase_crq, + TP_PROTO(const struct device *dev, int idx, u32 pi, u32 ci, + struct ubase_cmdq_desc *desc), + TP_ARGS(dev, idx, pi, ci, desc), + + TP_STRUCT__entry( + __field(int, idx) + __field(u32, pi) + __field(u32, ci) + __array(u32, data, TRACE_CMDQ_DESC_SIZE) + __dynamic_array(char, devname, TRACE_DEV_NAME_MAX_LEN) + ), + + TP_fast_assign( + __entry->idx = idx; + __entry->pi = pi; + __entry->ci = ci; + memcpy(&__entry->data, desc, + sizeof(u32) * TRACE_CMDQ_DESC_SIZE); + if (dev) { + snprintf(__get_str(devname), TRACE_DEV_NAME_MAX_LEN, + "%s %s", dev_driver_string(dev), dev_name(dev)); + } + ), + + TP_printk( + "%s %d-%u-%u data: %s", __get_str(devname), + __entry->idx, __entry->pi, __entry->ci, + __print_array(__entry->data, TRACE_CMDQ_DESC_SIZE, sizeof(u32)) + ) +); + +#endif /* __UBASE_TRACE_H__ */ + +/* This must be outside ifdef __UBASE_TRACE_H__ */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE ubase_trace +#include diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index 98875d5c6076..fd728af9e11a 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -51,6 +51,7 @@ enum ubase_opcode_type { /* Software commands */ UBASE_OPC_MUE_TO_UE = 0xF001, UBASE_OPC_UE_TO_MUE = 0xF002, + UBASE_OPC_CFG_VPORT_BUF = 0xF003, UBASE_OPC_DESTROY_CTX_RESOURCE = 0xF00D, }; -- Gitee From b205be08ddbce2d8f6f57c15d9f794a3daec98bb Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Tue, 16 Sep 2025 21:06:55 +0800 Subject: [PATCH 157/214] ub: ubase: Add trace events to aeqe and ceqe. ANBZ: #45460 commit 07877f7a4ca8fef4a1ef5b8431fc714eb3a72ae9 openEuler. This commit adds the aeqe/ceqe trace events shown at the ubase layer. Therefore, there are two trace events of ubase_aeqe and ubase_ceqe defined to the ubase trace framework. 1.Define the ubase_aeqe and ubase_ceqe events, which are shown for aeqe/ceqe trace information. 2.Invoke the trace event interface prefixed with trace_ in the interface for sending and receiving ceqe/aeqe commands and processing ceqe/aeqe datas. Signed-off-by: Yaoyao Tu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_eq.c | 5 +++ drivers/ub/ubase/ubase_trace.h | 64 ++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/drivers/ub/ubase/ubase_eq.c b/drivers/ub/ubase/ubase_eq.c index e82de4fa9069..3fe929654592 100644 --- a/drivers/ub/ubase/ubase_eq.c +++ b/drivers/ub/ubase/ubase_eq.c @@ -10,6 +10,7 @@ #include "ubase_cmd.h" #include "ubase_dev.h" #include "ubase_mailbox.h" +#include "ubase_trace.h" #include "ubase_eq.h" enum ubase_eq_type { @@ -85,6 +86,8 @@ static irqreturn_t ubase_ceq_int_handler(int irq, void *data) jfcn = u32_get_bits(ceqe->comp, UBASE_CEQE_COMP_CQN_M); + trace_ubase_ceqe(udev->dev, jfcn, &ceq->eq); + ubase_comp_handler(udev, jfcn); ++ceq->eq.cons_index; @@ -267,6 +270,8 @@ static int ubase_async_event_handler(struct ubase_dev *udev) while (aeqe) { dma_rmb(); + trace_ubase_aeqe(udev->dev, aeqe, eq); + ubase_dbg(udev, "event_type = 0x%x, sub_type = 0x%x, owner = %u, seq_num = %u, cons_index = %u.\n", aeqe->event_type, aeqe->sub_type, aeqe->owner, diff --git a/drivers/ub/ubase/ubase_trace.h b/drivers/ub/ubase/ubase_trace.h index 3a4c27f272aa..a49902e6512f 100644 --- a/drivers/ub/ubase/ubase_trace.h +++ b/drivers/ub/ubase/ubase_trace.h @@ -95,6 +95,70 @@ TRACE_EVENT(ubase_crq, ) ); +TRACE_EVENT(ubase_aeqe, + TP_PROTO(struct device *dev, struct ubase_aeqe *aeqe, + struct ubase_eq *eq), + TP_ARGS(dev, aeqe, eq), + + TP_STRUCT__entry( + __field(u32, event_type) + __field(u32, sub_type) + __field(u32, owner) + __field(u32, ci) + __field(u32, queue_event_num) + __field(u64, cmd_out_param) + __field(u16, cmd_seq_num) + __field(u8, cmd_status) + __dynamic_array(char, devname, TRACE_DEV_NAME_MAX_LEN) + ), + + TP_fast_assign( + __entry->event_type = aeqe->event_type; + __entry->sub_type = aeqe->sub_type; + __entry->owner = aeqe->owner; + __entry->ci = eq->cons_index; + __entry->queue_event_num = aeqe->event.queue_event.num; + __entry->cmd_out_param = aeqe->event.cmd.out_param; + __entry->cmd_seq_num = aeqe->event.cmd.seq_num; + __entry->cmd_status = aeqe->event.cmd.status; + if (dev) { + snprintf(__get_str(devname), TRACE_DEV_NAME_MAX_LEN, + "%s %s", dev_driver_string(dev), dev_name(dev)); + } + ), + + TP_printk( + "%s %u-%u-%u-%u-%u-%llu-%u-%u", __get_str(devname), + __entry->event_type, __entry->sub_type, __entry->owner, + __entry->ci, __entry->queue_event_num, __entry->cmd_out_param, + __entry->cmd_seq_num, __entry->cmd_status + ) +); + +TRACE_EVENT(ubase_ceqe, + TP_PROTO(struct device *dev, u32 jfcn, struct ubase_eq *eq), + TP_ARGS(dev, jfcn, eq), + + TP_STRUCT__entry( + __field(u32, jfcn) + __field(u32, ci) + __dynamic_array(char, devname, TRACE_DEV_NAME_MAX_LEN) + ), + + TP_fast_assign( + __entry->jfcn = jfcn; + __entry->ci = eq->cons_index; + if (dev) { + snprintf(__get_str(devname), TRACE_DEV_NAME_MAX_LEN, + "%s %s", dev_driver_string(dev), dev_name(dev)); + } + ), + + TP_printk( + "%s %u-%u", __get_str(devname), __entry->jfcn, __entry->ci + ) +); + #endif /* __UBASE_TRACE_H__ */ /* This must be outside ifdef __UBASE_TRACE_H__ */ -- Gitee From ea0bc422fca2dbdf02fb17b294707d46bd2b0201 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Wed, 17 Sep 2025 19:41:03 +0800 Subject: [PATCH 158/214] ub: ubase: Support for virtual register and unregister. ANBZ: #45460 commit fc9723c2bfb10d77e3c4b49b1626ddb1750bd3da openEuler. This patch adds registration and deregistration interfaces for virtual device handlers to the ubase driver: 1. Core Features: - Added the ubase_virt_register() function: Allows auxiliary devices to register virtual device handlers. - Added the ubase_virt_unregister() function: Provides a mechanism for deregistering virtual device handlers. 2. Implementation Characteristics: - Utilizes a mutex (virt_lock) to ensure thread-safe operations. - Supports passing bus UE ID and enable status parameters to the handlers. - Implements null pointer safety checks to enhance code robustness. This implementation provides a standard registration and deregistration mechanism for virtual devices, enhancing the interaction capabilities between driver modules and laying the foundation for future virtual device feature expansions. Signed-off-by: Yixi Shen Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_dev.c | 52 +++++++++++++++++++++++++++++++ drivers/ub/ubase/ubase_dev.h | 2 ++ drivers/ub/ubase/ubase_ubus.c | 34 ++++++++++++++++++++ include/ub/ubase/ubase_comm_dev.h | 5 +++ 4 files changed, 93 insertions(+) diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index c0ee8be61660..cc7d26a506ce 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -639,6 +639,39 @@ struct ubase_adev_caps *ubase_get_cdma_caps(struct auxiliary_device *adev) } EXPORT_SYMBOL(ubase_get_cdma_caps); +void ubase_virt_register(struct auxiliary_device *adev, + void (*virt_handler)(struct auxiliary_device *adev, + u16 bus_ue_id, bool is_en)) +{ + struct ubase_adev *uadev; + + if (!adev || !virt_handler) + return; + + uadev = container_of(adev, struct ubase_adev, adev); + + mutex_lock(&uadev->virt_lock); + if (!uadev->virt_handler) + uadev->virt_handler = virt_handler; + mutex_unlock(&uadev->virt_lock); +} +EXPORT_SYMBOL(ubase_virt_register); + +void ubase_virt_unregister(struct auxiliary_device *adev) +{ + struct ubase_adev *uadev; + + if (!adev) + return; + + uadev = container_of(adev, struct ubase_adev, adev); + + mutex_lock(&uadev->virt_lock); + uadev->virt_handler = NULL; + mutex_unlock(&uadev->virt_lock); +} +EXPORT_SYMBOL(ubase_virt_unregister); + struct ubase_adev_caps *ubase_get_unic_caps(struct auxiliary_device *adev) { struct ubase_dev *udev; @@ -652,6 +685,25 @@ struct ubase_adev_caps *ubase_get_unic_caps(struct auxiliary_device *adev) } EXPORT_SYMBOL(ubase_get_unic_caps); +void ubase_virt_handler(struct ubase_dev *udev, u16 bus_ue_id, bool is_en) +{ + struct ubase_adev *uadev; + int i; + + mutex_lock(&udev->priv.uadev_lock); + for (i = 0; i < UBASE_DRV_MAX; i++) { + uadev = udev->priv.uadev[i]; + if (!uadev) + continue; + + mutex_lock(&uadev->virt_lock); + if (uadev->virt_handler) + uadev->virt_handler(&uadev->adev, bus_ue_id, is_en); + mutex_unlock(&uadev->virt_lock); + } + mutex_unlock(&udev->priv.uadev_lock); +} + bool ubase_dbg_default(void) { return ubase_debug; diff --git a/drivers/ub/ubase/ubase_dev.h b/drivers/ub/ubase/ubase_dev.h index 7acf89644c6d..3f7bc9e02e94 100644 --- a/drivers/ub/ubase/ubase_dev.h +++ b/drivers/ub/ubase/ubase_dev.h @@ -315,4 +315,6 @@ void ubase_port_up(struct ubase_dev *udev); int ubase_dev_init(struct ubase_dev *udev); void ubase_dev_uninit(struct ubase_dev *udev); +void ubase_virt_handler(struct ubase_dev *udev, u16 bus_ue_id, bool is_en); + #endif diff --git a/drivers/ub/ubase/ubase_ubus.c b/drivers/ub/ubase/ubase_ubus.c index 7b05707955e0..c5b64805e90d 100644 --- a/drivers/ub/ubase/ubase_ubus.c +++ b/drivers/ub/ubase/ubase_ubus.c @@ -330,12 +330,46 @@ int ubase_ubus_irq_vector(struct device *dev, u32 idx) return ub_irq_vector(ue, idx); } +static int ubase_ubus_virt_configure(struct ub_entity *ue, int bus_ue_id, bool is_en) +{ + struct ubase_dev *udev = dev_get_drvdata(&ue->dev); + int ret; + + /* + * The ubus framework have ensure that only mue can come + * here, so we not need to check is this a mue again. + */ + ubase_info(udev, "ubase virt configure set idx = %d en = %d.\n", + bus_ue_id, is_en); + + if (!is_en) + ret = ub_disable_ue(ue, bus_ue_id); + else + ret = ub_enable_ue(ue, bus_ue_id); + + return ret; +} + +static int ubase_ubus_virt_notify(struct ub_entity *ue, int bus_ue_id, bool is_en) +{ + struct ubase_dev *udev = dev_get_drvdata(&ue->dev); + + ubase_info(udev, "ubase virt notify, ue id = %d, en = %d.\n", + bus_ue_id, is_en); + + ubase_virt_handler(udev, (u16)bus_ue_id, is_en); + + return 0; +} + static struct ub_driver ubase_ubus_driver = { .name = ubase_ubus_driver_name, .id_table = ubase_ubus_tbl, .probe = ubase_ubus_probe, .remove = ubase_ubus_remove, .shutdown = ubase_ubus_shutdown, + .virt_configure = ubase_ubus_virt_configure, + .virt_notify = ubase_ubus_virt_notify, .driver = {}, }; diff --git a/include/ub/ubase/ubase_comm_dev.h b/include/ub/ubase/ubase_comm_dev.h index d8bcb9349b42..9d5afb9e4ebc 100644 --- a/include/ub/ubase/ubase_comm_dev.h +++ b/include/ub/ubase/ubase_comm_dev.h @@ -223,6 +223,11 @@ struct ubase_adev_caps *ubase_get_unic_caps(struct auxiliary_device *adev); struct ubase_adev_caps *ubase_get_udma_caps(struct auxiliary_device *adev); struct ubase_adev_caps *ubase_get_cdma_caps(struct auxiliary_device *adev); +void ubase_virt_register(struct auxiliary_device *adev, + void (*virt_handler)(struct auxiliary_device *adev, + u16 bus_ue_id, bool is_en)); +void ubase_virt_unregister(struct auxiliary_device *adev); + int ubase_get_bus_eid(struct auxiliary_device *adev, struct ubase_bus_eid *eid); #endif -- Gitee From 14fb340a422c6b5089e0128a73eb84e8c49cefcd Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Wed, 17 Sep 2025 11:38:57 +0800 Subject: [PATCH 159/214] ub: ubase: adds the function that query oor caps ANBZ: #45460 commit c376af847e7c36679d6b170a618ec16021e82643 openEuler. This patch adds hardware OOR capability query functionality to the ubase driver: 1. Core Functionality: - Added the ubase_query_hw_oor_caps() function, integrated into the device initialization process. - Query hardware OOR capabilities using the UBASE_OPC_QUERY_OOR_CAPS command code. 2. Capability Parameter Acquisition: - Supports detection of OOR enablement status. - Retrieves reordering queue capabilities and configuration parameters. - Reads key parameters such as dynamic acknowledgment timeout and data size in transit. 3. Data Structures: - Added the ubase_query_oor_resp response structure. - Stores query results in the device capability structure for use by other modules. This implementation provides the driver with a detection mechanism for hardware OOR capabilities, laying the groundwork for subsequent out-of-order transmission functionalities. Signed-off-by: Yaoyao Tu Signed-off-by: Fengyan Mu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_dev.c | 2 +- drivers/ub/ubase/ubase_hw.c | 28 ++++++++++++++++++++++++++++ drivers/ub/ubase/ubase_hw.h | 11 +++++++++++ include/ub/ubase/ubase_comm_cmd.h | 1 + 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index cc7d26a506ce..269662b6ab1f 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -457,7 +457,7 @@ static const struct ubase_init_function ubase_init_func_map[] = { }, { "query hw oor caps", UBASE_SUP_NO_PMU, 0, - NULL, NULL + ubase_query_hw_oor_caps, NULL }, { "init irq table", UBASE_SUP_NO_PMU, 1, diff --git a/drivers/ub/ubase/ubase_hw.c b/drivers/ub/ubase/ubase_hw.c index 863f909a435b..117859341946 100644 --- a/drivers/ub/ubase/ubase_hw.c +++ b/drivers/ub/ubase/ubase_hw.c @@ -576,6 +576,34 @@ static void ubase_uninit_ta_tp_ext_buf(struct ubase_dev *udev) } } +int ubase_query_hw_oor_caps(struct ubase_dev *udev) +{ + struct ubase_caps *dev_caps = &udev->caps.dev_caps; + struct ubase_query_oor_resp resp = {0}; + struct ubase_cmd_buf in, out; + int ret; + + ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_OOR_CAPS, true, 0, NULL); + ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_OOR_CAPS, true, + sizeof(resp), &resp); + + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret) { + ubase_err(udev, + "failed to query hw oor caps, ret = %d.\n", ret); + return ret; + } + + dev_caps->oor_en = resp.oor_en; + dev_caps->reorder_queue_en = resp.reorder_cq_buffer_en; + dev_caps->reorder_cap = resp.reorder_cap; + dev_caps->reorder_queue_shift = resp.reorder_cq_shift; + dev_caps->on_flight_size = le32_to_cpu(resp.on_flight_size); + dev_caps->at_times = resp.dynamic_ack_timeout; + + return 0; +} + int ubase_query_controller_info(struct ubase_dev *udev) { struct ubase_caps *dev_caps = &udev->caps.dev_caps; diff --git a/drivers/ub/ubase/ubase_hw.h b/drivers/ub/ubase/ubase_hw.h index dff37099b719..99c3f85ad2f7 100644 --- a/drivers/ub/ubase/ubase_hw.h +++ b/drivers/ub/ubase/ubase_hw.h @@ -113,6 +113,16 @@ struct ubase_res_cmd_resp { __le32 ccc_max_cnt; }; +struct ubase_query_oor_resp { + u8 oor_en; + u8 reorder_cq_buffer_en; + u8 reorder_cap; + u8 reorder_cq_shift; + __le32 on_flight_size; + u8 dynamic_ack_timeout; + u8 rsvd0[15]; +}; + struct ubase_query_controller_info_resp { __le32 rsvd0[2]; u8 packet_pattern_mode : 1; @@ -149,5 +159,6 @@ void ubase_hw_uninit(struct ubase_dev *udev); int ubase_query_dev_res(struct ubase_dev *udev); int ubase_query_chip_info(struct ubase_dev *udev); int ubase_query_controller_info(struct ubase_dev *udev); +int ubase_query_hw_oor_caps(struct ubase_dev *udev); #endif diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index fd728af9e11a..f915b53c94d5 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -40,6 +40,7 @@ enum ubase_opcode_type { /* TA commands */ UBASE_OPC_TA_EXTDB_VA_CONFIG = 0x4000, UBASE_OPC_TA_TIMER_VA_CONFIG = 0x4001, + UBASE_OPC_QUERY_OOR_CAPS = 0x4200, /* PHY commands */ UBASE_OPC_QUERY_CHIP_INFO = 0x6201, -- Gitee From cbacd34e588f7ba708fae94197ed528cf3f00fc4 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Wed, 17 Sep 2025 21:29:30 +0800 Subject: [PATCH 160/214] ub: ubase: Supports for ctrl queue management. ANBZ: #45460 commit d7ce08663cc5bc33993af7b046eb09e98640c404 openEuler. This patch adds comprehensive control queue management functionality to the ubase driver, primarily aimed at facilitating efficient command interaction between the driver and MAMI (Management and Maintenance Interface). The control queue supports bidirectional communication, providing a reliable mechanism for command transmission and response. Key features implemented: 1. Queue Initialization and Management: - Creation, initialization, and destruction of control queues (ubase_ctrlq_init/ubase_ctrlq_uninit) - Mapping and management of send queues (CSQ) and completion queues (CRQ) - Queue depth detection and space management 2. Message Transmission Mechanism: - Support for synchronous and asynchronous message sending (ubase_ctrlq_send_msg) - Implementation of message sequence number management and timeout retry mechanisms - Provision of message integrity checks and verification 3. Interrupt Handling: - Completion queue (CRQ) interrupt handling (ubase_ctrlq_crq_handler) - Support for the distribution and processing of self-messages and other device messages - Implementation of service task scheduling and cleanup mechanisms 4. Event Handling Framework: - Provision of control queue event registration interfaces (ubase_ctrlq_register_crq_event) - Support for event callback mechanisms based on service types and operation codes - Dynamic registration and deregistration of event handlers 5. Reliability and Error Handling: - Comprehensive timeout control mechanisms (configurable timeout durations) - Message retransmission and error recovery mechanisms - Resource cleanup and state management This implementation provides the ubase driver with a core communication mechanism for interacting with MAMI, laying the foundation for subsequent advanced management functions and ensuring reliable and efficient command transmission between the driver and firmware. Signed-off-by: Zesong Li Signed-off-by: Xiongchuan Zhou Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/Makefile | 2 +- drivers/ub/ubase/ubase.h | 4 + drivers/ub/ubase/ubase_cmd.h | 21 + drivers/ub/ubase/ubase_ctrlq.c | 1030 +++++++++++++++++++++++++++ drivers/ub/ubase/ubase_ctrlq.h | 46 ++ drivers/ub/ubase/ubase_dev.c | 124 +++- drivers/ub/ubase/ubase_dev.h | 79 ++ drivers/ub/ubase/ubase_eq.c | 24 +- drivers/ub/ubase/ubase_eq.h | 5 + drivers/ub/ubase/ubase_trace.h | 43 ++ include/ub/ubase/ubase_comm_cmd.h | 1 + include/ub/ubase/ubase_comm_ctrlq.h | 68 ++ include/ub/ubase/ubase_comm_dev.h | 1 + 13 files changed, 1445 insertions(+), 3 deletions(-) create mode 100644 drivers/ub/ubase/ubase_ctrlq.c create mode 100644 drivers/ub/ubase/ubase_ctrlq.h create mode 100644 include/ub/ubase/ubase_comm_ctrlq.h diff --git a/drivers/ub/ubase/Makefile b/drivers/ub/ubase/Makefile index 64169ef5d796..7c21b558779c 100644 --- a/drivers/ub/ubase/Makefile +++ b/drivers/ub/ubase/Makefile @@ -9,7 +9,7 @@ ccflags-y += -I$(srctree)/drivers/ub/ubase/debugfs MODULE_NAME := ubase -UBASE_OBJS := ubase_main.o ubase_dev.o ubase_hw.o ubase_cmd.o \ +UBASE_OBJS := ubase_main.o ubase_dev.o ubase_hw.o ubase_cmd.o ubase_ctrlq.o \ debugfs/ubase_debugfs.o ubase_eq.o ubase_mailbox.o ubase_ubus.o \ debugfs/ubase_ctx_debugfs.o diff --git a/drivers/ub/ubase/ubase.h b/drivers/ub/ubase/ubase.h index dd684141890c..cfe047ce69c4 100644 --- a/drivers/ub/ubase/ubase.h +++ b/drivers/ub/ubase/ubase.h @@ -16,6 +16,8 @@ enum ubase_service_state { UBASE_STATE_CRQ_SERVICE_SCHED, UBASE_STATE_CRQ_HANDLING, + UBASE_STATE_CTRLQ_SERVICE_SCHED, + UBASE_STATE_CTRLQ_HANDLING, }; struct ubase_delay_work { @@ -64,4 +66,6 @@ static inline u32 ubase_read_reg(u8 __iomem *base, u32 reg) #define ubase_read_dev(a, reg) \ ubase_read_reg((a)->io_base.addr, reg) +#define ubase_addr_gen(addr_h, addr_l) ((u64)(addr_h) << 32 | (addr_l)) + #endif diff --git a/drivers/ub/ubase/ubase_cmd.h b/drivers/ub/ubase/ubase_cmd.h index dc65f83e5d82..3c230cb11ac6 100644 --- a/drivers/ub/ubase/ubase_cmd.h +++ b/drivers/ub/ubase/ubase_cmd.h @@ -49,6 +49,27 @@ struct ubase_query_version_cmd { __le32 caps[UBASE_CAP_LEN]; }; +enum ubase_ue2ue_sub_cmd { + UBASE_UE2UE_CTRLQ_MSG = 3, +}; + +struct ubase_ue2ue_common_head { + __le16 bus_ue_id; + __le16 mbx_ue_id; + u16 sub_cmd; + u16 status; +}; + +struct ubase_ue2ue_ctrlq_head { + struct ubase_ue2ue_common_head head; + u16 seq; + u16 in_size; + u16 out_size; + u8 need_resp : 1; + u8 is_resp : 1; + u8 rsv : 6; +}; + struct ubase_query_ueid_cmd { __le32 ueid[UBASE_BUS_EID_LEN]; u32 rsv[2]; diff --git a/drivers/ub/ubase/ubase_ctrlq.c b/drivers/ub/ubase/ubase_ctrlq.c new file mode 100644 index 000000000000..bdc2c5659b80 --- /dev/null +++ b/drivers/ub/ubase/ubase_ctrlq.c @@ -0,0 +1,1030 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include + +#include "ubase_dev.h" +#include "ubase_trace.h" +#include "ubase_cmd.h" +#include "ubase_ctrlq.h" + +static inline void ubase_ctrlq_crq_table_init(struct ubase_dev *udev) +{ + struct ubase_ctrlq_crq_table *crq_tab = &udev->ctrlq.crq_table; + + mutex_init(&crq_tab->lock); + INIT_LIST_HEAD(&crq_tab->crq_nbs.list); +} + +static inline void ubase_ctrlq_crq_table_uninit(struct ubase_dev *udev) +{ + struct ubase_ctrlq_crq_table *crq_tab = &udev->ctrlq.crq_table; + + mutex_destroy(&crq_tab->lock); +} + +static inline u16 ubase_ctrlq_msg_queue_depth(struct ubase_dev *udev) +{ + return udev->ctrlq.csq.depth << 1; +} + +static inline u16 ubase_ctrlq_max_seq(struct ubase_dev *udev) +{ + return ubase_ctrlq_msg_queue_depth(udev) - 1; +} + +static int ubase_ctrlq_msg_queue_init(struct ubase_dev *udev) +{ + u16 msg_ctx_size = sizeof(struct ubase_ctrlq_msg_ctx); + u16 depth = ubase_ctrlq_msg_queue_depth(udev); + struct ubase_ctrlq_msg_ctx *ctx; + u16 i; + + udev->ctrlq.msg_queue = kzalloc(depth * msg_ctx_size, GFP_KERNEL); + if (!udev->ctrlq.msg_queue) { + ubase_err(udev, "failed to alloc ctrlq msg queue.\n"); + return -ENOMEM; + } + + for (i = 0; i < depth; i++) { + ctx = &udev->ctrlq.msg_queue[i]; + init_completion(&ctx->done); + } + + return 0; +} + +static void ubase_ctrlq_msg_queue_uninit(struct ubase_dev *udev) +{ + kfree(udev->ctrlq.msg_queue); + udev->ctrlq.msg_queue = NULL; +} + +static int ubase_ctrlq_map_queue(struct ubase_dev *udev) +{ + struct ubase_ctrlq_ring *csq = &udev->ctrlq.csq; + struct ubase_ctrlq_ring *crq = &udev->ctrlq.crq; + u32 addr_h, addr_l; + u64 queue_addr; + size_t size; + + if (!ubase_dev_ctrlq_supported(udev)) + return 0; + + addr_h = ubase_read_dev(&udev->hw, UBASE_CTRLQ_CSQ_BASEADDR_H_REG); + addr_l = ubase_read_dev(&udev->hw, UBASE_CTRLQ_CSQ_BASEADDR_L_REG); + queue_addr = ubase_addr_gen(addr_h, addr_l); + size = csq->depth * UBASE_CTRLQ_BB_LEN; + csq->base_addr = devm_ioremap(udev->dev, queue_addr, size); + if (!csq->base_addr) { + ubase_err(udev, "failed to map ctrlq csq base addr, size = %lu.\n", + size); + return -ENOMEM; + } + + addr_h = ubase_read_dev(&udev->hw, UBASE_CTRLQ_CRQ_BASEADDR_H_REG); + addr_l = ubase_read_dev(&udev->hw, UBASE_CTRLQ_CRQ_BASEADDR_L_REG); + queue_addr = ubase_addr_gen(addr_h, addr_l); + size = crq->depth * UBASE_CTRLQ_BB_LEN; + crq->base_addr = devm_ioremap(udev->dev, queue_addr, size); + if (!crq->base_addr) { + ubase_err(udev, "failed to map ctrlq crq base addr, size = %lu.\n", + size); + goto err_map_crq; + } + + return 0; + +err_map_crq: + devm_iounmap(udev->dev, csq->base_addr); + csq->base_addr = NULL; + return -ENOMEM; +} + +static void ubase_ctrlq_unmap_queue(struct ubase_dev *udev) +{ + struct ubase_ctrlq_ring *csq = &udev->ctrlq.csq; + struct ubase_ctrlq_ring *crq = &udev->ctrlq.crq; + + if (!ubase_dev_ctrlq_supported(udev)) + return; + + if (csq->base_addr) { + devm_iounmap(udev->dev, csq->base_addr); + csq->base_addr = NULL; + } + + if (crq->base_addr) { + devm_iounmap(udev->dev, crq->base_addr); + crq->base_addr = NULL; + } +} + +static void ubase_ctrlq_queue_pi_ci_init(struct ubase_dev *udev) +{ + struct ubase_ctrlq_ring *csq = &udev->ctrlq.csq; + struct ubase_ctrlq_ring *crq = &udev->ctrlq.crq; + u16 crq_hw_pi; + + if (!ubase_dev_ctrlq_supported(udev)) + return; + + csq->pi = ubase_read_dev(&udev->hw, UBASE_CTRLQ_CSQ_TAIL_REG); + csq->ci = ubase_read_dev(&udev->hw, UBASE_CTRLQ_CSQ_HEAD_REG); + + crq_hw_pi = ubase_read_dev(&udev->hw, UBASE_CTRLQ_CRQ_TAIL_REG); + ubase_write_dev(&udev->hw, UBASE_CTRLQ_CRQ_HEAD_REG, crq_hw_pi); + crq->pi = crq_hw_pi; + crq->ci = crq_hw_pi; +} + +static int ubase_query_ctrlq_queue_info(struct ubase_dev *udev) +{ +#define UBASE_CTRLQ_QUEUE_DEFAULT 2048 + + struct ubase_ctrlq_ring *csq = &udev->ctrlq.csq; + struct ubase_ctrlq_ring *crq = &udev->ctrlq.crq; + + csq->depth = UBASE_CTRLQ_QUEUE_DEFAULT; + crq->depth = UBASE_CTRLQ_QUEUE_DEFAULT; + + return 0; +} + +static int ubase_ctrlq_get_queue_depth(struct ubase_dev *udev) +{ +#define ubase_reg_val_to_depth(a) ((a) << 3) + + struct ubase_ctrlq_ring *csq = &udev->ctrlq.csq; + struct ubase_ctrlq_ring *crq = &udev->ctrlq.crq; + u16 reg_val; + + if (!ubase_dev_ctrlq_supported(udev)) + return ubase_query_ctrlq_queue_info(udev); + + reg_val = (u16)ubase_read_dev(&udev->hw, UBASE_CTRLQ_CSQ_DEPTH_REG); + csq->depth = ubase_reg_val_to_depth(reg_val); + if (!csq->depth) { + ubase_err(udev, "the csq depth is 0.\n"); + return -EINVAL; + } + + reg_val = (u16)ubase_read_dev(&udev->hw, UBASE_CTRLQ_CRQ_DEPTH_REG); + crq->depth = ubase_reg_val_to_depth(reg_val); + if (!crq->depth) { + ubase_err(udev, "the crq depth is 0.\n"); + return -EINVAL; + } + + return 0; +} + +static int ubase_ctrlq_queue_init(struct ubase_dev *udev) +{ +#define CTRLQ_TX_TIMEOUT 3000 + + struct ubase_ctrlq_ring *csq = &udev->ctrlq.csq; + struct ubase_ctrlq_ring *crq = &udev->ctrlq.crq; + int ret; + + spin_lock_init(&csq->lock); + spin_lock_init(&crq->lock); + + ret = ubase_ctrlq_get_queue_depth(udev); + if (ret) { + ubase_err(udev, "failed to get queue depth, ret = %d.\n", + ret); + return ret; + } + + csq->tx_timeout = CTRLQ_TX_TIMEOUT; + ubase_ctrlq_queue_pi_ci_init(udev); + + ret = ubase_ctrlq_map_queue(udev); + if (ret) + ubase_err(udev, "failed to map ctrlq queue, ret = %d.\n", + ret); + + return ret; +} + +static void ubase_ctrlq_queue_uninit(struct ubase_dev *udev) +{ + ubase_ctrlq_unmap_queue(udev); +} + +int ubase_ctrlq_init(struct ubase_dev *udev) +{ + int ret; + + if (test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) { + ubase_ctrlq_queue_pi_ci_init(udev); + goto success; + } + + ret = ubase_ctrlq_queue_init(udev); + if (ret) + return ret; + + ret = ubase_ctrlq_msg_queue_init(udev); + if (ret) + goto err_msg_queue_init; + + udev->ctrlq.csq_next_seq = 1; + atomic_set(&udev->ctrlq.req_cnt, 0); + ubase_ctrlq_crq_table_init(udev); + +success: + set_bit(UBASE_CTRLQ_STATE_ENABLE, &udev->ctrlq.state); + return 0; + +err_msg_queue_init: + ubase_ctrlq_queue_uninit(udev); + return ret; +} + +static void ubase_ctrlq_clean_msg_queue(struct ubase_dev *udev) +{ + struct ubase_ctrlq_ring *csq = &udev->ctrlq.csq; + u16 depth = ubase_ctrlq_msg_queue_depth(udev); + struct ubase_ctrlq_msg_ctx *ctx; + u16 i; + + spin_lock_bh(&csq->lock); + for (i = 0; i < depth; i++) { + ctx = &udev->ctrlq.msg_queue[i]; + ctx->valid = 0; + } + spin_unlock_bh(&csq->lock); +} + +static void ubase_ctrlq_clean_pending_msgs(struct ubase_dev *udev) +{ +#define UBASE_CTRLQ_CLEAN_WAIT_TIME 5 + + struct ubase_ctrlq_ring *csq = &udev->ctrlq.csq; + u16 depth = ubase_ctrlq_msg_queue_depth(udev); + struct ubase_ctrlq_msg_ctx *ctx; + u16 i; + + spin_lock_bh(&csq->lock); + for (i = 1; i < depth; i++) { + ctx = &udev->ctrlq.msg_queue[i]; + if (!completion_done(&ctx->done)) + complete(&ctx->done); + } + spin_unlock_bh(&csq->lock); + + while (atomic_read(&udev->ctrlq.req_cnt)) + msleep(UBASE_CTRLQ_CLEAN_WAIT_TIME); +} + +void ubase_ctrlq_disable(struct ubase_dev *udev) +{ +#define UBASE_CTRLQ_CLEAR_WAIT_TIME 5 + + clear_bit(UBASE_CTRLQ_STATE_ENABLE, &udev->ctrlq.state); + + /* wait to ensure that the crq completes the possible left + * over commands. + */ + if (!test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) { + while (test_bit(UBASE_STATE_CTRLQ_HANDLING, + &udev->service_task.state)) + msleep(UBASE_CTRLQ_CLEAR_WAIT_TIME); + } + + ubase_ctrlq_clean_pending_msgs(udev); +} + +void ubase_ctrlq_uninit(struct ubase_dev *udev) +{ + if (udev->reset_stage != UBASE_RESET_STAGE_UNINIT) + ubase_ctrlq_disable(udev); + + if (!test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) { + ubase_ctrlq_crq_table_uninit(udev); + ubase_ctrlq_msg_queue_uninit(udev); + ubase_ctrlq_queue_uninit(udev); + } else { + ubase_ctrlq_clean_msg_queue(udev); + } +} + +static u16 ubase_ctrlq_calc_bb_num(u16 in_size) +{ + u16 data_size = in_size > UBASE_CTRLQ_DATA_LEN ? + in_size - UBASE_CTRLQ_DATA_LEN : 0; + + return (DIV_ROUND_UP(data_size, UBASE_CTRLQ_BB_LEN) + 1); +} + +static u16 ubase_ctrlq_remain_space(struct ubase_dev *udev) +{ + struct ubase_ctrlq_ring *csq = &udev->ctrlq.csq; + u16 used; + + used = (csq->pi - csq->ci + csq->depth) % csq->depth; + + return csq->depth - used - 1; +} + +static void ubase_ctrlq_fill_first_bb(struct ubase_dev *udev, + struct ubase_ctrlq_base_block *head, + struct ubase_ctrlq_msg *msg, + struct ubase_ctrlq_ue_info *ue_info) +{ + struct ub_entity *ue = to_ub_entity(udev->dev); + + head->service_ver = msg->service_ver; + head->service_type = msg->service_type; + head->opcode = msg->opcode; + head->mbx_ue_id = ue_info ? ue_info->mbx_ue_id : 0; + head->ret = msg->is_resp ? msg->resp_ret : 0; + head->bus_ue_id = cpu_to_le16(ue_info ? + ue_info->bus_ue_id : + ue->entity_idx); + memcpy(head->data, msg->in, min(msg->in_size, UBASE_CTRLQ_DATA_LEN)); +} + +static inline void ubase_ctrlq_csq_report_irq(struct ubase_dev *udev) +{ +#define UBASE_CTRLQ_CSQ_IRQ_EN BIT(16) +#define UBASE_CTRLQ_CSQ_IRQ_VEC_IDX 0 + + u32 val = UBASE_CTRLQ_CSQ_IRQ_EN | UBASE_CTRLQ_CSQ_IRQ_VEC_IDX; + + ubase_write_reg(udev->hw.rs0_base.addr, 0, val); +} + +static int ubase_ctrlq_send_to_cmdq(struct ubase_dev *udev, + struct ubase_ctrlq_base_block *head, + struct ubase_ctrlq_msg *msg, u8 num) +{ + u32 req_len = msg->in_size + sizeof(struct ubase_ue2ue_ctrlq_head) + + UBASE_CTRLQ_HDR_LEN; + struct ubase_ue2ue_ctrlq_head ue2ue_head = {0}; + u16 seq = le16_to_cpu(head->seq); + struct ubase_cmd_buf in; + void *req; + int ret; + + req = kzalloc(req_len, GFP_ATOMIC); + if (!req) + return -ENOMEM; + + ue2ue_head.head.sub_cmd = UBASE_UE2UE_CTRLQ_MSG; + ue2ue_head.seq = seq; + ue2ue_head.in_size = msg->in_size; + ue2ue_head.out_size = msg->out_size; + ue2ue_head.need_resp = msg->need_resp; + ue2ue_head.is_resp = msg->is_resp; + memcpy(req, &ue2ue_head, sizeof(ue2ue_head)); + memcpy((u8 *)req + sizeof(ue2ue_head), head, UBASE_CTRLQ_HDR_LEN); + memcpy((u8 *)req + sizeof(ue2ue_head) + UBASE_CTRLQ_HDR_LEN, msg->in, + msg->in_size); + + __ubase_fill_inout_buf(&in, UBASE_OPC_UE2UE_UBASE, false, req_len, req); + ret = __ubase_cmd_send_in(udev, &in); + if (ret) + ubase_err(udev, + "failed to send ue2ue ctrlq msg, seq = %u, ret = %d.\n", + seq, ret); + + kfree(req); + return ret; +} + +static void ubase_ctrlq_send_to_csq(struct ubase_dev *udev, + struct ubase_ctrlq_base_block *head, + struct ubase_ctrlq_msg *msg, u8 num) +{ + struct ubase_ctrlq_ring *csq = &udev->ctrlq.csq; + u32 total_size = msg->in_size; + u32 size, offset = 0; + u8 cnt = 0; + u8 *addr; + + while (cnt < num) { + addr = csq->base_addr + csq->pi * UBASE_CTRLQ_BB_LEN; + if (cnt == 0) { + memcpy_toio(addr, head, sizeof(*head)); + trace_ubase_ctrlq_csq(udev->dev, num, csq->pi, csq->ci, + head, sizeof(*head)); + total_size -= UBASE_CTRLQ_DATA_LEN; + offset += UBASE_CTRLQ_DATA_LEN; + } else { + size = min_t(u32, total_size, UBASE_CTRLQ_BB_LEN); + memcpy_toio(addr, (u8 *)msg->in + offset, size); + trace_ubase_ctrlq_csq(udev->dev, num, csq->pi, csq->ci, + (u8 *)msg->in + offset, size); + total_size -= size; + offset += size; + } + csq->pi++; + if (csq->pi >= csq->depth) + csq->pi = 0; + + cnt++; + } + + ubase_write_dev(&udev->hw, UBASE_CTRLQ_CSQ_TAIL_REG, csq->pi); + ubase_ctrlq_csq_report_irq(udev); +} + +static int ubase_ctrlq_send_msg_to_sq(struct ubase_dev *udev, + struct ubase_ctrlq_base_block *head, + struct ubase_ctrlq_msg *msg, u8 num) +{ + if (ubase_dev_ctrlq_supported(udev)) { + ubase_ctrlq_send_to_csq(udev, head, msg, num); + return 0; + } + + return ubase_ctrlq_send_to_cmdq(udev, head, msg, num); +} + +static int ubase_ctrlq_wait_completed(struct ubase_dev *udev, u16 seq, + struct ubase_ctrlq_msg *msg) +{ + struct ubase_ctrlq_msg_ctx *ctx = &udev->ctrlq.msg_queue[seq]; + struct ubase_ctrlq_ring *csq = &udev->ctrlq.csq; + int ret; + + if (!wait_for_completion_timeout(&ctx->done, + msecs_to_jiffies(csq->tx_timeout))) { + ubase_err(udev, + "ctrlq wait resp timeout, seq = %u, opcode = 0x%x, service_type = 0x%x.\n", + seq, msg->opcode, msg->service_type); + return -ETIMEDOUT; + } + + ret = ctx->result; + if (ret) + ubase_err(udev, + "ctrlq recv failed resp for seq = %u, opcode = 0x%x, service_type = 0x%x, ret = %d.\n", + seq, msg->opcode, msg->service_type, ret); + + return -ret; +} + +static int ubase_ctrlq_alloc_seq(struct ubase_dev *udev, + struct ubase_ctrlq_msg *msg, u16 *seq) +{ + struct ubase_ctrlq_msg_ctx *ctx = udev->ctrlq.msg_queue; + u16 max_seq = ubase_ctrlq_max_seq(udev); + u16 next_seq = udev->ctrlq.csq_next_seq; + u32 i; + + for (i = next_seq; i <= max_seq; i++) { + if (!ctx[i].valid) + goto success; + } + + /* seq 0 is not used. */ + for (i = 1; i < next_seq; i++) { + if (!ctx[i].valid) + goto success; + } + + return -EBUSY; + +success: + *seq = i; + udev->ctrlq.csq_next_seq = i + 1; + + return 0; +} + +static void ubase_ctrlq_free_seq(struct ubase_dev *udev, u16 seq) +{ + struct ubase_ctrlq_msg_ctx *ctx = &udev->ctrlq.msg_queue[seq]; + struct ubase_ctrlq_ring *csq = &udev->ctrlq.csq; + + spin_lock_bh(&csq->lock); + ctx->valid = 0; + spin_unlock_bh(&csq->lock); +} + +static void ubase_ctrlq_addto_msg_queue(struct ubase_dev *udev, u16 seq, + struct ubase_ctrlq_msg *msg, + struct ubase_ctrlq_ue_info *ue_info) +{ + struct ubase_ctrlq_msg_ctx *ctx; + + if (!msg->need_resp) + return; + + ctx = &udev->ctrlq.msg_queue[seq]; + ctx->valid = 1; + ctx->is_sync = msg->need_resp && msg->out && msg->out_size ? 1 : 0; + ctx->result = ETIME; + ctx->dead_jiffies = jiffies + msecs_to_jiffies(UBASE_CTRLQ_DEAD_TIME); + if (ctx->is_sync) { + ctx->out = msg->out; + ctx->out_size = msg->out_size; + } + + if (ue_info) { + ctx->ue_seq = ue_info->seq; + ctx->bus_ue_id = ue_info->bus_ue_id; + } + reinit_completion(&ctx->done); +} + +static int ubase_ctrlq_msg_check(struct ubase_dev *udev, + struct ubase_ctrlq_msg *msg) +{ + if (!msg || !msg->in || !msg->in_size) { + ubase_err(udev, "ctrlq input buf is invalid.\n"); + return -EINVAL; + } + + if (msg->is_resp && !(msg->resp_seq & UBASE_CTRLQ_SEQ_MASK)) { + ubase_err(udev, "ctrlq input resp_seq(%u) is invalid.\n", + msg->resp_seq); + return -EINVAL; + } + + if (msg->in_size > UBASE_CTRLQ_MAX_DATA_SIZE) { + ubase_err(udev, + "requested ctrlq space(%u) exceeds the maximum(%u).\n", + msg->in_size, UBASE_CTRLQ_MAX_DATA_SIZE); + return -EINVAL; + } + + return 0; +} + +static int ubase_ctrlq_check_csq_enough(struct ubase_dev *udev, u16 num) +{ + struct ubase_ctrlq_ring *csq = &udev->ctrlq.csq; + + if (!ubase_dev_ctrlq_supported(udev)) + return 0; + + csq->ci = (u16)ubase_read_dev(&udev->hw, UBASE_CTRLQ_CSQ_HEAD_REG); + if (num > ubase_ctrlq_remain_space(udev)) { + ubase_warn(udev, + "no enough space in ctrlq, ci = %u, num = %u.\n", + csq->ci, num); + return -EBUSY; + } + + return 0; +} + +static int ubase_ctrlq_send_real(struct ubase_dev *udev, + struct ubase_ctrlq_msg *msg, + struct ubase_ctrlq_ue_info *ue_info) +{ + bool sync_req = msg->out && msg->out_size && msg->need_resp; + bool no_resp = !msg->is_resp && !msg->need_resp; + struct ubase_ctrlq_ring *csq = &udev->ctrlq.csq; + struct ubase_ctrlq_base_block head = {0}; + u16 seq, num; + int ret; + + num = ubase_ctrlq_calc_bb_num(msg->in_size); + + spin_lock_bh(&csq->lock); + + ret = ubase_ctrlq_check_csq_enough(udev, num); + if (ret) + goto unlock; + + if (!msg->is_resp) { + ret = ubase_ctrlq_alloc_seq(udev, msg, &seq); + if (ret) { + ubase_warn(udev, "no enough seq in ctrlq.\n"); + goto unlock; + } + } else { + seq = msg->resp_seq; + } + + ubase_ctrlq_addto_msg_queue(udev, seq, msg, ue_info); + + head.bb_num = num; + head.seq = cpu_to_le16(seq); + ubase_ctrlq_fill_first_bb(udev, &head, msg, ue_info); + ret = ubase_ctrlq_send_msg_to_sq(udev, &head, msg, num); + if (ret) { + spin_unlock_bh(&csq->lock); + goto free_seq; + } + + spin_unlock_bh(&csq->lock); + + if (sync_req) + ret = ubase_ctrlq_wait_completed(udev, seq, msg); + +free_seq: + /* Only the seqs in synchronous requests and no response requests need to be released. */ + /* The seqs are released in periodic tasks of asynchronous requests. */ + if (sync_req || no_resp) + ubase_ctrlq_free_seq(udev, seq); + return ret; +unlock: + spin_unlock_bh(&csq->lock); + return ret; +} + +int __ubase_ctrlq_send(struct ubase_dev *udev, struct ubase_ctrlq_msg *msg, + struct ubase_ctrlq_ue_info *ue_info) +{ +#define UBASE_CTRLQ_RETRY_TIMES 3 +#define UBASE_RETRY_INTERVAL 100 + + int ret, retry_cnt = 0; + + ret = ubase_ctrlq_msg_check(udev, msg); + if (ret) + return ret; + + while (retry_cnt++ <= UBASE_CTRLQ_RETRY_TIMES) { + if (!test_bit(UBASE_CTRLQ_STATE_ENABLE, &udev->ctrlq.state)) { + ubase_warn(udev, "ctrlq is disabled in csq.\n"); + return -EAGAIN; + } + + atomic_inc(&udev->ctrlq.req_cnt); + ret = ubase_ctrlq_send_real(udev, msg, ue_info); + atomic_dec(&udev->ctrlq.req_cnt); + if (ret == -ETIMEDOUT && retry_cnt <= UBASE_CTRLQ_RETRY_TIMES) { + ubase_info(udev, + "Ctrlq send msg retry, retry cnt = %d.\n", + retry_cnt); + msleep(UBASE_RETRY_INTERVAL); + } else { + break; + } + } + + return ret; +} + +int ubase_ctrlq_send_msg(struct auxiliary_device *aux_dev, + struct ubase_ctrlq_msg *msg) +{ + if (!aux_dev || !msg) + return -EINVAL; + + return __ubase_ctrlq_send(__ubase_get_udev_by_adev(aux_dev), msg, NULL); +} +EXPORT_SYMBOL(ubase_ctrlq_send_msg); + +static bool ubase_ctrlq_crq_is_empty(struct ubase_dev *udev, struct ubase_hw *hw) +{ + udev->ctrlq.crq.pi = ubase_read_dev(hw, UBASE_CTRLQ_CRQ_TAIL_REG); + + return udev->ctrlq.crq.pi == udev->ctrlq.crq.ci; +} + +static void ubase_ctrlq_update_crq_ci(struct ubase_dev *udev, u8 bb_num) +{ + struct ubase_ctrlq_ring *crq = &udev->ctrlq.crq; + + crq->ci = (crq->ci + bb_num) % crq->depth; + ubase_write_dev(&udev->hw, UBASE_CTRLQ_CRQ_HEAD_REG, crq->ci); +} + +static void ubase_ctrlq_read_msg_data(struct ubase_dev *udev, u8 num, u8 *msg) +{ + struct ubase_ctrlq_ring *crq = &udev->ctrlq.crq; + u16 pos = crq->ci; + u8 i; + + for (i = 0; i < num; i++) { + memcpy_fromio(msg + i * UBASE_CTRLQ_BB_LEN, + (u8 *)crq->base_addr + pos * UBASE_CTRLQ_BB_LEN, + UBASE_CTRLQ_BB_LEN); + trace_ubase_ctrlq_crq(udev->dev, num, crq->pi, crq->ci, + msg + i * UBASE_CTRLQ_BB_LEN, + UBASE_CTRLQ_BB_LEN); + pos = (pos + 1) % crq->depth; + } +} + +static void ubase_ctrlq_crq_event_callback(struct ubase_dev *udev, + struct ubase_ctrlq_base_block *head, + void *msg_data, u16 msg_data_len, + u16 seq) +{ + struct ubase_ctrlq_crq_table *crq_tab = &udev->ctrlq.crq_table; + struct ubase_ctrlq_crq_event_nbs *nbs; + + mutex_lock(&crq_tab->lock); + list_for_each_entry(nbs, &crq_tab->crq_nbs.list, list) { + if (nbs->crq_nb.service_type == head->service_type && + nbs->crq_nb.opcode == head->opcode) { + nbs->crq_nb.crq_handler(nbs->crq_nb.back, + head->service_ver, + msg_data, + msg_data_len, + seq); + break; + } + } + mutex_unlock(&crq_tab->lock); +} + +static void ubase_ctrlq_notify_completed(struct ubase_dev *udev, + struct ubase_ctrlq_base_block *head, + u16 seq, void *msg, u16 msg_len) +{ + struct ubase_ctrlq_msg_ctx *ctx; + + ctx = &udev->ctrlq.msg_queue[seq]; + ctx->result = head->ret; + memcpy(ctx->out, msg, min(msg_len, ctx->out_size)); + + complete(&ctx->done); +} + +static bool ubase_ctrlq_check_seq(struct ubase_dev *udev, u16 seq) +{ + bool is_pushed = !!(seq & UBASE_CTRLQ_SEQ_MASK); + u16 max_seq = ubase_ctrlq_max_seq(udev); + + return is_pushed || (seq && seq <= max_seq); +} + +void ubase_ctrlq_handle_crq_msg(struct ubase_dev *udev, + struct ubase_ctrlq_base_block *head, + u16 seq, void *msg_data, u16 data_len) +{ + bool is_pushed = !!(seq & UBASE_CTRLQ_SEQ_MASK); + struct ubase_ctrlq_ring *csq = &udev->ctrlq.csq; + struct ubase_ctrlq_msg_ctx *ctx; + + if (!is_pushed) { + spin_lock_bh(&csq->lock); + ctx = &udev->ctrlq.msg_queue[seq]; + if (!ctx->valid) { + ubase_warn(udev, + "seq is invalid, opcode = 0x%x, service_type = 0x%x, seq = %u.\n", + head->opcode, head->service_type, seq); + goto unlock; + } + if (ctx->is_sync) { + ubase_ctrlq_notify_completed(udev, head, seq, msg_data, + data_len); + goto unlock; + } + ctx->valid = 0; + spin_unlock_bh(&csq->lock); + } + + ubase_ctrlq_crq_event_callback(udev, head, msg_data, data_len, seq); + return; + +unlock: + spin_unlock_bh(&csq->lock); +} + +static void ubase_ctrlq_handle_self_msg(struct ubase_dev *udev, + struct ubase_ctrlq_base_block *head) +{ + u16 seq = le16_to_cpu(head->seq); + u16 msg_len, data_len; + void *msg; + + msg_len = head->bb_num * UBASE_CTRLQ_BB_LEN; + msg = kzalloc(msg_len, GFP_KERNEL); + if (!msg) { + ubase_err(udev, + "failed to alloc ctrlq crq msg data, opcode = 0x%x, service_type = 0x%x, seq = %u.\n", + head->opcode, head->service_type, seq); + return; + } + + ubase_ctrlq_read_msg_data(udev, head->bb_num, msg); + data_len = msg_len - UBASE_CTRLQ_HDR_LEN; + + ubase_ctrlq_handle_crq_msg(udev, head, seq, + (u8 *)msg + UBASE_CTRLQ_HDR_LEN, data_len); + + kfree(msg); +} + +static void ubase_ctrlq_handle_other_msg(struct ubase_dev *udev, + struct ubase_ctrlq_base_block *head) +{ + u16 resp_len, seq = le16_to_cpu(head->seq); + bool is_pushed = !!(seq & UBASE_CTRLQ_SEQ_MASK); + struct ubase_ctrlq_ring *csq = &udev->ctrlq.csq; + struct ubase_ue2ue_ctrlq_head *ue2ue_head; + struct ubase_ctrlq_msg_ctx ctx = {0}; + struct ubase_cmd_buf in; + void *resp, *msg; + int ret; + + if (!is_pushed) { + spin_lock_bh(&csq->lock); + ctx = udev->ctrlq.msg_queue[seq]; + if (!ctx.valid) { + ubase_warn(udev, "invalid seq = %u, opcode = 0x%x, service_type = 0x%x.\n", + seq, head->opcode, head->service_type); + spin_unlock_bh(&csq->lock); + return; + } + if (!ctx.is_sync) + udev->ctrlq.msg_queue[seq].valid = 0; + spin_unlock_bh(&csq->lock); + } + + resp_len = head->bb_num * UBASE_CTRLQ_BB_LEN + + sizeof(struct ubase_ue2ue_ctrlq_head); + resp = kzalloc(resp_len, GFP_KERNEL); + if (!resp) { + ubase_err(udev, "failed to alloc resp mem, seq = %u.\n", seq); + return; + } + + msg = (u8 *)resp + sizeof(struct ubase_ue2ue_ctrlq_head); + ubase_ctrlq_read_msg_data(udev, head->bb_num, msg); + + ue2ue_head = (struct ubase_ue2ue_ctrlq_head *)resp; + ue2ue_head->head.sub_cmd = UBASE_UE2UE_CTRLQ_MSG; + ue2ue_head->head.bus_ue_id = is_pushed ? head->bus_ue_id : + cpu_to_le16(ctx.bus_ue_id); + ue2ue_head->seq = is_pushed ? seq : ctx.ue_seq; + __ubase_fill_inout_buf(&in, UBASE_OPC_UE2UE_UBASE, false, resp_len, resp); + ret = __ubase_cmd_send_in(udev, &in); + if (ret) + ubase_warn(udev, + "failed to send ue ctrlq msg, opc = 0x%x, service_type = 0x%x, ret = %d.\n", + head->opcode, head->service_type, ret); + + kfree(resp); +} + +static inline void ubase_ctrlq_reset_crq_ci(struct ubase_dev *udev) +{ + struct ubase_ctrlq_ring *crq = &udev->ctrlq.crq; + + crq->pi = ubase_read_dev(&udev->hw, UBASE_CTRLQ_CRQ_TAIL_REG); + crq->ci = crq->pi; + ubase_write_dev(&udev->hw, UBASE_CTRLQ_CRQ_HEAD_REG, crq->ci); +} + +void ubase_ctrlq_crq_handler(struct ubase_dev *udev) +{ + struct ubase_ctrlq_ring *crq = &udev->ctrlq.crq; + struct ub_entity *ue = to_ub_entity(udev->dev); + struct ubase_ctrlq_base_block head = {0}; + u8 bb_num; + u8 *addr; + u16 seq; + + while (!ubase_ctrlq_crq_is_empty(udev, &udev->hw)) { + if (!test_bit(UBASE_CTRLQ_STATE_ENABLE, &udev->ctrlq.state)) { + ubase_warn(udev, "ctrlq is disabled in crq.\n"); + return; + } + + addr = crq->base_addr + crq->ci * UBASE_CTRLQ_BB_LEN; + memcpy_fromio(&head, addr, UBASE_CTRLQ_HDR_LEN); + seq = le16_to_cpu(head.seq); + bb_num = head.bb_num; + + if (unlikely(!bb_num || bb_num > UBASE_CTRLQ_MAX_BB)) { + ubase_err(udev, "ctrlq crq bb_num(%u) is invalid.\n", + bb_num); + ubase_ctrlq_reset_crq_ci(udev); + return; + } + + if (!ubase_ctrlq_check_seq(udev, seq)) { + ubase_warn(udev, + "ctrlq recv invalid seq, seq = %u.\n", seq); + ubase_ctrlq_update_crq_ci(udev, bb_num); + continue; + } + + if (le16_to_cpu(head.bus_ue_id) == ue->entity_idx) + ubase_ctrlq_handle_self_msg(udev, &head); + else + ubase_ctrlq_handle_other_msg(udev, &head); + + ubase_ctrlq_update_crq_ci(udev, bb_num); + } +} + +void ubase_ctrlq_service_task(struct ubase_delay_work *ubase_work) +{ + struct ubase_dev *udev = container_of(ubase_work, struct ubase_dev, + service_task); + struct ubase_ctrlq_crq_table *crq_tab = &udev->ctrlq.crq_table; + + if (!test_and_clear_bit(UBASE_STATE_CTRLQ_SERVICE_SCHED, + &udev->service_task.state) || + test_and_set_bit(UBASE_STATE_CTRLQ_HANDLING, + &udev->service_task.state)) + return; + + if (time_is_before_eq_jiffies(crq_tab->last_crq_scheduled + + UBASE_CTRLQ_SCHED_TIMEOUT)) + ubase_warn(udev, + "ctrlq crq service task is scheduled after %ums on cpu%d!\n", + jiffies_to_msecs(jiffies - crq_tab->last_crq_scheduled), + smp_processor_id()); + + ubase_ctrlq_crq_handler(udev); + + clear_bit(UBASE_STATE_CTRLQ_HANDLING, &udev->service_task.state); +} + +void ubase_ctrlq_clean_service_task(struct ubase_delay_work *ubase_work) +{ + struct ubase_dev *udev = container_of(ubase_work, struct ubase_dev, + service_task); + struct ubase_ctrlq_ring *csq = &udev->ctrlq.csq; + u16 i, max_seq = ubase_ctrlq_max_seq(udev); + struct ubase_ctrlq_msg_ctx *ctx; + + if (!test_bit(UBASE_CTRLQ_STATE_ENABLE, &udev->ctrlq.state) || + ubase_dev_pmu_supported(udev)) + return; + + spin_lock_bh(&csq->lock); + for (i = 1; i <= max_seq; i++) { + ctx = &udev->ctrlq.msg_queue[i]; + if (ctx->valid && time_is_before_eq_jiffies(ctx->dead_jiffies)) + ctx->valid = 0; + } + spin_unlock_bh(&csq->lock); +} + +int ubase_ctrlq_register_crq_event(struct auxiliary_device *aux_dev, + struct ubase_ctrlq_event_nb *nb) +{ + struct ubase_ctrlq_crq_event_nbs *nbs, *tmp, *new_nbs; + struct ubase_ctrlq_crq_table *crq_tab; + struct ubase_dev *udev; + int ret; + + if (!aux_dev || !nb || !nb->crq_handler) + return -EINVAL; + + udev = __ubase_get_udev_by_adev(aux_dev); + crq_tab = &udev->ctrlq.crq_table; + mutex_lock(&crq_tab->lock); + list_for_each_entry_safe(nbs, tmp, &crq_tab->crq_nbs.list, list) { + if (nbs->crq_nb.service_type == nb->service_type && + nbs->crq_nb.opcode == nb->opcode) { + ret = -EEXIST; + goto err_crq_register; + } + } + + new_nbs = kzalloc(sizeof(*new_nbs), GFP_KERNEL); + if (!new_nbs) { + ret = -ENOMEM; + goto err_crq_register; + } + + new_nbs->crq_nb = *nb; + list_add_tail(&new_nbs->list, &crq_tab->crq_nbs.list); + mutex_unlock(&crq_tab->lock); + + return 0; + +err_crq_register: + mutex_unlock(&crq_tab->lock); + ubase_err(udev, + "failed to register ctrlq crq event, opcode = 0x%x, service_type = 0x%x, ret = %d.\n", + nb->opcode, nb->service_type, ret); + + return ret; +} +EXPORT_SYMBOL(ubase_ctrlq_register_crq_event); + +void ubase_ctrlq_unregister_crq_event(struct auxiliary_device *aux_dev, + u8 service_type, u8 opcode) +{ + struct ubase_ctrlq_crq_event_nbs *nbs, *tmp; + struct ubase_ctrlq_crq_table *crq_tab; + struct ubase_dev *udev; + + if (!aux_dev) + return; + + udev = __ubase_get_udev_by_adev(aux_dev); + crq_tab = &udev->ctrlq.crq_table; + mutex_lock(&crq_tab->lock); + list_for_each_entry_safe(nbs, tmp, &crq_tab->crq_nbs.list, list) { + if (nbs->crq_nb.service_type == service_type && + nbs->crq_nb.opcode == opcode) { + list_del(&nbs->list); + kfree(nbs); + break; + } + } + mutex_unlock(&crq_tab->lock); +} +EXPORT_SYMBOL(ubase_ctrlq_unregister_crq_event); diff --git a/drivers/ub/ubase/ubase_ctrlq.h b/drivers/ub/ubase/ubase_ctrlq.h new file mode 100644 index 000000000000..8bb5cb0cc1a9 --- /dev/null +++ b/drivers/ub/ubase/ubase_ctrlq.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UBASE_CTRLQ_H__ +#define __UBASE_CTRLQ_H__ + +#include + +#include "ubase_dev.h" + +#define UBASE_CTRLQ_TX_TIMEOUT 30000 +#define UBASE_CTRLQ_BB_LEN 32U +#define UBASE_CTRLQ_HDR_LEN 12 +#define UBASE_CTRLQ_DATA_LEN (UBASE_CTRLQ_BB_LEN - UBASE_CTRLQ_HDR_LEN) +#define UBASE_CTRLQ_MAX_BB 32 +#define UBASE_CTRLQ_SCHED_TIMEOUT (HZ / 2) +#define UBASE_CTRLQ_SEQ_MASK BIT(15) +#define UBASE_CTRLQ_DEAD_TIME 40000 + +enum ubase_ctrlq_state { + UBASE_CTRLQ_STATE_ENABLE, +}; + +struct ubase_ctrlq_ue_info { + u8 mbx_ue_id; + u16 seq; + u16 bus_ue_id; +}; + +int ubase_ctrlq_init(struct ubase_dev *udev); +void ubase_ctrlq_uninit(struct ubase_dev *udev); +void ubase_ctrlq_disable(struct ubase_dev *udev); + +int __ubase_ctrlq_send(struct ubase_dev *udev, struct ubase_ctrlq_msg *msg, + struct ubase_ctrlq_ue_info *ue_info); + +void ubase_ctrlq_service_task(struct ubase_delay_work *ubase_work); +void ubase_ctrlq_handle_crq_msg(struct ubase_dev *udev, + struct ubase_ctrlq_base_block *head, + u16 seq, void *msg, u16 data_len); +void ubase_ctrlq_clean_service_task(struct ubase_delay_work *ubase_work); + +#endif diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index 269662b6ab1f..8dc3640e0128 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -9,6 +9,7 @@ #include "debugfs/ubase_debugfs.h" #include "ubase_cmd.h" +#include "ubase_ctrlq.h" #include "ubase_hw.h" #include "ubase_mailbox.h" #include "ubase_dev.h" @@ -331,8 +332,19 @@ static void ubase_period_service_task(struct work_struct *work) ubase_enable_period_service_task(udev); } +void ubase_service_task(struct work_struct *work) +{ + struct ubase_delay_work *ubase_work = + container_of(work, struct ubase_delay_work, service_task.work); + + ubase_crq_service_task(ubase_work); + ubase_ctrlq_service_task(ubase_work); + ubase_ctrlq_clean_service_task(ubase_work); +} + static void ubase_init_delayed_work(struct ubase_dev *udev) { + INIT_DELAYED_WORK(&udev->service_task.service_task, ubase_service_task); INIT_DELAYED_WORK(&udev->period_service_task.service_task, ubase_period_service_task); } @@ -395,7 +407,108 @@ static void ubase_wq_uninit(struct ubase_dev *udev) destroy_workqueue(udev->ubase_wq); } +static int ubase_handle_ue2ue_ctrlq_req(struct ubase_dev *udev, + struct ubase_ue2ue_ctrlq_head *cmd, + u32 len) +{ + struct ubase_ctrlq_base_block *head = (struct ubase_ctrlq_base_block *)(cmd + 1); + u16 mbx_ue_id = le16_to_cpu(cmd->head.mbx_ue_id); + struct ubase_ctrlq_ue_info ue_info; + struct ubase_ctrlq_msg msg = {0}; + int ret; + + if (!ubase_mbx_ue_id_is_valid(mbx_ue_id, udev)) { + ubase_err(udev, "ubase ue2ue ctrlq req mbx ue id = %u error.\n", + mbx_ue_id); + return -EINVAL; + } + + if (cmd->in_size > (len - (sizeof(*cmd) + UBASE_CTRLQ_HDR_LEN))) { + ubase_err(udev, "ubase ue2ue cmd len = %u error.\n", cmd->in_size); + return -EINVAL; + } + + if (cmd->in_size > (len - (sizeof(*cmd) + UBASE_CTRLQ_HDR_LEN))) { + ubase_err(udev, "ubase e2e cmd len = %u error.\n", cmd->in_size); + return -EINVAL; + } + + msg.service_ver = head->service_ver; + msg.service_type = head->service_type; + msg.opcode = head->opcode; + msg.need_resp = cmd->need_resp; + msg.is_resp = cmd->is_resp; + msg.resp_seq = cmd->seq; + msg.in = (u8 *)head + UBASE_CTRLQ_HDR_LEN; + msg.in_size = cmd->in_size; + msg.out = NULL; + msg.out_size = 0; + + ue_info.bus_ue_id = le16_to_cpu(cmd->head.bus_ue_id); + ue_info.seq = cmd->seq; + ue_info.mbx_ue_id = mbx_ue_id; + + ret = __ubase_ctrlq_send(udev, &msg, &ue_info); + if (ret) + ubase_err(udev, "failed to send opc(0x%x) ctrlq, ret = %d.\n", + head->opcode, ret); + + return ret; +} + +static int ubase_handle_ue2ue_ctrlq_event(struct ubase_dev *udev, void *data, + u32 len) +{ + struct ubase_ue2ue_ctrlq_head *cmd = data; + struct ubase_ctrlq_base_block *head; + u16 data_len; + + if (len < (sizeof(*cmd) + UBASE_CTRLQ_HDR_LEN)) { + ubase_err(udev, "invalid ue2ue ctrlq event len(%u).\n", len); + return -EINVAL; + } + + if (ubase_dev_ctrlq_supported(udev)) + return ubase_handle_ue2ue_ctrlq_req(udev, cmd, len); + + head = (struct ubase_ctrlq_base_block *)(cmd + 1); + data_len = len - sizeof(*cmd) - UBASE_CTRLQ_HDR_LEN; + ubase_ctrlq_handle_crq_msg(udev, head, cmd->seq, + (u8 *)head + UBASE_CTRLQ_HDR_LEN, data_len); + + return 0; +} + +struct ubase_ue2ue_event_handler { + u16 sub_cmd; + int (*event_handler)(struct ubase_dev *udev, void *data, u32 len); +} ubase_ue2ue_events[] = { + { UBASE_UE2UE_CTRLQ_MSG, ubase_handle_ue2ue_ctrlq_event }, +}; + +static int ubase_handle_ue2ue_event(void *dev, void *data, u32 len) +{ + struct ubase_ue2ue_common_head *head = data; + struct ubase_dev *udev = dev; + int i; + + for (i = 0; i < ARRAY_SIZE(ubase_ue2ue_events); i++) { + if (ubase_ue2ue_events[i].sub_cmd == head->sub_cmd) + return ubase_ue2ue_events[i].event_handler(udev, data, + len); + } + + ubase_warn(udev, "unknown ubase ue2ue event, sub_cmd = %u.\n", + head->sub_cmd); + + return 0; +} + static struct ubase_crq_event_nb ubase_crq_events[] = { + { + .opcode = UBASE_OPC_UE2UE_UBASE, + .crq_handler = ubase_handle_ue2ue_event, + }, }; static void ubase_unregister_cmdq_crq_event(struct ubase_dev *udev) @@ -465,7 +578,7 @@ static const struct ubase_init_function ubase_init_func_map[] = { }, { "init ctrl queue", UBASE_SUP_NO_PMU, 1, - NULL, NULL + ubase_ctrlq_init, ubase_ctrlq_uninit }, { "register aeq event", UBASE_SUP_NO_PMU, 0, @@ -584,6 +697,15 @@ bool ubase_adev_ubl_supported(struct auxiliary_device *adev) } EXPORT_SYMBOL(ubase_adev_ubl_supported); +bool ubase_adev_ctrlq_supported(struct auxiliary_device *adev) +{ + if (!adev) + return false; + + return ubase_dev_ctrlq_supported(__ubase_get_udev_by_adev(adev)); +} +EXPORT_SYMBOL(ubase_adev_ctrlq_supported); + bool ubase_adev_eth_mac_supported(struct auxiliary_device *adev) { if (!adev) diff --git a/drivers/ub/ubase/ubase_dev.h b/drivers/ub/ubase/ubase_dev.h index 3f7bc9e02e94..d21e38861380 100644 --- a/drivers/ub/ubase/ubase_dev.h +++ b/drivers/ub/ubase/ubase_dev.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -136,6 +137,68 @@ struct ubase_crq_table { struct ubase_crq_event_nbs nbs; }; +#define UBASE_CTRLQ_DATA_NUM 5 + +#pragma pack(push, 1) +struct ubase_ctrlq_base_block { + u8 service_ver; + u8 service_type; + u8 bb_num; + u8 opcode; + u8 ret; + __le16 seq; + u8 mbx_ue_id; + __le16 bus_ue_id; + __le16 rsv; + __le32 data[UBASE_CTRLQ_DATA_NUM]; +}; +#pragma pack(pop) + +struct ubase_ctrlq_ring { + u16 ci; + u16 pi; + u16 depth; + u32 tx_timeout; + void __iomem *base_addr; + spinlock_t lock; +}; + +struct ubase_ctrlq_msg_ctx { + u8 valid : 1; + u8 is_sync : 1; + u8 rsv0 : 6; + u8 mbx_ue_id; + u8 result; + u8 rsv1; + u16 ue_seq; + u16 bus_ue_id; + u16 out_size; + void *out; + unsigned long dead_jiffies; + struct completion done; +}; + +struct ubase_ctrlq_crq_event_nbs { + struct list_head list; + struct ubase_ctrlq_event_nb crq_nb; +}; + +struct ubase_ctrlq_crq_table { + struct mutex lock; + unsigned long last_crq_scheduled; + struct ubase_ctrlq_crq_event_nbs crq_nbs; +}; + +struct ubase_ctrlq { + u16 csq_next_seq; + unsigned long state; + atomic_t req_cnt; + struct ubase_ctrlq_ring csq; + struct ubase_ctrlq_ring crq; + struct ubase_ctrlq_msg_ctx *msg_queue; + struct ubase_ctrlq_crq_table crq_table; +}; + #define UBASE_ACT_STAT_MAX_NUM 10U struct ubase_activate_dev_stats { u64 act_cnt; @@ -219,6 +282,7 @@ struct ubase_dev { struct ubase_delay_work period_service_task; struct ubase_delay_work arq_service_task; struct ubase_crq_table crq_table; + struct ubase_ctrlq ctrlq; unsigned long state_bits; struct list_head ue_list; struct mutex ue_list_lock; @@ -291,6 +355,11 @@ static inline bool ubase_dev_ta_timer_buf_supported(struct ubase_dev *udev) return ubase_get_cap_bit(udev, UBASE_SUPPORT_TA_TIMER_BUF_B); } +static inline bool ubase_dev_ctrlq_supported(struct ubase_dev *udev) +{ + return ubase_get_cap_bit(udev, UBASE_SUPPORT_CTRLQ_B); +} + static inline bool ubase_dev_eth_mac_supported(struct ubase_dev *udev) { return ubase_get_cap_bit(udev, UBASE_SUPPORT_ETH_MAC_B); @@ -306,6 +375,16 @@ static inline u32 ubase_jfs_num(struct ubase_dev *udev) dev_caps->public_jetty_cnt + dev_caps->rsvd_jetty_cnt; } +static inline bool ubase_mbx_ue_id_is_valid(u16 mbx_ue_id, + struct ubase_dev *udev) +{ + + if (!mbx_ue_id || (mbx_ue_id > udev->caps.dev_caps.ue_num - 1)) + return false; + + return true; +} + int ubase_adev_idx_alloc(void); void ubase_adev_idx_free(int id); diff --git a/drivers/ub/ubase/ubase_eq.c b/drivers/ub/ubase/ubase_eq.c index 3fe929654592..4c092ea533bb 100644 --- a/drivers/ub/ubase/ubase_eq.c +++ b/drivers/ub/ubase/ubase_eq.c @@ -123,12 +123,17 @@ void ubase_enable_misc_vector(struct ubase_dev *udev, bool enable) static unsigned long ubase_check_event_cause(struct ubase_dev *udev) { unsigned long event_cause = 0; + u32 ctrlq_src_reg; u32 cmdq_src_reg; cmdq_src_reg = ubase_read_dev(&udev->hw, UBASE_VECTOR0_CMDQ_SRC_REG); if (cmdq_src_reg & BIT(UBASE_VECTOR0_RX_CMDQ_INT_B)) event_cause |= BIT(UBASE_ASYNC_EVENT_CRQ_B); + ctrlq_src_reg = ubase_read_dev(&udev->hw, UBASE_VECTOR0_CTRLQ_SRC_REG); + if (ctrlq_src_reg & BIT(UBASE_VECTOR0_RX_CTRLQ_INT_B)) + event_cause |= BIT(UBASE_ASYNC_EVENT_CTRLQ_B); + return event_cause; } @@ -138,11 +143,16 @@ static void ubase_clear_event_cause(struct ubase_dev *udev, if (test_bit(UBASE_ASYNC_EVENT_CRQ_B, &event_cause)) ubase_write_dev(&udev->hw, UBASE_VECTOR0_CMDQ_SRC_REG, BIT(UBASE_VECTOR0_RX_CMDQ_INT_B)); + if (test_bit(UBASE_ASYNC_EVENT_CTRLQ_B, &event_cause)) + ubase_write_dev(&udev->hw, UBASE_VECTOR0_CTRLQ_SRC_REG, + BIT(UBASE_VECTOR0_RX_CTRLQ_INT_B)); } static void ubase_clear_all_event_cause(struct ubase_dev *udev) { - ubase_clear_event_cause(udev, BIT(UBASE_ASYNC_EVENT_CRQ_B)); + ubase_clear_event_cause(udev, + BIT(UBASE_ASYNC_EVENT_CRQ_B) | + BIT(UBASE_ASYNC_EVENT_CTRLQ_B)); } static void ubase_crq_task_schedule(struct ubase_dev *udev) @@ -155,6 +165,16 @@ static void ubase_crq_task_schedule(struct ubase_dev *udev) } } +static void ubase_ctrlq_task_schedule(struct ubase_dev *udev) +{ + if (!test_and_set_bit(UBASE_STATE_CTRLQ_SERVICE_SCHED, + &udev->service_task.state)) { + udev->ctrlq.crq_table.last_crq_scheduled = jiffies; + mod_delayed_work(udev->ubase_wq, + &udev->service_task.service_task, 0); + } +} + static int ubase_reg_event_handler(struct ubase_dev *udev) { unsigned long event_cause; @@ -165,6 +185,8 @@ static int ubase_reg_event_handler(struct ubase_dev *udev) if (test_bit(UBASE_ASYNC_EVENT_CRQ_B, &event_cause)) ubase_crq_task_schedule(udev); + if (test_bit(UBASE_ASYNC_EVENT_CTRLQ_B, &event_cause)) + ubase_ctrlq_task_schedule(udev); ubase_clear_event_cause(udev, event_cause); ubase_enable_misc_vector(udev, true); diff --git a/drivers/ub/ubase/ubase_eq.h b/drivers/ub/ubase/ubase_eq.h index 9b7b04911e92..d448bbab4c02 100644 --- a/drivers/ub/ubase/ubase_eq.h +++ b/drivers/ub/ubase/ubase_eq.h @@ -38,13 +38,18 @@ /* Vector0 interrupt CMDQ event source register(RW) */ #define UBASE_VECTOR0_CMDQ_SRC_REG 0x18004 +/* Vector0 interrupt CTRLQ event source register(RW) */ +#define UBASE_VECTOR0_CTRLQ_SRC_REG 0x18014 /* Vector0 interrupt control register */ #define UBASE_MISC_VECTOR_REG_OFFSET 0x18020 /* CMDQ register bits for RX event */ #define UBASE_VECTOR0_RX_CMDQ_INT_B 1 +/* CTRLQ register bits for RX event */ +#define UBASE_VECTOR0_RX_CTRLQ_INT_B 0 enum ubase_async_event_cause_bit { UBASE_ASYNC_EVENT_CRQ_B, + UBASE_ASYNC_EVENT_CTRLQ_B, }; enum ubase_eqc_irqn { diff --git a/drivers/ub/ubase/ubase_trace.h b/drivers/ub/ubase/ubase_trace.h index a49902e6512f..8d9cbe3a1c3f 100644 --- a/drivers/ub/ubase/ubase_trace.h +++ b/drivers/ub/ubase/ubase_trace.h @@ -159,6 +159,49 @@ TRACE_EVENT(ubase_ceqe, ) ); +DECLARE_EVENT_CLASS(ubase_ctrlq_template, + TP_PROTO(const struct device *dev, u16 bb_num, u16 pi, u16 ci, + const void *buf, u16 len), + TP_ARGS(dev, bb_num, pi, ci, buf, len), + + TP_STRUCT__entry( + __field(u16, bb_num) + __field(u16, pi) + __field(u16, ci) + __dynamic_array(u8, data, len) + __field(u16, len) + __dynamic_array(char, devname, TRACE_DEV_NAME_MAX_LEN) + ), + + TP_fast_assign( + __entry->bb_num = bb_num; + __entry->pi = pi; + __entry->ci = ci; + __entry->len = len; + memcpy(__get_dynamic_array(data), buf, len); + if (dev) { + snprintf(__get_str(devname), TRACE_DEV_NAME_MAX_LEN, + "%s %s", dev_driver_string(dev), dev_name(dev)); + } + ), + + TP_printk( + "%s %u-%u-%u data: %s", __get_str(devname), + __entry->bb_num, __entry->pi, __entry->ci, + __print_array(__get_dynamic_array(data), __entry->len, sizeof(__u8)) + ) +); + +DEFINE_EVENT(ubase_ctrlq_template, ubase_ctrlq_csq, + TP_PROTO(const struct device *dev, u16 bb_num, u16 pi, u16 ci, + const void *buf, u16 len), + TP_ARGS(dev, bb_num, pi, ci, buf, len)); + +DEFINE_EVENT(ubase_ctrlq_template, ubase_ctrlq_crq, + TP_PROTO(const struct device *dev, u16 bb_num, u16 pi, u16 ci, + const void *buf, u16 len), + TP_ARGS(dev, bb_num, pi, ci, buf, len)); + #endif /* __UBASE_TRACE_H__ */ /* This must be outside ifdef __UBASE_TRACE_H__ */ diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index f915b53c94d5..98c4ac1115e7 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -54,6 +54,7 @@ enum ubase_opcode_type { UBASE_OPC_UE_TO_MUE = 0xF002, UBASE_OPC_CFG_VPORT_BUF = 0xF003, UBASE_OPC_DESTROY_CTX_RESOURCE = 0xF00D, + UBASE_OPC_UE2UE_UBASE = 0xF00E, }; union ubase_mbox { diff --git a/include/ub/ubase/ubase_comm_ctrlq.h b/include/ub/ubase/ubase_comm_ctrlq.h new file mode 100644 index 000000000000..5b08cefe45dd --- /dev/null +++ b/include/ub/ubase/ubase_comm_ctrlq.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef _UB_UBASE_COMM_CTRLQ_H_ +#define _UB_UBASE_COMM_CTRLQ_H_ + +#include +#include + +#define UBASE_CTRLQ_MAX_DATA_SIZE 1000U + +#define UBASE_CTRLQ_CSQ_BASEADDR_L_REG 0x18800 +#define UBASE_CTRLQ_CSQ_BASEADDR_H_REG 0x18804 +#define UBASE_CTRLQ_CSQ_DEPTH_REG 0x18808 +#define UBASE_CTRLQ_CSQ_TAIL_REG 0x18810 +#define UBASE_CTRLQ_CSQ_HEAD_REG 0x18814 +#define UBASE_CTRLQ_CRQ_BASEADDR_L_REG 0x18818 +#define UBASE_CTRLQ_CRQ_BASEADDR_H_REG 0x1881c +#define UBASE_CTRLQ_CRQ_DEPTH_REG 0x18820 +#define UBASE_CTRLQ_CRQ_TAIL_REG 0x18824 +#define UBASE_CTRLQ_CRQ_HEAD_REG 0x18828 + +enum ubase_ctrlq_ser_ver { + UBASE_CTRLQ_SER_VER_01 = 0x01, +}; + +enum ubase_ctrlq_ser_type { + UBASE_CTRLQ_SER_TYPE_TP_ACL = 0x01, + UBASE_CTRLQ_SER_TYPE_DEV_REGISTER = 0x02, + UBASE_CTRLQ_SER_TYPE_IP_ACL = 0x03, + UBASE_CTRLQ_SER_TYPE_QOS = 0x04, +}; + +struct ubase_ctrlq_msg { + enum ubase_ctrlq_ser_ver service_ver; + enum ubase_ctrlq_ser_type service_type; + u8 opcode; + u8 need_resp : 1; + u8 is_resp : 1; + u8 resv : 6; + u8 resp_ret; /* must set when the is_resp field is true. */ + u16 resp_seq; /* must set when the is_resp field is true. */ + u16 in_size; + u16 out_size; + void *in; + void *out; +}; + +struct ubase_ctrlq_event_nb { + u8 service_type; + u8 opcode; + void *back; + int (*crq_handler)(struct auxiliary_device *adev, u8 service_ver, + void *data, u16 len, u16 seq); +}; + +int ubase_ctrlq_send_msg(struct auxiliary_device *aux_dev, + struct ubase_ctrlq_msg *msg); + +int ubase_ctrlq_register_crq_event(struct auxiliary_device *aux_dev, + struct ubase_ctrlq_event_nb *nb); +void ubase_ctrlq_unregister_crq_event(struct auxiliary_device *aux_dev, + u8 service_type, u8 opcode); + +#endif diff --git a/include/ub/ubase/ubase_comm_dev.h b/include/ub/ubase/ubase_comm_dev.h index 9d5afb9e4ebc..3f7f3d1c8540 100644 --- a/include/ub/ubase/ubase_comm_dev.h +++ b/include/ub/ubase/ubase_comm_dev.h @@ -214,6 +214,7 @@ struct ubase_bus_eid { }; bool ubase_adev_ubl_supported(struct auxiliary_device *adev); +bool ubase_adev_ctrlq_supported(struct auxiliary_device *adev); bool ubase_adev_eth_mac_supported(struct auxiliary_device *adev); struct ubase_resource_space *ubase_get_io_base(struct auxiliary_device *adev); -- Gitee From 38b672d1f07621ce954f3602e59da1d063df5584 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Thu, 18 Sep 2025 18:57:48 +0800 Subject: [PATCH 161/214] ub: ubase: support querying sl information in initialization phase ANBZ: #45460 commit acb96f3cf8c897bc0d0c38c35270acd5c8b2a72f openEuler. In the control plane scenario, the sl and vl of the UB system (UNIC, UDMA, and CDMA) are managed by the control plane. The control plane allocates sl and vl based on service requirements to ensure smooth service traffic running. This patch adds the logic for obtaining the sl and vl configuration ofcontrol plane for the UBASE driver. During initialization, UBASE queries the sl and vl information from the management plane and sends the information to the auxiliary device. Signed-off-by: Chuan Wu Signed-off-by: Haiqing Fang Signed-off-by: Haibin Lu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/Makefile | 3 +- drivers/ub/ubase/debugfs/ubase_debugfs.c | 9 + drivers/ub/ubase/debugfs/ubase_debugfs.h | 10 + drivers/ub/ubase/debugfs/ubase_qos_debugfs.c | 72 +++ drivers/ub/ubase/debugfs/ubase_qos_debugfs.h | 13 + drivers/ub/ubase/ubase_ctrlq.h | 19 + drivers/ub/ubase/ubase_dev.c | 2 +- drivers/ub/ubase/ubase_dev.h | 5 + drivers/ub/ubase/ubase_hw.h | 16 + drivers/ub/ubase/ubase_qos_hw.c | 582 +++++++++++++++++++ include/ub/ubase/ubase_comm_cmd.h | 5 + include/ub/ubase/ubase_comm_ctrlq.h | 5 + 12 files changed, 739 insertions(+), 2 deletions(-) create mode 100644 drivers/ub/ubase/debugfs/ubase_qos_debugfs.c create mode 100644 drivers/ub/ubase/debugfs/ubase_qos_debugfs.h create mode 100644 drivers/ub/ubase/ubase_qos_hw.c diff --git a/drivers/ub/ubase/Makefile b/drivers/ub/ubase/Makefile index 7c21b558779c..f660def9e25e 100644 --- a/drivers/ub/ubase/Makefile +++ b/drivers/ub/ubase/Makefile @@ -11,7 +11,8 @@ MODULE_NAME := ubase UBASE_OBJS := ubase_main.o ubase_dev.o ubase_hw.o ubase_cmd.o ubase_ctrlq.o \ debugfs/ubase_debugfs.o ubase_eq.o ubase_mailbox.o ubase_ubus.o \ - debugfs/ubase_ctx_debugfs.o + debugfs/ubase_ctx_debugfs.o debugfs/ubase_qos_debugfs.o \ + ubase_qos_hw.o $(MODULE_NAME)-objs := $(UBASE_OBJS) obj-$(CONFIG_UB_UBASE) := ubase.o diff --git a/drivers/ub/ubase/debugfs/ubase_debugfs.c b/drivers/ub/ubase/debugfs/ubase_debugfs.c index 46a794d935da..d1bcf453e3d1 100644 --- a/drivers/ub/ubase/debugfs/ubase_debugfs.c +++ b/drivers/ub/ubase/debugfs/ubase_debugfs.c @@ -9,6 +9,7 @@ #include #include "ubase_dev.h" +#include "ubase_qos_debugfs.h" #include "ubase_debugfs.h" static struct dentry *ubase_dbgfs_root; @@ -80,6 +81,14 @@ static struct ubase_dbg_dentry_info ubase_dbg_dentry[] = { }; static struct ubase_dbg_cmd_info ubase_dbg_cmd[] = { + { + .name = "adev_qos", + .dentry_index = UBASE_DBG_DENTRY_QOS, + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_adev_qos_info, + }, }; static int ubase_dbg_create_dir(struct device *dev, diff --git a/drivers/ub/ubase/debugfs/ubase_debugfs.h b/drivers/ub/ubase/debugfs/ubase_debugfs.h index 8b68b75d4cfc..8ea5dfc1bdf8 100644 --- a/drivers/ub/ubase/debugfs/ubase_debugfs.h +++ b/drivers/ub/ubase/debugfs/ubase_debugfs.h @@ -20,6 +20,16 @@ enum ubase_dbg_dentry_type { UBASE_DBG_DENTRY_ROOT, }; +static inline void ubase_dbg_dump_arr_info(struct seq_file *s, u8 *arr, u8 arr_num) +{ + u8 i; + + for (i = 0; i < arr_num; i++) + seq_printf(s, " %u", arr[i]); + + seq_puts(s, "\n"); +} + int ubase_dbg_init(struct ubase_dev *udev); void ubase_dbg_uninit(struct ubase_dev *udev); int ubase_dbg_register_debugfs(void); diff --git a/drivers/ub/ubase/debugfs/ubase_qos_debugfs.c b/drivers/ub/ubase/debugfs/ubase_qos_debugfs.c new file mode 100644 index 000000000000..760c19c12314 --- /dev/null +++ b/drivers/ub/ubase/debugfs/ubase_qos_debugfs.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include "ubase_debugfs.h" +#include "ubase_hw.h" +#include "ubase_mailbox.h" +#include "ubase_qos_debugfs.h" + +static void ubase_dbg_dump_adev_vl_info(struct seq_file *s, + struct ubase_adev_qos *qos) +{ + seq_puts(s, "vl:"); + ubase_dbg_dump_arr_info(s, qos->vl, qos->vl_num); + + seq_puts(s, "tp_req_vl:"); + ubase_dbg_dump_arr_info(s, qos->tp_req_vl, qos->tp_vl_num); + + seq_puts(s, "ctp_req_vl:"); + ubase_dbg_dump_arr_info(s, qos->ctp_req_vl, qos->ctp_vl_num); + + seq_puts(s, "nic_vl:"); + ubase_dbg_dump_arr_info(s, qos->nic_vl, qos->nic_vl_num); +} + +static void ubase_dbg_dump_adev_sl_info(struct seq_file *s, + struct ubase_adev_qos *qos) +{ + seq_puts(s, "sl:"); + ubase_dbg_dump_arr_info(s, qos->sl, qos->sl_num); + + seq_puts(s, "tp_sl:"); + ubase_dbg_dump_arr_info(s, qos->tp_sl, qos->tp_sl_num); + + seq_puts(s, "ctp_sl:"); + ubase_dbg_dump_arr_info(s, qos->ctp_sl, qos->ctp_sl_num); + + seq_puts(s, "nic_sl:"); + ubase_dbg_dump_arr_info(s, qos->nic_sl, qos->nic_sl_num); +} + +int ubase_dbg_dump_adev_qos_info(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + struct ubase_adev_qos *qos = &udev->qos; + struct ubase_dbg_adev_qos_info { + const char *format; + u8 qos_info; + } adev_qos_info[] = { + {"vl_num: %u\n", qos->vl_num}, + {"tp_vl_num: %u\n", qos->tp_vl_num}, + {"ctp_vl_num: %u\n", qos->ctp_vl_num}, + {"tp_resp_vl_offset: %u\n", qos->tp_resp_vl_offset}, + {"ctp_resp_vl_offset: %u\n", qos->ctp_resp_vl_offset}, + {"sl_num: %u\n", qos->sl_num}, + {"tp_sl_num: %u\n", qos->tp_sl_num}, + {"ctp_sl_num: %u\n", qos->ctp_sl_num}, + {"nic_sl_num: %u\n", qos->nic_sl_num}, + {"nic_vl_num: %u\n", qos->nic_vl_num}, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(adev_qos_info); i++) + seq_printf(s, adev_qos_info[i].format, adev_qos_info[i].qos_info); + + ubase_dbg_dump_adev_vl_info(s, qos); + ubase_dbg_dump_adev_sl_info(s, qos); + + return 0; +} diff --git a/drivers/ub/ubase/debugfs/ubase_qos_debugfs.h b/drivers/ub/ubase/debugfs/ubase_qos_debugfs.h new file mode 100644 index 000000000000..538c9da5d1dc --- /dev/null +++ b/drivers/ub/ubase/debugfs/ubase_qos_debugfs.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UBASE_QOS_DEBUGFS_H__ +#define __UBASE_QOS_DEBUGFS_H__ + +struct device; + +int ubase_dbg_dump_adev_qos_info(struct seq_file *s, void *data); +#endif diff --git a/drivers/ub/ubase/ubase_ctrlq.h b/drivers/ub/ubase/ubase_ctrlq.h index 8bb5cb0cc1a9..dc7239bc627e 100644 --- a/drivers/ub/ubase/ubase_ctrlq.h +++ b/drivers/ub/ubase/ubase_ctrlq.h @@ -30,6 +30,25 @@ struct ubase_ctrlq_ue_info { u16 bus_ue_id; }; +struct ubase_ctrlq_query_vl_resp { + __le16 vl_bitmap; + u8 rsv[18]; +}; + +struct ubase_ctrlq_query_vl_req { + u8 rsv[20]; +}; + +struct ubase_ctrlq_query_sl_resp { + __le16 unic_sl_bitmap; + __le16 udma_sl_bitmap; + u8 rsv[16]; +}; + +struct ubase_ctrlq_query_sl_req { + u8 rsv[20]; +}; + int ubase_ctrlq_init(struct ubase_dev *udev); void ubase_ctrlq_uninit(struct ubase_dev *udev); void ubase_ctrlq_disable(struct ubase_dev *udev); diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index 8dc3640e0128..073bc5a368e6 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -594,7 +594,7 @@ static const struct ubase_init_function ubase_init_func_map[] = { }, { "init qos", UBASE_SUP_ALL, 0, - NULL, NULL + ubase_qos_init, NULL }, { "prealloc memory", UBASE_SUP_UDMA, 1, diff --git a/drivers/ub/ubase/ubase_dev.h b/drivers/ub/ubase/ubase_dev.h index d21e38861380..cad11ff085fd 100644 --- a/drivers/ub/ubase/ubase_dev.h +++ b/drivers/ub/ubase/ubase_dev.h @@ -365,6 +365,11 @@ static inline bool ubase_dev_eth_mac_supported(struct ubase_dev *udev) return ubase_get_cap_bit(udev, UBASE_SUPPORT_ETH_MAC_B); } +static inline bool ubase_utp_supported(struct ubase_dev *udev) +{ + return ubase_get_cap_bit(udev, UBASE_SUPPORT_UTP_B); +} + static inline u32 ubase_jfs_num(struct ubase_dev *udev) { struct ubase_adev_caps *unic_caps = &udev->caps.unic_caps; diff --git a/drivers/ub/ubase/ubase_hw.h b/drivers/ub/ubase/ubase_hw.h index 99c3f85ad2f7..882f7ea67576 100644 --- a/drivers/ub/ubase/ubase_hw.h +++ b/drivers/ub/ubase/ubase_hw.h @@ -138,6 +138,11 @@ struct ubase_cfg_dma_buf_req { __le32 resv[3]; }; +struct ubase_config_sl_vl_cmd { + u8 sl_num; + u8 sl_vl[23]; +}; + struct ubase_query_chip_die_cmd { __le16 nl_port_id; __le16 chip_id; @@ -154,8 +159,19 @@ struct ubase_ctx_buf_map { u16 mb_cmd; }; +struct ubase_query_vl_ageing_cmd { + __le16 vl_ageing_en; + u8 rsv[22]; +}; + +struct ubase_query_ctp_vl_offset_cmd { + u8 ctp_vl_offset; + u8 rsv[23]; +}; + int ubase_hw_init(struct ubase_dev *udev); void ubase_hw_uninit(struct ubase_dev *udev); +int ubase_qos_init(struct ubase_dev *udev); int ubase_query_dev_res(struct ubase_dev *udev); int ubase_query_chip_info(struct ubase_dev *udev); int ubase_query_controller_info(struct ubase_dev *udev); diff --git a/drivers/ub/ubase/ubase_qos_hw.c b/drivers/ub/ubase/ubase_qos_hw.c new file mode 100644 index 000000000000..67e28f01c607 --- /dev/null +++ b/drivers/ub/ubase/ubase_qos_hw.c @@ -0,0 +1,582 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include "ubase_cmd.h" +#include "ubase_ctrlq.h" +#include "ubase_hw.h" + +int ubase_query_sl_vl_map(struct ubase_dev *udev, u8 *sl_vl) +{ + struct ubase_config_sl_vl_cmd resp = {0}; + struct ubase_config_sl_vl_cmd req = {0}; + struct ubase_cmd_buf in, out; + int ret; + + req.sl_num = UBASE_MAX_SL_NUM; + __ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_TA_SL_VL_MAP, true, + sizeof(req), &req); + __ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_TA_SL_VL_MAP, false, + sizeof(resp), &resp); + + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret) { + ubase_err(udev, "failed to query sl_vl map, ret = %d.\n", ret); + return ret; + } + + memcpy(sl_vl, resp.sl_vl, UBASE_MAX_SL_NUM); + return 0; +} + +static int ubase_query_vl_ageing(struct ubase_dev *udev, u16 *vl_ageing_en) +{ + struct ubase_query_vl_ageing_cmd resp = {0}; + struct ubase_query_vl_ageing_cmd req = {0}; + struct ubase_cmd_buf in, out; + int ret; + + ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_VL_AGEING_EN, true, + sizeof(req), &req); + ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_VL_AGEING_EN, false, + sizeof(resp), &resp); + + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret) { + ubase_err(udev, + "failed to query vl ageing configuration, ret = %d.\n", + ret); + return ret; + } + + *vl_ageing_en = le16_to_cpu(resp.vl_ageing_en); + + ubase_dbg(udev, "vl_ageing_en bitmap:%u.\n", *vl_ageing_en); + + return 0; +} + +static int ubase_query_ctp_vl_offset(struct ubase_dev *udev, u8 *ctp_vl_offset) +{ + struct ubase_query_ctp_vl_offset_cmd resp = {0}; + struct ubase_query_ctp_vl_offset_cmd req = {0}; + struct ubase_cmd_buf in, out; + int ret; + + ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_CTP_VL_OFFSET, true, + sizeof(req), &req); + ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_CTP_VL_OFFSET, false, + sizeof(resp), &resp); + + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret) { + ubase_err(udev, + "failed to query ctp vl offset, ret = %d.\n", ret); + return ret; + } + + *ctp_vl_offset = resp.ctp_vl_offset; + + ubase_dbg(udev, "ctp_vl_offset:%u.\n", *ctp_vl_offset); + + return 0; +} + +static inline void ubase_parse_udma_req_vl_uboe(struct ubase_dev *udev) +{ + struct ubase_adev_qos *qos = &udev->qos; + + qos->tp_vl_num = qos->vl_num; + memcpy(qos->tp_req_vl, qos->vl, qos->vl_num); +} + +static int ubase_parse_udma_req_vl_ub(struct ubase_dev *udev) +{ + struct ubase_adev_qos *qos = &udev->qos; + unsigned long vl_ageing_en; + int ret; + u8 i; + + ret = ubase_query_vl_ageing(udev, (u16 *)&vl_ageing_en); + if (ret) + return ret; + + for (i = 0; i < qos->vl_num; i++) { + if (test_bit(qos->vl[i], &vl_ageing_en)) + qos->tp_req_vl[qos->tp_vl_num++] = + qos->vl[i]; + else + qos->ctp_req_vl[qos->ctp_vl_num++] = + qos->vl[i]; + } + + return 0; +} + +static int ubase_parse_udma_req_vl(struct ubase_dev *udev) +{ + if (ubase_dev_ubl_supported(udev)) + return ubase_parse_udma_req_vl_ub(udev); + + ubase_parse_udma_req_vl_uboe(udev); + return 0; +} + +static int ubase_check_ctp_resp_vl(struct ubase_dev *udev, u8 ctp_vl_offset) +{ + struct ubase_adev_qos *qos = &udev->qos; + u8 i, ctp_resp_vl_off; + + for (i = 0; i < qos->ctp_vl_num; i++) { + ctp_resp_vl_off = qos->ctp_req_vl[i] + ctp_vl_offset; + if (ctp_resp_vl_off >= UBASE_MAX_VL_NUM) { + ubase_err(udev, + "the %uth ctp_resp_vl(%u) exceed max_vl_num(%u).\n", + i, ctp_resp_vl_off, UBASE_MAX_VL_NUM); + return -EINVAL; + } + } + + return 0; +} + +static int ubase_parse_ctp_resp_vl(struct ubase_dev *udev) +{ + struct ubase_adev_qos *qos = &udev->qos; + u8 ctp_vl_offset; + int ret; + + ret = ubase_query_ctp_vl_offset(udev, &ctp_vl_offset); + if (ret) + return ret; + + ret = ubase_check_ctp_resp_vl(udev, ctp_vl_offset); + if (ret) + return ret; + + qos->ctp_resp_vl_offset = ctp_vl_offset; + + return 0; +} + +static bool ubase_get_vl_sl(struct ubase_dev *udev, u8 vl, u8 *sl, u8 *sl_num) +{ + bool sl_exist = false; + u8 i; + + for (i = 0; i < UBASE_MAX_SL_NUM; i++) { + if (udev->qos.ue_sl_vl[i] == vl) { + sl[(*sl_num)++] = i; + sl_exist = true; + } + } + + return sl_exist; +} + +static int ubase_parse_udma_tp_sl(struct ubase_dev *udev) +{ + struct ubase_adev_qos *qos = &udev->qos; + bool exist; + u8 i; + + for (i = 0; i < qos->tp_vl_num; i++) { + exist = ubase_get_vl_sl(udev, qos->tp_req_vl[i], + qos->tp_sl, &qos->tp_sl_num); + if (!exist) { + ubase_err(udev, + "udma tp req vl(%u) doesn't have a corresponding sl.\n", + qos->tp_req_vl[i]); + return -EINVAL; + } + } + + return 0; +} + +static int ubase_parse_udma_ctp_sl(struct ubase_dev *udev) +{ + struct ubase_adev_qos *qos = &udev->qos; + bool exist; + u8 i; + + for (i = 0; i < qos->ctp_vl_num; i++) { + exist = ubase_get_vl_sl(udev, qos->ctp_req_vl[i], + qos->ctp_sl, &qos->ctp_sl_num); + if (!exist) { + ubase_err(udev, + "udma ctp req vl(%u) doesn't have a corresponding sl.\n", + qos->ctp_req_vl[i]); + return -EINVAL; + } + } + + return 0; +} + +static int ubase_parse_udma_sl(struct ubase_dev *udev) +{ + int ret; + + ret = ubase_parse_udma_tp_sl(udev); + if (ret) + return ret; + + return ubase_parse_udma_ctp_sl(udev); +} + +static void ubase_gather_udma_req_resp_vl(struct ubase_dev *udev, + u8 *req_vl, u8 req_vl_num, + u8 resp_vl_off) +{ + struct ubase_caps *dev_caps = &udev->caps.dev_caps; + struct ubase_adev_qos *qos = &udev->qos; + u8 i, j; + + for (i = 0; i < req_vl_num; i++) { + for (j = 0; j < qos->nic_vl_num; j++) { + if (req_vl[i] == dev_caps->req_vl[j]) { + dev_caps->resp_vl[j] = req_vl[i] + resp_vl_off; + break; + } + } + + if (j < qos->nic_vl_num) + continue; + + dev_caps->req_vl[dev_caps->vl_num] = req_vl[i]; + dev_caps->resp_vl[dev_caps->vl_num] = req_vl[i] + resp_vl_off; + dev_caps->vl_num++; + } +} + +static void ubase_gather_urma_req_resp_vl(struct ubase_dev *udev) +{ + struct ubase_caps *dev_caps = &udev->caps.dev_caps; + struct ubase_adev_qos *qos = &udev->qos; + + memcpy(dev_caps->req_vl, qos->nic_vl, qos->nic_vl_num); + memcpy(dev_caps->resp_vl, qos->nic_vl, qos->nic_vl_num); + dev_caps->vl_num = qos->nic_vl_num; + + /* Restriction: The unic vl can't be used as the dma resp vl. */ + ubase_gather_udma_req_resp_vl(udev, qos->tp_req_vl, + qos->tp_vl_num, + qos->tp_resp_vl_offset); + ubase_gather_udma_req_resp_vl(udev, qos->ctp_req_vl, + qos->ctp_vl_num, + qos->ctp_resp_vl_offset); + + /* dev_caps->vl_num is used for DCB tool configuration. Therefore, + * dev_caps->vl_num cannot exceed IEEE_8021QAZ_MAX_TCS. + */ + dev_caps->vl_num = min(dev_caps->vl_num, IEEE_8021QAZ_MAX_TCS); +} + +static inline void ubase_gather_cdma_req_resp_vl(struct ubase_dev *udev) +{ + struct ubase_adev_qos *qos = &udev->qos; + + ubase_gather_udma_req_resp_vl(udev, qos->ctp_req_vl, + qos->ctp_vl_num, + qos->ctp_resp_vl_offset); +} + +static inline int ubase_parse_udma_resp_vl(struct ubase_dev *udev) +{ + if (ubase_dev_ubl_supported(udev)) + return ubase_parse_ctp_resp_vl(udev); + + return 0; +} + +static int ubase_assign_urma_vl(struct ubase_dev *udev, u8 *urma_sl, + u8 urma_sl_num, u8 *urma_vl, u8 *urma_vl_num) +{ + u8 urma_vl_bitmap[UBASE_MAX_VL_NUM] = {0}; + u8 i, current_vl; + int index = 0; + + for (i = 0; i < urma_sl_num; i++) { + current_vl = udev->qos.ue_sl_vl[urma_sl[i]]; + if (current_vl >= UBASE_MAX_VL_NUM) { + ubase_err(udev, + "urma vl(%u) exceeds the maximum(%u).\n", + current_vl, UBASE_MAX_VL_NUM); + return -EINVAL; + } + + if (urma_vl_bitmap[current_vl] == 0) { + urma_vl[index++] = current_vl; + urma_vl_bitmap[current_vl] = 1; + (*urma_vl_num)++; + } + } + + return 0; +} + +static int ubase_parse_rack_nic_vl(struct ubase_dev *udev) +{ + return ubase_assign_urma_vl(udev, udev->qos.nic_sl, udev->qos.nic_sl_num, + udev->qos.nic_vl, &udev->qos.nic_vl_num); +} + +static int ubase_parse_rack_udma_req_vl(struct ubase_dev *udev) +{ + int ret; + + ret = ubase_assign_urma_vl(udev, udev->qos.sl, + udev->qos.sl_num, udev->qos.vl, + &udev->qos.vl_num); + if (ret) + return ret; + + return ubase_parse_udma_req_vl(udev); +} + +static int ubase_parse_rack_udma_vl(struct ubase_dev *udev) +{ + int ret; + + ret = ubase_parse_rack_udma_req_vl(udev); + if (ret) + return ret; + + return ubase_parse_udma_resp_vl(udev); +} + +static int ubase_parse_rack_cdma_resp_vl(struct ubase_dev *udev) +{ + return ubase_parse_ctp_resp_vl(udev); +} + +static int ubase_parse_rack_cdma_req_sl_vl(struct ubase_dev *udev) +{ + struct ubase_adev_qos *qos = &udev->qos; + bool exist = false; + u8 i; + + for (i = 0; i < qos->vl_num; i++) { + exist = ubase_get_vl_sl(udev, qos->vl[i], qos->ctp_sl, + &qos->ctp_sl_num); + if (exist) + qos->ctp_req_vl[qos->ctp_vl_num++] = qos->vl[i]; + } + + if (!qos->ctp_vl_num) { + ubase_err(udev, "cdma doesn't have any req vl.\n"); + return -EINVAL; + } + + return 0; +} + +static int ubase_parse_rack_cdma_sl_vl(struct ubase_dev *udev) +{ + int ret; + + ret = ubase_parse_rack_cdma_req_sl_vl(udev); + if (ret) + return ret; + + ret = ubase_parse_rack_cdma_resp_vl(udev); + if (ret) + return ret; + + ubase_gather_cdma_req_resp_vl(udev); + return 0; +} + +static inline int ubase_parse_rack_nic_sl_vl(struct ubase_dev *udev) +{ + return ubase_parse_rack_nic_vl(udev); +} + +static inline int ubase_parse_rack_udma_sl_vl(struct ubase_dev *udev) +{ + int ret; + + ret = ubase_parse_rack_udma_vl(udev); + if (ret) + return ret; + + return ubase_parse_udma_sl(udev); +} + +static int ubase_parse_rack_urma_sl_vl(struct ubase_dev *udev) +{ + int ret; + + ret = ubase_parse_rack_nic_sl_vl(udev); + if (ret) + return ret; + + if (ubase_dev_udma_supported(udev)) { + ret = ubase_parse_rack_udma_sl_vl(udev); + if (ret) + return ret; + } + + ubase_gather_urma_req_resp_vl(udev); + return 0; +} + +static int ubase_parse_rack_adev_sl_vl(struct ubase_dev *udev) +{ + if (ubase_dev_cdma_supported(udev)) + return ubase_parse_rack_cdma_sl_vl(udev); + + if (ubase_dev_urma_supported(udev)) + return ubase_parse_rack_urma_sl_vl(udev); + + return 0; +} + +static void ubase_init_udma_dscp_vl(struct ubase_dev *udev) +{ + struct ubase_adev_qos *qos = &udev->qos; + u8 i; + + for (i = 0; i < UBASE_MAX_DSCP; i++) + qos->dscp_vl[i] = qos->tp_req_vl[0]; +} + +static int ubase_parse_sl_vl(struct ubase_dev *udev) +{ + int ret; + + ret = ubase_query_sl_vl_map(udev, udev->qos.ue_sl_vl); + if (ret) + return ret; + + ret = ubase_parse_rack_adev_sl_vl(udev); + if (ret) + return ret; + + if (ubase_dev_udma_supported(udev)) + ubase_init_udma_dscp_vl(udev); + + if (ubase_utp_supported(udev) && ubase_dev_urma_supported(udev)) + udev->caps.unic_caps.tpg.max_cnt = udev->qos.nic_vl_num; + + return 0; +} + +static int ubase_ctrlq_query_vl(struct ubase_dev *udev) +{ + struct ubase_ctrlq_query_vl_resp resp = {0}; + struct ubase_ctrlq_query_vl_req req = {0}; + struct ubase_ctrlq_msg msg = {0}; + unsigned long vl_bitmap; + u8 i, vl_cnt = 0; + int ret; + + msg.service_ver = UBASE_CTRLQ_SER_VER_01; + msg.service_type = UBASE_CTRLQ_SER_TYPE_QOS; + msg.opcode = UBASE_CTRLQ_OPC_QUERY_VL; + msg.need_resp = 1; + msg.is_resp = 0; + msg.in_size = sizeof(req); + msg.in = &req; + msg.out_size = sizeof(resp); + msg.out = &resp; + + ret = __ubase_ctrlq_send(udev, &msg, NULL); + if (ret) { + ubase_err(udev, + "failed to send ctrlq msg when query vl, ret = %d.\n", ret); + return ret; + } + + vl_bitmap = le16_to_cpu(resp.vl_bitmap); + + for (i = 0; i < UBASE_MAX_VL_NUM; i++) + if (test_bit(i, &vl_bitmap)) + udev->qos.vl[vl_cnt++] = i; + + if (!vl_cnt) + return -EBUSY; + + udev->qos.vl_num = vl_cnt; + + ubase_dbg(udev, "ctrlq query vl_bitmap = %lx.\n", vl_bitmap); + + return 0; +} + +static int ubase_ctrlq_query_sl(struct ubase_dev *udev) +{ + struct ubase_ctrlq_query_sl_resp resp = {0}; + struct ubase_ctrlq_query_sl_req req = {0}; + u8 i, unic_sl_cnt = 0, udma_sl_cnt = 0; + struct ubase_ctrlq_msg msg = {0}; + unsigned long unic_sl_bitmap; + unsigned long udma_sl_bitmap; + int ret; + + msg.service_ver = UBASE_CTRLQ_SER_VER_01; + msg.service_type = UBASE_CTRLQ_SER_TYPE_QOS; + msg.opcode = UBASE_CTRLQ_OPC_QUERY_SL; + msg.need_resp = 1; + msg.is_resp = 0; + msg.in_size = sizeof(req); + msg.in = &req; + msg.out_size = sizeof(resp); + msg.out = &resp; + + ret = __ubase_ctrlq_send(udev, &msg, NULL); + if (ret) { + ubase_err(udev, + "failed to send ctrlq msg when query sl, ret = %d.\n", ret); + return ret; + } + + unic_sl_bitmap = le16_to_cpu(resp.unic_sl_bitmap); + udma_sl_bitmap = le16_to_cpu(resp.udma_sl_bitmap); + + for (i = 0; i < UBASE_MAX_SL_NUM; i++) { + if (test_bit(i, &unic_sl_bitmap)) + udev->qos.nic_sl[unic_sl_cnt++] = i; + if (test_bit(i, &udma_sl_bitmap)) + udev->qos.sl[udma_sl_cnt++] = i; + } + + if (!unic_sl_cnt) { + ubase_err(udev, "nic doesn't have any sl.\n"); + return -EIO; + } + + if (ubase_dev_udma_supported(udev) && !udma_sl_cnt) { + ubase_err(udev, "udma doesn't have any sl.\n"); + return -EIO; + } + + udev->qos.nic_sl_num = unic_sl_cnt; + udev->qos.sl_num = udma_sl_cnt; + + ubase_dbg(udev, "ctrlq query unic_sl_bitmap = 0x%lx, udma_sl_bitmap = 0x%lx.\n", + unic_sl_bitmap, udma_sl_bitmap); + + return 0; +} + +int ubase_qos_init(struct ubase_dev *udev) +{ + int ret = 0; + + if (ubase_dev_urma_supported(udev)) + ret = ubase_ctrlq_query_sl(udev); + else if (ubase_dev_cdma_supported(udev)) + ret = ubase_ctrlq_query_vl(udev); + + if (ret) + return ret; + + if (ubase_dev_pmu_supported(udev)) + return 0; + + return ubase_parse_sl_vl(udev); +} diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index 98c4ac1115e7..c2a1124a55a0 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -33,14 +33,19 @@ enum ubase_opcode_type { UBASE_OPC_QUERY_COMM_RSRC_PARAM = 0x0030, UBASE_OPC_QUERY_BUS_EID = 0x0047, + /* NL commands */ + UBASE_OPC_QUERY_VL_AGEING_EN = 0x2343, + /* TP commands */ UBASE_OPC_TP_TIMER_VA_CONFIG = 0x3007, UBASE_OPC_TP_EXTDB_VA_CONFIG = 0x3008, + UBASE_OPC_QUERY_CTP_VL_OFFSET = 0x3112, /* TA commands */ UBASE_OPC_TA_EXTDB_VA_CONFIG = 0x4000, UBASE_OPC_TA_TIMER_VA_CONFIG = 0x4001, UBASE_OPC_QUERY_OOR_CAPS = 0x4200, + UBASE_OPC_QUERY_TA_SL_VL_MAP = 0x4201, /* PHY commands */ UBASE_OPC_QUERY_CHIP_INFO = 0x6201, diff --git a/include/ub/ubase/ubase_comm_ctrlq.h b/include/ub/ubase/ubase_comm_ctrlq.h index 5b08cefe45dd..32b4831ccf86 100644 --- a/include/ub/ubase/ubase_comm_ctrlq.h +++ b/include/ub/ubase/ubase_comm_ctrlq.h @@ -34,6 +34,11 @@ enum ubase_ctrlq_ser_type { UBASE_CTRLQ_SER_TYPE_QOS = 0x04, }; +enum ubase_ctrlq_opc_type_qos { + UBASE_CTRLQ_OPC_QUERY_VL = 0x01, + UBASE_CTRLQ_OPC_QUERY_SL = 0x02, +}; + struct ubase_ctrlq_msg { enum ubase_ctrlq_ser_ver service_ver; enum ubase_ctrlq_ser_type service_type; -- Gitee From 1c57e87da70aa8870f72fc9572e3ff4ae32ee490 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Fri, 19 Sep 2025 10:35:25 +0800 Subject: [PATCH 162/214] ub: ubase: Support to config and query the sl scheduling mode and weight in ETS and TM modes. ANBZ: #45460 commit 442b66e0c7b030a015072a3cb6725601a89b4e8e openEuler. This patch provides interfaces for querying and configuring SL scheduling modes, along with the corresponding weights and priorities. In QoS, SL scheduling is divided into ETS scheduling and TM scheduling, each of which includes two scheduling modes: SP and dwrr. 1. The priority of the SP scheduling mode is higher than that of the dwrr scheduling mode. 2. In ETS scheduling, if multiple SLs are simultaneously in SP scheduling mode, the priority is determined in ascending order of SL from small to large. 3. In TM scheduling, when multiple SLs are in SP scheduling mode, the scheduling weight takes effect. Therefore, the weight of the SP scheduling mode corresponding to ETS must be 0, but the SP scheduling weight of TM cannot be 0. Signed-off-by: Haiqing Fang Signed-off-by: Qian Wu Signed-off-by: Xiongchuan Zhou Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_cmd.h | 61 ++++ drivers/ub/ubase/ubase_dev.c | 12 + drivers/ub/ubase/ubase_hw.h | 12 + drivers/ub/ubase/ubase_qos_hw.c | 469 ++++++++++++++++++++++++++++++ include/ub/ubase/ubase_comm_cmd.h | 5 + include/ub/ubase/ubase_comm_dev.h | 1 + include/ub/ubase/ubase_comm_qos.h | 35 +++ 7 files changed, 595 insertions(+) create mode 100644 include/ub/ubase/ubase_comm_qos.h diff --git a/drivers/ub/ubase/ubase_cmd.h b/drivers/ub/ubase/ubase_cmd.h index 3c230cb11ac6..9452a40d6bf0 100644 --- a/drivers/ub/ubase/ubase_cmd.h +++ b/drivers/ub/ubase/ubase_cmd.h @@ -70,6 +70,67 @@ struct ubase_ue2ue_ctrlq_head { u8 rsv : 6; }; +struct ubase_cfg_ets_vl_sch_cmd { + __le16 vl_bitmap; + u8 rsvd[2]; + u8 vl_bw[UBASE_MAX_VL_NUM]; + __le32 port_bitmap; +}; + +struct ubase_cfg_tm_vl_sch_cmd { + __le16 bus_ue_id; + __le16 vl_bitmap; + __le16 vl_tsa; + u8 rsvd[2]; + u8 vl_bw[UBASE_MAX_VL_NUM]; +}; + +struct ubase_ets_shaping_info { + u8 ir_b; + u8 ir_u; + u8 ir_s; + u8 bs_b; + u8 bs_s; + u8 rsvd[3]; + __le32 rate; +}; + +struct ubase_query_ets_tcg_cmd { + u8 tcg_weight[UBASE_MAX_TCG_NUM]; + __le16 tcg_tc_map[UBASE_MAX_TCG_NUM]; + struct ubase_ets_shaping_info tcg_info[UBASE_MAX_TCG_NUM]; +}; + +struct ubase_query_ets_port_cmd { + struct ubase_ets_shaping_info port_info; +}; + +struct ubase_fst_revert_tbl { + __le16 fst_idx; + u8 queue_ue_num; + u8 queue_que_num; + __le16 queue_vl_num; + u8 resv[2]; +}; + +struct ubase_rqmt_tbl { + __le16 fst_idx; + __le16 start_queue_idx; + __le16 queue_quantity_shift; + u8 resv[2]; +}; + +struct ubase_query_fst_fvt_rqmt_cmd { + __le16 bus_ue_id; + u8 rsv[2]; + __le16 sl_queue_vl_num; + __le16 sl_queue_start_qid; + __le16 fvt_vl_size; + __le16 fvt_rqmt_offset; + struct ubase_fst_revert_tbl fstr_info[UBASE_MAX_VL_NUM]; + struct ubase_rqmt_tbl rqmt_info[UBASE_MAX_VL_NUM]; +}; + struct ubase_query_ueid_cmd { __le32 ueid[UBASE_BUS_EID_LEN]; u32 rsv[2]; diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index 073bc5a368e6..ecc81369f834 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -831,6 +831,18 @@ bool ubase_dbg_default(void) return ubase_debug; } +struct ubase_adev_qos *ubase_get_adev_qos(struct auxiliary_device *adev) +{ + struct ubase_dev *udev; + + if (!adev) + return NULL; + + udev = __ubase_get_udev_by_adev(adev); + return &udev->qos; +} +EXPORT_SYMBOL(ubase_get_adev_qos); + static int ubase_query_bus_eid(struct ubase_dev *udev, struct ubase_bus_eid *eid) { struct ubase_query_ueid_cmd resp = {0}; diff --git a/drivers/ub/ubase/ubase_hw.h b/drivers/ub/ubase/ubase_hw.h index 882f7ea67576..8931093590cf 100644 --- a/drivers/ub/ubase/ubase_hw.h +++ b/drivers/ub/ubase/ubase_hw.h @@ -9,6 +9,8 @@ #include +#include "ubase_cmd.h" + #define UBASE_CTX_REMOVE_ALL (-2) #define UBASE_DEF_CEQ_VECTOR_NUM 1 @@ -171,10 +173,20 @@ struct ubase_query_ctp_vl_offset_cmd { int ubase_hw_init(struct ubase_dev *udev); void ubase_hw_uninit(struct ubase_dev *udev); +int ubase_query_sl_vl_map(struct ubase_dev *udev, u8 *sl_vl); int ubase_qos_init(struct ubase_dev *udev); +int ubase_query_ets_tc(struct ubase_dev *udev, u32 port_bitmap, + u16 vl_bitmap, struct ubase_cfg_ets_vl_sch_cmd *resp); +int ubase_query_ets_tcg(struct ubase_dev *udev, + struct ubase_query_ets_tcg_cmd *resp); +int ubase_query_ets_port(struct ubase_dev *udev, + struct ubase_query_ets_port_cmd *resp); int ubase_query_dev_res(struct ubase_dev *udev); int ubase_query_chip_info(struct ubase_dev *udev); int ubase_query_controller_info(struct ubase_dev *udev); int ubase_query_hw_oor_caps(struct ubase_dev *udev); +int ubase_query_fst_fvt_rqmt(struct ubase_dev *udev, + struct ubase_query_fst_fvt_rqmt_cmd *resp, + u16 bus_ue_id); #endif diff --git a/drivers/ub/ubase/ubase_qos_hw.c b/drivers/ub/ubase/ubase_qos_hw.c index 67e28f01c607..2298a40b6cb3 100644 --- a/drivers/ub/ubase/ubase_qos_hw.c +++ b/drivers/ub/ubase/ubase_qos_hw.c @@ -4,6 +4,8 @@ * */ +#include + #include "ubase_cmd.h" #include "ubase_ctrlq.h" #include "ubase_hw.h" @@ -31,6 +33,443 @@ int ubase_query_sl_vl_map(struct ubase_dev *udev, u8 *sl_vl) return 0; } +static inline unsigned long ubase_convert_sl_vl_bitmap(struct ubase_dev *udev, + unsigned long sl_bitmap) +{ + unsigned long vl_bitmap = 0; + u8 i; + + for (i = 0; i < UBASE_MAX_SL_NUM; i++) { + if (!test_bit(i, &sl_bitmap)) + continue; + + vl_bitmap |= 1 << udev->qos.ue_sl_vl[i]; + } + + return vl_bitmap; +} + +static void ubase_get_vl_sche_info(struct ubase_dev *udev, + struct ubase_sl_priqos *sl_priqos, + unsigned long *vl_bitmap, + u8 *vl_bw, u8 *vl_tsa) +{ + unsigned long sl_bitmap = sl_priqos->sl_bitmap; + u8 i; + + *vl_bitmap = ubase_convert_sl_vl_bitmap(udev, sl_bitmap); + for (i = 0; i < UBASE_MAX_SL_NUM; i++) { + if (!test_bit(i, &sl_bitmap)) + continue; + + vl_bw[udev->qos.ue_sl_vl[i]] = sl_priqos->weight[i]; + vl_tsa[udev->qos.ue_sl_vl[i]] = sl_priqos->sch_mode[i]; + } +} + +static void ubase_prase_tm_vl_sch_resp(struct ubase_dev *udev, + struct ubase_sl_priqos *sl_priqos, + struct ubase_cfg_tm_vl_sch_cmd *resp) +{ + unsigned long tsa_bitmap = le16_to_cpu(resp->vl_tsa); + unsigned long sl_bitmap = sl_priqos->sl_bitmap; + u8 i; + + for (i = 0; i < UBASE_MAX_SL_NUM; i++) { + if (!test_bit(i, &sl_bitmap)) + continue; + + sl_priqos->weight[i] = resp->vl_bw[udev->qos.ue_sl_vl[i]]; + sl_priqos->sch_mode[i] = test_bit(udev->qos.ue_sl_vl[i], + &tsa_bitmap) ? + UBASE_SL_DWRR : UBASE_SL_SP; + } +} + +static void ubase_prase_ets_vl_sch_resp(struct ubase_dev *udev, + struct ubase_sl_priqos *sl_priqos, + struct ubase_cfg_ets_vl_sch_cmd *resp) +{ + unsigned long sl_bitmap = sl_priqos->sl_bitmap; + u8 i; + + for (i = 0; i < UBASE_MAX_SL_NUM; i++) { + if (!test_bit(i, &sl_bitmap)) + continue; + + sl_priqos->weight[i] = resp->vl_bw[udev->qos.ue_sl_vl[i]]; + sl_priqos->sch_mode[i] = sl_priqos->weight[i] ? + UBASE_SL_DWRR : UBASE_SL_SP; + } +} + +static int ubase_check_sp_sch_param(struct ubase_dev *udev, u8 vl_bw, + u32 *bw_sum, u8 vl_idx, bool is_ets) +{ + if (is_ets) { + if (vl_bw) { + ubase_err(udev, + "vl(%u) vl_bw must be 0 in ets sp mode.\n", + vl_idx); + return -EINVAL; + } + + return 0; + } + + if (!vl_bw) { + ubase_err(udev, "vl(%u) vl_bw cannot be 0 in tm sp mode.\n", + vl_idx); + return -EINVAL; + } + + *bw_sum += vl_bw; + + return 0; +} + +static int ubase_check_dwrr_sch_param(struct ubase_dev *udev, u8 vl_bw, + u32 *bw_sum, u8 vl_idx) +{ + if (!vl_bw) { + ubase_err(udev, "vl(%u) bw cannot be 0 in dwrr mode.\n", + vl_idx); + return -EINVAL; + } + + *bw_sum += vl_bw; + + return 0; +} + +static int __ubase_check_qos_sch_param(struct ubase_dev *udev, + unsigned long vl_bitmap, + u8 *vl_bw, u8 *vl_tsa, bool is_ets) +{ +#define UBASE_BW_PERCENT 100 + + u32 bw_sum = 0; + int ret; + u8 i; + + for (i = 0; i < UBASE_MAX_VL_NUM; i++) { + if (!test_bit(i, &vl_bitmap)) + continue; + + switch (vl_tsa[i]) { + case IEEE_8021QAZ_TSA_STRICT: + ret = ubase_check_sp_sch_param(udev, vl_bw[i], &bw_sum, + i, is_ets); + if (ret) + return ret; + break; + case IEEE_8021QAZ_TSA_ETS: + ret = ubase_check_dwrr_sch_param(udev, vl_bw[i], + &bw_sum, i); + if (ret) + return ret; + break; + default: + ubase_err(udev, "not support tc%u tsa model: %u\n", + i, vl_tsa[i]); + return -EINVAL; + } + } + + if (bw_sum && bw_sum != UBASE_BW_PERCENT) { + ubase_err(udev, + "the vl_bw sum does not add up to 100 in %s mode.\n", + is_ets ? "ets dwrr" : "tm sp/dwrr"); + return -EINVAL; + } + + return 0; +} + +static int __ubase_config_tm_vl_sch(struct ubase_dev *udev, u16 vl_bitmap, + u8 *vl_bw, u8 *vl_tsa) +{ + struct ubase_cfg_tm_vl_sch_cmd req = {0}; + struct ubase_cmd_buf in; + u16 tsa_bitmap = 0; + int ret; + u8 i; + + /* the configuration takes effect for all entities. */ + req.bus_ue_id = cpu_to_le16(USHRT_MAX); + req.vl_bitmap = cpu_to_le16(vl_bitmap); + + for (i = 0; i < UBASE_MAX_VL_NUM; i++) + tsa_bitmap |= vl_tsa[i] ? 1 << i : 0; + + req.vl_tsa = cpu_to_le16(tsa_bitmap); + memcpy(req.vl_bw, vl_bw, UBASE_MAX_VL_NUM); + + ubase_fill_inout_buf(&in, UBASE_OPC_TA_VL_SCH_CONFIG, false, + sizeof(req), &req); + + ret = __ubase_cmd_send_in(udev, &in); + if (ret) + ubase_err(udev, "failed to config tm vl sch, ret = %d", ret); + + return ret; +} + +static int __ubase_config_ets_vl_sch(struct ubase_dev *udev, u16 vl_bitmap, + u8 *vl_bw, u32 port_bitmap) +{ + struct ubase_cfg_ets_vl_sch_cmd req = {0}; + struct ubase_cmd_buf in; + int ret; + + req.port_bitmap = cpu_to_le32(port_bitmap); + req.vl_bitmap = cpu_to_le16(vl_bitmap); + memcpy(req.vl_bw, vl_bw, UBASE_MAX_VL_NUM); + + ubase_fill_inout_buf(&in, UBASE_OPC_CFG_ETS_TC_INFO, false, sizeof(req), + &req); + + ret = __ubase_cmd_send_in(udev, &in); + if (ret) + ubase_err(udev, "failed to cfg ets vl sch, ret = %d.", ret); + + return ret; +} + +static int ubase_set_tm_priqos(struct ubase_dev *udev, + struct ubase_sl_priqos *sl_priqos) +{ + u8 vl_tsa[UBASE_MAX_VL_NUM] = {0}; + u8 vl_bw[UBASE_MAX_VL_NUM] = {0}; + unsigned long vl_bitmap = 0; + int ret; + + ubase_get_vl_sche_info(udev, sl_priqos, &vl_bitmap, vl_bw, vl_tsa); + + ret = __ubase_check_qos_sch_param(udev, vl_bitmap, vl_bw, vl_tsa, false); + if (ret) + return ret; + + return __ubase_config_tm_vl_sch(udev, vl_bitmap, vl_bw, vl_tsa); +} + +static int ubase_get_tm_priqos(struct ubase_dev *udev, + struct ubase_sl_priqos *sl_priqos) +{ + struct ubase_cfg_tm_vl_sch_cmd resp = {0}; + struct ubase_cfg_tm_vl_sch_cmd req = {0}; + struct ubase_cmd_buf in, out; + u16 vl_bitmap; + int ret; + + vl_bitmap = ubase_convert_sl_vl_bitmap(udev, sl_priqos->sl_bitmap); + req.vl_bitmap = cpu_to_le16(vl_bitmap); + + ubase_fill_inout_buf(&in, UBASE_OPC_TA_VL_SCH_CONFIG, true, + sizeof(req), &req); + ubase_fill_inout_buf(&out, UBASE_OPC_TA_VL_SCH_CONFIG, false, + sizeof(resp), &resp); + + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret) { + ubase_err(udev, + "failed to get tm vl sch mode and weight, ret = %d.\n", + ret); + return ret; + } + + ubase_prase_tm_vl_sch_resp(udev, sl_priqos, &resp); + + return 0; +} + +static int ubase_set_ets_priqos(struct ubase_dev *udev, + struct ubase_sl_priqos *sl_priqos) +{ + u8 vl_tsa[UBASE_MAX_VL_NUM] = {0}; + u8 vl_bw[UBASE_MAX_VL_NUM] = {0}; + unsigned long vl_bitmap = 0; + int ret; + + ubase_get_vl_sche_info(udev, sl_priqos, &vl_bitmap, vl_bw, vl_tsa); + + ret = __ubase_check_qos_sch_param(udev, vl_bitmap, vl_bw, vl_tsa, true); + if (ret) + return ret; + + return __ubase_config_ets_vl_sch(udev, vl_bitmap, vl_bw, + sl_priqos->port_bitmap); +} + +int ubase_query_ets_tc(struct ubase_dev *udev, u32 port_bitmap, + u16 vl_bitmap, struct ubase_cfg_ets_vl_sch_cmd *resp) +{ + struct ubase_cfg_ets_vl_sch_cmd req = {0}; + struct ubase_cmd_buf in, out; + int ret; + + req.port_bitmap = cpu_to_le32(port_bitmap); + req.vl_bitmap = cpu_to_le16(vl_bitmap); + + ubase_fill_inout_buf(&in, UBASE_OPC_CFG_ETS_TC_INFO, true, + sizeof(req), &req); + ubase_fill_inout_buf(&out, UBASE_OPC_CFG_ETS_TC_INFO, false, + sizeof(*resp), resp); + + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret) + ubase_err(udev, + "failed to query ets tc info, ret = %d.\n", ret); + + return ret; +} + +static int ubase_get_ets_priqos(struct ubase_dev *udev, + struct ubase_sl_priqos *sl_priqos) +{ + struct ubase_cfg_ets_vl_sch_cmd resp = {0}; + u32 port_bitmap; + u16 vl_bitmap; + int ret; + + vl_bitmap = ubase_convert_sl_vl_bitmap(udev, sl_priqos->sl_bitmap); + port_bitmap = sl_priqos->port_bitmap; + + ret = ubase_query_ets_tc(udev, port_bitmap, vl_bitmap, &resp); + if (ret) + return ret; + + ubase_prase_ets_vl_sch_resp(udev, sl_priqos, &resp); + + return 0; +} + +int ubase_query_ets_tcg(struct ubase_dev *udev, + struct ubase_query_ets_tcg_cmd *resp) +{ + struct ubase_query_ets_tcg_cmd req = {0}; + struct ubase_cmd_buf in, out; + int ret; + + ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_ETS_TCG_INFO, true, + sizeof(req), &req); + ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_ETS_TCG_INFO, false, + sizeof(*resp), resp); + + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret) + ubase_err(udev, + "failed to query ets tcg info, ret = %d.\n", ret); + + return ret; +} + +int ubase_query_ets_port(struct ubase_dev *udev, + struct ubase_query_ets_port_cmd *resp) +{ + struct ubase_query_ets_port_cmd req = {0}; + struct ubase_cmd_buf in, out; + int ret; + + ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_ETS_PORT_INFO, true, + sizeof(req), &req); + ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_ETS_PORT_INFO, false, + sizeof(*resp), resp); + + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret) + ubase_err(udev, + "failed to query ets port info, ret = %d.\n", ret); + + return ret; +} + +int ubase_query_fst_fvt_rqmt(struct ubase_dev *udev, + struct ubase_query_fst_fvt_rqmt_cmd *resp, + u16 bus_ue_id) +{ + struct ubase_query_fst_fvt_rqmt_cmd req = {0}; + struct ubase_cmd_buf in, out; + int ret; + + req.bus_ue_id = cpu_to_le16(bus_ue_id); + + ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_FST_FVT_RQMT, true, + sizeof(req), &req); + ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_FST_FVT_RQMT, false, + sizeof(*resp), resp); + + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret == -EPERM) + return -EOPNOTSUPP; + if (ret) + ubase_err(udev, + "failed to query fst fvt rqmt info, ret=%d.\n", ret); + + return ret; +} + +int ubase_check_qos_sch_param(struct auxiliary_device *adev, u16 vl_bitmap, + u8 *vl_bw, u8 *vl_tsa, bool is_ets) +{ + struct ubase_dev *udev; + + if (!adev || !vl_tsa || !vl_bw) + return -EINVAL; + + udev = __ubase_get_udev_by_adev(adev); + + return __ubase_check_qos_sch_param(udev, vl_bitmap, vl_bw, vl_tsa, + is_ets); +} +EXPORT_SYMBOL(ubase_check_qos_sch_param); + +int ubase_config_tm_vl_sch(struct auxiliary_device *adev, u16 vl_bitmap, + u8 *vl_bw, u8 *vl_tsa) +{ + struct ubase_dev *udev; + + if (!adev || !vl_bw || !vl_tsa) + return -EINVAL; + + udev = __ubase_get_udev_by_adev(adev); + + return __ubase_config_tm_vl_sch(udev, vl_bitmap, vl_bw, vl_tsa); +} +EXPORT_SYMBOL(ubase_config_tm_vl_sch); + +int ubase_set_priqos_info(struct device *dev, struct ubase_sl_priqos *sl_priqos) +{ + struct ubase_dev *udev; + + if (!dev || !sl_priqos || !sl_priqos->sl_bitmap) + return -EINVAL; + + udev = dev_get_drvdata(dev); + + if (sl_priqos->port_bitmap) + return ubase_set_ets_priqos(udev, sl_priqos); + + return ubase_set_tm_priqos(udev, sl_priqos); +} +EXPORT_SYMBOL(ubase_set_priqos_info); + +int ubase_get_priqos_info(struct device *dev, struct ubase_sl_priqos *sl_priqos) +{ + struct ubase_dev *udev; + + if (!dev || !sl_priqos || !sl_priqos->sl_bitmap) + return -EINVAL; + + udev = dev_get_drvdata(dev); + + if (sl_priqos->port_bitmap) + return ubase_get_ets_priqos(udev, sl_priqos); + + return ubase_get_tm_priqos(udev, sl_priqos); +} +EXPORT_SYMBOL(ubase_get_priqos_info); + static int ubase_query_vl_ageing(struct ubase_dev *udev, u16 *vl_ageing_en) { struct ubase_query_vl_ageing_cmd resp = {0}; @@ -580,3 +1019,33 @@ int ubase_qos_init(struct ubase_dev *udev) return ubase_parse_sl_vl(udev); } + +static bool ubase_is_udma_tp_vl(struct ubase_adev_qos *qos, u8 vl) +{ + u8 i; + + for (i = 0; i < qos->tp_vl_num; i++) { + if (qos->tp_req_vl[i] == vl) + return true; + } + + return false; +} + +void ubase_update_udma_dscp_vl(struct auxiliary_device *adev, u8 *dscp_vl, + u8 dscp_num) +{ + struct ubase_adev_qos *qos; + u8 i, arr_len; + + if (!adev || !dscp_vl) + return; + + qos = ubase_get_adev_qos(adev); + arr_len = min(UBASE_MAX_DSCP, dscp_num); + + for (i = 0; i < arr_len; i++) + qos->dscp_vl[i] = ubase_is_udma_tp_vl(qos, dscp_vl[i]) ? + dscp_vl[i] : qos->tp_req_vl[0]; +} +EXPORT_SYMBOL(ubase_update_udma_dscp_vl); diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index c2a1124a55a0..f4459c7c016e 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -34,6 +34,9 @@ enum ubase_opcode_type { UBASE_OPC_QUERY_BUS_EID = 0x0047, /* NL commands */ + UBASE_OPC_CFG_ETS_TC_INFO = 0x2340, + UBASE_OPC_QUERY_ETS_TCG_INFO = 0x2341, + UBASE_OPC_QUERY_ETS_PORT_INFO = 0x2342, UBASE_OPC_QUERY_VL_AGEING_EN = 0x2343, /* TP commands */ @@ -46,6 +49,8 @@ enum ubase_opcode_type { UBASE_OPC_TA_TIMER_VA_CONFIG = 0x4001, UBASE_OPC_QUERY_OOR_CAPS = 0x4200, UBASE_OPC_QUERY_TA_SL_VL_MAP = 0x4201, + UBASE_OPC_TA_VL_SCH_CONFIG = 0x4202, + UBASE_OPC_QUERY_FST_FVT_RQMT = 0x4212, /* PHY commands */ UBASE_OPC_QUERY_CHIP_INFO = 0x6201, diff --git a/include/ub/ubase/ubase_comm_dev.h b/include/ub/ubase/ubase_comm_dev.h index 3f7f3d1c8540..effbf70f028b 100644 --- a/include/ub/ubase/ubase_comm_dev.h +++ b/include/ub/ubase/ubase_comm_dev.h @@ -223,6 +223,7 @@ struct ubase_caps *ubase_get_dev_caps(struct auxiliary_device *adev); struct ubase_adev_caps *ubase_get_unic_caps(struct auxiliary_device *adev); struct ubase_adev_caps *ubase_get_udma_caps(struct auxiliary_device *adev); struct ubase_adev_caps *ubase_get_cdma_caps(struct auxiliary_device *adev); +struct ubase_adev_qos *ubase_get_adev_qos(struct auxiliary_device *adev); void ubase_virt_register(struct auxiliary_device *adev, void (*virt_handler)(struct auxiliary_device *adev, diff --git a/include/ub/ubase/ubase_comm_qos.h b/include/ub/ubase/ubase_comm_qos.h new file mode 100644 index 000000000000..01fc6942ca01 --- /dev/null +++ b/include/ub/ubase/ubase_comm_qos.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef _UB_UBASE_COMM_QOS_H_ +#define _UB_UBASE_COMM_QOS_H_ + +#include +#include +#include + +enum ubase_sl_sched_mode { + UBASE_SL_SP = IEEE_8021QAZ_TSA_STRICT, + UBASE_SL_DWRR = IEEE_8021QAZ_TSA_ETS, +}; + +struct ubase_sl_priqos { + u32 port_bitmap; + u32 sl_bitmap; + u8 weight[UBASE_MAX_SL_NUM]; + u8 sch_mode[UBASE_MAX_SL_NUM]; +}; + +int ubase_set_priqos_info(struct device *dev, struct ubase_sl_priqos *sl_priqos); +int ubase_get_priqos_info(struct device *dev, struct ubase_sl_priqos *sl_priqos); +int ubase_check_qos_sch_param(struct auxiliary_device *adev, u16 vl_bitmap, + u8 *vl_bw, u8 *vl_tsa, bool is_ets); +int ubase_config_tm_vl_sch(struct auxiliary_device *adev, u16 vl_bitmap, + u8 *vl_bw, u8 *vl_tsa); +void ubase_update_udma_dscp_vl(struct auxiliary_device *adev, u8 *dscp_vl, + u8 dscp_num); + +#endif -- Gitee From c17d89d20a9f651398049be557c0b5f2eb10f188 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Fri, 19 Sep 2025 11:31:29 +0800 Subject: [PATCH 163/214] ub: ubase: support create and delete UM TP/TPG ANBZ: #45460 commit 6e36d63bd16ef89c5bbe94f791e634fe0f7502ad openEuler. The UB system uses TP/TPG resources to establish links. If control plane is available, TP/TPG resources are managed by control plane. Services apply for resources from control plane when required. This patch adds the capabilities of applying for and destroying UM TP/TPG for the UBASE driver. When the UBASE driver is loaded, UBASE driver instructs the control plane to create and destroy UM TP/TPG. Signed-off-by: Haibin Lu Signed-off-by: Xiongchuan Zhou Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/Makefile | 2 +- drivers/ub/ubase/ubase_ctrlq.h | 26 ++++ drivers/ub/ubase/ubase_ctrlq_tp.c | 226 ++++++++++++++++++++++++++++ drivers/ub/ubase/ubase_ctrlq_tp.h | 16 ++ drivers/ub/ubase/ubase_eq.c | 34 +++++ drivers/ub/ubase/ubase_hw.c | 10 ++ drivers/ub/ubase/ubase_tp.c | 37 +++++ drivers/ub/ubase/ubase_tp.h | 32 ++++ include/ub/ubase/ubase_comm_ctrlq.h | 6 + 9 files changed, 388 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubase/ubase_ctrlq_tp.c create mode 100644 drivers/ub/ubase/ubase_ctrlq_tp.h create mode 100644 drivers/ub/ubase/ubase_tp.c create mode 100644 drivers/ub/ubase/ubase_tp.h diff --git a/drivers/ub/ubase/Makefile b/drivers/ub/ubase/Makefile index f660def9e25e..8999c24a949a 100644 --- a/drivers/ub/ubase/Makefile +++ b/drivers/ub/ubase/Makefile @@ -12,7 +12,7 @@ MODULE_NAME := ubase UBASE_OBJS := ubase_main.o ubase_dev.o ubase_hw.o ubase_cmd.o ubase_ctrlq.o \ debugfs/ubase_debugfs.o ubase_eq.o ubase_mailbox.o ubase_ubus.o \ debugfs/ubase_ctx_debugfs.o debugfs/ubase_qos_debugfs.o \ - ubase_qos_hw.o + ubase_qos_hw.o ubase_tp.o ubase_ctrlq_tp.o $(MODULE_NAME)-objs := $(UBASE_OBJS) obj-$(CONFIG_UB_UBASE) := ubase.o diff --git a/drivers/ub/ubase/ubase_ctrlq.h b/drivers/ub/ubase/ubase_ctrlq.h index dc7239bc627e..330478460626 100644 --- a/drivers/ub/ubase/ubase_ctrlq.h +++ b/drivers/ub/ubase/ubase_ctrlq.h @@ -30,6 +30,32 @@ struct ubase_ctrlq_ue_info { u16 bus_ue_id; }; +struct ubase_ctrlq_create_tp_req { + u8 trans_type : 4; + u8 rsv0 : 4; + u8 vl; + u8 rsv1[2]; +}; + +struct ubase_ctrlq_create_tp_resp { + __le32 tpgn : 24; + __le32 rsv : 8; + __le32 tpn_cnt : 8; + __le32 start_tpn : 24; +}; + +struct ubase_ctrlq_destroy_tp_req { + u8 trans_type : 4; + u8 rsv0 : 4; + u8 vl; + u8 rsv1[2]; +}; + +struct ubase_ctrlq_tp_fd_req { + __le32 tpn : 24; + __le32 rsv : 8; +}; + struct ubase_ctrlq_query_vl_resp { __le16 vl_bitmap; u8 rsv[18]; diff --git a/drivers/ub/ubase/ubase_ctrlq_tp.c b/drivers/ub/ubase/ubase_ctrlq_tp.c new file mode 100644 index 000000000000..5ebcb609a205 --- /dev/null +++ b/drivers/ub/ubase/ubase_ctrlq_tp.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include + +#include "ubase_ctrlq.h" +#include "ubase_dev.h" +#include "ubase_tp.h" +#include "ubase_ctrlq_tp.h" + +#define UBASE_TRANS_TYPE_UM_TP 0x2 +#define UBASE_TPG_FLAG 0x1 + +int ubase_notify_tp_fd_by_ctrlq(struct ubase_dev *udev, u32 tpn) +{ + struct ubase_tpg *tpg = udev->tp_ctx.tpg; + struct ubase_ctrlq_tp_fd_req req = {0}; + struct ubase_ctrlq_msg msg = {0}; + int ret, tmp_resp; + u32 i; + + spin_lock(&udev->tp_ctx.tpg_lock); + if (!tpg) { + spin_unlock(&udev->tp_ctx.tpg_lock); + ubase_warn(udev, + "ubase tpg res does not exist, tpn = %u.\n", tpn); + return 0; + } + + for (i = 0; i < udev->caps.unic_caps.tpg.max_cnt; i++) { + if (tpn >= tpg[i].start_tpn && + tpn < tpg[i].start_tpn + tpg[i].tp_cnt) { + ubase_dbg(udev, + "receive tp flush done AE, tpn:%u, tpgn:%u.\n", + tpn, i); + break; + } + } + spin_unlock(&udev->tp_ctx.tpg_lock); + + msg.service_ver = UBASE_CTRLQ_SER_VER_01; + msg.service_type = UBASE_CTRLQ_SER_TYPE_TP_ACL; + msg.opcode = UBASE_CTRLQ_OPC_TP_FLUSH_DONE; + msg.need_resp = 1; + msg.in_size = sizeof(req); + msg.in = &req; + msg.out_size = sizeof(tmp_resp); + msg.out = &tmp_resp; + req.tpn = cpu_to_le32(tpn); + + ret = __ubase_ctrlq_send(udev, &msg, NULL); + if (ret) + ubase_err(udev, "failed to notify tp flush done, ret = %d.\n", + ret); + + spin_lock(&udev->tp_ctx.tpg_lock); + if (udev->tp_ctx.tpg && i < udev->caps.unic_caps.tpg.max_cnt) + atomic_inc(&tpg[i].tp_fd_cnt); + else + ubase_warn(udev, + "ubase tpg res does not exist, tpn = %u.\n", tpn); + spin_unlock(&udev->tp_ctx.tpg_lock); + + return ret; +} + +static int ubase_create_tp_tpg_by_ctrlq(struct ubase_dev *udev, u32 vl) +{ + struct ubase_tp_layer_ctx *tp_ctx = &udev->tp_ctx; + struct ubase_ctrlq_create_tp_resp resp = {0}; + struct ubase_ctrlq_create_tp_req req = {0}; + struct ubase_ctrlq_msg msg = {0}; + int ret; + + msg.service_ver = UBASE_CTRLQ_SER_VER_01; + msg.service_type = UBASE_CTRLQ_SER_TYPE_TP_ACL; + msg.opcode = UBASE_CTRLQ_OPC_CREATE_TP; + msg.need_resp = 1; + msg.in_size = sizeof(req); + msg.in = &req; + msg.out_size = sizeof(resp); + msg.out = &resp; + + req.trans_type = UBASE_TRANS_TYPE_UM_TP; + req.vl = (u8)vl; + + ret = __ubase_ctrlq_send(udev, &msg, NULL); + if (ret && ret != -EEXIST) { + ubase_err(udev, "failed to alloc tp tpg, ret = %d.\n", ret); + return ret; + } + + tp_ctx->tpg[vl].mb_tpgn = le32_to_cpu(resp.tpgn); + tp_ctx->tpg[vl].start_tpn = le32_to_cpu(resp.start_tpn); + tp_ctx->tpg[vl].tp_cnt = resp.tpn_cnt; + + if (tp_ctx->tpg[vl].mb_tpgn != vl) + ubase_warn(udev, "unexpected tpgn, vl = %u, tpgn = %u.\n", + vl, tp_ctx->tpg[vl].mb_tpgn); + + return 0; +} + +static void ubase_wait_tp_flush_done_by_ctrlq(struct ubase_dev *udev, u32 vl) +{ + struct ubase_tpg *tpg = &udev->tp_ctx.tpg[vl]; + int i; + + for (i = 0; i < UBASE_WAIT_TP_FLUSH_TOTAL_STEPS; i++) { + msleep(1 << i); + + if (atomic_read(&tpg->tp_fd_cnt) == tpg->tp_cnt) + return; + } + + ubase_warn(udev, + "wait tp flush done timeout, tpgn = %u, tp_fd_cnt = %u.\n", + vl, atomic_read(&tpg->tp_fd_cnt)); +} + +static void ubase_destroy_tp_tpg_by_ctrlq(struct ubase_dev *udev, u32 vl) +{ + struct ubase_ctrlq_destroy_tp_req req = {0}; + struct ubase_ctrlq_msg msg = {0}; + int tmp_resp, ret; + + msg.service_ver = UBASE_CTRLQ_SER_VER_01; + msg.service_type = UBASE_CTRLQ_SER_TYPE_TP_ACL; + msg.opcode = UBASE_CTRLQ_OPC_DESTROY_TP; + msg.need_resp = 1; + msg.in_size = sizeof(req); + msg.in = &req; + msg.out_size = sizeof(tmp_resp); + msg.out = &tmp_resp; + + req.vl = (u8)vl; + req.trans_type = UBASE_TRANS_TYPE_UM_TP; + + ret = __ubase_ctrlq_send(udev, &msg, NULL); + if (ret) { + ubase_err(udev, + "failed to send destroy tp tpg request, tpgn = %u, ret = %d.\n", + vl, ret); + return; + } + + ubase_wait_tp_flush_done_by_ctrlq(udev, vl); +} + +static void ubase_destroy_multi_tp_tpg_by_ctrlq(struct ubase_dev *udev, u32 num) +{ + u32 idx; + + for (idx = 0; idx < num; idx++) + ubase_destroy_tp_tpg_by_ctrlq(udev, idx); +} + +static int ubase_create_multi_tp_tpg_by_ctrlq(struct ubase_dev *udev) +{ + int ret; + u32 i; + + for (i = 0; i < udev->caps.unic_caps.tpg.max_cnt; i++) { + atomic_set(&udev->tp_ctx.tpg[i].tp_fd_cnt, 0); + ret = ubase_create_tp_tpg_by_ctrlq(udev, i); + if (ret) { + ubase_err(udev, "failed to create tp tpg, tpgn = %u, ret = %d.\n", + i, ret); + goto err_create_tp_tpg; + } + } + + return 0; + +err_create_tp_tpg: + ubase_destroy_multi_tp_tpg_by_ctrlq(udev, i); + + return ret; +} + +void ubase_dev_uninit_rack_tp_tpg(struct ubase_dev *udev) +{ + struct ubase_adev_caps *unic_caps = &udev->caps.unic_caps; + struct ubase_tp_layer_ctx *tp_ctx = &udev->tp_ctx; + u32 num = unic_caps->tpg.max_cnt; + + if (!tp_ctx->tpg) + return; + + if (!test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + ubase_destroy_multi_tp_tpg_by_ctrlq(udev, num); + + spin_lock(&tp_ctx->tpg_lock); + devm_kfree(udev->dev, tp_ctx->tpg); + tp_ctx->tpg = NULL; + spin_unlock(&tp_ctx->tpg_lock); +} + +int ubase_dev_init_rack_tp_tpg(struct ubase_dev *udev) +{ + struct ubase_adev_caps *unic_caps = &udev->caps.unic_caps; + struct ubase_tp_layer_ctx *tp_ctx = &udev->tp_ctx; + int ret; + + spin_lock(&tp_ctx->tpg_lock); + tp_ctx->tpg = devm_kcalloc(udev->dev, unic_caps->tpg.max_cnt, + sizeof(struct ubase_tpg), GFP_ATOMIC); + if (!tp_ctx->tpg) { + spin_unlock(&tp_ctx->tpg_lock); + return -ENOMEM; + } + spin_unlock(&tp_ctx->tpg_lock); + + ret = ubase_create_multi_tp_tpg_by_ctrlq(udev); + if (ret) { + spin_lock(&tp_ctx->tpg_lock); + devm_kfree(udev->dev, tp_ctx->tpg); + tp_ctx->tpg = NULL; + spin_unlock(&tp_ctx->tpg_lock); + } + + return ret; +} diff --git a/drivers/ub/ubase/ubase_ctrlq_tp.h b/drivers/ub/ubase/ubase_ctrlq_tp.h new file mode 100644 index 000000000000..3d647d5d1625 --- /dev/null +++ b/drivers/ub/ubase/ubase_ctrlq_tp.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UBASE_CTRLQ_TP_H__ +#define __UBASE_CTRLQ_TP_H__ + +#include "ubase_dev.h" + +int ubase_notify_tp_fd_by_ctrlq(struct ubase_dev *udev, u32 tp_num); +void ubase_dev_uninit_rack_tp_tpg(struct ubase_dev *udev); +int ubase_dev_init_rack_tp_tpg(struct ubase_dev *udev); + +#endif diff --git a/drivers/ub/ubase/ubase_eq.c b/drivers/ub/ubase/ubase_eq.c index 4c092ea533bb..ae8ad2b51420 100644 --- a/drivers/ub/ubase/ubase_eq.c +++ b/drivers/ub/ubase/ubase_eq.c @@ -10,6 +10,7 @@ #include "ubase_cmd.h" #include "ubase_dev.h" #include "ubase_mailbox.h" +#include "ubase_tp.h" #include "ubase_trace.h" #include "ubase_eq.h" @@ -193,12 +194,40 @@ static int ubase_reg_event_handler(struct ubase_dev *udev) return event_cause ? IRQ_HANDLED : IRQ_NONE; } +static bool ubase_is_udma_tp_event(struct ubase_dev *udev, u32 tpn) +{ + struct ubase_tpg *tpg = udev->tp_ctx.tpg; + u32 i; + + spin_lock(&udev->tp_ctx.tpg_lock); + if (!tpg) { + ubase_warn(udev, "unexpected tp event, tpn = %u.\n", tpn); + spin_unlock(&udev->tp_ctx.tpg_lock); + return true; + } + + for (i = 0; i < udev->caps.unic_caps.tpg.max_cnt; i++) { + if (tpn >= tpg[i].start_tpn && + tpn < tpg[i].start_tpn + tpg[i].tp_cnt) { + spin_unlock(&udev->tp_ctx.tpg_lock); + return false; + } + } + spin_unlock(&udev->tp_ctx.tpg_lock); + + return true; +} + static bool ubase_is_udma_event(struct ubase_dev *udev, u8 event_type, u8 sub_type, struct ubase_aeqe *aeqe) { + u32 queue_id = aeqe->event.queue_event.num; + switch (event_type) { case UBASE_EVENT_TYPE_CHECK_TOKEN: return true; + case UBASE_EVENT_TYPE_TP_FLUSH_DONE: + return ubase_is_udma_tp_event(udev, queue_id); default: break; } @@ -990,6 +1019,11 @@ int ubase_register_ae_event(struct ubase_dev *udev) UBASE_EVENT_TYPE_MB, { ubase_cmd_mbx_event_cb }, udev + }, { + UBASE_DRV_UNIC, + UBASE_EVENT_TYPE_TP_FLUSH_DONE, + { ubase_ae_tp_flush_done }, + udev } }; struct ubase_aeq *aeq = &udev->irq_table.aeq; diff --git a/drivers/ub/ubase/ubase_hw.c b/drivers/ub/ubase/ubase_hw.c index 117859341946..a6ad242493bd 100644 --- a/drivers/ub/ubase/ubase_hw.c +++ b/drivers/ub/ubase/ubase_hw.c @@ -12,6 +12,7 @@ #include "ubase_cmd.h" #include "ubase_dev.h" #include "ubase_mailbox.h" +#include "ubase_tp.h" #include "ubase_hw.h" #define UBASE_CTX_REMOVE_ALL (-2) @@ -715,10 +716,18 @@ int ubase_hw_init(struct ubase_dev *udev) if (ret) goto err_init_ta_tp_ext_buf; + ret = ubase_dev_init_tp_tpg(udev); + if (ret) { + ubase_err(udev, "failed to init tp & tpg, ret = %d.\n", ret); + goto err_init_tp_tpg; + } + set_bit(UBASE_STATE_CTX_READY_B, &udev->state_bits); return 0; +err_init_tp_tpg: + ubase_uninit_ta_tp_ext_buf(udev); err_init_ta_tp_ext_buf: ubase_uninit_ctx_buf(udev); @@ -729,6 +738,7 @@ void ubase_hw_uninit(struct ubase_dev *udev) { clear_bit(UBASE_STATE_CTX_READY_B, &udev->state_bits); + ubase_dev_uninit_tp_tpg(udev); ubase_uninit_ta_tp_ext_buf(udev); if (!test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) diff --git a/drivers/ub/ubase/ubase_tp.c b/drivers/ub/ubase/ubase_tp.c new file mode 100644 index 000000000000..32b75d107136 --- /dev/null +++ b/drivers/ub/ubase/ubase_tp.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include "ubase_ctrlq_tp.h" +#include "ubase_tp.h" + +int ubase_ae_tp_flush_done(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct ubase_event_nb *ev_nb = container_of(nb, struct ubase_event_nb, nb); + struct ubase_dev *udev = (struct ubase_dev *)ev_nb->back; + struct ubase_aeq_notify_info *info = data; + u32 tp_num; + + tp_num = info->aeqe->event.queue_event.num; + + return ubase_notify_tp_fd_by_ctrlq(udev, tp_num); +} + +int ubase_dev_init_tp_tpg(struct ubase_dev *udev) +{ + if (!ubase_utp_supported(udev) || !ubase_dev_urma_supported(udev)) + return 0; + + return ubase_dev_init_rack_tp_tpg(udev); +} + +void ubase_dev_uninit_tp_tpg(struct ubase_dev *udev) +{ + if (!ubase_utp_supported(udev) || !ubase_dev_urma_supported(udev)) + return; + + ubase_dev_uninit_rack_tp_tpg(udev); +} diff --git a/drivers/ub/ubase/ubase_tp.h b/drivers/ub/ubase/ubase_tp.h new file mode 100644 index 000000000000..3d1d14dd7a25 --- /dev/null +++ b/drivers/ub/ubase/ubase_tp.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UBASE_TP_H__ +#define __UBASE_TP_H__ + +#include + +#include "ubase_dev.h" + +#define UBASE_WAIT_TP_FLUSH_TOTAL_STEPS 12 + +struct ubase_tpg { + u32 mb_tpgn; + u8 tpg_state; + u8 vl; + atomic_t tp_fd_cnt; + u8 tp_cnt; + unsigned long valid_tp; + u32 start_tpn; + u32 tp_shift; +}; + +int ubase_ae_tp_flush_done(struct notifier_block *nb, unsigned long event, + void *data); +int ubase_dev_init_tp_tpg(struct ubase_dev *udev); +void ubase_dev_uninit_tp_tpg(struct ubase_dev *udev); + +#endif diff --git a/include/ub/ubase/ubase_comm_ctrlq.h b/include/ub/ubase/ubase_comm_ctrlq.h index 32b4831ccf86..62ced5535723 100644 --- a/include/ub/ubase/ubase_comm_ctrlq.h +++ b/include/ub/ubase/ubase_comm_ctrlq.h @@ -34,6 +34,12 @@ enum ubase_ctrlq_ser_type { UBASE_CTRLQ_SER_TYPE_QOS = 0x04, }; +enum ubase_ctrlq_opc_type { + UBASE_CTRLQ_OPC_CREATE_TP = 0x11, + UBASE_CTRLQ_OPC_DESTROY_TP = 0x12, + UBASE_CTRLQ_OPC_TP_FLUSH_DONE = 0x14, +}; + enum ubase_ctrlq_opc_type_qos { UBASE_CTRLQ_OPC_QUERY_VL = 0x01, UBASE_CTRLQ_OPC_QUERY_SL = 0x02, -- Gitee From 9460a9b073728737d5912f6dd1b859f9d866306d Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Fri, 19 Sep 2025 18:11:55 +0800 Subject: [PATCH 164/214] ub: ubase: Provide an entity_list query interface. ANBZ: #45460 commit 5668f0ce7e03b56cd3fa82b7ad42123df35c2ada openEuler. This patch adds a management framework for the User Entity (UE) to the ubase driver, primarily implementing the following functionalities: 1. **UE List Management**: - Added functions for adding, deleting, and modifying UE nodes (ubase_add_ue_list/ubase_del_ue_list/ubase_modify_ue_list). - Utilizes a mutex (ue_list_lock) to ensure thread-safe operations on the UE list. - Supports linked list management with sorting by bus UE ID. 2. **Initialization Framework**: - Introduced a new UE initialization function, ubase_ue_init, integrated into the device initialization process. - Implements start index initialization (ubase_init_start_idx) for resource allocation. - Adds initialization for various locking mechanisms and completion counts. 3. **Resource Management**: - Initializes statistical information locks (stats_lock) and activation record locks. - Sets TPG context locks (tpg_lock) and activation context locks. - Initializes activation completion counts for synchronization operations. 4. **Cleanup Mechanism**: - Provides a UE uninitialization function, ubase_ue_uninit. - Ensures proper release of all lock resources. - Integrates into the device unloading process. This implementation provides a comprehensive infrastructure for user entity management in the ubase driver, supporting resource allocation and synchronization management in multi-UE environments, and offering the necessary framework support for subsequent advanced functionalities. Signed-off-by: Fengyan Mu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_dev.c | 60 +++++++++++++++++++++++++++- drivers/ub/ubase/ubase_hw.c | 66 +++++++++++++++++++++++++++++++ drivers/ub/ubase/ubase_hw.h | 2 + include/ub/ubase/ubase_comm_dev.h | 5 +++ 4 files changed, 132 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index ecc81369f834..fbf4fb78bc40 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -602,7 +602,7 @@ static const struct ubase_init_function ubase_init_func_map[] = { }, { "init ue", UBASE_SUP_NO_PMU, 0, - NULL, NULL + ubase_ue_init, ubase_ue_uninit }, { "init hw", UBASE_SUP_NO_PMU, 1, @@ -807,11 +807,69 @@ struct ubase_adev_caps *ubase_get_unic_caps(struct auxiliary_device *adev) } EXPORT_SYMBOL(ubase_get_unic_caps); +static bool ubase_add_ue_list(struct ubase_dev *udev, u16 bus_ue_id) +{ + struct ubase_ue_node *pos_node, *tmp_node, *new_node; + + new_node = kzalloc(sizeof(*new_node), GFP_KERNEL); + if (!new_node) { + ubase_err(udev, "failed to alloc ue node.\n"); + return false; + } + new_node->bus_ue_id = bus_ue_id; + + mutex_lock(&udev->ue_list_lock); + list_for_each_entry_safe(pos_node, tmp_node, &udev->ue_list, list) { + if (pos_node->bus_ue_id == bus_ue_id) { + kfree(new_node); + mutex_unlock(&udev->ue_list_lock); + return false; + } else if (bus_ue_id < pos_node->bus_ue_id) { + list_add(&new_node->list, pos_node->list.prev); + goto add_ue_end; + } + } + list_add_tail(&new_node->list, &udev->ue_list); + +add_ue_end: + mutex_unlock(&udev->ue_list_lock); + return true; +} + +static bool ubase_del_ue_list(struct ubase_dev *udev, u16 bus_ue_id) +{ + struct ubase_ue_node *pos_node, *tmp_node; + + mutex_lock(&udev->ue_list_lock); + list_for_each_entry_safe(pos_node, tmp_node, &udev->ue_list, list) { + if (pos_node->bus_ue_id == bus_ue_id) { + list_del(&pos_node->list); + kfree(pos_node); + mutex_unlock(&udev->ue_list_lock); + return true; + } + } + mutex_unlock(&udev->ue_list_lock); + + return false; +} + +static bool ubase_modify_ue_list(struct ubase_dev *udev, u16 bus_ue_id, bool is_en) +{ + if (is_en) + return ubase_add_ue_list(udev, bus_ue_id); + else + return ubase_del_ue_list(udev, bus_ue_id); +} + void ubase_virt_handler(struct ubase_dev *udev, u16 bus_ue_id, bool is_en) { struct ubase_adev *uadev; int i; + if (!ubase_modify_ue_list(udev, bus_ue_id, is_en)) + return; + mutex_lock(&udev->priv.uadev_lock); for (i = 0; i < UBASE_DRV_MAX; i++) { uadev = udev->priv.uadev[i]; diff --git a/drivers/ub/ubase/ubase_hw.c b/drivers/ub/ubase/ubase_hw.c index a6ad242493bd..8656c1f0f33a 100644 --- a/drivers/ub/ubase/ubase_hw.c +++ b/drivers/ub/ubase/ubase_hw.c @@ -229,6 +229,21 @@ int ubase_query_dev_res(struct ubase_dev *udev) return ubase_parse_dev_res(udev, &resp); } +static void ubase_init_start_idx(struct ubase_dev *udev) +{ + struct ubase_adev_caps *udma_caps = &udev->caps.udma_caps; + struct ubase_adev_caps *unic_caps = &udev->caps.unic_caps; + + unic_caps->jfs.start_idx = udev->caps.dev_caps.public_jetty_cnt + + udev->caps.dev_caps.rsvd_jetty_cnt; + + udma_caps->jfs.start_idx = unic_caps->jfs.max_cnt + + udev->caps.dev_caps.public_jetty_cnt + + udev->caps.dev_caps.rsvd_jetty_cnt; + udma_caps->jfr.start_idx = unic_caps->jfr.max_cnt; + udma_caps->jfc.start_idx = unic_caps->jfc.max_cnt; +} + static int ubase_config_ctx_buf_to_hw(struct ubase_dev *udev, struct ubase_ctx_buf_cap *ctx_buf, struct ubase_mbx_attr *attr) @@ -702,6 +717,57 @@ static inline void ubase_uninit_ctx_buf(struct ubase_dev *udev) ubase_ctx_free(udev, &udev->ctx_buf, UBASE_CTX_REMOVE_ALL); } +static void ubase_init_ctx_buf_lock(struct ubase_ctx_buf *ctx_buf) +{ + struct mutex *ctx_mutex[] = { &ctx_buf->jfs.ctx_mutex, + &ctx_buf->jfr.ctx_mutex, + &ctx_buf->jfc.ctx_mutex, + &ctx_buf->jtg.ctx_mutex, + &ctx_buf->rc.ctx_mutex }; + int i; + + for (i = 0; i < ARRAY_SIZE(ctx_mutex); i++) + mutex_init(ctx_mutex[i]); +} + +static void ubase_uninit_ctx_buf_lock(struct ubase_ctx_buf *ctx_buf) +{ + struct mutex *ctx_mutex[] = { &ctx_buf->jfs.ctx_mutex, + &ctx_buf->jfr.ctx_mutex, + &ctx_buf->jfc.ctx_mutex, + &ctx_buf->jtg.ctx_mutex, + &ctx_buf->rc.ctx_mutex }; + int i; + + for (i = 0; i < ARRAY_SIZE(ctx_mutex); i++) + mutex_destroy(ctx_mutex[i]); +} + +int ubase_ue_init(struct ubase_dev *udev) +{ + ubase_init_start_idx(udev); + INIT_LIST_HEAD(&udev->ue_list); + mutex_init(&udev->ue_list_lock); + mutex_init(&udev->stats.stats_lock); + mutex_init(&udev->stats.activate_record.lock); + spin_lock_init(&udev->tp_ctx.tpg_lock); + mutex_init(&udev->act_ctx.lock); + init_completion(&udev->act_ctx.self.activate_done); + init_completion(&udev->act_ctx.other.activate_done); + ubase_init_ctx_buf_lock(&udev->ctx_buf); + + return 0; +} + +void ubase_ue_uninit(struct ubase_dev *udev) +{ + mutex_destroy(&udev->act_ctx.lock); + mutex_destroy(&udev->stats.activate_record.lock); + mutex_destroy(&udev->stats.stats_lock); + mutex_destroy(&udev->ue_list_lock); + ubase_uninit_ctx_buf_lock(&udev->ctx_buf); +} + int ubase_hw_init(struct ubase_dev *udev) { int ret; diff --git a/drivers/ub/ubase/ubase_hw.h b/drivers/ub/ubase/ubase_hw.h index 8931093590cf..ca75809cab24 100644 --- a/drivers/ub/ubase/ubase_hw.h +++ b/drivers/ub/ubase/ubase_hw.h @@ -185,6 +185,8 @@ int ubase_query_dev_res(struct ubase_dev *udev); int ubase_query_chip_info(struct ubase_dev *udev); int ubase_query_controller_info(struct ubase_dev *udev); int ubase_query_hw_oor_caps(struct ubase_dev *udev); +int ubase_ue_init(struct ubase_dev *udev); +void ubase_ue_uninit(struct ubase_dev *udev); int ubase_query_fst_fvt_rqmt(struct ubase_dev *udev, struct ubase_query_fst_fvt_rqmt_cmd *resp, u16 bus_ue_id); diff --git a/include/ub/ubase/ubase_comm_dev.h b/include/ub/ubase/ubase_comm_dev.h index effbf70f028b..129102f054ee 100644 --- a/include/ub/ubase/ubase_comm_dev.h +++ b/include/ub/ubase/ubase_comm_dev.h @@ -192,6 +192,11 @@ struct ubase_adev_qos { u8 ue_sl_vl[UBASE_MAX_SL_NUM]; }; +struct ubase_ue_node { + struct list_head list; + u16 bus_ue_id; +}; + struct ubase_ue_caps { u8 ceq_vector_num; u8 aeq_vector_num; -- Gitee From 2eba3472cb8755a50ed1d65137a31fce22707af7 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Sat, 20 Sep 2025 12:10:09 +0800 Subject: [PATCH 165/214] ub: ubase: Support for ELR and entity reset. ANBZ: #45460 commit 727362c0978ca7ecd007ea2f3fc0f7873a615821 openEuler. The ELR reset function is used to reset entity level resources such as jet/tp for entity devices, and the device status remains the same after reset as before. when reset happened,ubus will call interface as follow: reset_prepare -> write ELR reg -> reset_done. in reset_prepare, if entity is primary entity, it will send cmd to IMP for notifying active entity which is belong to primary entity, call unic/udma reset_prepare interface to stop net stream, then wait entity finish stop net stream and ELR works.If not primary entity, just stop net stream. in write ELR reg, ubus write ELR reg, when reg status changed, IMP will reset hardware in reset_done,if entity is primary entity,uninit ubase resource, init ubase resource, call unic/udma reset_done interface to config net device parameter to hardware, then send cmd to IMP for notifying entity to resume.if entity is not primary entity, will read RST_ING reg, when reg value is 0, continue to do same things that primary entity did. Signed-off-by: Yixi Shen Signed-off-by: Xiongchuan Zhou Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/Makefile | 2 +- drivers/ub/ubase/ubase.h | 1 + drivers/ub/ubase/ubase_ctrlq.c | 33 ++++ drivers/ub/ubase/ubase_ctrlq.h | 13 ++ drivers/ub/ubase/ubase_dev.c | 165 +++++++++++++++++- drivers/ub/ubase/ubase_dev.h | 6 + drivers/ub/ubase/ubase_eq.h | 1 + drivers/ub/ubase/ubase_hw.c | 36 +++- drivers/ub/ubase/ubase_reset.c | 253 ++++++++++++++++++++++++++++ drivers/ub/ubase/ubase_reset.h | 28 +++ drivers/ub/ubase/ubase_ubus.c | 71 +++++++- include/ub/ubase/ubase_comm_cmd.h | 3 + include/ub/ubase/ubase_comm_ctrlq.h | 5 + include/ub/ubase/ubase_comm_dev.h | 8 + 14 files changed, 621 insertions(+), 4 deletions(-) create mode 100644 drivers/ub/ubase/ubase_reset.c diff --git a/drivers/ub/ubase/Makefile b/drivers/ub/ubase/Makefile index 8999c24a949a..8fe7ebaeee03 100644 --- a/drivers/ub/ubase/Makefile +++ b/drivers/ub/ubase/Makefile @@ -12,7 +12,7 @@ MODULE_NAME := ubase UBASE_OBJS := ubase_main.o ubase_dev.o ubase_hw.o ubase_cmd.o ubase_ctrlq.o \ debugfs/ubase_debugfs.o ubase_eq.o ubase_mailbox.o ubase_ubus.o \ debugfs/ubase_ctx_debugfs.o debugfs/ubase_qos_debugfs.o \ - ubase_qos_hw.o ubase_tp.o ubase_ctrlq_tp.o + ubase_qos_hw.o ubase_tp.o ubase_ctrlq_tp.o ubase_reset.o $(MODULE_NAME)-objs := $(UBASE_OBJS) obj-$(CONFIG_UB_UBASE) := ubase.o diff --git a/drivers/ub/ubase/ubase.h b/drivers/ub/ubase/ubase.h index cfe047ce69c4..4524c195030d 100644 --- a/drivers/ub/ubase/ubase.h +++ b/drivers/ub/ubase/ubase.h @@ -16,6 +16,7 @@ enum ubase_service_state { UBASE_STATE_CRQ_SERVICE_SCHED, UBASE_STATE_CRQ_HANDLING, + UBASE_SERVICE_STATE_RESET_SCHED, UBASE_STATE_CTRLQ_SERVICE_SCHED, UBASE_STATE_CTRLQ_HANDLING, }; diff --git a/drivers/ub/ubase/ubase_ctrlq.c b/drivers/ub/ubase/ubase_ctrlq.c index bdc2c5659b80..0849db308360 100644 --- a/drivers/ub/ubase/ubase_ctrlq.c +++ b/drivers/ub/ubase/ubase_ctrlq.c @@ -261,6 +261,32 @@ static void ubase_ctrlq_clean_msg_queue(struct ubase_dev *udev) spin_unlock_bh(&csq->lock); } +void ubase_ctrlq_disable_remote(struct ubase_dev *udev) +{ + struct ubase_ctrlq_chan_ctrl_req req = {0}; + struct ubase_ctrlq_msg msg = {0}; + u32 resp; + int ret; + + if (!ubase_dev_ctrlq_supported(udev)) + return; + + msg.service_ver = UBASE_CTRLQ_SER_VER_01; + msg.service_type = UBASE_CTRLQ_SER_TYPE_DEV_REGISTER; + msg.opcode = UBASE_CTRLQ_OPC_CTRLQ_CTRL; + msg.need_resp = 1; + msg.in_size = sizeof(req); + msg.in = &req; + msg.out_size = sizeof(resp); + msg.out = &resp; + req.opc = UBASE_CTRLQ_CHAN_DISABLE_OPC; + + ret = __ubase_ctrlq_send(udev, &msg, NULL); + if (ret) + ubase_err(udev, "failed to disable remote ctrlq, ret = %d.\n", + ret); +} + static void ubase_ctrlq_clean_pending_msgs(struct ubase_dev *udev) { #define UBASE_CTRLQ_CLEAN_WAIT_TIME 5 @@ -646,6 +672,13 @@ int __ubase_ctrlq_send(struct ubase_dev *udev, struct ubase_ctrlq_msg *msg, return ret; while (retry_cnt++ <= UBASE_CTRLQ_RETRY_TIMES) { + if (udev->reset_stage == UBASE_RESET_STAGE_UNINIT && + !(msg->opcode == UBASE_CTRLQ_OPC_CTRLQ_CTRL && + msg->service_type == UBASE_CTRLQ_SER_TYPE_DEV_REGISTER)) { + ubase_dbg(udev, "ctrlq send is disabled.\n"); + return -EAGAIN; + } + if (!test_bit(UBASE_CTRLQ_STATE_ENABLE, &udev->ctrlq.state)) { ubase_warn(udev, "ctrlq is disabled in csq.\n"); return -EAGAIN; diff --git a/drivers/ub/ubase/ubase_ctrlq.h b/drivers/ub/ubase/ubase_ctrlq.h index 330478460626..431253df5054 100644 --- a/drivers/ub/ubase/ubase_ctrlq.h +++ b/drivers/ub/ubase/ubase_ctrlq.h @@ -19,6 +19,8 @@ #define UBASE_CTRLQ_SCHED_TIMEOUT (HZ / 2) #define UBASE_CTRLQ_SEQ_MASK BIT(15) #define UBASE_CTRLQ_DEAD_TIME 40000 +#define UBASE_CTRLQ_CHAN_DISABLE_OPC 0x1 +#define UBASE_CTRL_PLANE_INIT_RES BIT(0) enum ubase_ctrlq_state { UBASE_CTRLQ_STATE_ENABLE, @@ -75,6 +77,16 @@ struct ubase_ctrlq_query_sl_req { u8 rsv[20]; }; +struct ubase_ctrlq_chan_ctrl_req { + u8 opc; + u8 rsv[3]; +}; + +struct ubase_ctrlq_reset_ctrl_req { + u8 flag; + u8 rsv[3]; +}; + int ubase_ctrlq_init(struct ubase_dev *udev); void ubase_ctrlq_uninit(struct ubase_dev *udev); void ubase_ctrlq_disable(struct ubase_dev *udev); @@ -87,5 +99,6 @@ void ubase_ctrlq_handle_crq_msg(struct ubase_dev *udev, struct ubase_ctrlq_base_block *head, u16 seq, void *msg, u16 data_len); void ubase_ctrlq_clean_service_task(struct ubase_delay_work *ubase_work); +void ubase_ctrlq_disable_remote(struct ubase_dev *udev); #endif diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index fbf4fb78bc40..e937bb1dc01d 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -12,6 +12,7 @@ #include "ubase_ctrlq.h" #include "ubase_hw.h" #include "ubase_mailbox.h" +#include "ubase_reset.h" #include "ubase_dev.h" #define UBASE_PERIOD_100MS 100 @@ -332,7 +333,15 @@ static void ubase_period_service_task(struct work_struct *work) ubase_enable_period_service_task(udev); } -void ubase_service_task(struct work_struct *work) +static void ubase_reset_service_task(struct work_struct *work) +{ + struct ubase_delay_work *ubase_work = + container_of(work, struct ubase_delay_work, service_task.work); + + ubase_reset_service(ubase_work); +} + +static void ubase_service_task(struct work_struct *work) { struct ubase_delay_work *ubase_work = container_of(work, struct ubase_delay_work, service_task.work); @@ -345,6 +354,8 @@ void ubase_service_task(struct work_struct *work) static void ubase_init_delayed_work(struct ubase_dev *udev) { INIT_DELAYED_WORK(&udev->service_task.service_task, ubase_service_task); + INIT_DELAYED_WORK(&udev->reset_service_task.service_task, + ubase_reset_service_task); INIT_DELAYED_WORK(&udev->period_service_task.service_task, ubase_period_service_task); } @@ -504,11 +515,25 @@ static int ubase_handle_ue2ue_event(void *dev, void *data, u32 len) return 0; } +static int ubase_handle_ue_reset_event(void *dev, void *data, u32 len) +{ + struct ubase_dev *udev = dev; + + ubase_info(udev, "recv ue reset req, will reset.\n"); + __ubase_reset_event(udev, UBASE_UE_RESET); + + return 0; +} + static struct ubase_crq_event_nb ubase_crq_events[] = { { .opcode = UBASE_OPC_UE2UE_UBASE, .crq_handler = ubase_handle_ue2ue_event, }, + { + .opcode = UBASE_OPC_NOTIFY_UE_RESET, + .crq_handler = ubase_handle_ue_reset_event, + }, }; static void ubase_unregister_cmdq_crq_event(struct ubase_dev *udev) @@ -688,6 +713,98 @@ void ubase_dev_uninit(struct ubase_dev *udev) } } +static void __ubase_reset_uninit(struct ubase_dev *udev, int end) +{ + int i; + + for (i = end; i >= 0; i--) { + if (!ubase_init_func_support(udev, + ubase_init_func_map[i].support_devs)) + continue; + + if (ubase_init_func_map[i].uninit_func && + ubase_init_func_map[i].need_reset) + ubase_init_func_map[i].uninit_func(udev); + } +} + +int ubase_dev_reset_init(struct ubase_dev *udev) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(ubase_init_func_map); i++) { + if (!ubase_init_func_map[i].need_reset) + continue; + + if (!ubase_init_func_support(udev, + ubase_init_func_map[i].support_devs)) + continue; + + if (!ubase_init_func_map[i].init_func) + continue; + + ret = ubase_init_func_map[i].init_func(udev); + if (ret) { + ubase_err(udev, "failed to %s, ret = %d.\n", + ubase_init_func_map[i].err_msg, ret); + goto err_init; + } + } + + return 0; + +err_init: + __ubase_reset_uninit(udev, i - 1); + + return ret; +} + +void ubase_dev_reset_uninit(struct ubase_dev *udev) +{ + __ubase_reset_uninit(udev, ARRAY_SIZE(ubase_init_func_map) - 1); +} + +void ubase_suspend_aux_devices(struct ubase_dev *udev) +{ + struct ubase_priv *priv = &udev->priv; + struct ubase_adev *uadev; + int i; + + mutex_lock(&priv->uadev_lock); + for (i = ARRAY_SIZE(ubase_adev_devices) - 1; i >= 0; i--) { + uadev = priv->uadev[i]; + if (!uadev) + continue; + + mutex_lock(&uadev->reset_lock); + if (uadev->reset_handler) + uadev->reset_handler(&uadev->adev, udev->reset_stage); + mutex_unlock(&uadev->reset_lock); + } + mutex_unlock(&priv->uadev_lock); +} + +void ubase_resume_aux_devices(struct ubase_dev *udev) +{ + struct ubase_priv *priv = &udev->priv; + struct ubase_adev *uadev; + int i; + + mutex_lock(&priv->uadev_lock); + for (i = 0; i < ARRAY_SIZE(ubase_adev_devices); i++) { + uadev = priv->uadev[i]; + if (!uadev) + continue; + + mutex_lock(&uadev->reset_lock); + if (uadev->reset_handler) + uadev->reset_handler(&uadev->adev, + UBASE_RESET_STAGE_INIT); + mutex_unlock(&uadev->reset_lock); + } + mutex_unlock(&priv->uadev_lock); +} + bool ubase_adev_ubl_supported(struct auxiliary_device *adev) { if (!adev) @@ -761,6 +878,19 @@ struct ubase_adev_caps *ubase_get_cdma_caps(struct auxiliary_device *adev) } EXPORT_SYMBOL(ubase_get_cdma_caps); +enum ubase_reset_stage ubase_get_reset_stage(struct auxiliary_device *adev) +{ + struct ubase_dev *udev; + + if (!adev) + return UBASE_RESET_STAGE_NONE; + + udev = __ubase_get_udev_by_adev(adev); + + return udev->reset_stage; +} +EXPORT_SYMBOL(ubase_get_reset_stage); + void ubase_virt_register(struct auxiliary_device *adev, void (*virt_handler)(struct auxiliary_device *adev, u16 bus_ue_id, bool is_en)) @@ -794,6 +924,39 @@ void ubase_virt_unregister(struct auxiliary_device *adev) } EXPORT_SYMBOL(ubase_virt_unregister); +void ubase_reset_register(struct auxiliary_device *adev, + void (*reset_handler)(struct auxiliary_device *adev, + enum ubase_reset_stage stage)) +{ + struct ubase_adev *uadev; + + if (!adev || !reset_handler) + return; + + uadev = container_of(adev, struct ubase_adev, adev); + + mutex_lock(&uadev->reset_lock); + if (!uadev->reset_handler) + uadev->reset_handler = reset_handler; + mutex_unlock(&uadev->reset_lock); +} +EXPORT_SYMBOL(ubase_reset_register); + +void ubase_reset_unregister(struct auxiliary_device *adev) +{ + struct ubase_adev *uadev; + + if (!adev) + return; + + uadev = container_of(adev, struct ubase_adev, adev); + + mutex_lock(&uadev->reset_lock); + uadev->reset_handler = NULL; + mutex_unlock(&uadev->reset_lock); +} +EXPORT_SYMBOL(ubase_reset_unregister); + struct ubase_adev_caps *ubase_get_unic_caps(struct auxiliary_device *adev) { struct ubase_dev *udev; diff --git a/drivers/ub/ubase/ubase_dev.h b/drivers/ub/ubase/ubase_dev.h index cad11ff085fd..0e2636b5490a 100644 --- a/drivers/ub/ubase/ubase_dev.h +++ b/drivers/ub/ubase/ubase_dev.h @@ -399,6 +399,12 @@ void ubase_port_up(struct ubase_dev *udev); int ubase_dev_init(struct ubase_dev *udev); void ubase_dev_uninit(struct ubase_dev *udev); +int ubase_dev_reset_init(struct ubase_dev *udev); +void ubase_dev_reset_uninit(struct ubase_dev *udev); + +void ubase_suspend_aux_devices(struct ubase_dev *udev); +void ubase_resume_aux_devices(struct ubase_dev *udev); + void ubase_virt_handler(struct ubase_dev *udev, u16 bus_ue_id, bool is_en); #endif diff --git a/drivers/ub/ubase/ubase_eq.h b/drivers/ub/ubase/ubase_eq.h index d448bbab4c02..49906c521ef3 100644 --- a/drivers/ub/ubase/ubase_eq.h +++ b/drivers/ub/ubase/ubase_eq.h @@ -189,6 +189,7 @@ struct ubase_aeq_work { int ubase_irq_table_init(struct ubase_dev *udev); void ubase_irq_table_uninit(struct ubase_dev *udev); +void ubase_irq_table_free(struct ubase_dev *udev); int ubase_register_ae_event(struct ubase_dev *udev); void ubase_unregister_ae_event(struct ubase_dev *udev); diff --git a/drivers/ub/ubase/ubase_hw.c b/drivers/ub/ubase/ubase_hw.c index 8656c1f0f33a..0dbe9a5f6d5b 100644 --- a/drivers/ub/ubase/ubase_hw.c +++ b/drivers/ub/ubase/ubase_hw.c @@ -10,6 +10,7 @@ #include #include "ubase_cmd.h" +#include "ubase_ctrlq.h" #include "ubase_dev.h" #include "ubase_mailbox.h" #include "ubase_tp.h" @@ -768,10 +769,41 @@ void ubase_ue_uninit(struct ubase_dev *udev) ubase_uninit_ctx_buf_lock(&udev->ctx_buf); } +static int ubase_notify_ctrl_plane_init_res(struct ubase_dev *udev) +{ + struct ubase_ctrlq_reset_ctrl_req req = {0}; + struct ubase_ctrlq_msg msg = {0}; + u32 resp; + int ret; + + msg.service_ver = UBASE_CTRLQ_SER_VER_01; + msg.service_type = UBASE_CTRLQ_SER_TYPE_DEV_REGISTER; + msg.opcode = UBASE_CTRLQ_OPC_UE_RESET_CTRL; + msg.need_resp = 1; + msg.in_size = sizeof(req); + msg.in = &req; + msg.out_size = sizeof(resp); + msg.out = &resp; + + req.flag = UBASE_CTRL_PLANE_INIT_RES; + + ret = __ubase_ctrlq_send(udev, &msg, NULL); + if (ret) + dev_err(udev->dev, + "failed to notify ctrl plane init res, ret = %d.\n", + ret); + + return ret; +} + int ubase_hw_init(struct ubase_dev *udev) { int ret; + ret = ubase_notify_ctrl_plane_init_res(udev); + if (ret) + return ret; + ret = ubase_ctx_buf_alloc(udev); if (ret) { ubase_err(udev, "failed to init ctx buf, ret = %d.\n", ret); @@ -807,8 +839,10 @@ void ubase_hw_uninit(struct ubase_dev *udev) ubase_dev_uninit_tp_tpg(udev); ubase_uninit_ta_tp_ext_buf(udev); - if (!test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + if (!test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) { + ubase_ctrlq_disable_remote(udev); ubase_destroy_ctx_res(udev); + } ubase_uninit_ctx_buf(udev); } diff --git a/drivers/ub/ubase/ubase_reset.c b/drivers/ub/ubase/ubase_reset.c new file mode 100644 index 000000000000..8701c0c66c55 --- /dev/null +++ b/drivers/ub/ubase/ubase_reset.c @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include +#include + +#include "debugfs/ubase_debugfs.h" +#include "ubase.h" +#include "ubase_cmd.h" +#include "ubase_ctrlq.h" +#include "ubase_hw.h" +#include "ubase_mailbox.h" +#include "ubase_ubus.h" +#include "ubase_reset.h" + +static void ubase_reset_task_schedule(struct ubase_dev *udev) +{ + if (!test_and_set_bit(UBASE_SERVICE_STATE_RESET_SCHED, + &udev->service_task.state)) { + udev->last_reset_scheduled = jiffies; + mod_delayed_work(udev->ubase_reset_wq, + &udev->reset_service_task.service_task, 0); + } +} + +void ubase_reset_service(struct ubase_delay_work *ubase_work) +{ + struct ubase_dev *udev = container_of(ubase_work, struct ubase_dev, + reset_service_task); + int ret; + + if (!test_and_clear_bit(UBASE_SERVICE_STATE_RESET_SCHED, + &udev->service_task.state)) + return; + + if (time_is_before_eq_jiffies(udev->last_reset_scheduled + + UBASE_RESET_SCHED_TIMEOUT)) + ubase_warn(udev, + "reset service task scheduled after %ums on cpu%d!\n", + jiffies_to_msecs(jiffies - udev->last_reset_scheduled), + smp_processor_id()); + + ret = ubase_ubus_reset_entry(udev->dev); + if (ret) + ubase_err(udev, "failed to reset hardware, ret = %d.\n", ret); +} + +void __ubase_reset_event(struct ubase_dev *udev, + enum ubase_reset_type reset_type) +{ + udev->reset_type = reset_type; + + switch (reset_type) { + case UBASE_UE_RESET: + case UBASE_ELR_RESET: + ubase_reset_task_schedule(udev); + break; + default: + ubase_err(udev, "unknown reset type = %u, do nothing.\n", + reset_type); + break; + } +} + +void ubase_reset_event(struct auxiliary_device *adev, + enum ubase_reset_type reset_type) +{ + struct ubase_dev *udev; + + if (!adev) + return; + + udev = __ubase_get_udev_by_adev(adev); + + __ubase_reset_event(udev, reset_type); +} +EXPORT_SYMBOL(ubase_reset_event); + +static int ubase_notify_ue_reset(struct ubase_dev *udev, u16 bus_ue_id, + u16 single) +{ + struct ubase_notify_ue_reset_cmd req = {0}; + struct ubase_cmd_buf in; + int ret; + + req.bus_ue_id = bus_ue_id; + req.single = single; + __ubase_fill_inout_buf(&in, UBASE_OPC_NOTIFY_UE_RESET, false, + sizeof(req), &req); + + ret = __ubase_cmd_send_in(udev, &in); + if (ret) + ubase_err(udev, "failed to notify ue%hu reset, ret = %d.\n", + bus_ue_id, ret); + + return ret; +} + +static void ubase_notify_all_ue_reset(struct ubase_dev *udev) +{ + struct ubase_ue_node *ue_node; + + list_for_each_entry(ue_node, &udev->ue_list, list) + ubase_notify_ue_reset(udev, ue_node->bus_ue_id, 0); +} + +static void ubase_wait_ue_reset_ready(struct ubase_dev *udev) +{ + struct ubase_ue_reset_ready_cmd resp; + struct ubase_cmd_buf out, in; + int try_cnt = 0; + int ret; + + if (!dev_num_vf(udev->dev)) + return; + + do { + memset(&resp, 0, sizeof(resp)); + __ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_UE_RST_RDY, true, + 0, NULL); + __ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_UE_RST_RDY, + false, sizeof(resp), &resp); + + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret) { + ubase_err(udev, "failed to query ue ready status, ret = %d.\n", + ret); + return; + } + + msleep(UBASE_RST_WAIT_CMD_TIME); + try_cnt++; + } while (resp.ue_unready_num && try_cnt < UBASE_RST_WAIT_CMD_COUNT); + + if (resp.ue_unready_num) + ubase_warn(udev, "wait ue reset ready timeout! unready num = %u.\n", + resp.ue_unready_num); +} + +static int ubase_ue_reset_done_check(struct ubase_dev *udev) +{ + u32 reset_done_reg; + int try_cnt = 0; + + while (try_cnt < UBASE_RST_WAIT_REG_COUNT) { + reset_done_reg = ubase_read_dev(&udev->hw, UBASE_RST_ING_REG); + if (!(reset_done_reg & BIT(UBASE_RST_ING_RST_DONE_B))) + return 0; + + try_cnt++; + msleep(UBASE_RST_WAIT_REG_TIME); + } + + ubase_warn(udev, "wait reset done reg time out.\n"); + return -EBUSY; +} + +static void ubase_reset_done(struct ubase_dev *udev) +{ + struct ubase_cmd_buf in; + int ret; + + if (!dev_num_vf(udev->dev)) + return; + + __ubase_fill_inout_buf(&in, UBASE_OPC_RESET_DONE, false, 0, NULL); + + ret = __ubase_cmd_send_in(udev, &in); + if (ret) + ubase_err(udev, "failed to send reset done cmd, ret = %d.\n", + ret); + + /* Wait for entities to detect that its mue have reset done */ + msleep(UBASE_RST_UE_WAIT_REG_TIME); +} + +void ubase_suspend(struct ubase_dev *udev) +{ + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits) || + test_and_set_bit(UBASE_STATE_DISABLED_B, &udev->state_bits)) { + ubase_warn(udev, + "failed to suspend ubase, device is not ready or removing.\n"); + return; + } + + set_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits); + + if (ubase_dev_pmu_supported(udev)) { + __ubase_cmd_disable(udev); + return; + } + + ubase_notify_all_ue_reset(udev); + + udev->reset_stage = UBASE_RESET_STAGE_DOWN; + ubase_suspend_aux_devices(udev); + ubase_wait_ue_reset_ready(udev); + udev->reset_stage = UBASE_RESET_STAGE_UNINIT; + + clear_bit(UBASE_STATE_CTX_READY_B, &udev->state_bits); + ubase_cmd_disable(udev); + ubase_ctrlq_disable_remote(udev); + ubase_ctrlq_disable(udev); + ubase_irq_table_free(udev); +} + +void ubase_resume(struct ubase_dev *udev) +{ + int ret; + + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits) || + !test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) { + ubase_warn(udev, + "failed to resume ubase, device is not ready or removing.\n"); + return; + } + + if (ubase_dev_pmu_supported(udev)) { + ubase_ubus_reinit(udev->dev); + __ubase_cmd_enable(udev); + clear_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits); + clear_bit(UBASE_STATE_DISABLED_B, &udev->state_bits); + return; + } + + ubase_suspend_aux_devices(udev); + ubase_dev_reset_uninit(udev); + ubase_ubus_reinit(udev->dev); + + udev->reset_stage = UBASE_RESET_STAGE_NONE; + ret = ubase_ue_reset_done_check(udev); + if (ret) + goto err_resume; + + ret = ubase_dev_reset_init(udev); + if (ret) + goto err_resume; + + ubase_resume_aux_devices(udev); + ubase_reset_done(udev); + + clear_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits); + clear_bit(UBASE_STATE_DISABLED_B, &udev->state_bits); + return; + +err_resume: + ubase_resume_aux_devices(udev); + clear_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits); + clear_bit(UBASE_STATE_DISABLED_B, &udev->state_bits); +} diff --git a/drivers/ub/ubase/ubase_reset.h b/drivers/ub/ubase/ubase_reset.h index ae1ae9b7fdc1..7781011ac123 100644 --- a/drivers/ub/ubase/ubase_reset.h +++ b/drivers/ub/ubase/ubase_reset.h @@ -9,6 +9,34 @@ #include "ubase_dev.h" +#define UBASE_RST_ING_REG 0x00018040 +#define UBASE_RST_ING_RST_DONE_B 0 + +#define UBASE_RESET_SCHED_TIMEOUT (3 * HZ) +#define UBASE_RST_WAIT_REG_COUNT 60 +#define UBASE_RST_WAIT_REG_TIME 50 +#define UBASE_RST_WAIT_CMD_COUNT 60 +#define UBASE_RST_WAIT_CMD_TIME 50 + +#define UBASE_RST_UE_WAIT_REG_TIME 200 +#define UBASE_RST_MAX_RETRY_CNT 5 #define UBASE_RST_WAIT_TIME 100 +struct ubase_notify_ue_reset_cmd { + u16 bus_ue_id; + u16 single; + u8 rsv[20]; +}; + +struct ubase_ue_reset_ready_cmd { + u16 ue_unready_num; + u8 rsv[22]; +}; + +void ubase_suspend(struct ubase_dev *udev); +void ubase_resume(struct ubase_dev *udev); +void ubase_reset_service(struct ubase_delay_work *ubase_work); +void __ubase_reset_event(struct ubase_dev *udev, + enum ubase_reset_type reset_type); + #endif diff --git a/drivers/ub/ubase/ubase_ubus.c b/drivers/ub/ubase/ubase_ubus.c index c5b64805e90d..7627a7d562fc 100644 --- a/drivers/ub/ubase/ubase_ubus.c +++ b/drivers/ub/ubase/ubase_ubus.c @@ -330,6 +330,32 @@ int ubase_ubus_irq_vector(struct device *dev, u32 idx) return ub_irq_vector(ue, idx); } +static int ubase_ubus_suspend(struct device *dev) +{ + struct ubase_dev *udev = dev_get_drvdata(dev); + + ubase_info(udev, "UBUS suspend start.\n"); + ubase_suspend(udev); + + return 0; +} + +static int ubase_ubus_resume(struct device *dev) +{ + struct ubase_dev *udev = dev_get_drvdata(dev); + + ubase_info(udev, "UBUS resume start.\n"); + ubase_resume(udev); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(ubase_ubus_pm_ops, ubase_ubus_suspend, ubase_ubus_resume); + +static const struct device_driver ubase_ue_driver = { + .pm = &ubase_ubus_pm_ops, +}; + static int ubase_ubus_virt_configure(struct ub_entity *ue, int bus_ue_id, bool is_en) { struct ubase_dev *udev = dev_get_drvdata(&ue->dev); @@ -362,6 +388,46 @@ static int ubase_ubus_virt_notify(struct ub_entity *ue, int bus_ue_id, bool is_e return 0; } +static void ubase_ubus_reset_prepare(struct ub_entity *ue) +{ + struct ubase_dev *udev = dev_get_drvdata(&ue->dev); + + ubase_info(udev, "UBUS ELR start.\n"); + ubase_suspend(udev); +} + +static void ubase_ubus_reset_done(struct ub_entity *ue) +{ + struct ubase_dev *udev = dev_get_drvdata(&ue->dev); + + ubase_resume(udev); + ubase_info(udev, "UBUS ELR done.\n"); +} + +static ub_ers_result_t ubase_ubus_error_detected(struct ub_entity *ue, + ub_channel_state_t state) +{ + struct ubase_dev *udev = dev_get_drvdata(&ue->dev); + + ubase_info(udev, "UBUS error detected, state = %u.\n", state); + + switch (state) { + case ub_channel_io_normal: + return UB_ERS_RESULT_NEED_RESET; + case ub_channel_io_frozen: + return UB_ERS_RESULT_DISCONNECT; + case ub_channel_io_perm_failure: + default: + return UB_ERS_RESULT_NONE; + } +} + +static const struct ub_error_handlers ubase_ubus_err_handler = { + .ub_reset_prepare = ubase_ubus_reset_prepare, + .ub_reset_done = ubase_ubus_reset_done, + .ub_error_detected = ubase_ubus_error_detected, +}; + static struct ub_driver ubase_ubus_driver = { .name = ubase_ubus_driver_name, .id_table = ubase_ubus_tbl, @@ -370,7 +436,10 @@ static struct ub_driver ubase_ubus_driver = { .shutdown = ubase_ubus_shutdown, .virt_configure = ubase_ubus_virt_configure, .virt_notify = ubase_ubus_virt_notify, - .driver = {}, + .err_handler = &ubase_ubus_err_handler, + .driver = { + .pm = &ubase_ubus_pm_ops, + }, }; int ubase_ubus_register_driver(void) diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index f4459c7c016e..afb444facfb5 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -63,6 +63,9 @@ enum ubase_opcode_type { UBASE_OPC_MUE_TO_UE = 0xF001, UBASE_OPC_UE_TO_MUE = 0xF002, UBASE_OPC_CFG_VPORT_BUF = 0xF003, + UBASE_OPC_NOTIFY_UE_RESET = 0xF006, + UBASE_OPC_QUERY_UE_RST_RDY = 0xF007, + UBASE_OPC_RESET_DONE = 0xF008, UBASE_OPC_DESTROY_CTX_RESOURCE = 0xF00D, UBASE_OPC_UE2UE_UBASE = 0xF00E, }; diff --git a/include/ub/ubase/ubase_comm_ctrlq.h b/include/ub/ubase/ubase_comm_ctrlq.h index 62ced5535723..410b36069a0b 100644 --- a/include/ub/ubase/ubase_comm_ctrlq.h +++ b/include/ub/ubase/ubase_comm_ctrlq.h @@ -45,6 +45,11 @@ enum ubase_ctrlq_opc_type_qos { UBASE_CTRLQ_OPC_QUERY_SL = 0x02, }; +enum ubase_ctrlq_opc_type_dev_register { + UBASE_CTRLQ_OPC_CTRLQ_CTRL = 0x14, + UBASE_CTRLQ_OPC_UE_RESET_CTRL = 0x15, +}; + struct ubase_ctrlq_msg { enum ubase_ctrlq_ser_ver service_ver; enum ubase_ctrlq_ser_type service_type; diff --git a/include/ub/ubase/ubase_comm_dev.h b/include/ub/ubase/ubase_comm_dev.h index 129102f054ee..83e0378a50d3 100644 --- a/include/ub/ubase/ubase_comm_dev.h +++ b/include/ub/ubase/ubase_comm_dev.h @@ -230,11 +230,19 @@ struct ubase_adev_caps *ubase_get_udma_caps(struct auxiliary_device *adev); struct ubase_adev_caps *ubase_get_cdma_caps(struct auxiliary_device *adev); struct ubase_adev_qos *ubase_get_adev_qos(struct auxiliary_device *adev); +void ubase_reset_event(struct auxiliary_device *adev, + enum ubase_reset_type reset_type); +enum ubase_reset_stage ubase_get_reset_stage(struct auxiliary_device *adev); + void ubase_virt_register(struct auxiliary_device *adev, void (*virt_handler)(struct auxiliary_device *adev, u16 bus_ue_id, bool is_en)); void ubase_virt_unregister(struct auxiliary_device *adev); +void ubase_reset_register(struct auxiliary_device *adev, + void (*reset_handler)(struct auxiliary_device *adev, + enum ubase_reset_stage stage)); +void ubase_reset_unregister(struct auxiliary_device *adev); int ubase_get_bus_eid(struct auxiliary_device *adev, struct ubase_bus_eid *eid); #endif -- Gitee From 80fbadbc31b990ffc825f115b2d46f709206225c Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Sat, 20 Sep 2025 12:41:31 +0800 Subject: [PATCH 166/214] ub: ubase: Support reset count information statistics. ANBZ: #45460 commit ee69042dad6a93d5251fe142abd60f97ec1f7501 openEuler. This patch is used to count the number of successful and failed resets, and to add a debugfs viewing method, facilitating the identification of issues related to the reset process. Relevant information can be viewed at /sys/kernel/debug/ubase/$dev_id/reset_info. Signed-off-by: Yixi Shen Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/debugfs/ubase_debugfs.c | 24 ++++++++++++++++++++++++ drivers/ub/ubase/ubase_reset.c | 23 +++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/drivers/ub/ubase/debugfs/ubase_debugfs.c b/drivers/ub/ubase/debugfs/ubase_debugfs.c index d1bcf453e3d1..477078246cba 100644 --- a/drivers/ub/ubase/debugfs/ubase_debugfs.c +++ b/drivers/ub/ubase/debugfs/ubase_debugfs.c @@ -14,6 +14,21 @@ static struct dentry *ubase_dbgfs_root; +static int ubase_dbg_dump_rst_info(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + + seq_printf(s, "ELR reset count: %u\n", udev->reset_stat.elr_reset_cnt); + seq_printf(s, "port reset count: %u\n", udev->reset_stat.port_reset_cnt); + seq_printf(s, "reset done count: %u\n", udev->reset_stat.reset_done_cnt); + seq_printf(s, "HW reset done count: %u\n", udev->reset_stat.hw_reset_done_cnt); + seq_printf(s, "reset fail count: %u\n", udev->reset_stat.reset_fail_cnt); + seq_printf(s, "udev state: 0x%lx\n", udev->state_bits); + + return 0; +} + + static bool __ubase_dbg_dentry_support(struct device *dev, u32 property) { struct ubase_dev *udev = dev_get_drvdata(dev); @@ -81,6 +96,15 @@ static struct ubase_dbg_dentry_info ubase_dbg_dentry[] = { }; static struct ubase_dbg_cmd_info ubase_dbg_cmd[] = { + { + .name = "reset_info", + .dentry_index = UBASE_DBG_DENTRY_ROOT, + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_PMU | + UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_rst_info, + }, { .name = "adev_qos", .dentry_index = UBASE_DBG_DENTRY_QOS, diff --git a/drivers/ub/ubase/ubase_reset.c b/drivers/ub/ubase/ubase_reset.c index 8701c0c66c55..dfc34dd7d203 100644 --- a/drivers/ub/ubase/ubase_reset.c +++ b/drivers/ub/ubase/ubase_reset.c @@ -26,6 +26,20 @@ static void ubase_reset_task_schedule(struct ubase_dev *udev) } } +static void ubase_reset_err_handle(struct ubase_dev *udev) +{ + udev->reset_stat.reset_fail_cnt++; + udev->reset_stat.reset_retry_cnt++; + if (udev->reset_stat.reset_retry_cnt < UBASE_RST_MAX_RETRY_CNT) { + ubase_reset_task_schedule(udev); + ubase_info(udev, "re-schedule reset task(%u).\n", + udev->reset_stat.reset_retry_cnt); + return; + } + + ubase_err(udev, "failed to reset, too many attempts.\n"); +} + void ubase_reset_service(struct ubase_delay_work *ubase_work) { struct ubase_dev *udev = container_of(ubase_work, struct ubase_dev, @@ -190,6 +204,7 @@ void ubase_suspend(struct ubase_dev *udev) if (ubase_dev_pmu_supported(udev)) { __ubase_cmd_disable(udev); + udev->reset_stat.elr_reset_cnt++; return; } @@ -200,6 +215,8 @@ void ubase_suspend(struct ubase_dev *udev) ubase_wait_ue_reset_ready(udev); udev->reset_stage = UBASE_RESET_STAGE_UNINIT; + udev->reset_stat.elr_reset_cnt++; + clear_bit(UBASE_STATE_CTX_READY_B, &udev->state_bits); ubase_cmd_disable(udev); ubase_ctrlq_disable_remote(udev); @@ -221,11 +238,14 @@ void ubase_resume(struct ubase_dev *udev) if (ubase_dev_pmu_supported(udev)) { ubase_ubus_reinit(udev->dev); __ubase_cmd_enable(udev); + udev->reset_stat.reset_done_cnt++; + udev->reset_stat.hw_reset_done_cnt++; clear_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits); clear_bit(UBASE_STATE_DISABLED_B, &udev->state_bits); return; } + udev->reset_stat.hw_reset_done_cnt++; ubase_suspend_aux_devices(udev); ubase_dev_reset_uninit(udev); ubase_ubus_reinit(udev->dev); @@ -242,6 +262,8 @@ void ubase_resume(struct ubase_dev *udev) ubase_resume_aux_devices(udev); ubase_reset_done(udev); + udev->reset_stat.reset_done_cnt++; + udev->reset_stat.reset_retry_cnt = 0; clear_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits); clear_bit(UBASE_STATE_DISABLED_B, &udev->state_bits); return; @@ -250,4 +272,5 @@ void ubase_resume(struct ubase_dev *udev) ubase_resume_aux_devices(udev); clear_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits); clear_bit(UBASE_STATE_DISABLED_B, &udev->state_bits); + ubase_reset_err_handle(udev); } -- Gitee From b4f5d56bf65cc04b5dd05ec7e0d4ba728bea8654 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Mon, 22 Sep 2025 13:54:07 +0800 Subject: [PATCH 167/214] ub: ubase: Added QoS and traffic management debugging features ANBZ: #45460 commit 38f3f5d80a6ddb029910a4a288e151243802580f openEuler. This patch adds comprehensive QoS (Quality of Service) and traffic management debugging capabilities to the ubase driver, exposing rich network configuration information through the debugfs interface. Main Features: 1. QoS Configuration Information Display: - Service Level to Virtual Channel Mapping (SL-VL) - RDMA DSCP to Virtual Channel Mapping Table - Enhanced Transmission Service (ETS) Configuration: - Traffic Class (TC) Scheduling Information and Weights - Traffic Class Group (TCG) Parameters and Rate Limits - Port-level ETS Configuration 2. Traffic Management Information Query: - Queue Information (Queue ID, Queue Set ID, Virtual Channel ID) - Queue Set Configuration (Scheduling Mode, Weights, Rate Limits) - Priority Settings (CIR/PIR Rates, Burst Size) - Priority Group Parameters (Scheduling Mode, Weights, Shaper Configuration) - Port-level Traffic Management Statistics 3. Control Queue Integration: - Query Virtual Channel Bitmap Information via Control Queue - Support Information Query for Multiple User Entities (UE) 4. Debugging Interface Enhancement: - Create a qos directory in debugfs to centrally manage all QoS-related debugging information - Provide formatted data display for easy user viewing and analysis This implementation provides network administrators and developers with a comprehensive QoS and traffic management visualization tool, facilitating the debugging and optimization of network performance configurations. Signed-off-by: Haiqing Fang Signed-off-by: Haibin Lu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/debugfs/ubase_debugfs.c | 93 ++++ drivers/ub/ubase/debugfs/ubase_qos_debugfs.c | 528 +++++++++++++++++++ drivers/ub/ubase/debugfs/ubase_qos_debugfs.h | 12 + drivers/ub/ubase/ubase_cmd.h | 89 ++++ drivers/ub/ubase/ubase_hw.h | 10 + drivers/ub/ubase/ubase_qos_hw.c | 116 ++++ include/ub/ubase/ubase_comm_cmd.h | 5 + 7 files changed, 853 insertions(+) diff --git a/drivers/ub/ubase/debugfs/ubase_debugfs.c b/drivers/ub/ubase/debugfs/ubase_debugfs.c index 477078246cba..15473dfddee1 100644 --- a/drivers/ub/ubase/debugfs/ubase_debugfs.c +++ b/drivers/ub/ubase/debugfs/ubase_debugfs.c @@ -84,6 +84,11 @@ int ubase_dbg_seq_file_init(struct device *dev, EXPORT_SYMBOL(ubase_dbg_seq_file_init); static struct ubase_dbg_dentry_info ubase_dbg_dentry[] = { + { + .name = "qos", + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + }, /* ue debugfs top-level directory, * "dev_name" refers to the ue name */ @@ -105,6 +110,54 @@ static struct ubase_dbg_cmd_info ubase_dbg_cmd[] = { .init = __ubase_dbg_seq_file_init, .read_func = ubase_dbg_dump_rst_info, }, + { + .name = "sl_vl_map", + .dentry_index = UBASE_DBG_DENTRY_QOS, + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_sl_vl_map, + }, + { + .name = "udma_dscp_vl_map", + .dentry_index = UBASE_DBG_DENTRY_QOS, + .property = UBASE_SUP_URMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_udma_dscp_vl_map, + }, + { + .name = "ets_tc", + .dentry_index = UBASE_DBG_DENTRY_QOS, + .property = UBASE_SUP_URMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_ets_tc_info, + }, + { + .name = "ets_tcg", + .dentry_index = UBASE_DBG_DENTRY_QOS, + .property = UBASE_SUP_URMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_ets_tcg_info, + }, + { + .name = "ets_port", + .dentry_index = UBASE_DBG_DENTRY_QOS, + .property = UBASE_SUP_URMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_ets_port_info, + }, + { + .name = "rack_vl_bitmap", + .dentry_index = UBASE_DBG_DENTRY_QOS, + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_UBL, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_rack_vl_bitmap, + }, { .name = "adev_qos", .dentry_index = UBASE_DBG_DENTRY_QOS, @@ -113,6 +166,46 @@ static struct ubase_dbg_cmd_info ubase_dbg_cmd[] = { .init = __ubase_dbg_seq_file_init, .read_func = ubase_dbg_dump_adev_qos_info, }, + { + .name = "tm_queue", + .dentry_index = UBASE_DBG_DENTRY_QOS, + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_tm_queue_info, + }, + { + .name = "tm_qset", + .dentry_index = UBASE_DBG_DENTRY_QOS, + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_tm_qset_info, + }, + { + .name = "tm_pri", + .dentry_index = UBASE_DBG_DENTRY_QOS, + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_tm_pri_info, + }, + { + .name = "tm_pg", + .dentry_index = UBASE_DBG_DENTRY_QOS, + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_tm_pg_info, + }, + { + .name = "tm_port", + .dentry_index = UBASE_DBG_DENTRY_QOS, + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_tm_port_info, + }, }; static int ubase_dbg_create_dir(struct device *dev, diff --git a/drivers/ub/ubase/debugfs/ubase_qos_debugfs.c b/drivers/ub/ubase/debugfs/ubase_qos_debugfs.c index 760c19c12314..6f12fc3ceb2c 100644 --- a/drivers/ub/ubase/debugfs/ubase_qos_debugfs.c +++ b/drivers/ub/ubase/debugfs/ubase_qos_debugfs.c @@ -4,11 +4,191 @@ * */ +#include + +#include "ubase_cmd.h" +#include "ubase_ctrlq.h" #include "ubase_debugfs.h" #include "ubase_hw.h" #include "ubase_mailbox.h" #include "ubase_qos_debugfs.h" +int ubase_dbg_dump_sl_vl_map(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + u8 i, sl_vl[UBASE_MAX_SL_NUM] = {0}; + int ret; + + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits) || + test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + return -EBUSY; + + ret = ubase_query_sl_vl_map(udev, sl_vl); + if (ret) + return ret; + + seq_puts(s, "sl sw_vl hw_vl\n"); + for (i = 0; i < UBASE_MAX_SL_NUM; i++) { + seq_printf(s, "%-4u", i); + seq_printf(s, "%-7u", udev->qos.ue_sl_vl[i]); + seq_printf(s, "%-7u", sl_vl[i]); + seq_puts(s, "\n"); + } + + return ret; +} + +int ubase_dbg_dump_udma_dscp_vl_map(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + int i; + + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits) || + test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + return -EBUSY; + + seq_puts(s, "dscp vl\n"); + for (i = 0; i < UBASE_MAX_DSCP; i++) { + seq_printf(s, "%-7d", i); + seq_printf(s, "%-4u", udev->qos.dscp_vl[i]); + seq_puts(s, "\n"); + } + + return 0; +} + +int ubase_dbg_dump_ets_tc_info(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + struct ubase_cfg_ets_vl_sch_cmd resp = {0}; + unsigned long vl_bitmap = 0; + struct ubase_caps *caps; + u32 port_bitmap; + int ret; + u8 i; + + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits) || + test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + return -EBUSY; + + caps = &udev->caps.dev_caps; + port_bitmap = 1 << caps->io_port_id; + for (i = 0; i < caps->vl_num; i++) { + vl_bitmap |= 1 << caps->req_vl[i]; + vl_bitmap |= 1 << caps->resp_vl[i]; + } + + ret = ubase_query_ets_tc(udev, port_bitmap, vl_bitmap, &resp); + if (ret) + return ret; + + seq_puts(s, "TC_ID SCH_MODE WEIGHT\n"); + vl_bitmap = le16_to_cpu(resp.vl_bitmap); + for (i = 0; i < UBASE_MAX_VL_NUM; i++) { + if (!test_bit(i, &vl_bitmap)) + continue; + + seq_printf(s, "%-7u", i); + seq_printf(s, "%-11s", resp.vl_bw[i] ? "dwrr" : "sp"); + seq_printf(s, "%-9u", resp.vl_bw[i]); + seq_puts(s, "\n"); + } + + return 0; +} + +int ubase_dbg_dump_ets_tcg_info(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + struct ubase_query_ets_tcg_cmd resp = {0}; + int ret; + u8 i; + + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits) || + test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + return -EBUSY; + + ret = ubase_query_ets_tcg(udev, &resp); + if (ret) + return ret; + + seq_puts(s, "TCG_ID SCH_MODE WEIGHT TC_BITMAP RATE(Mbps) "); + seq_puts(s, "IR_B IR_U IR_S BS_B BS_S\n"); + for (i = 0; i < UBASE_MAX_TCG_NUM; i++) { + seq_printf(s, "%-8u", i); + seq_printf(s, "%-11s", resp.tcg_weight[i] ? "dwrr" : "sp"); + seq_printf(s, "%-9u", resp.tcg_weight[i]); + seq_printf(s, "0x%04x%-6s", le16_to_cpu(resp.tcg_tc_map[i]), ""); + seq_printf(s, "%-13u", le32_to_cpu(resp.tcg_info[i].rate)); + seq_printf(s, "%-7u", resp.tcg_info[i].ir_b); + seq_printf(s, "%-7u", resp.tcg_info[i].ir_u); + seq_printf(s, "%-7u", resp.tcg_info[i].ir_s); + seq_printf(s, "%-7u", resp.tcg_info[i].bs_b); + seq_printf(s, "%-7u", resp.tcg_info[i].bs_s); + seq_puts(s, "\n"); + } + + return 0; +} + +int ubase_dbg_dump_ets_port_info(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + struct ubase_query_ets_port_cmd resp = {0}; + int ret; + + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits) || + test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + return -EBUSY; + + ret = ubase_query_ets_port(udev, &resp); + if (ret) + return ret; + + seq_printf(s, "RATE(Mbps): %u ", le32_to_cpu(resp.port_info.rate)); + seq_printf(s, "IR_B: %u ", resp.port_info.ir_b); + seq_printf(s, "IR_U: %u ", resp.port_info.ir_u); + seq_printf(s, "IR_S: %u ", resp.port_info.ir_s); + seq_printf(s, "BS_B: %u ", resp.port_info.bs_b); + seq_printf(s, "BS_S: %u", resp.port_info.bs_s); + seq_puts(s, "\n"); + + return 0; +} + +int ubase_dbg_dump_rack_vl_bitmap(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + struct ubase_ctrlq_query_vl_resp resp = {0}; + struct ubase_ctrlq_query_vl_req req = {0}; + struct ubase_ctrlq_msg msg = {0}; + u16 vl_bitmap; + int ret = 0; + + msg.service_ver = UBASE_CTRLQ_SER_VER_01; + msg.service_type = UBASE_CTRLQ_SER_TYPE_QOS; + msg.opcode = UBASE_CTRLQ_OPC_QUERY_VL; + msg.need_resp = 1; + msg.is_resp = 0; + msg.in_size = sizeof(req); + msg.in = &req; + msg.out_size = sizeof(resp); + msg.out = &resp; + + ret = __ubase_ctrlq_send(udev, &msg, NULL); + if (ret) { + ubase_err(udev, + "failed to send ctrlq msg when query vl, ret = %d.\n", ret); + return ret; + } + + vl_bitmap = le16_to_cpu(resp.vl_bitmap); + + seq_printf(s, "rack vl bitmap : 0x%x", vl_bitmap); + + return 0; +} + static void ubase_dbg_dump_adev_vl_info(struct seq_file *s, struct ubase_adev_qos *qos) { @@ -70,3 +250,351 @@ int ubase_dbg_dump_adev_qos_info(struct seq_file *s, void *data) return 0; } + +static void ubase_dbg_fill_tm_queue_seq(struct seq_file *s, + struct ubase_query_tm_queue_cmd *resp) +{ + u16 bitmap = le16_to_cpu(resp->link_vld_bitmap); + u8 i; + + seq_puts(s, "QUEUE_ID QSET_ID VL_ID LINK_VLD_BITMAP\n"); + + for (i = 0; i < resp->queue_num; i++) { + seq_printf(s, "%-11u", resp->queue_id[i]); + seq_printf(s, "%-10u", resp->qset_id[i]); + seq_printf(s, "%-7u", resp->queue_vl[i]); + seq_printf(s, "%-16u", ((u32)bitmap >> i) & 1U); + seq_puts(s, "\n"); + } +} + +int ubase_dbg_dump_tm_queue_info(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + struct ubase_query_tm_queue_cmd resp = {0}; + struct ubase_ue_node *ue_node; + u16 ue_id = 0; + int ret; + + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits) || + test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + return -EBUSY; + + ret = ubase_query_tm_queue(udev, ue_id, &resp); + if (ret) + return ret; + + seq_printf(s, "current ue: %u\n", ue_id); + + ubase_dbg_fill_tm_queue_seq(s, &resp); + + mutex_lock(&udev->ue_list_lock); + list_for_each_entry(ue_node, &udev->ue_list, list) { + ue_id = ue_node->bus_ue_id; + + memset(&resp, 0, sizeof(resp)); + ret = ubase_query_tm_queue(udev, ue_id, &resp); + if (ret) + goto out; + + seq_printf(s, "\ncurrent ue: %u\n", ue_id); + + ubase_dbg_fill_tm_queue_seq(s, &resp); + } + +out: + mutex_unlock(&udev->ue_list_lock); + return ret; +} + +static void ubase_dbg_fill_tm_qset_sharper_seq(struct ubase_query_tm_qset_cmd *resp, + struct seq_file *s, int qset_id) +{ + if (!resp->rate_limit_bypass) { + seq_printf(s, "%-7u", resp->ir_b[qset_id]); + seq_printf(s, "%-7u", resp->ir_u[qset_id]); + seq_printf(s, "%-7u", resp->ir_s[qset_id]); + seq_printf(s, "%-7u", resp->bs_b[qset_id]); + seq_printf(s, "%-7u", resp->bs_s[qset_id]); + seq_printf(s, "%-16u", le32_to_cpu(resp->rate[qset_id])); + return; + } + + seq_puts(s, "-- -- -- -- -- -- "); +} + +static void ubase_dbg_fill_tm_qset_seq(struct seq_file *s, + struct ubase_query_tm_qset_cmd *resp) +{ + u16 link_vld = le16_to_cpu(resp->qset_pri_link_vld); + u16 sch_mode = le16_to_cpu(resp->qset_sch_mode); + u8 i; + + seq_puts(s, "QSET_ID PRI_ID LINK_VLD_BITMAP SCH_MODE WEIGHT "); + seq_puts(s, "IR_B IR_U IR_S BS_B BS_S RATE(Mbps)\n"); + + for (i = 0; i < resp->qset_num; i++) { + seq_printf(s, "%-10u", resp->qset_id[i]); + seq_printf(s, "%-9u", resp->pri_id[i]); + seq_printf(s, "%-16u", ((u32)link_vld >> i) & 1U); + seq_printf(s, "%-11u", ((u32)sch_mode >> i) & 1U); + seq_printf(s, "%-9u", resp->qset_weight[i]); + + ubase_dbg_fill_tm_qset_sharper_seq(resp, s, i); + seq_puts(s, "\n"); + } +} + +int ubase_dbg_dump_tm_qset_info(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + struct ubase_query_tm_qset_cmd resp = {0}; + struct ubase_ue_node *ue_node; + u16 ue_id = 0; + int ret; + + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits) || + test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + return -EBUSY; + + ret = ubase_query_tm_qset(udev, ue_id, &resp); + if (ret) + return ret; + + seq_printf(s, "current ue: %u\n", ue_id); + + ubase_dbg_fill_tm_qset_seq(s, &resp); + + mutex_lock(&udev->ue_list_lock); + list_for_each_entry(ue_node, &udev->ue_list, list) { + ue_id = ue_node->bus_ue_id; + + memset(&resp, 0, sizeof(resp)); + ret = ubase_query_tm_qset(udev, ue_id, &resp); + if (ret) + goto out; + + seq_printf(s, "\ncurrent ue: %u\n", ue_id); + + ubase_dbg_fill_tm_qset_seq(s, &resp); + } + +out: + mutex_unlock(&udev->ue_list_lock); + return ret; +} + +static void ubase_dbg_fill_tm_pri_csharper_seq(struct ubase_query_tm_pri_cmd *resp, + struct seq_file *s) +{ + if (!resp->c_rate_limit_bypass) { + seq_printf(s, "%-9u", resp->c_ir_b); + seq_printf(s, "%-9u", resp->c_ir_u); + seq_printf(s, "%-9u", resp->c_ir_s); + seq_printf(s, "%-9u", resp->c_bs_b); + seq_printf(s, "%-9u", resp->c_bs_s); + seq_printf(s, "%-15u", le32_to_cpu(resp->c_rate)); + return; + } + + seq_puts(s, "-- -- -- -- -- -- "); +} + +static void ubase_dbg_fill_tm_pri_psharper_seq(struct ubase_query_tm_pri_cmd *resp, + struct seq_file *s) +{ + if (!resp->p_rate_limit_bypass) { + seq_printf(s, "%-9u", resp->p_ir_b); + seq_printf(s, "%-9u", resp->p_ir_u); + seq_printf(s, "%-9u", resp->p_ir_s); + seq_printf(s, "%-9u", resp->p_bs_b); + seq_printf(s, "%-9u", resp->p_bs_s); + seq_printf(s, "%-15u", le32_to_cpu(resp->p_rate)); + return; + } + + seq_puts(s, "-- -- -- -- -- -- "); +} + +static void ubase_dbg_fill_tm_pri_seq(struct seq_file *s, + struct ubase_query_tm_pri_cmd *resp) +{ + seq_puts(s, "PRI_ID PG_ID SCH_MODE WEIGHT "); + seq_puts(s, "C_IR_B C_IR_U C_IR_S C_BS_B C_BS_S C_RATE(Mbps) "); + seq_puts(s, "P_IR_B P_IR_U P_IR_S P_BS_B P_BS_S P_RATE(Mbps)\n"); + + seq_printf(s, "%-9u", resp->pri_id); + seq_printf(s, "%-8u", resp->pg_id); + seq_printf(s, "%-10u", resp->pri_sch_mode); + seq_printf(s, "%-9u", resp->pri_weight); + + ubase_dbg_fill_tm_pri_csharper_seq(resp, s); + + ubase_dbg_fill_tm_pri_psharper_seq(resp, s); + seq_puts(s, "\n"); +} + +int ubase_dbg_dump_tm_pri_info(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + struct ubase_query_tm_pri_cmd resp = {0}; + struct ubase_ue_node *ue_node; + u16 ue_id = 0; + int ret; + + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits) || + test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + return -EBUSY; + + ret = ubase_query_tm_pri(udev, ue_id, &resp); + if (ret) + return ret; + + seq_printf(s, "current ue: %u\n", ue_id); + + ubase_dbg_fill_tm_pri_seq(s, &resp); + + mutex_lock(&udev->ue_list_lock); + list_for_each_entry(ue_node, &udev->ue_list, list) { + ue_id = ue_node->bus_ue_id; + + memset(&resp, 0, sizeof(resp)); + ret = ubase_query_tm_pri(udev, ue_id, &resp); + if (ret) + goto out; + + seq_printf(s, "\ncurrent ue: %u\n", ue_id); + + ubase_dbg_fill_tm_pri_seq(s, &resp); + } + +out: + mutex_unlock(&udev->ue_list_lock); + return ret; +} + +static void ubase_dbg_fill_tm_pg_csharper_seq(struct ubase_query_tm_pg_cmd *resp, + struct seq_file *s) +{ + if (!resp->c_rate_limit_bypass) { + seq_printf(s, "%-9u", resp->c_ir_b); + seq_printf(s, "%-9u", resp->c_ir_u); + seq_printf(s, "%-9u", resp->c_ir_s); + seq_printf(s, "%-9u", resp->c_bs_b); + seq_printf(s, "%-9u", resp->c_bs_s); + seq_printf(s, "%-15u", le32_to_cpu(resp->c_rate)); + return; + } + + seq_puts(s, "-- -- -- -- -- -- "); +} + +static void ubase_dbg_fill_tm_pg_psharper_seq(struct ubase_query_tm_pg_cmd *resp, + struct seq_file *s) +{ + if (!resp->p_rate_limit_bypass) { + seq_printf(s, "%-9u", resp->p_ir_b); + seq_printf(s, "%-9u", resp->p_ir_u); + seq_printf(s, "%-9u", resp->p_ir_s); + seq_printf(s, "%-9u", resp->p_bs_b); + seq_printf(s, "%-9u", resp->p_bs_s); + seq_printf(s, "%-15u", le32_to_cpu(resp->p_rate)); + return; + } + + seq_puts(s, "-- -- -- -- -- -- "); +} + +static void ubase_dbg_fill_tm_pg_seq(struct seq_file *s, + struct ubase_query_tm_pg_cmd *resp) +{ + seq_puts(s, "PG_ID SCH_MODE WEIGHT "); + seq_puts(s, "C_IR_B C_IR_U C_IR_S C_BS_B C_BS_S C_RATE(Mbps) "); + seq_puts(s, "P_IR_B P_IR_U P_IR_S P_BS_B P_BS_S P_RATE(Mbps)\n"); + + seq_printf(s, "%-8u", resp->pg_id); + seq_printf(s, "%-10u", resp->pg_sch_mode); + seq_printf(s, "%-9u", resp->pg_weight); + + ubase_dbg_fill_tm_pg_csharper_seq(resp, s); + + ubase_dbg_fill_tm_pg_psharper_seq(resp, s); + seq_puts(s, "\n"); +} + +int ubase_dbg_dump_tm_pg_info(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + struct ubase_query_tm_pg_cmd resp = {0}; + struct ubase_ue_node *ue_node; + u16 ue_id = 0; + int ret; + + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits) || + test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + return -EBUSY; + + ret = ubase_query_tm_pg(udev, ue_id, &resp); + if (ret) + return ret; + + seq_printf(s, "current ue: %u\n", ue_id); + + ubase_dbg_fill_tm_pg_seq(s, &resp); + + mutex_lock(&udev->ue_list_lock); + list_for_each_entry(ue_node, &udev->ue_list, list) { + ue_id = ue_node->bus_ue_id; + + memset(&resp, 0, sizeof(resp)); + ret = ubase_query_tm_pg(udev, ue_id, &resp); + if (ret) + goto out; + + seq_printf(s, "\ncurrent ue: %u\n", ue_id); + + ubase_dbg_fill_tm_pg_seq(s, &resp); + } + +out: + mutex_unlock(&udev->ue_list_lock); + return ret; +} + +static void ubase_dbg_fill_tm_port_sharper_seq(struct ubase_query_tm_port_cmd *resp, + struct seq_file *s) +{ + if (!resp->rate_limit_bypass) { + seq_printf(s, "%-7u", resp->ir_b); + seq_printf(s, "%-7u", resp->ir_u); + seq_printf(s, "%-7u", resp->ir_s); + seq_printf(s, "%-7u", resp->bs_b); + seq_printf(s, "%-7u", resp->bs_s); + seq_printf(s, "%-13u", le32_to_cpu(resp->rate)); + return; + } + + seq_puts(s, "-- -- -- -- -- -- "); +} + +int ubase_dbg_dump_tm_port_info(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + struct ubase_query_tm_port_cmd resp = {0}; + int ret; + + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits) || + test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + return -EBUSY; + + ret = ubase_query_tm_port(udev, &resp); + if (ret) + return ret; + + seq_puts(s, "IR_B IR_U IR_S BS_B BS_S RATE(Mbps)\n"); + + ubase_dbg_fill_tm_port_sharper_seq(&resp, s); + seq_puts(s, "\n"); + + return 0; +} diff --git a/drivers/ub/ubase/debugfs/ubase_qos_debugfs.h b/drivers/ub/ubase/debugfs/ubase_qos_debugfs.h index 538c9da5d1dc..48f45a2dbec0 100644 --- a/drivers/ub/ubase/debugfs/ubase_qos_debugfs.h +++ b/drivers/ub/ubase/debugfs/ubase_qos_debugfs.h @@ -9,5 +9,17 @@ struct device; +int ubase_dbg_dump_sl_vl_map(struct seq_file *s, void *data); +int ubase_dbg_dump_udma_dscp_vl_map(struct seq_file *s, void *data); +int ubase_dbg_dump_ets_tc_info(struct seq_file *s, void *data); +int ubase_dbg_dump_ets_tcg_info(struct seq_file *s, void *data); +int ubase_dbg_dump_ets_port_info(struct seq_file *s, void *data); +int ubase_dbg_dump_rack_vl_bitmap(struct seq_file *s, void *data); int ubase_dbg_dump_adev_qos_info(struct seq_file *s, void *data); +int ubase_dbg_dump_tm_queue_info(struct seq_file *s, void *data); +int ubase_dbg_dump_tm_qset_info(struct seq_file *s, void *data); +int ubase_dbg_dump_tm_pri_info(struct seq_file *s, void *data); +int ubase_dbg_dump_tm_pg_info(struct seq_file *s, void *data); +int ubase_dbg_dump_tm_port_info(struct seq_file *s, void *data); + #endif diff --git a/drivers/ub/ubase/ubase_cmd.h b/drivers/ub/ubase/ubase_cmd.h index 9452a40d6bf0..008ec4ee780a 100644 --- a/drivers/ub/ubase/ubase_cmd.h +++ b/drivers/ub/ubase/ubase_cmd.h @@ -131,6 +131,95 @@ struct ubase_query_fst_fvt_rqmt_cmd { struct ubase_rqmt_tbl rqmt_info[UBASE_MAX_VL_NUM]; }; +struct ubase_query_tm_queue_cmd { + __le16 bus_ue_id; + u8 queue_num; + u8 resv0; + u8 queue_vl[UBASE_MAX_VL_NUM]; + u8 queue_id[UBASE_MAX_VL_NUM]; + u8 qset_id[UBASE_MAX_VL_NUM]; + __le16 link_vld_bitmap; + u8 resv1[2]; +}; + +struct ubase_query_tm_qset_cmd { + __le16 bus_ue_id; + u8 qset_num; + u8 rate_limit_bypass; + u8 ir_b[UBASE_MAX_VL_NUM]; + u8 ir_u[UBASE_MAX_VL_NUM]; + u8 ir_s[UBASE_MAX_VL_NUM]; + u8 bs_b[UBASE_MAX_VL_NUM]; + u8 bs_s[UBASE_MAX_VL_NUM]; + __le32 rate[UBASE_MAX_VL_NUM]; + u8 qset_id[UBASE_MAX_VL_NUM]; + u8 pri_id[UBASE_MAX_VL_NUM]; + __le16 qset_pri_link_vld; + __le16 qset_sch_mode; + u8 qset_weight[UBASE_MAX_VL_NUM]; + u8 resv1[16]; +}; + +struct ubase_query_tm_pri_cmd { + __le16 bus_ue_id; + u8 pri_id; + u8 pg_id; + u8 pri_sch_mode; + u8 pri_weight; + u8 c_ir_b; + u8 c_ir_u; + u8 c_ir_s; + u8 c_bs_b; + u8 c_bs_s; + u8 p_ir_b; + u8 p_ir_u; + u8 p_ir_s; + u8 p_bs_b; + u8 p_bs_s; + __le32 c_rate; + __le32 p_rate; + + u8 c_rate_limit_bypass; + u8 p_rate_limit_bypass; + u8 resv1[30]; +}; + +struct ubase_query_tm_pg_cmd { + __le16 bus_ue_id; + u8 pg_id; + u8 pg_sch_mode; + u8 pg_weight; + u8 c_ir_b; + u8 c_ir_u; + u8 c_ir_s; + u8 c_bs_b; + u8 c_bs_s; + u8 p_ir_b; + u8 p_ir_u; + u8 p_ir_s; + u8 p_bs_b; + u8 p_bs_s; + u8 resv0; + __le32 c_rate; + __le32 p_rate; + + u8 c_rate_limit_bypass; + u8 p_rate_limit_bypass; + u8 resv1[30]; +}; + +struct ubase_query_tm_port_cmd { + u8 ir_b; + u8 ir_u; + u8 ir_s; + u8 bs_b; + u8 bs_s; + u8 rate_limit_bypass; + u8 resv0[2]; + __le32 rate; + u8 resv1[12]; +}; + struct ubase_query_ueid_cmd { __le32 ueid[UBASE_BUS_EID_LEN]; u32 rsv[2]; diff --git a/drivers/ub/ubase/ubase_hw.h b/drivers/ub/ubase/ubase_hw.h index ca75809cab24..29867a49e50e 100644 --- a/drivers/ub/ubase/ubase_hw.h +++ b/drivers/ub/ubase/ubase_hw.h @@ -185,6 +185,16 @@ int ubase_query_dev_res(struct ubase_dev *udev); int ubase_query_chip_info(struct ubase_dev *udev); int ubase_query_controller_info(struct ubase_dev *udev); int ubase_query_hw_oor_caps(struct ubase_dev *udev); +int ubase_query_tm_queue(struct ubase_dev *udev, u16 bus_ue_id, + struct ubase_query_tm_queue_cmd *resp); +int ubase_query_tm_qset(struct ubase_dev *udev, u16 bus_ue_id, + struct ubase_query_tm_qset_cmd *resp); +int ubase_query_tm_pri(struct ubase_dev *udev, u16 bus_ue_id, + struct ubase_query_tm_pri_cmd *resp); +int ubase_query_tm_pg(struct ubase_dev *udev, u16 bus_ue_id, + struct ubase_query_tm_pg_cmd *resp); +int ubase_query_tm_port(struct ubase_dev *udev, + struct ubase_query_tm_port_cmd *resp); int ubase_ue_init(struct ubase_dev *udev); void ubase_ue_uninit(struct ubase_dev *udev); int ubase_query_fst_fvt_rqmt(struct ubase_dev *udev, diff --git a/drivers/ub/ubase/ubase_qos_hw.c b/drivers/ub/ubase/ubase_qos_hw.c index 2298a40b6cb3..6889d2e840c1 100644 --- a/drivers/ub/ubase/ubase_qos_hw.c +++ b/drivers/ub/ubase/ubase_qos_hw.c @@ -1049,3 +1049,119 @@ void ubase_update_udma_dscp_vl(struct auxiliary_device *adev, u8 *dscp_vl, dscp_vl[i] : qos->tp_req_vl[0]; } EXPORT_SYMBOL(ubase_update_udma_dscp_vl); + +int ubase_query_tm_queue(struct ubase_dev *udev, u16 bus_ue_id, + struct ubase_query_tm_queue_cmd *resp) +{ + struct ubase_query_tm_queue_cmd req = {0}; + struct ubase_cmd_buf in, out; + int ret; + + req.bus_ue_id = cpu_to_le16(bus_ue_id); + + ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_TM_Q_INFO, true, + sizeof(req), &req); + ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_TM_Q_INFO, false, + sizeof(*resp), resp); + + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret == -EPERM) + return -EOPNOTSUPP; + if (ret) + ubase_err(udev, + "failed to query tm queue info, bus_ue_id=%u, ret=%d.\n", + bus_ue_id, ret); + return ret; +} + +int ubase_query_tm_qset(struct ubase_dev *udev, u16 bus_ue_id, + struct ubase_query_tm_qset_cmd *resp) +{ + struct ubase_query_tm_qset_cmd req = {0}; + struct ubase_cmd_buf in, out; + int ret; + + req.bus_ue_id = cpu_to_le16(bus_ue_id); + + ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_TM_QS_INFO, true, + sizeof(req), &req); + ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_TM_QS_INFO, false, + sizeof(*resp), resp); + + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret == -EPERM) + return -EOPNOTSUPP; + if (ret) + ubase_err(udev, + "failed to query tm qset info, bus_ue_id = %u, ret = %d.\n", + bus_ue_id, ret); + return ret; +} + +int ubase_query_tm_pri(struct ubase_dev *udev, u16 bus_ue_id, + struct ubase_query_tm_pri_cmd *resp) +{ + struct ubase_query_tm_pri_cmd req = {0}; + struct ubase_cmd_buf in, out; + int ret; + + req.bus_ue_id = cpu_to_le16(bus_ue_id); + + ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_TM_PRI_INFO, true, + sizeof(req), &req); + ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_TM_PRI_INFO, false, + sizeof(*resp), resp); + + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret == -EPERM) + return -EOPNOTSUPP; + if (ret) + ubase_err(udev, + "failed to query tm pri info, bus_ue_id = %u, ret = %d.\n", + bus_ue_id, ret); + return ret; +} + +int ubase_query_tm_pg(struct ubase_dev *udev, u16 bus_ue_id, + struct ubase_query_tm_pg_cmd *resp) +{ + struct ubase_query_tm_pg_cmd req = {0}; + struct ubase_cmd_buf in, out; + int ret; + + req.bus_ue_id = cpu_to_le16(bus_ue_id); + + ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_TM_PG_INFO, true, + sizeof(req), &req); + ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_TM_PG_INFO, false, + sizeof(*resp), resp); + + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret == -EPERM) + return -EOPNOTSUPP; + if (ret) + ubase_err(udev, + "failed to query tm pg info, bus_ue_id = %u, ret = %d.\n", + bus_ue_id, ret); + return ret; +} + +int ubase_query_tm_port(struct ubase_dev *udev, + struct ubase_query_tm_port_cmd *resp) +{ + struct ubase_query_tm_port_cmd req = {0}; + struct ubase_cmd_buf in, out; + int ret; + + ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_TM_PORT_INFO, true, + sizeof(req), &req); + ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_TM_PORT_INFO, false, + sizeof(*resp), resp); + + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret == -EPERM) + return -EOPNOTSUPP; + if (ret) + ubase_err(udev, "failed to query tm port info, ret = %d.\n", ret); + return ret; +} diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index afb444facfb5..d9fec4164525 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -50,6 +50,11 @@ enum ubase_opcode_type { UBASE_OPC_QUERY_OOR_CAPS = 0x4200, UBASE_OPC_QUERY_TA_SL_VL_MAP = 0x4201, UBASE_OPC_TA_VL_SCH_CONFIG = 0x4202, + UBASE_OPC_QUERY_TM_Q_INFO = 0x4205, + UBASE_OPC_QUERY_TM_QS_INFO = 0x4206, + UBASE_OPC_QUERY_TM_PRI_INFO = 0x4207, + UBASE_OPC_QUERY_TM_PG_INFO = 0x4208, + UBASE_OPC_QUERY_TM_PORT_INFO = 0x4209, UBASE_OPC_QUERY_FST_FVT_RQMT = 0x4212, /* PHY commands */ -- Gitee From ba69fe6cdaa27051f6f52d1a220c9fed3a7f5393 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Mon, 22 Sep 2025 16:53:33 +0800 Subject: [PATCH 168/214] ub: ubase: Support to port reset ANBZ: #45460 commit 9e16043ddf6b7755059b53fa28aa1e28f79694c0 openEuler. This patch support to port reset, detail as follows: 1.When a RAS error occurs, a port down operation is required to reset, and a port handler interface has been added for unic/udma registration to handle port reset events. 2.Implemented port reset interface in both shared port mode and exclusive mode.In shared port mode Down the network card in the reset_prepare interface and up the network card in the reset_deone interface.In exclusive mode.In exclusive mode, directly turn down the network card and turn up the network card Signed-off-by: Yixi Shen Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/Makefile | 3 ++- drivers/ub/ubase/ubase.h | 1 + drivers/ub/ubase/ubase_dev.c | 35 +++++++++++++++++++++++++++++ drivers/ub/ubase/ubase_dev.h | 5 +++++ drivers/ub/ubase/ubase_err_handle.c | 30 +++++++++++++++++++++++++ drivers/ub/ubase/ubase_err_handle.h | 14 ++++++++++++ drivers/ub/ubase/ubase_reset.c | 7 ++++++ drivers/ub/ubase/ubase_reset.h | 1 + include/ub/ubase/ubase_comm_dev.h | 5 +++++ 9 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubase/ubase_err_handle.c create mode 100644 drivers/ub/ubase/ubase_err_handle.h diff --git a/drivers/ub/ubase/Makefile b/drivers/ub/ubase/Makefile index 8fe7ebaeee03..f0c279c2a069 100644 --- a/drivers/ub/ubase/Makefile +++ b/drivers/ub/ubase/Makefile @@ -12,7 +12,8 @@ MODULE_NAME := ubase UBASE_OBJS := ubase_main.o ubase_dev.o ubase_hw.o ubase_cmd.o ubase_ctrlq.o \ debugfs/ubase_debugfs.o ubase_eq.o ubase_mailbox.o ubase_ubus.o \ debugfs/ubase_ctx_debugfs.o debugfs/ubase_qos_debugfs.o \ - ubase_qos_hw.o ubase_tp.o ubase_ctrlq_tp.o ubase_reset.o + ubase_qos_hw.o ubase_tp.o ubase_ctrlq_tp.o ubase_reset.o \ + ubase_err_handle.o $(MODULE_NAME)-objs := $(UBASE_OBJS) obj-$(CONFIG_UB_UBASE) := ubase.o diff --git a/drivers/ub/ubase/ubase.h b/drivers/ub/ubase/ubase.h index 4524c195030d..53fe64654557 100644 --- a/drivers/ub/ubase/ubase.h +++ b/drivers/ub/ubase/ubase.h @@ -16,6 +16,7 @@ enum ubase_service_state { UBASE_STATE_CRQ_SERVICE_SCHED, UBASE_STATE_CRQ_HANDLING, + UBASE_SERVICE_STATE_ERR_SCHED, UBASE_SERVICE_STATE_RESET_SCHED, UBASE_STATE_CTRLQ_SERVICE_SCHED, UBASE_STATE_CTRLQ_HANDLING, diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index e937bb1dc01d..945f6d75aad7 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -10,6 +10,7 @@ #include "debugfs/ubase_debugfs.h" #include "ubase_cmd.h" #include "ubase_ctrlq.h" +#include "ubase_err_handle.h" #include "ubase_hw.h" #include "ubase_mailbox.h" #include "ubase_reset.h" @@ -347,6 +348,7 @@ static void ubase_service_task(struct work_struct *work) container_of(work, struct ubase_delay_work, service_task.work); ubase_crq_service_task(ubase_work); + ubase_errhandle_service_task(ubase_work); ubase_ctrlq_service_task(ubase_work); ubase_ctrlq_clean_service_task(ubase_work); } @@ -924,6 +926,39 @@ void ubase_virt_unregister(struct auxiliary_device *adev) } EXPORT_SYMBOL(ubase_virt_unregister); +void ubase_port_register(struct auxiliary_device *adev, + void (*port_handler)(struct auxiliary_device *adev, + bool link_up)) +{ + struct ubase_adev *uadev; + + if (!adev || !port_handler) + return; + + uadev = container_of(adev, struct ubase_adev, adev); + + mutex_lock(&uadev->port_lock); + if (!uadev->port_handler) + uadev->port_handler = port_handler; + mutex_unlock(&uadev->port_lock); +} +EXPORT_SYMBOL(ubase_port_register); + +void ubase_port_unregister(struct auxiliary_device *adev) +{ + struct ubase_adev *uadev; + + if (!adev) + return; + + uadev = container_of(adev, struct ubase_adev, adev); + + mutex_lock(&uadev->port_lock); + uadev->port_handler = NULL; + mutex_unlock(&uadev->port_lock); +} +EXPORT_SYMBOL(ubase_port_unregister); + void ubase_reset_register(struct auxiliary_device *adev, void (*reset_handler)(struct auxiliary_device *adev, enum ubase_reset_stage stage)) diff --git a/drivers/ub/ubase/ubase_dev.h b/drivers/ub/ubase/ubase_dev.h index 0e2636b5490a..79ade538aca0 100644 --- a/drivers/ub/ubase/ubase_dev.h +++ b/drivers/ub/ubase/ubase_dev.h @@ -355,6 +355,11 @@ static inline bool ubase_dev_ta_timer_buf_supported(struct ubase_dev *udev) return ubase_get_cap_bit(udev, UBASE_SUPPORT_TA_TIMER_BUF_B); } +static inline bool ubase_dev_err_handle_supported(struct ubase_dev *udev) +{ + return ubase_get_cap_bit(udev, UBASE_SUPPORT_ERR_HANDLE_B); +} + static inline bool ubase_dev_ctrlq_supported(struct ubase_dev *udev) { return ubase_get_cap_bit(udev, UBASE_SUPPORT_CTRLQ_B); diff --git a/drivers/ub/ubase/ubase_err_handle.c b/drivers/ub/ubase/ubase_err_handle.c new file mode 100644 index 000000000000..d91ab0f80b6b --- /dev/null +++ b/drivers/ub/ubase/ubase_err_handle.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include "ubase_cmd.h" +#include "ubase_dev.h" +#include "ubase_reset.h" +#include "ubase_err_handle.h" + +void ubase_errhandle_service_task(struct ubase_delay_work *ubase_work) +{ + struct ubase_dev *udev; + + udev = container_of(ubase_work, struct ubase_dev, service_task); + if (!test_and_clear_bit(UBASE_SERVICE_STATE_ERR_SCHED, + &ubase_work->state)) + return; + + if (!ubase_dev_err_handle_supported(udev)) { + ubase_err(udev, "not support err handle processing.\n"); + return; + } + + if (test_and_clear_bit(UBASE_STATE_PORT_RESETTING_B, &udev->state_bits)) { + ubase_err(udev, "ras occurred, ubase need to reset port.\n"); + ubase_port_reset(udev); + } +} diff --git a/drivers/ub/ubase/ubase_err_handle.h b/drivers/ub/ubase/ubase_err_handle.h new file mode 100644 index 000000000000..229e1d23b9ba --- /dev/null +++ b/drivers/ub/ubase/ubase_err_handle.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UBASE_ERR_HANDLE_H__ +#define __UBASE_ERR_HANDLE_H__ + +#include + +void ubase_errhandle_service_task(struct ubase_delay_work *ubase_work); + +#endif diff --git a/drivers/ub/ubase/ubase_reset.c b/drivers/ub/ubase/ubase_reset.c index dfc34dd7d203..bb1c281c02d3 100644 --- a/drivers/ub/ubase/ubase_reset.c +++ b/drivers/ub/ubase/ubase_reset.c @@ -40,6 +40,13 @@ static void ubase_reset_err_handle(struct ubase_dev *udev) ubase_err(udev, "failed to reset, too many attempts.\n"); } +void ubase_port_reset(struct ubase_dev *udev) +{ + ubase_port_down(udev); + ubase_port_up(udev); + udev->reset_stat.port_reset_cnt++; +} + void ubase_reset_service(struct ubase_delay_work *ubase_work) { struct ubase_dev *udev = container_of(ubase_work, struct ubase_dev, diff --git a/drivers/ub/ubase/ubase_reset.h b/drivers/ub/ubase/ubase_reset.h index 7781011ac123..03d0f02bb9a8 100644 --- a/drivers/ub/ubase/ubase_reset.h +++ b/drivers/ub/ubase/ubase_reset.h @@ -38,5 +38,6 @@ void ubase_resume(struct ubase_dev *udev); void ubase_reset_service(struct ubase_delay_work *ubase_work); void __ubase_reset_event(struct ubase_dev *udev, enum ubase_reset_type reset_type); +void ubase_port_reset(struct ubase_dev *udev); #endif diff --git a/include/ub/ubase/ubase_comm_dev.h b/include/ub/ubase/ubase_comm_dev.h index 83e0378a50d3..db65dae3b75c 100644 --- a/include/ub/ubase/ubase_comm_dev.h +++ b/include/ub/ubase/ubase_comm_dev.h @@ -239,6 +239,11 @@ void ubase_virt_register(struct auxiliary_device *adev, u16 bus_ue_id, bool is_en)); void ubase_virt_unregister(struct auxiliary_device *adev); +void ubase_port_register(struct auxiliary_device *adev, + void (*port_handler)(struct auxiliary_device *adev, + bool link_up)); +void ubase_port_unregister(struct auxiliary_device *adev); + void ubase_reset_register(struct auxiliary_device *adev, void (*reset_handler)(struct auxiliary_device *adev, enum ubase_reset_stage stage)); -- Gitee From 7e8662c6437eb44708d8a80fd20d23c2b608d076 Mon Sep 17 00:00:00 2001 From: Xiaobo Zhang Date: Tue, 23 Sep 2025 21:09:59 +0800 Subject: [PATCH 169/214] ub: ubase: support ras function ANBZ: #45460 commit 2cfe6e92133d3b8827416090280c55b7767917fa openEuler. RAS (Reliability, Availability, and Serviceability) is an important feature of the system. UB system also supports RAS feature. UB defines multiple types of RAS, and different types of RAS are processed in different ways to ensure system stability. This patch provides the support of the UBASE driver for RAS. The UBASE driver senses the RAS through interrupts and determines to route it to the corresponding auxiliary device or process it based on the RAS information. Signed-off-by: Yaoyao Tu Signed-off-by: Xiaobo Zhang Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_eq.c | 119 +++++++++++++++++++++++++++++++ drivers/ub/ubase/ubase_eq.h | 7 ++ drivers/ub/ubase/ubase_tp.c | 20 ++++++ drivers/ub/ubase/ubase_tp.h | 2 + include/ub/ubase/ubase_comm_eq.h | 17 +++++ 5 files changed, 165 insertions(+) diff --git a/drivers/ub/ubase/ubase_eq.c b/drivers/ub/ubase/ubase_eq.c index ae8ad2b51420..754f62011420 100644 --- a/drivers/ub/ubase/ubase_eq.c +++ b/drivers/ub/ubase/ubase_eq.c @@ -10,6 +10,7 @@ #include "ubase_cmd.h" #include "ubase_dev.h" #include "ubase_mailbox.h" +#include "ubase_reset.h" #include "ubase_tp.h" #include "ubase_trace.h" #include "ubase_eq.h" @@ -121,9 +122,23 @@ void ubase_enable_misc_vector(struct ubase_dev *udev, bool enable) enable ? 0 : 1); } +static void ubase_save_ras_type(struct ubase_dev *udev, u32 reg) +{ + if (ubase_dev_ubl_supported(udev)) { + set_bit(UBASE_STATE_PORT_RESETTING_B, &udev->state_bits); + return; + } + + if (reg & BIT(UBASE_SW_HANDSHAKE_0_HIMAC_RESET_B)) + set_bit(UBASE_STATE_HIMAC_RESETTING_B, &udev->state_bits); + else if (reg & BIT(UBASE_SW_HANDSHAKE_0_PORT_RESET_B)) + set_bit(UBASE_STATE_PORT_RESETTING_B, &udev->state_bits); +} + static unsigned long ubase_check_event_cause(struct ubase_dev *udev) { unsigned long event_cause = 0; + u32 sw_handshake_0_reg; u32 ctrlq_src_reg; u32 cmdq_src_reg; @@ -131,6 +146,13 @@ static unsigned long ubase_check_event_cause(struct ubase_dev *udev) if (cmdq_src_reg & BIT(UBASE_VECTOR0_RX_CMDQ_INT_B)) event_cause |= BIT(UBASE_ASYNC_EVENT_CRQ_B); + sw_handshake_0_reg = ubase_read_dev(&udev->hw, + UBASE_SW_HANDSHAKE_0_REG); + if (sw_handshake_0_reg & BIT(UBASE_SW_HANDSHAKE_0_RAS_B)) { + ubase_save_ras_type(udev, sw_handshake_0_reg); + event_cause |= BIT(UBASE_ASYNC_EVENT_RAS_B); + } + ctrlq_src_reg = ubase_read_dev(&udev->hw, UBASE_VECTOR0_CTRLQ_SRC_REG); if (ctrlq_src_reg & BIT(UBASE_VECTOR0_RX_CTRLQ_INT_B)) event_cause |= BIT(UBASE_ASYNC_EVENT_CTRLQ_B); @@ -141,9 +163,22 @@ static unsigned long ubase_check_event_cause(struct ubase_dev *udev) static void ubase_clear_event_cause(struct ubase_dev *udev, unsigned long event_cause) { + u32 sw_handshake_0_reg; + if (test_bit(UBASE_ASYNC_EVENT_CRQ_B, &event_cause)) ubase_write_dev(&udev->hw, UBASE_VECTOR0_CMDQ_SRC_REG, BIT(UBASE_VECTOR0_RX_CMDQ_INT_B)); + + if (test_bit(UBASE_ASYNC_EVENT_RAS_B, &event_cause)) { + sw_handshake_0_reg = ubase_read_dev(&udev->hw, + UBASE_SW_HANDSHAKE_0_REG); + sw_handshake_0_reg &= ~(BIT(UBASE_SW_HANDSHAKE_0_RAS_B) | + BIT(UBASE_SW_HANDSHAKE_0_PORT_RESET_B) | + BIT(UBASE_SW_HANDSHAKE_0_HIMAC_RESET_B)); + ubase_write_dev(&udev->hw, UBASE_SW_HANDSHAKE_0_REG, + sw_handshake_0_reg); + } + if (test_bit(UBASE_ASYNC_EVENT_CTRLQ_B, &event_cause)) ubase_write_dev(&udev->hw, UBASE_VECTOR0_CTRLQ_SRC_REG, BIT(UBASE_VECTOR0_RX_CTRLQ_INT_B)); @@ -153,6 +188,7 @@ static void ubase_clear_all_event_cause(struct ubase_dev *udev) { ubase_clear_event_cause(udev, BIT(UBASE_ASYNC_EVENT_CRQ_B) | + BIT(UBASE_ASYNC_EVENT_RAS_B) | BIT(UBASE_ASYNC_EVENT_CTRLQ_B)); } @@ -166,6 +202,14 @@ static void ubase_crq_task_schedule(struct ubase_dev *udev) } } +static void ubase_errhandle_task_schedule(struct ubase_dev *udev) +{ + if (!test_and_set_bit(UBASE_SERVICE_STATE_ERR_SCHED, + &udev->service_task.state)) + mod_delayed_work(udev->ubase_wq, + &udev->service_task.service_task, 0); +} + static void ubase_ctrlq_task_schedule(struct ubase_dev *udev) { if (!test_and_set_bit(UBASE_STATE_CTRLQ_SERVICE_SCHED, @@ -186,14 +230,56 @@ static int ubase_reg_event_handler(struct ubase_dev *udev) if (test_bit(UBASE_ASYNC_EVENT_CRQ_B, &event_cause)) ubase_crq_task_schedule(udev); + if (test_bit(UBASE_ASYNC_EVENT_RAS_B, &event_cause)) + ubase_errhandle_task_schedule(udev); + if (test_bit(UBASE_ASYNC_EVENT_CTRLQ_B, &event_cause)) ubase_ctrlq_task_schedule(udev); + ubase_clear_event_cause(udev, event_cause); ubase_enable_misc_vector(udev, true); return event_cause ? IRQ_HANDLED : IRQ_NONE; } +static bool ubase_is_udma_queue_id(u32 queue_id, struct ubase_res_caps *caps) +{ + u32 end = caps->start_idx + caps->max_cnt; + u32 begin = caps->start_idx; + + return begin <= queue_id && queue_id < end; +} + +static inline bool ubase_is_public_or_rsvd_jetty(u32 queue_id, + struct ubase_caps *caps) +{ + return queue_id < caps->public_jetty_cnt + caps->rsvd_jetty_cnt; +} + +static bool ubase_is_udma_ras_event(struct ubase_dev *udev, u32 queue_id, + u8 sub_type) +{ + switch (sub_type) { + case UBASE_SUBEVENT_TYPE_JFS_CHECK_ERROR: + return ubase_is_public_or_rsvd_jetty(queue_id, + &udev->caps.dev_caps) || + ubase_is_udma_queue_id(queue_id, + &udev->caps.udma_caps.jfs); + case UBASE_SUBEVENT_TYPE_JFR_CHECK_ERROR: + return ubase_is_udma_queue_id(queue_id, + &udev->caps.udma_caps.jfr); + case UBASE_SUBEVENT_TYPE_JFC_CHECK_ERROR: + return ubase_is_udma_queue_id(queue_id, + &udev->caps.udma_caps.jfc); + case UBASE_SUBEVENT_TYPE_JETTY_GROUP_CHECK_ERROR: + return true; + default: + break; + } + + return false; +} + static bool ubase_is_udma_tp_event(struct ubase_dev *udev, u32 tpn) { struct ubase_tpg *tpg = udev->tp_ctx.tpg; @@ -227,7 +313,13 @@ static bool ubase_is_udma_event(struct ubase_dev *udev, u8 event_type, case UBASE_EVENT_TYPE_CHECK_TOKEN: return true; case UBASE_EVENT_TYPE_TP_FLUSH_DONE: + case UBASE_EVENT_TYPE_TP_LEVEL_ERROR: return ubase_is_udma_tp_event(udev, queue_id); + case UBASE_EVENT_TYPE_JFR_LIMIT_REACHED: + return ubase_is_udma_queue_id(queue_id, + &udev->caps.udma_caps.jfr); + case UBASE_EVENT_TYPE_JETTY_LEVEL_ERROR: + return ubase_is_udma_ras_event(udev, queue_id, sub_type); default: break; } @@ -998,6 +1090,23 @@ void ubase_comp_unregister(struct auxiliary_device *adev) } EXPORT_SYMBOL(ubase_comp_unregister); +static int ubase_ae_entity_level_error(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct ubase_event_nb *ev_nb = container_of(nb, + struct ubase_event_nb, nb); + struct ubase_aeq_notify_info *info = data; + struct ubase_dev *udev = ev_nb->back; + + ubase_err(udev, + "ubase async event for ue level error, event_type=0x%x, sub_type=0x%x.\n", + info->event_type, info->sub_type); + + __ubase_reset_event(udev, UBASE_UE_RESET); + + return 0; +} + static void __ubase_unregister_ae_event(struct ubase_dev *udev, int num) { int i; @@ -1024,6 +1133,16 @@ int ubase_register_ae_event(struct ubase_dev *udev) UBASE_EVENT_TYPE_TP_FLUSH_DONE, { ubase_ae_tp_flush_done }, udev + }, { + UBASE_DRV_UNIC, + UBASE_EVENT_TYPE_TP_LEVEL_ERROR, + { ubase_ae_tp_level_error }, + udev + }, { + UBASE_DRV_UNIC, + UBASE_EVENT_TYPE_ENTITY_LEVEL_ERROR, + { ubase_ae_entity_level_error }, + udev } }; struct ubase_aeq *aeq = &udev->irq_table.aeq; diff --git a/drivers/ub/ubase/ubase_eq.h b/drivers/ub/ubase/ubase_eq.h index 49906c521ef3..a4a15c9144f4 100644 --- a/drivers/ub/ubase/ubase_eq.h +++ b/drivers/ub/ubase/ubase_eq.h @@ -42,13 +42,20 @@ #define UBASE_VECTOR0_CTRLQ_SRC_REG 0x18014 /* Vector0 interrupt control register */ #define UBASE_MISC_VECTOR_REG_OFFSET 0x18020 +/* Software handshake 0 register(RW) */ +#define UBASE_SW_HANDSHAKE_0_REG 0x18044 /* CMDQ register bits for RX event */ #define UBASE_VECTOR0_RX_CMDQ_INT_B 1 /* CTRLQ register bits for RX event */ #define UBASE_VECTOR0_RX_CTRLQ_INT_B 0 +/* Software handshake 0 register bit for ras report */ +#define UBASE_SW_HANDSHAKE_0_RAS_B 0 +#define UBASE_SW_HANDSHAKE_0_PORT_RESET_B 1 +#define UBASE_SW_HANDSHAKE_0_HIMAC_RESET_B 2 enum ubase_async_event_cause_bit { UBASE_ASYNC_EVENT_CRQ_B, + UBASE_ASYNC_EVENT_RAS_B, UBASE_ASYNC_EVENT_CTRLQ_B, }; diff --git a/drivers/ub/ubase/ubase_tp.c b/drivers/ub/ubase/ubase_tp.c index 32b75d107136..2b11b39fb6a4 100644 --- a/drivers/ub/ubase/ubase_tp.c +++ b/drivers/ub/ubase/ubase_tp.c @@ -5,6 +5,7 @@ */ #include "ubase_ctrlq_tp.h" +#include "ubase_reset.h" #include "ubase_tp.h" int ubase_ae_tp_flush_done(struct notifier_block *nb, unsigned long event, @@ -20,6 +21,25 @@ int ubase_ae_tp_flush_done(struct notifier_block *nb, unsigned long event, return ubase_notify_tp_fd_by_ctrlq(udev, tp_num); } +int ubase_ae_tp_level_error(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct ubase_event_nb *ev_nb = container_of(nb, + struct ubase_event_nb, nb); + struct ubase_aeq_notify_info *info = data; + struct ubase_dev *udev = ev_nb->back; + u32 queue_num; + + queue_num = info->aeqe->event.queue_event.num; + ubase_err(udev, + "ubase recv tp level error, event_type = 0x%x, sub_type = 0x%x, queue_num = %u\n", + info->event_type, info->sub_type, queue_num); + + __ubase_reset_event(udev, UBASE_UE_RESET); + + return 0; +} + int ubase_dev_init_tp_tpg(struct ubase_dev *udev) { if (!ubase_utp_supported(udev) || !ubase_dev_urma_supported(udev)) diff --git a/drivers/ub/ubase/ubase_tp.h b/drivers/ub/ubase/ubase_tp.h index 3d1d14dd7a25..965535a18f1a 100644 --- a/drivers/ub/ubase/ubase_tp.h +++ b/drivers/ub/ubase/ubase_tp.h @@ -26,6 +26,8 @@ struct ubase_tpg { int ubase_ae_tp_flush_done(struct notifier_block *nb, unsigned long event, void *data); +int ubase_ae_tp_level_error(struct notifier_block *nb, unsigned long event, + void *data); int ubase_dev_init_tp_tpg(struct ubase_dev *udev); void ubase_dev_uninit_tp_tpg(struct ubase_dev *udev); diff --git a/include/ub/ubase/ubase_comm_eq.h b/include/ub/ubase/ubase_comm_eq.h index 8e355b710025..fe97de1dd1ac 100644 --- a/include/ub/ubase/ubase_comm_eq.h +++ b/include/ub/ubase/ubase_comm_eq.h @@ -19,6 +19,23 @@ enum ubase_drv_type { UBASE_DRV_UVB, UBASE_DRV_MAX, }; +enum ubase_subevent_jetty_type { + UBASE_SUBEVENT_TYPE_JFS_CHECK_ERROR = 0x01, + UBASE_SUBEVENT_TYPE_JFR_CHECK_ERROR, + UBASE_SUBEVENT_TYPE_JFC_CHECK_ERROR, + UBASE_SUBEVENT_TYPE_JETTY_GROUP_CHECK_ERROR +}; + +enum ubase_subevent_tp_type { + UBASE_SUBEVENT_TYPE_TP_RETRY_ERROR = 0x01, + UBASE_SUBEVENT_TYPE_TP_UNEXPECTED_OPCODE, + UBASE_SUBEVENT_TYPE_TP_PSN_MSN_MISMATCH, + UBASE_SUBEVENT_TYPE_TP_PORT_DOWN +}; + +enum ubase_subevent_ue_type { + UBASE_SUBEVENT_TYPE_ENTITY_LEVEL_ERROR = 0x00 +}; enum ubase_event_type { UBASE_EVENT_TYPE_RESERVED = 0x00, -- Gitee From d057397249691a0423bc9e551d4cf7a9268e0529 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Wed, 29 Oct 2025 15:30:28 +0800 Subject: [PATCH 170/214] ub: ubase: support pre-alloc 2M pages. ANBZ: #45460 commit da7d25293a382c506d3d46f06aff4074d881f24f openEuler. This patch add the feature that UBASE supports pre-alloc 2M pages for DMA buf to improve hardware performance. This feature is controlled by a switch from firmware. If switch on, ubase will try to alloc 2M Bytes pages for JFS ctx buffer, TA timer buffer and udma rc queue buffer during the probe process. If alloc 2M pages failed, then try to alloc memory with the default page size. Signed-off-by: Chuan Wu Signed-off-by: Xiaobo Zhang Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/Makefile | 2 +- drivers/ub/ubase/debugfs/ubase_debugfs.c | 22 ++ drivers/ub/ubase/debugfs/ubase_qos_debugfs.c | 1 + drivers/ub/ubase/ubase.h | 1 + drivers/ub/ubase/ubase_dev.c | 3 +- drivers/ub/ubase/ubase_dev.h | 36 ++ drivers/ub/ubase/ubase_hw.c | 35 +- drivers/ub/ubase/ubase_pmem.c | 330 +++++++++++++++++++ drivers/ub/ubase/ubase_pmem.h | 15 + drivers/ub/ubase/ubase_qos_hw.c | 25 ++ include/ub/ubase/ubase_comm_dev.h | 1 + 11 files changed, 468 insertions(+), 3 deletions(-) create mode 100644 drivers/ub/ubase/ubase_pmem.c create mode 100644 drivers/ub/ubase/ubase_pmem.h diff --git a/drivers/ub/ubase/Makefile b/drivers/ub/ubase/Makefile index f0c279c2a069..b8ed7c83c8c9 100644 --- a/drivers/ub/ubase/Makefile +++ b/drivers/ub/ubase/Makefile @@ -13,7 +13,7 @@ UBASE_OBJS := ubase_main.o ubase_dev.o ubase_hw.o ubase_cmd.o ubase_ctrlq.o \ debugfs/ubase_debugfs.o ubase_eq.o ubase_mailbox.o ubase_ubus.o \ debugfs/ubase_ctx_debugfs.o debugfs/ubase_qos_debugfs.o \ ubase_qos_hw.o ubase_tp.o ubase_ctrlq_tp.o ubase_reset.o \ - ubase_err_handle.o + ubase_err_handle.o ubase_pmem.o $(MODULE_NAME)-objs := $(UBASE_OBJS) obj-$(CONFIG_UB_UBASE) := ubase.o diff --git a/drivers/ub/ubase/debugfs/ubase_debugfs.c b/drivers/ub/ubase/debugfs/ubase_debugfs.c index 15473dfddee1..515bea015113 100644 --- a/drivers/ub/ubase/debugfs/ubase_debugfs.c +++ b/drivers/ub/ubase/debugfs/ubase_debugfs.c @@ -28,6 +28,20 @@ static int ubase_dbg_dump_rst_info(struct seq_file *s, void *data) return 0; } +static int ubase_dbg_dump_prealloc_mem_info(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + struct ubase_prealloc_mem_info *pmem_info = &udev->pmem_info; + int status; + + status = test_bit(UBASE_STATE_PREALLOC_OK_B, &udev->state_bits); + + seq_printf(s, "status:%s\n", status ? "enabled" : "disabled"); + seq_printf(s, "comm_page_cnt:%u\n", pmem_info->comm.page_cnt); + seq_printf(s, "udma_page_cnt:%u\n", pmem_info->udma.page_cnt); + + return 0; +} static bool __ubase_dbg_dentry_support(struct device *dev, u32 property) { @@ -206,6 +220,14 @@ static struct ubase_dbg_cmd_info ubase_dbg_cmd[] = { .init = __ubase_dbg_seq_file_init, .read_func = ubase_dbg_dump_tm_port_info, }, + { + .name = "prealloc_mem_info", + .dentry_index = UBASE_DBG_DENTRY_ROOT, + .property = UBASE_SUP_URMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_prealloc_mem_info, + }, }; static int ubase_dbg_create_dir(struct device *dev, diff --git a/drivers/ub/ubase/debugfs/ubase_qos_debugfs.c b/drivers/ub/ubase/debugfs/ubase_qos_debugfs.c index 6f12fc3ceb2c..9b8221e422da 100644 --- a/drivers/ub/ubase/debugfs/ubase_qos_debugfs.c +++ b/drivers/ub/ubase/debugfs/ubase_qos_debugfs.c @@ -239,6 +239,7 @@ int ubase_dbg_dump_adev_qos_info(struct seq_file *s, void *data) {"ctp_sl_num: %u\n", qos->ctp_sl_num}, {"nic_sl_num: %u\n", qos->nic_sl_num}, {"nic_vl_num: %u\n", qos->nic_vl_num}, + {"ue_max_vl_id: %u\n", qos->ue_max_vl_id}, }; int i; diff --git a/drivers/ub/ubase/ubase.h b/drivers/ub/ubase/ubase.h index 53fe64654557..474e8384a1d6 100644 --- a/drivers/ub/ubase/ubase.h +++ b/drivers/ub/ubase/ubase.h @@ -12,6 +12,7 @@ #define UBASE_CAP_LEN 3 #define UBASE_MAX_TCG_NUM (4) +#define UBASE_PMEM_PAGE_SIZE (2 * 1024 * 1024UL) /* 2MB */ enum ubase_service_state { UBASE_STATE_CRQ_SERVICE_SCHED, diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index 945f6d75aad7..fae1dce27a65 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -13,6 +13,7 @@ #include "ubase_err_handle.h" #include "ubase_hw.h" #include "ubase_mailbox.h" +#include "ubase_pmem.h" #include "ubase_reset.h" #include "ubase_dev.h" @@ -625,7 +626,7 @@ static const struct ubase_init_function ubase_init_func_map[] = { }, { "prealloc memory", UBASE_SUP_UDMA, 1, - NULL, NULL + ubase_prealloc_mem_init, ubase_prealloc_mem_uninit }, { "init ue", UBASE_SUP_NO_PMU, 0, diff --git a/drivers/ub/ubase/ubase_dev.h b/drivers/ub/ubase/ubase_dev.h index 79ade538aca0..f47021b3e095 100644 --- a/drivers/ub/ubase/ubase_dev.h +++ b/drivers/ub/ubase/ubase_dev.h @@ -325,6 +325,16 @@ static inline bool ubase_dev_uvb_supported(struct ubase_dev *udev) return ubase_get_cap_bit(udev, UBASE_SUPPORT_UVB_B); } +static inline bool ubase_ip_over_urma_supported(struct ubase_dev *udev) +{ + return ubase_get_cap_bit(udev, UBASE_SUPPORT_IP_OVER_URMA_B); +} + +static inline bool ubase_ip_over_urma_utp_supported(struct ubase_dev *udev) +{ + return ubase_get_cap_bit(udev, UBASE_SUPPORT_IP_OVER_URMA_UTP_B); +} + static inline struct ubase_dev *ubase_get_udev_by_adev(struct auxiliary_device *adev) { @@ -370,11 +380,27 @@ static inline bool ubase_dev_eth_mac_supported(struct ubase_dev *udev) return ubase_get_cap_bit(udev, UBASE_SUPPORT_ETH_MAC_B); } +static inline bool ubase_dev_mac_stats_supported(struct ubase_dev *udev) +{ + return ubase_get_cap_bit(udev, UBASE_SUPPORT_MAC_STATS_B); +} + +static inline bool __ubase_dev_prealloc_supported(struct ubase_dev *udev) +{ + return ubase_get_cap_bit(udev, UBASE_SUPPORT_PRE_ALLOC_B); +} + static inline bool ubase_utp_supported(struct ubase_dev *udev) { return ubase_get_cap_bit(udev, UBASE_SUPPORT_UTP_B); } +static inline bool ubase_dev_prealloc_supported(struct ubase_dev *udev) +{ + return __ubase_dev_prealloc_supported(udev) && + PAGE_SIZE != UBASE_PMEM_PAGE_SIZE; +} + static inline u32 ubase_jfs_num(struct ubase_dev *udev) { struct ubase_adev_caps *unic_caps = &udev->caps.unic_caps; @@ -385,6 +411,16 @@ static inline u32 ubase_jfs_num(struct ubase_dev *udev) dev_caps->public_jetty_cnt + dev_caps->rsvd_jetty_cnt; } +static inline u32 ubase_jfs_ctx_align_size(struct ubase_dev *udev) +{ + return ALIGN(ubase_jfs_num(udev) * UBASE_JFS_CTX_SIZE, PAGE_SIZE); +} + +static inline u32 ubase_ta_timer_align_size(struct ubase_dev *udev) +{ + return ALIGN(udev->ta_ctx.timer_buf.size, PAGE_SIZE); +} + static inline bool ubase_mbx_ue_id_is_valid(u16 mbx_ue_id, struct ubase_dev *udev) { diff --git a/drivers/ub/ubase/ubase_hw.c b/drivers/ub/ubase/ubase_hw.c index 0dbe9a5f6d5b..548322881505 100644 --- a/drivers/ub/ubase/ubase_hw.c +++ b/drivers/ub/ubase/ubase_hw.c @@ -412,7 +412,22 @@ static int ubase_alloc_and_fill_ctx_buf(struct ubase_dev *udev, return ret; } -static int ubase_cmd_ctx_buf_alloc(struct ubase_dev *udev, +static int ubase_config_jfs_ctx_buf_by_pmem(struct ubase_dev *udev, + struct ubase_mbx_attr *attr) +{ + struct ubase_cmd_mailbox mailbox; + int ret; + + mailbox.dma = udev->pmem_info.comm.dma_addr; + ret = __ubase_hw_upgrade_ctx(udev, attr, &mailbox); + if (ret) + ubase_err(udev, + "failed to config jfs ctx buf, ret = %d.\n", ret); + + return ret; +} + +int ubase_cmd_ctx_buf_alloc(struct ubase_dev *udev, struct ubase_ctx_buf_cap *ctx_buf, struct ubase_mbx_attr *attr) { @@ -422,6 +437,10 @@ static int ubase_cmd_ctx_buf_alloc(struct ubase_dev *udev, if (!size) return 0; + if (attr->op == UBASE_MB_WRITE_JFS_CONTEXT_VA && + test_bit(UBASE_STATE_PREALLOC_OK_B, &udev->state_bits)) + return ubase_config_jfs_ctx_buf_by_pmem(udev, attr); + xa_init(&ctx_buf->ctx_xa); ret = ubase_alloc_and_fill_ctx_buf(udev, ctx_buf, attr, size); if (ret) @@ -530,11 +549,25 @@ static int ubase_config_dma_buf(struct ubase_dev *udev, u16 opc, u64 dma_addr) return ret; } +static int ubase_config_ta_timer_buf_by_pmem(struct ubase_dev *udev, u16 opc) +{ + u64 dma_addr, offset; + + offset = ubase_jfs_ctx_align_size(udev); + dma_addr = udev->pmem_info.comm.dma_addr + offset; + + return ubase_config_dma_buf(udev, opc, dma_addr); +} + static int ubase_init_dma_buf(struct ubase_dev *udev, struct ubase_dma_buf *buf, u16 opc) { int ret; + if (opc == UBASE_OPC_TA_TIMER_VA_CONFIG && + test_bit(UBASE_STATE_PREALLOC_OK_B, &udev->state_bits)) + return ubase_config_ta_timer_buf_by_pmem(udev, opc); + buf->addr = dma_alloc_coherent(udev->dev, buf->size, &buf->dma_addr, GFP_KERNEL); if (!buf->addr) diff --git a/drivers/ub/ubase/ubase_pmem.c b/drivers/ub/ubase/ubase_pmem.c new file mode 100644 index 000000000000..21b755d3cc8c --- /dev/null +++ b/drivers/ub/ubase/ubase_pmem.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include +#include + +#include "ubase_pmem.h" + +static u16 ubase_calc_comm_page_cnt(struct ubase_dev *udev) +{ + u32 ta_timer_size = ubase_ta_timer_align_size(udev); + u32 jfs_ctx_size = ubase_jfs_ctx_align_size(udev); + + return DIV_ROUND_UP(jfs_ctx_size + ta_timer_size, UBASE_PMEM_PAGE_SIZE); +} + +static inline u16 ubase_calc_que_page_cnt(u32 que_num, u64 que_size) +{ + if (!que_size) + return 0; + + return que_size > UBASE_PMEM_PAGE_SIZE ? + DIV_ROUND_UP(que_size * que_num, UBASE_PMEM_PAGE_SIZE) : + DIV_ROUND_UP(que_num, UBASE_PMEM_PAGE_SIZE / que_size); +} + +static u16 ubase_calc_udma_page_cnt(struct ubase_dev *udev) +{ +#define RCE_SIZE 64 + + struct ubase_adev_caps *udma_caps = &udev->caps.udma_caps; + u32 rcq_size; + + rcq_size = ALIGN(udma_caps->rc_que_depth * RCE_SIZE, PAGE_SIZE); + + return ubase_calc_que_page_cnt(udma_caps->rc_max_cnt, rcq_size); +} + +static int ubase_init_pmem_ctx(struct ubase_dev *udev, const char *type, + struct ubase_pmem_ctx *ctx, u16 page_cnt) +{ + if (!page_cnt) + return 0; + + ctx->pgs = kcalloc(page_cnt, sizeof(struct page *), GFP_KERNEL); + if (!ctx->pgs) { + ubase_err(udev, "failed to alloc mem for %s pgs.page_cnt=%u\n", + type, page_cnt); + return -ENOMEM; + } + + ctx->sg = kzalloc(page_cnt * sizeof(struct scatterlist), GFP_KERNEL); + if (!ctx->sg) { + ubase_err(udev, "failed to alloc mem for %s sg.page_cnt=%u\n", + type, page_cnt); + goto free_pgs; + } + + ctx->page_cnt = page_cnt; + return 0; + +free_pgs: + kfree(ctx->pgs); + return -ENOMEM; +} + +static int ubase_init_comm_pmem_ctx(struct ubase_dev *udev) +{ + return ubase_init_pmem_ctx(udev, "comm", &udev->pmem_info.comm, + ubase_calc_comm_page_cnt(udev)); +} + +static int ubase_init_udma_pmem_ctx(struct ubase_dev *udev) +{ + return ubase_init_pmem_ctx(udev, "udma", &udev->pmem_info.udma, + ubase_calc_udma_page_cnt(udev)); +} + +static void ubase_uninit_pmem_ctx(struct ubase_pmem_ctx *ctx) +{ + if (!ctx->page_cnt) + return; + + kfree(ctx->pgs); + ctx->pgs = NULL; + + kfree(ctx->sg); + ctx->sg = NULL; + + ctx->page_cnt = 0; +} + +static void ubase_uninit_comm_pmem_ctx(struct ubase_dev *udev) +{ + ubase_uninit_pmem_ctx(&udev->pmem_info.comm); +} + +static void ubase_uninit_udma_pmem_ctx(struct ubase_dev *udev) +{ + ubase_uninit_pmem_ctx(&udev->pmem_info.udma); +} + +static int ubase_alloc_pmem(struct ubase_dev *udev, const char *type, + struct page **pgs, int page_cnt) +{ + int order = get_order(UBASE_PMEM_PAGE_SIZE); + int i; + + for (i = 0; i < page_cnt; i++) { + pgs[i] = alloc_pages(GFP_KERNEL | __GFP_ZERO, order); + if (!pgs[i]) { + ubase_err(udev, + "failed to alloc %s pages[%d], page_cnt = %d, order = %d.\n", + type, i, page_cnt, order); + goto free_pgs; + } + } + + return 0; + +free_pgs: + for (i -= 1; i >= 0; i--) + __free_pages(pgs[i], order); + + return -ENOMEM; +} + +static int ubase_alloc_comm_pmem(struct ubase_dev *udev) +{ + struct ubase_pmem_ctx *ctx = &udev->pmem_info.comm; + + return ubase_alloc_pmem(udev, "comm", ctx->pgs, ctx->page_cnt); +} + +static int ubase_alloc_udma_pmem(struct ubase_dev *udev) +{ + struct ubase_pmem_ctx *ctx = &udev->pmem_info.udma; + + return ubase_alloc_pmem(udev, "udma", ctx->pgs, ctx->page_cnt); +} + +static void ubase_free_pmem(struct ubase_pmem_ctx *ctx) +{ + int order = get_order(UBASE_PMEM_PAGE_SIZE); + u32 i; + + for (i = 0; i < ctx->page_cnt; i++) + __free_pages(ctx->pgs[i], order); +} + +static void ubase_free_comm_pmem(struct ubase_dev *udev) +{ + ubase_free_pmem(&udev->pmem_info.comm); +} + +static void ubase_free_udma_pmem(struct ubase_dev *udev) +{ + ubase_free_pmem(&udev->pmem_info.udma); +} + +static int ubase_map_pmem(struct ubase_dev *udev, const char *type, + struct ubase_pmem_ctx *ctx) +{ + struct scatterlist *iter; + unsigned int nents; + u32 i; + + if (!ctx->page_cnt) + return 0; + + sg_init_table(ctx->sg, ctx->page_cnt); + + for_each_sg(ctx->sg, iter, ctx->page_cnt, i) + sg_set_page(iter, ctx->pgs[i], UBASE_PMEM_PAGE_SIZE, 0); + + nents = dma_map_sg(udev->dev, ctx->sg, ctx->page_cnt, DMA_BIDIRECTIONAL); + if (!nents) { + ubase_err(udev, "failed to map %s pages, page_cnt = %u.\n", + type, ctx->page_cnt); + return -ENOMEM; + } + + ctx->dma_addr = sg_dma_address(&ctx->sg[0]); + return 0; +} + +static int ubase_map_comm_pmem(struct ubase_dev *udev) +{ + return ubase_map_pmem(udev, "comm", &udev->pmem_info.comm); +} + +static int ubase_map_udma_pmem(struct ubase_dev *udev) +{ + struct ubase_pmem_caps *pmem = &udev->caps.udma_caps.pmem; + int ret; + + ret = ubase_map_pmem(udev, "udma", &udev->pmem_info.udma); + if (ret) + return ret; + + pmem->dma_addr = udev->pmem_info.udma.dma_addr; + pmem->dma_len = udev->pmem_info.udma.page_cnt * UBASE_PMEM_PAGE_SIZE; + + return 0; +} + +static void ubase_unmap_pmem(struct ubase_dev *udev, struct ubase_pmem_ctx *ctx) +{ + if (!ctx->page_cnt) + return; + + dma_unmap_sg(udev->dev, ctx->sg, ctx->page_cnt, DMA_BIDIRECTIONAL); + + ctx->dma_addr = 0; +} + +static void ubase_unmap_comm_pmem(struct ubase_dev *udev) +{ + ubase_unmap_pmem(udev, &udev->pmem_info.comm); +} + +static void ubase_unmap_udma_pmem(struct ubase_dev *udev) +{ + struct ubase_pmem_caps *pmem = &udev->caps.udma_caps.pmem; + + ubase_unmap_pmem(udev, &udev->pmem_info.udma); + + pmem->dma_addr = 0; + pmem->dma_len = 0; +} + +static const struct ubase_pmem_init_func { + int (*init)(struct ubase_dev *udev); + void (*uninit)(struct ubase_dev *udev); +} ubase_pmem_init_map[] = { + { ubase_init_comm_pmem_ctx, ubase_uninit_comm_pmem_ctx }, + { ubase_init_udma_pmem_ctx, ubase_uninit_udma_pmem_ctx }, + { ubase_alloc_comm_pmem, ubase_free_comm_pmem }, + { ubase_alloc_udma_pmem, ubase_free_udma_pmem }, + { ubase_map_comm_pmem, ubase_unmap_comm_pmem }, + { ubase_map_udma_pmem, ubase_unmap_udma_pmem }, +}; + +static int __ubase_prealloc_mem_init(struct ubase_dev *udev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ubase_pmem_init_map); i++) { + if (ubase_pmem_init_map[i].init(udev)) + goto init_fail; + } + + return 0; + +init_fail: + for (i -= 1; i >= 0; i--) + ubase_pmem_init_map[i].uninit(udev); + + return -ENOMEM; +} + +static void ubase_reset_pmem(struct ubase_pmem_ctx *ctx) +{ + void *va; + u32 i; + + for (i = 0; i < ctx->page_cnt; i++) { + va = page_address(ctx->pgs[i]); + memset(va, 0, UBASE_PMEM_PAGE_SIZE); + } +} + +static void ubase_prealloc_mem_reinit(struct ubase_dev *udev) +{ + ubase_reset_pmem(&udev->pmem_info.comm); + ubase_reset_pmem(&udev->pmem_info.udma); +} + +int ubase_prealloc_mem_init(struct ubase_dev *udev) +{ + if (test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) { + ubase_prealloc_mem_reinit(udev); + return 0; + } + + if (!ubase_dev_prealloc_supported(udev)) + return 0; + + ubase_info(udev, "pre-alloc memory feature is enabled.\n"); + + if (__ubase_prealloc_mem_init(udev)) { + ubase_warn(udev, + "warning: pre-alloc pages failed, will use normal pages.\n"); + return 0; /* use normal pages, must return success. */ + } + + set_bit(UBASE_STATE_PREALLOC_OK_B, &udev->state_bits); + + return 0; +} + +void ubase_prealloc_mem_uninit(struct ubase_dev *udev) +{ + int i; + + if (test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + return; + + if (!test_and_clear_bit(UBASE_STATE_PREALLOC_OK_B, &udev->state_bits)) + return; + + for (i = ARRAY_SIZE(ubase_pmem_init_map) - 1; i >= 0; i--) + ubase_pmem_init_map[i].uninit(udev); +} + +bool ubase_adev_prealloc_supported(struct auxiliary_device *adev) +{ + struct ubase_dev *udev; + + if (!adev) + return false; + + udev = __ubase_get_udev_by_adev(adev); + + return test_bit(UBASE_STATE_PREALLOC_OK_B, &udev->state_bits); +} +EXPORT_SYMBOL(ubase_adev_prealloc_supported); diff --git a/drivers/ub/ubase/ubase_pmem.h b/drivers/ub/ubase/ubase_pmem.h new file mode 100644 index 000000000000..dc527c666346 --- /dev/null +++ b/drivers/ub/ubase/ubase_pmem.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UBASE_PMEM_H__ +#define __UBASE_PMEM_H__ + +#include "ubase_dev.h" + +int ubase_prealloc_mem_init(struct ubase_dev *udev); +void ubase_prealloc_mem_uninit(struct ubase_dev *udev); + +#endif /* _UBASE_PMEM_H */ diff --git a/drivers/ub/ubase/ubase_qos_hw.c b/drivers/ub/ubase/ubase_qos_hw.c index 6889d2e840c1..5a5881f79547 100644 --- a/drivers/ub/ubase/ubase_qos_hw.c +++ b/drivers/ub/ubase/ubase_qos_hw.c @@ -883,6 +883,29 @@ static void ubase_init_udma_dscp_vl(struct ubase_dev *udev) qos->dscp_vl[i] = qos->tp_req_vl[0]; } +static void ubase_parse_max_vl(struct ubase_dev *udev) +{ + struct ubase_adev_caps *udma_caps = &udev->caps.udma_caps; + struct ubase_adev_qos *qos = &udev->qos; + u8 i, max_vl = 0; + + for (i = 0; i < qos->nic_vl_num; i++) + max_vl = max(qos->nic_vl[i], max_vl); + + for (i = 0; i < qos->tp_vl_num; i++) + max_vl = max(qos->tp_req_vl[i] + + qos->tp_resp_vl_offset, max_vl); + + for (i = 0; i < qos->ctp_vl_num; i++) + max_vl = max(qos->ctp_req_vl[i] + + qos->ctp_resp_vl_offset, max_vl); + + qos->ue_max_vl_id = max_vl; + + if (ubase_dev_urma_supported(udev)) + udma_caps->rc_max_cnt *= (max_vl + 1); +} + static int ubase_parse_sl_vl(struct ubase_dev *udev) { int ret; @@ -901,6 +924,8 @@ static int ubase_parse_sl_vl(struct ubase_dev *udev) if (ubase_utp_supported(udev) && ubase_dev_urma_supported(udev)) udev->caps.unic_caps.tpg.max_cnt = udev->qos.nic_vl_num; + ubase_parse_max_vl(udev); + return 0; } diff --git a/include/ub/ubase/ubase_comm_dev.h b/include/ub/ubase/ubase_comm_dev.h index db65dae3b75c..fd475afbf9d8 100644 --- a/include/ub/ubase/ubase_comm_dev.h +++ b/include/ub/ubase/ubase_comm_dev.h @@ -221,6 +221,7 @@ struct ubase_bus_eid { bool ubase_adev_ubl_supported(struct auxiliary_device *adev); bool ubase_adev_ctrlq_supported(struct auxiliary_device *adev); bool ubase_adev_eth_mac_supported(struct auxiliary_device *adev); +bool ubase_adev_prealloc_supported(struct auxiliary_device *aux_dev); struct ubase_resource_space *ubase_get_io_base(struct auxiliary_device *adev); struct ubase_resource_space *ubase_get_mem_base(struct auxiliary_device *adev); -- Gitee From 6d88e72685640bfd47286eff47fd79fbab428bfb Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Wed, 29 Oct 2025 15:56:56 +0800 Subject: [PATCH 171/214] ub: ubase: adds the function to get ub port stats for unic and other aux driver ANBZ: #45460 commit 9c1792c8e55980e78426ee2d7af2bd78907c7cc3 openEuler. This patch adds MAC layer statistical information query capabilities to the ubase driver, primarily implementing the following: 1. Core Functionality: - Added the `ubase_get_ub_port_stats` interface, which supports querying MAC statistics for a specified port. - Implemented the internal function `ubase_update_mac_stats` to interact with hardware through command queues. 2. Data Structures: - Defined the `ubase_ub_dl_stats` structure, which includes comprehensive MAC layer statistical fields. - Supports packet statistics for 16 virtual channels (VL), including bus packet statistics and control packet statistics. - Includes credit information, retransmission statistics, and CRC error statistics. 3. Hardware Interaction: - Added the `UBASE_OPC_STATS_MAC_ALL` opcode for querying MAC statistics. - Dynamically allocates command buffers to support device capability detection. This functionality provides crucial MAC layer statistical data support for network performance monitoring and fault diagnosis. Signed-off-by: Fengyan Mu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/Makefile | 2 +- drivers/ub/ubase/ubase_stats.c | 67 +++++++++++++++++++++ drivers/ub/ubase/ubase_stats.h | 18 ++++++ include/ub/ubase/ubase_comm_cmd.h | 1 + include/ub/ubase/ubase_comm_stats.h | 91 +++++++++++++++++++++++++++++ 5 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubase/ubase_stats.c create mode 100644 drivers/ub/ubase/ubase_stats.h diff --git a/drivers/ub/ubase/Makefile b/drivers/ub/ubase/Makefile index b8ed7c83c8c9..4c14450d516a 100644 --- a/drivers/ub/ubase/Makefile +++ b/drivers/ub/ubase/Makefile @@ -13,7 +13,7 @@ UBASE_OBJS := ubase_main.o ubase_dev.o ubase_hw.o ubase_cmd.o ubase_ctrlq.o \ debugfs/ubase_debugfs.o ubase_eq.o ubase_mailbox.o ubase_ubus.o \ debugfs/ubase_ctx_debugfs.o debugfs/ubase_qos_debugfs.o \ ubase_qos_hw.o ubase_tp.o ubase_ctrlq_tp.o ubase_reset.o \ - ubase_err_handle.o ubase_pmem.o + ubase_err_handle.o ubase_pmem.o ubase_stats.o $(MODULE_NAME)-objs := $(UBASE_OBJS) obj-$(CONFIG_UB_UBASE) := ubase.o diff --git a/drivers/ub/ubase/ubase_stats.c b/drivers/ub/ubase/ubase_stats.c new file mode 100644 index 000000000000..b30e839ebb0b --- /dev/null +++ b/drivers/ub/ubase/ubase_stats.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include "ubase_cmd.h" +#include "ubase_stats.h" + +static int ubase_update_mac_stats(struct ubase_dev *udev, u16 port_id, u64 *data, + u16 size, bool is_accumulate) +{ + u16 mac_stats_num = udev->caps.dev_caps.mac_stats_num; + struct ubase_query_mac_stats_cmd *cmd; + struct ubase_cmd_buf in, out; + u16 i, cmd_size; + int ret; + + if (!ubase_dev_mac_stats_supported(udev)) { + ubase_err(udev, "not support get mac stats.\n"); + return -EOPNOTSUPP; + } + + cmd_size = mac_stats_num * sizeof(u64) + sizeof(*cmd); + cmd = kzalloc(cmd_size, GFP_KERNEL); + if (!cmd) { + ubase_err(udev, "failed to alloc cmdq out_regs.\n"); + return -ENOMEM; + } + + cmd->port_id = cpu_to_le16(port_id); + ubase_fill_inout_buf(&in, UBASE_OPC_STATS_MAC_ALL, true, cmd_size, cmd); + ubase_fill_inout_buf(&out, UBASE_OPC_STATS_MAC_ALL, true, cmd_size, cmd); + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret) { + ubase_err(udev, "failed to get mac stats, ret = %d.\n", ret); + goto out_send_cmd_fail; + } + + mac_stats_num = min_t(u16, size, mac_stats_num); + if (is_accumulate) + for (i = 0; i < mac_stats_num; i++) + *data++ += le64_to_cpu(cmd->stats_val[i]); + else + for (i = 0; i < mac_stats_num; i++) + *data++ = le64_to_cpu(cmd->stats_val[i]); + +out_send_cmd_fail: + kfree(cmd); + + return ret; +} + +int ubase_get_ub_port_stats(struct auxiliary_device *adev, u16 port_id, + struct ubase_ub_dl_stats *data) +{ + struct ubase_dev *udev; + + if (!adev || !data) + return -EINVAL; + + udev = __ubase_get_udev_by_adev(adev); + + return ubase_update_mac_stats(udev, port_id, (u64 *)data, + sizeof(*data) / sizeof(u64), false); +} +EXPORT_SYMBOL(ubase_get_ub_port_stats); diff --git a/drivers/ub/ubase/ubase_stats.h b/drivers/ub/ubase/ubase_stats.h new file mode 100644 index 000000000000..b3f6e5d788cc --- /dev/null +++ b/drivers/ub/ubase/ubase_stats.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UBASE_STATS_H__ +#define __UBASE_STATS_H__ + +#include + +struct ubase_query_mac_stats_cmd { + __le16 port_id; + u8 resv[2]; + __le64 stats_val[]; +}; + +#endif /* _UBASE_STATS_H */ diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index d9fec4164525..9cce91bc5cb8 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -31,6 +31,7 @@ enum ubase_opcode_type { UBASE_OPC_QUERY_FW_VER = 0x0001, UBASE_OPC_QUERY_CTL_INFO = 0x0003, UBASE_OPC_QUERY_COMM_RSRC_PARAM = 0x0030, + UBASE_OPC_STATS_MAC_ALL = 0x0038, UBASE_OPC_QUERY_BUS_EID = 0x0047, /* NL commands */ diff --git a/include/ub/ubase/ubase_comm_stats.h b/include/ub/ubase/ubase_comm_stats.h index 80d32bc9273b..097f28dda7d6 100644 --- a/include/ub/ubase/ubase_comm_stats.h +++ b/include/ub/ubase/ubase_comm_stats.h @@ -9,6 +9,97 @@ #include +struct ubase_ub_dl_stats { + u64 dl_tx_vl0_pkt_num; + u64 dl_tx_vl1_pkt_num; + u64 dl_tx_vl2_pkt_num; + u64 dl_tx_vl3_pkt_num; + u64 dl_tx_vl4_pkt_num; + u64 dl_tx_vl5_pkt_num; + u64 dl_tx_vl6_pkt_num; + u64 dl_tx_vl7_pkt_num; + u64 dl_tx_vl8_pkt_num; + u64 dl_tx_vl9_pkt_num; + u64 dl_tx_vl10_pkt_num; + u64 dl_tx_vl11_pkt_num; + + u64 dl_rx_vl0_pkt_num; + u64 dl_rx_vl1_pkt_num; + u64 dl_rx_vl2_pkt_num; + u64 dl_rx_vl3_pkt_num; + u64 dl_rx_vl4_pkt_num; + u64 dl_rx_vl5_pkt_num; + u64 dl_rx_vl6_pkt_num; + u64 dl_rx_vl7_pkt_num; + u64 dl_rx_vl8_pkt_num; + u64 dl_rx_vl9_pkt_num; + u64 dl_rx_vl10_pkt_num; + u64 dl_rx_vl11_pkt_num; + + u64 dl_tx_busi_pkt_num; + u64 dl_tx_busi_block_num; + u64 dl_tx_busi_flit_num; + u64 dl_rx_busi_pkt_num; + u64 dl_rx_busi_block_num; + u64 dl_rx_busi_flit_num; + u64 dl_tx_ctrl_pkt_num; + u64 dl_tx_ctrl_pkt_flit_num; + u64 dl_rx_ctrl_pkt_num; + u64 dl_rx_ctrl_pkt_flit_num; + + u64 dl_rx_vl0_from_nl_crd; + u64 dl_rx_vl1_from_nl_crd; + u64 dl_rx_vl2_from_nl_crd; + u64 dl_rx_vl3_from_nl_crd; + u64 dl_rx_vl4_from_nl_crd; + u64 dl_rx_vl5_from_nl_crd; + u64 dl_rx_vl6_from_nl_crd; + u64 dl_rx_vl7_from_nl_crd; + u64 dl_rx_vl8_from_nl_crd; + u64 dl_rx_vl9_from_nl_crd; + u64 dl_rx_vl10_from_nl_crd; + u64 dl_rx_vl11_from_nl_crd; + + u64 dl_tx_vl0_to_nl_crd; + u64 dl_tx_vl1_to_nl_crd; + u64 dl_tx_vl2_to_nl_crd; + u64 dl_tx_vl3_to_nl_crd; + u64 dl_tx_vl4_to_nl_crd; + u64 dl_tx_vl5_to_nl_crd; + u64 dl_tx_vl6_to_nl_crd; + u64 dl_tx_vl7_to_nl_crd; + u64 dl_tx_vl8_to_nl_crd; + u64 dl_tx_vl9_to_nl_crd; + u64 dl_tx_vl10_to_nl_crd; + u64 dl_tx_vl11_to_nl_crd; + + u64 dl_tx_recv_ack_flit; + u64 dl_rx_send_ack_flit; + u64 dl_retry_req_sum; + u64 dl_retry_ack_sum; + u64 dl_crc_error_sum; + + u64 dl_tx_vl12_pkt_num; + u64 dl_tx_vl13_pkt_num; + u64 dl_tx_vl14_pkt_num; + u64 dl_tx_vl15_pkt_num; + + u64 dl_rx_vl12_pkt_num; + u64 dl_rx_vl13_pkt_num; + u64 dl_rx_vl14_pkt_num; + u64 dl_rx_vl15_pkt_num; + + u64 dl_rx_vl12_from_nl_crd; + u64 dl_rx_vl13_from_nl_crd; + u64 dl_rx_vl14_from_nl_crd; + u64 dl_rx_vl15_from_nl_crd; + + u64 dl_tx_vl12_to_nl_crd; + u64 dl_tx_vl13_to_nl_crd; + u64 dl_tx_vl14_to_nl_crd; + u64 dl_tx_vl15_to_nl_crd; +}; + struct ubase_eth_mac_stats { u64 tx_fragment_pkts; u64 tx_undersize_pkts; -- Gitee From c90b648759a5ff569d6e29de98731ef2b4de2612 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Wed, 29 Oct 2025 16:14:05 +0800 Subject: [PATCH 172/214] ub: ubase: pf supports debugfs query port/vl current. ANBZ: #45460 commit 0d814ca3397556cdd5456500715235cf321f2adb openEuler. This is a patch to add performance statistics functionality to the ubase driver. The main changes include: 1. Adding a perf_stats interface in debugfs for querying bandwidth statistics of ports and VLANs. 2. Adding hardware commands related to performance statistics. 3. Implementing the core functionality of performance statistics, ubase_perf_stats, which supports: - Specifying statistical periods and port bitmaps. - Querying receive and transmit bandwidth at the port and VLAN levels. - Thread-safe statistical operations (protected by mutex locks). 4. Exporting the performance statistics interface for use by other modules. Signed-off-by: Zihao Sheng Signed-off-by: Xiongchuan Zhou Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/debugfs/ubase_debugfs.c | 77 ++++++++++++ drivers/ub/ubase/ubase_cmd.h | 18 +++ drivers/ub/ubase/ubase_dev.c | 4 + drivers/ub/ubase/ubase_hw.c | 149 ++++++++++++++++++++++- drivers/ub/ubase/ubase_hw.h | 10 ++ include/ub/ubase/ubase_comm_cmd.h | 7 ++ include/ub/ubase/ubase_comm_stats.h | 19 +++ 7 files changed, 283 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubase/debugfs/ubase_debugfs.c b/drivers/ub/ubase/debugfs/ubase_debugfs.c index 515bea015113..d7495d8b4ef7 100644 --- a/drivers/ub/ubase/debugfs/ubase_debugfs.c +++ b/drivers/ub/ubase/debugfs/ubase_debugfs.c @@ -9,7 +9,9 @@ #include #include "ubase_dev.h" +#include "ubase_hw.h" #include "ubase_qos_debugfs.h" +#include "ubase_stats.h" #include "ubase_debugfs.h" static struct dentry *ubase_dbgfs_root; @@ -28,6 +30,73 @@ static int ubase_dbg_dump_rst_info(struct seq_file *s, void *data) return 0; } +static void ubase_dbg_fill_single_port(struct seq_file *s, + struct ubase_perf_stats_result *stats) +{ + int i; + + seq_printf(s, "\tport_id: %u\n", stats->port_id); + seq_printf(s, "\tport_tx_bw: %u(kbps)\n", le32_to_cpu(stats->tx_port_bw)); + seq_printf(s, "\tport_rx_bw: %u(kbps)\n", le32_to_cpu(stats->rx_port_bw)); + seq_puts(s, "\tvl tx_bw(kbps) rx_bw(kbps)\n"); + + for (i = 0; i < UBASE_STATS_MAX_VL_NUM; i++) { + seq_printf(s, "\t%-5d", i); + seq_printf(s, "%-21u", le32_to_cpu(stats->tx_vl_bw[i])); + seq_printf(s, "%-21u", le32_to_cpu(stats->rx_vl_bw[i])); + seq_puts(s, "\n"); + } + seq_puts(s, "\n"); +} + +static int ubase_dbg_dump_perf_stats_ub(struct seq_file *s, + struct ubase_dev *udev) +{ +#define UBASE_UB_PERF_STATS_PERIOD 10 +#define UBASE_QUERY_ALL_BITMAP 0 + + struct ubase_perf_stats_result *stats; + struct device *dev = udev->dev; + int ret, i; + + stats = devm_kcalloc(dev, UBASE_MAX_PORT_NUM, + sizeof(struct ubase_perf_stats_result), GFP_KERNEL); + if (!stats) + return -ENOMEM; + + ret = __ubase_perf_stats(udev, UBASE_QUERY_ALL_BITMAP, + UBASE_UB_PERF_STATS_PERIOD, stats, + UBASE_MAX_PORT_NUM); + if (ret) { + devm_kfree(dev, stats); + return ret; + } + + seq_printf(s, "perf_stats_period: %d(ms)\n", UBASE_UB_PERF_STATS_PERIOD); + seq_puts(s, "port bandwidth info:\n"); + + for (i = 0; i < UBASE_MAX_PORT_NUM; i++) { + if (!stats[i].valid) + break; + ubase_dbg_fill_single_port(s, &stats[i]); + } + + devm_kfree(dev, stats); + + return 0; +} + +static int ubase_dbg_dump_perf_stats(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + int ret = 0; + + if (ubase_dev_ubl_supported(udev)) + ret = ubase_dbg_dump_perf_stats_ub(s, udev); + + return ret; +} + static int ubase_dbg_dump_prealloc_mem_info(struct seq_file *s, void *data) { struct ubase_dev *udev = dev_get_drvdata(s->private); @@ -164,6 +233,14 @@ static struct ubase_dbg_cmd_info ubase_dbg_cmd[] = { .init = __ubase_dbg_seq_file_init, .read_func = ubase_dbg_dump_ets_port_info, }, + { + .name = "perf_stats", + .dentry_index = UBASE_DBG_DENTRY_ROOT, + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_perf_stats, + }, { .name = "rack_vl_bitmap", .dentry_index = UBASE_DBG_DENTRY_QOS, diff --git a/drivers/ub/ubase/ubase_cmd.h b/drivers/ub/ubase/ubase_cmd.h index 008ec4ee780a..86829104e481 100644 --- a/drivers/ub/ubase/ubase_cmd.h +++ b/drivers/ub/ubase/ubase_cmd.h @@ -70,6 +70,24 @@ struct ubase_ue2ue_ctrlq_head { u8 rsv : 6; }; +struct ubase_start_perf_stats_cmd { + __le32 period; + __le32 logic_port_bitmap[2]; + u8 rsv[12]; +}; + +struct ubase_stop_perf_stats_cmd { + __le32 period; /* ms */ + __le16 port_id; + u8 rsv[2]; + + __le32 tx_port_bw; /* kbps */ + __le32 rx_port_bw; + __le32 tx_vl_bw[UBASE_STATS_MAX_VL_NUM]; + __le32 rx_vl_bw[UBASE_STATS_MAX_VL_NUM]; + u8 rsv1[8]; +}; + struct ubase_cfg_ets_vl_sch_cmd { __le16 vl_bitmap; u8 rsvd[2]; diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index fae1dce27a65..7a18ae8e3e11 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -600,6 +600,10 @@ static const struct ubase_init_function ubase_init_func_map[] = { "query hw oor caps", UBASE_SUP_NO_PMU, 0, ubase_query_hw_oor_caps, NULL }, + { + "query port bitmap", UBASE_SUP_ALL, 0, + ubase_query_port_bitmap, NULL + }, { "init irq table", UBASE_SUP_NO_PMU, 1, ubase_irq_table_init, ubase_irq_table_uninit diff --git a/drivers/ub/ubase/ubase_hw.c b/drivers/ub/ubase/ubase_hw.c index 548322881505..d76c0f13222b 100644 --- a/drivers/ub/ubase/ubase_hw.c +++ b/drivers/ub/ubase/ubase_hw.c @@ -16,7 +16,7 @@ #include "ubase_tp.h" #include "ubase_hw.h" -#define UBASE_CTX_REMOVE_ALL (-2) +static DEFINE_MUTEX(ubase_perf_mutex); struct ubase_dma_buf_desc { struct ubase_dma_buf *buf; @@ -654,6 +654,29 @@ int ubase_query_hw_oor_caps(struct ubase_dev *udev) return 0; } +int ubase_query_port_bitmap(struct ubase_dev *udev) +{ + struct ubase_caps *dev_caps = &udev->caps.dev_caps; + struct ubase_query_port_bitmap_resp resp = {0}; + struct ubase_cmd_buf in, out; + int ret; + + ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_PORT_BITMAP, true, 0, NULL); + ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_PORT_BITMAP, true, + sizeof(resp), &resp); + + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret && ret != -EPERM) { + dev_err(udev->dev, + "failed to query port bitmap, ret = %d.\n", ret); + return ret; + } + + dev_caps->logic_port_bitmap = le32_to_cpu(resp.logic_port_bitmap); + + return 0; +} + int ubase_query_controller_info(struct ubase_dev *udev) { struct ubase_caps *dev_caps = &udev->caps.dev_caps; @@ -879,3 +902,127 @@ void ubase_hw_uninit(struct ubase_dev *udev) ubase_uninit_ctx_buf(udev); } + +static int ubase_start_perf_stats(struct ubase_dev *udev, u32 period, + u64 port_bitmap) +{ + struct ubase_start_perf_stats_cmd req = {0}; + struct ubase_cmd_buf in; + int ret; + + req.period = cpu_to_le32(period); + req.logic_port_bitmap[0] = cpu_to_le32(lower_32_bits(port_bitmap)); + req.logic_port_bitmap[1] = cpu_to_le32(upper_32_bits(port_bitmap)); + + __ubase_fill_inout_buf(&in, UBASE_OPC_START_PERF_STATS, false, + sizeof(req), &req); + ret = __ubase_cmd_send_in(udev, &in); + if (ret && ret != -EPERM) + ubase_err(udev, "failed to cfg perf stats period, ret = %d.\n", + ret); + + return ret == -EPERM ? -EOPNOTSUPP : ret; +} + +static int ubase_stop_perf_stats(struct ubase_dev *udev, + struct ubase_stop_perf_stats_cmd *resp, + u32 period, u16 port_id) +{ + struct ubase_stop_perf_stats_cmd req = {0}; + struct ubase_cmd_buf in, out; + int ret; + + req.period = cpu_to_le32(period); + req.port_id = cpu_to_le16(port_id); + + __ubase_fill_inout_buf(&in, UBASE_OPC_STOP_PERF_STATS, true, + sizeof(req), &req); + __ubase_fill_inout_buf(&out, UBASE_OPC_STOP_PERF_STATS, false, + sizeof(*resp), resp); + + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret && ret != -EPERM) + ubase_err(udev, "failed to query perf stats, ret = %d.\n", ret); + + return ret == -EPERM ? -EOPNOTSUPP : ret; +} + +int __ubase_perf_stats(struct ubase_dev *udev, u64 port_bitmap, u32 period, + struct ubase_perf_stats_result *data, u32 data_size) +{ +#define UBASE_MS_TO_US(ms) (1000 * (ms)) + struct ubase_stop_perf_stats_cmd resp = {0}; + unsigned long logic_port_bitmap; + int ret, j, k, port_num; + u8 i; + + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits) || + test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + return -EBUSY; + + logic_port_bitmap = udev->caps.dev_caps.logic_port_bitmap; + + if (port_bitmap) { + if (data_size < bitmap_weight((unsigned long *)&port_bitmap, + UBASE_MAX_PORT_NUM) || + !bitmap_subset((unsigned long *)&port_bitmap, + &logic_port_bitmap, + UBASE_MAX_PORT_NUM)) + return -EINVAL; + } else { + if (data_size != UBASE_MAX_PORT_NUM) + return -EINVAL; + + port_bitmap = logic_port_bitmap; + } + + mutex_lock(&ubase_perf_mutex); + ret = ubase_start_perf_stats(udev, period, port_bitmap); + if (ret) + goto unlock; + + usleep_range(UBASE_MS_TO_US(period), UBASE_MS_TO_US(period + 1)); + + port_num = bitmap_weight((unsigned long *)&port_bitmap, + UBASE_MAX_PORT_NUM); + for (i = 0, k = 0; i < UBASE_MAX_PORT_NUM && k < port_num; i++) { + if (!test_bit(i, (unsigned long *)&port_bitmap)) + continue; + ret = ubase_stop_perf_stats(udev, &resp, period, i); + if (ret) + goto unlock; + + data[k].tx_port_bw = le32_to_cpu(resp.tx_port_bw); + data[k].rx_port_bw = le32_to_cpu(resp.rx_port_bw); + data[k].port_id = i; + data[k].valid = 1; + + for (j = 0; j < UBASE_STATS_MAX_VL_NUM; j++) { + data[k].tx_vl_bw[j] = le32_to_cpu(resp.tx_vl_bw[j]); + data[k].rx_vl_bw[j] = le32_to_cpu(resp.rx_vl_bw[j]); + } + + k++; + } + +unlock: + mutex_unlock(&ubase_perf_mutex); + + return ret; +} + +int ubase_perf_stats(struct auxiliary_device *adev, u64 port_bitmap, u32 period, + struct ubase_perf_stats_result *data, u32 data_size) +{ + struct ubase_dev *udev; + + if (!adev || !data || !period || !data_size) + return -EINVAL; + + udev = ubase_get_udev_by_adev(adev); + if (!(ubase_dev_ubl_supported(udev) || ubase_dev_fwctl_supported(udev))) + return -EOPNOTSUPP; + + return __ubase_perf_stats(udev, port_bitmap, period, data, data_size); +} +EXPORT_SYMBOL(ubase_perf_stats); diff --git a/drivers/ub/ubase/ubase_hw.h b/drivers/ub/ubase/ubase_hw.h index 29867a49e50e..ee455ba317d1 100644 --- a/drivers/ub/ubase/ubase_hw.h +++ b/drivers/ub/ubase/ubase_hw.h @@ -125,6 +125,13 @@ struct ubase_query_oor_resp { u8 rsvd0[15]; }; +struct ubase_query_port_bitmap_resp { + __le32 logic_port_bitmap; + __le32 chip_id; + __le32 die_id; + __le32 resv[3]; +}; + struct ubase_query_controller_info_resp { __le32 rsvd0[2]; u8 packet_pattern_mode : 1; @@ -200,5 +207,8 @@ void ubase_ue_uninit(struct ubase_dev *udev); int ubase_query_fst_fvt_rqmt(struct ubase_dev *udev, struct ubase_query_fst_fvt_rqmt_cmd *resp, u16 bus_ue_id); +int ubase_query_port_bitmap(struct ubase_dev *udev); +int __ubase_perf_stats(struct ubase_dev *udev, u64 port_bitmap, u32 period, + struct ubase_perf_stats_result *data, u32 data_size); #endif diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index 9cce91bc5cb8..1809197d8426 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -58,6 +58,10 @@ enum ubase_opcode_type { UBASE_OPC_QUERY_TM_PORT_INFO = 0x4209, UBASE_OPC_QUERY_FST_FVT_RQMT = 0x4212, + /* DL commands */ + UBASE_OPC_START_PERF_STATS = 0x5103, + UBASE_OPC_STOP_PERF_STATS = 0x5104, + /* PHY commands */ UBASE_OPC_QUERY_CHIP_INFO = 0x6201, @@ -65,6 +69,9 @@ enum ubase_opcode_type { UBASE_OPC_POST_MB = 0x7000, UBASE_OPC_QUERY_MB_ST = 0X7001, + /* Ubctl commands */ + UBASE_OPC_QUERY_PORT_BITMAP = 0xA017, + /* Software commands */ UBASE_OPC_MUE_TO_UE = 0xF001, UBASE_OPC_UE_TO_MUE = 0xF002, diff --git a/include/ub/ubase/ubase_comm_stats.h b/include/ub/ubase/ubase_comm_stats.h index 097f28dda7d6..fdafc18e6700 100644 --- a/include/ub/ubase/ubase_comm_stats.h +++ b/include/ub/ubase/ubase_comm_stats.h @@ -9,6 +9,9 @@ #include +#define UBASE_STATS_MAX_VL_NUM 16 +#define UBASE_MAX_PORT_NUM 64 + struct ubase_ub_dl_stats { u64 dl_tx_vl0_pkt_num; u64 dl_tx_vl1_pkt_num; @@ -207,4 +210,20 @@ struct ubase_eth_mac_stats { u64 rx_merge_frame_smd_error_pkts; }; +struct ubase_perf_stats_result { + u8 valid : 1; + u8 resv0 : 7; + u8 port_id; + u8 resv1[2]; + u32 tx_port_bw; /* kbps */ + u32 rx_port_bw; + u32 tx_vl_bw[UBASE_STATS_MAX_VL_NUM]; + u32 rx_vl_bw[UBASE_STATS_MAX_VL_NUM]; +}; + +int ubase_get_ub_port_stats(struct auxiliary_device *adev, u16 port_id, + struct ubase_ub_dl_stats *data); +int ubase_perf_stats(struct auxiliary_device *adev, u64 port_bitmap, u32 period, + struct ubase_perf_stats_result *data, u32 data_size); + #endif /* _UBASE_COMM_STATS_H */ -- Gitee From cd224cd98433aa4e0cfe5c99c0eaba25f3fc5378 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Wed, 29 Oct 2025 16:25:14 +0800 Subject: [PATCH 173/214] ub: ubase: support for activate/deactivate dev interface ANBZ: #45460 commit 5515a422646773cf9744b1c8bd8f870d0700057f openEuler. This commit is used to implement ubase activate/deactivate interface. Currently, the udma stream destruction has ghost packets. To solve this problem, udma need to stop the stream before destroying the TP and Jetty. Therefore, in this commit, the stream stop and resume interfaces are provided for the udma to invoke, and notify other registered auxiliary devices to stop or resume stream. Signed-off-by: Xiaobo Zhang Signed-off-by: Xiongchuan Zhou Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/Makefile | 2 +- drivers/ub/ubase/ubase.h | 2 + drivers/ub/ubase/ubase_arq.c | 202 ++++++++++++++++++++++++ drivers/ub/ubase/ubase_arq.h | 21 +++ drivers/ub/ubase/ubase_cmd.c | 12 +- drivers/ub/ubase/ubase_cmd.h | 14 ++ drivers/ub/ubase/ubase_dev.c | 252 ++++++++++++++++++++++++++++++ drivers/ub/ubase/ubase_dev.h | 8 + drivers/ub/ubase/ubase_ubus.c | 20 +++ include/ub/ubase/ubase_comm_cmd.h | 2 + include/ub/ubase/ubase_comm_dev.h | 9 ++ 11 files changed, 541 insertions(+), 3 deletions(-) create mode 100644 drivers/ub/ubase/ubase_arq.c create mode 100644 drivers/ub/ubase/ubase_arq.h diff --git a/drivers/ub/ubase/Makefile b/drivers/ub/ubase/Makefile index 4c14450d516a..67440b08bcfe 100644 --- a/drivers/ub/ubase/Makefile +++ b/drivers/ub/ubase/Makefile @@ -13,7 +13,7 @@ UBASE_OBJS := ubase_main.o ubase_dev.o ubase_hw.o ubase_cmd.o ubase_ctrlq.o \ debugfs/ubase_debugfs.o ubase_eq.o ubase_mailbox.o ubase_ubus.o \ debugfs/ubase_ctx_debugfs.o debugfs/ubase_qos_debugfs.o \ ubase_qos_hw.o ubase_tp.o ubase_ctrlq_tp.o ubase_reset.o \ - ubase_err_handle.o ubase_pmem.o ubase_stats.o + ubase_err_handle.o ubase_pmem.o ubase_stats.o ubase_arq.o $(MODULE_NAME)-objs := $(UBASE_OBJS) obj-$(CONFIG_UB_UBASE) := ubase.o diff --git a/drivers/ub/ubase/ubase.h b/drivers/ub/ubase/ubase.h index 474e8384a1d6..e4e1517a061c 100644 --- a/drivers/ub/ubase/ubase.h +++ b/drivers/ub/ubase/ubase.h @@ -21,6 +21,8 @@ enum ubase_service_state { UBASE_SERVICE_STATE_RESET_SCHED, UBASE_STATE_CTRLQ_SERVICE_SCHED, UBASE_STATE_CTRLQ_HANDLING, + UBASE_STATE_ARQ_SERVICE_SCHED, + UBASE_STATE_ARQ_HANDLING, }; struct ubase_delay_work { diff --git a/drivers/ub/ubase/ubase_arq.c b/drivers/ub/ubase/ubase_arq.c new file mode 100644 index 000000000000..10735a68d7aa --- /dev/null +++ b/drivers/ub/ubase/ubase_arq.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include "ubase_arq.h" +#include "ubase_cmd.h" + +void ubase_arq_init(struct ubase_dev *udev) +{ + struct ubase_arq_msg_ring *arq = &udev->arq; + + arq->ci = 0; + arq->pi = 0; + atomic_set(&arq->count, 0); +} + +void ubase_arq_uninit(struct ubase_dev *udev) +{ + struct ubase_arq_msg_ring *arq = &udev->arq; + int i; + + for (i = 0; i < MAX_ARQ_MSG_NUM; i++) { + kfree(arq->msg[i].data); + arq->msg[i].data = NULL; + } +} + +static void ubase_send_activate_resp(struct ubase_dev *udev, __le16 bus_ue_id, + __le16 msn, int result) +{ + struct ubase_activate_resp resp = {0}; + struct ubase_cmd_buf in; + int ret; + + resp.bus_ue_id = bus_ue_id; + resp.msn = msn; + resp.result = result < 0 ? (u8)(-result) : (u8)result; + ubase_fill_inout_buf(&in, UBASE_OPC_ACTIVATE_RESP, false, sizeof(resp), + &resp); + ret = __ubase_cmd_send_in(udev, &in); + if (ret) + ubase_err(udev, + "failed to send activate dev resp, ue id = %u, msn = %u, ret = %d.\n", + le16_to_cpu(bus_ue_id), le16_to_cpu(msn), ret); +} + +static int ubase_activate_ue(struct ubase_dev *udev, struct ub_entity *ue, + u16 msn, u16 bus_ue_id) +{ + int ret; + + ret = ub_activate_entity(ue, bus_ue_id); + if (ret == -EBUSY) + ret = ubase_activate_handler(udev, bus_ue_id); + if (ret) + dev_err(udev->dev, + "failed to activate ue dev, ue id = %u, msn = %u, ret = %d.\n", + bus_ue_id, msn, ret); + + return ret; +} + +static int ubase_deactivate_ue(struct ubase_dev *udev, struct ub_entity *ue, + u16 msn, u16 bus_ue_id) +{ + int ret; + + ret = ub_deactivate_entity(ue, bus_ue_id); + if (ret == -EBUSY) + ret = ubase_deactivate_handler(udev, bus_ue_id); + if (ret) + dev_err(udev->dev, + "failed to deactivate ue dev, ue id=%u, msn=%u, ret=%d.\n", + bus_ue_id, msn, ret); + + return ret; +} + +static void ubase_handle_activate_req(struct ubase_dev *udev, void *data, + u32 len) +{ + struct ubase_activate_req *req = data; + u16 bus_ue_id = le16_to_cpu(req->bus_ue_id); + u16 msn = le16_to_cpu(req->msn); + struct ub_entity *ue; + int ret; + + if (!ubase_activate_proxy_supported(udev)) + return; + + ue = container_of(udev->dev, struct ub_entity, dev); + if (req->activate) + ret = ubase_activate_ue(udev, ue, msn, bus_ue_id); + else + ret = ubase_deactivate_ue(udev, ue, msn, bus_ue_id); + + ubase_send_activate_resp(udev, req->bus_ue_id, req->msn, ret); +} + +struct ubase_arq_event { + u16 opcode; + u16 len; + void (*arq_handler)(struct ubase_dev *udev, void *data, u32 len); +} ubase_arq_map[] = { + { + .opcode = UBASE_OPC_ACTIVATE_REQ, + .len = sizeof(struct ubase_activate_req), + .arq_handler = ubase_handle_activate_req, + }, +}; + +bool ubase_is_arq_msg(u16 opcode) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(ubase_arq_map); i++) { + if (ubase_arq_map[i].opcode == opcode) + return true; + } + + return false; +} + +static struct ubase_arq_event *ubase_get_arq_event(u16 opcode, u32 data_len) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(ubase_arq_map); i++) { + if (ubase_arq_map[i].opcode == opcode) + return data_len != ubase_arq_map[i].len ? + NULL : &ubase_arq_map[i]; + } + + return NULL; +} + +void ubase_cmd_arq_handler(struct ubase_delay_work *ubase_work) +{ + struct ubase_dev *udev = container_of(ubase_work, struct ubase_dev, + arq_service_task); + struct ubase_arq_msg_ring *arq = &udev->arq; + struct ubase_arq_event *event; + struct ubase_arq_msg *msg; + + if (!test_and_clear_bit(UBASE_STATE_ARQ_SERVICE_SCHED, + &udev->arq_service_task.state) || + test_and_set_bit(UBASE_STATE_ARQ_HANDLING, + &udev->arq_service_task.state)) + return; + + while (atomic_read(&arq->count) > 0) { + msg = &arq->msg[arq->ci]; + event = ubase_get_arq_event(msg->opcode, msg->data_len); + if (event) + event->arq_handler(udev, msg->data, msg->data_len); + kfree(msg->data); + msg->data = NULL; + + arq->ci = (arq->ci + 1) % MAX_ARQ_MSG_NUM; + atomic_dec(&arq->count); + } + + clear_bit(UBASE_STATE_ARQ_HANDLING, &udev->arq_service_task.state); +} + +static void ubase_arq_task_schedule(struct ubase_dev *udev) +{ + if (!test_and_set_bit(UBASE_STATE_ARQ_SERVICE_SCHED, + &udev->arq_service_task.state)) + mod_delayed_work(udev->ubase_arq_wq, + &udev->arq_service_task.service_task, 0); +} + +void ubase_add_to_arq(struct ubase_dev *udev, u16 opcode, void *msg_data, + u32 msg_data_len) +{ + struct ubase_arq_msg_ring *arq = &udev->arq; + + if (atomic_read(&arq->count) >= MAX_ARQ_MSG_NUM) { + ubase_warn(udev, + "arq queue full, drop opcode = 0x%x.\n", opcode); + return; + } + + arq->msg[arq->pi].data = kzalloc(msg_data_len, GFP_KERNEL); + if (!arq->msg[arq->pi].data) { + ubase_err(udev, + "failed to alloc arq msg data, opcode = 0x%x.\n", + opcode); + return; + } + + memcpy(arq->msg[arq->pi].data, msg_data, msg_data_len); + arq->msg[arq->pi].data_len = msg_data_len; + arq->msg[arq->pi].opcode = opcode; + arq->pi = (arq->pi + 1) % MAX_ARQ_MSG_NUM; + atomic_inc(&arq->count); + + ubase_arq_task_schedule(udev); +} diff --git a/drivers/ub/ubase/ubase_arq.h b/drivers/ub/ubase/ubase_arq.h new file mode 100644 index 000000000000..1998192b424e --- /dev/null +++ b/drivers/ub/ubase/ubase_arq.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UBASE_ARQ_H__ +#define __UBASE_ARQ_H__ + +#include + +#include "ubase_dev.h" + +void ubase_arq_init(struct ubase_dev *udev); +void ubase_arq_uninit(struct ubase_dev *udev); +void ubase_add_to_arq(struct ubase_dev *udev, u16 opcode, void *msg_data, + u32 msg_data_len); +bool ubase_is_arq_msg(u16 opcode); +void ubase_cmd_arq_handler(struct ubase_delay_work *ubase_work); + +#endif diff --git a/drivers/ub/ubase/ubase_cmd.c b/drivers/ub/ubase/ubase_cmd.c index 6a16d521d736..4526f92ac117 100644 --- a/drivers/ub/ubase/ubase_cmd.c +++ b/drivers/ub/ubase/ubase_cmd.c @@ -7,6 +7,7 @@ #include #include "ubase_cmd.h" +#include "ubase_arq.h" #include "ubase_hw.h" /* When use tracepoint, must define "CREATE_TRACE_POINTS" before include the @@ -359,6 +360,8 @@ int ubase_cmd_init(struct ubase_dev *udev) goto err_queue_init; } + ubase_arq_init(udev); + ubase_cmd_init_regs(udev); clear_bit(UBASE_STATE_CMD_DISABLE, &udev->hw.state); @@ -372,6 +375,7 @@ int ubase_cmd_init(struct ubase_dev *udev) err_query_version: set_bit(UBASE_STATE_CMD_DISABLE, &udev->hw.state); ubase_cmd_uninit_regs(udev); + ubase_arq_uninit(udev); ubase_cmd_queue_uninit(udev); err_queue_init: if (!test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) @@ -397,6 +401,7 @@ void ubase_cmd_uninit(struct ubase_dev *udev) msleep(UBASE_CMDQ_CLEAR_WAIT_TIME); } + ubase_arq_uninit(udev); ubase_cmd_queue_uninit(udev); if (!test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) @@ -618,8 +623,11 @@ void ubase_cmd_crq_handler(struct ubase_dev *udev) ubase_gen_bd_data(udev, bd_num, &msg_data, msg_data_len); - ubase_cmd_exec_callback(udev, opcode, msg_data, - msg_data_len); + if (ubase_is_arq_msg(opcode)) + ubase_add_to_arq(udev, opcode, msg_data, msg_data_len); + else + ubase_cmd_exec_callback(udev, opcode, msg_data, + msg_data_len); ubase_free_bd_data(msg_data, bd_num); } diff --git a/drivers/ub/ubase/ubase_cmd.h b/drivers/ub/ubase/ubase_cmd.h index 86829104e481..0187597493b7 100644 --- a/drivers/ub/ubase/ubase_cmd.h +++ b/drivers/ub/ubase/ubase_cmd.h @@ -238,6 +238,20 @@ struct ubase_query_tm_port_cmd { u8 resv1[12]; }; +struct ubase_activate_req { + __le16 bus_ue_id; + __le16 msn; + u8 activate; + u8 resv[19]; +}; + +struct ubase_activate_resp { + __le16 bus_ue_id; + __le16 msn; + u8 result; + u8 resv[19]; +}; + struct ubase_query_ueid_cmd { __le32 ueid[UBASE_BUS_EID_LEN]; u32 rsv[2]; diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index 7a18ae8e3e11..abdb3232edea 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -8,6 +8,7 @@ #include #include "debugfs/ubase_debugfs.h" +#include "ubase_arq.h" #include "ubase_cmd.h" #include "ubase_ctrlq.h" #include "ubase_err_handle.h" @@ -335,6 +336,14 @@ static void ubase_period_service_task(struct work_struct *work) ubase_enable_period_service_task(udev); } +static void ubase_arq_service_task(struct work_struct *work) +{ + struct ubase_delay_work *ubase_work = + container_of(work, struct ubase_delay_work, service_task.work); + + ubase_cmd_arq_handler(ubase_work); +} + static void ubase_reset_service_task(struct work_struct *work) { struct ubase_delay_work *ubase_work = @@ -361,6 +370,8 @@ static void ubase_init_delayed_work(struct ubase_dev *udev) ubase_reset_service_task); INIT_DELAYED_WORK(&udev->period_service_task.service_task, ubase_period_service_task); + INIT_DELAYED_WORK(&udev->arq_service_task.service_task, + ubase_arq_service_task); } static int ubase_wq_init(struct ubase_dev *udev) @@ -528,6 +539,40 @@ static int ubase_handle_ue_reset_event(void *dev, void *data, u32 len) return 0; } +static int ubase_handle_activate_resp(void *dev, void *data, u32 len) +{ + struct ubase_activate_resp *resp = data; + struct ubase_act_info *self, *other; + struct ubase_dev *udev = dev; + u16 msn; + + if (len != sizeof(*resp)) { + ubase_err(udev, + "activate dev resp len error, cur = %u, expect = %lu.\n", + len, sizeof(*resp)); + return -EINVAL; + } + + msn = le16_to_cpu(resp->msn); + self = &udev->act_ctx.self; + if (self->wait_msn == msn) { + self->result = -resp->result; + complete(&self->activate_done); + return 0; + } + other = &udev->act_ctx.other; + if (other->wait_msn == msn) { + other->result = -resp->result; + complete(&other->activate_done); + return 0; + } + + ubase_warn(udev, + "unknown msn in activate resp, msn = %u, self msn = %u, other msn = %u.\n", + msn, self->wait_msn, other->wait_msn); + + return -EIO; +} static struct ubase_crq_event_nb ubase_crq_events[] = { { .opcode = UBASE_OPC_UE2UE_UBASE, @@ -537,6 +582,10 @@ static struct ubase_crq_event_nb ubase_crq_events[] = { .opcode = UBASE_OPC_NOTIFY_UE_RESET, .crq_handler = ubase_handle_ue_reset_event, }, + { + .opcode = UBASE_OPC_ACTIVATE_RESP, + .crq_handler = ubase_handle_activate_resp, + }, }; static void ubase_unregister_cmdq_crq_event(struct ubase_dev *udev) @@ -1104,6 +1153,209 @@ struct ubase_adev_qos *ubase_get_adev_qos(struct auxiliary_device *adev) } EXPORT_SYMBOL(ubase_get_adev_qos); +static void ubase_activate_notify(struct ubase_dev *udev, + struct auxiliary_device *adev, bool activate) +{ + bool disable_state = test_bit(UBASE_STATE_DISABLED_B, &udev->state_bits); + struct ubase_adev *uadev; + int i; + + if (!disable_state) + mutex_lock(&udev->priv.uadev_lock); + + for (i = 0; i < UBASE_DRV_MAX; i++) { + uadev = udev->priv.uadev[i]; + if (!uadev || &uadev->adev == adev) + continue; + + mutex_lock(&uadev->activate_lock); + if (uadev->activate_handler) + uadev->activate_handler(&uadev->adev, activate); + mutex_unlock(&uadev->activate_lock); + } + + if (!disable_state) + mutex_unlock(&udev->priv.uadev_lock); +} + +void ubase_activate_register(struct auxiliary_device *adev, + void (*activate_handler)(struct auxiliary_device *adev, + bool activate)) +{ + struct ubase_adev *uadev; + + if (!adev || !activate_handler) + return; + + uadev = container_of(adev, struct ubase_adev, adev); + + mutex_lock(&uadev->activate_lock); + if (!uadev->activate_handler) + uadev->activate_handler = activate_handler; + mutex_unlock(&uadev->activate_lock); +} +EXPORT_SYMBOL(ubase_activate_register); + +void ubase_activate_unregister(struct auxiliary_device *adev) +{ + struct ubase_adev *uadev; + + if (!adev) + return; + + uadev = container_of(adev, struct ubase_adev, adev); + + mutex_lock(&uadev->activate_lock); + uadev->activate_handler = NULL; + mutex_unlock(&uadev->activate_lock); +} +EXPORT_SYMBOL(ubase_activate_unregister); + +static int ubase_wait_activate_done(struct ubase_dev *udev, u16 bus_ue_id) +{ +#define UBASE_ACTIVE_DEV_TIMEOUT 10000 + + struct ub_entity *ue = container_of(udev->dev, struct ub_entity, dev); + struct ubase_act_info *info; + + info = (ue->entity_idx == bus_ue_id) ? &udev->act_ctx.self : + &udev->act_ctx.other; + + if (!wait_for_completion_timeout(&info->activate_done, + msecs_to_jiffies(UBASE_ACTIVE_DEV_TIMEOUT))) { + ubase_err(udev, + "wait activate dev resp timeout, bus_ue_id = %u, msn = %u.\n", + bus_ue_id, info->wait_msn); + return -ETIMEDOUT; + } + + return info->result; +} + +static void ubase_record_msn(struct ubase_dev *udev, u16 bus_ue_id, u16 msn) +{ + struct ub_entity *ue = container_of(udev->dev, struct ub_entity, dev); + struct ubase_act_info *info; + + info = (ue->entity_idx == bus_ue_id) ? &udev->act_ctx.self : + &udev->act_ctx.other; + + info->wait_msn = msn; +} + +static void ubase_alloc_msn(struct ubase_dev *udev, u16 *msn) +{ + struct ubase_act_ctx *ctx = &udev->act_ctx; + + /* we cannot distinguish whether it is a real 0 + * or a 0 caused by the peer not assigning a value, + * so we skip the number 0. + */ + mutex_lock(&ctx->lock); + ++ctx->msn; + if (!ctx->msn) + ctx->msn = 1; + *msn = ctx->msn; + mutex_unlock(&ctx->lock); +} + +static int ubase_send_activate_dev_req(struct ubase_dev *udev, bool activate, + u16 bus_ue_id) +{ + struct ubase_activate_req req = {0}; + struct ubase_cmd_buf in; + u16 msn; + int ret; + + req.activate = activate ? 1 : 0; + req.bus_ue_id = cpu_to_le16(bus_ue_id); + ubase_alloc_msn(udev, &msn); + req.msn = cpu_to_le16(msn); + ubase_record_msn(udev, bus_ue_id, msn); + + ubase_fill_inout_buf(&in, UBASE_OPC_ACTIVATE_REQ, false, sizeof(req), + &req); + ret = __ubase_cmd_send_in(udev, &in); + if (ret) { + ubase_err(udev, + "failed to send activate dev req, ue id=%u, ret=%d.\n", + bus_ue_id, ret); + return ret; + } + + return ubase_wait_activate_done(udev, bus_ue_id); +} + +int ubase_activate_handler(struct ubase_dev *udev, u32 bus_ue_id) +{ + return ubase_send_activate_dev_req(udev, true, (u16)bus_ue_id); +} + +int ubase_deactivate_handler(struct ubase_dev *udev, u32 bus_ue_id) +{ + return ubase_send_activate_dev_req(udev, false, (u16)bus_ue_id); +} + +int ubase_activate_dev(struct auxiliary_device *adev) +{ + struct ubase_dev *udev; + struct ub_entity *ue; + int ret; + + if (!adev) + return 0; + + udev = __ubase_get_udev_by_adev(adev); + + ue = container_of(udev->dev, struct ub_entity, dev); + if (ubase_activate_proxy_supported(udev) && + !test_bit(UBASE_STATE_DISABLED_B, &udev->state_bits)) + ret = ub_activate_entity(ue, ue->entity_idx); + else + ret = ubase_activate_handler(udev, ue->entity_idx); + + if (ret) { + ubase_err(udev, + "failed to activate ubase dev, ret = %d.\n", ret); + return ret; + } + + ubase_activate_notify(udev, adev, true); + + return 0; +} +EXPORT_SYMBOL(ubase_activate_dev); + +int ubase_deactivate_dev(struct auxiliary_device *adev) +{ + struct ubase_dev *udev; + struct ub_entity *ue; + int ret; + + if (!adev) + return 0; + + udev = __ubase_get_udev_by_adev(adev); + + ue = container_of(udev->dev, struct ub_entity, dev); + ubase_activate_notify(udev, adev, false); + + if (ubase_activate_proxy_supported(udev) && + !test_bit(UBASE_STATE_DISABLED_B, &udev->state_bits)) + ret = ub_deactivate_entity(ue, ue->entity_idx); + else + ret = ubase_deactivate_handler(udev, ue->entity_idx); + + if (ret) { + ubase_err(udev, + "failed to deactivate ubase dev, ret = %d.\n", ret); + ubase_activate_notify(udev, adev, true); + } + + return ret; +} +EXPORT_SYMBOL(ubase_deactivate_dev); + static int ubase_query_bus_eid(struct ubase_dev *udev, struct ubase_bus_eid *eid) { struct ubase_query_ueid_cmd resp = {0}; diff --git a/drivers/ub/ubase/ubase_dev.h b/drivers/ub/ubase/ubase_dev.h index f47021b3e095..d32d9fb98377 100644 --- a/drivers/ub/ubase/ubase_dev.h +++ b/drivers/ub/ubase/ubase_dev.h @@ -390,6 +390,11 @@ static inline bool __ubase_dev_prealloc_supported(struct ubase_dev *udev) return ubase_get_cap_bit(udev, UBASE_SUPPORT_PRE_ALLOC_B); } +static inline bool ubase_activate_proxy_supported(struct ubase_dev *udev) +{ + return ubase_get_cap_bit(udev, UBASE_SUPPORT_ACTIVATE_PROXY_B); +} + static inline bool ubase_utp_supported(struct ubase_dev *udev) { return ubase_get_cap_bit(udev, UBASE_SUPPORT_UTP_B); @@ -448,4 +453,7 @@ void ubase_resume_aux_devices(struct ubase_dev *udev); void ubase_virt_handler(struct ubase_dev *udev, u16 bus_ue_id, bool is_en); +int ubase_activate_handler(struct ubase_dev *udev, u32 bus_ue_id); +int ubase_deactivate_handler(struct ubase_dev *udev, u32 bus_ue_id); + #endif diff --git a/drivers/ub/ubase/ubase_ubus.c b/drivers/ub/ubase/ubase_ubus.c index 7627a7d562fc..9ae27adb413b 100644 --- a/drivers/ub/ubase/ubase_ubus.c +++ b/drivers/ub/ubase/ubase_ubus.c @@ -388,6 +388,24 @@ static int ubase_ubus_virt_notify(struct ub_entity *ue, int bus_ue_id, bool is_e return 0; } +static int ubase_ubus_activate(struct ub_entity *ue, u32 bus_ue_id) +{ + struct ubase_dev *udev = dev_get_drvdata(&ue->dev); + + ubase_info(udev, "ubase activate ue id = %u.\n", bus_ue_id); + + return ubase_activate_handler(udev, bus_ue_id); +} + +static int ubase_ubus_deactivate(struct ub_entity *ue, u32 bus_ue_id) +{ + struct ubase_dev *udev = dev_get_drvdata(&ue->dev); + + ubase_info(udev, "ubase deactivate ue id = %u.\n", bus_ue_id); + + return ubase_deactivate_handler(udev, bus_ue_id); +} + static void ubase_ubus_reset_prepare(struct ub_entity *ue) { struct ubase_dev *udev = dev_get_drvdata(&ue->dev); @@ -437,6 +455,8 @@ static struct ub_driver ubase_ubus_driver = { .virt_configure = ubase_ubus_virt_configure, .virt_notify = ubase_ubus_virt_notify, .err_handler = &ubase_ubus_err_handler, + .activate = ubase_ubus_activate, + .deactivate = ubase_ubus_deactivate, .driver = { .pm = &ubase_ubus_pm_ops, }, diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index 1809197d8426..c412d77ed281 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -81,6 +81,8 @@ enum ubase_opcode_type { UBASE_OPC_RESET_DONE = 0xF008, UBASE_OPC_DESTROY_CTX_RESOURCE = 0xF00D, UBASE_OPC_UE2UE_UBASE = 0xF00E, + UBASE_OPC_ACTIVATE_REQ = 0xF00F, + UBASE_OPC_ACTIVATE_RESP = 0xF010, }; union ubase_mbox { diff --git a/include/ub/ubase/ubase_comm_dev.h b/include/ub/ubase/ubase_comm_dev.h index fd475afbf9d8..81b2c98af17f 100644 --- a/include/ub/ubase/ubase_comm_dev.h +++ b/include/ub/ubase/ubase_comm_dev.h @@ -249,6 +249,15 @@ void ubase_reset_register(struct auxiliary_device *adev, void (*reset_handler)(struct auxiliary_device *adev, enum ubase_reset_stage stage)); void ubase_reset_unregister(struct auxiliary_device *adev); + +void ubase_activate_register(struct auxiliary_device *adev, + void (*activate_handler)(struct auxiliary_device *adev, + bool activate)); +void ubase_activate_unregister(struct auxiliary_device *adev); + +int ubase_activate_dev(struct auxiliary_device *adev); +int ubase_deactivate_dev(struct auxiliary_device *adev); + int ubase_get_bus_eid(struct auxiliary_device *adev, struct ubase_bus_eid *eid); #endif -- Gitee From 5f5b3904bc4733a3591de2e60b66a60e47002b2a Mon Sep 17 00:00:00 2001 From: Fengyan Mu Date: Wed, 12 Nov 2025 15:54:24 +0800 Subject: [PATCH 174/214] net: unic: add support of unic driver for ub network ANBZ: #45460 commit a1f4613490f821d0a0b0ad5623ad9e76a824ed7b openEuler. This patch adds the support of UNIC driver. This patch includes the basic framework of the UNIC driver, such as: network device probe, debugfs framework setup, integration with ethtool and dcbnl tools, event handling framework, etc. The current work only provides initial support, and would incrementally add features or enhancements. Signed-off-by: Haiqing Fang Signed-off-by: Haibin Lu Signed-off-by: Yixi Shen Signed-off-by: Fengyan Mu Signed-off-by: Xiaobo Zhang Signed-off-by: Jianqiang Li Signed-off-by: Chuan Wu Signed-off-by: Zihao Sheng Signed-off-by: Junxin Chen Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- .../configs/L1-RECOMMEND/arm64/CONFIG_UB_NET | 1 + .../configs/L1-RECOMMEND/arm64/CONFIG_UB_UNIC | 1 + .../L1-RECOMMEND/arm64/CONFIG_UB_UNIC_DCB | 1 + .../L1-RECOMMEND/arm64/CONFIG_UB_UNIC_UBL | 1 + drivers/net/Makefile | 2 +- drivers/net/ub/Kconfig | 65 +- drivers/net/ub/Makefile | 3 +- drivers/net/ub/unic/Makefile | 12 + drivers/net/ub/unic/debugfs/unic_debugfs.c | 168 +++++ drivers/net/ub/unic/debugfs/unic_debugfs.h | 23 + drivers/net/ub/unic/unic.h | 113 +++ drivers/net/ub/unic/unic_cmd.h | 139 ++++ drivers/net/ub/unic/unic_crq.c | 49 ++ drivers/net/ub/unic/unic_crq.h | 12 + drivers/net/ub/unic/unic_dcbnl.c | 26 + drivers/net/ub/unic/unic_dcbnl.h | 18 + drivers/net/ub/unic/unic_dev.c | 702 ++++++++++++++++++ drivers/net/ub/unic/unic_dev.h | 277 +++++++ drivers/net/ub/unic/unic_ethtool.c | 68 ++ drivers/net/ub/unic/unic_ethtool.h | 27 + drivers/net/ub/unic/unic_event.c | 77 ++ drivers/net/ub/unic/unic_event.h | 15 + drivers/net/ub/unic/unic_guid.c | 98 +++ drivers/net/ub/unic/unic_guid.h | 14 + drivers/net/ub/unic/unic_hw.c | 654 ++++++++++++++++ drivers/net/ub/unic/unic_hw.h | 71 ++ drivers/net/ub/unic/unic_main.c | 108 +++ drivers/net/ub/unic/unic_netdev.c | 238 ++++++ drivers/net/ub/unic/unic_netdev.h | 23 + drivers/net/ub/unic/unic_qos_hw.c | 62 ++ drivers/net/ub/unic/unic_qos_hw.h | 19 + include/ub/ubase/ubase_comm_cmd.h | 13 + 32 files changed, 3096 insertions(+), 4 deletions(-) create mode 100644 anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_NET create mode 100644 anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UNIC create mode 100644 anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UNIC_DCB create mode 100644 anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UNIC_UBL create mode 100644 drivers/net/ub/unic/Makefile create mode 100644 drivers/net/ub/unic/debugfs/unic_debugfs.c create mode 100644 drivers/net/ub/unic/debugfs/unic_debugfs.h create mode 100644 drivers/net/ub/unic/unic.h create mode 100644 drivers/net/ub/unic/unic_cmd.h create mode 100644 drivers/net/ub/unic/unic_crq.c create mode 100644 drivers/net/ub/unic/unic_crq.h create mode 100644 drivers/net/ub/unic/unic_dcbnl.c create mode 100644 drivers/net/ub/unic/unic_dcbnl.h create mode 100644 drivers/net/ub/unic/unic_dev.c create mode 100644 drivers/net/ub/unic/unic_dev.h create mode 100644 drivers/net/ub/unic/unic_ethtool.c create mode 100644 drivers/net/ub/unic/unic_ethtool.h create mode 100644 drivers/net/ub/unic/unic_event.c create mode 100644 drivers/net/ub/unic/unic_event.h create mode 100644 drivers/net/ub/unic/unic_guid.c create mode 100644 drivers/net/ub/unic/unic_guid.h create mode 100644 drivers/net/ub/unic/unic_hw.c create mode 100644 drivers/net/ub/unic/unic_hw.h create mode 100644 drivers/net/ub/unic/unic_main.c create mode 100644 drivers/net/ub/unic/unic_netdev.c create mode 100644 drivers/net/ub/unic/unic_netdev.h create mode 100644 drivers/net/ub/unic/unic_qos_hw.c create mode 100644 drivers/net/ub/unic/unic_qos_hw.h diff --git a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_NET b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_NET new file mode 100644 index 000000000000..19f24d29ed04 --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_NET @@ -0,0 +1 @@ +CONFIG_UB_NET=y diff --git a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UNIC b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UNIC new file mode 100644 index 000000000000..cffa82fd7e70 --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UNIC @@ -0,0 +1 @@ +CONFIG_UB_UNIC=m diff --git a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UNIC_DCB b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UNIC_DCB new file mode 100644 index 000000000000..d5e0cf365e40 --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UNIC_DCB @@ -0,0 +1 @@ +CONFIG_UB_UNIC_DCB=y diff --git a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UNIC_UBL b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UNIC_UBL new file mode 100644 index 000000000000..587522749782 --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UB_UNIC_UBL @@ -0,0 +1 @@ +CONFIG_UB_UNIC_UBL=y diff --git a/drivers/net/Makefile b/drivers/net/Makefile index e089d6f44197..71b8dfe43a39 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -75,7 +75,7 @@ obj-$(CONFIG_WLAN) += wireless/ obj-$(CONFIG_IEEE802154) += ieee802154/ obj-$(CONFIG_WWAN) += wwan/ obj-$(CONFIG_MCTP) += mctp/ -obj-$(CONFIG_UB_UBL) += ub/ +obj-$(CONFIG_UB_NET) += ub/ obj-$(CONFIG_VMXNET3) += vmxnet3/ obj-$(CONFIG_XEN_NETDEV_FRONTEND) += xen-netfront.o diff --git a/drivers/net/ub/Kconfig b/drivers/net/ub/Kconfig index 6b32cc5cc49b..f74de4843655 100644 --- a/drivers/net/ub/Kconfig +++ b/drivers/net/ub/Kconfig @@ -3,12 +3,73 @@ # UBL configuration # +menuconfig UB_NET + bool "UB network driver support" + depends on NET + depends on ARM64 + default n + help + If you have a network (UB) card belonging to this class, say Y. + + UB is a new interconnection protocol that defines its own physical layer, + data link layer, and network layer. + This option includes the data link layer and all UB network device drivers. + On the otherside, it can also be used with ethernet link for layer 2, + which still integrates with the TCP/IP protocol stack. + +if UB_NET + config UB_UBL default n tristate "UB link support" - depends on ARM64 help This enables UB link support which is used to replace traditional ethernet layer. Say 'Y' or 'm' if your device works - in UB mode. \ No newline at end of file + in UB mode. + +config UB_UNIC + tristate "NIC based on UB devices" + default n + depends on UB_UBASE + help + This option is used to enable the networking capabilities of the + UB device and integrate with the TCP/IP protocol stack. + This module depends upon UB_UBASE driver to access the ub devices + and their associated operations. For UB devices, in addition to the + ubase driver, it also relies on the ubl driver. + +config UB_UNIC_UBL + bool "Hisilicon UNIC UB link support" + default n + depends on UB_UBL + depends on UB_UNIC + help + This option enables support for UB Link (UBL) in the Hisilicon + UNIC network driver. UB link is used to replace traditional + ethernet layer in the UB devices. When enable UB_UNIC_UBL config, + UNIC driver will use the interfaces defined by UBL. + + To use this feature, you must have both the UB_UBL and UB_UNIC + options enabled. + + If you are unsure whether you need this functionality, it is + recommended to leave it disabled to avoid unnecessary kernel bloat. + +config UB_UNIC_DCB + bool "Hisilicon UNIC Data Center Bridge Support" + default n + depends on DCB + depends on UB_UNIC + help + This option enables support for Data Center Bridging (DCB) in the + Hisilicon UNIC network driver. DCB allows for the configuration of + network traffic classes and priorities, enhancing network performance + in data center environments. + + To use this feature, you must have both the DCB and UB_UNIC options enabled. + + If you are unsure whether you need this functionality, it is + recommended to leave it disabled to avoid unnecessary kernel bloat. + +endif #UB_NET diff --git a/drivers/net/ub/Makefile b/drivers/net/ub/Makefile index 98c1cc30bc13..955b495f6fe2 100644 --- a/drivers/net/ub/Makefile +++ b/drivers/net/ub/Makefile @@ -4,4 +4,5 @@ # #### compile ubl -obj-$(CONFIG_UB_UBL) += dev/ \ No newline at end of file +obj-$(CONFIG_UB_UBL) += dev/ +obj-$(CONFIG_UB_UNIC) += unic/ \ No newline at end of file diff --git a/drivers/net/ub/unic/Makefile b/drivers/net/ub/unic/Makefile new file mode 100644 index 000000000000..3326a3aa7aae --- /dev/null +++ b/drivers/net/ub/unic/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Makefile for the HISILICON network device drivers. +# + +ccflags-y += -I$(srctree)/$(src) +ccflags-y += -I$(srctree)/drivers/net/ub/unic/debugfs + +obj-$(CONFIG_UB_UNIC) += unic.o +unic-objs = unic_main.o unic_ethtool.o unic_hw.o unic_guid.o unic_netdev.o unic_dev.o unic_qos_hw.o unic_event.o unic_crq.o +unic-objs += debugfs/unic_debugfs.o +unic-$(CONFIG_UB_UNIC_DCB) += unic_dcbnl.o diff --git a/drivers/net/ub/unic/debugfs/unic_debugfs.c b/drivers/net/ub/unic/debugfs/unic_debugfs.c new file mode 100644 index 000000000000..2f0adcff39c9 --- /dev/null +++ b/drivers/net/ub/unic/debugfs/unic_debugfs.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include +#include +#include +#include +#include + +#include "unic_dev.h" +#include "unic_hw.h" +#include "unic_debugfs.h" + +static int unic_dbg_dump_dev_info(struct seq_file *s, void *data) +{ + struct unic_dev *unic_dev = dev_get_drvdata(s->private); + struct net_device *netdev = unic_dev->comdev.netdev; + + seq_printf(s, "%-25s", "DEV_NAME:"); + seq_printf(s, "%-12s\n", netdev->name); + + return 0; +} + +static const struct unic_dbg_cap_bit_info { + const char *format; + bool (*get_bit)(struct unic_dev *dev); +} unic_cap_bits[] = { + {"\tsupport_ubl: %u\n", &unic_dev_ubl_supported}, + {"\tsupport_ets: %u\n", &unic_dev_ets_supported}, + {"\tsupport_fec: %u\n", &unic_dev_fec_supported}, + {"\tsupport_tc_speed_limit: %u\n", &unic_dev_tc_speed_limit_supported}, + {"\tsupport_tx_csum_offload: %u\n", &unic_dev_tx_csum_offload_supported}, + {"\tsupport_rx_csum_offload: %u\n", &unic_dev_rx_csum_offload_supported}, +}; + +static void unic_dbg_dump_caps_bits(struct unic_dev *unic_dev, + struct seq_file *s) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(unic_cap_bits); i++) + seq_printf(s, unic_cap_bits[i].format, + unic_cap_bits[i].get_bit(unic_dev)); +} + +static void unic_dbg_dump_caps(struct unic_dev *unic_dev, struct seq_file *s) +{ + struct unic_caps *unic_caps = &unic_dev->caps; + struct unic_dbg_caps_info { + const char *format; + u32 caps_info; + } unic_caps_info[] = { + {"\ttotal_ip_tbl_size: %hu\n", unic_caps->total_ip_tbl_size}, + {"\tmax_trans_unit: %hu\n", unic_caps->max_trans_unit}, + {"\tmin_trans_unit: %hu\n", unic_caps->min_trans_unit}, + {"\tvport_buf_size: %u\n", unic_caps->vport_buf_size}, + {"\tvport_buf_num: %hhu\n", unic_caps->vport_buf_num}, + {"\tmax_int_ql: %hu\n", unic_caps->max_int_ql}, + {"\tmax_int_gl: %hu\n", unic_caps->max_int_gl}, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(unic_caps_info); i++) + seq_printf(s, unic_caps_info[i].format, + unic_caps_info[i].caps_info); +} + +static int unic_dbg_dump_caps_info(struct seq_file *s, void *data) +{ + struct unic_dev *unic_dev = dev_get_drvdata(s->private); + + seq_puts(s, "CAP_BITS:\n"); + unic_dbg_dump_caps_bits(unic_dev, s); + seq_puts(s, "\nCAPS:\n"); + unic_dbg_dump_caps(unic_dev, s); + + return 0; +} + +static bool unic_dbg_dentry_support(struct device *dev, u32 property) +{ + struct unic_dev *unic_dev = dev_get_drvdata(dev); + + return ubase_dbg_dentry_support(unic_dev->comdev.adev, property); +} + +static struct ubase_dbg_dentry_info unic_dbg_dentry[] = { + /* keep unic at the bottom and add new directory above */ + { + .name = "unic", + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + }, +}; + +static struct ubase_dbg_cmd_info unic_dbg_cmd[] = { + { + .name = "dev_info", + .dentry_index = UNIC_DBG_DENTRY_ROOT, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_dev_info, + }, { + .name = "caps_info", + .dentry_index = UNIC_DBG_DENTRY_ROOT, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_caps_info, + }, +}; + +int unic_dbg_init(struct auxiliary_device *adev) +{ + struct dentry *ubase_root_dentry = unic_get_ubase_root_dentry(adev); + struct ubase_dbg_dentry_info dentry[UNIC_DBG_DENTRY_ROOT + 1] = {0}; + u8 dentry_num = ARRAY_SIZE(unic_dbg_dentry); + struct device *dev = &adev->dev; + struct unic_dev *unic_dev; + int ret; + + unic_dev = (struct unic_dev *)dev_get_drvdata(dev); + + if (!ubase_root_dentry) { + unic_err(unic_dev, "dbgfs root dentry does not exist.\n"); + return -ENOENT; + } + + unic_dev->dbgfs.dentry = debugfs_create_dir(unic_dbg_dentry[dentry_num - 1].name, + ubase_root_dentry); + if (IS_ERR(unic_dev->dbgfs.dentry)) { + unic_err(unic_dev, "failed to create unic debugfs root dir.\n"); + return PTR_ERR(unic_dev->dbgfs.dentry); + } + + memcpy(dentry, unic_dbg_dentry, sizeof(dentry)); + dentry[UNIC_DBG_DENTRY_ROOT].dentry = unic_dev->dbgfs.dentry; + unic_dev->dbgfs.cmd_info = unic_dbg_cmd; + unic_dev->dbgfs.cmd_info_size = ARRAY_SIZE(unic_dbg_cmd); + + ret = ubase_dbg_create_dentry(dev, &unic_dev->dbgfs, dentry, + ARRAY_SIZE(dentry) - 1); + if (ret) { + unic_err(unic_dev, + "failed to create unic debugfs dentry, ret = %d.\n", + ret); + goto create_dentry_err; + } + + return 0; + +create_dentry_err: + debugfs_remove_recursive(unic_dev->dbgfs.dentry); + + return ret; +} + +void unic_dbg_uninit(struct auxiliary_device *adev) +{ + struct unic_dev *unic_dev = dev_get_drvdata(&adev->dev); + + debugfs_remove_recursive(unic_dev->dbgfs.dentry); +} diff --git a/drivers/net/ub/unic/debugfs/unic_debugfs.h b/drivers/net/ub/unic/debugfs/unic_debugfs.h new file mode 100644 index 000000000000..6efd739d2c5b --- /dev/null +++ b/drivers/net/ub/unic/debugfs/unic_debugfs.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_DEBUGFS_H__ +#define __UNIC_DEBUGFS_H__ + +#include +#include + +#define unic_get_ubase_root_dentry(adev) ubase_diag_debugfs_root(adev) + +enum unic_dbg_dentry_type { + /* must be the last entry. */ + UNIC_DBG_DENTRY_ROOT +}; + +int unic_dbg_init(struct auxiliary_device *adev); +void unic_dbg_uninit(struct auxiliary_device *adev); + +#endif /* __UNIC_DEBUGFS_H__ */ diff --git a/drivers/net/ub/unic/unic.h b/drivers/net/ub/unic/unic.h new file mode 100644 index 000000000000..7f6572c50a0c --- /dev/null +++ b/drivers/net/ub/unic/unic.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_H__ +#define __UNIC_H__ + +#include +#include +#include +#include + +#define UNIC_CAP_LEN 3 +#define UNIC_MAX_VPORT_BUF_NUM 6 + +enum { + UNIC_SUPPORT_PFC_B = 0, + UNIC_SUPPORT_ETS_B = 1, + UNIC_SUPPORT_FEC_B = 2, + UNIC_SUPPORT_PAUSE_B = 3, + UNIC_SUPPORT_GRO_B = 5, + UNIC_SUPPORT_ETH_B = 7, + + UNIC_SUPPORT_TSO_B = 8, + UNIC_SUPPORT_RSS_B = 9, + UNIC_SUPPORT_SERIAL_SERDES_LB_B = 10, + UNIC_SUPPORT_TC_SPEED_LIMIT_B = 12, + UNIC_SUPPORT_TX_CSUM_OFFLOAD_B = 13, + UNIC_SUPPORT_TUNNEL_CSUM_OFFLOAD_B = 14, + UNIC_SUPPORT_PTP_B = 15, + + UNIC_SUPPORT_RX_CSUM_OFFLOAD_B = 16, + UNIC_SUPPORT_APP_LB_B = 17, + UNIC_SUPPORT_FEC_STATS_B = 21, + UNIC_SUPPORT_EXTERNAL_LB_B = 23, + + UNIC_SUPPORT_PARALLEL_SERDES_LB_B = 24, + UNIC_SUPPORT_CFG_VLAN_FILTER_B = 26, + UNIC_SUPPORT_CFG_MAC_B = 27, + + /* must be last entry and it should <= UNIC_CAP_LEN * 32 */ + UNIC_SUPPORT_MASK_NBITS +}; + +#define unic_get_cap_bit(unic_dev, nr) \ + test_bit(nr, (unsigned long *)((unic_dev)->cap_bits)) + +#define UNIC_USER_UPE BIT(0) /* unicast promisc enabled by user */ +#define UNIC_USER_MPE BIT(1) /* mulitcast promisc enabled by user */ +#define UNIC_USER_BPE BIT(2) /* broadcast promisc enabled by user */ +#define UNIC_OVERFLOW_MGP BIT(3) /* mulitcast guid overflow */ +#define UNIC_OVERFLOW_IPP BIT(4) /* unicast ip overflow */ +#define UNIC_UPE (UNIC_USER_UPE | \ + UNIC_OVERFLOW_IPP) +#define UNIC_MPE (UNIC_USER_MPE | \ + UNIC_OVERFLOW_MGP) + +#define UNIC_RSS_MAX_VL_NUM UBASE_NIC_MAX_VL_NUM +#define UNIC_INVALID_PRIORITY (0xff) +#define UNIC_MAX_PRIO_NUM IEEE_8021QAZ_MAX_TCS +#define UNIC_VL_TSA_DWRR IEEE_8021QAZ_TSA_ETS + +/* must be consistent with definition in firmware */ +enum unic_media_type { + UNIC_MEDIA_TYPE_UNKNOWN, + UNIC_MEDIA_TYPE_FIBER, + UNIC_MEDIA_TYPE_BACKPLANE, + UNIC_MEDIA_TYPE_NONE, +}; + +/* must be consistent with definition in firmware */ +enum unic_module_type { + UNIC_MODULE_TYPE_UNKNOWN = 0x00, + UNIC_MODULE_TYPE_FIBRE_LR = 0x01, + UNIC_MODULE_TYPE_FIBRE_SR = 0x02, + UNIC_MODULE_TYPE_AOC = 0x03, + UNIC_MODULE_TYPE_CR = 0x04, + UNIC_MODULE_TYPE_KR = 0x05, + UNIC_MODULE_TYPE_TP = 0x06, +}; + +#define UNIC_LANES_1 1 +#define UNIC_LANES_2 2 +#define UNIC_LANES_4 4 +#define UNIC_LANES_8 8 + +#define UNIC_SUPPORT_200G_X2_BIT BIT(0) +#define UNIC_SUPPORT_200G_X4_BIT BIT(1) +#define UNIC_SUPPORT_400G_X4_BIT BIT(2) +#define UNIC_SUPPORT_400G_X8_BIT BIT(3) +#define UNIC_SUPPORT_25G_X1_BIT BIT(4) +#define UNIC_SUPPORT_50G_X1_BIT BIT(5) +#define UNIC_SUPPORT_50G_X2_BIT BIT(6) +#define UNIC_SUPPORT_100G_X1_BIT BIT(7) +#define UNIC_SUPPORT_100G_X2_BIT BIT(8) +#define UNIC_SUPPORT_100G_X4_BIT BIT(9) +#define UNIC_SUPPORT_10G_X1_BIT BIT(10) + +enum unic_mac_speed { + UNIC_MAC_SPEED_UNKNOWN = 0, + UNIC_MAC_SPEED_10G = SPEED_10000, /* 10000 Mbps = 10 Gbps */ + UNIC_MAC_SPEED_25G = SPEED_25000, /* 25000 Mbps = 25 Gbps */ + UNIC_MAC_SPEED_50G = SPEED_50000, /* 50000 Mbps = 50 Gbps */ + UNIC_MAC_SPEED_100G = SPEED_100000, /* 100000 Mbps = 100 Gbps */ + UNIC_MAC_SPEED_200G = SPEED_200000, /* 200000 Mbps = 200 Gbps */ + UNIC_MAC_SPEED_400G = SPEED_400000, /* 400000 Mbps = 400 Gbps */ +}; + +#define UNIC_MBYTE_PER_SEND 125000 + +#endif diff --git a/drivers/net/ub/unic/unic_cmd.h b/drivers/net/ub/unic/unic_cmd.h new file mode 100644 index 000000000000..4b263009b3f1 --- /dev/null +++ b/drivers/net/ub/unic/unic_cmd.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_CMD_H__ +#define __UNIC_CMD_H__ + +#include +#include +#include + +#include "unic.h" + +struct unic_res_cmd_resp { + __le32 cap_bits[UNIC_CAP_LEN]; + __le32 rsvd[3]; + + u8 media_type; + u8 default_lanes; + __le16 rx_buff_len; + __le32 speed_ability; + __le32 default_speed; + __le16 total_ip_tbl_size; + u8 rsvd1[2]; + __le32 guid_tbl_size; + u8 rsvd2[4]; + __le32 uc_mac_tbl_size; + __le32 vlan_tbl_size; + + __le32 mng_tbl_size; + __le16 max_trans_unit; + __le16 min_trans_unit; + u8 rsvd3[4]; + __le16 vport_buf_size; /* unit: KB */ + u8 vport_buf_num; + u8 rsvd4[5]; + __le16 max_int_ql; + __le16 max_int_gl; + __le32 mc_mac_tbl_size; + u8 rsvd5[4]; +}; + +struct unic_config_max_trans_unit_cmd { + __le16 max_trans_unit; + u8 rsv[22]; +}; + +struct unic_ld_config_mode_cmd { + __le32 txrx_en; + u8 rsv[20]; +}; + +struct unic_link_status_cmd_resp { + u8 status; + u8 link_fail_code; + u8 rsvd[22]; +}; + +struct unic_query_port_info_resp { + __le32 speed; + __le32 speed_ability; + __le16 fec_mode; + __le16 fec_ability; + u8 autoneg; + u8 autoneg_ability; + u8 lanes; + u8 module_type; + u8 rsv[8]; +}; + +struct unic_cfg_speed_dup_cmd { + __le32 speed; + u8 duplex; + u8 lanes; + u8 rsv[18]; +}; + +struct unic_cfg_autoneg_mode_cmd { + u8 autoneg_en; + u8 rsv[23]; +}; + +struct unic_promisc_cfg_cmd { + u8 rsv0; + u8 promisc_uc_ind : 1; /* indicate if uc enable is set or not */ + u8 promisc_mc_ind : 1; /* indicate if mc enable is set or not */ + u8 rsv1 : 6; + u8 promisc_rx_uc_ip_en : 1; /* promisc rx ip uc enable or not */ + u8 promisc_rx_uc_guid_en : 1; /* promisc rx guid uc enable or not */ + u8 promisc_rx_mc_en : 1; /* promisc rx mc enable or not */ + u8 rsv2 : 5; + u8 rsv3[21]; +}; + +struct unic_query_net_guid_cmd { + u8 guid[16]; + u8 rsvd[8]; +}; + +struct unic_cfg_vport_buf_cmd { + u8 buf_num; + u8 rsvd[7]; +#define U32S_PER_U64 2 + __le32 buf_addr[UNIC_MAX_VPORT_BUF_NUM * U32S_PER_U64]; +}; + +struct unic_cfg_fec_cmd { + __le32 fec_mode; + u8 rsv[20]; +}; + +struct unic_query_flush_status_resp { + u8 status; + u8 rsv[23]; +}; + +enum unic_vl_map_type { + UNIC_PRIO_VL_MAP, + UNIC_DSCP_VL_MAP, +}; + +struct unic_config_vl_map_cmd { + u8 map_type; + u8 resv0[3]; + u8 prio_vl[UNIC_MAX_PRIO_NUM]; + u8 dscp_vl[UBASE_MAX_DSCP]; + u8 resv1[12]; +}; + +struct unic_config_vl_speed_cmd { + __le16 bus_ue_id; + __le16 vl_bitmap; + __le32 max_speed[UBASE_MAX_VL_NUM]; + u8 resv1[20]; +}; + +#endif diff --git a/drivers/net/ub/unic/unic_crq.c b/drivers/net/ub/unic/unic_crq.c new file mode 100644 index 000000000000..29a27c6c547d --- /dev/null +++ b/drivers/net/ub/unic/unic_crq.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include + +#include "unic_cmd.h" +#include "unic_dev.h" +#include "unic_hw.h" +#include "unic_netdev.h" +#include "unic_crq.h" + +static void __unic_handle_link_status_event(struct auxiliary_device *adev, + u8 hw_link_status) +{ + struct unic_dev *unic_dev = dev_get_drvdata(&adev->dev); + u8 link_status; + + if (test_and_set_bit(UNIC_STATE_LINK_UPDATING, &unic_dev->state)) + return; + + unic_dbg(unic_dev, "HW link_event status = %u.\n", hw_link_status); + + unic_dev->hw.mac.link_status = hw_link_status; + if (unic_dev_ubl_supported(unic_dev)) + hw_link_status = UNIC_LINK_STATUS_UP; + + link_status = test_bit(UNIC_STATE_DOWN, + &unic_dev->state) ? 0 : hw_link_status; + if (link_status != unic_dev->sw_link_status) { + unic_dev->sw_link_status = link_status; + unic_link_status_change(unic_dev->comdev.netdev, link_status); + } + + clear_bit(UNIC_STATE_LINK_UPDATING, &unic_dev->state); +} + +int unic_handle_link_status_event(void *dev, void *data, u32 len) +{ + struct unic_link_status_cmd_resp *resp = data; + struct auxiliary_device *adev = dev; + u8 hw_link_status = resp->status; + + __unic_handle_link_status_event(adev, hw_link_status); + + return 0; +} diff --git a/drivers/net/ub/unic/unic_crq.h b/drivers/net/ub/unic/unic_crq.h new file mode 100644 index 000000000000..0b7e9afa50df --- /dev/null +++ b/drivers/net/ub/unic/unic_crq.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_CRQ_H__ +#define __UNIC_CRQ_H__ + +int unic_handle_link_status_event(void *dev, void *data, u32 len); + +#endif diff --git a/drivers/net/ub/unic/unic_dcbnl.c b/drivers/net/ub/unic/unic_dcbnl.c new file mode 100644 index 000000000000..3110277da020 --- /dev/null +++ b/drivers/net/ub/unic/unic_dcbnl.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include + +#include "unic_hw.h" +#include "unic_netdev.h" +#include "unic_dcbnl.h" + +static const struct dcbnl_rtnl_ops unic_dcbnl_ops = { +}; + +void unic_set_dcbnl_ops(struct net_device *netdev) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + + if (!unic_dev_ets_supported(unic_dev)) + return; + + netdev->dcbnl_ops = &unic_dcbnl_ops; + + unic_dev->dcbx_cap = DCB_CAP_DCBX_VER_IEEE | DCB_CAP_DCBX_HOST; +} diff --git a/drivers/net/ub/unic/unic_dcbnl.h b/drivers/net/ub/unic/unic_dcbnl.h new file mode 100644 index 000000000000..a721fa51d6e4 --- /dev/null +++ b/drivers/net/ub/unic/unic_dcbnl.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_DCBNL_H__ +#define __UNIC_DCBNL_H__ + +#include + +#ifdef CONFIG_UB_UNIC_DCB +void unic_set_dcbnl_ops(struct net_device *netdev); +#else +static inline void unic_set_dcbnl_ops(struct net_device *netdev) {} +#endif + +#endif diff --git a/drivers/net/ub/unic/unic_dev.c b/drivers/net/ub/unic/unic_dev.c new file mode 100644 index 000000000000..a1724b269817 --- /dev/null +++ b/drivers/net/ub/unic/unic_dev.c @@ -0,0 +1,702 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#define dev_fmt(fmt) "unic: (pid %d) " fmt, current->pid + +#include +#include +#include +#ifdef CONFIG_UB_UNIC_UBL +#include +#endif +#include +#include +#include +#include + +#include "unic_cmd.h" +#include "unic_dcbnl.h" +#include "unic_ethtool.h" +#include "unic_event.h" +#include "unic_guid.h" +#include "unic_hw.h" +#include "unic_qos_hw.h" +#include "unic_netdev.h" +#include "unic_dev.h" + +#define UNIC_WATCHDOG_TIMEOUT (5 * HZ) + +#ifndef UB_DATA_LEN +#define UB_DATA_LEN 1500 +#endif + +static int netif_debug = -1; +module_param(netif_debug, int, 0); +MODULE_PARM_DESC(netif_debug, "network interface message level setting"); + +static int unic_debug; +module_param_named(debug, unic_debug, int, 0644); +MODULE_PARM_DESC(debug, "enable unic debug log, 0:disable, others:enable, default:0"); + +#define DEFAULT_MSG_LEVEL (NETIF_MSG_PROBE | NETIF_MSG_LINK | \ + NETIF_MSG_IFDOWN | NETIF_MSG_IFUP) + +static struct workqueue_struct *unic_wq; + +int unic_dbg_log(void) +{ + return unic_debug; +} + +u32 unic_channels_max_num(struct auxiliary_device *adev) +{ + struct ubase_adev_caps *unic_caps = ubase_get_unic_caps(adev); + u32 jfs_num = unic_caps->jfs.max_cnt; + u32 jfr_num = unic_caps->jfr.max_cnt; + u32 jfc_num = unic_caps->jfc.max_cnt; + + return min(min(jfs_num, jfr_num), jfc_num >> 1); +} + +void unic_update_queue_info(struct unic_dev *unic_dev) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct ubase_adev_qos *qos = ubase_get_adev_qos(adev); + struct unic_channels *channels = &unic_dev->channels; + struct unic_vl *vl = &channels->vl; + u32 i; + + for (i = 0; i < qos->nic_vl_num; i++) { + if (i < channels->rss_vl_num) { + vl->queue_count[i] = channels->rss_size; + vl->queue_offset[i] = i * channels->rss_size; + } else { + vl->queue_count[i] = 0; + vl->queue_offset[i] = channels->rss_vl_num * + channels->rss_size; + } + } +} + +static int unic_update_vl_sl_map(struct unic_dev *unic_dev) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct ubase_adev_qos *qos = ubase_get_adev_qos(adev); + u8 i; + + if (!qos) { + dev_err(adev->dev.parent, "failed to get qos info\n"); + return -ENODATA; + } + + for (i = 0; i < UBASE_MAX_SL_NUM; i++) { + if (qos->ue_sl_vl[i] >= UBASE_MAX_VL_NUM) { + dev_err(adev->dev.parent, + "the sl(%u) map to vl(%u) exceed max_vl_num(%u).\n", + i, qos->ue_sl_vl[i], UBASE_MAX_VL_NUM); + return -EINVAL; + } + + unic_dev->channels.vl.vl_sl[qos->ue_sl_vl[i]] = i; + } + + return 0; +} + +static inline void unic_get_hw_prio_vl(struct ubase_caps *caps, u8 *sw_prio_vl, + u8 *hw_prio_vl) +{ + int i; + + for (i = 0; i < UNIC_MAX_PRIO_NUM; i++) + hw_prio_vl[i] = caps->req_vl[sw_prio_vl[i]]; +} + +static inline void unic_get_hw_dscp_vl(struct ubase_caps *caps, u8 *hw_prio_vl, + u8 *dscp_prio, u8 *hw_dscp_vl) +{ + int i; + + for (i = 0; i < UBASE_MAX_DSCP; i++) + hw_dscp_vl[i] = dscp_prio[i] == UNIC_INVALID_PRIORITY ? + caps->req_vl[0] : hw_prio_vl[dscp_prio[i]]; +} + +int unic_set_vl_map(struct unic_dev *unic_dev, u8 *dscp_prio, u8 *prio_vl, + u8 map_type) +{ + struct ubase_caps *caps = ubase_get_dev_caps(unic_dev->comdev.adev); + u8 hw_prio_vl[UNIC_MAX_PRIO_NUM]; + u8 hw_dscp_vl[UBASE_MAX_DSCP]; + int ret; + + unic_get_hw_prio_vl(caps, prio_vl, hw_prio_vl); + unic_get_hw_dscp_vl(caps, hw_prio_vl, dscp_prio, hw_dscp_vl); + + if (unic_dev_ets_supported(unic_dev) && + !unic_dev_ubl_supported(unic_dev)) { + ret = unic_set_hw_vl_map(unic_dev, hw_dscp_vl, hw_prio_vl, + map_type); + if (ret) + return ret; + } + + ubase_update_udma_dscp_vl(unic_dev->comdev.adev, hw_dscp_vl, + UBASE_MAX_DSCP); + + return 0; +} + +static int unic_init_vl_map(struct unic_dev *unic_dev) +{ + struct unic_vl *vl = &unic_dev->channels.vl; + int i; + + for (i = 0; i < UBASE_MAX_DSCP; i++) + vl->dscp_prio[i] = UNIC_INVALID_PRIORITY; + + return unic_set_vl_map(unic_dev, vl->dscp_prio, + vl->prio_vl, UNIC_PRIO_VL_MAP); +} + +static void unic_vl_bitmap_init(struct unic_dev *unic_dev) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct ubase_caps *caps = ubase_get_dev_caps(adev); + u8 i; + + for (i = 0; i < caps->vl_num; i++) { + unic_dev->channels.vl.vl_bitmap |= 1 << caps->req_vl[i]; + unic_dev->channels.vl.vl_bitmap |= 1 << caps->resp_vl[i]; + } +} + +static int unic_init_vl_sch(struct unic_dev *unic_dev) +{ +#define UNIC_BW_PERCENT 100 + + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct ubase_caps *caps = ubase_get_dev_caps(adev); + struct unic_vl *vl = &unic_dev->channels.vl; + u8 vl_tsa[UBASE_MAX_VL_NUM] = {0}; + u8 vl_bw[UBASE_MAX_VL_NUM] = {0}; + int quo, rem; + u32 i; + + if (!unic_dev_ets_supported(unic_dev)) + return 0; + + quo = UNIC_BW_PERCENT / caps->vl_num; + rem = UNIC_BW_PERCENT % caps->vl_num; + + for (i = 0; i < caps->vl_num; i++) { + vl->vl_tsa[i] = UNIC_VL_TSA_DWRR; + vl->vl_bw[i] = quo; + if (i < rem) + vl->vl_bw[i]++; + + vl_bw[caps->req_vl[i]] = vl->vl_bw[i]; + vl_bw[caps->resp_vl[i]] = vl->vl_bw[i]; + vl_tsa[caps->req_vl[i]] = vl->vl_tsa[i]; + vl_tsa[caps->resp_vl[i]] = vl->vl_tsa[i]; + } + + return ubase_config_tm_vl_sch(adev, vl->vl_bitmap, vl_bw, vl_tsa); +} + +static int unic_init_vl_maxrate(struct unic_dev *unic_dev) +{ + u64 max_speed = unic_dev->hw.mac.max_speed; + u64 vl_maxrate[UBASE_MAX_VL_NUM]; + u8 i; + + if (!unic_dev_ets_supported(unic_dev) || + !unic_dev_tc_speed_limit_supported(unic_dev)) + return 0; + + for (i = 0; i < UBASE_MAX_VL_NUM; i++) + vl_maxrate[i] = max_speed * UNIC_MBYTE_PER_SEND; + + return unic_config_vl_rate_limit(unic_dev, vl_maxrate, + unic_dev->channels.vl.vl_bitmap); +} + +static int unic_init_vl_info(struct unic_dev *unic_dev) +{ + int ret; + + unic_vl_bitmap_init(unic_dev); + unic_update_queue_info(unic_dev); + + ret = unic_update_vl_sl_map(unic_dev); + if (ret) + return ret; + + ret = unic_init_vl_map(unic_dev); + if (ret) + return ret; + + ret = unic_init_vl_maxrate(unic_dev); + if (ret) + return ret; + + return unic_init_vl_sch(unic_dev); +} + +static int unic_init_channels_attr(struct unic_dev *unic_dev) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct unic_channels *channels = &unic_dev->channels; + struct ubase_adev_caps *unic_caps; + int ret; + + mutex_init(&channels->mutex); + + unic_caps = ubase_get_unic_caps(adev); + + channels->vl.vl_num = 1; + channels->rss_vl_num = 1; + channels->rss_size = unic_channels_max_num(unic_dev->comdev.adev); + channels->num = channels->rss_size * channels->rss_vl_num; + channels->sqebb_depth = unic_caps->jfs.depth; + channels->rqe_depth = unic_caps->jfr.depth; + channels->sq_cqe_depth = unic_caps->jfc.depth; + channels->rq_cqe_depth = unic_caps->jfc.depth; + channels->cqe_size = unic_caps->cqe_size; + channels->rx_buff_len = unic_dev->caps.rx_buff_len; + channels->sqebb_shift = ilog2(roundup_pow_of_two(channels->sqebb_depth)); + channels->sq_jfc_shift = ilog2(roundup_pow_of_two(channels->sq_cqe_depth)); + channels->rq_jfc_shift = ilog2(roundup_pow_of_two(channels->rq_cqe_depth)); + + ret = unic_init_vl_info(unic_dev); + if (ret) + mutex_destroy(&channels->mutex); + + return ret; +} + +static void unic_uninit_channels_attr(struct unic_dev *unic_dev) +{ + struct unic_channels *channels = &unic_dev->channels; + + mutex_destroy(&channels->mutex); +} + +static void unic_set_netdev_attr(struct net_device *netdev) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + + netdev->watchdog_timeo = UNIC_WATCHDOG_TIMEOUT; + netdev->priv_flags |= IFF_UNICAST_FLT; + + if (unic_dev_ubl_supported(unic_dev)) { + netdev->features |= NETIF_F_VLAN_CHALLENGED; + netdev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); + } + + if (unic_dev_tx_csum_offload_supported(unic_dev)) + netdev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; + + if (unic_dev_rx_csum_offload_supported(unic_dev)) + netdev->features |= NETIF_F_RXCSUM; + + netdev->hw_features |= netdev->features; + + netdev->vlan_features |= netdev->features & + ~NETIF_F_HW_VLAN_CTAG_FILTER; +} + +static int unic_dev_init_mtu(struct unic_dev *unic_dev) +{ + struct net_device *netdev = unic_dev->comdev.netdev; + struct unic_caps *caps = &unic_dev->caps; + int ret; + + netdev->mtu = UB_DATA_LEN; + netdev->max_mtu = caps->max_trans_unit; + netdev->min_mtu = caps->min_trans_unit; + + ret = unic_config_mtu(unic_dev, netdev->mtu); + if (ret == -EPERM) + return 0; + + return ret; +} + +static int unic_init_mac(struct unic_dev *unic_dev) +{ + struct unic_mac *mac = &unic_dev->hw.mac; + int ret; + + mac->duplex = DUPLEX_FULL; + mac->link_status = UNIC_LINK_STATUS_DOWN; + + ret = unic_set_mac_speed_duplex(unic_dev, mac->speed, mac->duplex, + mac->lanes); + if (ret && ret != -EPERM) + return ret; + + ret = unic_set_mac_autoneg(unic_dev, mac->autoneg); + if (ret && ret != -EPERM) + return ret; + + ret = unic_dev_fec_supported(unic_dev) && mac->user_fec_mode ? + unic_set_fec_mode(unic_dev, mac->user_fec_mode) : 0; + if (ret) + return ret; + + ret = unic_dev_init_mtu(unic_dev); + if (ret) { + dev_err(unic_dev->comdev.adev->dev.parent, + "failed to set initial mtu, ret = %d.\n", ret); + return ret; + } + + return 0; +} + +static void unic_task_schedule(struct unic_dev *unic_dev, + unsigned long delay_time) +{ + if (!test_bit(UNIC_STATE_REMOVING, &unic_dev->state)) + mod_delayed_work(unic_wq, &unic_dev->service_task, delay_time); +} + +static void unic_periodic_service_task(struct unic_dev *unic_dev) +{ + unsigned long delta = round_jiffies_relative(HZ); + + unic_link_status_update(unic_dev); + unic_update_port_info(unic_dev); + unic_sync_promisc_mode(unic_dev); + + unic_dev->serv_processed_cnt++; + unic_task_schedule(unic_dev, delta); +} + +static void unic_service_task(struct work_struct *work) +{ + struct unic_dev *unic_dev = + container_of(work, struct unic_dev, service_task.work); + + unic_periodic_service_task(unic_dev); +} + +static void unic_init_vport_info(struct unic_dev *unic_dev) +{ + unic_dev->vport.back = unic_dev; + + INIT_LIST_HEAD(&unic_dev->vport.addr_tbl.tmp_ip_list); + spin_lock_init(&unic_dev->vport.addr_tbl.tmp_ip_lock); + INIT_LIST_HEAD(&unic_dev->vport.addr_tbl.ip_list); + spin_lock_init(&unic_dev->vport.addr_tbl.ip_list_lock); +} + +static int unic_alloc_vport_buf(struct unic_dev *unic_dev) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + u8 i; + + for (i = 0; i < unic_dev->caps.vport_buf_num; i++) { + unic_dev->vbuf[i].buf = dma_alloc_coherent(adev->dev.parent, + unic_dev->caps.vport_buf_size, + &unic_dev->vbuf[i].dma_addr, GFP_KERNEL); + if (!unic_dev->vbuf[i].buf) { + dev_err(adev->dev.parent, + "failed to alloc vport buffer.\n"); + goto alloc_err; + } + } + + return 0; + +alloc_err: + for (; i > 0; i--) + dma_free_coherent(adev->dev.parent, + unic_dev->caps.vport_buf_size, + unic_dev->vbuf[i - 1].buf, unic_dev->vbuf[i - 1].dma_addr); + return -ENOMEM; +} + +static void unic_free_vport_buf(struct unic_dev *unic_dev) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + u32 i; + + for (i = 0; i < unic_dev->caps.vport_buf_num; i++) + dma_free_coherent(adev->dev.parent, + unic_dev->caps.vport_buf_size, + unic_dev->vbuf[i].buf, unic_dev->vbuf[i].dma_addr); +} + +static int unic_init_vport_buf(struct unic_dev *unic_dev) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + int ret; + + if (!unic_dev->caps.vport_buf_size || !unic_dev->caps.vport_buf_num) + return 0; + + if (unic_dev->caps.vport_buf_num > UNIC_MAX_VPORT_BUF_NUM) { + dev_err(adev->dev.parent, + "vport_buf_num exceeded the maximum(%d).\n", + UNIC_MAX_VPORT_BUF_NUM); + return -EINVAL; + } + + ret = unic_alloc_vport_buf(unic_dev); + if (ret) + return ret; + + ret = unic_cfg_vport_buf(unic_dev, true); + if (ret) + unic_free_vport_buf(unic_dev); + + return ret; +} + +static void unic_uninit_vport_buf(struct unic_dev *unic_dev) +{ + if (!unic_dev->caps.vport_buf_size || !unic_dev->caps.vport_buf_num) + return; + + unic_cfg_vport_buf(unic_dev, false); + unic_free_vport_buf(unic_dev); +} + +static int unic_init_vport(struct unic_dev *unic_dev) +{ + int ret; + + ret = unic_init_vport_buf(unic_dev); + if (ret) + return ret; + + unic_init_vport_info(unic_dev); + + return ret; +} + +static void unic_uninit_vport(struct unic_dev *unic_dev) +{ + unic_uninit_vport_buf(unic_dev); +} + +static void unic_init_netdev_info(struct net_device *netdev) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + + unic_set_netdev_attr(netdev); + unic_set_netdev_ops(netdev); + unic_set_ethtool_ops(netdev); + unic_set_dcbnl_ops(netdev); + + SET_NETDEV_DEV(netdev, unic_dev->comdev.adev->dev.parent); +} + +static int unic_init_dev_addr(struct unic_dev *unic_dev) +{ + if (unic_dev_ubl_supported(unic_dev)) + return unic_init_guid(unic_dev); + + return 0; +} + +static int unic_init_netdev_priv(struct net_device *netdev, + struct auxiliary_device *adev) +{ + struct unic_dev *priv = netdev_priv(netdev); + int ret; + + priv->comdev.netdev = netdev; + priv->comdev.adev = adev; + priv->msg_enable = netif_msg_init(netif_debug, DEFAULT_MSG_LEVEL); + priv->tid = ubase_get_dev_caps(adev)->tid; + mutex_init(&priv->act_info.mutex); + + ret = unic_query_dev_res(priv); + if (ret) + goto destroy_lock; + + ret = unic_init_vport(priv); + if (ret) + goto destroy_lock; + + ret = unic_update_port_info(priv); + if (ret) { + dev_err(adev->dev.parent, + "failed to update port info, ret = %d.\n", ret); + goto err_uninit_vport; + } + + ret = unic_init_mac(priv); + if (ret) + goto err_uninit_vport; + + ret = unic_init_dev_addr(priv); + if (ret) + goto err_uninit_vport; + + ret = unic_init_channels_attr(priv); + if (ret) + goto err_uninit_vport; + + set_bit(UNIC_STATE_DOWN, &priv->state); + return 0; + +err_uninit_vport: + unic_uninit_vport(priv); +destroy_lock: + mutex_destroy(&priv->act_info.mutex); + + return ret; +} + +static void unic_uninit_netdev_priv(struct net_device *netdev) +{ + struct unic_dev *priv = netdev_priv(netdev); + + unic_uninit_channels_attr(priv); + unic_uninit_vport(priv); + mutex_destroy(&priv->act_info.mutex); +} + +int unic_init_wq(void) +{ + unic_wq = alloc_workqueue("%s", WQ_UNBOUND, 0, "unic"); + if (!unic_wq) + return -ENOMEM; + + return 0; +} + +void unic_destroy_wq(void) +{ + destroy_workqueue(unic_wq); +} + +static void unic_init_period_task(struct net_device *netdev) +{ + struct unic_dev *priv = netdev_priv(netdev); + + INIT_DELAYED_WORK(&priv->service_task, unic_service_task); +} + +void unic_start_period_task(struct net_device *netdev) +{ + unsigned long delta = round_jiffies_relative(HZ); + struct unic_dev *unic_dev = netdev_priv(netdev); + + unic_task_schedule(unic_dev, delta); +} + +void unic_remove_period_task(struct unic_dev *unic_dev) +{ + if (unic_dev->service_task.work.func) + cancel_delayed_work_sync(&unic_dev->service_task); +} + +static struct net_device *unic_alloc_netdev(struct auxiliary_device *adev) +{ + struct ubase_caps *caps = ubase_get_dev_caps(adev); + u32 channel_num = unic_channels_max_num(adev); + struct net_device *netdev = NULL; + char name[IFNAMSIZ] = {0}; + + if (!channel_num) + channel_num = UNIC_DEFAULT_CHANNEL_NUM; + + if (ubase_adev_ubl_supported(adev)) { +#ifdef CONFIG_UB_UNIC_UBL + snprintf(name, IFNAMSIZ, "ublc%ud%ue%u", caps->chip_id, + caps->die_id, caps->ue_id); + netdev = alloc_netdev_mq(sizeof(struct unic_dev), name, + NET_NAME_USER, ubl_setup, channel_num); +#else + dev_warn(adev->dev.parent, + "failed to alloc netdev because of ubl macro is not enabled.\n"); +#endif + } + + return netdev; +} + +static void unic_start_dev_period_task(struct net_device *netdev) +{ + unic_init_period_task(netdev); + unic_start_period_task(netdev); +} + +int unic_dev_init(struct auxiliary_device *adev) +{ + struct net_device *netdev; + int ret; + + netdev = unic_alloc_netdev(adev); + if (!netdev) + return -ENOMEM; + + dev_set_drvdata(&adev->dev, netdev_priv(netdev)); + + ret = unic_init_netdev_priv(netdev, adev); + if (ret) { + dev_err(adev->dev.parent, + "failed to init netdev_priv, ret = %d.\n", ret); + goto err_free_netdev; + } + unic_init_netdev_info(netdev); + + ret = unic_register_event(adev); + if (ret) + goto err_uninit_netdev_priv; + + ret = register_netdev(netdev); + if (ret) { + dev_err(adev->dev.parent, + "failed to register netdev, ret = %d.\n", ret); + goto err_unregister_event; + } + + unic_start_dev_period_task(netdev); + + return 0; + +err_unregister_event: + unic_unregister_event(adev); +err_uninit_netdev_priv: + unic_uninit_netdev_priv(netdev); +err_free_netdev: + free_netdev(netdev); + return ret; +} + +void unic_dev_uninit(struct auxiliary_device *adev) +{ + struct unic_dev *priv = (struct unic_dev *)dev_get_drvdata(&adev->dev); + struct net_device *netdev = priv->comdev.netdev; + struct unic_promisc_en promisc_en = {0}; + + /* cancel service task and wait it finish before release resources. */ + unic_remove_period_task(priv); + + /* Ensure that the link status change is processed before + * unregister_netdev. Prevent the kernel from accessing the released + * netdev address. + */ + unic_unregister_event(adev); + + /* Explicitly disable promisc to avoid hardware promisc residue */ + unic_set_promisc_mode(priv, &promisc_en); + + if (netdev->reg_state != NETREG_UNINITIALIZED) + unregister_netdev(netdev); + + unic_uninit_netdev_priv(netdev); + + free_netdev(netdev); + + dev_set_drvdata(&adev->dev, NULL); +} diff --git a/drivers/net/ub/unic/unic_dev.h b/drivers/net/ub/unic/unic_dev.h new file mode 100644 index 000000000000..3c41471fa534 --- /dev/null +++ b/drivers/net/ub/unic/unic_dev.h @@ -0,0 +1,277 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_DEV_H__ +#define __UNIC_DEV_H__ + +#include +#include +#include +#include +#include +#include + +#include "unic.h" +#include "unic_cmd.h" +#include "unic_ethtool.h" + +#define UNIC_MOD_VERSION "1.0" +#define UNIC_AE_LEVEL_NUM 1 + +enum unic_dev_state { + UNIC_STATE_INVALID, + UNIC_STATE_RESETTING, + UNIC_STATE_INITED, + UNIC_STATE_DOWN, + UNIC_STATE_REMOVING, + UNIC_STATE_DISABLED, + UNIC_STATE_LINK_UPDATING, + UNIC_STATE_TESTING, + UNIC_STATE_DEACTIVATE, +}; + +enum unic_vport_state { + UNIC_VPORT_STATE_ALIVE, + UNIC_VPORT_STATE_PROMISC_CHANGE, + UNIC_VPORT_STATE_IP_TBL_CHANGE, + UNIC_VPORT_STATE_IP_QUERYING, +}; + +#define UNIC_DEFAULT_CHANNEL_NUM 1 + +#define unic_dbg(_unic_dev, fmt, ...) \ + do { \ + if (unic_dbg_log()) \ + netdev_info((_unic_dev)->comdev.netdev, "(pid %d) " fmt, \ + current->pid, ##__VA_ARGS__); \ + } while (0) + +#define unic_err(_unic_dev, fmt, ...) \ + netdev_err((_unic_dev)->comdev.netdev, "(pid %d) " fmt, \ + current->pid, ##__VA_ARGS__) + +#define unic_info(_unic_dev, fmt, ...) \ + netdev_info((_unic_dev)->comdev.netdev, "(pid %d) " fmt, \ + current->pid, ##__VA_ARGS__) + +#define unic_warn(_unic_dev, fmt, ...) \ + netdev_warn((_unic_dev)->comdev.netdev, "(pid %d) " fmt, \ + current->pid, ##__VA_ARGS__) + +struct unic_mac { + u8 port; /* see PORT_TP/PORT_FIBRE... */ + u8 media_type; /* port media type, see unic_media_type */ + u8 module_type; /* module type, see unic_module_type */ + u8 duplex; + u8 autoneg; + u8 support_autoneg; + u32 link_status; /* link status of mac & phy (if phy exists) */ + u32 speed; + u32 max_speed; + u32 speed_ability; + u32 lanes; /* lane number */ + u32 fec_mode; /* active fec mode */ + u32 fec_ability; /* supported fec mode */ + u32 user_fec_mode; /* user configured fec mode */ + u8 mac_addr[ETH_ALEN]; + + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); + __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); +}; + +struct unic_hw { + struct unic_mac mac; +}; + +struct unic_channel { + struct unic_sq *sq; + struct unic_rq *rq; + struct unic_cq *sq_cq; + struct unic_cq *rq_cq; + struct napi_struct napi; + /*​ ​monitor each jetty's return status in parallel operations.​ */ + int ret; +}; + +struct unic_coal_txrx { + struct unic_coalesce tx_coal; + struct unic_coalesce rx_coal; +}; + +struct unic_vl { + u8 vl_num; + u8 dscp_app_cnt; + u8 dscp_prio[UBASE_MAX_DSCP]; + u8 vl_tsa[UBASE_MAX_VL_NUM]; + u8 prio_vl[UNIC_MAX_PRIO_NUM]; + u8 vl_bw[UBASE_MAX_VL_NUM]; + u16 queue_count[UBASE_MAX_VL_NUM]; + u16 queue_offset[UBASE_MAX_VL_NUM]; + u8 vl_sl[UBASE_MAX_VL_NUM]; + u64 vl_maxrate[UBASE_MAX_VL_NUM]; + u16 vl_bitmap; +}; + +struct unic_channels { + struct unic_channel *c; + struct unic_vl vl; + u32 num; + u16 sqebb_depth; + u16 rqe_depth; + u32 sq_cqe_depth; + u32 rq_cqe_depth; + u16 rx_buff_len; + u16 cqe_size; + u32 rss_size; + u8 rss_vl_num; /* the number of vl used by unic */ + u8 sqebb_shift; + u8 sq_jfc_shift; + u8 rq_jfc_shift; + struct unic_coal_txrx unic_coal; + struct mutex mutex; /* protects modify channel's resource and mapping */ + unsigned long state; +}; + +struct unic_caps { + u16 rx_buff_len; + u16 total_ip_tbl_size; + u32 rsvd0[5]; + u16 max_trans_unit; + u16 min_trans_unit; + u32 vport_buf_size; /* unit: byte */ + u8 vport_buf_num; + u16 max_int_ql; /* max value of interrupt coalesce based on INT_QL */ + u16 max_int_gl; /* max value of interrupt coalesce based on INT_GL */ +}; + +struct unic_addr_tbl { + spinlock_t ip_list_lock; /* protect ip address need to add/detele */ + struct list_head ip_list; /* Store ip table */ + + spinlock_t tmp_ip_lock; /* protect ip address from controller */ + struct list_head tmp_ip_list; /* Store temprary ip table */ +}; + +struct unic_vport_buf { + void *buf; + dma_addr_t dma_addr; +}; + +struct unic_vport { + struct unic_dev *back; + struct unic_addr_tbl addr_tbl; + u8 overflow_promisc_flags; + u8 last_promisc_flags; + unsigned long state; +}; + +struct unic_act_info { + bool deactivate; + struct mutex mutex; /* protects modify deactivate state */ +}; + +struct unic_dev { + /* This member must be first, adaptor driver will relay on it */ + struct ubase_adev_com comdev; + struct unic_hw hw; + struct unic_channels channels; + struct ubase_dbgfs dbgfs; + u8 dcbx_cap; + struct unic_caps caps; + u32 cap_bits[UNIC_CAP_LEN]; + u32 msg_enable; + unsigned long state; + struct delayed_work service_task; + struct ubase_event_nb ae_nbs[UNIC_AE_LEVEL_NUM]; + u8 netdev_flags; + u8 loopback_flags; + struct unic_vport vport; + struct unic_vport_buf vbuf[UNIC_MAX_VPORT_BUF_NUM]; + unsigned long serv_processed_cnt; + struct unic_act_info act_info; + u32 tid; + u8 sw_link_status; +}; + +int unic_dev_init(struct auxiliary_device *adev); +void unic_dev_uninit(struct auxiliary_device *adev); +u32 unic_channels_max_num(struct auxiliary_device *adev); +void unic_start_period_task(struct net_device *netdev); +void unic_remove_period_task(struct unic_dev *unic_dev); +int unic_init_wq(void); +void unic_destroy_wq(void); +int unic_set_vl_map(struct unic_dev *unic_dev, u8 *dscp_prio, u8 *prio_vl, + u8 map_type); +int unic_dbg_log(void); + +static inline bool unic_dev_ubl_supported(struct unic_dev *unic_dev) +{ + return ubase_adev_ubl_supported(unic_dev->comdev.adev); +} + +static inline bool unic_dev_ets_supported(struct unic_dev *unic_dev) +{ + return unic_get_cap_bit(unic_dev, UNIC_SUPPORT_ETS_B); +} + +static inline bool unic_dev_fec_supported(struct unic_dev *unic_dev) +{ + return unic_get_cap_bit(unic_dev, UNIC_SUPPORT_FEC_B); +} + +static inline bool unic_dev_tc_speed_limit_supported(struct unic_dev *unic_dev) +{ + return unic_get_cap_bit(unic_dev, UNIC_SUPPORT_TC_SPEED_LIMIT_B); +} + +static inline bool unic_dev_tx_csum_offload_supported(struct unic_dev *unic_dev) +{ + return unic_get_cap_bit(unic_dev, UNIC_SUPPORT_TX_CSUM_OFFLOAD_B); +} + +static inline bool unic_dev_rx_csum_offload_supported(struct unic_dev *unic_dev) +{ + return unic_get_cap_bit(unic_dev, UNIC_SUPPORT_RX_CSUM_OFFLOAD_B); +} + +static inline bool __unic_removing(struct unic_dev *unic_dev) +{ + return test_bit(UNIC_STATE_REMOVING, &unic_dev->state); +} + +static inline bool __unic_resetting(struct unic_dev *unic_dev) +{ + return test_bit(UNIC_STATE_RESETTING, &unic_dev->state); +} + +static inline bool unic_resetting(struct net_device *netdev) +{ + return __unic_resetting(netdev_priv(netdev)); +} + +static inline bool __unic_initing(struct unic_dev *unic_dev) +{ + return !test_bit(UNIC_STATE_INITED, &unic_dev->state); +} + +static inline bool unic_initing(struct net_device *netdev) +{ + return __unic_initing(netdev_priv(netdev)); +} + +static inline bool unic_is_initing_or_resetting(struct unic_dev *unic_dev) +{ + return __unic_resetting(unic_dev) || __unic_initing(unic_dev); +} + +static inline u32 unic_cmd_timeout(struct unic_dev *unic_dev) +{ +#define UNIC_CMD_TIMEOUT 5000 + + return __unic_removing(unic_dev) ? UNIC_CMD_TIMEOUT : 0; +} + +#endif diff --git a/drivers/net/ub/unic/unic_ethtool.c b/drivers/net/ub/unic/unic_ethtool.c new file mode 100644 index 000000000000..c6577bbf7468 --- /dev/null +++ b/drivers/net/ub/unic/unic_ethtool.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include +#include + +#include "unic.h" +#include "unic_dev.h" +#include "unic_hw.h" +#include "unic_netdev.h" +#include "unic_ethtool.h" + +static u32 unic_get_link_status(struct net_device *netdev) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + + unic_link_status_update(unic_dev); + + return unic_dev->sw_link_status; +} + +static void unic_get_driver_info(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + struct ubase_caps *caps = ubase_get_dev_caps(unic_dev->comdev.adev); + u32 fw_version = 0; + + if (!caps) + unic_err(unic_dev, + "failed to get fw version, use default value 0.\n"); + else + fw_version = caps->fw_version; + + strscpy(drvinfo->version, UNIC_MOD_VERSION, sizeof(drvinfo->version)); + strscpy(drvinfo->driver, unic_dev->comdev.adev->name, + sizeof(drvinfo->driver)); + strscpy(drvinfo->bus_info, dev_name(unic_dev->comdev.adev->dev.parent), + sizeof(drvinfo->bus_info)); + + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "%u.%u.%u.%u", + u32_get_bits(fw_version, UBASE_FW_VERSION_BYTE3_MASK), + u32_get_bits(fw_version, UBASE_FW_VERSION_BYTE2_MASK), + u32_get_bits(fw_version, UBASE_FW_VERSION_BYTE1_MASK), + u32_get_bits(fw_version, UBASE_FW_VERSION_BYTE0_MASK)); +} + +#define UNIC_ETHTOOL_RING (ETHTOOL_RING_USE_RX_BUF_LEN | \ + ETHTOOL_RING_USE_TX_PUSH) +#define UNIC_ETHTOOL_COALESCE (ETHTOOL_COALESCE_USECS | \ + ETHTOOL_COALESCE_USE_ADAPTIVE | \ + ETHTOOL_COALESCE_MAX_FRAMES) + +static const struct ethtool_ops unic_ethtool_ops = { + .supported_ring_params = UNIC_ETHTOOL_RING, + .cap_link_lanes_supported = true, + .supported_coalesce_params = UNIC_ETHTOOL_COALESCE, + .get_link = unic_get_link_status, + .get_drvinfo = unic_get_driver_info, +}; + +void unic_set_ethtool_ops(struct net_device *netdev) +{ + netdev->ethtool_ops = &unic_ethtool_ops; +} diff --git a/drivers/net/ub/unic/unic_ethtool.h b/drivers/net/ub/unic/unic_ethtool.h new file mode 100644 index 000000000000..a440536e011f --- /dev/null +++ b/drivers/net/ub/unic/unic_ethtool.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_ETHTOOL_H__ +#define __UNIC_ETHTOOL_H__ + +#include +#include + +struct unic_reset_type_map { + enum ethtool_reset_flags reset_flags; + enum ubase_reset_type reset_type; +}; + +struct unic_coalesce { + u16 int_gl; + u16 int_ql; + u8 adapt_enable : 1; + u8 resv : 7; +}; + +void unic_set_ethtool_ops(struct net_device *netdev); + +#endif diff --git a/drivers/net/ub/unic/unic_event.c b/drivers/net/ub/unic/unic_event.c new file mode 100644 index 000000000000..a1cd5e4cafd4 --- /dev/null +++ b/drivers/net/ub/unic/unic_event.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#define dev_fmt(fmt) "unic: (pid %d) " fmt, current->pid + +#include +#include +#include +#include +#include +#include +#include + +#include "unic_cmd.h" +#include "unic_crq.h" +#include "unic_dcbnl.h" +#include "unic_dev.h" +#include "unic_hw.h" +#include "unic_netdev.h" +#include "unic_qos_hw.h" +#include "unic_event.h" + +static struct ubase_crq_event_nb unic_crq_events[] = { + { + .opcode = UBASE_OPC_QUERY_LINK_STATUS, + .crq_handler = unic_handle_link_status_event, + }, +}; + +static void unic_unregister_crq_event(struct auxiliary_device *adev, + u32 crq_event_num) +{ + u32 i; + + for (i = 0; i < crq_event_num; i++) + ubase_unregister_crq_event(adev, unic_crq_events[i].opcode); +} + +static int unic_register_crq_event(struct auxiliary_device *adev) +{ + int ret; + u32 i; + + for (i = 0; i < ARRAY_SIZE(unic_crq_events); i++) { + unic_crq_events[i].back = adev; + + ret = ubase_register_crq_event(adev, &unic_crq_events[i]); + if (ret) { + dev_err(adev->dev.parent, + "failed to register crq event[%u], ret = %d.\n", + i, ret); + unic_unregister_crq_event(adev, i); + return ret; + } + } + + return 0; +} + +int unic_register_event(struct auxiliary_device *adev) +{ + int ret; + + ret = unic_register_crq_event(adev); + if (ret) + return ret; + + return 0; +} + +void unic_unregister_event(struct auxiliary_device *adev) +{ + unic_unregister_crq_event(adev, ARRAY_SIZE(unic_crq_events)); +} diff --git a/drivers/net/ub/unic/unic_event.h b/drivers/net/ub/unic/unic_event.h new file mode 100644 index 000000000000..258b801f9b08 --- /dev/null +++ b/drivers/net/ub/unic/unic_event.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_EVENT_H__ +#define __UNIC_EVENT_H__ + +#include + +int unic_register_event(struct auxiliary_device *adev); +void unic_unregister_event(struct auxiliary_device *adev); + +#endif diff --git a/drivers/net/ub/unic/unic_guid.c b/drivers/net/ub/unic/unic_guid.c new file mode 100644 index 000000000000..5639ab540eee --- /dev/null +++ b/drivers/net/ub/unic/unic_guid.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#define dev_fmt(fmt) "unic: (pid %d) " fmt, current->pid + +#include +#ifdef CONFIG_UB_UNIC_UBL +#include +#endif +#include + +#include "unic_guid.h" + +#ifndef UBL_ALEN +#define UBL_ALEN 16 +#endif + +static bool unic_is_valid_net_guid(u8 *guid) +{ +#define UNIC_MGUID_PREFIX_LEN 14 + + u8 invalid_guid[UBL_ALEN]; + + memset(invalid_guid, 0, UBL_ALEN); + if (!memcmp(guid, invalid_guid, UBL_ALEN)) + return false; + + memset(invalid_guid, 0xff, UBL_ALEN); + if (!memcmp(guid, invalid_guid, UNIC_MGUID_PREFIX_LEN)) + return false; + + return true; +} + +static inline void unic_guid_le_to_net_trans(u8 *src_guid, u8 *dest_guid) +{ + int i; + + for (i = 0; i < UBL_ALEN; i++) + dest_guid[i] = src_guid[UBL_ALEN - i - 1]; +} + +static void unic_query_net_guid(struct unic_dev *unic_dev, u8 *guid) +{ + struct unic_query_net_guid_cmd resp = {0}; + struct ubase_cmd_buf in, out; + int ret; + + ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_NET_GUID, true, 0, NULL); + ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_NET_GUID, true, + sizeof(resp), &resp); + ret = ubase_cmd_send_inout(unic_dev->comdev.adev, &in, &out); + if (ret) { + dev_warn(unic_dev->comdev.adev->dev.parent, + "failed to query net guid, ret = %d.\n", ret); + return; + } + + memcpy(guid, resp.guid, UBL_ALEN); +} + +static void unic_get_net_guid(struct unic_dev *unic_dev, u8 *guid) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + u8 out_guid[UBL_ALEN] = {0}; + bool is_random = false; + + unic_query_net_guid(unic_dev, out_guid); + + unic_guid_le_to_net_trans(out_guid, guid); + + while (unlikely(!unic_is_valid_net_guid(guid))) { + get_random_bytes(guid, UBL_ALEN); + is_random = true; + } + + if (unlikely(is_random)) + dev_warn(adev->dev.parent, + "using random GUID %02x:%02x:...:%02x:%02x.\n", + guid[0], guid[1], + guid[UBL_ALEN - 2], guid[UBL_ALEN - 1]); +} + +int unic_init_guid(struct unic_dev *unic_dev) +{ + struct net_device *netdev = unic_dev->comdev.netdev; + u8 guid[UBL_ALEN]; + + unic_get_net_guid(unic_dev, guid); + + dev_addr_set(netdev, guid); + memcpy(netdev->perm_addr, guid, netdev->addr_len); + + return 0; +} diff --git a/drivers/net/ub/unic/unic_guid.h b/drivers/net/ub/unic/unic_guid.h new file mode 100644 index 000000000000..2f8f436db041 --- /dev/null +++ b/drivers/net/ub/unic/unic_guid.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_GUID_H__ +#define __UNIC_GUID_H__ + +#include "unic_dev.h" + +int unic_init_guid(struct unic_dev *unic_dev); + +#endif diff --git a/drivers/net/ub/unic/unic_hw.c b/drivers/net/ub/unic/unic_hw.c new file mode 100644 index 000000000000..0a35a7da3b84 --- /dev/null +++ b/drivers/net/ub/unic/unic_hw.c @@ -0,0 +1,654 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#define dev_fmt(fmt) "unic: (pid %d) " fmt, current->pid + +#include +#include + +#include "unic.h" +#include "unic_cmd.h" +#include "unic_dev.h" +#include "unic_hw.h" + +static const struct unic_speed_bit_map speed_bit_map[] = { + /* caution: should keep them in descending order by speed! + * see unic_get_max_speed() + */ + {UNIC_MAC_SPEED_400G, UNIC_LANES_8, UNIC_SUPPORT_400G_X8_BIT}, + {UNIC_MAC_SPEED_400G, UNIC_LANES_4, UNIC_SUPPORT_400G_X4_BIT}, + {UNIC_MAC_SPEED_200G, UNIC_LANES_4, UNIC_SUPPORT_200G_X4_BIT}, + {UNIC_MAC_SPEED_200G, UNIC_LANES_2, UNIC_SUPPORT_200G_X2_BIT}, + {UNIC_MAC_SPEED_100G, UNIC_LANES_4, UNIC_SUPPORT_100G_X4_BIT}, + {UNIC_MAC_SPEED_100G, UNIC_LANES_2, UNIC_SUPPORT_100G_X2_BIT}, + {UNIC_MAC_SPEED_100G, UNIC_LANES_1, UNIC_SUPPORT_100G_X1_BIT}, + {UNIC_MAC_SPEED_50G, UNIC_LANES_2, UNIC_SUPPORT_50G_X2_BIT}, + {UNIC_MAC_SPEED_50G, UNIC_LANES_1, UNIC_SUPPORT_50G_X1_BIT}, + {UNIC_MAC_SPEED_25G, UNIC_LANES_1, UNIC_SUPPORT_25G_X1_BIT}, + {UNIC_MAC_SPEED_10G, UNIC_LANES_1, UNIC_SUPPORT_10G_X1_BIT}, +}; + +static int unic_get_port_info(struct unic_dev *unic_dev) +{ + struct unic_query_port_info_resp resp = {0}; + struct unic_mac *mac = &unic_dev->hw.mac; + struct ubase_cmd_buf in, out; + int ret; + + ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_PORT_INFO, true, 0, NULL); + ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_PORT_INFO, false, + sizeof(resp), &resp); + + ret = ubase_cmd_send_inout(unic_dev->comdev.adev, &in, &out); + if (ret) + return ret; + + mac->speed = le32_to_cpu(resp.speed); + mac->speed_ability = le32_to_cpu(resp.speed_ability); + mac->autoneg = resp.autoneg; + mac->support_autoneg = resp.autoneg_ability; + mac->lanes = resp.lanes; + mac->module_type = resp.module_type; + mac->fec_mode = le16_to_cpu(resp.fec_mode); + mac->fec_ability = le16_to_cpu(resp.fec_ability); + + return 0; +} + +int unic_set_mac_autoneg(struct unic_dev *unic_dev, u8 autoneg) +{ + struct unic_cfg_autoneg_mode_cmd req = {0}; + struct ubase_cmd_buf in; + int ret; + + if (unic_dev_ubl_supported(unic_dev)) + return unic_is_initing_or_resetting(unic_dev) ? 0 : -EOPNOTSUPP; + + if (!unic_dev->hw.mac.support_autoneg) + return 0; + + req.autoneg_en = autoneg ? 1 : 0; + + ubase_fill_inout_buf(&in, UBASE_OPC_CONFIG_AUTONEG_MODE, false, + sizeof(req), &req); + + ret = ubase_cmd_send_in(unic_dev->comdev.adev, &in); + if (ret && ret != -EPERM) + dev_err(unic_dev->comdev.adev->dev.parent, + "failed to send cmd in config autoneg(%u), ret = %d.\n", + autoneg, ret); + + if (!ret) + unic_dev->hw.mac.autoneg = req.autoneg_en; + + return ret; +} + +int unic_set_mac_speed_duplex(struct unic_dev *unic_dev, u32 speed, u8 duplex, + u8 lanes) +{ + struct unic_cfg_speed_dup_cmd req = {0}; + struct ubase_cmd_buf in; + int ret; + + if (unic_dev_ubl_supported(unic_dev)) + return unic_is_initing_or_resetting(unic_dev) ? 0 : -EOPNOTSUPP; + + req.speed = cpu_to_le32(speed); + req.duplex = duplex; + req.lanes = lanes; + + ubase_fill_inout_buf(&in, UBASE_OPC_CONFIG_SPEED_DUP, false, + sizeof(req), &req); + + ret = ubase_cmd_send_in(unic_dev->comdev.adev, &in); + if (ret && ret != -EPERM) + dev_err(unic_dev->comdev.adev->dev.parent, + "failed to send cmd in config speed(%u), ret = %d.\n", + speed, ret); + + return ret; +} + +static void unic_set_fec_ability(struct unic_mac *mac) +{ + linkmode_clear_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT, mac->supported); + linkmode_clear_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT, mac->supported); + linkmode_clear_bit(ETHTOOL_LINK_MODE_FEC_BASER_BIT, mac->supported); + linkmode_clear_bit(ETHTOOL_LINK_MODE_FEC_LLRS_BIT, mac->supported); + + if (mac->fec_ability & ETHTOOL_FEC_RS) + linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT, + mac->supported); + if (mac->fec_ability & ETHTOOL_FEC_OFF) + linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT, + mac->supported); +} + +static const struct unic_link_mode_bit_map sr_linkmode_bit_map[] = { + {UNIC_SUPPORT_400G_X8_BIT, ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT}, + {UNIC_SUPPORT_400G_X4_BIT, ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT}, + {UNIC_SUPPORT_200G_X4_BIT, ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT}, + {UNIC_SUPPORT_200G_X2_BIT, ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT}, + {UNIC_SUPPORT_100G_X4_BIT, ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT}, + {UNIC_SUPPORT_100G_X2_BIT, ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT}, + {UNIC_SUPPORT_100G_X1_BIT, ETHTOOL_LINK_MODE_100000baseSR_Full_BIT}, + {UNIC_SUPPORT_50G_X2_BIT, ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT}, + {UNIC_SUPPORT_50G_X1_BIT, ETHTOOL_LINK_MODE_50000baseSR_Full_BIT}, + {UNIC_SUPPORT_25G_X1_BIT, ETHTOOL_LINK_MODE_25000baseSR_Full_BIT}, + {UNIC_SUPPORT_10G_X1_BIT, ETHTOOL_LINK_MODE_10000baseSR_Full_BIT}, +}; + +static const struct unic_link_mode_bit_map lr_linkmode_bit_map[] = { + {UNIC_SUPPORT_400G_X8_BIT, ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT}, + {UNIC_SUPPORT_400G_X4_BIT, ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT}, + {UNIC_SUPPORT_200G_X4_BIT, ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT}, + {UNIC_SUPPORT_200G_X2_BIT, ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT}, + {UNIC_SUPPORT_100G_X4_BIT, ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT}, + {UNIC_SUPPORT_100G_X2_BIT, ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT}, + {UNIC_SUPPORT_100G_X1_BIT, ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT}, + {UNIC_SUPPORT_50G_X1_BIT, ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT}, + {UNIC_SUPPORT_10G_X1_BIT, ETHTOOL_LINK_MODE_10000baseLR_Full_BIT}, +}; + +static const struct unic_link_mode_bit_map cr_linkmode_bit_map[] = { + {UNIC_SUPPORT_400G_X8_BIT, ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT}, + {UNIC_SUPPORT_400G_X4_BIT, ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT}, + {UNIC_SUPPORT_200G_X4_BIT, ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT}, + {UNIC_SUPPORT_200G_X2_BIT, ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT}, + {UNIC_SUPPORT_100G_X4_BIT, ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT}, + {UNIC_SUPPORT_100G_X2_BIT, ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT}, + {UNIC_SUPPORT_100G_X1_BIT, ETHTOOL_LINK_MODE_100000baseCR_Full_BIT}, + {UNIC_SUPPORT_50G_X2_BIT, ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT}, + {UNIC_SUPPORT_50G_X1_BIT, ETHTOOL_LINK_MODE_50000baseCR_Full_BIT}, + {UNIC_SUPPORT_25G_X1_BIT, ETHTOOL_LINK_MODE_25000baseCR_Full_BIT}, + {UNIC_SUPPORT_10G_X1_BIT, ETHTOOL_LINK_MODE_10000baseCR_Full_BIT}, +}; + +static const struct unic_link_mode_bit_map kr_linkmode_bit_map[] = { + {UNIC_SUPPORT_400G_X8_BIT, ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT}, + {UNIC_SUPPORT_400G_X4_BIT, ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT}, + {UNIC_SUPPORT_200G_X4_BIT, ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT}, + {UNIC_SUPPORT_200G_X2_BIT, ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT}, + {UNIC_SUPPORT_100G_X4_BIT, ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT}, + {UNIC_SUPPORT_100G_X2_BIT, ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT}, + {UNIC_SUPPORT_100G_X1_BIT, ETHTOOL_LINK_MODE_100000baseKR_Full_BIT}, + {UNIC_SUPPORT_50G_X2_BIT, ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT}, + {UNIC_SUPPORT_50G_X1_BIT, ETHTOOL_LINK_MODE_50000baseKR_Full_BIT}, + {UNIC_SUPPORT_25G_X1_BIT, ETHTOOL_LINK_MODE_25000baseKR_Full_BIT}, + {UNIC_SUPPORT_10G_X1_BIT, ETHTOOL_LINK_MODE_10000baseKR_Full_BIT}, +}; + +static void unic_set_linkmode(const struct unic_link_mode_bit_map *map, u32 map_size, + u32 speed_ability, unsigned long *link_mode) +{ + u32 i; + + for (i = 0; i < map_size; i++) { + if (speed_ability & map[i].speed_bit) + linkmode_set_bit(map[i].link_mode, link_mode); + } +} + +static void unic_set_linkmode_sr(u32 speed_ability, unsigned long *link_mode) +{ + unic_set_linkmode(sr_linkmode_bit_map, ARRAY_SIZE(sr_linkmode_bit_map), + speed_ability, link_mode); +} + +static void unic_set_linkmode_lr(u32 speed_ability, unsigned long *link_mode) +{ + unic_set_linkmode(lr_linkmode_bit_map, ARRAY_SIZE(lr_linkmode_bit_map), + speed_ability, link_mode); +} + +static void unic_set_linkmode_cr(u32 speed_ability, unsigned long *link_mode) +{ + unic_set_linkmode(cr_linkmode_bit_map, ARRAY_SIZE(cr_linkmode_bit_map), + speed_ability, link_mode); +} + +static void unic_set_linkmode_kr(u32 speed_ability, unsigned long *link_mode) +{ + unic_set_linkmode(kr_linkmode_bit_map, ARRAY_SIZE(kr_linkmode_bit_map), + speed_ability, link_mode); +} + +static void unic_update_speed_advertising(struct unic_mac *mac) +{ + u32 speed_ability = mac->speed_ability; + + switch (mac->module_type) { + case UNIC_MODULE_TYPE_FIBRE_LR: + unic_set_linkmode_lr(speed_ability, mac->advertising); + break; + case UNIC_MODULE_TYPE_FIBRE_SR: + case UNIC_MODULE_TYPE_AOC: + unic_set_linkmode_sr(speed_ability, mac->advertising); + break; + case UNIC_MODULE_TYPE_CR: + unic_set_linkmode_cr(speed_ability, mac->advertising); + break; + case UNIC_MODULE_TYPE_KR: + unic_set_linkmode_kr(speed_ability, mac->advertising); + break; + default: + break; + } +} + +static void unic_update_fec_advertising(struct unic_mac *mac) +{ + if (mac->fec_mode & ETHTOOL_FEC_RS) + linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT, + mac->advertising); + else if (mac->fec_mode & ETHTOOL_FEC_OFF) + linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT, + mac->advertising); +} + +static void unic_update_advertising(struct unic_dev *unic_dev) +{ + struct unic_mac *mac = &unic_dev->hw.mac; + + linkmode_zero(mac->advertising); + + unic_update_speed_advertising(mac); + unic_update_fec_advertising(mac); +} + +static void unic_update_port_capability(struct unic_dev *unic_dev) +{ + struct unic_mac *mac = &unic_dev->hw.mac; + + unic_set_fec_ability(mac); + + if (mac->support_autoneg) { + linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, mac->supported); + linkmode_copy(mac->advertising, mac->supported); + } else { + linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, mac->supported); + unic_update_advertising(unic_dev); + } +} + +int unic_update_port_info(struct unic_dev *unic_dev) +{ + int ret; + + ret = unic_get_port_info(unic_dev); + if (ret) + return ret; + + unic_update_port_capability(unic_dev); + + return 0; +} + +static void unic_setup_promisc_req(struct unic_promisc_cfg_cmd *req, + struct unic_promisc_en *promisc_en) +{ + req->promisc_uc_ind = 1; + req->promisc_rx_uc_guid_en = promisc_en->en_uc_guid; + req->promisc_rx_uc_ip_en = promisc_en->en_uc_ip; + + req->promisc_mc_ind = 1; + req->promisc_rx_mc_en = promisc_en->en_mc; +} + +int unic_set_promisc_mode(struct unic_dev *unic_dev, + struct unic_promisc_en *promisc_en) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct unic_promisc_cfg_cmd req = {0}; + struct ubase_cmd_buf in; + u32 time_out; + int ret; + + unic_setup_promisc_req(&req, promisc_en); + + ubase_fill_inout_buf(&in, UBASE_OPC_CFG_PROMISC_MODE, false, + sizeof(req), &req); + + time_out = unic_cmd_timeout(unic_dev); + ret = ubase_cmd_send_in_ex(adev, &in, time_out); + if (ret == -EPERM) + return 0; + else if (ret) + unic_err(unic_dev, + "failed to set promisc mode, ret = %d.\n", ret); + + return ret; +} + +void unic_fill_promisc_en(struct unic_promisc_en *promisc_en, u8 flags) +{ + promisc_en->en_uc_ip = !!(flags & UNIC_UPE); + promisc_en->en_mc = !!(flags & UNIC_MPE); +} + +int unic_activate_promisc_mode(struct unic_dev *unic_dev, bool activate) +{ + struct unic_promisc_en promisc_en = {0}; + u8 flags; + + flags = unic_dev->netdev_flags | unic_dev->vport.last_promisc_flags; + if (!flags) + return 0; + + if (activate) + unic_fill_promisc_en(&promisc_en, flags); + + return unic_set_promisc_mode(unic_dev, &promisc_en); +} + +int unic_sync_promisc_mode(struct unic_dev *unic_dev) +{ + struct unic_act_info *act_info = &unic_dev->act_info; + struct unic_vport *vport = &unic_dev->vport; + struct unic_promisc_en promisc_en; + struct auxiliary_device *adev; + int ret = 0; + + adev = unic_dev->comdev.adev; + + if (!mutex_trylock(&act_info->mutex)) + return 0; + + if (act_info->deactivate) + goto out; + + if (vport->last_promisc_flags != vport->overflow_promisc_flags) { + set_bit(UNIC_VPORT_STATE_PROMISC_CHANGE, &vport->state); + vport->last_promisc_flags = vport->overflow_promisc_flags; + } + + if (!test_and_clear_bit(UNIC_VPORT_STATE_PROMISC_CHANGE, &vport->state)) + goto out; + + unic_fill_promisc_en(&promisc_en, + unic_dev->netdev_flags | vport->last_promisc_flags); + + ret = unic_set_promisc_mode(unic_dev, &promisc_en); + if (ret) + set_bit(UNIC_VPORT_STATE_PROMISC_CHANGE, &vport->state); + +out: + mutex_unlock(&act_info->mutex); + + return ret; +} + +static void unic_parse_fiber_link_mode(struct unic_dev *unic_dev, + u32 speed_ability) +{ + struct unic_mac *mac = &unic_dev->hw.mac; + + unic_set_linkmode_sr(speed_ability, mac->supported); + unic_set_linkmode_lr(speed_ability, mac->supported); + unic_set_linkmode_cr(speed_ability, mac->supported); + + linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, mac->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT, mac->supported); +} + +static void unic_parse_backplane_link_mode(struct unic_dev *unic_dev, + u32 speed_ability) +{ + struct unic_mac *mac = &unic_dev->hw.mac; + + unic_set_linkmode_kr(speed_ability, mac->supported); + + linkmode_set_bit(ETHTOOL_LINK_MODE_Backplane_BIT, mac->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT, mac->supported); +} + +static void unic_parse_link_mode(struct unic_dev *unic_dev, u32 speed_ability) +{ + u8 media_type = unic_dev->hw.mac.media_type; + + if (media_type == UNIC_MEDIA_TYPE_FIBER) + unic_parse_fiber_link_mode(unic_dev, speed_ability); + else if (media_type == UNIC_MEDIA_TYPE_BACKPLANE) + unic_parse_backplane_link_mode(unic_dev, speed_ability); + else + dev_warn(unic_dev->comdev.adev->dev.parent, + "unknown media_type = %u.\n", media_type); +} + +static u32 unic_get_max_speed(u32 cur_speed, u32 speed_ability) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(speed_bit_map); i++) { + if (speed_ability & speed_bit_map[i].speed_bit) + return speed_bit_map[i].speed; + } + + return cur_speed; +} + +static void unic_parse_mac_cfg(struct unic_dev *unic_dev, + const struct unic_res_cmd_resp *resp) +{ + struct unic_mac *mac = &unic_dev->hw.mac; + + mac->media_type = resp->media_type; + mac->speed = le32_to_cpu(resp->default_speed); + mac->lanes = resp->default_lanes; + mac->speed_ability = le32_to_cpu(resp->speed_ability); + + unic_parse_link_mode(unic_dev, mac->speed_ability); + + mac->max_speed = unic_get_max_speed(mac->speed, mac->speed_ability); + + unic_dbg(unic_dev, + "default speed = %u, lanes = %u, speed_ability = 0x%x\n", + mac->speed, mac->lanes, mac->speed_ability); +} + +static void unic_parse_dev_caps(struct unic_dev *unic_dev, + const struct unic_res_cmd_resp *resp) +{ +#define KB 1024U + + struct unic_caps *caps = &unic_dev->caps; + int i; + + for (i = 0; i < UNIC_CAP_LEN; i++) + unic_dev->cap_bits[i] = le32_to_cpu(resp->cap_bits[i]); + + caps->rx_buff_len = le16_to_cpu(resp->rx_buff_len); + caps->total_ip_tbl_size = le16_to_cpu(resp->total_ip_tbl_size); + caps->max_trans_unit = le16_to_cpu(resp->max_trans_unit); + caps->min_trans_unit = le16_to_cpu(resp->min_trans_unit); + caps->vport_buf_size = le16_to_cpu(resp->vport_buf_size) * KB; + caps->vport_buf_num = resp->vport_buf_num; + caps->max_int_ql = le16_to_cpu(resp->max_int_ql); + caps->max_int_gl = le16_to_cpu(resp->max_int_gl); + + unic_parse_mac_cfg(unic_dev, resp); +} + +int unic_query_dev_res(struct unic_dev *unic_dev) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct unic_res_cmd_resp resp; + struct ubase_cmd_buf out; + struct ubase_cmd_buf in; + int ret; + + memset(&resp, 0, sizeof(resp)); + ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_NIC_RSRC_PARAM, true, 0, + NULL); + + ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_NIC_RSRC_PARAM, false, + sizeof(resp), &resp); + + ret = ubase_cmd_send_inout(adev, &in, &out); + if (ret) { + dev_err(adev->dev.parent, "failed to query unic res, ret = %d.\n", + ret); + return ret; + } + + unic_parse_dev_caps(unic_dev, &resp); + + return 0; +} + +int unic_config_mtu(struct unic_dev *unic_dev, int new_mtu) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct unic_config_max_trans_unit_cmd req = {0}; + struct ubase_cmd_buf in; + + req.max_trans_unit = cpu_to_le16(new_mtu); + + ubase_fill_inout_buf(&in, UBASE_OPC_CFG_MTU, false, sizeof(req), &req); + + return ubase_cmd_send_in(adev, &in); +} + +static int unic_query_flush_status(struct unic_dev *unic_dev, u8 *status) +{ + struct unic_query_flush_status_resp resp = {0}; + struct ubase_cmd_buf out; + struct ubase_cmd_buf in; + int ret; + + ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_FLUSH_STATUS, true, 0, NULL); + ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_FLUSH_STATUS, false, + sizeof(resp), &resp); + + ret = ubase_cmd_send_inout(unic_dev->comdev.adev, &in, &out); + if (ret) { + if (ret == -EPERM) + return -EOPNOTSUPP; + + unic_err(unic_dev, + "failed to send cmd when query flush status, ret = %d.\n", + ret); + return ret; + } + + *status = resp.status; + return ret; +} + +static int unic_wait_hw_queue_flush_done(struct unic_dev *unic_dev) +{ +#define UNIC_FLUSH_STATUS_WAIT_CNT 10 +#define UNIC_FLUSH_STATUS_WAIT_MS 50 +#define UNIC_FLUSH_DONE 1 + + int i, ret; + u8 status; + + for (i = 0; i < UNIC_FLUSH_STATUS_WAIT_CNT; i++) { + ret = unic_query_flush_status(unic_dev, &status); + if (ret) { + msleep(UNIC_FLUSH_STATUS_WAIT_MS); + continue; + } + + if (status == UNIC_FLUSH_DONE) + return 0; + + msleep(UNIC_FLUSH_STATUS_WAIT_MS); + } + + return -EBUSY; +} + +int unic_mac_cfg(struct unic_dev *unic_dev, bool enable) +{ + struct unic_ld_config_mode_cmd req = {0}; + unsigned long dl_conf_mode_en = 0; + struct ubase_cmd_buf in; + u32 time_out; + int ret; + + if (enable) { + set_bit(UNIC_DL_CONFIG_MODE_TX_EN_B, &dl_conf_mode_en); + set_bit(UNIC_DL_CONFIG_MODE_RX_EN_B, &dl_conf_mode_en); + } + + if (test_bit(UNIC_STATE_TESTING, &unic_dev->state) && enable) + set_bit(UNIC_DL_CONFIG_MODE_APP_LP_EN_B, &dl_conf_mode_en); + + req.txrx_en = cpu_to_le32((u32)dl_conf_mode_en); + + ubase_fill_inout_buf(&in, UBASE_OPC_DL_CONFIG_MODE, false, + sizeof(req), &req); + + time_out = unic_cmd_timeout(unic_dev); + ret = ubase_cmd_send_in_ex(unic_dev->comdev.adev, &in, time_out); + if (ret) + unic_err(unic_dev, "failed to %s mac, ret = %d.\n", + enable ? "enable" : "disable", ret); + + return ret; +} + +int unic_mac_mode_cfg(struct net_device *netdev, bool enable) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + int ret = 0; + + if (!unic_dev_ubl_supported(unic_dev)) + ret = unic_mac_cfg(unic_dev, enable); + + if (!enable && !unic_resetting(netdev)) + return unic_wait_hw_queue_flush_done(unic_dev); + + return ret; +} + +int unic_cfg_vport_buf(struct unic_dev *unic_dev, bool init) +{ + struct unic_cfg_vport_buf_cmd req = {0}; + struct ubase_cmd_buf in; + dma_addr_t addr; + u32 time_out; + int ret, i; + + req.buf_num = unic_dev->caps.vport_buf_num; + if (init) { + for (i = 0; i < unic_dev->caps.vport_buf_num; i++) { + addr = unic_dev->vbuf[i].dma_addr; + req.buf_addr[i * U32S_PER_U64] = cpu_to_le32(lower_32_bits(addr)); + req.buf_addr[i * U32S_PER_U64 + 1] = cpu_to_le32(upper_32_bits(addr)); + } + } + + ubase_fill_inout_buf(&in, UBASE_OPC_CFG_VPORT_BUF, false, + sizeof(req), &req); + time_out = unic_cmd_timeout(unic_dev); + ret = ubase_cmd_send_in_ex(unic_dev->comdev.adev, &in, time_out); + if (ret) + dev_err(unic_dev->comdev.adev->dev.parent, + "failed to config vport buffer, ret = %d.\n", ret); + return ret; +} + +int unic_set_fec_mode(struct unic_dev *unic_dev, u32 fec_mode) +{ + struct unic_cfg_fec_cmd req = {0}; + struct ubase_cmd_buf in; + int ret; + + req.fec_mode = cpu_to_le32(fec_mode); + ubase_fill_inout_buf(&in, UBASE_OPC_CONFIG_FEC_MODE, false, + sizeof(req), &req); + ret = ubase_cmd_send_in(unic_dev->comdev.adev, &in); + if (ret) + dev_err(unic_dev->comdev.adev->dev.parent, + "failed to set fec mode(%u), ret = %d.\n", + fec_mode, ret); + + return ret; +} diff --git a/drivers/net/ub/unic/unic_hw.h b/drivers/net/ub/unic/unic_hw.h new file mode 100644 index 000000000000..70320bae49c0 --- /dev/null +++ b/drivers/net/ub/unic/unic_hw.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_HW_H__ +#define __UNIC_HW_H__ + +#include +#include + +#include "unic_dev.h" + +#define UNIC_DL_CONFIG_MODE_TX_EN_B 0 +#define UNIC_DL_CONFIG_MODE_RX_EN_B 1 +#define UNIC_DL_CONFIG_MODE_APP_LP_EN_B 2 + +#define UNIC_LINK_STATUS_UP_B 0 +#define UNIC_LINK_STATUS_UP_M BIT(UNIC_LINK_STATUS_UP_B) + +#define UNIC_LINK_STATUS_DOWN 0 +#define UNIC_LINK_STATUS_UP 1 + +struct unic_caps_item { + void *p; + u32 default_val; + u8 size; + const char *name; +}; + +struct unic_speed_bit_map { + u32 speed; + u32 lanes; + u32 speed_bit; +}; + +struct unic_link_mode_bit_map { + u32 speed_bit; + enum ethtool_link_mode_bit_indices link_mode; +}; + +struct unic_promisc_en { + u8 en_uc_ip; + u8 en_uc_guid; + u8 en_mc; +}; + +int unic_update_port_info(struct unic_dev *unic_dev); + +int unic_set_mac_speed_duplex(struct unic_dev *unic_dev, u32 speed, u8 duplex, + u8 lanes); +int unic_set_mac_autoneg(struct unic_dev *unic_dev, u8 autoneg); + +int unic_query_dev_res(struct unic_dev *unic_dev); + +int unic_config_mtu(struct unic_dev *unic_dev, int new_mtu); + +int unic_mac_cfg(struct unic_dev *unic_dev, bool enable); +int unic_mac_mode_cfg(struct net_device *netdev, bool enable); + +int unic_sync_promisc_mode(struct unic_dev *unic_dev); +int unic_activate_promisc_mode(struct unic_dev *unic_dev, bool activate); +void unic_fill_promisc_en(struct unic_promisc_en *promisc_en, u8 flags); +int unic_set_promisc_mode(struct unic_dev *unic_dev, + struct unic_promisc_en *promisc_en); + +int unic_cfg_vport_buf(struct unic_dev *unic_dev, bool init); +int unic_set_fec_mode(struct unic_dev *unic_dev, u32 fec_mode); + +#endif diff --git a/drivers/net/ub/unic/unic_main.c b/drivers/net/ub/unic/unic_main.c new file mode 100644 index 000000000000..22b97068ab23 --- /dev/null +++ b/drivers/net/ub/unic/unic_main.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#define dev_fmt(fmt) "unic: (pid %d) " fmt, current->pid + +#include +#include +#include + +#include "debugfs/unic_debugfs.h" +#include "unic_dev.h" +#include "unic_hw.h" +#include "unic_netdev.h" + +static int unic_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct unic_dev *unic_dev; + int ret; + + ret = unic_dev_init(adev); + if (ret) { + dev_err(adev->dev.parent, + "failed to init unic dev, ret = %d.\n", ret); + return ret; + } + + unic_dev = (struct unic_dev *)dev_get_drvdata(&adev->dev); + ret = unic_dbg_init(adev); + if (ret) { + unic_err(unic_dev, + "failed to init unic debugfs, ret = %d.\n", ret); + unic_dev_uninit(adev); + return ret; + } + + set_bit(UNIC_STATE_INITED, &unic_dev->state); + + return 0; +} + +static void unic_remove(struct auxiliary_device *adev) +{ +#define UNIC_RESET_WAIT_TIME 100 + + struct unic_dev *unic_dev = (struct unic_dev *)dev_get_drvdata(&adev->dev); + + while (test_and_set_bit(UNIC_STATE_DISABLED, &unic_dev->state)) + msleep(UNIC_RESET_WAIT_TIME); + + set_bit(UNIC_STATE_REMOVING, &unic_dev->state); + unic_dbg_uninit(adev); + unic_dev_uninit(adev); +} + +static const struct auxiliary_device_id unic_id_table[] = { + { + .name = UBASE_ADEV_NAME ".unic", + }, + {}, +}; + +MODULE_DEVICE_TABLE(auxiliary, unic_id_table); + +static struct auxiliary_driver unic_drv = { + .probe = unic_probe, + .remove = unic_remove, + .name = "unic", + .id_table = unic_id_table, +}; + +static int __init unic_init(void) +{ + int ret; + + ret = unic_init_wq(); + if (ret) { + pr_err("unic: failed to create workqueue.\n"); + return ret; + } + + ret = auxiliary_driver_register(&unic_drv); + if (ret) + goto err_aux_reg; + + return ret; + +err_aux_reg: + unic_destroy_wq(); + return ret; +} + +static void __exit unic_exit(void) +{ + auxiliary_driver_unregister(&unic_drv); + unic_destroy_wq(); +} + +module_init(unic_init); +module_exit(unic_exit); + +MODULE_DESCRIPTION("UNIC: Hisilicon Network Driver"); +MODULE_AUTHOR("Huawei Tech. Co., Ltd."); +MODULE_LICENSE("GPL"); +MODULE_VERSION(UNIC_MOD_VERSION); diff --git a/drivers/net/ub/unic/unic_netdev.c b/drivers/net/ub/unic/unic_netdev.c new file mode 100644 index 000000000000..285b19738a4a --- /dev/null +++ b/drivers/net/ub/unic/unic_netdev.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#define dev_fmt(fmt) "unic: (pid %d) " fmt, current->pid + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "unic_cmd.h" +#include "unic_dev.h" +#include "unic_hw.h" +#include "unic_netdev.h" + +static int unic_net_up(struct net_device *netdev) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + int ret; + + ret = unic_mac_mode_cfg(netdev, true); + if (ret) + return ret; + + unic_dev->sw_link_status = UNIC_LINK_STATUS_DOWN; + + return 0; +} + +void unic_link_status_change(struct net_device *netdev, bool linkup) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + + /* stop/wake network interface by deactivate/activate event */ + if (test_bit(UNIC_STATE_DEACTIVATE, &unic_dev->state)) + goto out; + + if (linkup) { + if (!test_bit(UNIC_STATE_TESTING, &unic_dev->state)) + netif_carrier_on(netdev); + } else { + netif_carrier_off(netdev); + } + +out: + if (netif_msg_link(unic_dev)) + unic_info(unic_dev, "%s.\n", linkup ? "link up" : "link down"); +} + +void unic_link_status_update(struct unic_dev *unic_dev) +{ + u8 link_status = UNIC_LINK_STATUS_DOWN; + int ret; + + if (test_and_set_bit(UNIC_STATE_LINK_UPDATING, &unic_dev->state)) + return; + + if (test_bit(UNIC_STATE_DOWN, &unic_dev->state)) + goto ifdown; + + ret = unic_query_link_status(unic_dev, &link_status); + if (ret) { + clear_bit(UNIC_STATE_LINK_UPDATING, &unic_dev->state); + return; + } + + unic_dev->hw.mac.link_status = link_status; + if (unic_dev_ubl_supported(unic_dev)) + link_status = UNIC_LINK_STATUS_UP; + +ifdown: + if (link_status != unic_dev->sw_link_status) { + unic_dev->sw_link_status = link_status; + unic_link_status_change(unic_dev->comdev.netdev, link_status); + } + + clear_bit(UNIC_STATE_LINK_UPDATING, &unic_dev->state); +} + +int unic_net_open(struct net_device *netdev) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + int ret; + + if (test_bit(UNIC_STATE_RESETTING, &unic_dev->state)) + return -EBUSY; + + if (!test_bit(UNIC_STATE_DOWN, &unic_dev->state)) { + unic_warn(unic_dev, "net open repeatedly.\n"); + return 0; + } + + netif_carrier_off(netdev); + + /* only cfg mac mode, wake network interface by activate event */ + if (test_bit(UNIC_STATE_DEACTIVATE, &unic_dev->state)) { + ret = unic_mac_mode_cfg(netdev, true); + if (ret) { + unic_err(unic_dev, "failed to cfg mac mode, ret = %d.\n", + ret); + return ret; + } + + clear_bit(UNIC_STATE_DOWN, &unic_dev->state); + return 0; + } + + ret = unic_net_up(netdev); + if (ret) { + unic_err(unic_dev, "failed to up net, ret = %d.\n", ret); + return ret; + } + + clear_bit(UNIC_STATE_DOWN, &unic_dev->state); + + if (netif_msg_ifup(unic_dev)) + unic_info(unic_dev, "net open.\n"); + + return 0; +} + +int unic_net_open_no_link_change(struct net_device *netdev) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + + if (unic_resetting(netdev)) + return -EBUSY; + + if (test_bit(UNIC_STATE_DOWN, &unic_dev->state)) + return 0; + + netif_carrier_off(netdev); + + if (netif_msg_ifup(unic_dev)) + unic_info(unic_dev, "netif open.\n"); + + /* ensure that network interface is awakened when linked up, + * otherwise, it will be handled by periodic tasks + */ + if (unic_dev->sw_link_status == UNIC_LINK_STATUS_UP) + netif_carrier_on(netdev); + + return 0; +} + +static void unic_dev_stop(struct net_device *netdev) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + + unic_mac_mode_cfg(netdev, false); + + unic_link_status_update(unic_dev); +} + +void unic_net_stop_no_link_change(struct net_device *netdev) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + + if (test_bit(UNIC_STATE_DOWN, &unic_dev->state)) + return; + + if (netif_msg_ifdown(unic_dev)) + unic_info(unic_dev, "netif stop.\n"); + + netif_carrier_off(netdev); + netif_tx_disable(netdev); +} + +int unic_net_stop(struct net_device *netdev) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + + if (test_and_set_bit(UNIC_STATE_DOWN, &unic_dev->state)) + return 0; + + /* only cfg mac mode, because stop network interface has been done + * by deactivate event + */ + if (test_bit(UNIC_STATE_DEACTIVATE, &unic_dev->state)) { + unic_dev_stop(netdev); + return 0; + } + + if (netif_msg_ifdown(unic_dev)) + unic_info(unic_dev, "net stop.\n"); + + netif_carrier_off(netdev); + netif_tx_disable(netdev); + unic_dev_stop(netdev); + + return 0; +} + +static const struct net_device_ops unic_netdev_ops = { + .ndo_open = unic_net_open, + .ndo_stop = unic_net_stop, +}; + +void unic_set_netdev_ops(struct net_device *netdev) +{ + netdev->netdev_ops = &unic_netdev_ops; +} + +int unic_query_link_status(struct unic_dev *unic_dev, u8 *link_status) +{ + struct unic_link_status_cmd_resp resp = {0}; + struct ubase_cmd_buf out; + struct ubase_cmd_buf in; + int ret; + + ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_LINK_STATUS, true, 0, NULL); + + ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_LINK_STATUS, false, + sizeof(resp), &resp); + + ret = ubase_cmd_send_inout(unic_dev->comdev.adev, &in, &out); + if (ret) { + unic_err(unic_dev, + "failed to get mac link status, ret = %d.\n", ret); + return ret; + } + + *link_status = (resp.status & UNIC_LINK_STATUS_UP_M) > 0 ? + UNIC_LINK_STATUS_UP : UNIC_LINK_STATUS_DOWN; + + return 0; +} diff --git a/drivers/net/ub/unic/unic_netdev.h b/drivers/net/ub/unic/unic_netdev.h new file mode 100644 index 000000000000..f3383b8660a3 --- /dev/null +++ b/drivers/net/ub/unic/unic_netdev.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_NETDEV_H__ +#define __UNIC_NETDEV_H__ + +#include + +#include "unic_dev.h" + +int unic_net_open(struct net_device *netdev); +int unic_net_open_no_link_change(struct net_device *netdev); +int unic_net_stop(struct net_device *netdev); +void unic_net_stop_no_link_change(struct net_device *netdev); +void unic_set_netdev_ops(struct net_device *netdev); +void unic_link_status_update(struct unic_dev *unic_dev); +void unic_link_status_change(struct net_device *netdev, bool linkup); +int unic_query_link_status(struct unic_dev *unic_dev, u8 *link_status); + +#endif diff --git a/drivers/net/ub/unic/unic_qos_hw.c b/drivers/net/ub/unic/unic_qos_hw.c new file mode 100644 index 000000000000..2f463be3d324 --- /dev/null +++ b/drivers/net/ub/unic/unic_qos_hw.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#define dev_fmt(fmt) "unic: (pid %d) " fmt, current->pid + +#include "unic_hw.h" +#include "unic_qos_hw.h" + +int unic_set_hw_vl_map(struct unic_dev *unic_dev, u8 *dscp_vl, u8 *prio_vl, + u8 map_type) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct unic_config_vl_map_cmd req = {0}; + struct ubase_cmd_buf in; + int ret; + + memcpy(req.dscp_vl, dscp_vl, sizeof(req.dscp_vl)); + memcpy(req.prio_vl, prio_vl, sizeof(req.prio_vl)); + req.map_type = map_type; + ubase_fill_inout_buf(&in, UBASE_OPC_CFG_VL_MAP, false, + sizeof(req), &req); + ret = ubase_cmd_send_in(adev, &in); + if (ret) + dev_err(adev->dev.parent, "failed to set vl map. ret = %d.\n", ret); + + return ret; +} + +/* vl_maxrate: byte per second */ +int unic_config_vl_rate_limit(struct unic_dev *unic_dev, u64 *vl_maxrate, + u16 vl_bitmap) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct ubase_caps *caps = ubase_get_dev_caps(adev); + u32 max_speed = unic_dev->hw.mac.max_speed; + struct unic_config_vl_speed_cmd req = {0}; + struct ubase_cmd_buf in; + u32 vl_rate; + int i, ret; + + req.bus_ue_id = cpu_to_le16(USHRT_MAX); + req.vl_bitmap = cpu_to_le16(vl_bitmap); + for (i = 0; i < caps->vl_num; i++) { + vl_rate = vl_maxrate[i] / UNIC_MBYTE_PER_SEND; + vl_rate = vl_rate ? vl_rate : max_speed; + req.max_speed[caps->req_vl[i]] = cpu_to_le32(vl_rate); + req.max_speed[caps->resp_vl[i]] = cpu_to_le32(vl_rate); + } + + ubase_fill_inout_buf(&in, UBASE_OPC_VL_RATE_LIMIT_CONFIG, false, + sizeof(req), &req); + + ret = ubase_cmd_send_in(adev, &in); + if (ret) + dev_err(adev->dev.parent, + "failed to config vl rate limit, ret = %d.\n", ret); + + return ret; +} diff --git a/drivers/net/ub/unic/unic_qos_hw.h b/drivers/net/ub/unic/unic_qos_hw.h new file mode 100644 index 000000000000..9f6947463349 --- /dev/null +++ b/drivers/net/ub/unic/unic_qos_hw.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_QOS_HW_H__ +#define __UNIC_QOS_HW_H__ + +#include + +#include "unic_dev.h" + +int unic_set_hw_vl_map(struct unic_dev *unic_dev, u8 *dscp_vl, u8 *prio_vl, + u8 map_type); +int unic_config_vl_rate_limit(struct unic_dev *unic_dev, u64 *vl_maxrate, + u16 vl_bitmap); + +#endif diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index c412d77ed281..62ad1ee97d9b 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -31,14 +31,20 @@ enum ubase_opcode_type { UBASE_OPC_QUERY_FW_VER = 0x0001, UBASE_OPC_QUERY_CTL_INFO = 0x0003, UBASE_OPC_QUERY_COMM_RSRC_PARAM = 0x0030, + UBASE_OPC_QUERY_NIC_RSRC_PARAM = 0x0031, + UBASE_OPC_QUERY_LINK_STATUS = 0x0032, + UBASE_OPC_CFG_MTU = 0x0033, + UBASE_OPC_QUERY_NET_GUID = 0x0035, UBASE_OPC_STATS_MAC_ALL = 0x0038, UBASE_OPC_QUERY_BUS_EID = 0x0047, /* NL commands */ + UBASE_OPC_CFG_VL_MAP = 0x2206, UBASE_OPC_CFG_ETS_TC_INFO = 0x2340, UBASE_OPC_QUERY_ETS_TCG_INFO = 0x2341, UBASE_OPC_QUERY_ETS_PORT_INFO = 0x2342, UBASE_OPC_QUERY_VL_AGEING_EN = 0x2343, + UBASE_OPC_CFG_PROMISC_MODE = 0x240A, /* TP commands */ UBASE_OPC_TP_TIMER_VA_CONFIG = 0x3007, @@ -51,6 +57,7 @@ enum ubase_opcode_type { UBASE_OPC_QUERY_OOR_CAPS = 0x4200, UBASE_OPC_QUERY_TA_SL_VL_MAP = 0x4201, UBASE_OPC_TA_VL_SCH_CONFIG = 0x4202, + UBASE_OPC_VL_RATE_LIMIT_CONFIG = 0x4203, UBASE_OPC_QUERY_TM_Q_INFO = 0x4205, UBASE_OPC_QUERY_TM_QS_INFO = 0x4206, UBASE_OPC_QUERY_TM_PRI_INFO = 0x4207, @@ -59,10 +66,16 @@ enum ubase_opcode_type { UBASE_OPC_QUERY_FST_FVT_RQMT = 0x4212, /* DL commands */ + UBASE_OPC_DL_CONFIG_MODE = 0x5100, + UBASE_OPC_QUERY_FLUSH_STATUS = 0x5102, UBASE_OPC_START_PERF_STATS = 0x5103, UBASE_OPC_STOP_PERF_STATS = 0x5104, /* PHY commands */ + UBASE_OPC_CONFIG_SPEED_DUP = 0x6100, + UBASE_OPC_CONFIG_AUTONEG_MODE = 0x6101, + UBASE_OPC_CONFIG_FEC_MODE = 0x6102, + UBASE_OPC_QUERY_PORT_INFO = 0x6200, UBASE_OPC_QUERY_CHIP_INFO = 0x6201, /* Mailbox commands */ -- Gitee From 8b381da45c74c29bea3556dac48af66399e859fc Mon Sep 17 00:00:00 2001 From: Haibin Lu Date: Thu, 11 Sep 2025 15:40:42 +0800 Subject: [PATCH 175/214] net: unic: add io basic Rx/Tx functionality for unic ANBZ: #45460 commit d8164d3745d4497c9fe901371f64a07b9c409537 openEuler. This patch provides queue management and basic tx/rx functionality; however, this functionality depends on IP configuration management, which will be provided in subsequent patches. Signed-off-by: Haiqing Fang Signed-off-by: Haibin Lu Signed-off-by: Yixi Shen Signed-off-by: Jianqiang Li Signed-off-by: Junxin Chen Signed-off-by: Xiongchuan Zhou Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/Makefile | 2 +- drivers/net/ub/unic/debugfs/unic_debugfs.c | 52 +- drivers/net/ub/unic/unic_dev.c | 207 +++ drivers/net/ub/unic/unic_dev.h | 49 + drivers/net/ub/unic/unic_event.c | 28 + drivers/net/ub/unic/unic_event.h | 1 + drivers/net/ub/unic/unic_hw.h | 5 + drivers/net/ub/unic/unic_netdev.c | 324 ++++- drivers/net/ub/unic/unic_rx.c | 1220 ++++++++++++++++++ drivers/net/ub/unic/unic_rx.h | 178 +++ drivers/net/ub/unic/unic_trace.h | 155 +++ drivers/net/ub/unic/unic_tx.c | 1348 ++++++++++++++++++++ drivers/net/ub/unic/unic_tx.h | 305 +++++ drivers/net/ub/unic/unic_txrx.c | 372 ++++++ drivers/net/ub/unic/unic_txrx.h | 260 ++++ 15 files changed, 4501 insertions(+), 5 deletions(-) create mode 100644 drivers/net/ub/unic/unic_rx.c create mode 100644 drivers/net/ub/unic/unic_rx.h create mode 100644 drivers/net/ub/unic/unic_trace.h create mode 100644 drivers/net/ub/unic/unic_tx.c create mode 100644 drivers/net/ub/unic/unic_tx.h create mode 100644 drivers/net/ub/unic/unic_txrx.c create mode 100644 drivers/net/ub/unic/unic_txrx.h diff --git a/drivers/net/ub/unic/Makefile b/drivers/net/ub/unic/Makefile index 3326a3aa7aae..04fc77f05810 100644 --- a/drivers/net/ub/unic/Makefile +++ b/drivers/net/ub/unic/Makefile @@ -8,5 +8,5 @@ ccflags-y += -I$(srctree)/drivers/net/ub/unic/debugfs obj-$(CONFIG_UB_UNIC) += unic.o unic-objs = unic_main.o unic_ethtool.o unic_hw.o unic_guid.o unic_netdev.o unic_dev.o unic_qos_hw.o unic_event.o unic_crq.o -unic-objs += debugfs/unic_debugfs.o +unic-objs += debugfs/unic_debugfs.o unic_rx.o unic_tx.o unic_txrx.o unic-$(CONFIG_UB_UNIC_DCB) += unic_dcbnl.o diff --git a/drivers/net/ub/unic/debugfs/unic_debugfs.c b/drivers/net/ub/unic/debugfs/unic_debugfs.c index 2f0adcff39c9..15f6a2976b8f 100644 --- a/drivers/net/ub/unic/debugfs/unic_debugfs.c +++ b/drivers/net/ub/unic/debugfs/unic_debugfs.c @@ -81,6 +81,49 @@ static int unic_dbg_dump_caps_info(struct seq_file *s, void *data) return 0; } +static void unic_dump_page_pool_info(struct page_pool *page_pool, + struct seq_file *s, u32 index) +{ +#define UNIC_SIZE_1K 1024 + + seq_printf(s, "%-10u", index); + seq_printf(s, "%-14u", READ_ONCE(page_pool->pages_state_hold_cnt)); + seq_printf(s, "%-14d", atomic_read(&page_pool->pages_state_release_cnt)); + seq_printf(s, "%-21u", page_pool->p.pool_size); + seq_printf(s, "%-7u", page_pool->p.order); + seq_printf(s, "%-9d", page_pool->p.nid); + seq_printf(s, "%uK\n", page_pool->p.max_len / UNIC_SIZE_1K); +} + +static int unic_dbg_dump_page_pool_info(struct seq_file *s, void *data) +{ + struct unic_dev *unic_dev = dev_get_drvdata(s->private); + struct unic_rq *rq; + int ret = 0; + u32 i; + + seq_puts(s, "QUEUE_ID ALLOCATE_CNT FREE_CNT POOL_SIZE(PAGE_NUM) "); + seq_puts(s, "ORDER NUMA_ID MAX_LEN\n"); + + if (!mutex_trylock(&unic_dev->channels.mutex)) + return -EBUSY; + + if (__unic_resetting(unic_dev) || + !unic_dev->channels.c) { + ret = -EBUSY; + goto out; + } + + for (i = 0; i < unic_dev->channels.num; i++) { + rq = unic_dev->channels.c[i].rq; + unic_dump_page_pool_info(rq->page_pool, s, i); + } + +out: + mutex_unlock(&unic_dev->channels.mutex); + return ret; +} + static bool unic_dbg_dentry_support(struct device *dev, u32 property) { struct unic_dev *unic_dev = dev_get_drvdata(dev); @@ -112,7 +155,14 @@ static struct ubase_dbg_cmd_info unic_dbg_cmd[] = { .support = unic_dbg_dentry_support, .init = ubase_dbg_seq_file_init, .read_func = unic_dbg_dump_caps_info, - }, + }, { + .name = "page_pool_info", + .dentry_index = UNIC_DBG_DENTRY_ROOT, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_page_pool_info, + } }; int unic_dbg_init(struct auxiliary_device *adev) diff --git a/drivers/net/ub/unic/unic_dev.c b/drivers/net/ub/unic/unic_dev.c index a1724b269817..09d55c44900c 100644 --- a/drivers/net/ub/unic/unic_dev.c +++ b/drivers/net/ub/unic/unic_dev.c @@ -61,6 +61,13 @@ u32 unic_channels_max_num(struct auxiliary_device *adev) return min(min(jfs_num, jfr_num), jfc_num >> 1); } +u32 unic_get_max_rss_size(struct unic_dev *unic_dev) +{ + u8 vl_num = unic_dev->channels.rss_vl_num; + + return unic_channels_max_num(unic_dev->comdev.adev) / vl_num; +} + void unic_update_queue_info(struct unic_dev *unic_dev) { struct auxiliary_device *adev = unic_dev->comdev.adev; @@ -285,6 +292,196 @@ static void unic_uninit_channels_attr(struct unic_dev *unic_dev) mutex_destroy(&channels->mutex); } +int unic_init_tx(struct unic_dev *unic_dev, u32 num) +{ + struct unic_channel *c; + u32 i, j; + int ret; + + for (i = 0; i < num; i++) { + ret = unic_create_cq(unic_dev, i, UNIC_CQ_SQ); + if (ret) { + dev_err(unic_dev->comdev.adev->dev.parent, + "failed to init tx cq(%u), ret=%d.\n", i, ret); + goto err_create_cq; + } + } + + for (j = 0; j < num; j++) { + ret = unic_create_sq(unic_dev, j); + if (ret) { + dev_err(unic_dev->comdev.adev->dev.parent, + "failed to init tx sq(%u), ret=%d.\n", j, ret); + goto err_create_sq; + } + c = &unic_dev->channels.c[j]; + c->sq->cq = c->sq_cq; + } + + set_bit(UNIC_TX_INITED, &unic_dev->channels.state); + + return 0; + +err_create_sq: + unic_destroy_sq(unic_dev, j); +err_create_cq: + unic_destroy_cq(unic_dev, i, UNIC_CQ_SQ); + return ret; +} + +int unic_init_rx(struct unic_dev *unic_dev, u32 num) +{ + struct unic_channel *c; + u32 i, j; + int ret; + + for (i = 0; i < num; i++) { + ret = unic_create_cq(unic_dev, i, UNIC_CQ_RQ); + if (ret) { + dev_err(unic_dev->comdev.adev->dev.parent, + "failed to init rx cq(%u), ret=%d.\n", i, ret); + goto err_create_cq; + } + } + + for (j = 0; j < num; j++) { + ret = unic_create_rq(unic_dev, j); + if (ret) { + dev_err(unic_dev->comdev.adev->dev.parent, + "failed to init rx rq(%u), ret=%d.\n", j, ret); + goto err_create_rq; + } + c = &unic_dev->channels.c[j]; + c->rq->cq = c->rq_cq; + } + + set_bit(UNIC_RX_INITED, &unic_dev->channels.state); + + return 0; + +err_create_rq: + unic_destroy_rq(unic_dev, j); +err_create_cq: + unic_destroy_cq(unic_dev, i, UNIC_CQ_RQ); + return ret; +} + +static int unic_init_jetty(struct unic_dev *unic_dev, u32 num) +{ + int ret; + + ret = unic_init_rx(unic_dev, num); + if (ret) + return ret; + + ret = unic_init_tx(unic_dev, num); + if (ret) + unic_destroy_rx(unic_dev, num); + + return ret; +} + +void unic_destroy_tx(struct unic_dev *unic_dev, u32 num) +{ + if (!test_and_clear_bit(UNIC_TX_INITED, &unic_dev->channels.state)) + return; + + unic_destroy_sq(unic_dev, num); + unic_destroy_cq(unic_dev, num, UNIC_CQ_SQ); +} + +void unic_destroy_rx(struct unic_dev *unic_dev, u32 num) +{ + if (!test_and_clear_bit(UNIC_RX_INITED, &unic_dev->channels.state)) + return; + + unic_destroy_rq(unic_dev, num); + unic_destroy_cq(unic_dev, num, UNIC_CQ_RQ); +} + +static void unic_destroy_jetty(struct unic_dev *unic_dev, u32 num) +{ + unic_destroy_tx(unic_dev, num); + unic_destroy_rx(unic_dev, num); +} + +static int __unic_init_channels(struct unic_dev *unic_dev, u32 channels_num) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct unic_channels *channels = &unic_dev->channels; + int ret; + u32 i; + + channels->num = channels_num; + channels->c = kcalloc(channels_num, sizeof(*channels->c), GFP_KERNEL); + if (ZERO_OR_NULL_PTR(channels->c)) + return -ENOMEM; + + ret = unic_init_jetty(unic_dev, channels->num); + if (ret) { + dev_err(adev->dev.parent, + "failed to init jetty, ret = %d.\n", ret); + goto err_init_jetty; + } + + for (i = 0; i < channels->num; i++) + netif_napi_add(unic_dev->comdev.netdev, &channels->c[i].napi, + unic_napi_poll); + + return 0; + +err_init_jetty: + kfree(channels->c); + channels->c = NULL; + + return ret; +} + +int unic_init_channels(struct unic_dev *unic_dev, u32 channels_num) +{ + struct unic_channels *channels = &unic_dev->channels; + int ret; + + mutex_lock(&channels->mutex); + + ret = __unic_init_channels(unic_dev, channels_num); + if (!ret) + clear_bit(UNIC_STATE_CHANNEL_INVALID, &unic_dev->state); + + mutex_unlock(&channels->mutex); + return ret; +} + +static void __unic_uninit_channels(struct unic_dev *unic_dev) +{ + struct unic_channels *channels = &unic_dev->channels; + u32 i; + + if (!channels->c) + return; + + for (i = 0; i < channels->num; i++) + netif_napi_del(&channels->c[i].napi); + + unic_destroy_jetty(unic_dev, channels->num); + + kfree(channels->c); + + channels->c = NULL; +} + +void unic_uninit_channels(struct unic_dev *unic_dev) +{ + struct unic_channels *channels = &unic_dev->channels; + + mutex_lock(&channels->mutex); + + set_bit(UNIC_STATE_CHANNEL_INVALID, &unic_dev->state); + __unic_uninit_channels(unic_dev); + + mutex_unlock(&channels->mutex); +} + static void unic_set_netdev_attr(struct net_device *netdev) { struct unic_dev *unic_dev = netdev_priv(netdev); @@ -544,9 +741,18 @@ static int unic_init_netdev_priv(struct net_device *netdev, if (ret) goto err_uninit_vport; + ret = unic_init_channels(priv, priv->channels.num); + if (ret) { + dev_err(adev->dev.parent, + "failed to init channels, ret = %d.\n", ret); + goto err_uninit_channels_attr; + } + set_bit(UNIC_STATE_DOWN, &priv->state); return 0; +err_uninit_channels_attr: + unic_uninit_channels_attr(priv); err_uninit_vport: unic_uninit_vport(priv); destroy_lock: @@ -559,6 +765,7 @@ static void unic_uninit_netdev_priv(struct net_device *netdev) { struct unic_dev *priv = netdev_priv(netdev); + unic_uninit_channels(priv); unic_uninit_channels_attr(priv); unic_uninit_vport(priv); mutex_destroy(&priv->act_info.mutex); diff --git a/drivers/net/ub/unic/unic_dev.h b/drivers/net/ub/unic/unic_dev.h index 3c41471fa534..6469d8c72b54 100644 --- a/drivers/net/ub/unic/unic_dev.h +++ b/drivers/net/ub/unic/unic_dev.h @@ -17,6 +17,9 @@ #include "unic.h" #include "unic_cmd.h" #include "unic_ethtool.h" +#include "unic_rx.h" +#include "unic_tx.h" +#include "unic_txrx.h" #define UNIC_MOD_VERSION "1.0" #define UNIC_AE_LEVEL_NUM 1 @@ -30,6 +33,9 @@ enum unic_dev_state { UNIC_STATE_DISABLED, UNIC_STATE_LINK_UPDATING, UNIC_STATE_TESTING, + UNIC_STATE_FEC_STATS_UPDATING, + UNIC_STATE_MAC_STATS_UPDATING, + UNIC_STATE_CHANNEL_INVALID, UNIC_STATE_DEACTIVATE, }; @@ -40,6 +46,23 @@ enum unic_vport_state { UNIC_VPORT_STATE_IP_QUERYING, }; +enum unic_channel_state { + UNIC_TX_CHANGED, + UNIC_RX_CHANGED, + UNIC_TX_INITED, + UNIC_RX_INITED, +}; + +#define UNIC_CQE_PERIOD_0 0 +#define UNIC_CQE_PERIOD_4 4 +#define UNIC_CQE_PERIOD_16 16 +#define UNIC_CQE_PERIOD_64 64 +#define UNIC_CQE_PERIOD_256 256 +#define UNIC_CQE_PERIOD_1024 1024 +#define UNIC_CQE_PERIOD_4096 4096 +#define UNIC_CQE_PERIOD_16384 16384 +#define UNIC_CQE_PERIOD_ERR 16385 + #define UNIC_DEFAULT_CHANNEL_NUM 1 #define unic_dbg(_unic_dev, fmt, ...) \ @@ -199,10 +222,16 @@ struct unic_dev { int unic_dev_init(struct auxiliary_device *adev); void unic_dev_uninit(struct auxiliary_device *adev); u32 unic_channels_max_num(struct auxiliary_device *adev); +int unic_init_channels(struct unic_dev *unic_dev, u32 channels_num); +void unic_uninit_channels(struct unic_dev *unic_dev); void unic_start_period_task(struct net_device *netdev); void unic_remove_period_task(struct unic_dev *unic_dev); int unic_init_wq(void); void unic_destroy_wq(void); +int unic_init_rx(struct unic_dev *unic_dev, u32 num); +int unic_init_tx(struct unic_dev *unic_dev, u32 num); +void unic_destroy_rx(struct unic_dev *unic_dev, u32 num); +void unic_destroy_tx(struct unic_dev *unic_dev, u32 num); int unic_set_vl_map(struct unic_dev *unic_dev, u8 *dscp_prio, u8 *prio_vl, u8 map_type); int unic_dbg_log(void); @@ -267,6 +296,26 @@ static inline bool unic_is_initing_or_resetting(struct unic_dev *unic_dev) return __unic_resetting(unic_dev) || __unic_initing(unic_dev); } +static inline u32 unic_get_sq_cqe_mask(struct unic_dev *unic_dev) +{ + return unic_dev->channels.sq_cqe_depth - 1; +} + +static inline u32 unic_get_rq_cqe_mask(struct unic_dev *unic_dev) +{ + return unic_dev->channels.rq_cqe_depth - 1; +} + +static inline bool unic_tx_changed(struct unic_dev *unic_dev) +{ + return test_bit(UNIC_TX_CHANGED, &unic_dev->channels.state); +} + +static inline bool unic_rx_changed(struct unic_dev *unic_dev) +{ + return test_bit(UNIC_RX_CHANGED, &unic_dev->channels.state); +} + static inline u32 unic_cmd_timeout(struct unic_dev *unic_dev) { #define UNIC_CMD_TIMEOUT 5000 diff --git a/drivers/net/ub/unic/unic_event.c b/drivers/net/ub/unic/unic_event.c index a1cd5e4cafd4..9a38d1763285 100644 --- a/drivers/net/ub/unic/unic_event.c +++ b/drivers/net/ub/unic/unic_event.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "unic_cmd.h" #include "unic_crq.h" @@ -23,6 +24,33 @@ #include "unic_qos_hw.h" #include "unic_event.h" +int unic_comp_handler(struct notifier_block *nb, unsigned long jfcn, void *data) +{ + struct auxiliary_device *adev = (struct auxiliary_device *)data; + struct unic_dev *unic_dev = dev_get_drvdata(&adev->dev); + struct unic_channels *channels = &unic_dev->channels; + struct unic_cq *cq; + u32 index; + + if (test_bit(UNIC_STATE_CHANNEL_INVALID, &unic_dev->state)) + return -EBUSY; + + index = jfcn < channels->num ? jfcn : jfcn - channels->num; + if (index >= channels->num) + return -EINVAL; + + if (jfcn > channels->num) + cq = channels->c[index].rq->cq; + else + cq = channels->c[index].sq->cq; + + cq->event_cnt++; + + napi_schedule(&channels->c[index].napi); + + return 0; +} + static struct ubase_crq_event_nb unic_crq_events[] = { { .opcode = UBASE_OPC_QUERY_LINK_STATUS, diff --git a/drivers/net/ub/unic/unic_event.h b/drivers/net/ub/unic/unic_event.h index 258b801f9b08..cc863c3b60df 100644 --- a/drivers/net/ub/unic/unic_event.h +++ b/drivers/net/ub/unic/unic_event.h @@ -11,5 +11,6 @@ int unic_register_event(struct auxiliary_device *adev); void unic_unregister_event(struct auxiliary_device *adev); +int unic_comp_handler(struct notifier_block *nb, unsigned long jfcn, void *data); #endif diff --git a/drivers/net/ub/unic/unic_hw.h b/drivers/net/ub/unic/unic_hw.h index 70320bae49c0..c72a9d60d132 100644 --- a/drivers/net/ub/unic/unic_hw.h +++ b/drivers/net/ub/unic/unic_hw.h @@ -46,6 +46,11 @@ struct unic_promisc_en { u8 en_mc; }; +static inline bool unic_is_port_down(struct unic_dev *unic_dev) +{ + return unic_dev->hw.mac.link_status == UNIC_LINK_STATUS_DOWN; +} + int unic_update_port_info(struct unic_dev *unic_dev); int unic_set_mac_speed_duplex(struct unic_dev *unic_dev, u32 speed, u8 duplex, diff --git a/drivers/net/ub/unic/unic_netdev.c b/drivers/net/ub/unic/unic_netdev.c index 285b19738a4a..ac31dd4fe26e 100644 --- a/drivers/net/ub/unic/unic_netdev.c +++ b/drivers/net/ub/unic/unic_netdev.c @@ -18,20 +18,145 @@ #include #include #include +#include #include "unic_cmd.h" #include "unic_dev.h" +#include "unic_event.h" #include "unic_hw.h" +#include "unic_rx.h" +#include "unic_tx.h" +#include "unic_txrx.h" #include "unic_netdev.h" +static int unic_netdev_set_tcs(struct net_device *netdev) +{ +#define UNIC_OS_DEFAULT_VL 0 + + struct unic_dev *unic_dev = netdev_priv(netdev); + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct unic_channels *channels = &unic_dev->channels; + struct ubase_caps *caps = ubase_get_dev_caps(adev); + struct unic_vl *vl = &channels->vl; + int ret; + u8 i; + + if (!caps) { + unic_err(unic_dev, "failed to get caps info.\n"); + return -ENODATA; + } + + netdev_reset_tc(netdev); + + if (caps->vl_num <= 1) + return 0; + + ret = netdev_set_num_tc(netdev, channels->rss_vl_num); + if (ret) { + unic_err(unic_dev, "failed to set num_tc, ret = %d.\n", ret); + return ret; + } + + for (i = 0; i < channels->rss_vl_num; i++) + netdev_set_tc_queue(netdev, i, vl->queue_count[i], + vl->queue_offset[i]); + + for (i = 0; i < UNIC_MAX_PRIO_NUM; i++) { + if (vl->prio_vl[i] > channels->rss_vl_num) + netdev_set_prio_tc_map(netdev, i, UNIC_OS_DEFAULT_VL); + else + netdev_set_prio_tc_map(netdev, i, vl->prio_vl[i]); + } + + return 0; +} + +static int unic_real_num_queue_set(struct net_device *netdev) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + u32 queue_size = unic_dev->channels.num; + int ret; + + if (!queue_size) + queue_size = UNIC_DEFAULT_CHANNEL_NUM; + + ret = unic_netdev_set_tcs(netdev); + if (ret) + return ret; + + ret = netif_set_real_num_tx_queues(netdev, queue_size); + if (ret) { + unic_err(unic_dev, + "failed to set real num tx queues, ret = %d!\n", ret); + return ret; + } + + ret = netif_set_real_num_rx_queues(netdev, queue_size); + if (ret) { + unic_err(unic_dev, + "failed to set real num rx queues, ret = %d!\n", ret); + return ret; + } + + return 0; +} + +void unic_enable_channels(struct unic_dev *unic_dev) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct unic_channel *c; + u32 i; + + mutex_lock(&unic_dev->channels.mutex); + if (!unic_dev->channels.c) + goto out; + + for (i = 0; i < unic_dev->channels.num; i++) { + c = &unic_dev->channels.c[i]; + napi_enable(&c->napi); + } + + ubase_comp_register(adev, unic_comp_handler); + +out: + mutex_unlock(&unic_dev->channels.mutex); +} + +void unic_disable_channels(struct unic_dev *unic_dev) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct unic_channel *c; + u32 i; + + mutex_lock(&unic_dev->channels.mutex); + if (!unic_dev->channels.c) + goto out; + + ubase_comp_unregister(adev); + + for (i = 0; i < unic_dev->channels.num; i++) { + c = &unic_dev->channels.c[i]; + napi_disable(&c->napi); + } + +out: + mutex_unlock(&unic_dev->channels.mutex); +} + static int unic_net_up(struct net_device *netdev) { struct unic_dev *unic_dev = netdev_priv(netdev); int ret; + unic_clear_all_queue(netdev); + + unic_enable_channels(unic_dev); + ret = unic_mac_mode_cfg(netdev, true); - if (ret) + if (ret) { + unic_disable_channels(unic_dev); return ret; + } unic_dev->sw_link_status = UNIC_LINK_STATUS_DOWN; @@ -47,10 +172,13 @@ void unic_link_status_change(struct net_device *netdev, bool linkup) goto out; if (linkup) { - if (!test_bit(UNIC_STATE_TESTING, &unic_dev->state)) + if (!test_bit(UNIC_STATE_TESTING, &unic_dev->state)) { + netif_tx_wake_all_queues(netdev); netif_carrier_on(netdev); + } } else { netif_carrier_off(netdev); + netif_tx_stop_all_queues(netdev); } out: @@ -116,6 +244,13 @@ int unic_net_open(struct net_device *netdev) return 0; } + ret = unic_real_num_queue_set(netdev); + if (ret) { + unic_err(unic_dev, "failed to set real num queue, ret = %d.\n", + ret); + return ret; + } + ret = unic_net_up(netdev); if (ret) { unic_err(unic_dev, "failed to up net, ret = %d.\n", ret); @@ -133,6 +268,7 @@ int unic_net_open(struct net_device *netdev) int unic_net_open_no_link_change(struct net_device *netdev) { struct unic_dev *unic_dev = netdev_priv(netdev); + int ret; if (unic_resetting(netdev)) return -EBUSY; @@ -142,14 +278,26 @@ int unic_net_open_no_link_change(struct net_device *netdev) netif_carrier_off(netdev); + ret = unic_real_num_queue_set(netdev); + if (ret) { + unic_err(unic_dev, "failed to set real num queue, ret = %d.\n", + ret); + return ret; + } + + unic_clear_all_queue(netdev); + unic_enable_channels(unic_dev); + if (netif_msg_ifup(unic_dev)) unic_info(unic_dev, "netif open.\n"); /* ensure that network interface is awakened when linked up, * otherwise, it will be handled by periodic tasks */ - if (unic_dev->sw_link_status == UNIC_LINK_STATUS_UP) + if (unic_dev->sw_link_status == UNIC_LINK_STATUS_UP) { + netif_tx_wake_all_queues(netdev); netif_carrier_on(netdev); + } return 0; } @@ -175,6 +323,11 @@ void unic_net_stop_no_link_change(struct net_device *netdev) netif_carrier_off(netdev); netif_tx_disable(netdev); + netif_tx_stop_all_queues(netdev); + + unic_disable_channels(unic_dev); + unic_clear_all_queue(netdev); + unic_reset_tx_queue(netdev); } int unic_net_stop(struct net_device *netdev) @@ -198,13 +351,178 @@ int unic_net_stop(struct net_device *netdev) netif_carrier_off(netdev); netif_tx_disable(netdev); unic_dev_stop(netdev); + unic_disable_channels(unic_dev); + unic_clear_all_queue(netdev); + unic_reset_tx_queue(netdev); return 0; } +static void unic_fetch_stats_tx(struct rtnl_link_stats64 *stats, + struct unic_channel *channel) +{ + unsigned int start; + + do { + start = u64_stats_fetch_begin(&channel->sq->syncp); + + stats->tx_bytes += channel->sq->stats.bytes; + stats->tx_packets += channel->sq->stats.packets; + stats->tx_errors += channel->sq->stats.pad_err; + stats->tx_errors += channel->sq->stats.map_err; + stats->tx_errors += channel->sq->stats.over_max_sge_num; + stats->tx_errors += channel->sq->stats.csum_err; + stats->tx_errors += channel->sq->stats.vlan_err; + stats->tx_errors += channel->sq->stats.fd_cnt; + stats->tx_errors += channel->sq->stats.drop_cnt; + stats->tx_errors += channel->sq->stats.cfg5_drop_cnt; + + stats->tx_dropped += channel->sq->stats.pad_err; + stats->tx_dropped += channel->sq->stats.map_err; + stats->tx_dropped += channel->sq->stats.over_max_sge_num; + stats->tx_dropped += channel->sq->stats.csum_err; + stats->tx_dropped += channel->sq->stats.vlan_err; + stats->tx_dropped += channel->sq->stats.fd_cnt; + stats->tx_dropped += channel->sq->stats.drop_cnt; + stats->tx_dropped += channel->sq->stats.cfg5_drop_cnt; + } while (u64_stats_fetch_retry(&channel->sq->syncp, start)); +} + +static void unic_fetch_stats_rx(struct rtnl_link_stats64 *stats, + struct unic_channel *channel) +{ + unsigned int start; + + do { + start = u64_stats_fetch_begin(&channel->rq->syncp); + + stats->rx_bytes += channel->rq->stats.bytes; + stats->rx_packets += channel->rq->stats.packets; + stats->rx_crc_errors += channel->rq->stats.l2_err; + stats->rx_errors += channel->rq->stats.l3_l4_csum_err; + stats->rx_errors += channel->rq->stats.l2_err; + stats->rx_length_errors += channel->rq->stats.err_pkt_len_cnt; + + stats->rx_dropped += channel->rq->stats.alloc_skb_err; + stats->rx_dropped += channel->rq->stats.l2_err; + stats->rx_dropped += channel->rq->stats.err_pkt_len_cnt; + stats->rx_dropped += channel->rq->stats.trunc_cnt; + stats->rx_dropped += channel->rq->stats.doi_cnt; + stats->multicast += channel->rq->stats.multicast; + } while (u64_stats_fetch_retry(&channel->rq->syncp, start)); +} + +static void unic_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *stats) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + u32 channel_num = unic_dev->channels.num; + struct rtnl_link_stats64 total_stats; + struct unic_channel *channel; + u32 i; + + if (test_bit(UNIC_STATE_DOWN, &unic_dev->state)) + return; + + mutex_lock(&unic_dev->channels.mutex); + if (!unic_dev->channels.c) { + mutex_unlock(&unic_dev->channels.mutex); + return; + } + + memset(&total_stats, 0, sizeof(total_stats)); + for (i = 0; i < channel_num; i++) { + channel = &unic_dev->channels.c[i]; + unic_fetch_stats_tx(&total_stats, channel); + unic_fetch_stats_rx(&total_stats, channel); + } + + mutex_unlock(&unic_dev->channels.mutex); + + stats->tx_bytes = total_stats.tx_bytes; + stats->tx_packets = total_stats.tx_packets; + stats->tx_errors = total_stats.tx_errors; + stats->tx_dropped = total_stats.tx_dropped; + stats->tx_fifo_errors = netdev->stats.tx_fifo_errors; + stats->tx_compressed = netdev->stats.tx_compressed; + stats->tx_aborted_errors = netdev->stats.tx_aborted_errors; + stats->tx_carrier_errors = netdev->stats.tx_carrier_errors; + stats->tx_heartbeat_errors = netdev->stats.tx_heartbeat_errors; + stats->tx_window_errors = netdev->stats.tx_window_errors; + stats->collisions = netdev->stats.collisions; + + stats->rx_bytes = total_stats.rx_bytes; + stats->rx_packets = total_stats.rx_packets; + stats->rx_errors = total_stats.rx_errors; + stats->rx_dropped = total_stats.rx_dropped; + stats->rx_length_errors = total_stats.rx_length_errors; + stats->rx_crc_errors = total_stats.rx_crc_errors; + stats->multicast = total_stats.multicast; + stats->rx_missed_errors = netdev->stats.rx_missed_errors; + stats->rx_over_errors = netdev->stats.rx_over_errors; + stats->rx_frame_errors = netdev->stats.rx_frame_errors; + stats->rx_fifo_errors = netdev->stats.rx_fifo_errors; + stats->rx_compressed = netdev->stats.rx_compressed; +} + +static void unic_tx_timeout(struct net_device *netdev, u32 queue_idx) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + + unic_dump_sq_stats(netdev, queue_idx); + + ubase_reset_event(unic_dev->comdev.adev, UBASE_UE_RESET); +} + +static u8 unic_get_skb_dscp(struct sk_buff *skb) +{ +#define UNIC_INVALID_DSCP 0xff +#define UNIC_DSCP_SHIFT 2 + + __be16 protocol = skb->protocol; + u8 dscp = UNIC_INVALID_DSCP; + + if (protocol == htons(ETH_P_8021Q)) + protocol = vlan_get_protocol(skb); + + if (protocol == htons(ETH_P_IP)) + dscp = ipv4_get_dsfield(ip_hdr(skb)) >> UNIC_DSCP_SHIFT; + else if (protocol == htons(ETH_P_IPV6)) + dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> UNIC_DSCP_SHIFT; + + return dscp; +} + +static u16 unic_select_queue(struct net_device *netdev, struct sk_buff *skb, + struct net_device *sb_dev) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + struct unic_vl *vl = &unic_dev->channels.vl; + u8 dscp; + + if (!vl->dscp_app_cnt) + goto out; + + dscp = unic_get_skb_dscp(skb); + if (unlikely(dscp >= UBASE_MAX_DSCP)) + goto out; + + if (vl->dscp_prio[dscp] == UNIC_INVALID_PRIORITY) + skb->priority = 0; + else + skb->priority = vl->dscp_prio[dscp]; + +out: + return netdev_pick_tx(netdev, skb, sb_dev); +} + static const struct net_device_ops unic_netdev_ops = { + .ndo_get_stats64 = unic_get_stats64, + .ndo_start_xmit = unic_start_xmit, + .ndo_tx_timeout = unic_tx_timeout, .ndo_open = unic_net_open, .ndo_stop = unic_net_stop, + .ndo_select_queue = unic_select_queue, }; void unic_set_netdev_ops(struct net_device *netdev) diff --git a/drivers/net/ub/unic/unic_rx.c b/drivers/net/ub/unic/unic_rx.c new file mode 100644 index 000000000000..34095fbac40d --- /dev/null +++ b/drivers/net/ub/unic/unic_rx.c @@ -0,0 +1,1220 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#define dev_fmt(fmt) "unic: (pid %d) " fmt, current->pid + +#include +#include +#include +#ifdef CONFIG_UB_UNIC_UBL +#include +#endif +#include + +#include "unic_dev.h" +#include "unic_trace.h" +#include "unic_rx.h" + +#define UNIC_RX_HEAD_SIZE 256 +#define UNIC_RQ_CI_REVERSE (USHRT_MAX + 1) + +#define UNIC_RX_PTYPE_ENTRY(ptype, csum_level, ip_summed, l3type, hash_type) \ + { ptype, csum_level, CHECKSUM_##ip_summed, UNIC_L3_TYPE_##l3type, \ + 1, hash_type, 0 } + +#define UNIC_RX_PTYPE_UNUSED_ENTRY(ptype) \ + { ptype, 0, CHECKSUM_NONE, UNIC_L3_TYPE_RESV, \ + 0, PKT_HASH_TYPE_NONE, 0 } + +static const struct unic_rx_ptype unic_rx_ptype_tbl[] = { + UNIC_RX_PTYPE_UNUSED_ENTRY(0), + UNIC_RX_PTYPE_ENTRY(1, 0, NONE, ARP, PKT_HASH_TYPE_NONE), + UNIC_RX_PTYPE_ENTRY(2, 0, NONE, RARP, PKT_HASH_TYPE_NONE), + UNIC_RX_PTYPE_ENTRY(3, 0, NONE, LLDP, PKT_HASH_TYPE_NONE), + UNIC_RX_PTYPE_ENTRY(4, 0, NONE, STP, PKT_HASH_TYPE_NONE), + UNIC_RX_PTYPE_ENTRY(5, 0, NONE, MAC_PAUSE, PKT_HASH_TYPE_NONE), + UNIC_RX_PTYPE_ENTRY(6, 0, NONE, PFC_PAUSE, PKT_HASH_TYPE_NONE), + UNIC_RX_PTYPE_ENTRY(7, 0, NONE, CNM, PKT_HASH_TYPE_NONE), + UNIC_RX_PTYPE_ENTRY(8, 0, NONE, PTP, PKT_HASH_TYPE_NONE), + UNIC_RX_PTYPE_UNUSED_ENTRY(9), + UNIC_RX_PTYPE_UNUSED_ENTRY(10), + UNIC_RX_PTYPE_UNUSED_ENTRY(11), + UNIC_RX_PTYPE_UNUSED_ENTRY(12), + UNIC_RX_PTYPE_UNUSED_ENTRY(13), + UNIC_RX_PTYPE_UNUSED_ENTRY(14), + UNIC_RX_PTYPE_UNUSED_ENTRY(15), + UNIC_RX_PTYPE_UNUSED_ENTRY(16), + UNIC_RX_PTYPE_ENTRY(17, 0, NONE, IPV4, PKT_HASH_TYPE_NONE), + UNIC_RX_PTYPE_ENTRY(18, 0, NONE, IPV4, PKT_HASH_TYPE_NONE), + UNIC_RX_PTYPE_ENTRY(19, 0, UNNECESSARY, IPV4, PKT_HASH_TYPE_L4), + UNIC_RX_PTYPE_ENTRY(20, 0, UNNECESSARY, IPV4, PKT_HASH_TYPE_L4), + UNIC_RX_PTYPE_ENTRY(21, 0, NONE, IPV4, PKT_HASH_TYPE_NONE), + UNIC_RX_PTYPE_ENTRY(22, 0, NONE, IPV4, PKT_HASH_TYPE_L4), + UNIC_RX_PTYPE_ENTRY(23, 0, NONE, IPV4, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_ENTRY(24, 0, NONE, IPV4, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_ENTRY(25, 0, NONE, IPV4, PKT_HASH_TYPE_L4), + UNIC_RX_PTYPE_UNUSED_ENTRY(26), + UNIC_RX_PTYPE_UNUSED_ENTRY(27), + UNIC_RX_PTYPE_UNUSED_ENTRY(28), + UNIC_RX_PTYPE_ENTRY(29, 0, NONE, IPV4, PKT_HASH_TYPE_NONE), + UNIC_RX_PTYPE_ENTRY(30, 0, NONE, IPV4, PKT_HASH_TYPE_NONE), + UNIC_RX_PTYPE_ENTRY(31, 0, NONE, IPV4, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_ENTRY(32, 0, NONE, IPV4, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_ENTRY(33, 0, UNNECESSARY, IPV4, PKT_HASH_TYPE_L4), + UNIC_RX_PTYPE_ENTRY(34, 0, UNNECESSARY, IPV4, PKT_HASH_TYPE_L4), + UNIC_RX_PTYPE_ENTRY(35, 0, NONE, IPV4, PKT_HASH_TYPE_L4), + UNIC_RX_PTYPE_ENTRY(36, 0, NONE, IPV4, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_ENTRY(37, 0, NONE, IPV4, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_UNUSED_ENTRY(38), + UNIC_RX_PTYPE_ENTRY(39, 0, NONE, IPV4, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_ENTRY(40, 0, NONE, IPV4, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_ENTRY(41, 0, UNNECESSARY, IPV4, PKT_HASH_TYPE_L4), + UNIC_RX_PTYPE_ENTRY(42, 0, UNNECESSARY, IPV4, PKT_HASH_TYPE_L4), + UNIC_RX_PTYPE_ENTRY(43, 0, NONE, IPV4, PKT_HASH_TYPE_L4), + UNIC_RX_PTYPE_ENTRY(44, 0, NONE, IPV4, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_ENTRY(45, 0, NONE, IPV4, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_UNUSED_ENTRY(46), + UNIC_RX_PTYPE_UNUSED_ENTRY(47), + UNIC_RX_PTYPE_UNUSED_ENTRY(48), + UNIC_RX_PTYPE_UNUSED_ENTRY(49), + UNIC_RX_PTYPE_UNUSED_ENTRY(50), + UNIC_RX_PTYPE_UNUSED_ENTRY(51), + UNIC_RX_PTYPE_UNUSED_ENTRY(52), + UNIC_RX_PTYPE_UNUSED_ENTRY(53), + UNIC_RX_PTYPE_UNUSED_ENTRY(54), + UNIC_RX_PTYPE_UNUSED_ENTRY(55), + UNIC_RX_PTYPE_UNUSED_ENTRY(56), + UNIC_RX_PTYPE_UNUSED_ENTRY(57), + UNIC_RX_PTYPE_UNUSED_ENTRY(58), + UNIC_RX_PTYPE_UNUSED_ENTRY(59), + UNIC_RX_PTYPE_UNUSED_ENTRY(60), + UNIC_RX_PTYPE_UNUSED_ENTRY(61), + UNIC_RX_PTYPE_UNUSED_ENTRY(62), + UNIC_RX_PTYPE_UNUSED_ENTRY(63), + UNIC_RX_PTYPE_UNUSED_ENTRY(64), + UNIC_RX_PTYPE_UNUSED_ENTRY(65), + UNIC_RX_PTYPE_UNUSED_ENTRY(66), + UNIC_RX_PTYPE_UNUSED_ENTRY(67), + UNIC_RX_PTYPE_UNUSED_ENTRY(68), + UNIC_RX_PTYPE_UNUSED_ENTRY(69), + UNIC_RX_PTYPE_UNUSED_ENTRY(70), + UNIC_RX_PTYPE_UNUSED_ENTRY(71), + UNIC_RX_PTYPE_UNUSED_ENTRY(72), + UNIC_RX_PTYPE_UNUSED_ENTRY(73), + UNIC_RX_PTYPE_UNUSED_ENTRY(74), + UNIC_RX_PTYPE_UNUSED_ENTRY(75), + UNIC_RX_PTYPE_UNUSED_ENTRY(76), + UNIC_RX_PTYPE_UNUSED_ENTRY(77), + UNIC_RX_PTYPE_UNUSED_ENTRY(78), + UNIC_RX_PTYPE_UNUSED_ENTRY(79), + UNIC_RX_PTYPE_UNUSED_ENTRY(80), + UNIC_RX_PTYPE_UNUSED_ENTRY(81), + UNIC_RX_PTYPE_UNUSED_ENTRY(82), + UNIC_RX_PTYPE_UNUSED_ENTRY(83), + UNIC_RX_PTYPE_UNUSED_ENTRY(84), + UNIC_RX_PTYPE_UNUSED_ENTRY(85), + UNIC_RX_PTYPE_UNUSED_ENTRY(86), + UNIC_RX_PTYPE_UNUSED_ENTRY(87), + UNIC_RX_PTYPE_UNUSED_ENTRY(88), + UNIC_RX_PTYPE_UNUSED_ENTRY(89), + UNIC_RX_PTYPE_UNUSED_ENTRY(90), + UNIC_RX_PTYPE_UNUSED_ENTRY(91), + UNIC_RX_PTYPE_UNUSED_ENTRY(92), + UNIC_RX_PTYPE_UNUSED_ENTRY(93), + UNIC_RX_PTYPE_UNUSED_ENTRY(94), + UNIC_RX_PTYPE_UNUSED_ENTRY(95), + UNIC_RX_PTYPE_UNUSED_ENTRY(96), + UNIC_RX_PTYPE_UNUSED_ENTRY(97), + UNIC_RX_PTYPE_UNUSED_ENTRY(98), + UNIC_RX_PTYPE_UNUSED_ENTRY(99), + UNIC_RX_PTYPE_UNUSED_ENTRY(100), + UNIC_RX_PTYPE_UNUSED_ENTRY(101), + UNIC_RX_PTYPE_UNUSED_ENTRY(102), + UNIC_RX_PTYPE_UNUSED_ENTRY(103), + UNIC_RX_PTYPE_UNUSED_ENTRY(104), + UNIC_RX_PTYPE_UNUSED_ENTRY(105), + UNIC_RX_PTYPE_UNUSED_ENTRY(106), + UNIC_RX_PTYPE_UNUSED_ENTRY(107), + UNIC_RX_PTYPE_UNUSED_ENTRY(108), + UNIC_RX_PTYPE_UNUSED_ENTRY(109), + UNIC_RX_PTYPE_UNUSED_ENTRY(110), + UNIC_RX_PTYPE_ENTRY(111, 0, NONE, IPV6, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_ENTRY(112, 0, NONE, IPV6, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_ENTRY(113, 0, UNNECESSARY, IPV6, PKT_HASH_TYPE_L4), + UNIC_RX_PTYPE_ENTRY(114, 0, UNNECESSARY, IPV6, PKT_HASH_TYPE_L4), + UNIC_RX_PTYPE_ENTRY(115, 0, NONE, IPV6, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_ENTRY(116, 0, NONE, IPV6, PKT_HASH_TYPE_L4), + UNIC_RX_PTYPE_ENTRY(117, 0, NONE, IPV6, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_ENTRY(118, 0, NONE, IPV6, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_ENTRY(119, 0, NONE, IPV6, PKT_HASH_TYPE_L4), + UNIC_RX_PTYPE_UNUSED_ENTRY(120), + UNIC_RX_PTYPE_UNUSED_ENTRY(121), + UNIC_RX_PTYPE_UNUSED_ENTRY(122), + UNIC_RX_PTYPE_ENTRY(123, 0, NONE, IPV6, PKT_HASH_TYPE_NONE), + UNIC_RX_PTYPE_ENTRY(124, 0, NONE, IPV6, PKT_HASH_TYPE_NONE), + UNIC_RX_PTYPE_ENTRY(125, 0, NONE, IPV6, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_ENTRY(126, 0, NONE, IPV6, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_ENTRY(127, 0, UNNECESSARY, IPV6, PKT_HASH_TYPE_L4), + UNIC_RX_PTYPE_ENTRY(128, 0, UNNECESSARY, IPV6, PKT_HASH_TYPE_L4), + UNIC_RX_PTYPE_ENTRY(129, 0, NONE, IPV6, PKT_HASH_TYPE_L4), + UNIC_RX_PTYPE_ENTRY(130, 0, NONE, IPV6, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_ENTRY(131, 0, NONE, IPV6, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_UNUSED_ENTRY(132), + UNIC_RX_PTYPE_ENTRY(133, 0, NONE, IPV6, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_ENTRY(134, 0, NONE, IPV6, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_ENTRY(135, 0, UNNECESSARY, IPV6, PKT_HASH_TYPE_L4), + UNIC_RX_PTYPE_ENTRY(136, 0, UNNECESSARY, IPV6, PKT_HASH_TYPE_L4), + UNIC_RX_PTYPE_ENTRY(137, 0, NONE, IPV6, PKT_HASH_TYPE_L4), + UNIC_RX_PTYPE_ENTRY(138, 0, NONE, IPV6, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_ENTRY(139, 0, NONE, IPV6, PKT_HASH_TYPE_L3), + UNIC_RX_PTYPE_UNUSED_ENTRY(140), + UNIC_RX_PTYPE_UNUSED_ENTRY(141), + UNIC_RX_PTYPE_UNUSED_ENTRY(142), + UNIC_RX_PTYPE_UNUSED_ENTRY(143), + UNIC_RX_PTYPE_UNUSED_ENTRY(144), + UNIC_RX_PTYPE_UNUSED_ENTRY(145), + UNIC_RX_PTYPE_UNUSED_ENTRY(146), + UNIC_RX_PTYPE_UNUSED_ENTRY(147), + UNIC_RX_PTYPE_UNUSED_ENTRY(148), + UNIC_RX_PTYPE_UNUSED_ENTRY(149), + UNIC_RX_PTYPE_UNUSED_ENTRY(150), + UNIC_RX_PTYPE_UNUSED_ENTRY(151), + UNIC_RX_PTYPE_UNUSED_ENTRY(152), + UNIC_RX_PTYPE_UNUSED_ENTRY(153), + UNIC_RX_PTYPE_UNUSED_ENTRY(154), + UNIC_RX_PTYPE_UNUSED_ENTRY(155), + UNIC_RX_PTYPE_UNUSED_ENTRY(156), + UNIC_RX_PTYPE_UNUSED_ENTRY(157), + UNIC_RX_PTYPE_UNUSED_ENTRY(158), + UNIC_RX_PTYPE_UNUSED_ENTRY(159), + UNIC_RX_PTYPE_UNUSED_ENTRY(160), + UNIC_RX_PTYPE_UNUSED_ENTRY(161), + UNIC_RX_PTYPE_UNUSED_ENTRY(162), + UNIC_RX_PTYPE_UNUSED_ENTRY(163), + UNIC_RX_PTYPE_UNUSED_ENTRY(164), + UNIC_RX_PTYPE_UNUSED_ENTRY(165), + UNIC_RX_PTYPE_UNUSED_ENTRY(166), + UNIC_RX_PTYPE_UNUSED_ENTRY(167), + UNIC_RX_PTYPE_UNUSED_ENTRY(168), + UNIC_RX_PTYPE_UNUSED_ENTRY(169), + UNIC_RX_PTYPE_UNUSED_ENTRY(170), + UNIC_RX_PTYPE_UNUSED_ENTRY(171), + UNIC_RX_PTYPE_UNUSED_ENTRY(172), + UNIC_RX_PTYPE_UNUSED_ENTRY(173), + UNIC_RX_PTYPE_UNUSED_ENTRY(174), + UNIC_RX_PTYPE_UNUSED_ENTRY(175), + UNIC_RX_PTYPE_UNUSED_ENTRY(176), + UNIC_RX_PTYPE_UNUSED_ENTRY(177), + UNIC_RX_PTYPE_UNUSED_ENTRY(178), + UNIC_RX_PTYPE_UNUSED_ENTRY(179), + UNIC_RX_PTYPE_UNUSED_ENTRY(180), + UNIC_RX_PTYPE_UNUSED_ENTRY(181), + UNIC_RX_PTYPE_UNUSED_ENTRY(182), + UNIC_RX_PTYPE_UNUSED_ENTRY(183), + UNIC_RX_PTYPE_UNUSED_ENTRY(184), + UNIC_RX_PTYPE_UNUSED_ENTRY(185), + UNIC_RX_PTYPE_UNUSED_ENTRY(186), + UNIC_RX_PTYPE_UNUSED_ENTRY(187), + UNIC_RX_PTYPE_UNUSED_ENTRY(188), + UNIC_RX_PTYPE_UNUSED_ENTRY(189), + UNIC_RX_PTYPE_UNUSED_ENTRY(190), + UNIC_RX_PTYPE_UNUSED_ENTRY(191), + UNIC_RX_PTYPE_UNUSED_ENTRY(192), + UNIC_RX_PTYPE_UNUSED_ENTRY(193), + UNIC_RX_PTYPE_UNUSED_ENTRY(194), + UNIC_RX_PTYPE_UNUSED_ENTRY(195), + UNIC_RX_PTYPE_UNUSED_ENTRY(196), + UNIC_RX_PTYPE_UNUSED_ENTRY(197), + UNIC_RX_PTYPE_UNUSED_ENTRY(198), + UNIC_RX_PTYPE_UNUSED_ENTRY(199), + UNIC_RX_PTYPE_UNUSED_ENTRY(200), + UNIC_RX_PTYPE_UNUSED_ENTRY(201), + UNIC_RX_PTYPE_UNUSED_ENTRY(202), + UNIC_RX_PTYPE_UNUSED_ENTRY(203), + UNIC_RX_PTYPE_UNUSED_ENTRY(204), + UNIC_RX_PTYPE_UNUSED_ENTRY(205), + UNIC_RX_PTYPE_UNUSED_ENTRY(206), + UNIC_RX_PTYPE_UNUSED_ENTRY(207), + UNIC_RX_PTYPE_UNUSED_ENTRY(208), + UNIC_RX_PTYPE_UNUSED_ENTRY(209), + UNIC_RX_PTYPE_UNUSED_ENTRY(210), + UNIC_RX_PTYPE_UNUSED_ENTRY(211), + UNIC_RX_PTYPE_UNUSED_ENTRY(212), + UNIC_RX_PTYPE_UNUSED_ENTRY(213), + UNIC_RX_PTYPE_UNUSED_ENTRY(214), + UNIC_RX_PTYPE_UNUSED_ENTRY(215), + UNIC_RX_PTYPE_UNUSED_ENTRY(216), + UNIC_RX_PTYPE_UNUSED_ENTRY(217), + UNIC_RX_PTYPE_UNUSED_ENTRY(218), + UNIC_RX_PTYPE_UNUSED_ENTRY(219), + UNIC_RX_PTYPE_UNUSED_ENTRY(220), + UNIC_RX_PTYPE_UNUSED_ENTRY(221), + UNIC_RX_PTYPE_UNUSED_ENTRY(222), + UNIC_RX_PTYPE_UNUSED_ENTRY(223), + UNIC_RX_PTYPE_UNUSED_ENTRY(224), + UNIC_RX_PTYPE_UNUSED_ENTRY(225), + UNIC_RX_PTYPE_UNUSED_ENTRY(226), + UNIC_RX_PTYPE_UNUSED_ENTRY(227), + UNIC_RX_PTYPE_UNUSED_ENTRY(228), + UNIC_RX_PTYPE_UNUSED_ENTRY(229), + UNIC_RX_PTYPE_UNUSED_ENTRY(230), + UNIC_RX_PTYPE_UNUSED_ENTRY(231), + UNIC_RX_PTYPE_UNUSED_ENTRY(232), + UNIC_RX_PTYPE_UNUSED_ENTRY(233), + UNIC_RX_PTYPE_UNUSED_ENTRY(234), + UNIC_RX_PTYPE_UNUSED_ENTRY(235), + UNIC_RX_PTYPE_UNUSED_ENTRY(236), + UNIC_RX_PTYPE_UNUSED_ENTRY(237), + UNIC_RX_PTYPE_UNUSED_ENTRY(238), + UNIC_RX_PTYPE_UNUSED_ENTRY(239), + UNIC_RX_PTYPE_UNUSED_ENTRY(240), + UNIC_RX_PTYPE_UNUSED_ENTRY(241), + UNIC_RX_PTYPE_UNUSED_ENTRY(242), + UNIC_RX_PTYPE_UNUSED_ENTRY(243), + UNIC_RX_PTYPE_UNUSED_ENTRY(244), + UNIC_RX_PTYPE_UNUSED_ENTRY(245), + UNIC_RX_PTYPE_UNUSED_ENTRY(246), + UNIC_RX_PTYPE_UNUSED_ENTRY(247), + UNIC_RX_PTYPE_UNUSED_ENTRY(248), + UNIC_RX_PTYPE_UNUSED_ENTRY(249), + UNIC_RX_PTYPE_UNUSED_ENTRY(250), + UNIC_RX_PTYPE_UNUSED_ENTRY(251), + UNIC_RX_PTYPE_UNUSED_ENTRY(252), + UNIC_RX_PTYPE_UNUSED_ENTRY(253), + UNIC_RX_PTYPE_UNUSED_ENTRY(254), + UNIC_RX_PTYPE_UNUSED_ENTRY(255), +}; + +static inline u16 unic_get_rqe_depth(struct unic_rq *rq) +{ + struct unic_dev *unic_dev = netdev_priv(rq->netdev); + + return unic_dev->channels.rqe_depth; +} + +static inline u16 unic_get_rqe_mask(struct unic_rq *rq) +{ + return unic_get_rqe_depth(rq) - 1; +} + +static inline u16 unic_get_rx_buff_len(struct unic_rq *rq) +{ + struct unic_dev *unic_dev = netdev_priv(rq->netdev); + + return unic_dev->channels.rx_buff_len; +} + +static unsigned int unic_page_order(struct unic_channels *channels) +{ +#if (PAGE_SIZE < 8192) + if (channels->rx_buff_len > (PAGE_SIZE / 2)) + return 1; +#endif + return 0; +} + +static void unic_init_jfr_ctx(struct unic_rq *rq, u16 rqe_depth, + u16 channels_num, u32 idx, u32 tid) +{ + dma_addr_t rqe_dma_addr = rq->rqe_base_dma_addr; + dma_addr_t db_dma_addr = rq->sw_db.db_dma_addr; + struct unic_jfr_ctx *ctx = &rq->jfr_ctx; + u32 jfcn = idx + channels_num; + + ctx->state = UNIC_JFR_STATE_READY; + ctx->token_en = 0; + ctx->rqe_shift = ilog2(roundup_pow_of_two(rqe_depth)); + ctx->jfcn_l = jfcn & (u32)UNIC_JFR_JFCN_L_VALID_BIT; + ctx->jfcn_h = (jfcn >> UNIC_JFR_JFCN_H_OFFSET) & + (u32)UNIC_JFR_JFCN_H_VALID_BIT; + ctx->rqe_base_addr_l = (rqe_dma_addr >> UNIC_RQE_VA_L_PAGE_4K_OFFSET) & + UNIC_RQE_VA_L_VALID_BIT; + ctx->rqe_base_addr_h = (rqe_dma_addr >> UNIC_RQE_VA_H_PAGE_4K_OFFSET) & + UNIC_RQE_VA_H_VALID_BIT; + ctx->rqe_token_id_l = tid & (u32)UNIC_RQE_TOKEN_ID_L_MASK; + ctx->rqe_token_id_h = (tid >> UNIC_RQE_TOKEN_ID_H_OFFSET) & + (u32)UNIC_RQE_TOKEN_ID_H_MASK; + ctx->idx_que_addr_l = unic_get_rx_buff_len(rq); + + ctx->pld_token_id = tid; + ctx->pi = rqe_depth; + ctx->ci = 0; + ctx->record_db_en = UNIC_RECORD_EN; + ctx->record_db_addr_l = (db_dma_addr >> UNIC_RQE_DB_VA_L_64B_OFFSET) & + UNIC_RQE_DB_VA_L_VALID_BIT; + ctx->record_db_addr_m = (db_dma_addr >> UNIC_RQE_DB_VA_M_64B_OFFSET) & + UNIC_RQE_DB_VA_M_VALID_BIT; + ctx->record_db_addr_h = (db_dma_addr >> UNIC_RQE_DB_VA_H_64B_OFFSET) & + UNIC_RQE_DB_VA_H_VALID_BIT; + ctx->cqeie = 0; + ctx->cqesz = 0; +} + +static int unic_rq_alloc_resource(struct unic_dev *unic_dev, struct unic_rq *rq) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + u16 rqe_depth; + u32 size; + int ret; + + rqe_depth = unic_dev->channels.rqe_depth; + rq->rqe_info = devm_kcalloc(&adev->dev, rqe_depth, + sizeof(struct unic_rqe_info), GFP_KERNEL); + if (!rq->rqe_info) { + dev_err(adev->dev.parent, "failed to alloc unic rqe info.\n"); + ret = -ENOMEM; + goto err_alloc_rqe_info; + } + + size = rqe_depth * sizeof(struct unic_rqe); + rq->rqe = dma_alloc_coherent(adev->dev.parent, size, + &rq->rqe_base_dma_addr, GFP_KERNEL); + if (!rq->rqe) { + dev_err(adev->dev.parent, "failed to dma alloc unic rqe.\n"); + ret = -ENOMEM; + goto err_alloc_rqe; + } + + rq->sw_db.db_addr = dma_alloc_coherent(adev->dev.parent, + UNIC_JFR_DB_SIZE, + &rq->sw_db.db_dma_addr, + GFP_KERNEL); + if (!rq->sw_db.db_addr) { + dev_err(adev->dev.parent, + "failed to dma alloc software db addr.\n"); + ret = -ENOMEM; + goto err_alloc_sw_db; + } + + return 0; + +err_alloc_sw_db: + dma_free_coherent(adev->dev.parent, size, rq->rqe, + rq->rqe_base_dma_addr); +err_alloc_rqe: + devm_kfree(&adev->dev, rq->rqe_info); +err_alloc_rqe_info: + return ret; +} + +static void unic_rq_free_resource(struct unic_dev *unic_dev, struct unic_rq *rq) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct unic_sw_db *sw_db = &rq->sw_db; + u32 size; + + size = unic_dev->channels.rqe_depth * sizeof(struct unic_rqe); + dma_free_coherent(rq->parent_dev, UNIC_JFR_DB_SIZE, sw_db->db_addr, + sw_db->db_dma_addr); + dma_free_coherent(rq->parent_dev, size, rq->rqe, + rq->rqe_base_dma_addr); + devm_kfree(&adev->dev, rq->rqe_info); +} + +static int unic_mbx_create_jfr_context(struct auxiliary_device *adev, + struct unic_rq *rq, u32 idx) +{ + struct ubase_cmd_mailbox *mailbox; + struct ubase_mbx_attr attr = {0}; + int ret; + + mailbox = ubase_alloc_cmd_mailbox(adev); + if (IS_ERR_OR_NULL(mailbox)) { + dev_err(adev->dev.parent, + "failed to alloc mailbox for create jfr context, idx = %u.\n", + idx); + return -ENOMEM; + } + + memcpy(mailbox->buf, &rq->jfr_ctx, sizeof(struct unic_jfr_ctx)); + ubase_fill_mbx_attr(&attr, idx, UBASE_MB_CREATE_JFR_CONTEXT, 0); + ret = ubase_hw_upgrade_ctx_ex(adev, &attr, mailbox); + if (ret) + dev_err(adev->dev.parent, + "failed to post create jfr ctx mbx, idx = %u, ret = %d.\n", + idx, ret); + + ubase_free_cmd_mailbox(adev, mailbox); + return ret; +} + +static void unic_modify_jfr_state(struct auxiliary_device *adev, u32 idx, + enum unic_jfr_state state) +{ + struct unic_jfr_ctx *ctx, *ctx_mask; + struct ubase_cmd_mailbox *mailbox; + struct ubase_mbx_attr attr = {0}; + int ret; + + mailbox = ubase_alloc_cmd_mailbox(adev); + if (IS_ERR_OR_NULL(mailbox)) { + dev_err(adev->dev.parent, + "failed to alloc mbx for modify jfr(%u) state\n", idx); + return; + } + + ctx = (struct unic_jfr_ctx *)mailbox->buf; + ctx_mask = ctx + 1; + memset(ctx_mask, 0xff, sizeof(struct unic_jfr_ctx)); + ctx->state = state; + ctx_mask->state = 0; + + ubase_fill_mbx_attr(&attr, idx, UBASE_MB_MODIFY_JFR_CONTEXT, 0); + ret = ubase_hw_upgrade_ctx_ex(adev, &attr, mailbox); + if (ret) + dev_err(adev->dev.parent, + "failed to upgrade jfr(%u) ctx state, ret=%d.\n", idx, + ret); + + ubase_free_cmd_mailbox(adev, mailbox); +} + +static int unic_get_last_rqe_idx(struct unic_dev *unic_dev, struct unic_rq *rq) +{ + u8 jfc_shift = unic_dev->channels.rq_jfc_shift; + u16 buff_len = unic_get_rx_buff_len(rq); + struct unic_cq *cq = rq->cq; + u16 pkt_len, rqe_num; + union unic_cqe *cqe; + int rqe_idx = -1; + u32 cq_mask, ci; + + ci = cq->ci; + cq_mask = unic_get_rq_cqe_mask(unic_dev); + dma_rmb(); /* Memory barrier before read cqe */ + cqe = &cq->cqe[ci & cq_mask]; + while (unic_cqe_owner_is_soft(jfc_shift, ci, cqe->rx.owner)) { + pkt_len = cqe->rx.packet_len; + rqe_num = DIV_ROUND_UP(pkt_len, buff_len); + rqe_idx = cqe->rx.start_rqe_idx + rqe_num; + ci++; + cqe = &cq->cqe[ci & cq_mask]; + } + + return rqe_idx; +} + +static int unic_jfr_wait_flush_done(struct unic_dev *unic_dev, u32 idx, + struct unic_rq *rq) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct ubase_cmd_mailbox *mailbox; + struct ubase_mbx_attr attr = {0}; + struct unic_jfr_ctx *ctx; + int rqe_idx = -1; + int ret; + + mailbox = ubase_alloc_cmd_mailbox(adev); + if (IS_ERR_OR_NULL(mailbox)) + return -ENOMEM; + + ubase_fill_mbx_attr(&attr, idx, UBASE_MB_QUERY_JFR_CONTEXT, 0); + + ret = ubase_hw_upgrade_ctx_ex(adev, &attr, mailbox); + if (ret) + goto out; + + ctx = (struct unic_jfr_ctx *)mailbox->buf; + if (rq->ci == ctx->ci) + goto out; + + rqe_idx = unic_get_last_rqe_idx(unic_dev, rq); + if ((rqe_idx != -1) && ((rqe_idx & USHRT_MAX) == ctx->ci)) + goto out; + + ret = -EBUSY; +out: + ubase_free_cmd_mailbox(adev, mailbox); + return ret; +} + +static void unic_mbx_destroy_jfr_context(struct auxiliary_device *adev, u32 idx) +{ + struct ubase_cmd_mailbox *mailbox; + struct ubase_mbx_attr attr = {0}; + int ret; + + mailbox = ubase_alloc_cmd_mailbox(adev); + if (IS_ERR_OR_NULL(mailbox)) { + dev_err(adev->dev.parent, + "failed to alloc mailbox for destroy jfr(%u) context.\n", + idx); + return; + } + + ubase_fill_mbx_attr(&attr, idx, UBASE_MB_DESTROY_JFR_CONTEXT, 0); + ret = ubase_hw_upgrade_ctx_ex(adev, &attr, mailbox); + if (ret) + dev_err(adev->dev.parent, + "failed to post destroy jfr(%u) mailbox, ret: %d.\n", + idx, ret); + + ubase_free_cmd_mailbox(adev, mailbox); +} + +static void unic_multi_jfr_wait_flush(struct unic_dev *unic_dev, u32 num) +{ +#define UNIC_WAIT_JFR_FLUSH_DONE_TIME 100 +#define UNIC_WAIT_JFR_FLUSH_EVERY_TIME 10 + + struct auxiliary_device *adev = unic_dev->comdev.adev; + unsigned long end_jiffies, fd_bitmap = 0; + struct unic_channel *c; + bool timeout = false; + u32 i, fd_cnt = 0; + + end_jiffies = jiffies + msecs_to_jiffies(UNIC_WAIT_JFR_FLUSH_DONE_TIME); + while (!timeout) { + /* check the last result after waiting enough timeout */ + if (time_is_before_eq_jiffies(end_jiffies)) + timeout = true; + + for (i = 0; i < num; i++) { + c = &unic_dev->channels.c[i]; + if (test_bit(i, &fd_bitmap)) + continue; + + c->ret = unic_jfr_wait_flush_done(unic_dev, i, c->rq); + if (c->ret) + continue; + + fd_cnt++; + set_bit(i, &fd_bitmap); + } + + if (fd_cnt == num) + return; + + msleep(UNIC_WAIT_JFR_FLUSH_EVERY_TIME); + } + + for (i = 0; i < num; i++) { + c = &unic_dev->channels.c[i]; + if (!test_bit(i, &fd_bitmap)) + dev_err(adev->dev.parent, + "wait jfr(%u) flush timeout, ret=%d\n", i, + c->ret); + } +} + +static void unic_destroy_multi_jfr_context(struct unic_dev *unic_dev, u32 num) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + u32 i; + + for (i = 0; i < num; i++) + unic_modify_jfr_state(adev, i, UNIC_JFR_STATE_ERROR); + + unic_multi_jfr_wait_flush(unic_dev, num); + + for (i = 0; i < num; i++) + unic_mbx_destroy_jfr_context(adev, i); +} + +static int unic_rq_create_page_pool(struct unic_dev *unic_dev, u16 rqe_depth, + struct unic_rq *rq) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct unic_channels *channels = &unic_dev->channels; + u32 order = unic_page_order(channels); + struct page_pool_params pp_params = { + .flags = PP_FLAG_DMA_MAP | PP_FLAG_PAGE_FRAG | + PP_FLAG_DMA_SYNC_DEV, + .order = order, + .pool_size = rqe_depth * channels->rx_buff_len / + (PAGE_SIZE << order), + .nid = dev_to_node(adev->dev.parent), + .dev = adev->dev.parent, + .dma_dir = DMA_FROM_DEVICE, + .max_len = PAGE_SIZE << order, + .offset = 0, + }; + + rq->page_pool = page_pool_create(&pp_params); + if (IS_ERR(rq->page_pool)) { + dev_warn(adev->dev.parent, + "failed to create page pool, ret = %ld.\n", + PTR_ERR(rq->page_pool)); + rq->page_pool = NULL; + return -ENOMEM; + } + + return 0; +} + +static void unic_rq_free_page_pool(struct unic_rq *rq) +{ + page_pool_destroy(rq->page_pool); + rq->page_pool = NULL; +} + +static int unic_page_pool_alloc_frag(struct unic_dev *unic_dev, + struct unic_rq *rq, + struct unic_rqe_info *rqe_info) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct unic_channels *channels = &unic_dev->channels; + + rqe_info->page_offset = 0; + rqe_info->p = page_pool_dev_alloc_frag(rq->page_pool, + &rqe_info->page_offset, + channels->rx_buff_len); + if (unlikely(!rqe_info->p)) { + dev_err(adev->dev.parent, "failed to alloc page pool frag.\n"); + return -ENOMEM; + } + + rqe_info->buf = page_address(rqe_info->p); + rqe_info->rqe_dma_addr = page_pool_get_dma_addr(rqe_info->p); + + return 0; +} + +static void unic_page_pool_put_frags(struct unic_rq *rq, u32 rqe_num) +{ + u16 rqe_mask = unic_get_rqe_mask(rq); + u32 i; + + for (i = 0; i < rqe_num; i++, rq->ci++) + page_pool_put_full_page(rq->page_pool, + rq->rqe_info[rq->ci & rqe_mask].p, false); +} + +static void unic_rq_free_rx_buffers(struct unic_rq *rq) +{ + u32 rqe_num = rq->pi < rq->ci ? rq->pi + UNIC_RQ_CI_REVERSE - rq->ci : + rq->pi - rq->ci; + + unic_page_pool_put_frags(rq, rqe_num); + unic_rq_free_page_pool(rq); +} + +static int unic_rq_fill_rx_buffers(struct unic_dev *unic_dev, struct unic_rq *rq) +{ +#define UNIC_RESCHED_BD_NUM 1024 + + struct unic_rqe *rqe; + u16 i, rqe_depth; + int ret; + + rqe_depth = unic_dev->channels.rqe_depth; + ret = unic_rq_create_page_pool(unic_dev, rqe_depth, rq); + if (ret) + return ret; + + rqe = rq->rqe; + for (i = 0; i < rqe_depth; i++, rq->pi++) { + ret = unic_page_pool_alloc_frag(unic_dev, rq, &rq->rqe_info[i]); + if (ret) + goto err_alloc_page; + + rqe[i].buff_addr = cpu_to_le64(rq->rqe_info[i].rqe_dma_addr + + rq->rqe_info[i].page_offset); + if (!(i % UNIC_RESCHED_BD_NUM)) + cond_resched(); + } + + return 0; + +err_alloc_page: + unic_rq_free_rx_buffers(rq); + + return ret; +} + +int unic_create_rq(struct unic_dev *unic_dev, u32 idx) +{ + struct unic_channel *channel = &unic_dev->channels.c[idx]; + struct auxiliary_device *adev = unic_dev->comdev.adev; + u16 rqe_depth = unic_dev->channels.rqe_depth; + struct unic_rq *rq; + u16 channels_num; + int ret; + + rq = devm_kzalloc(&adev->dev, sizeof(*rq), GFP_KERNEL); + if (!rq) + return -ENOMEM; + + rq->parent_dev = adev->dev.parent; + + ret = unic_rq_alloc_resource(unic_dev, rq); + if (ret) + goto err_alloc_rq_buf; + + rq->netdev = unic_dev->comdev.netdev; + rq->queue_index = idx & U16_MAX; + ret = unic_rq_fill_rx_buffers(unic_dev, rq); + if (ret) + goto err_fill_rqe_buff; + + channels_num = unic_dev->channels.num; + unic_init_jfr_ctx(rq, rqe_depth, channels_num, idx, unic_dev->tid); + + *(u16 *)(rq->sw_db.db_addr) = rqe_depth; + + ret = unic_mbx_create_jfr_context(adev, rq, idx); + if (ret) + goto err_fill_send_rq_mbx; + + channel->rq = rq; + return 0; + +err_fill_send_rq_mbx: + unic_rq_free_rx_buffers(rq); +err_fill_rqe_buff: + unic_rq_free_resource(unic_dev, rq); +err_alloc_rq_buf: + devm_kfree(&adev->dev, rq); + return ret; +} + +static void unic_free_multi_rq_resource(struct unic_dev *unic_dev, u32 num) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct unic_channel *channel; + u32 i; + + for (i = 0; i < num; i++) { + channel = &unic_dev->channels.c[i]; + if (!channel->rq) { + dev_err(adev->dev.parent, + "failed to get channel rq(%u).\n", i); + continue; + } + unic_rq_free_rx_buffers(channel->rq); + unic_rq_free_resource(unic_dev, channel->rq); + devm_kfree(&adev->dev, channel->rq); + channel->rq = NULL; + } +} + +void unic_destroy_rq(struct unic_dev *unic_dev, u32 num) +{ + if (!num) + return; + + if (!__unic_resetting(unic_dev)) + unic_destroy_multi_jfr_context(unic_dev, num); + + unic_free_multi_rq_resource(unic_dev, num); +} + +static void unic_handle_rx_csum(struct net_device *netdev, struct sk_buff *skb, + union unic_cqe *cqe, struct unic_rq *rq) +{ +#define UNIC_RX_CQE_L3L4P_ENABLE 1 + + u16 csum = cqe->rx.unknown_pkt_l2_checksum; + + skb->ip_summed = CHECKSUM_NONE; + + if (!(netdev->features & NETIF_F_RXCSUM)) + return; + + if (!(cqe->rx.l3l4p & UNIC_RX_CQE_L3L4P_ENABLE)) + return; + + if (unlikely(cqe->rx.l3_err || cqe->rx.l4_err || cqe->rx.ol3_err || + cqe->rx.ol4_err)) { + unic_rq_stats_inc(rq, l3_l4_csum_err); + return; + } + + skb->ip_summed = unic_rx_ptype_tbl[cqe->rx.ptype].ip_summed; + skb->csum_level = unic_rx_ptype_tbl[cqe->rx.ptype].csum_level; + if (skb->ip_summed == CHECKSUM_COMPLETE) { + unic_rq_stats_inc(rq, csum_complete); + skb->csum = csum_unfold((__force __sum16)csum); + } +} + +#ifdef CONFIG_UB_UNIC_UBL +static __be16 unic_assign_ub_proto(struct net_device *netdev, + struct sk_buff *skb, u32 l3_type) +{ + u8 ub_type; + + if (l3_type == UNIC_L3_TYPE_IPV4) + ub_type = UB_IPV4_CFG_TYPE; + else if (l3_type == UNIC_L3_TYPE_IPV6) + ub_type = UB_IPV6_CFG_TYPE; + else + ub_type = UB_NOIP_CFG_TYPE; + + return ubl_type_trans(skb, netdev, ub_type); +} +#endif + +static int unic_handle_cqe(struct unic_rq *rq, union unic_cqe *cqe) +{ + struct unic_dev *unic_dev = netdev_priv(rq->netdev); + struct net_device *netdev = rq->netdev; + struct sk_buff *skb = rq->skb; + enum pkt_hash_types rss_type; + + if (unlikely(!cqe->rx.packet_len || cqe->rx.l2_err || cqe->rx.trunc)) { + if (!cqe->rx.packet_len) + unic_rq_stats_inc(rq, err_pkt_len_cnt); + else if (cqe->rx.l2_err) + unic_rq_stats_inc(rq, l2_err); + else + unic_rq_stats_inc(rq, trunc_cnt); + + return -EFAULT; + } + +#ifdef CONFIG_UB_UNIC_UBL + if (unic_dev_ubl_supported(unic_dev)) + skb->protocol = unic_assign_ub_proto(netdev, skb, + unic_rx_ptype_tbl[cqe->rx.ptype].l3_type); + else + skb->protocol = eth_type_trans(skb, netdev); +#else + skb->protocol = eth_type_trans(skb, netdev); +#endif + + unic_handle_rx_csum(netdev, skb, cqe, rq); + + rss_type = unic_rx_ptype_tbl[cqe->rx.ptype].hash_type; + + skb_set_hash(skb, le32_to_cpu(cqe->rx.rss_hash), rss_type); + + skb_record_rx_queue(skb, rq->queue_index); + + return 0; +} + +static void unic_fill_skb_frags(struct unic_rq *rq, struct sk_buff *skb, + struct unic_rqe_info *rqe_info, + u32 frag_index, u32 frag_size) +{ + u16 buff_len = unic_get_rx_buff_len(rq); + u32 page_offset = rqe_info->page_offset; + struct page *page = rqe_info->p; + + dma_sync_single_for_cpu(rq->parent_dev, + rqe_info->rqe_dma_addr + page_offset, frag_size, + DMA_FROM_DEVICE); + + skb_add_rx_frag(skb, frag_index, page, page_offset, frag_size, + buff_len); +} + +static int unic_add_skb_frags(struct unic_rq *rq, struct napi_struct *napi, + u16 pkt_len, u32 pull_len) +{ + u16 buff_len = unic_get_rx_buff_len(rq); + u16 rqe_num = DIV_ROUND_UP(pkt_len, buff_len); + u16 rqe_mask = unic_get_rqe_mask(rq); + struct sk_buff *head_skb = rq->skb; + u16 i, frag_size, frag_index = 0; + struct unic_rqe_info *rqe_info; + struct sk_buff *skb = head_skb; + struct sk_buff *new_skb; + u16 last_ci = rq->ci; + + /* add skb first frag */ + frag_size = rqe_num == 1 ? pkt_len : buff_len; + rqe_info = &rq->rqe_info[last_ci++ & rqe_mask]; + rqe_info->page_offset += pull_len; + unic_fill_skb_frags(rq, skb, rqe_info, frag_index++, + frag_size - pull_len); + + for (i = 1; i < rqe_num; i++, frag_index++, last_ci++) { + if (unlikely(frag_index >= MAX_SKB_FRAGS)) { + new_skb = napi_alloc_skb(napi, 0); + if (unlikely(!new_skb)) { + unic_rq_stats_inc(rq, alloc_skb_err); + return -ENOMEM; + } + + skb_mark_for_recycle(new_skb); + frag_index = 0; + + if (skb == head_skb) + skb_shinfo(skb)->frag_list = new_skb; + else + skb->next = new_skb; + + skb = new_skb; + } + + pkt_len = pkt_len - buff_len; + frag_size = i < rqe_num - 1 ? buff_len : pkt_len; + + if (unlikely(skb != head_skb)) { + head_skb->len += frag_size; + head_skb->data_len += frag_size; + head_skb->truesize += buff_len; + } + + rqe_info = &rq->rqe_info[last_ci & rqe_mask]; + unic_fill_skb_frags(rq, skb, rqe_info, frag_index, frag_size); + } + + return 0; +} + +static u32 unic_get_skb_linear_len(struct unic_rq *rq, u8 *va, + struct unic_dev *unic_dev) +{ + u32 pull_len; + + if (unic_dev_ubl_supported(unic_dev)) + pull_len = UNIC_RX_HEAD_SIZE; + else + pull_len = eth_get_headlen(rq->netdev, va, UNIC_RX_HEAD_SIZE); + + return pull_len; +} + +static int unic_create_skb(struct unic_rq *rq, struct napi_struct *napi, + u16 pkt_len) +{ + struct unic_dev *unic_dev = netdev_priv(rq->netdev); + struct unic_rqe_info *rqe_info; + struct sk_buff *skb; + u32 pull_len; + u16 rqe_mask; + int ret; + u8 *va; + + rqe_mask = unic_get_rqe_mask(rq); + rqe_info = &rq->rqe_info[rq->ci & rqe_mask]; + + skb = napi_alloc_skb(napi, UNIC_RX_HEAD_SIZE); + if (unlikely(!skb)) { + unic_rq_stats_inc(rq, alloc_skb_err); + return -ENOMEM; + } + + rq->skb = skb; + va = rqe_info->buf + rqe_info->page_offset; + + if (pkt_len <= UNIC_RX_HEAD_SIZE) { + dma_sync_single_for_cpu(rq->parent_dev, + rqe_info->rqe_dma_addr + rqe_info->page_offset, + ALIGN(pkt_len, sizeof(long)), + DMA_FROM_DEVICE); + + memcpy(__skb_put(skb, pkt_len), va, ALIGN(pkt_len, sizeof(long))); + + page_pool_put_full_page(rq->page_pool, rqe_info->p, false); + return 0; + } + + skb_mark_for_recycle(skb); + + pull_len = unic_get_skb_linear_len(rq, va, unic_dev); + __skb_put(skb, pull_len); + ret = unic_add_skb_frags(rq, napi, pkt_len, pull_len); + if (unlikely(ret)) { + dev_kfree_skb_any(rq->skb); + rq->skb = NULL; + return ret; + } + + memcpy(skb->data, va, ALIGN(pull_len, sizeof(long))); + + return 0; +} + +static void unic_fix_rq_ci(struct unic_rq *rq, union unic_cqe *cqe) +{ + struct unic_dev *unic_dev = netdev_priv(rq->netdev); + u32 start_rqe_idx = cqe->rx.start_rqe_idx; + u32 put_page_num; + + if (unlikely(rq->ci != cqe->rx.start_rqe_idx)) { + if (net_ratelimit()) + unic_warn(unic_dev, + "queue_index(%u) sw_rq_ci(%hu) hw_rq_ci(%u) do not match.\n", + rq->queue_index, rq->ci, start_rqe_idx); + if (start_rqe_idx < rq->ci) + start_rqe_idx += UNIC_RQ_CI_REVERSE; + put_page_num = start_rqe_idx - rq->ci; + unic_page_pool_put_frags(rq, put_page_num); + rq->pending_buf += put_page_num; + rq->ci = cqe->rx.start_rqe_idx; + } +} + +static int unic_rx_construct_skb(struct unic_rq *rq, struct napi_struct *napi, + union unic_cqe *cqe, u64 *bytes) +{ + u16 buff_len = unic_get_rx_buff_len(rq); + u16 pkt_len = cqe->rx.packet_len; + u16 rqe_num; + int ret; + + rq->cq->ci++; + if (unlikely(cqe->rx.doi)) { + unic_rq_stats_inc(rq, doi_cnt); + return -EFAULT; + } + + unic_fix_rq_ci(rq, cqe); + rqe_num = DIV_ROUND_UP(pkt_len, buff_len); + rq->pending_buf += rqe_num; + ret = unic_create_skb(rq, napi, pkt_len); + if (unlikely(ret)) + goto release_rx_buffer; + + ret = unic_handle_cqe(rq, cqe); + if (unlikely(ret)) + goto destroy_skb; + + *bytes += pkt_len; + rq->ci += rqe_num; + + return 0; + +destroy_skb: + dev_kfree_skb_any(rq->skb); + rq->skb = NULL; +release_rx_buffer: + unic_page_pool_put_frags(rq, rqe_num); + return ret; +} + +static bool unic_refill_rx_buffers(struct unic_rq *rq) +{ + struct unic_dev *unic_dev = netdev_priv(rq->netdev); + u16 rqe_mask = unic_get_rqe_mask(rq); + struct unic_rqe_info *rqe_info; + u16 pd_buff = rq->pending_buf; + u32 i, rq_ci = rq->ci; + struct unic_rqe *rqe; + bool ret = false; + + if (unlikely(rq_ci < pd_buff)) + rq_ci += UNIC_RQ_CI_REVERSE; + + for (i = rq_ci - pd_buff; i < rq_ci; i++) { + rqe_info = &rq->rqe_info[i & rqe_mask]; + if (unlikely(unic_page_pool_alloc_frag(unic_dev, rq, rqe_info))) { + unic_rq_stats_inc(rq, alloc_frag_err); + ret = true; + break; + } + + rqe = &rq->rqe[i & rqe_mask]; + rqe->buff_addr = rqe_info->rqe_dma_addr + rqe_info->page_offset; + rq->pi++; + } + + if (likely(i > rq_ci - pd_buff)) { + rq->pending_buf = rq_ci - i; + *(u16 *)(rq->sw_db.db_addr) = rq->pi; + } + + return ret; +} + +void unic_send_skb_to_stack(struct unic_channel *c, struct sk_buff *skb) +{ + struct napi_struct *napi = &c->napi; + + if (skb_has_frag_list(skb)) + napi_gro_flush(napi, false); + + napi_gro_receive(napi, skb); +} + +int unic_poll_rx(struct unic_channel *c, int budget, + void (*rx_fn)(struct unic_channel *, struct sk_buff *)) +{ +#define UNIC_RX_BUFFER_WRITE 16 + + struct unic_rq *rq = c->rq; + struct unic_dev *unic_dev = netdev_priv(rq->netdev); + u8 jfc_shift = unic_dev->channels.rq_jfc_shift; + struct unic_cq *cq = rq->cq; + struct napi_struct *napi; + u32 last_ci = cq->ci; + bool failure = false; + union unic_cqe *cqe; + int packets = 0; + u64 bytes = 0; + u32 cq_mask; + + napi = &c->napi; + cq_mask = unic_get_rq_cqe_mask(unic_dev); + + while (packets < budget) { + if (rq->pending_buf >= UNIC_RX_BUFFER_WRITE) { + unic_cq_doorbell(cq, last_ci); + last_ci = cq->ci; + failure = failure || unic_refill_rx_buffers(rq); + } + + dma_rmb(); /* Memory barrier before read cqe */ + cqe = &cq->cqe[cq->ci & cq_mask]; + if (!unic_cqe_owner_is_soft(jfc_shift, cq->ci, cqe->rx.owner)) + break; + + trace_unic_rx_cqe(rq->netdev, cq, rq->pi, rq->ci, cq_mask); + if (unic_rx_construct_skb(rq, napi, cqe, &bytes)) + break; + + rx_fn(c, rq->skb); + + rq->skb = NULL; + + packets++; + } + + if (rq->pending_buf) { + unic_cq_doorbell(cq, last_ci); + failure = failure || unic_refill_rx_buffers(rq); + } + + u64_stats_update_begin(&rq->syncp); + rq->stats.packets += (u64)packets; + rq->stats.bytes += bytes; + u64_stats_update_end(&rq->syncp); + + return failure ? budget : packets; +} + +static void unic_clear_rq_buffers(struct unic_rq *rq, union unic_cqe *cqe) +{ + u16 buff_len = unic_get_rx_buff_len(rq); + u16 pkt_len, rqe_num; + + if (cqe->rx.doi) { + unic_rq_stats_inc(rq, doi_cnt); + return; + } + + unic_fix_rq_ci(rq, cqe); + pkt_len = le16_to_cpu(cqe->rx.packet_len); + rqe_num = DIV_ROUND_UP(pkt_len, buff_len); + unic_page_pool_put_frags(rq, rqe_num); + + rq->pending_buf += rqe_num; +} + +void unic_clear_rq(struct unic_rq *rq) +{ + struct unic_dev *unic_dev = netdev_priv(rq->netdev); + u8 jfc_shift = unic_dev->channels.rq_jfc_shift; + u32 cq_mask = unic_get_rq_cqe_mask(unic_dev); + struct unic_cq *cq = rq->cq; + u32 last_ci = cq->ci; + union unic_cqe *cqe; + + cqe = &cq->cqe[cq->ci & cq_mask]; + while (unic_cqe_owner_is_soft(jfc_shift, cq->ci, cqe->rx.owner)) { + unic_clear_rq_buffers(rq, cqe); + cq->ci++; + cqe = &cq->cqe[cq->ci & cq_mask]; + } + + unic_cq_doorbell(cq, last_ci); + + unic_refill_rx_buffers(rq); +} diff --git a/drivers/net/ub/unic/unic_rx.h b/drivers/net/ub/unic/unic_rx.h new file mode 100644 index 000000000000..591cf4cbb6f1 --- /dev/null +++ b/drivers/net/ub/unic/unic_rx.h @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_RX_H__ +#define __UNIC_RX_H__ + +#include + +struct unic_dev; + +#define UNIC_RQE_VA_L_PAGE_4K_OFFSET 12 +#define UNIC_RQE_VA_L_VALID_BIT GENMASK(19, 0) +#define UNIC_RQE_VA_H_OFFSET 20 +#define UNIC_RQE_VA_H_PAGE_4K_OFFSET (UNIC_RQE_VA_H_OFFSET + \ + UNIC_RQE_VA_L_PAGE_4K_OFFSET) +#define UNIC_RQE_VA_H_VALID_BIT GENMASK(31, 0) + +#define UNIC_JFR_JFCN_L_VALID_BIT GENMASK(11, 0) +#define UNIC_JFR_JFCN_H_OFFSET 12U +#define UNIC_JFR_JFCN_H_VALID_BIT GENMASK(7, 0) + +#define UNIC_RQE_DB_VA_L_64B_OFFSET 6 +#define UNIC_RQE_DB_VA_L_VALID_BIT GENMASK(23, 0) +#define UNIC_RQE_DB_VA_M_OFFSET 24 +#define UNIC_RQE_DB_VA_M_64B_OFFSET \ + (UNIC_RQE_DB_VA_M_OFFSET + UNIC_RQE_DB_VA_L_64B_OFFSET) +#define UNIC_RQE_DB_VA_M_VALID_BIT GENMASK(31, 0) +#define UNIC_RQE_DB_VA_H_OFFSET 32 +#define UNIC_RQE_DB_VA_H_64B_OFFSET \ + (UNIC_RQE_DB_VA_H_OFFSET + UNIC_RQE_DB_VA_M_64B_OFFSET) +#define UNIC_RQE_DB_VA_H_VALID_BIT GENMASK(1, 0) + +#define UNIC_RQE_TOKEN_ID_L_MASK GENMASK(13, 0) +#define UNIC_RQE_TOKEN_ID_H_OFFSET 14U +#define UNIC_RQE_TOKEN_ID_H_MASK GENMASK(5, 0) + +#define UNIC_JFR_DB_SIZE 4 + +#define UNIC_RX_BUF_LEN_2K 2048 +#define UNIC_RX_BUF_LEN_4K 4096 + +#define unic_rq_stats_inc(rq, cnt) do { \ + typeof(rq) _rq = (rq); \ + u64_stats_update_begin(&(_rq)->syncp); \ + ((_rq)->stats.cnt)++; \ + u64_stats_update_end(&(_rq)->syncp); \ + } while (0) + +enum unic_jfr_state { + UNIC_JFR_STATE_RESET, + UNIC_JFR_STATE_READY, + UNIC_JFR_STATE_ERROR, + UNIC_JFR_STATE_RESERVED, +}; + +struct unic_channel; + +struct unic_rx_ptype { + u32 ptype : 8; + u32 csum_level : 2; + u32 ip_summed : 2; + u32 l3_type : 4; + u32 valid : 1; + u32 hash_type : 3; + u32 resv : 12; +}; + +struct unic_rq_stats { + u64 alloc_skb_err; + u64 bytes; + u64 packets; + u64 err_pkt_len_cnt; + u64 doi_cnt; + u64 trunc_cnt; + u64 multicast; + u64 l2_err; + u64 l3_l4_csum_err; + u64 csum_complete; + u64 alloc_frag_err; +}; + +struct unic_jfr_ctx { + /* DW0 */ + u32 state : 2; + u32 limit_wl : 2; + u32 rqe_size_shift : 3; + u32 token_en : 1; + u32 rqe_shift : 4; + u32 rnr_timer : 5; + u32 record_db_en : 1; + u32 rqe_token_id_l : 14; + /* DW1 */ + u32 rqe_token_id_h : 6; + u32 type : 3; + u32 rsv : 3; + u32 rqe_base_addr_l : 20; + /* DW2 */ + u32 rqe_base_addr_h; + /* DW3 */ + u32 rqe_position : 1; + u32 pld_position : 1; + u32 pld_token_id : 20; + u32 rsv1 : 10; + /* DW4 */ + u32 token_value; + /* DW5 */ + u32 user_data_l; + /* DW6 */ + u32 user_data_h; + /* DW7 */ + u32 pi : 16; + u32 ci : 16; + /* DW8 */ + u32 idx_que_addr_l; + /* DW9 */ + u32 idx_que_addr_h : 20; + u32 jfcn_l : 12; + /* DW10 */ + u32 jfcn_h : 8; + u32 record_db_addr_l : 24; + /* DW11 */ + u32 record_db_addr_m; + /* DW12 */ + u32 record_db_addr_h : 2; + u32 cqeie : 1; + u32 cqesz : 1; + u32 rsv3 : 28; + /* DW13 - DW15 */ + u32 rsv4[3]; +}; + +struct unic_rqe { + __le64 rsv; + __le64 buff_addr; +}; + +struct unic_rqe_info { + void *buf; + struct page *p; + dma_addr_t rqe_dma_addr; + u32 page_offset; +}; + +struct unic_sw_db { + void *db_addr; + dma_addr_t db_dma_addr; +}; + +struct unic_rq { + struct device *parent_dev; + struct unic_rq_stats stats; + struct u64_stats_sync syncp; + struct unic_jfr_ctx jfr_ctx; + struct unic_rqe *rqe; + dma_addr_t rqe_base_dma_addr; + struct unic_rqe_info *rqe_info; + struct unic_sw_db sw_db; + struct unic_cq *cq; + struct sk_buff *skb; + u16 pi; /* the start of next to product */ + u16 ci; /* the start of next to consume */ + u16 queue_index; + u16 pending_buf; + struct net_device *netdev; + struct page_pool *page_pool; +}; + +int unic_create_rq(struct unic_dev *unic_dev, u32 idx); +void unic_destroy_rq(struct unic_dev *unic_dev, u32 idx); +int unic_poll_rx(struct unic_channel *c, int budget, + void (*rx_fn)(struct unic_channel *, struct sk_buff *)); +void unic_clear_rq(struct unic_rq *rq); +void unic_send_skb_to_stack(struct unic_channel *c, struct sk_buff *skb); + +#endif diff --git a/drivers/net/ub/unic/unic_trace.h b/drivers/net/ub/unic/unic_trace.h new file mode 100644 index 000000000000..b6cec3303175 --- /dev/null +++ b/drivers/net/ub/unic/unic_trace.h @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +/* This must be outside ifdef _UNIC_TRACE_H_ */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM unic + +#if !defined(__UNIC_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) +#define __UNIC_TRACE_H__ + +#include + +#define trace_tx_max_sqebb_num 8 +#define trace_cqe_num (sizeof(union unic_cqe) / sizeof(u32)) +#define trace_sqebb_num(sqebb_num) \ + (sizeof(struct unic_sqebb) * (sqebb_num) / sizeof(u32)) + +#define trace_nlmsghdr_arr_size (sizeof(struct nlmsghdr) / sizeof(u32)) +#define trace_ifaddrmsg_arr_size (sizeof(struct ifaddrmsg) / sizeof(u32)) +#define trace_attrbuf_arr_size 16 + +DECLARE_EVENT_CLASS(unic_cqe_template, + TP_PROTO(const struct net_device *netdev, + const struct unic_cq *cq, u16 pi, u16 ci, + u32 cqe_mask), + TP_ARGS(netdev, cq, pi, ci, cqe_mask), + + TP_STRUCT__entry(__field(u32, jfcn) + __field(u32, cqe_mask) + __field(u16, pi) + __field(u16, ci) + __field(u32, cq_ci) + __field(u8, cqe_num) + __array(u32, cqe, trace_cqe_num) + __string(devname, netdev->name) + ), + + TP_fast_assign(__entry->jfcn = cq->jfcn; + __entry->cqe_mask = cqe_mask; + __entry->pi = pi; + __entry->ci = ci; + __entry->cq_ci = cq->ci; + __entry->cqe_num = unic_get_cqe_size() / sizeof(u32); + memcpy(__entry->cqe, + &cq->cqe[cq->ci & __entry->cqe_mask], + unic_get_cqe_size()); + __assign_str(devname, netdev->name); + ), + + TP_printk("%s-%u-%u/%u cqe(%u): %s", + __get_str(devname), __entry->jfcn, __entry->pi, + __entry->ci, __entry->cq_ci, + __print_array(__entry->cqe, __entry->cqe_num, + sizeof(u32)) + ) +); + +DEFINE_EVENT(unic_cqe_template, unic_tx_cqe, + TP_PROTO(const struct net_device *netdev, const struct unic_cq *cq, + u16 pi, u16 ci, u32 cqe_mask), + TP_ARGS(netdev, cq, pi, ci, cqe_mask)); + +DEFINE_EVENT(unic_cqe_template, unic_rx_cqe, + TP_PROTO(const struct net_device *netdev, const struct unic_cq *cq, + u16 pi, u16 ci, u32 cqe_mask), + TP_ARGS(netdev, cq, pi, ci, cqe_mask)); + +TRACE_EVENT(unic_tx_sqe, + TP_PROTO(struct unic_sq *sq, u16 sqebb_num, u16 sqebb_mask, + bool doorbell), + TP_ARGS(sq, sqebb_num, sqebb_mask, doorbell), + + TP_STRUCT__entry(__field(u32, jfcn) + __field(u16, pi) + __field(u16, ci) + __field(u16, sqebb_num) + __field(bool, doorbell) + __field(u16, real_pi) + __array(u32, sqebb, trace_sqebb_num(trace_tx_max_sqebb_num)) + __string(devname, sq->netdev->name) + ), + + TP_fast_assign(__entry->jfcn = sq->cq->jfcn; + __entry->pi = sq->pi; + __entry->ci = sq->ci; + __entry->sqebb_num = sqebb_num; + __entry->doorbell = doorbell; + __entry->real_pi = sq->pi & sqebb_mask; + if (__entry->real_pi + sqebb_num - 1 > sqebb_mask) { + memcpy(__entry->sqebb, &sq->sqebb[__entry->real_pi], + (sqebb_mask - __entry->real_pi + 1) * + sizeof(struct unic_sqebb)); + memcpy(&__entry->sqebb[sqebb_mask - __entry->real_pi + 1], + &sq->sqebb[0], + (__entry->real_pi + sqebb_num - 1 - sqebb_mask) * + sizeof(struct unic_sqebb)); + } else { + memcpy(__entry->sqebb, &sq->sqebb[__entry->real_pi], + sqebb_num * sizeof(struct unic_sqebb)); + } + unic_mask_key_words(__entry->sqebb); + __assign_str(devname, sq->netdev->name); + ), + + TP_printk("%s-%u-%u/%u-%d sqe: %s", + __get_str(devname), __entry->jfcn, __entry->pi, + __entry->ci, __entry->doorbell, + __print_array(__entry->sqebb, + trace_sqebb_num(__entry->sqebb_num), + sizeof(u32)) + ) +); + +TRACE_EVENT(unic_ip_req_skb, + TP_PROTO(const struct net_device *netdev, const struct sk_buff *skb, + const struct nlmsghdr *nh, const struct ifaddrmsg *info, + const char *attrbuf), + TP_ARGS(netdev, skb, nh, info, attrbuf), + + TP_STRUCT__entry(__field(u32, len) + __array(u32, nl_hdr, trace_nlmsghdr_arr_size) + __array(u32, ifa_info, trace_ifaddrmsg_arr_size) + __array(u32, attrs_buf, trace_attrbuf_arr_size) + __string(devname, netdev->name) + ), + + TP_fast_assign(__entry->len = skb->len; + memcpy(__entry->nl_hdr, nh, sizeof(struct nlmsghdr)); + memcpy(__entry->ifa_info, info, sizeof(struct ifaddrmsg)); + memcpy(__entry->attrs_buf, attrbuf, + sizeof(u32) * trace_attrbuf_arr_size); + __assign_str(devname, netdev->name); + ), + + TP_printk("%s netlink: len=%u nl_hdr=%s ifa_info=%s attrs_buf=%s", + __get_str(devname), __entry->len, + __print_array(__entry->nl_hdr, trace_nlmsghdr_arr_size, + sizeof(u32)), + __print_array(__entry->ifa_info, trace_ifaddrmsg_arr_size, + sizeof(u32)), + __print_array(__entry->attrs_buf, trace_attrbuf_arr_size, + sizeof(u32)) + ) +); +#endif /* _UNIC_TRACE_H_ */ + +/* This must be outside ifdef _UNIC_TRACE_H */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE unic_trace +#include diff --git a/drivers/net/ub/unic/unic_tx.c b/drivers/net/ub/unic/unic_tx.c new file mode 100644 index 000000000000..f0e97f1c3184 --- /dev/null +++ b/drivers/net/ub/unic/unic_tx.c @@ -0,0 +1,1348 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#define dev_fmt(fmt) "unic: (pid %d) " fmt, current->pid + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_UB_UNIC_UBL +#include +#endif +#include + +#include "unic_dev.h" +#include "unic_hw.h" +#include "unic_tx.h" + +/* When use tracepoint, must define "CREATE_TRACE_POINTS" before include the + * trace header file. + * If many source file need include the same header file, the + * "CREATE_TRACE_POINTS" only define once. + */ +#define CREATE_TRACE_POINTS +#include "unic_trace.h" + +#define UNIC_CSUM_OFFLOAD_DISABLE 0 +#define UNIC_CSUM_OFFLOAD_ENABLE 1 +#define IP_VERSION_IPV4 0x4 +#define IP_VERSION_IPV6 0x6 + +#define UNIC_CC_DEFAULT_FECN_MODE 0x4000 +#define UNIC_SGE_MAX_PAYLOAD UINT_MAX +#define UNIC_MIN_TX_LEN 60U +#define UNIC_SQE_CTRL_SECTION_NUM 2 +#define UNIC_SQE_MAX_SGE_NUM 18 +#define UNIC_SQEBB_MAX_SGE_NUM 4 +#define UNIC_HEADER_LEN_2B_OFFSET 1 +#define UNIC_HEADER_LEN_4B_OFFSET 2 +#define UNIC_SKB_FRAGS_START_INDEX 1 +#define UNIC_SQEBB_POINT_REVERSE (USHRT_MAX + 1) +#define UNIC_RCV_SEND_MAX_DIFF_VAL 512U + +#define unic_sqebb_cnt(sge_num) DIV_ROUND_UP((sge_num), 4) + +static inline u16 unic_get_sqe_depth(struct unic_sq *sq) +{ + struct unic_dev *unic_dev = netdev_priv(sq->netdev); + + return unic_dev->channels.sqebb_depth; +} + +static inline u16 unic_get_sqe_mask(struct unic_sq *sq) +{ + return unic_get_sqe_depth(sq) - 1; +} + +static u16 unic_get_spare_sqebb(struct unic_sq *sq) +{ + u16 sqe_depth = unic_get_sqe_depth(sq); + u32 pi = sq->pi; + + if (unlikely(sq->pi < sq->ci)) + pi += UNIC_SQEBB_POINT_REVERSE; + + return sqe_depth - (pi - sq->ci); +} + +static void unic_unmap_skb_buffer(struct unic_sq *sq, u32 info_index, + u8 skb_map_num, u16 sqebb_mask) +{ + struct unic_dev *unic_dev = netdev_priv(sq->netdev); + struct unic_sqe_ctrl_section *ctrl; + struct unic_sqe_sge_section *sge; + struct device *dev; + u8 i; + + ctrl = (struct unic_sqe_ctrl_section *)&sq->sqebb[info_index & sqebb_mask]; + sge = (struct unic_sqe_sge_section *)(ctrl + 1); + dev = &unic_dev->comdev.adev->dev; + dma_unmap_single(dev->parent, sge->address, sge->length, DMA_TO_DEVICE); + + for (i = UNIC_SKB_FRAGS_START_INDEX; i < skb_map_num; i++) { + if (!((i + UNIC_SQE_CTRL_SECTION_NUM) % UNIC_SQEBB_MAX_SGE_NUM)) { + info_index++; + sge = (struct unic_sqe_sge_section *) + &sq->sqebb[info_index & sqebb_mask]; + } else { + sge++; + } + + dma_unmap_page(dev->parent, sge->address, sge->length, DMA_TO_DEVICE); + } +} + +static void unic_consume_skb(struct sk_buff *skb, u32 info_index, + struct unic_sq *sq, int budget, u16 sqebb_mask) +{ + struct unic_sqe_ctrl_section *ctrl; + + ctrl = (struct unic_sqe_ctrl_section *)&sq->sqebb[info_index & sqebb_mask]; + unic_unmap_skb_buffer(sq, info_index, ctrl->sge_num, sqebb_mask); + + napi_consume_skb(skb, budget); +} + +static bool unic_check_hw_ci_valid(u16 hw_ci, u16 sq_ci, struct unic_sq *sq) +{ + u16 sqebb_mask = unic_get_sqe_mask(sq); + struct unic_sqe_ctrl_section *ctrl; + u16 sqebb_cnt, sw_ci; + + ctrl = (struct unic_sqe_ctrl_section *)&sq->sqebb[sq_ci & sqebb_mask]; + sqebb_cnt = unic_sqebb_cnt(ctrl->sge_num + UNIC_SQE_CTRL_SECTION_NUM); + sw_ci = sq_ci + sqebb_cnt - 1; + if (unlikely(sw_ci != hw_ci)) { + unic_sq_stats_inc(sq, ci_mismatch); + return false; + } + + return true; +} + +static void unic_reclaim_single_sqe_space(struct unic_sq *sq, u16 sqebb_mask, + u16 *sq_ci) +{ + struct unic_sqe_ctrl_section *ctrl; + u16 ci = *sq_ci & sqebb_mask; + u8 sge_num, sqebb_cnt; + + ctrl = (struct unic_sqe_ctrl_section *)&sq->sqebb[ci]; + sge_num = ctrl->sge_num; + sqebb_cnt = unic_sqebb_cnt(sge_num + UNIC_SQE_CTRL_SECTION_NUM); + if (unlikely(ci + sqebb_cnt - 1 > sqebb_mask)) { + memset(ctrl, 0, (sqebb_mask - ci + 1) * sizeof(struct unic_sqebb)); + memset(&sq->sqebb[0], 0, + (ci + sqebb_cnt - 1 - sqebb_mask) * sizeof(struct unic_sqebb)); + } else { + memset(ctrl, 0, sqebb_cnt * sizeof(struct unic_sqebb)); + } + + *sq_ci += sqebb_cnt; +} + +static void unic_flush_unused_sqe(struct unic_sq *sq, u16 sqebb_mask, + u16 *sq_ci) +{ + struct sk_buff *skb; + + while (*sq_ci != sq->pi) { + skb = sq->skbs[*sq_ci & sqebb_mask]; + unic_consume_skb(skb, *sq_ci, sq, 0, sqebb_mask); + unic_reclaim_single_sqe_space(sq, sqebb_mask, sq_ci); + } +} + +static bool unic_check_hw_ci_late(struct unic_sq *sq, u16 sq_ci) +{ + u32 effect_num, actual_num; + + if (likely(!sq->check_ci_late)) + return false; + + effect_num = sq->pi < sq->start_pi ? + (u32)sq->pi + UNIC_SQEBB_POINT_REVERSE - (u32)sq->start_pi : + (u32)sq->pi - (u32)sq->start_pi; + + actual_num = sq->pi < sq_ci ? + (u32)sq->pi + UNIC_SQEBB_POINT_REVERSE - (u32)sq_ci : + (u32)sq->pi - (u32)sq_ci; + + if (unlikely(actual_num > effect_num)) { + unic_sq_stats_inc(sq, drop_cnt); + return true; + } + + sq->check_ci_late = false; + return false; +} + +static bool unic_reclaim_sq_space(struct unic_sq *sq, int budget, u64 *bytes, + u64 *packets, bool clear) +{ + struct net_device *netdev = sq->netdev; + struct unic_dev *unic_dev = netdev_priv(netdev); + u8 jfc_shift = unic_dev->channels.sq_jfc_shift; + u16 sqebb_mask = unic_get_sqe_mask(sq); + union unic_cqe *cqe = sq->cq->cqe; + struct unic_cq *cq = sq->cq; + u32 cq_mask, cq_ci = cq->ci; + bool reclaimed = false; + struct sk_buff *skb; + u16 sq_ci = sq->ci; + + cq_mask = unic_get_sq_cqe_mask(unic_dev); + cqe = &cq->cqe[cq->ci & cq_mask]; + while (unic_cqe_owner_is_soft(jfc_shift, cq->ci, cqe->tx.owner)) { + trace_unic_tx_cqe(netdev, cq, sq->pi, sq_ci, cq_mask); + if (unlikely(cqe->tx.fd)) { + cq->ci++; + unic_sq_stats_inc(sq, fd_cnt); + unic_flush_unused_sqe(sq, sqebb_mask, &sq_ci); + reclaimed = true; + break; + } + + if (unlikely(!unic_check_hw_ci_valid(cqe->tx.raw_ci, sq_ci, sq))) + break; + + skb = sq->skbs[sq_ci & sqebb_mask]; + + if (!clear && likely(!unic_check_hw_ci_late(sq, sq_ci))) { + *bytes += skb->len; + (*packets)++; + } + + unic_consume_skb(skb, sq_ci, sq, budget, sqebb_mask); + unic_reclaim_single_sqe_space(sq, sqebb_mask, &sq_ci); + + reclaimed = true; + cq->ci++; + cqe = &cq->cqe[cq->ci & cq_mask]; + } + + unic_cq_doorbell(cq, cq_ci); + sq->ci = sq_ci; + return reclaimed; +} + +void unic_poll_tx(struct unic_sq *sq, int budget) +{ +#define UNIC_MIN_SPARE_SQEBB DIV_ROUND_UP(UNIC_SQE_CTRL_SECTION_NUM + \ + UNIC_SQE_MAX_SGE_NUM, \ + UNIC_SQEBB_MAX_SGE_NUM) + + struct net_device *netdev = sq->netdev; + struct netdev_queue *dev_queue; + struct unic_dev *unic_dev; + u64 packets = 0; + u64 bytes = 0; + + if (unlikely(!unic_reclaim_sq_space(sq, budget, &bytes, &packets, false))) + return; + + u64_stats_update_begin(&sq->syncp); + sq->stats.bytes += bytes; + sq->stats.packets += packets; + u64_stats_update_end(&sq->syncp); + + dev_queue = netdev_get_tx_queue(netdev, sq->queue_index); + netdev_tx_completed_queue(dev_queue, packets, bytes); + + if (unlikely(netif_carrier_ok(netdev) && + unic_get_spare_sqebb(sq) >= UNIC_MIN_SPARE_SQEBB)) { + unic_dev = netdev_priv(netdev); + if (netif_tx_queue_stopped(dev_queue) && + !test_bit(UNIC_STATE_DOWN, &unic_dev->state)) { + netif_tx_wake_queue(dev_queue); + unic_sq_stats_inc(sq, restart_queue); + } + } +} + +static u8 unic_sq_get_sl(struct unic_dev *unic_dev, u32 idx) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct ubase_adev_qos *qos = ubase_get_adev_qos(adev); + struct unic_vl *vl = &unic_dev->channels.vl; + int i; + + for (i = 0; i < unic_dev->channels.rss_vl_num; i++) + if (idx < vl->queue_offset[i] + vl->queue_count[i]) + break; + + return i >= unic_dev->channels.rss_vl_num ? + vl->vl_sl[qos->nic_vl[0]] : + vl->vl_sl[qos->nic_vl[i]]; +} + +static void unic_init_jfs_ctx(struct unic_dev *unic_dev, struct unic_sq *sq, + u32 idx, u32 tid) +{ + struct unic_jfs_ctx *ctx = &sq->jfs_ctx; + + ctx->ta_timeout = UNIC_TIMEOUT_8S; + ctx->type = UNIC_RAW_TYPE; + ctx->sqe_bb_shift = unic_dev->channels.sqebb_shift; + ctx->state = UNIC_JFS_STATE_READY; + ctx->sl = unic_sq_get_sl(unic_dev, idx) & UNIC_SQE_SL_BIT; + ctx->jfs_mode = UNIC_JFS; + ctx->sqe_token_id_l = tid & (u32)UNIC_SQE_TOKEN_ID_L_MASK; + ctx->sqe_token_id_h = (tid >> UNIC_SQE_TOKEN_ID_H_OFFSET) & + (u32)UNIC_SQE_TOKEN_ID_H_MASK; + ctx->sqe_base_addr_l = (sq->sqebb_dma_addr >> UNIC_SQE_VA0_OFFSET) & + UNIC_SQE_VA0_VALID_BIT; + ctx->sqe_base_addr_h = (sq->sqebb_dma_addr >> UNIC_SQE_VA1_OFFSET) & + UNIC_SQE_VA1_VALID_BIT; + ctx->tx_jfcn = idx; + ctx->sqe_pld_tokenid = tid; + ctx->avail_sgmt_ost = UNIC_AVAIL_SGMT_OST_INIT; +} + +static int unic_mbx_create_jfs_context(struct auxiliary_device *adev, + struct unic_sq *sq, u32 idx) +{ + struct ubase_cmd_mailbox *mailbox; + struct ubase_mbx_attr attr = {0}; + int ret; + + mailbox = ubase_alloc_cmd_mailbox(adev); + if (IS_ERR_OR_NULL(mailbox)) { + dev_err(adev->dev.parent, + "failed to alloc mailbox for create jfs context, idx = %u.\n", + idx); + return -ENOMEM; + } + + memcpy(mailbox->buf, &sq->jfs_ctx, sizeof(struct unic_jfs_ctx)); + ubase_fill_mbx_attr(&attr, idx, UBASE_MB_CREATE_JFS_CONTEXT, 0); + ret = ubase_hw_upgrade_ctx_ex(adev, &attr, mailbox); + if (ret) + dev_err(adev->dev.parent, + "failed to post create jfs ctx mbx, idx = %u, ret = %d.\n", + idx, ret); + + ubase_free_cmd_mailbox(adev, mailbox); + + return ret; +} + +static void unic_modify_jfs_state_to_error(struct auxiliary_device *adev, + u32 idx) +{ + struct unic_jfs_ctx *ctx, *ctx_mask; + struct ubase_cmd_mailbox *mailbox; + struct ubase_mbx_attr attr = {0}; + int ret; + + mailbox = ubase_alloc_cmd_mailbox(adev); + if (IS_ERR_OR_NULL(mailbox)) { + dev_err(adev->dev.parent, + "failed to alloc mbx for set jfs state.\n"); + return; + } + + ctx = (struct unic_jfs_ctx *)mailbox->buf; + ctx_mask = ctx + 1; + memset(ctx_mask, 0xff, sizeof(struct unic_jfs_ctx)); + ctx->state = UNIC_JFS_STATE_ERROR; + ctx_mask->state = 0; + + ubase_fill_mbx_attr(&attr, idx, UBASE_MB_MODIFY_JFS_CONTEXT, 0); + ret = ubase_hw_upgrade_ctx_ex(adev, &attr, mailbox); + if (ret) + dev_err(adev->dev.parent, + "failed to set jfs state, ret=%d.\n", ret); + + ubase_free_cmd_mailbox(adev, mailbox); +} + +static u16 unic_get_ta_timeout_ms(u8 ta_timeout) +{ +#define UNIC_DEFINE_128MS 128 +#define UNIC_DEFINE_1S 1000 +#define UNIC_DEFINE_8S 8000 +#define UNIC_DEFINE_64S 64000 + + switch (ta_timeout) { + case UNIC_TIMEOUT_128MS: + return UNIC_DEFINE_128MS; + case UNIC_TIMEOUT_1S: + return UNIC_DEFINE_1S; + case UNIC_TIMEOUT_8S: + return UNIC_DEFINE_8S; + default: + return UNIC_DEFINE_64S; + } +} + +static bool unic_jfs_schedule_complete(struct unic_jfs_ctx *ctx) +{ + u16 rcv_send_diff; + + rcv_send_diff = le16_to_cpu(ctx->next_rcv_ssn) - + le16_to_cpu(ctx->next_send_ssn); + if (ctx->state == UNIC_JFS_STATE_READY && ctx->PI == ctx->CI && + rcv_send_diff < UNIC_RCV_SEND_MAX_DIFF_VAL) + return true; + + if (ctx->state == UNIC_JFS_STATE_ERROR && + rcv_send_diff < UNIC_RCV_SEND_MAX_DIFF_VAL) + return true; + + return false; +} + +static int unic_jfs_flush_prepare(struct auxiliary_device *adev, u32 idx) +{ + struct ubase_cmd_mailbox *mailbox; + struct ubase_mbx_attr attr = {0}; + struct unic_jfs_ctx *ctx; + int ret; + + mailbox = ubase_alloc_cmd_mailbox(adev); + if (IS_ERR_OR_NULL(mailbox)) + return -ENOMEM; + + ubase_fill_mbx_attr(&attr, idx, UBASE_MB_QUERY_JFS_CONTEXT, 0); + + ret = ubase_hw_upgrade_ctx_ex(adev, &attr, mailbox); + if (ret) + goto out; + + ctx = (struct unic_jfs_ctx *)mailbox->buf; + if (!unic_jfs_schedule_complete(ctx)) + ret = -EBUSY; +out: + ubase_free_cmd_mailbox(adev, mailbox); + return ret; +} + +static inline bool unic_jfs_flush_ssn_vld(struct unic_jfs_ctx *ctx) +{ + u16 rcv_send_diff = le16_to_cpu(ctx->next_rcv_ssn) - + le16_to_cpu(ctx->next_send_ssn); + + return ctx->flush_ssn_vld && + (rcv_send_diff < UNIC_RCV_SEND_MAX_DIFF_VAL); +} + +static inline bool unic_jfs_flush_cqe_done(struct unic_jfs_ctx *ctx) +{ + return ctx->flush_cqe_done; +} + +static int unic_check_jfs_flush_done(struct auxiliary_device *adev, u32 idx, + bool (*check_fd)(struct unic_jfs_ctx *ctx)) +{ + struct ubase_cmd_mailbox *mailbox; + struct ubase_mbx_attr attr = {0}; + struct unic_jfs_ctx *ctx; + int ret; + + mailbox = ubase_alloc_cmd_mailbox(adev); + if (IS_ERR_OR_NULL(mailbox)) + return -ENOMEM; + + ubase_fill_mbx_attr(&attr, idx, UBASE_MB_QUERY_JFS_CONTEXT, 0); + + ret = ubase_hw_upgrade_ctx_ex(adev, &attr, mailbox); + if (ret) + goto out; + + ctx = (struct unic_jfs_ctx *)mailbox->buf; + if (!check_fd(ctx)) + ret = -EBUSY; + +out: + ubase_free_cmd_mailbox(adev, mailbox); + return ret; +} + +static void unic_multi_jfs_wait_flush(struct unic_dev *unic_dev, u32 num, + u16 timeout_ms, u32 start_idx) +{ +#define UNIC_WAIT_FLUSH_EVERY_STEP_TIME 50 +#define UNIC_WAIT_TIME_AFTER_FLUSH_DONE 100 + + struct auxiliary_device *adev = unic_dev->comdev.adev; + unsigned long end_jiffies, fd_bitmap = 0; + struct unic_channel *c; + bool timeout = false; + u32 i, fd_cnt = 0; + int ret; + + end_jiffies = jiffies + msecs_to_jiffies(timeout_ms); + while (!timeout) { + /* check the last result after waiting enough timeout */ + if (time_is_before_eq_jiffies(end_jiffies)) + timeout = true; + + for (i = 0; i < num; i++) { + c = &unic_dev->channels.c[i]; + if (test_bit(i, &fd_bitmap)) + continue; + + ret = unic_check_jfs_flush_done(adev, i + start_idx, + unic_jfs_flush_cqe_done); + if (ret) + continue; + + fd_cnt++; + set_bit(i, &fd_bitmap); + } + + if (fd_cnt == num) + break; + + msleep(UNIC_WAIT_FLUSH_EVERY_STEP_TIME); + } + + for (i = 0; i < num; i++) { + c = &unic_dev->channels.c[i]; + if (test_bit(i, &fd_bitmap)) + continue; + + ret = unic_check_jfs_flush_done(adev, i + start_idx, + unic_jfs_flush_ssn_vld); + if (ret) + dev_err(adev->dev.parent, + "wait jfs(%u) flush timeout, ret=%d.\n", + i + start_idx, ret); + } + + udelay(UNIC_WAIT_TIME_AFTER_FLUSH_DONE); +} + +static void unic_multi_jfs_flush_prepare(struct unic_dev *unic_dev, u32 num, + u32 timeout_ms, u32 start_idx) +{ +#define UNIC_WAIT_EVERY_STEP_TIME 50U + + struct auxiliary_device *adev = unic_dev->comdev.adev; + unsigned long end_jiffies, fd_bitmap = 0; + struct unic_channel *c; + bool timeout = false; + u32 i, fd_cnt = 0; + + end_jiffies = jiffies + msecs_to_jiffies(timeout_ms); + while (!timeout) { + /* check the last result after waiting enough timeout */ + if (time_is_before_eq_jiffies(end_jiffies)) + timeout = true; + + for (i = 0; i < num; i++) { + if (test_bit(i, &fd_bitmap)) + continue; + + c = &unic_dev->channels.c[i]; + c->ret = unic_jfs_flush_prepare(adev, i + start_idx); + if (c->ret) + continue; + + fd_cnt++; + set_bit(i, &fd_bitmap); + } + + if (fd_cnt == num) + return; + + msleep(UNIC_WAIT_EVERY_STEP_TIME); + } + + for (i = 0; i < num; i++) { + c = &unic_dev->channels.c[i]; + if (!test_bit(i, &fd_bitmap)) + dev_err(adev->dev.parent, + "wait jfs(%u) flush prepare timeout, ret=%d.\n", + i + start_idx, c->ret); + } +} + +static inline void unic_modify_multi_jfs_state(struct auxiliary_device *adev, + u32 num, u32 start_idx) +{ + u32 i; + + for (i = 0; i < num; i++) + unic_modify_jfs_state_to_error(adev, i + start_idx); +} + +static void unic_mbx_destroy_jfs_context(struct auxiliary_device *adev, u32 idx) +{ + struct ubase_cmd_mailbox *mailbox; + struct ubase_mbx_attr attr = {0}; + int ret; + + mailbox = ubase_alloc_cmd_mailbox(adev); + if (IS_ERR_OR_NULL(mailbox)) { + dev_err(adev->dev.parent, + "failed to alloc mbx for destroy jfs context, i=%u.\n", + idx); + goto out; + } + + ubase_fill_mbx_attr(&attr, idx, UBASE_MB_DESTROY_JFS_CONTEXT, 0); + ret = ubase_hw_upgrade_ctx_ex(adev, &attr, mailbox); + if (ret) + dev_err(adev->dev.parent, + "failed to post destroy jfs ctx mbx, ret=%d, i=%u.\n", + ret, idx); + +out: + ubase_free_cmd_mailbox(adev, mailbox); +} + +static inline void unic_destroy_multi_jfs_context(struct auxiliary_device *adev, + u32 num, u32 start_idx) +{ + u32 i; + + for (i = 0; i < num; i++) + unic_mbx_destroy_jfs_context(adev, i + start_idx); +} + +static void unic_destroy_multi_jfs(struct unic_dev *unic_dev, u32 num, + u32 start_idx) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + u32 timeout_ms; + +#if defined(UNIC_FPGA_COMPILE) + timeout_ms = unic_get_ta_timeout_ms(UNIC_TIMEOUT_64S); +#else + timeout_ms = unic_get_ta_timeout_ms(UNIC_TIMEOUT_8S); +#endif + + unic_multi_jfs_flush_prepare(unic_dev, num, timeout_ms, start_idx); + + unic_modify_multi_jfs_state(adev, num, start_idx); + + unic_multi_jfs_wait_flush(unic_dev, num, timeout_ms, start_idx); + + unic_destroy_multi_jfs_context(adev, num, start_idx); +} + +static int unic_sq_alloc_resource(struct auxiliary_device *adev, + struct unic_sq *sq, u32 sqebb_depth) +{ + u32 size = sqebb_depth * sizeof(struct unic_sqebb); + + sq->skbs = devm_kcalloc(&adev->dev, sqebb_depth, + sizeof(struct sk_buff *), GFP_KERNEL); + if (!sq->skbs) { + dev_err(adev->dev.parent, "failed to alloc unic sq skb buff.\n"); + return -ENOMEM; + } + + sq->sqebb = dma_alloc_coherent(adev->dev.parent, size, + &sq->sqebb_dma_addr, GFP_KERNEL); + if (!sq->sqebb) { + dev_err(adev->dev.parent, "failed to dma alloc unic sqebb.\n"); + goto err_alloc_unic_sqebb; + } + + return 0; + +err_alloc_unic_sqebb: + devm_kfree(&adev->dev, sq->skbs); + + return -ENOMEM; +} + +static void unic_sq_free_resource(struct auxiliary_device *adev, + struct unic_sq *sq, u32 sqebb_depth) +{ + u32 size = sqebb_depth * sizeof(struct unic_sqebb); + u16 sqebb_mask = unic_get_sqe_mask(sq); + + unic_flush_unused_sqe(sq, sqebb_mask, &sq->ci); + dma_free_coherent(adev->dev.parent, size, sq->sqebb, sq->sqebb_dma_addr); + devm_kfree(&adev->dev, sq->skbs); +} + +int unic_create_sq(struct unic_dev *unic_dev, u32 idx) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct ubase_resource_space *mem_base = ubase_get_mem_base(adev); + struct ubase_adev_caps *unic_caps = ubase_get_unic_caps(adev); + struct unic_channel *channel = &unic_dev->channels.c[idx]; + struct unic_sq *sq; + u32 jfs_start_idx; + u16 sqebb_depth; + u32 offset; + int ret; + + if (!unic_caps) { + dev_err(adev->dev.parent, "failed to get unic caps.\n"); + return -ENODATA; + } + + if (!mem_base) { + dev_err(adev->dev.parent, "failed to get mem base.\n"); + return -ENODATA; + } + + sq = devm_kzalloc(&adev->dev, sizeof(*sq), GFP_KERNEL); + if (!sq) { + dev_err(adev->dev.parent, "failed to alloc sq.\n"); + return -ENOMEM; + } + + sqebb_depth = unic_dev->channels.sqebb_depth; + ret = unic_sq_alloc_resource(adev, sq, sqebb_depth); + if (ret) + goto err_alloc_res; + + jfs_start_idx = unic_caps->jfs.start_idx; + sq->queue_index = idx & U16_MAX; + + /* The jfs doorbell offset must be consistent with that of the chip. */ + offset = UNIC_JFS_DB_4K_OFFSET; + sq->db_addr = mem_base->addr + UNIC_JFS_DB_BASE_OFFSET + + (idx + jfs_start_idx) * offset; + + sq->netdev = unic_dev->comdev.netdev; + sq->parent_dev = adev->dev.parent; + sq->check_ci_late = false; + + unic_init_jfs_ctx(unic_dev, sq, idx, unic_dev->tid); + + ret = unic_mbx_create_jfs_context(adev, sq, idx + jfs_start_idx); + if (ret) + goto err_mbx_create_jfs_context; + + channel->sq = sq; + return 0; + +err_mbx_create_jfs_context: + unic_sq_free_resource(adev, sq, sqebb_depth); +err_alloc_res: + devm_kfree(&adev->dev, sq); + return ret; +} + +static void unic_free_multi_sq_resource(struct unic_dev *unic_dev, u32 num) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct unic_channel *channel; + u32 i; + + for (i = 0; i < num; i++) { + channel = &unic_dev->channels.c[i]; + unic_sq_free_resource(adev, channel->sq, + unic_dev->channels.sqebb_depth); + devm_kfree(&adev->dev, channel->sq); + channel->sq = NULL; + } +} + +void unic_destroy_sq(struct unic_dev *unic_dev, u32 num) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct ubase_adev_caps *unic_caps = ubase_get_unic_caps(adev); + u32 jfs_start_idx; + + if (!num || !unic_caps) + return; + + jfs_start_idx = unic_caps->jfs.start_idx; + + if (!__unic_resetting(unic_dev)) + unic_destroy_multi_jfs(unic_dev, num, jfs_start_idx); + + unic_free_multi_sq_resource(unic_dev, num); +} + +#ifdef CONFIG_UB_UNIC_UBL +static int unic_apply_ub_pkt(struct unic_dev *unic_dev, struct unic_sq *sq, + struct sk_buff *skb) +{ + struct ublhdr *ubl = (struct ublhdr *)skb->data; + + if (unic_dev_ubl_supported(unic_dev)) { + if (unlikely(ubl->cfg == UB_NOIP_CFG_TYPE && + unic_is_port_down(unic_dev))) { + unic_sq_stats_inc(sq, cfg5_drop_cnt); + return -EIO; + } + + if (skb->protocol == htons(ETH_P_IP) || + skb->protocol == htons(ETH_P_IPV6)) + ubl->h_cc = htons(UNIC_CC_DEFAULT_FECN_MODE); + + ubl_rmv_sw_ctype(skb); + } + + return 0; +} +#endif + +static u32 unic_tx_need_sge_num(struct sk_buff *skb) +{ + u32 sge_num = 1, i, size; + skb_frag_t *frag; + + if (likely(skb->len <= UNIC_SGE_MAX_PAYLOAD && !skb_has_frag_list(skb))) + return skb_shinfo(skb)->nr_frags + 1U; + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + frag = &skb_shinfo(skb)->frags[i]; + size = skb_frag_size(frag); + if (!size) + continue; + + sge_num++; + } + + return sge_num; +} + +static int unic_maybe_stop_tx(struct net_device *netdev, struct unic_sq *sq, + u32 sge_num) +{ + u32 sqebb_num = unic_sqebb_cnt(sge_num + UNIC_SQE_CTRL_SECTION_NUM); + struct unic_dev *unic_dev = netdev_priv(netdev); + u16 spare_num = unic_get_spare_sqebb(sq); + + if (unlikely(sge_num > UNIC_SQE_MAX_SGE_NUM)) { + unic_sq_stats_inc(sq, over_max_sge_num); + return -ENOMEM; + } + + if (likely(spare_num >= sqebb_num)) + return 0; + + netif_stop_subqueue(netdev, sq->queue_index); + smp_mb(); /* Memory barrier before checking sqebb space */ + + if (unlikely(netif_carrier_ok(netdev) && + !test_bit(UNIC_STATE_DOWN, &unic_dev->state) && + unic_get_spare_sqebb(sq) > sqebb_num)) { + netif_start_subqueue(netdev, sq->queue_index); + return 0; + } + + unic_sq_stats_inc(sq, busy); + + return -EBUSY; +} + +static void unic_fill_ctrl_owner(struct unic_sq *sq, + struct unic_sqe_ctrl_section *ctrl) +{ + struct unic_dev *unic_dev = netdev_priv(sq->netdev); + u16 sqebb_shift = unic_dev->channels.sqebb_shift; + + ctrl->owner = ~(sq->pi >> sqebb_shift & 0x1); +} + +static void unic_fill_ctrl_l3_info(struct unic_sqe_ctrl_section *ctrl, + struct sk_buff *skb, struct unic_sq *sq) +{ +#define be32_to_le32(x) cpu_to_le32(be32_to_cpu(x)) + + struct ipv6hdr *ip6_hdr; + + if (skb->protocol == htons(ETH_P_IP)) { + ctrl->l3_type = UNIC_L3_TYPE_IPV4; + ctrl->dip_type = UNIC_L3_TYPE_IPV4; + ctrl->dip0 = be32_to_le32(ip_hdr(skb)->daddr); + } else if (skb->protocol == htons(ETH_P_IPV6)) { + ip6_hdr = ipv6_hdr(skb); + ctrl->l3_type = UNIC_L3_TYPE_IPV6; + ctrl->dip_type = UNIC_L3_TYPE_IPV6; + ctrl->dip0 = be32_to_le32(ip6_hdr->daddr.s6_addr32[3]); + ctrl->dip1 = be32_to_le32(ip6_hdr->daddr.s6_addr32[2]); + ctrl->dip2 = be32_to_le32(ip6_hdr->daddr.s6_addr32[1]); + ctrl->dip3 = be32_to_le32(ip6_hdr->daddr.s6_addr32[0]); + } else { + ctrl->l3_type = UNIC_L3_TYPE_NON_IP; + } +} + +static u8 unic_get_l4_protocol(struct sk_buff *skb, struct unic_sq *sq) +{ + u8 l4_proto, *l4hdr, *exthdr; + union l3_hdr_info l3hdr; + __be16 frag_off; + + l3hdr.hdr = skb_network_header(skb); + l4hdr = skb_transport_header(skb); + + if (skb->protocol == htons(ETH_P_IP)) { + l4_proto = l3hdr.v4->protocol; + } else { + exthdr = l3hdr.hdr + sizeof(*l3hdr.v6); + l4_proto = l3hdr.v6->nexthdr; + if (l4hdr != exthdr) + ipv6_skip_exthdr(skb, exthdr - skb->data, &l4_proto, + &frag_off); + } + + return l4_proto; +} + +static void unic_set_l2(struct sk_buff *skb, struct unic_sqe_ctrl_section *ctrl, + u8 *l2_hdr, u8 *l3_hdr, struct unic_dev *unic_dev) +{ +#define UNIC_L2_LEN_SUPPLEMENT 4 + + u32 l2_len = l3_hdr - l2_hdr; + + /* skb->data not include the first four bytes of the ub link header. + * But l2_len need include the length of complete ub link. + */ + if (unic_dev_ubl_supported(unic_dev)) + l2_len += UNIC_L2_LEN_SUPPLEMENT; + + /* compute L2 header size, defined in 2 Bytes */ + ctrl->l2hdr_len = cpu_to_le32(l2_len >> UNIC_HEADER_LEN_2B_OFFSET); +} + +static void unic_set_l3(struct sk_buff *skb, struct unic_sqe_ctrl_section *ctrl, + union l3_hdr_info l3, u8 *l4_hdr) +{ + u32 l3_len = l4_hdr - l3.hdr; + + /* compute L3 header size, defined in 4 Bytes */ + ctrl->l3hdr_len = cpu_to_le32(l3_len >> UNIC_HEADER_LEN_4B_OFFSET); + + if (l3.v4->version == IP_VERSION_IPV4) + ctrl->l3_csum = skb_is_gso(skb) ? UNIC_CSUM_OFFLOAD_ENABLE : + UNIC_CSUM_OFFLOAD_DISABLE; +} + +static int unic_set_l4(struct sk_buff *skb, struct unic_sqe_ctrl_section *ctrl, + union l4_hdr_info l4_hdr, u8 l4_proto, + struct unic_dev *unic_dev) +{ + switch (l4_proto) { + case IPPROTO_TCP: + ctrl->l4_csum = UNIC_CSUM_OFFLOAD_ENABLE; + ctrl->l4_type = UNIC_L4_TYPE_TCP; + ctrl->l4hdr_len = l4_hdr.tcp->doff; + break; + case IPPROTO_UDP: + ctrl->l4_csum = UNIC_CSUM_OFFLOAD_ENABLE; + ctrl->l4_type = UNIC_L4_TYPE_UDP; + /* compute inner/normal header size, defined in 4 Bytes */ + ctrl->l4hdr_len = sizeof(*l4_hdr.udp) >> + UNIC_HEADER_LEN_4B_OFFSET; + break; + default: + /* drop the skb tunnel packet if hardware don't support, + * because hardware can't calculate csum when TSO. + */ + if (skb_is_gso(skb)) { + unic_err(unic_dev, + "unknown l4 header tso packets hecksum offload.\n"); + return -EDOM; + } + /* the stack computes the IP header already, + * driver calculate l4 checksum when not TSO. + */ + return skb_checksum_help(skb); + } + + return 0; +} + +static int unic_set_l2l3l4(struct sk_buff *skb, u8 l4_proto, + struct unic_sqe_ctrl_section *ctrl, + struct net_device *netdev) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + u8 *l2_hdr = skb->data; + union l4_hdr_info l4; + union l3_hdr_info l3; + + l3.hdr = skb_network_header(skb); + l4.hdr = skb_transport_header(skb); + + unic_set_l2(skb, ctrl, l2_hdr, l3.hdr, unic_dev); + unic_set_l3(skb, ctrl, l3, l4.hdr); + return unic_set_l4(skb, ctrl, l4, l4_proto, unic_dev); +} + +static int unic_handle_csum_partial(struct unic_sqe_ctrl_section *ctrl, + struct sk_buff *skb, struct unic_sq *sq) +{ + struct net_device *netdev = sq->netdev; + u8 l4_proto; + int ret; + + if (skb->ip_summed != CHECKSUM_PARTIAL) + return 0; + + l4_proto = unic_get_l4_protocol(skb, sq); + ret = unic_set_l2l3l4(skb, l4_proto, ctrl, netdev); + if (unlikely(ret < 0)) { + unic_sq_stats_inc(sq, csum_err); + return ret; + } + + return 0; +} + +static int unic_handle_vlan_info(struct unic_sq *sq, struct sk_buff *skb) +{ + struct vlan_ethhdr *vhdr; + int ret; + + if (!(skb->protocol == htons(ETH_P_8021Q) || skb_vlan_tag_present(skb))) + return 0; + + ret = skb_cow_head(skb, 0); + if (unlikely(ret < 0)) { + unic_sq_stats_inc(sq, vlan_err); + return ret; + } + + vhdr = skb_vlan_eth_hdr(skb); + vhdr->h_vlan_TCI |= cpu_to_be16((skb->priority << VLAN_PRIO_SHIFT) & + VLAN_PRIO_MASK); + + skb->protocol = vlan_get_protocol(skb); + + return 0; +} + +static int unic_handle_ctrl_section(struct sk_buff *skb, struct unic_sq *sq, + struct unic_sqe_ctrl_section *ctrl, + u8 sge_num) +{ + int ret; + + ret = unic_handle_vlan_info(sq, skb); + if (unlikely(ret)) + goto vlan_err; + + unic_fill_ctrl_l3_info(ctrl, skb, sq); + + ret = unic_handle_csum_partial(ctrl, skb, sq); + if (unlikely(ret)) + goto csum_err; + + unic_fill_ctrl_owner(sq, ctrl); + + ctrl->sge_num = sge_num; + + return 0; + +csum_err: + memset(ctrl, 0, sizeof(*ctrl)); +vlan_err: + return ret; +} + +static inline void unic_fill_sge_section(struct unic_sqe_sge_section *sge, u64 addr, + u32 length, u32 tid) +{ + sge->length = length; + sge->token_id = tid; + sge->address = addr; +} + +static inline struct unic_sqe_sge_section * +unic_get_next_sge(u8 *sec_num, struct unic_sqe_sge_section *sge, + struct unic_sq *sq, u16 sqebb_mask) +{ + (*sec_num)++; + if (*sec_num >= UNIC_SQEBB_MAX_SGE_NUM) { + sq->pi++; + *sec_num = 0; + sge = (struct unic_sqe_sge_section *) + &sq->sqebb[sq->pi & sqebb_mask]; + } else { + sge++; + } + + return sge; +} + +static int unic_map_skb_frags(struct unic_sq *sq, struct sk_buff *skb, + u8 *skb_map_num, u8 *sec_num, + struct unic_sqe_sge_section *sge) +{ + struct net_device *netdev = sq->netdev; + u16 sqebb_mask = unic_get_sqe_mask(sq); + struct unic_dev *unic_dev; + struct device *dev; + skb_frag_t *frag; + dma_addr_t dma; + u32 size, i; + + unic_dev = netdev_priv(netdev); + dev = &unic_dev->comdev.adev->dev; + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + frag = &skb_shinfo(skb)->frags[i]; + size = skb_frag_size(frag); + if (!size) + continue; + + dma = skb_frag_dma_map(dev->parent, frag, 0, size, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev, dma))) { + unic_sq_stats_inc(sq, map_err); + return -ENOMEM; + } + + sge = unic_get_next_sge(sec_num, sge, sq, sqebb_mask); + unic_fill_sge_section(sge, (u64)dma, size, unic_dev->tid); + + (*skb_map_num)++; + } + + return 0; +} + +static void unic_clear_sge_section(struct unic_sq *sq, u16 idx, u8 skb_map_num, + u16 sqebb_mask) +{ + struct unic_sqe_ctrl_section *ctrl; + u16 index = idx & sqebb_mask; + u8 sqebb_cnt; + + ctrl = (struct unic_sqe_ctrl_section *)&sq->sqebb[index]; + sqebb_cnt = unic_sqebb_cnt(skb_map_num + UNIC_SQE_CTRL_SECTION_NUM); + if (unlikely(index + sqebb_cnt - 1 > sqebb_mask)) { + memset(ctrl, 0, (sqebb_mask - index + 1) * sizeof(struct unic_sqebb)); + memset(&sq->sqebb[0], 0, + (index + sqebb_cnt - 1 - sqebb_mask) * sizeof(struct unic_sqebb)); + } else { + memset(ctrl, 0, sqebb_cnt * sizeof(struct unic_sqebb)); + } +} + +static int unic_handle_sge_section(struct unic_sq *sq, struct sk_buff *skb, + struct unic_sqe_sge_section *sge, + u16 sqebb_mask) +{ + u8 sec_num = UNIC_SQE_CTRL_SECTION_NUM; + struct net_device *netdev = sq->netdev; + struct unic_dev *unic_dev; + struct device *dev; + u8 skb_map_num = 0; + u16 pi = sq->pi; + dma_addr_t dma; + u32 size; + + size = skb_headlen(skb); + unic_dev = netdev_priv(netdev); + dev = &unic_dev->comdev.adev->dev; + + dma = dma_map_single(dev->parent, skb->data, size, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev, dma))) { + unic_sq_stats_inc(sq, map_err); + return -ENOMEM; + } + + unic_fill_sge_section(sge, (u64)dma, size, unic_dev->tid); + skb_map_num++; + + if (unlikely(unic_map_skb_frags(sq, skb, &skb_map_num, &sec_num, sge))) + goto dma_map_err; + + sq->pi = pi; + return 0; + +dma_map_err: + unic_unmap_skb_buffer(sq, pi, skb_map_num, sqebb_mask); + unic_clear_sge_section(sq, pi, skb_map_num, sqebb_mask); + sq->pi = pi; + return -ENOMEM; +} + +static int unic_handle_sqe(struct sk_buff *skb, struct unic_sq *sq, u8 sge_num) +{ + u16 sqebb_mask = unic_get_sqe_mask(sq); + struct unic_sqe_ctrl_section *ctrl; + struct unic_sqe_sge_section *sge; + int ret; + + ctrl = (struct unic_sqe_ctrl_section *)&sq->sqebb[sq->pi & sqebb_mask]; + ret = unic_handle_ctrl_section(skb, sq, ctrl, sge_num); + if (unlikely(ret)) + return ret; + + sge = (struct unic_sqe_sge_section *)(ctrl + 1); + ret = unic_handle_sge_section(sq, skb, sge, sqebb_mask); + if (unlikely(ret)) + return ret; + + sq->skbs[sq->pi & sqebb_mask] = skb; + + return ret; +} + +static void unic_tx_doorbell(struct unic_sq *sq, u16 sge_num, bool doorbell) +{ + u16 sqebb_num = unic_sqebb_cnt(sge_num + UNIC_SQE_CTRL_SECTION_NUM); + u16 sqebb_mask = unic_get_sqe_mask(sq); + struct unic_jfs_db jfs_db = {0}; + + trace_unic_tx_sqe(sq, sqebb_num, sqebb_mask, doorbell); + + sq->pi += sqebb_num; + + if (!doorbell) { + unic_sq_stats_inc(sq, more); + return; + } + + jfs_db.pi = cpu_to_le16(sq->pi); + writel(*(u32 *)&jfs_db, sq->db_addr); + sq->last_pi = sq->pi; +} + +static void unic_tx_compensate_doorbell(struct unic_sq *sq) +{ + struct unic_jfs_db jfs_db = {0}; + + if (sq->last_pi == sq->pi) + return; + + trace_unic_tx_sqe(sq, 0, unic_get_sqe_mask(sq), true); + jfs_db.pi = cpu_to_le16(sq->pi); + writel(*(u32 *)&jfs_db, sq->db_addr); + sq->last_pi = sq->pi; +} + +netdev_tx_t unic_start_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + struct netdev_queue *dev_queue; + struct unic_sq *sq; + bool doorbell; + u32 sge_num; + int ret; + + sq = unic_dev->channels.c[skb->queue_mapping].sq; + + if (!unic_dev_ubl_supported(unic_dev) && + skb_put_padto(skb, UNIC_MIN_TX_LEN)) { + unic_sq_stats_inc(sq, pad_err); + goto xmit_drop_pkt; + } + + /* Prefetch the data used later */ + prefetch(skb->data); + + sge_num = unic_tx_need_sge_num(skb); + ret = unic_maybe_stop_tx(netdev, sq, sge_num); + if (unlikely(ret < 0)) { + if (ret == -EBUSY) { + unic_tx_compensate_doorbell(sq); + return NETDEV_TX_BUSY; + } + + unic_err(unic_dev, "unic stop tx, ret = %d.\n", ret); + goto xmit_drop_pkt; + } + +#ifdef CONFIG_UB_UNIC_UBL + if (unlikely(unic_apply_ub_pkt(unic_dev, sq, skb))) + goto xmit_drop_pkt; +#endif + + ret = unic_handle_sqe(skb, sq, sge_num); + if (unlikely(ret)) + goto xmit_drop_pkt; + + dev_queue = netdev_get_tx_queue(netdev, sq->queue_index); + doorbell = __netdev_tx_sent_queue(dev_queue, skb->len, + netdev_xmit_more()); + + unic_tx_doorbell(sq, sge_num, doorbell); + + return NETDEV_TX_OK; + +xmit_drop_pkt: + unic_tx_compensate_doorbell(sq); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +void unic_clear_sq(struct unic_sq *sq) +{ + unic_reclaim_sq_space(sq, 0, NULL, NULL, true); + sq->start_pi = sq->pi; + sq->check_ci_late = true; +} + +void unic_reset_tx_queue(struct net_device *netdev) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + struct netdev_queue *dev_queue; + struct unic_sq *sq; + u32 i; + + if (!unic_dev->channels.c) + return; + + for (i = 0; i < unic_dev->channels.num; i++) { + sq = unic_dev->channels.c[i].sq; + dev_queue = netdev_get_tx_queue(netdev, sq->queue_index); + netdev_tx_reset_queue(dev_queue); + } +} + +void unic_dump_sq_stats(struct net_device *netdev, u32 queue_idx) +{ + struct netdev_queue *queue = netdev_get_tx_queue(netdev, queue_idx); + struct unic_dev *unic_dev = netdev_priv(netdev); + struct unic_sq_stats *sq_stats; + struct unic_channel *channel; + struct unic_sq *sq; + + channel = &unic_dev->channels.c[queue_idx]; + sq = channel->sq; + sq_stats = &sq->stats; + + unic_info(unic_dev, + "tx timeout, queue index: %u, state: %lu\n" + "sq->pi: %u\n" + "sq->ci: %u\n" + "pad_err: %llu\n" + "bytes: %llu\n" + "packets: %llu\n" + "map_err: %llu\n" + "busy: %llu\n" + "more: %llu\n" + "restart_queue: %llu\n" + "over_max_sge_num: %llu\n" + "csum_err: %llu\n" + "ci_mismatch: %llu\n" + "fd_cnt: %llu\n" + "drop_cnt: %llu\n" + "cfg5_drop_cnt: %llu\n", + queue_idx, queue->state, sq->pi, sq->ci, + sq_stats->pad_err, sq_stats->bytes, sq_stats->packets, + sq_stats->map_err, sq_stats->busy, sq_stats->more, + sq_stats->restart_queue, sq_stats->over_max_sge_num, + sq_stats->csum_err, sq_stats->ci_mismatch, sq_stats->fd_cnt, + sq_stats->drop_cnt, sq_stats->cfg5_drop_cnt); +} + +void unic_mask_key_words(void *sqebb) +{ + struct unic_sqe_ctrl_section *ctrl; + struct unic_sqe_sge_section *sge; + u16 i; + + ctrl = (struct unic_sqe_ctrl_section *)sqebb; + sge = (struct unic_sqe_sge_section *)(ctrl + 1); + for (i = 0; i < ctrl->sge_num; i++) { + sge->address = 0; + sge++; + } +} diff --git a/drivers/net/ub/unic/unic_tx.h b/drivers/net/ub/unic/unic_tx.h new file mode 100644 index 000000000000..ff74c6bfda2f --- /dev/null +++ b/drivers/net/ub/unic/unic_tx.h @@ -0,0 +1,305 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_TX_H__ +#define __UNIC_TX_H__ + +#include + +struct unic_dev; + +enum unic_jfs_ctx_mode { + UNIC_JFS, + UNIC_JETTY, +}; + +#define UNIC_RAW_TYPE 0 +#define UNIC_RAW_SQEBB_SIZE 64 + +#define UNIC_SQE_VA0_OFFSET 12 +#define UNIC_SQE_VA0_VALID_BIT GENMASK(19, 0) +#define UNIC_SQE_VA1_OFFSET 32 +#define UNIC_SQE_VA1_VALID_BIT GENMASK(31, 0) +#define UNIC_SQE_TOKEN_ID_L_MASK GENMASK(11, 0) +#define UNIC_SQE_TOKEN_ID_H_OFFSET 12U +#define UNIC_SQE_TOKEN_ID_H_MASK GENMASK(7, 0) +#define UNIC_SQE_SL_BIT GENMASK(3, 0) + +#define UNIC_JFS_DB_BASE_OFFSET 0x1080 +#define UNIC_JFS_DB_4K_OFFSET 0x1000 +#define UNIC_JFS_DB_64K_OFFSET 0x10000 + +#define UNIC_PAGE_SIZE_4K 4096 +#define UNIC_AVAIL_SGMT_OST_INIT 512 + +#define unic_sq_stats_inc(sq, cnt) do { \ + typeof(sq) _sq = (sq); \ + u64_stats_update_begin(&(_sq)->syncp); \ + ((_sq)->stats.cnt)++; \ + u64_stats_update_end(&(_sq)->syncp); \ + } while (0) + +union l3_hdr_info { + struct iphdr *v4; + struct ipv6hdr *v6; + u8 *hdr; +}; + +union l4_hdr_info { + struct tcphdr *tcp; + struct udphdr *udp; + u8 *hdr; +}; + +enum unic_pkt_l4_type { + UNIC_L4_TYPE_UNKNOWN, + UNIC_L4_TYPE_TCP, + UNIC_L4_TYPE_UDP, + UNIC_L4_TYPE_RESERVE +}; + +enum unic_pkt_tun_type { + UNIC_TUN_NONE, + UNIC_TUN_UDP, + UNIC_TUN_NVGRE, + UNIC_TUN_OTHER +}; + +enum unic_jfs_ta_timeout { + UNIC_TIMEOUT_128MS, + UNIC_TIMEOUT_1S, + UNIC_TIMEOUT_8S, + UNIC_TIMEOUT_64S +}; + +enum unic_jfs_state { + UNIC_JFS_STATE_RESET, + UNIC_JFS_STATE_READY, + UNIC_JFS_STATE_ERROR, + UNIC_JFS_STATE_SUSPEND, + UNIC_JFS_STATE_RESERVED +}; + +struct unic_sq_stats { + u64 pad_err; + u64 bytes; + u64 packets; + u64 map_err; + u64 busy; + u64 more; + u64 restart_queue; + u64 over_max_sge_num; + u64 csum_err; + u64 ci_mismatch; + u64 vlan_err; + u64 fd_cnt; + u64 drop_cnt; + u64 cfg5_drop_cnt; +}; + +struct unic_jfs_ctx { + /* DW0 */ + u32 ta_timeout : 2; + u32 rnr_retry_num : 3; + u32 type : 3; + u32 sqe_bb_shift : 4; + u32 sl : 4; + u32 state : 3; + u32 jfs_mode : 1; + u32 sqe_token_id_l : 12; + /* DW1 */ + u32 sqe_token_id_h : 8; + u32 err_mode : 1; + u32 rsv0 : 1; + u32 cmp_odr : 1; + u32 rsv1 : 1; + u32 sqe_base_addr_l : 20; + /* DW2 */ + u32 sqe_base_addr_h; + /* DW3 */ + u32 rsv2; + /* DW4 */ + u32 tx_jfcn : 20; + u32 jfrn : 12; + /* DW5 */ + u32 jfrn1 : 8; + u32 rsv3 : 4; + u32 rx_jfcn : 20; + /* DW6 */ + u32 seid_idx : 10; + u32 pi_type : 1; + u32 rsv4 : 21; + /* DW7 */ + u32 user_data_l; + /* DW8 */ + u32 user_data_h; + /* DW9 */ + u32 sqe_position : 1; + u32 sqe_pld_position : 1; + u32 sqe_pld_tokenid : 20; + u32 rsv5 : 10; + /* DW10 */ + u32 tpn : 24; + u32 rsv6 : 8; + /* DW11 */ + u32 rmt_eid : 20; + u32 rsv7 : 12; + /* DW12 */ + u32 rmt_tokenid : 20; + u32 rsv8 : 12; + /* DW13 - DW15 */ + u32 rsv8_1[3]; + /* DW16 */ + u32 next_send_ssn : 16; + u32 src_order_wqe : 16; + /* DW17 */ + u32 src_order_ssn : 16; + u32 src_order_sgme_cnt : 16; + /* DW18 */ + u32 src_order_sgme_send_cnt : 16; + u32 CI : 16; + /* DW19 */ + u32 wqe_sgmt_send_cnt : 20; + u32 src_order_wqebb_num : 4; + u32 src_order_wqe_vld : 1; + u32 no_wqe_send_cnt : 4; + u32 so_lp_vld : 1; + u32 fence_lp_vld : 1; + u32 strong_fence_lp_vld : 1; + /* DW20 */ + u32 PI : 16; + u32 sq_db_doing : 1; + u32 ost_rce_credit : 15; + /* DW21 */ + u32 sq_db_retrying : 1; + u32 wmtp_rsv0 : 31; + /* DW22 */ + u32 wait_ack_timeout : 1; + u32 wait_rnr_timeout : 1; + u32 cqe_ie : 1; + u32 cqe_sz : 1; + u32 wml_rsv0 : 28; + /* DW23 */ + u32 wml_rsv1 : 32; + /* DW24 */ + u32 next_rcv_ssn : 16; + u32 next_cpl_bb_idx : 16; + /* DW25 */ + u32 next_cpl_sgmt_num : 20; + u32 we_rsv0 : 12; + /* DW26 */ + u32 next_cpl_bb_num : 4; + u32 next_cpl_cqe_en : 1; + u32 next_cpl_info_vld : 1; + u32 rpting_cqe : 1; + u32 not_rpt_cqe : 1; + u32 flush_ssn : 16; + u32 flush_ssn_vld : 1; + u32 flush_vld : 1; + u32 flush_cqe_done : 1; + u32 we_rsv1 : 5; + /* DW27 */ + u32 rcved_cont_ssn_num : 20; + u32 we_rsv2 : 12; + /* DW28 */ + u32 sq_timer; + /* DW29 */ + u32 rnr_cnt : 3; + u32 abt_ssn : 16; + u32 abt_ssn_vld : 1; + u32 taack_timeout_flag : 1; + u32 we_rsv3 : 9; + u32 err_type_l : 2; + /* DW30 */ + u32 err_type_h : 7; + u32 sq_flush_ssn : 16; + u32 we_rsv4 : 9; + /* DW31 */ + u32 avail_sgmt_ost : 10; + u32 read_op_cnt : 10; + u32 we_rsv5 : 12; +}; + +struct unic_sqe_ctrl_section { + __le16 sqe_bd_idx; + __le16 l3_type : 2; + __le16 l4_type : 2; + __le16 l3_csum : 1; + __le16 l4_csum : 1; + __le16 ol3_type : 2; + __le16 ol4_csum : 1; + __le16 tun_type : 4; + __le16 tsyn : 1; + __le16 tso : 1; + __le16 owner : 1; + + u8 l2hdr_len; + u8 l3hdr_len; + u8 l4hdr_len; + u8 sge_num : 5; + u8 rsv1 : 2; + u8 dip_type : 1; + + u8 ol2hdr_len; + u8 ol3hdr_len; + u8 ol4hdr_len; + u8 rsv2; + + __le32 mss : 14; + __le32 rsv3 : 18; + + __le32 dip0; + __le32 dip1; + __le32 dip2; + __le32 dip3; +}; + +struct unic_sqe_sge_section { + __le32 length; + __le32 token_id : 20; + __le32 rsv : 12; + __le64 address; +}; + +struct unic_sqebb { + u64 data[8]; +}; + +struct unic_jfs_db { + __le16 pi; + __le16 rsv; +}; + +struct unic_sq { + void __iomem *db_addr; + struct device *parent_dev; + struct unic_sq_stats stats; + struct u64_stats_sync syncp; + struct unic_jfs_ctx jfs_ctx; + struct unic_sqebb *sqebb; + dma_addr_t sqebb_dma_addr; + struct sk_buff **skbs; + struct unic_cq *cq; + u16 pi; /* the start of next to product */ + u16 ci; /* the start of next to consume */ + u16 last_pi; + u16 start_pi; + bool check_ci_late; + u16 queue_index; + struct net_device *netdev; +}; + +void unic_poll_tx(struct unic_sq *sq, int budget); +int unic_create_sq(struct unic_dev *unic_dev, u32 idx); +void unic_destroy_sq(struct unic_dev *unic_dev, u32 num); + +netdev_tx_t unic_start_xmit(struct sk_buff *skb, struct net_device *netdev); +void unic_clear_sq(struct unic_sq *sq); +void unic_reset_tx_queue(struct net_device *netdev); +void unic_dump_sq_stats(struct net_device *netdev, u32 queue_idx); +void unic_mask_key_words(void *sqebb); + +#endif diff --git a/drivers/net/ub/unic/unic_txrx.c b/drivers/net/ub/unic/unic_txrx.c new file mode 100644 index 000000000000..41017608fc51 --- /dev/null +++ b/drivers/net/ub/unic/unic_txrx.c @@ -0,0 +1,372 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#define dev_fmt(fmt) "unic: (pid %d) " fmt, current->pid + +#include + +#include "unic_dev.h" +#include "unic_txrx.h" + +static u16 unic_get_cqe_period(u16 cqe_period) +{ + u16 period[] = { + UNIC_CQE_PERIOD_0, + UNIC_CQE_PERIOD_4, + UNIC_CQE_PERIOD_16, + UNIC_CQE_PERIOD_64, + UNIC_CQE_PERIOD_256, + UNIC_CQE_PERIOD_1024, + UNIC_CQE_PERIOD_4096, + UNIC_CQE_PERIOD_16384 + }; + u16 i; + + for (i = 0; i < ARRAY_SIZE(period); ++i) { + if (cqe_period == period[i]) + return i; + } + + return 0; +} + +static void unic_init_jfc_ctx(struct unic_cq *cq, u8 jfc_shift, u32 tid, + u16 cqe_coal_cnt, u16 cqe_coal_period) +{ + struct unic_jfc_ctx *ctx = &cq->jfc_ctx; + + ctx->state = UNIC_JFC_STATE_VALID; + ctx->arm_st = UNIC_CTX_ALWAY_ARMED; + ctx->shift = jfc_shift - UNIC_CTX_SHIFT_BASE; + ctx->record_db_en = 0; + ctx->jfc_type = UNIC_RAW_JFC_TYPE; + ctx->inline_en = UNIC_NO_INLINE_DATA; + ctx->cqe_base_addr_l = (cq->cqe_dma_addr >> UNIC_CQE_VA_L_OFFSET) & + UNIC_CQE_VA_L_VALID_BIT; + ctx->cqe_base_addr_h = (cq->cqe_dma_addr >> UNIC_CQE_VA_H_OFFSET) & + UNIC_CQE_VA_H_VALID_BIT; + ctx->queue_token_id = tid; + ctx->cq_cnt_mode = UNIC_CQE_CNT_MODE_BY_COUNT; + ctx->ceqn = 0; + ctx->cqe_coalesce_cnt = cqe_coal_cnt; + ctx->cqe_coalesce_period = unic_get_cqe_period(cqe_coal_period); +} + +static int unic_cq_alloc_resource(struct auxiliary_device *adev, + struct unic_cq *cq, u32 cqe_depth) +{ + u32 size = cqe_depth * unic_get_cqe_size(); + + cq->cqe = dma_alloc_coherent(adev->dev.parent, size, + &cq->cqe_dma_addr, GFP_KERNEL); + if (!cq->cqe) { + dev_err(adev->dev.parent, "failed to dma alloc unic cqe.\n"); + return -ENOMEM; + } + + return 0; +} + +static void unic_cq_free_resource(struct auxiliary_device *adev, + struct unic_cq *cq, u32 cqe_depth) +{ + u32 size = cqe_depth * unic_get_cqe_size(); + union unic_cqe *cqe = cq->cqe; + + dma_free_coherent(adev->dev.parent, size, cqe, cq->cqe_dma_addr); +} + +static int unic_mbx_create_jfc_context(struct auxiliary_device *adev, + struct unic_cq *cq, u32 jfcn) +{ + struct ubase_cmd_mailbox *mailbox; + struct ubase_mbx_attr attr = {0}; + int ret; + + mailbox = ubase_alloc_cmd_mailbox(adev); + if (IS_ERR_OR_NULL(mailbox)) { + dev_err(adev->dev.parent, + "failed to alloc mailbox for create jfc context, jfcn = %u.\n", + jfcn); + return -ENOMEM; + } + + memcpy(mailbox->buf, &cq->jfc_ctx, sizeof(struct unic_jfc_ctx)); + ubase_fill_mbx_attr(&attr, jfcn, UBASE_MB_CREATE_JFC_CONTEXT, 0); + ret = ubase_hw_upgrade_ctx_ex(adev, &attr, mailbox); + if (ret) + dev_err(adev->dev.parent, + "failed to post create jfc ctx mbx, jfcn = %u, ret = %d\n", + jfcn, ret); + + ubase_free_cmd_mailbox(adev, mailbox); + return ret; +} + +static int unic_post_destroy_jfc_ctx(struct auxiliary_device *adev, u32 jfcn) +{ + struct ubase_cmd_mailbox *mailbox; + struct ubase_mbx_attr attr = {0}; + int ret; + + mailbox = ubase_alloc_cmd_mailbox(adev); + if (IS_ERR_OR_NULL(mailbox)) { + dev_err(adev->dev.parent, + "failed to alloc mbx for post destroy jfcn(%u) ctx.\n", + jfcn); + return -ENOMEM; + } + + ubase_fill_mbx_attr(&attr, jfcn, UBASE_MB_DESTROY_JFC_CONTEXT, 0); + + ret = ubase_hw_upgrade_ctx_ex(adev, &attr, mailbox); + if (ret) + dev_err(adev->dev.parent, + "failed to post destroy jfcn(%u) ctx mbx, ret=%d.\n", + jfcn, ret); + + ubase_free_cmd_mailbox(adev, mailbox); + return ret; +} + +#if defined(UNIC_FPGA_COMPILE) || defined(UNIC_EVB_COMPILE) +static void unic_jfc_wait_flush_done(struct unic_dev *unic_dev, u32 jfcn) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct ubase_cmd_mailbox *mailbox; + struct ubase_mbx_attr attr = {0}; + struct unic_jfc_ctx *ctx; + int ret; + + if (test_bit(UNIC_STATE_RESETTING, &unic_dev->state)) + return; + + mailbox = ubase_alloc_cmd_mailbox(adev); + if (IS_ERR_OR_NULL(mailbox)) { + dev_err(adev->dev.parent, + "failed to alloc mbx for jfc(%u) flush.\n", jfcn); + return; + } + + ubase_fill_mbx_attr(&attr, jfcn, UBASE_MB_QUERY_JFC_CONTEXT, 0); + + ret = ubase_hw_upgrade_ctx_ex(adev, &attr, mailbox); + if (ret) { + dev_err(adev->dev.parent, + "failed to wait jfcn(%u) flush, ret=%d\n", jfcn, ret); + goto out; + } + + ctx = (struct unic_jfc_ctx *)mailbox->buf; + if (ctx->wr_cqe_idx == ctx->pi) + goto out; + + dev_err(adev->dev.parent, "wait jfcn(%u) flush done timeout.\n", jfcn); +out: + ubase_free_cmd_mailbox(adev, mailbox); +} + +static void unic_jfc_wait_flush(struct unic_dev *unic_dev, + enum unic_cq_type type, u32 num, + unsigned long des_bitmap) +{ +#define UNIC_WAIT_FLUSH_JFC_TIME 10 + + struct unic_channel *c; + struct unic_cq **cq; + u32 i; + + /* Wait for the chip to complete the flush process through sleep */ + msleep(UNIC_WAIT_FLUSH_JFC_TIME); + + for (i = 0; i < num; i++) { + if (!test_bit(i, &des_bitmap)) + continue; + + c = &unic_dev->channels.c[i]; + cq = type == UNIC_CQ_RQ ? &c->rq_cq : &c->sq_cq; + unic_jfc_wait_flush_done(unic_dev, (*cq)->jfcn); + } +} +#endif + +static void unic_destroy_multi_jfc_context(struct unic_dev *unic_dev, + enum unic_cq_type type, u32 num) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + unsigned long des_bitmap = 0; + struct unic_channel *c; + struct unic_cq **cq; + int ret; + u32 i; + + /* Since jfc wait flush is a post-condition, + * there is no need to query if jfc destroy fails here. + * Other jfs/jfr query after set error, and query is a precondition. + */ + for (i = 0; i < num; i++) { + c = &unic_dev->channels.c[i]; + cq = type == UNIC_CQ_RQ ? &c->rq_cq : &c->sq_cq; + ret = unic_post_destroy_jfc_ctx(adev, (*cq)->jfcn); + if (!ret) + set_bit(i, &des_bitmap); + } + +#if defined(UNIC_FPGA_COMPILE) || defined(UNIC_EVB_COMPILE) + unic_jfc_wait_flush(unic_dev, type, num, des_bitmap); +#endif +} + +int unic_create_cq(struct unic_dev *unic_dev, u32 idx, enum unic_cq_type type) +{ + struct unic_channel *channel = &unic_dev->channels.c[idx]; + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct ubase_resource_space *mem_base = ubase_get_mem_base(adev); + u16 channels_num = unic_dev->channels.num; + struct unic_coalesce *coal; + struct unic_cq *cq; + u32 cqe_depth; + u8 jfc_shift; + int ret; + + if (!mem_base) + return -ENODATA; + + cq = devm_kzalloc(&adev->dev, sizeof(*cq), GFP_KERNEL); + if (!cq) + return -ENOMEM; + + if (type == UNIC_CQ_RQ) { + cqe_depth = unic_dev->channels.rq_cqe_depth; + cq->jfcn = idx + channels_num; + coal = &unic_dev->channels.unic_coal.rx_coal; + } else { + cqe_depth = unic_dev->channels.sq_cqe_depth; + cq->jfcn = idx; + coal = &unic_dev->channels.unic_coal.tx_coal; + } + + ret = unic_cq_alloc_resource(adev, cq, cqe_depth); + if (ret) + goto err_alloc_res; + + channels_num = unic_dev->channels.num; + jfc_shift = ilog2(roundup_pow_of_two(cqe_depth)); + unic_init_jfc_ctx(cq, jfc_shift, unic_dev->tid, + coal->int_ql, coal->int_gl); + + cq->db_addr = mem_base->addr + UNIC_JFC_DB_OFFSET; + + ret = unic_mbx_create_jfc_context(adev, cq, cq->jfcn); + if (ret) + goto err_fill_send_cq_mbx; + + if (type) + channel->rq_cq = cq; + else + channel->sq_cq = cq; + + return 0; + +err_fill_send_cq_mbx: + unic_cq_free_resource(adev, cq, cqe_depth); +err_alloc_res: + devm_kfree(&adev->dev, cq); + return ret; +} + +static void unic_multi_cq_res_destroy(struct unic_dev *unic_dev, + enum unic_cq_type type, u32 num) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct unic_channel *c; + struct unic_cq **cq; + u32 cqe_depth, i; + + cqe_depth = type == UNIC_CQ_RQ ? unic_dev->channels.rq_cqe_depth : + unic_dev->channels.sq_cqe_depth; + + for (i = 0; i < num; i++) { + c = &unic_dev->channels.c[i]; + cq = type == UNIC_CQ_RQ ? &c->rq_cq : &c->sq_cq; + unic_cq_free_resource(adev, *cq, cqe_depth); + devm_kfree(&adev->dev, *cq); + *cq = NULL; + } +} + +void unic_destroy_cq(struct unic_dev *unic_dev, u32 num, enum unic_cq_type type) +{ + if (!num) + return; + + /* The hardware does not access the configured memory after the reset, + * directly destroy the cq. + */ + if (test_bit(UNIC_STATE_RESETTING, &unic_dev->state)) + goto cq_res_destroy; + + unic_destroy_multi_jfc_context(unic_dev, type, num); + +cq_res_destroy: + unic_multi_cq_res_destroy(unic_dev, type, num); +} + +int unic_napi_poll(struct napi_struct *napi, int budget) +{ + struct unic_channel *c = container_of(napi, struct unic_channel, napi); + struct net_device *netdev = c->sq->netdev; + struct unic_dev *unic_dev = netdev_priv(netdev); + int work_done; + + if (unlikely(test_bit(UNIC_STATE_DOWN, &unic_dev->state))) { + napi_complete(napi); + return 0; + } + + unic_poll_tx(c->sq, budget); + + work_done = unic_poll_rx(c, budget, unic_send_skb_to_stack); + if (work_done >= budget) + return budget; + + napi_complete_done(napi, work_done); + + return work_done; +} + +void unic_cq_doorbell(struct unic_cq *cq, u32 last_ci) +{ + struct unic_jfc_db jfc_db = {0}; + + if (unlikely(cq->ci == last_ci)) + return; + + jfc_db.ci = cq->ci; + jfc_db.jfc_num = cq->jfcn; + + writeq(*(u64 *)&jfc_db, cq->db_addr); +} + +void unic_clear_all_queue(struct net_device *netdev) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + struct unic_channel *c; + u32 i; + + mutex_lock(&unic_dev->channels.mutex); + + if (!unic_dev->channels.c) + goto out; + + for (i = 0; i < unic_dev->channels.num; i++) { + c = &unic_dev->channels.c[i]; + unic_clear_sq(c->sq); + unic_clear_rq(c->rq); + } +out: + mutex_unlock(&unic_dev->channels.mutex); +} diff --git a/drivers/net/ub/unic/unic_txrx.h b/drivers/net/ub/unic/unic_txrx.h new file mode 100644 index 000000000000..b52354c8989f --- /dev/null +++ b/drivers/net/ub/unic/unic_txrx.h @@ -0,0 +1,260 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_TXRX_H__ +#define __UNIC_TXRX_H__ + +#include + +struct unic_dev; + +#define UNIC_CTX_SHIFT_BASE 6 + +#define UNIC_CQE_VA_L_OFFSET 12 +#define UNIC_CQE_VA_L_VALID_BIT GENMASK(19, 0) +#define UNIC_CQE_VA_H_OFFSET 32 +#define UNIC_CQE_VA_H_VALID_BIT GENMASK(31, 0) + +#define UNIC_CEQN_VALID_BIT GENMASK(7, 0) + +#define UNIC_JFC_DB_OFFSET 0x40 + +enum unic_armed_jfc { + UNIC_CTX_NO_ARMED, + UNIC_CTX_ALWAY_ARMED, + UNIC_CTX_REG_NEXT_CEQE, + UNIC_CTX_REG_NEXT_SOLICITED_CEQE, +}; + +enum unic_jfc_state { + UNIC_JFC_STATE_INVALID, + UNIC_JFC_STATE_VALID, + UNIC_JFC_STATE_ERROR, +}; + +enum unic_jfc_type { + UNIC_NORMAL_JFC_TYPE, + UNIC_RAW_JFC_TYPE, +}; + +enum unic_jfc_inline { + UNIC_NO_INLINE_DATA, + UNIC_INLINE_DATA, +}; + +enum unic_cqe_size { + UNIC_64_JFC_CQE_SIZE, + UNIC_128_JFC_CQE_SIZE, +}; + +enum unic_cq_cnt_mode { + UNIC_CQE_CNT_MODE_BY_COUNT, + UNIC_CQE_CNT_MODE_BY_CI_PI_GAP, +}; + +enum unic_record_db { + UNIC_NO_RECORD_EN, + UNIC_RECORD_EN, +}; + +enum unic_cq_type { + UNIC_CQ_SQ = 0, + UNIC_CQ_RQ, +}; + +enum unic_jfc_db_type { + UNIC_JFC_CQ_DB, + UNIC_JFC_CQ_ARM_DB, +}; + +enum unic_jfc_db_notify { + UNIC_JFC_NOTIFY_UNSOLICITED, + UNIC_JFC_NOTIFY_SOLICITED, +}; + +enum unic_pkt_l3_type { + /* 0 ~ 3 is also used by the raw sqe L3T */ + UNIC_L3_TYPE_IPV4, + UNIC_L3_TYPE_IPV6, + UNIC_L3_TYPE_NON_IP, + + UNIC_L3_TYPE_ARP, + UNIC_L3_TYPE_RARP, + UNIC_L3_TYPE_LLDP, + UNIC_L3_TYPE_STP, + UNIC_L3_TYPE_MAC_PAUSE, + UNIC_L3_TYPE_PFC_PAUSE, + UNIC_L3_TYPE_CNM, + UNIC_L3_TYPE_PTP, + + UNIC_L3_TYPE_RESV +}; + +struct unic_jfc_ctx { + /* DW0 */ + u32 state : 2; + u32 arm_st : 2; + u32 shift : 4; + u32 cqe_size : 1; + u32 record_db_en : 1; + u32 jfc_type : 1; + u32 inline_en : 1; + u32 cqe_base_addr_l : 20; + /* DW1 */ + u32 cqe_base_addr_h; + /* DW2 */ + u32 queue_token_id : 20; + u32 cq_cnt_mode : 1; + u32 rsv0 : 3; + u32 ceqn : 8; + /* DW3 */ + u32 ad : 24; + u32 rsv1 : 8; + /* DW4 */ + u32 pi : 22; + u32 cqe_coalesce_cnt : 10; + /* DW5 */ + u32 ci : 22; + u32 cqe_coalesce_period : 3; + u32 rsv2 : 7; + /* DW6 */ + u32 record_db_addr_l; + /* DW7 */ + u32 record_db_addr_h : 26; + u32 rsv3 : 6; + /* DW8 */ + u32 queue_position : 1; + u32 push_cqe_en : 1; + u32 token_en : 1; + u32 rsv4 : 9; + u32 tpn : 20; + /* DW9 ~ DW12 */ + u32 rmt_eid[4]; + /* DW13 */ + u32 seid_idx : 10; + u32 rmt_token_id : 20; + u32 rsv5 : 2; + /* DW14 */ + u32 remote_token_value; + /* DW15 */ + u32 int_vector : 16; + u32 stars_en : 1; + u32 rsv6 : 15; + /* DW16 */ + u32 poll : 1; + u32 cqe_report_timer : 24; + u32 se : 1; + u32 arm_sn : 2; + u32 rsv7 : 4; + /* DW17 */ + u32 se_cqe_idx : 24; + u32 rsv8 : 8; + /* DW18 */ + u32 wr_cqe_idx : 22; + u32 rsv9 : 10; + /* DW19 */ + u32 cqe_cnt : 24; + u32 rsv10 : 8; + /* DW20 ~ DW31 */ + u32 rsv12[12]; +}; + +union unic_cqe { + struct { + /* DW0 */ + u8 resv0 : 2; + u8 owner : 1; + u8 resv1 : 4; + u8 fd : 1; + u8 resv2; + u8 sub_status; + u8 status; + /* DW1 */ + u16 raw_ci; + u16 lcl_jfsn_low; + /* DW2 */ + u32 lcl_jfsn_high : 4; + u32 resv3 : 28; + /* DW3 ~ DW15 */ + u32 resv4[13]; + } tx; + + struct { + /* DW0 */ + u8 owner : 1; + u8 doi : 1; + u8 ol3_err : 1; + u8 ol4_err : 1; + u8 ts_vld : 1; + u8 udp0 : 1; + u8 trunc : 1; + u8 resv1 : 1; + u8 crcp : 1; + u8 l3l4p : 1; + u8 hoi : 1; + u8 l2_type : 2; + u8 l2_err : 1; + u8 l3_err : 1; + u8 l4_err : 1; + u8 ptype; + u8 ol2_type : 2; + u8 resv2 : 6; + /* DW1 */ + u16 start_rqe_idx; + u16 packet_len; + /* DW2 */ + u32 lcl_jfrn : 20; + u32 resv3 : 12; + /* DW3 */ + u32 rss_hash; + /* DW4 ~ DW5 */ + u64 timestamp; + /* DW6 */ + u16 unknown_pkt_l2_checksum; + u16 resv4; + /* DW7 ~ DW15 */ + u32 resv5[9]; + } rx; +}; + +struct unic_jfc_db { + __le32 ci : 24; + __le32 notify : 1; + __le32 cmd_ssn : 2; + __le32 type : 1; + __le32 rsv0 : 4; + + __le32 jfc_num : 20; + __le32 rsv1 : 12; +}; + +struct unic_cq { + void __iomem *db_addr; + union unic_cqe *cqe; + dma_addr_t cqe_dma_addr; + struct unic_jfc_ctx jfc_ctx; + u32 jfcn; + u32 ci; /* the start of next to consume */ + u64 event_cnt; +}; + +static inline u8 unic_get_cqe_size(void) +{ + return sizeof(union unic_cqe); +} + +static inline bool unic_cqe_owner_is_soft(u8 jfc_shift, u32 ci, u8 owner) +{ + return owner != ((ci >> jfc_shift) & 1); +} + +int unic_create_cq(struct unic_dev *unic_dev, u32 idx, enum unic_cq_type type); +void unic_destroy_cq(struct unic_dev *unic_dev, u32 num, enum unic_cq_type); +int unic_napi_poll(struct napi_struct *napi, int budget); +void unic_cq_doorbell(struct unic_cq *cq, u32 last_ci); +void unic_clear_all_queue(struct net_device *netdev); + +#endif -- Gitee From 9e9f131256bd4cbd3d3e73112aff2e75990d2af1 Mon Sep 17 00:00:00 2001 From: Haibin Lu Date: Thu, 11 Sep 2025 16:10:09 +0800 Subject: [PATCH 176/214] net: unic: add rack ip for unic ANBZ: #45460 commit eb06fbf35d12b6bbdcfc95c5a46ee402b21a8cf4 openEuler. This patch adds support for IP address configuration. However, it is crucial to note that IP address configuration must be performed exclusively by the management software. Consequently, the driver's current implementation for IP address configuration involves retrieving the IP address set by the management software via CRQ events, notifying the TCP/IP protocol stack, and subsequently performing local backup management upon successful configuration. Signed-off-by: Zhenyu Wan Signed-off-by: Fengyan Mu Signed-off-by: Jianqiang Li Signed-off-by: Junxin Chen Signed-off-by: Haibin Lu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/Makefile | 2 +- drivers/net/ub/unic/unic_comm_addr.c | 231 +++++++++ drivers/net/ub/unic/unic_comm_addr.h | 69 +++ drivers/net/ub/unic/unic_dev.c | 4 + drivers/net/ub/unic/unic_event.c | 49 ++ drivers/net/ub/unic/unic_main.c | 7 + drivers/net/ub/unic/unic_netdev.c | 98 ++++ drivers/net/ub/unic/unic_netdev.h | 2 + drivers/net/ub/unic/unic_rack_ip.c | 736 +++++++++++++++++++++++++++ drivers/net/ub/unic/unic_rack_ip.h | 85 ++++ include/ub/ubase/ubase_comm_ctrlq.h | 5 + 11 files changed, 1287 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ub/unic/unic_comm_addr.c create mode 100644 drivers/net/ub/unic/unic_comm_addr.h create mode 100644 drivers/net/ub/unic/unic_rack_ip.c create mode 100644 drivers/net/ub/unic/unic_rack_ip.h diff --git a/drivers/net/ub/unic/Makefile b/drivers/net/ub/unic/Makefile index 04fc77f05810..04b5330b5c27 100644 --- a/drivers/net/ub/unic/Makefile +++ b/drivers/net/ub/unic/Makefile @@ -8,5 +8,5 @@ ccflags-y += -I$(srctree)/drivers/net/ub/unic/debugfs obj-$(CONFIG_UB_UNIC) += unic.o unic-objs = unic_main.o unic_ethtool.o unic_hw.o unic_guid.o unic_netdev.o unic_dev.o unic_qos_hw.o unic_event.o unic_crq.o -unic-objs += debugfs/unic_debugfs.o unic_rx.o unic_tx.o unic_txrx.o +unic-objs += debugfs/unic_debugfs.o unic_rx.o unic_tx.o unic_txrx.o unic_comm_addr.o unic_rack_ip.o unic-$(CONFIG_UB_UNIC_DCB) += unic_dcbnl.o diff --git a/drivers/net/ub/unic/unic_comm_addr.c b/drivers/net/ub/unic/unic_comm_addr.c new file mode 100644 index 000000000000..cc2822453614 --- /dev/null +++ b/drivers/net/ub/unic/unic_comm_addr.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include +#include + +#include "unic_comm_addr.h" + +struct unic_comm_addr_node * +unic_comm_find_addr_node(struct list_head *list, const u8 *addr, u16 node_mask) +{ + struct unic_comm_addr_node *addr_node, *tmp; + + list_for_each_entry_safe(addr_node, tmp, list, node) { + if (unic_comm_addr_equal(addr_node->unic_addr, addr, + addr_node->node_mask, node_mask)) + return addr_node; + } + + return NULL; +} + +void unic_comm_update_addr_state(struct unic_comm_addr_node *addr_node, + enum UNIC_COMM_ADDR_STATE state) +{ + switch (state) { + case UNIC_COMM_ADDR_TO_ADD: + if (addr_node->state == UNIC_COMM_ADDR_TO_DEL) + addr_node->state = UNIC_COMM_ADDR_ACTIVE; + break; + case UNIC_COMM_ADDR_TO_DEL: + if (addr_node->state == UNIC_COMM_ADDR_TO_ADD) { + list_del(&addr_node->node); + kfree(addr_node); + } else { + addr_node->state = UNIC_COMM_ADDR_TO_DEL; + } + break; + case UNIC_COMM_ADDR_ACTIVE: + if (addr_node->state == UNIC_COMM_ADDR_TO_ADD) + addr_node->state = UNIC_COMM_ADDR_ACTIVE; + break; + default: + break; + } +} + +int unic_comm_update_addr_list(struct list_head *list, + spinlock_t *addr_list_lock, + enum UNIC_COMM_ADDR_STATE state, + const u8 *addr) +{ + struct unic_comm_addr_node *addr_node; + + spin_lock_bh(addr_list_lock); + + /* if the addr is already in the addr list, no need to add a new + * one into it, just check the addr state, convert it to a new + * state, or just remove it, or do nothing. + */ + addr_node = unic_comm_find_addr_node(list, addr, UNIC_COMM_ADDR_NO_MASK); + if (addr_node) { + unic_comm_update_addr_state(addr_node, state); + spin_unlock_bh(addr_list_lock); + return 0; + } + + /* if this addr is never added, unnecessary to delete */ + if (state == UNIC_COMM_ADDR_TO_DEL) { + spin_unlock_bh(addr_list_lock); + return -ENOENT; + } + + addr_node = kzalloc(sizeof(*addr_node), GFP_ATOMIC); + if (!addr_node) { + spin_unlock_bh(addr_list_lock); + return -ENOMEM; + } + + addr_node->state = state; + memcpy(addr_node->unic_addr, addr, UNIC_ADDR_LEN); + list_add_tail(&addr_node->node, list); + + spin_unlock_bh(addr_list_lock); + + return 0; +} + +void unic_comm_sync_from_addr_del_list(struct list_head *del_list, + struct list_head *addr_list) +{ + struct unic_comm_addr_node *addr_node, *tmp, *new_node; + + list_for_each_entry_safe(addr_node, tmp, del_list, node) { + new_node = unic_comm_find_addr_node(addr_list, + addr_node->unic_addr, + UNIC_COMM_ADDR_NO_MASK); + if (new_node) { + /* If the addr exists in the addr list, it means + * received a new TO_ADD request during the time window + * of configuring the addr, so we just need + * to change the addr node state to ACTIVE. + */ + new_node->state = UNIC_COMM_ADDR_ACTIVE; + list_del(&addr_node->node); + kfree(addr_node); + } else { + list_move_tail(&addr_node->node, addr_list); + } + } +} + +void unic_comm_sync_from_addr_add_list(struct list_head *add_list, + struct list_head *addr_list, + bool *all_added) +{ + struct unic_comm_addr_node *addr_node, *tmp, *new_node; + + list_for_each_entry_safe(addr_node, tmp, add_list, node) { + if (*all_added && addr_node->state == UNIC_COMM_ADDR_TO_ADD) + *all_added = false; + + /* If the addr from tmp_add_list is not in the + * addr_list, it means have received a TO_DEL request + * during the time window of adding the addr. If addr_node + * state is ACTIVE, then change its state to TO_DEL, + * then it will be removed at next time. If is TO_ADD, + * it means this address hasn't been added successfully, + * so just remove the addr node. + */ + new_node = unic_comm_find_addr_node(addr_list, + addr_node->unic_addr, + UNIC_COMM_ADDR_NO_MASK); + if (new_node) { + unic_comm_update_addr_state(new_node, addr_node->state); + list_del(&addr_node->node); + kfree(addr_node); + } else if (addr_node->state == UNIC_COMM_ADDR_ACTIVE) { + addr_node->state = UNIC_COMM_ADDR_TO_DEL; + list_move_tail(&addr_node->node, addr_list); + } else { + list_del(&addr_node->node); + kfree(addr_node); + } + } +} + +bool unic_comm_sync_addr_table(struct unic_vport *vport, + struct list_head *list, + spinlock_t *addr_list_lock, + void (*sync)(struct unic_vport *vport, + struct list_head *list), + void (*unsync)(struct unic_vport *vport, + struct list_head *list)) +{ + struct unic_comm_addr_node *addr_node, *tmp, *new_node; + struct list_head tmp_add_list, tmp_del_list; + bool all_added = true; + + INIT_LIST_HEAD(&tmp_add_list); + INIT_LIST_HEAD(&tmp_del_list); + + /* move the addr to the tmp_add_list and tmp_del_list, then + * we can add/delete these addr outside the spin lock + */ + spin_lock_bh(addr_list_lock); + + list_for_each_entry_safe(addr_node, tmp, list, node) { + switch (addr_node->state) { + case UNIC_COMM_ADDR_TO_DEL: + list_move_tail(&addr_node->node, &tmp_del_list); + break; + case UNIC_COMM_ADDR_TO_ADD: + new_node = kzalloc(sizeof(*new_node), GFP_ATOMIC); + if (!new_node) + goto stop_traverse; + memcpy(new_node->unic_addr, addr_node->unic_addr, + UNIC_ADDR_LEN); + new_node->state = addr_node->state; + new_node->node_mask = addr_node->node_mask; + list_add_tail(&new_node->node, &tmp_add_list); + break; + default: + break; + } + } + +stop_traverse: + spin_unlock_bh(addr_list_lock); + + /* delete first, in order to get max addr table space for adding */ + if (unsync) + unsync(vport, &tmp_del_list); + if (sync) + sync(vport, &tmp_add_list); + + /* if some addr were added/deleted fail, move back to the + * addr_list, and retry at next time. + */ + spin_lock_bh(addr_list_lock); + + unic_comm_sync_from_addr_del_list(&tmp_del_list, list); + unic_comm_sync_from_addr_add_list(&tmp_add_list, list, &all_added); + + spin_unlock_bh(addr_list_lock); + + return all_added; +} + +int unic_convert_ip_addr(struct sockaddr *addr, struct in6_addr *ip_addr) +{ + __be32 v4addr; + + switch (addr->sa_family) { + case AF_INET: + /* we transform ipv4 addr to ipv6 addr for later configuring */ + v4addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr; + ipv6_addr_set_v4mapped(v4addr, ip_addr); + break; + case AF_INET6: + memcpy(ip_addr, &((struct sockaddr_in6 *)addr)->sin6_addr, + sizeof(struct in6_addr)); + break; + default: + return -EINVAL; + } + return 0; +} diff --git a/drivers/net/ub/unic/unic_comm_addr.h b/drivers/net/ub/unic/unic_comm_addr.h new file mode 100644 index 000000000000..ae390e4a7f07 --- /dev/null +++ b/drivers/net/ub/unic/unic_comm_addr.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_COMM_ADDR__ +#define __UNIC_COMM_ADDR__ + +#include +#include + +#include "unic_dev.h" + +enum UNIC_COMM_ADDR_STATE { + UNIC_COMM_ADDR_TO_ADD, + UNIC_COMM_ADDR_TO_DEL, + UNIC_COMM_ADDR_ACTIVE +}; + +#define UNIC_COMM_ADDR_NO_MASK 0 +#define UNIC_IPV4_PREFIX 0xffff0000 + +#define UNIC_ADDR_LEN 16 +struct unic_comm_addr_node { + struct list_head node; + enum UNIC_COMM_ADDR_STATE state; + union { + u8 unic_addr[UNIC_ADDR_LEN]; + struct in6_addr ip_addr; + }; + u16 node_mask; +}; + +static inline bool unic_comm_addr_equal(const u8 *addr1, const u8 *addr2, + u16 mask1, u16 mask2) +{ + const u32 *a = (const u32 *)addr1; + const u32 *b = (const u32 *)addr2; + + return (((a[0] ^ b[0]) | (a[1] ^ b[1]) | + (a[2] ^ b[2]) | (a[3] ^ b[3])) | + (mask1 ^ mask2)) == 0; +} + +struct unic_comm_addr_node *unic_comm_find_addr_node(struct list_head *list, + const u8 *addr, + u16 node_mask); +void unic_comm_update_addr_state(struct unic_comm_addr_node *addr_node, + enum UNIC_COMM_ADDR_STATE state); +int unic_comm_update_addr_list(struct list_head *list, + spinlock_t *addr_list_lock, + enum UNIC_COMM_ADDR_STATE state, + const u8 *addr); +bool unic_comm_sync_addr_table(struct unic_vport *vport, + struct list_head *list, + spinlock_t *addr_list_lock, + void (*sync)(struct unic_vport *, + struct list_head *), + void (*unsync)(struct unic_vport *, + struct list_head *)); +void unic_comm_sync_from_addr_del_list(struct list_head *del_list, + struct list_head *addr_list); +void unic_comm_sync_from_addr_add_list(struct list_head *add_list, + struct list_head *addr_list, + bool *all_added); +int unic_convert_ip_addr(struct sockaddr *addr, struct in6_addr *ip_addr); + +#endif diff --git a/drivers/net/ub/unic/unic_dev.c b/drivers/net/ub/unic/unic_dev.c index 09d55c44900c..c1a51eb20c7f 100644 --- a/drivers/net/ub/unic/unic_dev.c +++ b/drivers/net/ub/unic/unic_dev.c @@ -25,6 +25,7 @@ #include "unic_hw.h" #include "unic_qos_hw.h" #include "unic_netdev.h" +#include "unic_rack_ip.h" #include "unic_dev.h" #define UNIC_WATCHDOG_TIMEOUT (5 * HZ) @@ -568,6 +569,7 @@ static void unic_periodic_service_task(struct unic_dev *unic_dev) unic_link_status_update(unic_dev); unic_update_port_info(unic_dev); + unic_sync_rack_ip_table(unic_dev); unic_sync_promisc_mode(unic_dev); unic_dev->serv_processed_cnt++; @@ -679,6 +681,7 @@ static int unic_init_vport(struct unic_dev *unic_dev) static void unic_uninit_vport(struct unic_dev *unic_dev) { + unic_uninit_rack_ip_table(unic_dev); unic_uninit_vport_buf(unic_dev); } @@ -867,6 +870,7 @@ int unic_dev_init(struct auxiliary_device *adev) goto err_unregister_event; } + unic_query_rack_ip(adev); unic_start_dev_period_task(netdev); return 0; diff --git a/drivers/net/ub/unic/unic_event.c b/drivers/net/ub/unic/unic_event.c index 9a38d1763285..bcd1e210f447 100644 --- a/drivers/net/ub/unic/unic_event.c +++ b/drivers/net/ub/unic/unic_event.c @@ -22,6 +22,7 @@ #include "unic_hw.h" #include "unic_netdev.h" #include "unic_qos_hw.h" +#include "unic_rack_ip.h" #include "unic_event.h" int unic_comp_handler(struct notifier_block *nb, unsigned long jfcn, void *data) @@ -51,6 +52,45 @@ int unic_comp_handler(struct notifier_block *nb, unsigned long jfcn, void *data) return 0; } +static struct ubase_ctrlq_event_nb unic_ctrlq_events[] = { + { + .service_type = UBASE_CTRLQ_SER_TYPE_IP_ACL, + .opcode = UBASE_CTRLQ_OPC_NOTIFY_IP, + .crq_handler = unic_handle_notify_ip_event, + } +}; + +static void unic_unregister_ctrlq_event(struct auxiliary_device *adev, + u32 ctrlq_crq_event_num) +{ + u32 i; + + for (i = 0; i < ctrlq_crq_event_num; i++) + ubase_ctrlq_unregister_crq_event(adev, + unic_ctrlq_events[i].service_type, + unic_ctrlq_events[i].opcode); +} + +static int unic_register_ctrlq_event(struct auxiliary_device *adev) +{ + int ret; + u32 i; + + for (i = 0; i < ARRAY_SIZE(unic_ctrlq_events); i++) { + unic_ctrlq_events[i].back = adev; + ret = ubase_ctrlq_register_crq_event(adev, &unic_ctrlq_events[i]); + if (ret) { + dev_err(adev->dev.parent, + "failed to register ctrlq event[%u], ret = %d.\n", + i, ret); + unic_unregister_ctrlq_event(adev, i); + return ret; + } + } + + return 0; +} + static struct ubase_crq_event_nb unic_crq_events[] = { { .opcode = UBASE_OPC_QUERY_LINK_STATUS, @@ -96,10 +136,19 @@ int unic_register_event(struct auxiliary_device *adev) if (ret) return ret; + ret = unic_register_ctrlq_event(adev); + if (ret) + goto unregister_crq; + return 0; + +unregister_crq: + unic_unregister_crq_event(adev, ARRAY_SIZE(unic_crq_events)); + return ret; } void unic_unregister_event(struct auxiliary_device *adev) { + unic_unregister_ctrlq_event(adev, ARRAY_SIZE(unic_ctrlq_events)); unic_unregister_crq_event(adev, ARRAY_SIZE(unic_crq_events)); } diff --git a/drivers/net/ub/unic/unic_main.c b/drivers/net/ub/unic/unic_main.c index 22b97068ab23..6c445f4714c5 100644 --- a/drivers/net/ub/unic/unic_main.c +++ b/drivers/net/ub/unic/unic_main.c @@ -82,6 +82,10 @@ static int __init unic_init(void) return ret; } + ret = unic_register_ipaddr_notifier(); + if (ret) + goto err_reg_ip_notifier; + ret = auxiliary_driver_register(&unic_drv); if (ret) goto err_aux_reg; @@ -89,12 +93,15 @@ static int __init unic_init(void) return ret; err_aux_reg: + unic_unregister_ipaddr_notifier(); +err_reg_ip_notifier: unic_destroy_wq(); return ret; } static void __exit unic_exit(void) { + unic_unregister_ipaddr_notifier(); auxiliary_driver_unregister(&unic_drv); unic_destroy_wq(); } diff --git a/drivers/net/ub/unic/unic_netdev.c b/drivers/net/ub/unic/unic_netdev.c index ac31dd4fe26e..b4b52d95bc74 100644 --- a/drivers/net/ub/unic/unic_netdev.c +++ b/drivers/net/ub/unic/unic_netdev.c @@ -28,6 +28,7 @@ #include "unic_tx.h" #include "unic_txrx.h" #include "unic_netdev.h" +#include "unic_rack_ip.h" static int unic_netdev_set_tcs(struct net_device *netdev) { @@ -530,6 +531,103 @@ void unic_set_netdev_ops(struct net_device *netdev) netdev->netdev_ops = &unic_netdev_ops; } +static bool unic_port_dev_check(const struct net_device *dev) +{ + return dev->netdev_ops == &unic_netdev_ops; +} + +static int unic_ipaddr_event(struct notifier_block *nb, unsigned long event, + struct sockaddr *sa, struct net_device *ndev, + u16 ip_mask) +{ + struct unic_dev *unic_dev; + int ret; + + if (ndev->type != ARPHRD_UB) + return NOTIFY_DONE; + + if (!unic_port_dev_check(ndev)) + return NOTIFY_DONE; + + unic_dev = netdev_priv(ndev); + + switch (event) { + case NETDEV_UP: + ret = unic_add_ip_addr(unic_dev, sa, ip_mask); + if (ret) + return NOTIFY_BAD; + break; + case NETDEV_DOWN: + ret = unic_rm_ip_addr(unic_dev, sa, ip_mask); + if (ret) + return NOTIFY_BAD; + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + +static int unic_inetaddr_event(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct in_ifaddr *ifa4 = (struct in_ifaddr *)ptr; + struct net_device *ndev = (struct net_device *)ifa4->ifa_dev->dev; + u16 ip4_mask = (u16)ifa4->ifa_prefixlen; + struct sockaddr_in in; + + in.sin_family = AF_INET; + in.sin_addr.s_addr = ifa4->ifa_address; + + return unic_ipaddr_event(nb, event, (struct sockaddr *)&in, ndev, + ip4_mask); +} + +static int unic_inet6addr_event(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct inet6_ifaddr *ifa6 = (struct inet6_ifaddr *)ptr; + struct net_device *ndev = (struct net_device *)ifa6->idev->dev; + u16 ip6_mask = (u16)ifa6->prefix_len; + struct sockaddr_in6 in6; + + in6.sin6_family = AF_INET6; + in6.sin6_addr = ifa6->addr; + + return unic_ipaddr_event(nb, event, (struct sockaddr *)&in6, ndev, + ip6_mask); +} + +static struct notifier_block unic_inetaddr_notifier = { + .notifier_call = unic_inetaddr_event +}; + +static struct notifier_block unic_inet6addr_notifier = { + .notifier_call = unic_inet6addr_event +}; + +int unic_register_ipaddr_notifier(void) +{ + int ret; + + ret = register_inetaddr_notifier(&unic_inetaddr_notifier); + if (ret) + return ret; + + ret = register_inet6addr_notifier(&unic_inet6addr_notifier); + if (ret) + unregister_inetaddr_notifier(&unic_inetaddr_notifier); + + return ret; +} + +void unic_unregister_ipaddr_notifier(void) +{ + unregister_inetaddr_notifier(&unic_inetaddr_notifier); + unregister_inet6addr_notifier(&unic_inet6addr_notifier); +} + int unic_query_link_status(struct unic_dev *unic_dev, u8 *link_status) { struct unic_link_status_cmd_resp resp = {0}; diff --git a/drivers/net/ub/unic/unic_netdev.h b/drivers/net/ub/unic/unic_netdev.h index f3383b8660a3..fd8fd16f1e92 100644 --- a/drivers/net/ub/unic/unic_netdev.h +++ b/drivers/net/ub/unic/unic_netdev.h @@ -17,6 +17,8 @@ int unic_net_stop(struct net_device *netdev); void unic_net_stop_no_link_change(struct net_device *netdev); void unic_set_netdev_ops(struct net_device *netdev); void unic_link_status_update(struct unic_dev *unic_dev); +int unic_register_ipaddr_notifier(void); +void unic_unregister_ipaddr_notifier(void); void unic_link_status_change(struct net_device *netdev, bool linkup); int unic_query_link_status(struct unic_dev *unic_dev, u8 *link_status); diff --git a/drivers/net/ub/unic/unic_rack_ip.c b/drivers/net/ub/unic/unic_rack_ip.c new file mode 100644 index 000000000000..f2ff97304e78 --- /dev/null +++ b/drivers/net/ub/unic/unic_rack_ip.c @@ -0,0 +1,736 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "unic_comm_addr.h" +#include "unic_trace.h" +#include "unic_rack_ip.h" + +static void unic_update_rack_addr_state(struct unic_vport *vport, + struct unic_comm_addr_node *addr_node, + enum UNIC_COMM_ADDR_STATE state, + const u8 *addr) +{ + struct auxiliary_device *adev = vport->back->comdev.adev; + struct unic_dev *unic_dev = dev_get_drvdata(&adev->dev); + char format_masked_ip_addr[UNIC_MASKED_FORMAT_IP_LEN]; + + /* update the state of address node by stack in rack server. + * if ip node exist in ip_list and receive the ack form stack, + * update_rack_addr_state and handle accidental deletion. + */ + switch (state) { + case UNIC_COMM_ADDR_TO_ADD: + if (addr_node->state == UNIC_COMM_ADDR_TO_ADD) + addr_node->state = UNIC_COMM_ADDR_ACTIVE; + break; + case UNIC_COMM_ADDR_TO_DEL: + if (addr_node->state == UNIC_COMM_ADDR_TO_DEL) { + list_del(&addr_node->node); + kfree(addr_node); + } else if (addr_node->state == UNIC_COMM_ADDR_ACTIVE) { + addr_node->state = UNIC_COMM_ADDR_TO_ADD; + unic_format_masked_ip_addr(format_masked_ip_addr, addr); + unic_info(unic_dev, + "deleted an existing ip %s by accident and need to add it.\n", + format_masked_ip_addr); + } + break; + default: + break; + } +} + +static int unic_update_stack_ip_addr(struct unic_vport *vport, + enum UNIC_COMM_ADDR_STATE state, + const u8 *addr, u16 ip_mask) +{ + spinlock_t *addr_list_lock = &vport->addr_tbl.ip_list_lock; + struct auxiliary_device *adev = vport->back->comdev.adev; + struct unic_dev *unic_dev = dev_get_drvdata(&adev->dev); + char format_masked_ip_addr[UNIC_MASKED_FORMAT_IP_LEN]; + struct list_head *list = &vport->addr_tbl.ip_list; + struct unic_comm_addr_node *addr_node, *tmp; + int ret = 0; + + clear_bit(UNIC_VPORT_STATE_IP_TBL_CHANGE, &vport->state); + + spin_lock_bh(addr_list_lock); + + addr_node = unic_comm_find_addr_node(list, addr, ip_mask); + if (addr_node) { + unic_update_rack_addr_state(vport, addr_node, state, addr); + goto finish_update_state; + } + + if (state == UNIC_COMM_ADDR_TO_DEL) { + ret = -ENOENT; + goto finish_update_state; + } + + /* handle accidental addition, create addr node for deleting*/ + addr_node = kzalloc(sizeof(*addr_node), GFP_ATOMIC); + if (!addr_node) { + ret = -ENOMEM; + goto finish_update_state; + } + + addr_node->state = UNIC_COMM_ADDR_TO_DEL; + addr_node->node_mask = ip_mask; + memcpy(addr_node->unic_addr, addr, UNIC_ADDR_LEN); + list_add_tail(&addr_node->node, list); + unic_format_masked_ip_addr(format_masked_ip_addr, addr); + unic_info(unic_dev, + "added a new ip %s by accident and need to delete it.\n", + format_masked_ip_addr); + set_bit(UNIC_VPORT_STATE_IP_TBL_CHANGE, &vport->state); + goto unlock_and_exit; + +finish_update_state: + list_for_each_entry(tmp, list, node) { + if (tmp->state != UNIC_COMM_ADDR_ACTIVE) { + set_bit(UNIC_VPORT_STATE_IP_TBL_CHANGE, &vport->state); + break; + } + } +unlock_and_exit: + spin_unlock_bh(addr_list_lock); + + return ret; +} + +int unic_handle_stack_ip_feedback(struct unic_vport *vport, + enum UNIC_COMM_ADDR_STATE state, + struct sockaddr *addr, u16 ip_mask) +{ + struct auxiliary_device *adev = vport->back->comdev.adev; + struct unic_dev *unic_dev = dev_get_drvdata(&adev->dev); + char format_masked_ip_addr[UNIC_MASKED_FORMAT_IP_LEN]; + struct in6_addr ip_addr; + int ret; + + ret = unic_convert_ip_addr(addr, &ip_addr); + if (ret) + unic_info(unic_dev, + "invalid IP protocol type, ret = %d.\n", ret); + + ret = unic_update_stack_ip_addr(vport, state, + (const unsigned char *)&ip_addr, + ip_mask); + if (ret) { + unic_format_masked_ip_addr(format_masked_ip_addr, (u8 *)&ip_addr); + unic_err(unic_dev, "failed to %s IP %s from ip list, ret = %d.\n", + state == UNIC_COMM_ADDR_TO_ADD ? "add" : "delete", + format_masked_ip_addr, ret); + } + + return ret; +} + +static bool unic_is_ipv4(struct unic_stack_ip_info *ip_info) +{ + if (ip_info->ip_addr[0] == 0x0 && ip_info->ip_addr[1] == 0x0 && + ip_info->ip_addr[2] == UNIC_IPV4_PREFIX) + return true; + else + return false; +} + +static int unic_fill_rtattr_pack(struct nlmsghdr *nh, size_t req_sz, + unsigned short rta_type, const void *payload, + size_t size) +{ + size_t nl_size = RTA_ALIGN(nh->nlmsg_len) + RTA_LENGTH(size); + struct rtattr *attr = (struct rtattr *)((char *)(nh) + + RTA_ALIGN(nh->nlmsg_len)); + + if (req_sz < nl_size) + return -EINVAL; + + nh->nlmsg_len = nl_size; + attr->rta_len = RTA_LENGTH(size); + attr->rta_type = rta_type; + memcpy(RTA_DATA(attr), payload, size); + + return 0; +} + +static int unic_fill_netlink_ip_req(struct unic_netlink_ip_req *req, + struct sk_buff *skb, struct net_device *dev, + struct unic_stack_ip_info *ip_info, + sa_family_t sa_family) +{ + int ret; + + NETLINK_CB(skb).portid = 0; + NETLINK_CB(skb).dst_group = 0; + NETLINK_CB(skb).flags = NETLINK_SKB_DST; + memset(req, 0, sizeof(*req)); + + if (ip_info->ip_cmd == UNIC_COMM_ADDR_TO_ADD) { + req->nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE; + req->nh.nlmsg_type = RTM_NEWADDR; + } else { + req->nh.nlmsg_flags = NLM_F_REQUEST; + req->nh.nlmsg_type = RTM_DELADDR; + } + + req->nh.nlmsg_len = NLMSG_LENGTH(sizeof(req->info)); + req->info.ifa_family = sa_family; + req->info.ifa_flags = IFA_F_PERMANENT; + req->info.ifa_scope = RT_SCOPE_UNIVERSE; + req->info.ifa_prefixlen = (u8)ip_info->ip_mask; + req->info.ifa_index = dev->ifindex; + + if (sa_family == AF_INET) { + ret = unic_fill_rtattr_pack(&req->nh, sizeof(*req), IFA_LOCAL, + &ip_info->ip_addr[UNIC_IP_FOURTH_PART], + sizeof(ip_info->ip_addr[UNIC_IP_FOURTH_PART])); + if (ret) + return ret; + + ret = unic_fill_rtattr_pack(&req->nh, sizeof(*req), IFA_ADDRESS, + &ip_info->ip_addr[UNIC_IP_FOURTH_PART], + sizeof(ip_info->ip_addr[UNIC_IP_FOURTH_PART])); + } else { + ret = unic_fill_rtattr_pack(&req->nh, sizeof(*req), IFA_LOCAL, + ip_info->ip_addr, + sizeof(ip_info->ip_addr)); + if (ret) + return ret; + + ret = unic_fill_rtattr_pack(&req->nh, sizeof(*req), IFA_ADDRESS, + ip_info->ip_addr, + sizeof(ip_info->ip_addr)); + } + + return ret; +} + +static int unic_set_stack_ip(struct net_device *dev, + struct unic_stack_ip_info *ip_info) +{ + struct unic_dev *unic_dev = netdev_priv(dev); + struct net *tmpdev = dev_net(dev); + struct unic_netlink_ip_req *req; + struct sock *sk = tmpdev->rtnl; + sa_family_t sa_family; + struct sk_buff *skb; + int ret; + + skb = alloc_skb(NLMSG_ALIGN(sizeof(*req)), GFP_KERNEL); + if (!skb) { + unic_info(unic_dev, + "failed to alloc skb, unic stop setting stack ip.\n"); + return -ENOMEM; + } + + req = skb_put(skb, sizeof(*req)); + sa_family = unic_is_ipv4(ip_info) ? AF_INET : AF_INET6; + ret = unic_fill_netlink_ip_req(req, skb, dev, ip_info, sa_family); + if (ret) { + unic_err(unic_dev, + "failed to fill netlink ip req, ret = %d.\n", ret); + goto failed_packet_seq; + } + + trace_unic_ip_req_skb(dev, skb, &req->nh, &req->info, req->attrbuf); + netlink_unicast(sk, skb, 0, 0); + + return 0; + +failed_packet_seq: + kfree_skb(skb); + + return ret; +} + +static int unic_sync_stack_ip(struct unic_vport *vport, + struct unic_comm_addr_node *ip_node, u16 ip_cmd) +{ + struct net_device *netdev = vport->back->comdev.netdev; + struct unic_dev *unic_dev = netdev_priv(netdev); + struct unic_stack_ip_info ip_info; + int ret; + + ip_info.ip_cmd = ip_cmd; + ip_info.ip_mask = ip_node->node_mask; + memcpy(&ip_info.ip_addr, &ip_node->ip_addr, sizeof(ip_info.ip_addr)); + + ret = unic_set_stack_ip(netdev, &ip_info); + if (ret) + unic_err(unic_dev, + "failed to set ip to stack, ret = %d.\n", ret); + + return ret; +} + +static void unic_sync_rack_ip_list(struct unic_vport *vport, + struct list_head *list, + enum unic_ctrlq_ip_event state) +{ + struct unic_comm_addr_node *ip_node, *tmp; + int ret; + + list_for_each_entry_safe(ip_node, tmp, list, node) { + ret = unic_sync_stack_ip(vport, ip_node, state); + if (ret) + set_bit(UNIC_VPORT_STATE_IP_TBL_CHANGE, &vport->state); + + list_del(&ip_node->node); + kfree(ip_node); + } +} + +static void unic_rack_sync_addr_table(struct unic_vport *vport, + struct list_head *list, + spinlock_t *addr_list_lock) +{ + struct auxiliary_device *adev = vport->back->comdev.adev; + struct unic_comm_addr_node *addr_node, *tmp, *new_node; + struct list_head tmp_add_list, tmp_del_list; + + INIT_LIST_HEAD(&tmp_add_list); + INIT_LIST_HEAD(&tmp_del_list); + + /* move the addr to the tmp_add_list and tmp_del_list, then + * we can add/delete these addr outside the spin lock + */ + spin_lock_bh(addr_list_lock); + + list_for_each_entry_safe(addr_node, tmp, list, node) { + if (addr_node->state == UNIC_COMM_ADDR_ACTIVE) + continue; + + new_node = kzalloc(sizeof(*new_node), GFP_ATOMIC); + if (!new_node) { + dev_err_ratelimited(&adev->dev, + "failed to alloc memory for new node.\n"); + goto stop_traverse; + } + memcpy(new_node->unic_addr, addr_node->unic_addr, UNIC_ADDR_LEN); + new_node->state = addr_node->state; + new_node->node_mask = addr_node->node_mask; + + if (new_node->state == UNIC_COMM_ADDR_TO_DEL) + list_add_tail(&new_node->node, &tmp_del_list); + else + list_add_tail(&new_node->node, &tmp_add_list); + } + +stop_traverse: + spin_unlock_bh(addr_list_lock); + + unic_sync_rack_ip_list(vport, &tmp_del_list, UNIC_CTRLQ_DEL_IP); + unic_sync_rack_ip_list(vport, &tmp_add_list, UNIC_CTRLQ_ADD_IP); +} + +void unic_sync_rack_ip_table(struct unic_dev *unic_dev) +{ + struct unic_vport *vport = &unic_dev->vport; + + if (!test_bit(UNIC_VPORT_STATE_IP_TBL_CHANGE, &vport->state)) + return; + + unic_rack_sync_addr_table(vport, &vport->addr_tbl.ip_list, + &vport->addr_tbl.ip_list_lock); +} + +static void unic_build_stack_ip_info(struct unic_ctrlq_ip_notify_req *req, + struct unic_stack_ip_info *st_ip) +{ +#define le32_to_be32(x) cpu_to_be32(le32_to_cpu(x)) + + st_ip->ip_cmd = le16_to_cpu(req->ip_cmd); + st_ip->ip_mask = le16_to_cpu(req->ip_mask); + st_ip->ip_addr[0] = le32_to_be32(req->ip_addr[3]); + st_ip->ip_addr[1] = le32_to_be32(req->ip_addr[2]); + st_ip->ip_addr[2] = le32_to_be32(req->ip_addr[1]); + st_ip->ip_addr[3] = le32_to_be32(req->ip_addr[0]); +} + +static int unic_update_rack_addr_list(struct list_head *list, + spinlock_t *addr_list_lock, + enum UNIC_COMM_ADDR_STATE state, + const u8 *addr, u16 ip_mask) +{ + struct unic_comm_addr_node *addr_node; + + spin_lock_bh(addr_list_lock); + + /* if the addr is already in the addr list, no need to add a new + * one into it, just check the addr state, convert it to a new + * state, or just remove it, or do nothing. + */ + addr_node = unic_comm_find_addr_node(list, addr, ip_mask); + if (addr_node) { + unic_comm_update_addr_state(addr_node, state); + spin_unlock_bh(addr_list_lock); + return 0; + } + + /* if this addr is never added, unnecessary to delete */ + if (state == UNIC_COMM_ADDR_TO_DEL) { + spin_unlock_bh(addr_list_lock); + return -ENOENT; + } + + addr_node = kzalloc(sizeof(*addr_node), GFP_ATOMIC); + if (!addr_node) { + spin_unlock_bh(addr_list_lock); + return -ENOMEM; + } + + addr_node->state = state; + addr_node->node_mask = ip_mask; + memcpy(addr_node->unic_addr, addr, UNIC_ADDR_LEN); + list_add_tail(&addr_node->node, list); + + spin_unlock_bh(addr_list_lock); + + return 0; +} + +static void unic_send_notify_ip_resp(struct auxiliary_device *adev, u16 seq, + u8 result) +{ + struct unic_dev *unic_dev = dev_get_drvdata(&adev->dev); + struct unic_ctrlq_ip_notify_resp resp = {0}; + struct ubase_ctrlq_msg msg = {0}; + int ret; + + msg.service_ver = UBASE_CTRLQ_SER_VER_01; + msg.service_type = UBASE_CTRLQ_SER_TYPE_IP_ACL; + msg.opcode = UBASE_CTRLQ_OPC_NOTIFY_IP; + msg.in_size = sizeof(resp); + msg.in = &resp; + msg.is_resp = 1; + msg.resp_seq = seq; + msg.resp_ret = result; + + ret = ubase_ctrlq_send_msg(adev, &msg); + if (ret) + unic_err(unic_dev, + "failed to notify ip resp by ctrlq, ret = %d.\n", ret); +} + +static int unic_update_tmp_ip_list(struct unic_dev *unic_dev, + struct list_head *list, + struct unic_stack_ip_info *st_ip) +{ + struct unic_comm_addr_node *addr_node; + enum UNIC_COMM_ADDR_STATE state; + + if (st_ip->ip_cmd == UNIC_CTRLQ_ADD_IP) { + state = UNIC_COMM_ADDR_TO_ADD; + } else if (st_ip->ip_cmd == UNIC_CTRLQ_DEL_IP) { + state = UNIC_COMM_ADDR_TO_DEL; + } else { + unic_err(unic_dev, "invalid ip cmd by ctrlq, ip cmd = %u.\n", + st_ip->ip_cmd); + return -EINVAL; + } + + addr_node = unic_comm_find_addr_node(list, (u8 *)&st_ip->ip_addr, + st_ip->ip_mask); + if (addr_node) { + addr_node->state = state; + return 0; + } + + addr_node = kzalloc(sizeof(*addr_node), GFP_ATOMIC); + if (!addr_node) + return -ENOMEM; + + addr_node->state = state; + addr_node->node_mask = st_ip->ip_mask; + memcpy(addr_node->unic_addr, (u8 *)&st_ip->ip_addr, UNIC_ADDR_LEN); + list_add_tail(&addr_node->node, list); + + return 0; +} + +int unic_handle_notify_ip_event(struct auxiliary_device *adev, u8 service_ver, + void *data, u16 len, u16 seq) +{ + struct unic_dev *priv = (struct unic_dev *)dev_get_drvdata(&adev->dev); + char format_ip[UNIC_MASKED_FORMAT_IP_LEN]; + struct unic_vport *vport = &priv->vport; + struct unic_ctrlq_ip_notify_req *req; + struct unic_stack_ip_info st_ip; + int ret; + + if (len < sizeof(*req)) { + unic_err(priv, "failed to verify ip info size, len = %u.\n", len); + return -EINVAL; + } + + req = (struct unic_ctrlq_ip_notify_req *)data; + + unic_build_stack_ip_info(req, &st_ip); + + spin_lock_bh(&vport->addr_tbl.tmp_ip_lock); + if (test_bit(UNIC_VPORT_STATE_IP_QUERYING, &vport->state)) { + ret = unic_update_tmp_ip_list(priv, &vport->addr_tbl.tmp_ip_list, + &st_ip); + goto out; + } + + if (st_ip.ip_cmd == UNIC_CTRLQ_ADD_IP) { + ret = unic_update_rack_addr_list(&vport->addr_tbl.ip_list, + &vport->addr_tbl.ip_list_lock, + UNIC_COMM_ADDR_TO_ADD, + (u8 *)&st_ip.ip_addr, + st_ip.ip_mask); + } else if (st_ip.ip_cmd == UNIC_CTRLQ_DEL_IP) { + ret = unic_update_rack_addr_list(&vport->addr_tbl.ip_list, + &vport->addr_tbl.ip_list_lock, + UNIC_COMM_ADDR_TO_DEL, + (u8 *)&st_ip.ip_addr, + st_ip.ip_mask); + } else { + ret = -EINVAL; + unic_err(priv, "invalid ip cmd by ctrlq, cmd = %u.\n", st_ip.ip_cmd); + goto out; + } + + if (ret == -ENOENT) { + unic_format_masked_ip_addr(format_ip, (u8 *)&st_ip.ip_addr); + unic_err(priv, "failed to delete IP %s from ip list.\n", + format_ip); + ret = 0; + goto out; + } + + if (!ret) + set_bit(UNIC_VPORT_STATE_IP_TBL_CHANGE, &vport->state); + +out: + spin_unlock_bh(&vport->addr_tbl.tmp_ip_lock); + unic_send_notify_ip_resp(adev, seq, (u8)(-ret)); + + return ret; +} + +static int unic_update_ctrlq_ip_list(struct unic_ip_info *ip_info, + struct unic_vport *vport, + struct list_head *list) +{ +#define le32_to_be32(x) cpu_to_be32(le32_to_cpu(x)) + + struct unic_comm_addr_node *ip_node, *new_node, *tmp_node; + struct unic_stack_ip_info st_ip; + + st_ip.ip_mask = le16_to_cpu(ip_info->ip_mask); + st_ip.ip_addr[0] = le32_to_be32(ip_info->ip_addr[3]); + st_ip.ip_addr[1] = le32_to_be32(ip_info->ip_addr[2]); + st_ip.ip_addr[2] = le32_to_be32(ip_info->ip_addr[1]); + st_ip.ip_addr[3] = le32_to_be32(ip_info->ip_addr[0]); + + ip_node = kzalloc(sizeof(*ip_node), GFP_KERNEL); + if (!ip_node) + return -ENOMEM; + + ip_node->state = UNIC_COMM_ADDR_TO_ADD; + ip_node->node_mask = st_ip.ip_mask; + memcpy(ip_node->unic_addr, &st_ip.ip_addr, UNIC_ADDR_LEN); + + new_node = unic_comm_find_addr_node(&vport->addr_tbl.ip_list, + ip_node->unic_addr, + ip_node->node_mask); + if (new_node) { + if (new_node->state != UNIC_COMM_ADDR_TO_ADD) + new_node->state = UNIC_COMM_ADDR_ACTIVE; + + kfree(ip_node); + } else { + list_add_tail(&ip_node->node, &vport->addr_tbl.ip_list); + } + + tmp_node = kzalloc(sizeof(*tmp_node), GFP_KERNEL); + if (!tmp_node) + return -ENOMEM; + + tmp_node->state = UNIC_COMM_ADDR_TO_ADD; + tmp_node->node_mask = st_ip.ip_mask; + memcpy(tmp_node->unic_addr, &st_ip.ip_addr, UNIC_ADDR_LEN); + list_add_tail(&tmp_node->node, list); + + return 0; +} + +static int unic_ctrlq_query_ip(struct auxiliary_device *adev, u16 *ip_index, + u8 *get_count, struct unic_vport *vport, + struct list_head *list) +{ + struct unic_dev *unic_dev = dev_get_drvdata(&adev->dev); + struct unic_ctrlq_query_ip_resp resp = {0}; + struct unic_ctrlq_query_ip_req req = {0}; + struct ubase_ctrlq_msg msg = {0}; + int ret; + u8 i; + + req.ip_index = cpu_to_le16(*ip_index); + + msg.service_ver = UBASE_CTRLQ_SER_VER_01; + msg.service_type = UBASE_CTRLQ_SER_TYPE_IP_ACL; + msg.opcode = UBASE_CTRLQ_OPC_QUERY_IP; + msg.need_resp = 1; + msg.is_resp = 0; + msg.in_size = sizeof(req); + msg.in = &req; + msg.out_size = sizeof(resp); + msg.out = &resp; + + ret = ubase_ctrlq_send_msg(adev, &msg); + if (ret) { + unic_err(unic_dev, + "failed to query ip by ctrlq, ret = %d.\n", ret); + return ret; + } + + spin_lock_bh(&vport->addr_tbl.ip_list_lock); + + for (i = 0; i < resp.get_count; i++) { + ret = unic_update_ctrlq_ip_list(&resp.ip_info[i], vport, list); + if (ret) + goto out; + } + + *get_count = resp.get_count; + *ip_index = le16_to_cpu(resp.ip_index); + +out: + spin_unlock_bh(&vport->addr_tbl.ip_list_lock); + + return ret; +} + +static void unic_update_rack_ip_list(struct unic_vport *vport, + struct list_head *list) +{ + struct unic_comm_addr_node *ip_node, *tmp, *new_node; + + spin_lock_bh(&vport->addr_tbl.ip_list_lock); + + list_for_each_entry_safe(ip_node, tmp, &vport->addr_tbl.ip_list, node) { + new_node = unic_comm_find_addr_node(list, ip_node->unic_addr, + ip_node->node_mask); + if (!new_node) + ip_node->state = UNIC_COMM_ADDR_TO_DEL; + } + + list_for_each_entry_safe(ip_node, tmp, &vport->addr_tbl.tmp_ip_list, node) { + new_node = unic_comm_find_addr_node(&vport->addr_tbl.ip_list, + ip_node->unic_addr, + ip_node->node_mask); + if (new_node) { + unic_comm_update_addr_state(new_node, ip_node->state); + } else { + if (ip_node->state == UNIC_COMM_ADDR_TO_ADD) + list_move_tail(&ip_node->node, + &vport->addr_tbl.ip_list); + } + } + + list_for_each_entry_safe(ip_node, tmp, &vport->addr_tbl.ip_list, node) { + if (ip_node->state != UNIC_COMM_ADDR_ACTIVE) { + set_bit(UNIC_VPORT_STATE_IP_TBL_CHANGE, &vport->state); + break; + } + } + + spin_unlock_bh(&vport->addr_tbl.ip_list_lock); +} + +void unic_query_rack_ip(struct auxiliary_device *adev) +{ +#define UNIC_LOOP_COUNT(total_size, size) ((total_size) / (size) + 1) + + struct unic_dev *priv = (struct unic_dev *)dev_get_drvdata(&adev->dev); + struct unic_comm_addr_node *ip_node, *tmp; + struct unic_vport *vport = &priv->vport; + struct list_head tmp_list; + u16 ip_index = 0, cnt = 0; + u8 get_count = 0; + int ret; + + set_bit(UNIC_VPORT_STATE_IP_QUERYING, &priv->vport.state); + INIT_LIST_HEAD(&tmp_list); + + while (ip_index < UNIC_IP_TABLE_SIZE && + cnt < UNIC_LOOP_COUNT(UNIC_IP_TABLE_SIZE, UNIC_CTRLQ_IP_REQ_SIZE)) { + ret = unic_ctrlq_query_ip(adev, &ip_index, &get_count, vport, + &tmp_list); + if (ret) { + unic_err(priv, + "failed to query ip info by ctrlq, ret = %d.\n", + ret); + break; + } + + if (get_count != UNIC_CTRLQ_IP_REQ_SIZE) + break; + + ip_index++; + cnt++; + } + + spin_lock_bh(&vport->addr_tbl.tmp_ip_lock); + + unic_update_rack_ip_list(vport, &tmp_list); + clear_bit(UNIC_VPORT_STATE_IP_QUERYING, &vport->state); + + list_for_each_entry_safe(ip_node, tmp, &vport->addr_tbl.tmp_ip_list, node) { + list_del(&ip_node->node); + kfree(ip_node); + } + + spin_unlock_bh(&vport->addr_tbl.tmp_ip_lock); + + list_for_each_entry_safe(ip_node, tmp, &tmp_list, node) { + list_del(&ip_node->node); + kfree(ip_node); + } +} + +void unic_uninit_rack_ip_table(struct unic_dev *unic_dev) +{ + struct unic_vport *vport = &unic_dev->vport; + struct list_head *list = &vport->addr_tbl.ip_list; + struct unic_comm_addr_node *ip_node, *tmp; + + spin_lock_bh(&vport->addr_tbl.ip_list_lock); + + list_for_each_entry_safe(ip_node, tmp, list, node) { + list_del(&ip_node->node); + kfree(ip_node); + } + + spin_unlock_bh(&vport->addr_tbl.ip_list_lock); +} + +int unic_add_ip_addr(struct unic_dev *unic_dev, struct sockaddr *addr, + u16 ip_mask) +{ + return unic_handle_stack_ip_feedback(&unic_dev->vport, + UNIC_COMM_ADDR_TO_ADD, + addr, ip_mask); +} + +int unic_rm_ip_addr(struct unic_dev *unic_dev, struct sockaddr *addr, + u16 ip_mask) +{ + return unic_handle_stack_ip_feedback(&unic_dev->vport, + UNIC_COMM_ADDR_TO_DEL, + addr, ip_mask); +} diff --git a/drivers/net/ub/unic/unic_rack_ip.h b/drivers/net/ub/unic/unic_rack_ip.h new file mode 100644 index 000000000000..48f62eb0fb70 --- /dev/null +++ b/drivers/net/ub/unic/unic_rack_ip.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_RACK_IP_H__ +#define __UNIC_RACK_IP_H__ + +#include "unic_dev.h" +#include "unic_comm_addr.h" + +#define UNIC_RTATTR_PACK_LENGTH 256 +#define UNIC_IP_TABLE_SIZE 128 +#define UNIC_CTRLQ_IP_REQ_SIZE 4 +#define UNIC_IP_FOURTH_PART 3 +#define UNIC_MASKED_FORMAT_IP_LEN 40 + +enum unic_ctrlq_ip_event { + UNIC_CTRLQ_ADD_IP, + UNIC_CTRLQ_DEL_IP, +}; + +struct unic_netlink_ip_req { + struct nlmsghdr nh; + struct ifaddrmsg info; + char attrbuf[UNIC_RTATTR_PACK_LENGTH]; +}; + +struct unic_ctrlq_query_ip_req { + __le16 ip_index; + u8 resv[18]; +}; + +struct unic_ip_info { + __le32 ip_addr[UNIC_CTRLQ_IP_REQ_SIZE]; + __le16 ip_mask; + u8 resv[2]; +}; + +struct unic_ctrlq_query_ip_resp { + __le16 ip_index; + u8 get_count; + u8 resv; + struct unic_ip_info ip_info[UNIC_CTRLQ_IP_REQ_SIZE]; +}; + +struct unic_ctrlq_ip_notify_req { + __le16 ip_cmd; + __le16 ip_mask; + __le32 ip_addr[UNIC_CTRLQ_IP_REQ_SIZE]; +}; + +struct unic_ctrlq_ip_notify_resp { + u8 resv[20]; +}; + +struct unic_stack_ip_info { + u16 ip_cmd; + u16 ip_mask; + __be32 ip_addr[UNIC_CTRLQ_IP_REQ_SIZE]; +}; + +static inline void unic_format_masked_ip_addr(char *format_masked_ip_addr, + const u8 *ip_addr) +{ +#define IP_START_BYTE 12 + + snprintf(format_masked_ip_addr, UNIC_MASKED_FORMAT_IP_LEN, + "****:****:****:****:****:****:%02x%02x:%02x%02x", + ip_addr[IP_START_BYTE], ip_addr[IP_START_BYTE + 1], + ip_addr[IP_START_BYTE + 2], ip_addr[IP_START_BYTE + 3]); +} + +void unic_sync_rack_ip_table(struct unic_dev *unic_dev); +int unic_handle_notify_ip_event(struct auxiliary_device *adev, u8 service_ver, + void *data, u16 len, u16 seq); +void unic_query_rack_ip(struct auxiliary_device *adev); +void unic_uninit_rack_ip_table(struct unic_dev *unic_dev); +int unic_add_ip_addr(struct unic_dev *unic_dev, struct sockaddr *addr, + u16 ip_mask); +int unic_rm_ip_addr(struct unic_dev *unic_dev, struct sockaddr *addr, + u16 ip_mask); + +#endif diff --git a/include/ub/ubase/ubase_comm_ctrlq.h b/include/ub/ubase/ubase_comm_ctrlq.h index 410b36069a0b..7e772dcb0746 100644 --- a/include/ub/ubase/ubase_comm_ctrlq.h +++ b/include/ub/ubase/ubase_comm_ctrlq.h @@ -45,6 +45,11 @@ enum ubase_ctrlq_opc_type_qos { UBASE_CTRLQ_OPC_QUERY_SL = 0x02, }; +enum ubase_ctrlq_opc_ip { + UBASE_CTRLQ_OPC_NOTIFY_IP = 0x01, + UBASE_CTRLQ_OPC_QUERY_IP = 0x02, +}; + enum ubase_ctrlq_opc_type_dev_register { UBASE_CTRLQ_OPC_CTRLQ_CTRL = 0x14, UBASE_CTRLQ_OPC_UE_RESET_CTRL = 0x15, -- Gitee From 25afcbe5153d3669c5fcec6b830e343ac12e99a9 Mon Sep 17 00:00:00 2001 From: Haibin Lu Date: Thu, 11 Sep 2025 16:35:53 +0800 Subject: [PATCH 177/214] net: unic: add fec statistic support ANBZ: #45460 commit 4292c10fd96f2822fd2e3003792e6e32536a37f6 openEuler. This patch adds fec statistic support for UNIC driver. Signed-off-by: Jianqiang Li Signed-off-by: Junxin Chen Signed-off-by: Haibin Lu Signed-off-by: Xiongchuan Zhou Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/Makefile | 2 +- drivers/net/ub/unic/debugfs/unic_debugfs.c | 1 + drivers/net/ub/unic/unic_cmd.h | 17 ++++++ drivers/net/ub/unic/unic_dev.c | 12 ++++ drivers/net/ub/unic/unic_dev.h | 22 ++++++++ drivers/net/ub/unic/unic_ethtool.c | 2 + drivers/net/ub/unic/unic_hw.c | 65 ++++++++++++++++++++++ drivers/net/ub/unic/unic_hw.h | 1 + drivers/net/ub/unic/unic_netdev.c | 10 ++++ drivers/net/ub/unic/unic_stats.c | 63 +++++++++++++++++++++ drivers/net/ub/unic/unic_stats.h | 22 ++++++++ include/ub/ubase/ubase_comm_cmd.h | 1 + 12 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ub/unic/unic_stats.c create mode 100644 drivers/net/ub/unic/unic_stats.h diff --git a/drivers/net/ub/unic/Makefile b/drivers/net/ub/unic/Makefile index 04b5330b5c27..37b2ccd7753f 100644 --- a/drivers/net/ub/unic/Makefile +++ b/drivers/net/ub/unic/Makefile @@ -8,5 +8,5 @@ ccflags-y += -I$(srctree)/drivers/net/ub/unic/debugfs obj-$(CONFIG_UB_UNIC) += unic.o unic-objs = unic_main.o unic_ethtool.o unic_hw.o unic_guid.o unic_netdev.o unic_dev.o unic_qos_hw.o unic_event.o unic_crq.o -unic-objs += debugfs/unic_debugfs.o unic_rx.o unic_tx.o unic_txrx.o unic_comm_addr.o unic_rack_ip.o +unic-objs += debugfs/unic_debugfs.o unic_rx.o unic_tx.o unic_txrx.o unic_comm_addr.o unic_rack_ip.o unic_stats.o unic-$(CONFIG_UB_UNIC_DCB) += unic_dcbnl.o diff --git a/drivers/net/ub/unic/debugfs/unic_debugfs.c b/drivers/net/ub/unic/debugfs/unic_debugfs.c index 15f6a2976b8f..f17def4f8537 100644 --- a/drivers/net/ub/unic/debugfs/unic_debugfs.c +++ b/drivers/net/ub/unic/debugfs/unic_debugfs.c @@ -35,6 +35,7 @@ static const struct unic_dbg_cap_bit_info { {"\tsupport_tc_speed_limit: %u\n", &unic_dev_tc_speed_limit_supported}, {"\tsupport_tx_csum_offload: %u\n", &unic_dev_tx_csum_offload_supported}, {"\tsupport_rx_csum_offload: %u\n", &unic_dev_rx_csum_offload_supported}, + {"\tsupport_fec_stats: %u\n", &unic_dev_fec_stats_supported}, }; static void unic_dbg_dump_caps_bits(struct unic_dev *unic_dev, diff --git a/drivers/net/ub/unic/unic_cmd.h b/drivers/net/ub/unic/unic_cmd.h index 4b263009b3f1..607baf7d533c 100644 --- a/drivers/net/ub/unic/unic_cmd.h +++ b/drivers/net/ub/unic/unic_cmd.h @@ -111,6 +111,23 @@ struct unic_cfg_fec_cmd { u8 rsv[20]; }; +struct unic_query_fec_stats_item { + __le32 corr_blocks_l; + __le32 corr_blocks_h; + __le32 uncorr_blocks_l; + __le32 uncorr_blocks_h; + __le32 corr_bits_l; + __le32 corr_bits_h; +}; + +#define UNIC_FEC_STATS_MAX_LANE 8 +struct unic_query_fec_stats_resp { + struct unic_query_fec_stats_item total; + struct unic_query_fec_stats_item lane[UNIC_FEC_STATS_MAX_LANE]; + u8 lane_num; + u8 rsv[31]; +}; + struct unic_query_flush_status_resp { u8 status; u8 rsv[23]; diff --git a/drivers/net/ub/unic/unic_dev.c b/drivers/net/ub/unic/unic_dev.c index c1a51eb20c7f..9a9dffcd4441 100644 --- a/drivers/net/ub/unic/unic_dev.c +++ b/drivers/net/ub/unic/unic_dev.c @@ -556,6 +556,14 @@ static int unic_init_mac(struct unic_dev *unic_dev) return 0; } +static void unic_update_stats_for_all(struct unic_dev *unic_dev) +{ + if (!unic_dev_ubl_supported(unic_dev) && + unic_dev_fec_stats_supported(unic_dev) && + unic_dev->hw.mac.fec_mode != ETHTOOL_FEC_OFF) + unic_update_fec_stats(unic_dev); +} + static void unic_task_schedule(struct unic_dev *unic_dev, unsigned long delay_time) { @@ -565,6 +573,7 @@ static void unic_task_schedule(struct unic_dev *unic_dev, static void unic_periodic_service_task(struct unic_dev *unic_dev) { +#define UNIC_UPDATE_STATS_TIMER_INTERVAL 300UL unsigned long delta = round_jiffies_relative(HZ); unic_link_status_update(unic_dev); @@ -572,6 +581,9 @@ static void unic_periodic_service_task(struct unic_dev *unic_dev) unic_sync_rack_ip_table(unic_dev); unic_sync_promisc_mode(unic_dev); + if (!(unic_dev->serv_processed_cnt % UNIC_UPDATE_STATS_TIMER_INTERVAL)) + unic_update_stats_for_all(unic_dev); + unic_dev->serv_processed_cnt++; unic_task_schedule(unic_dev, delta); } diff --git a/drivers/net/ub/unic/unic_dev.h b/drivers/net/ub/unic/unic_dev.h index 6469d8c72b54..64e67ddf2704 100644 --- a/drivers/net/ub/unic/unic_dev.h +++ b/drivers/net/ub/unic/unic_dev.h @@ -170,6 +170,22 @@ struct unic_caps { u16 max_int_gl; /* max value of interrupt coalesce based on INT_GL */ }; +struct unic_fec_stats_item { + u64 corr_blocks; + u64 uncorr_blocks; + u64 corr_bits; +}; + +struct unic_fec_stats { + u8 lane_num; + struct unic_fec_stats_item total; + struct unic_fec_stats_item lane[UNIC_FEC_STATS_MAX_LANE]; +}; + +struct unic_stats { + struct unic_fec_stats fec_stats; +}; + struct unic_addr_tbl { spinlock_t ip_list_lock; /* protect ip address need to add/detele */ struct list_head ip_list; /* Store ip table */ @@ -209,6 +225,7 @@ struct unic_dev { unsigned long state; struct delayed_work service_task; struct ubase_event_nb ae_nbs[UNIC_AE_LEVEL_NUM]; + struct unic_stats stats; u8 netdev_flags; u8 loopback_flags; struct unic_vport vport; @@ -266,6 +283,11 @@ static inline bool unic_dev_rx_csum_offload_supported(struct unic_dev *unic_dev) return unic_get_cap_bit(unic_dev, UNIC_SUPPORT_RX_CSUM_OFFLOAD_B); } +static inline bool unic_dev_fec_stats_supported(struct unic_dev *unic_dev) +{ + return unic_get_cap_bit(unic_dev, UNIC_SUPPORT_FEC_STATS_B); +} + static inline bool __unic_removing(struct unic_dev *unic_dev) { return test_bit(UNIC_STATE_REMOVING, &unic_dev->state); diff --git a/drivers/net/ub/unic/unic_ethtool.c b/drivers/net/ub/unic/unic_ethtool.c index c6577bbf7468..b53443535351 100644 --- a/drivers/net/ub/unic/unic_ethtool.c +++ b/drivers/net/ub/unic/unic_ethtool.c @@ -11,6 +11,7 @@ #include "unic_dev.h" #include "unic_hw.h" #include "unic_netdev.h" +#include "unic_stats.h" #include "unic_ethtool.h" static u32 unic_get_link_status(struct net_device *netdev) @@ -60,6 +61,7 @@ static const struct ethtool_ops unic_ethtool_ops = { .supported_coalesce_params = UNIC_ETHTOOL_COALESCE, .get_link = unic_get_link_status, .get_drvinfo = unic_get_driver_info, + .get_fec_stats = unic_get_fec_stats, }; void unic_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/ub/unic/unic_hw.c b/drivers/net/ub/unic/unic_hw.c index 0a35a7da3b84..5f454dbf6f3b 100644 --- a/drivers/net/ub/unic/unic_hw.c +++ b/drivers/net/ub/unic/unic_hw.c @@ -652,3 +652,68 @@ int unic_set_fec_mode(struct unic_dev *unic_dev, u32 fec_mode) return ret; } + +static void unic_fetch_fec_stats(struct unic_dev *unic_dev, + struct unic_fec_stats_item *unic_stats_item, + struct unic_query_fec_stats_item *resp_item) +{ +#define FETCH_FEC_STATS(high, low) (((u64)le32_to_cpu(high) << 32) | \ + (u64)le32_to_cpu(low)) + + if (unic_dev_ubl_supported(unic_dev)) { + unic_stats_item->corr_blocks = + FETCH_FEC_STATS(resp_item->corr_blocks_h, + resp_item->corr_blocks_l); + unic_stats_item->uncorr_blocks = + FETCH_FEC_STATS(resp_item->uncorr_blocks_h, + resp_item->uncorr_blocks_l); + unic_stats_item->corr_bits = + FETCH_FEC_STATS(resp_item->corr_bits_h, + resp_item->corr_bits_l); + } else { + unic_stats_item->corr_blocks += + FETCH_FEC_STATS(resp_item->corr_blocks_h, + resp_item->corr_blocks_l); + unic_stats_item->uncorr_blocks += + FETCH_FEC_STATS(resp_item->uncorr_blocks_h, + resp_item->uncorr_blocks_l); + unic_stats_item->corr_bits += + FETCH_FEC_STATS(resp_item->corr_bits_h, + resp_item->corr_bits_l); + } +} + +int unic_update_fec_stats(struct unic_dev *unic_dev) +{ + struct unic_query_fec_stats_resp resp = {0}; + struct unic_fec_stats *fec_stats; + struct ubase_cmd_buf in, out; + u8 i, lane_num; + int ret; + + if (test_and_set_bit(UNIC_STATE_FEC_STATS_UPDATING, &unic_dev->state)) + return -EBUSY; + + ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_FEC_STATS, true, 0, NULL); + ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_FEC_STATS, false, + sizeof(resp), &resp); + + ret = ubase_cmd_send_inout(unic_dev->comdev.adev, &in, &out); + if (ret) { + unic_err(unic_dev, + "failed to query fec stats, ret = %d.\n", ret); + goto err_send_cmd; + } + + fec_stats = &unic_dev->stats.fec_stats; + fec_stats->lane_num = resp.lane_num; + unic_fetch_fec_stats(unic_dev, &fec_stats->total, &resp.total); + lane_num = min_t(u8, fec_stats->lane_num, UNIC_FEC_STATS_MAX_LANE); + for (i = 0; i < lane_num; i++) + unic_fetch_fec_stats(unic_dev, &fec_stats->lane[i], + &resp.lane[i]); + +err_send_cmd: + clear_bit(UNIC_STATE_FEC_STATS_UPDATING, &unic_dev->state); + return ret; +} diff --git a/drivers/net/ub/unic/unic_hw.h b/drivers/net/ub/unic/unic_hw.h index c72a9d60d132..72e56cc705cd 100644 --- a/drivers/net/ub/unic/unic_hw.h +++ b/drivers/net/ub/unic/unic_hw.h @@ -72,5 +72,6 @@ int unic_set_promisc_mode(struct unic_dev *unic_dev, int unic_cfg_vport_buf(struct unic_dev *unic_dev, bool init); int unic_set_fec_mode(struct unic_dev *unic_dev, u32 fec_mode); +int unic_update_fec_stats(struct unic_dev *unic_dev); #endif diff --git a/drivers/net/ub/unic/unic_netdev.c b/drivers/net/ub/unic/unic_netdev.c index b4b52d95bc74..538aa704272a 100644 --- a/drivers/net/ub/unic/unic_netdev.c +++ b/drivers/net/ub/unic/unic_netdev.c @@ -164,6 +164,14 @@ static int unic_net_up(struct net_device *netdev) return 0; } +static void unic_clear_fec_stats(struct unic_dev *unic_dev) +{ + struct unic_fec_stats *fec_stats = &unic_dev->stats.fec_stats; + + if (!unic_dev_ubl_supported(unic_dev)) + memset(fec_stats, 0, sizeof(*fec_stats)); +} + void unic_link_status_change(struct net_device *netdev, bool linkup) { struct unic_dev *unic_dev = netdev_priv(netdev); @@ -176,6 +184,7 @@ void unic_link_status_change(struct net_device *netdev, bool linkup) if (!test_bit(UNIC_STATE_TESTING, &unic_dev->state)) { netif_tx_wake_all_queues(netdev); netif_carrier_on(netdev); + unic_clear_fec_stats(unic_dev); } } else { netif_carrier_off(netdev); @@ -298,6 +307,7 @@ int unic_net_open_no_link_change(struct net_device *netdev) if (unic_dev->sw_link_status == UNIC_LINK_STATUS_UP) { netif_tx_wake_all_queues(netdev); netif_carrier_on(netdev); + unic_clear_fec_stats(unic_dev); } return 0; diff --git a/drivers/net/ub/unic/unic_stats.c b/drivers/net/ub/unic/unic_stats.c new file mode 100644 index 000000000000..8167bb6a6fca --- /dev/null +++ b/drivers/net/ub/unic/unic_stats.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include +#include +#include + +#include "unic.h" +#include "unic_dev.h" +#include "unic_hw.h" +#include "unic_netdev.h" +#include "unic_stats.h" + +static void unic_get_fec_stats_total(struct unic_dev *unic_dev, u8 stats_flags, + struct ethtool_fec_stats *fec_stats) +{ + struct unic_fec_stats_item *total = &unic_dev->stats.fec_stats.total; + + if (stats_flags & UNIC_FEC_CORR_BLOCKS) + fec_stats->corrected_blocks.total = total->corr_blocks; + if (stats_flags & UNIC_FEC_UNCORR_BLOCKS) + fec_stats->uncorrectable_blocks.total = total->uncorr_blocks; + if (stats_flags & UNIC_FEC_CORR_BITS) + fec_stats->corrected_bits.total = total->corr_bits; +} + +static void unic_get_ubl_fec_stats(struct unic_dev *unic_dev, + struct ethtool_fec_stats *fec_stats) +{ + u32 fec_mode = unic_dev->hw.mac.fec_mode; + u8 stats_flags = 0; + + switch (fec_mode) { + case ETHTOOL_FEC_RS: + stats_flags = UNIC_FEC_UNCORR_BLOCKS | UNIC_FEC_CORR_BITS; + unic_get_fec_stats_total(unic_dev, stats_flags, fec_stats); + break; + default: + unic_err(unic_dev, + "fec stats is not supported in mode(0x%x).\n", + fec_mode); + break; + } +} + +void unic_get_fec_stats(struct net_device *ndev, + struct ethtool_fec_stats *fec_stats) +{ + struct unic_dev *unic_dev = netdev_priv(ndev); + + if (!unic_dev_fec_stats_supported(unic_dev) || + unic_dev->hw.mac.fec_mode == ETHTOOL_FEC_OFF) + return; + + if (unic_update_fec_stats(unic_dev)) + return; + + if (unic_dev_ubl_supported(unic_dev)) + unic_get_ubl_fec_stats(unic_dev, fec_stats); +} diff --git a/drivers/net/ub/unic/unic_stats.h b/drivers/net/ub/unic/unic_stats.h new file mode 100644 index 000000000000..d9d9f0b0ee3b --- /dev/null +++ b/drivers/net/ub/unic/unic_stats.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_STATS_H__ +#define __UNIC_STATS_H__ + +#include +#include +#include +#include + +#define UNIC_FEC_CORR_BLOCKS BIT(0) +#define UNIC_FEC_UNCORR_BLOCKS BIT(1) +#define UNIC_FEC_CORR_BITS BIT(2) + +void unic_get_fec_stats(struct net_device *ndev, + struct ethtool_fec_stats *fec_stats); + +#endif /* __UNIC_STATS_H__ */ diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index 62ad1ee97d9b..b41a1e05747e 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -77,6 +77,7 @@ enum ubase_opcode_type { UBASE_OPC_CONFIG_FEC_MODE = 0x6102, UBASE_OPC_QUERY_PORT_INFO = 0x6200, UBASE_OPC_QUERY_CHIP_INFO = 0x6201, + UBASE_OPC_QUERY_FEC_STATS = 0x6202, /* Mailbox commands */ UBASE_OPC_POST_MB = 0x7000, -- Gitee From 54cd86e0e86672df8a3baadae01cfe49363809be Mon Sep 17 00:00:00 2001 From: Junxin Chen Date: Wed, 20 Aug 2025 15:39:01 +0800 Subject: [PATCH 178/214] net: unic: support querying and configuring FEC parameters. ANBZ: #45460 commit 23682c1dcbd1efff73bd73d922e399706550edc3 openEuler. FEC is a way to ensure the correctness of transmitted data at the link layer. To adapt to the ub protocol, this patch adds the capability of querying and configuring FEC parameters for UNIC driver. Users can use the ethtool to query or configure FEC. Signed-off-by: Jianqiang Li Signed-off-by: Haibin Lu Signed-off-by: Junxin Chen Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/unic_ethtool.c | 62 ++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/drivers/net/ub/unic/unic_ethtool.c b/drivers/net/ub/unic/unic_ethtool.c index b53443535351..a789108ebeb5 100644 --- a/drivers/net/ub/unic/unic_ethtool.c +++ b/drivers/net/ub/unic/unic_ethtool.c @@ -23,6 +23,17 @@ static u32 unic_get_link_status(struct net_device *netdev) return unic_dev->sw_link_status; } +static int unic_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + + /* Ensure that the latest information is obtained. */ + unic_update_port_info(unic_dev); + + return 0; +} + static void unic_get_driver_info(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { @@ -49,6 +60,54 @@ static void unic_get_driver_info(struct net_device *netdev, u32_get_bits(fw_version, UBASE_FW_VERSION_BYTE0_MASK)); } +static int unic_get_fecparam(struct net_device *ndev, + struct ethtool_fecparam *fec) +{ + struct unic_dev *unic_dev = netdev_priv(ndev); + struct unic_mac *mac = &unic_dev->hw.mac; + + if (!unic_dev_fec_supported(unic_dev)) + return -EOPNOTSUPP; + + fec->fec = mac->fec_ability; + fec->active_fec = mac->fec_mode; + + return 0; +} + +static int unic_set_fecparam(struct net_device *ndev, + struct ethtool_fecparam *fec) +{ + struct unic_dev *unic_dev = netdev_priv(ndev); + struct unic_mac *mac = &unic_dev->hw.mac; + u32 fec_mode; + int ret; + + if (!unic_dev_fec_supported(unic_dev)) + return -EOPNOTSUPP; + + fec_mode = fec->fec; + if (!(mac->fec_ability & fec_mode)) { + unic_err(unic_dev, + "unsupported fec mode, fec mode = %u.\n", fec_mode); + return -EINVAL; + } + + if (mac->fec_mode == fec_mode) + return 0; + + if (netif_msg_ifdown(unic_dev)) + unic_info(unic_dev, "set fec mode = %u.\n", fec_mode); + + ret = unic_set_fec_mode(unic_dev, fec_mode); + if (ret) + return ret; + + mac->user_fec_mode = fec_mode; + + return 0; +} + #define UNIC_ETHTOOL_RING (ETHTOOL_RING_USE_RX_BUF_LEN | \ ETHTOOL_RING_USE_TX_PUSH) #define UNIC_ETHTOOL_COALESCE (ETHTOOL_COALESCE_USECS | \ @@ -60,7 +119,10 @@ static const struct ethtool_ops unic_ethtool_ops = { .cap_link_lanes_supported = true, .supported_coalesce_params = UNIC_ETHTOOL_COALESCE, .get_link = unic_get_link_status, + .get_link_ksettings = unic_get_link_ksettings, .get_drvinfo = unic_get_driver_info, + .get_fecparam = unic_get_fecparam, + .set_fecparam = unic_set_fecparam, .get_fec_stats = unic_get_fec_stats, }; -- Gitee From cf35cb48fb64dddc374c4d997133805ee9e3ad81 Mon Sep 17 00:00:00 2001 From: Junxin Chen Date: Wed, 20 Aug 2025 16:32:38 +0800 Subject: [PATCH 179/214] net: unic: support querying and configuring MTU. ANBZ: #45460 commit 78ed212d111e9738e333007f79d5ff3d714f0660 openEuler. MTU indicates the maximum size of a packet on a physical link. To adapt to the ub protocol, this patch adds the capability of querying and configuring MTU for UNIC driver. Users can use the iproute2 to query or configure MTU. Signed-off-by: Fengyan Mu Signed-off-by: Haibin Lu Signed-off-by: Junxin Chen Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/debugfs/unic_debugfs.c | 11 ++++++++ drivers/net/ub/unic/unic_dev.c | 24 +++++++++++++++++ drivers/net/ub/unic/unic_dev.h | 1 + drivers/net/ub/unic/unic_hw.c | 22 ++++++++++++++++ drivers/net/ub/unic/unic_hw.h | 2 ++ drivers/net/ub/unic/unic_netdev.c | 30 ++++++++++++++++++++++ 6 files changed, 90 insertions(+) diff --git a/drivers/net/ub/unic/debugfs/unic_debugfs.c b/drivers/net/ub/unic/debugfs/unic_debugfs.c index f17def4f8537..84bee8d6946a 100644 --- a/drivers/net/ub/unic/debugfs/unic_debugfs.c +++ b/drivers/net/ub/unic/debugfs/unic_debugfs.c @@ -18,10 +18,21 @@ static int unic_dbg_dump_dev_info(struct seq_file *s, void *data) { struct unic_dev *unic_dev = dev_get_drvdata(s->private); struct net_device *netdev = unic_dev->comdev.netdev; + u16 max_frame_size; + int ret; seq_printf(s, "%-25s", "DEV_NAME:"); seq_printf(s, "%-12s\n", netdev->name); + if (!unic_dev_ubl_supported(unic_dev)) { + ret = unic_check_validate_dump_mtu(unic_dev, netdev->mtu, + &max_frame_size); + if (!ret) { + seq_printf(s, "%-25s", "MAX_FRAME_SIZE:"); + seq_printf(s, "%-12u\n", max_frame_size); + } + } + return 0; } diff --git a/drivers/net/ub/unic/unic_dev.c b/drivers/net/ub/unic/unic_dev.c index 9a9dffcd4441..11c48c9e4709 100644 --- a/drivers/net/ub/unic/unic_dev.c +++ b/drivers/net/ub/unic/unic_dev.c @@ -556,6 +556,30 @@ static int unic_init_mac(struct unic_dev *unic_dev) return 0; } +int unic_set_mtu(struct unic_dev *unic_dev, int new_mtu) +{ + u16 max_frame_size; + int ret; + + new_mtu = max(new_mtu, UB_DATA_LEN); + + ret = unic_check_validate_dump_mtu(unic_dev, new_mtu, &max_frame_size); + if (ret == -EPERM) { + return 0; + } else if (ret < 0) { + unic_err(unic_dev, "invalid MTU(%d), please check, ret = %d.\n", + new_mtu, ret); + return -EINVAL; + } + + ret = unic_config_mtu(unic_dev, new_mtu); + if (ret) + unic_err(unic_dev, + "failed to change MTU to %d, ret = %d.\n", new_mtu, ret); + + return ret; +} + static void unic_update_stats_for_all(struct unic_dev *unic_dev) { if (!unic_dev_ubl_supported(unic_dev) && diff --git a/drivers/net/ub/unic/unic_dev.h b/drivers/net/ub/unic/unic_dev.h index 64e67ddf2704..869b0f8bf749 100644 --- a/drivers/net/ub/unic/unic_dev.h +++ b/drivers/net/ub/unic/unic_dev.h @@ -238,6 +238,7 @@ struct unic_dev { int unic_dev_init(struct auxiliary_device *adev); void unic_dev_uninit(struct auxiliary_device *adev); +int unic_set_mtu(struct unic_dev *unic_dev, int new_mtu); u32 unic_channels_max_num(struct auxiliary_device *adev); int unic_init_channels(struct unic_dev *unic_dev, u32 channels_num); void unic_uninit_channels(struct unic_dev *unic_dev); diff --git a/drivers/net/ub/unic/unic_hw.c b/drivers/net/ub/unic/unic_hw.c index 5f454dbf6f3b..2e97d03d01a3 100644 --- a/drivers/net/ub/unic/unic_hw.c +++ b/drivers/net/ub/unic/unic_hw.c @@ -500,6 +500,28 @@ int unic_query_dev_res(struct unic_dev *unic_dev) return 0; } +int unic_check_validate_dump_mtu(struct unic_dev *unic_dev, int new_mtu, + u16 *max_frame_size) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct unic_config_max_trans_unit_cmd resp = {0}; + struct unic_config_max_trans_unit_cmd req = {0}; + struct ubase_cmd_buf out; + struct ubase_cmd_buf in; + int ret; + + req.max_trans_unit = cpu_to_le16(new_mtu); + + ubase_fill_inout_buf(&in, UBASE_OPC_CFG_MTU, true, sizeof(req), &req); + ubase_fill_inout_buf(&out, UBASE_OPC_CFG_MTU, true, sizeof(resp), &resp); + + ret = ubase_cmd_send_inout(adev, &in, &out); + if (!ret) + *max_frame_size = le16_to_cpu(resp.max_trans_unit); + + return ret; +} + int unic_config_mtu(struct unic_dev *unic_dev, int new_mtu) { struct auxiliary_device *adev = unic_dev->comdev.adev; diff --git a/drivers/net/ub/unic/unic_hw.h b/drivers/net/ub/unic/unic_hw.h index 72e56cc705cd..a24d050ea00e 100644 --- a/drivers/net/ub/unic/unic_hw.h +++ b/drivers/net/ub/unic/unic_hw.h @@ -59,6 +59,8 @@ int unic_set_mac_autoneg(struct unic_dev *unic_dev, u8 autoneg); int unic_query_dev_res(struct unic_dev *unic_dev); +int unic_check_validate_dump_mtu(struct unic_dev *unic_dev, int new_mtu, + u16 *max_frame_size); int unic_config_mtu(struct unic_dev *unic_dev, int new_mtu); int unic_mac_cfg(struct unic_dev *unic_dev, bool enable); diff --git a/drivers/net/ub/unic/unic_netdev.c b/drivers/net/ub/unic/unic_netdev.c index 538aa704272a..124770ba1e52 100644 --- a/drivers/net/ub/unic/unic_netdev.c +++ b/drivers/net/ub/unic/unic_netdev.c @@ -476,6 +476,35 @@ static void unic_get_stats64(struct net_device *netdev, stats->rx_compressed = netdev->stats.rx_compressed; } +static int unic_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct unic_dev *unic_dev = (struct unic_dev *)netdev_priv(netdev); + int ret; + + if (netif_running(netdev)) { + unic_err(unic_dev, "failed to change MTU, due to network interface is up, please down it first and try again.\n"); + return -EBUSY; + } + + if (unic_resetting(netdev)) + return -EBUSY; + + if (netif_msg_ifdown(unic_dev)) + unic_info(unic_dev, "change mtu from %u to %d.\n", + netdev->mtu, new_mtu); + + ret = unic_set_mtu(unic_dev, new_mtu); + if (ret) { + unic_err(unic_dev, "failed to change MTU in hardware, ret = %d.\n", + ret); + return ret; + } + + netdev->mtu = (u32)new_mtu; + + return 0; +} + static void unic_tx_timeout(struct net_device *netdev, u32 queue_idx) { struct unic_dev *unic_dev = netdev_priv(netdev); @@ -531,6 +560,7 @@ static const struct net_device_ops unic_netdev_ops = { .ndo_get_stats64 = unic_get_stats64, .ndo_start_xmit = unic_start_xmit, .ndo_tx_timeout = unic_tx_timeout, + .ndo_change_mtu = unic_change_mtu, .ndo_open = unic_net_open, .ndo_stop = unic_net_stop, .ndo_select_queue = unic_select_queue, -- Gitee From c57f176b70bb1faace959592384038f1ca680911 Mon Sep 17 00:00:00 2001 From: Junxin Chen Date: Wed, 20 Aug 2025 18:08:39 +0800 Subject: [PATCH 180/214] net: unic: support querying and configuring promisc. ANBZ: #45460 commit 63341a0569f26ebc205cfe0e09b37a3dfcebf2dd openEuler. The promiscuous function enables user to capture all packets on the network link, which helps user analyze problems. To adapt to the ub protocol, This patch adds the capability of querying and configuring promisc for UNIC driver. Users can use the iproute2/debugfs to query or configure promisc. Signed-off-by: Xiaobo Zhang Signed-off-by: Haibin Lu Signed-off-by: Junxin Chen Signed-off-by: Xiongchuan Zhou Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/debugfs/unic_debugfs.c | 30 ++++++++++++++++++++++ drivers/net/ub/unic/unic_cmd.h | 5 +++- drivers/net/ub/unic/unic_hw.c | 19 ++++++++++++++ drivers/net/ub/unic/unic_hw.h | 2 ++ drivers/net/ub/unic/unic_netdev.c | 28 ++++++++++++++++++++ 5 files changed, 83 insertions(+), 1 deletion(-) diff --git a/drivers/net/ub/unic/debugfs/unic_debugfs.c b/drivers/net/ub/unic/debugfs/unic_debugfs.c index 84bee8d6946a..822b51ce9caf 100644 --- a/drivers/net/ub/unic/debugfs/unic_debugfs.c +++ b/drivers/net/ub/unic/debugfs/unic_debugfs.c @@ -136,6 +136,29 @@ static int unic_dbg_dump_page_pool_info(struct seq_file *s, void *data) return ret; } +static int unic_dbg_dump_promisc_cfg_hw(struct seq_file *s, void *data) +{ + struct unic_dev *unic_dev = dev_get_drvdata(s->private); + struct unic_promisc_cfg_cmd resp = {0}; + int ret; + + if (__unic_resetting(unic_dev)) + return -EBUSY; + + ret = unic_get_promisc_mode(unic_dev, &resp); + if (ret) + return ret; + + seq_printf(s, "rx_uc_ip_en : %-2u\n", resp.promisc_rx_uc_ip_en); + seq_printf(s, "rx_uc_guid_en : %-2u\n", resp.promisc_rx_uc_guid_en); + seq_printf(s, "rx_mc_en : %-2u\n", resp.promisc_rx_mc_en); + seq_printf(s, "rx_uc_mac_en : %-2u\n", resp.promisc_rx_uc_mac_en); + seq_printf(s, "rx_mc_mac_en : %-2u\n", resp.promisc_rx_mc_mac_en); + seq_printf(s, "rx_bc_en : %-2u\n", resp.promisc_rx_bc_en); + + return 0; +} + static bool unic_dbg_dentry_support(struct device *dev, u32 property) { struct unic_dev *unic_dev = dev_get_drvdata(dev); @@ -174,6 +197,13 @@ static struct ubase_dbg_cmd_info unic_dbg_cmd[] = { .support = unic_dbg_dentry_support, .init = ubase_dbg_seq_file_init, .read_func = unic_dbg_dump_page_pool_info, + }, { + .name = "promisc_cfg_hw", + .dentry_index = UNIC_DBG_DENTRY_ROOT, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_promisc_cfg_hw, } }; diff --git a/drivers/net/ub/unic/unic_cmd.h b/drivers/net/ub/unic/unic_cmd.h index 607baf7d533c..bf3e11e343cd 100644 --- a/drivers/net/ub/unic/unic_cmd.h +++ b/drivers/net/ub/unic/unic_cmd.h @@ -90,7 +90,10 @@ struct unic_promisc_cfg_cmd { u8 promisc_rx_uc_ip_en : 1; /* promisc rx ip uc enable or not */ u8 promisc_rx_uc_guid_en : 1; /* promisc rx guid uc enable or not */ u8 promisc_rx_mc_en : 1; /* promisc rx mc enable or not */ - u8 rsv2 : 5; + u8 promisc_rx_uc_mac_en : 1; /* promisc rx mac uc enable or not */ + u8 promisc_rx_mc_mac_en : 1; /* promisc rx mac mc enable or not */ + u8 promisc_rx_bc_en : 1; /* promisc rx bc enable or not */ + u8 rsv2 : 2; u8 rsv3[21]; }; diff --git a/drivers/net/ub/unic/unic_hw.c b/drivers/net/ub/unic/unic_hw.c index 2e97d03d01a3..381482346020 100644 --- a/drivers/net/ub/unic/unic_hw.c +++ b/drivers/net/ub/unic/unic_hw.c @@ -299,6 +299,25 @@ static void unic_setup_promisc_req(struct unic_promisc_cfg_cmd *req, req->promisc_rx_mc_en = promisc_en->en_mc; } +int unic_get_promisc_mode(struct unic_dev *unic_dev, + struct unic_promisc_cfg_cmd *resp) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct ubase_cmd_buf in, out; + int ret; + + ubase_fill_inout_buf(&in, UBASE_OPC_CFG_PROMISC_MODE, true, 0, NULL); + ubase_fill_inout_buf(&out, UBASE_OPC_CFG_PROMISC_MODE, true, + sizeof(*resp), resp); + + ret = ubase_cmd_send_inout(adev, &in, &out); + if (ret) + dev_err(adev->dev.parent, + "failed to query promisc mode, ret = %d.\n", ret); + + return ret; +} + int unic_set_promisc_mode(struct unic_dev *unic_dev, struct unic_promisc_en *promisc_en) { diff --git a/drivers/net/ub/unic/unic_hw.h b/drivers/net/ub/unic/unic_hw.h index a24d050ea00e..04f6d22e8a0c 100644 --- a/drivers/net/ub/unic/unic_hw.h +++ b/drivers/net/ub/unic/unic_hw.h @@ -69,6 +69,8 @@ int unic_mac_mode_cfg(struct net_device *netdev, bool enable); int unic_sync_promisc_mode(struct unic_dev *unic_dev); int unic_activate_promisc_mode(struct unic_dev *unic_dev, bool activate); void unic_fill_promisc_en(struct unic_promisc_en *promisc_en, u8 flags); +int unic_get_promisc_mode(struct unic_dev *unic_dev, + struct unic_promisc_cfg_cmd *resp); int unic_set_promisc_mode(struct unic_dev *unic_dev, struct unic_promisc_en *promisc_en); diff --git a/drivers/net/ub/unic/unic_netdev.c b/drivers/net/ub/unic/unic_netdev.c index 124770ba1e52..d8680ff9f894 100644 --- a/drivers/net/ub/unic/unic_netdev.c +++ b/drivers/net/ub/unic/unic_netdev.c @@ -505,6 +505,33 @@ static int unic_change_mtu(struct net_device *netdev, int new_mtu) return 0; } +static u8 unic_get_netdev_flags(struct net_device *netdev) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + u8 flags = 0; + + if (netdev->flags & IFF_PROMISC) { + if (unic_dev_ubl_supported(unic_dev)) + flags = UNIC_USER_UPE | UNIC_USER_MPE; + } else if (netdev->flags & IFF_ALLMULTI) { + flags = UNIC_USER_MPE; + } + + return flags; +} + +static void unic_set_rx_mode(struct net_device *netdev) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + struct unic_vport *vport = &unic_dev->vport; + u8 new_flags; + + new_flags = unic_get_netdev_flags(netdev); + unic_dev->netdev_flags = new_flags; + + set_bit(UNIC_VPORT_STATE_PROMISC_CHANGE, &vport->state); +} + static void unic_tx_timeout(struct net_device *netdev, u32 queue_idx) { struct unic_dev *unic_dev = netdev_priv(netdev); @@ -563,6 +590,7 @@ static const struct net_device_ops unic_netdev_ops = { .ndo_change_mtu = unic_change_mtu, .ndo_open = unic_net_open, .ndo_stop = unic_net_stop, + .ndo_set_rx_mode = unic_set_rx_mode, .ndo_select_queue = unic_select_queue, }; -- Gitee From 364dadbb91b7b9180a37ff04b65937354ab821d5 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Fri, 10 Oct 2025 11:43:01 +0800 Subject: [PATCH 181/214] net: unic: support querying and configuring simple RSS. ANBZ: #45460 commit 69e7ccde4d6cb74cf9c5a3ade8d2e45fc9f50fc9 openEuler. RSS enables hardware to determine the queue to which packets are sent after receiving packets. Here, hardware provides a simple RSS function, and UNIC driver only needs to configure packet receiving queue corresponding to the RSS. Hardware will select the receiving queue based on packet characteristics. To adapt to the ub protocol, This patch adds the capability of querying and configuring simple RSS for UNIC driver. Users can use debugfs to query simple RSS configuration. Signed-off-by: Xiaobo Zhang Signed-off-by: Haibin Lu Signed-off-by: Junxin Chen Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/debugfs/unic_debugfs.c | 38 +++++++++ drivers/net/ub/unic/unic_dev.c | 24 ++++++ drivers/net/ub/unic/unic_dev.h | 5 ++ drivers/net/ub/unic/unic_hw.c | 98 ++++++++++++++++++++++ drivers/net/ub/unic/unic_hw.h | 22 +++++ include/ub/ubase/ubase_comm_cmd.h | 1 + 6 files changed, 188 insertions(+) diff --git a/drivers/net/ub/unic/debugfs/unic_debugfs.c b/drivers/net/ub/unic/debugfs/unic_debugfs.c index 822b51ce9caf..112e64c25a34 100644 --- a/drivers/net/ub/unic/debugfs/unic_debugfs.c +++ b/drivers/net/ub/unic/debugfs/unic_debugfs.c @@ -43,6 +43,7 @@ static const struct unic_dbg_cap_bit_info { {"\tsupport_ubl: %u\n", &unic_dev_ubl_supported}, {"\tsupport_ets: %u\n", &unic_dev_ets_supported}, {"\tsupport_fec: %u\n", &unic_dev_fec_supported}, + {"\tsupport_rss: %u\n", &unic_dev_rss_supported}, {"\tsupport_tc_speed_limit: %u\n", &unic_dev_tc_speed_limit_supported}, {"\tsupport_tx_csum_offload: %u\n", &unic_dev_tx_csum_offload_supported}, {"\tsupport_rx_csum_offload: %u\n", &unic_dev_rx_csum_offload_supported}, @@ -136,6 +137,36 @@ static int unic_dbg_dump_page_pool_info(struct seq_file *s, void *data) return ret; } +static int unic_dbg_dump_rss_cfg_hw(struct seq_file *s, void *data) +{ +#define UNIC_RSS_CFG_ITEMS_NUM 6 + + struct unic_dev *unic_dev = dev_get_drvdata(s->private); + struct unic_cfg_rss_cmd resp = {0}; + u16 jfr_cnt; + u16 i = 0; + int ret; + + if (__unic_resetting(unic_dev)) + return -EBUSY; + + seq_puts(s, "TC_VAILD TC_MODE JFR_0 JFR_1 JFR_2 JFR_3\n"); + + ret = unic_query_rss_cfg(unic_dev, &resp); + if (ret) + return ret; + + seq_printf(s, "%-10u", resp.tc_vaild); + seq_printf(s, "%-9u", resp.tc_mode); + jfr_cnt = min(le16_to_cpu(resp.jfr_reg_num), UNIC_RSS_MAX_CNT); + jfr_cnt = min(UNIC_RSS_CFG_ITEMS_NUM, jfr_cnt); + for (i = 0; i < jfr_cnt; i++) + seq_printf(s, "%-7u", le16_to_cpu(resp.jfr_idx[i])); + seq_puts(s, "\n"); + + return 0; +} + static int unic_dbg_dump_promisc_cfg_hw(struct seq_file *s, void *data) { struct unic_dev *unic_dev = dev_get_drvdata(s->private); @@ -197,6 +228,13 @@ static struct ubase_dbg_cmd_info unic_dbg_cmd[] = { .support = unic_dbg_dentry_support, .init = ubase_dbg_seq_file_init, .read_func = unic_dbg_dump_page_pool_info, + }, { + .name = "rss_cfg_hw", + .dentry_index = UNIC_DBG_DENTRY_ROOT, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_rss_cfg_hw, }, { .name = "promisc_cfg_hw", .dentry_index = UNIC_DBG_DENTRY_ROOT, diff --git a/drivers/net/ub/unic/unic_dev.c b/drivers/net/ub/unic/unic_dev.c index 11c48c9e4709..c4efbd7ecd5f 100644 --- a/drivers/net/ub/unic/unic_dev.c +++ b/drivers/net/ub/unic/unic_dev.c @@ -406,6 +406,11 @@ static void unic_destroy_jetty(struct unic_dev *unic_dev, u32 num) unic_destroy_rx(unic_dev, num); } +static inline int unic_init_rss(struct unic_dev *unic_dev) +{ + return unic_set_rss_tc_mode(unic_dev, UNIC_RSS_TC_VALID); +} + static int __unic_init_channels(struct unic_dev *unic_dev, u32 channels_num) { struct auxiliary_device *adev = unic_dev->comdev.adev; @@ -429,8 +434,20 @@ static int __unic_init_channels(struct unic_dev *unic_dev, u32 channels_num) netif_napi_add(unic_dev->comdev.netdev, &channels->c[i].napi, unic_napi_poll); + ret = unic_init_rss(unic_dev); + if (ret) { + dev_err(adev->dev.parent, + "failed to init rss, ret = %d.\n", ret); + goto err_init_rss; + } + return 0; +err_init_rss: + for (; i > 0; i--) + netif_napi_del(&channels->c[i - 1].napi); + unic_destroy_jetty(unic_dev, channels->num); + err_init_jetty: kfree(channels->c); channels->c = NULL; @@ -453,11 +470,18 @@ int unic_init_channels(struct unic_dev *unic_dev, u32 channels_num) return ret; } +static inline void unic_uninit_rss(struct unic_dev *unic_dev) +{ + unic_set_rss_tc_mode(unic_dev, UNIC_RSS_TC_INVALID); +} + static void __unic_uninit_channels(struct unic_dev *unic_dev) { struct unic_channels *channels = &unic_dev->channels; u32 i; + unic_uninit_rss(unic_dev); + if (!channels->c) return; diff --git a/drivers/net/ub/unic/unic_dev.h b/drivers/net/ub/unic/unic_dev.h index 869b0f8bf749..fa630ea950e7 100644 --- a/drivers/net/ub/unic/unic_dev.h +++ b/drivers/net/ub/unic/unic_dev.h @@ -269,6 +269,11 @@ static inline bool unic_dev_fec_supported(struct unic_dev *unic_dev) return unic_get_cap_bit(unic_dev, UNIC_SUPPORT_FEC_B); } +static inline bool unic_dev_rss_supported(struct unic_dev *unic_dev) +{ + return unic_get_cap_bit(unic_dev, UNIC_SUPPORT_RSS_B); +} + static inline bool unic_dev_tc_speed_limit_supported(struct unic_dev *unic_dev) { return unic_get_cap_bit(unic_dev, UNIC_SUPPORT_TC_SPEED_LIMIT_B); diff --git a/drivers/net/ub/unic/unic_hw.c b/drivers/net/ub/unic/unic_hw.c index 381482346020..d2b7026514c4 100644 --- a/drivers/net/ub/unic/unic_hw.c +++ b/drivers/net/ub/unic/unic_hw.c @@ -758,3 +758,101 @@ int unic_update_fec_stats(struct unic_dev *unic_dev) clear_bit(UNIC_STATE_FEC_STATS_UPDATING, &unic_dev->state); return ret; } + +static void unic_set_rss_tc0_param(struct unic_channels *channels, + u16 jfr_cnt, __le16 *jfr_idx) +{ + struct unic_vl *vl = &channels->vl; + u16 jfr_index; + u16 i; + + if (!vl->queue_count[0]) + return; + + for (i = 0; i < jfr_cnt; i++) { + jfr_index = vl->queue_offset[0] + + i % vl->queue_count[0]; + jfr_idx[i] = cpu_to_le16(jfr_index); + } +} + +static void unic_set_rss_multi_tc_param(struct auxiliary_device *adev, + struct unic_channels *channels, + u16 jfr_cnt, __le16 *jfr_idx) +{ + struct ubase_adev_caps *unic_caps = ubase_get_unic_caps(adev); + struct unic_vl *vl = &channels->vl; + u16 jfr_index; + u16 queue_num; + u16 j, k = 0; + u16 i; + + queue_num = (u16)unic_caps->jfr.max_cnt / channels->rss_vl_num; + for (i = 0; i < channels->rss_vl_num; i++) { + if (!vl->queue_count[i]) + return; + for (j = 0; j < queue_num && k < jfr_cnt; j++, k++) { + jfr_index = vl->queue_offset[i] + + j % vl->queue_count[i]; + jfr_idx[k] = cpu_to_le16(jfr_index); + } + } +} + +int unic_set_rss_tc_mode(struct unic_dev *unic_dev, u8 tc_vaild) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct unic_channels *channels = &unic_dev->channels; + enum ubase_reset_stage reset_stage; + struct ubase_adev_caps *unic_caps; + struct unic_cfg_rss_cmd req = {0}; + struct ubase_cmd_buf in; + int ret; + + reset_stage = ubase_get_reset_stage(adev); + if (reset_stage == UBASE_RESET_STAGE_UNINIT) + return -EBUSY; + + unic_caps = ubase_get_unic_caps(adev); + + req.tc_vaild = tc_vaild; + req.tc_mode = channels->rss_vl_num <= 1 ? UNIC_RSS_TC_MODE0 : + UNIC_RSS_TC_MODE1; + req.jfr_reg_num = min(unic_caps->jfr.max_cnt, UNIC_RSS_MAX_CNT); + if (req.tc_vaild) { + if (req.tc_mode == UNIC_RSS_TC_MODE0) + unic_set_rss_tc0_param(channels, req.jfr_reg_num, + req.jfr_idx); + else + unic_set_rss_multi_tc_param(adev, channels, + req.jfr_reg_num, + req.jfr_idx); + } + + ubase_fill_inout_buf(&in, UBASE_OPC_TP_RSS_CONFIG, false, sizeof(req), + &req); + ret = ubase_cmd_send_in(adev, &in); + if (ret) + dev_err(adev->dev.parent, "failed to set rss tc mode. ret = %d.\n", + ret); + + return ret; +} + +int unic_query_rss_cfg(struct unic_dev *unic_dev, struct unic_cfg_rss_cmd *resp) +{ + struct unic_cfg_rss_cmd req = {0}; + struct ubase_cmd_buf in, out; + int ret; + + ubase_fill_inout_buf(&in, UBASE_OPC_TP_RSS_CONFIG, true, sizeof(req), + &req); + ubase_fill_inout_buf(&out, UBASE_OPC_TP_RSS_CONFIG, true, sizeof(*resp), + resp); + ret = ubase_cmd_send_inout(unic_dev->comdev.adev, &in, &out); + if (ret) + unic_err(unic_dev, + "failed to query rss cfg hw, ret = %d.\n", ret); + + return ret; +} diff --git a/drivers/net/ub/unic/unic_hw.h b/drivers/net/ub/unic/unic_hw.h index 04f6d22e8a0c..ebe0c714549d 100644 --- a/drivers/net/ub/unic/unic_hw.h +++ b/drivers/net/ub/unic/unic_hw.h @@ -22,6 +22,16 @@ #define UNIC_LINK_STATUS_DOWN 0 #define UNIC_LINK_STATUS_UP 1 +enum unic_rss_tc_mode { + UNIC_RSS_TC_MODE0, + UNIC_RSS_TC_MODE1 +}; + +enum unic_rss_tc_valid { + UNIC_RSS_TC_INVALID, + UNIC_RSS_TC_VALID +}; + struct unic_caps_item { void *p; u32 default_val; @@ -46,6 +56,15 @@ struct unic_promisc_en { u8 en_mc; }; +#define UNIC_RSS_MAX_CNT 10U +struct unic_cfg_rss_cmd { + u8 tc_mode; + u8 tc_vaild; + u8 jfr_reg_num; + u8 rsvd; + __le16 jfr_idx[UNIC_RSS_MAX_CNT]; +}; + static inline bool unic_is_port_down(struct unic_dev *unic_dev) { return unic_dev->hw.mac.link_status == UNIC_LINK_STATUS_DOWN; @@ -77,5 +96,8 @@ int unic_set_promisc_mode(struct unic_dev *unic_dev, int unic_cfg_vport_buf(struct unic_dev *unic_dev, bool init); int unic_set_fec_mode(struct unic_dev *unic_dev, u32 fec_mode); int unic_update_fec_stats(struct unic_dev *unic_dev); +int unic_set_rss_tc_mode(struct unic_dev *unic_dev, u8 tc_vaild); +int unic_query_rss_cfg(struct unic_dev *unic_dev, + struct unic_cfg_rss_cmd *resp); #endif diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index b41a1e05747e..7e78860d7778 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -49,6 +49,7 @@ enum ubase_opcode_type { /* TP commands */ UBASE_OPC_TP_TIMER_VA_CONFIG = 0x3007, UBASE_OPC_TP_EXTDB_VA_CONFIG = 0x3008, + UBASE_OPC_TP_RSS_CONFIG = 0x300B, UBASE_OPC_QUERY_CTP_VL_OFFSET = 0x3112, /* TA commands */ -- Gitee From 1d2b60a60e0c1a4b95cfe374038e7b23e52bcdfb Mon Sep 17 00:00:00 2001 From: Junxin Chen Date: Thu, 21 Aug 2025 19:58:52 +0800 Subject: [PATCH 182/214] net: unic: support querying and configuring the number of queues. ANBZ: #45460 commit b5c532e88d833088502b5d693313ec636a7ac9ba openEuler. To adapt to the ub protocol, This patch adds the capability of querying and configuring the number of queues for UNIC driver. Users can use the ethtool to query or configure the number of queues. Signed-off-by: Zihao Sheng Signed-off-by: Haibin Lu Signed-off-by: Junxin Chen Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/Makefile | 2 +- drivers/net/ub/unic/unic_channel.c | 100 +++++++++++++++++++++++++++++ drivers/net/ub/unic/unic_channel.h | 16 +++++ drivers/net/ub/unic/unic_dev.c | 27 ++++++++ drivers/net/ub/unic/unic_dev.h | 3 + drivers/net/ub/unic/unic_ethtool.c | 3 + 6 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ub/unic/unic_channel.c create mode 100644 drivers/net/ub/unic/unic_channel.h diff --git a/drivers/net/ub/unic/Makefile b/drivers/net/ub/unic/Makefile index 37b2ccd7753f..1419ee569595 100644 --- a/drivers/net/ub/unic/Makefile +++ b/drivers/net/ub/unic/Makefile @@ -8,5 +8,5 @@ ccflags-y += -I$(srctree)/drivers/net/ub/unic/debugfs obj-$(CONFIG_UB_UNIC) += unic.o unic-objs = unic_main.o unic_ethtool.o unic_hw.o unic_guid.o unic_netdev.o unic_dev.o unic_qos_hw.o unic_event.o unic_crq.o -unic-objs += debugfs/unic_debugfs.o unic_rx.o unic_tx.o unic_txrx.o unic_comm_addr.o unic_rack_ip.o unic_stats.o +unic-objs += unic_channel.o debugfs/unic_debugfs.o unic_rx.o unic_tx.o unic_txrx.o unic_comm_addr.o unic_rack_ip.o unic_stats.o unic-$(CONFIG_UB_UNIC_DCB) += unic_dcbnl.o diff --git a/drivers/net/ub/unic/unic_channel.c b/drivers/net/ub/unic/unic_channel.c new file mode 100644 index 000000000000..ce0d33e6c2c1 --- /dev/null +++ b/drivers/net/ub/unic/unic_channel.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include "unic_channel.h" +#include "unic_dev.h" +#include "unic_netdev.h" + +void unic_get_channels(struct net_device *ndev, + struct ethtool_channels *ch) +{ + struct unic_dev *unic_dev = netdev_priv(ndev); + + ch->max_combined = unic_channels_max_num(unic_dev->comdev.adev); + ch->combined_count = unic_dev->channels.rss_size; +} + +static int unic_check_rss_size_param(struct unic_dev *unic_dev, u32 new_rss_size, + u32 org_rss_size) +{ + u32 max_rss_size; + + if (org_rss_size == new_rss_size) { + unic_err(unic_dev, + "old num and new num are the same, rss_size = %u.\n", + org_rss_size); + return -EINVAL; + } + + max_rss_size = unic_get_max_rss_size(unic_dev); + if (new_rss_size < 1 || new_rss_size > max_rss_size) { + unic_err(unic_dev, + "the rss_size(%u) is out of the range [1, %u].\n", + new_rss_size, max_rss_size); + return -EINVAL; + } + + if (max_rss_size % new_rss_size) { + unic_err(unic_dev, + "the rss_size(%u) can't distributed to max_rss_size(%u).\n", + new_rss_size, max_rss_size); + return -EINVAL; + } + + return 0; +} + +static int unic_check_set_channels_available(struct net_device *ndev) +{ + struct unic_dev *unic_dev = netdev_priv(ndev); + + if (netif_running(ndev)) { + unic_err(unic_dev, "failed to set channels, due to network interface is up, please down it first and try again.\n"); + return -EBUSY; + } + + if (unic_resetting(ndev)) { + unic_err(unic_dev, + "failed to set channels, due to dev resetting.\n"); + return -EBUSY; + } + + return 0; +} + +int unic_set_channels(struct net_device *ndev, + struct ethtool_channels *ch) +{ + struct unic_dev *unic_dev = netdev_priv(ndev); + u32 org_rss_size = unic_dev->channels.rss_size; + u32 new_rss_size = ch->combined_count; + int ret, ret1; + + ret = unic_check_set_channels_available(ndev); + if (ret) + return ret; + + ret = unic_check_rss_size_param(unic_dev, new_rss_size, org_rss_size); + if (ret) + return ret; + + ret = unic_change_rss_size(unic_dev, new_rss_size, org_rss_size); + if (ret) { + unic_err(unic_dev, + "failed to change rss_size, revert to old rss_size, ret = %d.\n", + ret); + + ret1 = unic_change_rss_size(unic_dev, org_rss_size, org_rss_size); + if (ret1) { + unic_err(unic_dev, + "failed to revert to old rss_size, ret1 = %d.\n", + ret1); + return ret1; + } + } + + return ret; +} diff --git a/drivers/net/ub/unic/unic_channel.h b/drivers/net/ub/unic/unic_channel.h new file mode 100644 index 000000000000..b86af4a15417 --- /dev/null +++ b/drivers/net/ub/unic/unic_channel.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_CHANNEL_H__ +#define __UNIC_CHANNEL_H__ + +#include +#include + +int unic_set_channels(struct net_device *ndev, struct ethtool_channels *ch); +void unic_get_channels(struct net_device *ndev, struct ethtool_channels *ch); + +#endif diff --git a/drivers/net/ub/unic/unic_dev.c b/drivers/net/ub/unic/unic_dev.c index c4efbd7ecd5f..a0ea8edee2ef 100644 --- a/drivers/net/ub/unic/unic_dev.c +++ b/drivers/net/ub/unic/unic_dev.c @@ -869,6 +869,33 @@ void unic_remove_period_task(struct unic_dev *unic_dev) cancel_delayed_work_sync(&unic_dev->service_task); } +int unic_change_rss_size(struct unic_dev *unic_dev, u32 new_rss_size, + u32 org_rss_size) +{ + struct unic_channels *channels = &unic_dev->channels; + int ret; + + dev_info(unic_dev->comdev.adev->dev.parent, + "change rss_size from %u to %u.\n", org_rss_size, new_rss_size); + + mutex_lock(&channels->mutex); + __unic_uninit_channels(unic_dev); + + channels->rss_size = new_rss_size; + channels->num = channels->rss_size * channels->rss_vl_num; + + unic_update_queue_info(unic_dev); + + ret = __unic_init_channels(unic_dev, channels->num); + if (ret) + dev_err(unic_dev->comdev.adev->dev.parent, + "failed to change rss_size, ret = %d.\n", ret); + + mutex_unlock(&channels->mutex); + + return ret; +} + static struct net_device *unic_alloc_netdev(struct auxiliary_device *adev) { struct ubase_caps *caps = ubase_get_dev_caps(adev); diff --git a/drivers/net/ub/unic/unic_dev.h b/drivers/net/ub/unic/unic_dev.h index fa630ea950e7..f0df795ad011 100644 --- a/drivers/net/ub/unic/unic_dev.h +++ b/drivers/net/ub/unic/unic_dev.h @@ -240,6 +240,7 @@ int unic_dev_init(struct auxiliary_device *adev); void unic_dev_uninit(struct auxiliary_device *adev); int unic_set_mtu(struct unic_dev *unic_dev, int new_mtu); u32 unic_channels_max_num(struct auxiliary_device *adev); +u32 unic_get_max_rss_size(struct unic_dev *unic_dev); int unic_init_channels(struct unic_dev *unic_dev, u32 channels_num); void unic_uninit_channels(struct unic_dev *unic_dev); void unic_start_period_task(struct net_device *netdev); @@ -250,6 +251,8 @@ int unic_init_rx(struct unic_dev *unic_dev, u32 num); int unic_init_tx(struct unic_dev *unic_dev, u32 num); void unic_destroy_rx(struct unic_dev *unic_dev, u32 num); void unic_destroy_tx(struct unic_dev *unic_dev, u32 num); +int unic_change_rss_size(struct unic_dev *unic_dev, u32 new_rss_size, + u32 org_rss_size); int unic_set_vl_map(struct unic_dev *unic_dev, u8 *dscp_prio, u8 *prio_vl, u8 map_type); int unic_dbg_log(void); diff --git a/drivers/net/ub/unic/unic_ethtool.c b/drivers/net/ub/unic/unic_ethtool.c index a789108ebeb5..bdaeca41e056 100644 --- a/drivers/net/ub/unic/unic_ethtool.c +++ b/drivers/net/ub/unic/unic_ethtool.c @@ -12,6 +12,7 @@ #include "unic_hw.h" #include "unic_netdev.h" #include "unic_stats.h" +#include "unic_channel.h" #include "unic_ethtool.h" static u32 unic_get_link_status(struct net_device *netdev) @@ -121,6 +122,8 @@ static const struct ethtool_ops unic_ethtool_ops = { .get_link = unic_get_link_status, .get_link_ksettings = unic_get_link_ksettings, .get_drvinfo = unic_get_driver_info, + .get_channels = unic_get_channels, + .set_channels = unic_set_channels, .get_fecparam = unic_get_fecparam, .set_fecparam = unic_set_fecparam, .get_fec_stats = unic_get_fec_stats, -- Gitee From 771722a1ff53f7afc305693c1fe2e93888e58023 Mon Sep 17 00:00:00 2001 From: Junxin Chen Date: Thu, 21 Aug 2025 21:29:06 +0800 Subject: [PATCH 183/214] net: unic: support querying and configuring queue parameters. ANBZ: #45460 commit f42394db4fedb5dc3a995623c69ff7e32bcf6f9d openEuler. To adapt to the ub protocol, This patch adds the capability of querying and configuring queue parameters for UNIC driver. Users can use the ethtool to query or configure queue parameters. Signed-off-by: Zihao Sheng Signed-off-by: Haibin Lu Signed-off-by: Junxin Chen Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/unic_channel.c | 304 +++++++++++++++++++++++++++++ drivers/net/ub/unic/unic_channel.h | 14 ++ drivers/net/ub/unic/unic_ethtool.c | 2 + drivers/net/ub/unic/unic_ethtool.h | 2 + 4 files changed, 322 insertions(+) diff --git a/drivers/net/ub/unic/unic_channel.c b/drivers/net/ub/unic/unic_channel.c index ce0d33e6c2c1..eb2d838035c7 100644 --- a/drivers/net/ub/unic/unic_channel.c +++ b/drivers/net/ub/unic/unic_channel.c @@ -98,3 +98,307 @@ int unic_set_channels(struct net_device *ndev, return ret; } + +static bool unic_is_channels_param_changed(struct unic_dev *unic_dev, + struct ethtool_ringparam *param, + struct kernel_ethtool_ringparam *kernel_param, + struct unic_channel_param *channels_param) +{ + channels_param->sqebb_depth = roundup_pow_of_two(param->tx_pending); + channels_param->rqe_depth = roundup_pow_of_two(param->rx_pending); + channels_param->rx_buff_len = kernel_param->rx_buf_len; + + if (unic_dev->channels.sqebb_depth != channels_param->sqebb_depth) + set_bit(UNIC_TX_CHANGED, &unic_dev->channels.state); + + if (unic_dev->channels.rx_buff_len != channels_param->rx_buff_len || + unic_dev->channels.rqe_depth != channels_param->rqe_depth) + set_bit(UNIC_RX_CHANGED, &unic_dev->channels.state); + + return unic_tx_changed(unic_dev) || unic_rx_changed(unic_dev); +} + +static int unic_check_channels_param(struct unic_dev *unic_dev, + struct ethtool_ringparam *param, + struct kernel_ethtool_ringparam *kernel_param) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct ubase_adev_caps *unic_caps = ubase_get_unic_caps(adev); + struct net_device *netdev = unic_dev->comdev.netdev; + u32 i; + + if (netif_running(netdev)) { + unic_err(unic_dev, + "failed to set channels param, due to netdev is up, down it first and try again.\n"); + return -EBUSY; + } + + if (__unic_resetting(unic_dev) || !unic_dev->channels.c) { + unic_err(unic_dev, + "failed to check param value, due to dev resetting or uninited\n"); + return -EBUSY; + } + + for (i = 0; i < unic_dev->channels.num; i++) { + if (!unic_dev->channels.c[i].sq || + !unic_dev->channels.c[i].rq) { + unic_err(unic_dev, "channel(%u) sq or rq is null.\n", i); + return -ENXIO; + } + } + + if (param->rx_mini_pending || param->rx_jumbo_pending) + return -EINVAL; + + if (param->tx_pending > unic_caps->jfs.depth || + param->tx_pending < UNIC_TXRX_MIN_DEPTH || + param->rx_pending > unic_caps->jfr.depth || + param->rx_pending < UNIC_TXRX_MIN_DEPTH) { + unic_err(unic_dev, "queue depth out of range [%d, %u]\n", + UNIC_TXRX_MIN_DEPTH, unic_caps->jfs.depth); + return -EINVAL; + } + + if (kernel_param->rx_buf_len != UNIC_RX_BUF_LEN_2K && + kernel_param->rx_buf_len != UNIC_RX_BUF_LEN_4K) { + unic_err(unic_dev, "rx buf len only support %d and %d.\n", + UNIC_RX_BUF_LEN_2K, UNIC_RX_BUF_LEN_4K); + return -EINVAL; + } + + return 0; +} + +static void unic_dev_change_channels_param(struct unic_dev *unic_dev, + struct unic_channel_param param) +{ + struct unic_channels *channels = &unic_dev->channels; + + channels->sqebb_depth = param.sqebb_depth; + channels->rqe_depth = param.rqe_depth; + channels->sq_cqe_depth = param.sqebb_depth; + channels->rq_cqe_depth = param.rqe_depth; + + channels->rx_buff_len = param.rx_buff_len; + channels->sqebb_shift = ilog2(roundup_pow_of_two(channels->sqebb_depth)); + channels->sq_jfc_shift = ilog2(roundup_pow_of_two(channels->sq_cqe_depth)); + channels->rq_jfc_shift = ilog2(roundup_pow_of_two(channels->rq_cqe_depth)); +} + +static void unic_clear_txrx_channels_state(struct unic_dev *unic_dev) +{ + clear_bit(UNIC_TX_CHANGED, &unic_dev->channels.state); + clear_bit(UNIC_RX_CHANGED, &unic_dev->channels.state); +} + +static void unic_uninit_changed_channels(struct unic_dev *unic_dev) +{ + struct unic_channels *channels = &unic_dev->channels; + + if (unic_tx_changed(unic_dev)) + unic_destroy_tx(unic_dev, channels->num); + + if (unic_rx_changed(unic_dev)) + unic_destroy_rx(unic_dev, channels->num); +} + +static int unic_init_changed_channels(struct unic_dev *unic_dev) +{ + struct unic_channels *channels = &unic_dev->channels; + int ret = 0; + + if (unic_tx_changed(unic_dev)) { + ret = unic_init_tx(unic_dev, channels->num); + if (ret) + goto out; + } + + if (unic_rx_changed(unic_dev)) { + ret = unic_init_rx(unic_dev, channels->num); + if (ret) + goto err_init_rx_channels; + } + + goto out; + +err_init_rx_channels: + if (unic_tx_changed(unic_dev)) + unic_destroy_tx(unic_dev, channels->num); +out: + return ret; +} + +static int unic_modify_channels(struct unic_dev *unic_dev, + struct unic_channel_param new_param) +{ + struct unic_channels *channels = &unic_dev->channels; + int ret; + + mutex_lock(&channels->mutex); + unic_uninit_changed_channels(unic_dev); + + unic_dev_change_channels_param(unic_dev, new_param); + + ret = unic_init_changed_channels(unic_dev); + + mutex_unlock(&channels->mutex); + return ret; +} + +static void unic_backup_channels_param(struct unic_dev *unic_dev, + struct unic_channel_param *channels_param) +{ + channels_param->sqebb_depth = unic_dev->channels.sqebb_depth; + channels_param->rqe_depth = unic_dev->channels.rqe_depth; + channels_param->rx_buff_len = unic_dev->channels.rx_buff_len; +} + +static int unic_alloc_txrx_stats(struct unic_dev *unic_dev, + struct unic_sq_stats **sq_stats, + struct unic_rq_stats **rq_stats) +{ + if (unic_tx_changed(unic_dev)) { + *sq_stats = kcalloc(unic_dev->channels.num, sizeof(**sq_stats), + GFP_KERNEL); + if (ZERO_OR_NULL_PTR(*sq_stats)) + return -ENOMEM; + } + + if (unic_rx_changed(unic_dev)) { + *rq_stats = kcalloc(unic_dev->channels.num, sizeof(**rq_stats), + GFP_KERNEL); + if (ZERO_OR_NULL_PTR(*rq_stats)) { + if (unic_tx_changed(unic_dev)) + kfree(*sq_stats); + return -ENOMEM; + } + } + + return 0; +} + +static void unic_free_txrx_stats(struct unic_dev *unic_dev, + struct unic_sq_stats *sq_stats, + struct unic_rq_stats *rq_stats) +{ + if (unic_tx_changed(unic_dev)) + kfree(sq_stats); + + if (unic_rx_changed(unic_dev)) + kfree(rq_stats); +} + +static void unic_backup_sqrq_stats(struct unic_dev *unic_dev, + struct unic_sq_stats *sq_stats, + struct unic_rq_stats *rq_stats) +{ + u32 i; + + for (i = 0; i < unic_dev->channels.num; i++) { + if (unic_tx_changed(unic_dev)) + memcpy(&sq_stats[i], &unic_dev->channels.c[i].sq->stats, + sizeof(struct unic_sq_stats)); + if (unic_rx_changed(unic_dev)) + memcpy(&rq_stats[i], &unic_dev->channels.c[i].rq->stats, + sizeof(struct unic_rq_stats)); + } +} + +static void unic_backup_param_and_stats(struct unic_dev *unic_dev, + struct unic_channel_param *channels_param, + struct unic_sq_stats *sq_stats, + struct unic_rq_stats *rq_stats) +{ + unic_backup_channels_param(unic_dev, channels_param); + unic_backup_sqrq_stats(unic_dev, sq_stats, rq_stats); +} + +static void unic_restore_sqrq_stats(struct unic_dev *unic_dev, + struct unic_sq_stats *sq_stats, + struct unic_rq_stats *rq_stats) +{ + u32 i; + + for (i = 0; i < unic_dev->channels.num; i++) { + if (unic_rx_changed(unic_dev)) + memcpy(&unic_dev->channels.c[i].rq->stats, &rq_stats[i], + sizeof(struct unic_rq_stats)); + if (unic_tx_changed(unic_dev)) + memcpy(&unic_dev->channels.c[i].sq->stats, &sq_stats[i], + sizeof(struct unic_sq_stats)); + } +} + +int unic_set_channels_param(struct net_device *ndev, + struct ethtool_ringparam *param, + struct kernel_ethtool_ringparam *kernel_param, + struct netlink_ext_ack *extack) +{ + struct unic_channel_param new_param, org_param; + struct unic_dev *unic_dev = netdev_priv(ndev); + struct unic_sq_stats *sq_stats; + struct unic_rq_stats *rq_stats; + int ret, ret1; + + ret = unic_check_channels_param(unic_dev, param, kernel_param); + if (ret) + return ret; + + if (!unic_is_channels_param_changed(unic_dev, param, kernel_param, &new_param)) + return 0; + + ret = unic_alloc_txrx_stats(unic_dev, &sq_stats, &rq_stats); + if (ret) { + unic_err(unic_dev, "failed to alloc txrx stats, ret = %d.\n", ret); + goto out; + } + + unic_backup_param_and_stats(unic_dev, &org_param, sq_stats, rq_stats); + + unic_info(unic_dev, + "changing tx/rx depth from %u/%u to %u/%u, changing rx buffer len from %u to %u.\n", + unic_dev->channels.sqebb_depth, unic_dev->channels.rqe_depth, + new_param.sqebb_depth, new_param.rqe_depth, + unic_dev->channels.rx_buff_len, new_param.rx_buff_len); + + ret = unic_modify_channels(unic_dev, new_param); + if (ret) { + unic_err(unic_dev, + "failed to modify channels, revert to old value, ret = %d.\n", + ret); + ret1 = unic_modify_channels(unic_dev, org_param); + if (ret1) { + unic_err(unic_dev, + "failed to revert to old value, ret1 = %d.\n", + ret1); + unic_free_txrx_stats(unic_dev, sq_stats, rq_stats); + unic_clear_txrx_channels_state(unic_dev); + return ret1; + } + } + + unic_restore_sqrq_stats(unic_dev, sq_stats, rq_stats); + unic_free_txrx_stats(unic_dev, sq_stats, rq_stats); + +out: + unic_clear_txrx_channels_state(unic_dev); + + return ret; +} + +void unic_get_channels_param(struct net_device *ndev, + struct ethtool_ringparam *param, + struct kernel_ethtool_ringparam *kernel_param, + struct netlink_ext_ack *extack) +{ + struct unic_dev *unic_dev = netdev_priv(ndev); + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct ubase_adev_caps *unic_caps = ubase_get_unic_caps(adev); + + param->tx_max_pending = unic_caps->jfs.depth; + param->rx_max_pending = unic_caps->jfr.depth; + + param->tx_pending = unic_dev->channels.sqebb_depth; + param->rx_pending = unic_dev->channels.rqe_depth; + kernel_param->rx_buf_len = unic_dev->channels.rx_buff_len; +} diff --git a/drivers/net/ub/unic/unic_channel.h b/drivers/net/ub/unic/unic_channel.h index b86af4a15417..54889c6f725d 100644 --- a/drivers/net/ub/unic/unic_channel.h +++ b/drivers/net/ub/unic/unic_channel.h @@ -10,7 +10,21 @@ #include #include +struct unic_channel_param { + u32 sqebb_depth; + u32 rqe_depth; + u32 rx_buff_len; +}; + int unic_set_channels(struct net_device *ndev, struct ethtool_channels *ch); void unic_get_channels(struct net_device *ndev, struct ethtool_channels *ch); +int unic_set_channels_param(struct net_device *ndev, + struct ethtool_ringparam *param, + struct kernel_ethtool_ringparam *kernel_param, + struct netlink_ext_ack *extack); +void unic_get_channels_param(struct net_device *ndev, + struct ethtool_ringparam *param, + struct kernel_ethtool_ringparam *kernel_param, + struct netlink_ext_ack *extack); #endif diff --git a/drivers/net/ub/unic/unic_ethtool.c b/drivers/net/ub/unic/unic_ethtool.c index bdaeca41e056..9e50f0332d4e 100644 --- a/drivers/net/ub/unic/unic_ethtool.c +++ b/drivers/net/ub/unic/unic_ethtool.c @@ -124,6 +124,8 @@ static const struct ethtool_ops unic_ethtool_ops = { .get_drvinfo = unic_get_driver_info, .get_channels = unic_get_channels, .set_channels = unic_set_channels, + .get_ringparam = unic_get_channels_param, + .set_ringparam = unic_set_channels_param, .get_fecparam = unic_get_fecparam, .set_fecparam = unic_set_fecparam, .get_fec_stats = unic_get_fec_stats, diff --git a/drivers/net/ub/unic/unic_ethtool.h b/drivers/net/ub/unic/unic_ethtool.h index a440536e011f..4ba10f0b54b2 100644 --- a/drivers/net/ub/unic/unic_ethtool.h +++ b/drivers/net/ub/unic/unic_ethtool.h @@ -10,6 +10,8 @@ #include #include +#define UNIC_TXRX_MIN_DEPTH 64 + struct unic_reset_type_map { enum ethtool_reset_flags reset_flags; enum ubase_reset_type reset_type; -- Gitee From b89bbb200ba50636325bcfa3d0f67bc9e2712ab1 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Wed, 29 Oct 2025 16:39:38 +0800 Subject: [PATCH 184/214] ub: ubase: support debugfs for active dev stats. ANBZ: #45460 commit 6b3b919054909ca3d7bdfb1c9c2fe48c098ff1cb openEuler. This commit is used to support debugfs for active dev stats. Current commit uses debugfs to collect statistics on the number of times that the rx stream stop and resume interfaces are invoked. In addition, the latest 10 statistics can be queried. Signed-off-by: Xiaobo Zhang Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/debugfs/ubase_debugfs.c | 57 ++++++++++++++++++++++++ drivers/ub/ubase/ubase_dev.c | 10 ++++- drivers/ub/ubase/ubase_stats.c | 22 +++++++++ drivers/ub/ubase/ubase_stats.h | 3 ++ 4 files changed, 90 insertions(+), 2 deletions(-) diff --git a/drivers/ub/ubase/debugfs/ubase_debugfs.c b/drivers/ub/ubase/debugfs/ubase_debugfs.c index d7495d8b4ef7..ef0b162e1188 100644 --- a/drivers/ub/ubase/debugfs/ubase_debugfs.c +++ b/drivers/ub/ubase/debugfs/ubase_debugfs.c @@ -30,6 +30,55 @@ static int ubase_dbg_dump_rst_info(struct seq_file *s, void *data) return 0; } +static int ubase_dbg_dump_activate_record(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + struct ubase_activate_dev_stats *record; + u8 cnt = 1, stats_cnt; + u64 total, idx; + + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits) || + test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + return -EBUSY; + + record = &udev->stats.activate_record; + + mutex_lock(&record->lock); + + seq_puts(s, "current time : "); + ubase_dbg_format_time(ktime_get_real_seconds(), s); + seq_puts(s, "\n"); + seq_printf(s, "activate dev count : %llu\n", record->act_cnt); + seq_printf(s, "deactivate dev count : %llu\n", record->deact_cnt); + + total = record->act_cnt + record->deact_cnt; + if (!total) { + seq_puts(s, "activate dev change records : NA\n"); + mutex_unlock(&record->lock); + return 0; + } + + seq_puts(s, "activate dev change records :\n"); + seq_puts(s, "\tNo.\tTIME\t\t\t\tSTATUS\t\tRESULT\n"); + + stats_cnt = min(total, UBASE_ACT_STAT_MAX_NUM); + while (cnt <= stats_cnt) { + total--; + idx = total % UBASE_ACT_STAT_MAX_NUM; + seq_printf(s, "\t%-2d\t", cnt); + ubase_dbg_format_time(record->stats[idx].time, s); + seq_printf(s, "\t%s", record->stats[idx].activate ? + "activate" : "deactivate"); + seq_printf(s, "\t%d", record->stats[idx].result); + seq_puts(s, "\n"); + cnt++; + } + + mutex_unlock(&record->lock); + + return 0; +} + static void ubase_dbg_fill_single_port(struct seq_file *s, struct ubase_perf_stats_result *stats) { @@ -193,6 +242,14 @@ static struct ubase_dbg_cmd_info ubase_dbg_cmd[] = { .init = __ubase_dbg_seq_file_init, .read_func = ubase_dbg_dump_rst_info, }, + { + .name = "activate_record", + .dentry_index = UBASE_DBG_DENTRY_ROOT, + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_activate_record, + }, { .name = "sl_vl_map", .dentry_index = UBASE_DBG_DENTRY_QOS, diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index abdb3232edea..3921cd0ff824 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -16,6 +16,7 @@ #include "ubase_mailbox.h" #include "ubase_pmem.h" #include "ubase_reset.h" +#include "ubase_stats.h" #include "ubase_dev.h" #define UBASE_PERIOD_100MS 100 @@ -1317,12 +1318,15 @@ int ubase_activate_dev(struct auxiliary_device *adev) if (ret) { ubase_err(udev, "failed to activate ubase dev, ret = %d.\n", ret); - return ret; + goto activate_dev_err; } ubase_activate_notify(udev, adev, true); - return 0; +activate_dev_err: + ubase_update_activate_stats(udev, true, ret); + + return ret; } EXPORT_SYMBOL(ubase_activate_dev); @@ -1352,6 +1356,8 @@ int ubase_deactivate_dev(struct auxiliary_device *adev) ubase_activate_notify(udev, adev, true); } + ubase_update_activate_stats(udev, false, ret); + return ret; } EXPORT_SYMBOL(ubase_deactivate_dev); diff --git a/drivers/ub/ubase/ubase_stats.c b/drivers/ub/ubase/ubase_stats.c index b30e839ebb0b..7f536e0cd537 100644 --- a/drivers/ub/ubase/ubase_stats.c +++ b/drivers/ub/ubase/ubase_stats.c @@ -65,3 +65,25 @@ int ubase_get_ub_port_stats(struct auxiliary_device *adev, u16 port_id, sizeof(*data) / sizeof(u64), false); } EXPORT_SYMBOL(ubase_get_ub_port_stats); + +void ubase_update_activate_stats(struct ubase_dev *udev, bool activate, + int result) +{ + struct ubase_activate_dev_stats *record = &udev->stats.activate_record; + u64 idx, total; + + mutex_lock(&record->lock); + + if (activate) + record->act_cnt++; + else + record->deact_cnt++; + + total = record->act_cnt + record->deact_cnt; + idx = (total - 1) % UBASE_ACT_STAT_MAX_NUM; + record->stats[idx].activate = activate; + record->stats[idx].time = ktime_get_real_seconds(); + record->stats[idx].result = result; + + mutex_unlock(&record->lock); +} diff --git a/drivers/ub/ubase/ubase_stats.h b/drivers/ub/ubase/ubase_stats.h index b3f6e5d788cc..a6826dd461c7 100644 --- a/drivers/ub/ubase/ubase_stats.h +++ b/drivers/ub/ubase/ubase_stats.h @@ -15,4 +15,7 @@ struct ubase_query_mac_stats_cmd { __le64 stats_val[]; }; +void ubase_update_activate_stats(struct ubase_dev *udev, bool activate, + int result); + #endif /* _UBASE_STATS_H */ -- Gitee From 0c321a0c547a4e3a1c788953d775b9496f045cbc Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Tue, 23 Sep 2025 15:18:35 +0800 Subject: [PATCH 185/214] ub: ubase: Added debug information query function for FST/FVT/RQMT entries ANBZ: #45460 commit 0bbe314ab1feb4918f0643fd752f319fb2cfe310 openEuler. This patch enhances the debugfs of the ubase driver by adding support for querying information on FST (Flow Steering Table), FVT (Flow Virtualization Table), and RQMT (Requirement) entries, thereby improving QoS debugging capabilities. Main Features: 1. Entry Information Display: - FST Table: Displays configuration details such as the number of service level queue virtual channels and the starting queue ID. - FVT Table: Shows information on virtual channel size and requirement offsets. - FST_REVERT Table: Provides mapping relationships between FST index, UE index, queue index, and the number of virtual channels. - RQMT Table: Displays requirement information, including FST index, starting queue index, and queue number offset. 2. Multi-User Entity Support: - Supports querying entry information for the current UE and all registered UEs. - Uses a UE list lock to ensure thread-safe access. This feature provides network administrators with an in-depth visualization tool for traffic steering and virtualization configurations, aiding in the diagnosis and optimization of complex QoS policy configurations. Signed-off-by: Zihao Sheng Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/debugfs/ubase_debugfs.c | 8 ++ drivers/ub/ubase/debugfs/ubase_qos_debugfs.c | 102 +++++++++++++++++++ drivers/ub/ubase/debugfs/ubase_qos_debugfs.h | 1 + 3 files changed, 111 insertions(+) diff --git a/drivers/ub/ubase/debugfs/ubase_debugfs.c b/drivers/ub/ubase/debugfs/ubase_debugfs.c index ef0b162e1188..56ce970411bd 100644 --- a/drivers/ub/ubase/debugfs/ubase_debugfs.c +++ b/drivers/ub/ubase/debugfs/ubase_debugfs.c @@ -314,6 +314,14 @@ static struct ubase_dbg_cmd_info ubase_dbg_cmd[] = { .init = __ubase_dbg_seq_file_init, .read_func = ubase_dbg_dump_adev_qos_info, }, + { + .name = "fst_fvt_rqmt_info", + .dentry_index = UBASE_DBG_DENTRY_QOS, + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_fsv_fvt_rqmt, + }, { .name = "tm_queue", .dentry_index = UBASE_DBG_DENTRY_QOS, diff --git a/drivers/ub/ubase/debugfs/ubase_qos_debugfs.c b/drivers/ub/ubase/debugfs/ubase_qos_debugfs.c index 9b8221e422da..91e05df180bb 100644 --- a/drivers/ub/ubase/debugfs/ubase_qos_debugfs.c +++ b/drivers/ub/ubase/debugfs/ubase_qos_debugfs.c @@ -252,6 +252,108 @@ int ubase_dbg_dump_adev_qos_info(struct seq_file *s, void *data) return 0; } +static void ubase_dbg_fill_fst_fvt(struct seq_file *s, + struct ubase_query_fst_fvt_rqmt_cmd *resp) +{ + seq_puts(s, "\tFST:\n"); + seq_printf(s, "\t\tsl_queue_vl_num: %u\n", + le16_to_cpu(resp->sl_queue_vl_num)); + seq_printf(s, "\t\tsl_queue_start_qid: %u\n", + le16_to_cpu(resp->sl_queue_start_qid)); + seq_puts(s, "\tFVT:\n"); + seq_printf(s, "\t\tfvt_vl_size: %u\n", le16_to_cpu(resp->fvt_vl_size)); + seq_printf(s, "\t\tfvt_rqmt_offset: %u\n", + le16_to_cpu(resp->fvt_rqmt_offset)); +} + +static void ubase_dbg_fill_fst_revert(struct seq_file *s, + struct ubase_query_fst_fvt_rqmt_cmd *resp) +{ + u16 vl_num = min(UBASE_MAX_VL_NUM, le16_to_cpu(resp->sl_queue_vl_num)); + u16 j; + + seq_puts(s, "\tFST_REVERT:\n"); + seq_puts(s, "\t\tFST_IDX UE_IDX QUE_IDX VL_NUM\n"); + + for (j = 0; j < vl_num; j++) { + seq_puts(s, "\t\t"); + seq_printf(s, "%-9u", le16_to_cpu(resp->fstr_info[j].fst_idx)); + seq_printf(s, "%-10u", resp->fstr_info[j].queue_ue_num); + seq_printf(s, "%-10u", resp->fstr_info[j].queue_que_num); + seq_printf(s, "%-9u", le16_to_cpu(resp->fstr_info[j].queue_vl_num)); + seq_puts(s, "\n"); + } +} + +static void ubase_dbg_fill_rqmt(struct seq_file *s, + struct ubase_query_fst_fvt_rqmt_cmd *resp) +{ + u16 vl_size = min(UBASE_MAX_VL_NUM, le16_to_cpu(resp->fvt_vl_size)); + u16 j; + + seq_puts(s, "\tRQMT:\n"); + seq_puts(s, "\t\tFST_IDX QUE_IDX QUE_SHIFT\n"); + + for (j = 0; j < vl_size; j++) { + seq_puts(s, "\t\t"); + seq_printf(s, "%-9u", le16_to_cpu(resp->rqmt_info[j].fst_idx)); + seq_printf(s, "%-10u", + le16_to_cpu(resp->rqmt_info[j].start_queue_idx)); + seq_printf(s, "%-12u", + le16_to_cpu(resp->rqmt_info[j].queue_quantity_shift)); + seq_puts(s, "\n"); + } + + seq_puts(s, "\n"); +} + +static void ubase_dbg_fill_tbl_content(struct seq_file *s, + struct ubase_query_fst_fvt_rqmt_cmd *resp) +{ + ubase_dbg_fill_fst_fvt(s, resp); + + ubase_dbg_fill_fst_revert(s, resp); + + ubase_dbg_fill_rqmt(s, resp); +} + +int ubase_dbg_dump_fsv_fvt_rqmt(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + struct ubase_query_fst_fvt_rqmt_cmd resp = {0}; + struct ubase_ue_node *ue_node; + u16 ue_id; + int ret; + + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits) || + test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + return -EBUSY; + + seq_puts(s, "current ue:\n"); + ret = ubase_query_fst_fvt_rqmt(udev, &resp, 0); + if (ret) + return ret; + + ubase_dbg_fill_tbl_content(s, &resp); + + mutex_lock(&udev->ue_list_lock); + list_for_each_entry(ue_node, &udev->ue_list, list) { + ue_id = ue_node->bus_ue_id; + memset(&resp, 0, sizeof(resp)); + + seq_printf(s, "ue%u:\n", ue_id); + + ret = ubase_query_fst_fvt_rqmt(udev, &resp, ue_id); + if (ret) + goto out; + ubase_dbg_fill_tbl_content(s, &resp); + } + +out: + mutex_unlock(&udev->ue_list_lock); + return ret; +} + static void ubase_dbg_fill_tm_queue_seq(struct seq_file *s, struct ubase_query_tm_queue_cmd *resp) { diff --git a/drivers/ub/ubase/debugfs/ubase_qos_debugfs.h b/drivers/ub/ubase/debugfs/ubase_qos_debugfs.h index 48f45a2dbec0..e44b4cacd21e 100644 --- a/drivers/ub/ubase/debugfs/ubase_qos_debugfs.h +++ b/drivers/ub/ubase/debugfs/ubase_qos_debugfs.h @@ -16,6 +16,7 @@ int ubase_dbg_dump_ets_tcg_info(struct seq_file *s, void *data); int ubase_dbg_dump_ets_port_info(struct seq_file *s, void *data); int ubase_dbg_dump_rack_vl_bitmap(struct seq_file *s, void *data); int ubase_dbg_dump_adev_qos_info(struct seq_file *s, void *data); +int ubase_dbg_dump_fsv_fvt_rqmt(struct seq_file *s, void *data); int ubase_dbg_dump_tm_queue_info(struct seq_file *s, void *data); int ubase_dbg_dump_tm_qset_info(struct seq_file *s, void *data); int ubase_dbg_dump_tm_pri_info(struct seq_file *s, void *data); -- Gitee From 4c9e3090470c60277f061899398ba7fc2013ba5a Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Wed, 29 Oct 2025 16:50:32 +0800 Subject: [PATCH 186/214] ub: ubase: add function that query aeq/ceq/tp/tpg context ANBZ: #45460 commit f817c5c6db98ed97a926202808d90fa395048dc2 openEuler. This patch adds hardware context debugging functionality to the ubase driver, providing visual query capabilities for key hardware contexts such as AEQ, CEQ, TPG, and TP through the debugfs interface. 1. Main Features: - Supports querying AEQ/CEQ event queue contexts (status, size, interrupt configuration, etc.) - Provides display of TPG (Transmission Port Group) and TP (Transmission Port) context information - Implements hardware register context dump functionality with desensitization of key fields - Adds a new context debugging directory, integrating various context query interfaces 2. Technical Features: - Uses a mailbox mechanism to query hardware context registers - Masks sensitive addresses and token information - Supports multi-context group and port bitmap filtering - Thread-safe lock mechanism protects data structure access This implementation provides developers with an in-depth hardware state diagnostic tool, facilitating the debugging of complex hardware interaction issues. Signed-off-by: Fengyan Mu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/debugfs/ubase_ctx_debugfs.c | 369 +++++++++++++++++++ drivers/ub/ubase/debugfs/ubase_ctx_debugfs.h | 20 + drivers/ub/ubase/debugfs/ubase_debugfs.c | 62 ++++ drivers/ub/ubase/ubase_tp.h | 26 ++ 4 files changed, 477 insertions(+) create mode 100644 drivers/ub/ubase/debugfs/ubase_ctx_debugfs.h diff --git a/drivers/ub/ubase/debugfs/ubase_ctx_debugfs.c b/drivers/ub/ubase/debugfs/ubase_ctx_debugfs.c index 1004033c1581..33221a90edd9 100644 --- a/drivers/ub/ubase/debugfs/ubase_ctx_debugfs.c +++ b/drivers/ub/ubase/debugfs/ubase_ctx_debugfs.c @@ -7,6 +7,237 @@ #include #include "ubase_debugfs.h" +#include "ubase_hw.h" +#include "ubase_mailbox.h" +#include "ubase_tp.h" +#include "ubase_ctx_debugfs.h" + +#define UBASE_DEFAULT_CTXGN 0 + +static void ubase_dump_eq_ctx(struct seq_file *s, struct ubase_eq *eq) +{ + seq_printf(s, "%-5u", eq->eqn); + seq_printf(s, "%-13u", eq->entries_num); + seq_printf(s, "%-7u", eq->state); + seq_printf(s, "%-8u", eq->arm_st); + seq_printf(s, "%-10u", eq->eqe_size); + seq_printf(s, "%-11u", eq->eq_period); + seq_printf(s, "%-14u", eq->coalesce_cnt); + seq_printf(s, "%-6u", eq->irqn); + seq_printf(s, "%-10u", eq->eqc_irqn); + seq_printf(s, "%-12u", eq->cons_index); + seq_puts(s, "\n"); +} + +static void ubase_eq_ctx_titles_print(struct seq_file *s) +{ + seq_puts(s, "EQN ENTRIES_NUM STATE ARM_ST EQE_SIZE EQ_PERIOD "); + seq_puts(s, "COALESCE_CNT IRQN EQC_IRQN CONS_INDEX\n"); +} + +static void ubase_dump_aeq_ctx(struct seq_file *s, struct ubase_dev *udev, u32 idx) +{ + struct ubase_aeq *aeq = &udev->irq_table.aeq; + struct ubase_eq *eq = &aeq->eq; + + ubase_dump_eq_ctx(s, eq); +} + +static void ubase_dump_ceq_ctx(struct seq_file *s, struct ubase_dev *udev, u32 idx) +{ + struct ubase_ceq *ceq = &udev->irq_table.ceqs.ceq[idx]; + struct ubase_eq *eq = &ceq->eq; + + ubase_dump_eq_ctx(s, eq); +} + +static void ubase_tpg_ctx_titles_print(struct seq_file *s) +{ + seq_puts(s, "CHANNEL_ID TPGN TP_SHIFT VALID_TP "); + seq_puts(s, "START_TPN TPG_STATE TP_CNT\n"); +} + +static void ubase_dump_tpg_ctx(struct seq_file *s, struct ubase_dev *udev, u32 idx) +{ + struct ubase_tpg *tpg = &udev->tp_ctx.tpg[idx]; + + seq_printf(s, "%-12u", idx); + seq_printf(s, "%-9u", tpg->mb_tpgn); + seq_printf(s, "%-10u", tpg->tp_shift); + seq_printf(s, "%-10lu", tpg->valid_tp); + seq_printf(s, "%-11u", tpg->start_tpn); + seq_printf(s, "%-11u", tpg->tpg_state); + seq_printf(s, "%-8u", tpg->tp_cnt); + seq_puts(s, "\n"); +} + +enum ubase_dbg_ctx_type { + UBASE_DBG_AEQ_CTX = 0, + UBASE_DBG_CEQ_CTX, + UBASE_DBG_TPG_CTX, + UBASE_DBG_TP_CTX, +}; + +static u32 ubase_get_ctx_num(struct ubase_dev *udev, + enum ubase_dbg_ctx_type ctx_type, u32 ctxgn) +{ + struct ubase_adev_caps *unic_caps = &udev->caps.unic_caps; + u32 ctx_num = 0; + + switch (ctx_type) { + case UBASE_DBG_AEQ_CTX: + ctx_num = udev->caps.dev_caps.num_aeq_vectors; + break; + case UBASE_DBG_CEQ_CTX: + ctx_num = udev->irq_table.ceqs.num; + break; + case UBASE_DBG_TPG_CTX: + ctx_num = unic_caps->tpg.max_cnt; + break; + case UBASE_DBG_TP_CTX: + spin_lock(&udev->tp_ctx.tpg_lock); + if (udev->tp_ctx.tpg) + ctx_num = udev->tp_ctx.tpg[ctxgn].tp_cnt; + spin_unlock(&udev->tp_ctx.tpg_lock); + break; + default: + ubase_err(udev, "failed to get ctx num, ctx_type = %u.\n", + ctx_type); + break; + } + + return ctx_num; +} + +static int ubase_dbg_dump_context(struct seq_file *s, + enum ubase_dbg_ctx_type ctx_type) +{ + struct ubase_dbg_ctx { + void (*print_ctx_titles)(struct seq_file *s); + void (*get_ctx)(struct seq_file *s, struct ubase_dev *udev, u32 idx); + } dbg_ctx[] = { + {ubase_eq_ctx_titles_print, ubase_dump_aeq_ctx}, + {ubase_eq_ctx_titles_print, ubase_dump_ceq_ctx}, + {ubase_tpg_ctx_titles_print, ubase_dump_tpg_ctx}, + }; + struct ubase_dev *udev = dev_get_drvdata(s->private); + struct ubase_adev_caps *unic_caps = &udev->caps.unic_caps; + unsigned long port_bitmap; + u32 tp_pos, i; + + dbg_ctx[ctx_type].print_ctx_titles(s); + + port_bitmap = unic_caps->utp_port_bitmap; + for (i = 0; i < ubase_get_ctx_num(udev, ctx_type, UBASE_DEFAULT_CTXGN); i++) { + if (ctx_type != UBASE_DBG_TP_CTX) { + dbg_ctx[ctx_type].get_ctx(s, udev, i); + continue; + } + + tp_pos = (i % unic_caps->tpg.depth) * UBASE_TP_PORT_BITMAP_STEP; + if (test_bit(tp_pos, &port_bitmap)) + dbg_ctx[ctx_type].get_ctx(s, udev, i); + } + + return 0; +} + +struct ubase_ctx_info { + u32 start_idx; + u32 ctx_size; + u8 op; + const char *ctx_name; +}; + +static inline u32 ubase_get_ctx_group_num(struct ubase_dev *udev, + enum ubase_dbg_ctx_type ctx_type) +{ + if (ctx_type == UBASE_DBG_TP_CTX) + return udev->caps.unic_caps.tpg.max_cnt; + + return 1; +} + +static void ubase_get_ctx_info(struct ubase_dev *udev, + enum ubase_dbg_ctx_type ctx_type, + struct ubase_ctx_info *ctx_info, u32 ctxgn) +{ + switch (ctx_type) { + case UBASE_DBG_AEQ_CTX: + ctx_info->start_idx = 0; + ctx_info->ctx_size = UBASE_AEQ_CTX_SIZE; + ctx_info->op = UBASE_MB_QUERY_AEQ_CONTEXT; + ctx_info->ctx_name = "aeq"; + break; + case UBASE_DBG_CEQ_CTX: + ctx_info->start_idx = 0; + ctx_info->ctx_size = UBASE_CEQ_CTX_SIZE; + ctx_info->op = UBASE_MB_QUERY_CEQ_CONTEXT; + ctx_info->ctx_name = "ceq"; + break; + case UBASE_DBG_TPG_CTX: + ctx_info->start_idx = udev->caps.unic_caps.tpg.start_idx; + ctx_info->ctx_size = udev->ctx_buf.tpg.entry_size; + ctx_info->op = UBASE_MB_QUERY_TPG_CONTEXT; + ctx_info->ctx_name = "tpg"; + break; + case UBASE_DBG_TP_CTX: + spin_lock(&udev->tp_ctx.tpg_lock); + ctx_info->start_idx = udev->tp_ctx.tpg ? + udev->tp_ctx.tpg[ctxgn].start_tpn : 0; + spin_unlock(&udev->tp_ctx.tpg_lock); + + ctx_info->ctx_size = udev->ctx_buf.tp.entry_size; + ctx_info->op = UBASE_MB_QUERY_TP_CONTEXT; + ctx_info->ctx_name = "tp"; + break; + default: + ubase_err(udev, "failed to get ctx info, ctx_type = %u.\n", + ctx_type); + break; + } +} + +static void ubase_mask_eq_ctx_key_words(void *buf) +{ + struct ubase_eq_ctx *eq = (struct ubase_eq_ctx *)buf; + + eq->eqe_base_addr_l = 0; + eq->eqe_base_addr_h = 0; + eq->eqe_token_id = 0; + eq->eqe_token_value = 0; +} + +static void ubase_mask_tp_ctx_key_words(void *buf) +{ + struct ubase_tp_ctx *tp = (struct ubase_tp_ctx *)buf; + + tp->wqe_ba_l = 0; + tp->wqe_ba_h = 0; + tp->tp_wqe_token_id = 0; + tp->reorder_q_addr_l = 0; + tp->reorder_q_addr_h = 0; + tp->scc_token = 0; + tp->scc_token_1 = 0; +} + +static void ubase_mask_ctx_key_words(void *buf, + enum ubase_dbg_ctx_type ctx_type) +{ + switch (ctx_type) { + case UBASE_DBG_AEQ_CTX: + case UBASE_DBG_CEQ_CTX: + ubase_mask_eq_ctx_key_words(buf); + break; + case UBASE_DBG_TPG_CTX: + break; + case UBASE_DBG_TP_CTX: + ubase_mask_tp_ctx_key_words(buf); + break; + default: + break; + } +} static void __ubase_print_context_hw(struct seq_file *s, void *ctx_addr, u32 ctx_len) @@ -29,3 +260,141 @@ void ubase_print_context_hw(struct seq_file *s, void *ctx_addr, u32 ctx_len) __ubase_print_context_hw(s, ctx_addr, ctx_len); } EXPORT_SYMBOL(ubase_print_context_hw); + +static int ubase_dbg_dump_ctx_hw(struct seq_file *s, void *data, + enum ubase_dbg_ctx_type ctx_type) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + struct ubase_ctx_info ctx_info = {0}; + struct ubase_cmd_mailbox *mailbox; + u32 max_ctxgn, ctxn, ctxgn; + struct ubase_mbx_attr attr; + int ret = 0; + + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits) || + test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + return -EBUSY; + + mailbox = __ubase_alloc_cmd_mailbox(udev); + if (IS_ERR_OR_NULL(mailbox)) { + ubase_err(udev, + "failed to alloc mailbox for dump hw context.\n"); + return -ENOMEM; + } + + max_ctxgn = ubase_get_ctx_group_num(udev, ctx_type); + for (ctxgn = 0; ctxgn < max_ctxgn; ctxgn++) { + ubase_get_ctx_info(udev, ctx_type, &ctx_info, ctxgn); + + for (ctxn = 0; ctxn < ubase_get_ctx_num(udev, ctx_type, ctxgn); ctxn++) { + ubase_fill_mbx_attr(&attr, ctxn + ctx_info.start_idx, + ctx_info.op, 0); + ret = __ubase_hw_upgrade_ctx_ex(udev, &attr, mailbox); + if (ret) { + ubase_err(udev, + "failed to post query %s ctx mbx, ret = %d.\n", + ctx_info.ctx_name, ret); + goto upgrade_ctx_err; + } + + seq_printf(s, "offset\t%s%u\n", ctx_info.ctx_name, + ctxn + ctx_info.start_idx); + ubase_mask_ctx_key_words(mailbox->buf, ctx_type); + __ubase_print_context_hw(s, mailbox->buf, ctx_info.ctx_size); + seq_puts(s, "\n"); + } + } + +upgrade_ctx_err: + __ubase_free_cmd_mailbox(udev, mailbox); + + return ret; +} + +int ubase_dbg_dump_aeq_context(struct seq_file *s, void *data) +{ + return ubase_dbg_dump_context(s, UBASE_DBG_AEQ_CTX); +} + +int ubase_dbg_dump_ceq_context(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + int ret; + + if (!mutex_trylock(&udev->irq_table.ceq_lock)) + return -EBUSY; + + if (!udev->irq_table.ceqs.ceq) { + mutex_unlock(&udev->irq_table.ceq_lock); + return -EBUSY; + } + + ret = ubase_dbg_dump_context(s, UBASE_DBG_CEQ_CTX); + mutex_unlock(&udev->irq_table.ceq_lock); + + return ret; +} + +int ubase_dbg_dump_tpg_ctx(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + int ret; + + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits)) + return -EBUSY; + + if (!ubase_get_ctx_num(udev, UBASE_DBG_TPG_CTX, UBASE_DEFAULT_CTXGN)) + return -EOPNOTSUPP; + + if (!spin_trylock(&udev->tp_ctx.tpg_lock)) + return -EBUSY; + + if (!udev->tp_ctx.tpg) { + spin_unlock(&udev->tp_ctx.tpg_lock); + return -EBUSY; + } + + ret = ubase_dbg_dump_context(s, UBASE_DBG_TPG_CTX); + spin_unlock(&udev->tp_ctx.tpg_lock); + + return ret; +} + +int ubase_dbg_dump_tpg_ctx_hw(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits)) + return -EBUSY; + + if (!ubase_get_ctx_num(udev, UBASE_DBG_TPG_CTX, UBASE_DEFAULT_CTXGN)) + return -EOPNOTSUPP; + + return ubase_dbg_dump_ctx_hw(s, data, UBASE_DBG_TPG_CTX); +} + +int ubase_dbg_dump_tp_ctx_hw(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits)) + return -EBUSY; + + if (!ubase_get_ctx_num(udev, UBASE_DBG_TP_CTX, UBASE_DEFAULT_CTXGN)) + return -EOPNOTSUPP; + + if (!ubase_get_ctx_group_num(udev, UBASE_DBG_TP_CTX)) + return -EOPNOTSUPP; + + return ubase_dbg_dump_ctx_hw(s, data, UBASE_DBG_TP_CTX); +} + +int ubase_dbg_dump_aeq_ctx_hw(struct seq_file *s, void *data) +{ + return ubase_dbg_dump_ctx_hw(s, data, UBASE_DBG_AEQ_CTX); +} + +int ubase_dbg_dump_ceq_ctx_hw(struct seq_file *s, void *data) +{ + return ubase_dbg_dump_ctx_hw(s, data, UBASE_DBG_CEQ_CTX); +} diff --git a/drivers/ub/ubase/debugfs/ubase_ctx_debugfs.h b/drivers/ub/ubase/debugfs/ubase_ctx_debugfs.h new file mode 100644 index 000000000000..532665141fc8 --- /dev/null +++ b/drivers/ub/ubase/debugfs/ubase_ctx_debugfs.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UBASE_CTX_DEBUGFS_H__ +#define __UBASE_CTX_DEBUGFS_H__ + +struct device; + +int ubase_dbg_dump_aeq_context(struct seq_file *s, void *data); +int ubase_dbg_dump_ceq_context(struct seq_file *s, void *data); +int ubase_dbg_dump_tpg_ctx(struct seq_file *s, void *data); +int ubase_dbg_dump_tp_ctx_hw(struct seq_file *s, void *data); +int ubase_dbg_dump_tpg_ctx_hw(struct seq_file *s, void *data); +int ubase_dbg_dump_aeq_ctx_hw(struct seq_file *s, void *data); +int ubase_dbg_dump_ceq_ctx_hw(struct seq_file *s, void *data); + +#endif diff --git a/drivers/ub/ubase/debugfs/ubase_debugfs.c b/drivers/ub/ubase/debugfs/ubase_debugfs.c index 56ce970411bd..ab3e1a88ced8 100644 --- a/drivers/ub/ubase/debugfs/ubase_debugfs.c +++ b/drivers/ub/ubase/debugfs/ubase_debugfs.c @@ -8,6 +8,7 @@ #include #include +#include "ubase_ctx_debugfs.h" #include "ubase_dev.h" #include "ubase_hw.h" #include "ubase_qos_debugfs.h" @@ -216,6 +217,11 @@ int ubase_dbg_seq_file_init(struct device *dev, EXPORT_SYMBOL(ubase_dbg_seq_file_init); static struct ubase_dbg_dentry_info ubase_dbg_dentry[] = { + { + .name = "context", + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + }, { .name = "qos", .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_UBL_ETH, @@ -242,6 +248,22 @@ static struct ubase_dbg_cmd_info ubase_dbg_cmd[] = { .init = __ubase_dbg_seq_file_init, .read_func = ubase_dbg_dump_rst_info, }, + { + .name = "aeq_context", + .dentry_index = UBASE_DBG_DENTRY_CONTEXT, + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_aeq_context, + }, + { + .name = "ceq_context", + .dentry_index = UBASE_DBG_DENTRY_CONTEXT, + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_ceq_context, + }, { .name = "activate_record", .dentry_index = UBASE_DBG_DENTRY_ROOT, @@ -250,6 +272,46 @@ static struct ubase_dbg_cmd_info ubase_dbg_cmd[] = { .init = __ubase_dbg_seq_file_init, .read_func = ubase_dbg_dump_activate_record, }, + { + .name = "tpg_context", + .dentry_index = UBASE_DBG_DENTRY_CONTEXT, + .property = UBASE_SUP_URMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_tpg_ctx, + }, + { + .name = "tp_context_hw", + .dentry_index = UBASE_DBG_DENTRY_CONTEXT, + .property = UBASE_SUP_URMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_tp_ctx_hw, + }, + { + .name = "tpg_context_hw", + .dentry_index = UBASE_DBG_DENTRY_CONTEXT, + .property = UBASE_SUP_URMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_tpg_ctx_hw, + }, + { + .name = "aeq_context_hw", + .dentry_index = UBASE_DBG_DENTRY_CONTEXT, + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_aeq_ctx_hw, + }, + { + .name = "ceq_context_hw", + .dentry_index = UBASE_DBG_DENTRY_CONTEXT, + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_ceq_ctx_hw, + }, { .name = "sl_vl_map", .dentry_index = UBASE_DBG_DENTRY_QOS, diff --git a/drivers/ub/ubase/ubase_tp.h b/drivers/ub/ubase/ubase_tp.h index 965535a18f1a..0506e77c98f0 100644 --- a/drivers/ub/ubase/ubase_tp.h +++ b/drivers/ub/ubase/ubase_tp.h @@ -11,8 +11,34 @@ #include "ubase_dev.h" +#define UBASE_TP_PORT_BITMAP_STEP 2 + #define UBASE_WAIT_TP_FLUSH_TOTAL_STEPS 12 +struct ubase_tp_ctx { + u32 rsvd0; + u32 wqe_ba_l; + u32 wqe_ba_h : 20; + u32 rsvd1 : 12; + u32 rsvd2[5]; + u32 rsvd3_0 : 4; + u32 tp_wqe_token_id : 20; + u32 rsvd3_1 : 8; + u32 rsvd4[5]; + u32 rsvd5 : 4; + u32 reorder_q_addr_l : 28; + u32 reorder_q_addr_h : 24; + u32 rsvd6 : 8; + u32 rsvd7[5]; + u32 scc_token : 19; + u32 rsvd8 : 13; + u32 rsvd9[4]; + u32 rsvd10_0 : 24; + u32 scc_token_1 : 4; + u32 rsvd10_1 : 4; + u32 rsvd11[37]; +}; + struct ubase_tpg { u32 mb_tpgn; u8 tpg_state; -- Gitee From 911410a3fd5dfddc4b61a554af4b22bda3e95d0a Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Wed, 29 Oct 2025 16:53:51 +0800 Subject: [PATCH 187/214] ub: ubase: Supports the query of UBCL config. ANBZ: #45460 commit 8a2d8b74eb75df90052e3baad58e645aa0706b7f openEuler. UBCL config file is a binary file. The file content determines the configuration of the UB system and deeply affects the running of it. If the file can be read conveniently and effectively, the maintenance capability of the system will be greatly improved. This patch allows the UBASE driver to read the content of UBCL config through debugfs. Users can use debugfs to read the file. Signed-off-by: Yixi Shen Signed-off-by: Haibin Lu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/debugfs/ubase_debugfs.c | 88 ++++++++++++++++++++++++ drivers/ub/ubase/ubase_cmd.h | 10 +++ include/ub/ubase/ubase_comm_cmd.h | 1 + 3 files changed, 99 insertions(+) diff --git a/drivers/ub/ubase/debugfs/ubase_debugfs.c b/drivers/ub/ubase/debugfs/ubase_debugfs.c index ab3e1a88ced8..76175d604366 100644 --- a/drivers/ub/ubase/debugfs/ubase_debugfs.c +++ b/drivers/ub/ubase/debugfs/ubase_debugfs.c @@ -8,6 +8,7 @@ #include #include +#include "ubase_cmd.h" #include "ubase_ctx_debugfs.h" #include "ubase_dev.h" #include "ubase_hw.h" @@ -31,6 +32,85 @@ static int ubase_dbg_dump_rst_info(struct seq_file *s, void *data) return 0; } +static int ubase_query_ubcl_config(struct ubase_dev *udev, u16 offset, + u16 is_query, u16 size, + struct ubase_ubcl_config_cmd *resp) +{ + struct ubase_ubcl_config_cmd req; + struct ubase_cmd_buf in, out; + int ret; + + memset(resp, 0, sizeof(*resp)); + memset(&req, 0, sizeof(req)); + req.offset = cpu_to_le16(offset); + req.size = cpu_to_le16(size); + req.is_query_size = cpu_to_le16(is_query); + + __ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_UBCL_CONFIG, true, + sizeof(req), &req); + __ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_UBCL_CONFIG, true, + sizeof(*resp), resp); + ret = __ubase_cmd_send_inout(udev, &in, &out); + if (ret && ret != -EPERM) + ubase_err(udev, "failed to query UBCL_config, ret = %d.\n", ret); + + if (ret == -EPERM) + return -EOPNOTSUPP; + + return ret; +} + +static void ubase_dbg_fill_ubcl_content(struct ubase_ubcl_config_cmd *resp, + u32 *addr, struct seq_file *s) +{ + int i, j; + + for (i = 0; i < UBASE_UBCL_CFG_DATA_NUM; i += UBASE_UBCL_CFG_DATA_ALIGN) { + seq_printf(s, "%08X: ", (*addr * UBASE_UBCL_CFG_DATA_ALIGN)); + for (j = 0; j < UBASE_UBCL_CFG_DATA_ALIGN; j++) + seq_printf(s, "%08X ", resp->data[i + j]); + seq_puts(s, "\n"); + + *addr += UBASE_UBCL_CFG_DATA_ALIGN; + if ((i * sizeof(u32)) >= resp->size) + break; + } +} + +static int ubase_dbg_dump_ubcl_config(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + struct ubase_ubcl_config_cmd resp = {0}; + u16 read_size = sizeof(resp.data); + u16 offset = 0; + u16 total_size; + u32 addr = 0; + int ret; + + if (!test_bit(UBASE_STATE_INITED_B, &udev->state_bits) || + test_bit(UBASE_STATE_RST_HANDLING_B, &udev->state_bits)) + return -EBUSY; + + ret = ubase_query_ubcl_config(udev, offset, 1, 0, &resp); + if (ret) + return ret; + total_size = le16_to_cpu(resp.size); + + seq_puts(s, "UBCL_config:\n"); + seq_printf(s, "total_size: %u\n", total_size); + while (offset < total_size) { + read_size = min(read_size, total_size - offset); + ret = ubase_query_ubcl_config(udev, offset, 0, read_size, &resp); + if (ret) + return ret; + offset += le16_to_cpu(resp.size); + + ubase_dbg_fill_ubcl_content(&resp, &addr, s); + } + + return 0; +} + static int ubase_dbg_dump_activate_record(struct seq_file *s, void *data) { struct ubase_dev *udev = dev_get_drvdata(s->private); @@ -264,6 +344,14 @@ static struct ubase_dbg_cmd_info ubase_dbg_cmd[] = { .init = __ubase_dbg_seq_file_init, .read_func = ubase_dbg_dump_ceq_context, }, + { + .name = "UBCL_config", + .dentry_index = UBASE_DBG_DENTRY_ROOT, + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_ubcl_config, + }, { .name = "activate_record", .dentry_index = UBASE_DBG_DENTRY_ROOT, diff --git a/drivers/ub/ubase/ubase_cmd.h b/drivers/ub/ubase/ubase_cmd.h index 0187597493b7..63b67179f2fb 100644 --- a/drivers/ub/ubase/ubase_cmd.h +++ b/drivers/ub/ubase/ubase_cmd.h @@ -49,6 +49,16 @@ struct ubase_query_version_cmd { __le32 caps[UBASE_CAP_LEN]; }; +#define UBASE_UBCL_CFG_DATA_ALIGN 4 +#define UBASE_UBCL_CFG_DATA_NUM 60 +struct ubase_ubcl_config_cmd { + __le16 is_query_size; + __le16 offset; + __le16 size; + __le16 rsv; + __le32 data[UBASE_UBCL_CFG_DATA_NUM]; +}; + enum ubase_ue2ue_sub_cmd { UBASE_UE2UE_CTRLQ_MSG = 3, }; diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index 7e78860d7778..311a309d10bd 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -37,6 +37,7 @@ enum ubase_opcode_type { UBASE_OPC_QUERY_NET_GUID = 0x0035, UBASE_OPC_STATS_MAC_ALL = 0x0038, UBASE_OPC_QUERY_BUS_EID = 0x0047, + UBASE_OPC_QUERY_UBCL_CONFIG = 0x0050, /* NL commands */ UBASE_OPC_CFG_VL_MAP = 0x2206, -- Gitee From 99cc4b38b01b444e67c4706d1af972941f153ae7 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Wed, 29 Oct 2025 16:58:25 +0800 Subject: [PATCH 188/214] ub: ubase: Support querying dev caps. ANBZ: #45460 commit 7bb0989aa20b20df97785020ba318a0fc5915d37 openEuler. The UB driver supports a set of code running in different business contexts. The ub driver uses dev caps to distinguish different contexts. Therefore, the debugging method of dev caps is important. This patch allows the UBASE driver to print dev caps for users. Users can use this function through debugfs. Signed-off-by: Yaoyao Tu Signed-off-by: Fengyan Mu Signed-off-by: Haibin Lu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/debugfs/ubase_debugfs.c | 157 +++++++++++++++++++++++ 1 file changed, 157 insertions(+) diff --git a/drivers/ub/ubase/debugfs/ubase_debugfs.c b/drivers/ub/ubase/debugfs/ubase_debugfs.c index 76175d604366..ad97d7a58188 100644 --- a/drivers/ub/ubase/debugfs/ubase_debugfs.c +++ b/drivers/ub/ubase/debugfs/ubase_debugfs.c @@ -32,6 +32,154 @@ static int ubase_dbg_dump_rst_info(struct seq_file *s, void *data) return 0; } +static void ubase_dbg_dump_caps_bits(struct seq_file *s, struct ubase_dev *udev) +{ +#define CAP_FMT(name) "\tsupport_" #name ": %d\n" +#define PTRINT_CAP(name, func) seq_printf(s, CAP_FMT(name), func(udev)) + + PTRINT_CAP(ub_link, ubase_dev_ubl_supported); + PTRINT_CAP(ta_extdb_buffer_config, ubase_dev_ta_extdb_buf_supported); + PTRINT_CAP(ta_timer_buffer_config, ubase_dev_ta_timer_buf_supported); + PTRINT_CAP(err_handle, ubase_dev_err_handle_supported); + PTRINT_CAP(ctrlq, ubase_dev_ctrlq_supported); + PTRINT_CAP(eth_mac, ubase_dev_eth_mac_supported); + PTRINT_CAP(mac_stats, ubase_dev_mac_stats_supported); + PTRINT_CAP(prealloc, __ubase_dev_prealloc_supported); + PTRINT_CAP(udma, ubase_dev_udma_supported); + PTRINT_CAP(unic, ubase_dev_unic_supported); + PTRINT_CAP(uvb, ubase_dev_uvb_supported); + PTRINT_CAP(ip_over_urma, ubase_ip_over_urma_supported); + if (ubase_ip_over_urma_supported(udev)) + PTRINT_CAP(ip_over_urma_utp, ubase_ip_over_urma_utp_supported); + PTRINT_CAP(activate_proxy, ubase_activate_proxy_supported); + PTRINT_CAP(utp, ubase_utp_supported); +} + +static void ubase_dbg_dump_caps_info(struct seq_file *s, struct ubase_dev *udev) +{ + struct ubase_caps *dev_caps = &udev->caps.dev_caps; + struct ubase_dbg_common_caps_info { + const char *format; + u64 caps_info; + } ubase_common_caps_info[] = { + {"\tnum_ceq_vectors: %u\n", dev_caps->num_ceq_vectors}, + {"\tnum_aeq_vectors: %u\n", dev_caps->num_aeq_vectors}, + {"\tnum_misc_vectors: %u\n", dev_caps->num_misc_vectors}, + {"\taeqe_size: %u\n", dev_caps->aeqe_size}, + {"\tceqe_size: %u\n", dev_caps->ceqe_size}, + {"\taeqe_depth: %u\n", dev_caps->aeqe_depth}, + {"\tceqe_depth: %u\n", dev_caps->ceqe_depth}, + {"\ttotal_ue_num: %u\n", dev_caps->total_ue_num}, + {"\tta_extdb_buf_size: %llu\n", udev->ta_ctx.extdb_buf.size}, + {"\tta_timer_buf_size: %llu\n", udev->ta_ctx.timer_buf.size}, + {"\tpublic_jetty_cnt: %u\n", dev_caps->public_jetty_cnt}, + {"\tvl_num: %hhu\n", dev_caps->vl_num}, + {"\trsvd_jetty_cnt: %hu\n", dev_caps->rsvd_jetty_cnt}, + {"\tpacket_pattern_mode: %u\n", dev_caps->packet_pattern_mode}, + {"\tack_queue_num: %u\n", dev_caps->ack_queue_num}, + {"\toor_en: %u\n", dev_caps->oor_en}, + {"\treorder_queue_en: %u\n", dev_caps->reorder_queue_en}, + {"\ton_flight_size: %u\n", dev_caps->on_flight_size}, + {"\treorder_cap: %u\n", dev_caps->reorder_cap}, + {"\treorder_queue_shift: %u\n", dev_caps->reorder_queue_shift}, + {"\tat_times: %u\n", dev_caps->at_times}, + {"\tue_num: %u\n", dev_caps->ue_num}, + {"\tmac_stats_num: %u\n", dev_caps->mac_stats_num}, + {"\tlogic_port_bitmap: 0x%x\n", dev_caps->logic_port_bitmap}, + {"\tub_port_logic_id: %u\n", dev_caps->ub_port_logic_id}, + {"\tio_port_logic_id: %u\n", dev_caps->io_port_logic_id}, + {"\tio_port_id: %u\n", dev_caps->io_port_id}, + {"\tnl_port_id: %u\n", dev_caps->nl_port_id}, + {"\tchip_id: %u\n", dev_caps->chip_id}, + {"\tdie_id: %u\n", dev_caps->die_id}, + {"\tue_id: %u\n", dev_caps->ue_id}, + {"\tnl_id: %u\n", dev_caps->nl_id}, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(ubase_common_caps_info); i++) + seq_printf(s, ubase_common_caps_info[i].format, + ubase_common_caps_info[i].caps_info); +} + +static void ubase_dbg_dump_common_caps(struct seq_file *s, struct ubase_dev *udev) +{ + struct ubase_caps *dev_caps = &udev->caps.dev_caps; + + ubase_dbg_dump_caps_info(s, udev); + + seq_puts(s, "\treq_vl:"); + ubase_dbg_dump_arr_info(s, dev_caps->req_vl, dev_caps->vl_num); + + seq_puts(s, "\tresp_vl:"); + ubase_dbg_dump_arr_info(s, dev_caps->resp_vl, dev_caps->vl_num); +} + +static void ubase_dbg_dump_adev_caps(struct seq_file *s, + struct ubase_adev_caps *caps) +{ + struct ubase_dbg_adev_caps_info { + const char *format; + u32 caps_info; + } ubase_adev_caps_info[] = { + {"\tjfs_max_cnt: %u\n", caps->jfs.max_cnt}, + {"\tjfs_reserved_cnt: %u\n", caps->jfs.reserved_cnt}, + {"\tjfs_depth: %u\n", caps->jfs.depth}, + {"\tjfr_max_cnt: %u\n", caps->jfr.max_cnt}, + {"\tjfr_reserved_cnt: %u\n", caps->jfr.reserved_cnt}, + {"\tjfr_depth: %u\n", caps->jfr.depth}, + {"\tjfc_max_cnt: %u\n", caps->jfc.max_cnt}, + {"\tjfc_reserved_cnt: %u\n", caps->jfc.reserved_cnt}, + {"\tjfc_depth: %u\n", caps->jfc.depth}, + {"\ttp_max_cnt: %u\n", caps->tp.max_cnt}, + {"\ttp_reserved_cnt: %u\n", caps->tp.reserved_cnt}, + {"\ttp_depth: %u\n", caps->tp.depth}, + {"\ttpg_max_cnt: %u\n", caps->tpg.max_cnt}, + {"\ttpg_reserved_cnt: %u\n", caps->tpg.reserved_cnt}, + {"\ttpg_depth: %u\n", caps->tpg.depth}, + {"\tcqe_size: %hu\n", caps->cqe_size}, + {"\tutp_port_bitmap: 0x%x\n", caps->utp_port_bitmap}, + {"\tjtg_max_cnt: %u\n", caps->jtg_max_cnt}, + {"\trc_max_cnt: %u\n", caps->rc_max_cnt}, + {"\trc_depth: %u\n", caps->rc_que_depth}, + {"\tccc_max_cnt: %u\n", caps->ccc_max_cnt}, + {"\tdest_addr_max_cnt: %u\n", caps->dest_addr_max_cnt}, + {"\tseid_upi_max_cnt: %u\n", caps->seid_upi_max_cnt}, + {"\ttpm_max_cnt: %u\n", caps->tpm_max_cnt}, + {"\tprealloc_mem_dma_len: %llu\n", caps->pmem.dma_len}, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(ubase_adev_caps_info); i++) + seq_printf(s, ubase_adev_caps_info[i].format, + ubase_adev_caps_info[i].caps_info); +} + +static int ubase_dbg_dump_dev_caps(struct seq_file *s, void *data) +{ + struct ubase_dev *udev = dev_get_drvdata(s->private); + struct ubase_dev_caps *udev_caps = &udev->caps; + + seq_puts(s, "CAP_BITS:\n"); + ubase_dbg_dump_caps_bits(s, udev); + seq_puts(s, "\nCOMMON_CAPS:\n"); + ubase_dbg_dump_common_caps(s, udev); + + if (ubase_dev_pmu_supported(udev)) + return 0; + + seq_puts(s, "\nUNIC_CAPS:\n"); + ubase_dbg_dump_adev_caps(s, &udev_caps->unic_caps); + + if (ubase_dev_cdma_supported(udev)) + seq_puts(s, "\nCDMA_CAPS:\n"); + else + seq_puts(s, "\nUDMA_CAPS:\n"); + ubase_dbg_dump_adev_caps(s, &udev_caps->udma_caps); + + return 0; +} + static int ubase_query_ubcl_config(struct ubase_dev *udev, u16 offset, u16 is_query, u16 size, struct ubase_ubcl_config_cmd *resp) @@ -344,6 +492,15 @@ static struct ubase_dbg_cmd_info ubase_dbg_cmd[] = { .init = __ubase_dbg_seq_file_init, .read_func = ubase_dbg_dump_ceq_context, }, + { + .name = "caps_info", + .dentry_index = UBASE_DBG_DENTRY_ROOT, + .property = UBASE_SUP_URMA | UBASE_SUP_CDMA | UBASE_SUP_PMU | + UBASE_SUP_UBL_ETH, + .support = __ubase_dbg_dentry_support, + .init = __ubase_dbg_seq_file_init, + .read_func = ubase_dbg_dump_dev_caps, + }, { .name = "UBCL_config", .dentry_index = UBASE_DBG_DENTRY_ROOT, -- Gitee From 8b021f0d8f7871eb43c854a339cea0939004a86e Mon Sep 17 00:00:00 2001 From: Fengyan Mu Date: Mon, 10 Nov 2025 11:34:23 +0800 Subject: [PATCH 189/214] ub: ubase: Fix the problem that free pages when ubase driver destroy TA context ANBZ: #45460 commit c19452d5589463c279520ac69794b897c3069d36 openEuler. Fix the problem that free pages when ubase driver destroy TA context which we don't want to release, because the upper driver still need to use them for further use. Fixes: 8d68017f37fa ("ub: ubase: support for command process") Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_mailbox.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/ub/ubase/ubase_mailbox.c b/drivers/ub/ubase/ubase_mailbox.c index 9951929a7f8d..ff3ff78ca513 100644 --- a/drivers/ub/ubase/ubase_mailbox.c +++ b/drivers/ub/ubase/ubase_mailbox.c @@ -517,8 +517,7 @@ int __ubase_hw_upgrade_ctx_ex(struct ubase_dev *udev, } ret = __ubase_hw_upgrade_ctx(udev, attr, mailbox); - if ((ret && type == UBASE_MB_CREATE) || - (!ret && type == UBASE_MB_DESTROY)) + if (ret && type == UBASE_MB_CREATE) ubase_free_buf_ctx_page(udev, ctx_buf, attr->tag); return ret; -- Gitee From d94abfb4d4b8f31fcb800588147b96a1b08840a6 Mon Sep 17 00:00:00 2001 From: Fengyan Mu Date: Mon, 10 Nov 2025 15:08:50 +0800 Subject: [PATCH 190/214] ub: ubase: Fix some compile warnings ANBZ: #45460 commit 0c32deb41bc70fd6dc27d8421fd163cbd4172d45 openEuler. Fix some compile warnings including unused struct, incompatible definition of struct and missed "static" modifier. | Reported-by: kernel test robot | Closes: https://lore.kernel.org/oe-kbuild-all/202511081833 | .oncKY6Q3-lkp@intel.com/ | Closes: https://lore.kernel.org/oe-kbuild-all/202511091319 | .N8VHQUDq-lkp@intel.com/ | Closes: https://lore.kernel.org/oe-kbuild-all/202511090331 | .elnR753P-lkp@inte.com/ Fixes: da7d25293a38 ("ub: ubase: support pre-alloc 2M pages.") Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_ctrlq.c | 2 +- drivers/ub/ubase/ubase_hw.c | 6 ++--- drivers/ub/ubase/ubase_pmem.c | 40 +++++++++++++++++++++++++--------- drivers/ub/ubase/ubase_ubus.c | 6 +---- 4 files changed, 35 insertions(+), 19 deletions(-) diff --git a/drivers/ub/ubase/ubase_ctrlq.c b/drivers/ub/ubase/ubase_ctrlq.c index 0849db308360..d590463b0efc 100644 --- a/drivers/ub/ubase/ubase_ctrlq.c +++ b/drivers/ub/ubase/ubase_ctrlq.c @@ -905,7 +905,7 @@ static inline void ubase_ctrlq_reset_crq_ci(struct ubase_dev *udev) ubase_write_dev(&udev->hw, UBASE_CTRLQ_CRQ_HEAD_REG, crq->ci); } -void ubase_ctrlq_crq_handler(struct ubase_dev *udev) +static void ubase_ctrlq_crq_handler(struct ubase_dev *udev) { struct ubase_ctrlq_ring *crq = &udev->ctrlq.crq; struct ub_entity *ue = to_ub_entity(udev->dev); diff --git a/drivers/ub/ubase/ubase_hw.c b/drivers/ub/ubase/ubase_hw.c index d76c0f13222b..d728dd47f116 100644 --- a/drivers/ub/ubase/ubase_hw.c +++ b/drivers/ub/ubase/ubase_hw.c @@ -427,9 +427,9 @@ static int ubase_config_jfs_ctx_buf_by_pmem(struct ubase_dev *udev, return ret; } -int ubase_cmd_ctx_buf_alloc(struct ubase_dev *udev, - struct ubase_ctx_buf_cap *ctx_buf, - struct ubase_mbx_attr *attr) +static int ubase_cmd_ctx_buf_alloc(struct ubase_dev *udev, + struct ubase_ctx_buf_cap *ctx_buf, + struct ubase_mbx_attr *attr) { size_t size = ctx_buf->entry_cnt * ctx_buf->entry_size; int ret; diff --git a/drivers/ub/ubase/ubase_pmem.c b/drivers/ub/ubase/ubase_pmem.c index 21b755d3cc8c..a43f1c39c0be 100644 --- a/drivers/ub/ubase/ubase_pmem.c +++ b/drivers/ub/ubase/ubase_pmem.c @@ -9,6 +9,11 @@ #include "ubase_pmem.h" +struct ubase_pmem_init_func { + int (*init)(struct ubase_dev *udev); + void (*uninit)(struct ubase_dev *udev); +}; + static u16 ubase_calc_comm_page_cnt(struct ubase_dev *udev) { u32 ta_timer_size = ubase_ta_timer_align_size(udev); @@ -232,16 +237,31 @@ static void ubase_unmap_udma_pmem(struct ubase_dev *udev) pmem->dma_len = 0; } -static const struct ubase_pmem_init_func { - int (*init)(struct ubase_dev *udev); - void (*uninit)(struct ubase_dev *udev); -} ubase_pmem_init_map[] = { - { ubase_init_comm_pmem_ctx, ubase_uninit_comm_pmem_ctx }, - { ubase_init_udma_pmem_ctx, ubase_uninit_udma_pmem_ctx }, - { ubase_alloc_comm_pmem, ubase_free_comm_pmem }, - { ubase_alloc_udma_pmem, ubase_free_udma_pmem }, - { ubase_map_comm_pmem, ubase_unmap_comm_pmem }, - { ubase_map_udma_pmem, ubase_unmap_udma_pmem }, +static struct ubase_pmem_init_func ubase_pmem_init_map[] = { + { + .init = ubase_init_comm_pmem_ctx, + .uninit = ubase_uninit_comm_pmem_ctx, + }, + { + .init = ubase_init_udma_pmem_ctx, + .uninit = ubase_uninit_udma_pmem_ctx, + }, + { + .init = ubase_alloc_comm_pmem, + .uninit = ubase_free_comm_pmem, + }, + { + .init = ubase_alloc_udma_pmem, + .uninit = ubase_free_udma_pmem, + }, + { + .init = ubase_map_comm_pmem, + .uninit = ubase_unmap_comm_pmem, + }, + { + .init = ubase_map_udma_pmem, + .uninit = ubase_unmap_udma_pmem, + }, }; static int __ubase_prealloc_mem_init(struct ubase_dev *udev) diff --git a/drivers/ub/ubase/ubase_ubus.c b/drivers/ub/ubase/ubase_ubus.c index 9ae27adb413b..a589915cd686 100644 --- a/drivers/ub/ubase/ubase_ubus.c +++ b/drivers/ub/ubase/ubase_ubus.c @@ -350,11 +350,7 @@ static int ubase_ubus_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(ubase_ubus_pm_ops, ubase_ubus_suspend, ubase_ubus_resume); - -static const struct device_driver ubase_ue_driver = { - .pm = &ubase_ubus_pm_ops, -}; +static DEFINE_SIMPLE_DEV_PM_OPS(ubase_ubus_pm_ops, ubase_ubus_suspend, ubase_ubus_resume); static int ubase_ubus_virt_configure(struct ub_entity *ue, int bus_ue_id, bool is_en) { -- Gitee From 4ff7255191a7d3f232edb4c318cbcc208c872827 Mon Sep 17 00:00:00 2001 From: Haibin Lu Date: Wed, 24 Sep 2025 15:26:09 +0800 Subject: [PATCH 191/214] net: unic: Support driver dump register. ANBZ: #45460 commit 8ca3dec3d53e6bd47e70f9f599ace2a704aaf85e openEuler. Currently, the commit operation contains only the framework of dump registers and does not contain the specific register address of the bar space. This patch complete functions: 1.Add tlv format data to registers data. 2.Register information can be obtained from the firmware and bar space. Signed-off-by: Jianqiang Li Signed-off-by: Xiongchuan Zhou Signed-off-by: Haibin Lu Signed-off-by: Junxin Chen Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/unic_dev.h | 17 ++ drivers/net/ub/unic/unic_ethtool.c | 2 + drivers/net/ub/unic/unic_stats.c | 286 +++++++++++++++++++++++++++++ drivers/net/ub/unic/unic_stats.h | 70 +++++++ include/ub/ubase/ubase_comm_cmd.h | 6 + 5 files changed, 381 insertions(+) diff --git a/drivers/net/ub/unic/unic_dev.h b/drivers/net/ub/unic/unic_dev.h index f0df795ad011..8654aa9a819e 100644 --- a/drivers/net/ub/unic/unic_dev.h +++ b/drivers/net/ub/unic/unic_dev.h @@ -262,6 +262,11 @@ static inline bool unic_dev_ubl_supported(struct unic_dev *unic_dev) return ubase_adev_ubl_supported(unic_dev->comdev.adev); } +static inline bool unic_dev_eth_mac_supported(struct unic_dev *unic_dev) +{ + return ubase_adev_eth_mac_supported(unic_dev->comdev.adev); +} + static inline bool unic_dev_ets_supported(struct unic_dev *unic_dev) { return unic_get_cap_bit(unic_dev, UNIC_SUPPORT_ETS_B); @@ -327,6 +332,18 @@ static inline bool unic_is_initing_or_resetting(struct unic_dev *unic_dev) return __unic_resetting(unic_dev) || __unic_initing(unic_dev); } +static inline u32 unic_read_reg(struct unic_dev *unic_dev, u32 reg) +{ + struct ubase_resource_space *io_base = ubase_get_io_base(unic_dev->comdev.adev); + u8 __iomem *reg_addr; + + if (!io_base) + return 0; + + reg_addr = READ_ONCE(io_base->addr); + return readl(reg_addr + reg); +} + static inline u32 unic_get_sq_cqe_mask(struct unic_dev *unic_dev) { return unic_dev->channels.sq_cqe_depth - 1; diff --git a/drivers/net/ub/unic/unic_ethtool.c b/drivers/net/ub/unic/unic_ethtool.c index 9e50f0332d4e..474626833d89 100644 --- a/drivers/net/ub/unic/unic_ethtool.c +++ b/drivers/net/ub/unic/unic_ethtool.c @@ -122,6 +122,8 @@ static const struct ethtool_ops unic_ethtool_ops = { .get_link = unic_get_link_status, .get_link_ksettings = unic_get_link_ksettings, .get_drvinfo = unic_get_driver_info, + .get_regs_len = unic_get_regs_len, + .get_regs = unic_get_regs, .get_channels = unic_get_channels, .set_channels = unic_set_channels, .get_ringparam = unic_get_channels_param, diff --git a/drivers/net/ub/unic/unic_stats.c b/drivers/net/ub/unic/unic_stats.c index 8167bb6a6fca..dcefeab1bb2d 100644 --- a/drivers/net/ub/unic/unic_stats.c +++ b/drivers/net/ub/unic/unic_stats.c @@ -14,6 +14,292 @@ #include "unic_netdev.h" #include "unic_stats.h" +static u32 cmdq_regs_addr[] = { + UNIC_TX_CMDQ_DEPTH, + UNIC_TX_CMDQ_TAIL, + UNIC_TX_CMDQ_HEAD, + UNIC_RX_CMDQ_DEPTH, + UNIC_RX_CMDQ_TAIL, + UNIC_RX_CMDQ_HEAD, + UNIC_CMDQ_INT_GEN, + UNIC_CMDQ_INT_SCR, + UNIC_CMDQ_INT_MASK, + UNIC_CMDQ_INT_STS, +}; + +static u32 ctrlq_regs_addr[] = { + UNIC_TX_CTRLQ_DEPTH, + UNIC_TX_CTRLQ_TAIL, + UNIC_TX_CTRLQ_HEAD, + UNIC_RX_CTRLQ_DEPTH, + UNIC_RX_CTRLQ_TAIL, + UNIC_RX_CTRLQ_HEAD, + UNIC_CTRLQ_INT_GEN, + UNIC_CTRLQ_INT_SCR, + UNIC_CTRLQ_INT_MASK, + UNIC_CTRLQ_INT_STS, +}; + +static const struct unic_res_regs_group unic_res_reg_arr[] = { + { + UNIC_TAG_CMDQ, cmdq_regs_addr, ARRAY_SIZE(cmdq_regs_addr), + NULL + }, { + UNIC_TAG_CTRLQ, ctrlq_regs_addr, ARRAY_SIZE(ctrlq_regs_addr), + ubase_adev_ctrlq_supported + }, +}; + +static bool unic_dfx_reg_support(struct unic_dev *unic_dev, u32 property) +{ + if (((property & UBASE_SUP_UBL) && unic_dev_ubl_supported(unic_dev)) || + ((property & UBASE_SUP_ETH) && unic_dev_eth_mac_supported(unic_dev))) + return true; + + return false; +} + +static struct unic_dfx_regs_group unic_dfx_reg_arr[] = { + { + UNIC_REG_NUM_IDX_TA, UNIC_TAG_TA, UBASE_OPC_DFX_TA_REG, + UBASE_SUP_UBL_ETH, unic_dfx_reg_support + }, { + UNIC_REG_NUM_IDX_TP, UNIC_TAG_TP, UBASE_OPC_DFX_TP_REG, + UBASE_SUP_UBL_ETH, unic_dfx_reg_support + }, { + UNIC_REG_NUM_IDX_BA, UNIC_TAG_BA, UBASE_OPC_DFX_BA_REG, + UBASE_SUP_UBL_ETH, unic_dfx_reg_support + }, { + UNIC_REG_NUM_IDX_NL, UNIC_TAG_NL, UBASE_OPC_DFX_NL_REG, + UBASE_SUP_UBL_ETH, unic_dfx_reg_support + }, { + UNIC_REG_NUM_IDX_DL, UNIC_TAG_DL, UBASE_OPC_DFX_DL_REG, + UBASE_SUP_UBL, unic_dfx_reg_support + }, +}; + +static int unic_get_dfx_reg_num(struct unic_dev *unic_dev, u32 *reg_num, + u32 reg_arr_size) +{ + struct ubase_cmd_buf in, out; + int ret; + + ubase_fill_inout_buf(&in, UBASE_OPC_DFX_REG_NUM, true, 0, NULL); + ubase_fill_inout_buf(&out, UBASE_OPC_DFX_REG_NUM, true, + reg_arr_size * sizeof(u32), reg_num); + ret = ubase_cmd_send_inout(unic_dev->comdev.adev, &in, &out); + if (ret && ret != -EPERM) + unic_err(unic_dev, + "failed to query dfx reg num, ret = %d.\n", ret); + + return ret; +} + +static int unic_get_res_regs_len(struct unic_dev *unic_dev, + const struct unic_res_regs_group *reg_arr, + u32 reg_arr_size) +{ + u32 i, count = 0; + + for (i = 0; i < reg_arr_size; i++) { + if (reg_arr[i].is_supported && + !reg_arr[i].is_supported(unic_dev->comdev.adev)) + continue; + + count += reg_arr[i].regs_count * sizeof(u32) + + sizeof(struct unic_tlv_hdr); + } + + return count; +} + +static int unic_get_dfx_regs_len(struct unic_dev *unic_dev, + struct unic_dfx_regs_group *reg_arr, + u32 reg_arr_size, u32 *reg_num) +{ + u32 i, count = 0; + + for (i = 0; i < reg_arr_size; i++) { + if (!reg_arr[i].is_supported(unic_dev, reg_arr[i].property)) + continue; + + count += sizeof(struct unic_tlv_hdr) + sizeof(u32) * + reg_num[reg_arr[i].regs_idx]; + } + + return count; +} + +int unic_get_regs_len(struct net_device *netdev) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + u32 reg_arr_size; + int count = 0; + u32 *reg_num; + int ret; + + if (unic_resetting(netdev)) + return -EBUSY; + + count += unic_get_res_regs_len(unic_dev, unic_res_reg_arr, + ARRAY_SIZE(unic_res_reg_arr)); + reg_arr_size = ARRAY_SIZE(unic_dfx_reg_arr); + reg_num = kcalloc(reg_arr_size, sizeof(u32), GFP_KERNEL); + if (!reg_num) + return -ENOMEM; + + ret = unic_get_dfx_reg_num(unic_dev, reg_num, reg_arr_size); + if (!ret) { + count += unic_get_dfx_regs_len(unic_dev, unic_dfx_reg_arr, + reg_arr_size, reg_num); + } else if (ret != -EPERM) { + unic_err(unic_dev, + "failed to get dfx regs length, ret = %d.\n", ret); + kfree(reg_num); + + return -EBUSY; + } + + kfree(reg_num); + + return count; +} + +static u16 unic_fetch_res_regs(struct unic_dev *unic_dev, u8 *data, u16 tag, + u32 *regs_addr_arr, u32 reg_num) +{ + struct unic_tlv_hdr *tlv = (struct unic_tlv_hdr *)data; + u32 *reg = (u32 *)(data + sizeof(struct unic_tlv_hdr)); + u32 i; + + tlv->tag = tag; + tlv->len = sizeof(struct unic_tlv_hdr) + reg_num * sizeof(u32); + + for (i = 0; i < reg_num; i++) + *reg++ = unic_read_reg(unic_dev, regs_addr_arr[i]); + + return tlv->len; +} + +static u32 unic_get_res_regs(struct unic_dev *unic_dev, u8 *data) +{ + u32 i, data_len = 0; + + for (i = 0; i < ARRAY_SIZE(unic_res_reg_arr); i++) { + if (unic_res_reg_arr[i].is_supported && + !unic_res_reg_arr[i].is_supported(unic_dev->comdev.adev)) + continue; + + data_len += unic_fetch_res_regs(unic_dev, data + data_len, + unic_res_reg_arr[i].tag, + unic_res_reg_arr[i].regs_addr, + unic_res_reg_arr[i].regs_count); + } + + return data_len; +} + +static int unic_query_regs_data(struct unic_dev *unic_dev, u8 *data, + u32 reg_num, u16 opcode) +{ + u32 *reg = (u32 *)(data + sizeof(struct unic_tlv_hdr)); + struct ubase_cmd_buf in, out; + u32 *out_regs; + int ret; + u32 i; + + out_regs = kcalloc(reg_num, sizeof(u32), GFP_KERNEL); + if (!out_regs) + return -ENOMEM; + + ubase_fill_inout_buf(&in, opcode, true, 0, NULL); + ubase_fill_inout_buf(&out, opcode, true, reg_num * sizeof(u32), + out_regs); + ret = ubase_cmd_send_inout(unic_dev->comdev.adev, &in, &out); + if (ret) { + unic_err(unic_dev, + "failed to send getting reg cmd(0x%x), ret = %d.\n", + opcode, ret); + goto err_send_cmd; + } + + for (i = 0; i < reg_num; i++) + *reg++ = le32_to_cpu(*(out_regs + i)); + +err_send_cmd: + kfree(out_regs); + + return ret; +} + +static int unic_get_dfx_regs(struct unic_dev *unic_dev, u8 *data, + struct unic_dfx_regs_group *reg_arr, + u32 reg_arr_size, u32 *reg_num) +{ + struct unic_tlv_hdr *tlv; + u16 idx; + int ret; + u32 i; + + for (i = 0; i < reg_arr_size; i++) { + if (!reg_arr[i].is_supported(unic_dev, reg_arr[i].property)) + continue; + + idx = reg_arr[i].regs_idx; + ret = unic_query_regs_data(unic_dev, data, reg_num[idx], + reg_arr[i].opcode); + if (ret) { + unic_err(unic_dev, + "failed to query dfx regs, ret = %d.\n", ret); + return ret; + } + + tlv = (struct unic_tlv_hdr *)data; + tlv->tag = reg_arr[i].tag; + tlv->len = sizeof(*tlv) + reg_num[idx] * sizeof(u32); + data += tlv->len; + } + + return 0; +} + +void unic_get_regs(struct net_device *netdev, struct ethtool_regs *cmd, + void *data) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + u8 *pdata = (u8 *)data; + u32 reg_arr_size; + u32 *reg_num; + int ret; + + if (unic_resetting(netdev)) { + unic_err(unic_dev, "dev resetting, could not get regs.\n"); + return; + } + + reg_arr_size = ARRAY_SIZE(unic_dfx_reg_arr); + reg_num = kcalloc(reg_arr_size, sizeof(u32), GFP_KERNEL); + if (!reg_num) { + unic_err(unic_dev, "failed to alloc reg num array.\n"); + return; + } + + pdata += unic_get_res_regs(unic_dev, pdata); + ret = unic_get_dfx_reg_num(unic_dev, reg_num, reg_arr_size); + if (!ret) { + ret = unic_get_dfx_regs(unic_dev, pdata, unic_dfx_reg_arr, + reg_arr_size, reg_num); + if (ret) + unic_err(unic_dev, + "failed to get dfx regs, ret = %d.\n", ret); + } else if (ret != -EPERM) { + unic_err(unic_dev, + "failed to get dfx reg num, ret = %d.\n", ret); + } + + kfree(reg_num); +} + static void unic_get_fec_stats_total(struct unic_dev *unic_dev, u8 stats_flags, struct ethtool_fec_stats *fec_stats) { diff --git a/drivers/net/ub/unic/unic_stats.h b/drivers/net/ub/unic/unic_stats.h index d9d9f0b0ee3b..2a2a8746d838 100644 --- a/drivers/net/ub/unic/unic_stats.h +++ b/drivers/net/ub/unic/unic_stats.h @@ -16,6 +16,76 @@ #define UNIC_FEC_UNCORR_BLOCKS BIT(1) #define UNIC_FEC_CORR_BITS BIT(2) +#define UNIC_TX_CMDQ_DEPTH UBASE_CSQ_DEPTH_REG +#define UNIC_TX_CMDQ_TAIL UBASE_CSQ_TAIL_REG +#define UNIC_TX_CMDQ_HEAD UBASE_CSQ_HEAD_REG +#define UNIC_RX_CMDQ_DEPTH UBASE_CRQ_DEPTH_REG +#define UNIC_RX_CMDQ_TAIL UBASE_CRQ_TAIL_REG +#define UNIC_RX_CMDQ_HEAD UBASE_CRQ_HEAD_REG +#define UNIC_CMDQ_INT_GEN 0x18000 +#define UNIC_CMDQ_INT_SCR 0x18004 +#define UNIC_CMDQ_INT_MASK 0x18008 +#define UNIC_CMDQ_INT_STS 0x1800c + +#define UNIC_TX_CTRLQ_DEPTH UBASE_CTRLQ_CSQ_DEPTH_REG +#define UNIC_TX_CTRLQ_TAIL UBASE_CTRLQ_CSQ_TAIL_REG +#define UNIC_TX_CTRLQ_HEAD UBASE_CTRLQ_CSQ_HEAD_REG +#define UNIC_RX_CTRLQ_DEPTH UBASE_CTRLQ_CRQ_DEPTH_REG +#define UNIC_RX_CTRLQ_TAIL UBASE_CTRLQ_CRQ_TAIL_REG +#define UNIC_RX_CTRLQ_HEAD UBASE_CTRLQ_CRQ_HEAD_REG +#define UNIC_CTRLQ_INT_GEN 0x18010 +#define UNIC_CTRLQ_INT_SCR 0x18014 +#define UNIC_CTRLQ_INT_MASK 0x18018 +#define UNIC_CTRLQ_INT_STS 0x1801c + +enum unic_reg_num_idx { + UNIC_REG_NUM_IDX_DL = 0, + UNIC_REG_NUM_IDX_NL, + UNIC_REG_NUM_IDX_BA, + UNIC_REG_NUM_IDX_TP, + UNIC_REG_NUM_IDX_TA, + UNIC_REG_NUM_IDX_MAX, +}; + +enum unic_reg_tag { + UNIC_TAG_CMDQ = 0, + UNIC_TAG_CTRLQ, + UNIC_TAG_DL, + UNIC_TAG_NL, + UNIC_TAG_BA, + UNIC_TAG_TP, + UNIC_TAG_TA, + UNIC_TAG_MAX, +}; + +struct unic_res_regs_group { + u16 tag; + u32 *regs_addr; + u32 regs_count; + bool (*is_supported)(struct auxiliary_device *adev); +}; + +struct unic_dump_reg_hdr { + u8 flag; + u8 rsv[3]; +}; + +struct unic_tlv_hdr { + u16 tag; + u16 len; +}; + +struct unic_dfx_regs_group { + u16 regs_idx; + u16 tag; + u16 opcode; + u32 property; + bool (*is_supported)(struct unic_dev *unic_dev, u32 property); +}; + +int unic_get_regs_len(struct net_device *netdev); +void unic_get_regs(struct net_device *netdev, struct ethtool_regs *cmd, + void *data); void unic_get_fec_stats(struct net_device *ndev, struct ethtool_fec_stats *fec_stats); diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index 311a309d10bd..4eb3c435a8f9 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -36,6 +36,12 @@ enum ubase_opcode_type { UBASE_OPC_CFG_MTU = 0x0033, UBASE_OPC_QUERY_NET_GUID = 0x0035, UBASE_OPC_STATS_MAC_ALL = 0x0038, + UBASE_OPC_DFX_REG_NUM = 0x0039, + UBASE_OPC_DFX_DL_REG = 0x0040, + UBASE_OPC_DFX_NL_REG = 0x0042, + UBASE_OPC_DFX_BA_REG = 0x0043, + UBASE_OPC_DFX_TP_REG = 0x0044, + UBASE_OPC_DFX_TA_REG = 0x0045, UBASE_OPC_QUERY_BUS_EID = 0x0047, UBASE_OPC_QUERY_UBCL_CONFIG = 0x0050, -- Gitee From 3b3c4a8cd7827c5de32916911c754fa444eb79b1 Mon Sep 17 00:00:00 2001 From: Junxin Chen Date: Sat, 30 Aug 2025 15:20:46 +0800 Subject: [PATCH 192/214] net: unic: Add debugfs for JFS/JFR/JFC context. ANBZ: #45460 commit 615725081a2520ed66c0265c65964fd718f563dd openEuler. This patch adds debugfs support for JFS, JFR, and JFC contexts in the UNIC driver. The new debugfs interface allows users to dump and inspect the software and hardware configurations of these contexts, including their states, registers, and related parameters. The implementation includes: 1. New debugfs files (unic_ctx_debugfs.c and unic_ctx_debugfs.h) to handle context dumping. 2. Extended debugfs commands to support JFS, JFR, and JFC contexts in both software and hardware modes. 3. Integration with the existing debugfs framework to expose these new debug features. This enhancement improves the debugging and troubleshooting capabilities for the UNIC driver by providing detailed visibility into the internal states of JFS, JFR, and JFC contexts. Signed-off-by: Peng Li Signed-off-by: Xiongchuan Zhou Signed-off-by: Haibin Lu Signed-off-by: Junxin Chen Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/Makefile | 1 + .../net/ub/unic/debugfs/unic_ctx_debugfs.c | 365 ++++++++++++++++++ .../net/ub/unic/debugfs/unic_ctx_debugfs.h | 19 + drivers/net/ub/unic/debugfs/unic_debugfs.c | 62 +++ drivers/net/ub/unic/debugfs/unic_debugfs.h | 1 + 5 files changed, 448 insertions(+) create mode 100644 drivers/net/ub/unic/debugfs/unic_ctx_debugfs.c create mode 100644 drivers/net/ub/unic/debugfs/unic_ctx_debugfs.h diff --git a/drivers/net/ub/unic/Makefile b/drivers/net/ub/unic/Makefile index 1419ee569595..67ecd0ad8c11 100644 --- a/drivers/net/ub/unic/Makefile +++ b/drivers/net/ub/unic/Makefile @@ -9,4 +9,5 @@ ccflags-y += -I$(srctree)/drivers/net/ub/unic/debugfs obj-$(CONFIG_UB_UNIC) += unic.o unic-objs = unic_main.o unic_ethtool.o unic_hw.o unic_guid.o unic_netdev.o unic_dev.o unic_qos_hw.o unic_event.o unic_crq.o unic-objs += unic_channel.o debugfs/unic_debugfs.o unic_rx.o unic_tx.o unic_txrx.o unic_comm_addr.o unic_rack_ip.o unic_stats.o +unic-objs += debugfs/unic_ctx_debugfs.o unic-$(CONFIG_UB_UNIC_DCB) += unic_dcbnl.o diff --git a/drivers/net/ub/unic/debugfs/unic_ctx_debugfs.c b/drivers/net/ub/unic/debugfs/unic_ctx_debugfs.c new file mode 100644 index 000000000000..fc0d19fee038 --- /dev/null +++ b/drivers/net/ub/unic/debugfs/unic_ctx_debugfs.c @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include +#include +#include +#include + +#include "unic_ctx_debugfs.h" +#include "unic_debugfs.h" +#include "unic_dev.h" + +static inline void unic_jfs_ctx_titles_print(struct seq_file *s) +{ + seq_puts(s, "SQ_ID SQE_BB_SHIFT STATE JFS_MODE TX_JFCN\n"); +} + +static void unic_dump_jfs_ctx_info_sw(struct unic_sq *sq, struct seq_file *s, + u32 index) +{ + struct unic_jfs_ctx *ctx = &sq->jfs_ctx; + + seq_printf(s, "%-7u", index); + seq_printf(s, "%-14u", ctx->sqe_bb_shift); + seq_printf(s, "%-7u", ctx->state); + seq_printf(s, "%-10u", ctx->jfs_mode); + seq_printf(s, "%-9u\n", ctx->tx_jfcn); +} + +static inline void unic_jfr_ctx_titles_print(struct seq_file *s) +{ + seq_puts(s, "RQ_ID STATE RQE_SHIFT RX_JFCN PI CI"); + seq_puts(s, "RECORD_DB_EN\n"); +} + +static void unic_dump_jfr_ctx_info_sw(struct unic_rq *rq, struct seq_file *s, + u32 index) +{ + struct unic_jfr_ctx *ctx = &rq->jfr_ctx; + u32 jfcn; + + jfcn = ctx->jfcn_l | (ctx->jfcn_h << UNIC_JFR_JFCN_H_OFFSET); + + seq_printf(s, "%-7u", index); + seq_printf(s, "%-7u", ctx->state); + seq_printf(s, "%-11u", ctx->rqe_shift); + seq_printf(s, "%-9u", jfcn); + seq_printf(s, "%-7u", ctx->pi); + seq_printf(s, "%-7u", ctx->ci); + seq_printf(s, "%-14u\n", ctx->record_db_en); +} + +static inline void unic_jfc_ctx_titles_print(struct seq_file *s) +{ + seq_puts(s, "CQ_ID ARM_ST STATE INLINE_EN SHIFT CQE_COAL_CNT"); + seq_puts(s, "CEQN RECORD_DB_EN CQE_COAL_PEIRIOD\n"); +} + +static void unic_dump_jfc_ctx_info_sw(struct unic_cq *cq, struct seq_file *s, + u32 index) +{ + struct unic_jfc_ctx *ctx = &cq->jfc_ctx; + + seq_printf(s, "%-7u", index); + seq_printf(s, "%-8u", ctx->arm_st); + seq_printf(s, "%-7u", ctx->state); + seq_printf(s, "%-11u", ctx->inline_en); + seq_printf(s, "%-7u", ctx->shift); + seq_printf(s, "%-14u", ctx->cqe_coalesce_cnt); + seq_printf(s, "%-6u", ctx->ceqn); + seq_printf(s, "%-14u", ctx->record_db_en); + seq_printf(s, "%-18u\n", ctx->cqe_coalesce_period); +} + +static void unic_get_jfs_ctx_sw(struct unic_channels *channels, + struct seq_file *s, u32 index) +{ + struct unic_channel *channel = &channels->c[index]; + + unic_dump_jfs_ctx_info_sw(channel->sq, s, index); +} + +static void unic_get_jfr_ctx_sw(struct unic_channels *channels, + struct seq_file *s, u32 index) +{ + struct unic_channel *channel = &channels->c[index]; + + unic_dump_jfr_ctx_info_sw(channel->rq, s, index); +} + +static void unic_get_sq_jfc_ctx_sw(struct unic_channels *channels, + struct seq_file *s, u32 index) +{ + struct unic_channel *channel = &channels->c[index]; + + unic_dump_jfc_ctx_info_sw(channel->sq->cq, s, index); +} + +static void unic_get_rq_jfc_ctx_sw(struct unic_channels *channels, + struct seq_file *s, u32 index) +{ + struct unic_channel *channel = &channels->c[index]; + + unic_dump_jfc_ctx_info_sw(channel->rq->cq, s, index); +} + +enum unic_dbg_ctx_type { + UNIC_DBG_JFS_CTX = 0, + UNIC_DBG_JFR_CTX, + UNIC_DBG_SQ_JFC_CTX, + UNIC_DBG_RQ_JFC_CTX, +}; + +static int unic_dbg_dump_ctx_sw(struct seq_file *s, void *data, + enum unic_dbg_ctx_type ctx_type) +{ + struct unic_dbg_context { + void (*print_ctx_titles)(struct seq_file *s); + void (*get_ctx)(struct unic_channels *channels, struct seq_file *s, u32 index); + } dbg_ctx[] = { + {unic_jfs_ctx_titles_print, unic_get_jfs_ctx_sw}, + {unic_jfr_ctx_titles_print, unic_get_jfr_ctx_sw}, + {unic_jfc_ctx_titles_print, unic_get_sq_jfc_ctx_sw}, + {unic_jfc_ctx_titles_print, unic_get_rq_jfc_ctx_sw}, + }; + struct unic_dev *unic_dev = dev_get_drvdata(s->private); + int ret = 0; + u32 i; + + dbg_ctx[ctx_type].print_ctx_titles(s); + + if (!mutex_trylock(&unic_dev->channels.mutex)) + return -EBUSY; + + if (__unic_resetting(unic_dev) || !unic_dev->channels.c) { + ret = -EBUSY; + goto out; + } + + for (i = 0; i < unic_dev->channels.num; i++) + dbg_ctx[ctx_type].get_ctx(&unic_dev->channels, s, i); + +out: + mutex_unlock(&unic_dev->channels.mutex); + + return ret; +} + +int unic_dbg_dump_jfs_ctx_sw(struct seq_file *s, void *data) +{ + return unic_dbg_dump_ctx_sw(s, data, UNIC_DBG_JFS_CTX); +} + +int unic_dbg_dump_jfr_ctx_sw(struct seq_file *s, void *data) +{ + return unic_dbg_dump_ctx_sw(s, data, UNIC_DBG_JFR_CTX); +} + +int unic_dbg_dump_rq_jfc_ctx_sw(struct seq_file *s, void *data) +{ + return unic_dbg_dump_ctx_sw(s, data, UNIC_DBG_RQ_JFC_CTX); +} + +int unic_dbg_dump_sq_jfc_ctx_sw(struct seq_file *s, void *data) +{ + return unic_dbg_dump_ctx_sw(s, data, UNIC_DBG_SQ_JFC_CTX); +} + +struct unic_ctx_info { + u32 start_idx; + u32 ctx_size; + u8 op; + const char *ctx_name; +}; + +static int unic_get_ctx_info(struct unic_dev *unic_dev, + enum unic_dbg_ctx_type ctx_type, + struct unic_ctx_info *ctx_info) +{ + struct ubase_adev_caps *unic_caps = ubase_get_unic_caps(unic_dev->comdev.adev); + + if (!unic_caps) { + unic_err(unic_dev, "failed to get unic caps.\n"); + return -ENODATA; + } + + switch (ctx_type) { + case UNIC_DBG_JFS_CTX: + ctx_info->start_idx = unic_caps->jfs.start_idx; + ctx_info->ctx_size = UBASE_JFS_CTX_SIZE; + ctx_info->op = UBASE_MB_QUERY_JFS_CONTEXT; + ctx_info->ctx_name = "jfs"; + break; + case UNIC_DBG_JFR_CTX: + ctx_info->start_idx = unic_caps->jfr.start_idx; + ctx_info->ctx_size = UBASE_JFR_CTX_SIZE; + ctx_info->op = UBASE_MB_QUERY_JFR_CONTEXT; + ctx_info->ctx_name = "jfr"; + break; + case UNIC_DBG_SQ_JFC_CTX: + ctx_info->start_idx = unic_caps->jfc.start_idx; + ctx_info->ctx_size = UBASE_JFC_CTX_SIZE; + ctx_info->op = UBASE_MB_QUERY_JFC_CONTEXT; + ctx_info->ctx_name = "sq_jfc"; + break; + case UNIC_DBG_RQ_JFC_CTX: + ctx_info->start_idx = unic_caps->jfc.start_idx + + unic_dev->channels.num; + ctx_info->ctx_size = UBASE_JFC_CTX_SIZE; + ctx_info->op = UBASE_MB_QUERY_JFC_CONTEXT; + ctx_info->ctx_name = "rq_jfc"; + break; + default: + unic_err(unic_dev, "failed to get ctx info, ctx_type = %u.\n", + ctx_type); + return -ENODATA; + } + + return 0; +} + +static void unic_mask_jfs_ctx_key_words(void *buf) +{ + struct unic_jfs_ctx *jfs = (struct unic_jfs_ctx *)buf; + + jfs->sqe_token_id_l = 0; + jfs->sqe_token_id_h = 0; + jfs->sqe_base_addr_l = 0; + jfs->sqe_base_addr_h = 0; + jfs->sqe_pld_tokenid = 0; + jfs->rmt_tokenid = 0; + jfs->user_data_l = 0; + jfs->user_data_h = 0; +} + +static void unic_mask_jfr_ctx_key_words(void *buf) +{ + struct unic_jfr_ctx *jfr = (struct unic_jfr_ctx *)buf; + + jfr->rqe_token_id_l = 0; + jfr->rqe_token_id_h = 0; + jfr->rqe_base_addr_l = 0; + jfr->rqe_base_addr_h = 0; + jfr->pld_token_id = 0; + jfr->token_value = 0; + jfr->user_data_l = 0; + jfr->user_data_h = 0; + jfr->idx_que_addr_l = 0; + jfr->idx_que_addr_h = 0; + jfr->record_db_addr_l = 0; + jfr->record_db_addr_m = 0; + jfr->record_db_addr_h = 0; +} + +static void unic_mask_jfc_ctx_key_words(void *buf) +{ + struct unic_jfc_ctx *jfc = (struct unic_jfc_ctx *)buf; + + jfc->cqe_base_addr_l = 0; + jfc->cqe_base_addr_h = 0; + jfc->queue_token_id = 0; + jfc->record_db_addr_l = 0; + jfc->record_db_addr_h = 0; + jfc->rmt_token_id = 0; + jfc->remote_token_value = 0; +} + +static void unic_mask_ctx_key_words(void *buf, + enum unic_dbg_ctx_type ctx_type) +{ + switch (ctx_type) { + case UNIC_DBG_JFS_CTX: + unic_mask_jfs_ctx_key_words(buf); + break; + case UNIC_DBG_JFR_CTX: + unic_mask_jfr_ctx_key_words(buf); + break; + case UNIC_DBG_SQ_JFC_CTX: + case UNIC_DBG_RQ_JFC_CTX: + unic_mask_jfc_ctx_key_words(buf); + break; + default: + break; + } +} + +static int unic_dbg_dump_context_hw(struct seq_file *s, void *data, + enum unic_dbg_ctx_type ctx_type) +{ + struct unic_dev *unic_dev = dev_get_drvdata(s->private); + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct unic_ctx_info ctx_info = {0}; + struct ubase_cmd_mailbox *mailbox; + struct ubase_mbx_attr attr = {0}; + int ret = 0; + u32 i; + + if (!mutex_trylock(&unic_dev->channels.mutex)) + return -EBUSY; + + if (__unic_resetting(unic_dev) || + !unic_dev->channels.c) { + ret = -EBUSY; + goto channel_ready_err; + } + + mailbox = ubase_alloc_cmd_mailbox(adev); + if (IS_ERR_OR_NULL(mailbox)) { + unic_err(unic_dev, "failed to alloc mailbox for dump context.\n"); + ret = -ENOMEM; + goto channel_ready_err; + } + + ret = unic_get_ctx_info(unic_dev, ctx_type, &ctx_info); + if (ret) + goto upgrade_ctx_err; + + for (i = 0; i < unic_dev->channels.num; i++) { + ubase_fill_mbx_attr(&attr, i + ctx_info.start_idx, ctx_info.op, + 0); + ret = ubase_hw_upgrade_ctx_ex(adev, &attr, mailbox); + if (ret) { + unic_err(unic_dev, + "failed to post query %s ctx mbx, ret = %d.\n", + ctx_info.ctx_name, ret); + goto upgrade_ctx_err; + } + + seq_printf(s, "offset\t%s", ctx_info.ctx_name); + seq_printf(s, "%u\n", i); + unic_mask_ctx_key_words(mailbox->buf, ctx_type); + ubase_print_context_hw(s, mailbox->buf, ctx_info.ctx_size); + seq_puts(s, "\n"); + } + +upgrade_ctx_err: + ubase_free_cmd_mailbox(adev, mailbox); +channel_ready_err: + mutex_unlock(&unic_dev->channels.mutex); + + return ret; +} + +int unic_dbg_dump_jfs_context_hw(struct seq_file *s, void *data) +{ + return unic_dbg_dump_context_hw(s, data, UNIC_DBG_JFS_CTX); +} + +int unic_dbg_dump_jfr_context_hw(struct seq_file *s, void *data) +{ + return unic_dbg_dump_context_hw(s, data, UNIC_DBG_JFR_CTX); +} + +int unic_dbg_dump_sq_jfc_context_hw(struct seq_file *s, void *data) +{ + return unic_dbg_dump_context_hw(s, data, UNIC_DBG_SQ_JFC_CTX); +} + +int unic_dbg_dump_rq_jfc_context_hw(struct seq_file *s, void *data) +{ + return unic_dbg_dump_context_hw(s, data, UNIC_DBG_RQ_JFC_CTX); +} diff --git a/drivers/net/ub/unic/debugfs/unic_ctx_debugfs.h b/drivers/net/ub/unic/debugfs/unic_ctx_debugfs.h new file mode 100644 index 000000000000..cb6b08c80d8e --- /dev/null +++ b/drivers/net/ub/unic/debugfs/unic_ctx_debugfs.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_CTX_DEBUGFS_H__ +#define __UNIC_CTX_DEBUGFS_H__ + +int unic_dbg_dump_jfs_ctx_sw(struct seq_file *s, void *data); +int unic_dbg_dump_jfr_ctx_sw(struct seq_file *s, void *data); +int unic_dbg_dump_sq_jfc_ctx_sw(struct seq_file *s, void *data); +int unic_dbg_dump_rq_jfc_ctx_sw(struct seq_file *s, void *data); +int unic_dbg_dump_jfs_context_hw(struct seq_file *s, void *data); +int unic_dbg_dump_jfr_context_hw(struct seq_file *s, void *data); +int unic_dbg_dump_sq_jfc_context_hw(struct seq_file *s, void *data); +int unic_dbg_dump_rq_jfc_context_hw(struct seq_file *s, void *data); + +#endif diff --git a/drivers/net/ub/unic/debugfs/unic_debugfs.c b/drivers/net/ub/unic/debugfs/unic_debugfs.c index 112e64c25a34..29fc8917eaa2 100644 --- a/drivers/net/ub/unic/debugfs/unic_debugfs.c +++ b/drivers/net/ub/unic/debugfs/unic_debugfs.c @@ -11,6 +11,7 @@ #include #include "unic_dev.h" +#include "unic_ctx_debugfs.h" #include "unic_hw.h" #include "unic_debugfs.h" @@ -198,6 +199,11 @@ static bool unic_dbg_dentry_support(struct device *dev, u32 property) } static struct ubase_dbg_dentry_info unic_dbg_dentry[] = { + { + .name = "context", + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + }, /* keep unic at the bottom and add new directory above */ { .name = "unic", @@ -208,6 +214,34 @@ static struct ubase_dbg_dentry_info unic_dbg_dentry[] = { static struct ubase_dbg_cmd_info unic_dbg_cmd[] = { { + .name = "jfs_context", + .dentry_index = UNIC_DBG_DENTRY_CONTEXT, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_jfs_ctx_sw, + }, { + .name = "jfr_context", + .dentry_index = UNIC_DBG_DENTRY_CONTEXT, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_jfr_ctx_sw, + }, { + .name = "sq_jfc_context", + .dentry_index = UNIC_DBG_DENTRY_CONTEXT, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_sq_jfc_ctx_sw, + }, { + .name = "rq_jfc_context", + .dentry_index = UNIC_DBG_DENTRY_CONTEXT, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_rq_jfc_ctx_sw, + }, { .name = "dev_info", .dentry_index = UNIC_DBG_DENTRY_ROOT, .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, @@ -228,6 +262,34 @@ static struct ubase_dbg_cmd_info unic_dbg_cmd[] = { .support = unic_dbg_dentry_support, .init = ubase_dbg_seq_file_init, .read_func = unic_dbg_dump_page_pool_info, + }, { + .name = "jfs_context_hw", + .dentry_index = UNIC_DBG_DENTRY_CONTEXT, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_jfs_context_hw, + }, { + .name = "jfr_context_hw", + .dentry_index = UNIC_DBG_DENTRY_CONTEXT, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_jfr_context_hw, + }, { + .name = "sq_jfc_context_hw", + .dentry_index = UNIC_DBG_DENTRY_CONTEXT, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_sq_jfc_context_hw, + }, { + .name = "rq_jfc_context_hw", + .dentry_index = UNIC_DBG_DENTRY_CONTEXT, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_rq_jfc_context_hw, }, { .name = "rss_cfg_hw", .dentry_index = UNIC_DBG_DENTRY_ROOT, diff --git a/drivers/net/ub/unic/debugfs/unic_debugfs.h b/drivers/net/ub/unic/debugfs/unic_debugfs.h index 6efd739d2c5b..1afba40382d6 100644 --- a/drivers/net/ub/unic/debugfs/unic_debugfs.h +++ b/drivers/net/ub/unic/debugfs/unic_debugfs.h @@ -13,6 +13,7 @@ #define unic_get_ubase_root_dentry(adev) ubase_diag_debugfs_root(adev) enum unic_dbg_dentry_type { + UNIC_DBG_DENTRY_CONTEXT, /* must be the last entry. */ UNIC_DBG_DENTRY_ROOT }; -- Gitee From 62d437c9906b1e282363e686001e8754389f1dd1 Mon Sep 17 00:00:00 2001 From: Haibin Lu Date: Thu, 21 Aug 2025 21:50:58 +0800 Subject: [PATCH 193/214] net: unic: support querying and configuring coalesce parameters. ANBZ: #45460 commit e0ccc63cc72ec634fffe4107cd60e620c462ad0e openEuler. Coalesce parameters determine the interrupt reporting frequency, which can be adjusted in different scenarios to achieve service objectives. To adapt to the ub protocol, This patch adds the capability of querying and configuring coalesce parameters for UNIC driver. Users can use the ethtool to query or configure coalesce parameters. Signed-off-by: Fengyan Mu Signed-off-by: Haibin Lu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/unic_dev.c | 24 ++++ drivers/net/ub/unic/unic_dev.h | 1 + drivers/net/ub/unic/unic_ethtool.c | 171 +++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+) diff --git a/drivers/net/ub/unic/unic_dev.c b/drivers/net/ub/unic/unic_dev.c index a0ea8edee2ef..f039151844c2 100644 --- a/drivers/net/ub/unic/unic_dev.c +++ b/drivers/net/ub/unic/unic_dev.c @@ -293,6 +293,30 @@ static void unic_uninit_channels_attr(struct unic_dev *unic_dev) mutex_destroy(&channels->mutex); } +u16 unic_cqe_period_round_down(u16 cqe_period) +{ + u16 period[] = { + UNIC_CQE_PERIOD_0, + UNIC_CQE_PERIOD_4, + UNIC_CQE_PERIOD_16, + UNIC_CQE_PERIOD_64, + UNIC_CQE_PERIOD_256, + UNIC_CQE_PERIOD_1024, + UNIC_CQE_PERIOD_4096, + UNIC_CQE_PERIOD_16384, + UNIC_CQE_PERIOD_ERR + }; + u16 i; + + for (i = 0; i < ARRAY_SIZE(period) - 1; i++) { + if (cqe_period >= period[i] && + cqe_period < period[i + 1]) + return period[i]; + } + + return UNIC_CQE_PERIOD_ERR; +} + int unic_init_tx(struct unic_dev *unic_dev, u32 num) { struct unic_channel *c; diff --git a/drivers/net/ub/unic/unic_dev.h b/drivers/net/ub/unic/unic_dev.h index 8654aa9a819e..f014164c8e31 100644 --- a/drivers/net/ub/unic/unic_dev.h +++ b/drivers/net/ub/unic/unic_dev.h @@ -247,6 +247,7 @@ void unic_start_period_task(struct net_device *netdev); void unic_remove_period_task(struct unic_dev *unic_dev); int unic_init_wq(void); void unic_destroy_wq(void); +u16 unic_cqe_period_round_down(u16 cqe_period); int unic_init_rx(struct unic_dev *unic_dev, u32 num); int unic_init_tx(struct unic_dev *unic_dev, u32 num); void unic_destroy_rx(struct unic_dev *unic_dev, u32 num); diff --git a/drivers/net/ub/unic/unic_ethtool.c b/drivers/net/ub/unic/unic_ethtool.c index 474626833d89..eb90121ad6fb 100644 --- a/drivers/net/ub/unic/unic_ethtool.c +++ b/drivers/net/ub/unic/unic_ethtool.c @@ -109,6 +109,175 @@ static int unic_set_fecparam(struct net_device *ndev, return 0; } +static int unic_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *cmd, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + struct unic_coalesce *tx_coal = &unic_dev->channels.unic_coal.tx_coal; + struct unic_coalesce *rx_coal = &unic_dev->channels.unic_coal.rx_coal; + + if (unic_resetting(netdev)) + return -EBUSY; + + cmd->tx_coalesce_usecs = tx_coal->int_gl; + cmd->rx_coalesce_usecs = rx_coal->int_gl; + + cmd->tx_max_coalesced_frames = tx_coal->int_ql; + cmd->rx_max_coalesced_frames = rx_coal->int_ql; + + return 0; +} + +static int unic_check_gl_coalesce_para(struct net_device *netdev, + struct ethtool_coalesce *cmd) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + u32 rx_gl, tx_gl; + + if (cmd->rx_coalesce_usecs > unic_dev->caps.max_int_gl) { + unic_err(unic_dev, + "invalid rx-usecs value, rx-usecs range is [0, %u].\n", + unic_dev->caps.max_int_gl); + return -EINVAL; + } + + if (cmd->tx_coalesce_usecs > unic_dev->caps.max_int_gl) { + unic_err(unic_dev, + "invalid tx-usecs value, tx-usecs range is [0, %u].\n", + unic_dev->caps.max_int_gl); + return -EINVAL; + } + + rx_gl = unic_cqe_period_round_down(cmd->rx_coalesce_usecs); + if (rx_gl != cmd->rx_coalesce_usecs) { + unic_err(unic_dev, + "invalid rx_usecs(%u), because it must be power of 4.\n", + cmd->rx_coalesce_usecs); + return -EINVAL; + } + + tx_gl = unic_cqe_period_round_down(cmd->tx_coalesce_usecs); + if (tx_gl != cmd->tx_coalesce_usecs) { + unic_err(unic_dev, + "invalid tx_usecs(%u), because it must be power of 4.\n", + cmd->tx_coalesce_usecs); + return -EINVAL; + } + + return 0; +} + +static int unic_check_ql_coalesce_para(struct net_device *netdev, + struct ethtool_coalesce *cmd) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + + if ((cmd->tx_max_coalesced_frames || cmd->rx_max_coalesced_frames) && + !unic_dev->caps.max_int_ql) { + unic_err(unic_dev, "coalesced frames is not supported.\n"); + return -EOPNOTSUPP; + } + + if (cmd->tx_max_coalesced_frames > unic_dev->caps.max_int_ql || + cmd->rx_max_coalesced_frames > unic_dev->caps.max_int_ql) { + unic_err(unic_dev, + "invalid coalesced frames value, range is [0, %u].\n", + unic_dev->caps.max_int_ql); + return -ERANGE; + } + + return 0; +} + +static int +unic_check_coalesce_para(struct net_device *netdev, + struct ethtool_coalesce *cmd, + struct kernel_ethtool_coalesce *kernel_coal) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + int ret; + + if (cmd->use_adaptive_rx_coalesce || cmd->use_adaptive_tx_coalesce) { + unic_err(unic_dev, + "not support to enable adaptive coalesce.\n"); + return -EINVAL; + } + + ret = unic_check_gl_coalesce_para(netdev, cmd); + if (ret) { + unic_err(unic_dev, + "failed to check gl coalesce param, ret = %d.\n", ret); + return ret; + } + + ret = unic_check_ql_coalesce_para(netdev, cmd); + if (ret) + unic_err(unic_dev, + "failed to check ql coalesce param, ret = %d.\n", ret); + + return ret; +} + +static int unic_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *cmd, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + struct unic_coal_txrx *unic_coal = &unic_dev->channels.unic_coal; + struct unic_coalesce *tx_coal = &unic_coal->tx_coal; + struct unic_coalesce *rx_coal = &unic_coal->rx_coal; + struct unic_coalesce old_tx_coal, old_rx_coal; + int ret, ret1; + + if (test_bit(UNIC_STATE_DEACTIVATE, &unic_dev->state)) { + unic_err(unic_dev, + "failed to set coalesce, due to dev deacitve.\n"); + return -EBUSY; + } + + if (unic_resetting(netdev)) + return -EBUSY; + + ret = unic_check_coalesce_para(netdev, cmd, kernel_coal); + if (ret) + return ret; + + memcpy(&old_tx_coal, tx_coal, sizeof(struct unic_coalesce)); + memcpy(&old_rx_coal, rx_coal, sizeof(struct unic_coalesce)); + + tx_coal->int_gl = cmd->tx_coalesce_usecs; + rx_coal->int_gl = cmd->rx_coalesce_usecs; + + tx_coal->int_ql = cmd->tx_max_coalesced_frames; + rx_coal->int_ql = cmd->rx_max_coalesced_frames; + + unic_net_stop_no_link_change(netdev); + unic_uninit_channels(unic_dev); + + ret = unic_init_channels(unic_dev, unic_dev->channels.num); + if (ret) { + netdev_err(netdev, "failed to init channels, ret = %d.\n", ret); + memcpy(tx_coal, &old_tx_coal, sizeof(struct unic_coalesce)); + memcpy(rx_coal, &old_rx_coal, sizeof(struct unic_coalesce)); + ret1 = unic_init_channels(unic_dev, unic_dev->channels.num); + if (ret1) { + unic_err(unic_dev, + "failed to recover old channels, ret = %d.\n", + ret1); + return ret; + } + } + + ret1 = unic_net_open_no_link_change(netdev); + if (ret1) + unic_err(unic_dev, "failed to set net open, ret = %d.\n", ret1); + + return ret; +} + #define UNIC_ETHTOOL_RING (ETHTOOL_RING_USE_RX_BUF_LEN | \ ETHTOOL_RING_USE_TX_PUSH) #define UNIC_ETHTOOL_COALESCE (ETHTOOL_COALESCE_USECS | \ @@ -131,6 +300,8 @@ static const struct ethtool_ops unic_ethtool_ops = { .get_fecparam = unic_get_fecparam, .set_fecparam = unic_set_fecparam, .get_fec_stats = unic_get_fec_stats, + .get_coalesce = unic_get_coalesce, + .set_coalesce = unic_set_coalesce, }; void unic_set_ethtool_ops(struct net_device *netdev) -- Gitee From 1bc445e7880059785a24aee9300b141885083dcc Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Sun, 24 Aug 2025 10:16:25 +0800 Subject: [PATCH 194/214] net: unic: Support to query and clear historical NIC link status information ANBZ: #45460 commit 4420dfeed31d751757b64677336fe906295a7d51 openEuler. In debugfs, Added query interface to get the historical NIC link information, Includes the accumulated link up/down times and the latest ten NIC link status changes. Provided interface to clear these information stored in the memory when specified file is read (no parameter is required). Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/debugfs/unic_debugfs.c | 71 ++++++++++++++++++++++ drivers/net/ub/unic/unic_dev.c | 16 ++++- drivers/net/ub/unic/unic_dev.h | 12 ++++ drivers/net/ub/unic/unic_netdev.c | 23 +++++++ 4 files changed, 120 insertions(+), 2 deletions(-) diff --git a/drivers/net/ub/unic/debugfs/unic_debugfs.c b/drivers/net/ub/unic/debugfs/unic_debugfs.c index 29fc8917eaa2..e61e782b044d 100644 --- a/drivers/net/ub/unic/debugfs/unic_debugfs.c +++ b/drivers/net/ub/unic/debugfs/unic_debugfs.c @@ -191,6 +191,63 @@ static int unic_dbg_dump_promisc_cfg_hw(struct seq_file *s, void *data) return 0; } +static int unic_dbg_query_link_record(struct seq_file *s, void *data) +{ + struct unic_dev *unic_dev = dev_get_drvdata(s->private); + struct unic_link_stats *record = &unic_dev->stats.link_record; + u8 cnt = 1, stats_cnt; + u64 total, idx; + + mutex_lock(&record->lock); + + seq_puts(s, "current time : "); + ubase_dbg_format_time(ktime_get_real_seconds(), s); + seq_printf(s, "\nlink up count : %llu\n", record->link_up_cnt); + seq_printf(s, "link down count : %llu\n", record->link_down_cnt); + + total = record->link_up_cnt + record->link_down_cnt; + if (!total) { + seq_puts(s, "link change records : NA\n"); + mutex_unlock(&record->lock); + + return 0; + } + + seq_puts(s, "link change records :\n"); + seq_puts(s, "\tNo.\tTIME\t\t\t\tSTATUS\n"); + + stats_cnt = min(total, LINK_STAT_MAX_IDX); + while (cnt <= stats_cnt) { + total--; + idx = total % LINK_STAT_MAX_IDX; + seq_printf(s, "\t%-2d\t", cnt); + ubase_dbg_format_time(ktime_get_real_seconds(), s); + seq_printf(s, "\t%s\n", + record->stats[idx].link_status ? "LINK UP" : "LINK DOWN"); + cnt++; + } + + mutex_unlock(&record->lock); + + return 0; +} + +static int unic_dbg_clear_link_record(struct seq_file *s, void *data) +{ + struct unic_dev *unic_dev = dev_get_drvdata(s->private); + struct unic_link_stats *record = &unic_dev->stats.link_record; + + mutex_lock(&record->lock); + record->link_up_cnt = 0; + record->link_down_cnt = 0; + memset(record->stats, 0, sizeof(record->stats)); + mutex_unlock(&record->lock); + + seq_puts(s, "Link status records have been cleared!\n"); + + return 0; +} + static bool unic_dbg_dentry_support(struct device *dev, u32 property) { struct unic_dev *unic_dev = dev_get_drvdata(dev); @@ -304,6 +361,20 @@ static struct ubase_dbg_cmd_info unic_dbg_cmd[] = { .support = unic_dbg_dentry_support, .init = ubase_dbg_seq_file_init, .read_func = unic_dbg_dump_promisc_cfg_hw, + }, { + .name = "link_status_record", + .dentry_index = UNIC_DBG_DENTRY_ROOT, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_query_link_record, + }, { + .name = "clear_link_status_record", + .dentry_index = UNIC_DBG_DENTRY_ROOT, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_clear_link_record, } }; diff --git a/drivers/net/ub/unic/unic_dev.c b/drivers/net/ub/unic/unic_dev.c index f039151844c2..7ed3525837e9 100644 --- a/drivers/net/ub/unic/unic_dev.c +++ b/drivers/net/ub/unic/unic_dev.c @@ -574,6 +574,7 @@ static int unic_dev_init_mtu(struct unic_dev *unic_dev) static int unic_init_mac(struct unic_dev *unic_dev) { + struct unic_link_stats *record = &unic_dev->stats.link_record; struct unic_mac *mac = &unic_dev->hw.mac; int ret; @@ -601,9 +602,17 @@ static int unic_init_mac(struct unic_dev *unic_dev) return ret; } + mutex_init(&record->lock); return 0; } +static void unic_uninit_mac(struct unic_dev *unic_dev) +{ + struct unic_link_stats *record = &unic_dev->stats.link_record; + + mutex_destroy(&record->lock); +} + int unic_set_mtu(struct unic_dev *unic_dev, int new_mtu) { u16 max_frame_size; @@ -822,11 +831,11 @@ static int unic_init_netdev_priv(struct net_device *netdev, ret = unic_init_dev_addr(priv); if (ret) - goto err_uninit_vport; + goto unic_unint_mac; ret = unic_init_channels_attr(priv); if (ret) - goto err_uninit_vport; + goto unic_unint_mac; ret = unic_init_channels(priv, priv->channels.num); if (ret) { @@ -840,6 +849,8 @@ static int unic_init_netdev_priv(struct net_device *netdev, err_uninit_channels_attr: unic_uninit_channels_attr(priv); +unic_unint_mac: + unic_uninit_mac(priv); err_uninit_vport: unic_uninit_vport(priv); destroy_lock: @@ -854,6 +865,7 @@ static void unic_uninit_netdev_priv(struct net_device *netdev) unic_uninit_channels(priv); unic_uninit_channels_attr(priv); + unic_uninit_mac(priv); unic_uninit_vport(priv); mutex_destroy(&priv->act_info.mutex); } diff --git a/drivers/net/ub/unic/unic_dev.h b/drivers/net/ub/unic/unic_dev.h index f014164c8e31..af20b72168ee 100644 --- a/drivers/net/ub/unic/unic_dev.h +++ b/drivers/net/ub/unic/unic_dev.h @@ -182,8 +182,20 @@ struct unic_fec_stats { struct unic_fec_stats_item lane[UNIC_FEC_STATS_MAX_LANE]; }; +#define LINK_STAT_MAX_IDX 10U +struct unic_link_stats { + u64 link_up_cnt; + u64 link_down_cnt; + struct { + bool link_status; + time64_t link_tv_sec; + } stats[LINK_STAT_MAX_IDX]; + struct mutex lock; /* protects link record */ +}; + struct unic_stats { struct unic_fec_stats fec_stats; + struct unic_link_stats link_record; }; struct unic_addr_tbl { diff --git a/drivers/net/ub/unic/unic_netdev.c b/drivers/net/ub/unic/unic_netdev.c index d8680ff9f894..d2d213c90d0b 100644 --- a/drivers/net/ub/unic/unic_netdev.c +++ b/drivers/net/ub/unic/unic_netdev.c @@ -164,6 +164,27 @@ static int unic_net_up(struct net_device *netdev) return 0; } +static void unic_link_status_record(struct net_device *netdev, bool linkup) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + struct unic_link_stats *record = &unic_dev->stats.link_record; + u64 idx, total; + + mutex_lock(&record->lock); + + if (linkup) + record->link_up_cnt++; + else + record->link_down_cnt++; + + total = record->link_up_cnt + record->link_down_cnt; + idx = (total - 1) % LINK_STAT_MAX_IDX; + record->stats[idx].link_tv_sec = ktime_get_real_seconds(); + record->stats[idx].link_status = linkup; + + mutex_unlock(&record->lock); +} + static void unic_clear_fec_stats(struct unic_dev *unic_dev) { struct unic_fec_stats *fec_stats = &unic_dev->stats.fec_stats; @@ -194,6 +215,8 @@ void unic_link_status_change(struct net_device *netdev, bool linkup) out: if (netif_msg_link(unic_dev)) unic_info(unic_dev, "%s.\n", linkup ? "link up" : "link down"); + + unic_link_status_record(netdev, linkup); } void unic_link_status_update(struct unic_dev *unic_dev) -- Gitee From 7e9aaac88c608f45099afd9887cb76a9437a2d28 Mon Sep 17 00:00:00 2001 From: Haibin Lu Date: Tue, 16 Sep 2025 10:22:41 +0800 Subject: [PATCH 195/214] net: unic: Drive supports ub entity reset. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ANBZ: #45460 commit b395a4f7347c243a1ed6fdd02d8aa87a94af3c58 openEuler. In this patch, add a unified interface to tigger ELR reset by ubus, adapt ehtool\tx_timeout\ubus to use this reset interface, now we can use "ethtool --reset ublx dedicated"、"echo 1 > /sys/class/net /ublx/device/reset" to trigger ELR reset. Signed-off-by: Yixi Shen Signed-off-by: Xiongchuan Zhou Signed-off-by: Haibin Lu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/Makefile | 2 +- drivers/net/ub/unic/unic_ethtool.c | 37 ++++++++ drivers/net/ub/unic/unic_event.c | 25 ++++++ drivers/net/ub/unic/unic_reset.c | 130 +++++++++++++++++++++++++++++ drivers/net/ub/unic/unic_reset.h | 14 ++++ 5 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ub/unic/unic_reset.c create mode 100644 drivers/net/ub/unic/unic_reset.h diff --git a/drivers/net/ub/unic/Makefile b/drivers/net/ub/unic/Makefile index 67ecd0ad8c11..7098242033eb 100644 --- a/drivers/net/ub/unic/Makefile +++ b/drivers/net/ub/unic/Makefile @@ -9,5 +9,5 @@ ccflags-y += -I$(srctree)/drivers/net/ub/unic/debugfs obj-$(CONFIG_UB_UNIC) += unic.o unic-objs = unic_main.o unic_ethtool.o unic_hw.o unic_guid.o unic_netdev.o unic_dev.o unic_qos_hw.o unic_event.o unic_crq.o unic-objs += unic_channel.o debugfs/unic_debugfs.o unic_rx.o unic_tx.o unic_txrx.o unic_comm_addr.o unic_rack_ip.o unic_stats.o -unic-objs += debugfs/unic_ctx_debugfs.o +unic-objs += debugfs/unic_ctx_debugfs.o unic_reset.o unic-$(CONFIG_UB_UNIC_DCB) += unic_dcbnl.o diff --git a/drivers/net/ub/unic/unic_ethtool.c b/drivers/net/ub/unic/unic_ethtool.c index eb90121ad6fb..c9593ba74fe4 100644 --- a/drivers/net/ub/unic/unic_ethtool.c +++ b/drivers/net/ub/unic/unic_ethtool.c @@ -278,6 +278,42 @@ static int unic_set_coalesce(struct net_device *netdev, return ret; } +static const struct unic_reset_type_map unic_ethtool_reset_map[] = { + {ETH_RESET_DEDICATED, UBASE_UE_RESET}, +}; + +static int unic_reset(struct net_device *ndev, u32 *flags) +{ + enum ubase_reset_type reset_type = UBASE_NO_RESET; + struct unic_dev *unic_dev = netdev_priv(ndev); + enum ethtool_reset_flags reset_flags; + u32 i; + + if (unic_resetting(ndev)) { + unic_err(unic_dev, "failed to reset, due to dev resetting.\n"); + return -EBUSY; + } + + for (i = 0; i < ARRAY_SIZE(unic_ethtool_reset_map); i++) { + if (unic_ethtool_reset_map[i].reset_flags == *flags) { + reset_type = unic_ethtool_reset_map[i].reset_type; + reset_flags = unic_ethtool_reset_map[i].reset_flags; + break; + } + } + + if (reset_type == UBASE_NO_RESET) + return -EOPNOTSUPP; + + unic_info(unic_dev, + "ethtool setting reset type, type = %u.\n", reset_type); + + ubase_reset_event(unic_dev->comdev.adev, reset_type); + *flags &= ~reset_flags; + + return 0; +} + #define UNIC_ETHTOOL_RING (ETHTOOL_RING_USE_RX_BUF_LEN | \ ETHTOOL_RING_USE_TX_PUSH) #define UNIC_ETHTOOL_COALESCE (ETHTOOL_COALESCE_USECS | \ @@ -302,6 +338,7 @@ static const struct ethtool_ops unic_ethtool_ops = { .get_fec_stats = unic_get_fec_stats, .get_coalesce = unic_get_coalesce, .set_coalesce = unic_set_coalesce, + .reset = unic_reset, }; void unic_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/ub/unic/unic_event.c b/drivers/net/ub/unic/unic_event.c index bcd1e210f447..2f24cd423e2b 100644 --- a/drivers/net/ub/unic/unic_event.c +++ b/drivers/net/ub/unic/unic_event.c @@ -23,6 +23,7 @@ #include "unic_netdev.h" #include "unic_qos_hw.h" #include "unic_rack_ip.h" +#include "unic_reset.h" #include "unic_event.h" int unic_comp_handler(struct notifier_block *nb, unsigned long jfcn, void *data) @@ -52,6 +53,25 @@ int unic_comp_handler(struct notifier_block *nb, unsigned long jfcn, void *data) return 0; } +static void unic_rack_port_reset(struct unic_dev *unic_dev, bool link_up) +{ + if (link_up) + unic_dev->hw.mac.link_status = UNIC_LINK_STATUS_UP; + else + unic_dev->hw.mac.link_status = UNIC_LINK_STATUS_DOWN; +} + +static void unic_port_handler(struct auxiliary_device *adev, bool link_up) +{ + struct unic_dev *unic_dev = dev_get_drvdata(&adev->dev); + struct net_device *netdev = unic_dev->comdev.netdev; + + if (!netif_running(netdev)) + return; + + unic_rack_port_reset(unic_dev, link_up); +} + static struct ubase_ctrlq_event_nb unic_ctrlq_events[] = { { .service_type = UBASE_CTRLQ_SER_TYPE_IP_ACL, @@ -140,6 +160,9 @@ int unic_register_event(struct auxiliary_device *adev) if (ret) goto unregister_crq; + ubase_port_register(adev, unic_port_handler); + ubase_reset_register(adev, unic_reset_handler); + return 0; unregister_crq: @@ -149,6 +172,8 @@ int unic_register_event(struct auxiliary_device *adev) void unic_unregister_event(struct auxiliary_device *adev) { + ubase_reset_unregister(adev); + ubase_port_unregister(adev); unic_unregister_ctrlq_event(adev, ARRAY_SIZE(unic_ctrlq_events)); unic_unregister_crq_event(adev, ARRAY_SIZE(unic_crq_events)); } diff --git a/drivers/net/ub/unic/unic_reset.c b/drivers/net/ub/unic/unic_reset.c new file mode 100644 index 000000000000..ff1239cc5131 --- /dev/null +++ b/drivers/net/ub/unic/unic_reset.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include + +#include "unic_cmd.h" +#include "unic_dev.h" +#include "unic_hw.h" +#include "unic_netdev.h" +#include "unic_rack_ip.h" +#include "unic_reset.h" + +static void unic_dev_suspend(struct unic_dev *unic_dev) +{ + unic_uninit_channels(unic_dev); +} + +static void unic_reset_down(struct auxiliary_device *adev) +{ + struct unic_dev *priv = (struct unic_dev *)dev_get_drvdata(&adev->dev); + struct net_device *netdev = priv->comdev.netdev; + bool if_running; + int ret; + + if (!test_bit(UNIC_STATE_INITED, &priv->state) || + test_and_set_bit(UNIC_STATE_DISABLED, &priv->state)) { + unic_warn(priv, "failed to reset unic, device is not ready.\n"); + return; + } + + set_bit(UNIC_STATE_RESETTING, &priv->state); + if_running = netif_running(netdev); + + unic_info(priv, "unic reset start.\n"); + + unic_remove_period_task(priv); + + /* due to lack of cmdq when resetting, need to close promisc first, + * to prevent that concurrent deactivate event ubable to close promisc + * when resetting + */ + ret = unic_activate_promisc_mode(priv, false); + if (ret) + unic_warn(priv, "failed to close promisc, ret = %d.\n", ret); + else + set_bit(UNIC_VPORT_STATE_PROMISC_CHANGE, &priv->vport.state); + + rtnl_lock(); + ret = if_running ? unic_net_stop(netdev) : 0; + rtnl_unlock(); + if (ret) + unic_err(priv, "failed to stop unic net, ret = %d.\n", ret); +} + +static void unic_reset_uninit(struct auxiliary_device *adev) +{ + struct unic_dev *priv = (struct unic_dev *)dev_get_drvdata(&adev->dev); + + if (!test_bit(UNIC_STATE_RESETTING, &priv->state)) + return; + + unic_dev_suspend(priv); +} + +static int unic_dev_resume(struct unic_dev *unic_dev) +{ + int ret; + + ret = unic_init_channels(unic_dev, unic_dev->channels.num); + if (ret) + unic_err(unic_dev, "failed to init channels, ret = %d.\n", ret); + + return ret; +} + +static void unic_reset_init(struct auxiliary_device *adev) +{ + struct unic_dev *priv = (struct unic_dev *)dev_get_drvdata(&adev->dev); + struct net_device *netdev = priv->comdev.netdev; + bool if_running; + int ret; + + if (!test_bit(UNIC_STATE_RESETTING, &priv->state)) + return; + + ret = unic_dev_resume(priv); + if (ret) + goto err_unic_resume; + + unic_query_rack_ip(adev); + unic_start_period_task(netdev); + + if_running = netif_running(netdev); + clear_bit(UNIC_STATE_RESETTING, &priv->state); + clear_bit(UNIC_STATE_DISABLED, &priv->state); + rtnl_lock(); + ret = if_running ? unic_net_open(netdev) : 0; + rtnl_unlock(); + if (ret) + unic_err(priv, "failed to up net, ret = %d.\n", ret); + + unic_info(priv, "unic reset done.\n"); + + return; + +err_unic_resume: + clear_bit(UNIC_STATE_RESETTING, &priv->state); + clear_bit(UNIC_STATE_DISABLED, &priv->state); +} + +void unic_reset_handler(struct auxiliary_device *adev, + enum ubase_reset_stage stage) +{ + switch (stage) { + case UBASE_RESET_STAGE_DOWN: + unic_reset_down(adev); + break; + case UBASE_RESET_STAGE_UNINIT: + unic_reset_uninit(adev); + break; + case UBASE_RESET_STAGE_INIT: + unic_reset_init(adev); + break; + default: + break; + } +} diff --git a/drivers/net/ub/unic/unic_reset.h b/drivers/net/ub/unic/unic_reset.h new file mode 100644 index 000000000000..4ce313ac58aa --- /dev/null +++ b/drivers/net/ub/unic/unic_reset.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_RESET_H__ +#define __UNIC_RESET_H__ + +#include + +void unic_reset_handler(struct auxiliary_device *adev, enum ubase_reset_stage stage); + +#endif -- Gitee From 60580d05b70ed8e2cd69aabff15240b71561feaa Mon Sep 17 00:00:00 2001 From: Haibin Lu Date: Sat, 30 Aug 2025 10:03:33 +0800 Subject: [PATCH 196/214] net: unic: Support RAS ANBZ: #45460 commit 339b55a00771bd1379427c7afcf0b417c0e5b781 openEuler. RAS is an important feature of the system. UB also supports RAS function. UB protocol defines multiple types of ras, and different types of ras are processed in different ways to ensure system stability. This patch provides the support of the UNIC driver for ras. As an auxiliary device of the UBSAE driver, the UNIC driver registers the corresponding RAS processing function with the UBASE. After receiving the corresponding RAS, UBASE routes the RAS to UNIC for processing. Signed-off-by: Yaoyao Tu Signed-off-by: Haibin Lu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/unic_event.c | 73 +++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/drivers/net/ub/unic/unic_event.c b/drivers/net/ub/unic/unic_event.c index 2f24cd423e2b..faa91a99badc 100644 --- a/drivers/net/ub/unic/unic_event.c +++ b/drivers/net/ub/unic/unic_event.c @@ -148,14 +148,81 @@ static int unic_register_crq_event(struct auxiliary_device *adev) return 0; } +static void unic_unregister_ae_event(struct auxiliary_device *adev, + u8 asyn_event_num) +{ + struct unic_dev *unic_dev = dev_get_drvdata(&adev->dev); + u8 i; + + for (i = 0; i < asyn_event_num; i++) + ubase_event_unregister(adev, &unic_dev->ae_nbs[i]); +} + +static int unic_ae_jetty_level_error(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct ubase_event_nb *ev_nb = container_of(nb, + struct ubase_event_nb, nb); + struct auxiliary_device *adev = (struct auxiliary_device *)ev_nb->back; + struct unic_dev *unic_dev = dev_get_drvdata(&adev->dev); + struct ubase_aeq_notify_info *info = data; + u32 queue_num; + + /* Normally, UNIC does not report such abnormal events, + * but in order to maintain its scalability, + * unic reserves the reset processing of such events. + */ + queue_num = info->aeqe->event.queue_event.num; + unic_err(unic_dev, + "recv jetty level error, event_type = 0x%x, sub_type = 0x%x, queue_num = %u.\n", + info->event_type, info->sub_type, queue_num); + + ubase_reset_event(adev, UBASE_UE_RESET); + + return 0; +} + +static int unic_register_ae_event(struct auxiliary_device *adev) +{ + struct ubase_event_nb unic_ae_nbs[UNIC_AE_LEVEL_NUM] = { + { + UBASE_DRV_UNIC, + UBASE_EVENT_TYPE_JETTY_LEVEL_ERROR, + { unic_ae_jetty_level_error }, + adev + }, + }; + struct unic_dev *unic_dev = dev_get_drvdata(&adev->dev); + int ret; + u8 i; + + for (i = 0; i < ARRAY_SIZE(unic_ae_nbs); i++) { + unic_dev->ae_nbs[i] = unic_ae_nbs[i]; + ret = ubase_event_register(adev, &unic_dev->ae_nbs[i]); + if (ret) { + dev_err(adev->dev.parent, + "failed to register asyn event[%u], ret = %d.\n", + unic_dev->ae_nbs[i].event_type, ret); + unic_unregister_ae_event(adev, i); + return ret; + } + } + + return ret; +} + int unic_register_event(struct auxiliary_device *adev) { int ret; - ret = unic_register_crq_event(adev); + ret = unic_register_ae_event(adev); if (ret) return ret; + ret = unic_register_crq_event(adev); + if (ret) + goto unregister_ae; + ret = unic_register_ctrlq_event(adev); if (ret) goto unregister_crq; @@ -167,6 +234,9 @@ int unic_register_event(struct auxiliary_device *adev) unregister_crq: unic_unregister_crq_event(adev, ARRAY_SIZE(unic_crq_events)); +unregister_ae: + unic_unregister_ae_event(adev, UNIC_AE_LEVEL_NUM); + return ret; } @@ -176,4 +246,5 @@ void unic_unregister_event(struct auxiliary_device *adev) ubase_port_unregister(adev); unic_unregister_ctrlq_event(adev, ARRAY_SIZE(unic_ctrlq_events)); unic_unregister_crq_event(adev, ARRAY_SIZE(unic_crq_events)); + unic_unregister_ae_event(adev, UNIC_AE_LEVEL_NUM); } -- Gitee From cd4d7add5b2ebad77d513512b88151b8710a400e Mon Sep 17 00:00:00 2001 From: Haibin Lu Date: Tue, 16 Sep 2025 11:06:51 +0800 Subject: [PATCH 197/214] net: unic: support config/query the mapping between dscp and tc ANBZ: #45460 commit c20baa942215749ed839e4fc02b3d197e3e3d142 openEuler. This patch introduces support for configuring and querying the mapping between DSCP (Differentiated Services Code Point) and TC (Traffic Class) in the UNIC driver. The new functionality allows users to set and retrieve the DSCP-to-TC mappings, enhancing the driver's ability to manage network traffic prioritization. Signed-off-by: Haiqing Fang Signed-off-by: Xiaobo Zhang Signed-off-by: Haibin Lu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/unic_dcbnl.c | 138 +++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/drivers/net/ub/unic/unic_dcbnl.c b/drivers/net/ub/unic/unic_dcbnl.c index 3110277da020..9e44d74f3b38 100644 --- a/drivers/net/ub/unic/unic_dcbnl.c +++ b/drivers/net/ub/unic/unic_dcbnl.c @@ -10,7 +10,145 @@ #include "unic_netdev.h" #include "unic_dcbnl.h" +static int unic_dscp_prio_check(struct net_device *netdev, struct dcb_app *app) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + + if (!unic_dev_ets_supported(unic_dev)) + return -EOPNOTSUPP; + + if (netif_running(netdev)) { + unic_err(unic_dev, + "failed to set dscp-prio, due to network interface is up, pls down it first and try again.\n"); + return -EBUSY; + } + + if (app->selector != IEEE_8021QAZ_APP_SEL_DSCP || + app->protocol >= UBASE_MAX_DSCP || + app->priority >= UNIC_MAX_PRIO_NUM) + return -EINVAL; + + if (unic_resetting(netdev)) + return -EBUSY; + + return 0; +} + +static int unic_set_app(struct net_device *netdev, struct dcb_app *app, + struct unic_dev *unic_dev, struct unic_vl *vl) +{ + struct dcb_app old_app; + int ret; + + unic_info(unic_dev, "setapp dscp = %u, priority = %u.\n", + app->protocol, app->priority); + + ret = dcb_ieee_setapp(netdev, app); + if (ret) { + unic_err(unic_dev, "failed to add app, ret = %d.\n", ret); + return ret; + } + + old_app.selector = IEEE_8021QAZ_APP_SEL_DSCP; + old_app.protocol = app->protocol; + old_app.priority = vl->dscp_prio[app->protocol]; + + vl->dscp_prio[app->protocol] = app->priority; + ret = unic_set_vl_map(unic_dev, vl->dscp_prio, vl->prio_vl, + UNIC_DSCP_VL_MAP); + if (ret) { + vl->dscp_prio[app->protocol] = old_app.priority; + dcb_ieee_delapp(netdev, app); + return ret; + } + + if (old_app.priority == UNIC_INVALID_PRIORITY) { + vl->dscp_app_cnt++; + } else { + ret = dcb_ieee_delapp(netdev, &old_app); + if (ret) + unic_err(unic_dev, + "failed to delete old app, ret = %d.\n", ret); + } + + return ret; +} + +static int unic_dcbnl_ieee_setapp(struct net_device *netdev, + struct dcb_app *app) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + struct unic_vl *vl = &unic_dev->channels.vl; + int ret; + + ret = unic_dscp_prio_check(netdev, app); + if (ret) { + unic_err(unic_dev, "failed to set dscp-prio, ret = %d\n", ret); + return ret; + } + + /* dscp-prio already set */ + if (vl->dscp_prio[app->protocol] == app->priority) + return 0; + + return unic_set_app(netdev, app, unic_dev, vl); +} + +static int unic_del_app(struct net_device *netdev, struct dcb_app *app, + struct unic_dev *unic_dev, struct unic_vl *vl) +{ + u8 map_type = UNIC_DSCP_VL_MAP; + int ret; + + unic_info(unic_dev, "delapp dscp = %u, priority = %u\n", + app->protocol, app->priority); + + ret = dcb_ieee_delapp(netdev, app); + if (ret) + return ret; + + if (vl->dscp_app_cnt <= 1) + map_type = UNIC_PRIO_VL_MAP; + + vl->dscp_prio[app->protocol] = UNIC_INVALID_PRIORITY; + ret = unic_set_vl_map(unic_dev, vl->dscp_prio, vl->prio_vl, + map_type); + if (ret) { + vl->dscp_prio[app->protocol] = app->priority; + dcb_ieee_setapp(netdev, app); + return ret; + } + + if (vl->dscp_app_cnt) + vl->dscp_app_cnt--; + + return 0; +} + +static int unic_dcbnl_ieee_delapp(struct net_device *netdev, + struct dcb_app *app) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + struct unic_vl *vl = &unic_dev->channels.vl; + int ret; + + ret = unic_dscp_prio_check(netdev, app); + if (ret) { + unic_err(unic_dev, "failed to del dscp-prio, ret = %d.\n", ret); + return ret; + } + + if (app->priority != vl->dscp_prio[app->protocol]) { + unic_err(unic_dev, "failed to del no match dscp-prio.\n"); + return -EINVAL; + } + + return unic_del_app(netdev, app, unic_dev, vl); +} + static const struct dcbnl_rtnl_ops unic_dcbnl_ops = { + .ieee_setapp = unic_dcbnl_ieee_setapp, + .ieee_delapp = unic_dcbnl_ieee_delapp, }; void unic_set_dcbnl_ops(struct net_device *netdev) -- Gitee From 78a507cf066432cab6bb55346ee0fe772737251b Mon Sep 17 00:00:00 2001 From: Haibin Lu Date: Tue, 23 Sep 2025 17:25:20 +0800 Subject: [PATCH 198/214] net: unic: Add debugfs support for QoS configuration and query. ANBZ: #45460 commit 3a3016996e8e5ffff95b290866fc0899409a4e2d openEuler. This patch introduces debugfs support for configuring and querying QoS (Quality of Service) mappings in the UNIC driver. The new functionality allows users to inspect and manage DSCP (Differentiated Services Code Point) to TC (Traffic Class) mappings, as well as VLAN queue configurations. Signed-off-by: Haiqing Fang Signed-off-by: Xiaobo Zhang Signed-off-by: Haibin Lu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/Makefile | 2 +- drivers/net/ub/unic/debugfs/unic_debugfs.c | 35 ++++- drivers/net/ub/unic/debugfs/unic_debugfs.h | 1 + .../net/ub/unic/debugfs/unic_qos_debugfs.c | 148 ++++++++++++++++++ .../net/ub/unic/debugfs/unic_qos_debugfs.h | 15 ++ drivers/net/ub/unic/unic_hw.h | 1 + drivers/net/ub/unic/unic_qos_hw.c | 18 +++ drivers/net/ub/unic/unic_qos_hw.h | 2 + 8 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 drivers/net/ub/unic/debugfs/unic_qos_debugfs.c create mode 100644 drivers/net/ub/unic/debugfs/unic_qos_debugfs.h diff --git a/drivers/net/ub/unic/Makefile b/drivers/net/ub/unic/Makefile index 7098242033eb..fa5de4255e68 100644 --- a/drivers/net/ub/unic/Makefile +++ b/drivers/net/ub/unic/Makefile @@ -9,5 +9,5 @@ ccflags-y += -I$(srctree)/drivers/net/ub/unic/debugfs obj-$(CONFIG_UB_UNIC) += unic.o unic-objs = unic_main.o unic_ethtool.o unic_hw.o unic_guid.o unic_netdev.o unic_dev.o unic_qos_hw.o unic_event.o unic_crq.o unic-objs += unic_channel.o debugfs/unic_debugfs.o unic_rx.o unic_tx.o unic_txrx.o unic_comm_addr.o unic_rack_ip.o unic_stats.o -unic-objs += debugfs/unic_ctx_debugfs.o unic_reset.o +unic-objs += debugfs/unic_ctx_debugfs.o unic_reset.o debugfs/unic_qos_debugfs.o unic-$(CONFIG_UB_UNIC_DCB) += unic_dcbnl.o diff --git a/drivers/net/ub/unic/debugfs/unic_debugfs.c b/drivers/net/ub/unic/debugfs/unic_debugfs.c index e61e782b044d..f9cc6e887ce0 100644 --- a/drivers/net/ub/unic/debugfs/unic_debugfs.c +++ b/drivers/net/ub/unic/debugfs/unic_debugfs.c @@ -10,9 +10,10 @@ #include #include -#include "unic_dev.h" #include "unic_ctx_debugfs.h" +#include "unic_dev.h" #include "unic_hw.h" +#include "unic_qos_debugfs.h" #include "unic_debugfs.h" static int unic_dbg_dump_dev_info(struct seq_file *s, void *data) @@ -260,6 +261,10 @@ static struct ubase_dbg_dentry_info unic_dbg_dentry[] = { .name = "context", .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, .support = unic_dbg_dentry_support, + }, { + .name = "qos", + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, }, /* keep unic at the bottom and add new directory above */ { @@ -347,6 +352,13 @@ static struct ubase_dbg_cmd_info unic_dbg_cmd[] = { .support = unic_dbg_dentry_support, .init = ubase_dbg_seq_file_init, .read_func = unic_dbg_dump_rq_jfc_context_hw, + }, { + .name = "vl_queue", + .dentry_index = UNIC_DBG_DENTRY_QOS, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_vl_queue, }, { .name = "rss_cfg_hw", .dentry_index = UNIC_DBG_DENTRY_ROOT, @@ -361,6 +373,27 @@ static struct ubase_dbg_cmd_info unic_dbg_cmd[] = { .support = unic_dbg_dentry_support, .init = ubase_dbg_seq_file_init, .read_func = unic_dbg_dump_promisc_cfg_hw, + }, { + .name = "dscp_vl_map", + .dentry_index = UNIC_DBG_DENTRY_QOS, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_dscp_vl_map, + }, { + .name = "prio_vl_map", + .dentry_index = UNIC_DBG_DENTRY_QOS, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_prio_vl_map, + }, { + .name = "dscp_prio", + .dentry_index = UNIC_DBG_DENTRY_QOS, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_dscp_prio, }, { .name = "link_status_record", .dentry_index = UNIC_DBG_DENTRY_ROOT, diff --git a/drivers/net/ub/unic/debugfs/unic_debugfs.h b/drivers/net/ub/unic/debugfs/unic_debugfs.h index 1afba40382d6..065630fbd35e 100644 --- a/drivers/net/ub/unic/debugfs/unic_debugfs.h +++ b/drivers/net/ub/unic/debugfs/unic_debugfs.h @@ -14,6 +14,7 @@ enum unic_dbg_dentry_type { UNIC_DBG_DENTRY_CONTEXT, + UNIC_DBG_DENTRY_QOS, /* must be the last entry. */ UNIC_DBG_DENTRY_ROOT }; diff --git a/drivers/net/ub/unic/debugfs/unic_qos_debugfs.c b/drivers/net/ub/unic/debugfs/unic_qos_debugfs.c new file mode 100644 index 000000000000..85b3288f0bec --- /dev/null +++ b/drivers/net/ub/unic/debugfs/unic_qos_debugfs.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include "unic_dcbnl.h" +#include "unic_debugfs.h" +#include "unic_hw.h" +#include "unic_qos_debugfs.h" + +int unic_dbg_dump_vl_queue(struct seq_file *s, void *data) +{ + struct unic_dev *unic_dev = dev_get_drvdata(s->private); + struct unic_vl *vl = &unic_dev->channels.vl; + u8 i; + + seq_puts(s, "VL_ID Q_OFFSET Q_COUNT\n"); + + for (i = 0; i < unic_dev->channels.rss_vl_num; i++) { + seq_printf(s, "%-7d", i); + seq_printf(s, "%-12u", vl->queue_offset[i]); + seq_printf(s, "%-11u\n", vl->queue_count[i]); + } + + return 0; +} + +static void unic_dump_dscp_vl_map(struct unic_dev *unic_dev, + struct seq_file *s, u8 dscp, u8 hw_vl) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct ubase_adev_qos *qos = ubase_get_adev_qos(adev); + struct unic_vl *vl = &unic_dev->channels.vl; + u8 prio, sw_vl = 0; + + prio = vl->dscp_prio[dscp]; + if (prio == UNIC_INVALID_PRIORITY || + vl->prio_vl[prio] >= unic_dev->channels.rss_vl_num) + sw_vl = qos->nic_vl[0]; + else + sw_vl = qos->nic_vl[vl->prio_vl[prio]]; + + seq_printf(s, "%-6u", dscp); + seq_printf(s, "%-7u", sw_vl); + + if (!unic_dev_ubl_supported(unic_dev) && + unic_dev_ets_supported(unic_dev)) + seq_printf(s, "%-7u", hw_vl); + else + seq_puts(s, "--"); + + seq_puts(s, "\n"); +} + +int unic_dbg_dump_dscp_vl_map(struct seq_file *s, void *data) +{ + struct unic_dev *unic_dev = dev_get_drvdata(s->private); + struct unic_config_vl_map_cmd resp = {0}; + int ret; + u8 i; + + if (__unic_resetting(unic_dev)) + return -EBUSY; + + if (!unic_dev_ubl_supported(unic_dev) && + unic_dev_ets_supported(unic_dev)) { + ret = unic_query_vl_map(unic_dev, &resp); + if (ret) + return ret; + } + + seq_puts(s, "DSCP SW_VL HW_VL\n"); + + for (i = 0; i < UBASE_MAX_DSCP; i++) + unic_dump_dscp_vl_map(unic_dev, s, i, resp.dscp_vl[i]); + + return 0; +} + +static void unic_dump_prio_vl_map(struct unic_dev *unic_dev, + struct seq_file *s, u8 prio, u8 hw_vl) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct ubase_adev_qos *qos = ubase_get_adev_qos(adev); + struct unic_vl *vl = &unic_dev->channels.vl; + u8 sw_vl = 0; + + if (vl->prio_vl[prio] >= unic_dev->channels.rss_vl_num) + sw_vl = qos->nic_vl[0]; + else + sw_vl = qos->nic_vl[vl->prio_vl[prio]]; + + seq_printf(s, "%-6u", prio); + seq_printf(s, "%-7u", sw_vl); + + if (!unic_dev_ubl_supported(unic_dev) && + unic_dev_ets_supported(unic_dev)) + seq_printf(s, "%-7u", hw_vl); + else + seq_puts(s, "--"); + + seq_puts(s, "\n"); +} + +int unic_dbg_dump_prio_vl_map(struct seq_file *s, void *data) +{ + struct unic_dev *unic_dev = dev_get_drvdata(s->private); + struct unic_config_vl_map_cmd resp = {0}; + int ret; + u8 i; + + if (__unic_resetting(unic_dev)) + return -EBUSY; + + if (!unic_dev_ubl_supported(unic_dev) && + unic_dev_ets_supported(unic_dev)) { + ret = unic_query_vl_map(unic_dev, &resp); + if (ret) + return ret; + } + + seq_puts(s, "PRIO SW_VL HW_VL\n"); + + for (i = 0; i < UNIC_MAX_PRIO_NUM; i++) + unic_dump_prio_vl_map(unic_dev, s, i, resp.prio_vl[i]); + + return 0; +} + +int unic_dbg_dump_dscp_prio(struct seq_file *s, void *data) +{ + struct unic_dev *unic_dev = dev_get_drvdata(s->private); + struct unic_vl *vl = &unic_dev->channels.vl; + u16 i; + + if (__unic_resetting(unic_dev)) + return -EBUSY; + + seq_puts(s, "DSCP PRIO\n"); + + for (i = 0; i < UBASE_MAX_DSCP; i++) { + seq_printf(s, "%-6u", i); + seq_printf(s, "%-7u\n", vl->dscp_prio[i]); + } + + return 0; +} diff --git a/drivers/net/ub/unic/debugfs/unic_qos_debugfs.h b/drivers/net/ub/unic/debugfs/unic_qos_debugfs.h new file mode 100644 index 000000000000..f55616ab1617 --- /dev/null +++ b/drivers/net/ub/unic/debugfs/unic_qos_debugfs.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_QOS_DEBUGFS_H__ +#define __UNIC_QOS_DEBUGFS_H__ + +int unic_dbg_dump_vl_queue(struct seq_file *s, void *data); +int unic_dbg_dump_dscp_vl_map(struct seq_file *s, void *data); +int unic_dbg_dump_prio_vl_map(struct seq_file *s, void *data); +int unic_dbg_dump_dscp_prio(struct seq_file *s, void *data); + +#endif diff --git a/drivers/net/ub/unic/unic_hw.h b/drivers/net/ub/unic/unic_hw.h index ebe0c714549d..cfd46b6eadf4 100644 --- a/drivers/net/ub/unic/unic_hw.h +++ b/drivers/net/ub/unic/unic_hw.h @@ -11,6 +11,7 @@ #include #include "unic_dev.h" +#include "unic_qos_hw.h" #define UNIC_DL_CONFIG_MODE_TX_EN_B 0 #define UNIC_DL_CONFIG_MODE_RX_EN_B 1 diff --git a/drivers/net/ub/unic/unic_qos_hw.c b/drivers/net/ub/unic/unic_qos_hw.c index 2f463be3d324..79fb7271c08e 100644 --- a/drivers/net/ub/unic/unic_qos_hw.c +++ b/drivers/net/ub/unic/unic_qos_hw.c @@ -29,6 +29,24 @@ int unic_set_hw_vl_map(struct unic_dev *unic_dev, u8 *dscp_vl, u8 *prio_vl, return ret; } +int unic_query_vl_map(struct unic_dev *unic_dev, + struct unic_config_vl_map_cmd *resp) +{ + struct unic_config_vl_map_cmd req = {0}; + struct ubase_cmd_buf in, out; + int ret; + + ubase_fill_inout_buf(&in, UBASE_OPC_CFG_VL_MAP, true, sizeof(req), + &req); + ubase_fill_inout_buf(&out, UBASE_OPC_CFG_VL_MAP, false, sizeof(*resp), + resp); + ret = ubase_cmd_send_inout(unic_dev->comdev.adev, &in, &out); + if (ret) + unic_err(unic_dev, "failed to query vl map, ret = %d.\n", ret); + + return ret; +} + /* vl_maxrate: byte per second */ int unic_config_vl_rate_limit(struct unic_dev *unic_dev, u64 *vl_maxrate, u16 vl_bitmap) diff --git a/drivers/net/ub/unic/unic_qos_hw.h b/drivers/net/ub/unic/unic_qos_hw.h index 9f6947463349..82822cc871b6 100644 --- a/drivers/net/ub/unic/unic_qos_hw.h +++ b/drivers/net/ub/unic/unic_qos_hw.h @@ -13,6 +13,8 @@ int unic_set_hw_vl_map(struct unic_dev *unic_dev, u8 *dscp_vl, u8 *prio_vl, u8 map_type); +int unic_query_vl_map(struct unic_dev *unic_dev, + struct unic_config_vl_map_cmd *resp); int unic_config_vl_rate_limit(struct unic_dev *unic_dev, u64 *vl_maxrate, u16 vl_bitmap); -- Gitee From 0ce7bc671ecdecac56363121032ea577681bf040 Mon Sep 17 00:00:00 2001 From: Haibin Lu Date: Tue, 16 Sep 2025 11:55:53 +0800 Subject: [PATCH 199/214] net: unic: support config/query ets parameters ANBZ: #45460 commit d6b4c7258c23f655f7d0509c1cd80361abfc245d openEuler. This patch enhances the UNIC driver's support for DCB (Data Center Bridging) by adding functionality to configure and query ETS (Enhanced Transmission Selection) parameters, as well as manage VLAN queues. The changes include: 1. New DCBnl Commands: - `ieee_getets`: Retrieves current ETS configuration. - `ieee_setets`: Sets ETS parameters, including priority-to-TC mappings, TC bandwidth, and TSA (Traffic Shaping Algorithm). 2. ETS Configuration Management: - Adds validation for ETS parameters to ensure compatibility with hardware capabilities. - Implements logic to update VLAN mappings and traffic scheduling based on ETS settings. 3. VLAN Queue Adjustments: - Introduces functions to detect changes in VLAN queue numbers and adjust RSS (Receive Side Scaling) queue sizes accordingly, optimizing network performance. 4. Integration with Existing Framework: - Registers new DCBnl commands with the netlink interface for configuration and querying. - Updates internal structures and functions to support the new ETS and VLAN queue management features. This enhancement improves the UNIC driver's ability to manage network traffic prioritization and scheduling, providing greater flexibility and performance in data center environments. Signed-off-by: Haiqing Fang Signed-off-by: Xiaobo Zhang Signed-off-by: Haibin Lu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/unic_dcbnl.c | 244 ++++++++++++++++++++++++++++++ drivers/net/ub/unic/unic_dev.c | 22 +++ drivers/net/ub/unic/unic_dev.h | 12 ++ include/ub/ubase/ubase_comm_dev.h | 2 + 4 files changed, 280 insertions(+) diff --git a/drivers/net/ub/unic/unic_dcbnl.c b/drivers/net/ub/unic/unic_dcbnl.c index 9e44d74f3b38..2b50901d8434 100644 --- a/drivers/net/ub/unic/unic_dcbnl.c +++ b/drivers/net/ub/unic/unic_dcbnl.c @@ -5,11 +5,253 @@ */ #include +#include #include "unic_hw.h" #include "unic_netdev.h" #include "unic_dcbnl.h" +#define UNIC_PRIO_VL_MAP_CHANGED BIT(0) +#define UNIC_TC_TSA_CHANGED BIT(1) +#define UNIC_TC_BW_CHANGED BIT(2) + +static int unic_ets_prio_tc_validate(struct unic_dev *unic_dev, + struct ieee_ets *ets, u8 *changed, + u8 *vl_num) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct ubase_caps *caps = ubase_get_dev_caps(adev); + u32 max_queue = unic_channels_max_num(adev); + u8 i, max_vl = 0; + + for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { + if (ets->prio_tc[i] != unic_dev->channels.vl.prio_vl[i]) + *changed |= UNIC_PRIO_VL_MAP_CHANGED; + + max_vl = max(max_vl, ets->prio_tc[i] + 1); + } + + if (max_vl > caps->vl_num) { + unic_err(unic_dev, "tc num(%u) can't exceed max tc(%u).\n", + max_vl, caps->vl_num); + return -EINVAL; + } + + if (unic_get_rss_vl_num(unic_dev, max_vl) > max_queue) { + unic_err(unic_dev, + "tc num can't exceed queue num(%u).\n", max_queue); + return -EINVAL; + } + + *vl_num = max_vl; + + return 0; +} + +static int unic_ets_tc_bw_validate(struct unic_dev *unic_dev, + struct ieee_ets *ets, u8 *changed) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct ubase_caps *caps = ubase_get_dev_caps(adev); + struct unic_vl *vl = &unic_dev->channels.vl; + /* continuous bitmap configured by the dcb tool */ + u16 tc_bitmap = (1 << caps->vl_num) - 1; + int ret; + u8 i; + + ret = ubase_check_qos_sch_param(adev, tc_bitmap, ets->tc_tx_bw, + ets->tc_tsa, false); + if (ret) + return ret; + + for (i = 0; i < caps->vl_num; i++) { + if (vl->vl_tsa[i] != ets->tc_tsa[i]) + *changed |= UNIC_TC_TSA_CHANGED; + + if (vl->vl_bw[i] != ets->tc_tx_bw[i]) + *changed |= UNIC_TC_BW_CHANGED; + } + + return 0; +} + +static int unic_setets_params_validate(struct unic_dev *unic_dev, + struct ieee_ets *ets, u8 *changed, + u8 *vl_num) +{ + int ret; + + ret = unic_ets_prio_tc_validate(unic_dev, ets, changed, vl_num); + if (ret) + return ret; + + return unic_ets_tc_bw_validate(unic_dev, ets, changed); +} + +static int unic_dcbnl_ieee_getets(struct net_device *ndev, struct ieee_ets *ets) +{ + struct unic_dev *unic_dev = netdev_priv(ndev); + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct ubase_caps *caps = ubase_get_dev_caps(adev); + struct unic_vl *vl = &unic_dev->channels.vl; + + if (unic_resetting(ndev)) + return -EBUSY; + + if (!unic_dev_ets_supported(unic_dev)) + return -EOPNOTSUPP; + + memset(ets, 0, sizeof(*ets)); + ets->willing = 1; + ets->ets_cap = caps->vl_num; + + memcpy(ets->prio_tc, vl->prio_vl, sizeof(ets->prio_tc)); + memcpy(ets->tc_tx_bw, vl->vl_bw, sizeof(ets->tc_tx_bw)); + memcpy(ets->tc_tsa, vl->vl_tsa, sizeof(ets->tc_tsa)); + + return 0; +} + +static int unic_setets_preconditions(struct net_device *net_dev) +{ + struct unic_dev *unic_dev = netdev_priv(net_dev); + + if (!unic_dev_ets_supported(unic_dev)) + return -EOPNOTSUPP; + + if (netif_running(net_dev)) { + unic_err(unic_dev, + "failed to set ets, due to network interface is up, pls down it first and try again.\n"); + return -EBUSY; + } + + if (!(unic_dev->dcbx_cap & DCB_CAP_DCBX_VER_IEEE)) + return -EINVAL; + + if (unic_resetting(net_dev)) + return -EBUSY; + + return 0; +} + +static int unic_handle_prio_vl_change(struct unic_dev *unic_dev, + struct ieee_ets *ets, u8 changed) +{ + struct unic_vl *vl = &unic_dev->channels.vl; + u8 map_type; + int ret; + + if (!(changed & UNIC_PRIO_VL_MAP_CHANGED)) + return 0; + + map_type = vl->dscp_app_cnt ? UNIC_DSCP_VL_MAP : UNIC_PRIO_VL_MAP; + ret = unic_set_vl_map(unic_dev, vl->dscp_prio, ets->prio_tc, + map_type); + if (ret) + return ret; + + memcpy(vl->prio_vl, ets->prio_tc, sizeof(ets->prio_tc)); + + return 0; +} + +static inline void unic_convert_vl_sch_bw(struct ubase_caps *caps, u8 *vl_bw, + struct ieee_ets *ets) +{ + u8 i; + + for (i = 0; i < caps->vl_num; i++) { + vl_bw[caps->req_vl[i]] = ets->tc_tx_bw[i]; + vl_bw[caps->resp_vl[i]] = ets->tc_tx_bw[i]; + } +} + +static int unic_handle_tm_vl_sch_change(struct unic_dev *unic_dev, + struct ubase_caps *caps, + struct ieee_ets *ets) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct unic_vl *vl = &unic_dev->channels.vl; + u8 vl_tsa[UBASE_MAX_VL_NUM] = {0}; + u8 vl_bw[UBASE_MAX_VL_NUM] = {0}; + u32 i; + + unic_convert_vl_sch_bw(caps, vl_bw, ets); + + for (i = 0; i < caps->vl_num; i++) { + if (ets->tc_tsa[i]) { + vl_tsa[caps->req_vl[i]] = ets->tc_tsa[i]; + vl_tsa[caps->resp_vl[i]] = ets->tc_tsa[i]; + } + } + + return ubase_config_tm_vl_sch(adev, vl->vl_bitmap, vl_bw, vl_tsa); +} + +static int unic_handle_vl_tsa_bw_change(struct unic_dev *unic_dev, + struct ieee_ets *ets, u8 changed) +{ + u8 *vl_tsa = unic_dev->channels.vl.vl_tsa; + u8 *vl_bw = unic_dev->channels.vl.vl_bw; + int ret; + + struct ubase_caps *caps = ubase_get_dev_caps(unic_dev->comdev.adev); + + if (!(changed & UNIC_TC_TSA_CHANGED || changed & UNIC_TC_BW_CHANGED)) + return 0; + + ret = unic_handle_tm_vl_sch_change(unic_dev, caps, ets); + if (ret) + return ret; + + memcpy(vl_tsa, ets->tc_tsa, sizeof(ets->tc_tsa)); + memcpy(vl_bw, ets->tc_tx_bw, sizeof(ets->tc_tx_bw)); + + return 0; +} + +static int unic_setets_config(struct net_device *ndev, struct ieee_ets *ets, + u8 changed, u8 vl_num) +{ + struct unic_dev *unic_dev = netdev_priv(ndev); + int ret; + + ret = unic_handle_prio_vl_change(unic_dev, ets, changed); + if (ret) + return ret; + + ret = unic_handle_vl_tsa_bw_change(unic_dev, ets, changed); + if (ret) + return ret; + + unic_dev->channels.vl.vl_num = vl_num; + if (unic_rss_vl_num_changed(unic_dev, vl_num)) + return unic_update_channels(unic_dev, vl_num); + + return 0; +} + +static int unic_dcbnl_ieee_setets(struct net_device *ndev, struct ieee_ets *ets) +{ + struct unic_dev *unic_dev = netdev_priv(ndev); + u8 changed = 0; + u8 vl_num = 0; + int ret; + + ret = unic_setets_preconditions(ndev); + if (ret) + return ret; + + ret = unic_setets_params_validate(unic_dev, ets, &changed, &vl_num); + if (ret) + return ret; + + if (!changed) + return 0; + + return unic_setets_config(ndev, ets, changed, vl_num); +} + static int unic_dscp_prio_check(struct net_device *netdev, struct dcb_app *app) { struct unic_dev *unic_dev = netdev_priv(netdev); @@ -147,6 +389,8 @@ static int unic_dcbnl_ieee_delapp(struct net_device *netdev, } static const struct dcbnl_rtnl_ops unic_dcbnl_ops = { + .ieee_getets = unic_dcbnl_ieee_getets, + .ieee_setets = unic_dcbnl_ieee_setets, .ieee_setapp = unic_dcbnl_ieee_setapp, .ieee_delapp = unic_dcbnl_ieee_delapp, }; diff --git a/drivers/net/ub/unic/unic_dev.c b/drivers/net/ub/unic/unic_dev.c index 7ed3525837e9..ef79194c24bb 100644 --- a/drivers/net/ub/unic/unic_dev.c +++ b/drivers/net/ub/unic/unic_dev.c @@ -905,6 +905,13 @@ void unic_remove_period_task(struct unic_dev *unic_dev) cancel_delayed_work_sync(&unic_dev->service_task); } +bool unic_rss_vl_num_changed(struct unic_dev *unic_dev, u8 vl_num) +{ + struct unic_channels *channels = &unic_dev->channels; + + return channels->rss_vl_num != unic_get_rss_vl_num(unic_dev, vl_num); +} + int unic_change_rss_size(struct unic_dev *unic_dev, u32 new_rss_size, u32 org_rss_size) { @@ -932,6 +939,21 @@ int unic_change_rss_size(struct unic_dev *unic_dev, u32 new_rss_size, return ret; } +int unic_update_channels(struct unic_dev *unic_dev, u8 vl_num) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct unic_channels *channels = &unic_dev->channels; + u32 new_rss_size, old_rss_size = channels->rss_size; + + channels->rss_vl_num = unic_get_rss_vl_num(unic_dev, vl_num); + if (old_rss_size * channels->rss_vl_num > unic_channels_max_num(adev)) + new_rss_size = unic_get_max_rss_size(unic_dev); + else + new_rss_size = old_rss_size; + + return unic_change_rss_size(unic_dev, new_rss_size, old_rss_size); +} + static struct net_device *unic_alloc_netdev(struct auxiliary_device *adev) { struct ubase_caps *caps = ubase_get_dev_caps(adev); diff --git a/drivers/net/ub/unic/unic_dev.h b/drivers/net/ub/unic/unic_dev.h index af20b72168ee..a20744b810e8 100644 --- a/drivers/net/ub/unic/unic_dev.h +++ b/drivers/net/ub/unic/unic_dev.h @@ -257,6 +257,7 @@ int unic_init_channels(struct unic_dev *unic_dev, u32 channels_num); void unic_uninit_channels(struct unic_dev *unic_dev); void unic_start_period_task(struct net_device *netdev); void unic_remove_period_task(struct unic_dev *unic_dev); +void unic_update_queue_info(struct unic_dev *unic_dev); int unic_init_wq(void); void unic_destroy_wq(void); u16 unic_cqe_period_round_down(u16 cqe_period); @@ -264,8 +265,10 @@ int unic_init_rx(struct unic_dev *unic_dev, u32 num); int unic_init_tx(struct unic_dev *unic_dev, u32 num); void unic_destroy_rx(struct unic_dev *unic_dev, u32 num); void unic_destroy_tx(struct unic_dev *unic_dev, u32 num); +bool unic_rss_vl_num_changed(struct unic_dev *unic_dev, u8 vl_num); int unic_change_rss_size(struct unic_dev *unic_dev, u32 new_rss_size, u32 org_rss_size); +int unic_update_channels(struct unic_dev *unic_dev, u8 vl_num); int unic_set_vl_map(struct unic_dev *unic_dev, u8 *dscp_prio, u8 *prio_vl, u8 map_type); int unic_dbg_log(void); @@ -357,6 +360,15 @@ static inline u32 unic_read_reg(struct unic_dev *unic_dev, u32 reg) return readl(reg_addr + reg); } +static inline u8 unic_get_rss_vl_num(struct unic_dev *unic_dev, u8 max_vl) +{ + struct auxiliary_device *adev = unic_dev->comdev.adev; + struct ubase_adev_qos *qos = ubase_get_adev_qos(adev); + u8 vl_num = min(UNIC_RSS_MAX_VL_NUM, qos->nic_vl_num); + + return max_vl < vl_num ? max_vl : vl_num; +} + static inline u32 unic_get_sq_cqe_mask(struct unic_dev *unic_dev) { return unic_dev->channels.sq_cqe_depth - 1; diff --git a/include/ub/ubase/ubase_comm_dev.h b/include/ub/ubase/ubase_comm_dev.h index 81b2c98af17f..ac85c20311dd 100644 --- a/include/ub/ubase/ubase_comm_dev.h +++ b/include/ub/ubase/ubase_comm_dev.h @@ -24,6 +24,8 @@ struct iova_slot; #error "UBASE_MAX_VL_NUM can't less than IEEE_8021QAZ_MAX_TCS" #endif +#define UBASE_NIC_MAX_VL_NUM (2) + #define UBASE_SUP_UBL BIT(0) #define UBASE_SUP_ETH BIT(1) #define UBASE_SUP_UNIC BIT(2) -- Gitee From 75193fd9e06eb4a1d19bb3219ce2188f3067c5b1 Mon Sep 17 00:00:00 2001 From: Xiaobo Zhang Date: Mon, 1 Sep 2025 13:39:35 +0800 Subject: [PATCH 200/214] net: unic: support config/query traffic class parameters ANBZ: #45460 commit daea31ba9a6cd73a57dff9769b34b4d3fd673017 openEuler. This patch enhances the UNIC driver's DCB (Data Center Bridging) support by adding the following features: 1. DCBX Mode Configuration: - Introduces `unic_dcbnl_getdcbx` and `unic_dcbnl_setdcbx` functions to retrieve and set the DCBX mode, enabling compatibility with different DCBX versions and management modes. 2. Max Rate Management: - Adds `unic_ieee_getmaxrate` and `unic_ieee_setmaxrate` functions to query and configure the maximum transmission rates for each Traffic Class (TC), ensuring optimal network performance. - Includes `unic_check_maxrate` to validate rate settings, ensuring they fall within supported hardware limits. 3. Integration with DCBnl Interface: - Registers the new functions with the DCBnl netlink interface, allowing users to configure and query DCBX and max rate settings via standard network management tools. These enhancements improve the UNIC driver's ability to manage advanced QoS configurations, providing greater flexibility and control over network traffic prioritization and rate limiting. Signed-off-by: Haiqing Fang Signed-off-by: Xiaobo Zhang Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/unic_dcbnl.c | 93 ++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/drivers/net/ub/unic/unic_dcbnl.c b/drivers/net/ub/unic/unic_dcbnl.c index 2b50901d8434..5f14cebac540 100644 --- a/drivers/net/ub/unic/unic_dcbnl.c +++ b/drivers/net/ub/unic/unic_dcbnl.c @@ -388,11 +388,104 @@ static int unic_dcbnl_ieee_delapp(struct net_device *netdev, return unic_del_app(netdev, app, unic_dev, vl); } +static u8 unic_dcbnl_getdcbx(struct net_device *ndev) +{ + struct unic_dev *unic_dev = netdev_priv(ndev); + + return unic_dev->dcbx_cap; +} + +static u8 unic_dcbnl_setdcbx(struct net_device *ndev, u8 mode) +{ + struct unic_dev *unic_dev = netdev_priv(ndev); + + if ((mode & DCB_CAP_DCBX_LLD_MANAGED) || + (mode & DCB_CAP_DCBX_VER_CEE) || + !(mode & DCB_CAP_DCBX_HOST)) + return 1; + + unic_info(unic_dev, "set dcbx mode = %u\n.", mode); + + unic_dev->dcbx_cap = mode; + + return 0; +} + +static int unic_ieee_getmaxrate(struct net_device *ndev, + struct ieee_maxrate *maxrate) +{ + struct unic_dev *unic_dev = netdev_priv(ndev); + + if (!unic_dev_ets_supported(unic_dev) || + !unic_dev_tc_speed_limit_supported(unic_dev)) + return -EOPNOTSUPP; + + memcpy(maxrate->tc_maxrate, unic_dev->channels.vl.vl_maxrate, + sizeof(maxrate->tc_maxrate)); + return 0; +} + +static int unic_check_maxrate(struct unic_dev *unic_dev, + struct ieee_maxrate *maxrate) +{ + u32 max_speed = unic_dev->hw.mac.max_speed; + int i; + + for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { + if (!maxrate->tc_maxrate[i]) + continue; + + if (maxrate->tc_maxrate[i] / UNIC_MBYTE_PER_SEND > max_speed || + maxrate->tc_maxrate[i] < UNIC_MBYTE_PER_SEND) { + unic_err(unic_dev, + "invalid max_rate(%llubit), the range is [1Mbit, %uMbit].\n", + maxrate->tc_maxrate[i] * BITS_PER_BYTE, + max_speed); + return -EINVAL; + } + } + + return 0; +} + +static int unic_ieee_setmaxrate(struct net_device *ndev, + struct ieee_maxrate *maxrate) +{ + struct unic_dev *unic_dev = netdev_priv(ndev); + struct unic_vl *vl = &unic_dev->channels.vl; + int ret; + + if (!unic_dev_ets_supported(unic_dev) || + !unic_dev_tc_speed_limit_supported(unic_dev)) + return -EOPNOTSUPP; + + if (!(unic_dev->dcbx_cap & DCB_CAP_DCBX_VER_IEEE)) + return -EINVAL; + + ret = unic_check_maxrate(unic_dev, maxrate); + if (ret) + return ret; + + ret = unic_config_vl_rate_limit(unic_dev, maxrate->tc_maxrate, + vl->vl_bitmap); + if (ret) + return ret; + + memcpy(vl->vl_maxrate, maxrate->tc_maxrate, + sizeof(maxrate->tc_maxrate)); + + return 0; +} + static const struct dcbnl_rtnl_ops unic_dcbnl_ops = { .ieee_getets = unic_dcbnl_ieee_getets, .ieee_setets = unic_dcbnl_ieee_setets, + .ieee_getmaxrate = unic_ieee_getmaxrate, + .ieee_setmaxrate = unic_ieee_setmaxrate, .ieee_setapp = unic_dcbnl_ieee_setapp, .ieee_delapp = unic_dcbnl_ieee_delapp, + .getdcbx = unic_dcbnl_getdcbx, + .setdcbx = unic_dcbnl_setdcbx, }; void unic_set_dcbnl_ops(struct net_device *netdev) -- Gitee From 742dbc76d2b9bb19337c3645e846676c91c410e6 Mon Sep 17 00:00:00 2001 From: Haibin Lu Date: Tue, 16 Sep 2025 16:30:21 +0800 Subject: [PATCH 201/214] net: unic: support subscribes to the RX stream stop and recovery interface. ANBZ: #45460 commit 58daddb6f308b8a305b2fe3b3d35065b150aac4e openEuler. The ub system allows the aux driver to deliver the deactivate/activate dev command to ensure system robustness. The deactivate/activate command requires close cooperation between components. This patch allows the unic to complete its configuration when deactivate or deactivate. Signed-off-by: Zhenyu Wan Signed-off-by: Xiaobo Zhang Signed-off-by: Haibin Lu Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/unic_event.c | 101 +++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/drivers/net/ub/unic/unic_event.c b/drivers/net/ub/unic/unic_event.c index faa91a99badc..1785e0aad7f1 100644 --- a/drivers/net/ub/unic/unic_event.c +++ b/drivers/net/ub/unic/unic_event.c @@ -53,6 +53,105 @@ int unic_comp_handler(struct notifier_block *nb, unsigned long jfcn, void *data) return 0; } +static void unic_activate_event_process(struct unic_dev *unic_dev) +{ + struct unic_act_info *act_info = &unic_dev->act_info; + struct net_device *netdev = unic_dev->comdev.netdev; + int ret; + + if (test_bit(UNIC_STATE_DISABLED, &unic_dev->state)) { + unic_err(unic_dev, + "failed to process activate event, device is not ready.\n"); + return; + } + + rtnl_lock(); + + if (!test_bit(UNIC_STATE_DEACTIVATE, &unic_dev->state)) + goto unlock; + + /* if network interface has already been stopped, + * no need to open by activate event + */ + if (test_bit(UNIC_STATE_DOWN, &unic_dev->state)) + goto out; + + ret = unic_net_open_no_link_change(netdev); + if (ret) + unic_warn(unic_dev, "failed to open net, ret = %d.\n", ret); + + ret = unic_activate_promisc_mode(unic_dev, true); + if (ret) + unic_warn(unic_dev, "failed to open promisc, ret = %d.\n", ret); + else + clear_bit(UNIC_VPORT_STATE_PROMISC_CHANGE, &unic_dev->vport.state); + +out: + mutex_lock(&act_info->mutex); + act_info->deactivate = false; + mutex_unlock(&act_info->mutex); + clear_bit(UNIC_STATE_DEACTIVATE, &unic_dev->state); +unlock: + rtnl_unlock(); +} + +static void unic_deactivate_event_process(struct unic_dev *unic_dev) +{ + struct unic_act_info *act_info = &unic_dev->act_info; + struct net_device *netdev = unic_dev->comdev.netdev; + int ret; + + if (test_bit(UNIC_STATE_DISABLED, &unic_dev->state)) { + unic_err(unic_dev, + "failed to process deactivate event, device is not ready.\n"); + return; + } + + rtnl_lock(); + + if (test_bit(UNIC_STATE_DEACTIVATE, &unic_dev->state)) + goto unlock; + + /* when deactivate event occurs, set flag to true to prevent + * periodic tasks changing promisc + */ + mutex_lock(&act_info->mutex); + act_info->deactivate = true; + mutex_unlock(&act_info->mutex); + + ret = unic_activate_promisc_mode(unic_dev, false); + if (ret) + unic_warn(unic_dev, "failed to close promisc, ret = %d.\n", ret); + else + set_bit(UNIC_VPORT_STATE_PROMISC_CHANGE, &unic_dev->vport.state); + + /* if network interface has already been stopped, + * no need to stop again by deactivate event + */ + if (test_bit(UNIC_STATE_DOWN, &unic_dev->state)) + goto out; + + unic_net_stop_no_link_change(netdev); + +out: + set_bit(UNIC_STATE_DEACTIVATE, &unic_dev->state); +unlock: + rtnl_unlock(); +} + +static void unic_activate_handler(struct auxiliary_device *adev, bool activate) +{ + struct unic_dev *unic_dev = dev_get_drvdata(&adev->dev); + + unic_info(unic_dev, "receive %s event callback.\n", + activate ? "activate" : "deactivate"); + + if (activate) + unic_activate_event_process(unic_dev); + else + unic_deactivate_event_process(unic_dev); +} + static void unic_rack_port_reset(struct unic_dev *unic_dev, bool link_up) { if (link_up) @@ -229,6 +328,7 @@ int unic_register_event(struct auxiliary_device *adev) ubase_port_register(adev, unic_port_handler); ubase_reset_register(adev, unic_reset_handler); + ubase_activate_register(adev, unic_activate_handler); return 0; @@ -242,6 +342,7 @@ int unic_register_event(struct auxiliary_device *adev) void unic_unregister_event(struct auxiliary_device *adev) { + ubase_activate_unregister(adev); ubase_reset_unregister(adev); ubase_port_unregister(adev); unic_unregister_ctrlq_event(adev, ARRAY_SIZE(unic_ctrlq_events)); -- Gitee From 57693845c93c8c52cb69a36da9156aaedd9e7267 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Sat, 23 Aug 2025 15:53:06 +0800 Subject: [PATCH 202/214] net: unic: Debugfs supports querying IP specifications and IP entries. ANBZ: #45460 commit 48ffd57b4028d48472921c13d77630dc7f5b8777 openEuler. This patch adds the ability to query IP table entries and their specifications. Users can use standard tools to query IP table entries and their specifications. Signed-off-by: Xiongchuan Zhou Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/Makefile | 2 +- drivers/net/ub/unic/debugfs/unic_debugfs.c | 19 ++++++ drivers/net/ub/unic/debugfs/unic_debugfs.h | 1 + .../net/ub/unic/debugfs/unic_entry_debugfs.c | 59 +++++++++++++++++++ .../net/ub/unic/debugfs/unic_entry_debugfs.h | 16 +++++ 5 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ub/unic/debugfs/unic_entry_debugfs.c create mode 100644 drivers/net/ub/unic/debugfs/unic_entry_debugfs.h diff --git a/drivers/net/ub/unic/Makefile b/drivers/net/ub/unic/Makefile index fa5de4255e68..041815219e0c 100644 --- a/drivers/net/ub/unic/Makefile +++ b/drivers/net/ub/unic/Makefile @@ -9,5 +9,5 @@ ccflags-y += -I$(srctree)/drivers/net/ub/unic/debugfs obj-$(CONFIG_UB_UNIC) += unic.o unic-objs = unic_main.o unic_ethtool.o unic_hw.o unic_guid.o unic_netdev.o unic_dev.o unic_qos_hw.o unic_event.o unic_crq.o unic-objs += unic_channel.o debugfs/unic_debugfs.o unic_rx.o unic_tx.o unic_txrx.o unic_comm_addr.o unic_rack_ip.o unic_stats.o -unic-objs += debugfs/unic_ctx_debugfs.o unic_reset.o debugfs/unic_qos_debugfs.o +unic-objs += debugfs/unic_ctx_debugfs.o unic_reset.o debugfs/unic_qos_debugfs.o debugfs/unic_entry_debugfs.o unic-$(CONFIG_UB_UNIC_DCB) += unic_dcbnl.o diff --git a/drivers/net/ub/unic/debugfs/unic_debugfs.c b/drivers/net/ub/unic/debugfs/unic_debugfs.c index f9cc6e887ce0..63703934613d 100644 --- a/drivers/net/ub/unic/debugfs/unic_debugfs.c +++ b/drivers/net/ub/unic/debugfs/unic_debugfs.c @@ -14,6 +14,7 @@ #include "unic_dev.h" #include "unic_hw.h" #include "unic_qos_debugfs.h" +#include "unic_entry_debugfs.h" #include "unic_debugfs.h" static int unic_dbg_dump_dev_info(struct seq_file *s, void *data) @@ -258,6 +259,10 @@ static bool unic_dbg_dentry_support(struct device *dev, u32 property) static struct ubase_dbg_dentry_info unic_dbg_dentry[] = { { + .name = "ip_tbl", + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + }, { .name = "context", .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, .support = unic_dbg_dentry_support, @@ -276,6 +281,20 @@ static struct ubase_dbg_dentry_info unic_dbg_dentry[] = { static struct ubase_dbg_cmd_info unic_dbg_cmd[] = { { + .name = "ip_tbl_spec", + .dentry_index = UNIC_DBG_DENTRY_IP, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_ip_tbl_spec, + }, { + .name = "ip_tbl_list", + .dentry_index = UNIC_DBG_DENTRY_IP, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_ip_tbl_list, + }, { .name = "jfs_context", .dentry_index = UNIC_DBG_DENTRY_CONTEXT, .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, diff --git a/drivers/net/ub/unic/debugfs/unic_debugfs.h b/drivers/net/ub/unic/debugfs/unic_debugfs.h index 065630fbd35e..73a75b091b4b 100644 --- a/drivers/net/ub/unic/debugfs/unic_debugfs.h +++ b/drivers/net/ub/unic/debugfs/unic_debugfs.h @@ -13,6 +13,7 @@ #define unic_get_ubase_root_dentry(adev) ubase_diag_debugfs_root(adev) enum unic_dbg_dentry_type { + UNIC_DBG_DENTRY_IP = 0, UNIC_DBG_DENTRY_CONTEXT, UNIC_DBG_DENTRY_QOS, /* must be the last entry. */ diff --git a/drivers/net/ub/unic/debugfs/unic_entry_debugfs.c b/drivers/net/ub/unic/debugfs/unic_entry_debugfs.c new file mode 100644 index 000000000000..74b5fdb95aaa --- /dev/null +++ b/drivers/net/ub/unic/debugfs/unic_entry_debugfs.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#include +#include + +#include "unic_comm_addr.h" +#include "unic_dev.h" +#include "unic_debugfs.h" +#include "unic_entry_debugfs.h" + +static const char * const unic_entry_state_str[] = { + "TO_ADD", "TO_DEL", "ACTIVE" +}; + +int unic_dbg_dump_ip_tbl_spec(struct seq_file *s, void *data) +{ + struct unic_dev *unic_dev = dev_get_drvdata(s->private); + u32 total_ip_tbl_size, total_ue_num; + struct ubase_caps *ubase_caps; + + ubase_caps = ubase_get_dev_caps(unic_dev->comdev.adev); + total_ue_num = ubase_caps->total_ue_num; + total_ip_tbl_size = unic_dev->caps.total_ip_tbl_size; + + seq_printf(s, "total_ue_num\t: %u\n", total_ue_num); + seq_printf(s, "total_ip_tbl_size\t: %u\n", total_ip_tbl_size); + + return 0; +} + +int unic_dbg_dump_ip_tbl_list(struct seq_file *s, void *data) +{ + struct unic_dev *unic_dev = dev_get_drvdata(s->private); + struct unic_comm_addr_node *ip_node; + struct unic_addr_tbl *ip_tbl; + struct list_head *list; + u16 i = 0; + + seq_printf(s, "No %-43sSTATE IP_MASK\n", "IP_ADDR"); + + ip_tbl = &unic_dev->vport.addr_tbl; + list = &ip_tbl->ip_list; + spin_lock_bh(&ip_tbl->ip_list_lock); + list_for_each_entry(ip_node, list, node) { + seq_printf(s, "%-4d", i++); + seq_printf(s, "%-43pI6c", &ip_node->ip_addr.s6_addr); + seq_printf(s, "%-9s", unic_entry_state_str[ip_node->state]); + seq_printf(s, "%-3u", ip_node->node_mask); + + seq_puts(s, "\n"); + } + spin_unlock_bh(&ip_tbl->ip_list_lock); + + return 0; +} diff --git a/drivers/net/ub/unic/debugfs/unic_entry_debugfs.h b/drivers/net/ub/unic/debugfs/unic_entry_debugfs.h new file mode 100644 index 000000000000..73ab85f4d5f3 --- /dev/null +++ b/drivers/net/ub/unic/debugfs/unic_entry_debugfs.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef __UNIC_ENTRY_DEBUGFS_H__ +#define __UNIC_ENTRY_DEBUGFS_H__ + +#include +#include + +int unic_dbg_dump_ip_tbl_spec(struct seq_file *s, void *data); +int unic_dbg_dump_ip_tbl_list(struct seq_file *s, void *data); + +#endif /* _UNIC_ENTRY_DEBUGFS_H */ -- Gitee From 9feb297ffdd8629aacd05d13a0bfdedace1d1814 Mon Sep 17 00:00:00 2001 From: Fengyan Mu Date: Thu, 20 Nov 2025 10:20:15 +0800 Subject: [PATCH 203/214] ub: ubase: Fix priqos infomaton interface functions to query and check sl_bitmap ANBZ: #45460 commit a2e12d759c3b3669b6318ab54457559d2cd1679e openEuler. Previously, in the function 'ubase_get_priqos_info', the input parameter 'sl_priqos->sl_bitmap' was assumed to be provided by the caller. However, the caller doesn't know which SLs can be used. Therefore, this patch sets 'sl_priqos->sl_bitmap' to the union of SLs supported by UNIC and UDMA. And, in the function 'ubase_set_priqos_info', a check process for the input parameter 'sl_priqos->sl_bitmap' is added to ensure that the corresponding SLs in sl_bitmap are those supported by UNIC or UDMA. Fixes: 38f3f5d80a6d ("ub: ubase: Added QoS and traffic management debugging features") Signed-off-by: Zhang Lei Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_dev.h | 1 - drivers/ub/ubase/ubase_qos_hw.c | 36 ++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/drivers/ub/ubase/ubase_dev.h b/drivers/ub/ubase/ubase_dev.h index d32d9fb98377..c8ccd5bd107a 100644 --- a/drivers/ub/ubase/ubase_dev.h +++ b/drivers/ub/ubase/ubase_dev.h @@ -429,7 +429,6 @@ static inline u32 ubase_ta_timer_align_size(struct ubase_dev *udev) static inline bool ubase_mbx_ue_id_is_valid(u16 mbx_ue_id, struct ubase_dev *udev) { - if (!mbx_ue_id || (mbx_ue_id > udev->caps.dev_caps.ue_num - 1)) return false; diff --git a/drivers/ub/ubase/ubase_qos_hw.c b/drivers/ub/ubase/ubase_qos_hw.c index 5a5881f79547..bfd5c4e0f5c1 100644 --- a/drivers/ub/ubase/ubase_qos_hw.c +++ b/drivers/ub/ubase/ubase_qos_hw.c @@ -409,6 +409,36 @@ int ubase_query_fst_fvt_rqmt(struct ubase_dev *udev, return ret; } +static unsigned long ubase_get_sl_bitmap(struct ubase_dev *udev) +{ + struct ubase_adev_qos *qos = &udev->qos; + unsigned long sl_bitmap = 0; + u8 i; + + for (i = 0; i < qos->nic_sl_num; i++) + sl_bitmap |= 1 << qos->nic_sl[i]; + for (i = 0; i < qos->sl_num; i++) + sl_bitmap |= 1 << qos->sl[i]; + + return sl_bitmap; +} + +static int ubase_check_sl_bitmap(struct ubase_dev *udev, unsigned long sl_bitmap) +{ + unsigned long sl_bitmap_cap; + u8 i; + + sl_bitmap_cap = ubase_get_sl_bitmap(udev); + for (i = 0; i < UBASE_MAX_SL_NUM; i++) { + if (!test_bit(i, &sl_bitmap)) + continue; + if (!test_bit(i, &sl_bitmap_cap)) + return -EINVAL; + } + + return 0; +} + int ubase_check_qos_sch_param(struct auxiliary_device *adev, u16 vl_bitmap, u8 *vl_bw, u8 *vl_tsa, bool is_ets) { @@ -447,6 +477,9 @@ int ubase_set_priqos_info(struct device *dev, struct ubase_sl_priqos *sl_priqos) udev = dev_get_drvdata(dev); + if (ubase_check_sl_bitmap(udev, sl_priqos->sl_bitmap)) + return -EINVAL; + if (sl_priqos->port_bitmap) return ubase_set_ets_priqos(udev, sl_priqos); @@ -458,11 +491,12 @@ int ubase_get_priqos_info(struct device *dev, struct ubase_sl_priqos *sl_priqos) { struct ubase_dev *udev; - if (!dev || !sl_priqos || !sl_priqos->sl_bitmap) + if (!dev || !sl_priqos) return -EINVAL; udev = dev_get_drvdata(dev); + sl_priqos->sl_bitmap = ubase_get_sl_bitmap(udev); if (sl_priqos->port_bitmap) return ubase_get_ets_priqos(udev, sl_priqos); -- Gitee From 91a432172699e6532a6d9fcabaffe85e45aaba24 Mon Sep 17 00:00:00 2001 From: Fengyan Mu Date: Thu, 20 Nov 2025 10:34:28 +0800 Subject: [PATCH 204/214] ub: ubase: add CMDQ&CTRLQ compatibility code ANBZ: #45460 commit 313470c59d7f1158d832074319e35b823e4ccd29 openEuler. The ubase driver and firmware communicate through the CMDQ interface, which needs to be forward and backward compatible. When pairing new drivers with old firmware or old drivers with new firmware, it is essential to ensure that the program can still work. Therefore, some compatibility design needs to be done in advance, and the relevant interfaces should be implemented. In subsequent versions, the predefined compatibility plan should be followed to handle these scenarios. Specifically, during the ubase dev probe, the driver first queries the firmware for the chip version using a message with opcode 0x0001. It then informs the firmware of its supported feature capability set through a message with opcode 0x0007. Subsequently, it queries the firmware for the feature capability sets supported by both the chip and the firmware using a message with opcode 0x0030. These commands could ensure backward and forward compatibility. In the CTRLQ interface, the version number check is addedd. If the version is not supported or the command is not supported, an error response of EOPNOTSUPP is returned. In the CMDQ interface, we removed some obsolete capability bit definitions and interface parameters. And optimized the code for handling scenarios where firmware returns an unauthorized error. In the CTRLQ interface, we add an version number check for requests coming from the control plane. By the way, we have also optimized the parameters of some commands. Fixes: 8d68017f37fa ("ub: ubase: support for command process") Signed-off-by: Chuan Wu Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/debugfs/unic_debugfs.c | 1 - drivers/net/ub/unic/unic.h | 5 --- drivers/net/ub/unic/unic_cmd.h | 2 +- drivers/net/ub/unic/unic_dev.c | 21 ++++------- drivers/net/ub/unic/unic_dev.h | 5 --- drivers/net/ub/unic/unic_ethtool.c | 3 -- drivers/net/ub/unic/unic_hw.c | 14 +++---- drivers/net/ub/unic/unic_qos_hw.c | 3 +- drivers/net/ub/unic/unic_rack_ip.c | 7 +++- drivers/net/ub/unic/unic_stats.c | 29 ++++++-------- drivers/ub/ubase/debugfs/ubase_debugfs.c | 10 +---- drivers/ub/ubase/ubase_cmd.c | 16 ++++---- drivers/ub/ubase/ubase_cmd.h | 18 ++++++--- drivers/ub/ubase/ubase_ctrlq.c | 41 +++++++++++++++++--- drivers/ub/ubase/ubase_dev.c | 22 ++++++++--- drivers/ub/ubase/ubase_hw.c | 10 ++--- drivers/ub/ubase/ubase_hw.h | 44 +++++++++------------- drivers/ub/ubase/ubase_qos_hw.c | 9 ++--- include/ub/ubase/ubase_comm_cmd.h | 1 + include/ub/ubase/ubase_comm_dev.h | 5 --- 20 files changed, 132 insertions(+), 134 deletions(-) diff --git a/drivers/net/ub/unic/debugfs/unic_debugfs.c b/drivers/net/ub/unic/debugfs/unic_debugfs.c index 63703934613d..0a6dbdaffedc 100644 --- a/drivers/net/ub/unic/debugfs/unic_debugfs.c +++ b/drivers/net/ub/unic/debugfs/unic_debugfs.c @@ -46,7 +46,6 @@ static const struct unic_dbg_cap_bit_info { {"\tsupport_ubl: %u\n", &unic_dev_ubl_supported}, {"\tsupport_ets: %u\n", &unic_dev_ets_supported}, {"\tsupport_fec: %u\n", &unic_dev_fec_supported}, - {"\tsupport_rss: %u\n", &unic_dev_rss_supported}, {"\tsupport_tc_speed_limit: %u\n", &unic_dev_tc_speed_limit_supported}, {"\tsupport_tx_csum_offload: %u\n", &unic_dev_tx_csum_offload_supported}, {"\tsupport_rx_csum_offload: %u\n", &unic_dev_rx_csum_offload_supported}, diff --git a/drivers/net/ub/unic/unic.h b/drivers/net/ub/unic/unic.h index 7f6572c50a0c..e63ee6e900ff 100644 --- a/drivers/net/ub/unic/unic.h +++ b/drivers/net/ub/unic/unic.h @@ -20,16 +20,11 @@ enum { UNIC_SUPPORT_ETS_B = 1, UNIC_SUPPORT_FEC_B = 2, UNIC_SUPPORT_PAUSE_B = 3, - UNIC_SUPPORT_GRO_B = 5, UNIC_SUPPORT_ETH_B = 7, - UNIC_SUPPORT_TSO_B = 8, - UNIC_SUPPORT_RSS_B = 9, UNIC_SUPPORT_SERIAL_SERDES_LB_B = 10, UNIC_SUPPORT_TC_SPEED_LIMIT_B = 12, UNIC_SUPPORT_TX_CSUM_OFFLOAD_B = 13, - UNIC_SUPPORT_TUNNEL_CSUM_OFFLOAD_B = 14, - UNIC_SUPPORT_PTP_B = 15, UNIC_SUPPORT_RX_CSUM_OFFLOAD_B = 16, UNIC_SUPPORT_APP_LB_B = 17, diff --git a/drivers/net/ub/unic/unic_cmd.h b/drivers/net/ub/unic/unic_cmd.h index bf3e11e343cd..125802234e6b 100644 --- a/drivers/net/ub/unic/unic_cmd.h +++ b/drivers/net/ub/unic/unic_cmd.h @@ -150,7 +150,7 @@ struct unic_config_vl_map_cmd { }; struct unic_config_vl_speed_cmd { - __le16 bus_ue_id; + u8 resv0[2]; __le16 vl_bitmap; __le32 max_speed[UBASE_MAX_VL_NUM]; u8 resv1[20]; diff --git a/drivers/net/ub/unic/unic_dev.c b/drivers/net/ub/unic/unic_dev.c index ef79194c24bb..f8d5676bfc1f 100644 --- a/drivers/net/ub/unic/unic_dev.c +++ b/drivers/net/ub/unic/unic_dev.c @@ -248,10 +248,12 @@ static int unic_init_vl_info(struct unic_dev *unic_dev) return ret; ret = unic_init_vl_maxrate(unic_dev); - if (ret) + if (ret && ret != -EPERM) return ret; - return unic_init_vl_sch(unic_dev); + ret = unic_init_vl_sch(unic_dev); + + return ret == -EPERM ? 0 : ret; } static int unic_init_channels_attr(struct unic_dev *unic_dev) @@ -559,17 +561,12 @@ static int unic_dev_init_mtu(struct unic_dev *unic_dev) { struct net_device *netdev = unic_dev->comdev.netdev; struct unic_caps *caps = &unic_dev->caps; - int ret; netdev->mtu = UB_DATA_LEN; netdev->max_mtu = caps->max_trans_unit; netdev->min_mtu = caps->min_trans_unit; - ret = unic_config_mtu(unic_dev, netdev->mtu); - if (ret == -EPERM) - return 0; - - return ret; + return unic_config_mtu(unic_dev, netdev->mtu); } static int unic_init_mac(struct unic_dev *unic_dev) @@ -583,11 +580,11 @@ static int unic_init_mac(struct unic_dev *unic_dev) ret = unic_set_mac_speed_duplex(unic_dev, mac->speed, mac->duplex, mac->lanes); - if (ret && ret != -EPERM) + if (ret) return ret; ret = unic_set_mac_autoneg(unic_dev, mac->autoneg); - if (ret && ret != -EPERM) + if (ret) return ret; ret = unic_dev_fec_supported(unic_dev) && mac->user_fec_mode ? @@ -621,9 +618,7 @@ int unic_set_mtu(struct unic_dev *unic_dev, int new_mtu) new_mtu = max(new_mtu, UB_DATA_LEN); ret = unic_check_validate_dump_mtu(unic_dev, new_mtu, &max_frame_size); - if (ret == -EPERM) { - return 0; - } else if (ret < 0) { + if (ret) { unic_err(unic_dev, "invalid MTU(%d), please check, ret = %d.\n", new_mtu, ret); return -EINVAL; diff --git a/drivers/net/ub/unic/unic_dev.h b/drivers/net/ub/unic/unic_dev.h index a20744b810e8..51708850e38d 100644 --- a/drivers/net/ub/unic/unic_dev.h +++ b/drivers/net/ub/unic/unic_dev.h @@ -293,11 +293,6 @@ static inline bool unic_dev_fec_supported(struct unic_dev *unic_dev) return unic_get_cap_bit(unic_dev, UNIC_SUPPORT_FEC_B); } -static inline bool unic_dev_rss_supported(struct unic_dev *unic_dev) -{ - return unic_get_cap_bit(unic_dev, UNIC_SUPPORT_RSS_B); -} - static inline bool unic_dev_tc_speed_limit_supported(struct unic_dev *unic_dev) { return unic_get_cap_bit(unic_dev, UNIC_SUPPORT_TC_SPEED_LIMIT_B); diff --git a/drivers/net/ub/unic/unic_ethtool.c b/drivers/net/ub/unic/unic_ethtool.c index c9593ba74fe4..22f530c45107 100644 --- a/drivers/net/ub/unic/unic_ethtool.c +++ b/drivers/net/ub/unic/unic_ethtool.c @@ -67,9 +67,6 @@ static int unic_get_fecparam(struct net_device *ndev, struct unic_dev *unic_dev = netdev_priv(ndev); struct unic_mac *mac = &unic_dev->hw.mac; - if (!unic_dev_fec_supported(unic_dev)) - return -EOPNOTSUPP; - fec->fec = mac->fec_ability; fec->active_fec = mac->fec_mode; diff --git a/drivers/net/ub/unic/unic_hw.c b/drivers/net/ub/unic/unic_hw.c index d2b7026514c4..be606bfb6495 100644 --- a/drivers/net/ub/unic/unic_hw.c +++ b/drivers/net/ub/unic/unic_hw.c @@ -76,7 +76,7 @@ int unic_set_mac_autoneg(struct unic_dev *unic_dev, u8 autoneg) sizeof(req), &req); ret = ubase_cmd_send_in(unic_dev->comdev.adev, &in); - if (ret && ret != -EPERM) + if (ret) dev_err(unic_dev->comdev.adev->dev.parent, "failed to send cmd in config autoneg(%u), ret = %d.\n", autoneg, ret); @@ -105,7 +105,7 @@ int unic_set_mac_speed_duplex(struct unic_dev *unic_dev, u32 speed, u8 duplex, sizeof(req), &req); ret = ubase_cmd_send_in(unic_dev->comdev.adev, &in); - if (ret && ret != -EPERM) + if (ret) dev_err(unic_dev->comdev.adev->dev.parent, "failed to send cmd in config speed(%u), ret = %d.\n", speed, ret); @@ -334,9 +334,7 @@ int unic_set_promisc_mode(struct unic_dev *unic_dev, time_out = unic_cmd_timeout(unic_dev); ret = ubase_cmd_send_in_ex(adev, &in, time_out); - if (ret == -EPERM) - return 0; - else if (ret) + if (ret) unic_err(unic_dev, "failed to set promisc mode, ret = %d.\n", ret); @@ -561,15 +559,15 @@ static int unic_query_flush_status(struct unic_dev *unic_dev, u8 *status) struct ubase_cmd_buf in; int ret; + if (unic_dev_ubl_supported(unic_dev)) + return -EOPNOTSUPP; + ubase_fill_inout_buf(&in, UBASE_OPC_QUERY_FLUSH_STATUS, true, 0, NULL); ubase_fill_inout_buf(&out, UBASE_OPC_QUERY_FLUSH_STATUS, false, sizeof(resp), &resp); ret = ubase_cmd_send_inout(unic_dev->comdev.adev, &in, &out); if (ret) { - if (ret == -EPERM) - return -EOPNOTSUPP; - unic_err(unic_dev, "failed to send cmd when query flush status, ret = %d.\n", ret); diff --git a/drivers/net/ub/unic/unic_qos_hw.c b/drivers/net/ub/unic/unic_qos_hw.c index 79fb7271c08e..bba05964156b 100644 --- a/drivers/net/ub/unic/unic_qos_hw.c +++ b/drivers/net/ub/unic/unic_qos_hw.c @@ -59,7 +59,6 @@ int unic_config_vl_rate_limit(struct unic_dev *unic_dev, u64 *vl_maxrate, u32 vl_rate; int i, ret; - req.bus_ue_id = cpu_to_le16(USHRT_MAX); req.vl_bitmap = cpu_to_le16(vl_bitmap); for (i = 0; i < caps->vl_num; i++) { vl_rate = vl_maxrate[i] / UNIC_MBYTE_PER_SEND; @@ -72,7 +71,7 @@ int unic_config_vl_rate_limit(struct unic_dev *unic_dev, u64 *vl_maxrate, sizeof(req), &req); ret = ubase_cmd_send_in(adev, &in); - if (ret) + if (ret && ret != -EPERM) dev_err(adev->dev.parent, "failed to config vl rate limit, ret = %d.\n", ret); diff --git a/drivers/net/ub/unic/unic_rack_ip.c b/drivers/net/ub/unic/unic_rack_ip.c index f2ff97304e78..529856dcff73 100644 --- a/drivers/net/ub/unic/unic_rack_ip.c +++ b/drivers/net/ub/unic/unic_rack_ip.c @@ -41,7 +41,7 @@ static void unic_update_rack_addr_state(struct unic_vport *vport, addr_node->state = UNIC_COMM_ADDR_TO_ADD; unic_format_masked_ip_addr(format_masked_ip_addr, addr); unic_info(unic_dev, - "deleted an existing ip %s by accident and need to add it.\n", + "stack deleted an planned ip %s, need to re-add it.\n", format_masked_ip_addr); } break; @@ -90,7 +90,7 @@ static int unic_update_stack_ip_addr(struct unic_vport *vport, list_add_tail(&addr_node->node, list); unic_format_masked_ip_addr(format_masked_ip_addr, addr); unic_info(unic_dev, - "added a new ip %s by accident and need to delete it.\n", + "stack added a non-planned ip %s, need to delete it.\n", format_masked_ip_addr); set_bit(UNIC_VPORT_STATE_IP_TBL_CHANGE, &vport->state); goto unlock_and_exit; @@ -469,6 +469,9 @@ int unic_handle_notify_ip_event(struct auxiliary_device *adev, u8 service_ver, struct unic_stack_ip_info st_ip; int ret; + if (service_ver != UBASE_CTRLQ_SER_VER_01) + return -EOPNOTSUPP; + if (len < sizeof(*req)) { unic_err(priv, "failed to verify ip info size, len = %u.\n", len); return -EINVAL; diff --git a/drivers/net/ub/unic/unic_stats.c b/drivers/net/ub/unic/unic_stats.c index dcefeab1bb2d..d8ccd1876500 100644 --- a/drivers/net/ub/unic/unic_stats.c +++ b/drivers/net/ub/unic/unic_stats.c @@ -88,7 +88,7 @@ static int unic_get_dfx_reg_num(struct unic_dev *unic_dev, u32 *reg_num, ubase_fill_inout_buf(&out, UBASE_OPC_DFX_REG_NUM, true, reg_arr_size * sizeof(u32), reg_num); ret = ubase_cmd_send_inout(unic_dev->comdev.adev, &in, &out); - if (ret && ret != -EPERM) + if (ret) unic_err(unic_dev, "failed to query dfx reg num, ret = %d.\n", ret); @@ -149,17 +149,13 @@ int unic_get_regs_len(struct net_device *netdev) return -ENOMEM; ret = unic_get_dfx_reg_num(unic_dev, reg_num, reg_arr_size); - if (!ret) { - count += unic_get_dfx_regs_len(unic_dev, unic_dfx_reg_arr, - reg_arr_size, reg_num); - } else if (ret != -EPERM) { - unic_err(unic_dev, - "failed to get dfx regs length, ret = %d.\n", ret); + if (ret) { kfree(reg_num); - return -EBUSY; } + count += unic_get_dfx_regs_len(unic_dev, unic_dfx_reg_arr, + reg_arr_size, reg_num); kfree(reg_num); return count; @@ -286,17 +282,16 @@ void unic_get_regs(struct net_device *netdev, struct ethtool_regs *cmd, pdata += unic_get_res_regs(unic_dev, pdata); ret = unic_get_dfx_reg_num(unic_dev, reg_num, reg_arr_size); - if (!ret) { - ret = unic_get_dfx_regs(unic_dev, pdata, unic_dfx_reg_arr, - reg_arr_size, reg_num); - if (ret) - unic_err(unic_dev, - "failed to get dfx regs, ret = %d.\n", ret); - } else if (ret != -EPERM) { - unic_err(unic_dev, - "failed to get dfx reg num, ret = %d.\n", ret); + if (ret) { + kfree(reg_num); + return; } + ret = unic_get_dfx_regs(unic_dev, pdata, unic_dfx_reg_arr, + reg_arr_size, reg_num); + if (ret) + unic_err(unic_dev, "failed to get dfx regs, ret = %d.\n", ret); + kfree(reg_num); } diff --git a/drivers/ub/ubase/debugfs/ubase_debugfs.c b/drivers/ub/ubase/debugfs/ubase_debugfs.c index ad97d7a58188..bf49fc3fdc93 100644 --- a/drivers/ub/ubase/debugfs/ubase_debugfs.c +++ b/drivers/ub/ubase/debugfs/ubase_debugfs.c @@ -94,6 +94,7 @@ static void ubase_dbg_dump_caps_info(struct seq_file *s, struct ubase_dev *udev) {"\tdie_id: %u\n", dev_caps->die_id}, {"\tue_id: %u\n", dev_caps->ue_id}, {"\tnl_id: %u\n", dev_caps->nl_id}, + {"\tfw_version: %u\n", dev_caps->fw_version}, }; int i; @@ -123,29 +124,20 @@ static void ubase_dbg_dump_adev_caps(struct seq_file *s, u32 caps_info; } ubase_adev_caps_info[] = { {"\tjfs_max_cnt: %u\n", caps->jfs.max_cnt}, - {"\tjfs_reserved_cnt: %u\n", caps->jfs.reserved_cnt}, {"\tjfs_depth: %u\n", caps->jfs.depth}, {"\tjfr_max_cnt: %u\n", caps->jfr.max_cnt}, - {"\tjfr_reserved_cnt: %u\n", caps->jfr.reserved_cnt}, {"\tjfr_depth: %u\n", caps->jfr.depth}, {"\tjfc_max_cnt: %u\n", caps->jfc.max_cnt}, - {"\tjfc_reserved_cnt: %u\n", caps->jfc.reserved_cnt}, {"\tjfc_depth: %u\n", caps->jfc.depth}, {"\ttp_max_cnt: %u\n", caps->tp.max_cnt}, - {"\ttp_reserved_cnt: %u\n", caps->tp.reserved_cnt}, {"\ttp_depth: %u\n", caps->tp.depth}, {"\ttpg_max_cnt: %u\n", caps->tpg.max_cnt}, - {"\ttpg_reserved_cnt: %u\n", caps->tpg.reserved_cnt}, {"\ttpg_depth: %u\n", caps->tpg.depth}, {"\tcqe_size: %hu\n", caps->cqe_size}, {"\tutp_port_bitmap: 0x%x\n", caps->utp_port_bitmap}, {"\tjtg_max_cnt: %u\n", caps->jtg_max_cnt}, {"\trc_max_cnt: %u\n", caps->rc_max_cnt}, {"\trc_depth: %u\n", caps->rc_que_depth}, - {"\tccc_max_cnt: %u\n", caps->ccc_max_cnt}, - {"\tdest_addr_max_cnt: %u\n", caps->dest_addr_max_cnt}, - {"\tseid_upi_max_cnt: %u\n", caps->seid_upi_max_cnt}, - {"\ttpm_max_cnt: %u\n", caps->tpm_max_cnt}, {"\tprealloc_mem_dma_len: %llu\n", caps->pmem.dma_len}, }; int i; diff --git a/drivers/ub/ubase/ubase_cmd.c b/drivers/ub/ubase/ubase_cmd.c index 4526f92ac117..4eedb6eff530 100644 --- a/drivers/ub/ubase/ubase_cmd.c +++ b/drivers/ub/ubase/ubase_cmd.c @@ -306,10 +306,11 @@ int ubase_send_cmd(struct ubase_dev *udev, return ret; } -static int ubase_cmd_query_version(struct ubase_dev *udev, u32 *fw_version) +static int ubase_cmd_query_version(struct ubase_dev *udev) { struct ubase_query_version_cmd *resp; struct ubase_cmdq_desc desc; + u32 fw_ver; int ret; ubase_cmd_setup_basic_desc(&desc, UBASE_OPC_QUERY_FW_VER, true, 1); @@ -321,13 +322,14 @@ static int ubase_cmd_query_version(struct ubase_dev *udev, u32 *fw_version) } resp = (struct ubase_query_version_cmd *)desc.data; - *fw_version = le32_to_cpu(resp->firmware); + udev->caps.dev_caps.fw_version = le32_to_cpu(resp->fw_version); + fw_ver = udev->caps.dev_caps.fw_version; ubase_info(udev, "The firmware version is %u.%u.%u.%u\n", - u32_get_bits(*fw_version, UBASE_FW_VERSION_BYTE3_MASK), - u32_get_bits(*fw_version, UBASE_FW_VERSION_BYTE2_MASK), - u32_get_bits(*fw_version, UBASE_FW_VERSION_BYTE1_MASK), - u32_get_bits(*fw_version, UBASE_FW_VERSION_BYTE0_MASK)); + u32_get_bits(fw_ver, UBASE_FW_VERSION_BYTE3_MASK), + u32_get_bits(fw_ver, UBASE_FW_VERSION_BYTE2_MASK), + u32_get_bits(fw_ver, UBASE_FW_VERSION_BYTE1_MASK), + u32_get_bits(fw_ver, UBASE_FW_VERSION_BYTE0_MASK)); return 0; } @@ -366,7 +368,7 @@ int ubase_cmd_init(struct ubase_dev *udev) clear_bit(UBASE_STATE_CMD_DISABLE, &udev->hw.state); - ret = ubase_cmd_query_version(udev, &udev->caps.dev_caps.fw_version); + ret = ubase_cmd_query_version(udev); if (ret) goto err_query_version; diff --git a/drivers/ub/ubase/ubase_cmd.h b/drivers/ub/ubase/ubase_cmd.h index 63b67179f2fb..263e88c9fa49 100644 --- a/drivers/ub/ubase/ubase_cmd.h +++ b/drivers/ub/ubase/ubase_cmd.h @@ -43,10 +43,16 @@ enum ubase_cmd_state { }; struct ubase_query_version_cmd { - __le32 firmware; - __le32 hardware; - __le32 rsv; - __le32 caps[UBASE_CAP_LEN]; + __le32 fw_version; + u8 rsv[20]; +}; + +enum ubase_drv_cap_bit { + UBASE_CAP_SUP_ACTIVATE_B = 0, +}; + +struct ubase_notify_drv_cap_cmd { + u8 cap_bits[24]; /* see ubase_drv_cap_bit */ }; #define UBASE_UBCL_CFG_DATA_ALIGN 4 @@ -106,10 +112,10 @@ struct ubase_cfg_ets_vl_sch_cmd { }; struct ubase_cfg_tm_vl_sch_cmd { - __le16 bus_ue_id; + u8 rsvd0[2]; __le16 vl_bitmap; __le16 vl_tsa; - u8 rsvd[2]; + u8 rsvd1[2]; u8 vl_bw[UBASE_MAX_VL_NUM]; }; diff --git a/drivers/ub/ubase/ubase_ctrlq.c b/drivers/ub/ubase/ubase_ctrlq.c index d590463b0efc..b8d7681dd518 100644 --- a/drivers/ub/ubase/ubase_ctrlq.c +++ b/drivers/ub/ubase/ubase_ctrlq.c @@ -742,6 +742,32 @@ static void ubase_ctrlq_read_msg_data(struct ubase_dev *udev, u8 num, u8 *msg) } } +static void ubase_ctrlq_send_unsupported_resp(struct ubase_dev *udev, + struct ubase_ctrlq_base_block *head, + void *msg_data, u16 msg_data_len, + u16 seq) +{ + struct ubase_ctrlq_msg msg = {0}; + int ret; + + msg.service_ver = head->service_ver; + msg.service_type = head->service_type; + msg.opcode = head->opcode; + msg.in_size = msg_data_len; + msg.in = msg_data; + msg.is_resp = 1; + msg.resp_seq = seq; + msg.resp_ret = EOPNOTSUPP; + + ubase_info(udev, "ctrlq received unsupported req. seq=%u, ser_type=%d, ser_ver=%d, opc=%u.", + seq, head->service_type, head->service_ver, head->opcode); + + ret = __ubase_ctrlq_send(udev, &msg, NULL); + if (ret) + ubase_warn(udev, "failed to send ctrlq unsupported resp. seq=%u, ser_type=%d, ser_ver=%d, opc=%u.", + seq, head->service_type, head->service_ver, head->opcode); +} + static void ubase_ctrlq_crq_event_callback(struct ubase_dev *udev, struct ubase_ctrlq_base_block *head, void *msg_data, u16 msg_data_len, @@ -749,20 +775,25 @@ static void ubase_ctrlq_crq_event_callback(struct ubase_dev *udev, { struct ubase_ctrlq_crq_table *crq_tab = &udev->ctrlq.crq_table; struct ubase_ctrlq_crq_event_nbs *nbs; + int ret = -EOPNOTSUPP; mutex_lock(&crq_tab->lock); list_for_each_entry(nbs, &crq_tab->crq_nbs.list, list) { if (nbs->crq_nb.service_type == head->service_type && nbs->crq_nb.opcode == head->opcode) { - nbs->crq_nb.crq_handler(nbs->crq_nb.back, - head->service_ver, - msg_data, - msg_data_len, - seq); + ret = nbs->crq_nb.crq_handler(nbs->crq_nb.back, + head->service_ver, + msg_data, + msg_data_len, + seq); break; } } mutex_unlock(&crq_tab->lock); + + if (ret == -EOPNOTSUPP) + ubase_ctrlq_send_unsupported_resp(udev, head, msg_data, + msg_data_len, seq); } static void ubase_ctrlq_notify_completed(struct ubase_dev *udev, diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index 3921cd0ff824..fa61159d4295 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -454,11 +454,6 @@ static int ubase_handle_ue2ue_ctrlq_req(struct ubase_dev *udev, return -EINVAL; } - if (cmd->in_size > (len - (sizeof(*cmd) + UBASE_CTRLQ_HDR_LEN))) { - ubase_err(udev, "ubase e2e cmd len = %u error.\n", cmd->in_size); - return -EINVAL; - } - msg.service_ver = head->service_ver; msg.service_type = head->service_type; msg.opcode = head->opcode; @@ -621,6 +616,19 @@ static int ubase_register_cmdq_crq_event(struct ubase_dev *udev) return ret; } +static int ubase_notify_drv_capbilities(struct ubase_dev *udev) +{ + struct ubase_notify_drv_cap_cmd req = {0}; + struct ubase_cmd_buf in; + + set_bit(UBASE_CAP_SUP_ACTIVATE_B, (unsigned long *)req.cap_bits); + + __ubase_fill_inout_buf(&in, UBASE_OPC_NOTIFY_DRV_CAPS, false, + sizeof(req), &req); + + return __ubase_cmd_send_in(udev, &in); +} + static const struct ubase_init_function ubase_init_func_map[] = { { "init work queue", UBASE_SUP_ALL, 0, @@ -630,6 +638,10 @@ static const struct ubase_init_function ubase_init_func_map[] = { "init cmd queue", UBASE_SUP_ALL, 1, ubase_cmd_init, ubase_cmd_uninit }, + { + "notify drv capbilities", UBASE_SUP_ALL, 0, + ubase_notify_drv_capbilities, NULL + }, { "query dev res", UBASE_SUP_ALL, 0, ubase_query_dev_res, NULL diff --git a/drivers/ub/ubase/ubase_hw.c b/drivers/ub/ubase/ubase_hw.c index d728dd47f116..290a03a74b0e 100644 --- a/drivers/ub/ubase/ubase_hw.c +++ b/drivers/ub/ubase/ubase_hw.c @@ -156,13 +156,10 @@ static void ubase_parse_dev_caps_unic(struct ubase_dev *udev, struct ubase_adev_caps *unic_caps = &udev->caps.unic_caps; unic_caps->jfs.max_cnt = le32_to_cpu(resp->nic_jfs_max_cnt); - unic_caps->jfs.reserved_cnt = le32_to_cpu(resp->nic_jfs_reserved_cnt); unic_caps->jfs.depth = le32_to_cpu(resp->nic_jfs_depth); unic_caps->jfr.max_cnt = le32_to_cpu(resp->nic_jfr_max_cnt); - unic_caps->jfr.reserved_cnt = le32_to_cpu(resp->nic_jfr_reserved_cnt); unic_caps->jfr.depth = le32_to_cpu(resp->nic_jfr_depth); unic_caps->jfc.max_cnt = le32_to_cpu(resp->nic_jfc_max_cnt); - unic_caps->jfc.reserved_cnt = le32_to_cpu(resp->nic_jfc_reserved_cnt); unic_caps->jfc.depth = le32_to_cpu(resp->nic_jfc_depth); unic_caps->cqe_size = le16_to_cpu(resp->nic_cqe_size); unic_caps->utp_port_bitmap = le32_to_cpu(resp->port_bitmap); @@ -174,13 +171,10 @@ static void ubase_parse_dev_caps_udma(struct ubase_dev *udev, struct ubase_adev_caps *udma_caps = &udev->caps.udma_caps; udma_caps->jfs.max_cnt = le32_to_cpu(resp->udma_jfs_max_cnt); - udma_caps->jfs.reserved_cnt = le32_to_cpu(resp->udma_jfs_reserved_cnt); udma_caps->jfs.depth = le32_to_cpu(resp->udma_jfs_depth); udma_caps->jfr.max_cnt = le32_to_cpu(resp->udma_jfr_max_cnt); - udma_caps->jfr.reserved_cnt = le32_to_cpu(resp->udma_jfr_reserved_cnt); udma_caps->jfr.depth = le32_to_cpu(resp->udma_jfr_depth); udma_caps->jfc.max_cnt = le32_to_cpu(resp->udma_jfc_max_cnt); - udma_caps->jfc.reserved_cnt = le32_to_cpu(resp->udma_jfc_reserved_cnt); udma_caps->jfc.depth = le32_to_cpu(resp->udma_jfc_depth); udma_caps->cqe_size = le16_to_cpu(resp->udma_cqe_size); udma_caps->jtg_max_cnt = le32_to_cpu(resp->jtg_max_cnt); @@ -951,7 +945,7 @@ int __ubase_perf_stats(struct ubase_dev *udev, u64 port_bitmap, u32 period, struct ubase_perf_stats_result *data, u32 data_size) { #define UBASE_MS_TO_US(ms) (1000 * (ms)) - struct ubase_stop_perf_stats_cmd resp = {0}; + struct ubase_stop_perf_stats_cmd resp; unsigned long logic_port_bitmap; int ret, j, k, port_num; u8 i; @@ -988,6 +982,8 @@ int __ubase_perf_stats(struct ubase_dev *udev, u64 port_bitmap, u32 period, for (i = 0, k = 0; i < UBASE_MAX_PORT_NUM && k < port_num; i++) { if (!test_bit(i, (unsigned long *)&port_bitmap)) continue; + + memset(&resp, 0, sizeof(resp)); ret = ubase_stop_perf_stats(udev, &resp, period, i); if (ret) goto unlock; diff --git a/drivers/ub/ubase/ubase_hw.h b/drivers/ub/ubase/ubase_hw.h index ee455ba317d1..12bc101cd04f 100644 --- a/drivers/ub/ubase/ubase_hw.h +++ b/drivers/ub/ubase/ubase_hw.h @@ -30,7 +30,7 @@ struct ubase_caps_item { struct ubase_res_cmd_resp { __le32 cap_bits[UBASE_CAP_LEN]; - __le32 rsvd[3]; + __le32 rsvd0[3]; u8 rsvd1[2]; __le16 ceq_vector_num; @@ -43,37 +43,32 @@ struct ubase_res_cmd_resp { __le32 aeqe_depth; __le32 ceqe_depth; __le32 udma_jfs_max_cnt; - __le32 udma_jfs_reserved_cnt; + u8 rsvd2[4]; __le32 udma_jfs_depth; __le32 udma_jfr_max_cnt; - __le32 udma_jfr_reserved_cnt; + u8 rsvd3[4]; __le32 udma_jfr_depth; u8 nic_vl_num; - u8 rsvd2[3]; + u8 rsvd4[3]; u8 nic_vl[UBASE_MAX_REQ_VL_NUM]; __le32 udma_jfc_max_cnt; - __le32 udma_jfc_reserved_cnt; + u8 rsvd5[4]; __le32 udma_jfc_depth; - __le32 udma_tp_max_cnt; - __le32 udma_tp_reserved_cnt; - __le32 udma_tp_depth; - __le32 udma_tpg_max_cnt; - __le32 udma_tpg_reserved_cnt; - __le32 udma_tpg_depth; + u8 rsvd6[24]; __le32 nic_jfs_max_cnt; - __le32 nic_jfs_reserved_cnt; + u8 rsvd7[4]; __le32 nic_jfs_depth; __le32 nic_jfr_max_cnt; - __le32 nic_jfr_reserved_cnt; + u8 rsvd8[4]; __le32 nic_jfr_depth; - __le32 rsvd3[2]; + __le32 rsvd9[2]; - __le32 rsvd4; + __le32 rsvd10; __le32 nic_jfc_max_cnt; - __le32 nic_jfc_reserved_cnt; + u8 rsvd11[4]; __le32 nic_jfc_depth; __le32 nic_tp_max_cnt; __le32 nic_tp_reserved_cnt; @@ -83,10 +78,7 @@ struct ubase_res_cmd_resp { __le32 nic_tpg_reserved_cnt; __le32 nic_tpg_depth; __le32 total_ue_num; - __le32 jfs_ctx_size; - __le32 jfr_ctx_size; - __le32 jfc_ctx_size; - __le32 tp_ctx_size; + u8 rsvd12[16]; __le16 rsvd_jetty_cnt; __le16 mac_stats_num; @@ -95,24 +87,22 @@ struct ubase_res_cmd_resp { __le32 public_jetty_cnt; __le32 tp_extdb_buf_size; __le32 tp_timer_buf_size; - u8 port_work_mode; + u8 resv13; u8 udma_vl_num; u8 udma_tp_resp_vl_offset; u8 ue_num; __le32 port_bitmap; - u8 rsvd5[4]; + u8 rsvd14[4]; /* include udma tp and ctp req vl */ u8 udma_req_vl[UBASE_MAX_REQ_VL_NUM]; __le32 udma_rc_depth; - u8 rsvd6[4]; + u8 rsvd15[4]; __le32 jtg_max_cnt; __le32 rc_max_cnt_per_vl; - __le32 dest_addr_max_cnt; - __le32 seid_upi_max_cnt; + u8 rsvd16[8]; - __le32 tpm_max_cnt; - __le32 ccc_max_cnt; + u8 rsvd17[32]; }; struct ubase_query_oor_resp { diff --git a/drivers/ub/ubase/ubase_qos_hw.c b/drivers/ub/ubase/ubase_qos_hw.c index bfd5c4e0f5c1..8145edc4401c 100644 --- a/drivers/ub/ubase/ubase_qos_hw.c +++ b/drivers/ub/ubase/ubase_qos_hw.c @@ -195,13 +195,10 @@ static int __ubase_config_tm_vl_sch(struct ubase_dev *udev, u16 vl_bitmap, int ret; u8 i; - /* the configuration takes effect for all entities. */ - req.bus_ue_id = cpu_to_le16(USHRT_MAX); - req.vl_bitmap = cpu_to_le16(vl_bitmap); - for (i = 0; i < UBASE_MAX_VL_NUM; i++) tsa_bitmap |= vl_tsa[i] ? 1 << i : 0; + req.vl_bitmap = cpu_to_le16(vl_bitmap); req.vl_tsa = cpu_to_le16(tsa_bitmap); memcpy(req.vl_bw, vl_bw, UBASE_MAX_VL_NUM); @@ -209,7 +206,7 @@ static int __ubase_config_tm_vl_sch(struct ubase_dev *udev, u16 vl_bitmap, sizeof(req), &req); ret = __ubase_cmd_send_in(udev, &in); - if (ret) + if (ret && ret != -EPERM) ubase_err(udev, "failed to config tm vl sch, ret = %d", ret); return ret; @@ -230,7 +227,7 @@ static int __ubase_config_ets_vl_sch(struct ubase_dev *udev, u16 vl_bitmap, &req); ret = __ubase_cmd_send_in(udev, &in); - if (ret) + if (ret && ret != -EPERM) ubase_err(udev, "failed to cfg ets vl sch, ret = %d.", ret); return ret; diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index 4eb3c435a8f9..492b5e8513ea 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -30,6 +30,7 @@ enum ubase_opcode_type { /* Generic commands */ UBASE_OPC_QUERY_FW_VER = 0x0001, UBASE_OPC_QUERY_CTL_INFO = 0x0003, + UBASE_OPC_NOTIFY_DRV_CAPS = 0x0007, UBASE_OPC_QUERY_COMM_RSRC_PARAM = 0x0030, UBASE_OPC_QUERY_NIC_RSRC_PARAM = 0x0031, UBASE_OPC_QUERY_LINK_STATUS = 0x0032, diff --git a/include/ub/ubase/ubase_comm_dev.h b/include/ub/ubase/ubase_comm_dev.h index ac85c20311dd..35e0496ac01d 100644 --- a/include/ub/ubase/ubase_comm_dev.h +++ b/include/ub/ubase/ubase_comm_dev.h @@ -103,7 +103,6 @@ struct ubase_caps { struct ubase_res_caps { u32 max_cnt; u32 start_idx; - u32 reserved_cnt; u32 depth; }; @@ -123,10 +122,6 @@ struct ubase_adev_caps { u32 jtg_max_cnt; u32 rc_max_cnt; u32 rc_que_depth; - u32 ccc_max_cnt; - u32 dest_addr_max_cnt; - u32 seid_upi_max_cnt; - u32 tpm_max_cnt; u16 cqe_size; }; -- Gitee From 252427ae6195f951e7c6d1538100c0546a13687c Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Mon, 10 Nov 2025 20:33:36 +0800 Subject: [PATCH 205/214] net: unic: Fix the ethtool stats and basic capability information query interface ANBZ: #45460 commit 15bde327170a99bf1cc5e4869d70872c53b69ba6 openEuler. The ethtool tool supports querying stats information for the ub network port. Resolving issues with stats information collection and failure to query FEC and speed-related configurations. Fixes: 4420dfeed31d ("net: unic: Support to query and clear historical NIC link status information") Signed-off-by: Xiongchuan Zhou Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/unic_ethtool.c | 50 ++++++++++ drivers/net/ub/unic/unic_stats.c | 142 +++++++++++++++++++++++++++++ drivers/net/ub/unic/unic_stats.h | 21 +++++ 3 files changed, 213 insertions(+) diff --git a/drivers/net/ub/unic/unic_ethtool.c b/drivers/net/ub/unic/unic_ethtool.c index 22f530c45107..960477451df5 100644 --- a/drivers/net/ub/unic/unic_ethtool.c +++ b/drivers/net/ub/unic/unic_ethtool.c @@ -24,6 +24,46 @@ static u32 unic_get_link_status(struct net_device *netdev) return unic_dev->sw_link_status; } +static void unic_get_port_type(struct unic_dev *unic_dev, + struct ethtool_link_ksettings *cmd) +{ + u8 module_type = unic_dev->hw.mac.module_type; + u8 media_type = unic_dev->hw.mac.media_type; + + switch (media_type) { + case UNIC_MEDIA_TYPE_NONE: + case UNIC_MEDIA_TYPE_BACKPLANE: + cmd->base.port = PORT_NONE; + break; + case UNIC_MEDIA_TYPE_FIBER: + if (module_type == UNIC_MODULE_TYPE_CR) + cmd->base.port = PORT_DA; + else + cmd->base.port = PORT_FIBRE; + break; + default: + cmd->base.port = PORT_NONE; + break; + } +} + +static void unic_get_ksettings(struct unic_dev *unic_dev, + struct ethtool_link_ksettings *cmd) +{ + struct unic_mac *mac = &unic_dev->hw.mac; + + unic_get_port_type(unic_dev, cmd); + + cmd->base.speed = mac->speed; + cmd->base.duplex = mac->duplex; + cmd->base.autoneg = mac->autoneg; + + linkmode_copy(cmd->link_modes.supported, mac->supported); + linkmode_copy(cmd->link_modes.advertising, mac->advertising); + + cmd->lanes = mac->lanes; +} + static int unic_get_link_ksettings(struct net_device *netdev, struct ethtool_link_ksettings *cmd) { @@ -32,6 +72,13 @@ static int unic_get_link_ksettings(struct net_device *netdev, /* Ensure that the latest information is obtained. */ unic_update_port_info(unic_dev); + unic_get_ksettings(unic_dev, cmd); + + if (!unic_get_link_status(netdev)) { + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; + } + return 0; } @@ -326,6 +373,9 @@ static const struct ethtool_ops unic_ethtool_ops = { .get_drvinfo = unic_get_driver_info, .get_regs_len = unic_get_regs_len, .get_regs = unic_get_regs, + .get_ethtool_stats = unic_get_stats, + .get_strings = unic_get_stats_strings, + .get_sset_count = unic_get_sset_count, .get_channels = unic_get_channels, .set_channels = unic_set_channels, .get_ringparam = unic_get_channels_param, diff --git a/drivers/net/ub/unic/unic_stats.c b/drivers/net/ub/unic/unic_stats.c index d8ccd1876500..4647ba5e3e5e 100644 --- a/drivers/net/ub/unic/unic_stats.c +++ b/drivers/net/ub/unic/unic_stats.c @@ -78,6 +78,36 @@ static struct unic_dfx_regs_group unic_dfx_reg_arr[] = { }, }; +static const struct unic_stats_desc unic_sq_stats_str[] = { + {"pad_err", UNIC_SQ_STATS_FIELD_OFF(pad_err)}, + {"packets", UNIC_SQ_STATS_FIELD_OFF(packets)}, + {"bytes", UNIC_SQ_STATS_FIELD_OFF(bytes)}, + {"busy", UNIC_SQ_STATS_FIELD_OFF(busy)}, + {"more", UNIC_SQ_STATS_FIELD_OFF(more)}, + {"restart_queue", UNIC_SQ_STATS_FIELD_OFF(restart_queue)}, + {"over_max_sge_num", UNIC_SQ_STATS_FIELD_OFF(over_max_sge_num)}, + {"csum_err", UNIC_SQ_STATS_FIELD_OFF(csum_err)}, + {"ci_mismatch", UNIC_SQ_STATS_FIELD_OFF(ci_mismatch)}, + {"vlan_err", UNIC_SQ_STATS_FIELD_OFF(vlan_err)}, + {"fd_cnt", UNIC_SQ_STATS_FIELD_OFF(fd_cnt)}, + {"drop_cnt", UNIC_SQ_STATS_FIELD_OFF(drop_cnt)}, + {"cfg5_drop_cnt", UNIC_SQ_STATS_FIELD_OFF(cfg5_drop_cnt)} +}; + +static const struct unic_stats_desc unic_rq_stats_str[] = { + {"alloc_skb_err", UNIC_RQ_STATS_FIELD_OFF(alloc_skb_err)}, + {"packets", UNIC_RQ_STATS_FIELD_OFF(packets)}, + {"bytes", UNIC_RQ_STATS_FIELD_OFF(bytes)}, + {"err_pkt_len_cnt", UNIC_RQ_STATS_FIELD_OFF(err_pkt_len_cnt)}, + {"doi_cnt", UNIC_RQ_STATS_FIELD_OFF(doi_cnt)}, + {"trunc_cnt", UNIC_RQ_STATS_FIELD_OFF(trunc_cnt)}, + {"multicast", UNIC_RQ_STATS_FIELD_OFF(multicast)}, + {"l2_err", UNIC_RQ_STATS_FIELD_OFF(l2_err)}, + {"l3_l4_csum_err", UNIC_RQ_STATS_FIELD_OFF(l3_l4_csum_err)}, + {"alloc_frag_err", UNIC_RQ_STATS_FIELD_OFF(alloc_frag_err)}, + {"csum_complete", UNIC_RQ_STATS_FIELD_OFF(csum_complete)}, +}; + static int unic_get_dfx_reg_num(struct unic_dev *unic_dev, u32 *reg_num, u32 reg_arr_size) { @@ -295,6 +325,118 @@ void unic_get_regs(struct net_device *netdev, struct ethtool_regs *cmd, kfree(reg_num); } +static u64 *unic_get_queues_stats(struct unic_dev *unic_dev, + const struct unic_stats_desc *stats, + u32 stats_size, enum unic_queue_type type, + u64 *data) +{ + struct unic_channel *c; + u32 i, j; + u8 *q; + + for (i = 0; i < unic_dev->channels.num; i++) { + c = &unic_dev->channels.c[i]; + q = (type == UNIC_QUEUE_TYPE_SQ) ? (u8 *)c->sq : (u8 *)c->rq; + for (j = 0; j < stats_size; j++) { + *data = UNIC_STATS_READ(q, stats[j].offset); + data++; + } + } + + return data; +} + +void unic_get_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + u64 *p = data; + + if (unic_resetting(netdev) || !unic_dev->channels.c) { + unic_err(unic_dev, + "dev resetting or channel is null, could not get stats.\n"); + return; + } + + p = unic_get_queues_stats(unic_dev, unic_sq_stats_str, + ARRAY_SIZE(unic_sq_stats_str), + UNIC_QUEUE_TYPE_SQ, p); + + p = unic_get_queues_stats(unic_dev, unic_rq_stats_str, + ARRAY_SIZE(unic_rq_stats_str), + UNIC_QUEUE_TYPE_RQ, p); +} + +static u8 *unic_get_strings(u8 *data, const char *prefix, u32 num, + const struct unic_stats_desc *strs, u32 stats_size) +{ + u32 i, j; + + for (i = 0; i < num; i++) { + for (j = 0; j < stats_size; j++) { + data[ETH_GSTRING_LEN - 1] = '\0'; + + if (prefix) + scnprintf(data, ETH_GSTRING_LEN - 1, "%s%u_%s", + prefix, i, strs[j].desc); + else + scnprintf(data, ETH_GSTRING_LEN - 1, "%s", + strs[j].desc); + + data += ETH_GSTRING_LEN; + } + } + + return data; +} + +static u8 *unic_get_queues_strings(struct unic_dev *unic_dev, u8 *data) +{ + u32 channel_num = unic_dev->channels.num; + + /* get desc for Tx */ + data = unic_get_strings(data, "txq", channel_num, unic_sq_stats_str, + ARRAY_SIZE(unic_sq_stats_str)); + + /* get desc for Rx */ + data = unic_get_strings(data, "rxq", channel_num, unic_rq_stats_str, + ARRAY_SIZE(unic_rq_stats_str)); + + return data; +} + +void unic_get_stats_strings(struct net_device *netdev, u32 stringset, u8 *data) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + u8 *p = data; + + switch (stringset) { + case ETH_SS_STATS: + p = unic_get_queues_strings(unic_dev, p); + break; + default: + break; + } +} + +int unic_get_sset_count(struct net_device *netdev, int stringset) +{ + struct unic_dev *unic_dev = netdev_priv(netdev); + u32 channel_num = unic_dev->channels.num; + int count; + + switch (stringset) { + case ETH_SS_STATS: + count = ARRAY_SIZE(unic_sq_stats_str) * channel_num; + count += ARRAY_SIZE(unic_rq_stats_str) * channel_num; + break; + default: + return -EOPNOTSUPP; + } + + return count; +} + static void unic_get_fec_stats_total(struct unic_dev *unic_dev, u8 stats_flags, struct ethtool_fec_stats *fec_stats) { diff --git a/drivers/net/ub/unic/unic_stats.h b/drivers/net/ub/unic/unic_stats.h index 2a2a8746d838..623b732f3d8e 100644 --- a/drivers/net/ub/unic/unic_stats.h +++ b/drivers/net/ub/unic/unic_stats.h @@ -12,6 +12,13 @@ #include #include +#define UNIC_SQ_STATS_FIELD_OFF(fld) (offsetof(struct unic_sq, stats) + \ + offsetof(struct unic_sq_stats, fld)) +#define UNIC_RQ_STATS_FIELD_OFF(fld) (offsetof(struct unic_rq, stats) + \ + offsetof(struct unic_rq_stats, fld)) + +#define UNIC_STATS_READ(p, offset) (*(u64 *)((u8 *)(p) + (offset))) + #define UNIC_FEC_CORR_BLOCKS BIT(0) #define UNIC_FEC_UNCORR_BLOCKS BIT(1) #define UNIC_FEC_CORR_BITS BIT(2) @@ -58,6 +65,11 @@ enum unic_reg_tag { UNIC_TAG_MAX, }; +enum unic_queue_type { + UNIC_QUEUE_TYPE_SQ = 0, + UNIC_QUEUE_TYPE_RQ, +}; + struct unic_res_regs_group { u16 tag; u32 *regs_addr; @@ -83,9 +95,18 @@ struct unic_dfx_regs_group { bool (*is_supported)(struct unic_dev *unic_dev, u32 property); }; +struct unic_stats_desc { + char desc[ETH_GSTRING_LEN]; + u16 offset; +}; + int unic_get_regs_len(struct net_device *netdev); void unic_get_regs(struct net_device *netdev, struct ethtool_regs *cmd, void *data); +void unic_get_stats_strings(struct net_device *netdev, u32 stringset, u8 *data); +int unic_get_sset_count(struct net_device *netdev, int stringset); +void unic_get_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data); void unic_get_fec_stats(struct net_device *ndev, struct ethtool_fec_stats *fec_stats); -- Gitee From 4907f7af4b69b541f290ebd1e59e5f12a5a23290 Mon Sep 17 00:00:00 2001 From: Fengyan Mu Date: Wed, 12 Nov 2025 18:21:17 +0800 Subject: [PATCH 206/214] ub: ubase: Fix verification to ctrlq message seq ANBZ: #45460 commit e87c0f1d745bd8cea6ec52d5b79c163ba12d4a1d openEuler. The data sent by the UE to the UE will be accessed through seq, but the value of seq may not be trustworthy. Accessing untrusted seq data can cause anomalies. a new check for seq values has been added. Fixes: d7ce08663cc5 ("ub: ubase: Supports for ctrl queue management.") Signed-off-by: Yixi Shen Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_ctrlq.c | 13 +++++++++---- drivers/ub/ubase/ubase_ctrlq.h | 1 + drivers/ub/ubase/ubase_dev.c | 5 +++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/ub/ubase/ubase_ctrlq.c b/drivers/ub/ubase/ubase_ctrlq.c index b8d7681dd518..c49f713226c5 100644 --- a/drivers/ub/ubase/ubase_ctrlq.c +++ b/drivers/ub/ubase/ubase_ctrlq.c @@ -569,6 +569,11 @@ static int ubase_ctrlq_msg_check(struct ubase_dev *udev, return -EINVAL; } + if (msg->is_resp && msg->need_resp) { + ubase_err(udev, "ctrlq input resp type is invalid.\n"); + return -EINVAL; + } + if (msg->is_resp && !(msg->resp_seq & UBASE_CTRLQ_SEQ_MASK)) { ubase_err(udev, "ctrlq input resp_seq(%u) is invalid.\n", msg->resp_seq); @@ -759,13 +764,13 @@ static void ubase_ctrlq_send_unsupported_resp(struct ubase_dev *udev, msg.resp_seq = seq; msg.resp_ret = EOPNOTSUPP; - ubase_info(udev, "ctrlq received unsupported req. seq=%u, ser_type=%d, ser_ver=%d, opc=%u.", + ubase_info(udev, "ctrlq received unsupported req. seq=%u, ser_type=%d, ser_ver=%d, opc=0x%x.", seq, head->service_type, head->service_ver, head->opcode); ret = __ubase_ctrlq_send(udev, &msg, NULL); if (ret) - ubase_warn(udev, "failed to send ctrlq unsupported resp. seq=%u, ser_type=%d, ser_ver=%d, opc=%u.", - seq, head->service_type, head->service_ver, head->opcode); + ubase_warn(udev, "failed to send ctrlq unsupported resp. seq=%u, ser_type=%d, ser_ver=%d, opc=0x%x, ret=%d.", + seq, head->service_type, head->service_ver, head->opcode, ret); } static void ubase_ctrlq_crq_event_callback(struct ubase_dev *udev, @@ -809,7 +814,7 @@ static void ubase_ctrlq_notify_completed(struct ubase_dev *udev, complete(&ctx->done); } -static bool ubase_ctrlq_check_seq(struct ubase_dev *udev, u16 seq) +bool ubase_ctrlq_check_seq(struct ubase_dev *udev, u16 seq) { bool is_pushed = !!(seq & UBASE_CTRLQ_SEQ_MASK); u16 max_seq = ubase_ctrlq_max_seq(udev); diff --git a/drivers/ub/ubase/ubase_ctrlq.h b/drivers/ub/ubase/ubase_ctrlq.h index 431253df5054..b868ace06ab1 100644 --- a/drivers/ub/ubase/ubase_ctrlq.h +++ b/drivers/ub/ubase/ubase_ctrlq.h @@ -94,6 +94,7 @@ void ubase_ctrlq_disable(struct ubase_dev *udev); int __ubase_ctrlq_send(struct ubase_dev *udev, struct ubase_ctrlq_msg *msg, struct ubase_ctrlq_ue_info *ue_info); +bool ubase_ctrlq_check_seq(struct ubase_dev *udev, u16 seq); void ubase_ctrlq_service_task(struct ubase_delay_work *ubase_work); void ubase_ctrlq_handle_crq_msg(struct ubase_dev *udev, struct ubase_ctrlq_base_block *head, diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index fa61159d4295..76d179093abe 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -492,6 +492,11 @@ static int ubase_handle_ue2ue_ctrlq_event(struct ubase_dev *udev, void *data, if (ubase_dev_ctrlq_supported(udev)) return ubase_handle_ue2ue_ctrlq_req(udev, cmd, len); + if (!ubase_ctrlq_check_seq(udev, cmd->seq)) { + ubase_err(udev, "invalid ue2ue ctrlq seq(%u).\n", cmd->seq); + return -EINVAL; + } + head = (struct ubase_ctrlq_base_block *)(cmd + 1); data_len = len - sizeof(*cmd) - UBASE_CTRLQ_HDR_LEN; ubase_ctrlq_handle_crq_msg(udev, head, cmd->seq, -- Gitee From d374d52f5d093fff3fd0e8e47b9cbf3279c7e601 Mon Sep 17 00:00:00 2001 From: Fengyan Mu Date: Wed, 12 Nov 2025 18:37:23 +0800 Subject: [PATCH 207/214] ub: ubase: Optimization of ubase_ctrlq_send_msg interface parameters ANBZ: #45460 commit 0ee149f4767688691a85b306b128d9d84f4232ec openEuler. This patch optimizes the parameters of the ubase_ctrlq_send_msg interface by adding the is_async parameter to specify whether to call it synchronously. Fixes: d7ce08663cc5 ("ub: ubase: Supports for ctrl queue management.") Signed-off-by: Guangwei Zhang Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_cmd.h | 3 +- drivers/ub/ubase/ubase_ctrlq.c | 96 +++++++++++++++++++---------- drivers/ub/ubase/ubase_ctrlq.h | 20 ++++++ drivers/ub/ubase/ubase_dev.c | 3 + include/ub/ubase/ubase_comm_ctrlq.h | 3 +- 5 files changed, 91 insertions(+), 34 deletions(-) diff --git a/drivers/ub/ubase/ubase_cmd.h b/drivers/ub/ubase/ubase_cmd.h index 263e88c9fa49..a99422b59dc4 100644 --- a/drivers/ub/ubase/ubase_cmd.h +++ b/drivers/ub/ubase/ubase_cmd.h @@ -83,7 +83,8 @@ struct ubase_ue2ue_ctrlq_head { u16 out_size; u8 need_resp : 1; u8 is_resp : 1; - u8 rsv : 6; + u8 is_async : 1; + u8 rsv : 5; }; struct ubase_start_perf_stats_cmd { diff --git a/drivers/ub/ubase/ubase_ctrlq.c b/drivers/ub/ubase/ubase_ctrlq.c index c49f713226c5..352342ee01dc 100644 --- a/drivers/ub/ubase/ubase_ctrlq.c +++ b/drivers/ub/ubase/ubase_ctrlq.c @@ -369,11 +369,13 @@ static void ubase_ctrlq_fill_first_bb(struct ubase_dev *udev, head->service_type = msg->service_type; head->opcode = msg->opcode; head->mbx_ue_id = ue_info ? ue_info->mbx_ue_id : 0; - head->ret = msg->is_resp ? msg->resp_ret : 0; + head->ret = ubase_ctrlq_msg_is_resp(msg) ? msg->resp_ret : 0; head->bus_ue_id = cpu_to_le16(ue_info ? ue_info->bus_ue_id : ue->entity_idx); - memcpy(head->data, msg->in, min(msg->in_size, UBASE_CTRLQ_DATA_LEN)); + if (msg->in) + memcpy(head->data, msg->in, + min(msg->in_size, UBASE_CTRLQ_DATA_LEN)); } static inline void ubase_ctrlq_csq_report_irq(struct ubase_dev *udev) @@ -408,10 +410,12 @@ static int ubase_ctrlq_send_to_cmdq(struct ubase_dev *udev, ue2ue_head.out_size = msg->out_size; ue2ue_head.need_resp = msg->need_resp; ue2ue_head.is_resp = msg->is_resp; + ue2ue_head.is_async = msg->is_async; memcpy(req, &ue2ue_head, sizeof(ue2ue_head)); memcpy((u8 *)req + sizeof(ue2ue_head), head, UBASE_CTRLQ_HDR_LEN); - memcpy((u8 *)req + sizeof(ue2ue_head) + UBASE_CTRLQ_HDR_LEN, msg->in, - msg->in_size); + if (msg->in) + memcpy((u8 *)req + sizeof(ue2ue_head) + UBASE_CTRLQ_HDR_LEN, + msg->in, msg->in_size); __ubase_fill_inout_buf(&in, UBASE_OPC_UE2UE_UBASE, false, req_len, req); ret = __ubase_cmd_send_in(udev, &in); @@ -541,18 +545,17 @@ static void ubase_ctrlq_addto_msg_queue(struct ubase_dev *udev, u16 seq, { struct ubase_ctrlq_msg_ctx *ctx; - if (!msg->need_resp) + if (!(ubase_ctrlq_msg_is_sync_req(msg) || + ubase_ctrlq_msg_is_async_req(msg))) return; ctx = &udev->ctrlq.msg_queue[seq]; ctx->valid = 1; - ctx->is_sync = msg->need_resp && msg->out && msg->out_size ? 1 : 0; + ctx->is_sync = ubase_ctrlq_msg_is_sync_req(msg) ? 1 : 0; ctx->result = ETIME; ctx->dead_jiffies = jiffies + msecs_to_jiffies(UBASE_CTRLQ_DEAD_TIME); - if (ctx->is_sync) { - ctx->out = msg->out; - ctx->out_size = msg->out_size; - } + ctx->out = msg->out; + ctx->out_size = msg->out_size; if (ue_info) { ctx->ue_seq = ue_info->seq; @@ -564,30 +567,58 @@ static void ubase_ctrlq_addto_msg_queue(struct ubase_dev *udev, u16 seq, static int ubase_ctrlq_msg_check(struct ubase_dev *udev, struct ubase_ctrlq_msg *msg) { - if (!msg || !msg->in || !msg->in_size) { - ubase_err(udev, "ctrlq input buf is invalid.\n"); - return -EINVAL; - } - - if (msg->is_resp && msg->need_resp) { - ubase_err(udev, "ctrlq input resp type is invalid.\n"); + if ((!msg->in && msg->in_size) || (msg->in && !msg->in_size)) { + ubase_err(udev, "ctrlq msg in param error.\n"); return -EINVAL; } - if (msg->is_resp && !(msg->resp_seq & UBASE_CTRLQ_SEQ_MASK)) { - ubase_err(udev, "ctrlq input resp_seq(%u) is invalid.\n", - msg->resp_seq); + if ((!msg->out && msg->out_size) || (msg->out && !msg->out_size)) { + ubase_err(udev, "ctrlq msg out param error.\n"); return -EINVAL; } if (msg->in_size > UBASE_CTRLQ_MAX_DATA_SIZE) { ubase_err(udev, - "requested ctrlq space(%u) exceeds the maximum(%u).\n", + "ctrlq msg in_size(%u) exceeds the maximum(%u).\n", msg->in_size, UBASE_CTRLQ_MAX_DATA_SIZE); return -EINVAL; } - return 0; + if (ubase_ctrlq_msg_is_sync_req(msg)) + return 0; + + if (ubase_ctrlq_msg_is_async_req(msg)) { + if (msg->out) { + ubase_err(udev, "ctrlq msg out is not NULL in async req.\n"); + return -EINVAL; + } + return 0; + } + + if (ubase_ctrlq_msg_is_notify_req(msg)) { + if (msg->out) { + ubase_err(udev, "ctrlq msg out is not NULL in notify req.\n"); + return -EINVAL; + } + return 0; + } + + if (ubase_ctrlq_msg_is_resp(msg)) { + if (msg->out) { + ubase_err(udev, "ctrlq msg out is not NULL in resp.\n"); + return -EINVAL; + } + if (!(msg->resp_seq & UBASE_CTRLQ_SEQ_MASK)) { + ubase_err(udev, "ctrlq msg resp_seq error, resp_seq=%u.\n", + msg->resp_seq); + return -EINVAL; + } + return 0; + } + + ubase_err(udev, "ctrlq msg param error, is_resp=%u, is_async=%u, need_resp=%u.\n", + msg->is_resp, msg->is_async, msg->need_resp); + return -EINVAL; } static int ubase_ctrlq_check_csq_enough(struct ubase_dev *udev, u16 num) @@ -612,8 +643,6 @@ static int ubase_ctrlq_send_real(struct ubase_dev *udev, struct ubase_ctrlq_msg *msg, struct ubase_ctrlq_ue_info *ue_info) { - bool sync_req = msg->out && msg->out_size && msg->need_resp; - bool no_resp = !msg->is_resp && !msg->need_resp; struct ubase_ctrlq_ring *csq = &udev->ctrlq.csq; struct ubase_ctrlq_base_block head = {0}; u16 seq, num; @@ -627,7 +656,7 @@ static int ubase_ctrlq_send_real(struct ubase_dev *udev, if (ret) goto unlock; - if (!msg->is_resp) { + if (!ubase_ctrlq_msg_is_resp(msg)) { ret = ubase_ctrlq_alloc_seq(udev, msg, &seq); if (ret) { ubase_warn(udev, "no enough seq in ctrlq.\n"); @@ -645,20 +674,22 @@ static int ubase_ctrlq_send_real(struct ubase_dev *udev, ret = ubase_ctrlq_send_msg_to_sq(udev, &head, msg, num); if (ret) { spin_unlock_bh(&csq->lock); - goto free_seq; + if (!ubase_ctrlq_msg_is_resp(msg)) + ubase_ctrlq_free_seq(udev, seq); + return ret; } spin_unlock_bh(&csq->lock); - if (sync_req) + if (ubase_ctrlq_msg_is_sync_req(msg)) ret = ubase_ctrlq_wait_completed(udev, seq, msg); -free_seq: - /* Only the seqs in synchronous requests and no response requests need to be released. */ - /* The seqs are released in periodic tasks of asynchronous requests. */ - if (sync_req || no_resp) + if (ubase_ctrlq_msg_is_sync_req(msg) || + ubase_ctrlq_msg_is_notify_req(msg)) ubase_ctrlq_free_seq(udev, seq); + return ret; + unlock: spin_unlock_bh(&csq->lock); return ret; @@ -809,7 +840,8 @@ static void ubase_ctrlq_notify_completed(struct ubase_dev *udev, ctx = &udev->ctrlq.msg_queue[seq]; ctx->result = head->ret; - memcpy(ctx->out, msg, min(msg_len, ctx->out_size)); + if (ctx->out) + memcpy(ctx->out, msg, min(msg_len, ctx->out_size)); complete(&ctx->done); } diff --git a/drivers/ub/ubase/ubase_ctrlq.h b/drivers/ub/ubase/ubase_ctrlq.h index b868ace06ab1..c3d5f55db87d 100644 --- a/drivers/ub/ubase/ubase_ctrlq.h +++ b/drivers/ub/ubase/ubase_ctrlq.h @@ -87,6 +87,26 @@ struct ubase_ctrlq_reset_ctrl_req { u8 rsv[3]; }; +static inline bool ubase_ctrlq_msg_is_sync_req(struct ubase_ctrlq_msg *msg) +{ + return !msg->is_resp && !msg->is_async && msg->need_resp; +} + +static inline bool ubase_ctrlq_msg_is_async_req(struct ubase_ctrlq_msg *msg) +{ + return !msg->is_resp && msg->is_async && msg->need_resp; +} + +static inline bool ubase_ctrlq_msg_is_notify_req(struct ubase_ctrlq_msg *msg) +{ + return !msg->is_resp && !msg->is_async && !msg->need_resp; +} + +static inline bool ubase_ctrlq_msg_is_resp(struct ubase_ctrlq_msg *msg) +{ + return msg->is_resp && !msg->is_async && !msg->need_resp; +} + int ubase_ctrlq_init(struct ubase_dev *udev); void ubase_ctrlq_uninit(struct ubase_dev *udev); void ubase_ctrlq_disable(struct ubase_dev *udev); diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index 76d179093abe..7331afa03c80 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -459,11 +459,14 @@ static int ubase_handle_ue2ue_ctrlq_req(struct ubase_dev *udev, msg.opcode = head->opcode; msg.need_resp = cmd->need_resp; msg.is_resp = cmd->is_resp; + msg.is_async = cmd->is_async; msg.resp_seq = cmd->seq; msg.in = (u8 *)head + UBASE_CTRLQ_HDR_LEN; msg.in_size = cmd->in_size; msg.out = NULL; msg.out_size = 0; + if (ubase_ctrlq_msg_is_sync_req(&msg)) + msg.is_async = 1; ue_info.bus_ue_id = le16_to_cpu(cmd->head.bus_ue_id); ue_info.seq = cmd->seq; diff --git a/include/ub/ubase/ubase_comm_ctrlq.h b/include/ub/ubase/ubase_comm_ctrlq.h index 7e772dcb0746..da3337573b61 100644 --- a/include/ub/ubase/ubase_comm_ctrlq.h +++ b/include/ub/ubase/ubase_comm_ctrlq.h @@ -61,7 +61,8 @@ struct ubase_ctrlq_msg { u8 opcode; u8 need_resp : 1; u8 is_resp : 1; - u8 resv : 6; + u8 is_async : 1; + u8 resv : 5; u8 resp_ret; /* must set when the is_resp field is true. */ u16 resp_seq; /* must set when the is_resp field is true. */ u16 in_size; -- Gitee From 82e63d1c551e98528cf0acdbdb0e7e22c4a52ae2 Mon Sep 17 00:00:00 2001 From: Fengyan Mu Date: Thu, 20 Nov 2025 11:03:26 +0800 Subject: [PATCH 208/214] ub: ubase: Fix CTRLQ white list ANBZ: #45460 commit aca32b8e11f4d793529e2d06a7453d9dcbcf2f43 openEuler. According to the design requirements for CtrlQ interface compatibility, when the Ubase receives a message that it does not support, it needs to return a notsupport response to the peer. Currently, Ubase determines whether a message is supported by checking whether the corresponding callback function has been registered. This leads to an edge case: when the auxiliary drivers has not registered, the corresponding callback function is null, and in this case, Ubase also returns notsupport. This causes the peer to mistakenly believe that we does not support the message, and subsequently, the peer will no longer send this message, which is not in line with the design expectations. Therefore, it is necessary for Ubase to distinguish between the two scenarios: when a message is supported but the callback is not registered, and when a message is not supported at all. So we add a message whitelist in Ubase. Messages not included in the whitelist will return notsupport while those included will return a specific error code 255 indicating "DRV NOT EXIST." Fixes: d7ce08663cc5 ("ub: ubase: Supports for ctrl queue management.") Signed-off-by: Chuan Wu Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_ctrlq.c | 202 ++++++++++++++++++++-------- drivers/ub/ubase/ubase_dev.h | 8 +- include/ub/ubase/ubase_comm_ctrlq.h | 23 ++-- 3 files changed, 160 insertions(+), 73 deletions(-) diff --git a/drivers/ub/ubase/ubase_ctrlq.c b/drivers/ub/ubase/ubase_ctrlq.c index 352342ee01dc..9351ed2c70fd 100644 --- a/drivers/ub/ubase/ubase_ctrlq.c +++ b/drivers/ub/ubase/ubase_ctrlq.c @@ -11,19 +11,102 @@ #include "ubase_cmd.h" #include "ubase_ctrlq.h" -static inline void ubase_ctrlq_crq_table_init(struct ubase_dev *udev) +/* UNIC ctrlq msg white list */ +static const struct ubase_ctrlq_event_nb ubase_ctrlq_wlist_unic[] = { + {UBASE_CTRLQ_SER_TYPE_IP_ACL, UBASE_CTRLQ_OPC_NOTIFY_IP, NULL, NULL}, +}; + +/* UDMA ctrlq msg white list */ +static const struct ubase_ctrlq_event_nb ubase_ctrlq_wlist_udma[] = { + {UBASE_CTRLQ_SER_TYPE_TP_ACL, UBASE_CTRLQ_OPC_CHECK_TP_ACTIVE, NULL, NULL}, + {UBASE_CTRLQ_SER_TYPE_DEV_REGISTER, UBASE_CTRLQ_OPC_UPDATE_SEID, NULL, NULL}, + {UBASE_CTRLQ_SER_TYPE_DEV_REGISTER, UBASE_CTRLQ_OPC_NOTIFY_RES_RATIO, NULL, NULL}, +}; + +/* CDMA ctrlq msg white list */ +static const struct ubase_ctrlq_event_nb ubase_ctrlq_wlist_cdma[] = { + {UBASE_CTRLQ_SER_TYPE_DEV_REGISTER, UBASE_CTRLQ_OPC_UPDATE_SEID, NULL, NULL}, +}; + +static int ubase_ctrlq_alloc_crq_tbl_mem(struct ubase_dev *udev) { struct ubase_ctrlq_crq_table *crq_tab = &udev->ctrlq.crq_table; + u16 cnt = 0; + + if (ubase_dev_cdma_supported(udev)) { + cnt = ARRAY_SIZE(ubase_ctrlq_wlist_cdma); + } else if (ubase_dev_urma_supported(udev)) { + if (ubase_dev_unic_supported(udev)) + cnt += ARRAY_SIZE(ubase_ctrlq_wlist_unic); + if (ubase_dev_udma_supported(udev)) + cnt += ARRAY_SIZE(ubase_ctrlq_wlist_udma); + } + + if (!cnt) + return -EINVAL; + + crq_tab->crq_nbs = kcalloc(cnt, sizeof(struct ubase_ctrlq_event_nb), GFP_KERNEL); + if (!crq_tab->crq_nbs) + return -ENOMEM; + + crq_tab->crq_nb_cnt = cnt; + + return 0; +} + +static void ubase_ctrlq_free_crq_tbl_mem(struct ubase_dev *udev) +{ + struct ubase_ctrlq_crq_table *crq_tab = &udev->ctrlq.crq_table; + + kfree(crq_tab->crq_nbs); + crq_tab->crq_nbs = NULL; + crq_tab->crq_nb_cnt = 0; +} + +static void ubase_ctrlq_init_crq_wlist(struct ubase_dev *udev) +{ + struct ubase_ctrlq_crq_table *crq_tab = &udev->ctrlq.crq_table; + u32 offset = 0; + + if (ubase_dev_cdma_supported(udev)) { + memcpy(crq_tab->crq_nbs, ubase_ctrlq_wlist_cdma, + sizeof(ubase_ctrlq_wlist_cdma)); + } else if (ubase_dev_urma_supported(udev)) { + if (ubase_dev_unic_supported(udev)) { + memcpy(crq_tab->crq_nbs, ubase_ctrlq_wlist_unic, + sizeof(ubase_ctrlq_wlist_unic)); + offset = ARRAY_SIZE(ubase_ctrlq_wlist_unic); + } + if (ubase_dev_udma_supported(udev)) { + memcpy(&crq_tab->crq_nbs[offset], ubase_ctrlq_wlist_udma, + sizeof(ubase_ctrlq_wlist_udma)); + } + } +} + +static int ubase_ctrlq_crq_table_init(struct ubase_dev *udev) +{ + struct ubase_ctrlq_crq_table *crq_tab = &udev->ctrlq.crq_table; + int ret; + + ret = ubase_ctrlq_alloc_crq_tbl_mem(udev); + if (ret) + return ret; + + ubase_ctrlq_init_crq_wlist(udev); mutex_init(&crq_tab->lock); - INIT_LIST_HEAD(&crq_tab->crq_nbs.list); + + return 0; } -static inline void ubase_ctrlq_crq_table_uninit(struct ubase_dev *udev) +static void ubase_ctrlq_crq_table_uninit(struct ubase_dev *udev) { struct ubase_ctrlq_crq_table *crq_tab = &udev->ctrlq.crq_table; mutex_destroy(&crq_tab->lock); + + ubase_ctrlq_free_crq_tbl_mem(udev); } static inline u16 ubase_ctrlq_msg_queue_depth(struct ubase_dev *udev) @@ -233,10 +316,15 @@ int ubase_ctrlq_init(struct ubase_dev *udev) if (ret) goto err_msg_queue_init; + ret = ubase_ctrlq_crq_table_init(udev); + if (ret) + goto err_crq_table_init; + udev->ctrlq.csq_next_seq = 1; atomic_set(&udev->ctrlq.req_cnt, 0); - ubase_ctrlq_crq_table_init(udev); +err_crq_table_init: + ubase_ctrlq_msg_queue_uninit(udev); success: set_bit(UBASE_CTRLQ_STATE_ENABLE, &udev->ctrlq.state); return 0; @@ -780,8 +868,7 @@ static void ubase_ctrlq_read_msg_data(struct ubase_dev *udev, u8 num, u8 *msg) static void ubase_ctrlq_send_unsupported_resp(struct ubase_dev *udev, struct ubase_ctrlq_base_block *head, - void *msg_data, u16 msg_data_len, - u16 seq) + u16 resp_seq, u8 resp_ret) { struct ubase_ctrlq_msg msg = {0}; int ret; @@ -789,19 +876,14 @@ static void ubase_ctrlq_send_unsupported_resp(struct ubase_dev *udev, msg.service_ver = head->service_ver; msg.service_type = head->service_type; msg.opcode = head->opcode; - msg.in_size = msg_data_len; - msg.in = msg_data; msg.is_resp = 1; - msg.resp_seq = seq; - msg.resp_ret = EOPNOTSUPP; - - ubase_info(udev, "ctrlq received unsupported req. seq=%u, ser_type=%d, ser_ver=%d, opc=0x%x.", - seq, head->service_type, head->service_ver, head->opcode); + msg.resp_seq = resp_seq; + msg.resp_ret = resp_ret; ret = __ubase_ctrlq_send(udev, &msg, NULL); if (ret) - ubase_warn(udev, "failed to send ctrlq unsupported resp. seq=%u, ser_type=%d, ser_ver=%d, opc=0x%x, ret=%d.", - seq, head->service_type, head->service_ver, head->opcode, ret); + ubase_warn(udev, "failed to send ctrlq unsupport resp, ret=%d.", + ret); } static void ubase_ctrlq_crq_event_callback(struct ubase_dev *udev, @@ -809,27 +891,43 @@ static void ubase_ctrlq_crq_event_callback(struct ubase_dev *udev, void *msg_data, u16 msg_data_len, u16 seq) { +#define EDRVNOEXIST 255 struct ubase_ctrlq_crq_table *crq_tab = &udev->ctrlq.crq_table; - struct ubase_ctrlq_crq_event_nbs *nbs; - int ret = -EOPNOTSUPP; + int ret = -ENOENT; + u32 i; + + ubase_info(udev, + "ctrlq recv notice req: seq=%u, ser_type=%u, ser_ver=%u, opc=0x%x.", + seq, head->service_type, head->service_ver, head->opcode); mutex_lock(&crq_tab->lock); - list_for_each_entry(nbs, &crq_tab->crq_nbs.list, list) { - if (nbs->crq_nb.service_type == head->service_type && - nbs->crq_nb.opcode == head->opcode) { - ret = nbs->crq_nb.crq_handler(nbs->crq_nb.back, - head->service_ver, - msg_data, - msg_data_len, - seq); + for (i = 0; i < crq_tab->crq_nb_cnt; i++) { + if (crq_tab->crq_nbs[i].service_type == head->service_type && + crq_tab->crq_nbs[i].opcode == head->opcode) { + if (!crq_tab->crq_nbs[i].crq_handler) { + ret = -EDRVNOEXIST; + break; + } + ret = crq_tab->crq_nbs[i].crq_handler(crq_tab->crq_nbs[i].back, + head->service_ver, + msg_data, + msg_data_len, + seq); break; } } mutex_unlock(&crq_tab->lock); - if (ret == -EOPNOTSUPP) - ubase_ctrlq_send_unsupported_resp(udev, head, msg_data, - msg_data_len, seq); + if (ret == -ENOENT) { + ubase_info(udev, "this notice is not supported."); + ubase_ctrlq_send_unsupported_resp(udev, head, seq, EOPNOTSUPP); + } else if (ret == -EOPNOTSUPP) { + ubase_info(udev, "the notice processor return not support."); + ubase_ctrlq_send_unsupported_resp(udev, head, seq, EOPNOTSUPP); + } else if (ret == -EDRVNOEXIST) { + ubase_info(udev, "the notice processor is unregistered."); + ubase_ctrlq_send_unsupported_resp(udev, head, seq, EDRVNOEXIST); + } } static void ubase_ctrlq_notify_completed(struct ubase_dev *udev, @@ -1064,10 +1162,10 @@ void ubase_ctrlq_clean_service_task(struct ubase_delay_work *ubase_work) int ubase_ctrlq_register_crq_event(struct auxiliary_device *aux_dev, struct ubase_ctrlq_event_nb *nb) { - struct ubase_ctrlq_crq_event_nbs *nbs, *tmp, *new_nbs; struct ubase_ctrlq_crq_table *crq_tab; struct ubase_dev *udev; - int ret; + int ret = -ENOENT; + u32 i; if (!aux_dev || !nb || !nb->crq_handler) return -EINVAL; @@ -1075,31 +1173,21 @@ int ubase_ctrlq_register_crq_event(struct auxiliary_device *aux_dev, udev = __ubase_get_udev_by_adev(aux_dev); crq_tab = &udev->ctrlq.crq_table; mutex_lock(&crq_tab->lock); - list_for_each_entry_safe(nbs, tmp, &crq_tab->crq_nbs.list, list) { - if (nbs->crq_nb.service_type == nb->service_type && - nbs->crq_nb.opcode == nb->opcode) { - ret = -EEXIST; - goto err_crq_register; + for (i = 0; i < crq_tab->crq_nb_cnt; i++) { + if (crq_tab->crq_nbs[i].service_type == nb->service_type && + crq_tab->crq_nbs[i].opcode == nb->opcode) { + if (crq_tab->crq_nbs[i].crq_handler) { + ret = -EEXIST; + break; + } + crq_tab->crq_nbs[i].back = nb->back; + crq_tab->crq_nbs[i].crq_handler = nb->crq_handler; + ret = 0; + break; } } - new_nbs = kzalloc(sizeof(*new_nbs), GFP_KERNEL); - if (!new_nbs) { - ret = -ENOMEM; - goto err_crq_register; - } - - new_nbs->crq_nb = *nb; - list_add_tail(&new_nbs->list, &crq_tab->crq_nbs.list); - mutex_unlock(&crq_tab->lock); - - return 0; - -err_crq_register: mutex_unlock(&crq_tab->lock); - ubase_err(udev, - "failed to register ctrlq crq event, opcode = 0x%x, service_type = 0x%x, ret = %d.\n", - nb->opcode, nb->service_type, ret); return ret; } @@ -1108,9 +1196,9 @@ EXPORT_SYMBOL(ubase_ctrlq_register_crq_event); void ubase_ctrlq_unregister_crq_event(struct auxiliary_device *aux_dev, u8 service_type, u8 opcode) { - struct ubase_ctrlq_crq_event_nbs *nbs, *tmp; struct ubase_ctrlq_crq_table *crq_tab; struct ubase_dev *udev; + u32 i; if (!aux_dev) return; @@ -1118,11 +1206,11 @@ void ubase_ctrlq_unregister_crq_event(struct auxiliary_device *aux_dev, udev = __ubase_get_udev_by_adev(aux_dev); crq_tab = &udev->ctrlq.crq_table; mutex_lock(&crq_tab->lock); - list_for_each_entry_safe(nbs, tmp, &crq_tab->crq_nbs.list, list) { - if (nbs->crq_nb.service_type == service_type && - nbs->crq_nb.opcode == opcode) { - list_del(&nbs->list); - kfree(nbs); + for (i = 0; i < crq_tab->crq_nb_cnt; i++) { + if (crq_tab->crq_nbs[i].service_type == service_type && + crq_tab->crq_nbs[i].opcode == opcode) { + crq_tab->crq_nbs[i].back = NULL; + crq_tab->crq_nbs[i].crq_handler = NULL; break; } } diff --git a/drivers/ub/ubase/ubase_dev.h b/drivers/ub/ubase/ubase_dev.h index c8ccd5bd107a..8d21ff16ef19 100644 --- a/drivers/ub/ubase/ubase_dev.h +++ b/drivers/ub/ubase/ubase_dev.h @@ -178,15 +178,11 @@ struct ubase_ctrlq_msg_ctx { struct completion done; }; -struct ubase_ctrlq_crq_event_nbs { - struct list_head list; - struct ubase_ctrlq_event_nb crq_nb; -}; - struct ubase_ctrlq_crq_table { struct mutex lock; unsigned long last_crq_scheduled; - struct ubase_ctrlq_crq_event_nbs crq_nbs; + u16 crq_nb_cnt; + struct ubase_ctrlq_event_nb *crq_nbs; }; struct ubase_ctrlq { diff --git a/include/ub/ubase/ubase_comm_ctrlq.h b/include/ub/ubase/ubase_comm_ctrlq.h index da3337573b61..b1ee65524052 100644 --- a/include/ub/ubase/ubase_comm_ctrlq.h +++ b/include/ub/ubase/ubase_comm_ctrlq.h @@ -28,16 +28,17 @@ enum ubase_ctrlq_ser_ver { }; enum ubase_ctrlq_ser_type { - UBASE_CTRLQ_SER_TYPE_TP_ACL = 0x01, - UBASE_CTRLQ_SER_TYPE_DEV_REGISTER = 0x02, - UBASE_CTRLQ_SER_TYPE_IP_ACL = 0x03, - UBASE_CTRLQ_SER_TYPE_QOS = 0x04, + UBASE_CTRLQ_SER_TYPE_TP_ACL = 0x01, + UBASE_CTRLQ_SER_TYPE_DEV_REGISTER = 0x02, + UBASE_CTRLQ_SER_TYPE_IP_ACL = 0x03, + UBASE_CTRLQ_SER_TYPE_QOS = 0x04, }; -enum ubase_ctrlq_opc_type { +enum ubase_ctrlq_opc_type_tp { UBASE_CTRLQ_OPC_CREATE_TP = 0x11, UBASE_CTRLQ_OPC_DESTROY_TP = 0x12, UBASE_CTRLQ_OPC_TP_FLUSH_DONE = 0x14, + UBASE_CTRLQ_OPC_CHECK_TP_ACTIVE = 0x15, }; enum ubase_ctrlq_opc_type_qos { @@ -45,14 +46,16 @@ enum ubase_ctrlq_opc_type_qos { UBASE_CTRLQ_OPC_QUERY_SL = 0x02, }; -enum ubase_ctrlq_opc_ip { - UBASE_CTRLQ_OPC_NOTIFY_IP = 0x01, - UBASE_CTRLQ_OPC_QUERY_IP = 0x02, +enum ubase_ctrlq_opc_type_ip { + UBASE_CTRLQ_OPC_NOTIFY_IP = 0x01, + UBASE_CTRLQ_OPC_QUERY_IP = 0x02, }; enum ubase_ctrlq_opc_type_dev_register { - UBASE_CTRLQ_OPC_CTRLQ_CTRL = 0x14, - UBASE_CTRLQ_OPC_UE_RESET_CTRL = 0x15, + UBASE_CTRLQ_OPC_UPDATE_SEID = 0x02, + UBASE_CTRLQ_OPC_NOTIFY_RES_RATIO = 0x13, + UBASE_CTRLQ_OPC_CTRLQ_CTRL = 0x14, + UBASE_CTRLQ_OPC_UE_RESET_CTRL = 0x15, }; struct ubase_ctrlq_msg { -- Gitee From a83409543783b532588d68b4a6574a2b35a62e18 Mon Sep 17 00:00:00 2001 From: Fengyan Mu Date: Thu, 13 Nov 2025 11:20:06 +0800 Subject: [PATCH 209/214] net: unic: Fix configure coal parameters without deactivate ANBZ: #45460 commit 300514e725420aa5309724a6f8e5f43845602ac3 openEuler. When activating/deactivating, it is necessary to open/close the protocol stack with link. When configuring parameters, it is necessary to disable the network interface before making changes. Previously, flag was used to intercept and ensure that the two did not run concurrently. After analysis, the UNIC_ACTIVATE_FLAG check in the current code is redundant and can be removed. Fixes: e0ccc63cc72e ("net: unic: support querying and configuring coalesce parameters.") Signed-off-by: Zhenyu Wan Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/unic_ethtool.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/net/ub/unic/unic_ethtool.c b/drivers/net/ub/unic/unic_ethtool.c index 960477451df5..f2cfa3df1126 100644 --- a/drivers/net/ub/unic/unic_ethtool.c +++ b/drivers/net/ub/unic/unic_ethtool.c @@ -276,12 +276,6 @@ static int unic_set_coalesce(struct net_device *netdev, struct unic_coalesce old_tx_coal, old_rx_coal; int ret, ret1; - if (test_bit(UNIC_STATE_DEACTIVATE, &unic_dev->state)) { - unic_err(unic_dev, - "failed to set coalesce, due to dev deacitve.\n"); - return -EBUSY; - } - if (unic_resetting(netdev)) return -EBUSY; @@ -298,7 +292,6 @@ static int unic_set_coalesce(struct net_device *netdev, tx_coal->int_ql = cmd->tx_max_coalesced_frames; rx_coal->int_ql = cmd->rx_max_coalesced_frames; - unic_net_stop_no_link_change(netdev); unic_uninit_channels(unic_dev); ret = unic_init_channels(unic_dev, unic_dev->channels.num); @@ -307,18 +300,12 @@ static int unic_set_coalesce(struct net_device *netdev, memcpy(tx_coal, &old_tx_coal, sizeof(struct unic_coalesce)); memcpy(rx_coal, &old_rx_coal, sizeof(struct unic_coalesce)); ret1 = unic_init_channels(unic_dev, unic_dev->channels.num); - if (ret1) { + if (ret1) unic_err(unic_dev, "failed to recover old channels, ret = %d.\n", ret1); - return ret; - } } - ret1 = unic_net_open_no_link_change(netdev); - if (ret1) - unic_err(unic_dev, "failed to set net open, ret = %d.\n", ret1); - return ret; } -- Gitee From c34e10890dd83218ff3ac1d7f3a43c614fef9aaa Mon Sep 17 00:00:00 2001 From: Fengyan Mu Date: Thu, 13 Nov 2025 17:52:07 +0800 Subject: [PATCH 210/214] ub: ubase: Fix link status timestamp information ANBZ: #45460 commit f348a2d07557f2bd87d672f1c006d68436a4d179 openEuler. In the current version, the timestamp recorded in the link status record changes with the system time because the TIME is obtained through the ktime_get_real_seconds function when printing the link status record. This patch fixes the issue by changing the value of TIME to the historical moment recorded in the stats array. Fixes: 58daddb6f308 ("net: unic: support subscribes to the RX stream stop and recovery interface.") Signed-off-by: Xuanyu Pu Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/debugfs/unic_debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ub/unic/debugfs/unic_debugfs.c b/drivers/net/ub/unic/debugfs/unic_debugfs.c index 0a6dbdaffedc..f58ed6f7567a 100644 --- a/drivers/net/ub/unic/debugfs/unic_debugfs.c +++ b/drivers/net/ub/unic/debugfs/unic_debugfs.c @@ -222,7 +222,7 @@ static int unic_dbg_query_link_record(struct seq_file *s, void *data) total--; idx = total % LINK_STAT_MAX_IDX; seq_printf(s, "\t%-2d\t", cnt); - ubase_dbg_format_time(ktime_get_real_seconds(), s); + ubase_dbg_format_time(record->stats[idx].link_tv_sec, s); seq_printf(s, "\t%s\n", record->stats[idx].link_status ? "LINK UP" : "LINK DOWN"); cnt++; -- Gitee From 4a186810978e3e8b7d91a43660f10fced5236ea4 Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Thu, 13 Nov 2025 15:08:53 +0800 Subject: [PATCH 211/214] net: unic: Fix lose vport_ctx and vport_buf information query in debugfs ANBZ: #45460 commit 0bc3e46d0f2d581c68f2825235a8ab10f92c71a9 openEuler. Added vport_ctx and vport_buf queries in debugfs to facilitate viewing the related configuration information of pf. Fixes: 4420dfeed31d ("net: unic: Support to query and clear historical NIC link status information") Signed-off-by: Xiongchuan Zhou Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/net/ub/unic/debugfs/unic_debugfs.c | 73 ++++++++++++++++++++++ drivers/net/ub/unic/debugfs/unic_debugfs.h | 1 + drivers/net/ub/unic/unic_cmd.h | 8 +++ drivers/net/ub/unic/unic_hw.c | 23 +++++++ drivers/net/ub/unic/unic_hw.h | 2 + include/ub/ubase/ubase_comm_cmd.h | 1 + 6 files changed, 108 insertions(+) diff --git a/drivers/net/ub/unic/debugfs/unic_debugfs.c b/drivers/net/ub/unic/debugfs/unic_debugfs.c index f58ed6f7567a..4c7bf3bb83fc 100644 --- a/drivers/net/ub/unic/debugfs/unic_debugfs.c +++ b/drivers/net/ub/unic/debugfs/unic_debugfs.c @@ -39,6 +39,61 @@ static int unic_dbg_dump_dev_info(struct seq_file *s, void *data) return 0; } +static int unic_dbg_dump_vport_buf(struct seq_file *s, void *data) +{ + struct unic_dev *unic_dev = dev_get_drvdata(s->private); + + seq_printf(s, "vport buffer num: %u\n", unic_dev->caps.vport_buf_num); + seq_printf(s, "vport buffer size: %u\n", unic_dev->caps.vport_buf_size); + return 0; +} + +static void unic_dbg_fill_vport_ctx_content(struct unic_vport_ctx_cmd *resp, + struct seq_file *s) +{ + u32 i, j; + + for (i = 0; i < UNIC_VORT_CTX_DATA_NUM; i += UNIC_VORT_CTX_DATA_ALIGN) { + seq_printf(s, "%08X: ", i * UNIC_VORT_CTX_DATA_ALIGN); + for (j = 0; j < UNIC_VORT_CTX_DATA_ALIGN; j++) { + if ((i + j) == UNIC_VORT_CTX_DATA_NUM) + break; + seq_printf(s, "%08X ", resp->data[i + j]); + } + seq_puts(s, "\n"); + } +} + +static int unic_dbg_query_vport_ctx(struct seq_file *s) +{ + struct unic_dev *unic_dev = dev_get_drvdata(s->private); + struct unic_vport_ctx_cmd resp; + u16 offset = 0; + int ret; + + do { + memset(&resp, 0, sizeof(resp)); + ret = unic_query_vport_ctx(unic_dev, offset, &resp); + if (ret) + return ret; + offset = resp.offset; + + unic_dbg_fill_vport_ctx_content(&resp, s); + } while (resp.offset); + + return 0; +} + +static int unic_dbg_dump_vport_ctx(struct seq_file *s, void *data) +{ + struct unic_dev *unic_dev = dev_get_drvdata(s->private); + + if (__unic_resetting(unic_dev)) + return -EBUSY; + + return unic_dbg_query_vport_ctx(s); +} + static const struct unic_dbg_cap_bit_info { const char *format; bool (*get_bit)(struct unic_dev *dev); @@ -265,6 +320,10 @@ static struct ubase_dbg_dentry_info unic_dbg_dentry[] = { .name = "context", .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, .support = unic_dbg_dentry_support, + }, { + .name = "vport", + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, + .support = unic_dbg_dentry_support, }, { .name = "qos", .property = UBASE_SUP_UNIC | UBASE_SUP_UBL, @@ -328,6 +387,20 @@ static struct ubase_dbg_cmd_info unic_dbg_cmd[] = { .support = unic_dbg_dentry_support, .init = ubase_dbg_seq_file_init, .read_func = unic_dbg_dump_dev_info, + }, { + .name = "vport_buf", + .dentry_index = UNIC_DBG_DENTRY_VPORT, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL_ETH, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_vport_buf, + }, { + .name = "vport_ctx", + .dentry_index = UNIC_DBG_DENTRY_VPORT, + .property = UBASE_SUP_UNIC | UBASE_SUP_UBL_ETH, + .support = unic_dbg_dentry_support, + .init = ubase_dbg_seq_file_init, + .read_func = unic_dbg_dump_vport_ctx, }, { .name = "caps_info", .dentry_index = UNIC_DBG_DENTRY_ROOT, diff --git a/drivers/net/ub/unic/debugfs/unic_debugfs.h b/drivers/net/ub/unic/debugfs/unic_debugfs.h index 73a75b091b4b..853597b90f45 100644 --- a/drivers/net/ub/unic/debugfs/unic_debugfs.h +++ b/drivers/net/ub/unic/debugfs/unic_debugfs.h @@ -15,6 +15,7 @@ enum unic_dbg_dentry_type { UNIC_DBG_DENTRY_IP = 0, UNIC_DBG_DENTRY_CONTEXT, + UNIC_DBG_DENTRY_VPORT, UNIC_DBG_DENTRY_QOS, /* must be the last entry. */ UNIC_DBG_DENTRY_ROOT diff --git a/drivers/net/ub/unic/unic_cmd.h b/drivers/net/ub/unic/unic_cmd.h index 125802234e6b..ac571815be6a 100644 --- a/drivers/net/ub/unic/unic_cmd.h +++ b/drivers/net/ub/unic/unic_cmd.h @@ -109,6 +109,14 @@ struct unic_cfg_vport_buf_cmd { __le32 buf_addr[UNIC_MAX_VPORT_BUF_NUM * U32S_PER_U64]; }; +#define UNIC_VORT_CTX_DATA_NUM 13 +#define UNIC_VORT_CTX_DATA_ALIGN 4 +struct unic_vport_ctx_cmd { + u8 resv[2]; + __le16 offset; + __le32 data[UNIC_VORT_CTX_DATA_NUM]; +}; + struct unic_cfg_fec_cmd { __le32 fec_mode; u8 rsv[20]; diff --git a/drivers/net/ub/unic/unic_hw.c b/drivers/net/ub/unic/unic_hw.c index be606bfb6495..565ac56fb638 100644 --- a/drivers/net/ub/unic/unic_hw.c +++ b/drivers/net/ub/unic/unic_hw.c @@ -674,6 +674,29 @@ int unic_cfg_vport_buf(struct unic_dev *unic_dev, bool init) return ret; } +int unic_query_vport_ctx(struct unic_dev *unic_dev, u16 offset, + struct unic_vport_ctx_cmd *resp) +{ + struct unic_vport_ctx_cmd req; + struct ubase_cmd_buf in, out; + int ret; + + memset(&req, 0, sizeof(req)); + req.offset = cpu_to_le16(offset); + + ubase_fill_inout_buf(&in, UBASE_OPC_VPORT_CTX, true, + sizeof(req), &req); + ubase_fill_inout_buf(&out, UBASE_OPC_VPORT_CTX, true, + sizeof(*resp), resp); + + ret = ubase_cmd_send_inout(unic_dev->comdev.adev, &in, &out); + if (ret) + unic_err(unic_dev, + "failed to query vport ctx, offset = %u, ret = %d.\n", + offset, ret); + return ret; +} + int unic_set_fec_mode(struct unic_dev *unic_dev, u32 fec_mode) { struct unic_cfg_fec_cmd req = {0}; diff --git a/drivers/net/ub/unic/unic_hw.h b/drivers/net/ub/unic/unic_hw.h index cfd46b6eadf4..ba64d398e44b 100644 --- a/drivers/net/ub/unic/unic_hw.h +++ b/drivers/net/ub/unic/unic_hw.h @@ -95,6 +95,8 @@ int unic_set_promisc_mode(struct unic_dev *unic_dev, struct unic_promisc_en *promisc_en); int unic_cfg_vport_buf(struct unic_dev *unic_dev, bool init); +int unic_query_vport_ctx(struct unic_dev *unic_dev, u16 offset, + struct unic_vport_ctx_cmd *resp); int unic_set_fec_mode(struct unic_dev *unic_dev, u32 fec_mode); int unic_update_fec_stats(struct unic_dev *unic_dev); int unic_set_rss_tc_mode(struct unic_dev *unic_dev, u8 tc_vaild); diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index 492b5e8513ea..b0f97fb6cba7 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -102,6 +102,7 @@ enum ubase_opcode_type { UBASE_OPC_NOTIFY_UE_RESET = 0xF006, UBASE_OPC_QUERY_UE_RST_RDY = 0xF007, UBASE_OPC_RESET_DONE = 0xF008, + UBASE_OPC_VPORT_CTX = 0xF009, UBASE_OPC_DESTROY_CTX_RESOURCE = 0xF00D, UBASE_OPC_UE2UE_UBASE = 0xF00E, UBASE_OPC_ACTIVATE_REQ = 0xF00F, -- Gitee From ee800ffa07179479b36270101467b11b064acc08 Mon Sep 17 00:00:00 2001 From: Fengyan Mu Date: Thu, 20 Nov 2025 11:29:44 +0800 Subject: [PATCH 212/214] ub: ubase: flush the work queue. ANBZ: #45460 commit b056b8147b7551de6c6a1652032f55d0b5f51a20 openEuler. Fixed an Error issue when the ctrlq crq work queue accesses the ctrlq memory during the ELR reset. Wait until the work queue is complete in the suspend phase to ensure that no work queue is executed during the reset. Fixes: 727362c0978c ("ub: ubase: Support for ELR and entity reset.") Signed-off-by: ShiLong Signed-off-by: Jianqiang Li Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_dev.c | 8 ++++++++ drivers/ub/ubase/ubase_dev.h | 2 ++ drivers/ub/ubase/ubase_reset.c | 1 + 3 files changed, 11 insertions(+) diff --git a/drivers/ub/ubase/ubase_dev.c b/drivers/ub/ubase/ubase_dev.c index 7331afa03c80..9ea0d14a4d39 100644 --- a/drivers/ub/ubase/ubase_dev.c +++ b/drivers/ub/ubase/ubase_dev.c @@ -1317,6 +1317,14 @@ int ubase_deactivate_handler(struct ubase_dev *udev, u32 bus_ue_id) return ubase_send_activate_dev_req(udev, false, (u16)bus_ue_id); } +void ubase_flush_workqueue(struct ubase_dev *udev) +{ + flush_workqueue(udev->ubase_wq); + flush_workqueue(udev->ubase_async_wq); + flush_workqueue(udev->ubase_period_wq); + flush_workqueue(udev->ubase_arq_wq); +} + int ubase_activate_dev(struct auxiliary_device *adev) { struct ubase_dev *udev; diff --git a/drivers/ub/ubase/ubase_dev.h b/drivers/ub/ubase/ubase_dev.h index 8d21ff16ef19..45605409de6b 100644 --- a/drivers/ub/ubase/ubase_dev.h +++ b/drivers/ub/ubase/ubase_dev.h @@ -451,4 +451,6 @@ void ubase_virt_handler(struct ubase_dev *udev, u16 bus_ue_id, bool is_en); int ubase_activate_handler(struct ubase_dev *udev, u32 bus_ue_id); int ubase_deactivate_handler(struct ubase_dev *udev, u32 bus_ue_id); +void ubase_flush_workqueue(struct ubase_dev *udev); + #endif diff --git a/drivers/ub/ubase/ubase_reset.c b/drivers/ub/ubase/ubase_reset.c index bb1c281c02d3..7da51b021026 100644 --- a/drivers/ub/ubase/ubase_reset.c +++ b/drivers/ub/ubase/ubase_reset.c @@ -229,6 +229,7 @@ void ubase_suspend(struct ubase_dev *udev) ubase_ctrlq_disable_remote(udev); ubase_ctrlq_disable(udev); ubase_irq_table_free(udev); + ubase_flush_workqueue(udev); } void ubase_resume(struct ubase_dev *udev) -- Gitee From 8fc10dab2cab8f7982056fc62ee7dcebed36c4de Mon Sep 17 00:00:00 2001 From: Xiongchuan Zhou Date: Fri, 14 Nov 2025 15:55:34 +0800 Subject: [PATCH 213/214] ub: ubase: Fix the lose of HIMAC reset. ANBZ: #45460 commit 6006cbfe442fd607d4042a4d6d82f8239d93aae4 openEuler. Added support for HIMAC reset, including state detection, reset command sending, and debug information output. The modifications include: Added HIMAC reset handling logic to trigger reset operations when HIMAC anomalies are detected. Included HIMAC reset count in debug information for tracking and analysis. Defined a new command code UBASE_OPC_HIMAC_RESET for sending HIMAC reset commands. These changes enable comprehensive support for HIMAC reset, covering state management, command execution, and debug output. Fixes: 727362c0978c ("ub: ubase: Support for ELR and entity reset.") Signed-off-by: Xu Wang Signed-off-by: Xiongchuan Zhou Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/debugfs/ubase_debugfs.c | 1 + drivers/ub/ubase/ubase_err_handle.c | 20 ++++++++++++++++++++ include/ub/ubase/ubase_comm_cmd.h | 1 + 3 files changed, 22 insertions(+) diff --git a/drivers/ub/ubase/debugfs/ubase_debugfs.c b/drivers/ub/ubase/debugfs/ubase_debugfs.c index bf49fc3fdc93..21fe4d1fa9b6 100644 --- a/drivers/ub/ubase/debugfs/ubase_debugfs.c +++ b/drivers/ub/ubase/debugfs/ubase_debugfs.c @@ -24,6 +24,7 @@ static int ubase_dbg_dump_rst_info(struct seq_file *s, void *data) seq_printf(s, "ELR reset count: %u\n", udev->reset_stat.elr_reset_cnt); seq_printf(s, "port reset count: %u\n", udev->reset_stat.port_reset_cnt); + seq_printf(s, "himac reset count: %u\n", udev->reset_stat.himac_reset_cnt); seq_printf(s, "reset done count: %u\n", udev->reset_stat.reset_done_cnt); seq_printf(s, "HW reset done count: %u\n", udev->reset_stat.hw_reset_done_cnt); seq_printf(s, "reset fail count: %u\n", udev->reset_stat.reset_fail_cnt); diff --git a/drivers/ub/ubase/ubase_err_handle.c b/drivers/ub/ubase/ubase_err_handle.c index d91ab0f80b6b..179e59acec0a 100644 --- a/drivers/ub/ubase/ubase_err_handle.c +++ b/drivers/ub/ubase/ubase_err_handle.c @@ -9,6 +9,19 @@ #include "ubase_reset.h" #include "ubase_err_handle.h" +static void ubase_notify_himac_reset(struct ubase_dev *udev) +{ + struct ubase_cmd_buf in; + int ret; + + __ubase_fill_inout_buf(&in, UBASE_OPC_HIMAC_RESET, false, 0, NULL); + + ret = __ubase_cmd_send_in(udev, &in); + if (ret) + ubase_err(udev, + "failed to send himac reset cmd, ret = %d.\n", ret); +} + void ubase_errhandle_service_task(struct ubase_delay_work *ubase_work) { struct ubase_dev *udev; @@ -23,6 +36,13 @@ void ubase_errhandle_service_task(struct ubase_delay_work *ubase_work) return; } + if (test_and_clear_bit(UBASE_STATE_HIMAC_RESETTING_B, &udev->state_bits)) { + ubase_err(udev, "ras occurred, ubase need to reset himac.\n"); + ubase_notify_himac_reset(udev); + udev->reset_stat.himac_reset_cnt++; + return; + } + if (test_and_clear_bit(UBASE_STATE_PORT_RESETTING_B, &udev->state_bits)) { ubase_err(udev, "ras occurred, ubase need to reset port.\n"); ubase_port_reset(udev); diff --git a/include/ub/ubase/ubase_comm_cmd.h b/include/ub/ubase/ubase_comm_cmd.h index b0f97fb6cba7..7ae969c897b9 100644 --- a/include/ub/ubase/ubase_comm_cmd.h +++ b/include/ub/ubase/ubase_comm_cmd.h @@ -87,6 +87,7 @@ enum ubase_opcode_type { UBASE_OPC_QUERY_PORT_INFO = 0x6200, UBASE_OPC_QUERY_CHIP_INFO = 0x6201, UBASE_OPC_QUERY_FEC_STATS = 0x6202, + UBASE_OPC_HIMAC_RESET = 0x6302, /* Mailbox commands */ UBASE_OPC_POST_MB = 0x7000, -- Gitee From d2889d12e47047c74c8ec0e327a6528bf7916a33 Mon Sep 17 00:00:00 2001 From: Fengyan Mu Date: Thu, 20 Nov 2025 20:02:53 +0800 Subject: [PATCH 214/214] ub: ubase: Fix CTRLQ init process ANBZ: #45460 commit 785047eef6d2a17a8016986ff9f79bff97b64d4b openEuler. Fix CTRLQ init process, the wrong uninit process shouldn't be in front of the success process which may leads some unexpected mistakes. Fixes: d7ce08663cc5 ("ub: ubase: Supports for ctrl queue management.") Signed-off-by: Chuan Wu Signed-off-by: Fengyan Mu Signed-off-by: huwentao Signed-off-by: zhangxinghao --- drivers/ub/ubase/ubase_ctrlq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/ub/ubase/ubase_ctrlq.c b/drivers/ub/ubase/ubase_ctrlq.c index 9351ed2c70fd..39deb9667f59 100644 --- a/drivers/ub/ubase/ubase_ctrlq.c +++ b/drivers/ub/ubase/ubase_ctrlq.c @@ -323,12 +323,12 @@ int ubase_ctrlq_init(struct ubase_dev *udev) udev->ctrlq.csq_next_seq = 1; atomic_set(&udev->ctrlq.req_cnt, 0); -err_crq_table_init: - ubase_ctrlq_msg_queue_uninit(udev); success: set_bit(UBASE_CTRLQ_STATE_ENABLE, &udev->ctrlq.state); return 0; +err_crq_table_init: + ubase_ctrlq_msg_queue_uninit(udev); err_msg_queue_init: ubase_ctrlq_queue_uninit(udev); return ret; -- Gitee