1 // SPDX-License-Identifier: GPL-2.0-only
3 * Technologic Systems TS-73xx SBC FPGA loader
5 * Copyright (C) 2016 Florian Fainelli <f.fainelli@gmail.com>
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.
11 #include <linux/delay.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>
19 #define TS73XX_FPGA_DATA_REG 0
20 #define TS73XX_FPGA_CONFIG_REG 1
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
30 struct ts73xx_fpga_priv
{
31 void __iomem
*io_base
;
35 static int ts73xx_fpga_write_init(struct fpga_manager
*mgr
,
36 struct fpga_image_info
*info
,
37 const char *buf
, size_t count
)
39 struct ts73xx_fpga_priv
*priv
= mgr
->priv
;
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
);
50 static int ts73xx_fpga_write(struct fpga_manager
*mgr
, const char *buf
,
53 struct ts73xx_fpga_priv
*priv
= mgr
->priv
;
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
);
65 writeb(buf
[i
], priv
->io_base
+ TS73XX_FPGA_DATA_REG
);
72 static int ts73xx_fpga_write_complete(struct fpga_manager
*mgr
,
73 struct fpga_image_info
*info
)
75 struct ts73xx_fpga_priv
*priv
= mgr
->priv
;
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
);
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
);
88 reg
= readb(priv
->io_base
+ TS73XX_FPGA_CONFIG_REG
);
89 if ((reg
& TS73XX_FPGA_LOAD_OK
) != TS73XX_FPGA_LOAD_OK
)
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
,
101 static int ts73xx_fpga_probe(struct platform_device
*pdev
)
103 struct device
*kdev
= &pdev
->dev
;
104 struct ts73xx_fpga_priv
*priv
;
105 struct fpga_manager
*mgr
;
107 priv
= devm_kzalloc(kdev
, sizeof(*priv
), GFP_KERNEL
);
113 priv
->io_base
= devm_platform_ioremap_resource(pdev
, 0);
114 if (IS_ERR(priv
->io_base
))
115 return PTR_ERR(priv
->io_base
);
117 mgr
= devm_fpga_mgr_register(kdev
, "TS-73xx FPGA Manager",
118 &ts73xx_fpga_ops
, priv
);
119 return PTR_ERR_OR_ZERO(mgr
);
122 static struct platform_driver ts73xx_fpga_driver
= {
124 .name
= "ts73xx-fpga-mgr",
126 .probe
= ts73xx_fpga_probe
,
128 module_platform_driver(ts73xx_fpga_driver
);
130 MODULE_AUTHOR("Florian Fainelli <f.fainelli@gmail.com>");
131 MODULE_DESCRIPTION("TS-73xx FPGA Manager driver");
132 MODULE_LICENSE("GPL v2");