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