linux驱动移植-DM9000网卡驱动

----------------------------------------------------------------------------------------------------------------------------

内核版本:linux 5.2.8

根文件系统:busybox 1.25.0

u-boot:2016.05

----------------------------------------------------------------------------------------------------------------------------

在学习Mini2440裸机程序时,我们介绍过关于DM9000网卡的相关知识,包括电路图、以及DM9000寄存器等信息。具体可以参考Mini2440裸机开发之DM9000

本节对之前已经介绍过的知识不会再进行重复介绍。这一节我们将直入主题,介绍如何移植DM9000网卡驱动。

一、platform设备注册(dm9000)

在刚学习驱动移植的时候,我们为了使用nfs作为根文件系统,我们在 linux驱动移植-DM9000网卡驱动小节介绍了DM9000网卡驱动的移植,但是那时候我们仅仅是移植,并未对源码进行深入研究。 DM9000网卡设备驱动,其采用的也是platform设备驱动模型。
1.1 smdk2440_device_eth

我们定位到arch/arm/mach-s3c24xx/mach-smdk2440.c文件,在该文件中我们引入了dm9000.h头文件:

#include <linux/dm9000.h>

定义了DM9000网卡设备的物理基地址:

#define MACH_SMDK2440_DM9K_BASE (S3C2410_CS4 + 0x300)     # S3C2410_CS4 = 0X20000000

定义了网卡platform设备smdk2440_device_eth:

/* DM9000AEP 10/100 ethernet controller */

static struct resource smdk2440_dm9k_resource[] = {
        [0] = DEFINE_RES_MEM(MACH_SMDK2440_DM9K_BASE, 4),
        [1] = DEFINE_RES_MEM(MACH_SMDK2440_DM9K_BASE + 4, 4),
        [2] = DEFINE_RES_NAMED(IRQ_EINT7, 1, NULL, IORESOURCE_IRQ
                                                | IORESOURCE_IRQ_HIGHEDGE),   // 高电平触发
};

/*
 * The DM9000 has no eeprom, and it's MAC address is set by
 * the bootloader before starting the kernel.
 */
static struct dm9000_plat_data smdk2440_dm9k_pdata = {
        .flags          = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM),    // 标志位,16位模式、DM9000没有外挂EPPROM
};

static struct platform_device smdk2440_device_eth = {
        .name           = "dm9000",
        .id             = -1,
        .num_resources  = ARRAY_SIZE(smdk2440_dm9k_resource),
        .resource       = smdk2440_dm9k_resource,
        .dev            = {
                .platform_data  = &smdk2440_dm9k_pdata,
        },
};

我们重点关注一下platform设备资源定义,这里定义了三个资源,前两个是内存资源、第三个是中断资源:

注意:

  • 在定义DM9000网卡platform设备的时,第一个资源必须是IO内存资源,且定义的是DM9000地址寄存器地址;
  • 在定义DM9000网卡platform设备的时,第二个资源必须是IO内存资源,且定义的是DM9000数据寄存器地址;
  • 在定义DM9000网卡platform设备的时,第三个资源必须是中断资源,CPU中断引脚连接DM9000 INT引脚;

dm9000_plat_data用来保存DM9000设备的初始化配置,除了flags,还有一些其它参数,比如:

  • dev_addr:设置DM9000设备的初始化MAC地址;
  • inblk:设置接收数据操作函数;
  • outblk:设置发送数据操作函数;

我们已经定义了网卡相关的platform_device设备smdk2440_device_eth,并进行了初始化,那platform设备啥时候注册的呢?

linux内核启动的时候会根据uboot中设置的机器id执行相应的初始化工作,比如.init_machine、.init_irq,我们首先定位到arch/arm/mach-s3c24xx/mach-smdk2440.c:

MACHINE_START(S3C2440, "SMDK2440")
        /* Maintainer: Ben Dooks <ben-linux@fluff.org> */
        .atag_offset    = 0x100,

        .init_irq       = s3c2440_init_irq,
        .map_io         = smdk2440_map_io,
        .init_machine   = smdk2440_machine_init,
        .init_time      = smdk2440_init_time,
MACHINE_END

重点关注init_machine,init_machine中保存的是开发板资源注册的初始化代码。

