linux/drivers/fpga/ts73xx-fpga.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Technologic Systems TS-73xx SBC FPGA loader
   4 *
   5 * Copyright (C) 2016 Florian Fainelli <f.fainelli@gmail.com>
   6 *
   7 * FPGA Manager Driver for the on-board Altera Cyclone II FPGA found on
   8 * TS-7300, heavily based on load_fpga.c in their vendor tree.
   9 */
  10
  11#include <linux/delay.h>
  12#include <linux/io.h>
  13#include <linux/module.h>
  14#include <linux/platform_device.h>
  15#include <linux/string.h>
  16#include <linux/iopoll.h>
  17#include <linux/fpga/fpga-mgr.h>
  18
  19#define TS73XX_FPGA_DATA_REG            0
  20#define TS73XX_FPGA_CONFIG_REG          1
  21
  22#define TS73XX_FPGA_WRITE_DONE          0x1
  23#define TS73XX_FPGA_WRITE_DONE_TIMEOUT  1000    /* us */
  24#define TS73XX_FPGA_RESET               0x2
  25#define TS73XX_FPGA_RESET_LOW_DELAY     30      /* us */
  26#define TS73XX_FPGA_RESET_HIGH_DELAY    80      /* us */
  27#define TS73XX_FPGA_LOAD_OK             0x4
  28#define TS73XX_FPGA_CONFIG_LOAD         0x8
  29
  30struct ts73xx_fpga_priv {
  31        void __iomem    *io_base;
  32        struct device   *dev;
  33};
  34
  35static enum fpga_mgr_states ts73xx_fpga_state(struct fpga_manager *mgr)
  36{
  37        return FPGA_MGR_STATE_UNKNOWN;
  38}
  39
  40static int ts73xx_fpga_write_init(struct fpga_manager *mgr,
  41                                  struct fpga_image_info *info,
  42                                  const char *buf, size_t count)
  43{
  44        struct ts73xx_fpga_priv *priv = mgr->priv;
  45
  46        /* Reset the FPGA */
  47        writeb(0, priv->io_base + TS73XX_FPGA_CONFIG_REG);
  48        udelay(TS73XX_FPGA_RESET_LOW_DELAY);
  49        writeb(TS73XX_FPGA_RESET, priv->io_base + TS73XX_FPGA_CONFIG_REG);
  50        udelay(TS73XX_FPGA_RESET_HIGH_DELAY);
  51
  52        return 0;
  53}
  54
  55static int ts73xx_fpga_write(struct fpga_manager *mgr, const char *buf,
  56                             size_t count)
  57{
  58        struct ts73xx_fpga_priv *priv = mgr->priv;
  59        size_t i = 0;
  60        int ret;
  61        u8 reg;
  62
  63        while (count--) {
  64                ret = readb_poll_timeout(priv->io_base + TS73XX_FPGA_CONFIG_REG,
  65                                         reg, !(reg & TS73XX_FPGA_WRITE_DONE),
  66                                         1, TS73XX_FPGA_WRITE_DONE_TIMEOUT);
  67                if (ret < 0)
  68                        return ret;
  69
  70                writeb(buf[i], priv->io_base + TS73XX_FPGA_DATA_REG);
  71                i++;
  72        }
  73
  74        return 0;
  75}
  76
  77static int ts73xx_fpga_write_complete(struct fpga_manager *mgr,
  78                                      struct fpga_image_info *info)
  79{
  80        struct ts73xx_fpga_priv *priv = mgr->priv;
  81        u8 reg;
  82
  83        usleep_range(1000, 2000);
  84        reg = readb(priv->io_base + TS73XX_FPGA_CONFIG_REG);
  85        reg |= TS73XX_FPGA_CONFIG_LOAD;
  86        writeb(reg, priv->io_base + TS73XX_FPGA_CONFIG_REG);
  87
  88        usleep_range(1000, 2000);
  89        reg = readb(priv->io_base + TS73XX_FPGA_CONFIG_REG);
  90        reg &= ~TS73XX_FPGA_CONFIG_LOAD;
  91        writeb(reg, priv->io_base + TS73XX_FPGA_CONFIG_REG);
  92
  93        reg = readb(priv->io_base + TS73XX_FPGA_CONFIG_REG);
  94        if ((reg & TS73XX_FPGA_LOAD_OK) != TS73XX_FPGA_LOAD_OK)
  95                return -ETIMEDOUT;
  96
  97        return 0;
  98}
  99
 100static const struct fpga_manager_ops ts73xx_fpga_ops = {
 101        .state          = ts73xx_fpga_state,
 102        .write_init     = ts73xx_fpga_write_init,
 103        .write          = ts73xx_fpga_write,
 104        .write_complete = ts73xx_fpga_write_complete,
 105};
 106
 107static int ts73xx_fpga_probe(struct platform_device *pdev)
 108{
 109        struct device *kdev = &pdev->dev;
 110        struct ts73xx_fpga_priv *priv;
 111        struct fpga_manager *mgr;
 112        struct resource *res;
 113
 114        priv = devm_kzalloc(kdev, sizeof(*priv), GFP_KERNEL);
 115        if (!priv)
 116                return -ENOMEM;
 117
 118        priv->dev = kdev;
 119
 120        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 121        priv->io_base = devm_ioremap_resource(kdev, res);
 122        if (IS_ERR(priv->io_base)) {
 123                dev_err(kdev, "unable to remap registers\n");
 124                return PTR_ERR(priv->io_base);
 125        }
 126
 127        mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
 128                                   &ts73xx_fpga_ops, priv);
 129        if (!mgr)
 130                return -ENOMEM;
 131
 132        platform_set_drvdata(pdev, mgr);
 133
 134        return fpga_mgr_register(mgr);
 135}
 136
 137static int ts73xx_fpga_remove(struct platform_device *pdev)
 138{
 139        struct fpga_manager *mgr = platform_get_drvdata(pdev);
 140
 141        fpga_mgr_unregister(mgr);
 142
 143        return 0;
 144}
 145
 146static struct platform_driver ts73xx_fpga_driver = {
 147        .driver = {
 148                .name   = "ts73xx-fpga-mgr",
 149        },
 150        .probe  = ts73xx_fpga_probe,
 151        .remove = ts73xx_fpga_remove,
 152};
 153module_platform_driver(ts73xx_fpga_driver);
 154
 155MODULE_AUTHOR("Florian Fainelli <f.fainelli@gmail.com>");
 156MODULE_DESCRIPTION("TS-73xx FPGA Manager driver");
 157MODULE_LICENSE("GPL v2");
 158