首页 > 系统相关 >在f1c100s芯片上移植spi网卡enc28j60的linux驱动

在f1c100s芯片上移植spi网卡enc28j60的linux驱动

时间:2023-08-21 11:56:15浏览次数:35  
标签:FUNCTION f1c100s pinctrl 网卡 spi pins enc28j60 SUNXI

前言

我个人与全志的芯片颇有故事。在我还是一个不懂事的高中生时,我看到荔枝派的官方文档,顿时被这小小的板子给吸引住。点开文档的初见:

荔枝派Nano(下面简称Nano)是一款精致迷你的 Arm9 核心板/开发板,可用于初学者学习linux或者商用于产品开发。 Nano 在与SD卡相当的尺寸上(25.4 * 33mm)提供了丰富的外设 (LCD,UART,SPI,I2C,PWM,SDIO,KEYADC...)和较为强劲的性能(24M~408MHz, 32MB DDR)。
Nano 延续并发展了Zero精巧的PCB设计,使得开发和使用非常方便:

2.54mm排针直插面包板
直插40P RGB LCD
使用OTG口进行供电和数据传输(虚拟串口,更新固件等)
可配合使用使用堆叠式的WiFi 模块联网
?可直接贴片

当时高一的我连什么是单片机和微处理器都分不清楚,只觉得好神奇,居然有这样一种单片机,性能居然这么强悍,芯片封装那么小,还能跑linux?于是速速下单买了一块荔枝派回来。拿到手也不咋会用,照着教程不是报错就是缺库,再加上学业繁重,就放在那吃灰了。

后来高二暑假,再次捡起这块板子,把uboot编译了,又把磕磕碰碰地把linux的设备树照抄过来,磕磕碰碰地开了两个窗口照抄内核配置。当时就是喜欢配内核,配来配去,虽然这个过程非常操蛋,但是当终于解决了好多文档里根本不提的问题(比如说regulator必须要在内核配置中打开等)后,又觉得人生十分圆满,我终于会搞嵌入式linux了。21年的高二暑假把内核和rootfs配好后,手上刚好又有个为了给单片机提供以太网用的enc28j60模块。刚好点开linux的menuconfig,也发现内核支持这个模块,从此就和enc28j60结下了梁子。

我用的内核版本是5.4

第一次移植过程

在我脑海中,我依稀记得当初有多稚嫩。我在whycan上翻来翻去,总算知道了给linux内核加驱动要改设备树。然后我去翻了半天linux的documentation,却还是没搞懂设备树到底要怎么写。这不纯纯坑人+浪费时间吗?当时高二期末考试前没事干,就用电脑一直翻torvalds/linux仓库的源码,把enc28j60的源码看了,看不懂,又去看设备树的源代码,那个更加是天书,不知道在说个啥。后来放假了,就动手开始移植,我首先打开了linux/Documentation/devicetree/bindings/net/microchip,enc28j60.txt,这个是驱动开发者为我们留下的文档,让我们看看他说了个啥

* Microchip ENC28J60

This is a standalone 10 MBit ethernet controller with SPI interface.

For each device connected to a SPI bus, define a child node within
the SPI master node.

Required properties:
- compatible: Should be "microchip,enc28j60"
- reg: Specify the SPI chip select the ENC28J60 is wired to
- interrupts: Specify the interrupt index within the interrupt controller (referred
              to above in interrupt-parent) and interrupt type. The ENC28J60 natively
              generates falling edge interrupts, however, additional board logic
              might invert the signal.
- pinctrl-names: List of assigned state names, see pinctrl binding documentation.
- pinctrl-0: List of phandles to configure the GPIO pin used as interrupt line,
             see also generic and your platform specific pinctrl binding
             documentation.

Optional properties:
- spi-max-frequency: Maximum frequency of the SPI bus when accessing the ENC28J60.
  According to the ENC28J80 datasheet, the chip allows a maximum of 20 MHz, however,
  board designs may need to limit this value.

The MAC address will be determined using the optional properties
defined in ethernet.txt.

