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 <nannaiah@nvidia.com>
Change-Id: I8bb71c3704308f26ae97141ba1e8354da31fd70f
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2317274
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
Reviewed-by: automaticguardword <automaticguardword@nvidia.com>
Reviewed-by: Srinivas Ramachandran <srinivasra@nvidia.com>
Reviewed-by: Ashutosh Jha <ajha@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
GVS: Gerrit_Virtual_Submit
This commit is contained in:
nannaiah
2020-03-18 12:14:21 -07:00
committed by Revanth Kumar Uppala
parent 69cf090fde
commit 4862fe2d5b
2 changed files with 149 additions and 0 deletions

View File

@@ -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++) {

View File

@@ -40,6 +40,7 @@
#include <linux/of.h>
#include <linux/ktime.h>
#include <linux/hrtimer.h>
#include <linux/tegra-ivc.h>
#include <osi_core.h>
#include <osi_dma.h>
@@ -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;
};
/**