Add NPU device type to capabilities

In preparation to support the Linux kernel driver directly managing the
NPU without a subsystem. A NPU device type has been added to the
capabilities to be able to specify what kind of NPU is used.

Change-Id: I45345b7d75d234f821d70b858453d3af2a99194e
Signed-off-by: Mikael Olsson <mikael.olsson@arm.com>
diff --git a/driver_library/include/ethosu.hpp b/driver_library/include/ethosu.hpp
index 27f6828..d0e391c 100644
--- a/driver_library/include/ethosu.hpp
+++ b/driver_library/include/ethosu.hpp
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: Copyright 2020-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+ * SPDX-FileCopyrightText: Copyright 2020-2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
  * SPDX-License-Identifier: Apache-2.0
  *
  * Licensed under the Apache License, Version 2.0 (the License); you may
@@ -106,18 +106,39 @@
  * Hardware Configuration
  * @macsPerClockCycle:         MACs per clock cycle
  * @cmdStreamVersion:          NPU command stream version
+ * @type:                      NPU device type
  * @customDma:                 Custom DMA enabled
  */
 struct HardwareConfiguration {
 public:
-    HardwareConfiguration(uint32_t _macsPerClockCycle = 0, uint32_t _cmdStreamVersion = 0, bool _customDma = false) :
-        macsPerClockCycle(_macsPerClockCycle), cmdStreamVersion(_cmdStreamVersion), customDma(_customDma) {}
+    HardwareConfiguration(uint32_t _macsPerClockCycle = 0,
+                          uint32_t _cmdStreamVersion  = 0,
+                          uint32_t _type              = static_cast<uint32_t>(DeviceType::UNKNOWN),
+                          bool _customDma             = false) :
+        macsPerClockCycle(_macsPerClockCycle),
+        cmdStreamVersion(_cmdStreamVersion), customDma(_customDma) {
+
+        if (_type > static_cast<uint32_t>(DeviceType::DIRECT)) {
+            throw EthosU::Exception(std::string("Invalid device type: ").append(std::to_string(_type)).c_str());
+        }
+
+        type = static_cast<DeviceType>(_type);
+    }
+
+    enum class DeviceType {
+        UNKNOWN = 0,
+        SUBSYSTEM,
+        DIRECT,
+    };
 
     uint32_t macsPerClockCycle;
     uint32_t cmdStreamVersion;
+    DeviceType type;
     bool customDma;
 };
 
