Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ef1ea0b4 authored by Olof Johansson's avatar Olof Johansson Committed by David S. Miller
Browse files

pasemi_mac: add support for setting MTU



Currently keeping it at 1500 bytes or below since jumbo frames need
special checksum offload on TX.

Signed-off-by: default avatarOlof Johansson <olof@lixom.net>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5cea73b0
Loading
Loading
Loading
Loading
+158 −72
Original line number Diff line number Diff line
@@ -62,6 +62,10 @@

#define LRO_MAX_AGGR 64

#define PE_MIN_MTU	64
#define PE_MAX_MTU	1500
#define PE_DEF_MTU	ETH_DATA_LEN

#define DEFAULT_MSG_ENABLE	  \
	(NETIF_MSG_DRV		| \
	 NETIF_MSG_PROBE	| \
@@ -82,8 +86,6 @@
				 & ((ring)->size - 1))
#define RING_AVAIL(ring)	((ring->size) - RING_USED(ring))

#define BUF_SIZE 1646 /* 1500 MTU + ETH_HLEN + VLAN_HLEN + 2 64B cachelines */

MODULE_LICENSE("GPL");
MODULE_AUTHOR ("Olof Johansson <olof@lixom.net>");
MODULE_DESCRIPTION("PA Semi PWRficient Ethernet driver");
@@ -175,6 +177,24 @@ static int mac_to_intf(struct pasemi_mac *mac)
	return -1;
}

static void pasemi_mac_intf_disable(struct pasemi_mac *mac)
{
	unsigned int flags;

	flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG);
	flags &= ~PAS_MAC_CFG_PCFG_PE;
	write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags);
}

static void pasemi_mac_intf_enable(struct pasemi_mac *mac)
{
	unsigned int flags;

	flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG);
	flags |= PAS_MAC_CFG_PCFG_PE;
	write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags);
}

static int pasemi_get_mac_addr(struct pasemi_mac *mac)
{
	struct pci_dev *pdev = mac->pdev;
@@ -480,7 +500,7 @@ static void pasemi_mac_free_tx_resources(struct pasemi_mac *mac)

}

static void pasemi_mac_free_rx_resources(struct pasemi_mac *mac)
static void pasemi_mac_free_rx_buffers(struct pasemi_mac *mac)
{
	struct pasemi_mac_rxring *rx = rx_ring(mac);
	unsigned int i;
@@ -500,7 +520,12 @@ static void pasemi_mac_free_rx_resources(struct pasemi_mac *mac)
	}

	for (i = 0; i < RX_RING_SIZE; i++)
		RX_DESC(rx, i) = 0;
		RX_BUFF(rx, i) = 0;
}

static void pasemi_mac_free_rx_resources(struct pasemi_mac *mac)
{
	pasemi_mac_free_rx_buffers(mac);

	dma_free_coherent(&mac->dma_pdev->dev, RX_RING_SIZE * sizeof(u64),
			  rx_ring(mac)->buffers, rx_ring(mac)->buf_dma);
@@ -530,14 +555,14 @@ static void pasemi_mac_replenish_rx_ring(const struct net_device *dev,
		/* Entry in use? */
		WARN_ON(*buff);

		skb = dev_alloc_skb(BUF_SIZE);
		skb = dev_alloc_skb(mac->bufsz);
		skb_reserve(skb, LOCAL_SKB_ALIGN);

		if (unlikely(!skb))
			break;

		dma = pci_map_single(mac->dma_pdev, skb->data,
				     BUF_SIZE - LOCAL_SKB_ALIGN,
				     mac->bufsz - LOCAL_SKB_ALIGN,
				     PCI_DMA_FROMDEVICE);

		if (unlikely(dma_mapping_error(dma))) {
@@ -547,7 +572,7 @@ static void pasemi_mac_replenish_rx_ring(const struct net_device *dev,

		info->skb = skb;
		info->dma = dma;
		*buff = XCT_RXB_LEN(BUF_SIZE) | XCT_RXB_ADDR(dma);
		*buff = XCT_RXB_LEN(mac->bufsz) | XCT_RXB_ADDR(dma);
		fill++;
	}

@@ -677,7 +702,7 @@ static int pasemi_mac_clean_rx(struct pasemi_mac_rxring *rx,

		len = (macrx & XCT_MACRX_LLEN_M) >> XCT_MACRX_LLEN_S;

		pci_unmap_single(pdev, dma, BUF_SIZE-LOCAL_SKB_ALIGN,
		pci_unmap_single(pdev, dma, mac->bufsz - LOCAL_SKB_ALIGN,
				 PCI_DMA_FROMDEVICE);

		if (macrx & XCT_MACRX_CRC) {
@@ -901,24 +926,6 @@ static irqreturn_t pasemi_mac_tx_intr(int irq, void *data)
	return IRQ_HANDLED;
}

static void pasemi_mac_intf_disable(struct pasemi_mac *mac)
{
	unsigned int flags;

	flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG);
	flags &= ~PAS_MAC_CFG_PCFG_PE;
	write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags);
}

static void pasemi_mac_intf_enable(struct pasemi_mac *mac)
{
	unsigned int flags;

	flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG);
	flags |= PAS_MAC_CFG_PCFG_PE;
	write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags);
}

static void pasemi_adjust_link(struct net_device *dev)
{
	struct pasemi_mac *mac = netdev_priv(dev);
@@ -1175,11 +1182,71 @@ static int pasemi_mac_open(struct net_device *dev)

#define MAX_RETRIES 5000

static void pasemi_mac_pause_txchan(struct pasemi_mac *mac)
{
	unsigned int sta, retries;
	int txch = tx_ring(mac)->chan.chno;

	write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch),
		      PAS_DMA_TXCHAN_TCMDSTA_ST);

	for (retries = 0; retries < MAX_RETRIES; retries++) {
		sta = read_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch));
		if (!(sta & PAS_DMA_TXCHAN_TCMDSTA_ACT))
			break;
		cond_resched();
	}

	if (sta & PAS_DMA_TXCHAN_TCMDSTA_ACT)
		dev_err(&mac->dma_pdev->dev,
			"Failed to stop tx channel, tcmdsta %08x\n", sta);

	write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch), 0);
}

