diff mbox series

[v2,3/3] pwm: rzg2l-gpt: Add support for linking with poeg

Message ID 20221111192942.717137-4-biju.das.jz@bp.renesas.com
State Changes Requested
Headers show
Series Add support for linking gpt with poeg | expand

Commit Message

Biju Das Nov. 11, 2022, 7:29 p.m. UTC
This patch add support for linking poeg group with gpt, so that
gpt can control the output disable function.

Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
---
v1->v2:
 * Replaced id->poeg-id as per poeg bindings.
This patch depend upon [1]
[1] https://lore.kernel.org/linux-renesas-soc/20221111162325.471963-3-biju.das.jz@bp.renesas.com/T/#u
---
 drivers/pwm/pwm-rzg2l-gpt.c | 69 +++++++++++++++++++++++++++++++++++++
 1 file changed, 69 insertions(+)

Comments

Uwe Kleine-König Nov. 28, 2022, 1:01 p.m. UTC | #1
On Fri, Nov 11, 2022 at 07:29:42PM +0000, Biju Das wrote:
> This patch add support for linking poeg group with gpt, so that
> gpt can control the output disable function.

Without knowing what a "gpt" and a "poeg" is, I don't understand the
purpose of this patch. Why do I want the gpt to be able to control the
output disable function and what is this?

> Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
> ---
> v1->v2:
>  * Replaced id->poeg-id as per poeg bindings.
> This patch depend upon [1]
> [1] https://lore.kernel.org/linux-renesas-soc/20221111162325.471963-3-biju.das.jz@bp.renesas.com/T/#u
> ---
>  drivers/pwm/pwm-rzg2l-gpt.c | 69 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 69 insertions(+)
> 
> diff --git a/drivers/pwm/pwm-rzg2l-gpt.c b/drivers/pwm/pwm-rzg2l-gpt.c
> index 3f9325e958b8..e16f976c3dcb 100644
> --- a/drivers/pwm/pwm-rzg2l-gpt.c
> +++ b/drivers/pwm/pwm-rzg2l-gpt.c
> @@ -31,6 +31,7 @@
>  #define RZG2L_GTCR		0x2c
>  #define RZG2L_GTUDDTYC		0x30
>  #define RZG2L_GTIOR		0x34
> +#define RZG2L_GTINTAD		0x38
>  #define RZG2L_GTBER		0x40
>  #define RZG2L_GTCNT		0x48
>  #define RZG2L_GTCCRA		0x4c
> @@ -48,9 +49,15 @@
>  #define RZG2L_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_OAE		BIT(8)
>  #define RZG2L_GTIOR_OBE		BIT(24)
> +#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_LO_OUT_LO_END_TOGGLE	0x07
>  #define RZG2L_INIT_OUT_HI_OUT_HI_END_TOGGLE	0x1b
> @@ -64,12 +71,16 @@
>  #define RZG2L_GTIOR_GTIOB_OUT_LO_END_TOGGLE_CMP_MATCH \
>  	(FIELD_PREP(RZG2L_GTIOR_GTIOB, RZG2L_INIT_OUT_LO_OUT_LO_END_TOGGLE) | RZG2L_GTIOR_OBE)
>  
> +#define RZG2L_GTINTAD_GRP_MASK			GENMASK(25, 24)
> +
>  #define RZG2L_GTCCR(i) (0x4c + 4 * (i))
>  
>  #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_POEG_GROUPS	(4)
> +
>  #define RZG2L_IS_IOB(a) ((a) & 0x1)
>  #define RZG2L_GET_CH_INDEX(a) ((a) / 2)
>  
> @@ -85,6 +96,7 @@ struct rzg2l_gpt_chip {
>  	u32 state_period[RZG2L_MAX_HW_CHANNELS];
>  	u32 user_count[RZG2L_MAX_HW_CHANNELS];
>  	u8 prescale[RZG2L_MAX_HW_CHANNELS];
> +	DECLARE_BITMAP(poeg_gpt_link, RZG2L_MAX_POEG_GROUPS * RZG2L_MAX_HW_CHANNELS);
>  };
>  
>  static inline struct rzg2l_gpt_chip *to_rzg2l_gpt_chip(struct pwm_chip *chip)
> @@ -438,6 +450,62 @@ static void rzg2l_gpt_reset_assert_pm_disable(void *data)
>  	reset_control_assert(rzg2l_gpt->rstc);
>  }
>  
> +static void rzg2l_gpt_parse_properties(struct platform_device *pdev,
> +				       struct rzg2l_gpt_chip *rzg2l_gpt)
> +{
> +	struct of_phandle_args of_args;
> +	unsigned int i;
> +	u32 poeg_grp;
> +	int cells;
> +	u32 offs;
> +	int ret;
> +
> +	cells = of_property_count_u32_elems(pdev->dev.of_node, "renesas,poegs");
> +	if (cells == -EINVAL)
> +		return;
> +
> +	cells >>= 1;
> +	for (i = 0; i < cells; i++) {
> +		ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
> +						       "renesas,poegs", 1, i,
> +						       &of_args);
> +		if (ret) {
> +			dev_err(&pdev->dev,
> +				"Failed to parse 'renesas,poegs' property\n");
> +			return;
> +		}
> +
> +		if (of_args.args[0] >= RZG2L_MAX_HW_CHANNELS) {
> +			dev_err(&pdev->dev,
> +				"Invalid channel %d > 7\n", of_args.args[0]);
> +			return;
> +		}
> +
> +		if (!of_device_is_available(of_args.np)) {
> +			/* It's fine to have a phandle to a non-enabled poeg. */
> +			of_node_put(of_args.np);
> +			continue;
> +		}
> +
> +		if (!of_property_read_u32(of_args.np, "renesas,poeg-id", &poeg_grp)) {
> +			offs = RZG2L_GET_CH_OFFS(of_args.args[0]);
> +
> +			set_bit(poeg_grp * RZG2L_MAX_HW_CHANNELS + of_args.args[0],
> +				rzg2l_gpt->poeg_gpt_link);

You might want to check if poeg_grp * RZG2L_MAX_HW_CHANNELS +
of_args.args[0] is less than 32 (i.e. the length of the poeg_gpt_link
bitmap). Also ensure that poeg_grp is withing bounds.

> +			rzg2l_gpt_modify(rzg2l_gpt, offs + RZG2L_GTINTAD,
> +					 RZG2L_GTINTAD_GRP_MASK,
> +					 poeg_grp << 24);
> +
> +			rzg2l_gpt_modify(rzg2l_gpt, offs + RZG2L_GTIOR,
> +					 RZG2L_GTIOR_OBDF | RZG2L_GTIOR_OADF,
> +					 RZG2L_GTIOR_PIN_DISABLE_SETTING);
> +		}
> +
> +		of_node_put(of_args.np);
> +	}
> +}
> +
>  static int rzg2l_gpt_probe(struct platform_device *pdev)
>  {
>  	DECLARE_BITMAP(ch_en_bits, RZG2L_MAX_PWM_CHANNELS);

Best regards
Uwe
Biju Das Nov. 28, 2022, 1:46 p.m. UTC | #2
Hi Uwe,

> Subject: Re: [PATCH v2 3/3] pwm: rzg2l-gpt: Add support for linking with
> poeg
> 
> On Fri, Nov 11, 2022 at 07:29:42PM +0000, Biju Das wrote:
> > This patch add support for linking poeg group with gpt, so that gpt
> > can control the output disable function.
> 
> Without knowing what a "gpt" and a "poeg" is, I don't understand the
> purpose of this patch. Why do I want the gpt to be able to control the
> output disable function and what is this?

gpt is General PWM Timer (GPT)

poeg is Port Output Enable for GPT (POEG)

gpt detects "dead time error and short-circuits between output pins"
and send Output disable request to poeg

poeg performs "Output-disable request from the GPT" and informs application.

There are 4 poeg groups shared to all the gpt hw channels.

gpt needs to configure a poeg group for the following poeg operations
1) Output-disable through Input level detection based on external pins
2) Output-disable request from the GPT
3) Output-disable through register setting.

