1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (C) 2021-2022, Intel Corporation. */ |
3 | |
4 | #include "ice.h" |
5 | #include "ice_lib.h" |
6 | |
7 | /** |
8 | * ice_gnss_do_write - Write data to internal GNSS receiver |
9 | * @pf: board private structure |
10 | * @buf: command buffer |
11 | * @size: command buffer size |
12 | * |
13 | * Write UBX command data to the GNSS receiver |
14 | * |
15 | * Return: |
16 | * * number of bytes written - success |
17 | * * negative - error code |
18 | */ |
19 | static int |
20 | ice_gnss_do_write(struct ice_pf *pf, const unsigned char *buf, unsigned int size) |
21 | { |
22 | struct ice_aqc_link_topo_addr link_topo; |
23 | struct ice_hw *hw = &pf->hw; |
24 | unsigned int offset = 0; |
25 | int err = 0; |
26 | |
27 | memset(&link_topo, 0, sizeof(struct ice_aqc_link_topo_addr)); |
28 | link_topo.topo_params.index = ICE_E810T_GNSS_I2C_BUS; |
29 | link_topo.topo_params.node_type_ctx |= |
30 | FIELD_PREP(ICE_AQC_LINK_TOPO_NODE_CTX_M, |
31 | ICE_AQC_LINK_TOPO_NODE_CTX_OVERRIDE); |
32 | |
33 | /* It's not possible to write a single byte to u-blox. |
34 | * Write all bytes in a loop until there are 6 or less bytes left. If |
35 | * there are exactly 6 bytes left, the last write would be only a byte. |
36 | * In this case, do 4+2 bytes writes instead of 5+1. Otherwise, do the |
37 | * last 2 to 5 bytes write. |
38 | */ |
39 | while (size - offset > ICE_GNSS_UBX_WRITE_BYTES + 1) { |
40 | err = ice_aq_write_i2c(hw, topo_addr: link_topo, ICE_GNSS_UBX_I2C_BUS_ADDR, |
41 | cpu_to_le16(buf[offset]), |
42 | ICE_MAX_I2C_WRITE_BYTES, |
43 | data: &buf[offset + 1], NULL); |
44 | if (err) |
45 | goto err_out; |
46 | |
47 | offset += ICE_GNSS_UBX_WRITE_BYTES; |
48 | } |
49 | |
50 | /* Single byte would be written. Write 4 bytes instead of 5. */ |
51 | if (size - offset == ICE_GNSS_UBX_WRITE_BYTES + 1) { |
52 | err = ice_aq_write_i2c(hw, topo_addr: link_topo, ICE_GNSS_UBX_I2C_BUS_ADDR, |
53 | cpu_to_le16(buf[offset]), |
54 | ICE_MAX_I2C_WRITE_BYTES - 1, |
55 | data: &buf[offset + 1], NULL); |
56 | if (err) |
57 | goto err_out; |
58 | |
59 | offset += ICE_GNSS_UBX_WRITE_BYTES - 1; |
60 | } |
61 | |
62 | /* Do the last write, 2 to 5 bytes. */ |
63 | err = ice_aq_write_i2c(hw, topo_addr: link_topo, ICE_GNSS_UBX_I2C_BUS_ADDR, |
64 | cpu_to_le16(buf[offset]), params: size - offset - 1, |
65 | data: &buf[offset + 1], NULL); |
66 | if (err) |
67 | goto err_out; |
68 | |
69 | return size; |
70 | |
71 | err_out: |
72 | dev_err(ice_pf_to_dev(pf), "GNSS failed to write, offset=%u, size=%u, err=%d\n" , |
73 | offset, size, err); |
74 | |
75 | return err; |
76 | } |
77 | |
78 | /** |
79 | * ice_gnss_read - Read data from internal GNSS module |
80 | * @work: GNSS read work structure |
81 | * |
82 | * Read the data from internal GNSS receiver, write it to gnss_dev. |
83 | */ |
84 | static void ice_gnss_read(struct kthread_work *work) |
85 | { |
86 | struct gnss_serial *gnss = container_of(work, struct gnss_serial, |
87 | read_work.work); |
88 | unsigned long delay = ICE_GNSS_POLL_DATA_DELAY_TIME; |
89 | unsigned int i, bytes_read, data_len, count; |
90 | struct ice_aqc_link_topo_addr link_topo; |
91 | struct ice_pf *pf; |
92 | struct ice_hw *hw; |
93 | __be16 data_len_b; |
94 | char *buf = NULL; |
95 | u8 i2c_params; |
96 | int err = 0; |
97 | |
98 | pf = gnss->back; |
99 | if (!pf || !test_bit(ICE_FLAG_GNSS, pf->flags)) |
100 | return; |
101 | |
102 | hw = &pf->hw; |
103 | |
104 | memset(&link_topo, 0, sizeof(struct ice_aqc_link_topo_addr)); |
105 | link_topo.topo_params.index = ICE_E810T_GNSS_I2C_BUS; |
106 | link_topo.topo_params.node_type_ctx |= |
107 | FIELD_PREP(ICE_AQC_LINK_TOPO_NODE_CTX_M, |
108 | ICE_AQC_LINK_TOPO_NODE_CTX_OVERRIDE); |
109 | |
110 | i2c_params = ICE_GNSS_UBX_DATA_LEN_WIDTH | |
111 | ICE_AQC_I2C_USE_REPEATED_START; |
112 | |
113 | err = ice_aq_read_i2c(hw, topo_addr: link_topo, ICE_GNSS_UBX_I2C_BUS_ADDR, |
114 | cpu_to_le16(ICE_GNSS_UBX_DATA_LEN_H), |
115 | params: i2c_params, data: (u8 *)&data_len_b, NULL); |
116 | if (err) |
117 | goto requeue; |
118 | |
119 | data_len = be16_to_cpu(data_len_b); |
120 | if (data_len == 0 || data_len == U16_MAX) |
121 | goto requeue; |
122 | |
123 | /* The u-blox has data_len bytes for us to read */ |
124 | |
125 | data_len = min_t(typeof(data_len), data_len, PAGE_SIZE); |
126 | |
127 | buf = (char *)get_zeroed_page(GFP_KERNEL); |
128 | if (!buf) { |
129 | err = -ENOMEM; |
130 | goto requeue; |
131 | } |
132 | |
133 | /* Read received data */ |
134 | for (i = 0; i < data_len; i += bytes_read) { |
135 | unsigned int bytes_left = data_len - i; |
136 | |
137 | bytes_read = min_t(typeof(bytes_left), bytes_left, |
138 | ICE_MAX_I2C_DATA_SIZE); |
139 | |
140 | err = ice_aq_read_i2c(hw, topo_addr: link_topo, ICE_GNSS_UBX_I2C_BUS_ADDR, |
141 | cpu_to_le16(ICE_GNSS_UBX_EMPTY_DATA), |
142 | params: bytes_read, data: &buf[i], NULL); |
143 | if (err) |
144 | goto free_buf; |
145 | } |
146 | |
147 | count = gnss_insert_raw(gdev: pf->gnss_dev, buf, count: i); |
148 | if (count != i) |
149 | dev_warn(ice_pf_to_dev(pf), |
150 | "gnss_insert_raw ret=%d size=%d\n" , |
151 | count, i); |
152 | delay = ICE_GNSS_TIMER_DELAY_TIME; |
153 | free_buf: |
154 | free_page((unsigned long)buf); |
155 | requeue: |
156 | kthread_queue_delayed_work(worker: gnss->kworker, dwork: &gnss->read_work, delay); |
157 | if (err) |
158 | dev_dbg(ice_pf_to_dev(pf), "GNSS failed to read err=%d\n" , err); |
159 | } |
160 | |
161 | /** |
162 | * ice_gnss_struct_init - Initialize GNSS receiver |
163 | * @pf: Board private structure |
164 | * |
165 | * Initialize GNSS structures and workers. |
166 | * |
167 | * Return: |
168 | * * pointer to initialized gnss_serial struct - success |
169 | * * NULL - error |
170 | */ |
171 | static struct gnss_serial *ice_gnss_struct_init(struct ice_pf *pf) |
172 | { |
173 | struct device *dev = ice_pf_to_dev(pf); |
174 | struct kthread_worker *kworker; |
175 | struct gnss_serial *gnss; |
176 | |
177 | gnss = kzalloc(size: sizeof(*gnss), GFP_KERNEL); |
178 | if (!gnss) |
179 | return NULL; |
180 | |
181 | gnss->back = pf; |
182 | pf->gnss_serial = gnss; |
183 | |
184 | kthread_init_delayed_work(&gnss->read_work, ice_gnss_read); |
185 | kworker = kthread_create_worker(flags: 0, namefmt: "ice-gnss-%s" , dev_name(dev)); |
186 | if (IS_ERR(ptr: kworker)) { |
187 | kfree(objp: gnss); |
188 | return NULL; |
189 | } |
190 | |
191 | gnss->kworker = kworker; |
192 | |
193 | return gnss; |
194 | } |
195 | |
196 | /** |
197 | * ice_gnss_open - Open GNSS device |
198 | * @gdev: pointer to the gnss device struct |
199 | * |
200 | * Open GNSS device and start filling the read buffer for consumer. |
201 | * |
202 | * Return: |
203 | * * 0 - success |
204 | * * negative - error code |
205 | */ |
206 | static int ice_gnss_open(struct gnss_device *gdev) |
207 | { |
208 | struct ice_pf *pf = gnss_get_drvdata(gdev); |
209 | struct gnss_serial *gnss; |
210 | |
211 | if (!pf) |
212 | return -EFAULT; |
213 | |
214 | if (!test_bit(ICE_FLAG_GNSS, pf->flags)) |
215 | return -EFAULT; |
216 | |
217 | gnss = pf->gnss_serial; |
218 | if (!gnss) |
219 | return -ENODEV; |
220 | |
221 | kthread_queue_delayed_work(worker: gnss->kworker, dwork: &gnss->read_work, delay: 0); |
222 | |
223 | return 0; |
224 | } |
225 | |
226 | /** |
227 | * ice_gnss_close - Close GNSS device |
228 | * @gdev: pointer to the gnss device struct |
229 | * |
230 | * Close GNSS device, cancel worker, stop filling the read buffer. |
231 | */ |
232 | static void ice_gnss_close(struct gnss_device *gdev) |
233 | { |
234 | struct ice_pf *pf = gnss_get_drvdata(gdev); |
235 | struct gnss_serial *gnss; |
236 | |
237 | if (!pf) |
238 | return; |
239 | |
240 | gnss = pf->gnss_serial; |
241 | if (!gnss) |
242 | return; |
243 | |
244 | kthread_cancel_delayed_work_sync(work: &gnss->read_work); |
245 | } |
246 | |
247 | /** |
248 | * ice_gnss_write - Write to GNSS device |
249 | * @gdev: pointer to the gnss device struct |
250 | * @buf: pointer to the user data |
251 | * @count: size of the buffer to be sent to the GNSS device |
252 | * |
253 | * Return: |
254 | * * number of written bytes - success |
255 | * * negative - error code |
256 | */ |
257 | static int |
258 | ice_gnss_write(struct gnss_device *gdev, const unsigned char *buf, |
259 | size_t count) |
260 | { |
261 | struct ice_pf *pf = gnss_get_drvdata(gdev); |
262 | struct gnss_serial *gnss; |
263 | |
264 | /* We cannot write a single byte using our I2C implementation. */ |
265 | if (count <= 1 || count > ICE_GNSS_TTY_WRITE_BUF) |
266 | return -EINVAL; |
267 | |
268 | if (!pf) |
269 | return -EFAULT; |
270 | |
271 | if (!test_bit(ICE_FLAG_GNSS, pf->flags)) |
272 | return -EFAULT; |
273 | |
274 | gnss = pf->gnss_serial; |
275 | if (!gnss) |
276 | return -ENODEV; |
277 | |
278 | return ice_gnss_do_write(pf, buf, size: count); |
279 | } |
280 | |
281 | static const struct gnss_operations ice_gnss_ops = { |
282 | .open = ice_gnss_open, |
283 | .close = ice_gnss_close, |
284 | .write_raw = ice_gnss_write, |
285 | }; |
286 | |
287 | /** |
288 | * ice_gnss_register - Register GNSS receiver |
289 | * @pf: Board private structure |
290 | * |
291 | * Allocate and register GNSS receiver in the Linux GNSS subsystem. |
292 | * |
293 | * Return: |
294 | * * 0 - success |
295 | * * negative - error code |
296 | */ |
297 | static int ice_gnss_register(struct ice_pf *pf) |
298 | { |
299 | struct gnss_device *gdev; |
300 | int ret; |
301 | |
302 | gdev = gnss_allocate_device(ice_pf_to_dev(pf)); |
303 | if (!gdev) { |
304 | dev_err(ice_pf_to_dev(pf), |
305 | "gnss_allocate_device returns NULL\n" ); |
306 | return -ENOMEM; |
307 | } |
308 | |
309 | gdev->ops = &ice_gnss_ops; |
310 | gdev->type = GNSS_TYPE_UBX; |
311 | gnss_set_drvdata(gdev, data: pf); |
312 | ret = gnss_register_device(gdev); |
313 | if (ret) { |
314 | dev_err(ice_pf_to_dev(pf), "gnss_register_device err=%d\n" , |
315 | ret); |
316 | gnss_put_device(gdev); |
317 | } else { |
318 | pf->gnss_dev = gdev; |
319 | } |
320 | |
321 | return ret; |
322 | } |
323 | |
324 | /** |
325 | * ice_gnss_deregister - Deregister GNSS receiver |
326 | * @pf: Board private structure |
327 | * |
328 | * Deregister GNSS receiver from the Linux GNSS subsystem, |
329 | * release its resources. |
330 | */ |
331 | static void ice_gnss_deregister(struct ice_pf *pf) |
332 | { |
333 | if (pf->gnss_dev) { |
334 | gnss_deregister_device(gdev: pf->gnss_dev); |
335 | gnss_put_device(gdev: pf->gnss_dev); |
336 | pf->gnss_dev = NULL; |
337 | } |
338 | } |
339 | |
340 | /** |
341 | * ice_gnss_init - Initialize GNSS support |
342 | * @pf: Board private structure |
343 | */ |
344 | void ice_gnss_init(struct ice_pf *pf) |
345 | { |
346 | int ret; |
347 | |
348 | pf->gnss_serial = ice_gnss_struct_init(pf); |
349 | if (!pf->gnss_serial) |
350 | return; |
351 | |
352 | ret = ice_gnss_register(pf); |
353 | if (!ret) { |
354 | set_bit(nr: ICE_FLAG_GNSS, addr: pf->flags); |
355 | dev_info(ice_pf_to_dev(pf), "GNSS init successful\n" ); |
356 | } else { |
357 | ice_gnss_exit(pf); |
358 | dev_err(ice_pf_to_dev(pf), "GNSS init failure\n" ); |
359 | } |
360 | } |
361 | |
362 | /** |
363 | * ice_gnss_exit - Disable GNSS TTY support |
364 | * @pf: Board private structure |
365 | */ |
366 | void ice_gnss_exit(struct ice_pf *pf) |
367 | { |
368 | ice_gnss_deregister(pf); |
369 | clear_bit(nr: ICE_FLAG_GNSS, addr: pf->flags); |
370 | |
371 | if (pf->gnss_serial) { |
372 | struct gnss_serial *gnss = pf->gnss_serial; |
373 | |
374 | kthread_cancel_delayed_work_sync(work: &gnss->read_work); |
375 | kthread_destroy_worker(worker: gnss->kworker); |
376 | gnss->kworker = NULL; |
377 | |
378 | kfree(objp: gnss); |
379 | pf->gnss_serial = NULL; |
380 | } |
381 | } |
382 | |
383 | /** |
384 | * ice_gnss_is_gps_present - Check if GPS HW is present |
385 | * @hw: pointer to HW struct |
386 | */ |
387 | bool ice_gnss_is_gps_present(struct ice_hw *hw) |
388 | { |
389 | if (!hw->func_caps.ts_func_info.src_tmr_owned) |
390 | return false; |
391 | |
392 | if (!ice_is_gps_in_netlist(hw)) |
393 | return false; |
394 | |
395 | #if IS_ENABLED(CONFIG_PTP_1588_CLOCK) |
396 | if (ice_is_e810t(hw)) { |
397 | int err; |
398 | u8 data; |
399 | |
400 | err = ice_read_pca9575_reg_e810t(hw, ICE_PCA9575_P0_IN, data: &data); |
401 | if (err || !!(data & ICE_E810T_P0_GNSS_PRSNT_N)) |
402 | return false; |
403 | } else { |
404 | return false; |
405 | } |
406 | #else |
407 | if (!ice_is_e810t(hw)) |
408 | return false; |
409 | #endif /* IS_ENABLED(CONFIG_PTP_1588_CLOCK) */ |
410 | |
411 | return true; |
412 | } |
413 | |