linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] gpio-mvebu: no hardcoded timer assignment for pwm
@ 2024-01-30 10:55 Finn Behrens
  2024-02-06 11:43 ` Bartosz Golaszewski
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Finn Behrens @ 2024-01-30 10:55 UTC (permalink / raw)
  To: Thierry Reding, Uwe Kleine-König, Linus Walleij,
	Bartosz Golaszewski, linux-pwm, linux-gpio, linux-kernel
  Cc: Finn Behrens, Heisath, Yureka Lilian

Removes the hardcoded timer assignment of timers to pwm controllers.
This allows to use more than one pwm per gpio bank.

Original patch with chip_data interface by Heisath <jannis@imserv.org>

Link: https://wiki.kobol.io/helios4/pwm/#patch-requirement
Co-developed-by: Yureka Lilian <yuka@yuka.dev>
Signed-off-by: Yureka Lilian <yuka@yuka.dev>
Signed-off-by: Finn Behrens <me@kloenk.de>
---
 drivers/gpio/gpio-mvebu.c | 223 ++++++++++++++++++++++++--------------
 1 file changed, 139 insertions(+), 84 deletions(-)

diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c
index a13f3c18ccd4..303ea3be0b69 100644
--- a/drivers/gpio/gpio-mvebu.c
+++ b/drivers/gpio/gpio-mvebu.c
@@ -94,21 +94,43 @@
 
 #define MVEBU_MAX_GPIO_PER_BANK		32
 
-struct mvebu_pwm {
+enum mvebu_pwm_ctrl {
+	MVEBU_PWM_CTRL_SET_A = 0,
+	MVEBU_PWM_CTRL_SET_B,
+	MVEBU_PWM_CTRL_MAX
+};
+
+struct mvebu_pwmchip {
 	struct regmap		*regs;
 	u32			 offset;
 	unsigned long		 clk_rate;
-	struct gpio_desc	*gpiod;
-	struct pwm_chip		 chip;
 	spinlock_t		 lock;
-	struct mvebu_gpio_chip	*mvchip;
+	bool			 in_use;
 
 	/* Used to preserve GPIO/PWM registers across suspend/resume */
-	u32			 blink_select;
 	u32			 blink_on_duration;
 	u32			 blink_off_duration;
 };
 
+struct mvebu_pwm_chip_drv {
+	enum mvebu_pwm_ctrl	 ctrl;
+	struct gpio_desc	*gpiod;
+	bool			 master;
+};
+
+struct mvebu_pwm {
+	struct pwm_chip		 chip;
+	struct mvebu_gpio_chip	*mvchip;
+	struct mvebu_pwmchip	 controller;
+	enum mvebu_pwm_ctrl	 default_controller;
+
+	/* Used to preserve GPIO/PWM registers across suspend/resume */
+	u32				 blink_select;
+	struct mvebu_pwm_chip_drv	 drv[];
+};
+
+static struct mvebu_pwmchip  *mvebu_pwm_list[MVEBU_PWM_CTRL_MAX];
+
 struct mvebu_gpio_chip {
 	struct gpio_chip   chip;
 	struct regmap     *regs;
@@ -285,12 +307,12 @@ mvebu_gpio_write_level_mask(struct mvebu_gpio_chip *mvchip, u32 val)
  * Functions returning offsets of individual registers for a given
  * PWM controller.
  */
-static unsigned int mvebu_pwmreg_blink_on_duration(struct mvebu_pwm *mvpwm)
+static unsigned int mvebu_pwmreg_blink_on_duration(struct mvebu_pwmchip *mvpwm)
 {
 	return mvpwm->offset + PWM_BLINK_ON_DURATION_OFF;
 }
 
-static unsigned int mvebu_pwmreg_blink_off_duration(struct mvebu_pwm *mvpwm)
+static unsigned int mvebu_pwmreg_blink_off_duration(struct mvebu_pwmchip *mvpwm)
 {
 	return mvpwm->offset + PWM_BLINK_OFF_DURATION_OFF;
 }
@@ -623,39 +645,71 @@ static int mvebu_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
 	struct mvebu_pwm *mvpwm = to_mvebu_pwm(chip);
 	struct mvebu_gpio_chip *mvchip = mvpwm->mvchip;
 	struct gpio_desc *desc;
+	enum mvebu_pwm_ctrl id;
 	unsigned long flags;
 	int ret = 0;
+	struct mvebu_pwm_chip_drv *drv = &mvpwm->drv[pwm->hwpwm];
 
-	spin_lock_irqsave(&mvpwm->lock, flags);
+	spin_lock_irqsave(&mvpwm->controller.lock, flags);
 
-	if (mvpwm->gpiod) {
+	if (drv->gpiod || (mvchip->blink_en_reg & BIT(pwm->hwpwm))) {
 		ret = -EBUSY;
-	} else {
-		desc = gpiochip_request_own_desc(&mvchip->chip,
-						 pwm->hwpwm, "mvebu-pwm",
-						 GPIO_ACTIVE_HIGH,
-						 GPIOD_OUT_LOW);
-		if (IS_ERR(desc)) {
-			ret = PTR_ERR(desc);
-			goto out;
-		}
+		goto out;
+	}
+
+	desc = gpiochip_request_own_desc(&mvchip->chip,
+					 pwm->hwpwm, "mvebu-pwm",
+					 GPIO_ACTIVE_HIGH,
+					 GPIOD_OUT_LOW);
+	if (IS_ERR(desc)) {
+		ret = PTR_ERR(desc);
+		goto out;
+	}
 
-		mvpwm->gpiod = desc;
+	ret = gpiod_direction_output(desc, 0);
+	if (ret) {
+		gpiochip_free_own_desc(desc);
+		goto out;
 	}
+
+	for (id = MVEBU_PWM_CTRL_SET_A; id < MVEBU_PWM_CTRL_MAX; id++) {
+		if (!mvebu_pwm_list[id]->in_use) {
+			drv->ctrl = id;
+			drv->master = true;
+			mvebu_pwm_list[id]->in_use = true;
+			break;
+		}
+	}
+
+	if (!drv->master)
+		drv->ctrl = mvpwm->default_controller;
+
+	regmap_update_bits(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset,
+			   BIT(pwm->hwpwm), drv->ctrl ? BIT(pwm->hwpwm) : 0);
+
+	drv->gpiod = desc;
+
+	regmap_read(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset,
+		    &mvpwm->blink_select);
 out:
-	spin_unlock_irqrestore(&mvpwm->lock, flags);
+	spin_unlock_irqrestore(&mvpwm->controller.lock, flags);
 	return ret;
 }
 
 static void mvebu_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
 {
 	struct mvebu_pwm *mvpwm = to_mvebu_pwm(chip);
+	struct mvebu_pwm_chip_drv *drv = &mvpwm->drv[pwm->hwpwm];
 	unsigned long flags;
 
-	spin_lock_irqsave(&mvpwm->lock, flags);
-	gpiochip_free_own_desc(mvpwm->gpiod);
-	mvpwm->gpiod = NULL;
-	spin_unlock_irqrestore(&mvpwm->lock, flags);
+	spin_lock_irqsave(&mvpwm->controller.lock, flags);
+	if (drv->master)
+		mvebu_pwm_list[drv->ctrl]->in_use = false;
+
+	gpiochip_free_own_desc(drv->gpiod);
+	memset(drv, 0, sizeof(struct mvebu_pwm_chip_drv));
+
+	spin_unlock_irqrestore(&mvpwm->controller.lock, flags);
 }
 
 static int mvebu_pwm_get_state(struct pwm_chip *chip,
@@ -665,28 +719,35 @@ static int mvebu_pwm_get_state(struct pwm_chip *chip,
 
 	struct mvebu_pwm *mvpwm = to_mvebu_pwm(chip);
 	struct mvebu_gpio_chip *mvchip = mvpwm->mvchip;
+	struct mvebu_pwm_chip_drv *drv = &mvpwm->drv[pwm->hwpwm];
+	struct mvebu_pwmchip *controller;
 	unsigned long long val;
 	unsigned long flags;
 	u32 u;
 
-	spin_lock_irqsave(&mvpwm->lock, flags);
+	if (drv->gpiod)
+		controller = mvebu_pwm_list[drv->ctrl];
+	else
+		controller = &mvpwm->controller;
+
+	spin_lock_irqsave(&controller->lock, flags);
 
-	regmap_read(mvpwm->regs, mvebu_pwmreg_blink_on_duration(mvpwm), &u);
+	regmap_read(controller->regs, mvebu_pwmreg_blink_on_duration(controller), &u);
 	/* Hardware treats zero as 2^32. See mvebu_pwm_apply(). */
 	if (u > 0)
 		val = u;
 	else
 		val = UINT_MAX + 1ULL;
 	state->duty_cycle = DIV_ROUND_UP_ULL(val * NSEC_PER_SEC,
-			mvpwm->clk_rate);
+			controller->clk_rate);
 
-	regmap_read(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm), &u);
+	regmap_read(controller->regs, mvebu_pwmreg_blink_off_duration(controller), &u);
 	/* period = on + off duration */
 	if (u > 0)
 		val += u;
 	else
 		val += UINT_MAX + 1ULL;
-	state->period = DIV_ROUND_UP_ULL(val * NSEC_PER_SEC, mvpwm->clk_rate);
+	state->period = DIV_ROUND_UP_ULL(val * NSEC_PER_SEC, controller->clk_rate);
 
 	regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, &u);
 	if (u)
@@ -694,7 +755,7 @@ static int mvebu_pwm_get_state(struct pwm_chip *chip,
 	else
 		state->enabled = false;
 
-	spin_unlock_irqrestore(&mvpwm->lock, flags);
+	spin_unlock_irqrestore(&controller->lock, flags);
 
 	return 0;
 }
@@ -703,6 +764,8 @@ static int mvebu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 			   const struct pwm_state *state)
 {
 	struct mvebu_pwm *mvpwm = to_mvebu_pwm(chip);
+	struct mvebu_pwm_chip_drv *drv = &mvpwm->drv[pwm->hwpwm];
+	struct mvebu_pwmchip *controller;
 	struct mvebu_gpio_chip *mvchip = mvpwm->mvchip;
 	unsigned long long val;
 	unsigned long flags;
@@ -711,7 +774,11 @@ static int mvebu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 	if (state->polarity != PWM_POLARITY_NORMAL)
 		return -EINVAL;
 
-	val = (unsigned long long) mvpwm->clk_rate * state->duty_cycle;
+	if (drv->gpiod)
+		controller = mvebu_pwm_list[drv->ctrl];
+	else
+		controller = &mvpwm->controller;
+	val = (unsigned long long) controller->clk_rate * state->duty_cycle;
 	do_div(val, NSEC_PER_SEC);
 	if (val > UINT_MAX + 1ULL)
 		return -EINVAL;
@@ -726,7 +793,7 @@ static int mvebu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 	else
 		on = 1;
 
-	val = (unsigned long long) mvpwm->clk_rate * state->period;
+	val = (unsigned long long) controller->clk_rate * state->period;
 	do_div(val, NSEC_PER_SEC);
 	val -= on;
 	if (val > UINT_MAX + 1ULL)
@@ -738,16 +805,16 @@ static int mvebu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 	else
 		off = 1;
 
-	spin_lock_irqsave(&mvpwm->lock, flags);
+	spin_lock_irqsave(&controller->lock, flags);
 
-	regmap_write(mvpwm->regs, mvebu_pwmreg_blink_on_duration(mvpwm), on);
-	regmap_write(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm), off);
+	regmap_write(controller->regs, mvebu_pwmreg_blink_on_duration(controller), on);
+	regmap_write(controller->regs, mvebu_pwmreg_blink_off_duration(controller), off);
 	if (state->enabled)
 		mvebu_gpio_blink(&mvchip->chip, pwm->hwpwm, 1);
 	else
 		mvebu_gpio_blink(&mvchip->chip, pwm->hwpwm, 0);
 
-	spin_unlock_irqrestore(&mvpwm->lock, flags);
+	spin_unlock_irqrestore(&controller->lock, flags);
 
 	return 0;
 }
@@ -762,25 +829,27 @@ static const struct pwm_ops mvebu_pwm_ops = {
 static void __maybe_unused mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip)
 {
 	struct mvebu_pwm *mvpwm = mvchip->mvpwm;
+	struct mvebu_pwmchip *controller = &mvpwm->controller;
 
 	regmap_read(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset,
 		    &mvpwm->blink_select);
-	regmap_read(mvpwm->regs, mvebu_pwmreg_blink_on_duration(mvpwm),
-		    &mvpwm->blink_on_duration);
-	regmap_read(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm),
-		    &mvpwm->blink_off_duration);
+	regmap_read(controller->regs, mvebu_pwmreg_blink_on_duration(controller),
+		    &controller->blink_on_duration);
+	regmap_read(controller->regs, mvebu_pwmreg_blink_off_duration(controller),
+		    &controller->blink_off_duration);
 }
 
 static void __maybe_unused mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip)
 {
 	struct mvebu_pwm *mvpwm = mvchip->mvpwm;
+	struct mvebu_pwmchip *controller = &mvpwm->controller;
 
 	regmap_write(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset,
 		     mvpwm->blink_select);
-	regmap_write(mvpwm->regs, mvebu_pwmreg_blink_on_duration(mvpwm),
-		     mvpwm->blink_on_duration);
-	regmap_write(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm),
-		     mvpwm->blink_off_duration);
+	regmap_write(controller->regs, mvebu_pwmreg_blink_on_duration(controller),
+		     controller->blink_on_duration);
+	regmap_write(controller->regs, mvebu_pwmreg_blink_off_duration(controller),
+		     controller->blink_off_duration);
 }
 
 static int mvebu_pwm_probe(struct platform_device *pdev,
@@ -792,6 +861,7 @@ static int mvebu_pwm_probe(struct platform_device *pdev,
 	void __iomem *base;
 	u32 offset;
 	u32 set;
+	enum mvebu_pwm_ctrl ctrl_set;
 
 	if (mvchip->soc_variant == MVEBU_GPIO_SOC_VARIANT_A8K) {
 		int ret = of_property_read_u32(dev->of_node,
@@ -813,57 +883,39 @@ static int mvebu_pwm_probe(struct platform_device *pdev,
 	if (IS_ERR(mvchip->clk))
 		return PTR_ERR(mvchip->clk);
 
-	mvpwm = devm_kzalloc(dev, sizeof(struct mvebu_pwm), GFP_KERNEL);
+	mvpwm = devm_kzalloc(dev, struct_size(mvpwm, drv, mvchip->chip.ngpio), GFP_KERNEL);
 	if (!mvpwm)
 		return -ENOMEM;
 	mvchip->mvpwm = mvpwm;
 	mvpwm->mvchip = mvchip;
-	mvpwm->offset = offset;
 
-	if (mvchip->soc_variant == MVEBU_GPIO_SOC_VARIANT_A8K) {
-		mvpwm->regs = mvchip->regs;
+	base = devm_platform_ioremap_resource_byname(pdev, "pwm");
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+	mvpwm->controller.regs = devm_regmap_init_mmio(&pdev->dev, base,
+						       &mvebu_gpio_regmap_config);
+	if (IS_ERR(mvpwm->controller.regs))
+		return PTR_ERR(mvpwm->controller.regs);
 
-		switch (mvchip->offset) {
-		case AP80X_GPIO0_OFF_A8K:
-		case CP11X_GPIO0_OFF_A8K:
-			/* Blink counter A */
-			set = 0;
-			break;
-		case CP11X_GPIO1_OFF_A8K:
-			/* Blink counter B */
-			set = U32_MAX;
-			mvpwm->offset += PWM_BLINK_COUNTER_B_OFF;
-			break;
-		default:
-			return -EINVAL;
-		}
+	/*
+	 * User set A for lines of GPIO chip with id 0, B for GPIO chip
+	 * with id 1. Don't allow further GPIO chips to be used for PWM.
+	 */
+	if (id == 0) {
+		set = 0;
+		ctrl_set = MVEBU_PWM_CTRL_SET_A;
+	} else if (id == 1) {
+		set = U32_MAX;
+		ctrl_set = MVEBU_PWM_CTRL_SET_B;
 	} else {
-		base = devm_platform_ioremap_resource_byname(pdev, "pwm");
-		if (IS_ERR(base))
-			return PTR_ERR(base);
-
-		mvpwm->regs = devm_regmap_init_mmio(&pdev->dev, base,
-						    &mvebu_gpio_regmap_config);
-		if (IS_ERR(mvpwm->regs))
-			return PTR_ERR(mvpwm->regs);
-
-		/*
-		 * Use set A for lines of GPIO chip with id 0, B for GPIO chip
-		 * with id 1. Don't allow further GPIO chips to be used for PWM.
-		 */
-		if (id == 0)
-			set = 0;
-		else if (id == 1)
-			set = U32_MAX;
-		else
-			return -EINVAL;
+		return -EINVAL;
 	}
 
 	regmap_write(mvchip->regs,
 		     GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, set);
 
-	mvpwm->clk_rate = clk_get_rate(mvchip->clk);
-	if (!mvpwm->clk_rate) {
+	mvpwm->controller.clk_rate = clk_get_rate(mvchip->clk);
+	if (!mvpwm->controller.clk_rate) {
 		dev_err(dev, "failed to get clock rate\n");
 		return -EINVAL;
 	}
@@ -872,7 +924,10 @@ static int mvebu_pwm_probe(struct platform_device *pdev,
 	mvpwm->chip.ops = &mvebu_pwm_ops;
 	mvpwm->chip.npwm = mvchip->chip.ngpio;
 
-	spin_lock_init(&mvpwm->lock);
+	spin_lock_init(&mvpwm->controller.lock);
+
+	mvpwm->default_controller = ctrl_set;
+	mvebu_pwm_list[ctrl_set] = &mvpwm->controller;
 
 	return devm_pwmchip_add(dev, &mvpwm->chip);
 }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] gpio-mvebu: no hardcoded timer assignment for pwm
  2024-01-30 10:55 [PATCH] gpio-mvebu: no hardcoded timer assignment for pwm Finn Behrens
@ 2024-02-06 11:43 ` Bartosz Golaszewski
  2024-02-07 20:52 ` Jannis
  2024-02-08  8:05 ` Uwe Kleine-König
  2 siblings, 0 replies; 4+ messages in thread
