imx6 fec分析

时间:2023-12-11 17:29:26
/*****************************************************************************
* imx6 fec分析
* 本文主要分析imx6的网卡程序,phy使用ar8031。
*
* Tony Liu, 2016-4-19, Shenzhen
****************************************************************************/
/* 注册设备 */
kernel/arch/arm/mach-mx6/board_mx6q_sabresd.c
MACHINE_START(MX6Q_SABRESD, "Freescale i.MX 6Quad/DualLite/Solo Sabre-SD Board")
/* Maintainer: Freescale Semiconductor, Inc. */
.boot_params = MX6_PHYS_OFFSET + 0x100,
.fixup = fixup_mxc_board,
.map_io = mx6_map_io,
.init_irq = mx6_init_irq,
.init_machine = mx6_sabresd_board_init,
.timer = &mx6_sabresd_timer, |
.reserve = mx6q_sabresd_reserve, |
MACHINE_END |
V
static void __init mx6_sabresd_board_init(void)
{
... ...
imx6_init_fec(fec_data); ---------------------------------------------+
... ... | |
} | |
V |
void __init imx6_init_fec(struct fec_platform_data fec_data) |
{ |
fec_get_mac_addr(fec_data.mac); //获得mac地址 | |
if (!is_valid_ether_addr(fec_data.mac)) | |
random_ether_addr(fec_data.mac); | |
| |
if (cpu_is_mx6sl()) | |
imx6sl_add_fec(&fec_data); //注册设备 --------------------+ |
else | | |
imx6q_add_fec(&fec_data); | | |
} | | |
| | |
static int fec_get_mac_addr(unsigned char *mac) <------+ | |
{ | |
unsigned int value; | |
| |
value = readl(MX6_IO_ADDRESS(OCOTP_BASE_ADDR) + HW_OCOTP_MACn()); | |
value = 0x03040506; | |
mac[] = value & 0xff; | |
mac[] = (value >> ) & 0xff; | |
mac[] = (value >> ) & 0xff; | |
mac[] = (value >> ) & 0xff; | |
value = readl(MX6_IO_ADDRESS(OCOTP_BASE_ADDR) + HW_OCOTP_MACn()); | |
value = 0x0102; | |
mac[] = value & 0xff; | |
mac[] = (value >> ) & 0xff; | |
| |
return ; | |
} | |
| |
#define imx6sl_add_fec(pdata) \ <-----------------------+ |
imx_add_fec(&imx6sl_fec_data, pdata) | |
| |
struct platform_device *__init imx_add_fec( <-----------------------+ |
const struct imx_fec_data *data, |
const struct fec_platform_data *pdata) |
{ |
struct resource res[] = { |
{ |
.start = data->iobase, |
.end = data->iobase + SZ_4K - , |
.flags = IORESOURCE_MEM, |
}, { |
.start = data->irq, |
.end = data->irq, |
.flags = IORESOURCE_IRQ, |
}, |
}; |
|
if (!fuse_dev_is_available(MXC_DEV_ENET)) |
return ERR_PTR(-ENODEV); |
|
return imx_add_platform_device_dmamask(data->devid, , |
res, ARRAY_SIZE(res), |
pdata, sizeof(*pdata), DMA_BIT_MASK()); |
} |
|
|
static struct fec_platform_data fec_data __initdata = { <-----------+
.init = mx6q_sabresd_fec_phy_init, ----------+
.power_hibernate = mx6_sabresd_fec_power_hibernate, |
.phy = PHY_INTERFACE_MODE_RGMII, | |
#ifdef CONFIG_MX6_ENET_IRQ_TO_GPIO | |
.gpio_irq = MX6_ENET_IRQ, | |
#endif | |
}; | |
V |
static int mx6_sabresd_fec_power_hibernate(struct phy_device *phydev) |
{ |
unsigned short val; |
|
/*set AR8031 debug reg 0xb to hibernate power*/ |
phy_write(phydev, 0x1d, 0xb); |
val = phy_read(phydev, 0x1e); |
|
val |= 0x8000; |
phy_write(phydev, 0x1e, val); |
|
return ; |
} |
|
static int mx6q_sabresd_fec_phy_init(struct phy_device *phydev) <-------+
{
unsigned short val;
gpio_request(SABRESD_FEC_PHY_RESET,"phy-rst");
gpio_direction_output(SABRESD_FEC_PHY_RESET, );
mdelay();
gpio_direction_output(SABRESD_FEC_PHY_RESET, );
mdelay();
/* Ar8031 phy SmartEEE feature cause link status generates glitch,
* which cause ethernet link down/up issue, so disable SmartEEE
*/
/* ar8031芯片手册中有体积,有三种寄存器,不同的寄存器读取方法不一样。*/
// 1. 0xd确定设备地址, 0xe读取其中的值
phy_write(phydev, 0xd, 0x3); //芯片device address: 3
phy_write(phydev, 0xe, 0x805d); // offset: 0x805D
phy_write(phydev, 0xd, 0x4003); //keep the device address
val = phy_read(phydev, 0xe); //读取寄存器0x805d 的值
val &= ~(0x1 << ); //disable smartEEE
phy_write(phydev, 0xe, val); /* To enable AR8031 ouput a 125MHz clk from CLK_25M */
phy_write(phydev, 0xd, 0x7);
phy_write(phydev, 0xe, 0x8016);
phy_write(phydev, 0xd, 0x4007); //不知到为何这里要用0x4007
val = phy_read(phydev, 0xe);
//设置时钟125M
val &= 0xffe3;
val |= 0x18;
phy_write(phydev, 0xe, val); // 2. 0x1d确定设备地址, 0x1e读取其中的值
//将要配置的寄存器的值写入0x1d
/* introduce tx clock delay */
phy_write(phydev, 0x1d, 0x5); //debug register : 0x05
//从0x1e读取寄存器0x5的值
val = phy_read(phydev, 0x1e);
val |= 0x0100; //rgmii tx clock delay enable
phy_write(phydev, 0x1e, val);
// 3. 一般的寄存器直接对地址进行操作,读取寄存器地址为0x0的值
val = phy_read(phydev, 0x0);
if (val & BMCR_PDOWN)
phy_write(phydev, 0x0, (val & ~BMCR_PDOWN)); return ;
} // 驱动注册
kernel/drivers/net/fec.c static int __init
fec_enet_module_init(void)
{
printk(KERN_INFO "FEC Ethernet Driver\n"); return platform_driver_register(&fec_driver);
} |
V
static struct platform_driver fec_driver = {
.driver = {
.name = DRIVER_NAME, // "fec"
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &fec_pm_ops,
#endif
},
.id_table = fec_devtype,
.probe = fec_probe, -------------+
.remove = __devexit_p(fec_drv_remove), |
}; |
|
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 = ;
struct resource *r; r = platform_get_resource(pdev, IORESOURCE_MEM, );
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->pdev = pdev; if (!fep->hwp) {
ret = -ENOMEM;
goto failed_ioremap;
} platform_set_drvdata(pdev, ndev); pdata = pdev->dev.platform_data;
if (pdata)
fep->phy_interface = pdata->phy; #ifdef CONFIG_MX6_ENET_IRQ_TO_GPIO
gpio_request(pdata->gpio_irq, "gpio_enet_irq");
gpio_direction_input(pdata->gpio_irq); irq = gpio_to_irq(pdata->gpio_irq);
ret = request_irq(irq, fec_enet_interrupt,
IRQF_TRIGGER_RISING,
pdev->name, ndev);
if (ret)
goto failed_irq;
#else
/* This device has up to three irqs on some platforms */
for (i = ; i < ; i++) {
irq = platform_get_irq(pdev, i);
if (i && irq < )
break;
ret = request_irq(irq, fec_enet_interrupt, IRQF_DISABLED, pdev->name, ndev);
if (ret) {
while (--i >= ) {
irq = platform_get_irq(pdev, i);
free_irq(irq, ndev);
}
goto failed_irq;
}
}
#endif fep->clk = clk_get(&pdev->dev, "fec_clk");
if (IS_ERR(fep->clk)) {
ret = PTR_ERR(fep->clk);
goto failed_clk;
}
clk_enable(fep->clk); ret = fec_enet_init(ndev); ----------------------------------------------+
if (ret) |
goto failed_init; |
|
ret = fec_enet_mii_init(pdev); -----------------------------------------+ |
if (ret) | |
goto failed_mii_init; | |
| |
if (fec_ptp_malloc_priv(&(fep->ptp_priv))) { | |
if (fep->ptp_priv) { | |
fep->ptp_priv->hwp = fep->hwp; | |
ret = fec_ptp_init(fep->ptp_priv, pdev->id); | |
if (ret) | |
printk(KERN_WARNING "IEEE1588: ptp-timer is unavailable\n"); | |
else | |
fep->ptimer_present = ; | |
} else | |
printk(KERN_ERR "IEEE1588: failed to malloc memory\n"); | |
} | |
| |
/* Carrier starts down, phylib will bring it up */ | |
netif_carrier_off(ndev); | |
clk_disable(fep->clk); | |
| |
INIT_DELAYED_WORK(&fep->fixup_trigger_tx, fixup_trigger_tx_func); | |
| |
ret = register_netdev(ndev); | |
if (ret) | |
goto failed_register; | |
| |
return ; | |
| |
failed_register: | |
fec_enet_mii_remove(fep); | |
if (fep->ptimer_present) | |
fec_ptp_cleanup(fep->ptp_priv); | |
kfree(fep->ptp_priv); | |
failed_mii_init: | |
failed_init: | |
clk_disable(fep->clk); | |
clk_put(fep->clk); | |
failed_clk: | |
#ifdef CONFIG_MX6_ENET_IRQ_TO_GPIO | |
free_irq(irq, ndev); | |
#else | |
for (i = ; i < ; i++) { | |
irq = platform_get_irq(pdev, i); | |
if (irq > ) | |
free_irq(irq, ndev); | |
} | |
#endif | |
failed_irq: | |
iounmap(fep->hwp); | |
failed_ioremap: | |
free_netdev(ndev); | |
failed_alloc_etherdev: | |
release_mem_region(r->start, resource_size(r)); | |
| |
return ret; | |
} | |
| |
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); |
int err = -ENXIO, i; |
|
/* |
* 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) && pdev->id) { |
/* fec1 uses fec0 mii_bus */ |
fep->mii_bus = fec0_mii_bus; |
return ; |
} |
|
fep->mii_timeout = ; |
|
/* |
* Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed) |
*/ |
fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk), |
(FEC_ENET_MII_CLK << )) << ; |
|
/* set hold time to 2 internal clock cycle */ |
if (cpu_is_mx6q() || cpu_is_mx6dl()) |
fep->phy_speed |= FEC_ENET_HOLD_TIME; |
|
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); |
|
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, "%x", pdev->id + ); |
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 = ; i < PHY_MAX_ADDR; i++) |
fep->mii_bus->irq[i] = PHY_POLL; |
|
if (mdiobus_register(fep->mii_bus)) |
goto err_out_free_mdio_irq; |
|
/* save fec0 mii_bus */ |
if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) |
fec0_mii_bus = fep->mii_bus; |
|
return ; |
|
err_out_free_mdio_irq: |
kfree(fep->mii_bus->irq); |
err_out_free_mdiobus: |
mdiobus_free(fep->mii_bus); |
err_out: |
return err; |
} |
static int fec_enet_init(struct net_device *ndev) <-------------------------+
{
struct fec_enet_private *fep = netdev_priv(ndev);
struct bufdesc *cbd_base;
struct bufdesc *bdp;
int i; /* Allocate memory for buffer descriptors. */
cbd_base = dma_alloc_noncacheable(NULL, BUFDES_SIZE, &fep->bd_dma,
GFP_KERNEL);
if (!cbd_base) {
printk("FEC: allocate descriptor memory failed?\n");
return -ENOMEM;
} spin_lock_init(&fep->hw_lock); fep->netdev = ndev; /* Get the Ethernet address */
fec_get_mac(ndev); /* Set receive and transmit descriptor base. */
fep->rx_bd_base = cbd_base;
fep->tx_bd_base = cbd_base + RX_RING_SIZE; /* 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; ----------------------+ |
| |
fep->use_napi = FEC_NAPI_ENABLE; | |
fep->napi_weight = FEC_NAPI_WEIGHT; | |
if (fep->use_napi) { | |
fec_rx_int_is_enabled(ndev, false); | |
netif_napi_add(ndev, &fep->napi, fec_rx_poll, fep->napi_weight); | |
} | |
| |
/* Initialize the receive buffer descriptors. */ | |
bdp = fep->rx_bd_base; | |
for (i = ; i < RX_RING_SIZE; i++) { | |
| |
/* Initialize the BD for every fragment in the page. */ | |
bdp->cbd_sc = ; | |
bdp->cbd_bufaddr = ; | |
bdp++; | |
} | |
| |
/* Set the last buffer to wrap */ | |
bdp--; | |
bdp->cbd_sc |= BD_SC_WRAP; | |
| |
/* Init transmit descriptors */ | |
fec_enet_txbd_init(ndev); | |
| |
fec_restart(ndev, ); ---------------------------+ | |
| | |
return ; | | |
} | | |
| | |
static void | | |
fec_restart(struct net_device *dev, int duplex) <--------+ | |
{ | |
struct fec_enet_private *fep = netdev_priv(dev); | |
const struct platform_device_id *id_entry = | |
platform_get_device_id(fep->pdev); | |
int i, ret; | |
u32 val, temp_mac[], reg = ; | |
| |
/* Whack a reset. We should wait for this. */ | |
writel(, fep->hwp + FEC_ECNTRL); | |
udelay(); | |
| |
/* if uboot don't set MAC address, get MAC address | |
* from command line; if command line don't set MAC | |
* address, get from OCOTP; otherwise, allocate random | |
* address. | |
*/ | |
memcpy(&temp_mac, dev->dev_addr, ETH_ALEN); | |
writel(cpu_to_be32(temp_mac[]), fep->hwp + FEC_ADDR_LOW); | |
writel(cpu_to_be32(temp_mac[]), fep->hwp + FEC_ADDR_HIGH); | |
| |
/* Clear any outstanding interrupt. */ | |
writel(0xffc00000, fep->hwp + FEC_IEVENT); | |
| |
/* Reset all multicast. */ | |
writel(, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); | |
writel(, fep->hwp + FEC_GRP_HASH_TABLE_LOW); | |
#ifndef CONFIG_M5272 | |
writel(, fep->hwp + FEC_HASH_TABLE_HIGH); | |
writel(, fep->hwp + FEC_HASH_TABLE_LOW); | |
#endif | |
| |
/* FIXME: adjust RX FIFO size for performance*/ | |
#ifdef CONFIG_ARCH_MX53 | |
writel(FEC_RX_FIFO_BR, fep->hwp + FEC_R_FSTART); | |
#endif | |
| |
/* Set maximum receive buffer size. */ | |
writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE); | |
| |
/* Set receive and transmit descriptor base. */ | |
writel(fep->bd_dma, fep->hwp + FEC_R_DES_START); | |
writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc) * RX_RING_SIZE, | |
fep->hwp + FEC_X_DES_START); | |
/* Reinit transmit descriptors */ | |
fec_enet_txbd_init(dev); | |
| |
fep->dirty_tx = fep->cur_tx = fep->tx_bd_base; | |
fep->cur_rx = fep->rx_bd_base; | |
| |
/* Reset SKB transmit buffers. */ | |
fep->skb_cur = fep->skb_dirty = ; | |
for (i = ; i <= TX_RING_MOD_MASK; i++) { | |
if (fep->tx_skbuff[i]) { | |
dev_kfree_skb_any(fep->tx_skbuff[i]); | |
fep->tx_skbuff[i] = NULL; | |
} | |
} | |
| |
/* Enable MII mode */ | |
if (duplex) { | |
/* MII enable / FD enable */ | |
writel(OPT_FRAME_SIZE | 0x04, fep->hwp + FEC_R_CNTRL); | |
writel(0x04, fep->hwp + FEC_X_CNTRL); | |
} else { | |
/* MII enable / No Rcv on Xmit */ | |
writel(OPT_FRAME_SIZE | 0x06, fep->hwp + FEC_R_CNTRL); | |
writel(0x0, fep->hwp + FEC_X_CNTRL); | |
} | |
fep->full_duplex = duplex; | |
| |
/* Set MII speed */ | |
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); | |
| |
/* | |
* The phy interface and speed need to get configured | |
* differently on enet-mac. | |
*/ | |
if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { | |
val = readl(fep->hwp + FEC_R_CNTRL); | |
| |
/* MII or RMII */ | |
if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII) { | |
val |= ( << ); | |
printk("<tony> RGMII\n"); | |
} | |
else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) { | |
val |= ( << ); | |
printk("<tony> RMII\n"); | |
} | |
else { | |
val &= ~( << ); | |
printk("<tony> MII\n"); | |
} | |
//时能10M/100M支持 | |
/* 10M or 100M */ | |
if (fep->phy_dev && fep->phy_dev->speed == SPEED_100) { | |
val &= ~( << ); | |
printk("<tony> 1702, speed:100\n"); | |
} | |
else { | |
val |= ( << ); | |
printk("<tony> 1706, speed:10\n"); | |
} | |
| |
/* Enable pause frame | |
* ENET pause frame has two issues as ticket TKT116501 | |
* The issues have been fixed on Rigel TO1.1 and Arik TO1.2 | |
*/ | |
if ((cpu_is_mx6q() && | |
(mx6q_revision() >= IMX_CHIP_REVISION_1_2)) || | |
(cpu_is_mx6dl() && | |
(mx6dl_revision() >= IMX_CHIP_REVISION_1_1))) | |
val |= FEC_ENET_FCE; | |
| |
writel(val, fep->hwp + FEC_R_CNTRL); | |
} | |
| |
if (fep->ptimer_present) { | |
/* Set Timer count */ | |
ret = fec_ptp_start(fep->ptp_priv); | |
if (ret) { | |
fep->ptimer_present = ; | |
reg = 0x0; | |
} else | |
#if defined(CONFIG_SOC_IMX28) || defined(CONFIG_ARCH_MX6) | |
reg = 0x00000010; | |
#else | |
reg = 0x0; | |
#endif | |
} else | |
reg = 0x0; | |
| |
if (cpu_is_mx25() || cpu_is_mx53() || cpu_is_mx6sl()) { | |
if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) { | |
/* disable the gasket and wait */ | |
writel(, fep->hwp + FEC_MIIGSK_ENR); | |
while (readl(fep->hwp + FEC_MIIGSK_ENR) & ) | |
udelay(); | |
| |
/* | |
* configure the gasket: | |
* RMII, 50 MHz, no loopback, no echo | |
*/ | |
writel(, fep->hwp + FEC_MIIGSK_CFGR); | |
| |
/* re-enable the gasket */ | |
writel(, fep->hwp + FEC_MIIGSK_ENR); | |
udelay(); | |
if (!(readl(fep->hwp + FEC_MIIGSK_ENR) & )) { | |
udelay(); | |
if (!(readl(fep->hwp + FEC_MIIGSK_ENR) & )) | |
dev_err(&fep->pdev->dev, | |
"switch to RMII failed!\n"); | |
} | |
} | |
} | |
| |
/* ENET enable */ | |
val = reg | (0x1 << ); | |
//根据条件设置网络为1G | |
/* if phy work at 1G mode, set ENET RGMII speed to 1G */ | |
if (fep->phy_dev && (fep->phy_dev->supported & | |
(SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)) && | |
fep->phy_interface == PHY_INTERFACE_MODE_RGMII && | |
fep->phy_dev->speed == SPEED_1000) { | |
val |= (0x1 << ); //使能1000M模式 | |
printk(KERN_WARNING "<tony> 1772 speed:1000\n"); | |
} | |
| |
/* RX FIFO threshold setting for ENET pause frame feature | |
* Only set the parameters after ticket TKT116501 fixed. | |
* The issue has been fixed on Rigel TO1.1 and Arik TO1.2 | |
*/ | |
if ((cpu_is_mx6q() && | |
(mx6q_revision() >= IMX_CHIP_REVISION_1_2)) || | |
(cpu_is_mx6dl() && | |
(mx6dl_revision() >= IMX_CHIP_REVISION_1_1))) { | |
writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM); | |
writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL); | |
writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM); | |
writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL); | |
| |
/* OPD */ | |
writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD); | |
} | |
| |
if (cpu_is_mx6q() || cpu_is_mx6dl()) { | |
/* enable endian swap */ | |
val |= (0x1 << ); | |
/* enable ENET store and forward mode */ | |
writel(0x1 << , fep->hwp + FEC_X_WMRK); | |
} | |
writel(val, fep->hwp + FEC_ECNTRL); | |
| |
writel(, fep->hwp + FEC_R_DES_ACTIVE); | |
| |
/* Enable interrupts we wish to service */ | |
if (cpu_is_mx6q() || cpu_is_mx6dl() || cpu_is_mx2() || cpu_is_mx3()) | |
val = (FEC_1588_IMASK | FEC_DEFAULT_IMASK); | |
else | |
val = FEC_DEFAULT_IMASK; | |
writel(val, fep->hwp + FEC_IMASK); | |
} | |
| |
static 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, |
}; |
|
|
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_multicast_list = 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_enet_netpoll, |
#endif |
}; |
|
static int |
fec_enet_open(struct net_device *ndev) <-------------------------------+
{
struct fec_enet_private *fep = netdev_priv(ndev);
struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
int ret; if (fep->use_napi)
napi_enable(&fep->napi); /* I should reset the ring buffers here, but I don't yet know
* a simple way to do that.
*/
clk_enable(fep->clk);
ret = fec_enet_alloc_buffers(ndev);
if (ret)
return ret; /* Probe and connect to PHY when open the interface */
ret = fec_enet_mii_probe(ndev); ----------------------+
if (ret) { |
fec_enet_free_buffers(ndev); |
return ret; |
} |
|
phy_start(fep->phy_dev); |
netif_start_queue(ndev); |
fep->opened = ; |
|
ret = -EINVAL; |
if (pdata->init && pdata->init(fep->phy_dev)) |
return ret; |
|
return ; |
} |
|
kernel/drivers/net/fec.c |
static int fec_enet_mii_probe(struct net_device *ndev) <------------+
{
struct fec_enet_private *fep = netdev_priv(ndev);
struct phy_device *phy_dev = NULL;
char mdio_bus_id[MII_BUS_ID_SIZE];
char phy_name[MII_BUS_ID_SIZE + ];
int phy_id;
int dev_id = fep->pdev->id; fep->phy_dev = NULL; /* check for attached phy */
for (phy_id = ; (phy_id < PHY_MAX_ADDR); phy_id++) {
if ((fep->mii_bus->phy_mask & ( << phy_id)))
continue;
if (fep->mii_bus->phy_map[phy_id] == NULL)
continue;
if (fep->mii_bus->phy_map[phy_id]->phy_id == )
continue;
if (dev_id--)
continue;
strncpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE);
break;
} if (phy_id >= PHY_MAX_ADDR) {
printk(KERN_INFO "%s: no PHY, assuming direct connection "
"to switch\n", ndev->name);
strncpy(mdio_bus_id, "", MII_BUS_ID_SIZE);
phy_id = ;
} snprintf(phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT, mdio_bus_id, phy_id);
phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, ,
fep->phy_interface); |
+--------------------+
if (IS_ERR(phy_dev)) { |
printk(KERN_ERR "%s: could not attach to PHY\n", ndev->name); |
return PTR_ERR(phy_dev); |
} |
|
/* mask with MAC supported features */ |
if (cpu_is_mx6q() || cpu_is_mx6dl()) |
phy_dev->supported &= PHY_BASIC_FEATURES; |
// phy_dev->supported &= PHY_GBIT_FEATURES; |
else |
phy_dev->supported &= PHY_BASIC_FEATURES; |
|
/* enable phy pause frame for any platform */ |
phy_dev->supported |= ADVERTISED_Pause; |
|
phy_dev->advertising = phy_dev->supported; |
|
fep->phy_dev = phy_dev; |
fep->link = ; |
fep->full_duplex = ; |
|
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); |
|
return ; |
} |
|
kernel/drivers/net/fec.c |
static void fec_enet_adjust_link(struct net_device *ndev) <--------------+
{
struct fec_enet_private *fep = netdev_priv(ndev);
struct phy_device *phy_dev = fep->phy_dev;
struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
unsigned long flags; int status_change = ; spin_lock_irqsave(&fep->hw_lock, flags); /* Prevent a state halted on mii error */
if (fep->mii_timeout && phy_dev->state == PHY_HALTED) {
phy_dev->state = PHY_RESUMING;
goto spin_unlock;
} /* Duplex link change */
if (phy_dev->link) {
if (fep->full_duplex != phy_dev->duplex) {
fec_restart(ndev, phy_dev->duplex);
status_change = ;
}
} /* Link on or off change */
if (phy_dev->link != fep->link) {
fep->link = phy_dev->link;
if (phy_dev->link) {
fec_restart(ndev, phy_dev->duplex);
if (!fep->tx_full)
netif_wake_queue(ndev);
} else
fec_stop(ndev);
status_change = ;
} spin_unlock:
spin_unlock_irqrestore(&fep->hw_lock, flags); if (status_change) {
if (!phy_dev->link && phy_dev && pdata && pdata->power_hibernate)
pdata->power_hibernate(phy_dev);
phy_print_status(phy_dev);
} |
} |
|
V
/drivers/net/phy/phy.c
void phy_print_status(struct phy_device *phydev) //这里使每次网线插拔之后会在串口打印输出的内容
{
pr_info("PHY: %s - Link is %s", dev_name(&phydev->dev),
phydev->link ? "Up" : "Down");
if (phydev->link)
printk(KERN_CONT " - %d/%s", phydev->speed,
DUPLEX_FULL == phydev->duplex ?
"Full" : "Half"); printk(KERN_CONT "\n");
}