Skip to content
Snippets Groups Projects
fec.c 43.3 KiB
Newer Older
	/* mask with MAC supported features */
	if (id_entry->driver_data & FEC_QUIRK_HAS_GBIT)
		phy_dev->supported &= PHY_GBIT_FEATURES;
	else
		phy_dev->supported &= PHY_BASIC_FEATURES;

	phy_dev->advertising = phy_dev->supported;
Linus Torvalds's avatar
Linus Torvalds committed

	fep->phy_dev = phy_dev;
	fep->link = 0;
	fep->full_duplex = 0;
Linus Torvalds's avatar
Linus Torvalds committed

Lothar Waßmann's avatar
Lothar Waßmann committed
	printk(KERN_INFO
		"%s: Freescale FEC PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
		ndev->name,
		fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev),
		fep->phy_dev->irq);

static int fec_enet_mii_init(struct platform_device *pdev)
	static struct mii_bus *fec0_mii_bus;
	struct net_device *ndev = platform_get_drvdata(pdev);
	struct fec_enet_private *fep = netdev_priv(ndev);
	const struct platform_device_id *id_entry =
				platform_get_device_id(fep->pdev);
	/*
	 * The dual fec interfaces are not equivalent with enet-mac.
	 * Here are the differences:
	 *
	 *  - fec0 supports MII & RMII modes while fec1 only supports RMII
	 *  - fec0 acts as the 1588 time master while fec1 is slave
	 *  - external phys can only be configured by fec0
	 *
	 * That is to say fec1 can not work independently. It only works
	 * when fec0 is working. The reason behind this design is that the
	 * second interface is added primarily for Switch mode.
	 *
	 * Because of the last point above, both phys are attached on fec0
	 * mdio interface in board design, and need to be configured by
	 * fec0 mii_bus.
	 */
	if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && fep->dev_id > 0) {
		/* fec1 uses fec0 mii_bus */
		if (mii_cnt && fec0_mii_bus) {
			fep->mii_bus = fec0_mii_bus;
			mii_cnt++;
			return 0;
		}
		return -ENOENT;
Linus Torvalds's avatar
Linus Torvalds committed

	/*
	 * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed)
	 *
	 * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while
	 * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'.  The i.MX28
	 * Reference Manual has an error on this, and gets fixed on i.MX6Q
	 * document.
	fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk), 5000000);
	if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
		fep->phy_speed--;
	fep->phy_speed <<= 1;
	writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
Linus Torvalds's avatar
Linus Torvalds committed

	fep->mii_bus = mdiobus_alloc();
	if (fep->mii_bus == NULL) {
		err = -ENOMEM;
		goto err_out;
	fep->mii_bus->name = "fec_enet_mii_bus";
	fep->mii_bus->read = fec_enet_mdio_read;
	fep->mii_bus->write = fec_enet_mdio_write;
	fep->mii_bus->reset = fec_enet_mdio_reset;
	snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
		pdev->name, fep->dev_id + 1);
	fep->mii_bus->priv = fep;
	fep->mii_bus->parent = &pdev->dev;

	fep->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
	if (!fep->mii_bus->irq) {
		err = -ENOMEM;
		goto err_out_free_mdiobus;
	for (i = 0; i < PHY_MAX_ADDR; i++)
		fep->mii_bus->irq[i] = PHY_POLL;
Linus Torvalds's avatar
Linus Torvalds committed

	if (mdiobus_register(fep->mii_bus))
		goto err_out_free_mdio_irq;
Linus Torvalds's avatar
Linus Torvalds committed

	/* save fec0 mii_bus */
	if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
		fec0_mii_bus = fep->mii_bus;

Linus Torvalds's avatar
Linus Torvalds committed

err_out_free_mdio_irq:
	kfree(fep->mii_bus->irq);
err_out_free_mdiobus:
	mdiobus_free(fep->mii_bus);
err_out:
	return err;
static void fec_enet_mii_remove(struct fec_enet_private *fep)
Linus Torvalds's avatar
Linus Torvalds committed
{
	if (--mii_cnt == 0) {
		mdiobus_unregister(fep->mii_bus);
		kfree(fep->mii_bus->irq);
		mdiobus_free(fep->mii_bus);
	}
static int fec_enet_get_settings(struct net_device *ndev,
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct fec_enet_private *fep = netdev_priv(ndev);
	struct phy_device *phydev = fep->phy_dev;
Linus Torvalds's avatar
Linus Torvalds committed

Linus Torvalds's avatar
Linus Torvalds committed

	return phy_ethtool_gset(phydev, cmd);
static int fec_enet_set_settings(struct net_device *ndev,
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct fec_enet_private *fep = netdev_priv(ndev);
	struct phy_device *phydev = fep->phy_dev;
Linus Torvalds's avatar
Linus Torvalds committed

Linus Torvalds's avatar
Linus Torvalds committed

	return phy_ethtool_sset(phydev, cmd);
static void fec_enet_get_drvinfo(struct net_device *ndev,
				 struct ethtool_drvinfo *info)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct fec_enet_private *fep = netdev_priv(ndev);
	strcpy(info->driver, fep->pdev->dev.driver->name);
	strcpy(info->version, "Revision: 1.0");
	strcpy(info->bus_info, dev_name(&ndev->dev));
static const struct ethtool_ops fec_enet_ethtool_ops = {
	.get_settings		= fec_enet_get_settings,
	.set_settings		= fec_enet_set_settings,
	.get_drvinfo		= fec_enet_get_drvinfo,
	.get_link		= ethtool_op_get_link,
};
Linus Torvalds's avatar
Linus Torvalds committed

static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct fec_enet_private *fep = netdev_priv(ndev);
	struct phy_device *phydev = fep->phy_dev;
Linus Torvalds's avatar
Linus Torvalds committed

Linus Torvalds's avatar
Linus Torvalds committed

	return phy_mii_ioctl(phydev, rq, cmd);
static void fec_enet_free_buffers(struct net_device *ndev)
Sascha Hauer's avatar
Sascha Hauer committed
{
	struct fec_enet_private *fep = netdev_priv(ndev);
Sascha Hauer's avatar
Sascha Hauer committed
	int i;
	struct sk_buff *skb;
	struct bufdesc	*bdp;

	bdp = fep->rx_bd_base;
	for (i = 0; i < RX_RING_SIZE; i++) {
		skb = fep->rx_skbuff[i];

		if (bdp->cbd_bufaddr)
			dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
Sascha Hauer's avatar
Sascha Hauer committed
					FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
		if (skb)
			dev_kfree_skb(skb);
		bdp++;
	}

	bdp = fep->tx_bd_base;
	for (i = 0; i < TX_RING_SIZE; i++)
		kfree(fep->tx_bounce[i]);
}

static int fec_enet_alloc_buffers(struct net_device *ndev)
Sascha Hauer's avatar
Sascha Hauer committed
{
	struct fec_enet_private *fep = netdev_priv(ndev);
Sascha Hauer's avatar
Sascha Hauer committed
	int i;
	struct sk_buff *skb;
	struct bufdesc	*bdp;

	bdp = fep->rx_bd_base;
	for (i = 0; i < RX_RING_SIZE; i++) {
		skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
Sascha Hauer's avatar
Sascha Hauer committed
		if (!skb) {
Sascha Hauer's avatar
Sascha Hauer committed
			return -ENOMEM;
		}
		fep->rx_skbuff[i] = skb;

		bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, skb->data,
Sascha Hauer's avatar
Sascha Hauer committed
				FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
		bdp->cbd_sc = BD_ENET_RX_EMPTY;
		bdp++;
	}

	/* Set the last buffer to wrap. */
	bdp--;
	bdp->cbd_sc |= BD_SC_WRAP;

	bdp = fep->tx_bd_base;
	for (i = 0; i < TX_RING_SIZE; i++) {
		fep->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL);

		bdp->cbd_sc = 0;
		bdp->cbd_bufaddr = 0;
		bdp++;
	}

	/* Set the last buffer to wrap. */
	bdp--;
	bdp->cbd_sc |= BD_SC_WRAP;

	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
static int
fec_enet_open(struct net_device *ndev)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct fec_enet_private *fep = netdev_priv(ndev);
Sascha Hauer's avatar
Sascha Hauer committed
	int ret;
Linus Torvalds's avatar
Linus Torvalds committed

	/* I should reset the ring buffers here, but I don't yet know
	 * a simple way to do that.
	 */

	ret = fec_enet_alloc_buffers(ndev);
Sascha Hauer's avatar
Sascha Hauer committed
	if (ret)
		return ret;

	/* Probe and connect to PHY when open the interface */
	ret = fec_enet_mii_probe(ndev);
Linus Torvalds's avatar
Linus Torvalds committed
	fep->opened = 1;
Sascha Hauer's avatar
Sascha Hauer committed
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
}

static int
fec_enet_close(struct net_device *ndev)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct fec_enet_private *fep = netdev_priv(ndev);
Linus Torvalds's avatar
Linus Torvalds committed

Sascha Hauer's avatar
Sascha Hauer committed
	/* Don't know what to do yet. */
Linus Torvalds's avatar
Linus Torvalds committed
	fep->opened = 0;
	netif_stop_queue(ndev);
	fec_stop(ndev);
Linus Torvalds's avatar
Linus Torvalds committed

	if (fep->phy_dev) {
		phy_stop(fep->phy_dev);
		phy_disconnect(fep->phy_dev);
	fec_enet_free_buffers(ndev);
Sascha Hauer's avatar
Sascha Hauer committed

Linus Torvalds's avatar
Linus Torvalds committed
	return 0;
}

/* Set or clear the multicast filter for this adaptor.
 * Skeleton taken from sunlance driver.
 * The CPM Ethernet implementation allows Multicast as well as individual
 * MAC address filtering.  Some of the drivers check to make sure it is
 * a group multicast address, and discard those that are not.  I guess I
 * will do the same for now, but just remove the test if you want
 * individual filtering as well (do the upper net layers want or support
 * this kind of feature?).
 */

#define HASH_BITS	6		/* #bits in hash */
#define CRC32_POLY	0xEDB88320

static void set_multicast_list(struct net_device *ndev)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct fec_enet_private *fep = netdev_priv(ndev);
	struct netdev_hw_addr *ha;
	unsigned int i, bit, data, crc, tmp;
Linus Torvalds's avatar
Linus Torvalds committed
	unsigned char hash;

	if (ndev->flags & IFF_PROMISC) {
		tmp = readl(fep->hwp + FEC_R_CNTRL);
		tmp |= 0x8;
		writel(tmp, fep->hwp + FEC_R_CNTRL);
Linus Torvalds's avatar
Linus Torvalds committed

	tmp = readl(fep->hwp + FEC_R_CNTRL);
	tmp &= ~0x8;
	writel(tmp, fep->hwp + FEC_R_CNTRL);

	if (ndev->flags & IFF_ALLMULTI) {
		/* Catch all multicast addresses, so set the
		 * filter to all 1's
		 */
		writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
		writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW);

		return;
	}

	/* Clear filter and add the addresses in hash register
	 */
	writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
	writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW);

	netdev_for_each_mc_addr(ha, ndev) {
		/* calculate crc32 value of mac address */
		crc = 0xffffffff;

		for (i = 0; i < ndev->addr_len; i++) {
			data = ha->addr[i];
			for (bit = 0; bit < 8; bit++, data >>= 1) {
				crc = (crc >> 1) ^
				(((crc ^ data) & 1) ? CRC32_POLY : 0);
Linus Torvalds's avatar
Linus Torvalds committed
			}
		}

		/* only upper 6 bits (HASH_BITS) are used
		 * which point to specific bit in he hash registers
		 */
		hash = (crc >> (32 - HASH_BITS)) & 0x3f;

		if (hash > 31) {
			tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
			tmp |= 1 << (hash - 32);
			writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
		} else {
			tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_LOW);
			tmp |= 1 << hash;
			writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
		}
Sascha Hauer's avatar
Sascha Hauer committed
/* Set a MAC change in hardware. */
static int
fec_set_mac_address(struct net_device *ndev, void *p)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct fec_enet_private *fep = netdev_priv(ndev);
	struct sockaddr *addr = p;

	if (!is_valid_ether_addr(addr->sa_data))
		return -EADDRNOTAVAIL;

	memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);
Linus Torvalds's avatar
Linus Torvalds committed

	writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) |
		(ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24),
		fep->hwp + FEC_ADDR_LOW);
	writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24),
		fep->hwp + FEC_ADDR_HIGH);
	return 0;
