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