Example (for NXP i.MX28 with pin control stuff for GPIO irq):

        ssp2: ssp@80014000 {
                compatible = "fsl,imx28-spi";
                pinctrl-names = "default";
                pinctrl-0 = <&spi2_pins_b &spi2_sck_cfg>;

                enc28j60: ethernet@0 {
                        compatible = "microchip,enc28j60";
                        pinctrl-names = "default";
                        pinctrl-0 = <&enc28j60_pins>;
                        reg = <0>;
                        interrupt-parent = <&gpio3>;
                        interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
                        spi-max-frequency = <12000000>;
                };
        };

        pinctrl@80018000 {
                enc28j60_pins: enc28j60_pins@0 {
                        reg = <0>;
                        fsl,pinmux-ids = <
                                MX28_PAD_AUART0_RTS__GPIO_3_3    /* Interrupt */
                        >;
                        fsl,drive-strength = <MXS_DRIVE_4mA>;
                        fsl,voltage = <MXS_VOLTAGE_HIGH>;
                        fsl,pull-up = <MXS_PULL_DISABLE>;
                };
        };

我们发现配置好enc28j60模块,首先我们需要一个能用的spi设备节点。然后把我们的以太网设备挂在这个节点下,并且为它指定外部中断引脚。
于是我便照猫画虎,靠着代码能力把这一部分设备树的代码移植了过去。我知道这个设备树代码的意图是啥,但有的谜语是真的想不通:

  • 什么是ssp?我猜应该是spi吧
  • 为什么&ssp2enc28j60节点下都要塞一个pinctrl-names and pinctrl-0,这些是干嘛的
  • 为什么enc28j60pinctrlssp2的不一样?
  • interrupt-parentinterrupts是什么鬼?在全志平台上这俩参数应该咋写?
  • enc28j60_pins的意图是来一个GPIO的外部中断的引脚配置,这个在全志平台又得咋写?

当时怎么也无法思索出结果,随便瞎折腾了好几个星期,最后还是放弃了,又因为学业繁重,备战高考,一年多也没碰过这个荔枝派。高三后再试了一次,还是以失败告终。高一暑假买了荔枝派回来吃灰,高二暑假开始尝试却啥也不懂,高三暑假再次放弃,这一切成了我的一个心结,直到来到华子的一周年纪念日,我才琢磨出了这一切。

第二次移植过程

这几天突然发现可以在linux代码仓库里边用它的搜索功能全局搜索想要的代码,于是我就开始了递归学习。打开suniv-f1c100s.dtsi,对这里边的compatible等字符串一顿乱搜,终于发现了sunxi做bsp驱动移植的秘密,但是这些太复杂了,说也说不完,让我们直接开始移植。前置条件是首先应该先准备好一个已经调通的uboot、内核与根文件系统。

第零步:接线

  • VCC ---> 3V3
  • CS ---> PE7 (SPI1_CS)
  • SI ---> PE8 (SPI1_MOSI)
  • SCK ---> PE9 (SPI1_CLK)
  • SO ---> PE10 (SPI1_MISO)
  • INT ---> PE11
  • GND ---> GND

需要注意的是,全志f1c系列芯片只有GPIOD GPIOE GPIOF有外部中断,所以在选取引脚时要特别注意,而且要关注这个引脚是否被开发板引出了。

第一步:配置Menuconfig

终端输入make menuconfig,使能以下config:

  • Device Drivers ---> Network device support ---> Ethernet driver support ---> Microchip devices ---> ENC28J60 support
  • Device Drivers ---> SPI support

ENC28J60可以编译为模块,也可以直接编译进内核。保存.config

第二步:修改设备树

接着让我们修改设备树。根据驱动开发者留给我们的文档,我们初步的设备树需要的改动如下:

suniv-f1c100s.dtsi:在pinctrl下加入spi和enc28j60的中断引脚定义

pio: pinctrl@1c20800 {
        compatible = "allwinner,suniv-f1c100s-pinctrl";
        reg = <0x01c20800 0x400>;
        interrupts = <38>, <39>, <40>;
        clocks = <&ccu CLK_BUS_PIO>, <&osc24M>, <&osc32k>;
        clock-names = "apb", "hosc", "losc";
        gpio-controller;
        interrupt-controller;
        #interrupt-cells = <3>;
        #gpio-cells = <3>;

        uart0_pe_pins: uart0-pe-pins {
                pins = "PE0", "PE1";
                function = "uart0";
        };

        uart1_pa_pins: uart1-pa-pins {
                pins = "PA2", "PA3";
                function = "uart1";
        };
        
        mmc0_pins: mmc0-pins {
                pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5";
                function = "mmc0";
        };

        spi0_pins: spi0-pins{
                        pins = "PC0", "PC1", "PC2", "PC3";
                        function = "spi0";
        };
        
        spi1_pins: spi1-pins{
                pins="PE9","PE7","PE10","PE8";
                function = "spi1";
        };

        enc28j60_pins: enc28j60_pins{
                pins="PE11";
                function = "irq";
        };

};

