📄 fec.c
字号:
if (mii_reg & 0x0004) *s |= PHY_STAT_100FDX; else *s |= PHY_STAT_100HDX; }}static phy_info_t phy_info_dp83843 = { 0x020005c1, "DP83843BVJE", (const phy_cmd_t []) { /* config */ { mk_mii_write(MII_REG_ANAR, 0x01E1), NULL }, /* Auto-Negociation Register Control set to */ /* auto-negociate 10/100MBps, Half/Full duplex */ { mk_mii_read(MII_REG_CR), mii_parse_cr }, { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, { mk_mii_end, } }, (const phy_cmd_t []) { /* startup */ { mk_mii_write(MII_DP83843_MIPSCR, 0x0002), NULL }, /* Enable interrupts */ { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* Enable and Restart Auto-Negotiation */ { mk_mii_read(MII_REG_SR), mii_parse_sr }, { mk_mii_read(MII_REG_CR), mii_parse_cr }, { mk_mii_read(MII_DP83843_PHYSTS), mii_parse_dp83843_physts }, { mk_mii_end, } }, (const phy_cmd_t []) { /* ack_int */ { mk_mii_read(MII_DP83843_MIPGSR), NULL }, /* Acknowledge interrupts */ { mk_mii_read(MII_REG_SR), mii_parse_sr }, /* Find out the current status */ { mk_mii_read(MII_REG_CR), mii_parse_cr }, { mk_mii_read(MII_DP83843_PHYSTS), mii_parse_dp83843_physts }, { mk_mii_end, } }, (const phy_cmd_t []) { /* shutdown - disable interrupts */ { mk_mii_end, } }};#endif /* CONFIG_FEC_DP83843 *//* ----------------------------------------------------------------- *//* The National Semiconductor DP83846A is used on a Mediatrix board *//* ----------------------------------------------------------------- */#ifdef CONFIG_FEC_DP83846A/* Register definitions */#define MII_DP83846A_PHYSTS 0x10 /* PHY Status Register */static void mii_parse_dp83846a_physts(uint mii_reg, struct net_device *dev, uint data){ struct fec_enet_private *fep = (struct fec_enet_private *)dev->priv; volatile uint *s = &(fep->phy_status); int link_change_mask; *s &= ~(PHY_STAT_SPMASK); if (mii_reg & 0x0002) { if (mii_reg & 0x0004) *s |= PHY_STAT_10FDX; else *s |= PHY_STAT_10HDX; } else { if (mii_reg & 0x0004) *s |= PHY_STAT_100FDX; else *s |= PHY_STAT_100HDX; } link_change_mask = PHY_STAT_LINK | PHY_STAT_10FDX | PHY_STAT_10HDX | PHY_STAT_100FDX | PHY_STAT_100HDX; if(fep->old_status != (link_change_mask & *s)) { fep->old_status = (link_change_mask & *s); mii_queue_relink(mii_reg, dev, 0); }}static phy_info_t phy_info_dp83846a = { 0x020005c2, "DP83846A", (const phy_cmd_t []) { /* config */ { mk_mii_write(MII_REG_ANAR, 0x01E1), NULL }, /* Auto-Negociation Register Control set to */ /* auto-negociate 10/100MBps, Half/Full duplex */ { mk_mii_read(MII_REG_CR), mii_parse_cr }, { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, { mk_mii_end, } }, (const phy_cmd_t []) { /* startup */ { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* Enable and Restart Auto-Negotiation */ { mk_mii_read(MII_REG_SR), mii_parse_sr }, { mk_mii_read(MII_REG_CR), mii_parse_cr }, { mk_mii_read(MII_DP83846A_PHYSTS), mii_parse_dp83846a_physts }, { mk_mii_end, } }, (const phy_cmd_t []) { /* ack_int */ { mk_mii_read(MII_REG_SR), mii_parse_sr }, { mk_mii_read(MII_REG_CR), mii_parse_cr }, { mk_mii_read(MII_DP83846A_PHYSTS), mii_parse_dp83846a_physts }, { mk_mii_end, } }, (const phy_cmd_t []) { /* shutdown - disable interrupts */ { mk_mii_end, } }};#endif /* CONFIG_FEC_DP83846A */static phy_info_t *phy_info[] = {#ifdef CONFIG_FEC_LXT970 &phy_info_lxt970,#endif /* CONFIG_FEC_LXT970 */#ifdef CONFIG_FEC_LXT971 &phy_info_lxt971,#endif /* CONFIG_FEC_LXT971 */#ifdef CONFIG_FEC_QS6612 &phy_info_qs6612,#endif /* CONFIG_FEC_LXT971 */#ifdef CONFIG_FEC_DP83843 &phy_info_dp83843,#endif /* CONFIG_FEC_DP83843 */#ifdef CONFIG_FEC_DP83846A &phy_info_dp83846a,#endif /* CONFIG_FEC_DP83846A */ NULL};static void mii_display_status(struct net_device *dev){ struct fec_enet_private *fep = dev->priv; volatile uint *s = &(fep->phy_status); if (!fep->link && !fep->old_link) { /* Link is still down - don't print anything */ return; } printk("%s: status: ", dev->name); if (!fep->link) { printk("link down"); } else { printk("link up"); switch(*s & PHY_STAT_SPMASK) { case PHY_STAT_100FDX: printk(", 100 Mbps Full Duplex"); break; case PHY_STAT_100HDX: printk(", 100 Mbps Half Duplex"); break; case PHY_STAT_10FDX: printk(", 10 Mbps Full Duplex"); break; case PHY_STAT_10HDX: printk(", 10 Mbps Half Duplex"); break; default: printk(", Unknown speed/duplex"); } if (*s & PHY_STAT_ANC) printk(", auto-negotiation complete"); } if (*s & PHY_STAT_FAULT) printk(", remote fault"); printk(".\n");}static void mii_display_config(struct net_device *dev){ struct fec_enet_private *fep = dev->priv; volatile uint *s = &(fep->phy_status); printk("%s: config: auto-negotiation ", dev->name); if (*s & PHY_CONF_ANE) printk("on"); else printk("off"); if (*s & PHY_CONF_100FDX) printk(", 100FDX"); if (*s & PHY_CONF_100HDX) printk(", 100HDX"); if (*s & PHY_CONF_10FDX) printk(", 10FDX"); if (*s & PHY_CONF_10HDX) printk(", 10HDX"); if (!(*s & PHY_CONF_SPMASK)) printk(", No speed/duplex selected?"); if (*s & PHY_CONF_LOOP) printk(", loopback enabled"); printk(".\n"); fep->sequence_done = 1;}static void mii_relink(struct net_device *dev){ struct fec_enet_private *fep = dev->priv; int duplex; fep->link = (fep->phy_status & PHY_STAT_LINK) ? 1 : 0; mii_display_status(dev); fep->old_link = fep->link; if (fep->link) { duplex = 0; if (fep->phy_status & (PHY_STAT_100FDX | PHY_STAT_10FDX)) duplex = 1; fec_restart(dev, duplex); } else fec_stop(dev);#if 0 enable_irq(fep->mii_irq);#endif}static void mii_queue_relink(uint mii_reg, struct net_device *dev, uint data){ struct fec_enet_private *fep = dev->priv; fep->phy_task.routine = (void *)mii_relink; fep->phy_task.data = dev; schedule_task(&fep->phy_task);}static void mii_queue_config(uint mii_reg, struct net_device *dev, uint data){ struct fec_enet_private *fep = dev->priv; fep->phy_task.routine = (void *)mii_display_config; fep->phy_task.data = dev; schedule_task(&fep->phy_task);}phy_cmd_t phy_cmd_relink[] = { { mk_mii_read(MII_REG_CR), mii_queue_relink }, { mk_mii_end, } };phy_cmd_t phy_cmd_config[] = { { mk_mii_read(MII_REG_CR), mii_queue_config }, { mk_mii_end, } };/* Read remainder of PHY ID.*/static voidmii_discover_phy3(uint mii_reg, struct net_device *dev, uint data){ struct fec_enet_private *fep; int i; fep = dev->priv; fep->phy_id |= (mii_reg & 0xffff); for(i = 0; phy_info[i]; i++) if(phy_info[i]->id == (fep->phy_id >> 4)) break; if(!phy_info[i]) panic("%s: PHY id 0x%08x is not supported!\n", dev->name, fep->phy_id); fep->phy = phy_info[i]; fep->phy_id_done = 1; printk("%s: Phy @ 0x%x, type %s (0x%08x)\n", dev->name, fep->phy_addr, fep->phy->name, fep->phy_id);}/* Scan all of the MII PHY addresses looking for someone to respond * with a valid ID. This usually happens quickly. */static voidmii_discover_phy(uint mii_reg, struct net_device *dev, uint data){ struct fec_enet_private *fep; uint phytype; fep = dev->priv; if ((phytype = (mii_reg & 0xffff)) != 0xffff) { /* Got first part of ID, now get remainder. */ fep->phy_id = phytype << 16; mii_queue(dev, mk_mii_read(MII_REG_PHYIR2), mii_discover_phy3, 0); } else { fep->phy_addr++; if (fep->phy_addr < 32) { mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy, 0); } else { printk("fec: No PHY device found.\n"); } }}#endif /* CONFIG_USE_MDIO *//* This interrupt occurs when the PHY detects a link change.*/static void#ifdef CONFIG_RPXCLASSICmii_link_interrupt(void *dev_id)#elsemii_link_interrupt(int irq, void * dev_id, struct pt_regs * regs)#endif{#ifdef CONFIG_USE_MDIO struct net_device *dev = dev_id; struct fec_enet_private *fep = dev->priv; volatile immap_t *immap = (immap_t *)IMAP_ADDR; volatile fec_t *fecp = &(immap->im_cpm.cp_fec); unsigned int ecntrl = fecp->fec_ecntrl; /* We need the FEC enabled to access the MII */ if ((ecntrl & FEC_ECNTRL_ETHER_EN) == 0) { fecp->fec_ecntrl |= FEC_ECNTRL_ETHER_EN; }#endif /* CONFIG_USE_MDIO */#if 0 disable_irq(fep->mii_irq); /* disable now, enable later */#endif#ifdef CONFIG_USE_MDIO mii_do_cmd(dev, fep->phy->ack_int); mii_do_cmd(dev, phy_cmd_relink); /* restart and display status */ if ((ecntrl & FEC_ECNTRL_ETHER_EN) == 0) { fecp->fec_ecntrl = ecntrl; /* restore old settings */ }#elseprintk("%s[%d] %s: unexpected Link interrupt\n", __FILE__,__LINE__,__FUNCTION__);#endif /* CONFIG_USE_MDIO */}static intfec_enet_open(struct net_device *dev){ struct fec_enet_private *fep = dev->priv; /* I should reset the ring buffers here, but I don't yet know * a simple way to do that. */#ifdef CONFIG_USE_MDIO fep->sequence_done = 0; fep->link = 0; if (fep->phy) { mii_do_cmd(dev, fep->phy->config); mii_do_cmd(dev, phy_cmd_config); /* display configuration */ while(!fep->sequence_done) schedule(); mii_do_cmd(dev, fep->phy->startup);#if defined(CONFIG_USE_MDIO) && defined(CONFIG_FEC_DP83846A) if(fep->phy == &phy_info_dp83846a) { /* Initializing timers */ init_timer( &fep->phy_timer_list ); /* Starting timer for periodic link status check * After 100 milli-seconds, mdio_timer_callback function is called. */ fep->phy_timer_list.expires = jiffies + (100 * HZ / 1000); fep->phy_timer_list.data = (unsigned long)dev; fep->phy_timer_list.function = mdio_timer_callback; add_timer( &fep->phy_timer_list ); }#if defined(CONFIG_IP_PNP) printk("%s: Waiting for the link to be up...\n", dev->name); while(fep->link == 0 || ((((volatile fec_t*)dev->base_addr)->fec_ecntrl & FEC_ECNTRL_ETHER_EN) == 0)) { schedule(); }#endif /* CONFIG_IP_PNP */#endif /* CONFIG_USE_MDIO && CONFIG_FEC_DP83846A */ netif_start_queue(dev); return 0; /* Success */ } return -ENODEV; /* No PHY we understand */#else fep->link = 1; netif_start_queue(dev); return 0; /* Success */#endif /* CONFIG_USE_MDIO */}static intfec_enet_close(struct net_device *dev){ /* Don't know what to do yet. */ netif_stop_queue(dev); fec_stop(dev); return 0;}static struct net_device_stats *fec_enet_get_stats(struct net_device *dev){ struct fec_enet_private *fep = (struct fec_enet_private *)dev->priv; return &fep->stats;}#ifdef CONFIG_USE_MDIO#if defined(CONFIG_FEC_DP83846A)/* Execute the ack_int command set and schedules next timer call back. */static void mdio_timer_callback(unsigned long data){ struct net_device *dev = (struct net_device *)data; struct fec_enet_private *fep = (struct fec_enet_private *)(dev->priv); mii_do_cmd(dev, fep->phy->ack_int); if(fep->link == 0) { fep->phy_timer_list.expires = jiffies + (100 * HZ / 1000); /* Sleep for 100ms */ } else { fep->phy_timer_list.expires = jiffies + (1 * HZ); /* Sleep for 1 sec. */ } add_timer( &fep->phy_timer_list ); }#endif /* CONFIG_FEC_DP83846A */static void mdio_callback(uint regval, struct net_device *dev, uint data){ mdio_read_data_t* mrd = (mdio_read_data_t *)data; mrd->regval = 0xFFFF & regval; wake_up_process(mrd->sleeping_task);}static int mdio_read(struct net_device *dev, int phy_id, int location){ uint retval; mdio_read_data_t* mrd = (mdio_read_data_t *)kmalloc(sizeof(*mrd), GFP_KERNEL); mrd->sleeping_task = current; set_current_state(TASK_INTERRUPTIBLE); mii_queue(dev, mk_mii_read(location), mdio_callback, (unsigned int) mrd); schedule(); retval = mrd->regval; kfree(mrd); return retval;}void mdio_write(struct net_device *dev, int phy_id, int location, int value){ mii_queue(dev, mk_mii_write(location, value), NULL, 0);}static int fec_enet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd){ struct fec_enet_private *cep = (struct fec_enet_private *)dev->priv; struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data; int phy = cep->phy_addr & 0x1f; int retval; if (data == NULL) { retval = -EINVAL; } else { switch(cmd) { case SIOCETHTOOL: return netdev_ethtool_ioctl(dev, (void*)rq->ifr_data); break; case SIOCGMIIPHY: /* Get address of MII PHY in use. */ case SIOCDEVPRIVATE: /* for binary compat, remove in 2.5 */ data->phy_id = phy; case SIOCGMIIREG: /* Read MII PHY register. */ case SIOCDEVPRIVATE+1: /* for binary compat, remove in 2.5 */ data->val_out = mdio_read(dev, data->phy_id & 0x1f, data->reg_num & 0x1f); retval = 0; break; case SIOCSMIIREG: /* Write MII PHY register. */ case SIOCDEVPRIVATE+2: /* for binary compat, remove in 2.5 */ if (!capable(CAP_NET_ADMIN)) { retval = -EPERM; } else { mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in); retval = 0; } break; default: retval = -EOPNOTSUPP; break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -