summaryrefslogtreecommitdiffstats
path: root/src/lib/gpio.c
blob: b52d7b0c5fb6a846048c958c3754dc4f7c22a8cb (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
/*
 * This file is part of the coreboot project.
 *
 * Copyright 2014 Google Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <assert.h>
#include <base3.h>
#include <compiler.h>
#include <console/console.h>
#include <delay.h>
#include <gpio.h>

static int _gpio_base2_value(const gpio_t gpio[], int num_gpio)
{
	int i, result = 0;

	/* Wait until signals become stable */
	udelay(10);

	for (i = 0; i < num_gpio; i++)
		result |= gpio_get(gpio[i]) << i;

	return result;
}

int gpio_base2_value(const gpio_t gpio[], int num_gpio)
{
	int i;

	for (i = 0; i < num_gpio; i++)
		gpio_input(gpio[i]);

	return _gpio_base2_value(gpio, num_gpio);
}

int gpio_pulldown_base2_value(const gpio_t gpio[], int num_gpio)
{
	int i;

	for (i = 0; i < num_gpio; i++)
		gpio_input_pulldown(gpio[i]);

	return _gpio_base2_value(gpio, num_gpio);
}

int gpio_pullup_base2_value(const gpio_t gpio[], int num_gpio)
{
	int i;

	for (i = 0; i < num_gpio; i++)
		gpio_input_pullup(gpio[i]);

	return _gpio_base2_value(gpio, num_gpio);
}

int _gpio_base3_value(const gpio_t gpio[], int num_gpio, int binary_first)
{
	/*
	 * GPIOs which are tied to stronger external pull up or pull down
	 * will stay there regardless of the internal pull up or pull
	 * down setting.
	 *
	 * GPIOs which are floating will go to whatever level they're
	 * internally pulled to.
	 */

	static const char tristate_char[] = {[0] = '0', [1] = '1', [Z] = 'Z'};
	int temp;
	int index;
	int result = 0;
	int has_z = 0;
	int binary_below = 0;
	char value[32];
	assert(num_gpio <= 32);

	/* Enable internal pull up */
	for (index = 0; index < num_gpio; ++index)
		gpio_input_pullup(gpio[index]);

	/* Wait until signals become stable */
	udelay(10);

	/* Get gpio values at internal pull up */
	for (index = 0; index < num_gpio; ++index)
		value[index] = gpio_get(gpio[index]);

	/* Enable internal pull down */
	for (index = 0; index < num_gpio; ++index)
		gpio_input_pulldown(gpio[index]);

	/* Wait until signals become stable */
	udelay(10);

	/*
	 * Get gpio values at internal pull down.
	 * Compare with gpio pull up value and then
	 * determine a gpio final value/state:
	 *  0: pull down
	 *  1: pull up
	 *  2: floating
	 */
	printk(BIOS_DEBUG, "Reading tristate GPIOs: ");
	for (index = num_gpio - 1; index >= 0; --index) {
		temp = gpio_get(gpio[index]);
		temp |= ((value[index] ^ temp) << 1);
		printk(BIOS_DEBUG, "%c ", tristate_char[temp]);
		result = (result * 3) + temp;

		/*
		 * For binary_first we keep track of the normal ternary result
		 * and whether we found any pin that was a Z. We also determine
		 * the amount of numbers that can be represented with only
		 * binary digits (no Z) whose value in the normal ternary system
		 * is lower than the one we are parsing. Counting from the left,
		 * we add 2^i for any '1' digit to account for the binary
		 * numbers whose values would be below it if all following
		 * digits we parsed would be '0'. As soon as we find a '2' digit
		 * we can total the remaining binary numbers below as 2^(i+1)
		 * because we know that all binary representations counting only
		 * this and following digits must have values below our number
		 * (since 1xxx is always smaller than 2xxx).
		 *
		 * Example: 1 0 2 1 (counting from the left / most significant)
		 * '1' at 3^3: Add 2^3 = 8 to account for binaries 0000-0111
		 * '0' at 3^2: Ignore (not all binaries 1000-1100 are below us)
		 * '2' at 3^1: Add 2^(1+1) = 4 to account for binaries 1000-1011
		 * Stop adding for lower digits (3^0), all already accounted
		 * now. We know that there can be no binary numbers 1020-102X.
		 */
		if (binary_first && !has_z) {
			switch (temp) {
			case 0:	/* Ignore '0' digits. */
				break;
			case 1:	/* Account for binaries 0 to 2^index - 1. */
				binary_below += 1 << index;
				break;
			case 2:	/* Account for binaries 0 to 2^(index+1) - 1. */
				binary_below += 1 << (index + 1);
				has_z = 1;
			}
		}
	}

	if (binary_first) {
		if (has_z)
			result = result + (1 << num_gpio) - binary_below;
		else /* binary_below is normal binary system value if !has_z. */
			result = binary_below;
	}

	printk(BIOS_DEBUG, "= %d (%s base3 number system)\n", result,
	       binary_first ? "binary_first" : "standard");

	/* Disable pull up / pull down to conserve power */
	for (index = 0; index < num_gpio; ++index)
		gpio_input(gpio[index]);

	return result;
}

/* Default handler for ACPI path is to return NULL */
__weak const char *gpio_acpi_path(gpio_t gpio)
{
	return NULL;
}

/* Default handler returns 0 because type of gpio_t is unknown */
__weak uint16_t gpio_acpi_pin(gpio_t gpio)
{
	return 0;
}