aboutsummaryrefslogtreecommitdiff
path: root/core/drivers/bcm_gpio.c
blob: 58d34d6a43c0cb01f05f2eef1f44cda6ee054f59 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// SPDX-License-Identifier: BSD-2-Clause
/*
 * Copyright 2019 Broadcom.
 */
#include <assert.h>
#include <drivers/bcm_gpio.h>
#include <initcall.h>
#include <io.h>
#include <mm/core_memprot.h>
#include <platform_config.h>
#include <trace.h>

#define IPROC_GPIO_DATA_IN_OFFSET	0x00
#define IPROC_GPIO_DATA_OUT_OFFSET	0x04
#define IPROC_GPIO_OUT_EN_OFFSET	0x08
#define IPROC_GPIO_INT_MSK_OFFSET	0x18

#define GPIO_BANK_SIZE			0x200
#define NGPIOS_PER_BANK			32
#define GPIO_BANK(pin)			((pin) / NGPIOS_PER_BANK)

#define IPROC_GPIO_REG(pin, reg)	((reg) + \
	GPIO_BANK(pin) * GPIO_BANK_SIZE)

#define IPROC_GPIO_SHIFT(pin)		((pin) % NGPIOS_PER_BANK)

static SLIST_HEAD(, bcm_gpio_chip) gclist = SLIST_HEAD_INITIALIZER(gclist);

struct bcm_gpio_chip *bcm_gpio_pin_to_chip(unsigned int pin)
{
	struct bcm_gpio_chip *gc = NULL;

	SLIST_FOREACH(gc, &gclist, link)
		if ((pin >= gc->gpio_base) &&
		    (pin < (gc->gpio_base + gc->ngpios)))
			return gc;
	return NULL;
}

static bool __maybe_unused gpio_is_range_overlap(unsigned int start,
						 unsigned int end)
{
	struct bcm_gpio_chip *gc = NULL;

	SLIST_FOREACH(gc, &gclist, link)
		if ((start < (gc->gpio_base + gc->ngpios)) &&
		    (end > gc->gpio_base))
			return true;
	return false;
}

static void iproc_set_bit(unsigned int reg, unsigned int gpio)
{
	unsigned int offset = IPROC_GPIO_REG(gpio, reg);
	unsigned int shift = IPROC_GPIO_SHIFT(gpio);
	struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio);

	assert(gc);
	io_setbits32(gc->base + offset, BIT(shift));
}

static void iproc_clr_bit(unsigned int reg, unsigned int gpio)
{
	unsigned int offset = IPROC_GPIO_REG(gpio, reg);
	unsigned int shift = IPROC_GPIO_SHIFT(gpio);
	struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio);

	assert(gc);
	io_clrbits32(gc->base + offset, BIT(shift));
}

static void iproc_gpio_set(unsigned int gpio, enum gpio_level val)
{
	if (val == GPIO_LEVEL_HIGH)
		iproc_set_bit(IPROC_GPIO_DATA_OUT_OFFSET, gpio);
	else
		iproc_clr_bit(IPROC_GPIO_DATA_OUT_OFFSET, gpio);
}

static enum gpio_level iproc_gpio_get(unsigned int gpio)
{
	unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_DATA_IN_OFFSET);
	unsigned int shift = IPROC_GPIO_SHIFT(gpio);
	struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio);

	assert(gc);

	if (io_read32(gc->base + offset) & BIT(shift))
		return GPIO_LEVEL_HIGH;
	else
		return GPIO_LEVEL_LOW;
}

static void iproc_gpio_set_dir(unsigned int gpio, enum gpio_dir dir)
{
	if (dir == GPIO_DIR_OUT)
		iproc_set_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio);
	else
		iproc_clr_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio);
}

static enum gpio_dir iproc_gpio_get_dir(unsigned int gpio)
{
	unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_OUT_EN_OFFSET);
	unsigned int shift = IPROC_GPIO_SHIFT(gpio);
	struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio);

	assert(gc);

	if (io_read32(gc->base + offset) & BIT(shift))
		return GPIO_DIR_OUT;
	else
		return GPIO_DIR_IN;
}

static enum gpio_interrupt iproc_gpio_get_itr(unsigned int gpio)
{
	unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_INT_MSK_OFFSET);
	unsigned int shift = IPROC_GPIO_SHIFT(gpio);
	struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio);

	assert(gc);

	if (io_read32(gc->base + offset) & BIT(shift))
		return GPIO_INTERRUPT_ENABLE;
	else
		return GPIO_INTERRUPT_DISABLE;
}

static void iproc_gpio_set_itr(unsigned int gpio,
			       enum gpio_interrupt ena_dis)
{
	if (ena_dis == GPIO_INTERRUPT_ENABLE)
		iproc_set_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio);
	else
		iproc_clr_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio);
}

static const struct gpio_ops bcm_gpio_ops = {
	.get_direction = iproc_gpio_get_dir,
	.set_direction = iproc_gpio_set_dir,
	.get_value = iproc_gpio_get,
	.set_value = iproc_gpio_set,
	.get_interrupt = iproc_gpio_get_itr,
	.set_interrupt = iproc_gpio_set_itr,
};
KEEP_PAGER(bcm_gpio_ops);

static void iproc_gpio_init(struct bcm_gpio_chip *gc, unsigned int paddr,
			    unsigned int gpio_base, unsigned int ngpios)
{
	assert(!gpio_is_range_overlap(gpio_base, gpio_base + gc->ngpios));

	gc->base = core_mmu_get_va(paddr, MEM_AREA_IO_SEC);
	gc->chip.ops = &bcm_gpio_ops;
	gc->gpio_base = gpio_base;
	gc->ngpios = ngpios;

	SLIST_INSERT_HEAD(&gclist, gc, link);

	DMSG("gpio chip for <%u - %u>", gpio_base, gpio_base + ngpios);
}

static TEE_Result bcm_gpio_init(void)
{
	struct bcm_gpio_chip *gc = NULL;

#ifdef SECURE_GPIO_BASE0
	gc = malloc(sizeof(*gc));
	if (gc == NULL)
		return TEE_ERROR_OUT_OF_MEMORY;

	iproc_gpio_init(gc, SECURE_GPIO_BASE0, GPIO_NUM_START0, NUM_GPIOS0);
#endif
#ifdef SECURE_GPIO_BASE1
	gc = malloc(sizeof(*gc));
	if (gc == NULL)
		return TEE_ERROR_OUT_OF_MEMORY;

	iproc_gpio_init(gc, SECURE_GPIO_BASE1, GPIO_NUM_START1, NUM_GPIOS1);
#endif
	return TEE_SUCCESS;
}
driver_init(bcm_gpio_init);