summaryrefslogtreecommitdiff
path: root/board/keymile/km_arm/fpga_config.c
blob: abb5b7d60d0e1d3397a314a287bef687b2a35f00 (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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
// SPDX-License-Identifier: GPL-2.0+
/*
 * (C) Copyright 2012
 * Valentin Lontgchamp, Keymile AG, valentin.longchamp@keymile.com
 */

#include <common.h>
#include <i2c.h>
#include <linux/delay.h>
#include <linux/errno.h>

/* GPIO Pin from kirkwood connected to PROGRAM_B pin of the xilinx FPGA */
#define KM_XLX_PROGRAM_B_PIN    39

#define BOCO_ADDR	0x10

#define ID_REG		0x00
#define BOCO2_ID	0x5b

static int check_boco2(void)
{
	int ret;
	u8 id;

	ret = i2c_read(BOCO_ADDR, ID_REG, 1, &id, 1);
	if (ret) {
		printf("%s: error reading the BOCO id !!\n", __func__);
		return ret;
	}

	return (id == BOCO2_ID);
}

static int boco_clear_bits(u8 reg, u8 flags)
{
	int ret;
	u8 regval;

	/* give access to the EEPROM from FPGA */
	ret = i2c_read(BOCO_ADDR, reg, 1, &regval, 1);
	if (ret) {
		printf("%s: error reading the BOCO @%#x !!\n",
			__func__, reg);
		return ret;
	}
	regval &= ~flags;
	ret = i2c_write(BOCO_ADDR, reg, 1, &regval, 1);
	if (ret) {
		printf("%s: error writing the BOCO @%#x !!\n",
			__func__, reg);
		return ret;
	}

	return 0;
}

static int boco_set_bits(u8 reg, u8 flags)
{
	int ret;
	u8 regval;

	/* give access to the EEPROM from FPGA */
	ret = i2c_read(BOCO_ADDR, reg, 1, &regval, 1);
	if (ret) {
		printf("%s: error reading the BOCO @%#x !!\n",
			__func__, reg);
		return ret;
	}
	regval |= flags;
	ret = i2c_write(BOCO_ADDR, reg, 1, &regval, 1);
	if (ret) {
		printf("%s: error writing the BOCO @%#x !!\n",
			__func__, reg);
		return ret;
	}

	return 0;
}

#define SPI_REG		0x06
#define CFG_EEPROM	0x02
#define FPGA_PROG	0x04
#define FPGA_INIT_B	0x10
#define FPGA_DONE	0x20

#ifndef CONFIG_KM_FPGA_FORCE_CONFIG
static int fpga_done(void)
{
	int ret = 0;
	u8 regval;

	/* this is only supported with the boco2 design */
	if (!check_boco2())
		return 0;

	ret = i2c_read(BOCO_ADDR, SPI_REG, 1, &regval, 1);
	if (ret) {
		printf("%s: error reading the BOCO @%#x !!\n",
			__func__, SPI_REG);
		return 0;
	}

	return regval & FPGA_DONE ? 1 : 0;
}
#endif /* CONFIG_KM_FPGA_FORCE_CONFIG */

static int skip;

int trigger_fpga_config(void)
{
	int ret = 0;

	skip = 0;
#ifndef CONFIG_KM_FPGA_FORCE_CONFIG
	/* if the FPGA is already configured, we do not want to
	 * reconfigure it */
	skip = 0;
	if (fpga_done()) {
		printf("PCIe FPGA config: skipped\n");
		skip = 1;
		return 0;
	}
#endif /* CONFIG_KM_FPGA_FORCE_CONFIG */

	if (check_boco2()) {
		/* we have a BOCO2, this has to be triggered here */

		/* make sure the FPGA_can access the EEPROM */
		ret = boco_clear_bits(SPI_REG, CFG_EEPROM);
		if (ret)
			return ret;

		/* trigger the config start */
		ret = boco_clear_bits(SPI_REG, FPGA_PROG | FPGA_INIT_B);
		if (ret)
			return ret;

		/* small delay for the pulse */
		udelay(10);

		/* up signal for pulse end */
		ret = boco_set_bits(SPI_REG, FPGA_PROG);
		if (ret)
			return ret;

		/* finally, raise INIT_B to remove the config delay */
		ret = boco_set_bits(SPI_REG, FPGA_INIT_B);
		if (ret)
			return ret;

	} else {
		/* we do it the old way, with the gpio pin */
		kw_gpio_set_valid(KM_XLX_PROGRAM_B_PIN, 1);
		kw_gpio_direction_output(KM_XLX_PROGRAM_B_PIN, 0);
		/* small delay for the pulse */
		udelay(10);
		kw_gpio_direction_input(KM_XLX_PROGRAM_B_PIN);
	}

	return 0;
}

int wait_for_fpga_config(void)
{
	int ret = 0;
	u8 spictrl;
	u32 timeout = 20000;

	if (skip)
		return 0;

	if (!check_boco2()) {
		/* we do not have BOCO2, this is not really used */
		return 0;
	}

	printf("PCIe FPGA config:");
	do {
		ret = i2c_read(BOCO_ADDR, SPI_REG, 1, &spictrl, 1);
		if (ret) {
			printf("%s: error reading the BOCO spictrl !!\n",
				__func__);
			return ret;
		}
		if (timeout-- == 0) {
			printf(" FPGA_DONE timeout\n");
			return -EFAULT;
		}
		udelay(10);
	} while (!(spictrl & FPGA_DONE));

	printf(" done\n");

	return 0;
}

#if defined(CONFIG_KM_FPGA_NO_RESET)
int fpga_reset(void)
{
	/* no dedicated reset pin for FPGA */
	return 0;
}
#else

#define PRST1		0x4
#define PCIE_RST	0x10
#define TRAFFIC_RST	0x04

int fpga_reset(void)
{
	int ret = 0;
	u8 resets;

	if (!check_boco2()) {
		/* we do not have BOCO2, this is not really used */
		return 0;
	}

	/* if we have skipped, we only want to reset the PCIe part */
	resets = skip ? PCIE_RST : PCIE_RST | TRAFFIC_RST;

	ret = boco_clear_bits(PRST1, resets);
	if (ret)
		return ret;

	/* small delay for the pulse */
	udelay(10);

	ret = boco_set_bits(PRST1, resets);
	if (ret)
		return ret;

	return 0;
}
#endif

/* the FPGA was configured, we configure the BOCO2 so that the EEPROM
 * is available from the Bobcat SPI bus */
int toggle_eeprom_spi_bus(void)
{
	int ret = 0;

	if (!check_boco2()) {
		/* we do not have BOCO2, this is not really used */
		return 0;
	}

	ret = boco_set_bits(SPI_REG, CFG_EEPROM);
	if (ret)
		return ret;

	return 0;
}