이지보드-X5 에 디바이스 드라이버 포팅하기 - Ethernet
X5 보드에 2.6 커널 기반으로 이더넷 드라이버를 포팅하는 과정을 담고 있다. 이지보드-X5 에 커널 포팅하기 문서를 통해서 부팅 후, 로그인까지는 성공했다.
이제 X5 보드에 붙어있는 여러가지 디바이스들을 사용해보자.
그러기 위해서는 인식시켜야하는 작업이 필요한데, 이것은 디바이스 드라이버 포팅이라고 한다.
CS8900 에 대해서
X5 보드에 들어있는 이더넷칩은 CS8900 이라는 모델인데, 옛날 ISA 나 PCI 에서나 쓰던 종류이다. 다행히(?) 리눅스커널에서는 지원을 하고 있다.
포팅하기에 앞서 CS8900 에 대해서 알아보자.
CS8900 은 Cirrus Logic 사의 IEEE 802.3 Ethernet 컨트롤러이다. CS8900A 는 100핀 TGFP 패키지 형태이며, 내부에 ISAbus 인터페이스(24mA Drive Cap), 802.3 MAC 엔진, 4KB Buffer Memory, 구성용 EEPROM 인터페이스, 10BASE-T Analog Front End 인터페이스를 가지고 있다. 아래 그림은 CS8900A 내부 기능 블럭이다.
CS8900A 제어 신호
| SA[19:0] | 시스템 어드레스 버스로서 CHIP 내부의 I/O, 메모리 및 Boot PROM 엑세스에 필요한 어드레스 버스로 사용하며, I/O 읽기 및 쓰기에는 SA0-15 까지가 사용되고, 메모리 읽기/쓰기에는 SA0-19 까지 사용 | |
| SD[15:0] | 시스템 데이터 버스로서 호스트 시스템과 칩과의 데이터 전송용 버스 | |
| AEN | 어드레스 Enable 로서 I/O 및 메모리 액세스 시에는 Low, DMA 싸이클에서는 High 상태를 유지 | |
| nMEMR/nMEMW | Memory Read/Write | |
| nMEMCS16 | 메모리 칩 선택 16비트로서 칩이 메모리 모드에서 동작할 때, ISA 버스 상에 메모리 공간 범위에 해당되는 어드레스가 감지될 경우 출력되는 신호로서 inactive 시에는 3-State 로 유지 | |
| nREFRESH | DRAM Refresh 신호 | |
| nlOR/nlOW | I/O 읽기/쓰기 신호로서 어드레스 상에서, 유효한 어드레스가 감지되면, 어드레스가 지정하는 16비트 I/O 레지스터에 시스템 데이터 버스 상의 값을 Read/Write | |
| nlOCS16 | I/O 칩 선택 신호로서 16비트 어드레스 버스 상에 I/O 어드레스에 해당하는 어드레스가 감지될 경우 출력되는 신호 | |
| IOCHRDY | I/O Channel Ready | |
| nSSHE | 시스템 버스 High Enable 신호로서 시스템 데이터 버스의 High Byte(SD8-15)를 사용하여 데이터를 전송 | |
| INTRQ[3:0] | 인터럽트 요구 신호로서 인터럽트 이벤트가 발생했음을 알려주는 신호 | |
| DMARQ[2:0] | DMA 요구 신호로서 DMA 전송을 요구하는 출력신호 | |
| nDMACK[2:0] | DMA acknowledge 신호로서 호스트 시스템이 DMA 요구에 대한 응답 | |
| nCHIPSEL | Chip Select 로서 메모리 모드에서만 사용하며, I/O 및 DMA 모드에서는 low 유지 | |
| EESK/EECS/EEDATAIN/EEDATAOUT | EEPROM serial clock/chip select/data in/data out | |
| nELCS/nCSOUT | external logic chip select, external Boot PROM 용 chip select | |
| TXD+/TXD- | 10Base-T Transmit differential output pair | |
| RXD+/RXD- | 10Base-T Receive differential output pair | |
| XTAL[2:1] | 20MHz crystal | |
| nSEELP | 하드웨어 sleep | |
| nLINKLED | Link good LED 신호로서 칩이 유효한 링크 펄스를 감지했을 경우, Low 유지 | |
| nLANLED | LAN activity LED 로서 수신 패킷, 송신 패킷 또는 데이터 충돌이 일어났을 경우 6ms 동안 Low 유지 | |
| nBSTATUS | 버스 상태 신호로서 ISA 버스 액세스를 할 때 Low 유지 | |
| nTEST | Test Enable 신호로서 칩이 Boundary-Scan test 모드일 때 Low 유지 | |
| RES | Reference 저항으로 칩 내부 아날로그 회로 바이어스에 필요한 4.9K 옴 저항 연결 | |
| DVDD/DVSS | 디지털 Power/Ground | |
| AVDD/AVSS | 아날로그 Power/Ground |
I/O 모드
CS8900A 칩은 디폴트로 I/O 모드로 설정된다. I/O 모드에서 동작할 때는 8개의 16bit I/O 포트를 통해서 PacketPage 메모리를 액세스하게 되며, 이 포트들은 호스트 시스템의 I/O 주소공간에서 16개의 연속된 I/O 주소로 매핑된다.
전원이 공급되면, I/O 베이스 어드레스는 0x300 으로 설정된다. 베이스 어드레스는 구성하는 EEPROM 이나, 시스템 설정 단계에서 변경될 수 있다. CS8900A 에서 I/O 모드에 따른 베이스 어드레스를 기준으로 아래 표와 같이 I/O 가 맵핑되어 있다.
| Offset | Type | 설명 |
| 0x0000 | R/W | Receive/Transmit Data(port 0) |
| 0x0002 | R/W | Receive/Transmit Data(port 1) |
| 0x0004 | W | Transmit Command(TxCMD) |
| 0x0006 | W | Transmit Length(TxLength) |
| 0x0008 | R | Interrupt Status Queue |
| 0x000a | R/W | PacketPage pointer |
| 0x000c | R/W | PacketPage Data(port 0) |
| 0x000e | R/W | PacketPage Data(port 1) |
| 송수신 데이터 포트(포트 0, 1) | CS8900A 칩에 데이터를 송수신할 때는 이 포트를 사용한다. 16비트 데이터를 송수신할 때는 포트 0만을 사용하고, 32bit 데이터를 송수신할 때는 포트 0(Lower word), 포트 1(High word)을 같이 사용한다 |
| 송신 명령어 포트 | 호스트 시스템은 데이터를 송신할 때마다 송신 명령(TxCMD)을 이 포트에 써야 한다. TxCMD는 송신할 데이터가 있으며, 어떻게 보낼 것인지 알려준다. 이포트는 'PacketPage 베이스 어드레스+0x0144' 에 맵핑되어 있다 |
| 송신 길이 포트 | 송신 명령을 쓴 후에 바로 송신할 프레임 길이를 이 포트에 써야한다. 이 포트는 'PacketPage base+0x0146' 에 매핑되어 있다 |
| 인터럽트 상태 큐 포트 | 이 포트는 'PacketPage+0x0120' 에 맵핑되어 있는 Interrupt Status Queue(ISQ)의 현재값을 가지고 있다 |
| 패킷페이지 포인터 포트 | 호스트 시스템이 CS8900A 칩의 내부 레지스터를 액세스할 때마다 사용하는 포트이다. 처음 12비트(bit 0 - bit b)는 액세스할 레지스터의 내부 어드레스이며, 다음 3비트는 '0x011b' 로 설정되어 있다. 마지막 MSB 1 비트는 포인터가 자동 증가할지 여부를 결정한다. MSB=1 이면, 자동으로 증가한다 |
| 패킷페이지 데이터 포트 0,1 | CS8900A 의 내부 레지스터에 데이터를 보내거나 받을 때 사용하는 포트 |
I/O 모드 송신 절차
호스트는 TxCMD 포트(I/O base+0x0004) 에 송신 명령을 보낸다.
TxLength 포트(I/O base+0x0006)에 송신할 프레임 길이정보를 보낸다.
호스트는 패킷 페이지 포인터 포트(I/O base+0x000a)에 0x0138 을 써서, Busst 레지스터를 액세스할 준비를 한다.
- 패킷 페이지 데이터 포트(I/O base+0x000c)에 Burst 레지스터(레지스터 18) 값이 출력되기 때문에 이 값을 읽어, TxNOW(bit 8) 비트를 검사한다. 이 비트가 High 이면, 프레임을 보낼 수 있지만, Low 이면 CS8900A 내의 버퍼 메모리가 사용 가능해 질 때까지 기다려야 한다.
- 일단 CS 8900A 칩이 프레임을 받을 준비가 된 후에는 호스트는 송수신 데이터 포트(I/O base+0x0000)에 반복 쓰기 명령(REP OUT : Repetitive Write)을 보내 호스트의 전체 프레임을 CS8900A 버퍼 메모리에 전송한다.
패킷의 전송은 두 가지 단계로 전송된다. 첫 번째 단계에서는 호스트에서 이더넷 프레임을 CS8900A 버퍼 메모리로 전송하고, 두 번째 단계에서 CS8900A 가 프레임 데이터 앞에 Preamble(up to 7B), Start-of-Frame delimiter(SFD : 1B), Destination/Source 어드레스(DA (6B)/SA (6B)), length field(2B), Logical Link Control(LLC)를 붙이고 끝에 프레임 검사 시퀀스(Frame Check Sequence : FCS(4B))를 덧붙여 이더넷 패킷으로 만들어서 보낸다. 만약 CRC 포함해서 64 바이트가 안 되는 프레임은 패딩 비트를 붙여보낸다.
I/O 모드 수신 절차
- 프레임 데이터가 CS8900A 에 도착하면, 인터럽트를 발생시킨다.
- 호스트는 ISQ(Interrupt Status Queue) 포트(I/O base+0x0008)를 읽어 프레임이 도착했음을 알아낸다.
- 호스트는 송수신 데이터 포트(I/O base+0x0000)를 반복 읽기 명령(REP IN : repetitive)을 사용하여 프레임 데이터를 읽는다. 프레임 데이터 앞에 RxStatus 레지스터(Packet Page base+0x0400) 내용과 RxLength 레지스터(Packet Page base+0x0402) 내용이 먼저 나온다.
아날로그 Front-end 회로와 맨체스터 디코더에서 처리된 패킷은 다음 세가지 단계 1) 전처리 단계(Preprocessing), 2) 임시저장(Temporary Buffering), 3) 호스트로 전송(Transfer to Host)로 처리된다. 첫번째 단계는 전처리 단계로 목적지 어드레스 필터링, Early 인터럽트 발생, Acceptance 필터링, Normal 인터럽트 발생과정을 거친다.
첫번째 과정을 거친 프레임들은 호스트가 처리해 줄 때까지 on-chip RAM 에 저장된다.
포팅하기
단순히 생각해보면, 커널옵션에서 CS8900 드라이버를 선택해서 함께 빌드하면 될 것 같다. 하지만, 실제로 커널 옵션에 가보면, CS8900 옵션은 보이지 않는다.
CS8900 은 ISA 나 PCI 옵션에 의존성을 갖는데, 타겟 아키텍쳐를 PXA2XX 로 선택하는 순간, 자동으로 이런 옵션들이 disable 되기 때문이다.
강제로 지정해보자.
arch/arm/mach-pxa/Kconfig 파일에 다음을 추가한다.
...
config MACH_WJ_X5
bool "WJ_X5 Development Platform"
select PXA25x
select ISA // 추가
...
이제 커널 옵션에서 CS8900 옵션이 보일 것이다.
Device Drivers ---> [*] Network device support ---> [*] Ethernet (10 or 100Mbit) ---> <*> CS89x0 support
선택하고 빌드해서 부팅해보자!
pxa2xx-uart.0: ttyS0 at MMIO 0x40100000 (irq = 22) is a FFUART pxa2xx-uart.1: ttyS1 at MMIO 0x40200000 (irq = 21) is a BTUART pxa2xx-uart.2: ttyS2 at MMIO 0x40700000 (irq = 20) is a STUART console [ttyS2] enabled pxa2xx-uart.3: ttyS3 at MMIO 0x41600000 (irq = 7) is a HWUART brd: module loaded cs89x0:cs89x0_probe(0x0) Unable to handle kernel NULL pointer dereference at virtual address 0000030a pgd = c0004000 [0000030a] *pgd=00000000 Internal error: Oops: f5 [#1] Modules linked in: CPU: 0 Not tainted (2.6.28.4 #12) PC is at readword+0xc/0x14 LR is at cs89x0_probe1+0xec/0x82c pc : [<c014d9c8>] lr : [<c0019cd4>] psr: 60000013 sp : c381dd6c ip : c381dd7c fp : c381dd78 r10: 00000000 r9 : 00000000 r8 : 00000300 r7 : 00000000 r6 : c3842000 r5 : c3842380 r4 : 00000300 r3 : ffffffff r2 : 00000000 r1 : 0000000a r0 : 00000300 Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel Control: 0000397f Table: a0004000 DAC: 00000017 Process swapper (pid: 1, stack limit = 0xc381c260) Stack: (0xc381dd6c to 0xc381e000) dd60: c381de18 c381dd7c c0019cd4 c014d9c8 c003c3e4 dd80: c003bf88 20000013 c003bf58 c028acdc c025e868 c02a604e 00000000 00000016 dda0: c381de24 c381ddd4 c381ddb4 c0083810 c0083888 c381ddd8 00000000 c3842000 ddc0: 00000001 0000008c c381de08 c381ddd8 00000000 c3842000 00000000 00000000 dde0: c02a02e4 00000000 00000000 c381de08 c381ddfc c0023300 c3842000 00000000 de00: 00000000 c02a02e4 00000000 c381de38 c381de1c c001a4ac c0019bf4 c00232b8 de20: 00000001 00000000 c00198dc c381de54 c381de3c c00198ac c001a420 00000001 de40: 00000000 c381c000 c381de6c c381de58 c0019964 c0019874 c00200b4 c0020134 de60: c381dfd8 c381de70 c00242e4 c00198e8 c029fba8 c02a015c 000012d0 c02a0158 de80: 00000000 c381a000 00000000 0000000d c381def0 c381dea0 c0067294 c0066480 dea0: 00000000 00000044 000212d0 00000000 00000000 c381deec 00000010 00000000 dec0: 00000000 c381defc ffffffff 00000000 00000000 00000000 c3836180 00000000 dee0: c02aa02c c3803480 00000000 c381df40 c381defc c011a3e0 c0083054 c3816000 df00: 00000000 000000d0 c02a8c10 00000000 c381df38 c381df20 c3837000 c3837000 df20: c3836180 c02a8c10 00000000 00000000 00000000 c381df50 c381df44 c011a408 df40: c011a250 c381df78 c381df54 c00c0834 c011a3fc c028ec50 00000000 c3837000 df60: c381df94 00000094 c02a8c10 c381df90 c381df7c c00c0b00 c00c0808 c3836180 df80: c028ec50 c381dfbc c381df94 c006010c c00c0b18 00383431 00000000 c0010000 dfa0: 000000bf c028f664 c028f6a0 00000000 c381dfd8 c00200b4 c0020134 00000000 dfc0: 00000000 00000000 00000000 c381dff4 c381dfdc c0008be0 c0024294 00000001 dfe0: 00000000 00000000 00000000 c381dff8 c003d5ac c0008b64 ffffffff ffffffff Backtrace: [<c014d9bc>] (readword+0x0/0x14) from [<c0019cd4>] (cs89x0_probe1+0xec/0x82c) [<c0019be8>] (cs89x0_probe1+0x0/0x82c) from [<c001a4ac>] (cs89x0_probe+0x98/0x110) [<c001a414>] (cs89x0_probe+0x0/0x110) from [<c00198ac>] (probe_list2+0x44/0x74) r7:c00198dc r6:00000000 r5:00000001 r4:c00232b8 [<c0019868>] (probe_list2+0x0/0x74) from [<c0019964>] (net_olddevs_init+0x88/0xc8) r6:c381c000 r5:00000000 r4:00000001 [<c00198dc>] (net_olddevs_init+0x0/0xc8) from [<c00242e4>] (__exception_text_end+0x5c/0x17c) r5:c0020134 r4:c00200b4 [<c0024288>] (__exception_text_end+0x0/0x17c) from [<c0008be0>] (kernel_init+0x88/0xe8) [<c0008b58>] (kernel_init+0x0/0xe8) from [<c003d5ac>] (do_exit+0x0/0x6a8) r5:00000000 r4:00000000 Code: e89da800 e1a0c00d e92dd800 e24cb004 (e19000b1) ---[ end trace fd5e7911d5e8e0f0 ]--- Kernel panic - not syncing: Attempted to kill init!
익숙한 커널패닉이 발생했다. 트레이스 정보를 보아 처음 이더넷 칩을 인식하는 부분에서 부터 잘못되어 엉뚱한 주소값을 참조하고 있다.
이로서 원래 그대로의 드라이버 코드를 사용할수는 없다는 결론이 났다.
드라이버 코드 수정하기
해당 드라이버 파일(drivers/net/cs89x0.c)을 아래처럼 수정하자.
앞서 추가한 커널 옵션(CONFIG_MACH_WJ_X5)을 선택했을 경우에 실행되도록 했다. 파일은 cs89x0.c 에서 다운받을 수 있다.
...
#elif defined(CONFIG_MACH_WJ_X5)
#define CS8900_ENABLE_WORD (*((volatile char *)(0xF1000000+0x300 + 0x00000001)) = 0 ) // CS8900 칩은 활성화 시킴(매우 중요!)
static unsigned int netcard_portlist[] __used __initdata = {0xF1000000+0x300, 0}; // CS8900 기본 주소
static unsigned int cs8900_irq_map[] = {IRQ_GPIO(21), 0, 0, 0}; // 21번 GPIO 핀을 인터럽트로 할당
..
#if defined(CONFIG_MACH_WJ_X5)
if ( unit != 0 ) { err = -ENODEV; return ERR_PTR(err);} // 바로 에러를 리턴하도록 수정
#endif
...
#ifdef CONFIG_MACH_WJ_X5
CS8900_ENABLE_WORD; // CS8900 칩 활성화, 실제 이 루틴을 지우면 인식이 안되어 eth0 인터페이스 생성되지 않음
#endif
...
#ifdef CONFIG_MACH_WJ_X5
if (1)
{
/* Force a 10 Base-T connection. */
lp->force = FORCE_RJ45 | FORCE_FULL; // 랜선의 종류 설정
/* don't error if no cable plugged */
lp->auto_neg_cnf = IMM_BIT; // 랜선의 연결 여부 설정
#else
...
#if defined(CONFIG_SH_HICOSH4) || defined( CONFIG_MACH_WJ_X5 ) /* no EEPROM on HiCO, don't hazzle with it here */
if (1) {
printk(KERN_NOTICE "cs89x0: No EEPROM on HiCO.SH4\n");
} else
#endif
...
#if defined( CONFIG_ARCH_IXDP2X01 ) || defined( CONFIG_MACH_WJ_X5 )
i = cs8900_irq_map[0];
#else
...
#ifdef CONFIG_MACH_WJ_X5
{
*((volatile u8 *)(ioaddr+1)) = 0; // 칩을 리셋하기 위해 설정
}
#endif
...
#if defined( CONFIG_ARCH_IXDP2X01 ) || defined( CONFIG_MACH_WJ_X5 )
#else
if (((1 << dev->irq) & lp->irq_map) == 0) {
printk(KERN_ERR "%s: IRQ %d is not in our map of allowable IRQs, which is %x\n",
dev->name, dev->irq, lp->irq_map);
ret = -EAGAIN;
goto bad_out;
}
#endif
...
이제 다시 빌드해서 부팅시켜보자!
Copy Kernel Image ..... Copy Ramdisk Image ..... Starting kernel [MARCH 1912]... kernel command [EZBOOT mem=64M initrd=0xA0800000,5M root=/dev/ram ramdisk=16384 console=ttyS2,115200 ip0=1.1.1.2 mac=00:FA:07:78:65:05 netmask=255.255.255.0 gw=192.16] Uncompressing Linux.................................................................................... done, booting the kernel. Linux version 2.6.28.4 (root@fat81) (gcc version 3.4.3) #26 Sun Jan 13 01:15:10 KST 2013 CPU: XScale-PXA255 [69052d06] revision 6 (ARMv5TE), cr=0000397f CPU: VIVT data cache, VIVT instruction cache Machine: WJ-X5 Development Platform Memory policy: ECC disabled, Data cache writeback Memory clock: 99.53MHz (*27) Run Mode clock: 398.13MHz (*4) Turbo Mode clock: 398.13MHz (*1.0, inactive) Built 1 zonelists in Zone order, mobility grouping on. Total pages: 16256 Kernel command line: EZBOOT mem=64M initrd=0xA0800000,5M root=/dev/ram ramdisk=16384 console=ttyS2,115200 ip0=1.1.1.2 mac=00:FA:07:78:65:05 netmask=255.255.255.0 gw=1 PID hash table entries: 256 (order: 8, 1024 bytes) Console: colour dummy device 80x30 Dentry cache hash table entries: 8192 (order: 3, 32768 bytes) Inode-cache hash table entries: 4096 (order: 2, 16384 bytes) Memory: 64MB = 64MB total Memory: 57104KB available (2352K code, 185K data, 108K init) SLUB: Genslabs=12, HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1 Calibrating delay loop... 397.31 BogoMIPS (lpj=1986560) Mount-cache hash table entries: 512 CPU: Testing write buffer coherency: ok net_namespace: 288 bytes NET: Registered protocol family 16 NET: Registered protocol family 2 IP route cache hash table entries: 1024 (order: 0, 4096 bytes) TCP established hash table entries: 2048 (order: 2, 16384 bytes) TCP bind hash table entries: 2048 (order: 1, 8192 bytes) TCP: Hash tables configured (established 2048 bind 2048) TCP reno registered NET: Registered protocol family 1 checking if image is initramfs...it isn't (no cpio magic); looks like an initrd Freeing initrd memory: 5120K NetWinder Floating Point Emulator V0.97 (double precision) JFFS2 version 2.2. (NAND) �© 2001-2006 Red Hat, Inc. msgmni has been set to 121 io scheduler noop registered io scheduler anticipatory registered io scheduler deadline registered io scheduler cfq registered (default) pxa2xx-uart.0: ttyS0 at MMIO 0x40100000 (irq = 22) is a FFUART pxa2xx-uart.1: ttyS1 at MMIO 0x40200000 (irq = 21) is a BTUART pxa2xx-uart.2: ttyS2 at MMIO 0x40700000 (irq = 20) is a STUART console [ttyS2] enabled pxa2xx-uart.3: ttyS3 at MMIO 0x41600000 (irq = 7) is a HWUART brd: module loaded cs89x0:cs89x0_probe(0x0) // 이더넷 인식 시작 cs89x0.c: v2.4.3-pre1 Russell Nelson <nelson@crynwr.com>, Andrew Morton eth0: cs8900 rev J found at 0xf1000300 [Cirrus EEPROM] // cs8900 칩 인식함 cs89x0: No EEPROM on HiCO.SH4 cs89x0 media RJ-45, IRQ 85, programmed I/O, MAC 00:fa:07:78:65:05 // MAC 주소 읽음 cs89x0_probe1() successful cs89x0:cs89x0_probe(0x0) pxa25x_udc: version 30-June-2007 mice: PS/2 mouse device common for all mice TCP cubic registered RPC: Registered udp transport module. RPC: Registered tcp transport module. XScale DSP coprocessor detected. RAMDISK: Compressed image found at block 0 VFS: Mounted root (ext2 filesystem) readonly. Freeing init memory: 108K INIT: version 2.86 booting INIT: Entering runlevel: 3 eth0: using full-duplex 10Base-T (RJ-45) // eth0 인터페이스 생성 route: SIOC[ADD|DEL]RT: Network is unreachable mount: Mounting /dev/mtdblock2 on /app failed: No such device /usr/local/apache/bin/apachectl start: httpd started Starting system logger: [ OK ] Starting INET services: [ OK ] Welcome to FALinux (www.falinux.com) Linux Kernel 2.6.28.4 falinux login:
이제 이더넷을 통해 외부와 통신이 가능해졌다.
참고 자료(이지부트)
이지부트의 CS8900 코드를 보면 좀더 명확히 알 수 있다.
//------------------------------------------------------------------------------
// 화일명 : cs8900.c
// 설 명 : 이더넷칩 cs8900을 제어한다.
// 작성자 : 유영창 (주)제이닷디엔티 frog@falinux.com
// 작성일 : 2001년 10월 8일
// 수 정 : 2003년 05월 20일 pxa255 를 위해 수정
// 저작권 : (주)제이닷디엔티
// 주 의 :
//------------------------------------------------------------------------------
//******************************************************************************
//
// 헤더 정의
//
//******************************************************************************
#include <pxa255.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <mem_map.h>
#include <cs8900.h>
#include <config.h>
//******************************************************************************
//
// 광역 변수 정의
//
//******************************************************************************
//------------------------------------------------------------------------------
// 설명 : 설정된 맥 어드레스의 주소를 얻는다.
// 매개 : 없음
// 반환 : 맥 어드레스
// 주의 : 없음
//------------------------------------------------------------------------------
Byte *CS8900_GetMacAddress( void )
{
return Cfg.Local_MacAddress;
}
//------------------------------------------------------------------------------
// 설명 : IO mode의 packet page point에 addr(packet page address)를 쓰고,
// IO mode의 packet page data에 value(packet page data)를 써서 값을 바꾼다.
// 매개 : addr : packet page 주소.
// value : packet page 주소의 위치에 들어갈 값.
// 반환 : 없음.
// 주의 : 없음.
//------------------------------------------------------------------------------
static void CS8900_WriteToPPR(short addr, short value)
{
CS8900_PPPTR = addr;
CS8900_PPDATA = value;
return;
}
//------------------------------------------------------------------------------
// 설명 : IO mode의 packet page point에 addr(packet page address)를 쓰고,
// IO mode의 packet page data에서 값을 읽어 return한다.
// 매개 : addr : packet page 주소.
// 반환 : packet page addr에 있는 값.
// 주의 : 없음
//------------------------------------------------------------------------------
static short CS8900_ReadFromPPR( short addr )
{
CS8900_PPPTR = addr;
return CS8900_PPDATA;
}
//------------------------------------------------------------------------------
// 설명 : packet을 전송.
// 전송 가능한 상태인지 확인한 후 IO_RTX_DATA에 packet을 반복적으로 기록.
// 매개 : txPktBuf : 전송할 패켓 어드레스
// len : 패켓 크기
// 반환 : packet page addr에 있는 값.
// 주의 : 없음
//------------------------------------------------------------------------------
BOOL CS8900_Transmit( void *txPktBuf, int len )
{
short *endPtr, *s;
unsigned int tick = 0;
// Ethernet Tx on.
CS8900_PPPTR = PP_LineCTL;
CS8900_PPDATA |= SERIAL_TX_ON;
// packet 전체가 CS8900A에 보내진 후 전송시작.
CS8900_TXCMD = TX_AFTER_ALL;
CS8900_TXLEN = len;
// Tx가 준비될 때까지 대기.
//printf( "TX-Enter ");
while ((CS8900_ReadFromPPR(PP_BusST) & READY_FOR_TX_NOW)==0)
{
tick++;
if ( 0 == tick )
{
printf( " Error TX BIT\n");
return FALSE;
}
}
//printf( " TX-Exit\n");
// CS8900으로 packet 전송. 이것이 끝난 후 CS8900A가 RJ45로 packet 전송을 시작함.
for (s=txPktBuf, endPtr=txPktBuf+len; s<endPtr; s++)
{
CS8900_RTX_DATA = *s;
// printf( " %04X", SWAP16(*s) & 0xFFFF );
}
return TRUE;
}
//------------------------------------------------------------------------------
// 설명 : 한 프레임 패킷을 수신한다.
// packet 전송을 받을 때는 IO_RTX_DATA에서 state, length, packet의 순서로 읽어 옴.
// 매개 :
// 반환 : 값을 읽을 경우는 길이 / 없으면 0
// 주의 : ip의 경우 udp까지 check한 후 각각을 처리할 수 있는 receive 함수 호출.
//------------------------------------------------------------------------------
int CS8900_Resive(void *rxPktBuf, int max_size )
{
int i;
Word16 *s;
short isq, status, len, cnt;
isq=CS8900_ISQ;
// 수신 에러가 발생하였는가를 감시한다.
if( isq & ISQ_RX_MISS_EVENT )
{
printf( "Miss RX\n" );
return 0;
}
// 수신 이벤트가 발생하였는가를 감시한다.
if( isq & ISQ_RECEIVER_EVENT )
{
// 수신 상태를 얻는다.
status = CS8900_RTX_DATA;
len = CS8900_RTX_DATA; // 수신된 길이를 얻는다.
// printf( "RESIVE EVENT [%04X]\n", status );
// printf( "RESIVE LENGTH %d \n", len );
// 데이타가 틀리더라도 일단 읽어 온다.
cnt = len/2 + len%2;
for (i=0, s=rxPktBuf; i<cnt; i++)
{
*s = CS8900_RTX_DATA;
s++;
// printf( " [%04X]", SWAP16(*s) );
}
if( status & RX_OK ) return len;
}
return 0;
}
//------------------------------------------------------------------------------
// 역할 : CS8900이 네트워크에 링크 되어 있는가를 확인한다.
// 매개 :
// 반환 : 링크 되어 있으면 TRUE 실패하면 FALSE를 반환한다.
// 주의 :
//------------------------------------------------------------------------------
BOOL CS8900_CheckLink( void )
{
Word16 LineState;
LineState = CS8900_ReadFromPPR( PP_LineST );
//printf( "CS8900 Line Link Check\n" ); // 읽은값
// check Link on.
if( ! ( LineState & LINK_OK ) )
{
return FALSE; //printf("Ethernet Link failed. Check line.\n");
}
return TRUE; //printf("Ethernet Link Ok\n");
}
//------------------------------------------------------------------------------
// 역할 : CS8900을 인터럽트를 쓰지 않는 IO모드로 초기화 한다.
// 매개 :
// 반환 : 성공하면 TRUE 실패하면 FALSE를 반환한다.
// 주의 :
//------------------------------------------------------------------------------
BOOL CS8900_Init( void )
{
int lp;
Word32 DectectID;
Word16 ChipIDLow;
Word16 ChipIDHigh;
printf( "CS8900 Init............................\n" );
printf( " Mac Address : " );
printf( "[%02X %02X %02X %02X %02X %02X]\n", Cfg.Local_MacAddress[0],Cfg.Local_MacAddress[1],Cfg.Local_MacAddress[2],
Cfg.Local_MacAddress[3],Cfg.Local_MacAddress[4],Cfg.Local_MacAddress[5] );
// 포인터 레지스터의 값을 읽어 CS8900 이 존재하는지 확인한다.
CS8900_ENABLE_WORD; // WORD 억세스를 이네이블 시킨다. SHBE pin 토클
DectectID = CS8900_PPPTR; // ID를 얻어 온다.
printf( " Detect value : [%04X:%04X]\n", CS8900_DECTECT_MASK, DectectID & CS8900_DECTECT_MASK );
if( ( DectectID & CS8900_DECTECT_MASK ) != CS8900_DECTECT_MASK )
{
printf( " Can't access to Memory of CS8900A.\n");
return FALSE;
}
// CS8900의 Chip ID 를 확인한다.
ChipIDLow = CS8900_ReadFromPPR( PP_ChipID_LOW );
ChipIDHigh = CS8900_ReadFromPPR( PP_ChipID_HIGH );
// 바이트 정렬을 수행한다.
ChipIDHigh = SWAP16( ChipIDHigh );
ChipIDLow = SWAP16( ChipIDLow );
printf( " Chip ID : [%04X:%04X]\n", ChipIDHigh, ChipIDLow ); // 읽은값
//printf( " PRODUCT ID : [%04X]\n", ChipIDHigh ); // 칩 EISA에 등록된 제품 번호
//printf( " VERSION : [%04X]\n", ChipIDLow & 0x1F ); // 칩 버전
if ( ChipIDHigh != CHECK_CHIP_ID_HIGH )
{
printf(" Ethernet Chip is not CS8900A. Are you use CS8900A.\n");
return FALSE;
}
// 송수신을 불가능하게 한다.
CS8900_WriteToPPR( PP_LineCTL, 0x0000 );
// 모든 인터럽트를 금지 시킨다.
CS8900_WriteToPPR(PP_CS8900_ISAINT, INTRQ_HIGH_IMPEDENCE ); // Interrupt number.
CS8900_WriteToPPR(PP_CS8900_ISADMA, DMRQ_HIGH_IMPEDENCE ); // DMA Channel Number.
// 폴링 방식으로 송수신이 가능하도록 상태 및 제어 레지스터를 초기화 한다.
CS8900_WriteToPPR(PP_RxCFG, RX_OK_ENBL | RX_CRC_ERROR_ENBL);
CS8900_WriteToPPR(PP_RxCTL, RX_OK_ACCEPT | RX_MULTICAST_ACCEPT | RX_IA_ACCEPT | RX_BROADCAST_ACCEPT);
CS8900_WriteToPPR(PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL | TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL);
CS8900_WriteToPPR(PP_TxCMD, TX_START_ALL_BYTES);
CS8900_WriteToPPR(PP_BufCFG, 0x0000);
CS8900_WriteToPPR(PP_BusCTL, ENABLE_IRQ | IO_CHANNEL_READY_ON);
CS8900_WriteToPPR(PP_TestCTL, 0x0000);
// 전송 명령 초기화
CS8900_WriteToPPR(PP_TxCommand,0x00c0);
// MAC 어드레스 로직 필터 마스크 설정
// 여기서는 모든 패켓을 허가하게 설정한다.
for ( lp = 0 ; lp < 4 ; lp++ ) CS8900_WriteToPPR( PP_LAF+lp*2, 0xFFFF );
// MAC 어드레스를 설정한다.
for ( lp = 0 ; lp < 3 ; lp++ ) CS8900_WriteToPPR( PP_IA+lp*2, *((Word16 *)&( Cfg.Local_MacAddress[lp*2])));
// 송수신이 가능하게 만든다.
CS8900_WriteToPPR(PP_LineCTL, SERIAL_RX_ON | SERIAL_TX_ON);
//printf( " INIT OK!\n\n" );
return TRUE;
}
void CS8900_SwReset( void )
{
CS8900_Init();
// 전송 명령 초기화
// CS8900_WriteToPPR(PP_TxCommand,0x00c0);
}

