summaryrefslogtreecommitdiff
path: root/drivers/fpga/efinix-spi.c
blob: ed9a41232a32a9a875999bba3268b4254733d479 (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
254
255
256
257
258
259
260
// SPDX-License-Identifier: GPL-2.0-only
/*
 * FPGA Manager Driver for Efinix
 *
 * Copyright (C) 2025 iris-GmbH infrared & intelligent sensors
 *
 * Ian Dannapel <iansdannapel@gmail.com>
 *
 * Load Efinix FPGA firmware over SPI using the serial configuration interface.
 *
 * Note: Only passive mode (host initiates transfer) is currently supported.
 */

#include <linux/delay.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/spi/spi.h>

/*
 * 13 dummy bytes generate 104 SPI clock cycles (8 bits each).
 * Used to meet the requirement for >100 clock cycles idle sequence.
 */
#define EFINIX_SPI_IDLE_CYCLES_BYTES 13

/*
 * tDMIN: Minimum time between deassertion of CRESET_N to first
 * valid configuration data. (32 µs)
 */
#define EFINIX_TDMIN_US_MIN    35
#define EFINIX_TDMIN_US_MAX    40

/*
 * tCRESET_N: Minimum CRESET_N low pulse width required to
 * trigger re-configuration. (320 ns)
 */
#define EFINIX_TCRESETN_DELAY_MIN_US  1
#define EFINIX_TCRESETN_DELAY_MAX_US  2

/*
 * tUSER: Minimum configuration duration after CDONE goes high
 * before entering user mode. (25 µs)
 */
#define EFINIX_TUSER_US_MIN    30
#define EFINIX_TUSER_US_MAX    35

struct efinix_spi_conf {
	struct spi_device *spi;
	struct gpio_desc *cdone;
	struct gpio_desc *reset;
};

static void efinix_spi_reset(struct efinix_spi_conf *conf)
{
	gpiod_set_value(conf->reset, 1);
	usleep_range(EFINIX_TCRESETN_DELAY_MIN_US, EFINIX_TCRESETN_DELAY_MAX_US);
	gpiod_set_value(conf->reset, 0);
	usleep_range(EFINIX_TDMIN_US_MIN, EFINIX_TDMIN_US_MAX);
}

static enum fpga_mgr_states efinix_spi_state(struct fpga_manager *mgr)
{
	struct efinix_spi_conf *conf = mgr->priv;

	if (conf->cdone && gpiod_get_value(conf->cdone) == 1)
		return FPGA_MGR_STATE_OPERATING;

	return FPGA_MGR_STATE_UNKNOWN;
}

static int efinix_spi_write_init(struct fpga_manager *mgr,
				 struct fpga_image_info *info,
				 const char *buf, size_t count)
{
	struct efinix_spi_conf *conf = mgr->priv;
	struct spi_transfer assert_cs = {
		/* Keep CS asserted across configuration. */
		.cs_change = 1,
	};
	struct spi_message message;
	int ret;

	if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
		dev_err(&mgr->dev, "Partial reconfiguration not supported\n");
		return -EOPNOTSUPP;
	}

	/*
	 * Efinix passive SPI configuration requires chip select to stay
	 * asserted from reset until the bitstream is fully clocked in.
	 * Lock the SPI bus so no other device can toggle CS between the
	 * reset pulse and the write/complete transfers.
	 */
	spi_bus_lock(conf->spi->controller);
	spi_message_init_with_transfers(&message, &assert_cs, 1);
	ret = spi_sync_locked(conf->spi, &message);
	if (ret) {
		spi_bus_unlock(conf->spi->controller);
		return ret;
	}

	/* Reset with CS asserted */
	efinix_spi_reset(conf);

	return 0;
}

static int efinix_spi_write(struct fpga_manager *mgr, const char *buf,
			    size_t count)
{
	struct spi_transfer write_xfer = {
		.tx_buf = buf,
		.len = count,
		.cs_change = 1,
	};
	struct efinix_spi_conf *conf = mgr->priv;
	struct spi_message message;
	int ret;

	spi_message_init_with_transfers(&message, &write_xfer, 1);
	ret = spi_sync_locked(conf->spi, &message);
	if (ret) {
		dev_err(&mgr->dev, "SPI error in firmware write: %d\n", ret);
		spi_bus_unlock(conf->spi->controller);
	}

	return ret;
}

