Merge branch '100GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue
Tony Nguyen says: ==================== 100GbE Intel Wired LAN Driver Updates 2022-07-21 This series contains updates to ice driver only. Karol adds implementation for GNSS write; data is written to the GNSS module through TTY device using u-blox UBX protocol. * '100GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue: ice: add write functionality for GNSS TTY ice: add i2c write command ==================== Link: https://lore.kernel.org/r/20220721202842.3276257-1-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
502c6f8ced
@ -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;
|
||||||
|
|
||||||
|
@ -1395,7 +1395,7 @@ struct ice_aqc_get_link_topo {
|
|||||||
u8 rsvd[9];
|
u8 rsvd[9];
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Read I2C (direct, 0x06E2) */
|
/* Read/Write I2C (direct, 0x06E2/0x06E3) */
|
||||||
struct ice_aqc_i2c {
|
struct ice_aqc_i2c {
|
||||||
struct ice_aqc_link_topo_addr topo_addr;
|
struct ice_aqc_link_topo_addr topo_addr;
|
||||||
__le16 i2c_addr;
|
__le16 i2c_addr;
|
||||||
@ -1405,7 +1405,7 @@ struct ice_aqc_i2c {
|
|||||||
|
|
||||||
u8 rsvd;
|
u8 rsvd;
|
||||||
__le16 i2c_bus_addr;
|
__le16 i2c_bus_addr;
|
||||||
u8 rsvd2[4];
|
u8 i2c_data[4]; /* Used only by write command, reserved in read. */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Read I2C Response (direct, 0x06E2) */
|
/* Read I2C Response (direct, 0x06E2) */
|
||||||
@ -2124,7 +2124,7 @@ struct ice_aq_desc {
|
|||||||
struct ice_aqc_get_link_status get_link_status;
|
struct ice_aqc_get_link_status get_link_status;
|
||||||
struct ice_aqc_event_lan_overflow lan_overflow;
|
struct ice_aqc_event_lan_overflow lan_overflow;
|
||||||
struct ice_aqc_get_link_topo get_link_topo;
|
struct ice_aqc_get_link_topo get_link_topo;
|
||||||
struct ice_aqc_i2c read_i2c;
|
struct ice_aqc_i2c read_write_i2c;
|
||||||
struct ice_aqc_read_i2c_resp read_i2c_resp;
|
struct ice_aqc_read_i2c_resp read_i2c_resp;
|
||||||
} params;
|
} params;
|
||||||
};
|
};
|
||||||
@ -2241,6 +2241,7 @@ enum ice_adminq_opc {
|
|||||||
ice_aqc_opc_set_mac_lb = 0x0620,
|
ice_aqc_opc_set_mac_lb = 0x0620,
|
||||||
ice_aqc_opc_get_link_topo = 0x06E0,
|
ice_aqc_opc_get_link_topo = 0x06E0,
|
||||||
ice_aqc_opc_read_i2c = 0x06E2,
|
ice_aqc_opc_read_i2c = 0x06E2,
|
||||||
|
ice_aqc_opc_write_i2c = 0x06E3,
|
||||||
ice_aqc_opc_set_port_id_led = 0x06E9,
|
ice_aqc_opc_set_port_id_led = 0x06E9,
|
||||||
ice_aqc_opc_set_gpio = 0x06EC,
|
ice_aqc_opc_set_gpio = 0x06EC,
|
||||||
ice_aqc_opc_get_gpio = 0x06ED,
|
ice_aqc_opc_get_gpio = 0x06ED,
|
||||||
|
@ -4823,7 +4823,7 @@ ice_aq_read_i2c(struct ice_hw *hw, struct ice_aqc_link_topo_addr topo_addr,
|
|||||||
int status;
|
int status;
|
||||||
|
|
||||||
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_read_i2c);
|
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_read_i2c);
|
||||||
cmd = &desc.params.read_i2c;
|
cmd = &desc.params.read_write_i2c;
|
||||||
|
|
||||||
if (!data)
|
if (!data)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -4850,6 +4850,51 @@ ice_aq_read_i2c(struct ice_hw *hw, struct ice_aqc_link_topo_addr topo_addr,
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ice_aq_write_i2c
|
||||||
|
* @hw: pointer to the hw struct
|
||||||
|
* @topo_addr: topology address for a device to communicate with
|
||||||
|
* @bus_addr: 7-bit I2C bus address
|
||||||
|
* @addr: I2C memory address (I2C offset) with up to 16 bits
|
||||||
|
* @params: I2C parameters: bit [4] - I2C address type, bits [3:0] - data size to write (0-7 bytes)
|
||||||
|
* @data: pointer to data (0 to 4 bytes) to be written to the I2C device
|
||||||
|
* @cd: pointer to command details structure or NULL
|
||||||
|
*
|
||||||
|
* Write I2C (0x06E3)
|
||||||
|
*
|
||||||
|
* * Return:
|
||||||
|
* * 0 - Successful write to the i2c device
|
||||||
|
* * -EINVAL - Data size greater than 4 bytes
|
||||||
|
* * -EIO - FW error
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
ice_aq_write_i2c(struct ice_hw *hw, struct ice_aqc_link_topo_addr topo_addr,
|
||||||
|
u16 bus_addr, __le16 addr, u8 params, u8 *data,
|
||||||
|
struct ice_sq_cd *cd)
|
||||||
|
{
|
||||||
|
struct ice_aq_desc desc = { 0 };
|
||||||
|
struct ice_aqc_i2c *cmd;
|
||||||
|
u8 data_size;
|
||||||
|
|
||||||
|
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_write_i2c);
|
||||||
|
cmd = &desc.params.read_write_i2c;
|
||||||
|
|
||||||
|
data_size = FIELD_GET(ICE_AQC_I2C_DATA_SIZE_M, params);
|
||||||
|
|
||||||
|
/* data_size limited to 4 */
|
||||||
|
if (data_size > 4)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
cmd->i2c_bus_addr = cpu_to_le16(bus_addr);
|
||||||
|
cmd->topo_addr = topo_addr;
|
||||||
|
cmd->i2c_params = params;
|
||||||
|
cmd->i2c_addr = addr;
|
||||||
|
|
||||||
|
memcpy(cmd->i2c_data, data, data_size);
|
||||||
|
|
||||||
|
return ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ice_aq_set_driver_param - Set driver parameter to share via firmware
|
* ice_aq_set_driver_param - Set driver parameter to share via firmware
|
||||||
* @hw: pointer to the HW struct
|
* @hw: pointer to the HW struct
|
||||||
|
@ -214,5 +214,9 @@ int
|
|||||||
ice_aq_read_i2c(struct ice_hw *hw, struct ice_aqc_link_topo_addr topo_addr,
|
ice_aq_read_i2c(struct ice_hw *hw, struct ice_aqc_link_topo_addr topo_addr,
|
||||||
u16 bus_addr, __le16 addr, u8 params, u8 *data,
|
u16 bus_addr, __le16 addr, u8 params, u8 *data,
|
||||||
struct ice_sq_cd *cd);
|
struct ice_sq_cd *cd);
|
||||||
|
int
|
||||||
|
ice_aq_write_i2c(struct ice_hw *hw, struct ice_aqc_link_topo_addr topo_addr,
|
||||||
|
u16 bus_addr, __le16 addr, u8 params, u8 *data,
|
||||||
|
struct ice_sq_cd *cd);
|
||||||
bool ice_fw_supports_report_dflt_cfg(struct ice_hw *hw);
|
bool ice_fw_supports_report_dflt_cfg(struct ice_hw *hw);
|
||||||
#endif /* _ICE_COMMON_H_ */
|
#endif /* _ICE_COMMON_H_ */
|
||||||
|
@ -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 @@ exit:
|
|||||||
/**
|
/**
|
||||||
* 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 @@ exit:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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)
|
||||||
{
|
{
|
||||||
return 0;
|
struct gnss_serial *gnss = tty->driver_data;
|
||||||
|
|
||||||
|
/* Only allow to write on TTY 0 */
|
||||||
|
if (!gnss || gnss != gnss->back->gnss_serial[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_delayed_work_sync(&gnss->read_work);
|
kthread_cancel_work_sync(&gnss->write_work);
|
||||||
kfree(gnss);
|
kthread_cancel_delayed_work_sync(&gnss->read_work);
|
||||||
pf->gnss_serial = NULL;
|
kfree(gnss);
|
||||||
|
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 New Issue
Block a user