]>
Commit | Line | Data |
---|---|---|
d201cc17 MG |
1 | /* |
2 | * Driver for Altera Partial Reconfiguration IP Core | |
3 | * | |
4 | * Copyright (C) 2016-2017 Intel Corporation | |
5 | * | |
6 | * Based on socfpga-a10.c Copyright (C) 2015-2016 Altera Corporation | |
7 | * by Alan Tull <atull@opensource.altera.com> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify it | |
10 | * under the terms and conditions of the GNU General Public License, | |
11 | * version 2, as published by the Free Software Foundation. | |
12 | * | |
13 | * This program is distributed in the hope it will be useful, but WITHOUT | |
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
16 | * more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License along with | |
19 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
20 | */ | |
21 | #include <linux/delay.h> | |
22 | #include <linux/fpga/altera-pr-ip-core.h> | |
23 | #include <linux/fpga/fpga-mgr.h> | |
24 | #include <linux/module.h> | |
25 | ||
26 | #define ALT_PR_DATA_OFST 0x00 | |
27 | #define ALT_PR_CSR_OFST 0x04 | |
28 | ||
29 | #define ALT_PR_CSR_PR_START BIT(0) | |
30 | #define ALT_PR_CSR_STATUS_SFT 2 | |
31 | #define ALT_PR_CSR_STATUS_MSK (7 << ALT_PR_CSR_STATUS_SFT) | |
32 | #define ALT_PR_CSR_STATUS_NRESET (0 << ALT_PR_CSR_STATUS_SFT) | |
33 | #define ALT_PR_CSR_STATUS_PR_ERR (1 << ALT_PR_CSR_STATUS_SFT) | |
34 | #define ALT_PR_CSR_STATUS_CRC_ERR (2 << ALT_PR_CSR_STATUS_SFT) | |
35 | #define ALT_PR_CSR_STATUS_BAD_BITS (3 << ALT_PR_CSR_STATUS_SFT) | |
36 | #define ALT_PR_CSR_STATUS_PR_IN_PROG (4 << ALT_PR_CSR_STATUS_SFT) | |
37 | #define ALT_PR_CSR_STATUS_PR_SUCCESS (5 << ALT_PR_CSR_STATUS_SFT) | |
38 | ||
39 | struct alt_pr_priv { | |
40 | void __iomem *reg_base; | |
41 | }; | |
42 | ||
43 | static enum fpga_mgr_states alt_pr_fpga_state(struct fpga_manager *mgr) | |
44 | { | |
45 | struct alt_pr_priv *priv = mgr->priv; | |
46 | const char *err = "unknown"; | |
47 | enum fpga_mgr_states ret = FPGA_MGR_STATE_UNKNOWN; | |
48 | u32 val; | |
49 | ||
50 | val = readl(priv->reg_base + ALT_PR_CSR_OFST); | |
51 | ||
52 | val &= ALT_PR_CSR_STATUS_MSK; | |
53 | ||
54 | switch (val) { | |
55 | case ALT_PR_CSR_STATUS_NRESET: | |
56 | return FPGA_MGR_STATE_RESET; | |
57 | ||
58 | case ALT_PR_CSR_STATUS_PR_ERR: | |
59 | err = "pr error"; | |
60 | ret = FPGA_MGR_STATE_WRITE_ERR; | |
61 | break; | |
62 | ||
63 | case ALT_PR_CSR_STATUS_CRC_ERR: | |
64 | err = "crc error"; | |
65 | ret = FPGA_MGR_STATE_WRITE_ERR; | |
66 | break; | |
67 | ||
68 | case ALT_PR_CSR_STATUS_BAD_BITS: | |
69 | err = "bad bits"; | |
70 | ret = FPGA_MGR_STATE_WRITE_ERR; | |
71 | break; | |
72 | ||
73 | case ALT_PR_CSR_STATUS_PR_IN_PROG: | |
74 | return FPGA_MGR_STATE_WRITE; | |
75 | ||
76 | case ALT_PR_CSR_STATUS_PR_SUCCESS: | |
77 | return FPGA_MGR_STATE_OPERATING; | |
78 | ||
79 | default: | |
80 | break; | |
81 | } | |
82 | ||
83 | dev_err(&mgr->dev, "encountered error code %d (%s) in %s()\n", | |
84 | val, err, __func__); | |
85 | return ret; | |
86 | } | |
87 | ||
88 | static int alt_pr_fpga_write_init(struct fpga_manager *mgr, | |
89 | struct fpga_image_info *info, | |
90 | const char *buf, size_t count) | |
91 | { | |
92 | struct alt_pr_priv *priv = mgr->priv; | |
93 | u32 val; | |
94 | ||
95 | if (!(info->flags & FPGA_MGR_PARTIAL_RECONFIG)) { | |
96 | dev_err(&mgr->dev, "%s Partial Reconfiguration flag not set\n", | |
97 | __func__); | |
98 | return -EINVAL; | |
99 | } | |
100 | ||
101 | val = readl(priv->reg_base + ALT_PR_CSR_OFST); | |
102 | ||
103 | if (val & ALT_PR_CSR_PR_START) { | |
104 | dev_err(&mgr->dev, | |
105 | "%s Partial Reconfiguration already started\n", | |
106 | __func__); | |
107 | return -EINVAL; | |
108 | } | |
109 | ||
110 | writel(val | ALT_PR_CSR_PR_START, priv->reg_base + ALT_PR_CSR_OFST); | |
111 | ||
112 | return 0; | |
113 | } | |
114 | ||
115 | static int alt_pr_fpga_write(struct fpga_manager *mgr, const char *buf, | |
116 | size_t count) | |
117 | { | |
118 | struct alt_pr_priv *priv = mgr->priv; | |
119 | u32 *buffer_32 = (u32 *)buf; | |
120 | size_t i = 0; | |
121 | ||
122 | if (count <= 0) | |
123 | return -EINVAL; | |
124 | ||
125 | /* Write out the complete 32-bit chunks */ | |
126 | while (count >= sizeof(u32)) { | |
127 | writel(buffer_32[i++], priv->reg_base); | |
128 | count -= sizeof(u32); | |
129 | } | |
130 | ||
131 | /* Write out remaining non 32-bit chunks */ | |
132 | switch (count) { | |
133 | case 3: | |
134 | writel(buffer_32[i++] & 0x00ffffff, priv->reg_base); | |
135 | break; | |
136 | case 2: | |
137 | writel(buffer_32[i++] & 0x0000ffff, priv->reg_base); | |
138 | break; | |
139 | case 1: | |
140 | writel(buffer_32[i++] & 0x000000ff, priv->reg_base); | |
141 | break; | |
142 | case 0: | |
143 | break; | |
144 | default: | |
145 | /* This will never happen */ | |
146 | return -EFAULT; | |
147 | } | |
148 | ||
149 | if (alt_pr_fpga_state(mgr) == FPGA_MGR_STATE_WRITE_ERR) | |
150 | return -EIO; | |
151 | ||
152 | return 0; | |
153 | } | |
154 | ||
155 | static int alt_pr_fpga_write_complete(struct fpga_manager *mgr, | |
156 | struct fpga_image_info *info) | |
157 | { | |
158 | u32 i = 0; | |
159 | ||
160 | do { | |
161 | switch (alt_pr_fpga_state(mgr)) { | |
162 | case FPGA_MGR_STATE_WRITE_ERR: | |
163 | return -EIO; | |
164 | ||
165 | case FPGA_MGR_STATE_OPERATING: | |
166 | dev_info(&mgr->dev, | |
167 | "successful partial reconfiguration\n"); | |
168 | return 0; | |
169 | ||
170 | default: | |
171 | break; | |
172 | } | |
173 | udelay(1); | |
174 | } while (info->config_complete_timeout_us > i++); | |
175 | ||
176 | dev_err(&mgr->dev, "timed out waiting for write to complete\n"); | |
177 | return -ETIMEDOUT; | |
178 | } | |
179 | ||
180 | static const struct fpga_manager_ops alt_pr_ops = { | |
181 | .state = alt_pr_fpga_state, | |
182 | .write_init = alt_pr_fpga_write_init, | |
183 | .write = alt_pr_fpga_write, | |
184 | .write_complete = alt_pr_fpga_write_complete, | |
185 | }; | |
186 | ||
187 | int alt_pr_register(struct device *dev, void __iomem *reg_base) | |
188 | { | |
189 | struct alt_pr_priv *priv; | |
190 | u32 val; | |
191 | ||
192 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | |
193 | if (!priv) | |
194 | return -ENOMEM; | |
195 | ||
196 | priv->reg_base = reg_base; | |
197 | ||
198 | val = readl(priv->reg_base + ALT_PR_CSR_OFST); | |
199 | ||
200 | dev_dbg(dev, "%s status=%d start=%d\n", __func__, | |
201 | (val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT, | |
202 | (int)(val & ALT_PR_CSR_PR_START)); | |
203 | ||
204 | return fpga_mgr_register(dev, dev_name(dev), &alt_pr_ops, priv); | |
205 | } | |
206 | EXPORT_SYMBOL_GPL(alt_pr_register); | |
207 | ||
208 | int alt_pr_unregister(struct device *dev) | |
209 | { | |
210 | dev_dbg(dev, "%s\n", __func__); | |
211 | ||
212 | fpga_mgr_unregister(dev); | |
213 | ||
214 | return 0; | |
215 | } | |
216 | EXPORT_SYMBOL_GPL(alt_pr_unregister); | |
217 | ||
218 | MODULE_AUTHOR("Matthew Gerlach <matthew.gerlach@linux.intel.com>"); | |
219 | MODULE_DESCRIPTION("Altera Partial Reconfiguration IP Core"); | |
220 | MODULE_LICENSE("GPL v2"); |