blob: ce3343cd892fcefb3805e217eba25053f0158f71 [file] [log] [blame]
Victor Liuab669fd2024-01-23 11:40:42 -08001/*
2 * This file is part of the UWB stack for linux.
3 *
4 * Copyright (c) 2021 Qorvo US, Inc.
5 *
6 * This software is provided under the GNU General Public License, version 2
7 * (GPLv2), as well as under a Qorvo commercial license.
8 *
9 * You may choose to use this software under the terms of the GPLv2 License,
10 * version 2 ("GPLv2"), as published by the Free Software Foundation.
11 * You should have received a copy of the GPLv2 along with this program. If
12 * not, see <http://www.gnu.org/licenses/>.
13 *
14 * This program is distributed under the GPLv2 in the hope that it will be
15 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GPLv2 for more
17 * details.
18 *
19 * If you cannot meet the requirements of the GPLv2, you may not use this
20 * software for any purpose without first obtaining a commercial license from
21 * Qorvo. Please contact Qorvo to inquire about licensing terms.
22 */
23#include "dw3000_nfcc_coex_mcps.h"
24#include "dw3000_nfcc_coex_msg.h"
25#include "dw3000_nfcc_coex_buffer.h"
26#include "dw3000_nfcc_coex_core.h"
27#include "dw3000.h"
28#include "dw3000_trc.h"
29#include "dw3000_core.h"
30
31#define DW3000_NFCC_COEX_WATCHDOG_DEFAULT_DURATION_MS 24000
Jeremy Rocher20eeeaf2024-02-16 11:50:02 +010032#define DW3000_MARGIN_TO_ENTER_IDLE 2
Victor Liuab669fd2024-01-23 11:40:42 -080033
34static int dw3000_nfcc_coex_wakeup_and_send(struct dw3000 *dw,
35 s32 idle_duration_dtu,
36 u32 send_timestamp_dtu)
37{
38 struct dw3000_nfcc_coex *nfcc_coex = &dw->nfcc_coex;
39 int r;
40
41 trace_dw3000_nfcc_coex_wakeup_and_send(
42 dw, nfcc_coex->send, idle_duration_dtu, send_timestamp_dtu);
43
Jeremy Rocher20eeeaf2024-02-16 11:50:02 +010044 if (idle_duration_dtu >
45 DW3000_MARGIN_TO_ENTER_IDLE * dw->llhw->anticip_dtu) {
46 /* If idle_duration_dtu is too close to anticip_dtu, the
47 * idle_delay computed in dw3000_idle might end up negative
48 * and fail with -ETIME. A DW3000_MARGIN_TO_ENTER_IDLE
49 * multiplicator is used to avoid this.
50 */
Victor Liuab669fd2024-01-23 11:40:42 -080051 r = dw3000_idle(dw, true, send_timestamp_dtu,
52 dw3000_nfcc_coex_idle_timeout,
53 DW3000_OP_STATE_MAX);
54 goto wakeup_and_send_end;
55 } else if (dw->current_operational_state ==
56 DW3000_OP_STATE_DEEP_SLEEP) {
57 r = dw3000_deepsleep_wakeup_now(dw,
58 dw3000_nfcc_coex_idle_timeout,
59 send_timestamp_dtu,
60 DW3000_OP_STATE_MAX);
61 goto wakeup_and_send_end;
62 }
63
64 r = dw3000_nfcc_coex_configure(dw);
65 if (r)
66 goto wakeup_and_send_end;
67 r = dw3000_nfcc_coex_message_send(dw);
68 if (r)
69 goto wakeup_and_send_end;
70 return 0;
71
72wakeup_and_send_end:
73 if (r)
74 dw3000_nfcc_coex_disable(dw);
75 return r;
76}
77
78/**
79 * dw3000_nfcc_coex_handle_access() - handle access to provide to NFCC.
80 * @dw: Driver context.
81 * @data: Address of handle access information.
82 * @data_len: Number of byte of the data object.
83 *
84 * Return: 0 on success, else an error.
85 */
86static int dw3000_nfcc_coex_handle_access(struct dw3000 *dw, void *data,
87 int data_len)
88{
89 const struct llhw_vendor_cmd_nfcc_coex_handle_access *info = data;
90 struct dw3000_nfcc_coex *nfcc_coex = &dw->nfcc_coex;
91 const u32 dtu_per_ms = dw->llhw->dtu_freq_hz / 1000;
92 u32 now_dtu;
93 s32 idle_duration_dtu;
94 int r;
95
96 if (!info || data_len != sizeof(*info))
97 return -EINVAL;
98
99 if (timer_pending(&nfcc_coex->watchdog_timer)) {
100 trace_dw3000_nfcc_coex_err(dw, "watchdog timer is pending");
101 return -EBUSY;
102 }
103
104 r = dw3000_nfcc_coex_enable(dw, info->chan);
105 if (r)
106 return r;
107
108 now_dtu = dw3000_get_dtu_time(dw);
109 idle_duration_dtu = info->timestamp_dtu - now_dtu;
110
111 trace_dw3000_nfcc_coex_handle_access(dw, info, idle_duration_dtu);
112 nfcc_coex->send = info->start ? DW3000_NFCC_COEX_SEND_CLK_SYNC :
113 DW3000_NFCC_COEX_SEND_CLK_OFFSET;
114 if (info->start) {
115 nfcc_coex->version = info->version;
116 /*
117 * Save first start session date, to retrieve MSB bits lost
118 * for next received timestamp through session_time0_dtu.
119 * It's saved because between session_time0_dtu and
120 * access_start_dtu, a deep sleep can occur, and so
121 * `dtu_to_sys_time` must be call after the wake up.
122 * Between access_start_dtu and now, the duration can be less
123 * than 2 ms (fira slot) in multi-region feature.
124 * So the margin is like a second anticip dtu to add to provide
125 * time for NFC handling.
126 */
127 nfcc_coex->access_start_dtu =
128 info->timestamp_dtu + dw3000_nfcc_coex_margin_dtu;
129 }
130 nfcc_coex->watchdog_timer.expires =
131 jiffies +
132 msecs_to_jiffies((info->timestamp_dtu - now_dtu) / dtu_per_ms +
133 DW3000_NFCC_COEX_WATCHDOG_DEFAULT_DURATION_MS);
134 add_timer(&nfcc_coex->watchdog_timer);
135
136 /* Send message and so release the SPI close to the nfc_coex_margin. */
137 return dw3000_nfcc_coex_wakeup_and_send(dw, idle_duration_dtu,
138 info->timestamp_dtu);
139}
140
141/**
142 * dw3000_nfcc_coex_get_access_information() - Forward access info cached.
143 * @dw: Driver context.
144 * @data: Address where to write access information.
145 * @data_len: Number of byte of the data object.
146 *
147 * Return: 0 on success, else an error.
148 */
149static int dw3000_nfcc_coex_get_access_information(struct dw3000 *dw,
150 void *data, int data_len)
151{
152 const struct llhw_vendor_cmd_nfcc_coex_get_access_info *access_info =
153 &dw->nfcc_coex.access_info;
154
155 if (!data || data_len != sizeof(*access_info))
156 return -EINVAL;
157
158 memcpy(data, access_info, data_len);
159 return 0;
160}
161
162/**
163 * dw3000_nfcc_coex_stop() - Stop NFCC.
164 * @dw: Driver context.
165 * @data: Address of stop information.
166 * @data_len: Number of byte of the data object.
167 *
168 * Return: 0 on success, else an error.
169 */
170static int dw3000_nfcc_coex_stop(struct dw3000 *dw, void *data, int data_len)
171{
172 const struct llhw_vendor_cmd_nfcc_coex_stop *info = data;
173 struct dw3000_nfcc_coex *nfcc_coex = &dw->nfcc_coex;
174 const u32 dtu_per_ms = dw->llhw->dtu_freq_hz / 1000;
175 u32 now_dtu, send_timestamp_dtu;
176 s32 idle_duration_dtu;
177 int r;
178
179 if (data_len && data_len != sizeof(*info))
180 return -EINVAL;
181
182 if (timer_pending(&nfcc_coex->watchdog_timer)) {
183 trace_dw3000_nfcc_coex_err(dw, "watchdog timer is pending");
184 return -EBUSY;
185 }
186
187 r = dw3000_nfcc_coex_enable(dw, dw->config.chan);
188 if (r)
189 return r;
190
191 nfcc_coex->send = DW3000_NFCC_COEX_SEND_STOP;
192
193 if (info) {
194 now_dtu = dw3000_get_dtu_time(dw);
195 send_timestamp_dtu = info->timestamp_dtu;
196 idle_duration_dtu = send_timestamp_dtu - now_dtu;
197 nfcc_coex->watchdog_timer.expires =
198 jiffies +
199 msecs_to_jiffies(
200 (info->timestamp_dtu - now_dtu) / dtu_per_ms +
201 DW3000_NFCC_COEX_WATCHDOG_DEFAULT_DURATION_MS);
202 nfcc_coex->version = info->version;
203 } else {
204 send_timestamp_dtu = 0;
205 idle_duration_dtu = 0;
206 nfcc_coex->watchdog_timer.expires =
207 jiffies +
208 msecs_to_jiffies(
209 DW3000_NFCC_COEX_WATCHDOG_DEFAULT_DURATION_MS);
210 /* Cancel wakeup timer launch by idle() */
211 dw3000_idle_cancel_timer(dw);
212 }
213
214 add_timer(&nfcc_coex->watchdog_timer);
215
216 /* Send message and so release the SPI close to the nfc_coex_margin. */
217 return dw3000_nfcc_coex_wakeup_and_send(dw, idle_duration_dtu,
218 send_timestamp_dtu);
219}
220
221/**
222 * dw3000_nfcc_coex_vendor_cmd() - Vendor NFCC coexistence command processing.
223 *
224 * @dw: Driver context.
225 * @vendor_id: Vendor Identifier on 3 bytes.
226 * @subcmd: Sub-command identifier.
227 * @data: Null or data related with the sub-command.
228 * @data_len: Length of the data
229 *
230 * Return: 0 on success, 1 to request a stop, error on other value.
231 */
232int dw3000_nfcc_coex_vendor_cmd(struct dw3000 *dw, u32 vendor_id, u32 subcmd,
233 void *data, size_t data_len)
234{
235 /* NFCC needs a D0 chip or above. C0 does not have 2 SPI interfaces. */
236 if (__dw3000_chip_version == DW3000_C0_VERSION)
237 return -EOPNOTSUPP;
238
239 switch (subcmd) {
240 case LLHW_VENDOR_CMD_NFCC_COEX_HANDLE_ACCESS:
241 return dw3000_nfcc_coex_handle_access(dw, data, data_len);
242 case LLHW_VENDOR_CMD_NFCC_COEX_GET_ACCESS_INFORMATION:
243 return dw3000_nfcc_coex_get_access_information(dw, data,
244 data_len);
245 case LLHW_VENDOR_CMD_NFCC_COEX_STOP:
246 return dw3000_nfcc_coex_stop(dw, data, data_len);
247 default:
248 return -EINVAL;
249 }
250}