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>
This commit is contained in:
Sumit Batra 2023-12-05 19:13:47 +05:30 committed by David Leach
commit e0dc6f4fe4
12 changed files with 791 additions and 19 deletions

View file

@ -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;
};
};
};

View file

@ -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";
};

View file

@ -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)

View file

@ -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"

View file

@ -0,0 +1,6 @@
# Copyright 2023 NXP
# SPDX-License-Identifier: Apache-2.0
zephyr_library()
zephyr_library_sources(qdec_nxp_s32.c)

View file

@ -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

View file

@ -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)

View file

@ -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";
};
};
};

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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)