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