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