diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 45c65f8..2ec4a15 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -93,20 +93,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 { void __iomem *membase; 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; + bool master; +}; + +struct mvebu_pwm { struct pwm_chip chip; - spinlock_t lock; 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; @@ -283,12 +304,12 @@ mvebu_gpio_write_level_mask(struct mvebu_gpio_chip *mvchip, u32 val) * Functions returning addresses of individual registers for a given * PWM controller. */ -static void __iomem *mvebu_pwmreg_blink_on_duration(struct mvebu_pwm *mvpwm) +static void __iomem *mvebu_pwmreg_blink_on_duration(struct mvebu_pwmchip *mvpwm) { return mvpwm->membase + PWM_BLINK_ON_DURATION_OFF; } -static void __iomem *mvebu_pwmreg_blink_off_duration(struct mvebu_pwm *mvpwm) +static void __iomem *mvebu_pwmreg_blink_off_duration(struct mvebu_pwmchip *mvpwm) { return mvpwm->membase + PWM_BLINK_OFF_DURATION_OFF; } @@ -600,46 +621,80 @@ 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->lock, flags); + spin_lock_irqsave(&mvpwm->controller.lock, flags); - if (mvpwm->gpiod) { - ret = -EBUSY; - } else { - desc = gpio_to_desc(mvchip->chip.base + pwm->hwpwm); - if (!desc) { - ret = -ENODEV; - goto out; - } + regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, + &mvchip->blink_en_reg); + if (pwm->chip_data || (mvchip->blink_en_reg & BIT(pwm->hwpwm))) + return -EBUSY; - ret = gpiod_request(desc, "mvebu-pwm"); - if (ret) - goto out; + desc = gpio_to_desc(mvchip->chip.base + pwm->hwpwm); + if (!desc) { + ret = -ENODEV; + goto out; + } - ret = gpiod_direction_output(desc, 0); - if (ret) { - gpiod_free(desc); - goto out; - } + ret = gpiod_request(desc, "mvebu-pwm"); + if (ret) + goto out; - mvpwm->gpiod = desc; + ret = gpiod_direction_output(desc, 0); + if (ret) { + gpiod_free(desc); + goto out; } + + chip_data = kzalloc(sizeof(struct mvebu_pwm_chip_drv), GFP_KERNEL); + if (!chip_data) { + gpiod_free(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); - gpiod_free(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; + + gpiod_free(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, @@ -647,17 +702,24 @@ 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); val = (unsigned long long) - readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm)); + readl_relaxed(mvebu_pwmreg_blink_on_duration(controller)); val *= 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) @@ -666,9 +728,9 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip, state->duty_cycle = 1; val = (unsigned long long) - readl_relaxed(mvebu_pwmreg_blink_off_duration(mvpwm)); + readl_relaxed(mvebu_pwmreg_blink_off_duration(controller)); val *= NSEC_PER_SEC; - do_div(val, mvpwm->clk_rate); + do_div(val, controller->clk_rate); if (val < state->duty_cycle) { state->period = 1; } else { @@ -687,19 +749,21 @@ 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, 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 = mvebu_pwm_list[chip_data->ctrl]; 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; + val = (unsigned long long) controller->clk_rate * state->duty_cycle; do_div(val, NSEC_PER_SEC); if (val > UINT_MAX) return -EINVAL; @@ -708,7 +772,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) @@ -718,16 +782,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); - writel_relaxed(on, mvebu_pwmreg_blink_on_duration(mvpwm)); - writel_relaxed(off, mvebu_pwmreg_blink_off_duration(mvpwm)); + writel_relaxed(on, mvebu_pwmreg_blink_on_duration(controller)); + writel_relaxed(off, mvebu_pwmreg_blink_off_duration(controller)); 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; } @@ -746,10 +810,10 @@ static void __maybe_unused mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip) regmap_read(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, &mvpwm->blink_select); - mvpwm->blink_on_duration = - readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm)); - mvpwm->blink_off_duration = - readl_relaxed(mvebu_pwmreg_blink_off_duration(mvpwm)); + mvpwm->controller.blink_on_duration = + readl_relaxed(mvebu_pwmreg_blink_on_duration(&mvpwm->controller)); + mvpwm->controller.blink_off_duration = + readl_relaxed(mvebu_pwmreg_blink_off_duration(&mvpwm->controller)); } static void __maybe_unused mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip) @@ -758,10 +822,10 @@ static void __maybe_unused mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip) regmap_write(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, mvpwm->blink_select); - writel_relaxed(mvpwm->blink_on_duration, - mvebu_pwmreg_blink_on_duration(mvpwm)); - writel_relaxed(mvpwm->blink_off_duration, - mvebu_pwmreg_blink_off_duration(mvpwm)); + writel_relaxed(mvpwm->controller.blink_on_duration, + mvebu_pwmreg_blink_on_duration(&mvpwm->controller)); + writel_relaxed(mvpwm->controller.blink_off_duration, + mvebu_pwmreg_blink_off_duration(&mvpwm->controller)); } static int mvebu_pwm_probe(struct platform_device *pdev, @@ -772,6 +836,7 @@ static int mvebu_pwm_probe(struct platform_device *pdev, struct mvebu_pwm *mvpwm; struct resource *res; u32 set; + enum mvebu_pwm_ctrl ctrl_set; if (!of_device_is_compatible(mvchip->chip.of_node, "marvell,armada-370-gpio")) @@ -794,12 +859,15 @@ 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); @@ -809,15 +877,13 @@ static int mvebu_pwm_probe(struct platform_device *pdev, mvchip->mvpwm = mvpwm; mvpwm->mvchip = mvchip; - mvpwm->membase = devm_ioremap_resource(dev, res); - if (IS_ERR(mvpwm->membase)) - return PTR_ERR(mvpwm->membase); + mvpwm->controller.membase = devm_ioremap_resource(dev, res); + if (IS_ERR(mvpwm->controller.membase)) + return PTR_ERR(mvpwm->controller.membase); - mvpwm->clk_rate = clk_get_rate(mvchip->clk); - if (!mvpwm->clk_rate) { - dev_err(dev, "failed to get clock rate\n"); + mvpwm->controller.clk_rate = clk_get_rate(mvchip->clk); + if (!mvpwm->controller.clk_rate) return -EINVAL; - } mvpwm->chip.dev = dev; mvpwm->chip.ops = &mvebu_pwm_ops; @@ -830,7 +896,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); }