1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Xilinx Spartan6 and 7 Series SelectMAP interface driver |
4 | * |
5 | * (C) 2024 Charles Perry <charles.perry@savoirfairelinux.com> |
6 | * |
7 | * Manage Xilinx FPGA firmware loaded over the SelectMAP configuration |
8 | * interface. |
9 | */ |
10 | |
11 | #include "xilinx-core.h" |
12 | |
13 | #include <linux/gpio/consumer.h> |
14 | #include <linux/io.h> |
15 | #include <linux/module.h> |
16 | #include <linux/mod_devicetable.h> |
17 | #include <linux/of.h> |
18 | #include <linux/platform_device.h> |
19 | |
20 | struct xilinx_selectmap_conf { |
21 | struct xilinx_fpga_core core; |
22 | void __iomem *base; |
23 | }; |
24 | |
25 | #define to_xilinx_selectmap_conf(obj) \ |
26 | container_of(obj, struct xilinx_selectmap_conf, core) |
27 | |
28 | static int xilinx_selectmap_write(struct xilinx_fpga_core *core, |
29 | const char *buf, size_t count) |
30 | { |
31 | struct xilinx_selectmap_conf *conf = to_xilinx_selectmap_conf(core); |
32 | size_t i; |
33 | |
34 | for (i = 0; i < count; ++i) |
35 | writeb(val: buf[i], addr: conf->base); |
36 | |
37 | return 0; |
38 | } |
39 | |
40 | static int xilinx_selectmap_probe(struct platform_device *pdev) |
41 | { |
42 | struct xilinx_selectmap_conf *conf; |
43 | struct gpio_desc *gpio; |
44 | void __iomem *base; |
45 | |
46 | conf = devm_kzalloc(dev: &pdev->dev, size: sizeof(*conf), GFP_KERNEL); |
47 | if (!conf) |
48 | return -ENOMEM; |
49 | |
50 | conf->core.dev = &pdev->dev; |
51 | conf->core.write = xilinx_selectmap_write; |
52 | |
53 | base = devm_platform_get_and_ioremap_resource(pdev, index: 0, NULL); |
54 | if (IS_ERR(ptr: base)) |
55 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: base), |
56 | fmt: "ioremap error\n" ); |
57 | conf->base = base; |
58 | |
59 | /* CSI_B is active low */ |
60 | gpio = devm_gpiod_get_optional(dev: &pdev->dev, con_id: "csi" , flags: GPIOD_OUT_HIGH); |
61 | if (IS_ERR(ptr: gpio)) |
62 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: gpio), |
63 | fmt: "Failed to get CSI_B gpio\n" ); |
64 | |
65 | /* RDWR_B is active low */ |
66 | gpio = devm_gpiod_get_optional(dev: &pdev->dev, con_id: "rdwr" , flags: GPIOD_OUT_HIGH); |
67 | if (IS_ERR(ptr: gpio)) |
68 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: gpio), |
69 | fmt: "Failed to get RDWR_B gpio\n" ); |
70 | |
71 | return xilinx_core_probe(core: &conf->core); |
72 | } |
73 | |
74 | static const struct of_device_id xlnx_selectmap_of_match[] = { |
75 | { .compatible = "xlnx,fpga-xc7s-selectmap" , }, // Spartan-7 |
76 | { .compatible = "xlnx,fpga-xc7a-selectmap" , }, // Artix-7 |
77 | { .compatible = "xlnx,fpga-xc7k-selectmap" , }, // Kintex-7 |
78 | { .compatible = "xlnx,fpga-xc7v-selectmap" , }, // Virtex-7 |
79 | {}, |
80 | }; |
81 | MODULE_DEVICE_TABLE(of, xlnx_selectmap_of_match); |
82 | |
83 | static struct platform_driver xilinx_selectmap_driver = { |
84 | .driver = { |
85 | .name = "xilinx-selectmap" , |
86 | .of_match_table = xlnx_selectmap_of_match, |
87 | }, |
88 | .probe = xilinx_selectmap_probe, |
89 | }; |
90 | |
91 | module_platform_driver(xilinx_selectmap_driver); |
92 | |
93 | MODULE_LICENSE("GPL" ); |
94 | MODULE_AUTHOR("Charles Perry <charles.perry@savoirfairelinux.com>" ); |
95 | MODULE_DESCRIPTION("Load Xilinx FPGA firmware over SelectMap" ); |
96 | |