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

Commit d87eb127 authored by Scott Wood's avatar Scott Wood Committed by Kumar Gala
Browse files

gianfar: Add magic packet and suspend/resume support.

parent 7e1cc9c5
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -352,6 +352,9 @@ static int __init gfar_of_init(void)
		else
			gfar_data.interface = PHY_INTERFACE_MODE_MII;

		if (of_get_property(np, "fsl,magic-packet", NULL))
			gfar_data.device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET;

		ph = of_get_property(np, "phy-handle", NULL);
		if (ph == NULL) {
			u32 *fixed_link;
+120 −2
Original line number Diff line number Diff line
@@ -143,6 +143,9 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int l
static void gfar_vlan_rx_register(struct net_device *netdev,
		                struct vlan_group *grp);
void gfar_halt(struct net_device *dev);
#ifdef CONFIG_PM
static void gfar_halt_nodisable(struct net_device *dev);
#endif
void gfar_start(struct net_device *dev);
static void gfar_clear_exact_match(struct net_device *dev);
static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr);
@@ -216,6 +219,7 @@ static int gfar_probe(struct platform_device *pdev)

	spin_lock_init(&priv->txlock);
	spin_lock_init(&priv->rxlock);
	spin_lock_init(&priv->bflock);

	platform_set_drvdata(pdev, dev);

@@ -393,6 +397,103 @@ static int gfar_remove(struct platform_device *pdev)
	return 0;
}

#ifdef CONFIG_PM
static int gfar_suspend(struct platform_device *pdev, pm_message_t state)
{
	struct net_device *dev = platform_get_drvdata(pdev);
	struct gfar_private *priv = netdev_priv(dev);
	unsigned long flags;
	u32 tempval;

	int magic_packet = priv->wol_en &&
		(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);

	netif_device_detach(dev);

	if (netif_running(dev)) {
		spin_lock_irqsave(&priv->txlock, flags);
		spin_lock(&priv->rxlock);

		gfar_halt_nodisable(dev);

		/* Disable Tx, and Rx if wake-on-LAN is disabled. */
		tempval = gfar_read(&priv->regs->maccfg1);

		tempval &= ~MACCFG1_TX_EN;

		if (!magic_packet)
			tempval &= ~MACCFG1_RX_EN;

		gfar_write(&priv->regs->maccfg1, tempval);

		spin_unlock(&priv->rxlock);
		spin_unlock_irqrestore(&priv->txlock, flags);

#ifdef CONFIG_GFAR_NAPI
		napi_disable(&priv->napi);
#endif

		if (magic_packet) {
			/* Enable interrupt on Magic Packet */
			gfar_write(&priv->regs->imask, IMASK_MAG);

			/* Enable Magic Packet mode */
			tempval = gfar_read(&priv->regs->maccfg2);
			tempval |= MACCFG2_MPEN;
			gfar_write(&priv->regs->maccfg2, tempval);
		} else {
			phy_stop(priv->phydev);
		}
	}

	return 0;
}

static int gfar_resume(struct platform_device *pdev)
{
	struct net_device *dev = platform_get_drvdata(pdev);
	struct gfar_private *priv = netdev_priv(dev);
	unsigned long flags;
	u32 tempval;
	int magic_packet = priv->wol_en &&
		(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);

	if (!netif_running(dev)) {
		netif_device_attach(dev);
		return 0;
	}

	if (!magic_packet && priv->phydev)
		phy_start(priv->phydev);

	/* Disable Magic Packet mode, in case something
	 * else woke us up.
	 */

	spin_lock_irqsave(&priv->txlock, flags);
	spin_lock(&priv->rxlock);

	tempval = gfar_read(&priv->regs->maccfg2);
	tempval &= ~MACCFG2_MPEN;
	gfar_write(&priv->regs->maccfg2, tempval);

	gfar_start(dev);

	spin_unlock(&priv->rxlock);
	spin_unlock_irqrestore(&priv->txlock, flags);

	netif_device_attach(dev);

#ifdef CONFIG_GFAR_NAPI
	napi_enable(&priv->napi);
#endif

	return 0;
}
#else
#define gfar_suspend NULL
#define gfar_resume NULL
#endif

