Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * FPGA to/from HPS Bridge Driver for Altera SoCFPGA Devices
0004  *
0005  *  Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved.
0006  *
0007  * Includes this patch from the mailing list:
0008  *   fpga: altera-hps2fpga: fix HPS2FPGA bridge visibility to L3 masters
0009  *   Signed-off-by: Anatolij Gustschin <agust@denx.de>
0010  */
0011 
0012 /*
0013  * This driver manages bridges on a Altera SOCFPGA between the ARM host
0014  * processor system (HPS) and the embedded FPGA.
0015  *
0016  * This driver supports enabling and disabling of the configured ports, which
0017  * allows for safe reprogramming of the FPGA, assuming that the new FPGA image
0018  * uses the same port configuration.  Bridges must be disabled before
0019  * reprogramming the FPGA and re-enabled after the FPGA has been programmed.
0020  */
0021 
0022 #include <linux/clk.h>
0023 #include <linux/fpga/fpga-bridge.h>
0024 #include <linux/kernel.h>
0025 #include <linux/mfd/syscon.h>
0026 #include <linux/module.h>
0027 #include <linux/of_platform.h>
0028 #include <linux/regmap.h>
0029 #include <linux/reset.h>
0030 #include <linux/spinlock.h>
0031 
0032 #define ALT_L3_REMAP_OFST           0x0
0033 #define ALT_L3_REMAP_MPUZERO_MSK        0x00000001
0034 #define ALT_L3_REMAP_H2F_MSK            0x00000008
0035 #define ALT_L3_REMAP_LWH2F_MSK          0x00000010
0036 
0037 #define HPS2FPGA_BRIDGE_NAME            "hps2fpga"
0038 #define LWHPS2FPGA_BRIDGE_NAME          "lwhps2fpga"
0039 #define FPGA2HPS_BRIDGE_NAME            "fpga2hps"
0040 
0041 struct altera_hps2fpga_data {
0042     const char *name;
0043     struct reset_control *bridge_reset;
0044     struct regmap *l3reg;
0045     unsigned int remap_mask;
0046     struct clk *clk;
0047 };
0048 
0049 static int alt_hps2fpga_enable_show(struct fpga_bridge *bridge)
0050 {
0051     struct altera_hps2fpga_data *priv = bridge->priv;
0052 
0053     return reset_control_status(priv->bridge_reset);
0054 }
0055 
0056 /* The L3 REMAP register is write only, so keep a cached value. */
0057 static unsigned int l3_remap_shadow;
0058 static DEFINE_SPINLOCK(l3_remap_lock);
0059 
0060 static int _alt_hps2fpga_enable_set(struct altera_hps2fpga_data *priv,
0061                     bool enable)
0062 {
0063     unsigned long flags;
0064     int ret;
0065 
0066     /* bring bridge out of reset */
0067     if (enable)
0068         ret = reset_control_deassert(priv->bridge_reset);
0069     else
0070         ret = reset_control_assert(priv->bridge_reset);
0071     if (ret)
0072         return ret;
0073 
0074     /* Allow bridge to be visible to L3 masters or not */
0075     if (priv->remap_mask) {
0076         spin_lock_irqsave(&l3_remap_lock, flags);
0077         l3_remap_shadow |= ALT_L3_REMAP_MPUZERO_MSK;
0078 
0079         if (enable)
0080             l3_remap_shadow |= priv->remap_mask;
0081         else
0082             l3_remap_shadow &= ~priv->remap_mask;
0083 
0084         ret = regmap_write(priv->l3reg, ALT_L3_REMAP_OFST,
0085                    l3_remap_shadow);
0086         spin_unlock_irqrestore(&l3_remap_lock, flags);
0087     }
0088 
0089     return ret;
0090 }
0091 
0092 static int alt_hps2fpga_enable_set(struct fpga_bridge *bridge, bool enable)
0093 {
0094     return _alt_hps2fpga_enable_set(bridge->priv, enable);
0095 }
0096 
0097 static const struct fpga_bridge_ops altera_hps2fpga_br_ops = {
0098     .enable_set = alt_hps2fpga_enable_set,
0099     .enable_show = alt_hps2fpga_enable_show,
0100 };
0101 
0102 static struct altera_hps2fpga_data hps2fpga_data  = {
0103     .name = HPS2FPGA_BRIDGE_NAME,
0104     .remap_mask = ALT_L3_REMAP_H2F_MSK,
0105 };
0106 
0107 static struct altera_hps2fpga_data lwhps2fpga_data  = {
0108     .name = LWHPS2FPGA_BRIDGE_NAME,
0109     .remap_mask = ALT_L3_REMAP_LWH2F_MSK,
0110 };
0111 
0112 static struct altera_hps2fpga_data fpga2hps_data  = {
0113     .name = FPGA2HPS_BRIDGE_NAME,
0114 };
0115 
0116 static const struct of_device_id altera_fpga_of_match[] = {
0117     { .compatible = "altr,socfpga-hps2fpga-bridge",
0118       .data = &hps2fpga_data },
0119     { .compatible = "altr,socfpga-lwhps2fpga-bridge",
0120       .data = &lwhps2fpga_data },
0121     { .compatible = "altr,socfpga-fpga2hps-bridge",
0122       .data = &fpga2hps_data },
0123     {},
0124 };
0125 
0126 static int alt_fpga_bridge_probe(struct platform_device *pdev)
0127 {
0128     struct device *dev = &pdev->dev;
0129     struct altera_hps2fpga_data *priv;
0130     const struct of_device_id *of_id;
0131     struct fpga_bridge *br;
0132     u32 enable;
0133     int ret;
0134 
0135     of_id = of_match_device(altera_fpga_of_match, dev);
0136     if (!of_id) {
0137         dev_err(dev, "failed to match device\n");
0138         return -ENODEV;
0139     }
0140 
0141     priv = (struct altera_hps2fpga_data *)of_id->data;
0142 
0143     priv->bridge_reset = of_reset_control_get_exclusive_by_index(dev->of_node,
0144                                      0);
0145     if (IS_ERR(priv->bridge_reset)) {
0146         dev_err(dev, "Could not get %s reset control\n", priv->name);
0147         return PTR_ERR(priv->bridge_reset);
0148     }
0149 
0150     if (priv->remap_mask) {
0151         priv->l3reg = syscon_regmap_lookup_by_compatible("altr,l3regs");
0152         if (IS_ERR(priv->l3reg)) {
0153             dev_err(dev, "regmap for altr,l3regs lookup failed\n");
0154             return PTR_ERR(priv->l3reg);
0155         }
0156     }
0157 
0158     priv->clk = devm_clk_get(dev, NULL);
0159     if (IS_ERR(priv->clk)) {
0160         dev_err(dev, "no clock specified\n");
0161         return PTR_ERR(priv->clk);
0162     }
0163 
0164     ret = clk_prepare_enable(priv->clk);
0165     if (ret) {
0166         dev_err(dev, "could not enable clock\n");
0167         return -EBUSY;
0168     }
0169 
0170     if (!of_property_read_u32(dev->of_node, "bridge-enable", &enable)) {
0171         if (enable > 1) {
0172             dev_warn(dev, "invalid bridge-enable %u > 1\n", enable);
0173         } else {
0174             dev_info(dev, "%s bridge\n",
0175                  (enable ? "enabling" : "disabling"));
0176 
0177             ret = _alt_hps2fpga_enable_set(priv, enable);
0178             if (ret)
0179                 goto err;
0180         }
0181     }
0182 
0183     br = fpga_bridge_register(dev, priv->name,
0184                   &altera_hps2fpga_br_ops, priv);
0185     if (IS_ERR(br)) {
0186         ret = PTR_ERR(br);
0187         goto err;
0188     }
0189 
0190     platform_set_drvdata(pdev, br);
0191 
0192     return 0;
0193 
0194 err:
0195     clk_disable_unprepare(priv->clk);
0196 
0197     return ret;
0198 }
0199 
0200 static int alt_fpga_bridge_remove(struct platform_device *pdev)
0201 {
0202     struct fpga_bridge *bridge = platform_get_drvdata(pdev);
0203     struct altera_hps2fpga_data *priv = bridge->priv;
0204 
0205     fpga_bridge_unregister(bridge);
0206 
0207     clk_disable_unprepare(priv->clk);
0208 
0209     return 0;
0210 }
0211 
0212 MODULE_DEVICE_TABLE(of, altera_fpga_of_match);
0213 
0214 static struct platform_driver alt_fpga_bridge_driver = {
0215     .probe = alt_fpga_bridge_probe,
0216     .remove = alt_fpga_bridge_remove,
0217     .driver = {
0218         .name   = "altera_hps2fpga_bridge",
0219         .of_match_table = of_match_ptr(altera_fpga_of_match),
0220     },
0221 };
0222 
0223 module_platform_driver(alt_fpga_bridge_driver);
0224 
0225 MODULE_DESCRIPTION("Altera SoCFPGA HPS to FPGA Bridge");
0226 MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>");
0227 MODULE_LICENSE("GPL v2");