Commit | Line | Data |
---|---|---|
da607e19 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
2e76701b TS |
2 | /* |
3 | * motu-transaction.c - a part of driver for MOTU FireWire series | |
4 | * | |
5 | * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp> | |
2e76701b TS |
6 | */ |
7 | ||
8 | ||
9 | #include "motu.h" | |
10 | ||
11 | #define SND_MOTU_ADDR_BASE 0xfffff0000000ULL | |
12 | #define ASYNC_ADDR_HI 0x0b04 | |
13 | #define ASYNC_ADDR_LO 0x0b08 | |
14 | ||
15 | int snd_motu_transaction_read(struct snd_motu *motu, u32 offset, __be32 *reg, | |
16 | size_t size) | |
17 | { | |
18 | int tcode; | |
19 | ||
20 | if (size % sizeof(__be32) > 0 || size <= 0) | |
21 | return -EINVAL; | |
22 | if (size == sizeof(__be32)) | |
23 | tcode = TCODE_READ_QUADLET_REQUEST; | |
24 | else | |
25 | tcode = TCODE_READ_BLOCK_REQUEST; | |
26 | ||
27 | return snd_fw_transaction(motu->unit, tcode, | |
28 | SND_MOTU_ADDR_BASE + offset, reg, size, 0); | |
29 | } | |
30 | ||
31 | int snd_motu_transaction_write(struct snd_motu *motu, u32 offset, __be32 *reg, | |
32 | size_t size) | |
33 | { | |
34 | int tcode; | |
35 | ||
36 | if (size % sizeof(__be32) > 0 || size <= 0) | |
37 | return -EINVAL; | |
38 | if (size == sizeof(__be32)) | |
39 | tcode = TCODE_WRITE_QUADLET_REQUEST; | |
40 | else | |
41 | tcode = TCODE_WRITE_BLOCK_REQUEST; | |
42 | ||
43 | return snd_fw_transaction(motu->unit, tcode, | |
44 | SND_MOTU_ADDR_BASE + offset, reg, size, 0); | |
45 | } | |
46 | ||
47 | static void handle_message(struct fw_card *card, struct fw_request *request, | |
48 | int tcode, int destination, int source, | |
49 | int generation, unsigned long long offset, | |
50 | void *data, size_t length, void *callback_data) | |
51 | { | |
5aaab1bf TS |
52 | struct snd_motu *motu = callback_data; |
53 | __be32 *buf = (__be32 *)data; | |
54 | unsigned long flags; | |
55 | ||
56 | if (tcode != TCODE_WRITE_QUADLET_REQUEST) { | |
57 | fw_send_response(card, request, RCODE_COMPLETE); | |
58 | return; | |
59 | } | |
60 | ||
61 | if (offset != motu->async_handler.offset || length != 4) { | |
62 | fw_send_response(card, request, RCODE_ADDRESS_ERROR); | |
63 | return; | |
64 | } | |
65 | ||
66 | spin_lock_irqsave(&motu->lock, flags); | |
67 | motu->msg = be32_to_cpu(*buf); | |
68 | spin_unlock_irqrestore(&motu->lock, flags); | |
69 | ||
2e76701b | 70 | fw_send_response(card, request, RCODE_COMPLETE); |
5aaab1bf TS |
71 | |
72 | wake_up(&motu->hwdep_wait); | |
2e76701b TS |
73 | } |
74 | ||
75 | int snd_motu_transaction_reregister(struct snd_motu *motu) | |
76 | { | |
77 | struct fw_device *device = fw_parent_device(motu->unit); | |
78 | __be32 data; | |
79 | int err; | |
80 | ||
81 | if (motu->async_handler.callback_data == NULL) | |
82 | return -EINVAL; | |
83 | ||
84 | /* Register messaging address. Block transaction is not allowed. */ | |
85 | data = cpu_to_be32((device->card->node_id << 16) | | |
86 | (motu->async_handler.offset >> 32)); | |
87 | err = snd_motu_transaction_write(motu, ASYNC_ADDR_HI, &data, | |
88 | sizeof(data)); | |
89 | if (err < 0) | |
90 | return err; | |
91 | ||
92 | data = cpu_to_be32(motu->async_handler.offset); | |
93 | return snd_motu_transaction_write(motu, ASYNC_ADDR_LO, &data, | |
94 | sizeof(data)); | |
95 | } | |
96 | ||
97 | int snd_motu_transaction_register(struct snd_motu *motu) | |
98 | { | |
99 | static const struct fw_address_region resp_register_region = { | |
100 | .start = 0xffffe0000000ull, | |
101 | .end = 0xffffe000ffffull, | |
102 | }; | |
103 | int err; | |
104 | ||
105 | /* Perhaps, 4 byte messages are transferred. */ | |
106 | motu->async_handler.length = 4; | |
107 | motu->async_handler.address_callback = handle_message; | |
108 | motu->async_handler.callback_data = motu; | |
109 | ||
110 | err = fw_core_add_address_handler(&motu->async_handler, | |
111 | &resp_register_region); | |
112 | if (err < 0) | |
113 | return err; | |
114 | ||
115 | err = snd_motu_transaction_reregister(motu); | |
116 | if (err < 0) { | |
117 | fw_core_remove_address_handler(&motu->async_handler); | |
118 | motu->async_handler.address_callback = NULL; | |
119 | } | |
120 | ||
121 | return err; | |
122 | } | |
123 | ||
124 | void snd_motu_transaction_unregister(struct snd_motu *motu) | |
125 | { | |
126 | __be32 data; | |
127 | ||
128 | if (motu->async_handler.address_callback != NULL) | |
129 | fw_core_remove_address_handler(&motu->async_handler); | |
130 | motu->async_handler.address_callback = NULL; | |
131 | ||
132 | /* Unregister the address. */ | |
133 | data = cpu_to_be32(0x00000000); | |
134 | snd_motu_transaction_write(motu, ASYNC_ADDR_HI, &data, sizeof(data)); | |
135 | snd_motu_transaction_write(motu, ASYNC_ADDR_LO, &data, sizeof(data)); | |
136 | } |