diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index e29a9589b3cc..22733944b61f 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -632,6 +632,8 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) #if defined(CONFIG_MIPS) && defined(__BIG_ENDIAN) flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER; #endif + if (of_property_read_bool(np, "brcm,gpio-direct")) + flags |= BGPIOF_REG_DIRECT; of_property_for_each_u32(np, "brcm,gpio-bank-widths", bank_width) { struct brcmstb_gpio_bank *bank; @@ -680,7 +682,9 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) } gc->owner = THIS_MODULE; - gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pOF", np); + gc->label = devm_kasprintf(dev, GFP_KERNEL, "gpio-brcmstb@%zx", + (size_t)res->start + + GIO_BANK_OFF(bank->id, 0)); if (!gc->label) { err = -ENOMEM; goto fail; @@ -688,7 +692,7 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) gc->of_gpio_n_cells = 2; gc->of_xlate = brcmstb_gpio_of_xlate; /* not all ngpio lines are valid, will use bank width later */ - gc->ngpio = MAX_GPIO_PER_BANK; + gc->ngpio = bank_width; gc->offset = bank->id * MAX_GPIO_PER_BANK; gc->request = gpiochip_generic_request; gc->free = gpiochip_generic_free; @@ -699,8 +703,10 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) * Mask all interrupts by default, since wakeup interrupts may * be retained from S5 cold boot */ - need_wakeup_event |= !!__brcmstb_gpio_get_active_irqs(bank); - gc->write_reg(reg_base + GIO_MASK(bank->id), 0); + if (priv->parent_irq > 0) { + need_wakeup_event |= !!__brcmstb_gpio_get_active_irqs(bank); + gc->write_reg(reg_base + GIO_MASK(bank->id), 0); + } err = gpiochip_add_data(gc, bank); if (err) { diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index 021ad62778c2..bbefbf7064a7 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -235,6 +235,26 @@ static int bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) return 0; } +static int bgpio_set_direct(struct gpio_chip *gc, unsigned int gpio, int val) +{ + unsigned long mask = bgpio_line2mask(gc, gpio); + unsigned long flags; + + raw_spin_lock_irqsave(&gc->bgpio_lock, flags); + + gc->bgpio_data = gc->read_reg(gc->reg_dat); + + if (val) + gc->bgpio_data |= mask; + else + gc->bgpio_data &= ~mask; + + gc->write_reg(gc->reg_dat, gc->bgpio_data); + + raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); + return 0; +} + static int bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, int val) { @@ -337,6 +357,28 @@ static int bgpio_set_multiple_with_clear(struct gpio_chip *gc, return 0; } +static int bgpio_set_multiple_direct(struct gpio_chip *gc, + unsigned long *mask, + unsigned long *bits) +{ + unsigned long flags; + unsigned long set_mask, clear_mask; + + raw_spin_lock_irqsave(&gc->bgpio_lock, flags); + + bgpio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask); + + gc->bgpio_data = gc->read_reg(gc->reg_dat); + + gc->bgpio_data |= set_mask; + gc->bgpio_data &= ~clear_mask; + + gc->write_reg(gc->reg_dat, gc->bgpio_data); + + raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); + return 0; +} + static int bgpio_dir_return(struct gpio_chip *gc, unsigned int gpio, bool dir_out) { if (!gc->bgpio_pinctrl) @@ -390,6 +432,29 @@ static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) return bgpio_dir_return(gc, gpio, false); } +static int bgpio_dir_in_direct(struct gpio_chip *gc, unsigned int gpio) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&gc->bgpio_lock, flags); + + if (gc->reg_dir_in) + gc->bgpio_dir = ~gc->read_reg(gc->reg_dir_in); + if (gc->reg_dir_out) + gc->bgpio_dir = gc->read_reg(gc->reg_dir_out); + + gc->bgpio_dir &= ~bgpio_line2mask(gc, gpio); + + if (gc->reg_dir_in) + gc->write_reg(gc->reg_dir_in, ~gc->bgpio_dir); + if (gc->reg_dir_out) + gc->write_reg(gc->reg_dir_out, gc->bgpio_dir); + + raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); + + return 0; +} + static int bgpio_get_dir(struct gpio_chip *gc, unsigned int gpio) { /* Return 0 if output, 1 if input */ @@ -428,6 +493,28 @@ static void bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); } +static void bgpio_dir_out_direct(struct gpio_chip *gc, unsigned int gpio, + int val) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&gc->bgpio_lock, flags); + + if (gc->reg_dir_in) + gc->bgpio_dir = ~gc->read_reg(gc->reg_dir_in); + if (gc->reg_dir_out) + gc->bgpio_dir = gc->read_reg(gc->reg_dir_out); + + gc->bgpio_dir |= bgpio_line2mask(gc, gpio); + + if (gc->reg_dir_in) + gc->write_reg(gc->reg_dir_in, ~gc->bgpio_dir); + if (gc->reg_dir_out) + gc->write_reg(gc->reg_dir_out, gc->bgpio_dir); + + raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); +} + static int bgpio_dir_out_dir_first(struct gpio_chip *gc, unsigned int gpio, int val) { @@ -444,6 +531,22 @@ static int bgpio_dir_out_val_first(struct gpio_chip *gc, unsigned int gpio, return bgpio_dir_return(gc, gpio, true); } +static int bgpio_dir_out_dir_first_direct(struct gpio_chip *gc, + unsigned int gpio, int val) +{ + bgpio_dir_out_direct(gc, gpio, val); + gc->set(gc, gpio, val); + return 0; +} + +static int bgpio_dir_out_val_first_direct(struct gpio_chip *gc, + unsigned int gpio, int val) +{ + gc->set(gc, gpio, val); + bgpio_dir_out_direct(gc, gpio, val); + return 0; +} + static int bgpio_setup_accessors(struct device *dev, struct gpio_chip *gc, bool byte_be) @@ -537,6 +640,9 @@ static int bgpio_setup_io(struct gpio_chip *gc, } else if (flags & BGPIOF_NO_OUTPUT) { gc->set = bgpio_set_none; gc->set_multiple = NULL; + } else if (flags & BGPIOF_REG_DIRECT) { + gc->set = bgpio_set_direct; + gc->set_multiple = bgpio_set_multiple_direct; } else { gc->set = bgpio_set; gc->set_multiple = bgpio_set_multiple; @@ -573,11 +679,21 @@ static int bgpio_setup_direction(struct gpio_chip *gc, if (dirout || dirin) { gc->reg_dir_out = dirout; gc->reg_dir_in = dirin; - if (flags & BGPIOF_NO_SET_ON_INPUT) - gc->direction_output = bgpio_dir_out_dir_first; - else - gc->direction_output = bgpio_dir_out_val_first; - gc->direction_input = bgpio_dir_in; + if (flags & BGPIOF_REG_DIRECT) { + if (flags & BGPIOF_NO_SET_ON_INPUT) + gc->direction_output = + bgpio_dir_out_dir_first_direct; + else + gc->direction_output = + bgpio_dir_out_val_first_direct; + gc->direction_input = bgpio_dir_in_direct; + } else { + if (flags & BGPIOF_NO_SET_ON_INPUT) + gc->direction_output = bgpio_dir_out_dir_first; + else + gc->direction_output = bgpio_dir_out_val_first; + gc->direction_input = bgpio_dir_in; + } gc->get_direction = bgpio_get_dir; } else { if (flags & BGPIOF_NO_OUTPUT) diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 667f8fd58a79..35d84b17cb92 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -737,6 +737,7 @@ int bgpio_init(struct gpio_chip *gc, struct device *dev, #define BGPIOF_NO_SET_ON_INPUT BIT(6) #define BGPIOF_PINCTRL_BACKEND BIT(7) /* Call pinctrl direction setters */ #define BGPIOF_NO_INPUT BIT(8) /* only output */ +#define BGPIOF_REG_DIRECT BIT(15) /* ignore shadow registers */ #ifdef CONFIG_GPIOLIB_IRQCHIP int gpiochip_irqchip_add_domain(struct gpio_chip *gc,