Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
8.  Direct Memory Access (DMA) Managing DMA Resources Allocating DMA Resources DMA Callback Example  Previous   Contents   Next 
   
 

Determining Maximum Burst Sizes

Drivers specify the DMA burst sizes that their device supports in the dma_attr_burstsizes field of the ddi_dma_attr(9S) structure. This is a bitmap of the supported burst sizes. However, when DMA resources are allocated, the system might impose further restrictions on the burst sizes that might be actually used by the device. The ddi_dma_burstsizes(9F) routine can be used to obtain the allowed burst sizes. It returns the appropriate burst size bitmap for the device. When DMA resources are allocated, a driver can ask the system for appropriate burst sizes to use for its DMA engine.


Example 8-2 Determining Burst Size

#define BEST_BURST_SIZE 0x20 /* 32 bytes */

     if (ddi_dma_buf_bind_handle(xsp->handle,xsp->bp, flags, xxstart,
         (caddr_t)xsp, &cookie, &ccount) != DDI_DMA_MAPPED) {
             /* error handling */
      }
    burst = ddi_dma_burstsizes(xsp->handle);
    /* check which bit is set and choose one burstsize to */
     /* program the DMA engine */
     if (burst & BEST_BURST_SIZE) {
         program DMA engine to use this burst size
     } else {
         other cases
     }

Allocating Private DMA Buffers

Some device drivers might need to allocate memory for DMA transfers to or from a device, besides doing transfers requested by user threads and the kernel. Examples of this are setting up shared memory for communication with the device and allocating intermediate transfer buffers. Use ddi_dma_mem_alloc(9F) to allocate memory for DMA transfers.

int ddi_dma_mem_alloc(ddi_dma_handle_t handle, size_t length,
    ddi_device_acc_attr_t *accattrp, uint_t flags,
    int (*waitfp)(caddr_t), caddr_t arg, caddr_t *kaddrp,
    size_t *real_length, ddi_acc_handle_t *handlep);
handle

DMA handle

length

Length in bytes of the desired allocation

accattrp

Pointer to a device access attribute structure

flags

Data transfer mode flags; possible values are: DDI_DMA_CONSISTENT and DDI_DMA_STREAMING

waitfp

Address of callback function for handling resource allocation failures. See the ddi_dma_mem_alloc(9F) man page

arg

Argument to pass to the callback function

kaddrp

Pointer (on a successful return) that contains the address of the allocated storage

real_length

Length in bytes that was allocated

handlep

Pointer to a data access handle

flags should be set to DDI_DMA_CONSISTENT if the device accesses in a nonsequential fashion, or if synchronization steps using ddi_dma_sync(9F) should be as lightweight as possible (because of frequent use on small objects). This type of access is commonly known as consistent access. I/O parameter blocks that are used for communication between a device and the driver are set up this way.

On the IA platform, to allocate memory for DMA using physically contiguous pages, set the length of the scatter/gather list dma_attr_sgllen in the ddi_dma_attr(9S) structure to 1, and do not specify DDI_DMA_PARTIAL which would otherwise permit partial resource allocation.

Example 8-3 shows how to allocate IOPB memory and the necessary DMA resources to access it. DMA resources must still be allocated, and the DDI_DMA_CONSISTENT flag must be passed to the allocation function.


Example 8-3 Using ddi_dma_mem_alloc(9F)

if (ddi_dma_mem_alloc(xsp->iopb_handle, size, &accattr,
        DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &xsp->iopb_array,
        &real_length, &xsp->acchandle) != DDI_SUCCESS) {
        error handling
        goto failure;
}
if (ddi_dma_addr_bind_handle(xsp->iopb_handle, NULL,
        xsp->iopb_array, real_length,
        DDI_DMA_READ | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
        NULL, &cookie, &count) != DDI_DMA_MAPPED) {
        error handling
        ddi_dma_mem_free(&xsp->acchandle);
        goto failure;
}

flags should be set to DDI_DMA_STREAMING if the device is doing sequential, unidirectional, block-sized and block-aligned transfers to or from memory. This type of access is commonly known as streaming access.