From: Bartosz Golaszewski @ 2024-02-06 11:43 UTC (permalink / raw)
  To: Finn Behrens
  Cc: Thierry Reding, Uwe Kleine-König, Linus Walleij, linux-pwm,
	linux-gpio, linux-kernel, Heisath, Yureka Lilian

On Tue, Jan 30, 2024 at 11:55 AM Finn Behrens <me@kloenk.de> wrote:
>
> Removes the hardcoded timer assignment of timers to pwm controllers.
> This allows to use more than one pwm per gpio bank.
>
> Original patch with chip_data interface by Heisath <jannis@imserv.org>
>
> Link: https://wiki.kobol.io/helios4/pwm/#patch-requirement
> Co-developed-by: Yureka Lilian <yuka@yuka.dev>
> Signed-off-by: Yureka Lilian <yuka@yuka.dev>
> Signed-off-by: Finn Behrens <me@kloenk.de>
> ---

This looks good to me but I'd love for Uwe to Ack it before I pick it up.

Bart

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] gpio-mvebu: no hardcoded timer assignment for pwm
  2024-01-30 10:55 [PATCH] gpio-mvebu: no hardcoded timer assignment for pwm Finn Behrens
  2024-02-06 11:43 ` Bartosz Golaszewski
@ 2024-02-07 20:52 ` Jannis
  2024-02-08  8:05 ` Uwe Kleine-König
  2 siblings, 0 replies; 4+ messages in thread
From: Jannis @ 2024-02-07 20:52 UTC (permalink / raw)
  To: Finn Behrens, Bartosz Golaszewski, Uwe Kleine-König
  Cc: Yureka Lilian, Linus Walleij, Thierry Reding, linux-pwm,
	linux-kernel, linux-gpio

Thanks for raising this patch. I have been adjusting it forever, would 
be great to get it in.
Looks good from my side too.


Am 30.01.2024 um 11:55 schrieb Finn Behrens:
> Removes the hardcoded timer assignment of timers to pwm controllers.
> This allows to use more than one pwm per gpio bank.
>
> Original patch with chip_data interface by Heisath <jannis@imserv.org>
>
> Link: https://wiki.kobol.io/helios4/pwm/#patch-requirement
> Co-developed-by: Yureka Lilian <yuka@yuka.dev>
> Signed-off-by: Yureka Lilian <yuka@yuka.dev>
> Signed-off-by: Finn Behrens <me@kloenk.de>
> ---
>   drivers/gpio/gpio-mvebu.c | 223 ++++++++++++++++++++++++--------------
>   1 file changed, 139 insertions(+), 84 deletions(-)
>
> diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c
> index a13f3c18ccd4..303ea3be0b69 100644
> --- a/drivers/gpio/gpio-mvebu.c
> +++ b/drivers/gpio/gpio-mvebu.c
> @@ -94,21 +94,43 @@
>   
>   #define MVEBU_MAX_GPIO_PER_BANK		32
>   
> -struct mvebu_pwm {
> +enum mvebu_pwm_ctrl {
> +	MVEBU_PWM_CTRL_SET_A = 0,
> +	MVEBU_PWM_CTRL_SET_B,
> +	MVEBU_PWM_CTRL_MAX
> +};
> +
> +struct mvebu_pwmchip {
>   	struct regmap		*regs;
>   	u32			 offset;
>   	unsigned long		 clk_rate;
> -	struct gpio_desc	*gpiod;
> -	struct pwm_chip		 chip;
>   	spinlock_t		 lock;
> -	struct mvebu_gpio_chip	*mvchip;
> +	bool			 in_use;
>   
>   	/* Used to preserve GPIO/PWM registers across suspend/resume */
> -	u32			 blink_select;
>   	u32			 blink_on_duration;
>   	u32			 blink_off_duration;
>   };
>   
> +struct mvebu_pwm_chip_drv {
> +	enum mvebu_pwm_ctrl	 ctrl;
> +	struct gpio_desc	*gpiod;
> +	bool			 master;
> +};
> +
> +struct mvebu_pwm {
> +	struct pwm_chip		 chip;
> +	struct mvebu_gpio_chip	*mvchip;
> +	struct mvebu_pwmchip	 controller;
> +	enum mvebu_pwm_ctrl	 default_controller;
> +
> +	/* Used to preserve GPIO/PWM registers across suspend/resume */
> +	u32				 blink_select;
> +	struct mvebu_pwm_chip_drv	 drv[];
> +};
> +
> +static struct mvebu_pwmchip  *mvebu_pwm_list[MVEBU_PWM_CTRL_MAX];
> +
>   struct mvebu_gpio_chip {
>   	struct gpio_chip   chip;
>   	struct regmap     *regs;
> @@ -285,12 +307,12 @@ mvebu_gpio_write_level_mask(struct mvebu_gpio_chip *mvchip, u32 val)
>    * Functions returning offsets of individual registers for a given
>    * PWM controller.
>    */
> -static unsigned int mvebu_pwmreg_blink_on_duration(struct mvebu_pwm *mvpwm)
> +static unsigned int mvebu_pwmreg_blink_on_duration(struct mvebu_pwmchip *mvpwm)
>   {
>   	return mvpwm->offset + PWM_BLINK_ON_DURATION_OFF;
>   }
>   
> -static unsigned int mvebu_pwmreg_blink_off_duration(struct mvebu_pwm *mvpwm)
> +static unsigned int mvebu_pwmreg_blink_off_duration(struct mvebu_pwmchip *mvpwm)
>   {
>   	return mvpwm->offset + PWM_BLINK_OFF_DURATION_OFF;
>   }
> @@ -623,39 +645,71 @@ static int mvebu_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
>   	struct mvebu_pwm *mvpwm = to_mvebu_pwm(chip);
>   	struct mvebu_gpio_chip *mvchip = mvpwm->mvchip;
>   	struct gpio_desc *desc;
> +	enum mvebu_pwm_ctrl id;
>   	unsigned long flags;
>   	int ret = 0;
> +	struct mvebu_pwm_chip_drv *drv = &mvpwm->drv[pwm->hwpwm];
>   
> -	spin_lock_irqsave(&mvpwm->lock, flags);
> +	spin_lock_irqsave(&mvpwm->controller.lock, flags);
>   
> -	if (mvpwm->gpiod) {
> +	if (drv->gpiod || (mvchip->blink_en_reg & BIT(pwm->hwpwm))) {
>   		ret = -EBUSY;
> -	} else {
> -		desc = gpiochip_request_own_desc(&mvchip->chip,
> -						 pwm->hwpwm, "mvebu-pwm",
> -						 GPIO_ACTIVE_HIGH,
> -						 GPIOD_OUT_LOW);
> -		if (IS_ERR(desc)) {
> -			ret = PTR_ERR(desc);
> -			goto out;
> -		}
> +		goto out;
> +	}
> +
> +	desc = gpiochip_request_own_desc(&mvchip->chip,
> +					 pwm->hwpwm, "mvebu-pwm",
> +					 GPIO_ACTIVE_HIGH,
> +					 GPIOD_OUT_LOW);
> +	if (IS_ERR(desc)) {
> +		ret = PTR_ERR(desc);
> +		goto out;
> +	}
>   
> -		mvpwm->gpiod = desc;
> +	ret = gpiod_direction_output(desc, 0);
> +	if (ret) {
> +		gpiochip_free_own_desc(desc);
> +		goto out;
>   	}
> +
> +	for (id = MVEBU_PWM_CTRL_SET_A; id < MVEBU_PWM_CTRL_MAX; id++) {
> +		if (!mvebu_pwm_list[id]->in_use) {
> +			drv->ctrl = id;
> +			drv->master = true;
> +			mvebu_pwm_list[id]->in_use = true;
> +			break;
> +		}
> +	}
> +
> +	if (!drv->master)
> +		drv->ctrl = mvpwm->default_controller;
> +
> +	regmap_update_bits(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset,
> +			   BIT(pwm->hwpwm), drv->ctrl ? BIT(pwm->hwpwm) : 0);
> +
> +	drv->gpiod = desc;
> +
> +	regmap_read(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset,
> +		    &mvpwm->blink_select);
>   out:
> -	spin_unlock_irqrestore(&mvpwm->lock, flags);
> +	spin_unlock_irqrestore(&mvpwm->controller.lock, flags);
>   	return ret;
>   }
>   
>   static void mvebu_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
>   {
>   	struct mvebu_pwm *mvpwm = to_mvebu_pwm(chip);
> +	struct mvebu_pwm_chip_drv *drv = &mvpwm->drv[pwm->hwpwm];
>   	unsigned long flags;
>   
> -	spin_lock_irqsave(&mvpwm->lock, flags);
> -	gpiochip_free_own_desc(mvpwm->gpiod);
> -	mvpwm->gpiod = NULL;
> -	spin_unlock_irqrestore(&mvpwm->lock, flags);
> +	spin_lock_irqsave(&mvpwm->controller.lock, flags);
> +	if (drv->master)
> +		mvebu_pwm_list[drv->ctrl]->in_use = false;
> +
> +	gpiochip_free_own_desc(drv->gpiod);
> +	memset(drv, 0, sizeof(struct mvebu_pwm_chip_drv));
> +
> +	spin_unlock_irqrestore(&mvpwm->controller.lock, flags);
>   }
>   
>   static int mvebu_pwm_get_state(struct pwm_chip *chip,
> @@ -665,28 +719,35 @@ static int mvebu_pwm_get_state(struct pwm_chip *chip,
>   
>   	struct mvebu_pwm *mvpwm = to_mvebu_pwm(chip);
>   	struct mvebu_gpio_chip *mvchip = mvpwm->mvchip;
> +	struct mvebu_pwm_chip_drv *drv = &mvpwm->drv[pwm->hwpwm];
> +	struct mvebu_pwmchip *controller;
>   	unsigned long long val;
>   	unsigned long flags;
>   	u32 u;
>   
> -	spin_lock_irqsave(&mvpwm->lock, flags);
> +	if (drv->gpiod)
> +		controller = mvebu_pwm_list[drv->ctrl];
> +	else
> +		controller = &mvpwm->controller;
> +
> +	spin_lock_irqsave(&controller->lock, flags);
>   
> -	regmap_read(mvpwm->regs, mvebu_pwmreg_blink_on_duration(mvpwm), &u);
> +	regmap_read(controller->regs, mvebu_pwmreg_blink_on_duration(controller), &u);
>   	/* Hardware treats zero as 2^32. See mvebu_pwm_apply(). */
>   	if (u > 0)
>   		val = u;
>   	else
>   		val = UINT_MAX + 1ULL;
>   	state->duty_cycle = DIV_ROUND_UP_ULL(val * NSEC_PER_SEC,
> -			mvpwm->clk_rate);
> +			controller->clk_rate);
>   
> -	regmap_read(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm), &u);
> +	regmap_read(controller->regs, mvebu_pwmreg_blink_off_duration(controller), &u);
>   	/* period = on + off duration */
>   	if (u > 0)
>   		val += u;
>   	else
>   		val += UINT_MAX + 1ULL;
> -	state->period = DIV_ROUND_UP_ULL(val * NSEC_PER_SEC, mvpwm->clk_rate);
> +	state->period = DIV_ROUND_UP_ULL(val * NSEC_PER_SEC, controller->clk_rate);
>   
>   	regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, &u);
>   	if (u)
> @@ -694,7 +755,7 @@ static int mvebu_pwm_get_state(struct pwm_chip *chip,
>   	else
>   		state->enabled = false;
>   
> -	spin_unlock_irqrestore(&mvpwm->lock, flags);
> +	spin_unlock_irqrestore(&controller->lock, flags);
>   
>   	return 0;
>   }
> @@ -703,6 +764,8 @@ static int mvebu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
>   			   const struct pwm_state *state)
>   {
>   	struct mvebu_pwm *mvpwm = to_mvebu_pwm(chip);
> +	struct mvebu_pwm_chip_drv *drv = &mvpwm->drv[pwm->hwpwm];
> +	struct mvebu_pwmchip *controller;
>   	struct mvebu_gpio_chip *mvchip = mvpwm->mvchip;
>   	unsigned long long val;
>   	unsigned long flags;
> @@ -711,7 +774,11 @@ static int mvebu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
>   	if (state->polarity != PWM_POLARITY_NORMAL)
>   		return -EINVAL;
>   
> -	val = (unsigned long long) mvpwm->clk_rate * state->duty_cycle;
> +	if (drv->gpiod)
> +		controller = mvebu_pwm_list[drv->ctrl];
> +	else
> +		controller = &mvpwm->controller;
> +	val = (unsigned long long) controller->clk_rate * state->duty_cycle;
>   	do_div(val, NSEC_PER_SEC);
>   	if (val > UINT_MAX + 1ULL)
>   		return -EINVAL;
> @@ -726,7 +793,7 @@ static int mvebu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
>   	else
>   		on = 1;
>   
> -	val = (unsigned long long) mvpwm->clk_rate * state->period;
> +	val = (unsigned long long) controller->clk_rate * state->period;
>   	do_div(val, NSEC_PER_SEC);
>   	val -= on;
>   	if (val > UINT_MAX + 1ULL)
> @@ -738,16 +805,16 @@ static int mvebu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
>   	else
>   		off = 1;
>   
> -	spin_lock_irqsave(&mvpwm->lock, flags);
> +	spin_lock_irqsave(&controller->lock, flags);
>   
> -	regmap_write(mvpwm->regs, mvebu_pwmreg_blink_on_duration(mvpwm), on);
> -	regmap_write(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm), off);
> +	regmap_write(controller->regs, mvebu_pwmreg_blink_on_duration(controller), on);
> +	regmap_write(controller->regs, mvebu_pwmreg_blink_off_duration(controller), off);
>   	if (state->enabled)
>   		mvebu_gpio_blink(&mvchip->chip, pwm->hwpwm, 1);
>   	else
>   		mvebu_gpio_blink(&mvchip->chip, pwm->hwpwm, 0);
>   
> -	spin_unlock_irqrestore(&mvpwm->lock, flags);
> +	spin_unlock_irqrestore(&controller->lock, flags);
>   
>   	return 0;
>   }
> @@ -762,25 +829,27 @@ static const struct pwm_ops mvebu_pwm_ops = {
>   static void __maybe_unused mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip)
>   {
>   	struct mvebu_pwm *mvpwm = mvchip->mvpwm;
> +	struct mvebu_pwmchip *controller = &mvpwm->controller;
>   
>   	regmap_read(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset,
>   		    &mvpwm->blink_select);
> -	regmap_read(mvpwm->regs, mvebu_pwmreg_blink_on_duration(mvpwm),
> -		    &mvpwm->blink_on_duration);
> -	regmap_read(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm),
> -		    &mvpwm->blink_off_duration);
> +	regmap_read(controller->regs, mvebu_pwmreg_blink_on_duration(controller),
> +		    &controller->blink_on_duration);
> +	regmap_read(controller->regs, mvebu_pwmreg_blink_off_duration(controller),
> +		    &controller->blink_off_duration);
>   }
>   
>   static void __maybe_unused mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip)
>   {
>   	struct mvebu_pwm *mvpwm = mvchip->mvpwm;
> +	struct mvebu_pwmchip *controller = &mvpwm->controller;
>   
>   	regmap_write(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset,
>   		     mvpwm->blink_select);
> -	regmap_write(mvpwm->regs, mvebu_pwmreg_blink_on_duration(mvpwm),
> -		     mvpwm->blink_on_duration);
> -	regmap_write(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm),
> -		     mvpwm->blink_off_duration);
> +	regmap_write(controller->regs, mvebu_pwmreg_blink_on_duration(controller),
> +		     controller->blink_on_duration);
> +	regmap_write(controller->regs, mvebu_pwmreg_blink_off_duration(controller),
> +		     controller->blink_off_duration);
>   }
>   
>   static int mvebu_pwm_probe(struct platform_device *pdev,
> @@ -792,6 +861,7 @@ static int mvebu_pwm_probe(struct platform_device *pdev,
>   	void __iomem *base;
>   	u32 offset;
>   	u32 set;
> +	enum mvebu_pwm_ctrl ctrl_set;
>   
>   	if (mvchip->soc_variant == MVEBU_GPIO_SOC_VARIANT_A8K) {
>   		int ret = of_property_read_u32(dev->of_node,
> @@ -813,57 +883,39 @@ static int mvebu_pwm_probe(struct platform_device *pdev,
>   	if (IS_ERR(mvchip->clk))
>   		return PTR_ERR(mvchip->clk);
>   
> -	mvpwm = devm_kzalloc(dev, sizeof(struct mvebu_pwm), GFP_KERNEL);
> +	mvpwm = devm_kzalloc(dev, struct_size(mvpwm, drv, mvchip->chip.ngpio), GFP_KERNEL);
>   	if (!mvpwm)
>   		return -ENOMEM;
>   	mvchip->mvpwm = mvpwm;
>   	mvpwm->mvchip = mvchip;
> -	mvpwm->offset = offset;
>   
> -	if (mvchip->soc_variant == MVEBU_GPIO_SOC_VARIANT_A8K) {
> -		mvpwm->regs = mvchip->regs;
> +	base = devm_platform_ioremap_resource_byname(pdev, "pwm");
> +	if (IS_ERR(base))
> +		return PTR_ERR(base);
> +	mvpwm->controller.regs = devm_regmap_init_mmio(&pdev->dev, base,
> +						       &mvebu_gpio_regmap_config);
> +	if (IS_ERR(mvpwm->controller.regs))
> +		return PTR_ERR(mvpwm->controller.regs);
>   
> -		switch (mvchip->offset) {
> -		case AP80X_GPIO0_OFF_A8K:
> -		case CP11X_GPIO0_OFF_A8K:
> -			/* Blink counter A */
> -			set = 0;
> -			break;
> -		case CP11X_GPIO1_OFF_A8K:
> -			/* Blink counter B */
> -			set = U32_MAX;
> -			mvpwm->offset += PWM_BLINK_COUNTER_B_OFF;
> -			break;
> -		default:
> -			return -EINVAL;
> -		}
> +	/*
> +	 * User set A for lines of GPIO chip with id 0, B for GPIO chip
> +	 * with id 1. Don't allow further GPIO chips to be used for PWM.
> +	 */
> +	if (id == 0) {
> +		set = 0;
> +		ctrl_set = MVEBU_PWM_CTRL_SET_A;
> +	} else if (id == 1) {
> +		set = U32_MAX;
> +		ctrl_set = MVEBU_PWM_CTRL_SET_B;
>   	} else {
> -		base = devm_platform_ioremap_resource_byname(pdev, "pwm");
> -		if (IS_ERR(base))
> -			return PTR_ERR(base);
> -
> -		mvpwm->regs = devm_regmap_init_mmio(&pdev->dev, base,
> -						    &mvebu_gpio_regmap_config);
> -		if (IS_ERR(mvpwm->regs))
> -			return PTR_ERR(mvpwm->regs);
> -
> -		/*
> -		 * Use set A for lines of GPIO chip with id 0, B for GPIO chip
> -		 * with id 1. Don't allow further GPIO chips to be used for PWM.
> -		 */
> -		if (id == 0)
> -			set = 0;
> -		else if (id == 1)
> -			set = U32_MAX;
> -		else
> -			return -EINVAL;
> +		return -EINVAL;
>   	}
>   
>   	regmap_write(mvchip->regs,
>   		     GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, set);
>   
> -	mvpwm->clk_rate = clk_get_rate(mvchip->clk);
> -	if (!mvpwm->clk_rate) {
> +	mvpwm->controller.clk_rate = clk_get_rate(mvchip->clk);
> +	if (!mvpwm->controller.clk_rate) {
>   		dev_err(dev, "failed to get clock rate\n");
>   		return -EINVAL;
>   	}
> @@ -872,7 +924,10 @@ static int mvebu_pwm_probe(struct platform_device *pdev,
>   	mvpwm->chip.ops = &mvebu_pwm_ops;
>   	mvpwm->chip.npwm = mvchip->chip.ngpio;
>   
> -	spin_lock_init(&mvpwm->lock);
> +	spin_lock_init(&mvpwm->controller.lock);
> +
> +	mvpwm->default_controller = ctrl_set;
> +	mvebu_pwm_list[ctrl_set] = &mvpwm->controller;
>   
>   	return devm_pwmchip_add(dev, &mvpwm->chip);
>   }


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] gpio-mvebu: no hardcoded timer assignment for pwm
  2024-01-30 10:55 [PATCH] gpio-mvebu: no hardcoded timer assignment for pwm Finn Behrens
  2024-02-06 11:43 ` Bartosz Golaszewski
  2024-02-07 20:52 ` Jannis
@ 2024-02-08  8:05 ` Uwe Kleine-König
  2 siblings, 0 replies; 4+ messages in thread
