123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (C) 2018 Marvell International Ltd.
- */
- #include <dm.h>
- #include <malloc.h>
- #include <misc.h>
- #include <net.h>
- #include <pci.h>
- #include <pci_ids.h>
- #include <phy.h>
- #include <asm/io.h>
- #include <linux/delay.h>
- #include "nic_reg.h"
- #include "nic.h"
- #include "nicvf_queues.h"
- /* Register read/write APIs */
- void nicvf_reg_write(struct nicvf *nic, u64 offset, u64 val)
- {
- writeq(val, nic->reg_base + offset);
- }
- u64 nicvf_reg_read(struct nicvf *nic, u64 offset)
- {
- return readq(nic->reg_base + offset);
- }
- void nicvf_queue_reg_write(struct nicvf *nic, u64 offset,
- u64 qidx, u64 val)
- {
- void *addr = nic->reg_base + offset;
- writeq(val, (void *)(addr + (qidx << NIC_Q_NUM_SHIFT)));
- }
- u64 nicvf_queue_reg_read(struct nicvf *nic, u64 offset, u64 qidx)
- {
- void *addr = nic->reg_base + offset;
- return readq((void *)(addr + (qidx << NIC_Q_NUM_SHIFT)));
- }
- static void nicvf_handle_mbx_intr(struct nicvf *nic);
- /* VF -> PF mailbox communication */
- static void nicvf_write_to_mbx(struct nicvf *nic, union nic_mbx *mbx)
- {
- u64 *msg = (u64 *)mbx;
- nicvf_reg_write(nic, NIC_VF_PF_MAILBOX_0_1 + 0, msg[0]);
- nicvf_reg_write(nic, NIC_VF_PF_MAILBOX_0_1 + 8, msg[1]);
- }
- int nicvf_send_msg_to_pf(struct nicvf *nic, union nic_mbx *mbx)
- {
- int timeout = NIC_PF_VF_MBX_TIMEOUT;
- int sleep = 10;
- nic->pf_acked = false;
- nic->pf_nacked = false;
- nicvf_write_to_mbx(nic, mbx);
- nic_handle_mbx_intr(nic->nicpf, nic->vf_id);
- /* Wait for previous message to be acked, timeout 2sec */
- while (!nic->pf_acked) {
- if (nic->pf_nacked)
- return -1;
- mdelay(sleep);
- nicvf_handle_mbx_intr(nic);
- if (nic->pf_acked)
- break;
- timeout -= sleep;
- if (!timeout) {
- printf("PF didn't ack to mbox msg %d from VF%d\n",
- (mbx->msg.msg & 0xFF), nic->vf_id);
- return -1;
- }
- }
- return 0;
- }
- /* Checks if VF is able to comminicate with PF
- * and also gets the VNIC number this VF is associated to.
- */
- static int nicvf_check_pf_ready(struct nicvf *nic)
- {
- union nic_mbx mbx = {};
- mbx.msg.msg = NIC_MBOX_MSG_READY;
- if (nicvf_send_msg_to_pf(nic, &mbx)) {
- printf("PF didn't respond to READY msg\n");
- return 0;
- }
- return 1;
- }
- static void nicvf_handle_mbx_intr(struct nicvf *nic)
- {
- union nic_mbx mbx = {};
- struct eth_pdata *pdata = dev_get_plat(nic->dev);
- u64 *mbx_data;
- u64 mbx_addr;
- int i;
- mbx_addr = NIC_VF_PF_MAILBOX_0_1;
- mbx_data = (u64 *)&mbx;
- for (i = 0; i < NIC_PF_VF_MAILBOX_SIZE; i++) {
- *mbx_data = nicvf_reg_read(nic, mbx_addr);
- mbx_data++;
- mbx_addr += sizeof(u64);
- }
- debug("Mbox message: msg: 0x%x\n", mbx.msg.msg);
- switch (mbx.msg.msg) {
- case NIC_MBOX_MSG_READY:
- nic->pf_acked = true;
- nic->vf_id = mbx.nic_cfg.vf_id & 0x7F;
- nic->tns_mode = mbx.nic_cfg.tns_mode & 0x7F;
- nic->node = mbx.nic_cfg.node_id;
- if (!nic->set_mac_pending)
- memcpy(pdata->enetaddr,
- mbx.nic_cfg.mac_addr, 6);
- nic->loopback_supported = mbx.nic_cfg.loopback_supported;
- nic->link_up = false;
- nic->duplex = 0;
- nic->speed = 0;
- break;
- case NIC_MBOX_MSG_ACK:
- nic->pf_acked = true;
- break;
- case NIC_MBOX_MSG_NACK:
- nic->pf_nacked = true;
- break;
- case NIC_MBOX_MSG_BGX_LINK_CHANGE:
- nic->pf_acked = true;
- nic->link_up = mbx.link_status.link_up;
- nic->duplex = mbx.link_status.duplex;
- nic->speed = mbx.link_status.speed;
- if (nic->link_up) {
- printf("%s: Link is Up %d Mbps %s\n",
- nic->dev->name, nic->speed,
- nic->duplex == 1 ?
- "Full duplex" : "Half duplex");
- } else {
- printf("%s: Link is Down\n", nic->dev->name);
- }
- break;
- default:
- printf("Invalid message from PF, msg 0x%x\n", mbx.msg.msg);
- break;
- }
- nicvf_clear_intr(nic, NICVF_INTR_MBOX, 0);
- }
- static int nicvf_hw_set_mac_addr(struct nicvf *nic, struct udevice *dev)
- {
- union nic_mbx mbx = {};
- struct eth_pdata *pdata = dev_get_plat(dev);
- mbx.mac.msg = NIC_MBOX_MSG_SET_MAC;
- mbx.mac.vf_id = nic->vf_id;
- memcpy(mbx.mac.mac_addr, pdata->enetaddr, 6);
- return nicvf_send_msg_to_pf(nic, &mbx);
- }
- static void nicvf_config_cpi(struct nicvf *nic)
- {
- union nic_mbx mbx = {};
- mbx.cpi_cfg.msg = NIC_MBOX_MSG_CPI_CFG;
- mbx.cpi_cfg.vf_id = nic->vf_id;
- mbx.cpi_cfg.cpi_alg = nic->cpi_alg;
- mbx.cpi_cfg.rq_cnt = nic->qs->rq_cnt;
- nicvf_send_msg_to_pf(nic, &mbx);
- }
- static int nicvf_init_resources(struct nicvf *nic)
- {
- int err;
- nic->num_qs = 1;
- /* Enable Qset */
- nicvf_qset_config(nic, true);
- /* Initialize queues and HW for data transfer */
- err = nicvf_config_data_transfer(nic, true);
- if (err) {
- printf("Failed to alloc/config VF's QSet resources\n");
- return err;
- }
- return 0;
- }
- static void nicvf_snd_pkt_handler(struct nicvf *nic,
- struct cmp_queue *cq,
- void *cq_desc, int cqe_type)
- {
- struct cqe_send_t *cqe_tx;
- struct snd_queue *sq;
- struct sq_hdr_subdesc *hdr;
- cqe_tx = (struct cqe_send_t *)cq_desc;
- sq = &nic->qs->sq[cqe_tx->sq_idx];
- hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, cqe_tx->sqe_ptr);
- if (hdr->subdesc_type != SQ_DESC_TYPE_HEADER)
- return;
- nicvf_check_cqe_tx_errs(nic, cq, cq_desc);
- nicvf_put_sq_desc(sq, hdr->subdesc_cnt + 1);
- }
- static int nicvf_rcv_pkt_handler(struct nicvf *nic,
- struct cmp_queue *cq, void *cq_desc,
- void **ppkt, int cqe_type)
- {
- void *pkt;
- size_t pkt_len;
- struct cqe_rx_t *cqe_rx = (struct cqe_rx_t *)cq_desc;
- int err = 0;
- /* Check for errors */
- err = nicvf_check_cqe_rx_errs(nic, cq, cq_desc);
- if (err && !cqe_rx->rb_cnt)
- return -1;
- pkt = nicvf_get_rcv_pkt(nic, cq_desc, &pkt_len);
- if (!pkt) {
- debug("Packet not received\n");
- return -1;
- }
- if (pkt)
- *ppkt = pkt;
- return pkt_len;
- }
- int nicvf_cq_handler(struct nicvf *nic, void **ppkt, int *pkt_len)
- {
- int cq_qnum = 0;
- int processed_sq_cqe = 0;
- int processed_rq_cqe = 0;
- int processed_cqe = 0;
- unsigned long cqe_count, cqe_head;
- struct queue_set *qs = nic->qs;
- struct cmp_queue *cq = &qs->cq[cq_qnum];
- struct cqe_rx_t *cq_desc;
- /* Get num of valid CQ entries expect next one to be SQ completion */
- cqe_count = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_STATUS, cq_qnum);
- cqe_count &= 0xFFFF;
- if (!cqe_count)
- return 0;
- /* Get head of the valid CQ entries */
- cqe_head = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_HEAD, cq_qnum);
- cqe_head >>= 9;
- cqe_head &= 0xFFFF;
- if (cqe_count) {
- /* Get the CQ descriptor */
- cq_desc = (struct cqe_rx_t *)GET_CQ_DESC(cq, cqe_head);
- cqe_head++;
- cqe_head &= (cq->dmem.q_len - 1);
- /* Initiate prefetch for next descriptor */
- prefetch((struct cqe_rx_t *)GET_CQ_DESC(cq, cqe_head));
- switch (cq_desc->cqe_type) {
- case CQE_TYPE_RX:
- debug("%s: Got Rx CQE\n", nic->dev->name);
- *pkt_len = nicvf_rcv_pkt_handler(nic, cq, cq_desc,
- ppkt, CQE_TYPE_RX);
- processed_rq_cqe++;
- break;
- case CQE_TYPE_SEND:
- debug("%s: Got Tx CQE\n", nic->dev->name);
- nicvf_snd_pkt_handler(nic, cq, cq_desc, CQE_TYPE_SEND);
- processed_sq_cqe++;
- break;
- default:
- debug("%s: Got CQ type %u\n", nic->dev->name,
- cq_desc->cqe_type);
- break;
- }
- processed_cqe++;
- }
- /* Dequeue CQE */
- nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_DOOR,
- cq_qnum, processed_cqe);
- asm volatile ("dsb sy");
- return (processed_sq_cqe | processed_rq_cqe);
- }
- /* Qset error interrupt handler
- *
- * As of now only CQ errors are handled
- */
- void nicvf_handle_qs_err(struct nicvf *nic)
- {
- struct queue_set *qs = nic->qs;
- int qidx;
- u64 status;
- /* Check if it is CQ err */
- for (qidx = 0; qidx < qs->cq_cnt; qidx++) {
- status = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_STATUS,
- qidx);
- if (!(status & CQ_ERR_MASK))
- continue;
- /* Process already queued CQEs and reconfig CQ */
- nicvf_sq_disable(nic, qidx);
- nicvf_cmp_queue_config(nic, qs, qidx, true);
- nicvf_sq_free_used_descs(nic->dev, &qs->sq[qidx], qidx);
- nicvf_sq_enable(nic, &qs->sq[qidx], qidx);
- }
- }
- static int nicvf_free_pkt(struct udevice *dev, uchar *pkt, int pkt_len)
- {
- struct nicvf *nic = dev_get_priv(dev);
- if (pkt && pkt_len)
- free(pkt);
- nicvf_refill_rbdr(nic);
- return 0;
- }
- static int nicvf_xmit(struct udevice *dev, void *pkt, int pkt_len)
- {
- struct nicvf *nic = dev_get_priv(dev);
- int ret = 0;
- int rcv_len = 0;
- unsigned int timeout = 5000;
- void *rpkt = NULL;
- if (!nicvf_sq_append_pkt(nic, pkt, pkt_len)) {
- printf("VF%d: TX ring full\n", nic->vf_id);
- return -1;
- }
- /* check and update CQ for pkt sent */
- while (!ret && timeout--) {
- ret = nicvf_cq_handler(nic, &rpkt, &rcv_len);
- if (!ret) {
- debug("%s: %d, Not sent\n", __func__, __LINE__);
- udelay(10);
- }
- }
- return 0;
- }
- static int nicvf_recv(struct udevice *dev, int flags, uchar **packetp)
- {
- struct nicvf *nic = dev_get_priv(dev);
- void *pkt;
- int pkt_len = 0;
- #ifdef DEBUG
- u8 *dpkt;
- int i, j;
- #endif
- nicvf_cq_handler(nic, &pkt, &pkt_len);
- if (pkt_len) {
- #ifdef DEBUG
- dpkt = pkt;
- printf("RX packet contents:\n");
- for (i = 0; i < 8; i++) {
- puts("\t");
- for (j = 0; j < 10; j++)
- printf("%02x ", dpkt[i * 10 + j]);
- puts("\n");
- }
- #endif
- *packetp = pkt;
- }
- return pkt_len;
- }
- void nicvf_stop(struct udevice *dev)
- {
- struct nicvf *nic = dev_get_priv(dev);
- if (!nic->open)
- return;
- /* Free resources */
- nicvf_config_data_transfer(nic, false);
- /* Disable HW Qset */
- nicvf_qset_config(nic, false);
- nic->open = false;
- }
- int nicvf_open(struct udevice *dev)
- {
- int err;
- struct nicvf *nic = dev_get_priv(dev);
- nicvf_hw_set_mac_addr(nic, dev);
- /* Configure CPI alorithm */
- nic->cpi_alg = CPI_ALG_NONE;
- nicvf_config_cpi(nic);
- /* Initialize the queues */
- err = nicvf_init_resources(nic);
- if (err)
- return -1;
- if (!nicvf_check_pf_ready(nic))
- return -1;
- nic->open = true;
- /* Make sure queue initialization is written */
- asm volatile("dsb sy");
- return 0;
- }
- int nicvf_write_hwaddr(struct udevice *dev)
- {
- unsigned char ethaddr[ARP_HLEN];
- struct eth_pdata *pdata = dev_get_plat(dev);
- struct nicvf *nic = dev_get_priv(dev);
- /* If lower level firmware fails to set proper MAC
- * u-boot framework updates MAC to random address.
- * Use this hook to update mac address in environment.
- */
- if (!eth_env_get_enetaddr_by_index("eth", dev_seq(dev), ethaddr)) {
- eth_env_set_enetaddr_by_index("eth", dev_seq(dev),
- pdata->enetaddr);
- debug("%s: pMAC %pM\n", __func__, pdata->enetaddr);
- }
- eth_env_get_enetaddr_by_index("eth", dev_seq(dev), ethaddr);
- if (memcmp(ethaddr, pdata->enetaddr, ARP_HLEN)) {
- debug("%s: pMAC %pM\n", __func__, pdata->enetaddr);
- nicvf_hw_set_mac_addr(nic, dev);
- }
- return 0;
- }
- static void nicvf_probe_mdio_devices(void)
- {
- struct udevice *pdev;
- int err;
- static int probed;
- if (probed)
- return;
- err = dm_pci_find_device(PCI_VENDOR_ID_CAVIUM,
- PCI_DEVICE_ID_CAVIUM_SMI, 0,
- &pdev);
- if (err)
- debug("%s couldn't find SMI device\n", __func__);
- probed = 1;
- }
- int nicvf_initialize(struct udevice *dev)
- {
- struct nicvf *nicvf = dev_get_priv(dev);
- struct eth_pdata *pdata = dev_get_plat(dev);
- int ret = 0, bgx, lmac;
- char name[16];
- unsigned char ethaddr[ARP_HLEN];
- struct udevice *pfdev;
- struct nicpf *pf;
- static int vfid;
- if (dm_pci_find_device(PCI_VENDOR_ID_CAVIUM,
- PCI_DEVICE_ID_CAVIUM_NIC, 0, &pfdev)) {
- printf("%s NIC PF device not found..VF probe failed\n",
- __func__);
- return -1;
- }
- pf = dev_get_priv(pfdev);
- nicvf->vf_id = vfid++;
- nicvf->dev = dev;
- nicvf->nicpf = pf;
- nicvf_probe_mdio_devices();
- /* Enable TSO support */
- nicvf->hw_tso = true;
- nicvf->reg_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
- PCI_REGION_MEM);
- debug("nicvf->reg_base: %p\n", nicvf->reg_base);
- if (!nicvf->reg_base) {
- printf("Cannot map config register space, aborting\n");
- ret = -1;
- goto fail;
- }
- ret = nicvf_set_qset_resources(nicvf);
- if (ret)
- return -1;
- sprintf(name, "vnic%u", nicvf->vf_id);
- debug("%s name %s\n", __func__, name);
- device_set_name(dev, name);
- bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(pf->vf_lmac_map[nicvf->vf_id]);
- lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(pf->vf_lmac_map[nicvf->vf_id]);
- debug("%s VF %d BGX %d LMAC %d\n", __func__, nicvf->vf_id, bgx, lmac);
- debug("%s PF %p pfdev %p VF %p vfdev %p vf->pdata %p\n",
- __func__, nicvf->nicpf, nicvf->nicpf->udev, nicvf, nicvf->dev,
- pdata);
- fdt_board_get_ethaddr(bgx, lmac, ethaddr);
- debug("%s bgx %d lmac %d ethaddr %pM\n", __func__, bgx, lmac, ethaddr);
- if (is_valid_ethaddr(ethaddr)) {
- memcpy(pdata->enetaddr, ethaddr, ARP_HLEN);
- eth_env_set_enetaddr_by_index("eth", dev_seq(dev), ethaddr);
- }
- debug("%s enetaddr %pM ethaddr %pM\n", __func__,
- pdata->enetaddr, ethaddr);
- fail:
- return ret;
- }
- int octeontx_vnic_probe(struct udevice *dev)
- {
- return nicvf_initialize(dev);
- }
- static const struct eth_ops octeontx_vnic_ops = {
- .start = nicvf_open,
- .stop = nicvf_stop,
- .send = nicvf_xmit,
- .recv = nicvf_recv,
- .free_pkt = nicvf_free_pkt,
- .write_hwaddr = nicvf_write_hwaddr,
- };
- U_BOOT_DRIVER(octeontx_vnic) = {
- .name = "vnic",
- .id = UCLASS_ETH,
- .probe = octeontx_vnic_probe,
- .ops = &octeontx_vnic_ops,
- .priv_auto = sizeof(struct nicvf),
- .plat_auto = sizeof(struct eth_pdata),
- };
- static struct pci_device_id octeontx_vnic_supported[] = {
- { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_NICVF) },
- { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_NICVF_1) },
- {}
- };
- U_BOOT_PCI_DEVICE(octeontx_vnic, octeontx_vnic_supported);
|