Commit 04eb2376 authored by Alessandro Rubini's avatar Alessandro Rubini

Merge branch 'fmc-as-submodule'

parents 7a1fb9bc 43ee322e
[submodule "fmc-bus"]
path = fmc-bus
url = git://ohwr.org/fmc-projects/fmc-bus.git
DIRS = kernel tools doc
DIRS = fmc-bus/kernel kernel tools doc
all clean modules install modules_install:
for d in $(DIRS); do $(MAKE) -C $$d $@ || exit 1; done
\input texinfo @c -*-texinfo-*-
%
% fmc-bus.in - main file for the documentation
%
%%%%
%------------------------------------------------------------------------------
%
% NOTE FOR THE UNAWARE USER
% =========================
%
% This file is a texinfo source. It isn't the binary file of some strange
% editor of mine. If you want ASCII, you should "make fmc-bus.txt".
%
%------------------------------------------------------------------------------
%
% This is not a conventional info file...
% I use three extra features:
% - The '%' as a comment marker, if at beginning of line ("\%" -> "%")
% - leading blanks are allowed (this is something I can't live without)
% - braces are automatically escaped when they appear in example blocks
%
@comment %**start of header
@documentlanguage en
@setfilename fmc-bus.info
@settitle fmc-bus
@iftex
@afourpaper
@end iftex
@paragraphindent none
@comment %**end of header
@setchapternewpage off
@set update-month July 2012
@finalout
@titlepage
@title FMC Bus Abstraction for Linux
@subtitle @value{update-month}
@subtitle Implementing a bus abstraction for fmc mezzanines
@author Alessandro Rubini for CERN (BE-CO-HT)
@end titlepage
@headings single
@c ##########################################################################
@iftex
@contents
@end iftex
@c ##########################################################################
@node Top
@top Introduction
This document describes the implementation of the @i{fmc} bus for
Linux. FMC (FPGA Mezzanine Carrier) is the standard we use for our
I/O devices, in the context of White Rabbit and related hardware.
In our I/O environments we need to write drivers for each mezzanine
card, and such drivers must work independent of the carrier being used.
To achieve this, we abstract the FMC interface
We have a carrier for PCI-E called @i{SPEC} and one for VME called
@i{SVEC}, but more are planned. Also, we support stand-alone devices
(usually plugged on a SPEC card), controlled through Etherbone,
developed by GSI.
Currently, code and documentation for the FMC bus is part of the
@i{spec-sw} project at @code{ohwr.org}.
@menu
* What is a Linux Bus::
* FMC Device::
* FMC Driver::
* The API Offered by Carriers::
@end menu
@c ##########################################################################
@node What is a Linux Bus
@chapter What is a Linux Bus
Within the Linux kernel, a @i{bus} is a data structure with a few
methods. It's main role is registering a list of devices and a list
of drivers, offering a @i{match} function that compares the respective
identifiers (in a bus-specific way) to assign drivers to devices.
Activation and deactivation of devices happens through the @i{probe}
and @i{remove} functions of the respective driver; an advanced user
can also use @i{sysfs} to change the binding of drivers to devices
(for example, if more than one driver can drive the same device you
may want to force the choice).
@c ##########################################################################
@node FMC Device
@chapter FMC Device
Within this framework, the FMC device is created and registered by the
carrier driver. For example, the PCI driver for the SPEC card fills a
data structure for each SPEC that it drives, and registers an
associated FMC device. The SVEC driver can do exactly the same for
the VME carrier (actually, it should do it twice, because the SVEC
carries two FMC mezzanines. Similarly, an Etherbone driver will be
able to register its own FMC devices, offering communication primitives
through frame exchange.
The contents of the EEPROM within the FMC will be used for
identification purposes, i.e. for matching the device with its own
driver. For this reason the device structure includes a complete copy
of the EEPROM (actually, the carrier driver may choose to only return
the leading part of it).
This is the current structure defining a device. Please note that all
the machinery is in place but some details may still change in the future.
For this reason, there is a version field at the beginning of the structure.
As usual, the minor number will change for compatible changes (like a new
flag) and the minor number will increase when an incompatible change
happens (for example, a change in layout of some @i{fmc} data structures).
Device writers should just set it to the
value @t{FMC_VERSION}, and be ready to get back @t{-EINVAL} at
registration time.
@smallexample
struct fmc_device {
unsigned long version; /* to be set to FMC_VERSION */
struct fmc_device_id id; /* for the match function */
struct fmc_operations *op; /* carrier-provided */
int irq; /* according to host bus. 0 == none */
int eeprom_len; /* Usually 8kB, may be less */
uint8_t *eeprom; /* Full contents or leading part */
char *carrier_name; /* "SPEC" or similar, for special use */
void *carrier_data; /* "struct spec *" or equivalent */
__iomem void *base; /* May be NULL (Etherbone) */
struct device dev; /* For Linux use */
struct device *hwdev; /* The underlying hardware device */
struct sdb_array *sdb;
void *mezzanine_data;
};
@end smallexample
@c ##########################################################################
@node FMC Driver
@chapter FMC Driver
Within this framework the FMC driver is expected to be independent of
the carrier being used. The matching between device and driver is
only based on the content of the EEPROM (as mandated by the FMC
standard) and the driver will perform I/O accesses only by means of
carrier-provided functions.
In some special cases it is possible for a driver to directly access
FPGA registers, by means of the @code{base} field of the device
structure. This is needed for high-bandwidth peripherals like fast ADC
cards. The @code{base} pointer is NULL for remote devices, and the driver
will refuse to work with them if it needs direct access.
In even more special cases, the driver may access carrier-specific
functionality: the @code{carrier_name} string allows the driver to
check which is the current carrier and make use of the
@code{carrier_data} pointer. We chose to use carrier names rather
than numeric identifiers for greater flexibility, but also to avoid a
central registry within the @code{fmc.h} file -- we hope other users
will exploit our framework with their own carriers.
@c ##########################################################################
@node Functions Exported by the Core
@chapter Functions Exported by the Core
The FMC core exports the usual 4 functions that are needed for a bus
to work:
@smallexample
int fmc_driver_register(struct fmc_driver *drv);
void fmc_driver_unregister(struct fmc_driver *drv);
int fmc_device_register(struct fmc_device *tdev);
void fmc_device_unregister(struct fmc_device *tdev);
@end smallexample
They should be self-explicative, so nothing is added here.
@c ##########################################################################
@node The API Offered by Carriers
@chapter The API Offered by Carriers
The carrier provides a number of methods by means of the
@code{fmc_operations} structure, which currently is defined like this
(again, it is a moving target, please refer to the header rather than
this document):
@smallexample
struct fmc_operations {
uint32_t (*readl)(struct fmc_device *fmc, int offset);
void (*writel)(struct fmc_device *fmc, uint32_t value, int offset);
int (*reprogram)(struct fmc_device *f, struct fmc_driver *d, char *gw);
int (*validate)(struct fmc_device *fmc, struct fmc_driver *drv);
int (*irq_request)(struct fmc_device *fmc, irq_handler_t h,
char *name, int flags);
void (*irq_ack)(struct fmc_device *fmc);
int (*irq_free)(struct fmc_device *fmc);
int (*gpio_config)(struct fmc_device *fmc, struct fmc_gpio *gpio,
int ngpio);
int (*read_ee)(struct fmc_device *fmc, int pos, void *d, int l);
int (*write_ee)(struct fmc_device *fmc, int pos, const void *d, int l);
};
@end smallexample
The individual methods perform the following tasks:
@table @code
@item readl
@itemx writel
These functions access FPGA registers by whatever means the
carrier offers. They are not expected to fail, as most of the time
they will just make a memory access to the host bus. If the
carrier provided a @i{base} pointer, the driver may use direct
access through it instead. For this reason the header offers
the inline functions @i{fmc_readl} and @i{fmc_writel} that
access @i{base} if respective method is NULL.
For Etherbone, or other non-local carriers,
error-management is still to be defined.
@item validate
Module parameters are used to manage different applications for
two or more boards of the same kind. Validation uses
the @i{bus_id} module parameter (if provided) and returns the
matching index in the array. If no match is found, @code{-ENOENT}
is returned; if the argument has not been specified, all
devices match the driver and 0 is returned. The value returned
by the validate method can be used as index into other parameters
(for example, some drivers use the @code{lm32=} parameter in this
way). Such ``generic parameters'' are currently documented in the
@i{spec-sw} manual; this @i{validate} method is on show in
@code{spec-fmc.c} and it is used by @code{fmc-trivial.c}.
@item reprogram
The carrier enumerates FMC devices by loading a standard (or
@i{golden} FPGA binary that allows EEPROM access. Each driver, then,
will need to reprogram the FPGA by calling this
function. If the name argument is NULL,
the carrier will reprogram the golden binary. If the gateware name
has been overridden through module parameters (in a carrier-specific
way) the file loaded will match the parameters.
@item irq_request
@itemx irq_ack
@itemx irq_free
Interrupt management is carrier-specific, so it is abstracted
as operations. The interrupt number is listed in the device
structure, but it's only informative for the mezzanine driver.
The handler will receive the @i{fmc} pointer as @i{dev_id}; the
@i{flags} argument is still to be defined.
@item gpio_config
The method allows to configure a GPIO pin in the carrier, and
read its current value if it is configured as input. See
@ref{The GPIO Abstraction} for details.
@item read_ee
@itemx write_ee
Read or write the EEPROM. The functions are expected to be only
called before reprogramming and the carrier will refuse them
with @code{ENODEV} after reprogramming. The offset is limited
to 8kB but addresses up to 1MB are reserved to fit bigger I2C
devices in the future. Carriers may offer
access to other internal flash memories using these same methods:
for example the SPEC driver may define that its own I2C memory
is seen at offset 1M and the internal SPI flash is seen at offset
16M. This is carrier-specific and should only be used by the
driver after checking the @code{carrier_name} field.
@end table
@c ##########################################################################
@node The GPIO Abstraction
@chapter The GPIO Abstraction
Support for GPIO pins in the @i{fmc-bus} environment is a little
heavy, and deserves special discussion.
While the general idea of a carrier-independent driver seems to fly,
configuration of specific signals within the carrier needs at least
some knowledge of the carrier itself. For this reason, the specific
driver can request to configure carrier-specific GPIO pins, numbered
from 0 to at most 4095. Configuration is performed by passing
a pointer to an array of @t{struct fmc_gpio} items, as well as
the number of those items:
@example
struct fmc_gpio {
char *carrier_name;
int gpio;
int _gpio; /* internal use by the carrier */
int mode; /* GPIOF_DIR_OUT etc, from <linux/gpio.h> */
int irqmode; /* IRQF_TRIGGER_LOW and so on */
};
@end example
By specifying a @i{carrier_name} for each pin, the driver may access
different pins in different carriers. The @i{gpio_config} method
returns the number of pins successfully configured, and each carrier
just ignores requests for other carriers. So, for example, a driver
that has been developed and tested on both the SPEC and the SVEC may
request configuration of two different GPIO pins, and expect one such
configuration to succeed -- if none succeeds it most likely means that
the current carrier is a still-unknown one. (FIXME: the return value
is not actually used this way in current code).
If, however, your GPIO pin has a specific known role, you can
pass a special number in the @t{gpio} field. The header defines
the following macros:
@example
#define FMC_GPIO_RAW(x) (x) /* 4096 of them */
#define FMC_GPIO_IRQ(x) ((x) + 0x1000) /* 256 of them */
#define FMC_GPIO_LED(x) ((x) + 0x1100) /* 256 of them */
#define FMC_GPIO_KEY(x) ((x) + 0x1200) /* 256 of them */
#define FMC_GPIO_TP(x) ((x) + 0x1300) /* 256 of them */
#define FMC_GPIO_USER(x) ((x) + 0x1400) /* 256 of them */
@end example
Use of virtual GPIO numbers (anything but @t{FMC_GPIO_RAW}) is allowed
provided the @i{carrier_name} field is left unspecified (NULL). Each
carrier is responsible for providing a mapping between virtual and
physical GPIO numbers (and possibly cache the raw number in the
@t{_gpio} field). All carriers must map their I/O lines
to the sets above starting from zero. The SPEC, for example, maps
interrupt 0 and 1, and test points 0 through 3.
If, for example, a driver requires a free led and a test point (for a
scope probe to be plugged at some point during development) it may ask
for @t{FMC_GPIO_LED(0)} and @t{FMC_GPIO_TP(0)}. Each carrier will
provide suitable GPIO pins. Clearly, the person running the drivers
will know the order used by the specific carrier driver in assigning
leds and testpoints, so to make a carrier-dependent use of the diagnostic tools.
In theory, some form of autodetection should be possible: a driver
like the @i{wr-nic} (which uses IRQ(1) on the SPEC card) should
configure IRQ(0), make a test with software-generated interrupts and
configure IRQ(1) if the test fails -- the @i{wr-nic} gateware is
known to use IRQ1 on the SPEC, but the driver should be
carrier-independent if possible and thus use IRQ(0) as a first bet.
If a pin is configured as input, the @i{gpio_config} method returns 0
or 1, to report its current value. Invalid GPIO numbers will cause
@code{-ENODEV} to be returned for physical numbers and @code{-ENOENT}
for virtual mappings.
@bye
@c LocalWords: gnudd titlepage iftex texinfo CERN documentlanguage settitle
@c LocalWords: documentencoding setfilename afourpaper paragraphindent SVEC
@c LocalWords: setchapternewpage finalout Etherbone EEPROM
......@@ -72,9 +72,9 @@ the one used by @i{fine-delay-sw-v1.1}. If you are using that version, please
compile the manual you find in your source code repository and avoid
reading this one.
The package currently includes both the @i{spec} driver and the
@i{fmc} bus driver (documented separately).
Moreover, it includes some drivers for fmc cards.
The driver is a client of the FMC software bus abstraction. The package
@i{fmc-bus} is dowloaded as a @i{git} submodule of this package, and
its latest version can be found at @url{http://www.ohwr.org/projects/fmc-bus}.
@c ##########################################################################
@node Compiling the Drivers
......@@ -130,16 +130,20 @@ the case shown above your driver will end up being installed
/mnt/target/lib/modules/3.4.0/extra/spec.ko
@end example
Please note that by default the package compiles an installs the
@i{fmc-bus} modules, too (the project is a @i{git} submodule).
@c ##########################################################################
@node Role of spec.ko
@chapter Role of spec.ko
The @code{spec.ko} driver depends on @code{fmc.ko}, that must
be loaded first (if you don't rely on automatic dependencies).
be loaded first (unless you rely on automatic dependencies).
@code{spec.ko} registers as a PCI driver,
using both the ``old'' identifiers (i.e. the Gennum vendor and GN4124 device)
and the new ones (CERN vendor and SPEC device).
and the new ones (CERN vendor and SPEC device). It has a module device table,
so after installation it will be automatically loaded at the next boot.
@c ==========================================================================
@node SPEC Initialization
......@@ -149,7 +153,7 @@ For each new SPEC device found on the system, the driver performs the
following steps:
@itemize @bullet
@item It enables MSI interrupts, the only ones supported in this package.
@item It enables MSI interrupts if enabled with a parameter.
@item It loads the @code{fmc/spec-init.bin} ``golden'' gateware file.
@item It checks that the content of the binary is as expected (using
a minimal @i{sdb}-based verification).
......@@ -165,9 +169,10 @@ from the @i{files} area of the @i{spec-sw} project on @code{ohwr.org}.
The current binary version to be used with this software version is
@url{http://www.ohwr.org/attachments/download/1454/spec-init.bin-2012-07-24}.
The EEPROM content that has been read at initialization time
is still available to sub-drivers after they are loaded, but a
sub-driver cannot write to the EEPROM using carrier methods.
The EEPROM content that the carrier driver reads at initialization time
is still available to mezzanine drivers after they are loaded, but a
mezzanine driver cannot write to the EEPROM using carrier methods
after reprogramming the FPGA.
The carrier refuses to act on
registers after the golden gateware is replaced by a new
mezzanine-specific binary,
......@@ -175,8 +180,9 @@ which by definition is unknown to the carrier itself.
@b{Note:} currently the SPEC driver does not re-write the golden
binary file when the sub-driver releases control of the card. This
allows a further driver to make use of an existing binary, and may be
allows a further driver to make use of an existing binary, which may be
useful during development.
@c ==========================================================================
@node SPEC Module Parameters
@section SPEC Module Parameters
......@@ -226,12 +232,12 @@ gateware file. Similarly, the @i{spec} driver is not concerned with
programming the LM32 image, when it makes sense to. This is different
from the role splitting in previous versions of the driver.
@b{Note:} the gateware binary is looked-for in @i{/lib/firmware/fmc},
@b{Note:} the driver looks for the gateware binary in @i{/lib/firmware/fmc},
which is where all fmc-related external files are expected to live.
That's because our own installations share firmware for COTS peripherals
but mount a host-specific NFS subdirectory.
Please refer to the @i{fmc-bus} document for details about the overall
Please refer to the @i{fmc-bus} manual for details about the overall
design of the interactions of carriers and mezzanines.
@b{Warning:} currently the @i{match} function of the bus always
......@@ -242,65 +248,6 @@ the associated data structures and code. For this reason there is no
you load will drive all SPEC cards found on the system unless it
limits itself through parameters (see below)
@c ==========================================================================
@node Sub-Module Parameters
@section Sub-Module Parameters
Most of the FMC drivers, also called sub-modules, need the same
set of kernel parameters, this package includes support
to implement common parameters, by means of fields
in the @code{fmc_driver} structure and simple macro definitions.
The parameters are carrier-specific, in that they rely on the @i{busid}
concept, that varies among carriers (here it is a PCI bus-devfn number).
Drivers for other carriers will most likely offer something similar
but not identical to this; some code duplication is unavoidable.
This is the list of parameters, to see how they are used in sub-modules,
please look at @i{spec-trivial.c}.
@table @code
@item busid=
This is an array of integers, specifying the PCI bus and PCI devfn,
each of the fields being 8 bits (and the latter is most likely 0).
For example: @code{0x0400} (bus 4,
slot 0). If any such ID is specified, the sub-driver will only
accept to drive cards that appear in the list (even if the
FMC ID matches).
@item gateware=
The argument is an array of strings. If no @i{busid=} is
specified, the first string of @i{gateware=} is used for
all cards; otherwise the identifiers and gateware names are
paired one by one, in the order specified.
@item show_sdb=
For modules supporting it, this parameter asks sub-modules to
show the SDB internal structure to kernel messages. It is
disabled by default because those lines tend to hide more
important messages when you look at the system console while
loading the drivers.
@end table
For example, if you are using the trivial driver to load two different
gateware files to two different cards, you can use the following
parameters to load different binaries to the cards, after looking up
the PCI identifiers:
@smallexample
insmod fmc-trivial.ko \
busid=0x0200,0x0400 \
gateware=fmc/fine-delay.bin,fmc/simple-dio.bin
@end smallexample
Please note that not all sub-modules support all of those parameters.
You can use @i{modinfo} to check what is supported by each module.
@c ==========================================================================
@node Interrupts in spec.ko
@section Interrupts in spec.ko
......@@ -385,111 +332,16 @@ for the peripheral:
48: 70470 0 0 26 PCI-MSI-edge wr-nic
@end smallexample
@c ##########################################################################
@node fmc-trivial.ko
@chapter fmc-trivial.ko
The simple module @i{fmc-trivial} is just a simple client that
registers an interrupt handler. I used it to verify the basic mechanism
of the FMC bus and how interrupts worked.
The module implements the generic SPEC parameters, so it can program a
different gateware file in each card. The whole list of parameters it
accepts are:
@table @code
@item busid=
@itemx gateware=
Generic parameters. See @ref{Sub-Module Parameters}.
@c no docbook is generated, so the following is like "#if 0"
@ifdocbook
@item sdb=
This is an array of integers, specifying the @i{sdb}
starting address after programming the gateware. See
@ref{Sub-Module Parameters} about how this is used in multi-board
environments.
@item lm32=
This is a array of strings like @code{gateware=},
listing the name or names
to be used to reprogram the internal LM32. The same rules as for
@code{gateware=} above are used for matching binaries and cards.
The carrier will copy the @i{lm32} executable to the first
SDB record that is mapped as ``@code{WB4-BlockRAM}'' (thus,
it needs the @code{sdb=} values to be specified.
@end ifdocbook
@end table
This driver is worth reading, but it is not worth describing here.
@c ##########################################################################
@node fmc-write-eeprom.ko
@chapter fmc-write-eeprom.ko
This module is designed to load a binary file from @i{/lib/firmware}
and to write it to the internal EEPROM of the mezzanine card. This
driver uses the @code{busid} generic parameter, but doesn't use the
other ones (even if @i{modinfo} reports them).
Overwriting the EEPROM is not something you should do daily, and it is
expected to only happen during manufacturing. For this reason, the
module makes it unlikely for the random user to change a working EEPROM.
The module takes the following measures:
@itemize @bullet
@item It accepts a @code{file=} argument (within @i{/lib/firmware})
and if no such argument is received, it doesn't write anything
(i.e. there is no default file name)
@item If the file name ends with @code{.bin} it is written verbatim
starting at offset 0.
@item If the file name ends with @code{.tlv} it is interpreted as
type-length-value (i.e., it allows @i{writev(2)}-like operation).
@item If the file name doesn't match any of the patterns above, it is
ignored and no write is performed.
@item Only cards listed with @code{busid=} are written to. If no
@i{busid} is specified, no programming is done (and the probe function
of the driver will fail).
@end itemize
Each TLV tuple is formatted in this way: the header is 5 bytes,
followed by data. The first byte is @code{w} for @i{write}, the
next two bytes represent the address, in little-endian byte order, and
the next two represent the data length, in little-endian order. The length does
not include the header (it is the actual number of bytes to be
written).
This is a real example: that writes 5 bytes at position 0x110:
@smallexample
spusa.root# od -t x1 -Ax /lib/firmware/try.tlv
000000 77 10 01 05 00 30 31 32 33 34
00000a
spusa.root# insmod /tmp/fmc-write-eeprom.ko busid=0x0200 file=try.tlv
[19983.391498] spec 0000:03:00.0: write 5 bytes at 0x0110
[19983.414615] spec 0000:03:00.0: write_eeprom: success
@end smallexample
Please note that you'll most likely want to use SDBFS to build your
EEPROM image, at least if your mezzanines are being used in the White Rabbit
environment. For this reason the TLV format is not expected to be used much
and is not expected to be developed further.
@c ##########################################################################
@node The WR-NIC
@chapter The WR-NIC
With the current code base, the @code{wr-nic.ko} driver is designed
This package is expected to only host the @i{carrier} driver, i.e.
the one that registers a @i{device} for the FMC bus. However, at the
time being the @i{wr-nic} mezzanine driver is part of this package,
for various reasons.
The @code{wr-nic.ko} mezzanine driver is designed
to run with the simple 5-Channel @i{simple-DIO} mezzanine board,
because it includes both
code to access the network card and code to act on the DIO channels,
......@@ -497,7 +349,8 @@ using time tags that are provided by the @i{White Rabbit} mechanism.
Similarly, both incoming and outgoing frames can be time-stamped by
@i{White Rabbit}.
This driver is the most important driver in this package: it is
This driver is the most important driver in this package, from the
user's point of view. It is
a generic implementation of the @i{spec-sw} framework which can be
useful by itself as a White Rabbit starter kit. Moreover, it is a
complete driver that can serve as a model for other developments.
......@@ -552,11 +405,13 @@ register definitions. This is the role of each of them:
@table @file
@item include/linux/fmc.h
@itemx include/linux/sdb.h
@itemx include/linux/fmc-sdb.h
@item fmc-bus/kernel/include/linux/fmc.h
@itemx fmc-bus/kernel/include/linux/sdb.h
@itemx fmc-bus/kernel/include/linux/fmc-sdb.h
These three headers are used to define the interface to the
These three headers are part of the @i{fmc-bus} project,
whose repository is a @i{git} submodule of this one.
They are used to define the interface to the
FMC bus abstraction and the SDB self-description of the
internal FPGA bus. They are used by other @i{spec-sw} files as well.
We include them as @code{<linux/fmc.h>} because we plan to
......@@ -701,8 +556,8 @@ The module receives the following parameters:
These are standard parameters. You can either limit the driver
to only bind to some specific SPEC cards (not all of the ones
selected by the @i{match} function of @i{fmc-bus}), or to
assign different @i{gateware} files for each card. See
@ref{Sub-Module Parameters}.
assign different @i{gateware} files for each card. They
are fully described in the @i{fmc-bus} manual.
@item show_sdb=
......@@ -1060,30 +915,33 @@ The current version of the tool supports the following commands:
0 means ``stop generating pulses'').
@item mode <channel> <mode> [<channel> <mode> ...]
@itemx mode <mode0><mode1><mode2><mode3><mode4>
@itemx mode <m0><m1><m2><m3><m4>
Configure one or more channel for the specified mode. Each mode
is one character, so the second form receives a 5-byte
string argument. Available modes are ``@code{I}'' (input),
Configure one or more channels for the specified mode. Each mode
is represented by one character; the latter form above thus
configures all 5 channels by means of a since 5-bytes-long string.
Supported modes are ``@code{I}'' (input),
``@code{0}'' and ``@code{1}'' (output, steady state),
``@code{D}'' (DIO core output) or ``@code{-}'' (unchanged).
A channel managed by the DIO core is normally low and can
pulse high on request (see @code{pulse}
command. Uppercase @code{D} or @code{I} activate the 50-ohm termination
resistor, while lowercase @code{d} or @code{i} disable the termination
(output is always without termination, but I may add @code{L} and
@code{H} if needed: the driver supports all combinations).
resistor with either WR-DIO or input mode,
while lowercase @code{d} or @code{i} disable the termination.
Currently output modes are always without termination,
but I may add @code{L} and
@code{H} if needed: the hardware supports all combinations).
Please note that the @code{pulse} command turns the affected channel in
@i{DIO} mode anyways (without changing the termination).
@end table
Please note that generation of a pulse train is performed by
Generation of a pulse train is performed by
software running at interrupt time,
because the @i{simple DIO} card and gateware can only emit one pulse at
a requested @i{White Rabbit} time. For this reason you'll have a lower
limit for the pulse period, according to how powerful your computer
is -- and how much you loaded with other tasks. In the future, new
because the @i{simple DIO} card and gateware can only emit a single pulse at
a requested @i{White Rabbit} time. For this reason you'll observe that
the pulse period cannot be too short, according to how powerful your computer
is and how much you loaded it with other tasks. In the future, new
@i{gateware} files may perform pulse trains in hardware.
There is no command to flush the timestamp FIFOs, but you can
......
fmc-bus @ 53298896
Subproject commit 532988962e856188fb26890b2912c1171c4d3695
LINUX ?= /lib/modules/$(shell uname -r)/build
ccflags-y += -I$M/include
KBUILD_EXTRA_SYMBOLS := $M/../fmc-bus/kernel/Module.symvers
ccflags-y += -I$M/include -I$M/../fmc-bus/kernel/include
ccflags-y += $(WR_NIC_CFLAGS)
obj-m = fmc.o
obj-m += spec.o
obj-m += fmc-trivial.o
obj-m += fmc-write-eeprom.o
obj-m += wr-nic.o
fmc-y = fmc-core.o
fmc-y += fmc-sdb.o
spec-y = spec-pci.o
spec-y += spec-fmc.o
spec-y += spec-i2c.o
......
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/fmc.h>
#include "spec.h"
static int fmc_check_version(unsigned long version, const char *name)
{
if (__FMC_MAJOR(version) != FMC_MAJOR) {
pr_err("%s: \"%s\" has wrong major (has %li, expected %i)\n",
__func__, name, __FMC_MAJOR(version), FMC_MAJOR);
return -EINVAL;
}
if (__FMC_MINOR(version) != FMC_MINOR)
pr_info("%s: \"%s\" has wrong minor (has %li, expected %i)\n",
__func__, name, __FMC_MINOR(version), FMC_MINOR);
return 0;
}
static int fmc_match(struct device *dev, struct device_driver *drv)
{
//struct fmc_driver *fdrv = to_fmc_driver(drv);
//struct fmc_device *fdev = to_fmc_device(dev);
//const struct fmc_device_id *t = fdrv->id_table;
/* Currently, return 1 every time, until we define policies */
return 1;
}
static int fmc_uevent(struct device *dev, struct kobj_uevent_env *env)
{
//struct fmc_device *fdev = to_fmc_device(dev);
/* FIXME: The MODALIAS */
add_uevent_var(env, "MODALIAS=%s", "fmc");
return 0;
}
static int fmc_probe(struct device *dev)
{
struct fmc_driver *fdrv = to_fmc_driver(dev->driver);
struct fmc_device *fdev = to_fmc_device(dev);
return fdrv->probe(fdev);
}
static int fmc_remove(struct device *dev)
{
struct fmc_driver *fdrv = to_fmc_driver(dev->driver);
struct fmc_device *fdev = to_fmc_device(dev);
return fdrv->remove(fdev);
}
static void fmc_shutdown(struct device *dev)
{
/* not implemented but mandatory */
}
static struct bus_type fmc_bus_type = {
.name = "fmc",
.match = fmc_match,
.uevent = fmc_uevent,
.probe = fmc_probe,
.remove = fmc_remove,
.shutdown = fmc_shutdown,
};
/* Every device must have a release method: provide a default */
static void __fmc_release(struct device *dev){ }
/* This is needed as parent for our devices and dir in sysfs */
struct device fmc_bus = {
.release = __fmc_release,
.init_name = "fmc",
};
/* Functions for client modules */
int fmc_driver_register(struct fmc_driver *drv)
{
if (fmc_check_version(drv->version, drv->driver.name))
return -EINVAL;
drv->driver.bus = &fmc_bus_type;
return driver_register(&drv->driver);
}
EXPORT_SYMBOL(fmc_driver_register);
void fmc_driver_unregister(struct fmc_driver *drv)
{
driver_unregister(&drv->driver);
}
EXPORT_SYMBOL(fmc_driver_unregister);
int fmc_device_register(struct fmc_device *fdev)
{
if (fmc_check_version(fdev->version, fdev->carrier_name))
return -EINVAL;
device_initialize(&fdev->dev);
if (!fdev->dev.release)
fdev->dev.release = __fmc_release;
if (!fdev->dev.parent)
fdev->dev.parent = &fmc_bus;
fdev->dev.bus = &fmc_bus_type;
{
static int i;
/* FIXME: the name */
dev_set_name(&fdev->dev, "fmc-%04x", i++);
}
return device_add(&fdev->dev);
}
EXPORT_SYMBOL(fmc_device_register);
void fmc_device_unregister(struct fmc_device *fdev)
{
device_del(&fdev->dev);
put_device(&fdev->dev);
}
EXPORT_SYMBOL(fmc_device_unregister);
/* Init and exit are trivial */
static int fmc_init(void)
{
int err;
err = device_register(&fmc_bus);
if (err)
return err;
err = bus_register(&fmc_bus_type);
if (err)
device_unregister(&fmc_bus);
return err;
}
static void fmc_exit(void)
{
bus_unregister(&fmc_bus_type);
device_unregister(&fmc_bus);
}
module_init(fmc_init);
module_exit(fmc_exit);
MODULE_LICENSE("GPL");
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fmc.h>
#include <linux/sdb.h>
#include <linux/err.h>
#include <linux/fmc-sdb.h>
#include <asm/byteorder.h>
static uint32_t __sdb_rd(struct fmc_device *fmc, unsigned long address,
int convert)
{
uint32_t res = fmc_readl(fmc, address);
if (convert)
return __be32_to_cpu(res);
return res;
}
static struct sdb_array *__fmc_scan_sdb_tree(struct fmc_device *fmc,
unsigned long address, int level)
{
uint32_t onew;
int i, j, n, convert = 0;
struct sdb_array *arr, *sub;
onew = fmc_readl(fmc, address);
if (onew == SDB_MAGIC) {
/* Uh! If we are little-endian, we must convert */
if (SDB_MAGIC != __be32_to_cpu(SDB_MAGIC))
convert = 1;
} else if (onew == __be32_to_cpu(SDB_MAGIC)) {
/* ok, don't convert */
} else {
return ERR_PTR(-ENOENT);
}
/* So, the magic was there: get the count from offset 4*/
onew = __sdb_rd(fmc, address + 4, convert);
n = __be16_to_cpu(*(uint16_t *)&onew);
arr = kzalloc(sizeof(*arr), GFP_KERNEL);
if (arr) {
arr->record = kzalloc(sizeof(arr->record[0]) * n, GFP_KERNEL);
arr->subtree = kzalloc(sizeof(arr->subtree[0]) * n, GFP_KERNEL);
}
if (!arr || !arr->record || !arr->subtree) {
kfree(arr->record);
kfree(arr->subtree);
kfree(arr);
return ERR_PTR(-ENOMEM);
}
arr->len = n;
arr->level = level;
arr->fmc = fmc;
for (i = 0; i < n; i++) {
union sdb_record *r;
for (j = 0; j < sizeof(arr->record[0]); j += 4) {
*(uint32_t *)((void *)(arr->record + i) + j) =
__sdb_rd(fmc, address + (i * 64) + j, convert);
}
r = &arr->record[i];
arr->subtree[i] = ERR_PTR(-ENODEV);
if (r->empty.record_type == sdb_type_bridge) {
uint64_t subaddr = address + r->bridge.sdb_child;
struct sdb_component *c;
c = &r->bridge.sdb_component;
subaddr = __be64_to_cpu(subaddr);
sub = __fmc_scan_sdb_tree(fmc, subaddr, level + 1);
arr->subtree[i] = sub; /* may be error */
if (IS_ERR(sub))
continue;
sub->parent = arr;
sub->baseaddr = __be64_to_cpu(c->addr_first);
}
}
return arr;
}
int fmc_scan_sdb_tree(struct fmc_device *fmc, unsigned long address)
{
struct sdb_array *ret;
if (fmc->sdb)
return -EBUSY;
ret = __fmc_scan_sdb_tree(fmc, address, 0);
if (IS_ERR(ret))
return PTR_ERR(ret);
fmc->sdb = ret;
return 0;
}
EXPORT_SYMBOL(fmc_scan_sdb_tree);
static void __fmc_sdb_free(struct sdb_array *arr)
{
int i, n;
if (!arr) return;
n = arr->len;
for (i = 0; i < n; i++) {
if (IS_ERR(arr->subtree[i]))
continue;
__fmc_sdb_free(arr->subtree[i]);
}
kfree(arr->record);
kfree(arr->subtree);
kfree(arr);
}
int fmc_free_sdb_tree(struct fmc_device *fmc)
{
__fmc_sdb_free(fmc->sdb);
fmc->sdb = NULL;
return 0;
}
EXPORT_SYMBOL(fmc_free_sdb_tree);
static void __fmc_show_sdb_tree(struct fmc_device *fmc, struct sdb_array *arr)
{
int i, j, n = arr->len, level = arr->level;
struct sdb_array *ap;
for (i = 0; i < n; i++) {
unsigned long base;
union sdb_record *r;
struct sdb_product *p;
struct sdb_component *c;
r = &arr->record[i];
c = &r->dev.sdb_component;
p = &c->product;
base = 0;
for (ap = arr; ap; ap = ap->parent)
base += ap->baseaddr;
dev_info(fmc->hwdev, "SDB: ");
for (j = 0; j < level; j++)
printk(" ");
switch(r->empty.record_type) {
case sdb_type_interconnect:
printk("%08llx:%08x %.19s\n",
__be64_to_cpu(p->vendor_id),
__be32_to_cpu(p->device_id),
p->name);
break;
case sdb_type_device:
printk("%08llx:%08x %.19s (%08llx-%08llx)\n",
__be64_to_cpu(p->vendor_id),
__be32_to_cpu(p->device_id),
p->name,
__be64_to_cpu(c->addr_first) + base,
__be64_to_cpu(c->addr_last) + base);
break;
case sdb_type_bridge:
printk("%08llx:%08x %.19s (bridge: %08llx)\n",
__be64_to_cpu(p->vendor_id),
__be32_to_cpu(p->device_id),
p->name,
__be64_to_cpu(c->addr_first) + base);
if (IS_ERR(arr->subtree[i])) {
printk("(bridge error %li)\n",
PTR_ERR(arr->subtree[i]));
break;
}
__fmc_show_sdb_tree(fmc, arr->subtree[i]);
break;
case sdb_type_integration:
printk("integration\n");
break;
case sdb_type_empty:
printk("empty\n");
break;
default:
printk("UNKNOWN TYPE 0x%02x\n", r->empty.record_type);
break;
}
}
}
void fmc_show_sdb_tree(struct fmc_device *fmc)
{
if (!fmc->sdb)
return;
__fmc_show_sdb_tree(fmc, fmc->sdb);
}
EXPORT_SYMBOL(fmc_show_sdb_tree);
signed long fmc_find_sdb_device(struct sdb_array *tree,
uint64_t vid, uint32_t did, unsigned long *sz)
{
signed long res = -ENODEV;
union sdb_record *r;
struct sdb_product *p;
struct sdb_component *c;
int i, n = tree->len;
uint64_t last, first;
/* FIXME: what if the first interconnect is not at zero? */
for (i = 0; i < n; i++) {
r = &tree->record[i];
c = &r->dev.sdb_component;
p = &c->product;
if (!IS_ERR(tree->subtree[i]))
res = fmc_find_sdb_device(tree->subtree[i],
vid, did, sz);
if (res >= 0)
return res + tree->baseaddr;
if (r->empty.record_type != sdb_type_device)
continue;
if (__be64_to_cpu(p->vendor_id) != vid)
continue;
if (__be32_to_cpu(p->device_id) != did)
continue;
/* found */
last = __be64_to_cpu(c->addr_last);
first = __be64_to_cpu(c->addr_first);
if (sz) *sz = (typeof(*sz))(last + 1 -first);
return first + tree->baseaddr;
}
return res;
}
EXPORT_SYMBOL(fmc_find_sdb_device);
/* A trivial fmc driver that can load a gateware file and reports interrupts */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/fmc.h>
#include "spec.h"
static struct fmc_driver t_drv; /* initialized later */
irqreturn_t t_handler(int irq, void *dev_id)
{
struct fmc_device *fmc = dev_id;
fmc->op->irq_ack(fmc);
dev_info(fmc->hwdev, "received irq %i\n", irq);
return IRQ_HANDLED;
}
struct fmc_gpio t_gpio[] = {
{
.gpio = FMC_GPIO_IRQ(0),
.mode = GPIOF_DIR_IN,
.irqmode = IRQF_TRIGGER_RISING,
}, {
.gpio = FMC_GPIO_IRQ(1),
.mode = GPIOF_DIR_IN,
.irqmode = IRQF_TRIGGER_RISING,
}
};
int t_probe(struct fmc_device *fmc)
{
int ret;
int index;
index = fmc->op->validate(fmc, &t_drv);
if (index < 0)
return -EINVAL; /* not our device: invalid */
ret = fmc->op->irq_request(fmc, t_handler, "fmc-trivial", IRQF_SHARED);
if (ret < 0)
return ret;
/* ignore error code of call below, we really don't care */
fmc->op->gpio_config(fmc, t_gpio, ARRAY_SIZE(t_gpio));
/* Reprogram, if asked to. ESRCH == no filename specified */
ret = fmc->op->reprogram(fmc, &t_drv,"");
if (ret == -ESRCH)
ret = 0;
if (ret < 0)
fmc->op->irq_free(fmc);
/* FIXME: reprogram LM32 too */
return ret;
}
int t_remove(struct fmc_device *fmc)
{
fmc->op->irq_free(fmc);
return 0;
}
static struct fmc_driver t_drv = {
.version = FMC_VERSION,
.driver.name = KBUILD_MODNAME,
.probe = t_probe,
.remove = t_remove,
/* no table, as the current match just matches everything */
};
/* We accept the generic parameters */
FMC_PARAM_BUSID(t_drv);
FMC_PARAM_GATEWARE(t_drv);
static int t_init(void)
{
int ret;
ret = fmc_driver_register(&t_drv);
return ret;
}
static void t_exit(void)
{
fmc_driver_unregister(&t_drv);
}
module_init(t_init);
module_exit(t_exit);
MODULE_LICENSE("GPL and additional rights"); /* public domain */
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/module.h>
#include <linux/string.h>
#include <linux/firmware.h>
#include <linux/init.h>
#include <linux/fmc.h>
#include <asm/unaligned.h>
/*
* This module uses the firmware loader to program the whole or part
* of the FMC eeprom. The meat is in the _run functions. However, no
* default file name is provided, to avoid accidental mishaps. Also,
* you must pass the busid argument
*/
static struct fmc_driver fwe_drv;
FMC_PARAM_BUSID(fwe_drv);
/* The "file=" is like the generic "gateware=" used elsewhere */
static char *fwe_file[FMC_MAX_CARDS];
static int fwe_file_n;
module_param_array_named(file, fwe_file, charp, &fwe_file_n, 444);
static int fwe_run_tlv(struct fmc_device *fmc, const struct firmware *fw,
int write)
{
const uint8_t *p = fw->data;
int len = fw->size;
uint16_t thislen, thisaddr;
int err;
/* format is: 'w' addr16 len16 data... */
while (len > 5) {
thisaddr = get_unaligned_le16(p+1);
thislen = get_unaligned_le16(p+3);
if (p[0] != 'w' || thislen + 5 > len) {
dev_err(fmc->hwdev, "invalid tlv at offset %i\n",
p - fw->data);
return -EINVAL;
}
err = 0;
if (write) {
dev_info(fmc->hwdev, "write %i bytes at 0x%04x\n",
thislen, thisaddr);
err = fmc->op->write_ee(fmc, thisaddr, p + 5, thislen);
}
if (err < 0) {
dev_err(fmc->hwdev, "write failure @0x%04x\n",
thisaddr);
return err;
}
p += 5 + thislen;
len -= 5 + thislen;
}
if (write)
dev_info(fmc->hwdev, "write_eeprom: success\n");
return 0;
}
static int fwe_run_bin(struct fmc_device *fmc, const struct firmware *fw)
{
int ret;
dev_info(fmc->hwdev, "programming %i bytes\n", fw->size);
ret = fmc->op->write_ee(fmc, 0, (void *)fw->data, fw->size);
if (ret < 0) {
dev_info(fmc->hwdev, "write_eeprom: error %i\n", ret);
return ret;
}
dev_info(fmc->hwdev, "write_eeprom: success\n");
return 0;
}
static int fwe_run(struct fmc_device *fmc, const struct firmware *fw, char *s)
{
char *last4 = s + strlen(s) - 4;
int err;
if (!strcmp(last4,".bin"))
return fwe_run_bin(fmc, fw);
if (!strcmp(last4,".tlv")) {
err = fwe_run_tlv(fmc, fw, 0);
if (!err)
err = fwe_run_tlv(fmc, fw, 1);
return err;
}
dev_err(fmc->hwdev, "invalid file name \"%s\"\n", s);
return -EINVAL;
}
/*
* Programming is done at probe time. Morever, only those listed with
* busid= are programmed.
* card is probed for, only one is programmed. Unfortunately, it's
* difficult to know in advance when probing the first card if others
* are there.
*/
int fwe_probe(struct fmc_device *fmc)
{
int err, index;
const struct firmware *fw;
struct device *dev = fmc->hwdev;
char *s;
if (!fwe_drv.busid_n) {
dev_err(dev, "%s: no busid passed, refusing all cards\n",
KBUILD_MODNAME);
return -ENODEV;
}
index = fmc->op->validate(fmc, &fwe_drv);
s = fwe_file[index];
if (!s) {
dev_err(dev, "%s: no filename given: not programming\n",
KBUILD_MODNAME);
return -ENOENT;
}
err = request_firmware(&fw, s, dev);
if (err < 0) {
dev_err(dev, "request firmware \"%s\": error %i\n", s, err);
return err;
}
fwe_run(fmc, fw, s);
release_firmware(fw);
return 0;
}
int fwe_remove(struct fmc_device *fmc)
{
return 0;
}
static struct fmc_driver fwe_drv = {
.version = FMC_VERSION,
.driver.name = KBUILD_MODNAME,
.probe = fwe_probe,
.remove = fwe_remove,
/* no table, as the current match just matches everything */
};
static int fwe_init(void)
{
int ret;
ret = fmc_driver_register(&fwe_drv);
return ret;
}
static void fwe_exit(void)
{
fmc_driver_unregister(&fwe_drv);
}
module_init(fwe_init);
module_exit(fwe_exit);
MODULE_LICENSE("GPL");
/*
* This file is separate from sdb.h, because I want that one to remain
* unchanged (as far as possible) from the official sdb distribution
*
* This file and associated functionality are a playground for me to
* understand stuff which will later be implemented in more generic places.
*/
#include <linux/sdb.h>
/* This is the union of all currently defined types */
union sdb_record {
struct sdb_interconnect ic;
struct sdb_device dev;
struct sdb_bridge bridge;
struct sdb_integration integr;
struct sdb_empty empty;
};
struct fmc_device;
/* Every sdb table is turned into this structure */
struct sdb_array {
int len;
int level;
unsigned long baseaddr;
struct fmc_device *fmc; /* the device that hosts it */
struct sdb_array *parent; /* NULL at root */
union sdb_record *record; /* copies of the struct */
struct sdb_array **subtree; /* only valid for bridge items */
};
extern int fmc_scan_sdb_tree(struct fmc_device *fmc, unsigned long address);
extern void fmc_show_sdb_tree(struct fmc_device *fmc);
extern signed long fmc_find_sdb_device(struct sdb_array *tree, uint64_t vendor,
uint32_t device, unsigned long *sz);
extern int fmc_free_sdb_tree(struct fmc_device *fmc);
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#ifndef __LINUX_FMC_H__
#define __LINUX_FMC_H__
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
struct fmc_device;
struct fmc_driver;
/*
* This bus abstraction is developed separately from drivers, so we need
* to check the version of the data structures we receive.
*/
#define FMC_MAJOR 1
#define FMC_MINOR 0
#define FMC_VERSION ((FMC_MAJOR << 16) | FMC_MINOR)
#define __FMC_MAJOR(x) ((x) >> 16)
#define __FMC_MINOR(x) ((x) & 0xffff)
struct fmc_device_id {
/* FIXME: the device ID must be defined according to eeprom contents */
uint64_t unique_id;
};
#define FMC_MAX_CARDS 16 /* That many with the same matching driver... */
/* The driver is a pretty simple thing */
struct fmc_driver {
unsigned long version;
struct device_driver driver;
int (*probe)(struct fmc_device *);
int (*remove)(struct fmc_device *);
const struct fmc_device_id *id_table;
/* What follows is for generic module parameters */
int busid_n;
int busid_val[FMC_MAX_CARDS];
int gw_n;
char *gw_val[FMC_MAX_CARDS];
};
#define to_fmc_driver(x) container_of((x), struct fmc_driver, driver)
/* These are the generic parameters, that drivers may instantiate */
#define FMC_PARAM_BUSID(_d) \
module_param_array_named(busid, _d.busid_val, int, &_d.busid_n, 0444)
#define FMC_PARAM_GATEWARE(_d) \
module_param_array_named(gateware, _d.gw_val, charp, &_d.gw_n, 0444)
/*
* Drivers may need to configure gpio pins in the carrier. To read input
* (a very uncommon opeation, and definitely not in the hot paths), just
* configure one gpio only and get 0 or 1 as retval of the config method
*/
struct fmc_gpio {
char *carrier_name; /* name or NULL for virtual pins */
int gpio;
int _gpio; /* internal use by the carrier */
int mode; /* GPIOF_DIR_OUT etc, from <linux/gpio.h> */
int irqmode; /* IRQF_TRIGGER_LOW and so on */
};
/* The numbering of gpio pins allows access to raw pins or virtual roles */
#define FMC_GPIO_RAW(x) (x) /* 4096 of them */
#define __FMC_GPIO_IS_RAW(x) ((x) < 0x1000)
#define FMC_GPIO_IRQ(x) ((x) + 0x1000) /* 256 of them */
#define FMC_GPIO_LED(x) ((x) + 0x1100) /* 256 of them */
#define FMC_GPIO_KEY(x) ((x) + 0x1200) /* 256 of them */
#define FMC_GPIO_TP(x) ((x) + 0x1300) /* 256 of them */
#define FMC_GPIO_USER(x) ((x) + 0x1400) /* 256 of them */
/* We may add SCL and SDA, or other roles if the need arises */
/*
* The operations are offered by each carrier and should make driver
* design completely independent of th carrier. Named GPIO pins may be
* the exception.
*/
struct fmc_operations {
uint32_t (*readl)(struct fmc_device *fmc, int offset);
void (*writel)(struct fmc_device *fmc, uint32_t value, int offset);
int (*validate)(struct fmc_device *fmc, struct fmc_driver *drv);
int (*reprogram)(struct fmc_device *f, struct fmc_driver *d, char *gw);
int (*irq_request)(struct fmc_device *fmc, irq_handler_t h,
char *name, int flags);
void (*irq_ack)(struct fmc_device *fmc);
int (*irq_free)(struct fmc_device *fmc);
int (*gpio_config)(struct fmc_device *fmc, struct fmc_gpio *gpio,
int ngpio);
int (*read_ee)(struct fmc_device *fmc, int pos, void *d, int l);
int (*write_ee)(struct fmc_device *fmc, int pos, const void *d, int l);
};
/* The device reports all information needed to access hw */
struct fmc_device {
unsigned long version;
unsigned long flags;
struct fmc_device_id id; /* for the match function */
struct fmc_operations *op; /* carrier-provided */
int irq; /* according to host bus. 0 == none */
int eeprom_len; /* Usually 8kB, may be less */
uint8_t *eeprom; /* Full contents or leading part */
char *carrier_name; /* "SPEC" or similar, for special use */
void *carrier_data; /* "struct spec *" or equivalent */
__iomem void *base; /* May be NULL (Etherbone) */
struct device dev; /* For Linux use */
struct device *hwdev; /* The underlying hardware device */
struct sdb_array *sdb;
void *mezzanine_data;
};
#define to_fmc_device(x) container_of((x), struct fmc_device, dev)
#define FMC_DEVICE_HAS_GOLDEN 1
#define FMC_DEVICE_HAS_CUSTOM 2
#define FMC_DEVICE_NO_MEZZANINE 4
/* If the carrier offers no readl/writel, use base address */
static inline uint32_t fmc_readl(struct fmc_device *fmc, int offset)
{
if (unlikely(fmc->op->readl))
return fmc->op->readl(fmc, offset);
return readl(fmc->base + offset);
}
static inline void fmc_writel(struct fmc_device *fmc, uint32_t val, int off)
{
if (unlikely(fmc->op->writel))
fmc->op->writel(fmc, val, off);
else
writel(val, fmc->base + off);
}
/* pci-like naming */
static inline void *fmc_get_drvdata(struct fmc_device *fmc)
{
return dev_get_drvdata(&fmc->dev);
}
static inline void fmc_set_drvdata(struct fmc_device *fmc, void *data)
{
dev_set_drvdata(&fmc->dev, data);
}
/* The 4 access points */
extern int fmc_driver_register(struct fmc_driver *drv);
extern void fmc_driver_unregister(struct fmc_driver *drv);
extern int fmc_device_register(struct fmc_device *tdev);
extern void fmc_device_unregister(struct fmc_device *tdev);
#endif /* __LINUX_FMC_H__ */
/*
* This is version 1.0 of sdb.h, as included the specification v1.0
*/
#ifndef __SDB_H__
#define __SDB_H__
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif
/*
* All structures are 64 bytes long and are expected
* to live in an array, one for each interconnect.
* Most fields of the structures are shared among the
* various types, and most-specific fields are at the
* beginning (for alignment reasons, and to keep the
* magic number at the head of the interconnect record
*/
/* Product, 40 bytes at offset 24, 8-byte alignmed
*
* device_id is vendor-assigned; version is device-specific,
* date is hex (e.g 0x20120501), name is UTF-8, blank-filled
* and not terminated with a 0 byte.
*/
struct sdb_product {
uint64_t vendor_id; /* 0x18..0x1f */
uint32_t device_id; /* 0x20..0x23 */
uint32_t version; /* 0x24..0x27 */
uint32_t date; /* 0x28..0x2b */
uint8_t name[19]; /* 0x2c..0x3e */
uint8_t record_type; /* 0x3f */
};
/*
* Component, 56 bytes at offset 8, 8-byte aligned
*
* The address range is first to last, inclusive
* (for example 0x100000 - 0x10ffff)
*/
struct sdb_component {
uint64_t addr_first; /* 0x08..0x0f */
uint64_t addr_last; /* 0x10..0x17 */
struct sdb_product product; /* 0x18..0x3f */
};
/* Type of the SDB record */
enum sdb_record_type {
sdb_type_interconnect = 0x00,
sdb_type_device = 0x01,
sdb_type_bridge = 0x02,
sdb_type_integration = 0x80,
sdb_type_empty = 0xFF,
};
/* Type 0: interconnect (first of the array)
*
* sdb_records is the length of the table including this first
* record, version is 1. The bus type is enumerated later.
*/
#define SDB_MAGIC 0x5344422d /* "SDB-" */
struct sdb_interconnect {
uint32_t sdb_magic; /* 0x00-0x03 */
uint16_t sdb_records; /* 0x04-0x05 */
uint8_t sdb_version; /* 0x06 */
uint8_t sdb_bus_type; /* 0x07 */
struct sdb_component sdb_component; /* 0x08-0x3f */
};
/* Type 1: device
*
* class is 0 for "custom device", other values are
* to be standardized; ABI version is for the driver,
* bus-specific bits are defined by each bus (see below)
*/
struct sdb_device {
uint16_t abi_class; /* 0x00-0x01 */
uint8_t abi_ver_major; /* 0x02 */
uint8_t abi_ver_minor; /* 0x03 */
uint32_t bus_specific; /* 0x04-0x07 */
struct sdb_component sdb_component; /* 0x08-0x3f */
};
/* Type 2: bridge
*
* child is the address of the nested SDB table
*/
struct sdb_bridge {
uint64_t sdb_child; /* 0x00-0x07 */
struct sdb_component sdb_component; /* 0x08-0x3f */
};
/* Type 0x80: integration
*
* all types with bit 7 set are meta-information, so
* software can ignore the types it doesn't know. Here we
* just provide product information for an aggregate device
*/
struct sdb_integration {
uint8_t reserved[24]; /* 0x00-0x17 */
struct sdb_product product; /* 0x08-0x3f */
};
/* Type 0xff: empty
*
* this allows keeping empty slots during development,
* so they can be filled later with miminal efforts and
* no misleading description is ever shipped -- hopefully.
* It can also be used to pad a table to a desired length.
*/
struct sdb_empty {
uint8_t reserved[63]; /* 0x00-0x3e */
uint8_t record_type; /* 0x3f */
};
/* The type of bus, for bus-specific flags (currently only Wishbone) */
enum sdb_bus_type {
sdb_wishbone = 0x00
};
#define SDB_WB_WIDTH_MASK 0x0f
#define SDB_WB_ACCESS8 0x01
#define SDB_WB_ACCESS16 0x02
#define SDB_WB_ACCESS32 0x04
#define SDB_WB_ACCESS64 0x08
#define SDB_WB_LITTLE_ENDIAN 0x80
#endif /* __SDB_H__ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment