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 enum fpga_mgr_states
ts73xx_fpga_state(struct fpga_manager
*mgr
)
37 return FPGA_MGR_STATE_UNKNOWN
;
40 static int ts73xx_fpga_write_init(struct fpga_manager
*mgr
,
41 struct fpga_image_info
*info
,
42 const char *buf
, size_t count
)
44 struct ts73xx_fpga_priv
*priv
= mgr
->priv
;
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
);
55 static int ts73xx_fpga_write(struct fpga_manager
*mgr
, const char *buf
,
58 struct ts73xx_fpga_priv
*priv
= mgr
->priv
;
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
);
70 writeb(buf
[i
], priv
->io_base
+ TS73XX_FPGA_DATA_REG
);
77 static int ts73xx_fpga_write_complete(struct fpga_manager
*mgr
,
78 struct fpga_image_info
*info
)
80 struct ts73xx_fpga_priv
*priv
= mgr
->priv
;
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 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
);
93 reg
= readb(priv
->io_base
+ TS73XX_FPGA_CONFIG_REG
);
94 if ((reg
& TS73XX_FPGA_LOAD_OK
) != TS73XX_FPGA_LOAD_OK
)
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
,
107 static int ts73xx_fpga_probe(struct platform_device
*pdev
)
109 struct device
*kdev
= &pdev
->dev
;
110 struct ts73xx_fpga_priv
*priv
;
111 struct fpga_manager
*mgr
;
112 struct resource
*res
;
114 priv
= devm_kzalloc(kdev
, sizeof(*priv
), GFP_KERNEL
);
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
);
125 mgr
= devm_fpga_mgr_create(kdev
, "TS-73xx FPGA Manager",
126 &ts73xx_fpga_ops
, priv
);
130 return devm_fpga_mgr_register(kdev
, mgr
);
133 static struct platform_driver ts73xx_fpga_driver
= {
135 .name
= "ts73xx-fpga-mgr",
137 .probe
= ts73xx_fpga_probe
,
139 module_platform_driver(ts73xx_fpga_driver
);
141 MODULE_AUTHOR("Florian Fainelli <f.fainelli@gmail.com>");
142 MODULE_DESCRIPTION("TS-73xx FPGA Manager driver");
143 MODULE_LICENSE("GPL v2");