xref: /openbmc/linux/sound/firewire/dice/dice-transaction.c (revision 1a4e39c2e5ca2eb494a53ecd73055562f690bca0)
1 /*
2  * dice_transaction.c - a part of driver for Dice based devices
3  *
4  * Copyright (c) Clemens Ladisch
5  * Copyright (c) 2014 Takashi Sakamoto
6  *
7  * Licensed under the terms of the GNU General Public License, version 2.
8  */
9 
10 #include "dice.h"
11 
12 #define NOTIFICATION_TIMEOUT_MS	100
13 
14 static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type,
15 		       u64 offset)
16 {
17 	switch (type) {
18 	case SND_DICE_ADDR_TYPE_TX:
19 		offset += dice->tx_offset;
20 		break;
21 	case SND_DICE_ADDR_TYPE_RX:
22 		offset += dice->rx_offset;
23 		break;
24 	case SND_DICE_ADDR_TYPE_SYNC:
25 		offset += dice->sync_offset;
26 		break;
27 	case SND_DICE_ADDR_TYPE_RSRV:
28 		offset += dice->rsrv_offset;
29 		break;
30 	case SND_DICE_ADDR_TYPE_GLOBAL:
31 	default:
32 		offset += dice->global_offset;
33 		break;
34 	};
35 	offset += DICE_PRIVATE_SPACE;
36 	return offset;
37 }
38 
39 int snd_dice_transaction_write(struct snd_dice *dice,
40 			       enum snd_dice_addr_type type,
41 			       unsigned int offset, void *buf, unsigned int len)
42 {
43 	return snd_fw_transaction(dice->unit,
44 				  (len == 4) ? TCODE_WRITE_QUADLET_REQUEST :
45 					       TCODE_WRITE_BLOCK_REQUEST,
46 				  get_subaddr(dice, type, offset), buf, len, 0);
47 }
48 
49 int snd_dice_transaction_read(struct snd_dice *dice,
50 			      enum snd_dice_addr_type type, unsigned int offset,
51 			      void *buf, unsigned int len)
52 {
53 	return snd_fw_transaction(dice->unit,
54 				  (len == 4) ? TCODE_READ_QUADLET_REQUEST :
55 					       TCODE_READ_BLOCK_REQUEST,
56 				  get_subaddr(dice, type, offset), buf, len, 0);
57 }
58 
59 static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info)
60 {
61 	return snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
62 						info, 4);
63 }
64 
65 static int set_clock_info(struct snd_dice *dice,
66 			  unsigned int rate, unsigned int source)
67 {
68 	unsigned int retries = 3;
69 	unsigned int i;
70 	__be32 info;
71 	u32 mask;
72 	u32 clock;
73 	int err;
74 retry:
75 	err = get_clock_info(dice, &info);
76 	if (err < 0)
77 		goto end;
78 
79 	clock = be32_to_cpu(info);
80 	if (source != UINT_MAX) {
81 		mask = CLOCK_SOURCE_MASK;
82 		clock &= ~mask;
83 		clock |= source;
84 	}
85 	if (rate != UINT_MAX) {
86 		for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
87 			if (snd_dice_rates[i] == rate)
88 				break;
89 		}
90 		if (i == ARRAY_SIZE(snd_dice_rates)) {
91 			err = -EINVAL;
92 			goto end;
93 		}
94 
95 		mask = CLOCK_RATE_MASK;
96 		clock &= ~mask;
97 		clock |= i << CLOCK_RATE_SHIFT;
98 	}
99 	info = cpu_to_be32(clock);
100 
101 	if (completion_done(&dice->clock_accepted))
102 		reinit_completion(&dice->clock_accepted);
103 
104 	err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
105 						&info, 4);
106 	if (err < 0)
107 		goto end;
108 
109 	/* Timeout means it's invalid request, probably bus reset occurred. */
110 	if (wait_for_completion_timeout(&dice->clock_accepted,
111 			msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) {
112 		if (retries-- == 0) {
113 			err = -ETIMEDOUT;
114 			goto end;
115 		}
116 
117 		err = snd_dice_transaction_reinit(dice);
118 		if (err < 0)
119 			goto end;
120 
121 		msleep(500);	/* arbitrary */
122 		goto retry;
123 	}
124 end:
125 	return err;
126 }
127 
128 int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
129 					  unsigned int *source)
130 {
131 	__be32 info;
132 	int err;
133 
134 	err = get_clock_info(dice, &info);
135 	if (err >= 0)
136 		*source = be32_to_cpu(info) & CLOCK_SOURCE_MASK;
137 
138 	return err;
139 }
140 int snd_dice_transaction_set_clock_source(struct snd_dice *dice,
141 					  unsigned int source)
142 {
143 	return set_clock_info(dice, UINT_MAX, source);
144 }
145 
146 int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate)
147 {
148 	__be32 info;
149 	unsigned int index;
150 	int err;
151 
152 	err = get_clock_info(dice, &info);
153 	if (err < 0)
154 		goto end;
155 
156 	index = (be32_to_cpu(info) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT;
157 	if (index >= SND_DICE_RATES_COUNT) {
158 		err = -ENOSYS;
159 		goto end;
160 	}
161 
162 	*rate = snd_dice_rates[index];
163 end:
164 	return err;
165 }
166 int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate)
167 {
168 	return set_clock_info(dice, rate, UINT_MAX);
169 }
170 
171 int snd_dice_transaction_set_enable(struct snd_dice *dice)
172 {
173 	__be32 value;
174 	int err = 0;
175 
176 	if (dice->global_enabled)
177 		goto end;
178 
179 	value = cpu_to_be32(1);
180 	err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
181 				 get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
182 					     GLOBAL_ENABLE),
183 				 &value, 4,
184 				 FW_FIXED_GENERATION | dice->owner_generation);
185 	if (err < 0)
186 		goto end;
187 
188 	dice->global_enabled = true;
189 end:
190 	return err;
191 }
192 
193 void snd_dice_transaction_clear_enable(struct snd_dice *dice)
194 {
195 	__be32 value;
196 
197 	value = 0;
198 	snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
199 			   get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
200 				       GLOBAL_ENABLE),
201 			   &value, 4, FW_QUIET |
202 			   FW_FIXED_GENERATION | dice->owner_generation);
203 
204 	dice->global_enabled = false;
205 }
206 
207 static void dice_notification(struct fw_card *card, struct fw_request *request,
208 			      int tcode, int destination, int source,
209 			      int generation, unsigned long long offset,
210 			      void *data, size_t length, void *callback_data)
211 {
212 	struct snd_dice *dice = callback_data;
213 	u32 bits;
214 	unsigned long flags;
215 
216 	if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
217 		fw_send_response(card, request, RCODE_TYPE_ERROR);
218 		return;
219 	}
220 	if ((offset & 3) != 0) {
221 		fw_send_response(card, request, RCODE_ADDRESS_ERROR);
222 		return;
223 	}
224 
225 	bits = be32_to_cpup(data);
226 
227 	spin_lock_irqsave(&dice->lock, flags);
228 	dice->notification_bits |= bits;
229 	spin_unlock_irqrestore(&dice->lock, flags);
230 
231 	fw_send_response(card, request, RCODE_COMPLETE);
232 
233 	if (bits & NOTIFY_CLOCK_ACCEPTED)
234 		complete(&dice->clock_accepted);
235 	wake_up(&dice->hwdep_wait);
236 }
237 
238 static int register_notification_address(struct snd_dice *dice, bool retry)
239 {
240 	struct fw_device *device = fw_parent_device(dice->unit);
241 	__be64 *buffer;
242 	unsigned int retries;
243 	int err;
244 
245 	retries = (retry) ? 3 : 0;
246 
247 	buffer = kmalloc(2 * 8, GFP_KERNEL);
248 	if (!buffer)
249 		return -ENOMEM;
250 
251 	for (;;) {
252 		buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
253 		buffer[1] = cpu_to_be64(
254 			((u64)device->card->node_id << OWNER_NODE_SHIFT) |
255 			dice->notification_handler.offset);
256 
257 		dice->owner_generation = device->generation;
258 		smp_rmb(); /* node_id vs. generation */
259 		err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
260 					 get_subaddr(dice,
261 						     SND_DICE_ADDR_TYPE_GLOBAL,
262 						     GLOBAL_OWNER),
263 					 buffer, 2 * 8,
264 					 FW_FIXED_GENERATION |
265 							dice->owner_generation);
266 		if (err == 0) {
267 			/* success */
268 			if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER))
269 				break;
270 			/* The address seems to be already registered. */
271 			if (buffer[0] == buffer[1])
272 				break;
273 
274 			dev_err(&dice->unit->device,
275 				"device is already in use\n");
276 			err = -EBUSY;
277 		}
278 		if (err != -EAGAIN || retries-- > 0)
279 			break;
280 
281 		msleep(20);
282 	}
283 
284 	kfree(buffer);
285 
286 	if (err < 0)
287 		dice->owner_generation = -1;
288 
289 	return err;
290 }
291 
292 static void unregister_notification_address(struct snd_dice *dice)
293 {
294 	struct fw_device *device = fw_parent_device(dice->unit);
295 	__be64 *buffer;
296 
297 	buffer = kmalloc(2 * 8, GFP_KERNEL);
298 	if (buffer == NULL)
299 		return;
300 
301 	buffer[0] = cpu_to_be64(
302 		((u64)device->card->node_id << OWNER_NODE_SHIFT) |
303 		dice->notification_handler.offset);
304 	buffer[1] = cpu_to_be64(OWNER_NO_OWNER);
305 	snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
306 			   get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
307 				       GLOBAL_OWNER),
308 			   buffer, 2 * 8, FW_QUIET |
309 			   FW_FIXED_GENERATION | dice->owner_generation);
310 
311 	kfree(buffer);
312 
313 	dice->owner_generation = -1;
314 }
315 
316 void snd_dice_transaction_destroy(struct snd_dice *dice)
317 {
318 	struct fw_address_handler *handler = &dice->notification_handler;
319 
320 	if (handler->callback_data == NULL)
321 		return;
322 
323 	unregister_notification_address(dice);
324 
325 	fw_core_remove_address_handler(handler);
326 	handler->callback_data = NULL;
327 }
328 
329 int snd_dice_transaction_reinit(struct snd_dice *dice)
330 {
331 	struct fw_address_handler *handler = &dice->notification_handler;
332 
333 	if (handler->callback_data == NULL)
334 		return -EINVAL;
335 
336 	return register_notification_address(dice, false);
337 }
338 
339 int snd_dice_transaction_init(struct snd_dice *dice)
340 {
341 	struct fw_address_handler *handler = &dice->notification_handler;
342 	__be32 *pointers;
343 	int err;
344 
345 	/* Use the same way which dice_interface_check() does. */
346 	pointers = kmalloc(sizeof(__be32) * 10, GFP_KERNEL);
347 	if (pointers == NULL)
348 		return -ENOMEM;
349 
350 	/* Get offsets for sub-addresses */
351 	err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
352 				 DICE_PRIVATE_SPACE,
353 				 pointers, sizeof(__be32) * 10, 0);
354 	if (err < 0)
355 		goto end;
356 
357 	/* Allocation callback in address space over host controller */
358 	handler->length = 4;
359 	handler->address_callback = dice_notification;
360 	handler->callback_data = dice;
361 	err = fw_core_add_address_handler(handler, &fw_high_memory_region);
362 	if (err < 0) {
363 		handler->callback_data = NULL;
364 		goto end;
365 	}
366 
367 	/* Register the address space */
368 	err = register_notification_address(dice, true);
369 	if (err < 0) {
370 		fw_core_remove_address_handler(handler);
371 		handler->callback_data = NULL;
372 		goto end;
373 	}
374 
375 	dice->global_offset = be32_to_cpu(pointers[0]) * 4;
376 	dice->tx_offset = be32_to_cpu(pointers[2]) * 4;
377 	dice->rx_offset = be32_to_cpu(pointers[4]) * 4;
378 	dice->sync_offset = be32_to_cpu(pointers[6]) * 4;
379 	dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4;
380 
381 	/* Set up later. */
382 	if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4)
383 		dice->clock_caps = 1;
384 end:
385 	kfree(pointers);
386 	return err;
387 }
388