143113ff7SKarol Kolacinski // SPDX-License-Identifier: GPL-2.0
2d6b98c8dSKarol Kolacinski /* Copyright (C) 2021-2022, Intel Corporation. */
343113ff7SKarol Kolacinski 
443113ff7SKarol Kolacinski #include "ice.h"
543113ff7SKarol Kolacinski #include "ice_lib.h"
643113ff7SKarol Kolacinski 
743113ff7SKarol Kolacinski /**
8c7ef8221SArkadiusz Kubalewski  * ice_gnss_do_write - Write data to internal GNSS receiver
9d6b98c8dSKarol Kolacinski  * @pf: board private structure
10d6b98c8dSKarol Kolacinski  * @buf: command buffer
11d6b98c8dSKarol Kolacinski  * @size: command buffer size
12d6b98c8dSKarol Kolacinski  *
13d6b98c8dSKarol Kolacinski  * Write UBX command data to the GNSS receiver
14c7ef8221SArkadiusz Kubalewski  *
15c7ef8221SArkadiusz Kubalewski  * Return:
16c7ef8221SArkadiusz Kubalewski  * * number of bytes written - success
17c7ef8221SArkadiusz Kubalewski  * * negative - error code
18d6b98c8dSKarol Kolacinski  */
19bf15bb38SMichal Schmidt static int
ice_gnss_do_write(struct ice_pf * pf,const unsigned char * buf,unsigned int size)20bf15bb38SMichal Schmidt ice_gnss_do_write(struct ice_pf *pf, const unsigned char *buf, unsigned int size)
21d6b98c8dSKarol Kolacinski {
22d6b98c8dSKarol Kolacinski 	struct ice_aqc_link_topo_addr link_topo;
23d6b98c8dSKarol Kolacinski 	struct ice_hw *hw = &pf->hw;
24d6b98c8dSKarol Kolacinski 	unsigned int offset = 0;
25d6b98c8dSKarol Kolacinski 	int err = 0;
26d6b98c8dSKarol Kolacinski 
27d6b98c8dSKarol Kolacinski 	memset(&link_topo, 0, sizeof(struct ice_aqc_link_topo_addr));
28d6b98c8dSKarol Kolacinski 	link_topo.topo_params.index = ICE_E810T_GNSS_I2C_BUS;
29d6b98c8dSKarol Kolacinski 	link_topo.topo_params.node_type_ctx |=
30d6b98c8dSKarol Kolacinski 		FIELD_PREP(ICE_AQC_LINK_TOPO_NODE_CTX_M,
31d6b98c8dSKarol Kolacinski 			   ICE_AQC_LINK_TOPO_NODE_CTX_OVERRIDE);
32d6b98c8dSKarol Kolacinski 
33d6b98c8dSKarol Kolacinski 	/* It's not possible to write a single byte to u-blox.
34d6b98c8dSKarol Kolacinski 	 * Write all bytes in a loop until there are 6 or less bytes left. If
35d6b98c8dSKarol Kolacinski 	 * there are exactly 6 bytes left, the last write would be only a byte.
36d6b98c8dSKarol Kolacinski 	 * In this case, do 4+2 bytes writes instead of 5+1. Otherwise, do the
37d6b98c8dSKarol Kolacinski 	 * last 2 to 5 bytes write.
38d6b98c8dSKarol Kolacinski 	 */
39d6b98c8dSKarol Kolacinski 	while (size - offset > ICE_GNSS_UBX_WRITE_BYTES + 1) {
40d6b98c8dSKarol Kolacinski 		err = ice_aq_write_i2c(hw, link_topo, ICE_GNSS_UBX_I2C_BUS_ADDR,
41d6b98c8dSKarol Kolacinski 				       cpu_to_le16(buf[offset]),
42d6b98c8dSKarol Kolacinski 				       ICE_MAX_I2C_WRITE_BYTES,
43d6b98c8dSKarol Kolacinski 				       &buf[offset + 1], NULL);
44d6b98c8dSKarol Kolacinski 		if (err)
45d6b98c8dSKarol Kolacinski 			goto err_out;
46d6b98c8dSKarol Kolacinski 
47d6b98c8dSKarol Kolacinski 		offset += ICE_GNSS_UBX_WRITE_BYTES;
48d6b98c8dSKarol Kolacinski 	}
49d6b98c8dSKarol Kolacinski 
50d6b98c8dSKarol Kolacinski 	/* Single byte would be written. Write 4 bytes instead of 5. */
51d6b98c8dSKarol Kolacinski 	if (size - offset == ICE_GNSS_UBX_WRITE_BYTES + 1) {
52d6b98c8dSKarol Kolacinski 		err = ice_aq_write_i2c(hw, link_topo, ICE_GNSS_UBX_I2C_BUS_ADDR,
53d6b98c8dSKarol Kolacinski 				       cpu_to_le16(buf[offset]),
54d6b98c8dSKarol Kolacinski 				       ICE_MAX_I2C_WRITE_BYTES - 1,
55d6b98c8dSKarol Kolacinski 				       &buf[offset + 1], NULL);
56d6b98c8dSKarol Kolacinski 		if (err)
57d6b98c8dSKarol Kolacinski 			goto err_out;
58d6b98c8dSKarol Kolacinski 
59d6b98c8dSKarol Kolacinski 		offset += ICE_GNSS_UBX_WRITE_BYTES - 1;
60d6b98c8dSKarol Kolacinski 	}
61d6b98c8dSKarol Kolacinski 
62d6b98c8dSKarol Kolacinski 	/* Do the last write, 2 to 5 bytes. */
63d6b98c8dSKarol Kolacinski 	err = ice_aq_write_i2c(hw, link_topo, ICE_GNSS_UBX_I2C_BUS_ADDR,
64d6b98c8dSKarol Kolacinski 			       cpu_to_le16(buf[offset]), size - offset - 1,
65d6b98c8dSKarol Kolacinski 			       &buf[offset + 1], NULL);
66d6b98c8dSKarol Kolacinski 	if (err)
67d6b98c8dSKarol Kolacinski 		goto err_out;
68d6b98c8dSKarol Kolacinski 
69d6b98c8dSKarol Kolacinski 	return size;
70d6b98c8dSKarol Kolacinski 
71d6b98c8dSKarol Kolacinski err_out:
72d6b98c8dSKarol Kolacinski 	dev_err(ice_pf_to_dev(pf), "GNSS failed to write, offset=%u, size=%u, err=%d\n",
73d6b98c8dSKarol Kolacinski 		offset, size, err);
74d6b98c8dSKarol Kolacinski 
75bf15bb38SMichal Schmidt 	return err;
76d6b98c8dSKarol Kolacinski }
77d6b98c8dSKarol Kolacinski 
78d6b98c8dSKarol Kolacinski /**
7943113ff7SKarol Kolacinski  * ice_gnss_read - Read data from internal GNSS module
8043113ff7SKarol Kolacinski  * @work: GNSS read work structure
8143113ff7SKarol Kolacinski  *
82c7ef8221SArkadiusz Kubalewski  * Read the data from internal GNSS receiver, write it to gnss_dev.
8343113ff7SKarol Kolacinski  */
ice_gnss_read(struct kthread_work * work)8443113ff7SKarol Kolacinski static void ice_gnss_read(struct kthread_work *work)
8543113ff7SKarol Kolacinski {
8643113ff7SKarol Kolacinski 	struct gnss_serial *gnss = container_of(work, struct gnss_serial,
8743113ff7SKarol Kolacinski 						read_work.work);
882f8fdcb0SMichal Schmidt 	unsigned long delay = ICE_GNSS_POLL_DATA_DELAY_TIME;
89c7ef8221SArkadiusz Kubalewski 	unsigned int i, bytes_read, data_len, count;
9043113ff7SKarol Kolacinski 	struct ice_aqc_link_topo_addr link_topo;
9143113ff7SKarol Kolacinski 	struct ice_pf *pf;
9243113ff7SKarol Kolacinski 	struct ice_hw *hw;
9343113ff7SKarol Kolacinski 	__be16 data_len_b;
9443113ff7SKarol Kolacinski 	char *buf = NULL;
950a3ca086SKarol Kolacinski 	u8 i2c_params;
9643113ff7SKarol Kolacinski 	int err = 0;
9743113ff7SKarol Kolacinski 
9843113ff7SKarol Kolacinski 	pf = gnss->back;
99*05a1308aSSimon Horman 	if (!pf || !test_bit(ICE_FLAG_GNSS, pf->flags))
100c7ef8221SArkadiusz Kubalewski 		return;
10143113ff7SKarol Kolacinski 
102c7ef8221SArkadiusz Kubalewski 	hw = &pf->hw;
10343113ff7SKarol Kolacinski 
10443113ff7SKarol Kolacinski 	memset(&link_topo, 0, sizeof(struct ice_aqc_link_topo_addr));
10543113ff7SKarol Kolacinski 	link_topo.topo_params.index = ICE_E810T_GNSS_I2C_BUS;
10643113ff7SKarol Kolacinski 	link_topo.topo_params.node_type_ctx |=
10743113ff7SKarol Kolacinski 		FIELD_PREP(ICE_AQC_LINK_TOPO_NODE_CTX_M,
10843113ff7SKarol Kolacinski 			   ICE_AQC_LINK_TOPO_NODE_CTX_OVERRIDE);
10943113ff7SKarol Kolacinski 
11043113ff7SKarol Kolacinski 	i2c_params = ICE_GNSS_UBX_DATA_LEN_WIDTH |
11143113ff7SKarol Kolacinski 		     ICE_AQC_I2C_USE_REPEATED_START;
11243113ff7SKarol Kolacinski 
11343113ff7SKarol Kolacinski 	err = ice_aq_read_i2c(hw, link_topo, ICE_GNSS_UBX_I2C_BUS_ADDR,
11443113ff7SKarol Kolacinski 			      cpu_to_le16(ICE_GNSS_UBX_DATA_LEN_H),
11543113ff7SKarol Kolacinski 			      i2c_params, (u8 *)&data_len_b, NULL);
11643113ff7SKarol Kolacinski 	if (err)
1172f8fdcb0SMichal Schmidt 		goto requeue;
11843113ff7SKarol Kolacinski 
11943113ff7SKarol Kolacinski 	data_len = be16_to_cpu(data_len_b);
1202f8fdcb0SMichal Schmidt 	if (data_len == 0 || data_len == U16_MAX)
1212f8fdcb0SMichal Schmidt 		goto requeue;
12243113ff7SKarol Kolacinski 
1232f8fdcb0SMichal Schmidt 	/* The u-blox has data_len bytes for us to read */
12443113ff7SKarol Kolacinski 
1250a3ca086SKarol Kolacinski 	data_len = min_t(typeof(data_len), data_len, PAGE_SIZE);
1262f8fdcb0SMichal Schmidt 
1272f8fdcb0SMichal Schmidt 	buf = (char *)get_zeroed_page(GFP_KERNEL);
1282f8fdcb0SMichal Schmidt 	if (!buf) {
12943113ff7SKarol Kolacinski 		err = -ENOMEM;
1302f8fdcb0SMichal Schmidt 		goto requeue;
13143113ff7SKarol Kolacinski 	}
13243113ff7SKarol Kolacinski 
13343113ff7SKarol Kolacinski 	/* Read received data */
13443113ff7SKarol Kolacinski 	for (i = 0; i < data_len; i += bytes_read) {
1350a3ca086SKarol Kolacinski 		unsigned int bytes_left = data_len - i;
13643113ff7SKarol Kolacinski 
1370a3ca086SKarol Kolacinski 		bytes_read = min_t(typeof(bytes_left), bytes_left,
1380a3ca086SKarol Kolacinski 				   ICE_MAX_I2C_DATA_SIZE);
13943113ff7SKarol Kolacinski 
14043113ff7SKarol Kolacinski 		err = ice_aq_read_i2c(hw, link_topo, ICE_GNSS_UBX_I2C_BUS_ADDR,
14143113ff7SKarol Kolacinski 				      cpu_to_le16(ICE_GNSS_UBX_EMPTY_DATA),
14243113ff7SKarol Kolacinski 				      bytes_read, &buf[i], NULL);
14343113ff7SKarol Kolacinski 		if (err)
1442f8fdcb0SMichal Schmidt 			goto free_buf;
14543113ff7SKarol Kolacinski 	}
14643113ff7SKarol Kolacinski 
147c7ef8221SArkadiusz Kubalewski 	count = gnss_insert_raw(pf->gnss_dev, buf, i);
148c7ef8221SArkadiusz Kubalewski 	if (count != i)
149c7ef8221SArkadiusz Kubalewski 		dev_warn(ice_pf_to_dev(pf),
150c7ef8221SArkadiusz Kubalewski 			 "gnss_insert_raw ret=%d size=%d\n",
151c7ef8221SArkadiusz Kubalewski 			 count, i);
1522f8fdcb0SMichal Schmidt 	delay = ICE_GNSS_TIMER_DELAY_TIME;
1532f8fdcb0SMichal Schmidt free_buf:
15443113ff7SKarol Kolacinski 	free_page((unsigned long)buf);
1552f8fdcb0SMichal Schmidt requeue:
1562f8fdcb0SMichal Schmidt 	kthread_queue_delayed_work(gnss->kworker, &gnss->read_work, delay);
15743113ff7SKarol Kolacinski 	if (err)
15843113ff7SKarol Kolacinski 		dev_dbg(ice_pf_to_dev(pf), "GNSS failed to read err=%d\n", err);
15943113ff7SKarol Kolacinski }
16043113ff7SKarol Kolacinski 
16143113ff7SKarol Kolacinski /**
162c7ef8221SArkadiusz Kubalewski  * ice_gnss_struct_init - Initialize GNSS receiver
16343113ff7SKarol Kolacinski  * @pf: Board private structure
164c7ef8221SArkadiusz Kubalewski  *
165c7ef8221SArkadiusz Kubalewski  * Initialize GNSS structures and workers.
166c7ef8221SArkadiusz Kubalewski  *
167c7ef8221SArkadiusz Kubalewski  * Return:
168c7ef8221SArkadiusz Kubalewski  * * pointer to initialized gnss_serial struct - success
169c7ef8221SArkadiusz Kubalewski  * * NULL - error
17043113ff7SKarol Kolacinski  */
ice_gnss_struct_init(struct ice_pf * pf)171c7ef8221SArkadiusz Kubalewski static struct gnss_serial *ice_gnss_struct_init(struct ice_pf *pf)
17243113ff7SKarol Kolacinski {
17343113ff7SKarol Kolacinski 	struct device *dev = ice_pf_to_dev(pf);
17443113ff7SKarol Kolacinski 	struct kthread_worker *kworker;
17543113ff7SKarol Kolacinski 	struct gnss_serial *gnss;
17643113ff7SKarol Kolacinski 
17743113ff7SKarol Kolacinski 	gnss = kzalloc(sizeof(*gnss), GFP_KERNEL);
17843113ff7SKarol Kolacinski 	if (!gnss)
17943113ff7SKarol Kolacinski 		return NULL;
18043113ff7SKarol Kolacinski 
18143113ff7SKarol Kolacinski 	gnss->back = pf;
182c7ef8221SArkadiusz Kubalewski 	pf->gnss_serial = gnss;
18343113ff7SKarol Kolacinski 
18443113ff7SKarol Kolacinski 	kthread_init_delayed_work(&gnss->read_work, ice_gnss_read);
18543113ff7SKarol Kolacinski 	kworker = kthread_create_worker(0, "ice-gnss-%s", dev_name(dev));
1862b1d0a24SYang Yingliang 	if (IS_ERR(kworker)) {
18743113ff7SKarol Kolacinski 		kfree(gnss);
18843113ff7SKarol Kolacinski 		return NULL;
18943113ff7SKarol Kolacinski 	}
19043113ff7SKarol Kolacinski 
19143113ff7SKarol Kolacinski 	gnss->kworker = kworker;
19243113ff7SKarol Kolacinski 
19343113ff7SKarol Kolacinski 	return gnss;
19443113ff7SKarol Kolacinski }
19543113ff7SKarol Kolacinski 
19643113ff7SKarol Kolacinski /**
197c7ef8221SArkadiusz Kubalewski  * ice_gnss_open - Open GNSS device
198c7ef8221SArkadiusz Kubalewski  * @gdev: pointer to the gnss device struct
19943113ff7SKarol Kolacinski  *
200c7ef8221SArkadiusz Kubalewski  * Open GNSS device and start filling the read buffer for consumer.
201c7ef8221SArkadiusz Kubalewski  *
202c7ef8221SArkadiusz Kubalewski  * Return:
203c7ef8221SArkadiusz Kubalewski  * * 0 - success
204c7ef8221SArkadiusz Kubalewski  * * negative - error code
20543113ff7SKarol Kolacinski  */
ice_gnss_open(struct gnss_device * gdev)206c7ef8221SArkadiusz Kubalewski static int ice_gnss_open(struct gnss_device *gdev)
20743113ff7SKarol Kolacinski {
208c7ef8221SArkadiusz Kubalewski 	struct ice_pf *pf = gnss_get_drvdata(gdev);
20943113ff7SKarol Kolacinski 	struct gnss_serial *gnss;
21043113ff7SKarol Kolacinski 
21143113ff7SKarol Kolacinski 	if (!pf)
21243113ff7SKarol Kolacinski 		return -EFAULT;
21343113ff7SKarol Kolacinski 
214c7ef8221SArkadiusz Kubalewski 	if (!test_bit(ICE_FLAG_GNSS, pf->flags))
215c7ef8221SArkadiusz Kubalewski 		return -EFAULT;
21643113ff7SKarol Kolacinski 
217c7ef8221SArkadiusz Kubalewski 	gnss = pf->gnss_serial;
21843113ff7SKarol Kolacinski 	if (!gnss)
219c7ef8221SArkadiusz Kubalewski 		return -ENODEV;
22043113ff7SKarol Kolacinski 
22143113ff7SKarol Kolacinski 	kthread_queue_delayed_work(gnss->kworker, &gnss->read_work, 0);
22243113ff7SKarol Kolacinski 
22343113ff7SKarol Kolacinski 	return 0;
22443113ff7SKarol Kolacinski }
22543113ff7SKarol Kolacinski 
22643113ff7SKarol Kolacinski /**
227c7ef8221SArkadiusz Kubalewski  * ice_gnss_close - Close GNSS device
228c7ef8221SArkadiusz Kubalewski  * @gdev: pointer to the gnss device struct
229c7ef8221SArkadiusz Kubalewski  *
230c7ef8221SArkadiusz Kubalewski  * Close GNSS device, cancel worker, stop filling the read buffer.
23143113ff7SKarol Kolacinski  */
ice_gnss_close(struct gnss_device * gdev)232c7ef8221SArkadiusz Kubalewski static void ice_gnss_close(struct gnss_device *gdev)
23343113ff7SKarol Kolacinski {
234c7ef8221SArkadiusz Kubalewski 	struct ice_pf *pf = gnss_get_drvdata(gdev);
235c7ef8221SArkadiusz Kubalewski 	struct gnss_serial *gnss;
23643113ff7SKarol Kolacinski 
23743113ff7SKarol Kolacinski 	if (!pf)
23843113ff7SKarol Kolacinski 		return;
23943113ff7SKarol Kolacinski 
240c7ef8221SArkadiusz Kubalewski 	gnss = pf->gnss_serial;
241c7ef8221SArkadiusz Kubalewski 	if (!gnss)
242c7ef8221SArkadiusz Kubalewski 		return;
24343113ff7SKarol Kolacinski 
24443113ff7SKarol Kolacinski 	kthread_cancel_delayed_work_sync(&gnss->read_work);
24543113ff7SKarol Kolacinski }
24643113ff7SKarol Kolacinski 
24743113ff7SKarol Kolacinski /**
248c7ef8221SArkadiusz Kubalewski  * ice_gnss_write - Write to GNSS device
249c7ef8221SArkadiusz Kubalewski  * @gdev: pointer to the gnss device struct
25043113ff7SKarol Kolacinski  * @buf: pointer to the user data
251c7ef8221SArkadiusz Kubalewski  * @count: size of the buffer to be sent to the GNSS device
252d6b98c8dSKarol Kolacinski  *
253c7ef8221SArkadiusz Kubalewski  * Return:
254c7ef8221SArkadiusz Kubalewski  * * number of written bytes - success
255c7ef8221SArkadiusz Kubalewski  * * negative - error code
25643113ff7SKarol Kolacinski  */
25743113ff7SKarol Kolacinski static int
ice_gnss_write(struct gnss_device * gdev,const unsigned char * buf,size_t count)258c7ef8221SArkadiusz Kubalewski ice_gnss_write(struct gnss_device *gdev, const unsigned char *buf,
259c7ef8221SArkadiusz Kubalewski 	       size_t count)
26043113ff7SKarol Kolacinski {
261c7ef8221SArkadiusz Kubalewski 	struct ice_pf *pf = gnss_get_drvdata(gdev);
262d6b98c8dSKarol Kolacinski 	struct gnss_serial *gnss;
263d6b98c8dSKarol Kolacinski 
264d6b98c8dSKarol Kolacinski 	/* We cannot write a single byte using our I2C implementation. */
265d6b98c8dSKarol Kolacinski 	if (count <= 1 || count > ICE_GNSS_TTY_WRITE_BUF)
266d6b98c8dSKarol Kolacinski 		return -EINVAL;
267d6b98c8dSKarol Kolacinski 
268d6b98c8dSKarol Kolacinski 	if (!pf)
269d6b98c8dSKarol Kolacinski 		return -EFAULT;
270d6b98c8dSKarol Kolacinski 
271c7ef8221SArkadiusz Kubalewski 	if (!test_bit(ICE_FLAG_GNSS, pf->flags))
272c7ef8221SArkadiusz Kubalewski 		return -EFAULT;
273d6b98c8dSKarol Kolacinski 
274c7ef8221SArkadiusz Kubalewski 	gnss = pf->gnss_serial;
275c7ef8221SArkadiusz Kubalewski 	if (!gnss)
276c7ef8221SArkadiusz Kubalewski 		return -ENODEV;
277d6b98c8dSKarol Kolacinski 
278bf15bb38SMichal Schmidt 	return ice_gnss_do_write(pf, buf, count);
27943113ff7SKarol Kolacinski }
28043113ff7SKarol Kolacinski 
281c7ef8221SArkadiusz Kubalewski static const struct gnss_operations ice_gnss_ops = {
282c7ef8221SArkadiusz Kubalewski 	.open = ice_gnss_open,
283c7ef8221SArkadiusz Kubalewski 	.close = ice_gnss_close,
284c7ef8221SArkadiusz Kubalewski 	.write_raw = ice_gnss_write,
28543113ff7SKarol Kolacinski };
28643113ff7SKarol Kolacinski 
28743113ff7SKarol Kolacinski /**
288c7ef8221SArkadiusz Kubalewski  * ice_gnss_register - Register GNSS receiver
28943113ff7SKarol Kolacinski  * @pf: Board private structure
290c7ef8221SArkadiusz Kubalewski  *
291c7ef8221SArkadiusz Kubalewski  * Allocate and register GNSS receiver in the Linux GNSS subsystem.
292c7ef8221SArkadiusz Kubalewski  *
293c7ef8221SArkadiusz Kubalewski  * Return:
294c7ef8221SArkadiusz Kubalewski  * * 0 - success
295c7ef8221SArkadiusz Kubalewski  * * negative - error code
29643113ff7SKarol Kolacinski  */
ice_gnss_register(struct ice_pf * pf)297c7ef8221SArkadiusz Kubalewski static int ice_gnss_register(struct ice_pf *pf)
29843113ff7SKarol Kolacinski {
299c7ef8221SArkadiusz Kubalewski 	struct gnss_device *gdev;
300c7ef8221SArkadiusz Kubalewski 	int ret;
30143113ff7SKarol Kolacinski 
302c7ef8221SArkadiusz Kubalewski 	gdev = gnss_allocate_device(ice_pf_to_dev(pf));
303c7ef8221SArkadiusz Kubalewski 	if (!gdev) {
304c7ef8221SArkadiusz Kubalewski 		dev_err(ice_pf_to_dev(pf),
305c7ef8221SArkadiusz Kubalewski 			"gnss_allocate_device returns NULL\n");
306c7ef8221SArkadiusz Kubalewski 		return -ENOMEM;
30743113ff7SKarol Kolacinski 	}
30843113ff7SKarol Kolacinski 
309c7ef8221SArkadiusz Kubalewski 	gdev->ops = &ice_gnss_ops;
310c7ef8221SArkadiusz Kubalewski 	gdev->type = GNSS_TYPE_UBX;
311c7ef8221SArkadiusz Kubalewski 	gnss_set_drvdata(gdev, pf);
312c7ef8221SArkadiusz Kubalewski 	ret = gnss_register_device(gdev);
313c7ef8221SArkadiusz Kubalewski 	if (ret) {
314c7ef8221SArkadiusz Kubalewski 		dev_err(ice_pf_to_dev(pf), "gnss_register_device err=%d\n",
315c7ef8221SArkadiusz Kubalewski 			ret);
316c7ef8221SArkadiusz Kubalewski 		gnss_put_device(gdev);
317c7ef8221SArkadiusz Kubalewski 	} else {
318c7ef8221SArkadiusz Kubalewski 		pf->gnss_dev = gdev;
31943113ff7SKarol Kolacinski 	}
32043113ff7SKarol Kolacinski 
321c7ef8221SArkadiusz Kubalewski 	return ret;
32243113ff7SKarol Kolacinski }
32343113ff7SKarol Kolacinski 
32443113ff7SKarol Kolacinski /**
325c7ef8221SArkadiusz Kubalewski  * ice_gnss_deregister - Deregister GNSS receiver
326c7ef8221SArkadiusz Kubalewski  * @pf: Board private structure
327c7ef8221SArkadiusz Kubalewski  *
328c7ef8221SArkadiusz Kubalewski  * Deregister GNSS receiver from the Linux GNSS subsystem,
329c7ef8221SArkadiusz Kubalewski  * release its resources.
330c7ef8221SArkadiusz Kubalewski  */
ice_gnss_deregister(struct ice_pf * pf)331c7ef8221SArkadiusz Kubalewski static void ice_gnss_deregister(struct ice_pf *pf)
332c7ef8221SArkadiusz Kubalewski {
333c7ef8221SArkadiusz Kubalewski 	if (pf->gnss_dev) {
334c7ef8221SArkadiusz Kubalewski 		gnss_deregister_device(pf->gnss_dev);
335c7ef8221SArkadiusz Kubalewski 		gnss_put_device(pf->gnss_dev);
336c7ef8221SArkadiusz Kubalewski 		pf->gnss_dev = NULL;
337c7ef8221SArkadiusz Kubalewski 	}
338c7ef8221SArkadiusz Kubalewski }
339c7ef8221SArkadiusz Kubalewski 
340c7ef8221SArkadiusz Kubalewski /**
341c7ef8221SArkadiusz Kubalewski  * ice_gnss_init - Initialize GNSS support
34243113ff7SKarol Kolacinski  * @pf: Board private structure
34343113ff7SKarol Kolacinski  */
ice_gnss_init(struct ice_pf * pf)34443113ff7SKarol Kolacinski void ice_gnss_init(struct ice_pf *pf)
34543113ff7SKarol Kolacinski {
346c7ef8221SArkadiusz Kubalewski 	int ret;
34743113ff7SKarol Kolacinski 
348c7ef8221SArkadiusz Kubalewski 	pf->gnss_serial = ice_gnss_struct_init(pf);
349c7ef8221SArkadiusz Kubalewski 	if (!pf->gnss_serial)
35043113ff7SKarol Kolacinski 		return;
35143113ff7SKarol Kolacinski 
352c7ef8221SArkadiusz Kubalewski 	ret = ice_gnss_register(pf);
353c7ef8221SArkadiusz Kubalewski 	if (!ret) {
35443113ff7SKarol Kolacinski 		set_bit(ICE_FLAG_GNSS, pf->flags);
355c7ef8221SArkadiusz Kubalewski 		dev_info(ice_pf_to_dev(pf), "GNSS init successful\n");
356c7ef8221SArkadiusz Kubalewski 	} else {
357c7ef8221SArkadiusz Kubalewski 		ice_gnss_exit(pf);
358c7ef8221SArkadiusz Kubalewski 		dev_err(ice_pf_to_dev(pf), "GNSS init failure\n");
359c7ef8221SArkadiusz Kubalewski 	}
36043113ff7SKarol Kolacinski }
36143113ff7SKarol Kolacinski 
36243113ff7SKarol Kolacinski /**
36343113ff7SKarol Kolacinski  * ice_gnss_exit - Disable GNSS TTY support
36443113ff7SKarol Kolacinski  * @pf: Board private structure
36543113ff7SKarol Kolacinski  */
ice_gnss_exit(struct ice_pf * pf)36643113ff7SKarol Kolacinski void ice_gnss_exit(struct ice_pf *pf)
36743113ff7SKarol Kolacinski {
368c7ef8221SArkadiusz Kubalewski 	ice_gnss_deregister(pf);
369c7ef8221SArkadiusz Kubalewski 	clear_bit(ICE_FLAG_GNSS, pf->flags);
370d6b98c8dSKarol Kolacinski 
371c7ef8221SArkadiusz Kubalewski 	if (pf->gnss_serial) {
372c7ef8221SArkadiusz Kubalewski 		struct gnss_serial *gnss = pf->gnss_serial;
37343113ff7SKarol Kolacinski 
37443113ff7SKarol Kolacinski 		kthread_cancel_delayed_work_sync(&gnss->read_work);
375c7ef8221SArkadiusz Kubalewski 		kthread_destroy_worker(gnss->kworker);
376c7ef8221SArkadiusz Kubalewski 		gnss->kworker = NULL;
37743113ff7SKarol Kolacinski 
378c7ef8221SArkadiusz Kubalewski 		kfree(gnss);
379c7ef8221SArkadiusz Kubalewski 		pf->gnss_serial = NULL;
380c7ef8221SArkadiusz Kubalewski 	}
38143113ff7SKarol Kolacinski }
38243113ff7SKarol Kolacinski 
38343113ff7SKarol Kolacinski /**
38443113ff7SKarol Kolacinski  * ice_gnss_is_gps_present - Check if GPS HW is present
38543113ff7SKarol Kolacinski  * @hw: pointer to HW struct
38643113ff7SKarol Kolacinski  */
ice_gnss_is_gps_present(struct ice_hw * hw)38743113ff7SKarol Kolacinski bool ice_gnss_is_gps_present(struct ice_hw *hw)
38843113ff7SKarol Kolacinski {
38943113ff7SKarol Kolacinski 	if (!hw->func_caps.ts_func_info.src_tmr_owned)
39043113ff7SKarol Kolacinski 		return false;
39143113ff7SKarol Kolacinski 
39243113ff7SKarol Kolacinski #if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
39343113ff7SKarol Kolacinski 	if (ice_is_e810t(hw)) {
39443113ff7SKarol Kolacinski 		int err;
39543113ff7SKarol Kolacinski 		u8 data;
39643113ff7SKarol Kolacinski 
39743113ff7SKarol Kolacinski 		err = ice_read_pca9575_reg_e810t(hw, ICE_PCA9575_P0_IN, &data);
39843113ff7SKarol Kolacinski 		if (err || !!(data & ICE_E810T_P0_GNSS_PRSNT_N))
39943113ff7SKarol Kolacinski 			return false;
40043113ff7SKarol Kolacinski 	} else {
40143113ff7SKarol Kolacinski 		return false;
40243113ff7SKarol Kolacinski 	}
40343113ff7SKarol Kolacinski #else
40443113ff7SKarol Kolacinski 	if (!ice_is_e810t(hw))
40543113ff7SKarol Kolacinski 		return false;
40643113ff7SKarol Kolacinski #endif /* IS_ENABLED(CONFIG_PTP_1588_CLOCK) */
40743113ff7SKarol Kolacinski 
40843113ff7SKarol Kolacinski 	return true;
40943113ff7SKarol Kolacinski }
410