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