18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Isochronous I/O functionality: 48c2ecf20Sopenharmony_ci * - Isochronous DMA context management 58c2ecf20Sopenharmony_ci * - Isochronous bus resource management (channels, bandwidth), client side 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2006 Kristian Hoegsberg <krh@bitplanet.net> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/firewire.h> 138c2ecf20Sopenharmony_ci#include <linux/firewire-constants.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/mm.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 188c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 198c2ecf20Sopenharmony_ci#include <linux/export.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "core.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * Isochronous DMA context management 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciint fw_iso_buffer_alloc(struct fw_iso_buffer *buffer, int page_count) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci int i; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci buffer->page_count = 0; 348c2ecf20Sopenharmony_ci buffer->page_count_mapped = 0; 358c2ecf20Sopenharmony_ci buffer->pages = kmalloc_array(page_count, sizeof(buffer->pages[0]), 368c2ecf20Sopenharmony_ci GFP_KERNEL); 378c2ecf20Sopenharmony_ci if (buffer->pages == NULL) 388c2ecf20Sopenharmony_ci return -ENOMEM; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci for (i = 0; i < page_count; i++) { 418c2ecf20Sopenharmony_ci buffer->pages[i] = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); 428c2ecf20Sopenharmony_ci if (buffer->pages[i] == NULL) 438c2ecf20Sopenharmony_ci break; 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci buffer->page_count = i; 468c2ecf20Sopenharmony_ci if (i < page_count) { 478c2ecf20Sopenharmony_ci fw_iso_buffer_destroy(buffer, NULL); 488c2ecf20Sopenharmony_ci return -ENOMEM; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return 0; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ciint fw_iso_buffer_map_dma(struct fw_iso_buffer *buffer, struct fw_card *card, 558c2ecf20Sopenharmony_ci enum dma_data_direction direction) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci dma_addr_t address; 588c2ecf20Sopenharmony_ci int i; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci buffer->direction = direction; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci for (i = 0; i < buffer->page_count; i++) { 638c2ecf20Sopenharmony_ci address = dma_map_page(card->device, buffer->pages[i], 648c2ecf20Sopenharmony_ci 0, PAGE_SIZE, direction); 658c2ecf20Sopenharmony_ci if (dma_mapping_error(card->device, address)) 668c2ecf20Sopenharmony_ci break; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci set_page_private(buffer->pages[i], address); 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci buffer->page_count_mapped = i; 718c2ecf20Sopenharmony_ci if (i < buffer->page_count) 728c2ecf20Sopenharmony_ci return -ENOMEM; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return 0; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ciint fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, 788c2ecf20Sopenharmony_ci int page_count, enum dma_data_direction direction) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci int ret; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci ret = fw_iso_buffer_alloc(buffer, page_count); 838c2ecf20Sopenharmony_ci if (ret < 0) 848c2ecf20Sopenharmony_ci return ret; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci ret = fw_iso_buffer_map_dma(buffer, card, direction); 878c2ecf20Sopenharmony_ci if (ret < 0) 888c2ecf20Sopenharmony_ci fw_iso_buffer_destroy(buffer, card); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return ret; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fw_iso_buffer_init); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_civoid fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, 958c2ecf20Sopenharmony_ci struct fw_card *card) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci int i; 988c2ecf20Sopenharmony_ci dma_addr_t address; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci for (i = 0; i < buffer->page_count_mapped; i++) { 1018c2ecf20Sopenharmony_ci address = page_private(buffer->pages[i]); 1028c2ecf20Sopenharmony_ci dma_unmap_page(card->device, address, 1038c2ecf20Sopenharmony_ci PAGE_SIZE, buffer->direction); 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci for (i = 0; i < buffer->page_count; i++) 1068c2ecf20Sopenharmony_ci __free_page(buffer->pages[i]); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci kfree(buffer->pages); 1098c2ecf20Sopenharmony_ci buffer->pages = NULL; 1108c2ecf20Sopenharmony_ci buffer->page_count = 0; 1118c2ecf20Sopenharmony_ci buffer->page_count_mapped = 0; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fw_iso_buffer_destroy); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* Convert DMA address to offset into virtually contiguous buffer. */ 1168c2ecf20Sopenharmony_cisize_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci size_t i; 1198c2ecf20Sopenharmony_ci dma_addr_t address; 1208c2ecf20Sopenharmony_ci ssize_t offset; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci for (i = 0; i < buffer->page_count; i++) { 1238c2ecf20Sopenharmony_ci address = page_private(buffer->pages[i]); 1248c2ecf20Sopenharmony_ci offset = (ssize_t)completed - (ssize_t)address; 1258c2ecf20Sopenharmony_ci if (offset > 0 && offset <= PAGE_SIZE) 1268c2ecf20Sopenharmony_ci return (i << PAGE_SHIFT) + offset; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return 0; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistruct fw_iso_context *fw_iso_context_create(struct fw_card *card, 1338c2ecf20Sopenharmony_ci int type, int channel, int speed, size_t header_size, 1348c2ecf20Sopenharmony_ci fw_iso_callback_t callback, void *callback_data) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct fw_iso_context *ctx; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci ctx = card->driver->allocate_iso_context(card, 1398c2ecf20Sopenharmony_ci type, channel, header_size); 1408c2ecf20Sopenharmony_ci if (IS_ERR(ctx)) 1418c2ecf20Sopenharmony_ci return ctx; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci ctx->card = card; 1448c2ecf20Sopenharmony_ci ctx->type = type; 1458c2ecf20Sopenharmony_ci ctx->channel = channel; 1468c2ecf20Sopenharmony_ci ctx->speed = speed; 1478c2ecf20Sopenharmony_ci ctx->header_size = header_size; 1488c2ecf20Sopenharmony_ci ctx->callback.sc = callback; 1498c2ecf20Sopenharmony_ci ctx->callback_data = callback_data; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return ctx; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fw_iso_context_create); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_civoid fw_iso_context_destroy(struct fw_iso_context *ctx) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci ctx->card->driver->free_iso_context(ctx); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fw_iso_context_destroy); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ciint fw_iso_context_start(struct fw_iso_context *ctx, 1628c2ecf20Sopenharmony_ci int cycle, int sync, int tags) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci return ctx->card->driver->start_iso(ctx, cycle, sync, tags); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fw_iso_context_start); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ciint fw_iso_context_set_channels(struct fw_iso_context *ctx, u64 *channels) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci return ctx->card->driver->set_iso_channels(ctx, channels); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ciint fw_iso_context_queue(struct fw_iso_context *ctx, 1748c2ecf20Sopenharmony_ci struct fw_iso_packet *packet, 1758c2ecf20Sopenharmony_ci struct fw_iso_buffer *buffer, 1768c2ecf20Sopenharmony_ci unsigned long payload) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci return ctx->card->driver->queue_iso(ctx, packet, buffer, payload); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fw_iso_context_queue); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_civoid fw_iso_context_queue_flush(struct fw_iso_context *ctx) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci ctx->card->driver->flush_queue_iso(ctx); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fw_iso_context_queue_flush); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ciint fw_iso_context_flush_completions(struct fw_iso_context *ctx) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci return ctx->card->driver->flush_iso_completions(ctx); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fw_iso_context_flush_completions); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ciint fw_iso_context_stop(struct fw_iso_context *ctx) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci return ctx->card->driver->stop_iso(ctx); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fw_iso_context_stop); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci/* 2018c2ecf20Sopenharmony_ci * Isochronous bus resource management (channels, bandwidth), client side 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int manage_bandwidth(struct fw_card *card, int irm_id, int generation, 2058c2ecf20Sopenharmony_ci int bandwidth, bool allocate) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci int try, new, old = allocate ? BANDWIDTH_AVAILABLE_INITIAL : 0; 2088c2ecf20Sopenharmony_ci __be32 data[2]; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* 2118c2ecf20Sopenharmony_ci * On a 1394a IRM with low contention, try < 1 is enough. 2128c2ecf20Sopenharmony_ci * On a 1394-1995 IRM, we need at least try < 2. 2138c2ecf20Sopenharmony_ci * Let's just do try < 5. 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_ci for (try = 0; try < 5; try++) { 2168c2ecf20Sopenharmony_ci new = allocate ? old - bandwidth : old + bandwidth; 2178c2ecf20Sopenharmony_ci if (new < 0 || new > BANDWIDTH_AVAILABLE_INITIAL) 2188c2ecf20Sopenharmony_ci return -EBUSY; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci data[0] = cpu_to_be32(old); 2218c2ecf20Sopenharmony_ci data[1] = cpu_to_be32(new); 2228c2ecf20Sopenharmony_ci switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP, 2238c2ecf20Sopenharmony_ci irm_id, generation, SCODE_100, 2248c2ecf20Sopenharmony_ci CSR_REGISTER_BASE + CSR_BANDWIDTH_AVAILABLE, 2258c2ecf20Sopenharmony_ci data, 8)) { 2268c2ecf20Sopenharmony_ci case RCODE_GENERATION: 2278c2ecf20Sopenharmony_ci /* A generation change frees all bandwidth. */ 2288c2ecf20Sopenharmony_ci return allocate ? -EAGAIN : bandwidth; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci case RCODE_COMPLETE: 2318c2ecf20Sopenharmony_ci if (be32_to_cpup(data) == old) 2328c2ecf20Sopenharmony_ci return bandwidth; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci old = be32_to_cpup(data); 2358c2ecf20Sopenharmony_ci /* Fall through. */ 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return -EIO; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int manage_channel(struct fw_card *card, int irm_id, int generation, 2438c2ecf20Sopenharmony_ci u32 channels_mask, u64 offset, bool allocate) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci __be32 bit, all, old; 2468c2ecf20Sopenharmony_ci __be32 data[2]; 2478c2ecf20Sopenharmony_ci int channel, ret = -EIO, retry = 5; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci old = all = allocate ? cpu_to_be32(~0) : 0; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci for (channel = 0; channel < 32; channel++) { 2528c2ecf20Sopenharmony_ci if (!(channels_mask & 1 << channel)) 2538c2ecf20Sopenharmony_ci continue; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci ret = -EBUSY; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci bit = cpu_to_be32(1 << (31 - channel)); 2588c2ecf20Sopenharmony_ci if ((old & bit) != (all & bit)) 2598c2ecf20Sopenharmony_ci continue; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci data[0] = old; 2628c2ecf20Sopenharmony_ci data[1] = old ^ bit; 2638c2ecf20Sopenharmony_ci switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP, 2648c2ecf20Sopenharmony_ci irm_id, generation, SCODE_100, 2658c2ecf20Sopenharmony_ci offset, data, 8)) { 2668c2ecf20Sopenharmony_ci case RCODE_GENERATION: 2678c2ecf20Sopenharmony_ci /* A generation change frees all channels. */ 2688c2ecf20Sopenharmony_ci return allocate ? -EAGAIN : channel; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci case RCODE_COMPLETE: 2718c2ecf20Sopenharmony_ci if (data[0] == old) 2728c2ecf20Sopenharmony_ci return channel; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci old = data[0]; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* Is the IRM 1394a-2000 compliant? */ 2778c2ecf20Sopenharmony_ci if ((data[0] & bit) == (data[1] & bit)) 2788c2ecf20Sopenharmony_ci continue; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci fallthrough; /* It's a 1394-1995 IRM, retry */ 2818c2ecf20Sopenharmony_ci default: 2828c2ecf20Sopenharmony_ci if (retry) { 2838c2ecf20Sopenharmony_ci retry--; 2848c2ecf20Sopenharmony_ci channel--; 2858c2ecf20Sopenharmony_ci } else { 2868c2ecf20Sopenharmony_ci ret = -EIO; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return ret; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic void deallocate_channel(struct fw_card *card, int irm_id, 2958c2ecf20Sopenharmony_ci int generation, int channel) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci u32 mask; 2988c2ecf20Sopenharmony_ci u64 offset; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci mask = channel < 32 ? 1 << channel : 1 << (channel - 32); 3018c2ecf20Sopenharmony_ci offset = channel < 32 ? CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_HI : 3028c2ecf20Sopenharmony_ci CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_LO; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci manage_channel(card, irm_id, generation, mask, offset, false); 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci/** 3088c2ecf20Sopenharmony_ci * fw_iso_resource_manage() - Allocate or deallocate a channel and/or bandwidth 3098c2ecf20Sopenharmony_ci * @card: card interface for this action 3108c2ecf20Sopenharmony_ci * @generation: bus generation 3118c2ecf20Sopenharmony_ci * @channels_mask: bitmask for channel allocation 3128c2ecf20Sopenharmony_ci * @channel: pointer for returning channel allocation result 3138c2ecf20Sopenharmony_ci * @bandwidth: pointer for returning bandwidth allocation result 3148c2ecf20Sopenharmony_ci * @allocate: whether to allocate (true) or deallocate (false) 3158c2ecf20Sopenharmony_ci * 3168c2ecf20Sopenharmony_ci * In parameters: card, generation, channels_mask, bandwidth, allocate 3178c2ecf20Sopenharmony_ci * Out parameters: channel, bandwidth 3188c2ecf20Sopenharmony_ci * 3198c2ecf20Sopenharmony_ci * This function blocks (sleeps) during communication with the IRM. 3208c2ecf20Sopenharmony_ci * 3218c2ecf20Sopenharmony_ci * Allocates or deallocates at most one channel out of channels_mask. 3228c2ecf20Sopenharmony_ci * channels_mask is a bitfield with MSB for channel 63 and LSB for channel 0. 3238c2ecf20Sopenharmony_ci * (Note, the IRM's CHANNELS_AVAILABLE is a big-endian bitfield with MSB for 3248c2ecf20Sopenharmony_ci * channel 0 and LSB for channel 63.) 3258c2ecf20Sopenharmony_ci * Allocates or deallocates as many bandwidth allocation units as specified. 3268c2ecf20Sopenharmony_ci * 3278c2ecf20Sopenharmony_ci * Returns channel < 0 if no channel was allocated or deallocated. 3288c2ecf20Sopenharmony_ci * Returns bandwidth = 0 if no bandwidth was allocated or deallocated. 3298c2ecf20Sopenharmony_ci * 3308c2ecf20Sopenharmony_ci * If generation is stale, deallocations succeed but allocations fail with 3318c2ecf20Sopenharmony_ci * channel = -EAGAIN. 3328c2ecf20Sopenharmony_ci * 3338c2ecf20Sopenharmony_ci * If channel allocation fails, no bandwidth will be allocated either. 3348c2ecf20Sopenharmony_ci * If bandwidth allocation fails, no channel will be allocated either. 3358c2ecf20Sopenharmony_ci * But deallocations of channel and bandwidth are tried independently 3368c2ecf20Sopenharmony_ci * of each other's success. 3378c2ecf20Sopenharmony_ci */ 3388c2ecf20Sopenharmony_civoid fw_iso_resource_manage(struct fw_card *card, int generation, 3398c2ecf20Sopenharmony_ci u64 channels_mask, int *channel, int *bandwidth, 3408c2ecf20Sopenharmony_ci bool allocate) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci u32 channels_hi = channels_mask; /* channels 31...0 */ 3438c2ecf20Sopenharmony_ci u32 channels_lo = channels_mask >> 32; /* channels 63...32 */ 3448c2ecf20Sopenharmony_ci int irm_id, ret, c = -EINVAL; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci spin_lock_irq(&card->lock); 3478c2ecf20Sopenharmony_ci irm_id = card->irm_node->node_id; 3488c2ecf20Sopenharmony_ci spin_unlock_irq(&card->lock); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (channels_hi) 3518c2ecf20Sopenharmony_ci c = manage_channel(card, irm_id, generation, channels_hi, 3528c2ecf20Sopenharmony_ci CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_HI, 3538c2ecf20Sopenharmony_ci allocate); 3548c2ecf20Sopenharmony_ci if (channels_lo && c < 0) { 3558c2ecf20Sopenharmony_ci c = manage_channel(card, irm_id, generation, channels_lo, 3568c2ecf20Sopenharmony_ci CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_LO, 3578c2ecf20Sopenharmony_ci allocate); 3588c2ecf20Sopenharmony_ci if (c >= 0) 3598c2ecf20Sopenharmony_ci c += 32; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci *channel = c; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (allocate && channels_mask != 0 && c < 0) 3648c2ecf20Sopenharmony_ci *bandwidth = 0; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (*bandwidth == 0) 3678c2ecf20Sopenharmony_ci return; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci ret = manage_bandwidth(card, irm_id, generation, *bandwidth, allocate); 3708c2ecf20Sopenharmony_ci if (ret < 0) 3718c2ecf20Sopenharmony_ci *bandwidth = 0; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (allocate && ret < 0) { 3748c2ecf20Sopenharmony_ci if (c >= 0) 3758c2ecf20Sopenharmony_ci deallocate_channel(card, irm_id, generation, c); 3768c2ecf20Sopenharmony_ci *channel = ret; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fw_iso_resource_manage); 380