1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2016, Linaro Limited 4 */ 5 6 #include <assert.h> 7 #include <drivers/pl061_gpio.h> 8 #include <io.h> 9 #include <trace.h> 10 #include <util.h> 11 12 #ifndef PLAT_PL061_MAX_GPIOS 13 # define PLAT_PL061_MAX_GPIOS 32 14 #endif /* PLAT_PL061_MAX_GPIOS */ 15 16 #define MAX_GPIO_DEVICES ((PLAT_PL061_MAX_GPIOS + \ 17 (GPIOS_PER_PL061 - 1)) / GPIOS_PER_PL061) 18 19 #define GPIOS_PER_PL061 8 20 21 /* gpio register offsets */ 22 #define GPIODIR 0x400 23 #define GPIOIS 0x404 24 #define GPIOIBE 0x408 25 #define GPIOIEV 0x40C 26 #define GPIOIE 0x410 27 #define GPIORIS 0x414 28 #define GPIOMIS 0x418 29 #define GPIOIC 0x41C 30 #define GPIOAFSEL 0x420 31 32 /* gpio register masks */ 33 #define GPIOIE_ENABLED SHIFT_U32(1, 0) 34 #define GPIOIE_MASKED SHIFT_U32(0, 0) 35 #define GPIOAFSEL_HW SHIFT_U32(1, 0) 36 #define GPIOAFSEL_SW SHIFT_U32(0, 0) 37 #define GPIODIR_OUT SHIFT_U32(1, 0) 38 #define GPIODIR_IN SHIFT_U32(0, 0) 39 40 static vaddr_t pl061_reg_base[MAX_GPIO_DEVICES]; 41 42 static enum gpio_dir pl061_get_direction(unsigned int gpio_pin) 43 { 44 vaddr_t base_addr; 45 uint8_t data; 46 unsigned int offset; 47 48 assert(gpio_pin < PLAT_PL061_MAX_GPIOS); 49 50 base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061]; 51 offset = gpio_pin % GPIOS_PER_PL061; 52 data = read8(base_addr + GPIODIR); 53 if (data & BIT(offset)) 54 return GPIO_DIR_OUT; 55 return GPIO_DIR_IN; 56 } 57 58 static void pl061_set_direction(unsigned int gpio_pin, enum gpio_dir direction) 59 { 60 vaddr_t base_addr; 61 uint8_t data; 62 unsigned int offset; 63 64 assert(gpio_pin < PLAT_PL061_MAX_GPIOS); 65 66 base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061]; 67 offset = gpio_pin % GPIOS_PER_PL061; 68 if (direction == GPIO_DIR_OUT) { 69 data = read8(base_addr + GPIODIR) | BIT(offset); 70 write8(data, base_addr + GPIODIR); 71 } else { 72 data = read8(base_addr + GPIODIR) & ~BIT(offset); 73 write8(data, base_addr + GPIODIR); 74 } 75 } 76 77 /* 78 * The offset of GPIODATA register is 0. 79 * The values read from GPIODATA are determined for each bit, by the mask bit 80 * derived from the address used to access the data register, PADDR[9:2]. 81 * Bits that are 1 in the address mask cause the corresponding bits in GPIODATA 82 * to be read, and bits that are 0 in the address mask cause the corresponding 83 * bits in GPIODATA to be read as 0, regardless of their value. 84 */ 85 static enum gpio_level pl061_get_value(unsigned int gpio_pin) 86 { 87 vaddr_t base_addr; 88 unsigned int offset; 89 90 assert(gpio_pin < PLAT_PL061_MAX_GPIOS); 91 92 base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061]; 93 offset = gpio_pin % GPIOS_PER_PL061; 94 if (read8(base_addr + BIT(offset + 2))) 95 return GPIO_LEVEL_HIGH; 96 return GPIO_LEVEL_LOW; 97 } 98 99 /* 100 * In order to write GPIODATA, the corresponding bits in the mask, resulting 101 * from the address bus, PADDR[9:2], must be HIGH. Otherwise the bit values 102 * remain unchanged by the write. 103 */ 104 static void pl061_set_value(unsigned int gpio_pin, enum gpio_level value) 105 { 106 vaddr_t base_addr; 107 unsigned int offset; 108 109 assert(gpio_pin < PLAT_PL061_MAX_GPIOS); 110 111 base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061]; 112 offset = gpio_pin % GPIOS_PER_PL061; 113 if (value == GPIO_LEVEL_HIGH) 114 write8(BIT(offset), base_addr + BIT(offset + 2)); 115 else 116 write8(0, base_addr + BIT(offset + 2)); 117 } 118 119 static enum gpio_interrupt pl061_get_interrupt(unsigned int gpio_pin) 120 { 121 vaddr_t base_addr; 122 uint8_t data; 123 unsigned int offset; 124 125 assert(gpio_pin < PLAT_PL061_MAX_GPIOS); 126 127 base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061]; 128 offset = gpio_pin % GPIOS_PER_PL061; 129 data = read8(base_addr + GPIOIE); 130 if (data & BIT(offset)) 131 return GPIO_INTERRUPT_ENABLE; 132 return GPIO_INTERRUPT_DISABLE; 133 } 134 135 static void pl061_set_interrupt(unsigned int gpio_pin, 136 enum gpio_interrupt ena_dis) 137 { 138 vaddr_t base_addr; 139 uint8_t data; 140 unsigned int offset; 141 142 assert(gpio_pin < PLAT_PL061_MAX_GPIOS); 143 144 base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061]; 145 offset = gpio_pin % GPIOS_PER_PL061; 146 if (ena_dis == GPIO_INTERRUPT_ENABLE) { 147 data = read8(base_addr + GPIOIE) | BIT(offset); 148 write8(data, base_addr + GPIOIE); 149 } else { 150 data = read8(base_addr + GPIOIE) & ~BIT(offset); 151 write8(data, base_addr + GPIOIE); 152 } 153 } 154 155 /* 156 * Register the PL061 GPIO controller with a base address and the offset 157 * of start pin in this GPIO controller. 158 * This function is called after pl061_init(). 159 */ 160 void pl061_register(vaddr_t base_addr, unsigned int gpio_dev) 161 { 162 assert(gpio_dev < MAX_GPIO_DEVICES); 163 164 pl061_reg_base[gpio_dev] = base_addr; 165 } 166 167 static const struct gpio_ops pl061_ops = { 168 .get_direction = pl061_get_direction, 169 .set_direction = pl061_set_direction, 170 .get_value = pl061_get_value, 171 .set_value = pl061_set_value, 172 .get_interrupt = pl061_get_interrupt, 173 .set_interrupt = pl061_set_interrupt, 174 }; 175 176 /* 177 * Initialize PL061 GPIO controller 178 */ 179 void pl061_init(struct pl061_data *pd) 180 { 181 COMPILE_TIME_ASSERT(PLAT_PL061_MAX_GPIOS > 0); 182 183 assert(pd); 184 pd->chip.ops = &pl061_ops; 185 } 186 187 enum pl061_mode_control pl061_get_mode_control(unsigned int gpio_pin) 188 { 189 vaddr_t base_addr; 190 uint8_t data; 191 unsigned int offset; 192 193 assert(gpio_pin < PLAT_PL061_MAX_GPIOS); 194 195 base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061]; 196 offset = gpio_pin % GPIOS_PER_PL061; 197 data = read8(base_addr + GPIOAFSEL); 198 if (data & BIT(offset)) 199 return PL061_MC_HW; 200 return PL061_MC_SW; 201 } 202 203 void pl061_set_mode_control(unsigned int gpio_pin, 204 enum pl061_mode_control hw_sw) 205 { 206 vaddr_t base_addr; 207 uint8_t data; 208 unsigned int offset; 209 210 assert(gpio_pin < PLAT_PL061_MAX_GPIOS); 211 212 base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061]; 213 offset = gpio_pin % GPIOS_PER_PL061; 214 if (hw_sw == PL061_MC_HW) { 215 data = read8(base_addr + GPIOAFSEL) | BIT(offset); 216 write8(data, base_addr + GPIOAFSEL); 217 } else { 218 data = read8(base_addr + GPIOAFSEL) & ~BIT(offset); 219 write8(data, base_addr + GPIOAFSEL); 220 } 221 } 222