+std::ostream &operator<<(std::ostream &out, const HardwareConfiguration::DeviceType &deviceType);
+
 /**
  * Device capabilities
  * @hwId:                      Hardware
diff --git a/driver_library/python/src/ethosu_driver/swig/driver.i b/driver_library/python/src/ethosu_driver/swig/driver.i
index a8db7c1..721e4d0 100644
--- a/driver_library/python/src/ethosu_driver/swig/driver.i
+++ b/driver_library/python/src/ethosu_driver/swig/driver.i
@@ -1,5 +1,5 @@
 //
-// SPDX-FileCopyrightText: Copyright 2020, 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+// SPDX-FileCopyrightText: Copyright 2020, 2022-2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
 // SPDX-License-Identifier: Apache-2.0
 //
 %module driver
@@ -117,10 +117,21 @@
 %nodefaultctor HardwareConfiguration;
 class HardwareConfiguration {
     public:
-    HardwareConfiguration(uint32_t macs = 0, uint32_t cmdStreamVersion = 0, bool customDma = false);
+    HardwareConfiguration(uint32_t macs = 0, uint32_t cmdStreamVersion = 0, uint32_t type = static_cast<uint32_t>(DeviceType::UNKNOWN), bool customDma = false);
+
+    %feature("docstring",
+    "
+    DeviceType enumeration
+    ") DeviceType;
+    enum class DeviceType {
+        UNKNOWN = 0,
+        SUBSYSTEM,
+        DIRECT,
+    };
 
     uint32_t macsPerClockCycle;
     uint32_t cmdStreamVersion;
+    DeviceType type;
     bool customDma;
 };
 
@@ -129,6 +140,7 @@
         std::ostringstream out;
         out << "{macsPerClockCycle=" << $self->macsPerClockCycle <<
         ", cmdStreamVersion=" << $self->cmdStreamVersion <<
+        ", type=" << $self->type <<
         ", customDma=" <<  ($self->customDma? "True": "False") << "}";
         return out.str();
     }
diff --git a/driver_library/python/test/test_capabilities.py b/driver_library/python/test/test_capabilities.py
index ffb201c..7af1f0e 100644
--- a/driver_library/python/test/test_capabilities.py
+++ b/driver_library/python/test/test_capabilities.py
@@ -1,5 +1,5 @@
 #
-# SPDX-FileCopyrightText: Copyright 2021-2022 Arm Limited and/or its affiliates <open-source-office@arm.com>
+# SPDX-FileCopyrightText: Copyright 2021-2022, 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
 # SPDX-License-Identifier: Apache-2.0
 #
 from ethosu_driver._generated.driver import SemanticVersion
@@ -37,13 +37,14 @@
 
 
 def test_hw_configuration():
-    hw_cfg = HardwareConfiguration(128, 1, True)
+    hw_cfg = HardwareConfiguration(128, 1, HardwareConfiguration.DeviceType_SUBSYSTEM, True)
 
     assert 1 == hw_cfg.cmdStreamVersion
     assert 128 == hw_cfg.macsPerClockCycle
     assert hw_cfg.customDma
+    assert HardwareConfiguration.DeviceType_SUBSYSTEM == hw_cfg.type
 
-    assert "{macsPerClockCycle=128, cmdStreamVersion=1, customDma=True}" == hw_cfg.__str__()
+    assert "{macsPerClockCycle=128, cmdStreamVersion=1, type=subsystem, customDma=True}" == hw_cfg.__str__()
 
 
 def test_capabilities():
@@ -51,7 +52,7 @@
     product = SemanticVersion(400, 500, 600)
     architecture = SemanticVersion(700, 800, 900)
     hw_id = HardwareId(1, version, product, architecture)
-    hw_cfg = HardwareConfiguration(256, 1000, False)
+    hw_cfg = HardwareConfiguration(256, 1000, HardwareConfiguration.DeviceType_SUBSYSTEM, False)
     driver_v = SemanticVersion(10, 20, 30)
 
     cap = Capabilities(hw_id, hw_cfg, driver_v)
@@ -64,10 +65,11 @@
 
     assert 1000 == cap.hwCfg.cmdStreamVersion
     assert 256 == cap.hwCfg.macsPerClockCycle
+    assert HardwareConfiguration.DeviceType_SUBSYSTEM == cap.hwCfg.type
     assert not cap.hwCfg.customDma
 
     assert '{hwId={versionStatus=1, version={ major=100, minor=200, patch=300 }, ' \
            'product={ major=400, minor=500, patch=600 }, ' \
            'architecture={ major=700, minor=800, patch=900 }}, ' \
-           'hwCfg={macsPerClockCycle=256, cmdStreamVersion=1000, customDma=False}, ' \
+           'hwCfg={macsPerClockCycle=256, cmdStreamVersion=1000, type=subsystem, customDma=False}, ' \
            'driver={ major=10, minor=20, patch=30 }}' == cap.__str__()
diff --git a/driver_library/src/ethosu.cpp b/driver_library/src/ethosu.cpp
index a4feef1..0166f0a 100644
--- a/driver_library/src/ethosu.cpp
+++ b/driver_library/src/ethosu.cpp
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: Copyright 2020-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+ * SPDX-FileCopyrightText: Copyright 2020-2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
  * SPDX-License-Identifier: Apache-2.0
  *
  * Licensed under the Apache License, Version 2.0 (the License); you may
@@ -269,11 +269,25 @@
                    SemanticVersion(uapi.hw_id.version_major, uapi.hw_id.version_minor),
                    SemanticVersion(uapi.hw_id.product_major),
                    SemanticVersion(uapi.hw_id.arch_major_rev, uapi.hw_id.arch_minor_rev, uapi.hw_id.arch_patch_rev)),
-        HardwareConfiguration(uapi.hw_cfg.macs_per_cc, uapi.hw_cfg.cmd_stream_version, bool(uapi.hw_cfg.custom_dma)),
+        HardwareConfiguration(
+            uapi.hw_cfg.macs_per_cc, uapi.hw_cfg.cmd_stream_version, uapi.hw_cfg.type, bool(uapi.hw_cfg.custom_dma)),
         SemanticVersion(uapi.driver_major_rev, uapi.driver_minor_rev, uapi.driver_patch_rev));
     return capabilities;
 }
 
+ostream &operator<<(ostream &out, const HardwareConfiguration::DeviceType &deviceType) {
+    switch (deviceType) {
+    case HardwareConfiguration::DeviceType::UNKNOWN:
+        return out << "unknown";
+    case HardwareConfiguration::DeviceType::SUBSYSTEM:
+        return out << "subsystem";
+    case HardwareConfiguration::DeviceType::DIRECT:
+        return out << "direct";
+    default:
+        throw Exception(string("Invalid device type: ").append(to_string(static_cast<uint32_t>(deviceType))).c_str());
+    }
+}
+
 /****************************************************************************
  * Buffer
  ****************************************************************************/
