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