1.2  smdk2440_machine_init
static void __init smdk2440_machine_init(void)
{
        s3c24xx_fb_set_platdata(&smdk2440_fb_info);   
        s3c_i2c0_set_platdata(NULL);

        platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));  // s3c2440若干个platform设备注册 usb host controller、lcd、wdt等
        smdk_machine_init();  // s3c24x0系列若干个platform设备注册(通用)
}

这里利用platform_add_devices进行若干个platform设备的注册,该函数通过调用platform_device_register实现platform设备注册.

static struct platform_device *smdk2440_devices[] __initdata = {
        &s3c_device_ohci,
        &s3c_device_lcd,
        &s3c_device_wdt,
        &s3c_device_i2c0,
        &s3c_device_iis,
        &smdk2440_device_eth,
};

二、platform驱动注册(dm9000)

我们需要配置内核支持DM9000网卡驱动:

Device Drivers --->
   [*] Network device support -->
       [*] Ethernet driver support -->
[*] DM9000 support

这样我们内核才会支持DM9000网卡驱动。配置了DM9000 support之后,配置文件.config中会包含如下项:

# CONFIG_GEMINI_ETHERNET is not set
CONFIG_DM9000=y

当我们使用make uImage编译内核时会将dm9000.o编译进内核:

drivers/net/ethernet/davicom/Makefile:6:obj-$(CONFIG_DM9000) += dm9000.o

dm9000.c文件位于drivers/net/ethernet/davicom目录下。

2.1 入口和出口函数

我们在dm9000.c文件定位到驱动模块的入口和出口:

module_platform_driver(dm9000_driver);

module_platform_driver宏展开后本质上就是:

module_init(dm9000_driver_init); 
module_exit(dm9000_driver_exit); 
static int __init dm9000_driver_init(void)
{
     platform_driver_register(dm9000_driver);
}

static void __exit dm9000_driver_exit(void)
{
     platform_driver_unregister(dm9000_driver);
}

看到这里是不是有点意外,这里是通过platform_driver_register函数注册了一个platform驱动。

在plaftrom总线设备驱动模型中,我们知道当内核中有platform设备platform驱动匹配,会调用到platform_driver里的成员.probe,在这里就是dm9000_probe函数。

#ifdef CONFIG_OF
static const struct of_device_id dm9000_of_matches[] = {   // 设备树匹配使用
        { .compatible = "davicom,dm9000", },
        { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, dm9000_of_matches);
#endif

static struct platform_driver dm9000_driver = {
        .driver = {
                .name    = "dm9000",
                .pm      = &dm9000_drv_pm_ops,
                .of_match_table = of_match_ptr(dm9000_of_matches),
        },
        .probe   = dm9000_probe,
        .remove  = dm9000_drv_remove,
};
2.2 dm9000_probe
/*
 * Search DM9000 board, allocate space and register it
 */
static int
dm9000_probe(struct platform_device *pdev)
{
        struct dm9000_plat_data *pdata = dev_get_platdata(&pdev->dev);      // pdev->dev.platform_data
        struct board_info *db;  /* Point a board information structure */
        struct net_device *ndev;
        struct device *dev = &pdev->dev;
        const unsigned char *mac_src;
        int ret = 0;
        int iosize;
        int i;
        u32 id_val;
        int reset_gpios;
        enum of_gpio_flags flags;
        struct regulator *power;
        bool inv_mac_addr = false;

        power = devm_regulator_get(dev, "vcc");
        if (IS_ERR(power)) {
                if (PTR_ERR(power) == -EPROBE_DEFER)
                        return -EPROBE_DEFER;
                dev_dbg(dev, "no regulator provided\n");
        } else {
                ret = regulator_enable(power);
                if (ret != 0) {
                        dev_err(dev,
                                "Failed to enable power regulator: %d\n", ret);
                        return ret;
                }
                dev_dbg(dev, "regulator enabled\n");
        }

        reset_gpios = of_get_named_gpio_flags(dev->of_node, "reset-gpios", 0,
                                              &flags);
        if (gpio_is_valid(reset_gpios)) {
                ret = devm_gpio_request_one(dev, reset_gpios, flags,
                                            "dm9000_reset");
                if (ret) {
                        dev_err(dev, "failed to request reset gpio %d: %d\n",
                                reset_gpios, ret);
                        return -ENODEV;
                }

                /* According to manual PWRST# Low Period Min 1ms */
                msleep(2);
                gpio_set_value(reset_gpios, 1);
                /* Needs 3ms to read eeprom when PWRST is deasserted */
                msleep(4);
        }

     
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Graceful_scenery

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值