GNU Linux-libre 4.14.290-gnu1
[releases.git] / drivers / fpga / altera-freeze-bridge.c
1 /*
2  * FPGA Freeze Bridge Controller
3  *
4  *  Copyright (C) 2016 Altera Corporation. All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms and conditions of the GNU General Public License,
8  * version 2, as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 #include <linux/delay.h>
19 #include <linux/io.h>
20 #include <linux/kernel.h>
21 #include <linux/of_device.h>
22 #include <linux/module.h>
23 #include <linux/fpga/fpga-bridge.h>
24
25 #define FREEZE_CSR_STATUS_OFFSET                0
26 #define FREEZE_CSR_CTRL_OFFSET                  4
27 #define FREEZE_CSR_ILLEGAL_REQ_OFFSET           8
28 #define FREEZE_CSR_REG_VERSION                  12
29
30 #define FREEZE_CSR_SUPPORTED_VERSION            2
31 #define FREEZE_CSR_OFFICIAL_VERSION             0xad000003
32
33 #define FREEZE_CSR_STATUS_FREEZE_REQ_DONE       BIT(0)
34 #define FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE     BIT(1)
35
36 #define FREEZE_CSR_CTRL_FREEZE_REQ              BIT(0)
37 #define FREEZE_CSR_CTRL_RESET_REQ               BIT(1)
38 #define FREEZE_CSR_CTRL_UNFREEZE_REQ            BIT(2)
39
40 #define FREEZE_BRIDGE_NAME                      "freeze"
41
42 struct altera_freeze_br_data {
43         struct device *dev;
44         void __iomem *base_addr;
45         bool enable;
46 };
47
48 /*
49  * Poll status until status bit is set or we have a timeout.
50  */
51 static int altera_freeze_br_req_ack(struct altera_freeze_br_data *priv,
52                                     u32 timeout, u32 req_ack)
53 {
54         struct device *dev = priv->dev;
55         void __iomem *csr_illegal_req_addr = priv->base_addr +
56                                              FREEZE_CSR_ILLEGAL_REQ_OFFSET;
57         u32 status, illegal, ctrl;
58         int ret = -ETIMEDOUT;
59
60         do {
61                 illegal = readl(csr_illegal_req_addr);
62                 if (illegal) {
63                         dev_err(dev, "illegal request detected 0x%x", illegal);
64
65                         writel(1, csr_illegal_req_addr);
66
67                         illegal = readl(csr_illegal_req_addr);
68                         if (illegal)
69                                 dev_err(dev, "illegal request not cleared 0x%x",
70                                         illegal);
71
72                         ret = -EINVAL;
73                         break;
74                 }
75
76                 status = readl(priv->base_addr + FREEZE_CSR_STATUS_OFFSET);
77                 dev_dbg(dev, "%s %x %x\n", __func__, status, req_ack);
78                 status &= req_ack;
79                 if (status) {
80                         ctrl = readl(priv->base_addr + FREEZE_CSR_CTRL_OFFSET);
81                         dev_dbg(dev, "%s request %x acknowledged %x %x\n",
82                                 __func__, req_ack, status, ctrl);
83                         ret = 0;
84                         break;
85                 }
86
87                 udelay(1);
88         } while (timeout--);
89
90         if (ret == -ETIMEDOUT)
91                 dev_err(dev, "%s timeout waiting for 0x%x\n",
92                         __func__, req_ack);
93
94         return ret;
95 }
96
97 static int altera_freeze_br_do_freeze(struct altera_freeze_br_data *priv,
98                                       u32 timeout)
99 {
100         struct device *dev = priv->dev;
101         void __iomem *csr_ctrl_addr = priv->base_addr +
102                                       FREEZE_CSR_CTRL_OFFSET;
103         u32 status;
104         int ret;
105
106         status = readl(priv->base_addr + FREEZE_CSR_STATUS_OFFSET);
107
108         dev_dbg(dev, "%s %d %d\n", __func__, status, readl(csr_ctrl_addr));
109
110         if (status & FREEZE_CSR_STATUS_FREEZE_REQ_DONE) {
111                 dev_dbg(dev, "%s bridge already disabled %d\n",
112                         __func__, status);
113                 return 0;
114         } else if (!(status & FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE)) {
115                 dev_err(dev, "%s bridge not enabled %d\n", __func__, status);
116                 return -EINVAL;
117         }
118
119         writel(FREEZE_CSR_CTRL_FREEZE_REQ, csr_ctrl_addr);
120
121         ret = altera_freeze_br_req_ack(priv, timeout,
122                                        FREEZE_CSR_STATUS_FREEZE_REQ_DONE);
123
124         if (ret)
125                 writel(0, csr_ctrl_addr);
126         else
127                 writel(FREEZE_CSR_CTRL_RESET_REQ, csr_ctrl_addr);
128
129         return ret;
130 }
131
132 static int altera_freeze_br_do_unfreeze(struct altera_freeze_br_data *priv,
133                                         u32 timeout)
134 {
135         struct device *dev = priv->dev;
136         void __iomem *csr_ctrl_addr = priv->base_addr +
137                                       FREEZE_CSR_CTRL_OFFSET;
138         u32 status;
139         int ret;
140
141         writel(0, csr_ctrl_addr);
142
143         status = readl(priv->base_addr + FREEZE_CSR_STATUS_OFFSET);
144
145         dev_dbg(dev, "%s %d %d\n", __func__, status, readl(csr_ctrl_addr));
146
147         if (status & FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE) {
148                 dev_dbg(dev, "%s bridge already enabled %d\n",
149                         __func__, status);
150                 return 0;
151         } else if (!(status & FREEZE_CSR_STATUS_FREEZE_REQ_DONE)) {
152                 dev_err(dev, "%s bridge not frozen %d\n", __func__, status);
153                 return -EINVAL;
154         }
155
156         writel(FREEZE_CSR_CTRL_UNFREEZE_REQ, csr_ctrl_addr);
157
158         ret = altera_freeze_br_req_ack(priv, timeout,
159                                        FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE);
160
161         status = readl(priv->base_addr + FREEZE_CSR_STATUS_OFFSET);
162
163         dev_dbg(dev, "%s %d %d\n", __func__, status, readl(csr_ctrl_addr));
164
165         writel(0, csr_ctrl_addr);
166
167         return ret;
168 }
169
170 /*
171  * enable = 1 : allow traffic through the bridge
172  * enable = 0 : disable traffic through the bridge
173  */
174 static int altera_freeze_br_enable_set(struct fpga_bridge *bridge,
175                                        bool enable)
176 {
177         struct altera_freeze_br_data *priv = bridge->priv;
178         struct fpga_image_info *info = bridge->info;
179         u32 timeout = 0;
180         int ret;
181
182         if (enable) {
183                 if (info)
184                         timeout = info->enable_timeout_us;
185
186                 ret = altera_freeze_br_do_unfreeze(bridge->priv, timeout);
187         } else {
188                 if (info)
189                         timeout = info->disable_timeout_us;
190
191                 ret = altera_freeze_br_do_freeze(bridge->priv, timeout);
192         }
193
194         if (!ret)
195                 priv->enable = enable;
196
197         return ret;
198 }
199
200 static int altera_freeze_br_enable_show(struct fpga_bridge *bridge)
201 {
202         struct altera_freeze_br_data *priv = bridge->priv;
203
204         return priv->enable;
205 }
206
207 static const struct fpga_bridge_ops altera_freeze_br_br_ops = {
208         .enable_set = altera_freeze_br_enable_set,
209         .enable_show = altera_freeze_br_enable_show,
210 };
211
212 static const struct of_device_id altera_freeze_br_of_match[] = {
213         { .compatible = "altr,freeze-bridge-controller", },
214         {},
215 };
216 MODULE_DEVICE_TABLE(of, altera_freeze_br_of_match);
217
218 static int altera_freeze_br_probe(struct platform_device *pdev)
219 {
220         struct device *dev = &pdev->dev;
221         struct device_node *np = pdev->dev.of_node;
222         void __iomem *base_addr;
223         struct altera_freeze_br_data *priv;
224         struct resource *res;
225         u32 status, revision;
226
227         if (!np)
228                 return -ENODEV;
229
230         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
231         base_addr = devm_ioremap_resource(dev, res);
232         if (IS_ERR(base_addr))
233                 return PTR_ERR(base_addr);
234
235         revision = readl(base_addr + FREEZE_CSR_REG_VERSION);
236         if ((revision != FREEZE_CSR_SUPPORTED_VERSION) &&
237             (revision != FREEZE_CSR_OFFICIAL_VERSION)) {
238                 dev_err(dev,
239                         "%s unexpected revision 0x%x != 0x%x != 0x%x\n",
240                         __func__, revision, FREEZE_CSR_SUPPORTED_VERSION,
241                         FREEZE_CSR_OFFICIAL_VERSION);
242                 return -EINVAL;
243         }
244
245         priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
246         if (!priv)
247                 return -ENOMEM;
248
249         priv->dev = dev;
250
251         status = readl(base_addr + FREEZE_CSR_STATUS_OFFSET);
252         if (status & FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE)
253                 priv->enable = 1;
254
255         priv->base_addr = base_addr;
256
257         return fpga_bridge_register(dev, FREEZE_BRIDGE_NAME,
258                                     &altera_freeze_br_br_ops, priv);
259 }
260
261 static int altera_freeze_br_remove(struct platform_device *pdev)
262 {
263         fpga_bridge_unregister(&pdev->dev);
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");