xref: /linux/drivers/fpga/ts73xx-fpga.c (revision b2f1e6301efa4a80becdb0715416c3cbc693fbb4)
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/of.h>
15 #include <linux/platform_device.h>
16 #include <linux/string.h>
17 #include <linux/iopoll.h>
18 #include <linux/fpga/fpga-mgr.h>
19 
20 #define TS73XX_FPGA_DATA_REG		0
21 #define TS73XX_FPGA_CONFIG_REG		1
22 
23 #define TS73XX_FPGA_WRITE_DONE		0x1
24 #define TS73XX_FPGA_WRITE_DONE_TIMEOUT	1000	/* us */
25 #define TS73XX_FPGA_RESET		0x2
26 #define TS73XX_FPGA_RESET_LOW_DELAY	30	/* us */
27 #define TS73XX_FPGA_RESET_HIGH_DELAY	80	/* us */
28 #define TS73XX_FPGA_LOAD_OK		0x4
29 #define TS73XX_FPGA_CONFIG_LOAD		0x8
30 
31 struct ts73xx_fpga_priv {
32 	void __iomem	*io_base;
33 	struct device	*dev;
34 };
35 
36 static int ts73xx_fpga_write_init(struct fpga_manager *mgr,
37 				  struct fpga_image_info *info,
38 				  const char *buf, size_t count)
39 {
40 	struct ts73xx_fpga_priv *priv = mgr->priv;
41 
42 	/* Reset the FPGA */
43 	writeb(0, priv->io_base + TS73XX_FPGA_CONFIG_REG);
44 	udelay(TS73XX_FPGA_RESET_LOW_DELAY);
45 	writeb(TS73XX_FPGA_RESET, priv->io_base + TS73XX_FPGA_CONFIG_REG);
46 	udelay(TS73XX_FPGA_RESET_HIGH_DELAY);
47 
48 	return 0;
49 }
50 
51 static int ts73xx_fpga_write(struct fpga_manager *mgr, const char *buf,
52 			     size_t count)
53 {
54 	struct ts73xx_fpga_priv *priv = mgr->priv;
55 	size_t i = 0;
56 	int ret;
57 	u8 reg;
58 
59 	while (count--) {
60 		ret = readb_poll_timeout(priv->io_base + TS73XX_FPGA_CONFIG_REG,
61 					 reg, !(reg & TS73XX_FPGA_WRITE_DONE),
62 					 1, TS73XX_FPGA_WRITE_DONE_TIMEOUT);
63 		if (ret < 0)
64 			return ret;
65 
66 		writeb(buf[i], priv->io_base + TS73XX_FPGA_DATA_REG);
67 		i++;
68 	}
69 
70 	return 0;
71 }
72 
73 static int ts73xx_fpga_write_complete(struct fpga_manager *mgr,
74 				      struct fpga_image_info *info)
75 {
76 	struct ts73xx_fpga_priv *priv = mgr->priv;
77 	u8 reg;
78 
79 	usleep_range(1000, 2000);
80 	reg = readb(priv->io_base + TS73XX_FPGA_CONFIG_REG);
81 	reg |= TS73XX_FPGA_CONFIG_LOAD;
82 	writeb(reg, priv->io_base + TS73XX_FPGA_CONFIG_REG);
83 
84 	usleep_range(1000, 2000);
85 	reg = readb(priv->io_base + TS73XX_FPGA_CONFIG_REG);
86 	reg &= ~TS73XX_FPGA_CONFIG_LOAD;
87 	writeb(reg, priv->io_base + TS73XX_FPGA_CONFIG_REG);
88 
89 	reg = readb(priv->io_base + TS73XX_FPGA_CONFIG_REG);
90 	if ((reg & TS73XX_FPGA_LOAD_OK) != TS73XX_FPGA_LOAD_OK)
91 		return -ETIMEDOUT;
92 
93 	return 0;
94 }
95 
96 static const struct fpga_manager_ops ts73xx_fpga_ops = {
97 	.write_init	= ts73xx_fpga_write_init,
98 	.write		= ts73xx_fpga_write,
99 	.write_complete	= ts73xx_fpga_write_complete,
100 };
101 
102 static int ts73xx_fpga_probe(struct platform_device *pdev)
103 {
104 	struct device *kdev = &pdev->dev;
105 	struct ts73xx_fpga_priv *priv;
106 	struct fpga_manager *mgr;
107 
108 	priv = devm_kzalloc(kdev, sizeof(*priv), GFP_KERNEL);
109 	if (!priv)
110 		return -ENOMEM;
111 
112 	priv->dev = kdev;
113 
114 	priv->io_base = devm_platform_ioremap_resource(pdev, 0);
115 	if (IS_ERR(priv->io_base))
116 		return PTR_ERR(priv->io_base);
117 
118 	mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
119 				     &ts73xx_fpga_ops, priv);
120 	return PTR_ERR_OR_ZERO(mgr);
121 }
122 
123 static const struct of_device_id ts73xx_fpga_of_match[] = {
124 	{ .compatible = "technologic,ts7300-fpga" },
125 	{},
126 };
127 
128 MODULE_DEVICE_TABLE(of, ts73xx_fpga_of_match);
129 
130 static struct platform_driver ts73xx_fpga_driver = {
131 	.driver	= {
132 		.name	= "ts73xx-fpga-mgr",
133 		.of_match_table = ts73xx_fpga_of_match,
134 	},
135 	.probe	= ts73xx_fpga_probe,
136 };
137 module_platform_driver(ts73xx_fpga_driver);
138 
139 MODULE_AUTHOR("Florian Fainelli <f.fainelli@gmail.com>");
140 MODULE_DESCRIPTION("TS-73xx FPGA Manager driver");
141 MODULE_LICENSE("GPL v2");
142