改变引脚基本就是对着手册和文档照猫画虎的事情,这个引脚的function应该如何选取呢?让我们打开内核代码中的pinctrl-suniv-f1c100s.c

SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 7),
        SUNXI_FUNCTION(0x0, "gpio_in"),
        SUNXI_FUNCTION(0x1, "gpio_out"),
        SUNXI_FUNCTION(0x2, "csi"),  /* D4 */
        SUNXI_FUNCTION(0x3, "uart2"),  /* TX */
        SUNXI_FUNCTION(0x4, "spi1"),  /* CS */
        SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 7)),
SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 8),
        SUNXI_FUNCTION(0x0, "gpio_in"),
        SUNXI_FUNCTION(0x1, "gpio_out"),
        SUNXI_FUNCTION(0x2, "csi"),  /* D5 */
        SUNXI_FUNCTION(0x3, "uart2"),  /* RX */
        SUNXI_FUNCTION(0x4, "spi1"),  /* MOSI */
        SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 8)),
SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 9),
        SUNXI_FUNCTION(0x0, "gpio_in"),
        SUNXI_FUNCTION(0x1, "gpio_out"),
        SUNXI_FUNCTION(0x2, "csi"),  /* D6 */
        SUNXI_FUNCTION(0x3, "uart2"),  /* RTS */
        SUNXI_FUNCTION(0x4, "spi1"),  /* CLK */
        SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 9)),
SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 10),
        SUNXI_FUNCTION(0x0, "gpio_in"),
        SUNXI_FUNCTION(0x1, "gpio_out"),
        SUNXI_FUNCTION(0x2, "csi"),  /* D7 */
        SUNXI_FUNCTION(0x3, "uart2"),  /* CTS */
        SUNXI_FUNCTION(0x4, "spi1"),  /* MISO */
        SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 10)),
SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 11),
        SUNXI_FUNCTION(0x0, "gpio_in"),
        SUNXI_FUNCTION(0x1, "gpio_out"),
        SUNXI_FUNCTION(0x2, "clk0"),  /* OUT */
        SUNXI_FUNCTION(0x3, "i2c0"),  /* SCK */
        SUNXI_FUNCTION(0x4, "ir"),  /* RX */
        SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 11)),
SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 12),
        SUNXI_FUNCTION(0x0, "gpio_in"),
        SUNXI_FUNCTION(0x1, "gpio_out"),
        SUNXI_FUNCTION(0x2, "i2s"),  /* MCLK */
        SUNXI_FUNCTION(0x3, "i2c0"),  /* SDA */
        SUNXI_FUNCTION(0x4, "pwm0"),  /* PWM0 */
        SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 12)),

所以,我们现在知道了function这个字符串的值域。那么对于外部中断引脚,应该怎么填function呢?像spi的功能,上面的代码告诉我们,直接填spi1,那么SUNXI_FUNCTION_IRQ_BANK又是个啥?继续看它的头文件pinctrl-sunxi.h

#define SUNXI_FUNCTION_IRQ(_val, _irq)    \
{       \
        .name = "irq",     \
        .muxval = _val,     \
        .irqnum = _irq,     \
}

#define SUNXI_FUNCTION_IRQ_BANK(_val, _bank, _irq)  \
{       \
        .name = "irq",     \
        .muxval = _val,     \
        .irqbank = _bank,    \
        .irqnum = _irq,     \
}

所以我猜对于中断引脚,我们应该把function设置为irq,这样他就会自动帮我们初始化这个引脚的复用功能(全志可能没这个说法)。

接下来让我们修改suniv-f1c100s-licheepi-nano.dts

&spi1{
        status = "okay";
        pinctrl-names = "default";
        pinctrl-0 = <&spi1_pins>;
        enc28j60: ethernet@0 {
                compatible = "microchip,enc28j60";
                pinctrl-names = "default";
                pinctrl-0 = <&enc28j60_pins>;
                reg = <0>;
                interrupt-parent = <&pio>;
                interrupts = <4 11 IRQ_TYPE_EDGE_FALLING>;
                spi-max-frequency = <12000000>;
        };
};

