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