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