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 enum fpga_mgr_states ts73xx_fpga_state(struct fpga_manager *mgr) 36 { 37 return FPGA_MGR_STATE_UNKNOWN; 38 } 39 40 static 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 55 static 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 77 static 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 100 static 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 107 static 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 return PTR_ERR(priv->io_base); 124 125 mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager", 126 &ts73xx_fpga_ops, priv); 127 if (!mgr) 128 return -ENOMEM; 129 130 return devm_fpga_mgr_register(kdev, mgr); 131 } 132 133 static struct platform_driver ts73xx_fpga_driver = { 134 .driver = { 135 .name = "ts73xx-fpga-mgr", 136 }, 137 .probe = ts73xx_fpga_probe, 138 }; 139 module_platform_driver(ts73xx_fpga_driver); 140 141 MODULE_AUTHOR("Florian Fainelli <f.fainelli@gmail.com>"); 142 MODULE_DESCRIPTION("TS-73xx FPGA Manager driver"); 143 MODULE_LICENSE("GPL v2"); 144