/* * Copyright (C) 2015 Broadcom Corporation * * 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 version 2. * * This program is distributed "as is" WITHOUT ANY WARRANTY of any * kind, whether express or implied; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include <arch/io.h> #include <console/console.h> #include <gpio.h> #include <stdlib.h> #include <delay.h> #define dev_dbg(chip, fmt, args...) printk(BIOS_DEBUG, "[%s] " fmt, \ chip->label, args) #define CYGNUS_GPIO_DATA_IN_OFFSET 0x00 #define CYGNUS_GPIO_DATA_OUT_OFFSET 0x04 #define CYGNUS_GPIO_OUT_EN_OFFSET 0x08 #define CYGNUS_GPIO_IN_TYPE_OFFSET 0x0c #define CYGNUS_GPIO_INT_DE_OFFSET 0x10 #define CYGNUS_GPIO_INT_EDGE_OFFSET 0x14 #define CYGNUS_GPIO_INT_MSK_OFFSET 0x18 #define CYGNUS_GPIO_INT_STAT_OFFSET 0x1c #define CYGNUS_GPIO_INT_MSTAT_OFFSET 0x20 #define CYGNUS_GPIO_INT_CLR_OFFSET 0x24 #define CYGNUS_GPIO_PAD_RES_OFFSET 0x34 #define CYGNUS_GPIO_RES_EN_OFFSET 0x38 /* drive strength control for ASIU GPIO */ #define CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58 /* drive strength control for CCM/CRMU (AON) GPIO */ #define CYGNUS_GPIO_DRV0_CTRL_OFFSET 0x00 #define GPIO_BANK_SIZE 0x200 #define NGPIOS_PER_BANK 32 #define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK) #define CYGNUS_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + (reg)) #define CYGNUS_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK) #define GPIO_DRV_STRENGTH_BIT_SHIFT 20 #define GPIO_DRV_STRENGTH_BITS 3 #define GPIO_DRV_STRENGTH_BIT_MASK ((1 << GPIO_DRV_STRENGTH_BITS) - 1) /* * Cygnus GPIO core * * @base: I/O register base for Cygnus GPIO controller * @io_ctrl: I/O register base for certain type of Cygnus GPIO controller that * has the PINCONF support implemented outside of the GPIO block * @num_banks: number of GPIO banks, each bank supports up to 32 GPIOs * @pinmux_is_supported: flag to indicate this GPIO controller contains pins * that can be individually muxed to GPIO * @pctl_priv: pointer to pinctrl handle */ struct cygnus_gpio { void *base; void *io_ctrl; const char *label; int gpio_base; u16 ngpio; unsigned num_banks; int pinmux_is_supported; void *pctl_priv; }; /* * GPIO cores table * * Cygnus has 3 gpio cores. The tables contains descriptors of those cores. */ struct cygnus_gpio cygnus_gpio_table[] = { { .base = (void *)0x03024800, .io_ctrl = (void *)0x03024008, .label = "gpio_crmu", .gpio_base = 170, .ngpio = 6, }, { .base = (void *)0x1800a000, .io_ctrl = (void *)0x0301d164, .label = "gpio_ccm", .gpio_base = 0, .ngpio = 24, }, { .base = (void *)0x180a5000, .label = "gpio_asiu", .gpio_base = 24, .ngpio = 146, .pinmux_is_supported = 1 } }; /* * Map a GPIO in the local gpio_chip pin space to a pin in the Cygnus IOMUX * pinctrl pin space */ struct cygnus_gpio_pin_range { unsigned offset; unsigned pin_base; unsigned num_pins; }; #define CYGNUS_PINRANGE(o, p, n) { .offset = o, .pin_base = p, .num_pins = n } /* * Pin mapping table for mapping local GPIO pins to Cygnus IOMUX pinctrl pins. * This is for ASIU gpio. The offset is based on ASIU gpios. */ static const struct cygnus_gpio_pin_range cygnus_gpio_pintable[] = { CYGNUS_PINRANGE(0, 42, 1), CYGNUS_PINRANGE(1, 44, 3), CYGNUS_PINRANGE(4, 48, 1), CYGNUS_PINRANGE(5, 50, 3), CYGNUS_PINRANGE(8, 126, 1), CYGNUS_PINRANGE(9, 155, 1), CYGNUS_PINRANGE(10, 152, 1), CYGNUS_PINRANGE(11, 154, 1), CYGNUS_PINRANGE(12, 153, 1), CYGNUS_PINRANGE(13, 127, 3), CYGNUS_PINRANGE(16, 140, 1), CYGNUS_PINRANGE(17, 145, 7), CYGNUS_PINRANGE(24, 130, 10), CYGNUS_PINRANGE(34, 141, 4), CYGNUS_PINRANGE(38, 54, 1), CYGNUS_PINRANGE(39, 56, 3), CYGNUS_PINRANGE(42, 60, 3), CYGNUS_PINRANGE(45, 64, 3), CYGNUS_PINRANGE(48, 68, 2), CYGNUS_PINRANGE(50, 84, 6), CYGNUS_PINRANGE(56, 94, 6), CYGNUS_PINRANGE(62, 72, 1), CYGNUS_PINRANGE(63, 70, 1), CYGNUS_PINRANGE(64, 80, 1), CYGNUS_PINRANGE(65, 74, 3), CYGNUS_PINRANGE(68, 78, 1), CYGNUS_PINRANGE(69, 82, 1), CYGNUS_PINRANGE(70, 156, 17), CYGNUS_PINRANGE(87, 104, 12), CYGNUS_PINRANGE(99, 102, 2), CYGNUS_PINRANGE(101, 90, 4), CYGNUS_PINRANGE(105, 116, 10), CYGNUS_PINRANGE(123, 11, 1), CYGNUS_PINRANGE(124, 38, 4), CYGNUS_PINRANGE(128, 43, 1), CYGNUS_PINRANGE(129, 47, 1), CYGNUS_PINRANGE(130, 49, 1), CYGNUS_PINRANGE(131, 53, 1), CYGNUS_PINRANGE(132, 55, 1), CYGNUS_PINRANGE(133, 59, 1), CYGNUS_PINRANGE(134, 63, 1), CYGNUS_PINRANGE(135, 67, 1), CYGNUS_PINRANGE(136, 71, 1), CYGNUS_PINRANGE(137, 73, 1), CYGNUS_PINRANGE(138, 77, 1), CYGNUS_PINRANGE(139, 79, 1), CYGNUS_PINRANGE(140, 81, 1), CYGNUS_PINRANGE(141, 83, 1), CYGNUS_PINRANGE(142, 10, 1) }; static unsigned cygnus_gpio_to_pin(unsigned gpio) { int i; for (i = 0; i < ARRAY_SIZE(cygnus_gpio_pintable); i++) { const struct cygnus_gpio_pin_range *range = cygnus_gpio_pintable + i; if ((gpio < range->offset) || (gpio >= (range->offset + range->num_pins))) continue; return range->pin_base + (gpio - range->offset); } return -1; } static struct cygnus_gpio *cygnus_get_gpio_core(unsigned gpio, unsigned *gpio_offset) { int i; for (i = 0; i < ARRAY_SIZE(cygnus_gpio_table); i++) { struct cygnus_gpio *chip = cygnus_gpio_table + i; if ((gpio < chip->gpio_base) || (gpio >= (chip->gpio_base + chip->ngpio))) continue; *gpio_offset = gpio - chip->gpio_base; return chip; } return NULL; } static u32 cygnus_readl(struct cygnus_gpio *chip, unsigned int offset) { return read32(chip->base + offset); } static void cygnus_writel(struct cygnus_gpio *chip, unsigned int offset, u32 val) { write32(chip->base + offset, val); } /** * cygnus_set_bit - set or clear one bit (corresponding to the GPIO pin) in a * Cygnus GPIO register * * @cygnus_gpio: Cygnus GPIO device * @reg: register offset * @gpio: GPIO pin * @set: set or clear. 1 - set; 0 -clear */ static void cygnus_set_bit(struct cygnus_gpio *chip, unsigned int reg, unsigned gpio, int set) { unsigned int offset = CYGNUS_GPIO_REG(gpio, reg); unsigned int shift = CYGNUS_GPIO_SHIFT(gpio); u32 val; val = cygnus_readl(chip, offset); if (set) val |= BIT(shift); else val &= ~BIT(shift); cygnus_writel(chip, offset, val); } static int cygnus_get_bit(struct cygnus_gpio *chip, unsigned int reg, unsigned gpio) { unsigned int offset = CYGNUS_GPIO_REG(gpio, reg); unsigned int shift = CYGNUS_GPIO_SHIFT(gpio); u32 val; val = cygnus_readl(chip, offset) & BIT(shift); if (val) return 1; else return 0; } /* * Request the Cygnus IOMUX pinmux controller to mux individual pins to GPIO */ static int cygnus_gpio_request(struct cygnus_gpio *chip, unsigned offset) { /* not all Cygnus GPIO pins can be muxed individually */ if (!chip->pinmux_is_supported || (chip->pctl_priv == NULL)) return 0; return cygnus_gpio_request_enable(chip->pctl_priv, cygnus_gpio_to_pin(offset)); } static void cygnus_gpio_free(struct cygnus_gpio *chip, unsigned offset) { if (!chip->pinmux_is_supported || (chip->pctl_priv == NULL)) return; cygnus_gpio_disable_free(chip->pctl_priv, cygnus_gpio_to_pin(offset)); } static int cygnus_gpio_direction_input(struct cygnus_gpio *chip, unsigned gpio) { cygnus_set_bit(chip, CYGNUS_GPIO_OUT_EN_OFFSET, gpio, 0); dev_dbg(chip, "gpio:%u set input\n", gpio); return 0; } static int cygnus_gpio_direction_output(struct cygnus_gpio *chip, unsigned gpio, int value) { cygnus_set_bit(chip, CYGNUS_GPIO_OUT_EN_OFFSET, gpio, 1); cygnus_set_bit(chip, CYGNUS_GPIO_DATA_OUT_OFFSET, gpio, value); dev_dbg(chip, "gpio:%u set output, value:%d\n", gpio, value); return 0; } static void cygnus_gpio_set(struct cygnus_gpio *chip, unsigned gpio, int value) { cygnus_set_bit(chip, CYGNUS_GPIO_DATA_OUT_OFFSET, gpio, value); dev_dbg(chip, "gpio:%u set, value:%d\n", gpio, value); } static int cygnus_gpio_get(struct cygnus_gpio *chip, unsigned gpio) { return cygnus_get_bit(chip, CYGNUS_GPIO_DATA_IN_OFFSET, gpio); } static int cygnus_gpio_set_pull(struct cygnus_gpio *chip, unsigned gpio, int disable, int pull_up) { if (disable) { cygnus_set_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio, 0); } else { cygnus_set_bit(chip, CYGNUS_GPIO_PAD_RES_OFFSET, gpio, pull_up); cygnus_set_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio, 1); } dev_dbg(chip, "gpio:%u set pullup:%d\n", gpio, pull_up); return 0; } #define CYGNUS_GPIO_TEST_AON_GPIO0 170 #define CYGNUS_GPIO_TEST_SPI2_MISO 69 #define CYGNUS_GPIO_TEST_DELAY_S 3 static void cygnus_gpio_test(void) { gpio_t gpio_in; gpio_t gpio_out; unsigned val; printk(BIOS_INFO, "Start gpio test...\n"); gpio_in = CYGNUS_GPIO_TEST_AON_GPIO0; /* AON_GPIO0 */ gpio_input(gpio_in); gpio_input_pulldown(gpio_in); printk(BIOS_INFO, "GPIO get %d=%d\n", gpio_in, gpio_get(gpio_in)); gpio_in = CYGNUS_GPIO_TEST_SPI2_MISO; /* SPI2_MISO */ gpio_input(gpio_in); gpio_input_pullup(gpio_in); printk(BIOS_INFO, "GPIO get %d=%d\n", gpio_in, gpio_get(gpio_in)); val = 0; gpio_out = CYGNUS_GPIO_TEST_SPI2_MISO; gpio_output(gpio_out, val); printk(BIOS_INFO, "GPIO set %d=%d\n", gpio_out, val); delay(CYGNUS_GPIO_TEST_DELAY_S); val = 1; gpio_set(gpio_out, val); printk(BIOS_INFO, "GPIO set %d=%d\n", gpio_out, val); delay(CYGNUS_GPIO_TEST_DELAY_S); val = 0; gpio_set(gpio_out, val); printk(BIOS_INFO, "GPIO set %d=%d\n", gpio_out, val); delay(CYGNUS_GPIO_TEST_DELAY_S); val = 1; gpio_set(gpio_out, val); printk(BIOS_INFO, "GPIO set %d=%d\n", gpio_out, val); delay(CYGNUS_GPIO_TEST_DELAY_S); val = 0; gpio_set(gpio_out, val); printk(BIOS_INFO, "GPIO set %d=%d\n", gpio_out, val); delay(CYGNUS_GPIO_TEST_DELAY_S); gpio_free(CYGNUS_GPIO_TEST_AON_GPIO0); gpio_free(CYGNUS_GPIO_TEST_SPI2_MISO); printk(BIOS_INFO, "Gpio test completed...\n"); } void gpio_init(void) { int i; printk(BIOS_INFO, "Setting up the gpio...\n"); for (i = 0; i < ARRAY_SIZE(cygnus_gpio_table); i++) { struct cygnus_gpio *chip = cygnus_gpio_table + i; chip->num_banks = (chip->ngpio+NGPIOS_PER_BANK - 1) / NGPIOS_PER_BANK; if (chip->pinmux_is_supported) chip->pctl_priv = cygnus_pinmux_init(); } if (IS_ENABLED(CONFIG_CYGNUS_GPIO_TEST)) cygnus_gpio_test(); } void gpio_free(gpio_t gpio) { struct cygnus_gpio *chip; unsigned gpio_num; chip = cygnus_get_gpio_core(gpio, &gpio_num); if (chip == NULL) { dev_dbg(chip, "unable to find chip for gpio %d", gpio); return; } cygnus_gpio_free(chip, gpio_num); } void gpio_input(gpio_t gpio) { struct cygnus_gpio *chip; unsigned gpio_num; chip = cygnus_get_gpio_core(gpio, &gpio_num); if (chip == NULL) { dev_dbg(chip, "unable to find chip for gpio %d", gpio); return; } if (cygnus_gpio_request(chip, gpio_num) != 0) { printk(BIOS_ERR, "Cannot mux GPIO %d\n", gpio); return; } cygnus_gpio_direction_input(chip, gpio_num); } void gpio_input_pulldown(gpio_t gpio) { struct cygnus_gpio *chip; unsigned gpio_num; chip = cygnus_get_gpio_core(gpio, &gpio_num); if (chip == NULL) { dev_dbg(chip, "unable to find chip for gpio %d", gpio); return; } cygnus_gpio_set_pull(chip, gpio_num, 0, 0); } void gpio_input_pullup(gpio_t gpio) { struct cygnus_gpio *chip; unsigned gpio_num; chip = cygnus_get_gpio_core(gpio, &gpio_num); if (chip == NULL) { dev_dbg(chip, "unable to find chip for gpio %d", gpio); return; } cygnus_gpio_set_pull(chip, gpio_num, 0, 1); } int gpio_get(gpio_t gpio) { struct cygnus_gpio *chip; unsigned gpio_num; chip = cygnus_get_gpio_core(gpio, &gpio_num); if (chip == NULL) { dev_dbg(chip, "unable to find chip for gpio %d", gpio); return -1; } return cygnus_gpio_get(chip, gpio_num); } void gpio_set(gpio_t gpio, int value) { struct cygnus_gpio *chip; unsigned gpio_num; chip = cygnus_get_gpio_core(gpio, &gpio_num); if (chip == NULL) { dev_dbg(chip, "unable to find chip for gpio %d", gpio); return; } cygnus_gpio_set(chip, gpio_num, value); } void gpio_output(gpio_t gpio, int value) { struct cygnus_gpio *chip; unsigned gpio_num; chip = cygnus_get_gpio_core(gpio, &gpio_num); if (chip == NULL) { dev_dbg(chip, "unable to find chip for gpio %d", gpio); return; } if (cygnus_gpio_request(chip, gpio_num) != 0) { printk(BIOS_ERR, "Cannot mux GPIO %d\n", gpio); return; } cygnus_gpio_direction_output(chip, gpio_num, value); }