Using an enc28j60 on the zero

@RadxaYuntian
@linuxlion

Seeing this image:

We see on the left right side the last packet of the Raspberry in the initialization sequence, asking for the revision ID. The first 8 clock bytes are the OP code asking for Rev ID (obviously), the next 8 clock bytes are for the enc28j60 to reply on the MISO channel (you can see the reply).

On the left side is the Radxa, which interrupts the CS between the two “clock bytes”.
Because the Radxa interrupts CS after the OP code, enc28j60 will not reply.
Here is a screenshot of the datasheet how a communication like this MUST happen:
54

Thus, the CS pin of the SPI controller of the Radxa Zero is unuseable (at least for this use-case).

It is necessary to implement the CS pin via GPIO.
The SPI linux kernel implementation looks like it is possible to do this via Device Tree (cs-gpios) like the example here:

Can we have an example, how to change the CS Pin to be done via cs-gpios instead of using the CS pin of the Radxa SPI controller?

Thanks for your investigation. In that case you can try using spi-gpio binding and define SPI entirely with GPIO. You can use this dtbo as a template and define GPIO in the same style specified in that file.

@kwisatz Hi , firstly I think you have done some excellent research in this thread , and it has inspired and equipped to implement the overlay for the Waveshare CAN hat onto the Radxa Zero . I had done something similiar for RockPi S , but AMlogic resources and examples are in short supply on the internet.

The CAN hat employs Microchip MCP2515 , but its interface is not unlike the enc 28j60, meaning it has a SPI interface and a GPIO external IRQ interface. So with your help I have compiled the following dts source, and it works perfectly . Don’t know why I don’t have any SPI issues , could be because the packets are smaller and less frequent than Ethernet.

Blockquote /dts-v1/;
/plugin/;

/ {
compatible = “radxa,zero”, “amlogic,g12a”;

    fragment@0 
    {
	target-path = "/";

	__overlay__ 
            {
		can_mcp2515_osc: can_mcp2515_oscs 
                    {
			compatible = "fixed-clock";
			clock-frequency = <12000000>;
			#clock-cells = <0>;
		};
	};
};


    fragment@1 
    {
	target = <&gpio>;
	__overlay__ 
            {
		
                    mcp2515_int_pin: mcp2515_int_pin@0 
                    {
                            amlogic,pins = <&gpio 48>;	/* or is it <&gpio 85> ? */
                            amlogic,function = "irq"; /* in - whether "out" (1) or "in" (0) */
                            // amlogic,function = <0>; /* in - whether "out" (1) or "in" (0) */
                            amlogic,pull = <0>;
                    };
            };
    };




fragment@3 
{
    target = <&spicc1>; 
    __overlay__ 
    {
        pinctrl-0 = <&spicc1_pins &spicc1_ss0_pins>;
        pinctrl-names = "default";
        #address-cells = <1>;
        #size-cells = <0>;
        status = "okay";
        mcp2515: spidev@0 
        {
                compatible = "microchip,mcp2515";
                reg = <0>;
                                
               
               interrupt-parent = <&gpio_intc>;
               interrupts = <60 2>;
               
                                
                spi-max-frequency = <12000000>;
                clocks = <&can_mcp2515_osc>;
                pinctrl-0 = <&mcp2515_int_pin>;
        };
    };
};

};

As you can see I am using SPI1 (SPIB per gpio pinout diagram) and i am using GPIOC_7 as the GPIO IRQ input. Gpio number 48 represents the GPIOC_7 and the IRQ number 60 the IRQ mapping for GPIOC_7. The latter I got from your link table below :

  • 223:100 undefined (no interrupt)
  • 99:97 3 pins on bank GPIOE
  • 96:77 20 pins on bank GPIOX
  • 76:61 16 pins on bank GPIOA
  • 60:53 8 pins on bank GPIOC
  • 52:37 16 pins on bank BOOT
  • 36:28 9 pins on bank GPIOH
  • 27:12 16 pins on bank GPIOZ
  • 11:0 12 pins in the AO domain

So thank my man , you did some great work here . Have you managed success regarding your ethernet interface ?

Kind Regards

Anton

