[PATCH v7 9/9] pwm: rzg2l-gpt: Add support for gpt linking with poeg
Biju <[email protected]>
| Newsgroups | org.kernel.vger.linux-pwm,org.kernel.vger.linux-kernel,org.kernel.vger.linux-renesas-soc |
|---|---|
| Message-ID | <[email protected]> |
From: Biju Das <[email protected]> The General PWM Timer (GPT) is capable of detecting "dead time error and short-circuits between output pins" and send Output disable request to poeg(Port Output Enable for GPT). Add support for linking poeg group with gpt, so that gpt can control the output disable function by adding rzg2l_gpt_poeg_init() to parse the renesas,poegs device tree property and establish links between POEG groups (A–D) and GPT hardware channels (0–7). For each valid, enabled POEG phandle entry, the driver: - Reads the renesas,poeg-id from the POEG node and validates it against the supported range - Records the GPT–POEG association in a per-chip bitmap (poeg_gpt_link) - Configures GTINTAD to route the output disable request to the correct POEG group - Configures GTIOR (OADF/OBDF fields) to set both output pins to high-impedance on an output disable event Non-enabled POEG nodes are silently skipped. Signed-off-by: Biju Das <[email protected]> --- v6->v7: * Added rzg2l_gpt_poeg_link_channels() and used __free for deallocating device node pointer. * Replaced dev_err()->dev_err_probe() in rzg2l_gpt_poeg_link_channels(). * Replaced local variable cells with num_poeg_pairs in rzg2l_gpt_poeg_init(). v5->v6: * Dropped extra space in poegs variable assignment in rzg2l_gpt_poeg_init(). * Updated the comment sections of rzg2l_gpt_poeg_init() with POEG and GPT in upper-case and replaced configure->configures. * Format specifiers in dev_err() changed from %d to %u in rzg2l_gpt_poeg_init(). v5: * Updated commit description. * Replaced return type of rzg2l_gpt_poeg_init() from void->int and probe() checks this return value. * Added more error checks in rzg2l_gpt_poeg_init() V24 from [1]: [1] https://lore.kernel.org/all/[email protected]/ --- drivers/pwm/pwm-rzg2l-gpt.c | 92 +++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/drivers/pwm/pwm-rzg2l-gpt.c b/drivers/pwm/pwm-rzg2l-gpt.c index c61c648f71a8..4e1591d81b51 100644 --- a/drivers/pwm/pwm-rzg2l-gpt.c +++ b/drivers/pwm/pwm-rzg2l-gpt.c @@ -45,6 +45,7 @@ #define RZG2L_GTCR(ch) (0x2c + RZG2L_GET_CH_OFFS(ch)) #define RZG2L_GTUDDTYC(ch) (0x30 + RZG2L_GET_CH_OFFS(ch)) #define RZG2L_GTIOR(ch) (0x34 + RZG2L_GET_CH_OFFS(ch)) +#define RZG2L_GTINTAD(ch) (0x38 + RZG2L_GET_CH_OFFS(ch)) #define RZG2L_GTBER(ch) (0x40 + RZG2L_GET_CH_OFFS(ch)) #define RZG2L_GTCNT(ch) (0x48 + RZG2L_GET_CH_OFFS(ch)) #define RZG2L_GTCCR(ch, sub_ch) (0x4c + RZG2L_GET_CH_OFFS(ch) + 4 * (sub_ch)) @@ -62,12 +63,19 @@ #define RZG2L_GTUDDTYC_UP_COUNTING (RZG2L_GTUDDTYC_UP | RZG2L_GTUDDTYC_UDF) #define RZG2L_GTIOR_GTIOA GENMASK(4, 0) +#define RZG2L_GTIOR_OADF GENMASK(10, 9) #define RZG2L_GTIOR_GTIOB GENMASK(20, 16) +#define RZG2L_GTIOR_OBDF GENMASK(26, 25) #define RZG2L_GTIOR_GTIOx(sub_ch) ((sub_ch) ? RZG2L_GTIOR_GTIOB : RZG2L_GTIOR_GTIOA) #define RZG2L_GTIOR_OAE BIT(8) #define RZG2L_GTIOR_OBE BIT(24) #define RZG2L_GTIOR_OxE(sub_ch) ((sub_ch) ? RZG2L_GTIOR_OBE : RZG2L_GTIOR_OAE) +#define RZG2L_GTIOR_OADF_HIGH_IMP_ON_OUT_DISABLE BIT(9) +#define RZG2L_GTIOR_OBDF_HIGH_IMP_ON_OUT_DISABLE BIT(25) +#define RZG2L_GTIOR_PIN_DISABLE_SETTING \ + (RZG2L_GTIOR_OADF_HIGH_IMP_ON_OUT_DISABLE | RZG2L_GTIOR_OBDF_HIGH_IMP_ON_OUT_DISABLE) + #define RZG2L_INIT_OUT_HI_OUT_HI_END_TOGGLE 0x1b #define RZG2L_GTIOR_GTIOA_OUT_HI_END_TOGGLE_CMP_MATCH \ (RZG2L_INIT_OUT_HI_OUT_HI_END_TOGGLE | RZG2L_GTIOR_OAE) @@ -78,12 +86,17 @@ ((sub_ch) ? RZG2L_GTIOR_GTIOB_OUT_HI_END_TOGGLE_CMP_MATCH : \ RZG2L_GTIOR_GTIOA_OUT_HI_END_TOGGLE_CMP_MATCH) +#define RZG2L_GTINTAD_GRP_MASK GENMASK(25, 24) + #define RZG2L_MAX_HW_CHANNELS 8 #define RZG2L_CHANNELS_PER_IO 2 #define RZG2L_MAX_PWM_CHANNELS (RZG2L_MAX_HW_CHANNELS * RZG2L_CHANNELS_PER_IO) #define RZG2L_MAX_SCALE_FACTOR 1024 #define RZG2L_MAX_TICKS ((u64)U32_MAX * RZG2L_MAX_SCALE_FACTOR) +#define RZG2L_MAX_POEG_GROUPS 4 +#define RZG2L_LAST_POEG_GROUP 3 + struct rzg2l_gpt_info { u8 (*calculate_prescale)(u64 period); u32 gtcr_tpcs; @@ -98,6 +111,7 @@ struct rzg2l_gpt_chip { u64 period_ticks[RZG2L_MAX_HW_CHANNELS]; u32 channel_request_count[RZG2L_MAX_HW_CHANNELS]; u32 channel_enable_count[RZG2L_MAX_HW_CHANNELS]; + DECLARE_BITMAP(poeg_gpt_link, RZG2L_MAX_POEG_GROUPS * RZG2L_MAX_HW_CHANNELS); }; /* This represents a hardware configuration for one channel */ @@ -459,6 +473,80 @@ static const struct pwm_ops rzg2l_gpt_ops = { .write_waveform = rzg2l_gpt_write_waveform, }; +static int rzg2l_gpt_poeg_link_channels(struct device *dev, + struct rzg2l_gpt_chip *rzg2l_gpt, + struct of_phandle_args *of_args) +{ + struct device_node *np __free(device_node) = of_args->np; + u32 poeg_grp, bitpos; + + if (of_args->args[0] >= RZG2L_MAX_HW_CHANNELS) + return dev_err_probe(dev, -EINVAL, "Invalid channel %u >= %u\n", + of_args->args[0], RZG2L_MAX_HW_CHANNELS); + + if (!of_device_is_available(of_args->np)) + /* It's fine to have a phandle to a non-enabled poeg. */ + return 0; + + if (!of_property_read_u32(of_args->np, "renesas,poeg-id", &poeg_grp)) { + if (poeg_grp > RZG2L_LAST_POEG_GROUP) + return dev_err_probe(dev, -EINVAL, "Invalid poeg group %u > %u\n", + poeg_grp, RZG2L_LAST_POEG_GROUP); + + bitpos = of_args->args[0] + poeg_grp * RZG2L_MAX_HW_CHANNELS; + set_bit(bitpos, rzg2l_gpt->poeg_gpt_link); + + rzg2l_gpt_modify(rzg2l_gpt, RZG2L_GTINTAD(of_args->args[0]), + RZG2L_GTINTAD_GRP_MASK, poeg_grp << 24); + + rzg2l_gpt_modify(rzg2l_gpt, RZG2L_GTIOR(of_args->args[0]), + RZG2L_GTIOR_OBDF | RZG2L_GTIOR_OADF, + RZG2L_GTIOR_PIN_DISABLE_SETTING); + } + + return 0; +} + +/* + * This function links a POEG group{A,B,C,D} with a GPT channel{0..7} and + * configures the pin for output disable. + */ +static int rzg2l_gpt_poeg_init(struct platform_device *pdev, + struct rzg2l_gpt_chip *rzg2l_gpt) +{ + const char *poeg_name = "renesas,poegs"; + struct of_phandle_args of_args; + struct property *poegs; + int num_poeg_pairs; + int ret; + + poegs = of_find_property(pdev->dev.of_node, poeg_name, NULL); + if (!poegs) + return 0; + + num_poeg_pairs = of_property_count_u32_elems(pdev->dev.of_node, poeg_name); + if (num_poeg_pairs < 0) + return num_poeg_pairs; + + if (num_poeg_pairs & 1) + return -EINVAL; + + num_poeg_pairs >>= 1; + for (unsigned int i = 0; i < num_poeg_pairs; i++) { + ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, + poeg_name, 1, i, + &of_args); + if (ret) + return ret; + + ret = rzg2l_gpt_poeg_link_channels(&pdev->dev, rzg2l_gpt, &of_args); + if (ret) + return ret; + } + + return 0; +} + static int rzg2l_gpt_probe(struct platform_device *pdev) { struct rzg2l_gpt_chip *rzg2l_gpt; @@ -520,6 +608,10 @@ static int rzg2l_gpt_probe(struct platform_device *pdev) if (rzg2l_gpt->rate_khz * KILO != rate) return dev_err_probe(dev, -EINVAL, "Rate is not multiple of 1000\n"); + ret = rzg2l_gpt_poeg_init(pdev, rzg2l_gpt); + if (ret) + return dev_err_probe(dev, ret, "Failed to link gpt with poeg\n"); + mutex_init(&rzg2l_gpt->lock); chip->ops = &rzg2l_gpt_ops; -- 2.43.0