For Output-disable request from the GPT, it needs to configure the type of protection
	a) Dead Time Error Output Disable Request Enable
	b) Same Time Output Level High Disable Request Enable
	c) Same Time Output Level Low Disable Request Enable

The code flow for Output-disable request from the GPT is something like below
1) gpt detects a short circuit and send out-put disable request in the form
   of interrupt to poeg.

2) poeg gets this disable request interrupt, Output is disabled, and it stops the Output-Disable state internally
   to avoid IRQ storm and informs the application

3) Once Output-Disable request is raised, the interrupt processing in application software should identify and
   solve the cause of request. After that, the status to be issued should be cleared followed by the release of
   Output-Disable state.


> 
> > Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
> > ---
> > v1->v2:
> >  * Replaced id->poeg-id as per poeg bindings.
> > This patch depend upon [1]
> > [1]
> > https://lore.kernel.org/linux-renesas-soc/20221111162325.471963-3-biju
> > .das.jz@bp.renesas.com/T/#u
> > ---
> >  drivers/pwm/pwm-rzg2l-gpt.c | 69
> > +++++++++++++++++++++++++++++++++++++
> >  1 file changed, 69 insertions(+)
> >
> > diff --git a/drivers/pwm/pwm-rzg2l-gpt.c b/drivers/pwm/pwm-rzg2l-gpt.c
> > index 3f9325e958b8..e16f976c3dcb 100644
> > --- a/drivers/pwm/pwm-rzg2l-gpt.c
> > +++ b/drivers/pwm/pwm-rzg2l-gpt.c
> > @@ -31,6 +31,7 @@
> >  #define RZG2L_GTCR		0x2c
> >  #define RZG2L_GTUDDTYC		0x30
> >  #define RZG2L_GTIOR		0x34
> > +#define RZG2L_GTINTAD		0x38
> >  #define RZG2L_GTBER		0x40
> >  #define RZG2L_GTCNT		0x48
> >  #define RZG2L_GTCCRA		0x4c
> > @@ -48,9 +49,15 @@
> >  #define RZG2L_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_OAE		BIT(8)
> >  #define RZG2L_GTIOR_OBE		BIT(24)
> > +#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_LO_OUT_LO_END_TOGGLE	0x07
> >  #define RZG2L_INIT_OUT_HI_OUT_HI_END_TOGGLE	0x1b
> > @@ -64,12 +71,16 @@
> >  #define RZG2L_GTIOR_GTIOB_OUT_LO_END_TOGGLE_CMP_MATCH \
> >  	(FIELD_PREP(RZG2L_GTIOR_GTIOB, RZG2L_INIT_OUT_LO_OUT_LO_END_TOGGLE)
> > | RZG2L_GTIOR_OBE)
> >
> > +#define RZG2L_GTINTAD_GRP_MASK			GENMASK(25, 24)
> > +
> >  #define RZG2L_GTCCR(i) (0x4c + 4 * (i))
> >
> >  #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_POEG_GROUPS	(4)
> > +
> >  #define RZG2L_IS_IOB(a) ((a) & 0x1)
> >  #define RZG2L_GET_CH_INDEX(a) ((a) / 2)
> >
> > @@ -85,6 +96,7 @@ struct rzg2l_gpt_chip {
> >  	u32 state_period[RZG2L_MAX_HW_CHANNELS];
> >  	u32 user_count[RZG2L_MAX_HW_CHANNELS];
> >  	u8 prescale[RZG2L_MAX_HW_CHANNELS];
> > +	DECLARE_BITMAP(poeg_gpt_link, RZG2L_MAX_POEG_GROUPS *
> > +RZG2L_MAX_HW_CHANNELS);
> >  };
> >
> >  static inline struct rzg2l_gpt_chip *to_rzg2l_gpt_chip(struct
> > pwm_chip *chip) @@ -438,6 +450,62 @@ static void
> rzg2l_gpt_reset_assert_pm_disable(void *data)
> >  	reset_control_assert(rzg2l_gpt->rstc);
> >  }
> >
> > +static void rzg2l_gpt_parse_properties(struct platform_device *pdev,
> > +				       struct rzg2l_gpt_chip *rzg2l_gpt) {
> > +	struct of_phandle_args of_args;
> > +	unsigned int i;
> > +	u32 poeg_grp;
> > +	int cells;
> > +	u32 offs;
> > +	int ret;
> > +
> > +	cells = of_property_count_u32_elems(pdev->dev.of_node,
> "renesas,poegs");
> > +	if (cells == -EINVAL)
> > +		return;
> > +
> > +	cells >>= 1;
> > +	for (i = 0; i < cells; i++) {
> > +		ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
> > +						       "renesas,poegs", 1, i,
> > +						       &of_args);
> > +		if (ret) {
> > +			dev_err(&pdev->dev,
> > +				"Failed to parse 'renesas,poegs' property\n");
> > +			return;
> > +		}
> > +
> > +		if (of_args.args[0] >= RZG2L_MAX_HW_CHANNELS) {
> > +			dev_err(&pdev->dev,
> > +				"Invalid channel %d > 7\n", of_args.args[0]);
> > +			return;
> > +		}
> > +
> > +		if (!of_device_is_available(of_args.np)) {
> > +			/* It's fine to have a phandle to a non-enabled poeg.
> */
> > +			of_node_put(of_args.np);
> > +			continue;
> > +		}
> > +
> > +		if (!of_property_read_u32(of_args.np, "renesas,poeg-id",
> &poeg_grp)) {
> > +			offs = RZG2L_GET_CH_OFFS(of_args.args[0]);
> > +
> > +			set_bit(poeg_grp * RZG2L_MAX_HW_CHANNELS +
> of_args.args[0],
> > +				rzg2l_gpt->poeg_gpt_link);
> 
> You might want to check if poeg_grp * RZG2L_MAX_HW_CHANNELS +
> of_args.args[0] is less than 32 (i.e. the length of the poeg_gpt_link
> bitmap). Also ensure that poeg_grp is withing bounds.

OK will handle this in next version.

Cheers,
Biju

> 
> > +			rzg2l_gpt_modify(rzg2l_gpt, offs + RZG2L_GTINTAD,
> > +					 RZG2L_GTINTAD_GRP_MASK,
> > +					 poeg_grp << 24);
> > +
> > +			rzg2l_gpt_modify(rzg2l_gpt, offs + RZG2L_GTIOR,
> > +					 RZG2L_GTIOR_OBDF | RZG2L_GTIOR_OADF,
> > +					 RZG2L_GTIOR_PIN_DISABLE_SETTING);
> > +		}
> > +
> > +		of_node_put(of_args.np);
> > +	}
> > +}
> > +
> >  static int rzg2l_gpt_probe(struct platform_device *pdev)  {
> >  	DECLARE_BITMAP(ch_en_bits, RZG2L_MAX_PWM_CHANNELS);
> 
> |
diff mbox series

Patch

diff --git a/drivers/pwm/pwm-rzg2l-gpt.c b/drivers/pwm/pwm-rzg2l-gpt.c
index 3f9325e958b8..e16f976c3dcb 100644
--- a/drivers/pwm/pwm-rzg2l-gpt.c
+++ b/drivers/pwm/pwm-rzg2l-gpt.c
@@ -31,6 +31,7 @@ 
 #define RZG2L_GTCR		0x2c
 #define RZG2L_GTUDDTYC		0x30
 #define RZG2L_GTIOR		0x34
+#define RZG2L_GTINTAD		0x38
 #define RZG2L_GTBER		0x40
 #define RZG2L_GTCNT		0x48
 #define RZG2L_GTCCRA		0x4c
@@ -48,9 +49,15 @@ 
 #define RZG2L_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_OAE		BIT(8)
 #define RZG2L_GTIOR_OBE		BIT(24)
+#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_LO_OUT_LO_END_TOGGLE	0x07
 #define RZG2L_INIT_OUT_HI_OUT_HI_END_TOGGLE	0x1b
@@ -64,12 +71,16 @@ 
 #define RZG2L_GTIOR_GTIOB_OUT_LO_END_TOGGLE_CMP_MATCH \
 	(FIELD_PREP(RZG2L_GTIOR_GTIOB, RZG2L_INIT_OUT_LO_OUT_LO_END_TOGGLE) | RZG2L_GTIOR_OBE)
 
+#define RZG2L_GTINTAD_GRP_MASK			GENMASK(25, 24)
+
 #define RZG2L_GTCCR(i) (0x4c + 4 * (i))
 
 #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_POEG_GROUPS	(4)
+
 #define RZG2L_IS_IOB(a) ((a) & 0x1)
 #define RZG2L_GET_CH_INDEX(a) ((a) / 2)
 
@@ -85,6 +96,7 @@  struct rzg2l_gpt_chip {
 	u32 state_period[RZG2L_MAX_HW_CHANNELS];
 	u32 user_count[RZG2L_MAX_HW_CHANNELS];
 	u8 prescale[RZG2L_MAX_HW_CHANNELS];
+	DECLARE_BITMAP(poeg_gpt_link, RZG2L_MAX_POEG_GROUPS * RZG2L_MAX_HW_CHANNELS);
 };
 
 static inline struct rzg2l_gpt_chip *to_rzg2l_gpt_chip(struct pwm_chip *chip)
@@ -438,6 +450,62 @@  static void rzg2l_gpt_reset_assert_pm_disable(void *data)
 	reset_control_assert(rzg2l_gpt->rstc);
 }
 
