Commit | Line | Data |
---|---|---|
af275ec6 WH |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * FPGA Manager Driver for FPGA Management Engine (FME) | |
4 | * | |
5 | * Copyright (C) 2017-2018 Intel Corporation, Inc. | |
6 | * | |
7 | * Authors: | |
8 | * Kang Luwei <luwei.kang@intel.com> | |
9 | * Xiao Guangrong <guangrong.xiao@linux.intel.com> | |
10 | * Wu Hao <hao.wu@intel.com> | |
11 | * Joseph Grecco <joe.grecco@intel.com> | |
12 | * Enno Luebbers <enno.luebbers@intel.com> | |
13 | * Tim Whisonant <tim.whisonant@intel.com> | |
14 | * Ananda Ravuri <ananda.ravuri@intel.com> | |
15 | * Christopher Rauer <christopher.rauer@intel.com> | |
16 | * Henry Mitchel <henry.mitchel@intel.com> | |
17 | */ | |
18 | ||
19 | #include <linux/bitfield.h> | |
20 | #include <linux/module.h> | |
21 | #include <linux/iopoll.h> | |
22 | #include <linux/io-64-nonatomic-lo-hi.h> | |
23 | #include <linux/fpga/fpga-mgr.h> | |
24 | ||
25 | #include "dfl-fme-pr.h" | |
26 | ||
27 | /* FME Partial Reconfiguration Sub Feature Register Set */ | |
28 | #define FME_PR_DFH 0x0 | |
29 | #define FME_PR_CTRL 0x8 | |
30 | #define FME_PR_STS 0x10 | |
31 | #define FME_PR_DATA 0x18 | |
32 | #define FME_PR_ERR 0x20 | |
e150e3f4 WH |
33 | #define FME_PR_INTFC_ID_L 0xA8 |
34 | #define FME_PR_INTFC_ID_H 0xB0 | |
af275ec6 WH |
35 | |
36 | /* FME PR Control Register Bitfield */ | |
37 | #define FME_PR_CTRL_PR_RST BIT_ULL(0) /* Reset PR engine */ | |
38 | #define FME_PR_CTRL_PR_RSTACK BIT_ULL(4) /* Ack for PR engine reset */ | |
39 | #define FME_PR_CTRL_PR_RGN_ID GENMASK_ULL(9, 7) /* PR Region ID */ | |
40 | #define FME_PR_CTRL_PR_START BIT_ULL(12) /* Start to request PR service */ | |
41 | #define FME_PR_CTRL_PR_COMPLETE BIT_ULL(13) /* PR data push completion */ | |
42 | ||
43 | /* FME PR Status Register Bitfield */ | |
44 | /* Number of available entries in HW queue inside the PR engine. */ | |
45 | #define FME_PR_STS_PR_CREDIT GENMASK_ULL(8, 0) | |
46 | #define FME_PR_STS_PR_STS BIT_ULL(16) /* PR operation status */ | |
47 | #define FME_PR_STS_PR_STS_IDLE 0 | |
48 | #define FME_PR_STS_PR_CTRLR_STS GENMASK_ULL(22, 20) /* Controller status */ | |
49 | #define FME_PR_STS_PR_HOST_STS GENMASK_ULL(27, 24) /* PR host status */ | |
50 | ||
51 | /* FME PR Data Register Bitfield */ | |
52 | /* PR data from the raw-binary file. */ | |
53 | #define FME_PR_DATA_PR_DATA_RAW GENMASK_ULL(32, 0) | |
54 | ||
55 | /* FME PR Error Register */ | |
56 | /* PR Operation errors detected. */ | |
57 | #define FME_PR_ERR_OPERATION_ERR BIT_ULL(0) | |
58 | /* CRC error detected. */ | |
59 | #define FME_PR_ERR_CRC_ERR BIT_ULL(1) | |
60 | /* Incompatible PR bitstream detected. */ | |
61 | #define FME_PR_ERR_INCOMPATIBLE_BS BIT_ULL(2) | |
62 | /* PR data push protocol violated. */ | |
63 | #define FME_PR_ERR_PROTOCOL_ERR BIT_ULL(3) | |
64 | /* PR data fifo overflow error detected */ | |
65 | #define FME_PR_ERR_FIFO_OVERFLOW BIT_ULL(4) | |
66 | ||
67 | #define PR_WAIT_TIMEOUT 8000000 | |
68 | #define PR_HOST_STATUS_IDLE 0 | |
69 | ||
70 | struct fme_mgr_priv { | |
71 | void __iomem *ioaddr; | |
72 | u64 pr_error; | |
73 | }; | |
74 | ||
75 | static u64 pr_error_to_mgr_status(u64 err) | |
76 | { | |
77 | u64 status = 0; | |
78 | ||
79 | if (err & FME_PR_ERR_OPERATION_ERR) | |
80 | status |= FPGA_MGR_STATUS_OPERATION_ERR; | |
81 | if (err & FME_PR_ERR_CRC_ERR) | |
82 | status |= FPGA_MGR_STATUS_CRC_ERR; | |
83 | if (err & FME_PR_ERR_INCOMPATIBLE_BS) | |
84 | status |= FPGA_MGR_STATUS_INCOMPATIBLE_IMAGE_ERR; | |
85 | if (err & FME_PR_ERR_PROTOCOL_ERR) | |
86 | status |= FPGA_MGR_STATUS_IP_PROTOCOL_ERR; | |
87 | if (err & FME_PR_ERR_FIFO_OVERFLOW) | |
88 | status |= FPGA_MGR_STATUS_FIFO_OVERFLOW_ERR; | |
89 | ||
90 | return status; | |
91 | } | |
92 | ||
93 | static u64 fme_mgr_pr_error_handle(void __iomem *fme_pr) | |
94 | { | |
95 | u64 pr_status, pr_error; | |
96 | ||
97 | pr_status = readq(fme_pr + FME_PR_STS); | |
98 | if (!(pr_status & FME_PR_STS_PR_STS)) | |
99 | return 0; | |
100 | ||
101 | pr_error = readq(fme_pr + FME_PR_ERR); | |
102 | writeq(pr_error, fme_pr + FME_PR_ERR); | |
103 | ||
104 | return pr_error; | |
105 | } | |
106 | ||
107 | static int fme_mgr_write_init(struct fpga_manager *mgr, | |
108 | struct fpga_image_info *info, | |
109 | const char *buf, size_t count) | |
110 | { | |
111 | struct device *dev = &mgr->dev; | |
112 | struct fme_mgr_priv *priv = mgr->priv; | |
113 | void __iomem *fme_pr = priv->ioaddr; | |
114 | u64 pr_ctrl, pr_status; | |
115 | ||
116 | if (!(info->flags & FPGA_MGR_PARTIAL_RECONFIG)) { | |
117 | dev_err(dev, "only supports partial reconfiguration.\n"); | |
118 | return -EINVAL; | |
119 | } | |
120 | ||
121 | dev_dbg(dev, "resetting PR before initiated PR\n"); | |
122 | ||
123 | pr_ctrl = readq(fme_pr + FME_PR_CTRL); | |
124 | pr_ctrl |= FME_PR_CTRL_PR_RST; | |
125 | writeq(pr_ctrl, fme_pr + FME_PR_CTRL); | |
126 | ||
127 | if (readq_poll_timeout(fme_pr + FME_PR_CTRL, pr_ctrl, | |
128 | pr_ctrl & FME_PR_CTRL_PR_RSTACK, 1, | |
129 | PR_WAIT_TIMEOUT)) { | |
130 | dev_err(dev, "PR Reset ACK timeout\n"); | |
131 | return -ETIMEDOUT; | |
132 | } | |
133 | ||
134 | pr_ctrl = readq(fme_pr + FME_PR_CTRL); | |
135 | pr_ctrl &= ~FME_PR_CTRL_PR_RST; | |
136 | writeq(pr_ctrl, fme_pr + FME_PR_CTRL); | |
137 | ||
138 | dev_dbg(dev, | |
139 | "waiting for PR resource in HW to be initialized and ready\n"); | |
140 | ||
141 | if (readq_poll_timeout(fme_pr + FME_PR_STS, pr_status, | |
142 | (pr_status & FME_PR_STS_PR_STS) == | |
143 | FME_PR_STS_PR_STS_IDLE, 1, PR_WAIT_TIMEOUT)) { | |
144 | dev_err(dev, "PR Status timeout\n"); | |
145 | priv->pr_error = fme_mgr_pr_error_handle(fme_pr); | |
146 | return -ETIMEDOUT; | |
147 | } | |
148 | ||
149 | dev_dbg(dev, "check and clear previous PR error\n"); | |
150 | priv->pr_error = fme_mgr_pr_error_handle(fme_pr); | |
151 | if (priv->pr_error) | |
152 | dev_dbg(dev, "previous PR error detected %llx\n", | |
153 | (unsigned long long)priv->pr_error); | |
154 | ||
155 | dev_dbg(dev, "set PR port ID\n"); | |
156 | ||
157 | pr_ctrl = readq(fme_pr + FME_PR_CTRL); | |
158 | pr_ctrl &= ~FME_PR_CTRL_PR_RGN_ID; | |
159 | pr_ctrl |= FIELD_PREP(FME_PR_CTRL_PR_RGN_ID, info->region_id); | |
160 | writeq(pr_ctrl, fme_pr + FME_PR_CTRL); | |
161 | ||
162 | return 0; | |
163 | } | |
164 | ||
165 | static int fme_mgr_write(struct fpga_manager *mgr, | |
166 | const char *buf, size_t count) | |
167 | { | |
168 | struct device *dev = &mgr->dev; | |
169 | struct fme_mgr_priv *priv = mgr->priv; | |
170 | void __iomem *fme_pr = priv->ioaddr; | |
171 | u64 pr_ctrl, pr_status, pr_data; | |
172 | int delay = 0, pr_credit, i = 0; | |
173 | ||
174 | dev_dbg(dev, "start request\n"); | |
175 | ||
176 | pr_ctrl = readq(fme_pr + FME_PR_CTRL); | |
177 | pr_ctrl |= FME_PR_CTRL_PR_START; | |
178 | writeq(pr_ctrl, fme_pr + FME_PR_CTRL); | |
179 | ||
180 | dev_dbg(dev, "pushing data from bitstream to HW\n"); | |
181 | ||
182 | /* | |
183 | * driver can push data to PR hardware using PR_DATA register once HW | |
184 | * has enough pr_credit (> 1), pr_credit reduces one for every 32bit | |
185 | * pr data write to PR_DATA register. If pr_credit <= 1, driver needs | |
186 | * to wait for enough pr_credit from hardware by polling. | |
187 | */ | |
188 | pr_status = readq(fme_pr + FME_PR_STS); | |
189 | pr_credit = FIELD_GET(FME_PR_STS_PR_CREDIT, pr_status); | |
190 | ||
191 | while (count > 0) { | |
192 | while (pr_credit <= 1) { | |
193 | if (delay++ > PR_WAIT_TIMEOUT) { | |
194 | dev_err(dev, "PR_CREDIT timeout\n"); | |
195 | return -ETIMEDOUT; | |
196 | } | |
197 | udelay(1); | |
198 | ||
199 | pr_status = readq(fme_pr + FME_PR_STS); | |
200 | pr_credit = FIELD_GET(FME_PR_STS_PR_CREDIT, pr_status); | |
201 | } | |
202 | ||
203 | if (count < 4) { | |
83b15fed | 204 | dev_err(dev, "Invalid PR bitstream size\n"); |
af275ec6 WH |
205 | return -EINVAL; |
206 | } | |
207 | ||
208 | pr_data = 0; | |
209 | pr_data |= FIELD_PREP(FME_PR_DATA_PR_DATA_RAW, | |
210 | *(((u32 *)buf) + i)); | |
211 | writeq(pr_data, fme_pr + FME_PR_DATA); | |
212 | count -= 4; | |
213 | pr_credit--; | |
214 | i++; | |
215 | } | |
216 | ||
217 | return 0; | |
218 | } | |
219 | ||
220 | static int fme_mgr_write_complete(struct fpga_manager *mgr, | |
221 | struct fpga_image_info *info) | |
222 | { | |
223 | struct device *dev = &mgr->dev; | |
224 | struct fme_mgr_priv *priv = mgr->priv; | |
225 | void __iomem *fme_pr = priv->ioaddr; | |
226 | u64 pr_ctrl; | |
227 | ||
228 | pr_ctrl = readq(fme_pr + FME_PR_CTRL); | |
229 | pr_ctrl |= FME_PR_CTRL_PR_COMPLETE; | |
230 | writeq(pr_ctrl, fme_pr + FME_PR_CTRL); | |
231 | ||
232 | dev_dbg(dev, "green bitstream push complete\n"); | |
233 | dev_dbg(dev, "waiting for HW to release PR resource\n"); | |
234 | ||
235 | if (readq_poll_timeout(fme_pr + FME_PR_CTRL, pr_ctrl, | |
236 | !(pr_ctrl & FME_PR_CTRL_PR_START), 1, | |
237 | PR_WAIT_TIMEOUT)) { | |
238 | dev_err(dev, "PR Completion ACK timeout.\n"); | |
239 | return -ETIMEDOUT; | |
240 | } | |
241 | ||
242 | dev_dbg(dev, "PR operation complete, checking status\n"); | |
243 | priv->pr_error = fme_mgr_pr_error_handle(fme_pr); | |
244 | if (priv->pr_error) { | |
245 | dev_dbg(dev, "PR error detected %llx\n", | |
246 | (unsigned long long)priv->pr_error); | |
247 | return -EIO; | |
248 | } | |
249 | ||
250 | dev_dbg(dev, "PR done successfully\n"); | |
251 | ||
252 | return 0; | |
253 | } | |
254 | ||
255 | static enum fpga_mgr_states fme_mgr_state(struct fpga_manager *mgr) | |
256 | { | |
257 | return FPGA_MGR_STATE_UNKNOWN; | |
258 | } | |
259 | ||
260 | static u64 fme_mgr_status(struct fpga_manager *mgr) | |
261 | { | |
262 | struct fme_mgr_priv *priv = mgr->priv; | |
263 | ||
264 | return pr_error_to_mgr_status(priv->pr_error); | |
265 | } | |
266 | ||
267 | static const struct fpga_manager_ops fme_mgr_ops = { | |
268 | .write_init = fme_mgr_write_init, | |
269 | .write = fme_mgr_write, | |
270 | .write_complete = fme_mgr_write_complete, | |
271 | .state = fme_mgr_state, | |
272 | .status = fme_mgr_status, | |
273 | }; | |
274 | ||
5ebae801 WH |
275 | static void fme_mgr_get_compat_id(void __iomem *fme_pr, |
276 | struct fpga_compat_id *id) | |
277 | { | |
278 | id->id_l = readq(fme_pr + FME_PR_INTFC_ID_L); | |
279 | id->id_h = readq(fme_pr + FME_PR_INTFC_ID_H); | |
280 | } | |
281 | ||
af275ec6 WH |
282 | static int fme_mgr_probe(struct platform_device *pdev) |
283 | { | |
284 | struct dfl_fme_mgr_pdata *pdata = dev_get_platdata(&pdev->dev); | |
5ebae801 | 285 | struct fpga_compat_id *compat_id; |
af275ec6 WH |
286 | struct device *dev = &pdev->dev; |
287 | struct fme_mgr_priv *priv; | |
288 | struct fpga_manager *mgr; | |
289 | struct resource *res; | |
af275ec6 WH |
290 | |
291 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | |
292 | if (!priv) | |
293 | return -ENOMEM; | |
294 | ||
295 | if (pdata->ioaddr) | |
296 | priv->ioaddr = pdata->ioaddr; | |
297 | ||
298 | if (!priv->ioaddr) { | |
299 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
300 | priv->ioaddr = devm_ioremap_resource(dev, res); | |
301 | if (IS_ERR(priv->ioaddr)) | |
302 | return PTR_ERR(priv->ioaddr); | |
303 | } | |
304 | ||
5ebae801 WH |
305 | compat_id = devm_kzalloc(dev, sizeof(*compat_id), GFP_KERNEL); |
306 | if (!compat_id) | |
307 | return -ENOMEM; | |
308 | ||
309 | fme_mgr_get_compat_id(priv->ioaddr, compat_id); | |
310 | ||
084181fe AT |
311 | mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager", |
312 | &fme_mgr_ops, priv); | |
af275ec6 WH |
313 | if (!mgr) |
314 | return -ENOMEM; | |
315 | ||
5ebae801 | 316 | mgr->compat_id = compat_id; |
af275ec6 WH |
317 | platform_set_drvdata(pdev, mgr); |
318 | ||
084181fe | 319 | return fpga_mgr_register(mgr); |
af275ec6 WH |
320 | } |
321 | ||
322 | static int fme_mgr_remove(struct platform_device *pdev) | |
323 | { | |
324 | struct fpga_manager *mgr = platform_get_drvdata(pdev); | |
325 | ||
326 | fpga_mgr_unregister(mgr); | |
327 | ||
328 | return 0; | |
329 | } | |
330 | ||
331 | static struct platform_driver fme_mgr_driver = { | |
332 | .driver = { | |
333 | .name = DFL_FPGA_FME_MGR, | |
334 | }, | |
335 | .probe = fme_mgr_probe, | |
336 | .remove = fme_mgr_remove, | |
337 | }; | |
338 | ||
339 | module_platform_driver(fme_mgr_driver); | |
340 | ||
341 | MODULE_DESCRIPTION("FPGA Manager for DFL FPGA Management Engine"); | |
342 | MODULE_AUTHOR("Intel Corporation"); | |
343 | MODULE_LICENSE("GPL v2"); | |
344 | MODULE_ALIAS("platform:dfl-fme-mgr"); |