diff options
author | Wu Hao <hao.wu@intel.com> | 2018-06-29 20:53:25 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-07-15 07:55:46 -0400 |
commit | af275ec6160ba68714371cfe0575f9aa478ce02f (patch) | |
tree | 2c968af3bb4db2a5ae83b9e8d44521d32a9029f6 /drivers/fpga | |
parent | 29de76240e861d52b75405166337e94184f1875d (diff) |
fpga: dfl: add fpga manager platform driver for FME
This patch adds fpga manager driver for FPGA Management Engine (FME). It
implements fpga_manager_ops for FPGA Partial Reconfiguration function.
Signed-off-by: Tim Whisonant <tim.whisonant@intel.com>
Signed-off-by: Enno Luebbers <enno.luebbers@intel.com>
Signed-off-by: Shiva Rao <shiva.rao@intel.com>
Signed-off-by: Christopher Rauer <christopher.rauer@intel.com>
Signed-off-by: Kang Luwei <luwei.kang@intel.com>
Signed-off-by: Xiao Guangrong <guangrong.xiao@linux.intel.com>
Signed-off-by: Wu Hao <hao.wu@intel.com>
Acked-by: Alan Tull <atull@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/fpga')
-rw-r--r-- | drivers/fpga/Kconfig | 6 | ||||
-rw-r--r-- | drivers/fpga/Makefile | 1 | ||||
-rw-r--r-- | drivers/fpga/dfl-fme-mgr.c | 334 |
3 files changed, 341 insertions, 0 deletions
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index 43803a10cdae..46b48e5164db 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig | |||
@@ -156,6 +156,12 @@ config FPGA_DFL_FME | |||
156 | FPGA platform level management features. There shall be one FME | 156 | FPGA platform level management features. There shall be one FME |
157 | per DFL based FPGA device. | 157 | per DFL based FPGA device. |
158 | 158 | ||
159 | config FPGA_DFL_FME_MGR | ||
160 | tristate "FPGA DFL FME Manager Driver" | ||
161 | depends on FPGA_DFL_FME && HAS_IOMEM | ||
162 | help | ||
163 | Say Y to enable FPGA Manager driver for FPGA Management Engine. | ||
164 | |||
159 | config FPGA_DFL_PCI | 165 | config FPGA_DFL_PCI |
160 | tristate "FPGA DFL PCIe Device Driver" | 166 | tristate "FPGA DFL PCIe Device Driver" |
161 | depends on PCI && FPGA_DFL | 167 | depends on PCI && FPGA_DFL |
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index fd334d40aa1c..23f41b02f894 100644 --- a/drivers/fpga/Makefile +++ b/drivers/fpga/Makefile | |||
@@ -32,6 +32,7 @@ obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o | |||
32 | # FPGA Device Feature List Support | 32 | # FPGA Device Feature List Support |
33 | obj-$(CONFIG_FPGA_DFL) += dfl.o | 33 | obj-$(CONFIG_FPGA_DFL) += dfl.o |
34 | obj-$(CONFIG_FPGA_DFL_FME) += dfl-fme.o | 34 | obj-$(CONFIG_FPGA_DFL_FME) += dfl-fme.o |
35 | obj-$(CONFIG_FPGA_DFL_FME_MGR) += dfl-fme-mgr.o | ||
35 | 36 | ||
36 | dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o | 37 | dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o |
37 | 38 | ||
diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c new file mode 100644 index 000000000000..df843b51c663 --- /dev/null +++ b/drivers/fpga/dfl-fme-mgr.c | |||
@@ -0,0 +1,334 @@ | |||
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 | ||
33 | #define FME_PR_INTFC_ID_H 0xA8 | ||
34 | #define FME_PR_INTFC_ID_L 0xB0 | ||
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) { | ||
204 | dev_err(dev, "Invaild PR bitstream size\n"); | ||
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 | |||
275 | static int fme_mgr_probe(struct platform_device *pdev) | ||
276 | { | ||
277 | struct dfl_fme_mgr_pdata *pdata = dev_get_platdata(&pdev->dev); | ||
278 | struct device *dev = &pdev->dev; | ||
279 | struct fme_mgr_priv *priv; | ||
280 | struct fpga_manager *mgr; | ||
281 | struct resource *res; | ||
282 | int ret; | ||
283 | |||
284 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
285 | if (!priv) | ||
286 | return -ENOMEM; | ||
287 | |||
288 | if (pdata->ioaddr) | ||
289 | priv->ioaddr = pdata->ioaddr; | ||
290 | |||
291 | if (!priv->ioaddr) { | ||
292 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
293 | priv->ioaddr = devm_ioremap_resource(dev, res); | ||
294 | if (IS_ERR(priv->ioaddr)) | ||
295 | return PTR_ERR(priv->ioaddr); | ||
296 | } | ||
297 | |||
298 | mgr = fpga_mgr_create(dev, "DFL FME FPGA Manager", | ||
299 | &fme_mgr_ops, priv); | ||
300 | if (!mgr) | ||
301 | return -ENOMEM; | ||
302 | |||
303 | platform_set_drvdata(pdev, mgr); | ||
304 | |||
305 | ret = fpga_mgr_register(mgr); | ||
306 | if (ret) | ||
307 | fpga_mgr_free(mgr); | ||
308 | |||
309 | return ret; | ||
310 | } | ||
311 | |||
312 | static int fme_mgr_remove(struct platform_device *pdev) | ||
313 | { | ||
314 | struct fpga_manager *mgr = platform_get_drvdata(pdev); | ||
315 | |||
316 | fpga_mgr_unregister(mgr); | ||
317 | |||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | static struct platform_driver fme_mgr_driver = { | ||
322 | .driver = { | ||
323 | .name = DFL_FPGA_FME_MGR, | ||
324 | }, | ||
325 | .probe = fme_mgr_probe, | ||
326 | .remove = fme_mgr_remove, | ||
327 | }; | ||
328 | |||
329 | module_platform_driver(fme_mgr_driver); | ||
330 | |||
331 | MODULE_DESCRIPTION("FPGA Manager for DFL FPGA Management Engine"); | ||
332 | MODULE_AUTHOR("Intel Corporation"); | ||
333 | MODULE_LICENSE("GPL v2"); | ||
334 | MODULE_ALIAS("platform:dfl-fme-mgr"); | ||