10ab3f9a7SHaojian Zhuang /* 20ab3f9a7SHaojian Zhuang * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. 30ab3f9a7SHaojian Zhuang * 40ab3f9a7SHaojian Zhuang * Redistribution and use in source and binary forms, with or without 50ab3f9a7SHaojian Zhuang * modification, are permitted provided that the following conditions are met: 60ab3f9a7SHaojian Zhuang * 70ab3f9a7SHaojian Zhuang * Redistributions of source code must retain the above copyright notice, this 80ab3f9a7SHaojian Zhuang * list of conditions and the following disclaimer. 90ab3f9a7SHaojian Zhuang * 100ab3f9a7SHaojian Zhuang * Redistributions in binary form must reproduce the above copyright notice, 110ab3f9a7SHaojian Zhuang * this list of conditions and the following disclaimer in the documentation 120ab3f9a7SHaojian Zhuang * and/or other materials provided with the distribution. 130ab3f9a7SHaojian Zhuang * 140ab3f9a7SHaojian Zhuang * Neither the name of ARM nor the names of its contributors may be used 150ab3f9a7SHaojian Zhuang * to endorse or promote products derived from this software without specific 160ab3f9a7SHaojian Zhuang * prior written permission. 170ab3f9a7SHaojian Zhuang * 180ab3f9a7SHaojian Zhuang * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 190ab3f9a7SHaojian Zhuang * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 200ab3f9a7SHaojian Zhuang * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 210ab3f9a7SHaojian Zhuang * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 220ab3f9a7SHaojian Zhuang * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 230ab3f9a7SHaojian Zhuang * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 240ab3f9a7SHaojian Zhuang * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 250ab3f9a7SHaojian Zhuang * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 260ab3f9a7SHaojian Zhuang * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 270ab3f9a7SHaojian Zhuang * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 280ab3f9a7SHaojian Zhuang * POSSIBILITY OF SUCH DAMAGE. 290ab3f9a7SHaojian Zhuang * 300ab3f9a7SHaojian Zhuang * ARM PL061 GPIO Driver. 310ab3f9a7SHaojian Zhuang * Reference to ARM DDI 0190B document. 320ab3f9a7SHaojian Zhuang * 330ab3f9a7SHaojian Zhuang */ 340ab3f9a7SHaojian Zhuang 350ab3f9a7SHaojian Zhuang #include <assert.h> 360ab3f9a7SHaojian Zhuang #include <cassert.h> 370ab3f9a7SHaojian Zhuang #include <debug.h> 380ab3f9a7SHaojian Zhuang #include <errno.h> 390ab3f9a7SHaojian Zhuang #include <gpio.h> 400ab3f9a7SHaojian Zhuang #include <mmio.h> 410ab3f9a7SHaojian Zhuang #include <pl061_gpio.h> 42*152c8c11SMasahiro Yamada #include <utils.h> 430ab3f9a7SHaojian Zhuang 440ab3f9a7SHaojian Zhuang #if !PLAT_PL061_MAX_GPIOS 450ab3f9a7SHaojian Zhuang # define PLAT_PL061_MAX_GPIOS 32 460ab3f9a7SHaojian Zhuang #endif /* PLAT_PL061_MAX_GPIOS */ 470ab3f9a7SHaojian Zhuang 480ab3f9a7SHaojian Zhuang CASSERT(PLAT_PL061_MAX_GPIOS > 0, assert_plat_pl061_max_gpios); 490ab3f9a7SHaojian Zhuang 500ab3f9a7SHaojian Zhuang #define MAX_GPIO_DEVICES ((PLAT_PL061_MAX_GPIOS + \ 510ab3f9a7SHaojian Zhuang (GPIOS_PER_PL061 - 1)) / GPIOS_PER_PL061) 520ab3f9a7SHaojian Zhuang 530ab3f9a7SHaojian Zhuang #define PL061_GPIO_DIR 0x400 540ab3f9a7SHaojian Zhuang 550ab3f9a7SHaojian Zhuang #define GPIOS_PER_PL061 8 560ab3f9a7SHaojian Zhuang 570ab3f9a7SHaojian Zhuang static int pl061_get_direction(int gpio); 580ab3f9a7SHaojian Zhuang static void pl061_set_direction(int gpio, int direction); 590ab3f9a7SHaojian Zhuang static int pl061_get_value(int gpio); 600ab3f9a7SHaojian Zhuang static void pl061_set_value(int gpio, int value); 610ab3f9a7SHaojian Zhuang 620ab3f9a7SHaojian Zhuang static uintptr_t pl061_reg_base[MAX_GPIO_DEVICES]; 630ab3f9a7SHaojian Zhuang 640ab3f9a7SHaojian Zhuang static const gpio_ops_t pl061_gpio_ops = { 650ab3f9a7SHaojian Zhuang .get_direction = pl061_get_direction, 660ab3f9a7SHaojian Zhuang .set_direction = pl061_set_direction, 670ab3f9a7SHaojian Zhuang .get_value = pl061_get_value, 680ab3f9a7SHaojian Zhuang .set_value = pl061_set_value, 690ab3f9a7SHaojian Zhuang }; 700ab3f9a7SHaojian Zhuang 710ab3f9a7SHaojian Zhuang static int pl061_get_direction(int gpio) 720ab3f9a7SHaojian Zhuang { 730ab3f9a7SHaojian Zhuang uintptr_t base_addr; 740ab3f9a7SHaojian Zhuang unsigned int data, offset; 750ab3f9a7SHaojian Zhuang 760ab3f9a7SHaojian Zhuang assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS)); 770ab3f9a7SHaojian Zhuang 780ab3f9a7SHaojian Zhuang base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061]; 790ab3f9a7SHaojian Zhuang offset = gpio % GPIOS_PER_PL061; 800ab3f9a7SHaojian Zhuang data = mmio_read_8(base_addr + PL061_GPIO_DIR); 810ab3f9a7SHaojian Zhuang if (data & BIT(offset)) 820ab3f9a7SHaojian Zhuang return GPIO_DIR_OUT; 830ab3f9a7SHaojian Zhuang return GPIO_DIR_IN; 840ab3f9a7SHaojian Zhuang } 850ab3f9a7SHaojian Zhuang 860ab3f9a7SHaojian Zhuang static void pl061_set_direction(int gpio, int direction) 870ab3f9a7SHaojian Zhuang { 880ab3f9a7SHaojian Zhuang uintptr_t base_addr; 890ab3f9a7SHaojian Zhuang unsigned int data, offset; 900ab3f9a7SHaojian Zhuang 910ab3f9a7SHaojian Zhuang assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS)); 920ab3f9a7SHaojian Zhuang 930ab3f9a7SHaojian Zhuang base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061]; 940ab3f9a7SHaojian Zhuang offset = gpio % GPIOS_PER_PL061; 950ab3f9a7SHaojian Zhuang if (direction == GPIO_DIR_OUT) { 960ab3f9a7SHaojian Zhuang data = mmio_read_8(base_addr + PL061_GPIO_DIR) | BIT(offset); 970ab3f9a7SHaojian Zhuang mmio_write_8(base_addr + PL061_GPIO_DIR, data); 980ab3f9a7SHaojian Zhuang } else { 990ab3f9a7SHaojian Zhuang data = mmio_read_8(base_addr + PL061_GPIO_DIR) & ~BIT(offset); 1000ab3f9a7SHaojian Zhuang mmio_write_8(base_addr + PL061_GPIO_DIR, data); 1010ab3f9a7SHaojian Zhuang } 1020ab3f9a7SHaojian Zhuang } 1030ab3f9a7SHaojian Zhuang 1040ab3f9a7SHaojian Zhuang /* 1050ab3f9a7SHaojian Zhuang * The offset of GPIODATA register is 0. 1060ab3f9a7SHaojian Zhuang * The values read from GPIODATA are determined for each bit, by the mask bit 1070ab3f9a7SHaojian Zhuang * derived from the address used to access the data register, PADDR[9:2]. 1080ab3f9a7SHaojian Zhuang * Bits that are 1 in the address mask cause the corresponding bits in GPIODATA 1090ab3f9a7SHaojian Zhuang * to be read, and bits that are 0 in the address mask cause the corresponding 1100ab3f9a7SHaojian Zhuang * bits in GPIODATA to be read as 0, regardless of their value. 1110ab3f9a7SHaojian Zhuang */ 1120ab3f9a7SHaojian Zhuang static int pl061_get_value(int gpio) 1130ab3f9a7SHaojian Zhuang { 1140ab3f9a7SHaojian Zhuang uintptr_t base_addr; 1150ab3f9a7SHaojian Zhuang unsigned int offset; 1160ab3f9a7SHaojian Zhuang 1170ab3f9a7SHaojian Zhuang assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS)); 1180ab3f9a7SHaojian Zhuang 1190ab3f9a7SHaojian Zhuang base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061]; 1200ab3f9a7SHaojian Zhuang offset = gpio % GPIOS_PER_PL061; 1210ab3f9a7SHaojian Zhuang if (mmio_read_8(base_addr + BIT(offset + 2))) 1220ab3f9a7SHaojian Zhuang return GPIO_LEVEL_HIGH; 1230ab3f9a7SHaojian Zhuang return GPIO_LEVEL_LOW; 1240ab3f9a7SHaojian Zhuang } 1250ab3f9a7SHaojian Zhuang 1260ab3f9a7SHaojian Zhuang /* 1270ab3f9a7SHaojian Zhuang * In order to write GPIODATA, the corresponding bits in the mask, resulting 1280ab3f9a7SHaojian Zhuang * from the address bus, PADDR[9:2], must be HIGH. Otherwise the bit values 1290ab3f9a7SHaojian Zhuang * remain unchanged by the write. 1300ab3f9a7SHaojian Zhuang */ 1310ab3f9a7SHaojian Zhuang static void pl061_set_value(int gpio, int value) 1320ab3f9a7SHaojian Zhuang { 1330ab3f9a7SHaojian Zhuang uintptr_t base_addr; 1340ab3f9a7SHaojian Zhuang int offset; 1350ab3f9a7SHaojian Zhuang 1360ab3f9a7SHaojian Zhuang assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS)); 1370ab3f9a7SHaojian Zhuang 1380ab3f9a7SHaojian Zhuang base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061]; 1390ab3f9a7SHaojian Zhuang offset = gpio % GPIOS_PER_PL061; 1400ab3f9a7SHaojian Zhuang if (value == GPIO_LEVEL_HIGH) 1410ab3f9a7SHaojian Zhuang mmio_write_8(base_addr + BIT(offset + 2), BIT(offset)); 1420ab3f9a7SHaojian Zhuang else 1430ab3f9a7SHaojian Zhuang mmio_write_8(base_addr + BIT(offset + 2), 0); 1440ab3f9a7SHaojian Zhuang } 1450ab3f9a7SHaojian Zhuang 1460ab3f9a7SHaojian Zhuang 1470ab3f9a7SHaojian Zhuang /* 1480ab3f9a7SHaojian Zhuang * Register the PL061 GPIO controller with a base address and the offset 1490ab3f9a7SHaojian Zhuang * of start pin in this GPIO controller. 1500ab3f9a7SHaojian Zhuang * This function is called after pl061_gpio_ops_init(). 1510ab3f9a7SHaojian Zhuang */ 1520ab3f9a7SHaojian Zhuang void pl061_gpio_register(uintptr_t base_addr, int gpio_dev) 1530ab3f9a7SHaojian Zhuang { 1540ab3f9a7SHaojian Zhuang assert((gpio_dev >= 0) && (gpio_dev < MAX_GPIO_DEVICES)); 1550ab3f9a7SHaojian Zhuang 1560ab3f9a7SHaojian Zhuang pl061_reg_base[gpio_dev] = base_addr; 1570ab3f9a7SHaojian Zhuang } 1580ab3f9a7SHaojian Zhuang 1590ab3f9a7SHaojian Zhuang /* 1600ab3f9a7SHaojian Zhuang * Initialize PL061 GPIO controller with the total GPIO numbers in SoC. 1610ab3f9a7SHaojian Zhuang */ 1620ab3f9a7SHaojian Zhuang void pl061_gpio_init(void) 1630ab3f9a7SHaojian Zhuang { 1640ab3f9a7SHaojian Zhuang gpio_init(&pl061_gpio_ops); 1650ab3f9a7SHaojian Zhuang } 166