#ifdef CONFIG_NET_POLL_CONTROLLER
/*
 * fec_poll_controller: FEC Poll controller function
 * @dev: The FEC network adapter
 *
 * Polled functionality used by netconsole and others in non interrupt mode
 *
 */
void fec_poll_controller(struct net_device *dev)
{
	int i;
	struct fec_enet_private *fep = netdev_priv(dev);

	for (i = 0; i < FEC_IRQ_NUM; i++) {
		if (fep->irq[i] > 0) {
			disable_irq(fep->irq[i]);
			fec_enet_interrupt(fep->irq[i], dev);
			enable_irq(fep->irq[i]);
		}
	}
}
#endif

static const struct net_device_ops fec_netdev_ops = {
	.ndo_open		= fec_enet_open,
	.ndo_stop		= fec_enet_close,
	.ndo_start_xmit		= fec_enet_start_xmit,
	.ndo_set_rx_mode	= set_multicast_list,
	.ndo_change_mtu		= eth_change_mtu,
	.ndo_validate_addr	= eth_validate_addr,
	.ndo_tx_timeout		= fec_timeout,
	.ndo_set_mac_address	= fec_set_mac_address,
	.ndo_do_ioctl		= fec_enet_ioctl,
#ifdef CONFIG_NET_POLL_CONTROLLER
	.ndo_poll_controller	= fec_poll_controller,
#endif
Linus Torvalds's avatar
Linus Torvalds committed
 /*
  * XXX:  We need to clean up on failure exits here.
Linus Torvalds's avatar
Linus Torvalds committed
  */
static int fec_enet_init(struct net_device *ndev)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct fec_enet_private *fep = netdev_priv(ndev);
Sascha Hauer's avatar
Sascha Hauer committed
	struct bufdesc *cbd_base;
	struct bufdesc *bdp;
Sascha Hauer's avatar
Sascha Hauer committed
	int i;
Linus Torvalds's avatar
Linus Torvalds committed

	/* Allocate memory for buffer descriptors. */
	cbd_base = dma_alloc_coherent(NULL, PAGE_SIZE, &fep->bd_dma,
			GFP_KERNEL);
	if (!cbd_base) {
		printk("FEC: allocate descriptor memory failed?\n");
		return -ENOMEM;
	}

	spin_lock_init(&fep->hw_lock);

Linus Torvalds's avatar
Linus Torvalds committed

Linus Torvalds's avatar
Linus Torvalds committed

	/* Set receive and transmit descriptor base. */
Linus Torvalds's avatar
Linus Torvalds committed
	fep->rx_bd_base = cbd_base;
	fep->tx_bd_base = cbd_base + RX_RING_SIZE;

Sascha Hauer's avatar
Sascha Hauer committed
	/* The FEC Ethernet specific entries in the device structure */
	ndev->watchdog_timeo = TX_TIMEOUT;
	ndev->netdev_ops = &fec_netdev_ops;
	ndev->ethtool_ops = &fec_enet_ethtool_ops;

	/* Initialize the receive buffer descriptors. */
	bdp = fep->rx_bd_base;
	for (i = 0; i < RX_RING_SIZE; i++) {

		/* Initialize the BD for every fragment in the page. */
		bdp->cbd_sc = 0;
		bdp++;
	}

	/* Set the last buffer to wrap */
	bdp--;
	bdp->cbd_sc |= BD_SC_WRAP;

	/* ...and the same for transmit */
	bdp = fep->tx_bd_base;
	for (i = 0; i < TX_RING_SIZE; i++) {

		/* Initialize the BD for every fragment in the page. */
		bdp->cbd_sc = 0;
		bdp->cbd_bufaddr = 0;
		bdp++;
	}

	/* Set the last buffer to wrap */
	bdp--;
	bdp->cbd_sc |= BD_SC_WRAP;

#ifdef CONFIG_OF
static int __devinit fec_get_phy_mode_dt(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;

	if (np)
		return of_get_phy_mode(np);

	return -ENODEV;
}

static void __devinit fec_reset_phy(struct platform_device *pdev)
{
	int err, phy_reset;
	struct device_node *np = pdev->dev.of_node;

	if (!np)

	phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0);
	err = gpio_request_one(phy_reset, GPIOF_OUT_INIT_LOW, "phy-reset");
	if (err) {
		pr_debug("FEC: failed to get gpio phy-reset: %d\n", err);
		return;
	}
	msleep(1);
	gpio_set_value(phy_reset, 1);
}
#else /* CONFIG_OF */
static inline int fec_get_phy_mode_dt(struct platform_device *pdev)
{
	return -ENODEV;
}

