Commit 6007b1bd0f752a5c022f7944c65fb96c39d6db3d

Authored by Alessandro Rubini
Committed by Greg Kroah-Hartman
1 parent 056d83f3c3

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>

Showing 5 changed files with 313 additions and 0 deletions Side-by-side Diff

Documentation/fmc/00-INDEX
... ... @@ -30,4 +30,7 @@
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
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
... ... @@ -32,5 +32,13 @@
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
drivers/fmc/Makefile
... ... @@ -9,4 +9,5 @@
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
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");