From f55e36d5ab76c3097ff36ecea60b91c6b0d80fc8 Mon Sep 17 00:00:00 2001 From: Shai Malin Date: Mon, 13 Sep 2021 10:50:24 +0300 Subject: [PATCH 01/87] qed: Improve the stack space of filter_config() As it was reported and discussed in: https://lore.kernel.org/lkml/CAHk-=whF9F89vsfH8E9TGc0tZA-yhzi2Di8wOtquNB5vRkFX5w@mail.gmail.com/ This patch improves the stack space of qede_config_rx_mode() by splitting filter_config() to 3 functions and removing the union qed_filter_type_params. Reported-by: Naresh Kamboju Signed-off-by: Ariel Elior Signed-off-by: Shai Malin Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_l2.c | 23 ++------- .../net/ethernet/qlogic/qede/qede_filter.c | 47 ++++++++----------- include/linux/qed/qed_eth_if.h | 21 ++++----- 3 files changed, 30 insertions(+), 61 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index dfaf10edfabf..ba8c7a31cce1 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -2763,25 +2763,6 @@ static int qed_configure_filter_mcast(struct qed_dev *cdev, return qed_filter_mcast_cmd(cdev, &mcast, QED_SPQ_MODE_CB, NULL); } -static int qed_configure_filter(struct qed_dev *cdev, - struct qed_filter_params *params) -{ - enum qed_filter_rx_mode_type accept_flags; - - switch (params->type) { - case QED_FILTER_TYPE_UCAST: - return qed_configure_filter_ucast(cdev, ¶ms->filter.ucast); - case QED_FILTER_TYPE_MCAST: - return qed_configure_filter_mcast(cdev, ¶ms->filter.mcast); - case QED_FILTER_TYPE_RX_MODE: - accept_flags = params->filter.accept_flags; - return qed_configure_filter_rx_mode(cdev, accept_flags); - default: - DP_NOTICE(cdev, "Unknown filter type %d\n", (int)params->type); - return -EINVAL; - } -} - static int qed_configure_arfs_searcher(struct qed_dev *cdev, enum qed_filter_config_mode mode) { @@ -2904,7 +2885,9 @@ static const struct qed_eth_ops qed_eth_ops_pass = { .q_rx_stop = &qed_stop_rxq, .q_tx_start = &qed_start_txq, .q_tx_stop = &qed_stop_txq, - .filter_config = &qed_configure_filter, + .filter_config_rx_mode = &qed_configure_filter_rx_mode, + .filter_config_ucast = &qed_configure_filter_ucast, + .filter_config_mcast = &qed_configure_filter_mcast, .fastpath_stop = &qed_fastpath_stop, .eth_cqe_completion = &qed_fp_cqe_completion, .get_vport_stats = &qed_get_vport_stats, diff --git a/drivers/net/ethernet/qlogic/qede/qede_filter.c b/drivers/net/ethernet/qlogic/qede/qede_filter.c index a2e4dfb5cb44..f99b085b56a5 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_filter.c +++ b/drivers/net/ethernet/qlogic/qede/qede_filter.c @@ -619,30 +619,28 @@ static int qede_set_ucast_rx_mac(struct qede_dev *edev, enum qed_filter_xcast_params_type opcode, unsigned char mac[ETH_ALEN]) { - struct qed_filter_params filter_cmd; + struct qed_filter_ucast_params ucast; - memset(&filter_cmd, 0, sizeof(filter_cmd)); - filter_cmd.type = QED_FILTER_TYPE_UCAST; - filter_cmd.filter.ucast.type = opcode; - filter_cmd.filter.ucast.mac_valid = 1; - ether_addr_copy(filter_cmd.filter.ucast.mac, mac); + memset(&ucast, 0, sizeof(ucast)); + ucast.type = opcode; + ucast.mac_valid = 1; + ether_addr_copy(ucast.mac, mac); - return edev->ops->filter_config(edev->cdev, &filter_cmd); + return edev->ops->filter_config_ucast(edev->cdev, &ucast); } static int qede_set_ucast_rx_vlan(struct qede_dev *edev, enum qed_filter_xcast_params_type opcode, u16 vid) { - struct qed_filter_params filter_cmd; + struct qed_filter_ucast_params ucast; - memset(&filter_cmd, 0, sizeof(filter_cmd)); - filter_cmd.type = QED_FILTER_TYPE_UCAST; - filter_cmd.filter.ucast.type = opcode; - filter_cmd.filter.ucast.vlan_valid = 1; - filter_cmd.filter.ucast.vlan = vid; + memset(&ucast, 0, sizeof(ucast)); + ucast.type = opcode; + ucast.vlan_valid = 1; + ucast.vlan = vid; - return edev->ops->filter_config(edev->cdev, &filter_cmd); + return edev->ops->filter_config_ucast(edev->cdev, &ucast); } static int qede_config_accept_any_vlan(struct qede_dev *edev, bool action) @@ -1057,18 +1055,17 @@ static int qede_set_mcast_rx_mac(struct qede_dev *edev, enum qed_filter_xcast_params_type opcode, unsigned char *mac, int num_macs) { - struct qed_filter_params filter_cmd; + struct qed_filter_mcast_params mcast; int i; - memset(&filter_cmd, 0, sizeof(filter_cmd)); - filter_cmd.type = QED_FILTER_TYPE_MCAST; - filter_cmd.filter.mcast.type = opcode; - filter_cmd.filter.mcast.num = num_macs; + memset(&mcast, 0, sizeof(mcast)); + mcast.type = opcode; + mcast.num = num_macs; for (i = 0; i < num_macs; i++, mac += ETH_ALEN) - ether_addr_copy(filter_cmd.filter.mcast.mac[i], mac); + ether_addr_copy(mcast.mac[i], mac); - return edev->ops->filter_config(edev->cdev, &filter_cmd); + return edev->ops->filter_config_mcast(edev->cdev, &mcast); } int qede_set_mac_addr(struct net_device *ndev, void *p) @@ -1194,7 +1191,6 @@ void qede_config_rx_mode(struct net_device *ndev) { enum qed_filter_rx_mode_type accept_flags; struct qede_dev *edev = netdev_priv(ndev); - struct qed_filter_params rx_mode; unsigned char *uc_macs, *temp; struct netdev_hw_addr *ha; int rc, uc_count; @@ -1220,10 +1216,6 @@ void qede_config_rx_mode(struct net_device *ndev) netif_addr_unlock_bh(ndev); - /* Configure the struct for the Rx mode */ - memset(&rx_mode, 0, sizeof(struct qed_filter_params)); - rx_mode.type = QED_FILTER_TYPE_RX_MODE; - /* Remove all previous unicast secondary macs and multicast macs * (configure / leave the primary mac) */ @@ -1271,8 +1263,7 @@ void qede_config_rx_mode(struct net_device *ndev) qede_config_accept_any_vlan(edev, false); } - rx_mode.filter.accept_flags = accept_flags; - edev->ops->filter_config(edev->cdev, &rx_mode); + edev->ops->filter_config_rx_mode(edev->cdev, accept_flags); out: kfree(uc_macs); } diff --git a/include/linux/qed/qed_eth_if.h b/include/linux/qed/qed_eth_if.h index 812a4d751163..4df0bf0a0864 100644 --- a/include/linux/qed/qed_eth_if.h +++ b/include/linux/qed/qed_eth_if.h @@ -145,12 +145,6 @@ struct qed_filter_mcast_params { unsigned char mac[64][ETH_ALEN]; }; -union qed_filter_type_params { - enum qed_filter_rx_mode_type accept_flags; - struct qed_filter_ucast_params ucast; - struct qed_filter_mcast_params mcast; -}; - enum qed_filter_type { QED_FILTER_TYPE_UCAST, QED_FILTER_TYPE_MCAST, @@ -158,11 +152,6 @@ enum qed_filter_type { QED_MAX_FILTER_TYPES, }; -struct qed_filter_params { - enum qed_filter_type type; - union qed_filter_type_params filter; -}; - struct qed_tunn_params { u16 vxlan_port; u8 update_vxlan_port; @@ -314,8 +303,14 @@ struct qed_eth_ops { int (*q_tx_stop)(struct qed_dev *cdev, u8 rss_id, void *handle); - int (*filter_config)(struct qed_dev *cdev, - struct qed_filter_params *params); + int (*filter_config_rx_mode)(struct qed_dev *cdev, + enum qed_filter_rx_mode_type type); + + int (*filter_config_ucast)(struct qed_dev *cdev, + struct qed_filter_ucast_params *params); + + int (*filter_config_mcast)(struct qed_dev *cdev, + struct qed_filter_mcast_params *params); int (*fastpath_stop)(struct qed_dev *cdev); From 9eb4c320be9c1b357dbc905c7e60dfffa8c854b9 Mon Sep 17 00:00:00 2001 From: Len Baker Date: Sun, 12 Sep 2021 15:10:57 +0200 Subject: [PATCH 02/87] nfp: Prefer struct_size over open coded arithmetic As noted in the "Deprecated Interfaces, Language Features, Attributes, and Conventions" documentation [1], size calculations (especially multiplication) should not be performed in memory allocator (or similar) function arguments due to the risk of them overflowing. This could lead to values wrapping around and a smaller allocation being made than the caller was expecting. Using those allocations could lead to linear overflows of heap memory and other misbehaviors. So, use the struct_size() helper to do the arithmetic instead of the argument "size + count * size" in the kzalloc() function. [1] https://www.kernel.org/doc/html/v5.14/process/deprecated.html#open-coded-arithmetic-in-allocator-arguments Signed-off-by: Len Baker Reviewed-by: Gustavo A. R. Silva Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_repr.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c index 3b8e675087de..369f6ae700c7 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c @@ -499,8 +499,7 @@ struct nfp_reprs *nfp_reprs_alloc(unsigned int num_reprs) { struct nfp_reprs *reprs; - reprs = kzalloc(sizeof(*reprs) + - num_reprs * sizeof(struct net_device *), GFP_KERNEL); + reprs = kzalloc(struct_size(reprs, reprs, num_reprs), GFP_KERNEL); if (!reprs) return NULL; reprs->num_reprs = num_reprs; From 3537e507b662d6511157ed3cd981a3e83e57601d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 13 Sep 2021 15:20:22 +0200 Subject: [PATCH 03/87] nfc: do not break pr_debug() call into separate lines Remove unneeded line break between pr_debug and arguments. Signed-off-by: Krzysztof Kozlowski Signed-off-by: David S. Miller --- net/nfc/hci/llc_shdlc.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index aef750d7787c..71e10347e6a9 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -201,8 +201,7 @@ static void llc_shdlc_reset_t2(struct llc_shdlc *shdlc, int y_nr) del_timer_sync(&shdlc->t2_timer); shdlc->t2_active = false; - pr_debug - ("All sent frames acked. Stopped T2(retransmit)\n"); + pr_debug("All sent frames acked. Stopped T2(retransmit)\n"); } } else { skb = skb_peek(&shdlc->ack_pending_q); @@ -211,8 +210,7 @@ static void llc_shdlc_reset_t2(struct llc_shdlc *shdlc, int y_nr) msecs_to_jiffies(SHDLC_T2_VALUE_MS)); shdlc->t2_active = true; - pr_debug - ("Start T2(retransmit) for remaining unacked sent frames\n"); + pr_debug("Start T2(retransmit) for remaining unacked sent frames\n"); } } @@ -522,12 +520,11 @@ static void llc_shdlc_handle_send_queue(struct llc_shdlc *shdlc) unsigned long time_sent; if (shdlc->send_q.qlen) - pr_debug - ("sendQlen=%d ns=%d dnr=%d rnr=%s w_room=%d unackQlen=%d\n", - shdlc->send_q.qlen, shdlc->ns, shdlc->dnr, - shdlc->rnr == false ? "false" : "true", - shdlc->w - llc_shdlc_w_used(shdlc->ns, shdlc->dnr), - shdlc->ack_pending_q.qlen); + pr_debug("sendQlen=%d ns=%d dnr=%d rnr=%s w_room=%d unackQlen=%d\n", + shdlc->send_q.qlen, shdlc->ns, shdlc->dnr, + shdlc->rnr == false ? "false" : "true", + shdlc->w - llc_shdlc_w_used(shdlc->ns, shdlc->dnr), + shdlc->ack_pending_q.qlen); while (shdlc->send_q.qlen && shdlc->ack_pending_q.qlen < shdlc->w && (shdlc->rnr == false)) { @@ -649,8 +646,7 @@ static void llc_shdlc_sm_work(struct work_struct *work) llc_shdlc_handle_send_queue(shdlc); if (shdlc->t1_active && timer_pending(&shdlc->t1_timer) == 0) { - pr_debug - ("Handle T1(send ack) elapsed (T1 now inactive)\n"); + pr_debug("Handle T1(send ack) elapsed (T1 now inactive)\n"); shdlc->t1_active = false; r = llc_shdlc_send_s_frame(shdlc, S_FRAME_RR, @@ -660,8 +656,7 @@ static void llc_shdlc_sm_work(struct work_struct *work) } if (shdlc->t2_active && timer_pending(&shdlc->t2_timer) == 0) { - pr_debug - ("Handle T2(retransmit) elapsed (T2 inactive)\n"); + pr_debug("Handle T2(retransmit) elapsed (T2 inactive)\n"); shdlc->t2_active = false; From 747e3910d66909b34630904c7de7914745186fc1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 13 Sep 2021 15:20:24 +0200 Subject: [PATCH 04/87] nfc: fdp: drop unneeded debug prints ftrace is a preferred and standard way to debug entering and exiting functions so drop useless debug prints. Signed-off-by: Krzysztof Kozlowski Signed-off-by: David S. Miller --- drivers/nfc/fdp/i2c.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/nfc/fdp/i2c.c b/drivers/nfc/fdp/i2c.c index 051c43a2a52f..f78670bf41e0 100644 --- a/drivers/nfc/fdp/i2c.c +++ b/drivers/nfc/fdp/i2c.c @@ -335,7 +335,6 @@ static int fdp_nci_i2c_probe(struct i2c_client *client) return r; } - dev_dbg(dev, "I2C driver loaded\n"); return 0; } From b7b96587c18b709a7ebea73c8d140dfed81ab1cb Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 13 Sep 2021 15:20:25 +0200 Subject: [PATCH 05/87] nfc: pn533: drop unneeded debug prints ftrace is a preferred and standard way to debug entering and exiting functions so drop useless debug prints. Signed-off-by: Krzysztof Kozlowski Signed-off-by: David S. Miller --- drivers/nfc/pn533/i2c.c | 4 ---- drivers/nfc/pn533/pn533.c | 2 -- 2 files changed, 6 deletions(-) diff --git a/drivers/nfc/pn533/i2c.c b/drivers/nfc/pn533/i2c.c index e6bf8cfe3aa7..f5610b6b9894 100644 --- a/drivers/nfc/pn533/i2c.c +++ b/drivers/nfc/pn533/i2c.c @@ -128,7 +128,6 @@ static int pn533_i2c_read(struct pn533_i2c_phy *phy, struct sk_buff **skb) static irqreturn_t pn533_i2c_irq_thread_fn(int irq, void *data) { struct pn533_i2c_phy *phy = data; - struct i2c_client *client; struct sk_buff *skb = NULL; int r; @@ -137,9 +136,6 @@ static irqreturn_t pn533_i2c_irq_thread_fn(int irq, void *data) return IRQ_NONE; } - client = phy->i2c_dev; - dev_dbg(&client->dev, "IRQ\n"); - if (phy->hard_fault != 0) return IRQ_HANDLED; diff --git a/drivers/nfc/pn533/pn533.c b/drivers/nfc/pn533/pn533.c index 2f3f3fe9a0ba..c5f127fe2d45 100644 --- a/drivers/nfc/pn533/pn533.c +++ b/drivers/nfc/pn533/pn533.c @@ -1235,8 +1235,6 @@ static void pn533_listen_mode_timer(struct timer_list *t) { struct pn533 *dev = from_timer(dev, t, listen_timer); - dev_dbg(dev->dev, "Listen mode timeout\n"); - dev->cancel_listen = 1; pn533_poll_next_mod(dev); From 9981ab2151226d5be03b220142e383a1bd91daf2 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 13 Sep 2021 15:20:26 +0200 Subject: [PATCH 06/87] nfc: pn533: use dev_err() instead of pr_err() Print error message with reference to a device. Signed-off-by: Krzysztof Kozlowski Signed-off-by: David S. Miller --- drivers/nfc/pn533/pn533.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nfc/pn533/pn533.c b/drivers/nfc/pn533/pn533.c index c5f127fe2d45..da180335422c 100644 --- a/drivers/nfc/pn533/pn533.c +++ b/drivers/nfc/pn533/pn533.c @@ -2171,7 +2171,7 @@ void pn533_recv_frame(struct pn533 *dev, struct sk_buff *skb, int status) } if (skb == NULL) { - pr_err("NULL Frame -> link is dead\n"); + dev_err(dev->dev, "NULL Frame -> link is dead\n"); goto sched_wq; } From aed4146c5503f2015e94c67f834e41a165241358 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 13 Sep 2021 15:20:27 +0200 Subject: [PATCH 07/87] nfc: pn544: drop unneeded debug prints ftrace is a preferred and standard way to debug entering and exiting functions so drop useless debug prints. Signed-off-by: Krzysztof Kozlowski Signed-off-by: David S. Miller --- drivers/nfc/pn544/mei.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/nfc/pn544/mei.c b/drivers/nfc/pn544/mei.c index 5c10aac085a4..a519fa0a53e2 100644 --- a/drivers/nfc/pn544/mei.c +++ b/drivers/nfc/pn544/mei.c @@ -22,8 +22,6 @@ static int pn544_mei_probe(struct mei_cl_device *cldev, struct nfc_mei_phy *phy; int r; - pr_info("Probing NFC pn544\n"); - phy = nfc_mei_phy_alloc(cldev); if (!phy) { pr_err("Cannot allocate memory for pn544 mei phy.\n"); @@ -46,8 +44,6 @@ static void pn544_mei_remove(struct mei_cl_device *cldev) { struct nfc_mei_phy *phy = mei_cldev_get_drvdata(cldev); - pr_info("Removing pn544\n"); - pn544_hci_remove(phy->hdev); nfc_mei_phy_free(phy); From 64758c6363ea27a68bedf70f76c6f9887957f70d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 13 Sep 2021 15:20:28 +0200 Subject: [PATCH 08/87] nfc: pn544: drop unneeded memory allocation fail messages nfc_mei_phy_alloc() already prints an error message on memory allocation failure. Signed-off-by: Krzysztof Kozlowski Signed-off-by: David S. Miller --- drivers/nfc/pn544/mei.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/nfc/pn544/mei.c b/drivers/nfc/pn544/mei.c index a519fa0a53e2..c493f2dbd0e2 100644 --- a/drivers/nfc/pn544/mei.c +++ b/drivers/nfc/pn544/mei.c @@ -23,10 +23,8 @@ static int pn544_mei_probe(struct mei_cl_device *cldev, int r; phy = nfc_mei_phy_alloc(cldev); - if (!phy) { - pr_err("Cannot allocate memory for pn544 mei phy.\n"); + if (!phy) return -ENOMEM; - } r = pn544_hci_probe(phy, &mei_phy_ops, LLC_NOP_NAME, MEI_NFC_HEADER_SIZE, 0, MEI_NFC_MAX_HCI_PAYLOAD, From 270be6940714e71328560682cf333bc0789263fc Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 13 Sep 2021 15:20:34 +0200 Subject: [PATCH 09/87] nfc: microread: drop unneeded memory allocation fail messages nfc_mei_phy_alloc() already prints an error message on memory allocation failure. Signed-off-by: Krzysztof Kozlowski Signed-off-by: David S. Miller --- drivers/nfc/microread/mei.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/nfc/microread/mei.c b/drivers/nfc/microread/mei.c index 8edf761a6b2a..00689e18dc46 100644 --- a/drivers/nfc/microread/mei.c +++ b/drivers/nfc/microread/mei.c @@ -26,10 +26,8 @@ static int microread_mei_probe(struct mei_cl_device *cldev, pr_info("Probing NFC microread\n"); phy = nfc_mei_phy_alloc(cldev); - if (!phy) { - pr_err("Cannot allocate memory for microread mei phy.\n"); + if (!phy) return -ENOMEM; - } r = microread_probe(phy, &mei_phy_ops, LLC_NOP_NAME, MEI_NFC_HEADER_SIZE, 0, MEI_NFC_MAX_HCI_PAYLOAD, From d1c624ebaa5113e5e80f63d55c1ddc19324b3b66 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 13 Sep 2021 15:20:35 +0200 Subject: [PATCH 10/87] nfc: mrvl: drop unneeded memory allocation fail messages nci_skb_alloc() already prints an error message on memory allocation failure. Signed-off-by: Krzysztof Kozlowski Signed-off-by: David S. Miller --- drivers/nfc/nfcmrvl/fw_dnld.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/nfc/nfcmrvl/fw_dnld.c b/drivers/nfc/nfcmrvl/fw_dnld.c index edac56b01fd1..e83f65596a88 100644 --- a/drivers/nfc/nfcmrvl/fw_dnld.c +++ b/drivers/nfc/nfcmrvl/fw_dnld.c @@ -76,10 +76,8 @@ static struct sk_buff *alloc_lc_skb(struct nfcmrvl_private *priv, uint8_t plen) struct nci_data_hdr *hdr; skb = nci_skb_alloc(priv->ndev, (NCI_DATA_HDR_SIZE + plen), GFP_KERNEL); - if (!skb) { - pr_err("no memory for data\n"); + if (!skb) return NULL; - } hdr = skb_put(skb, NCI_DATA_HDR_SIZE); hdr->conn_id = NCI_CORE_LC_CONNID_PROP_FW_DL; From 13bb8429ca985497bfba3a4afa6cd470d15fed10 Mon Sep 17 00:00:00 2001 From: M Chetan Kumar Date: Mon, 13 Sep 2021 18:34:12 +0530 Subject: [PATCH 11/87] net: wwan: iosm: firmware flashing and coredump collection This patch brings-in support for M.2 7560 Device firmware flashing & coredump collection using devlink. - Driver Registers with Devlink framework. - Register devlink params callback for configuring device params required in flashing or coredump flow. - Implements devlink ops flash_update callback that programs modem firmware. - Creates region & snapshot required for device coredump log collection. On early detection of device in boot rom stage. Driver registers with Devlink framework and establish transport channel for PSI (Primary Signed Image) injection. Once PSI is injected to device, the device execution stage details are read to determine whether device is in flash or exception mode. The collected information is reported to devlink user space application & based on this informationi, application proceeds with either modem firmware flashing or coredump collection. Signed-off-by: M Chetan Kumar Signed-off-by: David S. Miller --- drivers/net/wwan/Kconfig | 1 + drivers/net/wwan/iosm/Makefile | 5 +- drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.c | 6 +- drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.h | 1 + drivers/net/wwan/iosm/iosm_ipc_coredump.c | 110 +++++ drivers/net/wwan/iosm/iosm_ipc_coredump.h | 75 +++ drivers/net/wwan/iosm/iosm_ipc_devlink.c | 360 ++++++++++++++ drivers/net/wwan/iosm/iosm_ipc_devlink.h | 207 ++++++++ drivers/net/wwan/iosm/iosm_ipc_flash.c | 562 ++++++++++++++++++++++ drivers/net/wwan/iosm/iosm_ipc_flash.h | 271 +++++++++++ drivers/net/wwan/iosm/iosm_ipc_imem.c | 103 +++- drivers/net/wwan/iosm/iosm_ipc_imem.h | 19 +- drivers/net/wwan/iosm/iosm_ipc_imem_ops.c | 317 ++++++++++++ drivers/net/wwan/iosm/iosm_ipc_imem_ops.h | 49 +- 14 files changed, 2055 insertions(+), 31 deletions(-) create mode 100644 drivers/net/wwan/iosm/iosm_ipc_coredump.c create mode 100644 drivers/net/wwan/iosm/iosm_ipc_coredump.h create mode 100644 drivers/net/wwan/iosm/iosm_ipc_devlink.c create mode 100644 drivers/net/wwan/iosm/iosm_ipc_devlink.h create mode 100644 drivers/net/wwan/iosm/iosm_ipc_flash.c create mode 100644 drivers/net/wwan/iosm/iosm_ipc_flash.h diff --git a/drivers/net/wwan/Kconfig b/drivers/net/wwan/Kconfig index 77dbfc418bce..17543be14665 100644 --- a/drivers/net/wwan/Kconfig +++ b/drivers/net/wwan/Kconfig @@ -71,6 +71,7 @@ config RPMSG_WWAN_CTRL config IOSM tristate "IOSM Driver for Intel M.2 WWAN Device" depends on INTEL_IOMMU + select NET_DEVLINK help This driver enables Intel M.2 WWAN Device communication. diff --git a/drivers/net/wwan/iosm/Makefile b/drivers/net/wwan/iosm/Makefile index 4f9f0ae398e1..b838034bb120 100644 --- a/drivers/net/wwan/iosm/Makefile +++ b/drivers/net/wwan/iosm/Makefile @@ -18,6 +18,9 @@ iosm-y = \ iosm_ipc_protocol.o \ iosm_ipc_protocol_ops.o \ iosm_ipc_mux.o \ - iosm_ipc_mux_codec.o + iosm_ipc_mux_codec.o \ + iosm_ipc_devlink.o \ + iosm_ipc_flash.o \ + iosm_ipc_coredump.o obj-$(CONFIG_IOSM) := iosm.o diff --git a/drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.c b/drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.c index 519361ec40df..128c999e08bb 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.c +++ b/drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.c @@ -8,7 +8,7 @@ #include "iosm_ipc_chnl_cfg.h" /* Max. sizes of a downlink buffers */ -#define IPC_MEM_MAX_DL_FLASH_BUF_SIZE (16 * 1024) +#define IPC_MEM_MAX_DL_FLASH_BUF_SIZE (64 * 1024) #define IPC_MEM_MAX_DL_LOOPBACK_SIZE (1 * 1024 * 1024) #define IPC_MEM_MAX_DL_AT_BUF_SIZE 2048 #define IPC_MEM_MAX_DL_RPC_BUF_SIZE (32 * 1024) @@ -60,6 +60,10 @@ static struct ipc_chnl_cfg modem_cfg[] = { { IPC_MEM_CTRL_CHL_ID_6, IPC_MEM_PIPE_12, IPC_MEM_PIPE_13, IPC_MEM_MAX_TDS_MBIM, IPC_MEM_MAX_TDS_MBIM, IPC_MEM_MAX_DL_MBIM_BUF_SIZE, WWAN_PORT_MBIM }, + /* Flash Channel/Coredump Channel */ + { IPC_MEM_CTRL_CHL_ID_7, IPC_MEM_PIPE_0, IPC_MEM_PIPE_1, + IPC_MEM_MAX_TDS_FLASH_UL, IPC_MEM_MAX_TDS_FLASH_DL, + IPC_MEM_MAX_DL_FLASH_BUF_SIZE, WWAN_PORT_UNKNOWN }, }; int ipc_chnl_cfg_get(struct ipc_chnl_cfg *chnl_cfg, int index) diff --git a/drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.h b/drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.h index 422471367f78..e77084e76718 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.h +++ b/drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.h @@ -23,6 +23,7 @@ enum ipc_channel_id { IPC_MEM_CTRL_CHL_ID_4, IPC_MEM_CTRL_CHL_ID_5, IPC_MEM_CTRL_CHL_ID_6, + IPC_MEM_CTRL_CHL_ID_7, }; /** diff --git a/drivers/net/wwan/iosm/iosm_ipc_coredump.c b/drivers/net/wwan/iosm/iosm_ipc_coredump.c new file mode 100644 index 000000000000..fba3c3454e80 --- /dev/null +++ b/drivers/net/wwan/iosm/iosm_ipc_coredump.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020-2021 Intel Corporation. + */ + +#include "iosm_ipc_coredump.h" + +/* Collect coredump data from modem */ +int ipc_coredump_collect(struct iosm_devlink *devlink, u8 **data, int entry, + u32 region_size) +{ + int ret, bytes_to_read, bytes_read = 0, i = 0; + s32 remaining; + u8 *data_ptr; + + data_ptr = vmalloc(region_size); + if (!data_ptr) + return -ENOMEM; + + remaining = devlink->cd_file_info[entry].actual_size; + ret = ipc_devlink_send_cmd(devlink, rpsi_cmd_coredump_get, entry); + if (ret) { + dev_err(devlink->dev, "Send coredump_get cmd failed"); + goto get_cd_fail; + } + while (remaining > 0) { + bytes_to_read = min(remaining, MAX_DATA_SIZE); + bytes_read = 0; + ret = ipc_imem_sys_devlink_read(devlink, data_ptr + i, + bytes_to_read, &bytes_read); + if (ret) { + dev_err(devlink->dev, "CD data read failed"); + goto get_cd_fail; + } + remaining -= bytes_read; + i += bytes_read; + } + + *data = data_ptr; + + return ret; +get_cd_fail: + vfree(data_ptr); + return ret; +} + +/* Get coredump list to be collected from modem */ +int ipc_coredump_get_list(struct iosm_devlink *devlink, u16 cmd) +{ + u32 byte_read, num_entries, file_size; + struct iosm_cd_table *cd_table; + u8 size[MAX_SIZE_LEN], i; + char *filename; + int ret = 0; + + cd_table = kzalloc(MAX_CD_LIST_SIZE, GFP_KERNEL); + if (!cd_table) { + ret = -ENOMEM; + goto cd_init_fail; + } + + ret = ipc_devlink_send_cmd(devlink, cmd, MAX_CD_LIST_SIZE); + if (ret) { + dev_err(devlink->dev, "rpsi_cmd_coredump_start failed"); + goto cd_init_fail; + } + + ret = ipc_imem_sys_devlink_read(devlink, (u8 *)cd_table, + MAX_CD_LIST_SIZE, &byte_read); + if (ret) { + dev_err(devlink->dev, "Coredump data is invalid"); + goto cd_init_fail; + } + + if (byte_read != MAX_CD_LIST_SIZE) + goto cd_init_fail; + + if (cmd == rpsi_cmd_coredump_start) { + num_entries = le32_to_cpu(cd_table->list.num_entries); + if (num_entries == 0 || num_entries > IOSM_NOF_CD_REGION) { + ret = -EINVAL; + goto cd_init_fail; + } + + for (i = 0; i < num_entries; i++) { + file_size = le32_to_cpu(cd_table->list.entry[i].size); + filename = cd_table->list.entry[i].filename; + + if (file_size > devlink->cd_file_info[i].default_size) { + ret = -EINVAL; + goto cd_init_fail; + } + + devlink->cd_file_info[i].actual_size = file_size; + dev_dbg(devlink->dev, "file: %s actual size %d", + filename, file_size); + devlink_flash_update_status_notify(devlink->devlink_ctx, + filename, + "FILENAME", 0, 0); + snprintf(size, sizeof(size), "%d", file_size); + devlink_flash_update_status_notify(devlink->devlink_ctx, + size, "FILE SIZE", + 0, 0); + } + } + +cd_init_fail: + kfree(cd_table); + return ret; +} diff --git a/drivers/net/wwan/iosm/iosm_ipc_coredump.h b/drivers/net/wwan/iosm/iosm_ipc_coredump.h new file mode 100644 index 000000000000..d5028153c8d1 --- /dev/null +++ b/drivers/net/wwan/iosm/iosm_ipc_coredump.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * Copyright (C) 2020-2021 Intel Corporation. + */ + +#ifndef _IOSM_IPC_COREDUMP_H_ +#define _IOSM_IPC_COREDUMP_H_ + +#include "iosm_ipc_devlink.h" + +/* Max number of bytes to receive for Coredump list structure */ +#define MAX_CD_LIST_SIZE 0x1000 + +/* Max buffer allocated to receive coredump data */ +#define MAX_DATA_SIZE 0x00010000 + +/* Max number of file entries */ +#define MAX_NOF_ENTRY 256 + +/* Max length */ +#define MAX_SIZE_LEN 32 + +/** + * struct iosm_cd_list_entry - Structure to hold coredump file info. + * @size: Number of bytes for the entry + * @filename: Coredump filename to be generated on host + */ +struct iosm_cd_list_entry { + __le32 size; + char filename[IOSM_MAX_FILENAME_LEN]; +} __packed; + +/** + * struct iosm_cd_list - Structure to hold list of coredump files + * to be collected. + * @num_entries: Number of entries to be received + * @entry: Contains File info + */ +struct iosm_cd_list { + __le32 num_entries; + struct iosm_cd_list_entry entry[MAX_NOF_ENTRY]; +} __packed; + +/** + * struct iosm_cd_table - Common Coredump table + * @version: Version of coredump structure + * @list: Coredump list structure + */ +struct iosm_cd_table { + __le32 version; + struct iosm_cd_list list; +} __packed; + +/** + * ipc_coredump_collect - To collect coredump + * @devlink: Pointer to devlink instance. + * @data: Pointer to snapshot + * @entry: ID of requested snapshot + * @region_size: Region size + * + * Returns: 0 on success, error on failure + */ +int ipc_coredump_collect(struct iosm_devlink *devlink, u8 **data, int entry, + u32 region_size); + +/** + * ipc_coredump_get_list - Get coredump list + * @devlink: Pointer to devlink instance. + * @cmd: RPSI command to be sent + * + * Returns: 0 on success, error on failure + */ +int ipc_coredump_get_list(struct iosm_devlink *devlink, u16 cmd); + +#endif /* _IOSM_IPC_COREDUMP_H_ */ diff --git a/drivers/net/wwan/iosm/iosm_ipc_devlink.c b/drivers/net/wwan/iosm/iosm_ipc_devlink.c new file mode 100644 index 000000000000..592792c277fe --- /dev/null +++ b/drivers/net/wwan/iosm/iosm_ipc_devlink.c @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020-2021 Intel Corporation. + */ + +#include "iosm_ipc_chnl_cfg.h" +#include "iosm_ipc_coredump.h" +#include "iosm_ipc_devlink.h" +#include "iosm_ipc_flash.h" + +/* Coredump list */ +static struct iosm_coredump_file_info list[IOSM_NOF_CD_REGION] = { + {"report.json", REPORT_JSON_SIZE,}, + {"coredump.fcd", COREDUMP_FCD_SIZE,}, + {"cdd.log", CDD_LOG_SIZE,}, + {"eeprom.bin", EEPROM_BIN_SIZE,}, + {"bootcore_trace.bin", BOOTCORE_TRC_BIN_SIZE,}, + {"bootcore_prev_trace.bin", BOOTCORE_PREV_TRC_BIN_SIZE,}, +}; + +/* Get the param values for the specific param ID's */ +static int ipc_devlink_get_param(struct devlink *dl, u32 id, + struct devlink_param_gset_ctx *ctx) +{ + struct iosm_devlink *ipc_devlink = devlink_priv(dl); + int rc = 0; + + switch (id) { + case IOSM_DEVLINK_PARAM_ID_ERASE_FULL_FLASH: + ctx->val.vu8 = ipc_devlink->param.erase_full_flash; + break; + + case IOSM_DEVLINK_PARAM_ID_DOWNLOAD_REGION: + ctx->val.vu8 = ipc_devlink->param.download_region; + break; + + case IOSM_DEVLINK_PARAM_ID_ADDRESS: + ctx->val.vu32 = ipc_devlink->param.address; + break; + + case IOSM_DEVLINK_PARAM_ID_REGION_COUNT: + ctx->val.vu8 = ipc_devlink->param.region_count; + break; + + default: + rc = -EOPNOTSUPP; + break; + } + + return rc; +} + +/* Set the param values for the specific param ID's */ +static int ipc_devlink_set_param(struct devlink *dl, u32 id, + struct devlink_param_gset_ctx *ctx) +{ + struct iosm_devlink *ipc_devlink = devlink_priv(dl); + int rc = 0; + + switch (id) { + case IOSM_DEVLINK_PARAM_ID_ERASE_FULL_FLASH: + ipc_devlink->param.erase_full_flash = ctx->val.vu8; + break; + + case IOSM_DEVLINK_PARAM_ID_DOWNLOAD_REGION: + ipc_devlink->param.download_region = ctx->val.vu8; + break; + + case IOSM_DEVLINK_PARAM_ID_ADDRESS: + ipc_devlink->param.address = ctx->val.vu32; + break; + + case IOSM_DEVLINK_PARAM_ID_REGION_COUNT: + ipc_devlink->param.region_count = ctx->val.vu8; + break; + + default: + rc = -EOPNOTSUPP; + break; + } + + return rc; +} + +/* Devlink param structure array */ +static const struct devlink_param iosm_devlink_params[] = { + DEVLINK_PARAM_DRIVER(IOSM_DEVLINK_PARAM_ID_ERASE_FULL_FLASH, + "erase_full_flash", DEVLINK_PARAM_TYPE_BOOL, + BIT(DEVLINK_PARAM_CMODE_RUNTIME), + ipc_devlink_get_param, ipc_devlink_set_param, + NULL), + DEVLINK_PARAM_DRIVER(IOSM_DEVLINK_PARAM_ID_DOWNLOAD_REGION, + "download_region", DEVLINK_PARAM_TYPE_BOOL, + BIT(DEVLINK_PARAM_CMODE_RUNTIME), + ipc_devlink_get_param, ipc_devlink_set_param, + NULL), + DEVLINK_PARAM_DRIVER(IOSM_DEVLINK_PARAM_ID_ADDRESS, + "address", DEVLINK_PARAM_TYPE_U32, + BIT(DEVLINK_PARAM_CMODE_RUNTIME), + ipc_devlink_get_param, ipc_devlink_set_param, + NULL), + DEVLINK_PARAM_DRIVER(IOSM_DEVLINK_PARAM_ID_REGION_COUNT, + "region_count", DEVLINK_PARAM_TYPE_U8, + BIT(DEVLINK_PARAM_CMODE_RUNTIME), + ipc_devlink_get_param, ipc_devlink_set_param, + NULL), +}; + +/* Get devlink flash component type */ +static enum iosm_flash_comp_type +ipc_devlink_get_flash_comp_type(const char comp_str[], u32 len) +{ + enum iosm_flash_comp_type fls_type; + + if (!strncmp("PSI", comp_str, len)) + fls_type = FLASH_COMP_TYPE_PSI; + else if (!strncmp("EBL", comp_str, len)) + fls_type = FLASH_COMP_TYPE_EBL; + else if (!strncmp("FLS", comp_str, len)) + fls_type = FLASH_COMP_TYPE_FLS; + else + fls_type = FLASH_COMP_TYPE_INVAL; + + return fls_type; +} + +/* Function triggered on devlink flash command + * Flash update function which calls multiple functions based on + * component type specified in the flash command + */ +static int ipc_devlink_flash_update(struct devlink *devlink, + struct devlink_flash_update_params *params, + struct netlink_ext_ack *extack) +{ + struct iosm_devlink *ipc_devlink = devlink_priv(devlink); + enum iosm_flash_comp_type fls_type; + u32 rc = -EINVAL; + u8 *mdm_rsp; + + mdm_rsp = kzalloc(IOSM_EBL_DW_PACK_SIZE, GFP_KERNEL); + if (!mdm_rsp) + return -ENOMEM; + + fls_type = ipc_devlink_get_flash_comp_type(params->component, + strlen(params->component)); + + switch (fls_type) { + case FLASH_COMP_TYPE_PSI: + rc = ipc_flash_boot_psi(ipc_devlink, params->fw); + break; + case FLASH_COMP_TYPE_EBL: + rc = ipc_flash_boot_ebl(ipc_devlink, params->fw); + if (!rc) + rc = ipc_flash_boot_set_capabilities(ipc_devlink, + mdm_rsp); + if (!rc) + rc = ipc_flash_read_swid(ipc_devlink, mdm_rsp); + break; + case FLASH_COMP_TYPE_FLS: + rc = ipc_flash_send_fls(ipc_devlink, params->fw, mdm_rsp); + break; + default: + devlink_flash_update_status_notify(devlink, "Invalid component", + params->component, 0, 0); + break; + } + + if (!rc) + devlink_flash_update_status_notify(devlink, "Flashing success", + params->component, 0, 0); + else + devlink_flash_update_status_notify(devlink, "Flashing failed", + params->component, 0, 0); + + kfree(mdm_rsp); + return rc; +} + +/* Call back function for devlink ops */ +static const struct devlink_ops devlink_flash_ops = { + .supported_flash_update_params = DEVLINK_SUPPORT_FLASH_UPDATE_COMPONENT, + .flash_update = ipc_devlink_flash_update, +}; + +/* Send command to modem to collect data */ +int ipc_devlink_send_cmd(struct iosm_devlink *ipc_devlink, u16 cmd, u32 entry) +{ + struct iosm_rpsi_cmd rpsi_cmd; + + rpsi_cmd.param.dword = cpu_to_le32(entry); + rpsi_cmd.cmd = cpu_to_le16(cmd); + rpsi_cmd.crc = rpsi_cmd.param.word[0] ^ rpsi_cmd.param.word[1] ^ + rpsi_cmd.cmd; + + return ipc_imem_sys_devlink_write(ipc_devlink, (u8 *)&rpsi_cmd, + sizeof(rpsi_cmd)); +} + +static int ipc_devlink_coredump_snapshot(struct devlink *dl, + const struct devlink_region_ops *ops, + struct netlink_ext_ack *extack, + u8 **data) +{ + struct iosm_devlink *ipc_devlink = devlink_priv(dl); + struct iosm_coredump_file_info *cd_list = ops->priv; + u32 region_size; + int rc; + + dev_dbg(ipc_devlink->dev, "Region:%s, ID:%d", ops->name, + cd_list->entry); + region_size = cd_list->default_size; + rc = ipc_coredump_collect(ipc_devlink, data, cd_list->entry, + region_size); + if (rc) { + dev_err(ipc_devlink->dev, "Fail to create snapshot,err %d", rc); + goto coredump_collect_err; + } + + /* Send coredump end cmd indicating end of coredump collection */ + if (cd_list->entry == (IOSM_NOF_CD_REGION - 1)) + ipc_coredump_get_list(ipc_devlink, rpsi_cmd_coredump_end); + + return rc; +coredump_collect_err: + ipc_coredump_get_list(ipc_devlink, rpsi_cmd_coredump_end); + return rc; +} + +/* To create regions for coredump files */ +static int ipc_devlink_create_region(struct iosm_devlink *devlink) +{ + struct devlink_region_ops *mdm_coredump; + int rc = 0; + u8 i; + + mdm_coredump = devlink->iosm_devlink_mdm_coredump; + for (i = 0; i < IOSM_NOF_CD_REGION; i++) { + mdm_coredump[i].name = list[i].filename; + mdm_coredump[i].snapshot = ipc_devlink_coredump_snapshot; + mdm_coredump[i].destructor = vfree; + devlink->cd_regions[i] = + devlink_region_create(devlink->devlink_ctx, + &mdm_coredump[i], MAX_SNAPSHOTS, + list[i].default_size); + + if (IS_ERR(devlink->cd_regions[i])) { + rc = PTR_ERR(devlink->cd_regions[i]); + dev_err(devlink->dev, "Devlink region fail,err %d", rc); + /* Delete previously created regions */ + for ( ; i > 0; i--) + devlink_region_destroy(devlink->cd_regions[i]); + goto region_create_fail; + } + list[i].entry = i; + mdm_coredump[i].priv = list + i; + } +region_create_fail: + return rc; +} + +/* To Destroy devlink regions */ +static void ipc_devlink_destroy_region(struct iosm_devlink *ipc_devlink) +{ + u8 i; + + for (i = 0; i < IOSM_NOF_CD_REGION; i++) + devlink_region_destroy(ipc_devlink->cd_regions[i]); +} + +/* Handle registration to devlink framework */ +struct iosm_devlink *ipc_devlink_init(struct iosm_imem *ipc_imem) +{ + struct ipc_chnl_cfg chnl_cfg_flash = { 0 }; + struct iosm_devlink *ipc_devlink; + struct devlink *devlink_ctx; + int rc; + + devlink_ctx = devlink_alloc(&devlink_flash_ops, + sizeof(struct iosm_devlink), + ipc_imem->dev); + if (!devlink_ctx) { + dev_err(ipc_imem->dev, "devlink_alloc failed"); + goto devlink_alloc_fail; + } + + ipc_devlink = devlink_priv(devlink_ctx); + ipc_devlink->devlink_ctx = devlink_ctx; + ipc_devlink->pcie = ipc_imem->pcie; + ipc_devlink->dev = ipc_imem->dev; + rc = devlink_register(devlink_ctx); + if (rc) { + dev_err(ipc_devlink->dev, "devlink_register failed rc %d", rc); + goto free_dl; + } + + rc = devlink_params_register(devlink_ctx, iosm_devlink_params, + ARRAY_SIZE(iosm_devlink_params)); + if (rc) { + dev_err(ipc_devlink->dev, + "devlink_params_register failed. rc %d", rc); + goto param_reg_fail; + } + + devlink_params_publish(devlink_ctx); + ipc_devlink->cd_file_info = list; + + rc = ipc_devlink_create_region(ipc_devlink); + if (rc) { + dev_err(ipc_devlink->dev, "Devlink Region create failed, rc %d", + rc); + goto region_create_fail; + } + + if (ipc_chnl_cfg_get(&chnl_cfg_flash, IPC_MEM_CTRL_CHL_ID_7) < 0) + goto chnl_get_fail; + + ipc_imem_channel_init(ipc_imem, IPC_CTYPE_CTRL, + chnl_cfg_flash, IRQ_MOD_OFF); + + init_completion(&ipc_devlink->devlink_sio.read_sem); + skb_queue_head_init(&ipc_devlink->devlink_sio.rx_list); + + dev_dbg(ipc_devlink->dev, "iosm devlink register success"); + + return ipc_devlink; + +chnl_get_fail: + ipc_devlink_destroy_region(ipc_devlink); +region_create_fail: + devlink_params_unpublish(devlink_ctx); + devlink_params_unregister(devlink_ctx, iosm_devlink_params, + ARRAY_SIZE(iosm_devlink_params)); +param_reg_fail: + devlink_unregister(devlink_ctx); +free_dl: + devlink_free(devlink_ctx); +devlink_alloc_fail: + return NULL; +} + +/* Handle unregistration of devlink */ +void ipc_devlink_deinit(struct iosm_devlink *ipc_devlink) +{ + struct devlink *devlink_ctx = ipc_devlink->devlink_ctx; + + ipc_devlink_destroy_region(ipc_devlink); + devlink_params_unpublish(devlink_ctx); + devlink_params_unregister(devlink_ctx, iosm_devlink_params, + ARRAY_SIZE(iosm_devlink_params)); + if (ipc_devlink->devlink_sio.devlink_read_pend) { + complete(&ipc_devlink->devlink_sio.read_sem); + complete(&ipc_devlink->devlink_sio.channel->ul_sem); + } + if (!ipc_devlink->devlink_sio.devlink_read_pend) + skb_queue_purge(&ipc_devlink->devlink_sio.rx_list); + + ipc_imem_sys_devlink_close(ipc_devlink); + devlink_unregister(devlink_ctx); + devlink_free(devlink_ctx); +} diff --git a/drivers/net/wwan/iosm/iosm_ipc_devlink.h b/drivers/net/wwan/iosm/iosm_ipc_devlink.h new file mode 100644 index 000000000000..392735080cb3 --- /dev/null +++ b/drivers/net/wwan/iosm/iosm_ipc_devlink.h @@ -0,0 +1,207 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * Copyright (C) 2020-2021 Intel Corporation. + */ + +#ifndef _IOSM_IPC_DEVLINK_H_ +#define _IOSM_IPC_DEVLINK_H_ + +#include + +#include "iosm_ipc_imem.h" +#include "iosm_ipc_imem_ops.h" +#include "iosm_ipc_pcie.h" + +/* MAX file name length */ +#define IOSM_MAX_FILENAME_LEN 32 +/* EBL response size */ +#define IOSM_EBL_RSP_SIZE 76 +/* MAX number of regions supported */ +#define IOSM_NOF_CD_REGION 6 +/* MAX number of SNAPSHOTS supported */ +#define MAX_SNAPSHOTS 1 +/* Default Coredump file size */ +#define REPORT_JSON_SIZE 0x800 +#define COREDUMP_FCD_SIZE 0x10E00000 +#define CDD_LOG_SIZE 0x30000 +#define EEPROM_BIN_SIZE 0x10000 +#define BOOTCORE_TRC_BIN_SIZE 0x8000 +#define BOOTCORE_PREV_TRC_BIN_SIZE 0x20000 + +/** + * enum iosm_devlink_param_id - Enum type to different devlink params + * @IOSM_DEVLINK_PARAM_ID_BASE: Devlink param base ID + * @IOSM_DEVLINK_PARAM_ID_ERASE_FULL_FLASH: Set if full erase required + * @IOSM_DEVLINK_PARAM_ID_DOWNLOAD_REGION: Set if fls file to be + * flashed is Loadmap/region file + * @IOSM_DEVLINK_PARAM_ID_ADDRESS: Address of the region to be + * flashed + * @IOSM_DEVLINK_PARAM_ID_REGION_COUNT: Max region count + */ + +enum iosm_devlink_param_id { + IOSM_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, + IOSM_DEVLINK_PARAM_ID_ERASE_FULL_FLASH, + IOSM_DEVLINK_PARAM_ID_DOWNLOAD_REGION, + IOSM_DEVLINK_PARAM_ID_ADDRESS, + IOSM_DEVLINK_PARAM_ID_REGION_COUNT, +}; + +/** + * enum iosm_rpsi_cmd_code - Enum type for RPSI command list + * @rpsi_cmd_code_ebl: Command to load ebl + * @rpsi_cmd_coredump_start: Command to get list of files and + * file size info from PSI + * @rpsi_cmd_coredump_get: Command to get the coredump data + * @rpsi_cmd_coredump_end: Command to stop receiving the coredump + */ +enum iosm_rpsi_cmd_code { + rpsi_cmd_code_ebl = 0x02, + rpsi_cmd_coredump_start = 0x10, + rpsi_cmd_coredump_get = 0x11, + rpsi_cmd_coredump_end = 0x12, +}; + +/** + * enum iosm_flash_comp_type - Enum for different flash component types + * @FLASH_COMP_TYPE_PSI: PSI flash comp type + * @FLASH_COMP_TYPE_EBL: EBL flash comp type + * @FLASH_COMP_TYPE_FLS: FLS flash comp type + * @FLASH_COMP_TYPE_INVAL: Invalid flash comp type + */ +enum iosm_flash_comp_type { + FLASH_COMP_TYPE_PSI, + FLASH_COMP_TYPE_EBL, + FLASH_COMP_TYPE_FLS, + FLASH_COMP_TYPE_INVAL, +}; + +/** + * struct iosm_devlink_sio - SIO instance + * @rx_list: Downlink skbuf list received from CP + * @read_sem: Needed for the blocking read or downlink transfer + * @channel_id: Reserved channel id for flashing/CD collection to RAM + * @channel: Channel instance for flashing and coredump + * @devlink_read_pend: Check if read is pending + */ +struct iosm_devlink_sio { + struct sk_buff_head rx_list; + struct completion read_sem; + int channel_id; + struct ipc_mem_channel *channel; + u32 devlink_read_pend; +}; + +/** + * struct iosm_flash_params - List of flash params required for flashing + * @address: Address of the region file to be flashed + * @region_count: Maximum no of regions for each fls file + * @download_region: To be set if region is being flashed + * @erase_full_flash: To set the flashing mode + * erase_full_flash = 1; full erase + * erase_full_flash = 0; no erase + * @erase_full_flash_done: Flag to check if it is a full erase + */ +struct iosm_flash_params { + u32 address; + u8 region_count; + u8 download_region; + u8 erase_full_flash; + u8 erase_full_flash_done; +}; + +/** + * struct iosm_ebl_ctx_data - EBL ctx data used during flashing + * @ebl_sw_info_version: SWID version info obtained from EBL + * @m_ebl_resp: Buffer used to read and write the ebl data + */ +struct iosm_ebl_ctx_data { + u8 ebl_sw_info_version; + u8 m_ebl_resp[IOSM_EBL_RSP_SIZE]; +}; + +/** + * struct iosm_coredump_file_info - Coredump file info + * @filename: Name of coredump file + * @default_size: Default size of coredump file + * @actual_size: Actual size of coredump file + * @entry: Index of the coredump file + */ +struct iosm_coredump_file_info { + char filename[IOSM_MAX_FILENAME_LEN]; + u32 default_size; + u32 actual_size; + u32 entry; +}; + +/** + * struct iosm_devlink - IOSM Devlink structure + * @devlink_sio: SIO instance for read/write functionality + * @pcie: Pointer to PCIe component + * @dev: Pointer to device struct + * @devlink_ctx: Pointer to devlink context + * @param: Params required for flashing + * @ebl_ctx: Data to be read and written to Modem + * @cd_file_info: coredump file info + * @iosm_devlink_mdm_coredump: region ops for coredump collection + * @cd_regions: coredump regions + */ +struct iosm_devlink { + struct iosm_devlink_sio devlink_sio; + struct iosm_pcie *pcie; + struct device *dev; + struct devlink *devlink_ctx; + struct iosm_flash_params param; + struct iosm_ebl_ctx_data ebl_ctx; + struct iosm_coredump_file_info *cd_file_info; + struct devlink_region_ops iosm_devlink_mdm_coredump[IOSM_NOF_CD_REGION]; + struct devlink_region *cd_regions[IOSM_NOF_CD_REGION]; +}; + +/** + * union iosm_rpsi_param_u - RPSI cmd param for CRC calculation + * @word: Words member used in CRC calculation + * @dword: Actual data + */ +union iosm_rpsi_param_u { + __le16 word[2]; + __le32 dword; +}; + +/** + * struct iosm_rpsi_cmd - Structure for RPSI Command + * @param: Used to calculate CRC + * @cmd: Stores the RPSI command + * @crc: Stores the CRC value + */ +struct iosm_rpsi_cmd { + union iosm_rpsi_param_u param; + __le16 cmd; + __le16 crc; +}; + +/** + * ipc_devlink_init - To initialize the devlink to IOSM driver + * @ipc_imem: Pointer to struct iosm_imem + * + * Returns: Pointer to iosm_devlink on success and NULL on failure + */ +struct iosm_devlink *ipc_devlink_init(struct iosm_imem *ipc_imem); + +/** + * ipc_devlink_deinit - To unintialize the devlink from IOSM driver. + * @ipc_devlink: Devlink instance + */ +void ipc_devlink_deinit(struct iosm_devlink *ipc_devlink); + +/** + * ipc_devlink_send_cmd - Send command to Modem + * @ipc_devlink: Pointer to struct iosm_devlink + * @cmd: Command to be sent to modem + * @entry: Command entry number + * + * Returns: 0 on success and failure value on error + */ +int ipc_devlink_send_cmd(struct iosm_devlink *ipc_devlink, u16 cmd, u32 entry); + +#endif /* _IOSM_IPC_DEVLINK_H */ diff --git a/drivers/net/wwan/iosm/iosm_ipc_flash.c b/drivers/net/wwan/iosm/iosm_ipc_flash.c new file mode 100644 index 000000000000..a43aafc70168 --- /dev/null +++ b/drivers/net/wwan/iosm/iosm_ipc_flash.c @@ -0,0 +1,562 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020-2021 Intel Corporation. + */ + +#include "iosm_ipc_coredump.h" +#include "iosm_ipc_devlink.h" +#include "iosm_ipc_flash.h" + +/* This function will pack the data to be sent to the modem using the + * payload, payload length and pack id + */ +static int ipc_flash_proc_format_ebl_pack(struct iosm_flash_data *flash_req, + u32 pack_length, u16 pack_id, + u8 *payload, u32 payload_length) +{ + u16 checksum = pack_id; + u32 i; + + if (payload_length + IOSM_EBL_HEAD_SIZE > pack_length) + return -EINVAL; + + flash_req->pack_id = cpu_to_le16(pack_id); + flash_req->msg_length = cpu_to_le32(payload_length); + checksum += (payload_length >> IOSM_EBL_PAYL_SHIFT) + + (payload_length & IOSM_EBL_CKSM); + + for (i = 0; i < payload_length; i++) + checksum += payload[i]; + + flash_req->checksum = cpu_to_le16(checksum); + + return 0; +} + +/* validate the response received from modem and + * check the type of errors received + */ +static int ipc_flash_proc_check_ebl_rsp(void *hdr_rsp, void *payload_rsp) +{ + struct iosm_ebl_error *err_info = payload_rsp; + u16 *rsp_code = hdr_rsp; + int res = 0; + u32 i; + + if (*rsp_code == IOSM_EBL_RSP_BUFF) { + for (i = 0; i < IOSM_MAX_ERRORS; i++) { + if (!err_info->error[i].error_code) { + pr_err("EBL: error_class = %d, error_code = %d", + err_info->error[i].error_class, + err_info->error[i].error_code); + } + } + res = -EINVAL; + } + + return res; +} + +/* Send data to the modem */ +static int ipc_flash_send_data(struct iosm_devlink *ipc_devlink, u32 size, + u16 pack_id, u8 *payload, u32 payload_length) +{ + struct iosm_flash_data flash_req; + int ret; + + ret = ipc_flash_proc_format_ebl_pack(&flash_req, size, + pack_id, payload, payload_length); + if (ret) { + dev_err(ipc_devlink->dev, "EBL2 pack failed for pack_id:%d", + pack_id); + goto ipc_free_payload; + } + + ret = ipc_imem_sys_devlink_write(ipc_devlink, (u8 *)&flash_req, + IOSM_EBL_HEAD_SIZE); + if (ret) { + dev_err(ipc_devlink->dev, "EBL Header write failed for Id:%x", + pack_id); + goto ipc_free_payload; + } + + ret = ipc_imem_sys_devlink_write(ipc_devlink, payload, payload_length); + if (ret) { + dev_err(ipc_devlink->dev, "EBL Payload write failed for Id:%x", + pack_id); + } + +ipc_free_payload: + return ret; +} + +/* Allocate flash channel and read LER data from modem */ +int ipc_flash_link_establish(struct iosm_imem *ipc_imem) +{ + u8 ler_data[IOSM_LER_RSP_SIZE]; + u32 bytes_read; + + /* Allocate channel for flashing/cd collection */ + ipc_imem->ipc_devlink->devlink_sio.channel = + ipc_imem_sys_devlink_open(ipc_imem); + + if (!ipc_imem->ipc_devlink->devlink_sio.channel) + goto chl_open_fail; + + if (ipc_imem_sys_devlink_read(ipc_imem->ipc_devlink, ler_data, + IOSM_LER_RSP_SIZE, &bytes_read)) + goto devlink_read_fail; + + if (bytes_read != IOSM_LER_RSP_SIZE) + goto devlink_read_fail; + return 0; + +devlink_read_fail: + ipc_imem_sys_devlink_close(ipc_imem->ipc_devlink); +chl_open_fail: + return -EIO; +} + +/* Receive data from the modem */ +static int ipc_flash_receive_data(struct iosm_devlink *ipc_devlink, u32 size, + u8 *mdm_rsp) +{ + u8 mdm_rsp_hdr[IOSM_EBL_HEAD_SIZE]; + u32 bytes_read; + int ret; + + ret = ipc_imem_sys_devlink_read(ipc_devlink, mdm_rsp_hdr, + IOSM_EBL_HEAD_SIZE, &bytes_read); + if (ret) { + dev_err(ipc_devlink->dev, "EBL rsp to read %d bytes failed", + IOSM_EBL_HEAD_SIZE); + goto ipc_flash_recv_err; + } + + if (bytes_read != IOSM_EBL_HEAD_SIZE) { + ret = -EINVAL; + goto ipc_flash_recv_err; + } + + ret = ipc_imem_sys_devlink_read(ipc_devlink, mdm_rsp, size, + &bytes_read); + if (ret) { + dev_err(ipc_devlink->dev, "EBL rsp to read %d bytes failed", + size); + goto ipc_flash_recv_err; + } + + if (bytes_read != size) { + ret = -EINVAL; + goto ipc_flash_recv_err; + } + + ret = ipc_flash_proc_check_ebl_rsp(mdm_rsp_hdr + 2, mdm_rsp); + +ipc_flash_recv_err: + return ret; +} + +/* Function to send command to modem and receive response */ +static int ipc_flash_send_receive(struct iosm_devlink *ipc_devlink, u16 pack_id, + u8 *payload, u32 payload_length, u8 *mdm_rsp) +{ + size_t frame_len = IOSM_EBL_DW_PACK_SIZE; + int ret; + + if (pack_id == FLASH_SET_PROT_CONF) + frame_len = IOSM_EBL_W_PACK_SIZE; + + ret = ipc_flash_send_data(ipc_devlink, frame_len, pack_id, payload, + payload_length); + if (ret) + goto ipc_flash_send_rcv; + + ret = ipc_flash_receive_data(ipc_devlink, + frame_len - IOSM_EBL_HEAD_SIZE, mdm_rsp); + +ipc_flash_send_rcv: + return ret; +} + +/* Set the capabilities for the EBL */ +int ipc_flash_boot_set_capabilities(struct iosm_devlink *ipc_devlink, + u8 *mdm_rsp) +{ + int ret; + + ipc_devlink->ebl_ctx.ebl_sw_info_version = + ipc_devlink->ebl_ctx.m_ebl_resp[EBL_RSP_SW_INFO_VER]; + ipc_devlink->ebl_ctx.m_ebl_resp[EBL_SKIP_ERASE] = IOSM_CAP_NOT_ENHANCED; + ipc_devlink->ebl_ctx.m_ebl_resp[EBL_SKIP_CRC] = IOSM_CAP_NOT_ENHANCED; + + if (ipc_devlink->ebl_ctx.m_ebl_resp[EBL_CAPS_FLAG] & + IOSM_CAP_USE_EXT_CAP) { + if (ipc_devlink->param.erase_full_flash) + ipc_devlink->ebl_ctx.m_ebl_resp[EBL_OOS_CONFIG] &= + ~((u8)IOSM_EXT_CAP_ERASE_ALL); + else + ipc_devlink->ebl_ctx.m_ebl_resp[EBL_OOS_CONFIG] &= + ~((u8)IOSM_EXT_CAP_COMMIT_ALL); + ipc_devlink->ebl_ctx.m_ebl_resp[EBL_EXT_CAPS_HANDLED] = + IOSM_CAP_USE_EXT_CAP; + } + + /* Write back the EBL capability to modem + * Request Set Protcnf command + */ + ret = ipc_flash_send_receive(ipc_devlink, FLASH_SET_PROT_CONF, + ipc_devlink->ebl_ctx.m_ebl_resp, + IOSM_EBL_RSP_SIZE, mdm_rsp); + return ret; +} + +/* Read the SWID type and SWID value from the EBL */ +int ipc_flash_read_swid(struct iosm_devlink *ipc_devlink, u8 *mdm_rsp) +{ + struct iosm_flash_msg_control cmd_msg; + struct iosm_swid_table *swid; + char ebl_swid[IOSM_SWID_STR]; + int ret; + + if (ipc_devlink->ebl_ctx.ebl_sw_info_version != + IOSM_EXT_CAP_SWID_OOS_PACK) + return -EINVAL; + + cmd_msg.action = cpu_to_le32(FLASH_OOSC_ACTION_READ); + cmd_msg.type = cpu_to_le32(FLASH_OOSC_TYPE_SWID_TABLE); + cmd_msg.length = cpu_to_le32(IOSM_MSG_LEN_ARG); + cmd_msg.arguments = cpu_to_le32(IOSM_MSG_LEN_ARG); + + ret = ipc_flash_send_receive(ipc_devlink, FLASH_OOS_CONTROL, + (u8 *)&cmd_msg, IOSM_MDM_SEND_16, mdm_rsp); + if (ret) + goto ipc_swid_err; + + cmd_msg.action = cpu_to_le32(*((u32 *)mdm_rsp)); + + ret = ipc_flash_send_receive(ipc_devlink, FLASH_OOS_DATA_READ, + (u8 *)&cmd_msg, IOSM_MDM_SEND_4, mdm_rsp); + if (ret) + goto ipc_swid_err; + + swid = (struct iosm_swid_table *)mdm_rsp; + dev_dbg(ipc_devlink->dev, "SWID %x RF_ENGINE_ID %x", swid->sw_id_val, + swid->rf_engine_id_val); + + snprintf(ebl_swid, sizeof(ebl_swid), "SWID: %x, RF_ENGINE_ID: %x", + swid->sw_id_val, swid->rf_engine_id_val); + + devlink_flash_update_status_notify(ipc_devlink->devlink_ctx, ebl_swid, + NULL, 0, 0); +ipc_swid_err: + return ret; +} + +/* Function to check if full erase or conditional erase was successful */ +static int ipc_flash_erase_check(struct iosm_devlink *ipc_devlink, u8 *mdm_rsp) +{ + int ret, count = 0; + u16 mdm_rsp_data; + + /* Request Flash Erase Check */ + do { + mdm_rsp_data = IOSM_MDM_SEND_DATA; + ret = ipc_flash_send_receive(ipc_devlink, FLASH_ERASE_CHECK, + (u8 *)&mdm_rsp_data, + IOSM_MDM_SEND_2, mdm_rsp); + if (ret) + goto ipc_erase_chk_err; + + mdm_rsp_data = *((u16 *)mdm_rsp); + if (mdm_rsp_data > IOSM_MDM_ERASE_RSP) { + dev_err(ipc_devlink->dev, + "Flash Erase Check resp wrong 0x%04X", + mdm_rsp_data); + ret = -EINVAL; + goto ipc_erase_chk_err; + } + count++; + msleep(IOSM_FLASH_ERASE_CHECK_INTERVAL); + } while ((mdm_rsp_data != IOSM_MDM_ERASE_RSP) && + (count < (IOSM_FLASH_ERASE_CHECK_TIMEOUT / + IOSM_FLASH_ERASE_CHECK_INTERVAL))); + + if (mdm_rsp_data != IOSM_MDM_ERASE_RSP) { + dev_err(ipc_devlink->dev, "Modem erase check timeout failure!"); + ret = -ETIMEDOUT; + } + +ipc_erase_chk_err: + return ret; +} + +/* Full erase function which will erase the nand flash through EBL command */ +static int ipc_flash_full_erase(struct iosm_devlink *ipc_devlink, u8 *mdm_rsp) +{ + u32 erase_address = IOSM_ERASE_START_ADDR; + struct iosm_flash_msg_control cmd_msg; + u32 erase_length = IOSM_ERASE_LEN; + int ret; + + dev_dbg(ipc_devlink->dev, "Erase full nand flash"); + cmd_msg.action = cpu_to_le32(FLASH_OOSC_ACTION_ERASE); + cmd_msg.type = cpu_to_le32(FLASH_OOSC_TYPE_ALL_FLASH); + cmd_msg.length = cpu_to_le32(erase_length); + cmd_msg.arguments = cpu_to_le32(erase_address); + + ret = ipc_flash_send_receive(ipc_devlink, FLASH_OOS_CONTROL, + (unsigned char *)&cmd_msg, + IOSM_MDM_SEND_16, mdm_rsp); + if (ret) + goto ipc_flash_erase_err; + + ipc_devlink->param.erase_full_flash_done = IOSM_SET_FLAG; + ret = ipc_flash_erase_check(ipc_devlink, mdm_rsp); + +ipc_flash_erase_err: + return ret; +} + +/* Logic for flashing all the Loadmaps available for individual fls file */ +static int ipc_flash_download_region(struct iosm_devlink *ipc_devlink, + const struct firmware *fw, u8 *mdm_rsp) +{ + __le32 reg_info[2]; /* 0th position region address, 1st position size */ + char *file_ptr; + u32 rest_len; + u32 raw_len; + int ret; + + file_ptr = (char *)fw->data; + reg_info[0] = cpu_to_le32(ipc_devlink->param.address); + + if (!ipc_devlink->param.erase_full_flash_done) { + reg_info[1] = cpu_to_le32(ipc_devlink->param.address + + fw->size - 2); + ret = ipc_flash_send_receive(ipc_devlink, FLASH_ERASE_START, + (u8 *)reg_info, IOSM_MDM_SEND_8, + mdm_rsp); + if (ret) + goto dl_region_fail; + + ret = ipc_flash_erase_check(ipc_devlink, mdm_rsp); + if (ret) + goto dl_region_fail; + } + + /* Request Flash Set Address */ + ret = ipc_flash_send_receive(ipc_devlink, FLASH_SET_ADDRESS, + (u8 *)reg_info, IOSM_MDM_SEND_4, mdm_rsp); + if (ret) + goto dl_region_fail; + + rest_len = fw->size; + + /* Request Flash Write Raw Image */ + ret = ipc_flash_send_data(ipc_devlink, IOSM_EBL_DW_PACK_SIZE, + FLASH_WRITE_IMAGE_RAW, (u8 *)&rest_len, + IOSM_MDM_SEND_4); + if (ret) + goto dl_region_fail; + + do { + raw_len = (rest_len > IOSM_FLS_BUF_SIZE) ? IOSM_FLS_BUF_SIZE : + rest_len; + ret = ipc_imem_sys_devlink_write(ipc_devlink, file_ptr, + raw_len); + if (ret) { + dev_err(ipc_devlink->dev, "Image write failed"); + goto dl_region_fail; + } + file_ptr += raw_len; + rest_len -= raw_len; + } while (rest_len); + + ret = ipc_flash_receive_data(ipc_devlink, IOSM_EBL_DW_PAYL_SIZE, + mdm_rsp); + +dl_region_fail: + return ret; +} + +/* Flash the individual fls files */ +int ipc_flash_send_fls(struct iosm_devlink *ipc_devlink, + const struct firmware *fw, u8 *mdm_rsp) +{ + u16 flash_cmd; + int ret; + + if (ipc_devlink->param.erase_full_flash) { + ipc_devlink->param.erase_full_flash = false; + ret = ipc_flash_full_erase(ipc_devlink, mdm_rsp); + if (ret) + goto ipc_flash_err; + } + + /* Request Sec Start */ + if (!ipc_devlink->param.download_region) { + ret = ipc_flash_send_receive(ipc_devlink, FLASH_SEC_START, + (u8 *)fw->data, fw->size, mdm_rsp); + if (ret) + goto ipc_flash_err; + } else { + /* Download regions */ + ipc_devlink->param.region_count -= IOSM_SET_FLAG; + ret = ipc_flash_download_region(ipc_devlink, fw, mdm_rsp); + if (ret) + goto ipc_flash_err; + + if (!ipc_devlink->param.region_count) { + /* Request Sec End */ + flash_cmd = IOSM_MDM_SEND_DATA; + ret = ipc_flash_send_receive(ipc_devlink, FLASH_SEC_END, + (u8 *)&flash_cmd, + IOSM_MDM_SEND_2, mdm_rsp); + } + } + +ipc_flash_err: + return ret; +} + +/* Inject RPSI */ +int ipc_flash_boot_psi(struct iosm_devlink *ipc_devlink, + const struct firmware *fw) +{ + u8 psi_ack_byte[IOSM_PSI_ACK], read_data[2]; + u32 bytes_read; + u8 *psi_code; + int ret; + + dev_dbg(ipc_devlink->dev, "Boot transfer PSI"); + psi_code = kzalloc(fw->size, GFP_KERNEL); + if (!psi_code) + return -ENOMEM; + + memcpy(psi_code, fw->data, fw->size); + ret = ipc_imem_sys_devlink_write(ipc_devlink, psi_code, fw->size); + if (ret) { + dev_err(ipc_devlink->dev, "RPSI Image write failed"); + goto ipc_flash_psi_free; + } + + ret = ipc_imem_sys_devlink_read(ipc_devlink, read_data, + IOSM_LER_ACK_SIZE, &bytes_read); + if (ret) { + dev_err(ipc_devlink->dev, "ipc_devlink_sio_read ACK failed"); + goto ipc_flash_psi_free; + } + + if (bytes_read != IOSM_LER_ACK_SIZE) { + ret = -EINVAL; + goto ipc_flash_psi_free; + } + + snprintf(psi_ack_byte, sizeof(psi_ack_byte), "%x%x", read_data[0], + read_data[1]); + devlink_flash_update_status_notify(ipc_devlink->devlink_ctx, + psi_ack_byte, "PSI ACK", 0, 0); + + if (read_data[0] == 0x00 && read_data[1] == 0xCD) { + dev_dbg(ipc_devlink->dev, "Coredump detected"); + ret = ipc_coredump_get_list(ipc_devlink, + rpsi_cmd_coredump_start); + if (ret) + dev_err(ipc_devlink->dev, "Failed to get cd list"); + } + +ipc_flash_psi_free: + kfree(psi_code); + return ret; +} + +/* Inject EBL */ +int ipc_flash_boot_ebl(struct iosm_devlink *ipc_devlink, + const struct firmware *fw) +{ + u32 ebl_size = fw->size; + u8 read_data[2]; + u32 bytes_read; + int ret; + + if (ipc_mmio_get_exec_stage(ipc_devlink->pcie->imem->mmio) != + IPC_MEM_EXEC_STAGE_PSI) { + devlink_flash_update_status_notify(ipc_devlink->devlink_ctx, + "Invalid execution stage", + NULL, 0, 0); + return -EINVAL; + } + + dev_dbg(ipc_devlink->dev, "Boot transfer EBL"); + ret = ipc_devlink_send_cmd(ipc_devlink, rpsi_cmd_code_ebl, + IOSM_RPSI_LOAD_SIZE); + if (ret) { + dev_err(ipc_devlink->dev, "Sending rpsi_cmd_code_ebl failed"); + goto ipc_flash_ebl_err; + } + + ret = ipc_imem_sys_devlink_read(ipc_devlink, read_data, IOSM_READ_SIZE, + &bytes_read); + if (ret) { + dev_err(ipc_devlink->dev, "rpsi_cmd_code_ebl read failed"); + goto ipc_flash_ebl_err; + } + + if (bytes_read != IOSM_READ_SIZE) { + ret = -EINVAL; + goto ipc_flash_ebl_err; + } + + ret = ipc_imem_sys_devlink_write(ipc_devlink, (u8 *)&ebl_size, + sizeof(ebl_size)); + if (ret) { + dev_err(ipc_devlink->dev, "EBL length write failed"); + goto ipc_flash_ebl_err; + } + + ret = ipc_imem_sys_devlink_read(ipc_devlink, read_data, IOSM_READ_SIZE, + &bytes_read); + if (ret) { + dev_err(ipc_devlink->dev, "EBL read failed"); + goto ipc_flash_ebl_err; + } + + if (bytes_read != IOSM_READ_SIZE) { + ret = -EINVAL; + goto ipc_flash_ebl_err; + } + + ret = ipc_imem_sys_devlink_write(ipc_devlink, (unsigned char *)fw->data, + fw->size); + if (ret) { + dev_err(ipc_devlink->dev, "EBL data transfer failed"); + goto ipc_flash_ebl_err; + } + + ret = ipc_imem_sys_devlink_read(ipc_devlink, read_data, IOSM_READ_SIZE, + &bytes_read); + if (ret) { + dev_err(ipc_devlink->dev, "EBL read failed"); + goto ipc_flash_ebl_err; + } + + if (bytes_read != IOSM_READ_SIZE) { + ret = -EINVAL; + goto ipc_flash_ebl_err; + } + + ret = ipc_imem_sys_devlink_read(ipc_devlink, + ipc_devlink->ebl_ctx.m_ebl_resp, + IOSM_EBL_RSP_SIZE, &bytes_read); + if (ret) { + dev_err(ipc_devlink->dev, "EBL response read failed"); + goto ipc_flash_ebl_err; + } + + if (bytes_read != IOSM_EBL_RSP_SIZE) + ret = -EINVAL; + +ipc_flash_ebl_err: + return ret; +} diff --git a/drivers/net/wwan/iosm/iosm_ipc_flash.h b/drivers/net/wwan/iosm/iosm_ipc_flash.h new file mode 100644 index 000000000000..aee848927228 --- /dev/null +++ b/drivers/net/wwan/iosm/iosm_ipc_flash.h @@ -0,0 +1,271 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * Copyright (C) 2020-2021 Intel Corporation. + */ + +#ifndef _IOSM_IPC_FLASH_H +#define _IOSM_IPC_FLASH_H + +/* Buffer size used to read the fls image */ +#define IOSM_FLS_BUF_SIZE 0x00100000 +/* Full erase start address */ +#define IOSM_ERASE_START_ADDR 0x00000000 +/* Erase length for NAND flash */ +#define IOSM_ERASE_LEN 0xFFFFFFFF +/* EBL response Header size */ +#define IOSM_EBL_HEAD_SIZE 8 +/* EBL payload size */ +#define IOSM_EBL_W_PAYL_SIZE 2048 +/* Total EBL pack size */ +#define IOSM_EBL_W_PACK_SIZE (IOSM_EBL_HEAD_SIZE + IOSM_EBL_W_PAYL_SIZE) +/* EBL payload size */ +#define IOSM_EBL_DW_PAYL_SIZE 16384 +/* Total EBL pack size */ +#define IOSM_EBL_DW_PACK_SIZE (IOSM_EBL_HEAD_SIZE + IOSM_EBL_DW_PAYL_SIZE) +/* EBL name size */ +#define IOSM_EBL_NAME 32 +/* Maximum supported error types */ +#define IOSM_MAX_ERRORS 8 +/* Read size for RPSI/EBL response */ +#define IOSM_READ_SIZE 2 +/* Link establishment response ack size */ +#define IOSM_LER_ACK_SIZE 2 +/* PSI ACK len */ +#define IOSM_PSI_ACK 8 +/* SWID capability for packed swid type */ +#define IOSM_EXT_CAP_SWID_OOS_PACK 0x02 +/* EBL error response buffer */ +#define IOSM_EBL_RSP_BUFF 0x0041 +/* SWID string length */ +#define IOSM_SWID_STR 64 +/* Load EBL command size */ +#define IOSM_RPSI_LOAD_SIZE 0 +/* EBL payload checksum */ +#define IOSM_EBL_CKSM 0x0000FFFF +/* SWID msg len and argument */ +#define IOSM_MSG_LEN_ARG 0 +/* Data to be sent to modem */ +#define IOSM_MDM_SEND_DATA 0x0000 +/* Data received from modem as part of erase check */ +#define IOSM_MDM_ERASE_RSP 0x0001 +/* Bit shift to calculate Checksum */ +#define IOSM_EBL_PAYL_SHIFT 16 +/* Flag To be set */ +#define IOSM_SET_FLAG 1 +/* Set flash erase check timeout to 100 msec */ +#define IOSM_FLASH_ERASE_CHECK_TIMEOUT 100 +/* Set flash erase check interval to 20 msec */ +#define IOSM_FLASH_ERASE_CHECK_INTERVAL 20 +/* Link establishment response ack size */ +#define IOSM_LER_RSP_SIZE 60 + +/** + * enum iosm_flash_package_type - Enum for the flashing operations + * @FLASH_SET_PROT_CONF: Write EBL capabilities + * @FLASH_SEC_START: Start writing the secpack + * @FLASH_SEC_END: Validate secpack end + * @FLASH_SET_ADDRESS: Set the address for flashing + * @FLASH_ERASE_START: Start erase before flashing + * @FLASH_ERASE_CHECK: Validate the erase functionality + * @FLASH_OOS_CONTROL: Retrieve data based on oos actions + * @FLASH_OOS_DATA_READ: Read data from EBL + * @FLASH_WRITE_IMAGE_RAW: Write the raw image to flash + */ +enum iosm_flash_package_type { + FLASH_SET_PROT_CONF = 0x0086, + FLASH_SEC_START = 0x0204, + FLASH_SEC_END, + FLASH_SET_ADDRESS = 0x0802, + FLASH_ERASE_START = 0x0805, + FLASH_ERASE_CHECK, + FLASH_OOS_CONTROL = 0x080C, + FLASH_OOS_DATA_READ = 0x080E, + FLASH_WRITE_IMAGE_RAW, +}; + +/** + * enum iosm_out_of_session_action - Actions possible over the + * OutOfSession command interface + * @FLASH_OOSC_ACTION_READ: Read data according to its type + * @FLASH_OOSC_ACTION_ERASE: Erase data according to its type + */ +enum iosm_out_of_session_action { + FLASH_OOSC_ACTION_READ = 2, + FLASH_OOSC_ACTION_ERASE = 3, +}; + +/** + * enum iosm_out_of_session_type - Data types that can be handled over the + * Out Of Session command Interface + * @FLASH_OOSC_TYPE_ALL_FLASH: The whole flash area + * @FLASH_OOSC_TYPE_SWID_TABLE: Read the swid table from the target + */ +enum iosm_out_of_session_type { + FLASH_OOSC_TYPE_ALL_FLASH = 8, + FLASH_OOSC_TYPE_SWID_TABLE = 16, +}; + +/** + * enum iosm_ebl_caps - EBL capability settings + * @IOSM_CAP_NOT_ENHANCED: If capability not supported + * @IOSM_CAP_USE_EXT_CAP: To be set if extended capability is set + * @IOSM_EXT_CAP_ERASE_ALL: Set Erase all capability + * @IOSM_EXT_CAP_COMMIT_ALL: Set the commit all capability + */ +enum iosm_ebl_caps { + IOSM_CAP_NOT_ENHANCED = 0x00, + IOSM_CAP_USE_EXT_CAP = 0x01, + IOSM_EXT_CAP_ERASE_ALL = 0x08, + IOSM_EXT_CAP_COMMIT_ALL = 0x20, +}; + +/** + * enum iosm_ebl_rsp - EBL response field + * @EBL_CAPS_FLAG: EBL capability flag + * @EBL_SKIP_ERASE: EBL skip erase flag + * @EBL_SKIP_CRC: EBL skip wr_pack crc + * @EBL_EXT_CAPS_HANDLED: EBL extended capability handled flag + * @EBL_OOS_CONFIG: EBL oos configuration + * @EBL_RSP_SW_INFO_VER: EBL SW info version + */ +enum iosm_ebl_rsp { + EBL_CAPS_FLAG = 50, + EBL_SKIP_ERASE = 54, + EBL_SKIP_CRC = 55, + EBL_EXT_CAPS_HANDLED = 57, + EBL_OOS_CONFIG = 64, + EBL_RSP_SW_INFO_VER = 70, +}; + +/** + * enum iosm_mdm_send_recv_data - Data to send to modem + * @IOSM_MDM_SEND_2: Send 2 bytes of payload + * @IOSM_MDM_SEND_4: Send 4 bytes of payload + * @IOSM_MDM_SEND_8: Send 8 bytes of payload + * @IOSM_MDM_SEND_16: Send 16 bytes of payload + */ +enum iosm_mdm_send_recv_data { + IOSM_MDM_SEND_2 = 2, + IOSM_MDM_SEND_4 = 4, + IOSM_MDM_SEND_8 = 8, + IOSM_MDM_SEND_16 = 16, +}; + +/** + * struct iosm_ebl_one_error - Structure containing error details + * @error_class: Error type- standard, security and text error + * @error_code: Specific error from error type + */ +struct iosm_ebl_one_error { + u16 error_class; + u16 error_code; +}; + +/** + * struct iosm_ebl_error- Structure with max error type supported + * @error: Array of one_error structure with max errors + */ +struct iosm_ebl_error { + struct iosm_ebl_one_error error[IOSM_MAX_ERRORS]; +}; + +/** + * struct iosm_swid_table - SWID table data for modem + * @number_of_data_sets: Number of swid types + * @sw_id_type: SWID type - SWID + * @sw_id_val: SWID value + * @rf_engine_id_type: RF engine ID type - RF_ENGINE_ID + * @rf_engine_id_val: RF engine ID value + */ +struct iosm_swid_table { + u32 number_of_data_sets; + char sw_id_type[IOSM_EBL_NAME]; + u32 sw_id_val; + char rf_engine_id_type[IOSM_EBL_NAME]; + u32 rf_engine_id_val; +}; + +/** + * struct iosm_flash_msg_control - Data sent to modem + * @action: Action to be performed + * @type: Type of action + * @length: Length of the action + * @arguments: Argument value sent to modem + */ +struct iosm_flash_msg_control { + __le32 action; + __le32 type; + __le32 length; + __le32 arguments; +}; + +/** + * struct iosm_flash_data - Header Data to be sent to modem + * @checksum: Checksum value calculated for the payload data + * @pack_id: Flash Action type + * @msg_length: Payload length + */ +struct iosm_flash_data { + __le16 checksum; + __le16 pack_id; + __le32 msg_length; +}; + +/** + * ipc_flash_boot_psi - Inject PSI image + * @ipc_devlink: Pointer to devlink structure + * @fw: FW image + * + * Returns: 0 on success and failure value on error + */ +int ipc_flash_boot_psi(struct iosm_devlink *ipc_devlink, + const struct firmware *fw); + +/** + * ipc_flash_boot_ebl - Inject EBL image + * @ipc_devlink: Pointer to devlink structure + * @fw: FW image + * + * Returns: 0 on success and failure value on error + */ +int ipc_flash_boot_ebl(struct iosm_devlink *ipc_devlink, + const struct firmware *fw); + +/** + * ipc_flash_boot_set_capabilities - Set modem bool capabilities in flash + * @ipc_devlink: Pointer to devlink structure + * @mdm_rsp: Pointer to modem response buffer + * + * Returns: 0 on success and failure value on error + */ +int ipc_flash_boot_set_capabilities(struct iosm_devlink *ipc_devlink, + u8 *mdm_rsp); + +/** + * ipc_flash_link_establish - Flash link establishment + * @ipc_imem: Pointer to struct iosm_imem + * + * Returns: 0 on success and failure value on error + */ +int ipc_flash_link_establish(struct iosm_imem *ipc_imem); + +/** + * ipc_flash_read_swid - Get swid during flash phase + * @ipc_devlink: Pointer to devlink structure + * @mdm_rsp: Pointer to modem response buffer + * + * Returns: 0 on success and failure value on error + */ +int ipc_flash_read_swid(struct iosm_devlink *ipc_devlink, u8 *mdm_rsp); + +/** + * ipc_flash_send_fls - Inject Modem subsystem fls file to device + * @ipc_devlink: Pointer to devlink structure + * @fw: FW image + * @mdm_rsp: Pointer to modem response buffer + * + * Returns: 0 on success and failure value on error + */ +int ipc_flash_send_fls(struct iosm_devlink *ipc_devlink, + const struct firmware *fw, u8 *mdm_rsp); +#endif diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem.c b/drivers/net/wwan/iosm/iosm_ipc_imem.c index 9f00e36b7f79..1cf49e9959f4 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_imem.c +++ b/drivers/net/wwan/iosm/iosm_ipc_imem.c @@ -6,6 +6,8 @@ #include #include "iosm_ipc_chnl_cfg.h" +#include "iosm_ipc_devlink.h" +#include "iosm_ipc_flash.h" #include "iosm_ipc_imem.h" #include "iosm_ipc_port.h" @@ -263,9 +265,12 @@ static void ipc_imem_dl_skb_process(struct iosm_imem *ipc_imem, switch (pipe->channel->ctype) { case IPC_CTYPE_CTRL: port_id = pipe->channel->channel_id; - - /* Pass the packet to the wwan layer. */ - wwan_port_rx(ipc_imem->ipc_port[port_id]->iosm_port, skb); + if (port_id == IPC_MEM_CTRL_CHL_ID_7) + ipc_imem_sys_devlink_notify_rx(ipc_imem->ipc_devlink, + skb); + else + wwan_port_rx(ipc_imem->ipc_port[port_id]->iosm_port, + skb); break; case IPC_CTYPE_WWAN: @@ -399,19 +404,8 @@ static void ipc_imem_rom_irq_exec(struct iosm_imem *ipc_imem) { struct ipc_mem_channel *channel; - if (ipc_imem->flash_channel_id < 0) { - ipc_imem->rom_exit_code = IMEM_ROM_EXIT_FAIL; - dev_err(ipc_imem->dev, "Missing flash app:%d", - ipc_imem->flash_channel_id); - return; - } - + channel = ipc_imem->ipc_devlink->devlink_sio.channel; ipc_imem->rom_exit_code = ipc_mmio_get_rom_exit_code(ipc_imem->mmio); - - /* Wake up the flash app to continue or to terminate depending - * on the CP ROM exit code. - */ - channel = &ipc_imem->channels[ipc_imem->flash_channel_id]; complete(&channel->ul_sem); } @@ -572,7 +566,7 @@ static void ipc_imem_handle_irq(struct iosm_imem *ipc_imem, int irq) enum ipc_phase old_phase, phase; bool retry_allocation = false; bool ul_pending = false; - int ch_id, i; + int i; if (irq != IMEM_IRQ_DONT_CARE) ipc_imem->ev_irq_pending[irq] = false; @@ -696,11 +690,8 @@ static void ipc_imem_handle_irq(struct iosm_imem *ipc_imem, int irq) if ((phase == IPC_P_PSI || phase == IPC_P_EBL) && ipc_imem->ipc_requested_state == IPC_MEM_DEVICE_IPC_RUNNING && ipc_mmio_get_ipc_state(ipc_imem->mmio) == - IPC_MEM_DEVICE_IPC_RUNNING && - ipc_imem->flash_channel_id >= 0) { - /* Wake up the flash app to open the pipes. */ - ch_id = ipc_imem->flash_channel_id; - complete(&ipc_imem->channels[ch_id].ul_sem); + IPC_MEM_DEVICE_IPC_RUNNING) { + complete(&ipc_imem->ipc_devlink->devlink_sio.channel->ul_sem); } /* Reset the expected CP state. */ @@ -1176,6 +1167,9 @@ void ipc_imem_cleanup(struct iosm_imem *ipc_imem) ipc_port_deinit(ipc_imem->ipc_port); } + if (ipc_imem->ipc_devlink) + ipc_devlink_deinit(ipc_imem->ipc_devlink); + ipc_imem_device_ipc_uninit(ipc_imem); ipc_imem_channel_reset(ipc_imem); @@ -1258,6 +1252,7 @@ struct iosm_imem *ipc_imem_init(struct iosm_pcie *pcie, unsigned int device_id, void __iomem *mmio, struct device *dev) { struct iosm_imem *ipc_imem = kzalloc(sizeof(*pcie->imem), GFP_KERNEL); + enum ipc_mem_exec_stage stage; if (!ipc_imem) return NULL; @@ -1272,9 +1267,6 @@ struct iosm_imem *ipc_imem_init(struct iosm_pcie *pcie, unsigned int device_id, ipc_imem->cp_version = 0; ipc_imem->device_sleep = IPC_HOST_SLEEP_ENTER_SLEEP; - /* Reset the flash channel id. */ - ipc_imem->flash_channel_id = -1; - /* Reset the max number of configured channels */ ipc_imem->nr_of_channels = 0; @@ -1328,8 +1320,21 @@ struct iosm_imem *ipc_imem_init(struct iosm_pcie *pcie, unsigned int device_id, goto imem_config_fail; } - return ipc_imem; + stage = ipc_mmio_get_exec_stage(ipc_imem->mmio); + if (stage == IPC_MEM_EXEC_STAGE_BOOT) { + /* Alloc and Register devlink */ + ipc_imem->ipc_devlink = ipc_devlink_init(ipc_imem); + if (!ipc_imem->ipc_devlink) { + dev_err(ipc_imem->dev, "Devlink register failed"); + goto imem_config_fail; + } + if (ipc_flash_link_establish(ipc_imem)) + goto devlink_channel_fail; + } + return ipc_imem; +devlink_channel_fail: + ipc_devlink_deinit(ipc_imem->ipc_devlink); imem_config_fail: hrtimer_cancel(&ipc_imem->td_alloc_timer); hrtimer_cancel(&ipc_imem->fast_update_timer); @@ -1361,3 +1366,51 @@ void ipc_imem_td_update_timer_suspend(struct iosm_imem *ipc_imem, bool suspend) { ipc_imem->td_update_timer_suspended = suspend; } + +/* Verify the CP execution state, copy the chip info, + * change the execution phase to ROM + */ +static int ipc_imem_devlink_trigger_chip_info_cb(struct iosm_imem *ipc_imem, + int arg, void *msg, + size_t msgsize) +{ + enum ipc_mem_exec_stage stage; + struct sk_buff *skb; + int rc = -EINVAL; + size_t size; + + /* Test the CP execution state. */ + stage = ipc_mmio_get_exec_stage(ipc_imem->mmio); + if (stage != IPC_MEM_EXEC_STAGE_BOOT) { + dev_err(ipc_imem->dev, + "Execution_stage: expected BOOT, received = %X", stage); + goto trigger_chip_info_fail; + } + /* Allocate a new sk buf for the chip info. */ + size = ipc_imem->mmio->chip_info_size; + if (size > IOSM_CHIP_INFO_SIZE_MAX) + goto trigger_chip_info_fail; + + skb = ipc_pcie_alloc_local_skb(ipc_imem->pcie, GFP_ATOMIC, size); + if (!skb) { + dev_err(ipc_imem->dev, "exhausted skbuf kernel DL memory"); + rc = -ENOMEM; + goto trigger_chip_info_fail; + } + /* Copy the chip info characters into the ipc_skb. */ + ipc_mmio_copy_chip_info(ipc_imem->mmio, skb_put(skb, size), size); + /* First change to the ROM boot phase. */ + dev_dbg(ipc_imem->dev, "execution_stage[%X] eq. BOOT", stage); + ipc_imem->phase = ipc_imem_phase_update(ipc_imem); + ipc_imem_sys_devlink_notify_rx(ipc_imem->ipc_devlink, skb); + rc = 0; +trigger_chip_info_fail: + return rc; +} + +int ipc_imem_devlink_trigger_chip_info(struct iosm_imem *ipc_imem) +{ + return ipc_task_queue_send_task(ipc_imem, + ipc_imem_devlink_trigger_chip_info_cb, + 0, NULL, 0, true); +} diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem.h b/drivers/net/wwan/iosm/iosm_ipc_imem.h index dc65b0712261..8380008232be 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_imem.h +++ b/drivers/net/wwan/iosm/iosm_ipc_imem.h @@ -7,6 +7,7 @@ #define IOSM_IPC_IMEM_H #include +#include #include "iosm_ipc_mmio.h" #include "iosm_ipc_pcie.h" @@ -69,7 +70,7 @@ struct ipc_chnl_cfg; #define IMEM_IRQ_DONT_CARE (-1) -#define IPC_MEM_MAX_CHANNELS 7 +#define IPC_MEM_MAX_CHANNELS 8 #define IPC_MEM_MUX_IP_SESSION_ENTRIES 8 @@ -98,6 +99,7 @@ struct ipc_chnl_cfg; #define IPC_MEM_DL_ETH_OFFSET 16 #define IPC_CB(skb) ((struct ipc_skb_cb *)((skb)->cb)) +#define IOSM_CHIP_INFO_SIZE_MAX 100 #define FULLY_FUNCTIONAL 0 @@ -304,9 +306,9 @@ enum ipc_phase { * @ipc_port: IPC PORT data structure pointer * @pcie: IPC PCIe * @dev: Pointer to device structure - * @flash_channel_id: Reserved channel id for flashing to RAM. * @ipc_requested_state: Expected IPC state on CP. * @channels: Channel list with UL/DL pipe pairs. + * @ipc_devlink: IPC Devlink data structure pointer * @ipc_status: local ipc_status * @nr_of_channels: number of configured channels * @startup_timer: startup timer for NAND support. @@ -349,9 +351,9 @@ struct iosm_imem { struct iosm_cdev *ipc_port[IPC_MEM_MAX_CHANNELS]; struct iosm_pcie *pcie; struct device *dev; - int flash_channel_id; enum ipc_mem_device_ipc_state ipc_requested_state; struct ipc_mem_channel channels[IPC_MEM_MAX_CHANNELS]; + struct iosm_devlink *ipc_devlink; u32 ipc_status; u32 nr_of_channels; struct hrtimer startup_timer; @@ -575,4 +577,15 @@ void ipc_imem_ipc_init_check(struct iosm_imem *ipc_imem); */ void ipc_imem_channel_init(struct iosm_imem *ipc_imem, enum ipc_ctype ctype, struct ipc_chnl_cfg chnl_cfg, u32 irq_moderation); + +/** + * ipc_imem_devlink_trigger_chip_info - Inform devlink that the chip + * information are available if the + * flashing to RAM interworking shall be + * executed. + * @ipc_imem: Pointer to imem structure + * + * Returns: 0 on success, -1 on failure + */ +int ipc_imem_devlink_trigger_chip_info(struct iosm_imem *ipc_imem); #endif diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem_ops.c b/drivers/net/wwan/iosm/iosm_ipc_imem_ops.c index 0a472ce77370..b885a6570235 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_imem_ops.c +++ b/drivers/net/wwan/iosm/iosm_ipc_imem_ops.c @@ -6,6 +6,7 @@ #include #include "iosm_ipc_chnl_cfg.h" +#include "iosm_ipc_devlink.h" #include "iosm_ipc_imem.h" #include "iosm_ipc_imem_ops.h" #include "iosm_ipc_port.h" @@ -331,3 +332,319 @@ int ipc_imem_sys_cdev_write(struct iosm_cdev *ipc_cdev, struct sk_buff *skb) out: return ret; } + +/* Open a SIO link to CP and return the channel instance */ +struct ipc_mem_channel *ipc_imem_sys_devlink_open(struct iosm_imem *ipc_imem) +{ + struct ipc_mem_channel *channel; + enum ipc_phase phase; + int channel_id; + + phase = ipc_imem_phase_update(ipc_imem); + switch (phase) { + case IPC_P_OFF: + case IPC_P_ROM: + /* Get a channel id as flash id and reserve it. */ + channel_id = ipc_imem_channel_alloc(ipc_imem, + IPC_MEM_CTRL_CHL_ID_7, + IPC_CTYPE_CTRL); + + if (channel_id < 0) { + dev_err(ipc_imem->dev, + "reservation of a flash channel id failed"); + goto error; + } + + ipc_imem->ipc_devlink->devlink_sio.channel_id = channel_id; + channel = &ipc_imem->channels[channel_id]; + + /* Enqueue chip info data to be read */ + if (ipc_imem_devlink_trigger_chip_info(ipc_imem)) { + dev_err(ipc_imem->dev, "Enqueue of chip info failed"); + channel->state = IMEM_CHANNEL_FREE; + goto error; + } + + return channel; + + case IPC_P_PSI: + case IPC_P_EBL: + ipc_imem->cp_version = ipc_mmio_get_cp_version(ipc_imem->mmio); + if (ipc_imem->cp_version == -1) { + dev_err(ipc_imem->dev, "invalid CP version"); + goto error; + } + + channel_id = ipc_imem->ipc_devlink->devlink_sio.channel_id; + return ipc_imem_channel_open(ipc_imem, channel_id, + IPC_HP_CDEV_OPEN); + + default: + /* CP is in the wrong state (e.g. CRASH or CD_READY) */ + dev_err(ipc_imem->dev, "SIO open refused, phase %d", phase); + } +error: + return NULL; +} + +/* Release a SIO channel link to CP. */ +void ipc_imem_sys_devlink_close(struct iosm_devlink *ipc_devlink) +{ + struct iosm_imem *ipc_imem = ipc_devlink->pcie->imem; + int boot_check_timeout = BOOT_CHECK_DEFAULT_TIMEOUT; + enum ipc_mem_exec_stage exec_stage; + struct ipc_mem_channel *channel; + enum ipc_phase curr_phase; + int status = 0; + u32 tail = 0; + + channel = ipc_imem->ipc_devlink->devlink_sio.channel; + curr_phase = ipc_imem->phase; + /* Increase the total wait time to boot_check_timeout */ + do { + exec_stage = ipc_mmio_get_exec_stage(ipc_imem->mmio); + if (exec_stage == IPC_MEM_EXEC_STAGE_RUN || + exec_stage == IPC_MEM_EXEC_STAGE_PSI) + break; + msleep(20); + boot_check_timeout -= 20; + } while (boot_check_timeout > 0); + + /* If there are any pending TDs then wait for Timeout/Completion before + * closing pipe. + */ + if (channel->ul_pipe.old_tail != channel->ul_pipe.old_head) { + status = wait_for_completion_interruptible_timeout + (&ipc_imem->ul_pend_sem, + msecs_to_jiffies(IPC_PEND_DATA_TIMEOUT)); + if (status == 0) { + dev_dbg(ipc_imem->dev, + "Data Timeout on UL-Pipe:%d Head:%d Tail:%d", + channel->ul_pipe.pipe_nr, + channel->ul_pipe.old_head, + channel->ul_pipe.old_tail); + } + } + + ipc_protocol_get_head_tail_index(ipc_imem->ipc_protocol, + &channel->dl_pipe, NULL, &tail); + + if (tail != channel->dl_pipe.old_tail) { + status = wait_for_completion_interruptible_timeout + (&ipc_imem->dl_pend_sem, + msecs_to_jiffies(IPC_PEND_DATA_TIMEOUT)); + if (status == 0) { + dev_dbg(ipc_imem->dev, + "Data Timeout on DL-Pipe:%d Head:%d Tail:%d", + channel->dl_pipe.pipe_nr, + channel->dl_pipe.old_head, + channel->dl_pipe.old_tail); + } + } + + /* Due to wait for completion in messages, there is a small window + * between closing the pipe and updating the channel is closed. In this + * small window there could be HP update from Host Driver. Hence update + * the channel state as CLOSING to aviod unnecessary interrupt + * towards CP. + */ + channel->state = IMEM_CHANNEL_CLOSING; + /* Release the pipe resources */ + ipc_imem_pipe_cleanup(ipc_imem, &channel->ul_pipe); + ipc_imem_pipe_cleanup(ipc_imem, &channel->dl_pipe); +} + +void ipc_imem_sys_devlink_notify_rx(struct iosm_devlink *ipc_devlink, + struct sk_buff *skb) +{ + skb_queue_tail(&ipc_devlink->devlink_sio.rx_list, skb); + complete(&ipc_devlink->devlink_sio.read_sem); +} + +/* PSI transfer */ +static int ipc_imem_sys_psi_transfer(struct iosm_imem *ipc_imem, + struct ipc_mem_channel *channel, + unsigned char *buf, int count) +{ + int psi_start_timeout = PSI_START_DEFAULT_TIMEOUT; + enum ipc_mem_exec_stage exec_stage; + + dma_addr_t mapping = 0; + int ret; + + ret = ipc_pcie_addr_map(ipc_imem->pcie, buf, count, &mapping, + DMA_TO_DEVICE); + if (ret) + goto pcie_addr_map_fail; + + /* Save the PSI information for the CP ROM driver on the doorbell + * scratchpad. + */ + ipc_mmio_set_psi_addr_and_size(ipc_imem->mmio, mapping, count); + ipc_doorbell_fire(ipc_imem->pcie, 0, IPC_MEM_EXEC_STAGE_BOOT); + + ret = wait_for_completion_interruptible_timeout + (&channel->ul_sem, + msecs_to_jiffies(IPC_PSI_TRANSFER_TIMEOUT)); + + if (ret <= 0) { + dev_err(ipc_imem->dev, "Failed PSI transfer to CP, Error-%d", + ret); + goto psi_transfer_fail; + } + /* If the PSI download fails, return the CP boot ROM exit code */ + if (ipc_imem->rom_exit_code != IMEM_ROM_EXIT_OPEN_EXT && + ipc_imem->rom_exit_code != IMEM_ROM_EXIT_CERT_EXT) { + ret = (-1) * ((int)ipc_imem->rom_exit_code); + goto psi_transfer_fail; + } + + dev_dbg(ipc_imem->dev, "PSI image successfully downloaded"); + + /* Wait psi_start_timeout milliseconds until the CP PSI image is + * running and updates the execution_stage field with + * IPC_MEM_EXEC_STAGE_PSI. Verify the execution stage. + */ + do { + exec_stage = ipc_mmio_get_exec_stage(ipc_imem->mmio); + + if (exec_stage == IPC_MEM_EXEC_STAGE_PSI) + break; + + msleep(20); + psi_start_timeout -= 20; + } while (psi_start_timeout > 0); + + if (exec_stage != IPC_MEM_EXEC_STAGE_PSI) + goto psi_transfer_fail; /* Unknown status of CP PSI process. */ + + ipc_imem->phase = IPC_P_PSI; + + /* Enter the PSI phase. */ + dev_dbg(ipc_imem->dev, "execution_stage[%X] eq. PSI", exec_stage); + + /* Request the RUNNING state from CP and wait until it was reached + * or timeout. + */ + ipc_imem_ipc_init_check(ipc_imem); + + ret = wait_for_completion_interruptible_timeout + (&channel->ul_sem, msecs_to_jiffies(IPC_PSI_TRANSFER_TIMEOUT)); + if (ret <= 0) { + dev_err(ipc_imem->dev, + "Failed PSI RUNNING state on CP, Error-%d", ret); + goto psi_transfer_fail; + } + + if (ipc_mmio_get_ipc_state(ipc_imem->mmio) != + IPC_MEM_DEVICE_IPC_RUNNING) { + dev_err(ipc_imem->dev, + "ch[%d] %s: unexpected CP IPC state %d, not RUNNING", + channel->channel_id, + ipc_imem_phase_get_string(ipc_imem->phase), + ipc_mmio_get_ipc_state(ipc_imem->mmio)); + + goto psi_transfer_fail; + } + + /* Create the flash channel for the transfer of the images. */ + if (!ipc_imem_sys_devlink_open(ipc_imem)) { + dev_err(ipc_imem->dev, "can't open flash_channel"); + goto psi_transfer_fail; + } + + ret = 0; +psi_transfer_fail: + ipc_pcie_addr_unmap(ipc_imem->pcie, count, mapping, DMA_TO_DEVICE); +pcie_addr_map_fail: + return ret; +} + +int ipc_imem_sys_devlink_write(struct iosm_devlink *ipc_devlink, + unsigned char *buf, int count) +{ + struct iosm_imem *ipc_imem = ipc_devlink->pcie->imem; + struct ipc_mem_channel *channel; + struct sk_buff *skb; + dma_addr_t mapping; + int ret; + + channel = ipc_imem->ipc_devlink->devlink_sio.channel; + + /* In the ROM phase the PSI image is passed to CP about a specific + * shared memory area and doorbell scratchpad directly. + */ + if (ipc_imem->phase == IPC_P_ROM) { + ret = ipc_imem_sys_psi_transfer(ipc_imem, channel, buf, count); + /* If the PSI transfer fails then send crash + * Signature. + */ + if (ret > 0) + ipc_imem_msg_send_feature_set(ipc_imem, + IPC_MEM_INBAND_CRASH_SIG, + false); + goto out; + } + + /* Allocate skb memory for the uplink buffer. */ + skb = ipc_pcie_alloc_skb(ipc_devlink->pcie, count, GFP_KERNEL, &mapping, + DMA_TO_DEVICE, 0); + if (!skb) { + ret = -ENOMEM; + goto out; + } + + memcpy(skb_put(skb, count), buf, count); + + IPC_CB(skb)->op_type = UL_USR_OP_BLOCKED; + + /* Add skb to the uplink skbuf accumulator. */ + skb_queue_tail(&channel->ul_list, skb); + + /* Inform the IPC tasklet to pass uplink IP packets to CP. */ + if (!ipc_imem_call_cdev_write(ipc_imem)) { + ret = wait_for_completion_interruptible(&channel->ul_sem); + + if (ret < 0) { + dev_err(ipc_imem->dev, + "ch[%d] no CP confirmation, status = %d", + channel->channel_id, ret); + ipc_pcie_kfree_skb(ipc_devlink->pcie, skb); + goto out; + } + } + ret = 0; +out: + return ret; +} + +int ipc_imem_sys_devlink_read(struct iosm_devlink *devlink, u8 *data, + u32 bytes_to_read, u32 *bytes_read) +{ + struct sk_buff *skb = NULL; + int rc = 0; + + /* check skb is available in rx_list or wait for skb */ + devlink->devlink_sio.devlink_read_pend = 1; + while (!skb && !(skb = skb_dequeue(&devlink->devlink_sio.rx_list))) { + if (!wait_for_completion_interruptible_timeout + (&devlink->devlink_sio.read_sem, + msecs_to_jiffies(IPC_READ_TIMEOUT))) { + dev_err(devlink->dev, "Read timedout"); + rc = -ETIMEDOUT; + goto devlink_read_fail; + } + } + devlink->devlink_sio.devlink_read_pend = 0; + if (bytes_to_read < skb->len) { + dev_err(devlink->dev, "Invalid size,expected len %d", skb->len); + rc = -EINVAL; + goto devlink_read_fail; + } + *bytes_read = skb->len; + memcpy(data, skb->data, skb->len); + +devlink_read_fail: + ipc_pcie_kfree_skb(devlink->pcie, skb); + return rc; +} diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem_ops.h b/drivers/net/wwan/iosm/iosm_ipc_imem_ops.h index 2007fe23e9a5..f0c88ac5643c 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_imem_ops.h +++ b/drivers/net/wwan/iosm/iosm_ipc_imem_ops.h @@ -9,7 +9,7 @@ #include "iosm_ipc_mux_codec.h" /* Maximum wait time for blocking read */ -#define IPC_READ_TIMEOUT 500 +#define IPC_READ_TIMEOUT 3000 /* The delay in ms for defering the unregister */ #define SIO_UNREGISTER_DEFER_DELAY_MS 1 @@ -98,4 +98,51 @@ int ipc_imem_sys_wwan_transmit(struct iosm_imem *ipc_imem, int if_id, */ void ipc_imem_wwan_channel_init(struct iosm_imem *ipc_imem, enum ipc_mux_protocol mux_type); + +/** + * ipc_imem_sys_devlink_open - Open a Flash/CD Channel link to CP + * @ipc_imem: iosm_imem instance + * + * Return: channel instance on success, NULL for failure + */ +struct ipc_mem_channel *ipc_imem_sys_devlink_open(struct iosm_imem *ipc_imem); + +/** + * ipc_imem_sys_devlink_close - Release a Flash/CD channel link to CP + * @ipc_devlink: Pointer to ipc_devlink data-struct + * + */ +void ipc_imem_sys_devlink_close(struct iosm_devlink *ipc_devlink); + +/** + * ipc_imem_sys_devlink_notify_rx - Receive downlink characters from CP, + * the downlink skbuf is added at the end of the + * downlink or rx list + * @ipc_devlink: Pointer to ipc_devlink data-struct + * @skb: Pointer to sk buffer + */ +void ipc_imem_sys_devlink_notify_rx(struct iosm_devlink *ipc_devlink, + struct sk_buff *skb); + +/** + * ipc_imem_sys_devlink_read - Copy the rx data and free the skbuf + * @ipc_devlink: Devlink instance + * @data: Buffer to read the data from modem + * @bytes_to_read: Size of destination buffer + * @bytes_read: Number of bytes read + * + * Return: 0 on success and failure value on error + */ +int ipc_imem_sys_devlink_read(struct iosm_devlink *ipc_devlink, u8 *data, + u32 bytes_to_read, u32 *bytes_read); + +/** + * ipc_imem_sys_devlink_write - Route the uplink buffer to CP + * @ipc_devlink: Devlink_sio instance + * @buf: Pointer to buffer + * @count: Number of data bytes to write + * Return: 0 on success and failure value on error + */ +int ipc_imem_sys_devlink_write(struct iosm_devlink *ipc_devlink, + unsigned char *buf, int count); #endif From 847371ce049badf6a56d7e9b8699eb44b686c55e Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 14 Sep 2021 09:13:23 +0300 Subject: [PATCH 12/87] mlxsw: spectrum: Bump minimum FW version to xx.2008.3326 Add latest verified version of Nvidia Spectrum-family switch firmware, for Spectrum (13.2008.3326), Spectrum-2 (29.2008.3326) and Spectrum-3 (30.2008.3326). Signed-off-by: Jiri Pirko Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 250c5a24264d..583b09be92e6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -47,7 +47,7 @@ #define MLXSW_SP1_FWREV_MAJOR 13 #define MLXSW_SP1_FWREV_MINOR 2008 -#define MLXSW_SP1_FWREV_SUBMINOR 2406 +#define MLXSW_SP1_FWREV_SUBMINOR 3326 #define MLXSW_SP1_FWREV_CAN_RESET_MINOR 1702 static const struct mlxsw_fw_rev mlxsw_sp1_fw_rev = { @@ -64,7 +64,7 @@ static const struct mlxsw_fw_rev mlxsw_sp1_fw_rev = { #define MLXSW_SP2_FWREV_MAJOR 29 #define MLXSW_SP2_FWREV_MINOR 2008 -#define MLXSW_SP2_FWREV_SUBMINOR 2406 +#define MLXSW_SP2_FWREV_SUBMINOR 3326 static const struct mlxsw_fw_rev mlxsw_sp2_fw_rev = { .major = MLXSW_SP2_FWREV_MAJOR, @@ -79,7 +79,7 @@ static const struct mlxsw_fw_rev mlxsw_sp2_fw_rev = { #define MLXSW_SP3_FWREV_MAJOR 30 #define MLXSW_SP3_FWREV_MINOR 2008 -#define MLXSW_SP3_FWREV_SUBMINOR 2406 +#define MLXSW_SP3_FWREV_SUBMINOR 3326 static const struct mlxsw_fw_rev mlxsw_sp3_fw_rev = { .major = MLXSW_SP3_FWREV_MAJOR, From 13eb056ee58ba2437c0d358cb2dff17ef9056cfb Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 14 Sep 2021 09:13:24 +0300 Subject: [PATCH 13/87] mlxsw: spectrum: Move port module mapping before core port init During port creation, mlxsw_core_port_init() is called with the front panel port number and the split port sub-number. Currently, this information is determined by the driver without firmware assistance. Subsequent patches are going to query this information from firmware, but this requires the port to be mapped to a module. Therefore, move port mapping before mlxsw_core_port_init(). Signed-off-by: Jiri Pirko Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum.c | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 583b09be92e6..912a7f8f6c1f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -533,14 +533,14 @@ mlxsw_sp_port_module_info_get(struct mlxsw_sp *mlxsw_sp, u8 local_port, return 0; } -static int mlxsw_sp_port_module_map(struct mlxsw_sp_port *mlxsw_sp_port) +static int +mlxsw_sp_port_module_map(struct mlxsw_sp *mlxsw_sp, u8 local_port, + const struct mlxsw_sp_port_mapping *port_mapping) { - struct mlxsw_sp_port_mapping *port_mapping = &mlxsw_sp_port->mapping; - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; char pmlp_pl[MLXSW_REG_PMLP_LEN]; int i; - mlxsw_reg_pmlp_pack(pmlp_pl, mlxsw_sp_port->local_port); + mlxsw_reg_pmlp_pack(pmlp_pl, local_port); mlxsw_reg_pmlp_width_set(pmlp_pl, port_mapping->width); for (i = 0; i < port_mapping->width; i++) { mlxsw_reg_pmlp_module_set(pmlp_pl, i, port_mapping->module); @@ -550,12 +550,11 @@ static int mlxsw_sp_port_module_map(struct mlxsw_sp_port *mlxsw_sp_port) return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl); } -static int mlxsw_sp_port_module_unmap(struct mlxsw_sp_port *mlxsw_sp_port) +static int mlxsw_sp_port_module_unmap(struct mlxsw_sp *mlxsw_sp, u8 local_port) { - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; char pmlp_pl[MLXSW_REG_PMLP_LEN]; - mlxsw_reg_pmlp_pack(pmlp_pl, mlxsw_sp_port->local_port); + mlxsw_reg_pmlp_pack(pmlp_pl, local_port); mlxsw_reg_pmlp_width_set(pmlp_pl, 0); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl); } @@ -1454,6 +1453,13 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, bool splittable; int err; + err = mlxsw_sp_port_module_map(mlxsw_sp, local_port, port_mapping); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to map module\n", + local_port); + return err; + } + splittable = lanes > 1 && !split; err = mlxsw_core_port_init(mlxsw_sp->core, local_port, port_mapping->module + 1, split, @@ -1464,7 +1470,7 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, if (err) { dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to init core port\n", local_port); - return err; + goto err_core_port_init; } dev = alloc_etherdev(sizeof(struct mlxsw_sp_port)); @@ -1498,13 +1504,6 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, dev->netdev_ops = &mlxsw_sp_port_netdev_ops; dev->ethtool_ops = &mlxsw_sp_port_ethtool_ops; - err = mlxsw_sp_port_module_map(mlxsw_sp_port); - if (err) { - dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to map module\n", - mlxsw_sp_port->local_port); - goto err_port_module_map; - } - err = mlxsw_sp_port_swid_set(mlxsw_sp_port, 0); if (err) { dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set SWID\n", @@ -1714,13 +1713,13 @@ err_port_system_port_mapping_set: err_dev_addr_init: mlxsw_sp_port_swid_set(mlxsw_sp_port, MLXSW_PORT_SWID_DISABLED_PORT); err_port_swid_set: - mlxsw_sp_port_module_unmap(mlxsw_sp_port); -err_port_module_map: free_percpu(mlxsw_sp_port->pcpu_stats); err_alloc_stats: free_netdev(dev); err_alloc_etherdev: mlxsw_core_port_fini(mlxsw_sp->core, local_port); +err_core_port_init: + mlxsw_sp_port_module_unmap(mlxsw_sp, local_port); return err; } @@ -1743,11 +1742,11 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) mlxsw_sp_port_tc_mc_mode_set(mlxsw_sp_port, false); mlxsw_sp_port_buffers_fini(mlxsw_sp_port); mlxsw_sp_port_swid_set(mlxsw_sp_port, MLXSW_PORT_SWID_DISABLED_PORT); - mlxsw_sp_port_module_unmap(mlxsw_sp_port); free_percpu(mlxsw_sp_port->pcpu_stats); WARN_ON_ONCE(!list_empty(&mlxsw_sp_port->vlans_list)); free_netdev(mlxsw_sp_port->dev); mlxsw_core_port_fini(mlxsw_sp->core, local_port); + mlxsw_sp_port_module_unmap(mlxsw_sp, local_port); } static int mlxsw_sp_cpu_port_create(struct mlxsw_sp *mlxsw_sp) From fec2386162d1625ef4a436e1dced27e235d5c181 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 14 Sep 2021 09:13:25 +0300 Subject: [PATCH 14/87] mlxsw: spectrum: Move port SWID set before core port init During port creation, mlxsw_core_port_init() is called with the front panel port number and the split port sub-number. Currently, this information is determined by the driver without firmware assistance. Subsequent patches are going to query this information from firmware, but this requires the port to assigned to SWID. Therefore, move port SWID assignment before mlxsw_core_port_init(). Signed-off-by: Jiri Pirko Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum.c | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 912a7f8f6c1f..e12232f371c8 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -351,12 +351,12 @@ static int mlxsw_sp_port_mtu_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 mtu) return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmtu), pmtu_pl); } -static int mlxsw_sp_port_swid_set(struct mlxsw_sp_port *mlxsw_sp_port, u8 swid) +static int mlxsw_sp_port_swid_set(struct mlxsw_sp *mlxsw_sp, + u8 local_port, u8 swid) { - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; char pspa_pl[MLXSW_REG_PSPA_LEN]; - mlxsw_reg_pspa_pack(pspa_pl, swid, mlxsw_sp_port->local_port); + mlxsw_reg_pspa_pack(pspa_pl, swid, local_port); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pspa), pspa_pl); } @@ -1460,6 +1460,13 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, return err; } + err = mlxsw_sp_port_swid_set(mlxsw_sp, local_port, 0); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set SWID\n", + local_port); + goto err_port_swid_set; + } + splittable = lanes > 1 && !split; err = mlxsw_core_port_init(mlxsw_sp->core, local_port, port_mapping->module + 1, split, @@ -1504,13 +1511,6 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, dev->netdev_ops = &mlxsw_sp_port_netdev_ops; dev->ethtool_ops = &mlxsw_sp_port_ethtool_ops; - err = mlxsw_sp_port_swid_set(mlxsw_sp_port, 0); - if (err) { - dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set SWID\n", - mlxsw_sp_port->local_port); - goto err_port_swid_set; - } - err = mlxsw_sp_port_dev_addr_init(mlxsw_sp_port); if (err) { dev_err(mlxsw_sp->bus_info->dev, "Port %d: Unable to init port mac address\n", @@ -1711,14 +1711,15 @@ err_max_speed_get: err_port_speed_by_width_set: err_port_system_port_mapping_set: err_dev_addr_init: - mlxsw_sp_port_swid_set(mlxsw_sp_port, MLXSW_PORT_SWID_DISABLED_PORT); -err_port_swid_set: free_percpu(mlxsw_sp_port->pcpu_stats); err_alloc_stats: free_netdev(dev); err_alloc_etherdev: mlxsw_core_port_fini(mlxsw_sp->core, local_port); err_core_port_init: + mlxsw_sp_port_swid_set(mlxsw_sp, local_port, + MLXSW_PORT_SWID_DISABLED_PORT); +err_port_swid_set: mlxsw_sp_port_module_unmap(mlxsw_sp, local_port); return err; } @@ -1741,11 +1742,12 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) mlxsw_sp_port_dcb_fini(mlxsw_sp_port); mlxsw_sp_port_tc_mc_mode_set(mlxsw_sp_port, false); mlxsw_sp_port_buffers_fini(mlxsw_sp_port); - mlxsw_sp_port_swid_set(mlxsw_sp_port, MLXSW_PORT_SWID_DISABLED_PORT); free_percpu(mlxsw_sp_port->pcpu_stats); WARN_ON_ONCE(!list_empty(&mlxsw_sp_port->vlans_list)); free_netdev(mlxsw_sp_port->dev); mlxsw_core_port_fini(mlxsw_sp->core, local_port); + mlxsw_sp_port_swid_set(mlxsw_sp, local_port, + MLXSW_PORT_SWID_DISABLED_PORT); mlxsw_sp_port_module_unmap(mlxsw_sp, local_port); } From ed403777f653b9b184cbdb380ea938adf8f31328 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 14 Sep 2021 09:13:26 +0300 Subject: [PATCH 15/87] mlxsw: reg: Add Port Local port to Label Port mapping Register The PLLP register returns the mapping from Local Port into Label Port. Signed-off-by: Jiri Pirko Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 48 +++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 6fbda6ebd590..97f46c468c6d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -5860,6 +5860,53 @@ static inline void mlxsw_reg_pddr_pack(char *payload, u8 local_port, mlxsw_reg_pddr_page_select_set(payload, page_select); } +/* PLLP - Port Local port to Label Port mapping Register + * ----------------------------------------------------- + * The PLLP register returns the mapping from Local Port into Label Port. + */ +#define MLXSW_REG_PLLP_ID 0x504A +#define MLXSW_REG_PLLP_LEN 0x10 + +MLXSW_REG_DEFINE(pllp, MLXSW_REG_PLLP_ID, MLXSW_REG_PLLP_LEN); + +/* reg_pllp_local_port + * Local port number. + * Access: Index + */ +MLXSW_ITEM32(reg, pllp, local_port, 0x00, 16, 8); + +/* reg_pllp_label_port + * Front panel label of the port. + * Access: RO + */ +MLXSW_ITEM32(reg, pllp, label_port, 0x00, 0, 8); + +/* reg_pllp_split_num + * Label split mapping for local_port. + * Access: RO + */ +MLXSW_ITEM32(reg, pllp, split_num, 0x04, 0, 4); + +/* reg_pllp_slot_index + * Slot index (0: Main board). + * Access: RO + */ +MLXSW_ITEM32(reg, pllp, slot_index, 0x08, 0, 4); + +static inline void mlxsw_reg_pllp_pack(char *payload, u8 local_port) +{ + MLXSW_REG_ZERO(pllp, payload); + mlxsw_reg_pllp_local_port_set(payload, local_port); +} + +static inline void mlxsw_reg_pllp_unpack(char *payload, u8 *label_port, + u8 *split_num, u8 *slot_index) +{ + *label_port = mlxsw_reg_pllp_label_port_get(payload); + *split_num = mlxsw_reg_pllp_split_num_get(payload); + *slot_index = mlxsw_reg_pllp_slot_index_get(payload); +} + /* PMTM - Port Module Type Mapping Register * ---------------------------------------- * The PMTM allows query or configuration of module types. @@ -12202,6 +12249,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { MLXSW_REG(pplr), MLXSW_REG(pmpe), MLXSW_REG(pddr), + MLXSW_REG(pllp), MLXSW_REG(pmtm), MLXSW_REG(htgt), MLXSW_REG(hpkt), From 1dbfc9d76551f4683a887900c2185c29690acab3 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 14 Sep 2021 09:13:27 +0300 Subject: [PATCH 16/87] mlxsw: spectrum: Use PLLP to get front panel number and split number Instead of relying on the values coming from the PMLP register, use PLLP to get the information about port front panel number and split number. Signed-off-by: Jiri Pirko Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum.c | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index e12232f371c8..c0e52afe1afd 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1441,6 +1441,23 @@ mlxsw_sp_port_vlan_classification_set(struct mlxsw_sp_port *mlxsw_sp_port, return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spvc), spvc_pl); } +static int mlxsw_sp_port_label_info_get(struct mlxsw_sp *mlxsw_sp, + u8 local_port, u8 *port_number, + u8 *split_port_subnumber, + u8 *slot_index) +{ + char pllp_pl[MLXSW_REG_PLLP_LEN]; + int err; + + mlxsw_reg_pllp_pack(pllp_pl, local_port); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(pllp), pllp_pl); + if (err) + return err; + mlxsw_reg_pllp_unpack(pllp_pl, port_number, + split_port_subnumber, slot_index); + return 0; +} + static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, u8 split_base_local_port, struct mlxsw_sp_port_mapping *port_mapping) @@ -1449,7 +1466,10 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, bool split = !!split_base_local_port; struct mlxsw_sp_port *mlxsw_sp_port; u32 lanes = port_mapping->width; + u8 split_port_subnumber; struct net_device *dev; + u8 port_number; + u8 slot_index; bool splittable; int err; @@ -1467,12 +1487,18 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, goto err_port_swid_set; } + err = mlxsw_sp_port_label_info_get(mlxsw_sp, local_port, &port_number, + &split_port_subnumber, &slot_index); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to get port label information\n", + local_port); + goto err_port_label_info_get; + } + splittable = lanes > 1 && !split; err = mlxsw_core_port_init(mlxsw_sp->core, local_port, - port_mapping->module + 1, split, - port_mapping->lane / lanes, - splittable, lanes, - mlxsw_sp->base_mac, + port_number, split, split_port_subnumber, + splittable, lanes, mlxsw_sp->base_mac, sizeof(mlxsw_sp->base_mac)); if (err) { dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to init core port\n", @@ -1717,6 +1743,7 @@ err_alloc_stats: err_alloc_etherdev: mlxsw_core_port_fini(mlxsw_sp->core, local_port); err_core_port_init: +err_port_label_info_get: mlxsw_sp_port_swid_set(mlxsw_sp, local_port, MLXSW_PORT_SWID_DISABLED_PORT); err_port_swid_set: From 78f824b33530125ae7dabf54d10c867f095aa7d6 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 14 Sep 2021 09:13:28 +0300 Subject: [PATCH 17/87] mlxsw: reg: Add Port Module To local DataBase Register The PMTDB register allows to query the possible module<->local port mapping than can be used in PMLP. It does not represent the actual/current mapping of the local to module. Actual mapping is only defined by PMLP. Signed-off-by: Jiri Pirko Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 64 +++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 97f46c468c6d..390e467050f3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -5766,6 +5766,69 @@ static inline void mlxsw_reg_pplr_pack(char *payload, u8 local_port, MLXSW_REG_PPLR_LB_TYPE_BIT_PHY_LOCAL : 0); } +/* PMTDB - Port Module To local DataBase Register + * ---------------------------------------------- + * The PMTDB register allows to query the possible module<->local port + * mapping than can be used in PMLP. It does not represent the actual/current + * mapping of the local to module. Actual mapping is only defined by PMLP. + */ +#define MLXSW_REG_PMTDB_ID 0x501A +#define MLXSW_REG_PMTDB_LEN 0x40 + +MLXSW_REG_DEFINE(pmtdb, MLXSW_REG_PMTDB_ID, MLXSW_REG_PMTDB_LEN); + +/* reg_pmtdb_slot_index + * Slot index (0: Main board). + * Access: Index + */ +MLXSW_ITEM32(reg, pmtdb, slot_index, 0x00, 24, 4); + +/* reg_pmtdb_module + * Module number. + * Access: Index + */ +MLXSW_ITEM32(reg, pmtdb, module, 0x00, 16, 8); + +/* reg_pmtdb_ports_width + * Port's width + * Access: Index + */ +MLXSW_ITEM32(reg, pmtdb, ports_width, 0x00, 12, 4); + +/* reg_pmtdb_num_ports + * Number of ports in a single module (split/breakout) + * Access: Index + */ +MLXSW_ITEM32(reg, pmtdb, num_ports, 0x00, 8, 4); + +enum mlxsw_reg_pmtdb_status { + MLXSW_REG_PMTDB_STATUS_SUCCESS, +}; + +/* reg_pmtdb_status + * Status + * Access: RO + */ +MLXSW_ITEM32(reg, pmtdb, status, 0x00, 0, 4); + +/* reg_pmtdb_port_num + * The local_port value which can be assigned to the module. + * In case of more than one port, port represent the / port of + * the module. + * Access: RO + */ +MLXSW_ITEM16_INDEXED(reg, pmtdb, port_num, 0x04, 0, 8, 0x02, 0x00, false); + +static inline void mlxsw_reg_pmtdb_pack(char *payload, u8 slot_index, u8 module, + u8 ports_width, u8 num_ports) +{ + MLXSW_REG_ZERO(pmtdb, payload); + mlxsw_reg_pmtdb_slot_index_set(payload, slot_index); + mlxsw_reg_pmtdb_module_set(payload, module); + mlxsw_reg_pmtdb_ports_width_set(payload, ports_width); + mlxsw_reg_pmtdb_num_ports_set(payload, num_ports); +} + /* PMPE - Port Module Plug/Unplug Event Register * --------------------------------------------- * This register reports any operational status change of a module. @@ -12247,6 +12310,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { MLXSW_REG(pspa), MLXSW_REG(pmaos), MLXSW_REG(pplr), + MLXSW_REG(pmtdb), MLXSW_REG(pmpe), MLXSW_REG(pddr), MLXSW_REG(pllp), From 32ada69bba7ebff4b5c886009afba899341ac815 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 14 Sep 2021 09:13:29 +0300 Subject: [PATCH 18/87] mlxsw: spectrum: Use PMTDB register to obtain split info Newly introduced PMTDB register is there to provide all needed info about particular requested port split configuration. Use it instead of figuring the info out manually in the driver. Signed-off-by: Jiri Pirko Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.c | 38 ---- drivers/net/ethernet/mellanox/mlxsw/core.h | 1 - .../net/ethernet/mellanox/mlxsw/resources.h | 6 - .../net/ethernet/mellanox/mlxsw/spectrum.c | 188 +++++++----------- .../net/ethernet/mellanox/mlxsw/spectrum.h | 4 +- 5 files changed, 77 insertions(+), 160 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index f080fab3de2b..98420db90ea1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -2944,44 +2944,6 @@ bool mlxsw_core_is_initialized(const struct mlxsw_core *mlxsw_core) return mlxsw_core->is_initialized; } -int mlxsw_core_module_max_width(struct mlxsw_core *mlxsw_core, u8 module) -{ - enum mlxsw_reg_pmtm_module_type module_type; - char pmtm_pl[MLXSW_REG_PMTM_LEN]; - int err; - - mlxsw_reg_pmtm_pack(pmtm_pl, module); - err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(pmtm), pmtm_pl); - if (err) - return err; - mlxsw_reg_pmtm_unpack(pmtm_pl, &module_type); - - /* Here we need to get the module width according to the module type. */ - - switch (module_type) { - case MLXSW_REG_PMTM_MODULE_TYPE_C2C8X: - case MLXSW_REG_PMTM_MODULE_TYPE_QSFP_DD: - case MLXSW_REG_PMTM_MODULE_TYPE_OSFP: - return 8; - case MLXSW_REG_PMTM_MODULE_TYPE_C2C4X: - case MLXSW_REG_PMTM_MODULE_TYPE_BP_4X: - case MLXSW_REG_PMTM_MODULE_TYPE_QSFP: - return 4; - case MLXSW_REG_PMTM_MODULE_TYPE_C2C2X: - case MLXSW_REG_PMTM_MODULE_TYPE_BP_2X: - case MLXSW_REG_PMTM_MODULE_TYPE_SFP_DD: - case MLXSW_REG_PMTM_MODULE_TYPE_DSFP: - return 2; - case MLXSW_REG_PMTM_MODULE_TYPE_C2C1X: - case MLXSW_REG_PMTM_MODULE_TYPE_BP_1X: - case MLXSW_REG_PMTM_MODULE_TYPE_SFP: - return 1; - default: - return -EINVAL; - } -} -EXPORT_SYMBOL(mlxsw_core_module_max_width); - static void mlxsw_core_buf_dump_dbg(struct mlxsw_core *mlxsw_core, const char *buf, size_t size) { diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index 80712dc803d0..d21981cc04ca 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -250,7 +250,6 @@ mlxsw_core_port_devlink_port_get(struct mlxsw_core *mlxsw_core, bool mlxsw_core_port_is_xm(const struct mlxsw_core *mlxsw_core, u8 local_port); struct mlxsw_env *mlxsw_core_env(const struct mlxsw_core *mlxsw_core); bool mlxsw_core_is_initialized(const struct mlxsw_core *mlxsw_core); -int mlxsw_core_module_max_width(struct mlxsw_core *mlxsw_core, u8 module); int mlxsw_core_schedule_dw(struct delayed_work *dwork, unsigned long delay); bool mlxsw_core_schedule_work(struct work_struct *work); diff --git a/drivers/net/ethernet/mellanox/mlxsw/resources.h b/drivers/net/ethernet/mellanox/mlxsw/resources.h index a56c9e19a390..a1512be77867 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/resources.h +++ b/drivers/net/ethernet/mellanox/mlxsw/resources.h @@ -25,9 +25,6 @@ enum mlxsw_res_id { MLXSW_RES_ID_MAX_SYSTEM_PORT, MLXSW_RES_ID_MAX_LAG, MLXSW_RES_ID_MAX_LAG_MEMBERS, - MLXSW_RES_ID_LOCAL_PORTS_IN_1X, - MLXSW_RES_ID_LOCAL_PORTS_IN_2X, - MLXSW_RES_ID_LOCAL_PORTS_IN_4X, MLXSW_RES_ID_GUARANTEED_SHARED_BUFFER, MLXSW_RES_ID_CELL_SIZE, MLXSW_RES_ID_MAX_HEADROOM_SIZE, @@ -84,9 +81,6 @@ static u16 mlxsw_res_ids[] = { [MLXSW_RES_ID_MAX_SYSTEM_PORT] = 0x2502, [MLXSW_RES_ID_MAX_LAG] = 0x2520, [MLXSW_RES_ID_MAX_LAG_MEMBERS] = 0x2521, - [MLXSW_RES_ID_LOCAL_PORTS_IN_1X] = 0x2610, - [MLXSW_RES_ID_LOCAL_PORTS_IN_2X] = 0x2611, - [MLXSW_RES_ID_LOCAL_PORTS_IN_4X] = 0x2612, [MLXSW_RES_ID_GUARANTEED_SHARED_BUFFER] = 0x2805, /* Bytes */ [MLXSW_RES_ID_CELL_SIZE] = 0x2803, /* Bytes */ [MLXSW_RES_ID_MAX_HEADROOM_SIZE] = 0x2811, /* Bytes */ diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index c0e52afe1afd..9cbc893c2545 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -529,6 +529,7 @@ mlxsw_sp_port_module_info_get(struct mlxsw_sp *mlxsw_sp, u8 local_port, port_mapping->module = module; port_mapping->width = width; + port_mapping->module_width = width; port_mapping->lane = mlxsw_reg_pmlp_tx_lane_get(pmlp_pl, 0); return 0; } @@ -1459,11 +1460,10 @@ static int mlxsw_sp_port_label_info_get(struct mlxsw_sp *mlxsw_sp, } static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, - u8 split_base_local_port, + bool split, struct mlxsw_sp_port_mapping *port_mapping) { struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; - bool split = !!split_base_local_port; struct mlxsw_sp_port *mlxsw_sp_port; u32 lanes = port_mapping->width; u8 split_port_subnumber; @@ -1519,7 +1519,6 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, mlxsw_sp_port->local_port = local_port; mlxsw_sp_port->pvid = MLXSW_SP_DEFAULT_VID; mlxsw_sp_port->split = split; - mlxsw_sp_port->split_base_local_port = split_base_local_port; mlxsw_sp_port->mapping = *port_mapping; mlxsw_sp_port->link.autoneg = 1; INIT_LIST_HEAD(&mlxsw_sp_port->vlans_list); @@ -1817,8 +1816,15 @@ static void mlxsw_sp_cpu_port_remove(struct mlxsw_sp *mlxsw_sp) kfree(mlxsw_sp_port); } +static bool mlxsw_sp_local_port_valid(u8 local_port) +{ + return local_port != MLXSW_PORT_CPU_PORT; +} + static bool mlxsw_sp_port_created(struct mlxsw_sp *mlxsw_sp, u8 local_port) { + if (!mlxsw_sp_local_port_valid(local_port)) + return false; return mlxsw_sp->ports[local_port] != NULL; } @@ -1855,7 +1861,7 @@ static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp) port_mapping = mlxsw_sp->port_mapping[i]; if (!port_mapping) continue; - err = mlxsw_sp_port_create(mlxsw_sp, i, 0, port_mapping); + err = mlxsw_sp_port_create(mlxsw_sp, i, false, port_mapping); if (err) goto err_port_create; } @@ -1922,17 +1928,10 @@ static void mlxsw_sp_port_module_info_fini(struct mlxsw_sp *mlxsw_sp) kfree(mlxsw_sp->port_mapping); } -static u8 mlxsw_sp_cluster_base_port_get(u8 local_port, unsigned int max_width) -{ - u8 offset = (local_port - 1) % max_width; - - return local_port - offset; -} - static int -mlxsw_sp_port_split_create(struct mlxsw_sp *mlxsw_sp, u8 base_port, +mlxsw_sp_port_split_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_port_mapping *port_mapping, - unsigned int count, u8 offset) + unsigned int count, const char *pmtdb_pl) { struct mlxsw_sp_port_mapping split_port_mapping; int err, i; @@ -1940,8 +1939,13 @@ mlxsw_sp_port_split_create(struct mlxsw_sp *mlxsw_sp, u8 base_port, split_port_mapping = *port_mapping; split_port_mapping.width /= count; for (i = 0; i < count; i++) { - err = mlxsw_sp_port_create(mlxsw_sp, base_port + i * offset, - base_port, &split_port_mapping); + u8 s_local_port = mlxsw_reg_pmtdb_port_num_get(pmtdb_pl, i); + + if (!mlxsw_sp_local_port_valid(s_local_port)) + continue; + + err = mlxsw_sp_port_create(mlxsw_sp, s_local_port, + true, &split_port_mapping); if (err) goto err_port_create; split_port_mapping.lane += split_port_mapping.width; @@ -1950,49 +1954,34 @@ mlxsw_sp_port_split_create(struct mlxsw_sp *mlxsw_sp, u8 base_port, return 0; err_port_create: - for (i--; i >= 0; i--) - if (mlxsw_sp_port_created(mlxsw_sp, base_port + i * offset)) - mlxsw_sp_port_remove(mlxsw_sp, base_port + i * offset); + for (i--; i >= 0; i--) { + u8 s_local_port = mlxsw_reg_pmtdb_port_num_get(pmtdb_pl, i); + + if (mlxsw_sp_port_created(mlxsw_sp, s_local_port)) + mlxsw_sp_port_remove(mlxsw_sp, s_local_port); + } return err; } static void mlxsw_sp_port_unsplit_create(struct mlxsw_sp *mlxsw_sp, - u8 base_port, - unsigned int count, u8 offset) + unsigned int count, + const char *pmtdb_pl) { struct mlxsw_sp_port_mapping *port_mapping; int i; /* Go over original unsplit ports in the gap and recreate them. */ - for (i = 0; i < count * offset; i++) { - port_mapping = mlxsw_sp->port_mapping[base_port + i]; - if (!port_mapping) + for (i = 0; i < count; i++) { + u8 local_port = mlxsw_reg_pmtdb_port_num_get(pmtdb_pl, i); + + port_mapping = mlxsw_sp->port_mapping[local_port]; + if (!port_mapping || !mlxsw_sp_local_port_valid(local_port)) continue; - mlxsw_sp_port_create(mlxsw_sp, base_port + i, 0, port_mapping); + mlxsw_sp_port_create(mlxsw_sp, local_port, + false, port_mapping); } } -static int mlxsw_sp_local_ports_offset(struct mlxsw_core *mlxsw_core, - unsigned int count, - unsigned int max_width) -{ - enum mlxsw_res_id local_ports_in_x_res_id; - int split_width = max_width / count; - - if (split_width == 1) - local_ports_in_x_res_id = MLXSW_RES_ID_LOCAL_PORTS_IN_1X; - else if (split_width == 2) - local_ports_in_x_res_id = MLXSW_RES_ID_LOCAL_PORTS_IN_2X; - else if (split_width == 4) - local_ports_in_x_res_id = MLXSW_RES_ID_LOCAL_PORTS_IN_4X; - else - return -EINVAL; - - if (!mlxsw_core_res_valid(mlxsw_core, local_ports_in_x_res_id)) - return -EINVAL; - return mlxsw_core_res_get(mlxsw_core, local_ports_in_x_res_id); -} - static struct mlxsw_sp_port * mlxsw_sp_port_get_by_local_port(struct mlxsw_sp *mlxsw_sp, u8 local_port) { @@ -2008,9 +1997,8 @@ static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port, struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); struct mlxsw_sp_port_mapping port_mapping; struct mlxsw_sp_port *mlxsw_sp_port; - int max_width; - u8 base_port; - int offset; + enum mlxsw_reg_pmtdb_status status; + char pmtdb_pl[MLXSW_REG_PMTDB_LEN]; int i; int err; @@ -2022,57 +2010,37 @@ static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port, return -EINVAL; } - max_width = mlxsw_core_module_max_width(mlxsw_core, - mlxsw_sp_port->mapping.module); - if (max_width < 0) { - netdev_err(mlxsw_sp_port->dev, "Cannot get max width of port module\n"); - NL_SET_ERR_MSG_MOD(extack, "Cannot get max width of port module"); - return max_width; - } - - /* Split port with non-max cannot be split. */ - if (mlxsw_sp_port->mapping.width != max_width) { - netdev_err(mlxsw_sp_port->dev, "Port cannot be split\n"); - NL_SET_ERR_MSG_MOD(extack, "Port cannot be split"); + if (mlxsw_sp_port->split) { + NL_SET_ERR_MSG_MOD(extack, "Port is already split"); return -EINVAL; } - offset = mlxsw_sp_local_ports_offset(mlxsw_core, count, max_width); - if (offset < 0) { - netdev_err(mlxsw_sp_port->dev, "Cannot obtain local port offset\n"); - NL_SET_ERR_MSG_MOD(extack, "Cannot obtain local port offset"); - return -EINVAL; + mlxsw_reg_pmtdb_pack(pmtdb_pl, 0, mlxsw_sp_port->mapping.module, + mlxsw_sp_port->mapping.module_width / count, + count); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(pmtdb), pmtdb_pl); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Failed to query split info"); + return err; } - /* Only in case max split is being done, the local port and - * base port may differ. - */ - base_port = count == max_width ? - mlxsw_sp_cluster_base_port_get(local_port, max_width) : - local_port; - - for (i = 0; i < count * offset; i++) { - /* Expect base port to exist and also the one in the middle in - * case of maximal split count. - */ - if (i == 0 || (count == max_width && i == count / 2)) - continue; - - if (mlxsw_sp_port_created(mlxsw_sp, base_port + i)) { - netdev_err(mlxsw_sp_port->dev, "Invalid split configuration\n"); - NL_SET_ERR_MSG_MOD(extack, "Invalid split configuration"); - return -EINVAL; - } + status = mlxsw_reg_pmtdb_status_get(pmtdb_pl); + if (status != MLXSW_REG_PMTDB_STATUS_SUCCESS) { + NL_SET_ERR_MSG_MOD(extack, "Unsupported split configuration"); + return -EINVAL; } port_mapping = mlxsw_sp_port->mapping; - for (i = 0; i < count; i++) - if (mlxsw_sp_port_created(mlxsw_sp, base_port + i * offset)) - mlxsw_sp_port_remove(mlxsw_sp, base_port + i * offset); + for (i = 0; i < count; i++) { + u8 s_local_port = mlxsw_reg_pmtdb_port_num_get(pmtdb_pl, i); - err = mlxsw_sp_port_split_create(mlxsw_sp, base_port, &port_mapping, - count, offset); + if (mlxsw_sp_port_created(mlxsw_sp, s_local_port)) + mlxsw_sp_port_remove(mlxsw_sp, s_local_port); + } + + err = mlxsw_sp_port_split_create(mlxsw_sp, &port_mapping, + count, pmtdb_pl); if (err) { dev_err(mlxsw_sp->bus_info->dev, "Failed to create split ports\n"); goto err_port_split_create; @@ -2081,7 +2049,7 @@ static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port, return 0; err_port_split_create: - mlxsw_sp_port_unsplit_create(mlxsw_sp, base_port, count, offset); + mlxsw_sp_port_unsplit_create(mlxsw_sp, count, pmtdb_pl); return err; } @@ -2090,11 +2058,10 @@ static int mlxsw_sp_port_unsplit(struct mlxsw_core *mlxsw_core, u8 local_port, { struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); struct mlxsw_sp_port *mlxsw_sp_port; + char pmtdb_pl[MLXSW_REG_PMTDB_LEN]; unsigned int count; - int max_width; - u8 base_port; - int offset; int i; + int err; mlxsw_sp_port = mlxsw_sp_port_get_by_local_port(mlxsw_sp, local_port); if (!mlxsw_sp_port) { @@ -2105,35 +2072,30 @@ static int mlxsw_sp_port_unsplit(struct mlxsw_core *mlxsw_core, u8 local_port, } if (!mlxsw_sp_port->split) { - netdev_err(mlxsw_sp_port->dev, "Port was not split\n"); NL_SET_ERR_MSG_MOD(extack, "Port was not split"); return -EINVAL; } - max_width = mlxsw_core_module_max_width(mlxsw_core, - mlxsw_sp_port->mapping.module); - if (max_width < 0) { - netdev_err(mlxsw_sp_port->dev, "Cannot get max width of port module\n"); - NL_SET_ERR_MSG_MOD(extack, "Cannot get max width of port module"); - return max_width; + count = mlxsw_sp_port->mapping.module_width / + mlxsw_sp_port->mapping.width; + + mlxsw_reg_pmtdb_pack(pmtdb_pl, 0, mlxsw_sp_port->mapping.module, + mlxsw_sp_port->mapping.module_width / count, + count); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(pmtdb), pmtdb_pl); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Failed to query split info"); + return err; } - count = max_width / mlxsw_sp_port->mapping.width; + for (i = 0; i < count; i++) { + u8 s_local_port = mlxsw_reg_pmtdb_port_num_get(pmtdb_pl, i); - offset = mlxsw_sp_local_ports_offset(mlxsw_core, count, max_width); - if (WARN_ON(offset < 0)) { - netdev_err(mlxsw_sp_port->dev, "Cannot obtain local port offset\n"); - NL_SET_ERR_MSG_MOD(extack, "Cannot obtain local port offset"); - return -EINVAL; + if (mlxsw_sp_port_created(mlxsw_sp, s_local_port)) + mlxsw_sp_port_remove(mlxsw_sp, s_local_port); } - base_port = mlxsw_sp_port->split_base_local_port; - - for (i = 0; i < count; i++) - if (mlxsw_sp_port_created(mlxsw_sp, base_port + i * offset)) - mlxsw_sp_port_remove(mlxsw_sp, base_port + i * offset); - - mlxsw_sp_port_unsplit_create(mlxsw_sp, base_port, count, offset); + mlxsw_sp_port_unsplit_create(mlxsw_sp, count, pmtdb_pl); return 0; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 3a43cba6d23c..83ab1ea92d31 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -144,7 +144,8 @@ struct mlxsw_sp_mall_entry; struct mlxsw_sp_port_mapping { u8 module; - u8 width; + u8 width; /* Number of lanes used by the port */ + u8 module_width; /* Number of lanes in the module (static) */ u8 lane; }; @@ -345,7 +346,6 @@ struct mlxsw_sp_port { u16 egr_types; struct mlxsw_sp_ptp_port_stats stats; } ptp; - u8 split_base_local_port; int max_mtu; u32 max_speed; struct mlxsw_sp_hdroom *hdroom; From cd92d79d5fdbdb62a9dff70470f2578707318125 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 14 Sep 2021 09:13:30 +0300 Subject: [PATCH 19/87] mlxsw: reg: Remove PMTM register It is not used anymore, remove it. Signed-off-by: Jiri Pirko Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 64 ----------------------- 1 file changed, 64 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 390e467050f3..8d87f3cc5711 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -5970,69 +5970,6 @@ static inline void mlxsw_reg_pllp_unpack(char *payload, u8 *label_port, *slot_index = mlxsw_reg_pllp_slot_index_get(payload); } -/* PMTM - Port Module Type Mapping Register - * ---------------------------------------- - * The PMTM allows query or configuration of module types. - */ -#define MLXSW_REG_PMTM_ID 0x5067 -#define MLXSW_REG_PMTM_LEN 0x10 - -MLXSW_REG_DEFINE(pmtm, MLXSW_REG_PMTM_ID, MLXSW_REG_PMTM_LEN); - -/* reg_pmtm_module - * Module number. - * Access: Index - */ -MLXSW_ITEM32(reg, pmtm, module, 0x00, 16, 8); - -enum mlxsw_reg_pmtm_module_type { - /* Backplane with 4 lanes */ - MLXSW_REG_PMTM_MODULE_TYPE_BP_4X, - /* QSFP */ - MLXSW_REG_PMTM_MODULE_TYPE_QSFP, - /* SFP */ - MLXSW_REG_PMTM_MODULE_TYPE_SFP, - /* Backplane with single lane */ - MLXSW_REG_PMTM_MODULE_TYPE_BP_1X = 4, - /* Backplane with two lane */ - MLXSW_REG_PMTM_MODULE_TYPE_BP_2X = 8, - /* Chip2Chip4x */ - MLXSW_REG_PMTM_MODULE_TYPE_C2C4X = 10, - /* Chip2Chip2x */ - MLXSW_REG_PMTM_MODULE_TYPE_C2C2X, - /* Chip2Chip1x */ - MLXSW_REG_PMTM_MODULE_TYPE_C2C1X, - /* QSFP-DD */ - MLXSW_REG_PMTM_MODULE_TYPE_QSFP_DD = 14, - /* OSFP */ - MLXSW_REG_PMTM_MODULE_TYPE_OSFP, - /* SFP-DD */ - MLXSW_REG_PMTM_MODULE_TYPE_SFP_DD, - /* DSFP */ - MLXSW_REG_PMTM_MODULE_TYPE_DSFP, - /* Chip2Chip8x */ - MLXSW_REG_PMTM_MODULE_TYPE_C2C8X, -}; - -/* reg_pmtm_module_type - * Module type. - * Access: RW - */ -MLXSW_ITEM32(reg, pmtm, module_type, 0x04, 0, 4); - -static inline void mlxsw_reg_pmtm_pack(char *payload, u8 module) -{ - MLXSW_REG_ZERO(pmtm, payload); - mlxsw_reg_pmtm_module_set(payload, module); -} - -static inline void -mlxsw_reg_pmtm_unpack(char *payload, - enum mlxsw_reg_pmtm_module_type *module_type) -{ - *module_type = mlxsw_reg_pmtm_module_type_get(payload); -} - /* HTGT - Host Trap Group Table * ---------------------------- * Configures the properties for forwarding to CPU. @@ -12314,7 +12251,6 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { MLXSW_REG(pmpe), MLXSW_REG(pddr), MLXSW_REG(pllp), - MLXSW_REG(pmtm), MLXSW_REG(htgt), MLXSW_REG(hpkt), MLXSW_REG(rgcr), From a962cc4ba1a10fa7285137d63f551a634a6d83bc Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 14 Sep 2021 10:33:17 +0200 Subject: [PATCH 20/87] s390/ctcm: remove incorrect kernel doc indicators Many comments above functions start with a kernel doc indicator, but the comments are not using kernel doc style. Get rid of the warnings by simply removing the indicator. E.g.: drivers/s390/net/ctcm_main.c:979: warning: This comment starts with '/**', but isn't a kernel-doc comment. Acked-by: Alexandra Winter Signed-off-by: Heiko Carstens Signed-off-by: Karsten Graul Signed-off-by: David S. Miller --- drivers/s390/net/ctcm_fsms.c | 60 ++++++++++++++++++------------------ drivers/s390/net/ctcm_main.c | 38 +++++++++++------------ drivers/s390/net/ctcm_mpc.c | 8 ++--- 3 files changed, 53 insertions(+), 53 deletions(-) diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c index 06281a0a0552..de2423c72b02 100644 --- a/drivers/s390/net/ctcm_fsms.c +++ b/drivers/s390/net/ctcm_fsms.c @@ -182,7 +182,7 @@ static void ctcmpc_chx_attnbusy(fsm_instance *, int, void *); static void ctcmpc_chx_resend(fsm_instance *, int, void *); static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg); -/** +/* * Check return code of a preceding ccw_device call, halt_IO etc... * * ch : The channel, the error belongs to. @@ -223,7 +223,7 @@ void ctcm_purge_skb_queue(struct sk_buff_head *q) } } -/** +/* * NOP action for statemachines */ static void ctcm_action_nop(fsm_instance *fi, int event, void *arg) @@ -234,7 +234,7 @@ static void ctcm_action_nop(fsm_instance *fi, int event, void *arg) * Actions for channel - statemachines. */ -/** +/* * Normal data has been send. Free the corresponding * skb (it's in io_queue), reset dev->tbusy and * revert to idle state. @@ -322,7 +322,7 @@ static void chx_txdone(fsm_instance *fi, int event, void *arg) ctcm_clear_busy_do(dev); } -/** +/* * Initial data is sent. * Notify device statemachine that we are up and * running. @@ -344,7 +344,7 @@ void ctcm_chx_txidle(fsm_instance *fi, int event, void *arg) fsm_event(priv->fsm, DEV_EVENT_TXUP, ch->netdev); } -/** +/* * Got normal data, check for sanity, queue it up, allocate new buffer * trigger bottom half, and initiate next read. * @@ -421,7 +421,7 @@ static void chx_rx(fsm_instance *fi, int event, void *arg) ctcm_ccw_check_rc(ch, rc, "normal RX"); } -/** +/* * Initialize connection by sending a __u16 of value 0. * * fi An instance of a channel statemachine. @@ -497,7 +497,7 @@ static void chx_firstio(fsm_instance *fi, int event, void *arg) } } -/** +/* * Got initial data, check it. If OK, * notify device statemachine that we are up and * running. @@ -538,7 +538,7 @@ static void chx_rxidle(fsm_instance *fi, int event, void *arg) } } -/** +/* * Set channel into extended mode. * * fi An instance of a channel statemachine. @@ -578,7 +578,7 @@ static void ctcm_chx_setmode(fsm_instance *fi, int event, void *arg) ch->retry = 0; } -/** +/* * Setup channel. * * fi An instance of a channel statemachine. @@ -641,7 +641,7 @@ static void ctcm_chx_start(fsm_instance *fi, int event, void *arg) } } -/** +/* * Shutdown a channel. * * fi An instance of a channel statemachine. @@ -682,7 +682,7 @@ static void ctcm_chx_haltio(fsm_instance *fi, int event, void *arg) } } -/** +/* * Cleanup helper for chx_fail and chx_stopped * cleanup channels queue and notify interface statemachine. * @@ -728,7 +728,7 @@ static void ctcm_chx_cleanup(fsm_instance *fi, int state, } } -/** +/* * A channel has successfully been halted. * Cleanup it's queue and notify interface statemachine. * @@ -741,7 +741,7 @@ static void ctcm_chx_stopped(fsm_instance *fi, int event, void *arg) ctcm_chx_cleanup(fi, CTC_STATE_STOPPED, arg); } -/** +/* * A stop command from device statemachine arrived and we are in * not operational mode. Set state to stopped. * @@ -754,7 +754,7 @@ static void ctcm_chx_stop(fsm_instance *fi, int event, void *arg) fsm_newstate(fi, CTC_STATE_STOPPED); } -/** +/* * A machine check for no path, not operational status or gone device has * happened. * Cleanup queue and notify interface statemachine. @@ -768,7 +768,7 @@ static void ctcm_chx_fail(fsm_instance *fi, int event, void *arg) ctcm_chx_cleanup(fi, CTC_STATE_NOTOP, arg); } -/** +/* * Handle error during setup of channel. * * fi An instance of a channel statemachine. @@ -817,7 +817,7 @@ static void ctcm_chx_setuperr(fsm_instance *fi, int event, void *arg) } } -/** +/* * Restart a channel after an error. * * fi An instance of a channel statemachine. @@ -858,7 +858,7 @@ static void ctcm_chx_restart(fsm_instance *fi, int event, void *arg) } } -/** +/* * Handle error during RX initial handshake (exchange of * 0-length block header) * @@ -893,7 +893,7 @@ static void ctcm_chx_rxiniterr(fsm_instance *fi, int event, void *arg) } } -/** +/* * Notify device statemachine if we gave up initialization * of RX channel. * @@ -914,7 +914,7 @@ static void ctcm_chx_rxinitfail(fsm_instance *fi, int event, void *arg) fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev); } -/** +/* * Handle RX Unit check remote reset (remote disconnected) * * fi An instance of a channel statemachine. @@ -946,7 +946,7 @@ static void ctcm_chx_rxdisc(fsm_instance *fi, int event, void *arg) ccw_device_halt(ch2->cdev, 0); } -/** +/* * Handle error during TX channel initialization. * * fi An instance of a channel statemachine. @@ -978,7 +978,7 @@ static void ctcm_chx_txiniterr(fsm_instance *fi, int event, void *arg) } } -/** +/* * Handle TX timeout by retrying operation. * * fi An instance of a channel statemachine. @@ -1050,7 +1050,7 @@ done: return; } -/** +/* * Handle fatal errors during an I/O command. * * fi An instance of a channel statemachine. @@ -1198,7 +1198,7 @@ int ch_fsm_len = ARRAY_SIZE(ch_fsm); * Actions for mpc channel statemachine. */ -/** +/* * Normal data has been send. Free the corresponding * skb (it's in io_queue), reset dev->tbusy and * revert to idle state. @@ -1361,7 +1361,7 @@ done: return; } -/** +/* * Got normal data, check for sanity, queue it up, allocate new buffer * trigger bottom half, and initiate next read. * @@ -1464,7 +1464,7 @@ again: } -/** +/* * Initialize connection by sending a __u16 of value 0. * * fi An instance of a channel statemachine. @@ -1516,7 +1516,7 @@ done: return; } -/** +/* * Got initial data, check it. If OK, * notify device statemachine that we are up and * running. @@ -2043,7 +2043,7 @@ int mpc_ch_fsm_len = ARRAY_SIZE(ctcmpc_ch_fsm); * Actions for interface - statemachine. */ -/** +/* * Startup channels by sending CTC_EVENT_START to each channel. * * fi An instance of an interface statemachine. @@ -2068,7 +2068,7 @@ static void dev_action_start(fsm_instance *fi, int event, void *arg) } } -/** +/* * Shutdown channels by sending CTC_EVENT_STOP to each channel. * * fi An instance of an interface statemachine. @@ -2122,7 +2122,7 @@ static void dev_action_restart(fsm_instance *fi, int event, void *arg) DEV_EVENT_START, dev); } -/** +/* * Called from channel statemachine * when a channel is up and running. * @@ -2183,7 +2183,7 @@ static void dev_action_chup(fsm_instance *fi, int event, void *arg) } } -/** +/* * Called from device statemachine * when a channel has been shutdown. * diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index fd705429708e..5ea7eeb07002 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -55,7 +55,7 @@ /* Some common global variables */ -/** +/* * The root device for ctcm group devices */ static struct device *ctcm_root_dev; @@ -65,7 +65,7 @@ static struct device *ctcm_root_dev; */ struct channel *channels; -/** +/* * Unpack a just received skb and hand it over to * upper layers. * @@ -180,7 +180,7 @@ void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb) } } -/** +/* * Release a specific channel in the channel list. * * ch Pointer to channel struct to be released. @@ -192,7 +192,7 @@ static void channel_free(struct channel *ch) fsm_newstate(ch->fsm, CTC_STATE_IDLE); } -/** +/* * Remove a specific channel in the channel list. * * ch Pointer to channel struct to be released. @@ -240,7 +240,7 @@ static void channel_remove(struct channel *ch) chid, ok ? "OK" : "failed"); } -/** +/* * Get a specific channel from the channel list. * * type Type of channel we are interested in. @@ -300,7 +300,7 @@ static long ctcm_check_irb_error(struct ccw_device *cdev, struct irb *irb) } -/** +/* * Check sense of a unit check. * * ch The channel, the sense code belongs to. @@ -414,7 +414,7 @@ int ctcm_ch_alloc_buffer(struct channel *ch) * Interface API for upper network layers */ -/** +/* * Open an interface. * Called from generic network layer when ifconfig up is run. * @@ -432,7 +432,7 @@ int ctcm_open(struct net_device *dev) return 0; } -/** +/* * Close an interface. * Called from generic network layer when ifconfig down is run. * @@ -451,7 +451,7 @@ int ctcm_close(struct net_device *dev) } -/** +/* * Transmit a packet. * This is a helper function for ctcm_tx(). * @@ -822,7 +822,7 @@ done: return rc; } -/** +/* * Start transmission of a packet. * Called from generic network device layer. * @@ -975,7 +975,7 @@ done: } -/** +/* * Sets MTU of an interface. * * dev Pointer to interface struct. @@ -1007,7 +1007,7 @@ static int ctcm_change_mtu(struct net_device *dev, int new_mtu) return 0; } -/** +/* * Returns interface statistics of a device. * * dev Pointer to interface struct. @@ -1144,7 +1144,7 @@ static struct net_device *ctcm_init_netdevice(struct ctcm_priv *priv) return dev; } -/** +/* * Main IRQ handler. * * cdev The ccw_device the interrupt is for. @@ -1257,7 +1257,7 @@ static const struct device_type ctcm_devtype = { .groups = ctcm_attr_groups, }; -/** +/* * Add ctcm specific attributes. * Add ctcm private data. * @@ -1293,7 +1293,7 @@ static int ctcm_probe_device(struct ccwgroup_device *cgdev) return 0; } -/** +/* * Add a new channel to the list of channels. * Keeps the channel list sorted. * @@ -1343,7 +1343,7 @@ static int add_channel(struct ccw_device *cdev, enum ctcm_channel_types type, snprintf(ch->id, CTCM_ID_SIZE, "ch-%s", dev_name(&cdev->dev)); ch->type = type; - /** + /* * "static" ccws are used in the following way: * * ccw[0..2] (Channel program for generic I/O): @@ -1471,7 +1471,7 @@ static enum ctcm_channel_types get_channel_type(struct ccw_device_id *id) return type; } -/** +/* * * Setup an interface. * @@ -1595,7 +1595,7 @@ out_err_result: return result; } -/** +/* * Shutdown an interface. * * cgdev Device to be shut down. @@ -1738,7 +1738,7 @@ static void print_banner(void) pr_info("CTCM driver initialized\n"); } -/** +/* * Initialize module. * This is called just after the module is loaded. * diff --git a/drivers/s390/net/ctcm_mpc.c b/drivers/s390/net/ctcm_mpc.c index f0436f555c62..88abfb5e8045 100644 --- a/drivers/s390/net/ctcm_mpc.c +++ b/drivers/s390/net/ctcm_mpc.c @@ -1016,7 +1016,7 @@ done: CTCM_PR_DEBUG("exit %s: ch=0x%p id=%s\n", __func__, ch, ch->id); } -/** +/* * Unpack a just received skb and hand it over to * upper layers. * special MPC version of unpack_skb. @@ -1211,7 +1211,7 @@ done: __func__, dev->name, ch, ch->id); } -/** +/* * tasklet helper for mpc's skb unpacking. * * ch The channel to work on. @@ -1320,7 +1320,7 @@ struct mpc_group *ctcmpc_init_mpc_group(struct ctcm_priv *priv) * CTCM_PROTO_MPC only */ -/** +/* * NOP action for statemachines */ static void mpc_action_nop(fsm_instance *fi, int event, void *arg) @@ -1426,7 +1426,7 @@ static void mpc_action_go_inop(fsm_instance *fi, int event, void *arg) } } -/** +/* * Handle mpc group action timeout. * MPC Group Station FSM action * CTCM_PROTO_MPC only From 239686c11f6acbb35c5c74fe2a3d172f22fcac70 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 14 Sep 2021 10:33:18 +0200 Subject: [PATCH 21/87] s390/lcs: remove incorrect kernel doc indicators Many comments above functions start with a kernel doc indicator, but the comments are not using kernel doc style. Get rid of the warnings by simply removing the indicator. E.g.: drivers/s390/net/lcs.c:2355: warning: This comment starts with '/**', but isn't a kernel-doc comment. Acked-by: Alexandra Winter Signed-off-by: Heiko Carstens Signed-off-by: Karsten Graul Signed-off-by: David S. Miller --- drivers/s390/net/lcs.c | 121 +++++++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 60 deletions(-) diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index 440219bcaa2b..c18fd48e02b6 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -40,18 +40,18 @@ #error Cannot compile lcs.c without some net devices switched on. #endif -/** +/* * initialization string for output */ static char version[] __initdata = "LCS driver"; -/** +/* * the root device for lcs group devices */ static struct device *lcs_root_dev; -/** +/* * Some prototypes. */ static void lcs_tasklet(unsigned long); @@ -62,14 +62,14 @@ static int lcs_send_delipm(struct lcs_card *, struct lcs_ipm_list *); #endif /* CONFIG_IP_MULTICAST */ static int lcs_recovery(void *ptr); -/** +/* * Debug Facility Stuff */ static char debug_buffer[255]; static debug_info_t *lcs_dbf_setup; static debug_info_t *lcs_dbf_trace; -/** +/* * LCS Debug Facility functions */ static void @@ -96,7 +96,7 @@ lcs_register_debug_facility(void) return 0; } -/** +/* * Allocate io buffers. */ static int @@ -123,7 +123,7 @@ lcs_alloc_channel(struct lcs_channel *channel) return 0; } -/** +/* * Free io buffers. */ static void @@ -151,7 +151,7 @@ lcs_cleanup_channel(struct lcs_channel *channel) lcs_free_channel(channel); } -/** +/* * LCS free memory for card and channels. */ static void @@ -162,7 +162,7 @@ lcs_free_card(struct lcs_card *card) kfree(card); } -/** +/* * LCS alloc memory for card and channels */ static struct lcs_card * @@ -402,7 +402,7 @@ lcs_do_start_thread(struct lcs_card *card, unsigned long thread) return rc; } -/** +/* * Initialize channels,card and state machines. */ static void @@ -451,7 +451,8 @@ static void lcs_clear_multicast_list(struct lcs_card *card) spin_unlock_irqrestore(&card->ipm_lock, flags); #endif } -/** + +/* * Cleanup channels,card and state machines. */ static void @@ -468,7 +469,7 @@ lcs_cleanup_card(struct lcs_card *card) lcs_cleanup_channel(&card->read); } -/** +/* * Start channel. */ static int @@ -517,7 +518,7 @@ lcs_clear_channel(struct lcs_channel *channel) } -/** +/* * Stop channel. */ static int @@ -545,7 +546,7 @@ lcs_stop_channel(struct lcs_channel *channel) return 0; } -/** +/* * start read and write channel */ static int @@ -565,7 +566,7 @@ lcs_start_channels(struct lcs_card *card) return rc; } -/** +/* * stop read and write channel */ static int @@ -577,7 +578,7 @@ lcs_stop_channels(struct lcs_card *card) return 0; } -/** +/* * Get empty buffer. */ static struct lcs_buffer * @@ -610,7 +611,7 @@ lcs_get_buffer(struct lcs_channel *channel) return buffer; } -/** +/* * Resume channel program if the channel is suspended. */ static int @@ -636,7 +637,7 @@ __lcs_resume_channel(struct lcs_channel *channel) } -/** +/* * Make a buffer ready for processing. */ static void __lcs_ready_buffer_bits(struct lcs_channel *channel, int index) @@ -678,7 +679,7 @@ lcs_ready_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer) return rc; } -/** +/* * Mark the buffer as processed. Take care of the suspend bit * of the previous buffer. This function is called from * interrupt context, so the lock must not be taken. @@ -712,7 +713,7 @@ __lcs_processed_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer) return __lcs_resume_channel(channel); } -/** +/* * Put a processed buffer back to state empty. */ static void @@ -728,7 +729,7 @@ lcs_release_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer) spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); } -/** +/* * Get buffer for a lan command. */ static struct lcs_buffer * @@ -785,7 +786,7 @@ lcs_alloc_reply(struct lcs_cmd *cmd) return reply; } -/** +/* * Notifier function for lancmd replies. Called from read irq. */ static void @@ -813,7 +814,7 @@ lcs_notify_lancmd_waiters(struct lcs_card *card, struct lcs_cmd *cmd) spin_unlock(&card->lock); } -/** +/* * Emit buffer of a lan command. */ static void @@ -877,7 +878,7 @@ lcs_send_lancmd(struct lcs_card *card, struct lcs_buffer *buffer, return rc ? -EIO : 0; } -/** +/* * LCS startup command */ static int @@ -895,7 +896,7 @@ lcs_send_startup(struct lcs_card *card, __u8 initiator) return lcs_send_lancmd(card, buffer, NULL); } -/** +/* * LCS shutdown command */ static int @@ -912,7 +913,7 @@ lcs_send_shutdown(struct lcs_card *card) return lcs_send_lancmd(card, buffer, NULL); } -/** +/* * LCS lanstat command */ static void @@ -939,7 +940,7 @@ lcs_send_lanstat(struct lcs_card *card) return lcs_send_lancmd(card, buffer, __lcs_lanstat_cb); } -/** +/* * send stoplan command */ static int @@ -958,7 +959,7 @@ lcs_send_stoplan(struct lcs_card *card, __u8 initiator) return lcs_send_lancmd(card, buffer, NULL); } -/** +/* * send startlan command */ static void @@ -986,7 +987,7 @@ lcs_send_startlan(struct lcs_card *card, __u8 initiator) } #ifdef CONFIG_IP_MULTICAST -/** +/* * send setipm command (Multicast) */ static int @@ -1010,7 +1011,7 @@ lcs_send_setipm(struct lcs_card *card,struct lcs_ipm_list *ipm_list) return lcs_send_lancmd(card, buffer, NULL); } -/** +/* * send delipm command (Multicast) */ static int @@ -1034,7 +1035,7 @@ lcs_send_delipm(struct lcs_card *card,struct lcs_ipm_list *ipm_list) return lcs_send_lancmd(card, buffer, NULL); } -/** +/* * check if multicast is supported by LCS */ static void @@ -1074,7 +1075,7 @@ lcs_check_multicast_support(struct lcs_card *card) return -EOPNOTSUPP; } -/** +/* * set or del multicast address on LCS card */ static void @@ -1129,7 +1130,7 @@ list_modified: spin_unlock_irqrestore(&card->ipm_lock, flags); } -/** +/* * get mac address for the relevant Multicast address */ static void @@ -1139,7 +1140,7 @@ lcs_get_mac_for_ipm(__be32 ipm, char *mac, struct net_device *dev) ip_eth_mc_map(ipm, mac); } -/** +/* * function called by net device to handle multicast address relevant things */ static void lcs_remove_mc_addresses(struct lcs_card *card, @@ -1260,7 +1261,7 @@ out: } #endif /* CONFIG_IP_MULTICAST */ -/** +/* * function called by net device to * handle multicast address relevant things */ @@ -1355,7 +1356,7 @@ lcs_schedule_recovery(struct lcs_card *card) schedule_work(&card->kernel_thread_starter); } -/** +/* * IRQ Handler for LCS channels */ static void @@ -1439,7 +1440,7 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) tasklet_schedule(&channel->irq_tasklet); } -/** +/* * Tasklet for IRQ handler */ static void @@ -1476,7 +1477,7 @@ lcs_tasklet(unsigned long data) wake_up(&channel->wait_q); } -/** +/* * Finish current tx buffer and make it ready for transmit. */ static void @@ -1490,7 +1491,7 @@ __lcs_emit_txbuffer(struct lcs_card *card) card->tx_emitted++; } -/** +/* * Callback for finished tx buffers. */ static void @@ -1515,7 +1516,7 @@ lcs_txbuffer_cb(struct lcs_channel *channel, struct lcs_buffer *buffer) spin_unlock(&card->lock); } -/** +/* * Packet transmit function called by network stack */ static int @@ -1593,7 +1594,7 @@ lcs_start_xmit(struct sk_buff *skb, struct net_device *dev) return rc; } -/** +/* * send startlan and lanstat command to make LCS device ready */ static int @@ -1648,7 +1649,7 @@ lcs_startlan(struct lcs_card *card) return rc; } -/** +/* * LCS detect function * setup channels and make them I/O ready */ @@ -1680,7 +1681,7 @@ lcs_detect(struct lcs_card *card) return rc; } -/** +/* * LCS Stop card */ static int @@ -1705,7 +1706,7 @@ lcs_stopcard(struct lcs_card *card) return rc; } -/** +/* * Kernel Thread helper functions for LGW initiated commands */ static void @@ -1721,7 +1722,7 @@ lcs_start_kernel_thread(struct work_struct *work) #endif } -/** +/* * Process control frames. */ static void @@ -1748,7 +1749,7 @@ lcs_get_control(struct lcs_card *card, struct lcs_cmd *cmd) lcs_notify_lancmd_waiters(card, cmd); } -/** +/* * Unpack network packet. */ static void @@ -1779,7 +1780,7 @@ lcs_get_skb(struct lcs_card *card, char *skb_data, unsigned int skb_len) netif_rx(skb); } -/** +/* * LCS main routine to get packets and lancmd replies from the buffers */ static void @@ -1829,7 +1830,7 @@ lcs_get_frames_cb(struct lcs_channel *channel, struct lcs_buffer *buffer) lcs_ready_buffer(&card->read, buffer); } -/** +/* * get network statistics for ifconfig and other user programs */ static struct net_device_stats * @@ -1842,7 +1843,7 @@ lcs_getstats(struct net_device *dev) return &card->stats; } -/** +/* * stop lcs device * This function will be called by user doing ifconfig xxx down */ @@ -1866,7 +1867,7 @@ lcs_stop_device(struct net_device *dev) return rc; } -/** +/* * start lcs device and make it runnable * This function will be called by user doing ifconfig xxx up */ @@ -1892,7 +1893,7 @@ lcs_open_device(struct net_device *dev) return rc; } -/** +/* * show function for portno called by cat or similar things */ static ssize_t @@ -1908,7 +1909,7 @@ lcs_portno_show (struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, "%d\n", card->portno); } -/** +/* * store the value which is piped to file portno */ static ssize_t @@ -2033,7 +2034,7 @@ static const struct device_type lcs_devtype = { .groups = lcs_attr_groups, }; -/** +/* * lcs_probe_device is called on establishing a new ccwgroup_device. */ static int @@ -2077,7 +2078,7 @@ lcs_register_netdev(struct ccwgroup_device *ccwgdev) return register_netdev(card->dev); } -/** +/* * lcs_new_device will be called by setting the group device online. */ static const struct net_device_ops lcs_netdev_ops = { @@ -2199,7 +2200,7 @@ out_err: return -ENODEV; } -/** +/* * lcs_shutdown_device, called when setting the group device offline. */ static int @@ -2240,7 +2241,7 @@ lcs_shutdown_device(struct ccwgroup_device *ccwgdev) return __lcs_shutdown_device(ccwgdev, 0); } -/** +/* * drive lcs recovery after startup and startlan initiated by Lan Gateway */ static int @@ -2271,7 +2272,7 @@ lcs_recovery(void *ptr) return 0; } -/** +/* * lcs_remove_device, free buffers and card */ static void @@ -2315,7 +2316,7 @@ static struct ccw_driver lcs_ccw_driver = { .int_class = IRQIO_LCS, }; -/** +/* * LCS ccwgroup driver registration */ static struct ccwgroup_driver lcs_group_driver = { @@ -2351,7 +2352,7 @@ static const struct attribute_group *lcs_drv_attr_groups[] = { NULL, }; -/** +/* * LCS Module/Kernel initialization function */ static int @@ -2389,7 +2390,7 @@ out_err: } -/** +/* * LCS module cleanup function */ static void From 478a31403b365d2f7b35a0cae8ee3e0594dc5bb1 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 14 Sep 2021 10:33:19 +0200 Subject: [PATCH 22/87] s390/netiucv: remove incorrect kernel doc indicators Many comments above functions start with a kernel doc indicator, but the comments are not using kernel doc style. Get rid of the warnings by simply removing the indicator. E.g.: drivers/s390/net/netiucv.c:1852: warning: This comment starts with '/**', but isn't a kernel-doc comment. Reviewed-by: Alexandra Winter Signed-off-by: Heiko Carstens Signed-off-by: Karsten Graul Signed-off-by: David S. Miller --- drivers/s390/net/fsm.c | 2 +- drivers/s390/net/netiucv.c | 104 ++++++++++++++++++------------------- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/drivers/s390/net/fsm.c b/drivers/s390/net/fsm.c index eb07862bd36a..98c4864932d2 100644 --- a/drivers/s390/net/fsm.c +++ b/drivers/s390/net/fsm.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * A generic FSM based on fsm used in isdn4linux * */ diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 5a0c2f07a3a2..981e7b1c6b96 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -58,7 +58,7 @@ MODULE_AUTHOR ("(C) 2001 IBM Corporation by Fritz Elfert (felfert@millenux.com)"); MODULE_DESCRIPTION ("Linux for S/390 IUCV network driver"); -/** +/* * Debug Facility stuff */ #define IUCV_DBF_SETUP_NAME "iucv_setup" @@ -107,7 +107,7 @@ DECLARE_PER_CPU(char[256], iucv_dbf_txt_buf); debug_sprintf_event(iucv_dbf_trace, level, text ); \ } while (0) -/** +/* * some more debug stuff */ #define PRINTK_HEADER " iucv: " /* for debugging */ @@ -118,7 +118,7 @@ static struct device_driver netiucv_driver = { .bus = &iucv_bus, }; -/** +/* * Per connection profiling data */ struct connection_profile { @@ -133,7 +133,7 @@ struct connection_profile { unsigned long tx_max_pending; }; -/** +/* * Representation of one iucv connection */ struct iucv_connection { @@ -154,13 +154,13 @@ struct iucv_connection { char userdata[17]; }; -/** +/* * Linked list of all connection structs. */ static LIST_HEAD(iucv_connection_list); static DEFINE_RWLOCK(iucv_connection_rwlock); -/** +/* * Representation of event-data for the * connection state machine. */ @@ -169,7 +169,7 @@ struct iucv_event { void *data; }; -/** +/* * Private part of the network device structure */ struct netiucv_priv { @@ -180,7 +180,7 @@ struct netiucv_priv { struct device *dev; }; -/** +/* * Link level header for a packet. */ struct ll_header { @@ -195,7 +195,7 @@ struct ll_header { #define NETIUCV_QUEUELEN_DEFAULT 50 #define NETIUCV_TIMEOUT_5SEC 5000 -/** +/* * Compatibility macros for busy handling * of network devices. */ @@ -223,7 +223,7 @@ static u8 iucvMagic_ebcdic[16] = { 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 }; -/** +/* * Convert an iucv userId to its printable * form (strip whitespace at end). * @@ -262,7 +262,7 @@ static char *netiucv_printuser(struct iucv_connection *conn) return netiucv_printname(conn->userid, 8); } -/** +/* * States of the interface statemachine. */ enum dev_states { @@ -270,7 +270,7 @@ enum dev_states { DEV_STATE_STARTWAIT, DEV_STATE_STOPWAIT, DEV_STATE_RUNNING, - /** + /* * MUST be always the last element!! */ NR_DEV_STATES @@ -283,7 +283,7 @@ static const char *dev_state_names[] = { "Running", }; -/** +/* * Events of the interface statemachine. */ enum dev_events { @@ -291,7 +291,7 @@ enum dev_events { DEV_EVENT_STOP, DEV_EVENT_CONUP, DEV_EVENT_CONDOWN, - /** + /* * MUST be always the last element!! */ NR_DEV_EVENTS @@ -304,11 +304,11 @@ static const char *dev_event_names[] = { "Connection down", }; -/** +/* * Events of the connection statemachine */ enum conn_events { - /** + /* * Events, representing callbacks from * lowlevel iucv layer) */ @@ -320,23 +320,23 @@ enum conn_events { CONN_EVENT_RX, CONN_EVENT_TXDONE, - /** + /* * Events, representing errors return codes from * calls to lowlevel iucv layer */ - /** + /* * Event, representing timer expiry. */ CONN_EVENT_TIMER, - /** + /* * Events, representing commands from upper levels. */ CONN_EVENT_START, CONN_EVENT_STOP, - /** + /* * MUST be always the last element!! */ NR_CONN_EVENTS, @@ -357,55 +357,55 @@ static const char *conn_event_names[] = { "Stop", }; -/** +/* * States of the connection statemachine. */ enum conn_states { - /** + /* * Connection not assigned to any device, * initial state, invalid */ CONN_STATE_INVALID, - /** + /* * Userid assigned but not operating */ CONN_STATE_STOPPED, - /** + /* * Connection registered, * no connection request sent yet, * no connection request received */ CONN_STATE_STARTWAIT, - /** + /* * Connection registered and connection request sent, * no acknowledge and no connection request received yet. */ CONN_STATE_SETUPWAIT, - /** + /* * Connection up and running idle */ CONN_STATE_IDLE, - /** + /* * Data sent, awaiting CONN_EVENT_TXDONE */ CONN_STATE_TX, - /** + /* * Error during registration. */ CONN_STATE_REGERR, - /** + /* * Error during registration. */ CONN_STATE_CONNERR, - /** + /* * MUST be always the last element!! */ NR_CONN_STATES, @@ -424,7 +424,7 @@ static const char *conn_state_names[] = { }; -/** +/* * Debug Facility Stuff */ static debug_info_t *iucv_dbf_setup = NULL; @@ -556,7 +556,7 @@ static void netiucv_callback_connres(struct iucv_path *path, u8 *ipuser) fsm_event(conn->fsm, CONN_EVENT_CONN_RES, conn); } -/** +/* * NOP action for statemachines */ static void netiucv_action_nop(fsm_instance *fi, int event, void *arg) @@ -567,7 +567,7 @@ static void netiucv_action_nop(fsm_instance *fi, int event, void *arg) * Actions of the connection statemachine */ -/** +/* * netiucv_unpack_skb * @conn: The connection where this skb has been received. * @pskb: The received skb. @@ -993,7 +993,7 @@ static const int CONN_FSM_LEN = sizeof(conn_fsm) / sizeof(fsm_node); * Actions for interface - statemachine. */ -/** +/* * dev_action_start * @fi: An instance of an interface statemachine. * @event: The event, just happened. @@ -1012,7 +1012,7 @@ static void dev_action_start(fsm_instance *fi, int event, void *arg) fsm_event(privptr->conn->fsm, CONN_EVENT_START, privptr->conn); } -/** +/* * Shutdown connection by sending CONN_EVENT_STOP to it. * * @param fi An instance of an interface statemachine. @@ -1034,7 +1034,7 @@ dev_action_stop(fsm_instance *fi, int event, void *arg) fsm_event(privptr->conn->fsm, CONN_EVENT_STOP, &ev); } -/** +/* * Called from connection statemachine * when a connection is up and running. * @@ -1067,7 +1067,7 @@ dev_action_connup(fsm_instance *fi, int event, void *arg) } } -/** +/* * Called from connection statemachine * when a connection has been shutdown. * @@ -1107,7 +1107,7 @@ static const fsm_node dev_fsm[] = { static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node); -/** +/* * Transmit a packet. * This is a helper function for netiucv_tx(). * @@ -1144,7 +1144,7 @@ static int netiucv_transmit_skb(struct iucv_connection *conn, spin_unlock_irqrestore(&conn->collect_lock, saveflags); } else { struct sk_buff *nskb = skb; - /** + /* * Copy the skb to a new allocated skb in lowmem only if the * data is located above 2G in memory or tailroom is < 2. */ @@ -1164,7 +1164,7 @@ static int netiucv_transmit_skb(struct iucv_connection *conn, } copied = 1; } - /** + /* * skb now is below 2G and has enough room. Add headers. */ header.next = nskb->len + NETIUCV_HDRLEN; @@ -1194,7 +1194,7 @@ static int netiucv_transmit_skb(struct iucv_connection *conn, if (copied) dev_kfree_skb(nskb); else { - /** + /* * Remove our headers. They get added * again on retransmit. */ @@ -1217,7 +1217,7 @@ static int netiucv_transmit_skb(struct iucv_connection *conn, * Interface API for upper network layers */ -/** +/* * Open an interface. * Called from generic network layer when ifconfig up is run. * @@ -1233,7 +1233,7 @@ static int netiucv_open(struct net_device *dev) return 0; } -/** +/* * Close an interface. * Called from generic network layer when ifconfig down is run. * @@ -1249,7 +1249,7 @@ static int netiucv_close(struct net_device *dev) return 0; } -/** +/* * Start transmission of a packet. * Called from generic network device layer. * @@ -1266,7 +1266,7 @@ static int netiucv_tx(struct sk_buff *skb, struct net_device *dev) int rc; IUCV_DBF_TEXT(trace, 4, __func__); - /** + /* * Some sanity checks ... */ if (skb == NULL) { @@ -1282,7 +1282,7 @@ static int netiucv_tx(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } - /** + /* * If connection is not running, try to restart it * and throw away packet. */ @@ -1304,7 +1304,7 @@ static int netiucv_tx(struct sk_buff *skb, struct net_device *dev) return rc ? NETDEV_TX_BUSY : NETDEV_TX_OK; } -/** +/* * netiucv_stats * @dev: Pointer to interface struct. * @@ -1745,7 +1745,7 @@ static void netiucv_unregister_device(struct device *dev) device_unregister(dev); } -/** +/* * Allocate and initialize a new connection structure. * Add it to the list of netiucv connections; */ @@ -1802,7 +1802,7 @@ out: return NULL; } -/** +/* * Release a connection structure and remove it from the * list of netiucv connections. */ @@ -1826,7 +1826,7 @@ static void netiucv_remove_connection(struct iucv_connection *conn) kfree_skb(conn->tx_buff); } -/** +/* * Release everything of a net device. */ static void netiucv_free_netdevice(struct net_device *dev) @@ -1848,7 +1848,7 @@ static void netiucv_free_netdevice(struct net_device *dev) } } -/** +/* * Initialize a net device. (Called from kernel in alloc_netdev()) */ static const struct net_device_ops netiucv_netdev_ops = { @@ -1873,7 +1873,7 @@ static void netiucv_setup_netdevice(struct net_device *dev) dev->netdev_ops = &netiucv_netdev_ops; } -/** +/* * Allocate and initialize everything of a net device. */ static struct net_device *netiucv_init_netdevice(char *username, char *userdata) From a1ac1b6e4137f5bb5cbb4a79456193c9ed1127d4 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Tue, 14 Sep 2021 10:33:20 +0200 Subject: [PATCH 23/87] s390/ism: switch from 'pci_' to 'dma_' API The wrappers in include/linux/pci-dma-compat.h should go away. The patch has been generated with the coccinelle script below. @@ expression e1, e2; @@ - pci_set_dma_mask(e1, e2) + dma_set_mask(&e1->dev, e2) Signed-off-by: Christophe JAILLET Signed-off-by: Karsten Graul Signed-off-by: David S. Miller --- drivers/s390/net/ism_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/net/ism_drv.c b/drivers/s390/net/ism_drv.c index 26cc943d2034..5f7e28de8b15 100644 --- a/drivers/s390/net/ism_drv.c +++ b/drivers/s390/net/ism_drv.c @@ -555,7 +555,7 @@ static int ism_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (ret) goto err_disable; - ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); if (ret) goto err_resource; From fa0866625543b4d8b3d026e4e0ef5ec25a453920 Mon Sep 17 00:00:00 2001 From: Karsten Graul Date: Tue, 14 Sep 2021 10:35:05 +0200 Subject: [PATCH 24/87] net/smc: add support for user defined EIDs SMC-Dv2 allows users to define EIDs which allows to create separate name spaces enabling users to cluster their SMC-Dv2 connections. Add support for user defined EIDs and extent the generic netlink interface so users can add, remove and dump EIDs. Signed-off-by: Karsten Graul Reviewed-by: Guvenc Gulce Signed-off-by: Guvenc Gulce Signed-off-by: David S. Miller --- include/uapi/linux/smc.h | 15 +++ net/smc/af_smc.c | 34 +++-- net/smc/smc.h | 3 - net/smc/smc_clc.c | 263 +++++++++++++++++++++++++++++++++++++-- net/smc/smc_clc.h | 16 ++- net/smc/smc_core.h | 1 + net/smc/smc_netlink.c | 32 ++++- net/smc/smc_netlink.h | 2 + 8 files changed, 335 insertions(+), 31 deletions(-) diff --git a/include/uapi/linux/smc.h b/include/uapi/linux/smc.h index 0f7f87c70baf..e3728af2832b 100644 --- a/include/uapi/linux/smc.h +++ b/include/uapi/linux/smc.h @@ -38,6 +38,9 @@ enum { /* SMC PNET Table commands */ #define SMC_GENL_FAMILY_VERSION 1 #define SMC_PCI_ID_STR_LEN 16 /* Max length of pci id string */ +#define SMC_MAX_HOSTNAME_LEN 32 /* Max length of the hostname */ +#define SMC_MAX_UEID 4 /* Max number of user EIDs */ +#define SMC_MAX_EID_LEN 32 /* Max length of an EID */ /* SMC_GENL_FAMILY commands */ enum { @@ -49,6 +52,10 @@ enum { SMC_NETLINK_GET_DEV_SMCR, SMC_NETLINK_GET_STATS, SMC_NETLINK_GET_FBACK_STATS, + SMC_NETLINK_DUMP_UEID, + SMC_NETLINK_ADD_UEID, + SMC_NETLINK_REMOVE_UEID, + SMC_NETLINK_FLUSH_UEID, }; /* SMC_GENL_FAMILY top level attributes */ @@ -242,4 +249,12 @@ enum { __SMC_NLA_FBACK_STATS_MAX, SMC_NLA_FBACK_STATS_MAX = __SMC_NLA_FBACK_STATS_MAX - 1 }; + +/* SMC_NETLINK_UEID attributes */ +enum { + SMC_NLA_EID_TABLE_UNSPEC, + SMC_NLA_EID_TABLE_ENTRY, /* string */ + __SMC_NLA_EID_TABLE_MAX, + SMC_NLA_EID_TABLE_MAX = __SMC_NLA_EID_TABLE_MAX - 1 +}; #endif /* _UAPI_LINUX_SMC_H */ diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index c038efc23ce3..e5d62acbe401 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -829,7 +829,7 @@ static int smc_connect_rdma(struct smc_sock *smc, smc_rmb_sync_sg_for_device(&smc->conn); reason_code = smc_clc_send_confirm(smc, ini->first_contact_local, - SMC_V1); + SMC_V1, NULL); if (reason_code) goto connect_abort; @@ -883,6 +883,7 @@ static int smc_connect_ism(struct smc_sock *smc, struct smc_clc_msg_accept_confirm *aclc, struct smc_init_info *ini) { + u8 *eid = NULL; int rc = 0; ini->is_smcd = true; @@ -918,8 +919,15 @@ static int smc_connect_ism(struct smc_sock *smc, smc_rx_init(smc); smc_tx_init(smc); + if (aclc->hdr.version > SMC_V1) { + struct smc_clc_msg_accept_confirm_v2 *clc_v2 = + (struct smc_clc_msg_accept_confirm_v2 *)aclc; + + eid = clc_v2->eid; + } + rc = smc_clc_send_confirm(smc, ini->first_contact_local, - aclc->hdr.version); + aclc->hdr.version, eid); if (rc) goto connect_abort; mutex_unlock(&smc_server_lgr_pending); @@ -1533,9 +1541,8 @@ static void smc_find_ism_v2_device_serv(struct smc_sock *new_smc, pclc_smcd = smc_get_clc_msg_smcd(pclc); smc_v2_ext = smc_get_clc_v2_ext(pclc); smcd_v2_ext = smc_get_clc_smcd_v2_ext(smc_v2_ext); - if (!smcd_v2_ext || - !smc_v2_ext->hdr.flag.seid) { /* no system EID support for SMCD */ - smc_find_ism_store_rc(SMC_CLC_DECL_NOSEID, ini); + if (!smcd_v2_ext) { + smc_find_ism_store_rc(SMC_CLC_DECL_NOV2DEXT, ini); goto not_found; } @@ -1555,13 +1562,13 @@ static void smc_find_ism_v2_device_serv(struct smc_sock *new_smc, } mutex_unlock(&smcd_dev_list.mutex); - if (ini->ism_dev[0]) { - smc_ism_get_system_eid(ini->ism_dev[0], &eid); - if (memcmp(eid, smcd_v2_ext->system_eid, SMC_MAX_EID_LEN)) - goto not_found; - } else { + if (!ini->ism_dev[0]) + goto not_found; + + smc_ism_get_system_eid(ini->ism_dev[0], &eid); + if (!smc_clc_match_eid(ini->negotiated_eid, smc_v2_ext, + smcd_v2_ext->system_eid, eid)) goto not_found; - } /* separate - outside the smcd_dev_list.lock */ smcd_version = ini->smcd_version; @@ -1579,6 +1586,7 @@ static void smc_find_ism_v2_device_serv(struct smc_sock *new_smc, } /* no V2 ISM device could be initialized */ ini->smcd_version = smcd_version; /* restore original value */ + ini->negotiated_eid[0] = 0; not_found: ini->smcd_version &= ~SMC_V2; @@ -1788,7 +1796,8 @@ static void smc_listen_work(struct work_struct *work) /* send SMC Accept CLC message */ rc = smc_clc_send_accept(new_smc, ini->first_contact_local, - ini->smcd_version == SMC_V2 ? SMC_V2 : SMC_V1); + ini->smcd_version == SMC_V2 ? SMC_V2 : SMC_V1, + ini->negotiated_eid); if (rc) goto out_unlock; @@ -2662,6 +2671,7 @@ static void __exit smc_exit(void) proto_unregister(&smc_proto); smc_pnet_exit(); smc_nl_exit(); + smc_clc_exit(); unregister_pernet_subsys(&smc_net_stat_ops); unregister_pernet_subsys(&smc_net_ops); rcu_barrier(); diff --git a/net/smc/smc.h b/net/smc/smc.h index d65e15f0c944..5e7def3ab730 100644 --- a/net/smc/smc.h +++ b/net/smc/smc.h @@ -29,9 +29,6 @@ * devices */ -#define SMC_MAX_HOSTNAME_LEN 32 -#define SMC_MAX_EID_LEN 32 - extern struct proto smc_proto; extern struct proto smc_proto6; diff --git a/net/smc/smc_clc.c b/net/smc/smc_clc.c index e286dafd6e88..a3d99f894f52 100644 --- a/net/smc/smc_clc.c +++ b/net/smc/smc_clc.c @@ -26,6 +26,7 @@ #include "smc_clc.h" #include "smc_ib.h" #include "smc_ism.h" +#include "smc_netlink.h" #define SMCR_CLC_ACCEPT_CONFIRM_LEN 68 #define SMCD_CLC_ACCEPT_CONFIRM_LEN 48 @@ -39,6 +40,223 @@ static const char SMCD_EYECATCHER[4] = {'\xe2', '\xd4', '\xc3', '\xc4'}; static u8 smc_hostname[SMC_MAX_HOSTNAME_LEN]; +struct smc_clc_eid_table { + rwlock_t lock; + struct list_head list; + u8 ueid_cnt; + u8 seid_enabled; +}; + +static struct smc_clc_eid_table smc_clc_eid_table; + +struct smc_clc_eid_entry { + struct list_head list; + u8 eid[SMC_MAX_EID_LEN]; +}; + +/* The size of a user EID is 32 characters. + * Valid characters should be (single-byte character set) A-Z, 0-9, '.' and '-'. + * Blanks should only be used to pad to the expected size. + * First character must be alphanumeric. + */ +static bool smc_clc_ueid_valid(char *ueid) +{ + char *end = ueid + SMC_MAX_EID_LEN; + + while (--end >= ueid && isspace(*end)) + ; + if (end < ueid) + return false; + if (!isalnum(*ueid) || islower(*ueid)) + return false; + while (ueid <= end) { + if ((!isalnum(*ueid) || islower(*ueid)) && *ueid != '.' && + *ueid != '-') + return false; + ueid++; + } + return true; +} + +static int smc_clc_ueid_add(char *ueid) +{ + struct smc_clc_eid_entry *new_ueid, *tmp_ueid; + int rc; + + if (!smc_clc_ueid_valid(ueid)) + return -EINVAL; + + /* add a new ueid entry to the ueid table if there isn't one */ + new_ueid = kzalloc(sizeof(*new_ueid), GFP_KERNEL); + if (!new_ueid) + return -ENOMEM; + memcpy(new_ueid->eid, ueid, SMC_MAX_EID_LEN); + + write_lock(&smc_clc_eid_table.lock); + if (smc_clc_eid_table.ueid_cnt >= SMC_MAX_UEID) { + rc = -ERANGE; + goto err_out; + } + list_for_each_entry(tmp_ueid, &smc_clc_eid_table.list, list) { + if (!memcmp(tmp_ueid->eid, ueid, SMC_MAX_EID_LEN)) { + rc = -EEXIST; + goto err_out; + } + } + list_add_tail(&new_ueid->list, &smc_clc_eid_table.list); + smc_clc_eid_table.ueid_cnt++; + write_unlock(&smc_clc_eid_table.lock); + return 0; + +err_out: + write_unlock(&smc_clc_eid_table.lock); + kfree(new_ueid); + return rc; +} + +int smc_nl_add_ueid(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr *nla_ueid = info->attrs[SMC_NLA_EID_TABLE_ENTRY]; + char *ueid; + + if (!nla_ueid || nla_len(nla_ueid) != SMC_MAX_EID_LEN + 1) + return -EINVAL; + ueid = (char *)nla_data(nla_ueid); + + return smc_clc_ueid_add(ueid); +} + +/* remove one or all ueid entries from the table */ +static int smc_clc_ueid_remove(char *ueid) +{ + struct smc_clc_eid_entry *lst_ueid, *tmp_ueid; + int rc = -ENOENT; + + /* remove table entry */ + write_lock(&smc_clc_eid_table.lock); + list_for_each_entry_safe(lst_ueid, tmp_ueid, &smc_clc_eid_table.list, + list) { + if (!ueid || !memcmp(lst_ueid->eid, ueid, SMC_MAX_EID_LEN)) { + list_del(&lst_ueid->list); + smc_clc_eid_table.ueid_cnt--; + kfree(lst_ueid); + rc = 0; + } + } + write_unlock(&smc_clc_eid_table.lock); + return rc; +} + +int smc_nl_remove_ueid(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr *nla_ueid = info->attrs[SMC_NLA_EID_TABLE_ENTRY]; + char *ueid; + + if (!nla_ueid || nla_len(nla_ueid) != SMC_MAX_EID_LEN + 1) + return -EINVAL; + ueid = (char *)nla_data(nla_ueid); + + return smc_clc_ueid_remove(ueid); +} + +int smc_nl_flush_ueid(struct sk_buff *skb, struct genl_info *info) +{ + smc_clc_ueid_remove(NULL); + return 0; +} + +static int smc_nl_ueid_dumpinfo(struct sk_buff *skb, u32 portid, u32 seq, + u32 flags, char *ueid) +{ + char ueid_str[SMC_MAX_EID_LEN + 1]; + void *hdr; + + hdr = genlmsg_put(skb, portid, seq, &smc_gen_nl_family, + flags, SMC_NETLINK_DUMP_UEID); + if (!hdr) + return -ENOMEM; + snprintf(ueid_str, sizeof(ueid_str), "%s", ueid); + if (nla_put_string(skb, SMC_NLA_EID_TABLE_ENTRY, ueid_str)) { + genlmsg_cancel(skb, hdr); + return -EMSGSIZE; + } + genlmsg_end(skb, hdr); + return 0; +} + +static int _smc_nl_ueid_dump(struct sk_buff *skb, u32 portid, u32 seq, + int start_idx) +{ + struct smc_clc_eid_entry *lst_ueid; + int idx = 0; + + read_lock(&smc_clc_eid_table.lock); + list_for_each_entry(lst_ueid, &smc_clc_eid_table.list, list) { + if (idx++ < start_idx) + continue; + if (smc_nl_ueid_dumpinfo(skb, portid, seq, NLM_F_MULTI, + lst_ueid->eid)) { + --idx; + break; + } + } + read_unlock(&smc_clc_eid_table.lock); + return idx; +} + +int smc_nl_dump_ueid(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct smc_nl_dmp_ctx *cb_ctx = smc_nl_dmp_ctx(cb); + int idx; + + idx = _smc_nl_ueid_dump(skb, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, cb_ctx->pos[0]); + + cb_ctx->pos[0] = idx; + return skb->len; +} + +static bool _smc_clc_match_ueid(u8 *peer_ueid) +{ + struct smc_clc_eid_entry *tmp_ueid; + + list_for_each_entry(tmp_ueid, &smc_clc_eid_table.list, list) { + if (!memcmp(tmp_ueid->eid, peer_ueid, SMC_MAX_EID_LEN)) + return true; + } + return false; +} + +bool smc_clc_match_eid(u8 *negotiated_eid, + struct smc_clc_v2_extension *smc_v2_ext, + u8 *peer_eid, u8 *local_eid) +{ + bool match = false; + int i; + + negotiated_eid[0] = 0; + read_lock(&smc_clc_eid_table.lock); + if (smc_clc_eid_table.seid_enabled && + smc_v2_ext->hdr.flag.seid && + !memcmp(peer_eid, local_eid, SMC_MAX_EID_LEN)) { + memcpy(negotiated_eid, peer_eid, SMC_MAX_EID_LEN); + match = true; + goto out; + } + + for (i = 0; i < smc_v2_ext->hdr.eid_cnt; i++) { + if (_smc_clc_match_ueid(smc_v2_ext->user_eids[i])) { + memcpy(negotiated_eid, smc_v2_ext->user_eids[i], + SMC_MAX_EID_LEN); + match = true; + goto out; + } + } +out: + read_unlock(&smc_clc_eid_table.lock); + return match; +} + /* check arriving CLC proposal */ static bool smc_clc_msg_prop_valid(struct smc_clc_msg_proposal *pclc) { @@ -550,6 +768,7 @@ int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini) if (ini->smc_type_v2 == SMC_TYPE_N) { pclc_smcd->v2_ext_offset = 0; } else { + struct smc_clc_eid_entry *ueident; u16 v2_ext_offset; u8 *eid = NULL; @@ -560,10 +779,19 @@ int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini) pclc_prfx->ipv6_prefixes_cnt * sizeof(ipv6_prfx[0]); pclc_smcd->v2_ext_offset = htons(v2_ext_offset); - v2_ext->hdr.eid_cnt = 0; + + read_lock(&smc_clc_eid_table.lock); + v2_ext->hdr.eid_cnt = smc_clc_eid_table.ueid_cnt; + plen += smc_clc_eid_table.ueid_cnt * SMC_MAX_EID_LEN; + i = 0; + list_for_each_entry(ueident, &smc_clc_eid_table.list, list) { + memcpy(v2_ext->user_eids[i++], ueident->eid, + sizeof(ueident->eid)); + } + v2_ext->hdr.flag.seid = smc_clc_eid_table.seid_enabled; + read_unlock(&smc_clc_eid_table.lock); v2_ext->hdr.ism_gid_cnt = ini->ism_offered_cnt; v2_ext->hdr.flag.release = SMC_RELEASE; - v2_ext->hdr.flag.seid = 1; v2_ext->hdr.smcd_v2_ext_offset = htons(sizeof(*v2_ext) - offsetofend(struct smc_clnt_opts_area_hdr, smcd_v2_ext_offset) + @@ -572,7 +800,7 @@ int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini) smc_ism_get_system_eid(ini->ism_dev[0], &eid); else smc_ism_get_system_eid(ini->ism_dev[1], &eid); - if (eid) + if (eid && v2_ext->hdr.flag.seid) memcpy(smcd_v2_ext->system_eid, eid, SMC_MAX_EID_LEN); plen += sizeof(*v2_ext) + sizeof(*smcd_v2_ext); if (ini->ism_offered_cnt) { @@ -607,7 +835,8 @@ int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini) } if (ini->smc_type_v2 != SMC_TYPE_N) { vec[i].iov_base = v2_ext; - vec[i++].iov_len = sizeof(*v2_ext); + vec[i++].iov_len = sizeof(*v2_ext) + + (v2_ext->hdr.eid_cnt * SMC_MAX_EID_LEN); vec[i].iov_base = smcd_v2_ext; vec[i++].iov_len = sizeof(*smcd_v2_ext); if (ini->ism_offered_cnt) { @@ -635,7 +864,8 @@ int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini) /* build and send CLC CONFIRM / ACCEPT message */ static int smc_clc_send_confirm_accept(struct smc_sock *smc, struct smc_clc_msg_accept_confirm_v2 *clc_v2, - int first_contact, u8 version) + int first_contact, u8 version, + u8 *eid) { struct smc_connection *conn = &smc->conn; struct smc_clc_msg_accept_confirm *clc; @@ -663,11 +893,8 @@ static int smc_clc_send_confirm_accept(struct smc_sock *smc, if (version == SMC_V1) { clc->hdr.length = htons(SMCD_CLC_ACCEPT_CONFIRM_LEN); } else { - u8 *eid = NULL; - clc_v2->chid = htons(smc_ism_get_chid(conn->lgr->smcd)); - smc_ism_get_system_eid(conn->lgr->smcd, &eid); - if (eid) + if (eid[0]) memcpy(clc_v2->eid, eid, SMC_MAX_EID_LEN); len = SMCD_CLC_ACCEPT_CONFIRM_LEN_V2; if (first_contact) @@ -732,7 +959,7 @@ static int smc_clc_send_confirm_accept(struct smc_sock *smc, /* send CLC CONFIRM message across internal TCP socket */ int smc_clc_send_confirm(struct smc_sock *smc, bool clnt_first_contact, - u8 version) + u8 version, u8 *eid) { struct smc_clc_msg_accept_confirm_v2 cclc_v2; int reason_code = 0; @@ -742,7 +969,7 @@ int smc_clc_send_confirm(struct smc_sock *smc, bool clnt_first_contact, memset(&cclc_v2, 0, sizeof(cclc_v2)); cclc_v2.hdr.type = SMC_CLC_CONFIRM; len = smc_clc_send_confirm_accept(smc, &cclc_v2, clnt_first_contact, - version); + version, eid); if (len < ntohs(cclc_v2.hdr.length)) { if (len >= 0) { reason_code = -ENETUNREACH; @@ -757,7 +984,7 @@ int smc_clc_send_confirm(struct smc_sock *smc, bool clnt_first_contact, /* send CLC ACCEPT message across internal TCP socket */ int smc_clc_send_accept(struct smc_sock *new_smc, bool srv_first_contact, - u8 version) + u8 version, u8 *negotiated_eid) { struct smc_clc_msg_accept_confirm_v2 aclc_v2; int len; @@ -765,7 +992,7 @@ int smc_clc_send_accept(struct smc_sock *new_smc, bool srv_first_contact, memset(&aclc_v2, 0, sizeof(aclc_v2)); aclc_v2.hdr.type = SMC_CLC_ACCEPT; len = smc_clc_send_confirm_accept(new_smc, &aclc_v2, srv_first_contact, - version); + version, negotiated_eid); if (len < ntohs(aclc_v2.hdr.length)) len = len >= 0 ? -EPROTO : -new_smc->clcsock->sk->sk_err; @@ -785,4 +1012,14 @@ void __init smc_clc_init(void) u = utsname(); memcpy(smc_hostname, u->nodename, min_t(size_t, strlen(u->nodename), sizeof(smc_hostname))); + + INIT_LIST_HEAD(&smc_clc_eid_table.list); + rwlock_init(&smc_clc_eid_table.lock); + smc_clc_eid_table.ueid_cnt = 0; + smc_clc_eid_table.seid_enabled = 1; +} + +void smc_clc_exit(void) +{ + smc_clc_ueid_remove(NULL); } diff --git a/net/smc/smc_clc.h b/net/smc/smc_clc.h index 32d37f7b70f2..0699e0cee308 100644 --- a/net/smc/smc_clc.h +++ b/net/smc/smc_clc.h @@ -14,8 +14,10 @@ #define _SMC_CLC_H #include +#include #include "smc.h" +#include "smc_netlink.h" #define SMC_CLC_PROPOSAL 0x01 #define SMC_CLC_ACCEPT 0x02 @@ -158,6 +160,7 @@ struct smc_clc_msg_proposal { /* clc proposal message sent by Linux */ } __aligned(4); #define SMC_CLC_MAX_V6_PREFIX 8 +#define SMC_CLC_MAX_UEID 8 struct smc_clc_msg_proposal_area { struct smc_clc_msg_proposal pclc_base; @@ -165,6 +168,7 @@ struct smc_clc_msg_proposal_area { struct smc_clc_msg_proposal_prefix pclc_prfx; struct smc_clc_ipv6_prefix pclc_prfx_ipv6[SMC_CLC_MAX_V6_PREFIX]; struct smc_clc_v2_extension pclc_v2_ext; + u8 user_eids[SMC_CLC_MAX_UEID][SMC_MAX_EID_LEN]; struct smc_clc_smcd_v2_extension pclc_smcd_v2_ext; struct smc_clc_smcd_gid_chid pclc_gidchids[SMC_MAX_ISM_DEVS]; struct smc_clc_msg_trail pclc_trl; @@ -330,10 +334,18 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen, int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info, u8 version); int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini); int smc_clc_send_confirm(struct smc_sock *smc, bool clnt_first_contact, - u8 version); + u8 version, u8 *eid); int smc_clc_send_accept(struct smc_sock *smc, bool srv_first_contact, - u8 version); + u8 version, u8 *negotiated_eid); void smc_clc_init(void) __init; +void smc_clc_exit(void); void smc_clc_get_hostname(u8 **host); +bool smc_clc_match_eid(u8 *negotiated_eid, + struct smc_clc_v2_extension *smc_v2_ext, + u8 *peer_eid, u8 *local_eid); +int smc_nl_dump_ueid(struct sk_buff *skb, struct netlink_callback *cb); +int smc_nl_add_ueid(struct sk_buff *skb, struct genl_info *info); +int smc_nl_remove_ueid(struct sk_buff *skb, struct genl_info *info); +int smc_nl_flush_ueid(struct sk_buff *skb, struct genl_info *info); #endif diff --git a/net/smc/smc_core.h b/net/smc/smc_core.h index c043ecdca5c4..83d30b06016f 100644 --- a/net/smc/smc_core.h +++ b/net/smc/smc_core.h @@ -310,6 +310,7 @@ struct smc_init_info { u8 first_contact_local; unsigned short vlan_id; u32 rc; + u8 negotiated_eid[SMC_MAX_EID_LEN]; /* SMC-R */ struct smc_clc_msg_local *ib_lcl; struct smc_ib_device *ib_dev; diff --git a/net/smc/smc_netlink.c b/net/smc/smc_netlink.c index 6fb6f96c1d17..4548ff2df245 100644 --- a/net/smc/smc_netlink.c +++ b/net/smc/smc_netlink.c @@ -19,11 +19,19 @@ #include "smc_core.h" #include "smc_ism.h" #include "smc_ib.h" +#include "smc_clc.h" #include "smc_stats.h" #include "smc_netlink.h" -#define SMC_CMD_MAX_ATTR 1 +const struct nla_policy +smc_gen_ueid_policy[SMC_NLA_EID_TABLE_MAX + 1] = { + [SMC_NLA_EID_TABLE_UNSPEC] = { .type = NLA_UNSPEC }, + [SMC_NLA_EID_TABLE_ENTRY] = { .type = NLA_STRING, + .len = SMC_MAX_EID_LEN, + }, +}; +#define SMC_CMD_MAX_ATTR 1 /* SMC_GENL generic netlink operation definition */ static const struct genl_ops smc_gen_nl_ops[] = { { @@ -66,6 +74,28 @@ static const struct genl_ops smc_gen_nl_ops[] = { /* can be retrieved by unprivileged users */ .dumpit = smc_nl_get_fback_stats, }, + { + .cmd = SMC_NETLINK_DUMP_UEID, + /* can be retrieved by unprivileged users */ + .dumpit = smc_nl_dump_ueid, + }, + { + .cmd = SMC_NETLINK_ADD_UEID, + .flags = GENL_ADMIN_PERM, + .doit = smc_nl_add_ueid, + .policy = smc_gen_ueid_policy, + }, + { + .cmd = SMC_NETLINK_REMOVE_UEID, + .flags = GENL_ADMIN_PERM, + .doit = smc_nl_remove_ueid, + .policy = smc_gen_ueid_policy, + }, + { + .cmd = SMC_NETLINK_FLUSH_UEID, + .flags = GENL_ADMIN_PERM, + .doit = smc_nl_flush_ueid, + }, }; static const struct nla_policy smc_gen_nl_policy[2] = { diff --git a/net/smc/smc_netlink.h b/net/smc/smc_netlink.h index 5ce2c0a89ccd..e8c6c3f0e98c 100644 --- a/net/smc/smc_netlink.h +++ b/net/smc/smc_netlink.h @@ -17,6 +17,8 @@ extern struct genl_family smc_gen_nl_family; +extern const struct nla_policy smc_gen_ueid_policy[]; + struct smc_nl_dmp_ctx { int pos[3]; }; From 11a26c59fc510091facd0d80236ac848da844830 Mon Sep 17 00:00:00 2001 From: Karsten Graul Date: Tue, 14 Sep 2021 10:35:06 +0200 Subject: [PATCH 25/87] net/smc: keep static copy of system EID The system EID is retrieved using an registered ISM device each time when needed. This adds some unnecessary complexity at all places where the system EID is needed, but no ISM device is at hand. Simplify the code and save the system EID in a static variable in smc_ism.c. Signed-off-by: Karsten Graul Reviewed-by: Guvenc Gulce Signed-off-by: Guvenc Gulce Signed-off-by: David S. Miller --- net/smc/af_smc.c | 2 +- net/smc/smc_clc.c | 5 +---- net/smc/smc_core.c | 10 ++-------- net/smc/smc_ism.c | 16 ++++++++++++---- net/smc/smc_ism.h | 2 +- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index e5d62acbe401..f69ef3f2019f 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -1565,7 +1565,7 @@ static void smc_find_ism_v2_device_serv(struct smc_sock *new_smc, if (!ini->ism_dev[0]) goto not_found; - smc_ism_get_system_eid(ini->ism_dev[0], &eid); + smc_ism_get_system_eid(&eid); if (!smc_clc_match_eid(ini->negotiated_eid, smc_v2_ext, smcd_v2_ext->system_eid, eid)) goto not_found; diff --git a/net/smc/smc_clc.c b/net/smc/smc_clc.c index a3d99f894f52..5a5ebf752860 100644 --- a/net/smc/smc_clc.c +++ b/net/smc/smc_clc.c @@ -796,10 +796,7 @@ int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini) offsetofend(struct smc_clnt_opts_area_hdr, smcd_v2_ext_offset) + v2_ext->hdr.eid_cnt * SMC_MAX_EID_LEN); - if (ini->ism_dev[0]) - smc_ism_get_system_eid(ini->ism_dev[0], &eid); - else - smc_ism_get_system_eid(ini->ism_dev[1], &eid); + smc_ism_get_system_eid(&eid); if (eid && v2_ext->hdr.flag.seid) memcpy(smcd_v2_ext->system_eid, eid, SMC_MAX_EID_LEN); plen += sizeof(*v2_ext) + sizeof(*smcd_v2_ext); diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index af227b65669e..4d0fcd8f8eba 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -223,7 +223,6 @@ int smc_nl_get_sys_info(struct sk_buff *skb, struct netlink_callback *cb) struct smc_nl_dmp_ctx *cb_ctx = smc_nl_dmp_ctx(cb); char hostname[SMC_MAX_HOSTNAME_LEN + 1]; char smc_seid[SMC_MAX_EID_LEN + 1]; - struct smcd_dev *smcd_dev; struct nlattr *attrs; u8 *seid = NULL; u8 *host = NULL; @@ -252,13 +251,8 @@ int smc_nl_get_sys_info(struct sk_buff *skb, struct netlink_callback *cb) if (nla_put_string(skb, SMC_NLA_SYS_LOCAL_HOST, hostname)) goto errattr; } - mutex_lock(&smcd_dev_list.mutex); - smcd_dev = list_first_entry_or_null(&smcd_dev_list.list, - struct smcd_dev, list); - if (smcd_dev) - smc_ism_get_system_eid(smcd_dev, &seid); - mutex_unlock(&smcd_dev_list.mutex); - if (seid && smc_ism_is_v2_capable()) { + if (smc_ism_is_v2_capable()) { + smc_ism_get_system_eid(&seid); memcpy(smc_seid, seid, SMC_MAX_EID_LEN); smc_seid[SMC_MAX_EID_LEN] = 0; if (nla_put_string(skb, SMC_NLA_SYS_SEID, smc_seid)) diff --git a/net/smc/smc_ism.c b/net/smc/smc_ism.c index 9cb2df289963..fd28cc498b98 100644 --- a/net/smc/smc_ism.c +++ b/net/smc/smc_ism.c @@ -23,6 +23,7 @@ struct smcd_dev_list smcd_dev_list = { }; static bool smc_ism_v2_capable; +static u8 smc_ism_v2_system_eid[SMC_MAX_EID_LEN]; /* Test if an ISM communication is possible - same CPC */ int smc_ism_cantalk(u64 peer_gid, unsigned short vlan_id, struct smcd_dev *smcd) @@ -42,9 +43,12 @@ int smc_ism_write(struct smcd_dev *smcd, const struct smc_ism_position *pos, return rc < 0 ? rc : 0; } -void smc_ism_get_system_eid(struct smcd_dev *smcd, u8 **eid) +void smc_ism_get_system_eid(u8 **eid) { - smcd->ops->get_system_eid(smcd, eid); + if (!smc_ism_v2_capable) + *eid = NULL; + else + *eid = smc_ism_v2_system_eid; } u16 smc_ism_get_chid(struct smcd_dev *smcd) @@ -435,9 +439,12 @@ int smcd_register_dev(struct smcd_dev *smcd) if (list_empty(&smcd_dev_list.list)) { u8 *system_eid = NULL; - smc_ism_get_system_eid(smcd, &system_eid); - if (system_eid[24] != '0' || system_eid[28] != '0') + smcd->ops->get_system_eid(smcd, &system_eid); + if (system_eid[24] != '0' || system_eid[28] != '0') { smc_ism_v2_capable = true; + memcpy(smc_ism_v2_system_eid, system_eid, + SMC_MAX_EID_LEN); + } } /* sort list: devices without pnetid before devices with pnetid */ if (smcd->pnetid[0]) @@ -533,4 +540,5 @@ EXPORT_SYMBOL_GPL(smcd_handle_irq); void __init smc_ism_init(void) { smc_ism_v2_capable = false; + memset(smc_ism_v2_system_eid, 0, SMC_MAX_EID_LEN); } diff --git a/net/smc/smc_ism.h b/net/smc/smc_ism.h index 113efc7352ed..004b22a13ffa 100644 --- a/net/smc/smc_ism.h +++ b/net/smc/smc_ism.h @@ -48,7 +48,7 @@ int smc_ism_unregister_dmb(struct smcd_dev *dev, struct smc_buf_desc *dmb_desc); int smc_ism_write(struct smcd_dev *dev, const struct smc_ism_position *pos, void *data, size_t len); int smc_ism_signal_shutdown(struct smc_link_group *lgr); -void smc_ism_get_system_eid(struct smcd_dev *dev, u8 **eid); +void smc_ism_get_system_eid(u8 **eid); u16 smc_ism_get_chid(struct smcd_dev *dev); bool smc_ism_is_v2_capable(void); void smc_ism_init(void); From 3c572145c24e21c24e1cd0fd168011eaba85da8e Mon Sep 17 00:00:00 2001 From: Karsten Graul Date: Tue, 14 Sep 2021 10:35:07 +0200 Subject: [PATCH 26/87] net/smc: add generic netlink support for system EID With SMC-Dv2 users can configure if the static system EID should be used during CLC handshake, or if only user EIDs are allowed. Add generic netlink support to enable and disable the system EID, and to retrieve the system EID and its current enabled state. Signed-off-by: Karsten Graul Reviewed-by: Guvenc Gulce Signed-off-by: Guvenc Gulce Signed-off-by: David S. Miller --- include/uapi/linux/smc.h | 12 ++++++++ net/smc/smc_clc.c | 62 ++++++++++++++++++++++++++++++++++++++++ net/smc/smc_clc.h | 3 ++ net/smc/smc_netlink.c | 15 ++++++++++ 4 files changed, 92 insertions(+) diff --git a/include/uapi/linux/smc.h b/include/uapi/linux/smc.h index e3728af2832b..b175bd0165a1 100644 --- a/include/uapi/linux/smc.h +++ b/include/uapi/linux/smc.h @@ -56,6 +56,9 @@ enum { SMC_NETLINK_ADD_UEID, SMC_NETLINK_REMOVE_UEID, SMC_NETLINK_FLUSH_UEID, + SMC_NETLINK_DUMP_SEID, + SMC_NETLINK_ENABLE_SEID, + SMC_NETLINK_DISABLE_SEID, }; /* SMC_GENL_FAMILY top level attributes */ @@ -257,4 +260,13 @@ enum { __SMC_NLA_EID_TABLE_MAX, SMC_NLA_EID_TABLE_MAX = __SMC_NLA_EID_TABLE_MAX - 1 }; + +/* SMC_NETLINK_SEID attributes */ +enum { + SMC_NLA_SEID_UNSPEC, + SMC_NLA_SEID_ENTRY, /* string */ + SMC_NLA_SEID_ENABLED, /* u8 */ + __SMC_NLA_SEID_TABLE_MAX, + SMC_NLA_SEID_TABLE_MAX = __SMC_NLA_SEID_TABLE_MAX - 1 +}; #endif /* _UAPI_LINUX_SMC_H */ diff --git a/net/smc/smc_clc.c b/net/smc/smc_clc.c index 5a5ebf752860..4afd9e71e5c2 100644 --- a/net/smc/smc_clc.c +++ b/net/smc/smc_clc.c @@ -143,6 +143,10 @@ static int smc_clc_ueid_remove(char *ueid) rc = 0; } } + if (!rc && !smc_clc_eid_table.ueid_cnt) { + smc_clc_eid_table.seid_enabled = 1; + rc = -EAGAIN; /* indicate success and enabling of seid */ + } write_unlock(&smc_clc_eid_table.lock); return rc; } @@ -216,6 +220,64 @@ int smc_nl_dump_ueid(struct sk_buff *skb, struct netlink_callback *cb) return skb->len; } +int smc_nl_dump_seid(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct smc_nl_dmp_ctx *cb_ctx = smc_nl_dmp_ctx(cb); + char seid_str[SMC_MAX_EID_LEN + 1]; + u8 seid_enabled; + void *hdr; + u8 *seid; + + if (cb_ctx->pos[0]) + return skb->len; + + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + &smc_gen_nl_family, NLM_F_MULTI, + SMC_NETLINK_DUMP_SEID); + if (!hdr) + return -ENOMEM; + if (!smc_ism_is_v2_capable()) + goto end; + + smc_ism_get_system_eid(&seid); + snprintf(seid_str, sizeof(seid_str), "%s", seid); + if (nla_put_string(skb, SMC_NLA_SEID_ENTRY, seid_str)) + goto err; + read_lock(&smc_clc_eid_table.lock); + seid_enabled = smc_clc_eid_table.seid_enabled; + read_unlock(&smc_clc_eid_table.lock); + if (nla_put_u8(skb, SMC_NLA_SEID_ENABLED, seid_enabled)) + goto err; +end: + genlmsg_end(skb, hdr); + cb_ctx->pos[0]++; + return skb->len; +err: + genlmsg_cancel(skb, hdr); + return -EMSGSIZE; +} + +int smc_nl_enable_seid(struct sk_buff *skb, struct genl_info *info) +{ + write_lock(&smc_clc_eid_table.lock); + smc_clc_eid_table.seid_enabled = 1; + write_unlock(&smc_clc_eid_table.lock); + return 0; +} + +int smc_nl_disable_seid(struct sk_buff *skb, struct genl_info *info) +{ + int rc = 0; + + write_lock(&smc_clc_eid_table.lock); + if (!smc_clc_eid_table.ueid_cnt) + rc = -ENOENT; + else + smc_clc_eid_table.seid_enabled = 0; + write_unlock(&smc_clc_eid_table.lock); + return rc; +} + static bool _smc_clc_match_ueid(u8 *peer_ueid) { struct smc_clc_eid_entry *tmp_ueid; diff --git a/net/smc/smc_clc.h b/net/smc/smc_clc.h index 0699e0cee308..974d01d16bb5 100644 --- a/net/smc/smc_clc.h +++ b/net/smc/smc_clc.h @@ -347,5 +347,8 @@ int smc_nl_dump_ueid(struct sk_buff *skb, struct netlink_callback *cb); int smc_nl_add_ueid(struct sk_buff *skb, struct genl_info *info); int smc_nl_remove_ueid(struct sk_buff *skb, struct genl_info *info); int smc_nl_flush_ueid(struct sk_buff *skb, struct genl_info *info); +int smc_nl_dump_seid(struct sk_buff *skb, struct netlink_callback *cb); +int smc_nl_enable_seid(struct sk_buff *skb, struct genl_info *info); +int smc_nl_disable_seid(struct sk_buff *skb, struct genl_info *info); #endif diff --git a/net/smc/smc_netlink.c b/net/smc/smc_netlink.c index 4548ff2df245..f13ab0661ed5 100644 --- a/net/smc/smc_netlink.c +++ b/net/smc/smc_netlink.c @@ -96,6 +96,21 @@ static const struct genl_ops smc_gen_nl_ops[] = { .flags = GENL_ADMIN_PERM, .doit = smc_nl_flush_ueid, }, + { + .cmd = SMC_NETLINK_DUMP_SEID, + /* can be retrieved by unprivileged users */ + .dumpit = smc_nl_dump_seid, + }, + { + .cmd = SMC_NETLINK_ENABLE_SEID, + .flags = GENL_ADMIN_PERM, + .doit = smc_nl_enable_seid, + }, + { + .cmd = SMC_NETLINK_DISABLE_SEID, + .flags = GENL_ADMIN_PERM, + .doit = smc_nl_disable_seid, + }, }; static const struct nla_policy smc_gen_nl_policy[2] = { From 01649011cc8295238ad8e7a7f657fc79807dae6b Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Mon, 13 Sep 2021 21:46:06 +0200 Subject: [PATCH 27/87] r8169: remove support for chip version RTL_GIGA_MAC_VER_27 This patch is a follow-up to beb401ec5006 ("r8169: deprecate support for RTL_GIGA_MAC_VER_27") that came with 5.12. Nobody complained, so let's remove support for this chip version. Signed-off-by: Heiner Kallweit Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169.h | 2 +- drivers/net/ethernet/realtek/r8169_main.c | 41 +------------ .../net/ethernet/realtek/r8169_phy_config.c | 59 ------------------- 3 files changed, 3 insertions(+), 99 deletions(-) diff --git a/drivers/net/ethernet/realtek/r8169.h b/drivers/net/ethernet/realtek/r8169.h index 2728df46ec41..8da4b66b71b5 100644 --- a/drivers/net/ethernet/realtek/r8169.h +++ b/drivers/net/ethernet/realtek/r8169.h @@ -37,7 +37,7 @@ enum mac_version { RTL_GIGA_MAC_VER_24, RTL_GIGA_MAC_VER_25, RTL_GIGA_MAC_VER_26, - RTL_GIGA_MAC_VER_27, + /* support for RTL_GIGA_MAC_VER_27 has been removed */ RTL_GIGA_MAC_VER_28, RTL_GIGA_MAC_VER_29, RTL_GIGA_MAC_VER_30, diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 46a6ff9a782d..0199914440ab 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -118,7 +118,6 @@ static const struct { [RTL_GIGA_MAC_VER_24] = {"RTL8168cp/8111cp" }, [RTL_GIGA_MAC_VER_25] = {"RTL8168d/8111d", FIRMWARE_8168D_1}, [RTL_GIGA_MAC_VER_26] = {"RTL8168d/8111d", FIRMWARE_8168D_2}, - [RTL_GIGA_MAC_VER_27] = {"RTL8168dp/8111dp" }, [RTL_GIGA_MAC_VER_28] = {"RTL8168dp/8111dp" }, [RTL_GIGA_MAC_VER_29] = {"RTL8105e", FIRMWARE_8105E_1}, [RTL_GIGA_MAC_VER_30] = {"RTL8105e", FIRMWARE_8105E_1}, @@ -985,33 +984,6 @@ DECLARE_RTL_COND(rtl_ocpar_cond) return RTL_R32(tp, OCPAR) & OCPAR_FLAG; } -static void r8168dp_1_mdio_access(struct rtl8169_private *tp, int reg, u32 data) -{ - RTL_W32(tp, OCPDR, data | ((reg & OCPDR_REG_MASK) << OCPDR_GPHY_REG_SHIFT)); - RTL_W32(tp, OCPAR, OCPAR_GPHY_WRITE_CMD); - RTL_W32(tp, EPHY_RXER_NUM, 0); - - rtl_loop_wait_low(tp, &rtl_ocpar_cond, 1000, 100); -} - -static void r8168dp_1_mdio_write(struct rtl8169_private *tp, int reg, int value) -{ - r8168dp_1_mdio_access(tp, reg, - OCPDR_WRITE_CMD | (value & OCPDR_DATA_MASK)); -} - -static int r8168dp_1_mdio_read(struct rtl8169_private *tp, int reg) -{ - r8168dp_1_mdio_access(tp, reg, OCPDR_READ_CMD); - - mdelay(1); - RTL_W32(tp, OCPAR, OCPAR_GPHY_READ_CMD); - RTL_W32(tp, EPHY_RXER_NUM, 0); - - return rtl_loop_wait_high(tp, &rtl_ocpar_cond, 1000, 100) ? - RTL_R32(tp, OCPDR) & OCPDR_DATA_MASK : -ETIMEDOUT; -} - #define R8168DP_1_MDIO_ACCESS_BIT 0x00020000 static void r8168dp_2_mdio_start(struct rtl8169_private *tp) @@ -1053,9 +1025,6 @@ static int r8168dp_2_mdio_read(struct rtl8169_private *tp, int reg) static void rtl_writephy(struct rtl8169_private *tp, int location, int val) { switch (tp->mac_version) { - case RTL_GIGA_MAC_VER_27: - r8168dp_1_mdio_write(tp, location, val); - break; case RTL_GIGA_MAC_VER_28: case RTL_GIGA_MAC_VER_31: r8168dp_2_mdio_write(tp, location, val); @@ -1072,8 +1041,6 @@ static void rtl_writephy(struct rtl8169_private *tp, int location, int val) static int rtl_readphy(struct rtl8169_private *tp, int location) { switch (tp->mac_version) { - case RTL_GIGA_MAC_VER_27: - return r8168dp_1_mdio_read(tp, location); case RTL_GIGA_MAC_VER_28: case RTL_GIGA_MAC_VER_31: return r8168dp_2_mdio_read(tp, location); @@ -1235,7 +1202,6 @@ static bool r8168ep_check_dash(struct rtl8169_private *tp) static enum rtl_dash_type rtl_check_dash(struct rtl8169_private *tp) { switch (tp->mac_version) { - case RTL_GIGA_MAC_VER_27: case RTL_GIGA_MAC_VER_28: case RTL_GIGA_MAC_VER_31: return r8168dp_check_dash(tp) ? RTL_DASH_DP : RTL_DASH_NONE; @@ -2040,8 +2006,7 @@ static enum mac_version rtl8169_get_mac_version(u16 xid, bool gmii) /* 8168DP family. */ /* It seems this early RTL8168dp version never made it to - * the wild. Let's see whether somebody complains, if not - * we'll remove support for this chip version completely. + * the wild. Support has been removed. * { 0x7cf, 0x288, RTL_GIGA_MAC_VER_27 }, */ { 0x7cf, 0x28a, RTL_GIGA_MAC_VER_28 }, @@ -2371,7 +2336,7 @@ static void rtl_jumbo_config(struct rtl8169_private *tp) r8168c_hw_jumbo_disable(tp); } break; - case RTL_GIGA_MAC_VER_27 ... RTL_GIGA_MAC_VER_28: + case RTL_GIGA_MAC_VER_28: if (jumbo) r8168dp_hw_jumbo_enable(tp); else @@ -3719,7 +3684,6 @@ static void rtl_hw_config(struct rtl8169_private *tp) [RTL_GIGA_MAC_VER_24] = rtl_hw_start_8168cp_3, [RTL_GIGA_MAC_VER_25] = rtl_hw_start_8168d, [RTL_GIGA_MAC_VER_26] = rtl_hw_start_8168d, - [RTL_GIGA_MAC_VER_27] = rtl_hw_start_8168d, [RTL_GIGA_MAC_VER_28] = rtl_hw_start_8168d_4, [RTL_GIGA_MAC_VER_29] = rtl_hw_start_8105e_1, [RTL_GIGA_MAC_VER_30] = rtl_hw_start_8105e_2, @@ -3982,7 +3946,6 @@ static void rtl8169_cleanup(struct rtl8169_private *tp, bool going_down) goto no_reset; switch (tp->mac_version) { - case RTL_GIGA_MAC_VER_27: case RTL_GIGA_MAC_VER_28: case RTL_GIGA_MAC_VER_31: rtl_loop_wait_low(tp, &rtl_npq_cond, 20, 2000); diff --git a/drivers/net/ethernet/realtek/r8169_phy_config.c b/drivers/net/ethernet/realtek/r8169_phy_config.c index 50f0f621b1aa..f7ad5487879b 100644 --- a/drivers/net/ethernet/realtek/r8169_phy_config.c +++ b/drivers/net/ethernet/realtek/r8169_phy_config.c @@ -548,64 +548,6 @@ static void rtl8168d_2_hw_phy_config(struct rtl8169_private *tp, rtl8168d_apply_firmware_cond(tp, phydev, 0xb300); } -static void rtl8168d_3_hw_phy_config(struct rtl8169_private *tp, - struct phy_device *phydev) -{ - static const struct phy_reg phy_reg_init[] = { - { 0x1f, 0x0002 }, - { 0x10, 0x0008 }, - { 0x0d, 0x006c }, - - { 0x1f, 0x0000 }, - { 0x0d, 0xf880 }, - - { 0x1f, 0x0001 }, - { 0x17, 0x0cc0 }, - - { 0x1f, 0x0001 }, - { 0x0b, 0xa4d8 }, - { 0x09, 0x281c }, - { 0x07, 0x2883 }, - { 0x0a, 0x6b35 }, - { 0x1d, 0x3da4 }, - { 0x1c, 0xeffd }, - { 0x14, 0x7f52 }, - { 0x18, 0x7fc6 }, - { 0x08, 0x0601 }, - { 0x06, 0x4063 }, - { 0x10, 0xf074 }, - { 0x1f, 0x0003 }, - { 0x13, 0x0789 }, - { 0x12, 0xf4bd }, - { 0x1a, 0x04fd }, - { 0x14, 0x84b0 }, - { 0x1f, 0x0000 }, - { 0x00, 0x9200 }, - - { 0x1f, 0x0005 }, - { 0x01, 0x0340 }, - { 0x1f, 0x0001 }, - { 0x04, 0x4000 }, - { 0x03, 0x1d21 }, - { 0x02, 0x0c32 }, - { 0x01, 0x0200 }, - { 0x00, 0x5554 }, - { 0x04, 0x4800 }, - { 0x04, 0x4000 }, - { 0x04, 0xf000 }, - { 0x03, 0xdf01 }, - { 0x02, 0xdf20 }, - { 0x01, 0x101a }, - { 0x00, 0xa0ff }, - { 0x04, 0xf800 }, - { 0x04, 0xf000 }, - { 0x1f, 0x0000 }, - }; - - rtl_writephy_batch(phydev, phy_reg_init); - r8168d_modify_extpage(phydev, 0x0023, 0x16, 0xffff, 0x0000); -} - static void rtl8168d_4_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev) { @@ -1332,7 +1274,6 @@ void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev, [RTL_GIGA_MAC_VER_24] = rtl8168cp_2_hw_phy_config, [RTL_GIGA_MAC_VER_25] = rtl8168d_1_hw_phy_config, [RTL_GIGA_MAC_VER_26] = rtl8168d_2_hw_phy_config, - [RTL_GIGA_MAC_VER_27] = rtl8168d_3_hw_phy_config, [RTL_GIGA_MAC_VER_28] = rtl8168d_4_hw_phy_config, [RTL_GIGA_MAC_VER_29] = rtl8105e_hw_phy_config, [RTL_GIGA_MAC_VER_30] = rtl8105e_hw_phy_config, From c70aae139d3940a0ae0922ed52384e14f092a963 Mon Sep 17 00:00:00 2001 From: Min Li Date: Mon, 13 Sep 2021 16:12:32 -0400 Subject: [PATCH 28/87] ptp: ptp_clockmatrix: Remove idtcm_enable_tod_sync() Not need since TCS firmware file will configure it properlly. Signed-off-by: Min Li Signed-off-by: David S. Miller --- drivers/ptp/ptp_clockmatrix.c | 229 +--------------------------------- 1 file changed, 2 insertions(+), 227 deletions(-) diff --git a/drivers/ptp/ptp_clockmatrix.c b/drivers/ptp/ptp_clockmatrix.c index fa636951169e..9b1c6b2c221c 100644 --- a/drivers/ptp/ptp_clockmatrix.c +++ b/drivers/ptp/ptp_clockmatrix.c @@ -353,8 +353,8 @@ static int wait_for_sys_apll_dpll_lock(struct idtcm *idtcm) apll &= SYS_APLL_LOSS_LOCK_LIVE_MASK; dpll &= DPLL_SYS_STATE_MASK; - if (apll == SYS_APLL_LOSS_LOCK_LIVE_LOCKED && - dpll == DPLL_STATE_LOCKED) { + if (apll == SYS_APLL_LOSS_LOCK_LIVE_LOCKED + && dpll == DPLL_STATE_LOCKED) { return 0; } else if (dpll == DPLL_STATE_FREERUN || dpll == DPLL_STATE_HOLDOVER || @@ -1675,222 +1675,6 @@ static int idtcm_enable(struct ptp_clock_info *ptp, return -EOPNOTSUPP; } -static int _enable_pll_tod_sync(struct idtcm *idtcm, - u8 pll, - u8 sync_src, - u8 qn, - u8 qn_plus_1) -{ - int err; - u8 val; - u16 dpll; - u16 out0 = 0, out1 = 0; - - if (qn == 0 && qn_plus_1 == 0) - return 0; - - switch (pll) { - case 0: - dpll = DPLL_0; - if (qn) - out0 = OUTPUT_0; - if (qn_plus_1) - out1 = OUTPUT_1; - break; - case 1: - dpll = DPLL_1; - if (qn) - out0 = OUTPUT_2; - if (qn_plus_1) - out1 = OUTPUT_3; - break; - case 2: - dpll = DPLL_2; - if (qn) - out0 = OUTPUT_4; - if (qn_plus_1) - out1 = OUTPUT_5; - break; - case 3: - dpll = DPLL_3; - if (qn) - out0 = OUTPUT_6; - if (qn_plus_1) - out1 = OUTPUT_7; - break; - case 4: - dpll = DPLL_4; - if (qn) - out0 = OUTPUT_8; - break; - case 5: - dpll = DPLL_5; - if (qn) - out0 = OUTPUT_9; - if (qn_plus_1) - out1 = OUTPUT_8; - break; - case 6: - dpll = DPLL_6; - if (qn) - out0 = OUTPUT_10; - if (qn_plus_1) - out1 = OUTPUT_11; - break; - case 7: - dpll = DPLL_7; - if (qn) - out0 = OUTPUT_11; - break; - default: - return -EINVAL; - } - - /* - * Enable OUTPUT OUT_SYNC. - */ - if (out0) { - err = idtcm_read(idtcm, out0, OUT_CTRL_1, &val, sizeof(val)); - if (err) - return err; - - val &= ~OUT_SYNC_DISABLE; - - err = idtcm_write(idtcm, out0, OUT_CTRL_1, &val, sizeof(val)); - if (err) - return err; - } - - if (out1) { - err = idtcm_read(idtcm, out1, OUT_CTRL_1, &val, sizeof(val)); - if (err) - return err; - - val &= ~OUT_SYNC_DISABLE; - - err = idtcm_write(idtcm, out1, OUT_CTRL_1, &val, sizeof(val)); - if (err) - return err; - } - - /* enable dpll sync tod pps, must be set before dpll_mode */ - err = idtcm_read(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val)); - if (err) - return err; - - val &= ~(TOD_SYNC_SOURCE_MASK << TOD_SYNC_SOURCE_SHIFT); - val |= (sync_src << TOD_SYNC_SOURCE_SHIFT); - val |= TOD_SYNC_EN; - - return idtcm_write(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val)); -} - -static int idtcm_enable_tod_sync(struct idtcm_channel *channel) -{ - struct idtcm *idtcm = channel->idtcm; - u8 pll; - u8 sync_src; - u8 qn; - u8 qn_plus_1; - u8 cfg; - int err = 0; - u16 output_mask = channel->output_mask; - u8 out8_mux = 0; - u8 out11_mux = 0; - u8 temp; - - /* - * set tod_out_sync_enable to 0. - */ - err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg)); - if (err) - return err; - - cfg &= ~TOD_OUT_SYNC_ENABLE; - - err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg)); - if (err) - return err; - - switch (channel->tod_n) { - case TOD_0: - sync_src = 0; - break; - case TOD_1: - sync_src = 1; - break; - case TOD_2: - sync_src = 2; - break; - case TOD_3: - sync_src = 3; - break; - default: - return -EINVAL; - } - - err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE, &temp, sizeof(temp)); - if (err) - return err; - - if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) == - Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) - out8_mux = 1; - - err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE, &temp, sizeof(temp)); - if (err) - return err; - - if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) == - Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) - out11_mux = 1; - - for (pll = 0; pll < 8; pll++) { - qn = 0; - qn_plus_1 = 0; - - if (pll < 4) { - /* First 4 pll has 2 outputs */ - qn = output_mask & 0x1; - output_mask = output_mask >> 1; - qn_plus_1 = output_mask & 0x1; - output_mask = output_mask >> 1; - } else if (pll == 4) { - if (out8_mux == 0) { - qn = output_mask & 0x1; - output_mask = output_mask >> 1; - } - } else if (pll == 5) { - if (out8_mux) { - qn_plus_1 = output_mask & 0x1; - output_mask = output_mask >> 1; - } - qn = output_mask & 0x1; - output_mask = output_mask >> 1; - } else if (pll == 6) { - qn = output_mask & 0x1; - output_mask = output_mask >> 1; - if (out11_mux) { - qn_plus_1 = output_mask & 0x1; - output_mask = output_mask >> 1; - } - } else if (pll == 7) { - if (out11_mux == 0) { - qn = output_mask & 0x1; - output_mask = output_mask >> 1; - } - } - - if (qn != 0 || qn_plus_1 != 0) - err = _enable_pll_tod_sync(idtcm, pll, sync_src, qn, - qn_plus_1); - if (err) - return err; - } - - return err; -} - static int idtcm_enable_tod(struct idtcm_channel *channel) { struct idtcm *idtcm = channel->idtcm; @@ -2101,15 +1885,6 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index) snprintf(channel->caps.name, sizeof(channel->caps.name), "IDT CM TOD%u", index); - if (!idtcm->deprecated) { - err = idtcm_enable_tod_sync(channel); - if (err) { - dev_err(&idtcm->client->dev, - "Failed at line %d in %s!", __LINE__, __func__); - return err; - } - } - /* Sync pll mode with hardware */ err = idtcm_get_pll_mode(channel, &channel->pll_mode); if (err) { From 794c3dffacc166f7a8f7a555ff7e75fcdb644a51 Mon Sep 17 00:00:00 2001 From: Min Li Date: Mon, 13 Sep 2021 16:12:33 -0400 Subject: [PATCH 29/87] ptp: ptp_clockmatrix: Add support for FW 5.2 (8A34005) So far we don't need to support new 5.2 functions but different register addresses Signed-off-by: Min Li Signed-off-by: David S. Miller --- drivers/ptp/idt8a340_reg.h | 61 +++++++++++- drivers/ptp/ptp_clockmatrix.c | 179 ++++++++++++++++++---------------- drivers/ptp/ptp_clockmatrix.h | 17 +++- 3 files changed, 165 insertions(+), 92 deletions(-) diff --git a/drivers/ptp/idt8a340_reg.h b/drivers/ptp/idt8a340_reg.h index ac524cf0f31f..dea8e1d6edaa 100644 --- a/drivers/ptp/idt8a340_reg.h +++ b/drivers/ptp/idt8a340_reg.h @@ -5,7 +5,7 @@ * https://github.com/richardcochran/regen * * Hand modified to include some HW registers. - * Based on 4.8.0, SCSR rev C commit a03c7ae5 + * Based on 5.2.0, Family Programming Guide (Sept 30, 2020) */ #ifndef HAVE_IDT8A340_REG #define HAVE_IDT8A340_REG @@ -100,6 +100,7 @@ #define RESET_CTRL 0xc000 #define SM_RESET 0x0012 +#define SM_RESET_V520 0x0013 #define SM_RESET_CMD 0x5A #define GENERAL_STATUS 0xc014 @@ -130,6 +131,8 @@ #define GPIO_USER_CONTROL 0xc160 #define GPIO0_TO_7_OUT 0x0000 #define GPIO8_TO_15_OUT 0x0001 +#define GPIO0_TO_7_OUT_V520 0x0002 +#define GPIO8_TO_15_OUT_V520 0x0003 #define STICKY_STATUS_CLEAR 0xc164 @@ -216,22 +219,27 @@ #define DPLL_REF_MODE 0x0035 #define DPLL_PHASE_MEASUREMENT_CFG 0x0036 #define DPLL_MODE 0x0037 +#define DPLL_MODE_V520 0x003B #define DPLL_1 0xc400 #define DPLL_2 0xc438 +#define DPLL_2_V520 0xc43c #define DPLL_3 0xc480 #define DPLL_4 0xc4b8 +#define DPLL_4_V520 0xc4bc #define DPLL_5 0xc500 #define DPLL_6 0xc538 +#define DPLL_6_V520 0xc53c #define DPLL_7 0xc580 #define SYS_DPLL 0xc5b8 +#define SYS_DPLL_V520 0xc5bc #define DPLL_CTRL_0 0xc600 #define DPLL_CTRL_DPLL_MANU_REF_CFG 0x0001 @@ -331,6 +339,7 @@ #define GPIO_ALERT_OUT_CFG 0x000e #define GPIO_TOD_NOTIFICATION_CFG 0x000f #define GPIO_CTRL 0x0010 +#define GPIO_CTRL_V520 0x0011 #define GPIO_1 0xc8d4 @@ -365,6 +374,7 @@ #define OUT_DIV_MUX 0xca12 #define OUTPUT_0 0xca14 +#define OUTPUT_0_V520 0xca20 /* FOD frequency output divider value */ #define OUT_DIV 0x0000 #define OUT_DUTY_CYCLE_HIGH 0x0004 @@ -374,28 +384,40 @@ #define OUT_PHASE_ADJ 0x000c #define OUTPUT_1 0xca24 +#define OUTPUT_1_V520 0xca30 #define OUTPUT_2 0xca34 +#define OUTPUT_2_V520 0xca40 #define OUTPUT_3 0xca44 +#define OUTPUT_3_V520 0xca50 #define OUTPUT_4 0xca54 +#define OUTPUT_4_V520 0xca60 #define OUTPUT_5 0xca64 +#define OUTPUT_5_V520 0xca80 #define OUTPUT_6 0xca80 +#define OUTPUT_6_V520 0xca90 #define OUTPUT_7 0xca90 +#define OUTPUT_7_V520 0xcaa0 #define OUTPUT_8 0xcaa0 +#define OUTPUT_8_V520 0xcab0 #define OUTPUT_9 0xcab0 +#define OUTPUT_9_V520 0xcac0 #define OUTPUT_10 0xcac0 +#define OUTPUT_10_V520 0xcad0 #define OUTPUT_11 0xcad0 +#define OUTPUT_11_V520 0xcae0 #define SERIAL 0xcae0 +#define SERIAL_V520 0xcaf0 #define PWM_ENCODER_0 0xcb00 @@ -416,50 +438,72 @@ #define PWM_DECODER_0 0xcb40 #define PWM_DECODER_1 0xcb48 +#define PWM_DECODER_1_V520 0xcb4a #define PWM_DECODER_2 0xcb50 +#define PWM_DECODER_2_V520 0xcb54 #define PWM_DECODER_3 0xcb58 +#define PWM_DECODER_3_V520 0xcb5e #define PWM_DECODER_4 0xcb60 +#define PWM_DECODER_4_V520 0xcb68 #define PWM_DECODER_5 0xcb68 +#define PWM_DECODER_5_V520 0xcb80 #define PWM_DECODER_6 0xcb70 +#define PWM_DECODER_6_V520 0xcb8a #define PWM_DECODER_7 0xcb80 +#define PWM_DECODER_7_V520 0xcb94 #define PWM_DECODER_8 0xcb88 +#define PWM_DECODER_8_V520 0xcb9e #define PWM_DECODER_9 0xcb90 +#define PWM_DECODER_9_V520 0xcba8 #define PWM_DECODER_10 0xcb98 +#define PWM_DECODER_10_V520 0xcbb2 #define PWM_DECODER_11 0xcba0 +#define PWM_DECODER_11_V520 0xcbbc #define PWM_DECODER_12 0xcba8 +#define PWM_DECODER_12_V520 0xcbc6 #define PWM_DECODER_13 0xcbb0 +#define PWM_DECODER_13_V520 0xcbd0 #define PWM_DECODER_14 0xcbb8 +#define PWM_DECODER_14_V520 0xcbda #define PWM_DECODER_15 0xcbc0 +#define PWM_DECODER_15_V520 0xcbe4 #define PWM_USER_DATA 0xcbc8 +#define PWM_USER_DATA_V520 0xcbf0 #define TOD_0 0xcbcc +#define TOD_0_V520 0xcc00 /* Enable TOD counter, output channel sync and even-PPS mode */ #define TOD_CFG 0x0000 +#define TOD_CFG_V520 0x0001 #define TOD_1 0xcbce +#define TOD_1_V520 0xcc02 #define TOD_2 0xcbd0 +#define TOD_2_V520 0xcc04 #define TOD_3 0xcbd2 +#define TOD_3_V520 0xcc06 #define TOD_WRITE_0 0xcc00 +#define TOD_WRITE_0_V520 0xcc10 /* 8-bit subns, 32-bit ns, 48-bit seconds */ #define TOD_WRITE 0x0000 /* Counter increments after TOD write is completed */ @@ -470,12 +514,16 @@ #define TOD_WRITE_CMD 0x000f #define TOD_WRITE_1 0xcc10 +#define TOD_WRITE_1_V520 0xcc20 #define TOD_WRITE_2 0xcc20 +#define TOD_WRITE_2_V520 0xcc30 #define TOD_WRITE_3 0xcc30 +#define TOD_WRITE_3_V520 0xcc40 #define TOD_READ_PRIMARY_0 0xcc40 +#define TOD_READ_PRIMARY_0_V520 0xcc50 /* 8-bit subns, 32-bit ns, 48-bit seconds */ #define TOD_READ_PRIMARY 0x0000 /* Counter increments after TOD write is completed */ @@ -484,22 +532,31 @@ #define TOD_READ_PRIMARY_SEL_CFG_0 0x000c /* Read trigger selection */ #define TOD_READ_PRIMARY_CMD 0x000e +#define TOD_READ_PRIMARY_CMD_V520 0x000f #define TOD_READ_PRIMARY_1 0xcc50 +#define TOD_READ_PRIMARY_1_V520 0xcc60 #define TOD_READ_PRIMARY_2 0xcc60 +#define TOD_READ_PRIMARY_2_V520 0xcc80 #define TOD_READ_PRIMARY_3 0xcc80 +#define TOD_READ_PRIMARY_3_V520 0xcc90 #define TOD_READ_SECONDARY_0 0xcc90 +#define TOD_READ_SECONDARY_0_V520 0xcca0 #define TOD_READ_SECONDARY_1 0xcca0 +#define TOD_READ_SECONDARY_1_V520 0xccb0 #define TOD_READ_SECONDARY_2 0xccb0 +#define TOD_READ_SECONDARY_2_V520 0xccc0 #define TOD_READ_SECONDARY_3 0xccc0 +#define TOD_READ_SECONDARY_3_V520 0xccd0 #define OUTPUT_TDC_CFG 0xccd0 +#define OUTPUT_TDC_CFG_V520 0xcce0 #define OUTPUT_TDC_0 0xcd00 @@ -512,8 +569,10 @@ #define INPUT_TDC 0xcd20 #define SCRATCH 0xcf50 +#define SCRATCH_V520 0xcf4c #define EEPROM 0xcf68 +#define EEPROM_V520 0xcf64 #define OTP 0xcf70 diff --git a/drivers/ptp/ptp_clockmatrix.c b/drivers/ptp/ptp_clockmatrix.c index 9b1c6b2c221c..2c552a0b6562 100644 --- a/drivers/ptp/ptp_clockmatrix.c +++ b/drivers/ptp/ptp_clockmatrix.c @@ -33,15 +33,21 @@ module_param(firmware, charp, 0); #define SETTIME_CORRECTION (0) -static int contains_full_configuration(const struct firmware *fw) +static int contains_full_configuration(struct idtcm *idtcm, + const struct firmware *fw) { - s32 full_count = FULL_FW_CFG_BYTES - FULL_FW_CFG_SKIPPED_BYTES; struct idtcm_fwrc *rec = (struct idtcm_fwrc *)fw->data; + u16 scratch = IDTCM_FW_REG(idtcm->fw_ver, V520, SCRATCH); + s32 full_count; s32 count = 0; u16 regaddr; u8 loaddr; s32 len; + /* 4 bytes skipped every 0x80 */ + full_count = (scratch - GPIO_USER_CONTROL) - + ((scratch >> 7) - (GPIO_USER_CONTROL >> 7)) * 4; + /* If the firmware contains 'full configuration' SM_RESET can be used * to ensure proper configuration. * @@ -57,7 +63,7 @@ static int contains_full_configuration(const struct firmware *fw) rec++; /* Top (status registers) and bottom are read-only */ - if (regaddr < GPIO_USER_CONTROL || regaddr >= SCRATCH) + if (regaddr < GPIO_USER_CONTROL || regaddr >= scratch) continue; /* Page size 128, last 4 bytes of page skipped */ @@ -152,6 +158,19 @@ static int idtcm_strverscmp(const char *version1, const char *version2) return 0; } +static enum fw_version idtcm_fw_version(const char *version) +{ + enum fw_version ver = V_DEFAULT; + + if (idtcm_strverscmp(version, "4.8.7") >= 0) + ver = V487; + + if (idtcm_strverscmp(version, "5.2.0") >= 0) + ver = V520; + + return ver; +} + static int idtcm_xfer_read(struct idtcm *idtcm, u8 regaddr, u8 *buf, @@ -353,8 +372,8 @@ static int wait_for_sys_apll_dpll_lock(struct idtcm *idtcm) apll &= SYS_APLL_LOSS_LOCK_LIVE_MASK; dpll &= DPLL_SYS_STATE_MASK; - if (apll == SYS_APLL_LOSS_LOCK_LIVE_LOCKED - && dpll == DPLL_STATE_LOCKED) { + if (apll == SYS_APLL_LOSS_LOCK_LIVE_LOCKED && + dpll == DPLL_STATE_LOCKED) { return 0; } else if (dpll == DPLL_STATE_FREERUN || dpll == DPLL_STATE_HOLDOVER || @@ -388,13 +407,14 @@ static int _idtcm_gettime(struct idtcm_channel *channel, struct timespec64 *ts) { struct idtcm *idtcm = channel->idtcm; + u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_PRIMARY_CMD); u8 buf[TOD_BYTE_COUNT]; u8 timeout = 10; u8 trigger; int err; err = idtcm_read(idtcm, channel->tod_read_primary, - TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger)); + tod_read_cmd, &trigger, sizeof(trigger)); if (err) return err; @@ -403,7 +423,7 @@ static int _idtcm_gettime(struct idtcm_channel *channel, trigger &= ~TOD_READ_TRIGGER_MODE; /* single shot */ err = idtcm_write(idtcm, channel->tod_read_primary, - TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger)); + tod_read_cmd, &trigger, sizeof(trigger)); if (err) return err; @@ -413,7 +433,7 @@ static int _idtcm_gettime(struct idtcm_channel *channel, idtcm->start_time = ktime_get_raw(); err = idtcm_read(idtcm, channel->tod_read_primary, - TOD_READ_PRIMARY_CMD, &trigger, + tod_read_cmd, &trigger, sizeof(trigger)); if (err) return err; @@ -559,35 +579,10 @@ static int _sync_pll_output(struct idtcm *idtcm, return err; } -static int sync_source_dpll_tod_pps(u16 tod_addr, u8 *sync_src) -{ - int err = 0; - - switch (tod_addr) { - case TOD_0: - *sync_src = SYNC_SOURCE_DPLL0_TOD_PPS; - break; - case TOD_1: - *sync_src = SYNC_SOURCE_DPLL1_TOD_PPS; - break; - case TOD_2: - *sync_src = SYNC_SOURCE_DPLL2_TOD_PPS; - break; - case TOD_3: - *sync_src = SYNC_SOURCE_DPLL3_TOD_PPS; - break; - default: - err = -EINVAL; - } - - return err; -} - static int idtcm_sync_pps_output(struct idtcm_channel *channel) { struct idtcm *idtcm = channel->idtcm; u8 pll; - u8 sync_src; u8 qn; u8 qn_plus_1; int err = 0; @@ -596,10 +591,6 @@ static int idtcm_sync_pps_output(struct idtcm_channel *channel) u8 temp; u16 output_mask = channel->output_mask; - err = sync_source_dpll_tod_pps(channel->tod_n, &sync_src); - if (err) - return err; - err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE, &temp, sizeof(temp)); if (err) @@ -655,8 +646,8 @@ static int idtcm_sync_pps_output(struct idtcm_channel *channel) } if (qn != 0 || qn_plus_1 != 0) - err = _sync_pll_output(idtcm, pll, sync_src, qn, - qn_plus_1); + err = _sync_pll_output(idtcm, pll, channel->sync_src, + qn, qn_plus_1); if (err) return err; @@ -793,46 +784,46 @@ static int _idtcm_set_dpll_scsr_tod(struct idtcm_channel *channel, return 0; } -static int get_output_base_addr(u8 outn) +static int get_output_base_addr(enum fw_version ver, u8 outn) { int base; switch (outn) { case 0: - base = OUTPUT_0; + base = IDTCM_FW_REG(ver, V520, OUTPUT_0); break; case 1: - base = OUTPUT_1; + base = IDTCM_FW_REG(ver, V520, OUTPUT_1); break; case 2: - base = OUTPUT_2; + base = IDTCM_FW_REG(ver, V520, OUTPUT_2); break; case 3: - base = OUTPUT_3; + base = IDTCM_FW_REG(ver, V520, OUTPUT_3); break; case 4: - base = OUTPUT_4; + base = IDTCM_FW_REG(ver, V520, OUTPUT_4); break; case 5: - base = OUTPUT_5; + base = IDTCM_FW_REG(ver, V520, OUTPUT_5); break; case 6: - base = OUTPUT_6; + base = IDTCM_FW_REG(ver, V520, OUTPUT_6); break; case 7: - base = OUTPUT_7; + base = IDTCM_FW_REG(ver, V520, OUTPUT_7); break; case 8: - base = OUTPUT_8; + base = IDTCM_FW_REG(ver, V520, OUTPUT_8); break; case 9: - base = OUTPUT_9; + base = IDTCM_FW_REG(ver, V520, OUTPUT_9); break; case 10: - base = OUTPUT_10; + base = IDTCM_FW_REG(ver, V520, OUTPUT_10); break; case 11: - base = OUTPUT_11; + base = IDTCM_FW_REG(ver, V520, OUTPUT_11); break; default: base = -EINVAL; @@ -1032,7 +1023,9 @@ static int idtcm_state_machine_reset(struct idtcm *idtcm) clear_boot_status(idtcm); - err = idtcm_write(idtcm, RESET_CTRL, SM_RESET, &byte, sizeof(byte)); + err = idtcm_write(idtcm, RESET_CTRL, + IDTCM_FW_REG(idtcm->fw_ver, V520, SM_RESET), + &byte, sizeof(byte)); if (!err) { for (i = 0; i < 30; i++) { @@ -1214,6 +1207,7 @@ static void display_pll_and_masks(struct idtcm *idtcm) static int idtcm_load_firmware(struct idtcm *idtcm, struct device *dev) { + u16 scratch = IDTCM_FW_REG(idtcm->fw_ver, V520, SCRATCH); char fname[128] = FW_FILENAME; const struct firmware *fw; struct idtcm_fwrc *rec; @@ -1239,7 +1233,7 @@ static int idtcm_load_firmware(struct idtcm *idtcm, rec = (struct idtcm_fwrc *) fw->data; - if (contains_full_configuration(fw)) + if (contains_full_configuration(idtcm, fw)) idtcm_state_machine_reset(idtcm); for (len = fw->size; len > 0; len -= sizeof(*rec)) { @@ -1263,7 +1257,7 @@ static int idtcm_load_firmware(struct idtcm *idtcm, err = 0; /* Top (status registers) and bottom are read-only */ - if (regaddr < GPIO_USER_CONTROL || regaddr >= SCRATCH) + if (regaddr < GPIO_USER_CONTROL || regaddr >= scratch) continue; /* Page size 128, last 4 bytes of page skipped */ @@ -1292,7 +1286,7 @@ static int idtcm_output_enable(struct idtcm_channel *channel, int err; u8 val; - base = get_output_base_addr(outn); + base = get_output_base_addr(idtcm->fw_ver, outn); if (!(base > 0)) { dev_err(&idtcm->client->dev, @@ -1366,7 +1360,8 @@ static int idtcm_get_pll_mode(struct idtcm_channel *channel, int err; u8 dpll_mode; - err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE, + err = idtcm_read(idtcm, channel->dpll_n, + IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_MODE), &dpll_mode, sizeof(dpll_mode)); if (err) return err; @@ -1383,7 +1378,8 @@ static int idtcm_set_pll_mode(struct idtcm_channel *channel, int err; u8 dpll_mode; - err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE, + err = idtcm_read(idtcm, channel->dpll_n, + IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_MODE), &dpll_mode, sizeof(dpll_mode)); if (err) return err; @@ -1394,7 +1390,8 @@ static int idtcm_set_pll_mode(struct idtcm_channel *channel, channel->pll_mode = pll_mode; - err = idtcm_write(idtcm, channel->dpll_n, DPLL_MODE, + err = idtcm_write(idtcm, channel->dpll_n, + IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_MODE), &dpll_mode, sizeof(dpll_mode)); if (err) return err; @@ -1404,8 +1401,8 @@ static int idtcm_set_pll_mode(struct idtcm_channel *channel, /* PTP Hardware Clock interface */ -/* - * Maximum absolute value for write phase offset in picoseconds +/** + * @brief Maximum absolute value for write phase offset in picoseconds * * Destination signed register is 32-bit register in resolution of 50ps * @@ -1679,23 +1676,31 @@ static int idtcm_enable_tod(struct idtcm_channel *channel) { struct idtcm *idtcm = channel->idtcm; struct timespec64 ts = {0, 0}; + u16 tod_cfg = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_CFG); u8 cfg; int err; + /* STEELAI-366 - Temporary workaround for ts2phc compatibility */ + if (0) { + err = idtcm_output_mask_enable(channel, false); + if (err) + return err; + } + /* * Start the TOD clock ticking. */ - err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg)); + err = idtcm_read(idtcm, channel->tod_n, tod_cfg, &cfg, sizeof(cfg)); if (err) return err; cfg |= TOD_ENABLE; - err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg)); + err = idtcm_write(idtcm, channel->tod_n, tod_cfg, &cfg, sizeof(cfg)); if (err) return err; - if (idtcm->deprecated) + if (idtcm->fw_ver < V487) return _idtcm_settime_deprecated(channel, &ts); else return _idtcm_settime(channel, &ts, @@ -1723,10 +1728,7 @@ static void idtcm_set_version_info(struct idtcm *idtcm) snprintf(idtcm->version, sizeof(idtcm->version), "%u.%u.%u", major, minor, hotfix); - if (idtcm_strverscmp(idtcm->version, "4.8.7") >= 0) - idtcm->deprecated = 0; - else - idtcm->deprecated = 1; + idtcm->fw_ver = idtcm_fw_version(idtcm->version); dev_info(&idtcm->client->dev, "%d.%d.%d, Id: 0x%04x HW Rev: %d OTP Config Select: %d", @@ -1760,6 +1762,7 @@ static const struct ptp_clock_info idtcm_caps_deprecated = { static int configure_channel_pll(struct idtcm_channel *channel) { + struct idtcm *idtcm = channel->idtcm; int err = 0; switch (channel->pll) { @@ -1781,7 +1784,7 @@ static int configure_channel_pll(struct idtcm_channel *channel) break; case 2: channel->dpll_freq = DPLL_FREQ_2; - channel->dpll_n = DPLL_2; + channel->dpll_n = IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_2); channel->hw_dpll_n = HW_DPLL_2; channel->dpll_phase = DPLL_PHASE_2; channel->dpll_ctrl_n = DPLL_CTRL_2; @@ -1797,7 +1800,7 @@ static int configure_channel_pll(struct idtcm_channel *channel) break; case 4: channel->dpll_freq = DPLL_FREQ_4; - channel->dpll_n = DPLL_4; + channel->dpll_n = IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_4); channel->hw_dpll_n = HW_DPLL_4; channel->dpll_phase = DPLL_PHASE_4; channel->dpll_ctrl_n = DPLL_CTRL_4; @@ -1813,7 +1816,7 @@ static int configure_channel_pll(struct idtcm_channel *channel) break; case 6: channel->dpll_freq = DPLL_FREQ_6; - channel->dpll_n = DPLL_6; + channel->dpll_n = IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_6); channel->hw_dpll_n = HW_DPLL_6; channel->dpll_phase = DPLL_PHASE_6; channel->dpll_ctrl_n = DPLL_CTRL_6; @@ -1836,6 +1839,7 @@ static int configure_channel_pll(struct idtcm_channel *channel) static int idtcm_enable_channel(struct idtcm *idtcm, u32 index) { + enum fw_version fw_ver = idtcm->fw_ver; struct idtcm_channel *channel; int err; @@ -1843,6 +1847,7 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index) return -EINVAL; channel = &idtcm->channel[index]; + channel->idtcm = idtcm; /* Set pll addresses */ err = configure_channel_pll(channel); @@ -1852,32 +1857,34 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index) /* Set tod addresses */ switch (index) { case 0: - channel->tod_read_primary = TOD_READ_PRIMARY_0; - channel->tod_write = TOD_WRITE_0; - channel->tod_n = TOD_0; + channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_0); + channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_0); + channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_0); + channel->sync_src = SYNC_SOURCE_DPLL0_TOD_PPS; break; case 1: - channel->tod_read_primary = TOD_READ_PRIMARY_1; - channel->tod_write = TOD_WRITE_1; - channel->tod_n = TOD_1; + channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_1); + channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_1); + channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_1); + channel->sync_src = SYNC_SOURCE_DPLL1_TOD_PPS; break; case 2: - channel->tod_read_primary = TOD_READ_PRIMARY_2; - channel->tod_write = TOD_WRITE_2; - channel->tod_n = TOD_2; + channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_2); + channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_2); + channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_2); + channel->sync_src = SYNC_SOURCE_DPLL2_TOD_PPS; break; case 3: - channel->tod_read_primary = TOD_READ_PRIMARY_3; - channel->tod_write = TOD_WRITE_3; - channel->tod_n = TOD_3; + channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_3); + channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_3); + channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_3); + channel->sync_src = SYNC_SOURCE_DPLL3_TOD_PPS; break; default: return -EINVAL; } - channel->idtcm = idtcm; - - if (idtcm->deprecated) + if (idtcm->fw_ver < V487) channel->caps = idtcm_caps_deprecated; else channel->caps = idtcm_caps; diff --git a/drivers/ptp/ptp_clockmatrix.h b/drivers/ptp/ptp_clockmatrix.h index fb323271063e..843a9d7129c5 100644 --- a/drivers/ptp/ptp_clockmatrix.h +++ b/drivers/ptp/ptp_clockmatrix.h @@ -57,10 +57,10 @@ #define IDTCM_MAX_WRITE_COUNT (512) -#define FULL_FW_CFG_BYTES (SCRATCH - GPIO_USER_CONTROL) -#define FULL_FW_CFG_SKIPPED_BYTES (((SCRATCH >> 7) \ - - (GPIO_USER_CONTROL >> 7)) \ - * 4) /* 4 bytes skipped every 0x80 */ +/* + * Return register address based on passed in firmware version + */ +#define IDTCM_FW_REG(FW, VER, REG) (((FW) < (VER)) ? (REG) : (REG##_##VER)) /* Values of DPLL_N.DPLL_MODE.PLL_MODE */ enum pll_mode { @@ -119,6 +119,12 @@ enum dpll_state { DPLL_STATE_MAX = DPLL_STATE_OPEN_LOOP, }; +enum fw_version { + V_DEFAULT = 0, + V487 = 1, + V520 = 2, +}; + struct idtcm; struct idtcm_channel { @@ -134,6 +140,7 @@ struct idtcm_channel { u16 tod_write; u16 tod_n; u16 hw_dpll_n; + u8 sync_src; enum pll_mode pll_mode; u8 pll; u16 output_mask; @@ -145,7 +152,7 @@ struct idtcm { u8 page_offset; u8 tod_mask; char version[16]; - u8 deprecated; + enum fw_version fw_ver; /* Overhead calculation for adjtime */ u8 calculate_overhead_flag; From da9facf1c1825201956c2553e06d455dea3e0313 Mon Sep 17 00:00:00 2001 From: Min Li Date: Mon, 13 Sep 2021 16:12:34 -0400 Subject: [PATCH 30/87] ptp: ptp_clockmatrix: Add support for pll_mode=0 and manual ref switch of WF and WP Also correct how initialize_dco_operating_mode is called Signed-off-by: Min Li Signed-off-by: David S. Miller --- drivers/ptp/idt8a340_reg.h | 4 + drivers/ptp/ptp_clockmatrix.c | 378 +++++++++++++++++++++++++++++----- drivers/ptp/ptp_clockmatrix.h | 47 ++++- 3 files changed, 379 insertions(+), 50 deletions(-) diff --git a/drivers/ptp/idt8a340_reg.h b/drivers/ptp/idt8a340_reg.h index dea8e1d6edaa..1c5210187110 100644 --- a/drivers/ptp/idt8a340_reg.h +++ b/drivers/ptp/idt8a340_reg.h @@ -635,6 +635,10 @@ #define STATE_MODE_SHIFT (0) #define STATE_MODE_MASK (0x7) +/* Bit definitions for the DPLL_MANU_REF_CFG register */ +#define MANUAL_REFERENCE_SHIFT (0) +#define MANUAL_REFERENCE_MASK (0x1f) + /* Bit definitions for the GPIO_CFG_GBL register */ #define SUPPLY_MODE_SHIFT (0) #define SUPPLY_MODE_MASK (0x3) diff --git a/drivers/ptp/ptp_clockmatrix.c b/drivers/ptp/ptp_clockmatrix.c index 2c552a0b6562..1a2e3c2e4328 100644 --- a/drivers/ptp/ptp_clockmatrix.c +++ b/drivers/ptp/ptp_clockmatrix.c @@ -33,6 +33,8 @@ module_param(firmware, charp, 0); #define SETTIME_CORRECTION (0) +static int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm); + static int contains_full_configuration(struct idtcm *idtcm, const struct firmware *fw) { @@ -657,8 +659,8 @@ static int idtcm_sync_pps_output(struct idtcm_channel *channel) } static int _idtcm_set_dpll_hw_tod(struct idtcm_channel *channel, - struct timespec64 const *ts, - enum hw_tod_write_trig_sel wr_trig) + struct timespec64 const *ts, + enum hw_tod_write_trig_sel wr_trig) { struct idtcm *idtcm = channel->idtcm; u8 buf[TOD_BYTE_COUNT]; @@ -920,9 +922,9 @@ static int idtcm_start_phase_pull_in(struct idtcm_channel *channel) return err; } -static int idtcm_do_phase_pull_in(struct idtcm_channel *channel, - s32 offset_ns, - u32 max_ffo_ppb) +static int do_phase_pull_in_fw(struct idtcm_channel *channel, + s32 offset_ns, + u32 max_ffo_ppb) { int err; @@ -991,7 +993,7 @@ static int _idtcm_adjtime_deprecated(struct idtcm_channel *channel, s64 delta) s64 now; if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS_DEPRECATED) { - err = idtcm_do_phase_pull_in(channel, delta, 0); + err = channel->do_phase_pull_in(channel, delta, 0); } else { idtcm->calculate_overhead_flag = 1; @@ -1220,7 +1222,7 @@ static int idtcm_load_firmware(struct idtcm *idtcm, if (firmware) /* module parameter */ snprintf(fname, sizeof(fname), "%s", firmware); - dev_dbg(&idtcm->client->dev, "requesting firmware '%s'", fname); + dev_info(&idtcm->client->dev, "firmware '%s'", fname); err = request_firmware(&fw, fname, dev); if (err) { @@ -1354,7 +1356,7 @@ static int idtcm_perout_enable(struct idtcm_channel *channel, } static int idtcm_get_pll_mode(struct idtcm_channel *channel, - enum pll_mode *pll_mode) + enum pll_mode *mode) { struct idtcm *idtcm = channel->idtcm; int err; @@ -1366,13 +1368,13 @@ static int idtcm_get_pll_mode(struct idtcm_channel *channel, if (err) return err; - *pll_mode = (dpll_mode >> PLL_MODE_SHIFT) & PLL_MODE_MASK; + *mode = (dpll_mode >> PLL_MODE_SHIFT) & PLL_MODE_MASK; return 0; } static int idtcm_set_pll_mode(struct idtcm_channel *channel, - enum pll_mode pll_mode) + enum pll_mode mode) { struct idtcm *idtcm = channel->idtcm; int err; @@ -1386,23 +1388,298 @@ static int idtcm_set_pll_mode(struct idtcm_channel *channel, dpll_mode &= ~(PLL_MODE_MASK << PLL_MODE_SHIFT); - dpll_mode |= (pll_mode << PLL_MODE_SHIFT); - - channel->pll_mode = pll_mode; + dpll_mode |= (mode << PLL_MODE_SHIFT); err = idtcm_write(idtcm, channel->dpll_n, IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_MODE), &dpll_mode, sizeof(dpll_mode)); + return err; +} + +static int idtcm_get_manual_reference(struct idtcm_channel *channel, + enum manual_reference *ref) +{ + struct idtcm *idtcm = channel->idtcm; + u8 dpll_manu_ref_cfg; + int err; + + err = idtcm_read(idtcm, channel->dpll_ctrl_n, + DPLL_CTRL_DPLL_MANU_REF_CFG, + &dpll_manu_ref_cfg, sizeof(dpll_manu_ref_cfg)); if (err) return err; + dpll_manu_ref_cfg &= (MANUAL_REFERENCE_MASK << MANUAL_REFERENCE_SHIFT); + + *ref = dpll_manu_ref_cfg >> MANUAL_REFERENCE_SHIFT; + return 0; } +static int idtcm_set_manual_reference(struct idtcm_channel *channel, + enum manual_reference ref) +{ + struct idtcm *idtcm = channel->idtcm; + u8 dpll_manu_ref_cfg; + int err; + + err = idtcm_read(idtcm, channel->dpll_ctrl_n, + DPLL_CTRL_DPLL_MANU_REF_CFG, + &dpll_manu_ref_cfg, sizeof(dpll_manu_ref_cfg)); + if (err) + return err; + + dpll_manu_ref_cfg &= ~(MANUAL_REFERENCE_MASK << MANUAL_REFERENCE_SHIFT); + + dpll_manu_ref_cfg |= (ref << MANUAL_REFERENCE_SHIFT); + + err = idtcm_write(idtcm, channel->dpll_ctrl_n, + DPLL_CTRL_DPLL_MANU_REF_CFG, + &dpll_manu_ref_cfg, sizeof(dpll_manu_ref_cfg)); + + return err; +} + +static int configure_dpll_mode_write_frequency(struct idtcm_channel *channel) +{ + struct idtcm *idtcm = channel->idtcm; + int err; + + err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_FREQUENCY); + + if (err) + dev_err(&idtcm->client->dev, "Failed to set pll mode to write frequency"); + else + channel->mode = PTP_PLL_MODE_WRITE_FREQUENCY; + + return err; +} + +static int configure_dpll_mode_write_phase(struct idtcm_channel *channel) +{ + struct idtcm *idtcm = channel->idtcm; + int err; + + err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_PHASE); + + if (err) + dev_err(&idtcm->client->dev, "Failed to set pll mode to write phase"); + else + channel->mode = PTP_PLL_MODE_WRITE_PHASE; + + return err; +} + +static int configure_manual_reference_write_frequency(struct idtcm_channel *channel) +{ + struct idtcm *idtcm = channel->idtcm; + int err; + + err = idtcm_set_manual_reference(channel, MANU_REF_WRITE_FREQUENCY); + + if (err) + dev_err(&idtcm->client->dev, "Failed to set manual reference to write frequency"); + else + channel->mode = PTP_PLL_MODE_WRITE_FREQUENCY; + + return err; +} + +static int configure_manual_reference_write_phase(struct idtcm_channel *channel) +{ + struct idtcm *idtcm = channel->idtcm; + int err; + + err = idtcm_set_manual_reference(channel, MANU_REF_WRITE_PHASE); + + if (err) + dev_err(&idtcm->client->dev, "Failed to set manual reference to write phase"); + else + channel->mode = PTP_PLL_MODE_WRITE_PHASE; + + return err; +} + +static int idtcm_stop_phase_pull_in(struct idtcm_channel *channel) +{ + int err; + + err = _idtcm_adjfine(channel, channel->current_freq_scaled_ppm); + if (err) + return err; + + channel->phase_pull_in = false; + + return 0; +} + +static long idtcm_work_handler(struct ptp_clock_info *ptp) +{ + struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps); + struct idtcm *idtcm = channel->idtcm; + + mutex_lock(&idtcm->reg_lock); + + (void)idtcm_stop_phase_pull_in(channel); + + mutex_unlock(&idtcm->reg_lock); + + /* Return a negative value here to not reschedule */ + return -1; +} + +static s32 phase_pull_in_scaled_ppm(s32 current_ppm, s32 phase_pull_in_ppb) +{ + /* ppb = scaled_ppm * 125 / 2^13 */ + /* scaled_ppm = ppb * 2^13 / 125 */ + + s64 max_scaled_ppm = (PHASE_PULL_IN_MAX_PPB << 13) / 125; + s64 scaled_ppm = (phase_pull_in_ppb << 13) / 125; + + current_ppm += scaled_ppm; + + if (current_ppm > max_scaled_ppm) + current_ppm = max_scaled_ppm; + else if (current_ppm < -max_scaled_ppm) + current_ppm = -max_scaled_ppm; + + return current_ppm; +} + +static int do_phase_pull_in_sw(struct idtcm_channel *channel, + s32 delta_ns, + u32 max_ffo_ppb) +{ + s32 current_ppm = channel->current_freq_scaled_ppm; + u32 duration_ms = MSEC_PER_SEC; + s32 delta_ppm; + s32 ppb; + int err; + + /* If the ToD correction is less than PHASE_PULL_IN_MIN_THRESHOLD_NS, + * skip. The error introduced by the ToD adjustment procedure would + * be bigger than the required ToD correction + */ + if (abs(delta_ns) < PHASE_PULL_IN_MIN_THRESHOLD_NS) + return 0; + + if (max_ffo_ppb == 0) + max_ffo_ppb = PHASE_PULL_IN_MAX_PPB; + + /* For most cases, keep phase pull-in duration 1 second */ + ppb = delta_ns; + while (abs(ppb) > max_ffo_ppb) { + duration_ms *= 2; + ppb /= 2; + } + + delta_ppm = phase_pull_in_scaled_ppm(current_ppm, ppb); + + err = _idtcm_adjfine(channel, delta_ppm); + + if (err) + return err; + + /* schedule the worker to cancel phase pull-in */ + ptp_schedule_worker(channel->ptp_clock, + msecs_to_jiffies(duration_ms) - 1); + + channel->phase_pull_in = true; + + return 0; +} + +static int initialize_operating_mode_with_manual_reference(struct idtcm_channel *channel, + enum manual_reference ref) +{ + struct idtcm *idtcm = channel->idtcm; + + channel->mode = PTP_PLL_MODE_UNSUPPORTED; + channel->configure_write_frequency = configure_manual_reference_write_frequency; + channel->configure_write_phase = configure_manual_reference_write_phase; + channel->do_phase_pull_in = do_phase_pull_in_sw; + + switch (ref) { + case MANU_REF_WRITE_PHASE: + channel->mode = PTP_PLL_MODE_WRITE_PHASE; + break; + case MANU_REF_WRITE_FREQUENCY: + channel->mode = PTP_PLL_MODE_WRITE_FREQUENCY; + break; + default: + dev_warn(&idtcm->client->dev, + "Unsupported MANUAL_REFERENCE: 0x%02x", ref); + } + + return 0; +} + +static int initialize_operating_mode_with_pll_mode(struct idtcm_channel *channel, + enum pll_mode mode) +{ + struct idtcm *idtcm = channel->idtcm; + int err = 0; + + channel->mode = PTP_PLL_MODE_UNSUPPORTED; + channel->configure_write_frequency = configure_dpll_mode_write_frequency; + channel->configure_write_phase = configure_dpll_mode_write_phase; + channel->do_phase_pull_in = do_phase_pull_in_fw; + + switch (mode) { + case PLL_MODE_WRITE_PHASE: + channel->mode = PTP_PLL_MODE_WRITE_PHASE; + break; + case PLL_MODE_WRITE_FREQUENCY: + channel->mode = PTP_PLL_MODE_WRITE_FREQUENCY; + break; + default: + dev_err(&idtcm->client->dev, + "Unsupported PLL_MODE: 0x%02x", mode); + err = -EINVAL; + } + + return err; +} + +static int initialize_dco_operating_mode(struct idtcm_channel *channel) +{ + enum manual_reference ref = MANU_REF_XO_DPLL; + enum pll_mode mode = PLL_MODE_DISABLED; + struct idtcm *idtcm = channel->idtcm; + int err; + + channel->mode = PTP_PLL_MODE_UNSUPPORTED; + + err = idtcm_get_pll_mode(channel, &mode); + if (err) { + dev_err(&idtcm->client->dev, "Unable to read pll mode!"); + return err; + } + + if (mode == PLL_MODE_PLL) { + err = idtcm_get_manual_reference(channel, &ref); + if (err) { + dev_err(&idtcm->client->dev, "Unable to read manual reference!"); + return err; + } + err = initialize_operating_mode_with_manual_reference(channel, ref); + } else { + err = initialize_operating_mode_with_pll_mode(channel, mode); + } + + if (channel->mode == PTP_PLL_MODE_WRITE_PHASE) + channel->configure_write_frequency(channel); + + return err; +} + /* PTP Hardware Clock interface */ /** - * @brief Maximum absolute value for write phase offset in picoseconds + * Maximum absolute value for write phase offset in picoseconds + * + * @channel: channel + * @delta_ns: delta in nanoseconds * * Destination signed register is 32-bit register in resolution of 50ps * @@ -1417,8 +1694,8 @@ static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns) s32 phase_50ps; s64 offset_ps; - if (channel->pll_mode != PLL_MODE_WRITE_PHASE) { - err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_PHASE); + if (channel->mode != PTP_PLL_MODE_WRITE_PHASE) { + err = channel->configure_write_phase(channel); if (err) return err; } @@ -1456,8 +1733,8 @@ static int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm) u8 buf[6] = {0}; s64 fcw; - if (channel->pll_mode != PLL_MODE_WRITE_FREQUENCY) { - err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_FREQUENCY); + if (channel->mode != PTP_PLL_MODE_WRITE_FREQUENCY) { + err = channel->configure_write_frequency(channel); if (err) return err; } @@ -1574,29 +1851,29 @@ static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta) enum scsr_tod_write_type_sel type; int err; - if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS) { - err = idtcm_do_phase_pull_in(channel, delta, 0); - if (err) - dev_err(&idtcm->client->dev, - "Failed at line %d in %s!", __LINE__, __func__); - return err; - } - - if (delta >= 0) { - ts = ns_to_timespec64(delta); - type = SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS; - } else { - ts = ns_to_timespec64(-delta); - type = SCSR_TOD_WR_TYPE_SEL_DELTA_MINUS; - } + if (channel->phase_pull_in == true) + return 0; mutex_lock(&idtcm->reg_lock); - err = _idtcm_settime(channel, &ts, type); - if (err) - dev_err(&idtcm->client->dev, - "Failed at line %d in %s!", __LINE__, __func__); - + if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS) { + err = channel->do_phase_pull_in(channel, delta, 0); + if (err) + dev_err(&idtcm->client->dev, + "Failed at line %d in %s!", __LINE__, __func__); + } else { + if (delta >= 0) { + ts = ns_to_timespec64(delta); + type = SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS; + } else { + ts = ns_to_timespec64(-delta); + type = SCSR_TOD_WR_TYPE_SEL_DELTA_MINUS; + } + err = _idtcm_settime(channel, &ts, type); + if (err) + dev_err(&idtcm->client->dev, + "Failed at line %d in %s!", __LINE__, __func__); + } mutex_unlock(&idtcm->reg_lock); return err; @@ -1626,15 +1903,21 @@ static int idtcm_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) struct idtcm *idtcm = channel->idtcm; int err; + if (channel->phase_pull_in == true) + return 0; + + if (scaled_ppm == channel->current_freq_scaled_ppm) + return 0; + mutex_lock(&idtcm->reg_lock); err = _idtcm_adjfine(channel, scaled_ppm); - if (err) - dev_err(&idtcm->client->dev, - "Failed at line %d in %s!", __LINE__, __func__); mutex_unlock(&idtcm->reg_lock); + if (!err) + channel->current_freq_scaled_ppm = scaled_ppm; + return err; } @@ -1746,6 +2029,7 @@ static const struct ptp_clock_info idtcm_caps = { .gettime64 = &idtcm_gettime, .settime64 = &idtcm_settime, .enable = &idtcm_enable, + .do_aux_work = &idtcm_work_handler, }; static const struct ptp_clock_info idtcm_caps_deprecated = { @@ -1758,6 +2042,7 @@ static const struct ptp_clock_info idtcm_caps_deprecated = { .gettime64 = &idtcm_gettime, .settime64 = &idtcm_settime_deprecated, .enable = &idtcm_enable, + .do_aux_work = &idtcm_work_handler, }; static int configure_channel_pll(struct idtcm_channel *channel) @@ -1847,7 +2132,9 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index) return -EINVAL; channel = &idtcm->channel[index]; + channel->idtcm = idtcm; + channel->current_freq_scaled_ppm = 0; /* Set pll addresses */ err = configure_channel_pll(channel); @@ -1892,13 +2179,9 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index) snprintf(channel->caps.name, sizeof(channel->caps.name), "IDT CM TOD%u", index); - /* Sync pll mode with hardware */ - err = idtcm_get_pll_mode(channel, &channel->pll_mode); - if (err) { - dev_err(&idtcm->client->dev, - "Error: %s - Unable to read pll mode", __func__); + err = initialize_dco_operating_mode(channel); + if (err) return err; - } err = idtcm_enable_tod(channel); if (err) { @@ -1931,7 +2214,6 @@ static void ptp_clock_unregister_all(struct idtcm *idtcm) for (i = 0; i < MAX_TOD; i++) { channel = &idtcm->channel[i]; - if (channel->ptp_clock) ptp_clock_unregister(channel->ptp_clock); } diff --git a/drivers/ptp/ptp_clockmatrix.h b/drivers/ptp/ptp_clockmatrix.h index 843a9d7129c5..833e5907c351 100644 --- a/drivers/ptp/ptp_clockmatrix.h +++ b/drivers/ptp/ptp_clockmatrix.h @@ -57,15 +57,27 @@ #define IDTCM_MAX_WRITE_COUNT (512) +#define PHASE_PULL_IN_MAX_PPB (144000) +#define PHASE_PULL_IN_MIN_THRESHOLD_NS (2) + /* * Return register address based on passed in firmware version */ #define IDTCM_FW_REG(FW, VER, REG) (((FW) < (VER)) ? (REG) : (REG##_##VER)) +/* PTP PLL Mode */ +enum ptp_pll_mode { + PTP_PLL_MODE_MIN = 0, + PTP_PLL_MODE_WRITE_FREQUENCY = PTP_PLL_MODE_MIN, + PTP_PLL_MODE_WRITE_PHASE, + PTP_PLL_MODE_UNSUPPORTED, + PTP_PLL_MODE_MAX = PTP_PLL_MODE_UNSUPPORTED, +}; + /* Values of DPLL_N.DPLL_MODE.PLL_MODE */ enum pll_mode { PLL_MODE_MIN = 0, - PLL_MODE_NORMAL = PLL_MODE_MIN, + PLL_MODE_PLL = PLL_MODE_MIN, PLL_MODE_WRITE_PHASE = 1, PLL_MODE_WRITE_FREQUENCY = 2, PLL_MODE_GPIO_INC_DEC = 3, @@ -75,6 +87,31 @@ enum pll_mode { PLL_MODE_MAX = PLL_MODE_DISABLED, }; +/* Values of DPLL_CTRL_n.DPLL_MANU_REF_CFG.MANUAL_REFERENCE */ +enum manual_reference { + MANU_REF_MIN = 0, + MANU_REF_CLK0 = MANU_REF_MIN, + MANU_REF_CLK1, + MANU_REF_CLK2, + MANU_REF_CLK3, + MANU_REF_CLK4, + MANU_REF_CLK5, + MANU_REF_CLK6, + MANU_REF_CLK7, + MANU_REF_CLK8, + MANU_REF_CLK9, + MANU_REF_CLK10, + MANU_REF_CLK11, + MANU_REF_CLK12, + MANU_REF_CLK13, + MANU_REF_CLK14, + MANU_REF_CLK15, + MANU_REF_WRITE_PHASE, + MANU_REF_WRITE_FREQUENCY, + MANU_REF_XO_DPLL, + MANU_REF_MAX = MANU_REF_XO_DPLL, +}; + enum hw_tod_write_trig_sel { HW_TOD_WR_TRIG_SEL_MIN = 0, HW_TOD_WR_TRIG_SEL_MSB = HW_TOD_WR_TRIG_SEL_MIN, @@ -141,7 +178,13 @@ struct idtcm_channel { u16 tod_n; u16 hw_dpll_n; u8 sync_src; - enum pll_mode pll_mode; + enum ptp_pll_mode mode; + int (*configure_write_frequency)(struct idtcm_channel *channel); + int (*configure_write_phase)(struct idtcm_channel *channel); + int (*do_phase_pull_in)(struct idtcm_channel *channel, + s32 offset_ns, u32 max_ffo_ppb); + s32 current_freq_scaled_ppm; + bool phase_pull_in; u8 pll; u16 output_mask; }; From 0f440524b69787ec2a843bc474a786beabc0bfa9 Mon Sep 17 00:00:00 2001 From: M Chetan Kumar Date: Tue, 14 Sep 2021 11:18:01 +0530 Subject: [PATCH 31/87] net: wwan: iosm: fix linux-next build error Removed "stdbool.h" inclusion in iosm_ipc_imem.h Fixes: 13bb8429ca98 ("net: wwan: iosm: firmware flashing and coredump collection") Reported-by: Stephen Rothwell Signed-off-by: M Chetan Kumar Signed-off-by: David S. Miller --- drivers/net/wwan/iosm/iosm_ipc_imem.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem.h b/drivers/net/wwan/iosm/iosm_ipc_imem.h index 8380008232be..6be6708b4eec 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_imem.h +++ b/drivers/net/wwan/iosm/iosm_ipc_imem.h @@ -7,7 +7,6 @@ #define IOSM_IPC_IMEM_H #include -#include #include "iosm_ipc_mmio.h" #include "iosm_ipc_pcie.h" From b9bbc4c1debc837ba56872fb3b2499ba6459ca8b Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Mon, 13 Sep 2021 21:58:26 +0200 Subject: [PATCH 32/87] ethtool: prevent endless loop if eeprom size is smaller than announced It shouldn't happen, but can happen that readable eeprom size is smaller than announced. Then we would be stuck in an endless loop here because after reaching the actual end reads return eeprom.len = 0. I faced this issue when making a mistake in driver development. Detect this scenario and return an error. Signed-off-by: Heiner Kallweit Signed-off-by: David S. Miller --- net/ethtool/ioctl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index f2abc3152888..999e2a6bed13 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -1537,6 +1537,10 @@ static int ethtool_get_any_eeprom(struct net_device *dev, void __user *useraddr, ret = getter(dev, &eeprom, data); if (ret) break; + if (!eeprom.len) { + ret = -EIO; + break; + } if (copy_to_user(userbuf, data, eeprom.len)) { ret = -EFAULT; break; From 32e3573f739209e612ea5c98291251b00cc6f70e Mon Sep 17 00:00:00 2001 From: Yajun Deng Date: Tue, 14 Sep 2021 11:49:35 +0800 Subject: [PATCH 33/87] skbuff: inline page_frag_alloc_align() The __alloc_frag_align() is short, and only called by two functions, so inline page_frag_alloc_align() for reduce the overhead of calls. Reported-by: kernel test robot Signed-off-by: Yajun Deng Signed-off-by: David S. Miller --- net/core/skbuff.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 2170bea2c7de..7c2ab27fcbf9 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -134,34 +134,31 @@ struct napi_alloc_cache { static DEFINE_PER_CPU(struct page_frag_cache, netdev_alloc_cache); static DEFINE_PER_CPU(struct napi_alloc_cache, napi_alloc_cache); -static void *__alloc_frag_align(unsigned int fragsz, gfp_t gfp_mask, - unsigned int align_mask) +void *__napi_alloc_frag_align(unsigned int fragsz, unsigned int align_mask) { struct napi_alloc_cache *nc = this_cpu_ptr(&napi_alloc_cache); - return page_frag_alloc_align(&nc->page, fragsz, gfp_mask, align_mask); -} - -void *__napi_alloc_frag_align(unsigned int fragsz, unsigned int align_mask) -{ fragsz = SKB_DATA_ALIGN(fragsz); - return __alloc_frag_align(fragsz, GFP_ATOMIC, align_mask); + return page_frag_alloc_align(&nc->page, fragsz, GFP_ATOMIC, align_mask); } EXPORT_SYMBOL(__napi_alloc_frag_align); void *__netdev_alloc_frag_align(unsigned int fragsz, unsigned int align_mask) { - struct page_frag_cache *nc; void *data; fragsz = SKB_DATA_ALIGN(fragsz); if (in_hardirq() || irqs_disabled()) { - nc = this_cpu_ptr(&netdev_alloc_cache); + struct page_frag_cache *nc = this_cpu_ptr(&netdev_alloc_cache); + data = page_frag_alloc_align(nc, fragsz, GFP_ATOMIC, align_mask); } else { + struct napi_alloc_cache *nc; + local_bh_disable(); - data = __alloc_frag_align(fragsz, GFP_ATOMIC, align_mask); + nc = this_cpu_ptr(&napi_alloc_cache); + data = page_frag_alloc_align(&nc->page, fragsz, GFP_ATOMIC, align_mask); local_bh_enable(); } return data; From 0ccf8511182436183c031e8a2f740ae91a02c625 Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Tue, 14 Sep 2021 14:33:45 +0200 Subject: [PATCH 34/87] net: phy: at803x: add support for qca 8327 internal phy Add support for qca8327 internal phy needed for correct init of the switch port. It does use the same qca8337 function and reg just with a different id. Signed-off-by: Ansuel Smith Tested-by: Rosen Penev Tested-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/at803x.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index bdac087058b2..719860a93d7c 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -1420,6 +1420,19 @@ static struct phy_driver at803x_driver[] = { .get_sset_count = at803x_get_sset_count, .get_strings = at803x_get_strings, .get_stats = at803x_get_stats, +}, { + /* QCA8327 */ + .phy_id = QCA8327_PHY_ID, + .phy_id_mask = QCA8K_PHY_ID_MASK, + .name = "QCA PHY 8327", + /* PHY_GBIT_FEATURES */ + .probe = at803x_probe, + .flags = PHY_IS_INTERNAL, + .config_init = qca83xx_config_init, + .soft_reset = genphy_soft_reset, + .get_sset_count = at803x_get_sset_count, + .get_strings = at803x_get_strings, + .get_stats = at803x_get_stats, }, }; module_phy_driver(at803x_driver); @@ -1430,6 +1443,8 @@ static struct mdio_device_id __maybe_unused atheros_tbl[] = { { PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) }, { PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) }, { PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) }, + { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) }, + { PHY_ID_MATCH_EXACT(QCA8327_PHY_ID) }, { } }; From e435a6b5315a05a4e4e9f77679a57fd0d679e384 Mon Sep 17 00:00:00 2001 From: Guangbin Huang Date: Tue, 14 Sep 2021 20:11:16 +0800 Subject: [PATCH 35/87] net: hns3: PF support get unicast MAC address space assigned by firmware Currently, there are two ways for PF to set the unicast MAC address space size: specified by config parameters in firmware or set to default value. That's mean if the config parameters in firmware is zero, driver will divide the whole unicast MAC address space equally to 8 PFs. However, in this case, the unicast MAC address space will be wasted a lot when the hardware actually has less then 8 PFs. And in the other hand, if one PF has much more VFs than other PFs, then each function of this PF will has much less address space than other PFs. In order to ameliorate the above two situations, introduce the third way of unicast MAC address space assignment: firmware divides the whole unicast MAC address space equally to functions of all PFs, and calculates the space size of each PF according to its function number. PF queries the space size by the querying device specification command when in initialization process. The third way assignment is lower priority than specified by config parameters, only if the config parameters is zero can be used, and if firmware does not support the third way assignment, then driver still divides the whole unicast MAC address space equally to 8 PFs. Signed-off-by: Guangbin Huang Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns3/hnae3.h | 1 + drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c | 2 ++ .../net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h | 4 +++- .../net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 11 ++++++++--- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h index 546a60530384..b100b63b13db 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h +++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h @@ -341,6 +341,7 @@ struct hnae3_dev_specs { u8 max_non_tso_bd_num; /* max BD number of one non-TSO packet */ u16 max_frm_size; u16 max_qset_num; + u16 umv_size; }; struct hnae3_client_ops { diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c index 2b66c59f5eaf..d297f22f5af9 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c @@ -924,6 +924,8 @@ hns3_dbg_dev_specs(struct hnae3_handle *h, char *buf, int len, int *pos) dev_specs->max_tm_rate); *pos += scnprintf(buf + *pos, len - *pos, "MAX QSET number: %u\n", dev_specs->max_qset_num); + *pos += scnprintf(buf + *pos, len - *pos, "umv size: %u\n", + dev_specs->umv_size); } static int hns3_dbg_dev_info(struct hnae3_handle *h, char *buf, int len) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h index 33244472e0d0..cfbb7c51b0cb 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h @@ -1188,7 +1188,9 @@ struct hclge_dev_specs_1_cmd { __le16 max_frm_size; __le16 max_qset_num; __le16 max_int_gl; - u8 rsv1[18]; + u8 rsv0[2]; + __le16 umv_size; + u8 rsv1[14]; }; /* mac speed type defined in firmware command */ diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index e55ba2e511b1..82deba80adfb 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -1342,8 +1342,6 @@ static void hclge_parse_cfg(struct hclge_cfg *cfg, struct hclge_desc *desc) cfg->umv_space = hnae3_get_field(__le32_to_cpu(req->param[1]), HCLGE_CFG_UMV_TBL_SPACE_M, HCLGE_CFG_UMV_TBL_SPACE_S); - if (!cfg->umv_space) - cfg->umv_space = HCLGE_DEFAULT_UMV_SPACE_PER_PF; cfg->pf_rss_size_max = hnae3_get_field(__le32_to_cpu(req->param[2]), HCLGE_CFG_PF_RSS_SIZE_M, @@ -1419,6 +1417,7 @@ static void hclge_set_default_dev_specs(struct hclge_dev *hdev) ae_dev->dev_specs.max_int_gl = HCLGE_DEF_MAX_INT_GL; ae_dev->dev_specs.max_frm_size = HCLGE_MAC_MAX_FRAME; ae_dev->dev_specs.max_qset_num = HCLGE_MAX_QSET_NUM; + ae_dev->dev_specs.umv_size = HCLGE_DEFAULT_UMV_SPACE_PER_PF; } static void hclge_parse_dev_specs(struct hclge_dev *hdev, @@ -1440,6 +1439,7 @@ static void hclge_parse_dev_specs(struct hclge_dev *hdev, ae_dev->dev_specs.max_qset_num = le16_to_cpu(req1->max_qset_num); ae_dev->dev_specs.max_int_gl = le16_to_cpu(req1->max_int_gl); ae_dev->dev_specs.max_frm_size = le16_to_cpu(req1->max_frm_size); + ae_dev->dev_specs.umv_size = le16_to_cpu(req1->umv_size); } static void hclge_check_dev_specs(struct hclge_dev *hdev) @@ -1460,6 +1460,8 @@ static void hclge_check_dev_specs(struct hclge_dev *hdev) dev_specs->max_int_gl = HCLGE_DEF_MAX_INT_GL; if (!dev_specs->max_frm_size) dev_specs->max_frm_size = HCLGE_MAC_MAX_FRAME; + if (!dev_specs->umv_size) + dev_specs->umv_size = HCLGE_DEFAULT_UMV_SPACE_PER_PF; } static int hclge_query_dev_specs(struct hclge_dev *hdev) @@ -1548,7 +1550,10 @@ static int hclge_configure(struct hclge_dev *hdev) hdev->tm_info.num_pg = 1; hdev->tc_max = cfg.tc_num; hdev->tm_info.hw_pfc_map = 0; - hdev->wanted_umv_size = cfg.umv_space; + if (cfg.umv_space) + hdev->wanted_umv_size = cfg.umv_space; + else + hdev->wanted_umv_size = hdev->ae_dev->dev_specs.umv_size; hdev->tx_spare_buf_size = cfg.tx_spare_buf_size; hdev->gro_en = true; if (cfg.vlan_fliter_cap == HCLGE_VLAN_FLTR_CAN_MDF) From 5c56ff486dfcb95652cac7802bfd032f667d97ec Mon Sep 17 00:00:00 2001 From: Guangbin Huang Date: Tue, 14 Sep 2021 20:11:17 +0800 Subject: [PATCH 36/87] net: hns3: PF support get multicast MAC address space assigned by firmware The new firmware supports to divides the whole multicast MAC address space equally to functions of all PFs, and calculates the space size of each PF according to its function number. To support this feature, PF driver adds querying multicast MAC address space size from firmware and limits used number according to space size. Signed-off-by: Guangbin Huang Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns3/hnae3.h | 5 +++ .../ethernet/hisilicon/hns3/hns3_debugfs.c | 2 ++ .../hisilicon/hns3/hns3pf/hclge_cmd.h | 3 +- .../hisilicon/hns3/hns3pf/hclge_debugfs.c | 3 ++ .../hisilicon/hns3/hns3pf/hclge_main.c | 35 +++++++++++++++---- .../hisilicon/hns3/hns3pf/hclge_main.h | 2 ++ 6 files changed, 43 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h index b100b63b13db..6ccb0109412b 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h +++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h @@ -95,6 +95,7 @@ enum HNAE3_DEV_CAP_BITS { HNAE3_DEV_SUPPORT_RXD_ADV_LAYOUT_B, HNAE3_DEV_SUPPORT_PORT_VLAN_BYPASS_B, HNAE3_DEV_SUPPORT_VLAN_FLTR_MDF_B, + HNAE3_DEV_SUPPORT_MC_MAC_MNG_B, }; #define hnae3_dev_fd_supported(hdev) \ @@ -151,6 +152,9 @@ enum HNAE3_DEV_CAP_BITS { #define hnae3_ae_dev_rxd_adv_layout_supported(ae_dev) \ test_bit(HNAE3_DEV_SUPPORT_RXD_ADV_LAYOUT_B, (ae_dev)->caps) +#define hnae3_ae_dev_mc_mac_mng_supported(ae_dev) \ + test_bit(HNAE3_DEV_SUPPORT_MC_MAC_MNG_B, (ae_dev)->caps) + enum HNAE3_PF_CAP_BITS { HNAE3_PF_SUPPORT_VLAN_FLTR_MDF_B = 0, }; @@ -342,6 +346,7 @@ struct hnae3_dev_specs { u16 max_frm_size; u16 max_qset_num; u16 umv_size; + u16 mc_mac_size; }; struct hnae3_client_ops { diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c index d297f22f5af9..a1555f074e06 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c @@ -926,6 +926,8 @@ hns3_dbg_dev_specs(struct hnae3_handle *h, char *buf, int len, int *pos) dev_specs->max_qset_num); *pos += scnprintf(buf + *pos, len - *pos, "umv size: %u\n", dev_specs->umv_size); + *pos += scnprintf(buf + *pos, len - *pos, "mc mac size: %u\n", + dev_specs->mc_mac_size); } static int hns3_dbg_dev_info(struct hnae3_handle *h, char *buf, int len) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h index cfbb7c51b0cb..bfcfefa9d2b5 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h @@ -1190,7 +1190,8 @@ struct hclge_dev_specs_1_cmd { __le16 max_int_gl; u8 rsv0[2]; __le16 umv_size; - u8 rsv1[14]; + __le16 mc_mac_size; + u8 rsv1[12]; }; /* mac speed type defined in firmware command */ diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c index 68ed1715ac52..61d30b2961f4 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c @@ -1968,6 +1968,9 @@ static int hclge_dbg_dump_umv_info(struct hclge_dev *hdev, char *buf, int len) } mutex_unlock(&hdev->vport_lock); + pos += scnprintf(buf + pos, len - pos, "used_mc_mac_num : %u\n", + hdev->used_mc_mac_num); + return 0; } diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 82deba80adfb..6d5f84666fa5 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -1440,6 +1440,7 @@ static void hclge_parse_dev_specs(struct hclge_dev *hdev, ae_dev->dev_specs.max_int_gl = le16_to_cpu(req1->max_int_gl); ae_dev->dev_specs.max_frm_size = le16_to_cpu(req1->max_frm_size); ae_dev->dev_specs.umv_size = le16_to_cpu(req1->umv_size); + ae_dev->dev_specs.mc_mac_size = le16_to_cpu(req1->mc_mac_size); } static void hclge_check_dev_specs(struct hclge_dev *hdev) @@ -8480,6 +8481,9 @@ static int hclge_init_umv_space(struct hclge_dev *hdev) hdev->share_umv_size = hdev->priv_umv_size + hdev->max_umv_size % (hdev->num_alloc_vport + 1); + if (hdev->ae_dev->dev_specs.mc_mac_size) + set_bit(HNAE3_DEV_SUPPORT_MC_MAC_MNG_B, hdev->ae_dev->caps); + return 0; } @@ -8497,6 +8501,8 @@ static void hclge_reset_umv_space(struct hclge_dev *hdev) hdev->share_umv_size = hdev->priv_umv_size + hdev->max_umv_size % (hdev->num_alloc_vport + 1); mutex_unlock(&hdev->vport_lock); + + hdev->used_mc_mac_num = 0; } static bool hclge_is_umv_space_full(struct hclge_vport *vport, bool need_lock) @@ -8758,6 +8764,7 @@ int hclge_add_mc_addr_common(struct hclge_vport *vport, struct hclge_dev *hdev = vport->back; struct hclge_mac_vlan_tbl_entry_cmd req; struct hclge_desc desc[3]; + bool is_new_addr = false; int status; /* mac addr check */ @@ -8771,6 +8778,13 @@ int hclge_add_mc_addr_common(struct hclge_vport *vport, hclge_prepare_mac_addr(&req, addr, true); status = hclge_lookup_mac_vlan_tbl(vport, &req, desc, true); if (status) { + if (hnae3_ae_dev_mc_mac_mng_supported(hdev->ae_dev) && + hdev->used_mc_mac_num >= + hdev->ae_dev->dev_specs.mc_mac_size) + goto err_no_space; + + is_new_addr = true; + /* This mac addr do not exist, add new entry for it */ memset(desc[0].data, 0, sizeof(desc[0].data)); memset(desc[1].data, 0, sizeof(desc[0].data)); @@ -8780,12 +8794,18 @@ int hclge_add_mc_addr_common(struct hclge_vport *vport, if (status) return status; status = hclge_add_mac_vlan_tbl(vport, &req, desc); - /* if already overflow, not to print each time */ - if (status == -ENOSPC && - !(vport->overflow_promisc_flags & HNAE3_OVERFLOW_MPE)) - dev_err(&hdev->pdev->dev, "mc mac vlan table is full\n"); + if (status == -ENOSPC) + goto err_no_space; + else if (!status && is_new_addr) + hdev->used_mc_mac_num++; return status; + +err_no_space: + /* if already overflow, not to print each time */ + if (!(vport->overflow_promisc_flags & HNAE3_OVERFLOW_MPE)) + dev_err(&hdev->pdev->dev, "mc mac vlan table is full\n"); + return -ENOSPC; } static int hclge_rm_mc_addr(struct hnae3_handle *handle, @@ -8822,12 +8842,15 @@ int hclge_rm_mc_addr_common(struct hclge_vport *vport, if (status) return status; - if (hclge_is_all_function_id_zero(desc)) + if (hclge_is_all_function_id_zero(desc)) { /* All the vfid is zero, so need to delete this entry */ status = hclge_remove_mac_vlan_tbl(vport, &req); - else + if (!status) + hdev->used_mc_mac_num--; + } else { /* Not all the vfid is zero, update the vfid */ status = hclge_add_mac_vlan_tbl(vport, &req, desc); + } } else if (status == -ENOENT) { status = 0; } diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h index de6afbcbfbac..ca25e2edf3f0 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h @@ -938,6 +938,8 @@ struct hclge_dev { u16 priv_umv_size; /* unicast mac vlan space shared by PF and its VFs */ u16 share_umv_size; + /* multicast mac address number used by PF and its VFs */ + u16 used_mc_mac_num; DECLARE_KFIFO(mac_tnl_log, struct hclge_mac_tnl_stats, HCLGE_MAC_TNL_LOG_SIZE); From 339133f6c318612f9a4556c300753beda27abc01 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 13 Sep 2021 16:31:56 +0200 Subject: [PATCH 37/87] net: dsa: tag_rtl4_a: Drop bit 9 from egress frames This drops the code setting bit 9 on egress frames on the Realtek "type A" (RTL8366RB) frames. This bit was set on ingress frames for unknown reason, and was set on egress frames as the format of ingress and egress frames was believed to be the same. As that assumption turned out to be false, and since this bit seems to have zero effect on the behaviour of the switch let's drop this bit entirely. Signed-off-by: Linus Walleij Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20210913143156.1264570-1-linus.walleij@linaro.org Signed-off-by: Jakub Kicinski --- net/dsa/tag_rtl4_a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/dsa/tag_rtl4_a.c b/net/dsa/tag_rtl4_a.c index f920487ae145..6d928ee3ef7a 100644 --- a/net/dsa/tag_rtl4_a.c +++ b/net/dsa/tag_rtl4_a.c @@ -54,7 +54,7 @@ static struct sk_buff *rtl4a_tag_xmit(struct sk_buff *skb, p = (__be16 *)tag; *p = htons(RTL4_A_ETHERTYPE); - out = (RTL4_A_PROTOCOL_RTL8366RB << RTL4_A_PROTOCOL_SHIFT) | (2 << 8); + out = (RTL4_A_PROTOCOL_RTL8366RB << RTL4_A_PROTOCOL_SHIFT); /* The lower bits indicate the port number */ out |= BIT(dp->index); From c12aa581f6d5e80c3c3675ab26a52c2b3b62f76e Mon Sep 17 00:00:00 2001 From: Aleksander Jan Bajkowski Date: Tue, 14 Sep 2021 23:20:58 +0200 Subject: [PATCH 38/87] MIPS: lantiq: dma: add small delay after reset Reading the DMA registers immediately after the reset causes Data Bus Error. Adding a small delay fixes this issue. Signed-off-by: Aleksander Jan Bajkowski Signed-off-by: David S. Miller --- arch/mips/lantiq/xway/dma.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/mips/lantiq/xway/dma.c b/arch/mips/lantiq/xway/dma.c index 63dccb2ed08b..2784715933d1 100644 --- a/arch/mips/lantiq/xway/dma.c +++ b/arch/mips/lantiq/xway/dma.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -222,6 +223,8 @@ ltq_dma_init(struct platform_device *pdev) clk_enable(clk); ltq_dma_w32_mask(0, DMA_RESET, LTQ_DMA_CTRL); + usleep_range(1, 10); + /* disable all interrupts */ ltq_dma_w32(0, LTQ_DMA_IRNEN); From 5ca9ce2ba4d5884cd94d1a856c675ab1242cd242 Mon Sep 17 00:00:00 2001 From: Aleksander Jan Bajkowski Date: Tue, 14 Sep 2021 23:20:59 +0200 Subject: [PATCH 39/87] MIPS: lantiq: dma: reset correct number of channel Different SoCs have a different number of channels, e.g .: * amazon-se has 10 channels, * danube+ar9 have 20 channels, * vr9 has 28 channels, * ar10 has 24 channels. We can read the ID register and, depending on the reported number of channels, reset the appropriate number of channels. Signed-off-by: Aleksander Jan Bajkowski Signed-off-by: David S. Miller --- arch/mips/lantiq/xway/dma.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/mips/lantiq/xway/dma.c b/arch/mips/lantiq/xway/dma.c index 2784715933d1..364ab39eb8a4 100644 --- a/arch/mips/lantiq/xway/dma.c +++ b/arch/mips/lantiq/xway/dma.c @@ -31,6 +31,7 @@ #define LTQ_DMA_PCTRL 0x44 #define LTQ_DMA_IRNEN 0xf4 +#define DMA_ID_CHNR GENMASK(26, 20) /* channel number */ #define DMA_DESCPT BIT(3) /* descriptor complete irq */ #define DMA_TX BIT(8) /* TX channel direction */ #define DMA_CHAN_ON BIT(0) /* channel on / off bit */ @@ -41,7 +42,6 @@ #define DMA_POLL BIT(31) /* turn on channel polling */ #define DMA_CLK_DIV4 BIT(6) /* polling clock divider */ #define DMA_2W_BURST BIT(1) /* 2 word burst length */ -#define DMA_MAX_CHANNEL 20 /* the soc has 20 channels */ #define DMA_ETOP_ENDIANNESS (0xf << 8) /* endianness swap etop channels */ #define DMA_WEIGHT (BIT(17) | BIT(16)) /* default channel wheight */ @@ -207,7 +207,7 @@ ltq_dma_init(struct platform_device *pdev) { struct clk *clk; struct resource *res; - unsigned id; + unsigned int id, nchannels; int i; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -229,17 +229,18 @@ ltq_dma_init(struct platform_device *pdev) ltq_dma_w32(0, LTQ_DMA_IRNEN); /* reset/configure each channel */ - for (i = 0; i < DMA_MAX_CHANNEL; i++) { + id = ltq_dma_r32(LTQ_DMA_ID); + nchannels = ((id & DMA_ID_CHNR) >> 20); + for (i = 0; i < nchannels; i++) { ltq_dma_w32(i, LTQ_DMA_CS); ltq_dma_w32(DMA_CHAN_RST, LTQ_DMA_CCTRL); ltq_dma_w32(DMA_POLL | DMA_CLK_DIV4, LTQ_DMA_CPOLL); ltq_dma_w32_mask(DMA_CHAN_ON, 0, LTQ_DMA_CCTRL); } - id = ltq_dma_r32(LTQ_DMA_ID); dev_info(&pdev->dev, "Init done - hw rev: %X, ports: %d, channels: %d\n", - id & 0x1f, (id >> 16) & 0xf, id >> 20); + id & 0x1f, (id >> 16) & 0xf, nchannels); return 0; } From 5ad74d39c51dd41b3c819f4f5396655f0629b4fd Mon Sep 17 00:00:00 2001 From: Aleksander Jan Bajkowski Date: Tue, 14 Sep 2021 23:21:00 +0200 Subject: [PATCH 40/87] MIPS: lantiq: dma: fix burst length for DEU The current definition of 2W burst length is invalid. This patch fixes it. Current downstream DEU driver doesn't use DMA. An incorrect burst length value doesn't cause any errors. This patch also adds other burst length values. Fixes: dfec1a827d2b ("MIPS: Lantiq: Add DMA support") Signed-off-by: Aleksander Jan Bajkowski Signed-off-by: David S. Miller --- arch/mips/lantiq/xway/dma.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/mips/lantiq/xway/dma.c b/arch/mips/lantiq/xway/dma.c index 364ab39eb8a4..53fcc672a294 100644 --- a/arch/mips/lantiq/xway/dma.c +++ b/arch/mips/lantiq/xway/dma.c @@ -41,7 +41,11 @@ #define DMA_IRQ_ACK 0x7e /* IRQ status register */ #define DMA_POLL BIT(31) /* turn on channel polling */ #define DMA_CLK_DIV4 BIT(6) /* polling clock divider */ -#define DMA_2W_BURST BIT(1) /* 2 word burst length */ +#define DMA_PCTRL_2W_BURST 0x1 /* 2 word burst length */ +#define DMA_PCTRL_4W_BURST 0x2 /* 4 word burst length */ +#define DMA_PCTRL_8W_BURST 0x3 /* 8 word burst length */ +#define DMA_TX_BURST_SHIFT 4 /* tx burst shift */ +#define DMA_RX_BURST_SHIFT 2 /* rx burst shift */ #define DMA_ETOP_ENDIANNESS (0xf << 8) /* endianness swap etop channels */ #define DMA_WEIGHT (BIT(17) | BIT(16)) /* default channel wheight */ @@ -192,7 +196,8 @@ ltq_dma_init_port(int p) break; case DMA_PORT_DEU: - ltq_dma_w32((DMA_2W_BURST << 4) | (DMA_2W_BURST << 2), + ltq_dma_w32((DMA_PCTRL_2W_BURST << DMA_TX_BURST_SHIFT) | + (DMA_PCTRL_2W_BURST << DMA_RX_BURST_SHIFT), LTQ_DMA_PCTRL); break; From 49293bbc50cb7d44223eb49e0f7cb38e7dac2361 Mon Sep 17 00:00:00 2001 From: Aleksander Jan Bajkowski Date: Tue, 14 Sep 2021 23:21:01 +0200 Subject: [PATCH 41/87] MIPS: lantiq: dma: make the burst length configurable by the drivers Make the burst length configurable by the drivers. Signed-off-by: Aleksander Jan Bajkowski Acked-by: Hauke Mehrtens Signed-off-by: David S. Miller --- .../include/asm/mach-lantiq/xway/xway_dma.h | 2 +- arch/mips/lantiq/xway/dma.c | 38 ++++++++++++++++--- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/arch/mips/include/asm/mach-lantiq/xway/xway_dma.h b/arch/mips/include/asm/mach-lantiq/xway/xway_dma.h index 8218a1356bd8..31ca9151b539 100644 --- a/arch/mips/include/asm/mach-lantiq/xway/xway_dma.h +++ b/arch/mips/include/asm/mach-lantiq/xway/xway_dma.h @@ -45,6 +45,6 @@ extern void ltq_dma_close(struct ltq_dma_channel *ch); extern void ltq_dma_alloc_tx(struct ltq_dma_channel *ch); extern void ltq_dma_alloc_rx(struct ltq_dma_channel *ch); extern void ltq_dma_free(struct ltq_dma_channel *ch); -extern void ltq_dma_init_port(int p); +extern void ltq_dma_init_port(int p, int tx_burst, int rx_burst); #endif diff --git a/arch/mips/lantiq/xway/dma.c b/arch/mips/lantiq/xway/dma.c index 53fcc672a294..f8eedeb15f18 100644 --- a/arch/mips/lantiq/xway/dma.c +++ b/arch/mips/lantiq/xway/dma.c @@ -182,7 +182,7 @@ ltq_dma_free(struct ltq_dma_channel *ch) EXPORT_SYMBOL_GPL(ltq_dma_free); void -ltq_dma_init_port(int p) +ltq_dma_init_port(int p, int tx_burst, int rx_burst) { ltq_dma_w32(p, LTQ_DMA_PS); switch (p) { @@ -191,16 +191,44 @@ ltq_dma_init_port(int p) * Tell the DMA engine to swap the endianness of data frames and * drop packets if the channel arbitration fails. */ - ltq_dma_w32_mask(0, DMA_ETOP_ENDIANNESS | DMA_PDEN, + ltq_dma_w32_mask(0, (DMA_ETOP_ENDIANNESS | DMA_PDEN), LTQ_DMA_PCTRL); break; - case DMA_PORT_DEU: - ltq_dma_w32((DMA_PCTRL_2W_BURST << DMA_TX_BURST_SHIFT) | - (DMA_PCTRL_2W_BURST << DMA_RX_BURST_SHIFT), + default: + break; + } + + switch (rx_burst) { + case 8: + ltq_dma_w32_mask(0x0c, (DMA_PCTRL_8W_BURST << DMA_RX_BURST_SHIFT), LTQ_DMA_PCTRL); break; + case 4: + ltq_dma_w32_mask(0x0c, (DMA_PCTRL_4W_BURST << DMA_RX_BURST_SHIFT), + LTQ_DMA_PCTRL); + break; + case 2: + ltq_dma_w32_mask(0x0c, (DMA_PCTRL_2W_BURST << DMA_RX_BURST_SHIFT), + LTQ_DMA_PCTRL); + break; + default: + break; + } + switch (tx_burst) { + case 8: + ltq_dma_w32_mask(0x30, (DMA_PCTRL_8W_BURST << DMA_TX_BURST_SHIFT), + LTQ_DMA_PCTRL); + break; + case 4: + ltq_dma_w32_mask(0x30, (DMA_PCTRL_4W_BURST << DMA_TX_BURST_SHIFT), + LTQ_DMA_PCTRL); + break; + case 2: + ltq_dma_w32_mask(0x30, (DMA_PCTRL_2W_BURST << DMA_TX_BURST_SHIFT), + LTQ_DMA_PCTRL); + break; default: break; } From 14d4e308e0aa0b78dc7a059716861a4380de3535 Mon Sep 17 00:00:00 2001 From: Aleksander Jan Bajkowski Date: Tue, 14 Sep 2021 23:21:02 +0200 Subject: [PATCH 42/87] net: lantiq: configure the burst length in ethernet drivers Configure the burst length in Ethernet drivers. This improves Ethernet performance by 58%. According to the vendor BSP, 8W burst length is supported by ar9 and newer SoCs. The NAT benchmark results on xRX200 (Down/Up): * 2W: 330 Mb/s * 4W: 432 Mb/s 372 Mb/s * 8W: 520 Mb/s 389 Mb/s Tested on xRX200 and xRX330. Signed-off-by: Aleksander Jan Bajkowski Signed-off-by: David S. Miller --- drivers/net/ethernet/lantiq_etop.c | 21 ++++++++++++++++++--- drivers/net/ethernet/lantiq_xrx200.c | 21 ++++++++++++++++++--- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c index 62f8c5212182..2258e3f19161 100644 --- a/drivers/net/ethernet/lantiq_etop.c +++ b/drivers/net/ethernet/lantiq_etop.c @@ -96,6 +96,9 @@ struct ltq_etop_priv { struct ltq_etop_chan ch[MAX_DMA_CHAN]; int tx_free[MAX_DMA_CHAN >> 1]; + int tx_burst_len; + int rx_burst_len; + spinlock_t lock; }; @@ -259,7 +262,7 @@ ltq_etop_hw_init(struct net_device *dev) /* enable crc generation */ ltq_etop_w32(PPE32_CGEN, LQ_PPE32_ENET_MAC_CFG); - ltq_dma_init_port(DMA_PORT_ETOP); + ltq_dma_init_port(DMA_PORT_ETOP, priv->tx_burst_len, rx_burst_len); for (i = 0; i < MAX_DMA_CHAN; i++) { int irq = LTQ_DMA_CH0_INT + i; @@ -472,8 +475,8 @@ ltq_etop_tx(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_BUSY; } - /* dma needs to start on a 16 byte aligned address */ - byte_offset = CPHYSADDR(skb->data) % 16; + /* dma needs to start on a burst length value aligned address */ + byte_offset = CPHYSADDR(skb->data) % (priv->tx_burst_len * 4); ch->skb[ch->dma.desc] = skb; netif_trans_update(dev); @@ -667,6 +670,18 @@ ltq_etop_probe(struct platform_device *pdev) spin_lock_init(&priv->lock); SET_NETDEV_DEV(dev, &pdev->dev); + err = device_property_read_u32(&pdev->dev, "lantiq,tx-burst-length", &priv->tx_burst_len); + if (err < 0) { + dev_err(&pdev->dev, "unable to read tx-burst-length property\n"); + return err; + } + + err = device_property_read_u32(&pdev->dev, "lantiq,rx-burst-length", &priv->rx_burst_len); + if (err < 0) { + dev_err(&pdev->dev, "unable to read rx-burst-length property\n"); + return err; + } + for (i = 0; i < MAX_DMA_CHAN; i++) { if (IS_TX(i)) netif_napi_add(dev, &priv->ch[i].napi, diff --git a/drivers/net/ethernet/lantiq_xrx200.c b/drivers/net/ethernet/lantiq_xrx200.c index fb78f17d734f..5d96248ce83b 100644 --- a/drivers/net/ethernet/lantiq_xrx200.c +++ b/drivers/net/ethernet/lantiq_xrx200.c @@ -71,6 +71,9 @@ struct xrx200_priv { struct net_device *net_dev; struct device *dev; + int tx_burst_len; + int rx_burst_len; + __iomem void *pmac_reg; }; @@ -316,8 +319,8 @@ static netdev_tx_t xrx200_start_xmit(struct sk_buff *skb, if (unlikely(dma_mapping_error(priv->dev, mapping))) goto err_drop; - /* dma needs to start on a 16 byte aligned address */ - byte_offset = mapping % 16; + /* dma needs to start on a burst length value aligned address */ + byte_offset = mapping % (priv->tx_burst_len * 4); desc->addr = mapping - byte_offset; /* Make sure the address is written before we give it to HW */ @@ -369,7 +372,7 @@ static int xrx200_dma_init(struct xrx200_priv *priv) int ret = 0; int i; - ltq_dma_init_port(DMA_PORT_ETOP); + ltq_dma_init_port(DMA_PORT_ETOP, priv->tx_burst_len, rx_burst_len); ch_rx->dma.nr = XRX200_DMA_RX; ch_rx->dma.dev = priv->dev; @@ -478,6 +481,18 @@ static int xrx200_probe(struct platform_device *pdev) if (err) eth_hw_addr_random(net_dev); + err = device_property_read_u32(dev, "lantiq,tx-burst-length", &priv->tx_burst_len); + if (err < 0) { + dev_err(dev, "unable to read tx-burst-length property\n"); + return err; + } + + err = device_property_read_u32(dev, "lantiq,rx-burst-length", &priv->rx_burst_len); + if (err < 0) { + dev_err(dev, "unable to read rx-burst-length property\n"); + return err; + } + /* bring up the dma engine and IP core */ err = xrx200_dma_init(priv); if (err) From 5535bcfa725a8cfe3ce62b817eb48e0a9b65c828 Mon Sep 17 00:00:00 2001 From: Aleksander Jan Bajkowski Date: Tue, 14 Sep 2021 23:21:03 +0200 Subject: [PATCH 43/87] dt-bindings: net: lantiq-xrx200-net: convert to the json-schema Convert the Lantiq PMAC Device Tree binding documentation to json-schema. Signed-off-by: Aleksander Jan Bajkowski Signed-off-by: David S. Miller --- .../bindings/net/lantiq,xrx200-net.txt | 21 ------- .../bindings/net/lantiq,xrx200-net.yaml | 59 +++++++++++++++++++ 2 files changed, 59 insertions(+), 21 deletions(-) delete mode 100644 Documentation/devicetree/bindings/net/lantiq,xrx200-net.txt create mode 100644 Documentation/devicetree/bindings/net/lantiq,xrx200-net.yaml diff --git a/Documentation/devicetree/bindings/net/lantiq,xrx200-net.txt b/Documentation/devicetree/bindings/net/lantiq,xrx200-net.txt deleted file mode 100644 index 5ff5e68bbbb6..000000000000 --- a/Documentation/devicetree/bindings/net/lantiq,xrx200-net.txt +++ /dev/null @@ -1,21 +0,0 @@ -Lantiq xRX200 GSWIP PMAC Ethernet driver -================================== - -Required properties: - -- compatible : "lantiq,xrx200-net" for the PMAC of the embedded - : GSWIP in the xXR200 -- reg : memory range of the PMAC core inside of the GSWIP core -- interrupts : TX and RX DMA interrupts. Use interrupt-names "tx" for - : the TX interrupt and "rx" for the RX interrupt. - -Example: - -ethernet@e10b308 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "lantiq,xrx200-net"; - reg = <0xe10b308 0xcf8>; - interrupts = <73>, <72>; - interrupt-names = "tx", "rx"; -}; diff --git a/Documentation/devicetree/bindings/net/lantiq,xrx200-net.yaml b/Documentation/devicetree/bindings/net/lantiq,xrx200-net.yaml new file mode 100644 index 000000000000..7bc074a42369 --- /dev/null +++ b/Documentation/devicetree/bindings/net/lantiq,xrx200-net.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/lantiq,xrx200-net.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Lantiq xRX200 GSWIP PMAC Ethernet driver + +maintainers: + - Hauke Mehrtens + +properties: + $nodename: + pattern: "^ethernet@[0-9a-f]+$" + + compatible: + const: lantiq,xrx200-net + + reg: + maxItems: 1 + + interrupts: + items: + - description: TX interrupt + - description: RX interrupt + + interrupt-names: + items: + - const: tx + - const: rx + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +required: + - compatible + - reg + - interrupt-parent + - interrupts + - interrupt-names + - "#address-cells" + - "#size-cells" + +additionalProperties: false + +examples: + - | + ethernet@e10b308 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "lantiq,xrx200-net"; + reg = <0xe10b308 0xcf8>; + interrupt-parent = <&icu0>; + interrupts = <73>, <72>; + interrupt-names = "tx", "rx"; + }; From dac0bad93741434d6a69c42206c498e26500ad88 Mon Sep 17 00:00:00 2001 From: Aleksander Jan Bajkowski Date: Tue, 14 Sep 2021 23:21:04 +0200 Subject: [PATCH 44/87] dt-bindings: net: lantiq,etop-xway: Document Lantiq Xway ETOP bindings Document the Lantiq Xway SoC series External Bus Unit (ETOP) bindings. Signed-off-by: Aleksander Jan Bajkowski Signed-off-by: David S. Miller --- .../bindings/net/lantiq,etop-xway.yaml | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/lantiq,etop-xway.yaml diff --git a/Documentation/devicetree/bindings/net/lantiq,etop-xway.yaml b/Documentation/devicetree/bindings/net/lantiq,etop-xway.yaml new file mode 100644 index 000000000000..4412abfb4987 --- /dev/null +++ b/Documentation/devicetree/bindings/net/lantiq,etop-xway.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/lantiq,etop-xway.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Lantiq Xway ETOP Ethernet driver + +maintainers: + - John Crispin + +properties: + $nodename: + pattern: "^ethernet@[0-9a-f]+$" + + compatible: + const: lantiq,etop-xway + + reg: + maxItems: 1 + + interrupts: + items: + - description: TX interrupt + - description: RX interrupt + + interrupt-names: + items: + - const: tx + - const: rx + + phy-mode: true + +required: + - compatible + - reg + - interrupt-parent + - interrupts + - interrupt-names + - phy-mode + +additionalProperties: false + +examples: + - | + ethernet@e180000 { + compatible = "lantiq,etop-xway"; + reg = <0xe180000 0x40000>; + interrupt-parent = <&icu0>; + interrupts = <73>, <78>; + interrupt-names = "tx", "rx"; + phy-mode = "rmii"; + }; From c68872146489946509b6bc1667e29192bff46d8d Mon Sep 17 00:00:00 2001 From: Aleksander Jan Bajkowski Date: Tue, 14 Sep 2021 23:21:05 +0200 Subject: [PATCH 45/87] dt-bindings: net: lantiq: Add the burst length properties The new added properties are used for configuring burst length. Signed-off-by: Aleksander Jan Bajkowski Signed-off-by: David S. Miller --- .../bindings/net/lantiq,etop-xway.yaml | 16 ++++++++++++++++ .../bindings/net/lantiq,xrx200-net.yaml | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/Documentation/devicetree/bindings/net/lantiq,etop-xway.yaml b/Documentation/devicetree/bindings/net/lantiq,etop-xway.yaml index 4412abfb4987..437502c5ca96 100644 --- a/Documentation/devicetree/bindings/net/lantiq,etop-xway.yaml +++ b/Documentation/devicetree/bindings/net/lantiq,etop-xway.yaml @@ -29,6 +29,18 @@ properties: - const: tx - const: rx + lantiq,tx-burst-length: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + TX programmable burst length. + enum: [2, 4, 8] + + lantiq,rx-burst-length: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + RX programmable burst length. + enum: [2, 4, 8] + phy-mode: true required: @@ -37,6 +49,8 @@ required: - interrupt-parent - interrupts - interrupt-names + - lantiq,tx-burst-length + - lantiq,rx-burst-length - phy-mode additionalProperties: false @@ -49,5 +63,7 @@ examples: interrupt-parent = <&icu0>; interrupts = <73>, <78>; interrupt-names = "tx", "rx"; + lantiq,tx-burst-length = <8>; + lantiq,rx-burst-length = <8>; phy-mode = "rmii"; }; diff --git a/Documentation/devicetree/bindings/net/lantiq,xrx200-net.yaml b/Documentation/devicetree/bindings/net/lantiq,xrx200-net.yaml index 7bc074a42369..16d831f22063 100644 --- a/Documentation/devicetree/bindings/net/lantiq,xrx200-net.yaml +++ b/Documentation/devicetree/bindings/net/lantiq,xrx200-net.yaml @@ -29,6 +29,18 @@ properties: - const: tx - const: rx + lantiq,tx-burst-length: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + TX programmable burst length. + enum: [2, 4, 8] + + lantiq,rx-burst-length: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + RX programmable burst length. + enum: [2, 4, 8] + '#address-cells': const: 1 @@ -41,6 +53,8 @@ required: - interrupt-parent - interrupts - interrupt-names + - lantiq,tx-burst-length + - lantiq,rx-burst-length - "#address-cells" - "#size-cells" @@ -56,4 +70,6 @@ examples: interrupt-parent = <&icu0>; interrupts = <73>, <72>; interrupt-names = "tx", "rx"; + lantiq,tx-burst-length = <8>; + lantiq,rx-burst-length = <8>; }; From 1618df6afab2c1856cd574444b76f6dccee080df Mon Sep 17 00:00:00 2001 From: Jonathan Lemon Date: Tue, 14 Sep 2021 19:16:19 -0700 Subject: [PATCH 46/87] ptp: ocp: parameterize the i2c driver used Move the xilinx i2c driver parameters to the resource block instead of hardcoding things in the registration functions. Signed-off-by: Jonathan Lemon Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index caf9b37c5eb1..d37eac69150a 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -131,6 +131,13 @@ struct ptp_ocp_flash_info { void *data; }; +struct ptp_ocp_i2c_info { + const char *name; + unsigned long fixed_rate; + size_t data_size; + void *data; +}; + struct ptp_ocp_ext_info { const char *name; int index; @@ -269,6 +276,10 @@ static struct ocp_resource ocp_fb_resource[] = { { OCP_I2C_RESOURCE(i2c_ctrl), .offset = 0x00150000, .size = 0x10000, .irq_vec = 7, + .extra = &(struct ptp_ocp_i2c_info) { + .name = "xiic-i2c", + .fixed_rate = 50000000, + }, }, { OCP_SERIAL_RESOURCE(gnss_port), @@ -944,21 +955,25 @@ ptp_ocp_register_spi(struct ptp_ocp *bp, struct ocp_resource *r) static struct platform_device * ptp_ocp_i2c_bus(struct pci_dev *pdev, struct ocp_resource *r, int id) { + struct ptp_ocp_i2c_info *info; struct resource res[2]; unsigned long start; + info = r->extra; start = pci_resource_start(pdev, 0) + r->offset; ptp_ocp_set_mem_resource(&res[0], start, r->size); ptp_ocp_set_irq_resource(&res[1], pci_irq_vector(pdev, r->irq_vec)); - return platform_device_register_resndata(&pdev->dev, "xiic-i2c", - id, res, 2, NULL, 0); + return platform_device_register_resndata(&pdev->dev, info->name, + id, res, 2, + info->data, info->data_size); } static int ptp_ocp_register_i2c(struct ptp_ocp *bp, struct ocp_resource *r) { struct pci_dev *pdev = bp->pdev; + struct ptp_ocp_i2c_info *info; struct platform_device *p; struct clk_hw *clk; char buf[32]; @@ -970,15 +985,17 @@ ptp_ocp_register_i2c(struct ptp_ocp *bp, struct ocp_resource *r) return 0; } + info = r->extra; id = pci_dev_id(bp->pdev); sprintf(buf, "AXI.%d", id); - clk = clk_hw_register_fixed_rate(&pdev->dev, buf, NULL, 0, 50000000); + clk = clk_hw_register_fixed_rate(&pdev->dev, buf, NULL, 0, + info->fixed_rate); if (IS_ERR(clk)) return PTR_ERR(clk); bp->i2c_clk = clk; - sprintf(buf, "xiic-i2c.%d", id); + sprintf(buf, "%s.%d", info->name, id); devm_clk_hw_register_clkdev(&pdev->dev, clk, NULL, buf); p = ptp_ocp_i2c_bus(bp->pdev, r, id); if (IS_ERR(p)) From 498ad3f4389a1f1d2b0e09fa8f906152b0cfaf67 Mon Sep 17 00:00:00 2001 From: Jonathan Lemon Date: Tue, 14 Sep 2021 19:16:20 -0700 Subject: [PATCH 47/87] ptp: ocp: Parameterize the TOD information display. Only display the TOD information if there is a corresponding TOD resource. Signed-off-by: Jonathan Lemon Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index d37eac69150a..2a6cc762c60e 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -743,7 +743,8 @@ ptp_ocp_info(struct ptp_ocp *bp) ptp_ocp_clock_name_from_val(select >> 16), ptp_clock_index(bp->ptp)); - ptp_ocp_tod_info(bp); + if (bp->tod) + ptp_ocp_tod_info(bp); } static struct device * From 1447149d653934dea868d22d00d7b5f1b86ef70b Mon Sep 17 00:00:00 2001 From: Jonathan Lemon Date: Tue, 14 Sep 2021 19:16:21 -0700 Subject: [PATCH 48/87] ptp: ocp: Skip I2C flash read when there is no controller. If an I2C controller isn't present, don't try and read the I2C flash. Signed-off-by: Jonathan Lemon Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 2a6cc762c60e..196a457929f0 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -228,8 +228,8 @@ static int ptp_ocp_ts_enable(void *priv, bool enable); * 3: GPS * 4: GPS2 (n/c) * 5: MAC - * 6: SPI IMU (inertial measurement unit) - * 7: I2C oscillator + * 6: N/C + * 7: I2C controller * 8: HWICAP * 9: SPI Flash */ @@ -706,6 +706,9 @@ ptp_ocp_get_serial_number(struct ptp_ocp *bp) struct device *dev; int err; + if (!bp->i2c_ctrl) + return; + dev = device_find_child(&bp->i2c_ctrl->dev, NULL, ptp_ocp_firstchild); if (!dev) { dev_err(&bp->pdev->dev, "Can't find I2C adapter\n"); From 56ec44033cd7398cc450fe3d356aa2b5a035e423 Mon Sep 17 00:00:00 2001 From: Jonathan Lemon Date: Tue, 14 Sep 2021 19:16:22 -0700 Subject: [PATCH 49/87] ptp: ocp: Skip resources with out of range irqs The TimeCard exposes different resources, which may have their own irqs. Space for the irqs is allocated through a MSI or MSI-X interrupt vector. On some platforms, the interrupt allocation fails. Rather than making this fatal, just skip exposing those resources. The main timecard functionality (that of a PTP clock) will work without the additional resources. Signed-off-by: Jonathan Lemon Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 47 +++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 196a457929f0..ad8b794fa7e6 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -139,7 +139,6 @@ struct ptp_ocp_i2c_info { }; struct ptp_ocp_ext_info { - const char *name; int index; irqreturn_t (*irq_fcn)(int irq, void *priv); int (*enable)(void *priv, bool enable); @@ -187,6 +186,7 @@ struct ocp_resource { int (*setup)(struct ptp_ocp *bp, struct ocp_resource *r); void *extra; unsigned long bp_offset; + const char * const name; }; static int ptp_ocp_register_mem(struct ptp_ocp *bp, struct ocp_resource *r); @@ -204,7 +204,7 @@ static int ptp_ocp_ts_enable(void *priv, bool enable); }) #define OCP_RES_LOCATION(member) \ - .bp_offset = offsetof(struct ptp_ocp, member) + .name = #member, .bp_offset = offsetof(struct ptp_ocp, member) #define OCP_MEM_RESOURCE(member) \ OCP_RES_LOCATION(member), .setup = ptp_ocp_register_mem @@ -243,7 +243,7 @@ static struct ocp_resource ocp_fb_resource[] = { OCP_EXT_RESOURCE(ts0), .offset = 0x01010000, .size = 0x10000, .irq_vec = 1, .extra = &(struct ptp_ocp_ext_info) { - .name = "ts0", .index = 0, + .index = 0, .irq_fcn = ptp_ocp_ts_irq, .enable = ptp_ocp_ts_enable, }, @@ -252,7 +252,7 @@ static struct ocp_resource ocp_fb_resource[] = { OCP_EXT_RESOURCE(ts1), .offset = 0x01020000, .size = 0x10000, .irq_vec = 2, .extra = &(struct ptp_ocp_ext_info) { - .name = "ts1", .index = 1, + .index = 1, .irq_fcn = ptp_ocp_ts_irq, .enable = ptp_ocp_ts_enable, }, @@ -925,18 +925,6 @@ ptp_ocp_register_spi(struct ptp_ocp *bp, struct ocp_resource *r) unsigned long start; int id; - /* XXX hack to work around old FPGA */ - if (bp->n_irqs < 10) { - dev_err(&bp->pdev->dev, "FPGA does not have SPI devices\n"); - return 0; - } - - if (r->irq_vec > bp->n_irqs) { - dev_err(&bp->pdev->dev, "spi device irq %d out of range\n", - r->irq_vec); - return 0; - } - start = pci_resource_start(pdev, 0) + r->offset; ptp_ocp_set_mem_resource(&res[0], start, r->size); ptp_ocp_set_irq_resource(&res[1], pci_irq_vector(pdev, r->irq_vec)); @@ -983,12 +971,6 @@ ptp_ocp_register_i2c(struct ptp_ocp *bp, struct ocp_resource *r) char buf[32]; int id; - if (r->irq_vec > bp->n_irqs) { - dev_err(&bp->pdev->dev, "i2c device irq %d out of range\n", - r->irq_vec); - return 0; - } - info = r->extra; id = pci_dev_id(bp->pdev); @@ -1080,7 +1062,7 @@ ptp_ocp_register_ext(struct ptp_ocp *bp, struct ocp_resource *r) ext->irq_vec = r->irq_vec; err = pci_request_irq(pdev, r->irq_vec, ext->info->irq_fcn, NULL, - ext, "ocp%d.%s", bp->id, ext->info->name); + ext, "ocp%d.%s", bp->id, r->name); if (err) { dev_err(&pdev->dev, "Could not get irq %d\n", r->irq_vec); goto out; @@ -1122,12 +1104,6 @@ ptp_ocp_register_serial(struct ptp_ocp *bp, struct ocp_resource *r) { int port; - if (r->irq_vec > bp->n_irqs) { - dev_err(&bp->pdev->dev, "serial device irq %d out of range\n", - r->irq_vec); - return 0; - } - port = ptp_ocp_serial_line(bp, r); if (port < 0) return port; @@ -1160,6 +1136,17 @@ ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r) return ptp_ocp_init_clock(bp); } +static bool +ptp_ocp_allow_irq(struct ptp_ocp *bp, struct ocp_resource *r) +{ + bool allow = !r->irq_vec || r->irq_vec < bp->n_irqs; + + if (!allow) + dev_err(&bp->pdev->dev, "irq %d out of range, skipping %s\n", + r->irq_vec, r->name); + return allow; +} + static int ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data) { @@ -1168,6 +1155,8 @@ ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data) table = (struct ocp_resource *)driver_data; for (r = table; r->setup; r++) { + if (!ptp_ocp_allow_irq(bp, r)) + continue; err = r->setup(bp, r); if (err) break; From bceff2905eff5d91de07a6be7617341f36716224 Mon Sep 17 00:00:00 2001 From: Jonathan Lemon Date: Tue, 14 Sep 2021 19:16:23 -0700 Subject: [PATCH 50/87] ptp: ocp: Report error if resource registration fails. If a resource could not be registered, report the name of the resource and the error code. Signed-off-by: Jonathan Lemon Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index ad8b794fa7e6..1f86e878ccba 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -1158,8 +1158,12 @@ ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data) if (!ptp_ocp_allow_irq(bp, r)) continue; err = r->setup(bp, r); - if (err) + if (err) { + dev_err(&bp->pdev->dev, + "Could not register %s: err %d\n", + r->name, err); break; + } } return err; } From dcf614692c6cb93c7f4957fbdc7f5d024f5e2f7b Mon Sep 17 00:00:00 2001 From: Jonathan Lemon Date: Tue, 14 Sep 2021 19:16:24 -0700 Subject: [PATCH 51/87] ptp: ocp: Add third timestamper The firmware may provide a third signal timestamper, so make it available for use. Signed-off-by: Jonathan Lemon Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 1f86e878ccba..23d5f20f43f8 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -162,6 +162,7 @@ struct ptp_ocp { struct ptp_ocp_ext_src *pps; struct ptp_ocp_ext_src *ts0; struct ptp_ocp_ext_src *ts1; + struct ptp_ocp_ext_src *ts2; struct img_reg __iomem *image; struct ptp_clock *ptp; struct ptp_clock_info ptp_info; @@ -228,7 +229,7 @@ static int ptp_ocp_ts_enable(void *priv, bool enable); * 3: GPS * 4: GPS2 (n/c) * 5: MAC - * 6: N/C + * 6: TS2 * 7: I2C controller * 8: HWICAP * 9: SPI Flash @@ -257,6 +258,15 @@ static struct ocp_resource ocp_fb_resource[] = { .enable = ptp_ocp_ts_enable, }, }, + { + OCP_EXT_RESOURCE(ts2), + .offset = 0x01060000, .size = 0x10000, .irq_vec = 6, + .extra = &(struct ptp_ocp_ext_info) { + .index = 2, + .irq_fcn = ptp_ocp_ts_irq, + .enable = ptp_ocp_ts_enable, + }, + }, { OCP_MEM_RESOURCE(pps_to_ext), .offset = 0x01030000, .size = 0x10000, @@ -497,6 +507,9 @@ ptp_ocp_enable(struct ptp_clock_info *ptp_info, struct ptp_clock_request *rq, case 1: ext = bp->ts1; break; + case 2: + ext = bp->ts2; + break; } break; case PTP_CLK_REQ_PPS: @@ -524,7 +537,7 @@ static const struct ptp_clock_info ptp_ocp_clock_info = { .adjphase = ptp_ocp_adjphase, .enable = ptp_ocp_enable, .pps = true, - .n_ext_ts = 2, + .n_ext_ts = 3, }; static void @@ -1407,6 +1420,8 @@ ptp_ocp_detach(struct ptp_ocp *bp) ptp_ocp_unregister_ext(bp->ts0); if (bp->ts1) ptp_ocp_unregister_ext(bp->ts1); + if (bp->ts2) + ptp_ocp_unregister_ext(bp->ts2); if (bp->pps) ptp_ocp_unregister_ext(bp->pps); if (bp->gnss_port != -1) From e1daf0ec73b2e9170d7df1e5cda409de3535bac2 Mon Sep 17 00:00:00 2001 From: Jonathan Lemon Date: Tue, 14 Sep 2021 19:16:25 -0700 Subject: [PATCH 52/87] ptp: ocp: Add SMA selector and controls The latest firmware for the TimeCard adds selectable signals for the SMA input/outputs. Add support for SMA selectors, and the GPIO controls needed for steering signals. Signed-off-by: Jonathan Lemon Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 389 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 361 insertions(+), 28 deletions(-) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 23d5f20f43f8..7441dac4e9c5 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -124,6 +124,13 @@ struct img_reg { u32 version; }; +struct gpio_reg { + u32 gpio1; + u32 __pad0; + u32 gpio2; + u32 __pad1; +}; + struct ptp_ocp_flash_info { const char *name; int pci_offset; @@ -159,6 +166,7 @@ struct ptp_ocp { struct tod_reg __iomem *tod; struct pps_reg __iomem *pps_to_ext; struct pps_reg __iomem *pps_to_clk; + struct gpio_reg __iomem *sma; struct ptp_ocp_ext_src *pps; struct ptp_ocp_ext_src *ts0; struct ptp_ocp_ext_src *ts1; @@ -283,6 +291,10 @@ static struct ocp_resource ocp_fb_resource[] = { OCP_MEM_RESOURCE(image), .offset = 0x00020000, .size = 0x1000, }, + { + OCP_MEM_RESOURCE(sma), + .offset = 0x00140000, .size = 0x1000, + }, { OCP_I2C_RESOURCE(i2c_ctrl), .offset = 0x00150000, .size = 0x10000, .irq_vec = 7, @@ -330,10 +342,12 @@ MODULE_DEVICE_TABLE(pci, ptp_ocp_pcidev_id); static DEFINE_MUTEX(ptp_ocp_lock); static DEFINE_IDR(ptp_ocp_idr); -static struct { +struct ocp_selector { const char *name; int value; -} ptp_ocp_clock[] = { +}; + +static struct ocp_selector ptp_ocp_clock[] = { { .name = "NONE", .value = 0 }, { .name = "TOD", .value = 1 }, { .name = "IRIG", .value = 2 }, @@ -343,33 +357,67 @@ static struct { { .name = "DCF", .value = 6 }, { .name = "REGS", .value = 0xfe }, { .name = "EXT", .value = 0xff }, + { } +}; + +static struct ocp_selector ptp_ocp_sma_in[] = { + { .name = "10Mhz", .value = 0x00 }, + { .name = "PPS1", .value = 0x01 }, + { .name = "PPS2", .value = 0x02 }, + { .name = "TS1", .value = 0x04 }, + { .name = "TS2", .value = 0x08 }, + { } +}; + +static struct ocp_selector ptp_ocp_sma_out[] = { + { .name = "10Mhz", .value = 0x00 }, + { .name = "PHC", .value = 0x01 }, + { .name = "MAC", .value = 0x02 }, + { .name = "GNSS", .value = 0x04 }, + { .name = "GNSS2", .value = 0x08 }, + { } }; static const char * -ptp_ocp_clock_name_from_val(int val) +ptp_ocp_select_name_from_val(struct ocp_selector *tbl, int val) { int i; - for (i = 0; i < ARRAY_SIZE(ptp_ocp_clock); i++) - if (ptp_ocp_clock[i].value == val) - return ptp_ocp_clock[i].name; + for (i = 0; tbl[i].name; i++) + if (tbl[i].value == val) + return tbl[i].name; return NULL; } static int -ptp_ocp_clock_val_from_name(const char *name) +ptp_ocp_select_val_from_name(struct ocp_selector *tbl, const char *name) { - const char *clk; + const char *select; int i; - for (i = 0; i < ARRAY_SIZE(ptp_ocp_clock); i++) { - clk = ptp_ocp_clock[i].name; - if (!strncasecmp(name, clk, strlen(clk))) - return ptp_ocp_clock[i].value; + for (i = 0; tbl[i].name; i++) { + select = tbl[i].name; + if (!strncasecmp(name, select, strlen(select))) + return tbl[i].value; } return -EINVAL; } +static ssize_t +ptp_ocp_select_table_show(struct ocp_selector *tbl, char *buf) +{ + ssize_t count; + int i; + + count = 0; + for (i = 0; tbl[i].name; i++) + count += sysfs_emit_at(buf, count, "%s ", tbl[i].name); + if (count) + count--; + count += sysfs_emit_at(buf, count, "\n"); + return count; +} + static int __ptp_ocp_gettime_locked(struct ptp_ocp *bp, struct timespec64 *ts, struct ptp_system_timestamp *sts) @@ -756,7 +804,7 @@ ptp_ocp_info(struct ptp_ocp *bp) select = ioread32(&bp->reg->select); dev_info(&bp->pdev->dev, "Version %d.%d.%d, clock %s, device ptp%d\n", version >> 24, (version >> 16) & 0xff, version & 0xffff, - ptp_ocp_clock_name_from_val(select >> 16), + ptp_ocp_select_name_from_val(ptp_ocp_clock, select >> 16), ptp_clock_index(bp->ptp)); if (bp->tod) @@ -1181,6 +1229,297 @@ ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data) return err; } +/* + * ANT0 == gps (in) + * ANT1 == sma1 (in) + * ANT2 == sma2 (in) + * ANT3 == sma3 (out) + * ANT4 == sma4 (out) + */ + +enum ptp_ocp_sma_mode { + SMA_MODE_IN, + SMA_MODE_OUT, +}; + +static struct ptp_ocp_sma_connector { + enum ptp_ocp_sma_mode mode; + bool fixed_mode; + u16 default_out_idx; +} ptp_ocp_sma_map[4] = { + { + .mode = SMA_MODE_IN, + .fixed_mode = true, + }, + { + .mode = SMA_MODE_IN, + .fixed_mode = true, + }, + { + .mode = SMA_MODE_OUT, + .fixed_mode = true, + .default_out_idx = 0, /* 10Mhz */ + }, + { + .mode = SMA_MODE_OUT, + .fixed_mode = true, + .default_out_idx = 1, /* PHC */ + }, +}; + +static ssize_t +ptp_ocp_show_output(u32 val, char *buf, int default_idx) +{ + const char *name; + ssize_t count; + + count = sysfs_emit(buf, "OUT: "); + name = ptp_ocp_select_name_from_val(ptp_ocp_sma_out, val); + if (!name) + name = ptp_ocp_sma_out[default_idx].name; + count += sysfs_emit_at(buf, count, "%s\n", name); + return count; +} + +static ssize_t +ptp_ocp_show_inputs(u32 val, char *buf, const char *zero_in) +{ + const char *name; + ssize_t count; + int i; + + count = sysfs_emit(buf, "IN: "); + for (i = 0; i < ARRAY_SIZE(ptp_ocp_sma_in); i++) { + if (val & ptp_ocp_sma_in[i].value) { + name = ptp_ocp_sma_in[i].name; + count += sysfs_emit_at(buf, count, "%s ", name); + } + } + if (!val && zero_in) + count += sysfs_emit_at(buf, count, "%s ", zero_in); + if (count) + count--; + count += sysfs_emit_at(buf, count, "\n"); + return count; +} + +static int +sma_parse_inputs(const char *buf, enum ptp_ocp_sma_mode *mode) +{ + struct ocp_selector *tbl[] = { ptp_ocp_sma_in, ptp_ocp_sma_out }; + int idx, count, dir; + char **argv; + int ret; + + argv = argv_split(GFP_KERNEL, buf, &count); + if (!argv) + return -ENOMEM; + + ret = -EINVAL; + if (!count) + goto out; + + idx = 0; + dir = *mode == SMA_MODE_IN ? 0 : 1; + if (!strcasecmp("IN:", argv[idx])) { + dir = 0; + idx++; + } + if (!strcasecmp("OUT:", argv[0])) { + dir = 1; + idx++; + } + *mode = dir == 0 ? SMA_MODE_IN : SMA_MODE_OUT; + + ret = 0; + for (; idx < count; idx++) + ret |= ptp_ocp_select_val_from_name(tbl[dir], argv[idx]); + if (ret < 0) + ret = -EINVAL; + +out: + argv_free(argv); + return ret; +} + +static ssize_t +ptp_ocp_sma_show(struct ptp_ocp *bp, int sma_nr, u32 val, char *buf, + const char *zero_in) +{ + struct ptp_ocp_sma_connector *sma = &ptp_ocp_sma_map[sma_nr - 1]; + + if (sma->mode == SMA_MODE_IN) + return ptp_ocp_show_inputs(val, buf, zero_in); + + return ptp_ocp_show_output(val, buf, sma->default_out_idx); +} + +static ssize_t +sma1_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + u32 val; + + val = ioread32(&bp->sma->gpio1) & 0x3f; + return ptp_ocp_sma_show(bp, 1, val, buf, ptp_ocp_sma_in[0].name); +} + +static ssize_t +sma2_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + u32 val; + + val = (ioread32(&bp->sma->gpio1) >> 16) & 0x3f; + return ptp_ocp_sma_show(bp, 2, val, buf, NULL); +} + +static ssize_t +sma3_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + u32 val; + + val = ioread32(&bp->sma->gpio2) & 0x3f; + return ptp_ocp_sma_show(bp, 3, val, buf, NULL); +} + +static ssize_t +sma4_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + u32 val; + + val = (ioread32(&bp->sma->gpio2) >> 16) & 0x3f; + return ptp_ocp_sma_show(bp, 4, val, buf, NULL); +} + +static void +ptp_ocp_sma_store_output(struct ptp_ocp *bp, u32 val, u32 shift) +{ + unsigned long flags; + u32 gpio, mask; + + mask = 0xffff << (16 - shift); + + spin_lock_irqsave(&bp->lock, flags); + + gpio = ioread32(&bp->sma->gpio2); + gpio = (gpio & mask) | (val << shift); + iowrite32(gpio, &bp->sma->gpio2); + + spin_unlock_irqrestore(&bp->lock, flags); +} + +static void +ptp_ocp_sma_store_inputs(struct ptp_ocp *bp, u32 val, u32 shift) +{ + unsigned long flags; + u32 gpio, mask; + + mask = 0xffff << (16 - shift); + + spin_lock_irqsave(&bp->lock, flags); + + gpio = ioread32(&bp->sma->gpio1); + gpio = (gpio & mask) | (val << shift); + iowrite32(gpio, &bp->sma->gpio1); + + spin_unlock_irqrestore(&bp->lock, flags); +} + +static ssize_t +ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr, u32 shift) +{ + struct ptp_ocp_sma_connector *sma = &ptp_ocp_sma_map[sma_nr - 1]; + enum ptp_ocp_sma_mode mode; + int val; + + mode = sma->mode; + val = sma_parse_inputs(buf, &mode); + if (val < 0) + return val; + + if (mode != sma->mode && sma->fixed_mode) + return -EOPNOTSUPP; + + if (mode != sma->mode) { + pr_err("Mode changes not supported yet.\n"); + return -EOPNOTSUPP; + } + + if (sma->mode == SMA_MODE_IN) + ptp_ocp_sma_store_inputs(bp, val, shift); + else + ptp_ocp_sma_store_output(bp, val, shift); + + return 0; +} + +static ssize_t +sma1_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + int err; + + err = ptp_ocp_sma_store(bp, buf, 1, 0); + return err ? err : count; +} + +static ssize_t +sma2_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + int err; + + err = ptp_ocp_sma_store(bp, buf, 2, 16); + return err ? err : count; +} + +static ssize_t +sma3_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + int err; + + err = ptp_ocp_sma_store(bp, buf, 3, 0); + return err ? err : count; +} + +static ssize_t +sma4_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + int err; + + err = ptp_ocp_sma_store(bp, buf, 4, 16); + return err ? err : count; +} +static DEVICE_ATTR_RW(sma1); +static DEVICE_ATTR_RW(sma2); +static DEVICE_ATTR_RW(sma3); +static DEVICE_ATTR_RW(sma4); + +static ssize_t +available_sma_inputs_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return ptp_ocp_select_table_show(ptp_ocp_sma_in, buf); +} +static DEVICE_ATTR_RO(available_sma_inputs); + +static ssize_t +available_sma_outputs_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return ptp_ocp_select_table_show(ptp_ocp_sma_out, buf); +} +static DEVICE_ATTR_RO(available_sma_outputs); + static ssize_t serialnum_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1216,7 +1555,7 @@ clock_source_show(struct device *dev, struct device_attribute *attr, char *buf) u32 select; select = ioread32(&bp->reg->select); - p = ptp_ocp_clock_name_from_val(select >> 16); + p = ptp_ocp_select_name_from_val(ptp_ocp_clock, select >> 16); return sysfs_emit(buf, "%s\n", p); } @@ -1229,7 +1568,7 @@ clock_source_store(struct device *dev, struct device_attribute *attr, unsigned long flags; int val; - val = ptp_ocp_clock_val_from_name(buf); + val = ptp_ocp_select_val_from_name(ptp_ocp_clock, buf); if (val < 0) return val; @@ -1245,19 +1584,7 @@ static ssize_t available_clock_sources_show(struct device *dev, struct device_attribute *attr, char *buf) { - const char *clk; - ssize_t count; - int i; - - count = 0; - for (i = 0; i < ARRAY_SIZE(ptp_ocp_clock); i++) { - clk = ptp_ocp_clock[i].name; - count += sysfs_emit_at(buf, count, "%s ", clk); - } - if (count) - count--; - count += sysfs_emit_at(buf, count, "\n"); - return count; + return ptp_ocp_select_table_show(ptp_ocp_clock, buf); } static DEVICE_ATTR_RO(available_clock_sources); @@ -1266,6 +1593,12 @@ static struct attribute *timecard_attrs[] = { &dev_attr_gnss_sync.attr, &dev_attr_clock_source.attr, &dev_attr_available_clock_sources.attr, + &dev_attr_sma1.attr, + &dev_attr_sma2.attr, + &dev_attr_sma3.attr, + &dev_attr_sma4.attr, + &dev_attr_available_sma_inputs.attr, + &dev_attr_available_sma_outputs.attr, NULL, }; ATTRIBUTE_GROUPS(timecard); From 6baf2925424a8346eacf0da85687f8277011b916 Mon Sep 17 00:00:00 2001 From: Jonathan Lemon Date: Tue, 14 Sep 2021 19:16:26 -0700 Subject: [PATCH 53/87] ptp: ocp: Add IRIG-B and DCF blocks IRIG (Inter-range Instrumentation Group) timecode format on one of the SMA output channels is provided by the IRIG master FPGA block. Enable the master when the IRIG output format is selected on either one of the output channels. By default, the output is in B007 format. DCF output format is provided by the DCF master block. Also enable the IRIG and DCF slaves, which parse an incoming signal from the external SMA connectors, and may be used to adjust the PHC. Signed-off-by: Jonathan Lemon Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 129 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 7441dac4e9c5..1a210b77744c 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -131,6 +131,48 @@ struct gpio_reg { u32 __pad1; }; +struct irig_master_reg { + u32 ctrl; + u32 status; + u32 __pad0; + u32 version; + u32 adj_sec; + u32 mode_ctrl; +}; + +#define IRIG_M_CTRL_ENABLE BIT(0) + +struct irig_slave_reg { + u32 ctrl; + u32 status; + u32 __pad0; + u32 version; + u32 adj_sec; + u32 mode_ctrl; +}; + +#define IRIG_S_CTRL_ENABLE BIT(0) + +struct dcf_master_reg { + u32 ctrl; + u32 status; + u32 __pad0; + u32 version; + u32 adj_sec; +}; + +#define DCF_M_CTRL_ENABLE BIT(0) + +struct dcf_slave_reg { + u32 ctrl; + u32 status; + u32 __pad0; + u32 version; + u32 adj_sec; +}; + +#define DCF_S_CTRL_ENABLE BIT(0) + struct ptp_ocp_flash_info { const char *name; int pci_offset; @@ -167,6 +209,10 @@ struct ptp_ocp { struct pps_reg __iomem *pps_to_ext; struct pps_reg __iomem *pps_to_clk; struct gpio_reg __iomem *sma; + struct irig_master_reg __iomem *irig_out; + struct irig_slave_reg __iomem *irig_in; + struct dcf_master_reg __iomem *dcf_out; + struct dcf_slave_reg __iomem *dcf_in; struct ptp_ocp_ext_src *pps; struct ptp_ocp_ext_src *ts0; struct ptp_ocp_ext_src *ts1; @@ -287,6 +333,22 @@ static struct ocp_resource ocp_fb_resource[] = { OCP_MEM_RESOURCE(tod), .offset = 0x01050000, .size = 0x10000, }, + { + OCP_MEM_RESOURCE(irig_in), + .offset = 0x01070000, .size = 0x10000, + }, + { + OCP_MEM_RESOURCE(irig_out), + .offset = 0x01080000, .size = 0x10000, + }, + { + OCP_MEM_RESOURCE(dcf_in), + .offset = 0x01090000, .size = 0x10000, + }, + { + OCP_MEM_RESOURCE(dcf_out), + .offset = 0x010A0000, .size = 0x10000, + }, { OCP_MEM_RESOURCE(image), .offset = 0x00020000, .size = 0x1000, @@ -366,6 +428,8 @@ static struct ocp_selector ptp_ocp_sma_in[] = { { .name = "PPS2", .value = 0x02 }, { .name = "TS1", .value = 0x04 }, { .name = "TS2", .value = 0x08 }, + { .name = "IRIG", .value = 0x10 }, + { .name = "DCF", .value = 0x20 }, { } }; @@ -375,6 +439,8 @@ static struct ocp_selector ptp_ocp_sma_out[] = { { .name = "MAC", .value = 0x02 }, { .name = "GNSS", .value = 0x04 }, { .name = "GNSS2", .value = 0x08 }, + { .name = "IRIG", .value = 0x10 }, + { .name = "DCF", .value = 0x20 }, { } }; @@ -1229,6 +1295,63 @@ ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data) return err; } +static void +ptp_ocp_enable_fpga(u32 __iomem *reg, u32 bit, bool enable) +{ + u32 ctrl; + bool on; + + ctrl = ioread32(reg); + on = ctrl & bit; + if (on ^ enable) { + ctrl &= ~bit; + ctrl |= enable ? bit : 0; + iowrite32(ctrl, reg); + } +} + +static void +ptp_ocp_irig_out(struct ptp_ocp *bp, bool enable) +{ + return ptp_ocp_enable_fpga(&bp->irig_out->ctrl, + IRIG_M_CTRL_ENABLE, enable); +} + +static void +ptp_ocp_irig_in(struct ptp_ocp *bp, bool enable) +{ + return ptp_ocp_enable_fpga(&bp->irig_in->ctrl, + IRIG_S_CTRL_ENABLE, enable); +} + +static void +ptp_ocp_dcf_out(struct ptp_ocp *bp, bool enable) +{ + return ptp_ocp_enable_fpga(&bp->dcf_out->ctrl, + DCF_M_CTRL_ENABLE, enable); +} + +static void +ptp_ocp_dcf_in(struct ptp_ocp *bp, bool enable) +{ + return ptp_ocp_enable_fpga(&bp->dcf_in->ctrl, + DCF_S_CTRL_ENABLE, enable); +} + +static void +__handle_signal_outputs(struct ptp_ocp *bp, u32 val) +{ + ptp_ocp_irig_out(bp, val & 0x00100010); + ptp_ocp_dcf_out(bp, val & 0x00200020); +} + +static void +__handle_signal_inputs(struct ptp_ocp *bp, u32 val) +{ + ptp_ocp_irig_in(bp, val & 0x00100010); + ptp_ocp_dcf_in(bp, val & 0x00200020); +} + /* * ANT0 == gps (in) * ANT1 == sma1 (in) @@ -1406,6 +1529,9 @@ ptp_ocp_sma_store_output(struct ptp_ocp *bp, u32 val, u32 shift) gpio = ioread32(&bp->sma->gpio2); gpio = (gpio & mask) | (val << shift); + + __handle_signal_outputs(bp, gpio); + iowrite32(gpio, &bp->sma->gpio2); spin_unlock_irqrestore(&bp->lock, flags); @@ -1423,6 +1549,9 @@ ptp_ocp_sma_store_inputs(struct ptp_ocp *bp, u32 val, u32 shift) gpio = ioread32(&bp->sma->gpio1); gpio = (gpio & mask) | (val << shift); + + __handle_signal_inputs(bp, gpio); + iowrite32(gpio, &bp->sma->gpio1); spin_unlock_irqrestore(&bp->lock, flags); From d14ee2525d3899332c0922d435bddb703f8b7e22 Mon Sep 17 00:00:00 2001 From: Jonathan Lemon Date: Tue, 14 Sep 2021 19:16:27 -0700 Subject: [PATCH 54/87] ptp: ocp: Add IRIG-B output mode control IRIG-B has several different output formats, the timecard defaults to using B007. Add a control which selects different output modes. Signed-off-by: Jonathan Lemon Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 1a210b77744c..2b3d4282d77a 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -1676,6 +1676,46 @@ gnss_sync_show(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR_RO(gnss_sync); +static ssize_t +irig_b_mode_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + u32 val; + + val = ioread32(&bp->irig_out->ctrl); + val = (val >> 16) & 0x07; + return sysfs_emit(buf, "%d\n", val); +} + +static ssize_t +irig_b_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + unsigned long flags; + int err; + u32 reg; + u8 val; + + err = kstrtou8(buf, 0, &val); + if (err) + return err; + if (val > 7) + return -EINVAL; + + reg = ((val & 0x7) << 16); + + spin_lock_irqsave(&bp->lock, flags); + iowrite32(0, &bp->irig_out->ctrl); /* disable */ + iowrite32(reg, &bp->irig_out->ctrl); /* change mode */ + iowrite32(reg | IRIG_M_CTRL_ENABLE, &bp->irig_out->ctrl); + spin_unlock_irqrestore(&bp->lock, flags); + + return count; +} +static DEVICE_ATTR_RW(irig_b_mode); + static ssize_t clock_source_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1728,6 +1768,7 @@ static struct attribute *timecard_attrs[] = { &dev_attr_sma4.attr, &dev_attr_available_sma_inputs.attr, &dev_attr_available_sma_outputs.attr, + &dev_attr_irig_b_mode.attr, NULL, }; ATTRIBUTE_GROUPS(timecard); From 89260d878253f44bdbb76bcfe18e4a237b0d9d1a Mon Sep 17 00:00:00 2001 From: Jonathan Lemon Date: Tue, 14 Sep 2021 19:16:28 -0700 Subject: [PATCH 55/87] ptp: ocp: Add sysfs attribute utc_tai_offset IRIG and DCF output time in UTC, but the timecard operates on TAI internally. Add an attribute node which allows adding an offset to these modes before output. Signed-off-by: Jonathan Lemon Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 49 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 2b3d4282d77a..2c3af3c9def7 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -230,8 +230,9 @@ struct ptp_ocp { int gnss_port; int mac_port; /* miniature atomic clock */ u8 serial[6]; - int flash_start; bool has_serial; + int flash_start; + u32 utc_tai_offset; }; struct ocp_resource { @@ -741,6 +742,23 @@ ptp_ocp_init_clock(struct ptp_ocp *bp) return 0; } +static void +ptp_ocp_utc_distribute(struct ptp_ocp *bp, u32 val) +{ + unsigned long flags; + + spin_lock_irqsave(&bp->lock, flags); + + bp->utc_tai_offset = val; + + if (bp->irig_out) + iowrite32(val, &bp->irig_out->adj_sec); + if (bp->dcf_out) + iowrite32(val, &bp->dcf_out->adj_sec); + + spin_unlock_irqrestore(&bp->lock, flags); +} + static void ptp_ocp_tod_info(struct ptp_ocp *bp) { @@ -1676,6 +1694,34 @@ gnss_sync_show(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR_RO(gnss_sync); +static ssize_t +utc_tai_offset_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", bp->utc_tai_offset); +} + +static ssize_t +utc_tai_offset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + int err; + u32 val; + + err = kstrtou32(buf, 0, &val); + if (err) + return err; + + ptp_ocp_utc_distribute(bp, val); + + return count; +} +static DEVICE_ATTR_RW(utc_tai_offset); + static ssize_t irig_b_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1769,6 +1815,7 @@ static struct attribute *timecard_attrs[] = { &dev_attr_available_sma_inputs.attr, &dev_attr_available_sma_outputs.attr, &dev_attr_irig_b_mode.attr, + &dev_attr_utc_tai_offset.attr, NULL, }; ATTRIBUTE_GROUPS(timecard); From 065efcc5e976d5e14d04c33e1509caca519b1cf4 Mon Sep 17 00:00:00 2001 From: Jonathan Lemon Date: Tue, 14 Sep 2021 19:16:29 -0700 Subject: [PATCH 56/87] ptp: ocp: Separate the init and info logic On startup, parts of the FPGA need to be initialized - break these out into their own functions, separate from the purely informational blocks. On startup, distrbute the UTC:TAI offset from the NMEA GNSS parser, if it is available. Signed-off-by: Jonathan Lemon Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 96 +++++++++++++++++++++++++++---------------- 1 file changed, 60 insertions(+), 36 deletions(-) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 2c3af3c9def7..ea12f685edf6 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -72,7 +72,7 @@ struct tod_reg { u32 status; u32 uart_polarity; u32 version; - u32 correction_sec; + u32 adj_sec; u32 __pad0[3]; u32 uart_baud; u32 __pad1[3]; @@ -728,16 +728,15 @@ ptp_ocp_init_clock(struct ptp_ocp *bp) sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC; if (!sync) { - ktime_get_real_ts64(&ts); + ktime_get_clocktai_ts64(&ts); ptp_ocp_settime(&bp->ptp_info, &ts); } - if (!ptp_ocp_gettimex(&bp->ptp_info, &ts, NULL)) - dev_info(&bp->pdev->dev, "Time: %lld.%ld, %s\n", - ts.tv_sec, ts.tv_nsec, - sync ? "in-sync" : "UNSYNCED"); - timer_setup(&bp->watchdog, ptp_ocp_watchdog, 0); - mod_timer(&bp->watchdog, jiffies + HZ); + /* If there is a clock supervisor, then enable the watchdog */ + if (bp->pps_to_clk) { + timer_setup(&bp->watchdog, ptp_ocp_watchdog, 0); + mod_timer(&bp->watchdog, jiffies + HZ); + } return 0; } @@ -759,6 +758,21 @@ ptp_ocp_utc_distribute(struct ptp_ocp *bp, u32 val) spin_unlock_irqrestore(&bp->lock, flags); } +static void +ptp_ocp_tod_init(struct ptp_ocp *bp) +{ + u32 ctrl, reg; + + ctrl = ioread32(&bp->tod->ctrl); + ctrl |= TOD_CTRL_PROTOCOL | TOD_CTRL_ENABLE; + ctrl &= ~(TOD_CTRL_DISABLE_FMT_A | TOD_CTRL_DISABLE_FMT_B); + iowrite32(ctrl, &bp->tod->ctrl); + + reg = ioread32(&bp->tod->utc_status); + if (reg & TOD_STATUS_UTC_VALID) + ptp_ocp_utc_distribute(bp, reg & TOD_STATUS_UTC_MASK); +} + static void ptp_ocp_tod_info(struct ptp_ocp *bp) { @@ -776,11 +790,6 @@ ptp_ocp_tod_info(struct ptp_ocp *bp) dev_info(&bp->pdev->dev, "TOD Version %d.%d.%d\n", version >> 24, (version >> 16) & 0xff, version & 0xffff); - ctrl = ioread32(&bp->tod->ctrl); - ctrl |= TOD_CTRL_PROTOCOL | TOD_CTRL_ENABLE; - ctrl &= ~(TOD_CTRL_DISABLE_FMT_A | TOD_CTRL_DISABLE_FMT_B); - iowrite32(ctrl, &bp->tod->ctrl); - ctrl = ioread32(&bp->tod->ctrl); idx = ctrl & TOD_CTRL_PROTOCOL ? 4 : 0; idx += (ctrl >> 16) & 3; @@ -795,7 +804,7 @@ ptp_ocp_tod_info(struct ptp_ocp *bp) reg = ioread32(&bp->tod->status); dev_info(&bp->pdev->dev, "status: %x\n", reg); - reg = ioread32(&bp->tod->correction_sec); + reg = ioread32(&bp->tod->adj_sec); dev_info(&bp->pdev->dev, "correction: %d\n", reg); reg = ioread32(&bp->tod->utc_status); @@ -879,22 +888,6 @@ out: put_device(dev); } -static void -ptp_ocp_info(struct ptp_ocp *bp) -{ - u32 version, select; - - version = ioread32(&bp->reg->version); - select = ioread32(&bp->reg->select); - dev_info(&bp->pdev->dev, "Version %d.%d.%d, clock %s, device ptp%d\n", - version >> 24, (version >> 16) & 0xff, version & 0xffff, - ptp_ocp_select_name_from_val(ptp_ocp_clock, select >> 16), - ptp_clock_index(bp->ptp)); - - if (bp->tod) - ptp_ocp_tod_info(bp); -} - static struct device * ptp_ocp_find_flash(struct ptp_ocp *bp) { @@ -1278,6 +1271,8 @@ ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r) { bp->flash_start = 1024 * 4096; + ptp_ocp_tod_init(bp); + return ptp_ocp_init_clock(bp); } @@ -1927,10 +1922,42 @@ ptp_ocp_complete(struct ptp_ocp *bp) } static void -ptp_ocp_resource_summary(struct ptp_ocp *bp) +ptp_ocp_phc_info(struct ptp_ocp *bp) +{ + struct timespec64 ts; + u32 version, select; + bool sync; + + version = ioread32(&bp->reg->version); + select = ioread32(&bp->reg->select); + dev_info(&bp->pdev->dev, "Version %d.%d.%d, clock %s, device ptp%d\n", + version >> 24, (version >> 16) & 0xff, version & 0xffff, + ptp_ocp_select_name_from_val(ptp_ocp_clock, select >> 16), + ptp_clock_index(bp->ptp)); + + sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC; + if (!ptp_ocp_gettimex(&bp->ptp_info, &ts, NULL)) + dev_info(&bp->pdev->dev, "Time: %lld.%ld, %s\n", + ts.tv_sec, ts.tv_nsec, + sync ? "in-sync" : "UNSYNCED"); +} + +static void +ptp_ocp_serial_info(struct device *dev, const char *name, int port, int baud) +{ + if (port != -1) + dev_info(dev, "%5s: /dev/ttyS%-2d @ %6d\n", name, port, baud); +} + +static void +ptp_ocp_info(struct ptp_ocp *bp) { struct device *dev = &bp->pdev->dev; + ptp_ocp_phc_info(bp); + if (bp->tod) + ptp_ocp_tod_info(bp); + if (bp->image) { u32 ver = ioread32(&bp->image->version); @@ -1942,10 +1969,8 @@ ptp_ocp_resource_summary(struct ptp_ocp *bp) dev_info(dev, "golden image, version %d\n", ver >> 16); } - if (bp->gnss_port != -1) - dev_info(dev, "GNSS @ /dev/ttyS%d 115200\n", bp->gnss_port); - if (bp->mac_port != -1) - dev_info(dev, "MAC @ /dev/ttyS%d 57600\n", bp->mac_port); + ptp_ocp_serial_info(dev, "GNSS", bp->gnss_port, 115200); + ptp_ocp_serial_info(dev, "MAC", bp->mac_port, 57600); } static void @@ -2049,7 +2074,6 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto out; ptp_ocp_info(bp); - ptp_ocp_resource_summary(bp); return 0; From f67bf662d2cffa2ddf19ffa23381d49c9cffd783 Mon Sep 17 00:00:00 2001 From: Jonathan Lemon Date: Tue, 14 Sep 2021 19:16:30 -0700 Subject: [PATCH 57/87] ptp: ocp: Add debugfs entry for timecard Provide a view into the timecard internals for debugging. Signed-off-by: Jonathan Lemon Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 233 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index ea12f685edf6..36924423444e 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -208,6 +209,7 @@ struct ptp_ocp { struct tod_reg __iomem *tod; struct pps_reg __iomem *pps_to_ext; struct pps_reg __iomem *pps_to_clk; + struct gpio_reg __iomem *pps_select; struct gpio_reg __iomem *sma; struct irig_master_reg __iomem *irig_out; struct irig_slave_reg __iomem *irig_in; @@ -224,6 +226,7 @@ struct ptp_ocp { struct platform_device *spi_flash; struct clk_hw *i2c_clk; struct timer_list watchdog; + struct dentry *debug_root; time64_t gnss_lost; int id; int n_irqs; @@ -354,6 +357,10 @@ static struct ocp_resource ocp_fb_resource[] = { OCP_MEM_RESOURCE(image), .offset = 0x00020000, .size = 0x1000, }, + { + OCP_MEM_RESOURCE(pps_select), + .offset = 0x00130000, .size = 0x1000, + }, { OCP_MEM_RESOURCE(sma), .offset = 0x00140000, .size = 0x1000, @@ -1815,6 +1822,225 @@ static struct attribute *timecard_attrs[] = { }; ATTRIBUTE_GROUPS(timecard); +static const char * +gpio_map(u32 gpio, u32 bit, const char *pri, const char *sec, const char *def) +{ + const char *ans; + + if (gpio & (1 << bit)) + ans = pri; + else if (gpio & (1 << (bit + 16))) + ans = sec; + else + ans = def; + return ans; +} + +static void +gpio_multi_map(char *buf, u32 gpio, u32 bit, + const char *pri, const char *sec, const char *def) +{ + char *ans = buf; + + strcpy(ans, def); + if (gpio & (1 << bit)) + ans += sprintf(ans, "%s ", pri); + if (gpio & (1 << (bit + 16))) + ans += sprintf(ans, "%s ", sec); +} + +static int +ptp_ocp_summary_show(struct seq_file *s, void *data) +{ + struct device *dev = s->private; + struct ptp_system_timestamp sts; + u32 sma_in, sma_out, ctrl, val; + struct ts_reg __iomem *ts_reg; + struct timespec64 ts; + struct ptp_ocp *bp; + const char *src; + char *buf; + bool on; + + buf = (char *)__get_free_page(GFP_KERNEL); + if (!buf) + return -ENOMEM; + + bp = dev_get_drvdata(dev); + sma_in = ioread32(&bp->sma->gpio1); + sma_out = ioread32(&bp->sma->gpio2); + + seq_printf(s, "%7s: /dev/ptp%d\n", "PTP", ptp_clock_index(bp->ptp)); + + sma1_show(dev, NULL, buf); + seq_printf(s, " sma1: %s", buf); + + sma2_show(dev, NULL, buf); + seq_printf(s, " sma2: %s", buf); + + sma3_show(dev, NULL, buf); + seq_printf(s, " sma3: %s", buf); + + sma4_show(dev, NULL, buf); + seq_printf(s, " sma4: %s", buf); + + if (bp->ts0) { + ts_reg = bp->ts0->mem; + on = ioread32(&ts_reg->enable); + src = "GNSS"; + seq_printf(s, "%7s: %s, src: %s\n", "TS0", + on ? " ON" : "OFF", src); + } + + if (bp->ts1) { + ts_reg = bp->ts1->mem; + on = ioread32(&ts_reg->enable); + src = gpio_map(sma_in, 2, "sma1", "sma2", "----"); + seq_printf(s, "%7s: %s, src: %s\n", "TS1", + on ? " ON" : "OFF", src); + } + + if (bp->ts2) { + ts_reg = bp->ts2->mem; + on = ioread32(&ts_reg->enable); + src = gpio_map(sma_in, 3, "sma1", "sma2", "----"); + seq_printf(s, "%7s: %s, src: %s\n", "TS2", + on ? " ON" : "OFF", src); + } + + if (bp->irig_out) { + ctrl = ioread32(&bp->irig_out->ctrl); + on = ctrl & IRIG_M_CTRL_ENABLE; + val = ioread32(&bp->irig_out->status); + gpio_multi_map(buf, sma_out, 4, "sma3", "sma4", "----"); + seq_printf(s, "%7s: %s, error: %d, mode %d, out: %s\n", "IRIG", + on ? " ON" : "OFF", val, (ctrl >> 16), buf); + } + + if (bp->irig_in) { + on = ioread32(&bp->irig_in->ctrl) & IRIG_S_CTRL_ENABLE; + val = ioread32(&bp->irig_in->status); + src = gpio_map(sma_in, 4, "sma1", "sma2", "----"); + seq_printf(s, "%7s: %s, error: %d, src: %s\n", "IRIG in", + on ? " ON" : "OFF", val, src); + } + + if (bp->dcf_out) { + on = ioread32(&bp->dcf_out->ctrl) & DCF_M_CTRL_ENABLE; + val = ioread32(&bp->dcf_out->status); + gpio_multi_map(buf, sma_out, 5, "sma3", "sma4", "----"); + seq_printf(s, "%7s: %s, error: %d, out: %s\n", "DCF", + on ? " ON" : "OFF", val, buf); + } + + if (bp->dcf_in) { + on = ioread32(&bp->dcf_in->ctrl) & DCF_S_CTRL_ENABLE; + val = ioread32(&bp->dcf_in->status); + src = gpio_map(sma_in, 5, "sma1", "sma2", "----"); + seq_printf(s, "%7s: %s, error: %d, src: %s\n", "DCF in", + on ? " ON" : "OFF", val, src); + } + + /* compute src for PPS1, used below. */ + if (bp->pps_select) { + val = ioread32(&bp->pps_select->gpio1); + if (val & 0x01) + src = gpio_map(sma_in, 0, "sma1", "sma2", "----"); + else if (val & 0x02) + src = "MAC"; + else if (val & 0x04) + src = "GNSS"; + else + src = "----"; + } else { + src = "?"; + } + + /* assumes automatic switchover/selection */ + val = ioread32(&bp->reg->select); + switch (val >> 16) { + case 0: + sprintf(buf, "----"); + break; + case 2: + sprintf(buf, "IRIG"); + break; + case 3: + sprintf(buf, "%s via PPS1", src); + break; + case 6: + sprintf(buf, "DCF"); + break; + default: + strcpy(buf, "unknown"); + break; + } + val = ioread32(&bp->reg->status); + seq_printf(s, "%7s: %s, state: %s\n", "PHC src", buf, + val & OCP_STATUS_IN_SYNC ? "sync" : "unsynced"); + + /* reuses PPS1 src from earlier */ + seq_printf(s, "MAC PPS1 src: %s\n", src); + + src = gpio_map(sma_in, 1, "sma1", "sma2", "GNSS2"); + seq_printf(s, "MAC PPS2 src: %s\n", src); + + if (!ptp_ocp_gettimex(&bp->ptp_info, &ts, &sts)) { + struct timespec64 sys_ts; + s64 pre_ns, post_ns, ns; + + pre_ns = timespec64_to_ns(&sts.pre_ts); + post_ns = timespec64_to_ns(&sts.post_ts); + ns = (pre_ns + post_ns) / 2; + ns += (s64)bp->utc_tai_offset * NSEC_PER_SEC; + sys_ts = ns_to_timespec64(ns); + + seq_printf(s, "%7s: %lld.%ld == %ptT TAI\n", "PHC", + ts.tv_sec, ts.tv_nsec, &ts); + seq_printf(s, "%7s: %lld.%ld == %ptT UTC offset %d\n", "SYS", + sys_ts.tv_sec, sys_ts.tv_nsec, &sys_ts, + bp->utc_tai_offset); + seq_printf(s, "%7s: PHC:SYS offset: %lld window: %lld\n", "", + timespec64_to_ns(&ts) - ns, + post_ns - pre_ns); + } + + free_page((unsigned long)buf); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(ptp_ocp_summary); + +static struct dentry *ptp_ocp_debugfs_root; + +static void +ptp_ocp_debugfs_add_device(struct ptp_ocp *bp) +{ + struct dentry *d; + + d = debugfs_create_dir(dev_name(&bp->dev), ptp_ocp_debugfs_root); + bp->debug_root = d; + debugfs_create_file("summary", 0444, bp->debug_root, + &bp->dev, &ptp_ocp_summary_fops); +} + +static void +ptp_ocp_debugfs_remove_device(struct ptp_ocp *bp) +{ + debugfs_remove_recursive(bp->debug_root); +} + +static void +ptp_ocp_debugfs_init(void) +{ + ptp_ocp_debugfs_root = debugfs_create_dir("timecard", NULL); +} + +static void +ptp_ocp_debugfs_fini(void) +{ + debugfs_remove_recursive(ptp_ocp_debugfs_root); +} + static void ptp_ocp_dev_release(struct device *dev) { @@ -1918,6 +2144,8 @@ ptp_ocp_complete(struct ptp_ocp *bp) if (device_add_groups(&bp->dev, timecard_groups)) pr_err("device add groups failed\n"); + ptp_ocp_debugfs_add_device(bp); + return 0; } @@ -1988,6 +2216,7 @@ ptp_ocp_detach_sysfs(struct ptp_ocp *bp) static void ptp_ocp_detach(struct ptp_ocp *bp) { + ptp_ocp_debugfs_remove_device(bp); ptp_ocp_detach_sysfs(bp); if (timer_pending(&bp->watchdog)) del_timer_sync(&bp->watchdog); @@ -2157,6 +2386,8 @@ ptp_ocp_init(void) const char *what; int err; + ptp_ocp_debugfs_init(); + what = "timecard class"; err = class_register(&timecard_class); if (err) @@ -2179,6 +2410,7 @@ out_register: out_notifier: class_unregister(&timecard_class); out: + ptp_ocp_debugfs_fini(); pr_err(KBUILD_MODNAME ": failed to register %s: %d\n", what, err); return err; } @@ -2189,6 +2421,7 @@ ptp_ocp_fini(void) bus_unregister_notifier(&i2c_bus_type, &ptp_ocp_i2c_notifier); pci_unregister_driver(&ptp_ocp_driver); class_unregister(&timecard_class); + ptp_ocp_debugfs_fini(); } module_init(ptp_ocp_init); From e3516bb45078fbcafeefe20a990080b9c838ac72 Mon Sep 17 00:00:00 2001 From: Jonathan Lemon Date: Tue, 14 Sep 2021 19:16:31 -0700 Subject: [PATCH 58/87] ptp: ocp: Add NMEA output The timecard can provide a NMEA-1083 ZDA (time and date) output string on a serial port, which can be used to drive other devices. Add the NMEA resources, and the serial port as a sysfs attribute. Signed-off-by: Jonathan Lemon Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 57 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 36924423444e..74b5561fbdae 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -215,6 +215,7 @@ struct ptp_ocp { struct irig_slave_reg __iomem *irig_in; struct dcf_master_reg __iomem *dcf_out; struct dcf_slave_reg __iomem *dcf_in; + struct tod_reg __iomem *nmea_out; struct ptp_ocp_ext_src *pps; struct ptp_ocp_ext_src *ts0; struct ptp_ocp_ext_src *ts1; @@ -232,6 +233,7 @@ struct ptp_ocp { int n_irqs; int gnss_port; int mac_port; /* miniature atomic clock */ + int nmea_port; u8 serial[6]; bool has_serial; int flash_start; @@ -289,8 +291,9 @@ static int ptp_ocp_ts_enable(void *priv, bool enable); * 5: MAC * 6: TS2 * 7: I2C controller - * 8: HWICAP + * 8: HWICAP (notused) * 9: SPI Flash + * 10: NMEA */ static struct ocp_resource ocp_fb_resource[] = { @@ -353,6 +356,10 @@ static struct ocp_resource ocp_fb_resource[] = { OCP_MEM_RESOURCE(dcf_out), .offset = 0x010A0000, .size = 0x10000, }, + { + OCP_MEM_RESOURCE(nmea_out), + .offset = 0x010B0000, .size = 0x10000, + }, { OCP_MEM_RESOURCE(image), .offset = 0x00020000, .size = 0x1000, @@ -381,6 +388,10 @@ static struct ocp_resource ocp_fb_resource[] = { OCP_SERIAL_RESOURCE(mac_port), .offset = 0x00180000 + 0x1000, .irq_vec = 5, }, + { + OCP_SERIAL_RESOURCE(nmea_port), + .offset = 0x00190000 + 0x1000, .irq_vec = 10, + }, { OCP_SPI_RESOURCE(spi_flash), .offset = 0x00310000, .size = 0x10000, .irq_vec = 9, @@ -761,6 +772,8 @@ ptp_ocp_utc_distribute(struct ptp_ocp *bp, u32 val) iowrite32(val, &bp->irig_out->adj_sec); if (bp->dcf_out) iowrite32(val, &bp->dcf_out->adj_sec); + if (bp->nmea_out) + iowrite32(val, &bp->nmea_out->adj_sec); spin_unlock_irqrestore(&bp->lock, flags); } @@ -1272,6 +1285,17 @@ ptp_ocp_register_mem(struct ptp_ocp *bp, struct ocp_resource *r) return 0; } +static void +ptp_ocp_nmea_out_init(struct ptp_ocp *bp) +{ + if (!bp->nmea_out) + return; + + iowrite32(0, &bp->nmea_out->ctrl); /* disable */ + iowrite32(7, &bp->nmea_out->uart_baud); /* 115200 */ + iowrite32(1, &bp->nmea_out->ctrl); /* enable */ +} + /* FB specific board initializers; last "resource" registered. */ static int ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r) @@ -1279,6 +1303,7 @@ ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r) bp->flash_start = 1024 * 4096; ptp_ocp_tod_init(bp); + ptp_ocp_nmea_out_init(bp); return ptp_ocp_init_clock(bp); } @@ -1941,6 +1966,13 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) on ? " ON" : "OFF", val, src); } + if (bp->nmea_out) { + on = ioread32(&bp->nmea_out->ctrl) & 1; + val = ioread32(&bp->nmea_out->status); + seq_printf(s, "%7s: %s, error: %d\n", "NMEA", + on ? " ON" : "OFF", val); + } + /* compute src for PPS1, used below. */ if (bp->pps_select) { val = ioread32(&bp->pps_select->gpio1); @@ -2069,6 +2101,7 @@ ptp_ocp_device_init(struct ptp_ocp *bp, struct pci_dev *pdev) spin_lock_init(&bp->lock); bp->gnss_port = -1; bp->mac_port = -1; + bp->nmea_port = -1; bp->pdev = pdev; device_initialize(&bp->dev); @@ -2134,6 +2167,10 @@ ptp_ocp_complete(struct ptp_ocp *bp) sprintf(buf, "ttyS%d", bp->mac_port); ptp_ocp_link_child(bp, buf, "ttyMAC"); } + if (bp->nmea_port != -1) { + sprintf(buf, "ttyS%d", bp->nmea_port); + ptp_ocp_link_child(bp, buf, "ttyNMEA"); + } sprintf(buf, "ptp%d", ptp_clock_index(bp->ptp)); ptp_ocp_link_child(bp, buf, "ptp"); @@ -2180,7 +2217,13 @@ ptp_ocp_serial_info(struct device *dev, const char *name, int port, int baud) static void ptp_ocp_info(struct ptp_ocp *bp) { + static int nmea_baud[] = { + 1200, 2400, 4800, 9600, 19200, 38400, + 57600, 115200, 230400, 460800, 921600, + 1000000, 2000000 + }; struct device *dev = &bp->pdev->dev; + u32 reg; ptp_ocp_phc_info(bp); if (bp->tod) @@ -2199,6 +2242,14 @@ ptp_ocp_info(struct ptp_ocp *bp) } ptp_ocp_serial_info(dev, "GNSS", bp->gnss_port, 115200); ptp_ocp_serial_info(dev, "MAC", bp->mac_port, 57600); + if (bp->nmea_out && bp->nmea_port != -1) { + int baud = -1; + + reg = ioread32(&bp->nmea_out->uart_baud); + if (reg < ARRAY_SIZE(nmea_baud)) + baud = nmea_baud[reg]; + ptp_ocp_serial_info(dev, "NMEA", bp->nmea_port, baud); + } } static void @@ -2232,6 +2283,8 @@ ptp_ocp_detach(struct ptp_ocp *bp) serial8250_unregister_port(bp->gnss_port); if (bp->mac_port != -1) serial8250_unregister_port(bp->mac_port); + if (bp->nmea_port != -1) + serial8250_unregister_port(bp->nmea_port); if (bp->spi_flash) platform_device_unregister(bp->spi_flash); if (bp->i2c_ctrl) @@ -2278,7 +2331,7 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id) * allow this - if not all of the IRQ's are returned, skip the * extra devices and just register the clock. */ - err = pci_alloc_irq_vectors(pdev, 1, 10, PCI_IRQ_MSI | PCI_IRQ_MSIX); + err = pci_alloc_irq_vectors(pdev, 1, 11, PCI_IRQ_MSI | PCI_IRQ_MSIX); if (err < 0) { dev_err(&pdev->dev, "alloc_irq_vectors err: %d\n", err); goto out; From 71d7e0850476918b2932ac1dca5a135a5584f742 Mon Sep 17 00:00:00 2001 From: Jonathan Lemon Date: Tue, 14 Sep 2021 19:16:32 -0700 Subject: [PATCH 59/87] ptp: ocp: Add second GNSS device Upcoming boards may have a second GNSS receiver, getting information from a different constellation than the first receiver, which provides some measure of anti-spoofing. Expose the sysfs attribute for this device, if detected. Signed-off-by: Jonathan Lemon Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 74b5561fbdae..d0e3096f53f6 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -232,6 +232,7 @@ struct ptp_ocp { int id; int n_irqs; int gnss_port; + int gnss2_port; int mac_port; /* miniature atomic clock */ int nmea_port; u8 serial[6]; @@ -286,8 +287,8 @@ static int ptp_ocp_ts_enable(void *priv, bool enable); * 0: N/C * 1: TS0 * 2: TS1 - * 3: GPS - * 4: GPS2 (n/c) + * 3: GNSS + * 4: GNSS2 * 5: MAC * 6: TS2 * 7: I2C controller @@ -384,6 +385,10 @@ static struct ocp_resource ocp_fb_resource[] = { OCP_SERIAL_RESOURCE(gnss_port), .offset = 0x00160000 + 0x1000, .irq_vec = 3, }, + { + OCP_SERIAL_RESOURCE(gnss2_port), + .offset = 0x00170000 + 0x1000, .irq_vec = 4, + }, { OCP_SERIAL_RESOURCE(mac_port), .offset = 0x00180000 + 0x1000, .irq_vec = 5, @@ -2100,6 +2105,7 @@ ptp_ocp_device_init(struct ptp_ocp *bp, struct pci_dev *pdev) bp->ptp_info = ptp_ocp_clock_info; spin_lock_init(&bp->lock); bp->gnss_port = -1; + bp->gnss2_port = -1; bp->mac_port = -1; bp->nmea_port = -1; bp->pdev = pdev; @@ -2163,6 +2169,10 @@ ptp_ocp_complete(struct ptp_ocp *bp) sprintf(buf, "ttyS%d", bp->gnss_port); ptp_ocp_link_child(bp, buf, "ttyGNSS"); } + if (bp->gnss2_port != -1) { + sprintf(buf, "ttyS%d", bp->gnss2_port); + ptp_ocp_link_child(bp, buf, "ttyGNSS2"); + } if (bp->mac_port != -1) { sprintf(buf, "ttyS%d", bp->mac_port); ptp_ocp_link_child(bp, buf, "ttyMAC"); @@ -2241,6 +2251,7 @@ ptp_ocp_info(struct ptp_ocp *bp) ver >> 16); } ptp_ocp_serial_info(dev, "GNSS", bp->gnss_port, 115200); + ptp_ocp_serial_info(dev, "GNSS2", bp->gnss2_port, 115200); ptp_ocp_serial_info(dev, "MAC", bp->mac_port, 57600); if (bp->nmea_out && bp->nmea_port != -1) { int baud = -1; @@ -2281,6 +2292,8 @@ ptp_ocp_detach(struct ptp_ocp *bp) ptp_ocp_unregister_ext(bp->pps); if (bp->gnss_port != -1) serial8250_unregister_port(bp->gnss_port); + if (bp->gnss2_port != -1) + serial8250_unregister_port(bp->gnss2_port); if (bp->mac_port != -1) serial8250_unregister_port(bp->mac_port); if (bp->nmea_port != -1) From a62a56d04e63cf0ececb5dd339811f0a14b7e77c Mon Sep 17 00:00:00 2001 From: Jonathan Lemon Date: Tue, 14 Sep 2021 19:16:33 -0700 Subject: [PATCH 60/87] ptp: ocp: Enable 4th timestamper / PPS generator A 4th timestamper is added which timestamps the output of the PHC. The clock nanosecond offset is not always zero, so when compared to other timestampers, this provides precise measurements. Also, the timestamper interrupt from the PHC can be used to generate a PPS signal for /dev/pps. Also allow PTP_CLK_REQ_PEROUT requests for a 1PPS output, but do not actually configure any output pins, this is done via sysfs. Signed-off-by: Jonathan Lemon Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 82 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 74 insertions(+), 8 deletions(-) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index d0e3096f53f6..be8ab727a4ef 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -191,7 +191,7 @@ struct ptp_ocp_i2c_info { struct ptp_ocp_ext_info { int index; irqreturn_t (*irq_fcn)(int irq, void *priv); - int (*enable)(void *priv, bool enable); + int (*enable)(void *priv, u32 req, bool enable); }; struct ptp_ocp_ext_src { @@ -237,10 +237,14 @@ struct ptp_ocp { int nmea_port; u8 serial[6]; bool has_serial; + u32 pps_req_map; int flash_start; u32 utc_tai_offset; }; +#define OCP_REQ_TIMESTAMP BIT(0) +#define OCP_REQ_PPS BIT(1) + struct ocp_resource { unsigned long offset; int size; @@ -258,7 +262,7 @@ static int ptp_ocp_register_serial(struct ptp_ocp *bp, struct ocp_resource *r); static int ptp_ocp_register_ext(struct ptp_ocp *bp, struct ocp_resource *r); static int ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r); static irqreturn_t ptp_ocp_ts_irq(int irq, void *priv); -static int ptp_ocp_ts_enable(void *priv, bool enable); +static int ptp_ocp_ts_enable(void *priv, u32 req, bool enable); #define bp_assign_entry(bp, res, val) ({ \ uintptr_t addr = (uintptr_t)(bp) + (res)->bp_offset; \ @@ -284,7 +288,7 @@ static int ptp_ocp_ts_enable(void *priv, bool enable); OCP_RES_LOCATION(member), .setup = ptp_ocp_register_ext /* This is the MSI vector mapping used. - * 0: N/C + * 0: TS3 (and PPS) * 1: TS0 * 2: TS1 * 3: GNSS @@ -329,6 +333,15 @@ static struct ocp_resource ocp_fb_resource[] = { .enable = ptp_ocp_ts_enable, }, }, + { + OCP_EXT_RESOURCE(pps), + .offset = 0x010C0000, .size = 0x10000, .irq_vec = 0, + .extra = &(struct ptp_ocp_ext_info) { + .index = 3, + .irq_fcn = ptp_ocp_ts_irq, + .enable = ptp_ocp_ts_enable, + }, + }, { OCP_MEM_RESOURCE(pps_to_ext), .offset = 0x01030000, .size = 0x10000, @@ -634,10 +647,12 @@ ptp_ocp_enable(struct ptp_clock_info *ptp_info, struct ptp_clock_request *rq, { struct ptp_ocp *bp = container_of(ptp_info, struct ptp_ocp, ptp_info); struct ptp_ocp_ext_src *ext = NULL; + u32 req; int err; switch (rq->type) { case PTP_CLK_REQ_EXTTS: + req = OCP_REQ_TIMESTAMP; switch (rq->extts.index) { case 0: ext = bp->ts0; @@ -648,18 +663,30 @@ ptp_ocp_enable(struct ptp_clock_info *ptp_info, struct ptp_clock_request *rq, case 2: ext = bp->ts2; break; + case 3: + ext = bp->pps; + break; } break; case PTP_CLK_REQ_PPS: + req = OCP_REQ_PPS; ext = bp->pps; break; + case PTP_CLK_REQ_PEROUT: + if (on && + (rq->perout.period.sec != 1 || rq->perout.period.nsec != 0)) + return -EINVAL; + /* This is a request for 1PPS on an output SMA. + * Allow, but assume manual configuration. + */ + return 0; default: return -EOPNOTSUPP; } err = -ENXIO; if (ext) - err = ext->info->enable(ext, on); + err = ext->info->enable(ext, req, on); return err; } @@ -675,7 +702,8 @@ static const struct ptp_clock_info ptp_ocp_clock_info = { .adjphase = ptp_ocp_adjphase, .enable = ptp_ocp_enable, .pps = true, - .n_ext_ts = 3, + .n_ext_ts = 4, + .n_per_out = 1, }; static void @@ -1163,6 +1191,16 @@ ptp_ocp_ts_irq(int irq, void *priv) struct ptp_clock_event ev; u32 sec, nsec; + if (ext == ext->bp->pps) { + if (ext->bp->pps_req_map & OCP_REQ_PPS) { + ev.type = PTP_CLOCK_PPS; + ptp_clock_event(ext->bp->ptp, &ev); + } + + if ((ext->bp->pps_req_map & ~OCP_REQ_PPS) == 0) + goto out; + } + /* XXX should fix API - this converts s/ns -> ts -> s/ns */ sec = ioread32(®->time_sec); nsec = ioread32(®->time_ns); @@ -1173,16 +1211,31 @@ ptp_ocp_ts_irq(int irq, void *priv) ptp_clock_event(ext->bp->ptp, &ev); +out: iowrite32(1, ®->intr); /* write 1 to ack */ return IRQ_HANDLED; } static int -ptp_ocp_ts_enable(void *priv, bool enable) +ptp_ocp_ts_enable(void *priv, u32 req, bool enable) { struct ptp_ocp_ext_src *ext = priv; struct ts_reg __iomem *reg = ext->mem; + struct ptp_ocp *bp = ext->bp; + + if (ext == bp->pps) { + u32 old_map = bp->pps_req_map; + + if (enable) + bp->pps_req_map |= req; + else + bp->pps_req_map &= ~req; + + /* if no state change, just return */ + if ((!!old_map ^ !!bp->pps_req_map) == 0) + return 0; + } if (enable) { iowrite32(1, ®->enable); @@ -1199,7 +1252,7 @@ ptp_ocp_ts_enable(void *priv, bool enable) static void ptp_ocp_unregister_ext(struct ptp_ocp_ext_src *ext) { - ext->info->enable(ext, false); + ext->info->enable(ext, ~0, false); pci_free_irq(ext->bp->pdev, ext->irq_vec, ext); kfree(ext); } @@ -1889,8 +1942,8 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) struct timespec64 ts; struct ptp_ocp *bp; const char *src; + bool on, map; char *buf; - bool on; buf = (char *)__get_free_page(GFP_KERNEL); if (!buf) @@ -1938,6 +1991,19 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) on ? " ON" : "OFF", src); } + if (bp->pps) { + ts_reg = bp->pps->mem; + src = "PHC"; + on = ioread32(&ts_reg->enable); + map = !!(bp->pps_req_map & OCP_REQ_TIMESTAMP); + seq_printf(s, "%7s: %s, src: %s\n", "TS3", + on & map ? " ON" : "OFF", src); + + map = !!(bp->pps_req_map & OCP_REQ_PPS); + seq_printf(s, "%7s: %s, src: %s\n", "PPS", + on & map ? " ON" : "OFF", src); + } + if (bp->irig_out) { ctrl = ioread32(&bp->irig_out->ctrl); on = ctrl & IRIG_M_CTRL_ENABLE; From 6d59d4fa1789e0ca51dc33494376272a61b80104 Mon Sep 17 00:00:00 2001 From: Jonathan Lemon Date: Tue, 14 Sep 2021 19:16:34 -0700 Subject: [PATCH 61/87] ptp: ocp: Have FPGA fold in ns adjustment for adjtime. The current implementation of adjtime uses gettime/settime to perform nanosecond adjustments. This introduces addtional phase errors due to delays. Instead, use the FPGA's ability to just apply the nanosecond adjustment to the clock directly. Signed-off-by: Jonathan Lemon Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index be8ab727a4ef..e6779a712bbb 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -594,9 +594,6 @@ ptp_ocp_settime(struct ptp_clock_info *ptp_info, const struct timespec64 *ts) struct ptp_ocp *bp = container_of(ptp_info, struct ptp_ocp, ptp_info); unsigned long flags; - if (ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC) - return 0; - spin_lock_irqsave(&bp->lock, flags); __ptp_ocp_settime_locked(bp, ts); spin_unlock_irqrestore(&bp->lock, flags); @@ -604,26 +601,39 @@ ptp_ocp_settime(struct ptp_clock_info *ptp_info, const struct timespec64 *ts) return 0; } +static void +__ptp_ocp_adjtime_locked(struct ptp_ocp *bp, u64 adj_val) +{ + u32 select, ctrl; + + select = ioread32(&bp->reg->select); + iowrite32(OCP_SELECT_CLK_REG, &bp->reg->select); + + iowrite32(adj_val, &bp->reg->offset_ns); + iowrite32(adj_val & 0x7f, &bp->reg->offset_window_ns); + + ctrl = OCP_CTRL_ADJUST_OFFSET | OCP_CTRL_ENABLE; + iowrite32(ctrl, &bp->reg->ctrl); + + /* restore clock selection */ + iowrite32(select >> 16, &bp->reg->select); +} + static int ptp_ocp_adjtime(struct ptp_clock_info *ptp_info, s64 delta_ns) { struct ptp_ocp *bp = container_of(ptp_info, struct ptp_ocp, ptp_info); - struct timespec64 ts; unsigned long flags; - int err; + u32 adj_ns, sign; - if (ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC) - return 0; + sign = delta_ns < 0 ? BIT(31) : 0; + adj_ns = sign ? -delta_ns : delta_ns; spin_lock_irqsave(&bp->lock, flags); - err = __ptp_ocp_gettime_locked(bp, &ts, NULL); - if (likely(!err)) { - timespec64_add_ns(&ts, delta_ns); - __ptp_ocp_settime_locked(bp, &ts); - } + __ptp_ocp_adjtime_locked(bp, sign | adj_ns); spin_unlock_irqrestore(&bp->lock, flags); - return err; + return 0; } static int @@ -636,7 +646,7 @@ ptp_ocp_null_adjfine(struct ptp_clock_info *ptp_info, long scaled_ppm) } static int -ptp_ocp_adjphase(struct ptp_clock_info *ptp_info, s32 phase_ns) +ptp_ocp_null_adjphase(struct ptp_clock_info *ptp_info, s32 phase_ns) { return -EOPNOTSUPP; } @@ -699,7 +709,7 @@ static const struct ptp_clock_info ptp_ocp_clock_info = { .settime64 = ptp_ocp_settime, .adjtime = ptp_ocp_adjtime, .adjfine = ptp_ocp_null_adjfine, - .adjphase = ptp_ocp_adjphase, + .adjphase = ptp_ocp_null_adjphase, .enable = ptp_ocp_enable, .pps = true, .n_ext_ts = 4, From 1acffc6e09ede525ecf733b1a700fdad8db9a203 Mon Sep 17 00:00:00 2001 From: Jonathan Lemon Date: Tue, 14 Sep 2021 19:16:35 -0700 Subject: [PATCH 62/87] ptp: ocp: Add timestamp window adjustment The following process is used to read the PHC clock and correlate the reading with the "correct" system time. - get starting timestamp - issue PCI write command - issue PCI read command - get ending timestamp - read latched sec/nsec registers The write command is posted to PCI bus and returns. When the write arrives at the FPGA, the PHC time is latched into the sec/nsec registers, and a flag is set indicating the registers are valid. The read command returns this flag, and the time retrieval proceeds. Below is a non-scaled picture of the timing diagram involved. The PHC time corresponds to some SYS time between [start, end]. Userspace usually uses the midpoint between [start, end] to estimate the PCI delay and match this with the PHC time. [start] | | write |-------+ | | \ | read |----+ +----->| | \ * PHC time latched into register | \ | midpoint | +------->| | | | | | +----| | / | |<--------+ | [end] | | As the diagram indicates, the PHC time is latched before the midpoint, so the system clock time is slightly off the real PHC time. This shows up as a phase error with an oscilliscope. The workaround here is to provide a tunable which reduces (shrinks) the end time in the above diagram. This in turn moves the calculated midpoint so the system time and PHC time are in agreemment. Currently, the adjustment reduces the end time by 3/16th of the entire window. E.g.: [start, end] ==> [start, (end - (3/16 * end)], which produces reasonably good results. Also reduce delays by just writing to the clock control register instead of performing a read/modify/write sequence, as the contents of the control register are known. Signed-off-by: Jonathan Lemon Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 77 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 11 deletions(-) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index e6779a712bbb..844b1401cc5d 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -240,6 +240,7 @@ struct ptp_ocp { u32 pps_req_map; int flash_start; u32 utc_tai_offset; + u32 ts_window_adjust; }; #define OCP_REQ_TIMESTAMP BIT(0) @@ -528,10 +529,9 @@ __ptp_ocp_gettime_locked(struct ptp_ocp *bp, struct timespec64 *ts, u32 ctrl, time_sec, time_ns; int i; - ctrl = ioread32(&bp->reg->ctrl); - ctrl |= OCP_CTRL_READ_TIME_REQ; - ptp_read_system_prets(sts); + + ctrl = OCP_CTRL_READ_TIME_REQ | OCP_CTRL_ENABLE; iowrite32(ctrl, &bp->reg->ctrl); for (i = 0; i < 100; i++) { @@ -541,6 +541,12 @@ __ptp_ocp_gettime_locked(struct ptp_ocp *bp, struct timespec64 *ts, } ptp_read_system_postts(sts); + if (sts && bp->ts_window_adjust) { + s64 ns = timespec64_to_ns(&sts->post_ts); + + sts->post_ts = ns_to_timespec64(ns - bp->ts_window_adjust); + } + time_ns = ioread32(&bp->reg->time_ns); time_sec = ioread32(&bp->reg->time_sec); @@ -580,8 +586,7 @@ __ptp_ocp_settime_locked(struct ptp_ocp *bp, const struct timespec64 *ts) iowrite32(time_ns, &bp->reg->adjust_ns); iowrite32(time_sec, &bp->reg->adjust_sec); - ctrl = ioread32(&bp->reg->ctrl); - ctrl |= OCP_CTRL_ADJUST_TIME; + ctrl = OCP_CTRL_ADJUST_TIME | OCP_CTRL_ENABLE; iowrite32(ctrl, &bp->reg->ctrl); /* restore clock selection */ @@ -726,8 +731,7 @@ __ptp_ocp_clear_drift_locked(struct ptp_ocp *bp) iowrite32(0, &bp->reg->drift_ns); - ctrl = ioread32(&bp->reg->ctrl); - ctrl |= OCP_CTRL_ADJUST_DRIFT; + ctrl = OCP_CTRL_ADJUST_DRIFT | OCP_CTRL_ENABLE; iowrite32(ctrl, &bp->reg->ctrl); /* restore clock selection */ @@ -759,6 +763,28 @@ ptp_ocp_watchdog(struct timer_list *t) mod_timer(&bp->watchdog, jiffies + HZ); } +static void +ptp_ocp_estimate_pci_timing(struct ptp_ocp *bp) +{ + ktime_t start, end; + ktime_t delay; + u32 ctrl; + + ctrl = ioread32(&bp->reg->ctrl); + ctrl = OCP_CTRL_READ_TIME_REQ | OCP_CTRL_ENABLE; + + iowrite32(ctrl, &bp->reg->ctrl); + + start = ktime_get_ns(); + + ctrl = ioread32(&bp->reg->ctrl); + + end = ktime_get_ns(); + + delay = end - start; + bp->ts_window_adjust = (delay >> 5) * 3; +} + static int ptp_ocp_init_clock(struct ptp_ocp *bp) { @@ -766,9 +792,7 @@ ptp_ocp_init_clock(struct ptp_ocp *bp) bool sync; u32 ctrl; - /* make sure clock is enabled */ - ctrl = ioread32(&bp->reg->ctrl); - ctrl |= OCP_CTRL_ENABLE; + ctrl = OCP_CTRL_ENABLE; iowrite32(ctrl, &bp->reg->ctrl); /* NO DRIFT Correction */ @@ -787,6 +811,8 @@ ptp_ocp_init_clock(struct ptp_ocp *bp) return -ENODEV; } + ptp_ocp_estimate_pci_timing(bp); + sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC; if (!sync) { ktime_get_clocktai_ts64(&ts); @@ -1217,7 +1243,7 @@ ptp_ocp_ts_irq(int irq, void *priv) ev.type = PTP_CLOCK_EXTTS; ev.index = ext->info->index; - ev.timestamp = sec * 1000000000ULL + nsec; + ev.timestamp = sec * NSEC_PER_SEC + nsec; ptp_clock_event(ext->bp->ptp, &ev); @@ -1817,6 +1843,34 @@ utc_tai_offset_store(struct device *dev, } static DEVICE_ATTR_RW(utc_tai_offset); +static ssize_t +ts_window_adjust_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", bp->ts_window_adjust); +} + +static ssize_t +ts_window_adjust_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + int err; + u32 val; + + err = kstrtou32(buf, 0, &val); + if (err) + return err; + + bp->ts_window_adjust = val; + + return count; +} +static DEVICE_ATTR_RW(ts_window_adjust); + static ssize_t irig_b_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1911,6 +1965,7 @@ static struct attribute *timecard_attrs[] = { &dev_attr_available_sma_outputs.attr, &dev_attr_irig_b_mode.attr, &dev_attr_utc_tai_offset.attr, + &dev_attr_ts_window_adjust.attr, NULL, }; ATTRIBUTE_GROUPS(timecard); From d7050a2b85ffbc0a2c36326028d6aa9784d6694d Mon Sep 17 00:00:00 2001 From: Jonathan Lemon Date: Tue, 14 Sep 2021 19:16:36 -0700 Subject: [PATCH 63/87] docs: ABI: Add sysfs documentation for timecard This patch describes the sysfs interface implemented by the ptp_ocp driver, under /sys/class/timecard. Signed-off-by: Jonathan Lemon Signed-off-by: David S. Miller --- Documentation/ABI/testing/sysfs-timecard | 174 +++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-timecard diff --git a/Documentation/ABI/testing/sysfs-timecard b/Documentation/ABI/testing/sysfs-timecard new file mode 100644 index 000000000000..97f6773794a5 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-timecard @@ -0,0 +1,174 @@ +What: /sys/class/timecard/ +Date: September 2021 +Contact: Jonathan Lemon +Description: This directory contains files and directories + providing a standardized interface to the ancillary + features of the OpenCompute timecard. + +What: /sys/class/timecard/ocpN/ +Date: September 2021 +Contact: Jonathan Lemon +Description: This directory contains the attributes of the Nth timecard + registered. + +What: /sys/class/timecard/ocpN/available_clock_sources +Date: September 2021 +Contact: Jonathan Lemon +Description: (RO) The list of available time sources that the PHC + uses for clock adjustments. + + ==== ================================================= + NONE no adjustments + PPS adjustments come from the PPS1 selector (default) + TOD adjustments from the GNSS/TOD module + IRIG adjustments from external IRIG-B signal + DCF adjustments from external DCF signal + ==== ================================================= + +What: /sys/class/timecard/ocpN/available_sma_inputs +Date: September 2021 +Contact: Jonathan Lemon +Description: (RO) Set of available destinations (sinks) for a SMA + input signal. + + ===== ================================================ + 10Mhz signal is used as the 10Mhz reference clock + PPS1 signal is sent to the PPS1 selector + PPS2 signal is sent to the PPS2 selector + TS1 signal is sent to timestamper 1 + TS2 signal is sent to timestamper 2 + IRIG signal is sent to the IRIG-B module + DCF signal is sent to the DCF module + ===== ================================================ + +What: /sys/class/timecard/ocpN/available_sma_outputs +Date: May 2021 +Contact: Jonathan Lemon +Description: (RO) Set of available sources for a SMA output signal. + + ===== ================================================ + 10Mhz output is from the 10Mhz reference clock + PHC output PPS is from the PHC clock + MAC output PPS is from the Miniature Atomic Clock + GNSS output PPS is from the GNSS module + GNSS2 output PPS is from the second GNSS module + IRIG output is from the PHC, in IRIG-B format + DCF output is from the PHC, in DCF format + ===== ================================================ + +What: /sys/class/timecard/ocpN/clock_source +Date: September 2021 +Contact: Jonathan Lemon +Description: (RW) Contains the current synchronization source used by + the PHC. May be changed by writing one of the listed + values from the available_clock_sources attribute set. + +What: /sys/class/timecard/ocpN/gnss_sync +Date: September 2021 +Contact: Jonathan Lemon +Description: (RO) Indicates whether a valid GNSS signal is received, + or when the signal was lost. + +What: /sys/class/timecard/ocpN/i2c +Date: September 2021 +Contact: Jonathan Lemon +Description: This optional attribute links to the associated i2c device. + +What: /sys/class/timecard/ocpN/irig_b_mode +Date: September 2021 +Contact: Jonathan Lemon +Description: (RW) An integer from 0-7 indicating the timecode format + of the IRIG-B output signal: B00 + +What: /sys/class/timecard/ocpN/pps +Date: September 2021 +Contact: Jonathan Lemon +Description: This optional attribute links to the associated PPS device. + +What: /sys/class/timecard/ocpN/ptp +Date: September 2021 +Contact: Jonathan Lemon +Description: This attribute links to the associated PTP device. + +What: /sys/class/timecard/ocpN/serialnum +Date: September 2021 +Contact: Jonathan Lemon +Description: (RO) Provides the serial number of the timecard. + +What: /sys/class/timecard/ocpN/sma1 +What: /sys/class/timecard/ocpN/sma2 +What: /sys/class/timecard/ocpN/sma3 +What: /sys/class/timecard/ocpN/sma4 +Date: September 2021 +Contact: Jonathan Lemon +Description: (RW) These attributes specify the direction of the signal + on the associated SMA connectors, and also the signal sink + or source. + + The display format of the attribute is a space separated + list of signals, prefixed by the input/output direction. + + The signal direction may be changed (if supported) by + prefixing the signal list with either "in:" or "out:". + If neither prefix is present, then the direction is unchanged. + + The output signal may be changed by writing one of the listed + values from the available_sma_outputs attribute set. + + The input destinations may be changed by writing multiple + values from the available_sma_inputs attribute set, + separated by spaces. If there are duplicated input + destinations between connectors, the lowest numbered SMA + connector is given priority. + + Note that not all input combinations may make sense. + + The 10Mhz reference clock input is currently only valid + on SMA1 and may not be combined with other destination sinks. + +What: /sys/class/timecard/ocpN/ts_window_adjust +Date: September 2021 +Contact: Jonathan Lemon +Description: (RW) When retrieving the PHC with the PTP SYS_OFFSET_EXTENDED + ioctl, a system timestamp is made before and after the PHC + time is retrieved. The midpoint between the two system + timestamps is usually taken to be the SYS time associated + with the PHC time. This estimate may be wrong, as it depends + on PCI latencies, and when the PHC time was latched + + The attribute value reduces the end timestamp by the given + number of nanoseconds, so the computed midpoint matches the + retrieved PHC time. + + The initial value is set based on measured PCI latency and + the estimated point where the FPGA latches the PHC time. This + value may be changed by writing an unsigned integer. + +What: /sys/class/timecard/ocpN/ttyGNSS +What: /sys/class/timecard/ocpN/ttyGNSS2 +Date: September 2021 +Contact: Jonathan Lemon +Description: These optional attributes link to the TTY serial ports + associated with the GNSS devices. + +What: /sys/class/timecard/ocpN/ttyMAC +Date: September 2021 +Contact: Jonathan Lemon +Description: This optional attribute links to the TTY serial port + associated with the Miniature Atomic Clock. + +What: /sys/class/timecard/ocpN/ttyNMEA +Date: September 2021 +Contact: Jonathan Lemon +Description: This optional attribute links to the TTY serial port + which outputs the PHC time in NMEA ZDA format. + +What: /sys/class/timecard/ocpN/utc_tai_offset +Date: September 2021 +Contact: Jonathan Lemon +Description: (RW) The DCF and IRIG output signals are in UTC, while the + TimeCard operates on TAI. This attribute allows setting the + offset in seconds, which is added to the TAI timebase for + these formats. + + The offset may be changed by writing an unsigned integer. From 38106b2c433e886344c33c90522343c37edfacea Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Tue, 14 Sep 2021 20:52:51 -0700 Subject: [PATCH 64/87] ibmvnic: Consolidate code in replenish_rx_pool() For better readability, consolidate related code in replenish_rx_pool() and add some comments. Reviewed-by: Rick Lindsley Reviewed-by: Dany Madden Signed-off-by: Sukadev Bhattiprolu Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index a775c69e4fd7..e8b1231be485 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -371,6 +371,8 @@ static void replenish_rx_pool(struct ibmvnic_adapter *adapter, } index = pool->free_map[pool->next_free]; + pool->free_map[pool->next_free] = IBMVNIC_INVALID_MAP; + pool->next_free = (pool->next_free + 1) % pool->size; if (pool->rx_buff[index].skb) dev_err(dev, "Inconsistent free_map!\n"); @@ -380,14 +382,15 @@ static void replenish_rx_pool(struct ibmvnic_adapter *adapter, dst = pool->long_term_buff.buff + offset; memset(dst, 0, pool->buff_size); dma_addr = pool->long_term_buff.addr + offset; - pool->rx_buff[index].data = dst; - pool->free_map[pool->next_free] = IBMVNIC_INVALID_MAP; + /* add the skb to an rx_buff in the pool */ + pool->rx_buff[index].data = dst; pool->rx_buff[index].dma = dma_addr; pool->rx_buff[index].skb = skb; pool->rx_buff[index].pool_index = pool->index; pool->rx_buff[index].size = pool->buff_size; + /* queue the rx_buff for the next send_subcrq_indirect */ sub_crq = &ind_bufp->indir_arr[ind_bufp->index++]; memset(sub_crq, 0, sizeof(*sub_crq)); sub_crq->rx_add.first = IBMVNIC_CRQ_CMD; @@ -405,7 +408,8 @@ static void replenish_rx_pool(struct ibmvnic_adapter *adapter, shift = 8; #endif sub_crq->rx_add.len = cpu_to_be32(pool->buff_size << shift); - pool->next_free = (pool->next_free + 1) % pool->size; + + /* if send_subcrq_indirect queue is full, flush to VIOS */ if (ind_bufp->index == IBMVNIC_MAX_IND_DESCS || i == count - 1) { lpar_rc = From 0f2bf3188c438210dd1f8d92d448d6226f9792e0 Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Tue, 14 Sep 2021 20:52:52 -0700 Subject: [PATCH 65/87] ibmvnic: Fix up some comments and messages Add/update some comments/function headers and fix up some messages. Reviewed-by: Rick Lindsley Reviewed-by: Dany Madden Signed-off-by: Sukadev Bhattiprolu Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 44 ++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index e8b1231be485..cd04c3eb6c41 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -243,14 +243,13 @@ static int alloc_long_term_buff(struct ibmvnic_adapter *adapter, rc = ibmvnic_wait_for_completion(adapter, &adapter->fw_done, 10000); if (rc) { - dev_err(dev, - "Long term map request aborted or timed out,rc = %d\n", + dev_err(dev, "LTB map request aborted or timed out, rc = %d\n", rc); goto out; } if (adapter->fw_done_rc) { - dev_err(dev, "Couldn't map long term buffer,rc = %d\n", + dev_err(dev, "Couldn't map LTB, rc = %d\n", adapter->fw_done_rc); rc = -1; goto out; @@ -281,7 +280,9 @@ static void free_long_term_buff(struct ibmvnic_adapter *adapter, adapter->reset_reason != VNIC_RESET_MOBILITY && adapter->reset_reason != VNIC_RESET_TIMEOUT) send_request_unmap(adapter, ltb->map_id); + dma_free_coherent(dev, ltb->size, ltb->buff, ltb->addr); + ltb->buff = NULL; ltb->map_id = 0; } @@ -574,6 +575,12 @@ static int reset_rx_pools(struct ibmvnic_adapter *adapter) return 0; } +/** + * release_rx_pools() - Release any rx pools attached to @adapter. + * @adapter: ibmvnic adapter + * + * Safe to call this multiple times - even if no pools are attached. + */ static void release_rx_pools(struct ibmvnic_adapter *adapter) { struct ibmvnic_rx_pool *rx_pool; @@ -628,6 +635,9 @@ static int init_rx_pools(struct net_device *netdev) return -1; } + /* Set num_active_rx_pools early. If we fail below after partial + * allocation, release_rx_pools() will know how many to look for. + */ adapter->num_active_rx_pools = rxadd_subcrqs; for (i = 0; i < rxadd_subcrqs; i++) { @@ -646,6 +656,7 @@ static int init_rx_pools(struct net_device *netdev) rx_pool->free_map = kcalloc(rx_pool->size, sizeof(int), GFP_KERNEL); if (!rx_pool->free_map) { + dev_err(dev, "Couldn't alloc free_map %d\n", i); release_rx_pools(adapter); return -1; } @@ -739,10 +750,19 @@ static void release_one_tx_pool(struct ibmvnic_adapter *adapter, free_long_term_buff(adapter, &tx_pool->long_term_buff); } +/** + * release_tx_pools() - Release any tx pools attached to @adapter. + * @adapter: ibmvnic adapter + * + * Safe to call this multiple times - even if no pools are attached. + */ static void release_tx_pools(struct ibmvnic_adapter *adapter) { int i; + /* init_tx_pools() ensures that ->tx_pool and ->tso_pool are + * both NULL or both non-NULL. So we only need to check one. + */ if (!adapter->tx_pool) return; @@ -793,6 +813,7 @@ static int init_one_tx_pool(struct net_device *netdev, static int init_tx_pools(struct net_device *netdev) { struct ibmvnic_adapter *adapter = netdev_priv(netdev); + struct device *dev = &adapter->vdev->dev; int tx_subcrqs; u64 buff_size; int i, rc; @@ -805,17 +826,27 @@ static int init_tx_pools(struct net_device *netdev) adapter->tso_pool = kcalloc(tx_subcrqs, sizeof(struct ibmvnic_tx_pool), GFP_KERNEL); + /* To simplify release_tx_pools() ensure that ->tx_pool and + * ->tso_pool are either both NULL or both non-NULL. + */ if (!adapter->tso_pool) { kfree(adapter->tx_pool); adapter->tx_pool = NULL; return -1; } + /* Set num_active_tx_pools early. If we fail below after partial + * allocation, release_tx_pools() will know how many to look for. + */ adapter->num_active_tx_pools = tx_subcrqs; for (i = 0; i < tx_subcrqs; i++) { buff_size = adapter->req_mtu + VLAN_HLEN; buff_size = ALIGN(buff_size, L1_CACHE_BYTES); + + dev_dbg(dev, "Init tx pool %d [%llu, %llu]\n", + i, adapter->req_tx_entries_per_subcrq, buff_size); + rc = init_one_tx_pool(netdev, &adapter->tx_pool[i], adapter->req_tx_entries_per_subcrq, buff_size); @@ -4774,9 +4805,10 @@ static void handle_query_map_rsp(union ibmvnic_crq *crq, dev_err(dev, "Error %ld in QUERY_MAP_RSP\n", rc); return; } - netdev_dbg(netdev, "page_size = %d\ntot_pages = %d\nfree_pages = %d\n", - crq->query_map_rsp.page_size, crq->query_map_rsp.tot_pages, - crq->query_map_rsp.free_pages); + netdev_dbg(netdev, "page_size = %d\ntot_pages = %u\nfree_pages = %u\n", + crq->query_map_rsp.page_size, + __be32_to_cpu(crq->query_map_rsp.tot_pages), + __be32_to_cpu(crq->query_map_rsp.free_pages)); } static void handle_query_cap_rsp(union ibmvnic_crq *crq, From 0df7b9ad8f844d692d841d550ec72352e78eb51e Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Tue, 14 Sep 2021 20:52:53 -0700 Subject: [PATCH 66/87] ibmvnic: Use/rename local vars in init_rx_pools To make the code more readable, use/rename some local variables. Basically we have a set of pools, num_pools. Each pool has a set of buffers, pool_size and each buffer is of size buff_size. pool_size is a bit ambiguous (whether size in bytes or buffers). Add a comment in the header file to make it explicit. Reviewed-by: Rick Lindsley Reviewed-by: Dany Madden Signed-off-by: Sukadev Bhattiprolu Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 17 +++++++++-------- drivers/net/ethernet/ibm/ibmvnic.h | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index cd04c3eb6c41..688489475791 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -620,14 +620,16 @@ static int init_rx_pools(struct net_device *netdev) struct ibmvnic_adapter *adapter = netdev_priv(netdev); struct device *dev = &adapter->vdev->dev; struct ibmvnic_rx_pool *rx_pool; - int rxadd_subcrqs; + u64 num_pools; + u64 pool_size; /* # of buffers in one pool */ u64 buff_size; int i, j; - rxadd_subcrqs = adapter->num_active_rx_scrqs; + num_pools = adapter->num_active_rx_scrqs; + pool_size = adapter->req_rx_add_entries_per_subcrq; buff_size = adapter->cur_rx_buf_sz; - adapter->rx_pool = kcalloc(rxadd_subcrqs, + adapter->rx_pool = kcalloc(num_pools, sizeof(struct ibmvnic_rx_pool), GFP_KERNEL); if (!adapter->rx_pool) { @@ -638,17 +640,16 @@ static int init_rx_pools(struct net_device *netdev) /* Set num_active_rx_pools early. If we fail below after partial * allocation, release_rx_pools() will know how many to look for. */ - adapter->num_active_rx_pools = rxadd_subcrqs; + adapter->num_active_rx_pools = num_pools; - for (i = 0; i < rxadd_subcrqs; i++) { + for (i = 0; i < num_pools; i++) { rx_pool = &adapter->rx_pool[i]; netdev_dbg(adapter->netdev, "Initializing rx_pool[%d], %lld buffs, %lld bytes each\n", - i, adapter->req_rx_add_entries_per_subcrq, - buff_size); + i, pool_size, buff_size); - rx_pool->size = adapter->req_rx_add_entries_per_subcrq; + rx_pool->size = pool_size; rx_pool->index = i; rx_pool->buff_size = ALIGN(buff_size, L1_CACHE_BYTES); rx_pool->active = 1; diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index 22df602323bc..5652566818fb 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -827,7 +827,7 @@ struct ibmvnic_rx_buff { struct ibmvnic_rx_pool { struct ibmvnic_rx_buff *rx_buff; - int size; + int size; /* # of buffers in the pool */ int index; int buff_size; atomic_t available; From 8243c7ed6d084434cada6d5e6d64d7abab632945 Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Tue, 14 Sep 2021 20:52:54 -0700 Subject: [PATCH 67/87] ibmvnic: Use/rename local vars in init_tx_pools Use/rename local variables in init_tx_pools() for consistency with init_rx_pools() and for readability. Also add some comments Reviewed-by: Rick Lindsley Reviewed-by: Dany Madden Signed-off-by: Sukadev Bhattiprolu Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 688489475791..97041b319beb 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -781,31 +781,31 @@ static void release_tx_pools(struct ibmvnic_adapter *adapter) static int init_one_tx_pool(struct net_device *netdev, struct ibmvnic_tx_pool *tx_pool, - int num_entries, int buf_size) + int pool_size, int buf_size) { struct ibmvnic_adapter *adapter = netdev_priv(netdev); int i; - tx_pool->tx_buff = kcalloc(num_entries, + tx_pool->tx_buff = kcalloc(pool_size, sizeof(struct ibmvnic_tx_buff), GFP_KERNEL); if (!tx_pool->tx_buff) return -1; if (alloc_long_term_buff(adapter, &tx_pool->long_term_buff, - num_entries * buf_size)) + pool_size * buf_size)) return -1; - tx_pool->free_map = kcalloc(num_entries, sizeof(int), GFP_KERNEL); + tx_pool->free_map = kcalloc(pool_size, sizeof(int), GFP_KERNEL); if (!tx_pool->free_map) return -1; - for (i = 0; i < num_entries; i++) + for (i = 0; i < pool_size; i++) tx_pool->free_map[i] = i; tx_pool->consumer_index = 0; tx_pool->producer_index = 0; - tx_pool->num_buffers = num_entries; + tx_pool->num_buffers = pool_size; tx_pool->buf_size = buf_size; return 0; @@ -815,17 +815,20 @@ static int init_tx_pools(struct net_device *netdev) { struct ibmvnic_adapter *adapter = netdev_priv(netdev); struct device *dev = &adapter->vdev->dev; - int tx_subcrqs; + int num_pools; + u64 pool_size; /* # of buffers in pool */ u64 buff_size; int i, rc; - tx_subcrqs = adapter->num_active_tx_scrqs; - adapter->tx_pool = kcalloc(tx_subcrqs, + pool_size = adapter->req_tx_entries_per_subcrq; + num_pools = adapter->num_active_tx_scrqs; + + adapter->tx_pool = kcalloc(num_pools, sizeof(struct ibmvnic_tx_pool), GFP_KERNEL); if (!adapter->tx_pool) return -1; - adapter->tso_pool = kcalloc(tx_subcrqs, + adapter->tso_pool = kcalloc(num_pools, sizeof(struct ibmvnic_tx_pool), GFP_KERNEL); /* To simplify release_tx_pools() ensure that ->tx_pool and * ->tso_pool are either both NULL or both non-NULL. @@ -839,9 +842,9 @@ static int init_tx_pools(struct net_device *netdev) /* Set num_active_tx_pools early. If we fail below after partial * allocation, release_tx_pools() will know how many to look for. */ - adapter->num_active_tx_pools = tx_subcrqs; + adapter->num_active_tx_pools = num_pools; - for (i = 0; i < tx_subcrqs; i++) { + for (i = 0; i < num_pools; i++) { buff_size = adapter->req_mtu + VLAN_HLEN; buff_size = ALIGN(buff_size, L1_CACHE_BYTES); @@ -849,8 +852,7 @@ static int init_tx_pools(struct net_device *netdev) i, adapter->req_tx_entries_per_subcrq, buff_size); rc = init_one_tx_pool(netdev, &adapter->tx_pool[i], - adapter->req_tx_entries_per_subcrq, - buff_size); + pool_size, buff_size); if (rc) { release_tx_pools(adapter); return rc; From 0d1af4fa71241104b8959f83974875dc52bc129f Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Tue, 14 Sep 2021 20:52:55 -0700 Subject: [PATCH 68/87] ibmvnic: init_tx_pools move loop-invariant code In init_tx_pools() move some loop-invariant code out of the loop. Reviewed-by: Rick Lindsley Reviewed-by: Dany Madden Signed-off-by: Sukadev Bhattiprolu Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 97041b319beb..bb9b8aec9c9b 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -843,11 +843,10 @@ static int init_tx_pools(struct net_device *netdev) * allocation, release_tx_pools() will know how many to look for. */ adapter->num_active_tx_pools = num_pools; + buff_size = adapter->req_mtu + VLAN_HLEN; + buff_size = ALIGN(buff_size, L1_CACHE_BYTES); for (i = 0; i < num_pools; i++) { - buff_size = adapter->req_mtu + VLAN_HLEN; - buff_size = ALIGN(buff_size, L1_CACHE_BYTES); - dev_dbg(dev, "Init tx pool %d [%llu, %llu]\n", i, adapter->req_tx_entries_per_subcrq, buff_size); From 129854f061d8925a259f29b16d82bc19a23cf377 Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Tue, 14 Sep 2021 20:52:56 -0700 Subject: [PATCH 69/87] ibmvnic: Use bitmap for LTB map_ids In a follow-on patch, we will reuse long term buffers when possible. When doing so we have to be careful to properly assign map ids. We can no longer assign them sequentially because a lower map id may be available and we could wrap at 255 and collide with an in-use map id. Instead, use a bitmap to track active map ids and to find a free map id. Don't need to take locks here since the map_id only changes during reset and at that time only the reset worker thread should be using the adapter. Noticed this when analyzing an error Dany Madden ran into with the patch set. Reported-by: Dany Madden Reviewed-by: Rick Lindsley Reviewed-by: Dany Madden Signed-off-by: Sukadev Bhattiprolu Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 12 ++++++++---- drivers/net/ethernet/ibm/ibmvnic.h | 3 ++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index bb9b8aec9c9b..4603597a9c10 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -228,8 +228,9 @@ static int alloc_long_term_buff(struct ibmvnic_adapter *adapter, dev_err(dev, "Couldn't alloc long term buffer\n"); return -ENOMEM; } - ltb->map_id = adapter->map_id; - adapter->map_id++; + ltb->map_id = find_first_zero_bit(adapter->map_ids, + MAX_MAP_ID); + bitmap_set(adapter->map_ids, ltb->map_id, 1); mutex_lock(&adapter->fw_lock); adapter->fw_done_rc = 0; @@ -284,6 +285,8 @@ static void free_long_term_buff(struct ibmvnic_adapter *adapter, dma_free_coherent(dev, ltb->size, ltb->buff, ltb->addr); ltb->buff = NULL; + /* mark this map_id free */ + bitmap_clear(adapter->map_ids, ltb->map_id, 1); ltb->map_id = 0; } @@ -1235,8 +1238,6 @@ static int init_resources(struct ibmvnic_adapter *adapter) return rc; } - adapter->map_id = 1; - rc = init_napi(adapter); if (rc) return rc; @@ -5557,6 +5558,9 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) adapter->vdev = dev; adapter->netdev = netdev; adapter->login_pending = false; + memset(&adapter->map_ids, 0, sizeof(adapter->map_ids)); + /* map_ids start at 1, so ensure map_id 0 is always "in-use" */ + bitmap_set(adapter->map_ids, 0, 1); ether_addr_copy(adapter->mac_addr, mac_addr_p); ether_addr_copy(netdev->dev_addr, adapter->mac_addr); diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index 5652566818fb..e97f1aa98c05 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -979,7 +979,8 @@ struct ibmvnic_adapter { u64 opt_tx_entries_per_subcrq; u64 opt_rxba_entries_per_subcrq; __be64 tx_rx_desc_req; - u8 map_id; +#define MAX_MAP_ID 255 + DECLARE_BITMAP(map_ids, MAX_MAP_ID); u32 num_active_rx_scrqs; u32 num_active_rx_pools; u32 num_active_rx_napi; From f8ac0bfa7d7acefecfa18da8e27ff67adaefe45c Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Tue, 14 Sep 2021 20:52:57 -0700 Subject: [PATCH 70/87] ibmvnic: Reuse LTB when possible Reuse the long term buffer during a reset as long as its size has not changed. If the size has changed, free it and allocate a new one of the appropriate size. When we do this, alloc_long_term_buff() and reset_long_term_buff() become identical. Drop reset_long_term_buff(). Reviewed-by: Rick Lindsley Reviewed-by: Dany Madden Signed-off-by: Sukadev Bhattiprolu Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 139 ++++++++++++++++------------- 1 file changed, 75 insertions(+), 64 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 4603597a9c10..dafb36690fdc 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -108,6 +108,8 @@ static int init_crq_queue(struct ibmvnic_adapter *adapter); static int send_query_phys_parms(struct ibmvnic_adapter *adapter); static void ibmvnic_tx_scrq_clean_buffer(struct ibmvnic_adapter *adapter, struct ibmvnic_sub_crq_queue *tx_scrq); +static void free_long_term_buff(struct ibmvnic_adapter *adapter, + struct ibmvnic_long_term_buff *ltb); struct ibmvnic_stat { char name[ETH_GSTRING_LEN]; @@ -214,23 +216,77 @@ static int ibmvnic_wait_for_completion(struct ibmvnic_adapter *adapter, return -ETIMEDOUT; } +/** + * reuse_ltb() - Check if a long term buffer can be reused + * @ltb: The long term buffer to be checked + * @size: The size of the long term buffer. + * + * An LTB can be reused unless its size has changed. + * + * Return: Return true if the LTB can be reused, false otherwise. + */ +static bool reuse_ltb(struct ibmvnic_long_term_buff *ltb, int size) +{ + return (ltb->buff && ltb->size == size); +} + +/** + * alloc_long_term_buff() - Allocate a long term buffer (LTB) + * + * @adapter: ibmvnic adapter associated to the LTB + * @ltb: container object for the LTB + * @size: size of the LTB + * + * Allocate an LTB of the specified size and notify VIOS. + * + * If the given @ltb already has the correct size, reuse it. Otherwise if + * its non-NULL, free it. Then allocate a new one of the correct size. + * Notify the VIOS either way since we may now be working with a new VIOS. + * + * Allocating larger chunks of memory during resets, specially LPM or under + * low memory situations can cause resets to fail/timeout and for LPAR to + * lose connectivity. So hold onto the LTB even if we fail to communicate + * with the VIOS and reuse it on next open. Free LTB when adapter is closed. + * + * Return: 0 if we were able to allocate the LTB and notify the VIOS and + * a negative value otherwise. + */ static int alloc_long_term_buff(struct ibmvnic_adapter *adapter, struct ibmvnic_long_term_buff *ltb, int size) { struct device *dev = &adapter->vdev->dev; int rc; - ltb->size = size; - ltb->buff = dma_alloc_coherent(dev, ltb->size, <b->addr, - GFP_KERNEL); - - if (!ltb->buff) { - dev_err(dev, "Couldn't alloc long term buffer\n"); - return -ENOMEM; + if (!reuse_ltb(ltb, size)) { + dev_dbg(dev, + "LTB size changed from 0x%llx to 0x%x, reallocating\n", + ltb->size, size); + free_long_term_buff(adapter, ltb); } - ltb->map_id = find_first_zero_bit(adapter->map_ids, - MAX_MAP_ID); - bitmap_set(adapter->map_ids, ltb->map_id, 1); + + if (ltb->buff) { + dev_dbg(dev, "Reusing LTB [map %d, size 0x%llx]\n", + ltb->map_id, ltb->size); + } else { + ltb->buff = dma_alloc_coherent(dev, size, <b->addr, + GFP_KERNEL); + if (!ltb->buff) { + dev_err(dev, "Couldn't alloc long term buffer\n"); + return -ENOMEM; + } + ltb->size = size; + + ltb->map_id = find_first_zero_bit(adapter->map_ids, + MAX_MAP_ID); + bitmap_set(adapter->map_ids, ltb->map_id, 1); + + dev_dbg(dev, + "Allocated new LTB [map %d, size 0x%llx]\n", + ltb->map_id, ltb->size); + } + + /* Ensure ltb is zeroed - specially when reusing it. */ + memset(ltb->buff, 0, ltb->size); mutex_lock(&adapter->fw_lock); adapter->fw_done_rc = 0; @@ -257,10 +313,7 @@ static int alloc_long_term_buff(struct ibmvnic_adapter *adapter, } rc = 0; out: - if (rc) { - dma_free_coherent(dev, ltb->size, ltb->buff, ltb->addr); - ltb->buff = NULL; - } + /* don't free LTB on communication error - see function header */ mutex_unlock(&adapter->fw_lock); return rc; } @@ -290,43 +343,6 @@ static void free_long_term_buff(struct ibmvnic_adapter *adapter, ltb->map_id = 0; } -static int reset_long_term_buff(struct ibmvnic_adapter *adapter, - struct ibmvnic_long_term_buff *ltb) -{ - struct device *dev = &adapter->vdev->dev; - int rc; - - memset(ltb->buff, 0, ltb->size); - - mutex_lock(&adapter->fw_lock); - adapter->fw_done_rc = 0; - - reinit_completion(&adapter->fw_done); - rc = send_request_map(adapter, ltb->addr, ltb->size, ltb->map_id); - if (rc) { - mutex_unlock(&adapter->fw_lock); - return rc; - } - - rc = ibmvnic_wait_for_completion(adapter, &adapter->fw_done, 10000); - if (rc) { - dev_info(dev, - "Reset failed, long term map request timed out or aborted\n"); - mutex_unlock(&adapter->fw_lock); - return rc; - } - - if (adapter->fw_done_rc) { - dev_info(dev, - "Reset failed, attempting to free and reallocate buffer\n"); - free_long_term_buff(adapter, ltb); - mutex_unlock(&adapter->fw_lock); - return alloc_long_term_buff(adapter, ltb, ltb->size); - } - mutex_unlock(&adapter->fw_lock); - return 0; -} - static void deactivate_rx_pools(struct ibmvnic_adapter *adapter) { int i; @@ -548,18 +564,10 @@ static int reset_rx_pools(struct ibmvnic_adapter *adapter) netdev_dbg(adapter->netdev, "Re-setting rx_pool[%d]\n", i); - if (rx_pool->buff_size != buff_size) { - free_long_term_buff(adapter, &rx_pool->long_term_buff); - rx_pool->buff_size = ALIGN(buff_size, L1_CACHE_BYTES); - rc = alloc_long_term_buff(adapter, - &rx_pool->long_term_buff, - rx_pool->size * - rx_pool->buff_size); - } else { - rc = reset_long_term_buff(adapter, - &rx_pool->long_term_buff); - } - + rx_pool->buff_size = ALIGN(buff_size, L1_CACHE_BYTES); + rc = alloc_long_term_buff(adapter, + &rx_pool->long_term_buff, + rx_pool->size * rx_pool->buff_size); if (rc) return rc; @@ -694,9 +702,12 @@ static int init_rx_pools(struct net_device *netdev) static int reset_one_tx_pool(struct ibmvnic_adapter *adapter, struct ibmvnic_tx_pool *tx_pool) { + struct ibmvnic_long_term_buff *ltb; int rc, i; - rc = reset_long_term_buff(adapter, &tx_pool->long_term_buff); + ltb = &tx_pool->long_term_buff; + + rc = alloc_long_term_buff(adapter, ltb, ltb->size); if (rc) return rc; From 489de956e7a2443e8ef80fe6b4dd6c6c28574f33 Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Tue, 14 Sep 2021 20:52:58 -0700 Subject: [PATCH 71/87] ibmvnic: Reuse rx pools when possible Rather than releasing the rx pools on and reallocating them on every reset, reuse the rx pools unless the pool parameters (number of pools, size of each pool or size of each buffer in a pool) have changed. If the pool parameters changed, then release the old pools (if any) and allocate new ones. Specifically release rx pools, if: - adapter is removed, - pool parameters change during reset, - we encounter an error when opening the adapter in response to a user request (in ibmvnic_open()). and don't release them: - in __ibmvnic_close() or - on errors in __ibmvnic_open() in the hope that we can reuse them on the next reset. With these, reset_rx_pools() can be dropped because its optimzation is now included in init_rx_pools() itself. cleanup_rx_pools() releases all the skbs associated with the pool and is called from ibmvnic_cleanup(), which is called on every reset. Since we want to reuse skbs across resets, move cleanup_rx_pools() out of ibmvnic_cleanup() and call it only when user closes the adapter. Add two new adapter fields, ->prev_rx_buf_sz, ->prev_rx_pool_size to keep track of the previous values and use them to decide whether to reuse or realloc the pools. Reviewed-by: Rick Lindsley Reviewed-by: Dany Madden Signed-off-by: Sukadev Bhattiprolu Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 198 +++++++++++++++++++---------- drivers/net/ethernet/ibm/ibmvnic.h | 3 + 2 files changed, 137 insertions(+), 64 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index dafb36690fdc..05aabb0effef 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -383,20 +383,27 @@ static void replenish_rx_pool(struct ibmvnic_adapter *adapter, * be 0. */ for (i = ind_bufp->index; i < count; ++i) { - skb = netdev_alloc_skb(adapter->netdev, pool->buff_size); + index = pool->free_map[pool->next_free]; + + /* We maybe reusing the skb from earlier resets. Allocate + * only if necessary. But since the LTB may have changed + * during reset (see init_rx_pools()), update LTB below + * even if reusing skb. + */ + skb = pool->rx_buff[index].skb; if (!skb) { - dev_err(dev, "Couldn't replenish rx buff\n"); - adapter->replenish_no_mem++; - break; + skb = netdev_alloc_skb(adapter->netdev, + pool->buff_size); + if (!skb) { + dev_err(dev, "Couldn't replenish rx buff\n"); + adapter->replenish_no_mem++; + break; + } } - index = pool->free_map[pool->next_free]; pool->free_map[pool->next_free] = IBMVNIC_INVALID_MAP; pool->next_free = (pool->next_free + 1) % pool->size; - if (pool->rx_buff[index].skb) - dev_err(dev, "Inconsistent free_map!\n"); - /* Copy the skb to the long term mapped DMA buffer */ offset = index * pool->buff_size; dst = pool->long_term_buff.buff + offset; @@ -547,45 +554,6 @@ static int init_stats_token(struct ibmvnic_adapter *adapter) return 0; } -static int reset_rx_pools(struct ibmvnic_adapter *adapter) -{ - struct ibmvnic_rx_pool *rx_pool; - u64 buff_size; - int rx_scrqs; - int i, j, rc; - - if (!adapter->rx_pool) - return -1; - - buff_size = adapter->cur_rx_buf_sz; - rx_scrqs = adapter->num_active_rx_pools; - for (i = 0; i < rx_scrqs; i++) { - rx_pool = &adapter->rx_pool[i]; - - netdev_dbg(adapter->netdev, "Re-setting rx_pool[%d]\n", i); - - rx_pool->buff_size = ALIGN(buff_size, L1_CACHE_BYTES); - rc = alloc_long_term_buff(adapter, - &rx_pool->long_term_buff, - rx_pool->size * rx_pool->buff_size); - if (rc) - return rc; - - for (j = 0; j < rx_pool->size; j++) - rx_pool->free_map[j] = j; - - memset(rx_pool->rx_buff, 0, - rx_pool->size * sizeof(struct ibmvnic_rx_buff)); - - atomic_set(&rx_pool->available, 0); - rx_pool->next_alloc = 0; - rx_pool->next_free = 0; - rx_pool->active = 1; - } - - return 0; -} - /** * release_rx_pools() - Release any rx pools attached to @adapter. * @adapter: ibmvnic adapter @@ -606,6 +574,7 @@ static void release_rx_pools(struct ibmvnic_adapter *adapter) netdev_dbg(adapter->netdev, "Releasing rx_pool[%d]\n", i); kfree(rx_pool->free_map); + free_long_term_buff(adapter, &rx_pool->long_term_buff); if (!rx_pool->rx_buff) @@ -624,8 +593,68 @@ static void release_rx_pools(struct ibmvnic_adapter *adapter) kfree(adapter->rx_pool); adapter->rx_pool = NULL; adapter->num_active_rx_pools = 0; + adapter->prev_rx_pool_size = 0; } +/** + * reuse_rx_pools() - Check if the existing rx pools can be reused. + * @adapter: ibmvnic adapter + * + * Check if the existing rx pools in the adapter can be reused. The + * pools can be reused if the pool parameters (number of pools, + * number of buffers in the pool and size of each buffer) have not + * changed. + * + * NOTE: This assumes that all pools have the same number of buffers + * which is the case currently. If that changes, we must fix this. + * + * Return: true if the rx pools can be reused, false otherwise. + */ +static bool reuse_rx_pools(struct ibmvnic_adapter *adapter) +{ + u64 old_num_pools, new_num_pools; + u64 old_pool_size, new_pool_size; + u64 old_buff_size, new_buff_size; + + if (!adapter->rx_pool) + return false; + + old_num_pools = adapter->num_active_rx_pools; + new_num_pools = adapter->req_rx_queues; + + old_pool_size = adapter->prev_rx_pool_size; + new_pool_size = adapter->req_rx_add_entries_per_subcrq; + + old_buff_size = adapter->prev_rx_buf_sz; + new_buff_size = adapter->cur_rx_buf_sz; + + /* Require buff size to be exactly same for now */ + if (old_buff_size != new_buff_size) + return false; + + if (old_num_pools == new_num_pools && old_pool_size == new_pool_size) + return true; + + if (old_num_pools < adapter->min_rx_queues || + old_num_pools > adapter->max_rx_queues || + old_pool_size < adapter->min_rx_add_entries_per_subcrq || + old_pool_size > adapter->max_rx_add_entries_per_subcrq) + return false; + + return true; +} + +/** + * init_rx_pools(): Initialize the set of receiver pools in the adapter. + * @netdev: net device associated with the vnic interface + * + * Initialize the set of receiver pools in the ibmvnic adapter associated + * with the net_device @netdev. If possible, reuse the existing rx pools. + * Otherwise free any existing pools and allocate a new set of pools + * before initializing them. + * + * Return: 0 on success and negative value on error. + */ static int init_rx_pools(struct net_device *netdev) { struct ibmvnic_adapter *adapter = netdev_priv(netdev); @@ -636,10 +665,18 @@ static int init_rx_pools(struct net_device *netdev) u64 buff_size; int i, j; - num_pools = adapter->num_active_rx_scrqs; pool_size = adapter->req_rx_add_entries_per_subcrq; + num_pools = adapter->req_rx_queues; buff_size = adapter->cur_rx_buf_sz; + if (reuse_rx_pools(adapter)) { + dev_dbg(dev, "Reusing rx pools\n"); + goto update_ltb; + } + + /* Allocate/populate the pools. */ + release_rx_pools(adapter); + adapter->rx_pool = kcalloc(num_pools, sizeof(struct ibmvnic_rx_pool), GFP_KERNEL); @@ -663,14 +700,12 @@ static int init_rx_pools(struct net_device *netdev) rx_pool->size = pool_size; rx_pool->index = i; rx_pool->buff_size = ALIGN(buff_size, L1_CACHE_BYTES); - rx_pool->active = 1; rx_pool->free_map = kcalloc(rx_pool->size, sizeof(int), GFP_KERNEL); if (!rx_pool->free_map) { dev_err(dev, "Couldn't alloc free_map %d\n", i); - release_rx_pools(adapter); - return -1; + goto out_release; } rx_pool->rx_buff = kcalloc(rx_pool->size, @@ -678,25 +713,58 @@ static int init_rx_pools(struct net_device *netdev) GFP_KERNEL); if (!rx_pool->rx_buff) { dev_err(dev, "Couldn't alloc rx buffers\n"); - release_rx_pools(adapter); - return -1; + goto out_release; } + } + + adapter->prev_rx_pool_size = pool_size; + adapter->prev_rx_buf_sz = adapter->cur_rx_buf_sz; + +update_ltb: + for (i = 0; i < num_pools; i++) { + rx_pool = &adapter->rx_pool[i]; + dev_dbg(dev, "Updating LTB for rx pool %d [%d, %d]\n", + i, rx_pool->size, rx_pool->buff_size); if (alloc_long_term_buff(adapter, &rx_pool->long_term_buff, - rx_pool->size * rx_pool->buff_size)) { - release_rx_pools(adapter); - return -1; - } + rx_pool->size * rx_pool->buff_size)) + goto out; + + for (j = 0; j < rx_pool->size; ++j) { + struct ibmvnic_rx_buff *rx_buff; - for (j = 0; j < rx_pool->size; ++j) rx_pool->free_map[j] = j; + /* NOTE: Don't clear rx_buff->skb here - will leak + * memory! replenish_rx_pool() will reuse skbs or + * allocate as necessary. + */ + rx_buff = &rx_pool->rx_buff[j]; + rx_buff->dma = 0; + rx_buff->data = 0; + rx_buff->size = 0; + rx_buff->pool_index = 0; + } + + /* Mark pool "empty" so replenish_rx_pools() will + * update the LTB info for each buffer + */ atomic_set(&rx_pool->available, 0); rx_pool->next_alloc = 0; rx_pool->next_free = 0; + /* replenish_rx_pool() may have called deactivate_rx_pools() + * on failover. Ensure pool is active now. + */ + rx_pool->active = 1; } - return 0; +out_release: + release_rx_pools(adapter); +out: + /* We failed to allocate one or more LTBs or map them on the VIOS. + * Hold onto the pools and any LTBs that we did allocate/map. + */ + return -1; } static int reset_one_tx_pool(struct ibmvnic_adapter *adapter, @@ -1072,7 +1140,6 @@ static void release_resources(struct ibmvnic_adapter *adapter) release_vpd_data(adapter); release_tx_pools(adapter); - release_rx_pools(adapter); release_napi(adapter); release_login_buffer(adapter); @@ -1345,6 +1412,7 @@ static int ibmvnic_open(struct net_device *netdev) if (rc) { netdev_err(netdev, "failed to initialize resources\n"); release_resources(adapter); + release_rx_pools(adapter); goto out; } } @@ -1474,7 +1542,6 @@ static void ibmvnic_cleanup(struct net_device *netdev) ibmvnic_napi_disable(adapter); ibmvnic_disable_irqs(adapter); - clean_rx_pools(adapter); clean_tx_pools(adapter); } @@ -1509,6 +1576,7 @@ static int ibmvnic_close(struct net_device *netdev) rc = __ibmvnic_close(netdev); ibmvnic_cleanup(netdev); + clean_rx_pools(adapter); return rc; } @@ -2237,7 +2305,6 @@ static int do_reset(struct ibmvnic_adapter *adapter, !adapter->rx_pool || !adapter->tso_pool || !adapter->tx_pool) { - release_rx_pools(adapter); release_tx_pools(adapter); release_napi(adapter); release_vpd_data(adapter); @@ -2254,9 +2321,10 @@ static int do_reset(struct ibmvnic_adapter *adapter, goto out; } - rc = reset_rx_pools(adapter); + rc = init_rx_pools(netdev); if (rc) { - netdev_dbg(adapter->netdev, "reset rx pools failed (%d)\n", + netdev_dbg(netdev, + "init rx pools failed (%d)\n", rc); goto out; } @@ -5592,6 +5660,7 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) init_completion(&adapter->reset_done); init_completion(&adapter->stats_done); clear_bit(0, &adapter->resetting); + adapter->prev_rx_buf_sz = 0; init_success = false; do { @@ -5692,6 +5761,7 @@ static void ibmvnic_remove(struct vio_dev *dev) unregister_netdevice(netdev); release_resources(adapter); + release_rx_pools(adapter); release_sub_crqs(adapter, 1); release_crq_queue(adapter); diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index e97f1aa98c05..b73a1b812368 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -986,7 +986,10 @@ struct ibmvnic_adapter { u32 num_active_rx_napi; u32 num_active_tx_scrqs; u32 num_active_tx_pools; + + u32 prev_rx_pool_size; u32 cur_rx_buf_sz; + u32 prev_rx_buf_sz; struct tasklet_struct tasklet; enum vnic_state state; From bbd809305bc72da8ac02841d3db5437bfe556233 Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Tue, 14 Sep 2021 20:52:59 -0700 Subject: [PATCH 72/87] ibmvnic: Reuse tx pools when possible Rather than releasing the tx pools on every close and reallocating them on open, reuse the tx pools unless the pool parameters (number of pools, size of each pool or size of each buffer in a pool) have changed. If the pool parameters changed, then release the old pools (if any) and allocate new ones. Specifically release tx pools, if: - adapter is removed, - pool parameters change during reset, - we encounter an error when opening the adapter in response to a user request (in ibmvnic_open()). and don't release them: - in __ibmvnic_close() or - on errors in __ibmvnic_open() in the hope that we can reuse them during this or next reset. With these changes reset_tx_pools() can be dropped because its optimization is now included in init_tx_pools() itself. cleanup_tx_pools() releases all the skbs associated with the pool and is called from ibmvnic_cleanup(), which is called on every reset. Since we want to reuse skbs across resets, move cleanup_tx_pools() out of ibmvnic_cleanup() and call it only when user closes the adapter. Add two new adapter fields, ->prev_mtu, ->prev_tx_pool_size to track the previous values and use them to decide whether to reuse or realloc the pools. Reviewed-by: Rick Lindsley Reviewed-by: Dany Madden Signed-off-by: Sukadev Bhattiprolu Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 215 +++++++++++++++++++---------- drivers/net/ethernet/ibm/ibmvnic.h | 2 + 2 files changed, 147 insertions(+), 70 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 05aabb0effef..d1be883f933a 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -767,53 +767,6 @@ out: return -1; } -static int reset_one_tx_pool(struct ibmvnic_adapter *adapter, - struct ibmvnic_tx_pool *tx_pool) -{ - struct ibmvnic_long_term_buff *ltb; - int rc, i; - - ltb = &tx_pool->long_term_buff; - - rc = alloc_long_term_buff(adapter, ltb, ltb->size); - if (rc) - return rc; - - memset(tx_pool->tx_buff, 0, - tx_pool->num_buffers * - sizeof(struct ibmvnic_tx_buff)); - - for (i = 0; i < tx_pool->num_buffers; i++) - tx_pool->free_map[i] = i; - - tx_pool->consumer_index = 0; - tx_pool->producer_index = 0; - - return 0; -} - -static int reset_tx_pools(struct ibmvnic_adapter *adapter) -{ - int tx_scrqs; - int i, rc; - - if (!adapter->tx_pool) - return -1; - - tx_scrqs = adapter->num_active_tx_pools; - for (i = 0; i < tx_scrqs; i++) { - ibmvnic_tx_scrq_clean_buffer(adapter, adapter->tx_scrq[i]); - rc = reset_one_tx_pool(adapter, &adapter->tso_pool[i]); - if (rc) - return rc; - rc = reset_one_tx_pool(adapter, &adapter->tx_pool[i]); - if (rc) - return rc; - } - - return 0; -} - static void release_vpd_data(struct ibmvnic_adapter *adapter) { if (!adapter->vpd) @@ -859,13 +812,13 @@ static void release_tx_pools(struct ibmvnic_adapter *adapter) kfree(adapter->tso_pool); adapter->tso_pool = NULL; adapter->num_active_tx_pools = 0; + adapter->prev_tx_pool_size = 0; } static int init_one_tx_pool(struct net_device *netdev, struct ibmvnic_tx_pool *tx_pool, int pool_size, int buf_size) { - struct ibmvnic_adapter *adapter = netdev_priv(netdev); int i; tx_pool->tx_buff = kcalloc(pool_size, @@ -874,13 +827,12 @@ static int init_one_tx_pool(struct net_device *netdev, if (!tx_pool->tx_buff) return -1; - if (alloc_long_term_buff(adapter, &tx_pool->long_term_buff, - pool_size * buf_size)) - return -1; - tx_pool->free_map = kcalloc(pool_size, sizeof(int), GFP_KERNEL); - if (!tx_pool->free_map) + if (!tx_pool->free_map) { + kfree(tx_pool->tx_buff); + tx_pool->tx_buff = NULL; return -1; + } for (i = 0; i < pool_size; i++) tx_pool->free_map[i] = i; @@ -893,6 +845,62 @@ static int init_one_tx_pool(struct net_device *netdev, return 0; } +/** + * reuse_tx_pools() - Check if the existing tx pools can be reused. + * @adapter: ibmvnic adapter + * + * Check if the existing tx pools in the adapter can be reused. The + * pools can be reused if the pool parameters (number of pools, + * number of buffers in the pool and mtu) have not changed. + * + * NOTE: This assumes that all pools have the same number of buffers + * which is the case currently. If that changes, we must fix this. + * + * Return: true if the tx pools can be reused, false otherwise. + */ +static bool reuse_tx_pools(struct ibmvnic_adapter *adapter) +{ + u64 old_num_pools, new_num_pools; + u64 old_pool_size, new_pool_size; + u64 old_mtu, new_mtu; + + if (!adapter->tx_pool) + return false; + + old_num_pools = adapter->num_active_tx_pools; + new_num_pools = adapter->num_active_tx_scrqs; + old_pool_size = adapter->prev_tx_pool_size; + new_pool_size = adapter->req_tx_entries_per_subcrq; + old_mtu = adapter->prev_mtu; + new_mtu = adapter->req_mtu; + + /* Require MTU to be exactly same to reuse pools for now */ + if (old_mtu != new_mtu) + return false; + + if (old_num_pools == new_num_pools && old_pool_size == new_pool_size) + return true; + + if (old_num_pools < adapter->min_tx_queues || + old_num_pools > adapter->max_tx_queues || + old_pool_size < adapter->min_tx_entries_per_subcrq || + old_pool_size > adapter->max_tx_entries_per_subcrq) + return false; + + return true; +} + +/** + * init_tx_pools(): Initialize the set of transmit pools in the adapter. + * @netdev: net device associated with the vnic interface + * + * Initialize the set of transmit pools in the ibmvnic adapter associated + * with the net_device @netdev. If possible, reuse the existing tx pools. + * Otherwise free any existing pools and allocate a new set of pools + * before initializing them. + * + * Return: 0 on success and negative value on error. + */ static int init_tx_pools(struct net_device *netdev) { struct ibmvnic_adapter *adapter = netdev_priv(netdev); @@ -900,7 +908,21 @@ static int init_tx_pools(struct net_device *netdev) int num_pools; u64 pool_size; /* # of buffers in pool */ u64 buff_size; - int i, rc; + int i, j, rc; + + num_pools = adapter->req_tx_queues; + + /* We must notify the VIOS about the LTB on all resets - but we only + * need to alloc/populate pools if either the number of buffers or + * size of each buffer in the pool has changed. + */ + if (reuse_tx_pools(adapter)) { + netdev_dbg(netdev, "Reusing tx pools\n"); + goto update_ltb; + } + + /* Allocate/populate the pools. */ + release_tx_pools(adapter); pool_size = adapter->req_tx_entries_per_subcrq; num_pools = adapter->num_active_tx_scrqs; @@ -925,6 +947,7 @@ static int init_tx_pools(struct net_device *netdev) * allocation, release_tx_pools() will know how many to look for. */ adapter->num_active_tx_pools = num_pools; + buff_size = adapter->req_mtu + VLAN_HLEN; buff_size = ALIGN(buff_size, L1_CACHE_BYTES); @@ -934,21 +957,73 @@ static int init_tx_pools(struct net_device *netdev) rc = init_one_tx_pool(netdev, &adapter->tx_pool[i], pool_size, buff_size); - if (rc) { - release_tx_pools(adapter); - return rc; - } + if (rc) + goto out_release; rc = init_one_tx_pool(netdev, &adapter->tso_pool[i], IBMVNIC_TSO_BUFS, IBMVNIC_TSO_BUF_SZ); - if (rc) { - release_tx_pools(adapter); - return rc; - } + if (rc) + goto out_release; + } + + adapter->prev_tx_pool_size = pool_size; + adapter->prev_mtu = adapter->req_mtu; + +update_ltb: + /* NOTE: All tx_pools have the same number of buffers (which is + * same as pool_size). All tso_pools have IBMVNIC_TSO_BUFS + * buffers (see calls init_one_tx_pool() for these). + * For consistency, we use tx_pool->num_buffers and + * tso_pool->num_buffers below. + */ + rc = -1; + for (i = 0; i < num_pools; i++) { + struct ibmvnic_tx_pool *tso_pool; + struct ibmvnic_tx_pool *tx_pool; + u32 ltb_size; + + tx_pool = &adapter->tx_pool[i]; + ltb_size = tx_pool->num_buffers * tx_pool->buf_size; + if (alloc_long_term_buff(adapter, &tx_pool->long_term_buff, + ltb_size)) + goto out; + + dev_dbg(dev, "Updated LTB for tx pool %d [%p, %d, %d]\n", + i, tx_pool->long_term_buff.buff, + tx_pool->num_buffers, tx_pool->buf_size); + + tx_pool->consumer_index = 0; + tx_pool->producer_index = 0; + + for (j = 0; j < tx_pool->num_buffers; j++) + tx_pool->free_map[j] = j; + + tso_pool = &adapter->tso_pool[i]; + ltb_size = tso_pool->num_buffers * tso_pool->buf_size; + if (alloc_long_term_buff(adapter, &tso_pool->long_term_buff, + ltb_size)) + goto out; + + dev_dbg(dev, "Updated LTB for tso pool %d [%p, %d, %d]\n", + i, tso_pool->long_term_buff.buff, + tso_pool->num_buffers, tso_pool->buf_size); + + tso_pool->consumer_index = 0; + tso_pool->producer_index = 0; + + for (j = 0; j < tso_pool->num_buffers; j++) + tso_pool->free_map[j] = j; } return 0; +out_release: + release_tx_pools(adapter); +out: + /* We failed to allocate one or more LTBs or map them on the VIOS. + * Hold onto the pools and any LTBs that we did allocate/map. + */ + return rc; } static void ibmvnic_napi_enable(struct ibmvnic_adapter *adapter) @@ -1139,8 +1214,6 @@ static void release_resources(struct ibmvnic_adapter *adapter) { release_vpd_data(adapter); - release_tx_pools(adapter); - release_napi(adapter); release_login_buffer(adapter); release_login_rsp_buffer(adapter); @@ -1413,6 +1486,7 @@ static int ibmvnic_open(struct net_device *netdev) netdev_err(netdev, "failed to initialize resources\n"); release_resources(adapter); release_rx_pools(adapter); + release_tx_pools(adapter); goto out; } } @@ -1541,8 +1615,6 @@ static void ibmvnic_cleanup(struct net_device *netdev) ibmvnic_napi_disable(adapter); ibmvnic_disable_irqs(adapter); - - clean_tx_pools(adapter); } static int __ibmvnic_close(struct net_device *netdev) @@ -1577,6 +1649,7 @@ static int ibmvnic_close(struct net_device *netdev) rc = __ibmvnic_close(netdev); ibmvnic_cleanup(netdev); clean_rx_pools(adapter); + clean_tx_pools(adapter); return rc; } @@ -2153,9 +2226,9 @@ static const char *reset_reason_to_string(enum ibmvnic_reset_reason reason) static int do_reset(struct ibmvnic_adapter *adapter, struct ibmvnic_rwi *rwi, u32 reset_state) { + struct net_device *netdev = adapter->netdev; u64 old_num_rx_queues, old_num_tx_queues; u64 old_num_rx_slots, old_num_tx_slots; - struct net_device *netdev = adapter->netdev; int rc; netdev_dbg(adapter->netdev, @@ -2305,7 +2378,6 @@ static int do_reset(struct ibmvnic_adapter *adapter, !adapter->rx_pool || !adapter->tso_pool || !adapter->tx_pool) { - release_tx_pools(adapter); release_napi(adapter); release_vpd_data(adapter); @@ -2314,9 +2386,10 @@ static int do_reset(struct ibmvnic_adapter *adapter, goto out; } else { - rc = reset_tx_pools(adapter); + rc = init_tx_pools(netdev); if (rc) { - netdev_dbg(adapter->netdev, "reset tx pools failed (%d)\n", + netdev_dbg(netdev, + "init tx pools failed (%d)\n", rc); goto out; } @@ -5661,6 +5734,7 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) init_completion(&adapter->stats_done); clear_bit(0, &adapter->resetting); adapter->prev_rx_buf_sz = 0; + adapter->prev_mtu = 0; init_success = false; do { @@ -5762,6 +5836,7 @@ static void ibmvnic_remove(struct vio_dev *dev) release_resources(adapter); release_rx_pools(adapter); + release_tx_pools(adapter); release_sub_crqs(adapter, 1); release_crq_queue(adapter); diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index b73a1b812368..b8e42f67d897 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -967,6 +967,7 @@ struct ibmvnic_adapter { u64 min_mtu; u64 max_mtu; u64 req_mtu; + u64 prev_mtu; u64 max_multicast_filters; u64 vlan_header_insertion; u64 rx_vlan_header_insertion; @@ -988,6 +989,7 @@ struct ibmvnic_adapter { u32 num_active_tx_pools; u32 prev_rx_pool_size; + u32 prev_tx_pool_size; u32 cur_rx_buf_sz; u32 prev_rx_buf_sz; From 1e080f17750d1083e8a32f7b350584ae1cd7ff20 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 13 Sep 2021 15:53:30 -0700 Subject: [PATCH 73/87] net: sched: update default qdisc visibility after Tx queue cnt changes mq / mqprio make the default child qdiscs visible. They only do so for the qdiscs which are within real_num_tx_queues when the device is registered. Depending on order of calls in the driver, or if user space changes config via ethtool -L the number of qdiscs visible under tc qdisc show will differ from the number of queues. This is confusing to users and potentially to system configuration scripts which try to make sure qdiscs have the right parameters. Add a new Qdisc_ops callback and make relevant qdiscs TTRT. Note that this uncovers the "shortcut" created by commit 1f27cde313d7 ("net: sched: use pfifo_fast for non real queues") The default child qdiscs beyond initial real_num_tx are always pfifo_fast, no matter what the sysfs setting is. Fixing this gets a little tricky because we'd need to keep a reference on whatever the default qdisc was at the time of creation. In practice this is likely an non-issue the qdiscs likely have to be configured to non-default settings, so whatever user space is doing such configuration can replace the pfifos... now that it will see them. Reported-by: Matthew Massey Reviewed-by: Dave Taht Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- include/net/sch_generic.h | 4 ++++ net/core/dev.c | 2 ++ net/sched/sch_generic.c | 9 +++++++++ net/sched/sch_mq.c | 24 ++++++++++++++++++++++++ net/sched/sch_mqprio.c | 23 +++++++++++++++++++++++ 5 files changed, 62 insertions(+) diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index c0069ac00e62..8c2d611639fc 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -308,6 +308,8 @@ struct Qdisc_ops { struct netlink_ext_ack *extack); void (*attach)(struct Qdisc *sch); int (*change_tx_queue_len)(struct Qdisc *, unsigned int); + void (*change_real_num_tx)(struct Qdisc *sch, + unsigned int new_real_tx); int (*dump)(struct Qdisc *, struct sk_buff *); int (*dump_stats)(struct Qdisc *, struct gnet_dump *); @@ -684,6 +686,8 @@ void qdisc_class_hash_grow(struct Qdisc *, struct Qdisc_class_hash *); void qdisc_class_hash_destroy(struct Qdisc_class_hash *); int dev_qdisc_change_tx_queue_len(struct net_device *dev); +void dev_qdisc_change_real_num_tx(struct net_device *dev, + unsigned int new_real_tx); void dev_init_scheduler(struct net_device *dev); void dev_shutdown(struct net_device *dev); void dev_activate(struct net_device *dev); diff --git a/net/core/dev.c b/net/core/dev.c index 74fd402d26dd..f930329f0dc2 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2921,6 +2921,8 @@ int netif_set_real_num_tx_queues(struct net_device *dev, unsigned int txq) if (dev->num_tc) netif_setup_tc(dev, txq); + dev_qdisc_change_real_num_tx(dev, txq); + dev->real_num_tx_queues = txq; if (disabling) { diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index a8dd06c74e31..66d2fbe9ef50 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -1330,6 +1330,15 @@ static int qdisc_change_tx_queue_len(struct net_device *dev, return 0; } +void dev_qdisc_change_real_num_tx(struct net_device *dev, + unsigned int new_real_tx) +{ + struct Qdisc *qdisc = dev->qdisc; + + if (qdisc->ops->change_real_num_tx) + qdisc->ops->change_real_num_tx(qdisc, new_real_tx); +} + int dev_qdisc_change_tx_queue_len(struct net_device *dev) { bool up = dev->flags & IFF_UP; diff --git a/net/sched/sch_mq.c b/net/sched/sch_mq.c index e79f1afe0cfd..db18d8a860f9 100644 --- a/net/sched/sch_mq.c +++ b/net/sched/sch_mq.c @@ -125,6 +125,29 @@ static void mq_attach(struct Qdisc *sch) priv->qdiscs = NULL; } +static void mq_change_real_num_tx(struct Qdisc *sch, unsigned int new_real_tx) +{ +#ifdef CONFIG_NET_SCHED + struct net_device *dev = qdisc_dev(sch); + struct Qdisc *qdisc; + unsigned int i; + + for (i = new_real_tx; i < dev->real_num_tx_queues; i++) { + qdisc = netdev_get_tx_queue(dev, i)->qdisc_sleeping; + /* Only update the default qdiscs we created, + * qdiscs with handles are always hashed. + */ + if (qdisc != &noop_qdisc && !qdisc->handle) + qdisc_hash_del(qdisc); + } + for (i = dev->real_num_tx_queues; i < new_real_tx; i++) { + qdisc = netdev_get_tx_queue(dev, i)->qdisc_sleeping; + if (qdisc != &noop_qdisc && !qdisc->handle) + qdisc_hash_add(qdisc, false); + } +#endif +} + static int mq_dump(struct Qdisc *sch, struct sk_buff *skb) { struct net_device *dev = qdisc_dev(sch); @@ -288,6 +311,7 @@ struct Qdisc_ops mq_qdisc_ops __read_mostly = { .init = mq_init, .destroy = mq_destroy, .attach = mq_attach, + .change_real_num_tx = mq_change_real_num_tx, .dump = mq_dump, .owner = THIS_MODULE, }; diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index 8766ab5b8788..7f23a92849d5 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -306,6 +306,28 @@ static void mqprio_attach(struct Qdisc *sch) priv->qdiscs = NULL; } +static void mqprio_change_real_num_tx(struct Qdisc *sch, + unsigned int new_real_tx) +{ + struct net_device *dev = qdisc_dev(sch); + struct Qdisc *qdisc; + unsigned int i; + + for (i = new_real_tx; i < dev->real_num_tx_queues; i++) { + qdisc = netdev_get_tx_queue(dev, i)->qdisc_sleeping; + /* Only update the default qdiscs we created, + * qdiscs with handles are always hashed. + */ + if (qdisc != &noop_qdisc && !qdisc->handle) + qdisc_hash_del(qdisc); + } + for (i = dev->real_num_tx_queues; i < new_real_tx; i++) { + qdisc = netdev_get_tx_queue(dev, i)->qdisc_sleeping; + if (qdisc != &noop_qdisc && !qdisc->handle) + qdisc_hash_add(qdisc, false); + } +} + static struct netdev_queue *mqprio_queue_get(struct Qdisc *sch, unsigned long cl) { @@ -623,6 +645,7 @@ static struct Qdisc_ops mqprio_qdisc_ops __read_mostly = { .init = mqprio_init, .destroy = mqprio_destroy, .attach = mqprio_attach, + .change_real_num_tx = mqprio_change_real_num_tx, .dump = mqprio_dump, .owner = THIS_MODULE, }; From 2e367522ce6bc302f2613a0334c9930dafccf2e7 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 13 Sep 2021 15:53:31 -0700 Subject: [PATCH 74/87] netdevsim: add ability to change channel count For testing visibility of mq/mqprio default children. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/netdevsim/ethtool.c | 28 ++++++++++++++++++++++++++++ drivers/net/netdevsim/netdevsim.h | 1 + 2 files changed, 29 insertions(+) diff --git a/drivers/net/netdevsim/ethtool.c b/drivers/net/netdevsim/ethtool.c index b03a0513eb7e..0ab6a40be611 100644 --- a/drivers/net/netdevsim/ethtool.c +++ b/drivers/net/netdevsim/ethtool.c @@ -81,6 +81,30 @@ static int nsim_set_ringparam(struct net_device *dev, return 0; } +static void +nsim_get_channels(struct net_device *dev, struct ethtool_channels *ch) +{ + struct netdevsim *ns = netdev_priv(dev); + + ch->max_combined = ns->nsim_bus_dev->num_queues; + ch->combined_count = ns->ethtool.channels; +} + +static int +nsim_set_channels(struct net_device *dev, struct ethtool_channels *ch) +{ + struct netdevsim *ns = netdev_priv(dev); + int err; + + err = netif_set_real_num_queues(dev, ch->combined_count, + ch->combined_count); + if (err) + return err; + + ns->ethtool.channels = ch->combined_count; + return 0; +} + static int nsim_get_fecparam(struct net_device *dev, struct ethtool_fecparam *fecparam) { @@ -118,6 +142,8 @@ static const struct ethtool_ops nsim_ethtool_ops = { .get_coalesce = nsim_get_coalesce, .get_ringparam = nsim_get_ringparam, .set_ringparam = nsim_set_ringparam, + .get_channels = nsim_get_channels, + .set_channels = nsim_set_channels, .get_fecparam = nsim_get_fecparam, .set_fecparam = nsim_set_fecparam, }; @@ -141,6 +167,8 @@ void nsim_ethtool_init(struct netdevsim *ns) ns->ethtool.fec.fec = ETHTOOL_FEC_NONE; ns->ethtool.fec.active_fec = ETHTOOL_FEC_NONE; + ns->ethtool.channels = ns->nsim_bus_dev->num_queues; + ethtool = debugfs_create_dir("ethtool", ns->nsim_dev_port->ddir); debugfs_create_u32("get_err", 0600, ethtool, &ns->ethtool.get_err); diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h index 793c86dc5a9c..d42eec05490f 100644 --- a/drivers/net/netdevsim/netdevsim.h +++ b/drivers/net/netdevsim/netdevsim.h @@ -62,6 +62,7 @@ struct nsim_ethtool_pauseparam { struct nsim_ethtool { u32 get_err; u32 set_err; + u32 channels; struct nsim_ethtool_pauseparam pauseparam; struct ethtool_coalesce coalesce; struct ethtool_ringparam ring; From 2d6a58996ee23f85745a8e42edaad7a2b86d1a83 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 13 Sep 2021 15:53:32 -0700 Subject: [PATCH 75/87] selftests: net: test ethtool -L vs mq Add a selftest for checking mq children are visible after ethtool -L. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../drivers/net/netdevsim/ethtool-common.sh | 2 +- .../drivers/net/netdevsim/tc-mq-visibility.sh | 77 +++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100755 tools/testing/selftests/drivers/net/netdevsim/tc-mq-visibility.sh diff --git a/tools/testing/selftests/drivers/net/netdevsim/ethtool-common.sh b/tools/testing/selftests/drivers/net/netdevsim/ethtool-common.sh index 7ca1f030d209..922744059aaa 100644 --- a/tools/testing/selftests/drivers/net/netdevsim/ethtool-common.sh +++ b/tools/testing/selftests/drivers/net/netdevsim/ethtool-common.sh @@ -50,7 +50,7 @@ function make_netdev { modprobe netdevsim fi - echo $NSIM_ID > /sys/bus/netdevsim/new_device + echo $NSIM_ID $@ > /sys/bus/netdevsim/new_device # get new device name ls /sys/bus/netdevsim/devices/netdevsim${NSIM_ID}/net/ } diff --git a/tools/testing/selftests/drivers/net/netdevsim/tc-mq-visibility.sh b/tools/testing/selftests/drivers/net/netdevsim/tc-mq-visibility.sh new file mode 100755 index 000000000000..fd13c8cfb7a8 --- /dev/null +++ b/tools/testing/selftests/drivers/net/netdevsim/tc-mq-visibility.sh @@ -0,0 +1,77 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-only + +source ethtool-common.sh + +set -o pipefail + +n_children() { + n=$(tc qdisc show dev $NDEV | grep '^qdisc' | wc -l) + echo $((n - 1)) +} + +tcq() { + tc qdisc $1 dev $NDEV ${@:2} +} + +n_child_assert() { + n=$(n_children) + if [ $n -ne $1 ]; then + echo "ERROR ($root): ${@:2}, expected $1 have $n" + ((num_errors++)) + else + ((num_passes++)) + fi +} + + +for root in mq mqprio; do + NDEV=$(make_netdev 1 4) + + opts= + [ $root == "mqprio" ] && opts='hw 0 num_tc 1 map 0 0 0 0 queues 1@0' + + tcq add root handle 100: $root $opts + n_child_assert 4 'Init' + + # All defaults + + for n in 3 2 1 2 3 4 1 4; do + ethtool -L $NDEV combined $n + n_child_assert $n "Change queues to $n while down" + done + + ip link set dev $NDEV up + + for n in 3 2 1 2 3 4 1 4; do + ethtool -L $NDEV combined $n + n_child_assert $n "Change queues to $n while up" + done + + # One real one + tcq replace parent 100:4 handle 204: pfifo_fast + n_child_assert 4 "One real queue" + + ethtool -L $NDEV combined 1 + n_child_assert 2 "One real queue, one default" + + ethtool -L $NDEV combined 4 + n_child_assert 4 "One real queue, rest default" + + # Graft some + tcq replace parent 100:1 handle 204: + n_child_assert 3 "Grafted" + + ethtool -L $NDEV combined 1 + n_child_assert 1 "Grafted, one" + + cleanup_nsim +done + +if [ $num_errors -eq 0 ]; then + echo "PASSED all $((num_passes)) checks" + exit 0 +else + echo "FAILED $num_errors/$((num_errors+num_passes)) checks" + exit 1 +fi From e9310aed8e6a5003abb2aa6b9229d2fb9ceb9e85 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Tue, 14 Sep 2021 15:58:28 +0300 Subject: [PATCH 76/87] net/mlx5: Publish and unpublish all devlink parameters at once The devlink parameters were published in two steps despite being static and known in advance. First step was to use devlink_params_publish() which iterated over all known up to that point parameters and sent notification messages. In second step, the call was devlink_param_publish() that looped over same parameters list and sent notification for new parameters. In order to simplify the API, move devlink_params_publish() to be called when all parameters were already added and save the need to iterate over parameters list again. As a side effect, this change fixes the error unwind flow in which parameters were not marked as unpublished. Fixes: 82e6c96f04e1 ("net/mlx5: Register to devlink ingress VLAN filter trap") Signed-off-by: Leon Romanovsky Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/devlink.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c index e84287ffc7ce..dde8c03c5cfe 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c @@ -625,7 +625,6 @@ static int mlx5_devlink_eth_param_register(struct devlink *devlink) devlink_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_ENABLE_ETH, value); - devlink_param_publish(devlink, &enable_eth_param); return 0; } @@ -636,7 +635,6 @@ static void mlx5_devlink_eth_param_unregister(struct devlink *devlink) if (!mlx5_eth_supported(dev)) return; - devlink_param_unpublish(devlink, &enable_eth_param); devlink_param_unregister(devlink, &enable_eth_param); } @@ -673,7 +671,6 @@ static int mlx5_devlink_rdma_param_register(struct devlink *devlink) devlink_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_ENABLE_RDMA, value); - devlink_param_publish(devlink, &enable_rdma_param); return 0; } @@ -684,7 +681,6 @@ static void mlx5_devlink_rdma_param_unregister(struct devlink *devlink) if (!IS_ENABLED(CONFIG_MLX5_INFINIBAND) || MLX5_ESWITCH_MANAGER(dev)) return; - devlink_param_unpublish(devlink, &enable_rdma_param); devlink_param_unregister(devlink, &enable_rdma_param); } @@ -709,7 +705,6 @@ static int mlx5_devlink_vnet_param_register(struct devlink *devlink) devlink_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_ENABLE_VNET, value); - devlink_param_publish(devlink, &enable_rdma_param); return 0; } @@ -720,7 +715,6 @@ static void mlx5_devlink_vnet_param_unregister(struct devlink *devlink) if (!mlx5_vnet_supported(dev)) return; - devlink_param_unpublish(devlink, &enable_vnet_param); devlink_param_unregister(devlink, &enable_vnet_param); } @@ -811,7 +805,6 @@ int mlx5_devlink_register(struct devlink *devlink) if (err) goto params_reg_err; mlx5_devlink_set_params_init_values(devlink); - devlink_params_publish(devlink); err = mlx5_devlink_auxdev_params_register(devlink); if (err) @@ -821,6 +814,7 @@ int mlx5_devlink_register(struct devlink *devlink) if (err) goto traps_reg_err; + devlink_params_publish(devlink); return 0; traps_reg_err: @@ -835,9 +829,9 @@ params_reg_err: void mlx5_devlink_unregister(struct devlink *devlink) { + devlink_params_unpublish(devlink); mlx5_devlink_traps_unregister(devlink); mlx5_devlink_auxdev_params_unregister(devlink); - devlink_params_unpublish(devlink); devlink_params_unregister(devlink, mlx5_devlink_params, ARRAY_SIZE(mlx5_devlink_params)); devlink_unregister(devlink); From c2d2f98850665a85bb64374bd8de0d19f9390656 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Tue, 14 Sep 2021 15:58:29 +0300 Subject: [PATCH 77/87] devlink: Delete not-used single parameter notification APIs There is no need in specific devlink_param_*publish(), because same output can be achieved by using devlink_params_*publish() in correct places. Signed-off-by: Leon Romanovsky Acked-by: Jakub Kicinski Signed-off-by: David S. Miller --- include/net/devlink.h | 4 ---- net/core/devlink.c | 48 ------------------------------------------- 2 files changed, 52 deletions(-) diff --git a/include/net/devlink.h b/include/net/devlink.h index 154cf0dbca37..cd89b2dc2354 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -1653,10 +1653,6 @@ void devlink_param_unregister(struct devlink *devlink, const struct devlink_param *param); void devlink_params_publish(struct devlink *devlink); void devlink_params_unpublish(struct devlink *devlink); -void devlink_param_publish(struct devlink *devlink, - const struct devlink_param *param); -void devlink_param_unpublish(struct devlink *devlink, - const struct devlink_param *param); int devlink_port_params_register(struct devlink_port *devlink_port, const struct devlink_param *params, size_t params_count); diff --git a/net/core/devlink.c b/net/core/devlink.c index a856ae401ea5..f30121f07467 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -10121,54 +10121,6 @@ void devlink_params_unpublish(struct devlink *devlink) } EXPORT_SYMBOL_GPL(devlink_params_unpublish); -/** - * devlink_param_publish - publish one configuration parameter - * - * @devlink: devlink - * @param: one configuration parameter - * - * Publish previously registered configuration parameter. - */ -void devlink_param_publish(struct devlink *devlink, - const struct devlink_param *param) -{ - struct devlink_param_item *param_item; - - list_for_each_entry(param_item, &devlink->param_list, list) { - if (param_item->param != param || param_item->published) - continue; - param_item->published = true; - devlink_param_notify(devlink, 0, param_item, - DEVLINK_CMD_PARAM_NEW); - break; - } -} -EXPORT_SYMBOL_GPL(devlink_param_publish); - -/** - * devlink_param_unpublish - unpublish one configuration parameter - * - * @devlink: devlink - * @param: one configuration parameter - * - * Unpublish previously registered configuration parameter. - */ -void devlink_param_unpublish(struct devlink *devlink, - const struct devlink_param *param) -{ - struct devlink_param_item *param_item; - - list_for_each_entry(param_item, &devlink->param_list, list) { - if (param_item->param != param || !param_item->published) - continue; - param_item->published = false; - devlink_param_notify(devlink, 0, param_item, - DEVLINK_CMD_PARAM_DEL); - break; - } -} -EXPORT_SYMBOL_GPL(devlink_param_unpublish); - /** * devlink_port_params_register - register port configuration parameters * From 3d7a6f677905ea74c060709a0c5fe7b3df1eaae3 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 15 Sep 2021 13:13:05 +0300 Subject: [PATCH 78/87] mlxsw: core: Initialize switch driver last Commit 961cf99a074f ("mlxsw: core: Re-order initialization sequence") changed the initialization sequence so that the switch driver (e.g., mlxsw_spectrum) is initialized before registration with the hwmon and thermal subsystems. This was done in order to avoid situations where hwmon/thermal code uses features not supported by current firmware version, which is only validated as part of switch driver initialization. Later, commit b79cb787ac70 ("mlxsw: Move fw flashing code into core.c") moved firmware validation and flashing code from the switch driver to mlxsw_core so that it is performed before driver initialization. Therefore, change the initialization sequence back to its original form. In addition to being more straightforward, it will allow us to simplify parts of the code in subsequent patches and future patchsets. Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index 98420db90ea1..44803746ded5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -1995,12 +1995,6 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, if (err) goto err_health_init; - if (mlxsw_driver->init) { - err = mlxsw_driver->init(mlxsw_core, mlxsw_bus_info, extack); - if (err) - goto err_driver_init; - } - err = mlxsw_hwmon_init(mlxsw_core, mlxsw_bus_info, &mlxsw_core->hwmon); if (err) goto err_hwmon_init; @@ -2014,6 +2008,12 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, if (err) goto err_env_init; + if (mlxsw_driver->init) { + err = mlxsw_driver->init(mlxsw_core, mlxsw_bus_info, extack); + if (err) + goto err_driver_init; + } + mlxsw_core->is_initialized = true; devlink_params_publish(devlink); @@ -2022,14 +2022,13 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, return 0; +err_driver_init: + mlxsw_env_fini(mlxsw_core->env); err_env_init: mlxsw_thermal_fini(mlxsw_core->thermal); err_thermal_init: mlxsw_hwmon_fini(mlxsw_core->hwmon); err_hwmon_init: - if (mlxsw_core->driver->fini) - mlxsw_core->driver->fini(mlxsw_core); -err_driver_init: mlxsw_core_health_fini(mlxsw_core); err_health_init: err_fw_rev_validate: @@ -2101,11 +2100,11 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core, devlink_params_unpublish(devlink); mlxsw_core->is_initialized = false; + if (mlxsw_core->driver->fini) + mlxsw_core->driver->fini(mlxsw_core); mlxsw_env_fini(mlxsw_core->env); mlxsw_thermal_fini(mlxsw_core->thermal); mlxsw_hwmon_fini(mlxsw_core->hwmon); - if (mlxsw_core->driver->fini) - mlxsw_core->driver->fini(mlxsw_core); mlxsw_core_health_fini(mlxsw_core); if (!reload) mlxsw_core_params_unregister(mlxsw_core); From 25a91f835a7b0964455594d11253060bf5b480b4 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 15 Sep 2021 13:13:06 +0300 Subject: [PATCH 79/87] mlxsw: core: Remove mlxsw_core_is_initialized() After the previous patch, the switch driver is always initialized last, making this function redundant. Remove it. Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.c | 8 -------- drivers/net/ethernet/mellanox/mlxsw/core.h | 1 - drivers/net/ethernet/mellanox/mlxsw/core_env.c | 6 ------ 3 files changed, 15 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index 44803746ded5..8c634976ca77 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -90,7 +90,6 @@ struct mlxsw_core { struct devlink_health_reporter *fw_fatal; } health; struct mlxsw_env *env; - bool is_initialized; /* Denotes if core was already initialized. */ unsigned long driver_priv[]; /* driver_priv has to be always the last item */ }; @@ -2014,7 +2013,6 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, goto err_driver_init; } - mlxsw_core->is_initialized = true; devlink_params_publish(devlink); if (!reload) @@ -2099,7 +2097,6 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core, } devlink_params_unpublish(devlink); - mlxsw_core->is_initialized = false; if (mlxsw_core->driver->fini) mlxsw_core->driver->fini(mlxsw_core); mlxsw_env_fini(mlxsw_core->env); @@ -2938,11 +2935,6 @@ struct mlxsw_env *mlxsw_core_env(const struct mlxsw_core *mlxsw_core) return mlxsw_core->env; } -bool mlxsw_core_is_initialized(const struct mlxsw_core *mlxsw_core) -{ - return mlxsw_core->is_initialized; -} - static void mlxsw_core_buf_dump_dbg(struct mlxsw_core *mlxsw_core, const char *buf, size_t size) { diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index d21981cc04ca..12023a550007 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -249,7 +249,6 @@ mlxsw_core_port_devlink_port_get(struct mlxsw_core *mlxsw_core, u8 local_port); bool mlxsw_core_port_is_xm(const struct mlxsw_core *mlxsw_core, u8 local_port); struct mlxsw_env *mlxsw_core_env(const struct mlxsw_core *mlxsw_core); -bool mlxsw_core_is_initialized(const struct mlxsw_core *mlxsw_core); int mlxsw_core_schedule_dw(struct delayed_work *dwork, unsigned long delay); bool mlxsw_core_schedule_work(struct work_struct *work); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_env.c b/drivers/net/ethernet/mellanox/mlxsw/core_env.c index 3713c45cfa1e..27e721f96b3b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_env.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_env.c @@ -667,12 +667,6 @@ mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 module, { struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); - /* Prevent switch driver from accessing uninitialized data. */ - if (!mlxsw_core_is_initialized(mlxsw_core)) { - *p_counter = 0; - return 0; - } - if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) return -EINVAL; From 163f3d2dd01cf9b8f58b56fe94ce517e468385a9 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 15 Sep 2021 13:13:07 +0300 Subject: [PATCH 80/87] mlxsw: core_env: Defer handling of module temperature warning events Module temperature events are currently handled in softIRQ context, requiring the 'module_info_lock' to be a spin lock. In future patchsets we will need to be able to hold the lock while sleeping. Therefore, defer handling of these events using a work queue so that the next patch will be able to convert the lock to a mutex. Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/core_env.c | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_env.c b/drivers/net/ethernet/mellanox/mlxsw/core_env.c index 27e721f96b3b..27eba0a0c91c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_env.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_env.c @@ -482,20 +482,30 @@ static int mlxsw_env_module_temp_event_enable(struct mlxsw_core *mlxsw_core, return 0; } -static void mlxsw_env_mtwe_event_func(const struct mlxsw_reg_info *reg, - char *mtwe_pl, void *priv) +struct mlxsw_env_module_temp_warn_event { + struct mlxsw_env *mlxsw_env; + char mtwe_pl[MLXSW_REG_MTWE_LEN]; + struct work_struct work; +}; + +static void mlxsw_env_mtwe_event_work(struct work_struct *work) { - struct mlxsw_env *mlxsw_env = priv; + struct mlxsw_env_module_temp_warn_event *event; + struct mlxsw_env *mlxsw_env; int i, sensor_warning; bool is_overheat; + event = container_of(work, struct mlxsw_env_module_temp_warn_event, + work); + mlxsw_env = event->mlxsw_env; + for (i = 0; i < mlxsw_env->module_count; i++) { /* 64-127 of sensor_index are mapped to the port modules * sequentially (module 0 is mapped to sensor_index 64, * module 1 to sensor_index 65 and so on) */ sensor_warning = - mlxsw_reg_mtwe_sensor_warning_get(mtwe_pl, + mlxsw_reg_mtwe_sensor_warning_get(event->mtwe_pl, i + MLXSW_REG_MTMP_MODULE_INDEX_MIN); spin_lock(&mlxsw_env->module_info_lock); is_overheat = @@ -524,10 +534,29 @@ static void mlxsw_env_mtwe_event_func(const struct mlxsw_reg_info *reg, spin_unlock(&mlxsw_env->module_info_lock); } } + + kfree(event); +} + +static void +mlxsw_env_mtwe_listener_func(const struct mlxsw_reg_info *reg, char *mtwe_pl, + void *priv) +{ + struct mlxsw_env_module_temp_warn_event *event; + struct mlxsw_env *mlxsw_env = priv; + + event = kmalloc(sizeof(*event), GFP_ATOMIC); + if (!event) + return; + + event->mlxsw_env = mlxsw_env; + memcpy(event->mtwe_pl, mtwe_pl, MLXSW_REG_MTWE_LEN); + INIT_WORK(&event->work, mlxsw_env_mtwe_event_work); + mlxsw_core_schedule_work(&event->work); } static const struct mlxsw_listener mlxsw_env_temp_warn_listener = - MLXSW_EVENTL(mlxsw_env_mtwe_event_func, MTWE, MTWE); + MLXSW_EVENTL(mlxsw_env_mtwe_listener_func, MTWE, MTWE); static int mlxsw_env_temp_warn_event_register(struct mlxsw_core *mlxsw_core) { From bd6e43f5953d39d5cb06041d5ae3e9e5470336ef Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 15 Sep 2021 13:13:08 +0300 Subject: [PATCH 81/87] mlxsw: core_env: Convert 'module_info_lock' to a mutex After the previous patch, the lock is always taken in process context so it can be converted to a mutex. It is needed for future changes where we will need to be able to sleep when holding the lock. Convert the lock to a mutex. Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/core_env.c | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_env.c b/drivers/net/ethernet/mellanox/mlxsw/core_env.c index 27eba0a0c91c..543f401cb5c6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_env.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_env.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "core.h" #include "core_env.h" @@ -19,7 +20,7 @@ struct mlxsw_env_module_info { struct mlxsw_env { struct mlxsw_core *core; u8 module_count; - spinlock_t module_info_lock; /* Protects 'module_info'. */ + struct mutex module_info_lock; /* Protects 'module_info'. */ struct mlxsw_env_module_info module_info[]; }; @@ -507,7 +508,7 @@ static void mlxsw_env_mtwe_event_work(struct work_struct *work) sensor_warning = mlxsw_reg_mtwe_sensor_warning_get(event->mtwe_pl, i + MLXSW_REG_MTMP_MODULE_INDEX_MIN); - spin_lock(&mlxsw_env->module_info_lock); + mutex_lock(&mlxsw_env->module_info_lock); is_overheat = mlxsw_env->module_info[i].is_overheat; @@ -517,13 +518,13 @@ static void mlxsw_env_mtwe_event_work(struct work_struct *work) * warning OR current state in "no warning" and MTWE * does not report warning. */ - spin_unlock(&mlxsw_env->module_info_lock); + mutex_unlock(&mlxsw_env->module_info_lock); continue; } else if (is_overheat && !sensor_warning) { /* MTWE reports "no warning", turn is_overheat off. */ mlxsw_env->module_info[i].is_overheat = false; - spin_unlock(&mlxsw_env->module_info_lock); + mutex_unlock(&mlxsw_env->module_info_lock); } else { /* Current state is "no warning" and MTWE reports * "warning", increase the counter and turn is_overheat @@ -531,7 +532,7 @@ static void mlxsw_env_mtwe_event_work(struct work_struct *work) */ mlxsw_env->module_info[i].is_overheat = true; mlxsw_env->module_info[i].module_overheat_counter++; - spin_unlock(&mlxsw_env->module_info_lock); + mutex_unlock(&mlxsw_env->module_info_lock); } } @@ -597,9 +598,9 @@ static void mlxsw_env_pmpe_event_work(struct work_struct *work) work); mlxsw_env = event->mlxsw_env; - spin_lock_bh(&mlxsw_env->module_info_lock); + mutex_lock(&mlxsw_env->module_info_lock); mlxsw_env->module_info[event->module].is_overheat = false; - spin_unlock_bh(&mlxsw_env->module_info_lock); + mutex_unlock(&mlxsw_env->module_info_lock); err = mlxsw_env_module_has_temp_sensor(mlxsw_env->core, event->module, &has_temp_sensor); @@ -699,9 +700,9 @@ mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 module, if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) return -EINVAL; - spin_lock_bh(&mlxsw_env->module_info_lock); + mutex_lock(&mlxsw_env->module_info_lock); *p_counter = mlxsw_env->module_info[module].module_overheat_counter; - spin_unlock_bh(&mlxsw_env->module_info_lock); + mutex_unlock(&mlxsw_env->module_info_lock); return 0; } @@ -725,7 +726,7 @@ int mlxsw_env_init(struct mlxsw_core *mlxsw_core, struct mlxsw_env **p_env) if (!env) return -ENOMEM; - spin_lock_init(&env->module_info_lock); + mutex_init(&env->module_info_lock); env->core = mlxsw_core; env->module_count = module_count; *p_env = env; @@ -755,6 +756,7 @@ err_oper_state_event_enable: err_module_plug_event_register: mlxsw_env_temp_warn_event_unregister(env); err_temp_warn_event_register: + mutex_destroy(&env->module_info_lock); kfree(env); return err; } @@ -765,5 +767,6 @@ void mlxsw_env_fini(struct mlxsw_env *env) /* Make sure there is no more event work scheduled. */ mlxsw_core_flush_owq(); mlxsw_env_temp_warn_event_unregister(env); + mutex_destroy(&env->module_info_lock); kfree(env); } From 06277ca23868d3e1ce9d09650f2cf14abc6b69be Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 15 Sep 2021 13:13:09 +0300 Subject: [PATCH 82/87] mlxsw: spectrum: Do not return an error in ndo_stop() The return value is not checked by the networking stack. Allows us to simplify a later patch. Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 9cbc893c2545..976d7e1ecb14 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -577,7 +577,8 @@ static int mlxsw_sp_port_stop(struct net_device *dev) struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); netif_stop_queue(dev); - return mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false); + mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false); + return 0; } static netdev_tx_t mlxsw_sp_port_xmit(struct sk_buff *skb, From 196bff2927a753b7472b2c48f93bed2c17f93a47 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 15 Sep 2021 13:13:10 +0300 Subject: [PATCH 83/87] mlxsw: spectrum: Do not return an error in mlxsw_sp_port_module_unmap() The return value is never checked. Allows us to simplify a later patch. Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 976d7e1ecb14..cea411884b05 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -551,13 +551,13 @@ mlxsw_sp_port_module_map(struct mlxsw_sp *mlxsw_sp, u8 local_port, return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl); } -static int mlxsw_sp_port_module_unmap(struct mlxsw_sp *mlxsw_sp, u8 local_port) +static void mlxsw_sp_port_module_unmap(struct mlxsw_sp *mlxsw_sp, u8 local_port) { char pmlp_pl[MLXSW_REG_PMLP_LEN]; mlxsw_reg_pmlp_pack(pmlp_pl, local_port); mlxsw_reg_pmlp_width_set(pmlp_pl, 0); - return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl); + mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl); } static int mlxsw_sp_port_open(struct net_device *dev) From 896f399be0780549703d23fabe6e6bf40c661475 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 15 Sep 2021 13:13:11 +0300 Subject: [PATCH 84/87] mlxsw: Track per-module port status In the common port module core, track the number of logical ports that are mapped to the port module and the number of logical ports using it that are administratively up. This will be used by later patches to potentially veto and control certain operations on the module, such as reset and setting its power mode. Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/core_env.c | 56 +++++++++++++++++++ .../net/ethernet/mellanox/mlxsw/core_env.h | 9 +++ drivers/net/ethernet/mellanox/mlxsw/minimal.c | 20 ++++++- .../net/ethernet/mellanox/mlxsw/spectrum.c | 37 ++++++++++-- 4 files changed, 113 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_env.c b/drivers/net/ethernet/mellanox/mlxsw/core_env.c index 543f401cb5c6..c7b7254061ee 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_env.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_env.c @@ -15,6 +15,8 @@ struct mlxsw_env_module_info { u64 module_overheat_counter; bool is_overheat; + int num_ports_mapped; + int num_ports_up; }; struct mlxsw_env { @@ -708,6 +710,60 @@ mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 module, } EXPORT_SYMBOL(mlxsw_env_module_overheat_counter_get); +void mlxsw_env_module_port_map(struct mlxsw_core *mlxsw_core, u8 module) +{ + struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); + + if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) + return; + + mutex_lock(&mlxsw_env->module_info_lock); + mlxsw_env->module_info[module].num_ports_mapped++; + mutex_unlock(&mlxsw_env->module_info_lock); +} +EXPORT_SYMBOL(mlxsw_env_module_port_map); + +void mlxsw_env_module_port_unmap(struct mlxsw_core *mlxsw_core, u8 module) +{ + struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); + + if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) + return; + + mutex_lock(&mlxsw_env->module_info_lock); + mlxsw_env->module_info[module].num_ports_mapped--; + mutex_unlock(&mlxsw_env->module_info_lock); +} +EXPORT_SYMBOL(mlxsw_env_module_port_unmap); + +int mlxsw_env_module_port_up(struct mlxsw_core *mlxsw_core, u8 module) +{ + struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); + + if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) + return -EINVAL; + + mutex_lock(&mlxsw_env->module_info_lock); + mlxsw_env->module_info[module].num_ports_up++; + mutex_unlock(&mlxsw_env->module_info_lock); + + return 0; +} +EXPORT_SYMBOL(mlxsw_env_module_port_up); + +void mlxsw_env_module_port_down(struct mlxsw_core *mlxsw_core, u8 module) +{ + struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); + + if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) + return; + + mutex_lock(&mlxsw_env->module_info_lock); + mlxsw_env->module_info[module].num_ports_up--; + mutex_unlock(&mlxsw_env->module_info_lock); +} +EXPORT_SYMBOL(mlxsw_env_module_port_down); + int mlxsw_env_init(struct mlxsw_core *mlxsw_core, struct mlxsw_env **p_env) { char mgpir_pl[MLXSW_REG_MGPIR_LEN]; diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_env.h b/drivers/net/ethernet/mellanox/mlxsw/core_env.h index 0bf5bd0f8a7e..ba9269f12cb8 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_env.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core_env.h @@ -27,6 +27,15 @@ mlxsw_env_get_module_eeprom_by_page(struct mlxsw_core *mlxsw_core, u8 module, int mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 module, u64 *p_counter); + +void mlxsw_env_module_port_map(struct mlxsw_core *mlxsw_core, u8 module); + +void mlxsw_env_module_port_unmap(struct mlxsw_core *mlxsw_core, u8 module); + +int mlxsw_env_module_port_up(struct mlxsw_core *mlxsw_core, u8 module); + +void mlxsw_env_module_port_down(struct mlxsw_core *mlxsw_core, u8 module); + int mlxsw_env_init(struct mlxsw_core *core, struct mlxsw_env **p_env); void mlxsw_env_fini(struct mlxsw_env *env); diff --git a/drivers/net/ethernet/mellanox/mlxsw/minimal.c b/drivers/net/ethernet/mellanox/mlxsw/minimal.c index d9d56c44e994..a3eca0b56bbe 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/minimal.c +++ b/drivers/net/ethernet/mellanox/mlxsw/minimal.c @@ -54,8 +54,20 @@ static int mlxsw_m_base_mac_get(struct mlxsw_m *mlxsw_m) return 0; } -static int mlxsw_m_port_dummy_open_stop(struct net_device *dev) +static int mlxsw_m_port_open(struct net_device *dev) { + struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev); + struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m; + + return mlxsw_env_module_port_up(mlxsw_m->core, mlxsw_m_port->module); +} + +static int mlxsw_m_port_stop(struct net_device *dev) +{ + struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev); + struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m; + + mlxsw_env_module_port_down(mlxsw_m->core, mlxsw_m_port->module); return 0; } @@ -70,8 +82,8 @@ mlxsw_m_port_get_devlink_port(struct net_device *dev) } static const struct net_device_ops mlxsw_m_port_netdev_ops = { - .ndo_open = mlxsw_m_port_dummy_open_stop, - .ndo_stop = mlxsw_m_port_dummy_open_stop, + .ndo_open = mlxsw_m_port_open, + .ndo_stop = mlxsw_m_port_stop, .ndo_get_devlink_port = mlxsw_m_port_get_devlink_port, }; @@ -266,6 +278,7 @@ static int mlxsw_m_port_module_map(struct mlxsw_m *mlxsw_m, u8 local_port, if (WARN_ON_ONCE(module >= max_ports)) return -EINVAL; + mlxsw_env_module_port_map(mlxsw_m->core, module); mlxsw_m->module_to_port[module] = ++mlxsw_m->max_ports; return 0; @@ -274,6 +287,7 @@ static int mlxsw_m_port_module_map(struct mlxsw_m *mlxsw_m, u8 local_port, static void mlxsw_m_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 module) { mlxsw_m->module_to_port[module] = -1; + mlxsw_env_module_port_unmap(mlxsw_m->core, module); } static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index cea411884b05..0e81ae723bc8 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -539,7 +539,9 @@ mlxsw_sp_port_module_map(struct mlxsw_sp *mlxsw_sp, u8 local_port, const struct mlxsw_sp_port_mapping *port_mapping) { char pmlp_pl[MLXSW_REG_PMLP_LEN]; - int i; + int i, err; + + mlxsw_env_module_port_map(mlxsw_sp->core, port_mapping->module); mlxsw_reg_pmlp_pack(pmlp_pl, local_port); mlxsw_reg_pmlp_width_set(pmlp_pl, port_mapping->width); @@ -548,36 +550,58 @@ mlxsw_sp_port_module_map(struct mlxsw_sp *mlxsw_sp, u8 local_port, mlxsw_reg_pmlp_tx_lane_set(pmlp_pl, i, port_mapping->lane + i); /* Rx & Tx */ } - return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl); + if (err) + goto err_pmlp_write; + return 0; + +err_pmlp_write: + mlxsw_env_module_port_unmap(mlxsw_sp->core, port_mapping->module); + return err; } -static void mlxsw_sp_port_module_unmap(struct mlxsw_sp *mlxsw_sp, u8 local_port) +static void mlxsw_sp_port_module_unmap(struct mlxsw_sp *mlxsw_sp, u8 local_port, + u8 module) { char pmlp_pl[MLXSW_REG_PMLP_LEN]; mlxsw_reg_pmlp_pack(pmlp_pl, local_port); mlxsw_reg_pmlp_width_set(pmlp_pl, 0); mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl); + mlxsw_env_module_port_unmap(mlxsw_sp->core, module); } static int mlxsw_sp_port_open(struct net_device *dev) { struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; int err; - err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, true); + err = mlxsw_env_module_port_up(mlxsw_sp->core, + mlxsw_sp_port->mapping.module); if (err) return err; + err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, true); + if (err) + goto err_port_admin_status_set; netif_start_queue(dev); return 0; + +err_port_admin_status_set: + mlxsw_env_module_port_down(mlxsw_sp->core, + mlxsw_sp_port->mapping.module); + return err; } static int mlxsw_sp_port_stop(struct net_device *dev) { struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; netif_stop_queue(dev); mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false); + mlxsw_env_module_port_down(mlxsw_sp->core, + mlxsw_sp_port->mapping.module); return 0; } @@ -1747,13 +1771,14 @@ err_port_label_info_get: mlxsw_sp_port_swid_set(mlxsw_sp, local_port, MLXSW_PORT_SWID_DISABLED_PORT); err_port_swid_set: - mlxsw_sp_port_module_unmap(mlxsw_sp, local_port); + mlxsw_sp_port_module_unmap(mlxsw_sp, local_port, port_mapping->module); return err; } static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) { struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port]; + u8 module = mlxsw_sp_port->mapping.module; cancel_delayed_work_sync(&mlxsw_sp_port->periodic_hw_stats.update_dw); cancel_delayed_work_sync(&mlxsw_sp_port->ptp.shaper_dw); @@ -1775,7 +1800,7 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) mlxsw_core_port_fini(mlxsw_sp->core, local_port); mlxsw_sp_port_swid_set(mlxsw_sp, local_port, MLXSW_PORT_SWID_DISABLED_PORT); - mlxsw_sp_port_module_unmap(mlxsw_sp, local_port); + mlxsw_sp_port_module_unmap(mlxsw_sp, local_port, module); } static int mlxsw_sp_cpu_port_create(struct mlxsw_sp *mlxsw_sp) From ef23841bb94ab525363e9161847894df70ee49ad Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 15 Sep 2021 13:13:12 +0300 Subject: [PATCH 85/87] mlxsw: reg: Add fields to PMAOS register The Ports Module Administrative and Operational Status (PMAOS) register configures and retrieves the per-module status. Extend it with fields required to support various module settings such as reset and power mode. Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 8d87f3cc5711..667856f00728 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -5681,6 +5681,14 @@ static inline void mlxsw_reg_pspa_pack(char *payload, u8 swid, u8 local_port) MLXSW_REG_DEFINE(pmaos, MLXSW_REG_PMAOS_ID, MLXSW_REG_PMAOS_LEN); +/* reg_pmaos_rst + * Module reset toggle. + * Note: Setting reset while module is plugged-in will result in transition to + * "initializing" operational state. + * Access: OP + */ +MLXSW_ITEM32(reg, pmaos, rst, 0x00, 31, 1); + /* reg_pmaos_slot_index * Slot index. * Access: Index @@ -5693,6 +5701,24 @@ MLXSW_ITEM32(reg, pmaos, slot_index, 0x00, 24, 4); */ MLXSW_ITEM32(reg, pmaos, module, 0x00, 16, 8); +enum mlxsw_reg_pmaos_admin_status { + MLXSW_REG_PMAOS_ADMIN_STATUS_ENABLED = 1, + MLXSW_REG_PMAOS_ADMIN_STATUS_DISABLED = 2, + /* If the module is active and then unplugged, or experienced an error + * event, the operational status should go to "disabled" and can only + * be enabled upon explicit enable command. + */ + MLXSW_REG_PMAOS_ADMIN_STATUS_ENABLED_ONCE = 3, +}; + +/* reg_pmaos_admin_status + * Module administrative state (the desired state of the module). + * Note: To disable a module, all ports associated with the port must be + * administatively down first. + * Access: RW + */ +MLXSW_ITEM32(reg, pmaos, admin_status, 0x00, 8, 4); + /* reg_pmaos_ase * Admin state update enable. * If this bit is set, admin state will be updated based on admin_state field. From 8f4ebdb0a2744557a95810787126eb0555e7424b Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 15 Sep 2021 13:13:13 +0300 Subject: [PATCH 86/87] mlxsw: Make PMAOS pack function more generic The PMAOS register has enable bits (e.g., PMAOS.ee) that allow changing only a subset of the fields, which is exactly what subsequent patches will need to do. Instead of passing multiple arguments to its pack function, only pass the module index and let the rest be set by the different callers. No functional changes intended. Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core_env.c | 6 ++++-- drivers/net/ethernet/mellanox/mlxsw/reg.h | 5 +---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_env.c b/drivers/net/ethernet/mellanox/mlxsw/core_env.c index c7b7254061ee..a474629643aa 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_env.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_env.c @@ -684,8 +684,10 @@ mlxsw_env_module_oper_state_event_enable(struct mlxsw_core *mlxsw_core, for (i = 0; i < module_count; i++) { char pmaos_pl[MLXSW_REG_PMAOS_LEN]; - mlxsw_reg_pmaos_pack(pmaos_pl, i, - MLXSW_REG_PMAOS_E_GENERATE_EVENT); + mlxsw_reg_pmaos_pack(pmaos_pl, i); + mlxsw_reg_pmaos_e_set(pmaos_pl, + MLXSW_REG_PMAOS_E_GENERATE_EVENT); + mlxsw_reg_pmaos_ee_set(pmaos_pl, true); err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl); if (err) return err; diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 667856f00728..6c96e124e4c8 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -5747,13 +5747,10 @@ enum mlxsw_reg_pmaos_e { */ MLXSW_ITEM32(reg, pmaos, e, 0x04, 0, 2); -static inline void mlxsw_reg_pmaos_pack(char *payload, u8 module, - enum mlxsw_reg_pmaos_e e) +static inline void mlxsw_reg_pmaos_pack(char *payload, u8 module) { MLXSW_REG_ZERO(pmaos, payload); mlxsw_reg_pmaos_module_set(payload, module); - mlxsw_reg_pmaos_e_set(payload, e); - mlxsw_reg_pmaos_ee_set(payload, true); } /* PPLR - Port Physical Loopback Register From 49fd3b645de8c2cc124c284b2be8985c50e76ee0 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 15 Sep 2021 13:13:14 +0300 Subject: [PATCH 87/87] mlxsw: Add support for transceiver modules reset Implement support for ethtool_ops::reset in order to reset transceiver modules. The module backing the netdev is reset when the 'ETH_RESET_PHY' flag is set. After a successful reset, the flag is cleared by the driver and other flags are ignored. This is in accordance with the interface documentation: "The reset() operation must clear the flags for the components which were actually reset. On successful return, the flags indicate the components which were not reset, either because they do not exist in the hardware or because they cannot be reset independently. The driver must never reset any components that were not requested." Reset is useful in order to allow a module to transition out of a fault state. From section 6.3.2.12 in CMIS 5.0: "Except for a power cycle, the only exit path from the ModuleFault state is to perform a module reset by taking an action that causes the ResetS transition signal to become TRUE (see Table 6-11)". An error is returned when the netdev is administratively up: # ip link set dev swp11 up # ethtool --reset swp11 phy ETHTOOL_RESET 0x40 Cannot issue ETHTOOL_RESET: Invalid argument # ip link set dev swp11 down # ethtool --reset swp11 phy ETHTOOL_RESET 0x40 Components reset: 0x40 An error is returned when the module is shared by multiple ports (split ports) and the "phy-shared" flag is not set: # devlink port split swp11 count 4 # ethtool --reset swp11s0 phy ETHTOOL_RESET 0x40 Cannot issue ETHTOOL_RESET: Invalid argument # ethtool --reset swp11s0 phy-shared ETHTOOL_RESET 0x400000 Components reset: 0x400000 # devlink port unsplit swp11s0 # ethtool --reset swp11 phy ETHTOOL_RESET 0x40 Components reset: 0x40 An error is also returned when one of the ports using the module is administratively up: # devlink port split swp11 count 4 # ip link set dev swp11s1 up # ethtool --reset swp11s0 phy-shared ETHTOOL_RESET 0x400000 Cannot issue ETHTOOL_RESET: Invalid argument # ip link set dev swp11s1 down # ethtool --reset swp11s0 phy-shared ETHTOOL_RESET 0x400000 Components reset: 0x400000 Reset is performed by writing to the "rst" bit of the PMAOS register, which instructs the firmware to assert the reset signal connected to the module for a fixed amount of time. Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/core_env.c | 53 +++++++++++++++++++ .../net/ethernet/mellanox/mlxsw/core_env.h | 4 ++ drivers/net/ethernet/mellanox/mlxsw/minimal.c | 10 ++++ .../mellanox/mlxsw/spectrum_ethtool.c | 10 ++++ 4 files changed, 77 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_env.c b/drivers/net/ethernet/mellanox/mlxsw/core_env.c index a474629643aa..9e367174743d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_env.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_env.c @@ -392,6 +392,59 @@ mlxsw_env_get_module_eeprom_by_page(struct mlxsw_core *mlxsw_core, u8 module, } EXPORT_SYMBOL(mlxsw_env_get_module_eeprom_by_page); +static int mlxsw_env_module_reset(struct mlxsw_core *mlxsw_core, u8 module) +{ + char pmaos_pl[MLXSW_REG_PMAOS_LEN]; + + mlxsw_reg_pmaos_pack(pmaos_pl, module); + mlxsw_reg_pmaos_rst_set(pmaos_pl, true); + + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl); +} + +int mlxsw_env_reset_module(struct net_device *netdev, + struct mlxsw_core *mlxsw_core, u8 module, u32 *flags) +{ + struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); + u32 req = *flags; + int err; + + if (!(req & ETH_RESET_PHY) && + !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT))) + return 0; + + if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) + return -EINVAL; + + mutex_lock(&mlxsw_env->module_info_lock); + + if (mlxsw_env->module_info[module].num_ports_up) { + netdev_err(netdev, "Cannot reset module when ports using it are administratively up\n"); + err = -EINVAL; + goto out; + } + + if (mlxsw_env->module_info[module].num_ports_mapped > 1 && + !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT))) { + netdev_err(netdev, "Cannot reset module without \"phy-shared\" flag when shared by multiple ports\n"); + err = -EINVAL; + goto out; + } + + err = mlxsw_env_module_reset(mlxsw_core, module); + if (err) { + netdev_err(netdev, "Failed to reset module\n"); + goto out; + } + + *flags &= ~(ETH_RESET_PHY | (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT)); + +out: + mutex_unlock(&mlxsw_env->module_info_lock); + return err; +} +EXPORT_SYMBOL(mlxsw_env_reset_module); + static int mlxsw_env_module_has_temp_sensor(struct mlxsw_core *mlxsw_core, u8 module, bool *p_has_temp_sensor) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_env.h b/drivers/net/ethernet/mellanox/mlxsw/core_env.h index ba9269f12cb8..c486397f5dfe 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_env.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core_env.h @@ -24,6 +24,10 @@ mlxsw_env_get_module_eeprom_by_page(struct mlxsw_core *mlxsw_core, u8 module, const struct ethtool_module_eeprom *page, struct netlink_ext_ack *extack); +int mlxsw_env_reset_module(struct net_device *netdev, + struct mlxsw_core *mlxsw_core, u8 module, + u32 *flags); + int mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 module, u64 *p_counter); diff --git a/drivers/net/ethernet/mellanox/mlxsw/minimal.c b/drivers/net/ethernet/mellanox/mlxsw/minimal.c index a3eca0b56bbe..9644e9c486b8 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/minimal.c +++ b/drivers/net/ethernet/mellanox/mlxsw/minimal.c @@ -136,11 +136,21 @@ mlxsw_m_get_module_eeprom_by_page(struct net_device *netdev, page, extack); } +static int mlxsw_m_reset(struct net_device *netdev, u32 *flags) +{ + struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); + struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; + + return mlxsw_env_reset_module(netdev, core, mlxsw_m_port->module, + flags); +} + static const struct ethtool_ops mlxsw_m_port_ethtool_ops = { .get_drvinfo = mlxsw_m_module_get_drvinfo, .get_module_info = mlxsw_m_get_module_info, .get_module_eeprom = mlxsw_m_get_module_eeprom, .get_module_eeprom_by_page = mlxsw_m_get_module_eeprom_by_page, + .reset = mlxsw_m_reset, }; static int diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c index 267590a0eee7..06f1645561c6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c @@ -1197,6 +1197,15 @@ mlxsw_sp_get_rmon_stats(struct net_device *dev, *ranges = mlxsw_rmon_ranges; } +static int mlxsw_sp_reset(struct net_device *dev, u32 *flags) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + u8 module = mlxsw_sp_port->mapping.module; + + return mlxsw_env_reset_module(dev, mlxsw_sp->core, module, flags); +} + const struct ethtool_ops mlxsw_sp_port_ethtool_ops = { .cap_link_lanes_supported = true, .get_drvinfo = mlxsw_sp_port_get_drvinfo, @@ -1218,6 +1227,7 @@ const struct ethtool_ops mlxsw_sp_port_ethtool_ops = { .get_eth_mac_stats = mlxsw_sp_get_eth_mac_stats, .get_eth_ctrl_stats = mlxsw_sp_get_eth_ctrl_stats, .get_rmon_stats = mlxsw_sp_get_rmon_stats, + .reset = mlxsw_sp_reset, }; struct mlxsw_sp1_port_link_mode {