@Antonr, I’m happy that the hours that we’ve spent on this have at least benefited someone else. As for us, we haven’t been able to make it work. I’ve not been able to redefine the entire GPIO as @RadxaYuntian suggested, and given how broken the CS is on the default mapping, the enc28j60 just won’t react to whatever the radxa-zero is sending.

I’d be interested in seeing your CS signal if you ever get the chance of connecting a logic analyzer.

1 Like

@kwisatz I will do that for you . It turns out I do have a logic analyzer , so ill put it to hopefully good use.

We just had a new engineer join our team, and this is his first assignment. I’ll let you know if we made more progress on this with GPIO-SPI.

Thanks for your investigation.
We purchased the enc28j60 module and tested it on radxa zero (based on amlogic). The problem you mentioned does exist. At the same time, we also tested on our other boards (based on rockchip) and found that it was feasible. The timing of the cs pin is the same as you described.

@kwisatz @shawaj @iplanux @feng Doing a mass ping here since we have implemented a gpio-spi based overlay and have tested it with real hardware.
You can find the source code for overlay here.
However, when testing against a iperf3 server, we only recorded around 500kpbs speed. Logic analyzer shows inconsistent clock signal, which is an unfortunately limitation with this software SPI imitation.

Edit: A SPI based overlay has been uploaded with combined bandwidth of ~3.5Mbps. @iplanux’s idea of using cs-gpios proves to be a better solution.

Sorry, may I just ask how to compile this overlay? I failed by running just simple dtc:

$ dtc -I dts -O dtb -o meson-g12a-spi-b-enc28j60.dtbo meson-g12a-spi-b-enc28j60.dts
Error: meson-g12a-spi-b-enc28j60.dts:4.1-9 syntax error
FATAL ERROR: Unable to parse input tree

Thank you.

This is currently intended to be compiled along with the kernel, so the kbuild can resolve include directives first. Your error message is on line 4 which is the unsupported #include line for dtc. So for right now you should delete those include lines, and replace the constants (dtc will error out on them) with the value you can find from those included files. Those files are in the Linux kernel.

We will have a out-of-tree build guide later.

Up and working! Thank you very much!

1 Like

@wanthalf perhaps did you forget the -@ parameter to dtc ? Apparently it’s mandatory to compile overlays that lay above another one ; I think it’s related to symbol resolution. I don’t know if it’s involved in includes though.

I have not found any documentation to that option, so I have no idea what it does - but I think (not sure) that I tried it as well, without any effect. But replacing the (three?) constants manually worked well.

I think it would be nice to include both this overlay and the enc28j60.ko kernel module in the pre-built radxa images. It is an easy and cheap way to add a basic wired ethernet interface to the Zero - and probably sufficient for many IoT projects.

I plan to use the third UART_EE_C, so I modified the overlay for SPI-A and it seems to work. I use GPIOH_4 for interrupt – that is number 32 (thanks to @Antonr for the numbers!). I hope it won’t collide with the UART_EE_C just because GPIOH_4 is also assigned as UART_EE_C_RTS…?

UPDATE: Well, it does… :frowning: So, changing to GPIOC_7, number 60.

Just in case someone were interested:

/dts-v1/;
/plugin/;

/ {
	compatible = "radxa,zero", "amlogic,g12a";

	fragment@0 {
		target = <&spicc0>;
		__overlay__ {
			pinctrl-0 = <&spicc0_x_pins>;
			pinctrl-names = "default";
			#address-cells = <1>;
			#size-cells = <0>;
			cs-gpios = <&gpio 75 1>;
			status = "okay";

			ethernet: enc28j60@0 {
				compatible = "microchip,enc28j60";
				reg = <0>;
				spi-max-frequency = <20000000>;

				pinctrl-names = "default";
				pinctrl-0 = <&enc28j60_int_pins>;

				interrupt-parent = <&gpio_intc>;
				interrupts = <60 2>;
			};
		};
	};

	fragment@1 {
		target = <&periphs_pinctrl>;
		__overlay__ {
			enc28j60_int_pins: enc28j60-int-pins@0 {
				mux {
					groups = "GPIOC_7";
					function = "gpio_periphs";
					bias-pull-up;
					drive-strength-microamp = <4000>;
				};
    		};
		};
	};
};

