aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/fmc/Kconfig8
-rw-r--r--drivers/fmc/Makefile1
-rw-r--r--drivers/fmc/fmc-write-eeprom.c176
3 files changed, 185 insertions, 0 deletions
diff --git a/drivers/fmc/Kconfig b/drivers/fmc/Kconfig
index 7eacef990be8..2bb1953c9681 100644
--- a/drivers/fmc/Kconfig
+++ b/drivers/fmc/Kconfig
@@ -32,4 +32,12 @@ config FMC_TRIVIAL
32 The driver also handles interrupts (we used it with a real carrier 32 The driver also handles interrupts (we used it with a real carrier
33 before the mezzanines were produced) 33 before the mezzanines were produced)
34 34
35config FMC_WRITE_EEPROM
36 tristate "FMC mezzanine driver to write I2C EEPROM"
37 help
38 This driver matches every mezzanine device and can write the
39 internal EEPROM of the PCB, using the firmware loader to get
40 its binary and the function carrier->reprogram to actually do it.
41 It is useful when the mezzanines are produced.
42
35endif # FMC 43endif # FMC
diff --git a/drivers/fmc/Makefile b/drivers/fmc/Makefile
index 52624e6c0b1f..13701fa6db79 100644
--- a/drivers/fmc/Makefile
+++ b/drivers/fmc/Makefile
@@ -9,3 +9,4 @@ fmc-y += fmc-dump.o
9 9
10obj-$(CONFIG_FMC_FAKEDEV) += fmc-fakedev.o 10obj-$(CONFIG_FMC_FAKEDEV) += fmc-fakedev.o
11obj-$(CONFIG_FMC_TRIVIAL) += fmc-trivial.o 11obj-$(CONFIG_FMC_TRIVIAL) += fmc-trivial.o
12obj-$(CONFIG_FMC_WRITE_EEPROM) += fmc-write-eeprom.o
diff --git a/drivers/fmc/fmc-write-eeprom.c b/drivers/fmc/fmc-write-eeprom.c
new file mode 100644
index 000000000000..2cc680dd604d
--- /dev/null
+++ b/drivers/fmc/fmc-write-eeprom.c
@@ -0,0 +1,176 @@
1/*
2 * Copyright (C) 2012 CERN (www.cern.ch)
3 * Author: Alessandro Rubini <rubini@gnudd.com>
4 *
5 * Released according to the GNU GPL, version 2 or any later version.
6 *
7 * This work is part of the White Rabbit project, a research effort led
8 * by CERN, the European Institute for Nuclear Research.
9 */
10#include <linux/module.h>
11#include <linux/string.h>
12#include <linux/firmware.h>
13#include <linux/init.h>
14#include <linux/fmc.h>
15#include <asm/unaligned.h>
16
17/*
18 * This module uses the firmware loader to program the whole or part
19 * of the FMC eeprom. The meat is in the _run functions. However, no
20 * default file name is provided, to avoid accidental mishaps. Also,
21 * you must pass the busid argument
22 */
23static struct fmc_driver fwe_drv;
24
25FMC_PARAM_BUSID(fwe_drv);
26
27/* The "file=" is like the generic "gateware=" used elsewhere */
28static char *fwe_file[FMC_MAX_CARDS];
29static int fwe_file_n;
30module_param_array_named(file, fwe_file, charp, &fwe_file_n, 444);
31
32static int fwe_run_tlv(struct fmc_device *fmc, const struct firmware *fw,
33 int write)
34{
35 const uint8_t *p = fw->data;
36 int len = fw->size;
37 uint16_t thislen, thisaddr;
38 int err;
39
40 /* format is: 'w' addr16 len16 data... */
41 while (len > 5) {
42 thisaddr = get_unaligned_le16(p+1);
43 thislen = get_unaligned_le16(p+3);
44 if (p[0] != 'w' || thislen + 5 > len) {
45 dev_err(&fmc->dev, "invalid tlv at offset %ti\n",
46 p - fw->data);
47 return -EINVAL;
48 }
49 err = 0;
50 if (write) {
51 dev_info(&fmc->dev, "write %i bytes at 0x%04x\n",
52 thislen, thisaddr);
53 err = fmc->op->write_ee(fmc, thisaddr, p + 5, thislen);
54 }
55 if (err < 0) {
56 dev_err(&fmc->dev, "write failure @0x%04x\n",
57 thisaddr);
58 return err;
59 }
60 p += 5 + thislen;
61 len -= 5 + thislen;
62 }
63 if (write)
64 dev_info(&fmc->dev, "write_eeprom: success\n");
65 return 0;
66}
67
68static int fwe_run_bin(struct fmc_device *fmc, const struct firmware *fw)
69{
70 int ret;
71
72 dev_info(&fmc->dev, "programming %zi bytes\n", fw->size);
73 ret = fmc->op->write_ee(fmc, 0, (void *)fw->data, fw->size);
74 if (ret < 0) {
75 dev_info(&fmc->dev, "write_eeprom: error %i\n", ret);
76 return ret;
77 }
78 dev_info(&fmc->dev, "write_eeprom: success\n");
79 return 0;
80}
81
82static int fwe_run(struct fmc_device *fmc, const struct firmware *fw, char *s)
83{
84 char *last4 = s + strlen(s) - 4;
85 int err;
86
87 if (!strcmp(last4, ".bin"))
88 return fwe_run_bin(fmc, fw);
89 if (!strcmp(last4, ".tlv")) {
90 err = fwe_run_tlv(fmc, fw, 0);
91 if (!err)
92 err = fwe_run_tlv(fmc, fw, 1);
93 return err;
94 }
95 dev_err(&fmc->dev, "invalid file name \"%s\"\n", s);
96 return -EINVAL;
97}
98
99/*
100 * Programming is done at probe time. Morever, only those listed with
101 * busid= are programmed.
102 * card is probed for, only one is programmed. Unfortunately, it's
103 * difficult to know in advance when probing the first card if others
104 * are there.
105 */
106int fwe_probe(struct fmc_device *fmc)
107{
108 int err, index = 0;
109 const struct firmware *fw;
110 struct device *dev = &fmc->dev;
111 char *s;
112
113 if (!fwe_drv.busid_n) {
114 dev_err(dev, "%s: no busid passed, refusing all cards\n",
115 KBUILD_MODNAME);
116 return -ENODEV;
117 }
118 if (fmc->op->validate)
119 index = fmc->op->validate(fmc, &fwe_drv);
120 if (index < 0) {
121 pr_err("%s: refusing device \"%s\"\n", KBUILD_MODNAME,
122 dev_name(dev));
123 return -ENODEV;
124 }
125 if (index >= fwe_file_n) {
126 pr_err("%s: no filename for device index %i\n",
127 KBUILD_MODNAME, index);
128 return -ENODEV;
129 }
130 s = fwe_file[index];
131 if (!s) {
132 pr_err("%s: no filename for \"%s\" not programming\n",
133 KBUILD_MODNAME, dev_name(dev));
134 return -ENOENT;
135 }
136 err = request_firmware(&fw, s, dev);
137 if (err < 0) {
138 dev_err(&fmc->dev, "request firmware \"%s\": error %i\n",
139 s, err);
140 return err;
141 }
142 fwe_run(fmc, fw, s);
143 release_firmware(fw);
144 return 0;
145}
146
147int fwe_remove(struct fmc_device *fmc)
148{
149 return 0;
150}
151
152static struct fmc_driver fwe_drv = {
153 .version = FMC_VERSION,
154 .driver.name = KBUILD_MODNAME,
155 .probe = fwe_probe,
156 .remove = fwe_remove,
157 /* no table, as the current match just matches everything */
158};
159
160static int fwe_init(void)
161{
162 int ret;
163
164 ret = fmc_driver_register(&fwe_drv);
165 return ret;
166}
167
168static void fwe_exit(void)
169{
170 fmc_driver_unregister(&fwe_drv);
171}
172
173module_init(fwe_init);
174module_exit(fwe_exit);
175
176MODULE_LICENSE("GPL");