1 /* 2 * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 * 6 * ARM PL061 GPIO Driver. 7 * Reference to ARM DDI 0190B document. 8 * 9 */ 10 11 #include <assert.h> 12 #include <errno.h> 13 14 #include <common/debug.h> 15 #include <drivers/arm/pl061_gpio.h> 16 #include <drivers/gpio.h> 17 #include <lib/cassert.h> 18 #include <lib/mmio.h> 19 #include <lib/utils.h> 20 21 #if !PLAT_PL061_MAX_GPIOS 22 # define PLAT_PL061_MAX_GPIOS 32 23 #endif /* PLAT_PL061_MAX_GPIOS */ 24 25 CASSERT(PLAT_PL061_MAX_GPIOS > 0, assert_plat_pl061_max_gpios); 26 27 #define MAX_GPIO_DEVICES ((PLAT_PL061_MAX_GPIOS + \ 28 (GPIOS_PER_PL061 - 1)) / GPIOS_PER_PL061) 29 30 #define PL061_GPIO_DIR 0x400 31 32 #define GPIOS_PER_PL061 8 33 34 static int pl061_get_direction(int gpio); 35 static void pl061_set_direction(int gpio, int direction); 36 static int pl061_get_value(int gpio); 37 static void pl061_set_value(int gpio, int value); 38 39 static uintptr_t pl061_reg_base[MAX_GPIO_DEVICES]; 40 41 static const gpio_ops_t pl061_gpio_ops = { 42 .get_direction = pl061_get_direction, 43 .set_direction = pl061_set_direction, 44 .get_value = pl061_get_value, 45 .set_value = pl061_set_value, 46 }; 47 48 static int pl061_get_direction(int gpio) 49 { 50 uintptr_t base_addr; 51 unsigned int data, offset; 52 53 assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS)); 54 55 base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061]; 56 offset = gpio % GPIOS_PER_PL061; 57 data = mmio_read_8(base_addr + PL061_GPIO_DIR); 58 if (data & BIT(offset)) 59 return GPIO_DIR_OUT; 60 return GPIO_DIR_IN; 61 } 62 63 static void pl061_set_direction(int gpio, int direction) 64 { 65 uintptr_t base_addr; 66 unsigned int data, offset; 67 68 assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS)); 69 70 base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061]; 71 offset = gpio % GPIOS_PER_PL061; 72 if (direction == GPIO_DIR_OUT) { 73 data = mmio_read_8(base_addr + PL061_GPIO_DIR) | BIT(offset); 74 mmio_write_8(base_addr + PL061_GPIO_DIR, data); 75 } else { 76 data = mmio_read_8(base_addr + PL061_GPIO_DIR) & ~BIT(offset); 77 mmio_write_8(base_addr + PL061_GPIO_DIR, data); 78 } 79 } 80 81 /* 82 * The offset of GPIODATA register is 0. 83 * The values read from GPIODATA are determined for each bit, by the mask bit 84 * derived from the address used to access the data register, PADDR[9:2]. 85 * Bits that are 1 in the address mask cause the corresponding bits in GPIODATA 86 * to be read, and bits that are 0 in the address mask cause the corresponding 87 * bits in GPIODATA to be read as 0, regardless of their value. 88 */ 89 static int pl061_get_value(int gpio) 90 { 91 uintptr_t base_addr; 92 unsigned int offset; 93 94 assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS)); 95 96 base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061]; 97 offset = gpio % GPIOS_PER_PL061; 98 if (mmio_read_8(base_addr + BIT(offset + 2))) 99 return GPIO_LEVEL_HIGH; 100 return GPIO_LEVEL_LOW; 101 } 102 103 /* 104 * In order to write GPIODATA, the corresponding bits in the mask, resulting 105 * from the address bus, PADDR[9:2], must be HIGH. Otherwise the bit values 106 * remain unchanged by the write. 107 */ 108 static void pl061_set_value(int gpio, int value) 109 { 110 uintptr_t base_addr; 111 int offset; 112 113 assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS)); 114 115 base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061]; 116 offset = gpio % GPIOS_PER_PL061; 117 if (value == GPIO_LEVEL_HIGH) 118 mmio_write_8(base_addr + BIT(offset + 2), BIT(offset)); 119 else 120 mmio_write_8(base_addr + BIT(offset + 2), 0); 121 } 122 123 124 /* 125 * Register the PL061 GPIO controller with a base address and the offset 126 * of start pin in this GPIO controller. 127 * This function is called after pl061_gpio_ops_init(). 128 */ 129 void pl061_gpio_register(uintptr_t base_addr, int gpio_dev) 130 { 131 assert((gpio_dev >= 0) && (gpio_dev < MAX_GPIO_DEVICES)); 132 133 pl061_reg_base[gpio_dev] = base_addr; 134 } 135 136 /* 137 * Initialize PL061 GPIO controller with the total GPIO numbers in SoC. 138 */ 139 void pl061_gpio_init(void) 140 { 141 gpio_init(&pl061_gpio_ops); 142 } 143