这里基本就是对着全志设备树,照着它文档抄了。现在我们也可以解答一下我高中时代的疑问。这个interrupt-parent是啥呢?interrupt-parent属性用于指定中断路由到的控制器,并包含引用中断控制器节点的单个 phandle。 该属性是继承的,因此可以在中断客户端节点或其任何父节点中指定。 “中断”属性中列出的中断始终引用节点的中断父级。翻译成人话就是它用的中断要对应在哪个中断控制器,至少在本芯片内是一种些许无用的抽象,因为f1c100s就一个简单的中断控制器INTC,但是对于更复杂的片上中断控制器则是特别好的设计。并且这样做也方便了我们配置外部引脚的中断。

dtsi中的#interrupt-cells = <3>告诉我们,interrupts这个属性应该有3个元素。通过翻其它全志的设备树,我们也知道了应该要如何配置interrupts这个属性。那就是先放引脚Port的编号,那就是A放0,B放1,C放2,D放3,E放4。。。第二个参数是引脚编号,我们用的PE11,直接填。第三个参数,直接抄模块的文档。

现在已经大功告成,让我们拷贝好设备树和内核镜像,准备启动!

第三步:启动内核

启动内核后,我们dmesg,发现并没有任何关于enc28j60的影子,甚至连关于spi的影子也没有,这是怎么回事呢?再折腾了好几天后,我发现这是因为spi根本就没有被加载。我们芯片设备树对spi的配置中是这样配置compatible属性的:"allwinner,suniv-spi", "allwinner,sun8i-h3-spi",事实上,linux内核中根本没有代码去适配allwinner,suniv-spi,所以内核会去找allwinner,sun8i-h3-spi。而在drivers/spi/spi-sun6i.c中:

