summaryrefslogtreecommitdiff
path: root/src/soc/intel/braswell/gpio_support.c
blob: d2abccc259b3b1aef5f1545a5d67f876cc9e0215 (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
/*
 * This file is part of the coreboot project.
 *
 *
 * 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 <device/mmio.h>
#include <gpio.h>
#include <console/console.h>
#include <soc/gpio.h>

/*
 * Return family number and internal pad number in that community by pad number
 * and which community it is in.
 */
uint16_t gpio_family_number(uint8_t community, uint8_t pad)
{
	/*
	 * Refer to BSW BIOS Writers Guide, Table "Family Number". BSW has 4 GPIO communities.
	 * Each community has up to 7 families and each family contains a range of Pad numbers.
	 * The number in the array is the maximum no. of that range.
	 * For example: East community, family 0, Pad 0~11.
	 */
	static const uint8_t community_base[GPIO_COMMUNITY_COUNT]
		[GPIO_FAMILIES_MAX_PER_COMM + 1] = {
		{0,  8, 16, 24, 32, 40, 48, 56}, /* Southwest */
		{0,  9, 22, 34, 46, 59, 59, 59}, /* North */
		{0, 12, 24, 24, 24, 24, 24, 24}, /* East */
		{0,  8, 20, 26, 34, 44, 55, 55}  /* Southeast */
	};
	const uint8_t *base;
	uint8_t i;

	/* Validate the pad number */
	if (pad > community_base[community][7])
		die("Pad number is out of range!");

	/* Locate the family number for the pad */
	base = &community_base[community][0];
	for (i = 0; i < 7; i++) {
		if ((pad >= base[0]) && (pad < base[1]))
			break;
		base++;
	}

	/* Family number in high byte and inner pad number in lowest byte */
	return (i << 8) + pad - *base;
}

/*
 * Return pad configuration register offset by pad number and which community it is in.
 */
uint32_t *gpio_pad_config_reg(uint8_t community, uint8_t pad)
{
	uint16_t fpad;
	uint32_t *pad_config_reg;

	/* Get the GPIO family number */
	fpad = gpio_family_number(community, pad);

	/*
	 * Refer to BSW BIOS Writers Guide, Table "Per Pad Memory Space Registers Addresses"
	 * for the Pad configuration register calculation.
	 */
	pad_config_reg = (uint32_t *)(COMMUNITY_BASE(community) + FAMILY_PAD_REGS_OFF +
		(FAMILY_PAD_REGS_SIZE * (fpad >> 8)) + (GPIO_REGS_SIZE * (fpad & 0xff)));

	return pad_config_reg;
}

static int gpio_get_community_num(gpio_t gpio_num, int *pad)
{
	int comm = 0;

	if (gpio_num >= GP_SW_00 && gpio_num <= GP_SW_97) {
		comm =  GP_SOUTHWEST;
		*pad = gpio_num % GP_SOUTHWEST_COUNT;

	} else if (gpio_num >= GP_NC_00 && gpio_num <= GP_NC_72) {
		comm =  GP_NORTH;
		*pad = gpio_num % GP_SOUTHWEST_COUNT;

	} else if (gpio_num >= GP_E_00 && gpio_num <= GP_E_26) {
		comm =  GP_EAST;
		*pad = gpio_num % (GP_SOUTHWEST_COUNT + GP_NORTH_COUNT);

	} else {
		comm = GP_SOUTHEAST;
		*pad = gpio_num % (GP_SOUTHWEST_COUNT + GP_NORTH_COUNT + GP_EAST_COUNT);
	}
	return comm;
}

static void gpio_config_pad(gpio_t gpio_num, const struct soc_gpio_map *cfg)
{
	int comm = 0;
	int pad_num = 0;
	uint32_t *pad_config0_reg;
	uint32_t *pad_config1_reg;

	if (gpio_num > MAX_GPIO_CNT)
		return;
	/* Get GPIO Community based on GPIO_NUMBER */
	comm = gpio_get_community_num(gpio_num, &pad_num);
	/* CONF0 */
	pad_config0_reg = gpio_pad_config_reg(comm, pad_num);
	/* CONF1 */
	pad_config1_reg = pad_config0_reg + 1;

	write32(pad_config0_reg, cfg->pad_conf0);
	write32(pad_config1_reg, cfg->pad_conf1);
}

void gpio_input_pullup(gpio_t gpio_num)
{
	struct soc_gpio_map cfg = GPIO_INPUT_PU_20K;
	gpio_config_pad(gpio_num, &cfg);
}

void gpio_input_pulldown(gpio_t gpio_num)
{
	struct soc_gpio_map cfg = GPIO_INPUT_PD_20K;
	gpio_config_pad(gpio_num, &cfg);
}

void gpio_input(gpio_t gpio_num)
{
	struct soc_gpio_map cfg = GPIO_INPUT_NO_PULL;
	gpio_config_pad(gpio_num, &cfg);
}

int gpio_get(gpio_t gpio_num)
{
	int comm = 0;
	int pad_num = 0;
	uint32_t *pad_config0_reg;
	u32 pad_value;

	if (gpio_num > MAX_GPIO_CNT)
		return -1;

	/* Get GPIO Community based on GPIO_NUMBER */
	comm = gpio_get_community_num(gpio_num, &pad_num);
	/* CONF0 */
	pad_config0_reg = gpio_pad_config_reg(comm, pad_num);

	pad_value = read32(pad_config0_reg);

	return pad_value & PAD_RX_BIT;
}

int get_gpio(int community_base, int pad0_offset)
{
	return (read32((void *)(community_base + pad0_offset))) & PAD_RX_BIT;
}