diff --git a/boards/arm/mr_canhubk3/mr_canhubk3-pinctrl.dtsi b/boards/arm/mr_canhubk3/mr_canhubk3-pinctrl.dtsi index 0b2906f2982..e057aeb1ef6 100644 --- a/boards/arm/mr_canhubk3/mr_canhubk3-pinctrl.dtsi +++ b/boards/arm/mr_canhubk3/mr_canhubk3-pinctrl.dtsi @@ -287,7 +287,6 @@ pinmux = , , , , , , - , , ; output-enable; }; @@ -300,4 +299,14 @@ output-enable; }; }; + + qdec_s32: qdec_s32 { + group1 { + pinmux = , + , + , + ; + input-enable; + }; + }; }; diff --git a/boards/arm/mr_canhubk3/mr_canhubk3.dts b/boards/arm/mr_canhubk3/mr_canhubk3.dts index 50b72338da2..97bca1402b4 100644 --- a/boards/arm/mr_canhubk3/mr_canhubk3.dts +++ b/boards/arm/mr_canhubk3/mr_canhubk3.dts @@ -11,6 +11,7 @@ #include #include #include "mr_canhubk3-pinctrl.dtsi" +#include / { model = "NXP MR-CANHUBK3"; @@ -42,6 +43,7 @@ green-pwm-led = &user_led1_green_pwm; blue-pwm-led = &user_led1_blue_pwm; pwm-led0 = &user_led1_blue_pwm; + qdec0 = &qdec0; }; leds { @@ -77,6 +79,39 @@ }; }; + qdec0: qdec0 { + compatible = "nxp,qdec-s32"; + pinctrl-0 = <&qdec_s32>; + pinctrl-names = "default"; + micro-ticks-per-rev = <685440000>; + status = "okay"; + trgmux = <&trgmux>; + trgmux-io-config = + <0 TRGMUX_IP_OUTPUT_EMIOS0_CH5_9_IPP_IND_CH6 TRGMUX_IP_INPUT_LCU1_LC0_OUT_I2>, + <1 TRGMUX_IP_OUTPUT_EMIOS0_CH5_9_IPP_IND_CH7 TRGMUX_IP_INPUT_LCU1_LC0_OUT_I3>, + <2 TRGMUX_IP_OUTPUT_LCU1_0_INP_I0 TRGMUX_IP_INPUT_SIUL2_IN2>, + <3 TRGMUX_IP_OUTPUT_LCU1_0_INP_I1 TRGMUX_IP_INPUT_SIUL2_IN3>; + lcu = <&lcu1>; + lcu-input-idx = + ; + lcu-mux-sel = + ; + lcu-output-filter-config = + /* LCU Out HW ID, Rise Filter, Fall Filter */ + <0 5 5>, /* LCU O0 */ + <1 5 5>, /* LCU O1 */ + <2 2 2>, /* LCU O2 */ + <3 2 2>; /* LCU O3 */ + emios = <&emios0>; + /* + * eMios channel numbers for qdec should be beyond the channel numbers + * used by the emios pwm + */ + emios-channels = <6 7>; + }; + gpio_keys { compatible = "gpio-keys"; user_button_1: button_0 { @@ -501,24 +536,6 @@ polarity = "ACTIVE_HIGH"; }; - pwm_6 { - channel = <6>; - pwm-mode = "OPWFMB"; - period = <65535>; - duty-cycle = <0>; - prescaler = <8>; - polarity = "ACTIVE_HIGH"; - }; - - pwm_7 { - channel = <7>; - pwm-mode = "OPWFMB"; - period = <65535>; - duty-cycle = <0>; - prescaler = <8>; - polarity = "ACTIVE_HIGH"; - }; - rgb_red { channel = <19>; master-bus = <&emios0_bus_a>; @@ -576,6 +593,14 @@ }; }; +&lcu1 { + status = "okay"; +}; + +&trgmux { + status = "okay"; +}; + &edma0 { status = "okay"; }; diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index 76bb17bc822..afa200a06ab 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -112,6 +112,7 @@ add_subdirectory_ifdef(CONFIG_OPT3001 opt3001) add_subdirectory_ifdef(CONFIG_PCNT_ESP32 pcnt_esp32) add_subdirectory_ifdef(CONFIG_PMS7003 pms7003) add_subdirectory_ifdef(CONFIG_QDEC_MCUX qdec_mcux) +add_subdirectory_ifdef(CONFIG_QDEC_NXP_S32 qdec_nxp_s32) add_subdirectory_ifdef(CONFIG_QDEC_NRFX qdec_nrfx) add_subdirectory_ifdef(CONFIG_QDEC_SAM qdec_sam) add_subdirectory_ifdef(CONFIG_QDEC_STM32 qdec_stm32) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index fba7501bad9..7e454740973 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -189,6 +189,7 @@ source "drivers/sensor/opt3001/Kconfig" source "drivers/sensor/pcnt_esp32/Kconfig" source "drivers/sensor/pms7003/Kconfig" source "drivers/sensor/qdec_mcux/Kconfig" +source "drivers/sensor/qdec_nxp_s32/Kconfig" source "drivers/sensor/qdec_nrfx/Kconfig" source "drivers/sensor/qdec_sam/Kconfig" source "drivers/sensor/qdec_stm32/Kconfig" diff --git a/drivers/sensor/qdec_nxp_s32/CMakeLists.txt b/drivers/sensor/qdec_nxp_s32/CMakeLists.txt new file mode 100644 index 00000000000..a3065a37aba --- /dev/null +++ b/drivers/sensor/qdec_nxp_s32/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(qdec_nxp_s32.c) diff --git a/drivers/sensor/qdec_nxp_s32/Kconfig b/drivers/sensor/qdec_nxp_s32/Kconfig new file mode 100644 index 00000000000..4250d9b2654 --- /dev/null +++ b/drivers/sensor/qdec_nxp_s32/Kconfig @@ -0,0 +1,10 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +config QDEC_NXP_S32 + bool "NXP Quad Decoder S32 drivers" + default y + depends on DT_HAS_NXP_QDEC_S32_ENABLED + select NXP_S32_EMIOS + help + Enable drivers for NXP S32 QUADRATURE DECODER diff --git a/drivers/sensor/qdec_nxp_s32/qdec_nxp_s32.c b/drivers/sensor/qdec_nxp_s32/qdec_nxp_s32.c new file mode 100644 index 00000000000..31d6297d5d3 --- /dev/null +++ b/drivers/sensor/qdec_nxp_s32/qdec_nxp_s32.c @@ -0,0 +1,451 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#define EMIOS_CHANNEL_COUNT 2U +#define EMIOS_CW_CH_IDX 0 +#define EMIOS_CCW_CH_IDX 1 + +/* LCU LUT control values for each of the 4 LC outputs */ +/* These values decide the direction of motor rotation */ +#define LCU_O0_LUT 0xAAAA +#define LCU_O1_LUT 0xCCCC +#define LCU_O2_LUT 0x4182 +#define LCU_O3_LUT 0x2814 + +LOG_MODULE_REGISTER(nxp_qdec_s32, CONFIG_SENSOR_LOG_LEVEL); + +#include +#include +#include + +#define DT_DRV_COMPAT nxp_qdec_s32 + +typedef void (*emios_callback_t)(void); + +/* Configuration variables from eMIOS Icu driver */ +extern eMios_Icu_Ip_ChStateType eMios_Icu_Ip_ChState[EMIOS_ICU_IP_NUM_OF_CHANNELS_USED]; +extern uint8 eMios_Icu_Ip_IndexInChState[EMIOS_ICU_IP_INSTANCE_COUNT][EMIOS_ICU_IP_NUM_OF_CHANNELS]; + +struct qdec_s32_config { + uint8_t emios_inst; + uint8_t emios_channels[EMIOS_CHANNEL_COUNT]; + const struct pinctrl_dev_config *pincfg; + + const Trgmux_Ip_InitType *trgmux_config; + + const Lcu_Ip_InitType *lcu_config; + emios_callback_t emios_cw_overflow_cb; + emios_callback_t emios_ccw_overflow_cb; +}; + +struct qdec_s32_data { + uint32_t counter_CW; + uint32_t counter_CCW; + int32_t abs_counter; + float micro_ticks_per_rev; + uint32_t ticks_per_sec; + uint32_t emios_cw_overflow_count; + uint32_t emios_ccw_overflow_count; +}; + +static void qdec_emios_overflow_count_cw_callback(const struct device *dev) +{ + struct qdec_s32_data *data = dev->data; + + data->emios_cw_overflow_count++; +} + +static void qdec_emios_overflow_count_ccw_callback(const struct device *dev) +{ + struct qdec_s32_data *data = dev->data; + + data->emios_ccw_overflow_count++; +} + +static int qdec_s32_fetch(const struct device *dev, enum sensor_channel ch) +{ + const struct qdec_s32_config *config = dev->config; + struct qdec_s32_data *data = dev->data; + + if (ch != SENSOR_CHAN_ALL) { + return -ENOTSUP; + } + + data->counter_CW = (uint32_t)(Emios_Icu_Ip_GetEdgeNumbers( + config->emios_inst, config->emios_channels[EMIOS_CW_CH_IDX])); /* CW counter */ + data->counter_CCW = (uint32_t)(Emios_Icu_Ip_GetEdgeNumbers( + config->emios_inst, config->emios_channels[EMIOS_CCW_CH_IDX]));/* CCW counter*/ + + data->abs_counter = (int32_t)( + +(data->counter_CW + EMIOS_ICU_IP_COUNTER_MASK * + data->emios_cw_overflow_count) + -(data->counter_CCW + EMIOS_ICU_IP_COUNTER_MASK * + data->emios_ccw_overflow_count)); + + LOG_DBG("ABS_COUNT = %d CW = %u OverFlow_CW = %u CCW = %u Overflow_CCW = %u", + data->abs_counter, data->counter_CW, + data->emios_cw_overflow_count, + data->counter_CCW, data->emios_ccw_overflow_count); + + return 0; +} + +static int qdec_s32_ch_get(const struct device *dev, enum sensor_channel ch, + struct sensor_value *val) +{ + struct qdec_s32_data *data = dev->data; + + double rotation = (data->abs_counter * 2.0 * M_PI) / data->micro_ticks_per_rev; + + switch (ch) { + case SENSOR_CHAN_ROTATION: + sensor_value_from_double(val, rotation); + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static const struct sensor_driver_api qdec_s32_api = { + .sample_fetch = &qdec_s32_fetch, + .channel_get = &qdec_s32_ch_get, +}; + +static int qdec_s32_initialize(const struct device *dev) +{ + const struct qdec_s32_config *config = dev->config; + uint8_t emios_inst, emios_hw_ch_cw, emios_hw_ch_ccw; + + pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); + + if (Trgmux_Ip_Init(config->trgmux_config)) { + LOG_ERR("Could not initialize Trgmux"); + return -EINVAL; + } + + LOG_DBG("TRGMUX ACCESS Input[0] =%d Output[0]=%d", + config->trgmux_config->paxLogicTrigger[0]->Input, + config->trgmux_config->paxLogicTrigger[0]->Output); + + if (Lcu_Ip_Init(config->lcu_config)) { + LOG_ERR("Could not initialize Lcu"); + return -EINVAL; + } + + /* Unmask relevant LCU OUT Channels */ + Lcu_Ip_SyncOutputValueType EncLcuEnable[4U]; + + EncLcuEnable[0].LogicOutputId = LCU_LOGIC_OUTPUT_0; + EncLcuEnable[0].Value = 1U; + EncLcuEnable[1].LogicOutputId = LCU_LOGIC_OUTPUT_1; + EncLcuEnable[1].Value = 1U; + EncLcuEnable[2].LogicOutputId = LCU_LOGIC_OUTPUT_2; + EncLcuEnable[2].Value = 1U; + EncLcuEnable[3].LogicOutputId = LCU_LOGIC_OUTPUT_3; + EncLcuEnable[3].Value = 1U; + Lcu_Ip_SetSyncOutputEnable(EncLcuEnable, 4U); + + emios_inst = config->emios_inst; + emios_hw_ch_cw = config->emios_channels[EMIOS_CW_CH_IDX]; + emios_hw_ch_ccw = config->emios_channels[EMIOS_CCW_CH_IDX]; + + /* Initialize the positions of the eMios hw channels used for QDEC + * to be beyond the eMios pwm hw channels. Currently only pwm and qdec + * are using the eMios channels so qdec ones are the last two. + */ + eMios_Icu_Ip_IndexInChState[emios_inst][emios_hw_ch_cw] + = EMIOS_ICU_IP_NUM_OF_CHANNELS_USED - 2; + eMios_Icu_Ip_IndexInChState[emios_inst][emios_hw_ch_ccw] + = EMIOS_ICU_IP_NUM_OF_CHANNELS_USED - 1; + + /* Set Overflow Notification for eMIOS channels meant + * for Clockwise and Counterclock rotation counters + */ + eMios_Icu_Ip_ChState[eMios_Icu_Ip_IndexInChState[emios_inst][emios_hw_ch_cw]] + .eMiosOverflowNotification = config->emios_cw_overflow_cb; + eMios_Icu_Ip_ChState[eMios_Icu_Ip_IndexInChState[emios_inst][emios_hw_ch_ccw]] + .eMiosOverflowNotification = config->emios_ccw_overflow_cb; + + Emios_Icu_Ip_SetInitialCounterValue( + config->emios_inst, config->emios_channels[EMIOS_CW_CH_IDX], (uint32_t)0x1U); + Emios_Icu_Ip_SetInitialCounterValue( + config->emios_inst, config->emios_channels[EMIOS_CCW_CH_IDX], (uint32_t)0x1U); + + Emios_Icu_Ip_SetMaxCounterValue(config->emios_inst, + config->emios_channels[EMIOS_CW_CH_IDX], + EMIOS_ICU_IP_COUNTER_MASK); + Emios_Icu_Ip_SetMaxCounterValue(config->emios_inst, + config->emios_channels[EMIOS_CCW_CH_IDX], + EMIOS_ICU_IP_COUNTER_MASK); + + /* This API sets MCB/EMIOS_ICU_MODE_EDGE_COUNTER mode */ + Emios_Icu_Ip_EnableEdgeCount(config->emios_inst, config->emios_channels[EMIOS_CW_CH_IDX]); + Emios_Icu_Ip_EnableEdgeCount(config->emios_inst, config->emios_channels[EMIOS_CCW_CH_IDX]); + + LOG_DBG("Init complete"); + + return 0; +} + +#define EMIOS_NXP_S32_MCB_OVERFLOW_CALLBACK(n) \ + static void qdec##n##_emios_overflow_count_cw_callback(void) \ + { \ + qdec_emios_overflow_count_cw_callback(DEVICE_DT_INST_GET(n)); \ + } \ + \ + static void qdec##n##_emios_overflow_count_ccw_callback(void) \ + { \ + qdec_emios_overflow_count_ccw_callback(DEVICE_DT_INST_GET(n)); \ + } + +#define EMIOS_NXP_S32_INSTANCE_CHECK(idx, node_id) \ + ((DT_REG_ADDR(node_id) == IP_EMIOS_##idx##_BASE) ? idx : 0) + +#define EMIOS_NXP_S32_GET_INSTANCE(node_id) \ + LISTIFY(__DEBRACKET eMIOS_INSTANCE_COUNT, EMIOS_NXP_S32_INSTANCE_CHECK, (|), node_id) + +#define LCU_NXP_S32_INSTANCE_CHECK(idx, node_id) \ + ((DT_REG_ADDR(node_id) == IP_LCU_##idx##_BASE) ? idx : 0) + +#define LCU_NXP_S32_GET_INSTANCE(node_id) \ + LISTIFY(__DEBRACKET LCU_INSTANCE_COUNT, LCU_NXP_S32_INSTANCE_CHECK, (|), node_id) + +#define TRGMUX_NXP_S32_INSTANCE_CHECK(node_id) \ + ((DT_REG_ADDR(node_id) == IP_TRGMUX_BASE) ? 0 : -1) + +#define TRGMUX_NXP_S32_GET_INSTANCE(node_id) TRGMUX_NXP_S32_INSTANCE_CHECK(node_id) + +/* LCU Logic Input Configuration */ +#define LogicInputCfg_Common(n, mux_sel_idx) \ + { \ + .MuxSel = DT_INST_PROP_BY_IDX(n, lcu_mux_sel, mux_sel_idx), \ + .SwSynMode = LCU_IP_SW_SYNC_IMMEDIATE, \ + .SwValue = LCU_IP_SW_OVERRIDE_LOGIC_LOW, \ + }; +#define LogicInput_Config_Common(n, hw_lc_input_id, logic_input_n_cfg) \ + { \ + .xLogicInputId = { \ + .HwInstId = LCU_NXP_S32_GET_INSTANCE(DT_INST_PHANDLE(n, lcu)), \ + .HwLcInputId = DT_INST_PROP_BY_IDX(n, lcu_input_idx, hw_lc_input_id), \ + }, \ + .pxLcInputConfig = &logic_input_n_cfg, \ + }; + +/* LCU Logic Output Configuration */ +#define LogicOutputCfg_Common(En_Debug_Mode, Lut_Control, Lut_Rise_Filt, Lut_Fall_Filt) \ + { \ + .EnDebugMode = (boolean)En_Debug_Mode, \ + .LutControl = Lut_Control, \ + .LutRiseFilt = Lut_Rise_Filt, \ + .LutFallFilt = Lut_Fall_Filt, \ + .EnLutDma = (boolean)FALSE, \ + .EnForceDma = (boolean)FALSE, \ + .EnLutInt = (boolean)FALSE, \ + .EnForceInt = (boolean)FALSE, \ + .InvertOutput = (boolean)FALSE, \ + .ForceSignalSel = 0U, \ + .ClearForceMode = LCU_IP_CLEAR_FORCE_SIGNAL_IMMEDIATE, \ + .ForceSyncSel = LCU_IP_SYNC_SEL_INPUT0, \ + }; +#define LogicOutput_Config_Common(n, logic_output_cfg, hw_lc_output_id) \ + { \ + .xLogicOutputId = { \ + .HwInstId = LCU_NXP_S32_GET_INSTANCE(DT_INST_PHANDLE(n, lcu)), \ + .HwLcOutputId = hw_lc_output_id, \ + .IntCallback = NULL_PTR, \ + }, \ + .pxLcOutputConfig = &logic_output_cfg, \ + }; + +#define LCU_IP_INIT_CONFIG(n) \ + const Lcu_Ip_LogicInputConfigType LogicInput##n##_0_Cfg = \ + LogicInputCfg_Common(n, 0) \ + const Lcu_Ip_LogicInputConfigType LogicInput##n##_1_Cfg = \ + LogicInputCfg_Common(n, 1) \ + const Lcu_Ip_LogicInputConfigType LogicInput##n##_2_Cfg = \ + LogicInputCfg_Common(n, 2) \ + const Lcu_Ip_LogicInputConfigType LogicInput##n##_3_Cfg = \ + LogicInputCfg_Common(n, 3) \ + \ + const Lcu_Ip_LogicInputType LogicInput##n##_0_Config = \ + LogicInput_Config_Common(n, 0, LogicInput##n##_0_Cfg) \ + const Lcu_Ip_LogicInputType LogicInput##n##_1_Config = \ + LogicInput_Config_Common(n, 1, LogicInput##n##_1_Cfg) \ + const Lcu_Ip_LogicInputType LogicInput##n##_2_Config = \ + LogicInput_Config_Common(n, 2, LogicInput##n##_2_Cfg) \ + const Lcu_Ip_LogicInputType LogicInput##n##_3_Config = \ + LogicInput_Config_Common(n, 3, LogicInput##n##_3_Cfg) \ + \ + const Lcu_Ip_LogicInputType \ + *const Lcu_Ip_ppxLogicInputArray##n##_Config[LCU_IP_NOF_CFG_LOGIC_INPUTS] = { \ + &LogicInput##n##_0_Config, \ + &LogicInput##n##_1_Config, \ + &LogicInput##n##_2_Config, \ + &LogicInput##n##_3_Config, \ + }; \ + \ + const Lcu_Ip_LogicOutputConfigType LogicOutput##n##_0_Cfg = LogicOutputCfg_Common( \ + LCU_IP_DEBUG_DISABLE, LCU_O0_LUT, \ + DT_INST_PROP_BY_IDX(n, lcu_output_filter_config, 1), \ + DT_INST_PROP_BY_IDX(n, lcu_output_filter_config, 2)) \ + const Lcu_Ip_LogicOutputConfigType LogicOutput##n##_1_Cfg = LogicOutputCfg_Common( \ + LCU_IP_DEBUG_DISABLE, LCU_O1_LUT, \ + DT_INST_PROP_BY_IDX(n, lcu_output_filter_config, 4), \ + DT_INST_PROP_BY_IDX(n, lcu_output_filter_config, 5)) \ + const Lcu_Ip_LogicOutputConfigType LogicOutput##n##_2_Cfg = LogicOutputCfg_Common( \ + LCU_IP_DEBUG_ENABLE, LCU_O2_LUT, \ + DT_INST_PROP_BY_IDX(n, lcu_output_filter_config, 7), \ + DT_INST_PROP_BY_IDX(n, lcu_output_filter_config, 8)) \ + const Lcu_Ip_LogicOutputConfigType LogicOutput##n##_3_Cfg = LogicOutputCfg_Common( \ + LCU_IP_DEBUG_ENABLE, LCU_O3_LUT, \ + DT_INST_PROP_BY_IDX(n, lcu_output_filter_config, 10), \ + DT_INST_PROP_BY_IDX(n, lcu_output_filter_config, 11)) \ + \ + const Lcu_Ip_LogicOutputType LogicOutput##n##_0_Config = \ + LogicOutput_Config_Common(n, LogicOutput##n##_0_Cfg, \ + DT_INST_PROP_BY_IDX(n, lcu_output_filter_config, 0)) \ + const Lcu_Ip_LogicOutputType LogicOutput##n##_1_Config = \ + LogicOutput_Config_Common(n, LogicOutput##n##_1_Cfg, \ + DT_INST_PROP_BY_IDX(n, lcu_output_filter_config, 3)) \ + const Lcu_Ip_LogicOutputType LogicOutput##n##_2_Config = \ + LogicOutput_Config_Common(n, LogicOutput##n##_2_Cfg, \ + DT_INST_PROP_BY_IDX(n, lcu_output_filter_config, 6)) \ + const Lcu_Ip_LogicOutputType LogicOutput##n##_3_Config = \ + LogicOutput_Config_Common(n, LogicOutput##n##_3_Cfg, \ + DT_INST_PROP_BY_IDX(n, lcu_output_filter_config, 9)) \ + \ + const Lcu_Ip_LogicOutputType \ + *const Lcu_Ip_ppxLogicOutputArray##n##_Config[LCU_IP_NOF_CFG_LOGIC_OUTPUTS] = { \ + &LogicOutput##n##_0_Config, \ + &LogicOutput##n##_1_Config, \ + &LogicOutput##n##_2_Config, \ + &LogicOutput##n##_3_Config, \ + }; \ + \ + const Lcu_Ip_LogicInputConfigType Lcu_Ip_LogicInputResetConfig##n = { \ + .MuxSel = LCU_IP_MUX_SEL_LOGIC_0, \ + .SwSynMode = LCU_IP_SW_SYNC_IMMEDIATE, \ + .SwValue = LCU_IP_SW_OVERRIDE_LOGIC_LOW, \ + }; \ + \ + const Lcu_Ip_LogicOutputConfigType Lcu_Ip_LogicOutputResetConfig##n = \ + LogicOutputCfg_Common(LCU_IP_DEBUG_DISABLE, 0U, 0U, 0U) \ + \ + const Lcu_Ip_LogicInstanceType LcuLogicInstance##n##_0_Config = { \ + .HwInstId = LCU_NXP_S32_GET_INSTANCE(DT_INST_PHANDLE(n, lcu)), \ + .NumLogicCellConfig = 0U, \ + .ppxLogicCellConfigArray = NULL_PTR, \ + .OperationMode = LCU_IP_INTERRUPT_MODE, \ + }; \ + const Lcu_Ip_LogicInstanceType \ + *const Lcu_Ip_ppxLogicInstanceArray##n##_Config[LCU_IP_NOF_CFG_LOGIC_INSTANCES] = { \ + &LcuLogicInstance##n##_0_Config, \ + }; \ + \ + Lcu_Ip_HwOutputStateType HwOutput##n##_0_State_Config; \ + Lcu_Ip_HwOutputStateType HwOutput##n##_1_State_Config; \ + Lcu_Ip_HwOutputStateType HwOutput##n##_2_State_Config; \ + Lcu_Ip_HwOutputStateType HwOutput##n##_3_State_Config; \ + Lcu_Ip_HwOutputStateType \ + *Lcu_Ip_ppxHwOutputStateArray##n##_Config[LCU_IP_NOF_CFG_LOGIC_OUTPUTS] = { \ + &HwOutput##n##_0_State_Config, \ + &HwOutput##n##_1_State_Config, \ + &HwOutput##n##_2_State_Config, \ + &HwOutput##n##_3_State_Config, \ + }; \ + \ + const Lcu_Ip_InitType Lcu_Ip_Init_Config##n = { \ + .ppxHwOutputStateArray = &Lcu_Ip_ppxHwOutputStateArray##n##_Config[0], \ + .ppxLogicInstanceConfigArray = &Lcu_Ip_ppxLogicInstanceArray##n##_Config[0], \ + .pxLogicOutputResetConfigArray = &Lcu_Ip_LogicOutputResetConfig##n, \ + .pxLogicInputResetConfigArray = &Lcu_Ip_LogicInputResetConfig##n, \ + .ppxLogicOutputConfigArray = &Lcu_Ip_ppxLogicOutputArray##n##_Config[0], \ + .ppxLogicInputConfigArray = &Lcu_Ip_ppxLogicInputArray##n##_Config[0], \ + }; + +#define Trgmux_Ip_LogicTrigger_Config(n, logic_channel, output, input) \ + { \ + .LogicChannel = logic_channel, \ + .Output = output, \ + .Input = input, \ + .HwInstId = TRGMUX_NXP_S32_GET_INSTANCE(DT_INST_PHANDLE(n, trgmux)), \ + .Lock = (boolean)FALSE, \ + }; + +#define TRGMUX_IP_INIT_CONFIG(n) \ + const Trgmux_Ip_LogicTriggerType \ + Trgmux_Ip_LogicTrigger##n##_0_Config = Trgmux_Ip_LogicTrigger_Config(n, \ + DT_INST_PROP_BY_IDX(n, trgmux_io_config, 0), \ + DT_INST_PROP_BY_IDX(n, trgmux_io_config, 1), \ + DT_INST_PROP_BY_IDX(n, trgmux_io_config, 2)) \ + const Trgmux_Ip_LogicTriggerType \ + Trgmux_Ip_LogicTrigger##n##_1_Config = Trgmux_Ip_LogicTrigger_Config(n, \ + DT_INST_PROP_BY_IDX(n, trgmux_io_config, 3), \ + DT_INST_PROP_BY_IDX(n, trgmux_io_config, 4), \ + DT_INST_PROP_BY_IDX(n, trgmux_io_config, 5)) \ + const Trgmux_Ip_LogicTriggerType \ + Trgmux_Ip_LogicTrigger##n##_2_Config = Trgmux_Ip_LogicTrigger_Config(n, \ + DT_INST_PROP_BY_IDX(n, trgmux_io_config, 6), \ + DT_INST_PROP_BY_IDX(n, trgmux_io_config, 7), \ + DT_INST_PROP_BY_IDX(n, trgmux_io_config, 8)) \ + const Trgmux_Ip_LogicTriggerType \ + Trgmux_Ip_LogicTrigger##n##_3_Config = Trgmux_Ip_LogicTrigger_Config(n, \ + DT_INST_PROP_BY_IDX(n, trgmux_io_config, 9), \ + DT_INST_PROP_BY_IDX(n, trgmux_io_config, 10), \ + DT_INST_PROP_BY_IDX(n, trgmux_io_config, 11)) \ + const Trgmux_Ip_InitType Trgmux_Ip_Init_##n##_Config = { \ + .paxLogicTrigger = { \ + &Trgmux_Ip_LogicTrigger##n##_0_Config, \ + &Trgmux_Ip_LogicTrigger##n##_1_Config, \ + &Trgmux_Ip_LogicTrigger##n##_2_Config, \ + &Trgmux_Ip_LogicTrigger##n##_3_Config, \ + }, \ + }; + + +#define QDEC_NXP_S32_INIT(n) \ + \ + static struct qdec_s32_data qdec_s32_##n##_data = { \ + .micro_ticks_per_rev = (float)(DT_INST_PROP(n, micro_ticks_per_rev) / 1000000), \ + .counter_CW = 1, \ + .counter_CCW = 1, \ + }; \ + \ + PINCTRL_DT_INST_DEFINE(n); \ + TRGMUX_IP_INIT_CONFIG(n) \ + LCU_IP_INIT_CONFIG(n) \ + EMIOS_NXP_S32_MCB_OVERFLOW_CALLBACK(n) \ + \ + static const struct qdec_s32_config qdec_s32_##n##_config = { \ + .emios_inst = EMIOS_NXP_S32_GET_INSTANCE(DT_INST_PHANDLE(n, emios)), \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .trgmux_config = &Trgmux_Ip_Init_##n##_Config, \ + .lcu_config = &Lcu_Ip_Init_Config##n, \ + .emios_channels = {DT_INST_PROP_BY_IDX(n, emios_channels, EMIOS_CW_CH_IDX), \ + DT_INST_PROP_BY_IDX(n, emios_channels, EMIOS_CCW_CH_IDX)}, \ + .emios_cw_overflow_cb = &qdec##n##_emios_overflow_count_cw_callback, \ + .emios_ccw_overflow_cb = &qdec##n##_emios_overflow_count_ccw_callback, \ + }; \ + \ + SENSOR_DEVICE_DT_INST_DEFINE(n, qdec_s32_initialize, NULL, &qdec_s32_##n##_data, \ + &qdec_s32_##n##_config, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &qdec_s32_api); + +DT_INST_FOREACH_STATUS_OKAY(QDEC_NXP_S32_INIT) diff --git a/dts/arm/nxp/nxp_s32k344_m7.dtsi b/dts/arm/nxp/nxp_s32k344_m7.dtsi index 036b89a540d..0289b24f42e 100644 --- a/dts/arm/nxp/nxp_s32k344_m7.dtsi +++ b/dts/arm/nxp/nxp_s32k344_m7.dtsi @@ -827,6 +827,24 @@ status = "disabled"; }; }; + + lcu0: lcu@40098000 { + compatible = "nxp,s32-lcu"; + reg = <0x40098000 0x4000>; + status = "disabled"; + }; + + lcu1: lcu@4009c000 { + compatible = "nxp,s32-lcu"; + reg = <0x4009c000 0x4000>; + status = "disabled"; + }; + + trgmux: trgmux@40080000 { + compatible = "nxp,s32-trgmux"; + reg = <0x40080000 0x4000>; + status = "disabled"; + }; }; }; diff --git a/dts/bindings/misc/nxp,s32-lcu.yaml b/dts/bindings/misc/nxp,s32-lcu.yaml new file mode 100644 index 00000000000..51707de20e8 --- /dev/null +++ b/dts/bindings/misc/nxp,s32-lcu.yaml @@ -0,0 +1,16 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: | + NXP S32 Logic control Unit node for S32 SoCs. + LCU selects multiple inputs from timers, Pulse Width Modulation + signals, and Input/Output (I/O) pads, and combines them + using a programmable logic function to create output waveforms + +compatible: "nxp,s32-lcu" + +include: [base.yaml] + +properties: + reg: + required: true diff --git a/dts/bindings/misc/nxp,s32-trgmux.yaml b/dts/bindings/misc/nxp,s32-trgmux.yaml new file mode 100644 index 00000000000..55a589d6836 --- /dev/null +++ b/dts/bindings/misc/nxp,s32-trgmux.yaml @@ -0,0 +1,16 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: | + NXP S32 Trigger Multiplexing Control node for S32 SoCs. + The device supports the triggering scheme between peripherals. + The supported trigger sources and destination can be found in + the device Ref Manual + +compatible: "nxp,s32-trgmux" + +include: [base.yaml] + +properties: + reg: + required: true diff --git a/dts/bindings/sensor/nxp,s32-qdec.yaml b/dts/bindings/sensor/nxp,s32-qdec.yaml new file mode 100644 index 00000000000..935d2122f23 --- /dev/null +++ b/dts/bindings/sensor/nxp,s32-qdec.yaml @@ -0,0 +1,112 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: | + Quadrature Decoder driver which processes encoder signals to determine motor revs + with the cooperation of S32 IP blocks- eMIOS, TRGMUX and LCU. + The sensor qdec application can be used for testing this driver. + The following example uses TRGMUX IN2 and IN3 to connect to LCU1 LC0 I0 and I1. + LCU1 LC0 O2 and O3 connect to eMIOS0 CH6(Clockwise rotation) and + CH7(Counter Clockwise rotation) via TRGMUX_INT_OUT37 and TRGMUX_INT_OUT38 + micro-ticks-per-rev is set as per vehicle gearbox reduction. + lcu output filters are set to capture maximum speed sensitivity and avoid channel noise. + + qdec0 { + compatible = "nxp,qdec-s32"; + pinctrl-0 = <&qdec_s32>; + pinctrl-names = "default"; + micro-ticks-per-rev = <685440000>; + status = "okay"; + trgmux = <&trgmux>; + trgmux-io-config = + <0 TRGMUX_IP_OUTPUT_EMIOS0_CH5_9_IPP_IND_CH6 TRGMUX_IP_INPUT_LCU1_LC0_OUT_I2>, + <1 TRGMUX_IP_OUTPUT_EMIOS0_CH5_9_IPP_IND_CH7 TRGMUX_IP_INPUT_LCU1_LC0_OUT_I3>, + <2 TRGMUX_IP_OUTPUT_LCU1_0_INP_I0 TRGMUX_IP_INPUT_SIUL2_IN2>, + <3 TRGMUX_IP_OUTPUT_LCU1_0_INP_I1 TRGMUX_IP_INPUT_SIUL2_IN3>; + lcu = <&lcu1>; + lcu-input-idx = <1>; + ; + lcu-mux-sel = + ; + lcu-output-filter-config = + /* LCU Out HW ID, Rise Filter, Fall Filter */ + <0 5 5>, /* LCU O0 */ + <1 5 5>, /* LCU O1 */ + <2 2 2>, /* LCU O2 */ + <3 2 2>; /* LCU O3 */ + emios = <&emios0>; + emios-channels = <6 7>; + }; + +compatible: "nxp,qdec-s32" + +include: [pinctrl-device.yaml, sensor-device.yaml] + +properties: + micro-ticks-per-rev: + type: int + description: | + This is a number that is used to determine how many revolutions * 1000000 + were done based on the current counter's value. + + trgmux: + type: phandle + description: | + phandle to the TRGMUX node. + + trgmux-io-config: + type: array + description: | + This gives the logic triggers configuration of TRGMUX module. + It contains 3 values for each of the 4 logic triggers used: + logic trigger number, output, input. + Hence, it's length should be '12'. + Ex: + trgmux-io-config = + <0 TRGMUX_IP_OUTPUT_EMIOS0_CH5_9_IPP_IND_CH6 TRGMUX_IP_INPUT_LCU1_LC0_OUT_I2>, + <1 TRGMUX_IP_OUTPUT_EMIOS0_CH5_9_IPP_IND_CH7 TRGMUX_IP_INPUT_LCU1_LC0_OUT_I3>, + <2 TRGMUX_IP_OUTPUT_LCU1_0_INP_I0 TRGMUX_IP_INPUT_SIUL2_IN2>, + <3 TRGMUX_IP_OUTPUT_LCU1_0_INP_I1 TRGMUX_IP_INPUT_SIUL2_IN3>; + + lcu: + type: phandle + description: | + phandle to the LCU node. + + emios: + type: phandle + description: | + phandle to the eMIOS node. + + lcu-output-filter-config: + type: array + description: | + This array gives the configuration for each of the four outputs of LCU module. + It contains the following for each output: hardware output id, rise filter and fall filter. + The filters specify the delay in terms of CORE_CLK between the input and output line of LC. + We use this delay to generate short pulses at the rising and falling edges of input pulse. + It's length should be '12' - 3 entries for each of the four LCU outputs. + Ex: lcu-output-filter-config = + /* LCU Out HW ID, Rise Filter, Fall Filter */ + <0 5 5>, /* LCU O0 */ + <1 5 5>, /* LCU O1 */ + <2 2 2>, /* LCU O2 */ + <3 2 2>; /* LCU O3 */ + + lcu-mux-sel: + type: array + description: | + This array configures the sources of input to the LCU module by programming the muxsel. + + lcu-input-idx: + type: array + description: | + This array configures the input indices to the LCU module which help to determine the + Logic Cell number used inside an LCU instance. + + emios-channels: + type: array + description: | + This is the array containing 2 emios channel TypeG numbers used by the qdec. diff --git a/include/zephyr/dt-bindings/sensor/qdec_nxp_s32.h b/include/zephyr/dt-bindings/sensor/qdec_nxp_s32.h new file mode 100644 index 00000000000..c7df471d929 --- /dev/null +++ b/include/zephyr/dt-bindings/sensor/qdec_nxp_s32.h @@ -0,0 +1,107 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Logic Trigger Numbers. See Trgmux_Ip_Init_PBcfg.h */ +#define TRGMUX_LOGIC_GROUP_0_TRIGGER_0 (0) /* Logic Trigger 0 */ +#define TRGMUX_LOGIC_GROUP_0_TRIGGER_1 (1) /* Logic Trigger 1 */ +#define TRGMUX_LOGIC_GROUP_1_TRIGGER_0 (2) /* Logic Trigger 2 */ +#define TRGMUX_LOGIC_GROUP_1_TRIGGER_1 (3) /* Logic Trigger 3 */ + +/*----------------------------------------------- + * TRGMUX HARDWARE TRIGGER INPUT + * See Trgmux_Ip_Cfg_Defines.h + *----------------------------------------------- + */ +#define TRGMUX_IP_INPUT_SIUL2_IN0 (60) +#define TRGMUX_IP_INPUT_SIUL2_IN1 (61) +#define TRGMUX_IP_INPUT_SIUL2_IN2 (62) +#define TRGMUX_IP_INPUT_SIUL2_IN3 (63) +#define TRGMUX_IP_INPUT_SIUL2_IN4 (64) +#define TRGMUX_IP_INPUT_SIUL2_IN5 (65) +#define TRGMUX_IP_INPUT_SIUL2_IN6 (66) +#define TRGMUX_IP_INPUT_SIUL2_IN7 (67) +#define TRGMUX_IP_INPUT_SIUL2_IN8 (68) +#define TRGMUX_IP_INPUT_SIUL2_IN9 (69) +#define TRGMUX_IP_INPUT_SIUL2_IN10 (70) +#define TRGMUX_IP_INPUT_SIUL2_IN11 (71) +#define TRGMUX_IP_INPUT_SIUL2_IN12 (72) +#define TRGMUX_IP_INPUT_SIUL2_IN13 (73) +#define TRGMUX_IP_INPUT_SIUL2_IN14 (74) +#define TRGMUX_IP_INPUT_SIUL2_IN15 (75) + +#define TRGMUX_IP_INPUT_LCU1_LC0_OUT_I0 (105) +#define TRGMUX_IP_INPUT_LCU1_LC0_OUT_I1 (106) +#define TRGMUX_IP_INPUT_LCU1_LC0_OUT_I2 (107) +#define TRGMUX_IP_INPUT_LCU1_LC0_OUT_I3 (108) + +/*----------------------------------------------- + * TRGMUX HARDWARE TRIGGER OUTPUT + * See Trgmux_Ip_Cfg_Defines.h + *----------------------------------------------- + */ +#define TRGMUX_IP_OUTPUT_LCU1_0_INP_I0 (144) +#define TRGMUX_IP_OUTPUT_LCU1_0_INP_I1 (145) +#define TRGMUX_IP_OUTPUT_LCU1_0_INP_I2 (146) +#define TRGMUX_IP_OUTPUT_LCU1_0_INP_I3 (147) + +#define TRGMUX_IP_OUTPUT_EMIOS0_CH1_4_IPP_IND_CH1 (32) +#define TRGMUX_IP_OUTPUT_EMIOS0_CH1_4_IPP_IND_CH2 (33) +#define TRGMUX_IP_OUTPUT_EMIOS0_CH1_4_IPP_IND_CH3 (34) +#define TRGMUX_IP_OUTPUT_EMIOS0_CH1_4_IPP_IND_CH4 (35) +#define TRGMUX_IP_OUTPUT_EMIOS0_CH5_9_IPP_IND_CH5 (36) +#define TRGMUX_IP_OUTPUT_EMIOS0_CH5_9_IPP_IND_CH6 (37) +#define TRGMUX_IP_OUTPUT_EMIOS0_CH5_9_IPP_IND_CH7 (38) +#define TRGMUX_IP_OUTPUT_EMIOS0_CH5_9_IPP_IND_CH9 (39) +#define TRGMUX_IP_OUTPUT_EMIOS0_CH10_13_IPP_IND_CH10 (40) +#define TRGMUX_IP_OUTPUT_EMIOS0_CH10_13_IPP_IND_CH11 (41) +#define TRGMUX_IP_OUTPUT_EMIOS0_CH10_13_IPP_IND_CH12 (42) +#define TRGMUX_IP_OUTPUT_EMIOS0_CH10_13_IPP_IND_CH13 (43) +#define TRGMUX_IP_OUTPUT_EMIOS0_CH14_15_IPP_IND_CH14 (44) +#define TRGMUX_IP_OUTPUT_EMIOS0_CH14_15_IPP_IND_CH15 (45) + +/*----------------------------------------------- + * LCU SOURCE MUX SELECT + * See Lcu_Ip_Cfg_Defines.h + *----------------------------------------------- + */ +#define LCU_IP_MUX_SEL_LOGIC_0 (0) +#define LCU_IP_MUX_SEL_LU_IN_0 (1) +#define LCU_IP_MUX_SEL_LU_IN_1 (2) +#define LCU_IP_MUX_SEL_LU_IN_2 (3) +#define LCU_IP_MUX_SEL_LU_IN_3 (4) +#define LCU_IP_MUX_SEL_LU_IN_4 (5) +#define LCU_IP_MUX_SEL_LU_IN_5 (6) +#define LCU_IP_MUX_SEL_LU_IN_6 (7) +#define LCU_IP_MUX_SEL_LU_IN_7 (8) +#define LCU_IP_MUX_SEL_LU_IN_8 (9) +#define LCU_IP_MUX_SEL_LU_IN_9 (10) +#define LCU_IP_MUX_SEL_LU_IN_10 (11) +#define LCU_IP_MUX_SEL_LU_IN_11 (12) +#define LCU_IP_MUX_SEL_LU_OUT_0 (13) +#define LCU_IP_MUX_SEL_LU_OUT_1 (14) +#define LCU_IP_MUX_SEL_LU_OUT_2 (15) +#define LCU_IP_MUX_SEL_LU_OUT_3 (16) +#define LCU_IP_MUX_SEL_LU_OUT_4 (17) +#define LCU_IP_MUX_SEL_LU_OUT_5 (18) +#define LCU_IP_MUX_SEL_LU_OUT_6 (19) +#define LCU_IP_MUX_SEL_LU_OUT_7 (20) +#define LCU_IP_MUX_SEL_LU_OUT_8 (21) +#define LCU_IP_MUX_SEL_LU_OUT_9 (22) +#define LCU_IP_MUX_SEL_LU_OUT_10 (23) +#define LCU_IP_MUX_SEL_LU_OUT_11 (24) + +#define LCU_IP_IN_0 (0) +#define LCU_IP_IN_1 (1) +#define LCU_IP_IN_2 (2) +#define LCU_IP_IN_3 (3) +#define LCU_IP_IN_4 (4) +#define LCU_IP_IN_5 (5) +#define LCU_IP_IN_6 (6) +#define LCU_IP_IN_7 (7) +#define LCU_IP_IN_8 (8) +#define LCU_IP_IN_9 (9) +#define LCU_IP_IN_10 (10) +#define LCU_IP_IN_11 (11)