drivers: Add GNSS API public header

This commit adds the public header for the GNSS API, along
with the initial GNSS Kconfig file and an entry in the
common linker file for registered GNSS data callbacks.

A very naive implementation of the GNSS data callback is
provided as well in drivers/gnss/gnss_publish.c

Signed-off-by: Bjarki Arge Andreasen <bjarkix123@gmail.com>
diff --git a/cmake/linker_script/common/common-rom.cmake b/cmake/linker_script/common/common-rom.cmake
index d81b215..35c023e 100644
--- a/cmake/linker_script/common/common-rom.cmake
+++ b/cmake/linker_script/common/common-rom.cmake
@@ -218,3 +218,11 @@
   zephyr_iterable_section(NAME zbus_channel KVMA RAM_REGION GROUP RODATA_REGION SUBALIGN 4)
   zephyr_iterable_section(NAME zbus_channel_observation KVMA RAM_REGION GROUP RODATA_REGION SUBALIGN 4)
 endif()
+
+if(CONFIG_GNSS)
+  zephyr_iterable_section(NAME gnss_data_callback KVMA RAM_REGION GROUP RODATA_REGION SUBALIGN 4)
+endif()
+
+if(CONFIG_GNSS_SATELLITES)
+  zephyr_iterable_section(NAME gnss_satellites_callback KVMA RAM_REGION GROUP RODATA_REGION SUBALIGN 4)
+endif()
diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt
index e9a1603..b7fcdbe 100644
--- a/drivers/CMakeLists.txt
+++ b/drivers/CMakeLists.txt
@@ -37,6 +37,7 @@
 add_subdirectory_ifdef(CONFIG_FLASH flash)
 add_subdirectory_ifdef(CONFIG_FPGA fpga)
 add_subdirectory_ifdef(CONFIG_FUEL_GAUGE fuel_gauge)
+add_subdirectory_ifdef(CONFIG_GNSS gnss)
 add_subdirectory_ifdef(CONFIG_GPIO gpio)
 add_subdirectory_ifdef(CONFIG_HWINFO hwinfo)
 add_subdirectory_ifdef(CONFIG_I2C i2c)
diff --git a/drivers/Kconfig b/drivers/Kconfig
index f486bad..1eca764 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -31,6 +31,7 @@
 source "drivers/flash/Kconfig"
 source "drivers/fpga/Kconfig"
 source "drivers/fuel_gauge/Kconfig"
+source "drivers/gnss/Kconfig"
 source "drivers/gpio/Kconfig"
 source "drivers/hwinfo/Kconfig"
 source "drivers/i2c/Kconfig"
