forked from mirrors/linux
		
	ice: add write functionality for GNSS TTY
Add the possibility to write raw bytes to the GNSS module through the first TTY device. This allows user to configure the module. Create a second read-only TTY device. Signed-off-by: Karol Kolacinski <karol.kolacinski@intel.com> Tested-by: Gurucharan <gurucharanx.g@intel.com> (A Contingent worker at Intel) Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
This commit is contained in:
		
							parent
							
								
									fcf9b695a5
								
							
						
					
					
						commit
						d6b98c8d24
					
				
					 4 changed files with 254 additions and 31 deletions
				
			
		| 
						 | 
					@ -901,6 +901,15 @@ To enable/disable UDP Segmentation Offload, issue the following command::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # ethtool -K <ethX> tx-udp-segmentation [off|on]
 | 
					  # ethtool -K <ethX> tx-udp-segmentation [off|on]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GNSS module
 | 
				
			||||||
 | 
					-----------
 | 
				
			||||||
 | 
					Allows user to read messages from the GNSS module and write supported commands.
 | 
				
			||||||
 | 
					If the module is physically present, driver creates 2 TTYs for each supported
 | 
				
			||||||
 | 
					device in /dev, ttyGNSS_<device>:<function>_0 and _1. First one (_0) is RW and
 | 
				
			||||||
 | 
					the second one is RO.
 | 
				
			||||||
 | 
					The protocol of write commands is dependent on the GNSS module as the driver
 | 
				
			||||||
 | 
					writes raw bytes from the TTY to the GNSS i2c. Please refer to the module
 | 
				
			||||||
 | 
					documentation for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Performance Optimization
 | 
					Performance Optimization
 | 
				
			||||||
