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