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