static inline void fec_reset_phy(struct platform_device *pdev)
{
	/*
	 * In case of platform probe, the reset has been done
	 * by machine code.
	 */
}
#endif /* CONFIG_OF */

static int __devinit
fec_probe(struct platform_device *pdev)
{
	struct fec_enet_private *fep;
	struct fec_platform_data *pdata;
	struct net_device *ndev;
	int i, irq, ret = 0;
	struct resource *r;
	const struct of_device_id *of_id;
	static int dev_id;

	of_id = of_match_device(fec_dt_ids, &pdev->dev);
	if (of_id)
		pdev->id_entry = of_id->data;

	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!r)
		return -ENXIO;

	r = request_mem_region(r->start, resource_size(r), pdev->name);
	if (!r)
		return -EBUSY;

	/* Init network device */
	ndev = alloc_etherdev(sizeof(struct fec_enet_private));
	if (!ndev) {
		ret = -ENOMEM;
		goto failed_alloc_etherdev;
	}

	SET_NETDEV_DEV(ndev, &pdev->dev);

	/* setup board info structure */
	fep = netdev_priv(ndev);

	fep->hwp = ioremap(r->start, resource_size(r));
	fep->dev_id = dev_id++;
		ret = -ENOMEM;
		goto failed_ioremap;
	}

	platform_set_drvdata(pdev, ndev);

	ret = fec_get_phy_mode_dt(pdev);
	if (ret < 0) {
		pdata = pdev->dev.platform_data;
		if (pdata)
			fep->phy_interface = pdata->phy;
		else
			fep->phy_interface = PHY_INTERFACE_MODE_MII;
	} else {
		fep->phy_interface = ret;
	}

	fec_reset_phy(pdev);
	for (i = 0; i < FEC_IRQ_NUM; i++) {
		irq = platform_get_irq(pdev, i);
		if (irq < 0) {
			if (i)
				break;
			ret = irq;
			goto failed_irq;
		}
		ret = request_irq(irq, fec_enet_interrupt, IRQF_DISABLED, pdev->name, ndev);
		if (ret) {
				irq = platform_get_irq(pdev, i);
				free_irq(irq, ndev);
			}
			goto failed_irq;
		}
	}

	fep->clk = clk_get(&pdev->dev, NULL);
	if (IS_ERR(fep->clk)) {
		ret = PTR_ERR(fep->clk);
		goto failed_clk;
	}
	clk_prepare_enable(fep->clk);
	ret = fec_enet_init(ndev);
	ret = fec_enet_mii_init(pdev);
	if (ret)
		goto failed_mii_init;

	/* Carrier starts down, phylib will bring it up */
	netif_carrier_off(ndev);

	ret = register_netdev(ndev);
	if (ret)
		goto failed_register;

	return 0;

