drivers: sensor: qdec_s32: Add QDEC support for S32
Add code to configure and program Lcu, Trgmux and Emios_Icu IPs to
get the the rotations by the motor in radians.
Co-authored-by: Benjamin Perseghetti <bperseghetti@rudislabs.com>
Co-authored-by: Peter van der Perk <peter.vanderperk@nxp.com>
Co-authored-by: Mayank Mahajan <mayankmahajan.x@nxp.com>
Signed-off-by: Sumit Batra <sumit.batra@nxp.com>
diff --git a/boards/arm/mr_canhubk3/mr_canhubk3-pinctrl.dtsi b/boards/arm/mr_canhubk3/mr_canhubk3-pinctrl.dtsi
index 0b2906f..e057aeb 100644
--- a/boards/arm/mr_canhubk3/mr_canhubk3-pinctrl.dtsi
+++ b/boards/arm/mr_canhubk3/mr_canhubk3-pinctrl.dtsi
@@ -287,7 +287,6 @@
pinmux = <PTB12_EMIOS_0_CH0_X_O>, <PTB13_EMIOS_0_CH1_G_O>,
<PTB14_EMIOS_0_CH2_G_O>, <PTB15_EMIOS_0_CH3_G_O>,
<PTB16_EMIOS_0_CH4_G_O>, <PTB17_EMIOS_0_CH5_G_O>,
- <PTA17_EMIOS_0_CH6_G_O>, <PTE7_EMIOS_0_CH7_G_O>,
<PTE14_EMIOS_0_CH19_Y_O>;
output-enable;
};
@@ -300,4 +299,14 @@
output-enable;
};
};
+
+ qdec_s32: qdec_s32 {
+ group1 {
+ pinmux = <PTB2_TRGMUX_IN3>,
+ <PTB3_TRGMUX_IN2>,
+ <TRGMUX_INT_OUT37_EMIOS_0_CH6_G>,
+ <TRGMUX_INT_OUT38_EMIOS_0_CH7_G>;
+ input-enable;
+ };
+ };
};
diff --git a/boards/arm/mr_canhubk3/mr_canhubk3.dts b/boards/arm/mr_canhubk3/mr_canhubk3.dts
index 50b7233..97bca14 100644
--- a/boards/arm/mr_canhubk3/mr_canhubk3.dts
+++ b/boards/arm/mr_canhubk3/mr_canhubk3.dts
@@ -11,6 +11,7 @@
#include <freq.h>
#include <dt-bindings/pwm/pwm.h>
#include "mr_canhubk3-pinctrl.dtsi"
+#include <zephyr/dt-bindings/sensor/qdec_nxp_s32.h>
/ {
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_IP_IN_0 LCU_IP_IN_1
+ LCU_IP_IN_2 LCU_IP_IN_3>;
+ lcu-mux-sel =
+ <LCU_IP_MUX_SEL_LU_IN_0 LCU_IP_MUX_SEL_LU_IN_1
+ LCU_IP_MUX_SEL_LU_OUT_0 LCU_IP_MUX_SEL_LU_OUT_1>;
+ 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 76bb17b..afa200a 100644
--- a/drivers/sensor/CMakeLists.txt
+++ b/drivers/sensor/CMakeLists.txt
@@ -112,6 +112,7 @@
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 fba7501..7e45474 100644
--- a/drivers/sensor/Kconfig
+++ b/drivers/sensor/Kconfig
@@ -189,6 +189,7 @@
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 0000000..a3065a3
--- /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 0000000..4250d9b
--- /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 0000000..31d6297
--- /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 <errno.h>
+#include <stdint.h>
+
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/sensor.h>
+#include <zephyr/logging/log.h>
+#include <zephyr/sys/arch_interface.h>
+
+#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 <Emios_Icu_Ip.h>
+#include <Trgmux_Ip.h>
+#include <Lcu_Ip.h>
+
+#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 036b89a..0289b24 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 0000000..51707de
--- /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 0000000..55a589d
--- /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 0000000..935d212
--- /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_IP_IN_0 LCU_IP_IN_1
+ LCU_IP_IN_2 LCU_IP_IN_3>;
+ lcu-mux-sel =
+ <LCU_IP_MUX_SEL_LU_IN_0 LCU_IP_MUX_SEL_LU_IN_1
+ LCU_IP_MUX_SEL_LU_OUT_0 LCU_IP_MUX_SEL_LU_OUT_1>;
+ 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 0000000..c7df471
--- /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)