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        of_id = of_match_device(altera_fpga_of_match, dev);
 136        if (!of_id) {
 137                dev_err(dev, "failed to match device\n");
 138                return -ENODEV;
 139        }
 140
 141        priv = (struct altera_hps2fpga_data *)of_id->data;
 142
 143        priv->bridge_reset = of_reset_control_get_exclusive_by_index(dev->of_node,
 144                                                                     0);
 145        if (IS_ERR(priv->bridge_reset)) {
 146                dev_err(dev, "Could not get %s reset control\n", priv->name);
 147                return PTR_ERR(priv->bridge_reset);
 148        }
 149
 150        if (priv->remap_mask) {
 151                priv->l3reg = syscon_regmap_lookup_by_compatible("altr,l3regs");
 152                if (IS_ERR(priv->l3reg)) {
 153                        dev_err(dev, "regmap for altr,l3regs lookup failed\n");
 154                        return PTR_ERR(priv->l3reg);
 155                }
 156        }
 157
 158        priv->clk = devm_clk_get(dev, NULL);
 159        if (IS_ERR(priv->clk)) {
 160                dev_err(dev, "no clock specified\n");
 161                return PTR_ERR(priv->clk);
 162        }
 163
 164        ret = clk_prepare_enable(priv->clk);
 165        if (ret) {
 166                dev_err(dev, "could not enable clock\n");
 167                return -EBUSY;
 168        }
 169
 170        if (!of_property_read_u32(dev->of_node, "bridge-enable", &enable)) {
 171                if (enable > 1) {
 172                        dev_warn(dev, "invalid bridge-enable %u > 1\n", enable);
 173                } else {
 174                        dev_info(dev, "%s bridge\n",
 175                                 (enable ? "enabling" : "disabling"));
 176
 177                        ret = _alt_hps2fpga_enable_set(priv, enable);
 178                        if (ret)
 179                                goto err;
 180                }
 181        }
 182
 183        br = devm_fpga_bridge_create(dev, priv->name,
 184                                     &altera_hps2fpga_br_ops, priv);
 185        if (!br) {
 186                ret = -ENOMEM;
 187                goto err;
 188        }
 189
 190        platform_set_drvdata(pdev, br);
 191
 192        ret = fpga_bridge_register(br);
 193        if (ret)
 194                goto err;
 195
 196        return 0;
 197
 198err:
 199        clk_disable_unprepare(priv->clk);
 200
 201        return ret;
 202}
 203
 204static int alt_fpga_bridge_remove(struct platform_device *pdev)
 205{
 206        struct fpga_bridge *bridge = platform_get_drvdata(pdev);
 207        struct altera_hps2fpga_data *priv = bridge->priv;
 208
 209        fpga_bridge_unregister(bridge);
 210
 211        clk_disable_unprepare(priv->clk);
 212
 213        return 0;
 214}
 215
 216MODULE_DEVICE_TABLE(of, altera_fpga_of_match);
 217
 218static struct platform_driver alt_fpga_bridge_driver = {
 219        .probe = alt_fpga_bridge_probe,
 220        .remove = alt_fpga_bridge_remove,
 221        .driver = {
 222                .name   = "altera_hps2fpga_bridge",
 223                .of_match_table = of_match_ptr(altera_fpga_of_match),
 224        },
 225};
 226
 227module_platform_driver(alt_fpga_bridge_driver);
 228
 229MODULE_DESCRIPTION("Altera SoCFPGA HPS to FPGA Bridge");
 230MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>");
 231MODULE_LICENSE("GPL v2");
 232