diff options
-rw-r--r-- | Documentation/networking/device_drivers/wwan/t7xx.rst | 18 | ||||
-rw-r--r-- | drivers/net/wwan/t7xx/t7xx_port_proxy.c | 3 | ||||
-rw-r--r-- | drivers/net/wwan/t7xx/t7xx_port_wwan.c | 116 | ||||
-rw-r--r-- | drivers/net/wwan/t7xx/t7xx_state_monitor.c | 4 |
4 files changed, 115 insertions, 26 deletions
diff --git a/Documentation/networking/device_drivers/wwan/t7xx.rst b/Documentation/networking/device_drivers/wwan/t7xx.rst index 8429b9927341..f346f5f85f15 100644 --- a/Documentation/networking/device_drivers/wwan/t7xx.rst +++ b/Documentation/networking/device_drivers/wwan/t7xx.rst @@ -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 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. References @@ -146,3 +160,7 @@ speak the Mobile Interface Broadband Model (MBIM) protocol"* [4] *Specification # 27.007 - 3GPP* - 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 diff --git a/drivers/net/wwan/t7xx/t7xx_port_proxy.c b/drivers/net/wwan/t7xx/t7xx_port_proxy.c index e53a152faee4..8f5e01705af2 100644 --- a/drivers/net/wwan/t7xx/t7xx_port_proxy.c +++ b/drivers/net/wwan/t7xx/t7xx_port_proxy.c @@ -112,6 +112,9 @@ static const 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 = &wwan_sub_port_ops, + .name = "fastboot", + .port_type = WWAN_PORT_FASTBOOT, }, }; diff --git a/drivers/net/wwan/t7xx/t7xx_port_wwan.c b/drivers/net/wwan/t7xx/t7xx_port_wwan.c index ddc20ddfa734..4b23ba693f3f 100644 --- a/drivers/net/wwan/t7xx/t7xx_port_wwan.c +++ b/drivers/net/wwan/t7xx/t7xx_port_wwan.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2021, MediaTek Inc. * Copyright (c) 2021-2022, Intel Corporation. + * Copyright (c) 2024, Fibocom Wireless Inc. * * Authors: * Amir Hanania <[email protected]> @@ -15,6 +16,7 @@ * Chiranjeevi Rapolu <[email protected]> * Eliot Lee <[email protected]> * Sreehari Kancharla <[email protected]> + * Jinjian Song <[email protected]> */ #include <linux/atomic.h> @@ -33,7 +35,7 @@ #include "t7xx_port_proxy.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); @@ -44,30 +46,60 @@ static int t7xx_port_ctrl_start(struct wwan_port *port) 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); 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; struct sk_buff *cur = skb, *cloned; struct t7xx_fsm_ctl *ctl; enum md_state md_state; int cnt = 0, ret; - if (!port_private->chan_enable) - return -EINVAL; - - port_conf = port_private->port_conf; - ctl = port_private->t7xx_dev->md->fsm_ctl; + 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_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); return -ENODEV; } @@ -75,10 +107,10 @@ static int t7xx_port_ctrl_tx(struct wwan_port *port, struct sk_buff *skb) while (cur) { cloned = skb_clone(cur, GFP_KERNEL); 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) { 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); return cnt ? cnt + ret : ret; } @@ -93,14 +125,53 @@ static int t7xx_port_ctrl_tx(struct wwan_port *port, struct sk_buff *skb) 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 = { - .start = t7xx_port_ctrl_start, - .stop = t7xx_port_ctrl_stop, - .tx = t7xx_port_ctrl_tx, + .start = t7xx_port_wwan_start, + .stop = t7xx_port_wwan_stop, + .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) { + 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; return 0; } @@ -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) { 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) return; - 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 WWWAN port %s", port_conf->name); - } + t7xx_port_wwan_create(port); } struct port_ops wwan_sub_port_ops = { diff --git a/drivers/net/wwan/t7xx/t7xx_state_monitor.c b/drivers/net/wwan/t7xx/t7xx_state_monitor.c index 038377fed102..9889ca4621cf 100644 --- a/drivers/net/wwan/t7xx/t7xx_state_monitor.c +++ b/drivers/net/wwan/t7xx/t7xx_state_monitor.c @@ -229,6 +229,7 @@ static void t7xx_lk_stage_event_handling(struct t7xx_fsm_ctl *ctl, unsigned int struct cldma_ctrl *md_ctrl; enum lk_event_id lk_event; struct device *dev; + struct t7xx_port *port; dev = &md->t7xx_dev->pdev->dev; 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 t7xx_cldma_stop(md_ctrl); 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); if (lk_event == LK_EVENT_CREATE_POST_DL_PORT) |