NAME | SYNOPSIS | API RESTRICTIONS | FEATURES | DESCRIPTION | EXTENDED DESCRIPTION | ALLOWED CALLING CONTEXTS | ATTRIBUTES
#include <ddi/vme/vme.h>
The function or functions documented here may not be used safely in all application contexts with all APIs provided in the ChorusOS 5.0 product.
See API(5FEA) for details.
DDI
Provides VME bus driver interface services.
The VME bus driver offers an API for VME device driver development. This VME bus API is an abstraction of the low-level VME bus services and covers the following VME functional modules:
Access to slaves' I/O registers.
Access to slaves' memory.
Exporting memory to other bus Masters.
The following bus options are covered by the API when available:
A16/A24/A32 address spaces.
D8/D16/D32 data sizes for I/O access.
D8/D16/D32/D64 data sizes and block transfers for memory access.
Read Modify Write cycles.
Bus arbiter, requester, time-out configuration through properties..
First of all, a VME device driver must register itself in the microkernel driver registry. This should be done from its main function using svDriverRegister. The driver must set the bus class to "vme" in its registry entry, and specify the lowest version number of VME bus interface required to run correctly. This registration allows the kernel to call back the VME device driver's drv_probe, drv_bind and drv_init routines at bus probing, binding and initialization phases respectively.
Within the drv_probe or drv_init routine, the VME device driver may establish a connection with the parent VME bus driver, as described below. Once a connection is established, the VME device driver may use services provided by the VME bus driver. Note that a connection to the bus driver can be established only within the driver's drv_probe or drv_init routine.
If the drv_init routine is defined, the VME bus driver invokes it at bus initialization time. Note that the drv_init routine is called in the DKI thread context, restricting the services available to it (drv_init can only invoke QUICC bus services).
The drv_init routine is called with three arguments:
The VME device driver's own node, which identifies the node associated to the device in the device tree.
The VME bus operations structure, which defines the VME bus API and its version.
The VME bus identifier, which is an opaque to be passed back when calling the VmeBusOps.open operation to connect the device driver to the VME bus.
From this point on, VmeBusOps.open must be the first call issued by the VME device driver to the VME bus driver:
KnError (*open)(VmeId vmeId, DevNode devNode, VmeEventHandler evtHandler, VmeLoadHandler loadHandler, void* cookie, VmeDevId* devId);
This establishes a connection to the bus, identified by the devId value returned in the last parameter.
This devId identifier is an argument for most of the other services defined in the VmeBusOps structure.
vmeId and devNode are given by the microkernel as parameters to the drv_init driver routine.
evtHandler is a handler in the device driver, which is invoked by the VME bus driver when a VME bus event occurs. The cookie argument is passed back to this handler as first parameter.
This VME bus event handler takes two additional parameters: the bus event type and an event type specific structure pointer.
The bus event type can be one of the following:
Notifies a device driver that the system is going to be shut down. The device driver should reset the device hardware and return from the event handler. Note that the driver must neither notify clients nor free allocated resources.
Notifies a device driver that the device should be shut down. The device driver should notify driver clients (via svDeviceEvent) that the device is going to be shut down and should then return from the event handler. Once the device entry is released by the last driver client, the device registry module invokes a driver call-back handler. Within this handler, the device driver should reset the device hardware, release all used resources and close the bus connection invoking VmeBusOps.close. Note that the VME_DEV_SHUTDOWN event may be used by a bus driver in order to confiscate (or to re-allocate) bus resources.
Notifies a device driver that the device has been removed from the bus and therefore the device driver instance has to be shut down. The actions taken by the driver are similar to the VME_DEV_SHUTDOWN case except that the device hardware must not be accessed by the driver.
Indicates that a VME bus module has detected a system failure.
Indicates that the system power source is about to stop.
Indicates that the VME bus arbiter has detected an arbitration timeout.
The event type specific structure pointer argument is always NULL for VME bus events.
As the event handler is executed in the context of an interrupt, its implementation must be restricted to the API allowed at interrupt level.
loadHandler is a handler in the device driver which is invoked by the VME bus driver when a new driver appears on the system (for example, a new driver is downloaded at run time). Note that loadHandler is optional and must be set to NULL when it is not implemented by the device driver. The cookie argument is passed back to this handler as an argument. Typically, this type of event is not important for a leaf VME device driver. loadHandler is usually used by a VME nexus driver (for example, SCSI host bus adapter) which has to try to apply a newly downloaded driver to its child devices which are not yet serviced. Note that the loadHandler routine is called in the DKI thread context (refer to the table "Allowed Calling Context" to check the list of QUICC bus services allowed in that context).
If VmeBusOps.open returns K_OK, a valid identifier is returned in the devId argument. This identifier must be used to call other services defined in the VmeBusOps structure. Some of these services also return an "Ops" structure defining new services on a new object instance, an I/O or a memory region, for example. Others simply perform a service for the device driver, without giving access to a sub-part of the API.
On failure, VmeBusOps.open returns an error code as follows:
The device node is invalid, that is, the device node is not a child of the bus node.
A connection is already open for the device node specified.
The system is out of memory.
To release the connection to the VME bus, the driver must call the VmeBusOps.close service as follows:
void (*close)(VmeDevId devId);
After having been disconnected from the VME bus, the device driver can no longer use any of the VME bus API services except VmeBusOps.open.
If the drv_probe routine is defined, the VME bus driver invokes it at bus probing time. Note that the drv_probe routine is called in the DKI thread context (refer to the table "Allowed Calling Context" to check the list of QUICC bus services allowed in that context).
The drv_probe routine is called with three arguments:
The VME device driver's own node, which identifies the node associated to the device in the device tree.
The VME bus operations structure, which defines the VME bus API and its version.
The VME bus identifier, which is an opaque to be passed back when calling the VmeBusOps.open operation to connect the device driver to the VME bus.
The drv_probe routine is called first by the bus driver. This allows the VME device driver to discover a device (which can be serviced by the driver) on the bus and to create a device node for the device in the device tree. If drv_probe creates a node, corresponding to a device residing on the bus, it should attach properties, specifying required bus resources, to the device node (for exmample, "intr", "mem-rgn"). The VME bus driver is able to refer to the device node properties and allocate bus resources for the device (if needed) . The bus driver can also check resource conflicts with other devices residing on the bus.
The drv_bind routine is called by the bus driver when the probing phase is complete. It enables the device driver to perform a driver-to-device binding. A VME device driver should examine the device's properties to check whether a given device can be serviced by the driver. If the check is positive, the driver binds itself to the device. This driver initiated binding is achievd by attaching the "driver" property to the device node. The "driver" property value is a NULL terminated ASCII string, specifying the driver name. Note that the driver name specified in the property must match the driver name specified in the driver registry entry. Under these conditions, the VME bus driver will invoke the driver's drv_init routine for the device node, as described above. Note that the drv_init routine is invoked by the bus driver if, and only if, the bus resources required for the device are successfully allocated/checked by the bus driver. Note that the drv_bind routine is called in the DKI thread context (refer to the table "Allowed Calling Context" to check the list of QUICC bus services allowed in that context).
Devices residing on the VME bus may be discovered by using the VME bus services. In such cases, a connection must be established between the VME bus driver and the VME device driver. In order to establish a connection, the device driver has to create a device node and to attach it to the bus node. This device node may be temporary but it is mandatory as an argument to the VmeBusOps.open routine. When the probing process is finished and the connection is closed, depending on the probing results, the device driver should either delete the temporary device node or transform it to a real device node. In the latter case, the device that may be serviced by the driver is located on the bus.
It is important that the device driver does not create redundant device nodes. In particular, when the device driver discovers a device and wishes to create a device node corresponding to the device, it should check that there is no existing device node (among the bus child nodes) representing the same device.
If a connection to the VME bus is established by the drv_probe routine, it should be closed (via VmeBusOps.close) before leaving the routine.
To perform I/O access to registers of a VME device, a VME driver must:
Map the device's registers into an I/O region.
Use services defined by the mapped I/O region to access registers.
Unmap the region when access is no longer needed.
The VmeBusOps.io_map service is used to map an I/O region from a VME device to enable access to this region as follows:
KnError (*io_map)(VmeDevId devId, VmePropIoRegs* ioRgn, VmeErrHandler errHandler, void* errCookie, VmeIoOps** ioOps, VmeIoId* ioId);
The ioRgn structure defines the I/O region to map. This structure is an element of the array stored in a property of the device node. This property should be retrieved by the driver using the device tree API: (dtreePropFind, dtreePropLength, dtreePropValue), prior to calling VmeBusOps.io_map.
The name of the property used to described device I/O regions is "io-regs", its value contains an array of VmePropIoRegs structures.
typedef struct VmePropIoRegs { VmeAddrSpace space; VmeAddr addr; VmeSize size; VmeAddr mask; } VmePropIoRegs;
The space field specifies the VME address space where the registers reside:
VME_A16 VME short address space
VME_A24 VME standard address space
VME_A32 VME extended address space
VME_CSR VME configuration / control and status registers' address space
The addr field specifies the VME start address of the registers' range.
The size field specifies the registers' range size in bytes.
The mask field specifies which address bits are fixed/floating. This field is used by the VME bus driver at the resource allocation stage to determine the device address decoder constraints. A bit set to zero within mask specifies a fixed address bit within addr. In other words, the corresponding bit within an allocated address must be the same as within addr. A bit set to one within mask specifies a floating address bit (any value of the corresponding bit within an allocated address is acceptable). When the VmePropIoRegs structure is passed to VmeBusOps.io_map, the I/O register range is already allocated and therefore the mask field is meaningless.
On success, K_OK is returned and appropriate I/O services are returned in the ioOps parameter. An identifier for the mapped I/O region is also returned in ioId. This identifier must be used as first parameter to further calls to the VmeIoOps services.
errHandler is a handler in the device driver, which is invoked by the VME bus driver when a VME bus error occurs while accessing the mapped region. errCookie is passed back to the errHandler as first parameter.
On failure, VmeBusOps.io_map returns an error code as follows:
A size of zero was specified.
Invalid or unsupported VME address space.
Not enough virtual address space to map I/O registers.
The system is out of memory.
Note that VME I/O access is restricted to supervisory data access.
Once the region has been successfully mapped, the driver can use the services defined by the VmeIoOps structure.
The VME bus provides five service routine sets to access a mapped I/O region as follows:
VmeIoOps.load_8/16/32
VmeIoOps.store_8/16/32
VmeIoOps.read_8/16/32
VmeIoOps.write_8/16/32
VmeIoOps.tas_8/16/32
There are three service routines in each set which deal with I/O registers of different widths. The _8, _16 or _32 suffix indicates the data size of the transfer on the VME bus.
Usable data sizes for I/O depend on the address space in which the region is mapped. If the driver tries to access a register using too large a data size transfer, the VME bus driver will generate an error, and notify the driver through the error handler attached to the mapped region. The error code will be VME_ERR_INVALID_SIZE.
In all service routines provided by VmeIoOps the ioId argument identifies the mapped I/O region.
The offset argument specifies the register offset, in bytes, within the mapped region.
These routines all handle byte swapping should the endian be different for the VME bus and the CPU.
uintxx_f (load_xx)(VmeIoId ioId, VmeSize offset);
The size of the returned value and of the data transfer on the VME bus is specified by the _xx suffix.
void (*store_xx)(VmeIoId ioId, VmeSize offset, uintxx_f value);
The size of the given value and the data transfer on the VME bus is specified by the _xx suffix.
The VmeIoOps.read_xx routine set loads values from a device register and writes them sequentially into a memory buffer, as follows:
void (*read_xx)(VmeIoId ioId, VmeSize offset, uintxx_f* buf, VmeSize count);
The size of each value loaded and of each data transfer on the VME bus is specified by the _xx suffix.
The count argument specifies the number of read transactions to perform.
The buf argument specifies the address of the memory buffer. The size of this buffer must be at least (count * size of each data transfer).
The VmeIoOps.write_xx routine set reads values sequentially from a memory buffer and stores them in a device register, as follows:
void (*write_xx)(VmeIoId ioId, VmeSize offset, uintxx_f* buf, VmeSize count);
The size of each value stored and of each data transfer on the VME bus is specified by the _xx suffix.
The count argument specifies the number of write transactions to perform.
The buf argument specifies the address of the memory buffer. The size of this buffer must be at least (count * size of each data transfer).
The VmeIoOps.tas_xx routine set reads a device register, compares it to a given value, and then writes back to the register the bits that compared true, as follows:
uintxx_f (*tas_xx)(VmeIoId ioId, VmeSize offset, uintxx_f enable, uintxx_f compare, uintxx_f set);
The size of each value and of each data transfer on the VME bus is specified by the _xx suffix.
The enable argument specifies a bit mask in order to select (enable) the bits to be modified, if compared true. Each bit set enables comparison and setting for that bit.
The compare argument specifies a value which is bitwise compared to the device register read value.
The set argument specifies a value to be written back to the device register for valid compared and enabled bits.
The device register read value is bitwise compared with the compare value and the enable value. Each enabled bit that compares true is replaced by the corresponding bit in the set value. A false comparison results in the original bit being unchanged.
The original value read from the device register is returned by the functions.
Assuming that readVal is the original value read and writeVal is the new value to write back, the following code illustrates the behavior of the tas_xx services:
/* read value into readVal */ replaced = ~(readVal ^ compare) & enable; writeVal = (set & replaced) | (readVal & ~replaced); /* write back writeVal */ return readVal;
When a driver no longer needs access to the device I/O register, or before closing the connection to the VME bus, it should unmap the I/O region used for these devices.
The VmeBusOps.io_unmap is used to unmap an I/O region previously mapped with VmeBusOps.io_map, and to release any resources allocated for this mapping, as follows:
void (*io_unmap)(VmeIoId ioId);
The ioId argument identifies the region to unmap.
When the VmeBusOps.io_unmap function is called, the driver can no longer use any services defined for the unmapped I/O region.
In order to access the memory of a VME device, a VME driver must:
Map device memory to the local virtual memory space.
Access device memory through the mapped region.
Unmap the region when access is no longer needed.
The mapped memory region may be accessed directly using its virtual address.
When accessing a memory mapped region, the driver must handle endianning problems.
The VmeBusOps.mem_map service is used to map a memory region from a VME device, enabling access to this region, as follows:
KnError (*mem_map)(VmeDevId devId, VmePropMemRgn* memRgn, VmeErrHandler errHandler, void* errCookie, VmeMemOps** memOps, VmeMemId* memId);
devId is the identifier returned by the VmeBusOps.open.
The memRgn structure defines the memory region to map. This structure is an element of the array stored in a property of the device node. This property should be retrieved by the driver using the device tree API: (dtreePropFind, dtreePropLength, dtreePropValue), prior to calling VmeBusOps.mem_map.
The name of the property used to describe device memory regions is "mem-rgn", its value contains an array of VmePropMemRgn structures, shown below:
typedef struct VmePropMemRgn { VmeAddrSpace space; VmeAddr addr; VmeSize size; VmeAddr mask; VmeMemAttr attr; } VmePropMemRgn;
The space field specifies the VME memory address space where the memory region resides:
VME short address space
VME standard address space
VME extended address space
The addr field specifies the VME start address of the memory region.
The size field specifies the memory region size in bytes.
The mask field specifies the device address decoder constraints (see section Mapping device I/O registers).
The attr argument specifies the mapping attributes. A combination of the following flags is allowed:
The memory region is readable.
The memory region is writable.
The memory region is executable.
The memory region is cacheable.
The memory region mapping inverts the byte order. Some MMUs support this type of mapping attribute. This feature may be used by a VME device driver in order to avoid byte swapping within a memory mapped region. Note that VmeBusOps.mem_map returns K_EINVAL if this type of feature is not supported.
the memory region is accessed using Supervisor AM codes
the memory region is accessed using User AM codes
the memory region is accessed using Program AM codes.
the memory region is accessed using Data AM codes.
the memory region is accessed using 8-bits data width cycles
the memory region is accessed using 16-bits data width cycles
the memory region is accessed using 32-bits data width cycles
the memory region is accessed using 64-bits data width cycles
Enables block transfers for this memory region.
Enables posted-write transfers for this memory region.
errHandler is a handler in the device driver which is invoked by the VME bus driver when a VME bus error occurs while accessing the mapped region. errCookie is passed back to errHandler as a first parameter (see section VME error Handling) .
On success, K_OK is returned and appropriate services are returned in the memOps parameter. An identifier for the mapped memory region is also returned in memId.
The identifier memId must be used as the first parameter to further calls to the VmeMemOps services, and to unmap the region when no longer needed.
On failure, VmeBusOps.mem_map returns an error code as follows:
A size of zero was specified.
Invalid or unsupported memory mapping attributes .
Not enough virtual address space to map.
The system is out of memory.
errHandler is a handler in the device driver which is invoked by the VME bus driver when a VME bus error occurs while accessing the mapped region. errCookie is passed back to the errHandler as first parameter.
On success, K_OK is returned and appropriate services are returned in the memOps parameter. An identifier for the mapped memory region is also returned in memId.
This identifier must be used as a first parameter to unmap the region when it is no longer needed.
On failure, VmeBusOps.mem_map returns an error code as follows:
A size of zero has been specified.
Invalid or unsupported memory mapping attributes.
Not enough virtual address space to map.
The system is out of memory.
Once the region has been successfully mapped, the driver can retrieve its virtual address to access memory directly, using the VmeMemOps.virt_addr service, as follows:
void* (*virt_addr)(VmeMemId memId);
The memId argument is the region identifier returned by VmeBusOps.mem_map.
This means that the device driver may now access the device memory by dereferencing the returned pointer.
Note that in this mode, the driver is responsible for handling any endianning problems.
The VmeMemOps.tas_xx routine set reads a memory location, compares it to a given value, and then writes back to this location the bits that compared true, as follows:
uintxx_f (*tas_xx)(VmeMemId memId, VmeSize offset, uintxx_f enable, uintxx_f compare, uintxx_f set);
The size of each value and of each data transfer on the VME bus is specified by the _xx suffix.
The memId is the memory region identifier returned by VmeBusOps.mem_map.
The offset argument indicates the offset in bytes, from the beginning of the region, of the memory location to access.
The enable argument specifies a bit mask to select (enable) the bits to be modified, if compared true. Each bit set enables comparison and setting for this bit.
The compare argument specifies a value which is bitwise compared to the read value.
The set argument specifies a value to be written back to the memory location for valid compared and enabled bits.
The memory location read value is bitwise compared with the compare value and the enable value. Each enabled bit that compares true is replaced by the corresponding bit in the set value. A false comparison results in the original bit being unchanged.
The original value read from the memory location is returned by the functions.
Assuming that readVal is the original value read and writeVal is the new value to write back, the following code illustrates the behavior of the tas_xx services:
/* read value into readVal */ replaced = ~(readVal ^ compare) & enable; writeVal = (set & replaced) | (readVal & ~replaced); /* write back writeVal */ return readVal;
When a driver no longer requires access to the device memory, or before closing the connection to the VME bus, it should unmap the memory region.
The VmeBusOps.mem_unmap is used to unmap a memory region, previously mapped with VmeBusOps.mem_map, and to release any system resources allocated for this mapping:
void (*mem_unmap)(VmeMemId memId);
The memId argument identifies the region to unmap.
Once the memory region is unmapped, the driver can no longer access it.
To perform Direct Memory Access to the memory of a VME device, a VME driver must:
Allocate a DMA region object in local space.
Use services defined by the DMA region to perform transfers.
Release the DMA region when no longer needed.
The VmeBusOps.dma_alloc service allocates a physical memory region of a specified size and maps it contiguously to the supervisor address space, as follows:
KnError (*dma_alloc)(VmeDevId devId, VmeSize size, VmeDmaAlign dmaConstr, VmeDmaAttr dmaAttr, uint32_f dmaMaxReq, VmeDmaHandler errHandler, void errCookie, VmeDmaRgnOps** dmaRgnOps, VmeDmaRgnId* dmaRgnId);
The devId argument is the identifier returned by VmeBusOps.open.
The size argument is the requested size in bytes to allocate for the DMA memory region.
The dmaConstr argument defines the address decoder constraints for the DMA memory region being allocated. Note that the dmaConstr argument may be set to NULL, specifying that there are no constraints on the DMA region. Otherwise, it should point to a VmeDmaAlign structure shown below:
typedef struct VmeDmaAlign { VmeAddr addr; VmeAddr alignment; VmeAddr floating; } VmeDmaAlign;
The addr field specifies the VME start address of the DMA region.
The alignment field is a mask which specifies constraints on the start address of the DMA region being allocated. A bit set within alignment specifies that the corresponding bit within the start address may take any value, that is, there are no specific constraints on that bit. A bit cleared within alignment specifies that the corresponding bit within the start address must take the same value as the corresponding bit of the addr field. This mask allows the caller to specify an alignment for the start address of the allocated DMA region by zeroing the required number of least significant bits. By resetting the required number of most significant bits, the caller may also indicate in which part of the VME space the memory must be allocated.
The floating field is a mask which indicates which bits of the returned address can vary while searching the allocated DMA region for the required size. In other words, bits cleared in the mask must be constant for all addresses in the range of the allocated DMA region. This mask may be used to specify that the allocated amount of memory must not span across a given address boundary.
The dmaAttr argument specifies the DMA transfer type. A combination of the following flags is allowed:
The region is used for DMA read transfer.
The region is used for DMA write transfer.
Allocate a synchronous DMA region for which no synchronization is needed between accesses from a DMA engine and the CPU. This attribute may not be supported on certain platforms.In that case, dma_alloc should return K_EINVAL.
The memAttr argument specifies the mapping attributes (see section Mapping device memory).
The dmaMaxReq argument indicates the maximum number of simultaneous transfers that may be further requested by the device driver for that region. The VME bus driver ensures that enough resources are reserved to handle at least dmaMaxReq simultaneous transfer requests on that region. As these resources are reserved on a per region basis, that is, on a per driver basis, this allows the DMA engine to be shared between drivers.
errHandler is a handler in the device driver. It is invoked by the VME bus driver when a VME bus error occurs whilst accessing to the DMA region. errCookie is passed back to the errHandler as a first argument. (see section VME error Handling) .
On success, K_OK is returned and appropriate services are returned in the dmaRgnOps parameter. An identifier for the mapped I/O region is also returned in dmaRgnId. This identifier must be used as first parameter to further calls to the VmeDmaOps services.
On failure, VmeBusOps.dma_alloc returns an error code as follows:
A size of zero was specified.
Invalid or unsupported DMA attributes.
Not enough virtual address space to map.
The system is out of memory.
The VmeDmaRgn.virt_addr routine returns the virtual start address of a DMA region, given the identifier of the region (dmaId):
void* (*virt_addr)(VmeDmaRgnId dmaRgnId);
The driver uses this address to access the DMA region. Note that the driver should synchronize the region as appropriate, depending on the attributes used when allocating the region.
If the DMA region was not allocated using the VME_DMA_SYNC attribute, the device driver should synchronize accesses to the same physical memory (the DMA region) from the CPU and the DMA engine correctly. Depending on the platform, these routines handle possible problems of cache flushing, DMA engine buffers, and others.
VmeDmaRgnOps.read_sync is a barrier between CPU writes and DMA reads from a given DMA sub-region:
void (*read_sync)(VmeDmaRgnId dmaRgnId, VmeSize offset, VmeSize size);
VmeDmaRgnOps.write_sync is a barrier between DMA writes and CPU reads from a given DMA sub-region:
void (*write_sync)(VmeDmaRgnId dmaRgnId, VmeSize offset, VmeSize size);
The dmaRgnId argument identifies the DMA region.
offset is the offset in bytes from the beginning of the DMA memory region.
offset and size both define the sub-region to synchronize.
The VmeDmaRgnOps.read function requests that a DMA transfer reads from the local memory region identified by srcDmaRgnId and writes to the mapped VME memory region identified by dstMemId:
KnError (*read)(VmeDmaRgnId srcDmaRgnId, VmeSize srcDmaRgnOff, VmeMemId dstMemId, VmePropMemRgn* dstMemRgn, VmeSize dstMemOff, VmeSize size, VmeDmaHandler dmaHandler, void* dmaCookie, VmeDmaReqId* dmaReqId);
The portion of the local memory region to read is defined by:
Identifier of the DMA region
Offset in bytes from the beginning of the region
Number of bytes to read starting from srcDmaRgnOff
The portion of the VME memory region to which to write is defined by:
Points to a VmePropMemRgn structure defining the VME region to write to.
Offset in bytes from the beginning of this region
Number of bytes to write starting at dstMemOff
Before calling VmeDmaRgnOps.read the VME driver should call VmeDmaRgnOps.read_sync to synchronize the DMA region, if it was not allocated using the VME_DMA_SYNC attribute.
The VmeDmaRgnOps.write function requests a DMA transfer to write to a local memory region identified by dstDmaRgnId and to read from the mapped VME memory region identified by srcMemId:
KnError (*write)(VmeDmaRgnId dstDmaRgnId, VmeSize dstDmaRgnOff, VmeMemId srcMemId, VmePropMemRgn* srcMemRgn VmeSize srcMemOff, VmeSize size, VmeDmaHandler dmaHandler, void* dmaCookie, VmeDmaReqId* dmaReqId);
The portion of the local memory region to write to is defined by:
Identifier of the DMA region
Offset in bytes from the beginning of the region
Number of bytes to write starting at dstDmaRgnOff
The portion of the VME memory region to read is defined by:
Points to a VmeBusOps.mem_map structure defininf the VME bue memory region to map to (see section Mapping Device Memory).
Offset in bytes from the beginning of this region
Number of bytes to read starting from srcMemOff
After a transfer requested using VmeDmaRgnOps.write is completed, the VME driver should call VmeDmaRgnOps.write_sync to synchronize the DMA region, if it was not allocated with the VME_DMA_SYNC attribute.
For both read and write routines:
On success, an identifier of the request is returned in dmaReqId that may be used to abort the transfer before it completes, using VmeDmaRgnOps.abort:
void (*abort)(VmeDmaReqId req);
On failure, K_ETOOMUCH is returned, to indicate that the maximum number of simultaneous transfer requests for that region has been reached. In that case, the driver should wait for the completion of the first pending request. That means, the driver should not call read or write routines before the dmaHandler for that region has been called back. The maximum number of simultaneous transfer requests that is accepted for a given DMA region is defined by the maxDmaReq argument of the VmeBusOps.dma_alloc service.
The dmaHandler argument is a handler in the device driver which is invoked by the VME bus driver when a DMA transfer has been completed.
This handler is a standard VmeErrHandler handler (see section VME Error Handling). However, if the request completed successfully, the VmeError pointer is set to NULL to indicate that no error occurred.
The dmaCookie is passed back to the dmaHandler as a parameter.
To ensure that the memory exported to other masters can be accessed through all possible VME cycle types, without alignment or other problems, only DMA memory regions can be exported.
This means that only memory regions allocated using VmeBusOps.dma_alloc can be made available for access by other masters on the bus.
Other masters may be of 2 kinds:
Devices which have their own DMA engine, and which need a VME address on the bus to transfer data to the memory of a CPU module
Other CPU modules, which need to access local memory to implement a communication protocol over a VME bus, either through programmed accesses, or through DMA (which in fact leads back to the first case).
In either case, the VmeDmaRgnOps.map service defined for an allocated DMA memory region must be used to export memory:
KnError (*map)(VmeDmaRgnId dmaRgnId, VmeSize dmaRgnOff, VmePropMemRgn* memRgn, VmeDmaMapId* dmaMapId);
The dmaRgnId argument identifies the allocated DMA region.
The memRgn argument points to a VmePropMemRgn structure, defining the VME bus memory region that should be responded to (see section Mapping device memory).
The dmaRgnOff is the offset in bytes from the beginning of the DMA region. Together with the size parameter, they define the local memory address range exported to the VME bus.
On success, K_OK is returned and an identifier for the DMA mapped region is returned in dmaMapId. This identifier must be used to unmap the region using VmeDmaRgnOps.unmap.
On failure, VmeDmaOps.map returns one of the following error codes::
the VME bus driver does not have enough resources to map the required DMA sub-region with the required attributes
the system is out of memory.
void (*unmap)(VmeDmaMapId dmaMapId);
dmaMapId is the identifier for the DMA sub-region previously mapped to the VME bus (using VmeDmaRgnOps.map).
When a driver no longer needs an allocated DMA region, or before closing the connection to the VME bus, it should release the DMA region.
The VmeBusOps.dma_free is used to release a DMA region, previously allocated with VmeBusOps.dma_alloc:
void (*dma_free)(VmeDmaRgnId dmaRgnId);
The dmaRgnId argument identifies the region to free.
When the VmeBusOps.dma_free function is called, the driver may no longer use the identifier of the DMA region (dmaRgnId).
To connect a handler to a VME interrupt request, a VME driver should:
Disable the interrupt at the device level, typically by accessing the device registers.
Attach a handler to the interrupt.
Enable the interrupt at the device level.
Use services defined by the attached interrupt object.
Disable the interrupt at the device level.
Detach the interrupt handler when no longer needed.
A VME device driver can connect a handler to a VME interrupt source by calling the VmeBusOps.intr_attach service:
KnError (*intr_attach)(VmeDevId devId, VmePropIntr* intr, VmeIntrHandler intrHandler, void* intrCookie, VmeIntrOps** intrOps, VmeIntrId* intrId);
The devId argument identifies the connection with the VME bus driver.
The intr argument indicates the VME interrupt source to which the handler will be connected. Available VME interrupt sources are VME bus interrupt requests 1 through 7. Typically, a VME device driver should find the interrupt to attach to by looking up the "intr" property in the device node.
The intrHandler argument is a handler in the device driver which is invoked by the VME bus driver when the corresponding interrupt request is triggered by a module on the bus:
typedef VmeIntrStatus (*VmeIntrHandler)(void* intrCookie, VmeIntrVector vector);
The intrCookie is passed back to this interrupt handler as first parameter. The second argument of this handler (vector) is the STATUS/ID value read on the bus during the interrupt acknowledge cycle. This type of VME interrupt handler must return a VmeIntrStatus value which indicates to the bus driver whether the interrupt was claimed by the handler, and how it was handled:
Must be returned by the interrupt handler if there is no pending interrupt for the device.
Must be returned by the interrupt handler if a pending device interrupt has been serviced by the interrupt handler and the interrupt has not been enabled (acknowledged) at VME bus level.
Must be returned by the interrupt handler if a pending device interrupt has been serviced by the interrupt handler and the interrupt has been enabled (acknowledged) at VME bus level.
On success, K_OK is returned and services defined on an attached interrupt object are returned in the intrOps parameter. An identifier for the attached interrupt is also returned in intrId. This identifier must be used as first parameter to further calls to the VmeIntrOps services.
A given VME bus interrupt request may be shared by multiple VME drivers on the same CPU module. Note that due to the VME bus design, the same interrupt request can not be handled on different CPU modules.
That means multiple drivers may attach a handler to the same VME interrupt request. In that case, when this type of interrupt request occurs on the bus, all the attached handlers are called in an order that is not defined and is implementation specific. When called, each handler can test the vector argument to check whether the request was triggered by the device it manages.
As an interrupt request is shareable, a driver must treat the interrupt it attaches to as being enabled at the bus level, and that it may occur as soon as the attachement is done. The fact that a driver could already have attached a handler to the interrupt, and enabled it, dictates this behavior.
If this is not acceptable for a given driver, it must disable the interrupt at the device level prior to attaching the handler. This ensures that the interrupt will not be called with the expected vector value until the interrupt is enabled again at the device level.
On failure, VmeBusOps.intr_attach returns one of the following error codes:
specified intr is invalid
not enough system memory
void (*mask) (VmeIntrId intrId);
Note that VmeIntrOps.mask does not guarantee that all other interrupt sources are still unmasked.
The VmeIntrOps.unmask service routine unmasks the interrupt source previously masked by VmeIntrOps.mask:
void (*unmask) (VmeIntrId intrId);
Note that VmeIntrOps.unmask does not guarantee that the interrupt source is unmasked immediately. The real interrupt source unmasking may be deferred.
The VmeIntrOps.mask/VmeIntrOps.unmask pair may be used at either base or interrupt level. Note that the VmeIntrOps.mask/VmeIntrOps.unmask pairs must not be nested.
The VmeIntrOps.enable and VmeIntrOps.disable service routines are dedicated to interrupt handler usage only. In other words, these routines may be called only by an interrupt handler.
The VmeIntrOps.enable service routine enables (and acknowledges) the bus interrupt source specified by intrId:
VmeIntrStatus (*enable) (VmeIntrId intrId);
VmeIntrOps.enable returns either VME_INTR_ACKNOWLEDGED or VME_INTR_CLAIMED. The VME_INTR_ACKNOWLEDGED return value means that the VME bus driver has enabled (and acknowledged) the interrupt at bus level. The VME_INTR_CLAIMED return value means that the VME bus driver has ignored the enable request and therefore the interrupt source is still disabled (and not acknowledged) at bus level.
Note that the bus driver typically refuses an explicit interrupt acknowledgement (issued by an interrupt handler) for the shared interrupts. In the latter case, the bus driver will acknowledge interrupts only when all interrupt handlers have been called.
Note that in cases where the VmeIntrOps.enable routine was called by an interrupt handler, the handler must return the value which was returned by VmeIntrOps.enable. Note also that once VmeIntrOps.enable has been called, the driver should be able to handle an immediate re-entrance into the interrupt handler code.
The VmeIntrOps.disable service routine disables the interrupt source previously enabled by VmeIntrOps.enable:
void (*disable) (VmeIntrId intrId);
If VmeIntrOps.enable returns VME_INTR_ACKNOWLEDGED, the driver must call VmeIntrOps.disable prior to returning from the interrupt handler.
When an interrupt occurs, the attached VmeIntrHandler is invoked with the interrupt source disabled at bus level. This behaves in exactly the same way as if VmeIntrOps.disable was called just prior to the handler invocation. Note that the interrupt handler must return to the bus driver in the same context as it was called, that is, with the interrupt source disabled at bus level.
On the other hand, the called interrupt handler may use the VmeIntrOps.enable/disable pair to allow the interrupt to be nested. This feature is typically used by a VME-to-bus bridge driver when the secondary bus interrupts are multiplexed, that is, multiple secondary bus interrupts are reported through the same primary bus interrupt. Typically, an interrupt handler of a VME-to-bus bridge driver would take the following actions:
Identify and disable the secondary bus interrupt source
Enable the primary bus interrupt source (enable)
Call handlers attached to the secondary bus interrupt source
Disable the primary bus interrupt source (disable)
Acknowledge (if needed) and enable the secondary bus interrupt source
Return from the interrupt handler
When a driver no longer needs to handle an interrupt, or before closing the connection to the VME bus, it should detach the handler attached to an interrupt source.
The VmeBusOps.intr_detach is used to detach a handler, previously attached with VmeBusOps.intr_attach, and to release any resources allocated for this attachment:
void (*intr_detach)(VmeIntrId intrId);
The intrId argument identifies the attachment to release.
When the VmeBusOps.intr_detach function is called, the driver is no longer allowed to use the identifier (intrId).
A VME driver may need to trigger (via software) an interrupt request on the VME bus, to implement a protocol between CPU modules, for example.
This can be done using the VmeBusOps.intr_trigger service:
KnError intr_trigger (VmeDevId devId, VmePropIntr intr, VmeIntrVector vector, VmeIntrAckHandler iackHandler, void* iackCookie);
The intr argument indicates which VME interrupt request to trigger on the bus.
The vector argument indicates which STATUS/ID to deliver on the bus during the interrupt acknowledge cycle.
The iackHandler argument is a handler in the device driver. It is invoked by the VME bus driver once the STATUS/ID has been provided to an interrupt handler on the VME bus:
typedef void (*VmeIntrAckHandler) (void* iackCookie);
The iackCookie is passed back to this handler as the first argument.
On success, intr_trigger returns K_OK, and the iackHandler handler is called once the triggered interrupt has been handled by an interrupt handler VME module.
On failure, VmeBusOps.intr_trigger returns one of the following error codes:
specified intr is invalid
a previously triggered interrupt has not yet been handled
When mapping VME bus space, a VME driver provides an error handler that is invoked by the VME bus driver when a bus error occurs.
Error handlers are used for the following services:
VmeBusOps.io_map
VmeBusOps.mem_map
VmeBusOps.dma_alloc
VmeDmaOps.read
VmeDmaOps.write
When a programmed, or direct, memory access is aborted because of a bus error, the VME bus invokes the error handler for the VME address and space concerned.
A VmeErrHandler is defined as follow:
typedef void (*VmeErrHandler)(void* errCookie, VmeError* err);
The errCookie is an opaque value that is given by the caller and passed back when the handler is called.
The err argument points to a structure describing the error as follows:
typedef struct VmeError { VmeErrCode code; VmeSize offset; } VmeError;
The code field indicates the type of error that occurred.
The offset field indicates the offset, within the associated region, at which the error occurred.
The error code can be as follows:
Unknown error, the VME bus driver is unable to determine the reason for the exception.
Invalid (not supported) access granularity has been used.
A parity error occurred during a transfer.
A VME bus error (BERR) occurred. The offset indicates the first address in the region at which a BERR occurred.
A DMA transfer has been aborted by the driver before it had completed. The offset indicates the address in the region where the transfer was stopped.
As a bus error may be reported in the context of an exception or an interrupt, the implementation of the error handler must be restricted to the interrupt level API.
To check whether a given VME bus controller is the VME system controller, a VME device driver should use VmeBusOps.is_sys_con:
Bool is_sys_con (VmeDevId devId);
The devId argument identifies the connection with the VME bus driver.
VmeBusOps.is_sys_con returns TRUE if the VME bus controller is the VME bus system controller. Otherwise, the routine returns FALSE.
The VmeBusOps.lock/unlock services allow a driver to implement critical sections, by ensuring mastership of the VME bus between a call to lock and a call to unlock.
void (*lock) (VmeDevId devId); void (*unlock) (VmeDevId devId);
The lock command requires and holds mastership of the VME bus until unlock is called. This allows a CPU module on the VME bus to run multiple cycles on the bus without losing mastership of the bus. This means that no other master can access the bus until unlock is called.
The table below lists the VME specific node properties. The alias column specifies the alias name which should be used by a VME bus or device driver referencing the property name. The name column specifies the property name ASCII string. The value column specifies the type of the property value. The bus column specifies properties specific to the VME bus node. The dev column specifies properties specific to the VME device node. The m in the bus/dev columns flags mandatory properties. The o in the bus/dev columns flags optional properties. The - symbol in the bus/dev columns flags properties which are not applied to a given node type.
alias |
name |
value |
bus |
dev |
---|---|---|---|---|
VME_PROP_INTR |
"intr" |
VmePropIntr[] |
- |
o |
VME_PROP_IO_REGS |
"io-regs" |
VmePropIoRegs[] |
- |
o |
VME_PROP_MEM_RGN |
"mem-rgn" |
VmePropMemRgn[] |
- |
o |
VME_PROP_DMA_BURST |
"dma-burst" |
VmePropDmaBurst |
m |
- |
VME_PROP_DMA_OFF_TIMER |
"dma-off" |
VmePropDmaOffTimer |
o |
- |
VME_PROP_DMA_MIN_SIZE |
"dma-min-size" |
VmePropDmaMinSize |
m |
- |
VME_PROP_BYTE_ORDER |
"byte-order" |
VmePropByteOrder |
m |
- |
VME_PROP_CLOCK_FREQ |
"clock-freq" |
VmePropClockFreq |
o |
- |
VME_PROP_REL_MODE |
"rel-mode" |
VmePropRelMode |
o |
- |
VME_PROP_REQ_MODE |
"req-mode" |
VmePropReqMode |
o |
- |
VME_PROP_REQ_LEVEL |
"req-level" |
VmePropReqLevel |
o |
- |
VME_PROP_ARB_MODE |
"arb-mode" |
VmePropArbMode |
o |
- |
VME_PROP_ARB_TIMEOUT |
"arb-timeout" |
VmePropArbTimeout |
o |
- |
VME_PROP_BUS_TIMEOUT |
"bus-timeout" |
VmePropBusTimeout |
o |
- |
When the value of a property is an array (for example, VmePropIntr[]). The size of this array (that is, the size of the array is the size of the property value returned by dtreePropValue) divided by the size of its element type (for example, sizeof(VmePropIntr)), defines the number of elements in the array. A VME device driver may then iterate through the array in order to perform an action for each element (for example, to attach an interrupt handler to each device interrupt line).
The VME_PROP_INTR property of a VME device node indicates the VME interrupt sources required/allocated for this device, and whether these interrupts may be shared or are exclusive to that device. This property value should be used by a driver for this device to attach an interrupt handler to each device interrupt (VmeBusOps.intr_attach).
The VME_PROP_IO_REGS property of a VME device node defines the VME I/O regions required/allocated for this device. This property value should be used by a driver for this device to map each I/O region for further access to the device (VmeBusOps.io_map).
The VME_PROP_MEM_RGN property of a VME device node defines the VME memory regions required/allocated for this device. This property value should be used by a driver for this device to map each memory region for further access to the device (VmeBusOps.mem_map).
Specific properties are used to configure the behavior of the VME bus controller. These properties are defined only for a VME bus node/nexus. Depending on the underlying controller, one or another feature described below may or may not be implemented. In cases where a feature is not implemented, the properties will not exist. On the other hand, a controller may allow for specific tuning not listed here. Thus, users should refer to the VME bus driver specific documentation to get the exact list of available properties.
Release On Request (ROR)
Release When Done (RWD)
Release On BCLR (ROC)
The Default setting is RWD
BR0
BR1
BR2
BR3 (highest priority)
The default setting is BR3
Demand: requester asserts its bus request regardless of the state of BRn.
Fair: requester does not request the bus until there are no other requests pending at its level.
The default setting is Demand
Note that these options are used only when the module is a system controller.
Fixed priority (PRI) (BR3...BR0)
Single Level (SGL) bus is granted permission only to request at level BR3
Round Robin Select (RRS)
The Default setting is RRS
Value in micro-seconds
The Default setting is 16us
Value in micro-seconds
The Default setting is 64us
Value in bytes
Defines how much data the DMA will transfer before giving the opportunity to another master to assume ownership of the bus. 0 means "until transfer completion".
Value in micro-seconds
Defines the minimum period the DMA is off the VME bus between tenures. 0 means the bus is immediatly re-requested.
The VME bus driver may support dynamic allocation on the VME bus resources. In cases where dynamic resource allocation is supported, a VME device driver may use the VmeBusOps.resource_alloc service routine in order to allocate a bus resource at run time:
KnError (*resource_alloc) (VmeDevId devId, DevProperty prop);
devId is returned by VmeBusOps.open.
prop specifies the bus resource being allocated.
The VmeBusOps.resource_alloc service routine allocates a given bus resource and, if the allocation request is satisfied, updates the device node properties in order to add the newly allocated bus resource.
On success, VmeBusOps.resource_alloc returns K_OK.
On failure, one of the following error codes is returned:
Dynamic resource allocation is not supported by the bus driver.
The property name is unknown.
The property value is invalid.
The bus resource is not available.
The system is out of memory.
When a dynamically allocated bus resource is no longer used, a VME device driver may release it by calling the VmeBusOps.resource_free service routine:
void (*resource_free) (VmeDevId devId, DevProperty prop);
devId is returned by VmeBusOps.open.
prop specifies the bus resource being released.
The VmeBusOps.resource_free service routine releases a given bus resource and updates the device node properties in order to remove the bus resource being released.
Note that the VmeBusOps.resource_alloc and VmeBusOps.resource_free routines should not be used by a simple device driver. This type of driver should opearate as though all needed resources have already been allocated and specified as properties in the device node by the bus driver. The driver should simply find the property and call an appropriate service routine, passing a pointer to the property value.
However, it is not always possible to determine all needed resources prior to device initialization. A typical case is a bus-to-bus bridge driver which can discover devices residing on the secondary bus only once the bus bridge hardware has already been initialized. In this case, when drv_init is called, the bus-to-bus bridge node would contain only resources needed for the bus-to-bus bridge device itself (for example, internal bus-to-bus bridge registers). Once devices residing on the secondary bus are discovered, the bus-to-bus bridge driver would request additional primary bus resources in order to satisfy the resource requirements for these devices. Another example is a bus which supports hot-pluggable devices. On this type of bus, the primary bus resources allocated by the hot-pluggable bus driver depend on devices currently plugged into the secondary bus. Usually, the resource requirements are changed when a hot-plug insertion/removal occurs.
Note that dynamic resource allocation might be also used by a device driver which implements lazy resource allocation. In this type of driver, the bus resource allocation might be performed at open time using VmeBusOps.resource_alloc. Then, the dynamically allocated resources might be released at close time using VmeBusOps.resource_free.
The table below specifies the contexts in which a caller is allowed to invoke each service.
Services |
Base level |
DKI Thread |
Interrupt |
Blocking |
---|---|---|---|---|
VmeBusOps.open |
- |
+ |
- |
+ |
VmeBusOps.close |
- |
+ |
- |
+ |
VmeBusOps.intr_attach |
- |
+ |
- |
+ |
VmeBusOps.intr_detach |
- |
+ |
- |
+ |
VmeBusOps.intr_trigger |
+ |
+ |
+ |
- |
VmeBusOps.io_map |
- |
+ |
- |
+ |
VmeBusOps.io_unmap |
- |
+ |
- |
+ |
VmeBusOps.dma_alloc |
- |
+ |
- |
+ |
VmeBusOps.dma_free |
- |
+ |
- |
+ |
VmeBusOps.mem_map |
- |
+ |
- |
+ |
VmeBusOps.mem_unmap |
- |
+ |
- |
+ |
VmeBusOps.resource_alloc |
- |
+ |
- |
+ |
VmeBusOps.resource_free |
- |
+ |
- |
+ |
VmeBusOps.is_sys_con |
+ |
+ |
+ |
- |
VmeBusOps.lock |
+ |
+ |
+ |
- |
VmeBusOps.unlock |
+ |
+ |
+ |
- |
VmeBusOps.cpuId |
+ |
+ |
+ |
- |
VmeIoOps.load_xx |
+ |
+ |
+ |
- |
VmeIoOps.store_xx |
+ |
+ |
+ |
- |
VmeIoOps.read_xx |
+ |
+ |
+ |
- |
VmeIoOps.write_xx |
+ |
+ |
+ |
- |
VmeIoOps.tas_xx |
+ |
+ |
+ |
- |
VmeMemOps.virt_addr |
+ |
+ |
+ |
- |
VmeMemOps.tas_xx |
+ |
+ |
+ |
- |
VmeDmaRgnOps.virt_addr |
+ |
+ |
+ |
- |
VmeDmaRgnOps.read_sync |
+ |
+ |
+ |
- |
VmeDmaRgnOps.write_sync |
+ |
+ |
+ |
- |
VmeDmaRgnOps.read |
+ |
+ |
+ |
- |
VmeDmaRgnOps.write |
+ |
+ |
+ |
- |
VmeDmaRgnOps.abort |
+ |
+ |
+ |
- |
VmeDmaRgnOps.map |
- |
+ |
- |
+ |
VmeDmaRgnOps.unmap |
- |
+ |
- |
+ |
VmeIntrOps.mask |
+ |
+ |
+ |
- |
VmeIntrOps.unmask |
+ |
+ |
+ |
- |
VmeIntrOps.enable |
- |
- |
+ |
- |
VmeIntrOps.disable |
- |
- |
+ |
- |
See attributes(5) for descriptions of the following attributes:
ATTRIBUTE TYPE | ATTRIBUTE VALUE |
---|---|
Interface Stability | Evolving |
NAME | SYNOPSIS | API RESTRICTIONS | FEATURES | DESCRIPTION | EXTENDED DESCRIPTION | ALLOWED CALLING CONTEXTS | ATTRIBUTES