Allow more than 1 PWM request (eg. PWM fan) on the same GPIO chip.

based on initial work on LK4.4 by Alban Browaeys.
URL: https://github.com/helios-4/linux-marvell/commit/743ae97
[Aditya Prayoga: forward port, cleanup]
Signed-off-by: Aditya Prayoga <adi...@kobol.io>
---
 drivers/gpio/gpio-mvebu.c | 63 ++++++++++++++++++++++++++++++-----------------
 1 file changed, 41 insertions(+), 22 deletions(-)

diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c
index 6e02148..0617e66 100644
--- a/drivers/gpio/gpio-mvebu.c
+++ b/drivers/gpio/gpio-mvebu.c
@@ -92,10 +92,17 @@
 
 #define MVEBU_MAX_GPIO_PER_BANK                32
 
+struct mvebu_pwm_item {
+       struct gpio_desc        *gpiod;
+       struct pwm_device       *device;
+       struct list_head         node;
+};
+
 struct mvebu_pwm {
        void __iomem            *membase;
        unsigned long            clk_rate;
-       struct gpio_desc        *gpiod;
+       int      id;
+       struct list_head         pwms;
        struct pwm_chip          chip;
        spinlock_t               lock;
        struct mvebu_gpio_chip  *mvchip;
@@ -599,29 +606,31 @@ 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;
+       struct mvebu_pwm_item *item;
        unsigned long flags;
        int ret = 0;
 
-       spin_lock_irqsave(&mvpwm->lock, flags);
-
-       if (mvpwm->gpiod) {
-               ret = -EBUSY;
-       } else {
-               desc = gpiochip_request_own_desc(&mvchip->chip,
-                                                pwm->hwpwm, "mvebu-pwm");
-               if (IS_ERR(desc)) {
-                       ret = PTR_ERR(desc);
-                       goto out;
-               }
+       item = kzalloc(sizeof(*item), GFP_KERNEL);
+       if (!item)
+               return -ENODEV;
 
-               ret = gpiod_direction_output(desc, 0);
-               if (ret) {
-                       gpiochip_free_own_desc(desc);
-                       goto out;
-               }
+       spin_lock_irqsave(&mvpwm->lock, flags);
+       desc = gpiochip_request_own_desc(&mvchip->chip,
+                                        pwm->hwpwm, "mvebu-pwm");
+       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;
        }
+       item->gpiod = desc;
+       item->device = pwm;
+       INIT_LIST_HEAD(&item->node);
+       list_add_tail(&item->node, &mvpwm->pwms);
 out:
        spin_unlock_irqrestore(&mvpwm->lock, flags);
        return ret;
@@ -630,12 +639,20 @@ static int mvebu_pwm_request(struct pwm_chip *chip, 
struct pwm_device *pwm)
 static void mvebu_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
 {
        struct mvebu_pwm *mvpwm = to_mvebu_pwm(chip);
+       struct mvebu_pwm_item *item, *tmp;
        unsigned long flags;
 
-       spin_lock_irqsave(&mvpwm->lock, flags);
-       gpiochip_free_own_desc(mvpwm->gpiod);
-       mvpwm->gpiod = NULL;
-       spin_unlock_irqrestore(&mvpwm->lock, flags);
+       list_for_each_entry_safe(item, tmp, &mvpwm->pwms, node) {
+               if (item->device == pwm) {
+                       spin_lock_irqsave(&mvpwm->lock, flags);
+                       gpiochip_free_own_desc(item->gpiod);
+                       item->gpiod = NULL;
+                       item->device = NULL;
+                       list_del(&item->node);
+                       spin_unlock_irqrestore(&mvpwm->lock, flags);
+                       kfree(item);
+               }
+       }
 }
 
 static void mvebu_pwm_get_state(struct pwm_chip *chip,
@@ -804,6 +821,8 @@ static int mvebu_pwm_probe(struct platform_device *pdev,
                return -ENOMEM;
        mvchip->mvpwm = mvpwm;
        mvpwm->mvchip = mvchip;
+       mvpwm->id     = id;
+       INIT_LIST_HEAD(&mvpwm->pwms);
 
        mvpwm->membase = devm_ioremap_resource(dev, res);
        if (IS_ERR(mvpwm->membase))
-- 
2.7.4

Reply via email to