コード例 #1
0
void
isl38xx_interface_reset(void *device_base, dma_addr_t host_address)
{
	u32 reg;

#if VERBOSE > SHOW_ERROR_MESSAGES
	DEBUG(SHOW_FUNCTION_CALLS, "isl38xx_interface_reset \n");
#endif

	/* load the address of the control block in the device */
	isl38xx_w32_flush(device_base, host_address, ISL38XX_CTRL_BLK_BASE_REG);
	udelay(ISL38XX_WRITEIO_DELAY);

	/* set the reset bit in the Device Interrupt Register */
	isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_RESET,
			  ISL38XX_DEV_INT_REG);
	udelay(ISL38XX_WRITEIO_DELAY);

	/* enable the interrupt for detecting initialization */

	/* Note: Do not enable other interrupts here. We want the
	 * device to have come up first 100% before allowing any other 
	 * interrupts. */
	reg = ISL38XX_INT_IDENT_INIT;

	isl38xx_w32_flush(device_base, reg, ISL38XX_INT_EN_REG);
	udelay(ISL38XX_WRITEIO_DELAY);  /* allow complete full reset */
}
コード例 #2
0
void
isl38xx_handle_sleep_request(isl38xx_control_block *control_block,
			     int *powerstate, void __iomem *device_base)
{
	if (isl38xx_in_queue(control_block, ISL38XX_CB_TX_DATA_LQ))
		
		return;

	if (isl38xx_in_queue(control_block, ISL38XX_CB_TX_MGMTQ))
		
		return;

	
	if (isl38xx_in_queue(control_block, ISL38XX_CB_RX_DATA_LQ))
		
		return;

	if (isl38xx_in_queue(control_block, ISL38XX_CB_RX_MGMTQ))
		
		return;

#if VERBOSE > SHOW_ERROR_MESSAGES
	DEBUG(SHOW_TRACING, "Device going to sleep mode\n");
#endif

	
	*powerstate = ISL38XX_PSM_POWERSAVE_STATE;

	
	isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_SLEEP,
			  ISL38XX_DEV_INT_REG);
	udelay(ISL38XX_WRITEIO_DELAY);
}
コード例 #3
0
void
isl38xx_handle_sleep_request(isl38xx_control_block *control_block,
			     int *powerstate, void __iomem *device_base)
{
	/* device requests to go into sleep mode
	 * check whether the transmit queues for data and management are empty */
	if (isl38xx_in_queue(control_block, ISL38XX_CB_TX_DATA_LQ))
		/* data tx queue not empty */
		return;

	if (isl38xx_in_queue(control_block, ISL38XX_CB_TX_MGMTQ))
		/* management tx queue not empty */
		return;

	/* check also whether received frames are pending */
	if (isl38xx_in_queue(control_block, ISL38XX_CB_RX_DATA_LQ))
		/* data rx queue not empty */
		return;

	if (isl38xx_in_queue(control_block, ISL38XX_CB_RX_MGMTQ))
		/* management rx queue not empty */
		return;

#if VERBOSE > SHOW_ERROR_MESSAGES
	DEBUG(SHOW_TRACING, "Device going to sleep mode\n");
#endif

	/* all queues are empty, allow the device to go into sleep mode */
	*powerstate = ISL38XX_PSM_POWERSAVE_STATE;

	/* assert the Sleep interrupt in the Device Interrupt Register */
	isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_SLEEP,
			  ISL38XX_DEV_INT_REG);
	udelay(ISL38XX_WRITEIO_DELAY);
}
コード例 #4
0
void
isl38xx_enable_common_interrupts(void *device_base) {
	u32 reg;
	reg = ( ISL38XX_INT_IDENT_UPDATE | 
			ISL38XX_INT_IDENT_SLEEP | ISL38XX_INT_IDENT_WAKEUP);
	isl38xx_w32_flush(device_base, reg, ISL38XX_INT_EN_REG);
	udelay(ISL38XX_WRITEIO_DELAY);
}
コード例 #5
0
void
isl38xx_interface_reset(void __iomem *device_base, dma_addr_t host_address)
{
#if VERBOSE > SHOW_ERROR_MESSAGES
	DEBUG(SHOW_FUNCTION_CALLS, "isl38xx_interface_reset\n");
#endif

	
	isl38xx_w32_flush(device_base, host_address, ISL38XX_CTRL_BLK_BASE_REG);
	udelay(ISL38XX_WRITEIO_DELAY);

	
	isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_RESET, ISL38XX_DEV_INT_REG);
	udelay(ISL38XX_WRITEIO_DELAY);

	

	isl38xx_w32_flush(device_base, ISL38XX_INT_IDENT_INIT, ISL38XX_INT_EN_REG);
	udelay(ISL38XX_WRITEIO_DELAY);  
}
コード例 #6
0
void
isl38xx_handle_wakeup(isl38xx_control_block *control_block,
		      int *powerstate, void __iomem *device_base)
{
	
	*powerstate = ISL38XX_PSM_ACTIVE_STATE;

	
	if (!isl38xx_in_queue(control_block, ISL38XX_CB_TX_DATA_LQ)
	    && !isl38xx_in_queue(control_block, ISL38XX_CB_TX_MGMTQ))
		return;

#if VERBOSE > SHOW_ERROR_MESSAGES
	DEBUG(SHOW_ANYTHING, "Wake up handler trigger the device\n");
#endif

	isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_UPDATE,
			  ISL38XX_DEV_INT_REG);
	udelay(ISL38XX_WRITEIO_DELAY);
}
コード例 #7
0
void
isl38xx_handle_wakeup(isl38xx_control_block *control_block,
		      int *powerstate, void __iomem *device_base)
{
	/* device is in active state, update the powerstate flag */
	*powerstate = ISL38XX_PSM_ACTIVE_STATE;

	/* now check whether there are frames pending for the card */
	if (!isl38xx_in_queue(control_block, ISL38XX_CB_TX_DATA_LQ)
	    && !isl38xx_in_queue(control_block, ISL38XX_CB_TX_MGMTQ))
		return;

#if VERBOSE > SHOW_ERROR_MESSAGES
	DEBUG(SHOW_ANYTHING, "Wake up handler trigger the device\n");
#endif

	/* either data or management transmit queue has a frame pending
	 * trigger the device by setting the Update bit in the Device Int reg */
	isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_UPDATE,
			  ISL38XX_DEV_INT_REG);
	udelay(ISL38XX_WRITEIO_DELAY);
}
コード例 #8
0
void
isl38xx_trigger_device(int asleep, void __iomem *device_base)
{
	u32 reg;

#if VERBOSE > SHOW_ERROR_MESSAGES
	u32 counter = 0;
	struct timeval current_time;
	DEBUG(SHOW_FUNCTION_CALLS, "isl38xx trigger device\n");
#endif

	
	if (asleep) {
		
#if VERBOSE > SHOW_ERROR_MESSAGES
		do_gettimeofday(&current_time);
		DEBUG(SHOW_TRACING, "%08li.%08li Device wakeup triggered\n",
		      current_time.tv_sec, (long)current_time.tv_usec);

		DEBUG(SHOW_TRACING, "%08li.%08li Device register read %08x\n",
		      current_time.tv_sec, (long)current_time.tv_usec,
		      readl(device_base + ISL38XX_CTRL_STAT_REG));
#endif

		reg = readl(device_base + ISL38XX_INT_IDENT_REG);
		if (reg == 0xabadface) {
#if VERBOSE > SHOW_ERROR_MESSAGES
			do_gettimeofday(&current_time);
			DEBUG(SHOW_TRACING,
			      "%08li.%08li Device register abadface\n",
			      current_time.tv_sec, (long)current_time.tv_usec);
#endif
			
			while (reg = readl(device_base + ISL38XX_CTRL_STAT_REG),
			       (reg & ISL38XX_CTRL_STAT_SLEEPMODE) == 0) {
				udelay(ISL38XX_WRITEIO_DELAY);
#if VERBOSE > SHOW_ERROR_MESSAGES
				counter++;
#endif
			}

#if VERBOSE > SHOW_ERROR_MESSAGES
			DEBUG(SHOW_TRACING,
			      "%08li.%08li Device register read %08x\n",
			      current_time.tv_sec, (long)current_time.tv_usec,
			      readl(device_base + ISL38XX_CTRL_STAT_REG));
			do_gettimeofday(&current_time);
			DEBUG(SHOW_TRACING,
			      "%08li.%08li Device asleep counter %i\n",
			      current_time.tv_sec, (long)current_time.tv_usec,
			      counter);
#endif
		}
		
		isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_WAKEUP,
				  ISL38XX_DEV_INT_REG);

#if VERBOSE > SHOW_ERROR_MESSAGES
		udelay(ISL38XX_WRITEIO_DELAY);

		
		reg = readl(device_base + ISL38XX_CTRL_STAT_REG);
		do_gettimeofday(&current_time);
		DEBUG(SHOW_TRACING, "%08li.%08li Device register read %08x\n",
		      current_time.tv_sec, (long)current_time.tv_usec, reg);
#endif
	} else {
		
#if VERBOSE > SHOW_ERROR_MESSAGES
		DEBUG(SHOW_TRACING, "Device is in active state\n");
#endif
		

		isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_UPDATE,
				  ISL38XX_DEV_INT_REG);
	}
}
コード例 #9
0
void
isl38xx_disable_interrupts(void __iomem *device)
{
	isl38xx_w32_flush(device, 0x00000000, ISL38XX_INT_EN_REG);
	udelay(ISL38XX_WRITEIO_DELAY);
}
コード例 #10
0
ファイル: islpci_dev.c プロジェクト: 3sOx/asuswrt-merlin
static int
isl_upload_firmware(islpci_private *priv)
{
	u32 reg, rc;
	void __iomem *device_base = priv->device_base;

	/* clear the RAMBoot and the Reset bit */
	reg = readl(device_base + ISL38XX_CTRL_STAT_REG);
	reg &= ~ISL38XX_CTRL_STAT_RESET;
	reg &= ~ISL38XX_CTRL_STAT_RAMBOOT;
	writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
	wmb();
	udelay(ISL38XX_WRITEIO_DELAY);

	/* set the Reset bit without reading the register ! */
	reg |= ISL38XX_CTRL_STAT_RESET;
	writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
	wmb();
	udelay(ISL38XX_WRITEIO_DELAY);

	/* clear the Reset bit */
	reg &= ~ISL38XX_CTRL_STAT_RESET;
	writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
	wmb();

	/* wait a while for the device to reboot */
	mdelay(50);

	{
		const struct firmware *fw_entry = NULL;
		long fw_len;
		const u32 *fw_ptr;

		rc = request_firmware(&fw_entry, priv->firmware, PRISM_FW_PDEV);
		if (rc) {
			printk(KERN_ERR
			       "%s: request_firmware() failed for '%s'\n",
			       "prism54", priv->firmware);
			return rc;
		}
		/* prepare the Direct Memory Base register */
		reg = ISL38XX_DEV_FIRMWARE_ADDRES;

		fw_ptr = (u32 *) fw_entry->data;
		fw_len = fw_entry->size;

		if (fw_len % 4) {
			printk(KERN_ERR
			       "%s: firmware '%s' size is not multiple of 32bit, aborting!\n",
			       "prism54", priv->firmware);
			release_firmware(fw_entry);
			return -EILSEQ; /* Illegal byte sequence  */;
		}

		while (fw_len > 0) {
			long _fw_len =
			    (fw_len >
			     ISL38XX_MEMORY_WINDOW_SIZE) ?
			    ISL38XX_MEMORY_WINDOW_SIZE : fw_len;
			u32 __iomem *dev_fw_ptr = device_base + ISL38XX_DIRECT_MEM_WIN;

			/* set the card's base address for writing the data */
			isl38xx_w32_flush(device_base, reg,
					  ISL38XX_DIR_MEM_BASE_REG);
			wmb();	/* be paranoid */

			/* increment the write address for next iteration */
			reg += _fw_len;
			fw_len -= _fw_len;

			/* write the data to the Direct Memory Window 32bit-wise */
			/* memcpy_toio() doesn't guarantee 32bit writes :-| */
			while (_fw_len > 0) {
				/* use non-swapping writel() */
				__raw_writel(*fw_ptr, dev_fw_ptr);
				fw_ptr++, dev_fw_ptr++;
				_fw_len -= 4;
			}

			/* flush PCI posting */
			(void) readl(device_base + ISL38XX_PCI_POSTING_FLUSH);
			wmb();	/* be paranoid again */

			BUG_ON(_fw_len != 0);
		}

		BUG_ON(fw_len != 0);

		/* Firmware version is at offset 40 (also for "newmac") */
		printk(KERN_DEBUG "%s: firmware version: %.8s\n",
		       priv->ndev->name, fw_entry->data + 40);

		release_firmware(fw_entry);
	}

	/* now reset the device
	 * clear the Reset & ClkRun bit, set the RAMBoot bit */
	reg = readl(device_base + ISL38XX_CTRL_STAT_REG);
	reg &= ~ISL38XX_CTRL_STAT_CLKRUN;
	reg &= ~ISL38XX_CTRL_STAT_RESET;
	reg |= ISL38XX_CTRL_STAT_RAMBOOT;
	isl38xx_w32_flush(device_base, reg, ISL38XX_CTRL_STAT_REG);
	wmb();
	udelay(ISL38XX_WRITEIO_DELAY);

	/* set the reset bit latches the host override and RAMBoot bits
	 * into the device for operation when the reset bit is reset */
	reg |= ISL38XX_CTRL_STAT_RESET;
	writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
	/* don't do flush PCI posting here! */
	wmb();
	udelay(ISL38XX_WRITEIO_DELAY);

	/* clear the reset bit should start the whole circus */
	reg &= ~ISL38XX_CTRL_STAT_RESET;
	writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
	/* don't do flush PCI posting here! */
	wmb();
	udelay(ISL38XX_WRITEIO_DELAY);

	return 0;
}
コード例 #11
0
ファイル: islpci_dev.c プロジェクト: 3sOx/asuswrt-merlin
irqreturn_t
islpci_interrupt(int irq, void *config)
{
	u32 reg;
	islpci_private *priv = config;
	struct net_device *ndev = priv->ndev;
	void __iomem *device = priv->device_base;
	int powerstate = ISL38XX_PSM_POWERSAVE_STATE;

	/* lock the interrupt handler */
	spin_lock(&priv->slock);

	/* received an interrupt request on a shared IRQ line
	 * first check whether the device is in sleep mode */
	reg = readl(device + ISL38XX_CTRL_STAT_REG);
	if (reg & ISL38XX_CTRL_STAT_SLEEPMODE)
		/* device is in sleep mode, IRQ was generated by someone else */
	{
#if VERBOSE > SHOW_ERROR_MESSAGES
		DEBUG(SHOW_TRACING, "Assuming someone else called the IRQ\n");
#endif
		spin_unlock(&priv->slock);
		return IRQ_NONE;
	}


	/* check whether there is any source of interrupt on the device */
	reg = readl(device + ISL38XX_INT_IDENT_REG);

	/* also check the contents of the Interrupt Enable Register, because this
	 * will filter out interrupt sources from other devices on the same irq ! */
	reg &= readl(device + ISL38XX_INT_EN_REG);
	reg &= ISL38XX_INT_SOURCES;

	if (reg != 0) {
		if (islpci_get_state(priv) != PRV_STATE_SLEEP)
			powerstate = ISL38XX_PSM_ACTIVE_STATE;

		/* reset the request bits in the Identification register */
		isl38xx_w32_flush(device, reg, ISL38XX_INT_ACK_REG);

#if VERBOSE > SHOW_ERROR_MESSAGES
		DEBUG(SHOW_FUNCTION_CALLS,
		      "IRQ: Identification register 0x%p 0x%x \n", device, reg);
#endif

		/* check for each bit in the register separately */
		if (reg & ISL38XX_INT_IDENT_UPDATE) {
#if VERBOSE > SHOW_ERROR_MESSAGES
			/* Queue has been updated */
			DEBUG(SHOW_TRACING, "IRQ: Update flag \n");

			DEBUG(SHOW_QUEUE_INDEXES,
			      "CB drv Qs: [%i][%i][%i][%i][%i][%i]\n",
			      le32_to_cpu(priv->control_block->
					  driver_curr_frag[0]),
			      le32_to_cpu(priv->control_block->
					  driver_curr_frag[1]),
			      le32_to_cpu(priv->control_block->
					  driver_curr_frag[2]),
			      le32_to_cpu(priv->control_block->
					  driver_curr_frag[3]),
			      le32_to_cpu(priv->control_block->
					  driver_curr_frag[4]),
			      le32_to_cpu(priv->control_block->
					  driver_curr_frag[5])
			    );

			DEBUG(SHOW_QUEUE_INDEXES,
			      "CB dev Qs: [%i][%i][%i][%i][%i][%i]\n",
			      le32_to_cpu(priv->control_block->
					  device_curr_frag[0]),
			      le32_to_cpu(priv->control_block->
					  device_curr_frag[1]),
			      le32_to_cpu(priv->control_block->
					  device_curr_frag[2]),
			      le32_to_cpu(priv->control_block->
					  device_curr_frag[3]),
			      le32_to_cpu(priv->control_block->
					  device_curr_frag[4]),
			      le32_to_cpu(priv->control_block->
					  device_curr_frag[5])
			    );
#endif

			/* cleanup the data low transmit queue */
			islpci_eth_cleanup_transmit(priv, priv->control_block);

			/* device is in active state, update the
			 * powerstate flag if necessary */
			powerstate = ISL38XX_PSM_ACTIVE_STATE;

			/* check all three queues in priority order
			 * call the PIMFOR receive function until the
			 * queue is empty */
			if (isl38xx_in_queue(priv->control_block,
						ISL38XX_CB_RX_MGMTQ) != 0) {
#if VERBOSE > SHOW_ERROR_MESSAGES
				DEBUG(SHOW_TRACING,
				      "Received frame in Management Queue\n");
#endif
				islpci_mgt_receive(ndev);

				islpci_mgt_cleanup_transmit(ndev);

				/* Refill slots in receive queue */
				islpci_mgmt_rx_fill(ndev);

				/* no need to trigger the device, next
                                   islpci_mgt_transaction does it */
			}

			while (isl38xx_in_queue(priv->control_block,
						ISL38XX_CB_RX_DATA_LQ) != 0) {
#if VERBOSE > SHOW_ERROR_MESSAGES
				DEBUG(SHOW_TRACING,
				      "Received frame in Data Low Queue \n");
#endif
				islpci_eth_receive(priv);
			}

			/* check whether the data transmit queues were full */
			if (priv->data_low_tx_full) {
				/* check whether the transmit is not full anymore */
				if (ISL38XX_CB_TX_QSIZE -
				    isl38xx_in_queue(priv->control_block,
						     ISL38XX_CB_TX_DATA_LQ) >=
				    ISL38XX_MIN_QTHRESHOLD) {
					/* nope, the driver is ready for more network frames */
					netif_wake_queue(priv->ndev);

					/* reset the full flag */
					priv->data_low_tx_full = 0;
				}
			}
		}

		if (reg & ISL38XX_INT_IDENT_INIT) {
			/* Device has been initialized */
#if VERBOSE > SHOW_ERROR_MESSAGES
			DEBUG(SHOW_TRACING,
			      "IRQ: Init flag, device initialized \n");
#endif
			wake_up(&priv->reset_done);
		}

		if (reg & ISL38XX_INT_IDENT_SLEEP) {
			/* Device intends to move to powersave state */
#if VERBOSE > SHOW_ERROR_MESSAGES
			DEBUG(SHOW_TRACING, "IRQ: Sleep flag \n");
#endif
			isl38xx_handle_sleep_request(priv->control_block,
						     &powerstate,
						     priv->device_base);
		}

		if (reg & ISL38XX_INT_IDENT_WAKEUP) {
			/* Device has been woken up to active state */
#if VERBOSE > SHOW_ERROR_MESSAGES
			DEBUG(SHOW_TRACING, "IRQ: Wakeup flag \n");
#endif

			isl38xx_handle_wakeup(priv->control_block,
					      &powerstate, priv->device_base);
		}
	} else {
#if VERBOSE > SHOW_ERROR_MESSAGES
		DEBUG(SHOW_TRACING, "Assuming someone else called the IRQ\n");
#endif
		spin_unlock(&priv->slock);
		return IRQ_NONE;
	}

	/* sleep -> ready */
	if (islpci_get_state(priv) == PRV_STATE_SLEEP
	    && powerstate == ISL38XX_PSM_ACTIVE_STATE)
		islpci_set_state(priv, PRV_STATE_READY);

	/* !sleep -> sleep */
	if (islpci_get_state(priv) != PRV_STATE_SLEEP
	    && powerstate == ISL38XX_PSM_POWERSAVE_STATE)
		islpci_set_state(priv, PRV_STATE_SLEEP);

	/* unlock the interrupt handler */
	spin_unlock(&priv->slock);

	return IRQ_HANDLED;
}
コード例 #12
0
void
isl38xx_trigger_device(int asleep, void __iomem *device_base)
{
	u32 reg;

#if VERBOSE > SHOW_ERROR_MESSAGES
	u32 counter = 0;
	struct timeval current_time;
	DEBUG(SHOW_FUNCTION_CALLS, "isl38xx trigger device\n");
#endif

	/* check whether the device is in power save mode */
	if (asleep) {
		/* device is in powersave, trigger the device for wakeup */
#if VERBOSE > SHOW_ERROR_MESSAGES
		do_gettimeofday(&current_time);
		DEBUG(SHOW_TRACING, "%08li.%08li Device wakeup triggered\n",
		      current_time.tv_sec, (long)current_time.tv_usec);

		DEBUG(SHOW_TRACING, "%08li.%08li Device register read %08x\n",
		      current_time.tv_sec, (long)current_time.tv_usec,
		      readl(device_base + ISL38XX_CTRL_STAT_REG));
#endif

		reg = readl(device_base + ISL38XX_INT_IDENT_REG);
		if (reg == 0xabadface) {
#if VERBOSE > SHOW_ERROR_MESSAGES
			do_gettimeofday(&current_time);
			DEBUG(SHOW_TRACING,
			      "%08li.%08li Device register abadface\n",
			      current_time.tv_sec, (long)current_time.tv_usec);
#endif
			/* read the Device Status Register until Sleepmode bit is set */
			while (reg = readl(device_base + ISL38XX_CTRL_STAT_REG),
			       (reg & ISL38XX_CTRL_STAT_SLEEPMODE) == 0) {
				udelay(ISL38XX_WRITEIO_DELAY);
#if VERBOSE > SHOW_ERROR_MESSAGES
				counter++;
#endif
			}

#if VERBOSE > SHOW_ERROR_MESSAGES
			DEBUG(SHOW_TRACING,
			      "%08li.%08li Device register read %08x\n",
			      current_time.tv_sec, (long)current_time.tv_usec,
			      readl(device_base + ISL38XX_CTRL_STAT_REG));
			do_gettimeofday(&current_time);
			DEBUG(SHOW_TRACING,
			      "%08li.%08li Device asleep counter %i\n",
			      current_time.tv_sec, (long)current_time.tv_usec,
			      counter);
#endif
		}
		/* assert the Wakeup interrupt in the Device Interrupt Register */
		isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_WAKEUP,
				  ISL38XX_DEV_INT_REG);

#if VERBOSE > SHOW_ERROR_MESSAGES
		udelay(ISL38XX_WRITEIO_DELAY);

		/* perform another read on the Device Status Register */
		reg = readl(device_base + ISL38XX_CTRL_STAT_REG);
		do_gettimeofday(&current_time);
		DEBUG(SHOW_TRACING, "%08li.%08li Device register read %08x\n",
		      current_time.tv_sec, (long)current_time.tv_usec, reg);
#endif
	} else {
		/* device is (still) awake  */
#if VERBOSE > SHOW_ERROR_MESSAGES
		DEBUG(SHOW_TRACING, "Device is in active state\n");
#endif
		/* trigger the device by setting the Update bit in the Device Int reg */

		isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_UPDATE,
				  ISL38XX_DEV_INT_REG);
	}
}
コード例 #13
0
int
islpci_eth_transmit(struct sk_buff *skb, struct net_device *ndev)
{
	islpci_private *priv = netdev_priv(ndev);
	isl38xx_control_block *cb = priv->control_block;
	u32 index;
	dma_addr_t pci_map_address;
	int frame_size;
	isl38xx_fragment *fragment;
	int offset;
	struct sk_buff *newskb;
	int newskb_offset;
	unsigned long flags;
	unsigned char wds_mac[6];
	u32 curr_frag;
	int err = 0;

#if VERBOSE > SHOW_ERROR_MESSAGES
	DEBUG(SHOW_FUNCTION_CALLS, "islpci_eth_transmit \n");
#endif

	/* lock the driver code */
	spin_lock_irqsave(&priv->slock, flags);

	/* check whether the destination queue has enough fragments for the frame */
	curr_frag = le32_to_cpu(cb->driver_curr_frag[ISL38XX_CB_TX_DATA_LQ]);
	if (unlikely(curr_frag - priv->free_data_tx >= ISL38XX_CB_TX_QSIZE)) {
		printk(KERN_ERR "%s: transmit device queue full when awake\n",
		       ndev->name);
		netif_stop_queue(ndev);

		/* trigger the device */
		isl38xx_w32_flush(priv->device_base, ISL38XX_DEV_INT_UPDATE,
				  ISL38XX_DEV_INT_REG);
		udelay(ISL38XX_WRITEIO_DELAY);

		err = -EBUSY;
		goto drop_free;
	}
	/* Check alignment and WDS frame formatting. The start of the packet should
	 * be aligned on a 4-byte boundary. If WDS is enabled add another 6 bytes
	 * and add WDS address information */
	if (likely(((long) skb->data & 0x03) | init_wds)) {
		/* get the number of bytes to add and re-allign */
		offset = (4 - (long) skb->data) & 0x03;
		offset += init_wds ? 6 : 0;

		/* check whether the current skb can be used  */
		if (!skb_cloned(skb) && (skb_tailroom(skb) >= offset)) {
			unsigned char *src = skb->data;

#if VERBOSE > SHOW_ERROR_MESSAGES
			DEBUG(SHOW_TRACING, "skb offset %i wds %i\n", offset,
			      init_wds);
#endif

			/* align the buffer on 4-byte boundary */
			skb_reserve(skb, (4 - (long) skb->data) & 0x03);
			if (init_wds) {
				/* wds requires an additional address field of 6 bytes */
				skb_put(skb, 6);
#ifdef ISLPCI_ETH_DEBUG
				printk("islpci_eth_transmit:wds_mac\n");
#endif
				memmove(skb->data + 6, src, skb->len);
				memcpy(skb->data, wds_mac, 6);
			} else {
				memmove(skb->data, src, skb->len);
			}

#if VERBOSE > SHOW_ERROR_MESSAGES
			DEBUG(SHOW_TRACING, "memmove %p %p %i \n", skb->data,
			      src, skb->len);
#endif
		} else {
			newskb =
			    dev_alloc_skb(init_wds ? skb->len + 6 : skb->len);
			if (unlikely(newskb == NULL)) {
				printk(KERN_ERR "%s: Cannot allocate skb\n",
				       ndev->name);
				err = -ENOMEM;
				goto drop_free;
			}
			newskb_offset = (4 - (long) newskb->data) & 0x03;

			/* Check if newskb->data is aligned */
			if (newskb_offset)
				skb_reserve(newskb, newskb_offset);

			skb_put(newskb, init_wds ? skb->len + 6 : skb->len);
			if (init_wds) {
				memcpy(newskb->data + 6, skb->data, skb->len);
				memcpy(newskb->data, wds_mac, 6);
#ifdef ISLPCI_ETH_DEBUG
				printk("islpci_eth_transmit:wds_mac\n");
#endif
			} else
				memcpy(newskb->data, skb->data, skb->len);

#if VERBOSE > SHOW_ERROR_MESSAGES
			DEBUG(SHOW_TRACING, "memcpy %p %p %i wds %i\n",
			      newskb->data, skb->data, skb->len, init_wds);
#endif

			newskb->dev = skb->dev;
			dev_kfree_skb_irq(skb);
			skb = newskb;
		}
	}
	/* display the buffer contents for debugging */
#if VERBOSE > SHOW_ERROR_MESSAGES
	DEBUG(SHOW_BUFFER_CONTENTS, "\ntx %p ", skb->data);
	display_buffer((char *) skb->data, skb->len);
#endif

	/* map the skb buffer to pci memory for DMA operation */
	pci_map_address = pci_map_single(priv->pdev,
					 (void *) skb->data, skb->len,
					 PCI_DMA_TODEVICE);
	if (unlikely(pci_map_address == 0)) {
		printk(KERN_WARNING "%s: cannot map buffer to PCI\n",
		       ndev->name);

		err = -EIO;
		goto drop_free;
	}
	/* Place the fragment in the control block structure. */
	index = curr_frag % ISL38XX_CB_TX_QSIZE;
	fragment = &cb->tx_data_low[index];

	priv->pci_map_tx_address[index] = pci_map_address;
	/* store the skb address for future freeing  */
	priv->data_low_tx[index] = skb;
	/* set the proper fragment start address and size information */
	frame_size = skb->len;
	fragment->size = cpu_to_le16(frame_size);
	fragment->flags = cpu_to_le16(0);	/* set to 1 if more fragments */
	fragment->address = cpu_to_le32(pci_map_address);
	curr_frag++;

	/* The fragment address in the control block must have been
	 * written before announcing the frame buffer to device. */
	wmb();
	cb->driver_curr_frag[ISL38XX_CB_TX_DATA_LQ] = cpu_to_le32(curr_frag);

	if (curr_frag - priv->free_data_tx + ISL38XX_MIN_QTHRESHOLD
	    > ISL38XX_CB_TX_QSIZE) {
		/* stop sends from upper layers */
		netif_stop_queue(ndev);

		/* set the full flag for the transmission queue */
		priv->data_low_tx_full = 1;
	}

	/* set the transmission time */
	ndev->trans_start = jiffies;
	priv->statistics.tx_packets++;
	priv->statistics.tx_bytes += skb->len;

	/* trigger the device */
	islpci_trigger(priv);

	/* unlock the driver code */
	spin_unlock_irqrestore(&priv->slock, flags);

	return 0;

      drop_free:
	priv->statistics.tx_dropped++;
	spin_unlock_irqrestore(&priv->slock, flags);
	dev_kfree_skb(skb);
	return err;
}