device: only allow PRE_KERNEL_1/2 and POST_KERNEL levels

Because devices share the same underlying infrastructure with SYS_INIT,
the same levels have always been available. However, not all of them are
needed, and some are not even usable. For example, `EARLY` can't be used
because when initialized `z_device_state_init` has not been called yet.
`SMP` has never been used by device drivers, and it is now in question,
likely to be moved to SMP specific hooks. Finally, `APPLICATION` does
not make much sense in the context of Kernel devices. Note that of the 3
levels just mentioned, only one was actively tested (`APPLICATION`) by
Kernel tests, meaning others were likely never considered in the context
of devices.

This patch leaves `PRE_KERNEL_1`, `PRE_KERNEL_2` and `POST_KERNEL`
available to devices. Others have been deprecated, and will generate a
compiler warning if used.

Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
diff --git a/doc/releases/migration-guide-3.5.rst b/doc/releases/migration-guide-3.5.rst
index 9e5c3b3..387aaca 100644
--- a/doc/releases/migration-guide-3.5.rst
+++ b/doc/releases/migration-guide-3.5.rst
@@ -94,6 +94,13 @@
   marked as deprecated as well. The new modem subsystem :kconfig:option:`CONFIG_MODEM_CMUX`
   and :kconfig:option:`CONFIG_MODEM_PPP`` should be used instead.
 
+* Device drivers should now be restricted to ``PRE_KERNEL_1``, ``PRE_KERNEL_2``
+  and ``POST_KERNEL`` initialization levels. Other device initialization levels,
+  including ``EARLY``, ``APPLICATION``, and ``SMP``, have been deprecated and
+  will be removed in future releases. Note that these changes do not apply to
+  initialization levels used in the context of the ``init.h`` API,
+  e.g. :c:macro:`SYS_INIT`.
+
 Picolibc-related Changes
 ************************
 
diff --git a/include/zephyr/device.h b/include/zephyr/device.h
index ce9b6c1..ca59579 100644
--- a/include/zephyr/device.h
+++ b/include/zephyr/device.h
@@ -118,8 +118,8 @@
  * stored in @ref device.data.
  * @param config Pointer to the device's private constant data, which will be
  * stored in @ref device.config.
- * @param level The device's initialization level. See @ref sys_init for
- * details.
+ * @param level The device's initialization level (PRE_KERNEL_1, PRE_KERNEL_2 or
+ * POST_KERNEL).
  * @param prio The device's priority within its initialization level. See
  * SYS_INIT() for details.
  * @param api Pointer to the device's API structure. Can be `NULL`.
@@ -169,7 +169,8 @@
  * stored in @ref device.data.
  * @param config Pointer to the device's private constant data, which will be
  * stored in @ref device.config field.
- * @param level The device's initialization level. See SYS_INIT() for details.
+ * @param level The device's initialization level (PRE_KERNEL_1, PRE_KERNEL_2 or
+ * POST_KERNEL).
  * @param prio The device's priority within its initialization level. See
  * SYS_INIT() for details.
  * @param api Pointer to the device's API structure. Can be `NULL`.
@@ -931,6 +932,25 @@
 		DEVICE_NAME_GET(dev_id)) =                                     \
 		Z_DEVICE_INIT(name, pm, data, config, api, state, deps)
 
+/* deprecated device initialization levels */
+#define Z_DEVICE_LEVEL_DEPRECATED_EARLY                                        \
+	__WARN("EARLY device driver level is deprecated")
+#define Z_DEVICE_LEVEL_DEPRECATED_PRE_KERNEL_1
+#define Z_DEVICE_LEVEL_DEPRECATED_PRE_KERNEL_2
+#define Z_DEVICE_LEVEL_DEPRECATED_POST_KERNEL
+#define Z_DEVICE_LEVEL_DEPRECATED_APPLICATION                                  \
+	__WARN("APPLICATION device driver level is deprecated")
+#define Z_DEVICE_LEVEL_DEPRECATED_SMP                                          \
+	__WARN("SMP device driver level is deprecated")
+
+/**
+ * @brief Issue a warning if the given init level is deprecated.
+ *
+ * @param level Init level
+ */
+#define Z_DEVICE_LEVEL_CHECK_DEPRECATED_LEVEL(level)                           \
+	Z_DEVICE_LEVEL_DEPRECATED_##level
+
 /**
  * @brief Define the init entry for a device.
  *
@@ -942,6 +962,8 @@
  * @param prio Initialization priority.
  */
 #define Z_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id, init_fn_, level, prio)     \
+	Z_DEVICE_LEVEL_CHECK_DEPRECATED_LEVEL(level)                           \
+                                                                               \
 	static const Z_DECL_ALIGN(struct init_entry) __used __noasan           \
 		Z_INIT_ENTRY_SECTION(level, prio,                              \
 				     Z_DEVICE_INIT_SUB_PRIO(node_id))          \