static int efinix_spi_write_complete(struct fpga_manager *mgr,
				     struct fpga_image_info *info)
{
	unsigned long timeout =
		jiffies + usecs_to_jiffies(info->config_complete_timeout_us);
	struct spi_transfer clk_cycles = {
		.len = EFINIX_SPI_IDLE_CYCLES_BYTES,
		/* Release CS after the trailing idle clocks are sent. */
		.cs_change = 0,
	};
	struct efinix_spi_conf *conf = mgr->priv;
	struct spi_message message;
	int done, ret;
	bool expired = false;
	u8 *dummy_buf;

	dummy_buf = kzalloc(EFINIX_SPI_IDLE_CYCLES_BYTES, GFP_KERNEL);
	if (!dummy_buf) {
		ret = -ENOMEM;
		goto unlock_spi;
	}

	/*
	 * Keep the bus locked while sending the trailing idle clocks, then
	 * let this final transfer deassert CS to terminate configuration.
	 */
	clk_cycles.tx_buf = dummy_buf;
	spi_message_init_with_transfers(&message, &clk_cycles, 1);
	ret = spi_sync_locked(conf->spi, &message);
	if (ret) {
		dev_err(&mgr->dev, "SPI error in write complete: %d\n", ret);
		goto free_buf;
	}

	if (conf->cdone) {
		while (!expired) {
			done = gpiod_get_value(conf->cdone);
			if (done < 0) {
				ret = done;
				goto free_buf;
			}
			if (done)
				break;

			usleep_range(10, 20);
			expired = time_after(jiffies, timeout);
		}

		if (expired) {
			dev_err(&mgr->dev, "Timeout waiting for CDONE\n");
			ret = -ETIMEDOUT;
			goto free_buf;
		}
	}

	usleep_range(EFINIX_TUSER_US_MIN, EFINIX_TUSER_US_MAX);

free_buf:
	kfree(dummy_buf);
unlock_spi:
	spi_bus_unlock(conf->spi->controller);

	return ret;
}

static const struct fpga_manager_ops efinix_spi_ops = {
	.state = efinix_spi_state,
	.write_init = efinix_spi_write_init,
	.write = efinix_spi_write,
	.write_complete = efinix_spi_write_complete,
};

static int efinix_spi_probe(struct spi_device *spi)
{
	struct efinix_spi_conf *conf;
	struct fpga_manager *mgr;

	if (!(spi->mode & SPI_CPHA) || !(spi->mode & SPI_CPOL))
		return dev_err_probe(&spi->dev, -EINVAL,
				     "Unsupported SPI mode, set CPHA and CPOL\n");

	conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL);
	if (!conf)
		return -ENOMEM;

	conf->reset = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_HIGH);
	if (IS_ERR(conf->reset))
		return dev_err_probe(&spi->dev, PTR_ERR(conf->reset),
				     "Failed to get RESET gpio\n");

	conf->cdone = devm_gpiod_get_optional(&spi->dev, "cdone", GPIOD_IN);
	if (IS_ERR(conf->cdone))
		return dev_err_probe(&spi->dev, PTR_ERR(conf->cdone),
				     "Failed to get CDONE gpio\n");

	conf->spi = spi;

	mgr = devm_fpga_mgr_register(&spi->dev,
				     "Efinix FPGA Manager",
				     &efinix_spi_ops, conf);

	return PTR_ERR_OR_ZERO(mgr);
}

static const struct of_device_id efinix_spi_of_match[] = {
	{ .compatible = "efinix,trion-config", },
	{}
};
MODULE_DEVICE_TABLE(of, efinix_spi_of_match);

static const struct spi_device_id efinix_ids[] = {
	{ "trion-config", 0 },
	{},
};
MODULE_DEVICE_TABLE(spi, efinix_ids);

static struct spi_driver efinix_spi_driver = {
	.driver = {
		.name = "efinix-spi",
		.of_match_table = efinix_spi_of_match,
	},
	.probe = efinix_spi_probe,
	.id_table = efinix_ids,
};

module_spi_driver(efinix_spi_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ian Dannapel <iansdannapel@gmail.com>");
MODULE_DESCRIPTION("Efinix FPGA SPI Programming Driver");