xref: /optee_os/core/drivers/pl061_gpio.c (revision f1d7853e192e905a63bd23aa710023da711ff96d)
1ce72d0c6SVictor Chong /*
2ce72d0c6SVictor Chong  * Copyright (c) 2016, Linaro Limited
3ce72d0c6SVictor Chong  * All rights reserved.
4ce72d0c6SVictor Chong  *
5ce72d0c6SVictor Chong  * Redistribution and use in source and binary forms, with or without
6ce72d0c6SVictor Chong  * modification, are permitted provided that the following conditions are met:
7ce72d0c6SVictor Chong  *
8ce72d0c6SVictor Chong  * 1. Redistributions of source code must retain the above copyright notice,
9ce72d0c6SVictor Chong  * this list of conditions and the following disclaimer.
10ce72d0c6SVictor Chong  *
11ce72d0c6SVictor Chong  * 2. Redistributions in binary form must reproduce the above copyright notice,
12ce72d0c6SVictor Chong  * this list of conditions and the following disclaimer in the documentation
13ce72d0c6SVictor Chong  * and/or other materials provided with the distribution.
14ce72d0c6SVictor Chong  *
15ce72d0c6SVictor Chong  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16ce72d0c6SVictor Chong  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17ce72d0c6SVictor Chong  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18ce72d0c6SVictor Chong  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19ce72d0c6SVictor Chong  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20ce72d0c6SVictor Chong  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21ce72d0c6SVictor Chong  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22ce72d0c6SVictor Chong  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23ce72d0c6SVictor Chong  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24ce72d0c6SVictor Chong  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25ce72d0c6SVictor Chong  * POSSIBILITY OF SUCH DAMAGE.
26ce72d0c6SVictor Chong  */
27ce72d0c6SVictor Chong 
28ce72d0c6SVictor Chong #include <assert.h>
29ce72d0c6SVictor Chong #include <drivers/pl061_gpio.h>
30bbab0cddSVictor Chong #include <io.h>
31bbab0cddSVictor Chong #include <trace.h>
32bbab0cddSVictor Chong #include <util.h>
33ce72d0c6SVictor Chong 
34ce72d0c6SVictor Chong #ifndef PLAT_PL061_MAX_GPIOS
35ce72d0c6SVictor Chong # define PLAT_PL061_MAX_GPIOS	32
36ce72d0c6SVictor Chong #endif	/* PLAT_PL061_MAX_GPIOS */
37ce72d0c6SVictor Chong 
38ce72d0c6SVictor Chong #define MAX_GPIO_DEVICES	((PLAT_PL061_MAX_GPIOS + \
39ce72d0c6SVictor Chong 	(GPIOS_PER_PL061 - 1)) / GPIOS_PER_PL061)
40ce72d0c6SVictor Chong 
41ce72d0c6SVictor Chong #define GPIOS_PER_PL061		8
42ce72d0c6SVictor Chong 
43bbab0cddSVictor Chong /* gpio register offsets */
44bbab0cddSVictor Chong #define GPIODIR		0x400
45bbab0cddSVictor Chong #define GPIOIS		0x404
46bbab0cddSVictor Chong #define GPIOIBE		0x408
47bbab0cddSVictor Chong #define GPIOIEV		0x40C
48bbab0cddSVictor Chong #define GPIOIE		0x410
49bbab0cddSVictor Chong #define GPIORIS		0x414
50bbab0cddSVictor Chong #define GPIOMIS		0x418
51bbab0cddSVictor Chong #define GPIOIC		0x41C
52bbab0cddSVictor Chong #define GPIOAFSEL	0x420
53bbab0cddSVictor Chong 
54bbab0cddSVictor Chong /* gpio register masks */
55bbab0cddSVictor Chong #define GPIOIE_ENABLED		SHIFT_U32(1, 0)
56bbab0cddSVictor Chong #define GPIOIE_MASKED		SHIFT_U32(0, 0)
57bbab0cddSVictor Chong #define GPIOAFSEL_HW		SHIFT_U32(1, 0)
58bbab0cddSVictor Chong #define GPIOAFSEL_SW		SHIFT_U32(0, 0)
59bbab0cddSVictor Chong #define GPIODIR_OUT			SHIFT_U32(1, 0)
60bbab0cddSVictor Chong #define GPIODIR_IN			SHIFT_U32(0, 0)
61ce72d0c6SVictor Chong 
62ce72d0c6SVictor Chong static vaddr_t pl061_reg_base[MAX_GPIO_DEVICES];
63ce72d0c6SVictor Chong 
64ce72d0c6SVictor Chong static enum gpio_dir pl061_get_direction(unsigned int gpio_pin)
65ce72d0c6SVictor Chong {
66ce72d0c6SVictor Chong 	vaddr_t base_addr;
67ce72d0c6SVictor Chong 	uint8_t data;
68ce72d0c6SVictor Chong 	unsigned int offset;
69ce72d0c6SVictor Chong 
70ce72d0c6SVictor Chong 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
71ce72d0c6SVictor Chong 
72ce72d0c6SVictor Chong 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
73ce72d0c6SVictor Chong 	offset = gpio_pin % GPIOS_PER_PL061;
74bbab0cddSVictor Chong 	data = read8(base_addr + GPIODIR);
75ce72d0c6SVictor Chong 	if (data & BIT(offset))
76ce72d0c6SVictor Chong 		return GPIO_DIR_OUT;
77ce72d0c6SVictor Chong 	return GPIO_DIR_IN;
78ce72d0c6SVictor Chong }
79ce72d0c6SVictor Chong 
80ce72d0c6SVictor Chong static void pl061_set_direction(unsigned int gpio_pin, enum gpio_dir direction)
81ce72d0c6SVictor Chong {
82ce72d0c6SVictor Chong 	vaddr_t base_addr;
83ce72d0c6SVictor Chong 	uint8_t data;
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;
90ce72d0c6SVictor Chong 	if (direction == GPIO_DIR_OUT) {
91bbab0cddSVictor Chong 		data = read8(base_addr + GPIODIR) | BIT(offset);
92bbab0cddSVictor Chong 		write8(data, base_addr + GPIODIR);
93ce72d0c6SVictor Chong 	} else {
94bbab0cddSVictor Chong 		data = read8(base_addr + GPIODIR) & ~BIT(offset);
95bbab0cddSVictor Chong 		write8(data, base_addr + GPIODIR);
96ce72d0c6SVictor Chong 	}
97ce72d0c6SVictor Chong }
98ce72d0c6SVictor Chong 
99ce72d0c6SVictor Chong /*
100ce72d0c6SVictor Chong  * The offset of GPIODATA register is 0.
101ce72d0c6SVictor Chong  * The values read from GPIODATA are determined for each bit, by the mask bit
102ce72d0c6SVictor Chong  * derived from the address used to access the data register, PADDR[9:2].
103ce72d0c6SVictor Chong  * Bits that are 1 in the address mask cause the corresponding bits in GPIODATA
104ce72d0c6SVictor Chong  * to be read, and bits that are 0 in the address mask cause the corresponding
105ce72d0c6SVictor Chong  * bits in GPIODATA to be read as 0, regardless of their value.
106ce72d0c6SVictor Chong  */
107ce72d0c6SVictor Chong static enum gpio_level pl061_get_value(unsigned int gpio_pin)
108ce72d0c6SVictor Chong {
109ce72d0c6SVictor Chong 	vaddr_t base_addr;
110ce72d0c6SVictor Chong 	unsigned int offset;
111ce72d0c6SVictor Chong 
112ce72d0c6SVictor Chong 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
113ce72d0c6SVictor Chong 
114ce72d0c6SVictor Chong 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
115ce72d0c6SVictor Chong 	offset = gpio_pin % GPIOS_PER_PL061;
116ce72d0c6SVictor Chong 	if (read8(base_addr + BIT(offset + 2)))
117ce72d0c6SVictor Chong 		return GPIO_LEVEL_HIGH;
118ce72d0c6SVictor Chong 	return GPIO_LEVEL_LOW;
119ce72d0c6SVictor Chong }
120ce72d0c6SVictor Chong 
121ce72d0c6SVictor Chong /*
122ce72d0c6SVictor Chong  * In order to write GPIODATA, the corresponding bits in the mask, resulting
123ce72d0c6SVictor Chong  * from the address bus, PADDR[9:2], must be HIGH. Otherwise the bit values
124ce72d0c6SVictor Chong  * remain unchanged by the write.
125ce72d0c6SVictor Chong  */
126ce72d0c6SVictor Chong static void pl061_set_value(unsigned int gpio_pin, enum gpio_level value)
127ce72d0c6SVictor Chong {
128ce72d0c6SVictor Chong 	vaddr_t base_addr;
129ce72d0c6SVictor Chong 	unsigned int offset;
130ce72d0c6SVictor Chong 
131ce72d0c6SVictor Chong 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
132ce72d0c6SVictor Chong 
133ce72d0c6SVictor Chong 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
134ce72d0c6SVictor Chong 	offset = gpio_pin % GPIOS_PER_PL061;
135ce72d0c6SVictor Chong 	if (value == GPIO_LEVEL_HIGH)
1363af633ebSVictor Chong 		write8(BIT(offset), base_addr + BIT(offset + 2));
137ce72d0c6SVictor Chong 	else
1383af633ebSVictor Chong 		write8(0, base_addr + BIT(offset + 2));
139ce72d0c6SVictor Chong }
140ce72d0c6SVictor Chong 
141*f1d7853eSVictor Chong static enum gpio_interrupt pl061_get_interrupt(unsigned int gpio_pin)
142*f1d7853eSVictor Chong {
143*f1d7853eSVictor Chong 	vaddr_t base_addr;
144*f1d7853eSVictor Chong 	uint8_t data;
145*f1d7853eSVictor Chong 	unsigned int offset;
146*f1d7853eSVictor Chong 
147*f1d7853eSVictor Chong 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
148*f1d7853eSVictor Chong 
149*f1d7853eSVictor Chong 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
150*f1d7853eSVictor Chong 	offset = gpio_pin % GPIOS_PER_PL061;
151*f1d7853eSVictor Chong 	data = read8(base_addr + GPIOIE);
152*f1d7853eSVictor Chong 	if (data & BIT(offset))
153*f1d7853eSVictor Chong 		return GPIO_INTERRUPT_ENABLE;
154*f1d7853eSVictor Chong 	return GPIO_INTERRUPT_DISABLE;
155*f1d7853eSVictor Chong }
156*f1d7853eSVictor Chong 
157*f1d7853eSVictor Chong static void pl061_set_interrupt(unsigned int gpio_pin,
158*f1d7853eSVictor Chong 	enum gpio_interrupt ena_dis)
159*f1d7853eSVictor Chong {
160*f1d7853eSVictor Chong 	vaddr_t base_addr;
161*f1d7853eSVictor Chong 	uint8_t data;
162*f1d7853eSVictor Chong 	unsigned int offset;
163*f1d7853eSVictor Chong 
164*f1d7853eSVictor Chong 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
165*f1d7853eSVictor Chong 
166*f1d7853eSVictor Chong 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
167*f1d7853eSVictor Chong 	offset = gpio_pin % GPIOS_PER_PL061;
168*f1d7853eSVictor Chong 	if (ena_dis == GPIO_INTERRUPT_ENABLE) {
169*f1d7853eSVictor Chong 		data = read8(base_addr + GPIOIE) | BIT(offset);
170*f1d7853eSVictor Chong 		write8(data, base_addr + GPIOIE);
171*f1d7853eSVictor Chong 	} else {
172*f1d7853eSVictor Chong 		data = read8(base_addr + GPIOIE) & ~BIT(offset);
173*f1d7853eSVictor Chong 		write8(data, base_addr + GPIOIE);
174*f1d7853eSVictor Chong 	}
175*f1d7853eSVictor Chong }
176ce72d0c6SVictor Chong 
177ce72d0c6SVictor Chong /*
178ce72d0c6SVictor Chong  * Register the PL061 GPIO controller with a base address and the offset
179ce72d0c6SVictor Chong  * of start pin in this GPIO controller.
180bbab0cddSVictor Chong  * This function is called after pl061_init().
181ce72d0c6SVictor Chong  */
182bbab0cddSVictor Chong void pl061_register(vaddr_t base_addr, unsigned int gpio_dev)
183ce72d0c6SVictor Chong {
184ce72d0c6SVictor Chong 	assert(gpio_dev < MAX_GPIO_DEVICES);
185ce72d0c6SVictor Chong 
186ce72d0c6SVictor Chong 	pl061_reg_base[gpio_dev] = base_addr;
187ce72d0c6SVictor Chong }
188ce72d0c6SVictor Chong 
189bbab0cddSVictor Chong static const struct gpio_ops pl061_ops = {
190bbab0cddSVictor Chong 	.get_direction = pl061_get_direction,
191bbab0cddSVictor Chong 	.set_direction = pl061_set_direction,
192bbab0cddSVictor Chong 	.get_value = pl061_get_value,
193bbab0cddSVictor Chong 	.set_value = pl061_set_value,
194*f1d7853eSVictor Chong 	.get_interrupt = pl061_get_interrupt,
195*f1d7853eSVictor Chong 	.set_interrupt = pl061_set_interrupt,
196bbab0cddSVictor Chong };
197bbab0cddSVictor Chong 
198ce72d0c6SVictor Chong /*
199bbab0cddSVictor Chong  * Initialize PL061 GPIO controller
200ce72d0c6SVictor Chong  */
201bbab0cddSVictor Chong void pl061_init(struct pl061_data *pd)
202ce72d0c6SVictor Chong {
203ce72d0c6SVictor Chong 	COMPILE_TIME_ASSERT(PLAT_PL061_MAX_GPIOS > 0);
204bbab0cddSVictor Chong 
205bbab0cddSVictor Chong 	assert(pd);
206bbab0cddSVictor Chong 	pd->chip.ops = &pl061_ops;
207ce72d0c6SVictor Chong }
208*f1d7853eSVictor Chong 
209*f1d7853eSVictor Chong enum pl061_mode_control pl061_get_mode_control(unsigned int gpio_pin)
210*f1d7853eSVictor Chong {
211*f1d7853eSVictor Chong 	vaddr_t base_addr;
212*f1d7853eSVictor Chong 	uint8_t data;
213*f1d7853eSVictor Chong 	unsigned int offset;
214*f1d7853eSVictor Chong 
215*f1d7853eSVictor Chong 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
216*f1d7853eSVictor Chong 
217*f1d7853eSVictor Chong 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
218*f1d7853eSVictor Chong 	offset = gpio_pin % GPIOS_PER_PL061;
219*f1d7853eSVictor Chong 	data = read8(base_addr + GPIOAFSEL);
220*f1d7853eSVictor Chong 	if (data & BIT(offset))
221*f1d7853eSVictor Chong 		return PL061_MC_HW;
222*f1d7853eSVictor Chong 	return PL061_MC_SW;
223*f1d7853eSVictor Chong }
224*f1d7853eSVictor Chong 
225*f1d7853eSVictor Chong void pl061_set_mode_control(unsigned int gpio_pin,
226*f1d7853eSVictor Chong 	enum pl061_mode_control hw_sw)
227*f1d7853eSVictor Chong {
228*f1d7853eSVictor Chong 	vaddr_t base_addr;
229*f1d7853eSVictor Chong 	uint8_t data;
230*f1d7853eSVictor Chong 	unsigned int offset;
231*f1d7853eSVictor Chong 
232*f1d7853eSVictor Chong 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
233*f1d7853eSVictor Chong 
234*f1d7853eSVictor Chong 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
235*f1d7853eSVictor Chong 	offset = gpio_pin % GPIOS_PER_PL061;
236*f1d7853eSVictor Chong 	if (hw_sw == PL061_MC_HW) {
237*f1d7853eSVictor Chong 		data = read8(base_addr + GPIOAFSEL) | BIT(offset);
238*f1d7853eSVictor Chong 		write8(data, base_addr + GPIOAFSEL);
239*f1d7853eSVictor Chong 	} else {
240*f1d7853eSVictor Chong 		data = read8(base_addr + GPIOAFSEL) & ~BIT(offset);
241*f1d7853eSVictor Chong 		write8(data, base_addr + GPIOAFSEL);
242*f1d7853eSVictor Chong 	}
243*f1d7853eSVictor Chong }
244