From nobody Wed Dec 24 01:51:54 2025 Received: from gimli.kloenk.de (gimli.kloenk.de [49.12.72.200]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7BD6A664D5; Tue, 30 Jan 2024 11:04:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=49.12.72.200 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706612692; cv=none; b=o9c4RzQmjlyOIYAg7Esx1DbcCJYU6zLCZB69kF1l2T3lrXZE2YH5Vf0shzsNQN0NOXJM+DnCn/5N26u96DtBbS2RfWV1mLle/CI0cyS9KMiYL0jLk+AjfCwGHL7EvRvuBLqkhAriUvx9AjZ++dVNdbrEVsEOGpXVt0QoXotpZJk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706612692; c=relaxed/simple; bh=xAazWK1TBwRgBwPDjmqMpw58fFQdD/vhCbBzfIdNgnE=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=pskkLEUE+V1UCbeOMwiiuGFD0d0aAPfxgrD4V+BLAjHqaFy08lRapPtLdbwxwMYZAV03LlJtRj75jgUKzUmiIENyibtLDKEuc0tfkP9BLRfI3M9yIJ6W2rcdOdwE4DomO1hKkWOmIQU4zSVpC6WVRIEXFURvC0vXCed5u4fKLbI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=kloenk.de; spf=pass smtp.mailfrom=kloenk.de; dkim=pass (1024-bit key) header.d=kloenk.de header.i=@kloenk.de header.b=WBygx/4t; arc=none smtp.client-ip=49.12.72.200 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=kloenk.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=kloenk.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=kloenk.de header.i=@kloenk.de header.b="WBygx/4t" From: Finn Behrens DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kloenk.de; s=mail; t=1706612151; bh=xJvrGE3xhE9lJyWcs/KsWV4ng9+Xpu8qmEntGeVtCtY=; h=From:To:Cc:Subject:Date; b=WBygx/4txnwJhHvnKKVa6OueTkZF5GvGMEK8HV51MM3ePQvHF2KMV+017p22Gmo37 47ZPb1m0UUzGpV8rfnmYXqioRHAe3Y64Bd2RWtjrOSzXCF4m17kkXTV6Jfpc5bQfSY ipg6uAPjwC5yEJo4uoLcTW90J1EKyW1kHENL8c3E= To: Thierry Reding , =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= , Linus Walleij , Bartosz Golaszewski , linux-pwm@vger.kernel.org, linux-gpio@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Finn Behrens , Heisath , Yureka Lilian Subject: [PATCH] gpio-mvebu: no hardcoded timer assignment for pwm Date: Tue, 30 Jan 2024 11:55:13 +0100 Message-ID: <20240130105515.30258-1-me@kloenk.de> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 Link: https://wiki.kobol.io/helios4/pwm/#patch-requirement Co-developed-by: Yureka Lilian Signed-off-by: Yureka Lilian Signed-off-by: Finn Behrens --- 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 @@ =20 #define MVEBU_MAX_GPIO_PER_BANK 32 =20 -struct mvebu_pwm { +enum mvebu_pwm_ctrl { + MVEBU_PWM_CTRL_SET_A =3D 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; =20 /* Used to preserve GPIO/PWM registers across suspend/resume */ - u32 blink_select; u32 blink_on_duration; u32 blink_off_duration; }; =20 +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 *m= vchip, 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 *m= vpwm) { return mvpwm->offset + PWM_BLINK_ON_DURATION_OFF; } =20 -static unsigned int mvebu_pwmreg_blink_off_duration(struct mvebu_pwm *mvpw= m) +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, s= truct pwm_device *pwm) struct mvebu_pwm *mvpwm =3D to_mvebu_pwm(chip); struct mvebu_gpio_chip *mvchip =3D mvpwm->mvchip; struct gpio_desc *desc; + enum mvebu_pwm_ctrl id; unsigned long flags; int ret =3D 0; + struct mvebu_pwm_chip_drv *drv =3D &mvpwm->drv[pwm->hwpwm]; =20 - spin_lock_irqsave(&mvpwm->lock, flags); + spin_lock_irqsave(&mvpwm->controller.lock, flags); =20 - if (mvpwm->gpiod) { + if (drv->gpiod || (mvchip->blink_en_reg & BIT(pwm->hwpwm))) { ret =3D -EBUSY; - } else { - desc =3D gpiochip_request_own_desc(&mvchip->chip, - pwm->hwpwm, "mvebu-pwm", - GPIO_ACTIVE_HIGH, - GPIOD_OUT_LOW); - if (IS_ERR(desc)) { - ret =3D PTR_ERR(desc); - goto out; - } + goto out; + } + + desc =3D gpiochip_request_own_desc(&mvchip->chip, + pwm->hwpwm, "mvebu-pwm", + GPIO_ACTIVE_HIGH, + GPIOD_OUT_LOW); + if (IS_ERR(desc)) { + ret =3D PTR_ERR(desc); + goto out; + } =20 - mvpwm->gpiod =3D desc; + ret =3D gpiod_direction_output(desc, 0); + if (ret) { + gpiochip_free_own_desc(desc); + goto out; } + + for (id =3D MVEBU_PWM_CTRL_SET_A; id < MVEBU_PWM_CTRL_MAX; id++) { + if (!mvebu_pwm_list[id]->in_use) { + drv->ctrl =3D id; + drv->master =3D true; + mvebu_pwm_list[id]->in_use =3D true; + break; + } + } + + if (!drv->master) + drv->ctrl =3D mvpwm->default_controller; + + regmap_update_bits(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offs= et, + BIT(pwm->hwpwm), drv->ctrl ? BIT(pwm->hwpwm) : 0); + + drv->gpiod =3D 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; } =20 static void mvebu_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) { struct mvebu_pwm *mvpwm =3D to_mvebu_pwm(chip); + struct mvebu_pwm_chip_drv *drv =3D &mvpwm->drv[pwm->hwpwm]; unsigned long flags; =20 - spin_lock_irqsave(&mvpwm->lock, flags); - gpiochip_free_own_desc(mvpwm->gpiod); - mvpwm->gpiod =3D NULL; - spin_unlock_irqrestore(&mvpwm->lock, flags); + spin_lock_irqsave(&mvpwm->controller.lock, flags); + if (drv->master) + mvebu_pwm_list[drv->ctrl]->in_use =3D false; + + gpiochip_free_own_desc(drv->gpiod); + memset(drv, 0, sizeof(struct mvebu_pwm_chip_drv)); + + spin_unlock_irqrestore(&mvpwm->controller.lock, flags); } =20 static int mvebu_pwm_get_state(struct pwm_chip *chip, @@ -665,28 +719,35 @@ static int mvebu_pwm_get_state(struct pwm_chip *chip, =20 struct mvebu_pwm *mvpwm =3D to_mvebu_pwm(chip); struct mvebu_gpio_chip *mvchip =3D mvpwm->mvchip; + struct mvebu_pwm_chip_drv *drv =3D &mvpwm->drv[pwm->hwpwm]; + struct mvebu_pwmchip *controller; unsigned long long val; unsigned long flags; u32 u; =20 - spin_lock_irqsave(&mvpwm->lock, flags); + if (drv->gpiod) + controller =3D mvebu_pwm_list[drv->ctrl]; + else + controller =3D &mvpwm->controller; + + spin_lock_irqsave(&controller->lock, flags); =20 - 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 =3D u; else val =3D UINT_MAX + 1ULL; state->duty_cycle =3D DIV_ROUND_UP_ULL(val * NSEC_PER_SEC, - mvpwm->clk_rate); + controller->clk_rate); =20 - regmap_read(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm), &u); + regmap_read(controller->regs, mvebu_pwmreg_blink_off_duration(controller)= , &u); /* period =3D on + off duration */ if (u > 0) val +=3D u; else val +=3D UINT_MAX + 1ULL; - state->period =3D DIV_ROUND_UP_ULL(val * NSEC_PER_SEC, mvpwm->clk_rate); + state->period =3D DIV_ROUND_UP_ULL(val * NSEC_PER_SEC, controller->clk_ra= te); =20 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 =3D false; =20 - spin_unlock_irqrestore(&mvpwm->lock, flags); + spin_unlock_irqrestore(&controller->lock, flags); =20 return 0; } @@ -703,6 +764,8 @@ static int mvebu_pwm_apply(struct pwm_chip *chip, struc= t pwm_device *pwm, const struct pwm_state *state) { struct mvebu_pwm *mvpwm =3D to_mvebu_pwm(chip); + struct mvebu_pwm_chip_drv *drv =3D &mvpwm->drv[pwm->hwpwm]; + struct mvebu_pwmchip *controller; struct mvebu_gpio_chip *mvchip =3D mvpwm->mvchip; unsigned long long val; unsigned long flags; @@ -711,7 +774,11 @@ static int mvebu_pwm_apply(struct pwm_chip *chip, stru= ct pwm_device *pwm, if (state->polarity !=3D PWM_POLARITY_NORMAL) return -EINVAL; =20 - val =3D (unsigned long long) mvpwm->clk_rate * state->duty_cycle; + if (drv->gpiod) + controller =3D mvebu_pwm_list[drv->ctrl]; + else + controller =3D &mvpwm->controller; + val =3D (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, struc= t pwm_device *pwm, else on =3D 1; =20 - val =3D (unsigned long long) mvpwm->clk_rate * state->period; + val =3D (unsigned long long) controller->clk_rate * state->period; do_div(val, NSEC_PER_SEC); val -=3D on; if (val > UINT_MAX + 1ULL) @@ -738,16 +805,16 @@ static int mvebu_pwm_apply(struct pwm_chip *chip, str= uct pwm_device *pwm, else off =3D 1; =20 - spin_lock_irqsave(&mvpwm->lock, flags); + spin_lock_irqsave(&controller->lock, flags); =20 - 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); =20 - spin_unlock_irqrestore(&mvpwm->lock, flags); + spin_unlock_irqrestore(&controller->lock, flags); =20 return 0; } @@ -762,25 +829,27 @@ static const struct pwm_ops mvebu_pwm_ops =3D { static void __maybe_unused mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchi= p) { struct mvebu_pwm *mvpwm =3D mvchip->mvpwm; + struct mvebu_pwmchip *controller =3D &mvpwm->controller; =20 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); } =20 static void __maybe_unused mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip) { struct mvebu_pwm *mvpwm =3D mvchip->mvpwm; + struct mvebu_pwmchip *controller =3D &mvpwm->controller; =20 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); } =20 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; =20 if (mvchip->soc_variant =3D=3D MVEBU_GPIO_SOC_VARIANT_A8K) { int ret =3D of_property_read_u32(dev->of_node, @@ -813,57 +883,39 @@ static int mvebu_pwm_probe(struct platform_device *pd= ev, if (IS_ERR(mvchip->clk)) return PTR_ERR(mvchip->clk); =20 - mvpwm =3D devm_kzalloc(dev, sizeof(struct mvebu_pwm), GFP_KERNEL); + mvpwm =3D devm_kzalloc(dev, struct_size(mvpwm, drv, mvchip->chip.ngpio), = GFP_KERNEL); if (!mvpwm) return -ENOMEM; mvchip->mvpwm =3D mvpwm; mvpwm->mvchip =3D mvchip; - mvpwm->offset =3D offset; =20 - if (mvchip->soc_variant =3D=3D MVEBU_GPIO_SOC_VARIANT_A8K) { - mvpwm->regs =3D mvchip->regs; + base =3D devm_platform_ioremap_resource_byname(pdev, "pwm"); + if (IS_ERR(base)) + return PTR_ERR(base); + mvpwm->controller.regs =3D devm_regmap_init_mmio(&pdev->dev, base, + &mvebu_gpio_regmap_config); + if (IS_ERR(mvpwm->controller.regs)) + return PTR_ERR(mvpwm->controller.regs); =20 - switch (mvchip->offset) { - case AP80X_GPIO0_OFF_A8K: - case CP11X_GPIO0_OFF_A8K: - /* Blink counter A */ - set =3D 0; - break; - case CP11X_GPIO1_OFF_A8K: - /* Blink counter B */ - set =3D U32_MAX; - mvpwm->offset +=3D 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 =3D=3D 0) { + set =3D 0; + ctrl_set =3D MVEBU_PWM_CTRL_SET_A; + } else if (id =3D=3D 1) { + set =3D U32_MAX; + ctrl_set =3D MVEBU_PWM_CTRL_SET_B; } else { - base =3D devm_platform_ioremap_resource_byname(pdev, "pwm"); - if (IS_ERR(base)) - return PTR_ERR(base); - - mvpwm->regs =3D 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 =3D=3D 0) - set =3D 0; - else if (id =3D=3D 1) - set =3D U32_MAX; - else - return -EINVAL; + return -EINVAL; } =20 regmap_write(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, set); =20 - mvpwm->clk_rate =3D clk_get_rate(mvchip->clk); - if (!mvpwm->clk_rate) { + mvpwm->controller.clk_rate =3D 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 *pde= v, mvpwm->chip.ops =3D &mvebu_pwm_ops; mvpwm->chip.npwm =3D mvchip->chip.ngpio; =20 - spin_lock_init(&mvpwm->lock); + spin_lock_init(&mvpwm->controller.lock); + + mvpwm->default_controller =3D ctrl_set; + mvebu_pwm_list[ctrl_set] =3D &mvpwm->controller; =20 return devm_pwmchip_add(dev, &mvpwm->chip); } --=20 2.43.0