xref: /optee_os/core/drivers/pl061_gpio.c (revision f7859906faa00a29398cf4a9f5f703dbf310fb63)
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 = io_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 	unsigned int offset;
62 
63 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
64 
65 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
66 	offset = gpio_pin % GPIOS_PER_PL061;
67 	if (direction == GPIO_DIR_OUT)
68 		io_setbits8(base_addr + GPIODIR, BIT(offset));
69 	else
70 		io_clrbits8(base_addr + GPIODIR, BIT(offset));
71 }
72 
73 /*
74  * The offset of GPIODATA register is 0.
75  * The values read from GPIODATA are determined for each bit, by the mask bit
76  * derived from the address used to access the data register, PADDR[9:2].
77  * Bits that are 1 in the address mask cause the corresponding bits in GPIODATA
78  * to be read, and bits that are 0 in the address mask cause the corresponding
79  * bits in GPIODATA to be read as 0, regardless of their value.
80  */
81 static enum gpio_level pl061_get_value(unsigned int gpio_pin)
82 {
83 	vaddr_t base_addr;
84 	unsigned int offset;
85 
86 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
87 
88 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
89 	offset = gpio_pin % GPIOS_PER_PL061;
90 	if (io_read8(base_addr + BIT(offset + 2)))
91 		return GPIO_LEVEL_HIGH;
92 	return GPIO_LEVEL_LOW;
93 }
94 
95 /*
96  * In order to write GPIODATA, the corresponding bits in the mask, resulting
97  * from the address bus, PADDR[9:2], must be HIGH. Otherwise the bit values
98  * remain unchanged by the write.
99  */
100 static void pl061_set_value(unsigned int gpio_pin, enum gpio_level value)
101 {
102 	vaddr_t base_addr;
103 	unsigned int offset;
104 
105 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
106 
107 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
108 	offset = gpio_pin % GPIOS_PER_PL061;
109 	if (value == GPIO_LEVEL_HIGH)
110 		io_write8(base_addr + BIT(offset + 2), BIT(offset));
111 	else
112 		io_write8(base_addr + BIT(offset + 2), 0);
113 }
114 
115 static enum gpio_interrupt pl061_get_interrupt(unsigned int gpio_pin)
116 {
117 	vaddr_t base_addr;
118 	uint8_t data;
119 	unsigned int offset;
120 
121 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
122 
123 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
124 	offset = gpio_pin % GPIOS_PER_PL061;
125 	data = io_read8(base_addr + GPIOIE);
126 	if (data & BIT(offset))
127 		return GPIO_INTERRUPT_ENABLE;
128 	return GPIO_INTERRUPT_DISABLE;
129 }
130 
131 static void pl061_set_interrupt(unsigned int gpio_pin,
132 	enum gpio_interrupt ena_dis)
133 {
134 	vaddr_t base_addr;
135 	unsigned int offset;
136 
137 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
138 
139 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
140 	offset = gpio_pin % GPIOS_PER_PL061;
141 	if (ena_dis == GPIO_INTERRUPT_ENABLE)
142 		io_setbits8(base_addr + GPIOIE, BIT(offset));
143 	else
144 		io_clrbits8(base_addr + GPIOIE, BIT(offset));
145 }
146 
147 /*
148  * Register the PL061 GPIO controller with a base address and the offset
149  * of start pin in this GPIO controller.
150  * This function is called after pl061_init().
151  */
152 void pl061_register(vaddr_t base_addr, unsigned int gpio_dev)
153 {
154 	assert(gpio_dev < MAX_GPIO_DEVICES);
155 
156 	pl061_reg_base[gpio_dev] = base_addr;
157 }
158 
159 static const struct gpio_ops pl061_ops = {
160 	.get_direction = pl061_get_direction,
161 	.set_direction = pl061_set_direction,
162 	.get_value = pl061_get_value,
163 	.set_value = pl061_set_value,
164 	.get_interrupt = pl061_get_interrupt,
165 	.set_interrupt = pl061_set_interrupt,
166 };
167 
168 /*
169  * Initialize PL061 GPIO controller
170  */
171 void pl061_init(struct pl061_data *pd)
172 {
173 	COMPILE_TIME_ASSERT(PLAT_PL061_MAX_GPIOS > 0);
174 
175 	assert(pd);
176 	pd->chip.ops = &pl061_ops;
177 }
178 
179 enum pl061_mode_control pl061_get_mode_control(unsigned int gpio_pin)
180 {
181 	vaddr_t base_addr;
182 	uint8_t data;
183 	unsigned int offset;
184 
185 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
186 
187 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
188 	offset = gpio_pin % GPIOS_PER_PL061;
189 	data = io_read8(base_addr + GPIOAFSEL);
190 	if (data & BIT(offset))
191 		return PL061_MC_HW;
192 	return PL061_MC_SW;
193 }
194 
195 void pl061_set_mode_control(unsigned int gpio_pin,
196 	enum pl061_mode_control hw_sw)
197 {
198 	vaddr_t base_addr;
199 	unsigned int offset;
200 
201 	assert(gpio_pin < PLAT_PL061_MAX_GPIOS);
202 
203 	base_addr = pl061_reg_base[gpio_pin / GPIOS_PER_PL061];
204 	offset = gpio_pin % GPIOS_PER_PL061;
205 	if (hw_sw == PL061_MC_HW)
206 		io_setbits8(base_addr + GPIOAFSEL, BIT(offset));
207 	else
208 		io_clrbits8(base_addr + GPIOAFSEL, BIT(offset));
209 }
210