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