Edit: it works after modifying enc28j60.c spi_read_buf function. See last post.
---
Hi All,
The iMX23 series does not have ethernet on chip. The Olinuxino Maxi includes USB ethernet but this uses a QFN chip which is not so easy to solder by hand. I am trying to get the ENC28J60 UEXT module (also from Olimex) running on the iMX23. This took me quite some time already because most options to get this thing up and running are not or only partially documented.
At this moment the hardware seems to be recognized correctly. The ENC28J60 is initialized, I get a MAC address, the kernel does not complain about interrupts anymore but for now I can only send data but not receive any data. ifconfig tells me something like:
RX packets:0 errors:34 dropped:0 overruns:0 frame:0
TX packets:38 errors:0 dropped:0 overruns:0 carrier:0
I see that there is a topic about the ENC28J60 on the A13 board. They experienced similar problems and they made quite some changes to the ENC28J60 driver sources. I think this is also neccesary for the iMX23 (probably because it does not support a full duplex SPI connection) but their sources for the A13 are specific for the Allwinner boards.
I guess more people would like to get a simple ethernet solution for this board so I will post some of the relevant configuration files later today. Maybe one of you out there can point me in the right direction about the failing RX packets of the module?
Regards,
Chax
Quote from: Chax on June 24, 2013, 07:38:48 PM
...
I guess more people would like to get a simple ethernet solution for this board so I will post some of the relevant configuration files later today. Maybe one of you out there can point me in the right direction about the failing RX packets of the module?
Hi Chax,
The Freescale i.MX23 Evaluation Kit has ethernet solution based on ENC28J60. Since that solution work for that board (kernel 2.6), there is no reason to fail on imx23-olinuxino boards.
Linux driver for ENC28J80 is not depend on board and SoC (imx23, A13 etc) so I don't see need to modify driver. Focus should be on configuration files for particular board and kernel version.
One of the reasons I am trying this is because the EVK has the Microchip ethernet device on the board but this is all kernel 2.6 based. Since SPI is working for kernel 3.7.something+ and the ENC28J60 driver is part of that kernel I try to get it all up and running on (let me check...) kernel 3.9.3 but no luck so far.
I guess it might have something to do with the DTS file for the Olinuxino in combination with SPI. I added the following to the imx23-olinuxino.dts:
ssp1: ssp@80034000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx23-spi";
pinctrl-names = "default";
pinctrl-0 = <&spi2_pins_a>;
status = "okay";
eth: enc28j60@0 {
// #address-cells = <1>;
// #size-cells = <1>;
compatible = "microchip,enc28j60";
spi-max-frequency = <1000000>;
reg = <0>;
interrupt-parent = <&gpio0>;
interrupts = <24 0x01>;
};
// spidev: spidev@0 {
// compatible = "spidev";
// spi-max-frequency = <1000000>;
// reg = <0>;
// };
};
The part that is commented out can be enabled (when disabling the eth: enc28j60@0 { ... } part. This way you can do some spi testing when you modify spidev_test.c.
Especially the 'interrupt-parent = <&gpio0>' and 'interrupts = <24 0x01>;' took me quite a while to figure out and I'm not sure IF the interrupts are actually working or handled correctly. I can see the number of interrupts increasing in /proc/interrupts but it also does this while I unplug the interrupt line from the development board. It is connected on the mx23 to physical pin 34 (GPMI_WPN) which Olimex has labeled Pin29/SoftSCL. Although this is GPIO23, you give it number 24 here because the number is one-based (aargh).
CPU0
16: 222695 - MXS Timer Tick
17: 6 - mxs-mmc
18: 17875 - mxs-dma
44: 29972285 gpio-mxs enc28j60
118: 0 - mxs-spi
119: 905 - mxs-dma
120: 0 - RTC alarm
124: 0 - 8006c000.serial
127: 12811 - uart-pl011
128: 4533 - ci13xxx_imx
Err: 0
I am aware that the linux driver is (or should be) independant of the board or soc that is used but in the other topic they made quite some modifications and they seemed to have the same problem: failing RX https://www.olimex.com/forum/index.php?topic=462.0. This had something to do with half duplex spi? The same probably applies for the mx23?
At this moment I don't know why the ENC28J60 is not working. When I use a scope everything about the hardware seems to be in order. Chip Select is doing ok, MISO/MOSI is working (I can query eth0 and it has a valid MAC address), cable unplug and plug events are registered by the kernel. The interrupts seem to do something because INT# on the ENC29J60 goes low and back high again. When I choose the wrong GPIO channel for the mx23 in the DTS file, it only goes low once and never goes high anymore.
The mx23 is very busy however doing... something with SPI:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
27 root 20 0 0 0 0 R 51.5 0.0 2:09.29 spi32766
15 root 20 0 0 0 0 D 36.5 0.0 1:16.91 kworker/0:1
Maybe you have a suggestion where to look next?
Yeah! Result! After messing with 'ethtool -s eth0 msglvl 0x7ff' I got messages about spi_read_buf(...) failing. This is due to the fact that SPI is half duplex in the mx23.
The guys from the A13 also changed their spi_read_buf(...) function to support half duplex. I simply removed the old spi_read_buf function in the enc28j60.c file and replaced it with their code:
/*
* SPI read buffer
* wait for the SPI transfer and copy received data to destination
*/
static int
spi_read_buf(struct enc28j60_net *priv, int len, u8 *data)
{
u8 *rx_buf = priv->spi_transfer_buf + 4;
u8 *tx_buf = priv->spi_transfer_buf;
struct spi_transfer t = {
.tx_buf = tx_buf,
.rx_buf = NULL,
.len = SPI_OPLEN,
.cs_change = 0,
};
struct spi_transfer r = {
.tx_buf = NULL,
.rx_buf = rx_buf,
.len = len,
.cs_change = 1,
};
struct spi_message msg;
int ret;
tx_buf[0] = ENC28J60_READ_BUF_MEM;
tx_buf[1] = tx_buf[2] = tx_buf[3] = 0; /* don't care */
spi_message_init(&msg);
spi_message_add_tail(&t, &msg);
spi_message_add_tail(&r, &msg);
ret = spi_sync(priv->spi, &msg);
if (ret == 0) {
memcpy(data, rx_buf, len);
ret = msg.status;
}
if (ret && netif_msg_drv(priv))
printk(KERN_DEBUG DRV_NAME ": %s() failed: ret = %d\n",
__func__, ret);
return ret;
}
Compiled it and now it works! 8)
Iperf results (half duplex @1MHz SPI as client):
Client connecting to 192.168.200.150, TCP port 5001
TCP window size: 21.0 KByte (default)
------------------------------------------------------------
[ 3] local 192.168.200.216 port 48029 connected with 192.168.200.150 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-14.4 sec 768 KBytes 436 Kbits/sec
Iperf results (half duplex @1MHz SPI as server):
Server listening on TCP port 5001
TCP window size: 85.3 KByte (default)
------------------------------------------------------------
[ 4] local 192.168.200.216 port 5001 connected with 192.168.200.150 port 56999
[ ID] Interval Transfer Bandwidth
[ 4] 0.0-14.2 sec 768 KBytes 443 Kbits/sec
No more errors:
eth0 Link encap:Ethernet HWaddr 86:e6:3f:0a:b4:45
inet addr:192.168.200.216 Bcast:192.168.200.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1836 errors:0 dropped:0 overruns:0 frame:0
TX packets:2330 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:978034 (955.1 KiB) TX bytes:2520956 (2.4 MiB)
Interrupt:44
wget throughput of 10 MB file (SPI @1 MHz.):
100%[======================================>] 10,485,760 46.2K/s in 3m 39s
2013-06-24 23:36:39 (46.7 KB/s) - `10mb.bin' saved [10485760/10485760]
wget throughput of 10 MB file (SPI @4 MHz.):
100%[======================================>] 10,485,760 78.3K/s in 2m 12s
2013-06-24 23:58:36 (77.7 KB/s) - `10mb.bin.1' saved [10485760/10485760]
The imx23 is still very busy though with handling SPI (even if there are no interrupts). 'top' output:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
27 root 20 0 0 0 0 S 46.5 0.0 4:59.18 spi32766
2652 root 20 0 0 0 0 D 39.6 0.0 0:55.20 kworker/0:0
15 root 20 0 0 0 0 S 10.2 0.0 2:05.58 kworker/0:1
That is for later but at least it works!
Hi,
Just a precision form my experience:
GPIO are not counted form 1 but from 0. The thing is you specified 0x1 as parameter wich means the interruption is done form low to high. In this case you want to put 0x2 wich means high to low.
So this is interrupts = <23 0x02>;
By choosing 24 (corresponding to spi_clk) you get an interruption at every clock tick, so processor constantly handles interruptions which explains high CPU usage that you experience.
see:
https://community.freescale.com/message/341723#341723
Pierre
Also for me the function you said above did not work, I had to use this function:
/*
* SPI read buffer
* wait for the SPI transfer and copy received data to destination
* New version correcting bug for half duplex mode
*/
static int
spi_read_buf(struct enc28j60_net *priv, int len, u8 *data)
{
static int nbmsg=0;
nbmsg++;
// printk("Receive message %d\n",nbmsg);
u8 *rx_buf = priv->spi_transfer_buf + 4;
u8 *tx2_buf=priv->spi_transfer_buf+2;
u8 *tx_buf = priv->spi_transfer_buf;
struct spi_transfer t = {
.tx_buf = tx_buf,
.rx_buf = NULL,
.len = SPI_OPLEN,
.cs_change = 0,
};
struct spi_transfer r = {
.tx_buf = NULL,
.rx_buf = rx_buf,
.len = len,
.cs_change = 1,
};
struct spi_transfer t2 = {
.tx_buf = tx2_buf,
.rx_buf = NULL,
.len = SPI_OPLEN,
.cs_change = 0,
};
struct spi_message msg;
int ret;
tx_buf[0] = ENC28J60_READ_BUF_MEM;
tx_buf[1] = tx_buf[2] = tx_buf[3] = 0; /* don't care */
tx2_buf[0]= ENC28J60_MSG_DEFAULT;
spi_message_init(&msg);
spi_message_add_tail(&t, &msg);
spi_message_add_tail(&r, &msg);
spi_message_add_tail(&t2, &msg);
ret = spi_sync(priv->spi, &msg);
if (ret == 0) {
memcpy(data, rx_buf, len);
ret = msg.status;
}
if (ret && netif_msg_drv(priv))
{
printk(KERN_DEBUG DRV_NAME ": %s() failed: ret = %d\n",
__func__, ret);
}
return ret;
}