1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * FPGA Freeze Bridge Controller |
4 | * |
5 | * Copyright (C) 2016 Altera Corporation. All rights reserved. |
6 | */ |
7 | #include <linux/delay.h> |
8 | #include <linux/io.h> |
9 | #include <linux/kernel.h> |
10 | #include <linux/mod_devicetable.h> |
11 | #include <linux/module.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/fpga/fpga-bridge.h> |
14 | |
15 | #define FREEZE_CSR_STATUS_OFFSET 0 |
16 | #define FREEZE_CSR_CTRL_OFFSET 4 |
17 | #define FREEZE_CSR_ILLEGAL_REQ_OFFSET 8 |
18 | #define FREEZE_CSR_REG_VERSION 12 |
19 | |
20 | #define FREEZE_CSR_SUPPORTED_VERSION 2 |
21 | #define FREEZE_CSR_OFFICIAL_VERSION 0xad000003 |
22 | |
23 | #define FREEZE_CSR_STATUS_FREEZE_REQ_DONE BIT(0) |
24 | #define FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE BIT(1) |
25 | |
26 | #define FREEZE_CSR_CTRL_FREEZE_REQ BIT(0) |
27 | #define FREEZE_CSR_CTRL_RESET_REQ BIT(1) |
28 | #define FREEZE_CSR_CTRL_UNFREEZE_REQ BIT(2) |
29 | |
30 | #define FREEZE_BRIDGE_NAME "freeze" |
31 | |
32 | struct altera_freeze_br_data { |
33 | struct device *dev; |
34 | void __iomem *base_addr; |
35 | bool enable; |
36 | }; |
37 | |
38 | /* |
39 | * Poll status until status bit is set or we have a timeout. |
40 | */ |
41 | static int altera_freeze_br_req_ack(struct altera_freeze_br_data *priv, |
42 | u32 timeout, u32 req_ack) |
43 | { |
44 | struct device *dev = priv->dev; |
45 | void __iomem *csr_illegal_req_addr = priv->base_addr + |
46 | FREEZE_CSR_ILLEGAL_REQ_OFFSET; |
47 | u32 status, illegal, ctrl; |
48 | int ret = -ETIMEDOUT; |
49 | |
50 | do { |
51 | illegal = readl(addr: csr_illegal_req_addr); |
52 | if (illegal) { |
53 | dev_err(dev, "illegal request detected 0x%x" , illegal); |
54 | |
55 | writel(val: 1, addr: csr_illegal_req_addr); |
56 | |
57 | illegal = readl(addr: csr_illegal_req_addr); |
58 | if (illegal) |
59 | dev_err(dev, "illegal request not cleared 0x%x" , |
60 | illegal); |
61 | |
62 | ret = -EINVAL; |
63 | break; |
64 | } |
65 | |
66 | status = readl(addr: priv->base_addr + FREEZE_CSR_STATUS_OFFSET); |
67 | dev_dbg(dev, "%s %x %x\n" , __func__, status, req_ack); |
68 | status &= req_ack; |
69 | if (status) { |
70 | ctrl = readl(addr: priv->base_addr + FREEZE_CSR_CTRL_OFFSET); |
71 | dev_dbg(dev, "%s request %x acknowledged %x %x\n" , |
72 | __func__, req_ack, status, ctrl); |
73 | ret = 0; |
74 | break; |
75 | } |
76 | |
77 | udelay(1); |
78 | } while (timeout--); |
79 | |
80 | if (ret == -ETIMEDOUT) |
81 | dev_err(dev, "%s timeout waiting for 0x%x\n" , |
82 | __func__, req_ack); |
83 | |
84 | return ret; |
85 | } |
86 | |
87 | static int altera_freeze_br_do_freeze(struct altera_freeze_br_data *priv, |
88 | u32 timeout) |
89 | { |
90 | struct device *dev = priv->dev; |
91 | void __iomem *csr_ctrl_addr = priv->base_addr + |
92 | FREEZE_CSR_CTRL_OFFSET; |
93 | u32 status; |
94 | int ret; |
95 | |
96 | status = readl(addr: priv->base_addr + FREEZE_CSR_STATUS_OFFSET); |
97 | |
98 | dev_dbg(dev, "%s %d %d\n" , __func__, status, readl(csr_ctrl_addr)); |
99 | |
100 | if (status & FREEZE_CSR_STATUS_FREEZE_REQ_DONE) { |
101 | dev_dbg(dev, "%s bridge already disabled %d\n" , |
102 | __func__, status); |
103 | return 0; |
104 | } else if (!(status & FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE)) { |
105 | dev_err(dev, "%s bridge not enabled %d\n" , __func__, status); |
106 | return -EINVAL; |
107 | } |
108 | |
109 | writel(FREEZE_CSR_CTRL_FREEZE_REQ, addr: csr_ctrl_addr); |
110 | |
111 | ret = altera_freeze_br_req_ack(priv, timeout, |
112 | FREEZE_CSR_STATUS_FREEZE_REQ_DONE); |
113 | |
114 | if (ret) |
115 | writel(val: 0, addr: csr_ctrl_addr); |
116 | else |
117 | writel(FREEZE_CSR_CTRL_RESET_REQ, addr: csr_ctrl_addr); |
118 | |
119 | return ret; |
120 | } |
121 | |
122 | static int altera_freeze_br_do_unfreeze(struct altera_freeze_br_data *priv, |
123 | u32 timeout) |
124 | { |
125 | struct device *dev = priv->dev; |
126 | void __iomem *csr_ctrl_addr = priv->base_addr + |
127 | FREEZE_CSR_CTRL_OFFSET; |
128 | u32 status; |
129 | int ret; |
130 | |
131 | writel(val: 0, addr: csr_ctrl_addr); |
132 | |
133 | status = readl(addr: priv->base_addr + FREEZE_CSR_STATUS_OFFSET); |
134 | |
135 | dev_dbg(dev, "%s %d %d\n" , __func__, status, readl(csr_ctrl_addr)); |
136 | |
137 | if (status & FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE) { |
138 | dev_dbg(dev, "%s bridge already enabled %d\n" , |
139 | __func__, status); |
140 | return 0; |
141 | } else if (!(status & FREEZE_CSR_STATUS_FREEZE_REQ_DONE)) { |
142 | dev_err(dev, "%s bridge not frozen %d\n" , __func__, status); |
143 | return -EINVAL; |
144 | } |
145 | |
146 | writel(FREEZE_CSR_CTRL_UNFREEZE_REQ, addr: csr_ctrl_addr); |
147 | |
148 | ret = altera_freeze_br_req_ack(priv, timeout, |
149 | FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE); |
150 | |
151 | status = readl(addr: priv->base_addr + FREEZE_CSR_STATUS_OFFSET); |
152 | |
153 | dev_dbg(dev, "%s %d %d\n" , __func__, status, readl(csr_ctrl_addr)); |
154 | |
155 | writel(val: 0, addr: csr_ctrl_addr); |
156 | |
157 | return ret; |
158 | } |
159 | |
160 | /* |
161 | * enable = 1 : allow traffic through the bridge |
162 | * enable = 0 : disable traffic through the bridge |
163 | */ |
164 | static int altera_freeze_br_enable_set(struct fpga_bridge *bridge, |
165 | bool enable) |
166 | { |
167 | struct altera_freeze_br_data *priv = bridge->priv; |
168 | struct fpga_image_info *info = bridge->info; |
169 | u32 timeout = 0; |
170 | int ret; |
171 | |
172 | if (enable) { |
173 | if (info) |
174 | timeout = info->enable_timeout_us; |
175 | |
176 | ret = altera_freeze_br_do_unfreeze(priv: bridge->priv, timeout); |
177 | } else { |
178 | if (info) |
179 | timeout = info->disable_timeout_us; |
180 | |
181 | ret = altera_freeze_br_do_freeze(priv: bridge->priv, timeout); |
182 | } |
183 | |
184 | if (!ret) |
185 | priv->enable = enable; |
186 | |
187 | return ret; |
188 | } |
189 | |
190 | static int altera_freeze_br_enable_show(struct fpga_bridge *bridge) |
191 | { |
192 | struct altera_freeze_br_data *priv = bridge->priv; |
193 | |
194 | return priv->enable; |
195 | } |
196 | |
197 | static const struct fpga_bridge_ops altera_freeze_br_br_ops = { |
198 | .enable_set = altera_freeze_br_enable_set, |
199 | .enable_show = altera_freeze_br_enable_show, |
200 | }; |
201 | |
202 | static const struct of_device_id altera_freeze_br_of_match[] = { |
203 | { .compatible = "altr,freeze-bridge-controller" , }, |
204 | {}, |
205 | }; |
206 | MODULE_DEVICE_TABLE(of, altera_freeze_br_of_match); |
207 | |
208 | static int altera_freeze_br_probe(struct platform_device *pdev) |
209 | { |
210 | struct device *dev = &pdev->dev; |
211 | struct device_node *np = pdev->dev.of_node; |
212 | void __iomem *base_addr; |
213 | struct altera_freeze_br_data *priv; |
214 | struct fpga_bridge *br; |
215 | u32 status, revision; |
216 | |
217 | if (!np) |
218 | return -ENODEV; |
219 | |
220 | base_addr = devm_platform_ioremap_resource(pdev, index: 0); |
221 | if (IS_ERR(ptr: base_addr)) |
222 | return PTR_ERR(ptr: base_addr); |
223 | |
224 | revision = readl(addr: base_addr + FREEZE_CSR_REG_VERSION); |
225 | if ((revision != FREEZE_CSR_SUPPORTED_VERSION) && |
226 | (revision != FREEZE_CSR_OFFICIAL_VERSION)) { |
227 | dev_err(dev, |
228 | "%s unexpected revision 0x%x != 0x%x != 0x%x\n" , |
229 | __func__, revision, FREEZE_CSR_SUPPORTED_VERSION, |
230 | FREEZE_CSR_OFFICIAL_VERSION); |
231 | return -EINVAL; |
232 | } |
233 | |
234 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
235 | if (!priv) |
236 | return -ENOMEM; |
237 | |
238 | priv->dev = dev; |
239 | |
240 | status = readl(addr: base_addr + FREEZE_CSR_STATUS_OFFSET); |
241 | if (status & FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE) |
242 | priv->enable = 1; |
243 | |
244 | priv->base_addr = base_addr; |
245 | |
246 | br = fpga_bridge_register(parent: dev, FREEZE_BRIDGE_NAME, |
247 | br_ops: &altera_freeze_br_br_ops, priv); |
248 | if (IS_ERR(ptr: br)) |
249 | return PTR_ERR(ptr: br); |
250 | |
251 | platform_set_drvdata(pdev, data: br); |
252 | |
253 | return 0; |
254 | } |
255 | |
256 | static int altera_freeze_br_remove(struct platform_device *pdev) |
257 | { |
258 | struct fpga_bridge *br = platform_get_drvdata(pdev); |
259 | |
260 | fpga_bridge_unregister(br); |
261 | |
262 | return 0; |
263 | } |
264 | |
265 | static struct platform_driver altera_freeze_br_driver = { |
266 | .probe = altera_freeze_br_probe, |
267 | .remove = altera_freeze_br_remove, |
268 | .driver = { |
269 | .name = "altera_freeze_br" , |
270 | .of_match_table = altera_freeze_br_of_match, |
271 | }, |
272 | }; |
273 | |
274 | module_platform_driver(altera_freeze_br_driver); |
275 | |
276 | MODULE_DESCRIPTION("Altera Freeze Bridge" ); |
277 | MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>" ); |
278 | MODULE_LICENSE("GPL v2" ); |
279 | |