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