static int nmk_pmx_enable(struct pinctrl_dev *pctldev, unsigned function, unsigned group) { struct nmk_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev); const struct nmk_pingroup *g; static unsigned int slpm[NUM_BANKS]; unsigned long flags; bool glitch; int ret = -EINVAL; int i; g = &npct->soc->groups[group]; if (g->altsetting < 0) return -EINVAL; dev_dbg(npct->dev, "enable group %s, %u pins\n", g->name, g->npins); /* Handle this special glitch on altfunction C */ glitch = (g->altsetting == NMK_GPIO_ALT_C); if (glitch) { spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); /* Initially don't put any pins to sleep when switching */ memset(slpm, 0xff, sizeof(slpm)); /* * Then mask the pins that need to be sleeping now when we're * switching to the ALT C function. */ for (i = 0; i < g->npins; i++) slpm[g->pins[i] / NMK_GPIO_PER_CHIP] &= ~BIT(g->pins[i]); nmk_gpio_glitch_slpm_init(slpm); } for (i = 0; i < g->npins; i++) { struct pinctrl_gpio_range *range; struct nmk_gpio_chip *nmk_chip; struct gpio_chip *chip; unsigned bit; range = nmk_match_gpio_range(pctldev, g->pins[i]); if (!range) { dev_err(npct->dev, "invalid pin offset %d in group %s at index %d\n", g->pins[i], g->name, i); goto out_glitch; } if (!range->gc) { dev_err(npct->dev, "GPIO chip missing in range for pin offset %d in group %s at index %d\n", g->pins[i], g->name, i); goto out_glitch; } chip = range->gc; nmk_chip = container_of(chip, struct nmk_gpio_chip, chip); dev_dbg(npct->dev, "setting pin %d to altsetting %d\n", g->pins[i], g->altsetting); clk_enable(nmk_chip->clk); bit = g->pins[i] % NMK_GPIO_PER_CHIP; /* * If the pin is switching to altfunc, and there was an * interrupt installed on it which has been lazy disabled, * actually mask the interrupt to prevent spurious interrupts * that would occur while the pin is under control of the * peripheral. Only SKE does this. */ nmk_gpio_disable_lazy_irq(nmk_chip, bit); __nmk_gpio_set_mode_safe(nmk_chip, bit, g->altsetting, glitch); clk_disable(nmk_chip->clk); } /* When all pins are successfully reconfigured we get here */ ret = 0; out_glitch: if (glitch) { nmk_gpio_glitch_slpm_restore(slpm); spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); } return ret; }
static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset, pin_cfg_t cfg, bool sleep, unsigned int *slpmregs) { static const char *afnames[] = { [NMK_GPIO_ALT_GPIO] = "GPIO", [NMK_GPIO_ALT_A] = "A", [NMK_GPIO_ALT_B] = "B", [NMK_GPIO_ALT_C] = "C" }; static const char *pullnames[] = { [NMK_GPIO_PULL_NONE] = "none", [NMK_GPIO_PULL_UP] = "up", [NMK_GPIO_PULL_DOWN] = "down", [3] /* illegal */ = "??" }; static const char *slpmnames[] = { [NMK_GPIO_SLPM_INPUT] = "input/wakeup", [NMK_GPIO_SLPM_NOCHANGE] = "no-change/no-wakeup", }; int pin = PIN_NUM(cfg); int pull = PIN_PULL(cfg); int af = PIN_ALT(cfg); int slpm = PIN_SLPM(cfg); int output = PIN_DIR(cfg); int val = PIN_VAL(cfg); bool glitch = af == NMK_GPIO_ALT_C; dev_dbg(nmk_chip->chip.dev, "pin %d [%#lx]: af %s, pull %s, slpm %s (%s%s)\n", pin, cfg, afnames[af], pullnames[pull], slpmnames[slpm], output ? "output " : "input", output ? (val ? "high" : "low") : ""); if (sleep) { int slpm_pull = PIN_SLPM_PULL(cfg); int slpm_output = PIN_SLPM_DIR(cfg); int slpm_val = PIN_SLPM_VAL(cfg); af = NMK_GPIO_ALT_GPIO; /* * The SLPM_* values are normal values + 1 to allow zero to * mean "same as normal". */ if (slpm_pull) pull = slpm_pull - 1; if (slpm_output) output = slpm_output - 1; if (slpm_val) val = slpm_val - 1; dev_dbg(nmk_chip->chip.dev, "pin %d: sleep pull %s, dir %s, val %s\n", pin, slpm_pull ? pullnames[pull] : "same", slpm_output ? (output ? "output" : "input") : "same", slpm_val ? (val ? "high" : "low") : "same"); } if (output) __nmk_gpio_make_output(nmk_chip, offset, val); else { __nmk_gpio_make_input(nmk_chip, offset); __nmk_gpio_set_pull(nmk_chip, offset, pull); } __nmk_gpio_set_lowemi(nmk_chip, offset, PIN_LOWEMI(cfg)); /* * If the pin is switching to altfunc, and there was an interrupt * installed on it which has been lazy disabled, actually mask the * interrupt to prevent spurious interrupts that would occur while the * pin is under control of the peripheral. Only SKE does this. */ if (af != NMK_GPIO_ALT_GPIO) nmk_gpio_disable_lazy_irq(nmk_chip, offset); /* * If we've backed up the SLPM registers (glitch workaround), modify * the backups since they will be restored. */ if (slpmregs) { if (slpm == NMK_GPIO_SLPM_NOCHANGE) slpmregs[nmk_chip->bank] |= BIT(offset); else slpmregs[nmk_chip->bank] &= ~BIT(offset); } else __nmk_gpio_set_slpm(nmk_chip, offset, slpm); __nmk_gpio_set_mode_safe(nmk_chip, offset, af, glitch); }
static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset, pin_cfg_t cfg, bool sleep, unsigned int *slpmregs) { static const char *afnames[] = { [NMK_GPIO_ALT_GPIO] = "GPIO", [NMK_GPIO_ALT_A] = "A", [NMK_GPIO_ALT_B] = "B", [NMK_GPIO_ALT_C] = "C" }; static const char *pullnames[] = { [NMK_GPIO_PULL_NONE] = "none", [NMK_GPIO_PULL_UP] = "up", [NMK_GPIO_PULL_DOWN] = "down", [3] /* illegal */ = "??" }; static const char *slpmnames[] = { [NMK_GPIO_SLPM_INPUT] = "input/wakeup", [NMK_GPIO_SLPM_NOCHANGE] = "no-change/no-wakeup", }; int pin = PIN_NUM(cfg); int pull = PIN_PULL(cfg); int af = PIN_ALT(cfg); int slpm = PIN_SLPM(cfg); int output = PIN_DIR(cfg); int val = PIN_VAL(cfg); bool glitch = af == NMK_GPIO_ALT_C; dev_dbg(nmk_chip->chip.dev, "pin %d [%#lx]: af %s, pull %s, slpm %s (%s%s)\n", pin, cfg, afnames[af], pullnames[pull], slpmnames[slpm], output ? "output " : "input", output ? (val ? "high" : "low") : ""); if (sleep) { int slpm_pull = PIN_SLPM_PULL(cfg); int slpm_output = PIN_SLPM_DIR(cfg); int slpm_val = PIN_SLPM_VAL(cfg); af = NMK_GPIO_ALT_GPIO; /* * The SLPM_* values are normal values + 1 to allow zero to * mean "same as normal". */ if (slpm_pull) pull = slpm_pull - 1; if (slpm_output) output = slpm_output - 1; if (slpm_val) val = slpm_val - 1; dev_dbg(nmk_chip->chip.dev, "pin %d: sleep pull %s, dir %s, val %s\n", pin, slpm_pull ? pullnames[pull] : "same", slpm_output ? (output ? "output" : "input") : "same", slpm_val ? (val ? "high" : "low") : "same"); } if (output) __nmk_gpio_make_output(nmk_chip, offset, val); else { __nmk_gpio_make_input(nmk_chip, offset); __nmk_gpio_set_pull(nmk_chip, offset, pull); } /* * If we've backed up the SLPM registers (glitch workaround), modify * the backups since they will be restored. */ if (slpmregs) { if (slpm == NMK_GPIO_SLPM_NOCHANGE) slpmregs[nmk_chip->bank] |= BIT(offset); else slpmregs[nmk_chip->bank] &= ~BIT(offset); } else __nmk_gpio_set_slpm(nmk_chip, offset, slpm); __nmk_gpio_set_mode_safe(nmk_chip, offset, af, glitch); }