For example, if an I/O transfer can be sped up by using an I/O cache, which at a minimum transfers (flushes) one cache line, ddi_dma_mem_alloc(9F) will round the size to a multiple of the cache line to avoid data corruption.

ddi_dma_mem_alloc(9F) returns the actual size of the allocated memory object. Because of padding and alignment requirements, the actual size might be larger than the requested size. ddi_dma_addr_bind_handle(9F) requires the actual length.

ddi_dma_mem_free(9F) is used to free the memory allocated by ddi_dma_mem_alloc(9F).


Note - If the memory is not properly aligned, the transfer will succeed but the system will choose a different (and possibly less efficient) transfer mode that requires fewer restrictions. For this reason, ddi_dma_mem_alloc(9F) is preferred over kmem_alloc(9F) when allocating memory for the device to access.


Handling Resource Allocation Failures

The resource-allocation routines provide the driver with several options when handling allocation failures. The waitfp argument indicates whether the allocation routines will block, return immediately, or schedule a callback, as shown in Table 8-1.

Table 8-1 Resource Allocation Handling

waitfp value

Indicated Action

DDI_DMA_DONTWAIT

Driver does not want to wait for resources to become available

DDI_DMA_SLEEP

Driver is willing to wait indefinitely for resources to become available

Other values

The address of a function to be called when resources are likely to be available

Programming the DMA Engine

When the resources have been successfully allocated, the device must be programmed. Although programming a DMA engine is device specific, all DMA engines require a starting address and a transfer count. Device drivers retrieve these two values from the DMA cookie returned by a successful call from ddi_dma_addr_bind_handle(9F), ddi_dma_buf_bind_handle(9F), or ddi_dma_getwin(9F). These functions all return the first DMA cookie and a cookie count indicating whether the DMA object consists of more than one cookie. If the cookie count N is greater than 1, ddi_dma_nextcookie(9F) has to be called N-1 times to retrieve all the remaining cookies.

A cookie is of type ddi_dma_cookie(9S) and has the following fields:

uint64_t        _dmac_ll;       /* 64-bit DMA address */
uint32_t        _dmac_la[2];    /* 2 x 32-bit address */
size_t          dmac_size;      /* DMA cookie size */
uint_t          dmac_type;      /* bus specific type bits */

The dmac_laddress specifies a 64-bit I/O address appropriate for programming the device's DMA engine. If a device has a 64-bit DMA address register, a driver should use this field to program the DMA engine. The dmac_address field specifies a 32-bit I/O address that should be used for devices that have a 32-bit DMA address register. dmac_size contains the transfer count. Depending on the bus architecture, the dmac_type field in the cookie might be required by the driver. The driver should not perform any manipulations, such as logical or arithmetic, on the cookie.


Example 8-4 ddi_dma_cookie(9S) Example

ddi_dma_cookie_t                    cookie;

     if (ddi_dma_buf_bind_handle(xsp->handle,xsp->bp, flags, xxstart,
         (caddr_t)xsp, &cookie, &xsp->ccount) != DDI_DMA_MAPPED) {
             /* error handling */
      }
     sglp = regp->sglist;
     for (cnt = 1; cnt <= SGLLEN; cnt++, sglp++) {
         /* store the cookie parms into the S/G list */
         ddi_put32(xsp->access_hdl, &sglp->dma_size,
             (uint32_t)cookie.dmac_size);
         ddi_put32(xsp->access_hdl, &sglp->dma_addr,
             cookie.dmac_address);
         /* Check for end of cookie list */
         if (cnt == xsp->ccount)
             break;
         /* Get next DMA cookie */
         (void) ddi_dma_nextcookie(xsp->handle, &cookie);
     }
        /* start DMA transfer */
     ddi_put8(xsp->access_hdl, &regp->csr,
         ENABLE_INTERRUPTS | START_TRANSFER);


Note - ddi_dma_addr_bind_handle(9F) and ddi_dma_buf_bind_handle(9F) can return more DMA cookies than fit into the scatter-gather list. In this case, the driver has to continue the transfer in the interrupt routine and reprogram the scatter-gather list with the remaining DMA cookies. You must handle sgllen cookies at a time.


 
 
 
  Previous   Contents   Next