diff --git a/drivers/misc/CMakeLists.txt b/drivers/misc/CMakeLists.txt index da3ee6da541..62b53f4f1ce 100644 --- a/drivers/misc/CMakeLists.txt +++ b/drivers/misc/CMakeLists.txt @@ -4,3 +4,4 @@ add_subdirectory_ifdef(CONFIG_ARM_ETHOS_U ethos_u) add_subdirectory_ifdef(CONFIG_FT800 ft8xx) add_subdirectory_ifdef(CONFIG_GROVE_LCD_RGB grove_lcd_rgb) add_subdirectory_ifdef(CONFIG_PIO_RPI_PICO pio_rpi_pico) +add_subdirectory_ifdef(CONFIG_NXP_S32_EMIOS nxp_s32_emios) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index e8137dcc131..342d9fb70f9 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -8,5 +8,6 @@ menu "Miscellaneous Drivers" source "drivers/misc/ft8xx/Kconfig" source "drivers/misc/grove_lcd_rgb/Kconfig" source "drivers/misc/pio_rpi_pico/Kconfig" +source "drivers/misc/nxp_s32_emios/Kconfig" endmenu diff --git a/drivers/misc/nxp_s32_emios/CMakeLists.txt b/drivers/misc/nxp_s32_emios/CMakeLists.txt new file mode 100644 index 00000000000..72320bb5eae --- /dev/null +++ b/drivers/misc/nxp_s32_emios/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources_ifdef(CONFIG_NXP_S32_EMIOS + nxp_s32_emios.c +) diff --git a/drivers/misc/nxp_s32_emios/Kconfig b/drivers/misc/nxp_s32_emios/Kconfig new file mode 100644 index 00000000000..83f60cf70d2 --- /dev/null +++ b/drivers/misc/nxp_s32_emios/Kconfig @@ -0,0 +1,22 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +config NXP_S32_EMIOS + bool "NXP S32 eMIOS drivers" + depends on DT_HAS_NXP_S32_EMIOS_ENABLED + help + Enable drivers for NXP S32 EMIOS + +if NXP_S32_EMIOS + +module = NXP_S32_EMIOS +module-str = NXP S32 eMIOS +source "subsys/logging/Kconfig.template.log_config" + +config NXP_S32_EMIOS_INIT_PRIORITY + int "NXP S32 eMIOS initialization priority" + default KERNEL_INIT_PRIORITY_DEVICE + help + System initialization priority for NXP S32 eMIOS drivers. + +endif diff --git a/drivers/misc/nxp_s32_emios/nxp_s32_emios.c b/drivers/misc/nxp_s32_emios/nxp_s32_emios.c new file mode 100644 index 00000000000..fd0a8c891ae --- /dev/null +++ b/drivers/misc/nxp_s32_emios/nxp_s32_emios.c @@ -0,0 +1,105 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#define LOG_MODULE_NAME nxp_s32_emios +#include +LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_NXP_S32_EMIOS_LOG_LEVEL); + +#include + +#define DT_DRV_COMPAT nxp_s32_emios + +struct nxp_s32_emios_config { + uint8_t instance; + Emios_Mcl_Ip_ConfigType *mcl_info; +}; + +static int nxp_s32_emios_init(const struct device *dev) +{ + const struct nxp_s32_emios_config *config = dev->config; + + if (Emios_Mcl_Ip_Init(config->instance, config->mcl_info)) { + LOG_ERR("Could not initialize eMIOS"); + return -EINVAL; + } + + return 0; +} + +#define MAX_MASTER_BUS_PERIOD 65535U +#define MIN_MASTER_BUS_PERIOD 2U +#define MAX_GLOB_PRESCALER 256U +#define MIN_GLOB_PRESCALER 1U + +#define NXP_S32_EMIOS_MASTER_BUS_MODE(mode) DT_CAT(EMIOS_IP_, mode) + +#define NXP_S32_EMIOS_INSTANCE_CHECK(idx, n) \ + ((DT_INST_REG_ADDR(n) == IP_EMIOS_##idx##_BASE) ? idx : 0) + +#define NXP_S32_EMIOS_GET_INSTANCE(n) \ + LISTIFY(__DEBRACKET eMIOS_INSTANCE_COUNT, NXP_S32_EMIOS_INSTANCE_CHECK, (|), n) + +#define NXP_S32_EMIOS_GENERATE_GLOBAL_CONFIG(n) \ + BUILD_ASSERT(IN_RANGE(DT_INST_PROP(n, clock_divider), \ + MIN_GLOB_PRESCALER, MAX_GLOB_PRESCALER), \ + "Divider for eMIOS global prescaler is out of range"); \ + const Emios_Ip_GlobalConfigType nxp_s32_emios_##n##_global_config = { \ + .allowDebugMode = true, \ + .clkDivVal = DT_INST_PROP(n, clock_divider) - 1U, \ + .enableGlobalTimeBase = true \ + }; + +#define NXP_S32_EMIOS_MASTER_BUS_VERIFY(node_id) \ + BUILD_ASSERT(IN_RANGE(DT_PROP(node_id, period), \ + MIN_MASTER_BUS_PERIOD, MAX_MASTER_BUS_PERIOD), \ + "Node "DT_NODE_PATH(node_id)": period is out of range"); + +#define NXP_S32_EMIOS_MASTER_BUS_CONFIG(node_id) \ + { \ + .hwChannel = DT_PROP(node_id, channel), \ + .defaultPeriod = DT_PROP(node_id, period), \ + .masterBusPrescaler = DT_PROP(node_id, prescaler) - 1, \ + .allowDebugMode = DT_PROP(node_id, freeze), \ + .masterMode = NXP_S32_EMIOS_MASTER_BUS_MODE(DT_STRING_TOKEN(node_id, mode)), \ + .masterBusAltPrescaler = 0, \ + }, + +#define NXP_S32_EMIOS_GENERATE_MASTER_BUS_CONFIG(n) \ + DT_FOREACH_CHILD_STATUS_OKAY(DT_INST_CHILD(n, master_bus), \ + NXP_S32_EMIOS_MASTER_BUS_VERIFY) \ + const Emios_Ip_MasterBusConfigType nxp_s32_emios_##n##_master_bus_config[] = { \ + DT_FOREACH_CHILD_STATUS_OKAY(DT_INST_CHILD(n, master_bus), \ + NXP_S32_EMIOS_MASTER_BUS_CONFIG) \ + }; + +#define NXP_S32_EMIOS_GENERATE_CONFIG(n) \ + NXP_S32_EMIOS_GENERATE_GLOBAL_CONFIG(n) \ + NXP_S32_EMIOS_GENERATE_MASTER_BUS_CONFIG(n) \ + const Emios_Mcl_Ip_ConfigType nxp_s32_emios_##n##_mcl_config = { \ + .channelsNumber = ARRAY_SIZE(nxp_s32_emios_##n##_master_bus_config), \ + .emiosGlobalConfig = &nxp_s32_emios_##n##_global_config, \ + .masterBusConfig = &nxp_s32_emios_##n##_master_bus_config \ + }; + +#define NXP_S32_EMIOS_INIT_DEVICE(n) \ + NXP_S32_EMIOS_GENERATE_CONFIG(n) \ + const struct nxp_s32_emios_config nxp_s32_emios_##n##_config = { \ + .instance = NXP_S32_EMIOS_GET_INSTANCE(n), \ + .mcl_info = (Emios_Mcl_Ip_ConfigType *)&nxp_s32_emios_##n##_mcl_config, \ + }; \ + DEVICE_DT_INST_DEFINE(n, \ + &nxp_s32_emios_init, \ + NULL, \ + NULL, \ + &nxp_s32_emios_##n##_config, \ + POST_KERNEL, \ + CONFIG_NXP_S32_EMIOS_INIT_PRIORITY, \ + NULL); + +DT_INST_FOREACH_STATUS_OKAY(NXP_S32_EMIOS_INIT_DEVICE) diff --git a/dts/arm/nxp/nxp_s32k344_m7.dtsi b/dts/arm/nxp/nxp_s32k344_m7.dtsi index 4f8708defff..31a4c1453d5 100644 --- a/dts/arm/nxp/nxp_s32k344_m7.dtsi +++ b/dts/arm/nxp/nxp_s32k344_m7.dtsi @@ -597,6 +597,144 @@ interrupt-names = "common", "tx", "rx", "safety"; status = "disabled"; }; + + emios0: emios@40088000 { + compatible = "nxp,s32-emios"; + reg = <0x40088000 0x4000>; + clocks = <&clock NXP_S32_EMIOS0_CLK>; + interrupts = <61 0>, <62 0>, <63 0>, + <64 0>, <65 0>, <66 0>; + status = "disabled"; + + master_bus { + emios0_bus_a: emios0_bus_a { + channel = <23>; + bus-type = "BUS_A"; + channel-mask = <0x07FFFFF>; + status = "disabled"; + }; + + emios0_bus_b: emios0_bus_b { + channel = <0>; + bus-type = "BUS_B"; + channel-mask = <0x00000FE>; + status = "disabled"; + }; + + emios0_bus_c: emios0_bus_c { + channel = <8>; + bus-type = "BUS_C"; + channel-mask = <0x0000FE00>; + status = "disabled"; + }; + + emios0_bus_d: emios0_bus_d { + channel = <16>; + bus-type = "BUS_D"; + channel-mask = <0x00FE0000>; + status = "disabled"; + }; + + emios0_bus_f: emios0_bus_f { + channel = <22>; + bus-type = "BUS_F"; + channel-mask = <0x0BFFFFF>; + status = "disabled"; + }; + }; + }; + + emios1: emios@4008c000 { + compatible = "nxp,s32-emios"; + reg = <0x4008c000 0x4000>; + clocks = <&clock NXP_S32_EMIOS1_CLK>; + interrupts = <69 0>, <70 0>, <71 0>, + <72 0>, <73 0>, <74 0>; + status = "disabled"; + + master_bus { + emios1_bus_a: emios1_bus_a { + channel = <23>; + bus-type = "BUS_A"; + channel-mask = <0x07FFFFF>; + status = "disabled"; + }; + + emios1_bus_b: emios1_bus_b { + channel = <0>; + bus-type = "BUS_B"; + channel-mask = <0x00000FE>; + status = "disabled"; + }; + + emios1_bus_c: emios1_bus_c { + channel = <8>; + bus-type = "BUS_C"; + channel-mask = <0x0000FE00>; + status = "disabled"; + }; + + emios1_bus_d: emios1_bus_d { + channel = <16>; + bus-type = "BUS_D"; + channel-mask = <0x00FE0000>; + status = "disabled"; + }; + + emios1_bus_f: emios1_bus_f { + channel = <22>; + channel-mask = <0x0BFFFFF>; + bus-type = "BUS_F"; + status = "disabled"; + }; + }; + }; + + emios2: emios@40090000 { + compatible = "nxp,s32-emios"; + reg = <0x40090000 0x4000>; + clocks = <&clock NXP_S32_EMIOS2_CLK>; + interrupts = <77 0>, <78 0>, <79 0>, + <80 0>, <81 0>, <82 0>; + status = "disabled"; + + master_bus { + emios2_bus_a: emios2_bus_a { + channel = <23>; + bus-type = "BUS_A"; + channel-mask = <0x07FFFFF>; + status = "disabled"; + }; + + emios2_bus_b: emios2_bus_b { + channel = <0>; + bus-type = "BUS_B"; + channel-mask = <0x00000FE>; + status = "disabled"; + }; + + emios2_bus_c: emios2_bus_c { + channel = <8>; + bus-type = "BUS_C"; + channel-mask = <0x0000FE00>; + status = "disabled"; + }; + + emios2_bus_d: emios2_bus_d { + channel = <16>; + bus-type = "BUS_D"; + channel-mask = <0x00FE0000>; + status = "disabled"; + }; + + emios2_bus_f: emios2_bus_f { + channel = <22>; + bus-type = "BUS_F"; + channel-mask = <0x0BFFFFF>; + status = "disabled"; + }; + }; + }; }; }; diff --git a/dts/bindings/misc/nxp,s32-emios.yaml b/dts/bindings/misc/nxp,s32-emios.yaml new file mode 100644 index 00000000000..b08c7f6596d --- /dev/null +++ b/dts/bindings/misc/nxp,s32-emios.yaml @@ -0,0 +1,101 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: | + NXP S32 Enhanced Modular IO SubSystem (eMIOS) node for S32 SoCs. + eMIOS provides independent unified channels (UCs), some of channels + have internal counter that either can be used independently or used + as a reference timebase (master bus) for other channels. + +compatible: "nxp,s32-emios" + +include: [base.yaml] + +properties: + reg: + required: true + + clock-divider: + type: int + required: true + description: | + Clock divider value for the global prescaler. Could be in range [1 ... 256] + +child-binding: + child-binding: + description: | + Node for eMIOS master bus. Each channel is capable to become a master bus has + a node defined in root devicetree but is disabled by default. To allow using + the master bus, the devicetree node should be enabled and dts properties + should be configured as required by application. + + For example, to enable bus A of eMIOS instance 0 that can be used as timebase + for channels from 0 to 22, freezed in debug mode: + master_bus { + emios0_bus_a: emios0_bus_a { + channel = <23>; + bus-type = "BUS_A"; + channel-mask = <0x07FFFFF>; + prescaler = <1>; + period = <65535>; + mode = ; + freeze; + status = "okay"; + }; + }; + + properties: + channel: + type: int + required: true + description: | + Channel identifier for the master bus. + + channel-mask: + type: int + required: true + description: | + A channel mask for channels that by hardware design can use this master bus + as timebase for the operation, lsb is channel 0. The mask bit for this master bus + must always 0 because a master bus should not do other thing than a base timer. + + prescaler: + type: int + required: true + description: | + Clock divider value for internal UC prescaler. + Clock for internal counter = (eMIOS clock / global prescaler) / internal prescaler. + enum: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + + bus-type: + type: string + required: true + description: | + Master bus type. + enum: + - "BUS_A" + - "BUS_B" + - "BUS_C" + - "BUS_D" + - "BUS_E" + - "BUS_F" + + mode: + type: string + required: true + description: | + Master bus mode. + enum: + - "MCB_UP_COUNTER" + - "MCB_UP_DOWN_COUNTER" + + period: + type: int + required: true + description: | + Default period (in ticks) for master bus at boot time. This determines PWM period + for channels use this bus as reference timebase. Could be in range [2 ... 65535] + + freeze: + type: boolean + description: Freeze internal counter when the chip enters Debug mode.