/* Reads the controller's registers to determine what interface
 * connects it to the PHY.
@@ -549,8 +650,9 @@ static void init_registers(struct net_device *dev)
}


#ifdef CONFIG_PM
/* Halt the receive and transmit queues */
void gfar_halt(struct net_device *dev)
static void gfar_halt_nodisable(struct net_device *dev)
{
	struct gfar_private *priv = netdev_priv(dev);
	struct gfar __iomem *regs = priv->regs;
@@ -573,6 +675,15 @@ void gfar_halt(struct net_device *dev)
			 (IEVENT_GRSC | IEVENT_GTSC)))
			cpu_relax();
	}
}
#endif

/* Halt the receive and transmit queues */
void gfar_halt(struct net_device *dev)
{
	struct gfar_private *priv = netdev_priv(dev);
	struct gfar __iomem *regs = priv->regs;
	u32 tempval;

	/* Disable Rx and Tx */
	tempval = gfar_read(&regs->maccfg1);
@@ -1969,7 +2080,12 @@ static irqreturn_t gfar_error(int irq, void *dev_id)
	u32 events = gfar_read(&priv->regs->ievent);

	/* Clear IEVENT */
	gfar_write(&priv->regs->ievent, IEVENT_ERR_MASK);
	gfar_write(&priv->regs->ievent, events & IEVENT_ERR_MASK);

	/* Magic Packet is not an error. */
	if ((priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) &&
	    (events & IEVENT_MAG))
		events &= ~IEVENT_MAG;

	/* Hmm... */
	if (netif_msg_rx_err(priv) || netif_msg_tx_err(priv))
@@ -2042,6 +2158,8 @@ MODULE_ALIAS("platform:fsl-gianfar");
static struct platform_driver gfar_driver = {
	.probe = gfar_probe,
	.remove = gfar_remove,
	.suspend = gfar_suspend,
	.resume = gfar_resume,
	.driver	= {
		.name = "fsl-gianfar",
		.owner = THIS_MODULE,
+10 −2
Original line number Diff line number Diff line
@@ -168,6 +168,7 @@ extern const char gfar_driver_version[];
#define MACCFG2_GMII            0x00000200
#define MACCFG2_HUGEFRAME	0x00000020
#define MACCFG2_LENGTHCHECK	0x00000010
#define MACCFG2_MPEN		0x00000008

#define ECNTRL_INIT_SETTINGS	0x00001000
#define ECNTRL_TBI_MODE         0x00000020
@@ -240,6 +241,7 @@ extern const char gfar_driver_version[];
#define IEVENT_CRL		0x00020000
#define IEVENT_XFUN		0x00010000
#define IEVENT_RXB0		0x00008000
#define IEVENT_MAG		0x00000800
#define IEVENT_GRSC		0x00000100
#define IEVENT_RXF0		0x00000080
#define IEVENT_FIR		0x00000008
@@ -252,7 +254,8 @@ extern const char gfar_driver_version[];
#define IEVENT_ERR_MASK         \
(IEVENT_RXC | IEVENT_BSY | IEVENT_EBERR | IEVENT_MSRO | \
 IEVENT_BABT | IEVENT_TXC | IEVENT_TXE | IEVENT_LC \
 | IEVENT_CRL | IEVENT_XFUN | IEVENT_DPE | IEVENT_PERR)
 | IEVENT_CRL | IEVENT_XFUN | IEVENT_DPE | IEVENT_PERR \
 | IEVENT_MAG)

#define IMASK_INIT_CLEAR	0x00000000
#define IMASK_BABR              0x80000000
@@ -270,6 +273,7 @@ extern const char gfar_driver_version[];
#define IMASK_CRL		0x00020000
#define IMASK_XFUN		0x00010000
#define IMASK_RXB0              0x00008000
#define IMASK_MAG		0x00000800
#define IMASK_GTSC              0x00000100
#define IMASK_RXFEN0		0x00000080
#define IMASK_FIR		0x00000008
@@ -737,10 +741,14 @@ struct gfar_private {
	unsigned int fifo_starve;
	unsigned int fifo_starve_off;

	/* Bitfield update lock */
	spinlock_t bflock;

	unsigned char vlan_enable:1,
		rx_csum_enable:1,
		extended_hash:1,
		bd_stash_en:1;
		bd_stash_en:1,
		wol_en:1; /* Wake-on-LAN enabled */
	unsigned short padding;

	unsigned int interruptTransmit;
+39 −2
Original line number Diff line number Diff line
@@ -479,14 +479,13 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva
static int gfar_set_rx_csum(struct net_device *dev, uint32_t data)
{
	struct gfar_private *priv = netdev_priv(dev);
	unsigned long flags;
	int err = 0;

	if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
		return -EOPNOTSUPP;

	if (dev->flags & IFF_UP) {
		unsigned long flags;

		/* Halt TX and RX, and process the frames which
		 * have already been received */
		spin_lock_irqsave(&priv->txlock, flags);
@@ -502,7 +501,9 @@ static int gfar_set_rx_csum(struct net_device *dev, uint32_t data)
		stop_gfar(dev);
	}

	spin_lock_irqsave(&priv->bflock, flags);
	priv->rx_csum_enable = data;
	spin_unlock_irqrestore(&priv->bflock, flags);

	if (dev->flags & IFF_UP)
		err = startup_gfar(dev);
@@ -564,6 +565,38 @@ static void gfar_set_msglevel(struct net_device *dev, uint32_t data)
	priv->msg_enable = data;
}

#ifdef CONFIG_PM
static void gfar_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
	struct gfar_private *priv = netdev_priv(dev);

	if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) {
		wol->supported = WAKE_MAGIC;
		wol->wolopts = priv->wol_en ? WAKE_MAGIC : 0;
	} else {
		wol->supported = wol->wolopts = 0;
	}
}

static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
	struct gfar_private *priv = netdev_priv(dev);
	unsigned long flags;

	if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) &&
	    wol->wolopts != 0)
		return -EINVAL;

	if (wol->wolopts & ~WAKE_MAGIC)
		return -EINVAL;

	spin_lock_irqsave(&priv->bflock, flags);
	priv->wol_en = wol->wolopts & WAKE_MAGIC ? 1 : 0;
	spin_unlock_irqrestore(&priv->bflock, flags);

	return 0;
}
#endif

const struct ethtool_ops gfar_ethtool_ops = {
	.get_settings = gfar_gsettings,
@@ -585,4 +618,8 @@ const struct ethtool_ops gfar_ethtool_ops = {
	.set_tx_csum = gfar_set_tx_csum,
	.get_msglevel = gfar_get_msglevel,
	.set_msglevel = gfar_set_msglevel,
#ifdef CONFIG_PM
	.get_wol = gfar_get_wol,
	.set_wol = gfar_set_wol,
#endif
};
+1 −0
Original line number Diff line number Diff line
@@ -69,6 +69,7 @@ struct gianfar_mdio_data {
#define FSL_GIANFAR_DEV_HAS_VLAN		0x00000020
#define FSL_GIANFAR_DEV_HAS_EXTENDED_HASH	0x00000040
#define FSL_GIANFAR_DEV_HAS_PADDING		0x00000080
#define FSL_GIANFAR_DEV_HAS_MAGIC_PACKET	0x00000100

/* Flags in gianfar_platform_data */
#define FSL_GIANFAR_BRD_HAS_PHY_INTR	0x00000001 /* set or use a timer */