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;
9905a1308aSSimon 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
392*89776a6aSJacob Keller if (!ice_is_gps_in_netlist(hw))
393*89776a6aSJacob Keller return false;
394*89776a6aSJacob Keller
39543113ff7SKarol Kolacinski #if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
39643113ff7SKarol Kolacinski if (ice_is_e810t(hw)) {
39743113ff7SKarol Kolacinski int err;
39843113ff7SKarol Kolacinski u8 data;
39943113ff7SKarol Kolacinski
40043113ff7SKarol Kolacinski err = ice_read_pca9575_reg_e810t(hw, ICE_PCA9575_P0_IN, &data);
40143113ff7SKarol Kolacinski if (err || !!(data & ICE_E810T_P0_GNSS_PRSNT_N))
40243113ff7SKarol Kolacinski return false;
40343113ff7SKarol Kolacinski } else {
40443113ff7SKarol Kolacinski return false;
40543113ff7SKarol Kolacinski }
40643113ff7SKarol Kolacinski #else
40743113ff7SKarol Kolacinski if (!ice_is_e810t(hw))
40843113ff7SKarol Kolacinski return false;
40943113ff7SKarol Kolacinski #endif /* IS_ENABLED(CONFIG_PTP_1588_CLOCK) */
41043113ff7SKarol Kolacinski
41143113ff7SKarol Kolacinski return true;
41243113ff7SKarol Kolacinski }
413