forked from Minki/linux
cf718eaa8f
Enabled the PF->VF Mailbox support. Mailbox message are interpreted as {type, opcode, data}. Supported message types are REQ, ACK and NACK. Signed-off-by: Srikanth Jampala <Jampala.Srikanth@cavium.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
205 lines
4.7 KiB
C
205 lines
4.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include <linux/workqueue.h>
|
|
|
|
#include "nitrox_csr.h"
|
|
#include "nitrox_hal.h"
|
|
#include "nitrox_dev.h"
|
|
|
|
#define RING_TO_VFNO(_x, _y) ((_x) / (_y))
|
|
|
|
/**
|
|
* mbx_msg_type - Mailbox message types
|
|
*/
|
|
enum mbx_msg_type {
|
|
MBX_MSG_TYPE_NOP,
|
|
MBX_MSG_TYPE_REQ,
|
|
MBX_MSG_TYPE_ACK,
|
|
MBX_MSG_TYPE_NACK,
|
|
};
|
|
|
|
/**
|
|
* mbx_msg_opcode - Mailbox message opcodes
|
|
*/
|
|
enum mbx_msg_opcode {
|
|
MSG_OP_VF_MODE = 1,
|
|
MSG_OP_VF_UP,
|
|
MSG_OP_VF_DOWN,
|
|
MSG_OP_CHIPID_VFID,
|
|
};
|
|
|
|
struct pf2vf_work {
|
|
struct nitrox_vfdev *vfdev;
|
|
struct nitrox_device *ndev;
|
|
struct work_struct pf2vf_resp;
|
|
};
|
|
|
|
static inline u64 pf2vf_read_mbox(struct nitrox_device *ndev, int ring)
|
|
{
|
|
u64 reg_addr;
|
|
|
|
reg_addr = NPS_PKT_MBOX_VF_PF_PFDATAX(ring);
|
|
return nitrox_read_csr(ndev, reg_addr);
|
|
}
|
|
|
|
static inline void pf2vf_write_mbox(struct nitrox_device *ndev, u64 value,
|
|
int ring)
|
|
{
|
|
u64 reg_addr;
|
|
|
|
reg_addr = NPS_PKT_MBOX_PF_VF_PFDATAX(ring);
|
|
nitrox_write_csr(ndev, reg_addr, value);
|
|
}
|
|
|
|
static void pf2vf_send_response(struct nitrox_device *ndev,
|
|
struct nitrox_vfdev *vfdev)
|
|
{
|
|
union mbox_msg msg;
|
|
|
|
msg.value = vfdev->msg.value;
|
|
|
|
switch (vfdev->msg.opcode) {
|
|
case MSG_OP_VF_MODE:
|
|
msg.data = ndev->mode;
|
|
break;
|
|
case MSG_OP_VF_UP:
|
|
vfdev->nr_queues = vfdev->msg.data;
|
|
atomic_set(&vfdev->state, __NDEV_READY);
|
|
break;
|
|
case MSG_OP_CHIPID_VFID:
|
|
msg.id.chipid = ndev->idx;
|
|
msg.id.vfid = vfdev->vfno;
|
|
break;
|
|
case MSG_OP_VF_DOWN:
|
|
vfdev->nr_queues = 0;
|
|
atomic_set(&vfdev->state, __NDEV_NOT_READY);
|
|
break;
|
|
default:
|
|
msg.type = MBX_MSG_TYPE_NOP;
|
|
break;
|
|
}
|
|
|
|
if (msg.type == MBX_MSG_TYPE_NOP)
|
|
return;
|
|
|
|
/* send ACK to VF */
|
|
msg.type = MBX_MSG_TYPE_ACK;
|
|
pf2vf_write_mbox(ndev, msg.value, vfdev->ring);
|
|
|
|
vfdev->msg.value = 0;
|
|
atomic64_inc(&vfdev->mbx_resp);
|
|
}
|
|
|
|
static void pf2vf_resp_handler(struct work_struct *work)
|
|
{
|
|
struct pf2vf_work *pf2vf_resp = container_of(work, struct pf2vf_work,
|
|
pf2vf_resp);
|
|
struct nitrox_vfdev *vfdev = pf2vf_resp->vfdev;
|
|
struct nitrox_device *ndev = pf2vf_resp->ndev;
|
|
|
|
switch (vfdev->msg.type) {
|
|
case MBX_MSG_TYPE_REQ:
|
|
/* process the request from VF */
|
|
pf2vf_send_response(ndev, vfdev);
|
|
break;
|
|
case MBX_MSG_TYPE_ACK:
|
|
case MBX_MSG_TYPE_NACK:
|
|
break;
|
|
};
|
|
|
|
kfree(pf2vf_resp);
|
|
}
|
|
|
|
void nitrox_pf2vf_mbox_handler(struct nitrox_device *ndev)
|
|
{
|
|
struct nitrox_vfdev *vfdev;
|
|
struct pf2vf_work *pfwork;
|
|
u64 value, reg_addr;
|
|
u32 i;
|
|
int vfno;
|
|
|
|
/* loop for VF(0..63) */
|
|
reg_addr = NPS_PKT_MBOX_INT_LO;
|
|
value = nitrox_read_csr(ndev, reg_addr);
|
|
for_each_set_bit(i, (const unsigned long *)&value, BITS_PER_LONG) {
|
|
/* get the vfno from ring */
|
|
vfno = RING_TO_VFNO(i, ndev->iov.max_vf_queues);
|
|
vfdev = ndev->iov.vfdev + vfno;
|
|
vfdev->ring = i;
|
|
/* fill the vf mailbox data */
|
|
vfdev->msg.value = pf2vf_read_mbox(ndev, vfdev->ring);
|
|
pfwork = kzalloc(sizeof(*pfwork), GFP_ATOMIC);
|
|
if (!pfwork)
|
|
continue;
|
|
|
|
pfwork->vfdev = vfdev;
|
|
pfwork->ndev = ndev;
|
|
INIT_WORK(&pfwork->pf2vf_resp, pf2vf_resp_handler);
|
|
queue_work(ndev->iov.pf2vf_wq, &pfwork->pf2vf_resp);
|
|
/* clear the corresponding vf bit */
|
|
nitrox_write_csr(ndev, reg_addr, BIT_ULL(i));
|
|
}
|
|
|
|
/* loop for VF(64..127) */
|
|
reg_addr = NPS_PKT_MBOX_INT_HI;
|
|
value = nitrox_read_csr(ndev, reg_addr);
|
|
for_each_set_bit(i, (const unsigned long *)&value, BITS_PER_LONG) {
|
|
/* get the vfno from ring */
|
|
vfno = RING_TO_VFNO(i + 64, ndev->iov.max_vf_queues);
|
|
vfdev = ndev->iov.vfdev + vfno;
|
|
vfdev->ring = (i + 64);
|
|
/* fill the vf mailbox data */
|
|
vfdev->msg.value = pf2vf_read_mbox(ndev, vfdev->ring);
|
|
|
|
pfwork = kzalloc(sizeof(*pfwork), GFP_ATOMIC);
|
|
if (!pfwork)
|
|
continue;
|
|
|
|
pfwork->vfdev = vfdev;
|
|
pfwork->ndev = ndev;
|
|
INIT_WORK(&pfwork->pf2vf_resp, pf2vf_resp_handler);
|
|
queue_work(ndev->iov.pf2vf_wq, &pfwork->pf2vf_resp);
|
|
/* clear the corresponding vf bit */
|
|
nitrox_write_csr(ndev, reg_addr, BIT_ULL(i));
|
|
}
|
|
}
|
|
|
|
int nitrox_mbox_init(struct nitrox_device *ndev)
|
|
{
|
|
struct nitrox_vfdev *vfdev;
|
|
int i;
|
|
|
|
ndev->iov.vfdev = kcalloc(ndev->iov.num_vfs,
|
|
sizeof(struct nitrox_vfdev), GFP_KERNEL);
|
|
if (!ndev->iov.vfdev)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < ndev->iov.num_vfs; i++) {
|
|
vfdev = ndev->iov.vfdev + i;
|
|
vfdev->vfno = i;
|
|
}
|
|
|
|
/* allocate pf2vf response workqueue */
|
|
ndev->iov.pf2vf_wq = alloc_workqueue("nitrox_pf2vf", 0, 0);
|
|
if (!ndev->iov.pf2vf_wq) {
|
|
kfree(ndev->iov.vfdev);
|
|
return -ENOMEM;
|
|
}
|
|
/* enable pf2vf mailbox interrupts */
|
|
enable_pf2vf_mbox_interrupts(ndev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void nitrox_mbox_cleanup(struct nitrox_device *ndev)
|
|
{
|
|
/* disable pf2vf mailbox interrupts */
|
|
disable_pf2vf_mbox_interrupts(ndev);
|
|
/* destroy workqueue */
|
|
if (ndev->iov.pf2vf_wq)
|
|
destroy_workqueue(ndev->iov.pf2vf_wq);
|
|
|
|
kfree(ndev->iov.vfdev);
|
|
ndev->iov.pf2vf_wq = NULL;
|
|
ndev->iov.vfdev = NULL;
|
|
}
|