123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507 |
- /*
- * Combined Ethernet driver for Motorola MPC8xx and MPC82xx.
- *
- * Copyright (c) 2003 Intracom S.A.
- * by Pantelis Antoniou <panto@intracom.gr>
- *
- * 2005 (c) MontaVista Software, Inc.
- * Vitaly Bordug <vbordug@ru.mvista.com>
- *
- * Heavily based on original FEC driver by Dan Malek <dan@embeddededge.com>
- * and modifications by Joakim Tjernlund <joakim.tjernlund@lumentis.se>
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- */
- #include <linux/config.h>
- #include <linux/module.h>
- #include <linux/types.h>
- #include <linux/kernel.h>
- #include <linux/sched.h>
- #include <linux/string.h>
- #include <linux/ptrace.h>
- #include <linux/errno.h>
- #include <linux/ioport.h>
- #include <linux/slab.h>
- #include <linux/interrupt.h>
- #include <linux/pci.h>
- #include <linux/init.h>
- #include <linux/delay.h>
- #include <linux/netdevice.h>
- #include <linux/etherdevice.h>
- #include <linux/skbuff.h>
- #include <linux/spinlock.h>
- #include <linux/mii.h>
- #include <linux/ethtool.h>
- #include <linux/bitops.h>
- #include <asm/pgtable.h>
- #include <asm/irq.h>
- #include <asm/uaccess.h>
- #include "fs_enet.h"
- /*************************************************/
- /*
- * Generic PHY support.
- * Should work for all PHYs, but link change is detected by polling
- */
- static void generic_timer_callback(unsigned long data)
- {
- struct net_device *dev = (struct net_device *)data;
- struct fs_enet_private *fep = netdev_priv(dev);
- fep->phy_timer_list.expires = jiffies + HZ / 2;
- add_timer(&fep->phy_timer_list);
- fs_mii_link_status_change_check(dev, 0);
- }
- static void generic_startup(struct net_device *dev)
- {
- struct fs_enet_private *fep = netdev_priv(dev);
- fep->phy_timer_list.expires = jiffies + HZ / 2; /* every 500ms */
- fep->phy_timer_list.data = (unsigned long)dev;
- fep->phy_timer_list.function = generic_timer_callback;
- add_timer(&fep->phy_timer_list);
- }
- static void generic_shutdown(struct net_device *dev)
- {
- struct fs_enet_private *fep = netdev_priv(dev);
- del_timer_sync(&fep->phy_timer_list);
- }
- /* ------------------------------------------------------------------------- */
- /* The Davicom DM9161 is used on the NETTA board */
- /* register definitions */
- #define MII_DM9161_ANAR 4 /* Aux. Config Register */
- #define MII_DM9161_ACR 16 /* Aux. Config Register */
- #define MII_DM9161_ACSR 17 /* Aux. Config/Status Register */
- #define MII_DM9161_10TCSR 18 /* 10BaseT Config/Status Reg. */
- #define MII_DM9161_INTR 21 /* Interrupt Register */
- #define MII_DM9161_RECR 22 /* Receive Error Counter Reg. */
- #define MII_DM9161_DISCR 23 /* Disconnect Counter Register */
- static void dm9161_startup(struct net_device *dev)
- {
- struct fs_enet_private *fep = netdev_priv(dev);
- fs_mii_write(dev, fep->mii_if.phy_id, MII_DM9161_INTR, 0x0000);
- /* Start autonegotiation */
- fs_mii_write(dev, fep->mii_if.phy_id, MII_BMCR, 0x1200);
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(HZ*8);
- }
- static void dm9161_ack_int(struct net_device *dev)
- {
- struct fs_enet_private *fep = netdev_priv(dev);
- fs_mii_read(dev, fep->mii_if.phy_id, MII_DM9161_INTR);
- }
- static void dm9161_shutdown(struct net_device *dev)
- {
- struct fs_enet_private *fep = netdev_priv(dev);
- fs_mii_write(dev, fep->mii_if.phy_id, MII_DM9161_INTR, 0x0f00);
- }
- /**********************************************************************************/
- static const struct phy_info phy_info[] = {
- {
- .id = 0x00181b88,
- .name = "DM9161",
- .startup = dm9161_startup,
- .ack_int = dm9161_ack_int,
- .shutdown = dm9161_shutdown,
- }, {
- .id = 0,
- .name = "GENERIC",
- .startup = generic_startup,
- .shutdown = generic_shutdown,
- },
- };
- /**********************************************************************************/
- static int phy_id_detect(struct net_device *dev)
- {
- struct fs_enet_private *fep = netdev_priv(dev);
- const struct fs_platform_info *fpi = fep->fpi;
- struct fs_enet_mii_bus *bus = fep->mii_bus;
- int i, r, start, end, phytype, physubtype;
- const struct phy_info *phy;
- int phy_hwid, phy_id;
- phy_hwid = -1;
- fep->phy = NULL;
- /* auto-detect? */
- if (fpi->phy_addr == -1) {
- start = 1;
- end = 32;
- } else { /* direct */
- start = fpi->phy_addr;
- end = start + 1;
- }
- for (phy_id = start; phy_id < end; phy_id++) {
- /* skip already used phy addresses on this bus */
- if (bus->usage_map & (1 << phy_id))
- continue;
- r = fs_mii_read(dev, phy_id, MII_PHYSID1);
- if (r == -1 || (phytype = (r & 0xffff)) == 0xffff)
- continue;
- r = fs_mii_read(dev, phy_id, MII_PHYSID2);
- if (r == -1 || (physubtype = (r & 0xffff)) == 0xffff)
- continue;
- phy_hwid = (phytype << 16) | physubtype;
- if (phy_hwid != -1)
- break;
- }
- if (phy_hwid == -1) {
- printk(KERN_ERR DRV_MODULE_NAME
- ": %s No PHY detected! range=0x%02x-0x%02x\n",
- dev->name, start, end);
- return -1;
- }
- for (i = 0, phy = phy_info; i < ARRAY_SIZE(phy_info); i++, phy++)
- if (phy->id == (phy_hwid >> 4) || phy->id == 0)
- break;
- if (i >= ARRAY_SIZE(phy_info)) {
- printk(KERN_ERR DRV_MODULE_NAME
- ": %s PHY id 0x%08x is not supported!\n",
- dev->name, phy_hwid);
- return -1;
- }
- fep->phy = phy;
- /* mark this address as used */
- bus->usage_map |= (1 << phy_id);
- printk(KERN_INFO DRV_MODULE_NAME
- ": %s Phy @ 0x%x, type %s (0x%08x)%s\n",
- dev->name, phy_id, fep->phy->name, phy_hwid,
- fpi->phy_addr == -1 ? " (auto-detected)" : "");
- return phy_id;
- }
- void fs_mii_startup(struct net_device *dev)
- {
- struct fs_enet_private *fep = netdev_priv(dev);
- if (fep->phy->startup)
- (*fep->phy->startup) (dev);
- }
- void fs_mii_shutdown(struct net_device *dev)
- {
- struct fs_enet_private *fep = netdev_priv(dev);
- if (fep->phy->shutdown)
- (*fep->phy->shutdown) (dev);
- }
- void fs_mii_ack_int(struct net_device *dev)
- {
- struct fs_enet_private *fep = netdev_priv(dev);
- if (fep->phy->ack_int)
- (*fep->phy->ack_int) (dev);
- }
- #define MII_LINK 0x0001
- #define MII_HALF 0x0002
- #define MII_FULL 0x0004
- #define MII_BASE4 0x0008
- #define MII_10M 0x0010
- #define MII_100M 0x0020
- #define MII_1G 0x0040
- #define MII_10G 0x0080
- /* return full mii info at one gulp, with a usable form */
- static unsigned int mii_full_status(struct mii_if_info *mii)
- {
- unsigned int status;
- int bmsr, adv, lpa, neg;
- struct fs_enet_private* fep = netdev_priv(mii->dev);
-
- /* first, a dummy read, needed to latch some MII phys */
- (void)mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
- bmsr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
- /* no link */
- if ((bmsr & BMSR_LSTATUS) == 0)
- return 0;
- status = MII_LINK;
-
- /* Lets look what ANEG says if it's supported - otherwize we shall
- take the right values from the platform info*/
- if(!mii->force_media) {
- /* autoneg not completed; don't bother */
- if ((bmsr & BMSR_ANEGCOMPLETE) == 0)
- return 0;
- adv = (*mii->mdio_read)(mii->dev, mii->phy_id, MII_ADVERTISE);
- lpa = (*mii->mdio_read)(mii->dev, mii->phy_id, MII_LPA);
- neg = lpa & adv;
- } else {
- neg = fep->fpi->bus_info->lpa;
- }
- if (neg & LPA_100FULL)
- status |= MII_FULL | MII_100M;
- else if (neg & LPA_100BASE4)
- status |= MII_FULL | MII_BASE4 | MII_100M;
- else if (neg & LPA_100HALF)
- status |= MII_HALF | MII_100M;
- else if (neg & LPA_10FULL)
- status |= MII_FULL | MII_10M;
- else
- status |= MII_HALF | MII_10M;
-
- return status;
- }
- void fs_mii_link_status_change_check(struct net_device *dev, int init_media)
- {
- struct fs_enet_private *fep = netdev_priv(dev);
- struct mii_if_info *mii = &fep->mii_if;
- unsigned int mii_status;
- int ok_to_print, link, duplex, speed;
- unsigned long flags;
- ok_to_print = netif_msg_link(fep);
- mii_status = mii_full_status(mii);
- if (!init_media && mii_status == fep->last_mii_status)
- return;
- fep->last_mii_status = mii_status;
- link = !!(mii_status & MII_LINK);
- duplex = !!(mii_status & MII_FULL);
- speed = (mii_status & MII_100M) ? 100 : 10;
- if (link == 0) {
- netif_carrier_off(mii->dev);
- netif_stop_queue(dev);
- if (!init_media) {
- spin_lock_irqsave(&fep->lock, flags);
- (*fep->ops->stop)(dev);
- spin_unlock_irqrestore(&fep->lock, flags);
- }
- if (ok_to_print)
- printk(KERN_INFO "%s: link down\n", mii->dev->name);
- } else {
- mii->full_duplex = duplex;
- netif_carrier_on(mii->dev);
- spin_lock_irqsave(&fep->lock, flags);
- fep->duplex = duplex;
- fep->speed = speed;
- (*fep->ops->restart)(dev);
- spin_unlock_irqrestore(&fep->lock, flags);
- netif_start_queue(dev);
- if (ok_to_print)
- printk(KERN_INFO "%s: link up, %dMbps, %s-duplex\n",
- dev->name, speed, duplex ? "full" : "half");
- }
- }
- /**********************************************************************************/
- int fs_mii_read(struct net_device *dev, int phy_id, int location)
- {
- struct fs_enet_private *fep = netdev_priv(dev);
- struct fs_enet_mii_bus *bus = fep->mii_bus;
- unsigned long flags;
- int ret;
- spin_lock_irqsave(&bus->mii_lock, flags);
- ret = (*bus->mii_read)(bus, phy_id, location);
- spin_unlock_irqrestore(&bus->mii_lock, flags);
- return ret;
- }
- void fs_mii_write(struct net_device *dev, int phy_id, int location, int value)
- {
- struct fs_enet_private *fep = netdev_priv(dev);
- struct fs_enet_mii_bus *bus = fep->mii_bus;
- unsigned long flags;
- spin_lock_irqsave(&bus->mii_lock, flags);
- (*bus->mii_write)(bus, phy_id, location, value);
- spin_unlock_irqrestore(&bus->mii_lock, flags);
- }
- /*****************************************************************************/
- /* list of all registered mii buses */
- static LIST_HEAD(fs_mii_bus_list);
- static struct fs_enet_mii_bus *lookup_bus(int method, int id)
- {
- struct list_head *ptr;
- struct fs_enet_mii_bus *bus;
- list_for_each(ptr, &fs_mii_bus_list) {
- bus = list_entry(ptr, struct fs_enet_mii_bus, list);
- if (bus->bus_info->method == method &&
- bus->bus_info->id == id)
- return bus;
- }
- return NULL;
- }
- static struct fs_enet_mii_bus *create_bus(const struct fs_mii_bus_info *bi)
- {
- struct fs_enet_mii_bus *bus;
- int ret = 0;
- bus = kmalloc(sizeof(*bus), GFP_KERNEL);
- if (bus == NULL) {
- ret = -ENOMEM;
- goto err;
- }
- memset(bus, 0, sizeof(*bus));
- spin_lock_init(&bus->mii_lock);
- bus->bus_info = bi;
- bus->refs = 0;
- bus->usage_map = 0;
- /* perform initialization */
- switch (bi->method) {
- case fsmii_fixed:
- ret = fs_mii_fixed_init(bus);
- if (ret != 0)
- goto err;
- break;
- case fsmii_bitbang:
- ret = fs_mii_bitbang_init(bus);
- if (ret != 0)
- goto err;
- break;
- #ifdef CONFIG_FS_ENET_HAS_FEC
- case fsmii_fec:
- ret = fs_mii_fec_init(bus);
- if (ret != 0)
- goto err;
- break;
- #endif
- default:
- ret = -EINVAL;
- goto err;
- }
- list_add(&bus->list, &fs_mii_bus_list);
- return bus;
- err:
- if (bus)
- kfree(bus);
- return ERR_PTR(ret);
- }
- static void destroy_bus(struct fs_enet_mii_bus *bus)
- {
- /* remove from bus list */
- list_del(&bus->list);
- /* nothing more needed */
- kfree(bus);
- }
- int fs_mii_connect(struct net_device *dev)
- {
- struct fs_enet_private *fep = netdev_priv(dev);
- const struct fs_platform_info *fpi = fep->fpi;
- struct fs_enet_mii_bus *bus = NULL;
- /* check method validity */
- switch (fpi->bus_info->method) {
- case fsmii_fixed:
- case fsmii_bitbang:
- break;
- #ifdef CONFIG_FS_ENET_HAS_FEC
- case fsmii_fec:
- break;
- #endif
- default:
- printk(KERN_ERR DRV_MODULE_NAME
- ": %s Unknown MII bus method (%d)!\n",
- dev->name, fpi->bus_info->method);
- return -EINVAL;
- }
- bus = lookup_bus(fpi->bus_info->method, fpi->bus_info->id);
- /* if not found create new bus */
- if (bus == NULL) {
- bus = create_bus(fpi->bus_info);
- if (IS_ERR(bus)) {
- printk(KERN_ERR DRV_MODULE_NAME
- ": %s MII bus creation failure!\n", dev->name);
- return PTR_ERR(bus);
- }
- }
- bus->refs++;
- fep->mii_bus = bus;
- fep->mii_if.dev = dev;
- fep->mii_if.phy_id_mask = 0x1f;
- fep->mii_if.reg_num_mask = 0x1f;
- fep->mii_if.mdio_read = fs_mii_read;
- fep->mii_if.mdio_write = fs_mii_write;
- fep->mii_if.force_media = fpi->bus_info->disable_aneg;
- fep->mii_if.phy_id = phy_id_detect(dev);
- return 0;
- }
- void fs_mii_disconnect(struct net_device *dev)
- {
- struct fs_enet_private *fep = netdev_priv(dev);
- struct fs_enet_mii_bus *bus = NULL;
- bus = fep->mii_bus;
- fep->mii_bus = NULL;
- if (--bus->refs <= 0)
- destroy_bus(bus);
- }
|