diff --git a/kernel/include/uapi/ethosu.h b/kernel/include/uapi/ethosu.h
index 58b9c34..699ef7e 100644
--- a/kernel/include/uapi/ethosu.h
+++ b/kernel/include/uapi/ethosu.h
@@ -194,15 +194,30 @@
 };
 
 /**
+ * enum ethosu_dev_type - NPU device type
+ * @ETHOSU_DEV_UNKNOWN:       Unknown NPU device type
+ * @ETHOSU_DEV_SUBSYSTEM:     NPU managed by a subsystem communicated with via a
+ *                            mailbox
+ * @ETHOSU_DEV_DIRECT:        NPU directly managed by the kernel driver
+ */
+enum ethosu_dev_type {
+	ETHOSU_DEV_UNKNOWN = 0,
+	ETHOSU_DEV_SUBSYSTEM,
+	ETHOSU_DEV_DIRECT,
+};
+
+/**
  * struct ethosu_uapi_device_hw_cfg - Device hardware configuration
  * @macs_per_cc:               MACs per clock cycle
  * @cmd_stream_version:        NPU command stream version
  * @custom_dma:                Custom DMA enabled
+ * @type:                      NPU device type
  */
 struct ethosu_uapi_device_hw_cfg {
 	__u32 macs_per_cc;
 	__u32 cmd_stream_version;
 	__u32 custom_dma;
+	__u32 type;
 };
 
 /**
diff --git a/kernel/rpmsg/ethosu_rpmsg_capabilities.c b/kernel/rpmsg/ethosu_rpmsg_capabilities.c
index 3c21d3e..30a9aef 100644
--- a/kernel/rpmsg/ethosu_rpmsg_capabilities.c
+++ b/kernel/rpmsg/ethosu_rpmsg_capabilities.c
@@ -97,6 +97,7 @@
 	cap->uapi->hw_cfg.macs_per_cc = rsp->macs_per_cc;
 	cap->uapi->hw_cfg.cmd_stream_version = rsp->cmd_stream_version;
 	cap->uapi->hw_cfg.custom_dma = rsp->custom_dma;
+	cap->uapi->hw_cfg.type = ETHOSU_DEV_SUBSYSTEM;
 
 	cap->errno = 0;
 	complete(&cap->done);
diff --git a/tests/run_inference_test.cpp b/tests/run_inference_test.cpp
index 512a2a0..88fc691 100644
--- a/tests/run_inference_test.cpp
+++ b/tests/run_inference_test.cpp
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+ * SPDX-FileCopyrightText: Copyright 2022-2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
  * SPDX-License-Identifier: Apache-2.0
  *
  * Licensed under the Apache License, Version 2.0 (the License); you may
@@ -68,6 +68,7 @@
     } catch (std::exception &e) { throw TestFailureException("Capabilities test: ", e.what()); }
 
     TEST_ASSERT(capabilities.hwId.architecture > SemanticVersion());
+    TEST_ASSERT(capabilities.hwCfg.type == HardwareConfiguration::DeviceType::SUBSYSTEM);
 }
 
 void testBufferSeek(const Device &device) {
diff --git a/utils/inference_runner/inference_runner.cpp b/utils/inference_runner/inference_runner.cpp
index d2546e2..531bd4e 100644
--- a/utils/inference_runner/inference_runner.cpp
+++ b/utils/inference_runner/inference_runner.cpp
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: Copyright 2020-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+ * SPDX-FileCopyrightText: Copyright 2020-2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
  * SPDX-License-Identifier: Apache-2.0
  *
  * Licensed under the Apache License, Version 2.0 (the License); you may
@@ -227,6 +227,7 @@
              << "\tdriver:" << capabilities.driver << endl
              << "\tmacs_per_cc:" << unsigned(capabilities.hwCfg.macsPerClockCycle) << endl
              << "\tcmd_stream_version:" << unsigned(capabilities.hwCfg.cmdStreamVersion) << endl
+             << "\ttype:" << capabilities.hwCfg.type << endl
              << "\tcustom_dma:" << std::boolalpha << capabilities.hwCfg.customDma << endl;
 
         /* Create network */