xref: /optee_os/core/drivers/pl061_gpio.c (revision 3af633ebf3eb6c00aaa69ae155e03f3126d637ec)
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 <trace.h>
30ce72d0c6SVictor Chong #include <gpio.h>
31ce72d0c6SVictor Chong #include <io.h>
32ce72d0c6SVictor Chong #include <util.h>
33ce72d0c6SVictor Chong #include <drivers/pl061_gpio.h>
34ce72d0c6SVictor Chong 
35ce72d0c6SVictor Chong #ifndef PLAT_PL061_MAX_GPIOS
36ce72d0c6SVictor Chong # define PLAT_PL061_MAX_GPIOS	32
37ce72d0c6SVictor Chong #endif	/* PLAT_PL061_MAX_GPIOS */
38ce72d0c6SVictor Chong 
39ce72d0c6SVictor Chong #define MAX_GPIO_DEVICES	((PLAT_PL061_MAX_GPIOS + \
40ce72d0c6SVictor Chong 	(GPIOS_PER_PL061 - 1)) / GPIOS_PER_PL061)
41ce72d0c6SVictor Chong 
42ce72d0c6SVictor Chong #define PL061_GPIO_DIR		0x400
43ce72d0c6SVictor Chong 
44ce72d0c6SVictor Chong #define GPIOS_PER_PL061		8
45ce72d0c6SVictor Chong 
46ce72d0c6SVictor Chong static enum gpio_dir pl061_get_direction(unsigned int gpio_pin);
47ce72d0c6SVictor Chong static void pl061_set_direction(unsigned int gpio_pin, enum gpio_dir direction);
48ce72d0c6SVictor Chong static enum gpio_level pl061_get_value(unsigned int gpio_pin);
49ce72d0c6SVictor Chong static void pl061_set_value(unsigned int gpio_pin, enum gpio_level value);
50ce72d0c6SVictor Chong 
51ce72d0c6SVictor Chong static vaddr_t pl061_reg_base[MAX_GPIO_DEVICES];
52ce72d0c6SVictor Chong 
53ce72d0c6SVictor Chong static const struct gpio_ops pl061_gpio_ops = {
54ce72d0c6SVictor Chong 	.get_direction	= pl061_get_direction,
55ce72d0c6SVictor Chong 	.set_direction	= pl061_set_direction,
56ce72d0c6SVictor Chong 	.get_value	= pl061_get_value,
57ce72d0c6SVictor Chong 	.set_value	= pl061_set_value,
58ce72d0c6SVictor Chong };
59ce72d0c6SVictor Chong 
60ce72d0c6SVictor Chong static enum gpio_dir pl061_get_direction(unsigned int gpio_pin)
61ce72d0c6SVictor Chong {
62ce72d0c6SVictor Chong 	vaddr_t base_addr;
63ce72d0c6SVictor Chong 	uint8_t data;
64ce72d0c6SVictor Chong 	unsigned int offset;
65ce72d0c6SVictor Chong 
66ce72d0c6SVictor Chong 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
67ce72d0c6SVictor Chong 
68ce72d0c6SVictor Chong 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
69ce72d0c6SVictor Chong 	offset = gpio_pin % GPIOS_PER_PL061;
70ce72d0c6SVictor Chong 	data = read8(base_addr + PL061_GPIO_DIR);
71ce72d0c6SVictor Chong 	if (data & BIT(offset))
72ce72d0c6SVictor Chong 		return GPIO_DIR_OUT;
73ce72d0c6SVictor Chong 	return GPIO_DIR_IN;
74ce72d0c6SVictor Chong }
75ce72d0c6SVictor Chong 
76ce72d0c6SVictor Chong static void pl061_set_direction(unsigned int gpio_pin, enum gpio_dir direction)
77ce72d0c6SVictor Chong {
78ce72d0c6SVictor Chong 	vaddr_t base_addr;
79ce72d0c6SVictor Chong 	uint8_t data;
80ce72d0c6SVictor Chong 	unsigned int offset;
81ce72d0c6SVictor Chong 
82ce72d0c6SVictor Chong 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
83ce72d0c6SVictor Chong 
84ce72d0c6SVictor Chong 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
85ce72d0c6SVictor Chong 	offset = gpio_pin % GPIOS_PER_PL061;
86ce72d0c6SVictor Chong 	if (direction == GPIO_DIR_OUT) {
87ce72d0c6SVictor Chong 		data = read8(base_addr + PL061_GPIO_DIR) | BIT(offset);
88*3af633ebSVictor Chong 		write8(data, base_addr + PL061_GPIO_DIR);
89ce72d0c6SVictor Chong 	} else {
90ce72d0c6SVictor Chong 		data = read8(base_addr + PL061_GPIO_DIR) & ~BIT(offset);
91*3af633ebSVictor Chong 		write8(data, base_addr + PL061_GPIO_DIR);
92ce72d0c6SVictor Chong 	}
93ce72d0c6SVictor Chong }
94ce72d0c6SVictor Chong 
95ce72d0c6SVictor Chong /*
96ce72d0c6SVictor Chong  * The offset of GPIODATA register is 0.
97ce72d0c6SVictor Chong  * The values read from GPIODATA are determined for each bit, by the mask bit
98ce72d0c6SVictor Chong  * derived from the address used to access the data register, PADDR[9:2].
99ce72d0c6SVictor Chong  * Bits that are 1 in the address mask cause the corresponding bits in GPIODATA
100ce72d0c6SVictor Chong  * to be read, and bits that are 0 in the address mask cause the corresponding
101ce72d0c6SVictor Chong  * bits in GPIODATA to be read as 0, regardless of their value.
102ce72d0c6SVictor Chong  */
103ce72d0c6SVictor Chong static enum gpio_level pl061_get_value(unsigned int gpio_pin)
104ce72d0c6SVictor Chong {
105ce72d0c6SVictor Chong 	vaddr_t base_addr;
106ce72d0c6SVictor Chong 	unsigned int offset;
107ce72d0c6SVictor Chong 
108ce72d0c6SVictor Chong 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
109ce72d0c6SVictor Chong 
110ce72d0c6SVictor Chong 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
111ce72d0c6SVictor Chong 	offset = gpio_pin % GPIOS_PER_PL061;
112ce72d0c6SVictor Chong 	if (read8(base_addr + BIT(offset + 2)))
113ce72d0c6SVictor Chong 		return GPIO_LEVEL_HIGH;
114ce72d0c6SVictor Chong 	return GPIO_LEVEL_LOW;
115ce72d0c6SVictor Chong }
116ce72d0c6SVictor Chong 
117ce72d0c6SVictor Chong /*
118ce72d0c6SVictor Chong  * In order to write GPIODATA, the corresponding bits in the mask, resulting
119ce72d0c6SVictor Chong  * from the address bus, PADDR[9:2], must be HIGH. Otherwise the bit values
120ce72d0c6SVictor Chong  * remain unchanged by the write.
121ce72d0c6SVictor Chong  */
122ce72d0c6SVictor Chong static void pl061_set_value(unsigned int gpio_pin, enum gpio_level value)
123ce72d0c6SVictor Chong {
124ce72d0c6SVictor Chong 	vaddr_t base_addr;
125ce72d0c6SVictor Chong 	unsigned int offset;
126ce72d0c6SVictor Chong 
127ce72d0c6SVictor Chong 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
128ce72d0c6SVictor Chong 
129ce72d0c6SVictor Chong 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
130ce72d0c6SVictor Chong 	offset = gpio_pin % GPIOS_PER_PL061;
131ce72d0c6SVictor Chong 	if (value == GPIO_LEVEL_HIGH)
132*3af633ebSVictor Chong 		write8(BIT(offset), base_addr + BIT(offset + 2));
133ce72d0c6SVictor Chong 	else
134*3af633ebSVictor Chong 		write8(0, base_addr + BIT(offset + 2));
135ce72d0c6SVictor Chong }
136ce72d0c6SVictor Chong 
137ce72d0c6SVictor Chong 
138ce72d0c6SVictor Chong /*
139ce72d0c6SVictor Chong  * Register the PL061 GPIO controller with a base address and the offset
140ce72d0c6SVictor Chong  * of start pin in this GPIO controller.
141ce72d0c6SVictor Chong  * This function is called after pl061_gpio_ops_init().
142ce72d0c6SVictor Chong  */
143ce72d0c6SVictor Chong void pl061_gpio_register(vaddr_t base_addr, unsigned int gpio_dev)
144ce72d0c6SVictor Chong {
145ce72d0c6SVictor Chong 	assert(gpio_dev < MAX_GPIO_DEVICES);
146ce72d0c6SVictor Chong 
147ce72d0c6SVictor Chong 	pl061_reg_base[gpio_dev] = base_addr;
148ce72d0c6SVictor Chong }
149ce72d0c6SVictor Chong 
150ce72d0c6SVictor Chong /*
151ce72d0c6SVictor Chong  * Initialize PL061 GPIO controller with the total GPIO numbers in SoC.
152ce72d0c6SVictor Chong  */
153ce72d0c6SVictor Chong void pl061_gpio_init(void)
154ce72d0c6SVictor Chong {
155ce72d0c6SVictor Chong 	COMPILE_TIME_ASSERT(PLAT_PL061_MAX_GPIOS > 0);
156ce72d0c6SVictor Chong 	gpio_init(&pl061_gpio_ops);
157ce72d0c6SVictor Chong }
158