10ab3f9a7SHaojian Zhuang /* 20ab3f9a7SHaojian Zhuang * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. 30ab3f9a7SHaojian Zhuang * 482cb2c1aSdp-arm * SPDX-License-Identifier: BSD-3-Clause 50ab3f9a7SHaojian Zhuang * 60ab3f9a7SHaojian Zhuang * ARM PL061 GPIO Driver. 70ab3f9a7SHaojian Zhuang * Reference to ARM DDI 0190B document. 80ab3f9a7SHaojian Zhuang * 90ab3f9a7SHaojian Zhuang */ 100ab3f9a7SHaojian Zhuang 110ab3f9a7SHaojian Zhuang #include <assert.h> 120ab3f9a7SHaojian Zhuang #include <errno.h> 13*09d40e0eSAntonio Nino Diaz 14*09d40e0eSAntonio Nino Diaz #include <common/debug.h> 15*09d40e0eSAntonio Nino Diaz #include <drivers/arm/pl061_gpio.h> 16*09d40e0eSAntonio Nino Diaz #include <drivers/gpio.h> 17*09d40e0eSAntonio Nino Diaz #include <lib/cassert.h> 18*09d40e0eSAntonio Nino Diaz #include <lib/mmio.h> 19*09d40e0eSAntonio Nino Diaz #include <lib/utils.h> 200ab3f9a7SHaojian Zhuang 210ab3f9a7SHaojian Zhuang #if !PLAT_PL061_MAX_GPIOS 220ab3f9a7SHaojian Zhuang # define PLAT_PL061_MAX_GPIOS 32 230ab3f9a7SHaojian Zhuang #endif /* PLAT_PL061_MAX_GPIOS */ 240ab3f9a7SHaojian Zhuang 250ab3f9a7SHaojian Zhuang CASSERT(PLAT_PL061_MAX_GPIOS > 0, assert_plat_pl061_max_gpios); 260ab3f9a7SHaojian Zhuang 270ab3f9a7SHaojian Zhuang #define MAX_GPIO_DEVICES ((PLAT_PL061_MAX_GPIOS + \ 280ab3f9a7SHaojian Zhuang (GPIOS_PER_PL061 - 1)) / GPIOS_PER_PL061) 290ab3f9a7SHaojian Zhuang 300ab3f9a7SHaojian Zhuang #define PL061_GPIO_DIR 0x400 310ab3f9a7SHaojian Zhuang 320ab3f9a7SHaojian Zhuang #define GPIOS_PER_PL061 8 330ab3f9a7SHaojian Zhuang 340ab3f9a7SHaojian Zhuang static int pl061_get_direction(int gpio); 350ab3f9a7SHaojian Zhuang static void pl061_set_direction(int gpio, int direction); 360ab3f9a7SHaojian Zhuang static int pl061_get_value(int gpio); 370ab3f9a7SHaojian Zhuang static void pl061_set_value(int gpio, int value); 380ab3f9a7SHaojian Zhuang 390ab3f9a7SHaojian Zhuang static uintptr_t pl061_reg_base[MAX_GPIO_DEVICES]; 400ab3f9a7SHaojian Zhuang 410ab3f9a7SHaojian Zhuang static const gpio_ops_t pl061_gpio_ops = { 420ab3f9a7SHaojian Zhuang .get_direction = pl061_get_direction, 430ab3f9a7SHaojian Zhuang .set_direction = pl061_set_direction, 440ab3f9a7SHaojian Zhuang .get_value = pl061_get_value, 450ab3f9a7SHaojian Zhuang .set_value = pl061_set_value, 460ab3f9a7SHaojian Zhuang }; 470ab3f9a7SHaojian Zhuang 480ab3f9a7SHaojian Zhuang static int pl061_get_direction(int gpio) 490ab3f9a7SHaojian Zhuang { 500ab3f9a7SHaojian Zhuang uintptr_t base_addr; 510ab3f9a7SHaojian Zhuang unsigned int data, offset; 520ab3f9a7SHaojian Zhuang 530ab3f9a7SHaojian Zhuang assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS)); 540ab3f9a7SHaojian Zhuang 550ab3f9a7SHaojian Zhuang base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061]; 560ab3f9a7SHaojian Zhuang offset = gpio % GPIOS_PER_PL061; 570ab3f9a7SHaojian Zhuang data = mmio_read_8(base_addr + PL061_GPIO_DIR); 580ab3f9a7SHaojian Zhuang if (data & BIT(offset)) 590ab3f9a7SHaojian Zhuang return GPIO_DIR_OUT; 600ab3f9a7SHaojian Zhuang return GPIO_DIR_IN; 610ab3f9a7SHaojian Zhuang } 620ab3f9a7SHaojian Zhuang 630ab3f9a7SHaojian Zhuang static void pl061_set_direction(int gpio, int direction) 640ab3f9a7SHaojian Zhuang { 650ab3f9a7SHaojian Zhuang uintptr_t base_addr; 660ab3f9a7SHaojian Zhuang unsigned int data, offset; 670ab3f9a7SHaojian Zhuang 680ab3f9a7SHaojian Zhuang assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS)); 690ab3f9a7SHaojian Zhuang 700ab3f9a7SHaojian Zhuang base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061]; 710ab3f9a7SHaojian Zhuang offset = gpio % GPIOS_PER_PL061; 720ab3f9a7SHaojian Zhuang if (direction == GPIO_DIR_OUT) { 730ab3f9a7SHaojian Zhuang data = mmio_read_8(base_addr + PL061_GPIO_DIR) | BIT(offset); 740ab3f9a7SHaojian Zhuang mmio_write_8(base_addr + PL061_GPIO_DIR, data); 750ab3f9a7SHaojian Zhuang } else { 760ab3f9a7SHaojian Zhuang data = mmio_read_8(base_addr + PL061_GPIO_DIR) & ~BIT(offset); 770ab3f9a7SHaojian Zhuang mmio_write_8(base_addr + PL061_GPIO_DIR, data); 780ab3f9a7SHaojian Zhuang } 790ab3f9a7SHaojian Zhuang } 800ab3f9a7SHaojian Zhuang 810ab3f9a7SHaojian Zhuang /* 820ab3f9a7SHaojian Zhuang * The offset of GPIODATA register is 0. 830ab3f9a7SHaojian Zhuang * The values read from GPIODATA are determined for each bit, by the mask bit 840ab3f9a7SHaojian Zhuang * derived from the address used to access the data register, PADDR[9:2]. 850ab3f9a7SHaojian Zhuang * Bits that are 1 in the address mask cause the corresponding bits in GPIODATA 860ab3f9a7SHaojian Zhuang * to be read, and bits that are 0 in the address mask cause the corresponding 870ab3f9a7SHaojian Zhuang * bits in GPIODATA to be read as 0, regardless of their value. 880ab3f9a7SHaojian Zhuang */ 890ab3f9a7SHaojian Zhuang static int pl061_get_value(int gpio) 900ab3f9a7SHaojian Zhuang { 910ab3f9a7SHaojian Zhuang uintptr_t base_addr; 920ab3f9a7SHaojian Zhuang unsigned int offset; 930ab3f9a7SHaojian Zhuang 940ab3f9a7SHaojian Zhuang assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS)); 950ab3f9a7SHaojian Zhuang 960ab3f9a7SHaojian Zhuang base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061]; 970ab3f9a7SHaojian Zhuang offset = gpio % GPIOS_PER_PL061; 980ab3f9a7SHaojian Zhuang if (mmio_read_8(base_addr + BIT(offset + 2))) 990ab3f9a7SHaojian Zhuang return GPIO_LEVEL_HIGH; 1000ab3f9a7SHaojian Zhuang return GPIO_LEVEL_LOW; 1010ab3f9a7SHaojian Zhuang } 1020ab3f9a7SHaojian Zhuang 1030ab3f9a7SHaojian Zhuang /* 1040ab3f9a7SHaojian Zhuang * In order to write GPIODATA, the corresponding bits in the mask, resulting 1050ab3f9a7SHaojian Zhuang * from the address bus, PADDR[9:2], must be HIGH. Otherwise the bit values 1060ab3f9a7SHaojian Zhuang * remain unchanged by the write. 1070ab3f9a7SHaojian Zhuang */ 1080ab3f9a7SHaojian Zhuang static void pl061_set_value(int gpio, int value) 1090ab3f9a7SHaojian Zhuang { 1100ab3f9a7SHaojian Zhuang uintptr_t base_addr; 1110ab3f9a7SHaojian Zhuang int offset; 1120ab3f9a7SHaojian Zhuang 1130ab3f9a7SHaojian Zhuang assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS)); 1140ab3f9a7SHaojian Zhuang 1150ab3f9a7SHaojian Zhuang base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061]; 1160ab3f9a7SHaojian Zhuang offset = gpio % GPIOS_PER_PL061; 1170ab3f9a7SHaojian Zhuang if (value == GPIO_LEVEL_HIGH) 1180ab3f9a7SHaojian Zhuang mmio_write_8(base_addr + BIT(offset + 2), BIT(offset)); 1190ab3f9a7SHaojian Zhuang else 1200ab3f9a7SHaojian Zhuang mmio_write_8(base_addr + BIT(offset + 2), 0); 1210ab3f9a7SHaojian Zhuang } 1220ab3f9a7SHaojian Zhuang 1230ab3f9a7SHaojian Zhuang 1240ab3f9a7SHaojian Zhuang /* 1250ab3f9a7SHaojian Zhuang * Register the PL061 GPIO controller with a base address and the offset 1260ab3f9a7SHaojian Zhuang * of start pin in this GPIO controller. 1270ab3f9a7SHaojian Zhuang * This function is called after pl061_gpio_ops_init(). 1280ab3f9a7SHaojian Zhuang */ 1290ab3f9a7SHaojian Zhuang void pl061_gpio_register(uintptr_t base_addr, int gpio_dev) 1300ab3f9a7SHaojian Zhuang { 1310ab3f9a7SHaojian Zhuang assert((gpio_dev >= 0) && (gpio_dev < MAX_GPIO_DEVICES)); 1320ab3f9a7SHaojian Zhuang 1330ab3f9a7SHaojian Zhuang pl061_reg_base[gpio_dev] = base_addr; 1340ab3f9a7SHaojian Zhuang } 1350ab3f9a7SHaojian Zhuang 1360ab3f9a7SHaojian Zhuang /* 1370ab3f9a7SHaojian Zhuang * Initialize PL061 GPIO controller with the total GPIO numbers in SoC. 1380ab3f9a7SHaojian Zhuang */ 1390ab3f9a7SHaojian Zhuang void pl061_gpio_init(void) 1400ab3f9a7SHaojian Zhuang { 1410ab3f9a7SHaojian Zhuang gpio_init(&pl061_gpio_ops); 1420ab3f9a7SHaojian Zhuang } 143