From 4862fe2d5b711cf49f47d733b0920966e6d48332 Mon Sep 17 00:00:00 2001 From: nannaiah Date: Wed, 18 Mar 2020 12:14:21 -0700 Subject: [PATCH] nvethernet: Add IVC handling for Common interrupts When ethernet virtualization exists the common interrupts are served from ethernet server and server sends ivc messages to host drivers to handle common interrupts. Bug 2694285 Change-Id: I0af0c7f97b3f2d34f4adb72c114c3cfca6a623fc Signed-off-by: Nagaraj Annaiah Change-Id: I8bb71c3704308f26ae97141ba1e8354da31fd70f Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2317274 Tested-by: mobile promotions Reviewed-by: automaticguardword Reviewed-by: Srinivas Ramachandran Reviewed-by: Ashutosh Jha Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit --- .../ethernet/nvidia/nvethernet/ether_linux.c | 136 ++++++++++++++++++ .../ethernet/nvidia/nvethernet/ether_linux.h | 13 ++ 2 files changed, 149 insertions(+) diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c index a8f0943b..742ea9d6 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c @@ -531,6 +531,12 @@ static void ether_free_irqs(struct ether_priv_data *pdata) pdata->common_irq_alloc_mask = 0U; } + if (pdata->ivck != NULL) { + cancel_work_sync(&pdata->ivc_work); + tegra_hv_ivc_unreserve(pdata->ivck); + devm_free_irq(pdata->dev, pdata->ivck->irq, pdata); + } + for (i = 0; i < pdata->osi_dma->num_dma_chans; i++) { chan = pdata->osi_dma->dma_chans[i]; @@ -547,6 +553,134 @@ static void ether_free_irqs(struct ether_priv_data *pdata) } } +/** + * @brief IVC ISR Routine + * + * Algorithm: IVC routine to handle common interrupt. + * 1) Verify if IVC channel is readable + * 2) Read IVC msg + * 3) Schedule ivc_work + * + * @param[in] irq: IRQ number. + * @param[in] data: Private data from ISR. + * + * @note MAC and PHY need to be initialized. + * + * @retval IRQ_HANDLED on success + * @retval IRQ_NONE on failure. + */ +static irqreturn_t ether_ivc_irq(int irq, void *data) +{ + struct ether_priv_data *pdata = data; + int ret; + + if (tegra_hv_ivc_channel_notified(pdata->ivck) != 0) { + dev_err(pdata->dev, "ivc channel not usable\n"); + return IRQ_HANDLED; + } + + if (tegra_hv_ivc_can_read(pdata->ivck)) { + dev_info(pdata->dev, "ivc read done\n"); + /* Read the current message for the ethernet server to be + * able to send further messages on next interrupt + */ + ret = tegra_hv_ivc_read(pdata->ivck, pdata->ivc_rx, + ETHER_MAX_IVC_BUF); + if (ret < 0) { + dev_err(pdata->dev, "IVC read failed: %d\n", ret); + } else { + /* Schedule work to execute the common IRQ Function + * which takes the appropriate action. + */ + schedule_work(&pdata->ivc_work); + } + } else { + dev_info(pdata->dev, "Can not read ivc channel: %d\n", + pdata->ivck->irq); + } + return IRQ_HANDLED; +} + +/** + * @brief IVC work + * + * Algorithm: Invoke OSI layer to handle common interrupt. + * + * @param[in] work: work structure. + * + * + * @retval void + */ + +static void ether_ivc_work(struct work_struct *work) +{ + struct ether_priv_data *pdata = + container_of(work, struct ether_priv_data, ivc_work); + osi_common_isr(pdata->osi_core); +} + +/** + * @Register IVC + * + * Algorithm: routine initializes IVC for common IRQ + * + * @param[in] pdata: OS dependent private data structure. + * + * @note IVC number need to be known. + * + * @retval 0 on success + * @retval "negative value" on failure. + */ +static int ether_init_ivc(struct ether_priv_data *pdata) +{ + struct device *dev = pdata->dev; + struct device_node *np, *hv_np; + uint32_t id; + int ret; + + np = dev->of_node; + if (!np) { + return -EINVAL; + } + + hv_np = of_parse_phandle(np, "ivc", 0); + if (!hv_np) { + return -EINVAL; + } + + ret = of_property_read_u32_index(np, "ivc", 1, &id); + if (ret) { + dev_err(dev, "ivc_init: Error in reading IVC DT\n"); + of_node_put(hv_np); + return -EINVAL; + } + + pdata->ivck = tegra_hv_ivc_reserve(hv_np, id, NULL); + of_node_put(hv_np); + if (IS_ERR_OR_NULL(pdata->ivck)) { + dev_err(dev, "Failed to reserve ivc channel:%u\n", id); + ret = PTR_ERR(pdata->ivck); + pdata->ivck = NULL; + return ret; + } + + dev_info(dev, "Reserved IVC channel #%u - frame_size=%d irq %d\n", + id, pdata->ivck->frame_size, pdata->ivck->irq); + + tegra_hv_ivc_channel_reset(pdata->ivck); + + INIT_WORK(&pdata->ivc_work, ether_ivc_work); + + ret = devm_request_irq(dev, pdata->ivck->irq, ether_ivc_irq, + 0, dev_name(dev), pdata); + if (ret) { + dev_err(dev, "Unable to request irq(%d)\n", pdata->ivck->irq); + tegra_hv_ivc_unreserve(pdata->ivck); + return ret; + } + return 0; +} + /** * @brief Register IRQs * @@ -577,6 +711,8 @@ static int ether_request_irqs(struct ether_priv_data *pdata) pdata->common_irq); return ret; } + /* TODO: Check return value and handle error */ + ether_init_ivc(pdata); pdata->common_irq_alloc_mask = 1; for (i = 0; i < osi_dma->num_dma_chans; i++) { diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h index bbf485ce..d6877718 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -121,6 +122,12 @@ */ #define ETHER_TX_MAX_FRAME_SIZE GSO_MAX_SIZE +/** + * @brief Ethernet Maximum IVC BUF + */ +#define ETHER_MAX_IVC_BUF 128 + + /** * @brief Check if Tx data buffer length is within bounds. * @@ -333,6 +340,12 @@ struct ether_priv_data { unsigned int tx_lpi_enabled; /** Time (usec) MAC waits to enter LPI after Tx complete */ unsigned int tx_lpi_timer; + /** ivc cookie */ + struct tegra_hv_ivc_cookie *ivck; + /** Buffer to receive pad ivc message */ + char ivc_rx[ETHER_MAX_IVC_BUF]; + /** ivc work */ + struct work_struct ivc_work; }; /**