+static void rzg2l_gpt_parse_properties(struct platform_device *pdev,
+				       struct rzg2l_gpt_chip *rzg2l_gpt)
+{
+	struct of_phandle_args of_args;
+	unsigned int i;
+	u32 poeg_grp;
+	int cells;
+	u32 offs;
+	int ret;
+
+	cells = of_property_count_u32_elems(pdev->dev.of_node, "renesas,poegs");
+	if (cells == -EINVAL)
+		return;
+
+	cells >>= 1;
+	for (i = 0; i < cells; i++) {
+		ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
+						       "renesas,poegs", 1, i,
+						       &of_args);
+		if (ret) {
+			dev_err(&pdev->dev,
+				"Failed to parse 'renesas,poegs' property\n");
+			return;
+		}
+
+		if (of_args.args[0] >= RZG2L_MAX_HW_CHANNELS) {
+			dev_err(&pdev->dev,
+				"Invalid channel %d > 7\n", of_args.args[0]);
+			return;
+		}
+
+		if (!of_device_is_available(of_args.np)) {
+			/* It's fine to have a phandle to a non-enabled poeg. */
+			of_node_put(of_args.np);
+			continue;
+		}
+
+		if (!of_property_read_u32(of_args.np, "renesas,poeg-id", &poeg_grp)) {
+			offs = RZG2L_GET_CH_OFFS(of_args.args[0]);
+
+			set_bit(poeg_grp * RZG2L_MAX_HW_CHANNELS + of_args.args[0],
+				rzg2l_gpt->poeg_gpt_link);
+
+			rzg2l_gpt_modify(rzg2l_gpt, offs + RZG2L_GTINTAD,
+					 RZG2L_GTINTAD_GRP_MASK,
+					 poeg_grp << 24);
+
+			rzg2l_gpt_modify(rzg2l_gpt, offs + RZG2L_GTIOR,
+					 RZG2L_GTIOR_OBDF | RZG2L_GTIOR_OADF,
+					 RZG2L_GTIOR_PIN_DISABLE_SETTING);
+		}
+
+		of_node_put(of_args.np);
+	}
+}
+
 static int rzg2l_gpt_probe(struct platform_device *pdev)
 {
 	DECLARE_BITMAP(ch_en_bits, RZG2L_MAX_PWM_CHANNELS);
@@ -480,6 +548,7 @@  static int rzg2l_gpt_probe(struct platform_device *pdev)
 	if (ret < 0)
 		goto clk_disable;
 
+	rzg2l_gpt_parse_properties(pdev, rzg2l_gpt);
 	mutex_init(&rzg2l_gpt->lock);
 	platform_set_drvdata(pdev, rzg2l_gpt);
 	bitmap_zero(ch_en_bits, RZG2L_MAX_PWM_CHANNELS);