Commit d8799c6d authored by Cesar Prados's avatar Cesar Prados

drv: remove the CERN submodule and add the vmebridge code

the submodule is hosted in CERN repo and as long as I could see
people without  CERN account are having problems to check it out.
parent 64d9fb0c
[submodule "drv"]
path = drv
url = git@github.com:GSI-CS-CO/kernel_modules.git
Subproject commit ce3cf1da1b98175b04c3e685b155fc153926790f
DIRS = \
driver \
.PHONY: all clean cleanall install $(DIRS)
all clean cleanall install: $(DIRS)
clean: TARGET = clean
cleanall: TARGET = cleanall
install: TARGET = install
$(DIRS):
$(MAKE) -C $@ $(TARGET)
* Make sure all windows mapping are removed at module remove time
do it in vme_window_remove. - OK
* Add a vme_release_mapping() function - OK
* Check with Julian if we want a strict CES driver emulation - OK
* Fix mapping creation desc pci address - OK
* Fix module unload BUG - OK
* At module unload: remove all the mappings - OK
* At file close remove all mappings for the task - OK
* Round up VME address and size to a 64K boundary at window creation - OK
* Implement mmap - OK
* Add CES vme_intset() and vme_intclr() functions - OK
* Write CES emulation user library - OK in vmelib
* Should that lib provides vme_intset() and vme_intclr()? - NO OK
* Write misc library (bus error check, byte swapping, ...) - OK in vmelib
* Implement DMA handling in driver - no need for driver API - OK
* Implement DMA support userspace library - OK
* In vme_dma_exit() try an abort after a wait_event_interruptible_timeout() - OK
00:00.0 Host bridge: Intel Corporation Mobile 945GME Express Memory Controller Hub (rev 03)
00:02.0 VGA compatible controller: Intel Corporation Mobile 945GME Express Integrated Graphics Controller (rev 03)
00:1c.0 PCI bridge: Intel Corporation 82801G (ICH7 Family) PCI Express Port 1 (rev 02)
00:1c.1 PCI bridge: Intel Corporation 82801G (ICH7 Family) PCI Express Port 2 (rev 02)
00:1c.4 PCI bridge: Intel Corporation 82801GR/GH/GHM (ICH7 Family) PCI Express Port 5 (rev 02)
00:1c.5 PCI bridge: Intel Corporation 82801GR/GH/GHM (ICH7 Family) PCI Express Port 6 (rev 02)
00:1d.0 USB Controller: Intel Corporation 82801G (ICH7 Family) USB UHCI Controller #1 (rev 02)
00:1d.7 USB Controller: Intel Corporation 82801G (ICH7 Family) USB2 EHCI Controller (rev 02)
00:1e.0 PCI bridge: Intel Corporation 82801 Mobile PCI Bridge (rev e2)
00:1f.0 ISA bridge: Intel Corporation 82801GHM (ICH7-M DH) LPC Interface Bridge (rev 02)
00:1f.1 IDE interface: Intel Corporation 82801G (ICH7 Family) IDE Controller (rev 02)
00:1f.3 SMBus: Intel Corporation 82801G (ICH7 Family) SMBus Controller (rev 02)
01:00.0 PCI bridge: Tundra Semiconductor Corp. Unknown device 8114 (rev 02)
02:07.0 Network controller: VMIC Unknown device 5565 (rev 01)
03:00.0 PCI bridge: Tundra Semiconductor Corp. Unknown device 8114 (rev 02)
04:0c.0 Bridge: Tundra Semiconductor Corp. Tsi148 [Tempe] (rev 01)
05:00.0 Serial controller: Oxford Semiconductor Ltd Unknown device c120
06:00.0 Ethernet controller: Intel Corporation 82573L Gigabit Ethernet Controller
-[00]-+-00.0 Host Bridge
|
+-02.0 VGA controller
|
+-1c.0-+ PCI Bridge PCIe port 1
| |
| +- [01-02]-+ Bus 1 - subordinate 2
| |
| +- 00.0-+ PCI bridge (TSI 384)
| |
| +- [02]-+ Bus 2
| |
| +- 07.0 VMIC Network controller
|
+-1c.1-+ PCI Bridge PCIe port 2
| |
| +- [03-04]-+ Bus 3 - subordinate 4
| |
| +- 00.0-+ PCI bridge (TSI 384)
| |
| +- [04]-+ Bus 4
| |
| +- 0c.0 VME bridge (TSI148)
|
+-1c.4-+ PCI Bridge PCIe port 5
| |
| +- [05]-+ Bus 5
| |
| +- 00.0 Serial controller (Oxford)
|
+-1c.5-+ PCI Bridge PCIe port 6
| |
| +- [06]-+ Bus 6
| |
| +- 00.0 Ethernet controller (82573L)
|
+-1d.0 USB Controller
|
+-1d.7 USB Controller
|
+-1e.0-+ PCI Bridge
| |
| +- [07]- Bus 7
|
+-1f.0 ISA bridge
|
+-1f.1 IDE interface
|
+-1f.3 SMBus
TSI148 PCI-VME bridge driver
----------------------------
Written by Sébastien Dugué - 23 February 2009
Table of Contents
-----------------
1. Introduction
2. Driver files layout
3. PCI-VME bridge driver initialization
4. VME address space mapping
4.1 VME mapping data structure
4.2 Driver API
4.3 Legacy CES driver API
4.4 User space ioctls API
5. DMA
5.1 DMA transfer data structure
5.2 Driver API
5.3 User space ioctls API
6. Interrupts
6.1 Driver API
6.2 Legacy CES driver API
7. VME bridge procfs tree
8. User space library
1. Introduction
------------
The TSI148 is a PCI to VME bridge device which allows the host CPU to access
the VME bus. Those accesses may be achieved either through the mapping of the
VME address space onto the PCI address space or via a 2-channel DMA controller
embedded into the TSI148. The chip also allows forwarding of VME interrupts
to the host CPU.
2. Driver files layout
-------------------
The driver sources are split across several files by main functionality.
- vme_bridge.c: provides PCI device support for the TSI148 chip. It handles
device registration with the PCI subsystem, driver initialization,
interrupt registration and basic front end file operations.
- vme_bridge.h: provides the data structures and definitions internal to the
driver.
- vme_window.c: provides high-level support for PCI to VME address space
mappings.
- vme_irq.c: provides high-level support for VME interrupt registration and
management.
- vme_dma.c: provides high-level DMA support for transfers with the VME bus.
- vme_dma.h: provides the DMA data structures used internally by the driver.
- vme_irq.c: provides high-level support for VME interrupt management.
- vme_misc.c: provides support for miscellaneous functionality of the device,
such as bus error checking.
- tsi148.c: provides low-level support for the TSI148 chip.
- tsi148.h: provides the low-level data structures and definitions for the
TSI148 chip.
- vmebus.h: provides the API for using the VME bridge driver, both for other
drivers and for user space applications. This API encompasses public
data structures, definitions and function prototypes.
3. PCI-VME bridge driver initialization
------------------------------------
The driver can be built as a modular driver, therefore it's entry points
vme_bridge_init_module() and vme_bridge_exit_module() are registered using the
standard module_init() and module_exit() macros. Those functions are then
called at insmod and rmmod time respectively
The TSI148 is a PCI device and as such is initialized as any standard PCI
device is. Therefore, all the vme_bridge_init_module() function does is to
register our struct pci_driver vme_bridge_driver with the PCI layer. This
structure holds the PCI IDs table for the devices (in our case only one) the
driver handles and the initialization (vme_bridge_init()) and exit
(vme_bridge_remove()) functions for the device.
The vme_bridge_init() initialization function is then automatically called
by the kernel for every device that match one of the IDs in the PCI IDs table.
vme_bridge_init() takes the following steps to initialize the device and
the driver data structures:
- Enable the device using pci_enable_device() before it can be used
by the driver.
- Enable PCI bus mastering for the device (It should be enabled by the BIOS,
but who knows, better to force it).
- Allocate our struct vme_bridge_device data structure that holds information
internal to the driver.
- Get the chip revision and print a message to the console indicating we've
found a TSI148 device.
- Check there is enough PCI address space opened in the parent bridge. If
the size is less than 32 MB (0x2000000) the function exits in error.
This value is quite arbitrary at the moment and will have to be tuned to
a more realistic value.
For example on the MEN A20 board, the total size allocated to the bus
the TSI148 resides on is 1 GB (and the TSI148 is, by design, the only
device on that bus).
- Map the TSI148 registers space into the kernel address space using
vme_bridge_map() which in turn calls pci_iomap() to map PCI BAR 0 for a size
of 4096.
- Check the device can do 32-bit DMA. If not, DMA will be disabled.
- Reset the TSI148 chip with tsi148_quiesce().
- Initialize window management with vme_window_init().
- Initialize DMA management with vme_dma_init() if we can do DMA.
- Initialize the TSI148 chip with tsi148_init()
- Register and create the character devices that will be used to access the
bridge functionality with vme_bridge_create_devices().
- Create and populate the driver procfs tree with vme_procfs_register().
- Create a PCI-VME window for mapping the TSI148 chip registers onto the VME
bus address space with vme_bridge_map_crg(). This is mandatory for proper
interrupt processing as explained below in section 6. Interrupts.
The physical window used is window number 7 (i.e. the last one). In order
to avoid wasting a physical window for only the 4K TSI148 registers address
range, the whole A24/D32 VME address space is mapped using that window. A
mapping is then created at the top of this address space to hold the TSI148
registers (at VME address 0x00fff000 for a size of 4K).
- Finally, initialize and enable the device interrupts.
4. VME address space mapping
-------------------------
The TSI148 chip allows to map different VME address space onto the PCI address
space by way of windows. However, the number of windows that can be mapped at
any time is limited to 8 (one of those windows is actually reserved for internal
driver use - see section 6. Interrupts).
Therefore an abstraction has been made on top of those physical windows which
is called a logical mapping. A mapping is in fact a sub-window, that maps only
a part of the VME address space represented by a physical window, with the same
address modifier and data bus width.
For example, if a physical window is used to map the entire A24/D32 VME
address space, then any driver or application that wants to map a sub-range of
that address space for a board will then need to create a mapping (sub-window)
on top of that physical window.
That way, it is possible, at system initialization time, to create the
physical windows needed, say to map the entire A16/D16, A24/D16, ... address
spaces and then when drivers or applications come up they will only need to
create mappings on top of those windows.
For the sake of simplicity, the data structure used to describe a physical
window or logical mapping is the same 'struct vme_mapping'.
Physical windows are created and destroyed with vme_create_window() and
vme_destroy_window() respectively.
NOTE: Due to constraints of the TSI148 chip, physical windows must have
their VME address and size aligned to a 64K boundary. Therefore the VME
address is automatically rounded down to the nearest 64K boundary and
the size is automatically rounded up to hold the requested address
space range. The aligned fields are returned in the passed descriptor.
Logical mappings are created and destroyed with vme_find_mapping() and
vme_release_mapping() respectively. What vme_find_mapping() does is it tries to
find a physical window that can be used to hold that logical mapping. If none
is found and if the force argument is set, then a new window is created to hold
that mapping. In the same way, calling vme_release_mapping() with its force
argument set will also destroy the physical window if there are no more users.
4.1 VME mapping data structure
--------------------------
As already mentioned, both physical windows and logical mappings are
described by a single data structure, namely a struct vme_mapping descriptor.
This data structure is declared in vmebus.h and has the following fields:
- window_num: physical window number. This field only has meaning when
creating or destroying physical windows. It is also set to the
underlying physical window number when a logical mapping is created.
- kernel_va: kernel virtual address of the mapping for use by drivers only.
- user_va: user virtual address of the mapping for use by applications only.
- fd: user file descriptor of the VME mapping device node.
- window_enabled: internal state of the physical window.
- data_width: VME data bus width of the mapping. Possible values are in the
vme_data_width enumeration.
- am: VME address modifier of the mapping. Possible values are in the
vme_address_modifier enumeration.
- read_prefetch_enabled: PCI read prefetch enabled state.
- read_prefetch_size: PCI read prefetch size (in cache lines). Possible
values are in the vme_read_prefetch_size enumeration.
- v2esst_mode: VME 2eSST transfer speed. Possible values are in the
vme_2esst_mode enumeration.
- bcast_select: VME 2eSST broadcast select.
- pci_addru: upper 32 bits of the mapping PCI bus start address. Must be 0.
- pci_addrl: lower 32 bits of the mapping PCI bus start address
- sizeu: upper 32 bits of the mapping size. Must be 0
- sizel: lower 32 bits of the mapping size.
- vme_addru: upper 32 bits of the mapping VME bus start address. Must be 0.
- vme_addrl: lower 32 bits of the mapping VME bus start address.
Typically, to create a logical mapping, one has to set the data_width, am,
VME address and size in the descriptor. The virtual address at which the VME
address space is mapped is then returned in kernel_va for drivers or user_va
for user space applications.
4.2 Driver API
----------
Drivers stacked on top of the VME bridge driver may directly use the
following functions which are made public and exported through vmebus.h.
- int vme_get_window_attr(struct vme_mapping *desc)
Get a physical window attributes. Only the window number in the struct
vme_mapping descriptor is used.
Return 0 on success, or -EINVAL if the window number is out of bounds.
- int vme_create_window(struct vme_mapping *desc)
Create and map a PCI-VME window according to the specified descriptor.
Return 0 on success, or a standard kernel error code on failure.
- int vme_destroy_window(struct vme_mapping *desc)
Unmap and remove the PCI-VME window specified by the descriptor. Only the
window number in the descriptor is used. Destroying a window also forcibly
remove all the mappings made on top of that window.
- int vme_find_mapping(struct vme_mapping *desc, int force)
Try to find a physical window that can hold the address space specified by
the descriptor. If a window is found, then its number is returned in the
passed descriptor. If no physical window match and the force flag is set,
then a new window is created to hold that mapping.
- int vme_release_mapping(struct vme_mapping *desc, int force)
Release a VME mapping made with vme_find_mapping(). If the mapping is the
last one on that physical window and the force flag is set then the
window is also destroyed.
- int vme_bus_error_check(int clear)
Check whether a bus error occurred and clear the error if clear is set.
Returns 1 if a bus error occurred else 0.
4.3 Legacy CES driver API
---------------------
For compatibility with drivers using the CES driver API, the following
functions are provided. Those are built on top of the previously described
functions.
- unsigned long find_controller(unsigned long vmeaddr, unsigned long len,
unsigned long am, unsigned long offset,
unsigned long size,
struct pdparam_master *param)
This function is an emulation of the CES driver functionality on Lynx-OS.
The CES function interface does not give all the needed VME parameters,
so the following choices were made and may have to be tweaked:
- if read prefetch is enabled the the prefetch size is set to 2 cache
lines
- the VME address and size are automatically aligned on 64k if needed
- the VME address is limited to 32 bits
- the size argument is not used anymore for read testing, but is used
to select the data bus width of the mapping and is expressed in bytes.
So, to setup a D16 mapping, size should be set to 2 and for a D32
mapping, it should be set to 4.
The kernel allocated mapping descriptor address (cookie) is stored in the
pdparam_master sgmin field for use by return_controller().
Returns the virtual address of the mapping, or a standard kernel error
code.
- unsigned long return_controller(unsigned logaddr, unsigned len)
This function is an emulation of the CES driver functionality on
Lynx-OS.
Returns 0 on success, or a standard kernel error code.
4.4 User space ioctls API
--------------------
All the functionality provided to drivers is also provided to user space
applications via ioctls (or via a library that makes use of those ioctls, see
section 8. User space library). Most of these ioctls only make calls to the
functions described in section 4.2 Driver API.
The device node used for those ioctls is /dev/vme_mwindow
- VME_IOCTL_GET_WINDOW_ATTR
Calls vme_get_window_attr(), the ioctl argument is a struct vme_mapping
descriptor.
- VME_IOCTL_CREATE_WINDOW
Calls vme_create_window(), the ioctl argument is a struct vme_mapping
descriptor.
- VME_IOCTL_DESTROY_WINDOW
Calls vme_destroy_window(), the ioctl argument is a struct vme_mapping
descriptor.
- VME_IOCTL_FIND_MAPPING
Calls vme_find_mapping(), the ioctl argument is a struct vme_mapping
descriptor. The force argument used by vme_find_mapping() can be set via the
VME_IOCTL_SET_CREATE_ON_FIND_FAIL ioctl (see below)
- VME_IOCTL_RELEASE_MAPPING
Calls vme_release_mapping(), the ioctl argument is a struct vme_mapping
descriptor. The force argument used by vme_release_mapping() can be set via
the VME_IOCTL_SET_DESTROY_ON_REMOVE ioctl (see below)
- VME_IOCTL_GET_CREATE_ON_FIND_FAIL
Set or clear the vme_create_on_find_fail flag indicating whether a new
window should be created if none can hold the mapping. The ioctl argument
is an int.
- VME_IOCTL_SET_CREATE_ON_FIND_FAIL
Get the vme_create_on_find_fail flag.
- VME_IOCTL_GET_DESTROY_ON_REMOVE
Set or clear the vme_destroy_on_remove flag indicating whether removing the
last mapping on a window should also destroys the window. The ioctl argument
is an int.
- VME_IOCTL_SET_DESTROY_ON_REMOVE
Get the vme_destroy_on_remove flag.
- VME_IOCTL_GET_BUS_ERROR
Calls vme_bus_error_check() to get bus error status.
5. DMA
---
The TSI148 chip provides 2 DMA channels to perform transfers between a VME
address space and the host memory.
The data structure that describes a DMA request is a struct vme_dma
descriptor.
The function that setups and starts the DMA transfer is vme_do_dma() which
does the following:
- it first tries to find an available (unused) channel to fulfill the
request (if no channel is available, it returns -EBUSY).
- it then setups the DMA transfer. A scatter gather list of the DMA buffer
pages is created, those pages are then mapped onto the PIC bus address
space and the TSI148 is programmed.
- finally, the DMA transfer is started and the caller is put to sleep
awaiting transfer completion signaled with an interrupt.
5.1 DMA transfer data structure
---------------------------
The data structure used to specify DMA transfers is the struct vme_dma
descriptor, declared in vmebus.h, which has the following fields:
- status: transfer status
- length: transfer length in bytes
- novmeinc: must be set to 1 when accessing a FIFO like device on the VME
bus, else must be 0.
- dir: transfer direction, either VME_DMA_TO_DEVICE or VME_DMA_FROM_DEVICE.
- src: transfer source attributes. This is a struct vme_dma_attr described
below.
- dst: transfer destination attributes. This is a struct vme_dma_attr
described below.
- ctrl: transfer control. This is a struct vme_dma_ctrl described below.
The struct vme_dma_attr is used for describing the attributes of a DMA
endpoint. All the field excepted for the address are only relevant for an
endpoint on the VME bus.
This data structure has the following fields:
- data_width: VME data bus width
- am: VME address modifier
- v2esst_mode: VME 2eSST transfer speed
- bcast_select: VME 2eSST broadcast select
- addru: upper 32 bits of the address. Must be 0.
- addrl: lower 32 bits of the address.
The struct vme_dma_ctrl defines the following fields:
- vme_block_size: VME bus block size when the source is VME. This field has
its values in the vme_dma_block_size enumeration.
- vme_backoff_time: VME bus backoff time when the source is VME. This field
has its values in the vme_dma_backoff enumeration.
- pci_block_size: PCI/X bus block size when the source is PCI. This field
has its values in the vme_dma_block_size enumeration.
- pci_backoff_time: PCI bus backoff time when the source is PCI. This field
has its values in the vme_dma_backoff enumeration.
5.2 Driver API
----------
Drivers stacked on top of the VME bridge driver may directly use the
following function which is made public and exported through vmebus.h.
- int vme_do_dma(struct vme_dma *desc)
This function first checks the validity of the user supplied DMA transfer
parameters. It then tries to find an available DMA channel to do the
transfer, setups that channel and starts the DMA.
Returns 0 on success, or a standard kernel error code on failure. The
completion status of the DMA transfer is returned in desc->status.
5.3 User space ioctls API
--------------------
User space applications can do DMA transfers through the use of an ioctl (or
via a library call, see section 8. User space library).
The device node used for the DMA ioctl is /dev/vme_dma
- VME_IOCTL_START_DMA
Calls vme_do_dma(), the ioctl argument is a struct vme_dma descriptor.
6. Interrupts
----------
The TSI148 allows VME interrupts to be forwarded onto the PCI bus to the host
CPU and IACK cycles to be generated on the VME bus. Furthermore, the TSI148 can
also interrupt the HOST CPU following internal events such as DMA completion.
NOTE: As already mentioned, due to some TSI148 quirks, the interrupt status
and interrupt clear registers need to be accessed through the VME bus.
There seems to be some internal state that must be flushed before we
can access those registers, and the only way so far is to perform a VME
cycle.
6.1 Driver API
----------
The VME bridge driver allows drivers to register handlers for VME interrupts
using the following API:
- int vme_request_irq(unsigned int vec, int (*handler)(void *),
void *arg, const char *name)
Register an interrupt handler for the given VME IRQ vector. When the
handler is called, it is passed the argument arg. The name argument is the
interrupt name and is informative only, it is used for statistics in
procfs.
NOTE: The return value of the handler is not used by the driver, this is
for compatibility with the CES driver API.
Returns 0 on success or a standard kernel error code.
- int vme_free_irq(unsigned int vec)
Unregister an interrupt handler for the given VME IRQ vector.
Returns 0 on success or a standard kernel error code.
- int vme_generate_interrupt(int level, int vector, signed long msecs)
This function generates a VME interrupt with the given level and vector
and waits for IACK for msecs milliseconds.
Returns 0 on success or a standard kernel error code (-ETIME if it timed
out).
6.2 Legacy CES driver API
---------------------
For compatibility with legacy drivers using the CES driver API, the
following functions are provided. Those are built on top of the previously
described functions.
- int vme_intset(int vec, int (*handler)(void *), void *arg, void *sav)
This function is an emulation of the CES driver functionality on Lynx-OS.
The sav argument is unused.
NOTE: The return value of the handler is not used by the driver.
Returns 0 on success, or a standard kernel error.
- int vme_intclr(int vec, void *sav)
This function is an emulation of the CES driver functionality on Lynx-OS.
Returns 0 on success, or a standard kernel error.
7. VME bridge procfs tree
----------------------
The VME bridge driver provides run-time information through pseudo files in
the procfs directory tree. The VME bridge procfs tree is rooted at /proc/vme
and provides the following files:
- /proc/vme/info
Provides misc information regarding the TSI148 chip, such as the chip
revision or the IRQ it's hooked on.
- /proc/vme/windows
Shows the VME mappings currently active, both for physical windows and for
logical mappings made on top of those.
- /proc/vme/interrupts
Shows the TSI148 interrupt sources counters.
- /proc/vme/irq
Shows the VME IRQ counters.
- /proc/vme/tsi148/pcfs
Dumps the TSI148 PCI/X Configuration Space Registers (PCFS)
- /proc/vme/tsi148/lcsr
Dumps the TSI148 Local Control and Status Registers (LCSR)
- /proc/vme/tsi148/gcsr
Dumps the TSI148 Global Control and Status Registers (GCSR)
- /proc/vme/tsi148/crcsr
Dumps the TSI148 Control and Status Registers (CSR)
8. User space library
-----------------
In order to make life easier for user space applications, the libvme library
is provided that wraps ioctls calls to the driver.
The library provides the following functions with exactly the same semantic as
described in 4.2 and 5.2:
- void *vme_map(struct vme_mapping *desc, int force)
Maps a VME address space. This is the user space equivalent to
vme_find_mapping()
Returns a user space virtual address for the mapping or NULL on error (in
that case errno is set appropriately).
- int vme_unmap(struct vme_mapping *desc, int force)
Unmap a VME address space. This is the user space equivalent to
vme_release_mapping().
Returns 0 on success or -1 on error (in that case errno is set
appropriately).
- int vme_dma_read(struct vme_dma *desc)
Perform a DMA read transfer on the VME bus with the parameters specified in
the descriptor.
Returns 0 on success or -1 on error (in that case errno is set
appropriately).
- int vme_dma_write(struct vme_dma *desc)
Perform a DMA write transfer on the VME bus with the parameters specified in
the descriptor.
Returns 0 on success or -1 on error (in that case errno is set
appropriately).
- int vme_bus_error_check(struct vme_mapping *desc)
Check for VME a bus error.
Returns 0 if no bus error occurred, 1 if bus error occurred or -1 on any
other error (with errno set appropriately).
For compatibility with legacy applications using the CES library, the
following functions are also provided:
- unsigned long find_controller(unsigned long vmeaddr, unsigned long len,
unsigned long am, unsigned long offset,
unsigned long size,
struct pdparam_master *param)
Map a VME address space into the user address space.
See section 4.3 Legacy CES driver API, for differences with the original CES
API.
Returns the virtual address of the mapping on success, or 0 if an error
occurs (in that case errno is set appropriately).
- unsigned long return_controller(struct vme_mapping *desc)
Release a VME mapping.
WARNING: The function interface has been changed to a single parameter,
a struct vme_mapping descriptor as the CES original parameters
are not enough to match a VME mapping.
Returns 0 on success, or -1 if an error occurs (in that case errno is set
appropriately).
ccflags-y += -g -Wall
ccflags-y += -DGIT_VERSION=\"$(GIT_VERSION)\"
obj-m += vmebus.o
vmebus-objs += tsi148.o
vmebus-objs += vme_bridge.o
vmebus-objs += vme_cesif.o
vmebus-objs += vme_dma.o
vmebus-objs += vme_irq.o
vmebus-objs += vme_misc.o
vmebus-objs += vme_window.o
CPU?=x86_64
all: driver
driver: modules
modules:
$(MAKE) -C $(KERNELSRC) M=`pwd` CPU=$(CPU) GIT_VERSION=$(GIT_VERSION) modules
clean:
$(RM) -r $(CPU)
$(MAKE) -C $(KERNELSRC) M=`pwd` clean
cleanall: clean
$(RM) -r $(ALL_CPUS)
/*
* tsi148.c - Low level support for the Tundra TSI148 PCI-VME Bridge Chip
*
* Copyright (c) 2009 Sebastien Dugue
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <asm/byteorder.h>
#include "vme_bridge.h"
#include "tsi148.h"
#undef DEBUG_DMA
static struct tsi148_chip *chip;
static struct dma_pool *dma_desc_pool;
static inline void reg_join(u32 high, u32 low, u64 *out)
{
*out = (unsigned long long)high << 32;
*out |= low;
}
/*
* Procfs support
*/
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *tsi148_proc_root;
/**
* tsi148_proc_pcfs_show() - Dump the PCFS register group
*
*/
static int tsi148_proc_pcfs_show(struct seq_file *m, void *data)
{
unsigned int tmp;
seq_printf(m, "\nPCFS Register Group:\n");
tmp = ioread32(&chip->pcfs.veni);
seq_printf(m, "\tpciveni 0x%04x\n", tmp & 0xffff);
seq_printf(m, "\tpcidevi 0x%04x\n", (tmp>>16) & 0xffff);
tmp = ioread32(&chip->pcfs.cmmd);
seq_printf(m, "\tpcicmd 0x%04x\n", tmp & 0xffff);
seq_printf(m, "\tpcistat 0x%04x\n", (tmp>>16) & 0xffff);
tmp = ioread32(&chip->pcfs.rev_class);
seq_printf(m, "\tpcirev 0x%02x\n", tmp & 0xff);
seq_printf(m, "\tpciclass 0x%06x\n", (tmp >> 8) & 0xffffff);
tmp = ioread32(&chip->pcfs.clsz);
seq_printf(m, "\tpciclsz 0x%02x\n", tmp & 0xff);
seq_printf(m, "\tpcimlat 0x%02x\n", (tmp>>8) & 0xff);
tmp = ioread32(&chip->pcfs.mbarl);
seq_printf(m, "\tpcimbarl 0x%08x\n", tmp);
tmp = ioread32(&chip->pcfs.mbaru);
seq_printf(m, "\tpcimbaru 0x%08x\n", tmp);
tmp = ioread32(&chip->pcfs.subv);
seq_printf(m, "\tpcisubv 0x%04x\n", tmp & 0xffff);
seq_printf(m, "\tpcisubi 0x%04x\n", (tmp>>16) & 0xffff);
tmp = ioread32(&chip->pcfs.intl);
seq_printf(m, "\tpciintl 0x%02x\n", tmp & 0xff);
seq_printf(m, "\tpciintp 0x%02x\n", (tmp>>8) & 0xff);
seq_printf(m, "\tpcimngn 0x%02x\n", (tmp>>16) & 0xff);
seq_printf(m, "\tpcimxla 0x%02x\n", (tmp>>24) & 0xff);
tmp = ioread32(&chip->pcfs.pcix_cap_id);
seq_printf(m, "\tpcixcap 0x%02x\n", tmp & 0xff);
tmp = ioread32(&chip->pcfs.pcix_status);
seq_printf(m, "\tpcixstat 0x%08x\n", tmp);
return 0;
}
static int tsi148_proc_pcfs_open(struct inode *inode, struct file *file)
{
return single_open(file, tsi148_proc_pcfs_show, NULL);
}
static const struct file_operations tsi148_proc_pcfs_ops = {
.open = tsi148_proc_pcfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/**
* tsi148_proc_lcsr_show() - Dump the LCSR register group
*
*/
static int tsi148_proc_lcsr_show(struct seq_file *m, void *data)
{
int i;
seq_printf(m, "\n");
/* Display outbound decoders */
seq_printf(m, "Local Control and Status Register Group (LCSR):\n");
seq_printf(m, "\nOutbound Translations:\n");
seq_printf(m, "No. otat otsau:otsal oteau:oteal"
" otofu:otofl\n");
for (i = 0; i < 8; i++) {
unsigned int otat, otsau, otsal, oteau, oteal, otofu, otofl;
otat = ioread32be(&chip->lcsr.otrans[i].otat);
otsau = ioread32be(&chip->lcsr.otrans[i].otsau);
otsal = ioread32be(&chip->lcsr.otrans[i].otsal);
oteau = ioread32be(&chip->lcsr.otrans[i].oteau);
oteal = ioread32be(&chip->lcsr.otrans[i].oteal);
otofu = ioread32be(&chip->lcsr.otrans[i].otofu);
otofl = ioread32be(&chip->lcsr.otrans[i].otofl);
seq_printf(m, "%d: %08X %08X:%08X %08X:%08X: %08X:%08X\n",
i, otat, otsau, otsal, oteau, oteal, otofu, otofl);
}
/* Display inbound decoders */
seq_printf(m, "\nInbound Translations:\n");
seq_printf(m, "No. itat itsau:itsal iteau:iteal"
" itofu:itofl\n");
for (i = 0; i < 8; i++) {
unsigned int itat, itsau, itsal, iteau, iteal, itofu, itofl;
itat = ioread32be(&chip->lcsr.itrans[i].itat);
itsau = ioread32be(&chip->lcsr.itrans[i].itsau);
itsal = ioread32be(&chip->lcsr.itrans[i].itsal);
iteau = ioread32be(&chip->lcsr.itrans[i].iteau);
iteal = ioread32be(&chip->lcsr.itrans[i].iteal);
itofu = ioread32be(&chip->lcsr.itrans[i].itofu);
itofl = ioread32be(&chip->lcsr.itrans[i].itofl);
seq_printf(m, "%d: %08X %08X:%08X %08X:%08X: %08X:%08X\n",
i, itat, itsau, itsal, iteau, iteal, itofu, itofl);
}
seq_printf(m, "\nVME Bus Control:\n");
seq_printf(m, "\tvmctrl 0x%08x\n", ioread32be(&chip->lcsr.vmctrl));
seq_printf(m, "\tvctrl 0x%08x\n", ioread32be(&chip->lcsr.vctrl));
seq_printf(m, "\tvstat 0x%08x\n", ioread32be(&chip->lcsr.vstat));
seq_printf(m, "PCI Status:\n");
seq_printf(m, "\tpcsr 0x%08x\n", ioread32be(&chip->lcsr.pstat));
seq_printf(m, "VME Exception Status:\n");
seq_printf(m, "\tveau:veal 0x%08x:%08x\n",
ioread32be(&chip->lcsr.veau),
ioread32be(&chip->lcsr.veal));
seq_printf(m, "\tveat 0x%08x\n", ioread32be(&chip->lcsr.veat));
seq_printf(m, "PCI Error Status:\n");
seq_printf(m, "\tedpau:edpal 0x%08x:%08x\n",
ioread32be(&chip->lcsr.edpau),
ioread32be(&chip->lcsr.edpal));
seq_printf(m, "\tedpxa 0x%08x\n", ioread32be(&chip->lcsr.edpxa));
seq_printf(m, "\tedpxs 0x%08x\n", ioread32be(&chip->lcsr.edpxs));
seq_printf(m, "\tedpat 0x%08x\n", ioread32be(&chip->lcsr.edpat));
seq_printf(m, "Inbound Translation Location Monitor:\n");
seq_printf(m, "\tlmbau:lmbal 0x%08x:%08x:\n",
ioread32be(&chip->lcsr.lmbau),
ioread32be(&chip->lcsr.lmbal));
seq_printf(m, "\tlmat 0x%08x\n", ioread32be(&chip->lcsr.lmat));
seq_printf(m, "Local bus Interrupt Control:\n");
seq_printf(m, "\tinten 0x%08x\n", ioread32be(&chip->lcsr.inten));
seq_printf(m, "\tinteo 0x%08x\n", ioread32be(&chip->lcsr.inteo));
seq_printf(m, "\tints 0x%08x\n", ioread32be(&chip->lcsr.ints));
seq_printf(m, "\tintc 0x%08x\n", ioread32be(&chip->lcsr.intc));
seq_printf(m, "\tintm1 0x%08x\n", ioread32be(&chip->lcsr.intm1));
seq_printf(m, "\tintm2 0x%08x\n", ioread32be(&chip->lcsr.intm2));
seq_printf(m, "VMEbus Interrupt Control:\n");
seq_printf(m, "\tbcu64 0x%08x\n", ioread32be(&chip->lcsr.bcu));
seq_printf(m, "\tbcl64 0x%08x\n", ioread32be(&chip->lcsr.bcl));
seq_printf(m, "\tbpgtr 0x%08x\n", ioread32be(&chip->lcsr.bpgtr));
seq_printf(m, "\tbpctr 0x%08x\n", ioread32be(&chip->lcsr.bpctr));
seq_printf(m, "\tvicr 0x%08x\n", ioread32be(&chip->lcsr.vicr));
seq_printf(m, "RMW Register Group:\n");
seq_printf(m, "\trmwau 0x%08x\n", ioread32be(&chip->lcsr.rmwau));
seq_printf(m, "\trmwal 0x%08x\n", ioread32be(&chip->lcsr.rmwal));
seq_printf(m, "\trmwen 0x%08x\n", ioread32be(&chip->lcsr.rmwen));
seq_printf(m, "\trmwc 0x%08x\n", ioread32be(&chip->lcsr.rmwc));
seq_printf(m, "\trmws 0x%08x\n", ioread32be(&chip->lcsr.rmws));
for(i = 0; i < TSI148_NUM_DMA_CHANNELS; i++){
seq_printf(m, "DMA Controler %d Registers\n", i);
seq_printf(m, "\tdctl 0x%08x\n",
ioread32be(&chip->lcsr.dma[i].dctl));
seq_printf(m, "\tdsta 0x%08x\n",
ioread32be(&chip->lcsr.dma[i].dsta));
seq_printf(m, "\tdcsau 0x%08x\n",
ioread32be(&chip->lcsr.dma[i].dcsau));
seq_printf(m, "\tdcsal 0x%08x\n",
ioread32be(&chip->lcsr.dma[i].dcsal));
seq_printf(m, "\tdcdau 0x%08x\n",
ioread32be(&chip->lcsr.dma[i].dcdau));
seq_printf(m, "\tdcdal 0x%08x\n",
ioread32be(&chip->lcsr.dma[i].dcdal));
seq_printf(m, "\tdclau 0x%08x\n",
ioread32be(&chip->lcsr.dma[i].dclau));
seq_printf(m, "\tdclal 0x%08x\n",
ioread32be(&chip->lcsr.dma[i].dclal));
seq_printf(m, "\tdsau 0x%08x\n",
ioread32be(&chip->lcsr.dma[i].dma_desc.dsau));
seq_printf(m, "\tdsal 0x%08x\n",
ioread32be(&chip->lcsr.dma[i].dma_desc.dsal));
seq_printf(m, "\tddau 0x%08x\n",
ioread32be(&chip->lcsr.dma[i].dma_desc.ddau));
seq_printf(m, "\tddal 0x%08x\n",
ioread32be(&chip->lcsr.dma[i].dma_desc.ddal));
seq_printf(m, "\tdsat 0x%08x\n",
ioread32be(&chip->lcsr.dma[i].dma_desc.dsat));
seq_printf(m, "\tddat 0x%08x\n",
ioread32be(&chip->lcsr.dma[i].dma_desc.ddat));
seq_printf(m, "\tdnlau 0x%08x\n",
ioread32be(&chip->lcsr.dma[i].dma_desc.dnlau));
seq_printf(m, "\tdnlal 0x%08x\n",
ioread32be(&chip->lcsr.dma[i].dma_desc.dnlal));
seq_printf(m, "\tdcnt 0x%08x\n",
ioread32be(&chip->lcsr.dma[i].dma_desc.dcnt));
seq_printf(m, "\tddbs 0x%08x\n",
ioread32be(&chip->lcsr.dma[i].dma_desc.ddbs));
}
return 0;
}
static int tsi148_proc_lcsr_open(struct inode *inode, struct file *file)
{
return single_open(file, tsi148_proc_lcsr_show, NULL);
}
static const struct file_operations tsi148_proc_lcsr_ops = {
.open = tsi148_proc_lcsr_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/**
* tsi148_proc_gcsr_show() - Dump the GCSR register group
*
*/
static int tsi148_proc_gcsr_show(struct seq_file *m, void *data)
{
int i;
unsigned int tmp;
seq_printf(m, "\nGlobal Control and Status Register Group (GCSR):\n");
tmp = ioread32be(&chip->gcsr.devi);
seq_printf(m, "\tveni 0x%04x\n", tmp & 0xffff);
seq_printf(m, "\tdevi 0x%04x\n", (tmp >> 16) & 0xffff);
tmp = ioread32be(&chip->gcsr.ctrl);
seq_printf(m, "\tgctrl 0x%04x\n", (tmp >> 16) & 0xffff);
seq_printf(m, "\tga 0x%02x\n", (tmp >> 8) & 0x3f);
seq_printf(m, "\trevid 0x%02x\n", tmp & 0xff);
seq_printf(m, "Semaphores:\n");
tmp = ioread32be(&chip->gcsr.semaphore[0]);
seq_printf(m, "\tsem0 0x%02x\n", (tmp >> 24) & 0xff);
seq_printf(m, "\tsem1 0x%02x\n", (tmp >> 16) & 0xff);
seq_printf(m, "\tsem2 0x%02x\n", (tmp >> 8) & 0xff);
seq_printf(m, "\tsem3 0x%02x\n", tmp & 0xff);
tmp = ioread32be(&chip->gcsr.semaphore[4]);
seq_printf(m, "\tsem4 0x%02x\n", (tmp >> 24) & 0xff);
seq_printf(m, "\tsem5 0x%02x\n", (tmp >> 16) & 0xff);
seq_printf(m, "\tsem6 0x%02x\n", (tmp >> 8) & 0xff);
seq_printf(m, "\tsem7 0x%02x\n", tmp & 0xff);
seq_printf(m, "Mailboxes:\n");
for (i = 0; i <= TSI148_NUM_MAILBOXES; i++){
seq_printf(m, "\t Mailbox #%d: 0x%08x\n", i,
ioread32be(&chip->gcsr.mbox[i]));
}
return 0;
}
static int tsi148_proc_gcsr_open(struct inode *inode, struct file *file)
{
return single_open(file, tsi148_proc_gcsr_show, NULL);
}
static const struct file_operations tsi148_proc_gcsr_ops = {
.open = tsi148_proc_gcsr_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/**
* tsi148_proc_crcsr_show() - Dump the CRCSR register group
*
*/
static int tsi148_proc_crcsr_show(struct seq_file *m, void *data)
{
seq_printf(m, "\nCR/CSR Register Group:\n");
seq_printf(m, "\tbcr 0x%08x\n", ioread32be(&chip->crcsr.csrbcr));
seq_printf(m, "\tbsr 0x%08x\n", ioread32be(&chip->crcsr.csrbsr));
seq_printf(m, "\tbar 0x%08x\n", ioread32be(&chip->crcsr.cbar));
return 0;
}
static int tsi148_proc_crcsr_open(struct inode *inode, struct file *file)
{
return single_open(file, tsi148_proc_crcsr_show, NULL);
}
static const struct file_operations tsi148_proc_crcsr_ops = {
.open = tsi148_proc_crcsr_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/**
* tsi148_procfs_register() - Create the VME proc tree
* @vme_root: Root directory of the VME proc tree
*/
void tsi148_procfs_register(struct proc_dir_entry *vme_root)
{
struct proc_dir_entry *entry;
tsi148_proc_root = proc_mkdir("tsi148", vme_root);
entry = proc_create("pcfs", S_IFREG | S_IRUGO, tsi148_proc_root,
&tsi148_proc_pcfs_ops);
if (!entry)
printk(KERN_WARNING PFX "Failed to create proc pcfs node\n");
entry = proc_create("lcsr", S_IFREG | S_IRUGO, tsi148_proc_root,
&tsi148_proc_lcsr_ops);
if (!entry)
printk(KERN_WARNING PFX "Failed to create proc lcsr node\n");
entry = proc_create("gcsr", S_IFREG | S_IRUGO, tsi148_proc_root,
&tsi148_proc_gcsr_ops);
if (!entry)
printk(KERN_WARNING PFX "Failed to create proc gcsr node\n");
entry = proc_create("crcsr", S_IFREG | S_IRUGO, tsi148_proc_root,
&tsi148_proc_crcsr_ops);
if (!entry)
printk(KERN_WARNING PFX "Failed to create proc crcsr node\n");
}
/**
* tsi148_procfs_unregister() - Remove the VME proc tree
*
*/
void tsi148_procfs_unregister(struct proc_dir_entry *vme_root)
{
remove_proc_entry("pcfs", tsi148_proc_root);
remove_proc_entry("lcsr", tsi148_proc_root);
remove_proc_entry("gcsr", tsi148_proc_root);
remove_proc_entry("crcsr", tsi148_proc_root);
remove_proc_entry("tsi148", vme_root);
}
#endif /* CONFIG_PROC_FS */
/*
* PCI and VME bus interrupt handlers
*/
/**
* tsi148_handle_pci_error() - Handle a PCI bus error reported by the bridge
*
* This handler simply displays a message on the console and clears
* the error.
*/
void tsi148_handle_pci_error(void)
{
/* Display raw error information */
printk(KERN_ERR PFX
"PCI error at address: %.8x %.8x - attributes: %.8x\n"
"PCI-X attributes: %.8x - PCI-X split completion: %.8x\n",
ioread32be(&chip->lcsr.edpau), ioread32be(&chip->lcsr.edpal),
ioread32be(&chip->lcsr.edpat),
ioread32be(&chip->lcsr.edpxa), ioread32be(&chip->lcsr.edpxs));
/* Clear error */
iowrite32be(TSI148_LCSR_EDPAT_EDPCL, &chip->lcsr.edpat);
}
/**
* tsi148_handle_vme_error() - Handle a VME bus error reported by the bridge
*
* Retrieve and report the offending VME address and Address Modifier, clearing
* the error.
*/
void tsi148_handle_vme_error(struct vme_bus_error *error)
{
u32 attr;
u32 addru, addrl;
/* Store the address */
addru = ioread32be(&chip->lcsr.veau);
addrl = ioread32be(&chip->lcsr.veal);
reg_join(addru, addrl, &error->address);
/* Get the Address Modifier from the attributes */
attr = ioread32be(&chip->lcsr.veat);
error->am = (attr & TSI148_LCSR_VEAT_AM) >> TSI148_LCSR_VEAT_AM_SHIFT;
/* Display raw error information */
if (vme_report_bus_errors) {
printk(KERN_ERR PFX "VME bus error at address: %.8x %.8x - "
"AM: 0x%x - tsi148 attributes: %.8x\n",
addru, addrl, error->am, attr);
}
/* Overflow detected: not much we can do about it */
if (attr & TSI148_LCSR_VEAT_VEOF)
printk(KERN_ERR PFX "VME Bus Exception Overflow Detected\n");
/* Clear error */
iowrite32be(TSI148_LCSR_VEAT_VESCL, &chip->lcsr.veat);
}
/**
* tsi148_generate_interrupt() - Generate an interrupt on the VME bus
* @level: IRQ level (1-7)
* @vector: IRQ vector (0-255)
* @msecs: Timeout for IACK in milliseconds
*
* This function generates an interrupt on the VME bus and waits for IACK
* for msecs milliseconds.
*
* Returns 0 on success or -ETIME if the timeout expired.
*
*/
int tsi148_generate_interrupt(int level, int vector, signed long msecs)
{
unsigned int val;
signed long timeout = msecs_to_jiffies(msecs);
if ((level < 1) || (level > 7))
return -EINVAL;
if ((vector < 0) || (vector > 255))
return -EINVAL;
/* Generate VME IRQ */
val = ioread32be(&chip->lcsr.vicr) & ~TSI148_LCSR_VICR_STID_M;
val |= ((level << 8) | vector);
iowrite32be(val, &chip->lcsr.vicr);
/* Wait until IACK */
while (ioread32be(&chip->lcsr.vicr) & TSI148_LCSR_VICR_IRQS) {
set_current_state(TASK_INTERRUPTIBLE);
timeout = schedule_timeout(timeout);
if (timeout == 0)
break;
}
if (ioread32be(&chip->lcsr.vicr) & TSI148_LCSR_VICR_IRQS) {
/* No IACK received, clear the IRQ */
val = ioread32be(&chip->lcsr.vicr);
val &= ~(TSI148_LCSR_VICR_IRQL_M | TSI148_LCSR_VICR_STID_M);
val |= TSI148_LCSR_VICR_IRQC;
iowrite32be(val, &chip->lcsr.vicr);
return -ETIME;
}
return 0;
}
/*
* Utility functions
*/
/**
* am_to_attr() - Convert a standard VME address modifier to TSI148 attributes
* @am: Address modifier
* @addr_size: Address size
* @transfer_mode: Transfer Mode
* @user_access: User / Supervisor access
* @data_access: Data / Program access
*
*/
static int am_to_attr(enum vme_address_modifier am,
unsigned int *addr_size,
unsigned int *transfer_mode,
unsigned int *user_access,
unsigned int *data_access)
{
switch (am) {
case VME_A16_USER:
*addr_size = TSI148_A16;
*transfer_mode = TSI148_SCT;
*data_access = TSI148_DATA;
*user_access = TSI148_USER;
break;
case VME_A16_SUP:
*addr_size = TSI148_A16;
*transfer_mode = TSI148_SCT;
*data_access = TSI148_DATA;
*user_access = TSI148_SUPER;
break;
case VME_A24_USER_MBLT:
*addr_size = TSI148_A24;
*transfer_mode = TSI148_MBLT;
*data_access = TSI148_DATA;
*user_access = TSI148_USER;
break;
case VME_A24_USER_DATA_SCT:
*addr_size = TSI148_A24;
*transfer_mode = TSI148_SCT;
*data_access = TSI148_DATA;
*user_access = TSI148_USER;
break;
case VME_A24_USER_PRG_SCT:
*addr_size = TSI148_A24;
*transfer_mode = TSI148_SCT;
*data_access = TSI148_PROG;
*user_access = TSI148_USER;
break;
case VME_A24_USER_BLT:
*addr_size = TSI148_A24;
*transfer_mode = TSI148_BLT;
*data_access = TSI148_DATA;
*user_access = TSI148_USER;
break;
case VME_A24_SUP_MBLT:
*addr_size = TSI148_A24;
*transfer_mode = TSI148_MBLT;
*data_access = TSI148_DATA;
*user_access = TSI148_SUPER;
break;
case VME_A24_SUP_DATA_SCT:
*addr_size = TSI148_A24;
*transfer_mode = TSI148_SCT;
*data_access = TSI148_DATA;
*user_access = TSI148_SUPER;
break;
case VME_A24_SUP_PRG_SCT:
*addr_size = TSI148_A24;
*transfer_mode = TSI148_SCT;
*data_access = TSI148_PROG;
*user_access = TSI148_SUPER;
break;
case VME_A24_SUP_BLT:
*addr_size = TSI148_A24;
*transfer_mode = TSI148_BLT;
*data_access = TSI148_DATA;
*user_access = TSI148_SUPER;
break;
case VME_A32_USER_MBLT:
*addr_size = TSI148_A32;
*transfer_mode = TSI148_MBLT;
*data_access = TSI148_DATA;
*user_access = TSI148_USER;
break;
case VME_A32_USER_DATA_SCT:
*addr_size = TSI148_A32;
*transfer_mode = TSI148_SCT;
*data_access = TSI148_DATA;
*user_access = TSI148_USER;
break;
case VME_A32_USER_PRG_SCT:
*addr_size = TSI148_A32;
*transfer_mode = TSI148_SCT;
*data_access = TSI148_PROG;
*user_access = TSI148_USER;
break;
case VME_A32_USER_BLT:
*addr_size = TSI148_A32;
*transfer_mode = TSI148_BLT;
*data_access = TSI148_DATA;
*user_access = TSI148_USER;
break;
case VME_A32_SUP_MBLT:
*addr_size = TSI148_A32;
*transfer_mode = TSI148_MBLT;
*data_access = TSI148_DATA;
*user_access = TSI148_SUPER;
break;
case VME_A32_SUP_DATA_SCT:
*addr_size = TSI148_A32;
*transfer_mode = TSI148_SCT;
*data_access = TSI148_DATA;
*user_access = TSI148_SUPER;
break;
case VME_A32_SUP_PRG_SCT:
*addr_size = TSI148_A32;
*transfer_mode = TSI148_SCT;
*data_access = TSI148_PROG;
*user_access = TSI148_SUPER;
break;
case VME_A32_SUP_BLT:
*addr_size = TSI148_A32;
*transfer_mode = TSI148_BLT;
*data_access = TSI148_DATA;
*user_access = TSI148_SUPER;
break;
case VME_A64_SCT:
*addr_size = TSI148_A64;
*transfer_mode = TSI148_SCT;
*data_access = TSI148_DATA;
*user_access = TSI148_SUPER;
break;
case VME_A64_BLT:
*addr_size = TSI148_A64;
*transfer_mode = TSI148_BLT;
*data_access = TSI148_DATA;
*user_access = TSI148_SUPER;
break;
case VME_A64_MBLT:
*addr_size = TSI148_A64;
*transfer_mode = TSI148_MBLT;
*data_access = TSI148_DATA;
*user_access = TSI148_SUPER;
break;
case VME_CR_CSR:
*addr_size = TSI148_CRCSR;
*transfer_mode = TSI148_SCT;
*data_access = TSI148_DATA;
*user_access = TSI148_USER;
break;
default:
return -1;
}
return 0;
}
/**
* attr_to_am() - Convert TSI148 attributes to a standard VME address modifier
* @addr_size: Address size
* @transfer_mode: Transfer Mode
* @user_access: User / Supervisor access
* @data_access: Data / Program access
* @am: Address modifier
*
*/
static void attr_to_am(unsigned int addr_size, unsigned int transfer_mode,
unsigned int user_access, unsigned int data_access,
int *am)
{
if (addr_size == TSI148_A16) {
if (user_access == TSI148_USER)
*am = VME_A16_USER;
else
*am = VME_A16_SUP;
}
else if (addr_size == TSI148_A24) {
if (user_access == TSI148_USER) {
if (transfer_mode == TSI148_SCT) {
if (data_access == TSI148_DATA)
*am = VME_A24_USER_DATA_SCT;
else
*am = VME_A24_USER_PRG_SCT;
} else if (transfer_mode == TSI148_BLT)
*am = VME_A24_USER_BLT;
else if (transfer_mode == TSI148_MBLT)
*am = VME_A24_USER_MBLT;
} else if (user_access == TSI148_SUPER) {
if (transfer_mode == TSI148_SCT) {
if (data_access == TSI148_DATA)
*am = VME_A24_SUP_DATA_SCT;
else
*am = VME_A24_SUP_PRG_SCT;
} else if (transfer_mode == TSI148_BLT)
*am = VME_A24_SUP_BLT;
else if (transfer_mode == TSI148_MBLT)
*am = VME_A24_SUP_MBLT;
}
}
else if (addr_size == TSI148_A32) {
if (user_access == TSI148_USER) {
if (transfer_mode == TSI148_SCT) {
if (data_access == TSI148_DATA)
*am = VME_A32_USER_DATA_SCT;
else
*am = VME_A32_USER_PRG_SCT;
} else if (transfer_mode == TSI148_BLT)
*am = VME_A32_USER_BLT;
else if (transfer_mode == TSI148_MBLT)
*am = VME_A32_USER_MBLT;
} else if (user_access == TSI148_SUPER) {
if (transfer_mode == TSI148_SCT) {
if (data_access == TSI148_DATA)
*am = VME_A32_SUP_DATA_SCT;
else
*am = VME_A32_SUP_PRG_SCT;
} else if (transfer_mode == TSI148_BLT)
*am = VME_A32_SUP_BLT;
else if (transfer_mode == TSI148_MBLT)
*am = VME_A32_SUP_MBLT;
}
}
else if (addr_size == TSI148_A64) {
if (transfer_mode == TSI148_SCT)
*am = VME_A64_SCT;
else if (transfer_mode == TSI148_BLT)
*am = VME_A64_BLT;
else if (transfer_mode == TSI148_MBLT)
*am = VME_A64_MBLT;
}
else if (addr_size == TSI148_CRCSR)
*am = VME_CR_CSR;
}
/*
* sub64hi() - Calculate the upper 32 bits of the 64 bit difference
* hi/lo0 - hi/lo1
*/
static int sub64hi(unsigned int lo0, unsigned int hi0,
unsigned int lo1, unsigned int hi1)
{
if (lo0 < lo1)
return hi0 - hi1 - 1;
return hi0 - hi1;
}
/*
* sub64hi() - Calculate the upper 32 bits of the 64 bit sum
* hi/lo0 + hi/lo1
*/
static int add64hi(unsigned int lo0, unsigned int hi0,
unsigned int lo1, unsigned int hi1)
{
if ((lo0 + lo1) < lo1)
return hi0 + hi1 + 1;
return hi0 + hi1;
}
/*
* TSI148 DMA support
*/
/**
* tsi148_dma_get_status() - Get the status of a DMA channel
* @chan: DMA channel descriptor
*
**/
int tsi148_dma_get_status(struct dma_channel *chan)
{
return ioread32be(&chip->lcsr.dma[chan->num].dsta);
}
/**
* tsi148_dma_busy() - Get the busy status of a DMA channel
* @chan: DMA channel descriptor
*
**/
int tsi148_dma_busy(struct dma_channel *chan)
{
return tsi148_dma_get_status(chan) & TSI148_LCSR_DSTA_BSY;
}
/**
* tsi148_dma_done() - Get the done status of a DMA channel
* @chan: DMA channel descriptor
*
*/
int tsi148_dma_done(struct dma_channel *chan)
{
return tsi148_dma_get_status(chan) & TSI148_LCSR_DSTA_DON;
}
/**
* tsi148_setup_dma_attributes() - Build the DMA attributes word
* @v2esst_mode: VME 2eSST transfer speed
* @data_width: VME data width (D16 or D32 only)
* @am: VME address modifier
*
* This function builds the DMA attributes word. All parameters are
* checked.
*
* NOTE: The function relies on the fact that the DSAT and DDAT registers have
* the same layout.
*
* Returns %EINVAL if any parameter is not consistent.
*/
static int tsi148_setup_dma_attributes(enum vme_2esst_mode v2esst_mode,
enum vme_data_width data_width,
enum vme_address_modifier am)
{
unsigned int attrs = 0;
unsigned int addr_size;
unsigned int user_access;
unsigned int data_access;
unsigned int transfer_mode;
switch (v2esst_mode) {
case VME_SST160:
case VME_SST267:
case VME_SST320:
attrs |= ((v2esst_mode << TSI148_LCSR_DSAT_2eSSTM_SHIFT) &
TSI148_LCSR_DSAT_2eSSTM_M);
break;
default:
printk(KERN_ERR
"%s: invalid v2esst_mode %d\n",
__func__, v2esst_mode);
return -EINVAL;
}
switch (data_width) {
case VME_D16:
attrs |= ((TSI148_DW_16 << TSI148_LCSR_DSAT_DBW_SHIFT) &
TSI148_LCSR_DSAT_DBW_M);
break;
case VME_D32:
attrs |= ((TSI148_DW_32 << TSI148_LCSR_DSAT_DBW_SHIFT) &
TSI148_LCSR_DSAT_DBW_M);
break;
default:
printk(KERN_ERR
"%s: invalid data_width %d\n",
__func__, data_width);
return -EINVAL;
}
if (am_to_attr(am, &addr_size, &transfer_mode, &user_access,
&data_access)) {
printk(KERN_ERR
"%s: invalid am %x\n",
__func__, am);
return -EINVAL;
}
switch (transfer_mode) {
case TSI148_SCT:
case TSI148_BLT:
case TSI148_MBLT:
case TSI148_2eVME:
case TSI148_2eSST:
case TSI148_2eSSTB:
attrs |= ((transfer_mode << TSI148_LCSR_DSAT_TM_SHIFT) &
TSI148_LCSR_DSAT_TM_M);
break;
default:
printk(KERN_ERR
"%s: invalid transfer_mode %d\n",
__func__, transfer_mode);
return -EINVAL;
}
switch (user_access) {
case TSI148_SUPER:
attrs |= TSI148_LCSR_DSAT_SUP;
break;
case TSI148_USER:
break;
default:
printk(KERN_ERR
"%s: invalid user_access %d\n",
__func__, user_access);
return -EINVAL;
}
switch (data_access) {
case TSI148_PROG:
attrs |= TSI148_LCSR_DSAT_PGM;
break;
case TSI148_DATA:
break;
default:
printk(KERN_ERR
"%s: invalid data_access %d\n",
__func__, data_access);
return -EINVAL;
}
switch (addr_size) {
case TSI148_A16:
case TSI148_A24:
case TSI148_A32:
case TSI148_A64:
case TSI148_CRCSR:
case TSI148_USER1:
case TSI148_USER2:
case TSI148_USER3:
case TSI148_USER4:
attrs |= ((addr_size << TSI148_LCSR_DSAT_AMODE_SHIFT) &
TSI148_LCSR_DSAT_AMODE_M);
break;
default:
printk(KERN_ERR
"%s: invalid addr_size %d\n",
__func__, addr_size);
return -EINVAL;
}
return attrs;
}
/**
* tsi148_dma_setup_src() - Setup the source attributes for a DMA transfer
* @desc: DMA transfer descriptor
*
*/
static int tsi148_dma_setup_src(struct vme_dma *desc)
{
int rc;
switch (desc->dir) {
case VME_DMA_TO_DEVICE: /* src = PCI */
rc = TSI148_LCSR_DSAT_TYP_PCI;
break;
case VME_DMA_FROM_DEVICE: /* src = VME */
rc = tsi148_setup_dma_attributes(desc->src.v2esst_mode,
desc->src.data_width,
desc->src.am);
if (rc >= 0)
rc |= TSI148_LCSR_DSAT_TYP_VME;
break;
default:
printk(KERN_ERR
"%s: invalid direction %d\n",
__func__, desc->dir);
rc = -EINVAL;
}
return rc;
}
/**
* tsi148_dma_setup_dst() - Setup the destination attributes for a DMA transfer
* @desc: DMA transfer descriptor
*
*/
static int tsi148_dma_setup_dst(struct vme_dma *desc)
{
int rc;
switch (desc->dir) {
case VME_DMA_TO_DEVICE: /* dst = VME */
rc = tsi148_setup_dma_attributes(desc->dst.v2esst_mode,
desc->dst.data_width,
desc->dst.am);
if (rc >= 0)
rc |= TSI148_LCSR_DDAT_TYP_VME;
break;
case VME_DMA_FROM_DEVICE: /* dst = PCI */
rc = TSI148_LCSR_DDAT_TYP_PCI;
break;
default:
printk(KERN_ERR
"%s: invalid direction %d\n",
__func__, desc->dir);
rc = -EINVAL;
}
return rc;
}
/**
* tsi148_dma_setup_ctl() - Setup the DMA control word
* @desc: DMA transfer descriptor
*
*/
static int tsi148_dma_setup_ctl(struct vme_dma *desc)
{
int rc = 0;
/* A little bit of checking. Could be done earlier, dunno. */
if ((desc->ctrl.vme_block_size < VME_DMA_BSIZE_32) ||
(desc->ctrl.vme_block_size > VME_DMA_BSIZE_4096))
return -EINVAL;
if ((desc->ctrl.vme_backoff_time < VME_DMA_BACKOFF_0) ||
(desc->ctrl.vme_backoff_time > VME_DMA_BACKOFF_64))
return -EINVAL;
if ((desc->ctrl.pci_block_size < VME_DMA_BSIZE_32) ||
(desc->ctrl.pci_block_size > VME_DMA_BSIZE_4096))
return -EINVAL;
if ((desc->ctrl.pci_backoff_time < VME_DMA_BACKOFF_0) ||
(desc->ctrl.pci_backoff_time > VME_DMA_BACKOFF_64))
return -EINVAL;
rc |= (desc->ctrl.vme_block_size << TSI148_LCSR_DCTL_VBKS_SHIFT) &
TSI148_LCSR_DCTL_VBKS_M;
rc |= (desc->ctrl.vme_backoff_time << TSI148_LCSR_DCTL_VBOT_SHIFT) &
TSI148_LCSR_DCTL_VBOT_M;
rc |= (desc->ctrl.pci_block_size << TSI148_LCSR_DCTL_PBKS_SHIFT) &
TSI148_LCSR_DCTL_PBKS_M;
rc |= (desc->ctrl.pci_backoff_time << TSI148_LCSR_DCTL_PBOT_SHIFT) &
TSI148_LCSR_DCTL_PBOT_M;
return rc;
}
/**
* tsi148_dma_free_chain() - Free all the linked HW DMA descriptors of a channel
* @chan: DMA channel
*
*/
void tsi148_dma_free_chain(struct dma_channel *chan)
{
struct hw_desc_entry *hw_desc;
struct hw_desc_entry *tmp;
int count = 1;
list_for_each_entry_safe(hw_desc, tmp, &chan->hw_desc_list, list) {
pci_pool_free(dma_desc_pool, hw_desc->va, hw_desc->phys);
list_del(&hw_desc->list);
kfree(hw_desc);
count++;
}
}
static inline int get_vmeaddr(struct vme_dma *desc, unsigned int *vme_addr)
{
switch (desc->dir) {
case VME_DMA_TO_DEVICE: /* src = PCI - dst = VME */
*vme_addr = desc->dst.addrl;
return 0;
case VME_DMA_FROM_DEVICE: /* src = VME - dst = PCI */
*vme_addr = desc->src.addrl;
return 0;
default:
return -EINVAL;
}
}
static int
hwdesc_init(struct dma_channel *chan, dma_addr_t *phys,
struct tsi148_dma_desc **virt, struct hw_desc_entry **hw_desc)
{
struct hw_desc_entry *entry;
/* Allocate a HW DMA descriptor from the pool */
*virt = pci_pool_alloc(dma_desc_pool, GFP_KERNEL, phys);
if (!*virt)
return -ENOMEM;
/* keep the virt. and phys. addresses of the descriptor in a list */
*hw_desc = kmalloc(sizeof(struct hw_desc_entry), GFP_KERNEL);
if (!*hw_desc) {
pci_pool_free(dma_desc_pool, *virt, *phys);
return -ENOMEM;
}
entry = *hw_desc;
entry->va = *virt;
entry->phys = *phys;
list_add_tail(&entry->list, &chan->hw_desc_list);
return 0;
}
/*
* Setup the hardware descriptors for the link list.
* Beware that the fields have to be setup in big endian mode.
*/
static int
tsi148_fill_dma_desc(struct dma_channel *chan, struct tsi148_dma_desc *tsi,
unsigned int vme_addr, dma_addr_t dma_addr, unsigned int len,
unsigned int dsat, unsigned int ddat)
{
struct vme_dma *desc = &chan->desc;
/* Setup the source and destination addresses */
tsi->dsau = 0;
tsi->ddau = 0;
switch (desc->dir) {
case VME_DMA_TO_DEVICE: /* src = PCI - dst = VME */
tsi->dsal = cpu_to_be32(dma_addr);
tsi->ddal = cpu_to_be32(vme_addr);
tsi->ddbs = cpu_to_be32(desc->dst.bcast_select);
break;
case VME_DMA_FROM_DEVICE: /* src = VME - dst = PCI */
tsi->dsal = cpu_to_be32(vme_addr);
tsi->ddal = cpu_to_be32(dma_addr);
tsi->ddbs = 0;
break;
default:
return -EINVAL;
}
tsi->dcnt = cpu_to_be32(len);
tsi->dsat = cpu_to_be32(dsat);
tsi->ddat = cpu_to_be32(ddat);
/* By default behave as if we were at the end of the list */
tsi->dnlau = 0;
tsi->dnlal = cpu_to_be32(TSI148_LCSR_DNLAL_LLA);
return 0;
}
#ifdef DEBUG_DMA
static void tsi148_dma_debug_info(struct tsi148_dma_desc *tsi, int i, int j)
{
printk(KERN_DEBUG PFX "descriptor %d-%d @%p\n", i, j, tsi);
printk(KERN_DEBUG PFX " src : %08x:%08x %08x\n",
be32_to_cpu(tsi->dsau), be32_to_cpu(tsi->dsal),
be32_to_cpu(tsi->dsat));
printk(KERN_DEBUG PFX " dst : %08x:%08x %08x\n",
be32_to_cpu(tsi->ddau), be32_to_cpu(tsi->ddal),
be32_to_cpu(tsi->ddat));
printk(KERN_DEBUG PFX " cnt : %08x\n", be32_to_cpu(tsi->dcnt));
printk(KERN_DEBUG PFX " nxt : %08x:%08x\n",
be32_to_cpu(tsi->dnlau), be32_to_cpu(tsi->dnlal));
}
#else
static void tsi148_dma_debug_info(struct tsi148_dma_desc *tsi, int i, int j)
{
}
#endif
/*
* verbatim from the VME64 standard:
* 2.3.7 Block Transfer Capabilities, p. 42.
* RULE 2.12a: D08(EO), D16, D32 and MD32 block transfer cycles
* (BLT) *MUST NOT* cross any 256 byte boundary.
* RULE 2.78: MBLT cycles *MUST NOT* cross any 2048 byte boundary.
*/
static inline int __tsi148_get_bshift(int am)
{
switch (am) {
case VME_A64_MBLT:
case VME_A32_USER_MBLT:
case VME_A32_SUP_MBLT:
case VME_A24_USER_MBLT:
case VME_A24_SUP_MBLT:
return 11;
case VME_A64_BLT:
case VME_A32_USER_BLT:
case VME_A32_SUP_BLT:
case VME_A40_BLT:
case VME_A24_USER_BLT:
case VME_A24_SUP_BLT:
return 8;
default:
return 0;
}
}
static int tsi148_get_bshift(struct dma_channel *chan)
{
struct vme_dma *desc = &chan->desc;
int am;
if (desc->dir == VME_DMA_FROM_DEVICE)
am = desc->src.am;
else
am = desc->dst.am;
return __tsi148_get_bshift(am);
}
/* Note: block sizes are always powers of 2 */
static inline unsigned long tsi148_get_bsize(int bshift)
{
return 1 << bshift;
}
static inline unsigned long tsi148_get_bmask(unsigned long bsize)
{
return bsize - 1;
}
/*
* Add a certain chunk of data to the TSI DMA linked list.
* Note that this function deals with physical addresses on the
* host CPU and VME addresses on the VME bus.
*/
static int
tsi148_dma_link_add(struct dma_channel *chan, struct tsi148_dma_desc **virt,
unsigned int vme_addr, dma_addr_t dma_addr, unsigned int size,
int numpages, int index, int bshift, unsigned int dsat,
unsigned int ddat)
{
struct hw_desc_entry *hw_desc = NULL;
struct tsi148_dma_desc *curr;
struct tsi148_dma_desc *next = NULL;
struct vme_dma *desc = &chan->desc;
dma_addr_t phys_next;
dma_addr_t dma;
dma_addr_t dma_end;
unsigned int vme;
unsigned int vme_end;
unsigned int len;
int rc;
int i = 0;
/*
* The function expects **virt to be already initialised.
* When calling this function in a loop, it will always set virt
* to point to the next link in the chain, or to NULL when the
* current link is the last in the chain.
*/
BUG_ON(virt == NULL || *virt == NULL);
curr = *virt;
vme = vme_addr;
vme_end = vme_addr + size;
dma = dma_addr;
dma_end = dma_addr + size;
len = size;
while (vme < vme_end && dma < dma_end) {
/* calculate the length up to the next block boundary */
if (bshift) {
unsigned long bsize = tsi148_get_bsize(bshift);
unsigned long bmask = tsi148_get_bmask(bsize);
unsigned int unaligned = vme & bmask;
len = bsize - unaligned;
}
/* check the VME block won't overflow */
if (dma + len > dma_end)
len = dma_end - dma;
tsi148_fill_dma_desc(chan, curr, vme, dma, len, dsat, ddat);
/* increment the VME address unless novmeinc is set */
if (!desc->novmeinc)
vme += len;
dma += len;
/* chain consecutive links together */
if (index < numpages - 1 || dma < dma_end) {
rc = hwdesc_init(chan, &phys_next, &next, &hw_desc);
if (rc)
return rc;
curr->dnlau = 0;
curr->dnlal = cpu_to_be32(phys_next &
TSI148_LCSR_DNLAL_DNLAL_M);
}
tsi148_dma_debug_info(curr, index, i);
curr = next;
i++;
}
*virt = next;
return 0;
}
/**
* tsi148_dma_setup_chain() - Setup the linked list of TSI148 DMA descriptors
* @chan: DMA channel descriptor
* @dsat: DMA source attributes
* @ddat: DMA destination attributes
*
*/
static int tsi148_dma_setup_chain(struct dma_channel *chan,
unsigned int dsat, unsigned int ddat)
{
int i;
int rc;
struct scatterlist *sg;
struct vme_dma *desc = &chan->desc;
dma_addr_t phys_start;
struct tsi148_dma_desc *curr = NULL;
struct hw_desc_entry *hw_desc = NULL;
unsigned int vme_addr = 0;
dma_addr_t dma_addr;
unsigned int len;
int bshift = tsi148_get_bshift(chan);
rc = hwdesc_init(chan, &phys_start, &curr, &hw_desc);
if (rc)
return rc;
rc = get_vmeaddr(desc, &vme_addr);
if (rc)
goto out_free;
for_each_sg(chan->sgl, sg, chan->sg_mapped, i) {
dma_addr = sg_dma_address(sg);
len = sg_dma_len(sg);
rc = tsi148_dma_link_add(chan, &curr, vme_addr, dma_addr, len,
chan->sg_mapped, i, bshift, dsat, ddat);
if (rc)
goto out_free;
/* For non incrementing DMA, reset the VME address */
if (!desc->novmeinc)
vme_addr += len;
}
/* Now program the DMA registers with the first entry in the list */
iowrite32be(0, &chip->lcsr.dma[chan->num].dma_desc.dnlau);
iowrite32be(phys_start & TSI148_LCSR_DNLAL_DNLAL_M,
&chip->lcsr.dma[chan->num].dma_desc.dnlal);
return 0;
out_free:
tsi148_dma_free_chain(chan);
return rc;
}
/**
* tsi148_dma_setup() - Setup a TSI148 DMA channel for a transfer
* @chan: DMA channel descriptor
*
*/
int tsi148_dma_setup(struct dma_channel *chan)
{
int rc;
struct vme_dma *desc = &chan->desc;
unsigned int dsat;
unsigned int ddat;
unsigned int dctl;
/* Setup DMA source attributes */
if ((rc = tsi148_dma_setup_src(desc)) < 0) {
printk(KERN_ERR "%s: src setup failed\n", __func__);
return rc;
}
dsat = rc;
/* Setup DMA destination attributes */
if ((rc = tsi148_dma_setup_dst(desc)) < 0) {
printk(KERN_ERR "%s: dst setup failed\n", __func__);
return rc;
}
ddat = rc;
/* Setup DMA control */
if ((rc = tsi148_dma_setup_ctl(desc)) < 0) {
printk(KERN_ERR "%s: ctl setup failed\n", __func__);
return rc;
}
dctl = rc;
/* always use the scatter-gather list */
chan->chained = 1;
dctl &= ~TSI148_LCSR_DCTL_MOD;
iowrite32be(dctl, &chip->lcsr.dma[chan->num].dctl);
rc = tsi148_dma_setup_chain(chan, dsat, ddat);
return rc;
}
/**
* tsi148_dma_start() - Start DMA transfer on a channel
* @chan: DMA channel descriptor
*
*/
void tsi148_dma_start(struct dma_channel *chan)
{
unsigned int dctl;
dctl = ioread32be(&chip->lcsr.dma[chan->num].dctl);
dctl |= TSI148_LCSR_DCTL_DGO;
iowrite32be(dctl, &chip->lcsr.dma[chan->num].dctl);
}
/**
* tsi148_dma_abort() - Abort a DMA transfer
* @chan: DMA channel descriptor
*
*/
void tsi148_dma_abort(struct dma_channel *chan)
{
unsigned int dctl;
dctl = ioread32be(&chip->lcsr.dma[chan->num].dctl);
dctl |= TSI148_LCSR_DCTL_ABT;
iowrite32be(dctl, &chip->lcsr.dma[chan->num].dctl);
}
/**
* tsi148_dma_release() - Release resources used for a DMA transfer
* @chan: DMA channel descriptor
*
* Only chained transfer are allocating memory. For direct transfer, there
* is nothig to free.
*/
void tsi148_dma_release(struct dma_channel *chan)
{
if (chan->chained)
tsi148_dma_free_chain(chan);
}
void tsi148_dma_exit(void)
{
pci_pool_destroy(dma_desc_pool);
}
int tsi148_dma_init(void)
{
/*
* Create the DMA chained descriptor pool.
* Those descriptors must be 64-bit aligned as specified in the
* TSI148 User Manual. Also do not allow descriptors to cross a
* page boundary as the 2 pages may not be contiguous.
*/
dma_desc_pool = pci_pool_create("vme_dma_desc_pool", vme_bridge->pdev,
sizeof(struct tsi148_dma_desc),
8, 4096);
if (dma_desc_pool == NULL) {
printk(KERN_WARNING PFX "Failed to allocate DMA pool\n");
return -ENOMEM;
}
return 0;
}
/*
* TSI148 window support
*/
/**
* tsi148_setup_window_attributes() - Build the window attributes word
* @v2esst_mode: VME 2eSST transfer speed
* @data_width: VME data width (D16 or D32 only)
* @am: VME address modifier
*
* This function builds the window attributes word. All parameters are
* checked.
*
* Returns %EINVAL if any parameter is not consistent.
*/
static int tsi148_setup_window_attributes(enum vme_2esst_mode v2esst_mode,
enum vme_data_width data_width,
enum vme_address_modifier am)
{
unsigned int attrs = 0;
unsigned int addr_size;
unsigned int user_access;
unsigned int data_access;
unsigned int transfer_mode;
switch (v2esst_mode) {
case VME_SST160:
case VME_SST267:
case VME_SST320:
attrs |= ((v2esst_mode << TSI148_LCSR_OTAT_2eSSTM_SHIFT) &
TSI148_LCSR_OTAT_2eSSTM_M);
break;
default:
return -EINVAL;
}
switch (data_width) {
case VME_D16:
attrs |= ((TSI148_DW_16 << TSI148_LCSR_OTAT_DBW_SHIFT) &
TSI148_LCSR_OTAT_DBW_M);
break;
case VME_D32:
attrs |= ((TSI148_DW_32 << TSI148_LCSR_OTAT_DBW_SHIFT) &
TSI148_LCSR_OTAT_DBW_M);
break;
default:
return -EINVAL;
}
if (am_to_attr(am, &addr_size, &transfer_mode, &user_access,
&data_access))
return -EINVAL;
switch (transfer_mode) {
case TSI148_SCT:
case TSI148_BLT:
case TSI148_MBLT:
case TSI148_2eVME:
case TSI148_2eSST:
case TSI148_2eSSTB:
attrs |= ((transfer_mode << TSI148_LCSR_OTAT_TM_SHIFT) &
TSI148_LCSR_OTAT_TM_M);
break;
default:
return -EINVAL;
}
switch (user_access) {
case TSI148_SUPER:
attrs |= TSI148_LCSR_OTAT_SUP;
break;
case TSI148_USER:
break;
default:
return -EINVAL;
}
switch (data_access) {
case TSI148_PROG:
attrs |= TSI148_LCSR_OTAT_PGM;
break;
case TSI148_DATA:
break;
default:
return -EINVAL;
}
switch (addr_size) {
case TSI148_A16:
case TSI148_A24:
case TSI148_A32:
case TSI148_A64:
case TSI148_CRCSR:
case TSI148_USER1:
case TSI148_USER2:
case TSI148_USER3:
case TSI148_USER4:
attrs |= ((addr_size << TSI148_LCSR_OTAT_AMODE_SHIFT) &
TSI148_LCSR_OTAT_AMODE_M);
break;
default:
return -EINVAL;
}
return attrs;
}
/**
* tsi148_get_window_attr() - Get a hardware window attributes
* @desc: Descriptor of the window (only the window number is used)
*
*/
void tsi148_get_window_attr(struct vme_mapping *desc)
{
int window_num = desc->window_num;
unsigned int transfer_mode;
unsigned int user_access;
unsigned int data_access;
unsigned int addr_size;
int am = -1;
struct tsi148_otrans *trans = &chip->lcsr.otrans[window_num];
unsigned int otat;
unsigned int otsau, otsal;
unsigned int oteau, oteal;
unsigned int otofu, otofl;
/* Get window Control & Bus attributes register */
otat = ioread32be(&trans->otat);
if (otat & TSI148_LCSR_OTAT_EN)
desc->window_enabled = 1;
if (!(otat & TSI148_LCSR_OTAT_MRPFD))
desc->read_prefetch_enabled = 1;
desc->read_prefetch_size = (otat & TSI148_LCSR_OTAT_PFS_M) >>
TSI148_LCSR_OTAT_PFS_SHIFT;
desc->v2esst_mode = (otat & TSI148_LCSR_OTAT_2eSSTM_M) >>
TSI148_LCSR_OTAT_2eSSTM_SHIFT;
switch ((otat & TSI148_LCSR_OTAT_DBW_M) >> TSI148_LCSR_OTAT_DBW_SHIFT) {
case TSI148_LCSR_OTAT_DBW_16:
desc->data_width = VME_D16;
break;
case TSI148_LCSR_OTAT_DBW_32:
desc->data_width = VME_D32;
break;
}
desc->bcast_select = ioread32be(&chip->lcsr.otrans[window_num].otbs);
/*
* The VME address modifiers is encoded into the 4 following registers.
* Here we then have to map that back to a single value - This is
* coming out real ugly (sigh).
*/
addr_size = (otat & TSI148_LCSR_OTAT_AMODE_M) >>
TSI148_LCSR_OTAT_AMODE_SHIFT;
transfer_mode = (otat & TSI148_LCSR_OTAT_TM_M) >>
TSI148_LCSR_OTAT_TM_SHIFT;
user_access = (otat & TSI148_LCSR_OTAT_SUP) ?
TSI148_SUPER : TSI148_USER;
data_access = (otat & TSI148_LCSR_OTAT_PGM) ?
TSI148_PROG : TSI148_DATA;
attr_to_am(addr_size, transfer_mode, user_access, data_access, &am);
if (am == -1)
printk(KERN_WARNING PFX
"%s - unsupported AM:\n"
"\taddr size: %x TM: %x usr/sup: %d data/prg:%d\n",
__func__, addr_size, transfer_mode, user_access,
data_access);
desc->am = am;
/* Get window mappings */
otsal = ioread32be(&chip->lcsr.otrans[window_num].otsal);
otsau = ioread32be(&chip->lcsr.otrans[window_num].otsau);
oteal = ioread32be(&chip->lcsr.otrans[window_num].oteal);
oteau = ioread32be(&chip->lcsr.otrans[window_num].oteau);
otofl = ioread32be(&chip->lcsr.otrans[window_num].otofl);
otofu = ioread32be(&chip->lcsr.otrans[window_num].otofu);
desc->pci_addrl = otsal;
desc->pci_addru = otsau;
desc->sizel = oteal - otsal;
desc->sizeu = sub64hi(oteal, oteau, otsal, otsau);
desc->vme_addrl = otofl + otsal;
if (addr_size == TSI148_A64)
desc->vme_addru = add64hi(otofl, otofu, otsal, otsau);
else
desc->vme_addru = 0;
}
/**
* tsi148_create_window() - Create a PCI-VME window
* @desc: Window descriptor
*
* Setup the TSI148 outbound window registers and enable the window for
* use.
*
* Returns 0 on success, %EINVAL in case of wrong parameter.
*/
int tsi148_create_window(struct vme_mapping *desc)
{
int window_num = desc->window_num;
int rc;
unsigned int addr;
unsigned int size;
struct tsi148_otrans *trans = &chip->lcsr.otrans[window_num];
unsigned int otat = 0;
unsigned int oteal, oteau;
unsigned int otofl, otofu;
/*
* Do some sanity checking on the window descriptor.
* PCI address, VME address and window size should be aligned
* on 64k boudaries. So round down the VME address and round up the
* VME size to 64K boundaries.
* The PCI address should already have been rounded.
*/
addr = desc->vme_addrl;
size = desc->sizel;
if (addr & 0xffff)
addr = desc->vme_addrl & ~0xffff;
if (size & 0xffff)
size = (desc->vme_addrl + desc->sizel + 0x10000) & ~0xffff;
desc->vme_addrl = addr;
desc->sizel = size;
if (desc->pci_addrl & 0xffff)
return -EINVAL;
/* Setup the window attributes */
rc = tsi148_setup_window_attributes(desc->v2esst_mode,
desc->data_width,
desc->am);
if (rc < 0)
return rc;
otat = (unsigned int)rc;
/* Enable the window */
otat |= TSI148_LCSR_OTAT_EN;
if (desc->read_prefetch_enabled) {
/* clear Read prefetch disable bit */
otat &= ~TSI148_LCSR_OTAT_MRPFD;
otat |= ((desc->read_prefetch_size <<
TSI148_LCSR_OTAT_PFS_SHIFT) & TSI148_LCSR_OTAT_PFS_M);
} else {
/* Set Read prefetch disable bit */
otat |= TSI148_LCSR_OTAT_MRPFD;
}
/* Setup the VME 2eSST broadcast select */
iowrite32be(desc->bcast_select & TSI148_LCSR_OTBS_M,
&trans->otbs);
/* Setup the PCI side start address */
iowrite32be(desc->pci_addrl & TSI148_LCSR_OTSAL_M,
&trans->otsal);
iowrite32be(desc->pci_addru, &trans->otsau);
/*
* Setup the PCI side end address
* Note that 'oteal' is formed by the upper 2 bytes common to the
* addresses in the last 64K chunk of the window.
*/
oteal = (desc->pci_addrl + desc->sizel - 1) & ~0xffff;
oteau = add64hi(desc->pci_addrl, desc->pci_addru,
desc->sizel, desc->sizeu);
iowrite32be(oteal, &trans->oteal);
iowrite32be(oteau, &trans->oteau);
/* Setup the VME offset */
otofl = desc->vme_addrl - desc->pci_addrl;
otofu = sub64hi(desc->vme_addrl, desc->vme_addru,
desc->pci_addrl, desc->pci_addru);
iowrite32be(otofl, &trans->otofl);
iowrite32be(otofu, &trans->otofu);
/*
* Finally, write the window attributes, also enabling the
* window
*/
iowrite32be(otat, &trans->otat);
desc->window_enabled = 1;
return 0;
}
/**
* tsi148_remove_window() - Disable a PCI-VME window
* @desc: Window descriptor (only the window number is used)
*
* Disable the PCI-VME window specified in the descriptor.
*/
void tsi148_remove_window(struct vme_mapping *desc)
{
int window_num = desc->window_num;
struct tsi148_otrans *trans = &chip->lcsr.otrans[window_num];
/* Clear the attribute register thus disabling the window */
iowrite32be(0, &trans->otat);
/* Wipe the window registers */
iowrite32be(0, &trans->otsal);
iowrite32be(0, &trans->otsau);
iowrite32be(0, &trans->oteal);
iowrite32be(0, &trans->oteau);
iowrite32be(0, &trans->otofl);
iowrite32be(0, &trans->otofu);
iowrite32be(0, &trans->otbs);
desc->window_enabled = 0;
}
/**
* am_to_crgattrs() - Convert address modifier to CRG Attributes
* @am: Address modifier
*/
static int am_to_crgattrs(enum vme_address_modifier am)
{
unsigned int attrs = 0;
unsigned int addr_size;
unsigned int user_access;
unsigned int data_access;
unsigned int transfer_mode;
if (am_to_attr(am, &addr_size, &transfer_mode, &user_access,
&data_access))
return -EINVAL;
switch (addr_size) {
case TSI148_A16:
attrs |= TSI148_LCSR_CRGAT_AS_16;
break;
case TSI148_A24:
attrs |= TSI148_LCSR_CRGAT_AS_24;
break;
case TSI148_A32:
attrs |= TSI148_LCSR_CRGAT_AS_32;
break;
case TSI148_A64:
attrs |= TSI148_LCSR_CRGAT_AS_64;
break;
default:
return -EINVAL;
}
switch (data_access) {
case TSI148_PROG:
attrs |= TSI148_LCSR_CRGAT_PGM;
break;
case TSI148_DATA:
attrs |= TSI148_LCSR_CRGAT_DATA;
break;
default:
return -EINVAL;
}
switch (user_access) {
case TSI148_SUPER:
attrs |= TSI148_LCSR_CRGAT_SUPR;
break;
case TSI148_USER:
attrs |= TSI148_LCSR_CRGAT_NPRIV;
break;
default:
return -EINVAL;
}
return attrs;
}
/**
* tsi148_setup_crg() - Setup the CRG inbound mapping
* @vme_base: VME base address for the CRG mapping
* @am: Address modifier for the mapping
*/
int tsi148_setup_crg(unsigned int vme_base,
enum vme_address_modifier am)
{
int attrs;
iowrite32be(0, &chip->lcsr.cbau);
iowrite32be(vme_base, &chip->lcsr.cbal);
attrs = am_to_crgattrs(am);
if (attrs < 0) {
iowrite32be(0, &chip->lcsr.cbal);
return -1;
}
attrs |= TSI148_LCSR_CRGAT_EN;
iowrite32be(attrs, &chip->lcsr.crgat);
return 0;
}
/**
* tsi148_disable_crg() - Disable the CRG VME mapping
*
*/
void tsi148_disable_crg(struct tsi148_chip *regs)
{
iowrite32be(0, &regs->lcsr.crgat);
iowrite32be(0, &regs->lcsr.cbau);
iowrite32be(0, &regs->lcsr.cbal);
}
/**
* tsi148_quiesce() - Shutdown the TSI148 chip
*
* Put VME bridge in quiescent state. Disable all decoders,
* clear all interrupts.
*/
void tsi148_quiesce(struct tsi148_chip *regs)
{
int i;
unsigned int val;
/* Shutdown all inbound and outbound windows. */
for (i = 0; i < TSI148_NUM_OUT_WINDOWS; i++) {
iowrite32be(0, &regs->lcsr.itrans[i].itat);
iowrite32be(0, &regs->lcsr.otrans[i].otat);
}
for (i= 0; i < TSI148_NUM_DMA_CHANNELS; i++) {
iowrite32be(0, &regs->lcsr.dma[i].dma_desc.dnlau);
iowrite32be(0, &regs->lcsr.dma[i].dma_desc.dnlal);
iowrite32be(0, &regs->lcsr.dma[i].dma_desc.dcnt);
}
/* Shutdown Location monitor. */
iowrite32be(0, &regs->lcsr.lmat);
/* Shutdown CRG map. */
tsi148_disable_crg(regs);
/* Clear error status. */
iowrite32be(TSI148_LCSR_EDPAT_EDPCL, &regs->lcsr.edpat);
iowrite32be(TSI148_LCSR_VEAT_VESCL, &regs->lcsr.veat);
iowrite32be(TSI148_LCSR_PCSR_SRTO | TSI148_LCSR_PCSR_SRTE |
TSI148_LCSR_PCSR_DTTE | TSI148_LCSR_PCSR_MRCE,
&regs->lcsr.pstat);
/* Clear VIRQ interrupt (if any) */
iowrite32be(TSI148_LCSR_VICR_IRQC, &regs->lcsr.vicr);
/* Disable and clear all interrupts. */
iowrite32be(0, &regs->lcsr.inteo);
iowrite32be(0xffffffff, &regs->lcsr.intc);
iowrite32be(0xffffffff, &regs->lcsr.inten);
/* Map all Interrupts to PCI INTA */
iowrite32be(0, &regs->lcsr.intm1);
iowrite32be(0, &regs->lcsr.intm2);
/*
* Set bus master timeout
* The timeout overrides the DMA block size if set too low.
* Enable release on request so that there will not be a re-arbitration
* for each transfer.
*/
val = ioread32be(&regs->lcsr.vmctrl);
val &= ~(TSI148_LCSR_VMCTRL_VTON_M | TSI148_LCSR_VMCTRL_VREL_M);
val |= (TSI148_LCSR_VMCTRL_VTON_512 | TSI148_LCSR_VMCTRL_VREL_T_D_R);
iowrite32be(val, &regs->lcsr.vmctrl);
}
/*
*
*/
/**
* tsi148_init() - Chip initialization
* @regs: Chip registers base address
*
*/
void tsi148_init(struct tsi148_chip *regs)
{
unsigned int vme_stat_reg;
/* Clear board fail, and power-up reset */
vme_stat_reg = ioread32be(&regs->lcsr.vstat);
vme_stat_reg &= ~TSI148_LCSR_VSTAT_BDFAIL;
vme_stat_reg |= TSI148_LCSR_VSTAT_CPURST;
iowrite32be(vme_stat_reg, &regs->lcsr.vstat);
/* Store the chip base address */
chip = regs;
}
/*
* tsi148.h - Low level support for the Tundra TSI148 PCI-VME Bridge Chip
*
* Copyright (c) 2009 Sebastien Dugue
*
* 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.
*
* Borrowed and modified from the tsi148.h file from:
* Tom Armistead, Updated and maintained by Ajit Prem and
* copyrighted 2004 Motorola Inc.
*
*/
#ifndef _TSI148_H
#define _TSI148_H
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
#endif /* CONFIG_PROC_FS */
#include <asm-generic/iomap.h>
#include "vme_dma.h"
#include "vmebus.h"
/*
* Define the number of each resources that the Tsi148 supports.
*/
#define TSI148_NUM_OUT_WINDOWS 8 /* Number of outbound windows */
#define TSI148_NUM_IN_WINDOWS 8 /* Nuber of inbound windows */
#define TSI148_NUM_DMA_CHANNELS 2 /* Number of DMA channels */
#define TSI148_NUM_MAILBOXES 4 /* Number of mailboxes */
#define TSI148_NUM_SEMAPHORES 8 /* Number of semaphores */
#define PCI_VENDOR_ID_TUNDRA 0x10e3
#define PCI_DEVICE_ID_TUNDRA_TSI148 0x0148
/*
* VME constants for the TSI148
*/
/* Address mode */
enum tsi148_address_size {
TSI148_A16 = 0,
TSI148_A24 = 1,
TSI148_A32 = 2,
TSI148_A64 = 4,
TSI148_CRCSR = 5,
TSI148_USER1 = 8,
TSI148_USER2 = 9,
TSI148_USER3 = 10,
TSI148_USER4 = 11
};
/* VME access type definitions */
enum tsi148_user_access_type {
TSI148_USER = 0,
TSI148_SUPER
};
enum tsi148_data_access_type {
TSI148_DATA = 0,
TSI148_PROG
};
/* VME transfer mode */
enum tsi148_transfer_mode {
TSI148_SCT = 0,
TSI148_BLT,
TSI148_MBLT,
TSI148_2eVME,
TSI148_2eSST,
TSI148_2eSSTB
};
enum tsi148_data_width {
TSI148_DW_16 = 0,
TSI148_DW_32
};
/*
* TSI148 CRG registers structure.
*
* The TSI148 Combined Register Group (CRG) consists of the following
* combination of registers:
*
* 0x000 PCFS - PCI Configuration Space Registers
* 0x100 LCSR - Local Control and Status Registers
* 0x600 GCSR - Global Control and Status Registers
* 0xff4 CR/CSR - Subset of Configuration ROM / Control and Status Registers
*/
/*
* Misc structures used in the LCSR registers description
*/
/* Outbound translation registers in LCSR group */
struct tsi148_otrans {
unsigned int otsau; /* Starting address */
unsigned int otsal;
unsigned int oteau; /* Ending address */
unsigned int oteal;
unsigned int otofu; /* Translation offset */
unsigned int otofl;
unsigned int otbs; /* 2eSST broadcast select */
unsigned int otat; /* Attributes */
};
/* Inbound translation registers in LCSR group */
struct tsi148_itrans {
unsigned int itsau; /* Starting address */
unsigned int itsal;
unsigned int iteau; /* Ending address */
unsigned int iteal;
unsigned int itofu; /* Translation offset */
unsigned int itofl;
unsigned int itat; /* Attributes */
unsigned char reserved[4];
};
/* DMAC linked-list descriptor */
struct tsi148_dma_desc {
unsigned int dsau; /* Source Address */
unsigned int dsal;
unsigned int ddau; /* Destination Address */
unsigned int ddal;
unsigned int dsat; /* Source attributes */
unsigned int ddat; /* Destination attributes */
unsigned int dnlau; /* Next link address */
unsigned int dnlal;
unsigned int dcnt; /* Byte count */
unsigned int ddbs; /* 2eSST Broadcast select */
};
/* DMAC registers in LCSR group */
struct tsi148_dma {
unsigned int dctl; /* Control */
unsigned int dsta; /* Status */
unsigned int dcsau; /* Current Source address */
unsigned int dcsal;
unsigned int dcdau; /* Current Destination address */
unsigned int dcdal;
unsigned int dclau; /* Current Link address */
unsigned int dclal;
struct tsi148_dma_desc dma_desc;
unsigned char reserved[0x38];
};
/*
*
* PCFS register group - PCI configuration space
*
*/
struct tsi148_pcfs {
unsigned short veni;
unsigned short devi;
unsigned short cmmd;
unsigned short stat;
unsigned int rev_class;
unsigned char clsz;
unsigned char mlat;
unsigned char head;
unsigned char reserved0[1];
unsigned int mbarl;
unsigned int mbaru;
unsigned char reserved1[20];
unsigned short subv;
unsigned short subi;
unsigned char reserved2[4];
unsigned char capp;
unsigned char reserved3[7];
unsigned char intl;
unsigned char intp;
unsigned char mngn;
unsigned char mxla;
/*
* offset 0x40 - PCI-X Capp PCIXCD/NCAPP/PCIXCAP
* offset 0x44 - PCI-X Capp PCIXSTAT
*/
unsigned char pcix_cap_id;
unsigned char pcix_next_cap_ptr;
unsigned short pcix_command;
unsigned int pcix_status;
/*
* offset 0x48 to 0xff - Reserved
*/
unsigned char reserved4[0xb8];
};
/*
*
* LCSR registers group definition
*
* offsets are given relative to the Combined Register Group (CRG)
*
*/
struct tsi148_lcsr {
/* Outbound translations offset 0x100 */
struct tsi148_otrans otrans[TSI148_NUM_OUT_WINDOWS];
/*
* VME bus IACK registers offset 0x200
*
* Note: Even though the registers are defined as 32-bits in
* the spec, we only want to issue 8-bit IACK cycles on the bus.
*/
unsigned char viack[8 * 4];
/* RMW registers offset 0x220 */
unsigned int rmwau;
unsigned int rmwal;
unsigned int rmwen;
unsigned int rmwc;
unsigned int rmws;
/* VMEbus control offset 0x234 */
unsigned int vmctrl;
unsigned int vctrl;
unsigned int vstat;
/* PCI status offset 0x240 */
unsigned int pstat;
unsigned char pstatrsvd[0xc];
/* VME filter offset 0x250 */
unsigned int vmefl;
unsigned char vmeflrsvd[0xc];
/* VME exception offset 0x260 */
unsigned int veau;
unsigned int veal;
unsigned int veat;
unsigned char vearsvd[0x4];
/* PCI error offset 0x270 */
unsigned int edpau;
unsigned int edpal;
unsigned int edpxa;
unsigned int edpxs;
unsigned int edpat;
unsigned char edparsvd[0x7c];
/* Inbound Translations offset 0x300 */
struct tsi148_itrans itrans[TSI148_NUM_IN_WINDOWS];
/* Inbound Translation for GCSR offset 0x400 */
unsigned int gbau;
unsigned int gbal;
unsigned int gcsrat;
/* Inbound Translation for CRG offset 0x40C */
unsigned int cbau;
unsigned int cbal;
unsigned int crgat;
/* Inbound Translation for CR/CSR offset 0x418 */
unsigned int crou;
unsigned int crol;
unsigned int crat;
/* Inbound Translation for Location Monitor offset 0x424 */
unsigned int lmbau;
unsigned int lmbal;
unsigned int lmat;
/* VME bus Interrupt Control offset 0x430 */
unsigned int bcu;
unsigned int bcl;
unsigned int bpgtr;
unsigned int bpctr;
unsigned int vicr;
unsigned char vicrsvd[0x4];
/* Local Bus Interrupt Control offset 0x448 */
unsigned int inten;
unsigned int inteo;
unsigned int ints;
unsigned int intc;
unsigned int intm1;
unsigned int intm2;
unsigned char reserved[0xa0];
/* DMA Controllers offset 500 */
struct tsi148_dma dma[TSI148_NUM_DMA_CHANNELS];
};
/*
*
* GCSR registers group definition
*
* offsets are given relative to the Combined Register Group (CRG)
*
*/
struct tsi148_gcsr {
/* Header offset 0x600 */
unsigned short devi;
unsigned short veni;
/* Control offset 0x604 */
unsigned short ctrl;
unsigned char ga;
unsigned char revid;
/* Semaphores offset 0x608
*
* semaphores 3/2/1/0 are at offset 0x608
* 7/6/5/4 are at offset 0x60C
*/
unsigned char semaphore[TSI148_NUM_SEMAPHORES];
/* Mail Boxes offset 0x610 */
unsigned int mbox[TSI148_NUM_MAILBOXES];
};
/*
*
* CR/CSR registers group definition
*
* offsets are given relative to the Combined Register Group (CRG)
*
*/
struct tsi148_crcsr {
unsigned int csrbcr; /* offset 0xff4 */
unsigned int csrbsr; /* offset 0xff8 */
unsigned int cbar; /* offset 0xffc */
};
/*
*
* TSI148 CRG definition
*
*/
struct tsi148_chip {
struct tsi148_pcfs pcfs;
struct tsi148_lcsr lcsr;
struct tsi148_gcsr gcsr;
unsigned char reserved[0xFF4 - 0x61C - 4];
struct tsi148_crcsr crcsr;
};
/*
* TSI148 Register Bit Definitions
*/
/*
* PFCS Register Set
*/
/* Command/Status Registers (CRG + $004) */
#define TSI148_PCFS_CMMD_SERR (1<<8) /* SERR_L out pin ssys err */
#define TSI148_PCFS_CMMD_PERR (1<<6) /* PERR_L out pin parity */
#define TSI148_PCFS_CMMD_MSTR (1<<2) /* PCI bus master */
#define TSI148_PCFS_CMMD_MEMSP (1<<1) /* PCI mem space access */
#define TSI148_PCFS_CMMD_IOSP (1<<0) /* PCI I/O space enable */
#define TSI148_PCFS_STAT_DPE (1<<15) /* Detected Parity Error */
#define TSI148_PCFS_STAT_SIGSE (1<<14) /* Signalled System Error */
#define TSI148_PCFS_STAT_RCVMA (1<<13) /* Received Master Abort */
#define TSI148_PCFS_STAT_RCVTA (1<<12) /* Received Target Abort */
#define TSI148_PCFS_STAT_SIGTA (1<<11) /* Signalled Target Abort */
#define TSI148_PCFS_STAT_SELTIM (3<<9) /* DELSEL Timing */
#define TSI148_PCFS_STAT_DPED (1<<8) /* Data Parity Err Reported */
#define TSI148_PCFS_STAT_FAST (1<<7) /* Fast back-to-back Cap */
#define TSI148_PCFS_STAT_P66M (1<<5) /* 66 MHz Capable */
#define TSI148_PCFS_STAT_CAPL (1<<4) /* Capab List - address $34 */
/* Revision ID/Class Code Registers (CRG +$008) */
#define TSI148_PCFS_BCLAS_M (0xFF<<24) /* Class ID */
#define TSI148_PCFS_SCLAS_M (0xFF<<16) /* Sub-Class ID */
#define TSI148_PCFS_PIC_M (0xFF<<8) /* Sub-Class ID */
#define TSI148_PCFS_REVID_M (0xFF<<0) /* Rev ID */
/* Cache Line Size/ Master Latency Timer/ Header Type Registers (CRG + $00C) */
#define TSI148_PCFS_HEAD_M (0xFF<<16) /* Master Lat Timer */
#define TSI148_PCFS_MLAT_M (0xFF<<8) /* Master Lat Timer */
#define TSI148_PCFS_CLSZ_M (0xFF<<0) /* Cache Line Size */
/* Memory Base Address Lower Reg (CRG + $010) */
#define TSI148_PCFS_MBARL_BASEL_M (0xFFFFF<<12) /* Base Addr Lower Mask */
#define TSI148_PCFS_MBARL_PRE (1<<3) /* Prefetch */
#define TSI148_PCFS_MBARL_MTYPE_M (3<<1) /* Memory Type Mask */
#define TSI148_PCFS_MBARL_IOMEM (1<<0) /* I/O Space Indicator */
/* PCI-X Capabilities Register (CRG + $040) */
#define TSI148_PCFS_PCIXCAP_MOST_M (7<<4) /* Max outstanding Split Tran */
#define TSI148_PCFS_PCIXCAP_MMRBC_M (3<<2) /* Max Mem Read byte cnt */
#define TSI148_PCFS_PCIXCAP_ERO (1<<1) /* Enable Relaxed Ordering */
#define TSI148_PCFS_PCIXCAP_DPERE (1<<0) /* Data Parity Recover Enable */
/* PCI-X Status Register (CRG +$054) */
#define TSI148_PCFS_PCIXSTAT_RSCEM (1<<29) /* Recieved Split Comp Error */
#define TSI148_PCFS_PCIXSTAT_DMCRS_M (7<<26) /* max Cumulative Read Size */
#define TSI148_PCFS_PCIXSTAT_DMOST_M (7<<23) /* max outstanding Split Tr */
#define TSI148_PCFS_PCIXSTAT_DMMRC_M (3<<21) /* max mem read byte count */
#define TSI148_PCFS_PCIXSTAT_DC (1<<20) /* Device Complexity */
#define TSI148_PCFS_PCIXSTAT_USC (1<<19) /* Unexpected Split comp */
#define TSI148_PCFS_PCIXSTAT_SCD (1<<18) /* Split completion discard */
#define TSI148_PCFS_PCIXSTAT_133C (1<<17) /* 133MHz capable */
#define TSI148_PCFS_PCIXSTAT_64D (1<<16) /* 64 bit device */
#define TSI148_PCFS_PCIXSTAT_BN_M (0xFF<<8) /* Bus number */
#define TSI148_PCFS_PCIXSTAT_DN_M (0x1F<<3) /* Device number */
#define TSI148_PCFS_PCIXSTAT_FN_M (7<<0) /* Function Number */
/*
* LCSR Registers
*/
/* Outbound Translation Starting Address Lower */
#define TSI148_LCSR_OTSAL_M (0xFFFF<<16) /* Mask */
/* Outbound Translation Ending Address Lower */
#define TSI148_LCSR_OTEAL_M (0xFFFF<<16) /* Mask */
/* Outbound Translation Offset Lower */
#define TSI148_LCSR_OTOFFL_M (0xFFFF<<16) /* Mask */
/* Outbound Translation 2eSST Broadcast Select */
#define TSI148_LCSR_OTBS_M (0xFFFFF<<0) /* Mask */
/* Outbound Translation Attribute */
#define TSI148_LCSR_OTAT_EN (1<<31) /* Window Enable */
#define TSI148_LCSR_OTAT_MRPFD (1<<18) /* Prefetch Disable */
#define TSI148_LCSR_OTAT_PFS_SHIFT 16
#define TSI148_LCSR_OTAT_PFS_M (3<<16) /* Prefetch Size Mask */
#define TSI148_LCSR_OTAT_PFS_2 (0<<16) /* 2 Cache Lines P Size */
#define TSI148_LCSR_OTAT_PFS_4 (1<<16) /* 4 Cache Lines P Size */
#define TSI148_LCSR_OTAT_PFS_8 (2<<16) /* 8 Cache Lines P Size */
#define TSI148_LCSR_OTAT_PFS_16 (3<<16) /* 16 Cache Lines P Size */
#define TSI148_LCSR_OTAT_2eSSTM_SHIFT 11
#define TSI148_LCSR_OTAT_2eSSTM_M (7<<11) /* 2eSST Xfer Rate Mask */
#define TSI148_LCSR_OTAT_2eSSTM_160 (0<<11) /* 160MB/s 2eSST Xfer Rate */
#define TSI148_LCSR_OTAT_2eSSTM_267 (1<<11) /* 267MB/s 2eSST Xfer Rate */
#define TSI148_LCSR_OTAT_2eSSTM_320 (2<<11) /* 320MB/s 2eSST Xfer Rate */
#define TSI148_LCSR_OTAT_TM_SHIFT 8
#define TSI148_LCSR_OTAT_TM_M (7<<8) /* Xfer Protocol Mask */
#define TSI148_LCSR_OTAT_TM_SCT (0<<8) /* SCT Xfer Protocol */
#define TSI148_LCSR_OTAT_TM_BLT (1<<8) /* BLT Xfer Protocol */
#define TSI148_LCSR_OTAT_TM_MBLT (2<<8) /* MBLT Xfer Protocol */
#define TSI148_LCSR_OTAT_TM_2eVME (3<<8) /* 2eVME Xfer Protocol */
#define TSI148_LCSR_OTAT_TM_2eSST (4<<8) /* 2eSST Xfer Protocol */
#define TSI148_LCSR_OTAT_TM_2eSSTB (5<<8) /* 2eSST Bcast Xfer Protocol */
#define TSI148_LCSR_OTAT_DBW_SHIFT 6
#define TSI148_LCSR_OTAT_DBW_M (3<<6) /* Max Data Width */
#define TSI148_LCSR_OTAT_DBW_16 (0<<6) /* 16-bit Data Width */
#define TSI148_LCSR_OTAT_DBW_32 (1<<6) /* 32-bit Data Width */
#define TSI148_LCSR_OTAT_SUP (1<<5) /* Supervisory Access */
#define TSI148_LCSR_OTAT_PGM (1<<4) /* Program Access */
#define TSI148_LCSR_OTAT_AMODE_SHIFT 0
#define TSI148_LCSR_OTAT_AMODE_M (0xf<<0) /* Address Mode Mask */
#define TSI148_LCSR_OTAT_AMODE_A16 (0<<0) /* A16 Address Space */
#define TSI148_LCSR_OTAT_AMODE_A24 (1<<0) /* A24 Address Space */
#define TSI148_LCSR_OTAT_AMODE_A32 (2<<0) /* A32 Address Space */
#define TSI148_LCSR_OTAT_AMODE_A64 (4<<0) /* A32 Address Space */
#define TSI148_LCSR_OTAT_AMODE_CRCSR (5<<0) /* CR/CSR Address Space */
#define TSI148_LCSR_OTAT_AMODE_USER1 (8<<0) /* User1 Address Space */
#define TSI148_LCSR_OTAT_AMODE_USER2 (9<<0) /* User2 Address Space */
#define TSI148_LCSR_OTAT_AMODE_USER3 (0xa<<0) /* User3 Address Space */
#define TSI148_LCSR_OTAT_AMODE_USER4 (0xb<<0) /* User4 Address Space */
/* VME Master Control Register CRG+$234 */
#define TSI148_LCSR_VMCTRL_VSA (1<<27) /* VMEbus Stop Ack */
#define TSI148_LCSR_VMCTRL_VS (1<<26) /* VMEbus Stop */
#define TSI148_LCSR_VMCTRL_DHB (1<<25) /* Device Has Bus */
#define TSI148_LCSR_VMCTRL_DWB (1<<24) /* Device Wants Bus */
#define TSI148_LCSR_VMCTRL_RMWEN (1<<20) /* RMW Enable */
#define TSI148_LCSR_VMCTRL_A64DS (1<<16) /* A64 Data strobes */
#define TSI148_LCSR_VMCTRL_VTOFF_M (7<<12) /* VMEbus Master Time off */
#define TSI148_LCSR_VMCTRL_VTOFF_0 (0<<12) /* 0us */
#define TSI148_LCSR_VMCTRL_VTOFF_1 (1<<12) /* 1us */
#define TSI148_LCSR_VMCTRL_VTOFF_2 (2<<12) /* 2us */
#define TSI148_LCSR_VMCTRL_VTOFF_4 (3<<12) /* 4us */
#define TSI148_LCSR_VMCTRL_VTOFF_8 (4<<12) /* 8us */
#define TSI148_LCSR_VMCTRL_VTOFF_16 (5<<12) /* 16us */
#define TSI148_LCSR_VMCTRL_VTOFF_32 (6<<12) /* 32us */
#define TSI148_LCSR_VMCTRL_VTOFF_64 (7<<12) /* 64us */
#define TSI148_LCSR_VMCTRL_VTON_M (7<<8) /* VMEbus Master Time On */
#define TSI148_LCSR_VMCTRL_VTON_4 (0<<8) /* 4us */
#define TSI148_LCSR_VMCTRL_VTON_8 (1<<8) /* 8us */
#define TSI148_LCSR_VMCTRL_VTON_16 (2<<8) /* 16us */
#define TSI148_LCSR_VMCTRL_VTON_32 (3<<8) /* 32us */
#define TSI148_LCSR_VMCTRL_VTON_64 (4<<8) /* 64us */
#define TSI148_LCSR_VMCTRL_VTON_128 (5<<8) /* 128us */
#define TSI148_LCSR_VMCTRL_VTON_256 (6<<8) /* 256us */
#define TSI148_LCSR_VMCTRL_VTON_512 (7<<8) /* 512us */
#define TSI148_LCSR_VMCTRL_VREL_M (3<<3) /* Master Rel Mode Mask */
#define TSI148_LCSR_VMCTRL_VREL_T_D (0<<3) /* Time on or Done */
#define TSI148_LCSR_VMCTRL_VREL_T_R_D (1<<3) /* Time on and REQ or Done */
#define TSI148_LCSR_VMCTRL_VREL_T_B_D (2<<3) /* Time on and BCLR or Done */
#define TSI148_LCSR_VMCTRL_VREL_T_D_R (3<<3) /* Time on or Done and REQ */
#define TSI148_LCSR_VMCTRL_VFAIR (1<<2) /* VMEbus Master Fair Mode */
#define TSI148_LCSR_VMCTRL_VREQL_M (3<<0) /* VMEbus Master Req Level Mask */
/* VMEbus Control Register CRG+$238 */
#define TSI148_LCSR_VCTRL_DLT_M (0xF<<24) /* Deadlock Timer */
#define TSI148_LCSR_VCTRL_DLT_OFF (0<<24) /* Deadlock Timer Off */
#define TSI148_LCSR_VCTRL_DLT_16 (1<<24) /* 16 VCLKS */
#define TSI148_LCSR_VCTRL_DLT_32 (2<<24) /* 32 VCLKS */
#define TSI148_LCSR_VCTRL_DLT_64 (3<<24) /* 64 VCLKS */
#define TSI148_LCSR_VCTRL_DLT_128 (4<<24) /* 128 VCLKS */
#define TSI148_LCSR_VCTRL_DLT_256 (5<<24) /* 256 VCLKS */
#define TSI148_LCSR_VCTRL_DLT_512 (6<<24) /* 512 VCLKS */
#define TSI148_LCSR_VCTRL_DLT_1024 (7<<24) /* 1024 VCLKS */
#define TSI148_LCSR_VCTRL_DLT_2048 (8<<24) /* 2048 VCLKS */
#define TSI148_LCSR_VCTRL_DLT_4096 (9<<24) /* 4096 VCLKS */
#define TSI148_LCSR_VCTRL_DLT_8192 (0xa<<24) /* 8192 VCLKS */
#define TSI148_LCSR_VCTRL_DLT_16384 (0xb<<24) /* 16384 VCLKS */
#define TSI148_LCSR_VCTRL_DLT_32768 (0xc<<24) /* 32768 VCLKS */
#define TSI148_LCSR_VCTRL_NERBB (1<<20) /* No Early Release of Bus Busy */
#define TSI148_LCSR_VCTRL_SRESET (1<<17) /* System Reset */
#define TSI148_LCSR_VCTRL_LRESET (1<<16) /* Local Reset */
#define TSI148_LCSR_VCTRL_SFAILAI (1<<15) /* SYSFAIL Auto Slot ID */
#define TSI148_LCSR_VCTRL_BID_M (0x1F<<8) /* Broadcast ID Mask */
#define TSI148_LCSR_VCTRL_ATOEN (1<<7) /* Arbiter Time-out Enable */
#define TSI148_LCSR_VCTRL_ROBIN (1<<6) /* VMEbus Round Robin */
#define TSI148_LCSR_VCTRL_GTO_M (0xf<<0) /* VMEbus Global Time-out Mask */
#define TSI148_LCSR_VCTRL_GTO_8 (0<<0) /* 8 us */
#define TSI148_LCSR_VCTRL_GTO_16 (1<<0) /* 16 us */
#define TSI148_LCSR_VCTRL_GTO_32 (2<<0) /* 32 us */
#define TSI148_LCSR_VCTRL_GTO_64 (3<<0) /* 64 us */
#define TSI148_LCSR_VCTRL_GTO_128 (4<<0) /* 128 us */
#define TSI148_LCSR_VCTRL_GTO_256 (5<<0) /* 256 us */
#define TSI148_LCSR_VCTRL_GTO_512 (6<<0) /* 512 us */
#define TSI148_LCSR_VCTRL_GTO_1024 (7<<0) /* 1024 us */
#define TSI148_LCSR_VCTRL_GTO_2048 (7<<0) /* 2048 us */
/* VMEbus Status Register CRG + $23C */
#define TSI148_LCSR_VSTAT_CPURST (1<<15) /* Clear power up reset */
#define TSI148_LCSR_VSTAT_BDFAIL (1<<14) /* Board fail */
#define TSI148_LCSR_VSTAT_PURSTS (1<<12) /* Power up reset status */
#define TSI148_LCSR_VSTAT_BDFAILS (1<<11) /* Board Fail Status */
#define TSI148_LCSR_VSTAT_SYSFAILS (1<<10) /* System Fail Status */
#define TSI148_LCSR_VSTAT_ACFAILS (1<<9) /* AC fail status */
#define TSI148_LCSR_VSTAT_SCONS (1<<8) /* System Cont Status */
#define TSI148_LCSR_VSTAT_GAP (1<<5) /* Geographic Addr Parity */
#define TSI148_LCSR_VSTAT_GA_M (0x1F<<0) /* Geographic Addr Mask */
/* PCI Configuration Status Register CRG+$240 */
#define TSI148_LCSR_PCSR_SRTO (7<<24) /* */
#define TSI148_LCSR_PCSR_SRTT (1<<22) /* */
#define TSI148_LCSR_PCSR_CCTM (1<<21) /* */
#define TSI148_LCSR_PCSR_DRQ (1<<20) /* */
#define TSI148_LCSR_PCSR_DTTT (1<<19) /* */
#define TSI148_LCSR_PCSR_MRCT (1<<18) /* */
#define TSI148_LCSR_PCSR_MRC (1<<17) /* */
#define TSI148_LCSR_PCSR_SBH (1<<16) /* */
#define TSI148_LCSR_PCSR_SRTE (1<<10) /* */
#define TSI148_LCSR_PCSR_DTTE (1<<9) /* */
#define TSI148_LCSR_PCSR_MRCE (1<<8) /* */
#define TSI148_LCSR_PCSR_REQ64S (1<<6) /* Request 64 status set */
#define TSI148_LCSR_PCSR_M66ENS (1<<5) /* M66ENS 66Mhz enable */
#define TSI148_LCSR_PCSR_FRAMES (1<<4) /* Frame Status */
#define TSI148_LCSR_PCSR_IRDYS (1<<3) /* IRDY status */
#define TSI148_LCSR_PCSR_DEVSELS (1<<2) /* DEVL status */
#define TSI148_LCSR_PCSR_STOPS (1<<1) /* STOP status */
#define TSI148_LCSR_PCSR_TRDYS (1<<0) /* TRDY status */
/* VMEbus Exception Attributes Register CRG+0x268 */
#define TSI148_LCSR_VEAT_VES (1<<31)
#define TSI148_LCSR_VEAT_VEOF (1<<30)
#define TSI148_LCSR_VEAT_VESCL (1<<29)
#define TSI148_LCSR_VEAT_2EOT (1<<21)
#define TSI148_LCSR_VEAT_2EST (1<<20)
#define TSI148_LCSR_VEAT_BERR (1<<19)
#define TSI148_LCSR_VEAT_LWORD (1<<18)
#define TSI148_LCSR_VEAT_WRITE (1<<17)
#define TSI148_LCSR_VEAT_IACK (1<<16)
#define TSI148_LCSR_VEAT_DS1 (1<<15)
#define TSI148_LCSR_VEAT_DS0 (1<<14)
#define TSI148_LCSR_VEAT_AM_SHIFT 8
#define TSI148_LCSR_VEAT_AM (0x3f<<8)
#define TSI148_LCSR_VEAT_XAM (0xff<<0)
/* PCI/X Error diagnostic attributes register CRG + 0x280 */
#define TSI148_LCSR_EDPAT_EDPST (1<<31)
#define TSI148_LCSR_EDPAT_EDPOF (1<<30)
#define TSI148_LCSR_EDPAT_EDPCL (1<<29)
#define TSI148_LCSR_EDPAT_SCD (1<<17)
#define TSI148_LCSR_EDPAT_USC (1<<16)
#define TSI148_LCSR_EDPAT_SRT (1<<15)
#define TSI148_LCSR_EDPAT_SCEM (1<<14)
#define TSI148_LCSR_EDPAT_DPED (1<<13)
#define TSI148_LCSR_EDPAT_DPE (1<<12)
#define TSI148_LCSR_EDPAT_MRC (1<<11)
#define TSI148_LCSR_EDPAT_RMA (1<<10)
#define TSI148_LCSR_EDPAT_RTA (1<<9)
#define TSI148_LCSR_EDPAT_DTT (1<<8)
#define TSI148_LCSR_EDPAT_CBEA_M (0xf<<4)
#define TSI148_LCSR_EDPAT_COMM_M (0xf<<0)
/* Inbound Translation Starting Address Lower */
#define TSI148_LCSR_ITSAL6432_M (0xFFFF<<16) /* Mask */
#define TSI148_LCSR_ITSAL24_M (0x00FFF<<12) /* Mask */
#define TSI148_LCSR_ITSAL16_M (0x0000FFF<<4) /* Mask */
/* Inbound Translation Ending Address Lower */
#define TSI148_LCSR_ITEAL6432_M (0xFFFF<<16) /* Mask */
#define TSI148_LCSR_ITEAL24_M (0x00FFF<<12) /* Mask */
#define TSI148_LCSR_ITEAL16_M (0x0000FFF<<4) /* Mask */
/* Inbound Translation Offset Lower */
#define TSI148_LCSR_ITOFFL6432_M (0xFFFF<<16) /* Mask */
#define TSI148_LCSR_ITOFFL24_M (0xFFFFF<<12) /* Mask */
#define TSI148_LCSR_ITOFFL16_M (0xFFFFFFF<<4) /* Mask */
/* Inbound Translation Attribute */
#define TSI148_LCSR_ITAT_EN (1<<31) /* Window Enable */
#define TSI148_LCSR_ITAT_TH (1<<18) /* Prefetch Threshold */
#define TSI148_LCSR_ITAT_VFS_M (3<<16) /* Virtual FIFO Size Mask */
#define TSI148_LCSR_ITAT_VFS_64 (0<<16) /* 64 bytes Virtual FIFO Size */
#define TSI148_LCSR_ITAT_VFS_128 (1<<16) /* 128 bytes Virtual FIFO Sz */
#define TSI148_LCSR_ITAT_VFS_256 (2<<16) /* 256 bytes Virtual FIFO Sz */
#define TSI148_LCSR_ITAT_VFS_512 (3<<16) /* 512 bytes Virtual FIFO Sz */
#define TSI148_LCSR_ITAT_2eSSTM_M (7<<12) /* 2eSST Xfer Rate Mask */
#define TSI148_LCSR_ITAT_2eSSTM_160 (0<<12) /* 160MB/s 2eSST Xfer Rate */
#define TSI148_LCSR_ITAT_2eSSTM_267 (1<<12) /* 267MB/s 2eSST Xfer Rate */
#define TSI148_LCSR_ITAT_2eSSTM_320 (2<<12) /* 320MB/s 2eSST Xfer Rate */
#define TSI148_LCSR_ITAT_2eSSTB (1<<11) /* 2eSST Bcast Xfer Protocol */
#define TSI148_LCSR_ITAT_2eSST (1<<10) /* 2eSST Xfer Protocol */
#define TSI148_LCSR_ITAT_2eVME (1<<9) /* 2eVME Xfer Protocol */
#define TSI148_LCSR_ITAT_MBLT (1<<8) /* MBLT Xfer Protocol */
#define TSI148_LCSR_ITAT_BLT (1<<7) /* BLT Xfer Protocol */
#define TSI148_LCSR_ITAT_AS_M (7<<4) /* Address Space Mask */
#define TSI148_LCSR_ITAT_AS_A16 (0<<4) /* A16 Address Space */
#define TSI148_LCSR_ITAT_AS_A24 (1<<4) /* A24 Address Space */
#define TSI148_LCSR_ITAT_AS_A32 (2<<4) /* A32 Address Space */
#define TSI148_LCSR_ITAT_AS_A64 (4<<4) /* A64 Address Space */
#define TSI148_LCSR_ITAT_SUPR (1<<3) /* Supervisor Access */
#define TSI148_LCSR_ITAT_NPRIV (1<<2) /* Non-Priv (User) Access */
#define TSI148_LCSR_ITAT_PGM (1<<1) /* Program Access */
#define TSI148_LCSR_ITAT_DATA (1<<0) /* Data Access */
/* GCSR Base Address Lower Address CRG +$404 */
#define TSI148_LCSR_GBAL_M (0x7FFFFFF<<5) /* Mask */
/* GCSR Attribute Register CRG + $408 */
#define TSI148_LCSR_GCSRAT_EN (1<<7) /* Enable access to GCSR */
#define TSI148_LCSR_GCSRAT_AS_M (7<<4) /* Address Space Mask */
#define TSI148_LCSR_GCSRAT_AS_16 (0<<4) /* Address Space 16 */
#define TSI148_LCSR_GCSRAT_AS_24 (1<<4) /* Address Space 24 */
#define TSI148_LCSR_GCSRAT_AS_32 (2<<4) /* Address Space 32 */
#define TSI148_LCSR_GCSRAT_AS_64 (4<<4) /* Address Space 64 */
#define TSI148_LCSR_GCSRAT_SUPR (1<<3) /* Sup set -GCSR decoder */
#define TSI148_LCSR_GCSRAT_NPRIV (1<<2) /* Non-Privliged set - CGSR */
#define TSI148_LCSR_GCSRAT_PGM (1<<1) /* Program set - GCSR decoder */
#define TSI148_LCSR_GCSRAT_DATA (1<<0) /* DATA set GCSR decoder */
/* CRG Base Address Lower Address CRG + $410 */
#define TSI148_LCSR_CBAL_M (0xFFFFF<<12)
/* CRG Attribute Register CRG + $414 */
#define TSI148_LCSR_CRGAT_EN (1<<7) /* Enable PRG Access */
#define TSI148_LCSR_CRGAT_AS_M (7<<4) /* Address Space */
#define TSI148_LCSR_CRGAT_AS_16 (0<<4) /* Address Space 16 */
#define TSI148_LCSR_CRGAT_AS_24 (1<<4) /* Address Space 24 */
#define TSI148_LCSR_CRGAT_AS_32 (2<<4) /* Address Space 32 */
#define TSI148_LCSR_CRGAT_AS_64 (4<<4) /* Address Space 64 */
#define TSI148_LCSR_CRGAT_SUPR (1<<3) /* Supervisor Access */
#define TSI148_LCSR_CRGAT_NPRIV (1<<2) /* Non-Privliged(User) Access */
#define TSI148_LCSR_CRGAT_PGM (1<<1) /* Program Access */
#define TSI148_LCSR_CRGAT_DATA (1<<0) /* Data Access */
/* CR/CSR Offset Lower Register CRG + $41C */
#define TSI148_LCSR_CROL_M (0x1FFF<<19) /* Mask */
/* CR/CSR Attribute register CRG + $420 */
#define TSI148_LCSR_CRAT_EN (1<<7) /* Enable access to CR/CSR */
/* Location Monitor base address lower register CRG + $428 */
#define TSI148_LCSR_LMBAL_M (0x7FFFFFF<<5) /* Mask */
/* Location Monitor Attribute Register CRG + $42C */
#define TSI148_LCSR_LMAT_EN (1<<7) /* Enable Location Monitor */
#define TSI148_LCSR_LMAT_AS_M (7<<4) /* Address Space MASK */
#define TSI148_LCSR_LMAT_AS_16 (0<<4) /* A16 */
#define TSI148_LCSR_LMAT_AS_24 (1<<4) /* A24 */
#define TSI148_LCSR_LMAT_AS_32 (2<<4) /* A32 */
#define TSI148_LCSR_LMAT_AS_64 (4<<4) /* A64 */
#define TSI148_LCSR_LMAT_SUPR (1<<3) /* Supervisor Access */
#define TSI148_LCSR_LMAT_NPRIV (1<<2) /* Non-Priv (User) Access */
#define TSI148_LCSR_LMAT_PGM (1<<1) /* Program Access */
#define TSI148_LCSR_LMAT_DATA (1<<0) /* Data Access */
/* Broadcast Pulse Generator Timer Register CRG + $438 */
#define TSI148_LCSR_BPGTR_BPGT_M (0xFFFF<<0) /* Mask */
/* Broadcast Programmable Clock Timer Register CRG + $43C */
#define TSI148_LCSR_BPCTR_BPCT_M (0xFFFFFF<<0) /* Mask */
/* VMEbus Interrupt Control Register CRG + $440 */
#define TSI148_LCSR_VICR_CNTS_M (3<<30) /* Cntr Source MASK */
#define TSI148_LCSR_VICR_CNTS_DIS (0<<30) /* Cntr Disable */
#define TSI148_LCSR_VICR_CNTS_IRQ1 (1<<30) /* IRQ1 to Cntr */
#define TSI148_LCSR_VICR_CNTS_IRQ2 (2<<30) /* IRQ2 to Cntr */
#define TSI148_LCSR_VICR_EDGIS_M (3<<28) /* Edge interupt MASK */
#define TSI148_LCSR_VICR_EDGIS_DIS (0<<28) /* Edge interupt Disable */
#define TSI148_LCSR_VICR_EDGIS_IRQ1 (1<<28) /* IRQ1 to Edge */
#define TSI148_LCSR_VICR_EDGIS_IRQ2 (2<<28) /* IRQ2 to Edge */
#define TSI148_LCSR_VICR_IRQ1F_M (3<<26) /* IRQ1* Function MASK */
#define TSI148_LCSR_VICR_IRQ1F_NORM (1<<26) /* Normal */
#define TSI148_LCSR_VICR_IRQ1F_PULSE (2<<26) /* Pulse Generator */
#define TSI148_LCSR_VICR_IRQ1F_PROG (3<<26) /* Programmable Clock */
#define TSI148_LCSR_VICR_IRQ1F_1U (4<<26) /* 1.02us Clock */
#define TSI148_LCSR_VICR_IRQ2F_M (3<<24) /* IRQ2* Function MASK */
#define TSI148_LCSR_VICR_IRQ2F_NORM (1<<24) /* Normal */
#define TSI148_LCSR_VICR_IRQ2F_PULSE (2<<24) /* Pulse Generator */
#define TSI148_LCSR_VICR_IRQ2F_PROG (3<<24) /* Programmable Clock */
#define TSI148_LCSR_VICR_IRQ2F_1U (4<<24) /* 1us Clock */
#define TSI148_LCSR_VICR_BIP (1<<23) /* Broadcast Interrupt Pulse */
#define TSI148_LCSR_VICR_BIPS (1<<22) /* BIP status */
#define TSI148_LCSR_VICR_IRQC (1<<15) /* VMEbus IRQ Clear */
#define TSI148_LCSR_VICR_IRQLS (7<<12) /* VMEbus IRQ Level Status */
#define TSI148_LCSR_VICR_IRQS (1<<11) /* VMEbus IRQ Status */
#define TSI148_LCSR_VICR_IRQL_M (7<<8) /* VMEbus SW IRQ Level Mask */
#define TSI148_LCSR_VICR_IRQL_1 (1<<8) /* VMEbus SW IRQ Level 1 */
#define TSI148_LCSR_VICR_IRQL_2 (2<<8) /* VMEbus SW IRQ Level 2 */
#define TSI148_LCSR_VICR_IRQL_3 (3<<8) /* VMEbus SW IRQ Level 3 */
#define TSI148_LCSR_VICR_IRQL_4 (4<<8) /* VMEbus SW IRQ Level 4 */
#define TSI148_LCSR_VICR_IRQL_5 (5<<8) /* VMEbus SW IRQ Level 5 */
#define TSI148_LCSR_VICR_IRQL_6 (6<<8) /* VMEbus SW IRQ Level 6 */
#define TSI148_LCSR_VICR_IRQL_7 (7<<8) /* VMEbus SW IRQ Level 7 */
#define TSI148_LCSR_VICR_STID_M (0xFF<<0) /* Status/ID Mask */
/* Interrupt Enable Register CRG + $448 */
/* Interrupt Enable Out Register CRG + $44c */
/* Interrupt Status Register CRG + $450 */
/* Interrupt Clear Register CRG + $454 */
/* Note: IRQ1-7 bits are reserved in INTC */
#define TSI148_LCSR_INT_DMA_SHIFT 24
#define TSI148_LCSR_INT_DMA_M (3<<24) /* DMAC mask */
#define TSI148_LCSR_INT_DMA1 (1<<25) /* DMAC 1 */
#define TSI148_LCSR_INT_DMA0 (1<<24) /* DMAC 0 */
#define TSI148_LCSR_INT_LM_SHIFT 20
#define TSI148_LCSR_INT_LM_M (0xf<<20)
#define TSI148_LCSR_INT_LM3 (1<<23) /* Location Monitor 3 */
#define TSI148_LCSR_INT_LM2 (1<<22) /* Location Monitor 2 */
#define TSI148_LCSR_INT_LM1 (1<<21) /* Location Monitor 1 */
#define TSI148_LCSR_INT_LM0 (1<<20) /* Location Monitor 0 */
#define TSI148_LCSR_INT_MB_SHIFT 16
#define TSI148_LCSR_INT_MB_M (0xf<<16)
#define TSI148_LCSR_INT_MB3 (1<<19) /* Mail Box 3 */
#define TSI148_LCSR_INT_MB2 (1<<18) /* Mail Box 2 */
#define TSI148_LCSR_INT_MB1 (1<<17) /* Mail Box 1 */
#define TSI148_LCSR_INT_MB0 (1<<16) /* Mail Box 0 */
#define TSI148_LCSR_INT_PERR (1<<13) /* VMEbus Error */
#define TSI148_LCSR_INT_VERR (1<<12) /* VMEbus Access Time-out */
#define TSI148_LCSR_INT_VIE (1<<11) /* VMEbus IRQ Edge */
#define TSI148_LCSR_INT_IACK (1<<10) /* IACK */
#define TSI148_LCSR_INT_SYSFL (1<<9) /* System Fail */
#define TSI148_LCSR_INT_ACFL (1<<8) /* AC Fail */
#define TSI148_LCSR_INT_IRQM (0x7f<<1)
#define TSI148_LCSR_INT_IRQ7 (1<<7) /* IRQ7 */
#define TSI148_LCSR_INT_IRQ6 (1<<6) /* IRQ6 */
#define TSI148_LCSR_INT_IRQ5 (1<<5) /* IRQ5 */
#define TSI148_LCSR_INT_IRQ4 (1<<4) /* IRQ4 */
#define TSI148_LCSR_INT_IRQ3 (1<<3) /* IRQ3 */
#define TSI148_LCSR_INT_IRQ2 (1<<2) /* IRQ2 */
#define TSI148_LCSR_INT_IRQ1 (1<<1) /* IRQ1 */
/* Interrupt Map Register 1 CRG + $458 */
#define TSI148_LCSR_INTM1_DMA1M_M (3<<18) /* DMA 1 */
#define TSI148_LCSR_INTM1_DMA0M_M (3<<16) /* DMA 0 */
#define TSI148_LCSR_INTM1_LM3M_M (3<<14) /* Location Monitor 3 */
#define TSI148_LCSR_INTM1_LM2M_M (3<<12) /* Location Monitor 2 */
#define TSI148_LCSR_INTM1_LM1M_M (3<<10) /* Location Monitor 1 */
#define TSI148_LCSR_INTM1_LM0M_M (3<<8) /* Location Monitor 0 */
#define TSI148_LCSR_INTM1_MB3M_M (3<<6) /* Mail Box 3 */
#define TSI148_LCSR_INTM1_MB2M_M (3<<4) /* Mail Box 2 */
#define TSI148_LCSR_INTM1_MB1M_M (3<<2) /* Mail Box 1 */
#define TSI148_LCSR_INTM1_MB0M_M (3<<0) /* Mail Box 0 */
/* Interrupt Map Register 2 CRG + $45C */
#define TSI148_LCSR_INTM2_PERRM_M (3<<26) /* PCI Bus Error */
#define TSI148_LCSR_INTM2_VERRM_M (3<<24) /* VMEbus Error */
#define TSI148_LCSR_INTM2_VIEM_M (3<<22) /* VMEbus IRQ Edge */
#define TSI148_LCSR_INTM2_IACKM_M (3<<20) /* IACK */
#define TSI148_LCSR_INTM2_SYSFLM_M (3<<18) /* System Fail */
#define TSI148_LCSR_INTM2_ACFLM_M (3<<16) /* AC Fail */
#define TSI148_LCSR_INTM2_IRQ7M_M (3<<14) /* IRQ7 */
#define TSI148_LCSR_INTM2_IRQ6M_M (3<<12) /* IRQ6 */
#define TSI148_LCSR_INTM2_IRQ5M_M (3<<10) /* IRQ5 */
#define TSI148_LCSR_INTM2_IRQ4M_M (3<<8) /* IRQ4 */
#define TSI148_LCSR_INTM2_IRQ3M_M (3<<6) /* IRQ3 */
#define TSI148_LCSR_INTM2_IRQ2M_M (3<<4) /* IRQ2 */
#define TSI148_LCSR_INTM2_IRQ1M_M (3<<2) /* IRQ1 */
/* DMA Control (0-1) Registers CRG + $500 */
#define TSI148_LCSR_DCTL_ABT (1<<27) /* Abort */
#define TSI148_LCSR_DCTL_PAU (1<<26) /* Pause */
#define TSI148_LCSR_DCTL_DGO (1<<25) /* DMA Go */
#define TSI148_LCSR_DCTL_MOD (1<<23) /* Mode */
#define TSI148_LCSR_DCTL_VFAR (1<<17) /* VME Flush on Aborted Read */
#define TSI148_LCSR_DCTL_PFAR (1<<16) /* PCI Flush on Aborted Read */
#define TSI148_LCSR_DCTL_VBKS_SHIFT 12
#define TSI148_LCSR_DCTL_VBKS_M (7<<12) /* VMEbus block Size MASK */
#define TSI148_LCSR_DCTL_VBKS_32 (0<<12) /* VMEbus block Size 32 */
#define TSI148_LCSR_DCTL_VBKS_64 (1<<12) /* VMEbus block Size 64 */
#define TSI148_LCSR_DCTL_VBKS_128 (2<<12) /* VMEbus block Size 128 */
#define TSI148_LCSR_DCTL_VBKS_256 (3<<12) /* VMEbus block Size 256 */
#define TSI148_LCSR_DCTL_VBKS_512 (4<<12) /* VMEbus block Size 512 */
#define TSI148_LCSR_DCTL_VBKS_1024 (5<<12) /* VMEbus block Size 1024 */
#define TSI148_LCSR_DCTL_VBKS_2048 (6<<12) /* VMEbus block Size 2048 */
#define TSI148_LCSR_DCTL_VBKS_4096 (7<<12) /* VMEbus block Size 4096 */
#define TSI148_LCSR_DCTL_VBOT_SHIFT 8
#define TSI148_LCSR_DCTL_VBOT_M (7<<8) /* VMEbus back-off MASK */
#define TSI148_LCSR_DCTL_VBOT_0 (0<<8) /* VMEbus back-off 0us */
#define TSI148_LCSR_DCTL_VBOT_1 (1<<8) /* VMEbus back-off 1us */
#define TSI148_LCSR_DCTL_VBOT_2 (2<<8) /* VMEbus back-off 2us */
#define TSI148_LCSR_DCTL_VBOT_4 (3<<8) /* VMEbus back-off 4us */
#define TSI148_LCSR_DCTL_VBOT_8 (4<<8) /* VMEbus back-off 8us */
#define TSI148_LCSR_DCTL_VBOT_16 (5<<8) /* VMEbus back-off 16us */
#define TSI148_LCSR_DCTL_VBOT_32 (6<<8) /* VMEbus back-off 32us */
#define TSI148_LCSR_DCTL_VBOT_64 (7<<8) /* VMEbus back-off 64us */
#define TSI148_LCSR_DCTL_PBKS_SHIFT 4
#define TSI148_LCSR_DCTL_PBKS_M (7<<4) /* PCI block size MASK */
#define TSI148_LCSR_DCTL_PBKS_32 (0<<4) /* PCI block size 32 bytes */
#define TSI148_LCSR_DCTL_PBKS_64 (1<<4) /* PCI block size 64 bytes */
#define TSI148_LCSR_DCTL_PBKS_128 (2<<4) /* PCI block size 128 bytes */
#define TSI148_LCSR_DCTL_PBKS_256 (3<<4) /* PCI block size 256 bytes */
#define TSI148_LCSR_DCTL_PBKS_512 (4<<4) /* PCI block size 512 bytes */
#define TSI148_LCSR_DCTL_PBKS_1024 (5<<4) /* PCI block size 1024 bytes */
#define TSI148_LCSR_DCTL_PBKS_2048 (6<<4) /* PCI block size 2048 bytes */
#define TSI148_LCSR_DCTL_PBKS_4096 (7<<4) /* PCI block size 4096 bytes */
#define TSI148_LCSR_DCTL_PBOT_SHIFT 0
#define TSI148_LCSR_DCTL_PBOT_M (7<<0) /* PCI back off MASK */
#define TSI148_LCSR_DCTL_PBOT_0 (0<<0) /* PCI back off 0us */
#define TSI148_LCSR_DCTL_PBOT_1 (1<<0) /* PCI back off 1us */
#define TSI148_LCSR_DCTL_PBOT_2 (2<<0) /* PCI back off 2us */
#define TSI148_LCSR_DCTL_PBOT_4 (3<<0) /* PCI back off 3us */
#define TSI148_LCSR_DCTL_PBOT_8 (4<<0) /* PCI back off 4us */
#define TSI148_LCSR_DCTL_PBOT_16 (5<<0) /* PCI back off 8us */
#define TSI148_LCSR_DCTL_PBOT_32 (6<<0) /* PCI back off 16us */
#define TSI148_LCSR_DCTL_PBOT_64 (7<<0) /* PCI back off 32us */
/* DMA Status Registers (0-1) CRG + $504 */
#define TSI148_LCSR_DSTA_VBE (1<<28) /* Error */
#define TSI148_LCSR_DSTA_ABT (1<<27) /* Abort */
#define TSI148_LCSR_DSTA_PAU (1<<26) /* Pause */
#define TSI148_LCSR_DSTA_DON (1<<25) /* Done */
#define TSI148_LCSR_DSTA_BSY (1<<24) /* Busy */
#define TSI148_LCSR_DSTA_ERRS (1<<20) /* Error Source */
#define TSI148_LCSR_DSTA_ERT (3<<16) /* Error Type */
/* DMA Current Link Address Lower (0-1) */
#define TSI148_LCSR_DCLAL_M (0x3FFFFFF<<6) /* Mask */
/* DMA Source Attribute (0-1) Reg */
#define TSI148_LCSR_DSAT_TYP_SHIFT 28
#define TSI148_LCSR_DSAT_TYP_M (3<<28) /* Source Bus Type */
#define TSI148_LCSR_DSAT_TYP_PCI (0<<28) /* PCI Bus */
#define TSI148_LCSR_DSAT_TYP_VME (1<<28) /* VMEbus */
#define TSI148_LCSR_DSAT_TYP_PAT (2<<28) /* Data Pattern */
#define TSI148_LCSR_DSAT_PSZ (1<<25) /* Pattern Size */
#define TSI148_LCSR_DSAT_NIP (1<<24) /* No Increment */
#define TSI148_LCSR_DSAT_2eSSTM_SHIFT 11
#define TSI148_LCSR_DSAT_2eSSTM_M (3<<11) /* 2eSST Trans Rate Mask */
#define TSI148_LCSR_DSAT_2eSSTM_160 (0<<11) /* 160 MB/s */
#define TSI148_LCSR_DSAT_2eSSTM_267 (1<<11) /* 267 MB/s */
#define TSI148_LCSR_DSAT_2eSSTM_320 (2<<11) /* 320 MB/s */
#define TSI148_LCSR_DSAT_TM_SHIFT 8
#define TSI148_LCSR_DSAT_TM_M (7<<8) /* Bus Transfer Protocol Mask */
#define TSI148_LCSR_DSAT_TM_SCT (0<<8) /* SCT */
#define TSI148_LCSR_DSAT_TM_BLT (1<<8) /* BLT */
#define TSI148_LCSR_DSAT_TM_MBLT (2<<8) /* MBLT */
#define TSI148_LCSR_DSAT_TM_2eVME (3<<8) /* 2eVME */
#define TSI148_LCSR_DSAT_TM_2eSST (4<<8) /* 2eSST */
#define TSI148_LCSR_DSAT_TM_2eSSTB (5<<8) /* 2eSST Broadcast */
#define TSI148_LCSR_DSAT_DBW_SHIFT 6
#define TSI148_LCSR_DSAT_DBW_M (3<<6) /* Max Data Width MASK */
#define TSI148_LCSR_DSAT_DBW_16 (0<<6) /* 16 Bits */
#define TSI148_LCSR_DSAT_DBW_32 (1<<6) /* 32 Bits */
#define TSI148_LCSR_DSAT_SUP (1<<5) /* Supervisory Mode */
#define TSI148_LCSR_DSAT_PGM (1<<4) /* Program Mode */
#define TSI148_LCSR_DSAT_AMODE_SHIFT 0
#define TSI148_LCSR_DSAT_AMODE_M (0xf<<0) /* Address Space Mask */
#define TSI148_LCSR_DSAT_AMODE_16 (0<<0) /* A16 */
#define TSI148_LCSR_DSAT_AMODE_24 (1<<0) /* A24 */
#define TSI148_LCSR_DSAT_AMODE_32 (2<<0) /* A32 */
#define TSI148_LCSR_DSAT_AMODE_64 (4<<0) /* A64 */
#define TSI148_LCSR_DSAT_AMODE_CRCSR (5<<0) /* CR/CSR */
#define TSI148_LCSR_DSAT_AMODE_USER1 (8<<0) /* User1 */
#define TSI148_LCSR_DSAT_AMODE_USER2 (9<<0) /* User2 */
#define TSI148_LCSR_DSAT_AMODE_USER3 (0xa<<0) /* User3 */
#define TSI148_LCSR_DSAT_AMODE_USER4 (0xb<<0) /* User4 */
/* DMA Destination Attribute Registers (0-1) */
#define TSI148_LCSR_DDAT_TYP_SHIFT 28
#define TSI148_LCSR_DDAT_TYP_M (1<<28) /* Destination Bus Type */
#define TSI148_LCSR_DDAT_TYP_PCI (0<<28) /* Destination PCI Bus */
#define TSI148_LCSR_DDAT_TYP_VME (1<<28) /* Destination VMEbus */
#define TSI148_LCSR_DDAT_2eSSTM_M (3<<11) /* 2eSST Transfer Rate Mask */
#define TSI148_LCSR_DDAT_2eSSTM_160 (0<<11) /* 160 MB/s */
#define TSI148_LCSR_DDAT_2eSSTM_267 (1<<11) /* 267 MB/s */
#define TSI148_LCSR_DDAT_2eSSTM_320 (2<<11) /* 320 MB/s */
#define TSI148_LCSR_DDAT_TM_M (7<<8) /* Bus Transfer Protocol Mask */
#define TSI148_LCSR_DDAT_TM_SCT (0<<8) /* SCT */
#define TSI148_LCSR_DDAT_TM_BLT (1<<8) /* BLT */
#define TSI148_LCSR_DDAT_TM_MBLT (2<<8) /* MBLT */
#define TSI148_LCSR_DDAT_TM_2eVME (3<<8) /* 2eVME */
#define TSI148_LCSR_DDAT_TM_2eSST (4<<8) /* 2eSST */
#define TSI148_LCSR_DDAT_TM_2eSSTB (5<<8) /* 2eSST Broadcast */
#define TSI148_LCSR_DDAT_DBW_M (3<<6) /* Max Data Width MASK */
#define TSI148_LCSR_DDAT_DBW_16 (0<<6) /* 16 Bits */
#define TSI148_LCSR_DDAT_DBW_32 (1<<6) /* 32 Bits */
#define TSI148_LCSR_DDAT_SUP (1<<5) /* Supervisory/User Access */
#define TSI148_LCSR_DDAT_PGM (1<<4) /* Program/Data Access */
#define TSI148_LCSR_DDAT_AMODE_M (0xf<<0) /* Address Space Mask */
#define TSI148_LCSR_DDAT_AMODE_16 (0<<0) /* A16 */
#define TSI148_LCSR_DDAT_AMODE_24 (1<<0) /* A24 */
#define TSI148_LCSR_DDAT_AMODE_32 (2<<0) /* A32 */
#define TSI148_LCSR_DDAT_AMODE_64 (4<<0) /* A64 */
#define TSI148_LCSR_DDAT_AMODE_CRCSR (5<<0) /* CRC/SR */
#define TSI148_LCSR_DDAT_AMODE_USER1 (8<<0) /* User1 */
#define TSI148_LCSR_DDAT_AMODE_USER2 (9<<0) /* User2 */
#define TSI148_LCSR_DDAT_AMODE_USER3 (0xa<<0) /* User3 */
#define TSI148_LCSR_DDAT_AMODE_USER4 (0xb<<0) /* User4 */
/* DMA Next Link Address Lower */
#define TSI148_LCSR_DNLAL_DNLAL_M (0x1FFFFFFF<<3) /* Address Mask */
#define TSI148_LCSR_DNLAL_LLA (1<<0) /* Last Link Address Indicator */
/* DMA 2eSST Broadcast Select */
#define TSI148_LCSR_DBS_M (0x1FFFFF<<0) /* Mask */
/*
* GCSR Register Group
*/
/* GCSR Control and Status Register CRG + $604 */
#define TSI148_GCSR_GCTRL_LRST (1<<15) /* Local Reset */
#define TSI148_GCSR_GCTRL_SFAILEN (1<<14) /* System Fail enable */
#define TSI148_GCSR_GCTRL_BDFAILS (1<<13) /* Board Fail Status */
#define TSI148_GCSR_GCTRL_SCON (1<<12) /* System Copntroller */
#define TSI148_GCSR_GCTRL_MEN (1<<11) /* Module Enable (READY) */
#define TSI148_GCSR_GCTRL_LMI3S (1<<7) /* Loc Monitor 3 Int Status */
#define TSI148_GCSR_GCTRL_LMI2S (1<<6) /* Loc Monitor 2 Int Status */
#define TSI148_GCSR_GCTRL_LMI1S (1<<5) /* Loc Monitor 1 Int Status */
#define TSI148_GCSR_GCTRL_LMI0S (1<<4) /* Loc Monitor 0 Int Status */
#define TSI148_GCSR_GCTRL_MBI3S (1<<3) /* Mail box 3 Int Status */
#define TSI148_GCSR_GCTRL_MBI2S (1<<2) /* Mail box 2 Int Status */
#define TSI148_GCSR_GCTRL_MBI1S (1<<1) /* Mail box 1 Int Status */
#define TSI148_GCSR_GCTRL_MBI0S (1<<0) /* Mail box 0 Int Status */
#define TSI148_GCSR_GAP (1<<5) /* Geographic Addr Parity */
#define TSI148_GCSR_GA_M (0x1F<<0) /* Geographic Address Mask */
/*
* CR/CSR Register Group
*/
/* CR/CSR Bit Clear Register CRG + $FF4 */
#define TSI148_CRCSR_CSRBCR_LRSTC (1<<7) /* Local Reset Clear */
#define TSI148_CRCSR_CSRBCR_SFAILC (1<<6) /* System Fail Enable Clear */
#define TSI148_CRCSR_CSRBCR_BDFAILS (1<<5) /* Board Fail Status */
#define TSI148_CRCSR_CSRBCR_MENC (1<<4) /* Module Enable Clear */
#define TSI148_CRCSR_CSRBCR_BERRSC (1<<3) /* Bus Error Status Clear */
/* CR/CSR Bit Set Register CRG+$FF8 */
#define TSI148_CRCSR_CSRBSR_LRSTS (1<<7) /* Local Reset Set */
#define TSI148_CRCSR_CSRBSR_SFAILS (1<<6) /* System Fail Enable Set */
#define TSI148_CRCSR_CSRBSR_BDFAILS (1<<5) /* Board Fail Status */
#define TSI148_CRCSR_CSRBSR_MENS (1<<4) /* Module Enable Set */
#define TSI148_CRCSR_CSRBSR_BERRS (1<<3) /* Bus Error Status Set */
/* CR/CSR Base Address Register CRG + FFC */
#define TSI148_CRCSR_CBAR_M (0x1F<<3) /* Mask */
/* Low level misc inline stuff */
static inline int tsi148_get_slotnum(struct tsi148_chip *regs)
{
return ioread32be(&regs->lcsr.vstat) & 0x1f;
}
static inline int tsi148_get_syscon(struct tsi148_chip *regs)
{
int syscon = 0;
if (ioread32be(&regs->lcsr.vstat) & 0x100)
syscon = 1;
return syscon;
}
static inline int tsi148_set_interrupts(struct tsi148_chip *regs,
unsigned int mask)
{
iowrite32be(mask, &regs->lcsr.inteo);
iowrite32be(mask, &regs->lcsr.inten);
/* Quick sanity check */
if ((ioread32be(&regs->lcsr.inteo) != mask) ||
(ioread32be(&regs->lcsr.inten) != mask))
return -1;
return 0;
}
static inline unsigned int tsi148_get_int_enabled(struct tsi148_chip *regs)
{
return ioread32be(&regs->lcsr.inteo);
}
static inline unsigned int tsi148_get_int_status(struct tsi148_chip *regs)
{
return ioread32be(&regs->lcsr.ints);
}
static inline void tsi148_clear_int(struct tsi148_chip *regs, unsigned int mask)
{
iowrite32be(mask, &regs->lcsr.intc);
}
static inline int tsi148_iack8(struct tsi148_chip *regs, int irq)
{
int vec = ioread8(&regs->lcsr.viack[(irq * 4) + 3]);
return vec & 0xff;
}
static inline int tsi148_bus_error_chk(struct tsi148_chip *regs, int clear)
{
unsigned int veat = ioread32be(&regs->lcsr.veat);
if (veat & TSI148_LCSR_VEAT_VES) {
if (clear)
iowrite32be(TSI148_LCSR_VEAT_VESCL, &regs->lcsr.veat);
return 1;
}
return 0;
}
extern void tsi148_handle_pci_error(void);
extern void tsi148_handle_vme_error(struct vme_bus_error *);
extern int tsi148_generate_interrupt(int, int, signed long);
extern int tsi148_dma_get_status(struct dma_channel *);
extern int tsi148_dma_busy(struct dma_channel *);
extern int tsi148_dma_done(struct dma_channel *);
extern int tsi148_dma_setup(struct dma_channel *);
extern void tsi148_dma_start(struct dma_channel *);
extern void tsi148_dma_abort(struct dma_channel *);
extern void tsi148_dma_release(struct dma_channel *);
extern void tsi148_dma_exit(void);
extern int tsi148_dma_init(void);
extern void tsi148_get_window_attr(struct vme_mapping *);
extern int tsi148_create_window(struct vme_mapping *);
extern void tsi148_remove_window(struct vme_mapping *);
extern int tsi148_setup_crg(unsigned int, enum vme_address_modifier);
extern void tsi148_disable_crg(struct tsi148_chip *);
#ifdef CONFIG_PROC_FS
extern void tsi148_procfs_register(struct proc_dir_entry *);
extern void tsi148_procfs_unregister(struct proc_dir_entry *);
#endif /* CONFIG_PROC_FS */
extern void tsi148_quiesce(struct tsi148_chip *);
extern void tsi148_init(struct tsi148_chip *);
#endif /* _TSI148_H */
/*
* vme_bridge.c - PCI-VME bridge driver
*
* Copyright (c) 2009 Sebastien Dugue
*
* 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 file provides the PCI-VME bridge support. It is highly coupled to
* the Tundra TSI148 bridge chip capabilities.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/pci.h>
#include "vme_bridge.h"
static char version[] =
"PCI-VME bridge: V" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")";
/* Module parameters */
static int vme_irq;
static int vme_slotnum = -1;
struct vme_bridge_device *vme_bridge;
/* CRG mapping on the VME bus - Maps to the top of the A24 address space */
#define A24D32_WINDOW 7
#define A24D32_AM VME_A24_USER_DATA_SCT
#define A24D32_DBW VME_D32
#define A24D32_VME_SIZE 0x1000000
struct vme_mapping a24d32_desc;
#define CRG_WINDOW 7
#define CRG_AM VME_A24_USER_DATA_SCT
#define CRG_DBW VME_D32
#define CRG_VME_BASE 0x00fff000
#define CRG_VME_SIZE 0x1000
struct vme_mapping crg_desc;
void *crg_base;
static unsigned int dma_ok;
static struct class *vme_class;
static struct pci_device_id tsi148_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_TUNDRA, PCI_DEVICE_ID_TUNDRA_TSI148) },
{ 0, },
};
static int vme_bridge_init(struct pci_dev *pdev,
const struct pci_device_id *id);
static void vme_bridge_remove(struct pci_dev *pdev);
static struct pci_driver vme_bridge_driver = {
.name = "vme_bridge",
.id_table = tsi148_ids,
.probe = vme_bridge_init,
.remove = vme_bridge_remove,
};
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *vme_root;
/*
* Procfs stuff
*/
static int vme_info_proc_show(struct seq_file *m, void *data)
{
seq_printf(m, "PCI-VME driver v%s (%s)\n\n",
DRV_MODULE_VERSION, DRV_MODULE_RELDATE);
seq_printf(m, "chip revision: %08X\n", vme_bridge->rev);
seq_printf(m, "chip IRQ: %d\n", vme_bridge->irq);
seq_printf(m, "VME slot: %d\n", vme_bridge->slot);
seq_printf(m, "VME SysCon: %s\n",
vme_bridge->syscon ? "Yes" : "No");
seq_printf(m, "chip registers at: 0x%p\n", vme_bridge->regs);
seq_printf(m, "\n");
return 0;
}
static int vme_info_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, vme_info_proc_show, NULL);
}
static const struct file_operations vme_info_proc_ops = {
.open = vme_info_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
void vme_procfs_register(void)
{
struct proc_dir_entry *entry;
/* Create /proc/vme directory */
vme_root = proc_mkdir("vme", NULL);
/* Create /proc/vme/info file */
entry = proc_create("info", S_IFREG | S_IRUGO, vme_root, &vme_info_proc_ops);
if (!entry)
printk(KERN_WARNING PFX "Failed to create proc info node\n");
/* Create /proc/vme/windows file */
entry = proc_create("windows", S_IFREG | S_IRUGO, vme_root, &vme_window_proc_ops);
if (!entry)
printk(KERN_WARNING PFX "Failed to create proc windows node\n");
/* Create /proc/vme/interrupts file */
entry = proc_create("interrupts", S_IFREG | S_IRUGO, vme_root, &vme_interrupts_proc_ops);
if (!entry)
printk(KERN_WARNING PFX
"Failed to create proc interrupts node\n");
/* Create /proc/vme/irq file */
entry = proc_create("irq", S_IFREG | S_IRUGO, vme_root, &vme_irq_proc_ops);
if (!entry)
printk(KERN_WARNING PFX "Failed to create proc irq node\n");
/* Create specific TSI148 proc entries */
tsi148_procfs_register(vme_root);
}
void vme_procfs_unregister(void)
{
tsi148_procfs_unregister(vme_root);
remove_proc_entry("irq", vme_root);
remove_proc_entry("interrupts", vme_root);
remove_proc_entry("windows", vme_root);
remove_proc_entry("info", vme_root);
remove_proc_entry("vme", NULL);
}
#else /* !CONFIG_PROC_FS */
void vme_procfs_register(void) {}
void vme_procfs_unregister(void) {}
#endif /* CONFIG_PROC_FS */
/*
* Driver file operation entry points
*/
static int vme_open(struct inode *, struct file *);
static int vme_release(struct inode *, struct file *);
static ssize_t vme_read(struct file *, char *, size_t, loff_t *);
static ssize_t vme_write(struct file *, const char *, size_t, loff_t *);
static long vme_ioctl(struct file *, unsigned int, unsigned long);
static int vme_mmap(struct file *, struct vm_area_struct *);
static struct file_operations vme_fops = {
.owner = THIS_MODULE,
.open = vme_open,
.release = vme_release,
.read = vme_read,
.write = vme_write,
.unlocked_ioctl = vme_ioctl,
.mmap = vme_mmap,
};
static struct file_operations vme_mwindow_fops = {
.owner = THIS_MODULE,
.release = vme_window_release,
.unlocked_ioctl = vme_window_ioctl,
.mmap = vme_window_mmap,
};
static struct file_operations vme_dma_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = vme_dma_ioctl,
};
static struct file_operations vme_misc_fops = {
.owner = THIS_MODULE,
.read = vme_misc_read,
.write = vme_misc_write,
.unlocked_ioctl = vme_misc_ioctl,
};
const struct dev_entry devlist[] = {
{VME_MINOR_MWINDOW, "vme_mwindow", 0, &vme_mwindow_fops},
{VME_MINOR_DMA, "vme_dma", 0, &vme_dma_fops},
{VME_MINOR_CTL, "vme_ctl", 0, &vme_misc_fops},
{VME_MINOR_REGS, "vme_regs", DEV_RW, &vme_misc_fops}
};
static int vme_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct file_operations *f_op = NULL;
int rc = 0;
switch (minor) {
case VME_MINOR_MWINDOW:
f_op = &vme_mwindow_fops;
break;
case VME_MINOR_DMA:
if (!dma_ok)
return -ENODEV;
f_op = &vme_dma_fops;
break;
case VME_MINOR_CTL:
case VME_MINOR_REGS:
f_op = &vme_misc_fops;
break;
default:
return -ENXIO;
}
if (f_op && f_op->open)
rc = f_op->open(inode, file);
return rc;
}
static int vme_release(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct file_operations *f_op = NULL;
switch (minor) {
case VME_MINOR_MWINDOW:
f_op = &vme_mwindow_fops;
break;
case VME_MINOR_DMA:
f_op = &vme_dma_fops;
break;
case VME_MINOR_CTL:
case VME_MINOR_REGS:
f_op = &vme_misc_fops;
break;
default:
return -ENXIO;
}
if (f_op && f_op->release)
return f_op->release(inode, file);
return 0;
}
static ssize_t vme_read(struct file *file, char *buf, size_t count,
loff_t *ppos)
{
unsigned int minor = iminor(file->f_dentry->d_inode);
struct file_operations *f_op = NULL;
/*
* Do some sanity checking before handling processing to the
* specific device.
*/
if (!(devlist[minor].flags & DEV_RW))
return -ENODEV;
switch (minor) {
case VME_MINOR_REGS:
f_op = &vme_misc_fops;
break;
default:
return -ENXIO;
}
if (f_op && f_op->read)
return f_op->read(file, buf, count, ppos);
return 0;
}
static ssize_t vme_write(struct file *file, const char *buf, size_t count,
loff_t *ppos)
{
unsigned int minor = iminor(file->f_dentry->d_inode);
struct file_operations *f_op = NULL;
/*
* Do some sanity checking before handling processing to the
* specific device.
*/
if (!(devlist[minor].flags & DEV_RW))
return -ENODEV;
switch (minor) {
case VME_MINOR_REGS:
f_op = &vme_misc_fops;
break;
default:
return -ENXIO;
}
if (f_op && f_op->write)
return f_op->write(file, buf, count, ppos);
return 0;
}
long vme_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
unsigned int minor = iminor(file->f_dentry->d_inode);
struct file_operations *f_op = NULL;
switch(minor) {
case VME_MINOR_MWINDOW:
f_op = &vme_mwindow_fops;
break;
case VME_MINOR_DMA:
f_op = &vme_dma_fops;
break;
default:
return -ENXIO;
}
if (f_op && f_op->unlocked_ioctl)
return f_op->unlocked_ioctl(file, cmd, arg);
return 0;
}
static int vme_mmap(struct file *file, struct vm_area_struct *vma)
{
unsigned int minor = iminor(file->f_dentry->d_inode);
struct file_operations *f_op = NULL;
switch(minor) {
case VME_MINOR_MWINDOW:
f_op = &vme_mwindow_fops;
break;
default:
return -ENXIO;
}
if (f_op && f_op->mmap)
return f_op->mmap(file, vma);
return 0;
}
/**
* vme_bridge_create_devices() - Create the device nodes for the bridge
*
* Returns 0 on success, or a standard kernel error.
*/
static int vme_bridge_create_devices(void)
{
int i;
int rc;
vme_class = class_create(THIS_MODULE, "vme");
if (IS_ERR(vme_class)) {
rc = PTR_ERR(vme_class);
printk(KERN_ERR PFX "Failed to create vme class\n");
return rc;
}
if (register_chrdev(VME_MAJOR, "vme", &vme_fops)) {
printk(KERN_ERR PFX "Failed to register device\n");
class_destroy(vme_class);
return -ENODEV;
}
for (i = 0; i < ARRAY_SIZE(devlist); i++) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
device_create(vme_class, NULL,
MKDEV(VME_MAJOR, devlist[i].minor),
devlist[i].name);
#else
device_create(vme_class, NULL,
MKDEV(VME_MAJOR, devlist[i].minor), "%s",
devlist[i].name);
#endif /* 2.6.28 */
}
return 0;
}
/**
* vme_bridge_remove_devices() - Remove the device nodes for the bridge
*
*/
static void vme_bridge_remove_devices(void)
{
int i;
unregister_chrdev(VME_MAJOR, "vme");
for (i = 0; i < ARRAY_SIZE(devlist); i++)
device_destroy(vme_class, MKDEV (VME_MAJOR, devlist[i].minor));
class_destroy(vme_class);
}
/**
* vme_bridge_map_regs() - Map VME chip registers
* @pdev: PCI device to map
*
* Maps the 4k VME bridge register space.
*
* RETURNS: zero on success or -ERRNO value
*/
static int vme_bridge_map_regs(struct pci_dev *pdev)
{
unsigned int tmp;
unsigned int vid, did;
int rc = 0;
/* Check nobody is using our MMIO resource (BAR 0) */
rc = pci_request_region(pdev, 0, "VME Bridge registers");
if (rc) {
printk(KERN_ERR PFX
"Failed to allocate PCI resource for BAR 0\n");
return rc;
}
/* Map the chip registers - those are on BAR 0 */
vme_bridge->regs = (struct tsi148_chip *)pci_iomap(pdev, 0, 4096);
if (!vme_bridge->regs) {
printk(KERN_ERR PFX "Failed to map bridge register space\n");
return -ENOMEM;
}
/* Check that the mapping worked out OK */
tmp = ioread32(vme_bridge->regs);
vid = tmp & 0xffff;
did = (tmp >> 16) & 0xffff;
if ((vid != PCI_VENDOR_ID_TUNDRA) &&
(did != PCI_DEVICE_ID_TUNDRA_TSI148)) {
printk(KERN_ERR PFX
"Failed reading mapped VME bridge registers\n");
pci_iounmap(pdev, vme_bridge->regs);
return -ENODEV;
}
return 0;
}
/**
* vme_bridge_map_crg() - Map the bridge CRG address space
*
*
*/
static int vme_bridge_map_crg(void)
{
int rc;
unsigned int tmp;
unsigned int vid, did;
/* Create and A24/D32 window */
a24d32_desc.window_num = A24D32_WINDOW;
a24d32_desc.am = A24D32_AM;
a24d32_desc.read_prefetch_enabled = 0;
a24d32_desc.data_width = A24D32_DBW;
a24d32_desc.sizeu = 0;
a24d32_desc.sizel = A24D32_VME_SIZE;
a24d32_desc.vme_addru = 0;
a24d32_desc.vme_addrl = 0;
if ((rc = vme_create_window(&a24d32_desc)) != 0) {
printk(KERN_ERR PFX "Failed to create A24/D32 window\n");
return rc;
}
/* Create a VME mapping for the CRG address space */
crg_desc.am = CRG_AM;
crg_desc.read_prefetch_enabled = 0;
crg_desc.data_width = CRG_DBW;
crg_desc.sizeu = 0;
crg_desc.sizel = CRG_VME_SIZE;
crg_desc.vme_addru = 0;
crg_desc.vme_addrl = CRG_VME_BASE;
if ((rc = vme_find_mapping(&crg_desc, 0)) != 0) {
printk(KERN_ERR PFX "Failed to create CRG mapping\n");
return rc;
}
/* Setup the CRG inbound address and attributes accordingly */
if ((rc = tsi148_setup_crg(CRG_VME_BASE, CRG_AM)) != 0) {
printk(KERN_ERR PFX "Failed to setup CRG\n");
goto out_destroy_window;
}
/* Check that the mapping is OK */
tmp = ioread32(crg_desc.kernel_va);
vid = tmp & 0xffff;
did = (tmp >> 16) & 0xffff;
if ((vid != PCI_VENDOR_ID_TUNDRA) &&
(did != PCI_DEVICE_ID_TUNDRA_TSI148)) {
printk(KERN_ERR PFX
"Failed reading mapped VME CRG\n");
tsi148_disable_crg(vme_bridge->regs);
rc = -ENODEV;
goto out_destroy_window;
}
crg_base = crg_desc.kernel_va;
return 0;
out_destroy_window:
if (vme_destroy_window(crg_desc.window_num) != 0)
printk(KERN_ERR PFX
"%s - Failed to destroy window\n", __func__);
return rc;
}
static int vme_bridge_unmap_crg(void)
{
tsi148_disable_crg(vme_bridge->regs);
return vme_destroy_window(crg_desc.window_num);
}
/**
* vme_bridge_init_interrupts() - Initialize the bridge interrupts
*
*/
static int vme_bridge_init_interrupts(void)
{
int rc;
unsigned int intmask;
/* Register our interrupt handler */
rc = request_irq(vme_bridge->irq, vme_bridge_interrupt, IRQF_SHARED,
"VME Bridge", vme_bridge);
if (rc) {
printk(KERN_ERR PFX "Failed to register irq %d handler\n",
vme_bridge->irq);
return rc;
}
/* Enable DMA, mailbox, VIRQ (syscon only) & LM Interrupts */
intmask = (TSI148_LCSR_INT_DMA1 | TSI148_LCSR_INT_DMA0 |
TSI148_LCSR_INT_LM3 | TSI148_LCSR_INT_LM2 |
TSI148_LCSR_INT_LM1 | TSI148_LCSR_INT_LM0 |
TSI148_LCSR_INT_MB3 | TSI148_LCSR_INT_MB2 |
TSI148_LCSR_INT_MB1 | TSI148_LCSR_INT_MB0 |
TSI148_LCSR_INT_PERR | TSI148_LCSR_INT_VERR);
if (vme_bridge->syscon)
intmask |= (TSI148_LCSR_INT_IRQ7 | TSI148_LCSR_INT_IRQ6 |
TSI148_LCSR_INT_IRQ5 | TSI148_LCSR_INT_IRQ4 |
TSI148_LCSR_INT_IRQ3 | TSI148_LCSR_INT_IRQ2 |
TSI148_LCSR_INT_IRQ1);
printk(KERN_DEBUG PFX "Enabling interrupts %08x\n", intmask);
if (vme_enable_interrupts(intmask)) {
printk(KERN_ERR PFX "Failed to enable interrupts");
free_irq(vme_bridge->irq, vme_bridge);
return -ENODEV;
}
return 0;
}
static inline void vme_bus_error_init(struct vme_verr *verr)
{
spin_lock_init(&verr->lock);
verr->desc.valid = 0;
INIT_LIST_HEAD(&verr->h_list);
}
/**
* Linux kernel's function pci_find_parent_resource has changed in commit:
* f44116ae PCI: Remove pci_find_parent_resource() use for allocation
* This commit changed functionality of this function in a way that it cannot
* be used anymore. Due to this there is a local copy of old version of the
* function.
*
* Function vme_bridge_init uses pci_find_parent_resource to find PCI bridge's
* resource with flag IORESOURCE_MEM. By not specifying range of resource, old
* version of the function returns a range of allocated region. However, new
* function expects the range of resource to contain subrange of PCI bridge.
* The problem is that PCI bridge resource mapping is not known at this point.
* Even more this is the information that we're looking for.
*/
/**
* pci_find_parent_resource - return resource region of parent bus of given region
* @dev: PCI device structure contains resources to be searched
* @res: child resource record for which parent is sought
*
* For given resource region of given device, return the resource
* region of parent bus the given region is contained in or where
* it should be allocated from.
*/
static struct resource *
pci_find_parent_resource_local(const struct pci_dev *dev, struct resource *res)
{
const struct pci_bus *bus = dev->bus;
int i;
struct resource *best = NULL, *r;
pci_bus_for_each_resource(bus, r, i) {
if (!r)
continue;
if (res->start && !(res->start >= r->start && res->end <= r->end))
continue; /* Not contained */
if ((res->flags ^ r->flags) & (IORESOURCE_IO | IORESOURCE_MEM))
continue; /* Wrong type */
if (!((res->flags ^ r->flags) & IORESOURCE_PREFETCH))
return r; /* Exact match */
/* We can't insert a non-prefetch resource inside a prefetchable parent .. */
if (r->flags & IORESOURCE_PREFETCH)
continue;
/* .. but we can put a prefetchable resource inside a non-prefetchable one */
if (!best)
best = r;
}
return best;
}
/**
* vme_bridge_init() - Initialize the device
* @dev: PCI device to register
* @id: Entry in piix_pci_tbl matching with @pdev
*
* Called from kernel PCI layer. Here we initialize the TSI148 VME bridge,
* do some sanity checks and finally allocate the driver resources.
*
* RETURNS:
* Zero on success, or -ERRNO value.
*/
static int vme_bridge_init(struct pci_dev *pdev,
const struct pci_device_id *id)
{
int rc;
int rev;
struct resource res;
struct resource *parent_rsrc;
if ((rc = pci_enable_device (pdev))) {
printk(KERN_ERR PFX "Could not enable device %s\n",
pci_name(pdev));
return rc;
}
/* Enable bus mastering */
pci_set_master(pdev);
vme_bridge = kzalloc(sizeof(struct vme_bridge_device), GFP_KERNEL);
if (vme_bridge == NULL) {
printk(KERN_ERR PFX "Could not allocate vme_bridge struct\n");
rc = -ENOMEM;
goto out_err;
}
pci_set_drvdata(pdev, vme_bridge);
vme_bridge->pdev = pdev;
/* initialise the bus error struct */
vme_bus_error_init(&vme_bridge->verr);
dev_info(&pdev->dev, "%s\n", version);
/* Get chip revision */
pci_read_config_dword(pdev, PCI_CLASS_REVISION, &rev);
vme_bridge->rev = rev & 0xff;
printk(KERN_INFO PFX "found TSI148 VME bridge rev 0x%.4x\n",
vme_bridge->rev);
/*
* Get the VME bridge interrupt number unless it has been specified
* by the user.
*/
if (vme_irq == 0) {
vme_irq = pdev->irq;
printk(KERN_INFO PFX "Using IRQ %d\n", vme_irq);
if (vme_irq == 0)
vme_irq = 0x50;
}
if ((vme_irq == 0) || (vme_irq == 0xff)) {
printk(KERN_ERR PFX "Bad IRQ number: %d\n", vme_irq);
rc = -ENODEV;
goto out_free;
}
vme_bridge->irq = vme_irq;
/* Check there is enough address space opened in the parent bridge */
memset(&res, 0, sizeof(res));
res.flags = IORESOURCE_MEM;
parent_rsrc = pci_find_parent_resource_local(pdev, &res);
if (parent_rsrc == 0) {
printk(KERN_ERR PFX
"cannot get VME parent device PCI resource\n");
rc = -ENODEV;
goto out_err;
}
if ((parent_rsrc->end - parent_rsrc->start) < 0x2000000) {
printk(KERN_ERR PFX
"Not enough PCI memory space available: %lx\n",
(unsigned long)(parent_rsrc->end - parent_rsrc->start));
rc = -ENOMEM;
goto out_err;
}
printk(KERN_DEBUG PFX "Parent bridge window size: 0x%lx\n",
(unsigned long)(parent_rsrc->end - parent_rsrc->start + 1));
/* Map the bridge register space */
if ((rc = vme_bridge_map_regs(pdev)) != 0)
goto out_err;
if ((rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(BITS_PER_LONG))) != 0) {
printk(KERN_ERR PFX "Bridge does not support 32 bit PCI DMA\n");
/* Indicate to the DMA handling code that DMA is unusable */
dma_ok = 0;
} else
dma_ok = 1;
/* Find out which slot we're in unless the user specified it */
if (vme_slotnum == -1)
vme_slotnum = tsi148_get_slotnum(vme_bridge->regs);
if (!vme_slotnum)
printk(KERN_INFO PFX "on VME slot 0, i.e. no VME64x backplane");
else if (vme_slotnum < 0 || vme_slotnum > 21)
printk(KERN_WARNING PFX "Weird VME slot %d\n", vme_slotnum);
vme_bridge->slot = vme_slotnum;
/* Get System controller status */
vme_bridge->syscon = tsi148_get_syscon(vme_bridge->regs);
printk(KERN_DEBUG PFX "Register space @ %p - VME slot: %d SysCon: %s\n",
vme_bridge->regs, vme_bridge->slot,
vme_bridge->syscon ? "yes" : "no");
/* Init the hardware */
tsi148_quiesce(vme_bridge->regs);
/* Initialize window management */
vme_window_init();
/* Initialize DMA management */
if (dma_ok && ((rc = vme_dma_init()) != 0))
dma_ok = 0;
tsi148_init(vme_bridge->regs);
/* Register char devices */
if ((rc = vme_bridge_create_devices()) != 0)
goto out_unmap_regs;
vme_procfs_register();
/* Map the CRG address space */
if ((rc = vme_bridge_map_crg()) != 0)
goto out_remove_devices;
printk(KERN_DEBUG PFX "Mapped CRG space @ %p (VME %08x)\n",
crg_base, crg_desc.vme_addrl);
/* Initialize and enable interrupts */
if ((rc = vme_bridge_init_interrupts()) != 0)
goto out_unmap_crg;
return 0;
out_unmap_crg:
vme_bridge_unmap_crg();
out_remove_devices:
vme_bridge_remove_devices();
out_unmap_regs:
pci_iounmap(pdev, vme_bridge->regs);
pci_release_region(pdev, 0);
out_free:
kfree(vme_bridge);
out_err:
pci_disable_device(pdev);
return rc;
}
/**
* vme_bridge_remove() - Cleanup for module unloading
* @dev: PCI device to unregister
*
*/
static void vme_bridge_remove(struct pci_dev *pdev)
{
/* First quiesce the bridge */
tsi148_quiesce(vme_bridge->regs);
/* Disable and free interrupts */
disable_irq(vme_bridge->irq);
free_irq(vme_bridge->irq, vme_bridge);
vme_bridge_unmap_crg();
vme_window_exit();
if (dma_ok)
vme_dma_exit();
pci_iounmap(pdev, vme_bridge->regs);
pci_release_region(pdev, 0);
pci_disable_device(pdev);
vme_procfs_unregister();
vme_bridge_remove_devices();
class_destroy(vme_class);
kfree(vme_bridge);
}
/*
* VME Bus
*
* Since there's no auto-discovery in VME, vme_driver_register already receives
* the number (n) of devices that the calling driver may control. Usually this
* information is obtained from user-space by the caller via insmod parameters.
*
* These n devices are created with id's ranging from 0 to n-1. For each of the
* devices, the .match and possibly .probe methods are called.
*
* If .match fails for a particular device, the device is removed.
*/
/* device_unregister() complains if dev.release() is missing */
static void vme_bus_release(struct device *dev)
{
}
static struct device vme_bus = {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
.bus_id = "vme",
#else
.init_name = "vme",
#endif
.release = vme_bus_release,
};
struct vme_dev {
struct device dev;
struct device *next;
unsigned int id;
};
#define to_vme_dev(x) container_of((x), struct vme_dev, dev)
static int vme_bus_match(struct device *dev, struct device_driver *driver)
{
struct vme_driver *vme_driver = to_vme_driver(driver);
if (dev->platform_data == vme_driver) {
if (!vme_driver->match ||
vme_driver->match(dev, to_vme_dev(dev)->id))
return 1;
dev->platform_data = NULL;
}
return 0;
}
static int vme_bus_probe(struct device *dev)
{
struct vme_driver *vme_driver = dev->platform_data;
if (vme_driver->probe)
return vme_driver->probe(dev, to_vme_dev(dev)->id);
return 0;
}
static int vme_bus_remove(struct device *dev)
{
struct vme_driver *vme_driver = dev->platform_data;
if (vme_driver->remove)
return vme_driver->remove(dev, to_vme_dev(dev)->id);
return 0;
}
static void vme_bus_shutdown(struct device *dev)
{
struct vme_driver *vme_driver = dev->platform_data;
if (vme_driver->shutdown)
vme_driver->shutdown(dev, to_vme_dev(dev)->id);
}
static int vme_bus_suspend(struct device *dev, pm_message_t state)
{
struct vme_driver *vme_driver = dev->platform_data;
if (vme_driver->suspend)
return vme_driver->suspend(dev, to_vme_dev(dev)->id, state);
return 0;
}
static int vme_bus_resume(struct device *dev)
{
struct vme_driver *vme_driver = dev->platform_data;
if (vme_driver->resume)
return vme_driver->resume(dev, to_vme_dev(dev)->id);
return 0;
}
static struct bus_type vme_bus_type = {
.name = "vme",
.match = vme_bus_match,
.probe = vme_bus_probe,
.remove = vme_bus_remove,
.shutdown = vme_bus_shutdown,
.suspend = vme_bus_suspend,
.resume = vme_bus_resume
};
static void vme_dev_release(struct device *dev)
{
kfree(to_vme_dev(dev));
}
void vme_unregister_driver(struct vme_driver *vme_driver)
{
struct device *dev = vme_driver->devices;
while (dev) {
struct device *tmp = to_vme_dev(dev)->next;
device_unregister(dev);
dev = tmp;
}
driver_unregister(&vme_driver->driver);
}
EXPORT_SYMBOL_GPL(vme_unregister_driver);
int vme_register_driver(struct vme_driver *vme_driver, unsigned int ndev)
{
int error;
unsigned int id;
vme_driver->driver.bus = &vme_bus_type;
vme_driver->devices = NULL;
error = driver_register(&vme_driver->driver);
if (error)
return error;
for (id = 0; id < ndev; id++) {
struct vme_dev *vme_dev;
vme_dev = kzalloc(sizeof(*vme_dev), GFP_KERNEL);
if (!vme_dev) {
error = -ENOMEM;
break;
}
vme_dev->dev.parent = &vme_bus;
vme_dev->dev.bus = &vme_bus_type;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
snprintf(vme_dev->dev.bus_id, BUS_ID_SIZE, "%s.%u",
vme_driver->driver.name, id);
#else
dev_set_name(&vme_dev->dev, "%s.%u",
vme_driver->driver.name, id);
#endif
vme_dev->dev.platform_data = vme_driver;
vme_dev->dev.release = vme_dev_release;
vme_dev->id = id;
vme_dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
vme_dev->dev.dma_mask = &vme_dev->dev.coherent_dma_mask;
error = device_register(&vme_dev->dev);
if (error) {
put_device(&vme_dev->dev);
break;
}
if (vme_dev->dev.platform_data) {
vme_dev->next = vme_driver->devices;
vme_driver->devices = &vme_dev->dev;
} else {
device_unregister(&vme_dev->dev);
}
}
if (!error && !vme_driver->devices)
error = -ENODEV;
if (error)
vme_unregister_driver(vme_driver);
return error;
}
EXPORT_SYMBOL_GPL(vme_register_driver);
static int __init vme_bridge_init_module(void)
{
int error;
error = device_register(&vme_bus);
if (error)
goto out;
error = bus_register(&vme_bus_type);
if (error)
goto bus_register_failed;
error = pci_register_driver(&vme_bridge_driver);
if (error)
goto pci_register_driver_failed;
if (!error)
goto out;
pci_register_driver_failed:
bus_unregister(&vme_bus_type);
bus_register_failed:
device_unregister(&vme_bus);
out:
return error;
}
static void __exit vme_bridge_exit_module(void)
{
pci_unregister_driver(&vme_bridge_driver);
bus_unregister(&vme_bus_type);
device_unregister(&vme_bus);
}
module_init(vme_bridge_init_module);
module_exit(vme_bridge_exit_module);
/* Module parameters */
module_param(vme_irq, int, S_IRUGO);
MODULE_PARM_DESC(vme_irq, "VME Bridge interrupt in the range 1-255");
module_param(vme_slotnum, int, S_IRUGO);
MODULE_PARM_DESC(vme_slotnum, "Slot Number in the range 1-21");
extern unsigned int vme_create_on_find_fail;
module_param(vme_create_on_find_fail, int, S_IRUGO);
MODULE_PARM_DESC(vme_create_on_find_fail, "When set, allows creating a new "
"window if a mapping cannot be found");
extern unsigned int vme_destroy_on_remove;
module_param(vme_destroy_on_remove, int, S_IRUGO);
MODULE_PARM_DESC(vme_destroy_on_remove, "When set, removing the last mapping "
"on a window also destroy the window");
unsigned int vme_report_bus_errors;
module_param(vme_report_bus_errors, int, 0644);
MODULE_PARM_DESC(vme_report_bus_errors, "When set, prints a message to the "
"kernel log whenever a VME Bus Error is detected");
MODULE_AUTHOR("Sebastien Dugue");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Tundra TSI148 PCI-VME Bridge driver");
MODULE_VERSION(DRV_MODULE_VERSION);
MODULE_VERSION(GIT_VERSION);
MODULE_DEVICE_TABLE(pci, tsi148_ids);
/*
* vme_bridge.h - PCI-VME bridge driver definitions
*
* Copyright (c) 2009 Sebastien Dugue
*
* 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 file provides the data structures and definitions internal to the
* driver.
*/
#ifndef _VME_BRIDGE_H
#define _VME_BRIDGE_H
#include <linux/device.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include "tsi148.h"
#include "vmebus.h"
#define PFX "VME Bridge: "
#define DRV_MODULE_NAME "vmebus"
#define DRV_MODULE_VERSION "1.4"
#define DRV_MODULE_RELDATE "May, 19 2010"
/*
* We just keep the last VME error caught, protecting it with a spinlock.
* A new VME bus error overwrites it.
* This is very simple yet good enough for most (sane) purposes.
* A linked list of handlers is kept for async notification of bus errors.
*/
struct vme_verr {
spinlock_t lock;
struct vme_bus_error_desc desc;
struct list_head h_list;
};
struct vme_bridge_device {
int rev; /* chip revision */
int irq; /* chip interrupt */
int slot; /* the VME slot we're in */
int syscon; /* syscon status */
struct tsi148_chip *regs;
struct pci_dev *pdev;
struct vme_verr verr;
};
/**
* struct vme_berr_handler - VME bus error handler descriptor
* @h_list: handler's list entry
* @offset: VME Bus error descriptor (Initial address + Address Modifier)
* @size: Size of the VME address range of interest
* @func: Handler function
*/
struct vme_berr_handler {
struct list_head h_list;
struct vme_bus_error error;
size_t size;
vme_berr_handler_t func;
};
extern struct vme_bridge_device *vme_bridge;
extern struct resource *vmepcimem;
extern void *crg_base;
extern unsigned int vme_report_bus_errors;
/* Use the standard VME Major */
#define VME_MAJOR 221
/*
* Define our own minor numbers
* If a device get added, do not forget to update devlist[] in vme_bridge.c
*/
#define VME_MINOR_MWINDOW 0
#define VME_MINOR_DMA 1
#define VME_MINOR_CTL 3
#define VME_MINOR_REGS 4
#define VME_MINOR_NR VME_MINOR_LM + 1
/* Devices access rules */
#define DEV_RW 1 /* Device implements read/write */
struct dev_entry {
unsigned int minor;
char *name;
unsigned int flags;
struct file_operations *fops;
};
extern const struct dev_entry devlist[];
/* Prototypes */
/* vme_irq.c */
extern void account_dma_interrupt(int);
extern irqreturn_t vme_bridge_interrupt(int, void *);
extern int vme_enable_interrupts(unsigned int);
extern int vme_disable_interrupts(unsigned int);
/* vme_window.c */
extern int vme_window_release(struct inode *, struct file *);
extern long vme_window_ioctl(struct file *, unsigned int, unsigned long);
extern int vme_window_mmap(struct file *, struct vm_area_struct *);
extern void vme_window_init(void);
extern void vme_window_exit(void);
/* vme_dma.c */
extern void handle_dma_interrupt(int);
extern long vme_dma_ioctl(struct file *, unsigned int, unsigned long);
extern int vme_dma_init(void);
extern void vme_dma_exit(void);
/* vme_misc.c */
extern ssize_t vme_misc_read(struct file *, char *, size_t, loff_t *);
extern ssize_t vme_misc_write(struct file *, const char *, size_t, loff_t *);
extern long vme_misc_ioctl(struct file *, unsigned int, unsigned long);
extern int vme_bus_error_check(int clear);
extern int vme_bus_error_check_clear(struct vme_bus_error *);
/* Procfs stuff grouped here for comodity */
#ifdef CONFIG_PROC_FS
extern const struct file_operations vme_interrupts_proc_ops;
extern const struct file_operations vme_irq_proc_ops;
extern const struct file_operations vme_window_proc_ops;
#endif
#endif /* _VME_BRIDGE_H */
/*
* vme_cesif - Emulation for the LynxOS CES driver
*
* Copyright (c) 2009 Sebastien Dugue
*
* 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 file provides emulation for the find_controller(), return_controller(),
* vme_intset() and vme_intclr() interfaces of the LynxOS CES driver.
*/
#define DEBUG
#include <linux/device.h>
#include "vmebus.h"
#include "vme_bridge.h"
/**
* find_controller() - Maps a VME address space into the PCI address space
* @vmeaddr: VME physical start address of the mapping
* @len: Window size (must be a multiple of 64k)
* @am: VME address modifier
* @offset: Offset in the mapping for read access test (Not used)
* @size: Data width
* @param: VME mapping parameters
*
* This function is an emulation of the CES driver functionality on LynxOS.
*
* The CES function interface does not give all the needed VME parameters, so
* the following choices were made and may have to be tweaked.
*
* - if read prefetch is enabled the the prefetch size is set to 2 cache lines
* - the VME address and size are automatically aligned on 64k if needed
* - the VME address is limited to 32 bits
*
* The kernel allocated mapping descriptor address (cookie) is stored in
* the pdparam_master sgmin field for use by return_controller().
*
* @return virtual address of the mapping - if success.
* @return -1 - on failure.
*/
unsigned long find_controller(unsigned long vmeaddr, unsigned long len,
unsigned long am, unsigned long offset,
unsigned long size, struct pdparam_master *param)
{
struct vme_mapping *desc;
int rc = 0;
/* Allocate our mapping descriptor */
if ((desc = kzalloc(sizeof(struct vme_mapping), GFP_KERNEL)) == NULL) {
printk(KERN_ERR PFX "%s - "
"Failed to allocate mapping descriptor\n", __func__);
return -1;
}
/* Now fill it with the parameters we got */
if (param->rdpref) {
desc->read_prefetch_enabled = 1;
desc->read_prefetch_size = VME_PREFETCH_2;
}
switch (size) {
case 2:
desc->data_width = VME_D16;
break;
case 4:
desc->data_width = VME_D32;
break;
default:
printk(KERN_ERR PFX "%s - Unsupported data width %ld\n",
__func__, size);
rc = -1;
goto out_free;
break;
}
desc->am = am;
desc->bcast_select = 0;
/*
* Note: no rounding up/down for size and address at this point,
* since that's taken care of when creating the window (if any).
*/
desc->sizel = len;
desc->sizeu = 0;
desc->vme_addrl = vmeaddr;
desc->vme_addru = 0;
/*
* Now we're all set up, just call the mapping function. We force
* window creation if no existing mapping can be found.
*/
if ((rc = vme_find_mapping(desc, 1))) {
printk(KERN_ERR PFX "%s - "
"Failed (rc is %d) to find a mapping "
"for VME addr: %.8lx Size:%8lx AM: %.2lx\n",
__func__, rc, vmeaddr, len, am);
rc = -1;
goto out_free;
}
printk(KERN_DEBUG PFX "%s - "
"Mapping found, VME addr: %.8lx "
"Size:%.8lx AM: %.2lx mapped at %p\n",
__func__, vmeaddr, len, am, desc->kernel_va);
return (unsigned long)desc->kernel_va;
out_free:
kfree(desc);
return rc;
}
EXPORT_SYMBOL_GPL(find_controller);
/**
* @brief Release a VME mapping
*
* @param logaddr - CPU logical address returned by @ref find_controller()
* @param len - size of the mapped window in bytes.
*
* This function is an emulation of the CES driver functionality on LynxOS.
*
* @return 0 - on success.
* @return -1 - if fails.
*/
unsigned long return_controller(unsigned logaddr, unsigned len)
{
struct vme_mapping *desc = find_vme_mapping_from_addr(logaddr);
int err;
if (!desc) {
printk(KERN_ERR PFX "%s - mapping not found @ 0x%x",
__func__, logaddr);
return -1;
}
err = vme_release_mapping(desc, 1);
if (!err)
return 0;
printk(KERN_ERR PFX "%s - failed to remove mapping @ 0x%x (err=%d)\n",
__func__, logaddr, err);
return -1;
}
EXPORT_SYMBOL_GPL(return_controller);
/**
* vme_intset() - Install an interrupt handler for the given vector
* @vec: Interrupt vector
* @handler: Handler function
* @arg: Handler argument
* @sav: Unused
*
* This function is an emulation of the CES driver functionality on LynxOS.
*
* Returns 0 on success, or a standard kernel error
*/
int vme_intset(int vec, int (*handler)(void *), void *arg, void *sav)
{
return vme_request_irq(vec, handler, arg, NULL);
}
EXPORT_SYMBOL_GPL(vme_intset);
/**
* vme_intclr() - Uninstall the interrupt handler for the given vector
* @vec: Interrupt vector
* @sav: Unused
*
* This function is an emulation of the CES driver functionality on LynxOS.
*
* Returns 0 on success, or a standard kernel error
*/
int vme_intclr(int vec, void *sav)
{
return vme_free_irq(vec);
}
EXPORT_SYMBOL_GPL(vme_intclr);
/*
* vme_dma.c - PCI-VME bridge DMA management
*
* Copyright (c) 2009 Sebastien Dugue
*
* 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 file provides the PCI-VME bridge DMA management code.
*/
#include <linux/pagemap.h>
#include <linux/version.h>
#include <linux/sched.h>
#include <linux/delay.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
#include <linux/semaphore.h>
#else
#include <asm/semaphore.h>
#endif
#include <asm/atomic.h>
#include "vmebus.h"
#include "vme_bridge.h"
#include "vme_dma.h"
struct dma_channel channels[TSI148_NUM_DMA_CHANNELS];
/*
* @dma_semaphore manages the common queue to access all the DMA channels.
* Once a process gets through the semaphore, it must acquire
* dma_lock mutex to atomically look for an available channel.
* The @disable flag can be set to disable any further DMA transfers.
*/
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,31)
static struct compat_semaphore dma_semaphore;
#else
static struct semaphore dma_semaphore;
#endif
static struct mutex dma_lock;
static atomic_t dma_disable;
/*
* Used for synchronizing between DMA transfer using a channel and
* module exit
*/
wait_queue_head_t channel_wait[TSI148_NUM_DMA_CHANNELS];
void handle_dma_interrupt(int channel_mask)
{
if (channel_mask & 1)
wake_up(&channels[0].wait);
if (channel_mask & 2)
wake_up(&channels[1].wait);
account_dma_interrupt(channel_mask);
}
static int sgl_fill_user_pages(struct page **pages, unsigned long uaddr,
const unsigned int nr_pages, int rw)
{
int ret;
/* Get user pages for the DMA transfer */
down_read(&current->mm->mmap_sem);
ret = get_user_pages(current, current->mm, uaddr, nr_pages, rw, 0,
pages, NULL);
up_read(&current->mm->mmap_sem);
return ret;
}
static int sgl_fill_kernel_pages(struct page **pages, unsigned long kaddr,
const unsigned int nr_pages, int rw)
{
void *addr = (void *)kaddr;
int i;
if (is_vmalloc_addr(addr)) {
for (i = 0; i < nr_pages; i++)
pages[i] = vmalloc_to_page(addr + PAGE_SIZE * i);
} else {
/* Note: this supports lowmem pages only */
if (!virt_addr_valid(kaddr))
return -EINVAL;
for (i = 0; i < nr_pages; i++)
pages[i] = virt_to_page(kaddr + PAGE_SIZE * i);
}
return nr_pages;
}
/**
* sgl_map_user_pages() - Pin user pages and put them into a scatter gather list
* @sgl: Scatter gather list to fill
* @nr_pages: Number of pages
* @uaddr: User buffer address
* @count: Length of user buffer
* @rw: Direction (0=read from userspace / 1 = write to userspace)
* @to_user: 1 - transfer is to/from a user-space buffer. 0 - kernel buffer.
*
* This function pins the pages of the userspace buffer and fill in the
* scatter gather list.
*/
static int sgl_map_user_pages(struct scatterlist *sgl,
const unsigned int nr_pages, unsigned long uaddr,
size_t length, int rw, int to_user)
{
int rc;
int i;
struct page **pages;
if ((pages = kmalloc(nr_pages * sizeof(struct page *),
GFP_KERNEL)) == NULL)
return -ENOMEM;
if (to_user) {
rc = sgl_fill_user_pages(pages, uaddr, nr_pages, rw);
if (rc >= 0 && rc < nr_pages) {
/* Some pages were pinned, release these */
for (i = 0; i < rc; i++)
page_cache_release(pages[i]);
rc = -ENOMEM;
goto out_free;
}
} else {
rc = sgl_fill_kernel_pages(pages, uaddr, nr_pages, rw);
}
if (rc < 0)
/* We completely failed to get the pages */
goto out_free;
/* Populate the scatter/gather list */
sg_init_table(sgl, nr_pages);
/* Take a shortcut here when we only have a single page transfer */
if (nr_pages > 1) {
unsigned int off = offset_in_page(uaddr);
unsigned int len = PAGE_SIZE - off;
sg_set_page (&sgl[0], pages[0], len, off);
length -= len;
for (i = 1; i < nr_pages; i++) {
sg_set_page (&sgl[i], pages[i],
(length < PAGE_SIZE) ? length : PAGE_SIZE,
0);
length -= PAGE_SIZE;
}
} else
sg_set_page (&sgl[0], pages[0], length, offset_in_page(uaddr));
out_free:
/* We do not need the pages array anymore */
kfree(pages);
return nr_pages;
}
/**
* sgl_unmap_user_pages() - Release the scatter gather list pages
* @sgl: The scatter gather list
* @nr_pages: Number of pages in the list
* @dirty: Flag indicating whether the pages should be marked dirty
* @to_user: 1 when transfer is to/from user-space (0 for to/from kernel)
*
*/
static void sgl_unmap_user_pages(struct scatterlist *sgl,
const unsigned int nr_pages, int dirty,
int to_user)
{
int i;
if (!to_user)
return;
for (i = 0; i < nr_pages; i++) {
struct page *page = sg_page(&sgl[i]);
if (dirty && !PageReserved(page))
SetPageDirty(page);
page_cache_release (page);
}
}
/**
* vme_dma_setup() - Setup a DMA transfer
* @desc: DMA channel to setup
* @to_user: 1 if the transfer is to/from a user-space buffer.
* 0 if it is to/from a kernel buffer.
*
* Setup a DMA transfer.
*
* Returns 0 on success, or a standard kernel error code on failure.
*/
static int vme_dma_setup(struct dma_channel *channel, int to_user)
{
int rc = 0;
struct vme_dma *desc = &channel->desc;
unsigned int length = desc->length;
uint64_t uaddr;
int nr_pages;
/* Create the scatter gather list */
if (desc->dir == VME_DMA_TO_DEVICE) {
uaddr = desc->src.addru;
uaddr <<= 32;
uaddr |= desc->src.addrl;
} else {
uaddr = desc->dst.addru;
uaddr <<= 32;
uaddr |= desc->dst.addrl;
}
/* Check for overflow */
if ((uaddr + length) < uaddr)
return -EINVAL;
nr_pages = ((uaddr & ~PAGE_MASK) + length + ~PAGE_MASK) >> PAGE_SHIFT;
if ((channel->sgl = kmalloc(nr_pages * sizeof(struct scatterlist),
GFP_KERNEL)) == NULL)
return -ENOMEM;
/* Map the user pages into the scatter gather list */
channel->sg_pages = sgl_map_user_pages(channel->sgl, nr_pages, uaddr,
length,
(desc->dir==VME_DMA_FROM_DEVICE),
to_user);
if (channel->sg_pages <= 0) {
rc = channel->sg_pages;
goto out_free_sgl;
}
/* Map the sg list entries onto the PCI bus */
channel->sg_mapped = pci_map_sg(vme_bridge->pdev, channel->sgl,
channel->sg_pages, desc->dir);
rc = tsi148_dma_setup(channel);
if (rc)
goto out_unmap_sgl;
return 0;
out_unmap_sgl:
pci_unmap_sg(vme_bridge->pdev, channel->sgl, channel->sg_mapped,
desc->dir);
sgl_unmap_user_pages(channel->sgl, channel->sg_pages, 0, to_user);
out_free_sgl:
kfree(channel->sgl);
return rc;
}
/**
* vme_dma_start() - Start a DMA transfer
* @channel: DMA channel to start
*
*/
static void vme_dma_start(struct dma_channel *channel)
{
/* Not much to do here */
tsi148_dma_start(channel);
}
/* This function has to be called with dma_semaphore and dma_lock held. */
static struct dma_channel *__lock_avail_channel(void)
{
struct dma_channel *channel;
int i;
for (i = 0; i < TSI148_NUM_DMA_CHANNELS; i++) {
channel = &channels[i];
if (!channel->busy) {
channel->busy = 1;
return channel;
}
}
WARN_ON_ONCE(i == TSI148_NUM_DMA_CHANNELS);
return ERR_PTR(-EDEADLK);
}
/*
* Wait in the queue of the semaphore for an available channel. Then find
* this newly available channel, and acquire it by flagging it as busy.
*/
static struct dma_channel *vme_dma_channel_acquire(void)
{
struct dma_channel *channel;
int rc;
/* do not process any requests if dma_disable is set */
if (atomic_read(&dma_disable))
return ERR_PTR(-EBUSY);
/* wait for a channel to be available */
rc = down_interruptible(&dma_semaphore);
if (rc)
return ERR_PTR(rc);
/*
* dma_disable might have been flagged while this task was
* sleeping on dma_semaphore.
*/
if (atomic_read(&dma_disable)) {
up(&dma_semaphore);
return ERR_PTR(-EBUSY);
}
/* find the available channel */
mutex_lock(&dma_lock);
channel = __lock_avail_channel();
mutex_unlock(&dma_lock);
return channel;
}
static void vme_dma_channel_release(struct dma_channel *channel)
{
/* release the channel busy flag */
mutex_lock(&dma_lock);
channel->busy = 0;
mutex_unlock(&dma_lock);
/* up the DMA semaphore to mark there's a channel available */
up(&dma_semaphore);
}
/*
* @to_user: 1 - the transfer is to/from a user-space buffer
* 0 - the transfer is to/from a kernel buffer
*/
static int __vme_do_dma(struct vme_dma *desc, int to_user)
{
int rc = 0;
struct dma_channel *channel;
/* First check the transfer length */
if (!desc->length) {
printk(KERN_ERR PFX "%s: Wrong length %d\n",
__func__, desc->length);
return -EINVAL;
}
/* Check the transfer direction validity */
if ((desc->dir != VME_DMA_FROM_DEVICE) &&
(desc->dir != VME_DMA_TO_DEVICE)) {
printk(KERN_ERR PFX "%s: Wrong direction %d\n",
__func__, desc->dir);
return -EINVAL;
}
/* Check we're within a 32-bit address space */
if (BITS_PER_LONG < 64 && (desc->src.addru || desc->dst.addru)) {
printk(KERN_ERR PFX "%s: Addresses are not 32-bit\n", __func__);
return -EINVAL;
}
/* Acquire an available channel */
channel = vme_dma_channel_acquire();
if (IS_ERR(channel))
return PTR_ERR(channel);
memcpy(&channel->desc, desc, sizeof(struct vme_dma));
/* Setup the DMA transfer */
rc = vme_dma_setup(channel, to_user);
if (rc)
goto out_release_channel;
/* Start the DMA transfer */
vme_dma_start(channel);
/* Wait for DMA completion */
rc = wait_event_interruptible(channel->wait,
!tsi148_dma_busy(channel));
/* React to user-space signals by aborting the ongoing DMA transfer */
if (rc) {
tsi148_dma_abort(channel);
/* leave some time for the bridge to clear the DMA channel */
udelay(10);
}
desc->status = tsi148_dma_get_status(channel);
/* Now do some cleanup and we're done */
tsi148_dma_release(channel);
pci_unmap_sg(vme_bridge->pdev, channel->sgl, channel->sg_mapped,
desc->dir);
sgl_unmap_user_pages(channel->sgl, channel->sg_pages, 0, to_user);
kfree(channel->sgl);
out_release_channel:
vme_dma_channel_release(channel);
/* Signal we're done in case we're in module exit */
wake_up(&channel_wait[channel->num]);
return rc;
}
/**
* vme_do_dma() - Do a DMA transfer
* @desc: DMA transfer descriptor
*
* This function first checks the validity of the user supplied DMA transfer
* parameters. It then tries to find an available DMA channel to do the
* transfer, setups that channel and starts the DMA.
*
* Returns 0 on success, or a standard kernel error code on failure.
*/
int vme_do_dma(struct vme_dma *desc)
{
return __vme_do_dma(desc, 1);
}
EXPORT_SYMBOL_GPL(vme_do_dma);
/**
* vme_do_dma_kernel() - Do a DMA transfer to/from a kernel buffer
* @desc: DMA transfer descriptor
*
* Returns 0 on success, or a standard kernel error code on failure.
*/
int vme_do_dma_kernel(struct vme_dma *desc)
{
return __vme_do_dma(desc, 0);
}
EXPORT_SYMBOL_GPL(vme_do_dma_kernel);
/**
* vme_dma_ioctl() - ioctl file method for the VME DMA device
* @file: Device file descriptor
* @cmd: ioctl number
* @arg: ioctl argument
*
* Currently the VME DMA device supports the following ioctl:
*
* VME_IOCTL_START_DMA
*/
long vme_dma_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int rc = 0;
struct vme_dma desc;
void __user *argp = (void __user *)arg;
switch (cmd) {
case VME_IOCTL_START_DMA:
/* Get the DMA transfer descriptor */
if (copy_from_user(&desc, (void *)argp, sizeof(struct vme_dma)))
return -EFAULT;
/* Do the DMA */
rc = vme_do_dma(&desc);
if (rc)
return rc;
/*
* Copy back the DMA transfer descriptor containing the DMA
* updated status.
*/
if (copy_to_user((void *)argp, &desc, sizeof(struct vme_dma)))
return -EFAULT;
break;
default:
rc = -ENOIOCTLCMD;
}
return rc;
}
/**
* vme_dma_exit() - Release DMA management resources
*
*
*/
void vme_dma_exit(void)
{
int i;
/* do not perform any further DMA operations */
atomic_set(&dma_disable, 1);
/* abort all the in flight DMA operations */
for (i = 0; i < TSI148_NUM_DMA_CHANNELS; i++) {
tsi148_dma_abort(&channels[i]);
}
/* wait until all the channels are idle */
for (i = 0; i < TSI148_NUM_DMA_CHANNELS; i++) {
down(&dma_semaphore);
up(&dma_semaphore);
}
tsi148_dma_exit();
}
/**
* vme_dma_init() - Initialize DMA management
*
*/
int vme_dma_init(void)
{
int i;
for (i = 0; i < TSI148_NUM_DMA_CHANNELS; i++) {
channels[i].num = i;
init_waitqueue_head(&channels[i].wait);
init_waitqueue_head(&channel_wait[i]);
INIT_LIST_HEAD(&channels[i].hw_desc_list);
}
sema_init(&dma_semaphore, TSI148_NUM_DMA_CHANNELS);
mutex_init(&dma_lock);
atomic_set(&dma_disable, 0);
return tsi148_dma_init();
}
/*
* vme_dma.h -
*
* Copyright (c) 2009 Sebastien Dugue
*
* 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.
*
*/
#ifndef _VME_DMA_H
#define _VME_DMA_H
#include "vmebus.h"
/**
* struct dma_channel - Internal data structure representing a DMA channel
* @busy: Busy flag
* @num: Channel number
* @desc: DMA transfer descriptor currently used
* @chained: Chained (1) / Direct (0) transfer
* @sgl: Scatter gather list of userspace pages for the transfer
* @sg_pages: Number of pages in the scatter gather list
* @sg_mapped: Number of pages mapped onto the PCI bus
* @hw_desc: List of hardware descriptors
* @wait: Wait queue for the DMA channel
*
* Note: concurrent access to all the DMA channels is protected by a
* per-bridge mutex.
*/
struct dma_channel {
unsigned int busy;
unsigned int num;
struct vme_dma desc;
int chained;
struct scatterlist *sgl;
int sg_pages;
int sg_mapped;
struct list_head hw_desc_list;
wait_queue_head_t wait;
};
/**
* struct hw_desc_entry - Hardware descriptor
* @list: Descriptors list
* @va: Virtual address of the descriptor
* @phys: Bus address of the descriptor
*
* This data structure is used internally to keep track of the hardware
* descriptors that are allocated in order to free them when the transfer
* is done.
*/
struct hw_desc_entry {
struct list_head list;
void *va;
dma_addr_t phys;
};
#endif /* _VME_DMA_H */
/*
* vme_irq.c - PCI-VME bridge interrupt management
*
* Copyright (c) 2009 Sebastien Dugue
*
* 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 file provides the PCI-VME bridge interrupt management code.
*/
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include "tsi148.h"
#include "vme_bridge.h"
unsigned int vme_interrupts_enabled;
struct vme_irq {
int (*handler)(void *arg);
void *arg;
#ifdef CONFIG_PROC_FS
int count;
char *name;
#endif
};
#define VME_NUM_VECTORS 256
/* Mutex to prevent concurrent access to the IRQ table */
static DEFINE_MUTEX(vme_irq_table_lock);
static struct vme_irq vme_irq_table[VME_NUM_VECTORS];
/* Interrupt counters */
enum interrupt_idx {
INT_DMA0 = 0,
INT_DMA1,
INT_MB0,
INT_MB1,
INT_MB2,
INT_MB3,
INT_LM0,
INT_LM1,
INT_LM2,
INT_LM3,
INT_IRQ1,
INT_IRQ2,
INT_IRQ3,
INT_IRQ4,
INT_IRQ5,
INT_IRQ6,
INT_IRQ7,
INT_PERR,
INT_VERR,
INT_SPURIOUS
};
struct interrupt_stats {
unsigned int count;
char *name;
} int_stats[] = {
{.name = "DMA0"}, {.name = "DMA1"},
{.name = "MB0"}, {.name = "MB1"}, {.name = "MB2"}, {.name = "MB3"},
{.name = "LM0"}, {.name = "LM1"}, {.name = "LM2"}, {.name = "LM3"},
{.name = "IRQ1"}, {.name = "IRQ2"}, {.name = "IRQ3"}, {.name = "IRQ4"},
{.name = "IRQ5"}, {.name = "IRQ6"}, {.name = "IRQ7"},
{.name = "PERR"}, {.name = "VERR"}, {.name = "SPURIOUS"}
};
#ifdef CONFIG_PROC_FS
static int vme_interrupts_proc_show(struct seq_file *m, void *data)
{
int i;
seq_printf(m, " Source Count\n");
seq_printf(m, "--------------------------\n\n");
for (i = 0; i < ARRAY_SIZE(int_stats); i++)
seq_printf(m, "%-8s %d\n", int_stats[i].name,
int_stats[i].count);
return 0;
}
static int vme_interrupts_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, vme_interrupts_proc_show, NULL);
}
const struct file_operations vme_interrupts_proc_ops = {
.open = vme_interrupts_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int vme_irq_proc_show(struct seq_file *m, void *data)
{
int i;
struct vme_irq *virq;
seq_printf(m, "Vector Count Client\n");
seq_printf(m, "------------------------------------------------\n\n");
for (i = 0; i < VME_NUM_VECTORS; i++) {
virq = &vme_irq_table[i];
if (virq->handler)
seq_printf(m, " %3d %10u %s\n",
i, virq->count, virq->name);
}
return 0;
}
static int vme_irq_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, vme_irq_proc_show, NULL);
}
const struct file_operations vme_irq_proc_ops = {
.open = vme_irq_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#endif /* CONFIG_PROC_FS */
void account_dma_interrupt(int channel_mask)
{
if (channel_mask & 1)
int_stats[INT_DMA0].count++;
if (channel_mask & 2)
int_stats[INT_DMA1].count++;
}
static void handle_pci_error(void)
{
tsi148_handle_pci_error();
int_stats[INT_PERR].count++;
}
static int
vme_berr_match(struct vme_bus_error *error, struct vme_berr_handler *handler)
{
struct vme_bus_error *err = &handler->error;
return error->am == err->am && error->address >= err->address &&
error->address < err->address + handler->size;
}
static void __vme_dispatch_berr(struct vme_bus_error *error)
{
struct vme_berr_handler *handler;
list_for_each_entry(handler, &vme_bridge->verr.h_list, h_list) {
if (vme_berr_match(error, handler))
handler->func(error);
}
}
static void handle_vme_error(void)
{
struct vme_bus_error_desc desc;
unsigned long flags;
spinlock_t *lock;
lock = &vme_bridge->verr.lock;
spin_lock_irqsave(lock, flags);
tsi148_handle_vme_error(&desc.error);
desc.valid = 1;
memcpy(&vme_bridge->verr.desc, &desc, sizeof(desc));
__vme_dispatch_berr(&desc.error);
spin_unlock_irqrestore(lock, flags);
int_stats[INT_VERR].count++;
}
static void handle_mbox_interrupt(int mb_mask)
{
if (mb_mask & 1)
int_stats[INT_MB0].count++;
if (mb_mask & 2)
int_stats[INT_MB1].count++;
if (mb_mask & 4)
int_stats[INT_MB2].count++;
if (mb_mask & 8)
int_stats[INT_MB3].count++;
}
static void handle_lm_interrupt(int lm_mask)
{
if (lm_mask & 1)
int_stats[INT_LM0].count++;
if (lm_mask & 2)
int_stats[INT_LM1].count++;
if (lm_mask & 4)
int_stats[INT_LM2].count++;
if (lm_mask & 8)
int_stats[INT_LM3].count++;
}
/**
* handle_vme_interrupt() - VME IRQ handler
* @irq_mask: Mask of the raised IRQs
*
* Get the IRQ vector through an IACK cycle and call the handler for
* that vector if installed.
*/
static void handle_vme_interrupt(int irq_mask)
{
int i;
int vec;
struct vme_irq *virq;
for (i = 7; i > 0; i--) {
if (irq_mask & (1 << i)) {
/* Generate an 8-bit IACK cycle and get the vector */
vec = tsi148_iack8(vme_bridge->regs, i);
virq = &vme_irq_table[vec];
if (virq->handler) {
#ifdef CONFIG_PROC_FS
virq->count++;
#endif
virq->handler(virq->arg);
}
int_stats[INT_IRQ1 + i - 1].count++;
}
}
}
/**
* vme_bridge_interrupt() - VME bridge main interrupt handler
*
*/
irqreturn_t vme_bridge_interrupt(int irq, void *ptr)
{
unsigned int raised;
unsigned int mask;
/*
* We need to read the interrupt status from the VME bus to make
* sure the internal FIFO has been flushed of pending writes.
*/
while ((raised = tsi148_get_int_status(crg_base)) != 0) {
/*
* Clearing of the interrupts must be done by writing to the
* INTS register through the VME bus.
*/
tsi148_clear_int(crg_base, raised);
mask = raised & vme_interrupts_enabled;
/* Only handle enabled interrupts */
if (!mask) {
int_stats[INT_SPURIOUS].count++;
return IRQ_NONE;
}
if (mask & TSI148_LCSR_INT_DMA_M) {
handle_dma_interrupt((mask & TSI148_LCSR_INT_DMA_M) >>
TSI148_LCSR_INT_DMA_SHIFT);
mask &= ~TSI148_LCSR_INT_DMA_M;
}
if (mask & TSI148_LCSR_INT_PERR) {
handle_pci_error();
mask &= ~TSI148_LCSR_INT_PERR;
}
if (mask & TSI148_LCSR_INT_VERR) {
handle_vme_error();
mask &= ~TSI148_LCSR_INT_VERR;
}
if (mask & TSI148_LCSR_INT_MB_M) {
handle_mbox_interrupt((mask & TSI148_LCSR_INT_MB_M) >>
TSI148_LCSR_INT_MB_SHIFT);
mask &= ~TSI148_LCSR_INT_MB_M;
}
if (mask & TSI148_LCSR_INT_LM_M) {
handle_lm_interrupt((mask & TSI148_LCSR_INT_LM_M) >>
TSI148_LCSR_INT_LM_SHIFT);
mask &= ~TSI148_LCSR_INT_LM_M;
}
if (mask & TSI148_LCSR_INT_IRQM) {
handle_vme_interrupt(mask & TSI148_LCSR_INT_IRQM);
mask &= ~TSI148_LCSR_INT_IRQM;
}
/* Check that we handled everything */
if (mask)
printk(KERN_WARNING PFX
"Unhandled interrupt %08x (enabled %08x)\n",
mask, vme_interrupts_enabled);
}
return IRQ_HANDLED;
}
/**
* vme_enable_interrupts() - Enable VME bridge interrupts
* @mask: Interrupts to enable
*
*/
int vme_enable_interrupts(unsigned int mask)
{
unsigned int enabled;
unsigned int new;
enabled = tsi148_get_int_enabled(vme_bridge->regs);
new = enabled | mask;
vme_interrupts_enabled = new;
return tsi148_set_interrupts(vme_bridge->regs, new);
}
/**
* vme_disable_interrupts() - Disable VME bridge interrupts
* @mask: Interrupts to disable
*
*/
int vme_disable_interrupts(unsigned int mask)
{
unsigned int enabled;
unsigned int new;
enabled = tsi148_get_int_enabled(vme_bridge->regs);
new = enabled & ~mask;
vme_interrupts_enabled = new;
return tsi148_set_interrupts(vme_bridge->regs, new);
}
/**
* vme_request_irq() - Install handler for a given VME IRQ vector
* @vec: VME IRQ vector
* @handler: Interrupt handler
* @arg: Interrupt handler argument
* @name: Interrupt name (only used for stats in Procfs)
*
*/
int vme_request_irq(unsigned int vec, int (*handler)(void *),
void *arg, const char *name)
{
struct vme_irq *virq;
int rc = 0;
/* Check the vector is within the bound */
if (vec >= VME_NUM_VECTORS)
return -EINVAL;
if ((rc = mutex_lock_interruptible(&vme_irq_table_lock)) != 0)
return rc;
virq = &vme_irq_table[vec];
/* Check if that vector is already used */
if (virq->handler) {
rc = -EBUSY;
goto out_unlock;
}
virq->handler = handler;
virq->arg = arg;
#ifdef CONFIG_PROC_FS
virq->count = 0;
if (name)
virq->name = (char *)name;
else
virq->name = "Unknown";
#endif
out_unlock:
mutex_unlock(&vme_irq_table_lock);
if (!rc)
printk(KERN_DEBUG PFX "Registered vector %d for %s\n",
vec, virq->name);
else
printk(KERN_WARNING PFX "Could not install ISR: vector %d "
"already in use by %s", vec, virq->name);
return rc;
}
EXPORT_SYMBOL_GPL(vme_request_irq);
/**
* vme_free_irq() - Uninstall handler for a given VME IRQ vector
* @vec: VME IRQ vector
*
*/
int vme_free_irq(unsigned int vec)
{
struct vme_irq *virq;
int rc = 0;
/* Check the vector is within the bound */
if (vec >= VME_NUM_VECTORS)
return -EINVAL;
if ((rc = mutex_lock_interruptible(&vme_irq_table_lock)) != 0)
return rc;
virq = &vme_irq_table[vec];
/* Check there really was a handler installed */
if (!virq->handler) {
rc = -EINVAL;
goto out_unlock;
}
virq->handler = NULL;
virq->arg = NULL;
#ifdef CONFIG_PROC_FS
virq->count = 0;
virq->name = NULL;
#endif
out_unlock:
mutex_unlock(&vme_irq_table_lock);
return rc;
}
EXPORT_SYMBOL_GPL(vme_free_irq);
/**
* vme_generate_interrupt() - Generate an interrupt on the VME bus
* @level: IRQ level (1-7)
* @vector: IRQ vector (0-255)
* @msecs: Timeout for IACK in milliseconds
*
* This function generates an interrupt on the VME bus and waits for IACK
* for msecs milliseconds.
*
* Returns 0 on success or -ETIME if the timeout expired.
*
*/
int vme_generate_interrupt(int level, int vector, signed long msecs)
{
return tsi148_generate_interrupt(level, vector, msecs);
}
EXPORT_SYMBOL_GPL(vme_generate_interrupt);
/*
* vme_misc.c - PCI-VME bridge miscellaneous interfaces
*
* Copyright (c) 2009 Sebastien Dugue
*
* 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 file provides some PCI-VME bridge miscellaneous interfaces:
*
* - Access to VME control (requestor, arbitrer) - Not implemented yet
* - VME Bus error checking
*/
#include "vme_bridge.h"
int vme_bus_error_check(int clear)
{
return tsi148_bus_error_chk(vme_bridge->regs, clear);
}
EXPORT_SYMBOL_GPL(vme_bus_error_check);
static inline int
__vme_bus_error_check_clear(struct vme_bus_error *err)
{
struct vme_bus_error_desc *desc = &vme_bridge->verr.desc;
struct vme_bus_error *vme_err = &desc->error;
if (desc->valid && vme_err->am == err->am &&
vme_err->address == err->address) {
desc->valid = 0;
return 1;
}
return 0;
}
/**
* vme_register_berr_handler() - register a VME Bus Error handler
* @error: Bus Error descriptor: Initial Address + Address Modifier
* @size: Size of the address range of interest. The Initial Address
* is the address provided in @error.
* @func: Handler function for the bus errors in the range above.
*
* NOTE: the handler function will be called in interrupt context.
* Return the address of the registered handler on success, ERR_PTR otherwise.
*/
struct vme_berr_handler *
vme_register_berr_handler(struct vme_bus_error *error, size_t size,
vme_berr_handler_t func)
{
spinlock_t *lock = &vme_bridge->verr.lock;
struct vme_berr_handler *handler;
unsigned long flags;
if (!size) {
printk(KERN_WARNING PFX "%s: size cannot be 0.\n", __func__);
return ERR_PTR(-EINVAL);
}
handler = kzalloc(sizeof(struct vme_berr_handler), GFP_KERNEL);
if (handler == NULL) {
printk(KERN_ERR PFX "Unable to allocate Bus Error Handler\n");
return ERR_PTR(-ENOMEM);
}
spin_lock_irqsave(lock, flags);
memcpy(&handler->error, error, sizeof(struct vme_bus_error));
handler->size = size;
handler->func = func;
list_add_tail(&handler->h_list, &vme_bridge->verr.h_list);
spin_unlock_irqrestore(lock, flags);
return handler;
}
EXPORT_SYMBOL_GPL(vme_register_berr_handler);
static void __vme_unregister_berr_handler(struct vme_berr_handler *handler)
{
struct list_head *h_pos, *temp;
struct vme_berr_handler *entry;
list_for_each_safe(h_pos, temp, &vme_bridge->verr.h_list) {
entry = list_entry(h_pos, struct vme_berr_handler, h_list);
if (entry == handler) {
list_del(h_pos);
kfree(entry);
return;
}
}
printk(KERN_WARNING PFX "%s: handler not found\n", __func__);
}
/**
* vme_unregister_berr_handler - Unregister a VME Bus Error Handler
* @handler: Address of the registered handler to be removed.
*/
void vme_unregister_berr_handler(struct vme_berr_handler *handler)
{
spinlock_t *lock = &vme_bridge->verr.lock;
unsigned long flags;
spin_lock_irqsave(lock, flags);
__vme_unregister_berr_handler(handler);
spin_unlock_irqrestore(lock, flags);
}
EXPORT_SYMBOL_GPL(vme_unregister_berr_handler);
/**
* vme_bus_error_check_clear - check and clear VME bus errors
* @bus_error: bus error to be checked
*
* Note: the bus error is only cleared if it matches the given bus
* error descriptor.
*/
int vme_bus_error_check_clear(struct vme_bus_error *err)
{
unsigned long flags;
spinlock_t *lock;
int ret;
lock = &vme_bridge->verr.lock;
spin_lock_irqsave(lock, flags);
ret = __vme_bus_error_check_clear(err);
spin_unlock_irqrestore(lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(vme_bus_error_check_clear);
ssize_t vme_misc_read(struct file *file, char *buf, size_t count,
loff_t *ppos)
{
return 0;
}
ssize_t vme_misc_write(struct file *file, const char *buf, size_t count,
loff_t *ppos)
{
return 0;
}
long vme_misc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
return -ENOIOCTLCMD;
}
/*
* vme_window.c - PCI-VME window management
*
* Copyright (c) 2009 Sebastien Dugue
*
* 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 file provides the PCI-VME bridge window management support:
*
* - Window creation and deletion
* - Mapping creation and removal
* - Procfs interface to windows and mappings information
*/
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h>
#include "vmebus.h"
#include "vme_bridge.h"
/**
* struct window - Hardware window descriptor.
* @lock: Mutex protecting the descriptor
* @active: Flag indicating whether the window is in use
* @rsrc: PCI bus resource of the window
* @desc: This physical window descriptor
* @mappings: List of mappings using this window
* @users: Number of users of this window
*
* This structure holds the information concerning hardware
* windows.
*
*/
struct window {
struct mutex lock;
unsigned int active;
struct resource rsrc;
struct vme_mapping desc;
struct list_head mappings;
int users;
};
struct window window_table[TSI148_NUM_OUT_WINDOWS];
/**
* struct vme_taskinfo - Store information about a mapping's user
*
* @pid_nr: pid number
* @name: name of the process
* @file: struct file to identify the owner of the mapping. Set it to
* NULL when the request comes from the kernel. In that case
* @pid_nr and @name won't be filled in.
*
* @note on the name length.
* As it's not only the process name that is stored here, but also
* module name -- lengths should be MAX allowed for the module name
*/
struct vme_taskinfo {
pid_t pid_nr;
char name[MODULE_NAME_LEN];
struct file *file;
};
/**
* struct mapping - Logical mapping descriptor
* @list: List of the mappings
* @mapping: The mapping descriptor
* @client: The user of this mapping
*
* This structure holds the information concerning logical mappings
* made on top a hardware windows.
*/
struct mapping {
struct list_head list;
struct vme_mapping desc;
struct vme_taskinfo client;
};
/*
* Flag controlling whether to create a new window if a mapping cannot
* be found.
*/
unsigned int vme_create_on_find_fail;
/*
* Flag controlling whether removing the last mapping on a window should
* also destroys the window.
*/
unsigned int vme_destroy_on_remove;
#ifdef CONFIG_PROC_FS
/* VME address modifiers names */
static char *amod[] = {
"A64MBLT", "A64", "Invalid 0x02", "A64BLT",
"A64LCK", "A32LCK", "Invalid 0x06", "Invalid 0x07",
"A32MBLT USER", "A32 USER DATA", "A32 USER PROG", "A32BLT USER",
"A32MBLT SUP", "A32 SUP DATA", "A32 SUP PROG", "A32BLT SUP",
"Invalid 0x10", "Invalid 0x11", "Invalid 0x12", "Invalid 0x13",
"Invalid 0x14", "Invalid 0x15", "Invalid 0x16", "Invalid 0x17",
"Invalid 0x18", "Invalid 0x19", "Invalid 0x1a", "Invalid 0x1b",
"Invalid 0x1c", "Invalid 0x1d", "Invalid 0x1e", "Invalid 0x1f",
"2e6U", "2e3U", "Invalid 0x22", "Invalid 0x23",
"Invalid 0x24", "Invalid 0x25", "Invalid 0x26", "Invalid 0x27",
"Invalid 0x28", "A16 USER", "Invalid 0x2a", "Invalid 0x2b",
"A16LCK", "A16 SUP", "Invalid 0x2e", "CR/CSR",
"Invalid 0x30", "Invalid 0x31", "Invalid 0x32", "Invalid 0x33",
"A40", "A40LCK", "Invalid 0x36", "A40BLT",
"A24MBLT USER", "A24 USER DATA", "A24 USER PROG", "A24BLT USER",
"A24MBLT SUP", "A24 SUP DATA", "A24 SUP PROG", "A24BLT SUP"
};
static int vme_window_proc_show_mapping(struct seq_file *m, int num, struct mapping *mapping)
{
char *pfs;
struct vme_mapping *desc = &mapping->desc;
/*
va PCI VME Size Address Modifier Data Prefetch
Description Width Size
dd: xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx - ssssssssssssssss / sss sssss
client: ddddd ssssssssssssssss
*/
seq_printf(m, " %2d: %p %.8x %.8x %.8x - ",
num,
desc->kernel_va, desc->pci_addrl,
desc->vme_addrl, desc->sizel);
if (desc->read_prefetch_enabled)
switch (desc->read_prefetch_size) {
case VME_PREFETCH_2:
pfs = "PFS2";
break;
case VME_PREFETCH_4:
pfs = "PFS4";
break;
case VME_PREFETCH_8:
pfs = "PFS8";
break;
case VME_PREFETCH_16:
pfs = "PFS16";
break;
default:
pfs = "?";
break;
}
else
pfs = "NOPF";
seq_printf(m, "(0x%02x)%s / D%2d %5s\n",
desc->am, amod[desc->am], desc->data_width, pfs);
seq_printf(m, " client: ");
if (mapping->client.pid_nr)
seq_printf(m, "%d ", (unsigned int)mapping->client.pid_nr);
if (mapping->client.name[0])
seq_printf(m, "%s", mapping->client.name);
seq_printf(m, "\n");
return 0;
}
static int vme_window_proc_show_window(struct seq_file *m, int window_num)
{
char *pfs;
struct window *window = &window_table[window_num];
struct mapping *mapping;
struct vme_mapping *desc;
int count = 0;
seq_printf(m, "Window %d: ", window_num);
if (!window->active)
seq_printf(m, "Not Active\n");
else {
seq_printf(m, "Active - ");
if (window->users == 0)
seq_printf(m, "No users\n");
else
seq_printf(m, "%2d user%c\n", window->users,
(window->users > 1)?'s':' ');
}
if (!window->active) {
seq_printf(m, "\n");
return 0;
}
desc = &window->desc;
seq_printf(m, " %p %.8x %.8x %.8x - ",
desc->kernel_va, desc->pci_addrl,
desc->vme_addrl, desc->sizel);
if (desc->read_prefetch_enabled)
switch (desc->read_prefetch_size) {
case VME_PREFETCH_2:
pfs = "PFS2";
break;
case VME_PREFETCH_4:
pfs = "PFS4";
break;
case VME_PREFETCH_8:
pfs = "PFS8";
break;
case VME_PREFETCH_16:
pfs = "PFS16";
break;
default:
pfs = "?";
break;
}
else
pfs = "NOPF";
seq_printf(m, "(0x%02x)%s / D%2d %5s\n",
desc->am, amod[desc->am], desc->data_width, pfs);
if (list_empty(&window->mappings)) {
seq_printf(m, "\n");
return 0;
}
seq_printf(m, "\n Mappings:\n");
list_for_each_entry(mapping, &window->mappings, list) {
vme_window_proc_show_mapping(m, count, mapping);
count++;
}
seq_printf(m, "\n");
return 0;
}
static int vme_window_proc_show(struct seq_file *m, void *data)
{
int i;
seq_printf(m, "\nPCI-VME Windows\n");
seq_printf(m, "===============\n\n");
seq_printf(m, " va PCI VME Size Address Modifier Data Prefetch\n");
seq_printf(m, " and Description Width Size\n");
seq_printf(m, "----------------------------------------------------------------------------\n\n");
for (i = 0; i < TSI148_NUM_OUT_WINDOWS; i++)
vme_window_proc_show_window(m, i);
return 0;
}
static int vme_window_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, vme_window_proc_show, NULL);
}
const struct file_operations vme_window_proc_ops = {
.open = vme_window_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#endif /* CONFIG_PROC_FS */
/**
* vme_window_release() - release file method for the VME window device
* @inode: Device inode
* @file: Device file descriptor
*
* The release method is in charge of releasing all the mappings made by
* the process closing the device file.
*/
int vme_window_release(struct inode *inode, struct file *file)
{
int i;
struct window *window;
struct mapping *mapping;
struct mapping *tmp;
for (i = 0; i < TSI148_NUM_OUT_WINDOWS; i++) {
window = &window_table[i];
if (mutex_lock_interruptible(&window->lock))
return -ERESTARTSYS;
if ((!window->active) || (list_empty(&window->mappings)))
goto try_next;
list_for_each_entry_safe(mapping, tmp,
&window->mappings, list) {
if (mapping->client.file == file) {
/*
* OK, that mapping is held by the process
* release it.
*/
list_del(&mapping->list);
kfree(mapping);
window->users--;
}
}
try_next:
mutex_unlock(&window->lock);
}
return 0;
}
/**
* add_mapping() - Helper function to add a mapping to a window
* @window: Window to add the mapping to
* @desc: Mapping descriptor
* @file: owner of the mapping (NULL if it comes from the kernel)
*
* It is assumed that the window mutex is held on entry to this function.
*/
static int
add_mapping(struct window *window, struct vme_mapping *desc, struct file *file)
{
struct mapping *mapping;
/* Create a logical mapping for this hardware window */
if ((mapping = kzalloc(sizeof(struct mapping), GFP_KERNEL)) == NULL) {
printk(KERN_ERR PFX "%s - "
"Failed to allocate mapping\n", __func__);
return -ENOMEM;
}
/* Save the window descriptor for this window. */
memcpy(&mapping->desc, desc, sizeof(struct vme_mapping));
/* Store the task's info only if the request comes from user-space */
if (file) {
mapping->client.file = file;
mapping->client.pid_nr = task_pid_nr(current);
strcpy(mapping->client.name, current->comm);
} else {
strcpy(mapping->client.name, "kernel");
}
/* Insert mapping at end of window mappings list */
list_add_tail(&mapping->list, &window->mappings);
/* Increment user count */
window->users++;
return 0;
}
/**
* remove_mapping() - Helper function to remove a mapping from a window
* @window: Window to remove the mapping from
* @desc: Mapping descriptor
* @file: struct file of the owner of the mapping (NULL if the kernel owns it)
*
* The specified descriptor is searched into the window mapping list by
* only matching the VME address, the size and the virtual address.
*
* It is assumed that the window mutex is held on entry to this function.
*/
static int
remove_mapping(struct window *window, struct vme_mapping *desc, struct file *file)
{
struct mapping *mapping;
struct mapping *tmp;
list_for_each_entry_safe(mapping, tmp, &window->mappings, list) {
if ((mapping->desc.vme_addru == desc->vme_addru) &&
(mapping->desc.vme_addrl == desc->vme_addrl) &&
(mapping->desc.sizeu == desc->sizeu) &&
(mapping->desc.sizel == desc->sizel) &&
(mapping->desc.kernel_va == desc->kernel_va) &&
(!file || mapping->client.file == file)) {
/* Found the matching mapping */
list_del(&mapping->list);
kfree(mapping);
window->users--;
return 0;
}
}
return -EINVAL;
}
/**
* find_vme_mapping_from_addr - Find corresponding vme_mapping structure from
* logical address, returned by find_controller()
*
* @param logaddr - address to search for.
*
*
* @return vme_mapping pointer - if found.
* @return NULL - not found.
*/
struct vme_mapping* find_vme_mapping_from_addr(unsigned logaddr)
{
int cntr;
struct window *window;
struct mapping *mapping;
for (cntr = 0; cntr < TSI148_NUM_OUT_WINDOWS; cntr++) {
window = &window_table[cntr];
if (mutex_lock_interruptible(&window->lock))
return NULL;
list_for_each_entry(mapping, &window->mappings, list) {
if ((unsigned long)mapping->desc.kernel_va == logaddr) {
mutex_unlock(&window->lock);
return &mapping->desc; /* bingo */
}
}
mutex_unlock(&window->lock);
}
return NULL; /* not found */
}
EXPORT_SYMBOL_GPL(find_vme_mapping_from_addr);
/**
* vme_get_window_attr() - Get a PCI-VME hardware window attributes
* @desc: Contains the window number we're interested in
*
* Get the specified window attributes.
*
* This function is used by the VME_IOCTL_GET_WINDOW_ATTR ioctl from
* user applications but can also be used by drivers stacked on top
* of this one.
*
* Return 0 on success, or %EINVAL if the window number is out of bounds.
*/
int vme_get_window_attr(struct vme_mapping *desc)
{
int window_num = desc->window_num;
if ((window_num < 0) || (window_num >= TSI148_NUM_OUT_WINDOWS))
return -EINVAL;
if (mutex_lock_interruptible(&window_table[window_num].lock))
return -ERESTARTSYS;
memcpy(desc, &window_table[window_num].desc,
sizeof(struct vme_mapping));
tsi148_get_window_attr(desc);
mutex_unlock(&window_table[window_num].lock);
return 0;
}
EXPORT_SYMBOL_GPL(vme_get_window_attr);
/**
* vme_create_window() - Create and map a PCI-VME window
* @desc: Descriptor of the window to create
*
* Create and map a PCI-VME window according to the &struct vme_mapping
* parameter.
*
* This function is used by the VME_IOCTL_CREATE_WINDOW ioctl from
* user applications but can also be used by drivers stacked on top
* of this one.
*
* Return 0 on success, or a standard kernel error code on failure.
*/
int vme_create_window(struct vme_mapping *desc)
{
int window_num = desc->window_num;
struct window *window;
int rc = 0;
/* A little bit of checking */
if ((window_num < 0) || (window_num >= TSI148_NUM_OUT_WINDOWS))
return -EINVAL;
if (desc->sizel == 0)
return -EINVAL;
/* Round down the initial VME address to a 64K boundary */
if (desc->vme_addrl & 0xffff) {
unsigned int lowaddr = desc->vme_addrl & ~0xffff;
printk(KERN_INFO PFX "%s - aligning VME address %08x to 64K "
"boundary %08x.\n", __func__, desc->vme_addrl, lowaddr);
desc->vme_addrl = lowaddr;
desc->sizel += desc->vme_addrl - lowaddr;
}
/*
* Round up the mapping size to a 64K boundary
* Note that vme_addrl is already aligned
*/
if (desc->sizel & 0xffff) {
unsigned int newsize = (desc->sizel + 0x10000) & ~0xffff;
printk(KERN_INFO PFX "%s - rounding up size %08x to 64K "
"boundary %08x.\n", __func__, desc->sizel, newsize);
desc->sizel = newsize;
}
/*
* OK from now on we don't want someone else mucking with our
* window.
*/
window = &window_table[window_num];
if (mutex_lock_interruptible(&window->lock))
return -ERESTARTSYS;
if (window->active) {
rc = -EBUSY;
goto out_unlock;
}
/* Allocate and map a PCI address space for the window */
window->rsrc.name = kmalloc(32, GFP_KERNEL);
if (!window->rsrc.name)
/* Not fatal, we can live with a nameless resource */
printk(KERN_WARNING PFX "%s - "
"failed to allocate resource name\n", __func__);
else
sprintf((char *)window->rsrc.name, "VME Window %d", window_num);
window->rsrc.start = 0;
window->rsrc.end = desc->sizel;
window->rsrc.flags = IORESOURCE_MEM;
/*
* Allocate a PCI region for our window. Align the region to a 64K
* boundary for the TSI148 chip.
*/
rc = pci_bus_alloc_resource(vme_bridge->pdev->bus, &window->rsrc,
desc->sizel, 0x10000,
PCIBIOS_MIN_MEM, 0, NULL, NULL);
if (rc) {
printk(KERN_ERR PFX "%s - "
"Failed to allocate bus resource for window %d "
"start 0x%lx size 0x%.8x\n",
__func__, window_num, (unsigned long)window->rsrc.start,
desc->sizel);
goto out_free;
}
desc->kernel_va = ioremap(window->rsrc.start, desc->sizel);
if (desc->kernel_va == NULL) {
printk(KERN_ERR PFX "%s - "
"failed to map window %d start 0x%lx size 0x%.8x\n",
__func__, window_num, (unsigned long)window->rsrc.start,
desc->sizel);
rc = -ENOMEM;
goto out_release;
}
desc->pci_addrl = window->rsrc.start;
/* Now setup the chip for that window */
rc = tsi148_create_window(desc);
if (rc)
goto out_unmap;
/* Copy the descriptor */
memcpy(&window->desc, desc, sizeof(struct vme_mapping));
/* Mark the window as active now */
window->active = 1;
mutex_unlock(&window->lock);
return 0;
out_unmap:
iounmap(desc->kernel_va);
out_release:
release_resource(&window->rsrc);
out_free:
if (window->rsrc.name)
kfree(window->rsrc.name);
memset(&window->rsrc, 0, sizeof(struct resource));
out_unlock:
mutex_unlock(&window->lock);
return rc;
}
EXPORT_SYMBOL_GPL(vme_create_window);
/**
* vme_destroy_window() - Unmap and remove a PCI-VME window
* @window_num: Window Number of the window to be destroyed
*
* Unmap and remove the PCI-VME window specified in the &struct vme_mapping
* parameter also release all the mappings on top of that window.
*
* This function is used by the VME_IOCTL_DESTROY_WINDOW ioctl from
* user applications but can also be used by drivers stacked on top
* of this one.
*
* NOTE: destroying a window also forcibly remove all the mappings
* ont top of that window.
*
* Return 0 on success, or a standard kernel error code on failure.
*/
int vme_destroy_window(int window_num)
{
struct window *window;
struct mapping *mapping;
struct mapping *tmp;
int rc = 0;
if ((window_num < 0) || (window_num >= TSI148_NUM_OUT_WINDOWS))
return -EINVAL;
/*
* Prevent somebody else from changing our window from under us
*/
window = &window_table[window_num];
if (mutex_lock_interruptible(&window->lock))
return -ERESTARTSYS;
/*
* Maybe we should silently ignore trying to destroy an unused
* window.
*/
if (!window->active) {
rc = -EINVAL;
goto out_unlock;
}
/* Remove all mappings */
if (window->users > 0) {
list_for_each_entry_safe(mapping, tmp,
&window->mappings, list) {
list_del(&mapping->list);
kfree(mapping);
window->users--;
}
}
if (window->users)
printk(KERN_ERR "%s: %d mappings still alive "
"on window %d\n",
__func__, window->users, window_num);
/* Mark the window as unused */
window->active = 0;
/* Unmap the window */
iounmap(window->desc.kernel_va);
window->desc.kernel_va = NULL;
tsi148_remove_window(&window->desc);
/* Release the PCI bus resource */
release_resource(&window->rsrc);
if (window->rsrc.name)
kfree(window->rsrc.name);
memset(&window->rsrc, 0, sizeof(struct resource));
out_unlock:
mutex_unlock(&window->lock);
return rc;
}
EXPORT_SYMBOL_GPL(vme_destroy_window);
/*
* In order to save windows in the bridge, we map the whole address space
* onto a single window, so that subsequent mappings of the same kind will
* all be attached to it.
*/
static void vme_optimize_window_size(struct vme_mapping *desc)
{
unsigned int resize = 0;
switch (desc->am) {
case VME_A24_USER_MBLT:
case VME_A24_USER_DATA_SCT:
case VME_A24_USER_PRG_SCT:
case VME_A24_USER_BLT:
case VME_A24_SUP_MBLT:
case VME_A24_SUP_DATA_SCT:
case VME_A24_SUP_PRG_SCT:
case VME_A24_SUP_BLT:
resize = 0x1000000;
break;
case VME_A16_USER:
case VME_A16_LCK:
case VME_A16_SUP:
resize = 0x10000;
break;
default:
break;
}
if (!resize)
return;
printk(KERN_INFO PFX "window %d: optimizing size to 0x%08x\n",
desc->window_num, resize);
desc->sizeu = 0;
desc->sizel = resize;
desc->vme_addru = 0;
desc->vme_addrl = 0;
}
static int vme_mapping_sanity_check(const struct vme_mapping *mapping)
{
unsigned int vme_addru = mapping->vme_addru;
unsigned int vme_addrl = mapping->vme_addrl;
unsigned int err = 0;
switch (mapping->am) {
case VME_A16_USER:
case VME_A16_LCK:
case VME_A16_SUP:
if (vme_addru || vme_addrl & ~0xffff)
err = 16;
break;
case VME_A24_USER_MBLT:
case VME_A24_USER_DATA_SCT:
case VME_A24_USER_PRG_SCT:
case VME_A24_USER_BLT:
case VME_A24_SUP_MBLT:
case VME_A24_SUP_DATA_SCT:
case VME_A24_SUP_PRG_SCT:
case VME_A24_SUP_BLT:
if (vme_addru || vme_addrl & ~0xffffff)
err = 24;
break;
case VME_A32_LCK:
case VME_A32_USER_MBLT:
case VME_A32_USER_DATA_SCT:
case VME_A32_USER_PRG_SCT:
case VME_A32_USER_BLT:
case VME_A32_SUP_MBLT:
case VME_A32_SUP_DATA_SCT:
case VME_A32_SUP_PRG_SCT:
case VME_A32_SUP_BLT:
if (vme_addru)
err = 32;
break;
case VME_A40_SCT:
case VME_A40_LCK:
case VME_A40_BLT:
if (vme_addru & ~0xff)
err = 40;
break;
default:
break;
}
if (err) {
printk(KERN_ERR PFX "Error: 0x%08x %08x out of A%d range\n",
vme_addru, vme_addrl, err);
return -EINVAL;
}
return 0;
}
static int
__vme_find_mapping(struct vme_mapping *match, int force, struct file *file)
{
int i;
int rc = 0;
struct window *window;
unsigned int offset;
struct vme_mapping wnd;
rc = vme_mapping_sanity_check(match);
if (rc)
return rc;
for (i = 0; i < TSI148_NUM_OUT_WINDOWS; i++) {
window = &window_table[i];
if (mutex_lock_interruptible(&window->lock))
return -ERESTARTSYS;
/* First check if window is in use */
if (!window->active)
goto try_next;
/*
* Check if the window matches what we're looking for.
*
* Right now we only deal with 32-bit (or lower) address space
* windows,
*/
/* Check that the window is enabled in the hardware */
if (!window->desc.window_enabled)
goto try_next;
/* Check that the window has a <= 32-bit address space */
if ((window->desc.vme_addru != 0) || (window->desc.sizeu != 0))
goto try_next;
/* Check the address modifier and data width */
if ((window->desc.am != match->am) ||
(window->desc.data_width != match->data_width))
goto try_next;
/* Check the boundaries */
if ((window->desc.vme_addrl > match->vme_addrl) ||
((window->desc.vme_addrl + window->desc.sizel) <
(match->vme_addrl + match->sizel)))
goto try_next;
/* Check the 2eSST transfer speed if 2eSST is enabled */
if ((window->desc.am == VME_2e6U) &&
(window->desc.v2esst_mode != match->v2esst_mode))
goto try_next;
/*
* Good, we found one mapping
* NOTE: we're exiting the loop with window->lock still held
*/
break;
try_next:
mutex_unlock(&window->lock);
}
/* Window found */
if (i < TSI148_NUM_OUT_WINDOWS) {
offset = match->vme_addrl - window->desc.vme_addrl;
/* Now set the virtual address of the mapping */
match->kernel_va = window->desc.kernel_va + offset;
match->pci_addrl = window->desc.pci_addrl + offset;
/* Assign window number */
match->window_num = i;
/* Add the new mapping to the window */
rc = add_mapping(window, match, file);
mutex_unlock(&window->lock);
return rc;
}
/*
* Bad luck, no matching window found - create a new one if
* force is set.
*/
if (!force)
return -EBUSY;
/* Get the first unused window */
for (i = 0; i < TSI148_NUM_OUT_WINDOWS; i++) {
window = &window_table[i];
if (!window->active)
break;
}
if (i >= TSI148_NUM_OUT_WINDOWS)
/* No more window available - bail out */
return -EBUSY;
/*
* Setup the physical window descriptor that can hold the requested
* mapping. The VME address and size may be realigned by the low level
* code in the descriptor, so make a private copy for window creation.
*/
memcpy(&wnd, match, sizeof(struct vme_mapping));
wnd.window_num = i;
vme_optimize_window_size(&wnd);
rc = vme_create_window(&wnd);
if (rc)
return rc;
/* Now set the virtual address of the mapping */
window = &window_table[wnd.window_num];
offset = match->vme_addrl - window->desc.vme_addrl;
match->kernel_va = window->desc.kernel_va + offset;
match->pci_addrl = window->desc.pci_addrl + offset;
match->window_num = wnd.window_num;
/* And add that mapping to it */
rc = add_mapping(window, match, file);
return rc;
}
static int
__vme_release_mapping(struct vme_mapping *desc, int force, struct file *file)
{
int window_num = desc->window_num;
struct window *window;
int rc = 0;
if ((window_num < 0) || (window_num >= TSI148_NUM_OUT_WINDOWS))
return -EINVAL;
window = &window_table[window_num];
if (mutex_lock_interruptible(&window->lock))
return -ERESTARTSYS;
if (!window->active) {
rc = -EINVAL;
goto out_unlock;
}
/* Remove the mapping */
rc = remove_mapping(window, desc, file);
if (rc)
goto out_unlock;
/* Check if there are no more users of this window */
if ((window->users == 0) && force) {
mutex_unlock(&window->lock);
return vme_destroy_window(window_num);
}
out_unlock:
mutex_unlock(&window->lock);
return rc;
}
/**
* vme_find_mapping() - Find a window matching the specified descriptor
* @match: Descriptor of the window to look for
* @force: Force window creation if no match was found.
*
* Try to find a window matching the specified descriptor. If a window is
* found, then its number is returned in the &struct vme_mapping parameter.
*
* If no window match and force is set, then a new window is created
* to hold that mapping.
*
* This function is used by the VME_IOCTL_FIND_MAPPING ioctl from
* user applications but can also be used by drivers stacked on top
* of this one.
*
* Returns 0 on success, or a standard kernel error code.
*/
int vme_find_mapping(struct vme_mapping *match, int force)
{
return __vme_find_mapping(match, force, NULL);
}
EXPORT_SYMBOL_GPL(vme_find_mapping);
/**
* vme_release_mapping() - Release a mapping allocated with vme_find_mapping
*
* @desc: Descriptor of the mapping to release
* @force: force window destruction
*
* Release a VME mapping. If the mapping is the last one on that window and
* force is set then the window is also destroyed.
*
* This function is used by the VME_IOCTL_RELEASE_MAPPING ioctl from
* user applications but can also be used by drivers stacked on top
* of this one.
*
* @return 0 - on success.
* @return standard kernel error code - if failed.
*/
int vme_release_mapping(struct vme_mapping *desc, int force)
{
return __vme_release_mapping(desc, force, NULL);
}
EXPORT_SYMBOL_GPL(vme_release_mapping);
static int vme_destroy_window_ioctl(int __user *argp)
{
int window_num;
if (get_user(window_num, argp))
return -EFAULT;
return vme_destroy_window(window_num);
}
static int vme_bus_error_check_clear_ioctl(struct vme_bus_error __user *argp)
{
struct vme_bus_error_desc desc;
if (copy_from_user(&desc, argp, sizeof(struct vme_bus_error_desc)))
return -EFAULT;
desc.valid = vme_bus_error_check_clear(&desc.error);
if (copy_to_user(argp, &desc, sizeof(struct vme_bus_error_desc)))
return -EFAULT;
return 0;
}
/**
* vme_window_ioctl() - ioctl file method for the VME window device
* @file: Device file descriptor
* @cmd: ioctl number
* @arg: ioctl argument
*
* Currently the VME window device supports the following ioctls:
*
* VME_IOCTL_GET_WINDOW_ATTR
* VME_IOCTL_CREATE_WINDOW
* VME_IOCTL_DESTROY_WINDOW
* VME_IOCTL_FIND_MAPPING
* VME_IOCTL_RELEASE_MAPPING
* VME_IOCTL_GET_CREATE_ON_FIND_FAIL
* VME_IOCTL_SET_CREATE_ON_FIND_FAIL
* VME_IOCTL_GET_DESTROY_ON_REMOVE
* VME_IOCTL_SET_DESTROY_ON_REMOVE
*/
long vme_window_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int rc;
struct vme_mapping desc;
void __user *argp = (void __user *)arg;
switch (cmd) {
case VME_IOCTL_GET_WINDOW_ATTR:
/*
* Get a window attributes.
*
* arg is a pointer to a struct vme_mapping with only
* the window number specified.
*/
if (copy_from_user(&desc, (void *)argp,
sizeof(struct vme_mapping)))
return -EFAULT;
rc = vme_get_window_attr(&desc);
if (rc)
return rc;
if (copy_to_user((void *)argp, &desc,
sizeof(struct vme_mapping)))
return -EFAULT;
break;
case VME_IOCTL_CREATE_WINDOW:
/* Create and map a window.
*
* arg is a pointer to a struct vme_mapping specifying
* the window number as well as its attributes.
*/
if (copy_from_user(&desc, (void *)argp,
sizeof(struct vme_mapping))) {
return -EFAULT;
}
rc = vme_create_window(&desc);
if (rc)
return rc;
if (copy_to_user((void *)argp, &desc,
sizeof(struct vme_mapping)))
return -EFAULT;
break;
case VME_IOCTL_DESTROY_WINDOW:
/* Unmap and destroy a window.
*
* arg is a pointer to the window number
*/
return vme_destroy_window_ioctl((int __user *)argp);
case VME_IOCTL_FIND_MAPPING:
/*
* Find a window suitable for this mapping.
*
* arg is a pointer to a struct vme_mapping specifying
* the attributes of the window to look for. If a match is
* found then the virtual address for the requested mapping
* is set in the window descriptor otherwise if
* vme_create_on_find_fail is set then create a new window to
* hold that mapping.
*/
if (copy_from_user(&desc, (void *)argp,
sizeof(struct vme_mapping)))
return -EFAULT;
rc = __vme_find_mapping(&desc, vme_create_on_find_fail, file);
if (rc)
return rc;
if (copy_to_user((void *)argp, &desc,
sizeof(struct vme_mapping)))
return -EFAULT;
break;
case VME_IOCTL_RELEASE_MAPPING:
/* remove a mapping.
*
* arg is a pointer to a struct vme_mapping specifying
* the attributes of the mapping to be removed.
* If the mapping is the last on that window and if
* vme_destroy_on_remove is set then the window is also
* destroyed.
*/
if (copy_from_user(&desc, (void *)argp,
sizeof(struct vme_mapping)))
return -EFAULT;
rc = __vme_release_mapping(&desc, vme_destroy_on_remove, file);
break;
case VME_IOCTL_GET_CREATE_ON_FIND_FAIL:
rc = put_user(vme_create_on_find_fail,
(unsigned int __user *)argp);
break;
case VME_IOCTL_SET_CREATE_ON_FIND_FAIL:
rc = get_user(vme_create_on_find_fail,
(unsigned int __user *)argp);
break;
case VME_IOCTL_GET_DESTROY_ON_REMOVE:
rc = put_user(vme_destroy_on_remove,
(unsigned int __user *)argp);
break;
case VME_IOCTL_SET_DESTROY_ON_REMOVE:
rc = get_user(vme_destroy_on_remove,
(unsigned int __user *)argp);
break;
case VME_IOCTL_GET_BUS_ERROR:
rc = put_user(vme_bus_error_check(1),
(unsigned int __user *)argp);
break;
case VME_IOCTL_CHECK_CLEAR_BUS_ERROR:
return vme_bus_error_check_clear_ioctl(argp);
default:
rc = -ENOIOCTLCMD;
}
return rc;
}
/**
* vme_remap_pfn_range() - Small wrapper for io_remap_pfn_range
* @vma: User vma to map to
*
* This is a small helper function which sets the approriate vma flags
* and protection before calling io_remap_pfn_range().
*
* The following flags are added to vm_flags:
* - VM_IO This is an iomem region
* - VM_RESERVED Do not swap that mapping
* - VM_DONTCOPY Don't copy that mapping on fork
* - VM_DONTEXPAND Prevent resizing with mremap()
*
* The mapping is also set non-cacheable via the vm_page_prot field.
*/
#ifndef VM_RESERVED
#define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP)
#endif
static int vme_remap_pfn_range(struct vm_area_struct *vma)
{
vma->vm_flags |= VM_IO | VM_RESERVED | VM_DONTCOPY | VM_DONTEXPAND;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
return io_remap_pfn_range(vma,
vma->vm_start,
vma->vm_pgoff,
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
}
/*
* Check that a particular VME mapping corresponds to a region to be mmapped.
*
* mmap() works with whole pages. Thus we need to see VME mappings as blocks
* of pages, even if that means we see them bigger than they really are.
*/
static int vme_check_mmap_region(int windownr, struct vme_mapping *mapping,
unsigned int addr, unsigned int size)
{
struct vme_mapping *window = &window_table[windownr].desc;
unsigned int aligned_start;
unsigned int aligned_end;
unsigned int end;
/* check that the requested region doesn't stick out of the window */
if (addr < window->pci_addrl ||
addr + size > window->pci_addrl + window->sizel) {
return 0;
}
/* compute the page-aligned boundaries of the VME mapping */
aligned_start = mapping->pci_addrl & PAGE_MASK;
end = mapping->pci_addrl + mapping->sizel;
if (end & (~PAGE_MASK))
aligned_end = (end & PAGE_MASK) + PAGE_SIZE;
else
aligned_end = end;
/* and compare these boundaries to those of the requested region */
if (aligned_start == addr && aligned_end == addr + size)
return 1;
return 0;
}
/**
* vme_window_mmap() - Map to userspace a VME address mapping
* @file: Device file descriptor
* @vma: User vma to map to
*
* The PCI physical address is in vma->vm_pgoff and is guaranteed to be unique
* among all mappings. The range size is given by (vma->vm_end - vma->vm_start)
*/
int vme_window_mmap(struct file *file, struct vm_area_struct *vma)
{
int i;
int rc = -EINVAL;
struct window *window;
struct mapping *mapping;
unsigned int addr;
unsigned int size;
/* Find the mapping this mmap call refers to */
for (i = 0; i < TSI148_NUM_OUT_WINDOWS; i++) {
window = &window_table[i];
if (mutex_lock_interruptible(&window->lock))
return -ERESTARTSYS;
/* Check the window is active and contains mappings */
if ((!window->active) || (list_empty(&window->mappings)))
goto try_next;
list_for_each_entry(mapping, &window->mappings, list) {
addr = vma->vm_pgoff << PAGE_SHIFT;
size = vma->vm_end - vma->vm_start;
if (mapping->client.file == file &&
vme_check_mmap_region(i, &mapping->desc, addr, size)) {
rc = vme_remap_pfn_range(vma);
mutex_unlock(&window->lock);
return rc;
}
}
try_next:
mutex_unlock(&window->lock);
}
return rc;
}
/**
* vme_window_init() - Initialize VME windows handling
*
* Not much to do here aside from initializing the windows mutexes and
* mapping lists.
*/
void vme_window_init(void)
{
int i;
for (i = 0; i < TSI148_NUM_OUT_WINDOWS; i++) {
INIT_LIST_HEAD(&window_table[i].mappings);
mutex_init(&window_table[i].lock);
window_table[i].active = 0;
}
}
/**
* vme_window_exit() - Cleanup for module unload
*
* Unmap all the windows that were mapped.
*/
void vme_window_exit(void)
{
int i;
for (i = 0; i < TSI148_NUM_OUT_WINDOWS; i++) {
if (window_table[i].active)
vme_destroy_window(i);
}
}
/**
* \file vmebus.h
* \brief PCI-VME public API
* \author Sebastien Dugue
* \date 04/02/2009
*
* This API presents in fact 2 APIs with some common definitions. One for
* drivers and one for user applications. User applications cannot use the
* driver specific parts enclosed in \#ifdef __KERNEL__ sections.
*
* Copyright (c) 2009 \em Sebastien \em Dugue
*
* \par License:
* 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.
*
*/
#ifndef _VME_H
#define _VME_H
#ifdef __KERNEL__
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#endif /* __KERNEL__ */
#include <linux/types.h>
/*
* VME window attributes
*/
/**
* \brief Read prefetch size
*/
enum vme_read_prefetch_size {
VME_PREFETCH_2 = 0,
VME_PREFETCH_4,
VME_PREFETCH_8,
VME_PREFETCH_16
};
/**
* \brief Data width
*/
enum vme_data_width {
VME_D8 = 8,
VME_D16 = 16,
VME_D32 = 32,
VME_D64 = 64
};
/**
* \brief 2eSST data transfer speed
*/
enum vme_2esst_mode {
VME_SST160 = 0,
VME_SST267,
VME_SST320
};
/**
* \brief Address modifiers
*/
enum vme_address_modifier {
VME_A64_MBLT = 0, /* 0x00 */
VME_A64_SCT, /* 0x01 */
VME_A64_BLT = 3, /* 0x03 */
VME_A64_LCK, /* 0x04 */
VME_A32_LCK, /* 0x05 */
VME_A32_USER_MBLT = 8, /* 0x08 */
VME_A32_USER_DATA_SCT, /* 0x09 */
VME_A32_USER_PRG_SCT, /* 0x0a */
VME_A32_USER_BLT, /* 0x0b */
VME_A32_SUP_MBLT, /* 0x0c */
VME_A32_SUP_DATA_SCT, /* 0x0d */
VME_A32_SUP_PRG_SCT, /* 0x0e */
VME_A32_SUP_BLT, /* 0x0f */
VME_2e6U = 0x20, /* 0x20 */
VME_2e3U, /* 0x21 */
VME_A16_USER = 0x29, /* 0x29 */
VME_A16_LCK = 0x2c, /* 0x2c */
VME_A16_SUP = 0x2d, /* 0x2d */
VME_CR_CSR = 0x2f, /* 0x2f */
VME_A40_SCT = 0x34, /* 0x34 */
VME_A40_LCK, /* 0x35 */
VME_A40_BLT = 0x37, /* 0x37 */
VME_A24_USER_MBLT, /* 0x38 */
VME_A24_USER_DATA_SCT, /* 0x39 */
VME_A24_USER_PRG_SCT, /* 0x3a */
VME_A24_USER_BLT, /* 0x3b */
VME_A24_SUP_MBLT, /* 0x3c */
VME_A24_SUP_DATA_SCT, /* 0x3d */
VME_A24_SUP_PRG_SCT, /* 0x3e */
VME_A24_SUP_BLT, /* 0x3f */
};
/**
* \brief PCI-VME mapping descriptor
* \param window_num Hardware window number
* \param kernel_va Kernel virtual address of the mapping for use by drivers
* \param user_va User virtual address of the mapping for use by applications
* \param fd User file descriptor for this mapping
* \param window_enabled State of the hardware window
* \param data_width VME data width
* \param am VME address modifier
* \param read_prefetch_enabled PCI read prefetch enabled state
* \param read_prefetch_size PCI read prefetch size (in cache lines)
* \param v2esst_mode VME 2eSST transfer speed
* \param bcast_select VME 2eSST broadcast select
* \param pci_addru PCI bus start address upper 32 bits
* \param pci_addrl PCI bus start address lower 32 bits
* \param sizeu Window size upper 32 bits
* \param sizel Window size lower 32 bits
* \param vme_addru VME bus start address upper 32 bits
* \param vme_addrl VME bus start address lower 32 bits
*
* This data structure is used for describing both a hardware window
* and a logical mapping on top of a hardware window. Therefore some of
* the fields are only relevant to one of those two entities.
*/
struct vme_mapping {
int window_num;
/* Reserved for kernel use */
void *kernel_va;
/* Reserved for userspace */
void *user_va;
int fd;
/* Window settings */
int window_enabled;
enum vme_data_width data_width;
enum vme_address_modifier am;
int read_prefetch_enabled;
enum vme_read_prefetch_size read_prefetch_size;
enum vme_2esst_mode v2esst_mode;
int bcast_select;
unsigned int pci_addru;
unsigned int pci_addrl;
unsigned int sizeu;
unsigned int sizel;
unsigned int vme_addru;
unsigned int vme_addrl;
};
/**
* \brief VME RMW descriptor
* \param vme_addru VME address for the RMW cycle upper 32 bits
* \param vme_addrl VME address for the RMW cycle lower 32 bits
* \param am VME address modifier
* \param enable_mask Bitmask of the bit
* \param compare_data
* \param swap_data
*
*/
struct vme_rmw {
unsigned int vme_addru;
unsigned int vme_addrl;
enum vme_address_modifier am;
unsigned int enable_mask;
unsigned int compare_data;
unsigned int swap_data;
};
/**
* \brief DMA endpoint attributes
* \param data_width VME data width (only used if endpoint is on the VME bus)
* \param am VME address modifier (ditto)
* \param v2esst_mode VME 2eSST transfer speed (ditto)
* \param bcast_select VME 2eSST broadcast select (ditto)
* \param addru Address upper 32 bits
* \param addrl Address lower 32 bits
*
* This data structure is used for describing the attributes of a DMA endpoint.
* All the field excepted for the address are only relevant for an endpoint
* on the VME bus.
*/
struct vme_dma_attr {
enum vme_data_width data_width;
enum vme_address_modifier am;
enum vme_2esst_mode v2esst_mode;
unsigned int bcast_select;
unsigned int addru;
unsigned int addrl;
};
/** \brief DMA block size on the PCI or VME bus */
enum vme_dma_block_size {
VME_DMA_BSIZE_32 = 0,
VME_DMA_BSIZE_64,
VME_DMA_BSIZE_128,
VME_DMA_BSIZE_256,
VME_DMA_BSIZE_512,
VME_DMA_BSIZE_1024,
VME_DMA_BSIZE_2048,
VME_DMA_BSIZE_4096
};
/** \brief DMA backoff time (us) on the PCI or VME bus */
enum vme_dma_backoff {
VME_DMA_BACKOFF_0 = 0,
VME_DMA_BACKOFF_1,
VME_DMA_BACKOFF_2,
VME_DMA_BACKOFF_4,
VME_DMA_BACKOFF_8,
VME_DMA_BACKOFF_16,
VME_DMA_BACKOFF_32,
VME_DMA_BACKOFF_64
};
/**
* \brief DMA control
* \param vme_block_size VME bus block size when the source is VME
* \param vme_backoff_time VME bus backoff time when the source is VME
* \param pci_block_size PCI/X bus block size when the source is PCI
* \param pci_backoff_time PCI bus backoff time when the source is PCI
*
*/
struct vme_dma_ctrl {
enum vme_dma_block_size vme_block_size;
enum vme_dma_backoff vme_backoff_time;
enum vme_dma_block_size pci_block_size;
enum vme_dma_backoff pci_backoff_time;
};
/** \brief DMA transfer direction */
enum vme_dma_dir {
VME_DMA_TO_DEVICE = 1,
VME_DMA_FROM_DEVICE
};
/**
* \brief VME DMA transfer descriptor
* \param status Transfer status
* \param length Transfer size in bytes
* \param novmeinc Must be set to 1 when accessing a FIFO like device on the VME
* \param dir Transfer direction
* \param src Transfer source attributes
* \param dst Transfer destination attributes
* \param opt Transfer control
*
*/
struct vme_dma {
unsigned int status;
unsigned int length;
unsigned int novmeinc;
enum vme_dma_dir dir;
struct vme_dma_attr src;
struct vme_dma_attr dst;
struct vme_dma_ctrl ctrl;
};
/**
* \brief VME Bus Error
* \param address Address of the bus error
* \param am Address Modifier of the bus error
*/
struct vme_bus_error {
__u64 address;
enum vme_address_modifier am;
};
/**
* \brief VME Bus Error descriptor
* \param error Address/AM of the bus error
* \param valid Valid Flag: 0 -> no error, 1 -> error
*/
struct vme_bus_error_desc {
struct vme_bus_error error;
int valid;
};
/*! @name VME single access swapping policy
*@{
*/
#define SINGLE_NO_SWAP 0
#define SINGLE_AUTO_SWAP 1
#define SINGLE_WORD_SWAP 2
#define SINGLE_BYTEWORD_SWAP 3
/*@}*/
/*! @name page qualifier
*
*@{
*/
#define VME_PG_SHARED 0x00
#define VME_PG_PRIVATE 0x02
/*@}*/
/**
* \brief VME mapping attributes
* \param iack VME IACK 0 -> IACK pages
* \param rdpref VME read prefetch option 0 -> Disable
* \param wrpost VME write posting option
* \param swap VME swap options
* \param sgmin page descriptor number returned by find_controller
* \param dum -- dum[0] page qualifier (shared/private), dum[1] XPC ADP-type
* dum[2] - reserved, _must_ be 0.
*
* This structure is used for the find_controller() and return_controller()
* LynxOS CES driver emulation.
*/
struct pdparam_master
{
unsigned long iack;
unsigned long rdpref;
unsigned long wrpost;
unsigned long swap;
unsigned long sgmin;
unsigned long dum[3];
};
/**
* \name Window management ioctl numbers
* \{
*/
/** Get a physical window attributes */
#define VME_IOCTL_GET_WINDOW_ATTR _IOWR('V', 0, struct vme_mapping)
/** Create a physical window */
#define VME_IOCTL_CREATE_WINDOW _IOW( 'V', 1, struct vme_mapping)
/** Destroy a physical window */
#define VME_IOCTL_DESTROY_WINDOW _IOW( 'V', 2, int)
/** Create a mapping over a physical window */
#define VME_IOCTL_FIND_MAPPING _IOWR('V', 3, struct vme_mapping)
/** Remove a mapping */
#define VME_IOCTL_RELEASE_MAPPING _IOW( 'V', 4, struct vme_mapping)
/** Get the create on find failed flag */
#define VME_IOCTL_GET_CREATE_ON_FIND_FAIL _IOR( 'V', 5, unsigned int)
/** Set the create on find failed flag */
#define VME_IOCTL_SET_CREATE_ON_FIND_FAIL _IOW( 'V', 6, unsigned int)
/** Get the destroy on remove flag */
#define VME_IOCTL_GET_DESTROY_ON_REMOVE _IOR( 'V', 7, unsigned int)
/** Set the destroy on remove flag */
#define VME_IOCTL_SET_DESTROY_ON_REMOVE _IOW( 'V', 8, unsigned int)
/** Get bus error status -- DEPRECATED */
#define VME_IOCTL_GET_BUS_ERROR _IOR( 'V', 9, unsigned int)
/** Check (and possibly clear) the bus error status */
#define VME_IOCTL_CHECK_CLEAR_BUS_ERROR _IOWR('V',10, struct vme_bus_error_desc)
/* \}*/
/**
* DMA ioctls
* \{
*/
/** Start a DMA transfer */
#define VME_IOCTL_START_DMA _IOWR('V', 10, struct vme_dma)
/* \}*/
#ifdef __KERNEL__
/*
* Those definitions are for drivers only and are not visible to userspace.
*/
struct vme_driver {
int (*match)(struct device *, unsigned int);
int (*probe)(struct device *, unsigned int);
int (*remove)(struct device *, unsigned int);
void (*shutdown)(struct device *, unsigned int);
int (*suspend)(struct device *, unsigned int, pm_message_t);
int (*resume)(struct device *, unsigned int);
struct device_driver driver;
struct device *devices;
};
#define to_vme_driver(x) container_of((x), struct vme_driver, driver)
typedef void (*vme_berr_handler_t)(struct vme_bus_error *);
/* API for new drivers */
extern int vme_register_driver(struct vme_driver *vme_driver, unsigned int ndev);
extern void vme_unregister_driver(struct vme_driver *vme_driver);
extern int vme_request_irq(unsigned int, int (*)(void *),
void *, const char *);
extern int vme_free_irq(unsigned int );
extern int vme_generate_interrupt(int, int, signed long);
extern struct vme_mapping* find_vme_mapping_from_addr(unsigned);
extern int vme_get_window_attr(struct vme_mapping *);
extern int vme_create_window(struct vme_mapping *);
extern int vme_destroy_window(int);
extern int vme_find_mapping(struct vme_mapping *, int);
extern int vme_release_mapping(struct vme_mapping *, int);
extern int vme_do_dma(struct vme_dma *);
extern int vme_do_dma_kernel(struct vme_dma *);
extern int vme_bus_error_check(int);
extern struct vme_berr_handler *
vme_register_berr_handler(struct vme_bus_error *, size_t, vme_berr_handler_t);
extern void vme_unregister_berr_handler(struct vme_berr_handler *);
/* API providing an emulation of the CES VME driver for legacy drivers */
extern unsigned long find_controller(unsigned long, unsigned long,
unsigned long, unsigned long,
unsigned long, struct pdparam_master *);
extern unsigned long return_controller(unsigned, unsigned);
extern int vme_intset(int, int (*)(void *), void *, void *);
extern int vme_intclr(int, void *);
#endif /* __KERNEL__ */
#endif /* _VME_H */
obj-m += ctr_pps_irq.o
obj-m += ctr_multi_irq.o
obj-m += loopirq.o
obj-m += vmemon.o
#
# Makefile for test drivers.
#
# include the build environment
include ../../common.mk
OUTPUTDIR?=`pwd`/$(CPU)/$(KVER)
all: vmedriver vmeheaders mymodules
.PHONY : all vmedriver vmeheaders mymodules clean help
vmedriver:
cp ../driver/$(CPU)/$(KVER)/Module.symvers .
mymodules: vmedriver
$(MAKE) -C $(KERNELSRC) M=`pwd` CROSS_COMPILE=$(CROSS_COMPILE) modules
mkdir -p $(OUTPUTDIR)
mv *.o *.ko $(OUTPUTDIR)
help:
$(MAKE) -C $(KERNELSRC) M=`pwd` help
clean cleanall:
$(MAKE) -C $(KERNELSRC) M=`pwd` clean
rm -rf $(ALL_CPUS)
# no install rule
install:
#include <linux/version.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <asm-generic/iomap.h>
#include <linux/delay.h>
#include "../include/vmebus.h"
#include "ctrhard.h"
#define PFX "Ctr1KHzIrq: "
#define DRV_MODULE_VERSION "1.0"
static struct vme_mapping ctr_desc = {
.data_width = VME_D32,
.am = VME_A24_USER_DATA_SCT,
.read_prefetch_enabled = 0,
.sizeu = 0,
.sizel = 0x10000,
.vme_addru = 0,
.vme_addrl = 0xc00000
};
static void *vmeaddr = NULL; /* Point to CTR hardware */
CtrDrvrMemoryMap *mmap;
#define CTR_IRQ_LEVEL 2
#define CTR_IRQ_VECTOR 0xb8
unsigned int ClearInterrupt(void)
{
unsigned int source;
source = ioread32be((unsigned int *)&mmap->InterruptSource);
return source;
}
void EnableCtrInterrupt(void) {
unsigned int val;
printk(KERN_DEBUG PFX "Enabling 1KHz interrupt on IRQ%d Vector %d\n",
CTR_IRQ_LEVEL, CTR_IRQ_VECTOR);
/* Setup the IRQ level and vector */
val = (unsigned long)((CTR_IRQ_LEVEL << 8) | (CTR_IRQ_VECTOR & 0xff));
iowrite32be(val, &mmap->Setup);
if (vme_bus_error_check(1))
printk(KERN_ERR PFX "Bus error writing Setup\n");
/* Will interrupt once per millisecond */
iowrite32be(CtrDrvrInterruptMask1KHZ |
CtrDrvrInterruptMaskPPS |
CtrDrvrInterruptMaskGMT_EVENT_IN,
&mmap->InterruptEnable);
if (vme_bus_error_check(1))
printk(KERN_ERR PFX "Bus error writing InterruptEnable\n");
val = ioread32be(&mmap->InterruptEnable);
if (vme_bus_error_check(1))
printk(KERN_ERR PFX "Bus error reading InterruptEnable\n");
if (val != (CtrDrvrInterruptMask1KHZ |
CtrDrvrInterruptMaskPPS |
CtrDrvrInterruptMaskGMT_EVENT_IN))
printk(KERN_DEBUG PFX "Failed to enable 1KHz interrupt %08x\n",
val);
}
void DisableCtrInterrupt(void)
{
printk(KERN_DEBUG PFX "Disabling 1KHz interrupt\n");
iowrite32be(0, &mmap->InterruptEnable);
if (vme_bus_error_check(1))
printk(KERN_ERR PFX "Bus error writing InterruptEnable\n");
}
void EnableCtrModule(void)
{
int val = ioread32be(&mmap->Command);
printk(KERN_DEBUG PFX "Enabling CTR module\n");
if (vme_bus_error_check(1))
printk(KERN_ERR PFX "Bus error reading Command\n");
val |= CtrDrvrCommandENABLE;
val &= ~CtrDrvrCommandDISABLE;
iowrite32be(val, &mmap->Command);
if (vme_bus_error_check(1))
printk(KERN_ERR PFX "Bus error writing Command\n");
}
void DisableCtrModule(void)
{
int val = ioread32be(&mmap->Command);
printk(KERN_DEBUG PFX "Disabling CTR module\n");
if (vme_bus_error_check(1))
printk(KERN_ERR PFX "Bus error reading Command\n");
val &= ~CtrDrvrCommandENABLE;
val |= CtrDrvrCommandDISABLE;
iowrite32be(val, &mmap->Command);
if (vme_bus_error_check(1))
printk(KERN_ERR PFX "Bus error writing Command\n");
}
void ResetCtr(void)
{
printk(KERN_DEBUG PFX "Resetting CTR module\n");
iowrite32be(CtrDrvrCommandRESET, &mmap->Command);
if (vme_bus_error_check(1))
printk(KERN_ERR PFX "Bus error Writing Command\n");
/* Wait at least 10 ms after a "reset", for the module to recover */
msleep(20);
}
int IntrHandler(void *arg) {
unsigned int source;
source = ClearInterrupt();
if (source & CtrDrvrInterruptMaskPPS)
printk(KERN_DEBUG PFX "Received PPS IRQ\n");
return 0;
}
void show_ctr_info(void)
{
printk(KERN_DEBUG PFX "CableId: %08x\n",
ioread32be(&mmap->CableId));
printk(KERN_DEBUG PFX "VhdlVersion: %08x\n",
ioread32be(&mmap->VhdlVersion));
printk(KERN_DEBUG PFX "InterruptEnable: %08x\n",
ioread32be(&mmap->InterruptEnable));
printk(KERN_DEBUG PFX "Status: %08x\n",
ioread32be(&mmap->Status));
printk(KERN_DEBUG PFX "Command: %08x\n",
ioread32be(&mmap->Command));
printk(KERN_DEBUG PFX "Setup: %08x\n",
ioread32be(&mmap->Setup));
printk(KERN_DEBUG PFX "PartityErrs: %08x\n",
ioread32be(&mmap->PartityErrs));
printk(KERN_DEBUG PFX "SyncErrs: %08x\n",
ioread32be(&mmap->SyncErrs));
printk(KERN_DEBUG PFX "TotalErrs: %08x\n",
ioread32be(&mmap->TotalErrs));
printk(KERN_DEBUG PFX "CodeViolErrs: %08x\n",
ioread32be(&mmap->CodeViolErrs));
printk(KERN_DEBUG PFX "QueueErrs: %08x\n",
ioread32be(&mmap->QueueErrs));
}
int ctrirq_init(void)
{
int rc;
if ((rc = vme_find_mapping(&ctr_desc, 1)) != 0) {
printk(KERN_ERR PFX "Failed to map CTR_n");
return rc;
}
vmeaddr = ctr_desc.kernel_va;
mmap = (CtrDrvrMemoryMap *) vmeaddr;
ResetCtr();
EnableCtrModule();
if ((rc = vme_request_irq(CTR_IRQ_VECTOR, IntrHandler, &ctr_desc,
"ctrirq")) != 0) {
printk(KERN_ERR PFX "Failed to register interrupt handler\n");
goto out_unmap;
}
EnableCtrInterrupt();
show_ctr_info();
return 0;
out_unmap:
DisableCtrModule();
if (vme_release_mapping(&ctr_desc, 1) != 0)
printk(KERN_WARNING PFX "Failed to release mapping on error\n");
return rc;
}
void ctrirq_exit(void)
{
DisableCtrInterrupt();
DisableCtrModule();
if (vme_free_irq(CTR_IRQ_VECTOR))
printk(KERN_WARNING PFX "Failed to free irq\n");
if (vme_release_mapping(&ctr_desc, 1) != 0)
printk(KERN_WARNING PFX "Failed to release mapping\n");
}
static int __init ctrirq_init_module(void)
{
return ctrirq_init();
}
static void __exit ctrirq_exit_module(void)
{
ctrirq_exit();
}
module_init(ctrirq_init_module);
module_exit(ctrirq_exit_module);
MODULE_AUTHOR("Sebastien Dugue");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("CTR 1KHz IRQ test module");
MODULE_VERSION(DRV_MODULE_VERSION);
#include <linux/version.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <asm-generic/iomap.h>
#include <linux/delay.h>
#include "../include/vmebus.h"
#include "ctrhard.h"
#define PFX "CtrPPSIrq: "
#define DRV_MODULE_VERSION "1.0"
static struct vme_mapping ctr_desc = {
.data_width = VME_D32,
.am = VME_A24_USER_DATA_SCT,
.read_prefetch_enabled = 0,
.sizeu = 0,
.sizel = 0x10000,
.vme_addru = 0,
.vme_addrl = 0xc00000
};
static void *vmeaddr = NULL; /* Point to CTR hardware */
CtrDrvrMemoryMap *mmap;
#define CTR_IRQ_LEVEL 2
#define CTR_IRQ_VECTOR 0xb8
unsigned int ClearInterrupt(void)
{
unsigned int source;
source = ioread32be((unsigned int *)&mmap->InterruptSource);
return source;
}
void EnableCtrInterrupt(void) {
unsigned int val;
printk(KERN_DEBUG PFX "Enabling PPS interrupt on IRQ%d Vector %d\n",
CTR_IRQ_LEVEL, CTR_IRQ_VECTOR);
/* Setup the IRQ level and vector */
val = (unsigned long)((CTR_IRQ_LEVEL << 8) | (CTR_IRQ_VECTOR & 0xff));
iowrite32be(val, &mmap->Setup);
if (vme_bus_error_check(1))
printk(KERN_ERR PFX "Bus error writing Setup\n");
/* Will interrupt once per second */
iowrite32be(CtrDrvrInterruptMaskPPS, &mmap->InterruptEnable);
if (vme_bus_error_check(1))
printk(KERN_ERR PFX "Bus error writing InterruptEnable\n");
val = ioread32be(&mmap->InterruptEnable);
if (vme_bus_error_check(1))
printk(KERN_ERR PFX "Bus error reading InterruptEnable\n");
if (val != CtrDrvrInterruptMaskPPS)
printk(KERN_DEBUG PFX "Failed to enable PPS interrupt %08x\n",
val);
}
void DisableCtrInterrupt(void)
{
printk(KERN_DEBUG PFX "Disabling PPS interrupt\n");
iowrite32be(0, &mmap->InterruptEnable);
if (vme_bus_error_check(1))
printk(KERN_ERR PFX "Bus error writing InterruptEnable\n");
}
void EnableCtrModule(void)
{
int val = ioread32be(&mmap->Command);
printk(KERN_DEBUG PFX "Enabling CTR module\n");
if (vme_bus_error_check(1))
printk(KERN_ERR PFX "Bus error reading Command\n");
val |= CtrDrvrCommandENABLE;
val &= ~CtrDrvrCommandDISABLE;
iowrite32be(val, &mmap->Command);
if (vme_bus_error_check(1))
printk(KERN_ERR PFX "Bus error writing Command\n");
}
void DisableCtrModule(void)
{
int val = ioread32be(&mmap->Command);
printk(KERN_DEBUG PFX "Disabling CTR module\n");
if (vme_bus_error_check(1))
printk(KERN_ERR PFX "Bus error reading Command\n");
val &= ~CtrDrvrCommandENABLE;
val |= CtrDrvrCommandDISABLE;
iowrite32be(val, &mmap->Command);
if (vme_bus_error_check(1))
printk(KERN_ERR PFX "Bus error writing Command\n");
}
void ResetCtr(void)
{
printk(KERN_DEBUG PFX "Resetting CTR module\n");
iowrite32be(CtrDrvrCommandRESET, &mmap->Command);
if (vme_bus_error_check(1))
printk(KERN_ERR PFX "Bus error Writing Command\n");
/* Wait at least 10 ms after a "reset", for the module to recover */
msleep(20);
}
int IntrHandler(void *arg) {
unsigned int source;
source = ClearInterrupt();
printk(KERN_DEBUG PFX "Received PPS IRQ source=%08x\n", source);
return 0;
}
void show_ctr_info(void)
{
printk(KERN_DEBUG PFX "CableId: %08x\n",
ioread32be(&mmap->CableId));
printk(KERN_DEBUG PFX "VhdlVersion: %08x\n",
ioread32be(&mmap->VhdlVersion));
printk(KERN_DEBUG PFX "InterruptEnable: %08x\n",
ioread32be(&mmap->InterruptEnable));
printk(KERN_DEBUG PFX "Status: %08x\n",
ioread32be(&mmap->Status));
printk(KERN_DEBUG PFX "Command: %08x\n",
ioread32be(&mmap->Command));
printk(KERN_DEBUG PFX "Setup: %08x\n",
ioread32be(&mmap->Setup));
printk(KERN_DEBUG PFX "PartityErrs: %08x\n",
ioread32be(&mmap->PartityErrs));
printk(KERN_DEBUG PFX "SyncErrs: %08x\n",
ioread32be(&mmap->SyncErrs));
printk(KERN_DEBUG PFX "TotalErrs: %08x\n",
ioread32be(&mmap->TotalErrs));
printk(KERN_DEBUG PFX "CodeViolErrs: %08x\n",
ioread32be(&mmap->CodeViolErrs));
printk(KERN_DEBUG PFX "QueueErrs: %08x\n",
ioread32be(&mmap->QueueErrs));
}
int ctrirq_init(void)
{
int rc;
if ((rc = vme_find_mapping(&ctr_desc, 1)) != 0) {
printk(KERN_ERR PFX "Failed to map CTR_n");
return rc;
}
vmeaddr = ctr_desc.kernel_va;
mmap = (CtrDrvrMemoryMap *) vmeaddr;
ResetCtr();
EnableCtrModule();
if ((rc = vme_request_irq(CTR_IRQ_VECTOR, IntrHandler, &ctr_desc,
"ctrirq")) != 0) {
printk(KERN_ERR PFX "Failed to register interrupt handler\n");
goto out_unmap;
}
EnableCtrInterrupt();
show_ctr_info();
return 0;
out_unmap:
DisableCtrModule();
if (vme_release_mapping(&ctr_desc, 1) != 0)
printk(KERN_WARNING PFX "Failed to release mapping on error\n");
return rc;
}
void ctrirq_exit(void)
{
DisableCtrInterrupt();
DisableCtrModule();
if (vme_free_irq(CTR_IRQ_VECTOR))
printk(KERN_WARNING PFX "Failed to free irq\n");
if (vme_release_mapping(&ctr_desc, 1) != 0)
printk(KERN_WARNING PFX "Failed to release mapping\n");
}
static int __init ctrirq_init_module(void)
{
return ctrirq_init();
}
static void __exit ctrirq_exit_module(void)
{
ctrirq_exit();
}
module_init(ctrirq_init_module);
module_exit(ctrirq_exit_module);
MODULE_AUTHOR("Sebastien Dugue");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("CTR PPS IRQ test module");
MODULE_VERSION(DRV_MODULE_VERSION);
/* ********************************************************************************************* */
/* */
/* CTR (Controls Timing Receiver) Public include file containing driver structures, that */
/* describe the CTR hardware layout. */
/* */
/* Julian Lewis 26th Aug 2003 */
/* */
/* ********************************************************************************************* */
#ifndef CTR_HARDWARE
#define CTR_HARDWARE
/* There are four possible hardware formats which influence the hardware resources */
/* Define one of these symbols in the make file accordingly. */
/* #define CTR_PMC Must also define CTR_PCI PMC is a special case of PCI */
/* #define CTR_PCI PCI module, could be PCI only or PMC, see above */
/* #define CTR_G64 */
/* #define CTR_VME */
/* Under the gcc we use on LynxOs either __LITTLE / BIG_ENDIAN__ is defined. */
/* Under Linux on the Intel platforms __i386__ is defined */
#ifdef __i386__
#ifndef __LITTLE_ENDIAN__
#define __LITTLE_ENDIAN__
#endif
#endif
/* ======================================================================================= */
/* Ctr basic event processing behaviour */
/* ======================================================================================= */
/* Each time an event arrives it is stored in the event history; except if its a telegram */
/* or millisecond event. Telegram events are stored in one of the incomming telegram */
/* buffers depending on the details of the header. Next the trigger table is searched */
/* and if a frame match is found, any telegram conditions are next checked. If the */
/* trigger fires, the appropriate counter configuration is overwritten if it is not locked.*/
/* If the counter history is not locked, the Frame, Trigger Index and Trigger Time fields */
/* are set up, and the StartTime and OnZeroTime are cleared. When the counter is started */
/* the StartTime is filled in, and when the counter reaches zero, the OnZeroTime is filled */
/* in and the LockHistory field in the counter control block is incremented, preventing it */
/* from being overwritten. Later the driver ISR may clear the LockHistory field once the */
/* counter history and trigger information has been obtained. */
/* ========================================================== */
/* Counter definitions */
/* ========================================================== */
/* Ways of addressing the Ctr counters and output channels */
/* Counter zero is not a real counter but is used to denote a */
/* direct action resulting from a trigger. Eg a bus interrupt */
/* triggered from an incomming event frame. The trigger can */
/* still produce bus interupts and hence looks like a counter */
/* in the counter history and configuration entries. */
typedef enum {
CtrDrvrCounter0, /* 0: Counter zero the direct trigger */
CtrDrvrCounter1, /* 1: First counter is counter one */
CtrDrvrCounter2, /* 2: Second counter */
CtrDrvrCounter3, /* 3: Third counter */
CtrDrvrCounter4, /* 4: Fourth counter */
CtrDrvrCounter5, /* 5: Fifth counter */
CtrDrvrCounter6, /* 6: Sixth counter */
CtrDrvrCounter7, /* 7: Seventh counter */
CtrDrvrCounter8, /* 8: Eighth is last counter CtrDrvrCOUNTERS */
CtrDrvrCOUNTERS /* Number of counters = Counter 0 and Real counters */
} CtrDrvrCounter;
/* Define a bit for each counter to be used in bit mask operations */
/* This is just (1 << counter) */
/* Then we decided to add straight through bits for 40MHz, X1 & X2 Clocks */
typedef enum {
CtrDrvrCounterMask0 = 0x01, /* Bit 0: Counter zero is a direct trigger bit */
CtrDrvrCounterMask1 = 0x02, /* Bit 1: Counter one bit */
CtrDrvrCounterMask2 = 0x04, /* Bit 2: Counter two bit */
CtrDrvrCounterMask3 = 0x08, /* Bit 3: Counter three bit */
CtrDrvrCounterMask4 = 0x10, /* Bit 4: Counter four bit */
CtrDrvrCounterMask5 = 0x20, /* Bit 5: Counter five bit */
CtrDrvrCounterMask6 = 0x40, /* Bit 6: Counter six bit */
CtrDrvrCounterMask7 = 0x80, /* Bit 7: Counter seven bit */
CtrDrvrCounterMask8 = 0x100, /* Bit 8: Counter eight bit */
/* These next signals can be routed directly to a counter output lemo */
CtrDrvrCounterMask40 = 0x200, /* Bit 9: 40MHz clock bit */
CtrDrvrCounterMaskX1 = 0x400, /* Bit 10: Ext-1 clock bit */
CtrDrvrCounterMaskX2 = 0x800, /* Bit 11: Ext-2 clock bit */
} CtrDrvrCounterMask;
/* In addition to the counters there are other sources of interrupt that can */
/* be enabled and connected to. */
#define CtrDrvrInterruptSOURCES 14
typedef enum {
CtrDrvrInterruptMaskCOUNTER_0 = 0x01,
CtrDrvrInterruptMaskCOUNTER_1 = 0x02,
CtrDrvrInterruptMaskCOUNTER_2 = 0x04,
CtrDrvrInterruptMaskCOUNTER_3 = 0x08,
CtrDrvrInterruptMaskCOUNTER_4 = 0x10,
CtrDrvrInterruptMaskCOUNTER_5 = 0x20,
CtrDrvrInterruptMaskCOUNTER_6 = 0x40,
CtrDrvrInterruptMaskCOUNTER_7 = 0x80,
CtrDrvrInterruptMaskCOUNTER_8 = 0x100,
CtrDrvrInterruptMaskPLL_ITERATION = 0x200,
CtrDrvrInterruptMaskGMT_EVENT_IN = 0x400,
CtrDrvrInterruptMaskPPS = 0x800,
CtrDrvrInterruptMask1KHZ = 0x1000,
CtrDrvrInterruptMaskMATCHED = 0x2000
} CtrDrvrInterruptMask;
/* Different possibilities for counter start. For the PMC version access to the */
/* external starts and clocks is via a seperate connector on the PMVC board. */
typedef enum {
CtrDrvrCounterStartNORMAL, /* 0: The next millisecond tick starts the counter */
CtrDrvrCounterStartEXT1, /* 1: The counter uses external start number one */
CtrDrvrCounterStartEXT2, /* 2: The counter uses external start number two */
CtrDrvrCounterStartCHAINED, /* 3: The output of the previous counter is the start */
CtrDrvrCounterStartSELF, /* 4: The output of this counter is the start (Divide) */
CtrDrvrCounterStartREMOTE, /* 5: The counter waits for a remote start, see later */
CtrDrvrCounterStartPPS, /* 6: The counter waits for the PPS */
CtrDrvrCounterStartCHAINED_STOP, /* 7: The counter is started by previous and stopped by next */
CtrDrvrCounterSTARTS /* 8: The number of possible counter starts */
} CtrDrvrCounterStart;
/* Different counter configuration modes for burst and multi start */
/* Divide can be obtained from SELF start and MULTIPLE mode. */
/* When the start is SELF, the first start is like NORMAL to start the loop. */
#define CtrDrvrCounterStopBURST CtrDrvrCounterStartEXT2
typedef enum {
CtrDrvrCounterModeNORMAL, /* 0: One load, one start and one output mode */
CtrDrvrCounterModeMULTIPLE, /* 1: One load, multiple starts, multiple outputs */
CtrDrvrCounterModeBURST, /* 2: Like MULTIPLE, but terminated by external start two */
CtrDrvrCounterModeMULT_BURST, /* 3: Both mode 1 & 2 = Multiple bursts */
CtrDrvrCounterMODES /* 4: There are 4 possible counter configuration modes */
} CtrDrvrCounterMode;
/* Different sources for the counter clock.For the PMC version access to the */
/* external starts and clocks is via a seperate connector on the PMVC board. */
typedef enum {
CtrDrvrCounterClock1KHZ, /* 0: The 1KHZ CTrain clock from the 01 events */
CtrDrvrCounterClock10MHZ, /* 1: Divided down 40MHZ phase synchronous with 1KHZ */
CtrDrvrCounterClock40MHZ, /* 2: Recovered 40MHZ by the PLL from the data edges */
CtrDrvrCounterClockEXT1, /* 3: External clock one */
CtrDrvrCounterClockEXT2, /* 4: External clock two */
CtrDrvrCounterClockCHAINED, /* 5: The output of the previous counter is the clock */
CtrDrvrCounterCLOCKS /* 6: The number of possible clock sources */
} CtrDrvrCounterClock;
/* What happens when the counter reaches zero, possible actions are... */
/* (00) No output or interrupt N.B. Counter output can still be used in chaining */
/* (01) Bus interrupt only */
/* (10) = External output */
/* (11) = Output and interrupt */
typedef enum {
CtrDrvrCounterOnZeroBUS = 0x01, /* Bit 0: On zero make a bus interrupt */
CtrDrvrCounterOnZeroOUT = 0x02 /* Bit 1: On zero make an output pulse */
} CtrDrvrCounterOnZero;
/* ==================================================================================== */
/* Counter configuration structure */
/* ==================================================================================== */
/* The counter configuration struc. The ctr has one active counter configuration block */
/* for each counter, these structures are updated either from the host processor, when */
/* the counter is under remote control, or, they can be overwritten when an incomming */
/* timing frames triggers. For each possible trigger, a RAM stored configuration block */
/* is copied to the counters active configuration block when the trigger fires. */
/* Note that counter zero uses only the field "OnZero". */
typedef struct {
CtrDrvrCounterOnZero OnZero; /* What to do on reaching zero. */
CtrDrvrCounterStart Start; /* The counters start. */
CtrDrvrCounterMode Mode; /* The counters operating mode. */
CtrDrvrCounterClock Clock; /* Clock specification. */
unsigned long PulsWidth; /* Number of 40MHz ticks, 0 = as fast as possible. */
unsigned long Delay; /* 32 bit delay to load into counter. */
} CtrDrvrCounterConfiguration;
/* N.B. If the PulseWidth is large, then the output starts to look like a level */
/* or a gate. The maximum PulsWidth is therefore important. */
/* OK The above configuration structure is too big, so using bit-fields we will make a */
/* more compact version occupying only two long words. */
#define CtrDrvrCounterConfigON_ZERO_MASK 0xC0000000 /* 2-Bits 0/Out/Bus/OutBus */
#define CtrDrvrCounterConfigSTART_MASK 0x38000000 /* 3-Bits Nor/X1/X2/Chain/Self/Rem/Chain_stop */
#define CtrDrvrCounterConfigMODE_MASK 0x06000000 /* 2-Bits Nor/Mult/Burst/Mult+Burst */
#define CtrDrvrCounterConfigCLOCK_MASK 0x01C00000 /* 3-Bits 1K/10M/40M/X1/X2/Chain */
#define CtrDrvrCounterConfigPULSE_WIDTH_MASK 0x003FFFFF /* 22-Bits 0..104 ms */
typedef struct {
unsigned long Config; /* Comprised of the above bit fields. */
unsigned long Delay; /* 32 bit delay to load into counter. */
} CtrDrvrHwCounterConfiguration; /* Compact form in two longs */
/* ==================================================================================== */
/* The counter configuration table contains all the counter configurations that can be */
/* copied to the active configuration when they are triggered. This data is located in */
/* the RAM along with the TriggerTable, see below. */
/* ==================================================================================== */
/* Both the Configuration table and the Trigger table have the same size and they are */
/* both located in RAM, they share the same index. */
#define CtrDrvrRamTableSIZE 2048
/* Ram base configuration table, each configuration has a corresponding entry in the */
/* trigger table with the same index, it is also located in Ram. */
typedef CtrDrvrHwCounterConfiguration CtrDrvrRamConfigurationTable[CtrDrvrRamTableSIZE];
/* =============================================================================== */
/* Remote control bits to operate counters directly from host. */
/* The ctrp can do a lot of usefull things even if it is not connected to the */
/* timing cable. It can still drive counters, make outputs and trains and is able */
/* to provoke bus interrupts. */
/* =============================================================================== */
/* When LockConfig is set in the control block, the triggers can't overwrite the */
/* counter configuration. It is completely under remote control, however, it is */
/* always possible to control the counter this way, even if not locked. */
/* These bits are set directly from the host in the counters control block */
/* located in the FPGA, see below. The corresponding FPGA location is write */
/* only, a rising edge on one of these bits provokes the corresponding */
/* action on the counter. */
#define CtrDrvrREMOTES 5
typedef enum {
CtrDrvrRemoteLOAD = 0x01, /* Bit 1: Load the counter remotely */
CtrDrvrRemoteSTOP = 0x02, /* Bit 2: Remote kill counter no matter what its state */
CtrDrvrRemoteSTART = 0x04, /* Bit 3: Start the counter remotely */
CtrDrvrRemoteOUT = 0x08, /* Bit 4: Make output on output pin, counter not disturbed */
CtrDrvrRemoteBUS = 0x10, /* Bit 5: Make a bus interrupt now, counter not disturbed */
CtrDrvrRemoteEXEC = 0x20 /* Bit 6: Execute remote command else set mask */
} CtrDrvrRemote;
/* =============================================================================== */
/* Event frames */
/* =============================================================================== */
/* Layout of an event frame, it occupies one 32bit frame on the timing cable. */
typedef unsigned long CtrDrvrFrameLong;
#ifdef __LITTLE_ENDIAN__
typedef struct {
unsigned short Value; /* Can be a WILD card in which case value is ignored */
unsigned char Code;
unsigned char Header; /* Like 01 for C-Train, usually Mch:4 Type:4 */
} CtrDrvrFrameStruct;
#else
typedef struct {
unsigned char Header; /* Like 01 for C-Train, usually Mch:4 Type:4 */
unsigned char Code;
unsigned short Value; /* Can be a WILD card in which case value is ignored */
} CtrDrvrFrameStruct;
#endif
typedef union {
CtrDrvrFrameLong Long;
CtrDrvrFrameStruct Struct;
} CtrDrvrEventFrame;
/* The "Value" field can be set to "WILD" in which case it is not included in the */
/* trigger test logic, however the Full event is stored in the counter history. */
#define CtrDrvrEventWILD 0xFFFF;
/* There are different accelerators on the CPS timing network. Here we make the */
/* definition of which accelerator corresponds to the 6 positions on the ctr */
/* module. In general this assignement is in order of priority, highest energy */
/* comes first, followed by its injectors. */
typedef enum {
CtrDrvrMachineNONE = 0, /* 0: No machine value */
CtrDrvrMachineLHC = 1, /* 1: LHC */
CtrDrvrMachineSPS = 2, /* 2: SPS new or legacy */
CtrDrvrMachineCPS = 3, /* 3: PS or SCT */
CtrDrvrMachinePSB = 4, /* 4: Booster or FCT */
CtrDrvrMachineLEI = 5, /* 5: Leir Ion ring */
CtrDrvrMachineADE = 6, /* 6: ADE Anti proton dec */
CtrDrvrMachine777 = 7, /* 7: Not used yet */
CtrDrvrMachineMACHINES = 7 /* Number of machines, hence telegram buffers */
} CtrDrvrMachine;
/* Machines currently in the CTF (CLIC Test Facility) network */
/* CLIC is the Compact LInear Collider and runs independently */
#define CtrDrvrMachineSCT CtrDrvrMachineCPS /* Slow CLIC telegram */
#define CtrDrvrMachineFCT CtrDrvrMachinePSB /* Fast CLIC telegram */
/* Most of these event types will be suppressed leaving only SIMPLE and TELEGRAM */
/* These types are active when the first nibble in the event frame has a machine */
/* code in it in the range 1..7 */
typedef enum {
CtrDrvrMachineEventTypeSC_NUM = 0,
CtrDrvrMachineEventTypeSPS_SIMPLE = 1,
CtrDrvrMachineEventTypeSPS_SSC = 2,
CtrDrvrMachineEventTypeTELEGRAM = 3,
CtrDrvrMachineEventTypeSIMPLE = 4,
CtrDrvrMachineEventTypeTCU = 5,
CtrDrvrMachineEventTypeTYPES = 6
} CtrDrvrMachineEventType;
/* Again most of this is legacy stuff, only MILLI_SECOND, CABLE_ID, and UTC will remain */
/* Special events occur when the first nibble is zero. */
typedef enum {
CtrDrvrSpecialEventTypeMILLI_SECOND = 1,
CtrDrvrSpecialEventTypeSPS_SECOND = 2,
CtrDrvrSpecialEventTypeSPS_MINUTE = 3,
CtrDrvrSpecialEventTypeSPS_HOUR = 4,
CtrDrvrSpecialEventTypeSPS_DAY = 5,
CtrDrvrSpecialEventTypeSPS_MONTH = 6,
CtrDrvrSpecialEventTypeSPS_YEAR = 7,
CtrDrvrSpecialEventTypePS_DATE = 8,
CtrDrvrSpecialEventTypePS_TIME = 9,
CtrDrvrSpecialEventTypeCABLE_ID = 10,
CtrDrvrSpecialEventTypeUTC = 11,
CtrDrvrSpecialEventTypeTYPES = 11
} CtrDrvrSpecialEventType;
/* =============================================================================== */
/* Special headers and codes recognized by hardware */
/* =============================================================================== */
/* Each millisecond the UTC time is incremented by 1ms, and the CTime register is */
/* set to the 24 bit binary value. All transactions and actions a synchronized to */
/* this clock. */
#define CtrDrvrMILLISECOND_HEADER 0x01
/* When the MTG_CABLE_ID event arrives, the "Value" is stored in the CableId */
/* FPGA register in the memory map. */
#define CtrDrvrMTG_CABLEID_HEADER 0x0A
/* The UTC time in seconds is transmitted in two frames 0xB5<MSW> 0xB6<LSW> */
/* the B6 event validates the time, B5 comes first in any millisecond. */
#define CtrDrvrUTC_TIME_MSW_CODE 0xB5 /* MS 16 bits of UTC second */
#define CtrDrvrUTC_TIME_LSW_CODE 0xB6 /* Validates the time */
/* Store the "Value" of these events in the incomming telegram buffer for the */
/* machine nibble in the header, at the position indicated by the "Code". The */
/* first nibble is the machine, the second nibble in the header byte is the */
/* fixed value "3" indicating its a telegram. */
#define CtrDrvrTELEGRAM_HEADER(machine) (0x03 & (machine<<4))
#define CtrDrvrTELEGRAM_LHC_HEADER 0x13
#define CtrDrvrTELEGRAM_SPS_HEADER 0x23
#define CtrDrvrTELEGRAM_CPS_HEADER 0x33
#define CtrDrvrTELEGRAM_PSB_HEADER 0x43
#define CtrDrvrTELEGRAM_LEI_HEADER 0x53
#define CtrDrvrTELEGRAM_ADE_HEADER 0x63
#define CtrDrvrTELEGRAM_777_HEADER 0x73
/* Put these in the event history, search the triggers. The "Code" is the event */
/* code and the "Value" may carry other data. */
/* Wild cards can be used to mask the "Value". */
#define CtrDrvrSIMPLE_HEADER(machine) (0x04 & (machine<<4))
#define CtrDrvrSIMPLE_LHC_HEADER 0x14
#define CtrDrvrSIMPLE_SPS_HEADER 0x24
#define CtrDrvrSIMPLE_CPS_HEADER 0x34
#define CtrDrvrSIMPLE_PSB_HEADER 0x44
#define CtrDrvrSIMPLE_LEI_HEADER 0x54
#define CtrDrvrSIMPLE_ADE_HEADER 0x64
#define CtrDrvrSIMPLE_777_HEADER 0x74
/* One special simple event, "TELEGRAM_READY" is used to swap between the incomming */
/* and active telegram buffers. The machine is in the first nibble of the header */
/* and the second nibble is set to "4". The code is set to "FE", and the "Value" */
/* can be ignored. */
#define CtrDrvrSIMPLE_TELEGRAM_READY_CODE 0xFE
#define CtrDrvrSIMPLE_TELEGRAM_READY_FRAME(machine) (0x04FE0000 & (machine<<28))
/* =============================================================================== */
/* We need to define time stamps, they include the UTC second and the nano second */
/* in the UTC second, it also includes the C-Train machine time value. */
/* =============================================================================== */
typedef struct {
unsigned long Second; /* UTC Second */
unsigned long TicksHPTDC; /* 25/32 ns ticks, zero if no HPTDC */
} CtrDrvrTime;
/* Sometimes we want not just the UTC time but the milli second modulo value as well */
typedef struct {
unsigned long CTrain; /* Milli second modulo at Time */
CtrDrvrTime Time;
} CtrDrvrCTime;
/* =============================================================================== */
/* These counter configurations are implemented in the FPGA and really control the */
/* counters actively. Writing here really affects the running counters, and this */
/* permits controlling the counter remotely, and routing its output to the ctrp */
/* out put pins on the connector. */
/* =============================================================================== */
/* These FPGA resident structures control counters. Each time a trigger occurs, */
/* the ctr examines first the LockConfig field, if it is non zero, the trigger */
/* is completely ignored. If it is zero, then the ctr examines the LockHistory, */
/* if it is non zero, the counter history is not overwritten, but the counter */
/* configuration is overwritten as usual. If the LockHistory is zero, then the */
/* ctr fills in the counter history as usual. In both cases, zero and non zero, */
/* at OnZeroTime, it increments the LockHistory flag. It will be up to the host */
/* to read and clear the LockHistory flag. This flag is cleared by the CTR on */
/* reading. If the host sets the LockConfig flag then the the counter is under */
/* REMOTE control. */
/* Note the OutMask is used to rout a counters output to physical output pins. */
/* This permits doing a wire-or inside the CTR. Note also that if the OutMask */
/* contains a zero value the counter makes no output even if the OnZero field */
/* has the OnZeroOUT bit set in its configuration. */
typedef struct {
/* The two lock flags give priority to bus accesses */
unsigned long LockConfig; /* Triggers do not overwrite the config */
unsigned long LockHistory; /* CLEAR ON READ: Dont overwrite history */
CtrDrvrRemote Remote; /* Used to remote control the counter */
CtrDrvrCounterMask OutMask; /* Physical output pins */
} CtrDrvrCounterControl;
/* Pack Remote and OutMask fields into a long for the hardware */
typedef struct {
unsigned long LockConfig; /* Triggers do not overwrite the config */
unsigned long LockHistory; /* CLEAR ON READ: Dont overwrite history */
unsigned long RemOutMask; /* Remote and hardware mask combined */
} CtrDrvrHwCounterControl;
#define CtrDrvrCntrCntrlREMOTE_MASK 0x3F
#define CtrDrvrCntrCntrlOUT_MASK 0x3FFC0
#define CtrDrvrCntrCntrlTTL_BAR 0x80000000
/* The counter history is only one entry per counter deep, it is protected by the */
/* LockHistory flag in the control block. If this flag is non zero, their is a valid */
/* history to be read. If it is greater than one, the history is not up to date. The */
/* main use of history is within the host ISR, if interrupts come too fast, then */
/* some histories will be missed. */
/* The incomming event frame that provoked the trigger needs to be stored. This */
/* is because some interesting data may be contained in its value part when wild */
/* cards are in use. See Frame field */
typedef struct {
unsigned long Index; /* Index into trigger table of loading trigger */
CtrDrvrEventFrame Frame; /* The actual frame (without Wild Cards !) */
CtrDrvrCTime TriggerTime; /* Time counter got loaded */
CtrDrvrCTime StartTime; /* Time counter start arrived. */
CtrDrvrCTime OnZeroTime; /* Time the counter reaced zero, or interrupted */
} CtrDrvrCounterHistory;
/* The hardware active implementation in the FPGA of a counter, it contains a control */
/* block, a history, and the actual counter configuration. */
typedef struct {
CtrDrvrHwCounterControl Control;
CtrDrvrCounterHistory History;
CtrDrvrHwCounterConfiguration Config;
} CtrDrvrFpgaCounter;
/* Now we instantiate one Fpga configuration for each of the real counters and for */
/* counter zero, when direct action triggers fire. Eg when an incomming event frame */
/* provokes a bus interrupt we use counter zero. */
typedef CtrDrvrFpgaCounter CtrDrvrCounterBlock[CtrDrvrCOUNTERS];
/* ============================================================================ */
/* Before we can define the trigger condition of a counter we must first define */
/* a few more enumerations. */
/* ============================================================================ */
typedef enum {
CtrDrvrTriggerConditionNO_CHECK, /* Do not check the telegram */
CtrDrvrTriggerConditionEQUALITY, /* Trigger telegram condition must equal the telegram */
CtrDrvrTriggerConditionAND, /* Trigger telegram condition AND telegram must be non zero */
CtrDrvrTriggerCONDITIONS /* There are 3 kinds of trigger conditions */
} CtrDrvrTriggerCondition;
/* We need to define a telegram at this point. A telegram describes the current */
/* machine cycle as a sequential list of parameters transmitted over the timing */
/* network each basic period. Each machine has its own telegram. */
#define CtrDrvrTgmGROUP_VALUES 64
typedef unsigned short CtrDrvrTgm[CtrDrvrTgmGROUP_VALUES]; /* 64x16 bit values */
typedef CtrDrvrTgm CtrDrvrTgmBlock[CtrDrvrMachineMACHINES]; /* One telegram per machine */
typedef struct {
unsigned short GroupNumber; /* The serial position in the telegram starting at one */
unsigned short GroupValue; /* The value of the parameter at this position */
} CtrDrvrTgmGroup;
/* OK now we define a trigger. This consists of an event frame and a telegram condition. */
/* This trigger is too big to fit into the FPGA, so we keep this structure for the */
/* driver API, and declare a second version packed into bit fields. */
typedef struct {
unsigned long Ctim; /* Ctim equipment number */
CtrDrvrEventFrame Frame; /* Timing frame with optional wild card */
CtrDrvrTriggerCondition TriggerCondition; /* Type of trigger */
CtrDrvrMachine Machine; /* Which telegram if used */
CtrDrvrCounter Counter; /* Which counter to configure */
CtrDrvrTgmGroup Group; /* Trigger telegram condition */
} CtrDrvrTrigger; /* Used in driver API */
/* Now define a compact form of trigger as packed bits in two longs. */
/* CtrDrvrTriggerCondition needs 2-bits 0 (No Check) 1 (Equal) 2 (And) */
/* CtrDrvrMachine needs 3 bits 0(No) 1(LHC) 2(SPS) ... 7(Not used yet) */
/* CtrDrvrCounter needs 4 bits 0(NO) 1(1) ... 8(8) */
/* The number of Tgm groups is 64 needs 6 bits, we will use 7 */
/* The group value is 16 bits */
#define CtrDrvrTrigCONDITION_MASK 0xC0000000
#define CtrDrvrTrigMACHINE_MASK 0x38000000
#define CtrDrvrTrigCOUNTER_MASK 0x07800000
#define CtrDrvrTrigGROUP_NUMBER_MASK 0x007F0000
#define CtrDrvrTrigGROUP_VALUE_MASK 0x0000FFFF
typedef struct {
CtrDrvrEventFrame Frame; /* Timing frame with optional wild card */
unsigned long Trigger; /* Packed trigger bits as above */
} CtrDrvrHwTrigger; /* Compact hardware form of a trigger */
/* This trigger table is searched by the ctr hardware for each incomming event. */
/* If the trigger fires, the corresponding counter configuration from the counter */
/* configuration table is copied the the active counter configuration specified by */
/* counter. */
typedef CtrDrvrHwTrigger CtrDrvrRamTriggerTable[CtrDrvrRamTableSIZE];
/* ==================================================== */
/* This structure is very low on the whish list, so if */
/* we have time and hardware resources enough, then OK, */
/* otherwise we don't need it. It will be in RAM. */
/* ==================================================== */
#define CtrDrvrHISTORY_TABLE_SIZE 1024
/* Each entry in the history buffer contains the frame */
/* excluding telegram and millisecond events. An entry */
/* also contains the UTC and Millisecond modulo arrival */
/* time of the events. */
typedef struct {
CtrDrvrEventFrame Frame;
CtrDrvrCTime CTime;
} CtrDrgvrEventHistoryEntry;
/* The Index field points to the next write address, it */
/* behaves like a ring buffer after entry 1023, the next */
/* entry address is zero. */
typedef struct {
unsigned long Index;
CtrDrgvrEventHistoryEntry Entries[CtrDrvrHISTORY_TABLE_SIZE];
} CtrDrvrEventHistory;
/* ==================================================== */
/* The status and alarm bit meanings */
/* ==================================================== */
/* Alarms and Status bits */
#define CtrDrvrSTATAE 12
#define CtrDrvrHwStatusMASK 0x3FF
#define CtrDrvrTypeStatusMask 0x380
typedef enum {
CtrDrvrStatusGMT_OK = 0x01, /* No GMT errors */
CtrDrvrStatusPLL_OK = 0x02, /* PLL locked on GMT */
CtrDrvrStatusEXT_CLK_1_OK = 0x04, /* Ext clock present */
CtrDrvrStatusEXT_CLK_2_OK = 0x08, /* Ext clock present */
CtrDrvrStatusSELF_TEST_OK = 0x10, /* Self test OK */
CtrDrvrStatusENABLED = 0x20, /* Module enabled */
CtrDrvrStatusHPTDC_IN = 0x40, /* HPTDC chip installed */
CtrDrvrStatusCTRI = 0x80, /* This is a CTRI module */
CtrDrvrStatusCTRP = 0x100, /* This is a CTRP module */
CtrDrvrStatusCTRV = 0x200, /* This is a CTRV module */
/* Software status bits */
CtrDrvrStatusNO_LOST_INTERRUPTS = 0x400, /* Interrupts too fast */
CtrDrvrStatusNO_BUS_ERROR = 0x800 /* Module has not seen a bus error */
} CtrDrvrStatus;
typedef enum {
CtrDrvrIoStatusCTRXE = 0x0001, /* Beam Energy extension */
CtrDrvrIoStatusCTRXI = 0x0002, /* IO extension */
CtrDrvrIoStatusV1_PCB = 0x0004, /* PCB version 1 */
CtrDrvrIoStatusV2_PCB = 0x0008, /* PCB version 2 */
CtrDrvrIoStatusS1 = 0x0010, /* External start 1 input value */
CtrDrvrIoStatusS2 = 0x0020, /* External start 2 input value */
CtrDrvrIoStatusX1 = 0x0040, /* External clock 1 input value */
CtrDrvrIoStatusX2 = 0x0080, /* External clock 2 input value */
CtrDrvrIoStatusO1 = 0x0100, /* Lemo output 1 state */
CtrDrvrIoStatusO2 = 0x0200, /* Lemo output 2 state */
CtrDrvrIoStatusO3 = 0x0400, /* Lemo output 3 state */
CtrDrvrIoStatusO4 = 0x0800, /* Lemo output 4 state */
CtrDrvrIoStatusO5 = 0x1000, /* Lemo output 5 state */
CtrDrvrIoStatusO6 = 0x2000, /* Lemo output 6 state */
CtrDrvrIoStatusO7 = 0x4000, /* Lemo output 7 state */
CtrDrvrIoStatusO8 = 0x8000, /* Lemo output 8 state */
/* Added by pablo */
CtrDrvrIOStatusIDOkP = 0x10000, /* PCB Id read Ok */
CtrDrvrIOStatusDebugHistory = 0x20000, /* Debug History Mode On */
CtrDrvrIOStatusUtcPllEnabled = 0x40000, /* Utc Pll Enabled 0=Brutal 1=Soft lock */
CtrDrvrIOStatusExtendedMemory = 0x80000, /* Extended memory for VHDL revision March 2008 */
CtrDrvrIOStatusTemperatureOk = 0x100000 /* Sensor present */
} CtrDrvrIoStatus;
#define CtrDrvrIoSTATAE 21
/* ==================================================== */
/* Command bits for ctrp module */
/* ==================================================== */
typedef enum {
CtrDrvrCommandRESET = 0x01, /* Do a hardware reset */
CtrDrvrCommandENABLE = 0x02, /* Enable timing input */
CtrDrvrCommandDISABLE = 0x04, /* Disable timing input */
CtrDrvrCommandLATCH_UTC = 0x08, /* Prior to reading it */
CtrDrvrCommandSET_UTC = 0x10, /* When no timing connected */
CtrDrvrCommandSET_HPTDC = 0x20, /* Say HPTDC detected */
/* Added by pablo */
CtrDrvrCommandDISABLE_HPTDC = 0x40, /* Say HPTDC disabled */
CtrDrvrCommandDebugHisOn = 0x80, /* Enable Debug History Mode */
CtrDrvrCommandDebugHisOff = 0x100, /* Disable Debug History Mode */
CtrDrvrCommandUtcPllOff = 0x200, /* Disable Utc Pll Brutal On */
CtrDrvrCommandUtcPllOn = 0x400 /* Enable Utc Pll Brutal Off */
} CtrDrvrCommand;
/* ==================================================== */
/* Pll Debug information */
/* ==================================================== */
typedef struct {
unsigned long Error; /* RO: Pll phase error */
unsigned long Integrator; /* RW: Pll Integrator */
unsigned long Dac; /* RO: Value applied to DAC */
unsigned long LastItLen; /* RO: Last iteration length */
unsigned long Phase; /* RW: Pll phase */
unsigned long NumAverage; /* RW: Numeric average */
unsigned long KP; /* RW: Constant of proportionality */
unsigned long KI; /* RW: Constant of integration */
} CtrDrvrPll;
/* The PLL asynchronous period is needed to convert PLL units into */
/* time values. This value is kept a a constant in the driver to */
/* be used in PLL calculations as follows ... */
/* ErrorNs = (Error*PllAsyncPeriodNs)/NumAverage */
/* PhaseNs = (Phase*PllAsyncPeriodNs)/NumAverage */
typedef float CtrDrvrPllAsyncPeriodNs;
/* ==================================================== */
/* Module statistics */
/* ==================================================== */
typedef struct {
unsigned long PllErrorThreshold; /* Pll error threshold to generate a pll error RW */
unsigned long PllDacLowPassValue; /* Low Passed DAC value used if the GMT is missed (CIC filter) RO */
unsigned long PllDacCICConstant; /* Log2 of the number of avarages of the DAC Value CIC low pass filter RW */
unsigned long PllMonitorCICConstant; /* Log2 of the interrupt reduction factor (averages over this count) RW */
unsigned long PhaseDCM; /* (Not used yet) Phase of the spartan DLL. Not used in the standard VHDL RW */
unsigned long UtcPllPhaseError; /* Phase error Utc */
unsigned long Temperature; /* Card temperature. Only valid in CTRIs V3 RO */
unsigned long MsMissedErrs; /* Number of millisencods missed since last power up. (Latches the time) RO */
CtrDrvrTime LastMsMissed;
unsigned long PllErrors; /* Number of pll errors since last power up (past threshold ) RO */
CtrDrvrTime LastPllError;
unsigned long MissedFrames; /* Number of missed frames since last power up */
CtrDrvrTime LastFrameMissed;
unsigned long BadReceptionCycles; /* Number of bad reception cycles since last power up */
unsigned long ReceivedFrames; /* Number of received frames since last power up */
unsigned long SentFramesEvent; /* Last Sent Frames Event */
unsigned long UtcPllErrs; /* Number of utc pll errors since last reset */
CtrDrvrTime LastExt1Start; /* External Start 1 time tag */
} CtrDrvrModuleStats;
/* ==================================================== */
/* The ctrp memory map */
/* ==================================================== */
/* N.B. For efficiency in the ISR, the source field must be the first. */
typedef struct {
/* FPGA resident structures */
CtrDrvrInterruptMask InterruptSource; /* Cleared on read */
CtrDrvrInterruptMask InterruptEnable; /* Enable interrupts */
unsigned long HptdcJtag; /* Jtag control reg for HpTdc */
/* See below for the meaning of these fields */
unsigned long InputDelay; /* Specified in 40MHz ticks */
unsigned long CableId; /* ID of CTG piloting GMT */
unsigned long VhdlVersion; /* UTC time of VHDL build */
unsigned long OutputByte; /* Output byte number 1..8 on VME P2 + Enable front pannel */
CtrDrvrStatus Status; /* Current status */
CtrDrvrCommand Command; /* Write command to module */
CtrDrvrPll Pll; /* Pll parameters */
CtrDrvrCTime ReadTime; /* Latched date time and CTrain */
unsigned long SetTime; /* Used to set UTC if no cable */
CtrDrvrCounterBlock Counters; /* The counter configurations and status */
CtrDrvrTgmBlock Telegrams; /* Active telegrams */
/* RAM resident structures, they must be read/write */
CtrDrvrRamTriggerTable Trigs; /* 2048 Trigger conditions */
CtrDrvrRamConfigurationTable Configs; /* 2048 Counter configurations */
CtrDrvrEventHistory EventHistory; /* 1024 Events deep */
unsigned long Setup; /* VME Level and Interrupt vector */
unsigned long LastReset; /* UTC Second of last reset */
unsigned long PartityErrs; /* Number of parity errors since last reset */
unsigned long SyncErrs; /* Number of frame synchronization errors since last reset */
unsigned long TotalErrs; /* Total number of IO errors since last reset */
unsigned long CodeViolErrs; /* Number of code violations since last reset */
unsigned long QueueErrs; /* Number of input Queue overflows since last reset */
/* New registers used only in recent VHDL, old makes a bus error ! */
CtrDrvrIoStatus IoStat; /* IO status */
unsigned long IdLSL; /* ID Chip value Least Sig 32-bits */
unsigned long IdMSL; /* ID Chip value Most Sig 32-bits */
CtrDrvrModuleStats ModStats; /* Module statistics */
} CtrDrvrMemoryMap;
/* ===================================================================== */
/* InputDelay: This is the delay in 40MHz ticks used to compensate for */
/* the transmission delay from the CTGU to this particular card. The */
/* CTGU advances UTC time be 100us, and hence the InputDelay can be */
/* calculated as: InputDelay = 100us - TransmissionDelay. */
/* ===================================================================== */
/* CableId: This number is transmitted over the timing cable to indicate */
/* which one of the eight cables the CTR is attached. See CtrDrvrMachine */
/* ===================================================================== */
/* VhdlVersion: This is the UTC second when the VHDL program was */
/* compiled into the FPGA bit stream. */
/* ===================================================================== */
/* OutputByte: In the VME version of the CTR, the eight counter outputs */
/* can be placed on one byte of the P2 connector. If this value is zero */
/* the CTR does not drive the P2 connector, a value between 1..4 selects */
/* the byte number in the 64bit P2 VME bus. */
/* ===================================================================== */
#endif
#include <linux/version.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/delay.h>
#include "../include/vmebus.h"
#define PFX "LoopIrq: "
#define DRV_MODULE_VERSION "1.0"
static int irq_level = -1;
static int irq_vector = -1;
#define DEFAULT_IRQ_LEVEL 7
#define DEFAULT_IRQ_VECTOR 0xaa
int handler(void *arg)
{
printk(KERN_DEBUG PFX "Received IRQ%d Vector %d\n",
DEFAULT_IRQ_LEVEL, DEFAULT_IRQ_VECTOR);
return 0;
}
static int __init testirq_init_module(void)
{
int rc;
int i;
if ((irq_level < 1) || (irq_level > 7))
irq_level = DEFAULT_IRQ_LEVEL;
if ((irq_vector < 0) || (irq_vector > 255))
irq_vector = DEFAULT_IRQ_VECTOR;
printk(KERN_INFO "VME IRQ loopback test V%s - IRQ%d Vector %d\n",
DRV_MODULE_VERSION, irq_level, irq_vector);
if ((rc = vme_request_irq(irq_vector, handler, NULL,
"loopirq")) != 0) {
printk(KERN_ERR PFX "Failed to register interrupt handler\n");
return -ENODEV;
}
for (i = 0; i < 10; i++) {
printk(KERN_DEBUG PFX "Generating VME IRQ%d Vector %d\n",
irq_level, irq_vector);
if (vme_generate_interrupt(irq_level, irq_vector, 100)) {
printk(KERN_ERR PFX "IACK timeout.\n");
break;
}
msleep(1500);
}
return 0;
}
static void __exit testirq_exit_module(void)
{
vme_free_irq(irq_vector);
}
module_init(testirq_init_module);
module_exit(testirq_exit_module);
/* Module parameters */
module_param(irq_level, int, S_IRUGO);
MODULE_PARM_DESC(irq_level, "VME IRQ level in the range 1-7");
module_param(irq_vector, int, S_IRUGO);
MODULE_PARM_DESC(irq_vector, "VME IRQ vector in the range 0-255");
MODULE_AUTHOR("Sebastien Dugue");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("VME IRQ loopback test module");
MODULE_VERSION(DRV_MODULE_VERSION);
/*
* VMEBus Monitor
*
* This module is intended to be a multi-purpose VME diagnostic tool.
*
* Currently it supports:
* - VME Bus Error notification
* - Debugging of creation/removal of VME mappings
*
* Copyright (c) 2010 Emilio G. Cota <cota@braap.org>
*/
#include <linux/module.h>
#include <linux/err.h>
#include "../include/vmebus.h"
#define PFX "VMEMon: "
#define VMEMON_MAPPINGS_MAX 20
static int am = VME_A32_USER_DATA_SCT;
static unsigned int size = 1024;
static unsigned int offset = 0;
module_param(am, int, 0644);
MODULE_PARM_DESC(am, "Address Modifier");
module_param(size, int, 0644);
MODULE_PARM_DESC(size, "Size of the region of interest");
module_param(offset, int, 0644);
MODULE_PARM_DESC(offset, "Offset of the region of interest");
static int addr_mod[VMEMON_MAPPINGS_MAX];
static int addr_mod_num;
MODULE_PARM_DESC(addr_mod, "Enumeration of address modifiers to map");
module_param_array(addr_mod, int, &addr_mod_num, 0);
static int data_width[VMEMON_MAPPINGS_MAX];
static int data_width_num;
MODULE_PARM_DESC(data_width, "Enumeration of data widths to map");
module_param_array(data_width, int, &data_width_num, 0);
static int base_addr[VMEMON_MAPPINGS_MAX];
static int base_addr_num;
MODULE_PARM_DESC(base_addr, "Enumeration of VME addresses to be mapped");
module_param_array(base_addr, int, &base_addr_num, 0);
static int window_size[VMEMON_MAPPINGS_MAX];
static int window_size_num;
MODULE_PARM_DESC(window_size, "Enumeration of window sizes to map");
module_param_array(window_size, int, &window_size_num, 0);
static long kaddr[VMEMON_MAPPINGS_MAX];
static struct vme_berr_handler *handler;
static void berr_handler(struct vme_bus_error *error)
{
printk(KERN_INFO PFX "Bus Error @ 0x%llx am=0x%x\n",
(unsigned long long)error->address, error->am);
}
static void __init vmemon_pdparam_init(struct pdparam_master *param)
{
memset(param, 0, sizeof(*param));
param->iack = 1; /* no iack */
param->rdpref = 0; /* no VME read prefetch option */
param->wrpost = 0; /* no VME write posting option */
param->swap = 1; /* VME auto swap option */
param->sgmin = 0; /* reserved, _must_ be 0 */
param->dum[0] = VME_PG_SHARED; /* window is shareable */
param->dum[1] = 0; /* XPC ADP-type */
param->dum[2] = 0; /* reserved, _must_ be 0 */
}
static void __init vmemon_map(void)
{
struct pdparam_master pdp;
int i;
if (!addr_mod_num)
return;
if (data_width_num != addr_mod_num ||
base_addr_num != addr_mod_num ||
window_size_num != addr_mod_num) {
printk(KERN_INFO PFX "Invalid mapping input, aborting.\n");
addr_mod_num = 0;
return;
}
vmemon_pdparam_init(&pdp);
for (i = 0; i < addr_mod_num; i++) {
unsigned long dw = data_width[i] / 8;
kaddr[i] = find_controller(base_addr[i], window_size[i],
addr_mod[i], 0, dw, &pdp);
if (kaddr[i] == -1) {
printk(KERN_ERR PFX "Mapping %d failed: base=0x%08x "
"size=0x%08x am=%d dw=%d\n", i, base_addr[i],
window_size[i], addr_mod[i], data_width[i]);
kaddr[i] = 0;
continue;
}
}
}
static int __init vmemon_init(void)
{
struct vme_bus_error error;
error.address = offset;
error.am = am;
printk(KERN_INFO PFX "Registering berr handler, "
"offset=0x%x size=0x%d am=0x%x\n", offset, size, am);
handler = vme_register_berr_handler(&error, size, berr_handler);
if (IS_ERR(handler)) {
printk(KERN_ERR PFX "Could not register Berr handler: %ld.\n",
PTR_ERR(handler));
return PTR_ERR(handler);
}
vmemon_map();
printk(KERN_INFO PFX "Berr handler registered successfully\n");
return 0;
}
static void __exit vmemon_unmap(void)
{
int i;
if (!addr_mod_num)
return;
printk(KERN_INFO PFX "Returning %d mappings", addr_mod_num);
for (i = 0; i < addr_mod_num; i++) {
if (return_controller(kaddr[i], window_size[i])) {
printk(KERN_WARNING PFX "Couldn't unmap %d.", i);
continue;
}
kaddr[i] = 0;
}
}
static void __exit vmemon_exit(void)
{
printk(KERN_INFO PFX "Unregistering berr handler\n");
vme_unregister_berr_handler(handler);
vmemon_unmap();
}
module_init(vmemon_init);
module_exit(vmemon_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Emilio G. Cota");
MODULE_DESCRIPTION("VME Monitor");
../lib/libvmebus.h
\ No newline at end of file
../driver/vmebus.h
\ No newline at end of file
# include the build environment
include ../../common.mk
#include version information
include ../versions.mk
CFLAGS += -I../include -fPIC
CFLAGS += -DGIT_VERSION=\"$(GIT_VERSION)\"
all: libvmebus.$(CPU).a
libvmebus.$(CPU).a: libvmebus.$(CPU).o
$(AR) rv $@ $^
$(RANLIB) $@
libvmebus.$(CPU).o: libvmebus.c libvmebus.h
clean:
$(RM) *.$(CPU).{o,a}
cleanall:
$(RM) *.{$(ALL_CPUS_COMMAS)}.{o,a}
LIBS_LIST=libvmebus.$(CPU).a
# use default instalation rule for libs
install: install_libs_global
HEADERS_LIST=libvmebus.h
# use default instalation rule for headers
install: install_headers_global
install: install_extra_lib install_extra_headers
install_extra_headers:
# create link acc/local/$(CPU)/include/libvmebus.h -> ../drv/$(PRODUCT_NAME)/libvmebus.h
@echo " Create link acc/local/$(CPU)/include/libvmebus.h -> ../drv/$(PRODUCT_NAME)/libvmebus.h "
$(V)$(INSTALL_DIR_CMD) $(INSTALL_DIR_PARAMS) $(DEPLOY_PATH)acc/local/$(CPU)/include
$(V)$(INSTALL_LINK) $(INSTALL_LINK_PARAMS) ../drv/$(PRODUCT_NAME)/libvmebus.h $(DEPLOY_PATH)acc/local/$(CPU)/include/libvmebus.h
install_extra_lib:
# create link acc/local/$(CPU)/include/libvmebus.a -> ../drv/$(PRODUCT_NAME)/libvmebus.a
@echo " Create link acc/local/$(CPU)/lib/libvmebus.a -> ../drv/$(PRODUCT_NAME)/libvmebus.a "
$(V)$(INSTALL_DIR_CMD) $(INSTALL_DIR_PARAMS) $(DEPLOY_PATH)acc/local/$(CPU)/lib
$(V)$(INSTALL_LINK) $(INSTALL_LINK_PARAMS) ../drv/$(PRODUCT_NAME)/libvmebus.a $(DEPLOY_PATH)acc/local/$(CPU)/lib/libvmebus.a
/**
* \file libvmebus.c
* \brief VME Bus access user library
* \author Sbastien Dugu
* \date 04/02/2009
*
* This library gives userspace applications access to the VME bus
*
* Copyright (c) 2009 \em Sbastien \em Dugu
*
* \par License:
* 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.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <vmebus.h>
const char * const libvmebus_version_s = "libvmebus version: " GIT_VERSION;
/** \brief VME address space mapping device */
#define VME_MWINDOW_DEV "/dev/vme_mwindow"
/** \brief VME DMA device */
#define VME_DMA_DEV "/dev/vme_dma"
/**
* \brief Check for VME a bus error
* \param desc VME mapping descriptor
*
* \return 0 if no bus error occured, 1 if bus error occured or -1 on
* any other error (with errno set appropriately).
*
* Note: This function is DEPRECATED. Use vme_bus_error_check_clear instead.
*/
int vme_bus_error_check(struct vme_mapping *desc)
{
int bus_err;
if (ioctl(desc->fd, VME_IOCTL_GET_BUS_ERROR, &bus_err) < 0) {
#ifdef DEBUG
printf("libvmebus: Failed to get bus error status: %s\n",
strerror(errno));
#endif
return -1;
}
return bus_err;
}
/**
* \brief Check and clear a VME bus error from a given address and mapping
* \param desc VME mapping descriptor
* \param address VME address to be checked
*
* \return 0 if no bus error occured, 1 if bus error occured or -1 on
* any other error (with errno set appropriately).
*
* Note that the VME bus error is cleared _only_ if it matches the
* given address/am pair.
*/
int vme_bus_error_check_clear(struct vme_mapping *vme_desc, __u64 address)
{
struct vme_bus_error_desc desc;
desc.valid = 0;
desc.error.address = address;
desc.error.am = vme_desc->am;
if (ioctl(vme_desc->fd, VME_IOCTL_CHECK_CLEAR_BUS_ERROR, &desc) < 0) {
#ifdef DEBUG
printf("libvmebus: Failed to check bus error status: %s\n",
strerror(errno));
#endif
return -1;
}
return desc.valid;
}
static off_t __page_addr(off_t address)
{
long pagemask = sysconf(_SC_PAGESIZE) - 1;
return address & ~pagemask;
}
static size_t __page_aligned_size(struct vme_mapping *desc)
{
return desc->pci_addrl + desc->sizel - __page_addr(desc->pci_addrl);
}
static size_t __page_aligned_size_incr(struct vme_mapping *desc)
{
return desc->pci_addrl - __page_addr(desc->pci_addrl);
}
/*
* Note that the caller might want to map a non-page-aligned region.
* In order to comply with the alignment requirements of mmap, we map a larger,
* page-aligned region and then store the mapped address of the requested
* non-page-aligned region.
*/
static int vme_mmap(struct vme_mapping *desc)
{
void *addr;
size_t size;
off_t page_addr;
page_addr = __page_addr(desc->pci_addrl);
size = __page_aligned_size(desc);
addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, desc->fd, page_addr);
if (addr == MAP_FAILED)
return -1;
desc->user_va = addr + __page_aligned_size_incr(desc);
return 0;
}
static int vme_munmap(struct vme_mapping *desc)
{
void *addr = desc->user_va - __page_aligned_size_incr(desc);
return munmap(addr, __page_aligned_size(desc));
}
/**
* \brief Map a VME address space into the user address space
* \param vmeaddr VME physical start address of the mapping
* \param len Window size (must be a multiple of 64k)
* \param am VME address modifier
* \param offset Offset in the mapping for read access test (Not used)
* \param size Data width
* \param param VME mapping parameters
*
* \return the virtual address of the mapping on succes, or 0 if an error
* occurs (in that case errno is set appropriately).
*
* \deprecated This function is an emulation of the CES functionality on LynxOS,
* new applications should use the vme_map() function instead.
*
* The CES function interface does not give all the needed VME parameters, so
* the following choices were made and may have to be tweaked:
*
* \li if read prefetch is enabled then the prefetch size is set to 2 cache
* lines
* \li the VME address and size are automatically aligned on 64k if needed
* \li the VME address is limited to 32 bits
*
* \note The descriptor allocated by this function is stored into the
* pdparam_master->sgmin field and the file descriptor in
* pdparam_master->dum[1].
*
* \note We voluntarily do not close the file descriptor here, because doing
* so would automatically remove the mapping made using it. The
* mapping will be removed at return_controller() time.
*/
unsigned long find_controller(unsigned long vmeaddr, unsigned long len,
unsigned long am, unsigned long offset,
unsigned long size, struct pdparam_master *param)
{
struct vme_mapping *desc;
int org_force_create;
int force_create = 1;
int fd;
/* Allocate our mapping descriptor */
if ((desc = calloc(1, sizeof(struct vme_mapping))) == NULL)
return 0;
/* Now fill it with the parameters we got */
desc->window_num = 0;
desc->am = am;
if (param->rdpref) {
desc->read_prefetch_enabled = 1;
desc->read_prefetch_size = VME_PREFETCH_2;
}
switch (size) {
case 2:
desc->data_width = VME_D16;
break;
case 4:
desc->data_width = VME_D32;
break;
default:
printf("libvmebus: %s - "
"Unsupported data width %ld\n", __func__, size);
goto out_free;
break;
}
desc->bcast_select = 0;
if (len & 0xffff) {
printf("libvmebus: %s - "
"Mapping size %lx is not 64k aligned, "
"aligning it to %lx.\n",
__func__, len, (len + 0x10000) & ~0xffff);
len &= ~0xffff;
}
desc->sizel = len;
desc->sizeu = 0;
if (vmeaddr & 0xffff) {
printf("libvmebus: %s - "
"VME address %lx is not 64k aligned, "
"aligning it to %lx.\n",
__func__, vmeaddr, vmeaddr & ~0xffff);
vmeaddr &= ~0xffff;
}
desc->vme_addrl = vmeaddr;
desc->vme_addru = 0;
/*
* Now we're all set up, let's start the real stuff
*/
if ((fd = open(VME_MWINDOW_DEV, O_RDWR)) < 0)
goto out_free;
desc->fd = fd;
/* Save the force create flag */
if (ioctl(fd, VME_IOCTL_GET_CREATE_ON_FIND_FAIL, &org_force_create) < 0)
goto out_free;
/* Set the force create flag */
if (ioctl(fd, VME_IOCTL_SET_CREATE_ON_FIND_FAIL, &force_create) < 0)
goto out_free;
/* Create a new mapping for the area */
if (ioctl(fd, VME_IOCTL_FIND_MAPPING, desc) < 0)
goto out_restore_flag;
/* Now mmap the area */
if (vme_mmap(desc))
goto out_restore_flag;
/*
* Save the descriptor address into the struct pdparam_master sgmin
* field, it will be freed with the call to return_controller().
*/
param->sgmin = (unsigned long)desc;
/* Restore the force create flag */
ioctl(fd, VME_IOCTL_SET_CREATE_ON_FIND_FAIL, &org_force_create);
#ifdef DEBUG
printf("Created mapping\n\tVME addr: 0x%08x size: 0x%08x "
"AM: 0x%02x data width: %d\n\n",
desc->vme_addrl, desc->sizel, desc->am, desc->data_width);
#endif
return (unsigned long)desc->user_va;
out_restore_flag:
ioctl(fd, VME_IOCTL_SET_CREATE_ON_FIND_FAIL, &org_force_create);
out_free:
free(desc);
return 0;
}
/**
* \brief Release a VME mapping
* \param desc VME mapping descriptor
*
* \return 0 on success, or -1 if an error occurs (in that case errno is set
* appropriately).
*
* \deprecated This function is an emulation of the CES functionality on LynxOS,
* new applications should use the vme_unmap() function instead.
*
* \warning The function interface has been changed to a single parameter,
* a struct vme_mapping descriptor as the CES original parameters
* are not enough to match a VME mapping.
*/
unsigned long return_controller(struct vme_mapping *desc)
{
int ret = 0;
int org_force_destroy;
int force_destroy = 1;
if (!desc) {
errno = -EFAULT;
return -1;
}
if (vme_munmap(desc)) {
printf("libvmebus: %s - failed to munmap\n", __func__);
ret = -1;
}
/* Save the force destroy flag */
if (ioctl(desc->fd, VME_IOCTL_GET_DESTROY_ON_REMOVE,
&org_force_destroy) < 0) {
printf("libvmebus: %s - "
"Failed to get the force destroy flag\n", __func__);
}
/* Set the force destroy flag */
if (ioctl(desc->fd, VME_IOCTL_SET_DESTROY_ON_REMOVE,
&force_destroy) < 0) {
printf("libvmebus: %s - "
"Failed to set the force destroy flag\n", __func__);
}
if (ioctl(desc->fd, VME_IOCTL_RELEASE_MAPPING, desc) < 0) {
printf("libvmebus: %s - "
"failed to release mapping\n", __func__);
ret = -1;
}
/* Restore the force destroy flag */
if (ioctl(desc->fd, VME_IOCTL_SET_DESTROY_ON_REMOVE,
&org_force_destroy) < 0)
printf("libvmebus: %s - "
"Failed to restore force destroy flag\n", __func__);
close(desc->fd);
free(desc);
return ret;
}
/**
* \brief Map a VME address space into the user address space
*
* \param desc -- a struct vme_mapping descriptor describing the mapping
* \param force -- flag indicating whether a new window should be created if
* needed.
*
* \return a userspace virtual address for the mapping or NULL on error
* (in that case errno is set appropriately).
*/
void *vme_map(struct vme_mapping *desc, int force)
{
int fd;
int org_force_create;
if ((fd = open(VME_MWINDOW_DEV, O_RDWR)) < 0)
return NULL;
desc->fd = fd;
/* Save the force create flag */
if (ioctl(fd, VME_IOCTL_GET_CREATE_ON_FIND_FAIL, &org_force_create) < 0)
goto out_close;
/* Set the force create flag */
if (ioctl(fd, VME_IOCTL_SET_CREATE_ON_FIND_FAIL, &force) < 0)
goto out_close;
/* Create a new mapping for the area */
if (ioctl(fd, VME_IOCTL_FIND_MAPPING, desc) < 0)
goto out_restore_flag;
/* Now mmap the area */
if (vme_mmap(desc))
goto out_restore_flag;
/* Restore the force create flag */
ioctl(fd, VME_IOCTL_SET_CREATE_ON_FIND_FAIL, &org_force_create);
#ifdef DEBUG
printf("Created mapping\n\tVME addr: 0x%08x size: 0x%08x "
"AM: 0x%02x data width: %d\n\n",
desc->vme_addrl, desc->sizel, desc->am, desc->data_width);
#endif
return desc->user_va;
out_restore_flag:
ioctl(fd, VME_IOCTL_SET_CREATE_ON_FIND_FAIL, &org_force_create);
out_close:
close(fd);
return NULL;
}
/**
* \brief Unmap a VME address space
* \param desc a struct vme_mapping descriptor describing the mapping
* \param force flag indicating whether the window should be destroyed if
* needed
*
* \return 0 on success or -1 on error (in that case errno is set
* appropriately).
*
* This function unmaps a VME address space mapped using vme_map().
*
*/
int vme_unmap(struct vme_mapping *desc, int force)
{
int ret = 0;
int org_force_destroy;
if (!desc) {
errno = -EFAULT;
return -1;
}
if (vme_munmap(desc)) {
printf("libvmebus: %s - failed to munmap\n", __func__);
ret = -1;
}
if (force) {
/* Save the force destroy flag */
if (ioctl(desc->fd, VME_IOCTL_GET_DESTROY_ON_REMOVE,
&org_force_destroy) < 0) {
printf("libvmebus: %s - "
"Failed to get the force destroy flag\n",
__func__);
}
/* Set the force destroy flag */
if (ioctl(desc->fd, VME_IOCTL_SET_DESTROY_ON_REMOVE,
&force) < 0) {
printf("libvmebus: %s - "
"Failed to set the force destroy flag\n",
__func__);
}
}
if (ioctl(desc->fd, VME_IOCTL_RELEASE_MAPPING, desc) < 0) {
printf("libvmebus: %s - "
"failed to release mapping\n",
__func__);
ret = -1;
}
if (force) {
/* Restore the force destroy flag */
if (ioctl(desc->fd, VME_IOCTL_SET_DESTROY_ON_REMOVE,
&org_force_destroy) < 0)
printf("libvmebus: %s - "
"Failed to restore force destroy flag\n",
__func__);
}
close(desc->fd);
return ret;
}
/**
* \brief Perform a DMA read on the VME bus
* \param desc DMA transfer descriptor
*
* \return 0 on success or -1 on error (in that case errno is set
* appropriately).
*
* This function performs a DMA read with the parameters specified in the
* struct vme_dma descriptor.
*/
int vme_dma_read(struct vme_dma *desc)
{
int rc = 0;
int fd;
if ((fd = open(VME_DMA_DEV, O_RDONLY)) < 0)
return -1;
if (ioctl(fd, VME_IOCTL_START_DMA, desc) < 0)
rc = -1;
close(fd);
return rc;
}
/**
* \brief Perform a DMA write on the VME bus
* \param desc DMA transfer descriptor
*
* \return 0 on success or -1 on error (in that case errno is set
* appropriately).
*
* This function performs a DMA write with the parameters specified in the
* struct vme_dma descriptor.
*
*/
int vme_dma_write(struct vme_dma *desc)
{
int rc = 0;
int fd;
if ((fd = open(VME_DMA_DEV, O_WRONLY)) < 0)
return -1;
if (ioctl(fd, VME_IOCTL_START_DMA, desc) < 0)
rc = -1;
close(fd);
return rc;
}
/**
* \file libvmebus.h
* \brief VME Bus access user library interface
* \author Sbastien Dugu
* \date 04/02/2009
*
* This library gives userspace applications access to the VME bus
*
* Copyright (c) 2009 \em Sbastien \em Dugu
*
* \par License:
* 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.
*
*/
#ifndef _LIBVMEBUS_H_INCLUDE_
#define _LIBVMEBUS_H_INCLUDE_
#ifdef __cplusplus
extern "C" {
#endif
#include <vmebus.h>
/**
* \brief Swap a 16-bit halfword bytewise
* \param val The 16-bit halfword to swap
*
* \return The swapped value
*
*/
static inline unsigned short swapbe16(unsigned short val)
{
return (((val & 0xff00) >> 8) | ((val & 0xff) << 8));
}
/**
* \brief Swap a 32-bit word bytewise
* \param val The 32-bit word to swap
*
* \return The swapped value
*
*/
static inline unsigned int swapbe32(unsigned int val)
{
return (((val & 0xff000000) >> 24) | ((val & 0xff0000) >> 8) |
((val & 0xff00) << 8) | ((val & 0xff) << 24));
}
extern int vme_bus_error_check(struct vme_mapping *desc);
extern int vme_bus_error_check_clear(struct vme_mapping *desc, __u64 address);
/* VME address space mapping - CES library emulation */
extern unsigned long find_controller(unsigned long vmeaddr, unsigned long len,
unsigned long am, unsigned long offset,
unsigned long size,
struct pdparam_master *param);
extern unsigned long return_controller(struct vme_mapping *desc);
/* VME address space mapping */
extern void *vme_map(struct vme_mapping *, int);
extern int vme_unmap(struct vme_mapping *, int);
/* DMA access */
extern int vme_dma_read(struct vme_dma *);
extern int vme_dma_write(struct vme_dma *);
/* libvmebus version string */
extern const char * const libvmebus_version_s;
#ifdef __cplusplus
}
#endif
#endif /* _LIBVMEBUS_H_INCLUDE_ */
# include the build environment
include ../../common.mk
#include version information
include ../versions.mk
SRCS = create_window.c destroy_window.c setflag.c \
test_mapping.c testctr.c testctr2.c testctr_ces.c \
testvd80.c vd80spd.c vd80spd-dma.c mm6390-dma.c \
testvd802.c hsm-dma.c berrtest.c doublemap.c
CFLAGS = -Wall -DDEBUG -D_GNU_SOURCE -g -I../include
CFLAGS += -DGIT_VERSION=\"$(GIT_VERSION)\"
LIBVMEBUS = ../lib/libvmebus.$(CPU).a
LIBVD80 = libvd80.$(CPU).a
LDLIBS = -lrt
ALL=$(SRCS:.c=.$(CPU))
all: $(ALL)
.PHONY: all sources doc clean libvd80doc
sources: $(SRCS) $(SCRIPTS)
testctr_ces.$(CPU): testctr_ces.c $(LIBVMEBUS)
testctr2.$(CPU): testctr2.c $(LIBVMEBUS)
testvd80.$(CPU): testvd80.c $(LIBVD80) $(LIBVMEBUS)
testvd802.$(CPU): testvd802.c $(LIBVMEBUS)
vd80spd.$(CPU): vd80spd.c $(LIBVD80) $(LIBVMEBUS)
vd80spd-dma.$(CPU): vd80spd-dma.c $(LIBVD80) $(LIBVMEBUS)
libvd80.$(CPU).o: libvd80.c libvd80.h
libvd80.$(CPU).a: libvd80.$(CPU).o
$(AR) rv $(LIBVD80) $^
$(RANLIB) $(LIBVD80)
libvd80doc: libvd80.c libvd80.h vd80.h
mkdir -p doc
doxygen libvd80.dox
mm6390-dma.$(CPU): mm6390-dma.c $(LIBVMEBUS)
hsm-dma.$(CPU): hsm-dma.c $(LIBVMEBUS)
berrtest.$(CPU): berrtest.c $(LIBVMEBUS)
doublemap.$(CPU): doublemap.c $(LIBVMEBUS)
doc: libvd80doc
clean:
$(RM) *.$(CPU) *.o *.$(CPU).a
$(RM) -r doc
cleanall:
$(RM) *.{$(ALL_CPUS_COMMAS)} *.o *.{$(ALL_CPUS_COMMAS)}.a
$(RM) -r doc
# deliver create_window to the same place as driver is
install:
# check whether all needed variables are defined
$(call check_defined,PRODUCT_NAME)
# deploy drivers
@echo "Install create_window into $(INST_DRIVER_PATH)"
$(V)$(INSTALL_DIR_CMD) $(INSTALL_DIR_PARAMS) $(INST_DRIVER_PATH)
$(V)$(INSTALL_BIN_CMD) $(INSTALL_BIN_PARAMS) create_window.$(CPU) $(INST_DRIVER_PATH)/create_window
/*
* berrtest.c - Bus Error Test
*
* Generate a few bus errors and see if they are properly handled.
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <libvmebus.h>
static char git_version[] = "git_version: " GIT_VERSION;
/* Number of (faulty) accesses to be done */
#define ACCESSES 4
#define AM 0x9
#define DW VME_D32
#define VME_ADDR 0x1000000
#define MAP_SIZE 0x1000
int main(int argc, char *argv[])
{
struct vme_mapping mapping;
uint64_t vme_addr;
uint32_t *virt;
int berr;
int i;
printf("%s: %s\n", argv[0], git_version);
printf("%s\n", libvmebus_version_s);
memset(&mapping, 0, sizeof(struct vme_mapping));
mapping.sizel = MAP_SIZE;
mapping.vme_addrl = VME_ADDR;
mapping.am = AM;
mapping.data_width = DW;
virt = vme_map(&mapping, 1);
if (virt == NULL) {
perror("vme_map");
printf("Mapping failed\n");
exit(EXIT_FAILURE);
}
vme_addr = mapping.vme_addrl;
for (i = 0; i < ACCESSES; i++, virt++, vme_addr += sizeof(uint32_t)) {
/* cause a VME bus error */
*virt = i;
/* sleep so that the bus error gets picked up by the driver */
sleep(1);
berr = vme_bus_error_check_clear(&mapping, vme_addr);
if (berr < 0) {
printf("vmebus: check_clear_berr failed\n");
exit(EXIT_FAILURE);
}
if (berr) {
printf("vmebus: bus error @ 0x%llx\n",
(unsigned long long)vme_addr);
}
berr = vme_bus_error_check_clear(&mapping, vme_addr);
if (berr < 0) {
printf("vmebus: check_clear_berr failed\n");
exit(EXIT_FAILURE);
}
if (berr) {
printf("BUG: bus error reported twice.\n");
exit(EXIT_FAILURE);
}
}
vme_unmap(&mapping, 1);
return 0;
}
/*
* create_window.L865 0 0x29 16 0x0 0x10000
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <vmebus.h>
struct vme_mapping window;
struct vme_mapping testw;
int window_num;
int am, dsize, vmeaddr, length;
#define VME_MWINDOW_DEV "/dev/vme_mwindow"
static char git_version[] = "git_version: " GIT_VERSION;
void usage(char * prgname)
{
printf("Usage: %s winnum am dwidth vmeaddr size\n", prgname);
printf(" window: 0..7\n");
printf(" am: standard VME address modifier (in hex)\n");
printf(" data width: 16 or 32\n");
printf(" VME address: VME start address\n");
printf(" size: window size\n");
printf("\n%s\n", git_version);
}
void parse_args(int argc, char **argv)
{
if (argc != 6) {
usage(argv[0]);
exit(1);
}
if (sscanf(argv[1], "%d", &window_num) != 1) {
printf("Failed to parse window number\n");
usage(argv[0]);
exit(1);
}
if (sscanf(argv[2], "%x", &am) != 1) {
printf("Failed to parse address modifier\n");
usage(argv[0]);
exit(1);
}
if (sscanf(argv[3], "%d", &dsize) != 1) {
printf("Failed to parse data size\n");
usage(argv[0]);
exit(1);
}
if (sscanf(argv[4], "%x", &vmeaddr) != 1) {
printf("Failed to parse VME address\n");
usage(argv[0]);
exit(1);
}
if (sscanf(argv[5], "%x", &length) != 1) {
printf("Failed to parse length\n");
usage(argv[0]);
exit(1);
}
}
int compare_windows(struct vme_mapping *m1, struct vme_mapping *m2)
{
int err = 0;
printf("Checking windows:\n");
if (m1->window_num != m2->window_num) {
err = 1;
printf("\tWindow number mismatch %d -> %d\n",
m1->window_num, m2->window_num);
}
if (m1->am != m2->am) {
err = 1;
printf("Address modifier mismatch 0x%0x -> 0x%0x\n",
m1->am, m2->am);
}
if (m1->data_width != m2->data_width) {
err = 1;
printf("Data width mismatch %d -> %d\n",
m1->data_width, m2->data_width);
}
if (m1->read_prefetch_enabled != m2->read_prefetch_enabled) {
err = 1;
printf("Read prefetch enabled mismatch %d -> %d\n",
m1->read_prefetch_enabled, m2->read_prefetch_enabled);
}
if (m1->read_prefetch_size != m2->read_prefetch_size) {
err = 1;
printf("Read prefetch size mismatch %d -> %d\n",
m1->read_prefetch_size, m2->read_prefetch_size);
}
if (m1->v2esst_mode != m2->v2esst_mode) {
err = 1;
printf("2eSST speed mismatch %d -> %d\n",
m1->v2esst_mode, m2->v2esst_mode);
}
if (m1->bcast_select != m2->bcast_select) {
err = 1;
printf("2eSST broadcast select mismatch %d -> %d\n",
m1->bcast_select, m2->bcast_select);
}
if ((m1->pci_addrl != m2->pci_addrl) &&
(m1->pci_addru != m2->pci_addru)) {
err = 1;
printf("PCI address mismatch 0x%08x:%08x -> 0x%08x:%08x\n",
m1->pci_addru, m1->pci_addrl,
m2->pci_addru, m2->pci_addrl);
}
if ((m1->vme_addrl != m2->vme_addrl) &&
(m1->vme_addru != m2->vme_addru)) {
err = 1;
printf("VME address mismatch 0x%08x:%08x -> 0x%08x:%08x\n",
m1->vme_addru, m1->vme_addrl,
m2->vme_addru, m2->vme_addrl);
}
if ((m1->sizel != m2->sizel) &&
(m1->sizeu != m2->sizeu)) {
err = 1;
printf("Size mismatch 0x%08x:%08x -> 0x%08x:%08x\n",
m1->sizeu, m1->sizel,
m2->sizeu, m2->sizel);
}
if (!err)
printf("\tOK\n\n");
return err;
}
void dump_window(struct vme_mapping *m)
{
printf("\tWindow number: %d\n", m->window_num);
printf("\tAddress modifier: 0x%0x\n", m->am);
printf("\tData width: %d\n", m->data_width);
printf("\tRead prefetch enabled: %d\n", m->read_prefetch_enabled);
printf("\tRead prefetch size: %d\n", m->read_prefetch_size);
printf("\t2eSST speed: %d\n", m->v2esst_mode);
printf("\t2eSST bcast select: %d\n", m->bcast_select);
printf("\tPCI address: 0x%08x:%08x\n", m->pci_addru, m->pci_addrl);
printf("\tVME address: 0x%08x:%08x\n", m->vme_addru, m->vme_addrl);
printf("\tSize: 0x%08x:%08x\n", m->sizeu, m->sizel);
}
int main(int argc, char **argv)
{
int fd;
parse_args(argc, argv);
memset(&window, 0, sizeof(struct vme_mapping));
if ((window_num < 0) || (window_num > 7)) {
printf("Wrong window number %d_n", window_num);
exit(1);
}
window.window_num = window_num;
window.am = am;
switch (dsize) {
case 16:
window.data_width = VME_D16;
break;
case 32:
window.data_width = VME_D32;
break;
default:
printf("Unsupported data width %d\n", dsize);
exit(1);
}
window.vme_addru = 0;
window.vme_addrl = vmeaddr;
window.sizeu = 0;
window.sizel = length;
printf("Creating window %d\n\tVME addr: 0x%08x size: 0x%08x "
"AM: 0x%02x data width: %d\n\n",
window_num, vmeaddr, length, am, dsize);
if ((fd = open(VME_MWINDOW_DEV, O_RDWR)) < 0) {
printf("Failed to open %s: %s\n", VME_MWINDOW_DEV,
strerror(errno));
exit(1);
}
if (ioctl(fd, VME_IOCTL_CREATE_WINDOW, &window) < 0) {
printf("Failed to create window: %s\n", strerror(errno));
close(fd);
exit(1);
}
memset(&testw, 0, sizeof(struct vme_mapping));
testw.window_num = window_num;
if (ioctl(fd, VME_IOCTL_GET_WINDOW_ATTR, &testw) < 0) {
printf("Failed to get window attributes: %s\n",
strerror(errno));
close(fd);
exit(1);
}
compare_windows(&window, &testw);
dump_window(&window);
close(fd);
return 0;
}
/*
* destroy_window.L865 0
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <vmebus.h>
static char git_version[] = "git_version: " GIT_VERSION;
int window_num;
#define VME_MWINDOW_DEV "/dev/vme_mwindow"
void usage(char * prgname)
{
printf("Usage: %s winnum\n", prgname);
printf(" window: 0..7\n");
printf("\n%s\n", git_version);
}
void parse_args(int argc, char **argv)
{
if (argc != 2) {
usage(argv[0]);
exit(1);
}
if (sscanf(argv[1], "%d", &window_num) != 1) {
printf("Failed to parse window number\n");
usage(argv[0]);
exit(1);
}
}
int main(int argc, char **argv)
{
int fd;
parse_args(argc, argv);
if ((window_num < 0) || (window_num > 7)) {
printf("Wrong window number %d_n", window_num);
exit(1);
}
if ((fd = open(VME_MWINDOW_DEV, O_RDWR)) < 0) {
printf("Failed to open %s: %s\n", VME_MWINDOW_DEV,
strerror(errno));
exit(1);
}
printf("Destroying window %d\n\n", window_num);
if (ioctl(fd, VME_IOCTL_DESTROY_WINDOW, &window_num) < 0) {
printf("Failed to destroy window#%d: %s\n",
window_num, strerror(errno));
close(fd);
exit(1);
}
close(fd);
return 0;
}
/*
* doublemap.c
*
* Test that several mappings can be done (and removed) successfully from
* a single process.
*/
#include <unistd.h>
#include <libvmebus.h>
#include <stdio.h>
#include <string.h>
struct vme_mapping map[2];
void *virt[2];
static int do_mapping(int am, int dw, int size, int vme_addr, int i)
{
map[i].am = am;
map[i].data_width = dw;
map[i].sizel = size;
map[i].vme_addrl = vme_addr;
virt[i] = vme_map(&map[i], 1);
if (virt[i] == NULL) {
perror("vme_map");
printf("Mapping %d failed\n", i);
return -1;
}
printf("Mapping %d okay\n", i);
return 0;
}
int main(int argc, char *argv[])
{
int ret;
ret = do_mapping(VME_A16_USER, VME_D16, 0x1000, 0x0, 0);
if (ret)
return ret;
ret = do_mapping(VME_A24_USER_DATA_SCT, VME_D16, 0x20000, 0x900000, 1);
if (ret)
return ret;
vme_unmap(&map[0], 0);
vme_unmap(&map[1], 0);
return 0;
}
#!/bin/sh
module="vmebus"
major=221
# Set the console loglevel to DEBUG
echo 8 > /proc/sys/kernel/printk
# Load module
/sbin/insmod ../driver/$module.ko $* || exit 1
# Remove stale nodes
rm -f /dev/vme_mwindow
rm -f /dev/vme_dma
rm -f /dev/vme_ctl
rm -f /dev/vme_regs
# Create device nodes
mknod /dev/vme_mwindow c $major 0
mknod /dev/vme_dma c $major 1
mknod /dev/vme_ctl c $major 2
mknod /dev/vme_regs c $major 3
#!/bin/sh
module="vmebus"
major=221
# Load module
/sbin/rmmod ../driver/$module.ko $* || exit 1
# Remove stale nodes
rm -f /dev/vme_mwindow
rm -f /dev/vme_dma
rm -f /dev/vme_ctl
rm -f /dev/vme_regs
#!/bin/sh
#=======================================================================
# Access to Front-end serial line console
#=======================================================================
set -x
x=`dirname $0`; [ "$x" = "." ] && x=/mcr/bin;
cd $x
# default escape character:
E="-e^[^["
[ $# == 0 ] && {
( echo "Usage $0 <dsc_name>";
echo " where <dsc_name> is one of the followings:";
echo " - on TN";
egrep ^console /acc/sys/adm/conserver.feop | awk '{print $2}' | sort -u | pr -5 -a -t -w80;
echo " - on GPN";
egrep ^console /acc/sys/adm/conserver.felab | awk '{print $2}' | sort -u | pr -5 -a -t -w80;
) | less
exit 2
}
U=""
while [ $# -ne 1 ]; do
case $1 in
-e*)
E="$1"
;;
*)
U="$U $1"
;;
esac
shift
done
d="$1"
x=`host $d | egrep -v 'NXDOMAIN|dyndns' | awk '{print $4}'`
[ ! -z "$x" ] && {
# Host exists Check for ILO connection request
case $d in
cs-*)
d1="csm-"${d:3}
;;
*)
d1="$d-ilo"
;;
esac
[ `host $d1 | egrep -v 'NXDOMAIN|dyndns' | wc -l` = 1 ] && exec /usr/bin/ssh -l root $U $d1
M=cs-ccr-feop
case $x in
137.138.*)
M=cs-ccr-felab
;;
esac
exec ./console -M$M $E $U $d
}
# check for this one in straight the lists
x1=`egrep ^console /acc/sys/adm/conserver.feop | awk '{print $2}' | sort -u`
[ `echo $x1 | tr ' ' '\n' | egrep -w ^$d | wc -l` = 1 ] && {
exec ./console -Mcs-ccr-feop $E $U $d
}
x2=`egrep ^console /acc/sys/adm/conserver.felab | awk '{print $2}' | sort -u`
[ `echo $x2 | tr ' ' '\n' | egrep -w ^$d | wc -l` = 1 ] && {
exec ./console -Mcs-ccr-felab $E $U $d
}
# If non-ambiguous name connect
[ `echo $x1 | tr ' ' '\n' | egrep $d | wc -l` = 1 ] && {
d=`echo $x1 | tr ' ' '\n' | egrep $d`;
exec ./console -Mcs-ccr-feop $E $U $d
}
[ `echo $x2 | tr ' ' '\n' | egrep $d | wc -l` = 1 ] && {
d=`echo $x2 | tr ' ' '\n' | egrep $d`;
exec ./console -Mcs-ccr-felab $E $U $d
}
y=`echo $x1 $x2 | tr ' ' '\n' | egrep $d`
[ $? -ne 0 ] && {
echo There is no console for $d - sorry
echo "===================================================================="
exit 2
}
echo "===================================================================="
echo $d is ambiguous: Please choose one of the followings:
echo $x1 $x2 | tr ' ' '\n' | egrep $d | pr -5 -a -t -w80
echo "===================================================================="
exit 2
/*
* hsm-dma.c - DMA throughput measurement of an HSM 8170 from CES.
*
* This module has 1MB of memory which can be accessed in BLT and SCT modes.
*
* NOTE: In our tests we have found that this module doesn't work reliably
* in BLT mode: DMAing to the board and then reading back again through
* DMA yields non-consistent results; sometimes the read values are off
* with respect to the block that was written by one 'int'.
*
* Anyway, since this test is only concerned about DMA throughput, data
* coherency is not checked at all. We just transfer zeroes back and forth.
*/
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <time.h>
#include <libvmebus.h>
#define HSM_VME_ADDR 0x11000000
#define HSM_SIZE 0x100000
#define HSM_AM VME_A32_USER_BLT
#define HSM_DW VME_D32
static char git_version[] = "git_version: " GIT_VERSION;
struct stats {
int cycles;
double delta;
};
#define STATS_MAXLEN 100
static unsigned int duration;
static struct stats rd_stats;
static struct stats wr_stats;
static time_t init_time;
long long ts_subtract(struct timespec *a, struct timespec *b)
{
long long ns;
ns = (b->tv_sec - a->tv_sec) * 1000000000LL;
ns += (b->tv_nsec - a->tv_nsec);
return ns;
}
static void
dma_desc_init(int32_t *buf, struct vme_dma *desc, enum vme_dma_dir dir)
{
struct vme_dma_attr *pci;
struct vme_dma_attr *vme;
memset(desc, 0, sizeof(struct vme_dma));
desc->dir = dir;
desc->length = HSM_SIZE;
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;
if (dir == VME_DMA_TO_DEVICE) {
pci = &desc->src;
vme = &desc->dst;
} else {
pci = &desc->dst;
vme = &desc->src;
}
/* strange casting to keep compiler quiet */
pci->addru = (unsigned int)
(((unsigned long long)(uintptr_t)buf)>>32);
pci->addrl = (unsigned int)(uintptr_t)buf;
vme->addru = 0;
vme->addrl = HSM_VME_ADDR;
vme->am = HSM_AM;
vme->data_width = HSM_DW;
}
static inline void
wr_desc_init(int32_t *buf, struct vme_dma *desc)
{
return dma_desc_init(buf, desc, VME_DMA_TO_DEVICE);
}
static inline void
rd_desc_init(int32_t *buf, struct vme_dma *desc)
{
return dma_desc_init(buf, desc, VME_DMA_FROM_DEVICE);
}
static inline void update_stats(struct stats *stats, unsigned long long ns)
{
stats->cycles++;
stats->delta += (double)ns/(double)1000000000.0;
}
static inline double calc_tput(struct stats *stats)
{
return (double)(HSM_SIZE) * stats->cycles / stats->delta / 1024.0 /
1024.0;
}
static void fill_entry(char *str, struct stats *stats, const char *title)
{
snprintf(str, STATS_MAXLEN, "%s\t\t%16.6f\t%11llu\t%11.6f", title,
calc_tput(stats), (unsigned long long)stats->cycles * HSM_SIZE,
stats->delta);
str[STATS_MAXLEN - 1] = '\0';
}
/*
* Several instances of this program are likely to be called at the same
* time. To avoid mixing up statistics coming from each of the instances,
* we show the stats in a single printf statement.
*/
static void print_stats(void)
{
char rd_str[STATS_MAXLEN];
char wr_str[STATS_MAXLEN];
fill_entry(rd_str, &rd_stats, "Read ");
fill_entry(wr_str, &wr_stats, "Write");
printf("\nDMA Stats\tThroughput(MB/s)\tSize(bytes)\tDuration(s)\n"
"======================================================="
"============\n"
"%s\n"
"%s\n", rd_str, wr_str);
}
static int __dma(struct vme_dma *desc, int write)
{
unsigned long long delta_ns;
struct timespec ts_start;
struct timespec ts_end;
struct stats *stats = write ? &wr_stats : &rd_stats;
int rc;
clock_gettime(CLOCK_MONOTONIC, &ts_start);
if (write)
rc = vme_dma_write(desc);
else
rc = vme_dma_read(desc);
if (rc) {
printf("DMA %s failed: %s\n", write ? "write" : "read",
strerror(errno));
return -1;
}
clock_gettime(CLOCK_MONOTONIC, &ts_end);
delta_ns = ts_subtract(&ts_start, &ts_end);
update_stats(stats, delta_ns);
return 0;
}
static inline int dma_write(struct vme_dma *desc)
{
return __dma(desc, 1);
}
static inline int dma_read(struct vme_dma *desc)
{
return __dma(desc, 0);
}
static void bail_out(void)
{
print_stats();
exit(EXIT_SUCCESS);
}
static void sighandler(int sig)
{
return bail_out();
}
static void sighandler_init(void)
{
struct sigaction act;
act.sa_handler = sighandler;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGTERM, &act, 0);
sigaction(SIGINT, &act, 0);
}
static void __check_completion(void)
{
time_t now = time(NULL);
if (now >= init_time + duration)
return bail_out();
}
static inline void check_completion(void)
{
if (!duration)
return;
return __check_completion();
}
int main(int argc, char *argv[])
{
struct vme_dma rd_desc;
struct vme_dma wr_desc;
int32_t *wr_buf = NULL;
int32_t *rd_buf = NULL;
int rc = 0;
if (argc != 2) {
printf("%s: measure DMA throughput the HSM 8170\n", argv[0]);
printf("Usage: %s <duration>\n", argv[0]);
printf(" d: duration in seconds of the test.\n"
" default: 0 (loop forever.)\n"
"This program can be interrupted anytime with the "
"TERM or INT signals\n");
printf("\n%s\n", git_version);
printf("%s\n", libvmebus_version_s);
exit(EXIT_FAILURE);
}
sscanf(argv[1], "%d", &duration);
sighandler_init();
if (posix_memalign((void **)&rd_buf, 4096, HSM_SIZE)) {
printf("Failed to allocate rd buffer: %s\n", strerror(errno));
rc = 1;
goto out;
}
if (posix_memalign((void **)&wr_buf, 4096, HSM_SIZE)) {
printf("Failed to allocate wr buffer: %s\n", strerror(errno));
rc = 1;
goto out;
}
rd_desc_init(rd_buf, &rd_desc);
wr_desc_init(wr_buf, &wr_desc);
init_time = time(NULL);
while (1) {
rc = dma_write(&wr_desc);
if (rc)
goto out;
rc = dma_read(&rd_desc);
if (rc)
goto out;
check_completion();
}
out:
if (wr_buf)
free(wr_buf);
if (rd_buf)
free(rd_buf);
return rc;
}
#!/bin/bash
#
# Launch a group of processes all accessing concurrently the HSM module.
DURATION=5
TASKS=1
CPU='L865'
USAGE="`basename $0`: stress-test the HSM VME memory module.\n \
Usage: `basename $0` -d<seconds> -n<#tasks>\n \
options:\n \
d: Duration of the test, in seconds (default: $DURATION.)\n \
n: Number of concurrent tasks accessing the HSM (default: $TASKS.)\n";
while getopts d:n: c
do
case $c in
d)
DURATION=$OPTARG;;
n)
TASKS=$OPTARG;;
\?)
printf "$USAGE";
exit 2;
esac
done
for i in $(seq $TASKS)
do
echo "executing task $i"
./hsm-dma.$CPU $DURATION &
done
let DURATION++
sleep $DURATION
exit 0
/**
* \file libvd80.c
* \brief VD80 access user library
* \author Sébastien Dugué
* \date 04/02/2009
*
* This library gives userspace applications access to the VD80 board.
*
* Copyright (c) 2009 \em Sébastien \em Dugué
*
* \par License:
* 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.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <libvmebus.h>
#include "libvd80.h"
const char * const libvd80vmebridge_version_s = "libvd80(vmebridge) version: " GIT_VERSION;
unsigned int *regs;
struct vme_mapping regs_desc;
/**
* \brief Start sampling
*
* \return 0 on success else 1 on a VME bus error.
*/
int vd80_cmd_start(void)
{
regs[VD80_GCR1/4] |= swapbe32(VD80_COMMAND_START);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error starting sampling\n");
return 1;
}
return 0;
}
/**
* \brief Stop sampling
*
* \return 0 on success else 1 on a VME bus error.
*/
int vd80_cmd_stop(void)
{
regs[VD80_GCR1/4] |= swapbe32(VD80_COMMAND_STOP);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error stopping sampling\n");
return 1;
}
return 0;
}
/**
* \brief Generate an internal trigger
*
* \return 0 on success else 1 on a VME bus error.
*/
int vd80_cmd_trig(void)
{
regs[VD80_GCR1/4] |= swapbe32(VD80_COMMAND_TRIG);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error triggering sampling\n");
return 1;
}
return 0;
}
/**
* \brief Start subsampling
*
* \return 0 on success else 1 on a VME bus error.
*/
int vd80_cmd_substart(void)
{
regs[VD80_GCR1/4] |= swapbe32(VD80_COMMAND_SUBSTART);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error starting sub sampling\n");
return 1;
}
return 0;
}
/**
* \brief Stop subsampling
*
* \return 0 on success else 1 on a VME bus error.
*/
int vd80_cmd_substop(void)
{
regs[VD80_GCR1/4] |= swapbe32(VD80_COMMAND_SUBSTOP);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error stopping sub sampling\n");
return 1;
}
return 0;
}
/**
* \brief Enable readout of the sample buffer
* \param channel Channel to read samples from (1 to 16)
*
* \return 0 on success else 1 on a VME bus error.
*/
int vd80_cmd_read(unsigned int channel)
{
regs[VD80_GCR1/4] |= swapbe32((VD80_COMMAND_READ |
((channel << VD80_OPERANT_SHIFT) &
VD80_OPERANT_MASK)));
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error setting READ for channel %d\n", channel);
return 1;
}
return 0;
}
/**
* \brief Record only a single sample
*
* \return 0 on success else 1 on a VME bus error.
*/
int vd80_cmd_single(void)
{
regs[VD80_GCR1/4] |= swapbe32(VD80_COMMAND_SINGLE);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error setting SINGLE\n");
return 1;
}
return 0;
}
/**
* \brief Clear the pre-trigger counter
*
* \return 0 on success else 1 on a VME bus error.
*/
int vd80_cmd_clear(void)
{
regs[VD80_GCR1/4] |= swapbe32(VD80_COMMAND_CLEAR);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error setting CLEAR\n");
return 1;
}
return 0;
}
/**
* \brief Set little or big endian samples recording
* \param bigend When set, samples are stored in big endian ordering, else in
* little endian
*
* \return 0 on success else 1 on a VME bus error.
*/
int vd80_cmd_setbig(int bigend)
{
if (bigend)
regs[VD80_GCR2/4] |= swapbe32(VD80_BIGEND);
else
regs[VD80_GCR2/4] &= swapbe32(~VD80_BIGEND);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error setting samples endianess\n");
return 1;
}
return 0;
}
/**
* \brief Enable/Disable generation of the test waveform
* \param debug When set, enable generation of the test waveform, else disable
*
* \return 0 on success else 1 on a VME bus error.
*
* The test waveform consists in a digital ramp.
*/
int vd80_cmd_setdebug(int debug)
{
if (debug)
regs[VD80_GCR2/4] |= swapbe32(VD80_DBCNTSEL);
else
regs[VD80_GCR2/4] &= swapbe32(~VD80_DBCNTSEL);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error setting debug counter select\n");
return 1;
}
return 0;
}
/**
* \brief Set the number of post-trigger samples
* \param num Number of post-trigger samples to store
*
* \return 0 on success or -1 on a VME bus error.
*
* Set the number of samples recorded between the trigger and sampling end.
*/
int vd80_set_postsamples(unsigned int num)
{
regs[VD80_TCR2/4] = swapbe32(num & 0x7ffff);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error setting POSTSAMPLES\n");
return -1;
}
return 0;
}
/**
* \brief Get the number of post-trigger samples
*
* \return the number of requested samples on success or -1 on a VME bus error.
*
* Get the number of post-trigger samples requested.
*/
int vd80_get_postsamples(void)
{
int val = swapbe32(regs[VD80_TCR2/4]);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error reading POSTSAMPLES\n");
return -1;
}
return val;
}
/**
* \brief Get the number of pre-trigger samples
*
* \return the number of pre-trigger samples or -1 on a VME bus error.
*
* Get the number of samples recorded between the sampling start and the
* trigger.
*/
int vd80_get_pre_length(void)
{
int reg = swapbe32(regs[VD80_PTSR/4]);
int val = (reg & VD80_PRESAMPLES_MASK) >>
VD80_PRESAMPLES_SHIFT;
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error reading PRESAMPLES\n");
return -1;
}
return val;
}
/**
* \brief Get the number of post-trigger samples
*
* \return the number of post-trigger samples or -1 on a VME bus error.
*
* Get the number of samples recorded between the trigger and the sampling
* end.
*/
int vd80_get_post_length(void)
{
int reg = swapbe32(regs[VD80_TSR/4]);
int val = (reg & VD80_ACTPOSTSAMPLES_MASK) >>
VD80_ACTPOSTSAMPLES_SHIFT;
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error reading ACTPOSTSAMPLES\n");
return -1;
}
return val;
}
/**
* \brief Get the total shot length
*
* \return the total number of samples or -1 on a VME bus error.
*
* Get the total number of samples taken during a shot
* (pre-samples + post-samples).
*/
int vd80_get_shot_length(void)
{
int reg = swapbe32(regs[VD80_SSR/4]);
int val = (reg & VD80_SHOTLEN_MASK) >>
VD80_SHOTLEN_SHIFT;
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error reading SHOTLEN\n");
return -1;
}
return val;
}
/**
* \brief Set the readout starting sample
* \param readstart READSTART value to set
*
* \return 0 on success else 1 on a VME bus error.
*
* \note READSTART counts in steps of 32 samples. Therefore the number of
* samples is READSTART * 32.
*/
int vd80_set_readstart(unsigned int readstart)
{
regs[VD80_MCR1/4] = swapbe32((readstart << VD80_READSTART_SHIFT) &
VD80_READSTART_MASK);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error setting READSTART\n");
return 1;
}
return 0;
}
/**
* \brief Set the number of samples that must be read
* \param readlen READLEN value to set
*
* \return 0 on success else 1 on a VME bus error.
*
* \note READLEN counts in steps of 32 samples. Therefore the number of samples
* is (READLEN + 1) * 32 and the minimum number of samples is 32
*/
int vd80_set_readlen(unsigned int readlen)
{
regs[VD80_MCR2/4] = swapbe32((readlen << VD80_READLEN_SHIFT) &
VD80_READLEN_MASK);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error setting READLEN\n");
return 1;
}
return 0;
}
/**
* \brief Get one sample from the sample memory
*
*
*/
int vd80_get_sample(void)
{
return regs[VD80_MEMOUT/4];
}
/**
* \brief Probe and configure the VD80
*
* \return 0 on success else 1 on failure.
*
* This function probes and configures the VD80 using the CR/CSR address space.
*
* It performs the following operations:
*
* \li map the VME CR/CSR address space of the board
* \li check we're talking to a VD80 board by checking the module ID
* \li configure the function 0 Address Decode Compare register (\a ADER0) to
* map the operational registers on an A24/D32 address space at base
* address 0x600000. Used for operational registers access.
* \li configure the function 1 Address Decode Compare register (\a ADER1) to
* map the operational registers on an A24-BLT/D32 address space at base
* address 0x600000. Used for DMA reading of the samples memory using
* VME block transfer.
*
* \note The VME CR/CSR address space is unmapped on function return.
*/
int vd80_map(void)
{
int rc = 1;
struct vme_mapping crcsr_desc;
unsigned int *crcsr;
int i;
unsigned int val;
char cr_sig[2];
char board_id[5];
char rev_id[5];
unsigned int desc_offset;
char board_desc[512];
/*
* Open a mapping into the VD80 CR/CSR (0x2f) address space for
* configuring the board.
*/
memset(&crcsr_desc, 0, sizeof(struct vme_mapping));
crcsr_desc.window_num = 0;
crcsr_desc.am = VD80_SETUP_AM;
crcsr_desc.read_prefetch_enabled = 0;
crcsr_desc.data_width = VD80_SETUP_DW;
crcsr_desc.sizeu = 0;
crcsr_desc.sizel = VD80_SETUP_SIZE;
crcsr_desc.vme_addru = 0;
crcsr_desc.vme_addrl = VD80_SETUP_BASE;
if ((crcsr = vme_map(&crcsr_desc, 1)) == NULL) {
printf("Failed to map CR/CSR: %s\n", strerror(errno));
return 1;
}
/* First check we're accessing a configuration ROM */
cr_sig[0] = swapbe32(crcsr[VD80_CR_SIG1/4]) & 0xff;
if (vme_bus_error_check(&crcsr_desc)) {
printf("Bus error reading CR signature byte 0 at %08x\n",
VD80_SETUP_BASE + VD80_CR_SIG1);
goto out;
}
cr_sig[1] = swapbe32(crcsr[VD80_CR_SIG2/4]) & 0xff;
if (vme_bus_error_check(&crcsr_desc)) {
printf("Bus error reading CR signature byte 1 at %08x\n",
VD80_SETUP_BASE + VD80_CR_SIG2);
goto out;
}
if ((cr_sig[0] != 'C') && (cr_sig[1] != 'R')) {
printf("Not a Configuration ROM at 0x%x (%08x %08x)\n",
VD80_SETUP_BASE, swapbe32(crcsr[VD80_CR_SIG1/4]),
swapbe32(crcsr[VD80_CR_SIG2/4]));
goto out;
}
/* Try to read module ID - offset 0x30 */
for (i = 0; i < VD80_CR_BOARD_ID_LEN; i++) {
val = swapbe32(crcsr[VD80_CR_BOARD_ID/4 + i]);
board_id[i] = (char)(val & 0xff);
}
board_id[4] = '\0';
if (vme_bus_error_check(&crcsr_desc)) {
printf("Bus error reading board ID\n");
goto out;
}
for (i = 0; i < VD80_CR_REV_ID_LEN; i++) {
val = swapbe32(crcsr[VD80_CR_REV_ID/4 + i]);
rev_id[i] = (char)(val & 0xff);
}
rev_id[4] = '\0';
if (vme_bus_error_check(&crcsr_desc)) {
printf("Bus error reading revision ID\n");
goto out;
}
if (strncmp(board_id, "VD80", 4)) {
printf("No VD80 board found at base address 0x%x\n",
VD80_SETUP_BASE);
goto out;
}
printf("Found board ID %s - revision ID: %s\n", board_id, rev_id);
/* Read board string */
desc_offset = 0;
for (i = 0; i < VD80_CR_DESC_PTR_LEN; i++) {
desc_offset <<= 8;
desc_offset |= swapbe32(crcsr[VD80_CR_DESC_PTR/4 + i]) & 0xff;
}
desc_offset &= ~3;
if (desc_offset != 0) {
for (i = 0; i < VD80_UCR_BOARD_DESC_LEN; i++) {
val = swapbe32(crcsr[0x1040/4 + i]);
board_desc[i] = (char)(val & 0xff);
if (board_desc[i] == '\0')
break;
}
board_desc[320] = '\0';
}
if (vme_bus_error_check(&crcsr_desc)) {
printf("Bus error reading board description\n");
goto out;
}
printf("Board name: %s\n", board_desc);
printf("\n");
/* Configure ADER0 for A24 USER DATA access (AM=0x39 BA=0x600000) */
crcsr[VD80_CSR_ADER0/4] = swapbe32((VD80_OPER_BASE >> 24) & 0xff);
crcsr[VD80_CSR_ADER0/4 + 1] = swapbe32((VD80_OPER_BASE >> 16) & 0xff);
crcsr[VD80_CSR_ADER0/4 + 2] = swapbe32((VD80_OPER_BASE >> 8) & 0xff);
crcsr[VD80_CSR_ADER0/4 + 3] = swapbe32(VD80_OPER_AM * 4);
if (vme_bus_error_check(&crcsr_desc)) {
printf("Bus error setting ADER0\n");
goto out;
}
/* Configure ADER1 for A24 USER block transfer (AM=0x3b BA=0x600000) */
crcsr[VD80_CSR_ADER1/4] = swapbe32((VD80_OPER_BASE >> 24) & 0xff);
crcsr[VD80_CSR_ADER1/4 + 1] = swapbe32((VD80_OPER_BASE >> 16) & 0xff);
crcsr[VD80_CSR_ADER1/4 + 2] = swapbe32((VD80_OPER_BASE >> 8) & 0xff);
crcsr[VD80_CSR_ADER1/4 + 3] = swapbe32(VD80_DMA_AM * 4);
if (vme_bus_error_check(&crcsr_desc)) {
printf("Bus error setting ADER1\n");
goto out;
}
/*
* Clear address counter enable for function 1 to use it for block
* transfer of the sample memory.
*/
val = swapbe32(crcsr[VD80_CSR_FUNC_ACEN/4]) | 0x02;
crcsr[VD80_CSR_FUNC_ACEN/4] = swapbe32(0);
if (vme_bus_error_check(&crcsr_desc)) {
printf("Bus error setting ACEN\n");
goto out;
}
/* Enable the module */
crcsr[VD80_CSR_BITSET/4] = swapbe32(VD80_CSR_ENABLE_MODULE);
if (vme_bus_error_check(&crcsr_desc)) {
printf("Bus error enabling module\n");
goto out;
}
rc = 0;
out:
if (vme_unmap(&crcsr_desc, 1))
printf("Failed to unmap CR/CSR space: %s\n",
strerror(errno));
return rc;
}
/**
* \brief Map and initialize the VD80 for operation
*
* \return 0 on success else 1 on failure.
*
* This functions performs the necessary initializations to prepare the
* board for sampling:
*
* \li map the operational registers into the user address space
* \li clear any interrupts
* \li disable interrupts
* \li configure the sampling clock source to use the internal clock divided
* by 2 which yields a 100kHz sampling clock
* \li configure the trigger source to use the internal trigger
* \li set the number of post samples to 0
* \li setup the pretrigger control to clear the pretrigger counter on a
* \a START command, to clear the \a PRESAMPLES counter on reading the
* pretrigger status register and to unse the sample clock for the
* pretrigger counter
* \li enable generation of the test waveform
*
* \note Those settings are \b testing settings and must be adapted for
* operational use.
*
*/
int vd80_init(void)
{
int rc = 1;
/* Open a mapping for the VD80 operational registers */
memset(&regs_desc, 0, sizeof(struct vme_mapping));
regs_desc.window_num = 0;
regs_desc.am = VD80_OPER_AM;
regs_desc.read_prefetch_enabled = 0;
regs_desc.data_width = VD80_OPER_DW;
regs_desc.sizeu = 0;
regs_desc.sizel = VD80_OPER_SIZE;
regs_desc.vme_addru = 0;
regs_desc.vme_addrl = VD80_OPER_BASE;
if ((regs = vme_map(&regs_desc, 1)) == NULL) {
printf("Failed to map VD80 operational registers: %s\n",
strerror(errno));
printf(" addr: %x size: %x am: %x dw: %d\n", VD80_OPER_BASE,
VD80_OPER_SIZE, VD80_OPER_AM, VD80_OPER_DW);
return 1;
}
/* Clear interrupts */
regs[VD80_GCR1/4] |= swapbe32(VD80_INTCLR | VD80_IOERRCLR);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error initializing GCR1\n");
goto out;
}
/*
* Disable interrupts, set little endian mode and disable debug
* counter (Clear all bits).
*/
regs[VD80_GCR2/4] = 0;
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error initializing GCR2\n");
goto out;
}
/*
* Set sampling clock:
* - Internal clock
* - No VXI clock output
* - Divide mode
* - Rising edge
* - No 50 ohm termination
* - Clkdiv = 2 (I want a 100kHz clock from the 200 kHz internal
* clock - which yields a 10 us sampling period)
*/
regs[VD80_CCR/4] = swapbe32(1 << VD80_CLKDIV_SHIFT);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error initializing CCR\n");
goto out;
}
/*
* Set trigger:
* - Internal trigger
* - No VXI trigger output
* - No trigger output
* - Rising edge
* - No 50 ohm termination
* - No trigger synchronization
*/
regs[VD80_TCR1/4] = 0;
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error initializing TCR1\n");
goto out;
}
/* Set number of post samples to 0 */
if (vd80_set_postsamples(0))
goto out;
/*
* Setup the Pretrigger Control Register:
* - Clear pretrigger counter on START
* - Clear PRESAMPLES on Pretrigger Status Register read
* - Clock the pretrigger counter on the sample clock
*/
regs[VD80_PTCR/4] |= swapbe32(VD80_PSCNTCLRONSTART |
VD80_PSREGCLRONREAD |
VD80_PSCNTCLKONSMPL);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error initializing PTCR\n");
goto out;
}
/* Enable generation of debug waveform */
if ((rc = vd80_cmd_setdebug(1)))
goto out;
return 0;
out:
if (vme_unmap(&regs_desc, 1))
printf("Failed to unmap operational registers: %s\n",
strerror(errno));
return rc;
}
/**
* \brief Unmap the VD80 operational address space
*
* \return 0 on success else 1 on failure
*/
int vd80_exit(void)
{
int rc = 0;
if ((rc = vme_unmap(&regs_desc, 1)))
printf("Failed to return_controler: %s\n", strerror(errno));
printf("Unmapped VD80 registers\n");
return rc;
}
/**
* \file libvd80.h
* \brief VD80 access user library interface
* \author Sbastien Dugu
* \date 04/02/2009
*
* This library gives userspace applications access to the VD80 board.
*
* Copyright (c) 2009 \em Sbastien \em Dugu
*
* \par License:
* 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.
*
*/
#ifndef _LIBVD80_H
#define _LIBVD80_H
#include "vd80.h"
/**
* \name CR/CSR address space mapping parameters
* \{
*/
/** CR/CSR base address */
#define VD80_SETUP_BASE 0x600000
/** CR/CSR size */
#define VD80_SETUP_SIZE 0x80000
/** CR/CSR access address modifier */
#define VD80_SETUP_AM VME_CR_CSR
/** CR/CSR access data width */
#define VD80_SETUP_DW VME_D32
/* \}*/
/**
* \name Operational registers mapping parameters
* \{
*/
/** Registers base address */
#define VD80_OPER_BASE 0x600000
/** Registers size */
#define VD80_OPER_SIZE 0x100
/** Registers access address modifier */
#define VD80_OPER_AM VME_A24_USER_DATA_SCT
/** Registers access data width */
#define VD80_OPER_DW VME_D32
/* \}*/
/**
* \name Operational DMA parameters
* \{
*/
/** DMA base address */
#define VD80_DMA_BASE VD80_OPER_BASE + VD80_MEMOUT
/** DMA address modifier */
#define VD80_DMA_AM VME_A24_USER_MBLT
/** DMA data width */
#define VD80_DMA_DW VME_D32
/* \}*/
extern int vd80_cmd_start();
extern int vd80_cmd_stop();
extern int vd80_cmd_trig();
extern int vd80_cmd_substart();
extern int vd80_cmd_substop();
extern int vd80_cmd_read(unsigned int);
extern int vd80_cmd_single();
extern int vd80_cmd_clear();
extern int vd80_cmd_setbig(int);
extern int vd80_cmd_setdebug(int);
extern int vd80_set_postsamples(unsigned int);
extern int vd80_get_postsamples();
extern int vd80_get_pre_length();
extern int vd80_get_post_length();
extern int vd80_get_shot_length();
extern int vd80_set_readstart(unsigned int);
extern int vd80_set_readlen(unsigned int);
extern int vd80_get_sample();
extern int vd80_map();
extern int vd80_init();
extern int vd80_exit();
/* libvd80 in the vmebridge version string */
extern const char * const libvd80vmebridge_version_s;
#endif /* _LIBVD80_H */
/*
* mm6390-dma.c - Test DMA using an MM6390 memory board
*
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/mman.h>
#include <libvmebus.h>
static char git_version[] = "git_version: " GIT_VERSION;
/* Registers address space */
#define REGS_BASE 0
#define REGS_OFFSET 0xbe00
#define REGS_SIZE 0x10000
#define REGS_AM VME_A16_USER
#define REGS_DBW VME_D16
/* Memory array address space */
#define MEM_BASE 0
#define MEM_SIZE 0x2000000
#define MEM_AM VME_A32_USER_MBLT
#define MEM_DBW VME_D32
struct vme_mapping regs_desc;
unsigned char *regs;
struct vme_dma dma_desc;
unsigned long long *wr_buf;
unsigned long long *rd_buf;
long long timespec_subtract(struct timespec * a, struct timespec *b)
{
long long ns;
ns = (b->tv_sec - a->tv_sec) * 1000000000LL;
ns += (b->tv_nsec - a->tv_nsec);
return ns;
}
int main(int argc, char **argv)
{
int rc = 0;
int i;
int val;
struct timespec write_start;
struct timespec write_end;
struct timespec read_start;
struct timespec read_end;
unsigned long long delta_ns;
double delta;
double throughput;
printf("%s %s\n", argv[0], git_version);
printf("%s\n", libvmebus_version_s);
/* Open a mapping for the CSR register */
memset(&regs_desc, 0, sizeof(struct vme_mapping));
regs_desc.am = REGS_AM;
regs_desc.read_prefetch_enabled = 0;
regs_desc.data_width = REGS_DBW;
regs_desc.sizeu = 0;
regs_desc.sizel = REGS_SIZE;
regs_desc.vme_addru = 0;
regs_desc.vme_addrl = REGS_BASE;
if ((regs = vme_map(&regs_desc, 1)) == NULL) {
printf("Failed to map CSR register: %s\n",
strerror(errno));
printf(" addr: %x size: %x am: %x dw: %d\n", REGS_BASE,
REGS_SIZE, REGS_AM, REGS_DBW);
return 1;
}
val = regs[REGS_OFFSET+1];
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error reading CSR\n");
rc = 1;
goto out;
}
printf("CSR reg: %x\n", val);
regs[REGS_OFFSET+1] = 0;
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error writing CSR\n");
rc = 1;
goto out;
}
/* Allocate memory for the buffers */
if (posix_memalign((void **)&wr_buf, 4096, MEM_SIZE)) {
printf("Failed to allocate buffer: %s\n", strerror(errno));
rc = 1;
goto out;
}
/* Allocate memory for the buffers */
if (posix_memalign((void **)&rd_buf, 4096, MEM_SIZE)) {
printf("Failed to allocate buffer: %s\n", strerror(errno));
rc = 1;
goto out;
}
/* Initialize the buffer */
printf("Initializing buffer\n");
for (i = 0; i < MEM_SIZE/8; i++)
wr_buf[i] = i;
/* DMA write the buffer */
printf("DMA Writing %d Bytes\n", MEM_SIZE);
memset(&dma_desc, 0, sizeof(struct vme_dma));
dma_desc.dir = VME_DMA_TO_DEVICE;
/* strange casting to keep compiler quiet */
dma_desc.src.addru = (unsigned int)
(((unsigned long long)(uintptr_t)rd_buf)>>32);
dma_desc.src.addrl = (unsigned int)(uintptr_t)wr_buf;
dma_desc.dst.data_width = MEM_DBW;
dma_desc.dst.am = MEM_AM;
dma_desc.dst.addru = 0;
dma_desc.dst.addrl = MEM_BASE;
dma_desc.length = MEM_SIZE;
dma_desc.ctrl.pci_block_size = VME_DMA_BSIZE_4096;
dma_desc.ctrl.pci_backoff_time = VME_DMA_BACKOFF_0;
dma_desc.ctrl.vme_block_size = VME_DMA_BSIZE_4096;
dma_desc.ctrl.vme_backoff_time = VME_DMA_BACKOFF_0;
clock_gettime(CLOCK_MONOTONIC, &write_start);
if ((rc = vme_dma_write(&dma_desc))) {
printf("Failed to DMA write memory: %s\n", strerror(errno));
goto out;
}
clock_gettime(CLOCK_MONOTONIC, &write_end);
/* DMA read the buffer back */
printf("DMA Reading %d Bytes\n", MEM_SIZE);
memset(&dma_desc, 0, sizeof(struct vme_dma));
dma_desc.dir = VME_DMA_FROM_DEVICE;
dma_desc.src.data_width = MEM_DBW;
dma_desc.src.am = MEM_AM;
dma_desc.src.addru = 0;
dma_desc.src.addrl = MEM_BASE;
/* strange casting to keep compiler quiet */
dma_desc.dst.addru = (unsigned int)
(((unsigned long long)(uintptr_t)rd_buf)>>32);
dma_desc.dst.addrl = (unsigned int)(uintptr_t)rd_buf;
dma_desc.length = MEM_SIZE;
dma_desc.ctrl.pci_block_size = VME_DMA_BSIZE_4096;
dma_desc.ctrl.pci_backoff_time = VME_DMA_BACKOFF_0;
dma_desc.ctrl.vme_block_size = VME_DMA_BSIZE_4096;
dma_desc.ctrl.vme_backoff_time = VME_DMA_BACKOFF_0;
clock_gettime(CLOCK_MONOTONIC, &read_start);
if ((rc = vme_dma_read(&dma_desc))) {
printf("Failed to DMA read memory: %s\n", strerror(errno));
goto out;
}
clock_gettime(CLOCK_MONOTONIC, &read_end);
/* Compare buffers */
for (i = 0; i < MEM_SIZE/8; i++) {
if (rd_buf[i] != wr_buf[i]) {
rc = 1;
break;
}
}
if (rc != 0) {
printf("Buffer mismatch at offset 0x%08x\n", i*8);
for (i = 0; i < 10; i++)
printf("%04x %016llx %016llx\n",
i*8, wr_buf[i], rd_buf[i]);
} else
printf("Success\n\n");
delta_ns = timespec_subtract(&write_start, &write_end);
delta = (double)delta_ns/(double)1000000000.0;
throughput = (double)(MEM_SIZE) / delta;
printf("Write time: %.6fs - throughput: %.6f MB/s\n",
delta, throughput/(1024.0*1024.0));
delta_ns = timespec_subtract(&read_start, &read_end);
delta = (double)delta_ns/(double)1000000000.0;
throughput = (double)(MEM_SIZE) / delta;
printf("Read time: %.6fs - throughput: %.6f MB/s\n",
delta, throughput/(1024.0*1024.0));
out:
if (vme_unmap(&regs_desc, 1))
printf("Failed to unmap CSR register: %s\n",
strerror(errno));
return rc;
}
/*
* Set or clear the force create and force destroy flags
*
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <vmebus.h>
static char git_version[] = "git_version: " GIT_VERSION;
char *prgname;
int force_create = -1;
int force_destroy = -1;
int show_flags = 0;
#define VME_MWINDOW_DEV "/dev/vme_mwindow"
void usage()
{
printf("Usage: %s -c {0|1} -d {0|1} [-s]\n",
prgname);
printf(" -c: Set or clear force create flag\n");
printf(" -d: Set or clear force destroy flag\n");
printf(" -s: Show the current flags status\n");
printf("\n%s\n", git_version);
printf("\n");
}
void parse_args(int argc, char **argv)
{
prgname = argv[0];
argv++;
argc--;
while (argc) {
char opt;
if (**argv != '-') {
printf("Unknown option prefix %s, skipping\n",
*argv);
argv++;
argc--;
if (argc == 0) {
printf("No more arguments\n\n");
usage();
exit(1);
}
}
opt = *(*argv + 1);
argv++;
argc--;
switch (opt) {
case 's':
show_flags = 1;
break;
case 'c':
if (sscanf(*argv, "%d", &force_create) != 1) {
printf("Failed to parse force create flag\n");
usage();
exit(1);
}
argv++;
argc--;
break;
case 'd':
if (sscanf(*argv, "%d", &force_destroy) != 1) {
printf("Failed to parse force destroy flag\n");
usage();
exit(1);
}
argv++;
argc--;
break; default:
printf("Unknown option -%c\n\n", opt);
usage();
exit(1);
break;
}
}
if ((force_create == -1) && (force_destroy == -1) &&
(show_flags == 0)) {
printf("\n\nHuh, what do you want to do?\n\n");
usage();
exit(1);
}
}
int main(int argc, char **argv)
{
int fd;
int rc;
parse_args(argc, argv);
if ((fd = open(VME_MWINDOW_DEV, O_RDWR)) < 0) {
printf("Failed to open %s: %s\n", VME_MWINDOW_DEV,
strerror(errno));
exit(1);
}
if (show_flags) {
int orig_force_create;
int orig_force_destroy;
if ((rc = ioctl(fd, VME_IOCTL_GET_CREATE_ON_FIND_FAIL,
&orig_force_create)) < 0) {
printf("Failed to get force create flag: %s\n",
strerror(errno));
close(fd);
exit(1);
}
if ((rc = ioctl(fd, VME_IOCTL_GET_DESTROY_ON_REMOVE,
&orig_force_destroy)) < 0) {
printf("Failed to get force destroy flag: %s\n",
strerror(errno));
close(fd);
exit(1);
}
printf("\nForce flags state: create %d - destroy %d\n\n",
orig_force_create, orig_force_destroy);
}
if (force_create != -1) {
printf("%s force create flag\n",
(force_create == 0)?"Clearing":"Setting");
if ((rc = ioctl(fd, VME_IOCTL_SET_CREATE_ON_FIND_FAIL,
&force_create)) < 0) {
printf("Failed to set force create flag: %s\n",
strerror(errno));
close(fd);
exit(1);
}
}
if (force_destroy != -1) {
printf("%s force destroy flag\n",
(force_destroy == 0)?"Clearing":"Setting");
if ((rc = ioctl(fd, VME_IOCTL_SET_DESTROY_ON_REMOVE,
&force_create)) < 0) {
printf("Failed to set force destroy flag: %s\n",
strerror(errno));
close(fd);
exit(1);
}
}
close(fd);
return 0;
}
create_window.L865 0 0x29 16 0x0 0x10000
create_window.L865 1 0x39 16 0x10000 0x1000000
create_window.L865 2 0x39 32 0x10000 0x1000000
create_window.L865 6 0x09 32 0x400000 0x1000000
create_window.L865 7 0x09 16 0x10000 0x1000000
# create_mapping.L865 -n 3600 -m 39 -w 16 -a 0100c000 -s 4000
# create_mapping.L865 -n 3600 -m 39 -w 16 -a 0100c000 -s 5000
# create_mapping.L865 -n 3600 -m 09 -w 32 -a 00410000 -s 10000
/*
* test_mapping.L865 -c -m 0x29 -w 16 -a 0x0 -s 0x10000
*
* test_mapping.L865 -c -d -n 60 -m 0x29 -w 16 -a 0x0 -s 0x10000
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <vmebus.h>
static char git_version[] = "git_version: " GIT_VERSION;
char *prgname;
struct vme_mapping mapping;
int am = -1;
int dsize = -1;
int vmeaddr = -1;
int length = -1;
int force_create = -1;
int force_destroy = -1;
int snooze = 0;
#define VME_MWINDOW_DEV "/dev/vme_mwindow"
void usage()
{
printf("Usage: %s [-c] [-d] [-n snooze] -m am -w dwidth "
"-a vmeaddr -s size\n",
prgname);
printf(" -c: force creation of window if needed\n");
printf(" -d: force deletion of window if needed\n");
printf(" -n snooze: sleep duration (default 0)\n");
printf(" -m am: standard VME address modifier (in hex)\n");
printf(" -w dwidth: Data width 16 or 32\n");
printf(" -a vmeaddr: VME start address (in hex)\n");
printf(" -s size: mapping size (in hex)\n");
printf("\n%s\n", git_version);
printf("\n");
}
void parse_args(int argc, char **argv)
{
int err = 0;
prgname = argv[0];
argv++;
argc--;
while (argc) {
char opt;
if (**argv != '-') {
printf("Unknown option prefix %s, skipping\n",
*argv);
argv++;
argc--;
if (argc == 0) {
printf("No more arguments\n\n");
usage();
exit(1);
}
}
opt = *(*argv + 1);
argv++;
argc--;
switch (opt) {
case 'c':
force_create = 1;
break;
case 'd':
force_destroy = 1;
break;
case 'n':
if (sscanf(*argv, "%d", &snooze) != 1) {
printf("Failed to parse snooze duration\n");
usage();
exit(1);
}
argv++;
argc--;
break;
case 'm':
if (sscanf(*argv, "%x", &am) != 1) {
printf("Failed to parse address modifier\n");
usage();
exit(1);
}
argv++;
argc--;
break;
case 'w':
if (sscanf(*argv, "%d", &dsize) != 1) {
printf("Failed to parse data size\n");
usage();
exit(1);
}
argv++;
argc--;
break;
case 'a':
if (sscanf(*argv, "%x", &vmeaddr) != 1) {
printf("Failed to parse VME address\n");
usage();
exit(1);
}
argv++;
argc--;
break;
case 's':
if (sscanf(*argv, "%x", &length) != 1) {
printf("Failed to parse length\n");
usage();
exit(1);
}
argv++;
argc--;
break;
default:
printf("Unknown option -%c\n\n", opt);
usage();
exit(1);
break;
}
}
if (am == -1) {
err = 1;
printf("No address modifier specified\n");
}
if (dsize == -1) {
err = 1;
printf("No data width specified\n");
}
if (vmeaddr == -1) {
err = 1;
printf("No VME start address specified\n");
}
if (length == -1) {
err = 1;
printf("No length specified\n");
}
if (err) {
printf("\n");
usage();
exit(1);
}
}
void dump_mapping(struct vme_mapping *m)
{
printf("\n");
printf("\tWindow number: %d\n", m->window_num);
printf("\tAddress modifier: 0x%0x\n", m->am);
printf("\tData width: %d\n", m->data_width);
printf("\tRead prefetch enabled: %d\n", m->read_prefetch_enabled);
printf("\tRead prefetch size: %d\n", m->read_prefetch_size);
printf("\t2eSST speed: %d\n", m->v2esst_mode);
printf("\t2eSST bcast select: %d\n", m->bcast_select);
printf("\tPCI address: 0x%08x:%08x\n", m->pci_addru, m->pci_addrl);
printf("\tVME address: 0x%08x:%08x\n", m->vme_addru, m->vme_addrl);
printf("\tSize: 0x%08x:%08x\n", m->sizeu, m->sizel);
printf("\n");
}
int main(int argc, char **argv)
{
int fd;
int org_force_create;
int org_force_destroy;
parse_args(argc, argv);
memset(&mapping, 0, sizeof(struct vme_mapping));
mapping.window_num = 0;
mapping.am = am;
switch (dsize) {
case 16:
mapping.data_width = VME_D16;
break;
case 32:
mapping.data_width = VME_D32;
break;
default:
printf("Unsupported data width %d\n", dsize);
exit(1);
}
mapping.vme_addru = 0;
mapping.vme_addrl = vmeaddr;
mapping.sizeu = 0;
mapping.sizel = length;
printf("Creating mapping\n\tVME addr: 0x%08x size: 0x%08x "
"AM: 0x%02x data width: %d\n\n",
vmeaddr, length, am, dsize);
if ((fd = open(VME_MWINDOW_DEV, O_RDWR)) < 0) {
printf("Failed to open %s: %s\n", VME_MWINDOW_DEV,
strerror(errno));
exit(1);
}
if (ioctl(fd, VME_IOCTL_GET_CREATE_ON_FIND_FAIL,
&org_force_create) < 0) {
printf("Failed to get force create flag: %s\n",
strerror(errno));
close(fd);
exit(1);
}
if (ioctl(fd, VME_IOCTL_GET_DESTROY_ON_REMOVE,
&org_force_destroy) < 0) {
printf("Failed to get force destroy flag: %s\n",
strerror(errno));
close(fd);
exit(1);
}
if (force_create != -1) {
if (ioctl(fd, VME_IOCTL_SET_CREATE_ON_FIND_FAIL,
&force_create) < 0) {
printf("Failed to set force create flag: %s\n",
strerror(errno));
close(fd);
exit(1);
}
}
if (force_destroy != -1) {
if (ioctl(fd, VME_IOCTL_SET_DESTROY_ON_REMOVE,
&force_destroy) < 0) {
printf("Failed to set force destroy flag: %s\n",
strerror(errno));
close(fd);
exit(1);
}
}
if (ioctl(fd, VME_IOCTL_FIND_MAPPING, &mapping) < 0) {
printf("Failed to create mapping: %s\n", strerror(errno));
close(fd);
exit(1);
}
dump_mapping(&mapping);
if (snooze) {
printf("Going to sleep for %ds\n", snooze);
sleep(snooze);
printf("Back to business\n");
}
if (ioctl(fd, VME_IOCTL_RELEASE_MAPPING, &mapping) < 0) {
printf("Failed to release mapping: %s\n", strerror(errno));
close(fd);
exit(1);
}
/* Restore original force flags */
if (ioctl(fd, VME_IOCTL_SET_CREATE_ON_FIND_FAIL,
&org_force_create) < 0) {
printf("Failed to restore force_create flag: %s\n",
strerror(errno));
close(fd);
exit(1);
}
if (ioctl(fd, VME_IOCTL_SET_DESTROY_ON_REMOVE,
&org_force_destroy) < 0) {
printf("Failed to restore force_destroy flag: %s\n",
strerror(errno));
close(fd);
exit(1);
}
close(fd);
return 0;
}
/*
* testctr.c - simple mmap test to access a CTR card.
*
* CTR Main logic - A24/D32 0x39 @0xC00000 size 0x10000
*
* CtrDrvrInterruptMask InterruptSource; Cleared on read
* CtrDrvrInterruptMask InterruptEnable; Enable interrupts
* unsigned long HptdcJtag; Jtag control reg for HpTdc
* unsigned long InputDelay; Specified in 40MHz ticks
* unsigned long CableId; ID of CTG piloting GMT
* unsigned long VhdlVersion; UTC time of VHDL build
* unsigned long OutputByte; Output byte number 1..8 on VME P2 +
* Enable front pannel
* CtrDrvrStatus Status; Current status
* CtrDrvrCommand Command; Write command to module
* CtrDrvrPll Pll; Pll parameters
* CtrDrvrCTime ReadTime; Latched date time and CTrain
* unsigned long SetTime; Used to set UTC if no cable
* CtrDrvrCounterBlock Counters; The counter configurations and status
* CtrDrvrTgmBlock Telegrams; Active telegrams
* CtrDrvrRamTriggerTable Trigs; 2048 Trigger conditions
* CtrDrvrRamConfigurationTable Configs; 2048 Counter configurations
* CtrDrvrEventHistory EventHistory; 1024 Events deep
* unsigned long Setup; VME Level and Interrupt vector
* unsigned long LastReset; UTC Second of last reset
* unsigned long PartityErrs; Number of parity errors since last reset
* unsigned long SyncErrs; Number of frame synchronization errors since
* last reset
* unsigned long TotalErrs; Total number of IO errors since last reset
* unsigned long CodeViolErrs; Number of code violations since last reset
* unsigned long QueueErrs; Number of input Queue overflows since last
* reset
*
* New registers used only in recent VHDL, old makes a bus error !
*
* CtrDrvrIoStatus IoStat; IO status
* unsigned long IdLSL; ID Chip value Least Sig 32-bits
* unsigned long IdMSL; ID Chip value Most Sig 32-bits
* CtrDrvrModuleStats ModStats; Module statistics
*
*
*
* CTR JTAG - A16/D16 0x29 @
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <vmebus.h>
static char git_version[] = "git_version: " GIT_VERSION;
#define VME_MWINDOW_DEV "/dev/vme_mwindow"
#define MODULE_AM VME_A24_USER_DATA_SCT
#define MODULE_DW VME_D32
#define MODULE_VME_ADDR 0xc00000
#define MODULE_VME_SIZE 0x10000
static unsigned int swapbe(unsigned int val)
{
return (((val & 0xff000000) >> 24) | ((val & 0xff0000) >> 8) |
((val & 0xff00) << 8) | ((val & 0xff) << 24));
}
int main(int argc, char **argv)
{
int fd;
struct vme_mapping desc;
unsigned int *vme_buf;
int org_force_create;
int org_force_destroy;
int force_create;
int force_destroy;
int i;
unsigned int test;
int bus_err;
printf("%s: %s\n\n", argv[0], git_version);
/* Setup the mapping descriptor */
memset(&desc, 0, sizeof(struct vme_mapping));
desc.window_num = 0;
desc.am = MODULE_AM;
desc.data_width = VME_D32;
desc.vme_addru = 0;
desc.vme_addrl = MODULE_VME_ADDR;
desc.sizeu = 0;
desc.sizel = MODULE_VME_SIZE;
printf("Creating mapping\n\tVME addr: 0x%08x size: 0x%08x "
"AM: 0x%02x data width: %d\n\n",
desc.vme_addrl, desc.sizel, desc.am, desc.data_width);
if ((fd = open(VME_MWINDOW_DEV, O_RDWR)) < 0) {
printf("Failed to open %s: %s\n", VME_MWINDOW_DEV,
strerror(errno));
exit(1);
}
/* Save the force flags */
if (ioctl(fd, VME_IOCTL_GET_CREATE_ON_FIND_FAIL,
&org_force_create) < 0) {
printf("Failed to get force create flag: %s\n",
strerror(errno));
goto out_err;
}
if (ioctl(fd, VME_IOCTL_GET_DESTROY_ON_REMOVE,
&org_force_destroy) < 0) {
printf("Failed to get force destroy flag: %s\n",
strerror(errno));
goto out_err;
}
/* Set the force flags */
force_create = 1;
if (ioctl(fd, VME_IOCTL_SET_CREATE_ON_FIND_FAIL, &force_create) < 0) {
printf("Failed to set force create flag: %s\n",
strerror(errno));
goto out_err;
}
force_destroy = 1;
if (ioctl(fd, VME_IOCTL_SET_DESTROY_ON_REMOVE, &force_destroy) < 0) {
printf("Failed to set force create flag: %s\n",
strerror(errno));
goto out_err;
}
/* Create a new mapping for the area */
if (ioctl(fd, VME_IOCTL_FIND_MAPPING, &desc) < 0) {
printf("Failed to create mapping: %s\n", strerror(errno));
goto out_err;
}
/* Now mmap the area */
if ((vme_buf = mmap(0, MODULE_VME_SIZE, PROT_READ, MAP_PRIVATE, fd,
desc.pci_addrl)) == MAP_FAILED) {
printf("Failed to mmap: %s\n", strerror(errno));
goto out_mmap_err;
}
/* Now test read access */
for (i = 1; i <= 5; i++) {
test = swapbe(vme_buf[i]);
printf("Read 0x%08x at address 0x%08x\n",
test, MODULE_VME_ADDR + i*4);
}
/* Check for a bus error */
if (ioctl(fd, VME_IOCTL_GET_BUS_ERROR, &bus_err) < 0) {
printf("Failed to get bus error status: %s\n", strerror(errno));
goto out_err;
}
if (bus_err)
printf("Bus error occured\n");
sleep(10);
if (munmap(vme_buf, MODULE_VME_SIZE))
printf("Failed to unmap: %s\n", strerror(errno));
out_mmap_err:
if (ioctl(fd, VME_IOCTL_RELEASE_MAPPING, &desc) < 0)
printf("Failed to release mapping: %s\n", strerror(errno));
out_err:
/* Restore flags */
if (ioctl(fd, VME_IOCTL_SET_CREATE_ON_FIND_FAIL, &org_force_create) < 0)
printf("Failed to restore force create flag: %s\n",
strerror(errno));
if (ioctl(fd, VME_IOCTL_SET_DESTROY_ON_REMOVE, &org_force_destroy) < 0)
printf("Failed to restore force destroy flag: %s\n",
strerror(errno));
close(fd);
return 0;
}
/*
* testctr_ces.c - simple test to access a CTR card using the CES emulation.
*
* CTR Main logic - A24/D32 0x39 @0xC00000 size 0x10000
*
* CtrDrvrInterruptMask InterruptSource; Cleared on read
* CtrDrvrInterruptMask InterruptEnable; Enable interrupts
* unsigned long HptdcJtag; Jtag control reg for HpTdc
* unsigned long InputDelay; Specified in 40MHz ticks
* unsigned long CableId; ID of CTG piloting GMT
* unsigned long VhdlVersion; UTC time of VHDL build
* unsigned long OutputByte; Output byte number 1..8 on VME P2 +
* Enable front pannel
* CtrDrvrStatus Status; Current status
* CtrDrvrCommand Command; Write command to module
* CtrDrvrPll Pll; Pll parameters
* CtrDrvrCTime ReadTime; Latched date time and CTrain
* unsigned long SetTime; Used to set UTC if no cable
* CtrDrvrCounterBlock Counters; The counter configurations and status
* CtrDrvrTgmBlock Telegrams; Active telegrams
* CtrDrvrRamTriggerTable Trigs; 2048 Trigger conditions
* CtrDrvrRamConfigurationTable Configs; 2048 Counter configurations
* CtrDrvrEventHistory EventHistory; 1024 Events deep
* unsigned long Setup; VME Level and Interrupt vector
* unsigned long LastReset; UTC Second of last reset
* unsigned long PartityErrs; Number of parity errors since last reset
* unsigned long SyncErrs; Number of frame synchronization errors since
* last reset
* unsigned long TotalErrs; Total number of IO errors since last reset
* unsigned long CodeViolErrs; Number of code violations since last reset
* unsigned long QueueErrs; Number of input Queue overflows since last
* reset
*
* New registers used only in recent VHDL, old makes a bus error !
*
* CtrDrvrIoStatus IoStat; IO status
* unsigned long IdLSL; ID Chip value Least Sig 32-bits
* unsigned long IdMSL; ID Chip value Most Sig 32-bits
* CtrDrvrModuleStats ModStats; Module statistics
*
*
*
* CTR JTAG - A16/D16 0x29 @
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <libvmebus.h>
static char git_version[] = "git_version: " GIT_VERSION;
#define MODULE_AM VME_A24_USER_DATA_SCT
#define MODULE_DW VME_D32
#define MODULE_VME_ADDR 0xc00000
#define MODULE_VME_SIZE 0x10000
int main(int argc, char **argv)
{
struct pdparam_master param;
struct vme_mapping *desc;
unsigned int *vme_buf;
unsigned int val;
printf("%s: %s\n", argv[0], git_version);
printf("%s\n\n", libvmebus_version_s);
memset(&param, 0, sizeof(struct pdparam_master));
if ((vme_buf = (unsigned int *)find_controller(MODULE_VME_ADDR,
MODULE_VME_SIZE,
MODULE_AM, 0, 4,
&param)) == 0) {
printf("Failed to find_controller: %s\n", strerror(errno));
exit(1);
}
printf("find_controller mmap'ed at %p\n\n", vme_buf);
desc = (struct vme_mapping *)param.sgmin;
val = vme_buf[1];
printf("Read 0x%08x at %x\n", val, MODULE_VME_ADDR + 4);
/* Check for a bus error */
if (vme_bus_error_check(desc))
printf("Bus error occured\n");
printf("Writing 0x55aa55aa at address %x\n", MODULE_VME_ADDR + 4);
vme_buf[1] = 0x55aa55aa;
/* Check for a bus error */
if (vme_bus_error_check(desc))
printf("Bus error occured\n");
printf("\n");
if (return_controller(desc))
printf("Failed to return_controler: %s\n", strerror(errno));
return 0;
}
/*
* testctr_ces.c - simple test to access a CTR card using the CES emulation.
*
* CTR Main logic - A24/D32 0x39 @0xC00000 size 0x10000
*
* CtrDrvrInterruptMask InterruptSource; Cleared on read
* CtrDrvrInterruptMask InterruptEnable; Enable interrupts
* unsigned long HptdcJtag; Jtag control reg for HpTdc
* unsigned long InputDelay; Specified in 40MHz ticks
* unsigned long CableId; ID of CTG piloting GMT
* unsigned long VhdlVersion; UTC time of VHDL build
* unsigned long OutputByte; Output byte number 1..8 on VME P2 +
* Enable front pannel
* CtrDrvrStatus Status; Current status
* CtrDrvrCommand Command; Write command to module
* CtrDrvrPll Pll; Pll parameters
* CtrDrvrCTime ReadTime; Latched date time and CTrain
* unsigned long SetTime; Used to set UTC if no cable
* CtrDrvrCounterBlock Counters; The counter configurations and status
* CtrDrvrTgmBlock Telegrams; Active telegrams
* CtrDrvrRamTriggerTable Trigs; 2048 Trigger conditions
* CtrDrvrRamConfigurationTable Configs; 2048 Counter configurations
* CtrDrvrEventHistory EventHistory; 1024 Events deep
* unsigned long Setup; VME Level and Interrupt vector
* unsigned long LastReset; UTC Second of last reset
* unsigned long PartityErrs; Number of parity errors since last reset
* unsigned long SyncErrs; Number of frame synchronization errors since
* last reset
* unsigned long TotalErrs; Total number of IO errors since last reset
* unsigned long CodeViolErrs; Number of code violations since last reset
* unsigned long QueueErrs; Number of input Queue overflows since last
* reset
*
* New registers used only in recent VHDL, old makes a bus error !
*
* CtrDrvrIoStatus IoStat; IO status
* unsigned long IdLSL; ID Chip value Least Sig 32-bits
* unsigned long IdMSL; ID Chip value Most Sig 32-bits
* CtrDrvrModuleStats ModStats; Module statistics
*
*
*
* CTR JTAG - A16/D16 0x29 @
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <libvmebus.h>
static char git_version[] = "git_version: " GIT_VERSION;
#define MODULE_AM VME_A24_USER_DATA_SCT
#define MODULE_DW VME_D32
#define MODULE_VME_ADDR 0xc00000
#define MODULE_VME_SIZE 0x10000
int main(int argc, char **argv)
{
struct pdparam_master param;
struct vme_mapping *desc;
unsigned int *vme_buf;
int i;
unsigned int val;
unsigned int saved;
printf("%s: %s\n", argv[0], git_version);
printf("%s\n\n", libvmebus_version_s);
memset(&param, 0, sizeof(struct pdparam_master));
if ((vme_buf = (unsigned int *)find_controller(MODULE_VME_ADDR,
MODULE_VME_SIZE,
MODULE_AM, 0, 4,
&param)) == 0) {
printf("Failed to find_controller: %s\n", strerror(errno));
exit(1);
}
printf("find_controller mmap'ed at %p\n\n", vme_buf);
desc = (struct vme_mapping *)param.sgmin;
/* Now test read access */
for (i = 1; i <= 5; i++) {
val = swapbe32(vme_buf[i]);
printf("%x: 0x%08x\n", MODULE_VME_ADDR + i*4, val);
}
/* Check for a bus error */
if (vme_bus_error_check(desc))
printf("Bus error occured\n");
/* Test a write access */
printf("\n");
saved = swapbe32(vme_buf[1]);
printf("Read 0x%08x at 0x%08x\n", saved, MODULE_VME_ADDR + 4);
/* Check for a bus error */
if (vme_bus_error_check(desc))
printf("Bus error occured\n");
printf("\n");
printf("Writing 0x55aa55aa at address %x\n", MODULE_VME_ADDR + 4);
vme_buf[1] = swapbe32(0x55aa55aa);
fsync(desc->fd);
/* Check for a bus error */
if (vme_bus_error_check(desc))
printf("Bus error occured\n");
val = swapbe32(vme_buf[1]);
/* Check for a bus error */
if (vme_bus_error_check(desc))
printf("Bus error occured\n");
printf("Read back 0x%08x at address %x\n", val, MODULE_VME_ADDR + 4);
/*
vme_buf[1] = swapbe32(saved);
if (vme_bus_error_check(desc))
printf("Bus error occured\n");
*/
printf("\n");
if (return_controller(desc))
printf("Failed to return_controler: %s\n", strerror(errno));
return 0;
}
/*
* testvd80.c - simple VD80 test.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <libvmebus.h>
#include "libvd80.h"
static char git_version[] = "git_version: " GIT_VERSION;
#define CHAN_USED 1
unsigned int shot_samples;
unsigned int pre_samples;
unsigned int post_samples;
unsigned int illegal_samples;
unsigned int trigger_position;
unsigned int sample_start;
unsigned int sample_length;
unsigned int read_start;
unsigned int read_length;
unsigned int *read_buf[CHAN_USED];
/**
* test_sampling() - Run a sampling test
* @duration: Sampling duration in us
*/
int test_sampling(unsigned int duration)
{
int rc;
struct timespec ts;
ts.tv_sec = duration / 1000000;
ts.tv_nsec = duration * 1000 - (ts.tv_sec * 1000000000);
printf("delay=%d us - tv_sec=%ld tv_nsec=%ld\n", duration,
ts.tv_sec, ts.tv_nsec);
/* Start sampling */
printf("Starting sampling\n");
if ((rc = vd80_cmd_start()))
return 1;
/* Wait for a while */
clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
/* Trigger */
if ((rc = vd80_cmd_trig()))
return 1;
printf("Sampling stopped\n");
/* Get the number of samples */
shot_samples = vd80_get_shot_length();
post_samples = vd80_get_post_length();
if ((shot_samples < 0) || (post_samples < 0))
return 1;
pre_samples = shot_samples - post_samples;
sample_start = pre_samples;
read_start = (pre_samples / 32) * 32;
trigger_position = pre_samples - read_start;
sample_length = post_samples + trigger_position;
read_length = (sample_length % 32) ? ((sample_length / 32) + 1) * 32 :
(sample_length / 32) * 32;
/*
* Illegal samples are those samples at the end of the buffer which
* were not recorded but will be read because the unit for reading is
* 32 samples.
*/
illegal_samples = read_length - sample_length;
printf("Requested post-trigger samples: %d\n", vd80_get_postsamples());
printf("Recorded shot samples: %d\n", shot_samples);
printf("Recorded pre-trigger samples: %d\n", pre_samples);
printf("Recorded post-trigger samples: %d\n", post_samples);
printf("sample_start: %d\n", sample_start);
printf("sample_length: %d\n", sample_length);
printf("read_start: %d\n", read_start);
printf("read_length: %d\n", read_length);
printf("illegal_samples: %d\n", illegal_samples);
printf("trigger_position: %d\n", trigger_position);
return 0;
}
/**
* dump_samples() - Dump the samples read from the CHAN_USED channels
* @num_frames: number of frames
*
*/
void
dump_samples(unsigned int num_frames)
{
int i, j;
printf("samp ");
for (i = 0; i < CHAN_USED; i++)
printf("Ch%-2d ", i + 1);
printf("\n");
for (i = 0; i < num_frames; i++) {
printf("%4d", i*2);
for (j = 0; j < CHAN_USED; j++)
printf(" %.4x",
swapbe16((read_buf[j][i] >> 16) & 0xffff));
printf("\n");
printf("%4d", i*2 + 1);
for (j = 0; j < CHAN_USED; j++)
printf(" %.4x",
swapbe16(read_buf[j][i] & 0xffff));
printf("\n");
}
}
/**
* read_samples() - Read recorded samples
* @first_frame: First frame to read
* @num_frames: Number of frames to read
*
* @note One frame is 32 samples which makes it 2 samples for each channel.
*
*/
int read_samples(unsigned int first_frame, unsigned int num_frames)
{
int rc = 0;
int i, j;
if (num_frames <= 0)
return -1;
/* Set readstart and readlen */
if ((rc = vd80_set_readstart(first_frame)))
return rc;
if ((rc = vd80_set_readlen(num_frames - 1)))
return rc;
/* Allocate memory for the buffers */
for (i = 0; i < CHAN_USED; i++) {
read_buf[i] = NULL;
if (posix_memalign((void **)&read_buf[i], 4096,
num_frames * 4)) {
printf("Failed to allocate buffer for channel %d: %s\n",
i+1, strerror(errno));
rc = 1;
goto out_free;
}
}
/* Read the samples for each channel */
for (i = 0; i < CHAN_USED; i++) {
/* Select the channel to read from */
if ((rc = vd80_cmd_read(i + 1)))
goto out_free;
/* Read the samples */
for (j = 0; j < num_frames; j++)
read_buf[i][j] = vd80_get_sample();
}
printf("Done reading %d samples on %d channels\n",
num_frames * 2, CHAN_USED);
dump_samples(num_frames);
out_free:
for (i = 0; i < CHAN_USED; i++) {
if (read_buf[i])
free(read_buf[i]);
}
return rc;
}
/**
* read_samples_dma() - Read recorded samples
* @first_frame: First frame to read
* @num_frames: Number of frames to read
*
* @note One frame is 32 samples which makes it 2 samples for each channel.
*
*/
int read_samples_dma(unsigned int first_frame, unsigned int num_frames)
{
int rc = 0;
int i;
struct vme_dma dma_desc;
if (num_frames <= 0)
return -1;
/* Set readstart and readlen */
if ((rc = vd80_set_readstart(first_frame)))
return rc;
if ((rc = vd80_set_readlen(num_frames - 1)))
return rc;
/* Setup the common parts of the DMA descriptor */
memset(&dma_desc, 0, sizeof(struct vme_dma));
dma_desc.dir = VME_DMA_FROM_DEVICE;
dma_desc.length = num_frames * 4;
dma_desc.novmeinc = 1;
dma_desc.src.data_width = VD80_DMA_DW;
dma_desc.src.am = VD80_DMA_AM;
dma_desc.src.addru = 0;
dma_desc.src.addrl = VD80_DMA_BASE;
dma_desc.dst.addru = 0;
dma_desc.ctrl.pci_block_size = VME_DMA_BSIZE_4096;
dma_desc.ctrl.pci_backoff_time = VME_DMA_BACKOFF_0;
dma_desc.ctrl.vme_block_size = VME_DMA_BSIZE_4096;
dma_desc.ctrl.vme_backoff_time = VME_DMA_BACKOFF_0;
/* Allocate memory for the buffers */
for (i = 0; i < CHAN_USED; i++) {
read_buf[i] = NULL;
if (posix_memalign((void **)&read_buf[i], 4096,
num_frames * 4)) {
printf("Failed to allocate buffer for channel %d: %s\n",
i+1, strerror(errno));
rc = 1;
goto out_free;
}
}
/* Read the samples for each channel */
for (i = 0; i < CHAN_USED; i++) {
/* Setup the DMA descriptor */
/* strange casting to keep compiler quiet */
dma_desc.dst.addru = (unsigned int)
(((unsigned long long)(uintptr_t)read_buf[i])>>32);
dma_desc.dst.addrl = (unsigned int)(uintptr_t)read_buf[i];
/* Select the channel to read from */
if ((rc = vd80_cmd_read(i + 1)))
goto out_free;
/* DMA read the samples */
if ((rc = vme_dma_read(&dma_desc))) {
printf("Failed to read samples for channel %d: %s\n",
i + 1, strerror(errno));
rc = 1;
goto out_free;
}
}
printf("Done reading %d samples on %d channels\n",
num_frames * 2, CHAN_USED);
dump_samples(num_frames);
out_free:
for (i = 0; i < CHAN_USED; i++) {
if (read_buf[i])
free(read_buf[i]);
}
return rc;
}
int main(int argc, char **argv)
{
int rc = 0;
printf("%s: %s\n", argv[0], git_version);
printf("%s\n", libvmebus_version_s);
printf("%s\n\n", libvd80vmebridge_version_s);
if (vd80_map())
exit(1);
if (vd80_init())
exit(1);
vd80_cmd_setbig(0);
/* Run a 10 s recording */
if ((rc = test_sampling(1000000)))
goto out;
if (shot_samples == 0) {
printf("No samples recorded - exiting\n");
rc = 1;
goto out;
}
/* limit for BLT is 44 frames -> 88 samples -> 176 bytes */
// if ((rc = read_samples_dma(0, 44)))
/* limit for MBLT is 492 frames -> 984 samples -> 1968 samples */
/* number of frames must be a multiple of 2 (multiple of 8 bytes) */
if ((rc = read_samples_dma(0, 492)))
goto out;
out:
rc = vd80_exit();
return rc;
}
/*
* testvd80.c - simple VD80 test.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <libvmebus.h>
#include "vd80.h"
static char git_version[] = "git_version: " GIT_VERSION;
#define VD80_SETUP_BASE 0x580000
#define VD80_SETUP_SIZE 0x80000
#define VD80_SETUP_AM VME_CR_CSR
#define VD80_SETUP_DW VME_D32
#define VD80_OPER_BASE 0x580000
#define VD80_OPER_SIZE 0x100
#define VD80_OPER_AM VME_A24_USER_DATA_SCT
#define VD80_OPER_DW VME_D32
#define VD80_DMA_BASE VD80_OPER_BASE + VD80_MEMOUT
#define VD80_DMA_AM VME_A24_USER_MBLT
#define VD80_DMA_DW VME_D32
unsigned int *regs;
struct vme_mapping regs_desc;
#define CHAN_USED 1
unsigned int shot_samples;
unsigned int pre_samples;
unsigned int post_samples;
unsigned int illegal_samples;
unsigned int trigger_position;
unsigned int sample_start;
unsigned int sample_length;
unsigned int read_start;
unsigned int read_length;
unsigned int *read_buf[CHAN_USED];
int vd80_cmd_start(void)
{
regs[VD80_GCR1/4] |= swapbe32(VD80_COMMAND_START);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error starting sampling\n");
return 1;
}
return 0;
}
/**
* \brief Stop sampling
*
* \return 0 on success else 1 on a VME bus error.
*/
int vd80_cmd_stop(void)
{
regs[VD80_GCR1/4] |= swapbe32(VD80_COMMAND_STOP);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error stopping sampling\n");
return 1;
}
return 0;
}
/**
* \brief Generate an internal trigger
*
* \return 0 on success else 1 on a VME bus error.
*/
int vd80_cmd_trig(void)
{
regs[VD80_GCR1/4] |= swapbe32(VD80_COMMAND_TRIG);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error triggering sampling\n");
return 1;
}
return 0;
}
/**
* \brief Start subsampling
*
* \return 0 on success else 1 on a VME bus error.
*/
int vd80_cmd_substart(void)
{
regs[VD80_GCR1/4] |= swapbe32(VD80_COMMAND_SUBSTART);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error starting sub sampling\n");
return 1;
}
return 0;
}
/**
* \brief Stop subsampling
*
* \return 0 on success else 1 on a VME bus error.
*/
int vd80_cmd_substop(void)
{
regs[VD80_GCR1/4] |= swapbe32(VD80_COMMAND_SUBSTOP);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error stopping sub sampling\n");
return 1;
}
return 0;
}
/**
* \brief Enable readout of the sample buffer
* \param channel Channel to read samples from (1 to 16)
*
* \return 0 on success else 1 on a VME bus error.
*/
int vd80_cmd_read(unsigned int channel)
{
regs[VD80_GCR1/4] |= swapbe32((VD80_COMMAND_READ |
((channel << VD80_OPERANT_SHIFT) &
VD80_OPERANT_MASK)));
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error setting READ for channel %d\n", channel);
return 1;
}
return 0;
}
/**
* \brief Record only a single sample
*
* \return 0 on success else 1 on a VME bus error.
*/
int vd80_cmd_single(void)
{
regs[VD80_GCR1/4] |= swapbe32(VD80_COMMAND_SINGLE);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error setting SINGLE\n");
return 1;
}
return 0;
}
/**
* \brief Clear the pre-trigger counter
*
* \return 0 on success else 1 on a VME bus error.
*/
int vd80_cmd_clear(void)
{
regs[VD80_GCR1/4] |= swapbe32(VD80_COMMAND_CLEAR);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error setting CLEAR\n");
return 1;
}
return 0;
}
/**
* \brief Set little or big endian samples recording
* \param bigend When set, samples are stored in big endian ordering, else in
* little endian
*
* \return 0 on success else 1 on a VME bus error.
*/
int vd80_cmd_setbig(int bigend)
{
if (bigend)
regs[VD80_GCR2/4] |= swapbe32(VD80_BIGEND);
else
regs[VD80_GCR2/4] &= swapbe32(~VD80_BIGEND);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error setting samples endianess\n");
return 1;
}
return 0;
}
/**
* \brief Enable/Disable generation of the test waveform
* \param debug When set, enable generation of the test waveform, else disable
*
* \return 0 on success else 1 on a VME bus error.
*
* The test waveform consists in a digital ramp.
*/
int vd80_cmd_setdebug(int debug)
{
if (debug)
regs[VD80_GCR2/4] |= swapbe32(VD80_DBCNTSEL);
else
regs[VD80_GCR2/4] &= swapbe32(~VD80_DBCNTSEL);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error setting debug counter select\n");
return 1;
}
return 0;
}
/**
* \brief Set the number of post-trigger samples
* \param num Number of post-trigger samples to store
*
* \return 0 on success or -1 on a VME bus error.
*
* Set the number of samples recorded between the trigger and sampling end.
*/
int vd80_set_postsamples(unsigned int num)
{
regs[VD80_TCR2/4] = swapbe32(num & 0x7ffff);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error setting POSTSAMPLES\n");
return -1;
}
return 0;
}
/**
* \brief Get the number of post-trigger samples
*
* \return the number of requested samples on success or -1 on a VME bus error.
*
* Get the number of post-trigger samples requested.
*/
int vd80_get_postsamples(void)
{
int val = swapbe32(regs[VD80_TCR2/4]);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error reading POSTSAMPLES\n");
return -1;
}
return val;
}
/**
* \brief Get the number of pre-trigger samples
*
* \return the number of pre-trigger samples or -1 on a VME bus error.
*
* Get the number of samples recorded between the sampling start and the
* trigger.
*/
int vd80_get_pre_length(void)
{
int reg = swapbe32(regs[VD80_PTSR/4]);
int val = (reg & VD80_PRESAMPLES_MASK) >>
VD80_PRESAMPLES_SHIFT;
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error reading PRESAMPLES\n");
return -1;
}
return val;
}
/**
* \brief Get the number of post-trigger samples
*
* \return the number of post-trigger samples or -1 on a VME bus error.
*
* Get the number of samples recorded between the trigger and the sampling
* end.
*/
int vd80_get_post_length(void)
{
int reg = swapbe32(regs[VD80_TSR/4]);
int val = (reg & VD80_ACTPOSTSAMPLES_MASK) >>
VD80_ACTPOSTSAMPLES_SHIFT;
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error reading ACTPOSTSAMPLES\n");
return -1;
}
return val;
}
/**
* \brief Get the total shot length
*
* \return the total number of samples or -1 on a VME bus error.
*
* Get the total number of samples taken during a shot
* (pre-samples + post-samples).
*/
int vd80_get_shot_length(void)
{
int reg = swapbe32(regs[VD80_SSR/4]);
int val = (reg & VD80_SHOTLEN_MASK) >>
VD80_SHOTLEN_SHIFT;
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error reading SHOTLEN\n");
return -1;
}
return val;
}
/**
* \brief Set the readout starting sample
* \param readstart READSTART value to set
*
* \return 0 on success else 1 on a VME bus error.
*
* \note READSTART counts in steps of 32 samples. Therefore the number of
* samples is READSTART * 32.
*/
int vd80_set_readstart(unsigned int readstart)
{
regs[VD80_MCR1/4] = swapbe32((readstart << VD80_READSTART_SHIFT) &
VD80_READSTART_MASK);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error setting READSTART\n");
return 1;
}
return 0;
}
/**
* \brief Set the number of samples that must be read
* \param readlen READLEN value to set
*
* \return 0 on success else 1 on a VME bus error.
*
* \note READLEN counts in steps of 32 samples. Therefore the number of samples
* is (READLEN + 1) * 32 and the minimum number of samples is 32
*/
int vd80_set_readlen(unsigned int readlen)
{
regs[VD80_MCR2/4] = swapbe32((readlen << VD80_READLEN_SHIFT) &
VD80_READLEN_MASK);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error setting READLEN\n");
return 1;
}
return 0;
}
/**
* \brief Get one sample from the sample memory
*
*
*/
int vd80_get_sample(void)
{
return regs[VD80_MEMOUT/4];
}
/**
* \brief Probe and configure the VD80
*
* \return 0 on success else 1 on failure.
*
* This function probes and configures the VD80 using the CR/CSR address space.
*
* It performs the following operations:
*
* \li map the VME CR/CSR address space of the board
* \li check we're talking to a VD80 board by checking the module ID
* \li configure the function 0 Address Decode Compare register (\a ADER0) to
* map the operational registers on an A24/D32 address space at base
* address 0x600000. Used for operational registers access.
* \li configure the function 1 Address Decode Compare register (\a ADER1) to
* map the operational registers on an A24-BLT/D32 address space at base
* address 0x600000. Used for DMA reading of the samples memory using
* VME block transfer.
*
* \note The VME CR/CSR address space is unmapped on function return.
*/
int vd80_map(void)
{
int rc = 1;
struct vme_mapping crcsr_desc;
unsigned int *crcsr;
int i;
unsigned int val;
char cr_sig[2];
char board_id[5];
char rev_id[5];
unsigned int desc_offset;
char board_desc[512];
/*
* Open a mapping into the VD80 CR/CSR (0x2f) address space for
* configuring the board.
*/
memset(&crcsr_desc, 0, sizeof(struct vme_mapping));
crcsr_desc.window_num = 0;
crcsr_desc.am = VD80_SETUP_AM;
crcsr_desc.read_prefetch_enabled = 0;
crcsr_desc.data_width = VD80_SETUP_DW;
crcsr_desc.sizeu = 0;
crcsr_desc.sizel = VD80_SETUP_SIZE;
crcsr_desc.vme_addru = 0;
crcsr_desc.vme_addrl = VD80_SETUP_BASE;
if ((crcsr = vme_map(&crcsr_desc, 1)) == NULL) {
printf("Failed to map CR/CSR: %s\n", strerror(errno));
return 1;
}
/* First check we're accessing a configuration ROM */
cr_sig[0] = swapbe32(crcsr[VD80_CR_SIG1/4]) & 0xff;
if (vme_bus_error_check(&crcsr_desc)) {
printf("Bus error reading CR signature byte 0 at %08x\n",
VD80_SETUP_BASE + VD80_CR_SIG1);
goto out;
}
cr_sig[1] = swapbe32(crcsr[VD80_CR_SIG2/4]) & 0xff;
if (vme_bus_error_check(&crcsr_desc)) {
printf("Bus error reading CR signature byte 1 at %08x\n",
VD80_SETUP_BASE + VD80_CR_SIG2);
goto out;
}
if ((cr_sig[0] != 'C') && (cr_sig[1] != 'R')) {
printf("Not a Configuration ROM at 0x%x (%08x %08x)\n",
VD80_SETUP_BASE, swapbe32(crcsr[VD80_CR_SIG1/4]),
swapbe32(crcsr[VD80_CR_SIG2/4]));
goto out;
}
/* Try to read module ID - offset 0x30 */
for (i = 0; i < VD80_CR_BOARD_ID_LEN; i++) {
val = swapbe32(crcsr[VD80_CR_BOARD_ID/4 + i]);
board_id[i] = (char)(val & 0xff);
}
board_id[4] = '\0';
if (vme_bus_error_check(&crcsr_desc)) {
printf("Bus error reading board ID\n");
goto out;
}
for (i = 0; i < VD80_CR_REV_ID_LEN; i++) {
val = swapbe32(crcsr[VD80_CR_REV_ID/4 + i]);
rev_id[i] = (char)(val & 0xff);
}
rev_id[4] = '\0';
if (vme_bus_error_check(&crcsr_desc)) {
printf("Bus error reading revision ID\n");
goto out;
}
if (strncmp(board_id, "VD80", 4)) {
printf("No VD80 board found at base address 0x%x\n",
VD80_SETUP_BASE);
goto out;
}
printf("Found board ID %s - revision ID: %s\n", board_id, rev_id);
/* Read board string */
desc_offset = 0;
for (i = 0; i < VD80_CR_DESC_PTR_LEN; i++) {
desc_offset <<= 8;
desc_offset |= swapbe32(crcsr[VD80_CR_DESC_PTR/4 + i]) & 0xff;
}
desc_offset &= ~3;
if (desc_offset != 0) {
for (i = 0; i < VD80_UCR_BOARD_DESC_LEN; i++) {
val = swapbe32(crcsr[0x1040/4 + i]);
board_desc[i] = (char)(val & 0xff);
if (board_desc[i] == '\0')
break;
}
board_desc[320] = '\0';
}
if (vme_bus_error_check(&crcsr_desc)) {
printf("Bus error reading board description\n");
goto out;
}
printf("Board name: %s\n", board_desc);
printf("\n");
/* Configure ADER0 for A24 USER DATA access (AM=0x39 BA=0x600000) */
crcsr[VD80_CSR_ADER0/4] = swapbe32((VD80_OPER_BASE >> 24) & 0xff);
crcsr[VD80_CSR_ADER0/4 + 1] = swapbe32((VD80_OPER_BASE >> 16) & 0xff);
crcsr[VD80_CSR_ADER0/4 + 2] = swapbe32((VD80_OPER_BASE >> 8) & 0xff);
crcsr[VD80_CSR_ADER0/4 + 3] = swapbe32(VD80_OPER_AM * 4);
if (vme_bus_error_check(&crcsr_desc)) {
printf("Bus error setting ADER0\n");
goto out;
}
/* Configure ADER1 for A24 USER block transfer (AM=0x3b BA=0x600000) */
crcsr[VD80_CSR_ADER1/4] = swapbe32((VD80_OPER_BASE >> 24) & 0xff);
crcsr[VD80_CSR_ADER1/4 + 1] = swapbe32((VD80_OPER_BASE >> 16) & 0xff);
crcsr[VD80_CSR_ADER1/4 + 2] = swapbe32((VD80_OPER_BASE >> 8) & 0xff);
crcsr[VD80_CSR_ADER1/4 + 3] = swapbe32(VD80_DMA_AM * 4);
if (vme_bus_error_check(&crcsr_desc)) {
printf("Bus error setting ADER1\n");
goto out;
}
/*
* Clear address counter enable for function 1 to use it for block
* transfer of the sample memory.
*/
val = swapbe32(crcsr[VD80_CSR_FUNC_ACEN/4]) | 0x02;
crcsr[VD80_CSR_FUNC_ACEN/4] = swapbe32(0);
if (vme_bus_error_check(&crcsr_desc)) {
printf("Bus error setting ACEN\n");
goto out;
}
/* Enable the module */
crcsr[VD80_CSR_BITSET/4] = swapbe32(VD80_CSR_ENABLE_MODULE);
if (vme_bus_error_check(&crcsr_desc)) {
printf("Bus error enabling module\n");
goto out;
}
rc = 0;
out:
if (vme_unmap(&crcsr_desc, 1))
printf("Failed to unmap CR/CSR space: %s\n",
strerror(errno));
return rc;
}
/**
* \brief Map and initialize the VD80 for operation
*
* \return 0 on success else 1 on failure.
*
* This functions performs the necessary initializations to prepare the
* board for sampling:
*
* \li map the operational registers into the user address space
* \li clear any interrupts
* \li disable interrupts
* \li configure the sampling clock source to use the internal clock divided
* by 2 which yields a 100kHz sampling clock
* \li configure the trigger source to use the internal trigger
* \li set the number of post samples to 0
* \li setup the pretrigger control to clear the pretrigger counter on a
* \a START command, to clear the \a PRESAMPLES counter on reading the
* pretrigger status register and to unse the sample clock for the
* pretrigger counter
* \li enable generation of the test waveform
*
* \note Those settings are \b testing settings and must be adapted for
* operational use.
*
*/
int vd80_init(void)
{
int rc = 1;
/* Open a mapping for the VD80 operational registers */
memset(&regs_desc, 0, sizeof(struct vme_mapping));
regs_desc.window_num = 0;
regs_desc.am = VD80_OPER_AM;
regs_desc.read_prefetch_enabled = 0;
regs_desc.data_width = VD80_OPER_DW;
regs_desc.sizeu = 0;
regs_desc.sizel = VD80_OPER_SIZE;
regs_desc.vme_addru = 0;
regs_desc.vme_addrl = VD80_OPER_BASE;
if ((regs = vme_map(&regs_desc, 1)) == NULL) {
printf("Failed to map VD80 operational registers: %s\n",
strerror(errno));
printf(" addr: %x size: %x am: %x dw: %d\n", VD80_OPER_BASE,
VD80_OPER_SIZE, VD80_OPER_AM, VD80_OPER_DW);
return 1;
}
/* Clear interrupts */
regs[VD80_GCR1/4] |= swapbe32(VD80_INTCLR | VD80_IOERRCLR);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error initializing GCR1\n");
goto out;
}
/*
* Disable interrupts, set little endian mode and disable debug
* counter (Clear all bits).
*/
regs[VD80_GCR2/4] = 0;
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error initializing GCR2\n");
goto out;
}
/*
* Set sampling clock:
* - Internal clock
* - No VXI clock output
* - Divide mode
* - Rising edge
* - No 50 ohm termination
* - Clkdiv = 2 (I want a 100kHz clock from the 200 kHz internal
* clock - which yields a 10 us sampling period)
*/
regs[VD80_CCR/4] = swapbe32(1 << VD80_CLKDIV_SHIFT);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error initializing CCR\n");
goto out;
}
/*
* Set trigger:
* - Internal trigger
* - No VXI trigger output
* - No trigger output
* - Rising edge
* - No 50 ohm termination
* - No trigger synchronization
*/
regs[VD80_TCR1/4] = 0;
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error initializing TCR1\n");
goto out;
}
/* Set number of post samples to 0 */
if (vd80_set_postsamples(0))
goto out;
/*
* Setup the Pretrigger Control Register:
* - Clear pretrigger counter on START
* - Clear PRESAMPLES on Pretrigger Status Register read
* - Clock the pretrigger counter on the sample clock
*/
regs[VD80_PTCR/4] |= swapbe32(VD80_PSCNTCLRONSTART |
VD80_PSREGCLRONREAD |
VD80_PSCNTCLKONSMPL);
if (vme_bus_error_check(&regs_desc)) {
printf("Bus error initializing PTCR\n");
goto out;
}
/* Enable generation of debug waveform */
if ((rc = vd80_cmd_setdebug(1)))
goto out;
return 0;
out:
if (vme_unmap(&regs_desc, 1))
printf("Failed to unmap operational registers: %s\n",
strerror(errno));
return rc;
}
/**
* \brief Unmap the VD80 operational address space
*
* \return 0 on success else 1 on failure
*/
int vd80_exit(void)
{
int rc = 0;
if ((rc = vme_unmap(&regs_desc, 1)))
printf("Failed to return_controler: %s\n", strerror(errno));
printf("Unmapped VD80 registers\n");
return rc;
}
/**
* test_sampling() - Run a sampling test
* @duration: Sampling duration in us
*/
int test_sampling(unsigned int duration)
{
int rc;
struct timespec ts;
ts.tv_sec = duration / 1000000;
ts.tv_nsec = duration * 1000 - (ts.tv_sec * 1000000000);
printf("delay=%d us - tv_sec=%ld tv_nsec=%ld\n", duration,
ts.tv_sec, ts.tv_nsec);
/* Start sampling */
printf("Starting sampling\n");
if ((rc = vd80_cmd_start()))
return 1;
/* Wait for a while */
clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
/* Trigger */
if ((rc = vd80_cmd_trig()))
return 1;
printf("Sampling stopped\n");
/* Get the number of samples */
shot_samples = vd80_get_shot_length();
post_samples = vd80_get_post_length();
if ((shot_samples < 0) || (post_samples < 0))
return 1;
pre_samples = shot_samples - post_samples;
sample_start = pre_samples;
read_start = (pre_samples / 32) * 32;
trigger_position = pre_samples - read_start;
sample_length = post_samples + trigger_position;
read_length = (sample_length % 32) ? ((sample_length / 32) + 1) * 32 :
(sample_length / 32) * 32;
/*
* Illegal samples are those samples at the end of the buffer which
* were not recorded but will be read because the unit for reading is
* 32 samples.
*/
illegal_samples = read_length - sample_length;
printf("Requested post-trigger samples: %d\n", vd80_get_postsamples());
printf("Recorded shot samples: %d\n", shot_samples);
printf("Recorded pre-trigger samples: %d\n", pre_samples);
printf("Recorded post-trigger samples: %d\n", post_samples);
printf("sample_start: %d\n", sample_start);
printf("sample_length: %d\n", sample_length);
printf("read_start: %d\n", read_start);
printf("read_length: %d\n", read_length);
printf("illegal_samples: %d\n", illegal_samples);
printf("trigger_position: %d\n", trigger_position);
return 0;
}
/**
* dump_samples() - Dump the samples read from the CHAN_USED channels
* @num_frames: number of frames
*
*/
void
dump_samples(unsigned int num_frames)
{
int i, j;
printf("samp ");
for (i = 0; i < CHAN_USED; i++)
printf("Ch%-2d ", i + 1);
printf("\n");
for (i = 0; i < num_frames; i++) {
printf("%4d", i*2);
for (j = 0; j < CHAN_USED; j++)
printf(" %.4x",
swapbe16((read_buf[j][i] >> 16) & 0xffff));
printf("\n");
printf("%4d", i*2 + 1);
for (j = 0; j < CHAN_USED; j++)
printf(" %.4x",
swapbe16(read_buf[j][i] & 0xffff));
printf("\n");
}
}
/**
* read_samples() - Read recorded samples
* @first_frame: First frame to read
* @num_frames: Number of frames to read
*
* @note One frame is 32 samples which makes it 2 samples for each channel.
*
*/
int read_samples(unsigned int first_frame, unsigned int num_frames)
{
int rc = 0;
int i, j;
if (num_frames <= 0)
return -1;
/* Set readstart and readlen */
if ((rc = vd80_set_readstart(first_frame)))
return rc;
if ((rc = vd80_set_readlen(num_frames - 1)))
return rc;
/* Allocate memory for the buffers */
for (i = 0; i < CHAN_USED; i++) {
read_buf[i] = NULL;
if (posix_memalign((void **)&read_buf[i], 4096,
num_frames * 4)) {
printf("Failed to allocate buffer for channel %d: %s\n",
i+1, strerror(errno));
rc = 1;
goto out_free;
}
}
/* Read the samples for each channel */
for (i = 0; i < CHAN_USED; i++) {
/* Select the channel to read from */
if ((rc = vd80_cmd_read(i + 1)))
goto out_free;
/* Read the samples */
for (j = 0; j < num_frames; j++)
read_buf[i][j] = vd80_get_sample();
}
printf("Done reading %d samples on %d channels\n",
num_frames * 2, CHAN_USED);
dump_samples(num_frames);
out_free:
for (i = 0; i < CHAN_USED; i++) {
if (read_buf[i])
free(read_buf[i]);
}
return rc;
}
/**
* read_samples_dma() - Read recorded samples
* @first_frame: First frame to read
* @num_frames: Number of frames to read
*
* @note One frame is 32 samples which makes it 2 samples for each channel.
*
*/
int read_samples_dma(unsigned int first_frame, unsigned int num_frames)
{
int rc = 0;
int i;
struct vme_dma dma_desc;
if (num_frames <= 0)
return -1;
/* Set readstart and readlen */
if ((rc = vd80_set_readstart(first_frame)))
return rc;
if ((rc = vd80_set_readlen(num_frames - 1)))
return rc;
/* Setup the common parts of the DMA descriptor */
memset(&dma_desc, 0, sizeof(struct vme_dma));
dma_desc.dir = VME_DMA_FROM_DEVICE;
dma_desc.length = num_frames * 4;
dma_desc.novmeinc = 1;
dma_desc.src.data_width = VD80_DMA_DW;
dma_desc.src.am = VD80_DMA_AM;
dma_desc.src.addru = 0;
dma_desc.src.addrl = VD80_DMA_BASE;
dma_desc.dst.addru = 0;
dma_desc.ctrl.pci_block_size = VME_DMA_BSIZE_4096;
dma_desc.ctrl.pci_backoff_time = VME_DMA_BACKOFF_0;
dma_desc.ctrl.vme_block_size = VME_DMA_BSIZE_4096;
dma_desc.ctrl.vme_backoff_time = VME_DMA_BACKOFF_0;
/* Allocate memory for the buffers */
for (i = 0; i < CHAN_USED; i++) {
read_buf[i] = NULL;
if (posix_memalign((void **)&read_buf[i], 4096,
num_frames * 4)) {
printf("Failed to allocate buffer for channel %d: %s\n",
i+1, strerror(errno));
rc = 1;
goto out_free;
}
}
/* Read the samples for each channel */
for (i = 0; i < CHAN_USED; i++) {
/* Setup the DMA descriptor */
/* strange casting to keep compiler quiet */
dma_desc.dst.addru = (unsigned int)
(((unsigned long long)(uintptr_t)read_buf[i])>>32);
dma_desc.dst.addrl = (unsigned int)(uintptr_t)read_buf[i];
/* Select the channel to read from */
if ((rc = vd80_cmd_read(i + 1)))
goto out_free;
/* DMA read the samples */
if ((rc = vme_dma_read(&dma_desc))) {
printf("Failed to read samples for channel %d: %s\n",
i + 1, strerror(errno));
rc = 1;
goto out_free;
}
}
printf("Done reading %d samples on %d channels\n",
num_frames * 2, CHAN_USED);
dump_samples(num_frames);
out_free:
for (i = 0; i < CHAN_USED; i++) {
if (read_buf[i])
free(read_buf[i]);
}
return rc;
}
int main(int argc, char **argv)
{
int rc = 0;
printf("%s: %s\n", argv[0], git_version);
printf("%s\n\n", libvmebus_version_s);
if (vd80_map())
exit(1);
if (vd80_init())
exit(1);
vd80_cmd_setbig(0);
/* Run a 10 s recording */
if ((rc = test_sampling(1000000)))
goto out;
if (shot_samples == 0) {
printf("No samples recorded - exiting\n");
rc = 1;
goto out;
}
/* limit for BLT is 44 frames -> 88 samples -> 176 bytes */
// if ((rc = read_samples_dma(0, 44)))
/* limit for MBLT is 492 frames -> 984 samples -> 1968 samples */
/* number of frames must be a multiple of 2 (multiple of 8 bytes) */
if ((rc = read_samples_dma(0, 492)))
goto out;
out:
rc = vd80_exit();
return rc;
}
/*
* vd80.h - Register definitions for the VD80 Transient Recorder
*
* Copyright (c) 2009 Sebastien Dugue
*
* 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.
*
*/
#ifndef _VD80_H
#define _VD80_H
#define VD80_CHANNELS 16
/*
* Configuration ROM Registers
*
* Register offsets are given relative to the CR/CSR base address.
*/
#define VD80_CR_CHKSUM 0x00
#define VD80_CR_ROM_LEN_LEN 3
#define VD80_CR_ROM_LEN 0x04 /* 0x04 - 0x0c */
#define VD80_CR_CR_DW 0x10
#define VD80_CR_CSR_DW 0x14
#define VD80_CR_SPACE_ID 0x18
#define VD80_CR_SIG1 0x1c
#define VD80_CR_SIG2 0x20
#define VD80_CR_MANUF_ID_LEN 3
#define VD80_CR_MANUF_ID 0x24 /* 0x24 - 0x2c */
#define VD80_CR_BOARD_ID_LEN 4
#define VD80_CR_BOARD_ID 0x30 /* 0x30 - 0x3c */
#define VD80_CR_REV_ID_LEN 4
#define VD80_CR_REV_ID 0x40 /* 0x40 - 0x4c */
#define VD80_CR_DESC_PTR_LEN 3
#define VD80_CR_DESC_PTR 0x50 /* 0x50 - 0x58 */
#define VD80_CR_PGM_ID 0x7c
#define VD80_CR_OFFSET_LEN 3
#define VD80_CR_BEG_USER_CR 0x80
#define VD80_CR_END_USER_CR 0x8c
#define VD80_CR_BEG_CRAM 0x98
#define VD80_CR_END_CRAM 0xa4
#define VD80_CR_BEG_USER_CSR 0xb0
#define VD80_CR_END_USER_CSR 0xbc
#define VD80_CR_BEG_SN 0xc8
#define VD80_CR_END_SN 0xd4
#define VD80_CR_SLAVE_PARAM 0xe0
#define VD80_CR_INTERRUPTER_CAP 0xf4
#define VD80_CR_CRAM_DW 0xfc
#define VD80_CR_FUNC0_DW 0x100
#define VD80_CR_FUNC1_DW 0x104
#define VD80_CR_FUNC2_DW 0x108
#define VD80_CR_FUNC3_DW 0x10c
#define VD80_CR_FUNC_AM_MASK_LEN 8
#define VD80_CR_FUNC0_AM_MASK 0x120
#define VD80_CR_FUNC1_AM_MASK 0x140
#define VD80_CR_FUNC2_AM_MASK 0x160
#define VD80_CR_FUNC3_AM_MASK 0x180
#define VD80_CR_FUNC_ADEM_LEN 4
#define VD80_CR_FUNC0_ADEM 0x620
#define VD80_CR_FUNC1_ADEM 0x630
#define VD80_CR_FUNC2_ADEM 0x640
#define VD80_CR_FUNC3_ADEM 0x650
/*
* User Configuration ROM Registers
*
* Register offsets are given relative to the CR/CSR base address.
*/
#define VD80_UCR_BOARD_SERIAL_LEN 4
#define VD80_UCR_BOARD_SERIAL 0x1000
#define VD80_UCR_BOARD_DESC_LEN 0x140
#define VD80_UCR_BOARD_DESC 0x1040
/*
* Control and Status Registers
*
* Register offsets are given relative to the CR/CSR base address.
*/
#define VD80_CSR_IRQ_LEVEL 0x7fbf4
#define VD80_CSR_IRQ_VECTOR 0x7fbf8
#define VD80_CSR_FUNC_ACEN 0x7fbfc
#define VD80_CSR_ADER_LEN 4
#define VD80_CSR_ADER0 0x7ff60
#define VD80_CSR_ADER1 0x7ff70
#define VD80_CSR_ADER2 0x7ff80
#define VD80_CSR_ADER3 0x7ff90
#define VD80_CSR_CRAM_OWNER 0x7fff0
#define VD80_CSR_BITCLR 0x7fff4
#define VD80_CSR_BITSET 0x7fff8
#define VD80_CSR_ENABLE_SYSFAIL (1 << 6)
#define VD80_CSR_ENABLE_MODULE (1 << 4)
#define VD80_CSR_BERR_FLAG (1 << 3)
#define VD80_CSR_CRAM_OWNER_BIT (1 << 2)
#define VD80_CSR_CRCSR_BASE 0x7fffc
/*
* Operational Registers.
*
* Register offsets are given relative to the operational base address
* setup at module initialization.
*/
/* General Control Register 1 offset 0x10 */
#define VD80_GCR1 0x10
#define VD80_INTCLR (1 << 9)
#define VD80_IOERRCLR (1 << 8)
#define VD80_OPERANT_SHIFT 4
#define VD80_OPERANT_MASK (0xf << VD80_OPERANT_SHIFT)
#define VD80_COMMAND_SHIFT 0
#define VD80_COMMAND_MASK (0xf << VD80_COMMAND_SHIFT)
#define VD80_COMMAND_NONE (0 << VD80_COMMAND_SHIFT)
#define VD80_COMMAND_SINGLE (1 << VD80_COMMAND_SHIFT)
#define VD80_COMMAND_TRIG (2 << VD80_COMMAND_SHIFT)
#define VD80_COMMAND_READ (4 << VD80_COMMAND_SHIFT)
#define VD80_COMMAND_SUBSTOP (8 << VD80_COMMAND_SHIFT)
#define VD80_COMMAND_SUBSTART (9 << VD80_COMMAND_SHIFT)
#define VD80_COMMAND_START (0xb << VD80_COMMAND_SHIFT)
#define VD80_COMMAND_CLEAR (0xc << VD80_COMMAND_SHIFT)
#define VD80_COMMAND_STOP (0xe << VD80_COMMAND_SHIFT)
/* General Control Register 1 offset 0x14 */
#define VD80_GCR2 0x14
#define VD80_DBCNTSEL (1 << 6)
#define VD80_BIGEND (1 << 5)
#define VD80_CALINTEN (1 << 4)
#define VD80_IOERRINTEN (1 << 3)
#define VD80_SMPLINTEN (1 << 2)
#define VD80_SHOTINTEN (1 << 1)
#define VD80_TRIGINTEN (1 << 0)
/* General Status Register offset 0x18 */
#define VD80_GSR 0x18
#define VD80_MEMOUTRDY (1 << 10)
#define VD80_IOERRTRIG (1 << 9)
#define VD80_IOERRCLK (1 << 8)
#define VD80_CALINTEVENT (1 << 7)
#define VD80_IOERRINTEVENT (1 << 6)
#define VD80_SMPLINTEVENT (1 << 5)
#define VD80_SHOTINTEVENT (1 << 4)
#define VD80_TRIGINTEVENT (1 << 3)
#define VD80_STATE_SHIFT 0
#define VD80_STATE_MASK (7 << VD80_STATE_SHIFT)
#define VD80_STATE_IDLE (1 << VD80_STATE_SHIFT)
#define VD80_STATE_PRETRIG (2 << VD80_STATE_SHIFT)
#define VD80_STATE_POSTTRIG (4 << VD80_STATE_SHIFT)
/* Clock Control Register offset 0x1c */
#define VD80_CCR 0x1c
#define VD80_CLKDIV_SHIFT 16
#define VD80_CLKDIV_MASK (0xffff << VD80_CLKDIV_SHIFT)
#define VD80_CLKTERM_SHIFT 9
#define VD80_CLKTERM_MASK (1 << VD80_CLKTERM_SHIFT)
#define VD80_CLKTERM_50OHM (1 << VD80_CLKTERM_SHIFT)
#define VD80_CLKTERM_NONE (0 << VD80_CLKTERM_SHIFT)
#define VD80_CLKEDGE_SHIFT 8
#define VD80_CLKEDGE_MASK (1 << VD80_CLKEDGE_SHIFT)
#define VD80_CLKEDGE_RISING (0 << VD80_CLKEDGE_SHIFT)
#define VD80_CLKEDGE_FALLING (1 << VD80_CLKEDGE_SHIFT)
#define VD80_CLKDIVMODE_SHIFT 7
#define VD80_CLKDIVMODE_MASK (1 << VD80_CLKDIVMODE_SHIFT)
#define VD80_CLKDIVMODE_DIVIDE (0 << VD80_CLKDIVMODE_SHIFT)
#define VD80_CLKDIVMODE_SUBSAMPLE (1 << VD80_CLKDIVMODE_SHIFT)
#define VD80_CLKVXIOUT_SHIFT 4
#define VD80_CLKVXIOUT_MASK (7 << VD80_CLKVXIOUT_SHIFT)
#define VD80_CLKVXIOUT_VXI_NONE (0 << VD80_CLKVXIOUT_SHIFT)
#define VD80_CLKVXIOUT_VXI_TRIG0 (1 << VD80_CLKVXIOUT_SHIFT)
#define VD80_CLKVXIOUT_VXI_TRIG1 (2 << VD80_CLKVXIOUT_SHIFT)
#define VD80_CLKVXIOUT_VXI_TRIG2 (3 << VD80_CLKVXIOUT_SHIFT)
#define VD80_CLKVXIOUT_VXI_TRIG3 (4 << VD80_CLKVXIOUT_SHIFT)
#define VD80_CLKVXIOUT_VXI_TRIG4 (5 << VD80_CLKVXIOUT_SHIFT)
#define VD80_CLKVXIOUT_VXI_TRIG5 (6 << VD80_CLKVXIOUT_SHIFT)
#define VD80_CLKVXIOUT_VXI_TRIG6 (7 << VD80_CLKVXIOUT_SHIFT)
#define VD80_CLKSOURCE_SHIFT 0
#define VD80_CLKSOURCE_MASK (0xf << VD80_CLKSOURCE_SHIFT)
#define VD80_CLKSOURCE_INT (0 << VD80_CLKSOURCE_SHIFT)
#define VD80_CLKSOURCE_VXI_TRIG0 (1 << VD80_CLKSOURCE_SHIFT)
#define VD80_CLKSOURCE_VXI_TRIG1 (2 << VD80_CLKSOURCE_SHIFT)
#define VD80_CLKSOURCE_VXI_TRIG2 (3 << VD80_CLKSOURCE_SHIFT)
#define VD80_CLKSOURCE_VXI_TRIG3 (4 << VD80_CLKSOURCE_SHIFT)
#define VD80_CLKSOURCE_VXI_TRIG4 (5 << VD80_CLKSOURCE_SHIFT)
#define VD80_CLKSOURCE_VXI_TRIG5 (6 << VD80_CLKSOURCE_SHIFT)
#define VD80_CLKSOURCE_VXI_TRIG6 (7 << VD80_CLKSOURCE_SHIFT)
#define VD80_CLKSOURCE_EXT (8 << VD80_CLKSOURCE_SHIFT)
/* Trigger Control Register 1 offset 0x20 */
#define VD80_TCR1 0x20
#define VD80_TRIGSYNC (1 << 10)
#define VD80_TRIG_TERM_SHIFT 9
#define VD80_TRIG_TERM_MASK (1 << VD80_TRIG_TERM_SHIFT)
#define VD80_TRIG_TERM_50OHM (1 << 9)
#define VD80_TRIG_TERM_NONE (0 << 9)
#define VD80_TRIGEDGE_SHIFT 8
#define VD80_TRIGEDGE_MASK (0 << VD80_TRIGEDGE_SHIFT)
#define VD80_TRIGEDGE_RISING (0 << VD80_TRIGEDGE_SHIFT)
#define VD80_TRIGEDGE_FALLING (1 << VD80_TRIGEDGE_SHIFT)
#define VD80_TRIGEXTOUT (1 << 7)
#define VD80_TRIGVXIOUT_SHIFT 4
#define VD80_TRIGVXIOUT_MASK (7 << VD80_TRIGVXIOUT_SHIFT)
#define VD80_TRIGVXIOUT_VXI_NONE (0 << VD80_TRIGVXIOUT_SHIFT)
#define VD80_TRIGVXIOUT_VXI_TRIG0 (1 << VD80_TRIGVXIOUT_SHIFT)
#define VD80_TRIGVXIOUT_VXI_TRIG1 (2 << VD80_TRIGVXIOUT_SHIFT)
#define VD80_TRIGVXIOUT_VXI_TRIG2 (3 << VD80_TRIGVXIOUT_SHIFT)
#define VD80_TRIGVXIOUT_VXI_TRIG3 (4 << VD80_TRIGVXIOUT_SHIFT)
#define VD80_TRIGVXIOUT_VXI_TRIG4 (5 << VD80_TRIGVXIOUT_SHIFT)
#define VD80_TRIGVXIOUT_VXI_TRIG5 (6 << VD80_TRIGVXIOUT_SHIFT)
#define VD80_TRIGVXIOUT_VXI_TRIG6 (7 << VD80_TRIGVXIOUT_SHIFT)
#define VD80_TRIGSOURCE_SHIFT 0
#define VD80_TRIGSOURCE_MASK (0xf << VD80_TRIGSOURCE_SHIFT)
#define VD80_TRIGSOURCE_INT (0 << VD80_TRIGSOURCE_SHIFT)
#define VD80_TRIGSOURCE_VXI_TRIG0 (1 << VD80_TRIGSOURCE_SHIFT)
#define VD80_TRIGSOURCE_VXI_TRIG1 (2 << VD80_TRIGSOURCE_SHIFT)
#define VD80_TRIGSOURCE_VXI_TRIG2 (3 << VD80_TRIGSOURCE_SHIFT)
#define VD80_TRIGSOURCE_VXI_TRIG3 (4 << VD80_TRIGSOURCE_SHIFT)
#define VD80_TRIGSOURCE_VXI_TRIG4 (5 << VD80_TRIGSOURCE_SHIFT)
#define VD80_TRIGSOURCE_VXI_TRIG5 (6 << VD80_TRIGSOURCE_SHIFT)
#define VD80_TRIGSOURCE_VXI_TRIG6 (7 << VD80_TRIGSOURCE_SHIFT)
#define VD80_TRIGSOURCE_EXT (8 << VD80_TRIGSOURCE_SHIFT)
#define VD80_TRIGSOURCE_ANALOG (9 << VD80_TRIGSOURCE_SHIFT)
/* Trigger Control Register 2 offset 0x24 */
#define VD80_TCR2 0x24
#define VD80_POSTSAMPLES_SHIFT 0
#define VD80_POSTSAMPLES_MASK (0x7ffff << VD80_POSTSAMPLES_SHIFT)
/* Trigger Status Register offset 0x28 */
#define VD80_TSR 0x28
#define VD80_ACTPOSTSAMPLES_SHIFT 0
#define VD80_ACTPOSTSAMPLES_MASK (0x1ffffff << VD80_ACTPOSTSAMPLES_SHIFT)
/* Shot Status Register offset 0x2c */
#define VD80_SSR 0x2c
#define VD80_SHOTLEN_SHIFT 0
#define VD80_SHOTLEN_MASK (0x1ffffff << VD80_SHOTLEN_SHIFT)
/* ADC registers - channel 1 to 16 offset 0x30-0x4c */
#define VD80_ADCR1 0x30 /* channels 2-1 */
#define VD80_ADCR2 0x34 /* channels 4-3 */
#define VD80_ADCR3 0x38 /* channels 6-5 */
#define VD80_ADCR4 0x3c /* channels 8-7 */
#define VD80_ADCR5 0x40 /* channels 10-9 */
#define VD80_ADCR6 0x44 /* channels 12-11 */
#define VD80_ADCR7 0x48 /* channels 14-13 */
#define VD80_ADCR8 0x4c /* channels 16-15 */
/* Memory Control Register 1 offset 0x50 */
#define VD80_MCR1 0x50
#define VD80_READSTART_SHIFT 0
#define VD80_READSTART_MASK (0x7ffff << VD80_READSTART_SHIFT)
/* Memory Control Register 2 offset 0x54 */
#define VD80_MCR2 0x54
#define VD80_READLEN_SHIFT 0
#define VD80_READLEN_MASK (0x7ffff << VD80_READSTART_SHIFT)
/* Pre Trigger Status Register offset 0x58 */
#define VD80_PTSR 0x58
#define VD80_PRESAMPLES_SHIFT 0
#define VD80_PRESAMPLES_MASK (0x1ffffff << VD80_PRESAMPLES_SHIFT)
/* Pre Trigger Control Register offset 0x5c */
#define VD80_PTCR 0x5c
#define VD80_PSCNTCLKONSMPL (1 << 2)
#define VD80_PSREGCLRONREAD (1 << 1)
#define VD80_PSCNTCLRONSTART (1 << 0)
/* Memory Read/Write Register offset 0x00-0x0c */
/* Locations 0x04-0x0c are mirrors of 0x00? TBC. */
#define VD80_MRWR 0x00
/* More significant alias for reading */
#define VD80_MEMOUT 0x00
/* Definitions for writing */
#define VD80_MEMSEG_SHIFT 16
#define VD80_MEMSEG_MASK (0xf << VD80_MRWR_MEMSEG_SHIFT)
#define VD80_MEMIN_SHIFT 0
#define VD80_MEMIN_MASK (0xffff << VD80_MRWR_MEMIN_SHIFT)
/* Calibration Register offset 0x60-0x6c */
/* Locations 0x64-0x6c are mirrors of 0x60? TBC. */
#define VD80_CALR 0x60
#define VD80_CALKEY_SHIFT 24
#define VD80_CALKEY_MASK (0xf << VD80_CALKEY_SHIFT)
#define VD80_CALMSHD (1 << 23)
#define VD80_CALFUNC_SHIFT 21
#define VD80_CALFUNC_MASK (3 << VD80_CALFUNC_SHIFT)
#define VD80_CALRDY (1 << 20)
#define VD80_CALADDR_ROM_SHIFT 17
#define VD80_CALADDR_ROM_MASK (3 << VD80_CALADDR_ROM_SHIFT)
#define VD80_CALADDR_CHAN_SHIFT 13
#define VD80_CALADDR_CHAN_MASK (0xf << VD80_CALADDR_CHAN_SHIFT)
#define VD80_CALADDR_LSB_SHIFT 12
#define VD80_CALADDR_OFFSET (0 << VD80_CALADDR_LSB_SHIFT)
#define VD80_CALADDR_GAIN (1 << VD80_CALADDR_LSB_SHIFT)
#define VD80_CALDATA_SHIFT 0
#define VD80_CALDATA_MASK (0xfff << VD80_CALDATA_SHIFT)
/* Sub Sample Control Register 0x70-0x7c */
/* Locations 0x74-0x7c are mirrors of 0x70. */
#define VD80_SUBSAMPCR 0x70
#define VD80_SUBDIV_SHIFT 16
#define VD80_SUBDIV_MASK (0xffff << VD80_SUBDIV_SHIFT)
#define VD80_TRIGEN (1 << 4)
#define VD80_SUBMODE_SHIFT 3
#define VD80_SUBMODE_FIFO (0 << VD80_SUBMODE_SHIFT)
#define VD80_SUBMODE_RING (1 << VD80_SUBMODE_SHIFT)
#define VD80_SUBSIZE_SHIFT 0
#define VD80_SUBSIZE_MASK (7 << VD80_SUBSIZE_SHIFT)
#define VD80_SUBSIZE_1 (0 << VD80_SUBSIZE_SHIFT)
#define VD80_SUBSIZE_2 (1 << VD80_SUBSIZE_SHIFT)
#define VD80_SUBSIZE_4 (2 << VD80_SUBSIZE_SHIFT)
#define VD80_SUBSIZE_8 (3 << VD80_SUBSIZE_SHIFT)
#define VD80_SUBSIZE_16 (4 << VD80_SUBSIZE_SHIFT)
#define VD80_SUBSIZE_32 (5 << VD80_SUBSIZE_SHIFT)
#define VD80_SUBSIZE_64 (6 << VD80_SUBSIZE_SHIFT)
/* Sub Sample buffers, channel 1 to 16 0x80-0xbc */
#define VD80_SUBSAMP_CHAN1 0x80
#define VD80_SUBSAMP_CHAN2 0x84
#define VD80_SUBSAMP_CHAN3 0x88
#define VD80_SUBSAMP_CHAN4 0x8c
#define VD80_SUBSAMP_CHAN5 0x90
#define VD80_SUBSAMP_CHAN6 0x94
#define VD80_SUBSAMP_CHAN7 0x98
#define VD80_SUBSAMP_CHAN8 0x9c
#define VD80_SUBSAMP_CHAN9 0xa0
#define VD80_SUBSAMP_CHAN10 0xa4
#define VD80_SUBSAMP_CHAN11 0xa8
#define VD80_SUBSAMP_CHAN12 0xac
#define VD80_SUBSAMP_CHAN13 0xb0
#define VD80_SUBSAMP_CHAN14 0xb4
#define VD80_SUBSAMP_CHAN15 0xb8
#define VD80_SUBSAMP_CHAN16 0xbc
#define VD80_SUBSAMP_FULL (1 << 17)
#define VD80_SUBSAMP_EMPTY (1 << 16)
#define VD80_SUBSAMP_SAMP_SHIFT 0
#define VD80_SUBSAMP_SAMP_MASK (0xffff << VD80_SUBSAMP_SAMP_SHIFT)
/* Analog Trigger Levels, channel 1 to 16 0xc0-0xfc */
#define VD80_ATRIG_CHAN1 0xc0
#define VD80_ATRIG_CHAN2 0xc4
#define VD80_ATRIG_CHAN3 0xc8
#define VD80_ATRIG_CHAN4 0xcc
#define VD80_ATRIG_CHAN5 0xc0
#define VD80_ATRIG_CHAN6 0xc4
#define VD80_ATRIG_CHAN7 0xc8
#define VD80_ATRIG_CHAN8 0xcc
#define VD80_ATRIG_CHAN9 0xc0
#define VD80_ATRIG_CHAN10 0xc4
#define VD80_ATRIG_CHAN11 0xc8
#define VD80_ATRIG_CHAN12 0xcc
#define VD80_ATRIG_CHAN13 0xc0
#define VD80_ATRIG_CHAN14 0xc4
#define VD80_ATRIG_CHAN15 0xc8
#define VD80_ATRIG_CHAN16 0xcc
#define VD80_ATRIG_LEVEL_ABOVE_SHIFT 17
#define VD80_ATRIG_LEVEL_ABOVE_MASK (0xfff << VD80_ATRIG_LEVEL_ABOVE_SHIFT)
#define VD80_ATRIG_LEVEL_BELOW_SHIFT 5
#define VD80_ATRIG_LEVEL_BELOW_MASK (0xfff << VD80_ATRIG_LEVEL_BELOW_SHIFT)
#define VD80_ATRIG_LOGIC_SHIFT 4
#define VD80_ATRIG_LOGIC_OR (0 << VD80_ATRIG_LOGIC_SHIFT)
#define VD80_ATRIG_LOGIC_AND (1 << VD80_ATRIG_LOGIC_SHIFT)
#define VD80_ATRIG_CHANGE_SHIFT 4
#define VD80_ATRIG_CHANGE_LEVEL (0 << VD80_ATRIG_CHANGE_SHIFT)
#define VD80_ATRIG_CHANGE_EDGE (1 << VD80_ATRIG_CHANGE_SHIFT)
#define VD80_ATRIG_WIN_SHIFT 0
#define VD80_ATRIG_WIN_MASK (7 << VD80_ATRIG_WIN_SHIFT)
#define VD80_ATRIG_WIN_DISABLED (0 << VD80_ATRIG_WIN_SHIFT)
#define VD80_ATRIG_WIN_BELOW_AND_ABOVE (1 << VD80_ATRIG_WIN_SHIFT)
#define VD80_ATRIG_WIN_BELOW_OR_ABOVE (2 << VD80_ATRIG_WIN_SHIFT)
#define VD80_ATRIG_WIN_BELOW (3 << VD80_ATRIG_WIN_SHIFT)
#define VD80_ATRIG_WIN_ABOVE (4 << VD80_ATRIG_WIN_SHIFT)
#endif /* _VD80_H */
/*
* testvd80.c - simple VD80 test.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/mman.h>
#include <libvmebus.h>
#include "libvd80.h"
static char git_version[] = "git_version: " GIT_VERSION;
#define CHAN_USED 16
#define MAX_MBLT_FRAMES 492
#define MAX_BLT_FRAMES 44
#define SAMPLE_TIME 15000000
unsigned int shot_samples;
unsigned int *read_buf[CHAN_USED];
struct timespec read_start_time[CHAN_USED];
struct timespec read_end_time[CHAN_USED];
long long timespec_subtract(struct timespec * a, struct timespec *b)
{
long long ns;
ns = (b->tv_sec - a->tv_sec) * 1000000000LL;
ns += (b->tv_nsec - a->tv_nsec);
return ns;
}
/**
* test_sampling() - Run a sampling test
* @duration: Sampling duration in us
*/
int test_sampling(unsigned int duration)
{
int rc;
struct timespec ts;
ts.tv_sec = duration / 1000000;
ts.tv_nsec = duration * 1000 - (ts.tv_sec * 1000000000);
/* Start sampling */
printf("Starting sampling for %d us\n", duration);
if ((rc = vd80_cmd_start()))
return 1;
/* Wait for a while */
clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
/* Trigger */
if ((rc = vd80_cmd_trig()))
return 1;
printf("Sampling stopped\n");
/* Get the number of samples */
shot_samples = vd80_get_shot_length();
printf("Recorded %d samples\n", shot_samples);
return 0;
}
/**
* dump_samples() - Dump the samples read from the channels
* @num_frames: number of frames
* @num_chans: number of channels
*/
void
dump_samples(unsigned int num_frames, unsigned int num_chans)
{
int i, j;
printf("samp ");
for (i = 0; i < num_chans; i++)
printf("Ch%-2d ", i + 1);
printf("\n");
for (i = 0; i < num_frames; i++) {
printf("%4d", i*2);
for (j = 0; j < num_chans; j++)
printf(" %.4x",
swapbe16((read_buf[j][i] >> 16) & 0xffff));
printf("\n");
printf("%4d", i*2 + 1);
for (j = 0; j < num_chans; j++)
printf(" %.4x",
swapbe16(read_buf[j][i] & 0xffff));
printf("\n");
}
}
/**
* read_samples_dma() - Read recorded samples
* @first_frame: First frame to read
* @num_frames: Number of frames to read
*
* @note One frame is 32 samples which makes it 2 samples for each channel.
*
*/
int time_read_samples_dma(unsigned int first_frame, unsigned int num_frames,
unsigned int num_chans)
{
int rc = 0;
int i, j;
struct vme_dma dma_desc;
unsigned int rem_start;
unsigned int rem_length;
double total_time = 0.0;
double total_throughput;
if (num_frames <= 0)
return -1;
/* Set readstart and readlen */
if ((rc = vd80_set_readstart(first_frame)))
return rc;
if ((rc = vd80_set_readlen(num_frames - 1)))
return rc;
/* Setup the common parts of the DMA descriptor */
memset(&dma_desc, 0, sizeof(struct vme_dma));
dma_desc.dir = VME_DMA_FROM_DEVICE;
dma_desc.novmeinc = 1;
dma_desc.src.data_width = VD80_DMA_DW;
dma_desc.src.am = VD80_DMA_AM;
dma_desc.ctrl.pci_block_size = VME_DMA_BSIZE_4096;
dma_desc.ctrl.pci_backoff_time = VME_DMA_BACKOFF_0;
dma_desc.ctrl.vme_block_size = VME_DMA_BSIZE_4096;
dma_desc.ctrl.vme_backoff_time = VME_DMA_BACKOFF_0;
/* Allocate memory for the buffers */
for (i = 0; i < num_chans; i++) {
read_buf[i] = NULL;
if (posix_memalign((void **)&read_buf[i], 4096,
num_frames * 4)) {
printf("Failed to allocate buffer for channel %d: %s\n",
i+1, strerror(errno));
rc = 1;
goto out_free;
}
}
/* Read the samples for each channel */
printf("Starting samples DMA readout of %d frames\n", num_frames);
for (i = 0; i < num_chans; i++) {
/* Read the samples */
clock_gettime(CLOCK_MONOTONIC,&read_start_time[i]);
/* Select the channel to read from */
if ((rc = vd80_cmd_read(i + 1)))
goto out_free;
/*
* DMA read MAX_MBLT_FRAMES frames (MAX_MBLT_FRAMES 32-bit
* words) at a time
* */
for (j = 0; j < num_frames/MAX_MBLT_FRAMES; j++) {
/* Setup the DMA descriptor */
dma_desc.src.addru = 0;
dma_desc.src.addrl = VD80_DMA_BASE;
/* strange casting to keep compiler quiet */
dma_desc.dst.addru = (unsigned int)
(((unsigned long long)(uintptr_t)
(read_buf[i] + (j*MAX_MBLT_FRAMES)))
>>32);
dma_desc.dst.addrl = (unsigned int)(uintptr_t)
(read_buf[i] + (j*MAX_MBLT_FRAMES));
dma_desc.length = MAX_MBLT_FRAMES * 4;
/* printf("DMA read frame %d len %d -> 0x%08x\n", */
/* j * MAX_MBLT_FRAMES, MAX_MBLT_FRAMES*4, */
/* dma_desc.dst.addrl); */
/* DMA read the samples */
if ((rc = vme_dma_read(&dma_desc))) {
printf("Failed to read samples for channel %d: "
"%s\n", i + 1, strerror(errno));
rc = 1;
goto out_free;
}
}
/* Get the remaining samples */
rem_start = (num_frames/MAX_MBLT_FRAMES) * MAX_MBLT_FRAMES;
rem_length = num_frames - rem_start;
if (rem_length) {
/* Setup the DMA descriptor */
dma_desc.src.addru = 0;
dma_desc.src.addrl = VD80_DMA_BASE;
/* strange casting to keep compiler quiet */
dma_desc.dst.addru = (unsigned int)
(((unsigned long long)(uintptr_t)
(read_buf[i] + rem_start))
>>32);
dma_desc.dst.addrl = (unsigned int)(uintptr_t)
(read_buf[i] + rem_start);
dma_desc.length = rem_length * 4;
/* printf("DMA read frame %d len %d -> 0x%08x\n", */
/* rem_start, rem_length, dma_desc.dst.addrl); */
/* DMA read the samples */
if ((rc = vme_dma_read(&dma_desc))) {
printf("Failed to read samples for channel %d: "
"%s\n", i + 1, strerror(errno));
rc = 1;
goto out_free;
}
}
clock_gettime(CLOCK_MONOTONIC,&read_end_time[i]);
}
printf("Done reading %d samples on %d channels\n",
num_frames * 2, num_chans);
printf("Throughput: \n");
/* Display throughput */
for (i = 0; i < num_chans; i++) {
unsigned long long delta_ns;
double delta;
double throughput;
delta_ns = timespec_subtract(&read_start_time[i],
&read_end_time[i]);
delta = (double)delta_ns/(double)1000000000.0;
throughput = (double)(num_frames * 2) / delta;
printf(" Ch%-2d: %.6fs - %.6f MSPS\n",
i+1, delta, throughput/(1024.0*1024.0));
total_time += delta;
}
total_throughput = (double)(num_frames * 2 * num_chans) / total_time;
printf("\n");
printf("Total: %.6f s %.6f MSPS\n",
total_time, total_throughput/(1024.0*1024.0));
printf("\n");
out_free:
for (i = 0; i < num_chans; i++) {
if (read_buf[i])
free(read_buf[i]);
}
return rc;
}
int main(int argc, char **argv)
{
int rc = 0;
printf("%s: %s\n", argv[0], git_version);
printf("%s\n", libvmebus_version_s);
printf("%s\n\n", libvd80vmebridge_version_s);
if (vd80_map())
exit(1);
if (vd80_init())
exit(1);
vd80_cmd_setbig(0);
/* Run a 15 s recording to be sure to get 1M samples */
if ((rc = test_sampling(SAMPLE_TIME)))
goto out;
/* Check we've got at least 1M samples */
if (shot_samples < (1024*1024)) {
printf("Not enough samples recorded - exiting\n");
rc = 1;
goto out;
}
/* Make sure we won't be swapped */
if (mlockall(MCL_CURRENT|MCL_FUTURE)) {
printf("Failed to lock process address space: %s\n",
strerror(errno));
printf("Results may not be accurate\n");
}
/* Read 1M samples (512K frames) from 16 channels */
if ((rc = time_read_samples_dma(0, 512*1024, 1)))
goto out;
out:
rc = vd80_exit();
munlockall();
return rc;
}
/*
* testvd80.c - simple VD80 test.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/mman.h>
#include <libvmebus.h>
#include "libvd80.h"
static char git_version[] = "git_version: " GIT_VERSION;
#define CHAN_USED 16
unsigned int shot_samples;
unsigned int *read_buf[CHAN_USED];
struct timespec read_start_time[CHAN_USED];
struct timespec read_end_time[CHAN_USED];
long long timespec_subtract(struct timespec * a, struct timespec *b)
{
long long ns;
ns = (b->tv_sec - a->tv_sec) * 1000000000LL;
ns += (b->tv_nsec - a->tv_nsec);
return ns;
}
/**
* test_sampling() - Run a sampling test
* @duration: Sampling duration in us
*/
int test_sampling(unsigned int duration)
{
int rc;
struct timespec ts;
ts.tv_sec = duration / 1000000;
ts.tv_nsec = duration * 1000 - (ts.tv_sec * 1000000000);
/* Start sampling */
printf("Starting sampling for %d us\n", duration);
if ((rc = vd80_cmd_start()))
return 1;
/* Wait for a while */
clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
/* Trigger */
if ((rc = vd80_cmd_trig()))
return 1;
printf("Sampling stopped\n");
/* Get the number of samples */
shot_samples = vd80_get_shot_length();
printf("Recorded %d samples\n", shot_samples);
return 0;
}
/**
* time_read_samples() - Measure the time it takes to read the samples
* @first_frame: First frame to read
* @num_frames: Number of frames to read
* @num_chans: Number of channels to read
*
* @note One frame is 32 samples which makes it 2 samples for each channel.
*
*/
int time_read_samples(unsigned int first_frame, unsigned int num_frames,
unsigned int num_chans)
{
int rc = 0;
int i, j;
double total_time = 0.0;
double total_throughput;
if (num_frames <= 0)
return -1;
/* Set readstart and readlen */
if ((rc = vd80_set_readstart(first_frame)))
return rc;
if ((rc = vd80_set_readlen(num_frames - 1)))
return rc;
/* Allocate memory for the buffers */
for (i = 0; i < num_chans; i++) {
read_buf[i] = NULL;
if (posix_memalign((void **)&read_buf[i], 4096,
num_frames * 4)) {
printf("Failed to allocate buffer for channel %d: %s\n",
i+1, strerror(errno));
rc = 1;
goto out_free;
}
}
/* Read the samples for each channel */
printf("Starting samples readout\n");
for (i = 0; i < num_chans; i++) {
/* Select the channel to read from */
if ((rc = vd80_cmd_read(i + 1)))
goto out_free;
/* Read the samples */
clock_gettime(CLOCK_MONOTONIC,&read_start_time[i]);
for (j = 0; j < num_frames; j++)
read_buf[i][j] = vd80_get_sample();
clock_gettime(CLOCK_MONOTONIC,&read_end_time[i]);
}
printf("Done reading %d samples on %d channels\n\n",
num_frames * 2, num_chans);
printf("Throughput: \n");
/* Display throughput */
for (i = 0; i < num_chans; i++) {
unsigned long long delta_ns;
double delta;
double throughput;
delta_ns = timespec_subtract(&read_start_time[i],
&read_end_time[i]);
delta = (double)delta_ns/(double)1000000000.0;
throughput = (double)(num_frames * 2) / delta;
printf(" Ch%-2d: %.6fs - %.6f MSPS\n",
i+1, delta, throughput/(1024.0*1024.0));
total_time += delta;
}
total_throughput = (double)(num_frames * 2 * num_chans) / total_time;
printf("\n");
printf("Total: %.6f s %.6f MSPS\n",
total_time, total_throughput/(1024.0*1024.0));
printf("\n");
out_free:
for (i = 0; i < num_chans; i++) {
if (read_buf[i])
free(read_buf[i]);
}
return rc;
}
int main(int argc, char **argv)
{
int rc = 0;
printf("%s: %s\n", argv[0], git_version);
printf("%s\n", libvmebus_version_s);
printf("%s\n\n", libvd80vmebridge_version_s);
if (vd80_map())
exit(1);
if (vd80_init())
exit(1);
vd80_cmd_setbig(0);
/* Run a 15 s recording to be sure to get 1M samples */
if ((rc = test_sampling(15000000)))
goto out;
/* Check we've got at least 1M samples */
if (shot_samples < (1024*1024)) {
printf("No samples recorded - exiting\n");
rc = 1;
goto out;
}
/* Make sure we won't be swapped */
if (mlockall(MCL_CURRENT|MCL_FUTURE)) {
printf("Failed to lock process address space: %s\n",
strerror(errno));
printf("Results may not be accurate\n");
}
/* Read 1M samples (512K frames) from 1 channel */
if ((rc = time_read_samples(0, 512*1024, 16)))
goto out;
out:
rc = vd80_exit();
munlockall();
return rc;
}
# include the build environment
include ../../common.mk
#include version information
include ../versions.mk
CFLAGS = -Wall -I../include $(EXTRACFLAGS)
CFLAGS += -DGIT_VERSION=\"$(GIT_VERSION)\"
LDFLAGS = -L../lib
LDLIBS = -lvmebus.$(CPU)
progs := vme.$(CPU)
all: $(progs)
vme.$(CPU): vme.c
.PHONY: all clean celanall
clean:
$(RM) $(progs) *.o
cleanall:
$(RM) -r vme.{${ALL_CPUS_COMMAS}}
PROGS_LIST=vme.$(CPU)
# use default instalation rule for programs/tools
install: install_prog_global
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <libvmebus.h>
static char git_version[] = "git_version: " GIT_VERSION;
void print_version(void);
void usage(char *prog)
{
fprintf(stderr,
"usage: %s [-v vme_address][options]\n\n", prog);
fprintf(stderr,
"This tool can read/write at a specific VME address\n\n");
fprintf(stderr,
"Options\n");
fprintf(stderr,
" -v 0x<NUM> : VME address to access\n");
fprintf(stderr,
" -s 0x<NUM> : address offset to access in bytes (skip bytes))\n");
fprintf(stderr,
" -a 0x<NUM> : address modifier for VME bus (default 0x09)\n");
fprintf(stderr,
" -l <NUM> : VME map length in bytes (default 512KiB)\n");
fprintf(stderr,
" -w <NUM> : value to write\n");
fprintf(stderr,
" -d <NUM> : data width in bits (default 32 bits)\n");
fprintf(stderr,
" -D <NUM> : access data width in bits. It defines\n"
" which data type use to access the memory\n"
" (default 32 bits)\n");
fprintf(stderr,
" -n <NUM> : number of incremental accesses to the memory\n");
fprintf(stderr,
" -H : hide the address\n");
fprintf(stderr,
" -h : print this help\n");
fprintf(stderr,
" -V : version\n");
fprintf(stderr, "\n");
fprintf(stderr,
"Data Width\n");
fprintf(stderr,
"The supported data widths are 8, 16 and 32 for the access\n"
"data width; 16 and 32 for the data width\n");
fprintf(stderr, "\n");
fprintf(stderr,
"Address Modifier\n");
fprintf(stderr,
"The supported address modifier are the following:\n");
fprintf(stderr, " 0x09 A32 user data\n");
fprintf(stderr, " 0x29 A16 user data\n");
fprintf(stderr, " 0x39 A24 user data\n");
fprintf(stderr, "\n");
print_version();
exit(1);
}
void print_version(void)
{
fprintf(stderr, "%s\n", git_version);
fprintf(stderr, "%s\n", libvmebus_version_s);
}
int is_invalid_width(int data_width)
{
return (data_width != 8 &&
data_width != 16 &&
data_width != 32);
}
static void vme_mem_write(int width, volatile void *addr, uint32_t value)
{
switch (width) {
case 8:
*(uint8_t *)addr = value;
break;
case 16:
*(uint16_t *)addr = htons(value);
break;
case 32:
*(uint32_t *)addr = htonl(value);
break;
}
}
static uint32_t vme_mem_read(int width, volatile void *addr)
{
switch (width) {
case 8:
return *(uint8_t *)addr;
case 16:
return ntohs(*(uint16_t *)addr);
case 32:
return ntohl(*(uint32_t *)addr);
}
return -1;
}
int main(int argc, char *argv[])
{
struct vme_mapping map;
struct vme_mapping *mapp = &map;
volatile void *ptr;
unsigned int vmebase, am, data_width, access_width;
unsigned int offset;
unsigned int length;
int i, count;
int c;
int write, offsets_on;
int width;
uint32_t word, datum;
/* vme defaults */
count = 1;
am = VME_A32_USER_DATA_SCT;
data_width = access_width = VME_D32;
write = 0;
offsets_on = 1;
offset = 0;
length = 0x80000;
while ((c = getopt(argc, argv, "Hv:s:D:d:a:n:w:l:hV")) != -1) {
switch (c) {
case 'H':
offsets_on = 0;
break;
case 's':
offset = strtoul(optarg, NULL, 0);
break;
case 'v':
vmebase = strtoul(optarg, NULL, 0);
break;
case 'd':
case 'D':
width = strtoul(optarg, NULL, 0);
if (is_invalid_width(width)) {
fprintf(stderr, "invalid data width %d\n", width);
exit(1);
}
if (c == 'd')
data_width = width;
else
access_width = width;
break;
case 'a':
am = strtoul(optarg, NULL, 0);
break;
case 'n':
count = strtoul(optarg, NULL, 0);
break;
case 'l':
length = strtoul(optarg, NULL, 0);
break;
case 'w':
write = 1;
word = strtoul(optarg, NULL, 0);
break;
case '?':
case 'h':
usage(argv[0]);
break;
case 'V':
print_version();
exit(1);
default:
break;
}
}
/* Prepare mmap description */
memset(mapp, 0, sizeof(*mapp));
mapp->am = am;
mapp->data_width = (data_width == 8) ? 16 : data_width;
mapp->sizel = length;
mapp->vme_addrl = vmebase;
/* Do mmap */
ptr = vme_map(mapp, 1);
if (!ptr) {
printf("could not map at 0x%08x\n", vmebase);
exit(1);
}
fprintf(stderr, "vme 0x%08x kernel %p user %p\n",
vmebase, mapp->kernel_va, mapp->user_va);
for (i = 0; i < count; i++, offset += 4) {
volatile void *addr = ptr + offset;
if (!write) {
datum = vme_mem_read(access_width, addr);
if (offsets_on)
printf("%08x: ", vmebase + offset);
printf("%08x\n", datum);
} else {
vme_mem_write(access_width, addr, word);
}
}
vme_unmap(mapp, 1);
return 0;
}
#define product's name
PRODUCT_NAME=vmebus
# common version
VER_MAJOR=1
VER_MINOR=0
VER_PATCH=1
# version of the header file(s)
HEADER_MAJOR=$(VER_MAJOR)
HEADER_MINOR=$(VER_MINOR)
HEADER_PATCH=$(VER_PATCH)
# version of the library
LIB_MAJOR=$(VER_MAJOR)
LIB_MINOR=$(VER_MINOR)
LIB_PATCH=$(VER_PATCH)
# version of the programs
PROG_MAJOR=$(VER_MAJOR)
PROG_MINOR=$(VER_MINOR)
PROG_PATCH=$(VER_PATCH)
# define versions
VER_PREV=
VER_CURRENT=$(VER_MAJOR).$(VER_MINOR)
VER_NEXT=
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