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