========================
 | 
					========================
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -545,8 +545,8 @@ struct ice_pf {
 | 
				
			||||||
	u32 msg_enable;
 | 
						u32 msg_enable;
 | 
				
			||||||
	struct ice_ptp ptp;
 | 
						struct ice_ptp ptp;
 | 
				
			||||||
	struct tty_driver *ice_gnss_tty_driver;
 | 
						struct tty_driver *ice_gnss_tty_driver;
 | 
				
			||||||
	struct tty_port gnss_tty_port;
 | 
						struct tty_port *gnss_tty_port[ICE_GNSS_TTY_MINOR_DEVICES];
 | 
				
			||||||
	struct gnss_serial *gnss_serial;
 | 
						struct gnss_serial *gnss_serial[ICE_GNSS_TTY_MINOR_DEVICES];
 | 
				
			||||||
	u16 num_rdma_msix;		/* Total MSIX vectors for RDMA driver */
 | 
						u16 num_rdma_msix;		/* Total MSIX vectors for RDMA driver */
 | 
				
			||||||
	u16 rdma_base_vector;
 | 
						u16 rdma_base_vector;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,103 @@
 | 
				
			||||||
// SPDX-License-Identifier: GPL-2.0
 | 
					// SPDX-License-Identifier: GPL-2.0
 | 
				
			||||||
/* Copyright (C) 2018-2021, Intel Corporation. */
 | 
					/* Copyright (C) 2021-2022, Intel Corporation. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "ice.h"
 | 
					#include "ice.h"
 | 
				
			||||||
#include "ice_lib.h"
 | 
					#include "ice_lib.h"
 | 
				
			||||||
#include <linux/tty_driver.h>
 | 
					#include <linux/tty_driver.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * ice_gnss_do_write - Write data to internal GNSS
 | 
				
			||||||
 | 
					 * @pf: board private structure
 | 
				
			||||||
 | 
					 * @buf: command buffer
 | 
				
			||||||
 | 
					 * @size: command buffer size
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Write UBX command data to the GNSS receiver
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static unsigned int
 | 
				
			||||||
 | 
					ice_gnss_do_write(struct ice_pf *pf, unsigned char *buf, unsigned int size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ice_aqc_link_topo_addr link_topo;
 | 
				
			||||||
 | 
						struct ice_hw *hw = &pf->hw;
 | 
				
			||||||
 | 
						unsigned int offset = 0;
 | 
				
			||||||
 | 
						int err = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memset(&link_topo, 0, sizeof(struct ice_aqc_link_topo_addr));
 | 
				
			||||||
 | 
						link_topo.topo_params.index = ICE_E810T_GNSS_I2C_BUS;
 | 
				
			||||||
 | 
						link_topo.topo_params.node_type_ctx |=
 | 
				
			||||||
 | 
							FIELD_PREP(ICE_AQC_LINK_TOPO_NODE_CTX_M,
 | 
				
			||||||
 | 
								   ICE_AQC_LINK_TOPO_NODE_CTX_OVERRIDE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* It's not possible to write a single byte to u-blox.
 | 
				
			||||||
 | 
						 * Write all bytes in a loop until there are 6 or less bytes left. If
 | 
				
			||||||
 | 
						 * there are exactly 6 bytes left, the last write would be only a byte.
 | 
				
			||||||
 | 
						 * In this case, do 4+2 bytes writes instead of 5+1. Otherwise, do the
 | 
				
			||||||
 | 
						 * last 2 to 5 bytes write.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						while (size - offset > ICE_GNSS_UBX_WRITE_BYTES + 1) {
 | 
				
			||||||
 | 
							err = ice_aq_write_i2c(hw, link_topo, ICE_GNSS_UBX_I2C_BUS_ADDR,
 | 
				
			||||||
 | 
									       cpu_to_le16(buf[offset]),
 | 
				
			||||||
 | 
									       ICE_MAX_I2C_WRITE_BYTES,
 | 
				
			||||||
 | 
									       &buf[offset + 1], NULL);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								goto err_out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							offset += ICE_GNSS_UBX_WRITE_BYTES;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Single byte would be written. Write 4 bytes instead of 5. */
 | 
				
			||||||
 | 
						if (size - offset == ICE_GNSS_UBX_WRITE_BYTES + 1) {
 | 
				
			||||||
 | 
							err = ice_aq_write_i2c(hw, link_topo, ICE_GNSS_UBX_I2C_BUS_ADDR,
 | 
				
			||||||
 | 
									       cpu_to_le16(buf[offset]),
 | 
				
			||||||
 | 
									       ICE_MAX_I2C_WRITE_BYTES - 1,
 | 
				
			||||||
 | 
									       &buf[offset + 1], NULL);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								goto err_out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							offset += ICE_GNSS_UBX_WRITE_BYTES - 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Do the last write, 2 to 5 bytes. */
 | 
				
			||||||
 | 
						err = ice_aq_write_i2c(hw, link_topo, ICE_GNSS_UBX_I2C_BUS_ADDR,
 | 
				
			||||||
 | 
								       cpu_to_le16(buf[offset]), size - offset - 1,
 | 
				
			||||||
 | 
								       &buf[offset + 1], NULL);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							goto err_out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					err_out:
 | 
				
			||||||
 | 
						dev_err(ice_pf_to_dev(pf), "GNSS failed to write, offset=%u, size=%u, err=%d\n",
 | 
				
			||||||
 | 
							offset, size, err);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return offset;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * ice_gnss_write_pending - Write all pending data to internal GNSS
 | 
				
			||||||
 | 
					 * @work: GNSS write work structure
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void ice_gnss_write_pending(struct kthread_work *work)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct gnss_serial *gnss = container_of(work, struct gnss_serial,
 | 
				
			||||||
 | 
											write_work);
 | 
				
			||||||
 | 
						struct ice_pf *pf = gnss->back;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!list_empty(&gnss->queue)) {
 | 
				
			||||||
 | 
							struct gnss_write_buf *write_buf = NULL;
 | 
				
			||||||
 | 
							unsigned int bytes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							write_buf = list_first_entry(&gnss->queue,
 | 
				
			||||||
 | 
										     struct gnss_write_buf, queue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							bytes = ice_gnss_do_write(pf, write_buf->buf, write_buf->size);
 | 
				
			||||||
 | 
							dev_dbg(ice_pf_to_dev(pf), "%u bytes written to GNSS\n", bytes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							list_del(&write_buf->queue);
 | 
				
			||||||
 | 
							kfree(write_buf->buf);
 | 
				
			||||||
 | 
							kfree(write_buf);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * ice_gnss_read - Read data from internal GNSS module
 | 
					 * ice_gnss_read - Read data from internal GNSS module
 | 
				
			||||||
 * @work: GNSS read work structure
 | 
					 * @work: GNSS read work structure
 | 
				
			||||||
| 
						 | 
					@ -104,8 +197,9 @@ static void ice_gnss_read(struct kthread_work *work)
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * ice_gnss_struct_init - Initialize GNSS structure for the TTY
 | 
					 * ice_gnss_struct_init - Initialize GNSS structure for the TTY
 | 
				
			||||||
 * @pf: Board private structure
 | 
					 * @pf: Board private structure
 | 
				
			||||||
 | 
					 * @index: TTY device index
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static struct gnss_serial *ice_gnss_struct_init(struct ice_pf *pf)
 | 
					static struct gnss_serial *ice_gnss_struct_init(struct ice_pf *pf, int index)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct device *dev = ice_pf_to_dev(pf);
 | 
						struct device *dev = ice_pf_to_dev(pf);
 | 
				
			||||||
	struct kthread_worker *kworker;
 | 
						struct kthread_worker *kworker;
 | 
				
			||||||
| 
						 | 
					@ -118,9 +212,11 @@ static struct gnss_serial *ice_gnss_struct_init(struct ice_pf *pf)
 | 
				
			||||||
	mutex_init(&gnss->gnss_mutex);
 | 
						mutex_init(&gnss->gnss_mutex);
 | 
				
			||||||
	gnss->open_count = 0;
 | 
						gnss->open_count = 0;
 | 
				
			||||||
	gnss->back = pf;
 | 
						gnss->back = pf;
 | 
				
			||||||
	pf->gnss_serial = gnss;
 | 
						pf->gnss_serial[index] = gnss;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kthread_init_delayed_work(&gnss->read_work, ice_gnss_read);
 | 
						kthread_init_delayed_work(&gnss->read_work, ice_gnss_read);
 | 
				
			||||||
 | 
						INIT_LIST_HEAD(&gnss->queue);
 | 
				
			||||||
 | 
						kthread_init_work(&gnss->write_work, ice_gnss_write_pending);
 | 
				
			||||||
	/* Allocate a kworker for handling work required for the GNSS TTY
 | 
						/* Allocate a kworker for handling work required for the GNSS TTY
 | 
				
			||||||
	 * writes.
 | 
						 * writes.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
| 
						 | 
					@ -156,10 +252,10 @@ static int ice_gnss_tty_open(struct tty_struct *tty, struct file *filp)
 | 
				
			||||||
	tty->driver_data = NULL;
 | 
						tty->driver_data = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Get the serial object associated with this tty pointer */
 | 
						/* Get the serial object associated with this tty pointer */
 | 
				
			||||||
	gnss = pf->gnss_serial;
 | 
						gnss = pf->gnss_serial[tty->index];
 | 
				
			||||||
	if (!gnss) {
 | 
						if (!gnss) {
 | 
				
			||||||
		/* Initialize GNSS struct on the first device open */
 | 
							/* Initialize GNSS struct on the first device open */
 | 
				
			||||||
		gnss = ice_gnss_struct_init(pf);
 | 
							gnss = ice_gnss_struct_init(pf, tty->index);
 | 
				
			||||||
		if (!gnss)
 | 
							if (!gnss)
 | 
				
			||||||
			return -ENOMEM;
 | 
								return -ENOMEM;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -212,25 +308,100 @@ static void ice_gnss_tty_close(struct tty_struct *tty, struct file *filp)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * ice_gnss_tty_write - Dummy TTY write function to avoid kernel panic
 | 
					 * ice_gnss_tty_write - Write GNSS data
 | 
				
			||||||
 * @tty: pointer to the tty_struct
 | 
					 * @tty: pointer to the tty_struct
 | 
				
			||||||
 * @buf: pointer to the user data
 | 
					 * @buf: pointer to the user data
 | 
				
			||||||
 * @cnt: the number of characters that was able to be sent to the hardware (or
 | 
					 * @count: the number of characters queued to be sent to the HW
 | 
				
			||||||
 *       queued to be sent at a later time)
 | 
					 *
 | 
				
			||||||
 | 
					 * The write function call is called by the user when there is data to be sent
 | 
				
			||||||
 | 
					 * to the hardware. First the tty core receives the call, and then it passes the
 | 
				
			||||||
 | 
					 * data on to the tty driver's write function. The tty core also tells the tty
 | 
				
			||||||
 | 
					 * driver the size of the data being sent.
 | 
				
			||||||
 | 
					 * If any errors happen during the write call, a negative error value should be
 | 
				
			||||||
 | 
					 * returned instead of the number of characters queued to be written.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int
 | 
					static int
 | 
				
			||||||
ice_gnss_tty_write(struct tty_struct *tty, const unsigned char *buf, int cnt)
 | 
					ice_gnss_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return 0;
 | 
						struct gnss_write_buf *write_buf;
 | 
				
			||||||
 | 
						struct gnss_serial *gnss;
 | 
				
			||||||
 | 
						unsigned char *cmd_buf;
 | 
				
			||||||
 | 
						struct ice_pf *pf;
 | 
				
			||||||
 | 
						int err = count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* We cannot write a single byte using our I2C implementation. */
 | 
				
			||||||
 | 
						if (count <= 1 || count > ICE_GNSS_TTY_WRITE_BUF)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gnss = tty->driver_data;
 | 
				
			||||||
 | 
						if (!gnss)
 | 
				
			||||||
 | 
							return -EFAULT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pf = (struct ice_pf *)tty->driver->driver_state;
 | 
				
			||||||
 | 
						if (!pf)
 | 
				
			||||||
 | 
							return -EFAULT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Only allow to write on TTY 0 */
 | 
				
			||||||
 | 
						if (gnss != pf->gnss_serial[0])
 | 
				
			||||||
 | 
							return -EIO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&gnss->gnss_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!gnss->open_count) {
 | 
				
			||||||
 | 
							err = -EINVAL;
 | 
				
			||||||
 | 
							goto exit;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cmd_buf = kcalloc(count, sizeof(*buf), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!cmd_buf) {
 | 
				
			||||||
 | 
							err = -ENOMEM;
 | 
				
			||||||
 | 
							goto exit;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memcpy(cmd_buf, buf, count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Send the data out to a hardware port */
 | 
				
			||||||
 | 
						write_buf = kzalloc(sizeof(*write_buf), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!write_buf) {
 | 
				
			||||||
 | 
							err = -ENOMEM;
 | 
				
			||||||
 | 
							goto exit;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						write_buf->buf = cmd_buf;
 | 
				
			||||||
 | 
						write_buf->size = count;
 | 
				
			||||||
 | 
						INIT_LIST_HEAD(&write_buf->queue);
 | 
				
			||||||
 | 
						list_add_tail(&write_buf->queue, &gnss->queue);
 | 
				
			||||||
 | 
						kthread_queue_work(gnss->kworker, &gnss->write_work);
 | 
				
			||||||
 | 
					exit:
 | 
				
			||||||
 | 
						mutex_unlock(&gnss->gnss_mutex);
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * ice_gnss_tty_write_room - Dummy TTY write_room function to avoid kernel panic
 | 
					 * ice_gnss_tty_write_room - Returns the numbers of characters to be written.
 | 
				
			||||||
 * @tty: pointer to the tty_struct
 | 
					 * @tty: pointer to the tty_struct
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This routine returns the numbers of characters the tty driver will accept
 | 
				
			||||||
 | 
					 * for queuing to be written or 0 if either the TTY is not open or user
 | 
				
			||||||
 | 
					 * tries to write to the TTY other than the first.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static unsigned int ice_gnss_tty_write_room(struct tty_struct *tty)
 | 
					static unsigned int ice_gnss_tty_write_room(struct tty_struct *tty)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct gnss_serial *gnss = tty->driver_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Only allow to write on TTY 0 */
 | 
				
			||||||
 | 
						if (!gnss || gnss != gnss->back->gnss_serial[0])
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&gnss->gnss_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!gnss->open_count) {
 | 
				
			||||||
 | 
							mutex_unlock(&gnss->gnss_mutex);
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_unlock(&gnss->gnss_mutex);
 | 
				
			||||||
 | 
						return ICE_GNSS_TTY_WRITE_BUF;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct tty_operations tty_gps_ops = {
 | 
					static const struct tty_operations tty_gps_ops = {
 | 
				
			||||||
| 
						 | 
					@ -250,11 +421,13 @@ static struct tty_driver *ice_gnss_create_tty_driver(struct ice_pf *pf)
 | 
				
			||||||
	const int ICE_TTYDRV_NAME_MAX = 14;
 | 
						const int ICE_TTYDRV_NAME_MAX = 14;
 | 
				
			||||||
	struct tty_driver *tty_driver;
 | 
						struct tty_driver *tty_driver;
 | 
				
			||||||
	char *ttydrv_name;
 | 
						char *ttydrv_name;
 | 
				
			||||||
 | 
						unsigned int i;
 | 
				
			||||||
	int err;
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tty_driver = tty_alloc_driver(1, TTY_DRIVER_REAL_RAW);
 | 
						tty_driver = tty_alloc_driver(ICE_GNSS_TTY_MINOR_DEVICES,
 | 
				
			||||||
 | 
									      TTY_DRIVER_REAL_RAW);
 | 
				
			||||||
	if (IS_ERR(tty_driver)) {
 | 
						if (IS_ERR(tty_driver)) {
 | 
				
			||||||
		dev_err(ice_pf_to_dev(pf), "Failed to allocate memory for GNSS TTY\n");
 | 
							dev_err(dev, "Failed to allocate memory for GNSS TTY\n");
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -284,23 +457,32 @@ static struct tty_driver *ice_gnss_create_tty_driver(struct ice_pf *pf)
 | 
				
			||||||
	tty_driver->driver_state = pf;
 | 
						tty_driver->driver_state = pf;
 | 
				
			||||||
	tty_set_operations(tty_driver, &tty_gps_ops);
 | 
						tty_set_operations(tty_driver, &tty_gps_ops);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pf->gnss_serial = NULL;
 | 
						for (i = 0; i < ICE_GNSS_TTY_MINOR_DEVICES; i++) {
 | 
				
			||||||
 | 
							pf->gnss_tty_port[i] = kzalloc(sizeof(*pf->gnss_tty_port[i]),
 | 
				
			||||||
 | 
										       GFP_KERNEL);
 | 
				
			||||||
 | 
							pf->gnss_serial[i] = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tty_port_init(&pf->gnss_tty_port);
 | 
							tty_port_init(pf->gnss_tty_port[i]);
 | 
				
			||||||
	tty_port_link_device(&pf->gnss_tty_port, tty_driver, 0);
 | 
							tty_port_link_device(pf->gnss_tty_port[i], tty_driver, i);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = tty_register_driver(tty_driver);
 | 
						err = tty_register_driver(tty_driver);
 | 
				
			||||||
	if (err) {
 | 
						if (err) {
 | 
				
			||||||
		dev_err(ice_pf_to_dev(pf), "Failed to register TTY driver err=%d\n",
 | 
							dev_err(dev, "Failed to register TTY driver err=%d\n", err);
 | 
				
			||||||
			err);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		tty_port_destroy(&pf->gnss_tty_port);
 | 
							for (i = 0; i < ICE_GNSS_TTY_MINOR_DEVICES; i++) {
 | 
				
			||||||
 | 
								tty_port_destroy(pf->gnss_tty_port[i]);
 | 
				
			||||||
 | 
								kfree(pf->gnss_tty_port[i]);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		kfree(ttydrv_name);
 | 
							kfree(ttydrv_name);
 | 
				
			||||||
		tty_driver_kref_put(pf->ice_gnss_tty_driver);
 | 
							tty_driver_kref_put(pf->ice_gnss_tty_driver);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < ICE_GNSS_TTY_MINOR_DEVICES; i++)
 | 
				
			||||||
 | 
							dev_info(dev, "%s%d registered\n", ttydrv_name, i);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return tty_driver;
 | 
						return tty_driver;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -328,17 +510,25 @@ void ice_gnss_init(struct ice_pf *pf)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void ice_gnss_exit(struct ice_pf *pf)
 | 
					void ice_gnss_exit(struct ice_pf *pf)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						unsigned int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!test_bit(ICE_FLAG_GNSS, pf->flags) || !pf->ice_gnss_tty_driver)
 | 
						if (!test_bit(ICE_FLAG_GNSS, pf->flags) || !pf->ice_gnss_tty_driver)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tty_port_destroy(&pf->gnss_tty_port);
 | 
						for (i = 0; i < ICE_GNSS_TTY_MINOR_DEVICES; i++) {
 | 
				
			||||||
 | 
							if (pf->gnss_tty_port[i]) {
 | 
				
			||||||
 | 
								tty_port_destroy(pf->gnss_tty_port[i]);
 | 
				
			||||||
 | 
								kfree(pf->gnss_tty_port[i]);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (pf->gnss_serial) {
 | 
							if (pf->gnss_serial[i]) {
 | 
				
			||||||
		struct gnss_serial *gnss = pf->gnss_serial;
 | 
								struct gnss_serial *gnss = pf->gnss_serial[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								kthread_cancel_work_sync(&gnss->write_work);
 | 
				
			||||||
			kthread_cancel_delayed_work_sync(&gnss->read_work);
 | 
								kthread_cancel_delayed_work_sync(&gnss->read_work);
 | 
				
			||||||
			kfree(gnss);
 | 
								kfree(gnss);
 | 
				
			||||||
		pf->gnss_serial = NULL;
 | 
								pf->gnss_serial[i] = NULL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tty_unregister_driver(pf->ice_gnss_tty_driver);
 | 
						tty_unregister_driver(pf->ice_gnss_tty_driver);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
/* SPDX-License-Identifier: GPL-2.0 */
 | 
					/* SPDX-License-Identifier: GPL-2.0 */
 | 
				
			||||||
/* Copyright (C) 2018-2021, Intel Corporation. */
 | 
					/* Copyright (C) 2021-2022, Intel Corporation. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef _ICE_GNSS_H_
 | 
					#ifndef _ICE_GNSS_H_
 | 
				
			||||||
#define _ICE_GNSS_H_
 | 
					#define _ICE_GNSS_H_
 | 
				
			||||||
| 
						 | 
					@ -8,14 +8,34 @@
 | 
				
			||||||
#include <linux/tty_flip.h>
 | 
					#include <linux/tty_flip.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define ICE_E810T_GNSS_I2C_BUS		0x2
 | 
					#define ICE_E810T_GNSS_I2C_BUS		0x2
 | 
				
			||||||
 | 
					#define ICE_GNSS_TIMER_DELAY_TIME	(HZ / 10) /* 0.1 second per message */
 | 
				
			||||||
 | 
					/* Create 2 minor devices, both using the same GNSS module. First one is RW,
 | 
				
			||||||
 | 
					 * second one RO.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define ICE_GNSS_TTY_MINOR_DEVICES	2
 | 
				
			||||||
 | 
					#define ICE_GNSS_TTY_WRITE_BUF		250
 | 
				
			||||||
 | 
					#define ICE_MAX_I2C_DATA_SIZE		FIELD_MAX(ICE_AQC_I2C_DATA_SIZE_M)
 | 
				
			||||||
 | 
					#define ICE_MAX_I2C_WRITE_BYTES		4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* u-blox ZED-F9T specific definitions */
 | 
				
			||||||
#define ICE_GNSS_UBX_I2C_BUS_ADDR	0x42
 | 
					#define ICE_GNSS_UBX_I2C_BUS_ADDR	0x42
 | 
				
			||||||
/* Data length register is big endian */
 | 
					/* Data length register is big endian */
 | 
				
			||||||
#define ICE_GNSS_UBX_DATA_LEN_H		0xFD
 | 
					#define ICE_GNSS_UBX_DATA_LEN_H		0xFD
 | 
				
			||||||
#define ICE_GNSS_UBX_DATA_LEN_WIDTH	2
 | 
					#define ICE_GNSS_UBX_DATA_LEN_WIDTH	2
 | 
				
			||||||
#define ICE_GNSS_UBX_EMPTY_DATA		0xFF
 | 
					#define ICE_GNSS_UBX_EMPTY_DATA		0xFF
 | 
				
			||||||
#define ICE_GNSS_TIMER_DELAY_TIME	(HZ / 10) /* 0.1 second per message */
 | 
					/* For u-blox writes are performed without address so the first byte to write is
 | 
				
			||||||
#define ICE_MAX_I2C_DATA_SIZE		FIELD_MAX(ICE_AQC_I2C_DATA_SIZE_M)
 | 
					 * passed as I2C addr parameter.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define ICE_GNSS_UBX_WRITE_BYTES	(ICE_MAX_I2C_WRITE_BYTES + 1)
 | 
				
			||||||
#define ICE_MAX_UBX_READ_TRIES		255
 | 
					#define ICE_MAX_UBX_READ_TRIES		255
 | 
				
			||||||
 | 
					#define ICE_MAX_UBX_ACK_READ_TRIES	4095
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct gnss_write_buf {
 | 
				
			||||||
 | 
						struct list_head queue;
 | 
				
			||||||
 | 
						unsigned int size;
 | 
				
			||||||
 | 
						unsigned char *buf;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * struct gnss_serial - data used to initialize GNSS TTY port
 | 
					 * struct gnss_serial - data used to initialize GNSS TTY port
 | 
				
			||||||
| 
						 | 
					@ -25,6 +45,8 @@
 | 
				
			||||||
 * @gnss_mutex: gnss_mutex used to protect GNSS serial operations
 | 
					 * @gnss_mutex: gnss_mutex used to protect GNSS serial operations
 | 
				
			||||||
 * @kworker: kwork thread for handling periodic work
 | 
					 * @kworker: kwork thread for handling periodic work
 | 
				
			||||||
 * @read_work: read_work function for handling GNSS reads
 | 
					 * @read_work: read_work function for handling GNSS reads
 | 
				
			||||||
 | 
					 * @write_work: write_work function for handling GNSS writes
 | 
				
			||||||
 | 
					 * @queue: write buffers queue
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
struct gnss_serial {
 | 
					struct gnss_serial {
 | 
				
			||||||
	struct ice_pf *back;
 | 
						struct ice_pf *back;
 | 
				
			||||||
| 
						 | 
					@ -33,6 +55,8 @@ struct gnss_serial {
 | 
				
			||||||
	struct mutex gnss_mutex; /* protects GNSS serial structure */
 | 
						struct mutex gnss_mutex; /* protects GNSS serial structure */
 | 
				
			||||||
	struct kthread_worker *kworker;
 | 
						struct kthread_worker *kworker;
 | 
				
			||||||
	struct kthread_delayed_work read_work;
 | 
						struct kthread_delayed_work read_work;
 | 
				
			||||||
 | 
						struct kthread_work write_work;
 | 
				
			||||||
 | 
						struct list_head queue;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if IS_ENABLED(CONFIG_TTY)
 | 
					#if IS_ENABLED(CONFIG_TTY)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue