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