Hi Yuntian,
I have tried to compile my files using dtc
$ dtc -I dts -O dtb -o meson-g12a-spi-b-enc28j60.dtbo meson-g12a-spi-b-enc28j60.dts
But no matter what, it never works and always gives the same error:
Error: meson-g12a-spi-b-enc28j60.dts:xxx syntax error
FATAL ERROR: Unable to parse input tree.
I have already deleted those #includes, but the error came from the next line continue.

Do you have the out-of-tree bulid yet? Or can you please tell me how to compile the .dts file correctly?

Thanks!

Hi @kwisatz @feng,
How do you compile the .dts files into Radxa? I have tried with dtc to compile and still not working.
It always gives same error:
Error: meson-g12a-spi-b-enc28j60.dts: xxx syntax error
FATAL ERROR: Unable to parse input tree.
I have already tried to delete #include, but it changes nothing, the error came immediately from the next line.
I don’t have any clue how to compile it now. Could you please give me some guides?

For @Enoch_Lee, here is my (manually) preprocessed source for meson-g12a-spi-b-enc28j60.dts. In this form it can already be directly compiled.

/dts-v1/;
/plugin/;

/ {
	compatible = "radxa,zero", "amlogic,g12a";

	fragment@0 {
		target = <&spicc1>;
		__overlay__ {
			pinctrl-0 = <&spicc1_pins>;
			pinctrl-names = "default";
			#address-cells = <1>;
			#size-cells = <0>;
			cs-gpios = <&gpio 22 1>;
			status = "okay";

			ethernet: enc28j60@0 {
				compatible = "microchip,enc28j60";
				reg = <0>;
				spi-max-frequency = <20000000>;

				pinctrl-names = "default";
				pinctrl-0 = <&enc28j60_int_pins>;

				interrupt-parent = <&gpio_intc>;
				interrupts = <85 2>;
			};
		};
	};

	fragment@1 {
		target = <&periphs_pinctrl>;
		__overlay__ {
			enc28j60_int_pins: enc28j60-int-pins@0 {
				mux {
					groups = "GPIOX_8";
					function = "gpio_periphs";
					bias-pull-up;
					drive-strength-microamp = <4000>;
				};
    		};
		};
	};
};

The source for meson-g12a-spi-a-enc28j60.dts has been posted above in this post. But I thought the precompiled overlays were now part of the Radxa distribution, aren’t they?

Hi @wanthalf, I have already compiled both .dts Files from you, and copied them into the Overlay Ordner. But I can still not find the Device(ENC28J60) in the lsmod or dmesg and when I connect my enc28j60 to Radxa Zero via SPIA or SPIB pins, I still can’t get any data from the Ethernet. The uEnv.txt data from me is like this:

verbosity=7
console=ttyAML0,115200
overlay_prefix=meson
rootfstype=ext4
fdtfile=amlogic/meson-g12a-radxa-zero.dtb
overlays=meson-g12a-spi-b-enc28j60
param_spidev_spi_bus=1
param_spidev_max_freq=10000000
rootuuid=dc5be088-dcd0-4a74-96e5-7879177587da
initrdsize=0xc16519
kernelversion=5.10.69-999-amlogic
initrdimg=initrd.img-5.10.69-999-amlogic
kernelimg=vmlinuz-5.10.69-999-amlogic

and the Pin assignment is like:

MOSI -> GPIOH4 (SPI_B_MOSI)
MISO -> GPIOH5 (SPI_B_MISO)
SCK -> GPIOH7 (SPI_B_SLK)
CS -> GPIOH6 (SPI_B_SS0)
INT -> GPIOX8 (PWM_C)

I would like to ask:

  • How you connected the PINs to Radxa Zero to get it to work?

  • Did I miss any steps?(Compile -> copy into Overlay -> add new element in uEnv.txt -> Reboot)

  • What did you write in your uEnv.txt?

I hope I can get your reply, it will help me a lot.~( ̄▽ ̄)~
Thank you!!

From the look of it you are using a custom kernel. Please check if you have included enc28j60 driver in your build:

grep -i enc28j60 /boot/config*

Hi @RadxaYuntian ,
Thanks for your reply. The driver from enc28j69 is maybe not in the build, the Terminal shows “CONFIG_ENC28J60 is not set”. I’m using Debian Buster from your official Website. What should I do next?