failed_register:
	fec_enet_mii_remove(fep);
failed_mii_init:
	clk_disable_unprepare(fep->clk);
	clk_put(fep->clk);
failed_clk:
	for (i = 0; i < FEC_IRQ_NUM; i++) {
		irq = platform_get_irq(pdev, i);
		if (irq > 0)
			free_irq(irq, ndev);
	}
failed_irq:
failed_ioremap:
	free_netdev(ndev);
failed_alloc_etherdev:
	release_mem_region(r->start, resource_size(r));

	return ret;
}

static int __devexit
fec_drv_remove(struct platform_device *pdev)
{
	struct net_device *ndev = platform_get_drvdata(pdev);
	struct fec_enet_private *fep = netdev_priv(ndev);
	unregister_netdev(ndev);
	for (i = 0; i < FEC_IRQ_NUM; i++) {
		int irq = platform_get_irq(pdev, i);
		if (irq > 0)
			free_irq(irq, ndev);
	}
	clk_disable_unprepare(fep->clk);

	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	BUG_ON(!r);
	release_mem_region(r->start, resource_size(r));

#ifdef CONFIG_PM
fec_suspend(struct device *dev)
	struct net_device *ndev = dev_get_drvdata(dev);
	struct fec_enet_private *fep = netdev_priv(ndev);
	if (netif_running(ndev)) {
		fec_stop(ndev);
		netif_device_detach(ndev);
	clk_disable_unprepare(fep->clk);
fec_resume(struct device *dev)
	struct net_device *ndev = dev_get_drvdata(dev);
	struct fec_enet_private *fep = netdev_priv(ndev);
	clk_prepare_enable(fep->clk);
	if (netif_running(ndev)) {
		fec_restart(ndev, fep->full_duplex);
		netif_device_attach(ndev);
static const struct dev_pm_ops fec_pm_ops = {
	.suspend	= fec_suspend,
	.resume		= fec_resume,
	.freeze		= fec_suspend,
	.thaw		= fec_resume,
	.poweroff	= fec_suspend,
	.restore	= fec_resume,
};
#endif
static struct platform_driver fec_driver = {
	.driver	= {
		.name	= DRIVER_NAME,
		.owner	= THIS_MODULE,
#ifdef CONFIG_PM
		.pm	= &fec_pm_ops,
#endif
		.of_match_table = fec_dt_ids,
	.id_table = fec_devtype,
	.probe	= fec_probe,
	.remove	= __devexit_p(fec_drv_remove),
module_platform_driver(fec_driver);
Linus Torvalds's avatar
Linus Torvalds committed

MODULE_LICENSE("GPL");