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