| /********************************************************************** |
| * Author: Cavium, Inc. |
| * |
| * Contact: support@cavium.com |
| * Please include "LiquidIO" in the subject. |
| * |
| * Copyright (c) 2003-2016 Cavium, Inc. |
| * |
| * This file is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License, Version 2, as |
| * published by the Free Software Foundation. |
| * |
| * This file is distributed in the hope that it will be useful, but |
| * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty |
| * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or |
| * NONINFRINGEMENT. See the GNU General Public License for more details. |
| ***********************************************************************/ |
| #include <linux/pci.h> |
| #include <linux/netdevice.h> |
| #include "liquidio_common.h" |
| #include "octeon_droq.h" |
| #include "octeon_iq.h" |
| #include "response_manager.h" |
| #include "octeon_device.h" |
| #include "octeon_main.h" |
| #include "octeon_mailbox.h" |
| #include "cn23xx_pf_device.h" |
| |
| /** |
| * octeon_mbox_read: |
| * @oct: Pointer mailbox |
| * |
| * Reads the 8-bytes of data from the mbox register |
| * Writes back the acknowldgement inidcating completion of read |
| */ |
| int octeon_mbox_read(struct octeon_mbox *mbox) |
| { |
| union octeon_mbox_message msg; |
| int ret = 0; |
| |
| spin_lock(&mbox->lock); |
| |
| msg.u64 = readq(mbox->mbox_read_reg); |
| |
| if ((msg.u64 == OCTEON_PFVFACK) || (msg.u64 == OCTEON_PFVFSIG)) { |
| spin_unlock(&mbox->lock); |
| return 0; |
| } |
| |
| if (mbox->state & OCTEON_MBOX_STATE_REQUEST_RECEIVING) { |
| mbox->mbox_req.data[mbox->mbox_req.recv_len - 1] = msg.u64; |
| mbox->mbox_req.recv_len++; |
| } else { |
| if (mbox->state & OCTEON_MBOX_STATE_RESPONSE_RECEIVING) { |
| mbox->mbox_resp.data[mbox->mbox_resp.recv_len - 1] = |
| msg.u64; |
| mbox->mbox_resp.recv_len++; |
| } else { |
| if ((mbox->state & OCTEON_MBOX_STATE_IDLE) && |
| (msg.s.type == OCTEON_MBOX_REQUEST)) { |
| mbox->state &= ~OCTEON_MBOX_STATE_IDLE; |
| mbox->state |= |
| OCTEON_MBOX_STATE_REQUEST_RECEIVING; |
| mbox->mbox_req.msg.u64 = msg.u64; |
| mbox->mbox_req.q_no = mbox->q_no; |
| mbox->mbox_req.recv_len = 1; |
| } else { |
| if ((mbox->state & |
| OCTEON_MBOX_STATE_RESPONSE_PENDING) && |
| (msg.s.type == OCTEON_MBOX_RESPONSE)) { |
| mbox->state &= |
| ~OCTEON_MBOX_STATE_RESPONSE_PENDING; |
| mbox->state |= |
| OCTEON_MBOX_STATE_RESPONSE_RECEIVING |
| ; |
| mbox->mbox_resp.msg.u64 = msg.u64; |
| mbox->mbox_resp.q_no = mbox->q_no; |
| mbox->mbox_resp.recv_len = 1; |
| } else { |
| writeq(OCTEON_PFVFERR, |
| mbox->mbox_read_reg); |
| mbox->state |= OCTEON_MBOX_STATE_ERROR; |
| spin_unlock(&mbox->lock); |
| return 1; |
| } |
| } |
| } |
| } |
| |
| if (mbox->state & OCTEON_MBOX_STATE_REQUEST_RECEIVING) { |
| if (mbox->mbox_req.recv_len < mbox->mbox_req.msg.s.len) { |
| ret = 0; |
| } else { |
| mbox->state &= ~OCTEON_MBOX_STATE_REQUEST_RECEIVING; |
| mbox->state |= OCTEON_MBOX_STATE_REQUEST_RECEIVED; |
| ret = 1; |
| } |
| } else { |
| if (mbox->state & OCTEON_MBOX_STATE_RESPONSE_RECEIVING) { |
| if (mbox->mbox_resp.recv_len < |
| mbox->mbox_resp.msg.s.len) { |
| ret = 0; |
| } else { |
| mbox->state &= |
| ~OCTEON_MBOX_STATE_RESPONSE_RECEIVING; |
| mbox->state |= |
| OCTEON_MBOX_STATE_RESPONSE_RECEIVED; |
| ret = 1; |
| } |
| } else { |
| WARN_ON(1); |
| } |
| } |
| |
| writeq(OCTEON_PFVFACK, mbox->mbox_read_reg); |
| |
| spin_unlock(&mbox->lock); |
| |
| return ret; |
| } |
| |
| /** |
| * octeon_mbox_write: |
| * @oct: Pointer Octeon Device |
| * @mbox_cmd: Cmd to send to mailbox. |
| * |
| * Populates the queue specific mbox structure |
| * with cmd information. |
| * Write the cmd to mbox register |
| */ |
| int octeon_mbox_write(struct octeon_device *oct, |
| struct octeon_mbox_cmd *mbox_cmd) |
| { |
| struct octeon_mbox *mbox = oct->mbox[mbox_cmd->q_no]; |
| u32 count, i, ret = OCTEON_MBOX_STATUS_SUCCESS; |
| long timeout = LIO_MBOX_WRITE_WAIT_TIME; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&mbox->lock, flags); |
| |
| if ((mbox_cmd->msg.s.type == OCTEON_MBOX_RESPONSE) && |
| !(mbox->state & OCTEON_MBOX_STATE_REQUEST_RECEIVED)) { |
| spin_unlock_irqrestore(&mbox->lock, flags); |
| return OCTEON_MBOX_STATUS_FAILED; |
| } |
| |
| if ((mbox_cmd->msg.s.type == OCTEON_MBOX_REQUEST) && |
| !(mbox->state & OCTEON_MBOX_STATE_IDLE)) { |
| spin_unlock_irqrestore(&mbox->lock, flags); |
| return OCTEON_MBOX_STATUS_BUSY; |
| } |
| |
| if (mbox_cmd->msg.s.type == OCTEON_MBOX_REQUEST) { |
| memcpy(&mbox->mbox_resp, mbox_cmd, |
| sizeof(struct octeon_mbox_cmd)); |
| mbox->state = OCTEON_MBOX_STATE_RESPONSE_PENDING; |
| } |
| |
| spin_unlock_irqrestore(&mbox->lock, flags); |
| |
| count = 0; |
| |
| while (readq(mbox->mbox_write_reg) != OCTEON_PFVFSIG) { |
| schedule_timeout_uninterruptible(timeout); |
| if (count++ == LIO_MBOX_WRITE_WAIT_CNT) { |
| ret = OCTEON_MBOX_STATUS_FAILED; |
| break; |
| } |
| } |
| |
| if (ret == OCTEON_MBOX_STATUS_SUCCESS) { |
| writeq(mbox_cmd->msg.u64, mbox->mbox_write_reg); |
| for (i = 0; i < (u32)(mbox_cmd->msg.s.len - 1); i++) { |
| count = 0; |
| while (readq(mbox->mbox_write_reg) != |
| OCTEON_PFVFACK) { |
| schedule_timeout_uninterruptible(timeout); |
| if (count++ == LIO_MBOX_WRITE_WAIT_CNT) { |
| ret = OCTEON_MBOX_STATUS_FAILED; |
| break; |
| } |
| } |
| if (ret == OCTEON_MBOX_STATUS_SUCCESS) |
| writeq(mbox_cmd->data[i], mbox->mbox_write_reg); |
| else |
| break; |
| } |
| } |
| |
| spin_lock_irqsave(&mbox->lock, flags); |
| if (mbox_cmd->msg.s.type == OCTEON_MBOX_RESPONSE) { |
| mbox->state = OCTEON_MBOX_STATE_IDLE; |
| writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg); |
| } else { |
| if ((!mbox_cmd->msg.s.resp_needed) || |
| (ret == OCTEON_MBOX_STATUS_FAILED)) { |
| mbox->state &= ~OCTEON_MBOX_STATE_RESPONSE_PENDING; |
| if (!(mbox->state & |
| (OCTEON_MBOX_STATE_REQUEST_RECEIVING | |
| OCTEON_MBOX_STATE_REQUEST_RECEIVED))) |
| mbox->state = OCTEON_MBOX_STATE_IDLE; |
| } |
| } |
| spin_unlock_irqrestore(&mbox->lock, flags); |
| |
| return ret; |
| } |
| |
| static void get_vf_stats(struct octeon_device *oct, |
| struct oct_vf_stats *stats) |
| { |
| int i; |
| |
| for (i = 0; i < oct->num_iqs; i++) { |
| if (!oct->instr_queue[i]) |
| continue; |
| stats->tx_packets += oct->instr_queue[i]->stats.tx_done; |
| stats->tx_bytes += oct->instr_queue[i]->stats.tx_tot_bytes; |
| } |
| |
| for (i = 0; i < oct->num_oqs; i++) { |
| if (!oct->droq[i]) |
| continue; |
| stats->rx_packets += oct->droq[i]->stats.rx_pkts_received; |
| stats->rx_bytes += oct->droq[i]->stats.rx_bytes_received; |
| } |
| } |
| |
| /** |
| * octeon_mbox_process_cmd: |
| * @mbox: Pointer mailbox |
| * @mbox_cmd: Pointer to command received |
| * |
| * Process the cmd received in mbox |
| */ |
| static int octeon_mbox_process_cmd(struct octeon_mbox *mbox, |
| struct octeon_mbox_cmd *mbox_cmd) |
| { |
| struct octeon_device *oct = mbox->oct_dev; |
| |
| switch (mbox_cmd->msg.s.cmd) { |
| case OCTEON_VF_ACTIVE: |
| dev_dbg(&oct->pci_dev->dev, "got vfactive sending data back\n"); |
| mbox_cmd->msg.s.type = OCTEON_MBOX_RESPONSE; |
| mbox_cmd->msg.s.resp_needed = 1; |
| mbox_cmd->msg.s.len = 2; |
| mbox_cmd->data[0] = 0; /* VF version is in mbox_cmd->data[0] */ |
| ((struct lio_version *)&mbox_cmd->data[0])->major = |
| LIQUIDIO_BASE_MAJOR_VERSION; |
| ((struct lio_version *)&mbox_cmd->data[0])->minor = |
| LIQUIDIO_BASE_MINOR_VERSION; |
| ((struct lio_version *)&mbox_cmd->data[0])->micro = |
| LIQUIDIO_BASE_MICRO_VERSION; |
| memcpy(mbox_cmd->msg.s.params, (uint8_t *)&oct->pfvf_hsword, 6); |
| /* Sending core cofig info to the corresponding active VF.*/ |
| octeon_mbox_write(oct, mbox_cmd); |
| break; |
| |
| case OCTEON_VF_FLR_REQUEST: |
| dev_info(&oct->pci_dev->dev, |
| "got a request for FLR from VF that owns DPI ring %u\n", |
| mbox->q_no); |
| pcie_flr(oct->sriov_info.dpiring_to_vfpcidev_lut[mbox->q_no]); |
| break; |
| |
| case OCTEON_PF_CHANGED_VF_MACADDR: |
| if (OCTEON_CN23XX_VF(oct)) |
| octeon_pf_changed_vf_macaddr(oct, |
| mbox_cmd->msg.s.params); |
| break; |
| |
| case OCTEON_GET_VF_STATS: |
| dev_dbg(&oct->pci_dev->dev, "Got VF stats request. Sending data back\n"); |
| mbox_cmd->msg.s.type = OCTEON_MBOX_RESPONSE; |
| mbox_cmd->msg.s.resp_needed = 1; |
| mbox_cmd->msg.s.len = 1 + |
| sizeof(struct oct_vf_stats) / sizeof(u64); |
| get_vf_stats(oct, (struct oct_vf_stats *)mbox_cmd->data); |
| octeon_mbox_write(oct, mbox_cmd); |
| break; |
| default: |
| break; |
| } |
| return 0; |
| } |
| |
| /** |
| *octeon_mbox_process_message: |
| * |
| * Process the received mbox message. |
| */ |
| int octeon_mbox_process_message(struct octeon_mbox *mbox) |
| { |
| struct octeon_mbox_cmd mbox_cmd; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&mbox->lock, flags); |
| |
| if (mbox->state & OCTEON_MBOX_STATE_ERROR) { |
| if (mbox->state & (OCTEON_MBOX_STATE_RESPONSE_PENDING | |
| OCTEON_MBOX_STATE_RESPONSE_RECEIVING)) { |
| memcpy(&mbox_cmd, &mbox->mbox_resp, |
| sizeof(struct octeon_mbox_cmd)); |
| mbox->state = OCTEON_MBOX_STATE_IDLE; |
| writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg); |
| spin_unlock_irqrestore(&mbox->lock, flags); |
| mbox_cmd.recv_status = 1; |
| if (mbox_cmd.fn) |
| mbox_cmd.fn(mbox->oct_dev, &mbox_cmd, |
| mbox_cmd.fn_arg); |
| return 0; |
| } |
| |
| mbox->state = OCTEON_MBOX_STATE_IDLE; |
| writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg); |
| spin_unlock_irqrestore(&mbox->lock, flags); |
| return 0; |
| } |
| |
| if (mbox->state & OCTEON_MBOX_STATE_RESPONSE_RECEIVED) { |
| memcpy(&mbox_cmd, &mbox->mbox_resp, |
| sizeof(struct octeon_mbox_cmd)); |
| mbox->state = OCTEON_MBOX_STATE_IDLE; |
| writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg); |
| spin_unlock_irqrestore(&mbox->lock, flags); |
| mbox_cmd.recv_status = 0; |
| if (mbox_cmd.fn) |
| mbox_cmd.fn(mbox->oct_dev, &mbox_cmd, mbox_cmd.fn_arg); |
| return 0; |
| } |
| |
| if (mbox->state & OCTEON_MBOX_STATE_REQUEST_RECEIVED) { |
| memcpy(&mbox_cmd, &mbox->mbox_req, |
| sizeof(struct octeon_mbox_cmd)); |
| if (!mbox_cmd.msg.s.resp_needed) { |
| mbox->state &= ~OCTEON_MBOX_STATE_REQUEST_RECEIVED; |
| if (!(mbox->state & |
| OCTEON_MBOX_STATE_RESPONSE_PENDING)) |
| mbox->state = OCTEON_MBOX_STATE_IDLE; |
| writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg); |
| } |
| |
| spin_unlock_irqrestore(&mbox->lock, flags); |
| octeon_mbox_process_cmd(mbox, &mbox_cmd); |
| return 0; |
| } |
| |
| spin_unlock_irqrestore(&mbox->lock, flags); |
| WARN_ON(1); |
| |
| return 0; |
| } |
| |
| int octeon_mbox_cancel(struct octeon_device *oct, int q_no) |
| { |
| struct octeon_mbox *mbox = oct->mbox[q_no]; |
| struct octeon_mbox_cmd *mbox_cmd; |
| unsigned long flags = 0; |
| |
| spin_lock_irqsave(&mbox->lock, flags); |
| mbox_cmd = &mbox->mbox_resp; |
| |
| if (!(mbox->state & OCTEON_MBOX_STATE_RESPONSE_PENDING)) { |
| spin_unlock_irqrestore(&mbox->lock, flags); |
| return 1; |
| } |
| |
| mbox->state = OCTEON_MBOX_STATE_IDLE; |
| memset(mbox_cmd, 0, sizeof(*mbox_cmd)); |
| writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg); |
| spin_unlock_irqrestore(&mbox->lock, flags); |
| |
| return 0; |
| } |