summaryrefslogtreecommitdiff
path: root/drivers/gpio/bcm2835_gpio.c
blob: 332cfc2b231835ca7c4ffd9f0ead44b2a8351730 (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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
/*
 * Copyright (C) 2012 Vikram Narayananan
 * <vikram186@gmail.com>
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
#include <dm.h>
#include <errno.h>
#include <asm/gpio.h>
#include <asm/io.h>

#define GPIO_NAME_SIZE		20

struct bcm2835_gpios {
	char label[BCM2835_GPIO_COUNT][GPIO_NAME_SIZE];
	struct bcm2835_gpio_regs *reg;
};

/**
 * gpio_is_requested() - check if a GPIO has been requested
 *
 * @bank:	Bank to check
 * @offset:	GPIO offset within bank to check
 * @return true if marked as requested, false if not
 */
static inline bool gpio_is_requested(struct bcm2835_gpios *gpios, int offset)
{
	return *gpios->label[offset] != '\0';
}

static int check_requested(struct udevice *dev, unsigned offset,
			   const char *func)
{
	struct bcm2835_gpios *gpios = dev_get_priv(dev);
	struct gpio_dev_priv *uc_priv = dev->uclass_priv;

	if (!gpio_is_requested(gpios, offset)) {
		printf("omap_gpio: %s: error: gpio %s%d not requested\n",
		       func, uc_priv->bank_name, offset);
		return -EPERM;
	}

	return 0;
}

static int bcm2835_gpio_request(struct udevice *dev, unsigned offset,
				const char *label)
{
	struct bcm2835_gpios *gpios = dev_get_priv(dev);

	if (gpio_is_requested(gpios, offset))
		return -EBUSY;

	strncpy(gpios->label[offset], label, GPIO_NAME_SIZE);
	gpios->label[offset][GPIO_NAME_SIZE - 1] = '\0';

	return 0;
}

static int bcm2835_gpio_free(struct udevice *dev, unsigned offset)
{
	struct bcm2835_gpios *gpios = dev_get_priv(dev);
	int ret;

	ret = check_requested(dev, offset, __func__);
	if (ret)
		return ret;
	gpios->label[offset][0] = '\0';

	return 0;
}

static int bcm2835_gpio_direction_input(struct udevice *dev, unsigned gpio)
{
	struct bcm2835_gpios *gpios = dev_get_priv(dev);
	unsigned val;

	val = readl(&gpios->reg->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]);
	val &= ~(BCM2835_GPIO_FSEL_MASK << BCM2835_GPIO_FSEL_SHIFT(gpio));
	val |= (BCM2835_GPIO_INPUT << BCM2835_GPIO_FSEL_SHIFT(gpio));
	writel(val, &gpios->reg->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]);

	return 0;
}

static int bcm2835_gpio_direction_output(struct udevice *dev, unsigned gpio,
					 int value)
{
	struct bcm2835_gpios *gpios = dev_get_priv(dev);
	unsigned val;

	gpio_set_value(gpio, value);

	val = readl(&gpios->reg->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]);
	val &= ~(BCM2835_GPIO_FSEL_MASK << BCM2835_GPIO_FSEL_SHIFT(gpio));
	val |= (BCM2835_GPIO_OUTPUT << BCM2835_GPIO_FSEL_SHIFT(gpio));
	writel(val, &gpios->reg->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]);

	return 0;
}

static bool bcm2835_gpio_is_output(const struct bcm2835_gpios *gpios, int gpio)
{
	u32 val;

	val = readl(&gpios->reg->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]);
	val &= BCM2835_GPIO_FSEL_MASK << BCM2835_GPIO_FSEL_SHIFT(gpio);
	return val ? true : false;
}

static int bcm2835_get_value(const struct bcm2835_gpios *gpios, unsigned gpio)
{
	unsigned val;

	val = readl(&gpios->reg->gplev[BCM2835_GPIO_COMMON_BANK(gpio)]);

	return (val >> BCM2835_GPIO_COMMON_SHIFT(gpio)) & 0x1;
}

static int bcm2835_gpio_get_value(struct udevice *dev, unsigned gpio)
{
	const struct bcm2835_gpios *gpios = dev_get_priv(dev);

	return bcm2835_get_value(gpios, gpio);
}

static int bcm2835_gpio_set_value(struct udevice *dev, unsigned gpio,
				  int value)
{
	struct bcm2835_gpios *gpios = dev_get_priv(dev);
	u32 *output_reg = value ? gpios->reg->gpset : gpios->reg->gpclr;

	writel(1 << BCM2835_GPIO_COMMON_SHIFT(gpio),
				&output_reg[BCM2835_GPIO_COMMON_BANK(gpio)]);

	return 0;
}

static int bcm2835_gpio_get_function(struct udevice *dev, unsigned offset)
{
	struct bcm2835_gpios *gpios = dev_get_priv(dev);

	if (!gpio_is_requested(gpios, offset))
		return GPIOF_UNUSED;

	/* GPIOF_FUNC is not implemented yet */
	if (bcm2835_gpio_is_output(gpios, offset))
		return GPIOF_OUTPUT;
	else
		return GPIOF_INPUT;
}

static int bcm2835_gpio_get_state(struct udevice *dev, unsigned int offset,
				  char *buf, int bufsize)
{
	struct gpio_dev_priv *uc_priv = dev->uclass_priv;
	struct bcm2835_gpios *gpios = dev_get_priv(dev);
	const char *label;
	bool requested;
	bool is_output;
	int size;

	label = gpios->label[offset];
	is_output = bcm2835_gpio_is_output(gpios, offset);
	size = snprintf(buf, bufsize, "%s%d: ",
			uc_priv->bank_name ? uc_priv->bank_name : "", offset);
	buf += size;
	bufsize -= size;
	requested = gpio_is_requested(gpios, offset);
	snprintf(buf, bufsize, "%s: %d [%c]%s%s",
		 is_output ? "out" : " in",
		 bcm2835_get_value(gpios, offset),
		 requested ? 'x' : ' ',
		 requested ? " " : "",
		 label);

	return 0;
}

static const struct dm_gpio_ops gpio_bcm2835_ops = {
	.request		= bcm2835_gpio_request,
	.free			= bcm2835_gpio_free,
	.direction_input	= bcm2835_gpio_direction_input,
	.direction_output	= bcm2835_gpio_direction_output,
	.get_value		= bcm2835_gpio_get_value,
	.set_value		= bcm2835_gpio_set_value,
	.get_function		= bcm2835_gpio_get_function,
	.get_state		= bcm2835_gpio_get_state,
};

static int bcm2835_gpio_probe(struct udevice *dev)
{
	struct bcm2835_gpios *gpios = dev_get_priv(dev);
	struct bcm2835_gpio_platdata *plat = dev_get_platdata(dev);
	struct gpio_dev_priv *uc_priv = dev->uclass_priv;

	uc_priv->bank_name = "GPIO";
	uc_priv->gpio_count = BCM2835_GPIO_COUNT;
	gpios->reg = (struct bcm2835_gpio_regs *)plat->base;

	return 0;
}

U_BOOT_DRIVER(gpio_bcm2835) = {
	.name	= "gpio_bcm2835",
	.id	= UCLASS_GPIO,
	.ops	= &gpio_bcm2835_ops,
	.probe	= bcm2835_gpio_probe,
	.priv_auto_alloc_size = sizeof(struct bcm2835_gpios),
};