diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index d1afedf4dcbf..e81008678a38 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -77,6 +77,8 @@ struct gpio_bank { bool workaround_enabled; void (*set_dataout)(struct gpio_bank *bank, unsigned gpio, int enable); + void (*set_dataout_multiple)(struct gpio_bank *bank, + unsigned long *mask, unsigned long *bits); int (*get_context_loss_count)(struct device *dev); struct omap_gpio_reg_offs *regs; @@ -161,6 +163,51 @@ static int omap_get_gpio_dataout(struct gpio_bank *bank, int offset) return (readl_relaxed(reg) & (BIT(offset))) != 0; } +/* set multiple data out values using dedicate set/clear register */ +static void omap_set_gpio_dataout_reg_multiple(struct gpio_bank *bank, + unsigned long *mask, + unsigned long *bits) +{ + void __iomem *reg = bank->base; + u32 l; + + l = *bits & *mask; + writel_relaxed(l, reg + bank->regs->set_dataout); + bank->context.dataout |= l; + + l = ~*bits & *mask; + writel_relaxed(l, reg + bank->regs->clr_dataout); + bank->context.dataout &= ~l; +} + +/* set multiple data out values using mask register */ +static void omap_set_gpio_dataout_mask_multiple(struct gpio_bank *bank, + unsigned long *mask, + unsigned long *bits) +{ + void __iomem *reg = bank->base + bank->regs->dataout; + u32 l = (readl_relaxed(reg) & ~*mask) | (*bits & *mask); + + writel_relaxed(l, reg); + bank->context.dataout = l; +} + +static unsigned long omap_get_gpio_datain_multiple(struct gpio_bank *bank, + unsigned long *mask) +{ + void __iomem *reg = bank->base + bank->regs->datain; + + return readl_relaxed(reg) & *mask; +} + +static unsigned long omap_get_gpio_dataout_multiple(struct gpio_bank *bank, + unsigned long *mask) +{ + void __iomem *reg = bank->base + bank->regs->dataout; + + return readl_relaxed(reg) & *mask; +} + static inline void omap_gpio_rmw(void __iomem *base, u32 reg, u32 mask, bool set) { int l = readl_relaxed(base + reg); @@ -968,6 +1015,26 @@ static int omap_gpio_output(struct gpio_chip *chip, unsigned offset, int value) return 0; } +static int omap_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) +{ + struct gpio_bank *bank = gpiochip_get_data(chip); + void __iomem *reg = bank->base + bank->regs->direction; + unsigned long in = readl_relaxed(reg), l; + + *bits = 0; + + l = in & *mask; + if (l) + *bits |= omap_get_gpio_datain_multiple(bank, &l); + + l = ~in & *mask; + if (l) + *bits |= omap_get_gpio_dataout_multiple(bank, &l); + + return 0; +} + static int omap_gpio_debounce(struct gpio_chip *chip, unsigned offset, unsigned debounce) { @@ -1012,6 +1079,17 @@ static void omap_gpio_set(struct gpio_chip *chip, unsigned offset, int value) raw_spin_unlock_irqrestore(&bank->lock, flags); } +static void omap_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) +{ + struct gpio_bank *bank = gpiochip_get_data(chip); + unsigned long flags; + + raw_spin_lock_irqsave(&bank->lock, flags); + bank->set_dataout_multiple(bank, mask, bits); + raw_spin_unlock_irqrestore(&bank->lock, flags); +} + /*---------------------------------------------------------------------*/ static void omap_gpio_show_rev(struct gpio_bank *bank) @@ -1073,9 +1151,11 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc) bank->chip.get_direction = omap_gpio_get_direction; bank->chip.direction_input = omap_gpio_input; bank->chip.get = omap_gpio_get; + bank->chip.get_multiple = omap_gpio_get_multiple; bank->chip.direction_output = omap_gpio_output; bank->chip.set_config = omap_gpio_set_config; bank->chip.set = omap_gpio_set; + bank->chip.set_multiple = omap_gpio_set_multiple; if (bank->is_mpuio) { bank->chip.label = "mpuio"; if (bank->regs->wkup_en) @@ -1209,10 +1289,14 @@ static int omap_gpio_probe(struct platform_device *pdev) pdata->get_context_loss_count; } - if (bank->regs->set_dataout && bank->regs->clr_dataout) + if (bank->regs->set_dataout && bank->regs->clr_dataout) { bank->set_dataout = omap_set_gpio_dataout_reg; - else + bank->set_dataout_multiple = omap_set_gpio_dataout_reg_multiple; + } else { bank->set_dataout = omap_set_gpio_dataout_mask; + bank->set_dataout_multiple = + omap_set_gpio_dataout_mask_multiple; + } raw_spin_lock_init(&bank->lock); raw_spin_lock_init(&bank->wa_lock);