1*0ab3f9a7SHaojian Zhuang /* 2*0ab3f9a7SHaojian Zhuang * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. 3*0ab3f9a7SHaojian Zhuang * 4*0ab3f9a7SHaojian Zhuang * Redistribution and use in source and binary forms, with or without 5*0ab3f9a7SHaojian Zhuang * modification, are permitted provided that the following conditions are met: 6*0ab3f9a7SHaojian Zhuang * 7*0ab3f9a7SHaojian Zhuang * Redistributions of source code must retain the above copyright notice, this 8*0ab3f9a7SHaojian Zhuang * list of conditions and the following disclaimer. 9*0ab3f9a7SHaojian Zhuang * 10*0ab3f9a7SHaojian Zhuang * Redistributions in binary form must reproduce the above copyright notice, 11*0ab3f9a7SHaojian Zhuang * this list of conditions and the following disclaimer in the documentation 12*0ab3f9a7SHaojian Zhuang * and/or other materials provided with the distribution. 13*0ab3f9a7SHaojian Zhuang * 14*0ab3f9a7SHaojian Zhuang * Neither the name of ARM nor the names of its contributors may be used 15*0ab3f9a7SHaojian Zhuang * to endorse or promote products derived from this software without specific 16*0ab3f9a7SHaojian Zhuang * prior written permission. 17*0ab3f9a7SHaojian Zhuang * 18*0ab3f9a7SHaojian Zhuang * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19*0ab3f9a7SHaojian Zhuang * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20*0ab3f9a7SHaojian Zhuang * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21*0ab3f9a7SHaojian Zhuang * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22*0ab3f9a7SHaojian Zhuang * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23*0ab3f9a7SHaojian Zhuang * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24*0ab3f9a7SHaojian Zhuang * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25*0ab3f9a7SHaojian Zhuang * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26*0ab3f9a7SHaojian Zhuang * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27*0ab3f9a7SHaojian Zhuang * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28*0ab3f9a7SHaojian Zhuang * POSSIBILITY OF SUCH DAMAGE. 29*0ab3f9a7SHaojian Zhuang * 30*0ab3f9a7SHaojian Zhuang * ARM PL061 GPIO Driver. 31*0ab3f9a7SHaojian Zhuang * Reference to ARM DDI 0190B document. 32*0ab3f9a7SHaojian Zhuang * 33*0ab3f9a7SHaojian Zhuang */ 34*0ab3f9a7SHaojian Zhuang 35*0ab3f9a7SHaojian Zhuang #include <assert.h> 36*0ab3f9a7SHaojian Zhuang #include <cassert.h> 37*0ab3f9a7SHaojian Zhuang #include <debug.h> 38*0ab3f9a7SHaojian Zhuang #include <errno.h> 39*0ab3f9a7SHaojian Zhuang #include <gpio.h> 40*0ab3f9a7SHaojian Zhuang #include <mmio.h> 41*0ab3f9a7SHaojian Zhuang #include <pl061_gpio.h> 42*0ab3f9a7SHaojian Zhuang 43*0ab3f9a7SHaojian Zhuang #if !PLAT_PL061_MAX_GPIOS 44*0ab3f9a7SHaojian Zhuang # define PLAT_PL061_MAX_GPIOS 32 45*0ab3f9a7SHaojian Zhuang #endif /* PLAT_PL061_MAX_GPIOS */ 46*0ab3f9a7SHaojian Zhuang 47*0ab3f9a7SHaojian Zhuang CASSERT(PLAT_PL061_MAX_GPIOS > 0, assert_plat_pl061_max_gpios); 48*0ab3f9a7SHaojian Zhuang 49*0ab3f9a7SHaojian Zhuang #define MAX_GPIO_DEVICES ((PLAT_PL061_MAX_GPIOS + \ 50*0ab3f9a7SHaojian Zhuang (GPIOS_PER_PL061 - 1)) / GPIOS_PER_PL061) 51*0ab3f9a7SHaojian Zhuang 52*0ab3f9a7SHaojian Zhuang #define PL061_GPIO_DIR 0x400 53*0ab3f9a7SHaojian Zhuang 54*0ab3f9a7SHaojian Zhuang #define GPIOS_PER_PL061 8 55*0ab3f9a7SHaojian Zhuang #define BIT(nr) (1UL << (nr)) 56*0ab3f9a7SHaojian Zhuang 57*0ab3f9a7SHaojian Zhuang static int pl061_get_direction(int gpio); 58*0ab3f9a7SHaojian Zhuang static void pl061_set_direction(int gpio, int direction); 59*0ab3f9a7SHaojian Zhuang static int pl061_get_value(int gpio); 60*0ab3f9a7SHaojian Zhuang static void pl061_set_value(int gpio, int value); 61*0ab3f9a7SHaojian Zhuang 62*0ab3f9a7SHaojian Zhuang static uintptr_t pl061_reg_base[MAX_GPIO_DEVICES]; 63*0ab3f9a7SHaojian Zhuang 64*0ab3f9a7SHaojian Zhuang static const gpio_ops_t pl061_gpio_ops = { 65*0ab3f9a7SHaojian Zhuang .get_direction = pl061_get_direction, 66*0ab3f9a7SHaojian Zhuang .set_direction = pl061_set_direction, 67*0ab3f9a7SHaojian Zhuang .get_value = pl061_get_value, 68*0ab3f9a7SHaojian Zhuang .set_value = pl061_set_value, 69*0ab3f9a7SHaojian Zhuang }; 70*0ab3f9a7SHaojian Zhuang 71*0ab3f9a7SHaojian Zhuang static int pl061_get_direction(int gpio) 72*0ab3f9a7SHaojian Zhuang { 73*0ab3f9a7SHaojian Zhuang uintptr_t base_addr; 74*0ab3f9a7SHaojian Zhuang unsigned int data, offset; 75*0ab3f9a7SHaojian Zhuang 76*0ab3f9a7SHaojian Zhuang assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS)); 77*0ab3f9a7SHaojian Zhuang 78*0ab3f9a7SHaojian Zhuang base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061]; 79*0ab3f9a7SHaojian Zhuang offset = gpio % GPIOS_PER_PL061; 80*0ab3f9a7SHaojian Zhuang data = mmio_read_8(base_addr + PL061_GPIO_DIR); 81*0ab3f9a7SHaojian Zhuang if (data & BIT(offset)) 82*0ab3f9a7SHaojian Zhuang return GPIO_DIR_OUT; 83*0ab3f9a7SHaojian Zhuang return GPIO_DIR_IN; 84*0ab3f9a7SHaojian Zhuang } 85*0ab3f9a7SHaojian Zhuang 86*0ab3f9a7SHaojian Zhuang static void pl061_set_direction(int gpio, int direction) 87*0ab3f9a7SHaojian Zhuang { 88*0ab3f9a7SHaojian Zhuang uintptr_t base_addr; 89*0ab3f9a7SHaojian Zhuang unsigned int data, offset; 90*0ab3f9a7SHaojian Zhuang 91*0ab3f9a7SHaojian Zhuang assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS)); 92*0ab3f9a7SHaojian Zhuang 93*0ab3f9a7SHaojian Zhuang base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061]; 94*0ab3f9a7SHaojian Zhuang offset = gpio % GPIOS_PER_PL061; 95*0ab3f9a7SHaojian Zhuang if (direction == GPIO_DIR_OUT) { 96*0ab3f9a7SHaojian Zhuang data = mmio_read_8(base_addr + PL061_GPIO_DIR) | BIT(offset); 97*0ab3f9a7SHaojian Zhuang mmio_write_8(base_addr + PL061_GPIO_DIR, data); 98*0ab3f9a7SHaojian Zhuang } else { 99*0ab3f9a7SHaojian Zhuang data = mmio_read_8(base_addr + PL061_GPIO_DIR) & ~BIT(offset); 100*0ab3f9a7SHaojian Zhuang mmio_write_8(base_addr + PL061_GPIO_DIR, data); 101*0ab3f9a7SHaojian Zhuang } 102*0ab3f9a7SHaojian Zhuang } 103*0ab3f9a7SHaojian Zhuang 104*0ab3f9a7SHaojian Zhuang /* 105*0ab3f9a7SHaojian Zhuang * The offset of GPIODATA register is 0. 106*0ab3f9a7SHaojian Zhuang * The values read from GPIODATA are determined for each bit, by the mask bit 107*0ab3f9a7SHaojian Zhuang * derived from the address used to access the data register, PADDR[9:2]. 108*0ab3f9a7SHaojian Zhuang * Bits that are 1 in the address mask cause the corresponding bits in GPIODATA 109*0ab3f9a7SHaojian Zhuang * to be read, and bits that are 0 in the address mask cause the corresponding 110*0ab3f9a7SHaojian Zhuang * bits in GPIODATA to be read as 0, regardless of their value. 111*0ab3f9a7SHaojian Zhuang */ 112*0ab3f9a7SHaojian Zhuang static int pl061_get_value(int gpio) 113*0ab3f9a7SHaojian Zhuang { 114*0ab3f9a7SHaojian Zhuang uintptr_t base_addr; 115*0ab3f9a7SHaojian Zhuang unsigned int offset; 116*0ab3f9a7SHaojian Zhuang 117*0ab3f9a7SHaojian Zhuang assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS)); 118*0ab3f9a7SHaojian Zhuang 119*0ab3f9a7SHaojian Zhuang base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061]; 120*0ab3f9a7SHaojian Zhuang offset = gpio % GPIOS_PER_PL061; 121*0ab3f9a7SHaojian Zhuang if (mmio_read_8(base_addr + BIT(offset + 2))) 122*0ab3f9a7SHaojian Zhuang return GPIO_LEVEL_HIGH; 123*0ab3f9a7SHaojian Zhuang return GPIO_LEVEL_LOW; 124*0ab3f9a7SHaojian Zhuang } 125*0ab3f9a7SHaojian Zhuang 126*0ab3f9a7SHaojian Zhuang /* 127*0ab3f9a7SHaojian Zhuang * In order to write GPIODATA, the corresponding bits in the mask, resulting 128*0ab3f9a7SHaojian Zhuang * from the address bus, PADDR[9:2], must be HIGH. Otherwise the bit values 129*0ab3f9a7SHaojian Zhuang * remain unchanged by the write. 130*0ab3f9a7SHaojian Zhuang */ 131*0ab3f9a7SHaojian Zhuang static void pl061_set_value(int gpio, int value) 132*0ab3f9a7SHaojian Zhuang { 133*0ab3f9a7SHaojian Zhuang uintptr_t base_addr; 134*0ab3f9a7SHaojian Zhuang int offset; 135*0ab3f9a7SHaojian Zhuang 136*0ab3f9a7SHaojian Zhuang assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS)); 137*0ab3f9a7SHaojian Zhuang 138*0ab3f9a7SHaojian Zhuang base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061]; 139*0ab3f9a7SHaojian Zhuang offset = gpio % GPIOS_PER_PL061; 140*0ab3f9a7SHaojian Zhuang if (value == GPIO_LEVEL_HIGH) 141*0ab3f9a7SHaojian Zhuang mmio_write_8(base_addr + BIT(offset + 2), BIT(offset)); 142*0ab3f9a7SHaojian Zhuang else 143*0ab3f9a7SHaojian Zhuang mmio_write_8(base_addr + BIT(offset + 2), 0); 144*0ab3f9a7SHaojian Zhuang } 145*0ab3f9a7SHaojian Zhuang 146*0ab3f9a7SHaojian Zhuang 147*0ab3f9a7SHaojian Zhuang /* 148*0ab3f9a7SHaojian Zhuang * Register the PL061 GPIO controller with a base address and the offset 149*0ab3f9a7SHaojian Zhuang * of start pin in this GPIO controller. 150*0ab3f9a7SHaojian Zhuang * This function is called after pl061_gpio_ops_init(). 151*0ab3f9a7SHaojian Zhuang */ 152*0ab3f9a7SHaojian Zhuang void pl061_gpio_register(uintptr_t base_addr, int gpio_dev) 153*0ab3f9a7SHaojian Zhuang { 154*0ab3f9a7SHaojian Zhuang assert((gpio_dev >= 0) && (gpio_dev < MAX_GPIO_DEVICES)); 155*0ab3f9a7SHaojian Zhuang 156*0ab3f9a7SHaojian Zhuang pl061_reg_base[gpio_dev] = base_addr; 157*0ab3f9a7SHaojian Zhuang } 158*0ab3f9a7SHaojian Zhuang 159*0ab3f9a7SHaojian Zhuang /* 160*0ab3f9a7SHaojian Zhuang * Initialize PL061 GPIO controller with the total GPIO numbers in SoC. 161*0ab3f9a7SHaojian Zhuang */ 162*0ab3f9a7SHaojian Zhuang void pl061_gpio_init(void) 163*0ab3f9a7SHaojian Zhuang { 164*0ab3f9a7SHaojian Zhuang gpio_init(&pl061_gpio_ops); 165*0ab3f9a7SHaojian Zhuang } 166