1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2016, Linaro Limited 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <assert.h> 30 #include <drivers/pl061_gpio.h> 31 #include <io.h> 32 #include <trace.h> 33 #include <util.h> 34 35 #ifndef PLAT_PL061_MAX_GPIOS 36 # define PLAT_PL061_MAX_GPIOS 32 37 #endif /* PLAT_PL061_MAX_GPIOS */ 38 39 #define MAX_GPIO_DEVICES ((PLAT_PL061_MAX_GPIOS + \ 40 (GPIOS_PER_PL061 - 1)) / GPIOS_PER_PL061) 41 42 #define GPIOS_PER_PL061 8 43 44 /* gpio register offsets */ 45 #define GPIODIR 0x400 46 #define GPIOIS 0x404 47 #define GPIOIBE 0x408 48 #define GPIOIEV 0x40C 49 #define GPIOIE 0x410 50 #define GPIORIS 0x414 51 #define GPIOMIS 0x418 52 #define GPIOIC 0x41C 53 #define GPIOAFSEL 0x420 54 55 /* gpio register masks */ 56 #define GPIOIE_ENABLED SHIFT_U32(1, 0) 57 #define GPIOIE_MASKED SHIFT_U32(0, 0) 58 #define GPIOAFSEL_HW SHIFT_U32(1, 0) 59 #define GPIOAFSEL_SW SHIFT_U32(0, 0) 60 #define GPIODIR_OUT SHIFT_U32(1, 0) 61 #define GPIODIR_IN SHIFT_U32(0, 0) 62 63 static vaddr_t pl061_reg_base[MAX_GPIO_DEVICES]; 64 65 static enum gpio_dir pl061_get_direction(unsigned int gpio_pin) 66 { 67 vaddr_t base_addr; 68 uint8_t data; 69 unsigned int offset; 70 71 assert(gpio_pin < PLAT_PL061_MAX_GPIOS); 72 73 base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061]; 74 offset = gpio_pin % GPIOS_PER_PL061; 75 data = read8(base_addr + GPIODIR); 76 if (data & BIT(offset)) 77 return GPIO_DIR_OUT; 78 return GPIO_DIR_IN; 79 } 80 81 static void pl061_set_direction(unsigned int gpio_pin, enum gpio_dir direction) 82 { 83 vaddr_t base_addr; 84 uint8_t data; 85 unsigned int offset; 86 87 assert(gpio_pin < PLAT_PL061_MAX_GPIOS); 88 89 base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061]; 90 offset = gpio_pin % GPIOS_PER_PL061; 91 if (direction == GPIO_DIR_OUT) { 92 data = read8(base_addr + GPIODIR) | BIT(offset); 93 write8(data, base_addr + GPIODIR); 94 } else { 95 data = read8(base_addr + GPIODIR) & ~BIT(offset); 96 write8(data, base_addr + GPIODIR); 97 } 98 } 99 100 /* 101 * The offset of GPIODATA register is 0. 102 * The values read from GPIODATA are determined for each bit, by the mask bit 103 * derived from the address used to access the data register, PADDR[9:2]. 104 * Bits that are 1 in the address mask cause the corresponding bits in GPIODATA 105 * to be read, and bits that are 0 in the address mask cause the corresponding 106 * bits in GPIODATA to be read as 0, regardless of their value. 107 */ 108 static enum gpio_level pl061_get_value(unsigned int gpio_pin) 109 { 110 vaddr_t base_addr; 111 unsigned int offset; 112 113 assert(gpio_pin < PLAT_PL061_MAX_GPIOS); 114 115 base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061]; 116 offset = gpio_pin % GPIOS_PER_PL061; 117 if (read8(base_addr + BIT(offset + 2))) 118 return GPIO_LEVEL_HIGH; 119 return GPIO_LEVEL_LOW; 120 } 121 122 /* 123 * In order to write GPIODATA, the corresponding bits in the mask, resulting 124 * from the address bus, PADDR[9:2], must be HIGH. Otherwise the bit values 125 * remain unchanged by the write. 126 */ 127 static void pl061_set_value(unsigned int gpio_pin, enum gpio_level value) 128 { 129 vaddr_t base_addr; 130 unsigned int offset; 131 132 assert(gpio_pin < PLAT_PL061_MAX_GPIOS); 133 134 base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061]; 135 offset = gpio_pin % GPIOS_PER_PL061; 136 if (value == GPIO_LEVEL_HIGH) 137 write8(BIT(offset), base_addr + BIT(offset + 2)); 138 else 139 write8(0, base_addr + BIT(offset + 2)); 140 } 141 142 static enum gpio_interrupt pl061_get_interrupt(unsigned int gpio_pin) 143 { 144 vaddr_t base_addr; 145 uint8_t data; 146 unsigned int offset; 147 148 assert(gpio_pin < PLAT_PL061_MAX_GPIOS); 149 150 base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061]; 151 offset = gpio_pin % GPIOS_PER_PL061; 152 data = read8(base_addr + GPIOIE); 153 if (data & BIT(offset)) 154 return GPIO_INTERRUPT_ENABLE; 155 return GPIO_INTERRUPT_DISABLE; 156 } 157 158 static void pl061_set_interrupt(unsigned int gpio_pin, 159 enum gpio_interrupt ena_dis) 160 { 161 vaddr_t base_addr; 162 uint8_t data; 163 unsigned int offset; 164 165 assert(gpio_pin < PLAT_PL061_MAX_GPIOS); 166 167 base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061]; 168 offset = gpio_pin % GPIOS_PER_PL061; 169 if (ena_dis == GPIO_INTERRUPT_ENABLE) { 170 data = read8(base_addr + GPIOIE) | BIT(offset); 171 write8(data, base_addr + GPIOIE); 172 } else { 173 data = read8(base_addr + GPIOIE) & ~BIT(offset); 174 write8(data, base_addr + GPIOIE); 175 } 176 } 177 178 /* 179 * Register the PL061 GPIO controller with a base address and the offset 180 * of start pin in this GPIO controller. 181 * This function is called after pl061_init(). 182 */ 183 void pl061_register(vaddr_t base_addr, unsigned int gpio_dev) 184 { 185 assert(gpio_dev < MAX_GPIO_DEVICES); 186 187 pl061_reg_base[gpio_dev] = base_addr; 188 } 189 190 static const struct gpio_ops pl061_ops = { 191 .get_direction = pl061_get_direction, 192 .set_direction = pl061_set_direction, 193 .get_value = pl061_get_value, 194 .set_value = pl061_set_value, 195 .get_interrupt = pl061_get_interrupt, 196 .set_interrupt = pl061_set_interrupt, 197 }; 198 199 /* 200 * Initialize PL061 GPIO controller 201 */ 202 void pl061_init(struct pl061_data *pd) 203 { 204 COMPILE_TIME_ASSERT(PLAT_PL061_MAX_GPIOS > 0); 205 206 assert(pd); 207 pd->chip.ops = &pl061_ops; 208 } 209 210 enum pl061_mode_control pl061_get_mode_control(unsigned int gpio_pin) 211 { 212 vaddr_t base_addr; 213 uint8_t data; 214 unsigned int offset; 215 216 assert(gpio_pin < PLAT_PL061_MAX_GPIOS); 217 218 base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061]; 219 offset = gpio_pin % GPIOS_PER_PL061; 220 data = read8(base_addr + GPIOAFSEL); 221 if (data & BIT(offset)) 222 return PL061_MC_HW; 223 return PL061_MC_SW; 224 } 225 226 void pl061_set_mode_control(unsigned int gpio_pin, 227 enum pl061_mode_control hw_sw) 228 { 229 vaddr_t base_addr; 230 uint8_t data; 231 unsigned int offset; 232 233 assert(gpio_pin < PLAT_PL061_MAX_GPIOS); 234 235 base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061]; 236 offset = gpio_pin % GPIOS_PER_PL061; 237 if (hw_sw == PL061_MC_HW) { 238 data = read8(base_addr + GPIOAFSEL) | BIT(offset); 239 write8(data, base_addr + GPIOAFSEL); 240 } else { 241 data = read8(base_addr + GPIOAFSEL) & ~BIT(offset); 242 write8(data, base_addr + GPIOAFSEL); 243 } 244 } 245