xref: /optee_os/core/drivers/pl061_gpio.c (revision a50cb361d9e5735f197ccc87beb0d24af8315369)
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 <drivers/pl061_gpio.h>
30 #include <io.h>
31 #include <trace.h>
32 #include <util.h>
33 
34 #ifndef PLAT_PL061_MAX_GPIOS
35 # define PLAT_PL061_MAX_GPIOS	32
36 #endif	/* PLAT_PL061_MAX_GPIOS */
37 
38 #define MAX_GPIO_DEVICES	((PLAT_PL061_MAX_GPIOS + \
39 	(GPIOS_PER_PL061 - 1)) / GPIOS_PER_PL061)
40 
41 #define GPIOS_PER_PL061		8
42 
43 /* gpio register offsets */
44 #define GPIODIR		0x400
45 #define GPIOIS		0x404
46 #define GPIOIBE		0x408
47 #define GPIOIEV		0x40C
48 #define GPIOIE		0x410
49 #define GPIORIS		0x414
50 #define GPIOMIS		0x418
51 #define GPIOIC		0x41C
52 #define GPIOAFSEL	0x420
53 
54 /* gpio register masks */
55 #define GPIOIE_ENABLED		SHIFT_U32(1, 0)
56 #define GPIOIE_MASKED		SHIFT_U32(0, 0)
57 #define GPIOAFSEL_HW		SHIFT_U32(1, 0)
58 #define GPIOAFSEL_SW		SHIFT_U32(0, 0)
59 #define GPIODIR_OUT			SHIFT_U32(1, 0)
60 #define GPIODIR_IN			SHIFT_U32(0, 0)
61 
62 static vaddr_t pl061_reg_base[MAX_GPIO_DEVICES];
63 
64 static enum gpio_dir pl061_get_direction(unsigned int gpio_pin)
65 {
66 	vaddr_t base_addr;
67 	uint8_t data;
68 	unsigned int offset;
69 
70 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
71 
72 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
73 	offset = gpio_pin % GPIOS_PER_PL061;
74 	data = read8(base_addr + GPIODIR);
75 	if (data & BIT(offset))
76 		return GPIO_DIR_OUT;
77 	return GPIO_DIR_IN;
78 }
79 
80 static void pl061_set_direction(unsigned int gpio_pin, enum gpio_dir direction)
81 {
82 	vaddr_t base_addr;
83 	uint8_t data;
84 	unsigned int offset;
85 
86 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
87 
88 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
89 	offset = gpio_pin % GPIOS_PER_PL061;
90 	if (direction == GPIO_DIR_OUT) {
91 		data = read8(base_addr + GPIODIR) | BIT(offset);
92 		write8(data, base_addr + GPIODIR);
93 	} else {
94 		data = read8(base_addr + GPIODIR) & ~BIT(offset);
95 		write8(data, base_addr + GPIODIR);
96 	}
97 }
98 
99 /*
100  * The offset of GPIODATA register is 0.
101  * The values read from GPIODATA are determined for each bit, by the mask bit
102  * derived from the address used to access the data register, PADDR[9:2].
103  * Bits that are 1 in the address mask cause the corresponding bits in GPIODATA
104  * to be read, and bits that are 0 in the address mask cause the corresponding
105  * bits in GPIODATA to be read as 0, regardless of their value.
106  */
107 static enum gpio_level pl061_get_value(unsigned int gpio_pin)
108 {
109 	vaddr_t base_addr;
110 	unsigned int offset;
111 
112 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
113 
114 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
115 	offset = gpio_pin % GPIOS_PER_PL061;
116 	if (read8(base_addr + BIT(offset + 2)))
117 		return GPIO_LEVEL_HIGH;
118 	return GPIO_LEVEL_LOW;
119 }
120 
121 /*
122  * In order to write GPIODATA, the corresponding bits in the mask, resulting
123  * from the address bus, PADDR[9:2], must be HIGH. Otherwise the bit values
124  * remain unchanged by the write.
125  */
126 static void pl061_set_value(unsigned int gpio_pin, enum gpio_level value)
127 {
128 	vaddr_t base_addr;
129 	unsigned int offset;
130 
131 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
132 
133 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
134 	offset = gpio_pin % GPIOS_PER_PL061;
135 	if (value == GPIO_LEVEL_HIGH)
136 		write8(BIT(offset), base_addr + BIT(offset + 2));
137 	else
138 		write8(0, base_addr + BIT(offset + 2));
139 }
140 
141 static enum gpio_interrupt pl061_get_interrupt(unsigned int gpio_pin)
142 {
143 	vaddr_t base_addr;
144 	uint8_t data;
145 	unsigned int offset;
146 
147 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
148 
149 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
150 	offset = gpio_pin % GPIOS_PER_PL061;
151 	data = read8(base_addr + GPIOIE);
152 	if (data & BIT(offset))
153 		return GPIO_INTERRUPT_ENABLE;
154 	return GPIO_INTERRUPT_DISABLE;
155 }
156 
157 static void pl061_set_interrupt(unsigned int gpio_pin,
158 	enum gpio_interrupt ena_dis)
159 {
160 	vaddr_t base_addr;
161 	uint8_t data;
162 	unsigned int offset;
163 
164 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
165 
166 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
167 	offset = gpio_pin % GPIOS_PER_PL061;
168 	if (ena_dis == GPIO_INTERRUPT_ENABLE) {
169 		data = read8(base_addr + GPIOIE) | BIT(offset);
170 		write8(data, base_addr + GPIOIE);
171 	} else {
172 		data = read8(base_addr + GPIOIE) & ~BIT(offset);
173 		write8(data, base_addr + GPIOIE);
174 	}
175 }
176 
177 /*
178  * Register the PL061 GPIO controller with a base address and the offset
179  * of start pin in this GPIO controller.
180  * This function is called after pl061_init().
181  */
182 void pl061_register(vaddr_t base_addr, unsigned int gpio_dev)
183 {
184 	assert(gpio_dev < MAX_GPIO_DEVICES);
185 
186 	pl061_reg_base[gpio_dev] = base_addr;
187 }
188 
189 static const struct gpio_ops pl061_ops = {
190 	.get_direction = pl061_get_direction,
191 	.set_direction = pl061_set_direction,
192 	.get_value = pl061_get_value,
193 	.set_value = pl061_set_value,
194 	.get_interrupt = pl061_get_interrupt,
195 	.set_interrupt = pl061_set_interrupt,
196 };
197 
198 /*
199  * Initialize PL061 GPIO controller
200  */
201 void pl061_init(struct pl061_data *pd)
202 {
203 	COMPILE_TIME_ASSERT(PLAT_PL061_MAX_GPIOS > 0);
204 
205 	assert(pd);
206 	pd->chip.ops = &pl061_ops;
207 }
208 
209 enum pl061_mode_control pl061_get_mode_control(unsigned int gpio_pin)
210 {
211 	vaddr_t base_addr;
212 	uint8_t data;
213 	unsigned int offset;
214 
215 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
216 
217 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
218 	offset = gpio_pin % GPIOS_PER_PL061;
219 	data = read8(base_addr + GPIOAFSEL);
220 	if (data & BIT(offset))
221 		return PL061_MC_HW;
222 	return PL061_MC_SW;
223 }
224 
225 void pl061_set_mode_control(unsigned int gpio_pin,
226 	enum pl061_mode_control hw_sw)
227 {
228 	vaddr_t base_addr;
229 	uint8_t data;
230 	unsigned int offset;
231 
232 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
233 
234 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
235 	offset = gpio_pin % GPIOS_PER_PL061;
236 	if (hw_sw == PL061_MC_HW) {
237 		data = read8(base_addr + GPIOAFSEL) | BIT(offset);
238 		write8(data, base_addr + GPIOAFSEL);
239 	} else {
240 		data = read8(base_addr + GPIOAFSEL) & ~BIT(offset);
241 		write8(data, base_addr + GPIOAFSEL);
242 	}
243 }
244