Commit | Line | Data |
---|---|---|
75a6faf6 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
061c97d1 AG |
2 | /* |
3 | * Xilinx Spartan6 Slave Serial SPI Driver | |
4 | * | |
5 | * Copyright (C) 2017 DENX Software Engineering | |
6 | * | |
7 | * Anatolij Gustschin <agust@denx.de> | |
8 | * | |
061c97d1 AG |
9 | * Manage Xilinx FPGA firmware that is loaded over SPI using |
10 | * the slave serial configuration interface. | |
11 | */ | |
12 | ||
13 | #include <linux/delay.h> | |
14 | #include <linux/device.h> | |
15 | #include <linux/fpga/fpga-mgr.h> | |
16 | #include <linux/gpio/consumer.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/mod_devicetable.h> | |
19 | #include <linux/of.h> | |
20 | #include <linux/spi/spi.h> | |
21 | #include <linux/sizes.h> | |
22 | ||
23 | struct xilinx_spi_conf { | |
24 | struct spi_device *spi; | |
25 | struct gpio_desc *prog_b; | |
26 | struct gpio_desc *done; | |
27 | }; | |
28 | ||
29 | static enum fpga_mgr_states xilinx_spi_state(struct fpga_manager *mgr) | |
30 | { | |
31 | struct xilinx_spi_conf *conf = mgr->priv; | |
32 | ||
33 | if (!gpiod_get_value(conf->done)) | |
34 | return FPGA_MGR_STATE_RESET; | |
35 | ||
36 | return FPGA_MGR_STATE_UNKNOWN; | |
37 | } | |
38 | ||
39 | static int xilinx_spi_write_init(struct fpga_manager *mgr, | |
40 | struct fpga_image_info *info, | |
41 | const char *buf, size_t count) | |
42 | { | |
43 | struct xilinx_spi_conf *conf = mgr->priv; | |
44 | const size_t prog_latency_7500us = 7500; | |
45 | const size_t prog_pulse_1us = 1; | |
46 | ||
47 | if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) { | |
48 | dev_err(&mgr->dev, "Partial reconfiguration not supported.\n"); | |
49 | return -EINVAL; | |
50 | } | |
51 | ||
52 | gpiod_set_value(conf->prog_b, 1); | |
53 | ||
54 | udelay(prog_pulse_1us); /* min is 500 ns */ | |
55 | ||
56 | gpiod_set_value(conf->prog_b, 0); | |
57 | ||
58 | if (gpiod_get_value(conf->done)) { | |
59 | dev_err(&mgr->dev, "Unexpected DONE pin state...\n"); | |
60 | return -EIO; | |
61 | } | |
62 | ||
63 | /* program latency */ | |
64 | usleep_range(prog_latency_7500us, prog_latency_7500us + 100); | |
65 | return 0; | |
66 | } | |
67 | ||
68 | static int xilinx_spi_write(struct fpga_manager *mgr, const char *buf, | |
69 | size_t count) | |
70 | { | |
71 | struct xilinx_spi_conf *conf = mgr->priv; | |
72 | const char *fw_data = buf; | |
73 | const char *fw_data_end = fw_data + count; | |
74 | ||
75 | while (fw_data < fw_data_end) { | |
76 | size_t remaining, stride; | |
77 | int ret; | |
78 | ||
79 | remaining = fw_data_end - fw_data; | |
80 | stride = min_t(size_t, remaining, SZ_4K); | |
81 | ||
82 | ret = spi_write(conf->spi, fw_data, stride); | |
83 | if (ret) { | |
84 | dev_err(&mgr->dev, "SPI error in firmware write: %d\n", | |
85 | ret); | |
86 | return ret; | |
87 | } | |
88 | fw_data += stride; | |
89 | } | |
90 | ||
91 | return 0; | |
92 | } | |
93 | ||
94 | static int xilinx_spi_apply_cclk_cycles(struct xilinx_spi_conf *conf) | |
95 | { | |
96 | struct spi_device *spi = conf->spi; | |
97 | const u8 din_data[1] = { 0xff }; | |
98 | int ret; | |
99 | ||
100 | ret = spi_write(conf->spi, din_data, sizeof(din_data)); | |
101 | if (ret) | |
102 | dev_err(&spi->dev, "applying CCLK cycles failed: %d\n", ret); | |
103 | ||
104 | return ret; | |
105 | } | |
106 | ||
107 | static int xilinx_spi_write_complete(struct fpga_manager *mgr, | |
108 | struct fpga_image_info *info) | |
109 | { | |
110 | struct xilinx_spi_conf *conf = mgr->priv; | |
111 | unsigned long timeout; | |
112 | int ret; | |
113 | ||
114 | if (gpiod_get_value(conf->done)) | |
115 | return xilinx_spi_apply_cclk_cycles(conf); | |
116 | ||
117 | timeout = jiffies + usecs_to_jiffies(info->config_complete_timeout_us); | |
118 | ||
119 | while (time_before(jiffies, timeout)) { | |
120 | ||
121 | ret = xilinx_spi_apply_cclk_cycles(conf); | |
122 | if (ret) | |
123 | return ret; | |
124 | ||
125 | if (gpiod_get_value(conf->done)) | |
126 | return xilinx_spi_apply_cclk_cycles(conf); | |
127 | } | |
128 | ||
129 | dev_err(&mgr->dev, "Timeout after config data transfer.\n"); | |
130 | return -ETIMEDOUT; | |
131 | } | |
132 | ||
133 | static const struct fpga_manager_ops xilinx_spi_ops = { | |
134 | .state = xilinx_spi_state, | |
135 | .write_init = xilinx_spi_write_init, | |
136 | .write = xilinx_spi_write, | |
137 | .write_complete = xilinx_spi_write_complete, | |
138 | }; | |
139 | ||
140 | static int xilinx_spi_probe(struct spi_device *spi) | |
141 | { | |
142 | struct xilinx_spi_conf *conf; | |
7085e2a9 | 143 | struct fpga_manager *mgr; |
061c97d1 AG |
144 | |
145 | conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL); | |
146 | if (!conf) | |
147 | return -ENOMEM; | |
148 | ||
149 | conf->spi = spi; | |
150 | ||
151 | /* PROGRAM_B is active low */ | |
152 | conf->prog_b = devm_gpiod_get(&spi->dev, "prog_b", GPIOD_OUT_LOW); | |
153 | if (IS_ERR(conf->prog_b)) { | |
154 | dev_err(&spi->dev, "Failed to get PROGRAM_B gpio: %ld\n", | |
155 | PTR_ERR(conf->prog_b)); | |
156 | return PTR_ERR(conf->prog_b); | |
157 | } | |
158 | ||
159 | conf->done = devm_gpiod_get(&spi->dev, "done", GPIOD_IN); | |
160 | if (IS_ERR(conf->done)) { | |
161 | dev_err(&spi->dev, "Failed to get DONE gpio: %ld\n", | |
162 | PTR_ERR(conf->done)); | |
163 | return PTR_ERR(conf->done); | |
164 | } | |
165 | ||
084181fe AT |
166 | mgr = devm_fpga_mgr_create(&spi->dev, |
167 | "Xilinx Slave Serial FPGA Manager", | |
168 | &xilinx_spi_ops, conf); | |
7085e2a9 AT |
169 | if (!mgr) |
170 | return -ENOMEM; | |
171 | ||
172 | spi_set_drvdata(spi, mgr); | |
173 | ||
084181fe | 174 | return fpga_mgr_register(mgr); |
061c97d1 AG |
175 | } |
176 | ||
177 | static int xilinx_spi_remove(struct spi_device *spi) | |
178 | { | |
7085e2a9 AT |
179 | struct fpga_manager *mgr = spi_get_drvdata(spi); |
180 | ||
181 | fpga_mgr_unregister(mgr); | |
061c97d1 AG |
182 | |
183 | return 0; | |
184 | } | |
185 | ||
186 | static const struct of_device_id xlnx_spi_of_match[] = { | |
187 | { .compatible = "xlnx,fpga-slave-serial", }, | |
188 | {} | |
189 | }; | |
190 | MODULE_DEVICE_TABLE(of, xlnx_spi_of_match); | |
191 | ||
192 | static struct spi_driver xilinx_slave_spi_driver = { | |
193 | .driver = { | |
194 | .name = "xlnx-slave-spi", | |
195 | .of_match_table = of_match_ptr(xlnx_spi_of_match), | |
196 | }, | |
197 | .probe = xilinx_spi_probe, | |
198 | .remove = xilinx_spi_remove, | |
199 | }; | |
200 | ||
201 | module_spi_driver(xilinx_slave_spi_driver) | |
202 | ||
203 | MODULE_LICENSE("GPL v2"); | |
204 | MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>"); | |
205 | MODULE_DESCRIPTION("Load Xilinx FPGA firmware over SPI"); |