1117035Sgordon// SPDX-License-Identifier: GPL-2.0-only
2117035Sgordon/*
3117035Sgordon * isochronous resources helper functions
4117035Sgordon *
5117692Sobrien * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
6117035Sgordon */
7117035Sgordon
8117035Sgordon#include <linux/device.h>
9153455Sjhb#include <linux/firewire.h>
10117035Sgordon#include <linux/firewire-constants.h>
11117035Sgordon#include <linux/export.h>
12117035Sgordon#include <linux/jiffies.h>
13117035Sgordon#include <linux/mutex.h>
14147090Sbrooks#include <linux/sched.h>
15147090Sbrooks#include <linux/spinlock.h>
16147090Sbrooks#include "iso-resources.h"
17147090Sbrooks
18147090Sbrooks/**
19117035Sgordon * fw_iso_resources_init - initializes a &struct fw_iso_resources
20117035Sgordon * @r: the resource manager to initialize
21117035Sgordon * @unit: the device unit for which the resources will be needed
22117035Sgordon *
23117035Sgordon * If the device does not support all channel numbers, change @r->channels_mask
24117035Sgordon * after calling this function.
25117035Sgordon */
26117035Sgordonint fw_iso_resources_init(struct fw_iso_resources *r, struct fw_unit *unit)
27117035Sgordon{
28117035Sgordon	r->channels_mask = ~0uLL;
29117035Sgordon	r->unit = unit;
30117035Sgordon	mutex_init(&r->mutex);
31117035Sgordon	r->allocated = false;
32117035Sgordon
33117035Sgordon	return 0;
34117035Sgordon}
35117035SgordonEXPORT_SYMBOL(fw_iso_resources_init);
36117035Sgordon
37117035Sgordon/**
38117035Sgordon * fw_iso_resources_destroy - destroy a resource manager
39117035Sgordon * @r: the resource manager that is no longer needed
40117035Sgordon */
41117035Sgordonvoid fw_iso_resources_destroy(struct fw_iso_resources *r)
42117035Sgordon{
43117035Sgordon	WARN_ON(r->allocated);
44117035Sgordon	mutex_destroy(&r->mutex);
45117035Sgordon}
46117035SgordonEXPORT_SYMBOL(fw_iso_resources_destroy);
47117449Sgordon
48117449Sgordonstatic unsigned int packet_bandwidth(unsigned int max_payload_bytes, int speed)
49117035Sgordon{
50117035Sgordon	unsigned int bytes, s400_bytes;
51117035Sgordon
52117035Sgordon	/* iso packets have three header quadlets and quadlet-aligned payload */
53117035Sgordon	bytes = 3 * 4 + ALIGN(max_payload_bytes, 4);
54117035Sgordon
55117035Sgordon	/* convert to bandwidth units (quadlets at S1600 = bytes at S400) */
56117035Sgordon	if (speed <= SCODE_400)
57117035Sgordon		s400_bytes = bytes * (1 << (SCODE_400 - speed));
58117035Sgordon	else
59117035Sgordon		s400_bytes = DIV_ROUND_UP(bytes, 1 << (speed - SCODE_400));
60117035Sgordon
61117035Sgordon	return s400_bytes;
62117035Sgordon}
63117035Sgordon
64117035Sgordonstatic int current_bandwidth_overhead(struct fw_card *card)
65117035Sgordon{
66117692Sobrien	/*
67117791Sobrien	 * Under the usual pessimistic assumption (cable length 4.5 m), the
68117601Sgordon	 * isochronous overhead for N cables is 1.797 ��s + N * 0.494 ��s, or
69117791Sobrien	 * 88.3 + N * 24.3 in bandwidth units.
70117711Sobrien	 *
71139113Sru	 * The calculation below tries to deduce N from the current gap count.
72117711Sobrien	 * If the gap count has been optimized by measuring the actual packet
73117711Sobrien	 * transmission time, this derived overhead should be near the actual
74117035Sgordon	 * overhead as well.
75117035Sgordon	 */
76117035Sgordon	return card->gap_count < 63 ? card->gap_count * 97 / 10 + 89 : 512;
77117035Sgordon}
78117035Sgordon
79117692Sobrienstatic int wait_isoch_resource_delay_after_bus_reset(struct fw_card *card)
80117035Sgordon{
81117035Sgordon	for (;;) {
82117035Sgordon		s64 delay = (card->reset_jiffies + HZ) - get_jiffies_64();
83117035Sgordon		if (delay <= 0)
84143026Strhodes			return 0;
85117601Sgordon		if (schedule_timeout_interruptible(delay) > 0)
86117035Sgordon			return -ERESTARTSYS;
87117035Sgordon	}
88117035Sgordon}
89117601Sgordon
90117035Sgordon/**
91117692Sobrien * fw_iso_resources_allocate - allocate isochronous channel and bandwidth
92117692Sobrien * @r: the resource manager
93117035Sgordon * @max_payload_bytes: the amount of data (including CIP headers) per packet
94117035Sgordon * @speed: the speed (e.g., SCODE_400) at which the packets will be sent
95117035Sgordon *
96117035Sgordon * This function allocates one isochronous channel and enough bandwidth for the
97117035Sgordon * specified packet size.
98117035Sgordon *
99117035Sgordon * Returns the channel number that the caller must use for streaming, or
100117035Sgordon * a negative error code.  Due to potentionally long delays, this function is
101117791Sobrien * interruptible and can return -ERESTARTSYS.  On success, the caller is
102117035Sgordon * responsible for calling fw_iso_resources_update() on bus resets, and
103117035Sgordon * fw_iso_resources_free() when the resources are not longer needed.
104117692Sobrien */
105119664Sphkint fw_iso_resources_allocate(struct fw_iso_resources *r,
106118826Sharti			      unsigned int max_payload_bytes, int speed)
107133799Smarius{
108119664Sphk	struct fw_card *card = fw_parent_device(r->unit)->card;
109117791Sobrien	int bandwidth, channel, err;
110117035Sgordon
111117086Sgordon	if (WARN_ON(r->allocated))
112117791Sobrien		return -EBADFD;
113127075Sphk
114117791Sobrien	r->bandwidth = packet_bandwidth(max_payload_bytes, speed);
115119664Sphk
116117035Sgordonretry_after_bus_reset:
117139105Sru	spin_lock_irq(&card->lock);
118133799Smarius	r->generation = card->generation;
119119664Sphk	r->bandwidth_overhead = current_bandwidth_overhead(card);
120119664Sphk	spin_unlock_irq(&card->lock);
121119664Sphk
122139115Sru	err = wait_isoch_resource_delay_after_bus_reset(card);
123119664Sphk	if (err < 0)
124119664Sphk		return err;
125119664Sphk
126117693Sobrien	mutex_lock(&r->mutex);
127145630Sdarrenr
128117693Sobrien	bandwidth = r->bandwidth + r->bandwidth_overhead;
129117693Sobrien	fw_iso_resource_manage(card, r->generation, r->channels_mask,
130117035Sgordon			       &channel, &bandwidth, true);
131117035Sgordon	if (channel == -EAGAIN) {
132117035Sgordon		mutex_unlock(&r->mutex);
133119664Sphk		goto retry_after_bus_reset;
134120492Sfjoe	}
135117035Sgordon	if (channel >= 0) {
136117035Sgordon		r->channel = channel;
137123131Simp		r->allocated = true;
138117057Sgordon	} else {
139117079Sgordon		if (channel == -EBUSY)
140117079Sgordon			dev_err(&r->unit->device,
141117035Sgordon				"isochronous resources exhausted\n");
142117035Sgordon		else
143117035Sgordon			dev_err(&r->unit->device,
144117692Sobrien				"isochronous resource allocation failed\n");
145117035Sgordon	}
146117035Sgordon
147117035Sgordon	mutex_unlock(&r->mutex);
148117601Sgordon
149117035Sgordon	return channel;
150117035Sgordon}
151117043SgordonEXPORT_SYMBOL(fw_iso_resources_allocate);
152117601Sgordon
153117035Sgordon/**
154117035Sgordon * fw_iso_resources_update - update resource allocations after a bus reset
155117035Sgordon * @r: the resource manager
156117057Sgordon *
157117035Sgordon * This function must be called from the driver's .update handler to reallocate
158117035Sgordon * any resources that were allocated before the bus reset.  It is safe to call
159117057Sgordon * this function if no resources are currently allocated.
160117601Sgordon *
161117057Sgordon * Returns a negative error code on failure.  If this happens, the caller must
162117057Sgordon * stop streaming.
163117057Sgordon */
164117692Sobrienint fw_iso_resources_update(struct fw_iso_resources *r)
165118826Sharti{
166117692Sobrien	struct fw_card *card = fw_parent_device(r->unit)->card;
167117692Sobrien	int bandwidth, channel;
168117692Sobrien
169145630Sdarrenr	mutex_lock(&r->mutex);
170117035Sgordon
171117692Sobrien	if (!r->allocated) {
172117035Sgordon		mutex_unlock(&r->mutex);
173117692Sobrien		return 0;
174117035Sgordon	}
175117035Sgordon
176117035Sgordon	spin_lock_irq(&card->lock);
177147090Sbrooks	r->generation = card->generation;
178147090Sbrooks	r->bandwidth_overhead = current_bandwidth_overhead(card);
179117035Sgordon	spin_unlock_irq(&card->lock);
180117035Sgordon
181117035Sgordon	bandwidth = r->bandwidth + r->bandwidth_overhead;
182117035Sgordon
183117692Sobrien	fw_iso_resource_manage(card, r->generation, 1uLL << r->channel,
184117692Sobrien			       &channel, &bandwidth, true);
185117035Sgordon	/*
186117692Sobrien	 * When another bus reset happens, pretend that the allocation
187117692Sobrien	 * succeeded; we will try again for the new generation later.
188117035Sgordon	 */
189117692Sobrien	if (channel < 0 && channel != -EAGAIN) {
190117692Sobrien		r->allocated = false;
191117692Sobrien		if (channel == -EBUSY)
192117035Sgordon			dev_err(&r->unit->device,
193138366Sobrien				"isochronous resources exhausted\n");
194138366Sobrien		else
195138366Sobrien			dev_err(&r->unit->device,
196117692Sobrien				"isochronous resource allocation failed\n");
197117692Sobrien	}
198117035Sgordon
199126874Sdes	mutex_unlock(&r->mutex);
200126874Sdes
201126874Sdes	return channel;
202117035Sgordon}
203141478SdesEXPORT_SYMBOL(fw_iso_resources_update);
204141478Sdes
205141478Sdes/**
206141478Sdes * fw_iso_resources_free - frees allocated resources
207141478Sdes * @r: the resource manager
208141478Sdes *
209141478Sdes * This function deallocates the channel and bandwidth, if allocated.
210117035Sgordon */
211117035Sgordonvoid fw_iso_resources_free(struct fw_iso_resources *r)
212117035Sgordon{
213117035Sgordon	struct fw_card *card;
214117035Sgordon	int bandwidth, channel;
215117035Sgordon
216117692Sobrien	/* Not initialized. */
217117035Sgordon	if (r->unit == NULL)
218117035Sgordon		return;
219117692Sobrien	card = fw_parent_device(r->unit)->card;
220117035Sgordon
221117692Sobrien	mutex_lock(&r->mutex);
222117035Sgordon
223117035Sgordon	if (r->allocated) {
224139103Sru		bandwidth = r->bandwidth + r->bandwidth_overhead;
225117035Sgordon		fw_iso_resource_manage(card, r->generation, 1uLL << r->channel,
226117035Sgordon				       &channel, &bandwidth, false);
227117035Sgordon		if (channel < 0)
228117035Sgordon			dev_err(&r->unit->device,
229117601Sgordon				"isochronous resource deallocation failed\n");
230117601Sgordon
231139020Sru		r->allocated = false;
232139020Sru	}
233139020Sru
234139020Sru	mutex_unlock(&r->mutex);
235139020Sru}
236117035SgordonEXPORT_SYMBOL(fw_iso_resources_free);
237117692Sobrien