1473f01f7SAlan Tull // SPDX-License-Identifier: GPL-2.0
2e5f8efa5SAlan Tull /*
3e5f8efa5SAlan Tull * FPGA to/from HPS Bridge Driver for Altera SoCFPGA Devices
4e5f8efa5SAlan Tull *
5e5f8efa5SAlan Tull * Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved.
6e5f8efa5SAlan Tull *
7e5f8efa5SAlan Tull * Includes this patch from the mailing list:
8e5f8efa5SAlan Tull * fpga: altera-hps2fpga: fix HPS2FPGA bridge visibility to L3 masters
9e5f8efa5SAlan Tull * Signed-off-by: Anatolij Gustschin <agust@denx.de>
10e5f8efa5SAlan Tull */
11e5f8efa5SAlan Tull
12e5f8efa5SAlan Tull /*
13e5f8efa5SAlan Tull * This driver manages bridges on a Altera SOCFPGA between the ARM host
14e5f8efa5SAlan Tull * processor system (HPS) and the embedded FPGA.
15e5f8efa5SAlan Tull *
16e5f8efa5SAlan Tull * This driver supports enabling and disabling of the configured ports, which
17e5f8efa5SAlan Tull * allows for safe reprogramming of the FPGA, assuming that the new FPGA image
18e5f8efa5SAlan Tull * uses the same port configuration. Bridges must be disabled before
19e5f8efa5SAlan Tull * reprogramming the FPGA and re-enabled after the FPGA has been programmed.
20e5f8efa5SAlan Tull */
21e5f8efa5SAlan Tull
22e5f8efa5SAlan Tull #include <linux/clk.h>
23e5f8efa5SAlan Tull #include <linux/fpga/fpga-bridge.h>
24e5f8efa5SAlan Tull #include <linux/kernel.h>
25e5f8efa5SAlan Tull #include <linux/mfd/syscon.h>
26e5f8efa5SAlan Tull #include <linux/module.h>
27e5f8efa5SAlan Tull #include <linux/of_platform.h>
28e5f8efa5SAlan Tull #include <linux/regmap.h>
29e5f8efa5SAlan Tull #include <linux/reset.h>
30e5f8efa5SAlan Tull #include <linux/spinlock.h>
31e5f8efa5SAlan Tull
32e5f8efa5SAlan Tull #define ALT_L3_REMAP_OFST 0x0
33e5f8efa5SAlan Tull #define ALT_L3_REMAP_MPUZERO_MSK 0x00000001
34e5f8efa5SAlan Tull #define ALT_L3_REMAP_H2F_MSK 0x00000008
35e5f8efa5SAlan Tull #define ALT_L3_REMAP_LWH2F_MSK 0x00000010
36e5f8efa5SAlan Tull
37e5f8efa5SAlan Tull #define HPS2FPGA_BRIDGE_NAME "hps2fpga"
38e5f8efa5SAlan Tull #define LWHPS2FPGA_BRIDGE_NAME "lwhps2fpga"
39e5f8efa5SAlan Tull #define FPGA2HPS_BRIDGE_NAME "fpga2hps"
40e5f8efa5SAlan Tull
41e5f8efa5SAlan Tull struct altera_hps2fpga_data {
42e5f8efa5SAlan Tull const char *name;
43e5f8efa5SAlan Tull struct reset_control *bridge_reset;
44e5f8efa5SAlan Tull struct regmap *l3reg;
45e5f8efa5SAlan Tull unsigned int remap_mask;
46e5f8efa5SAlan Tull struct clk *clk;
47e5f8efa5SAlan Tull };
48e5f8efa5SAlan Tull
alt_hps2fpga_enable_show(struct fpga_bridge * bridge)49e5f8efa5SAlan Tull static int alt_hps2fpga_enable_show(struct fpga_bridge *bridge)
50e5f8efa5SAlan Tull {
51e5f8efa5SAlan Tull struct altera_hps2fpga_data *priv = bridge->priv;
52e5f8efa5SAlan Tull
53e5f8efa5SAlan Tull return reset_control_status(priv->bridge_reset);
54e5f8efa5SAlan Tull }
55e5f8efa5SAlan Tull
56e5f8efa5SAlan Tull /* The L3 REMAP register is write only, so keep a cached value. */
57e5f8efa5SAlan Tull static unsigned int l3_remap_shadow;
584ae2bd4bSIan Abbott static DEFINE_SPINLOCK(l3_remap_lock);
59e5f8efa5SAlan Tull
_alt_hps2fpga_enable_set(struct altera_hps2fpga_data * priv,bool enable)60e5f8efa5SAlan Tull static int _alt_hps2fpga_enable_set(struct altera_hps2fpga_data *priv,
61e5f8efa5SAlan Tull bool enable)
62e5f8efa5SAlan Tull {
63e5f8efa5SAlan Tull unsigned long flags;
64e5f8efa5SAlan Tull int ret;
65e5f8efa5SAlan Tull
66e5f8efa5SAlan Tull /* bring bridge out of reset */
67e5f8efa5SAlan Tull if (enable)
68e5f8efa5SAlan Tull ret = reset_control_deassert(priv->bridge_reset);
69e5f8efa5SAlan Tull else
70e5f8efa5SAlan Tull ret = reset_control_assert(priv->bridge_reset);
71e5f8efa5SAlan Tull if (ret)
72e5f8efa5SAlan Tull return ret;
73e5f8efa5SAlan Tull
74e5f8efa5SAlan Tull /* Allow bridge to be visible to L3 masters or not */
75e5f8efa5SAlan Tull if (priv->remap_mask) {
76e5f8efa5SAlan Tull spin_lock_irqsave(&l3_remap_lock, flags);
77e5f8efa5SAlan Tull l3_remap_shadow |= ALT_L3_REMAP_MPUZERO_MSK;
78e5f8efa5SAlan Tull
79e5f8efa5SAlan Tull if (enable)
80e5f8efa5SAlan Tull l3_remap_shadow |= priv->remap_mask;
81e5f8efa5SAlan Tull else
82e5f8efa5SAlan Tull l3_remap_shadow &= ~priv->remap_mask;
83e5f8efa5SAlan Tull
84e5f8efa5SAlan Tull ret = regmap_write(priv->l3reg, ALT_L3_REMAP_OFST,
85e5f8efa5SAlan Tull l3_remap_shadow);
86e5f8efa5SAlan Tull spin_unlock_irqrestore(&l3_remap_lock, flags);
87e5f8efa5SAlan Tull }
88e5f8efa5SAlan Tull
89e5f8efa5SAlan Tull return ret;
90e5f8efa5SAlan Tull }
91e5f8efa5SAlan Tull
alt_hps2fpga_enable_set(struct fpga_bridge * bridge,bool enable)92e5f8efa5SAlan Tull static int alt_hps2fpga_enable_set(struct fpga_bridge *bridge, bool enable)
93e5f8efa5SAlan Tull {
94e5f8efa5SAlan Tull return _alt_hps2fpga_enable_set(bridge->priv, enable);
95e5f8efa5SAlan Tull }
96e5f8efa5SAlan Tull
97e5f8efa5SAlan Tull static const struct fpga_bridge_ops altera_hps2fpga_br_ops = {
98e5f8efa5SAlan Tull .enable_set = alt_hps2fpga_enable_set,
99e5f8efa5SAlan Tull .enable_show = alt_hps2fpga_enable_show,
100e5f8efa5SAlan Tull };
101e5f8efa5SAlan Tull
102e5f8efa5SAlan Tull static struct altera_hps2fpga_data hps2fpga_data = {
103e5f8efa5SAlan Tull .name = HPS2FPGA_BRIDGE_NAME,
104e5f8efa5SAlan Tull .remap_mask = ALT_L3_REMAP_H2F_MSK,
105e5f8efa5SAlan Tull };
106e5f8efa5SAlan Tull
107e5f8efa5SAlan Tull static struct altera_hps2fpga_data lwhps2fpga_data = {
108e5f8efa5SAlan Tull .name = LWHPS2FPGA_BRIDGE_NAME,
109e5f8efa5SAlan Tull .remap_mask = ALT_L3_REMAP_LWH2F_MSK,
110e5f8efa5SAlan Tull };
111e5f8efa5SAlan Tull
112e5f8efa5SAlan Tull static struct altera_hps2fpga_data fpga2hps_data = {
113e5f8efa5SAlan Tull .name = FPGA2HPS_BRIDGE_NAME,
114e5f8efa5SAlan Tull };
115e5f8efa5SAlan Tull
116e5f8efa5SAlan Tull static const struct of_device_id altera_fpga_of_match[] = {
117e5f8efa5SAlan Tull { .compatible = "altr,socfpga-hps2fpga-bridge",
118e5f8efa5SAlan Tull .data = &hps2fpga_data },
119e5f8efa5SAlan Tull { .compatible = "altr,socfpga-lwhps2fpga-bridge",
120e5f8efa5SAlan Tull .data = &lwhps2fpga_data },
121e5f8efa5SAlan Tull { .compatible = "altr,socfpga-fpga2hps-bridge",
122e5f8efa5SAlan Tull .data = &fpga2hps_data },
123e5f8efa5SAlan Tull {},
124e5f8efa5SAlan Tull };
125e5f8efa5SAlan Tull
alt_fpga_bridge_probe(struct platform_device * pdev)126e5f8efa5SAlan Tull static int alt_fpga_bridge_probe(struct platform_device *pdev)
127e5f8efa5SAlan Tull {
128e5f8efa5SAlan Tull struct device *dev = &pdev->dev;
129e5f8efa5SAlan Tull struct altera_hps2fpga_data *priv;
130e5f8efa5SAlan Tull const struct of_device_id *of_id;
131371cd1b1SAlan Tull struct fpga_bridge *br;
132e5f8efa5SAlan Tull u32 enable;
133e5f8efa5SAlan Tull int ret;
134e5f8efa5SAlan Tull
135e5f8efa5SAlan Tull of_id = of_match_device(altera_fpga_of_match, dev);
13626ffca5eSGustavo A. R. Silva if (!of_id) {
13726ffca5eSGustavo A. R. Silva dev_err(dev, "failed to match device\n");
13826ffca5eSGustavo A. R. Silva return -ENODEV;
13926ffca5eSGustavo A. R. Silva }
14026ffca5eSGustavo A. R. Silva
141e5f8efa5SAlan Tull priv = (struct altera_hps2fpga_data *)of_id->data;
142e5f8efa5SAlan Tull
1434fd72fd2SPhilipp Zabel priv->bridge_reset = of_reset_control_get_exclusive_by_index(dev->of_node,
1444fd72fd2SPhilipp Zabel 0);
145e5f8efa5SAlan Tull if (IS_ERR(priv->bridge_reset)) {
146e5f8efa5SAlan Tull dev_err(dev, "Could not get %s reset control\n", priv->name);
147e5f8efa5SAlan Tull return PTR_ERR(priv->bridge_reset);
148e5f8efa5SAlan Tull }
149e5f8efa5SAlan Tull
150e5f8efa5SAlan Tull if (priv->remap_mask) {
151e5f8efa5SAlan Tull priv->l3reg = syscon_regmap_lookup_by_compatible("altr,l3regs");
152e5f8efa5SAlan Tull if (IS_ERR(priv->l3reg)) {
153e5f8efa5SAlan Tull dev_err(dev, "regmap for altr,l3regs lookup failed\n");
154e5f8efa5SAlan Tull return PTR_ERR(priv->l3reg);
155e5f8efa5SAlan Tull }
156e5f8efa5SAlan Tull }
157e5f8efa5SAlan Tull
158e5f8efa5SAlan Tull priv->clk = devm_clk_get(dev, NULL);
159e5f8efa5SAlan Tull if (IS_ERR(priv->clk)) {
160e5f8efa5SAlan Tull dev_err(dev, "no clock specified\n");
161e5f8efa5SAlan Tull return PTR_ERR(priv->clk);
162e5f8efa5SAlan Tull }
163e5f8efa5SAlan Tull
164e5f8efa5SAlan Tull ret = clk_prepare_enable(priv->clk);
165e5f8efa5SAlan Tull if (ret) {
166e5f8efa5SAlan Tull dev_err(dev, "could not enable clock\n");
167e5f8efa5SAlan Tull return -EBUSY;
168e5f8efa5SAlan Tull }
169e5f8efa5SAlan Tull
170e5f8efa5SAlan Tull if (!of_property_read_u32(dev->of_node, "bridge-enable", &enable)) {
171e5f8efa5SAlan Tull if (enable > 1) {
172e5f8efa5SAlan Tull dev_warn(dev, "invalid bridge-enable %u > 1\n", enable);
173e5f8efa5SAlan Tull } else {
174e5f8efa5SAlan Tull dev_info(dev, "%s bridge\n",
175e5f8efa5SAlan Tull (enable ? "enabling" : "disabling"));
176e5f8efa5SAlan Tull
177e5f8efa5SAlan Tull ret = _alt_hps2fpga_enable_set(priv, enable);
178d721f9bbSTobias Klauser if (ret)
179d721f9bbSTobias Klauser goto err;
180e5f8efa5SAlan Tull }
181e5f8efa5SAlan Tull }
182e5f8efa5SAlan Tull
183*0d70af3cSRuss Weight br = fpga_bridge_register(dev, priv->name,
184213befe0SAlan Tull &altera_hps2fpga_br_ops, priv);
185*0d70af3cSRuss Weight if (IS_ERR(br)) {
186*0d70af3cSRuss Weight ret = PTR_ERR(br);
187371cd1b1SAlan Tull goto err;
188371cd1b1SAlan Tull }
189371cd1b1SAlan Tull
190371cd1b1SAlan Tull platform_set_drvdata(pdev, br);
191371cd1b1SAlan Tull
192371cd1b1SAlan Tull return 0;
193371cd1b1SAlan Tull
194371cd1b1SAlan Tull err:
195d721f9bbSTobias Klauser clk_disable_unprepare(priv->clk);
196d721f9bbSTobias Klauser
197d721f9bbSTobias Klauser return ret;
198e5f8efa5SAlan Tull }
199e5f8efa5SAlan Tull
alt_fpga_bridge_remove(struct platform_device * pdev)200e5f8efa5SAlan Tull static int alt_fpga_bridge_remove(struct platform_device *pdev)
201e5f8efa5SAlan Tull {
202e5f8efa5SAlan Tull struct fpga_bridge *bridge = platform_get_drvdata(pdev);
203e5f8efa5SAlan Tull struct altera_hps2fpga_data *priv = bridge->priv;
204e5f8efa5SAlan Tull
205371cd1b1SAlan Tull fpga_bridge_unregister(bridge);
206e5f8efa5SAlan Tull
207e5f8efa5SAlan Tull clk_disable_unprepare(priv->clk);
208e5f8efa5SAlan Tull
209e5f8efa5SAlan Tull return 0;
210e5f8efa5SAlan Tull }
211e5f8efa5SAlan Tull
212e5f8efa5SAlan Tull MODULE_DEVICE_TABLE(of, altera_fpga_of_match);
213e5f8efa5SAlan Tull
214e5f8efa5SAlan Tull static struct platform_driver alt_fpga_bridge_driver = {
215e5f8efa5SAlan Tull .probe = alt_fpga_bridge_probe,
216e5f8efa5SAlan Tull .remove = alt_fpga_bridge_remove,
217e5f8efa5SAlan Tull .driver = {
218e5f8efa5SAlan Tull .name = "altera_hps2fpga_bridge",
219e5f8efa5SAlan Tull .of_match_table = of_match_ptr(altera_fpga_of_match),
220e5f8efa5SAlan Tull },
221e5f8efa5SAlan Tull };
222e5f8efa5SAlan Tull
223e5f8efa5SAlan Tull module_platform_driver(alt_fpga_bridge_driver);
224e5f8efa5SAlan Tull
225e5f8efa5SAlan Tull MODULE_DESCRIPTION("Altera SoCFPGA HPS to FPGA Bridge");
226e5f8efa5SAlan Tull MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>");
227e5f8efa5SAlan Tull MODULE_LICENSE("GPL v2");
228