[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <87c8edd-41e0-136-d1ac-168ceff5855@linux.intel.com>
Date: Tue, 17 Jan 2023 16:07:51 +0200 (EET)
From: Ilpo Järvinen <ilpo.jarvinen@...ux.intel.com>
To: m.chetan.kumar@...ux.intel.com
cc: Netdev <netdev@...r.kernel.org>, kuba@...nel.org,
davem@...emloft.net, johannes@...solutions.net,
ryazanov.s.a@...il.com, loic.poulain@...aro.org,
ricardo.martinez@...ux.intel.com,
chiranjeevi.rapolu@...ux.intel.com, haijun.liu@...iatek.com,
edumazet@...gle.com, pabeni@...hat.com,
chandrashekar.devegowda@...el.com, linuxwwan@...el.com,
linuxwwan_5g@...el.com,
Mishra Soumya Prakash <soumya.prakash.mishra@...el.com>,
Jesse Brandeburg <jesse.brandeburg@...el.com>
Subject: Re: [PATCH v4 net-next 4/5] net: wwan: t7xx: Enable devlink based
fw flashing and coredump collection
On Mon, 16 Jan 2023, m.chetan.kumar@...ux.intel.com wrote:
> From: M Chetan Kumar <m.chetan.kumar@...ux.intel.com>
>
> Adds support for t7xx wwan device firmware flashing & coredump collection
> using devlink.
>
> 1> Driver Registers with Devlink framework.
> 2> Implements devlink ops flash_update callback that programs modem fw.
> 3> Creates region & snapshot required for device coredump log collection.
>
> On early detection of wwan device in fastboot mode driver sets up CLDMA0 HW
> tx/rx queues for raw data transfer and then registers to devlink framework.
> On user space application issuing command for firmware update the driver
> sends fastboot flash command & firmware to program NAND.
>
> In flashing procedure the fastboot command & response are exchanged between
> driver and device. Once firmware flashing is success completion status is
> reported to user space application.
>
> Below is the devlink command usage for firmware flashing
>
> $devlink dev flash pci/$BDF file ABC.img component ABC
>
> Note: ABC.img is the firmware to be programmed to "ABC" partition.
>
> In case of coredump collection when wwan device encounters an exception
> it reboots & stays in fastboot mode for coredump collection by host driver.
> On detecting exception state driver collects the core dump, creates the
> devlink region & reports an event to user space application for dump
> collection. The user space application invokes devlink region read command
> for dump collection.
>
> Below are the devlink commands used for coredump collection.
>
> devlink region new pci/$BDF/mr_dump
> devlink region read pci/$BDF/mr_dump snapshot $ID address $ADD length $LEN
> devlink region del pci/$BDF/mr_dump snapshot $ID
>
> Signed-off-by: M Chetan Kumar <m.chetan.kumar@...ux.intel.com>
> Signed-off-by: Devegowda Chandrashekar <chandrashekar.devegowda@...el.com>
> Signed-off-by: Mishra Soumya Prakash <soumya.prakash.mishra@...el.com>
> Reviewed-by: Jesse Brandeburg <jesse.brandeburg@...el.com>
> --
> v4:
> * cppcheck - reduce variableScope for
> - skb_ccci & ret var in t7xx_port_ap_msg_tx.
> - ret in t7xx_devlink_port_read.
> - len, skb & ret in t7xx_devlink_port_write.
> v3:
> * No Change.
> v2:
> * Remove devlink pointer inside the port state container.
> * Rename t7xx_devlink_region_list to t7xx_devlink_region_infos &
> use region index in initialization.
> * Change t7xx_devlink_region_infos to const.
> * Handle remaining packet data if the buffer is less than the skb data.
> * Drop t7xx_devlink_fb_send_buffer(), push fragmentation logic to
> t7xx_devlink_port_write().
> * Add "\n" to log message.
> * Move mrdump_region allocation to devlink initialization.
> * Drop snprintf for CTS command fill.
> * Drop intermediate mdata buffer & zipsize.
> * For mcmd use strcmp instead of strncmp.
> * Drop set_fastboot_dl instead use devlink param for fastboot operational mode.
> * Drop unnecessary logs.
> * Change t7xx_devlink_create_region to t7xx_devlink_create_regions.
> * Use BUILD_BUG_ON on array size checks.
> * Use ARRAY_SIZE inside loop.
> * Correct indentation.
> * Drop odd empty line.
> * Push common devlink initialization code to t7xx_devlink_init.
> * Use skb_queue_purge instead of running loop to free skbs.
> * Change t7xx_regions index to enums.
> * Remove dev in devlink container.
> * Refactor struct to separate out devlink static and dynamic data structs.
> * Use min_t.
> * Drop unnecessary var assginment during initialization.
> * Change while() to for().
> * Correct size check.
> * Rename result to ret.
> * Clean-up error handling path in t7xx_devlink_fb_get_core & t7xx_devlink_fb_dump_log.
> * Drop __func__ in log message.
> * Change NOTY to NOTIFY.
> * Push channel enable or disable cb to port proxy.
> * Use array index in t7xx_devlink_region_list initialization.
> * Drop t7xx_port_proxy_get_port_by_name() instead access port name directly via port_prox.
> * Drop udev based event reporting logic.
> * Drop get_core prefix in goto label.
> * Remove unnessary header files.
> * Allocate memory for mrdump_region->buf inside get_core.
> * Remove 'region->buf' in t7xx_devlink_region_snapshot.
> * Destroy workqueue on following error case in 'devlink_init'.
> * Remove useless checks(dl->mode) and condition(dl->wq).
> * Support devlink component versioning.
> * Kconfig changes to select devlink.
> ---
> drivers/net/wwan/Kconfig | 1 +
> drivers/net/wwan/t7xx/Makefile | 4 +-
> drivers/net/wwan/t7xx/t7xx_pci.c | 16 +-
> drivers/net/wwan/t7xx/t7xx_pci.h | 2 +
> drivers/net/wwan/t7xx/t7xx_port.h | 2 +
> drivers/net/wwan/t7xx/t7xx_port_ap_msg.c | 79 +++
> drivers/net/wwan/t7xx/t7xx_port_ap_msg.h | 11 +
> drivers/net/wwan/t7xx/t7xx_port_devlink.c | 669 +++++++++++++++++++++
> drivers/net/wwan/t7xx/t7xx_port_devlink.h | 86 +++
> drivers/net/wwan/t7xx/t7xx_port_proxy.c | 32 +
> drivers/net/wwan/t7xx/t7xx_port_proxy.h | 4 +
> drivers/net/wwan/t7xx/t7xx_port_wwan.c | 22 +-
> drivers/net/wwan/t7xx/t7xx_reg.h | 6 +
> drivers/net/wwan/t7xx/t7xx_state_monitor.c | 37 +-
> 14 files changed, 945 insertions(+), 26 deletions(-)
> create mode 100644 drivers/net/wwan/t7xx/t7xx_port_ap_msg.c
> create mode 100644 drivers/net/wwan/t7xx/t7xx_port_ap_msg.h
> create mode 100644 drivers/net/wwan/t7xx/t7xx_port_devlink.c
> create mode 100644 drivers/net/wwan/t7xx/t7xx_port_devlink.h
>
> diff --git a/drivers/net/wwan/Kconfig b/drivers/net/wwan/Kconfig
> index 410b0245114e..dd7a9883c1ff 100644
> --- a/drivers/net/wwan/Kconfig
> +++ b/drivers/net/wwan/Kconfig
> @@ -108,6 +108,7 @@ config IOSM
> config MTK_T7XX
> tristate "MediaTek PCIe 5G WWAN modem T7xx device"
> depends on PCI
> + select NET_DEVLINK
> select RELAY if WWAN_DEBUGFS
> help
> Enables MediaTek PCIe based 5G WWAN modem (T7xx series) device.
> diff --git a/drivers/net/wwan/t7xx/Makefile b/drivers/net/wwan/t7xx/Makefile
> index ba5c607404a4..c3f1520b3c49 100644
> --- a/drivers/net/wwan/t7xx/Makefile
> +++ b/drivers/net/wwan/t7xx/Makefile
> @@ -18,7 +18,9 @@ mtk_t7xx-y:= t7xx_pci.o \
> t7xx_hif_dpmaif_rx.o \
> t7xx_dpmaif.o \
> t7xx_netdev.o \
> - t7xx_pci_rescan.o
> + t7xx_pci_rescan.o \
> + t7xx_port_devlink.o \
> + t7xx_port_ap_msg.o
>
> mtk_t7xx-$(CONFIG_WWAN_DEBUGFS) += \
> t7xx_port_trace.o \
> diff --git a/drivers/net/wwan/t7xx/t7xx_pci.c b/drivers/net/wwan/t7xx/t7xx_pci.c
> index 3f5ebbc11b82..624f96b42775 100644
> --- a/drivers/net/wwan/t7xx/t7xx_pci.c
> +++ b/drivers/net/wwan/t7xx/t7xx_pci.c
> @@ -40,6 +40,7 @@
> #include "t7xx_pci.h"
> #include "t7xx_pci_rescan.h"
> #include "t7xx_pcie_mac.h"
> +#include "t7xx_port_devlink.h"
> #include "t7xx_reg.h"
> #include "t7xx_state_monitor.h"
>
> @@ -107,7 +108,7 @@ static int t7xx_pci_pm_init(struct t7xx_pci_dev *t7xx_dev)
> pm_runtime_set_autosuspend_delay(&pdev->dev, PM_AUTOSUSPEND_MS);
> pm_runtime_use_autosuspend(&pdev->dev);
>
> - return t7xx_wait_pm_config(t7xx_dev);
> + return 0;
> }
>
> void t7xx_pci_pm_init_late(struct t7xx_pci_dev *t7xx_dev)
> @@ -704,16 +705,20 @@ static int t7xx_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> t7xx_pci_infracfg_ao_calc(t7xx_dev);
> t7xx_mhccif_init(t7xx_dev);
>
> - ret = t7xx_md_init(t7xx_dev);
> + ret = t7xx_devlink_register(t7xx_dev);
> if (ret)
> return ret;
>
> + ret = t7xx_md_init(t7xx_dev);
> + if (ret)
> + goto err_devlink_unregister;
> +
> t7xx_pcie_mac_interrupts_dis(t7xx_dev);
>
> ret = t7xx_interrupt_init(t7xx_dev);
> if (ret) {
> t7xx_md_exit(t7xx_dev);
> - return ret;
> + goto err_devlink_unregister;
> }
>
> t7xx_rescan_done();
> @@ -721,6 +726,10 @@ static int t7xx_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> t7xx_pcie_mac_interrupts_en(t7xx_dev);
>
> return 0;
> +
> +err_devlink_unregister:
> + t7xx_devlink_unregister(t7xx_dev);
> + return ret;
> }
>
> static void t7xx_pci_remove(struct pci_dev *pdev)
> @@ -730,6 +739,7 @@ static void t7xx_pci_remove(struct pci_dev *pdev)
>
> t7xx_dev = pci_get_drvdata(pdev);
> t7xx_md_exit(t7xx_dev);
> + t7xx_devlink_unregister(t7xx_dev);
>
> for (i = 0; i < EXT_INT_NUM; i++) {
> if (!t7xx_dev->intr_handler[i])
> diff --git a/drivers/net/wwan/t7xx/t7xx_pci.h b/drivers/net/wwan/t7xx/t7xx_pci.h
> index 112efa534eac..44a8a5034696 100644
> --- a/drivers/net/wwan/t7xx/t7xx_pci.h
> +++ b/drivers/net/wwan/t7xx/t7xx_pci.h
> @@ -59,6 +59,7 @@ typedef irqreturn_t (*t7xx_intr_callback)(int irq, void *param);
> * @md_pm_lock: protects PCIe sleep lock
> * @sleep_disable_count: PCIe L1.2 lock counter
> * @sleep_lock_acquire: indicates that sleep has been disabled
> + * @dl: devlink struct
> */
> struct t7xx_pci_dev {
> t7xx_intr_callback intr_handler[EXT_INT_NUM];
> @@ -81,6 +82,7 @@ struct t7xx_pci_dev {
> #ifdef CONFIG_WWAN_DEBUGFS
> struct dentry *debugfs_dir;
> #endif
> + struct t7xx_devlink *dl;
> };
>
> enum t7xx_pm_id {
> diff --git a/drivers/net/wwan/t7xx/t7xx_port.h b/drivers/net/wwan/t7xx/t7xx_port.h
> index 09acb1ef144d..dfa7ad2a9796 100644
> --- a/drivers/net/wwan/t7xx/t7xx_port.h
> +++ b/drivers/net/wwan/t7xx/t7xx_port.h
> @@ -42,6 +42,8 @@ enum port_ch {
> /* to AP */
> PORT_CH_AP_CONTROL_RX = 0x1000,
> PORT_CH_AP_CONTROL_TX = 0x1001,
> + PORT_CH_AP_MSG_RX = 0x101E,
> + PORT_CH_AP_MSG_TX = 0x101F,
>
> /* to MD */
> PORT_CH_CONTROL_RX = 0x2000,
> diff --git a/drivers/net/wwan/t7xx/t7xx_port_ap_msg.c b/drivers/net/wwan/t7xx/t7xx_port_ap_msg.c
> new file mode 100644
> index 000000000000..9621f013de5d
> --- /dev/null
> +++ b/drivers/net/wwan/t7xx/t7xx_port_ap_msg.c
> @@ -0,0 +1,79 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2022-2023, Intel Corporation.
> + */
> +
> +#include "t7xx_port.h"
> +#include "t7xx_port_ap_msg.h"
> +#include "t7xx_port_devlink.h"
> +#include "t7xx_port_proxy.h"
> +#include "t7xx_state_monitor.h"
> +
> +int t7xx_port_ap_msg_tx(struct t7xx_port *port, char *buff, size_t len)
> +{
> + const struct t7xx_port_conf *port_conf;
> + size_t offset, chunk_len = 0, txq_mtu;
> + struct t7xx_fsm_ctl *ctl;
> + enum md_state md_state;
> +
> + if (!len || !port->chan_enable)
> + return -EINVAL;
> +
> + port_conf = port->port_conf;
> + ctl = port->t7xx_dev->md->fsm_ctl;
> + md_state = t7xx_fsm_get_md_state(ctl);
> + if (md_state == MD_STATE_WAITING_FOR_HS1 || md_state == MD_STATE_WAITING_FOR_HS2) {
> + dev_warn(port->dev, "Cannot write to %s port when md_state=%d\n",
> + port_conf->name, md_state);
> + return -ENODEV;
> + }
> +
> + txq_mtu = t7xx_get_port_mtu(port);
> + for (offset = 0; offset < len; offset += chunk_len) {
> + struct sk_buff *skb_ccci;
> + int ret;
> +
> + chunk_len = min(len - offset, txq_mtu - sizeof(struct ccci_header));
> + skb_ccci = t7xx_port_alloc_skb(chunk_len);
> + if (!skb_ccci)
> + return -ENOMEM;
> +
> + skb_put_data(skb_ccci, buff + offset, chunk_len);
> + ret = t7xx_port_send_skb(port, skb_ccci, 0, 0);
> + if (ret) {
> + dev_kfree_skb_any(skb_ccci);
> + dev_err(port->dev, "Write error on %s port, %d\n",
> + port_conf->name, ret);
> + return ret;
> + }
> + }
> +
> + return len;
> +}
> +
> +static int t7xx_port_ap_msg_init(struct t7xx_port *port)
> +{
> + struct t7xx_devlink *dl = port->t7xx_dev->dl;
> +
> + port->rx_length_th = T7XX_MAX_QUEUE_LENGTH;
> + dl->status = T7XX_DEVLINK_IDLE;
> + dl->port = port;
> +
> + return 0;
> +}
> +
> +static void t7xx_port_ap_msg_uninit(struct t7xx_port *port)
> +{
> + struct t7xx_devlink *dl = port->t7xx_dev->dl;
> +
> + dl->mode = T7XX_NORMAL_MODE;
> + skb_queue_purge(&port->rx_skb_list);
> +}
> +
> +struct port_ops ap_msg_port_ops = {
> + .init = &t7xx_port_ap_msg_init,
> + .recv_skb = &t7xx_port_enqueue_skb,
> + .uninit = &t7xx_port_ap_msg_uninit,
> + .enable_chl = &t7xx_port_enable_chl,
> + .disable_chl = &t7xx_port_disable_chl,
> +};
> diff --git a/drivers/net/wwan/t7xx/t7xx_port_ap_msg.h b/drivers/net/wwan/t7xx/t7xx_port_ap_msg.h
> new file mode 100644
> index 000000000000..4838d87d86cf
> --- /dev/null
> +++ b/drivers/net/wwan/t7xx/t7xx_port_ap_msg.h
> @@ -0,0 +1,11 @@
> +/* SPDX-License-Identifier: GPL-2.0-only
> + *
> + * Copyright (c) 2022-2023, Intel Corporation.
> + */
> +
> +#ifndef __T7XX_PORT_AP_MSG_H__
> +#define __T7XX_PORT_AP_MSG_H__
> +
> +int t7xx_port_ap_msg_tx(struct t7xx_port *port, char *buff, size_t len);
> +
> +#endif /* __T7XX_PORT_AP_MSG_H__ */
> diff --git a/drivers/net/wwan/t7xx/t7xx_port_devlink.c b/drivers/net/wwan/t7xx/t7xx_port_devlink.c
> new file mode 100644
> index 000000000000..7786f8cc5e8e
> --- /dev/null
> +++ b/drivers/net/wwan/t7xx/t7xx_port_devlink.c
> @@ -0,0 +1,669 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2022-2023, Intel Corporation.
> + */
> +
> +#include <linux/vmalloc.h>
> +
> +#include "t7xx_hif_cldma.h"
> +#include "t7xx_pci_rescan.h"
> +#include "t7xx_port.h"
> +#include "t7xx_port_ap_msg.h"
> +#include "t7xx_port_devlink.h"
> +#include "t7xx_port_proxy.h"
> +#include "t7xx_state_monitor.h"
> +
> +static struct t7xx_devlink_region_info t7xx_devlink_region_infos[] = {
> + [T7XX_MRDUMP_INDEX] = {"mr_dump", T7XX_MRDUMP_SIZE},
> + [T7XX_LKDUMP_INDEX] = {"lk_dump", T7XX_LKDUMP_SIZE},
> +};
> +
> +static int t7xx_devlink_port_read(struct t7xx_port *port, char *buf, size_t count)
> +{
> + struct sk_buff *skb;
> + int read_len;
> +
> + spin_lock_irq(&port->rx_wq.lock);
> + if (skb_queue_empty(&port->rx_skb_list)) {
> + int ret = wait_event_interruptible_locked_irq(port->rx_wq,
> + !skb_queue_empty(&port->rx_skb_list));
> + if (ret == -ERESTARTSYS) {
> + spin_unlock_irq(&port->rx_wq.lock);
> + return -EINTR;
> + }
> + }
> + skb = skb_dequeue(&port->rx_skb_list);
> + spin_unlock_irq(&port->rx_wq.lock);
> +
> + read_len = min_t(size_t, count, skb->len);
> + memcpy(buf, skb->data, read_len);
> +
> + if (read_len < skb->len) {
> + skb_pull(skb, read_len);
> + skb_queue_head(&port->rx_skb_list, skb);
> + } else {
> + consume_skb(skb);
> + }
> +
> + return read_len;
> +}
> +
> +static int t7xx_devlink_port_write(struct t7xx_port *port, const char *buf, size_t count)
> +{
> + const struct t7xx_port_conf *port_conf = port->port_conf;
> + size_t actual = count, offset = 0;
> + int txq_mtu;
> +
> + txq_mtu = t7xx_get_port_mtu(port);
> + if (txq_mtu < 0)
> + return -EINVAL;
> +
> + while (actual) {
> + int len = min_t(size_t, actual, txq_mtu);
> + struct sk_buff *skb;
> + int ret;
> +
> + skb = __dev_alloc_skb(len, GFP_KERNEL);
> + if (!skb)
> + return -ENOMEM;
> +
> + skb_put_data(skb, buf + offset, len);
> + ret = t7xx_port_send_raw_skb(port, skb);
> + if (ret) {
> + dev_err(port->dev, "write error on %s, size: %d, ret: %d\n",
> + port_conf->name, len, ret);
> + dev_kfree_skb(skb);
> + return ret;
> + }
> +
> + offset += len;
> + actual -= len;
> + }
> +
> + return count;
> +}
> +
> +static int t7xx_devlink_fb_handle_response(struct t7xx_port *port, char *data)
> +{
> + char status[T7XX_FB_RESPONSE_SIZE + 1];
> + int ret = 0, index;
> +
> + for (index = 0; index < T7XX_FB_RESP_COUNT; index++) {
> + int read_bytes = t7xx_devlink_port_read(port, status, T7XX_FB_RESPONSE_SIZE);
> +
> + if (read_bytes < 0) {
> + dev_err(port->dev, "status read interrupted\n");
> + ret = -EIO;
First t7xx_devlink_port_read does -ERESTARTSYS -> -EINTR and then here you
do -ERESTARTSYS -> -EIO.
> + break;
> + }
> +
> + status[read_bytes] = '\0';
> + dev_dbg(port->dev, "raw response from device: %s\n", status);
> + if (!strncmp(status, T7XX_FB_RESP_INFO, strlen(T7XX_FB_RESP_INFO))) {
> + break;
> + } else if (!strncmp(status, T7XX_FB_RESP_OKAY, strlen(T7XX_FB_RESP_OKAY))) {
> + break;
> + } else if (!strncmp(status, T7XX_FB_RESP_FAIL, strlen(T7XX_FB_RESP_FAIL))) {
> + ret = -EPROTO;
> + break;
> + } else if (!strncmp(status, T7XX_FB_RESP_DATA, strlen(T7XX_FB_RESP_DATA))) {
> + if (data)
> + snprintf(data, T7XX_FB_RESPONSE_SIZE, "%s",
> + status + strlen(T7XX_FB_RESP_DATA));
> + break;
> + }
> + }
> +
> + return ret;
> +}
> +
> +static int t7xx_devlink_fb_raw_command(char *cmd, struct t7xx_port *port, char *data)
> +{
> + int ret, cmd_size = strlen(cmd);
> +
> + if (cmd_size > T7XX_FB_COMMAND_SIZE) {
> + dev_err(port->dev, "command length %d is long\n", cmd_size);
> + return -EINVAL;
> + }
> +
> + if (cmd_size != t7xx_devlink_port_write(port, cmd, cmd_size)) {
> + dev_err(port->dev, "raw command = %s write failed\n", cmd);
> + return -EIO;
> + }
> +
> + dev_dbg(port->dev, "raw command = %s written to the device\n", cmd);
> + ret = t7xx_devlink_fb_handle_response(port, data);
> + if (ret)
> + dev_err(port->dev, "raw command = %s response FAILURE:%d\n", cmd, ret);
> +
> + return ret;
> +}
> +
> +static int t7xx_devlink_fb_download_command(struct t7xx_port *port, size_t size)
> +{
> + char download_command[T7XX_FB_COMMAND_SIZE];
> +
> + snprintf(download_command, sizeof(download_command), "%s:%08zx",
> + T7XX_FB_CMD_DOWNLOAD, size);
> + return t7xx_devlink_fb_raw_command(download_command, port, NULL);
> +}
> +
> +static int t7xx_devlink_fb_download(struct t7xx_port *port, const u8 *buf, size_t size)
> +{
> + int ret;
> +
> + if (!size)
> + return -EINVAL;
> +
> + ret = t7xx_devlink_fb_download_command(port, size);
> + if (ret)
> + return ret;
> +
> + ret = t7xx_devlink_port_write(port, buf, size);
> + if (ret < 0)
> + return ret;
> +
> + return t7xx_devlink_fb_handle_response(port, NULL);
> +}
> +
> +static int t7xx_devlink_fb_flash(struct t7xx_port *port, const char *cmd)
> +{
> + char flash_command[T7XX_FB_COMMAND_SIZE];
> +
> + snprintf(flash_command, sizeof(flash_command), "%s:%s", T7XX_FB_CMD_FLASH, cmd);
> + return t7xx_devlink_fb_raw_command(flash_command, port, NULL);
> +}
> +
> +static int t7xx_devlink_get_part_ver_fb_mode(struct t7xx_port *port, const char *cmd, char *data)
> +{
> + char req_command[T7XX_FB_COMMAND_SIZE];
> +
> + snprintf(req_command, sizeof(req_command), "%s:%s", T7XX_FB_CMD_GET_VER, cmd);
> + return t7xx_devlink_fb_raw_command(req_command, port, data);
> +}
> +
> +static int t7xx_devlink_get_part_ver_norm_mode(struct t7xx_port *port, const char *cmd, char *data)
> +{
> + char req_command[T7XX_FB_COMMAND_SIZE];
> + int len;
> +
> + len = snprintf(req_command, sizeof(req_command), "%s:%s", T7XX_FB_CMD_GET_VER, cmd);
> + t7xx_port_ap_msg_tx(port, req_command, len);
> +
> + return t7xx_devlink_fb_handle_response(port, data);
> +}
> +
> +static int t7xx_devlink_fb_flash_partition(struct t7xx_port *port, const char *partition,
> + const u8 *buf, size_t size)
> +{
> + int ret;
> +
> + ret = t7xx_devlink_fb_download(port, buf, size);
> + if (ret < 0)
> + return ret;
> +
> + return t7xx_devlink_fb_flash(port, partition);
> +}
> +
> +static int t7xx_devlink_fb_get_core(struct t7xx_port *port)
> +{
> + u32 mrd_mb = T7XX_MRDUMP_SIZE / (1024 * 1024);
> + struct t7xx_devlink *dl = port->t7xx_dev->dl;
> + char mcmd[T7XX_FB_MCMD_SIZE + 1];
> + size_t offset_dlen = 0;
> + int clen, dlen, ret;
> +
> + dl->regions[T7XX_MRDUMP_INDEX].buf = vmalloc(dl->regions[T7XX_MRDUMP_INDEX].info->size);
> + if (!dl->regions[T7XX_MRDUMP_INDEX].buf)
> + return -ENOMEM;
> +
> + set_bit(T7XX_MRDUMP_STATUS, &dl->status);
> + ret = t7xx_devlink_fb_raw_command(T7XX_FB_CMD_OEM_MRDUMP, port, NULL);
> + if (ret) {
> + dev_err(port->dev, "%s command failed\n", T7XX_FB_CMD_OEM_MRDUMP);
> + goto free_mem;
> + }
> +
> + while (dl->regions[T7XX_MRDUMP_INDEX].info->size > offset_dlen) {
> + clen = t7xx_devlink_port_read(port, mcmd, sizeof(mcmd) - 1);
> + mcmd[clen] = '\0';
What if clen < 0?
> + if (clen == strlen(T7XX_FB_CMD_RTS) && (!strcmp(mcmd, T7XX_FB_CMD_RTS))) {
How is this different from just !strcmp()?
> + memset(mcmd, 0, sizeof(mcmd));
> + if (t7xx_devlink_port_write(port, T7XX_FB_CMD_CTS, strlen(T7XX_FB_CMD_CTS))
> + != strlen(T7XX_FB_CMD_CTS)) {
Split into two lines using a variable for the result, it's much more
readable then. Or perhaps create a helper?
> + dev_err(port->dev, "write for _CTS failed:%zu\n",
> + strlen(T7XX_FB_CMD_CTS));
> + goto free_mem;
> + }
> +
> + dlen = t7xx_devlink_port_read(port, dl->regions[T7XX_MRDUMP_INDEX].buf +
> + offset_dlen, T7XX_FB_MDATA_SIZE);
> + if (dlen <= 0) {
> + dev_err(port->dev, "read data error(%d)\n", dlen);
> + ret = -EPROTO;
-RESTARTSYS/-EINTR -> -EPROTO ???
> + goto free_mem;
> + }
> + offset_dlen += dlen;
> +
> + if (t7xx_devlink_port_write(port, T7XX_FB_CMD_FIN, strlen(T7XX_FB_CMD_FIN))
> + != strlen(T7XX_FB_CMD_FIN)) {
> + dev_err(port->dev, "_FIN failed, (Read %05zu:%05zu)\n",
> + strlen(T7XX_FB_CMD_FIN), offset_dlen);
> + ret = -EPROTO;
Ditto.
> + goto free_mem;
> + }
> + continue;
> + } else if ((clen == strlen(T7XX_FB_RESP_MRDUMP_DONE)) &&
> + (!strcmp(mcmd, T7XX_FB_RESP_MRDUMP_DONE))) {
!strcmp()?
> + dev_dbg(port->dev, "%s! size:%zd\n", T7XX_FB_RESP_MRDUMP_DONE, offset_dlen);
> + clear_bit(T7XX_MRDUMP_STATUS, &dl->status);
> + return 0;
> + }
> + dev_err(port->dev, "getcore protocol error (read len %05d, response %s)\n",
> + clen, mcmd);
> + ret = -EPROTO;
> + goto free_mem;
> + }
> +
> + dev_err(port->dev, "mrdump exceeds %uMB size. Discarded!\n", mrd_mb);
> +
> +free_mem:
> + vfree(dl->regions[T7XX_MRDUMP_INDEX].buf);
> + clear_bit(T7XX_MRDUMP_STATUS, &dl->status);
> + return ret;
> +}
> +
> +static int t7xx_devlink_fb_dump_log(struct t7xx_port *port)
> +{
> + struct t7xx_devlink *dl = port->t7xx_dev->dl;
> + struct t7xx_devlink_region *lkdump_region;
> + char rsp[T7XX_FB_RESPONSE_SIZE];
> + int datasize = 0, ret;
> + size_t offset = 0;
> +
> + if (dl->status != T7XX_DEVLINK_IDLE) {
> + dev_err(&dl->t7xx_dev->pdev->dev, "Modem is busy!\n");
> + return -EBUSY;
> + }
> +
> + set_bit(T7XX_LKDUMP_STATUS, &dl->status);
> + ret = t7xx_devlink_fb_raw_command(T7XX_FB_CMD_OEM_LKDUMP, port, rsp);
> + if (ret) {
> + dev_err(port->dev, "%s command returns failure\n", T7XX_FB_CMD_OEM_LKDUMP);
> + clear_bit(T7XX_LKDUMP_STATUS, &dl->status);
> + return ret;
> + }
> +
> + ret = kstrtoint(rsp, 16, &datasize);
> + if (ret) {
> + dev_err(port->dev, "kstrtoint error!\n");
Maybe something looking more English than "kstrtoint" would make the user
happier... :-)
> + clear_bit(T7XX_LKDUMP_STATUS, &dl->status);
I'd goto into unroll path rather than sprinkle this clear_bit()
everywhere in this function.
> + return ret;
> + }
> +
> + lkdump_region = &dl->regions[T7XX_LKDUMP_INDEX];
> + if (datasize > lkdump_region->info->size) {
> + dev_err(port->dev, "lkdump size is more than %dKB. Discarded!\n",
> + T7XX_LKDUMP_SIZE / 1024);
> + clear_bit(T7XX_LKDUMP_STATUS, &dl->status);
> + return -EPROTO;
-EFBIG ?
> + }
> +
> + lkdump_region->buf = vmalloc(lkdump_region->info->size);
> + if (!lkdump_region->buf) {
> + clear_bit(T7XX_LKDUMP_STATUS, &dl->status);
> + return -ENOMEM;
> + }
> +
> + while (datasize > 0) {
> + int dlen = t7xx_devlink_port_read(port, lkdump_region->buf + offset, datasize);
> +
> + if (dlen <= 0) {
> + dev_err(port->dev, "lkdump read error ret = %d\n", dlen);
> + clear_bit(T7XX_LKDUMP_STATUS, &dl->status);
> + return -EPROTO;
-ERESTARTSYS/-EINTR -> -EPROTO?
> + }
> +
> + datasize -= dlen;
> + offset += dlen;
> + }
> +
> + dev_dbg(port->dev, "LKDUMP DONE! size:%zd\n", offset);
> + clear_bit(T7XX_LKDUMP_STATUS, &dl->status);
> + return t7xx_devlink_fb_handle_response(port, NULL);
> +}
> +
> +static int t7xx_devlink_flash_update(struct devlink *devlink,
> + struct devlink_flash_update_params *params,
> + struct netlink_ext_ack *extack)
> +{
> + struct t7xx_devlink *dl = devlink_priv(devlink);
> + const char *component = params->component;
> + const struct firmware *fw = params->fw;
> + struct t7xx_port *port;
> + int ret;
> +
> + if (dl->mode != T7XX_FB_DL_MODE) {
> + dev_err(&dl->t7xx_dev->pdev->dev, "Modem is not in fastboot download mode!\n");
> + ret = -EPERM;
> + goto err_out;
> + }
> +
> + if (dl->status != T7XX_DEVLINK_IDLE) {
> + dev_err(&dl->t7xx_dev->pdev->dev, "Modem is busy!\n");
> + ret = -EBUSY;
> + goto err_out;
> + }
> +
> + if (!component || !fw->data) {
> + ret = -EINVAL;
> + goto err_out;
> + }
> +
> + set_bit(T7XX_FLASH_STATUS, &dl->status);
> + port = dl->port;
> + dev_dbg(port->dev, "flash partition name:%s binary size:%zu\n", component, fw->size);
> + ret = t7xx_devlink_fb_flash_partition(port, component, fw->data, fw->size);
> + if (ret) {
> + devlink_flash_update_status_notify(devlink, "flashing failure!",
> + params->component, 0, 0);
> + } else {
> + devlink_flash_update_status_notify(devlink, "flashing success!",
> + params->component, 0, 0);
One could use %s and !ret ? "success" : "failure"
> + }
> + clear_bit(T7XX_FLASH_STATUS, &dl->status);
> +
> +err_out:
> + return ret;
> +}
> +
> +enum t7xx_devlink_param_id {
> + T7XX_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
> + T7XX_DEVLINK_PARAM_ID_FASTBOOT,
> +};
> +
> +static const struct devlink_param t7xx_devlink_params[] = {
> + DEVLINK_PARAM_DRIVER(T7XX_DEVLINK_PARAM_ID_FASTBOOT,
> + "fastboot", DEVLINK_PARAM_TYPE_BOOL,
> + BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
> + NULL, NULL, NULL),
> +};
> +
> +bool t7xx_devlink_param_get_fastboot(struct devlink *devlink)
> +{
> + union devlink_param_value saved_value;
> +
> + devlink_param_driverinit_value_get(devlink, T7XX_DEVLINK_PARAM_ID_FASTBOOT,
> + &saved_value);
> + return saved_value.vbool;
> +}
> +
> +static int t7xx_devlink_reload_down(struct devlink *devlink, bool netns_change,
> + enum devlink_reload_action action,
> + enum devlink_reload_limit limit,
> + struct netlink_ext_ack *extack)
> +{
> + struct t7xx_devlink *dl = devlink_priv(devlink);
> +
> + switch (action) {
> + case DEVLINK_RELOAD_ACTION_DRIVER_REINIT:
> + return 0;
> + case DEVLINK_RELOAD_ACTION_FW_ACTIVATE:
> + if (!dl->mode)
> + return -EPERM;
> + return t7xx_devlink_fb_raw_command(T7XX_FB_CMD_REBOOT, dl->port, NULL);
> + default:
> + /* Unsupported action should not get to this function */
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static int t7xx_devlink_reload_up(struct devlink *devlink,
> + enum devlink_reload_action action,
> + enum devlink_reload_limit limit,
> + u32 *actions_performed,
> + struct netlink_ext_ack *extack)
> +{
> + struct t7xx_devlink *dl = devlink_priv(devlink);
Add newline.
> + *actions_performed = BIT(action);
> + switch (action) {
> + case DEVLINK_RELOAD_ACTION_DRIVER_REINIT:
> + case DEVLINK_RELOAD_ACTION_FW_ACTIVATE:
> + t7xx_rescan_queue_work(dl->t7xx_dev->pdev);
> + return 0;
> + default:
> + /* Unsupported action should not get to this function */
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static int t7xx_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
> + struct netlink_ext_ack *extack)
> +{
> + struct t7xx_devlink *dl = devlink_priv(devlink);
> + char *part_name, *ver, *part_no, *data;
> + int ret, total_part, i, ver_len;
> + struct t7xx_port *port;
> +
> + port = dl->port;
> + port->port_conf->ops->enable_chl(port);
> +
> + if (dl->status != T7XX_DEVLINK_IDLE) {
> + dev_err(&dl->t7xx_dev->pdev->dev, "Modem is busy!\n");
> + return -EBUSY;
> + }
> +
> + data = kzalloc(T7XX_FB_RESPONSE_SIZE, GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + set_bit(T7XX_GET_INFO, &dl->status);
> + if (dl->mode == T7XX_FB_DL_MODE)
> + ret = t7xx_devlink_get_part_ver_fb_mode(port, "", data);
> + else
> + ret = t7xx_devlink_get_part_ver_norm_mode(port, "", data);
> +
> + if (ret < 0)
> + goto err_clear_bit;
> +
> + part_no = strsep(&data, ",");
> + if (kstrtoint(part_no, 16, &total_part)) {
> + dev_err(&dl->t7xx_dev->pdev->dev, "kstrtoint error!\n");
More meaningful error msg.
> + ret = -EINVAL;
> + goto err_clear_bit;
> + }
> +
> + for (i = 0; i < total_part; i++) {
The whole operation below is quite fancy, I'd add some comment telling the
intent.
> + part_name = strsep(&data, ",");
> + ver = strsep(&data, ",");
Can ver become NULL here?
> + ver_len = strlen(ver);
> + if (ver[ver_len - 2] == 0x5C && ver[ver_len - 1] == 0x6E)
> + ver[ver_len - 4] = '\0';
Is ver_len guaranteed to be large enough?
> + ret = devlink_info_version_running_put_ext(req, part_name, ver,
> + DEVLINK_INFO_VERSION_TYPE_COMPONENT);
> + }
> +
> +err_clear_bit:
> + clear_bit(T7XX_GET_INFO, &dl->status);
> + kfree(data);
> + return ret;
> +}
> +
> +/* Call back function for devlink ops */
> +static const struct devlink_ops devlink_flash_ops = {
> + .supported_flash_update_params = DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK,
> + .flash_update = t7xx_devlink_flash_update,
> + .reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT) |
> + BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE),
> + .info_get = t7xx_devlink_info_get,
> + .reload_down = t7xx_devlink_reload_down,
> + .reload_up = t7xx_devlink_reload_up,
> +};
> +
> +static int t7xx_devlink_region_snapshot(struct devlink *dl, const struct devlink_region_ops *ops,
> + struct netlink_ext_ack *extack, u8 **data)
> +{
> + struct t7xx_devlink *t7xx_dl = devlink_priv(dl);
> + struct t7xx_devlink_region *region = ops->priv;
> + struct t7xx_port *port = t7xx_dl->port;
> + u8 *snapshot_mem;
> +
> + if (t7xx_dl->status != T7XX_DEVLINK_IDLE)
> + return -EBUSY;
> +
> + if (!strncmp(ops->name, "mr_dump", strlen("mr_dump"))) {
> + snapshot_mem = vmalloc(region->info->size);
> + memcpy(snapshot_mem, region->buf, region->info->size);
> + *data = snapshot_mem;
> + } else if (!strncmp(ops->name, "lk_dump", strlen("lk_dump"))) {
> + int ret;
> +
> + ret = t7xx_devlink_fb_dump_log(port);
> + if (ret)
> + return ret;
> +
> + *data = region->buf;
> + }
> +
> + return 0;
> +}
> +
> +/* To create regions for dump files */
> +static int t7xx_devlink_create_regions(struct t7xx_devlink *dl)
> +{
> + int ret, i;
> +
> + BUILD_BUG_ON(ARRAY_SIZE(t7xx_devlink_region_infos) > ARRAY_SIZE(dl->regions));
> + for (i = 0; i < ARRAY_SIZE(t7xx_devlink_region_infos); i++) {
> + dl->regions[i].info = &t7xx_devlink_region_infos[i];
> + dl->regions[i].ops.name = dl->regions[i].info->name;
> + dl->regions[i].ops.snapshot = t7xx_devlink_region_snapshot;
> + dl->regions[i].ops.destructor = vfree;
> + dl->regions[i].dlreg = devlink_region_create(dl->ctx, &dl->regions[i].ops,
> + T7XX_MAX_SNAPSHOTS,
> + t7xx_devlink_region_infos[i].size);
> + if (IS_ERR(dl->regions[i].dlreg)) {
> + ret = PTR_ERR(dl->regions[i].dlreg);
> + dev_err(dl->port->dev, "devlink region fail,err %d\n", ret);
creating devlink region failed ?
> + for ( ; i >= 0; i--)
> + devlink_region_destroy(dl->regions[i].dlreg);
while (i >= 0) + ...regions[i--]... is simpler and equal.
> +
> + return ret;
> + }
> +
> + dl->regions[i].ops.priv = &dl->regions[i];
> + }
> +
> + return 0;
> +}
> +
> +int t7xx_devlink_register(struct t7xx_pci_dev *t7xx_dev)
> +{
> + union devlink_param_value value;
> + struct devlink *dl_ctx;
> +
> + dl_ctx = devlink_alloc(&devlink_flash_ops, sizeof(struct t7xx_devlink),
> + &t7xx_dev->pdev->dev);
> + if (!dl_ctx)
> + return -ENOMEM;
> +
> + t7xx_dev->dl = devlink_priv(dl_ctx);
> + t7xx_dev->dl->ctx = dl_ctx;
> + t7xx_dev->dl->t7xx_dev = t7xx_dev;
> + devlink_params_register(dl_ctx, t7xx_devlink_params, ARRAY_SIZE(t7xx_devlink_params));
> + value.vbool = false;
> + devlink_param_driverinit_value_set(dl_ctx, T7XX_DEVLINK_PARAM_ID_FASTBOOT, value);
> + devlink_set_features(dl_ctx, DEVLINK_F_RELOAD);
> + devlink_register(dl_ctx);
> +
> + return 0;
> +}
> +
> +void t7xx_devlink_unregister(struct t7xx_pci_dev *t7xx_dev)
> +{
> + struct devlink *dl_ctx = t7xx_dev->dl->ctx;
> +
> + devlink_unregister(dl_ctx);
> + devlink_params_unregister(dl_ctx, t7xx_devlink_params, ARRAY_SIZE(t7xx_devlink_params));
> + devlink_free(dl_ctx);
> +}
> +
> +static void t7xx_devlink_work(struct work_struct *work)
> +{
> + struct t7xx_devlink *dl;
> +
> + dl = container_of(work, struct t7xx_devlink, ws);
> + t7xx_devlink_fb_get_core(dl->port);
> +}
> +
> +/**
> + * t7xx_devlink_init - Initialize devlink to t7xx driver
> + * @port: Pointer to port structure
> + *
> + * Returns: 0 on success and error values on failure
> + */
> +static int t7xx_devlink_init(struct t7xx_port *port)
> +{
> + struct t7xx_devlink *dl = port->t7xx_dev->dl;
> + struct workqueue_struct *dl_wq;
> + int rc;
> +
> + dl_wq = create_workqueue("t7xx_devlink");
> + if (!dl_wq) {
> + dev_err(port->dev, "create_workqueue failed\n");
> + return -ENODATA;
> + }
> +
> + INIT_WORK(&dl->ws, t7xx_devlink_work);
> + port->rx_length_th = T7XX_MAX_QUEUE_LENGTH;
> +
> + dl->mode = T7XX_NORMAL_MODE;
> + dl->status = T7XX_DEVLINK_IDLE;
> + dl->wq = dl_wq;
> + dl->port = port;
> +
> + rc = t7xx_devlink_create_regions(dl);
> + if (rc) {
> + destroy_workqueue(dl->wq);
> + dev_err(port->dev, "devlink region creation failed, rc %d\n", rc);
> + return -ENOMEM;
> + }
> +
> + return 0;
> +}
> +
> +static void t7xx_devlink_uninit(struct t7xx_port *port)
> +{
> + struct t7xx_devlink *dl = port->t7xx_dev->dl;
> + int i;
> +
> + vfree(dl->regions[T7XX_MRDUMP_INDEX].buf);
> +
> + dl->mode = T7XX_NORMAL_MODE;
> + destroy_workqueue(dl->wq);
> +
> + BUILD_BUG_ON(ARRAY_SIZE(t7xx_devlink_region_infos) > ARRAY_SIZE(dl->regions));
The same BUILD_BUG_ON again? Maybe just make a single static_assert()
outside of the functions.
> + for (i = 0; i < ARRAY_SIZE(t7xx_devlink_region_infos); ++i)
> + devlink_region_destroy(dl->regions[i].dlreg);
> +
> + skb_queue_purge(&port->rx_skb_list);
> +}
> +
> +static int t7xx_devlink_enable_chl(struct t7xx_port *port)
> +{
> + struct t7xx_devlink *dl = port->t7xx_dev->dl;
> +
> + t7xx_port_enable_chl(port);
> + if (dl->mode == T7XX_FB_DUMP_MODE)
> + queue_work(dl->wq, &dl->ws);
> +
> + return 0;
> +}
> +
> +struct port_ops devlink_port_ops = {
> + .init = &t7xx_devlink_init,
> + .recv_skb = &t7xx_port_enqueue_skb,
> + .uninit = &t7xx_devlink_uninit,
> + .enable_chl = &t7xx_devlink_enable_chl,
> + .disable_chl = &t7xx_port_disable_chl,
> +};
> diff --git a/drivers/net/wwan/t7xx/t7xx_port_devlink.h b/drivers/net/wwan/t7xx/t7xx_port_devlink.h
> new file mode 100644
> index 000000000000..4074004110b8
> --- /dev/null
> +++ b/drivers/net/wwan/t7xx/t7xx_port_devlink.h
> @@ -0,0 +1,86 @@
> +/* SPDX-License-Identifier: GPL-2.0-only
> + *
> + * Copyright (c) 2022-2023, Intel Corporation.
> + */
> +
> +#ifndef __T7XX_PORT_DEVLINK_H__
> +#define __T7XX_PORT_DEVLINK_H__
> +
> +#include <net/devlink.h>
> +#include <linux/string.h>
> +
> +#include "t7xx_pci.h"
> +
> +#define T7XX_MAX_QUEUE_LENGTH 32
> +#define T7XX_FB_COMMAND_SIZE 64
> +#define T7XX_FB_RESPONSE_SIZE 512
> +#define T7XX_FB_MCMD_SIZE 64
> +#define T7XX_FB_MDATA_SIZE 1024
> +#define T7XX_FB_RESP_COUNT 30
> +
> +#define T7XX_FB_CMD_RTS "_RTS"
> +#define T7XX_FB_CMD_CTS "_CTS"
> +#define T7XX_FB_CMD_FIN "_FIN"
> +#define T7XX_FB_CMD_OEM_MRDUMP "oem mrdump"
> +#define T7XX_FB_CMD_OEM_LKDUMP "oem dump_pllk_log"
> +#define T7XX_FB_CMD_DOWNLOAD "download"
> +#define T7XX_FB_CMD_FLASH "flash"
> +#define T7XX_FB_CMD_REBOOT "reboot"
> +#define T7XX_FB_RESP_MRDUMP_DONE "MRDUMP08_DONE"
> +#define T7XX_FB_RESP_OKAY "OKAY"
> +#define T7XX_FB_RESP_FAIL "FAIL"
> +#define T7XX_FB_RESP_DATA "DATA"
> +#define T7XX_FB_RESP_INFO "INFO"
> +#define T7XX_FB_CMD_GET_VER "get_version"
> +
> +#define T7XX_FB_EVENT_SIZE 50
> +
> +#define T7XX_MAX_SNAPSHOTS 1
> +#define T7XX_MRDUMP_SIZE (160 * 1024 * 1024)
> +#define T7XX_LKDUMP_SIZE (256 * 1024)
> +#define T7XX_TOTAL_REGIONS 2
> +
> +#define T7XX_FLASH_STATUS 0
> +#define T7XX_MRDUMP_STATUS 1
> +#define T7XX_LKDUMP_STATUS 2
> +#define T7XX_GET_INFO 3
> +#define T7XX_DEVLINK_IDLE 0
> +
> +#define T7XX_NORMAL_MODE 0
> +#define T7XX_FB_DL_MODE 1
> +#define T7XX_FB_DUMP_MODE 2
> +
> +/* Internal region indexes */
> +enum t7xx_regions {
> + T7XX_MRDUMP_INDEX,
> + T7XX_LKDUMP_INDEX,
> +};
> +
> +struct t7xx_devlink_region_info {
> + const char *name;
> + size_t size;
> +};
> +
> +struct t7xx_devlink_region {
> + struct t7xx_devlink_region_info *info;
> + struct devlink_region_ops ops;
> + struct devlink_region *dlreg;
> + void *buf;
> +};
> +
> +struct t7xx_devlink {
> + struct t7xx_devlink_region regions[T7XX_TOTAL_REGIONS];
> + struct t7xx_pci_dev *t7xx_dev;
> + struct workqueue_struct *wq;
> + struct t7xx_port *port;
> + struct work_struct ws;
> + struct devlink *ctx;
> + unsigned long status;
> + u8 mode;
> +};
> +
> +bool t7xx_devlink_param_get_fastboot(struct devlink *devlink);
> +int t7xx_devlink_register(struct t7xx_pci_dev *t7xx_dev);
> +void t7xx_devlink_unregister(struct t7xx_pci_dev *t7xx_dev);
> +
> +#endif /*__T7XX_PORT_DEVLINK_H__*/
> diff --git a/drivers/net/wwan/t7xx/t7xx_port_proxy.c b/drivers/net/wwan/t7xx/t7xx_port_proxy.c
> index b457e8da098e..5cc03a5a9bcc 100644
> --- a/drivers/net/wwan/t7xx/t7xx_port_proxy.c
> +++ b/drivers/net/wwan/t7xx/t7xx_port_proxy.c
> @@ -40,6 +40,7 @@
> #define Q_IDX_CTRL 0
> #define Q_IDX_MBIM 2
> #define Q_IDX_AT_CMD 5
> +#define Q_IDX_AP_MSG 2
>
> #define INVALID_SEQ_NUM GENMASK(15, 0)
>
> @@ -97,7 +98,18 @@ static const struct t7xx_port_conf t7xx_port_conf[] = {
> .path_id = CLDMA_ID_AP,
> .ops = &ctl_port_ops,
> .name = "t7xx_ap_ctrl",
> + }, {
> + .tx_ch = PORT_CH_AP_MSG_TX,
> + .rx_ch = PORT_CH_AP_MSG_RX,
> + .txq_index = Q_IDX_AP_MSG,
> + .rxq_index = Q_IDX_AP_MSG,
> + .txq_exp_index = Q_IDX_AP_MSG,
> + .rxq_exp_index = Q_IDX_AP_MSG,
> + .path_id = CLDMA_ID_AP,
> + .ops = &ap_msg_port_ops,
> + .name = "ap_msg",
> },
> +
> };
>
> static struct t7xx_port_conf t7xx_early_port_conf[] = {
> @@ -109,6 +121,8 @@ static struct t7xx_port_conf t7xx_early_port_conf[] = {
> .txq_exp_index = CLDMA_Q_IDX_DUMP,
> .rxq_exp_index = CLDMA_Q_IDX_DUMP,
> .path_id = CLDMA_ID_AP,
> + .ops = &devlink_port_ops,
> + .name = "devlink",
> },
> };
>
> @@ -325,6 +339,24 @@ int t7xx_port_send_skb(struct t7xx_port *port, struct sk_buff *skb, unsigned int
> return t7xx_port_send_ccci_skb(port, skb, pkt_header, ex_msg);
> }
>
> +int t7xx_port_enable_chl(struct t7xx_port *port)
> +{
> + spin_lock(&port->port_update_lock);
> + port->chan_enable = true;
> + spin_unlock(&port->port_update_lock);
> +
> + return 0;
> +}
> +
> +int t7xx_port_disable_chl(struct t7xx_port *port)
> +{
> + spin_lock(&port->port_update_lock);
> + port->chan_enable = false;
> + spin_unlock(&port->port_update_lock);
> +
> + return 0;
> +}
> +
> static void t7xx_proxy_setup_ch_mapping(struct port_proxy *port_prox)
> {
> struct t7xx_port *port;
> diff --git a/drivers/net/wwan/t7xx/t7xx_port_proxy.h b/drivers/net/wwan/t7xx/t7xx_port_proxy.h
> index 0f3fb53259b7..b86594ed0458 100644
> --- a/drivers/net/wwan/t7xx/t7xx_port_proxy.h
> +++ b/drivers/net/wwan/t7xx/t7xx_port_proxy.h
> @@ -93,6 +93,8 @@ struct ctrl_msg_header {
> /* Port operations mapping */
> extern struct port_ops wwan_sub_port_ops;
> extern struct port_ops ctl_port_ops;
> +extern struct port_ops devlink_port_ops;
> +extern struct port_ops ap_msg_port_ops;
>
> #ifdef CONFIG_WWAN_DEBUGFS
> extern struct port_ops t7xx_trace_port_ops;
> @@ -108,5 +110,7 @@ int t7xx_port_proxy_chl_enable_disable(struct port_proxy *port_prox, unsigned in
> void t7xx_port_proxy_set_cfg(struct t7xx_modem *md, enum port_cfg_id cfg_id);
> int t7xx_port_proxy_recv_skb(struct cldma_queue *queue, struct sk_buff *skb);
> int t7xx_port_proxy_recv_skb_from_dedicated_queue(struct cldma_queue *queue, struct sk_buff *skb);
> +int t7xx_port_enable_chl(struct t7xx_port *port);
> +int t7xx_port_disable_chl(struct t7xx_port *port);
>
> #endif /* __T7XX_PORT_PROXY_H__ */
> diff --git a/drivers/net/wwan/t7xx/t7xx_port_wwan.c b/drivers/net/wwan/t7xx/t7xx_port_wwan.c
> index 33fa8c22598a..183dc6e97760 100644
> --- a/drivers/net/wwan/t7xx/t7xx_port_wwan.c
> +++ b/drivers/net/wwan/t7xx/t7xx_port_wwan.c
> @@ -134,24 +134,6 @@ static int t7xx_port_wwan_recv_skb(struct t7xx_port *port, struct sk_buff *skb)
> return 0;
> }
>
> -static int t7xx_port_wwan_enable_chl(struct t7xx_port *port)
> -{
> - spin_lock(&port->port_update_lock);
> - port->chan_enable = true;
> - spin_unlock(&port->port_update_lock);
> -
> - return 0;
> -}
> -
> -static int t7xx_port_wwan_disable_chl(struct t7xx_port *port)
> -{
> - spin_lock(&port->port_update_lock);
> - port->chan_enable = false;
> - spin_unlock(&port->port_update_lock);
> -
> - return 0;
> -}
> -
> static void t7xx_port_wwan_md_state_notify(struct t7xx_port *port, unsigned int state)
> {
> const struct t7xx_port_conf *port_conf = port->port_conf;
> @@ -171,7 +153,7 @@ struct port_ops wwan_sub_port_ops = {
> .init = t7xx_port_wwan_init,
> .recv_skb = t7xx_port_wwan_recv_skb,
> .uninit = t7xx_port_wwan_uninit,
> - .enable_chl = t7xx_port_wwan_enable_chl,
> - .disable_chl = t7xx_port_wwan_disable_chl,
> + .enable_chl = t7xx_port_enable_chl,
> + .disable_chl = t7xx_port_disable_chl,
> .md_state_notify = t7xx_port_wwan_md_state_notify,
> };
> diff --git a/drivers/net/wwan/t7xx/t7xx_reg.h b/drivers/net/wwan/t7xx/t7xx_reg.h
> index 3b665c6116fe..d27ba45b12ec 100644
> --- a/drivers/net/wwan/t7xx/t7xx_reg.h
> +++ b/drivers/net/wwan/t7xx/t7xx_reg.h
> @@ -101,10 +101,16 @@ enum t7xx_pm_resume_state {
> PM_RESUME_REG_STATE_L2_EXP,
> };
>
> +enum host_event_e {
> + HOST_EVENT_INIT = 0,
> + FASTBOOT_DL_NOTIFY = 0x3,
> +};
> +
> #define T7XX_PCIE_MISC_DEV_STATUS 0x0d1c
> #define MISC_RESET_TYPE_FLDR BIT(27)
> #define MISC_RESET_TYPE_PLDR BIT(26)
> #define MISC_LK_EVENT_MASK GENMASK(11, 8)
> +#define HOST_EVENT_MASK GENMASK(31, 28)
>
> enum lk_event_id {
> LK_EVENT_NORMAL = 0,
> diff --git a/drivers/net/wwan/t7xx/t7xx_state_monitor.c b/drivers/net/wwan/t7xx/t7xx_state_monitor.c
> index 6e957d3c0490..ed00f8834c76 100644
> --- a/drivers/net/wwan/t7xx/t7xx_state_monitor.c
> +++ b/drivers/net/wwan/t7xx/t7xx_state_monitor.c
> @@ -37,6 +37,7 @@
> #include "t7xx_modem_ops.h"
> #include "t7xx_pci.h"
> #include "t7xx_pcie_mac.h"
> +#include "t7xx_port_devlink.h"
> #include "t7xx_port_proxy.h"
> #include "t7xx_reg.h"
> #include "t7xx_state_monitor.h"
> @@ -206,11 +207,22 @@ static void fsm_routine_exception(struct t7xx_fsm_ctl *ctl, struct t7xx_fsm_comm
> fsm_finish_command(ctl, cmd, 0);
> }
>
> +static void t7xx_host_event_notify(struct t7xx_modem *md, unsigned int event_id)
> +{
> + u32 value;
> +
> + value = ioread32(IREG_BASE(md->t7xx_dev) + T7XX_PCIE_MISC_DEV_STATUS);
> + value &= ~HOST_EVENT_MASK;
> + value |= FIELD_PREP(HOST_EVENT_MASK, event_id);
> + iowrite32(value, IREG_BASE(md->t7xx_dev) + T7XX_PCIE_MISC_DEV_STATUS);
> +}
> +
> static void t7xx_lk_stage_event_handling(struct t7xx_fsm_ctl *ctl, unsigned int status)
> {
> struct t7xx_modem *md = ctl->md;
> struct cldma_ctrl *md_ctrl;
> enum lk_event_id lk_event;
> + struct t7xx_port *port;
> struct device *dev;
>
> dev = &md->t7xx_dev->pdev->dev;
> @@ -221,10 +233,21 @@ static void t7xx_lk_stage_event_handling(struct t7xx_fsm_ctl *ctl, unsigned int
> break;
>
> case LK_EVENT_CREATE_PD_PORT:
> + case LK_EVENT_CREATE_POST_DL_PORT:
> md_ctrl = md->md_ctrl[CLDMA_ID_AP];
> t7xx_cldma_hif_hw_init(md_ctrl);
> t7xx_cldma_stop(md_ctrl);
> t7xx_cldma_switch_cfg(md_ctrl, CLDMA_DEDICATED_Q_CFG);
> + port = ctl->md->t7xx_dev->dl->port;
> + if (WARN_ON(!port))
> + return;
> +
> + if (lk_event == LK_EVENT_CREATE_PD_PORT)
> + md->t7xx_dev->dl->mode = T7XX_FB_DUMP_MODE;
> + else
> + md->t7xx_dev->dl->mode = T7XX_FB_DL_MODE;
> +
> + port->port_conf->ops->enable_chl(port);
> t7xx_cldma_start(md_ctrl);
> break;
>
> @@ -271,13 +294,23 @@ static void fsm_routine_stopping(struct t7xx_fsm_ctl *ctl, struct t7xx_fsm_comma
> t7xx_cldma_stop(md_ctrl);
>
> if (!ctl->md->rgu_irq_asserted) {
> + if (t7xx_devlink_param_get_fastboot(t7xx_dev->dl->ctx))
> + t7xx_host_event_notify(ctl->md, FASTBOOT_DL_NOTIFY);
> +
> t7xx_mhccif_h2d_swint_trigger(t7xx_dev, H2D_CH_DRM_DISABLE_AP);
> /* Wait for the DRM disable to take effect */
> msleep(FSM_DRM_DISABLE_DELAY_MS);
>
> - err = t7xx_acpi_fldr_func(t7xx_dev);
> - if (err)
> + if (t7xx_devlink_param_get_fastboot(t7xx_dev->dl->ctx)) {
> + /* Do not try fldr because device will always wait for
> + * MHCCIF bit 13 in fastboot download flow.
> + */
> t7xx_mhccif_h2d_swint_trigger(t7xx_dev, H2D_CH_DEVICE_RESET);
> + } else {
> + err = t7xx_acpi_fldr_func(t7xx_dev);
> + if (err)
> + t7xx_mhccif_h2d_swint_trigger(t7xx_dev, H2D_CH_DEVICE_RESET);
> + }
> }
>
> fsm_finish_command(ctl, cmd, fsm_stopped_handler(ctl));
>
--
i.
Powered by blists - more mailing lists