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 */
fw_iso_resources_init(struct fw_iso_resources * r,struct fw_unit * unit)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 */
fw_iso_resources_destroy(struct fw_iso_resources * r)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
packet_bandwidth(unsigned int max_payload_bytes,int speed)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
current_bandwidth_overhead(struct fw_card * card)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
wait_isoch_resource_delay_after_bus_reset(struct fw_card * card)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 */
fw_iso_resources_allocate(struct fw_iso_resources * r,unsigned int max_payload_bytes,int speed)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 */
fw_iso_resources_update(struct fw_iso_resources * r)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 */
fw_iso_resources_free(struct fw_iso_resources * r)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