static const struct of_device_id sun6i_spi_match[] = {
        { .compatible = "allwinner,sun6i-a31-spi", .data = &sun6i_a31_spi_cfg },
        { .compatible = "allwinner,sun8i-h3-spi",  .data = &sun8i_h3_spi_cfg },
        {
                .compatible = "allwinner,sun50i-r329-spi",
                .data = &sun50i_r329_spi_cfg

所以我们来到内核配置Device Drivers ---> SPI support,发现allwinner的spi就俩,一个Allwinner A31 SPI controller,一个Allwinner A31 SPI controller。查看Kconfig后发现,后者对应的symbol是SPI_SUN6I。故我们应该将后者设置成Y。而我之前是将前者设置成Y,后者设置成了N。

再次启动内核,一切都顺利加载

...

[    1.098960] enc28j60 spi0.0: Ethernet driver 1.02 probed
[    1.104485] enc28j60 spi0.0: Ethernet driver 1.02 loaded

....

[    4.343318] enc28j60 spi0.0 eth0: link down
Starting ntpd: [    4.354284] enc28j60 spi0.0 eth0: normal mode
[    4.359272] enc28j60 spi0.0 eth0: multicast mode
OK
[    5.292753] enc28j60 spi0.0 eth0: link up - Half duplex

...

移植之后

我的rootfs没有dhcp的软件,没法自动拿ip地址。所以我们需要配置一下buildroot。
进入目录,make menuconfig,进入
Target packages ---> Networking applications,勾上以下内容:

  • dhcp
    • dhcp relay
    • dhcp client
  • dhcpd
  • dhcpdump
  • dnsmasq
    • tftp support
    • dhcp support
  • ethtool
    • enable pretty printing
  • hostapd
  • httping
  • iptables
  • iw
  • lrzsz
  • ntp
    • ntpd
    • ntptime
  • openssh
    • client
    • server
    • key utilities
  • tcpdump
  • wget

再次重新上电,进入系统,输入ifconfig,非常漂亮地拿到了ip地址,不过由于校园网需要认证,目前没法访问内网和外网。

# ifconfig
eth0      Link encap:Ethernet  HWaddr 96:54:CC:C7:65:B4
          inet addr:59.66.*.*  Bcast:59.66.*.*  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:953 errors:0 dropped:0 overruns:0 frame:0
          TX packets:44 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:70235 (68.5 KiB)  TX bytes:3760 (3.6 KiB)
          Interrupt:67

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:128 errors:0 dropped:0 overruns:0 frame:0
          TX packets:128 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:9472 (9.2 KiB)  TX bytes:9472 (9.2 KiB)

困扰了两年的心结,从高二困扰到大一,终于解决了!

标签:FUNCTION,f1c100s,pinctrl,网卡,spi,pins,enc28j60,SUNXI
From: https://www.cnblogs.com/maomaotou-thu/p/17645648.html

相关文章

  • Minecraft-Spigot插件开发入门
    Plugin.yml文件plugin.yml是一个包含你的插件信息的文件。没有这个文件,你的插件将无法工作。它由一组属性组成,每个属性都定义在一个新的行上,没有缩进。所有的属性都是区分大小写的。Plugin文件必要的三个属性main这个属性指向你的插件的类,它扩展了JavaPlugin。这必须包含......
  • DP2515完全兼容MCP2515支持SPI通信的can V2.0B控制器新能源汽车通信应用
    DP2515完全兼容MCP2515支持SPI通信的canV2.0B控制器新能源汽车通信应用说明DP2515是一款独立控制器局域网络(ControllerAreaNetwork,CAN)协议控制器,完全支持CANV2.0B技术规范。该器件能发送和接收标准和扩展数据顿以及远程帧。MCP2515自带的两个验收屏蔽寄存器和六个验收......
  • SPI驱动0.96寸OLED单色屏刷新率测试以及代码优化改进,方法适用于SPI驱动其他设备
    目前嵌入式当中OLED常用驱屏方式有两种:SPI或IIC。以速度来讲,SPI速度相较于IIC会快上一些,硬件IIC相较于模拟IIC速度又会快上一些。此外还有模拟SPI的,但该种用法我遇到较少,本文就硬件SPI驱动OLED屏幕做一个简单的刷新率测试。 测试硬件平台:CH32V307VCT6+杜邦线连接0.96寸SPI接口O......
  • X710网卡LACP模式下ifdown网卡后交换机侧依然处于UP状态,导致网络通信异常
    以下配置属于临时配置,重启后失效,具体建议在bios或者固件中解决。主要包含两个配置:1、使用ifdown命令关闭网卡无法使linkdown,交换机侧依然认为端口UP进行流量转发,无法正常通信2、在某些环境中,LACP可能无法正常工作,这些环境要求将包含LCAP信息的LLDP帧转发到网络堆栈。#查看网卡......
  • 如何找到docker容器中的网卡外联的veth pair的另一张网卡
    1、概述在Docker容器中,每个容器都有一个或多个网络接口(网卡),用于连接容器内部与宿主机或其他容器进行通信。这些网络接口中的一些可能是vethpair,也就是虚拟以太网对,它们以成对的方式存在,一侧连接到容器内部,另一侧连接到宿主机的网络命名空间。vethpair的一侧称为......
  • centos 7 2009 Linux系统之多网卡Team链路聚合配置
    Linux系统之Team链路聚合配置操作步骤一、Team链路聚合介绍1.Teaming技术简介2.网卡的bonding和Teaming技术3.Team常用工作模式二、实践环境规划三、添加物理网卡1.新增物理网卡2.查看网卡信息四、Team链路聚合配置1.查看team的......
  • java Sping aop 以及Spring aop 的应用事务管理
    1.回顾线程死锁概念和如何避免死锁的发生:线程的通信waitnotify()notify():---Object类线程的状态:NEW--->start()--->就绪状态---CPU时间片---运行状态RUNNABLE]--->sleep()--->TIMED_WAITING--->wait()---->WAITING----sysn---Blocked---->终止状态[T]线程池:常见......
  • SPI与SDIO两种操作方式读写SD卡速度测试
    SPI与SDIO两种操作方式读写SD卡速度测试 目前嵌入式应用中,若要用到SD卡,一般有两种操作方式可选:一种是SPI操作SD卡的方式;一种是SDIO操作SD卡的方式。一般是SPI操作SD卡方式使用较多,因为一般正常的MCU都带有SPI接口,但SDIO接口一般存在于大容量MCU中,中小容量的MCU一般不带有SDI......
  • 关于VSAN集群主机报"检测到物理网卡错误率较高"告警处理过程
       VSAN集群主机报"检测到物理网卡错误率较高"告警:    1、使用命令检测物理网卡的丢包情况:esxclinetworknicstatsget-nvmnicX。也可以通过主机->监控->VSAN->性能->物理适配器->选择物理适配器观察丢包率和吞吐量情况。     例如:NICstatisticsforvmnic0 ......
  • kvm虚拟化增加网卡
    第一:先进入到kvm机器,执行virt-manager(如果没有的话,就先执行yumgroupinstall'xwindowssystem'-y,等待安装完成之后再执行yuminstallvirt-manager-y,等待安装完成) 第二步:执行完第一步,会弹出图形化界面管理,选择关闭你要配置双网卡的堡垒机,选中之后点击右键(如图) 第三步:再......