Commit 2dac6381 authored by Jinjian Song's avatar Jinjian Song Committed by David S. Miller

net: wwan: t7xx: Add fastboot WWAN port

On early detection of wwan device in fastboot mode, driver sets
up CLDMA0 HW tx/rx queues for raw data transfer and then create
fastboot port to userspace.

Application can use this port to flash firmware and collect
core dump by fastboot protocol commands.
E.g., flash firmware through fastboot port:
 - "download:%08x": write data to memory with the download size.
 - "flash:%s": write the previously downloaded image to the named partition.
 - "reboot": reboot the device.

Link: https://android.googlesource.com/platform/system/core/+/refs/heads/main/fastboot/README.mdSigned-off-by: default avatarJinjian Song <jinjian.song@fibocom.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d27553c1
...@@ -125,6 +125,20 @@ The driver exposes an AT port by implementing AT WWAN Port. ...@@ -125,6 +125,20 @@ The driver exposes an AT port by implementing AT WWAN Port.
The userspace end of the control port is a /dev/wwan0at0 character The userspace end of the control port is a /dev/wwan0at0 character
device. Application shall use this interface to issue AT commands. device. Application shall use this interface to issue AT commands.
fastboot port userspace ABI
---------------------------
/dev/wwan0fastboot0 character device
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The driver exposes a fastboot protocol interface by implementing
fastboot WWAN Port. The userspace end of the fastboot channel pipe is a
/dev/wwan0fastboot0 character device. Application shall use this interface for
fastboot protocol communication.
Please note that driver needs to be reloaded to export /dev/wwan0fastboot0
port, because device needs a cold reset after enter ``fastboot_switching``
mode.
The MediaTek's T700 modem supports the 3GPP TS 27.007 [4] specification. The MediaTek's T700 modem supports the 3GPP TS 27.007 [4] specification.
References References
...@@ -146,3 +160,7 @@ speak the Mobile Interface Broadband Model (MBIM) protocol"* ...@@ -146,3 +160,7 @@ speak the Mobile Interface Broadband Model (MBIM) protocol"*
[4] *Specification # 27.007 - 3GPP* [4] *Specification # 27.007 - 3GPP*
- https://www.3gpp.org/DynaReport/27007.htm - https://www.3gpp.org/DynaReport/27007.htm
[5] *fastboot "a mechanism for communicating with bootloaders"*
- https://android.googlesource.com/platform/system/core/+/refs/heads/main/fastboot/README.md
...@@ -112,6 +112,9 @@ static const struct t7xx_port_conf t7xx_early_port_conf[] = { ...@@ -112,6 +112,9 @@ static const struct t7xx_port_conf t7xx_early_port_conf[] = {
.txq_exp_index = CLDMA_Q_IDX_DUMP, .txq_exp_index = CLDMA_Q_IDX_DUMP,
.rxq_exp_index = CLDMA_Q_IDX_DUMP, .rxq_exp_index = CLDMA_Q_IDX_DUMP,
.path_id = CLDMA_ID_AP, .path_id = CLDMA_ID_AP,
.ops = &wwan_sub_port_ops,
.name = "fastboot",
.port_type = WWAN_PORT_FASTBOOT,
}, },
}; };
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
/* /*
* Copyright (c) 2021, MediaTek Inc. * Copyright (c) 2021, MediaTek Inc.
* Copyright (c) 2021-2022, Intel Corporation. * Copyright (c) 2021-2022, Intel Corporation.
* Copyright (c) 2024, Fibocom Wireless Inc.
* *
* Authors: * Authors:
* Amir Hanania <amir.hanania@intel.com> * Amir Hanania <amir.hanania@intel.com>
...@@ -15,6 +16,7 @@ ...@@ -15,6 +16,7 @@
* Chiranjeevi Rapolu <chiranjeevi.rapolu@intel.com> * Chiranjeevi Rapolu <chiranjeevi.rapolu@intel.com>
* Eliot Lee <eliot.lee@intel.com> * Eliot Lee <eliot.lee@intel.com>
* Sreehari Kancharla <sreehari.kancharla@intel.com> * Sreehari Kancharla <sreehari.kancharla@intel.com>
* Jinjian Song <jinjian.song@fibocom.com>
*/ */
#include <linux/atomic.h> #include <linux/atomic.h>
...@@ -33,7 +35,7 @@ ...@@ -33,7 +35,7 @@
#include "t7xx_port_proxy.h" #include "t7xx_port_proxy.h"
#include "t7xx_state_monitor.h" #include "t7xx_state_monitor.h"
static int t7xx_port_ctrl_start(struct wwan_port *port) static int t7xx_port_wwan_start(struct wwan_port *port)
{ {
struct t7xx_port *port_mtk = wwan_port_get_drvdata(port); struct t7xx_port *port_mtk = wwan_port_get_drvdata(port);
...@@ -44,30 +46,60 @@ static int t7xx_port_ctrl_start(struct wwan_port *port) ...@@ -44,30 +46,60 @@ static int t7xx_port_ctrl_start(struct wwan_port *port)
return 0; return 0;
} }
static void t7xx_port_ctrl_stop(struct wwan_port *port) static void t7xx_port_wwan_stop(struct wwan_port *port)
{ {
struct t7xx_port *port_mtk = wwan_port_get_drvdata(port); struct t7xx_port *port_mtk = wwan_port_get_drvdata(port);
atomic_dec(&port_mtk->usage_cnt); atomic_dec(&port_mtk->usage_cnt);
} }
static int t7xx_port_ctrl_tx(struct wwan_port *port, struct sk_buff *skb) static int t7xx_port_fastboot_tx(struct t7xx_port *port, struct sk_buff *skb)
{
struct sk_buff *cur = skb, *tx_skb;
size_t actual, len, offset = 0;
int txq_mtu;
int ret;
txq_mtu = t7xx_get_port_mtu(port);
if (txq_mtu < 0)
return -EINVAL;
actual = cur->len;
while (actual) {
len = min_t(size_t, actual, txq_mtu);
tx_skb = __dev_alloc_skb(len, GFP_KERNEL);
if (!tx_skb)
return -ENOMEM;
skb_put_data(tx_skb, cur->data + offset, len);
ret = t7xx_port_send_raw_skb(port, tx_skb);
if (ret) {
dev_kfree_skb(tx_skb);
dev_err(port->dev, "Write error on fastboot port, %d\n", ret);
break;
}
offset += len;
actual -= len;
}
dev_kfree_skb(skb);
return 0;
}
static int t7xx_port_ctrl_tx(struct t7xx_port *port, struct sk_buff *skb)
{ {
struct t7xx_port *port_private = wwan_port_get_drvdata(port);
const struct t7xx_port_conf *port_conf; const struct t7xx_port_conf *port_conf;
struct sk_buff *cur = skb, *cloned; struct sk_buff *cur = skb, *cloned;
struct t7xx_fsm_ctl *ctl; struct t7xx_fsm_ctl *ctl;
enum md_state md_state; enum md_state md_state;
int cnt = 0, ret; int cnt = 0, ret;
if (!port_private->chan_enable) port_conf = port->port_conf;
return -EINVAL; ctl = port->t7xx_dev->md->fsm_ctl;
port_conf = port_private->port_conf;
ctl = port_private->t7xx_dev->md->fsm_ctl;
md_state = t7xx_fsm_get_md_state(ctl); md_state = t7xx_fsm_get_md_state(ctl);
if (md_state == MD_STATE_WAITING_FOR_HS1 || md_state == MD_STATE_WAITING_FOR_HS2) { if (md_state == MD_STATE_WAITING_FOR_HS1 || md_state == MD_STATE_WAITING_FOR_HS2) {
dev_warn(port_private->dev, "Cannot write to %s port when md_state=%d\n", dev_warn(port->dev, "Cannot write to %s port when md_state=%d\n",
port_conf->name, md_state); port_conf->name, md_state);
return -ENODEV; return -ENODEV;
} }
...@@ -75,10 +107,10 @@ static int t7xx_port_ctrl_tx(struct wwan_port *port, struct sk_buff *skb) ...@@ -75,10 +107,10 @@ static int t7xx_port_ctrl_tx(struct wwan_port *port, struct sk_buff *skb)
while (cur) { while (cur) {
cloned = skb_clone(cur, GFP_KERNEL); cloned = skb_clone(cur, GFP_KERNEL);
cloned->len = skb_headlen(cur); cloned->len = skb_headlen(cur);
ret = t7xx_port_send_skb(port_private, cloned, 0, 0); ret = t7xx_port_send_skb(port, cloned, 0, 0);
if (ret) { if (ret) {
dev_kfree_skb(cloned); dev_kfree_skb(cloned);
dev_err(port_private->dev, "Write error on %s port, %d\n", dev_err(port->dev, "Write error on %s port, %d\n",
port_conf->name, ret); port_conf->name, ret);
return cnt ? cnt + ret : ret; return cnt ? cnt + ret : ret;
} }
...@@ -93,14 +125,53 @@ static int t7xx_port_ctrl_tx(struct wwan_port *port, struct sk_buff *skb) ...@@ -93,14 +125,53 @@ static int t7xx_port_ctrl_tx(struct wwan_port *port, struct sk_buff *skb)
return 0; return 0;
} }
static int t7xx_port_wwan_tx(struct wwan_port *port, struct sk_buff *skb)
{
struct t7xx_port *port_private = wwan_port_get_drvdata(port);
const struct t7xx_port_conf *port_conf = port_private->port_conf;
int ret;
if (!port_private->chan_enable)
return -EINVAL;
if (port_conf->port_type != WWAN_PORT_FASTBOOT)
ret = t7xx_port_ctrl_tx(port_private, skb);
else
ret = t7xx_port_fastboot_tx(port_private, skb);
return ret;
}
static const struct wwan_port_ops wwan_ops = { static const struct wwan_port_ops wwan_ops = {
.start = t7xx_port_ctrl_start, .start = t7xx_port_wwan_start,
.stop = t7xx_port_ctrl_stop, .stop = t7xx_port_wwan_stop,
.tx = t7xx_port_ctrl_tx, .tx = t7xx_port_wwan_tx,
}; };
static void t7xx_port_wwan_create(struct t7xx_port *port)
{
const struct t7xx_port_conf *port_conf = port->port_conf;
unsigned int header_len = sizeof(struct ccci_header), mtu;
struct wwan_port_caps caps;
if (!port->wwan.wwan_port) {
mtu = t7xx_get_port_mtu(port);
caps.frag_len = mtu - header_len;
caps.headroom_len = header_len;
port->wwan.wwan_port = wwan_create_port(port->dev, port_conf->port_type,
&wwan_ops, &caps, port);
if (IS_ERR(port->wwan.wwan_port))
dev_err(port->dev, "Unable to create WWAN port %s", port_conf->name);
}
}
static int t7xx_port_wwan_init(struct t7xx_port *port) static int t7xx_port_wwan_init(struct t7xx_port *port)
{ {
const struct t7xx_port_conf *port_conf = port->port_conf;
if (port_conf->port_type == WWAN_PORT_FASTBOOT)
t7xx_port_wwan_create(port);
port->rx_length_th = RX_QUEUE_MAXLEN; port->rx_length_th = RX_QUEUE_MAXLEN;
return 0; return 0;
} }
...@@ -152,21 +223,14 @@ static int t7xx_port_wwan_disable_chl(struct t7xx_port *port) ...@@ -152,21 +223,14 @@ static int t7xx_port_wwan_disable_chl(struct t7xx_port *port)
static void t7xx_port_wwan_md_state_notify(struct t7xx_port *port, unsigned int state) 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; const struct t7xx_port_conf *port_conf = port->port_conf;
unsigned int header_len = sizeof(struct ccci_header), mtu;
struct wwan_port_caps caps; if (port_conf->port_type == WWAN_PORT_FASTBOOT)
return;
if (state != MD_STATE_READY) if (state != MD_STATE_READY)
return; return;
if (!port->wwan.wwan_port) { t7xx_port_wwan_create(port);
mtu = t7xx_get_port_mtu(port);
caps.frag_len = mtu - header_len;
caps.headroom_len = header_len;
port->wwan.wwan_port = wwan_create_port(port->dev, port_conf->port_type,
&wwan_ops, &caps, port);
if (IS_ERR(port->wwan.wwan_port))
dev_err(port->dev, "Unable to create WWWAN port %s", port_conf->name);
}
} }
struct port_ops wwan_sub_port_ops = { struct port_ops wwan_sub_port_ops = {
......
...@@ -229,6 +229,7 @@ static void t7xx_lk_stage_event_handling(struct t7xx_fsm_ctl *ctl, unsigned int ...@@ -229,6 +229,7 @@ static void t7xx_lk_stage_event_handling(struct t7xx_fsm_ctl *ctl, unsigned int
struct cldma_ctrl *md_ctrl; struct cldma_ctrl *md_ctrl;
enum lk_event_id lk_event; enum lk_event_id lk_event;
struct device *dev; struct device *dev;
struct t7xx_port *port;
dev = &md->t7xx_dev->pdev->dev; dev = &md->t7xx_dev->pdev->dev;
lk_event = FIELD_GET(MISC_LK_EVENT_MASK, status); lk_event = FIELD_GET(MISC_LK_EVENT_MASK, status);
...@@ -244,6 +245,9 @@ static void t7xx_lk_stage_event_handling(struct t7xx_fsm_ctl *ctl, unsigned int ...@@ -244,6 +245,9 @@ static void t7xx_lk_stage_event_handling(struct t7xx_fsm_ctl *ctl, unsigned int
t7xx_cldma_stop(md_ctrl); t7xx_cldma_stop(md_ctrl);
t7xx_cldma_switch_cfg(md_ctrl, CLDMA_DEDICATED_Q_CFG); t7xx_cldma_switch_cfg(md_ctrl, CLDMA_DEDICATED_Q_CFG);
port = &ctl->md->port_prox->ports[0];
port->port_conf->ops->enable_chl(port);
t7xx_cldma_start(md_ctrl); t7xx_cldma_start(md_ctrl);
if (lk_event == LK_EVENT_CREATE_POST_DL_PORT) if (lk_event == LK_EVENT_CREATE_POST_DL_PORT)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment