133 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Technologic Systems TS-73xx SBC FPGA loader4 *5 * Copyright (C) 2016 Florian Fainelli <f.fainelli@gmail.com>6 *7 * FPGA Manager Driver for the on-board Altera Cyclone II FPGA found on8 * 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 020#define TS73XX_FPGA_CONFIG_REG 121 22#define TS73XX_FPGA_WRITE_DONE 0x123#define TS73XX_FPGA_WRITE_DONE_TIMEOUT 1000 /* us */24#define TS73XX_FPGA_RESET 0x225#define TS73XX_FPGA_RESET_LOW_DELAY 30 /* us */26#define TS73XX_FPGA_RESET_HIGH_DELAY 80 /* us */27#define TS73XX_FPGA_LOAD_OK 0x428#define TS73XX_FPGA_CONFIG_LOAD 0x829 30struct ts73xx_fpga_priv {31 void __iomem *io_base;32 struct device *dev;33};34 35static int ts73xx_fpga_write_init(struct fpga_manager *mgr,36 struct fpga_image_info *info,37 const char *buf, size_t count)38{39 struct ts73xx_fpga_priv *priv = mgr->priv;40 41 /* Reset the FPGA */42 writeb(0, priv->io_base + TS73XX_FPGA_CONFIG_REG);43 udelay(TS73XX_FPGA_RESET_LOW_DELAY);44 writeb(TS73XX_FPGA_RESET, priv->io_base + TS73XX_FPGA_CONFIG_REG);45 udelay(TS73XX_FPGA_RESET_HIGH_DELAY);46 47 return 0;48}49 50static int ts73xx_fpga_write(struct fpga_manager *mgr, const char *buf,51 size_t count)52{53 struct ts73xx_fpga_priv *priv = mgr->priv;54 size_t i = 0;55 int ret;56 u8 reg;57 58 while (count--) {59 ret = readb_poll_timeout(priv->io_base + TS73XX_FPGA_CONFIG_REG,60 reg, !(reg & TS73XX_FPGA_WRITE_DONE),61 1, TS73XX_FPGA_WRITE_DONE_TIMEOUT);62 if (ret < 0)63 return ret;64 65 writeb(buf[i], priv->io_base + TS73XX_FPGA_DATA_REG);66 i++;67 }68 69 return 0;70}71 72static int ts73xx_fpga_write_complete(struct fpga_manager *mgr,73 struct fpga_image_info *info)74{75 struct ts73xx_fpga_priv *priv = mgr->priv;76 u8 reg;77 78 usleep_range(1000, 2000);79 reg = readb(priv->io_base + TS73XX_FPGA_CONFIG_REG);80 reg |= TS73XX_FPGA_CONFIG_LOAD;81 writeb(reg, priv->io_base + TS73XX_FPGA_CONFIG_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 reg = readb(priv->io_base + TS73XX_FPGA_CONFIG_REG);89 if ((reg & TS73XX_FPGA_LOAD_OK) != TS73XX_FPGA_LOAD_OK)90 return -ETIMEDOUT;91 92 return 0;93}94 95static const struct fpga_manager_ops ts73xx_fpga_ops = {96 .write_init = ts73xx_fpga_write_init,97 .write = ts73xx_fpga_write,98 .write_complete = ts73xx_fpga_write_complete,99};100 101static int ts73xx_fpga_probe(struct platform_device *pdev)102{103 struct device *kdev = &pdev->dev;104 struct ts73xx_fpga_priv *priv;105 struct fpga_manager *mgr;106 107 priv = devm_kzalloc(kdev, sizeof(*priv), GFP_KERNEL);108 if (!priv)109 return -ENOMEM;110 111 priv->dev = kdev;112 113 priv->io_base = devm_platform_ioremap_resource(pdev, 0);114 if (IS_ERR(priv->io_base))115 return PTR_ERR(priv->io_base);116 117 mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",118 &ts73xx_fpga_ops, priv);119 return PTR_ERR_OR_ZERO(mgr);120}121 122static struct platform_driver ts73xx_fpga_driver = {123 .driver = {124 .name = "ts73xx-fpga-mgr",125 },126 .probe = ts73xx_fpga_probe,127};128module_platform_driver(ts73xx_fpga_driver);129 130MODULE_AUTHOR("Florian Fainelli <f.fainelli@gmail.com>");131MODULE_DESCRIPTION("TS-73xx FPGA Manager driver");132MODULE_LICENSE("GPL v2");133