// SPDX-License-Identifier: BSD-2-Clause /* * Copyright 2019 Broadcom. */ #include #include #include #include #include #include #include #define IPROC_GPIO_DATA_IN_OFFSET 0x00 #define IPROC_GPIO_DATA_OUT_OFFSET 0x04 #define IPROC_GPIO_OUT_EN_OFFSET 0x08 #define IPROC_GPIO_INT_MSK_OFFSET 0x18 #define GPIO_BANK_SIZE 0x200 #define NGPIOS_PER_BANK 32 #define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK) #define IPROC_GPIO_REG(pin, reg) ((reg) + \ GPIO_BANK(pin) * GPIO_BANK_SIZE) #define IPROC_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK) static SLIST_HEAD(, bcm_gpio_chip) gclist = SLIST_HEAD_INITIALIZER(gclist); struct bcm_gpio_chip *bcm_gpio_pin_to_chip(unsigned int pin) { struct bcm_gpio_chip *gc = NULL; SLIST_FOREACH(gc, &gclist, link) if ((pin >= gc->gpio_base) && (pin < (gc->gpio_base + gc->ngpios))) return gc; return NULL; } static bool __maybe_unused gpio_is_range_overlap(unsigned int start, unsigned int end) { struct bcm_gpio_chip *gc = NULL; SLIST_FOREACH(gc, &gclist, link) if ((start < (gc->gpio_base + gc->ngpios)) && (end > gc->gpio_base)) return true; return false; } static void iproc_set_bit(unsigned int reg, unsigned int gpio) { unsigned int offset = IPROC_GPIO_REG(gpio, reg); unsigned int shift = IPROC_GPIO_SHIFT(gpio); struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio); assert(gc); io_setbits32(gc->base + offset, BIT(shift)); } static void iproc_clr_bit(unsigned int reg, unsigned int gpio) { unsigned int offset = IPROC_GPIO_REG(gpio, reg); unsigned int shift = IPROC_GPIO_SHIFT(gpio); struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio); assert(gc); io_clrbits32(gc->base + offset, BIT(shift)); } static void iproc_gpio_set(unsigned int gpio, enum gpio_level val) { if (val == GPIO_LEVEL_HIGH) iproc_set_bit(IPROC_GPIO_DATA_OUT_OFFSET, gpio); else iproc_clr_bit(IPROC_GPIO_DATA_OUT_OFFSET, gpio); } static enum gpio_level iproc_gpio_get(unsigned int gpio) { unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_DATA_IN_OFFSET); unsigned int shift = IPROC_GPIO_SHIFT(gpio); struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio); assert(gc); if (io_read32(gc->base + offset) & BIT(shift)) return GPIO_LEVEL_HIGH; else return GPIO_LEVEL_LOW; } static void iproc_gpio_set_dir(unsigned int gpio, enum gpio_dir dir) { if (dir == GPIO_DIR_OUT) iproc_set_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio); else iproc_clr_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio); } static enum gpio_dir iproc_gpio_get_dir(unsigned int gpio) { unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_OUT_EN_OFFSET); unsigned int shift = IPROC_GPIO_SHIFT(gpio); struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio); assert(gc); if (io_read32(gc->base + offset) & BIT(shift)) return GPIO_DIR_OUT; else return GPIO_DIR_IN; } static enum gpio_interrupt iproc_gpio_get_itr(unsigned int gpio) { unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_INT_MSK_OFFSET); unsigned int shift = IPROC_GPIO_SHIFT(gpio); struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio); assert(gc); if (io_read32(gc->base + offset) & BIT(shift)) return GPIO_INTERRUPT_ENABLE; else return GPIO_INTERRUPT_DISABLE; } static void iproc_gpio_set_itr(unsigned int gpio, enum gpio_interrupt ena_dis) { if (ena_dis == GPIO_INTERRUPT_ENABLE) iproc_set_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio); else iproc_clr_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio); } static const struct gpio_ops bcm_gpio_ops = { .get_direction = iproc_gpio_get_dir, .set_direction = iproc_gpio_set_dir, .get_value = iproc_gpio_get, .set_value = iproc_gpio_set, .get_interrupt = iproc_gpio_get_itr, .set_interrupt = iproc_gpio_set_itr, }; KEEP_PAGER(bcm_gpio_ops); static void iproc_gpio_init(struct bcm_gpio_chip *gc, unsigned int paddr, unsigned int gpio_base, unsigned int ngpios) { assert(!gpio_is_range_overlap(gpio_base, gpio_base + gc->ngpios)); gc->base = core_mmu_get_va(paddr, MEM_AREA_IO_SEC); gc->chip.ops = &bcm_gpio_ops; gc->gpio_base = gpio_base; gc->ngpios = ngpios; SLIST_INSERT_HEAD(&gclist, gc, link); DMSG("gpio chip for <%u - %u>", gpio_base, gpio_base + ngpios); } static TEE_Result bcm_gpio_init(void) { struct bcm_gpio_chip *gc = NULL; #ifdef SECURE_GPIO_BASE0 gc = malloc(sizeof(*gc)); if (gc == NULL) return TEE_ERROR_OUT_OF_MEMORY; iproc_gpio_init(gc, SECURE_GPIO_BASE0, GPIO_NUM_START0, NUM_GPIOS0); #endif #ifdef SECURE_GPIO_BASE1 gc = malloc(sizeof(*gc)); if (gc == NULL) return TEE_ERROR_OUT_OF_MEMORY; iproc_gpio_init(gc, SECURE_GPIO_BASE1, GPIO_NUM_START1, NUM_GPIOS1); #endif return TEE_SUCCESS; } driver_init(bcm_gpio_init);