1*da607e19SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 231ef9134SClemens Ladisch /* 331ef9134SClemens Ladisch * isochronous resources helper functions 431ef9134SClemens Ladisch * 531ef9134SClemens Ladisch * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 631ef9134SClemens Ladisch */ 731ef9134SClemens Ladisch 831ef9134SClemens Ladisch #include <linux/device.h> 931ef9134SClemens Ladisch #include <linux/firewire.h> 1031ef9134SClemens Ladisch #include <linux/firewire-constants.h> 11d81a6d71SPaul Gortmaker #include <linux/export.h> 1231ef9134SClemens Ladisch #include <linux/jiffies.h> 1331ef9134SClemens Ladisch #include <linux/mutex.h> 1431ef9134SClemens Ladisch #include <linux/sched.h> 1531ef9134SClemens Ladisch #include <linux/spinlock.h> 1631ef9134SClemens Ladisch #include "iso-resources.h" 1731ef9134SClemens Ladisch 1831ef9134SClemens Ladisch /** 1931ef9134SClemens Ladisch * fw_iso_resources_init - initializes a &struct fw_iso_resources 2031ef9134SClemens Ladisch * @r: the resource manager to initialize 2131ef9134SClemens Ladisch * @unit: the device unit for which the resources will be needed 2231ef9134SClemens Ladisch * 2331ef9134SClemens Ladisch * If the device does not support all channel numbers, change @r->channels_mask 2431ef9134SClemens Ladisch * after calling this function. 2531ef9134SClemens Ladisch */ 265b2599a0SClemens Ladisch int fw_iso_resources_init(struct fw_iso_resources *r, struct fw_unit *unit) 2731ef9134SClemens Ladisch { 2831ef9134SClemens Ladisch r->channels_mask = ~0uLL; 2959294a01STakashi Sakamoto r->unit = unit; 3031ef9134SClemens Ladisch mutex_init(&r->mutex); 3131ef9134SClemens Ladisch r->allocated = false; 325b2599a0SClemens Ladisch 335b2599a0SClemens Ladisch return 0; 3431ef9134SClemens Ladisch } 353a691b28SClemens Ladisch EXPORT_SYMBOL(fw_iso_resources_init); 3631ef9134SClemens Ladisch 3731ef9134SClemens Ladisch /** 3831ef9134SClemens Ladisch * fw_iso_resources_destroy - destroy a resource manager 3931ef9134SClemens Ladisch * @r: the resource manager that is no longer needed 4031ef9134SClemens Ladisch */ 4131ef9134SClemens Ladisch void fw_iso_resources_destroy(struct fw_iso_resources *r) 4231ef9134SClemens Ladisch { 4331ef9134SClemens Ladisch WARN_ON(r->allocated); 4431ef9134SClemens Ladisch mutex_destroy(&r->mutex); 4531ef9134SClemens Ladisch } 463a691b28SClemens Ladisch EXPORT_SYMBOL(fw_iso_resources_destroy); 4731ef9134SClemens Ladisch 4831ef9134SClemens Ladisch static unsigned int packet_bandwidth(unsigned int max_payload_bytes, int speed) 4931ef9134SClemens Ladisch { 5031ef9134SClemens Ladisch unsigned int bytes, s400_bytes; 5131ef9134SClemens Ladisch 5231ef9134SClemens Ladisch /* iso packets have three header quadlets and quadlet-aligned payload */ 5331ef9134SClemens Ladisch bytes = 3 * 4 + ALIGN(max_payload_bytes, 4); 5431ef9134SClemens Ladisch 5531ef9134SClemens Ladisch /* convert to bandwidth units (quadlets at S1600 = bytes at S400) */ 5631ef9134SClemens Ladisch if (speed <= SCODE_400) 5731ef9134SClemens Ladisch s400_bytes = bytes * (1 << (SCODE_400 - speed)); 5831ef9134SClemens Ladisch else 5931ef9134SClemens Ladisch s400_bytes = DIV_ROUND_UP(bytes, 1 << (speed - SCODE_400)); 6031ef9134SClemens Ladisch 6131ef9134SClemens Ladisch return s400_bytes; 6231ef9134SClemens Ladisch } 6331ef9134SClemens Ladisch 6431ef9134SClemens Ladisch static int current_bandwidth_overhead(struct fw_card *card) 6531ef9134SClemens Ladisch { 6631ef9134SClemens Ladisch /* 6731ef9134SClemens Ladisch * Under the usual pessimistic assumption (cable length 4.5 m), the 6831ef9134SClemens Ladisch * isochronous overhead for N cables is 1.797 µs + N * 0.494 µs, or 6931ef9134SClemens Ladisch * 88.3 + N * 24.3 in bandwidth units. 7031ef9134SClemens Ladisch * 7131ef9134SClemens Ladisch * The calculation below tries to deduce N from the current gap count. 7231ef9134SClemens Ladisch * If the gap count has been optimized by measuring the actual packet 7331ef9134SClemens Ladisch * transmission time, this derived overhead should be near the actual 7431ef9134SClemens Ladisch * overhead as well. 7531ef9134SClemens Ladisch */ 7631ef9134SClemens Ladisch return card->gap_count < 63 ? card->gap_count * 97 / 10 + 89 : 512; 7731ef9134SClemens Ladisch } 7831ef9134SClemens Ladisch 7931ef9134SClemens Ladisch static int wait_isoch_resource_delay_after_bus_reset(struct fw_card *card) 8031ef9134SClemens Ladisch { 8131ef9134SClemens Ladisch for (;;) { 8231ef9134SClemens Ladisch s64 delay = (card->reset_jiffies + HZ) - get_jiffies_64(); 8331ef9134SClemens Ladisch if (delay <= 0) 8431ef9134SClemens Ladisch return 0; 8531ef9134SClemens Ladisch if (schedule_timeout_interruptible(delay) > 0) 8631ef9134SClemens Ladisch return -ERESTARTSYS; 8731ef9134SClemens Ladisch } 8831ef9134SClemens Ladisch } 8931ef9134SClemens Ladisch 9031ef9134SClemens Ladisch /** 9131ef9134SClemens Ladisch * fw_iso_resources_allocate - allocate isochronous channel and bandwidth 9231ef9134SClemens Ladisch * @r: the resource manager 9331ef9134SClemens Ladisch * @max_payload_bytes: the amount of data (including CIP headers) per packet 9431ef9134SClemens Ladisch * @speed: the speed (e.g., SCODE_400) at which the packets will be sent 9531ef9134SClemens Ladisch * 9631ef9134SClemens Ladisch * This function allocates one isochronous channel and enough bandwidth for the 9731ef9134SClemens Ladisch * specified packet size. 9831ef9134SClemens Ladisch * 9931ef9134SClemens Ladisch * Returns the channel number that the caller must use for streaming, or 10031ef9134SClemens Ladisch * a negative error code. Due to potentionally long delays, this function is 10131ef9134SClemens Ladisch * interruptible and can return -ERESTARTSYS. On success, the caller is 10231ef9134SClemens Ladisch * responsible for calling fw_iso_resources_update() on bus resets, and 10331ef9134SClemens Ladisch * fw_iso_resources_free() when the resources are not longer needed. 10431ef9134SClemens Ladisch */ 10531ef9134SClemens Ladisch int fw_iso_resources_allocate(struct fw_iso_resources *r, 10631ef9134SClemens Ladisch unsigned int max_payload_bytes, int speed) 10731ef9134SClemens Ladisch { 10831ef9134SClemens Ladisch struct fw_card *card = fw_parent_device(r->unit)->card; 10931ef9134SClemens Ladisch int bandwidth, channel, err; 11031ef9134SClemens Ladisch 11131ef9134SClemens Ladisch if (WARN_ON(r->allocated)) 11231ef9134SClemens Ladisch return -EBADFD; 11331ef9134SClemens Ladisch 11431ef9134SClemens Ladisch r->bandwidth = packet_bandwidth(max_payload_bytes, speed); 11531ef9134SClemens Ladisch 11631ef9134SClemens Ladisch retry_after_bus_reset: 11731ef9134SClemens Ladisch spin_lock_irq(&card->lock); 11831ef9134SClemens Ladisch r->generation = card->generation; 11931ef9134SClemens Ladisch r->bandwidth_overhead = current_bandwidth_overhead(card); 12031ef9134SClemens Ladisch spin_unlock_irq(&card->lock); 12131ef9134SClemens Ladisch 12231ef9134SClemens Ladisch err = wait_isoch_resource_delay_after_bus_reset(card); 12331ef9134SClemens Ladisch if (err < 0) 12431ef9134SClemens Ladisch return err; 12531ef9134SClemens Ladisch 12631ef9134SClemens Ladisch mutex_lock(&r->mutex); 12731ef9134SClemens Ladisch 12831ef9134SClemens Ladisch bandwidth = r->bandwidth + r->bandwidth_overhead; 12931ef9134SClemens Ladisch fw_iso_resource_manage(card, r->generation, r->channels_mask, 130f30e6d3eSStefan Richter &channel, &bandwidth, true); 13131ef9134SClemens Ladisch if (channel == -EAGAIN) { 13231ef9134SClemens Ladisch mutex_unlock(&r->mutex); 13331ef9134SClemens Ladisch goto retry_after_bus_reset; 13431ef9134SClemens Ladisch } 13531ef9134SClemens Ladisch if (channel >= 0) { 13631ef9134SClemens Ladisch r->channel = channel; 13731ef9134SClemens Ladisch r->allocated = true; 13831ef9134SClemens Ladisch } else { 13931ef9134SClemens Ladisch if (channel == -EBUSY) 14031ef9134SClemens Ladisch dev_err(&r->unit->device, 14131ef9134SClemens Ladisch "isochronous resources exhausted\n"); 14231ef9134SClemens Ladisch else 14331ef9134SClemens Ladisch dev_err(&r->unit->device, 14431ef9134SClemens Ladisch "isochronous resource allocation failed\n"); 14531ef9134SClemens Ladisch } 14631ef9134SClemens Ladisch 14731ef9134SClemens Ladisch mutex_unlock(&r->mutex); 14831ef9134SClemens Ladisch 14931ef9134SClemens Ladisch return channel; 15031ef9134SClemens Ladisch } 1513a691b28SClemens Ladisch EXPORT_SYMBOL(fw_iso_resources_allocate); 15231ef9134SClemens Ladisch 15331ef9134SClemens Ladisch /** 15431ef9134SClemens Ladisch * fw_iso_resources_update - update resource allocations after a bus reset 15531ef9134SClemens Ladisch * @r: the resource manager 15631ef9134SClemens Ladisch * 15731ef9134SClemens Ladisch * This function must be called from the driver's .update handler to reallocate 15831ef9134SClemens Ladisch * any resources that were allocated before the bus reset. It is safe to call 15931ef9134SClemens Ladisch * this function if no resources are currently allocated. 16031ef9134SClemens Ladisch * 16131ef9134SClemens Ladisch * Returns a negative error code on failure. If this happens, the caller must 16231ef9134SClemens Ladisch * stop streaming. 16331ef9134SClemens Ladisch */ 16431ef9134SClemens Ladisch int fw_iso_resources_update(struct fw_iso_resources *r) 16531ef9134SClemens Ladisch { 16631ef9134SClemens Ladisch struct fw_card *card = fw_parent_device(r->unit)->card; 16731ef9134SClemens Ladisch int bandwidth, channel; 16831ef9134SClemens Ladisch 16931ef9134SClemens Ladisch mutex_lock(&r->mutex); 17031ef9134SClemens Ladisch 17131ef9134SClemens Ladisch if (!r->allocated) { 17231ef9134SClemens Ladisch mutex_unlock(&r->mutex); 17331ef9134SClemens Ladisch return 0; 17431ef9134SClemens Ladisch } 17531ef9134SClemens Ladisch 17631ef9134SClemens Ladisch spin_lock_irq(&card->lock); 17731ef9134SClemens Ladisch r->generation = card->generation; 17831ef9134SClemens Ladisch r->bandwidth_overhead = current_bandwidth_overhead(card); 17931ef9134SClemens Ladisch spin_unlock_irq(&card->lock); 18031ef9134SClemens Ladisch 18131ef9134SClemens Ladisch bandwidth = r->bandwidth + r->bandwidth_overhead; 18231ef9134SClemens Ladisch 18331ef9134SClemens Ladisch fw_iso_resource_manage(card, r->generation, 1uLL << r->channel, 184f30e6d3eSStefan Richter &channel, &bandwidth, true); 18531ef9134SClemens Ladisch /* 18631ef9134SClemens Ladisch * When another bus reset happens, pretend that the allocation 18731ef9134SClemens Ladisch * succeeded; we will try again for the new generation later. 18831ef9134SClemens Ladisch */ 18931ef9134SClemens Ladisch if (channel < 0 && channel != -EAGAIN) { 19031ef9134SClemens Ladisch r->allocated = false; 19131ef9134SClemens Ladisch if (channel == -EBUSY) 19231ef9134SClemens Ladisch dev_err(&r->unit->device, 19331ef9134SClemens Ladisch "isochronous resources exhausted\n"); 19431ef9134SClemens Ladisch else 19531ef9134SClemens Ladisch dev_err(&r->unit->device, 19631ef9134SClemens Ladisch "isochronous resource allocation failed\n"); 19731ef9134SClemens Ladisch } 19831ef9134SClemens Ladisch 19931ef9134SClemens Ladisch mutex_unlock(&r->mutex); 20031ef9134SClemens Ladisch 20131ef9134SClemens Ladisch return channel; 20231ef9134SClemens Ladisch } 2033a691b28SClemens Ladisch EXPORT_SYMBOL(fw_iso_resources_update); 20431ef9134SClemens Ladisch 20531ef9134SClemens Ladisch /** 20631ef9134SClemens Ladisch * fw_iso_resources_free - frees allocated resources 20731ef9134SClemens Ladisch * @r: the resource manager 20831ef9134SClemens Ladisch * 20931ef9134SClemens Ladisch * This function deallocates the channel and bandwidth, if allocated. 21031ef9134SClemens Ladisch */ 21131ef9134SClemens Ladisch void fw_iso_resources_free(struct fw_iso_resources *r) 21231ef9134SClemens Ladisch { 2130c264af7STakashi Sakamoto struct fw_card *card; 21431ef9134SClemens Ladisch int bandwidth, channel; 21531ef9134SClemens Ladisch 2160c264af7STakashi Sakamoto /* Not initialized. */ 2170c264af7STakashi Sakamoto if (r->unit == NULL) 2180c264af7STakashi Sakamoto return; 2190c264af7STakashi Sakamoto card = fw_parent_device(r->unit)->card; 2200c264af7STakashi Sakamoto 22131ef9134SClemens Ladisch mutex_lock(&r->mutex); 22231ef9134SClemens Ladisch 22331ef9134SClemens Ladisch if (r->allocated) { 22431ef9134SClemens Ladisch bandwidth = r->bandwidth + r->bandwidth_overhead; 22531ef9134SClemens Ladisch fw_iso_resource_manage(card, r->generation, 1uLL << r->channel, 226f30e6d3eSStefan Richter &channel, &bandwidth, false); 22731ef9134SClemens Ladisch if (channel < 0) 22831ef9134SClemens Ladisch dev_err(&r->unit->device, 22931ef9134SClemens Ladisch "isochronous resource deallocation failed\n"); 23031ef9134SClemens Ladisch 23131ef9134SClemens Ladisch r->allocated = false; 23231ef9134SClemens Ladisch } 23331ef9134SClemens Ladisch 23431ef9134SClemens Ladisch mutex_unlock(&r->mutex); 23531ef9134SClemens Ladisch } 2363a691b28SClemens Ladisch EXPORT_SYMBOL(fw_iso_resources_free); 237