diff --git a/drivers/gnss/CMakeLists.txt b/drivers/gnss/CMakeLists.txt
new file mode 100644
index 0000000..71a4d9d
--- /dev/null
+++ b/drivers/gnss/CMakeLists.txt
@@ -0,0 +1,5 @@
+# Copyright (c) 2023 Trackunit Corporation
+# SPDX-License-Identifier: Apache-2.0
+
+zephyr_library()
+zephyr_library_sources(gnss_publish.c)
diff --git a/drivers/gnss/Kconfig b/drivers/gnss/Kconfig
new file mode 100644
index 0000000..4afb167
--- /dev/null
+++ b/drivers/gnss/Kconfig
@@ -0,0 +1,21 @@
+# Copyright (c) 2023 Trackunit Corporation
+# SPDX-License-Identifier: Apache-2.0
+
+menuconfig GNSS
+	bool "GNSS drivers"
+	select EXPERIMENTAL
+	help
+	  Enable GNSS drivers and configuration.
+
+if GNSS
+
+config GNSS_SATELLITES
+	bool "GNSS satellites support"
+	help
+	  Enable GNSS sattelites callback.
+
+module = GNSS
+module-str = gnss
+source "subsys/logging/Kconfig.template.log_config"
+
+endif
diff --git a/drivers/gnss/gnss_publish.c b/drivers/gnss/gnss_publish.c
new file mode 100644
index 0000000..98e3a2e
--- /dev/null
+++ b/drivers/gnss/gnss_publish.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2023 Trackunit Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "gnss_publish.h"
+#include <zephyr/kernel.h>
+#include <zephyr/sys/iterable_sections.h>
+
+static struct k_spinlock lock;
+
+void gnss_publish_data(const struct device *dev, const struct gnss_data *data)
+{
+	K_SPINLOCK(&lock) {
+		STRUCT_SECTION_FOREACH(gnss_data_callback, callback) {
+			if (callback->dev == NULL || callback->dev == dev) {
+				callback->callback(dev, data);
+			}
+		}
+	}
+}
+
+#if CONFIG_GNSS_SATELLITES
+void gnss_publish_satellites(const struct device *dev, const struct gnss_satellite *satellites,
+			     uint16_t size)
+{
+	K_SPINLOCK(&lock) {
+		STRUCT_SECTION_FOREACH(gnss_satellites_callback, callback) {
+			if (callback->dev == NULL || callback->dev == dev) {
+				callback->callback(dev, satellites, size);
+			}
+		}
+	}
+}
+#endif
diff --git a/drivers/gnss/gnss_publish.h b/drivers/gnss/gnss_publish.h
new file mode 100644
index 0000000..b686c7e
--- /dev/null
+++ b/drivers/gnss/gnss_publish.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2023 Trackunit Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef ZEPHYR_DRIVERS_GNSS_GNSS_H_
+#define ZEPHYR_DRIVERS_GNSS_GNSS_H_
+
+#include <zephyr/drivers/gnss.h>
+
+/** Internal function used by GNSS drivers to publish GNSS data */
+void gnss_publish_data(const struct device *dev, const struct gnss_data *data);
+
+/** Internal function used by GNSS drivers to publish GNSS satellites */
+void gnss_publish_satellites(const struct device *dev, const struct gnss_satellite *satellites,
+			     uint16_t size);
+
+#endif /* ZEPHYR_DRIVERS_GNSS_GNSS_H_ */
diff --git a/include/zephyr/drivers/gnss.h b/include/zephyr/drivers/gnss.h
new file mode 100644
index 0000000..4c25bd8
--- /dev/null
+++ b/include/zephyr/drivers/gnss.h
@@ -0,0 +1,496 @@
+/*
+ * Copyright (c) 2023 Trackunit Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file gnss.h
+ * @brief Public GNSS API.
+ */
+
+#ifndef ZEPHYR_INCLUDE_DRIVERS_GNSS_H_
+#define ZEPHYR_INCLUDE_DRIVERS_GNSS_H_
+
+/**
+ * @brief GNSS Interface
+ * @defgroup gnss_interface GNSS Interface
+ * @ingroup io_interfaces
+ * @{
+ */
+
+#include <zephyr/types.h>
+#include <zephyr/device.h>
+#include <zephyr/data/navigation.h>
+#include <errno.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** GNSS PPS modes */
+enum gnss_pps_mode {
+	/** PPS output disabled */
+	GNSS_PPS_MODE_DISABLED = 0,
+	/** PPS output always enabled */
+	GNSS_PPS_MODE_ENABLED = 1,
+	/** PPS output enabled from first lock */
+	GNSS_PPS_MODE_ENABLED_AFTER_LOCK = 2,
+	/** PPS output enabled while locked */
+	GNSS_PPS_MODE_ENABLED_WHILE_LOCKED = 3
+};
+
+/** API for setting fix rate */
+typedef int (*gnss_set_fix_rate_t)(const struct device *dev, uint32_t fix_interval_ms);
+
+/** API for getting fix rate */
+typedef int (*gnss_get_fix_rate_t)(const struct device *dev, uint32_t *fix_interval_ms);
+
+/**
+ * @brief GNSS periodic tracking configuration
+ *
+ * @note Setting either active_time or inactive_time to 0 will disable periodic
+ * function.
+ */
+struct gnss_periodic_config {
+	/** The time the GNSS will spend in the active state in ms */
+	uint32_t active_time_ms;
+	/** The time the GNSS will spend in the inactive state in ms */
+	uint32_t inactive_time_ms;
+};
+
+/** API for setting periodic tracking configuration */
+typedef int (*gnss_set_periodic_config_t)(const struct device *dev,
+					  const struct gnss_periodic_config *periodic_config);
+
+/** API for setting periodic tracking configuration */
+typedef int (*gnss_get_periodic_config_t)(const struct device *dev,
+					  struct gnss_periodic_config *periodic_config);
+
+/** GNSS navigation modes */
+enum gnss_navigation_mode {
+	/** Dynamics have no impact on tracking */
+	GNSS_NAVIGATION_MODE_ZERO_DYNAMICS = 0,
+	/** Low dynamics have higher impact on tracking */
+	GNSS_NAVIGATION_MODE_LOW_DYNAMICS = 1,
+	/** Low and high dynamics have equal impact on tracking */
+	GNSS_NAVIGATION_MODE_BALANCED_DYNAMICS = 2,
+	/** High dynamics have higher impact on tracking */
+	GNSS_NAVIGATION_MODE_HIGH_DYNAMICS = 3
+};
+
+/** API for setting navigation mode */
+typedef int (*gnss_set_navigation_mode_t)(const struct device *dev,
+					  enum gnss_navigation_mode mode);
+
+/** API for getting navigation mode */
+typedef int (*gnss_get_navigation_mode_t)(const struct device *dev,
+					  enum gnss_navigation_mode *mode);
+
+/** Systems contained in gnss_systems_t */
+enum gnss_system {
+	/** Global Positioning System (GPS) */
+	GNSS_SYSTEM_GPS = BIT(0),
+	/** GLObal NAvigation Satellite System (GLONASS) */
+	GNSS_SYSTEM_GLONASS = BIT(1),
+	/** Galileo */
+	GNSS_SYSTEM_GALILEO = BIT(2),
+	/** BeiDou Navigation Satellite System */
+	GNSS_SYSTEM_BEIDOU = BIT(3),
+	/** Quasi-Zenith Satellite System (QZSS) */
+	GNSS_SYSTEM_QZSS = BIT(4),
+	/** Indian Regional Navigation Satellite System (IRNSS) */
+	GNSS_SYSTEM_IRNSS = BIT(5),
+	/** Satellite-Based Augmentation System (SBAS) */
+	GNSS_SYSTEM_SBAS = BIT(6),
+	/** Indoor Messaging System (IMES) */
+	GNSS_SYSTEM_IMES = BIT(7),
+};
+
+/** Type storing bitmask of GNSS systems */
+typedef uint32_t gnss_systems_t;
+
+/** API for enabling systems */
+typedef int (*gnss_set_enabled_systems_t)(const struct device *dev, gnss_systems_t systems);
+
+/** API for getting enabled systems */
+typedef int (*gnss_get_enabled_systems_t)(const struct device *dev, gnss_systems_t *systems);
+
+/** API for getting enabled systems */
+typedef int (*gnss_get_supported_systems_t)(const struct device *dev, gnss_systems_t *systems);
+
+/** GNSS fix status */
+enum gnss_fix_status {
+	/** No GNSS fix aqcuired */
+	GNSS_FIX_STATUS_NO_FIX = 0,
+	/** GNSS fix aqcuired */
+	GNSS_FIX_STATUS_GNSS_FIX = 1,
+	/** Differential GNSS fix acquired */
+	GNSS_FIX_STATUS_DGNSS_FIX = 2,
+	/** Estimated fix acquired */
+	GNSS_FIX_STATUS_ESTIMATED_FIX = 3,
+};
+
+/** GNSS fix quality */
+enum gnss_fix_quality {
+	/** Invalid fix */
+	GNSS_FIX_QUALITY_INVALID = 0,
+	/** Standard positioning service */
+	GNSS_FIX_QUALITY_GNSS_SPS = 1,
+	/** Differential GNSS */
+	GNSS_FIX_QUALITY_DGNSS = 2,
+	/** Precise positioning service */
+	GNSS_FIX_QUALITY_GNSS_PPS = 3,
+	/** Real-time kinematic */
+	GNSS_FIX_QUALITY_RTK = 4,
+	/** Floating real-time kinematic */
+	GNSS_FIX_QUALITY_FLOAT_RTK = 5,
+	/** Estimated fix */
+	GNSS_FIX_QUALITY_ESTIMATED = 6,
+};
+
+/** GNSS info data structure */
+struct gnss_info {
+	/** Number of satellites being tracked */
+	uint16_t satellites_cnt;
+	/** Horizontal dilution of precision in 1/1000 */
+	uint16_t hdop;
+	/** The fix status */
+	enum gnss_fix_status fix_status;
+	/** The fix quality */
+	enum gnss_fix_quality fix_quality;
+};
+
+/** GNSS time data structure */
+struct gnss_time {
+	/** Hour [0, 23] */
+	uint8_t hour;
+	/** Minute [0, 59] */
+	uint8_t minute;
+	/** Millisecond [0, 59999] */
+	uint16_t millisecond;
+	/** Day of month [1, 31] */
+	uint8_t month_day;
+	/** Month [1, 12] */
+	uint8_t month;
+	/** Year [0, 99] */
+	uint8_t century_year;
+};
+
+/** GNSS API structure */
+__subsystem struct gnss_driver_api {
+	gnss_set_fix_rate_t set_fix_rate;
+	gnss_get_fix_rate_t get_fix_rate;
+	gnss_set_periodic_config_t set_periodic_config;
+	gnss_get_periodic_config_t get_periodic_config;
+	gnss_set_navigation_mode_t set_navigation_mode;
+	gnss_get_navigation_mode_t get_navigation_mode;
+	gnss_set_enabled_systems_t set_enabled_systems;
+	gnss_get_enabled_systems_t get_enabled_systems;
+	gnss_get_supported_systems_t get_supported_systems;
+};
+
+/** GNSS data structure */
+struct gnss_data {
+	/** Navigation data acquired */
+	struct navigation_data nav_data;
+	/** GNSS info when navigation data was acquired */
+	struct gnss_info info;
+	/** UTC time when data was acquired */
+	struct gnss_time utc;
+};
+
+/** Template for GNSS data callback */
+typedef void (*gnss_data_callback_t)(const struct device *dev, const struct gnss_data *data);
+
+/** GNSS callback structure */
+struct gnss_data_callback {
+	/** Filter callback to GNSS data from this device if not NULL */
+	const struct device *dev;
+	/** Callback called when GNSS data is published */
+	gnss_data_callback_t callback;
+};
+
+/** GNSS satellite structure */
+struct gnss_satellite {
+	/** Pseudo-random noise sequence */
+	uint8_t prn;
+	/** Signal-to-noise ratio in dB */
+	uint8_t snr;
+	/** Elevation in degrees [0, 90] */
+	uint8_t elevation;
+	/** Azimuth relative to True North in degrees [0, 359] */
+	uint16_t azimuth;
+	/** System of satellite */
+	enum gnss_system system;
+	/** True if satellite is being tracked */
+	uint8_t is_tracked : 1;
+};
+
+/** Template for GNSS satellites callback */
+typedef void (*gnss_satellites_callback_t)(const struct device *dev,
+					   const struct gnss_satellite *satellites,
+					   uint16_t size);
+
+/** GNSS callback structure */
+struct gnss_satellites_callback {
+	/** Filter callback to GNSS data from this device if not NULL */
+	const struct device *dev;
+	/** Callback called when GNSS satellites is published */
+	gnss_satellites_callback_t callback;
+};
+
+/**
+ * @brief Set the GNSS fix rate
+ *
+ * @param dev Device instance
+ * @param fix_interval_ms Fix interval to set in milliseconds
+ *
+ * @return 0 if successful
+ * @return -errno negative errno code on failure
+ */
+__syscall int gnss_set_fix_rate(const struct device *dev, uint32_t fix_interval_ms);
+
+static inline int z_impl_gnss_set_fix_rate(const struct device *dev, uint32_t fix_interval_ms)
+{
+	const struct gnss_driver_api *api = (const struct gnss_driver_api *)dev->api;
+
+	if (api->set_fix_rate == NULL) {
+		return -ENOSYS;
+	}
+
+	return api->set_fix_rate(dev, fix_interval_ms);
+}
+
+/**
+ * @brief Get the GNSS fix rate
+ *
+ * @param dev Device instance
+ * @param fix_interval_ms Destination for fix interval in milliseconds
+ *
+ * @return 0 if successful
+ * @return -errno negative errno code on failure
+ */
+__syscall int gnss_get_fix_rate(const struct device *dev, uint32_t *fix_interval_ms);
+
+static inline int z_impl_gnss_get_fix_rate(const struct device *dev, uint32_t *fix_interval_ms)
+{
+	const struct gnss_driver_api *api = (const struct gnss_driver_api *)dev->api;
+
+	if (api->get_fix_rate == NULL) {
+		return -ENOSYS;
+	}
+
+	return api->get_fix_rate(dev, fix_interval_ms);
+}
+
+/**
+ * @brief Set the GNSS periodic tracking configuration
+ *
+ * @param dev Device instance
+ * @param config Periodic tracking configuration to set
+ *
+ * @return 0 if successful
+ * @return -errno negative errno code on failure
+ */
+__syscall int gnss_set_periodic_config(const struct device *dev,
+				       const struct gnss_periodic_config *config);
+
+static inline int z_impl_gnss_set_periodic_config(const struct device *dev,
+						  const struct gnss_periodic_config *config)
+{
+	const struct gnss_driver_api *api = (const struct gnss_driver_api *)dev->api;
+
+	if (api->set_periodic_config == NULL) {
+		return -ENOSYS;
+	}
+
+	return api->set_periodic_config(dev, config);
+}
+
+/**
+ * @brief Get the GNSS periodic tracking configuration
+ *
+ * @param dev Device instance
+ * @param config Destination for periodic tracking configuration
+ *
+ * @return 0 if successful
+ * @return -errno negative errno code on failure
+ */
+__syscall int gnss_get_periodic_config(const struct device *dev,
+				       struct gnss_periodic_config *config);
+
+static inline int z_impl_gnss_get_periodic_config(const struct device *dev,
+						  struct gnss_periodic_config *config)
+{
+	const struct gnss_driver_api *api = (const struct gnss_driver_api *)dev->api;
+
+	if (api->get_periodic_config == NULL) {
+		return -ENOSYS;
+	}
+
+	return api->get_periodic_config(dev, config);
+}
+
+/**
+ * @brief Set the GNSS navigation mode
+ *
+ * @param dev Device instance
+ * @param mode Navigation mode to set
+ *
+ * @return 0 if successful
+ * @return -errno negative errno code on failure
+ */
+__syscall int gnss_set_navigation_mode(const struct device *dev,
+				       enum gnss_navigation_mode mode);
+
+static inline int z_impl_gnss_set_navigation_mode(const struct device *dev,
+						  enum gnss_navigation_mode mode)
+{
+	const struct gnss_driver_api *api = (const struct gnss_driver_api *)dev->api;
+
+	if (api->set_navigation_mode == NULL) {
+		return -ENOSYS;
+	}
+
+	return api->set_navigation_mode(dev, mode);
+}
+
+/**
+ * @brief Get the GNSS navigation mode
+ *
+ * @param dev Device instance
+ * @param mode Destination for navigation mode
+ *
+ * @return 0 if successful
+ * @return -errno negative errno code on failure
+ */
+__syscall int gnss_get_navigation_mode(const struct device *dev,
+				       enum gnss_navigation_mode *mode);
+
+static inline int z_impl_gnss_get_navigation_mode(const struct device *dev,
+						  enum gnss_navigation_mode *mode)
+{
+	const struct gnss_driver_api *api = (const struct gnss_driver_api *)dev->api;
+
+	if (api->get_navigation_mode == NULL) {
+		return -ENOSYS;
+	}
+
+	return api->get_navigation_mode(dev, mode);
+}
+
+/**
+ * @brief Set enabled GNSS systems
+ *
+ * @param dev Device instance
+ * @param systems Systems to enable
+ *
+ * @return 0 if successful
+ * @return -errno negative errno code on failure
+ */
+__syscall int gnss_set_enabled_systems(const struct device *dev, gnss_systems_t systems);
+
+static inline int z_impl_gnss_set_enabled_systems(const struct device *dev,
+						  gnss_systems_t systems)
+{
+	const struct gnss_driver_api *api = (const struct gnss_driver_api *)dev->api;
+
+	if (api->set_enabled_systems == NULL) {
+		return -ENOSYS;
+	}
+
+	return api->set_enabled_systems(dev, systems);
+}
+
+/**
+ * @brief Get enabled GNSS systems
+ *
+ * @param dev Device instance
+ * @param systems Destination for enabled systems
+ *
+ * @return 0 if successful
+ * @return -errno negative errno code on failure
+ */
+__syscall int gnss_get_enabled_systems(const struct device *dev, gnss_systems_t *systems);
+
+static inline int z_impl_gnss_get_enabled_systems(const struct device *dev,
+						  gnss_systems_t *systems)
+{
+	const struct gnss_driver_api *api = (const struct gnss_driver_api *)dev->api;
+
+	if (api->get_enabled_systems == NULL) {
+		return -ENOSYS;
+	}
+
+	return api->get_enabled_systems(dev, systems);
+}
+
+/**
+ * @brief Get supported GNSS systems
+ *
+ * @param dev Device instance
+ * @param systems Destination for supported systems
+ *
+ * @return 0 if successful
+ * @return -errno negative errno code on failure
+ */
+__syscall int gnss_get_supported_systems(const struct device *dev, gnss_systems_t *systems);
+
+static inline int z_impl_gnss_get_supported_systems(const struct device *dev,
+						    gnss_systems_t *systems)
+{
+	const struct gnss_driver_api *api = (const struct gnss_driver_api *)dev->api;
+
+	if (api->get_supported_systems == NULL) {
+		return -ENOSYS;
+	}
+
+	return api->get_supported_systems(dev, systems);
+}
+
+/**
+ * @brief Register a callback structure for GNSS data published
+ *
+ * @param _dev Device pointer
+ * @param _callback The callback function
+ */
+#if CONFIG_GNSS
+#define GNSS_DATA_CALLBACK_DEFINE(_dev, _callback)                                              \
+	static const STRUCT_SECTION_ITERABLE(gnss_data_callback,                                \
+					     _gnss_data_callback__##_callback) = {              \
+		.dev = _dev,                                                                    \
+		.callback = _callback,                                                          \
+	}
+#else
+#define GNSS_DATA_CALLBACK_DEFINE(_dev, _callback)
+#endif
+
+/**
+ * @brief Register a callback structure for GNSS satellites published
+ *
+ * @param _dev Device pointer
+ * @param _callback The callback function
+ */
+#if CONFIG_GNSS_SATELLITES
+#define GNSS_SATELLITES_CALLBACK_DEFINE(_dev, _callback)                                        \
+	static const STRUCT_SECTION_ITERABLE(gnss_satellites_callback,                          \
+					     _gnss_satellites_callback__##_callback) = {        \
+		.dev = _dev,                                                                    \
+		.callback = _callback,                                                          \
+	}
+#else
+#define GNSS_SATELLITES_CALLBACK_DEFINE(_dev, _callback)
+#endif
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <syscalls/gnss.h>
+
+#endif /* ZEPHYR_INCLUDE_DRIVERS_GNSS_H_ */
diff --git a/include/zephyr/linker/common-rom/common-rom-misc.ld b/include/zephyr/linker/common-rom/common-rom-misc.ld
index 5da776a..1fbf777 100644
--- a/include/zephyr/linker/common-rom/common-rom-misc.ld
+++ b/include/zephyr/linker/common-rom/common-rom-misc.ld
@@ -59,3 +59,11 @@
 	ITERABLE_SECTION_ROM(shell_dynamic_subcmds, 4)
 
 	ITERABLE_SECTION_ROM(cfb_font, 4)
+
+#if defined(CONFIG_GNSS)
+	ITERABLE_SECTION_ROM(gnss_data_callback, 4)
+#endif
+
+#if defined(CONFIG_GNSS_SATELLITES)
+	ITERABLE_SECTION_ROM(gnss_satellites_callback, 4)
+#endif