Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

FMC: add a driver to write mezzanine EEPROM

This driver allows to reprogram the EEPROM in a mezzanine, to store
its own identifiers during manufacturing or to save other useful data.

Signed-off-by: Alessandro Rubini <rubini@gnudd.com>
Acked-by: Juan David Gonzalez Cobas <dcobas@cern.ch>
Acked-by: Emilio G. Cota <cota@braap.org>
Acked-by: Samuel Iglesias Gonsalvez <siglesias@igalia.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Alessandro Rubini and committed by
Greg Kroah-Hartman
6007b1bd 056d83f3

+313
+3
Documentation/fmc/00-INDEX
··· 30 30 31 31 fmc-trivial.txt 32 32 - about drivers/fmc/fmc-trivial.ko 33 + 34 + fmc-write-eeprom.txt 35 + - about drivers/fmc/fmc-write-eeprom.ko
+125
Documentation/fmc/fmc-write-eeprom.txt
··· 1 + fmc-write-eeprom 2 + ================ 3 + 4 + This module is designed to load a binary file from /lib/firmware and to 5 + write it to the internal EEPROM of the mezzanine card. This driver uses 6 + the `busid' generic parameter. 7 + 8 + Overwriting the EEPROM is not something you should do daily, and it is 9 + expected to only happen during manufacturing. For this reason, the 10 + module makes it unlikely for the random user to change a working EEPROM. 11 + 12 + The module takes the following measures: 13 + 14 + * It accepts a `file=' argument (within /lib/firmware) and if no 15 + such argument is received, it doesn't write anything to EEPROM 16 + (i.e. there is no default file name). 17 + 18 + * If the file name ends with `.bin' it is written verbatim starting 19 + at offset 0. 20 + 21 + * If the file name ends with `.tlv' it is interpreted as 22 + type-length-value (i.e., it allows writev(2)-like operation). 23 + 24 + * If the file name doesn't match any of the patterns above, it is 25 + ignored and no write is performed. 26 + 27 + * Only cards listed with `busid=' are written to. If no busid is 28 + specified, no programming is done (and the probe function of the 29 + driver will fail). 30 + 31 + 32 + Each TLV tuple is formatted in this way: the header is 5 bytes, 33 + followed by data. The first byte is `w' for write, the next two bytes 34 + represent the address, in little-endian byte order, and the next two 35 + represent the data length, in little-endian order. The length does not 36 + include the header (it is the actual number of bytes to be written). 37 + 38 + This is a real example: that writes 5 bytes at position 0x110: 39 + 40 + spusa.root# od -t x1 -Ax /lib/firmware/try.tlv 41 + 000000 77 10 01 05 00 30 31 32 33 34 42 + 00000a 43 + spusa.root# insmod /tmp/fmc-write-eeprom.ko busid=0x0200 file=try.tlv 44 + [19983.391498] spec 0000:03:00.0: write 5 bytes at 0x0110 45 + [19983.414615] spec 0000:03:00.0: write_eeprom: success 46 + 47 + Please note that you'll most likely want to use SDBFS to build your 48 + EEPROM image, at least if your mezzanines are being used in the White 49 + Rabbit environment. For this reason the TLV format is not expected to 50 + be used much and is not expected to be developed further. 51 + 52 + If you want to try reflashing fake EEPROM devices, you can use the 53 + fmc-fakedev.ko module (see *note fmc-fakedev::). Whenever you change 54 + the image starting at offset 0, it will deregister and register again 55 + after two seconds. Please note, however, that if fmc-write-eeprom is 56 + still loaded, the system will associate it to the new device, which 57 + will be reprogrammed and thus will be unloaded after two seconds. The 58 + following example removes the module after it reflashed fakedev the 59 + first time. 60 + 61 + spusa.root# insmod fmc-fakedev.ko 62 + [ 72.984733] fake-fmc: Manufacturer: fake-vendor 63 + [ 72.989434] fake-fmc: Product name: fake-design-for-testing 64 + spusa.root# insmod fmc-write-eeprom.ko busid=0 file=fdelay-eeprom.bin; \ 65 + rmmod fmc-write-eeprom 66 + [ 130.874098] fake-fmc: Matching a generic driver (no ID) 67 + [ 130.887845] fake-fmc: programming 6155 bytes 68 + [ 130.894567] fake-fmc: write_eeprom: success 69 + [ 132.895794] fake-fmc: Manufacturer: CERN 70 + [ 132.899872] fake-fmc: Product name: FmcDelay1ns4cha 71 + 72 + 73 + Writing to the EEPROM 74 + ===================== 75 + 76 + Once you have created a binary file for your EEPROM, you can write it 77 + to the storage medium using the fmc-write-eeprom (See *note 78 + fmc-write-eeprom::, while relying on a carrier driver. The procedure 79 + here shown here uses the SPEC driver 80 + (`http://www.ohwr.org/projects/spec-sw'). 81 + 82 + The example assumes no driver is already loaded (actually, I unloaded 83 + them by hand as everything loads automatically at boot time after you 84 + installed the modules), and shows kernel messages together with 85 + commands. Here the prompt is spusa.root# and two SPEC cards are plugged 86 + in the system. 87 + 88 + spusa.root# insmod fmc.ko 89 + spusa.root# insmod spec.ko 90 + [13972.382818] spec 0000:02:00.0: probe for device 0002:0000 91 + [13972.392773] spec 0000:02:00.0: got file "fmc/spec-init.bin", 1484404 (0x16a674) bytes 92 + [13972.591388] spec 0000:02:00.0: FPGA programming successful 93 + [13972.883011] spec 0000:02:00.0: EEPROM has no FRU information 94 + [13972.888719] spec 0000:02:00.0: No device_id filled, using index 95 + [13972.894676] spec 0000:02:00.0: No mezzanine_name found 96 + [13972.899863] /home/rubini/wip/spec-sw/kernel/spec-gpio.c - spec_gpio_init 97 + [13972.906578] spec 0000:04:00.0: probe for device 0004:0000 98 + [13972.916509] spec 0000:04:00.0: got file "fmc/spec-init.bin", 1484404 (0x16a674) bytes 99 + [13973.115096] spec 0000:04:00.0: FPGA programming successful 100 + [13973.401798] spec 0000:04:00.0: EEPROM has no FRU information 101 + [13973.407474] spec 0000:04:00.0: No device_id filled, using index 102 + [13973.413417] spec 0000:04:00.0: No mezzanine_name found 103 + [13973.418600] /home/rubini/wip/spec-sw/kernel/spec-gpio.c - spec_gpio_init 104 + spusa.root# ls /sys/bus/fmc/devices 105 + fmc-0000 fmc-0001 106 + spusa.root# insmod fmc-write-eeprom.ko busid=0x0200 file=fdelay-eeprom.bin 107 + [14103.966259] spec 0000:02:00.0: Matching an generic driver (no ID) 108 + [14103.975519] spec 0000:02:00.0: programming 6155 bytes 109 + [14126.373762] spec 0000:02:00.0: write_eeprom: success 110 + [14126.378770] spec 0000:04:00.0: Matching an generic driver (no ID) 111 + [14126.384903] spec 0000:04:00.0: fmc_write_eeprom: no filename given: not programming 112 + [14126.392600] fmc_write_eeprom: probe of fmc-0001 failed with error -2 113 + 114 + Reading back the EEPROM 115 + ======================= 116 + 117 + In order to read back the binary content of the EEPROM of your 118 + mezzanine device, the bus creates a read-only sysfs file called eeprom 119 + for each mezzanine it knows about: 120 + 121 + spusa.root# cd /sys/bus/fmc/devices; ls -l */eeprom 122 + -r--r--r-- 1 root root 8192 Apr 9 16:53 FmcDelay1ns4cha-f001/eeprom 123 + -r--r--r-- 1 root root 8192 Apr 9 17:19 fake-design-for-testing-f002/eeprom 124 + -r--r--r-- 1 root root 8192 Apr 9 17:19 fake-design-for-testing-f003/eeprom 125 + -r--r--r-- 1 root root 8192 Apr 9 17:19 fmc-f004/eeprom
+8
drivers/fmc/Kconfig
··· 32 32 The driver also handles interrupts (we used it with a real carrier 33 33 before the mezzanines were produced) 34 34 35 + config 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 + 35 43 endif # FMC
+1
drivers/fmc/Makefile
··· 9 9 10 10 obj-$(CONFIG_FMC_FAKEDEV) += fmc-fakedev.o 11 11 obj-$(CONFIG_FMC_TRIVIAL) += fmc-trivial.o 12 + obj-$(CONFIG_FMC_WRITE_EEPROM) += fmc-write-eeprom.o
+176
drivers/fmc/fmc-write-eeprom.c
··· 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 + */ 23 + static struct fmc_driver fwe_drv; 24 + 25 + FMC_PARAM_BUSID(fwe_drv); 26 + 27 + /* The "file=" is like the generic "gateware=" used elsewhere */ 28 + static char *fwe_file[FMC_MAX_CARDS]; 29 + static int fwe_file_n; 30 + module_param_array_named(file, fwe_file, charp, &fwe_file_n, 444); 31 + 32 + static 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 + 68 + static 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 + 82 + static 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 + */ 106 + int 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 + 147 + int fwe_remove(struct fmc_device *fmc) 148 + { 149 + return 0; 150 + } 151 + 152 + static 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 + 160 + static int fwe_init(void) 161 + { 162 + int ret; 163 + 164 + ret = fmc_driver_register(&fwe_drv); 165 + return ret; 166 + } 167 + 168 + static void fwe_exit(void) 169 + { 170 + fmc_driver_unregister(&fwe_drv); 171 + } 172 + 173 + module_init(fwe_init); 174 + module_exit(fwe_exit); 175 + 176 + MODULE_LICENSE("GPL");