1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright 2019 Broadcom. 4 */ 5 #include <assert.h> 6 #include <drivers/bcm_gpio.h> 7 #include <initcall.h> 8 #include <io.h> 9 #include <mm/core_memprot.h> 10 #include <platform_config.h> 11 #include <trace.h> 12 13 #define IPROC_GPIO_DATA_IN_OFFSET 0x00 14 #define IPROC_GPIO_DATA_OUT_OFFSET 0x04 15 #define IPROC_GPIO_OUT_EN_OFFSET 0x08 16 #define IPROC_GPIO_INT_MSK_OFFSET 0x18 17 18 #define GPIO_BANK_SIZE 0x200 19 #define NGPIOS_PER_BANK 32 20 #define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK) 21 22 #define IPROC_GPIO_REG(pin, reg) ((reg) + \ 23 GPIO_BANK(pin) * GPIO_BANK_SIZE) 24 25 #define IPROC_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK) 26 27 static SLIST_HEAD(, bcm_gpio_chip) gclist = SLIST_HEAD_INITIALIZER(gclist); 28 29 struct bcm_gpio_chip *bcm_gpio_pin_to_chip(unsigned int pin) 30 { 31 struct bcm_gpio_chip *gc = NULL; 32 33 SLIST_FOREACH(gc, &gclist, link) 34 if ((pin >= gc->gpio_base) && 35 (pin < (gc->gpio_base + gc->ngpios))) 36 return gc; 37 return NULL; 38 } 39 40 static bool __maybe_unused gpio_is_range_overlap(unsigned int start, 41 unsigned int end) 42 { 43 struct bcm_gpio_chip *gc = NULL; 44 45 SLIST_FOREACH(gc, &gclist, link) 46 if ((start < (gc->gpio_base + gc->ngpios)) && 47 (end > gc->gpio_base)) 48 return true; 49 return false; 50 } 51 52 static void iproc_set_bit(unsigned int reg, unsigned int gpio) 53 { 54 unsigned int offset = IPROC_GPIO_REG(gpio, reg); 55 unsigned int shift = IPROC_GPIO_SHIFT(gpio); 56 struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio); 57 58 assert(gc); 59 io_setbits32(gc->base + offset, BIT(shift)); 60 } 61 62 static void iproc_clr_bit(unsigned int reg, unsigned int gpio) 63 { 64 unsigned int offset = IPROC_GPIO_REG(gpio, reg); 65 unsigned int shift = IPROC_GPIO_SHIFT(gpio); 66 struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio); 67 68 assert(gc); 69 io_clrbits32(gc->base + offset, BIT(shift)); 70 } 71 72 static void iproc_gpio_set(unsigned int gpio, enum gpio_level val) 73 { 74 if (val == GPIO_LEVEL_HIGH) 75 iproc_set_bit(IPROC_GPIO_DATA_OUT_OFFSET, gpio); 76 else 77 iproc_clr_bit(IPROC_GPIO_DATA_OUT_OFFSET, gpio); 78 } 79 80 static enum gpio_level iproc_gpio_get(unsigned int gpio) 81 { 82 unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_DATA_IN_OFFSET); 83 unsigned int shift = IPROC_GPIO_SHIFT(gpio); 84 struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio); 85 86 assert(gc); 87 88 if (io_read32(gc->base + offset) & BIT(shift)) 89 return GPIO_LEVEL_HIGH; 90 else 91 return GPIO_LEVEL_LOW; 92 } 93 94 static void iproc_gpio_set_dir(unsigned int gpio, enum gpio_dir dir) 95 { 96 if (dir == GPIO_DIR_OUT) 97 iproc_set_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio); 98 else 99 iproc_clr_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio); 100 } 101 102 static enum gpio_dir iproc_gpio_get_dir(unsigned int gpio) 103 { 104 unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_OUT_EN_OFFSET); 105 unsigned int shift = IPROC_GPIO_SHIFT(gpio); 106 struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio); 107 108 assert(gc); 109 110 if (io_read32(gc->base + offset) & BIT(shift)) 111 return GPIO_DIR_OUT; 112 else 113 return GPIO_DIR_IN; 114 } 115 116 static enum gpio_interrupt iproc_gpio_get_itr(unsigned int gpio) 117 { 118 unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_INT_MSK_OFFSET); 119 unsigned int shift = IPROC_GPIO_SHIFT(gpio); 120 struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio); 121 122 assert(gc); 123 124 if (io_read32(gc->base + offset) & BIT(shift)) 125 return GPIO_INTERRUPT_ENABLE; 126 else 127 return GPIO_INTERRUPT_DISABLE; 128 } 129 130 static void iproc_gpio_set_itr(unsigned int gpio, 131 enum gpio_interrupt ena_dis) 132 { 133 if (ena_dis == GPIO_INTERRUPT_ENABLE) 134 iproc_set_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio); 135 else 136 iproc_clr_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio); 137 } 138 139 static const struct gpio_ops bcm_gpio_ops = { 140 .get_direction = iproc_gpio_get_dir, 141 .set_direction = iproc_gpio_set_dir, 142 .get_value = iproc_gpio_get, 143 .set_value = iproc_gpio_set, 144 .get_interrupt = iproc_gpio_get_itr, 145 .set_interrupt = iproc_gpio_set_itr, 146 }; 147 KEEP_PAGER(bcm_gpio_ops); 148 149 static void iproc_gpio_init(struct bcm_gpio_chip *gc, unsigned int paddr, 150 unsigned int gpio_base, unsigned int ngpios) 151 { 152 assert(!gpio_is_range_overlap(gpio_base, gpio_base + gc->ngpios)); 153 154 gc->base = core_mmu_get_va(paddr, MEM_AREA_IO_SEC); 155 gc->chip.ops = &bcm_gpio_ops; 156 gc->gpio_base = gpio_base; 157 gc->ngpios = ngpios; 158 159 SLIST_INSERT_HEAD(&gclist, gc, link); 160 161 DMSG("gpio chip for <%u - %u>", gpio_base, gpio_base + ngpios); 162 } 163 164 static TEE_Result bcm_gpio_init(void) 165 { 166 struct bcm_gpio_chip *gc = NULL; 167 168 #ifdef SECURE_GPIO_BASE0 169 gc = malloc(sizeof(*gc)); 170 if (gc == NULL) 171 return TEE_ERROR_OUT_OF_MEMORY; 172 173 iproc_gpio_init(gc, SECURE_GPIO_BASE0, GPIO_NUM_START0, NUM_GPIOS0); 174 #endif 175 #ifdef SECURE_GPIO_BASE1 176 gc = malloc(sizeof(*gc)); 177 if (gc == NULL) 178 return TEE_ERROR_OUT_OF_MEMORY; 179 180 iproc_gpio_init(gc, SECURE_GPIO_BASE1, GPIO_NUM_START1, NUM_GPIOS1); 181 #endif 182 return TEE_SUCCESS; 183 } 184 driver_init(bcm_gpio_init); 185