Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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 resource *res;
120
121	priv = devm_kzalloc(kdev, sizeof(*priv), GFP_KERNEL);
122	if (!priv)
123		return -ENOMEM;
124
125	priv->dev = kdev;
126
127	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
128	priv->io_base = devm_ioremap_resource(kdev, res);
129	if (IS_ERR(priv->io_base)) {
130		dev_err(kdev, "unable to remap registers\n");
131		return PTR_ERR(priv->io_base);
132	}
133
134	return fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
135				 &ts73xx_fpga_ops, priv);
136}
137
138static int ts73xx_fpga_remove(struct platform_device *pdev)
139{
140	fpga_mgr_unregister(&pdev->dev);
141
142	return 0;
143}
144
145static struct platform_driver ts73xx_fpga_driver = {
146	.driver	= {
147		.name	= "ts73xx-fpga-mgr",
148	},
149	.probe	= ts73xx_fpga_probe,
150	.remove	= ts73xx_fpga_remove,
151};
152module_platform_driver(ts73xx_fpga_driver);
153
154MODULE_AUTHOR("Florian Fainelli <f.fainelli@gmail.com>");
155MODULE_DESCRIPTION("TS-73xx FPGA Manager driver");
156MODULE_LICENSE("GPL v2");