Esempio n. 1
0
static void fjes_raise_intr_rxdata_task(struct work_struct *work)
{
	struct fjes_adapter *adapter = container_of(work,
			struct fjes_adapter, raise_intr_rxdata_task);
	struct fjes_hw *hw = &adapter->hw;
	enum ep_partner_status pstatus;
	int max_epid, my_epid, epid;

	my_epid = hw->my_epid;
	max_epid = hw->max_epid;

	for (epid = 0; epid < max_epid; epid++)
		hw->ep_shm_info[epid].tx_status_work = 0;

	for (epid = 0; epid < max_epid; epid++) {
		if (epid == my_epid)
			continue;

		pstatus = fjes_hw_get_partner_ep_status(hw, epid);
		if (pstatus == EP_PARTNER_SHARED) {
			hw->ep_shm_info[epid].tx_status_work =
				hw->ep_shm_info[epid].tx.info->v1i.tx_status;

			if (hw->ep_shm_info[epid].tx_status_work ==
				FJES_TX_DELAY_SEND_PENDING) {
				hw->ep_shm_info[epid].tx.info->v1i.tx_status =
					FJES_TX_DELAY_SEND_NONE;
			}
		}
	}

	for (epid = 0; epid < max_epid; epid++) {
		if (epid == my_epid)
			continue;

		pstatus = fjes_hw_get_partner_ep_status(hw, epid);
		if ((hw->ep_shm_info[epid].tx_status_work ==
		     FJES_TX_DELAY_SEND_PENDING) &&
		    (pstatus == EP_PARTNER_SHARED) &&
		    !(hw->ep_shm_info[epid].rx.info->v1i.rx_status &
		      FJES_RX_POLL_WORK)) {
			fjes_hw_raise_interrupt(hw, epid,
						REG_ICTL_MASK_RX_DATA);
			hw->ep_shm_info[epid].ep_stats.send_intr_rx += 1;
		}
	}

	usleep_range(500, 1000);
}
Esempio n. 2
0
void fjes_hw_raise_epstop(struct fjes_hw *hw)
{
	enum ep_partner_status status;
	int epidx;

	for (epidx = 0; epidx < hw->max_epid; epidx++) {
		if (epidx == hw->my_epid)
			continue;

		status = fjes_hw_get_partner_ep_status(hw, epidx);
		switch (status) {
		case EP_PARTNER_SHARED:
			fjes_hw_raise_interrupt(hw, epidx,
						REG_ICTL_MASK_TXRX_STOP_REQ);
			break;
		default:
			break;
		}

		set_bit(epidx, &hw->hw_info.buffer_unshare_reserve_bit);
		set_bit(epidx, &hw->txrx_stop_req_bit);

		hw->ep_shm_info[epidx].tx.info->v1i.rx_status |=
				FJES_RX_STOP_REQ_REQUEST;
	}
}
Esempio n. 3
0
static void fjes_stop_req_irq(struct fjes_adapter *adapter, int src_epid)
{
	struct fjes_hw *hw = &adapter->hw;
	enum ep_partner_status status;
	unsigned long flags;

	set_bit(src_epid, &hw->hw_info.buffer_unshare_reserve_bit);

	status = fjes_hw_get_partner_ep_status(hw, src_epid);
	trace_fjes_stop_req_irq_pre(hw, src_epid, status);
	switch (status) {
	case EP_PARTNER_WAITING:
		spin_lock_irqsave(&hw->rx_status_lock, flags);
		hw->ep_shm_info[src_epid].tx.info->v1i.rx_status |=
				FJES_RX_STOP_REQ_DONE;
		spin_unlock_irqrestore(&hw->rx_status_lock, flags);
		clear_bit(src_epid, &hw->txrx_stop_req_bit);
		/* fall through */
	case EP_PARTNER_UNSHARE:
	case EP_PARTNER_COMPLETE:
	default:
		set_bit(src_epid, &adapter->unshare_watch_bitmask);
		if (!work_pending(&adapter->unshare_watch_task))
			queue_work(adapter->control_wq,
				   &adapter->unshare_watch_task);
		break;
	case EP_PARTNER_SHARED:
		set_bit(src_epid, &hw->epstop_req_bit);

		if (!work_pending(&hw->epstop_task))
			queue_work(adapter->control_wq, &hw->epstop_task);
		break;
	}
	trace_fjes_stop_req_irq_post(hw, src_epid);
}
Esempio n. 4
0
static int fjes_rxframe_search_exist(struct fjes_adapter *adapter,
				     int start_epid)
{
	struct fjes_hw *hw = &adapter->hw;
	enum ep_partner_status pstatus;
	int max_epid, cur_epid;
	int i;

	max_epid = hw->max_epid;
	start_epid = (start_epid + 1 + max_epid) % max_epid;

	for (i = 0; i < max_epid; i++) {
		cur_epid = (start_epid + i) % max_epid;
		if (cur_epid == hw->my_epid)
			continue;

		pstatus = fjes_hw_get_partner_ep_status(hw, cur_epid);
		if (pstatus == EP_PARTNER_SHARED) {
			if (!fjes_hw_epbuf_rx_is_empty(
				&hw->ep_shm_info[cur_epid].rx))
				return cur_epid;
		}
	}
	return -1;
}
Esempio n. 5
0
static void fjes_tx_stall_task(struct work_struct *work)
{
	struct fjes_adapter *adapter = container_of(work,
			struct fjes_adapter, tx_stall_task);
	struct net_device *netdev = adapter->netdev;
	struct fjes_hw *hw = &adapter->hw;
	int all_queue_available, sendable;
	enum ep_partner_status pstatus;
	int max_epid, my_epid, epid;
	union ep_buffer_info *info;
	int i;

	if (((long)jiffies -
		dev_trans_start(netdev)) > FJES_TX_TX_STALL_TIMEOUT) {
		netif_wake_queue(netdev);
		return;
	}

	my_epid = hw->my_epid;
	max_epid = hw->max_epid;

	for (i = 0; i < 5; i++) {
		all_queue_available = 1;

		for (epid = 0; epid < max_epid; epid++) {
			if (my_epid == epid)
				continue;

			pstatus = fjes_hw_get_partner_ep_status(hw, epid);
			sendable = (pstatus == EP_PARTNER_SHARED);
			if (!sendable)
				continue;

			info = adapter->hw.ep_shm_info[epid].tx.info;

			if (!(info->v1i.rx_status & FJES_RX_MTU_CHANGING_DONE))
				return;

			if (EP_RING_FULL(info->v1i.head, info->v1i.tail,
					 info->v1i.count_max)) {
				all_queue_available = 0;
				break;
			}
		}

		if (all_queue_available) {
			netif_wake_queue(netdev);
			return;
		}
	}

	usleep_range(50, 100);

	queue_work(adapter->txrx_wq, &adapter->tx_stall_task);
}
Esempio n. 6
0
/* fjes_close - Disables a network interface */
static int fjes_close(struct net_device *netdev)
{
	struct fjes_adapter *adapter = netdev_priv(netdev);
	struct fjes_hw *hw = &adapter->hw;
	unsigned long flags;
	int epidx;

	netif_tx_stop_all_queues(netdev);
	netif_carrier_off(netdev);

	fjes_hw_raise_epstop(hw);

	napi_disable(&adapter->napi);

	spin_lock_irqsave(&hw->rx_status_lock, flags);
	for (epidx = 0; epidx < hw->max_epid; epidx++) {
		if (epidx == hw->my_epid)
			continue;

		if (fjes_hw_get_partner_ep_status(hw, epidx) ==
		    EP_PARTNER_SHARED)
			adapter->hw.ep_shm_info[epidx]
				   .tx.info->v1i.rx_status &=
				~FJES_RX_POLL_WORK;
	}
	spin_unlock_irqrestore(&hw->rx_status_lock, flags);

	fjes_free_irq(adapter);

	cancel_delayed_work_sync(&adapter->interrupt_watch_task);
	cancel_work_sync(&adapter->unshare_watch_task);
	adapter->unshare_watch_bitmask = 0;
	cancel_work_sync(&adapter->raise_intr_rxdata_task);
	cancel_work_sync(&adapter->tx_stall_task);

	cancel_work_sync(&hw->update_zone_task);
	cancel_work_sync(&hw->epstop_task);

	fjes_hw_wait_epstop(hw);

	fjes_free_resources(adapter);

	return 0;
}
Esempio n. 7
0
static int fjes_dbg_status_show(struct seq_file *m, void *v)
{
	struct fjes_adapter *adapter = m->private;
	struct fjes_hw *hw = &adapter->hw;
	int max_epid = hw->max_epid;
	int my_epid = hw->my_epid;
	int epidx;

	seq_puts(m, "EPID\tSTATUS           SAME_ZONE        CONNECTED\n");
	for (epidx = 0; epidx < max_epid; epidx++) {
		if (epidx == my_epid) {
			seq_printf(m, "ep%d\t%-16c %-16c %-16c\n",
				   epidx, '-', '-', '-');
		} else {
			seq_printf(m, "ep%d\t%-16s %-16c %-16c\n",
				   epidx,
				   ep_status_string[fjes_hw_get_partner_ep_status(hw, epidx)],
				   fjes_hw_epid_is_same_zone(hw, epidx) ? 'Y' : 'N',
				   fjes_hw_epid_is_shared(hw->hw_info.share, epidx) ? 'Y' : 'N');
		}
	}

	return 0;
}
Esempio n. 8
0
static void fjes_hw_update_zone_task(struct work_struct *work)
{
	struct fjes_hw *hw = container_of(work,
			struct fjes_hw, update_zone_task);

	struct my_s {u8 es_status; u8 zone; } *info;
	union fjes_device_command_res *res_buf;
	enum ep_partner_status pstatus;

	struct fjes_adapter *adapter;
	struct net_device *netdev;

	ulong unshare_bit = 0;
	ulong share_bit = 0;
	ulong irq_bit = 0;

	int epidx;
	int ret;

	adapter = (struct fjes_adapter *)hw->back;
	netdev = adapter->netdev;
	res_buf = hw->hw_info.res_buf;
	info = (struct my_s *)&res_buf->info.info;

	mutex_lock(&hw->hw_info.lock);

	ret = fjes_hw_request_info(hw);
	switch (ret) {
	case -ENOMSG:
	case -EBUSY:
	default:
		if (!work_pending(&adapter->force_close_task)) {
			adapter->force_reset = true;
			schedule_work(&adapter->force_close_task);
		}
		break;

	case 0:

		for (epidx = 0; epidx < hw->max_epid; epidx++) {
			if (epidx == hw->my_epid) {
				hw->ep_shm_info[epidx].es_status =
					info[epidx].es_status;
				hw->ep_shm_info[epidx].zone =
					info[epidx].zone;
				continue;
			}

			pstatus = fjes_hw_get_partner_ep_status(hw, epidx);
			switch (pstatus) {
			case EP_PARTNER_UNSHARE:
			default:
				if ((info[epidx].zone !=
					FJES_ZONING_ZONE_TYPE_NONE) &&
				    (info[epidx].es_status ==
					FJES_ZONING_STATUS_ENABLE) &&
				    (info[epidx].zone ==
					info[hw->my_epid].zone))
					set_bit(epidx, &share_bit);
				else
					set_bit(epidx, &unshare_bit);
				break;

			case EP_PARTNER_COMPLETE:
			case EP_PARTNER_WAITING:
				if ((info[epidx].zone ==
					FJES_ZONING_ZONE_TYPE_NONE) ||
				    (info[epidx].es_status !=
					FJES_ZONING_STATUS_ENABLE) ||
				    (info[epidx].zone !=
					info[hw->my_epid].zone)) {
					set_bit(epidx,
						&adapter->unshare_watch_bitmask);
					set_bit(epidx,
						&hw->hw_info.buffer_unshare_reserve_bit);
				}
				break;

			case EP_PARTNER_SHARED:
				if ((info[epidx].zone ==
					FJES_ZONING_ZONE_TYPE_NONE) ||
				    (info[epidx].es_status !=
					FJES_ZONING_STATUS_ENABLE) ||
				    (info[epidx].zone !=
					info[hw->my_epid].zone))
					set_bit(epidx, &irq_bit);
				break;
			}

			hw->ep_shm_info[epidx].es_status =
				info[epidx].es_status;
			hw->ep_shm_info[epidx].zone = info[epidx].zone;
		}
		break;
	}

	mutex_unlock(&hw->hw_info.lock);

	for (epidx = 0; epidx < hw->max_epid; epidx++) {
		if (epidx == hw->my_epid)
			continue;

		if (test_bit(epidx, &share_bit)) {
			fjes_hw_setup_epbuf(&hw->ep_shm_info[epidx].tx,
					    netdev->dev_addr, netdev->mtu);

			mutex_lock(&hw->hw_info.lock);

			ret = fjes_hw_register_buff_addr(
				hw, epidx, &hw->ep_shm_info[epidx]);

			switch (ret) {
			case 0:
				break;
			case -ENOMSG:
			case -EBUSY:
			default:
				if (!work_pending(&adapter->force_close_task)) {
					adapter->force_reset = true;
					schedule_work(
					  &adapter->force_close_task);
				}
				break;
			}
			mutex_unlock(&hw->hw_info.lock);
		}

		if (test_bit(epidx, &unshare_bit)) {
			mutex_lock(&hw->hw_info.lock);

			ret = fjes_hw_unregister_buff_addr(hw, epidx);

			switch (ret) {
			case 0:
				break;
			case -ENOMSG:
			case -EBUSY:
			default:
				if (!work_pending(&adapter->force_close_task)) {
					adapter->force_reset = true;
					schedule_work(
					  &adapter->force_close_task);
				}
				break;
			}

			mutex_unlock(&hw->hw_info.lock);

			if (ret == 0)
				fjes_hw_setup_epbuf(
					&hw->ep_shm_info[epidx].tx,
					netdev->dev_addr, netdev->mtu);
		}

		if (test_bit(epidx, &irq_bit)) {
			fjes_hw_raise_interrupt(hw, epidx,
						REG_ICTL_MASK_TXRX_STOP_REQ);

			set_bit(epidx, &hw->txrx_stop_req_bit);
			hw->ep_shm_info[epidx].tx.
				info->v1i.rx_status |=
					FJES_RX_STOP_REQ_REQUEST;
			set_bit(epidx, &hw->hw_info.buffer_unshare_reserve_bit);
		}
	}

	if (irq_bit || adapter->unshare_watch_bitmask) {
		if (!work_pending(&adapter->unshare_watch_task))
			queue_work(adapter->control_wq,
				   &adapter->unshare_watch_task);
	}
}
Esempio n. 9
0
static netdev_tx_t
fjes_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
{
	struct fjes_adapter *adapter = netdev_priv(netdev);
	struct fjes_hw *hw = &adapter->hw;

	int max_epid, my_epid, dest_epid;
	enum ep_partner_status pstatus;
	struct netdev_queue *cur_queue;
	char shortpkt[VLAN_ETH_HLEN];
	bool is_multi, vlan;
	struct ethhdr *eth;
	u16 queue_no = 0;
	u16 vlan_id = 0;
	netdev_tx_t ret;
	char *data;
	int len;

	ret = NETDEV_TX_OK;
	is_multi = false;
	cur_queue = netdev_get_tx_queue(netdev, queue_no);

	eth = (struct ethhdr *)skb->data;
	my_epid = hw->my_epid;

	vlan = (vlan_get_tag(skb, &vlan_id) == 0) ? true : false;

	data = skb->data;
	len = skb->len;

	if (is_multicast_ether_addr(eth->h_dest)) {
		dest_epid = 0;
		max_epid = hw->max_epid;
		is_multi = true;
	} else if (is_local_ether_addr(eth->h_dest)) {
		dest_epid = eth->h_dest[ETH_ALEN - 1];
		max_epid = dest_epid + 1;

		if ((eth->h_dest[0] == 0x02) &&
		    (0x00 == (eth->h_dest[1] | eth->h_dest[2] |
			      eth->h_dest[3] | eth->h_dest[4])) &&
		    (dest_epid < hw->max_epid)) {
			;
		} else {
			dest_epid = 0;
			max_epid = 0;
			ret = NETDEV_TX_OK;

			adapter->stats64.tx_packets += 1;
			hw->ep_shm_info[my_epid].net_stats.tx_packets += 1;
			adapter->stats64.tx_bytes += len;
			hw->ep_shm_info[my_epid].net_stats.tx_bytes += len;
		}
	} else {
		dest_epid = 0;
		max_epid = 0;
		ret = NETDEV_TX_OK;

		adapter->stats64.tx_packets += 1;
		hw->ep_shm_info[my_epid].net_stats.tx_packets += 1;
		adapter->stats64.tx_bytes += len;
		hw->ep_shm_info[my_epid].net_stats.tx_bytes += len;
	}

	for (; dest_epid < max_epid; dest_epid++) {
		if (my_epid == dest_epid)
			continue;

		pstatus = fjes_hw_get_partner_ep_status(hw, dest_epid);
		if (pstatus != EP_PARTNER_SHARED) {
			if (!is_multi)
				hw->ep_shm_info[dest_epid].ep_stats
					.tx_dropped_not_shared += 1;
			ret = NETDEV_TX_OK;
		} else if (!fjes_hw_check_epbuf_version(
				&adapter->hw.ep_shm_info[dest_epid].rx, 0)) {
			/* version is NOT 0 */
			adapter->stats64.tx_carrier_errors += 1;
			hw->ep_shm_info[dest_epid].net_stats
						.tx_carrier_errors += 1;
			hw->ep_shm_info[dest_epid].ep_stats
					.tx_dropped_ver_mismatch += 1;

			ret = NETDEV_TX_OK;
		} else if (!fjes_hw_check_mtu(
				&adapter->hw.ep_shm_info[dest_epid].rx,
				netdev->mtu)) {
			adapter->stats64.tx_dropped += 1;
			hw->ep_shm_info[dest_epid].net_stats.tx_dropped += 1;
			adapter->stats64.tx_errors += 1;
			hw->ep_shm_info[dest_epid].net_stats.tx_errors += 1;
			hw->ep_shm_info[dest_epid].ep_stats
					.tx_dropped_buf_size_mismatch += 1;

			ret = NETDEV_TX_OK;
		} else if (vlan &&
			   !fjes_hw_check_vlan_id(
				&adapter->hw.ep_shm_info[dest_epid].rx,
				vlan_id)) {
			hw->ep_shm_info[dest_epid].ep_stats
				.tx_dropped_vlanid_mismatch += 1;
			ret = NETDEV_TX_OK;
		} else {
			if (len < VLAN_ETH_HLEN) {
				memset(shortpkt, 0, VLAN_ETH_HLEN);
				memcpy(shortpkt, skb->data, skb->len);
				len = VLAN_ETH_HLEN;
				data = shortpkt;
			}

			if (adapter->tx_retry_count == 0) {
				adapter->tx_start_jiffies = jiffies;
				adapter->tx_retry_count = 1;
			} else {
				adapter->tx_retry_count++;
			}

			if (fjes_tx_send(adapter, dest_epid, data, len)) {
				if (is_multi) {
					ret = NETDEV_TX_OK;
				} else if (
					   ((long)jiffies -
					    (long)adapter->tx_start_jiffies) >=
					    FJES_TX_RETRY_TIMEOUT) {
					adapter->stats64.tx_fifo_errors += 1;
					hw->ep_shm_info[dest_epid].net_stats
								.tx_fifo_errors += 1;
					adapter->stats64.tx_errors += 1;
					hw->ep_shm_info[dest_epid].net_stats
								.tx_errors += 1;

					ret = NETDEV_TX_OK;
				} else {
					netif_trans_update(netdev);
					hw->ep_shm_info[dest_epid].ep_stats
						.tx_buffer_full += 1;
					netif_tx_stop_queue(cur_queue);

					if (!work_pending(&adapter->tx_stall_task))
						queue_work(adapter->txrx_wq,
							   &adapter->tx_stall_task);

					ret = NETDEV_TX_BUSY;
				}
			} else {
				if (!is_multi) {
					adapter->stats64.tx_packets += 1;
					hw->ep_shm_info[dest_epid].net_stats
								.tx_packets += 1;
					adapter->stats64.tx_bytes += len;
					hw->ep_shm_info[dest_epid].net_stats
								.tx_bytes += len;
				}

				adapter->tx_retry_count = 0;
				ret = NETDEV_TX_OK;
			}
		}
	}

	if (ret == NETDEV_TX_OK) {
		dev_kfree_skb(skb);
		if (is_multi) {
			adapter->stats64.tx_packets += 1;
			hw->ep_shm_info[my_epid].net_stats.tx_packets += 1;
			adapter->stats64.tx_bytes += 1;
			hw->ep_shm_info[my_epid].net_stats.tx_bytes += len;
		}
	}

	return ret;
}
Esempio n. 10
0
static int fjes_poll(struct napi_struct *napi, int budget)
{
	struct fjes_adapter *adapter =
			container_of(napi, struct fjes_adapter, napi);
	struct net_device *netdev = napi->dev;
	struct fjes_hw *hw = &adapter->hw;
	struct sk_buff *skb;
	int work_done = 0;
	int cur_epid = 0;
	int epidx;
	size_t frame_len;
	void *frame;

	spin_lock(&hw->rx_status_lock);
	for (epidx = 0; epidx < hw->max_epid; epidx++) {
		if (epidx == hw->my_epid)
			continue;

		if (fjes_hw_get_partner_ep_status(hw, epidx) ==
		    EP_PARTNER_SHARED)
			adapter->hw.ep_shm_info[epidx]
				   .tx.info->v1i.rx_status |= FJES_RX_POLL_WORK;
	}
	spin_unlock(&hw->rx_status_lock);

	while (work_done < budget) {
		prefetch(&adapter->hw);
		frame = fjes_rxframe_get(adapter, &frame_len, &cur_epid);

		if (frame) {
			skb = napi_alloc_skb(napi, frame_len);
			if (!skb) {
				adapter->stats64.rx_dropped += 1;
				hw->ep_shm_info[cur_epid].net_stats
							 .rx_dropped += 1;
				adapter->stats64.rx_errors += 1;
				hw->ep_shm_info[cur_epid].net_stats
							 .rx_errors += 1;
			} else {
				memcpy(skb_put(skb, frame_len),
				       frame, frame_len);
				skb->protocol = eth_type_trans(skb, netdev);
				skb->ip_summed = CHECKSUM_UNNECESSARY;

				netif_receive_skb(skb);

				work_done++;

				adapter->stats64.rx_packets += 1;
				hw->ep_shm_info[cur_epid].net_stats
							 .rx_packets += 1;
				adapter->stats64.rx_bytes += frame_len;
				hw->ep_shm_info[cur_epid].net_stats
							 .rx_bytes += frame_len;

				if (is_multicast_ether_addr(
					((struct ethhdr *)frame)->h_dest)) {
					adapter->stats64.multicast += 1;
					hw->ep_shm_info[cur_epid].net_stats
								 .multicast += 1;
				}
			}

			fjes_rxframe_release(adapter, cur_epid);
			adapter->unset_rx_last = true;
		} else {
			break;
		}
	}

	if (work_done < budget) {
		napi_complete_done(napi, work_done);

		if (adapter->unset_rx_last) {
			adapter->rx_last_jiffies = jiffies;
			adapter->unset_rx_last = false;
		}

		if (((long)jiffies - (long)adapter->rx_last_jiffies) < 3) {
			napi_reschedule(napi);
		} else {
			spin_lock(&hw->rx_status_lock);
			for (epidx = 0; epidx < hw->max_epid; epidx++) {
				if (epidx == hw->my_epid)
					continue;
				if (fjes_hw_get_partner_ep_status(hw, epidx) ==
				    EP_PARTNER_SHARED)
					adapter->hw.ep_shm_info[epidx].tx
						   .info->v1i.rx_status &=
						~FJES_RX_POLL_WORK;
			}
			spin_unlock(&hw->rx_status_lock);

			fjes_hw_set_irqmask(hw, REG_ICTL_MASK_RX_DATA, false);
		}
	}

	return work_done;
}