diff options
Diffstat (limited to 'drivers/gpio/gpio-mvebu.c')
-rw-r--r-- | drivers/gpio/gpio-mvebu.c | 435 |
1 files changed, 373 insertions, 62 deletions
diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index a649556ac3ca..19a92efabbef 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -42,29 +42,44 @@ #include <linux/io.h> #include <linux/of_irq.h> #include <linux/of_device.h> +#include <linux/pwm.h> #include <linux/clk.h> #include <linux/pinctrl/consumer.h> #include <linux/irqchip/chained_irq.h> +#include <linux/platform_device.h> +#include <linux/bitops.h> + +#include "gpiolib.h" /* * GPIO unit register offsets. */ -#define GPIO_OUT_OFF 0x0000 -#define GPIO_IO_CONF_OFF 0x0004 -#define GPIO_BLINK_EN_OFF 0x0008 -#define GPIO_IN_POL_OFF 0x000c -#define GPIO_DATA_IN_OFF 0x0010 -#define GPIO_EDGE_CAUSE_OFF 0x0014 -#define GPIO_EDGE_MASK_OFF 0x0018 -#define GPIO_LEVEL_MASK_OFF 0x001c +#define GPIO_OUT_OFF 0x0000 +#define GPIO_IO_CONF_OFF 0x0004 +#define GPIO_BLINK_EN_OFF 0x0008 +#define GPIO_IN_POL_OFF 0x000c +#define GPIO_DATA_IN_OFF 0x0010 +#define GPIO_EDGE_CAUSE_OFF 0x0014 +#define GPIO_EDGE_MASK_OFF 0x0018 +#define GPIO_LEVEL_MASK_OFF 0x001c +#define GPIO_BLINK_CNT_SELECT_OFF 0x0020 + +/* + * PWM register offsets. + */ +#define PWM_BLINK_ON_DURATION_OFF 0x0 +#define PWM_BLINK_OFF_DURATION_OFF 0x4 + /* The MV78200 has per-CPU registers for edge mask and level mask */ #define GPIO_EDGE_MASK_MV78200_OFF(cpu) ((cpu) ? 0x30 : 0x18) #define GPIO_LEVEL_MASK_MV78200_OFF(cpu) ((cpu) ? 0x34 : 0x1C) -/* The Armada XP has per-CPU registers for interrupt cause, interrupt +/* + * The Armada XP has per-CPU registers for interrupt cause, interrupt * mask and interrupt level mask. Those are relative to the - * percpu_membase. */ + * percpu_membase. + */ #define GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu) ((cpu) * 0x4) #define GPIO_EDGE_MASK_ARMADAXP_OFF(cpu) (0x10 + (cpu) * 0x4) #define GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu) (0x20 + (cpu) * 0x4) @@ -75,6 +90,20 @@ #define MVEBU_MAX_GPIO_PER_BANK 32 +struct mvebu_pwm { + void __iomem *membase; + unsigned long clk_rate; + struct gpio_desc *gpiod; + struct pwm_chip chip; + spinlock_t lock; + struct mvebu_gpio_chip *mvchip; + + /* Used to preserve GPIO/PWM registers across suspend/resume */ + u32 blink_select; + u32 blink_on_duration; + u32 blink_off_duration; +}; + struct mvebu_gpio_chip { struct gpio_chip chip; spinlock_t lock; @@ -84,48 +113,55 @@ struct mvebu_gpio_chip { struct irq_domain *domain; int soc_variant; + /* Used for PWM support */ + struct clk *clk; + struct mvebu_pwm *mvpwm; + /* Used to preserve GPIO registers across suspend/resume */ - u32 out_reg; - u32 io_conf_reg; - u32 blink_en_reg; - u32 in_pol_reg; - u32 edge_mask_regs[4]; - u32 level_mask_regs[4]; + u32 out_reg; + u32 io_conf_reg; + u32 blink_en_reg; + u32 in_pol_reg; + u32 edge_mask_regs[4]; + u32 level_mask_regs[4]; }; /* * Functions returning addresses of individual registers for a given * GPIO controller. */ -static inline void __iomem *mvebu_gpioreg_out(struct mvebu_gpio_chip *mvchip) +static void __iomem *mvebu_gpioreg_out(struct mvebu_gpio_chip *mvchip) { return mvchip->membase + GPIO_OUT_OFF; } -static inline void __iomem *mvebu_gpioreg_blink(struct mvebu_gpio_chip *mvchip) +static void __iomem *mvebu_gpioreg_blink(struct mvebu_gpio_chip *mvchip) { return mvchip->membase + GPIO_BLINK_EN_OFF; } -static inline void __iomem * -mvebu_gpioreg_io_conf(struct mvebu_gpio_chip *mvchip) +static void __iomem *mvebu_gpioreg_blink_counter_select(struct mvebu_gpio_chip + *mvchip) +{ + return mvchip->membase + GPIO_BLINK_CNT_SELECT_OFF; +} + +static void __iomem *mvebu_gpioreg_io_conf(struct mvebu_gpio_chip *mvchip) { return mvchip->membase + GPIO_IO_CONF_OFF; } -static inline void __iomem *mvebu_gpioreg_in_pol(struct mvebu_gpio_chip *mvchip) +static void __iomem *mvebu_gpioreg_in_pol(struct mvebu_gpio_chip *mvchip) { return mvchip->membase + GPIO_IN_POL_OFF; } -static inline void __iomem * -mvebu_gpioreg_data_in(struct mvebu_gpio_chip *mvchip) +static void __iomem *mvebu_gpioreg_data_in(struct mvebu_gpio_chip *mvchip) { return mvchip->membase + GPIO_DATA_IN_OFF; } -static inline void __iomem * -mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip) +static void __iomem *mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip) { int cpu; @@ -142,8 +178,7 @@ mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip) } } -static inline void __iomem * -mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvchip) +static void __iomem *mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvchip) { int cpu; @@ -182,10 +217,23 @@ static void __iomem *mvebu_gpioreg_level_mask(struct mvebu_gpio_chip *mvchip) } /* - * Functions implementing the gpio_chip methods + * Functions returning addresses of individual registers for a given + * PWM controller. */ +static void __iomem *mvebu_pwmreg_blink_on_duration(struct mvebu_pwm *mvpwm) +{ + return mvpwm->membase + PWM_BLINK_ON_DURATION_OFF; +} + +static void __iomem *mvebu_pwmreg_blink_off_duration(struct mvebu_pwm *mvpwm) +{ + return mvpwm->membase + PWM_BLINK_OFF_DURATION_OFF; +} -static void mvebu_gpio_set(struct gpio_chip *chip, unsigned pin, int value) +/* + * Functions implementing the gpio_chip methods + */ +static void mvebu_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); unsigned long flags; @@ -194,19 +242,19 @@ static void mvebu_gpio_set(struct gpio_chip *chip, unsigned pin, int value) spin_lock_irqsave(&mvchip->lock, flags); u = readl_relaxed(mvebu_gpioreg_out(mvchip)); if (value) - u |= 1 << pin; + u |= BIT(pin); else - u &= ~(1 << pin); + u &= ~BIT(pin); writel_relaxed(u, mvebu_gpioreg_out(mvchip)); spin_unlock_irqrestore(&mvchip->lock, flags); } -static int mvebu_gpio_get(struct gpio_chip *chip, unsigned pin) +static int mvebu_gpio_get(struct gpio_chip *chip, unsigned int pin) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); u32 u; - if (readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & (1 << pin)) { + if (readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & BIT(pin)) { u = readl_relaxed(mvebu_gpioreg_data_in(mvchip)) ^ readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); } else { @@ -216,7 +264,8 @@ static int mvebu_gpio_get(struct gpio_chip *chip, unsigned pin) return (u >> pin) & 1; } -static void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value) +static void mvebu_gpio_blink(struct gpio_chip *chip, unsigned int pin, + int value) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); unsigned long flags; @@ -225,36 +274,38 @@ static void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value) spin_lock_irqsave(&mvchip->lock, flags); u = readl_relaxed(mvebu_gpioreg_blink(mvchip)); if (value) - u |= 1 << pin; + u |= BIT(pin); else - u &= ~(1 << pin); + u &= ~BIT(pin); writel_relaxed(u, mvebu_gpioreg_blink(mvchip)); spin_unlock_irqrestore(&mvchip->lock, flags); } -static int mvebu_gpio_direction_input(struct gpio_chip *chip, unsigned pin) +static int mvebu_gpio_direction_input(struct gpio_chip *chip, unsigned int pin) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); unsigned long flags; int ret; u32 u; - /* Check with the pinctrl driver whether this pin is usable as - * an input GPIO */ + /* + * Check with the pinctrl driver whether this pin is usable as + * an input GPIO + */ ret = pinctrl_gpio_direction_input(chip->base + pin); if (ret) return ret; spin_lock_irqsave(&mvchip->lock, flags); u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)); - u |= 1 << pin; + u |= BIT(pin); writel_relaxed(u, mvebu_gpioreg_io_conf(mvchip)); spin_unlock_irqrestore(&mvchip->lock, flags); return 0; } -static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned pin, +static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned int pin, int value) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); @@ -262,8 +313,10 @@ static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned pin, int ret; u32 u; - /* Check with the pinctrl driver whether this pin is usable as - * an output GPIO */ + /* + * Check with the pinctrl driver whether this pin is usable as + * an output GPIO + */ ret = pinctrl_gpio_direction_output(chip->base + pin); if (ret) return ret; @@ -273,16 +326,17 @@ static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned pin, spin_lock_irqsave(&mvchip->lock, flags); u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)); - u &= ~(1 << pin); + u &= ~BIT(pin); writel_relaxed(u, mvebu_gpioreg_io_conf(mvchip)); spin_unlock_irqrestore(&mvchip->lock, flags); return 0; } -static int mvebu_gpio_to_irq(struct gpio_chip *chip, unsigned pin) +static int mvebu_gpio_to_irq(struct gpio_chip *chip, unsigned int pin) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); + return irq_create_mapping(mvchip->domain, pin); } @@ -389,7 +443,7 @@ static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type) pin = d->hwirq; - u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & (1 << pin); + u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & BIT(pin); if (!u) return -EINVAL; @@ -409,13 +463,13 @@ static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type) case IRQ_TYPE_EDGE_RISING: case IRQ_TYPE_LEVEL_HIGH: u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); - u &= ~(1 << pin); + u &= ~BIT(pin); writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip)); break; case IRQ_TYPE_EDGE_FALLING: case IRQ_TYPE_LEVEL_LOW: u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); - u |= 1 << pin; + u |= BIT(pin); writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip)); break; case IRQ_TYPE_EDGE_BOTH: { @@ -428,10 +482,10 @@ static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type) * set initial polarity based on current input level */ u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); - if (v & (1 << pin)) - u |= 1 << pin; /* falling */ + if (v & BIT(pin)) + u |= BIT(pin); /* falling */ else - u &= ~(1 << pin); /* rising */ + u &= ~BIT(pin); /* rising */ writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip)); break; } @@ -461,7 +515,7 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc) irq = irq_find_mapping(mvchip->domain, i); - if (!(cause & (1 << i))) + if (!(cause & BIT(i))) continue; type = irq_get_trigger_type(irq); @@ -470,7 +524,7 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc) u32 polarity; polarity = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); - polarity ^= 1 << i; + polarity ^= BIT(i); writel_relaxed(polarity, mvebu_gpioreg_in_pol(mvchip)); } @@ -480,6 +534,246 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc) chained_irq_exit(chip, desc); } +/* + * Functions implementing the pwm_chip methods + */ +static struct mvebu_pwm *to_mvebu_pwm(struct pwm_chip *chip) +{ + return container_of(chip, struct mvebu_pwm, chip); +} + +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; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&mvpwm->lock, flags); + + if (mvpwm->gpiod) { + ret = -EBUSY; + } else { + desc = gpio_to_desc(mvchip->chip.base + pwm->hwpwm); + if (!desc) { + ret = -ENODEV; + goto out; + } + + ret = gpiod_request(desc, "mvebu-pwm"); + if (ret) + goto out; + + ret = gpiod_direction_output(desc, 0); + if (ret) { + gpiod_free(desc); + goto out; + } + + mvpwm->gpiod = desc; + } +out: + spin_unlock_irqrestore(&mvpwm->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); + unsigned long flags; + + spin_lock_irqsave(&mvpwm->lock, flags); + gpiod_free(mvpwm->gpiod); + mvpwm->gpiod = NULL; + spin_unlock_irqrestore(&mvpwm->lock, flags); +} + +static void mvebu_pwm_get_state(struct pwm_chip *chip, + struct pwm_device *pwm, + struct pwm_state *state) { + + struct mvebu_pwm *mvpwm = to_mvebu_pwm(chip); + struct mvebu_gpio_chip *mvchip = mvpwm->mvchip; + unsigned long long val; + unsigned long flags; + u32 u; + + spin_lock_irqsave(&mvpwm->lock, flags); + + val = (unsigned long long) + readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm)); + val *= NSEC_PER_SEC; + do_div(val, mvpwm->clk_rate); + if (val > UINT_MAX) + state->duty_cycle = UINT_MAX; + else if (val) + state->duty_cycle = val; + else + state->duty_cycle = 1; + + val = (unsigned long long) + readl_relaxed(mvebu_pwmreg_blink_off_duration(mvpwm)); + val *= NSEC_PER_SEC; + do_div(val, mvpwm->clk_rate); + if (val < state->duty_cycle) { + state->period = 1; + } else { + val -= state->duty_cycle; + if (val > UINT_MAX) + state->period = UINT_MAX; + else if (val) + state->period = val; + else + state->period = 1; + } + + u = readl_relaxed(mvebu_gpioreg_blink(mvchip)); + if (u) + state->enabled = true; + else + state->enabled = false; + + spin_unlock_irqrestore(&mvpwm->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_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; + do_div(val, NSEC_PER_SEC); + if (val > UINT_MAX) + return -EINVAL; + if (val) + on = val; + else + on = 1; + + val = (unsigned long long) mvpwm->clk_rate * + (state->period - state->duty_cycle); + do_div(val, NSEC_PER_SEC); + if (val > UINT_MAX) + return -EINVAL; + if (val) + off = val; + else + off = 1; + + spin_lock_irqsave(&mvpwm->lock, flags); + + writel_relaxed(on, mvebu_pwmreg_blink_on_duration(mvpwm)); + writel_relaxed(off, mvebu_pwmreg_blink_off_duration(mvpwm)); + 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); + + return 0; +} + +static const struct pwm_ops mvebu_pwm_ops = { + .request = mvebu_pwm_request, + .free = mvebu_pwm_free, + .get_state = mvebu_pwm_get_state, + .apply = mvebu_pwm_apply, + .owner = THIS_MODULE, +}; + +static void __maybe_unused mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip) +{ + struct mvebu_pwm *mvpwm = mvchip->mvpwm; + + mvpwm->blink_select = + readl_relaxed(mvebu_gpioreg_blink_counter_select(mvchip)); + mvpwm->blink_on_duration = + readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm)); + mvpwm->blink_off_duration = + readl_relaxed(mvebu_pwmreg_blink_off_duration(mvpwm)); +} + +static void __maybe_unused mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip) +{ + struct mvebu_pwm *mvpwm = mvchip->mvpwm; + + writel_relaxed(mvpwm->blink_select, + mvebu_gpioreg_blink_counter_select(mvchip)); + writel_relaxed(mvpwm->blink_on_duration, + mvebu_pwmreg_blink_on_duration(mvpwm)); + writel_relaxed(mvpwm->blink_off_duration, + mvebu_pwmreg_blink_off_duration(mvpwm)); +} + +static int mvebu_pwm_probe(struct platform_device *pdev, + struct mvebu_gpio_chip *mvchip, + int id) +{ + struct device *dev = &pdev->dev; + struct mvebu_pwm *mvpwm; + struct resource *res; + u32 set; + + if (!of_device_is_compatible(mvchip->chip.of_node, + "marvell,armada-370-xp-gpio")) + return 0; + + if (IS_ERR(mvchip->clk)) + return PTR_ERR(mvchip->clk); + + /* + * There are only two sets of PWM configuration registers for + * all the GPIO lines on those SoCs which this driver reserves + * for the first two GPIO chips. So if the resource is missing + * we can't treat it as an error. + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm"); + if (!res) + return 0; + + /* + * 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; + writel_relaxed(0, mvebu_gpioreg_blink_counter_select(mvchip)); + + mvpwm = devm_kzalloc(dev, sizeof(struct mvebu_pwm), GFP_KERNEL); + if (!mvpwm) + return -ENOMEM; + mvchip->mvpwm = mvpwm; + mvpwm->mvchip = mvchip; + + mvpwm->membase = devm_ioremap_resource(dev, res); + if (IS_ERR(mvpwm->membase)) + return PTR_ERR(mvpwm->membase); + + mvpwm->clk_rate = clk_get_rate(mvchip->clk); + if (!mvpwm->clk_rate) { + dev_err(dev, "failed to get clock rate\n"); + return -EINVAL; + } + + mvpwm->chip.dev = dev; + mvpwm->chip.ops = &mvebu_pwm_ops; + mvpwm->chip.npwm = mvchip->chip.ngpio; + + spin_lock_init(&mvpwm->lock); + + return pwmchip_add(&mvpwm->chip); +} + #ifdef CONFIG_DEBUG_FS #include <linux/seq_file.h> @@ -507,7 +801,7 @@ static void mvebu_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) if (!label) continue; - msk = 1 << i; + msk = BIT(i); is_out = !(io_conf & msk); seq_printf(s, " gpio-%-3d (%-20.20s)", chip->base + i, label); @@ -551,6 +845,10 @@ static const struct of_device_id mvebu_gpio_of_match[] = { .data = (void *) MVEBU_GPIO_SOC_VARIANT_ARMADAXP, }, { + .compatible = "marvell,armada-370-xp-gpio", + .data = (void *) MVEBU_GPIO_SOC_VARIANT_ORION, + }, + { /* sentinel */ }, }; @@ -596,6 +894,9 @@ static int mvebu_gpio_suspend(struct platform_device *pdev, pm_message_t state) BUG(); } + if (IS_ENABLED(CONFIG_PWM)) + mvebu_pwm_suspend(mvchip); + return 0; } @@ -639,6 +940,9 @@ static int mvebu_gpio_resume(struct platform_device *pdev) BUG(); } + if (IS_ENABLED(CONFIG_PWM)) + mvebu_pwm_resume(mvchip); + return 0; } @@ -650,7 +954,6 @@ static int mvebu_gpio_probe(struct platform_device *pdev) struct resource *res; struct irq_chip_generic *gc; struct irq_chip_type *ct; - struct clk *clk; unsigned int ngpios; bool have_irqs; int soc_variant; @@ -684,10 +987,10 @@ static int mvebu_gpio_probe(struct platform_device *pdev) return id; } - clk = devm_clk_get(&pdev->dev, NULL); + mvchip->clk = devm_clk_get(&pdev->dev, NULL); /* Not all SoCs require a clock.*/ - if (!IS_ERR(clk)) - clk_prepare_enable(clk); + if (!IS_ERR(mvchip->clk)) + clk_prepare_enable(mvchip->clk); mvchip->soc_variant = soc_variant; mvchip->chip.label = dev_name(&pdev->dev); @@ -712,8 +1015,10 @@ static int mvebu_gpio_probe(struct platform_device *pdev) if (IS_ERR(mvchip->membase)) return PTR_ERR(mvchip->membase); - /* The Armada XP has a second range of registers for the - * per-CPU registers */ + /* + * The Armada XP has a second range of registers for the + * per-CPU registers + */ if (soc_variant == MVEBU_GPIO_SOC_VARIANT_ARMADAXP) { res = platform_get_resource(pdev, IORESOURCE_MEM, 1); mvchip->percpu_membase = devm_ioremap_resource(&pdev->dev, @@ -780,7 +1085,8 @@ static int mvebu_gpio_probe(struct platform_device *pdev) goto err_domain; } - /* NOTE: The common accessors cannot be used because of the percpu + /* + * NOTE: The common accessors cannot be used because of the percpu * access to the mask registers */ gc = irq_get_domain_generic_chip(mvchip->domain, 0); @@ -801,7 +1107,8 @@ static int mvebu_gpio_probe(struct platform_device *pdev) ct->handler = handle_edge_irq; ct->chip.name = mvchip->chip.label; - /* Setup the interrupt handlers. Each chip can have up to 4 + /* + * Setup the interrupt handlers. Each chip can have up to 4 * interrupt handlers, with each handler dealing with 8 GPIO * pins. */ @@ -814,6 +1121,10 @@ static int mvebu_gpio_probe(struct platform_device *pdev) mvchip); } + /* Armada 370/XP has simple PWM support for GPIO lines */ + if (IS_ENABLED(CONFIG_PWM)) + return mvebu_pwm_probe(pdev, mvchip, id); + return 0; err_domain: |