static void pasemi_mac_pause_rxchan(struct pasemi_mac *mac)
{
	unsigned int sta, retries;
	int rxch = rx_ring(mac)->chan.chno;

	write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch),
		      PAS_DMA_RXCHAN_CCMDSTA_ST);
	for (retries = 0; retries < MAX_RETRIES; retries++) {
		sta = read_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch));
		if (!(sta & PAS_DMA_RXCHAN_CCMDSTA_ACT))
			break;
		cond_resched();
	}

	if (sta & PAS_DMA_RXCHAN_CCMDSTA_ACT)
		dev_err(&mac->dma_pdev->dev,
			"Failed to stop rx channel, ccmdsta 08%x\n", sta);
	write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch), 0);
}

static void pasemi_mac_pause_rxint(struct pasemi_mac *mac)
{
	unsigned int sta, retries;

	write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if),
		      PAS_DMA_RXINT_RCMDSTA_ST);
	for (retries = 0; retries < MAX_RETRIES; retries++) {
		sta = read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if));
		if (!(sta & PAS_DMA_RXINT_RCMDSTA_ACT))
			break;
		cond_resched();
	}

	if (sta & PAS_DMA_RXINT_RCMDSTA_ACT)
		dev_err(&mac->dma_pdev->dev,
			"Failed to stop rx interface, rcmdsta %08x\n", sta);
	write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if), 0);
}

static int pasemi_mac_close(struct net_device *dev)
{
	struct pasemi_mac *mac = netdev_priv(dev);
	unsigned int sta;
	int retries;
	int rxch, txch;

	rxch = rx_ring(mac)->chan.chno;
@@ -1217,51 +1284,9 @@ static int pasemi_mac_close(struct net_device *dev)
	pasemi_mac_clean_tx(tx_ring(mac));
	pasemi_mac_clean_rx(rx_ring(mac), RX_RING_SIZE);

	/* Disable interface */
	write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch),
		      PAS_DMA_TXCHAN_TCMDSTA_ST);
	write_dma_reg( PAS_DMA_RXINT_RCMDSTA(mac->dma_if),
		      PAS_DMA_RXINT_RCMDSTA_ST);
	write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch),
		      PAS_DMA_RXCHAN_CCMDSTA_ST);

	for (retries = 0; retries < MAX_RETRIES; retries++) {
		sta = read_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(rxch));
		if (!(sta & PAS_DMA_TXCHAN_TCMDSTA_ACT))
			break;
		cond_resched();
	}

	if (sta & PAS_DMA_TXCHAN_TCMDSTA_ACT)
		dev_err(&mac->dma_pdev->dev, "Failed to stop tx channel\n");

	for (retries = 0; retries < MAX_RETRIES; retries++) {
		sta = read_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch));
		if (!(sta & PAS_DMA_RXCHAN_CCMDSTA_ACT))
			break;
		cond_resched();
	}

	if (sta & PAS_DMA_RXCHAN_CCMDSTA_ACT)
		dev_err(&mac->dma_pdev->dev, "Failed to stop rx channel\n");

	for (retries = 0; retries < MAX_RETRIES; retries++) {
		sta = read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if));
		if (!(sta & PAS_DMA_RXINT_RCMDSTA_ACT))
			break;
		cond_resched();
	}

	if (sta & PAS_DMA_RXINT_RCMDSTA_ACT)
		dev_err(&mac->dma_pdev->dev, "Failed to stop rx interface\n");

	/* Then, disable the channel. This must be done separately from
	 * stopping, since you can't disable when active.
	 */

	write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch), 0);
	write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch), 0);
	write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if), 0);
	pasemi_mac_pause_txchan(mac);
	pasemi_mac_pause_rxint(mac);
	pasemi_mac_pause_rxchan(mac);

	free_irq(mac->tx->chan.irq, mac->tx);
	free_irq(mac->rx->chan.irq, mac->rx);
