diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 9f34e10..13f0bd4 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -37,6 +37,7 @@ select HAS_DEBUG_LL select HAVE_MACH_ARM_HEAD select HAVE_CLK + select PINCTRL_AT91 config ARCH_BCM2835 bool "Broadcom BCM2835 boards" diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile index ab80e5f..8599f50 100644 --- a/arch/arm/mach-at91/Makefile +++ b/arch/arm/mach-at91/Makefile @@ -1,4 +1,4 @@ -obj-y += setup.o clock.o gpio.o irq_fixup.o +obj-y += setup.o clock.o irq_fixup.o obj-$(CONFIG_CMD_AT91_BOOT_TEST) += boot_test_cmd.o obj-$(CONFIG_AT91_BOOTSTRAP) += bootstrap.o diff --git a/arch/arm/mach-at91/gpio.c b/arch/arm/mach-at91/gpio.c deleted file mode 100644 index 402634b..0000000 --- a/arch/arm/mach-at91/gpio.c +++ /dev/null @@ -1,670 +0,0 @@ -/* - * Copyright (C) 2005 HP Labs - * Copyright (C) 2011-2012 Jean-Christophe PLAGNIOL-VILLARD - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define MAX_GPIO_BANKS 5 - -static int gpio_banks = 0; - -/* - * Functionnality can change with newer chips - */ -struct at91_gpio_chip { - struct gpio_chip chip; - void __iomem *regbase; /* PIO bank virtual address */ - struct at91_pinctrl_mux_ops *ops; /* ops */ -}; - -#define to_at91_gpio_chip(c) container_of(c, struct at91_gpio_chip, chip) - -static struct at91_gpio_chip gpio_chip[MAX_GPIO_BANKS]; - -static inline struct at91_gpio_chip *pin_to_controller(unsigned pin) -{ - pin /= MAX_NB_GPIO_PER_BANK; - if (likely(pin < gpio_banks)) - return &gpio_chip[pin]; - - return NULL; -} - -/** - * struct at91_pinctrl_mux_ops - describes an At91 mux ops group - * on new IP with support for periph C and D the way to mux in - * periph A and B has changed - * So provide the right call back - * if not present means the IP does not support it - * @get_periph: return the periph mode configured - * @mux_A_periph: mux as periph A - * @mux_B_periph: mux as periph B - * @mux_C_periph: mux as periph C - * @mux_D_periph: mux as periph D - * @set_deglitch: enable/disable deglitch - * @set_debounce: enable/disable debounce - * @set_pulldown: enable/disable pulldown - * @disable_schmitt_trig: disable schmitt trigger - */ -struct at91_pinctrl_mux_ops { - enum at91_mux (*get_periph)(void __iomem *pio, unsigned mask); - void (*mux_A_periph)(void __iomem *pio, unsigned mask); - void (*mux_B_periph)(void __iomem *pio, unsigned mask); - void (*mux_C_periph)(void __iomem *pio, unsigned mask); - void (*mux_D_periph)(void __iomem *pio, unsigned mask); - bool (*get_deglitch)(void __iomem *pio, unsigned pin); - void (*set_deglitch)(void __iomem *pio, unsigned mask, bool in_on); - bool (*get_debounce)(void __iomem *pio, unsigned pin, u32 *div); - void (*set_debounce)(void __iomem *pio, unsigned mask, bool in_on, u32 div); - bool (*get_pulldown)(void __iomem *pio, unsigned pin); - void (*set_pulldown)(void __iomem *pio, unsigned mask, bool in_on); - bool (*get_schmitt_trig)(void __iomem *pio, unsigned pin); - void (*disable_schmitt_trig)(void __iomem *pio, unsigned mask); -}; - -#ifdef CONFIG_CMD_AT91MUX -static unsigned at91_mux_get_pullup(void __iomem *pio, unsigned pin) -{ - return (__raw_readl(pio + PIO_PUSR) >> pin) & 0x1; -} - -static unsigned at91_mux_get_multidrive(void __iomem *pio, unsigned pin) -{ - return (__raw_readl(pio + PIO_MDSR) >> pin) & 0x1; -} - -static enum at91_mux at91_mux_pio3_get_periph(void __iomem *pio, unsigned mask) -{ - unsigned select; - - if (__raw_readl(pio + PIO_PSR) & mask) - return AT91_MUX_GPIO; - - select = !!(__raw_readl(pio + PIO_ABCDSR1) & mask); - select |= (!!(__raw_readl(pio + PIO_ABCDSR2) & mask) << 1); - - return select + 1; -} - -static enum at91_mux at91_mux_get_periph(void __iomem *pio, unsigned mask) -{ - unsigned select; - - if (__raw_readl(pio + PIO_PSR) & mask) - return AT91_MUX_GPIO; - - select = __raw_readl(pio + PIO_ABSR) & mask; - - return select + 1; -} - -static bool at91_mux_get_deglitch(void __iomem *pio, unsigned pin) -{ - return (__raw_readl(pio + PIO_IFSR) >> pin) & 0x1; -} - -static bool at91_mux_pio3_get_debounce(void __iomem *pio, unsigned pin, u32 *div) -{ - *div = __raw_readl(pio + PIO_SCDR); - - return (__raw_readl(pio + PIO_IFSCSR) >> pin) & 0x1; -} - -static bool at91_mux_pio3_get_pulldown(void __iomem *pio, unsigned pin) -{ - return (__raw_readl(pio + PIO_PPDSR) >> pin) & 0x1; -} - -static bool at91_mux_pio3_get_schmitt_trig(void __iomem *pio, unsigned pin) -{ - return (__raw_readl(pio + PIO_SCHMITT) >> pin) & 0x1; -} -#else -#define at91_mux_get_periph NULL -#define at91_mux_pio3_get_periph NULL -#define at91_mux_get_deglitch NULL -#define at91_mux_pio3_get_debounce NULL -#define at91_mux_pio3_get_pulldown NULL -#define at91_mux_pio3_get_schmitt_trig NULL -#endif - -static struct at91_pinctrl_mux_ops at91rm9200_ops = { - .get_periph = at91_mux_get_periph, - .mux_A_periph = at91_mux_set_A_periph, - .mux_B_periph = at91_mux_set_B_periph, - .get_deglitch = at91_mux_get_deglitch, - .set_deglitch = at91_mux_set_deglitch, -}; - -static struct at91_pinctrl_mux_ops at91sam9x5_ops = { - .get_periph = at91_mux_pio3_get_periph, - .mux_A_periph = at91_mux_pio3_set_A_periph, - .mux_B_periph = at91_mux_pio3_set_B_periph, - .mux_C_periph = at91_mux_pio3_set_C_periph, - .mux_D_periph = at91_mux_pio3_set_D_periph, - .get_deglitch = at91_mux_get_deglitch, - .set_deglitch = at91_mux_pio3_set_deglitch, - .get_debounce = at91_mux_pio3_get_debounce, - .set_debounce = at91_mux_pio3_set_debounce, - .get_pulldown = at91_mux_pio3_get_pulldown, - .set_pulldown = at91_mux_pio3_set_pulldown, - .get_schmitt_trig = at91_mux_pio3_get_schmitt_trig, - .disable_schmitt_trig = at91_mux_pio3_disable_schmitt_trig, -}; - -int at91_mux_pin(unsigned pin, enum at91_mux mux, int use_pullup) -{ - struct at91_gpio_chip *at91_gpio = pin_to_controller(pin); - void __iomem *pio; - struct device_d *dev; - unsigned mask = pin_to_mask(pin); - int bank = pin_to_bank(pin); - - if (!at91_gpio) - return -EINVAL; - - pio = at91_gpio->regbase; - if (!pio) - return -EINVAL; - - dev = at91_gpio->chip.dev; - at91_mux_disable_interrupt(pio, mask); - - pin %= MAX_NB_GPIO_PER_BANK; - if (mux) { - dev_dbg(dev, "pio%c%d configured as periph%c with pullup = %d\n", - bank + 'A', pin, mux - 1 + 'A', use_pullup); - } else { - dev_dbg(dev, "pio%c%d configured as gpio with pullup = %d\n", - bank + 'A', pin, use_pullup); - } - - switch(mux) { - case AT91_MUX_GPIO: - at91_mux_gpio_enable(pio, mask); - break; - case AT91_MUX_PERIPH_A: - at91_gpio->ops->mux_A_periph(pio, mask); - break; - case AT91_MUX_PERIPH_B: - at91_gpio->ops->mux_B_periph(pio, mask); - break; - case AT91_MUX_PERIPH_C: - if (!at91_gpio->ops->mux_C_periph) - return -EINVAL; - at91_gpio->ops->mux_C_periph(pio, mask); - break; - case AT91_MUX_PERIPH_D: - if (!at91_gpio->ops->mux_D_periph) - return -EINVAL; - at91_gpio->ops->mux_D_periph(pio, mask); - break; - } - if (mux) - at91_mux_gpio_disable(pio, mask); - - if (use_pullup >= 0) - at91_mux_set_pullup(pio, mask, use_pullup); - - return 0; -} -EXPORT_SYMBOL(at91_mux_pin); - -/* - * mux the pin to the gpio controller (instead of "A" or "B" peripheral), and - * configure it for an input. - */ -int at91_set_gpio_input(unsigned pin, int use_pullup) -{ - struct at91_gpio_chip *at91_gpio = pin_to_controller(pin); - void __iomem *pio = at91_gpio->regbase; - unsigned mask = pin_to_mask(pin); - int ret; - - ret = at91_mux_pin(pin, AT91_MUX_GPIO, use_pullup); - if (ret) - return ret; - - dev_dbg(at91_gpio->chip.dev, "pio%c%d configured as input\n", - pin_to_bank(pin) + 'A', pin_to_bank_offset(pin)); - - at91_mux_gpio_input(pio, mask, true); - - return 0; -} - -/* - * mux the pin to the gpio controller (instead of "A" or "B" peripheral), - * and configure it for an output. - */ -int at91_set_gpio_output(unsigned pin, int value) -{ - struct at91_gpio_chip *at91_gpio = pin_to_controller(pin); - void __iomem *pio = at91_gpio->regbase; - unsigned mask = pin_to_mask(pin); - int ret; - - ret = at91_mux_pin(pin, AT91_MUX_GPIO, -1); - if (ret) - return ret; - - dev_dbg(at91_gpio->chip.dev, "pio%c%d configured as output val = %d\n", - pin_to_bank(pin) + 'A', pin_to_bank_offset(pin), value); - - at91_mux_gpio_input(pio, mask, false); - at91_mux_gpio_set(pio, mask, value); - return 0; -} -EXPORT_SYMBOL(at91_set_gpio_output); - -/* - * enable/disable the glitch filter; mostly used with IRQ handling. - */ -int at91_set_deglitch(unsigned pin, int is_on) -{ - struct at91_gpio_chip *at91_gpio = pin_to_controller(pin); - void __iomem *pio = at91_gpio->regbase; - unsigned mask = pin_to_mask(pin); - - if (!pio) - return -EINVAL; - - at91_gpio->ops->set_deglitch(pio, mask, is_on); - return 0; -} -EXPORT_SYMBOL(at91_set_deglitch); - -/* - * enable/disable the debounce filter; - */ -int at91_set_debounce(unsigned pin, int is_on, int div) -{ - struct at91_gpio_chip *at91_gpio = pin_to_controller(pin); - void __iomem *pio = at91_gpio->regbase; - unsigned mask = pin_to_mask(pin); - - if (!pio || !at91_gpio->ops->set_debounce) - return -EINVAL; - - at91_gpio->ops->set_debounce(pio, mask, is_on, div); - return 0; -} -EXPORT_SYMBOL(at91_set_debounce); - -/* - * enable/disable the multi-driver; This is only valid for output and - * allows the output pin to run as an open collector output. - */ -int at91_set_multi_drive(unsigned pin, int is_on) -{ - struct at91_gpio_chip *at91_gpio = pin_to_controller(pin); - void __iomem *pio = at91_gpio->regbase; - unsigned mask = pin_to_mask(pin); - - if (!pio) - return -EINVAL; - - at91_mux_set_multidrive(pio, mask, is_on); - return 0; -} -EXPORT_SYMBOL(at91_set_multi_drive); - -/* - * enable/disable the pull-down. - * If pull-up already enabled while calling the function, we disable it. - */ -int at91_set_pulldown(unsigned pin, int is_on) -{ - struct at91_gpio_chip *at91_gpio = pin_to_controller(pin); - void __iomem *pio = at91_gpio->regbase; - unsigned mask = pin_to_mask(pin); - - if (!pio || !at91_gpio->ops->set_pulldown) - return -EINVAL; - - /* Disable pull-up anyway */ - at91_mux_set_pullup(pio, mask, 0); - at91_gpio->ops->set_pulldown(pio, mask, is_on); - return 0; -} -EXPORT_SYMBOL(at91_set_pulldown); - -/* - * disable Schmitt trigger - */ -int at91_disable_schmitt_trig(unsigned pin) -{ - struct at91_gpio_chip *at91_gpio = pin_to_controller(pin); - void __iomem *pio = at91_gpio->regbase; - unsigned mask = pin_to_mask(pin); - - if (!pio || !at91_gpio->ops->disable_schmitt_trig) - return -EINVAL; - - at91_gpio->ops->disable_schmitt_trig(pio, mask); - return 0; -} -EXPORT_SYMBOL(at91_disable_schmitt_trig); - -#ifdef CONFIG_CMD_AT91MUX -static void at91mux_printf_mode(unsigned bank, unsigned pin) -{ - struct at91_gpio_chip *at91_gpio = &gpio_chip[bank]; - void __iomem *pio = at91_gpio->regbase; - enum at91_mux mode; - u32 pdsr; - - unsigned mask = pin_to_mask(pin); - - mode = at91_gpio->ops->get_periph(pio, mask); - - if (mode == AT91_MUX_GPIO) { - pdsr = __raw_readl(pio + PIO_PDSR); - - printf("[gpio] %s", pdsr & mask ? "set" : "clear"); - } else { - printf("[periph %c]", mode + 'A' - 1); - } -} - -static void at91mux_dump_config(void) -{ - int bank, j; - - /* print heading */ - printf("Pin\t"); - for (bank = 0; bank < gpio_banks; bank++) { - printf("PIO%c\t\t", 'A' + bank); - }; - printf("\n\n"); - - /* print pin status */ - for (j = 0; j < 32; j++) { - printf("%i:\t", j); - - for (bank = 0; bank < gpio_banks; bank++) { - at91mux_printf_mode(bank, j); - - printf("\t"); - } - - printf("\n"); - } -} - -static void at91mux_print_en_disable(const char *str, bool is_on) -{ - printf("%s = ", str); - - if (is_on) - printf("enable\n"); - else - printf("disable\n"); -} - -static void at91mux_dump_pio_config(unsigned bank, unsigned pin) -{ - struct at91_gpio_chip *at91_gpio = &gpio_chip[bank]; - void __iomem *pio = at91_gpio->regbase; - u32 div; - - printf("pio%c%u configuration\n\n", bank + 'A', pin); - - at91mux_printf_mode(bank, pin); - printf("\n"); - - at91mux_print_en_disable("multidrive", - at91_mux_get_multidrive(pio, pin)); - - at91mux_print_en_disable("pullup", - at91_mux_get_pullup(pio, pin)); - - if (at91_gpio->ops->get_deglitch) - at91mux_print_en_disable("degitch", - at91_gpio->ops->get_deglitch(pio, pin)); - - if (at91_gpio->ops->get_debounce) { - printf("debounce = "); - if (at91_gpio->ops->get_debounce(pio, pin, &div)) - printf("enable at %d\n", div); - else - printf("disable\n"); - } - - if (at91_gpio->ops->get_pulldown) - at91mux_print_en_disable("pulldown", - at91_gpio->ops->get_pulldown(pio, pin)); - - if (at91_gpio->ops->get_schmitt_trig) - at91mux_print_en_disable("schmitt trigger", - !at91_gpio->ops->get_schmitt_trig(pio, pin)); -} - -static int do_at91mux(int argc, char *argv[]) -{ - int opt; - unsigned bank = 0; - unsigned pin = 0; - - if (argc < 2) { - at91mux_dump_config(); - return 0; - } - - while ((opt = getopt(argc, argv, "b:p:")) > 0) { - switch (opt) { - case 'b': - bank = simple_strtoul(optarg, NULL, 10); - break; - case 'p': - pin = simple_strtoul(optarg, NULL, 10); - break; - } - } - - if (bank >= gpio_banks) { - printf("bank %c >= supported %c banks\n", bank + 'A', - gpio_banks + 'A'); - return 1; - } - - if (pin >= 32) { - printf("pin %u >= supported %d pins\n", pin, 32); - return 1; - } - - at91mux_dump_pio_config(bank, pin); - - return 0; -} - -BAREBOX_CMD_HELP_START(at91mux) -BAREBOX_CMD_HELP_TEXT("Dump current MUX configuration. If a BANK or PIN has been") -BAREBOX_CMD_HELP_TEXT("specified dump pin details.") -BAREBOX_CMD_HELP_TEXT("") -BAREBOX_CMD_HELP_TEXT("Options:") -BAREBOX_CMD_HELP_OPT ("-p PIN", "pin number") -BAREBOX_CMD_HELP_OPT ("-b BANK", "bank number") -BAREBOX_CMD_HELP_END - -BAREBOX_CMD_START(at91mux) - .cmd = do_at91mux, - BAREBOX_CMD_DESC("list MUX configuration") - BAREBOX_CMD_OPTS("[-pb]") - BAREBOX_CMD_GROUP(CMD_GRP_INFO) - BAREBOX_CMD_HELP(cmd_at91mux_help) - BAREBOX_CMD_COMPLETE(empty_complete) -BAREBOX_CMD_END -#endif -/*--------------------------------------------------------------------------*/ - -static int at91_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); - void __iomem *pio = at91_gpio->regbase; - unsigned mask = 1 << offset; - - return at91_mux_gpio_get(pio, mask); -} - -static void at91_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); - void __iomem *pio = at91_gpio->regbase; - unsigned mask = 1 << offset; - - at91_mux_gpio_set(pio, mask, value); -} - -static int at91_gpio_direction_output(struct gpio_chip *chip, unsigned offset, - int value) -{ - struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); - void __iomem *pio = at91_gpio->regbase; - unsigned mask = 1 << offset; - - at91_mux_gpio_set(pio, mask, value); - __raw_writel(mask, pio + PIO_OER); - - return 0; -} - -static int at91_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ - struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); - void __iomem *pio = at91_gpio->regbase; - unsigned mask = 1 << offset; - - __raw_writel(mask, pio + PIO_ODR); - - return 0; -} - -static int at91_gpio_request(struct gpio_chip *chip, unsigned offset) -{ - struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); - void __iomem *pio = at91_gpio->regbase; - unsigned mask = 1 << offset; - - dev_dbg(chip->dev, "%s:%d pio%c%d(%d)\n", __func__, __LINE__, - 'A' + pin_to_bank(chip->base), offset, chip->base + offset); - at91_mux_gpio_enable(pio, mask); - - return 0; -} - -static void at91_gpio_free(struct gpio_chip *chip, unsigned offset) -{ - dev_dbg(chip->dev, "%s:%d pio%c%d(%d)\n", __func__, __LINE__, - 'A' + pin_to_bank(chip->base), offset, chip->base + offset); -} - -static struct gpio_ops at91_gpio_ops = { - .request = at91_gpio_request, - .free = at91_gpio_free, - .direction_input = at91_gpio_direction_input, - .direction_output = at91_gpio_direction_output, - .get = at91_gpio_get, - .set = at91_gpio_set, -}; - -static int at91_gpio_probe(struct device_d *dev) -{ - struct at91_gpio_chip *at91_gpio; - struct clk *clk; - int ret; - - BUG_ON(dev->id > MAX_GPIO_BANKS); - - at91_gpio = &gpio_chip[dev->id]; - - ret = dev_get_drvdata(dev, (unsigned long *)&at91_gpio->ops); - if (ret) { - dev_err(dev, "dev_get_drvdata failed: %d\n", ret); - return ret; - } - - clk = clk_get(dev, NULL); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - dev_err(dev, "clock not found: %d\n", ret); - return ret; - } - - ret = clk_enable(clk); - if (ret < 0) { - dev_err(dev, "clock failed to enable: %d\n", ret); - clk_put(clk); - return ret; - } - - gpio_banks = max(gpio_banks, dev->id + 1); - at91_gpio->regbase = dev_request_mem_region(dev, 0); - - at91_gpio->chip.ops = &at91_gpio_ops; - at91_gpio->chip.ngpio = MAX_NB_GPIO_PER_BANK; - at91_gpio->chip.dev = dev; - at91_gpio->chip.base = dev->id * MAX_NB_GPIO_PER_BANK; - - ret = gpiochip_add(&at91_gpio->chip); - if (ret) { - dev_err(dev, "couldn't add gpiochip, ret = %d\n", ret); - return ret; - } - - return 0; -} - -static struct platform_device_id at91_gpio_ids[] = { - { - .name = "at91rm9200-gpio", - .driver_data = (unsigned long)&at91rm9200_ops, - }, { - .name = "at91sam9x5-gpio", - .driver_data = (unsigned long)&at91sam9x5_ops, - }, { - /* sentinel */ - }, -}; - -static struct driver_d at91_gpio_driver = { - .name = "at91-gpio", - .probe = at91_gpio_probe, - .id_table = at91_gpio_ids, -}; - -static int at91_gpio_init(void) -{ - return platform_driver_register(&at91_gpio_driver); -} -postcore_initcall(at91_gpio_init); diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 83ba2b6..398b931 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -7,6 +7,12 @@ from the devicetree. Legacy drivers here may not need this core support but instead provide their own SoC specific APIs +config PINCTRL_AT91 + select PINCTRL if OFDEVICE + bool + help + The pinmux controller found on AT91 SoCs. + config PINCTRL_IMX_IOMUX_V1 select PINCTRL if OFDEVICE bool diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 566ba11..3ea8649 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_PINCTRL) += pinctrl.o +obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o obj-$(CONFIG_PINCTRL_IMX_IOMUX_V1) += imx-iomux-v1.o obj-$(CONFIG_PINCTRL_IMX_IOMUX_V2) += imx-iomux-v2.o obj-$(CONFIG_PINCTRL_IMX_IOMUX_V3) += imx-iomux-v3.o diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c new file mode 100644 index 0000000..e212f7a --- /dev/null +++ b/drivers/pinctrl/pinctrl-at91.c @@ -0,0 +1,683 @@ +/* + * Copyright (C) 2005 HP Labs + * Copyright (C) 2011-2012 Jean-Christophe PLAGNIOL-VILLARD + * Copyright (C) 2014 Raphaƫl Poggi + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +struct at91_pinctrl { + struct pinctrl_device pctl; + struct at91_pinctrl_mux_ops *ops; +}; + +struct at91_gpio_chip { + struct gpio_chip chip; + void __iomem *regbase; /* PIO bank virtual address */ + struct at91_pinctrl_mux_ops *ops; /* ops */ +}; + +enum at91_mux { + AT91_MUX_GPIO = 0, + AT91_MUX_PERIPH_A = 1, + AT91_MUX_PERIPH_B = 2, + AT91_MUX_PERIPH_C = 3, + AT91_MUX_PERIPH_D = 4, +}; + +#define MAX_GPIO_BANKS 5 +#define to_at91_pinctrl(c) container_of(c, struct at91_pinctrl, pctl); +#define to_at91_gpio_chip(c) container_of(c, struct at91_gpio_chip, chip) + +#define PULL_UP (1 << 0) +#define MULTI_DRIVE (1 << 1) +#define DEGLITCH (1 << 2) +#define PULL_DOWN (1 << 3) +#define DIS_SCHMIT (1 << 4) +#define DEBOUNCE (1 << 16) +#define DEBOUNCE_VAL_SHIFT 17 +#define DEBOUNCE_VAL (0x3fff << DEBOUNCE_VAL_SHIFT) + +static int gpio_banks; + +static struct at91_gpio_chip gpio_chip[MAX_GPIO_BANKS]; + +static inline void __iomem *pin_to_controller(unsigned pin) +{ + pin /= MAX_NB_GPIO_PER_BANK; + if (likely(pin < gpio_banks)) + return gpio_chip[pin].regbase; + + return NULL; +} + +/** + * struct at91_pinctrl_mux_ops - describes an At91 mux ops group + * on new IP with support for periph C and D the way to mux in + * periph A and B has changed + * So provide the right call back + * if not present means the IP does not support it + * @get_periph: return the periph mode configured + * @mux_A_periph: mux as periph A + * @mux_B_periph: mux as periph B + * @mux_C_periph: mux as periph C + * @mux_D_periph: mux as periph D + * @set_deglitch: enable/disable deglitch + * @set_debounce: enable/disable debounce + * @set_pulldown: enable/disable pulldown + * @disable_schmitt_trig: disable schmitt trigger + */ +struct at91_pinctrl_mux_ops { + enum at91_mux (*get_periph)(void __iomem *pio, unsigned mask); + void (*mux_A_periph)(void __iomem *pio, unsigned mask); + void (*mux_B_periph)(void __iomem *pio, unsigned mask); + void (*mux_C_periph)(void __iomem *pio, unsigned mask); + void (*mux_D_periph)(void __iomem *pio, unsigned mask); + bool (*get_deglitch)(void __iomem *pio, unsigned pin); + void (*set_deglitch)(void __iomem *pio, unsigned mask, bool in_on); + bool (*get_debounce)(void __iomem *pio, unsigned pin, u32 *div); + void (*set_debounce)(void __iomem *pio, unsigned mask, bool in_on, u32 div); + bool (*get_pulldown)(void __iomem *pio, unsigned pin); + void (*set_pulldown)(void __iomem *pio, unsigned mask, bool in_on); + bool (*get_schmitt_trig)(void __iomem *pio, unsigned pin); + void (*disable_schmitt_trig)(void __iomem *pio, unsigned mask); +}; + +int at91_mux_pin(unsigned pin, enum at91_mux mux, int use_pullup) +{ + struct at91_gpio_chip *at91_gpio = pin_to_controller(pin); + void __iomem *pio; + struct device_d *dev; + unsigned mask = pin_to_mask(pin); + int bank = pin_to_bank(pin); + + if (!at91_gpio) + return -EINVAL; + + pio = at91_gpio->regbase; + if (!pio) + return -EINVAL; + + dev = at91_gpio->chip.dev; + at91_mux_disable_interrupt(pio, mask); + + pin %= MAX_NB_GPIO_PER_BANK; + if (mux) { + dev_dbg(dev, "pio%c%d configured as periph%c with pullup = %d\n", + bank + 'A', pin, mux - 1 + 'A', use_pullup); + } else { + dev_dbg(dev, "pio%c%d configured as gpio with pullup = %d\n", + bank + 'A', pin, use_pullup); + } + + switch(mux) { + case AT91_MUX_GPIO: + at91_mux_gpio_enable(pio, mask); + break; + case AT91_MUX_PERIPH_A: + at91_gpio->ops->mux_A_periph(pio, mask); + break; + case AT91_MUX_PERIPH_B: + at91_gpio->ops->mux_B_periph(pio, mask); + break; + case AT91_MUX_PERIPH_C: + if (!at91_gpio->ops->mux_C_periph) + return -EINVAL; + at91_gpio->ops->mux_C_periph(pio, mask); + break; + case AT91_MUX_PERIPH_D: + if (!at91_gpio->ops->mux_D_periph) + return -EINVAL; + at91_gpio->ops->mux_D_periph(pio, mask); + break; + } + if (mux) + at91_mux_gpio_disable(pio, mask); + + if (use_pullup >= 0) + at91_mux_set_pullup(pio, mask, use_pullup); + + return 0; +} +EXPORT_SYMBOL(at91_mux_pin); + +/* + * mux the pin to the gpio controller (instead of "A" or "B" peripheral), and + * configure it for an input. + */ +int at91_set_gpio_input(unsigned pin, int use_pullup) +{ + struct at91_gpio_chip *at91_gpio = pin_to_controller(pin); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = pin_to_mask(pin); + int ret; + + ret = at91_mux_pin(pin, AT91_MUX_GPIO, use_pullup); + if (ret) + return ret; + + dev_dbg(at91_gpio->chip.dev, "pio%c%d configured as input\n", + pin_to_bank(pin) + 'A', pin_to_bank_offset(pin)); + + at91_mux_gpio_input(pio, mask, true); + + return 0; +} + +/* + * mux the pin to the gpio controller (instead of "A" or "B" peripheral), + * and configure it for an output. + */ +int at91_set_gpio_output(unsigned pin, int value) +{ + struct at91_gpio_chip *at91_gpio = pin_to_controller(pin); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = pin_to_mask(pin); + int ret; + + ret = at91_mux_pin(pin, AT91_MUX_GPIO, -1); + if (ret) + return ret; + + dev_dbg(at91_gpio->chip.dev, "pio%c%d configured as output val = %d\n", + pin_to_bank(pin) + 'A', pin_to_bank_offset(pin), value); + + at91_mux_gpio_input(pio, mask, false); + at91_mux_gpio_set(pio, mask, value); + return 0; +} +EXPORT_SYMBOL(at91_set_gpio_output); + +/* + * enable/disable the glitch filter; mostly used with IRQ handling. + */ +int at91_set_deglitch(unsigned pin, int is_on) +{ + struct at91_gpio_chip *at91_gpio = pin_to_controller(pin); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = pin_to_mask(pin); + + if (!pio) + return -EINVAL; + + at91_gpio->ops->set_deglitch(pio, mask, is_on); + return 0; +} +EXPORT_SYMBOL(at91_set_deglitch); + +/* + * enable/disable the debounce filter; + */ +int at91_set_debounce(unsigned pin, int is_on, int div) +{ + struct at91_gpio_chip *at91_gpio = pin_to_controller(pin); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = pin_to_mask(pin); + + if (!pio || !at91_gpio->ops->set_debounce) + return -EINVAL; + + at91_gpio->ops->set_debounce(pio, mask, is_on, div); + return 0; +} +EXPORT_SYMBOL(at91_set_debounce); + +/* + * enable/disable the multi-driver; This is only valid for output and + * allows the output pin to run as an open collector output. + */ +int at91_set_multi_drive(unsigned pin, int is_on) +{ + struct at91_gpio_chip *at91_gpio = pin_to_controller(pin); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = pin_to_mask(pin); + + if (!pio) + return -EINVAL; + + at91_mux_set_multidrive(pio, mask, is_on); + return 0; +} +EXPORT_SYMBOL(at91_set_multi_drive); + +/* + * enable/disable the pull-down. + * If pull-up already enabled while calling the function, we disable it. + */ +int at91_set_pulldown(unsigned pin, int is_on) +{ + struct at91_gpio_chip *at91_gpio = pin_to_controller(pin); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = pin_to_mask(pin); + + if (!pio || !at91_gpio->ops->set_pulldown) + return -EINVAL; + + /* Disable pull-up anyway */ + at91_mux_set_pullup(pio, mask, 0); + at91_gpio->ops->set_pulldown(pio, mask, is_on); + return 0; +} +EXPORT_SYMBOL(at91_set_pulldown); + +/* + * disable Schmitt trigger + */ +int at91_disable_schmitt_trig(unsigned pin) +{ + struct at91_gpio_chip *at91_gpio = pin_to_controller(pin); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = pin_to_mask(pin); + + if (!pio || !at91_gpio->ops->disable_schmitt_trig) + return -EINVAL; + + at91_gpio->ops->disable_schmitt_trig(pio, mask); + return 0; +} +EXPORT_SYMBOL(at91_disable_schmitt_trig); + +static enum at91_mux at91_mux_pio3_get_periph(void __iomem *pio, unsigned mask) +{ + unsigned select; + + if (__raw_readl(pio + PIO_PSR) & mask) + return AT91_MUX_GPIO; + + select = !!(__raw_readl(pio + PIO_ABCDSR1) & mask); + select |= (!!(__raw_readl(pio + PIO_ABCDSR2) & mask) << 1); + + return select + 1; +} + +static enum at91_mux at91_mux_get_periph(void __iomem *pio, unsigned mask) +{ + unsigned select; + + if (__raw_readl(pio + PIO_PSR) & mask) + return AT91_MUX_GPIO; + + select = __raw_readl(pio + PIO_ABSR) & mask; + + return select + 1; +} + +static bool at91_mux_get_deglitch(void __iomem *pio, unsigned pin) +{ + return (__raw_readl(pio + PIO_IFSR) >> pin) & 0x1; +} + +static bool at91_mux_pio3_get_debounce(void __iomem *pio, unsigned pin, u32 *div) +{ + *div = __raw_readl(pio + PIO_SCDR); + + return (__raw_readl(pio + PIO_IFSCSR) >> pin) & 0x1; +} + +static bool at91_mux_pio3_get_pulldown(void __iomem *pio, unsigned pin) +{ + return (__raw_readl(pio + PIO_PPDSR) >> pin) & 0x1; +} + +static bool at91_mux_pio3_get_schmitt_trig(void __iomem *pio, unsigned pin) +{ + return (__raw_readl(pio + PIO_SCHMITT) >> pin) & 0x1; +} + +static struct at91_pinctrl_mux_ops at91rm9200_ops = { + .get_periph = at91_mux_get_periph, + .mux_A_periph = at91_mux_set_A_periph, + .mux_B_periph = at91_mux_set_B_periph, + .get_deglitch = at91_mux_get_deglitch, + .set_deglitch = at91_mux_set_deglitch, +}; + +static struct at91_pinctrl_mux_ops at91sam9x5_ops = { + .get_periph = at91_mux_pio3_get_periph, + .mux_A_periph = at91_mux_pio3_set_A_periph, + .mux_B_periph = at91_mux_pio3_set_B_periph, + .mux_C_periph = at91_mux_pio3_set_C_periph, + .mux_D_periph = at91_mux_pio3_set_D_periph, + .get_deglitch = at91_mux_get_deglitch, + .set_deglitch = at91_mux_pio3_set_deglitch, + .get_debounce = at91_mux_pio3_get_debounce, + .set_debounce = at91_mux_pio3_set_debounce, + .get_pulldown = at91_mux_pio3_get_pulldown, + .set_pulldown = at91_mux_pio3_set_pulldown, + .get_schmitt_trig = at91_mux_pio3_get_schmitt_trig, + .disable_schmitt_trig = at91_mux_pio3_disable_schmitt_trig, +}; + +static struct of_device_id at91_pinctrl_dt_ids[] = { + { + .compatible = "atmel,at91rm9200-pinctrl", + .data = (unsigned long)&at91rm9200_ops, + }, { + .compatible = "atmel,at91sam9x5-pinctrl", + .data = (unsigned long)&at91sam9x5_ops, + }, { + /* sentinel */ + } +}; + +static struct at91_pinctrl_mux_ops *at91_pinctrl_get_driver_data(struct device_d *dev) +{ + struct at91_pinctrl_mux_ops *ops_data = NULL; + int rc; + + if (dev->device_node) { + const struct of_device_id *match; + match = of_match_node(at91_pinctrl_dt_ids, dev->device_node); + if (!match) + ops_data = NULL; + else + ops_data = (struct at91_pinctrl_mux_ops *)match->data; + } else { + rc = dev_get_drvdata(dev, (unsigned long *)&ops_data); + if (rc) + ops_data = NULL; + } + + return ops_data; +} + +static int at91_pinctrl_set_conf(struct at91_pinctrl *info, unsigned int pin_num, unsigned int mux, unsigned int conf) +{ + unsigned int mask; + void __iomem *pio; + + pio = pin_to_controller(pin_num); + mask = pin_to_mask(pin_num); + + if (conf & PULL_UP && conf & PULL_DOWN) + return -EINVAL; + + at91_mux_set_pullup(pio, mask, conf & PULL_UP); + at91_mux_set_multidrive(pio, mask, conf & MULTI_DRIVE); + if (info->ops->set_deglitch) + info->ops->set_deglitch(pio, mask, conf & DEGLITCH); + if (info->ops->set_debounce) + info->ops->set_debounce(pio, mask, conf & DEBOUNCE, + (conf & DEBOUNCE_VAL) >> DEBOUNCE_VAL_SHIFT); + if (info->ops->set_pulldown) + info->ops->set_pulldown(pio, mask, conf & PULL_DOWN); + if (info->ops->disable_schmitt_trig && conf & DIS_SCHMIT) + info->ops->disable_schmitt_trig(pio, mask); + + return 0; +} + +static int at91_pinctrl_set_state(struct pinctrl_device *pdev, struct device_node *np) +{ + struct at91_pinctrl *info; + const __be32 *list; + int i, size; + int ret = 0; + int bank_num, pin_num, mux, conf; + + info = to_at91_pinctrl(pdev); + + list = of_get_property(np, "atmel,pins", &size); + size /= sizeof(*list); + + if (!size || size % 4) { + dev_err(pdev->dev, "wrong pins number or pins and configs should be by 4\n"); + return -EINVAL; + } + + for (i = 0; i < size; i += 4) { + bank_num = be32_to_cpu(*list++); + pin_num = be32_to_cpu(*list++); + mux = be32_to_cpu(*list++); + conf = be32_to_cpu(*list++); + + ret = at91_mux_pin(pin_num, mux, conf & PULL_UP); + if (ret) { + dev_err(pdev->dev, "failed to mux pin %d\n", pin_num); + return ret; + } + + ret = at91_pinctrl_set_conf(info, pin_num, mux, conf); + if (ret) { + dev_err(pdev->dev, "failed to set conf on pin %d\n", pin_num); + return ret; + } + } + + return ret; +} + +static struct pinctrl_ops at91_pinctrl_ops = { + .set_state = at91_pinctrl_set_state, +}; + +static int at91_pinctrl_probe(struct device_d *dev) +{ + struct at91_pinctrl *info; + int ret; + + if (!IS_ENABLED(CONFIG_PINCTRL)) + return 0; + + info = xzalloc(sizeof(struct at91_pinctrl)); + + info->ops = at91_pinctrl_get_driver_data(dev); + if (!info->ops) { + dev_err(dev, "failed to retrieve driver data\n"); + return -ENODEV; + } + + info->pctl.dev = dev; + info->pctl.ops = &at91_pinctrl_ops; + + ret = pinctrl_register(&info->pctl); + if (ret) + return ret; + + dev_info(dev, "AT91 pinctrl registered\n"); + + return 0; +} + +static struct platform_device_id at91_pinctrl_ids[] = { + { + .name = "at91rm9200-pinctrl", + .driver_data = (unsigned long)&at91rm9200_ops, + }, { + .name = "at91sam9x5-pinctrl", + .driver_data = (unsigned long)&at91sam9x5_ops, + }, { + /* sentinel */ + }, +}; + +static struct driver_d at91_pinctrl_driver = { + .name = "pinctrl-at91", + .probe = at91_pinctrl_probe, + .id_table = at91_pinctrl_ids, + .of_compatible = DRV_OF_COMPAT(at91_pinctrl_dt_ids), +}; + +static int at91_pinctrl_init(void) +{ + return platform_driver_register(&at91_pinctrl_driver); +} +coredevice_initcall(at91_pinctrl_init); + +static int at91_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << offset; + + return at91_mux_gpio_get(pio, mask); +} + +static void at91_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << offset; + + at91_mux_gpio_set(pio, mask, value); +} + +static int at91_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << offset; + + at91_mux_gpio_set(pio, mask, value); + __raw_writel(mask, pio + PIO_OER); + + return 0; +} + +static int at91_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << offset; + + __raw_writel(mask, pio + PIO_ODR); + + return 0; +} + +static int at91_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << offset; + + dev_dbg(chip->dev, "%s:%d pio%c%d(%d)\n", __func__, __LINE__, + 'A' + pin_to_bank(chip->base), offset, chip->base + offset); + at91_mux_gpio_enable(pio, mask); + + return 0; +} + +static void at91_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + dev_dbg(chip->dev, "%s:%d pio%c%d(%d)\n", __func__, __LINE__, + 'A' + pin_to_bank(chip->base), offset, chip->base + offset); +} + +static struct gpio_ops at91_gpio_ops = { + .request = at91_gpio_request, + .free = at91_gpio_free, + .direction_input = at91_gpio_direction_input, + .direction_output = at91_gpio_direction_output, + .get = at91_gpio_get, + .set = at91_gpio_set, +}; + +static struct of_device_id at91_gpio_dt_ids[] = { + { + .compatible = "atmel,at91rm9200-gpio", + }, { + .compatible = "atmel,at91sam9x5-gpio", + }, { + /* sentinel */ + }, +}; + +static int at91_gpio_probe(struct device_d *dev) +{ + struct at91_gpio_chip *at91_gpio; + struct clk *clk; + int ret; + int alias_idx = of_alias_get_id(dev->device_node, "gpio"); + + BUG_ON(dev->id > MAX_GPIO_BANKS); + + at91_gpio = &gpio_chip[alias_idx]; + + clk = clk_get(dev, NULL); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + dev_err(dev, "clock not found: %d\n", ret); + return ret; + } + + ret = clk_enable(clk); + if (ret < 0) { + dev_err(dev, "clock failed to enable: %d\n", ret); + clk_put(clk); + return ret; + } + + gpio_banks = max(gpio_banks, alias_idx + 1); + at91_gpio->regbase = dev_request_mem_region(dev, 0); + if (!at91_gpio->regbase) + return -EBUSY; + + at91_gpio->chip.ops = &at91_gpio_ops; + at91_gpio->chip.ngpio = MAX_NB_GPIO_PER_BANK; + at91_gpio->chip.dev = dev; + at91_gpio->chip.base = dev->id * MAX_NB_GPIO_PER_BANK; + + ret = gpiochip_add(&at91_gpio->chip); + if (ret) { + dev_err(dev, "couldn't add gpiochip, ret = %d\n", ret); + return ret; + } + + dev_info(dev, "AT91 gpio driver registered\n"); + + return 0; +} + +static struct platform_device_id at91_gpio_ids[] = { + { + .name = "at91rm9200-gpio", + }, { + .name = "at91sam9x5-gpio", + }, { + /* sentinel */ + }, +}; + +static struct driver_d at91_gpio_driver = { + .name = "gpio-at91", + .probe = at91_gpio_probe, + .id_table = at91_gpio_ids, + .of_compatible = DRV_OF_COMPAT(at91_gpio_dt_ids), +}; + +static int at91_gpio_init(void) +{ + return platform_driver_register(&at91_gpio_driver); +} +coredevice_initcall(at91_gpio_init);