NAME | SYNOPSIS | FEATURES | DESCRIPTION | EXTENDED DESCRIPTION | ATTRIBUTES | SEE ALSO
#include <ddi/quicc/quicc.h>
DDI
The QUICC bus driver offers an API for QUICC device driver development.
The QUICC bus API is an abstraction of Motorola's QUICC micro-contollers' internal peripheral bus and external bus mechanisms.
The API caters for the following QUICC functionalities:
QUICC internal and external interrupt management
access to QUICC device I/O registers
access to QUICC parallel I/O port's pins
DMA management
management of QUICC shared resources (Baud Rate Generators, Communication processor's channel commands)
Initially, a QUICC device driver must register itself in the kernel driver registry. This should be done from its main function using svDriverRegister. The driver must set the bus class to "quicc" in its registry entry, and specify the lowest version number of the QUICC bus interface required to run correctly. This registration allows the kernel to call back the QUICC 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 QUICC device driver may establish a connection with the parent QUICC bus driver, as described below. Once such a connection is established, the QUICC device driver may use services provided by the QUICC bus driver. Note that a connection to the bus driver can only be established from within the driver's drv_probe or drv_init routine.
The drv_init routine is defined if the drv_init field of the driver registry entry is set to a non-zero value. If drv_init is defined, then the QUICC bus driver will invoke it at bus initialization time. Note that the drv_init routine is called in the DKI thread context which means that is it only possible to invoke the QUICC bus services allowed in the DKI thread context.
The drv_init routine is called with three arguments:
The QUICC device driver's own node, which identifies the node associated with the device in the device tree.
The QUICC bus operations structure, which defines the QUICC bus API and its version.
The QUICC bus identifier, which is an opaque value used to connect the device driver to the QUICC bus when calling the QuiccBusOps.open operation.
From this point, QuiccBusOps.open must be the first call issued by the QUICC device driver to the QUICC bus driver:
KnError (*open) (QuiccId quiccId, DevNode devNode, QuiccEventHandler eventHandler, QuiccLoadHandler loadHandler, void* cookie, QuiccDevId* devId);
The above function establishes a connection between the device and the bus. The device is identified by the devId value, returned as the last argument. The devId identifier is used for many other services defined in the QuiccBusOps structure.
The quiccId and devNode fields are given by the QUICC bus driver as parameters to the driver's drv_init routine.
eventHandler is a handler in the device driver which is invoked by the QUICC bus driver when a QUICC bus event occurs. Note that eventHandler 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 a first argument.
This QUICC bus event handler takes two additionnal parameters:
the bus event type, which is 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 not notify clients nor release used 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 then it should return from the event handler. Once the device entry is released from 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 QuiccBusOps.close. Note that the QUICC_DEV_SHUTDOWN event may be used by a bus driver to confiscate (or to re-allocate) bus resources. The QUICC bus driver will invoke the driver's drv_init routine again once the bus resources are re-allocated for the device.
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 those for QUICC_DEV_SHUTDOWN, detailed above, except that the device hardware must not be accessed by the driver.
an event type specific structure pointer:
This argument is always NULL for QUICC bus events.
As the event handler may be 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 QUICC bus driver when a new driver appears in 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 a unique argument. Typically, a leaf QUICC device driver is not affected by such an event. loadHandler is usually used by a QUICC nexus driver (for example, a PCI/ISA bus bridge) when trying to apply a newly downloaded driver to its child devices which are not yet being serviced. Not that loadHandler is called in the DKI thread context.
On success, QuiccBusOps.open returns K_OK and a valid identifier is returned as the devId argument. This identifier must be used to call other services defined in the QuiccBusOps structure. Some of these services also return an "Ops" structure which defines the new services on a new object instance (for exampe, a memory region). Others perform a simple service for the device driver, without giving access to a subpart of the API.
On failure, QuiccBusOps.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 given device node.
The system is out of memory.
To release the connection with the QUICC bus, the driver must call the QuiccBusOps.close service:
void (*close) (QuiccDevId devId);
After being disconnected from the QUICC bus, the device driver may no longer use any of the QUICC bus API services.
The probe routine is defined if the drv_probe field of DrvRegEntry is set to a non-zero value. If the drv_probe routine is defined, then the QUICC bus driver invokes it at bus probing time. Note that the drv_probe routine is called in the context of a DKI thread which means that it is only possible to invoke the QUICC bus services allowed in a DKI thread context.
The drv_probe routine is called with three arguments:
The node associated with the parent QUICC bus in the device tree.
The QUICC bus operations structure, which defines the QUICC bus API and its version.
Tthe QUICC bus identifier, which is an opaque value used to connect the device driver to the QUICC bus when calling the QuiccBusOps.open operation.
The drv_probe routine is called first by the bus driver. That allows the QUICC device driver to discover a device (which can be serviced by the device 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 appropriate properties to the device node, specifying the driver's required bus resources (for example, "intr" and/or "mem-rgn"). By examining the node properties, the QUICC bus driver will allocate bus resources for the device (if needed) and/or will 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 allows the device driver to perform a driver-to-device binding. Typically, a QUICC device driver examines the "channel" device node property in order to check whether it can service the device. If the check is positive, the driver binds itself to the device. This binding is done by attaching the "driver" property to the device node. The "driver" property value is a NULL terminated ASCII string specifying the driver name. The driver name specified in the property must match the driver name specified in the driver registry entry. Under these conditions, the QUICC 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, or checked, by the bus driver. Note also, that the drv_bind routine is called in the context of a DKI thread which means that it is only possible to invoke the QUICC bus services allowed in a DKI thread context.
Devices residing on the QUICC bus can be discovered by using the QUICC bus services. To enable this, a connection must be established between the QUICC bus driver and the QUICC device driver. In order to establish such a connection, the device driver has to create a device node and attach it to the bus node. This device node may be temporary but it is mandatory as an argument to the QuiccBusOps.open routine. When the probing process is complete and the connection is closed, depending on the probing results, the device driver should either delete the temporary device node or change 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 QUICC bus is established by the drv_probe routine, it should be closed (via QuiccBusOps.close) before leaving the routine.
To perform I/O access to QUICC device registers, a QUICC driver must:
map the device's registers into an I/O region
use services defined by the mapped I/O region to access the registers
unmap the region when access is no longer needed
The QuiccBusOps.io_map service is used to map an I/O region of a QUICC device so that it is accessible:
KnError (*io_map)(QuiccDevId devId, QuiccPropIoRegs* ioRgn, QuiccErrHandler errHandler, void* errCookie, QuiccIoOps** ioOps, QuiccIoId* ioId)
The devId field is returned by QuiccBusOps.open.
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 (see the dtreePropFind, dtreePropLength, dtreePropValue manpages) prior to the call to QuiccBusOps.io_map.
The name of the property used to describe device I/O regions is "io-regs". Its value contains an array which stores the QuiccPropIoRegs structure, shown below:
typedef struct QuiccPropIoRegs { QuiccSpace space; QuiccAddr addr; QuiccSize size; QuiccAddr mask; } QuiccPropIoRegs;
The space field specifies the QUICC address space where the registers reside:
QUICC internal space (internal memory space).
QUICC external space (bus space).
The addr field specifies the QUICC 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 and which are floating. This field is used by the QUICC bus driver at the resource allocation stage in order to determine the device address decoder constraints. A bit set to zero within mask specifies a fixed address bit within addr. In other words, a bit within an allocated address must correspond to the same bit within addr. A bit set to one within mask specifies a floating address bit, that is, any value of the corresponding bit within an allocated address is acceptable. When the QuiccPropIoRegs structure is passed to QuiccBusOps.io_map the I/O register range is already allocated and therefore the mask field is meaningless.
errHandler is a handler in the device driver which is invoked by the QUICC bus driver when a QUICC bus error occurs while the driver is accessing the mapped region. errCookie is passed back to the errHandler as a first parameter.
On failure, QuiccBusOps.io_map will return one of the following error codes:
A size of zero was specified.
Invalid, or not supported, QUICC address space.
Not enough virtual address space to map I/O registers.
The system is out of memory.
Once the region is successfully mapped, the driver can use the services defined by the QuiccIoOps structure.
The QUICC bus provides four service routine sets to access a mapped I/O region:
QuiccIoOps.load_8/16/32
QuiccIoOps.store_8/16/32
QuiccIoOps.read_8/16/32
QuiccIoOps.write_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 QUICC bus.
In all service routines provided by QuiccIoOps the ioId argument identifies the mapped I/O region.
The offset argument specifies the register offset, in bytes, within the mapped region.
All these routines handle byte swapping in case the endian is different for the QUICC bus and the CPU.
uintxx_f (load_xx)(QuiccIoId ioId, QuiccSize offset);
The size of the returned value and of the data transfer on the QUICC bus is specified by the _xx suffix.
void (*store_xx)(QuiccIoId ioId, QuiccSize offset, uintxx_f value);
The size of the given value and of the data transfer on the QUICC bus is specified by the _xx suffix.
The QuiccIoOps.read_xx routine set loads values from a device register and sequentially writes them into a memory buffer:
void (*read_xx)(QuiccIoId ioId, QuiccSize offset, uintxx_f* buf, QuiccSize count);
The size of each value loaded, and of each data transfer on the QUICC 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 the value of count multiplied by the size of each data transfer.
The QuiccIoOps.write_xx routine set sequentially reads values from a memory buffer and stores them in a device register:
void (*write_xx)(QuiccIoId ioId, QuiccSize offset, uintxx_f* buf, QuiccSize count);
The size of each value stored, and of each data transfer on the QUICC 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 the value of count multiplied by the size of each data transfer.
When a driver no longer needs access to the device I/O registers, or before closing the connection to the QUICC bus, it should unmap the I/O region used for these devices.
QuiccBusOps.io_unmap is used to unmap an I/O region previously mapped with QuiccBusOps.io_map and to release any resources allocated for this mapping:
void (*io_unmap)(QuiccIoId ioId);
The ioId argument identifies the region to unmap.
When the QuiccBusOps.io_unmap function is called, the driver is no longer able to use any services defined for the unmapped I/O region.
To access the memory of a QUICC device, a QUICC 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.
The QuiccBusOps.mem_map service is used to map a memory region from a QUICC device, enabling access to this region:
KnError (*mem_map) (QuiccDevId devId, QuiccPropMemRgn* memRgn, QuiccMemAttr memAttr, QuiccErrHandler errHandler, void* errCookie, void** memAddr, QuiccMemId* memId);
devId is the identifier returned by QuiccBusOps.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. The property should be retrieved by the driver using the device tree (see the dtreePropFind, dtreePropLength, dtreePropValue manpages) prior to a call to QuiccBusOps.mem_map .
The name of the property used to describe device memory regions is "mem-rgn". Its value contains an array of QuiccPropMemRgn structures shown below:
typedef struct QuiccPropMemRgn { QuiccSpace space; QuiccAddr addr; QuiccSize size; QuiccAddr mask; } QuiccPropMemRgn;
The space field specifies where the memory region resides within the QUICC memory address space. Either in:
QUICC internal space (internal memory space) , or
QUICC external space (bus spaces).
The addr field specifies the QUICC 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 memAttr 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
errHandler is a handler in the device driver which is invoked by the QUICC bus driver when a QUICC bus error occurs while the driver is accessing the mapped region. errCookie is passed back to the errHandler as a first argument. (see the section QUICC error Handling.)
On success, K_OK is returned and the start virtual address of the mapped memory region is returned in the memAddr argument. An identifier for the mapped memory region is also returned in memId. This identifier must be used to unmap the memory region.
On failure, QuiccBusOps.mem_map will return one of the following error codes:
A size of zero has been specified.
Invalid, or not supported, memory mapping attributes.
Not enough virtual address space to map.
The system is out of memory.
Once the region is successfully mapped, the device driver may access the device memory by de-referencing the returned pointer. Note that in this mode, the driver must handle all problems of endianning.
When a driver does not require access to the device memory, or before closing the connection to the QUICC bus, it should unmap the memory region.
The QuiccBusOps.mem_unmap is used to unmap a memory region (previously mapped with QuiccBusOps.mem_map) and to release any system resources used for this mapping:
void (*mem_unmap) (QuiccMemId memId);
The memId argument identifies the memory region to unmap.
Once the memory region is unmapped, the driver may no longer access that region.
To provide DMA to the system memory, a QUICC device driver must:
allocate a DMA region
use services defined by the DMA region object to retrieve the DMA region properties (the virtual and QUICC addresses)
program the device DMA engine to perform a DMA transfer
release the DMA region when no longer needed
The QuiccBusOps.dma_alloc service allocates a memory region of a specified size and contiguously maps it to the supervisor address space:
KnError (*dma_alloc) (QuiccDevId devId, QuiccSize size, QuiccDmaAlign* dmaConstr, /* NULL if no constraints */ QuiccDmaAttr dmaAttr, QuiccMemAttr memAttr, QuiccErrHandler errHandler, void* errCookie, QuiccDmaOps** dmaOps, QuiccDmaId* dmaId);
The devId argument is the identifier returned by QuiccBusOps.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 being allocated. Otherwise, it should point to a QuiccDmaAlign structure, shown below:
typedef struct QuiccDmaAlign { QuiccAddr addr; QuiccAddr alignment; QuiccAddr floating; } QuiccDmaAlign;
The addr field specifies the QUICC 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 QUICC 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. Bits cleared in the mask must be constant for all addresses in the allocated DMA region range. 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:
Region used for DMA read transfer.
Region used for DMA write transfer.
Allocate a synchronous DMA region for which no synchronization is needed for access to the DMA engine and the CPU. This attribute may not be supported on a given platform. In this case, QuiccBusOps.dma_alloc returns K_EINVAL.
Allocate a DMA region in Dual Ported RAM, rather than in system memory.
The memAttr argument specifies the mapping attributes (see section Mapping Device Memory).
errHandler is a handler in the device driver, which is invoked by the QUICC bus driver when a QUICC bus error occurs while accessing the DMA region. errCookie is passed back to the errHandler as a first argument. (See section QUICC Error Handling.)
On success, K_OK is returned and appropriate services are returned in the dmaOps argument. An identifier for the DMA region is also returned in dmaId. This identifier must be used as a first argument to call the QuiccDmaOps services.
On failure, QuiccBusOps.dma_alloc will return one of the following errors:
A size of zero was specified.
Invalid, or not supported, memory mapping attributes.
Not enough virtual address space to map.
The system is out of memory.
The QuiccDmaOps.virt_addr routine returns the virtual start address of a given DMA region (specified by dmaId):
void* (*virt_addr) (QuiccDmaId dmaId);
The driver uses this address to access the DMA region using the CPU instructions.
The QuiccDmaOps.phys_addr routine returns the QUICC start address of a given DMA region (specified by dmaId):
QuiccAddr (*phys_addr) (QuiccDmaId dmaId);
The driver uses this address to program its DMA engine. (Some QUICC internal controllers need to be progammed with the offset of the DMA region within QUICC DPRAM. In such cases, the start address of the DPRAM should be substracted from the address returned by phys_addr. The start address of DPRAM is stored in the QUICC bus node "dpram-rgn" property.)
Note that the driver should synchronize the region depending on the attributes used when allocating the region (see section Synchronizing the DMA Region).
If the DMA region was not allocated using the QUICC_DMA_SYNC attribute, the device driver should correctly synchronize access for the CPU and the DMA engine to the same memory (the DMA region). Depending on the platform, these routines handle possible problems with cache coherency, DMA engine buffers, and others.
QuiccDmaOps.read_sync is a barrier between CPU writes and DMA reads from a given DMA sub-region:
void (*read_sync) (QuiccDmaId dmaId, QuiccSize offset, QuiccSize size);
QuiccDmaOps.write_sync is a barrier between DMA writes and CPU reads from a given DMA sub-region:
void (*write_sync) (QuiccDmaId dmaId, QuiccSize offset, QuiccSize size);
For both routines:
dmaId identifies the DMA region
offset is the offset, in bytes, from the beginning of the DMA memory region
offset and size define the sub-region to synchronize
When a driver no longer needs an allocated DMA region, or before closing the connection to the QUICC bus, it should release the DMA region.
The QuiccBusOps.dma_free is used to release a DMA region, previously allocated with QuiccBusOps.dma_alloc:
void (*dma_free) (QuiccDmaId dmaId);
The dmaId argument identifies the DMA region to be freed.
Once the DMA region is released, the driver is no longer able to access it.
To connect a handler to a QUICC interrupt source, a QUICC driver should:
disable the interrupt at the device level, usually by accessing the device registers
attach a handler to the interrupt
enable the interrupt at the device level
use services defined for the attached interrupt object
disable the interrupt at the device level
detach the interrupt handler when no longer needed
KnError (*intr\_attach) (QuiccDevId devId, QuiccPropIntr* devIntr, QuiccIntrHandler devIntrHandler, void* devIntrCookie, QuiccIntrOps** intrOps, QuiccIntrId* intrId);
The devId argument identifies the connection with the QUICC bus driver.
The intr argument indicates which QUICC interrupt source the handler will be connected to. Typically, a QUICC device driver should find the interrupt to attach to by looking at the "intr" property in the device node. The value of this type of property is a QuiccPropIntr structure, shown below:
typedef struct QuiccPropIntr { QuiccIntrSrc src; QuiccIntrMode mode; } QuiccPropIntr;
The src field specifies the QUICC interrupt source. The interrupt sources available are specific to each QUICC bus driver implementation, and depend on the underlying QUICC micro-controller.
The mode field specifies the QUICC interrupt mode:
QUICC interrupt source is level sensitive (triggered on level)
QUICC interrupt source is edge sensitive (triggered on edge)
The intrHandler argument is a handler in the device driver which is invoked by the QUICC bus driver when the corresponding interrupt occurs:
typedef QuiccIntrStatus (*QuiccIntrHandler) (void* intrCookie);
The intrCookie is passed back to the interrupt handler as an argument. The QUICC interrupt handler must return a QuiccIntrStatus value, which indicates to the bus driver whether the interrupt was claimed by the handler, and, if it was, how it was handled:
Must be returned by the interrupt handler if there is no pending interrupt for 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 QUICC bus level. (See section Enabling/Disabling a Serviced Interrupt.)
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 QUICC bus level. (See section Enabling/Disabling an Attached Interrupt.)
On success, K_OK is returned and services defined on an attached interrupt object are returned in the intrOps argument. An identifier for the attached interrupt is also returned in intrId. This identifier must be used as a first argument to call the QuiccIntrOps services.
The QUICC bus allows an interrupt source to be shared among multiple devices if the interrupt is level sensitive. Multiple drivers may attach a handler to the same QUICC interrupt line. In that case, when an interrupt occurs on the bus, all the attached handlers are called in the order they were attached. When called, each handler can test the device status to check whether the interrupt was triggered by the device it manages.
As an interrupt line is shareable, a driver must consider that the interrupt it attaches to is enabled at the bus level and that the interrupt may occur as soon as the attachment is complete. The fact that a driver could already have attached a handler to the interrupt, and enabled it, dictates this behaviour.
If this is not acceptable for a given driver, it must disable the interrupt at the device level, prior to attaching to the handler. This ensures that the interrupt will not occur, and that the handler will not be called until the interrupt is enabled again at the device level.
On failure, QuiccBusOps.intr_attach will return one of the following error messages:
specified intr is invalid
specified interrupt source is already configured differently than required in devIntr
specified edge sensitive interrupt source is already attached to another driver (edge sensitive interrupts are not shareable)
not enough system memory
void (*mask) (QuiccIntrId intrId);
Note that QuiccIntrOps.mask does not guarantee that all other interrupt sources are still unmasked.
The QuiccIntrOps.unmask service routine unmasks the interrupt source, previously masked by QuiccIntrOps.mask:
void (*unmask) (QuiccIntrId intrId);
Note that QuiccIntrOps.unmask does not guarantee that the interrupt source is unmasked immediately. The real interrupt source unmasking may be deferred.
The QuiccIntrOps.mask/QuiccIntrOps.unmask pair may be used at either base or interrupt level. Note that the pair must not be nested.
The QuiccIntrOps.enable and QuiccIntrOps.disable service routines are dedicated to interrupt handler usage only. In other words, these routines may be called only by an interrupt handler.
The QuiccIntrOps.enable service routine enables (and acknowledges) the interrupt source specified by intrId:
QuiccIntrStatus (*enable) (QuiccIntrId intrId);
QuiccIntrOps.enable returns either QUICC_INTR_ACKNOWLEDGED or QUICC_INTR_CLAIMED. The QUICC_INTR_ACKNOWLEDGED return value means that the QUICC bus driver has enabled (and acknowledged) the interrupt at bus level. The QUICC_INTR_CLAIMED return value means that the QUICC bus driver has ignored the request and therefore the interrupt source is still disabled (and not acknowledged) at bus level.
The bus driver typically refuses an explicit interrupt acknowledge (issued by an interrupt handler) for shared interrupts. In this case, the bus driver will acknowledge interrupts only when all interrupt handlers have been called.
When the QuiccIntrOps.enable routine has been called by an interrupt handler, the handler must return the value which has been returned by QuiccIntrOps.enable. Once QuiccIntrOps.enable is called, the driver should be able to handle an immediate re-entrance in the interrupt handler code.
The QuiccIntrOps.disable service routine disables the interrupt source previously enabled by QuiccIntrOps.enable:
void (*disable) (QuiccIntrId intrId);
If QuiccIntrOps.enable returns QUICC_INTR_ACKNOWLEDGED, the driver must call QuiccIntrOps.disable prior to returning from the interrupt handler.
When an interrupt occurs, the attached QuiccIntrHandler is invoked with the interrupt source disabled at bus level. This behaves in exactly the way as if QuiccIntrOps.disable has been called 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 QuiccIntrOps.enable/disable pair to allow the interrupt to be nested. This feature is typically used by a QUICC-to-bus bridge driver when the secondary bus interrupts are multiplexed, that is, multiple secondary bus interrupts are reported through the same primary QUICC bus interrupt. Typically, an interrupt handler of this type of QUICC-to-bus bridge driver would take the following actions:
identify and disable the secondary bus interrupt source
enable the primary QUICC bus interrupt source, using QuiccIntrOps.enable
call handlers attached to the secondary bus interrupt source
disable the primary QUICC bus interrupt source, using QuiccIntrOps.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 QUICC bus, it should detach the handler attached to an interrupt source.
The QuiccBusOps.intr_detach is used to detach a handler, previously attached with QuiccBusOps.intr_attach, and to release any resources allocated for this attachement:
void (*intr\_detach) (QuiccIntrId intrId);
The intrId argument identifies the attachement to release.
When the QuiccBusOps.intr_detach function is called, the driver may no longer use the identifier intrId.
When mapping QUICC bus space, a QUICC device driver uses an error handler, invoked by the QUICC bus driver, when a bus error occurs.
Error handlers are used for the following services:
QuiccBusOps.io_map
QuiccBusOps.mem_map
QuiccBusOps.dma_map
When a programmed or direct memory access is aborted because of a bus error, the QUICC bus driver invokes the error handler for the QUICC address and space concerned.
A QuiccErrHandler is defined as follows:
typedef void (*QuiccErrHandler) (void* errCookie, QuiccBusError* err);
The errCookie is an opaque value given by the device driver and is passed back when the handler is called.
The err argument points to a QuiccBusError structure, describing the error as follows:
typedef struct QuiccBusError{ QuiccErrorCode code; QuiccSize offset; } QuiccBusError;
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, that is, the QUICC bus driver is unable to determine the reason for the exception
invalid (not supported) access granularity has been used
a parity error was detected on the QUICC busses
a bus error was asserted by an external bus slave
an error occurred in a transaction to internal memory (Dual Port RAM or internal registers)
a bus transaction was timed out
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.
A QUICC device driver can send a command to the Communication Processor, for a given internal controller channel, by calling the QuiccBusOps.cmd_send service:
KnError (*cmd_send) (QuiccDevId devId, QuiccPropChannel* channel, QuiccSubChannel subChannel, QuiccCommand cmd);
The devId argument identifies the connection with the QUICC bus driver.
The channel argument indicates the QUICC internal controller channel to which the command will be applied. Typically, a QUICC device driver should find the channel to use by looking at the "channel" property in the device node. The value of this type of property is a QuiccPropChannel enumeration value in:
QUICC_SMC1
QUICC_DSP_R
QUICC_SMC2
QUICC_DSP_T
QUICC_SCC1
QUICC_SCC2
QUICC_SCC3
QUICC_SCC4
QUICC_RISC_TIMER
QUICC_MCC1
QUICC_MCC2
QUICC_FCC1
QUICC_FCC1_HDLC
QUICC_FCC1_ATM
QUICC_FCC1_ETHER
QUICC_FCC2
QUICC_FCC2_HDLC
QUICC_FCC2_ATM
QUICC_FCC2_ETHER
QUICC_FCC3
QUICC_FCC3_HDLC
QUICC_FCC3_ATM
QUICC_FCC3_ETHER
The subChannel argument indicates the sub-channel for a Multi-channel controller. Otherwise, the argument should be zero.
The cmd argument indicates the QUICC command to execute and should be in:
initialize receive and transmit parameters
initialize receive parameters
initialize transmit parameters
enter hunt mode
stop transmit when transmit fifo is empty
stop transmit after current frame is sent
restart transmit after stopped
make receive buffer descriptor available to CPU
risc (CP) timers management
ethernet group address management
GCI performs timeout functions
stop receive after current frame
reset block check sequence
GCI receiver sends abort
activate an ATM channel
start current DSP chain
initialize DSP chain
On success, K_OK is returned.
On failure, QuiccBusOps.cmd_send returns an error code as follows:
CPU was not able to execute the command in the maximum time required to execute any of the available commands
invalid channel, or command is invalid for the given channel
A QUICC device driver can configure and enable a Baud-Rate-Generator to work at a given frequency by calling the QuiccBusOps.brg_conf service:
KnError (*brg_conf) (QuiccDevId devId, QuiccPropBrg* brg, QuiccFreq freq);
The devId argument identifies the connection with the QUICC bus driver.
The brg argument indicates which QUICC bus Baud-Rate-Generator to use. Typically, a QUICC device driver should select BRG by looking at the "brg" property in the device node. The value of such a property is a QuiccPropBrg BRG number. The available BRG numbers are specific to each QUICC bus driver implementation and depend on the underlying QUICC micro-controller.
The freq argument indicates the frequency, in Hertz, required for the clock, delivered by the BRG being configured.
On success, K_OK is returned.
On failure, QuiccBusOps.brg_confreturns one of the following error codes:
the brg argument is invalid
the specified brg is not able to work at the required frequency
To configure a parallel I/O port pin as a general purpose I/O pin, a QUICC driver should:
attach to the I/O pin
use services defined for the attached I/O pin
detach from the I/O pin when no longer needed
A QUICC device driver can attach to a general purpose I/O port pin by calling the QuiccBusOps.io_pin_attach service:
KnError (*io_pin_attach) (QuiccDevId devId, QuiccPropIoPin* ioPin, QuiccIoPinOps** ioPinOps, QuiccIoPinId* ioPinId);
The devId argument identifies the connection with the QUICC bus driver.
The ioPin argument indicates the QUICC general purpose I/O pin to attach to. Typically, a QUICC device driver should find the I/O pin to attach to by looking up the "io-pin" property in the device node. The value of such a property is a QuiccPropIoPin structure, shown below:
typedef struct QuiccPropIoPin { QuiccIoPin pin; QuiccIoPort port; Bool intrUsed; QuiccPropIntr intr; } QuiccPropIoPin;
The port field indicates the parallel I/O port and should be in:
QUICC_IO_PORT_A
QUICC_IO_PORT_B
QUICC_IO_PORT_C
QUICC_IO_PORT_D
The pin field is a bit field specifying the port pin to attach to. Only one bit among the bit fields should be set, indicating the I/O pin to use. This field may be zero (no bit set) to indicate that the external signal logically associated to that pin is not available (physically not connected).
The intrUsed argument indicates whether a QUICC interrupt may be triggered on the I/O pin value changes. If intrUsed is TRUE then the intr field defines the interrupt source used to attach a handler to the interrupt. (See section "Attaching a handler to a QUICC interrupt source".)
On success, K_OK is returned and services defined on an attached I/O pin object are returned in the ioPinOps argument. An identifier for the attached interrupt is also returned in ioPinId. This identifier must be used as a first argument to further calls to the QuiccIoPinOps services.
uint32_f (*get) (QuiccIoPinId pinId);
The ioPinId argument identifies the I/O port pin to read.
QuiccIoPinOps.get returns the current state of the I/O port pin (0/1).
void (*set) (QuiccIoPinId pinId);
The ioPinId argument identifies the I/O port pin to set.
void (*clear) (QuiccIoPinId pinId);
The ioPinId argument identifies the I/O port pin to clear.
When a driver no longer needs to use an I/O port pin, or before closing the connection to the QUICC bus, it should detach from the attached pin.
The QuiccBusOps.io_pin_detach is used to release a pin, previously attached with QuiccBusOps.io_pin_attach, and to release any resources allocated for this attachement:
void (*io\_pin\_detach) (QuiccIoPinId ioPinId);
The ioPinId argument identifies the attachment to release.
When the QuiccBusOps.io_pin_detach function is called, the driver is no longer able to use the identifier ioPinId.
The following table lists the QUICC specific node properties.
The alias column specifies the alias name which should be used by a QUICC 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 used by a QUICC bus node. The dev column specifies properties used by a QUICC device node. The symbol m in the bus and dev columns flags mandatory properties. The symbol o in the bus and dev columns flags optional properties. The symbol - in the bus and dev columns flags properties which are not applied to the given node type.
Table 1 QUICC specific propertiesalias | name | value | bus | dev |
QUICC_PROP_INTR | "intr" | QuiccPropIntr[] | -- | o |
QUICC_PROP_IO_REGS | "io-regs" | QuiccPropIoRegs[] | -- | o |
QUICC_PROP_MEM_RGN | "mem-rgn" | QuiccPropMemRgn[] | -- | o |
QUICC_PROP_CHANNEL | "channel" | QuiccPropChannel | -- | o |
QUICC_PROP_BRG | "brg" | QuiccPropBrg | -- | o |
QUICC_PROP_IO_PIN | "io-pin" | QuiccPropIoPin[] | -- | o |
QUICC_PROP_DMA_BURST | "dma-burst" | QuiccPropDmaBurst | m | -- |
QUICC_PROP_DMA_MIN_SIZE | "dma-min-size" | QuiccPropDmaMinSize | m | -- |
QUICC_PROP_BYTE_ORDER | "byte-order" | QuiccPropByteOrder | m | -- |
QUICC_PROP_IMAP_RGN | "imap-rgn" | QuiccPropMemRgn | m | -- |
QUICC_PROP_DPRAM_RGN | "dpram-rgn" | QuiccPropMemRgn | m | -- |
QUICC_PROP_CLOCK_FREQ | "clock-freq" | QuiccPropClockFreq | m | -- |
QUICC_PROP_BRG_CLOCK_FREQ | "brg-clock-freq" | QuiccPropClockFreq | m | -- |
QUICC_PROP_CPM_CLOCK_FREQ | "cpm-clock-freq" | QuiccPropClockFreq | m | -- |
When the value of a property is an array (for example, QuiccPropIntr[]), the size of the array is the size of the property value returned by dtreePropLength. The size of the array divided by the size of its element type (for exmample, sizeof(QuiccPropIntr)) defines the number of elements in the array. Determining this allows a QUICC device driver to iterate through the array in order to perform an action for each element (for instance, to attach an interrupt handler to each device interrupt line).
The QUICC bus driver may support the dynamic allocation of QUICC bus resources. When dynamic resource allocation is supported, a QUICC device driver may use the QuiccBusOps.resource_alloc service routine to allocate a bus resource at run time:
KnError (*resource_alloc) (QuiccDevId devId, DevProperty prop);
devId is returned by QuiccBusOps.open
prop specifies the bus resource being allocated
The QuiccBusOps.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, QuiccBusOps.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 QUICC device driver may release it by calling the QuiccBusOps.resource_free service routine:
void (*resource_free) (QuiccDevId devId, DevProperty prop);
devId is returned by QuiccBusOps.open
prop specifies the bus resource being released
The QuiccBusOps.resource_free service routine releases a given bus resource and updates the device node properties accordingly.
The following bus resource properties may be dynamically allocated and released:
QUICC_PROP_INTR
QUICC_PROP_IO_REGS
QUICC_PROP_MEM_RGN
QUICC_PROP_BRG
Note that the QuiccBusOps.resource_alloc and QuiccBusOps.resource_free routines should not be used by a simple device driver. This type of a driver should assume that all needed resources are already allocated and specified as properties in the device node by the bus driver. The driver should only find the relevant property and call an appropriate service routine, passing a pointer to the property value.
Occassionally, it is not possible to determine all of the required resources prior to device initialization. An example is a bus-to-bus bridge driver which can discover devices residing on the secondary bus only when the bus bridge hardware is already initialized. In this case, when drv_init is called, the bus-to-bus bridge node would contain only the resources needed for the bus-to-bus bridge device itself (the internal bus-to-bus bridge registers). Once the 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 the 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 the devices currently plugged into the secondary bus. Usually, the resource requirements are changed when a hot-plug insertion or removal occurs.
Note that dynamic resource allocation might also be used by a device driver which implements a lazy resource allocation. In this type of driver, the bus resource allocation might be performed at open time using QuiccBusOps.resource_alloc. Then, the dynamically allocated resources might be released at close time using QuiccBusOps.resource_free.
The following table specifies the contexts in which a caller is allowed to invoke each service.
Table 2 QUICC bus invocation contextServices | Base Level | DKI thread | Intr | Blocking |
QuiccBusOps.open | -- | + | -- | + |
QuiccBusOps.close | -- | + | -- | + |
QuiccBusOps.intr_attach | -- | + | -- | + |
QuiccBusOps.intr_detach | -- | + | -- | + |
QuiccBusOps.io_pin_attach | -- | + | -- | + |
QuiccBusOps.io_pin_detach | -- | + | -- | + |
QuiccBusOps.io_map | -- | + | -- | + |
QuiccBusOps.io_unmap | -- | + | -- | + |
QuiccBusOps.mem_map | -- | + | -- | + |
QuiccBusOps.mem_unmap | -- | + | -- | + |
QuiccBusOps.dma_alloc | -- | + | -- | + |
QuiccBusOps.dma_free | -- | + | -- | + |
QuiccBusOps.resource_alloc | -- | + | -- | + |
QuiccBusOps.resource_free | -- | + | -- | + |
QuiccBusOps.cmd_send | + | + | + | -- |
QuiccBusOps.brg_conf | + | + | + | -- |
QuiccIoPinOps.get | + | + | + | -- |
QuiccIoPinOps.set | + | + | + | -- |
QuiccIoPinOps.clear | + | + | + | -- |
QuiccIoOps.load_xx | + | + | + | -- |
QuiccIoOps.store_xx | + | + | + | -- |
QuiccIoOps.read_xx | + | + | + | -- |
QuiccIoOps.write_xx | + | + | + | -- |
QuiccDmaOps.phys_addr | + | + | + | -- |
QuiccDmaOps.virt_addr | + | + | + | -- |
QuiccDmaOps.read_sync | + | + | + | -- |
QuiccDmaOps.write_sync | + | + | + | -- |
QuiccIntrOps.mask | + | + | + | -- |
QuiccIntrOps.unmask | + | + | + | -- |
QuiccIntrOps.enable | + | + | + | -- |
QuiccIntrOps.disable | + | + | + | -- |
See attributes(5) for descriptions of the following attributes:
ATTRIBUTE TYPE | ATTRIBUTE VALUE |
---|---|
Interface Stability | Evolving |
dtreeNodeRoot(9DKI), svDriverRegister(9DKI), svMemAlloc(9DKI), svPhysAlloc(9DKI), svPhysMap(9DKI), svDkiThreadCall(9DKI), svTimeoutSet(9DKI), usecBusyWait(9DKI), DISABLE_PREEMPT(9DKI)
NAME | SYNOPSIS | FEATURES | DESCRIPTION | EXTENDED DESCRIPTION | ATTRIBUTES | SEE ALSO