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