// SPDX-License-Identifier: GPL-2.0/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */ #include <linux/kernel.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/io.h>#include <linux/slab.h>#include <linux/etherdevice.h>#include "ionic.h"#include "ionic_dev.h"#include "ionic_lif.h" static void ionic_watchdog_cb(struct timer_list *t){ struct ionic *ionic = timer_container_of(ionic, t, watchdog_timer); struct ionic_lif *lif = ionic->lif; struct ionic_deferred_work *work; int hb; mod_timer(&ionic->watchdog_timer, round_jiffies(jiffies + ionic->watchdog_period)); if (!lif) return; hb = ionic_heartbeat_check(ionic); dev_dbg(ionic->dev, "%s: hb %d running %d UP %d\n", __func__, hb, netif_running(lif->netdev), test_bit(IONIC_LIF_F_UP, lif->state)); if (hb >= 0 && !test_bit(IONIC_LIF_F_FW_RESET, lif->state)) ionic_link_status_check_request(lif, CAN_NOT_SLEEP); if (test_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state) && !test_bit(IONIC_LIF_F_FW_RESET, lif->state)) { work = kzalloc_obj(*work, GFP_ATOMIC); if (!work) { netdev_err(lif->netdev, "rxmode change dropped\n"); return; } work->type = IONIC_DW_TYPE_RX_MODE; netdev_dbg(lif->netdev, "deferred: rx_mode\n"); ionic_lif_deferred_enqueue(lif, work); }} static void ionic_napi_schedule_do_softirq(struct napi_struct *napi){ local_bh_disable(); napi_schedule(napi); local_bh_enable();} void ionic_doorbell_napi_work(struct work_struct *work){ struct ionic_qcq *qcq = container_of(work, struct ionic_qcq, doorbell_napi_work); unsigned long now, then, dif; now = READ_ONCE(jiffies); then = qcq->q.dbell_jiffies; dif = now - then; if (dif > qcq->q.dbell_deadline) ionic_napi_schedule_do_softirq(&qcq->napi);} static int ionic_get_preferred_cpu(struct ionic *ionic, struct ionic_intr_info *intr){ int cpu; cpu = cpumask_first_and(*intr->affinity_mask, cpu_online_mask); if (cpu >= nr_cpu_ids) cpu = cpumask_local_spread(0, dev_to_node(ionic->dev)); return cpu;} static void ionic_queue_dbell_napi_work(struct ionic *ionic, struct ionic_qcq *qcq){ int cpu; if (!(qcq->flags & IONIC_QCQ_F_INTR)) return; cpu = ionic_get_preferred_cpu(ionic, &qcq->intr); queue_work_on(cpu, ionic->wq, &qcq->doorbell_napi_work);} static void ionic_doorbell_check_dwork(struct work_struct *work){ struct ionic *ionic = container_of(work, struct ionic, doorbell_check_dwork.work); struct ionic_lif *lif = ionic->lif; mutex_lock(&lif->queue_lock); if (test_bit(IONIC_LIF_F_FW_STOPPING, lif->state) || test_bit(IONIC_LIF_F_FW_RESET, lif->state)) { mutex_unlock(&lif->queue_lock); return; } ionic_napi_schedule_do_softirq(&lif->adminqcq->napi); if (test_bit(IONIC_LIF_F_UP, lif->state)) { int i; for (i = 0; i < lif->nxqs; i++) { ionic_queue_dbell_napi_work(ionic, lif->txqcqs[i]); ionic_queue_dbell_napi_work(ionic, lif->rxqcqs[i]); } if (lif->hwstamp_txq && lif->hwstamp_txq->flags & IONIC_QCQ_F_INTR) ionic_napi_schedule_do_softirq(&lif->hwstamp_txq->napi); if (lif->hwstamp_rxq && lif->hwstamp_rxq->flags & IONIC_QCQ_F_INTR) ionic_napi_schedule_do_softirq(&lif->hwstamp_rxq->napi); } mutex_unlock(&lif->queue_lock); ionic_queue_doorbell_check(ionic, IONIC_NAPI_DEADLINE);} bool ionic_doorbell_wa(struct ionic *ionic){ u8 asic_type = ionic->idev.dev_info.asic_type; return !asic_type || asic_type == IONIC_ASIC_TYPE_ELBA;} static int ionic_watchdog_init(struct ionic *ionic){ struct ionic_dev *idev = &ionic->idev; timer_setup(&ionic->watchdog_timer, ionic_watchdog_cb, 0); ionic->watchdog_period = IONIC_WATCHDOG_SECS * HZ; /* set times to ensure the first check will proceed */ atomic_long_set(&idev->last_check_time, jiffies - 2 * HZ); idev->last_hb_time = jiffies - 2 * ionic->watchdog_period; /* init as ready, so no transition if the first check succeeds */ idev->last_fw_hb = 0; idev->fw_hb_ready = true; idev->fw_status_ready = true; idev->fw_generation = IONIC_FW_STS_F_GENERATION & ioread8(&idev->dev_info_regs->fw_status); ionic->wq = alloc_workqueue("%s-wq", WQ_UNBOUND, 0, dev_name(ionic->dev)); if (!ionic->wq) { dev_err(ionic->dev, "alloc_workqueue failed"); return -ENOMEM; } if (ionic_doorbell_wa(ionic)) INIT_DELAYED_WORK(&ionic->doorbell_check_dwork, ionic_doorbell_check_dwork); return 0;} void ionic_queue_doorbell_check(struct ionic *ionic, int delay){ int cpu; if (!ionic->lif->doorbell_wa) return; cpu = ionic_get_preferred_cpu(ionic, &ionic->lif->adminqcq->intr); queue_delayed_work_on(cpu, ionic->wq, &ionic->doorbell_check_dwork, delay);} void ionic_init_devinfo(struct ionic *ionic){ struct ionic_dev *idev = &ionic->idev; idev->dev_info.asic_type = ioread8(&idev->dev_info_regs->asic_type); idev->dev_info.asic_rev = ioread8(&idev->dev_info_regs->asic_rev); memcpy_fromio(idev->dev_info.fw_version, idev->dev_info_regs->fw_version, IONIC_DEVINFO_FWVERS_BUFLEN); memcpy_fromio(idev->dev_info.serial_num, idev->dev_info_regs->serial_num, IONIC_DEVINFO_SERIAL_BUFLEN); idev->dev_info.fw_version[IONIC_DEVINFO_FWVERS_BUFLEN] = 0; idev->dev_info.serial_num[IONIC_DEVINFO_SERIAL_BUFLEN] = 0; dev_dbg(ionic->dev, "fw_version %s\n", idev->dev_info.fw_version);} static void ionic_map_disc_cmb(struct ionic *ionic){ struct ionic_identity *ident = &ionic->ident; u32 length_reg0, length, offset, num_regions; struct ionic_dev_bar *bar = ionic->bars; struct ionic_dev *idev = &ionic->idev; struct device *dev = ionic->dev; int err, sz, i; u64 end; mutex_lock(&ionic->dev_cmd_lock); ionic_dev_cmd_discover_cmb(idev); err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); if (!err) { sz = min(sizeof(ident->cmb_layout), sizeof(idev->dev_cmd_regs->data)); memcpy_fromio(&ident->cmb_layout, &idev->dev_cmd_regs->data, sz); } mutex_unlock(&ionic->dev_cmd_lock); if (err) { dev_warn(dev, "Cannot discover CMB layout, disabling CMB\n"); return; } bar += 2; num_regions = le32_to_cpu(ident->cmb_layout.num_regions); if (!num_regions || num_regions > IONIC_MAX_CMB_REGIONS) { dev_warn(dev, "Invalid number of CMB entries (%d)\n", num_regions); return; } dev_dbg(dev, "ionic_cmb_layout_identity num_regions %d flags %x:\n", num_regions, ident->cmb_layout.flags); for (i = 0; i < num_regions; i++) { offset = le32_to_cpu(ident->cmb_layout.region[i].offset); length = le32_to_cpu(ident->cmb_layout.region[i].length); end = offset + length; dev_dbg(dev, "CMB entry %d: bar_num %u cmb_type %u offset %x length %u\n", i, ident->cmb_layout.region[i].bar_num, ident->cmb_layout.region[i].cmb_type, offset, length); if (end > (bar->len >> IONIC_CMB_SHIFT_64K)) { dev_warn(dev, "Out of bounds CMB region %d offset %x length %u\n", i, offset, length); return; } } /* if first entry matches PCI config, expdb is not supported */ if (ident->cmb_layout.region[0].bar_num == bar->res_index && le32_to_cpu(ident->cmb_layout.region[0].length) == bar->len && !ident->cmb_layout.region[0].offset) { dev_warn(dev, "No CMB mapping discovered\n"); return; } /* process first entry for regular mapping */ length_reg0 = le32_to_cpu(ident->cmb_layout.region[0].length); if (!length_reg0) { dev_warn(dev, "region len = 0. No CMB mapping discovered\n"); return; } /* Verify first entry size matches expected 8MB size (in 64KB pages) */ if (length_reg0 != IONIC_BAR2_CMB_ENTRY_SIZE >> IONIC_CMB_SHIFT_64K) { dev_warn(dev, "Unexpected CMB size in entry 0: %u pages\n", length_reg0); return; } sz = BITS_TO_LONGS((length_reg0 << IONIC_CMB_SHIFT_64K) / PAGE_SIZE) * sizeof(long); idev->cmb_inuse = kzalloc(sz, GFP_KERNEL); if (!idev->cmb_inuse) { dev_warn(dev, "No memory for CMB, disabling\n"); idev->phy_cmb_pages = 0; idev->phy_cmb_expdb64_pages = 0; idev->phy_cmb_expdb128_pages = 0; idev->phy_cmb_expdb256_pages = 0; idev->phy_cmb_expdb512_pages = 0; idev->cmb_npages = 0; return; } for (i = 0; i < num_regions; i++) { /* check this region matches first region length as to * ease implementation */ if (le32_to_cpu(ident->cmb_layout.region[i].length) != length_reg0) continue; offset = le32_to_cpu(ident->cmb_layout.region[i].offset); switch (ident->cmb_layout.region[i].cmb_type) { case IONIC_CMB_TYPE_DEVMEM: idev->phy_cmb_pages = bar->bus_addr + offset; idev->cmb_npages = (length_reg0 << IONIC_CMB_SHIFT_64K) / PAGE_SIZE; dev_dbg(dev, "regular cmb mapping: bar->bus_addr %pa region[%d].length %u\n", &bar->bus_addr, i, length); dev_dbg(dev, "idev->phy_cmb_pages %pad, idev->cmb_npages %u\n", &idev->phy_cmb_pages, idev->cmb_npages); break; case IONIC_CMB_TYPE_EXPDB64: idev->phy_cmb_expdb64_pages = bar->bus_addr + (offset << IONIC_CMB_SHIFT_64K); dev_dbg(dev, "idev->phy_cmb_expdb64_pages %pad\n", &idev->phy_cmb_expdb64_pages); break; case IONIC_CMB_TYPE_EXPDB128: idev->phy_cmb_expdb128_pages = bar->bus_addr + (offset << IONIC_CMB_SHIFT_64K); dev_dbg(dev, "idev->phy_cmb_expdb128_pages %pad\n", &idev->phy_cmb_expdb128_pages); break; case IONIC_CMB_TYPE_EXPDB256: idev->phy_cmb_expdb256_pages = bar->bus_addr + (offset << IONIC_CMB_SHIFT_64K); dev_dbg(dev, "idev->phy_cmb_expdb256_pages %pad\n", &idev->phy_cmb_expdb256_pages); break; case IONIC_CMB_TYPE_EXPDB512: idev->phy_cmb_expdb512_pages = bar->bus_addr + (offset << IONIC_CMB_SHIFT_64K); dev_dbg(dev, "idev->phy_cmb_expdb512_pages %pad\n", &idev->phy_cmb_expdb512_pages); break; default: dev_warn(dev, "[%d] Invalid cmb_type (%d)\n", i, ident->cmb_layout.region[i].cmb_type); break; } }} static void ionic_map_classic_cmb(struct ionic *ionic){ struct ionic_dev_bar *bar = ionic->bars; struct ionic_dev *idev = &ionic->idev; struct device *dev = ionic->dev; int sz; bar += 2; /* classic CMB mapping */ idev->phy_cmb_pages = bar->bus_addr; idev->cmb_npages = bar->len / PAGE_SIZE; dev_dbg(dev, "classic cmb mapping: bar->bus_addr %pa bar->len %lu\n", &bar->bus_addr, bar->len); dev_dbg(dev, "idev->phy_cmb_pages %pad, idev->cmb_npages %u\n", &idev->phy_cmb_pages, idev->cmb_npages); sz = BITS_TO_LONGS(idev->cmb_npages) * sizeof(long); idev->cmb_inuse = kzalloc(sz, GFP_KERNEL); if (!idev->cmb_inuse) { idev->phy_cmb_pages = 0; idev->cmb_npages = 0; }} void ionic_map_cmb(struct ionic *ionic){ struct pci_dev *pdev = ionic->pdev; struct device *dev = ionic->dev; if (!(pci_resource_flags(pdev, 4) & IORESOURCE_MEM)) { dev_dbg(dev, "No CMB, disabling\n"); return; } if (ionic->ident.dev.capabilities & cpu_to_le64(IONIC_DEV_CAP_DISC_CMB)) ionic_map_disc_cmb(ionic); else ionic_map_classic_cmb(ionic);} int ionic_dev_setup(struct ionic *ionic){ struct ionic_dev_bar *bar = ionic->bars; unsigned int num_bars = ionic->num_bars; struct ionic_dev *idev = &ionic->idev; struct device *dev = ionic->dev; u32 sig; int err; /* BAR0: dev_cmd and interrupts */ if (num_bars < 1) { dev_err(dev, "No bars found, aborting\n"); return -EFAULT; } if (bar->len < IONIC_BAR0_SIZE) { dev_err(dev, "Resource bar size %lu too small, aborting\n", bar->len); return -EFAULT; } idev->dev_info_regs = bar->vaddr + IONIC_BAR0_DEV_INFO_REGS_OFFSET; idev->dev_cmd_regs = bar->vaddr + IONIC_BAR0_DEV_CMD_REGS_OFFSET; idev->intr_status = bar->vaddr + IONIC_BAR0_INTR_STATUS_OFFSET; idev->intr_ctrl = bar->vaddr + IONIC_BAR0_INTR_CTRL_OFFSET; idev->hwstamp_regs = &idev->dev_info_regs->hwstamp; sig = ioread32(&idev->dev_info_regs->signature); if (sig != IONIC_DEV_INFO_SIGNATURE) { dev_err(dev, "Incompatible firmware signature %x", sig); return -EFAULT; } ionic_init_devinfo(ionic); /* BAR1: doorbells */ bar++; if (num_bars < 2) { dev_err(dev, "Doorbell bar missing, aborting\n"); return -EFAULT; } err = ionic_watchdog_init(ionic); if (err) return err; idev->db_pages = bar->vaddr; idev->phy_db_pages = bar->bus_addr; /* BAR2: optional controller memory mapping */ bar++; mutex_init(&idev->cmb_inuse_lock); if (num_bars < 3 || !ionic->bars[IONIC_PCI_BAR_CMB].len) { idev->cmb_inuse = NULL; idev->phy_cmb_pages = 0; idev->cmb_npages = 0; return 0; } return 0;} void ionic_dev_teardown(struct ionic *ionic){ struct ionic_dev *idev = &ionic->idev; kfree(idev->cmb_inuse); idev->cmb_inuse = NULL; idev->phy_cmb_pages = 0; idev->cmb_npages = 0; idev->phy_cmb_expdb64_pages = 0; idev->phy_cmb_expdb128_pages = 0; idev->phy_cmb_expdb256_pages = 0; idev->phy_cmb_expdb512_pages = 0; if (ionic->wq) { destroy_workqueue(ionic->wq); ionic->wq = NULL; } mutex_destroy(&idev->cmb_inuse_lock);} /* Devcmd Interface */static bool __ionic_is_fw_running(struct ionic_dev *idev, u8 *status_ptr){ u8 fw_status; if (!idev->dev_info_regs) { if (status_ptr) *status_ptr = 0xff; return false; } fw_status = ioread8(&idev->dev_info_regs->fw_status); if (status_ptr) *status_ptr = fw_status; /* firmware is useful only if the running bit is set and * fw_status != 0xff (bad PCI read) */ return (fw_status != 0xff) && (fw_status & IONIC_FW_STS_F_RUNNING);} bool ionic_is_fw_running(struct ionic_dev *idev){ return __ionic_is_fw_running(idev, NULL);} Lines 1–500 of 1088. Load more
// SPDX-License-Identifier: GPL-2.0 /* Copyright(c) 2017 - 2019 Pensando Systems, Inc */ #include <linux/kernel.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/io.h> #include <linux/slab.h> #include <linux/etherdevice.h> #include "ionic.h" #include "ionic_dev.h" #include "ionic_lif.h" static void ionic_watchdog_cb(struct timer_list *t) { struct ionic *ionic = timer_container_of(ionic, t, watchdog_timer); struct ionic_lif *lif = ionic->lif; struct ionic_deferred_work *work; int hb; mod_timer(&ionic->watchdog_timer, round_jiffies(jiffies + ionic->watchdog_period)); if (!lif) return; hb = ionic_heartbeat_check(ionic); dev_dbg(ionic->dev, "%s: hb %d running %d UP %d\n", __func__, hb, netif_running(lif->netdev), test_bit(IONIC_LIF_F_UP, lif->state)); if (hb >= 0 && !test_bit(IONIC_LIF_F_FW_RESET, lif->state)) ionic_link_status_check_request(lif, CAN_NOT_SLEEP); if (test_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state) && !test_bit(IONIC_LIF_F_FW_RESET, lif->state)) { work = kzalloc_obj(*work, GFP_ATOMIC); if (!work) { netdev_err(lif->netdev, "rxmode change dropped\n"); return; } work->type = IONIC_DW_TYPE_RX_MODE; netdev_dbg(lif->netdev, "deferred: rx_mode\n"); ionic_lif_deferred_enqueue(lif, work); } } static void ionic_napi_schedule_do_softirq(struct napi_struct *napi) { local_bh_disable(); napi_schedule(napi); local_bh_enable(); } void ionic_doorbell_napi_work(struct work_struct *work) { struct ionic_qcq *qcq = container_of(work, struct ionic_qcq, doorbell_napi_work); unsigned long now, then, dif; now = READ_ONCE(jiffies); then = qcq->q.dbell_jiffies; dif = now - then; if (dif > qcq->q.dbell_deadline) ionic_napi_schedule_do_softirq(&qcq->napi); } static int ionic_get_preferred_cpu(struct ionic *ionic, struct ionic_intr_info *intr) { int cpu; cpu = cpumask_first_and(*intr->affinity_mask, cpu_online_mask); if (cpu >= nr_cpu_ids) cpu = cpumask_local_spread(0, dev_to_node(ionic->dev)); return cpu; } static void ionic_queue_dbell_napi_work(struct ionic *ionic, struct ionic_qcq *qcq) { int cpu; if (!(qcq->flags & IONIC_QCQ_F_INTR)) return; cpu = ionic_get_preferred_cpu(ionic, &qcq->intr); queue_work_on(cpu, ionic->wq, &qcq->doorbell_napi_work); } static void ionic_doorbell_check_dwork(struct work_struct *work) { struct ionic *ionic = container_of(work, struct ionic, doorbell_check_dwork.work); struct ionic_lif *lif = ionic->lif; mutex_lock(&lif->queue_lock); if (test_bit(IONIC_LIF_F_FW_STOPPING, lif->state) || test_bit(IONIC_LIF_F_FW_RESET, lif->state)) { mutex_unlock(&lif->queue_lock); return; } ionic_napi_schedule_do_softirq(&lif->adminqcq->napi); if (test_bit(IONIC_LIF_F_UP, lif->state)) { int i; for (i = 0; i < lif->nxqs; i++) { ionic_queue_dbell_napi_work(ionic, lif->txqcqs[i]); ionic_queue_dbell_napi_work(ionic, lif->rxqcqs[i]); } if (lif->hwstamp_txq && lif->hwstamp_txq->flags & IONIC_QCQ_F_INTR) ionic_napi_schedule_do_softirq(&lif->hwstamp_txq->napi); if (lif->hwstamp_rxq && lif->hwstamp_rxq->flags & IONIC_QCQ_F_INTR) ionic_napi_schedule_do_softirq(&lif->hwstamp_rxq->napi); } mutex_unlock(&lif->queue_lock); ionic_queue_doorbell_check(ionic, IONIC_NAPI_DEADLINE); } bool ionic_doorbell_wa(struct ionic *ionic) { u8 asic_type = ionic->idev.dev_info.asic_type; return !asic_type || asic_type == IONIC_ASIC_TYPE_ELBA; } static int ionic_watchdog_init(struct ionic *ionic) { struct ionic_dev *idev = &ionic->idev; timer_setup(&ionic->watchdog_timer, ionic_watchdog_cb, 0); ionic->watchdog_period = IONIC_WATCHDOG_SECS * HZ; /* set times to ensure the first check will proceed */ atomic_long_set(&idev->last_check_time, jiffies - 2 * HZ); idev->last_hb_time = jiffies - 2 * ionic->watchdog_period; /* init as ready, so no transition if the first check succeeds */ idev->last_fw_hb = 0; idev->fw_hb_ready = true; idev->fw_status_ready = true; idev->fw_generation = IONIC_FW_STS_F_GENERATION & ioread8(&idev->dev_info_regs->fw_status); ionic->wq = alloc_workqueue("%s-wq", WQ_UNBOUND, 0, dev_name(ionic->dev)); if (!ionic->wq) { dev_err(ionic->dev, "alloc_workqueue failed"); return -ENOMEM; } if (ionic_doorbell_wa(ionic)) INIT_DELAYED_WORK(&ionic->doorbell_check_dwork, ionic_doorbell_check_dwork); return 0; } void ionic_queue_doorbell_check(struct ionic *ionic, int delay) { int cpu; if (!ionic->lif->doorbell_wa) return; cpu = ionic_get_preferred_cpu(ionic, &ionic->lif->adminqcq->intr); queue_delayed_work_on(cpu, ionic->wq, &ionic->doorbell_check_dwork, delay); } void ionic_init_devinfo(struct ionic *ionic) { struct ionic_dev *idev = &ionic->idev; idev->dev_info.asic_type = ioread8(&idev->dev_info_regs->asic_type); idev->dev_info.asic_rev = ioread8(&idev->dev_info_regs->asic_rev); memcpy_fromio(idev->dev_info.fw_version, idev->dev_info_regs->fw_version, IONIC_DEVINFO_FWVERS_BUFLEN); memcpy_fromio(idev->dev_info.serial_num, idev->dev_info_regs->serial_num, IONIC_DEVINFO_SERIAL_BUFLEN); idev->dev_info.fw_version[IONIC_DEVINFO_FWVERS_BUFLEN] = 0; idev->dev_info.serial_num[IONIC_DEVINFO_SERIAL_BUFLEN] = 0; dev_dbg(ionic->dev, "fw_version %s\n", idev->dev_info.fw_version); } static void ionic_map_disc_cmb(struct ionic *ionic) { struct ionic_identity *ident = &ionic->ident; u32 length_reg0, length, offset, num_regions; struct ionic_dev_bar *bar = ionic->bars; struct ionic_dev *idev = &ionic->idev; struct device *dev = ionic->dev; int err, sz, i; u64 end; mutex_lock(&ionic->dev_cmd_lock); ionic_dev_cmd_discover_cmb(idev); err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); if (!err) { sz = min(sizeof(ident->cmb_layout), sizeof(idev->dev_cmd_regs->data)); memcpy_fromio(&ident->cmb_layout, &idev->dev_cmd_regs->data, sz); } mutex_unlock(&ionic->dev_cmd_lock); if (err) { dev_warn(dev, "Cannot discover CMB layout, disabling CMB\n"); return; } bar += 2; num_regions = le32_to_cpu(ident->cmb_layout.num_regions); if (!num_regions || num_regions > IONIC_MAX_CMB_REGIONS) { dev_warn(dev, "Invalid number of CMB entries (%d)\n", num_regions); return; } dev_dbg(dev, "ionic_cmb_layout_identity num_regions %d flags %x:\n", num_regions, ident->cmb_layout.flags); for (i = 0; i < num_regions; i++) { offset = le32_to_cpu(ident->cmb_layout.region[i].offset); length = le32_to_cpu(ident->cmb_layout.region[i].length); end = offset + length; dev_dbg(dev, "CMB entry %d: bar_num %u cmb_type %u offset %x length %u\n", i, ident->cmb_layout.region[i].bar_num, ident->cmb_layout.region[i].cmb_type, offset, length); if (end > (bar->len >> IONIC_CMB_SHIFT_64K)) { dev_warn(dev, "Out of bounds CMB region %d offset %x length %u\n", i, offset, length); return; } } /* if first entry matches PCI config, expdb is not supported */ if (ident->cmb_layout.region[0].bar_num == bar->res_index && le32_to_cpu(ident->cmb_layout.region[0].length) == bar->len && !ident->cmb_layout.region[0].offset) { dev_warn(dev, "No CMB mapping discovered\n"); return; } /* process first entry for regular mapping */ length_reg0 = le32_to_cpu(ident->cmb_layout.region[0].length); if (!length_reg0) { dev_warn(dev, "region len = 0. No CMB mapping discovered\n"); return; } /* Verify first entry size matches expected 8MB size (in 64KB pages) */ if (length_reg0 != IONIC_BAR2_CMB_ENTRY_SIZE >> IONIC_CMB_SHIFT_64K) { dev_warn(dev, "Unexpected CMB size in entry 0: %u pages\n", length_reg0); return; } sz = BITS_TO_LONGS((length_reg0 << IONIC_CMB_SHIFT_64K) / PAGE_SIZE) * sizeof(long); idev->cmb_inuse = kzalloc(sz, GFP_KERNEL); if (!idev->cmb_inuse) { dev_warn(dev, "No memory for CMB, disabling\n"); idev->phy_cmb_pages = 0; idev->phy_cmb_expdb64_pages = 0; idev->phy_cmb_expdb128_pages = 0; idev->phy_cmb_expdb256_pages = 0; idev->phy_cmb_expdb512_pages = 0; idev->cmb_npages = 0; return; } for (i = 0; i < num_regions; i++) { /* check this region matches first region length as to * ease implementation */ if (le32_to_cpu(ident->cmb_layout.region[i].length) != length_reg0) continue; offset = le32_to_cpu(ident->cmb_layout.region[i].offset); switch (ident->cmb_layout.region[i].cmb_type) { case IONIC_CMB_TYPE_DEVMEM: idev->phy_cmb_pages = bar->bus_addr + offset; idev->cmb_npages = (length_reg0 << IONIC_CMB_SHIFT_64K) / PAGE_SIZE; dev_dbg(dev, "regular cmb mapping: bar->bus_addr %pa region[%d].length %u\n", &bar->bus_addr, i, length); dev_dbg(dev, "idev->phy_cmb_pages %pad, idev->cmb_npages %u\n", &idev->phy_cmb_pages, idev->cmb_npages); break; case IONIC_CMB_TYPE_EXPDB64: idev->phy_cmb_expdb64_pages = bar->bus_addr + (offset << IONIC_CMB_SHIFT_64K); dev_dbg(dev, "idev->phy_cmb_expdb64_pages %pad\n", &idev->phy_cmb_expdb64_pages); break; case IONIC_CMB_TYPE_EXPDB128: idev->phy_cmb_expdb128_pages = bar->bus_addr + (offset << IONIC_CMB_SHIFT_64K); dev_dbg(dev, "idev->phy_cmb_expdb128_pages %pad\n", &idev->phy_cmb_expdb128_pages); break; case IONIC_CMB_TYPE_EXPDB256: idev->phy_cmb_expdb256_pages = bar->bus_addr + (offset << IONIC_CMB_SHIFT_64K); dev_dbg(dev, "idev->phy_cmb_expdb256_pages %pad\n", &idev->phy_cmb_expdb256_pages); break; case IONIC_CMB_TYPE_EXPDB512: idev->phy_cmb_expdb512_pages = bar->bus_addr + (offset << IONIC_CMB_SHIFT_64K); dev_dbg(dev, "idev->phy_cmb_expdb512_pages %pad\n", &idev->phy_cmb_expdb512_pages); break; default: dev_warn(dev, "[%d] Invalid cmb_type (%d)\n", i, ident->cmb_layout.region[i].cmb_type); break; } } } static void ionic_map_classic_cmb(struct ionic *ionic) { struct ionic_dev_bar *bar = ionic->bars; struct ionic_dev *idev = &ionic->idev; struct device *dev = ionic->dev; int sz; bar += 2; /* classic CMB mapping */ idev->phy_cmb_pages = bar->bus_addr; idev->cmb_npages = bar->len / PAGE_SIZE; dev_dbg(dev, "classic cmb mapping: bar->bus_addr %pa bar->len %lu\n", &bar->bus_addr, bar->len); dev_dbg(dev, "idev->phy_cmb_pages %pad, idev->cmb_npages %u\n", &idev->phy_cmb_pages, idev->cmb_npages); sz = BITS_TO_LONGS(idev->cmb_npages) * sizeof(long); idev->cmb_inuse = kzalloc(sz, GFP_KERNEL); if (!idev->cmb_inuse) { idev->phy_cmb_pages = 0; idev->cmb_npages = 0; } } void ionic_map_cmb(struct ionic *ionic) { struct pci_dev *pdev = ionic->pdev; struct device *dev = ionic->dev; if (!(pci_resource_flags(pdev, 4) & IORESOURCE_MEM)) { dev_dbg(dev, "No CMB, disabling\n"); return; } if (ionic->ident.dev.capabilities & cpu_to_le64(IONIC_DEV_CAP_DISC_CMB)) ionic_map_disc_cmb(ionic); else ionic_map_classic_cmb(ionic); } int ionic_dev_setup(struct ionic *ionic) { struct ionic_dev_bar *bar = ionic->bars; unsigned int num_bars = ionic->num_bars; struct ionic_dev *idev = &ionic->idev; struct device *dev = ionic->dev; u32 sig; int err; /* BAR0: dev_cmd and interrupts */ if (num_bars < 1) { dev_err(dev, "No bars found, aborting\n"); return -EFAULT; } if (bar->len < IONIC_BAR0_SIZE) { dev_err(dev, "Resource bar size %lu too small, aborting\n", bar->len); return -EFAULT; } idev->dev_info_regs = bar->vaddr + IONIC_BAR0_DEV_INFO_REGS_OFFSET; idev->dev_cmd_regs = bar->vaddr + IONIC_BAR0_DEV_CMD_REGS_OFFSET; idev->intr_status = bar->vaddr + IONIC_BAR0_INTR_STATUS_OFFSET; idev->intr_ctrl = bar->vaddr + IONIC_BAR0_INTR_CTRL_OFFSET; idev->hwstamp_regs = &idev->dev_info_regs->hwstamp; sig = ioread32(&idev->dev_info_regs->signature); if (sig != IONIC_DEV_INFO_SIGNATURE) { dev_err(dev, "Incompatible firmware signature %x", sig); return -EFAULT; } ionic_init_devinfo(ionic); /* BAR1: doorbells */ bar++; if (num_bars < 2) { dev_err(dev, "Doorbell bar missing, aborting\n"); return -EFAULT; } err = ionic_watchdog_init(ionic); if (err) return err; idev->db_pages = bar->vaddr; idev->phy_db_pages = bar->bus_addr; /* BAR2: optional controller memory mapping */ bar++; mutex_init(&idev->cmb_inuse_lock); if (num_bars < 3 || !ionic->bars[IONIC_PCI_BAR_CMB].len) { idev->cmb_inuse = NULL; idev->phy_cmb_pages = 0; idev->cmb_npages = 0; return 0; } return 0; } void ionic_dev_teardown(struct ionic *ionic) { struct ionic_dev *idev = &ionic->idev; kfree(idev->cmb_inuse); idev->cmb_inuse = NULL; idev->phy_cmb_pages = 0; idev->cmb_npages = 0; idev->phy_cmb_expdb64_pages = 0; idev->phy_cmb_expdb128_pages = 0; idev->phy_cmb_expdb256_pages = 0; idev->phy_cmb_expdb512_pages = 0; if (ionic->wq) { destroy_workqueue(ionic->wq); ionic->wq = NULL; } mutex_destroy(&idev->cmb_inuse_lock); } /* Devcmd Interface */ static bool __ionic_is_fw_running(struct ionic_dev *idev, u8 *status_ptr) { u8 fw_status; if (!idev->dev_info_regs) { if (status_ptr) *status_ptr = 0xff; return false; } fw_status = ioread8(&idev->dev_info_regs->fw_status); if (status_ptr) *status_ptr = fw_status; /* firmware is useful only if the running bit is set and * fw_status != 0xff (bad PCI read) */ return (fw_status != 0xff) && (fw_status & IONIC_FW_STS_F_RUNNING); } bool ionic_is_fw_running(struct ionic_dev *idev) { return __ionic_is_fw_running(idev, NULL); }