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 30 struct ts73xx_fpga_priv { 31 void __iomem *io_base; 32 struct device *dev; 33 }; 34 35 static 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 50 static 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 72 static 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 95 static 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 101 static 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 struct resource *res; 107 108 priv = devm_kzalloc(kdev, sizeof(*priv), GFP_KERNEL); 109 if (!priv) 110 return -ENOMEM; 111 112 priv->dev = kdev; 113 114 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 115 priv->io_base = devm_ioremap_resource(kdev, res); 116 if (IS_ERR(priv->io_base)) 117 return PTR_ERR(priv->io_base); 118 119 mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager", 120 &ts73xx_fpga_ops, priv); 121 if (!mgr) 122 return -ENOMEM; 123 124 return devm_fpga_mgr_register(kdev, mgr); 125 } 126 127 static struct platform_driver ts73xx_fpga_driver = { 128 .driver = { 129 .name = "ts73xx-fpga-mgr", 130 }, 131 .probe = ts73xx_fpga_probe, 132 }; 133 module_platform_driver(ts73xx_fpga_driver); 134 135 MODULE_AUTHOR("Florian Fainelli <f.fainelli@gmail.com>"); 136 MODULE_DESCRIPTION("TS-73xx FPGA Manager driver"); 137 MODULE_LICENSE("GPL v2"); 138