From e4728fcf779c37d1bcbd4b6505c9b40d4bb9ff48 Mon Sep 17 00:00:00 2001 From: Heisath Date: Mon, 22 Feb 2021 12:24:54 +0100 Subject: [PATCH] Removes the hardcoded timer assignment of timers to pwm controllers This allows to use more than one pwm per gpio bank. Original patch by helios4 team, updated to work on LK5.11+ Signed-off-by: Heisath --- drivers/gpio/gpio-mvebu.c | 198 +++++++++++++++++++++++++++----------- 1 file changed, 144 insertions(+), 54 deletions(-) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 3a19b4140..195b685de 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -92,20 +92,41 @@ #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; - unsigned long clk_rate; + unsigned long clk_rate; + spinlock_t lock; + bool in_use; + + /* Used to preserve GPIO/PWM registers across suspend/resume */ + u32 blink_on_duration; + u32 blink_off_duration; +}; + +struct mvebu_pwm_chip_drv { + enum mvebu_pwm_ctrl ctrl; struct gpio_desc *gpiod; - struct pwm_chip chip; - spinlock_t lock; + bool master; +}; + +struct mvebu_pwm { + struct pwm_chip chip; struct mvebu_gpio_chip *mvchip; + struct mvebu_pwmchip controller; + enum mvebu_pwm_ctrl default_counter; /* Used to preserve GPIO/PWM registers across suspend/resume */ u32 blink_select; - u32 blink_on_duration; - u32 blink_off_duration; }; +static struct mvebu_pwmchip *mvebu_pwm_list[MVEBU_PWM_CTRL_MAX]; + struct mvebu_gpio_chip { struct gpio_chip chip; struct regmap *regs; @@ -282,12 +303,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 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 PWM_BLINK_OFF_DURATION_OFF; } @@ -647,39 +668,84 @@ 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 *chip_data; + + spin_lock_irqsave(&mvpwm->controller.lock, flags); - spin_lock_irqsave(&mvpwm->lock, flags); + regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, + &mvchip->blink_en_reg); - if (mvpwm->gpiod) { + if (pwm->chip_data || (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; + } - mvpwm->gpiod = desc; + 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; + } + + ret = gpiod_direction_output(desc, 0); + if (ret) { + gpiochip_free_own_desc(desc); + goto out; } + + chip_data = kzalloc(sizeof(struct mvebu_pwm_chip_drv), GFP_KERNEL); + if (!chip_data) { + gpiochip_free_own_desc(desc); + ret = -ENOMEM; + goto out; + } + + for (id = MVEBU_PWM_CTRL_SET_A; id < MVEBU_PWM_CTRL_MAX; id++) { + if (!mvebu_pwm_list[id]->in_use) { + chip_data->ctrl = id; + chip_data->master = true; + mvebu_pwm_list[id]->in_use = true; + break; + } + } + + if (!chip_data->master) + chip_data->ctrl = mvpwm->default_counter; + + regmap_update_bits(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, + BIT(pwm->hwpwm), chip_data->ctrl ? BIT(pwm->hwpwm) : 0); + + chip_data->gpiod = desc; + pwm->chip_data = chip_data; + + 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 *chip_data = (struct mvebu_pwm_chip_drv*) pwm->chip_data; 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 (chip_data->master) + mvebu_pwm_list[chip_data->ctrl]->in_use = false; + + gpiochip_free_own_desc(chip_data->gpiod); + kfree(chip_data); + pwm->chip_data = NULL; + spin_unlock_irqrestore(&mvpwm->controller.lock, flags); } static void mvebu_pwm_get_state(struct pwm_chip *chip, @@ -687,16 +753,23 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip, struct pwm_state *state) { struct mvebu_pwm *mvpwm = to_mvebu_pwm(chip); + struct mvebu_pwm_chip_drv *chip_data = (struct mvebu_pwm_chip_drv*) pwm->chip_data; + struct mvebu_pwmchip *controller; struct mvebu_gpio_chip *mvchip = mvpwm->mvchip; unsigned long long val; unsigned long flags; u32 u; - spin_lock_irqsave(&mvpwm->lock, flags); + if (chip_data) + controller = mvebu_pwm_list[chip_data->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); val = (unsigned long long) u * NSEC_PER_SEC; - do_div(val, mvpwm->clk_rate); + do_div(val, controller->clk_rate); if (val > UINT_MAX) state->duty_cycle = UINT_MAX; else if (val) @@ -705,10 +778,10 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip, state->duty_cycle = 1; val = (unsigned long long) u; /* on duration */ - regmap_read(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm), &u); + regmap_read(controller->regs, mvebu_pwmreg_blink_off_duration(controller), &u); val += (unsigned long long) u; /* period = on + off duration */ val *= NSEC_PER_SEC; - do_div(val, mvpwm->clk_rate); + do_div(val, controller->clk_rate); if (val > UINT_MAX) state->period = UINT_MAX; else if (val) @@ -722,19 +795,27 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip, else state->enabled = false; - spin_unlock_irqrestore(&mvpwm->lock, flags); + spin_unlock_irqrestore(&controller->lock, flags); } 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 *chip_data = (struct mvebu_pwm_chip_drv*) pwm->chip_data; + struct mvebu_pwmchip *controller; struct mvebu_gpio_chip *mvchip = mvpwm->mvchip; unsigned long long val; unsigned long flags; unsigned int on, off; - val = (unsigned long long) mvpwm->clk_rate * state->duty_cycle; + if (chip_data) + controller = mvebu_pwm_list[chip_data->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) return -EINVAL; @@ -743,7 +824,7 @@ static int mvebu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, else on = 1; - val = (unsigned long long) mvpwm->clk_rate * + val = (unsigned long long) controller->clk_rate * (state->period - state->duty_cycle); do_div(val, NSEC_PER_SEC); if (val > UINT_MAX) @@ -753,16 +834,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; } @@ -778,25 +859,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, @@ -807,6 +890,7 @@ static int mvebu_pwm_probe(struct platform_device *pdev, struct mvebu_pwm *mvpwm; void __iomem *base; u32 set; + enum mvebu_pwm_ctrl ctrl_set; if (!of_device_is_compatible(mvchip->chip.of_node, "marvell,armada-370-gpio")) @@ -828,12 +912,16 @@ static int mvebu_pwm_probe(struct platform_device *pdev, * 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) + if (id == 0) { set = 0; - else if (id == 1) + ctrl_set = MVEBU_PWM_CTRL_SET_A; + } else if (id == 1) { set = U32_MAX; - else + ctrl_set = MVEBU_PWM_CTRL_SET_B; + } else { return -EINVAL; + } + regmap_write(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, set); @@ -847,13 +935,13 @@ static int mvebu_pwm_probe(struct platform_device *pdev, if (IS_ERR(base)) return PTR_ERR(base); - mvpwm->regs = devm_regmap_init_mmio(&pdev->dev, base, + mvpwm->controller.regs = devm_regmap_init_mmio(&pdev->dev, base, &mvebu_gpio_regmap_config); - if (IS_ERR(mvpwm->regs)) - return PTR_ERR(mvpwm->regs); + if (IS_ERR(mvpwm->controller.regs)) + return PTR_ERR(mvpwm->controller.regs); - 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; } @@ -869,7 +957,9 @@ static int mvebu_pwm_probe(struct platform_device *pdev, */ mvpwm->chip.base = -1; - spin_lock_init(&mvpwm->lock); + spin_lock_init(&mvpwm->controller.lock); + mvpwm->default_counter = ctrl_set; + mvebu_pwm_list[ctrl_set] = &mvpwm->controller; return pwmchip_add(&mvpwm->chip); } -- Created with Armbian build tools https://github.com/armbian/build