xref: /openbmc/linux/drivers/fpga/ts73xx-fpga.c (revision 09c434b8)
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 
38 struct ts73xx_fpga_priv {
39 	void __iomem	*io_base;
40 	struct device	*dev;
41 };
42 
43 static enum fpga_mgr_states ts73xx_fpga_state(struct fpga_manager *mgr)
44 {
45 	return FPGA_MGR_STATE_UNKNOWN;
46 }
47 
48 static 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 
63 static 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 
85 static 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 
108 static 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 
115 static 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 
145 static 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 
154 static 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 };
161 module_platform_driver(ts73xx_fpga_driver);
162 
163 MODULE_AUTHOR("Florian Fainelli <f.fainelli@gmail.com>");
164 MODULE_DESCRIPTION("TS-73xx FPGA Manager driver");
165 MODULE_LICENSE("GPL v2");
166