Commit 2f2a8e4e authored by Federico Vaga's avatar Federico Vaga

Merge remote-tracking branch 'sw/develop' into develop

parents ea2e89f3 f7923c7d
......@@ -76,3 +76,33 @@ doc/manual/fmcadc100m14b4cha_gateware_manual.txt
*.aux
*.log
*.toc
*~
.*cmd
.*ersions
*.o
*.ko
*.order
*.symvers
*.mod.c
*.mod
*.o.d
*.tmp
*.lof
*.bib
*.bbl
*.blg
*.out
*.a
\#*\#
*.sh
Makefile.specific
# eclipse files
.pydevproject
.cproject
.project
.settings
TAGS*
*.unsigned
GTAGS
GPATH
GRTAGS
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
build/
\ No newline at end of file
TOP_DIR ?= ..
DRIVER_NAME := fmc-adc-100m14b4ch
VERSION := $(shell git describe --abbrev=0)
DIR_NAME := $(DRIVER_NAME)-$(VERSION)
KEEP_TEMP ?= n
BUILD ?= $(abspath build)
BUILD_DKMS := $(BUILD)/dkms
BUILD_DKMSSOURCE := $(BUILD_DKMS)/source
BUILD_DKMSTREE := $(BUILD_DKMS)/tree
DKMS_OPT := --dkmstree $(BUILD_DKMSTREE) -m $(DRIVER_NAME)/$(VERSION)
all: kernel
kernel: dkms-tar dkms-rpm
dkms-tree:
@mkdir -p $(BUILD_DKMSSOURCE)
@mkdir -p $(BUILD_DKMSTREE)
dkms-src: dkms-tree
$(eval $@_dir := $(BUILD_DKMSSOURCE)/$(DRIVER_NAME)-$(VERSION))
$(eval $@_src := $(shell git ls-tree -r --name-only HEAD $(TOP_DIR)/kernel/))
@mkdir -p $($@_dir)
@cp $(TOP_DIR)/distribution/dkms.conf $($@_dir)
@cp $(TOP_DIR)/distribution/fmc-adc-100m14b4ch-dkms-mkrpm.spec $($@_dir)
@cp $($@_src) $($@_dir)
@cp $(TOP_DIR)/LICENSES/GPL-2.0-or-later.txt $($@_dir)/LICENSE
@sed -r -i -e "s/^GIT_VERSION\s=\s.*/GIT_VERSION = $(VERSION)/" $($@_dir)/Makefile
@sed -r -i -e "s/@PKGVER@/$(VERSION)/g" -e "s/@PKGNAME@/$(DRIVER_NAME)/g" $($@_dir)/dkms.conf
dkms-add: dkms-src
@dkms add $(DKMS_OPT) --sourcetree $(BUILD_DKMSSOURCE)
dkms-tar: dkms-add
@dkms mktarball $(DKMS_OPT) --source-only
dkms-rpm: dkms-add
@dkms mkrpm $(DKMS_OPT) --source-only
clean:
@rm -rf $(BUILD)
.PHONY: dkmstree dkms-add kernel-dkms-tar
PACKAGE_NAME="@PKGNAME@"
PACKAGE_VERSION="@PKGVER@"
CLEAN="make KVERSION=$kernelver DKMSTREE=$dkms_tree DKMS=1 clean"
MAKE[0]="make KVERSION=$kernelver DKMSTREE=$dkms_tree DKMS=1"
BUILT_MODULE_NAME[0]="@PKGNAME@"
DEST_MODULE_LOCATION[0]="/updates"
AUTOINSTALL="yes"
BUILD_DEPENDS[0]="zio"
\ No newline at end of file
# Mainline copied from the template, added requirements
%{?!module_name: %{error: You did not specify a module name (%%module_name)}}
%{?!version: %{error: You did not specify a module version (%%version)}}
%{?!kernel_versions: %{error: You did not specify kernel versions (%%kernel_version)}}
%{?!packager: %define packager DKMS <dkms-devel@lists.us.dell.com>}
%{?!license: %define license Unknown}
%{?!_dkmsdir: %define _dkmsdir /var/lib/dkms}
%{?!_srcdir: %define _srcdir %_prefix/src}
%{?!_datarootdir: %define _datarootdir %{_datadir}}
Summary: %{module_name} %{version} dkms package
Name: %{module_name}
Version: %{version}
License: %license
Release: 1dkms
BuildArch: noarch
Group: System/Kernel
Requires: dkms >= 1.95, zio >= 1.4
BuildRequires: dkms
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root/
%description
Kernel modules for %{module_name} %{version} in a DKMS wrapper.
%prep
if [ "%mktarball_line" != "none" ]; then
/usr/sbin/dkms mktarball -m %module_name -v %version %mktarball_line --archive `basename %{module_name}-%{version}.dkms.tar.gz`
cp -af %{_dkmsdir}/%{module_name}/%{version}/tarball/`basename %{module_name}-%{version}.dkms.tar.gz` %{module_name}-%{version}.dkms.tar.gz
fi
%install
if [ "$RPM_BUILD_ROOT" != "/" ]; then
rm -rf $RPM_BUILD_ROOT
fi
mkdir -p $RPM_BUILD_ROOT/%{_srcdir}
mkdir -p $RPM_BUILD_ROOT/%{_datarootdir}/%{module_name}
if [ -d %{_sourcedir}/%{module_name}-%{version} ]; then
cp -Lpr %{_sourcedir}/%{module_name}-%{version} $RPM_BUILD_ROOT/%{_srcdir}
fi
if [ -f %{module_name}-%{version}.dkms.tar.gz ]; then
install -m 644 %{module_name}-%{version}.dkms.tar.gz $RPM_BUILD_ROOT/%{_datarootdir}/%{module_name}
fi
if [ -f %{_sourcedir}/common.postinst ]; then
install -m 755 %{_sourcedir}/common.postinst $RPM_BUILD_ROOT/%{_datarootdir}/%{module_name}/postinst
fi
%clean
if [ "$RPM_BUILD_ROOT" != "/" ]; then
rm -rf $RPM_BUILD_ROOT
fi
%post
for POSTINST in %{_prefix}/lib/dkms/common.postinst %{_datarootdir}/%{module_name}/postinst; do
if [ -f $POSTINST ]; then
$POSTINST %{module_name} %{version} %{_datarootdir}/%{module_name}
exit $?
fi
echo "WARNING: $POSTINST does not exist."
done
echo -e "ERROR: DKMS version is too old and %{module_name} was not"
echo -e "built with legacy DKMS support."
echo -e "You must either rebuild %{module_name} with legacy postinst"
echo -e "support or upgrade DKMS to a more current version."
exit 1
%preun
echo -e
echo -e "Uninstall of %{module_name} module (version %{version}) beginning:"
dkms remove -m %{module_name} -v %{version} --all --rpm_safe_upgrade
exit 0
%files
%defattr(-,root,root)
%{_srcdir}
%{_datarootdir}/%{module_name}/
%changelog
* %(date "+%a %b %d %Y") %packager %{version}-%{release}
- Automatic build by DKMS
*~
*.aux
*.cp
*.cps
*.fn
*.fns
*.html
*.info
*.ky
*.log
/*.pdf
*.pg
*.texi
*.toc
*.tp
/*.txt
*.vr
_build
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXPROJ = FMCADC100M14bit4Channel
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# FMC ADC 100M 14 bit 4 Channel documentation build configuration file, created by
# sphinx-quickstart on Thu Jan 25 09:54:50 2018.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.autodoc',
'sphinx.ext.todo',
'sphinx.ext.coverage',
'sphinx.ext.graphviz',]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'FMC ADC 100M 14 bit 4 Channel'
copyright = '2018, Federico Vaga <federico.vaga@cern.ch>'
author = 'Federico Vaga <federico.vaga@cern.ch>'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '5.0'
# The full version, including alpha/beta/rc tags.
release = '5.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
#html_theme = 'alabaster'
html_theme = "sphinx_rtd_theme"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# This is required for the alabaster theme
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
html_sidebars = {
'**': [
'relations.html', # needs 'show_related': True theme option to display
'searchbox.html',
]
}
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'FMCADC100M14bit4Channeldoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'FMCADC100M14bit4Channel.tex', 'FMC ADC 100M 14 bit 4 Channel Documentation',
'Federico Vaga \\textless{}federico.vaga@cern.ch\\textgreater{}', 'manual'),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'fmcadc100m14bit4channel', 'FMC ADC 100M 14 bit 4 Channel Documentation',
[author], 1)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'FMCADC100M14bit4Channel', 'FMC ADC 100M 14 bit 4 Channel Documentation',
author, 'FMCADC100M14bit4Channel', 'One line description of project.',
'Miscellaneous'),
]
# -- Options for Epub output ----------------------------------------------
# Bibliographic Dublin Core info.
epub_title = project
epub_author = author
epub_publisher = author
epub_copyright = copyright
# The unique identifier of the text. This can be a ISBN number
# or the project homepage.
#
# epub_identifier = ''
# A unique identification for the text.
#
# epub_uid = ''
# A list of files that should not be packed into the epub file.
epub_exclude_files = ['search.html']
smart_quotes = False
Two snapshots of fa-config-if
This source diff could not be displayed because it is too large. You can view the blob instead.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
<!--Created by yFiles for Java 2.10-->
<key for="graphml" id="d0" yfiles.type="resources"/>
<key for="port" id="d1" yfiles.type="portgraphics"/>
<key for="port" id="d2" yfiles.type="portgeometry"/>
<key for="port" id="d3" yfiles.type="portuserdata"/>
<key attr.name="url" attr.type="string" for="node" id="d4"/>
<key attr.name="description" attr.type="string" for="node" id="d5"/>
<key for="node" id="d6" yfiles.type="nodegraphics"/>
<key attr.name="Description" attr.type="string" for="graph" id="d7"/>
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
<graph edgedefault="directed" id="G">
<data key="d7"/>
<node id="n0" yfiles.foldertype="group">
<data key="d4"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="336.0847775000001" width="418.6536000000003" x="484.98495999999955" y="98.16290250000009"/>
<y:Fill color="#DCF7F7" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="418.6536000000003" x="0.0" y="0.0">ADC state machine</y:NodeLabel>
<y:Shape type="rectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="6" bottomF="6.0" left="6" leftF="6.25" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="332.98495999999955" y="181.41986249999957"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="65.201171875" x="-7.6005859375" y="0.0">Folder 1</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n0:">
<node id="n0::n0">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="45.319279999999935" x="667.6807200000001" y="180.75231999999983"/>
<y:Fill color="#99CCFF" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="3.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="34.26953125" x="5.524874374999968" y="6.015625">IDLE<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n1">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="40.0" width="106.63855999999987" x="782.0" y="233.6238400000001"/>
<y:Fill color="#99CCFF" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="3.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="31.9375" modelName="custom" textColor="#000000" visible="true" width="78.630859375" x="14.003850312499935" y="4.03125">pre-trigger
count<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n2">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="40.0" width="106.63855999999987" x="782.0" y="373.2476800000002"/>
<y:Fill color="#99CCFF" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="3.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="84.21484375" x="11.211858124999935" y="11.015625">wait trigger<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n3">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="40.0" width="106.63855999999987" x="509.9156799999996" y="373.2476800000002"/>
<y:Fill color="#99CCFF" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="3.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="31.9375" modelName="custom" textColor="#000000" visible="true" width="62.482421875" x="22.078069062499935" y="4.03125">wait end
of shot<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n4">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="40.0" width="106.63855999999987" x="626.9994062499998" y="233.6238400000001"/>
<y:Fill color="#99CCFF" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="3.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="31.9375" modelName="custom" textColor="#000000" visible="true" width="83.166015625" x="11.736272187499935" y="4.03125">decrement
shots count<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n5">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="20.0" width="78.0" x="667.4445724999997" y="383.2476800000002"/>
<y:Fill color="#FFCC0000" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="68.212890625" x="4.8935546875" y="1.015625">trigger fire<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n6">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="114.0" x="506.23495999999955" y="180.75231999999983"/>
<y:Fill color="#FFCC0000" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" modelName="custom" textColor="#000000" visible="true" width="108.53125" x="2.734375" y="-0.96875">post-trigger done
&amp; shots done<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n7">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="20.0" width="78.0" x="796.3192799999999" y="313.43576000000013"/>
<y:Fill color="#FFCC0000" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="68.787109375" x="4.6064453125" y="1.015625">pre-trigger<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n8">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="20.0" width="45.319279999999935" x="812.65964" y="185.75231999999983"/>
<y:Fill color="#FFCC0000" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="31.94921875" x="6.685030624999968" y="1.015625">start<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n9">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="20.0" width="30.0" x="628.9578399999998" y="134.6238400000001"/>
<y:Fill color="#FFCC0000" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="29.916015625" x="0.0419921875" y="1.015625">stop<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n10">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="106.63855999999987" x="626.9994062499998" y="296.49536000000035"/>
<y:Fill color="#FFCC0000" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" modelName="custom" textColor="#000000" visible="true" width="108.53125" x="-0.9463450000000648" y="-0.96875">post-trigger done
&amp; shots not done<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
</graph>
</node>
<node id="n1">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="163.0" x="624.9445724999997" y="464.2476800000002"/>
<y:Fill color="#FF9900" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="106.9609375" x="28.01953125" y="6.015625">zfat_irq_trg_fire()<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n2">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="163.0" x="253.1656799999996" y="180.75231999999983"/>
<y:Fill color="#FF9900" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="114.00390625" x="24.498046875" y="6.015625">zfat_irq_acq_end()<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n3">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="51.0" width="106.63855999999987" x="281.3463999999997" y="88.62384000000009"/>
<y:Fill color="#FFCC00" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="74.125" x="16.256779999999935" y="16.515625">FSM is idle?<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="diamond"/>
</y:ShapeNode>
</data>
</node>
<node id="n4">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="212.0" x="478.6670327129068" y="49.12384000000009"/>
<y:Fill color="#FF9900" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="96.103515625" x="57.9482421875" y="6.015625">zfad_start_dma<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n5">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="78.0" x="295.6656799999996" y="28.623840000000087"/>
<y:Fill color="#FFCC0000" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="54.513671875" x="11.7431640625" y="6.015625">wait 2us<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<edge id="e0" source="n0::n6" target="n2">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-1.7053025658242404E-13" sy="0.0" tx="62.80756116053874" ty="0.0"/>
<y:LineStyle color="#000000" type="dashed" width="2.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="six_pos" modelPosition="thead" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="56.822265625" x="-79.92321770507851" y="-19.968749335937673">interrupt<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="0.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e1" source="n0::n5" target="n1">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="2.2737367544323206E-13" sy="-5.6843418860808015E-14" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="dashed" width="2.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="six_pos" modelPosition="stail" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="56.822265625" x="1.9999924218749356" y="10.088866523437673">interrupt<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="0.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e0" source="n0::n0" target="n0::n8">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="3.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e1" source="n0::n1" target="n0::n7">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="3.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e2" source="n0::n2" target="n0::n5">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="3.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e3" source="n0::n3" target="n0::n6">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="3.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e4" source="n0::n3" target="n0::n10">
<data key="d10">
<y:ArcEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="599.9306030273438" y="321.0838317871094"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="3.0"/>
<y:Arrows source="none" target="standard"/>
<y:Arc height="38.15989303588867" ratio="1.0688996315002441" type="fixedRatio"/>
</y:ArcEdge>
</data>
</edge>
<edge id="n0::e5" source="n0::n4" target="n0::n1">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="3.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e6" source="n0::n5" target="n0::n3">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="3.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e7" source="n0::n6" target="n0::n0">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="3.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e8" source="n0::n7" target="n0::n2">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="3.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e9" source="n0::n8" target="n0::n1">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="3.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e10" source="n0::n9" target="n0::n0">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="690.34036" y="144.6238400000001"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="3.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e11" source="n0::n10" target="n0::n4">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="3.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e2" source="n2" target="n3">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e3" source="n3" target="n5">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="six_pos" modelPosition="stail" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="18.947265625" x="2.000000068358986" y="-23.976318691406163">no<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="0.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e4" source="n5" target="n3">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="253.5" y="43.62384000000009"/>
<y:Point x="253.5" y="114.12384000000009"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="96.091796875" x="-90.21157836914062" y="-45.96875033203118">only 5 times
error otherwise<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="1.0" segment="0"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="0.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e5" source="n3" target="n4">
<data key="d10">
<y:ArcEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="455.1663513183594" y="66.62355041503906"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="six_pos" modelPosition="head" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="24.736328125" x="54.31234999511696" y="-47.27544606210242">yes<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="0.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:Arc height="22.945877075195312" ratio="0.36000263690948486" type="fixedRatio"/>
</y:ArcEdge>
</data>
</edge>
</graph>
<data key="d0">
<y:Resources/>
</data>
</graphml>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
<!--Created by yFiles for Java 2.9-->
<key for="graphml" id="d0" yfiles.type="resources"/>
<key for="port" id="d1" yfiles.type="portgraphics"/>
<key for="port" id="d2" yfiles.type="portgeometry"/>
<key for="port" id="d3" yfiles.type="portuserdata"/>
<key attr.name="url" attr.type="string" for="node" id="d4"/>
<key attr.name="description" attr.type="string" for="node" id="d5"/>
<key for="node" id="d6" yfiles.type="nodegraphics"/>
<key attr.name="Description" attr.type="string" for="graph" id="d7"/>
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
<graph edgedefault="directed" id="G">
<data key="d7"/>
<node id="n0" yfiles.foldertype="group">
<data key="d4"/>
<data key="d6">
<y:TableNode configuration="YED_TABLE_NODE">
<y:Geometry height="416.0" width="366.0" x="322.0" y="137.4999999999999"/>
<y:Fill color="#ECF5FF" color2="#CCFFFF" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="21.45361328125" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="175.68701171875" x="95.156494140625" y="4.0">ZIO FMC ADC Hierarchy</y:NodeLabel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.962890625" modelName="custom" rotationAngle="270.0" textColor="#000000" visible="true" width="41.904296875" x="3.0" y="78.0478515625">level 1<y:LabelModel>
<y:RowNodeLabelModel offset="3.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:RowNodeLabelModelParameter horizontalPosition="0.0" id="row_0" inside="true"/>
</y:ModelParameter>
</y:NodeLabel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.962890625" modelName="custom" rotationAngle="270.0" textColor="#000000" visible="true" width="41.904296875" x="3.0" y="210.0478515625">level 2<y:LabelModel>
<y:RowNodeLabelModel offset="3.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:RowNodeLabelModelParameter horizontalPosition="0.0" id="row_1" inside="true"/>
</y:ModelParameter>
</y:NodeLabel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.962890625" modelName="custom" rotationAngle="270.0" textColor="#000000" visible="true" width="41.904296875" x="3.0" y="334.0478515625">level 3<y:LabelModel>
<y:RowNodeLabelModel offset="3.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:RowNodeLabelModelParameter horizontalPosition="0.0" id="row_2" inside="true"/>
</y:ModelParameter>
</y:NodeLabel>
<y:StyleProperties>
<y:Property class="java.awt.Color" name="yed.table.header.color.main" value="#d9eaff"/>
<y:Property class="java.awt.Color" name="yed.table.section.color" value="#7192b2"/>
<y:Property name="y.view.tabular.TableNodePainter.ALTERNATE_COLUMN_SELECTION_STYLE">
<y:SimpleStyle fillColor="#474A4380" lineColor="#000000" lineType="line" lineWidth="3.0"/>
</y:Property>
<y:Property class="java.lang.String" name="yed.table.lane.style" value="lane.style.rows"/>
<y:Property name="y.view.tabular.TableNodePainter.ALTERNATE_ROW_STYLE">
<y:SimpleStyle fillColor="#474A4340" lineColor="#000000" lineType="line" lineWidth="1.0"/>
</y:Property>
<y:Property name="y.view.tabular.TableNodePainter.ALTERNATE_ROW_SELECTION_STYLE">
<y:SimpleStyle fillColor="#474A4380" lineColor="#000000" lineType="line" lineWidth="3.0"/>
</y:Property>
<y:Property class="java.awt.Color" name="yed.table.lane.color.alternating" value="#ccd8e3"/>
<y:Property class="java.awt.Color" name="yed.table.header.color.alternating" value="#ccd8e3"/>
<y:Property name="y.view.tabular.TableNodePainter.ALTERNATE_COLUMN_STYLE">
<y:SimpleStyle fillColor="#474A4340" lineColor="#000000" lineType="line" lineWidth="1.0"/>
</y:Property>
<y:Property class="java.awt.Color" name="yed.table.lane.color.main" value="#d9eaff"/>
<y:Property class="java.lang.Double" name="yed.table.header.height" value="24.0"/>
</y:StyleProperties>
<y:State autoResize="true" closed="false" closedHeight="80.0" closedWidth="100.0"/>
<y:Insets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
<y:BorderInsets bottom="17" bottomF="16.999999999999886" left="25" leftF="25.0" right="24" rightF="23.5" top="35" topF="35.0"/>
<y:Table autoResizeTable="true" defaultColumnWidth="120.0" defaultMinimumColumnWidth="80.0" defaultMinimumRowHeight="50.0" defaultRowHeight="80.0">
<y:DefaultColumnInsets bottom="0.0" left="0.0" right="0.0" top="0.0"/>
<y:DefaultRowInsets bottom="0.0" left="24.0" right="0.0" top="0.0"/>
<y:Insets bottom="0.0" left="0.0" right="0.0" top="30.0"/>
<y:Columns>
<y:Column id="column_0" minimumWidth="80.0" width="342.0">
<y:Insets bottom="0.0" left="0.0" right="0.0" top="0.0"/>
</y:Column>
</y:Columns>
<y:Rows>
<y:Row height="138.0" id="row_0" minimumHeight="50.0">
<y:Insets bottom="0.0" left="24.0" right="0.0" top="0.0"/>
</y:Row>
<y:Row height="126.0" id="row_1" minimumHeight="50.0">
<y:Insets bottom="0.0" left="24.0" right="0.0" top="0.0"/>
</y:Row>
<y:Row height="122.0" id="row_2" minimumHeight="50.0">
<y:Insets bottom="0.0" left="24.0" right="0.0" top="0.0"/>
</y:Row>
</y:Rows>
</y:Table>
</y:TableNode>
</data>
<graph edgedefault="directed" id="n0:">
<node id="n0::n0">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="57.0" width="293.5" x="371.0" y="202.4999999999999"/>
<y:Fill color="#FF9900" transparent="false"/>
<y:BorderStyle color="#FF6600" type="line" width="3.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" borderDistance="0.0" fontFamily="Dialog" fontSize="13" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="19.12646484375" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="60.392578125" x="116.5537109375" y="18.936767578125">fmc-adc</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n1">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="49.0" width="131.0" x="451.5" y="343.4999999999999"/>
<y:Fill color="#FF9900" transparent="false"/>
<y:BorderStyle color="#FF6600" type="line" width="3.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" borderDistance="0.0" fontFamily="Dialog" fontSize="13" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="19.12646484375" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="39.73095703125" x="45.634521484375" y="14.936767578125">cset0</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n2">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="34.0" width="59.0" x="371.0" y="457.4999999999999"/>
<y:Fill color="#FADCAF" transparent="false"/>
<y:BorderStyle color="#FF6600" type="line" width="3.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" borderDistance="0.0" fontFamily="Dialog" fontSize="13" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="19.12646484375" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="44.21240234375" x="7.393798828125" y="7.436767578125">chan0</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n3">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="34.0" width="59.0" x="487.5" y="457.4999999999999"/>
<y:Fill color="#FADCAF" transparent="false"/>
<y:BorderStyle color="#FF6600" type="line" width="3.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" borderDistance="0.0" fontFamily="Dialog" fontSize="13" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="19.12646484375" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="44.21240234375" x="7.393798828125" y="7.436767578125">chan2</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n4">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="34.0" width="59.0" x="430.0" y="502.5"/>
<y:Fill color="#FADCAF" transparent="false"/>
<y:BorderStyle color="#FF6600" type="line" width="3.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" borderDistance="0.0" fontFamily="Dialog" fontSize="13" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="19.12646484375" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="44.21240234375" x="7.393798828125" y="7.436767578125">chan1</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n5">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="34.0" width="59.0" x="546.5" y="502.5"/>
<y:Fill color="#FADCAF" transparent="false"/>
<y:BorderStyle color="#FF6600" type="line" width="3.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" borderDistance="0.0" fontFamily="Dialog" fontSize="13" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="19.12646484375" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="44.21240234375" x="7.393798828125" y="7.436767578125">chan3</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n6">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="34.0" width="59.0" x="605.5" y="457.4999999999999"/>
<y:Fill color="#FADCAF" transparent="false"/>
<y:BorderStyle color="#FF6600" type="line" width="3.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" borderDistance="0.0" fontFamily="Dialog" fontSize="13" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="19.12646484375" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="39.8642578125" x="9.56787109375" y="7.436767578125">chani</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
</graph>
</node>
<edge id="n0::e0" source="n0::n0" target="n0::n1">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="2.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e1" source="n0::n1" target="n0::n2">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="2.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e2" source="n0::n1" target="n0::n3">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="2.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e3" source="n0::n1" target="n0::n4">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="2.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e4" source="n0::n1" target="n0::n5">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="2.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e5" source="n0::n1" target="n0::n6">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="2.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
</graph>
<data key="d0">
<y:Resources/>
</data>
</graphml>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
<!--Created by yFiles for Java 2.9-->
<key for="graphml" id="d0" yfiles.type="resources"/>
<key for="port" id="d1" yfiles.type="portgraphics"/>
<key for="port" id="d2" yfiles.type="portgeometry"/>
<key for="port" id="d3" yfiles.type="portuserdata"/>
<key attr.name="url" attr.type="string" for="node" id="d4"/>
<key attr.name="description" attr.type="string" for="node" id="d5"/>
<key for="node" id="d6" yfiles.type="nodegraphics"/>
<key attr.name="Description" attr.type="string" for="graph" id="d7"/>
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
<graph edgedefault="directed" id="G">
<data key="d7"/>
<node id="n0">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="53.0" x="263.0" y="212.0"/>
<y:Fill color="#FFFF99" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.962890625" modelName="custom" textColor="#000000" visible="true" width="39.255859375" x="6.8720703125" y="6.0185546875">chan1<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n1">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="53.0" x="316.0" y="212.0"/>
<y:Fill color="#CCFFCC" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.962890625" modelName="custom" textColor="#000000" visible="true" width="39.255859375" x="6.8720703125" y="6.0185546875">chan2<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n2">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="53.0" x="369.0" y="212.0"/>
<y:Fill color="#CCFFFF" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.962890625" modelName="custom" textColor="#000000" visible="true" width="39.255859375" x="6.8720703125" y="6.0185546875">chan3<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n3">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="53.0" x="422.0" y="212.0"/>
<y:Fill color="#99CCFF" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.962890625" modelName="custom" textColor="#000000" visible="true" width="39.255859375" x="6.8720703125" y="6.0185546875">chan4<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n4">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="85.0" x="178.0" y="212.0"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.962890625" modelName="custom" textColor="#000000" visible="true" width="79.650390625" x="2.6748046875" y="6.0185546875">0x00000000<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n5">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="53.0" x="263.0" y="242.0"/>
<y:Fill color="#FFFF99" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.962890625" modelName="custom" textColor="#000000" visible="true" width="39.255859375" x="6.8720703125" y="6.0185546875">chan1<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n6">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="53.0" x="316.0" y="242.0"/>
<y:Fill color="#CCFFCC" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.962890625" modelName="custom" textColor="#000000" visible="true" width="39.255859375" x="6.8720703125" y="6.0185546875">chan2<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n7">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="53.0" x="369.0" y="242.0"/>
<y:Fill color="#CCFFFF" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.962890625" modelName="custom" textColor="#000000" visible="true" width="39.255859375" x="6.8720703125" y="6.0185546875">chan3<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n8">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="53.0" x="422.0" y="242.0"/>
<y:Fill color="#99CCFF" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.962890625" modelName="custom" textColor="#000000" visible="true" width="39.255859375" x="6.8720703125" y="6.0185546875">chan4<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n9">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="85.0" x="178.0" y="242.0"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.962890625" modelName="custom" textColor="#000000" visible="true" width="79.650390625" x="2.6748046875" y="6.0185546875">0x00000008<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n10">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="297.0" x="178.0" y="272.0"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.962890625" modelName="custom" textColor="#000000" visible="true" width="60.953125" x="118.0234375" y="6.0185546875">...............<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
</graph>
<data key="d0">
<y:Resources/>
</data>
</graphml>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
<!--Created by yFiles for Java 2.10-->
<key for="graphml" id="d0" yfiles.type="resources"/>
<key for="port" id="d1" yfiles.type="portgraphics"/>
<key for="port" id="d2" yfiles.type="portgeometry"/>
<key for="port" id="d3" yfiles.type="portuserdata"/>
<key attr.name="url" attr.type="string" for="node" id="d4"/>
<key attr.name="description" attr.type="string" for="node" id="d5"/>
<key for="node" id="d6" yfiles.type="nodegraphics"/>
<key attr.name="Description" attr.type="string" for="graph" id="d7"/>
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
<graph edgedefault="directed" id="G">
<data key="d7"/>
<node id="n0" yfiles.foldertype="group">
<data key="d4"/>
<data key="d5"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="243.4609375" width="173.0" x="511.0" y="59.5390625"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#FFFF99" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="173.0" x="0.0" y="0.0">ADC Library</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="65.201171875" x="-7.6005859375" y="0.0">Folder 1</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n0:">
<node id="n0::n0">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="92.0" width="50.0" x="619.0" y="136.0"/>
<y:Fill color="#FFFF99" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="3.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" modelName="custom" textColor="#000000" visible="true" width="42.607421875" x="3.6962890625" y="30.03125">library
route<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n1">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="92.0" width="50.0" x="526.0" y="96.0"/>
<y:Fill color="#FFFF99" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="3.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" modelName="custom" textColor="#000000" visible="true" width="42.607421875" x="3.6962890625" y="30.03125">library
ZIO<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n2">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="92.0" width="50.0" x="526.0" y="196.0"/>
<y:Fill color="#FFFF99" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="3.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" modelName="custom" textColor="#000000" visible="true" width="42.607421875" x="3.6962890625" y="30.03125">library
others<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
</graph>
</node>
<node id="n1">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="92.0" width="68.0" x="712.0" y="136.0"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="3.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="55.486328125" x="6.2568359375" y="37.015625">program<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n2">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="92.0" width="50.0" x="433.0" y="96.0"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="3.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" modelName="custom" textColor="#000000" visible="true" width="39.302734375" x="5.3486328125" y="30.03125">ZIO
driver<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n3">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="92.0" width="50.0" x="433.0" y="196.0"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="3.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" modelName="custom" textColor="#000000" visible="true" width="39.302734375" x="5.3486328125" y="30.03125">Other
driver<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<edge id="e0" source="n1" target="n0::n0">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="3.0"/>
<y:Arrows source="standard" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e0" source="n0::n0" target="n0::n1">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="3.0"/>
<y:Arrows source="standard" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e1" source="n0::n0" target="n0::n2">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="3.0"/>
<y:Arrows source="standard" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e1" source="n0::n2" target="n3">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="3.0"/>
<y:Arrows source="standard" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e2" source="n0::n1" target="n2">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="3.0"/>
<y:Arrows source="standard" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
</graph>
<data key="d0">
<y:Resources/>
</data>
</graphml>
..
SPDX-License-Identifier: CC-BY-SA-4.0
SPDX-FileCopyrightText: 2020 CERN
Welcome to FMC ADC 100M 14 bit 4 Channel's documentation!
=========================================================
This is the user manual of the driver for the `FMC ADC 100M 14b 4cha`_
board developed on the `Open Hardware Repository`_. FMC is the form
factor of the card, ADC is its role, 100M means it can acquire
100Msample per second, 14b is the numbers of meaningful bits and 4cha
states it has 4 input channels (plus a trigger input).
.. _`Open Hardware Repository`: http://www.ohwr.org/
.. _`FMC ADC 100M 14b 4cha`: http://www.ohwr.org/projects/fmc-adc-100m14b4cha
.. toctree::
:maxdepth: 2
:caption: Contents:
software/index
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
..
SPDX-License-Identifier: CC-BY-SA-4.0
SPDX-FileCopyrightText: 2020 CERN
======
Driver
======
Driver Features
===============
Requirements
============
The fmcadc100m14b4ch device driver has been developed and tested on Linux
3.10. Other Linux versions might work as well but it is not guaranteed.
This driver depends on the `zio`_ framework and `fmc`_ library; we
developed and tested against version `zio`_ 1.4 and `fmc`_ 1.0.
The FPGA address space must be visible on the host system. This requires
a driver for the FPGA carrier that exports the FPGA address space to the
host. As of today we support `SPEC`_ and `SVEC`_.
Compile And Install
====================
The compile and install the fmcadc100m14b4ch device driver you need
first to export the path to its direct dependencies, and then you
execute ``make``. This driver depends on the `zio`_ framework and `fmc`_ library; on a VME system it depends also on the VME bridge driver from CERN BE-CO.
::
$ cd /path/to/fmc-adc-100m14b4cha-sw/kernel
$ export LINUX=/path/to/linux/sources
$ export ZIO=/path/to/zio
$ export FMC=/path/to/fmc-sw
$ export VMEBUS=/path/to/vmebridge
$ make
$ make install
.. note::
Since version v5.0.0 the fmcadc100m14b4ch device driver does not
depend anymore on `fmc-bus`_ subsystem, instead it uses a new
`fmc`_ library
The building process generates 3 Linux modules:
*kernel/fmc-adc-100m14b4ch.ko*, *kernel/fmc-adc-100m14b4ch-spec.ko*, and
*kernel/fmc-adc-100m14b4ch-svec.ko*.
Top Level Driver
================
The fmcadc100m14b4ch is a generic driver for an FPGA device that could
be instanciated on a number of FMC carriers. For each carrier we write
a little Linux module which acts as a top level driver (like the MFD
drivers in the Linux kernel). In these modules there is the knowledge
about the virtual memory range, the IRQ lines, and the DMA engine to
be used.
The top level driver is a platform driver that matches a string
containing the application identifier. The carrier driver builds this
identification string from the device ID embedded into the FPGA
(https://ohwr.org/project/fpga-dev-id).
Module Parameters
=================
The driver accepts a few load-time parameters for configuration. You can
pass them to insmod directly, or write them in ``/etc/modules.conf`` or
the proper file in ``/etc/modutils/``.
The following parameters are used:
enable_test_data=[0, 1]
This is for testing purpose. When set to 1, this option enables the
testing data, so the ADC doesn't store samples, but fills memory with
sequential numbers. The 64 bit data vector is filled with sequential
values from a free-running 25 bit counter: channel 0 sweeps the full
range, channel 1 goes from 0 to 511, other channel always report 0.
Trigger detection is unaffected by use of test data.
fa_calib_period_s=NUMBER
The ADC periodically adjusts the calibration data based on the
current temperature. You can use this module parameter to pass
your period in seconds. By default the value is 0 (meaning
feature disabled).
.. warning::
The periodical calibration is by default disabled because we were
not able to perform a complete validation. Use it at your own risk.
.. _zio: https://www.ohwr.org/project/zio
.. _fmc: https://www.ohwr.org/project/fmc-sw
.. _`fmc-bus`: http://www.ohwr.org/projects/fmc-bus
.. _`SVEC`: https://www.ohwr.org/projects/svec
.. _`SPEC`: https://www.ohwr.org/projects/spec
Device Abstraction
==================
This driver is based on the ZIO framework and the fmc-bus. It supports
initial setup of the board; it allows users to manually configure the
board, to start and stop acquisitions, to force trigger, to read
acquisition time-stamps and to read acquired samples.
The driver is designed as a ZIO driver. ZIO is a framework for
input/output hosted on http://www.ohwr.org/projects/zio.
ZIO devices are organized as csets (channel sets), and each of them
includes channels. This device offers one cset and four channels.
However, the device can only stores interleaved data for all four
channels.
The current approach to this is defining 5 channels: channels 0 to 3 are
the actual input connectors, and their software counterpart is used to
configure the channels; the last channel is called *i*, and is the
interleave channel where data is retrieved.
The Overall Device
''''''''''''''''''
As said, the device has 1 cset with 4+1 channels. Channels from 0 to 3
represent the physical channels 1 to 4. The 5th channel *chani* represent
a virtual channel created automatically by the ZIO framework; this
channel represent the interleave acquisition on the cset.
.. graphviz::
:align: center
graph layers {
node [shape=box];
adc [label="FMC ADC 100M4B4CHA"];
adc -- cset0;
cset0 -- chan0;
cset0 -- chan1;
cset0 -- chan2;
cset0 -- chan3;
cset0 -- chani;
}
The ADC registers can be accessed in the proper sysfs directory:::
cd /sys/bus/zio/devices/adc-100m14b-${ID}.
The overall device (*adc-100m14b*) provides the following attributes:
calibration_data
It is a binary attribute which allows the user to change the runt-time
calibration data (the EEPROM will not be touched). The ``fau-calibration``
tool can be used to read write calibration data.
To be consistent, this binary interface expects **only** little endian
values because this is the endianess used to store calibration data for
this device.
temperature
It shows the current temperature
The Channel Set
'''''''''''''''
The ADC has 1 Channel Set named ``cset0``. Its attributes are used to
control the ADC state machine, the channel parameters and so on.
Some attributes are channel-specific, and one may thing they should live
at channel-level. Unfortunately, ZIO currently lacks the mechanisms to
convey channel attributes in the meta-data associated with an
interleaved acquisition (where several channels coexist), and for this
reason we chose to put them all at cset level. This may change in future
releases, but the library implementation will follow, so there will be
no effect on API users.
The description of attributes that follows is mainly useful for the
shell user, to diagnose the system and hack around with parameters.
Channel-specific Cset Attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The cset includes three attributes for each channel, as follows:
chN-50ohm-term
The read-write attribute accepts values 0 or 1. By writing 1, you
turn on the termination resistor. Default is 0.
chN-offset
The user offset is an integer value in the range [-5000,5000], and
it represents millivolts. The offset represents the center-scale
of conversion for the input channel. Internally, a DAC is used to
generate the requested voltage, which is then subtracted from the
input signal. DAC values are corrected according to the
calibration values retrieved from the FMC EEPROM. For this reason,
the offset may saturate at values less than +/- 5V.
chN-offset-zero
The necessary offset to to bring the signal to 0.
chN-vref
The "voltage reference" used for conversion. This attribute may be
renamed to "range" in the future (again, with no effect on API
users). Changing the range does not reset the user offset, which
is automatically adjusted according to the new calibration values.
The attribute accepts three values: 35 represents the 100mV range
(-50mV to +50mV); 17 represents 1V range; 69 represents 10V range
(-5V to +5V); 0 detaches the input connector from the ADC. The
numbers used here derive from hardware values, and the attributes
refuses any other value.
cnN-saturation
The user saturation level in the range [0, 32767]. Users can use
this value to configure their own saturation level. The hardware
applies this value symmetrically on the negative side. By default
is set at the maximum value.
Generic Cset Attributes
~~~~~~~~~~~~~~~~~~~~~~~
This section lists the attributes that are defined by this driver;
ZIO-wide attributes (current_buffer, enable and so on) are not
described.
fsm-auto-start
This attribute can be set to 1 or 0. It is 0 by default. If set
to 1, the acquisition state-machine is automatically restarted
after the previous run is complete. Thus, for example, a card
configured for external trigger, after the first acquisition will
continue acquiring and storing blocks to the ZIO buffer every time a
new trigger event is detected. Applications can read such blocks
from the char device.
fsm-command
Write-only: start (1) or stop (2) the state machine. The values
used reflects the hardware registers. Stopping the state machine
aborts any ongoing acquisition. Starting the state machine is
required in order to run an acquisition (the library manages this
internally). The green LED ACQ on the front panel reflect the fact
that the state machine has started. Restarting a running state
machine is equivalent to first stopping it.
fsm-state
Read-only current state of the FSM. Useful for diagnostics in
strange situation. Please refer to the firmware manual (or to
source code) about the various states.
resolution-bits
This read-only attribute returns 14, the number of valid bits in
the ADC data stream.
undersample
The ADC always acquires at 100MSamples/s and this value cannot be
changed (it actually can, but it is not currently supported nor
even tested). If you need less samples you can tell the card to
decimate (or under-sample) the data stream. The attribute accepts
an integer value, 1 to 65536; it means to pick one sample every
that many. Thus, but writing 100 you get a 1Ms data stream, and by
writing 2 you get a 50Ms data stream.
sample-frequency
This read-only attributes returns the measured sampling frequency
sample-counter
Number of samples acquired on each channel during the last
acquisition. If queried while the acquisition is running you will
get the number of samples acquired till that moment. It can be
used to evaluate the progress of a slow acquisition.
max-sample-mshot
Maximum number of samples that can be stored in the FPGA memory in
multi-shot mode
output-randomizer
It allows to enable or disable the *Data Output Randomizer* in the ADC
chip.
Timestamp Attributes
~~~~~~~~~~~~~~~~~~~~
The ADC mark with a timestamp all these events: state machine start,
state machine stop and acquisition end. The device split each timestamp
in 3 attributes named: second (s), ticks (t) and bins (b).
Seconds represents (by default) the number of second since the epoch;
ticks is the number of clocks at 125Mhz, the value is between 0 and
125000000 and it increments seconds when it overflow. At the moment, the
bins register is unused.
For example, to read the entire timestamp of the state machine start
event you should do::
cat /sys/bus/zio/devices/adc-100m14b-0200/cset0/tstamp-acq-str-s
cat /sys/bus/zio/devices/adc-100m14b-0200/cset0/tstamp-acq-str-t
cat /sys/bus/zio/devices/adc-100m14b-0200/cset0/tstamp-acq-str-b
The channel set exports 3 time stamps:
tstamp-acq-str-{s|t|b}
this is the time stamp of the last acquisition start command
execution
tstamp-acq-end-{s|t|b}
it is the time of last sample acquired
tstamp-acq-stop-{s|t|b}
this is the time stamp of the last acquisition stop command
execution
While the trigger instance export a time stamp:
tstamp-trg-lst-{s|t|b}
this is the time stamp of the last trigger fire. Please bear in
mind that in multi-shot acquisition you have several trigger fire,
so this time stamp refers only to the last one. If you need the
time stamp for each trigger fire you have to get it from the
zio_control of the associated acquisition block.
By default these time stamps represent (more or less) the time since the
epoch. The user can change this and configure a different timing base.
The following attributes show the current base time:
tstamp-base-{su|sl|b}
The current time known by the FPGA bitstream. It could be an
internal clock or white-rabbit.
The Channels
''''''''''''
The ADC has 4 input channels. Each channel features one attribute, other
attributes in the directory are defined by the kernel or by ZIO.
current-value
the current value is a 16 bit number, resulting from the 14 bit ADC
value and calibration correction. The value is reported as unsigned,
even if it actually represents a signed 16-bit integer. (This because
ZIO manages 32-bit attributes and the value shown comes directly from
the hardware)
The Trigger
'''''''''''
In ZIO, the trigger is a separate software module, that can be replaced
at run time. This driver includes its own ZIO trigger type, that is
selected by default when the driver is initialized. You can change
trigger type (for example use the timer ZIO trigger) but this is not the
typical use case for this board.
The name of the ADC trigger is adc-100m14b. Like all other ZIO objects,
each instance of the trigger has a sysfs directory with its own
attributes:
The ADC has its own zio_trigger_type and it can not work with any other
ZIO's trigger. The ADC trigger is called fmc-adc-trg. We advise you
against replacing the trigger with another one.
The trigger supports four operating modes: the external trigger is
driven by a specific LEMO connector on the front panel of the card. The
internal trigger activates on data threshold in one of the four input
channels - either positive-going or negative-going. The timer trigger
that fires a trigger a given time. The software trigger is activated by
simply writing to a register.
This is the list of attributes (excluding kernel-generic and ZIO-generic
ones):
source-triggered
It is a bitmask where only one bit is set and it identifies the trigger
type that triggered the last acquisition. Look at the header file, or
the gateware document, for the meaning of each bit.
source
It is a bitmask that enable (1) or disable (0) the available triggers.
It supports multi-triggers, so you can enable more than one trigger at
the same time. Look at the header file, or the gateware document, for
the meaning of each bit.
polarity
It is a bitmask that set the trigger polarity to positive (0) on
negative (1) for each trigger that supports it. Look at the header file,
or the gateware document, for the meaning of each bit.
chN-threshold
These attributes choose the value of the data threshold (as a signed
16-bit value).
chN-hysteresis
These attributes choose the value of hysteresis associated to the
threshold.
chN-delay, ext-delay
The delay attribute tells how many samples to delay actual
acquisition since the trigger fired. Being sample-based, the
resolution is 10ns. By default delay is 0. The undersampling
does not have effect.
enable
This is a standard zio attribute, and the code uses it to enable or
disable the hardware trigger (i.e. internal and external). By
default the trigger is enabled.
int-channel, int-threshold
If the internal trigger is selected, these attributes choose the
channel being monitored (range is 0..3) and the value of the data
threshold (as a signed 16-bit value).
nshots
Number of trigger shots. The state machine acquires all trigger
events to internal on-board memory, and performs DMA only at the
end. In single-shot, the acquisition can be as long ad 32Msamples
(on-board memory is 256MB), but in multi-shot acquisition is first
done to in-FPGA memory, and thus each shot can only acquire 2048
samples.
post-samples, pre-samples
Number of samples to acquire. The pre-samples are acquired before
the actual trigger event (plus its optional delay). The post
samples start from the trigger-sample itself. The total number of
samples acquired corresponds to the sum of the two numbers. For
multi-shot acquisition, each shot acquires that many sample, but
pre + post must be at most 2048.
trg-time-{su|sl|t}
When the trigger time is enabled, then these attributes can be used
to program the trigger time.
The Buffer
''''''''''
In ZIO, buffers are separate objects. The framework offers two buffer
types: kmalloc and vmalloc. The former uses the kmalloc function to
allocate each block, the latter uses vmalloc to allocate the whole data
area. While the kmalloc buffer is linked with the core ZIO kernel
module, vmalloc is a separate module. The driver currently prefers
kmalloc, but even when it preferred vmalloc (up to mid June 2013), if
the respective module wad not loaded, ZIO would instantiate kmalloc.
You can change the buffer type, while not acquiring, by writing its name
to the proper attribute. For example::
echo vmalloc > /sys/bus/zio/devices/adc-100m14b-0200/cset0/current_buffer
The disadvantage of kmalloc is that each block is limited in size.
usually 128kB (but current kernels allows up to 4MB blocks). The bigger
the block the more likely allocation fails. If you make a multi-shot
acquisition you need to ensure the buffer can fit enough blocks, and the
buffer size is defined for each buffer instance, i.e. for each channel.
In this case we acquire only from the interleaved channel, so before
making a 1000-long multishot acquisition you can do::
export DEV=/sys/bus/zio/devices/adc-100m14b-0200
echo 1000 > $DEV/cset0/chani/buffer/max-buffer-len
The vmalloc buffer allows mmap support, so when using vmalloc you can
save a copy of your data (actually, you save it automatically if you use
the library calls to allocate and fill the user-space buffer). However,
a vmalloc buffer allocates the whole data space at the beginning, which
may be unsuitable if you have several cards and acquire from one of them
at a time.
The vmalloc buffer type starts off with a size of 128kB, but you can
change it (while not acquiring), by writing to the associated attribute
of the interleaved channel. For example this sets it to 10MB::
export DEV=/sys/bus/zio/devices/adc-100m14b-0200
echo 10000 > $DEV/cset0/chani/buffer/max-buffer-kb
The debugfs Interface
=====================
The fmcadc100m14b4cha driver exports a set of debugfs attributes which
are supposed to be used only for debugging activities. For each device
instance you will see a directory in ``/sys/kernel/debug/adc-100m14b-*``.
data_pattern
It set/unset the data pattern in the ADC chip. It uses the
following syntax: "adc <enable> <pattern>", where *<enable>* could
be 0 (disable), or (enable); and *<pattern>* could be any 14bit
value. If you are disabling the feature, then the pattern is not
necessary
trigger_software
Write to this file to instantaneously trigger an acquisition.
spi-regs
It dumps the ADC's registers.
regs
It dumps the FPGA registers
Reading Data with Char Devices
==============================
To read data from user-space, applications should use the ZIO char
device interface. ZIO creates 2 char devices for each channel (as
documented in ZIO documentation). The ADC acquires only interleaved
samples, so ZIO creates two char device, as shown below::
$ ls -l /dev/zio/
total 0
crw------- 1 root root 250, 8 Aug 23 22:21 adc-100m14b-0200-0-i-ctrl
crw------- 1 root root 250, 9 Aug 23 22:21 adc-100m14b-0200-0-i-data
The actual path names depend on the version of udev you are running. The
fmc-adc library tries both names (the new one shown above, and the older
one, without a ``zio`` sub-directory). Also, please note that a still-newer
version of udev obeys device permissions, so you'll have read-only and
write-only device files (in this case they are both read-only).
If more than one board is probed for, you'll have two or more similar
pairs of devices, differing in the dev_id field, i.e. the ``0200`` shown
above. The dev_id field is built using the PCI bus and the devfn octet;
the example above refers to slot 0 of bus 2. (Most of the time each
PCI-E physical slot is mapped as a bus, so the slot number is usually
zero).
The ADC hardware does not allow to read data from a specific channel;
data is only transferred as an interleaved block of samples. Neither the
ZIO core nor the driver split interleaved data into 4 different buffers,
because that task is computationally intensive, and is better left to
the application (which may or may not need to do it). Thus, the driver
returns to user-space a block of interleaved samples.
To read this interleaved block you can read directly the interleaved
data char device adc-100m14b-0200-0-i-data using any program, for
example cat or hexdump::
$ hexdump -n 8 -e '"" 1/2 "%x\n"' /dev/zio/adc-100m14b-0200-0-i-data
fffc
e474
8034
8084
The ADC hardware always interleaves all 4 channels, and you cannot
acquire a subset of the channels. The acquired stream, thus, follows
this format:
.. figure:: ../img/interleaved.pdf
:alt: ADC interleaved data
The char-device model of ZIO is documented in the ZIO manual; basically,
the ctrl device returns metadata and the data device returns data. Items
in there are strictly ordered, so you can read metadata and then the
associated data, or read only data blocks and discard the associated
metadata.
The ``zio-dump`` tool, part of the ZIO distribution, turns metadata and data
into a meaningful grep-friendly text stream.
User Header Files
=================
Internally the driver uses the header file ``fmc-adc-100m14b4cha.h`` for the
declaration of all the functions, constants and structures. Some of these are
also available for the user-space programs; especially the constants to be
used to properly interpret the ``zio_control`` attributes, or the bitmask
fields definitions.
Troubleshooting
'''''''''''''''
This chapter lists a few errors that may happen and how to deal with
them.
Installation issue with modules_install
'''''''''''''''''''''''''''''''''''''''
The command ``sudo make modules_install`` may place the modules in the wrong
directory or fail with an error like::
make: *** /lib/modules/<kernel-version>/build: No such file or directory.
This happens when you compiled by setting ``LINUX=`` and your sudo is not
propagating the environment to its child processes. In this case, you
should run this command instead::
sudo make modules_install LINUX=$LINUX
..
SPDX-License-Identifier: CC-BY-SA-4.0
SPDX-FileCopyrightText: 2020 CERN
============
The Software
============
.. toctree::
:maxdepth: 1
:caption: Contents
driver
tools
..
SPDX-License-Identifier: CC-BY-SA-4.0
SPDX-FileCopyrightText: 2020 CERN
Tools
=====
The driver is distributed with a few tools living in the ``tools/``
subdirectory and they do not use any dedicated library, instead
they do raw accesses to the driver. The programs are meant to provide
examples about the use of the driver interface.
The generic library `adc-lib`_ supports this driver, so you may want to
consider to use the generic tools from that library.
.. _`adc-lib`: http://www.ohwr.org/projects/adc-lib
Trigger Configuration
---------------------
The program ``fau-trg-config`` configures the FMC ADC trigger. The tool
offers command line parameters to configure every register exported by
the driver. The help screen for the program summarizes the options::
# ./tools/fau-trg-config --help
fau-trg-config [OPTIONS] <DEVICE>
<DEVICE>: ZIO name of the device to use
--pre|-p <value>: number of pre samples
--post|-P <value>: number of pre samples
--nshots|-n <value>: number of trigger shots
--delay|-d <value>: set the ticks delay of the trigger
--threshold|-t <value>: set internal trigger threshold
--channel|-c <value>: select the internal channel as trigger
--external: set to external trigger. The default is the internal trigger.
--negative-edge: set internal trigger polarity to negative edge. The default
is positive edge.
--enable-sw-trg: enable the software trigger. By default is disabled.
--disable-hw-trg: disable the hardware trigger. By default is enabled
--force: force all attribute to the program default
--help|-h: show this help
NOTE: The software trigger works only if also hardware trigger is enabled
The tool gets the configuration values from the user and it writes them
to the corresponding sysfs attributes for the specified device. or
example, if you want to configure the board for the external trigger and
3 shots of 10 pre-samples and 100 post-samples, this is the associated
command line::
# ./tools/fau-trg-config --external --pre 10 --post 100 --re-enable 2 \
adc-100m14b-0200
As shown, the nshot parameter is passed as a number of re-enables,
because the trigger is initially automatically enabled. This may change
in the future, for better naming consistency with hardware documentation
and across tools.
Acquisition Time
----------------
The program fau-acq-time retrieves the timestamps associated with the
acquisition. This is the help screen of the program::
./tools/fau-acq-time --help
fau-acq-time [OPTIONS] <DEVICE>
<DEVICE>: ZIO name of the device to use
--last|-l : time between the last trigger and the acquisition end
--full|-f : time between the acquisition start and the acquisition end
--help|-h: show this help
The program can return two different *types* of acquisition time. The
value returned by **last** represent the time elapsed between the last
trigger event and the acquire-end event; this is the time spent during
the last capture.
The value returned by **full** is the time elapsed between the
acquisition start event and the acquisition end event, i.e. the total
time spent waiting for all trigger events and the time spent acquiring
all samples.
Channel Configuration
---------------------
The program ``tools/fau-config-if`` is a simple graphic tool that allow to
select offset and range for the four channels, activate termination and
see the current value of each channel, every 500ms.
The program open one window for each detected card, and configures it by
writing to sysfs. Such writes are also reported to stdout (in the
terminal where you invoked the program), so you can easily copy the
pathnames in your shell commands.
The figure below shows two instances of the tool, running on the same card
with device_id 0x200 (your window decorations will be different, according
to your choice of window manager or desktop environment).
The first one (at the left) is running under Tk-8.5; the second one shows
the graphic appearance of Tk-8.4 (and earlier versions). If you prefer the
older one, run *wish8.4 tools/fau-config-if* instead of
``tools/fau-config-if`` (or set the previous version as default Tk interpreter).
.. figure:: ../img/config-if.gif
:alt: Two snapshots of fa-config-if
:align: center
Parallel Port Burst
-------------------
If you have a Parallel Port you can use it to generate bursts of pulses
with a software program. This may be useful to test the external
trigger; you can connect the parallel port to the external trigger of
the FMC ADC and generate your trigger events with this program
The program parport-burst, part of this package, generates a burst
according to three command line parameters: the I/O port of the data
byte of the parallel port, the repeat count and the duration of each
period. This example makes 1000 pulses of 100 usec each, using the
physical address of my parallel port (if yours is part of the
motherboard, the address is ``378``)::
./tools/parport-burst dd00 1000 100
Calibration Data
----------------
The FMC ADC 100M mezzanine stores its calibration data in the FMC
EEPROM at offset 256. You could you ``hexdump`` to read it:::
$ hexdump -n 108 /sys/bus/zio/devices/adc-100m14b-0021/calibration_data
0000000 ffe8 0044 ffc2 ff9d 76bc 7658 7637 769c
0000010 1211 ffd7 002e ffa6 ff88 7894 78ae 7887
0000020 78ba 121d ff94 ff7e ff36 fef8 7962 7915
0000030 7881 7851 122a 0000 fffc 0000 0003 7d1e
0000040 7d5f 7e05 7d3c 1211 0000 0000 0000 0000
0000050 801b 8014 8018 8014 121d 0000 0000 0000
0000060 0000 8303 82dc 82e3 82ce 122a
The output is hard to read, that's why we wrote ``fau-calibration``
that make the calibration data human readable:::
$ fau-calibration -f /sys/bus/zio/devices/adc-100m14b-0021/calibration_data
ADC Range 10V
temperature: 46.250000 C
gain: [0x76bc, 0x7658, 0x7637, 0x769c]
offset: [0xffffffe8, 0x0044, 0xffffffc2, 0xffffff9d]
DAC Range 10V
temperature: 46.250000 C
gain: [0x7d1e, 0x7d5f, 0x7e05, 0x7d3c]
offset: [0x0000, 0xfffffffc, 0x0000, 0x0003]
ADC Range 1V
temperature: 46.370000 C
gain: [0x7894, 0x78ae, 0x7887, 0x78ba]
offset: [0xffffffd7, 0x002e, 0xffffffa6, 0xffffff88]
DAC Range 1V
temperature: 46.370000 C
gain: [0x801b, 0x8014, 0x8018, 0x8014]
offset: [0x0000, 0x0000, 0x0000, 0x0000]
ADC Range 100mV
temperature: 46.500000 C
gain: [0x7962, 0x7915, 0x7881, 0x7851]
offset: [0xffffff94, 0xffffff7e, 0xffffff36, 0xfffffef8]
DAC Range 100mV
temperature: 46.500000 C
gain: [0x8303, 0x82dc, 0x82e3, 0x82ce]
offset: [0x0000, 0x0000, 0x0000, 0x0000]
# include parent_common.mk for buildsystem's defines
# use absolute path for REPO_PARENT
-include $(REPO_PARENT)/parent_common.mk
all: kernel tools
DIRS =kernel tools
.PHONY: all clean modules install modules_install $(DIRS)
install modules_install:
all clean modules install modules_install: $(DIRS)
clean: TARGET = clean
modules: TARGET = modules
install: TARGET = install
modules_install: TARGET = modules_install
$(DIRS):
$(MAKE) -C $@ $(TARGET)
CONFIG_FMC_ADC_SVEC ?= CONFIG_VME
VMEBUS_EXTRA_SYMBOLS-$(CONFIG_FMC_ADC_SVEC) := $(VMEBUS_ABS)/driver/Module.symvers
ZIO_VERSION = $(shell cd $(ZIO_ABS); git describe --always --dirty --long --tags)
VERSION = $(shell cd $(src); git describe --always --dirty --long --tags)
KBUILD_EXTRA_SYMBOLS += $(ZIO_EXTRA_SYMBOLS-y)
KBUILD_EXTRA_SYMBOLS += $(FMC_EXTRA_SYMBOLS-y)
KBUILD_EXTRA_SYMBOLS += $(VMEBUS_EXTRA_SYMBOLS-y)
ccflags-y = -DVERSION=\"$(VERSION)\"
ccflags-y += -DCONFIG_FMC_ADC_SVEC
ccflags-y += -I$(src)
ccflags-y += -I$(ZIO_ABS)/include
ccflags-y += -I$(FMC_ABS)/include
ccflags-y += -I$(VMEBUS_ABS)/driver
ccflags-$(CONFIG_FMC_ADC_SVEC) += -I$(VMEBUS_ABS)/include
ccflags-$(CONFIG_FMC_ADC_DEBUG) += -DDEBUG
# Extract ZIO minimum compatible version
ccflags-y += -D__ZIO_MIN_MAJOR_VERSION=$(shell echo $(ZIO_VERSION) | cut -d '-' -f 1 | cut -d '.' -f 1 | tr -d 'v'; )
ccflags-y += -D__ZIO_MIN_MINOR_VERSION=$(shell echo $(ZIO_VERSION) | cut -d '-' -f 1 | cut -d '.' -f 2; )
# add versions of supermodule. It is useful when fine-delay-sw is included as sub-module
# of a bigger project that we want to track
ifdef CONFIG_SUPER_REPO
ifdef CONFIG_SUPER_REPO_VERSION
SUBMODULE_VERSIONS-y += MODULE_INFO(version_$(CONFIG_SUPER_REPO),\"$(CONFIG_SUPER_REPO_VERSION)\");
endif
endif
# add versions of used submodules
SUBMODULE_VERSIONS-y += MODULE_INFO(version_zio,\"$(ZIO_VERSION)\");
# include our header before to avoid conflicts with the kernel
LINUXINCLUDE := -I$(FMC_ABS)/include $(LINUXINCLUDE)
ccflags-y += -DADDITIONAL_VERSIONS="$(SUBMODULE_VERSIONS-y)"
subdirs-ccflags-y = $(ccflags-y)
obj-m := fmc-adc-100m14b4ch.o
obj-m += fmc-adc-100m14b4ch-spec.o
obj-m += fmc-adc-100m14b4ch-svec.o
fmc-adc-100m14b4ch-y = fa-core.o
fmc-adc-100m14b4ch-y += fa-zio-drv.o
fmc-adc-100m14b4ch-y += fa-calibration.o
fmc-adc-100m14b4ch-y += fa-regtable.o
fmc-adc-100m14b4ch-y += fa-zio-trg.o
fmc-adc-100m14b4ch-y += fa-irq.o
fmc-adc-100m14b4ch-y += fa-debug.o
fmc-adc-100m14b4ch-y += fa-dma.o
fmc-adc-100m14b4ch-y += spi.o
fmc-adc-100m14b4ch-spec-objs := fmc-adc-100m14b4ch-spec-core.o
fmc-adc-100m14b4ch-svec-objs := fmc-adc-100m14b4ch-svec-core.o
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Copyright (C) 2019 CERN
-include Makefile.specific
# include parent_common.mk for buildsystem's defines
# use absolute path for REPO_PARENT
-include $(REPO_PARENT)/parent_common.mk
CPPCHECK ?= cppcheck
DKMS ?= 0
CURDIR := $(shell /bin/pwd)
KVERSION ?= $(shell uname -r)
LINUX ?= /lib/modules/$(KVERSION)/build
ifdef REPO_PARENT
ZIO ?= $(REPO_PARENT)/fmc/zio
FMC ?= $(REPO_PARENT)/fmc-sw
VMEBUS ?= $(REPO_PARENT)/vmebridge-ng
endif
ifeq ($(DKMS), 1)
# Take last installed version (if installed using RPM it should be OK)
ZIO_VERSION ?= $(shell basename $(shell ls -d $(DKMSTREE)/zio/* | grep -E "\/[0-9]+\.[0-9]+\.[0-9]+" | sort -V | tail -n 1))
ZIO_ABS ?= $(DKMSTREE)/zio/$(ZIO_VERSION)/source
ZIO_EXTRA_SYMBOLS-y = $(DKMSTREE)/zio/kernel-$(KVERSION)-$(shell uname -p)/module/Module.symvers
else
ifndef ZIO
$(error "Missing ZIO environment variable")
endif
ifndef FMC
$(error "Missing FMC environment variable")
endif
ifndef VMEBUS
$(error "Missing VMEBUS environment variable")
endif
ZIO_ABS ?= $(abspath $(ZIO))
ZIO_EXTRA_SYMBOLS-y = $(ZIO_ABS)/drivers/zio/Module.symvers
ZIO_VERSION ?= $(shell cd $(ZIO_ABS); git describe --always --dirty --long --tags)
FMC_ABS ?= $(abspath $(FMC))
FMC_EXTRA_SYMBOLS-y = $(FMC_ABS)/drivers/fmc/Module.symvers
endif
VMEBUS_ABS ?= $(abspath $(VMEBUS) )
GIT_VERSION = $(shell git describe --always --dirty --long --tags)
all modules:
$(MAKE) -C $(LINUX) M=$(CURDIR) ZIO_ABS=$(ZIO_ABS) FMC_ABS=$(FMC_ABS) \
ZIO_EXTRA_SYMBOLS-y=$(ZIO_EXTRA_SYMBOLS-y) \
FMC_EXTRA_SYMBOLS-y=$(FMC_EXTRA_SYMBOLS-y) \
ZIO_VERSION=$(ZIO_VERSION) \
GIT_VERSION=$(GIT_VERSION) \
VMEBUS_ABS=$(VMEBUS_ABS) modules
install modules_install: modules
$(MAKE) -C $(LINUX) M=$(CURDIR) modules_install
# be able to run the "clean" rule even if $(LINUX) is not valid
clean:
rm -rf *.o *~ .*.cmd *.ko *.mod.c .tmp_versions Module.symvers \
Module.markers modules.order
cppcheck:
$(CPPCHECK) -q -I. -I$(ZIO_ABS)/include -I$(FMC_BUS_ABS)/ --enable=all *.c *.h
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* EEPROM calibration block retreival code for fa-dev
*/
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/byteorder/generic.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/zio.h>
#include <linux/moduleparam.h>
#include <linux/jiffies.h>
#include <fmc-adc-100m14b4cha.h>
static int fa_calib_period_s = 0;
module_param_named(calib_s, fa_calib_period_s, int, 0444);
/* This identity calibration is used as default */
static const struct fa_calib_stanza fa_identity_calib = {
.offset = { 0, },
.gain = {0x8000, 0x8000, 0x8000, 0x8000},
.temperature = 50 * 100, /* 50 celsius degrees */
};
/* Max difference from identity thing */
#define FA_CALIB_MAX_DELTA_OFFSET 0x1000
#define FA_CALIB_MAX_DELTA_GAIN 0x1000
#define FA_CALIB_MAX_DELTA_TEMP (40 * 100) /* 10-90 celsius */
static bool fa_calib_is_busy(struct fa_dev *fa)
{
return !!fa_readl(fa, fa->fa_adc_csr_base, &zfad_regs[ZFA_STA_CALIB_BUSY]);
}
static int fa_calib_apply(struct fa_dev *fa)
{
if (fa_calib_is_busy(fa))
return -EBUSY;
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFA_CTL_CALIB_APPLY], 1);
ndelay(100);
if (fa_calib_is_busy(fa))
return -EBUSY;
return 0;
}
static void fa_calib_gain_set(struct fa_dev *fa, unsigned int chan, int val)
{
int attr_idx;
attr_idx = zfad_get_chx_index(ZFA_CHx_GAIN, chan);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[attr_idx], val);
}
static void fa_calib_offset_set(struct fa_dev *fa, unsigned int chan, int val)
{
int attr_idx;
attr_idx = zfad_get_chx_index(ZFA_CHx_OFFSET, chan);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[attr_idx],
val & 0xFFFF /* prevent warning */);
}
/*
* Empirical values for the gain error slope
* 10V 0.0012500
* 1V -0.0000233
* 100mV -0.0000163
*
* Multiply by 0x8000 to have the same range as the calibration data.
* To do integer math we also multiply by 0x2000.
*/
static const int64_t gain_adc_error_slope_fix[] = {
[FA100M14B4C_RANGE_10V] = 335544,
[FA100M14B4C_RANGE_1V] = -6255,
[FA100M14B4C_RANGE_100mV] = -4375,
};
/**
* Compute the correct gain
* @range: voltage range
* @gain_c: calibration value
* @delta_temp: temperature difference: (current temp. - calibration temp.)
* the unit must be centi-degree
*/
static int fa_calib_adc_gain_fix(int range, int32_t gain_c,
int32_t delta_temp)
{
int64_t error;
error = gain_adc_error_slope_fix[range] * delta_temp;
error /= 0x2000; /* see comment above for gain_adc_error_slope_fix */
error /= 100; /* convert to degree */
return gain_c - error;
}
/*
* Empirical values for the gain error slope
* 10V 0.00017100
* 1V -0.00000349
* 100mV 0.00001540
* Multiply by 0x8000 to have the same range as the calibration data.
* To do integer math we also multiply by 0x2000.
*/
static const int64_t gain_dac_error_slope_fix[] = {
[FA100M14B4C_RANGE_10V] = 459025,
[FA100M14B4C_RANGE_1V] = -937,
[FA100M14B4C_RANGE_100mV] = 4134,
};
/**
* Compute the correct gain
* @range: voltage range
* @gain_c: calibration value
* @delta_temp: temperature difference: (current temp. - calibration temp.)
* the unit must be centi-degree
*/
static int fa_calib_dac_gain_fix(int range, uint32_t gain_c,
int32_t delta_temp)
{
int64_t error;
error = gain_dac_error_slope_fix[range] * delta_temp;
error /= 0x2000; /* see comment above for gain_dac_error_slope_fix */
error /= 100; /* convert to degree */
return gain_c - error;
}
/**
* Calibrate a ADC channel
* @fa ADC instance
* @chan channel number
* @temperature temperature
*
* You must hold &fa->zdev->cset->lock while calling this function
*/
void fa_calib_adc_config_chan(struct fa_dev *fa, unsigned int chan,
int32_t temperature)
{
int range = fa->range[chan];
struct fa_calib_stanza *cal = &fa->calib.dac[range];
int32_t delta_temp;
int gain;
int err;
if (temperature == 0xFFFFFFFF)
temperature = fa_temperature_read(fa);
delta_temp = (temperature / 10) - cal->temperature;
if (unlikely((fa->flags & FA_DEV_F_PATTERN_DATA)))
gain = cal->gain[chan];
else
gain = fa_calib_adc_gain_fix(range, cal->gain[chan],
delta_temp);
dev_dbg(&fa->pdev->dev,
"%s: {delta-temperature: %d, chan: %d, range: %d, gain: 0x%x, offset: 0x%x}\n",
__func__, delta_temp, chan, range, gain, cal->offset[chan]);
fa_calib_gain_set(fa, chan, gain);
fa_calib_offset_set(fa, chan, cal->offset[chan]);
err = fa_calib_apply(fa);
if (err)
dev_err(&fa->pdev->dev, "Can't apply calibration values\n");
}
/**
* It sets the DAC voltage to apply an offset on the input channel
* @fa ADC device
* @chan channel number
* @val DAC values (-5V: 0x0000, 0V: 0x8000, +5V: 0x7FFF)
*
* Return: 0 on success, otherwise a negative error number
*/
static int fa_dac_offset_set(struct fa_dev *fa, unsigned int chan,
uint32_t val)
{
return fa_spi_xfer(fa, FA_SPI_SS_DAC(chan), 16, val, NULL);
}
static int64_t fa_dac_offset_raw_get(int32_t offset)
{
int64_t hwval;
hwval = offset * 0x8000LL / 5000000;
if (hwval == 0x8000)
hwval = 0x7fff; /* -32768 .. 32767 */
return hwval;
}
static int64_t fa_dac_offset_raw_calibrate(int32_t raw_offset,
int gain, int offset)
{
int64_t hwval;
hwval = ((raw_offset + offset) * gain) >> 15; /* signed */
hwval += 0x8000; /* offset binary */
if (hwval < 0)
hwval = 0;
if (hwval > 0xffff)
hwval = 0xffff;
return hwval;
}
static int fa_dac_offset_get(struct fa_dev *fa, unsigned int chan)
{
int32_t off_uv = fa->user_offset[chan] + fa->zero_offset[chan];
if (WARN(off_uv < DAC_SAT_LOW,
"DAC lower saturation %d < %d\n",
off_uv, DAC_SAT_LOW)) {
off_uv = DAC_SAT_LOW;
}
if (WARN(off_uv > DAC_SAT_UP,
"DAC upper saturation %d > %d\n",
off_uv, DAC_SAT_UP)) {
off_uv = DAC_SAT_UP;
}
return off_uv;
}
/**
* Calibrate a DAC channel
* @fa ADC instance
* @chan channel number
* @temperature temperature
*
* You must hold &fa->zdev->cset->lock while calling this function
*/
int fa_calib_dac_config_chan(struct fa_dev *fa, unsigned int chan,
int32_t temperature)
{
int32_t off_uv = fa_dac_offset_get(fa, chan);
int32_t off_uv_raw = fa_dac_offset_raw_get(off_uv);
int range = fa->range[chan];
struct fa_calib_stanza *cal = &fa->calib.dac[range];
int32_t delta_temp;
int gain;
int hwval;
if (temperature == 0xFFFFFFFF)
temperature = fa_temperature_read(fa);
delta_temp = (temperature / 10) - cal->temperature;
if (unlikely((fa->flags & FA_DEV_F_PATTERN_DATA)))
gain = cal->gain[chan];
else
gain = fa_calib_dac_gain_fix(range, cal->gain[chan],
delta_temp);
dev_dbg(&fa->pdev->dev,
"%s: {delta-temperature: %d, chan: %d, range: %d, gain: 0x%x, offset: 0x%x}\n",
__func__, delta_temp, chan, range, gain, cal->offset[chan]);
hwval = fa_dac_offset_raw_calibrate(off_uv_raw, gain,
cal->offset[chan]);
return fa_dac_offset_set(fa, chan, hwval);
}
void fa_calib_config(struct fa_dev *fa)
{
int32_t temperature;
int i;
temperature = fa_temperature_read(fa);
spin_lock(&fa->zdev->cset->lock);
for (i = 0; i < FA100M14B4C_NCHAN; ++i) {
fa_calib_adc_config_chan(fa, i, temperature);
fa_calib_dac_config_chan(fa, i, temperature);
}
spin_unlock(&fa->zdev->cset->lock);
}
/**
* Periodically update gain calibration values
* @fa: FMC ADC device
*
* In the ADC we have a calibration value that is good for a small
* temperature range. We proved empirically that the gain error has a
* linear behavior with respect to the temperature.
*
*/
static void fa_calib_gain_update(unsigned long arg)
{
struct fa_dev *fa = (void *)arg;
fa_calib_config(fa);
mod_timer(&fa->calib_timer, jiffies + HZ * fa_calib_period_s);
}
/* Actual verification code */
static int fa_verify_calib_stanza(struct device *msgdev, char *name, int r,
struct fa_calib_stanza *cal)
{
const struct fa_calib_stanza *iden = &fa_identity_calib;
int i;
for (i = 0; i < ARRAY_SIZE(cal->offset); i++) {
if (abs(cal->offset[i] - iden->offset[i])
> FA_CALIB_MAX_DELTA_OFFSET) {
dev_err(msgdev, "wrong offset (%i) 0x%x\n",
i, cal->offset[i]);
return -EINVAL;
}
if (abs((s16)(cal->gain[i] - iden->gain[i]))
> FA_CALIB_MAX_DELTA_GAIN) {
dev_err(msgdev, "invalid gain (%i) 0x%x\n",
i, cal->gain[i]);
return -EINVAL;
}
}
if (abs((s16)(cal->temperature - iden->temperature))
> FA_CALIB_MAX_DELTA_TEMP) {
dev_err(msgdev, "invalid temper 0x%x\n", cal->temperature);
return -EINVAL;
}
return 0;
}
static int fa_verify_calib(struct device *msgdev, struct fa_calib *calib)
{
int i, err = 0;
for (i = 0; i < ARRAY_SIZE(calib->adc); i++) {
err = fa_verify_calib_stanza(msgdev, "adc", i, calib->adc + i);
if (err)
return err;
err = fa_verify_calib_stanza(msgdev, "dac", i, calib->dac + i);
if (err)
return err;
}
return 0;
}
/**
* @calib: calibration data
*
* We know for sure that our structure is only made of 16bit fields
*/
static void fa_calib_le16_to_cpus(struct fa_calib *calib)
{
int i;
uint16_t *p = (void *)calib;
for (i = 0; i < sizeof(*calib) / sizeof(uint16_t); i++)
le16_to_cpus(p + i); /* s == in situ */
}
/**
* @calib: calibration data
*
* We know for sure that our structure is only made of 16bit fields
*/
static void fa_calib_cpu_to_le16s(struct fa_calib *calib)
{
int i;
uint16_t *p = (void *)calib;
for (i = 0; i < sizeof(*calib) / sizeof(uint16_t); i++)
cpu_to_le16s(p + i); /* s == in situ */
}
static void fa_identity_calib_set(struct fa_calib *calib)
{
int i;
/* Retrieve calibration data from the eeprom, then verify it */
for (i = 0; i < FA_CALIB_STANZA_N; ++i) {
memcpy(&calib->adc[i], &fa_identity_calib,
sizeof(calib->adc[i]));
memcpy(&calib->dac[i], &fa_identity_calib,
sizeof(calib->dac[i]));
}
fa_calib_le16_to_cpus(calib);
}
static void fa_calib_write(struct fa_dev *fa, struct fa_calib *calib)
{
int err;
fa_calib_le16_to_cpus(calib);
err = fa_verify_calib(fa->msgdev, calib);
if (err) {
dev_info(fa->msgdev, "Apply Calibration Identity\n");
fa_identity_calib_set(&fa->calib);
} else {
memcpy(&fa->calib, calib, sizeof(*calib));
}
}
static ssize_t fa_write_eeprom(struct file *file, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct fa_dev *fa = get_zfadc(dev);
struct fa_calib *calib = (struct fa_calib *) buf;
if (off != 0 || count != sizeof(*calib))
return -EINVAL;
fa_calib_write(fa, calib);
fa_calib_config(fa);
return count;
}
static ssize_t fa_read_eeprom(struct file *file, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct fa_dev *fa = get_zfadc(dev);
struct fa_calib *calib = (struct fa_calib *) buf;
if (off != 0 || count < sizeof(fa->calib))
return -EINVAL;
memcpy(calib, &fa->calib, sizeof(fa->calib));
fa_calib_cpu_to_le16s(calib);
return count;
}
struct bin_attribute dev_attr_calibration = {
.attr = {
.name = "calibration_data",
.mode = 0644,
},
.size = sizeof(struct fa_calib),
.write = fa_write_eeprom,
.read = fa_read_eeprom,
};
int fa_calib_init(struct fa_dev *fa)
{
struct fa_calib calib;
int ret;
ret = fmc_slot_eeprom_read(fa->slot, &calib,
FA_CAL_OFFSET, sizeof(calib));
if (ret < 0) {
dev_warn(fa->msgdev,
"Failed to read calibration from EEPROM: using identity calibration %d\n",
ret);
fa_identity_calib_set(&calib);
goto out;
}
fa_calib_write(fa, &calib);
/* Prepare the timely recalibration */
fa_calib_config(fa);
if (fa_calib_period_s) {
setup_timer(&fa->calib_timer, fa_calib_gain_update, (unsigned long)fa);
mod_timer(&fa->calib_timer, jiffies + HZ * fa_calib_period_s);
}
out:
return 0;
}
void fa_calib_exit(struct fa_dev *fa)
{
if (fa_calib_period_s)
del_timer_sync(&fa->calib_timer);
fa_identity_calib_set(&fa->calib);
fa_calib_config(fa);
}
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2012-2019 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@gmail.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/dmaengine.h>
#include <linux/mod_devicetable.h>
#include <linux/ipmi-fru.h>
#include <linux/fmc.h>
#include "fmc-adc-100m14b4cha.h"
#include <platform_data/fmc-adc-100m14b4cha.h>
static int fa_enable_test_data_fpga;
module_param_named(enable_test_data_fpga, fa_enable_test_data_fpga, int, 0444);
#define FA_EEPROM_TYPE "at24c64"
static const int zfad_hw_range[] = {
[FA100M14B4C_RANGE_10V_CAL] = 0x44,
[FA100M14B4C_RANGE_1V_CAL] = 0x40,
[FA100M14B4C_RANGE_100mV_CAL] = 0x42,
[FA100M14B4C_RANGE_10V] = 0x45,
[FA100M14B4C_RANGE_1V] = 0x11,
[FA100M14B4C_RANGE_100mV] = 0x23,
[FA100M14B4C_RANGE_OPEN] = 0x00,
};
/* fmc-adc specific workqueue */
struct workqueue_struct *fa_workqueue;
/**
* Enable/Disable Data Output Randomizer
* @fa: the adc descriptor
* @enable:
*/
int fa_adc_output_randomizer_set(struct fa_dev *fa, bool enable)
{
uint32_t tx, rx;
int err;
tx = 0x8000;
tx |= (1 << 8);
err = fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, tx, &rx);
if (err)
return err;
if (enable)
rx |= BIT(6);
else
rx &= ~BIT(6);
tx = 0x0000;
tx |= (1 << 8);
tx |= (rx & 0xFF);
err = fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, tx, NULL);
if (err)
return err;
return 0;
}
/**
* Check if the Data Output Randomizer is enabled
* @fa: the adc descriptor
* Return: true if the feature is enabled, otherwise false
*/
bool fa_adc_is_output_randomizer(struct fa_dev *fa)
{
uint32_t tx, rx;
int err;
tx = 0x8000;
tx |= (1 << 8);
err = fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, tx, &rx);
if (err)
return false;
return !!(rx & BIT(6));
}
/**
* Read FMC mezzanine temperature
* @fa: the adc descriptor
*
* DS18B20 returns units of 1/16 degree. We return units
* of 1/1000 of a degree instead.
*/
int32_t fa_temperature_read(struct fa_dev *fa)
{
uint32_t raw_temp;
raw_temp = fa_readl(fa, fa->fa_ow_base, &zfad_regs[ZFA_DS18B20_TEMP]);
return (raw_temp * 1000 + 8) / 16;
}
/**
* Do a software trigger
* @fa: the adc descriptor
*
* Return: 0 on success, otherwise a negative error number
*/
int fa_trigger_software(struct fa_dev *fa)
{
struct zio_ti *ti = fa->zdev->cset->ti;
struct zio_attribute *ti_zattr = ti->zattr_set.std_zattr;
unsigned int timeout;
int err;
/* Fire if software trigger is enabled (index 5) */
if (!(ti->zattr_set.ext_zattr[FA100M14B4C_TATTR_SRC].value &
FA100M14B4C_TRG_SRC_SW)) {
dev_info(&fa->pdev->dev, "sw trigger is not enabled\n");
return -EPERM;
}
/* Fire if nsamples!=0 */
if (!ti->nsamples) {
dev_info(&fa->pdev->dev, "pre + post = 0: cannot acquire\n");
return -EINVAL;
}
/*
* We can do a software trigger if the FSM is not in
* the WAIT trigger status. Wait for it.
* Remember that: timeout is in us, a sample takes 10ns
*/
timeout = ti_zattr[ZIO_ATTR_TRIG_PRE_SAMP].value / 10;
err = fa_fsm_wait_state(fa, FA100M14B4C_STATE_WAIT, timeout);
if (err)
return err;
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_SW], 1);
return 0;
}
/**
* Description:
* The version from the Linux kernel automatically squash contiguous pages.
* Sometimes we do not want to squash (e.g. SVEC)
*/
static int sg_alloc_table_from_pages_no_squash(struct sg_table *sgt,
struct page **pages,
unsigned int n_pages,
unsigned int offset,
unsigned long size,
unsigned int max_segment,
gfp_t gfp_mask)
{
struct scatterlist *sg;
int err, i;
err = sg_alloc_table(sgt, n_pages, GFP_KERNEL);
if (unlikely(err))
return err;
for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
unsigned long chunk_size;
chunk_size = PAGE_SIZE - offset;
sg_set_page(sg, pages[i], min(size, chunk_size), offset);
offset = 0;
size -= chunk_size;
}
return 0;
}
/*
* zfad_convert_hw_range
* @usr_val: range value
*
* return the enum associated to the range value
*/
int zfad_convert_hw_range(uint32_t bitmask)
{
int i;
for (i = 0; i < ARRAY_SIZE(zfad_hw_range); i++)
if (zfad_hw_range[i] == bitmask)
return i;
return -EINVAL;
}
/* Calculate correct index in fa_regfield array for channel from CHx indexes */
int zfad_get_chx_index(unsigned long addr, unsigned int chan)
{
int offset;
offset = ZFA_CHx_MULT * (FA100M14B4C_NCHAN - chan);
return addr - offset;
}
/*
* zfad_init_saturation
* @fa: the fmc-adc descriptor
*
* Initialize all saturation registers to the maximum value
*/
void zfad_init_saturation(struct fa_dev *fa)
{
int idx, i;
for (i = 0, idx = ZFA_CH1_SAT; i < FA100M14B4C_NCHAN; ++i, idx += ZFA_CHx_MULT)
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[idx], 0x7fff);
}
/*
* fa_adc_range_set
* @fa: the fmc-adc descriptor
* @chan: the channel to calibrate
* @usr_val: the volt range to set and calibrate
*
* When the input range changes, we must write new fixup values.
* Gain ad offsets must be corrected with offset and gain calibration value.
* An open input and test data do not need any correction.
*/
int fa_adc_range_set(struct fa_dev *fa, struct zio_channel *chan, int range)
{
int i;
/* Actually set the range */
i = zfad_get_chx_index(ZFA_CHx_CTL_RANGE, chan->index);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[i], zfad_hw_range[range]);
if (range == FA100M14B4C_RANGE_OPEN)
range = FA100M14B4C_RANGE_1V;
else if (range >= FA100M14B4C_RANGE_10V_CAL)
range -= FA100M14B4C_RANGE_10V_CAL;
if (range < 0 || range > ARRAY_SIZE(fa->calib.adc)) {
dev_info(fa->msgdev, "Invalid range %i or ch %i\n",
range, chan->index);
return -EINVAL;
}
spin_lock(&fa->zdev->cset->lock);
fa->range[chan->index] = range;
spin_unlock(&fa->zdev->cset->lock);
return 0;
}
static enum fa100m14b4c_fsm_state fa_fsm_get_state(struct fa_dev *fa)
{
return fa_readl(fa, fa->fa_adc_csr_base, &zfad_regs[ZFA_STA_FSM]);
}
static bool fa_fsm_is_state(struct fa_dev *fa,
enum fa100m14b4c_fsm_state state)
{
return fa_fsm_get_state(fa) == state;
}
int fa_fsm_wait_state(struct fa_dev *fa,
enum fa100m14b4c_fsm_state state,
unsigned int timeout_us)
{
unsigned long timeout;
timeout = jiffies + usecs_to_jiffies(timeout_us);
while (!fa_fsm_is_state(fa, state)) {
cpu_relax();
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
}
return 0;
}
static void fa_fpga_data_pattern_set(struct fa_dev *fa, unsigned int enable)
{
fa_writel(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFA_CTL_TEST_DATA_EN], enable);
}
/**
* It enables or disables the pattern data on the ADC
* @fa The ADC device instance
* @pattern the pattern data to get from the ADC
* @enable 0 to disable, 1 to enable
*/
int fa_adc_data_pattern_set(struct fa_dev *fa, uint16_t pattern,
unsigned int enable)
{
uint32_t frame_tx;
int err;
dev_dbg(&fa->pdev->dev, "%s {patter: 0x%04x, enable: %d}\n", __func__, pattern, enable);
spin_lock(&fa->zdev->cset->lock);
frame_tx = 0x0000; /* write mode */
frame_tx |= 0x0400; /* A4 pattern */
frame_tx |= pattern & 0xFF; /* LSB pattern */
err = fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, frame_tx, NULL);
if (err)
goto err;
frame_tx = 0x0000; /* write mode */
frame_tx |= 0x0300; /* A3 pattern + enable */
frame_tx |= (pattern & 0xFF00) >> 8; /* MSB pattern */
frame_tx |= (enable ? 0x80 : 0x00); /* Enable the pattern data */
err = fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, frame_tx, NULL);
if (err)
goto err;
if (enable)
fa->flags |= FA_DEV_F_PATTERN_DATA;
else
fa->flags &= ~FA_DEV_F_PATTERN_DATA;
err:
spin_unlock(&fa->zdev->cset->lock);
return err;
}
/**
* Get current status for data pattern
* @fa The ADC device instance
* @pattern the pattern data to get from the ADC
* @enable 0 to disable, 1 to enable
*/
int fa_adc_data_pattern_get(struct fa_dev *fa, uint16_t *pattern,
unsigned int *enable)
{
uint32_t tx, rx;
int err;
tx = 0x8000 | (3 << 8);
err = fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, tx, &rx);
if (err)
return err;
*enable = !!(rx & 0x80);
*pattern = ((rx & 0x3F) << 8);
tx = 0x8000 | (4 << 8);
err = fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, tx, &rx);
if (err)
return err;
*pattern |= (rx & 0xFF);
return 0;
}
/*
* zfad_fsm_command
* @fa: the fmc-adc descriptor
* @command: the command to apply to FSM
*
* This function checks if the command can be done and performs some
* preliminary operation beforehand
*/
int zfad_fsm_command(struct fa_dev *fa, uint32_t command)
{
struct zio_cset *cset = fa->zdev->cset;
uint32_t val;
if (command != FA100M14B4C_CMD_START &&
command != FA100M14B4C_CMD_STOP) {
dev_info(fa->msgdev, "Invalid command %i\n", command);
return -EINVAL;
}
/*
* When any command occurs we are ready to start a new acquisition, so
* we must abort any previous one. If it is STOP, we abort because we
* abort an acquisition. If it is START, we abort because if there was
* a previous start but the acquisition end interrupt doesn't occurs,
* START mean RESTART. If it is a clean START, the abort has not
* effects.
*
* This is done only if ADC is using its own trigger, otherwise it is
* not necessary.
*
* The case of fmc-adc-trg is optimized because is the most common
* case
*/
if (likely(cset->trig == &zfat_type || command == FA100M14B4C_CMD_STOP))
zio_trigger_abort_disable(cset, 0);
/* Reset counters */
fa->n_shots = 0;
fa->n_fires = 0;
/* If START, check if we can start */
if (command == FA100M14B4C_CMD_START) {
/* Verify that SerDes PLL is lockes */
val = fa_readl(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFA_STA_SERDES_PLL]);
if (!val) {
dev_info(fa->msgdev, "Cannot start acquisition: "
"SerDes PLL not locked\n");
return -EBUSY;
}
/* Verify that SerDes is synched */
val = fa_readl(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFA_STA_SERDES_SYNCED]);
if (!val) {
dev_info(fa->msgdev, "Cannot start acquisition: "
"SerDes not synchronized\n");
return -EBUSY;
}
/* Now we can arm the trigger for the incoming acquisition */
zio_arm_trigger(cset->ti);
/*
* FIXME maybe zio_arm_trigger() can return an error when it
* is not able to arm a trigger.
*
* It returns -EPERM, but the error can be -ENOMEM or -EINVAL
* from zfat_arm_trigger() or zfad_input_cset()
*/
if (!(cset->ti->flags & ZIO_TI_ARMED)) {
dev_info(fa->msgdev, "Cannot start acquisition: "
"Trigger refuses to arm\n");
return -EIO;
}
dev_dbg(fa->msgdev, "FSM START Command\n");
fa_enable_irqs(fa);
fa_writel(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFA_CTL_RST_TRG_STA], 1);
} else {
dev_dbg(fa->msgdev, "FSM STOP Command\n");
fa->enable_auto_start = 0;
fa_disable_irqs(fa);
}
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFA_CTL_FMS_CMD],
command);
return 0;
}
static void fa_init_timetag(struct fa_dev *fa)
{
unsigned long seconds;
seconds = get_seconds();
fa_writel(fa, fa->fa_utc_base, &zfad_regs[ZFA_UTC_SECONDS_U],
(seconds >> 32) & 0xFFFFFFFF);
fa_writel(fa, fa->fa_utc_base, &zfad_regs[ZFA_UTC_SECONDS_L],
(seconds >> 00) & 0xFFFFFFFF);
}
/*
* Specific check and init
*/
static int __fa_init(struct fa_dev *fa)
{
struct zio_device *zdev = fa->zdev;
int i, addr;
/* Use identity calibration */
fa->mshot_max_samples = fa_readl(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFA_MULT_MAX_SAMP]);
/* Force stop FSM to prevent early trigger fire */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFA_CTL_FMS_CMD],
FA100M14B4C_CMD_STOP);
/* Initialize channels to use 1V range */
for (i = 0; i < 4; ++i) {
addr = zfad_get_chx_index(ZFA_CHx_CTL_RANGE,
zdev->cset->chan[i].index);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[addr],
FA100M14B4C_RANGE_1V);
fa_adc_range_set(fa, &zdev->cset->chan[i],
FA100M14B4C_RANGE_1V);
/* reset channel offset */
fa->user_offset[i] = 0;
fa->zero_offset[i] = 0;
}
fa_calib_config(fa);
/* Enable mezzanine clock */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFA_CTL_CLK_EN], 1);
/* Set decimation to minimum */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_SR_UNDER], 1);
/* Set test data register */
fa_fpga_data_pattern_set(fa, fa_enable_test_data_fpga);
/* disable test pattern data in the ADC */
fa_adc_data_pattern_set(fa, 0, 0);
/* Set to single shot mode by default */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_SHOTS_NB], 1);
zfat_trigger_source_reset(fa);
/* Zero offsets and release the DAC clear */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFA_CTL_DAC_CLR_N], 1);
/* Initialize channel saturation values */
zfad_init_saturation(fa);
fa_init_timetag(fa);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_EXT_DLY], 0);
/* disable auto_start */
fa->enable_auto_start = 0;
return 0;
}
/* This structure lists the various subsystems */
struct fa_modlist {
char *name;
int (*init)(struct fa_dev *);
void (*exit)(struct fa_dev *);
};
static struct fa_modlist mods[] = {
{"spi", fa_spi_init, fa_spi_exit},
{"zio", fa_zio_init, fa_zio_exit},
{"debug", fa_debug_init, fa_debug_exit},
{"calibration", fa_calib_init, fa_calib_exit},
};
static int fa_resource_validation(struct platform_device *pdev)
{
struct resource *r;
r = platform_get_resource(pdev, IORESOURCE_IRQ, ADC_IRQ_TRG);
if (!r) {
dev_err(&pdev->dev,
"The ADC needs an interrupt number for the IRQ\n");
return -ENXIO;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, ADC_MEM_BASE);
if (!r) {
dev_err(&pdev->dev,
"The ADC needs base address\n");
return -ENXIO;
}
return 0;
}
#define FA_FMC_NAME "FmcAdc100m14b4cha"
static bool fa_fmc_slot_is_valid(struct fa_dev *fa)
{
int ret;
void *fru = NULL;
char *fmc_name = NULL;
if (!fmc_slot_fru_valid(fa->slot)) {
dev_err(fa->msgdev, "Can't identify FMC card: invalid FRU\n");
return -EINVAL;
}
fru = kmalloc(FRU_SIZE_MAX, GFP_KERNEL);
if (!fru)
return -ENOMEM;
ret = fmc_slot_eeprom_read(fa->slot, fru, 0x0, FRU_SIZE_MAX);
if (ret != FRU_SIZE_MAX) {
dev_err(fa->msgdev, "Failed to read FRU header\n");
goto err;
}
fmc_name = fru_get_product_name(fru);
ret = strcmp(fmc_name, FA_FMC_NAME);
if (ret) {
dev_err(fa->msgdev,
"Invalid FMC card: expectd '%s', found '%s'\n",
FA_FMC_NAME, fmc_name);
goto err;
}
kfree(fmc_name);
kfree(fru);
return true;
err:
kfree(fmc_name);
kfree(fru);
return false;
}
static void fa_memops_detect(struct fa_dev *fa)
{
if (fa_is_flag_set(fa, FMC_ADC_BIG_ENDIAN)) {
fa->memops.read = ioread32be;
fa->memops.write = iowrite32be;
} else {
fa->memops.read = ioread32;
fa->memops.write = iowrite32;
}
}
static void fa_sg_alloc_table_init(struct fa_dev *fa)
{
if (fa_is_flag_set(fa, FMC_ADC_NOSQUASH_SCATTERLIST))
fa->sg_alloc_table_from_pages = sg_alloc_table_from_pages_no_squash;
else
fa->sg_alloc_table_from_pages = __sg_alloc_table_from_pages;
}
static struct fmc_adc_platform_data fmc_adc_pdata_default = {
.flags = 0,
.vme_ddr_offset = 0,
.calib_trig_time = 0,
.calib_trig_threshold = 0,
.calib_trig_internal = 0,
};
/* probe and remove are called by fa-spec.c */
int fa_probe(struct platform_device *pdev)
{
struct fa_modlist *m = NULL;
struct fa_dev *fa;
struct resource *r;
int err, i = 0, slot_nr;
err = fa_resource_validation(pdev);
if (err)
return err;
/* Driver data */
fa = devm_kzalloc(&pdev->dev, sizeof(struct fa_dev), GFP_KERNEL);
if (!fa)
return -ENOMEM;
platform_set_drvdata(pdev, fa);
fa->pdev = pdev;
fa->msgdev = &fa->pdev->dev;
if (!pdev->dev.platform_data) {
dev_err(fa->msgdev, "Missing platform data, use default\n");
pdev->dev.platform_data = &fmc_adc_pdata_default;
}
fa_memops_detect(fa);
fa_sg_alloc_table_init(fa);
r = platform_get_resource(pdev, IORESOURCE_MEM, ADC_MEM_BASE);
fa->fa_top_level = ioremap(r->start, resource_size(r));
fa->fa_adc_csr_base = fa->fa_top_level + ADC_CSR_OFF;
fa->fa_irq_adc_base = fa->fa_top_level + ADC_EIC_OFF;
fa->fa_ow_base = fa->fa_top_level + ADC_OW_OFF;
fa->fa_spi_base = fa->fa_top_level + ADC_SPI_OFF;
fa->fa_utc_base = fa->fa_top_level + ADC_UTC_OFF;
slot_nr = fa_readl(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFA_STA_FMC_NR]) + 1;
fa->slot = fmc_slot_get(pdev->dev.parent->parent, slot_nr);
if (IS_ERR(fa->slot)) {
dev_err(fa->msgdev, "Can't find FMC slot %d err: %ld\n",
slot_nr, PTR_ERR(fa->slot));
goto out_fmc;
}
if (!fmc_slot_present(fa->slot)) {
dev_err(fa->msgdev, "Can't identify FMC card: missing card\n");
goto out_fmc_pre;
}
if (strcmp(fmc_slot_eeprom_type_get(fa->slot), FA_EEPROM_TYPE)) {
dev_warn(fa->msgdev, "use non standard EERPOM type \"%s\"\n",
FA_EEPROM_TYPE);
err = fmc_slot_eeprom_type_set(fa->slot, FA_EEPROM_TYPE);
if (err) {
dev_err(fa->msgdev,
"Failed to change EEPROM type to \"%s\"",
FA_EEPROM_TYPE);
goto out_fmc_eeprom;
}
}
if(!fa_fmc_slot_is_valid(fa))
goto out_fmc_err;
err = fa_dma_request_channel(fa);
if (err)
goto out_dma;
/* init all subsystems */
for (i = 0, m = mods; i < ARRAY_SIZE(mods); i++, m++) {
dev_dbg(fa->msgdev, "Calling init for \"%s\"\n", m->name);
err = m->init(fa);
if (err) {
dev_err(fa->msgdev, "error initializing %s\n", m->name);
goto out;
}
}
/* time to execute specific driver init */
err = __fa_init(fa);
if (err < 0)
goto out;
err = fa_setup_irqs(fa);
if (err < 0)
goto out_irq;
return 0;
out_irq:
out:
while (--m, --i >= 0)
if (m->exit)
m->exit(fa);
iounmap(fa->fa_top_level);
fa_dma_release_channel(fa);
out_dma:
out_fmc_err:
out_fmc_eeprom:
out_fmc_pre:
fmc_slot_put(fa->slot);
out_fmc:
devm_kfree(&pdev->dev, fa);
platform_set_drvdata(pdev, NULL);
return err;
}
int fa_remove(struct platform_device *pdev)
{
struct fa_dev *fa = platform_get_drvdata(pdev);
struct fa_modlist *m;
int i = ARRAY_SIZE(mods);
if (WARN(!fa, "asked to remove fmc-adc-100m device but it does not exists\n"))
return 0;
fa_free_irqs(fa);
flush_workqueue(fa_workqueue);
while (--i >= 0) {
m = mods + i;
if (m->exit)
m->exit(fa);
}
fa_dma_release_channel(fa);
iounmap(fa->fa_top_level);
fmc_slot_put(fa->slot);
return 0;
}
static const struct platform_device_id fa_id[] = {
{
.name = "fmc-adc-100m",
.driver_data = ADC_VER,
},
{},
/* TODO we should support different version */
};
static struct platform_driver fa_dev_drv = {
.driver = {
.name = KBUILD_MODNAME,
},
.probe = fa_probe,
.remove = fa_remove,
.id_table = fa_id,
};
static int fa_init(void)
{
int ret;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,15,0)
fa_workqueue = alloc_workqueue(fa_dev_drv.driver.name,
WQ_NON_REENTRANT | WQ_UNBOUND |
WQ_MEM_RECLAIM, 1);
#else
fa_workqueue = alloc_workqueue(fa_dev_drv.driver.name,
WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
#endif
if (fa_workqueue == NULL)
return -ENOMEM;
/* First trigger and zio driver */
ret = fa_trig_init();
if (ret)
goto out1;
ret = fa_zio_register();
if (ret)
goto out2;
/* Finally the fmc driver, whose probe instantiates zio devices */
ret = platform_driver_register(&fa_dev_drv);
if (ret)
goto out3;
return ret;
out3:
fa_zio_unregister();
out2:
fa_trig_exit();
out1:
destroy_workqueue(fa_workqueue);
return ret;
}
static void fa_exit(void)
{
platform_driver_unregister(&fa_dev_drv);
fa_zio_unregister();
fa_trig_exit();
if (fa_workqueue != NULL)
destroy_workqueue(fa_workqueue);
}
module_init(fa_init);
module_exit(fa_exit);
MODULE_AUTHOR("Federico Vaga");
MODULE_DESCRIPTION("FMC-ADC-100MS-14b Linux Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(VERSION);
MODULE_DEVICE_TABLE(platform, fa_id);
ADDITIONAL_VERSIONS;
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright CERN 2018-2019
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include "fmc-adc-100m14b4cha.h"
#define FA_DBG_REG32_CH(_n) \
{.name = "ADC-CSR:ch"#_n"_ctl", .offset = ADC_CSR_OFF + 0x080 + ((_n - 1) * 0x40)}, \
{.name = "ADC-CSR:ch"#_n"_sta", .offset = ADC_CSR_OFF + 0x084 + ((_n - 1) * 0x40)}, \
{.name = "ADC-CSR:ch"#_n"_cal_nb", .offset = ADC_CSR_OFF + 0x088 + ((_n - 1) * 0x40)}, \
{.name = "ADC-CSR:ch"#_n"_sat", .offset = ADC_CSR_OFF + 0x08C + ((_n - 1) * 0x40)}, \
{.name = "ADC-CSR:ch"#_n"_trig_thres", .offset = ADC_CSR_OFF + 0x090 + ((_n - 1) * 0x40)}, \
{.name = "ADC-CSR:ch"#_n"_trig_dly", .offset = ADC_CSR_OFF + 0x094 + ((_n - 1) * 0x40)}
#define FA_DBG_REG32_TIM(_name, _off) \
{ \
.name = "TIME-TAG:"#_name"_seconds_upper", \
.offset = ADC_UTC_OFF + _off \
}, { \
.name = "TIME-TAG:"#_name"_seconds_lower", \
.offset = ADC_UTC_OFF + _off + 0x4, \
}, { \
.name = "TIME-TAG:"#_name"_coarse", \
.offset = ADC_UTC_OFF + _off + 0x8, \
}
static const struct debugfs_reg32 fa_debugfs_reg32[] = {
{
.name = "ADC-CSR:ctl",
.offset = ADC_CSR_OFF + 0x000,
},
{
.name = "ADC-CSR:sta",
.offset = ADC_CSR_OFF + 0x004,
},
{
.name = "ADC-CSR:trig_stat",
.offset = ADC_CSR_OFF + 0x008,
},
{
.name = "ADC-CSR:trig_en",
.offset = ADC_CSR_OFF + 0x00C,
},
{
.name = "ADC-CSR:trig_pol",
.offset = ADC_CSR_OFF + 0x010,
},
{
.name = "ADC-CSR:ext_trig_dly",
.offset = ADC_CSR_OFF + 0x014,
},
{
.name = "ADC-CSR:sw_trig",
.offset = ADC_CSR_OFF + 0x018,
},
{
.name = "ADC-CSR:shots",
.offset = ADC_CSR_OFF + 0x01C,
},
{
.name = "ADC-CSR:multi_depth",
.offset = ADC_CSR_OFF + 0x020,
},
{
.name = "ADC-CSR:trig_pos",
.offset = ADC_CSR_OFF + 0x024,
},
{
.name = "ADC-CSR:fs_freq",
.offset = ADC_CSR_OFF + 0x028,
},
{
.name = "ADC-CSR:downsample",
.offset = ADC_CSR_OFF + 0x02C,
},
{
.name = "ADC-CSR:pre_samples",
.offset = ADC_CSR_OFF + 0x030,
},
{
.name = "ADC-CSR:post_samples",
.offset = ADC_CSR_OFF + 0x034,
},
{
.name = "ADC-CSR:samples_cnt",
.offset = ADC_CSR_OFF + 0x038,
},
FA_DBG_REG32_CH(1),
FA_DBG_REG32_CH(2),
FA_DBG_REG32_CH(3),
FA_DBG_REG32_CH(4),
{
.name = "ADC-EIC:disable_mask",
.offset = ADC_EIC_OFF + 0x0,
},
{
.name = "ADC-EIC:enable_mask",
.offset = ADC_EIC_OFF + 0x4,
},
{
.name = "ADC-EIC:status_mask",
.offset = ADC_EIC_OFF + 0x8,
},
{
.name = "ADC-EIC:source",
.offset = ADC_EIC_OFF + 0xC,
},
FA_DBG_REG32_TIM(base_time, 0x00),
FA_DBG_REG32_TIM(time_trig, 0x0C),
FA_DBG_REG32_TIM(trig_tag, 0x18),
FA_DBG_REG32_TIM(acq_start_tag, 0x24),
FA_DBG_REG32_TIM(acq_stop_tag, 0x30),
FA_DBG_REG32_TIM(acq_end_tag, 0x3C),
};
static void fa_regdump_seq_read_spi(struct fa_dev *fa, struct seq_file *s)
{
int i, err;
seq_printf(s, "ADC SPI registers\n");
seq_printf(s, "Address Data\n");
for (i = 0; i < 5; ++i) {
uint32_t tx, rx;
tx = 0x8000 | (i << 8);
err = fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, tx, &rx);
rx &= 0xFF; /* the value is 8bit */
if (err)
seq_printf(s, "A%d %02xh read failure!\n",
i, i);
else
seq_printf(s, "A%d %02xh 0x%02x\n",
i, i, rx);
}
}
static int fa_regdump_seq_read(struct seq_file *s, void *data)
{
struct fa_dev *fa = s->private;
fa_regdump_seq_read_spi(fa, s);
return 0;
}
static int fa_regdump_open(struct inode *inode, struct file *file)
{
return single_open(file, fa_regdump_seq_read, inode->i_private);
}
static const struct file_operations fa_regdump_ops = {
.owner = THIS_MODULE,
.open = fa_regdump_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static ssize_t fa_trg_sw_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct fa_dev *fa = file->private_data;
int err;
err = fa_trigger_software(fa);
return err ? err : count;
}
static const struct file_operations fa_trg_sw_ops = {
.owner = THIS_MODULE,
.open = simple_open,
.write = fa_trg_sw_write,
};
#define FA_ADC_DATA_PATTERN_CMD_SIZE 16UL
static int fa_data_pattern_adc_write(struct fa_dev *fa, const char __user *buf,
size_t count)
{
char buf_l[FA_ADC_DATA_PATTERN_CMD_SIZE];
int err;
memset(buf_l, 0, FA_ADC_DATA_PATTERN_CMD_SIZE);
err = copy_from_user(buf_l, buf, min(count,
FA_ADC_DATA_PATTERN_CMD_SIZE));
if (err)
return -EFAULT;
if ((count == 1 || count == 2)&& buf_l[0] == '0') {
err = fa_adc_data_pattern_set(fa, 0, 0);
fa_calib_init(fa);
return err;
} else if (count > 2 && buf_l[0] == '1' && buf_l[1] == ' ') {
uint16_t pattern = 0;
err = kstrtou16(buf_l + 2, 0, &pattern);
if (err)
return err;
err = fa_adc_data_pattern_set(fa, pattern & 0x3FFF, 1);
fa_calib_exit(fa);
return err;
} else {
return -EINVAL;
}
}
static ssize_t fa_data_pattern_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct fa_dev *fa = file->private_data;
if (strncmp(buf, "adc ", 4) == 0) {
int err;
err = fa_data_pattern_adc_write(fa, buf + 4, count - 4);
return err ? err : count;
} else {
dev_err(&fa->pdev->dev, "Unknown command \"%s\"\n", buf);
return -EINVAL;
}
}
static ssize_t fa_data_pattern_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
struct fa_dev *fa = file->private_data;
char buf_l[FA_ADC_DATA_PATTERN_CMD_SIZE];
uint16_t pattern;
unsigned int enable;
int err;
if (*ppos > 0)
return 0;
err = fa_adc_data_pattern_get(fa, &pattern, &enable);
if (err)
return err;
snprintf(buf_l, FA_ADC_DATA_PATTERN_CMD_SIZE, "adc %d 0x%02x\n",
enable, pattern);
count = min(count, strlen(buf_l));
err = copy_to_user(buf, buf_l, count);
*ppos += count;
return err ? err : count;
}
static const struct file_operations fa_data_pattern_ops = {
.owner = THIS_MODULE,
.open = simple_open,
.write = fa_data_pattern_write,
.read = fa_data_pattern_read,
};
int fa_debug_init(struct fa_dev *fa)
{
int err;
fa->dbg_dir = debugfs_create_dir(dev_name(&fa->pdev->dev), NULL);
if (IS_ERR_OR_NULL(fa->dbg_dir)) {
err = PTR_ERR(fa->dbg_dir);
dev_err(&fa->pdev->dev,
"Cannot create debugfs directory \"%s\" (%d)\n",
dev_name(&fa->zdev->head.dev), err);
return err;
}
fa->dbg_reg32.regs = fa_debugfs_reg32;
fa->dbg_reg32.nregs = ARRAY_SIZE(fa_debugfs_reg32);
fa->dbg_reg32.base = fa->fa_top_level;
fa->dbg_reg = debugfs_create_regset32("regs", 0444, fa->dbg_dir,
&fa->dbg_reg32);
if (IS_ERR_OR_NULL(fa->dbg_reg)) {
err = PTR_ERR(fa->dbg_reg);
dev_warn(&fa->pdev->dev,
"Cannot create debugfs file \"regs\" (%d)\n",
err);
}
fa->dbg_reg_spi = debugfs_create_file("spi-regs", 0444,
fa->dbg_dir, fa,
&fa_regdump_ops);
if (IS_ERR_OR_NULL(fa->dbg_reg_spi)) {
dev_warn(&fa->pdev->dev,
"Cannot create regdump debugfs file\n");
}
fa->dbg_trg_sw = debugfs_create_file("trigger_software", 0200,
fa->dbg_dir, fa,
&fa_trg_sw_ops);
if (IS_ERR_OR_NULL(fa->dbg_trg_sw)) {
dev_warn(&fa->pdev->dev,
"Cannot create software trigger file\n");
}
fa->dbg_data_pattern = debugfs_create_file("data_pattern", 0644,
fa->dbg_dir, fa,
&fa_data_pattern_ops);
if (IS_ERR_OR_NULL(fa->dbg_data_pattern)) {
dev_warn(&fa->pdev->dev,
"Cannot create ADC data pattern file\n");
}
return 0;
}
void fa_debug_exit(struct fa_dev *fa)
{
debugfs_remove_recursive(fa->dbg_dir);
}
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright CERN 2012-2019
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <linux/errno.h>
#ifdef CONFIG_FMC_ADC_SVEC
#include "vmebus.h"
#endif
#include "fmc-adc-100m14b4cha.h"
/* Endianess */
#ifndef LITTLE_ENDIAN
#define LITTLE_ENDIAN 0
#endif
#ifndef BIG_ENDIAN
#define BIG_ENDIAN 1
#endif
static void zfad_dma_done(struct zio_cset *cset);
static int __get_endian(void)
{
int i = 1;
char *p = (char *)&i;
if (p[0] == 1)
return LITTLE_ENDIAN;
else
return BIG_ENDIAN;
}
/**
* Fix endianess from big to host endianess (32bit)
*/
static void __endianness(unsigned int byte_length, void *buffer)
{
int i, size;
uint32_t *ptr;
/* CPU may be little endian, VME is big endian */
if (__get_endian() == LITTLE_ENDIAN) {
ptr = buffer;
/* swap samples and trig timetag all seen as 32bits words */
size = byte_length/4;
for (i = 0; i < size; ++i, ++ptr)
*ptr = __be32_to_cpu(*ptr);
}
}
struct zfad_timetag {
uint32_t sec_low;
uint32_t sec_high;
uint32_t ticks;
uint32_t status;
};
/**
* It matches a valid DMA channel
*/
static bool fa_dmaengine_filter(struct dma_chan *dchan, void *arg)
{
struct dma_device *ddev = dchan->device;
int dev_id = (*((int *)arg) >> 16) & 0xFFFF;
int chan_id = *((int *)arg) & 0xFFFF;
return ddev->dev_id == dev_id && dchan->chan_id == chan_id;
}
static bool fa_dmaengine_filter_svec(struct dma_chan *dchan, void *arg)
{
struct fa_dev *fa = arg;
struct device *device_ref;
device_ref = fa->pdev->dev.parent->parent->parent->parent->parent->parent;
return (dchan->device->dev == device_ref);
}
int fa_dma_request_channel(struct fa_dev *fa)
{
dma_cap_mask_t dma_mask;
struct resource *r;
int dma_dev_id;
if (fa_is_flag_set(fa, FMC_ADC_SVEC))
return 0;
r = platform_get_resource(fa->pdev, IORESOURCE_DMA, ADC_DMA);
if (!r) {
dev_err(&fa->pdev->dev, "Can't set find DMA channel\n");
return -ENODEV;
}
dma_dev_id = r->start;
dma_cap_zero(dma_mask);
dma_cap_set(DMA_SLAVE, dma_mask);
dma_cap_set(DMA_PRIVATE, dma_mask);
fa->dchan = dma_request_channel(dma_mask,
fa_dmaengine_filter, &dma_dev_id);
if (!fa->dchan)
return -ENODEV;
return 0;
}
/**
* Remove this function and its use when VME Bridge will support virtual
* DMA channels. Then we will request a channel only once at probe like
* on SPEC
*/
static int fa_dma_request_channel_svec(struct fa_dev *fa)
{
dma_cap_mask_t dma_mask;
if (!fa_is_flag_set(fa, FMC_ADC_SVEC))
return 0;
dma_cap_zero(dma_mask);
dma_cap_set(DMA_SLAVE, dma_mask);
dma_cap_set(DMA_PRIVATE, dma_mask);
fa->dchan = dma_request_channel(dma_mask,
fa_dmaengine_filter_svec, fa);
if (!fa->dchan)
return -ENODEV;
return 0;
}
static void __fa_dma_release_channel(struct fa_dev *fa)
{
dma_release_channel(fa->dchan);
}
void fa_dma_release_channel(struct fa_dev *fa)
{
if (fa_is_flag_set(fa, FMC_ADC_SVEC))
return;
__fa_dma_release_channel(fa);
}
/**
* Remove this function and its use when VME Bridge will support virtual
* DMA channels. Then we will request a channel only once at probe like
* on SPEC
*/
static void fa_dma_release_channel_svec(struct fa_dev *fa)
{
if (fa_is_flag_set(fa, FMC_ADC_SVEC))
__fa_dma_release_channel(fa);
}
static uint32_t fa_ddr_offset_single(struct fa_dev *fa)
{
struct zio_cset *cset = fa->zdev->cset;
uint32_t off, trg_pos, pre_samp;
int nchan = FA100M14B4C_NCHAN;
struct zio_control *ctrl = cset->chan[nchan].current_ctrl;
/* get pre-samples from the current control (interleave chan) */
pre_samp = ctrl->attr_trigger.std_val[ZIO_ATTR_TRIG_PRE_SAMP];
/* Get trigger position in DDR */
trg_pos = fa_readl(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFAT_POS]);
/*
* compute mem offset (in bytes): pre-samp is converted to
* bytes
*/
off = trg_pos - (pre_samp * cset->ssize * nchan);
dev_dbg(fa->msgdev,
"Trigger @ 0x%08x, pre_samp %i, offset 0x%08x\n",
trg_pos, pre_samp, off);
return off;
}
static uint32_t fa_ddr_offset_multi(struct fa_dev *fa, uint32_t shot_n)
{
struct zio_cset *cset = fa->zdev->cset;
uint32_t off;
off = cset->interleave->current_ctrl->ssize * cset->ti->nsamples;
off += FA_TRIG_TIMETAG_BYTES;
off *= shot_n;
return off;
}
static uint32_t fa_ddr_offset(struct fa_dev *fa, uint32_t shot_n)
{
WARN(fa->n_shots == 1 && shot_n != 0,
"Inconsistent shot number %d\n", shot_n);
if (fa->n_shots == 1) {
return fa_ddr_offset_single(fa);
} else {
return fa_ddr_offset_multi(fa, shot_n);
}
}
static unsigned int zfad_block_n_pages(struct zio_block *block)
{
unsigned int nr_pages;
long kaddr = (long)block->data;
nr_pages = ((kaddr & ~PAGE_MASK) + block->datalen + ~PAGE_MASK);
nr_pages >>= PAGE_SHIFT;
return nr_pages;
}
#ifdef CONFIG_FMC_ADC_SVEC
#define ADC_VME_DDR_ADDR 0x00
#define ADC_VME_DDR_DATA 0x04
#define SVEC_FUNC_NR 1 /* HARD coded in SVEC */
static unsigned long fa_ddr_data_vme_addr(struct fa_dev *fa)
{
struct fmc_adc_platform_data *data = fa->pdev->dev.platform_data;
struct vme_dev *vdev;
unsigned long addr;
vdev = to_vme_dev(fa->pdev->dev.parent->parent->parent->parent);
if (WARN(vdev->map[SVEC_FUNC_NR].kernel_va == NULL,
"Invalid VME function\n"))
return ~0; /* invalid address, we will see VME errors */
WARN(data->vme_ddr_offset == 0, "Invalid DDR DATA offset");
addr = vdev->map[SVEC_FUNC_NR].vme_addrl;
addr += data->vme_ddr_offset + ADC_VME_DDR_DATA;
return addr;
}
static void *fa_ddr_addr_reg_off(struct fa_dev *fa)
{
struct fmc_adc_platform_data *data = fa->pdev->dev.platform_data;
struct vme_dev *vdev;
void *addr;
vdev = to_vme_dev(fa->pdev->dev.parent->parent->parent->parent);
if (WARN(vdev->map[SVEC_FUNC_NR].kernel_va == NULL,
"Invalid VME function\n"))
return NULL; /* invalid address, we will see VME errors */
addr = vdev->map[SVEC_FUNC_NR].kernel_va;
addr += data->vme_ddr_offset + ADC_VME_DDR_ADDR;
return addr;
}
#define VME_NO_ADDR_INCREMENT 1
/* FIXME: move to include again */
#ifndef lower_32_bits
#define lower_32_bits(n) ((u32)(n))
#endif /* lower_32_bits */
static void build_dma_desc(struct vme_dma *desc, unsigned long vme_addr,
void *addr_dest, ssize_t len)
{
struct vme_dma_attr *vme;
struct vme_dma_attr *pci;
memset(desc, 0, sizeof(struct vme_dma));
vme = &desc->src;
pci = &desc->dst;
desc->dir = VME_DMA_FROM_DEVICE;
desc->length = len;
desc->novmeinc = VME_NO_ADDR_INCREMENT;
desc->ctrl.pci_block_size = VME_DMA_BSIZE_4096;
desc->ctrl.pci_backoff_time = VME_DMA_BACKOFF_0;
desc->ctrl.vme_block_size = VME_DMA_BSIZE_4096;
desc->ctrl.vme_backoff_time = VME_DMA_BACKOFF_0;
vme->data_width = VME_D32;
vme->am = VME_A24_USER_DATA_SCT;
/*vme->am = VME_A24_USER_MBLT;*/
vme->addru = upper_32_bits(vme_addr);
vme->addrl = lower_32_bits(vme_addr);
pci->addru = upper_32_bits((unsigned long)addr_dest);
pci->addrl = lower_32_bits((unsigned long)addr_dest);
}
#endif
static int zfad_dma_block_to_pages(struct page **pages, unsigned int nr_pages,
struct zio_block *block)
{
int i;
void *data = (void *) block->data;
if (is_vmalloc_addr(data)) {
for (i = 0; i < nr_pages; ++i)
pages[i] = vmalloc_to_page(data + PAGE_SIZE * i);
} else {
for (i = 0; i < nr_pages; ++i)
pages[i] = virt_to_page(data + PAGE_SIZE * i);
}
return 0;
}
static void zfad_dma_context_exit_svec(struct zio_cset *cset,
struct zfad_block *zfad_block)
{
__endianness(zfad_block->block->datalen,
zfad_block->block->data);
kfree(zfad_block->dma_ctx);
}
static void zfad_dma_context_exit(struct zio_cset *cset,
struct zfad_block *zfad_block)
{
struct fa_dev *fa = cset->zdev->priv_d;
if (fa_is_flag_set(fa, FMC_ADC_SVEC))
zfad_dma_context_exit_svec(cset, zfad_block);
}
static int zfad_dma_context_init_svec(struct zio_cset *cset,
struct zfad_block *zfad_block)
{
#ifdef CONFIG_FMC_ADC_SVEC
struct fa_dev *fa = cset->zdev->priv_d;
struct vme_dma *desc;
int err;
dev_dbg(&fa->pdev->dev, "SVEC build DMA context\n");
desc = kmalloc(sizeof(struct vme_dma), GFP_ATOMIC);
if (!desc)
return -ENOMEM;
if (zfad_block == cset->interleave->priv_d) {
void *addr = fa_ddr_addr_reg_off(fa);
if (!addr) {
err = -ENODEV;
goto err_reg_addr;
}
/*
* Only for the first block:
* write the data address in the ddr_addr register: this
* address has been computed after ACQ_END by looking to the
* trigger position see fa-irq.c::irq_acq_end.
* Be careful: the SVEC HW version expects an address of 32bits word
* therefore mem-offset in byte is translated into 32bit word
*/
fa_iowrite(fa, zfad_block->sconfig.src_addr / 4, addr);
}
zfad_block->dma_ctx = desc;
build_dma_desc(desc, fa_ddr_data_vme_addr(fa),
zfad_block->block->data,
zfad_block->block->datalen);
return 0;
err_reg_addr:
kfree(desc);
zfad_block->dma_ctx = NULL;
return err;
#else
return 0;
#endif
}
/**
* It initialize the DMA context for the given block transfer
*/
static int zfad_dma_context_init(struct zio_cset *cset,
struct zfad_block *zfad_block)
{
struct fa_dev *fa = cset->zdev->priv_d;
if (fa_is_flag_set(fa, FMC_ADC_SVEC))
return zfad_dma_context_init_svec(cset, zfad_block);
return 0;
}
/**
* The proper function from the DMA engine does not allow us to set
* the context, but we need it (e.g. VME bus)
*/
static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_sg_ctx(
struct dma_chan *chan,
struct scatterlist *sgl, unsigned int sg_len,
enum dma_transfer_direction dir,
unsigned long flags, void *ctx)
{
if (!(chan->device && chan->device->device_prep_slave_sg))
return NULL;
return chan->device->device_prep_slave_sg(chan, sgl, sg_len,
DMA_DEV_TO_MEM, 0, ctx);
}
/**
* zfad_dma_complete
* @arg: data block instance
*
* It handles the data transfer completion of a block
*/
static void fa_dma_complete(void *arg,
const struct dmaengine_result *result)
{
struct zfad_block *zfad_block = arg;
struct zio_cset *cset = zfad_block->cset;
struct fa_dev *fa = cset->zdev->priv_d;
if (result->result != DMA_TRANS_NOERROR)
dev_err(&fa->pdev->dev, "DMA failed %d\n", result->result);
/* Release DMA resources */
dma_unmap_sg(&fa->pdev->dev,
zfad_block->sgt.sgl,
zfad_block->sgt.nents,
DMA_DEV_TO_MEM);
sg_free_table(&zfad_block->sgt);
/* Clean/fix the context */
zfad_dma_context_exit(cset, zfad_block);
/* Complete the full acquisition when the last transfer is over */
--fa->transfers_left;
if (!fa->transfers_left) {
fa_dma_release_channel_svec(fa);
zfad_dma_done(cset);
}
}
/**
* zfad_dma_prep_slave_sg
* @dchan: DMA channel to use
* @cset: ZIO channel set that owns the data
* @zfad_block: data block instance to transfer
*
* It prepare the scatterlist for the block transfer and it submits it
* to the dma engine.
*/
static int zfad_dma_prep_slave_sg(struct dma_chan *dchan,
struct zio_cset *cset,
struct zfad_block *zfad_block)
{
struct fa_dev *fa = cset->zdev->priv_d;
struct dma_async_tx_descriptor *tx;
struct page **pages;
unsigned int nr_pages, sg_mapped;
size_t max_segment_size;
int err;
/* prepare the context for the block transfer */
zfad_dma_context_init(cset, zfad_block);
/* Convert buffer to pages */
nr_pages = zfad_block_n_pages(zfad_block->block);
pages = kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL);
if (!pages) {
err = -ENOMEM;
goto err_alloc_pages;
}
err = zfad_dma_block_to_pages(pages, nr_pages, zfad_block->block);
if (err)
goto err_to_pages;
max_segment_size = min(zfad_block->block->datalen + PAGE_SIZE, /* PAGE aligned */
(size_t)dma_get_max_seg_size(dchan->device->dev));
/* Find something that fits in the [SW-]IOMMU */
do {
dev_dbg(&fa->pdev->dev, "DMA max segment %ld\n",
max_segment_size);
max_segment_size &= PAGE_MASK; /* to make alloc_table happy */
err = fa->sg_alloc_table_from_pages(&zfad_block->sgt, pages,
nr_pages,
offset_in_page(zfad_block->block->data),
zfad_block->block->datalen,
max_segment_size,
GFP_KERNEL);
if (unlikely(err))
goto err_sgt;
sg_mapped = dma_map_sg(&fa->pdev->dev,
zfad_block->sgt.sgl,
zfad_block->sgt.nents,
DMA_DEV_TO_MEM);
if (sg_mapped <= 0) {
sg_free_table(&zfad_block->sgt);
err = sg_mapped ? sg_mapped : -ENOMEM;
max_segment_size /= 2;
} else {
err = 0;
break;
}
} while (max_segment_size >= PAGE_SIZE);
if (err)
goto err_map;
/* Prepare the DMA transmisison */
tx = dmaengine_prep_slave_sg_ctx(dchan, zfad_block->sgt.sgl, sg_mapped,
DMA_DEV_TO_MEM, 0, zfad_block->dma_ctx);
if (!tx) {
dev_err(&cset->head.dev,
"Failed to prepare dmaengine transfer descriptor\n");
return -EBUSY;
}
tx->callback_result = fa_dma_complete;
tx->callback_param = (void *)zfad_block;
zfad_block->tx = tx;
/* Submit the DMA transmission to the DMA engine */
zfad_block->cookie = dmaengine_submit(tx);
if (zfad_block->cookie < 0) {
err = zfad_block->cookie;
goto err_submit;
}
/* we do not need the pages anymore */
kfree(pages);
return 0;
err_submit:
dma_unmap_sg(&fa->pdev->dev,
zfad_block->sgt.sgl,
zfad_block->sgt.nents,
DMA_DEV_TO_MEM);
err_map:
sg_free_table(&zfad_block->sgt);
err_sgt:
err_to_pages:
kfree(pages);
err_alloc_pages:
return err;
}
static int fa_dmaengine_slave_config(struct fa_dev *fa,
struct dma_slave_config *sconfig)
{
/*
* For SVEC we must set the DMA context, there is not slave config
*/
if (fa_is_flag_set(fa, FMC_ADC_SVEC))
return 0;
return dmaengine_slave_config(fa->dchan, sconfig);
}
/**
* It maps the ZIO blocks with an sg table, then it starts the DMA transfer
* from the ADC to the host memory.
*
* @param cset
*/
static int zfad_dma_start(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
struct zfad_block *zfad_block = cset->interleave->priv_d;
int err, i;
err = fa_fsm_wait_state(fa, FA100M14B4C_STATE_IDLE, 10);
if (err) {
dev_warn(fa->msgdev,
"Can't start DMA on the last acquisition, "
"State Machine is not IDLE\n");
return err;
}
dev_dbg(fa->msgdev, "Start DMA transfer\n");
err = fa_dma_request_channel_svec(fa);
if (err)
return err;
/*
* Disable all triggers to prevent fires between
* different DMA transfers required for multi-shots
*/
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SRC], 0);
for (i = 0; i < fa->n_shots; ++i) {
/*
* TODO
* Let's see what to do with SVEC. SVEC need to set
* the DMA_DDR_ADDR in hardware before starting the DMA
* (it configures the DMA window).
* In single shot is not a big deal, in multishot we may have
* to issue_pending many time since we can't update the
* DMA_DDR_ADDR for each block submitted to the dma engine.
* But sice the blocks are contigous, perhaps there is no need
* because the address of shot 2 is exactly after shot 1
*/
zfad_block[i].sconfig.direction = DMA_DEV_TO_MEM;
zfad_block[i].sconfig.src_addr_width = 8; /* 2 bytes for each channel (4) */
zfad_block[i].sconfig.src_addr = fa_ddr_offset(fa, i);
err = fa_dmaengine_slave_config(fa, &zfad_block[i].sconfig);
if (err)
goto err_config;
err = zfad_dma_prep_slave_sg(fa->dchan, cset, &zfad_block[i]);
if (err)
goto err_prep;
}
fa->transfers_left = fa->n_shots;
dma_async_issue_pending(fa->dchan);
return 0;
err_prep:
err_config:
dmaengine_terminate_all(fa->dchan);
fa_dma_release_channel_svec(fa);
while (--i >= 0) {
dma_unmap_sg(&fa->pdev->dev,
zfad_block[i].sgt.sgl,
zfad_block[i].sgt.nents,
DMA_DEV_TO_MEM);
sg_free_table(&zfad_block[i].sgt);
}
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SRC],
cset->ti->zattr_set.ext_zattr[FA100M14B4C_TATTR_SRC].value);
dev_err(fa->msgdev, "Failed to run a DMA transfer (%d)\n", err);
return err;
}
static void zfad_tstamp_start_get(struct fa_dev *fa,
struct zio_timestamp *ztstamp)
{
ztstamp->secs = fa_readl(fa, fa->fa_utc_base,
&zfad_regs[ZFA_UTC_ACQ_START_SECONDS_U]);
ztstamp->secs <<= 32;
ztstamp->secs |= fa_readl(fa, fa->fa_utc_base,
&zfad_regs[ZFA_UTC_ACQ_START_SECONDS_L]);
ztstamp->ticks = fa_readl(fa, fa->fa_utc_base,
&zfad_regs[ZFA_UTC_ACQ_START_COARSE]);
ztstamp->bins = 0;
}
static int zfad_block_timetag_extract(struct zio_block *block,
struct zfad_timetag *timetag)
{
struct zfad_timetag *tg;
tg = block->data + block->datalen - FA_TRIG_TIMETAG_BYTES;
if (unlikely((tg->sec_high >> 8) != 0xACCE55))
return -EINVAL;
/* resize the datalen, by removing the trigger tstamp */
block->datalen = block->datalen - FA_TRIG_TIMETAG_BYTES;
memcpy(timetag, tg, sizeof(*timetag));
return 0;
}
static void zfad_block_ctrl_tstamp_update(struct zio_block *block,
struct zfad_timetag *timetag)
{
struct zio_control *ctrl = zio_get_ctrl(block);
struct zio_timestamp *ztstamp = &ctrl->tstamp;
ztstamp->secs = ((uint64_t)timetag->sec_high & 0xFF) << 32;
ztstamp->secs |= timetag->sec_low;
ztstamp->ticks = timetag->ticks;
ztstamp->bins = 0;
}
static void zfad_block_ctrl_attr_update(struct zio_block *block,
struct zfad_timetag *timetag,
unsigned int seq_num)
{
struct zio_control *ctrl = zio_get_ctrl(block);
uint32_t *ext_val = ctrl->attr_channel.ext_val;
ext_val[FA100M14B4C_TATTR_STA]= timetag->status;
ctrl->seq_num = seq_num;
}
static void zfad_block_ctrl_tstamp_start_update(struct zio_block *block,
struct zio_timestamp *ztstamp)
{
struct zio_control *ctrl = zio_get_ctrl(block);
uint32_t *ext_val;
ext_val = ctrl->attr_channel.ext_val;
ext_val[FA100M14B4C_DATTR_ACQ_START_S] = ztstamp->secs;
ext_val[FA100M14B4C_DATTR_ACQ_START_C] = ztstamp->ticks;
ext_val[FA100M14B4C_DATTR_ACQ_START_F] = ztstamp->bins;
}
static void zfad_curr_ctrl_sync(struct zio_cset *cset,
struct zio_block *block)
{
struct zio_channel *interleave = cset->interleave;
struct zio_control *ctrl;
if (WARN(!block, "Missing block\n"))
return;
ctrl = zio_get_ctrl(block);
/* Sync the channel current control with the last ctrl block*/
memcpy(&interleave->current_ctrl->tstamp,
&ctrl->tstamp, sizeof(struct zio_timestamp));
interleave->current_ctrl->seq_num = ctrl->seq_num;
}
/**
* It completes a DMA transfer.
* It tells to the ZIO framework that all blocks are done. Then, it re-enable
* the trigger for the next acquisition. If the device is configured for
* continuous acquisition, the function automatically start the next
* acquisition
*
* @param cset
*/
static void zfad_dma_done(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
struct zfad_block *zfad_block = cset->interleave->priv_d;
struct zio_ti *ti = cset->ti;
struct zio_block *block = NULL;
struct zio_timestamp ztstamp;
int i;
/*
* Lower CSET_HW_BUSY
*/
spin_lock(&cset->lock);
cset->flags &= ~ZIO_CSET_HW_BUSY;
spin_unlock(&cset->lock);
/* for each shot, set the timetag of each ctrl block by reading the
* trig-timetag appended after the samples. Set also the acquisition
* start timetag on every blocks
*/
zfad_tstamp_start_get(fa, &ztstamp);
for (i = 0; i < fa->n_shots; ++i) {
struct zfad_timetag timetag;
int err;
block = zfad_block[i].block;
err = zfad_block_timetag_extract(block, &timetag);
if (err)
memset(&timetag, 0, sizeof(timetag));
zfad_block_ctrl_tstamp_start_update(block, &ztstamp);
zfad_block_ctrl_tstamp_update(block, &timetag);
zfad_block_ctrl_attr_update(block, &timetag, i);
}
zfad_curr_ctrl_sync(cset, block);
/*
* All DMA transfers done! Inform the trigger about this, so
* it can store blocks into the buffer
*/
dev_dbg(fa->msgdev, "%i blocks transfered\n", fa->n_shots);
zio_trigger_data_done(cset);
/* we can safely re-enable triggers */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SRC],
ti->zattr_set.ext_zattr[FA100M14B4C_TATTR_SRC].value);
if (fa->enable_auto_start) {
/* Automatic start next acquisition */
dev_dbg(fa->msgdev, "Automatic start\n");
zfad_fsm_command(fa, FA100M14B4C_CMD_START);
}
}
/**
* It handles the error condition of a DMA transfer.
* The function turn off the state machine by sending the STOP command
*
* @param cset
*/
static void zfad_dma_error(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
/*
* Lower CSET_HW_BUSY
*/
spin_lock(&cset->lock);
cset->flags &= ~ZIO_CSET_HW_BUSY;
spin_unlock(&cset->lock);
zfad_fsm_command(fa, FA100M14B4C_CMD_STOP);
fa->n_dma_err++;
/* FIXME stop pending */
//dmaengine_terminate_all();
if (fa->n_fires == 0)
dev_err(fa->msgdev,
"DMA error occurs but no block was acquired\n");
}
/*
* job executed within a work thread
* Depending of the carrier the job slightly differs:
* SVEC: dma_start() blocks till the the DMA ends
* (fully managed by the vmebus driver)
* Therefore the DMA outcome can be processed immediately
* SPEC: dma_start() launch the job an returns immediately.
* An interrupt DMA_DONE or ERROR is expecting to signal the end
* of the DMA transaction
* (See fa-spec-irq.c::fa-spec_irq_handler)
*/
void fa_irq_work(struct work_struct *work)
{
struct fa_dev *fa = container_of(work, struct fa_dev, irq_work);
struct zio_cset *cset = fa->zdev->cset;
int res;
/*
* This check is not crucial because the HW implements
* a solid state machine and acq-end can happens only after
* the execution of the n requested shots.
*/
fa->n_fires = fa->n_shots - fa_readl(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFAT_SHOTS_REM]);
if (fa->n_fires != fa->n_shots) {
dev_err(fa->msgdev,
"Expected %i trigger fires, but %i occurs\n",
fa->n_shots, fa->n_fires);
}
res = zfad_dma_start(cset);
if (res) {
/* Stop acquisition on error */
zfad_dma_error(cset);
}
}
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright CERN 2012-2019
* Author: Federico Vaga <federico.vaga@gmail.com>
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include "fmc-adc-100m14b4cha.h"
/*
* fat_get_irq_status
* @fa: adc descriptor
* @irq_status: destination of irq status
*
* Get irq and clear the register. To clear an interrupt we have to write 1
* on the handled interrupt. We handle all interrupt so we clear all interrupts
*/
static void fa_get_irq_status(struct fa_dev *fa, uint32_t *irq_status)
{
/* Get current interrupts status */
*irq_status = fa_readl(fa, fa->fa_irq_adc_base, &zfad_regs[ZFA_IRQ_ADC_SRC]);
dev_dbg(fa->msgdev,
"IRQ fired an interrupt. IRQ status register: 0x%x\n",
*irq_status);
if (*irq_status)
/* Clear current interrupts status */
fa_writel(fa, fa->fa_irq_adc_base, &zfad_regs[ZFA_IRQ_ADC_SRC],
*irq_status);
}
/*
* fa_irq_handler
* @irq:
* @arg: pointer to fa_dev
*
* The ADC svec firmware fires interrupt from a single wishbone core
* and throught the VIC ACQ_END and TRIG events. Note about "TRIG"
* event: the main reason to listen this interrupt was to read the
* intermediate time stamps in case of multishots.
* With the new firmware (>=3.0) the stamps come with the data,
* therefore the driver doesn't have to listen "TRIG" event. This
* enhancement remove completely the risk of loosing interrupt in case
* of small number of samples and makes the retry loop in the hanlder
* obsolete.
*/
irqreturn_t fa_irq_handler(int irq, void *arg)
{
struct fa_dev *fa = arg;
struct zio_cset *cset = fa->zdev->cset;
uint32_t status;
unsigned long flags;
struct zfad_block *zfad_block;
/* irq to handle */
fa_get_irq_status(fa, &status);
if (!status)
return IRQ_NONE; /* No interrupt fired by this mezzanine */
if (status & FA_IRQ_ADC_ACQ_END) {
dev_dbg(fa->msgdev, "Handle ADC interrupts\n");
/*
* Acquiring samples is a critical section
* protected against any concurrent abbort trigger.
* This is done by raising the flag CSET_BUSY at ACQ_END
* and lowered it at the end of DMA_END.
*/
spin_lock_irqsave(&cset->lock, flags);
zfad_block = cset->interleave->priv_d;
/* Check first if any concurrent trigger stop */
/* has deleted zio blocks. In such a case */
/* the flag is not raised and nothing is done */
if (zfad_block != NULL && (cset->ti->flags & ZIO_TI_ARMED))
cset->flags |= ZIO_CSET_HW_BUSY;
spin_unlock_irqrestore(&cset->lock, flags);
if (cset->flags & ZIO_CSET_HW_BUSY) {
/* Job deferred to the workqueue: */
/* Start DMA and ack irq on the carrier */
queue_work(fa_workqueue, &fa->irq_work);
/* register the core firing the IRQ in order to */
/* check right IRQ seq.: ACQ_END followed by DMA_END */
fa->last_irq_core_src = irq;
}
} else { /* unexpected interrupt we have to ack anyway */
dev_err(fa->msgdev,
"%s unexpected interrupt 0x%x\n",
__func__, status);
}
return IRQ_HANDLED;
}
int fa_setup_irqs(struct fa_dev *fa)
{
struct resource *r;
int err;
/* Request IRQ */
dev_dbg(fa->msgdev, "Request irq\n");
r = platform_get_resource(fa->pdev, IORESOURCE_IRQ, ADC_IRQ_TRG);
err = request_any_context_irq(r->start, fa_irq_handler, 0,
r->name, fa);
if (err < 0) {
dev_err(fa->msgdev, "can't request irq %lli (error %i)\n",
r->start, err);
return err;
}
/* workqueue is required to execute DMA transaction */
INIT_WORK(&fa->irq_work, fa_irq_work);
return err;
}
int fa_free_irqs(struct fa_dev *fa)
{
/*
* When we unload the driver the FPGA is still running so it may
* rises interrupts. Disable IRQs in order to prevent spurious
* interrupt when the driver is not there to handle them.
*/
fa_disable_irqs(fa);
/* Release ADC IRQs */
free_irq(platform_get_irq(fa->pdev, ADC_IRQ_TRG), fa);
return 0;
}
int fa_enable_irqs(struct fa_dev *fa)
{
fa_writel(fa, fa->fa_irq_adc_base,
&zfad_regs[ZFA_IRQ_ADC_ENABLE_MASK],
FA_IRQ_ADC_ACQ_END);
return 0;
}
int fa_disable_irqs(struct fa_dev *fa)
{
fa_writel(fa, fa->fa_irq_adc_base,
&zfad_regs[ZFA_IRQ_ADC_DISABLE_MASK],
FA_IRQ_ADC_ACQ_END);
return 0;
}
int fa_ack_irq(struct fa_dev *fa, int irq_id)
{
return 0;
}
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright CERN 2012-2019
* Author: Federico Vaga <federico.vaga@gmail.com>
*
* Table of register masks, used by driver functions
*/
#include "fmc-adc-100m14b4cha.h"
/* Definition of the fmc-adc registers fields: offset - mask - isbitfield */
const struct zfa_field_desc zfad_regs[] = {
/* Control registers */
[ZFA_CTL_FMS_CMD] = {0x00, 0x00000003, 1},
[ZFA_CTL_CLK_EN] = {0x00, 0x00000004, 1},
[ZFA_CTL_DAC_CLR_N] = {0x00, 0x00000008, 1},
[ZFA_CTL_BSLIP] = {0x00, 0x00000010, 1},
[ZFA_CTL_TEST_DATA_EN] = {0x00, 0x00000020, 1},
[ZFA_CTL_TRIG_LED] = {0x00, 0x00000040, 1},
[ZFA_CTL_ACQ_LED] = {0x00, 0x00000080, 1},
[ZFA_CTL_RST_TRG_STA] = {0x00, 0x00000100, 1},
[ZFA_CTL_CALIB_APPLY] = {0x00, 0x00008000, 1},
/* Status registers */
[ZFA_STA_FSM] = {0x04, 0x00000007, 1},
[ZFA_STA_SERDES_PLL] = {0x04, 0x00000008, 1},
[ZFA_STA_SERDES_SYNCED] = {0x04, 0x00000010, 1},
[ZFA_STA_FMC_NR] = {0x04, 0x000000c0, 1},
[ZFA_STA_CALIB_BUSY] = {0x04, 0x00008000, 1},
/* Trigger */
/* Config register */
[ZFAT_CFG_STA] = {0x08, 0xFFFFFFFF, 0},
[ZFAT_CFG_SRC] = {0x0C, 0xFFFFFFFF, 0},
[ZFAT_CFG_POL] = {0x10, 0xFFFFFFFF, 0},
/* Delay */
[ZFAT_EXT_DLY] = {0x14, 0xFFFFFFFF, 0},
/* Software */
[ZFAT_SW] = {0x18, 0xFFFFFFFF, 0},
/* Number of shots */
[ZFAT_SHOTS_NB] = {0x1C, 0x0000FFFF, 1},
/* Remaining shots counter */
[ZFAT_SHOTS_REM] = {0x1C, 0xFFFF0000, 1},
/* Multishot max samples*/
[ZFA_MULT_MAX_SAMP] = {0x20, 0xFFFFFFFF, 0},
/* Position address */
[ZFAT_POS] = {0x24, 0xFFFFFFFF, 0},
/* Sampling clock frequency */
[ZFAT_SAMPLING_HZ] = {0x28, 0xFFFFFFFF, 0},
/* Sample rate */
[ZFAT_SR_UNDER] = {0x2C, 0xFFFFFFFF, 0},
/* Pre-sample */
[ZFAT_PRE] = {0x30, 0xFFFFFFFF, 0},
/* Post-sample */
[ZFAT_POST] = {0x34, 0xFFFFFFFF, 0},
/* Sample counter */
[ZFAT_CNT] = {0x38, 0xFFFFFFFF, 0},
/* Channel 1 */
[ZFA_CH1_CTL_RANGE] = {0x80, 0x00000077, 1},
[ZFA_CH1_CTL_TERM] = {0x80, 0x00000008, 1},
[ZFA_CH1_STA] = {0x84, 0x0000FFFF, 0},
[ZFA_CH1_GAIN] = {0x88, 0x0000FFFF, 1},
[ZFA_CH1_OFFSET] = {0x88, 0xFFFF0000, 1},
[ZFA_CH1_SAT] = {0x8C, 0x00007FFF, 0},
[ZFA_CH1_HYST] = {0x90, 0xFFFF0000, 1},
[ZFA_CH1_THRES] = {0x90, 0x0000FFFF, 1},
[ZFA_CH1_DLY] = {0x94, 0xFFFFFFFF, 0},
/* Channel 2 */
[ZFA_CH2_CTL_RANGE] = {0xC0, 0x00000077, 1},
[ZFA_CH2_CTL_TERM] = {0xC0, 0x00000008, 1},
[ZFA_CH2_STA] = {0xC4, 0x0000FFFF, 0},
[ZFA_CH2_GAIN] = {0xC8, 0x0000FFFF, 1},
[ZFA_CH2_OFFSET] = {0xC8, 0xFFFF0000, 1},
[ZFA_CH2_SAT] = {0xCC, 0x00007FFF, 0},
[ZFA_CH2_HYST] = {0xD0, 0xFFFF0000, 1},
[ZFA_CH2_THRES] = {0xD0, 0x0000FFFF, 1},
[ZFA_CH2_DLY] = {0xD4, 0xFFFFFFFF, 0},
/* Channel 3 */
[ZFA_CH3_CTL_RANGE] = {0x100, 0x00000077, 1},
[ZFA_CH3_CTL_TERM] = {0x100, 0x00000008, 1},
[ZFA_CH3_STA] = {0x104, 0x0000FFFF, 0},
[ZFA_CH3_GAIN] = {0x108, 0x0000FFFF, 1},
[ZFA_CH3_OFFSET] = {0x108, 0xFFFF0000, 1},
[ZFA_CH3_SAT] = {0x10C, 0x00007FFF, 0},
[ZFA_CH3_HYST] = {0x110, 0xFFFF0000, 1},
[ZFA_CH3_THRES] = {0x110, 0x0000FFFF, 1},
[ZFA_CH3_DLY] = {0x114, 0xFFFFFFFF, 0},
/* Channel 4 */
[ZFA_CH4_CTL_RANGE] = {0x140, 0x00000077, 1},
[ZFA_CH4_CTL_TERM] = {0x140, 0x00000008, 1},
[ZFA_CH4_STA] = {0x144, 0x0000FFFF, 0},
[ZFA_CH4_GAIN] = {0x148, 0x0000FFFF, 1},
[ZFA_CH4_OFFSET] = {0x148, 0xFFFF0000, 1},
[ZFA_CH4_SAT] = {0x14C, 0x00007FFF, 0},
[ZFA_CH4_HYST] = {0x150, 0xFFFF0000, 1},
[ZFA_CH4_THRES] = {0x150, 0x0000FFFF, 1},
[ZFA_CH4_DLY] = {0x154, 0xFFFFFFFF, 0},
/* IRQ */
[ZFA_IRQ_ADC_DISABLE_MASK] = {0x00, 0x00000003, 0},
[ZFA_IRQ_ADC_ENABLE_MASK] = {0x04, 0x00000003, 0},
[ZFA_IRQ_ADC_MASK_STATUS] = {0x08, 0x00000003, 0},
[ZFA_IRQ_ADC_SRC] = {0x0C, 0x00000003, 0},
[ZFA_IRQ_VIC_CTRL] = {0x00, 0x000FFFFF, 0},
[ZFA_IRQ_VIC_ENABLE_MASK] = {0x08, 0x00000003, 0},
[ZFA_IRQ_VIC_DISABLE_MASK] = {0x0C, 0x00000003, 0},
[ZFA_IRQ_VIC_MASK_STATUS] = {0x10, 0x00000003, 0},
/* DS18B20 UID/Temperature */
[ZFA_DS18B20_ID_U] = {0x00, 0xFFFFFFFF, 0},
[ZFA_DS18B20_ID_L] = {0x04, 0xFFFFFFFF, 0},
[ZFA_DS18B20_TEMP] = {0x08, 0x0000FFFF, 0},
[ZFA_DS18B20_STAT] = {0x0C, 0x00000003, 0},
/* UTC */
[ZFA_UTC_SECONDS_U] = {0x00, ~0x0, 0},
[ZFA_UTC_SECONDS_L] = {0x04, ~0x0, 0},
[ZFA_UTC_COARSE] = {0x08, ~0x0, 0},
[ZFA_UTC_TRIG_TIME_SECONDS_U] = {0x0C, ~0x0, 0},
[ZFA_UTC_TRIG_TIME_SECONDS_L] = {0x10, ~0x0, 0},
[ZFA_UTC_TRIG_TIME_COARSE] = {0x14, ~0x0, 0},
[ZFA_UTC_TRIG_SECONDS_U] = {0x18, ~0x0, 0},
[ZFA_UTC_TRIG_SECONDS_L] = {0x1C, ~0x0, 0},
[ZFA_UTC_TRIG_COARSE] = {0x20, ~0x0, 0},
[ZFA_UTC_ACQ_START_SECONDS_U] = {0x24, ~0x0, 0},
[ZFA_UTC_ACQ_START_SECONDS_L] = {0x28, ~0x0, 0},
[ZFA_UTC_ACQ_START_COARSE] = {0x2C, ~0x0, 0},
[ZFA_UTC_ACQ_STOP_SECONDS_U] = {0x30, ~0x0, 0},
[ZFA_UTC_ACQ_STOP_SECONDS_L] = {0x34, ~0x0, 0},
[ZFA_UTC_ACQ_STOP_COARSE] = {0x38, ~0x0, 0},
[ZFA_UTC_ACQ_END_SECONDS_U] = {0x3C, ~0x0, 0},
[ZFA_UTC_ACQ_END_SECONDS_L] = {0x40, ~0x0, 0},
[ZFA_UTC_ACQ_END_COARSE] = {0x44, ~0x0, 0},
};
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright CERN 2012-2019
* Author: Federico Vaga <federico.vaga@gmail.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/moduleparam.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <asm/byteorder.h>
#include "fmc-adc-100m14b4cha.h"
ZIO_PARAM_BUFFER(adc_buffer);
/*
* zio device attributes
*/
static ZIO_ATTR_DEFINE_STD(ZIO_DEV, zfad_cset_std_zattr) = {
ZIO_ATTR(zdev, ZIO_ATTR_NBITS, ZIO_RO_PERM, ZFA_SW_R_NOADDRES_NBIT, FA100M14B4C_NBIT),
};
/*
* In the following table, "ATTR_EXT" are extended attributes that
* end up in the control structure (i.e. they are metadata for the
* acquisition), while PARAM_EXT are parameters: sysfs files that
* don't end up in the control structure of ZIO blocks
*/
static struct zio_attribute zfad_cset_ext_zattr[] = {
/*
* sample-decimation
* ADC acquire always at the maximum sample rate, to make "slower"
* acquisition you can decimate samples. 0 is a forbidden value, 1
* for the maximum speed.
*/
ZIO_ATTR_EXT("undersample", ZIO_RW_PERM, ZFAT_SR_UNDER, 1),
ZIO_ATTR_EXT("ch0-offset", ZIO_RW_PERM, ZFA_CH1_OFFSET, 0),
ZIO_ATTR_EXT("ch1-offset", ZIO_RW_PERM, ZFA_CH2_OFFSET, 0),
ZIO_ATTR_EXT("ch2-offset", ZIO_RW_PERM, ZFA_CH3_OFFSET, 0),
ZIO_ATTR_EXT("ch3-offset", ZIO_RW_PERM, ZFA_CH4_OFFSET, 0),
ZIO_ATTR_EXT("ch0-offset-zero", ZIO_RW_PERM, ZFA_SW_CH1_OFFSET_ZERO, 0),
ZIO_ATTR_EXT("ch1-offset-zero", ZIO_RW_PERM, ZFA_SW_CH2_OFFSET_ZERO, 0),
ZIO_ATTR_EXT("ch2-offset-zero", ZIO_RW_PERM, ZFA_SW_CH3_OFFSET_ZERO, 0),
ZIO_ATTR_EXT("ch3-offset-zero", ZIO_RW_PERM, ZFA_SW_CH4_OFFSET_ZERO, 0),
ZIO_ATTR_EXT("ch0-vref", ZIO_RW_PERM, ZFA_CH1_CTL_RANGE, 0),
ZIO_ATTR_EXT("ch1-vref", ZIO_RW_PERM, ZFA_CH2_CTL_RANGE, 0),
ZIO_ATTR_EXT("ch2-vref", ZIO_RW_PERM, ZFA_CH3_CTL_RANGE, 0),
ZIO_ATTR_EXT("ch3-vref", ZIO_RW_PERM, ZFA_CH4_CTL_RANGE, 0),
ZIO_ATTR_EXT("ch0-saturation", ZIO_RW_PERM, ZFA_CH1_SAT, 0),
ZIO_ATTR_EXT("ch1-saturation", ZIO_RW_PERM, ZFA_CH2_SAT, 0),
ZIO_ATTR_EXT("ch2-saturation", ZIO_RW_PERM, ZFA_CH3_SAT, 0),
ZIO_ATTR_EXT("ch3-saturation", ZIO_RW_PERM, ZFA_CH4_SAT, 0),
ZIO_ATTR_EXT("ch0-50ohm-term", ZIO_RW_PERM, ZFA_CH1_CTL_TERM, 0),
ZIO_ATTR_EXT("ch1-50ohm-term", ZIO_RW_PERM, ZFA_CH2_CTL_TERM, 0),
ZIO_ATTR_EXT("ch2-50ohm-term", ZIO_RW_PERM, ZFA_CH3_CTL_TERM, 0),
ZIO_ATTR_EXT("ch3-50ohm-term", ZIO_RW_PERM, ZFA_CH4_CTL_TERM, 0),
/* last acquisition start time stamp */
ZIO_ATTR_EXT("tstamp-acq-str-su", ZIO_RO_PERM,
ZFA_UTC_ACQ_START_SECONDS_U, 0),
ZIO_ATTR_EXT("tstamp-acq-str-sl", ZIO_RO_PERM,
ZFA_UTC_ACQ_START_SECONDS_L, 0),
ZIO_ATTR_EXT("tstamp-acq-str-t", ZIO_RO_PERM,
ZFA_UTC_ACQ_START_COARSE, 0),
/* Timing base */
ZIO_ATTR_EXT("tstamp-base-su", ZIO_RW_PERM, ZFA_UTC_SECONDS_U, 0),
ZIO_ATTR_EXT("tstamp-base-sl", ZIO_RW_PERM, ZFA_UTC_SECONDS_L, 0),
ZIO_ATTR_EXT("tstamp-base-t", ZIO_RW_PERM, ZFA_UTC_COARSE, 0),
/* Parameters (not attributes) follow */
/*
* State machine commands
* 1: start
* 2: stop
*/
ZIO_PARAM_EXT("fsm-command", ZIO_WO_PERM, ZFA_CTL_FMS_CMD, 0),
/*
* Automatic start acquisition
* 1: enabled
* 0: disabled
*/
ZIO_PARAM_EXT("fsm-auto-start", ZIO_RW_PERM, ZFA_SW_R_NOADDERS_AUTO, 0),
/*
* fsm - status of the state machine:
* 1: IDLE
* 2: PRE_TRIG
* 3: WAIT_TRIG
* 4: POST_TRIG
* 5: DECR_SHOT
* 7: Illegal
* */
ZIO_PARAM_EXT("fsm-state", ZIO_RO_PERM, ZFA_STA_FSM, 0),
/* last acquisition end time stamp */
ZIO_PARAM_EXT("tstamp-acq-end-su", ZIO_RO_PERM,
ZFA_UTC_ACQ_END_SECONDS_U, 0),
ZIO_PARAM_EXT("tstamp-acq-end-sl", ZIO_RO_PERM,
ZFA_UTC_ACQ_END_SECONDS_L, 0),
ZIO_PARAM_EXT("tstamp-acq-end-t", ZIO_RO_PERM,
ZFA_UTC_ACQ_END_COARSE, 0),
/* last acquisition stop time stamp */
ZIO_PARAM_EXT("tstamp-acq-stp-su", ZIO_RO_PERM,
ZFA_UTC_ACQ_STOP_SECONDS_U, 0),
ZIO_PARAM_EXT("tstamp-acq-stp-sl", ZIO_RO_PERM,
ZFA_UTC_ACQ_STOP_SECONDS_L, 0),
ZIO_PARAM_EXT("tstamp-acq-stp-t", ZIO_RO_PERM,
ZFA_UTC_ACQ_STOP_COARSE, 0),
ZIO_PARAM_EXT("sample-frequency", ZIO_RO_PERM, ZFAT_SAMPLING_HZ, 0),
ZIO_PARAM_EXT("max-sample-mshot", ZIO_RO_PERM, ZFA_MULT_MAX_SAMP, 0),
ZIO_PARAM_EXT("sample-counter", ZIO_RO_PERM, ZFAT_CNT, 0),
ZIO_PARAM_EXT("output-randomizer", ZIO_RW_PERM, ZFA_SW_R_NOADDERS_RAND, 0),
};
#if 0 /* FIXME Unused until TLV control will be available */
static ZIO_ATTR_DEFINE_STD(ZIO_DEV, zfad_chan_std_zattr) = {
/* the offset is complement 2 format */
ZIO_ATTR(zdev, ZIO_ATTR_OFFSET, ZIO_RW_PERM, ZFA_CHx_OFFSET, 0),
/*
* in-range
* 0x23 (35): 100mV range
* 0x11 (17): 1V range
* 0x45 (69): 10V range
* 0x00 (0): Open input
*/
ZIO_ATTR(zdev, ZIO_ATTR_VREFTYPE, ZIO_RW_PERM, ZFA_CHx_CTL_RANGE, 0x11),
};
#endif
static struct zio_attribute zfad_chan_ext_zattr[] = {
#if 0 /* FIXME Unused until TLV control will be available */
ZIO_ATTR("saturation", ZIO_RW_PERM, ZFA_CHx_SAT, 0),
#endif
/*ZIO_ATTR(zdev, "50ohm-termination", ZIO_RW_PERM, ZFA_CHx_CTL_TERM, 0x11),*/
ZIO_PARAM_EXT("current-value", ZIO_RO_PERM, ZFA_CHx_STA, 0),
};
static struct zio_attribute zfad_dev_ext_zattr[] = {
/* Get Mezzanine temperature from the DS18B20 chip */
ZIO_PARAM_EXT("temperature", ZIO_RO_PERM, ZFA_SW_R_NOADDRES_TEMP, 0),
};
/* Temporarily, user values are the same as hardware values */
int zfad_convert_user_range(uint32_t user_val)
{
return zfad_convert_hw_range(user_val);
}
static bool fa_is_dac_offset_valid(int32_t user, int32_t zero)
{
int32_t offset = user + zero;
return (offset >= DAC_SAT_LOW && offset <= DAC_SAT_UP);
}
/*
* zfad_conf_set
*
* set a value to a FMC-ADC registers
*/
static int zfad_conf_set(struct device *dev, struct zio_attribute *zattr,
uint32_t usr_val)
{
struct fa_dev *fa = get_zfadc(dev);
void *baseoff = fa->fa_adc_csr_base;
struct zio_channel *chan;
int i, range, reg_index, err;
reg_index = zattr->id;
i = FA100M14B4C_NCHAN;
if (zattr->id >= ZFA_UTC_SECONDS_U &&
zattr->id <= ZFA_UTC_ACQ_END_COARSE)
baseoff = fa->fa_utc_base;
switch (reg_index) {
/*
* Most of the following "case" statements are simply
* error checking and ancillary operations. The actual
* programming of hardware is done at the end of the
* switch, in the catch-all final zfa_hardware_write()
*/
case ZFA_SW_R_NOADDERS_RAND:
return fa_adc_output_randomizer_set(fa, !!usr_val);
case ZFA_SW_R_NOADDERS_AUTO:
fa->enable_auto_start = usr_val;
return 0;
case ZFA_SW_CH1_OFFSET_ZERO:
i--;
/*fallthrough*/
case ZFA_SW_CH2_OFFSET_ZERO:
i--;
/*fallthrough*/
case ZFA_SW_CH3_OFFSET_ZERO:
i--;
/*fallthrough*/
case ZFA_SW_CH4_OFFSET_ZERO:
i--;
chan = to_zio_cset(dev)->chan + i;
if (!fa_is_dac_offset_valid(fa->user_offset[chan->index],
usr_val))
return -EINVAL;
spin_lock(&fa->zdev->cset->lock);
fa->zero_offset[i] = usr_val;
fa_calib_dac_config_chan(fa, i, ~0);
spin_unlock(&fa->zdev->cset->lock);
return 0;
case ZFA_CHx_SAT:
/* TODO when TLV */
break;
/* FIXME temporary until TLV control */
case ZFA_CH1_SAT:
case ZFA_CH2_SAT:
case ZFA_CH3_SAT:
case ZFA_CH4_SAT:
if (usr_val & ~zfad_regs[reg_index].mask) {
dev_err(dev, "invalid saturation value (max: %d)\n",
zfad_regs[reg_index].mask);
return -EINVAL;
}
break;
/* FIXME temporary until TLV control */
case ZFA_CH1_OFFSET:
i--;
/*fallthrough*/
case ZFA_CH2_OFFSET:
i--;
/*fallthrough*/
case ZFA_CH3_OFFSET:
i--;
/*fallthrough*/
case ZFA_CH4_OFFSET:
i--;
chan = to_zio_cset(dev)->chan + i;
if (!fa_is_dac_offset_valid(usr_val,
fa->zero_offset[chan->index]))
return -EINVAL;
spin_lock(&fa->zdev->cset->lock);
fa->user_offset[chan->index] = usr_val;
err = fa_calib_dac_config_chan(fa, i, ~0);
spin_unlock(&fa->zdev->cset->lock);
return err;
case ZFA_CHx_OFFSET:
chan = to_zio_chan(dev);
spin_lock(&fa->zdev->cset->lock);
fa->user_offset[chan->index] = usr_val;
err = fa_calib_dac_config_chan(fa, chan->index, ~0);
spin_unlock(&fa->zdev->cset->lock);
return err;
case ZFAT_SR_UNDER:
if (usr_val == 0)
usr_val++;
break;
/* FIXME temporary until TLV control */
case ZFA_CH1_CTL_TERM:
/*fallthrough*/
case ZFA_CH2_CTL_TERM:
/*fallthrough*/
case ZFA_CH3_CTL_TERM:
/*fallthrough*/
case ZFA_CH4_CTL_TERM:
/*fallthrough*/
case ZFA_CHx_CTL_TERM:
if (usr_val > 1)
usr_val = 1;
break;
/* FIXME temporary until TLV control */
case ZFA_CH1_CTL_RANGE:
i--;
/*fallthrough*/
case ZFA_CH2_CTL_RANGE:
i--;
/*fallthrough*/
case ZFA_CH3_CTL_RANGE:
i--;
/*fallthrough*/
case ZFA_CH4_CTL_RANGE:
i--;
range = zfad_convert_user_range(usr_val);
if (range < 0)
return range;
err = fa_adc_range_set(fa, &to_zio_cset(dev)->chan[i], range);
if (err)
return err;
spin_lock(&fa->zdev->cset->lock);
fa_calib_adc_config_chan(fa, i, ~0);
fa_calib_dac_config_chan(fa, i, ~0);
spin_unlock(&fa->zdev->cset->lock);
return 0;
case ZFA_CHx_CTL_RANGE:
range = zfad_convert_user_range(usr_val);
if (range < 0)
return range;
err = fa_adc_range_set(fa, &to_zio_cset(dev)->chan[i], range);
if (err)
return err;
spin_lock(&fa->zdev->cset->lock);
fa_calib_adc_config_chan(fa, i, ~0);
fa_calib_dac_config_chan(fa, i, ~0);
spin_unlock(&fa->zdev->cset->lock);
return 0;
case ZFA_UTC_COARSE:
if (usr_val >= FA100M14B4C_UTC_CLOCK_FREQ) {
dev_err(fa->msgdev,
"ticks time must be in the range [0, %d]\n",
FA100M14B4C_UTC_CLOCK_FREQ);
return -EINVAL;
}
break;
case ZFA_CTL_FMS_CMD:
return zfad_fsm_command(fa, usr_val);
}
fa_writel(fa, baseoff, &zfad_regs[reg_index], usr_val);
return 0;
}
/*
* zfad_info_get
*
* get a register value from FMC-ADC.
*/
static int zfad_info_get(struct device *dev, struct zio_attribute *zattr,
uint32_t *usr_val)
{
struct fa_dev *fa = get_zfadc(dev);
void *baseoff = fa->fa_adc_csr_base;
int i, reg_index;
i = FA100M14B4C_NCHAN;
if (zattr->id >= ZFA_UTC_SECONDS_U &&
zattr->id <= ZFA_UTC_ACQ_END_COARSE)
baseoff = fa->fa_utc_base;
switch (zattr->id) {
/* FIXME temporary until TLV control */
case ZFA_CH1_OFFSET:
i--;
/*fallthrough*/
case ZFA_CH2_OFFSET:
i--;
/*fallthrough*/
case ZFA_CH3_OFFSET:
i--;
/*fallthrough*/
case ZFA_CH4_OFFSET:
i--;
*usr_val = fa->user_offset[i];
return 0;
case ZFA_CHx_OFFSET:
*usr_val = fa->user_offset[to_zio_chan(dev)->index];
return 0;
case ZFA_SW_R_NOADDERS_RAND:
*usr_val = fa_adc_is_output_randomizer(fa);
return 0;
case ZFA_SW_R_NOADDRES_NBIT:
/*fallthrough*/
case ZFA_SW_R_NOADDERS_AUTO:
/* ZIO automatically return the attribute value */
return 0;
case ZFA_SW_R_NOADDRES_TEMP:
*usr_val = fa_temperature_read(fa);
return 0;
case ZFA_SW_CH1_OFFSET_ZERO:
i--;
/*fallthrough*/
case ZFA_SW_CH2_OFFSET_ZERO:
i--;
/*fallthrough*/
case ZFA_SW_CH3_OFFSET_ZERO:
i--;
/*fallthrough*/
case ZFA_SW_CH4_OFFSET_ZERO:
i--;
*usr_val = fa->zero_offset[i];
return 0;
case ZFA_CHx_SAT:
/*fallthrough*/
case ZFA_CHx_CTL_TERM:
/*fallthrough*/
case ZFA_CHx_CTL_RANGE:
reg_index = zfad_get_chx_index(zattr->id, to_zio_chan(dev)->index);
break;
case ZFA_CHx_STA:
reg_index = zfad_get_chx_index(zattr->id, to_zio_chan(dev)->index);
*usr_val = fa_readl(fa, fa->fa_adc_csr_base,
&zfad_regs[reg_index]);
i = (int16_t)(*usr_val); /* now signed integer */
*usr_val = i;
return 0;
default:
reg_index = zattr->id;
}
*usr_val = fa_readl(fa, baseoff, &zfad_regs[reg_index]);
return 0;
}
static const struct zio_sysfs_operations zfad_s_op = {
.conf_set = zfad_conf_set,
.info_get = zfad_info_get,
};
static inline int zfat_overflow_detection(struct zio_ti *ti)
{
struct fa_dev *fa = ti->cset->zdev->priv_d;
struct zio_attribute *ti_zattr = ti->zattr_set.std_zattr;
uint32_t nshot_t, nsamples;
size_t shot_size;
if (ti->cset->trig != &zfat_type)
nshot_t = 1; /* with any other trigger work in one-shot mode */
else
nshot_t = ti_zattr[ZIO_ATTR_TRIG_N_SHOTS].value;
/*
* +2 because of the timetag at the end
*/
nsamples = ti_zattr[ZIO_ATTR_TRIG_PRE_SAMP].value +
ti_zattr[ZIO_ATTR_TRIG_POST_SAMP].value;
shot_size = ((nsamples + 2) * ti->cset->ssize) * FA100M14B4C_NCHAN;
if ( (shot_size * nshot_t) > FA100M14B4C_MAX_ACQ_BYTE ) {
dev_err(fa->msgdev, "Cannot acquire, dev memory overflow\n");
return -ENOMEM;
}
/* in case of multi shot, each shot cannot exceed the dpram size */
if ( (nshot_t > 1) &&
(nsamples > fa->mshot_max_samples) ) {
dev_err(fa->msgdev, "Cannot acquire such amount of samples "
"(req: %d , max: %d) in multi shot mode."
"dev memory overflow\n",
nsamples, fa->mshot_max_samples);
return -ENOMEM;
}
return 0;
}
/*
* zfad_input_cset_software
* @fa the adc instance to use
* @cset channel set to acquire
*
* If the user is using the ADC trigger, then it can do a multi-shot
* acquisition.
* If the user is using a software trigger, it cannot do multi-shot.
* The generic arm trigger used by software trigger returns a
* zio_block. We must convert it into a zfad_block to perform DMA
*/
static int zfad_input_cset_software(struct fa_dev *fa, struct zio_cset *cset)
{
struct zfad_block *tmp;
int err;
tmp = kzalloc(sizeof(struct zfad_block), GFP_ATOMIC);
if (!tmp)
return -ENOMEM;
tmp->block = cset->interleave->active_block;
cset->interleave->priv_d = tmp;
/* Configure post samples */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_POST],
cset->ti->nsamples);
/* Start the acquisition */
zfad_fsm_command(fa, FA100M14B4C_CMD_START);
fa->n_shots = 1;
/* Fire software trigger */
err = fa_trigger_software(fa);
if (err)
return err;
return -EAGAIN;
}
/*
* zfad_input_cset
* @cset: channel set to acquire
*
* Prepare the FMC-ADC for the acquisition.
*/
static int zfad_input_cset(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
int err;
/* Check if device memory allows this acquisition */
err = zfat_overflow_detection(cset->ti);
if (err)
return err;
dev_dbg(fa->msgdev, "Ready to acquire\n");
/* ZIO should configure only the interleaved channel */
if (!cset->interleave)
return -EINVAL;
/* nsamples can't be 0 */
if (!cset->interleave->current_ctrl->nsamples) {
dev_info(fa->msgdev, "pre + post = 0: can't acquire\n");
return -EINVAL;
}
/* If not the fmc-adc-trg, then is a ZIO software trigger */
if (unlikely(cset->trig != &zfat_type))
return zfad_input_cset_software(fa, cset);
return -EAGAIN; /* data_done on DMA_DONE interrupt */
}
/*
* zfad_stop_cset
* @cset: channel set to stop
*
* Stop an acquisition, reset indexes and disable interrupts. This function
* is useful only if the driver is using a software trigger.
*/
static void zfad_stop_cset(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
/* If the user is using a software trigger */
if (cset->trig != &zfat_type) {
/* Force the acquisition to stop */
zfad_fsm_command(fa, FA100M14B4C_CMD_STOP);
/* Release zfad_block */
kfree(cset->interleave->priv_d);
cset->interleave->priv_d = NULL;
/* Clear active block */
cset->interleave->active_block = NULL;
}
}
/*
* zfad_zio_probe
* @zdev: the real zio device
*
* The device registration completes. Copy the calibration data from the
* eeprom and initialize some registers
*/
static int zfad_zio_probe(struct zio_device *zdev)
{
struct fa_dev *fa = zdev->priv_d;
int err;
dev_dbg(fa->msgdev, "%s:%d\n", __func__, __LINE__);
/* Save also the pointer to the real zio_device */
fa->zdev = zdev;
err = device_create_bin_file(&zdev->head.dev, &dev_attr_calibration);
if (err)
return err;
/* We don't have csets at this point, so don't do anything more */
return 0;
}
/*
* zfad_zio_remove
* @zdev: the real zio device
*
* Release FMC interrupt handler
*/
static int zfad_zio_remove(struct zio_device *zdev)
{
device_remove_bin_file(&zdev->head.dev, &dev_attr_calibration);
return 0;
}
/* Device description */
static struct zio_channel zfad_chan_tmpl = {
.zattr_set = {
/* FIXME usable only when TLV control is available */
/*.std_zattr = zfad_chan_std_zattr,*/
.ext_zattr = zfad_chan_ext_zattr,
.n_ext_attr = ARRAY_SIZE(zfad_chan_ext_zattr),
},
};
static struct zio_cset zfad_cset[] = {
{
.raw_io = zfad_input_cset,
.stop_io = zfad_stop_cset,
.ssize = 2,
.n_chan = FA100M14B4C_NCHAN,
.chan_template = &zfad_chan_tmpl,
.flags = ZIO_CSET_TYPE_ANALOG | /* is analog */
ZIO_DIR_INPUT | /* is input */
ZIO_CSET_INTERLEAVE_ONLY,/* interleave only */
.zattr_set = {
.std_zattr = zfad_cset_std_zattr,
.ext_zattr = zfad_cset_ext_zattr,
.n_ext_attr = ARRAY_SIZE(zfad_cset_ext_zattr),
},
}
};
static struct zio_device zfad_tmpl = {
.owner = THIS_MODULE,
.s_op = &zfad_s_op,
.flags = 0,
.cset = zfad_cset,
.n_cset = ARRAY_SIZE(zfad_cset),
.zattr_set = {
.ext_zattr = zfad_dev_ext_zattr,
.n_ext_attr = ARRAY_SIZE(zfad_dev_ext_zattr),
},
/* This driver prefers its own trigger */
.preferred_trigger = "adc-100m14b",
.preferred_buffer = "kmalloc",
};
/* List of supported boards */
static const struct zio_device_id zfad_table[] = {
{"adc-100m14b", &zfad_tmpl},
{},
};
static struct zio_driver fa_zdrv = {
.driver = {
.name = "adc-100m14b",
.owner = THIS_MODULE,
},
.id_table = zfad_table,
.probe = zfad_zio_probe,
.remove = zfad_zio_remove,
/* Take the version from ZIO git sub-module */
.min_version = ZIO_VERSION(__ZIO_MIN_MAJOR_VERSION,
__ZIO_MIN_MINOR_VERSION,
0), /* Change it if you use new features from
a specific patch */
};
/*
* fa_zio_unregister
*
* It is a simple wrapper invoked by module_init to register this zio driver
*/
int fa_zio_register(void)
{
return zio_register_driver(&fa_zdrv);
}
/*
* fa_zio_unregister
*
* It is a simple wrapper invoked by module_exit to unregister this zio driver
*/
void fa_zio_unregister(void)
{
zio_unregister_driver(&fa_zdrv);
}
/*
* fa_zio_init
*
* It checks if we can register this device. If it is possibile, the function
* registers both device and trigger. The FMC probe invokes this function.
*/
int fa_zio_init(struct fa_dev *fa)
{
int err;
if (adc_buffer)
zfad_tmpl.preferred_buffer = adc_buffer;
/* Allocate the hardware zio_device for registration */
fa->hwzdev = zio_allocate_device();
if (IS_ERR(fa->hwzdev)) {
dev_err(fa->msgdev, "Cannot allocate ZIO device\n");
return PTR_ERR(fa->hwzdev);
}
/* Mandatory fields */
fa->hwzdev->owner = THIS_MODULE;
fa->hwzdev->priv_d = fa;
/* Register the hardware zio_device */
err = zio_register_device(fa->hwzdev, "adc-100m14b",
fa->pdev->id);
if (err) {
dev_err(fa->msgdev, "Cannot register ZIO device fmc-adc-100m14b\n");
zio_free_device(fa->hwzdev);
}
return err;
}
/*
* fa_zio_exit
*
* It removes both device and trigger form the ZIO framework. The FMC remove
* invokes this function.
*/
void fa_zio_exit(struct fa_dev *fa)
{
zio_unregister_device(fa->hwzdev);
zio_free_device(fa->hwzdev);
}
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright CERN 2012-2019
* Author: Federico Vaga
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include "fmc-adc-100m14b4cha.h"
struct zfat_instance {
struct zio_ti ti;
struct fa_dev *fa;
unsigned int n_acq_dev; /* number of acquisitions on device memory */
unsigned int n_err; /* number of errors */
};
#define to_zfat_instance(_ti) container_of(_ti, struct zfat_instance, ti)
/* zio trigger attributes */
static ZIO_ATTR_DEFINE_STD(ZIO_TRG, zfat_std_zattr) = {
/* Number of shots */
ZIO_ATTR(trig, ZIO_ATTR_TRIG_N_SHOTS, ZIO_RW_PERM, ZFAT_SHOTS_NB, 1),
ZIO_ATTR(trig, ZIO_ATTR_TRIG_PRE_SAMP, ZIO_RW_PERM, ZFAT_PRE, 0),
ZIO_ATTR(trig, ZIO_ATTR_TRIG_POST_SAMP, ZIO_RW_PERM, ZFAT_POST, 1),
};
static struct zio_attribute zfat_ext_zattr[] = {
[FA100M14B4C_TATTR_STA] = ZIO_ATTR_EXT("source-triggered", ZIO_RW_PERM,
ZFAT_CFG_STA, 0),
[FA100M14B4C_TATTR_SRC] = ZIO_ATTR_EXT("source", ZIO_RW_PERM,
ZFAT_CFG_SRC,
FA100M14B4C_TRG_SRC_SW),
[FA100M14B4C_TATTR_POL] = ZIO_ATTR_EXT("polarity", ZIO_RW_PERM,
ZFAT_CFG_POL, 0),
/* Internal trigger threshold value is 2 complement format */
[FA100M14B4C_TATTR_CH1_THRES] = ZIO_ATTR_EXT("ch0-threshold",
ZIO_RW_PERM,
ZFA_CH1_THRES, 0),
[FA100M14B4C_TATTR_CH2_THRES] = ZIO_ATTR_EXT("ch1-threshold",
ZIO_RW_PERM,
ZFA_CH2_THRES, 0),
[FA100M14B4C_TATTR_CH3_THRES] = ZIO_ATTR_EXT("ch2-threshold",
ZIO_RW_PERM,
ZFA_CH3_THRES, 0),
[FA100M14B4C_TATTR_CH4_THRES] = ZIO_ATTR_EXT("ch3-threshold",
ZIO_RW_PERM,
ZFA_CH4_THRES, 0),
[FA100M14B4C_TATTR_CH1_HYST] = ZIO_ATTR_EXT("ch0-hysteresis",
ZIO_RW_PERM,
ZFA_CH1_HYST, 0),
[FA100M14B4C_TATTR_CH2_HYST] = ZIO_ATTR_EXT("ch1-hysteresis",
ZIO_RW_PERM,
ZFA_CH2_HYST, 0),
[FA100M14B4C_TATTR_CH3_HYST] = ZIO_ATTR_EXT("ch2-hysteresis",
ZIO_RW_PERM,
ZFA_CH3_HYST, 0),
[FA100M14B4C_TATTR_CH4_HYST] = ZIO_ATTR_EXT("ch3-hysteresis",
ZIO_RW_PERM,
ZFA_CH4_HYST, 0),
[FA100M14B4C_TATTR_CH1_DLY] = ZIO_ATTR_EXT("ch0-delay",
ZIO_RW_PERM,
ZFA_CH1_DLY, 0),
[FA100M14B4C_TATTR_CH2_DLY] = ZIO_ATTR_EXT("ch1-delay",
ZIO_RW_PERM,
ZFA_CH2_DLY, 0),
[FA100M14B4C_TATTR_CH3_DLY] = ZIO_ATTR_EXT("ch2-delay",
ZIO_RW_PERM,
ZFA_CH3_DLY, 0),
[FA100M14B4C_TATTR_CH4_DLY] = ZIO_ATTR_EXT("ch3-delay",
ZIO_RW_PERM,
ZFA_CH4_DLY, 0),
/* Time Trigger */
[FA100M14B4C_TATTR_TRG_TIM_SU] = ZIO_ATTR_EXT("trg-time-su",
ZIO_RW_PERM,
ZFA_UTC_TRIG_TIME_SECONDS_U,
0),
[FA100M14B4C_TATTR_TRG_TIM_SL] = ZIO_ATTR_EXT("trg-time-sl",
ZIO_RW_PERM,
ZFA_UTC_TRIG_TIME_SECONDS_L,
0),
[FA100M14B4C_TATTR_TRG_TIM_C] = ZIO_ATTR_EXT("trg-time-t",
ZIO_RW_PERM,
ZFA_UTC_TRIG_TIME_COARSE,
0),
/*
* Delay to apply on the trigger in sampling clock period. The default
* clock frequency is 100MHz (period = 10ns)
*/
[FA100M14B4C_TATTR_EXT_DLY] = ZIO_ATTR_EXT("ext-delay", ZIO_RW_PERM,
ZFAT_EXT_DLY, 0),
/* last trigger time stamp */
[FA100M14B4C_TATTR_TRG_SU] = ZIO_PARAM_EXT("tstamp-trg-lst-su",
ZIO_RO_PERM,
ZFA_UTC_TRIG_SECONDS_U, 0),
[FA100M14B4C_TATTR_TRG_SL] = ZIO_PARAM_EXT("tstamp-trg-lst-sl",
ZIO_RO_PERM,
ZFA_UTC_TRIG_SECONDS_L, 0),
[FA100M14B4C_TATTR_TRG_C] = ZIO_PARAM_EXT("tstamp-trg-lst-t",
ZIO_RO_PERM,
ZFA_UTC_TRIG_COARSE, 0),
};
/*
* Reset to default value
*/
void zfat_trigger_source_reset(struct fa_dev *fa)
{
fa_writel(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFAT_CFG_SRC],
FA100M14B4C_TRG_SRC_SW | FA100M14B4C_TRG_SRC_ALT);
}
/*
* zfat_conf_set
*
* set a value to a FMC-ADC trigger register
*/
static int zfat_conf_set(struct device *dev, struct zio_attribute *zattr,
uint32_t usr_val)
{
struct fa_dev *fa = get_zfadc(dev);
void *baseoff = fa->fa_adc_csr_base;
uint32_t tmp_val = usr_val;
if (zattr->id >= ZFA_UTC_SECONDS_U &&
zattr->id <= ZFA_UTC_ACQ_END_COARSE)
baseoff = fa->fa_utc_base;
switch (zattr->id) {
case ZFAT_SHOTS_NB:
if (!tmp_val) {
dev_err(fa->msgdev, "nshots cannot be 0\n");
return -EINVAL;
}
break;
case ZFAT_POST:
if (tmp_val < 2) {
dev_err(fa->msgdev, "minimum post samples 2 (HW limitation)\n");
return -EINVAL;
}
tmp_val--; /* Remove one sample for the trigger */
break;
case ZFAT_CFG_SRC:
/*
* Do not copy to hardware when globally disabled
* We tell ZIO to save the value locally and will do
* it when the user starts an acquisition
*
* We cannot save the value in cache only when disabled
* because the trigger is always disabled during configuration
*/
return 0;
}
fa_writel(fa, baseoff, &zfad_regs[zattr->id], tmp_val);
return 0;
}
/* zfat_info_get
*
* get the value of a FMC-ADC trigger register
*/
static int zfat_info_get(struct device *dev, struct zio_attribute *zattr,
uint32_t *usr_val)
{
struct fa_dev *fa = get_zfadc(dev);
void *baseoff = fa->fa_adc_csr_base;
if (zattr->id >= ZFA_UTC_SECONDS_U &&
zattr->id <= ZFA_UTC_ACQ_END_COARSE)
baseoff = fa->fa_utc_base;
switch (zattr->id) {
case ZFAT_CFG_SRC:
/*
* The good value for the trigger source is always in
* the ZIO cache.
*/
return 0;
}
*usr_val = fa_readl(fa, baseoff, &zfad_regs[zattr->id]);
switch (zattr->id) {
case ZFAT_POST:
(*usr_val)++; /* add the trigger sample */
break;
}
return 0;
}
static const struct zio_sysfs_operations zfat_s_op = {
.conf_set = zfat_conf_set,
.info_get = zfat_info_get,
};
/* create an instance of the FMC-ADC trigger */
static struct zio_ti *zfat_create(struct zio_trigger_type *trig,
struct zio_cset *cset,
struct zio_control *ctrl, fmode_t flags)
{
struct fa_dev *fa = cset->zdev->priv_d;
struct zfat_instance *zfat;
if (!fa) {
/* This only happens if we have a bug in the init sequence */
dev_err(fa->msgdev, "No FMC device associated\n");
return ERR_PTR(-ENODEV);
}
zfat = kzalloc(sizeof(struct zfat_instance), GFP_KERNEL);
if (!zfat)
return ERR_PTR(-ENOMEM);
zfat->fa = fa;
zfat->ti.cset = cset;
return &zfat->ti;
}
static void zfat_destroy(struct zio_ti *ti)
{
struct fa_dev *fa = ti->cset->zdev->priv_d;
struct zfat_instance *zfat = to_zfat_instance(ti);
/* Disable all trigger sources */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SRC], 0);
/* Other triggers cannot use pre-samples */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_PRE], 0);
/* Reset post samples */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_POST], 0);
/* Other triggers can handle only 1 shot */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_SHOTS_NB], 1);
kfree(zfat);
}
/*
*
* Enable or disable the trigger sources globally.
* On disable (status > 0), we disable all the trigger sources
* On enable (status == 0), we enable the trigger soruces specified in the
* correspondent sysfs attribute
*/
static void zfat_change_status(struct zio_ti *ti, unsigned int status)
{
struct fa_dev *fa = ti->cset->zdev->priv_d;
uint32_t src = ti->zattr_set.ext_zattr[FA100M14B4C_TATTR_SRC].value;
if (status)
fa_writel(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFAT_CFG_SRC], 0);
else
fa_writel(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFAT_CFG_SRC], src);
}
/*
* zfat_data_done
* @cset: channels set
*
* Transfer is over for all blocks. Store all blocks and free
* zfad_blocks vector. Here we are storing only blocks
* that were filled by a trigger fire. If for some reason the data_done
* occurs before the natural end of the acquisition, un-filled block
* are not stored.
*/
static int zfat_data_done(struct zio_cset *cset)
{
struct zfad_block *zfad_block = cset->interleave->priv_d;
struct zio_bi *bi = cset->interleave->bi;
struct fa_dev *fa = cset->zdev->priv_d;
unsigned int i;
dev_dbg(fa->msgdev, "Data done\n");
/* Nothing to store */
if (!zfad_block)
return 0;
/* Store blocks */
for (i = 0; i < fa->n_shots; ++i)
if (likely(i < fa->n_fires)) {/* Store filled blocks */
dev_dbg(fa->msgdev, "Store Block %i/%i\n",
i + 1, fa->n_shots);
zio_buffer_store_block(bi, zfad_block[i].block);
} else { /* Free un-filled blocks */
dev_dbg(fa->msgdev, "Free un-acquired block %d/%d "
"(received %d shots)\n",
i + 1, fa->n_shots, fa->n_fires);
zio_buffer_free_block(bi, zfad_block[i].block);
}
/* Clear active block */
fa->n_shots = 0;
fa->n_fires = 0;
kfree(zfad_block);
cset->interleave->priv_d = NULL;
/*
* Reset trigger source to avoid clean up the register for the next
* acquisition
*/
zfat_trigger_source_reset(fa);
return 0;
}
/*
* zfat_arm_trigger
* @ti: trigger instance
*
* The ADC in multi-shot mode need to allocate a block for each programmed shot.
* We need a custom arm_trigger to allocate N blocks at times.
*/
static int zfat_arm_trigger(struct zio_ti *ti)
{
struct zio_channel *interleave = ti->cset->interleave;
struct fa_dev *fa = ti->cset->zdev->priv_d;
struct zio_block *block;
struct zfad_block *zfad_block;
unsigned int size;
uint32_t dev_mem_off;
int i, err = 0;
dev_dbg(fa->msgdev, "Arming trigger\n");
/* Update the current control: sequence, nsamples and tstamp */
interleave->current_ctrl->nsamples = ti->nsamples;
/* Allocate the necessary blocks for multi-shot acquisition */
fa->n_shots = ti->zattr_set.std_zattr[ZIO_ATTR_TRIG_N_SHOTS].value;
dev_dbg(fa->msgdev, "programmed shot %i\n", fa->n_shots);
if (!fa->n_shots) {
dev_info(fa->msgdev, "Cannot arm. No programmed shots\n");
return -EINVAL;
}
/*
* Allocate a new block for DMA transfer. Sometimes we are in an
* atomic context and we cannot use in_atomic()
*/
zfad_block = kmalloc(sizeof(struct zfad_block) * fa->n_shots,
GFP_ATOMIC);
if (!zfad_block)
return -ENOMEM;
interleave->priv_d = zfad_block;
/*
* Calculate the required size to store all channels. This is
* an interleaved acquisition, so nsamples represents the
* number of sample on all channels (n_chan * chan_samples)
* Trig time stamp are appended after the post samples
* (4*32bits word) size should be 32bits word aligned
* ti->nsamples is the sum of (pre-samp+ post-samp)*4chan
* because it's the interleave channel.
*/
size = (interleave->current_ctrl->ssize * ti->nsamples)
+ FA_TRIG_TIMETAG_BYTES;
/* check if size is 32 bits word aligned: should be always the case */
if (size % 4) {
/* should never happen: increase the size accordling */
dev_warn(fa->msgdev,
"\nzio data block size should 32bit word aligned."
"original size:%d was increased by %d bytes\n",
size, size%4);
size += size % 4;
}
dev_mem_off = 0;
/* Allocate ZIO blocks */
for (i = 0; i < fa->n_shots; ++i) {
dev_dbg(fa->msgdev, "Allocating block %d ...\n", i);
block = zio_buffer_alloc_block(interleave->bi, size,
GFP_ATOMIC);
if (!block) {
dev_err(fa->msgdev,
"\narm trigger fail, cannot allocate block\n");
err = -ENOMEM;
goto out_allocate;
}
/* Copy the updated control into the block */
memcpy(zio_get_ctrl(block), interleave->current_ctrl,
zio_control_size(interleave));
/* Add to the vector of prepared blocks */
zfad_block[i].block = block;
zfad_block[i].cset = ti->cset;
zfad_block[i].shot_n = i;
}
err = ti->cset->raw_io(ti->cset);
if (err != -EAGAIN && err != 0)
goto out_allocate;
return err;
out_allocate:
while ((--i) >= 0)
zio_buffer_free_block(interleave->bi, zfad_block[i].block);
kfree(zfad_block);
interleave->priv_d = NULL;
return err;
}
/*
* zfat_abort
* @cset: channel set to abort
*
* Abort acquisition empty the list of prepared buffer.
*/
static void zfat_abort(struct zio_ti *ti)
{
struct zio_cset *cset = ti->cset;
struct fa_dev *fa = cset->zdev->priv_d;
struct zio_bi *bi = cset->interleave->bi;
struct zfad_block *zfad_block = cset->interleave->priv_d;
unsigned int i;
dev_dbg(fa->msgdev, "Aborting trigger\n");
/* Nothing to free */
if (!zfad_block)
return;
/* Free all blocks */
for (i = 0; i < fa->n_shots; ++i)
zio_buffer_free_block(bi, zfad_block[i].block);
kfree(zfad_block);
cset->interleave->priv_d = NULL;
}
/* Output it is not supported by this trigger */
static int zfat_push(struct zio_ti *ti, struct zio_channel *chan,
struct zio_block *block)
{
struct zio_cset *cset = ti->cset;
struct fa_dev *fa = cset->zdev->priv_d;
dev_err(fa->msgdev, "trigger \"%s\" does not support output\n",
ti->head.name);
return -EIO;
}
static const struct zio_trigger_operations zfat_ops = {
.create = zfat_create,
.destroy = zfat_destroy,
.change_status = zfat_change_status,
.data_done = zfat_data_done,
.arm = zfat_arm_trigger,
.abort = zfat_abort,
.push_block = zfat_push,
};
/* Definition of the trigger type -- can't be static */
struct zio_trigger_type zfat_type = {
.owner = THIS_MODULE,
.zattr_set = {
.std_zattr = zfat_std_zattr,
.ext_zattr = zfat_ext_zattr,
.n_ext_attr = ARRAY_SIZE(zfat_ext_zattr),
},
.s_op = &zfat_s_op,
.t_op = &zfat_ops,
};
int fa_trig_init(void)
{
int err;
err = zio_register_trig(&zfat_type, "adc-100m14b");
if (err)
pr_err("%s: Cannot register ZIO trigger type"
" \"adc-100m14b\" (error %i)\n", KBUILD_MODNAME, err);
return err;
}
void fa_trig_exit(void)
{
zio_unregister_trig(&zfat_type);
}
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2012-2019 Federico Vaga
*/
#ifndef _FIELD_DESC_H_
#define _FIELD_DESC_H_
#include <linux/types.h>
/*
* zfa_field_desc is a field register descriptor. By using address, mask
* and shift the driver can describe every fields in registers.
*/
struct zfa_field_desc {
unsigned long offset; /* related to its component base */
uint32_t mask; /* bit mask a register field */
int is_bitfield; /* whether it maps full register or a field */
};
#endif /* _FIELD_DESC_H_ */
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2020 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/fmc.h>
#include "platform_data/fmc-adc-100m14b4cha.h"
enum fa_spec_dev_offsets {
FA_SPEC_ADC_MEM_START = 0x000002000,
FA_SPEC_ADC_MEM_END = 0x000003FFF,
};
static const struct fmc_adc_platform_data fmc_adc_pdata = {
.flags = 0,
.calib_trig_time = 0,
.calib_trig_threshold = 0,
.calib_trig_internal = 0,
};
static int fa_spec_probe(struct platform_device *pdev) {
static struct resource fa_spec_fdt_res[] = {
{
.name = "fmc-adc-mem",
.flags = IORESOURCE_MEM,
},
{
.name = "fmc-adc-dma",
.flags = IORESOURCE_DMA,
},
{
.name = "fmc-adc-irq",
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
}};
struct platform_device_info pdevinfo = {
.parent = &pdev->dev,
.name = "fmc-adc-100m",
.id = PLATFORM_DEVID_AUTO,
.res = fa_spec_fdt_res,
.num_res = ARRAY_SIZE(fa_spec_fdt_res),
.data = &fmc_adc_pdata,
.size_data = sizeof(fmc_adc_pdata),
.dma_mask = DMA_BIT_MASK(32),
};
struct platform_device *pdev_child;
struct resource *rmem;
struct resource *r;
int irq;
int dma_dev_chan;
struct fmc_slot *slot;
bool present;
slot = fmc_slot_get(pdev->dev.parent, 1);
if (IS_ERR(slot)) {
dev_err(&pdev->dev, "Can't find FMC slot 1 err: %ld\n",
PTR_ERR(slot));
return PTR_ERR(slot);
}
present = fmc_slot_present(slot);
fmc_slot_put(slot);
if (!present) {
dev_err(&pdev->dev,
"FMC slot: 1, not present\n");
return -ENODEV;
}
rmem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!rmem) {
dev_err(&pdev->dev, "Missing memory resource\n");
return -EINVAL;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "Missing IRQ number\n");
return -EINVAL;
}
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!r) {
dev_err(&pdev->dev, "Missing DMA engine\n");
return -EINVAL;
}
dma_dev_chan = r->start;
fa_spec_fdt_res[0].parent = rmem;
fa_spec_fdt_res[0].start = rmem->start + FA_SPEC_ADC_MEM_START;
fa_spec_fdt_res[0].end = rmem->start + FA_SPEC_ADC_MEM_END;
fa_spec_fdt_res[1].start = dma_dev_chan;
fa_spec_fdt_res[2].start = irq;
pdev_child = platform_device_register_full(&pdevinfo);
if (IS_ERR(pdev_child))
return PTR_ERR(pdev_child);
platform_set_drvdata(pdev, pdev_child);
return 0;
}
static int fa_spec_remove(struct platform_device *pdev) {
struct platform_device *pdev_child = platform_get_drvdata(pdev);
platform_device_unregister(pdev_child);
return 0;
}
/**
* List of supported platform
*/
enum fa_spec_version {
FA_SPEC_VER = 0,
};
static const struct platform_device_id fa_spec_id_table[] = {
{
.name = "fmc-adc-100m-spec",
.driver_data = FA_SPEC_VER,
},
{
.name = "id:000010DC41444301",
.driver_data = FA_SPEC_VER,
},
{
.name = "id:000010dc41444301",
.driver_data = FA_SPEC_VER,
},
{},
};
static struct platform_driver fa_spec_driver = {
.driver =
{
.name = "fmc-adc-100m-spec",
.owner = THIS_MODULE,
},
.id_table = fa_spec_id_table,
.probe = fa_spec_probe,
.remove = fa_spec_remove,
};
module_platform_driver(fa_spec_driver);
MODULE_AUTHOR("Federico Vaga <federico.vaga@cern.ch>");
MODULE_LICENSE("GPL");
MODULE_VERSION(VERSION);
MODULE_DESCRIPTION("Driver for the SPEC ADC 100 MSamples 14 bits 4 Channels");
MODULE_DEVICE_TABLE(platform, fa_spec_id_table);
MODULE_SOFTDEP("pre: spec_fmc_carrier fmc-adc-100m14b4ch");
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2020 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/mfd/core.h>
#include <linux/fmc.h>
#include "platform_data/fmc-adc-100m14b4cha.h"
/*
* From SVEC but we do not want to add a dependency for these 4 registers
* which should never change by design. If they do, and you end up here:
* sorry! It shouldn't have happened.
*/
#define SVEC_BASE_REGS_CSR 0x40UL
#define SVEC_FPGA_CSR_DDR4_ADDR (SVEC_BASE_REGS_CSR + 0x18)
#define SVEC_FPGA_CSR_DDR4_DATA (SVEC_BASE_REGS_CSR + 0x1C)
#define SVEC_FPGA_CSR_DDR5_ADDR (SVEC_BASE_REGS_CSR + 0x20)
#define SVEC_FPGA_CSR_DDR5_DATA (SVEC_BASE_REGS_CSR + 0x24)
enum fa_svec_dev_offsets {
FA_SVEC_ADC1_MEM_START = 0x000002000,
FA_SVEC_ADC1_MEM_END = 0x00003FFF,
FA_SVEC_ADC2_MEM_START = 0x00004000,
FA_SVEC_ADC2_MEM_END = 0x000005FFF,
};
static struct fmc_adc_platform_data fmc_adc_pdata1 = {
.flags = FMC_ADC_BIG_ENDIAN | FMC_ADC_SVEC,
.vme_ddr_offset = SVEC_FPGA_CSR_DDR4_ADDR,
.calib_trig_time = 0,
.calib_trig_threshold = 0,
.calib_trig_internal = 0,
};
static struct fmc_adc_platform_data fmc_adc_pdata2 = {
.flags = FMC_ADC_BIG_ENDIAN | FMC_ADC_SVEC,
.vme_ddr_offset = SVEC_FPGA_CSR_DDR5_ADDR,
.calib_trig_time = 0,
.calib_trig_threshold = 0,
.calib_trig_internal = 0,
};
/* MFD devices */
enum svec_fpga_mfd_devs_enum {
FA_SVEC_MFD_FA1 = 0,
FA_SVEC_MFD_FA2,
};
static struct resource fa_svec_res1[] = {
{
.name = "fmc-adc-100m-mem.1",
.flags = IORESOURCE_MEM,
.start = FA_SVEC_ADC1_MEM_START,
.end = FA_SVEC_ADC1_MEM_END,
},
{
.name = "fmc-adc-100m-dma.1",
.flags = IORESOURCE_DMA,
},
{
.name = "fmc-adc-100m-irq.1",
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
.start = 0,
.end = 0,
},
};
static struct resource fa_svec_res2[] = {
{
.name = "fmc-adc-100m-mem.2",
.flags = IORESOURCE_MEM,
.start = FA_SVEC_ADC2_MEM_START,
.end = FA_SVEC_ADC2_MEM_END,
},
{
.name = "fmc-adc-100m-dma.2",
.flags = IORESOURCE_DMA,
},
{
.name = "fmc-adc-100m-irq.2",
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
.start = 1,
.end = 1,
},
};
#define MFD_ADC(_n) \
{ \
.name = "fmc-adc-100m", \
.platform_data = &fmc_adc_pdata##_n, \
.pdata_size = sizeof(fmc_adc_pdata##_n), \
.num_resources = ARRAY_SIZE(fa_svec_res##_n), \
.resources = fa_svec_res##_n, \
}
static const struct mfd_cell fa_svec_mfd_devs1[] = {
MFD_ADC(1),
};
static const struct mfd_cell fa_svec_mfd_devs2[] = {
MFD_ADC(2),
};
static const struct mfd_cell fa_svec_mfd_devs3[] = {
MFD_ADC(1),
MFD_ADC(2),
};
static const struct mfd_cell *fa_svec_mfd_devs[] = {
fa_svec_mfd_devs1,
fa_svec_mfd_devs2,
fa_svec_mfd_devs3,
};
static int fa_svec_probe(struct platform_device *pdev)
{
struct resource *rmem;
int idev = 0;
int ndev;
int irq;
int i;
rmem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!rmem) {
dev_err(&pdev->dev, "Missing memory resource\n");
return -EINVAL;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "Missing IRQ number\n");
return -EINVAL;
}
for (i = 1; i <= 2; ++i) {
struct fmc_slot *slot = fmc_slot_get(pdev->dev.parent, i);
int present;
if (IS_ERR(slot)) {
dev_err(&pdev->dev,
"Can't find FMC slot %d err: %ld\n",
i, PTR_ERR(slot));
return PTR_ERR(slot);
}
present = fmc_slot_present(slot);
fmc_slot_put(slot);
dev_dbg(&pdev->dev, "FMC slot: %d, present: %d\n",
i, present);
if (present)
idev |= BIT(i - 1);
}
if (idev == 0)
return -ENODEV;
idev--;
/*
* We know that this design uses the HTVIC IRQ controller.
* This IRQ controller has a linear mapping, so it is enough
* to give the first one as input
*/
ndev = 1 + !!(idev & 0x2);
dev_dbg(&pdev->dev, "Found %d, point to mfd_cell %d\n", ndev, idev);
return mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO,
fa_svec_mfd_devs[idev], ndev,
rmem, irq, NULL);
}
static int fa_svec_remove(struct platform_device *pdev)
{
mfd_remove_devices(&pdev->dev);
return 0;
}
/**
* List of supported platform
*/
enum fa_svec_version {
FA_SVEC_VER = 0,
};
static const struct platform_device_id fa_svec_id_table[] = {
{
.name = "fmc-adc-100m-svec",
.driver_data = FA_SVEC_VER,
},
{
.name = "id:000010DC41444302",
.driver_data = FA_SVEC_VER,
},
{
.name = "id:000010dc41444302",
.driver_data = FA_SVEC_VER,
},
{},
};
static struct platform_driver fa_svec_driver = {
.driver = {
.name = "fmc-adc-100m-svec",
.owner = THIS_MODULE,
},
.id_table = fa_svec_id_table,
.probe = fa_svec_probe,
.remove = fa_svec_remove,
};
module_platform_driver(fa_svec_driver);
MODULE_AUTHOR("Federico Vaga <federico.vaga@cern.ch>");
MODULE_LICENSE("GPL");
MODULE_VERSION(VERSION);
MODULE_DESCRIPTION("Driver for the SVEC ADC 100 MSamples 14 bits 4 Channels");
MODULE_DEVICE_TABLE(platform, fa_svec_id_table);
MODULE_SOFTDEP("pre: svec_fmc_carrier fmc-adc-100m14b4ch");
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright CERN 2012-2019
* Author: Federico Vaga <federico.vaga@gmail.com>
*/
#ifndef FMC_ADC_100M14B4C_H_
#define FMC_ADC_100M14B4C_H_
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#ifndef BIT
#define BIT(nr) (1UL << (nr))
#endif
#endif
/* Trigger sources */
#define FA100M14B4C_TRG_SRC_EXT BIT(0)
#define FA100M14B4C_TRG_SRC_SW BIT(1)
#define FA100M14B4C_TRG_SRC_TIM BIT(4)
#define FA100M14B4C_TRG_SRC_ALT BIT(5)
#define FA100M14B4C_TRG_SRC_CH1 BIT(8)
#define FA100M14B4C_TRG_SRC_CH2 BIT(9)
#define FA100M14B4C_TRG_SRC_CH3 BIT(10)
#define FA100M14B4C_TRG_SRC_CH4 BIT(11)
#define FA100M14B4C_TRG_SRC_CHx(_x) (FA100M14B4C_TRG_SRC_CH1 << ((_x) - 1))
/* Trigger Polarity */
#define FA100M14B4C_TRG_POL_EXT FA100M14B4C_TRG_SRC_EXT
#define FA100M14B4C_TRG_POL_CH1 FA100M14B4C_TRG_SRC_CH1
#define FA100M14B4C_TRG_POL_CH2 FA100M14B4C_TRG_SRC_CH2
#define FA100M14B4C_TRG_POL_CH3 FA100M14B4C_TRG_SRC_CH3
#define FA100M14B4C_TRG_POL_CH4 FA100M14B4C_TRG_SRC_CH4
#define FA100M14B4C_TRG_POL_CHx(_x) (FA100M14B4C_TRG_POL_CH1 << ((_x) - 1))
enum fa_versions {
ADC_VER = 0,
};
/*
* Trigger Extended Attribute Enumeration
*/
enum fa100m14b4c_trg_ext_attr {
/*
* The trigger extended attribute order is the same in the declaration
* and in the zio_control, so we can always use enumeration. But, the
* enumeration must start with 0 followed by only consecutive value.
*
* The parameters are not exposed to user space by zio_controle, so it
* is not necessary to export to user space the correspondent enum
*/
FA100M14B4C_TATTR_STA = 0,
FA100M14B4C_TATTR_SRC,
FA100M14B4C_TATTR_POL,
FA100M14B4C_TATTR_EXT_DLY,
FA100M14B4C_TATTR_CH1_THRES,
FA100M14B4C_TATTR_CH2_THRES,
FA100M14B4C_TATTR_CH3_THRES,
FA100M14B4C_TATTR_CH4_THRES,
FA100M14B4C_TATTR_CH1_HYST,
FA100M14B4C_TATTR_CH2_HYST,
FA100M14B4C_TATTR_CH3_HYST,
FA100M14B4C_TATTR_CH4_HYST,
FA100M14B4C_TATTR_CH1_DLY,
FA100M14B4C_TATTR_CH2_DLY,
FA100M14B4C_TATTR_CH3_DLY,
FA100M14B4C_TATTR_CH4_DLY,
FA100M14B4C_TATTR_TRG_TIM_SU,
FA100M14B4C_TATTR_TRG_TIM_SL,
FA100M14B4C_TATTR_TRG_TIM_C,
#ifdef __KERNEL__
FA100M14B4C_TATTR_TRG_SU,
FA100M14B4C_TATTR_TRG_SL,
FA100M14B4C_TATTR_TRG_C,
#endif
};
/*
* Device Extended Attribute Enumeration
*/
enum fa100m14b4c_dev_ext_attr {
/*
* NOTE: At the moment the only extended attributes we have in
* the device hierarchy are in the cset level, so we can safely
* start from index 0
*/
FA100M14B4C_DATTR_DECI = 0,
FA100M14B4C_DATTR_CH0_OFFSET,
FA100M14B4C_DATTR_CH1_OFFSET,
FA100M14B4C_DATTR_CH2_OFFSET,
FA100M14B4C_DATTR_CH3_OFFSET,
FA100M14B4C_DATTR_CH0_VREF,
FA100M14B4C_DATTR_CH1_VREF,
FA100M14B4C_DATTR_CH2_VREF,
FA100M14B4C_DATTR_CH3_VREF,
FA100M14B4C_DATTR_CH0_50TERM,
FA100M14B4C_DATTR_CH1_50TERM,
FA100M14B4C_DATTR_CH2_50TERM,
FA100M14B4C_DATTR_CH3_50TERM,
FA100M14B4C_DATTR_ACQ_START_S,
FA100M14B4C_DATTR_ACQ_START_C,
FA100M14B4C_DATTR_ACQ_START_F,
};
#define FA100M14B4C_UTC_CLOCK_FREQ 125000000
#define FA100M14B4C_UTC_CLOCK_NS 8
#define FA100M14B4C_NCHAN 4 /* We have 4 of them,no way out of it */
#define FA100M14B4C_NBIT 14
/* ADC DDR memory */
#define FA100M14B4C_MAX_ACQ_BYTE 0x10000000 /* 256MB */
enum fa100m14b4c_input_range {
FA100M14B4C_RANGE_10V = 0x0,
FA100M14B4C_RANGE_1V,
FA100M14B4C_RANGE_100mV,
FA100M14B4C_RANGE_OPEN, /* Channel disconnected from ADC */
FA100M14B4C_RANGE_10V_CAL, /* Channel disconnected from ADC */
FA100M14B4C_RANGE_1V_CAL, /* Channel disconnected from ADC */
FA100M14B4C_RANGE_100mV_CAL, /* Channel disconnected from ADC */
};
enum fa100m14b4c_fsm_cmd {
FA100M14B4C_CMD_NONE = 0x0,
FA100M14B4C_CMD_START = 0x1,
FA100M14B4C_CMD_STOP = 0x2,
};
/* All possible state of the state machine, other values are invalid*/
enum fa100m14b4c_fsm_state {
FA100M14B4C_STATE_IDLE = 0x1,
FA100M14B4C_STATE_PRE,
FA100M14B4C_STATE_WAIT,
FA100M14B4C_STATE_POST,
FA100M14B4C_STATE_DECR,
};
/* ADC and DAC Calibration, from EEPROM */
struct fa_calib_stanza {
int16_t offset[4]; /* One per channel */
uint16_t gain[4]; /* One per channel */
uint16_t temperature;
};
#define FA_CALIB_STANZA_N 3
struct fa_calib {
struct fa_calib_stanza adc[FA_CALIB_STANZA_N]; /* For input, one per range */
struct fa_calib_stanza dac[FA_CALIB_STANZA_N]; /* For user offset, one per range */
};
#ifdef __KERNEL__ /* All the rest is only of kernel users */
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/workqueue.h>
#include <linux/debugfs.h>
#include <linux/platform_device.h>
#include <linux/fmc.h>
#include <linux/zio.h>
#include <linux/zio-dma.h>
#include <linux/zio-sysfs.h>
#include <linux/zio-buffer.h>
#include <linux/zio-trigger.h>
#include "field-desc.h"
#include <platform_data/fmc-adc-100m14b4cha.h>
#define ADC_CSR_OFF 0x1000
#define ADC_EIC_OFF 0x1500
#define ADC_OW_OFF 0x1700
#define ADC_SPI_OFF 0x1800
#define ADC_UTC_OFF 0x1900
#define DAC_SAT_LOW -5000000
#define DAC_SAT_UP 5000000
#define ADC_DMA 0
enum fa_irq_resource {
ADC_IRQ_TRG = 0,
};
enum fa_mem_resource {
ADC_MEM_BASE = 0,
};
enum fa_bus_resource {
ADC_CARR_VME_ADDR,
};
struct fa_memory_ops {
u32 (*read)(void *addr);
void (*write)(u32 value, void *addr);
};
/*
* ZFA_CHx_MULT : the trick which requires channel regs id grouped and ordered
* address offset between two registers of the same type on consecutive channel
*/
#define ZFA_CHx_MULT 9
/* Device registers */
enum zfadc_dregs_enum {
/* Device */
/* Control registers */
ZFA_CTL_FMS_CMD,
ZFA_CTL_CLK_EN,
ZFA_CTL_DAC_CLR_N,
ZFA_CTL_BSLIP,
ZFA_CTL_TEST_DATA_EN,
ZFA_CTL_TRIG_LED,
ZFA_CTL_ACQ_LED,
ZFA_CTL_RST_TRG_STA,
ZFA_CTL_CALIB_APPLY,
/* Status registers */
ZFA_STA_FSM,
ZFA_STA_SERDES_PLL,
ZFA_STA_SERDES_SYNCED,
ZFA_STA_FMC_NR,
ZFA_STA_CALIB_BUSY,
/* Configuration register */
ZFAT_CFG_STA,
ZFAT_CFG_SRC,
ZFAT_CFG_POL,
/* Delay*/
ZFAT_EXT_DLY,
/* Software */
ZFAT_SW,
/* Number of shots */
ZFAT_SHOTS_NB,
/* Remaining shots counter */
ZFAT_SHOTS_REM,
/* Sample rate */
ZFAT_SR_UNDER,
/* Sampling clock frequency */
ZFAT_SAMPLING_HZ,
/* Position address */
ZFAT_POS,
/* Pre-sample */
ZFAT_PRE,
/* Post-sample */
ZFAT_POST,
/* Sample counter */
ZFAT_CNT,
/* start:declaration block requiring some order */
/* Channel 1 */
ZFA_CH1_CTL_RANGE,
ZFA_CH1_CTL_TERM,
ZFA_CH1_STA,
ZFA_CH1_GAIN,
ZFA_CH1_OFFSET,
ZFA_CH1_SAT,
ZFA_CH1_THRES,
ZFA_CH1_HYST,
ZFA_CH1_DLY,
/* Channel 2 */
ZFA_CH2_CTL_RANGE,
ZFA_CH2_CTL_TERM,
ZFA_CH2_STA,
ZFA_CH2_GAIN,
ZFA_CH2_OFFSET,
ZFA_CH2_SAT,
ZFA_CH2_THRES,
ZFA_CH2_HYST,
ZFA_CH2_DLY,
/* Channel 3 */
ZFA_CH3_CTL_RANGE,
ZFA_CH3_CTL_TERM,
ZFA_CH3_STA,
ZFA_CH3_GAIN,
ZFA_CH3_OFFSET,
ZFA_CH3_SAT,
ZFA_CH3_THRES,
ZFA_CH3_HYST,
ZFA_CH3_DLY,
/* Channel 4 */
ZFA_CH4_CTL_RANGE,
ZFA_CH4_CTL_TERM,
ZFA_CH4_STA,
ZFA_CH4_GAIN,
ZFA_CH4_OFFSET,
ZFA_CH4_SAT,
ZFA_CH4_THRES,
ZFA_CH4_HYST,
ZFA_CH4_DLY,
/*
* CHx__ are specifc ids used by some internal arithmetic
* Be carefull: the arithmetic expects
* that ch1 to ch4 are declared in the enum just above
* in the right order and grouped.
* Don't insert any other id in this area
*/
ZFA_CHx_CTL_RANGE,
ZFA_CHx_CTL_TERM,
ZFA_CHx_STA,
ZFA_CHx_GAIN,
ZFA_CHx_OFFSET,
ZFA_CHx_SAT,
ZFA_CHx_THRES,
ZFA_CHx_HYST,
ZFA_CHx_DLY,
/* Other options */
ZFA_MULT_MAX_SAMP,
/* end:declaration block requiring some order */
/* two wishbone core for IRQ: VIC, ADC */
ZFA_IRQ_ADC_DISABLE_MASK,
ZFA_IRQ_ADC_ENABLE_MASK,
ZFA_IRQ_ADC_MASK_STATUS,
ZFA_IRQ_ADC_SRC,
ZFA_IRQ_VIC_CTRL,
ZFA_IRQ_VIC_DISABLE_MASK,
ZFA_IRQ_VIC_ENABLE_MASK,
ZFA_IRQ_VIC_MASK_STATUS,
/* DS18B20 UID/Temperature */
ZFA_DS18B20_ID_U,
ZFA_DS18B20_ID_L,
ZFA_DS18B20_TEMP,
ZFA_DS18B20_STAT,
/* UTC core */
ZFA_UTC_SECONDS_U,
ZFA_UTC_SECONDS_L,
ZFA_UTC_COARSE,
ZFA_UTC_TRIG_TIME_SECONDS_U,
ZFA_UTC_TRIG_TIME_SECONDS_L,
ZFA_UTC_TRIG_TIME_COARSE,
ZFA_UTC_TRIG_SECONDS_U,
ZFA_UTC_TRIG_SECONDS_L,
ZFA_UTC_TRIG_COARSE,
ZFA_UTC_ACQ_START_SECONDS_U,
ZFA_UTC_ACQ_START_SECONDS_L,
ZFA_UTC_ACQ_START_COARSE,
ZFA_UTC_ACQ_STOP_SECONDS_U,
ZFA_UTC_ACQ_STOP_SECONDS_L,
ZFA_UTC_ACQ_STOP_COARSE,
ZFA_UTC_ACQ_END_SECONDS_U,
ZFA_UTC_ACQ_END_SECONDS_L,
ZFA_UTC_ACQ_END_COARSE,
ZFA_HW_PARAM_COMMON_LAST,
};
/*
* Acquisition metadata. It contains the trigger timestamp and the trigger
* source. This block is added after the post-trigger-samples in the DDR.
*/
#define FA_TRIG_TIMETAG_BYTES 0x10
/*
* ADC parameter id not mapped to Hw register
* Id is used as zio attribute id
*/
enum fa_sw_param_id {
/* to guarantee unique zio attr id */
ZFA_SW_R_NOADDRES_NBIT = ZFA_HW_PARAM_COMMON_LAST,
ZFA_SW_R_NOADDRES_TEMP,
ZFA_SW_R_NOADDERS_AUTO,
ZFA_SW_R_NOADDERS_RAND,
ZFA_SW_CH1_OFFSET_ZERO,
ZFA_SW_CH2_OFFSET_ZERO,
ZFA_SW_CH3_OFFSET_ZERO,
ZFA_SW_CH4_OFFSET_ZERO,
ZFA_SW_PARAM_COMMON_LAST,
};
/* adc IRQ values */
enum fa_irq_adc {
FA_IRQ_ADC_NONE = 0x0,
FA_IRQ_ADC_ACQ_END = 0x2,
};
/* Carrier-specific operations (gateware does not fully decouple
carrier specific stuff, such as DMA or resets, from
mezzanine-specific operations). */
struct fa_dev; /* forward declaration */
/*
* Flag bit set when the ADC uses pattern data
*/
#define FA_DEV_F_PATTERN_DATA BIT(0)
/*
* fa_dev: is the descriptor of the FMC ADC mezzanine
*
* @pdev: the pointer to the fmc_device generic structure
* @zdev: is the pointer to the real zio_device in use
* @hwzdev: is the pointer to the fake zio_device, used to initialize and
* to remove a zio_device
*
* @n_shots: total number of programmed shots for an acquisition
* @n_fires: number of trigger fire occurred within an acquisition
*
* @n_dma_err: number of errors
* @user_offset: user offset (micro-Volts)
* @zero_offset: necessary offset to push the channel to zero (micro-Volts)
*/
struct fa_dev {
unsigned long flags;
struct device *msgdev; /**< device used to print messages */
/* the pointer to the platform_device generic structure */
struct platform_device *pdev;
/* the pointer to the real zio_device in use */
struct zio_device *zdev;
/* the pointer to the fake zio_device, used for init/remove */
struct zio_device *hwzdev;
struct dma_chan *dchan;
struct fmc_slot *slot;
struct fa_memory_ops memops;
/* carrier common base offset addresses */
void *fa_adc_csr_base;
void *fa_spi_base;
void *fa_ow_base;
void *fa_top_level;
void *fa_irq_vic_base;
void *fa_irq_adc_base;
void *fa_utc_base;
/* DMA description */
struct zio_dma_sgt *zdma;
struct sg_table sgt;
struct work_struct irq_work;
/*
* keep last core having fired an IRQ
* Used to check irq sequence: ACQ followed by DMA
*/
int last_irq_core_src;
/* Acquisition */
unsigned int n_shots;
unsigned int n_fires;
unsigned int transfers_left;
unsigned int mshot_max_samples;
/* Statistic informations */
unsigned int n_dma_err;
/* Configuration */
int32_t user_offset[4]; /* one per channel */
int32_t zero_offset[FA100M14B4C_NCHAN];
/* one-wire */
uint8_t ds18_id[8];
unsigned long next_t;
int temp; /* temperature: scaled by 4 bits */
/* Calibration Data */
struct fa_calib calib;
int32_t range[FA100M14B4C_NCHAN];
struct timer_list calib_timer;
/* flag */
int enable_auto_start;
struct dentry *dbg_dir;
struct debugfs_regset32 dbg_reg32;
struct dentry *dbg_reg;
struct dentry *dbg_reg_spi;
struct dentry *dbg_trg_sw;
struct dentry *dbg_data_pattern;
/* Operations */
int (*sg_alloc_table_from_pages)(struct sg_table *sgt,
struct page **pages,
unsigned int n_pages,
unsigned int offset,
unsigned long size,
unsigned int max_segment,
gfp_t gfp_mask);
};
/*
* zfad_block
* @block is zio_block which contains data and metadata from a single shot
* @first_nent is the index of the first nent used for this block
* @cset: channel set source for the block
* @tx: DMA transfer descriptor
* @cookie: transfer token
*/
struct zfad_block {
struct zio_block *block;
unsigned int first_nent;
struct zio_cset *cset;
struct dma_async_tx_descriptor *tx;
dma_cookie_t cookie;
struct sg_table sgt;
void *dma_ctx;
unsigned int shot_n;
struct dma_slave_config sconfig;
};
/*
* Channel signal transmission delay
* Trigger and channel signals are not going through the
* same path on the board and trigger is faster.
* Trying to sample the trigger itself by connecting
* it to a channel, one can see a delay of 30ns between trigger and
* its sampling. This constant is added to the trigger delay to
* conpensate the channel signal transmission delay.
* Expressed in tick count 3*10ns = 30ns
*/
#define FA_CH_TX_DELAY 3
#define FA_CAL_OFFSET 0x0100 /* Offset in EEPROM */
#define FA_CAL_NO_OFFSET ((int16_t)0x0000)
#define FA_CAL_NO_GAIN ((uint16_t)0x8000)
/* SPI Slave Select lines (as defined in spec_top_fmc_adc_100Ms.vhd) */
#define FA_SPI_SS_ADC 0
#define FA_SPI_SS_DAC(ch) ((ch) + 1)
/* Global variable exported by fa-zio-trg.c */
extern struct zio_trigger_type zfat_type;
static inline struct fa_dev *get_zfadc(struct device *dev)
{
switch (to_zio_head(dev)->zobj_type) {
case ZIO_DEV:
return to_zio_dev(dev)->priv_d;
case ZIO_CSET:
return to_zio_cset(dev)->zdev->priv_d;
case ZIO_CHAN:
return to_zio_chan(dev)->cset->zdev->priv_d;
case ZIO_TI:
return to_zio_ti(dev)->cset->zdev->priv_d;
default:
return NULL;
}
return NULL;
}
static inline u32 fa_ioread(struct fa_dev *fa, void *addr)
{
return fa->memops.read(addr);
}
static inline void fa_iowrite(struct fa_dev *fa, u32 value, void *addr)
{
fa->memops.write(value, addr);
}
static inline uint32_t fa_readl(struct fa_dev *fa,
void *base_off,
const struct zfa_field_desc *field)
{
uint32_t cur;
cur = fa_ioread(fa, base_off + field->offset);
if (field->is_bitfield) {
/* apply mask and shift right accordlying to the mask */
cur &= field->mask;
cur /= (field->mask & -(field->mask));
} else {
cur &= field->mask; /* bitwise and with the mask */
}
return cur;
}
static inline void fa_writel(struct fa_dev *fa,
void *base_off,
const struct zfa_field_desc *field,
uint32_t usr_val)
{
uint32_t cur, val;
val = usr_val;
/* Read current register value first if it's a bitfield */
if (field->is_bitfield) {
cur = fa_ioread(fa, base_off+field->offset);
/* */
cur &= ~field->mask; /* clear bits according to the mask */
val = usr_val * (field->mask & -(field->mask));
if (val & ~field->mask)
dev_warn(fa->msgdev,
"addr %p: value 0x%x doesn't fit mask 0x%x\n",
base_off+field->offset, val, field->mask);
val &= field->mask;
val |= cur;
}
fa_iowrite(fa, val, base_off + field->offset);
}
static inline int fa_is_flag_set(struct fa_dev *fa, unsigned long flag)
{
const struct fmc_adc_platform_data *fmc_adc_pdata;
fmc_adc_pdata = fa->pdev->dev.platform_data;
return !!(fmc_adc_pdata->flags & flag);
}
extern struct bin_attribute dev_attr_calibration;
/* Global variable exported by fa-core.c */
extern struct workqueue_struct *fa_workqueue;
/* Global variable exported by fa-regtable.c */
extern const struct zfa_field_desc zfad_regs[];
/* Functions exported by fa-core.c */
extern int zfad_fsm_command(struct fa_dev *fa, uint32_t command);
extern int zfad_convert_hw_range(uint32_t bitmask);
extern int32_t fa_temperature_read(struct fa_dev *fa);
extern int fa_trigger_software(struct fa_dev *fa);
extern int fa_fsm_wait_state(struct fa_dev *fa,
enum fa100m14b4c_fsm_state state,
unsigned int timeout_us);
extern int fa_adc_data_pattern_set(struct fa_dev *fa, uint16_t pattern,
unsigned int enable);
extern int fa_adc_data_pattern_get(struct fa_dev *fa, uint16_t *pattern,
unsigned int *enable);
extern int fa_adc_output_randomizer_set(struct fa_dev *fa, bool enable);
extern bool fa_adc_is_output_randomizer(struct fa_dev *fa);
/* Temporarily, user values are the same as hardware values */
extern int zfad_convert_user_range(uint32_t user_val);
extern int fa_adc_range_set(struct fa_dev *fa, struct zio_channel *chan,
int range);
extern int zfad_get_chx_index(unsigned long addr, unsigned int chan);
/* Function exported by fa-dma.c */
extern void fa_irq_work(struct work_struct *work);
extern int fa_dma_request_channel(struct fa_dev *fa);
extern void fa_dma_release_channel(struct fa_dev *fa);
/* Functions exported by fa-zio-drv.c */
extern int fa_zio_register(void);
extern void fa_zio_unregister(void);
extern int fa_zio_init(struct fa_dev *fa);
extern void fa_zio_exit(struct fa_dev *fa);
/* Functions exported by fa-zio-trg.c */
extern void zfat_trigger_source_reset(struct fa_dev *fa);
extern int fa_trig_init(void);
extern void fa_trig_exit(void);
/* Functions exported by fa-irq.c */
extern void zfat_irq_trg_fire(struct zio_cset *cset);
extern int fa_setup_irqs(struct fa_dev *fa);
extern int fa_free_irqs(struct fa_dev *fa);
extern int fa_enable_irqs(struct fa_dev *fa);
extern int fa_disable_irqs(struct fa_dev *fa);
/* functions exported by spi.c */
extern int fa_spi_xfer(struct fa_dev *fa, int cs, int num_bits,
uint32_t tx, uint32_t *rx);
extern int fa_spi_init(struct fa_dev *fd);
extern void fa_spi_exit(struct fa_dev *fd);
/* function exporetd by fa-calibration.c */
extern int fa_calib_init(struct fa_dev *fa);
extern void fa_calib_exit(struct fa_dev *fa);
extern void fa_calib_config(struct fa_dev *fa);
extern void fa_calib_adc_config_chan(struct fa_dev *fa, unsigned int chan,
int32_t temperature);
extern int fa_calib_dac_config_chan(struct fa_dev *fa, unsigned int chan,
int32_t temperature);
/* functions exported by fa-debug.c */
extern int fa_debug_init(struct fa_dev *fa);
extern void fa_debug_exit(struct fa_dev *fa);
#endif /* __KERNEL__ */
#endif /* FMC_ADC_H_ */
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2020 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#ifndef __FMC_ADC_PDATA_H__
#define __FMC_ADC_PDATA_H__
#define FMC_ADC_BIG_ENDIAN BIT(0)
#define FMC_ADC_NOSQUASH_SCATTERLIST BIT(1)
/*
* In principle this should not be necessary. The two variants should
* be as close as possible to each other. But this is not the case, the DMA
* interface is different and we need to distinguish between SPEC and SVEC.
* NOTE any other carrier is not supported!
*/
#define FMC_ADC_SVEC BIT(3)
struct fmc_adc_platform_data {
unsigned long flags;
unsigned long vme_ddr_offset;
uint8_t calib_trig_time;
uint8_t calib_trig_threshold;
uint8_t calib_trig_internal;
};
#endif
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@gmail.com>
*/
#include <linux/jiffies.h>
#include <linux/io.h>
#include <linux/delay.h>
#include "fmc-adc-100m14b4cha.h"
/* SPI register */
#define FA_SPI_RX(x) (x * 4)
#define FA_SPI_TX(x) (x * 4)
#define FA_SPI_CTRL 0x10
#define FA_SPI_DIV 0x14
#define FA_SPI_CS 0x18
/* SPI control register fields mask */
#define FA_SPI_CTRL_CHAR_LEN 0x007F
#define FA_SPI_CTRL_GO 0x0100 /* go/busy */
#define FA_SPI_CTRL_BUSY 0x0100 /* go/busy */
#define FA_SPI_CTRL_Rx_NEG 0x0200
#define FA_SPI_CTRL_Tx_NEG 0x0400
#define FA_SPI_CTRL_LSB 0x0800
#define FA_SPI_CTRL_IE 0x1000
#define FA_SPI_CTRL_ASS 0x2000
int fa_spi_xfer(struct fa_dev *fa, int cs, int num_bits,
uint32_t tx, uint32_t *rx)
{
uint32_t regval;
unsigned long j = jiffies + HZ;
int err = 0;
/* Put out value (LSB-aligned) in the T0 register (bits 0..31) */
fa_iowrite(fa, tx, fa->fa_spi_base + FA_SPI_TX(0));
/* Configure SPI controller */
regval = FA_SPI_CTRL_ASS | /* Automatic Slave Select*/
FA_SPI_CTRL_Tx_NEG | /* Change on falling edge */
num_bits; /* In CHAR_LEN field */
fa_iowrite(fa, regval, fa->fa_spi_base + FA_SPI_CTRL);
/* Set Chip Select */
fa_iowrite(fa, (1 << cs), fa->fa_spi_base + FA_SPI_CS);
/* Start transfer */
fa_iowrite(fa, regval | FA_SPI_CTRL_GO,
fa->fa_spi_base + FA_SPI_CTRL);
/* Wait transfer complete */
while (fa_ioread(fa, fa->fa_spi_base + FA_SPI_CTRL)
& FA_SPI_CTRL_BUSY) {
if (jiffies > j) {
dev_err(fa->msgdev, "SPI transfer error cs:%d, ctrl: 0x%x\n",
cs, fa_ioread(fa, fa->fa_spi_base + FA_SPI_CTRL));
err = -EIO;
goto out;
}
}
/* Transfer compleate, read data */
regval = fa_ioread(fa, fa->fa_spi_base + FA_SPI_RX(0));
if (rx)
*rx = regval;
out:
/* Clear Chip Select */
fa_iowrite(fa, 0, fa->fa_spi_base + FA_SPI_CTRL);
return err;
}
int fa_spi_init(struct fa_dev *fa)
{
uint32_t rx;
/* Divider must be 100, according to firmware guide */
fa_iowrite(fa, 100, fa->fa_spi_base + FA_SPI_DIV);
/* software reset the ADC chip (register 0) */
fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, BIT(8), &rx);
msleep(5);
/* Force 2's complement data output (register 1, bit 5) */
fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, (1 << 8) | (1 << 5), &rx);
return 0;
}
void fa_spi_exit(struct fa_dev *fa)
{
/* nothing to do */
}
fau-acq-time
fau-trg-config
fau-calibration
parport-burst
# If it exists includes Makefile.specific. In this Makefile, you should put
# specific Makefile code that you want to run before this. For example,
# build a particular environment.
-include Makefile.specific
# include parent_common.mk for buildsystem's defines
REPO_PARENT ?= ../..
-include $(REPO_PARENT)/parent_common.mk
# user-space tools for spec-fine-delay
DESTDIR ?= /usr/local
GIT_VERSION := $(shell git describe --dirty --long --tags)
CFLAGS += -I../kernel -Wno-trigraphs -Wall -Werror -ggdb -O2 $(EXTRACFLAGS)
CFLAGS += -DGIT_VERSION="\"$(GIT_VERSION)\""
CC ?= $(CROSS_COMPILE)gcc
progs := fau-trg-config
progs += fau-acq-time
progs += fau-calibration
progs += parport-burst
# we are not in the kernel, so we need to piggy-back on "make modules"
all modules: $(progs)
clean:
rm -f $(progs) *.o *~
# make nothing for modules_install, but avoid errors
modules_install:
install:
install -d $(DESTDIR)/bin
install -D $(progs) $(DESTDIR)/bin
# we need this as we are out of the kernel
%: %.c
$(CC) $(CFLAGS) $^ -o $@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
* Copyright 2012-2019 CERN
* Author: Federico Vaga <federico.vaga@gmail.com>
*
* This is a simple program which calculate the acquisition time.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <getopt.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
static char git_version[] = "version: " GIT_VERSION;
#define buf_len 50
/* user will edit by adding the device name */
char basepath[40] = "/sys/bus/zio/devices/";
enum fau_attribute {
FAU_UTR_STR_S,
FAU_UTR_STR_T,
FAU_UTR_STR_B,
FAU_UTR_END_S,
FAU_UTR_END_T,
FAU_UTR_END_B,
FAU_UTR_STP_S,
FAU_UTR_STP_T,
FAU_UTR_STP_B,
FAU_UTR_TRG_S,
FAU_UTR_TRG_T,
FAU_UTR_TRG_B,
FAU_UTC_NUM_ATTR,
};
const char *attribute[] = {
[FAU_UTR_STR_S] = "/cset0/tstamp-acq-str-s",
[FAU_UTR_STR_T] = "/cset0/tstamp-acq-str-t",
[FAU_UTR_STR_B] = "/cset0/tstamp-acq-str-b",
[FAU_UTR_END_S] = "/cset0/tstamp-acq-end-s",
[FAU_UTR_END_T] = "/cset0/tstamp-acq-end-t",
[FAU_UTR_END_B] = "/cset0/tstamp-acq-end-b",
[FAU_UTR_STP_S] = "/cset0/tstamp-acq-stp-s",
[FAU_UTR_STP_T] = "/cset0/tstamp-acq-stp-t",
[FAU_UTR_STP_B] = "/cset0/tstamp-acq-stp-b",
[FAU_UTR_TRG_S] = "/cset0/trigger/tstamp-trg-lst-s",
[FAU_UTR_TRG_T] = "/cset0/trigger/tstamp-trg-lst-t",
[FAU_UTR_TRG_B] = "/cset0/trigger/tstamp-trg-lst-b",
};
/* Write a sysfs attribute */
int fau_read_attribute(enum fau_attribute attr, long *val)
{
char fullpath[200];
FILE *f;
sprintf(fullpath, "%s%s", basepath, attribute[attr]);
f = fopen(fullpath, "r");
if (!f)
return -1;
if (fscanf(f, "%li", val) != 1) {
fclose(f);
errno = EINVAL;
return -1;
}
fclose(f);
return 0;
}
int fau_read_time(long time[3], enum fau_attribute first_attr)
{
int i, attr = first_attr, err = 0;
for(i = 0; i < 3; ++i, ++attr) {
err += (fau_read_attribute(attr, &time[i]) * (-1));
if (i == 1)
time[i] *= 8; /* convert ticks (125Mhz) */
}
return err;
}
void fau_print_time(long time1[3], long time2[3])
{
long result[3];
result[0] = time2[0] - time1[0];
if (time2[1] >= time1[1]) {
result[1] = time2[1] - time1[1];
} else {
result[0]--;
result[1] = (1000000000)-(time1[1] - time1[2]);
}
printf("Acquisition time: %li.%09li\n\n",
result[0], result[1]);
}
static void fau_help()
{
printf("\nfau-acq-time [OPTIONS] <DEVICE>\n\n");
printf(" <DEVICE>: ZIO name of the device to use\n");
printf(" --last|-l : time between the last trigger and the acquisition end\n");
printf(" --full|-f : time between the acquisition start and the acquisition end\n");
printf(" --version|-V : print version information\n");
printf(" --help|-h : show this help\n\n");
}
static void print_version(char *pname)
{
printf("%s %s\n", pname, git_version);
}
int main(int argc, char *argv[])
{
/* getop attribute */
static int last = 0, full = 0;
static struct option options[] = {
{"last",no_argument, &last, 1},
{"full",no_argument, &full, 1},
{"version",no_argument, 0, 'V'},
{"help",no_argument, 0, 'h'},
{0, 0, 0, 0}
};
int opt_index = 0, err = 0;
char c;
long time1[3], time2[3];
if (argc == 1) {
fau_help();
exit(1);
}
while( (c = getopt_long(argc, argv, "lfVh", options, &opt_index)) >=0 ){
if (c == 'h') {
fau_help();
exit(1);
break;
}
if (c == 'V') {
print_version(argv[0]);
exit(1);
}
}
if (optind != argc - 1 ) {
fprintf(stderr, "%s: DEVICE-ID is a mandatory argument\n",
argv[0]);
fau_help();
exit(1);
}
strcat(basepath, argv[argc-1]);
printf("Sysfs path to device is: %s\n", basepath);
if (last) {
err = fau_read_time(time1, FAU_UTR_TRG_S);
if (err)
exit(1);
printf("Last Trigger fired at %li.%09li\n",
time1[0], time1[1]);
err = fau_read_time(time2, FAU_UTR_END_S);
if (err)
exit(1);
printf("Last Acquisition end at %li.%09li\n",
time2[0], time2[1]);
fau_print_time(time1, time2);
}
if (full) {
err = fau_read_time(time1, FAU_UTR_STR_S);
if (err)
exit(1);
printf("Last Acquisition start at %li.%09li\n",
time1[0], time1[1]);
err = fau_read_time(time2, FAU_UTR_END_S);
if (err)
exit(1);
printf("Last Acquisition end at %li.%09li\n",
time2[0], time2[1]);
fau_print_time(time1, time2);
}
exit(0);
}
// SPDX-License-Identifier: GPL-3.0-or-later
/*
* Copyright (C) 2019 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <inttypes.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <endian.h>
#include <fmc-adc-100m14b4cha.h>
static char options[] = "hf:o:D:b";
static const char help_msg[] =
"Usage: fau-calibration [options]\n"
"\n"
"It reads calibration data from a file that contains it in binary\n"
"form and it shows it on STDOUT in binary form or in human readable\n"
"one (default).\n"
"This could be used to change the ADC calibration data at runtime\n"
"by redirectiong the binary output of this program to the proper \n"
"sysfs binary attribute\n"
"Rembember that we expect all values to be little endian\n"
"\n"
"General options:\n"
"-h Print this message\n"
"-b Show Calibration in binary form \n"
"\n"
"Read options:\n"
"-f Source file where to read calibration data from\n"
"-o Offset in bytes within the file (default 0)\n"
"Write options:\n"
"-D FMC ADC Target Device ID\n"
"\n";
/**
* Read calibration data from file
* @path: file path
* @calib: calibration data
* @offset: offset in file
*
* Return: number of bytes read
*/
static int fau_calibration_read(char *path, struct fa_calib *calib,
off_t offset)
{
int fd;
int ret = 0;
fd = open(path, O_RDONLY);
if (fd < 0)
return -1;
ret = lseek(fd, offset, SEEK_SET);
if (ret >= 0)
ret = read(fd, calib, sizeof(*calib));
close(fd);
return ret;
}
static void fau_calibration_dump_stanza(struct fa_calib_stanza *stanza)
{
fprintf(stdout, " temperature: %f C\n",
stanza->temperature * 0.01);
fprintf(stdout, " gain: [0x%04"PRIx16", 0x%04"PRIx16", 0x%04"PRIx16", 0x%04"PRIx16"]\n",
stanza->gain[0],
stanza->gain[1],
stanza->gain[2],
stanza->gain[3]);
fprintf(stdout, " offset: [0x%04"PRIx16", 0x%04"PRIx16", 0x%04"PRIx16", 0x%04"PRIx16"]\n",
stanza->offset[0],
stanza->offset[1],
stanza->offset[2],
stanza->offset[3]);
}
/**
* Print calibration data on stdout in humand readable format
* @calib: calibration data
*/
static void fau_calibration_dump_human(struct fa_calib *calib)
{
uint16_t *data16 = (uint16_t *)calib;
int i;
/* Fix endianess */
for (i = 0; i < sizeof(*calib) / sizeof(uint16_t); ++i)
data16[i] = le16toh(data16[i]);
fputs("ADC Range 10V\n", stdout);
fau_calibration_dump_stanza(&calib->adc[FA100M14B4C_RANGE_10V]);
fputs("DAC Range 10V\n", stdout);
fau_calibration_dump_stanza(&calib->dac[FA100M14B4C_RANGE_10V]);
fputs("ADC Range 1V\n", stdout);
fau_calibration_dump_stanza(&calib->adc[FA100M14B4C_RANGE_1V]);
fputs("DAC Range 1V\n", stdout);
fau_calibration_dump_stanza(&calib->dac[FA100M14B4C_RANGE_1V]);
fputs("ADC Range 100mV\n", stdout);
fau_calibration_dump_stanza(&calib->adc[FA100M14B4C_RANGE_100mV]);
fputs("DAC Range 100mV\n", stdout);
fau_calibration_dump_stanza(&calib->dac[FA100M14B4C_RANGE_100mV]);
fputc('\n', stdout);
}
/**
* Print binary calibration data on stdout
* @calib: calibration data
*/
static int fau_calibration_dump_machine(struct fa_calib *calib)
{
return write(fileno(stdout), calib, sizeof(*calib));
}
/**
* Write calibration data to device
* @devid: Device ID
* @calib: calibration data
*
* Return: number of bytes wrote
*/
static int fau_calibration_write(unsigned int devid, struct fa_calib *calib)
{
char path[128];
int fd;
int ret;
sprintf(path,
"/sys/bus/zio/devices/adc-100m14b-%04x/calibration_data",
devid);
fd = open(path, O_WRONLY);
if (fd < 0)
return -1;
ret = write(fd, calib, sizeof(*calib));
close(fd);
return ret;
}
int main(int argc, char *argv[])
{
char c;
int ret;
char *path = NULL;
unsigned int offset = 0;
unsigned int devid = 0;
int show_bin = 0, write = 0;
struct fa_calib calib;
while ((c = getopt(argc, argv, options)) != -1) {
switch (c) {
default:
case 'h':
fprintf(stderr, help_msg);
exit(EXIT_SUCCESS);
case 'D':
ret = sscanf(optarg, "0x%x", &devid);
if (ret != 1) {
fprintf(stderr,
"Invalid devid %s\n",
optarg);
exit(EXIT_FAILURE);
}
write = 1;
break;
case 'f':
path = optarg;
break;
case 'o':
ret = sscanf(optarg, "0x%x", &offset);
if (ret != 1) {
ret = sscanf(optarg, "%u", &offset);
if (ret != 1) {
fprintf(stderr,
"Invalid offset %s\n",
optarg);
exit(EXIT_FAILURE);
}
}
break;
case 'b':
show_bin = 1;
break;
}
}
if (!path) {
fputs("Calibration file is mandatory\n", stderr);
exit(EXIT_FAILURE);
}
/* Read EEPROM file */
ret = fau_calibration_read(path, &calib, offset);
if (ret < 0) {
fprintf(stderr, "Can't read calibration data from '%s'. %s\n",
path, strerror(errno));
exit(EXIT_FAILURE);
}
if (ret != sizeof(calib)) {
fprintf(stderr,
"Can't read all calibration data from '%s'. %s\n",
path, strerror(errno));
exit(EXIT_FAILURE);
}
/* Show calibration data*/
if (show_bin)
fau_calibration_dump_machine(&calib);
else if(!write)
fau_calibration_dump_human(&calib);
/* Write calibration data */
if (write) {
ret = fau_calibration_write(devid, &calib);
if (ret < 0) {
fprintf(stderr,
"Can't write calibration data to '0x%x'. %s\n",
devid, strerror(errno));
exit(EXIT_FAILURE);
}
if (ret != sizeof(calib)) {
fprintf(stderr,
"Can't write all calibration data to '0x%x'. %s\n",
devid, strerror(errno));
exit(EXIT_FAILURE);
}
}
exit(EXIT_SUCCESS);
}
#!/usr/bin/wish
set base "/sys/bus/zio/devices/adc-100m14b-"
# We trace the "v" (values) array to write to sysfs
trace add variable v write doact
proc doact {var element op} {
global v; # lazy me
global argv0 base
foreach "index channel what" [split $element :] {}
#puts "trace: $index $channel $var = \"$v($element)\""
set fname ${base}$index/cset0/ch${channel}-$what
if ![file writable $fname] {
puts stderr "$argv0: file \"$fname\" not writable"
return
}
puts "$v($element) > $fname"
set F [open $fname w]
puts $F $v($element)
close $F
}
# Create one window for each card
proc onewin {dir index} {
global v base labels
set t [toplevel .$index]
foreach ch "1 2 3 4" {
set hwch [expr $ch - 1]
pack [set f [frame $t.f$ch]] -side left -expand true
pack [label $f.l -text "ch $ch"]
pack [scale $f.off -orient v -from 5000 -to -5000 -resolution 10 \
-variable v($index:$hwch:offset)]
set v($index:$hwch:vref) 17
pack [radiobutton $f.100m -text "100mV" \
-variable v($index:$hwch:vref) -value 35]
pack [radiobutton $f.1v -text "1V" \
-variable v($index:$hwch:vref) -value 17]
pack [radiobutton $f.10v -text "10V" \
-variable v($index:$hwch:vref) -value 69]
pack [checkbutton $f.r -text "term" \
-variable v($index:$hwch:50ohm-term)]
pack [label $f.cur -text ""]
lappend labels $f.cur ${base}$index/cset0/chan${hwch}/current-value
if {$ch != 4} {
pack [frame $t.sep$ch -width 4 -bg black] -side left -fill y
}
}
}
set dirs [glob -nocomplain "${base}*"]
if ![llength $dirs] {
puts stderr "$argv0: No matches for \"${base}*\""
exit 1
}
proc checkval {} {
global labels
foreach {l f} $labels {
# if we rmmodded, break out of this loop
if [catch {set F [open $f]}] break
set v [read -nonewline $F]
close $F
if {$v >= 32768} {
set v [expr $v - 65536]
}
$l config -text $v
}
after 500 checkval
}
foreach dir $dirs {
regsub $base $dir "" index
onewin $dir $index
after 500 checkval
}
wm withdraw .
// SPDX-License-Identifier: GPL-3.0-or-later
/*
* Copyright 2012-2019 CERN
* Author: Federico Vaga <federico.vaga@gmail.com>
*
* This is a simple program to configure the FMC ADC trigger. It is not bug
* aware because it is only a demo program to show you how you can handle the
* trigger.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <getopt.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
static char git_version[] = "version: " GIT_VERSION;
#define buf_len 50
/* user will edit by adding the device name */
char basepath[40] = "/sys/bus/zio/devices/";
enum fau_attribute {
FAU_TRG_EN,
FAU_TRG_PRE,
FAU_TRG_PST,
FAU_TRG_RE_EN,
FAU_TRG_EXT,
FAU_SW_TRG_EN,
FAU_TRG_DLY,
FAU_TRG_THR,
FAU_TRG_CHN,
FAU_TRG_POL,
FAU_TRIG_NUM_ATTR,
};
const char *attribute[] = {
[FAU_TRG_EN] = "/cset0/trigger/enable",
[FAU_TRG_PRE] = "/cset0/trigger/pre-samples",
[FAU_TRG_PST] = "/cset0/trigger/post-samples",
[FAU_TRG_RE_EN] = "/cset0/trigger/nshots",
[FAU_TRG_EXT] = "/cset0/trigger/external",
[FAU_SW_TRG_EN] = "/cset0/trigger/sw-trg-enable",
[FAU_TRG_DLY] = "/cset0/trigger/delay",
[FAU_TRG_THR] = "/cset0/trigger/int-threshold",
[FAU_TRG_CHN] = "/cset0/trigger/int-channel",
[FAU_TRG_POL] = "/cset0/trigger/polarity",
};
/* Write a sysfs attribute */
int fau_write_attribute(enum fau_attribute attr, uint32_t val)
{
int ret, fd;
char buf[buf_len], fullpath[200];
/* convert val to string */
sprintf(buf,"%d",val);
/* build the attribute path */
strcpy(fullpath, basepath);
strcat(fullpath, attribute[attr]);
/* Write the attribute */
printf("Writing %s in %s\n", buf, fullpath);
fd = open(fullpath, O_WRONLY);
if (fd < 0)
return -ENOENT;
ret = write(fd, buf, strlen(buf));
close(fd);
return ret;
}
static void fau_help()
{
printf("\nfau-trg-config [OPTIONS] <DEVICE>\n\n");
printf(" <DEVICE>: ZIO name of the device to use\n");
printf(" --pre|-p <value>: number of pre samples\n");
printf(" --post|-P <value>: number of pre samples\n");
printf(" --nshots|-n <value>: number of trigger shots\n");
printf(" --delay|-d <value>: set the ticks delay of the trigger\n");
printf(" --threshold|-t <value>: set internal trigger threshold\n");
printf(" --channel|-c <value>: select the internal channel as "
"trigger\n");
printf(" --external: set to external trigger. The default is the "
"internal trigger.\n");
printf(" --negative-edge: set internal trigger polarity to negative "
"edge. The default\n is positive edge.\n");
printf(" --enable-sw-trg: enable the software trigger. By default is "
"disabled.\n");
printf(" --disable-hw-trg: disable the hardware trigger. By default "
"is enabled\n");
printf(" --force: force all attribute to the program default\n");
printf(" --version|-V: print version information\n");
printf(" --help|-h: show this help\n\n");
printf("NOTE: The software trigger works only if also hardware trigger "
"is enabled\n\n");
}
static void print_version(char *pname)
{
printf("%s %s\n", pname, git_version);
}
int main(int argc, char *argv[])
{
/* default attribute */
static int attrdef[FAU_TRIG_NUM_ATTR] = {1, 0, 0, 0, 0, 0, 0, 0, 0 ,0};
/* getop attribute */
static int attrval[FAU_TRIG_NUM_ATTR] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
static int force = 0;
static struct option options[] = {
{"pre",required_argument, 0, 'p'},
{"post",required_argument, 0, 'P'},
{"nshots",required_argument, 0, 'n'},
{"delay",required_argument, 0, 'd'},
{"threshold",required_argument, 0, 't'},
{"negative-edge",no_argument, &attrval[FAU_TRG_POL], 1},
{"channel",required_argument, 0, 'c'},
{"external", no_argument, &attrval[FAU_TRG_EXT], 1},
{"enable-sw-trg", no_argument, &attrval[FAU_SW_TRG_EN], 1},
{"disable-hw-trg", no_argument, &attrval[FAU_TRG_EN], 0},
{"force", no_argument, &force, 1},
{"version",no_argument, 0, 'V'},
{"help",no_argument, 0, 'h'},
{0, 0, 0, 0}
};
int i, opt_index = 0, err = 0, cur_val;
char c;
if (argc == 1) {
fau_help();
exit(1);
}
while( (c = getopt_long(argc, argv, "p:P:n:d:t:c:Vh",
options, &opt_index)) >=0 ){
switch(c){
case 'p':
attrval[FAU_TRG_PRE] = atoi(optarg);
break;
case 'P':
attrval[FAU_TRG_PST] = atoi(optarg);
break;
case 'n':
attrval[FAU_TRG_RE_EN] = atoi(optarg);
break;
case 'd':
attrval[FAU_TRG_DLY] = atoi(optarg);
break;
case 't':
attrval[FAU_TRG_THR] = atoi(optarg);
break;
case 'c':
attrval[FAU_TRG_CHN] = atoi(optarg);
break;
case 'V':
print_version(argv[0]);
exit(1);
case 'h':
fau_help();
exit(1);
break;
}
}
if (optind != argc - 1 ) {
fprintf(stderr, "%s: DEVICE-ID is a mandatory argument\n",
argv[0]);
fau_help();
exit(1);
}
strcat(basepath, argv[optind]);
printf("Sysfs path to device is: %s\n", basepath);
for (i = 0; i < FAU_TRIG_NUM_ATTR; ++i) {
cur_val = attrval[i];
if (cur_val == -1) {
if (force)
cur_val = attrdef[i];
else
continue; /* skip unsetted attribute */
}
err = fau_write_attribute(i, cur_val);
if (err) {
fprintf(stderr, "%s: error %d for attribute %d\n",
argv[0], err, i);
exit(1);
}
}
exit(0);
}
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* a simple output tool to make a burst on a parallel port
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <glob.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
static char git_version[] = "version: " GIT_VERSION;
/* Returns the numer of microsecond timer ticks (Tomasz Wlostowski) */
static int64_t get_tics()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (int64_t)tv.tv_sec * 1000 * 1000 + tv.tv_usec;
}
/* Microsecond-accurate delay-to */
static void delay_to(int64_t until)
{
while (get_tics() < until)
;
}
static void print_version(char *pname)
{
printf("%s %s\n", pname, git_version);
}
int main(int argc, char **argv)
{
int fd, addr, count, usec, ret;
int64_t tics;
if ((argc == 2) &&
(!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version"))) {
print_version(argv[0]);
exit(0);
}
if (argc != 4) {
fprintf(stderr,
"%s: Use \"%s <hexaddr> <count> <period-usec>\"\n",
argv[0], argv[0]);
exit(1);
}
if (sscanf(argv[1], "%x", &addr) != 1) {
fprintf(stderr, "%s: wrong hex \"%s\"\n", argv[0], argv[1]);
exit(1);
}
if (sscanf(argv[2], "%i", &count) != 1) {
fprintf(stderr, "%s: wrong count \"%s\"\n", argv[0], argv[2]);
exit(1);
}
if (sscanf(argv[3], "%i", &usec) != 1) {
fprintf(stderr, "%s: wrong period \"%s\"\n", argv[0], argv[3]);
exit(1);
}
fprintf(stderr, "%s: using port 0x%x, %i pulses, period %i us\n",
argv[0], addr, count, usec);
fd = open("/dev/port", O_RDWR);
if (fd < 0) {
fprintf(stderr, "%s: /dev/port: %s\n", argv[0],
strerror(errno));
exit(1);
}
tics = get_tics();
do {
char b[]={0x00, 0xff};
lseek(fd, addr, SEEK_SET);
ret = write(fd, b + 1, 1);
lseek(fd, addr, SEEK_SET);
ret = write(fd, b + 0, 1);
if (count > 1) {
tics += usec;
delay_to(tics);
}
} while (--count);
return ret;
}
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