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