@@ -1415,6 +1440,62 @@ static int pasemi_mac_poll(struct napi_struct *napi, int budget)
	return pkts;
}

static int pasemi_mac_change_mtu(struct net_device *dev, int new_mtu)
{
	struct pasemi_mac *mac = netdev_priv(dev);
	unsigned int reg;
	unsigned int rcmdsta;
	int running;

	if (new_mtu < PE_MIN_MTU || new_mtu > PE_MAX_MTU)
		return -EINVAL;

	running = netif_running(dev);

	if (running) {
		/* Need to stop the interface, clean out all already
		 * received buffers, free all unused buffers on the RX
		 * interface ring, then finally re-fill the rx ring with
		 * the new-size buffers and restart.
		 */

		napi_disable(&mac->napi);
		netif_tx_disable(dev);
		pasemi_mac_intf_disable(mac);

		rcmdsta = read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if));
		pasemi_mac_pause_rxint(mac);
		pasemi_mac_clean_rx(rx_ring(mac), RX_RING_SIZE);
		pasemi_mac_free_rx_buffers(mac);
	}

	/* Change maxf, i.e. what size frames are accepted.
	 * Need room for ethernet header and CRC word
	 */
	reg = read_mac_reg(mac, PAS_MAC_CFG_MACCFG);
	reg &= ~PAS_MAC_CFG_MACCFG_MAXF_M;
	reg |= PAS_MAC_CFG_MACCFG_MAXF(new_mtu + ETH_HLEN + 4);
	write_mac_reg(mac, PAS_MAC_CFG_MACCFG, reg);

	dev->mtu = new_mtu;
	/* MTU + ETH_HLEN + VLAN_HLEN + 2 64B cachelines */
	mac->bufsz = new_mtu + ETH_HLEN + ETH_FCS_LEN + LOCAL_SKB_ALIGN + 128;

	if (running) {
		write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if),
			      rcmdsta | PAS_DMA_RXINT_RCMDSTA_EN);

		rx_ring(mac)->next_to_fill = 0;
		pasemi_mac_replenish_rx_ring(dev, RX_RING_SIZE-1);

		napi_enable(&mac->napi);
		netif_start_queue(dev);
		pasemi_mac_intf_enable(mac);
	}

	return 0;
}

static int __devinit
pasemi_mac_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
@@ -1503,6 +1584,11 @@ pasemi_mac_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
	dev->hard_start_xmit = pasemi_mac_start_tx;
	dev->set_multicast_list = pasemi_mac_set_rx_mode;
	dev->set_mac_address = pasemi_mac_set_mac_addr;
	dev->mtu = PE_DEF_MTU;
	/* 1500 MTU + ETH_HLEN + VLAN_HLEN + 2 64B cachelines */
	mac->bufsz = dev->mtu + ETH_HLEN + ETH_FCS_LEN + LOCAL_SKB_ALIGN + 128;

	dev->change_mtu = pasemi_mac_change_mtu;

	if (err)
		goto out;
+14 −0
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ struct pasemi_mac {
	struct phy_device *phydev;
	struct napi_struct napi;

	int		bufsz; /* RX ring buffer size */
	u8		type;
#define MAC_TYPE_GMAC	1
#define MAC_TYPE_XAUI	2
@@ -96,6 +97,7 @@ struct pasemi_mac_buffer {
/* MAC CFG register offsets */
enum {
	PAS_MAC_CFG_PCFG = 0x80,
	PAS_MAC_CFG_MACCFG = 0x84,
	PAS_MAC_CFG_ADR0 = 0x8c,
	PAS_MAC_CFG_ADR1 = 0x90,
	PAS_MAC_CFG_TXP = 0x98,
@@ -132,6 +134,18 @@ enum {
#define PAS_MAC_CFG_PCFG_SPD_100M	0x00000001
#define PAS_MAC_CFG_PCFG_SPD_1G		0x00000002
#define PAS_MAC_CFG_PCFG_SPD_10G	0x00000003

#define PAS_MAC_CFG_MACCFG_TXT_M	0x70000000
#define PAS_MAC_CFG_MACCFG_TXT_S	28
#define PAS_MAC_CFG_MACCFG_PRES_M	0x0f000000
#define PAS_MAC_CFG_MACCFG_PRES_S	24
#define PAS_MAC_CFG_MACCFG_MAXF_M	0x00ffff00
#define PAS_MAC_CFG_MACCFG_MAXF_S	8
#define PAS_MAC_CFG_MACCFG_MAXF(x)	(((x) << PAS_MAC_CFG_MACCFG_MAXF_S) & \
					 PAS_MAC_CFG_MACCFG_MAXF_M)
#define PAS_MAC_CFG_MACCFG_MINF_M	0x000000ff
#define PAS_MAC_CFG_MACCFG_MINF_S	0

#define PAS_MAC_CFG_TXP_FCF		0x01000000
#define PAS_MAC_CFG_TXP_FCE		0x00800000
#define PAS_MAC_CFG_TXP_FC		0x00400000