From: Uwe Kleine-König @ 2024-02-08  8:05 UTC (permalink / raw)
  To: Finn Behrens
  Cc: Thierry Reding, Linus Walleij, Bartosz Golaszewski, linux-pwm,
	linux-gpio, linux-kernel, Heisath, Yureka Lilian

[-- Attachment #1: Type: text/plain, Size: 3565 bytes --]

Hello,

On Tue, Jan 30, 2024 at 11:55:13AM +0100, Finn Behrens wrote:
> Removes the hardcoded timer assignment of timers to pwm controllers.
> This allows to use more than one pwm per gpio bank.
> 
> Original patch with chip_data interface by Heisath <jannis@imserv.org>
> 
> Link: https://wiki.kobol.io/helios4/pwm/#patch-requirement
> Co-developed-by: Yureka Lilian <yuka@yuka.dev>
> Signed-off-by: Yureka Lilian <yuka@yuka.dev>
> Signed-off-by: Finn Behrens <me@kloenk.de>

I find this patch hard to understand and I hope it's more complicated
than it could be. I wonder if it would be beneficial to split this patch
in two. In the first patch just introduce the new structures with all
the necessary renaming and only in the second patch implement the added
flexibility.

Some more details below.

>  drivers/gpio/gpio-mvebu.c | 223 ++++++++++++++++++++++++--------------
>  1 file changed, 139 insertions(+), 84 deletions(-)
> 
> diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c
> index a13f3c18ccd4..303ea3be0b69 100644
> --- a/drivers/gpio/gpio-mvebu.c
> +++ b/drivers/gpio/gpio-mvebu.c
> @@ -94,21 +94,43 @@
>  
>  #define MVEBU_MAX_GPIO_PER_BANK		32
>  
> -struct mvebu_pwm {
> +enum mvebu_pwm_ctrl {
> +	MVEBU_PWM_CTRL_SET_A = 0,
> +	MVEBU_PWM_CTRL_SET_B,
> +	MVEBU_PWM_CTRL_MAX
> +};
> +
> +struct mvebu_pwmchip {
>  	struct regmap		*regs;
>  	u32			 offset;
>  	unsigned long		 clk_rate;
> -	struct gpio_desc	*gpiod;
> -	struct pwm_chip		 chip;
>  	spinlock_t		 lock;
> -	struct mvebu_gpio_chip	*mvchip;
> +	bool			 in_use;
>  
>  	/* Used to preserve GPIO/PWM registers across suspend/resume */
> -	u32			 blink_select;
>  	u32			 blink_on_duration;
>  	u32			 blink_off_duration;
>  };
>  
> +struct mvebu_pwm_chip_drv {
> +	enum mvebu_pwm_ctrl	 ctrl;
> +	struct gpio_desc	*gpiod;
> +	bool			 master;
> +};
> +
> +struct mvebu_pwm {
> +	struct pwm_chip		 chip;
> +	struct mvebu_gpio_chip	*mvchip;
> +	struct mvebu_pwmchip	 controller;
> +	enum mvebu_pwm_ctrl	 default_controller;
> +
> +	/* Used to preserve GPIO/PWM registers across suspend/resume */
> +	u32				 blink_select;
> +	struct mvebu_pwm_chip_drv	 drv[];
> +};

So we have three different structures related to pwm. Some highlevel
description (in a comment or at least the commit log) about how the
hardware works and which struct describes what would be helpful. I gave
up after 15 min of reading this patch and trying to understand it.

> +static struct mvebu_pwmchip  *mvebu_pwm_list[MVEBU_PWM_CTRL_MAX];

Huh, a static variable. Does that mean we can only have one mvebu_gpio
device?

> +
>  struct mvebu_gpio_chip {
>  	struct gpio_chip   chip;
>  	struct regmap     *regs;
> @@ -285,12 +307,12 @@ mvebu_gpio_write_level_mask(struct mvebu_gpio_chip *mvchip, u32 val)
>   * Functions returning offsets of individual registers for a given
>   * PWM controller.
>   */
> -static unsigned int mvebu_pwmreg_blink_on_duration(struct mvebu_pwm *mvpwm)
> +static unsigned int mvebu_pwmreg_blink_on_duration(struct mvebu_pwmchip *mvpwm)

I'm a fan of picking always the same variable name for the same thing
and different names for different things. "mvpwm" is used for variables
of type struct mvebu_pwmchip and struct mvebu_pwm.

>  {
>  	return mvpwm->offset + PWM_BLINK_ON_DURATION_OFF;
>  }

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | https://www.pengutronix.de/ |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2024-02-08  8:05 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-01-30 10:55 [PATCH] gpio-mvebu: no hardcoded timer assignment for pwm Finn Behrens
2024-02-06 11:43 ` Bartosz Golaszewski
2024-02-07 20:52 ` Jannis
2024-02-08  8:05 ` Uwe Kleine-König

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).