1088 lines · 27.7 KB · C
RawDownload
// 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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t