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