xref: /optee_os/core/drivers/bcm_gpio.c (revision 41e5aa8f18c4d48083341ff3df9e75f0c77cf703)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright 2019 Broadcom.
4  */
5 #include <assert.h>
6 #include <drivers/bcm_gpio.h>
7 #include <initcall.h>
8 #include <io.h>
9 #include <mm/core_memprot.h>
10 #include <platform_config.h>
11 #include <trace.h>
12 
13 #define IPROC_GPIO_DATA_IN_OFFSET	0x00
14 #define IPROC_GPIO_DATA_OUT_OFFSET	0x04
15 #define IPROC_GPIO_OUT_EN_OFFSET	0x08
16 #define IPROC_GPIO_INT_MSK_OFFSET	0x18
17 
18 #define GPIO_BANK_SIZE			0x200
19 #define NGPIOS_PER_BANK			32
20 #define GPIO_BANK(pin)			((pin) / NGPIOS_PER_BANK)
21 
22 #define IPROC_GPIO_REG(pin, reg)	((reg) + \
23 	GPIO_BANK(pin) * GPIO_BANK_SIZE)
24 
25 #define IPROC_GPIO_SHIFT(pin)		((pin) % NGPIOS_PER_BANK)
26 
27 static SLIST_HEAD(, bcm_gpio_chip) gclist = SLIST_HEAD_INITIALIZER(gclist);
28 
29 struct bcm_gpio_chip *bcm_gpio_pin_to_chip(unsigned int pin)
30 {
31 	struct bcm_gpio_chip *gc = NULL;
32 
33 	SLIST_FOREACH(gc, &gclist, link)
34 		if ((pin >= gc->gpio_base) &&
35 		    (pin < (gc->gpio_base + gc->ngpios)))
36 			return gc;
37 	return NULL;
38 }
39 
40 static bool __maybe_unused gpio_is_range_overlap(unsigned int start,
41 						 unsigned int end)
42 {
43 	struct bcm_gpio_chip *gc = NULL;
44 
45 	SLIST_FOREACH(gc, &gclist, link)
46 		if ((start < (gc->gpio_base + gc->ngpios)) &&
47 		    (end > gc->gpio_base))
48 			return true;
49 	return false;
50 }
51 
52 static void iproc_set_bit(unsigned int reg, unsigned int gpio)
53 {
54 	unsigned int offset = IPROC_GPIO_REG(gpio, reg);
55 	unsigned int shift = IPROC_GPIO_SHIFT(gpio);
56 	struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio);
57 
58 	assert(gc);
59 	io_setbits32(gc->base + offset, BIT(shift));
60 }
61 
62 static void iproc_clr_bit(unsigned int reg, unsigned int gpio)
63 {
64 	unsigned int offset = IPROC_GPIO_REG(gpio, reg);
65 	unsigned int shift = IPROC_GPIO_SHIFT(gpio);
66 	struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio);
67 
68 	assert(gc);
69 	io_clrbits32(gc->base + offset, BIT(shift));
70 }
71 
72 static void iproc_gpio_set(unsigned int gpio, enum gpio_level val)
73 {
74 	if (val == GPIO_LEVEL_HIGH)
75 		iproc_set_bit(IPROC_GPIO_DATA_OUT_OFFSET, gpio);
76 	else
77 		iproc_clr_bit(IPROC_GPIO_DATA_OUT_OFFSET, gpio);
78 }
79 
80 static enum gpio_level iproc_gpio_get(unsigned int gpio)
81 {
82 	unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_DATA_IN_OFFSET);
83 	unsigned int shift = IPROC_GPIO_SHIFT(gpio);
84 	struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio);
85 
86 	assert(gc);
87 
88 	if (io_read32(gc->base + offset) & BIT(shift))
89 		return GPIO_LEVEL_HIGH;
90 	else
91 		return GPIO_LEVEL_LOW;
92 }
93 
94 static void iproc_gpio_set_dir(unsigned int gpio, enum gpio_dir dir)
95 {
96 	if (dir == GPIO_DIR_OUT)
97 		iproc_set_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio);
98 	else
99 		iproc_clr_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio);
100 }
101 
102 static enum gpio_dir iproc_gpio_get_dir(unsigned int gpio)
103 {
104 	unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_OUT_EN_OFFSET);
105 	unsigned int shift = IPROC_GPIO_SHIFT(gpio);
106 	struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio);
107 
108 	assert(gc);
109 
110 	if (io_read32(gc->base + offset) & BIT(shift))
111 		return GPIO_DIR_OUT;
112 	else
113 		return GPIO_DIR_IN;
114 }
115 
116 static enum gpio_interrupt iproc_gpio_get_itr(unsigned int gpio)
117 {
118 	unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_INT_MSK_OFFSET);
119 	unsigned int shift = IPROC_GPIO_SHIFT(gpio);
120 	struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio);
121 
122 	assert(gc);
123 
124 	if (io_read32(gc->base + offset) & BIT(shift))
125 		return GPIO_INTERRUPT_ENABLE;
126 	else
127 		return GPIO_INTERRUPT_DISABLE;
128 }
129 
130 static void iproc_gpio_set_itr(unsigned int gpio,
131 			       enum gpio_interrupt ena_dis)
132 {
133 	if (ena_dis == GPIO_INTERRUPT_ENABLE)
134 		iproc_set_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio);
135 	else
136 		iproc_clr_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio);
137 }
138 
139 static const struct gpio_ops bcm_gpio_ops = {
140 	.get_direction = iproc_gpio_get_dir,
141 	.set_direction = iproc_gpio_set_dir,
142 	.get_value = iproc_gpio_get,
143 	.set_value = iproc_gpio_set,
144 	.get_interrupt = iproc_gpio_get_itr,
145 	.set_interrupt = iproc_gpio_set_itr,
146 };
147 KEEP_PAGER(bcm_gpio_ops);
148 
149 static void iproc_gpio_init(struct bcm_gpio_chip *gc, unsigned int paddr,
150 			    unsigned int gpio_base, unsigned int ngpios)
151 {
152 	assert(!gpio_is_range_overlap(gpio_base, gpio_base + gc->ngpios));
153 
154 	gc->base = core_mmu_get_va(paddr, MEM_AREA_IO_SEC);
155 	gc->chip.ops = &bcm_gpio_ops;
156 	gc->gpio_base = gpio_base;
157 	gc->ngpios = ngpios;
158 
159 	SLIST_INSERT_HEAD(&gclist, gc, link);
160 
161 	DMSG("gpio chip for <%u - %u>", gpio_base, gpio_base + ngpios);
162 }
163 
164 static TEE_Result bcm_gpio_init(void)
165 {
166 	struct bcm_gpio_chip *gc = NULL;
167 
168 #ifdef SECURE_GPIO_BASE0
169 	gc = malloc(sizeof(*gc));
170 	if (gc == NULL)
171 		return TEE_ERROR_OUT_OF_MEMORY;
172 
173 	iproc_gpio_init(gc, SECURE_GPIO_BASE0, GPIO_NUM_START0, NUM_GPIOS0);
174 #endif
175 #ifdef SECURE_GPIO_BASE1
176 	gc = malloc(sizeof(*gc));
177 	if (gc == NULL)
178 		return TEE_ERROR_OUT_OF_MEMORY;
179 
180 	iproc_gpio_init(gc, SECURE_GPIO_BASE1, GPIO_NUM_START1, NUM_GPIOS1);
181 #endif
182 	return TEE_SUCCESS;
183 }
184 driver_init(bcm_gpio_init);
185