diff --git a/part14-ethernet/Makefile b/part14-ethernet/Makefile new file mode 100644 index 0000000..1e3608f --- /dev/null +++ b/part14-ethernet/Makefile @@ -0,0 +1,20 @@ +CFILES = $(wildcard *.c lib/*.c kernel/*.c net/*.c) +SFILES = $(wildcard boot/*.S lib/*.S kernel/*.S) +OFILES = $(CFILES:.c=.o) $(SFILES:.S=.o) +LLVMPATH = /opt/homebrew/opt/llvm/bin +CLANGFLAGS = -Wall -O2 -ffreestanding -nostdlib -mcpu=cortex-a72+nosimd + +all: clean kernel8.img + +%.o: %.c + $(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c $< -o $@ + +%.o: %.S + $(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c $< -o $@ + +kernel8.img: $(OFILES) + $(LLVMPATH)/ld.lld -m aarch64elf -nostdlib $(OFILES) -T boot/link.ld -o kernel8.elf + $(LLVMPATH)/llvm-objcopy -O binary kernel8.elf kernel8.img + +clean: + /bin/rm kernel8.elf */*.o *.img > /dev/null 2> /dev/null || true diff --git a/part14-ethernet/boot/boot.S b/part14-ethernet/boot/boot.S new file mode 100644 index 0000000..f644aee --- /dev/null +++ b/part14-ethernet/boot/boot.S @@ -0,0 +1,114 @@ +#include "sysregs.h" + +#define LOCAL_CONTROL 0xff800000 +#define LOCAL_PRESCALER 0xff800008 +#define OSC_FREQ 54000000 +#define MAIN_STACK 0x400000 + +.section ".text.boot" // Make sure the linker puts this at the start of the kernel image + +.global _start // Execution starts here + +_start: + ldr x0, =LOCAL_CONTROL // Sort out the timer + str wzr, [x0] + mov w1, 0x80000000 + str w1, [x0, #(LOCAL_PRESCALER - LOCAL_CONTROL)] + + ldr x0, =OSC_FREQ + msr cntfrq_el0, x0 + msr cntvoff_el2, xzr + + // Check processor ID is zero (executing on main core), else hang + mrs x1, mpidr_el1 + and x1, x1, #3 + cbz x1, 2f + + // We're not on the main core, so hang in an infinite wait loop + adr x5, spin_cpu0 +1: wfe + ldr x4, [x5, x1, lsl #3] + cbz x4, 1b + + ldr x2, =__stack_start // Get ourselves a fresh stack - location depends on CPU core asking + lsl x1, x1, #9 // Multiply core_number by 512 + add x3, x2, x1 // Add to the address + mov sp, x3 + + mov x0, #0 + mov x1, #0 + mov x2, #0 + mov x3, #0 + br x4 + b 1b +2: // We're on the main core! + // First enable the FPU + + mov x0, #0x33ff + msr cptr_el3, x0 // Disable coprocessor traps to EL3 + mov x0, #3 << 20 + msr cpacr_el1, x0 // Enable FP/SIMD at EL1 + + // Now get ready to switch from EL3 down to EL1 + + ldr x0, =SCTLR_VALUE_MMU_DISABLED + msr sctlr_el1, x0 + + ldr x0, =HCR_VALUE + msr hcr_el2, x0 + + ldr x0, =SCR_VALUE + msr scr_el3, x0 + + ldr x0, =SPSR_VALUE + msr spsr_el3, x0 + + adr x0, el1_entry + msr elr_el3, x0 + + eret +el1_entry: + // We're in EL1 + // Clean the BSS section + ldr x1, =__bss_start // Start address + ldr w2, =__bss_size // Size of the section +3: cbz w2, 4f // Quit loop if zero + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, 3b // Loop if non-zero + + // Set stack to start somewhere safe + mov sp, #MAIN_STACK + + // Jump to our main() routine in C (make sure it doesn't return) +4: bl main + // In case it does return, halt the master core too + b 1b + +.ltorg + +.org 0x110 +.globl spin_cpu0 +spin_cpu0: + .quad 0 + +.org 0x118 +.globl spin_cpu1 +spin_cpu1: + .quad 0 + +.org 0x120 +.globl spin_cpu2 +spin_cpu2: + .quad 0 + +.org 0x128 +.globl spin_cpu3 +spin_cpu3: + .quad 0 + +.globl get_el +get_el: + mrs x0, CurrentEL + lsr x0, x0, #2 + ret diff --git a/part14-ethernet/boot/link.ld b/part14-ethernet/boot/link.ld new file mode 100644 index 0000000..4e84390 --- /dev/null +++ b/part14-ethernet/boot/link.ld @@ -0,0 +1,35 @@ +SECTIONS +{ + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + __bss_size = (__bss_end - __bss_start)>>3; + } + .cpu1Stack : + { + . = ALIGN(16); + __stack_start = .; + . = . + 512; + __cpu1_stack = .; + } + .cpu2Stack : + { + . = . + 512; + __cpu2_stack = .; + } + .cpu3Stack : + { + . = . + 512; + __cpu3_stack = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} diff --git a/part14-ethernet/boot/sysregs.h b/part14-ethernet/boot/sysregs.h new file mode 100644 index 0000000..385cd79 --- /dev/null +++ b/part14-ethernet/boot/sysregs.h @@ -0,0 +1,44 @@ +#ifndef _SYSREGS_H +#define _SYSREGS_H + +// *************************************** +// SCTLR_EL1, System Control Register (EL1), Page 2654 of AArch64-Reference-Manual. +// *************************************** + +#define SCTLR_RESERVED (3 << 28) | (3 << 22) | (1 << 20) | (1 << 11) +#define SCTLR_EE_LITTLE_ENDIAN (0 << 25) +#define SCTLR_EOE_LITTLE_ENDIAN (0 << 24) +#define SCTLR_I_CACHE_DISABLED (0 << 12) +#define SCTLR_D_CACHE_DISABLED (0 << 2) +#define SCTLR_I_CACHE_ENABLED (1 << 12) +#define SCTLR_D_CACHE_ENABLED (1 << 2) +#define SCTLR_MMU_DISABLED (0 << 0) +#define SCTLR_MMU_ENABLED (1 << 0) + +#define SCTLR_VALUE_MMU_DISABLED (SCTLR_RESERVED | SCTLR_EE_LITTLE_ENDIAN | SCTLR_I_CACHE_ENABLED | SCTLR_D_CACHE_ENABLED | SCTLR_MMU_DISABLED) + +// *************************************** +// HCR_EL2, Hypervisor Configuration Register (EL2), Page 2487 of AArch64-Reference-Manual. +// *************************************** + +#define HCR_RW (1 << 31) +#define HCR_VALUE HCR_RW + +// *************************************** +// SCR_EL3, Secure Configuration Register (EL3), Page 2648 of AArch64-Reference-Manual. +// *************************************** + +#define SCR_RESERVED (3 << 4) +#define SCR_RW (1 << 10) +#define SCR_NS (1 << 0) +#define SCR_VALUE (SCR_RESERVED | SCR_RW | SCR_NS) + +// *************************************** +// SPSR_EL3, Saved Program Status Register (EL3) Page 389 of AArch64-Reference-Manual. +// *************************************** + +#define SPSR_MASK_ALL (7 << 6) +#define SPSR_EL1h (5 << 0) +#define SPSR_VALUE (SPSR_MASK_ALL | SPSR_EL1h) + +#endif diff --git a/part14-ethernet/include/fb.h b/part14-ethernet/include/fb.h new file mode 100644 index 0000000..8beda43 --- /dev/null +++ b/part14-ethernet/include/fb.h @@ -0,0 +1,13 @@ +void fb_init(); +void drawPixel(int x, int y, unsigned char attr); +void drawChar(unsigned char ch, int x, int y, unsigned char attr, int zoom); +void drawString(int x, int y, char *s, unsigned char attr, int zoom); +void drawRect(int x1, int y1, int x2, int y2, unsigned char attr, int fill); +void drawCircle(int x0, int y0, int radius, unsigned char attr, int fill); +void drawLine(int x1, int y1, int x2, int y2, unsigned char attr); +void moveRect(int oldx, int oldy, int width, int height, int shiftx, int shifty, unsigned char attr); +void wait_msec(unsigned int n); +void debugstr(char *str); +void debugcrlf(void); +void debugch(unsigned char b); +void debughex(unsigned int d); diff --git a/part14-ethernet/include/io.h b/part14-ethernet/include/io.h new file mode 100644 index 0000000..ff2fa98 --- /dev/null +++ b/part14-ethernet/include/io.h @@ -0,0 +1,17 @@ +#define PERIPHERAL_BASE 0xFE000000 +#define LEGACY_BASE 0x7E000000 +#define SAFE_ADDRESS 0x00400000 // Somewhere safe to store a lot of data + +void uart_init(); +void uart_writeText(char *buffer); +void uart_loadOutputFifo(); +unsigned char uart_readByte(); +unsigned int uart_isReadByteReady(); +void uart_writeByteBlockingActual(unsigned char ch); +void uart_update(); +void mmio_write(long reg, unsigned int val); +unsigned int mmio_read(long reg); +void gpio_useAsAlt0(unsigned int pin_number); +void gpio_useAsAlt3(unsigned int pin_number); +void uart_hex(unsigned int d); +void uart_byte(unsigned char b); diff --git a/part14-ethernet/include/mb.h b/part14-ethernet/include/mb.h new file mode 100644 index 0000000..0e1861b --- /dev/null +++ b/part14-ethernet/include/mb.h @@ -0,0 +1,34 @@ +extern volatile unsigned int mbox[36]; + +enum { + MBOX_REQUEST = 0 +}; + +enum { + MBOX_CH_POWER = 0, + MBOX_CH_FB = 1, + MBOX_CH_VUART = 2, + MBOX_CH_VCHIQ = 3, + MBOX_CH_LEDS = 4, + MBOX_CH_BTNS = 5, + MBOX_CH_TOUCH = 6, + MBOX_CH_COUNT = 7, + MBOX_CH_PROP = 8 // Request from ARM for response by VideoCore +}; + +enum { + MBOX_TAG_SETPOWER = 0x28001, + MBOX_TAG_SETCLKRATE = 0x38002, + + MBOX_TAG_SETPHYWH = 0x48003, + MBOX_TAG_SETVIRTWH = 0x48004, + MBOX_TAG_SETVIRTOFF = 0x48009, + MBOX_TAG_SETDEPTH = 0x48005, + MBOX_TAG_SETPXLORDR = 0x48006, + MBOX_TAG_GETFB = 0x40001, + MBOX_TAG_GETPITCH = 0x40008, + + MBOX_TAG_LAST = 0 +}; + +unsigned int mbox_call(unsigned char ch); diff --git a/part14-ethernet/include/multicore.h b/part14-ethernet/include/multicore.h new file mode 100644 index 0000000..4c3dc0f --- /dev/null +++ b/part14-ethernet/include/multicore.h @@ -0,0 +1,11 @@ +extern unsigned int spin_cpu0; +extern unsigned int spin_cpu1; +extern unsigned int spin_cpu2; +extern unsigned int spin_cpu3; + +void start_core1(void (*func)(void)); +void start_core2(void (*func)(void)); +void start_core3(void (*func)(void)); +void clear_core1(void); +void clear_core2(void); +void clear_core3(void); diff --git a/part14-ethernet/include/spi.h b/part14-ethernet/include/spi.h new file mode 100644 index 0000000..c5fca8b --- /dev/null +++ b/part14-ethernet/include/spi.h @@ -0,0 +1,6 @@ +void spi_init(); +void spi_send_recv(unsigned char *sbuffer, unsigned char *rbuffer, unsigned int size); +void spi_send(unsigned char *data, unsigned int size); +void spi_recv(unsigned char *data, unsigned int size); +void spi_send_no_selection(unsigned char command); +void spi_chip_select(unsigned char chip_select); diff --git a/part14-ethernet/include/terminal.h b/part14-ethernet/include/terminal.h new file mode 100644 index 0000000..ef05b85 --- /dev/null +++ b/part14-ethernet/include/terminal.h @@ -0,0 +1,253 @@ +unsigned int vgapal[] = { + 0x000000, + 0x0000AA, + 0x00AA00, + 0x00AAAA, + 0xAA0000, + 0xAA00AA, + 0xAA5500, + 0xAAAAAA, + 0x555555, + 0x5555FF, + 0x55FF55, + 0x55FFFF, + 0xFF5555, + 0xFF55FF, + 0xFFFF55, + 0xFFFFFF +}; + +enum { + FONT_WIDTH = 8, + FONT_HEIGHT = 8, + FONT_BPG = 8, // Bytes per glyph + FONT_BPL = 1, // Bytes per line + FONT_NUMGLYPHS = 224 +}; + +unsigned char font[FONT_NUMGLYPHS][FONT_BPG] = { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0000 (nul) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0001 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0002 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0003 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0004 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0005 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0006 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0007 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0008 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0009 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000A + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000B + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000C + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000D + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000E + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000F + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0010 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0011 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0012 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0013 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0014 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0015 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0016 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0017 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0018 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0019 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001A + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001B + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001C + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001D + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001E + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001F + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0020 (space) + { 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00}, // U+0021 (!) + { 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0022 (") + { 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, // U+0023 (#) + { 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00}, // U+0024 ($) + { 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00}, // U+0025 (%) + { 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00}, // U+0026 (&) + { 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0027 (') + { 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00}, // U+0028 (() + { 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00}, // U+0029 ()) + { 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, // U+002A (*) + { 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00}, // U+002B (+) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+002C (,) + { 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // U+002D (-) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+002E (.) + { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // U+002F (/) + { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0) + { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1) + { 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2) + { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3) + { 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4) + { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5) + { 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6) + { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7) + { 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8) + { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // U+0039 (9) + { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+003A (:) + { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+003B (;) + { 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00}, // U+003C (<) + { 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00}, // U+003D (=) + { 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00}, // U+003E (>) + { 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00}, // U+003F (?) + { 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00}, // U+0040 (@) + { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0041 (A) + { 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0042 (B) + { 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // U+0043 (C) + { 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // U+0044 (D) + { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0045 (E) + { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // U+0046 (F) + { 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // U+0047 (G) + { 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0048 (H) + { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0049 (I) + { 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // U+004A (J) + { 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+004B (K) + { 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // U+004C (L) + { 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+004D (M) + { 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+004E (N) + { 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+004F (O) + { 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+0050 (P) + { 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // U+0051 (Q) + { 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // U+0052 (R) + { 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // U+0053 (S) + { 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0054 (T) + { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U+0055 (U) + { 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0056 (V) + { 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // U+0057 (W) + { 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+0058 (X) + { 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+0059 (Y) + { 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+005A (Z) + { 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00}, // U+005B ([) + { 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00}, // U+005C (\) + { 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00}, // U+005D (]) + { 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00}, // U+005E (^) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, // U+005F (_) + { 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0060 (`) + { 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // U+0061 (a) + { 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // U+0062 (b) + { 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // U+0063 (c) + { 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00}, // U+0064 (d) + { 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00}, // U+0065 (e) + { 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00}, // U+0066 (f) + { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0067 (g) + { 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // U+0068 (h) + { 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0069 (i) + { 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // U+006A (j) + { 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // U+006B (k) + { 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+006C (l) + { 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // U+006D (m) + { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n) + { 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+006F (o) + { 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+0070 (p) + { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // U+0071 (q) + { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r) + { 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // U+0073 (s) + { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // U+0075 (u) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0076 (v) + { 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // U+0077 (w) + { 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // U+0078 (x) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y) + { 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // U+007A (z) + { 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00}, // U+007B ({) + { 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, // U+007C (|) + { 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00}, // U+007D (}) + { 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007E (~) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007F + { 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x18, 0x30, 0x1E}, // U+00C7 (C cedille) + { 0x00, 0x33, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00FC (u umlaut) + { 0x38, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00}, // U+00E9 (e aigu) + { 0x7E, 0xC3, 0x3C, 0x60, 0x7C, 0x66, 0xFC, 0x00}, // U+00E2 (a circumflex) + { 0x33, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E4 (a umlaut) + { 0x07, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E0 (a grave) + { 0x0C, 0x0C, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E5 (a ring) + { 0x00, 0x00, 0x1E, 0x03, 0x03, 0x1E, 0x30, 0x1C}, // U+00E7 (c cedille) + { 0x7E, 0xC3, 0x3C, 0x66, 0x7E, 0x06, 0x3C, 0x00}, // U+00EA (e circumflex) + { 0x33, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00}, // U+00EB (e umlaut) + { 0x07, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00}, // U+00E8 (e grave) + { 0x33, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00EF (i umlaut) + { 0x3E, 0x63, 0x1C, 0x18, 0x18, 0x18, 0x3C, 0x00}, // U+00EE (i circumflex) + { 0x07, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00EC (i grave) + { 0x63, 0x1C, 0x36, 0x63, 0x7F, 0x63, 0x63, 0x00}, // U+00C4 (A umlaut) + { 0x0C, 0x0C, 0x00, 0x1E, 0x33, 0x3F, 0x33, 0x00}, // U+00C5 (A ring) + { 0x07, 0x00, 0x3F, 0x06, 0x1E, 0x06, 0x3F, 0x00}, // U+00C8 (E grave) + { 0x00, 0x00, 0xFE, 0x30, 0xFE, 0x33, 0xFE, 0x00}, // U+00E6 (ae) + { 0x7C, 0x36, 0x33, 0x7F, 0x33, 0x33, 0x73, 0x00}, // U+00C6 (AE) + { 0x1E, 0x33, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F4 (o circumflex) + { 0x00, 0x33, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F6 (o umlaut) + { 0x00, 0x07, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F2 (o grave) + { 0x1E, 0x33, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00FB (u circumflex) + { 0x00, 0x07, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00F9 (u grave) + { 0x00, 0x33, 0x00, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+00FF (y umlaut) + { 0xC3, 0x18, 0x3C, 0x66, 0x66, 0x3C, 0x18, 0x00}, // U+00D6 (O umlaut) + { 0x33, 0x00, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+00DC (U umlaut) + { 0x18, 0x18, 0x7E, 0x03, 0x03, 0x7E, 0x18, 0x18}, // U+00A2 (dollarcents) + { 0x1C, 0x36, 0x26, 0x0F, 0x06, 0x67, 0x3F, 0x00}, // U+00A3 (pound sterling) + { 0x33, 0x33, 0x1E, 0x3F, 0x0C, 0x3F, 0x0C, 0x0C}, // U+00A5 (yen) + { 0x7C, 0xC6, 0x1C, 0x36, 0x36, 0x1C, 0x33, 0x1E}, // U+00A7 (paragraph) + { 0x70, 0xD8, 0x18, 0x3C, 0x18, 0x18, 0x1B, 0x0E}, // U+0192 (dutch florijn) + { 0x38, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E1 (a aigu) + { 0x1C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00ED (i augu) + { 0x00, 0x38, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F3 (o aigu) + { 0x00, 0x38, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00FA (u aigu) + { 0x00, 0x1F, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x00}, // U+00F1 (n ~) + { 0x3F, 0x00, 0x33, 0x37, 0x3F, 0x3B, 0x33, 0x00}, // U+00D1 (N ~) + { 0x3C, 0x36, 0x36, 0x7C, 0x00, 0x00, 0x00, 0x00}, // U+00AA (superscript a) + { 0x1C, 0x36, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00}, // U+00BA (superscript 0) + { 0x0C, 0x00, 0x0C, 0x06, 0x03, 0x33, 0x1E, 0x00}, // U+00BF (inverted ?) + { 0x00, 0x00, 0x00, 0x3F, 0x03, 0x03, 0x00, 0x00}, // U+2310 (gun pointing right) + { 0x00, 0x00, 0x00, 0x3F, 0x30, 0x30, 0x00, 0x00}, // U+00AC (gun pointing left) + { 0xC3, 0x63, 0x33, 0x7B, 0xCC, 0x66, 0x33, 0xF0}, // U+00BD (1/2) + { 0xC3, 0x63, 0x33, 0xBD, 0xEC, 0xF6, 0xF3, 0x03}, // U+00BC (1/4) + { 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00}, // U+00A1 (inverted !) + { 0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00}, // U+00AB (<<) + { 0x00, 0x33, 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00}, // U+00BB (>>) + { 0x55, 0x00, 0xAA, 0x00, 0x55, 0x00, 0xAA, 0x00}, // U+2591 (25% solid) + { 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA}, // U+2592 (50% solid) + { 0xFF, 0xAA, 0xFF, 0x55, 0xFF, 0xAA, 0xFF, 0x55}, // U+2593 (75% solid) + { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}, // U+2502 (thin vertical) + { 0x08, 0x08, 0x08, 0x08, 0x0f, 0x08, 0x08, 0x08}, // U+2524 (down L, left L, up L) + { 0x08, 0x08, 0x08, 0x0F, 0x08, 0x0F, 0x08, 0x08}, // U+2561 (up L, down L, left D) + { 0x14, 0x14, 0x14, 0x14, 0x17, 0x14, 0x14, 0x14}, // U+2562 (up D, down D, left L) + { 0x00, 0x00, 0x00, 0x00, 0x1F, 0x14, 0x14, 0x14}, // U+2556 (down D, left L) + { 0x00, 0x00, 0x00, 0x0F, 0x08, 0x0F, 0x08, 0x08}, // U+2555 (down L, left D) + { 0x14, 0x14, 0x14, 0x17, 0x10, 0x17, 0x14, 0x14}, // U+2563 (up D, down D, left D) + { 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14}, // U+2551 (double vertical) + { 0x00, 0x00, 0x00, 0x1F, 0x10, 0x17, 0x14, 0x14}, // U+2557 (down D, left D) + { 0x14, 0x14, 0x14, 0x17, 0x10, 0x1F, 0x00, 0x00}, // U+255D (up D, left D) + { 0x14, 0x14, 0x14, 0x14, 0x1F, 0x00, 0x00, 0x00}, // U+255C (up D, left L) + { 0x08, 0x08, 0x08, 0x0F, 0x08, 0x0F, 0x00, 0x00}, // U+255B (up L, left D) + { 0x00, 0x00, 0x00, 0x00, 0x0f, 0x08, 0x08, 0x08}, // U+2510 (down L, left L) + { 0x08, 0x08, 0x08, 0x08, 0xf8, 0x00, 0x00, 0x00}, // U+2514 (up L, right L) + { 0x08, 0x08, 0x08, 0x08, 0xff, 0x00, 0x00, 0x00}, // U+2534 (up L, right L, left L) + { 0x00, 0x00, 0x00, 0x00, 0xff, 0x08, 0x08, 0x08}, // U+252C (down L, right L, left L) + { 0x08, 0x08, 0x08, 0x08, 0xf8, 0x08, 0x08, 0x08}, // U+251C (down L, right L, up L) + { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00}, // U+2500 (thin horizontal) + { 0x08, 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08}, // U+253C (up L, right L, left L, down L) + { 0x08, 0x08, 0x08, 0xF8, 0x08, 0xF8, 0x08, 0x08}, // U+255E (up L, down L, right D) + { 0x14, 0x14, 0x14, 0x14, 0xF4, 0x14, 0x14, 0x14}, // U+255F (up D, down D, right L) + { 0x14, 0x14, 0x14, 0xF4, 0x04, 0xFC, 0x00, 0x00}, // U+255A (up D, right D) + { 0x00, 0x00, 0x00, 0xFC, 0x04, 0xF4, 0x14, 0x14}, // U+2554 (down D, right D) + { 0x14, 0x14, 0x14, 0xF7, 0x00, 0xFF, 0x00, 0x00}, // U+2569 (left D, right D, up D) + { 0x00, 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x14, 0x14}, // U+2566 (left D, right D, down D) + { 0x14, 0x14, 0x14, 0xF4, 0x04, 0xF4, 0x14, 0x14}, // U+2560 (up D, down D, right D) + { 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00}, // U+2550 (double horizontal) + { 0x14, 0x14, 0x14, 0xF7, 0x00, 0xF7, 0x14, 0x14}, // U+256C (left D, right D, down D, up D) + { 0x08, 0x08, 0x08, 0xFF, 0x00, 0xFF, 0x00, 0x00}, // U+2567 (left D, right D, up L) + { 0x14, 0x14, 0x14, 0x14, 0xFF, 0x00, 0x00, 0x00}, // U+2568 (left L, right L, up D) + { 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x08, 0x08}, // U+2564 (left D, right D, down L) + { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x14, 0x14, 0x14}, // U+2565 (left L, right L, down D) + { 0x14, 0x14, 0x14, 0x14, 0xFC, 0x00, 0x00, 0x00}, // U+2559 (up D, right L) + { 0x08, 0x08, 0x08, 0xF8, 0x08, 0xF8, 0x00, 0x00}, // U+2558 (up L, right D) + { 0x00, 0x00, 0x00, 0xF8, 0x08, 0xF8, 0x08, 0x08}, // U+2552 (down L, right D) + { 0x00, 0x00, 0x00, 0x00, 0xFC, 0x14, 0x14, 0x14}, // U+2553 (down D, right L) + { 0x14, 0x14, 0x14, 0x14, 0xFF, 0x14, 0x14, 0x14}, // U+256B (left L, right L, down D, up D) + { 0x08, 0x08, 0x08, 0xFF, 0x08, 0xFF, 0x08, 0x08}, // U+256A (left D, right D, down L, up L) + { 0x08, 0x08, 0x08, 0x08, 0x0f, 0x00, 0x00, 0x00}, // U+2518 (up L, left L) + { 0x00, 0x00, 0x00, 0x00, 0xf8, 0x08, 0x08, 0x08}, // U+250C (down L, right L) + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // U+2588 (solid) + { 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}, // U+2584 (bottom half) + { 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F}, // U+258C (left half) + { 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0}, // U+2590 (right half) + { 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, // U+2580 (top half) +}; diff --git a/part14-ethernet/kernel/irq.c b/part14-ethernet/kernel/irq.c new file mode 100644 index 0000000..3ab82d1 --- /dev/null +++ b/part14-ethernet/kernel/irq.c @@ -0,0 +1,27 @@ +#include "kernel.h" + +void enable_interrupt_controller() { + REGS_IRQ->irq0_enable_0 = SYS_TIMER_IRQ_1 | SYS_TIMER_IRQ_3; +} + +void disable_interrupt_controller() { + REGS_IRQ->irq0_enable_0 = 0; +} + +void handle_irq() { + unsigned int irq = REGS_IRQ->irq0_pending_0; + + while(irq) { + if (irq & SYS_TIMER_IRQ_1) { + irq &= ~SYS_TIMER_IRQ_1; + + handle_timer_1(); + } + + if (irq & SYS_TIMER_IRQ_3) { + irq &= ~SYS_TIMER_IRQ_3; + + handle_timer_3(); + } + } +} diff --git a/part14-ethernet/kernel/irqentry.S b/part14-ethernet/kernel/irqentry.S new file mode 100644 index 0000000..d24ae00 --- /dev/null +++ b/part14-ethernet/kernel/irqentry.S @@ -0,0 +1,160 @@ +#define SYNC_INVALID_EL1t 0 +#define IRQ_INVALID_EL1t 1 +#define FIQ_INVALID_EL1t 2 +#define ERROR_INVALID_EL1t 3 + +#define SYNC_INVALID_EL1h 4 +#define IRQ_INVALID_EL1h 5 +#define FIQ_INVALID_EL1h 6 +#define ERROR_INVALID_EL1h 7 + +#define SYNC_INVALID_EL0_64 8 +#define IRQ_INVALID_EL0_64 9 +#define FIQ_INVALID_EL0_64 10 +#define ERROR_INVALID_EL0_64 11 + +#define SYNC_INVALID_EL0_32 12 +#define IRQ_INVALID_EL0_32 13 +#define FIQ_INVALID_EL0_32 14 +#define ERROR_INVALID_EL0_32 15 + +//stack frame size +#define S_FRAME_SIZE 256 + +.macro kernel_entry + sub sp, sp, #S_FRAME_SIZE + stp x0, x1, [sp, #16 * 0] + stp x2, x3, [sp, #16 * 1] + stp x4, x5, [sp, #16 * 2] + stp x6, x7, [sp, #16 * 3] + stp x8, x9, [sp, #16 * 4] + stp x10, x11, [sp, #16 * 5] + stp x12, x13, [sp, #16 * 6] + stp x14, x15, [sp, #16 * 7] + stp x16, x17, [sp, #16 * 8] + stp x18, x19, [sp, #16 * 9] + stp x20, x21, [sp, #16 * 10] + stp x22, x23, [sp, #16 * 11] + stp x24, x25, [sp, #16 * 12] + stp x26, x27, [sp, #16 * 13] + stp x28, x29, [sp, #16 * 14] + str x30, [sp, #16 * 15] +.endm + +.macro kernel_exit + ldp x0, x1, [sp, #16 * 0] + ldp x2, x3, [sp, #16 * 1] + ldp x4, x5, [sp, #16 * 2] + ldp x6, x7, [sp, #16 * 3] + ldp x8, x9, [sp, #16 * 4] + ldp x10, x11, [sp, #16 * 5] + ldp x12, x13, [sp, #16 * 6] + ldp x14, x15, [sp, #16 * 7] + ldp x16, x17, [sp, #16 * 8] + ldp x18, x19, [sp, #16 * 9] + ldp x20, x21, [sp, #16 * 10] + ldp x22, x23, [sp, #16 * 11] + ldp x24, x25, [sp, #16 * 12] + ldp x26, x27, [sp, #16 * 13] + ldp x28, x29, [sp, #16 * 14] + ldr x30, [sp, #16 * 15] + add sp, sp, #S_FRAME_SIZE + eret +.endm + +.macro handle_invalid_entry type + kernel_entry + mov x0, #\type + mrs x1, esr_el1 + mrs x2, elr_el1 + + // We could pass this to a function to print an error here + // e.g. bl show_invalid_entry_message + // + // For now we'll just hang + + b err_hang +.endm + +.macro ventry label +.align 7 + b \label +.endm + +//Exception vectors table +.align 11 +.globl vectors +vectors: + ventry sync_invalid_el1t // Synchronous EL1t + ventry irq_invalid_el1t // IRQ EL1t + ventry fiq_invalid_el1t // FIQ EL1t + ventry error_invalid_el1t // Error EL1t + + ventry sync_invalid_el1h // Synchronous EL1h + ventry handle_el1_irq // IRQ EL1h + ventry fiq_invalid_el1h // FIQ EL1h + ventry error_invalid_el1h // Error EL1h + + ventry sync_invalid_el0_64 // Synchronous 64-bit EL0 + ventry irq_invalid_el0_64 // IRQ 64-bit EL0 + ventry fiq_invalid_el0_64 // FIQ 64-bit EL0 + ventry error_invalid_el0_64 // Error 64-bit EL0 + + ventry sync_invalid_el0_32 // Synchronous 32-bit EL0 + ventry irq_invalid_el0_32 // IRQ 32-bit EL0 + ventry fiq_invalid_el0_32 // FIQ 32-bit EL0 + ventry error_invalid_el0_32 // Error 32-bit EL0 + + +sync_invalid_el1t: + handle_invalid_entry SYNC_INVALID_EL1t + +irq_invalid_el1t: + handle_invalid_entry IRQ_INVALID_EL1t + +fiq_invalid_el1t: + handle_invalid_entry FIQ_INVALID_EL1t + +error_invalid_el1t: + handle_invalid_entry ERROR_INVALID_EL1t + +sync_invalid_el1h: + handle_invalid_entry SYNC_INVALID_EL1h + +fiq_invalid_el1h: + handle_invalid_entry FIQ_INVALID_EL1h + +error_invalid_el1h: + handle_invalid_entry ERROR_INVALID_EL1h + +sync_invalid_el0_64: + handle_invalid_entry SYNC_INVALID_EL0_64 + +irq_invalid_el0_64: + handle_invalid_entry IRQ_INVALID_EL0_64 + +fiq_invalid_el0_64: + handle_invalid_entry FIQ_INVALID_EL0_64 + +error_invalid_el0_64: + handle_invalid_entry ERROR_INVALID_EL0_64 + +sync_invalid_el0_32: + handle_invalid_entry SYNC_INVALID_EL0_32 + +irq_invalid_el0_32: + handle_invalid_entry IRQ_INVALID_EL0_32 + +fiq_invalid_el0_32: + handle_invalid_entry FIQ_INVALID_EL0_32 + +error_invalid_el0_32: + handle_invalid_entry ERROR_INVALID_EL0_32 + +handle_el1_irq: + kernel_entry + bl handle_irq + kernel_exit + +.globl err_hang +err_hang: b err_hang diff --git a/part14-ethernet/kernel/kernel.c b/part14-ethernet/kernel/kernel.c new file mode 100644 index 0000000..89bfe31 --- /dev/null +++ b/part14-ethernet/kernel/kernel.c @@ -0,0 +1,191 @@ +#include "../include/fb.h" +#include "../include/io.h" +#include "../include/spi.h" +#include "../include/multicore.h" +#include "../net/enc28j60.h" +#include "kernel.h" + +void initProgress(void) +{ + drawRect(0, 0, 301, 50, 0x0f, 0); + drawString(309, 21, "Core 0", 0x0f, 1); + + drawRect(0, 60, 301, 110, 0x0f, 0); + drawString(309, 81, "Core 1", 0x0f, 1); + + drawRect(0, 120, 301, 170, 0x0f, 0); + drawString(309, 141, "Timer 1", 0x0f, 1); + + drawRect(0, 180, 301, 230, 0x0f, 0); + drawString(309, 201, "Timer 3", 0x0f, 1); +} + +void drawProgress(unsigned int core, unsigned int val) { + unsigned char col = (core + 1) + ((core + 1) << 4); + + // val should be 0-100 + if (val == 0) drawRect(1, (60 * core) + 1, 300, (60 * core) + 49, 0x00, 1); + if (val > 0) drawRect(1, (60 * core) + 1, (val * 3), (60 * core) + 49, col, 1); +} + +void core0_main(void) +{ + unsigned int core0_val = 0; + + while (core0_val <= 100) { + wait_msec(0x100000); + drawProgress(0, core0_val); + core0_val++; + } + + debugstr("Core 0 done."); + debugcrlf(); + + irq_disable(); + disable_interrupt_controller(); + + while(1); +} + +void core1_main(void) +{ + unsigned int core1_val = 0; + + clear_core1(); // Only run once + + while (core1_val <= 100) { + wait_msec(0x3FFFF); + drawProgress(1, core1_val); + core1_val++; + } + + debugstr("Core 1 done."); + debugcrlf(); + + while(1); +} + +// TIMER FUNCTIONS + +const unsigned int timer1_int = CLOCKHZ; +const unsigned int timer3_int = CLOCKHZ / 4; +unsigned int timer1_val = 0; +unsigned int timer3_val = 0; + +void timer_init() { + timer1_val = REGS_TIMER->counter_lo; + timer1_val += timer1_int; + REGS_TIMER->compare[1] = timer1_val; + + timer3_val = REGS_TIMER->counter_lo; + timer3_val += timer3_int; + REGS_TIMER->compare[3] = timer3_val; +} + +void handle_timer_1() { + timer1_val += timer1_int; + REGS_TIMER->compare[1] = timer1_val; + REGS_TIMER->control_status |= SYS_TIMER_IRQ_1; + + unsigned int progval = timer1_val / timer1_int; + if (progval <= 100) { + drawProgress(2, progval); + } else { + debugstr("Timer 1 done."); + debugcrlf(); + } +} + +void handle_timer_3() { + timer3_val += timer3_int; + REGS_TIMER->compare[3] = timer3_val; + REGS_TIMER->control_status |= SYS_TIMER_IRQ_3; + + unsigned int progval = timer3_val / timer3_int; + if (progval <= 100) drawProgress(3, progval); +} + +unsigned long timer_get_ticks() { + unsigned int hi = REGS_TIMER->counter_hi; + unsigned int lo = REGS_TIMER->counter_lo; + + //double check hi value didn't change after setting it... + if (hi != REGS_TIMER->counter_hi) { + hi = REGS_TIMER->counter_hi; + lo = REGS_TIMER->counter_lo; + } + + return ((unsigned long)hi << 32) | lo; +} + +void timer_sleep(unsigned int ms) { + unsigned long start = timer_get_ticks(); + + while(timer_get_ticks() < start + (ms * 1000)); +} + +void HAL_Delay(volatile unsigned int Delay) { + timer_sleep(Delay); +} + +unsigned int HAL_GetTick(void) { + return timer_get_ticks(); +} + +void init_network(void) +{ + ENC_HandleTypeDef handle; + + unsigned char macaddr[6]; + macaddr[0] = 0xc0; + macaddr[1] = 0xff; + macaddr[2] = 0xee; + macaddr[3] = 0xc0; + macaddr[4] = 0xff; + macaddr[5] = 0xee; + + debugstr("Setting MAC address to C0:FF:EE:C0:FF:EE."); + debugcrlf(); + + handle.Init.DuplexMode = ETH_MODE_FULLDUPLEX; + handle.Init.MACAddr = macaddr; + handle.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE; + handle.Init.InterruptEnableBits = 0; + + debugstr("Starting network up."); + debugcrlf(); + + if (!ENC_Start(&handle)) { + debugstr("Could not initialise network card."); + } else { + debugstr("Network successfully card initialised."); + } + debugcrlf(); +} + +void main(void) +{ + fb_init(); + + unsigned int i=0; + while (i++<30) debugcrlf(); + + initProgress(); + + // Kick off the timers + + irq_init_vectors(); + enable_interrupt_controller(); + irq_enable(); + timer_init(); + spi_init(); + init_network(); + + // Kick it off on core 1 + + start_core1(core1_main); + + // Loop endlessly + + core0_main(); +} diff --git a/part14-ethernet/kernel/kernel.h b/part14-ethernet/kernel/kernel.h new file mode 100644 index 0000000..5ff14d5 --- /dev/null +++ b/part14-ethernet/kernel/kernel.h @@ -0,0 +1,51 @@ +#define PERIPHERAL_BASE 0xFE000000 +#define CLOCKHZ 1000000 + +struct timer_regs { + volatile unsigned int control_status; + volatile unsigned int counter_lo; + volatile unsigned int counter_hi; + volatile unsigned int compare[4]; +}; + +#define REGS_TIMER ((struct timer_regs *)(PERIPHERAL_BASE + 0x00003000)) + +struct arm_irq_regs_2711 { + volatile unsigned int irq0_pending_0; + volatile unsigned int irq0_pending_1; + volatile unsigned int irq0_pending_2; + volatile unsigned int res0; + volatile unsigned int irq0_enable_0; + volatile unsigned int irq0_enable_1; + volatile unsigned int irq0_enable_2; + volatile unsigned int res1; + volatile unsigned int irq0_disable_0; + volatile unsigned int irq0_disable_1; + volatile unsigned int irq0_disable_2; +}; + +typedef struct arm_irq_regs_2711 arm_irq_regs; + +#define REGS_IRQ ((arm_irq_regs *)(PERIPHERAL_BASE + 0x0000B200)) + +enum vc_irqs { + SYS_TIMER_IRQ_0 = 1, + SYS_TIMER_IRQ_1 = 2, + SYS_TIMER_IRQ_2 = 4, + SYS_TIMER_IRQ_3 = 8, + AUX_IRQ = (1 << 29) +}; + +void irq_init_vectors(); +void irq_enable(); +void irq_disable(); +void enable_interrupt_controller(); +void disable_interrupt_controller(); + +void handle_timer_1(); +void handle_timer_3(); + +unsigned long timer_get_ticks(); +void timer_sleep(unsigned int ms); +void HAL_Delay(volatile unsigned int Delay); +unsigned int HAL_GetTick(void); diff --git a/part14-ethernet/kernel/utils.S b/part14-ethernet/kernel/utils.S new file mode 100644 index 0000000..6027c0d --- /dev/null +++ b/part14-ethernet/kernel/utils.S @@ -0,0 +1,15 @@ +.globl irq_init_vectors +irq_init_vectors: + adr x0, vectors + msr vbar_el1, x0 + ret + +.globl irq_enable +irq_enable: + msr daifclr, #2 + ret + +.globl irq_disable +irq_disable: + msr daifset, #2 + ret diff --git a/part14-ethernet/lib/fb.c b/part14-ethernet/lib/fb.c new file mode 100644 index 0000000..80ef496 --- /dev/null +++ b/part14-ethernet/lib/fb.c @@ -0,0 +1,260 @@ +#include "../include/io.h" +#include "../include/mb.h" +#include "../include/terminal.h" + +unsigned int width, height, pitch, isrgb; +unsigned char *fb; + +int curx = 0; +int cury = 0; + +void fb_init() +{ + mbox[0] = 35*4; // Length of message in bytes + mbox[1] = MBOX_REQUEST; + + mbox[2] = MBOX_TAG_SETPHYWH; // Tag identifier + mbox[3] = 8; // Value size in bytes + mbox[4] = 8; // Value size in bytes (again!) + mbox[5] = 1920; // Value(width) + mbox[6] = 1080; // Value(height) + + mbox[7] = MBOX_TAG_SETVIRTWH; + mbox[8] = 8; + mbox[9] = 8; + mbox[10] = 1920; + mbox[11] = 1080; + + mbox[12] = MBOX_TAG_SETVIRTOFF; + mbox[13] = 8; + mbox[14] = 8; + mbox[15] = 0; // Value(x) + mbox[16] = 0; // Value(y) + + mbox[17] = MBOX_TAG_SETDEPTH; + mbox[18] = 4; + mbox[19] = 4; + mbox[20] = 32; // Bits per pixel + + mbox[21] = MBOX_TAG_SETPXLORDR; + mbox[22] = 4; + mbox[23] = 4; + mbox[24] = 1; // RGB + + mbox[25] = MBOX_TAG_GETFB; + mbox[26] = 8; + mbox[27] = 8; + mbox[28] = 4096; // FrameBufferInfo.pointer + mbox[29] = 0; // FrameBufferInfo.size + + mbox[30] = MBOX_TAG_GETPITCH; + mbox[31] = 4; + mbox[32] = 4; + mbox[33] = 0; // Bytes per line + + mbox[34] = MBOX_TAG_LAST; + + // Check call is successful and we have a pointer with depth 32 + if (mbox_call(MBOX_CH_PROP) && mbox[20] == 32 && mbox[28] != 0) { + mbox[28] &= 0x3FFFFFFF; // Convert GPU address to ARM address + width = mbox[10]; // Actual physical width + height = mbox[11]; // Actual physical height + pitch = mbox[33]; // Number of bytes per line + isrgb = mbox[24]; // Pixel order + fb = (unsigned char *)((long)mbox[28]); + } +} + +void drawPixel(int x, int y, unsigned char attr) +{ + int offs = (y * pitch) + (x * 4); + *((unsigned int*)(fb + offs)) = vgapal[attr & 0x0f]; +} + +void drawRect(int x1, int y1, int x2, int y2, unsigned char attr, int fill) +{ + int y=y1; + + while (y <= y2) { + int x=x1; + while (x <= x2) { + if ((x == x1 || x == x2) || (y == y1 || y == y2)) drawPixel(x, y, attr); + else if (fill) drawPixel(x, y, (attr & 0xf0) >> 4); + x++; + } + y++; + } +} + +void drawLine(int x1, int y1, int x2, int y2, unsigned char attr) +{ + int dx, dy, p, x, y; + + dx = x2-x1; + dy = y2-y1; + x = x1; + y = y1; + p = 2*dy-dx; + + while (x= 0) { + drawPixel(x,y,attr); + y++; + p = p+2*dy-2*dx; + } else { + drawPixel(x,y,attr); + p = p+2*dy; + } + x++; + } +} + +void drawCircle(int x0, int y0, int radius, unsigned char attr, int fill) +{ + int x = radius; + int y = 0; + int err = 0; + + while (x >= y) { + if (fill) { + drawLine(x0 - y, y0 + x, x0 + y, y0 + x, (attr & 0xf0) >> 4); + drawLine(x0 - x, y0 + y, x0 + x, y0 + y, (attr & 0xf0) >> 4); + drawLine(x0 - x, y0 - y, x0 + x, y0 - y, (attr & 0xf0) >> 4); + drawLine(x0 - y, y0 - x, x0 + y, y0 - x, (attr & 0xf0) >> 4); + } + drawPixel(x0 - y, y0 + x, attr); + drawPixel(x0 + y, y0 + x, attr); + drawPixel(x0 - x, y0 + y, attr); + drawPixel(x0 + x, y0 + y, attr); + drawPixel(x0 - x, y0 - y, attr); + drawPixel(x0 + x, y0 - y, attr); + drawPixel(x0 - y, y0 - x, attr); + drawPixel(x0 + y, y0 - x, attr); + + if (err <= 0) { + y += 1; + err += 2*y + 1; + } + + if (err > 0) { + x -= 1; + err -= 2*x + 1; + } + } +} + +void drawChar(unsigned char ch, int x, int y, unsigned char attr, int zoom) +{ + unsigned char *glyph = (unsigned char *)&font + (ch < FONT_NUMGLYPHS ? ch : 0) * FONT_BPG; + + for (int i=1;i<=(FONT_HEIGHT*zoom);i++) { + for (int j=0;j<(FONT_WIDTH*zoom);j++) { + unsigned char mask = 1 << (j/zoom); + unsigned char col = (*glyph & mask) ? attr & 0x0f : (attr & 0xf0) >> 4; + + drawPixel(x+j, y+i, col); + } + glyph += (i%zoom) ? 0 : FONT_BPL; + } +} + +void drawString(int x, int y, char *s, unsigned char attr, int zoom) +{ + while (*s) { + if (*s == '\r') { + x = 0; + } else if(*s == '\n') { + x = 0; y += (FONT_HEIGHT*zoom); + } else { + drawChar(*s, x, y, attr, zoom); + x += (FONT_WIDTH*zoom); + } + s++; + } +} + +void moveRect(int oldx, int oldy, int width, int height, int shiftx, int shifty, unsigned char attr) +{ + unsigned int newx = oldx + shiftx, newy = oldy + shifty; + unsigned int xcount = 0, ycount = 0; + unsigned int bitmap[width][height]; // This is very unsafe if it's too big for the stack... + unsigned int offs; + + // Save the bitmap + while (xcount < width) { + while (ycount < height) { + offs = ((oldy + ycount) * pitch) + ((oldx + xcount) * 4); + + bitmap[xcount][ycount] = *((unsigned int*)(fb + offs)); + ycount++; + } + ycount=0; + xcount++; + } + // Wipe it out with background colour + drawRect(oldx, oldy, oldx + width, oldy + width, attr, 1); + // Draw it again + for (int i=newx;i= 1920) { + curx = 0; cury += 8; + } + if (cury + 8 >= 1080) { + cury = 0; + } + drawString(curx, cury, str, 0x0f, 1); + curx += (strlen(str) * 8); +} + +void debugcrlf(void) { + curx = 0; cury += 8; +} + +void debugch(unsigned char b) { + unsigned int n; + int c; + for(c=4;c>=0;c-=4) { + n=(b>>c)&0xF; + n+=n>9?0x37:0x30; + debugstr((char *)&n); + } + debugstr(" "); +} + +void debughex(unsigned int d) { + unsigned int n; + int c; + for(c=28;c>=0;c-=4) { + n=(d>>c)&0xF; + n+=n>9?0x37:0x30; + debugstr((char *)&n); + } + debugstr(" "); +} diff --git a/part14-ethernet/lib/io.c b/part14-ethernet/lib/io.c new file mode 100644 index 0000000..34a02aa --- /dev/null +++ b/part14-ethernet/lib/io.c @@ -0,0 +1,199 @@ +#include "../include/io.h" + +// GPIO + +enum { + GPFSEL0 = PERIPHERAL_BASE + 0x200000, + GPSET0 = PERIPHERAL_BASE + 0x20001C, + GPCLR0 = PERIPHERAL_BASE + 0x200028, + GPPUPPDN0 = PERIPHERAL_BASE + 0x2000E4 +}; + +enum { + GPIO_MAX_PIN = 53, + GPIO_FUNCTION_OUT = 1, + GPIO_FUNCTION_ALT5 = 2, + GPIO_FUNCTION_ALT3 = 7, + GPIO_FUNCTION_ALT0 = 4 +}; + +enum { + Pull_None = 0, + Pull_Down = 1, // Are down and up the right way around? + Pull_Up = 2 +}; + +void mmio_write(long reg, unsigned int val) { *(volatile unsigned int *)reg = val; } +unsigned int mmio_read(long reg) { return *(volatile unsigned int *)reg; } + +unsigned int gpio_call(unsigned int pin_number, unsigned int value, unsigned int base, unsigned int field_size, unsigned int field_max) { + unsigned int field_mask = (1 << field_size) - 1; + + if (pin_number > field_max) return 0; + if (value > field_mask) return 0; + + unsigned int num_fields = 32 / field_size; + unsigned int reg = base + ((pin_number / num_fields) * 4); + unsigned int shift = (pin_number % num_fields) * field_size; + + unsigned int curval = mmio_read(reg); + curval &= ~(field_mask << shift); + curval |= value << shift; + mmio_write(reg, curval); + + return 1; +} + +unsigned int gpio_set (unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPSET0, 1, GPIO_MAX_PIN); } +unsigned int gpio_clear (unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPCLR0, 1, GPIO_MAX_PIN); } +unsigned int gpio_pull (unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPPUPPDN0, 2, GPIO_MAX_PIN); } +unsigned int gpio_function(unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPFSEL0, 3, GPIO_MAX_PIN); } + +void gpio_useAsAlt0(unsigned int pin_number) { + gpio_pull(pin_number, Pull_None); + gpio_function(pin_number, GPIO_FUNCTION_ALT0); +} + +void gpio_useAsAlt3(unsigned int pin_number) { + gpio_pull(pin_number, Pull_None); + gpio_function(pin_number, GPIO_FUNCTION_ALT3); +} + +void gpio_useAsAlt5(unsigned int pin_number) { + gpio_pull(pin_number, Pull_None); + gpio_function(pin_number, GPIO_FUNCTION_ALT5); +} + +void gpio_initOutputPinWithPullNone(unsigned int pin_number) { + gpio_pull(pin_number, Pull_None); + gpio_function(pin_number, GPIO_FUNCTION_OUT); +} + +void gpio_setPinOutputBool(unsigned int pin_number, unsigned int onOrOff) { + if (onOrOff) { + gpio_set(pin_number, 1); + } else { + gpio_clear(pin_number, 1); + } +} + +// UART + +enum { + AUX_BASE = PERIPHERAL_BASE + 0x215000, + AUX_IRQ = AUX_BASE, + AUX_ENABLES = AUX_BASE + 4, + AUX_MU_IO_REG = AUX_BASE + 64, + AUX_MU_IER_REG = AUX_BASE + 68, + AUX_MU_IIR_REG = AUX_BASE + 72, + AUX_MU_LCR_REG = AUX_BASE + 76, + AUX_MU_MCR_REG = AUX_BASE + 80, + AUX_MU_LSR_REG = AUX_BASE + 84, + AUX_MU_MSR_REG = AUX_BASE + 88, + AUX_MU_SCRATCH = AUX_BASE + 92, + AUX_MU_CNTL_REG = AUX_BASE + 96, + AUX_MU_STAT_REG = AUX_BASE + 100, + AUX_MU_BAUD_REG = AUX_BASE + 104, + AUX_UART_CLOCK = 500000000, + UART_MAX_QUEUE = 16 * 1024 +}; + +#define AUX_MU_BAUD(baud) ((AUX_UART_CLOCK/(baud*8))-1) + +unsigned char uart_output_queue[UART_MAX_QUEUE]; +unsigned int uart_output_queue_write = 0; +unsigned int uart_output_queue_read = 0; + +void uart_init() { + mmio_write(AUX_ENABLES, 1); //enable UART1 + mmio_write(AUX_MU_IER_REG, 0); + mmio_write(AUX_MU_CNTL_REG, 0); + mmio_write(AUX_MU_LCR_REG, 3); //8 bits + mmio_write(AUX_MU_MCR_REG, 0); + mmio_write(AUX_MU_IER_REG, 0); + mmio_write(AUX_MU_IIR_REG, 0xC6); //disable interrupts + mmio_write(AUX_MU_BAUD_REG, AUX_MU_BAUD(115200)); + gpio_useAsAlt5(14); + gpio_useAsAlt5(15); + mmio_write(AUX_MU_CNTL_REG, 3); //enable RX/TX +} + +unsigned int uart_isOutputQueueEmpty() { + return uart_output_queue_read == uart_output_queue_write; +} + +unsigned int uart_isReadByteReady() { return mmio_read(AUX_MU_LSR_REG) & 0x01; } +unsigned int uart_isWriteByteReady() { return mmio_read(AUX_MU_LSR_REG) & 0x20; } + +unsigned char uart_readByte() { + while (!uart_isReadByteReady()); + return (unsigned char)mmio_read(AUX_MU_IO_REG); +} + +void uart_writeByteBlockingActual(unsigned char ch) { + while (!uart_isWriteByteReady()); + mmio_write(AUX_MU_IO_REG, (unsigned int)ch); +} + +void uart_loadOutputFifo() { + while (!uart_isOutputQueueEmpty() && uart_isWriteByteReady()) { + uart_writeByteBlockingActual(uart_output_queue[uart_output_queue_read]); + uart_output_queue_read = (uart_output_queue_read + 1) & (UART_MAX_QUEUE - 1); // Don't overrun + } +} + +void uart_writeByteBlocking(unsigned char ch) { + unsigned int next = (uart_output_queue_write + 1) & (UART_MAX_QUEUE - 1); // Don't overrun + + while (next == uart_output_queue_read) uart_loadOutputFifo(); + + uart_output_queue[uart_output_queue_write] = ch; + uart_output_queue_write = next; +} + +void uart_writeText(char *buffer) { + while (*buffer) { + if (*buffer == '\n') uart_writeByteBlockingActual('\r'); + uart_writeByteBlockingActual(*buffer++); + } +} + +void uart_drainOutputQueue() { + while (!uart_isOutputQueueEmpty()) uart_loadOutputFifo(); +} + +void uart_update() { + uart_loadOutputFifo(); + + if (uart_isReadByteReady()) { + unsigned char ch = uart_readByte(); + if (ch == '\r') uart_writeText("\n"); else uart_writeByteBlocking(ch); + } +} + +void uart_hex(unsigned int d) { + unsigned int n; + int c; + for(c=28;c>=0;c-=4) { + // get highest tetrad + n=(d>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + + uart_writeByteBlockingActual(n); + } +} + +void uart_byte(unsigned char b) { + unsigned int n; + int c; + for(c=4;c>=0;c-=4) { + // get highest tetrad + n=(b>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + + uart_writeByteBlockingActual(n); + } + uart_writeByteBlockingActual(' '); +} diff --git a/part14-ethernet/lib/mb.c b/part14-ethernet/lib/mb.c new file mode 100644 index 0000000..65dbbb8 --- /dev/null +++ b/part14-ethernet/lib/mb.c @@ -0,0 +1,39 @@ +#include "../include/io.h" + +// The buffer must be 16-byte aligned as only the upper 28 bits of the address can be passed via the mailbox +volatile unsigned int __attribute__((aligned(16))) mbox[36]; + +enum { + VIDEOCORE_MBOX = (PERIPHERAL_BASE + 0x0000B880), + MBOX_READ = (VIDEOCORE_MBOX + 0x0), + MBOX_POLL = (VIDEOCORE_MBOX + 0x10), + MBOX_SENDER = (VIDEOCORE_MBOX + 0x14), + MBOX_STATUS = (VIDEOCORE_MBOX + 0x18), + MBOX_CONFIG = (VIDEOCORE_MBOX + 0x1C), + MBOX_WRITE = (VIDEOCORE_MBOX + 0x20), + MBOX_RESPONSE = 0x80000000, + MBOX_FULL = 0x80000000, + MBOX_EMPTY = 0x40000000 +}; + +unsigned int mbox_call(unsigned char ch) +{ + // 28-bit address (MSB) and 4-bit value (LSB) + unsigned int r = ((unsigned int)((long) &mbox) &~ 0xF) | (ch & 0xF); + + // Wait until we can write + while (mmio_read(MBOX_STATUS) & MBOX_FULL); + + // Write the address of our buffer to the mailbox with the channel appended + mmio_write(MBOX_WRITE, r); + + while (1) { + // Is there a reply? + while (mmio_read(MBOX_STATUS) & MBOX_EMPTY); + + // Is it a reply to our message? + if (r == mmio_read(MBOX_READ)) return mbox[1]==MBOX_RESPONSE; // Is it successful? + + } + return 0; +} diff --git a/part14-ethernet/lib/multicore.c b/part14-ethernet/lib/multicore.c new file mode 100644 index 0000000..0000bcd --- /dev/null +++ b/part14-ethernet/lib/multicore.c @@ -0,0 +1,44 @@ +#include "../include/multicore.h" + +void store32(unsigned long address, unsigned long value) +{ + *(unsigned long *) address = value; +} + +unsigned long load32(unsigned long address) +{ + return *(unsigned long *) address; +} + +void start_core1(void (*func)(void)) +{ + store32((unsigned long)&spin_cpu1, (unsigned long)func); + asm volatile ("sev"); +} + +void start_core2(void (*func)(void)) +{ + store32((unsigned long)&spin_cpu2, (unsigned long)func); + asm volatile ("sev"); +} + +void start_core3(void (*func)(void)) +{ + store32((unsigned long)&spin_cpu3, (unsigned long)func); + asm volatile ("sev"); +} + +void clear_core1(void) +{ + store32((unsigned long)&spin_cpu1, 0); +} + +void clear_core2(void) +{ + store32((unsigned long)&spin_cpu2, 0); +} + +void clear_core3(void) +{ + store32((unsigned long)&spin_cpu3, 0); +} diff --git a/part14-ethernet/lib/spi.c b/part14-ethernet/lib/spi.c new file mode 100644 index 0000000..cf1cdf0 --- /dev/null +++ b/part14-ethernet/lib/spi.c @@ -0,0 +1,98 @@ +#include "../include/io.h" + +// SPI + +struct Spi0Regs { + volatile unsigned int cs; + volatile unsigned int fifo; + volatile unsigned int clock; + volatile unsigned int data_length; + volatile unsigned int ltoh; + volatile unsigned int dc; +}; + +#define REGS_SPI0 ((struct Spi0Regs *)(PERIPHERAL_BASE + 0x00204000)) + +// CS Register +#define CS_LEN_LONG (1 << 25) +#define CS_DMA_LEN (1 << 24) +#define CS_CSPOL2 (1 << 23) +#define CS_CSPOL1 (1 << 22) +#define CS_CSPOL0 (1 << 21) +#define CS_RXF (1 << 20) +#define CS_RXR (1 << 19) +#define CS_TXD (1 << 18) +#define CS_RXD (1 << 17) +#define CS_DONE (1 << 16) +#define CS_LEN (1 << 13) +#define CS_REN (1 << 12) +#define CS_ADCS (1 << 11) +#define CS_INTR (1 << 10) +#define CS_INTD (1 << 9) +#define CS_DMAEN (1 << 8) +#define CS_TA (1 << 7) +#define CS_CSPOL (1 << 6) +#define CS_CLEAR_RX (1 << 5) +#define CS_CLEAR_TX (1 << 4) +#define CS_CPOL__SHIFT 3 +#define CS_CPHA__SHIFT 2 +#define CS_CS (1 << 0) +#define CS_CS__SHIFT 0 + +void spi_init() { + gpio_useAsAlt0(7); //CS1 + gpio_useAsAlt0(8); //CS0 + gpio_useAsAlt0(9); //MISO + gpio_useAsAlt0(10); //MOSI + gpio_useAsAlt0(11); //SCLK +} + +void spi_chip_select(unsigned char chip_select) { + REGS_SPI0->cs = (REGS_SPI0->cs & ~CS_CS) | (chip_select << CS_CS__SHIFT); +} + +void spi_send_recv(unsigned char *sbuffer, unsigned char *rbuffer, unsigned int size) { + REGS_SPI0->data_length = size; + REGS_SPI0->cs = REGS_SPI0->cs | CS_CLEAR_RX | CS_CLEAR_TX | CS_TA; + + unsigned int read_count = 0; + unsigned int write_count = 0; + + while(read_count < size || write_count < size) { + while(write_count < size && REGS_SPI0->cs & CS_TXD) { + if (sbuffer) { + REGS_SPI0->fifo = *sbuffer++; + } else { + REGS_SPI0->fifo = 0; + } + + write_count++; + } + + while(read_count < size && REGS_SPI0->cs & CS_RXD) { + unsigned int data = REGS_SPI0->fifo; + + if (rbuffer) { + *rbuffer++ = data; + } + + read_count++; + } + } + + while(!(REGS_SPI0->cs & CS_DONE)) { + while(REGS_SPI0->cs & CS_RXD) { + unsigned int r = REGS_SPI0->fifo; + } + } + + REGS_SPI0->cs = (REGS_SPI0->cs & ~CS_TA); +} + +void spi_send(unsigned char *data, unsigned int size) { + spi_send_recv(data, 0, size); +} + +void spi_recv(unsigned char *data, unsigned int size) { + spi_send_recv(0, data, size); +} diff --git a/part14-ethernet/net/enc28j60.c b/part14-ethernet/net/enc28j60.c new file mode 100644 index 0000000..8570ea2 --- /dev/null +++ b/part14-ethernet/net/enc28j60.c @@ -0,0 +1,1452 @@ +/** + ****************************************************************************** + * @file enc28j60.c + * @author Christian Schoffit, portions from Gregory Nutt: + * Copyright (C) 2010-2012, 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * @version V1.0.0 + * @date 02-June-2015 + * @brief This file provides a set of functions needed to manage the ENC28J60 + * Stand-Alone Ethernet Controller with SPI Interface. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2015 Christian Schoffit

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Christian Schoffit nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +/* +Module Feature Issue Issue Summary Affected Revisions + B1 B4 B5 B7 +MAC Interface - 1. MAC registers unreliable with slow asynchronous SPI clock X X +Reset - 2. CLKRDY set early X X X X +Core Operating 3. Industrial (-40°C to +85°C) temperature range unsupported X X + Specifications +Oscillator CLKOUT pin 4. CLKOUT unavailable in Power Save mode X X X X +Memory Ethernet 5. Receive buffer must start at 0000h X X X X + Buffer +Interrupts - 6. Receive Packet Pending Interrupt Flag (PKTIF) unreliable X X X X +PHY - 7. TPIN+/- automatic polarity detection and correction + unreliable X X X X +PHY - 8. RBIAS resistor value differs between silicon revisions X X +PHY - 9. Internal loopback in half-duplex unreliable X X X X +PHY - 10. Internal loopback in full-duplex unreliable X X X X +PHY LEDs - 11. Combined Collision and Duplex Status mode unavailable X X X X +Transmit - 12. Transmit abort may stall transmit logic X X X X +Logic +PHY - 13. Received link pulses potentially cause collisions X X +Memory Ethernet 14. Even values in ERXRDPT may corrupt receive buffer X X X X + Buffer +Transmit - 15. LATECOL Status bit unreliable X X X X +Logic +PHY LEDs - 16. LED auto-polarity detection unreliable X X X X +DMA - 17. DMA checksum calculations will abort receive packets X X X X +Receive - 18. Pattern match filter allows reception of extra packets X X X X +Filter +SPI - 19. Reset command unavailable in Power Save mode X X X X +Interface + +Only workaround relative to issues affecting B7 silicon revision are implemented. Therefore, issues +specific to Ethernet conformance are not addressed, since they only affect B1 and B3 silicon revisions. + +Erratas 7, 8, 16... have workaround implemented by hardware + +Errata 18 is implemented in lwip stack +*/ + +/* Includes ------------------------------------------------------------------*/ +#include "enc28j60.h" +#include "../include/fb.h" + +/** @addtogroup BSP + * @{ + */ + +/** @addtogroup Components + * @{ + */ + +/** @defgroup ENC28J60 + * @{ + */ + +/** @defgroup ENC28J60_Private_Types_Definitions + * @{ + */ + +/** @defgroup ENC28J60_Private_Defines + * @{ + */ + +/* Poll timeout */ + +#define ENC_POLLTIMEOUT 50 + +/** + * @} + */ + +/** @defgroup ENC28J60_Private_Macros + * @{ + */ + +/* Packet Memory ************************************************************/ + +/* Packet memory layout */ + +#define ALIGNED_BUFSIZE ((CONFIG_NET_ETH_MTU + 255) & ~255) + +/* Work around Errata #5 (spurious reset of ERXWRPT to 0) by placing the RX + * FIFO at the beginning of packet memory. + */ + +# define PKTMEM_RX_START 0x0000 /* RX buffer must be at addr 0 for errata 5 */ +# define PKTMEM_RX_END (PKTMEM_END-ALIGNED_BUFSIZE) /* RX buffer length is total SRAM minus TX buffer */ +# define PKTMEM_TX_START (PKTMEM_RX_END+1) /* Start TX buffer after */ +# define PKTMEM_TX_ENDP1 (PKTMEM_TX_START+ALIGNED_BUFSIZE) /* Allow TX buffer for two frames */ + +/* Misc. Helper Macros ******************************************************/ + +#define enc_rdgreg(ctrlreg) \ + enc_rdgreg2(ENC_RCR | GETADDR(ctrlreg)) +#define enc_wrgreg(ctrlreg, wrdata) \ + enc_wrgreg2(ENC_WCR | GETADDR(ctrlreg), wrdata) +#define enc_bfcgreg(ctrlreg,clrbits) \ + enc_wrgreg2(ENC_BFC | GETADDR(ctrlreg), clrbits) +#define enc_bfsgreg(ctrlreg,setbits) \ + enc_wrgreg2(ENC_BFS | GETADDR(ctrlreg), setbits) + +/** + * @} + */ + +/** @defgroup ENC28J60_Private_Variables + * @{ + */ + + /* Stores how many iterations the microcontroller can do in 1 µs */ +static uint32_t iter_per_us=0; + +/** + * @} + */ + +/** @defgroup ENC28J60_Private_Function_Prototypes + * @{ + */ +/** + * @} + */ + +/** @defgroup ENC28J60_Private_Functions + * @{ + */ + +/** + Calibrate the constant time + **/ + +static void calibrate(void) +{ + uint32_t time; + volatile uint32_t i; + + iter_per_us = 1000000; + + time = HAL_GetTick(); + /* Wait for next tick */ + while (HAL_GetTick() == time) { + /* wait */ + } + for (i=0; ibank = 0; /* Initialize the trace on the current selected bank */ + //up_mdelay(2); + HAL_Delay(2); /* >1000 µs, conforms to errata #2 */ +} + +/**************************************************************************** + * Function: enc_setbank + * + * Description: + * Set the bank for these next control register access. + * + * Assumption: + * The caller has exclusive access to the SPI bus + * + * Parameters: + * handle - Reference to the driver state structure + * bank - The bank to select (0-3) + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +void enc_setbank(ENC_HandleTypeDef *handle, uint8_t bank) { + + if (bank != handle->bank) { + /* Select bank 0 (just so that all of the bits are cleared) */ + + enc_bfcgreg(ENC_ECON1, ECON1_BSEL_MASK); + + /* Then OR in bits to get the correct bank */ + + if (bank != 0) + { + enc_bfsgreg(ENC_ECON1, (bank << ECON1_BSEL_SHIFT)); + } + + /* Then remember the bank setting */ + + handle->bank = bank; + } +} + +/**************************************************************************** + * Function: enc_rdbreg + * + * Description: + * Read from a banked control register using the RCR command. + * + * Parameters: + * handle - Reference to the driver state structure + * ctrlreg - Bit encoded address of banked register to read + * + * Returned Value: + * The byte read from the banked register + * + * Assumptions: + * + ****************************************************************************/ + +static uint8_t enc_rdbreg(ENC_HandleTypeDef *handle, uint8_t ctrlreg) +{ + uint8_t data[3]; + + /* Set the bank */ + + enc_setbank(handle, GETBANK(ctrlreg)); + + /* Send the RCR command and collect the data. How we collect the data + * depends on if this is a PHY/CAN or not. The normal sequence requires + * 16-clocks: 8 to clock out the cmd and 8 to clock in the data. + */ + + data[0] = ENC_RCR | GETADDR(ctrlreg); + + /* The PHY/MAC sequence requires 24-clocks: 8 to clock out the cmd, + * 8 dummy bits, and 8 to clock in the PHY/MAC data. + */ + + ENC_SPI_SendBuf(data, data, (ISPHYMAC(ctrlreg))?3:2); + return (ISPHYMAC(ctrlreg))?data[2]:data[1]; +} + +/**************************************************************************** + * Function: enc_wrbreg + * + * Description: + * Write to a banked control register using the WCR command. Unlike + * reading, this same SPI sequence works for normal, MAC, and PHY + * registers. + * + * Parameters: + * handle - Reference to the driver state structure + * ctrlreg - Bit encoded address of banked register to write + * wrdata - The data to send + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static void enc_wrbreg(ENC_HandleTypeDef *handle, uint8_t ctrlreg, + uint8_t wrdata) +{ + uint8_t data[2]; + + /* Set the bank */ + + enc_setbank(handle, GETBANK(ctrlreg)); + + /* Send the WCR command and data. The sequence requires 16-clocks: + * 8 to clock out the cmd + 8 to clock out the data. + */ + + data[0] = ENC_WCR | GETADDR(ctrlreg); + data[1] = wrdata; + + ENC_SPI_SendBuf(data, NULL, 2); +} + +/**************************************************************************** + * Function: enc_waitbreg + * + * Description: + * Wait until banked register bit(s) take a specific value (or a timeout + * occurs). + * + * Parameters: + * handle - Reference to the driver state structure + * ctrlreg - Bit encoded address of banked register to check + * bits - The bits to check (a mask) + * value - The value of the bits to return (value under mask) + * + * Returned Value: + * OK on success, negated errno on failure + * + * Assumptions: + * + ****************************************************************************/ + +static bool enc_waitbreg(ENC_HandleTypeDef *handle, uint8_t ctrlreg, + uint8_t bits, uint8_t value) +{ + uint32_t start = HAL_GetTick(); + uint32_t elapsed; + uint8_t rddata; + + /* Loop until the exit condition is met */ + + do + { + /* Read the byte from the requested banked register */ + + rddata = enc_rdbreg(handle, ctrlreg); + elapsed = HAL_GetTick() - start; + } + while ((rddata & bits) != value && elapsed < ENC_POLLTIMEOUT); + + return (rddata & bits) == value; +} + + +/**************************************************************************** + * Function: enc_rdphy + * + * Description: + * Read 16-bits of PHY data. + * + * Parameters: + * priv - Reference to the driver state structure + * phyaddr - The PHY register address + * + * Returned Value: + * 16-bit value read from the PHY + * + * Assumptions: + * + ****************************************************************************/ + +static uint16_t enc_rdphy(ENC_HandleTypeDef *handle, uint8_t phyaddr) +{ + uint16_t data = 0; + + /* "To read from a PHY register: + * + * 1. Write the address of the PHY register to read from into the MIREGADR + * register. + */ + + enc_wrbreg(handle, ENC_MIREGADR, phyaddr); + + /* 2. Set the MICMD.MIIRD bit. The read operation begins and the + * MISTAT.BUSY bit is set. + */ + + enc_wrbreg(handle, ENC_MICMD, MICMD_MIIRD); + + /* 3. Wait 10.24 µs. Poll the MISTAT.BUSY bit to be certain that the + * operation is complete. While busy, the host controller should not + * start any MIISCAN operations or write to the MIWRH register. + * + * When the MAC has obtained the register contents, the BUSY bit will + * clear itself. + */ + +// volatile int i; +// for (i=0; i<12*17; i++) { +// } + + up_udelay(12); + + if (enc_waitbreg(handle, ENC_MISTAT, MISTAT_BUSY, 0x00)) + { + /* 4. Clear the MICMD.MIIRD bit. */ + + enc_wrbreg(handle, ENC_MICMD, 0x00); + + /* 5. Read the desired data from the MIRDL and MIRDH registers. The + * order that these bytes are accessed is unimportant." + */ + + data = (uint16_t)enc_rdbreg(handle, ENC_MIRDL); + data |= (uint16_t)enc_rdbreg(handle, ENC_MIRDH) << 8; + } + + return data; +} + +/**************************************************************************** + * Function: enc_wrphy + * + * Description: + * write 16-bits of PHY data. + * + * Parameters: + * handle - Reference to the driver state structure + * phyaddr - The PHY register address + * phydata - 16-bit data to write to the PHY + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static void enc_wrphy(ENC_HandleTypeDef *handle, uint8_t phyaddr, + uint16_t phydata) +{ + /* "To write to a PHY register: + * + * 1. Write the address of the PHY register to write to into the + * MIREGADR register. + */ + + enc_wrbreg(handle, ENC_MIREGADR, phyaddr); + + /* 2. Write the lower 8 bits of data to write into the MIWRL register. */ + + enc_wrbreg(handle, ENC_MIWRL, phydata); + + /* 3. Write the upper 8 bits of data to write into the MIWRH register. + * Writing to this register automatically begins the MIIM transaction, + * so it must be written to after MIWRL. The MISTAT.BUSY bit becomes + * set. + */ + + enc_wrbreg(handle, ENC_MIWRH, phydata >> 8); + + /* The PHY register will be written after the MIIM operation completes, + * which takes 10.24 µs. When the write operation has completed, the BUSY + * bit will clear itself. + * + * The host controller should not start any MIISCAN or MIIRD operations + * while busy." + */ + + /* wait for approx 12 µs */ +// volatile int i; +// for (i=0; i<12*17; i++) { +// } + + up_udelay(12); + enc_waitbreg(handle, ENC_MISTAT, MISTAT_BUSY, 0x00); +} + + +/**************************************************************************** + * Function: enc_pwrfull + * + * Description: + * When normal operation is desired, the host controller must perform + * a slightly modified procedure: + * + * 1. Wake-up by clearing ECON2.PWRSV. + * 2. Wait at least 300 µs for the PHY to stabilize. To accomplish the + * delay, the host controller may poll ESTAT.CLKRDY and wait for it + * to become set. + * 3. Restore receive capability by setting ECON1.RXEN. + * + * After leaving Sleep mode, there is a delay of many milliseconds + * before a new link is established (assuming an appropriate link + * partner is present). The host controller may wish to wait until + * the link is established before attempting to transmit any packets. + * The link status can be determined by polling the PHSTAT2.LSTAT bit. + * Alternatively, the link change interrupt may be used if it is + * enabled. + * + * Parameters: + * handle - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +/* Power save mode not used (fix errata 4 and 19) */ +#if 0 +static void enc_pwrfull(ENC_HandleTypeDef *handle) +{ + /* 1. Wake-up by clearing ECON2.PWRSV. */ + + enc_bfcgreg(ENC_ECON2, ECON2_PWRSV); + + /* 2. Wait at least 300 µs for the PHY to stabilize. To accomplish the + * delay, the host controller may poll ESTAT.CLKRDY and wait for it to + * become set. + */ + + /* wait for approx 350 µs */ +// volatile int i; +// for (i=0; i<350*17; i++) { +// } + + up_udelay(350); + + enc_waitbreg(handle, ENC_ESTAT, ESTAT_CLKRDY, ESTAT_CLKRDY); + + /* 3. Restore receive capability by setting ECON1.RXEN. + * + * The caller will do this when it is ready to receive packets + */ +} +#endif + +/** + * @brief Initialize the enc28j60 and configure the needed hardware resources + * @param handle: Handle on data configuration. + * @retval None + */ +bool ENC_Start(ENC_HandleTypeDef *handle) +{ + /* register value */ + uint8_t regval; + + /* Calibrate time constant */ + calibrate(); + + /* System reset */ + enc_reset(handle); + + /* Use bank 0 */ + enc_setbank(handle, 0); + + /* Check if we are actually communicating with the ENC28J60. If its + * 0x00 or 0xff, then we are probably not communicating correctly + * via SPI. + */ + + regval = enc_rdbreg(handle, ENC_EREVID); + if (regval == 0x00 || regval == 0xff) { + return false; + } + debugstr("Board revision: "); + debughex(regval); + debugcrlf(); + + /* Initialize ECON2: Enable address auto increment. + */ + + enc_wrgreg(ENC_ECON2, ECON2_AUTOINC /* | ECON2_VRPS*/); + + /* Initialize receive buffer. + * First, set the receive buffer start address. + */ + + handle->nextpkt = PKTMEM_RX_START; + enc_wrbreg(handle, ENC_ERXSTL, PKTMEM_RX_START & 0xff); + enc_wrbreg(handle, ENC_ERXSTH, PKTMEM_RX_START >> 8); + + /* Set the receive data pointer */ + + /* Errata 14 */ + enc_wrbreg(handle, ENC_ERXRDPTL, PKTMEM_RX_END & 0xff); + enc_wrbreg(handle, ENC_ERXRDPTH, PKTMEM_RX_END >> 8); +/* + enc_wrbreg(handle, ENC_ERXRDPTL, PKTMEM_RX_START & 0xff); + enc_wrbreg(handle, ENC_ERXRDPTH, PKTMEM_RX_START >> 8); +*/ + + /* Set the receive buffer end. */ + + enc_wrbreg(handle, ENC_ERXNDL, PKTMEM_RX_END & 0xff); + enc_wrbreg(handle, ENC_ERXNDH, PKTMEM_RX_END >> 8); + + /* Set transmit buffer start. */ + + handle->transmitLength = 0; + enc_wrbreg(handle, ENC_ETXSTL, PKTMEM_TX_START & 0xff); + enc_wrbreg(handle, ENC_ETXSTH, PKTMEM_TX_START >> 8); + + /* Set filter mode: unicast OR broadcast AND crc valid */ + + enc_wrbreg(handle, ENC_ERXFCON, ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_BCEN); + + do { + HAL_Delay(10); /* Wait for 10 ms to let the clock be ready */ + regval = enc_rdbreg(handle, ENC_ESTAT); + } while ((regval & ESTAT_CLKRDY) == 0); + + /* Enable MAC receive */ + + enc_wrbreg(handle, ENC_MACON1, MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS); + + /* Enable automatic padding and CRC operations */ + + if (handle->Init.DuplexMode == ETH_MODE_HALFDUPLEX) { + enc_wrbreg(handle, ENC_MACON3, + ((handle->Init.ChecksumMode == ETH_CHECKSUM_BY_HARDWARE)?MACON3_PADCFG0 | MACON3_TXCRCEN:0) | + MACON3_FRMLNEN); + enc_wrbreg(handle, ENC_MACON4, MACON4_DEFER); /* Defer transmission enable */ + + /* Set Non-Back-to-Back Inter-Packet Gap */ + + enc_wrbreg(handle, ENC_MAIPGL, 0x12); + enc_wrbreg(handle, ENC_MAIPGH, 0x0c); + + /* Set Back-to-Back Inter-Packet Gap */ + + enc_wrbreg(handle, ENC_MABBIPG, 0x12); + } else { + /* Set filter mode: unicast OR broadcast AND crc valid AND Full Duplex */ + + enc_wrbreg(handle, ENC_MACON3, + ((handle->Init.ChecksumMode == ETH_CHECKSUM_BY_HARDWARE)?MACON3_PADCFG0 | MACON3_TXCRCEN:0) | + MACON3_FRMLNEN | MACON3_FULDPX); + + /* Set Non-Back-to-Back Inter-Packet Gap */ + + enc_wrbreg(handle, ENC_MAIPGL, 0x12); + + /* Set Back-to-Back Inter-Packet Gap */ + + enc_wrbreg(handle, ENC_MABBIPG, 0x15); + } + + /* Set the maximum packet size which the controller will accept */ + + enc_wrbreg(handle, ENC_MAMXFLL, (CONFIG_NET_ETH_MTU+18) & 0xff); + enc_wrbreg(handle, ENC_MAMXFLH, (CONFIG_NET_ETH_MTU+18) >> 8); + + /* Configure LEDs (No, just use the defaults for now) */ + /* enc_wrphy(priv, ENC_PHLCON, ??); */ + + /* Setup up PHCON1 & 2 */ + + if (handle->Init.DuplexMode == ETH_MODE_HALFDUPLEX) { + enc_wrphy(handle, ENC_PHCON1, 0x00); + enc_wrphy(handle, ENC_PHCON2, PHCON2_HDLDIS); /* errata 9 workaround */ + } else { + enc_wrphy(handle, ENC_PHCON1, PHCON1_PDPXMD); /* errata 10 workaround */ + enc_wrphy(handle, ENC_PHCON2, 0x00); + } + + /* Not used Restore normal operation mode + enc_pwrfull(handle); */ + + /* Process interrupt settings */ + if (handle->Init.InterruptEnableBits & EIE_LINKIE) { + /* Enable link change interrupt in PHY module */ + enc_wrphy(handle, ENC_PHIE, PHIE_PGEIE | PHIE_PLNKIE); + } + + /* Since we not modify PHLCON register, we don't fall in errata 11 case */ + + /* Reset all interrupt flags */ + enc_bfcgreg(ENC_EIR, EIR_ALLINTS); + + regval = handle->Init.InterruptEnableBits; + if (regval) { + /* Ensure INTIE is set when at least an interruption is selected */ + regval |= EIE_INTIE; + } + /* Enable selected interrupts in ethernet controller module */ + enc_bfsgreg(ENC_EIE, regval); + + /* Enable the receiver */ + enc_bfsgreg(ENC_ECON1, ECON1_RXEN); + + return true; +} + +/** + * @} + */ + +/**************************************************************************** + * Function: ENC_SetMacAddr + * + * Description: + * Set the MAC address to the configured value. This is done after ifup + * or after a TX timeout. Note that this means that the interface must + * be down before configuring the MAC addr. + * + * Parameters: + * handle - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +void ENC_SetMacAddr(ENC_HandleTypeDef *handle) +{ + /* Program the hardware with it's MAC address (for filtering). + * MAADR1 MAC Address Byte 1 (MAADR<47:40>), OUI Byte 1 + * MAADR2 MAC Address Byte 2 (MAADR<39:32>), OUI Byte 2 + * MAADR3 MAC Address Byte 3 (MAADR<31:24>), OUI Byte 3 + * MAADR4 MAC Address Byte 4 (MAADR<23:16>) + * MAADR5 MAC Address Byte 5 (MAADR<15:8>) + * MAADR6 MAC Address Byte 6 (MAADR<7:0>) + */ + + enc_wrbreg(handle, ENC_MAADR1, handle->Init.MACAddr[0]); + enc_wrbreg(handle, ENC_MAADR2, handle->Init.MACAddr[1]); + enc_wrbreg(handle, ENC_MAADR3, handle->Init.MACAddr[2]); + enc_wrbreg(handle, ENC_MAADR4, handle->Init.MACAddr[3]); + enc_wrbreg(handle, ENC_MAADR5, handle->Init.MACAddr[4]); + enc_wrbreg(handle, ENC_MAADR6, handle->Init.MACAddr[5]); +} + + +/**************************************************************************** + * Function: ENC_WriteBuffer + * + * Description: + * Write a buffer of data. + * + * Parameters: + * buffer - A pointer to the buffer to write from + * buflen - The number of bytes to write + * + * Returned Value: + * None + * + * Assumptions: + * Read pointer is set to the correct address + * + ****************************************************************************/ + +void ENC_WriteBuffer(void *buffer, uint16_t buflen) +{ + /* Send the WBM command and copy the packet itself into the transmit + * buffer at the position of the EWRPT register. + */ + + /* Select ENC28J60 chip + * + * "The WBM command is started by lowering the CS pin. ..." + * We explicitly select the ENC28J60 chip because we have to transmits several pieces of + * information while keeping CS low + * + */ + + ENC_SPI_Select(true); + + /* Send the write buffer memory command (ignoring the response) + * + * "...The [3-bit]WBM opcode should then be sent to the ENC28J60, + * followed by the 5-bit constant, 1Ah." + */ + + + ENC_SPI_SendWithoutSelection(ENC_WBM); + + /* Send the buffer + * + * "... After the WBM command and constant are sent, the data to + * be stored in the memory pointed to by EWRPT should be shifted + * out MSb first to the ENC28J60. After 8 data bits are received, + * the Write Pointer will automatically increment if AUTOINC is + * set. The host controller can continue to provide clocks on the + * SCK pin and send data on the SI pin, without raising CS, to + * keep writing to the memory. In this manner, with AUTOINC + * enabled, it is possible to continuously write sequential bytes + * to the buffer memory without any extra SPI command + * overhead. + */ + + ENC_SPI_SendBuf(buffer, NULL, buflen); + + /* De-select ENC28J60 chip + * + * "The WBM command is terminated by bringing up the CS pin. ..." + * done in ENC_SPI_SendBuf callback + */ + +} + +/**************************************************************************** + * Function: enc_rdbuffer + * + * Description: + * Read a buffer of data. + * + * Parameters: + * buffer - A pointer to the buffer to read into + * buflen - The number of bytes to read + * + * Returned Value: + * None + * + * Assumptions: + * Read pointer is set to the correct address + * + ****************************************************************************/ + +static void enc_rdbuffer(void *buffer, int16_t buflen) +{ + /* Select ENC28J60 chip */ + + ENC_SPI_Select(true); + + /* Send the read buffer memory command (ignoring the response) */ + + ENC_SPI_SendWithoutSelection(ENC_RBM); + + /* Then read the buffer data */ + + ENC_SPI_SendBuf(NULL, buffer, buflen); + + /* De-select ENC28J60 chip: done in ENC_SPI_SendBuf callback */ +} + +/**************************************************************************** + * Function: ENC_RestoreTXBuffer + * + * Description: + * Prepare TX buffer + * + * Parameters: + * handle - Reference to the driver state structure + * len - length of buffer + * + * Returned Value: + * ERR_OK 0 No error, everything OK. + * ERR_MEM -1 Out of memory error. + * ERR_TIMEOUT -3 Timeout. + * + * Assumptions: + * + ****************************************************************************/ + +int8_t ENC_RestoreTXBuffer(ENC_HandleTypeDef *handle, uint16_t len) +{ + uint16_t txend; + uint8_t control_write[2]; + + /* Wait while TX is busy */ + if (!enc_waitgreg(ENC_ECON1, ECON1_TXRTS, 0)) { + return ERR_TIMEOUT; + } + + /* Verify that the hardware is ready to send another packet. The driver + * starts a transmission process by setting ECON1.TXRTS. When the packet is + * finished transmitting or is aborted due to an error/cancellation, the + * ECON1.TXRTS bit will be cleared. + * + * NOTE: If we got here, then we have committed to sending a packet. + * higher level logic must have assured that TX-related interrupts are disabled. + */ + + /* Send the packet: address=priv->dev.d_buf, length=priv->dev.d_len */ + + /* Set transmit buffer start (is this necessary?). */ + + enc_wrbreg(handle, ENC_ETXSTL, PKTMEM_TX_START & 0xff); + enc_wrbreg(handle, ENC_ETXSTH, PKTMEM_TX_START >> 8); + + /* Reset the write pointer to start of transmit buffer */ + + enc_wrbreg(handle, ENC_EWRPTL, PKTMEM_TX_START & 0xff); + enc_wrbreg(handle, ENC_EWRPTH, PKTMEM_TX_START >> 8); + + /* Set the TX End pointer based on the size of the packet to send. Note + * that the offset accounts for the control byte at the beginning the + * buffer plus the size of the packet data. + */ + + txend = PKTMEM_TX_START + len; + + if (txend+8>PKTMEM_TX_ENDP1) { + return ERR_MEM; + } + + enc_wrbreg(handle, ENC_ETXNDL, txend & 0xff); + enc_wrbreg(handle, ENC_ETXNDH, txend >> 8); + + /* Send the write buffer memory command (ignoring the response) + * + * "...The [3-bit]WBM opcode should then be sent to the ENC28J60, + * followed by the 5-bit constant, 1Ah." + * + * "...the ENC28J60 requires a single per packet control byte to + * precede the packet for transmission." + * + * POVERRIDE: Per Packet Override bit (Not set): + * 1 = The values of PCRCEN, PPADEN and PHUGEEN will override the + * configuration defined by MACON3. + * 0 = The values in MACON3 will be used to determine how the packet + * will be transmitted + * PCRCEN: Per Packet CRC Enable bit (Set, but won't be used because + * POVERRIDE is zero). + * PPADEN: Per Packet Padding Enable bit (Set, but won't be used because + * POVERRIDE is zero). + * PHUGEEN: Per Packet Huge Frame Enable bit (Set, but won't be used + * because POVERRIDE is zero). + */ + + control_write[0] = ENC_WBM; + control_write[1] = PKTCTRL_PCRCEN | PKTCTRL_PPADEN | PKTCTRL_PHUGEEN; + ENC_SPI_SendBuf(control_write, control_write, 2); + + return ERR_OK; +} + +/**************************************************************************** + * Function: ENC_Transmit + * + * Description: + * Start hardware transmission. Called either from: + * + * - pkif interrupt when an application responds to the receipt of data + * by trying to send something, or + * - From watchdog based polling. + * + * Parameters: + * handle - Reference to the driver state structure + * len - length of buffer + * + * Returned Value: + * none + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef USE_PROTOTHREADS +PT_THREAD(ENC_Transmit(struct pt *pt, ENC_HandleTypeDef *handle)) +#else +void ENC_Transmit(ENC_HandleTypeDef *handle) +#endif +{ + PT_BEGIN(pt); + + if (handle->transmitLength != 0) { + /* A frame is ready for transmission */ + /* Set TXRTS to send the packet in the transmit buffer */ + + //enc_bfsgreg(ENC_ECON1, ECON1_TXRTS); + /* Implement erratas 12, 13 and 15 */ + /* Reset transmit logic */ + handle->retries = 16; + do { + enc_bfsgreg(ENC_ECON1, ECON1_TXRST); + enc_bfcgreg(ENC_ECON1, ECON1_TXRST); + enc_bfcgreg(ENC_EIR, EIR_TXERIF | EIR_TXIF); + + /* Start transmission */ + enc_bfsgreg(ENC_ECON1, ECON1_TXRTS); + +#ifdef USE_PROTOTHREADS + handle->startTime = HAL_GetTick(); + handle->duration = 20; /* Timeout after 20 ms */ + PT_WAIT_UNTIL(pt, (((enc_rdgreg(ENC_EIR) & (EIR_TXIF | EIR_TXERIF)) != 0) || + (HAL_GetTick() - handle->startTime > handle->duration))); +#else + /* Wait for end of transmission */ + enc_waitwhilegreg(ENC_EIR, EIR_TXIF | EIR_TXERIF, 0); +#endif + + /* Stop transmission */ + enc_bfcgreg(ENC_ECON1, ECON1_TXRTS); + + { + uint16_t addtTsv4; + uint8_t tsv4, regval; + + /* read tsv */ + addtTsv4 = PKTMEM_TX_START + handle->transmitLength + 4; + + enc_wrbreg(handle, ENC_ERDPTL, addtTsv4 & 0xff); + enc_wrbreg(handle, ENC_ERDPTH, addtTsv4 >> 8); + + enc_rdbuffer(&tsv4, 1); + regval = enc_rdgreg(ENC_EIR); + if (!(regval & EIR_TXERIF) || !(tsv4 & TSV_LATECOL)) { + break; + } + } + handle->retries--; + } while (handle->retries > 0); + /* Transmission finished (but can be unsuccessful) */ + handle->transmitLength = 0; + } + PT_END(pt); +} + +/**************************************************************************** + * Function: ENC_GetReceivedFrame + * + * Description: + * Check if we have received packet, and if so, retrieve them. + * + * Parameters: + * handle - Reference to the driver state structure + * + * Returned Value: + * true if new packet is available; false otherwise + * + * Assumptions: + * + ****************************************************************************/ + +bool ENC_GetReceivedFrame(ENC_HandleTypeDef *handle) +{ + uint8_t rsv[6]; + uint16_t pktlen; + uint16_t rxstat; + + uint8_t pktcnt; + + bool result = true; + + pktcnt = enc_rdbreg(handle, ENC_EPKTCNT); + if (pktcnt == 0) { + return false; + }; + + /* Set the read pointer to the start of the received packet (ERDPT) */ + + enc_wrbreg(handle, ENC_ERDPTL, (handle->nextpkt) & 0xff); + enc_wrbreg(handle, ENC_ERDPTH, (handle->nextpkt) >> 8); + + /* Read the next packet pointer and the 4 byte read status vector (RSV) + * at the beginning of the received packet. (ERDPT should auto-increment + * and wrap to the beginning of the read buffer as necessary) + */ + + enc_rdbuffer(rsv, 6); + + /* Decode the new next packet pointer, and the RSV. The + * RSV is encoded as: + * + * Bits 0-15: Indicates length of the received frame. This includes the + * destination address, source address, type/length, data, + * padding and CRC fields. This field is stored in little- + * endian format. + * Bits 16-31: Bit encoded RX status. + */ + + handle->nextpkt = (uint16_t)rsv[1] << 8 | (uint16_t)rsv[0]; + pktlen = (uint16_t)rsv[3] << 8 | (uint16_t)rsv[2]; + rxstat = (uint16_t)rsv[5] << 8 | (uint16_t)rsv[4]; + + /* Check if the packet was received OK */ + + if ((rxstat & RXSTAT_OK) == 0) { +#ifdef CONFIG_ENC28J60_STATS + priv->stats.rxnotok++; +#endif + result = false; + } else { /* Check for a usable packet length (4 added for the CRC) */ + if (pktlen > (CONFIG_NET_ETH_MTU + 4) || pktlen <= (ETH_HDRLEN + 4)) { + #ifdef CONFIG_ENC28J60_STATS + priv->stats.rxpktlen++; + #endif + result = false; + } else { /* Otherwise, read and process the packet */ + /* Save the packet length (without the 4 byte CRC) in handle->RxFrameInfos.length*/ + + handle->RxFrameInfos.length = pktlen - 4; + + /* Copy the data data from the receive buffer to priv->dev.d_buf. + * ERDPT should be correctly positioned from the last call to to + * end_rdbuffer (above). + */ + + enc_rdbuffer(handle->RxFrameInfos.buffer, handle->RxFrameInfos.length); + + } + } + + /* Move the RX read pointer to the start of the next received packet. + * This frees the memory we just read. + */ + + /* Errata 14 (on se sert de rxstat comme variable temporaire */ + rxstat = handle->nextpkt; + if (rxstat == PKTMEM_RX_START) { + rxstat = PKTMEM_RX_END; + } else { + rxstat--; + } + enc_wrbreg(handle, ENC_ERXRDPTL, rxstat & 0xff); + enc_wrbreg(handle, ENC_ERXRDPTH, rxstat >> 8); +/* + enc_wrbreg(handle, ENC_ERXRDPTL, (handle->nextpkt)); + enc_wrbreg(handle, ENC_ERXRDPTH, (handle->nextpkt) >> 8); +*/ + + /* Decrement the packet counter indicate we are done with this packet */ + + enc_bfsgreg(ENC_ECON2, ECON2_PKTDEC); + + return result; +} + +/**************************************************************************** + * Function: enc_linkstatus + * + * Description: + * The current link status can be obtained from the PHSTAT1.LLSTAT or + * PHSTAT2.LSTAT. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static void enc_linkstatus(ENC_HandleTypeDef *handle) +{ + handle->LinkStatus = enc_rdphy(handle, ENC_PHSTAT2); +} + +/**************************************************************************** + * Function: ENC_EnableInterrupts + * + * Description: + * Enable individual ENC28J60 interrupts + * + * Parameters: + * bits - The individual bits to enable + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +void ENC_EnableInterrupts(uint8_t bits) +{ + enc_bfsgreg(ENC_EIE, bits); +} + + +/**************************************************************************** + * Function: ENC_IRQHandler + * + * Description: + * Perform interrupt handling logic outside of the interrupt handler (on + * the work queue thread). + * + * Parameters: + * handle - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +void ENC_IRQHandler(ENC_HandleTypeDef *handle) +{ + uint8_t eir; + + /* Disable further interrupts by clearing the global interrupt enable bit. + * "After an interrupt occurs, the host controller should clear the global + * enable bit for the interrupt pin before servicing the interrupt. Clearing + * the enable bit will cause the interrupt pin to return to the non-asserted + * state (high). Doing so will prevent the host controller from missing a + * falling edge should another interrupt occur while the immediate interrupt + * is being serviced." + */ + + enc_bfcgreg(ENC_EIE, EIE_INTIE); + + /* Read EIR for interrupt flags + */ + + eir = enc_rdgreg(ENC_EIR) & EIR_ALLINTS; + + /* PKTIF is not reliable, check PKCNT instead */ + if (enc_rdbreg(handle, ENC_EPKTCNT) != 0) { + /* Manage EIR_PKTIF by software */ + eir |= EIR_PKTIF; + } + + /* Store interrupt flags in handle */ + handle->interruptFlags = eir; + + /* If link status has changed, read it */ + if ((eir & EIR_LINKIF) != 0) /* Link change interrupt */ + { + enc_linkstatus(handle); /* Get current link status */ + enc_rdphy(handle, ENC_PHIR); /* Clear the LINKIF interrupt */ + } + + /* Reset ENC28J60 interrupt flags, except PKTIF form which interruption is deasserted when PKTCNT reaches 0 */ + enc_bfcgreg(ENC_EIR, EIR_ALLINTS); + + /* Enable Ethernet interrupts */ + /* done after effective process on interrupts enc_bfsgreg(ENC_EIE, EIE_INTIE); */ +} + +/**************************************************************************** + * Function: ENC_GetPkcnt + * + * Description: + * Get the number of pending receive packets + * + * Parameters: + * handle - Reference to the driver state structure + * + * Returned Value: + * the number of receive packet not processed yet + * + * Assumptions: + * + ****************************************************************************/ + +void ENC_GetPkcnt(ENC_HandleTypeDef *handle) +{ + handle->pktCnt = enc_rdbreg(handle, ENC_EPKTCNT); +} + + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/part14-ethernet/net/enc28j60.h b/part14-ethernet/net/enc28j60.h new file mode 100644 index 0000000..eaab987 --- /dev/null +++ b/part14-ethernet/net/enc28j60.h @@ -0,0 +1,816 @@ +/** + ****************************************************************************** + * @file enc28j60.h + * @author Christian Schoffit, portions from Gregory Nutt: + * Copyright (C) 2010-2012, 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * @version V1.0.0 + * @date 02-June-2015 + * @brief This file provides a set of functions needed to manage the ENC28J60 + * Stand-Alone Ethernet Controller with SPI Interface. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2015 Christian Schoffit

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Christian Schoffit nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +#ifndef ENC28J60_H_INCLUDED +#define ENC28J60_H_INCLUDED + +#include +#include + +#ifdef USE_PROTOTHREADS +#include "pt.h" +#else +#define PT_BEGIN(x) +#define PT_END(x) +#endif + +#ifndef NULL +#define NULL ((void *)0) +#endif + +/* Ethernet frames are between 64 and 1518 bytes long */ + +#define MIN_FRAMELEN 64 +#define MAX_FRAMELEN 1518 + + +/* External functions --------------------------------------------------------*/ +void HAL_Delay(volatile uint32_t Delay); +uint32_t HAL_GetTick(void); + +/* Callback functions *********************************************************/ + +/** + * Implement SPI Slave selection and deselection. Must be provided by user code + * param select: true if the ENC28J60 slave SPI if selected, false otherwise + * retval none + */ + +void ENC_SPI_Select(bool select); + +/** + * Implement SPI single byte send and receive. + * The ENC28J60 slave SPI must already be selected and wont be deselected after transmission + * Must be provided by user code + * param command: command or data to be sent to ENC28J60 + * retval answer from ENC28J60 + */ + +void ENC_SPI_SendWithoutSelection(uint8_t command); + +/** + * Implement SPI single byte send and receive. Must be provided by user code + * param command: command or data to be sent to ENC28J60 + * retval answer from ENC28J60 + */ + +void ENC_SPI_Send(uint8_t command); + +/** + * Implement SPI buffer send and receive. Must be provided by user code + * param master2slave: data to be sent from host to ENC28J60, can be NULL if we only want to receive data from slave + * param slave2master: answer from ENC28J60 to host, can be NULL if we only want to send data to slave + * retval none + */ + +void ENC_SPI_SendBuf(uint8_t *master2slave, uint8_t *slave2master, uint16_t bufferSize); + +/* Exported types ------------------------------------------------------------*/ +/** @defgroup ETH_Exported_Types ETH Exported Types + * @{ + */ + +/** + * @brief ETH Init Structure definition + */ + +typedef struct +{ + uint32_t DuplexMode; /*!< Selects the MAC duplex mode: Half-Duplex or Full-Duplex mode + This parameter can be a value of @ref ETH_Duplex_Mode */ + + uint8_t *MACAddr; /*!< MAC Address of used Hardware: must be pointer on an array of 6 bytes */ + + uint32_t ChecksumMode; /*!< Selects if the checksum is check by hardware or by software. + This parameter can be a value of @ref ETH_Checksum_Mode */ + + uint8_t InterruptEnableBits; /*!< Selects the enabled interrupts */ +} ENC_InitTypeDef; + + +/** + * @brief Received Frame Informations structure definition + */ +typedef struct +{ + uint32_t length; /*!< Frame length */ + + uint8_t buffer[MAX_FRAMELEN+20]; /*!< Frame buffer */ + +} ENC_RxFrameInfos; + + +/** + * @brief ENC28J60 Handle Structure definition + */ + +typedef struct +{ + ENC_InitTypeDef Init; /*!< Ethernet Init Configuration */ + + uint8_t bank; /*!< Currently selected bank */ + uint8_t interruptFlags;/*!< The last value of interrupts flags */ + uint8_t pktCnt; /*!< The number of pending receive packets */ + uint16_t nextpkt; /*!< Next packet address */ + uint16_t LinkStatus; /*!< Ethernet link status */ + uint16_t transmitLength;/*!< The length of ip frame to transmit */ + uint32_t startTime; /*!< The start time of the current timer */ + uint32_t duration; /*!< The duration of the current timer in ms */ + uint16_t retries; /*!< The number of transmission retries left to do */ + + ENC_RxFrameInfos RxFrameInfos; /*!< last Rx frame infos */ +} ENC_HandleTypeDef; + + /** + * @} + */ + +/* Exported constants --------------------------------------------------------*/ +/** @defgroup ETH_Exported_Constants ETH Exported Constants + * @{ + */ +/* Size of the Ethernet header */ +#define ETH_HDRLEN 14 /* Minimum size: 2*6 + 2 */ + + +#define ERR_OK 0 /* No error, everything OK. */ +#define ERR_MEM -1 /* Out of memory error. */ +#define ERR_BUF -2 /* Buffer error. */ +#define ERR_TIMEOUT -3 /* Timeout. */ + +/* ENC28J60 Commands ********************************************************/ +/* A total of seven instructions are implemented on the ENC28J60. Where: + * + * aaaaaa is the 5-bit address of a control register, and + * dddddddd is one or more bytes of data that may accompany the command. + */ + +#define ENC_RCR (0x00) /* Read Control Register + * 000 | aaaaa | (Register value returned)) */ +#define ENC_RBM (0x3a) /* Read Buffer Memory + * 001 | 11010 | (Read buffer data follows) */ +#define ENC_WCR (0x40) /* Write Control Register + * 010 | aaaaa | dddddddd */ +#define ENC_WBM (0x7a) /* Write Buffer Memory + * 011 | 11010 | (Write buffer data follows) */ +#define ENC_BFS (0x80) /* Bit Field Set + * 100 | aaaaa | dddddddd */ +#define ENC_BFC (0xa0) /* Bit Field Clear + * 101 | aaaaa | dddddddd */ +#define ENC_SRC (0xff) /* System Reset + * 111 | 11111 | (No data) */ + +/* Global Control Registers *************************************************/ +/* Control registers are accessed with the RCR, RBM, WCR, BFS, and BFC + * commands. The following identifies all ENC28J60 control registers. The + * control register memory is partitioned into four banks, selectable by the + * bank select bits, BSEL1:BSEL0, in the ECON1 register. + * + * The last five locations (0x1b to 0x1f) of all banks point to a common set + * of registers: EIE, EIR, ESTAT, ECON2 and ECON1. These are key registers + * used in controlling and monitoring the operation of the device. Their + * common mapping allows easy access without switching the bank. + * + * Control registers for the ENC28J60 are generically grouped as ETH, MAC and + * MII registers. Register names starting with E belong to the ETH group. + * Similarly, registers names starting with MA belong to the MAC group and + * registers prefixed with MI belong to the MII group. + */ + +#define ENC_EIE (0x1b) /* Ethernet Interrupt Enable Register */ +#define ENC_EIR (0x1c) /* Ethernet Interupt Request Register */ +#define ENC_ESTAT (0x1d) /* Ethernet Status Register */ +#define ENC_ECON2 (0x1e) /* Ethernet Control 2 Register */ +#define ENC_ECON1 (0x1f) /* Ethernet Control 1 Register */ + +/* Ethernet Interrupt Enable Register Bit Definitions */ + +#define EIE_RXERIE (1 << 0) /* Bit 0: Receive Error Interrupt Enable */ +#define EIE_TXERIE (1 << 1) /* Bit 1: Transmit Error Interrupt Enable */ + /* Bit 2: Reserved */ +#define EIE_TXIE (1 << 3) /* Bit 3: Transmit Enable */ +#define EIE_LINKIE (1 << 4) /* Bit 4: Link Status Change Interrupt Enable */ +#define EIE_DMAIE (1 << 5) /* Bit 5: DMA Interrupt Enable */ +#define EIE_PKTIE (1 << 6) /* Bit 6: Receive Packet Pending Interrupt Enable */ +#define EIE_INTIE (1 << 7) /* Bit 7: Global INT Interrupt Enable */ + +/* Ethernet Interrupt Request Register Bit Definitions */ + +#define EIR_RXERIF (1 << 0) /* Bit 0: Receive Error Interrupt */ +#define EIR_TXERIF (1 << 1) /* Bit 1: Transmit Error Interrupt */ + /* Bit 2: Reserved */ +#define EIR_TXIF (1 << 3) /* Bit 3: Transmit Interrupt */ +#define EIR_LINKIF (1 << 4) /* Bit 4: Link Change Interrupt */ +#define EIR_DMAIF (1 << 5) /* Bit 5: DMA Interrupt */ +#define EIR_PKTIF (1 << 6) /* Bit 6: Receive Packet Pending Interrupt */ + /* Bit 7: Reserved */ +#define EIR_ALLINTS (EIR_RXERIF | EIR_TXERIF | EIR_TXIF | EIR_LINKIF | EIR_DMAIF) /* All interrupts bar EIR_PKTIF*/ + +/* Ethernet Status Register Bit Definitions */ + +#define ESTAT_CLKRDY (1 << 0) /* Bit 0: Clock Ready */ +#define ESTAT_TXABRT (1 << 1) /* Bit 1: Transmit Abort Error */ +#define ESTAT_RXBUSY (1 << 2) /* Bit 2: Receive Busy */ + /* Bit 3: Reserved */ +#define ESTAT_LATECOL (1 << 4) /* Bit 4: Late Collision Error */ + /* Bit 5: Reserved */ +#define ESTAT_BUFER (1 << 6) /* Bit 6: Ethernet Buffer Error Status */ +#define ESTAT_INT (1 << 7) /* Bit 7: INT Interrupt */ + +/* Ethernet Control 1 Register Bit Definitions */ + +#define ECON1_BSEL_SHIFT (0) /* Bits 0-1: Bank select */ +#define ECON1_BSEL_MASK (3 << ECON1_BSEL_SHIFT) +# define ECON1_BSEL_BANK0 (0 << ECON1_BSEL_SHIFT) /* Bank 0 */ +# define ECON1_BSEL_BANK1 (1 << ECON1_BSEL_SHIFT) /* Bank 1 */ +# define ECON1_BSEL_BANK2 (2 << ECON1_BSEL_SHIFT) /* Bank 2 */ +# define ECON1_BSEL_BANK3 (3 << ECON1_BSEL_SHIFT) /* Bank 3 */ +#define ECON1_RXEN (1 << 2) /* Bit 2: Receive Enable */ +#define ECON1_TXRTS (1 << 3) /* Bit 3: Transmit Request to Send */ +#define ECON1_CSUMEN (1 << 4) /* Bit 4: DMA Checksum Enable */ +#define ECON1_DMAST (1 << 5) /* Bit 5: DMA Start and Busy Status */ +#define ECON1_RXRST (1 << 6) /* Bit 6: Receive Logic Reset */ +#define ECON1_TXRST (1 << 7) /* Bit 7: Transmit Logic Reset */ + +/* Ethernet Control 2 Register */ + /* Bits 0-2: Reserved */ +#define ECON2_VRPS (1 << 3) /* Bit 3: Voltage Regulator Power Save Enable */ + /* Bit 4: Reserved */ +#define ECON2_PWRSV (1 << 5) /* Bit 5: Power Save Enable */ +#define ECON2_PKTDEC (1 << 6) /* Bit 6: Packet Decrement */ +#define ECON2_AUTOINC (1 << 7) /* Bit 7: Automatic Buffer Pointer Increment Enable */ + +/* Banked Control Registers *************************************************/ +/* The remaining control registers are identified with a a 5 bit address and + * a bank selection. We pack the bank number and an indication if this is + * a MAC/PHY register access together with the control register address + * together to keep the design simpler. + */ + +#define ENC_ADDR_SHIFT (0) /* Bits 0-4: Register address */ +#define ENC_ADDR_MASK (0x1f << ENC_ADDR_SHIFT) +#define ENC_BANK_SHIFT (5) /* Bits 5-6: Bank number */ +#define ENC_BANK_MASK (3 << ENC_BSEL_SHIFT) +# define ENC_BANK0 (0 << ENC_BSEL_SHIFT) +# define ENC_BANK1 (1 << ENC_BSEL_SHIFT) +# define ENC_BANK2 (2 << ENC_BSEL_SHIFT) +# define ENC_BANK3 (3 << ENC_BSEL_SHIFT) +#define ENC_PHYMAC_SHIFT (7) /* Bit 7: This is a PHY/MAC command */ +#define ENC_PHYMAC (1 << ENC_PHYMAC_SHIFT) + +#define REGADDR(a,b,m) ((m) << ENC_PHYMAC_SHIFT | (b) << ENC_BANK_SHIFT | (a)) +#define GETADDR(a) ((a) & ENC_ADDR_MASK) +#define GETBANK(a) (((a) >> ENC_BANK_SHIFT) & 3) +#define ISPHYMAC(a) (((a) & ENC_PHYMAC) != 0) + +/* Bank 0 Control Register Addresses */ + +#define ENC_ERDPTL REGADDR(0x00, 0, 0) /* Read Pointer Low Byte (ERDPT<7:0> */ +#define ENC_ERDPTH REGADDR(0x01, 0, 0) /* Read Pointer High Byte (ERDPT<12:8>) */ +#define ENC_EWRPTL REGADDR(0x02, 0, 0) /* Write Pointer Low Byte (EWRPT<7:0>) */ +#define ENC_EWRPTH REGADDR(0x03, 0, 0) /* Write Pointer High Byte (EWRPT<12:8>) */ +#define ENC_ETXSTL REGADDR(0x04, 0, 0) /* TX Start Low Byte (ETXST<7:0>) */ +#define ENC_ETXSTH REGADDR(0x05, 0, 0) /* TX Start High Byte (ETXST<12:8>) */ +#define ENC_ETXNDL REGADDR(0x06, 0, 0) /* TX End Low Byte (ETXND<7:0>) */ +#define ENC_ETXNDH REGADDR(0x07, 0, 0) /* TX End High Byte (ETXND<12:8>) */ +#define ENC_ERXSTL REGADDR(0x08, 0, 0) /* RX Start Low Byte (ERXST<7:0>) */ +#define ENC_ERXSTH REGADDR(0x09, 0, 0) /* RX Start High Byte (ERXST<12:8>) */ +#define ENC_ERXNDL REGADDR(0x0a, 0, 0) /* RX End Low Byte (ERXND<7:0>) */ +#define ENC_ERXNDH REGADDR(0x0b, 0, 0) /* RX End High Byte (ERXND<12:8>) */ +#define ENC_ERXRDPTL REGADDR(0x0c, 0, 0) /* RX RD Pointer Low Byte (ERXRDPT<7:0>) */ +#define ENC_ERXRDPTH REGADDR(0x0d, 0, 0) /* RX RD Pointer High Byte (ERXRDPT<12:8>) */ +#define ENC_ERXWRPTL REGADDR(0x0e, 0, 0) /* RX WR Pointer Low Byte (ERXWRPT<7:0>) */ +#define ENC_ERXWRPTH REGADDR(0x0f, 0, 0) /* RX WR Pointer High Byte (ERXWRPT<12:8>) */ +#define ENC_EDMASTL REGADDR(0x10, 0, 0) /* DMA Start Low Byte (EDMAST<7:0>) */ +#define ENC_EDMASTH REGADDR(0x11, 0, 0) /* DMA Start High Byte (EDMAST<12:8>) */ +#define ENC_EDMANDL REGADDR(0x12, 0, 0) /* DMA End Low Byte (EDMAND<7:0>) */ +#define ENC_EDMANDH REGADDR(0x13, 0, 0) /* DMA End High Byte (EDMAND<12:8>) */ +#define ENC_EDMADSTL REGADDR(0x14, 0, 0) /* DMA Destination Low Byte (EDMADST<7:0>) */ +#define ENC_EDMADSTH REGADDR(0x15, 0, 0) /* DMA Destination High Byte (EDMADST<12:8>) */ +#define ENC_EDMACSL REGADDR(0x16, 0, 0) /* DMA Checksum Low Byte (EDMACS<7:0>) */ +#define ENC_EDMACSH REGADDR(0x17, 0, 0) /* DMA Checksum High Byte (EDMACS<15:8>) */ + /* 0x18-0x1a: Reserved */ + /* 0x1b-0x1f: EIE, EIR, ESTAT, ECON2, ECON1 */ +/* Bank 1 Control Register Addresses */ + +#define ENC_EHT0 REGADDR(0x00, 1, 0) /* Hash Table Byte 0 (EHT<7:0>) */ +#define ENC_EHT1 REGADDR(0x01, 1, 0) /* Hash Table Byte 1 (EHT<15:8>) */ +#define ENC_EHT2 REGADDR(0x02, 1, 0) /* Hash Table Byte 2 (EHT<23:16>) */ +#define ENC_EHT3 REGADDR(0x03, 1, 0) /* Hash Table Byte 3 (EHT<31:24>) */ +#define ENC_EHT4 REGADDR(0x04, 1, 0) /* Hash Table Byte 4 (EHT<39:32>) */ +#define ENC_EHT5 REGADDR(0x05, 1, 0) /* Hash Table Byte 5 (EHT<47:40>) */ +#define ENC_EHT6 REGADDR(0x06, 1, 0) /* Hash Table Byte 6 (EHT<55:48>) */ +#define ENC_EHT7 REGADDR(0x07, 1, 0) /* Hash Table Byte 7 (EHT<63:56>) */ +#define ENC_EPMM0 REGADDR(0x08, 1, 0) /* Pattern Match Mask Byte 0 (EPMM<7:0>) */ +#define ENC_EPMM1 REGADDR(0x09, 1, 0) /* Pattern Match Mask Byte 1 (EPMM<15:8>) */ +#define ENC_EPMM2 REGADDR(0x0a, 1, 0) /* Pattern Match Mask Byte 2 (EPMM<23:16>) */ +#define ENC_EPMM3 REGADDR(0x0b, 1, 0) /* Pattern Match Mask Byte 3 (EPMM<31:24>) */ +#define ENC_EPMM4 REGADDR(0x0c, 1, 0) /* Pattern Match Mask Byte 4 (EPMM<39:32>) */ +#define ENC_EPMM5 REGADDR(0x0d, 1, 0) /* Pattern Match Mask Byte 5 (EPMM<47:40>) */ +#define ENC_EPMM6 REGADDR(0x0e, 1, 0) /* Pattern Match Mask Byte 6 (EPMM<55:48>) */ +#define ENC_EPMM7 REGADDR(0x0f, 1, 0) /* Pattern Match Mask Byte 7 (EPMM<63:56>) */ +#define ENC_EPMCSL REGADDR(0x10, 1, 0) /* Pattern Match Checksum Low Byte (EPMCS<7:0>) */ +#define ENC_EPMCSH REGADDR(0x11, 1, 0) /* Pattern Match Checksum High Byte (EPMCS<15:0>) */ + /* 0x12-0x13: Reserved */ +#define ENC_EPMOL REGADDR(0x14, 1, 0) /* Pattern Match Offset Low Byte (EPMO<7:0>) */ +#define ENC_EPMOH REGADDR(0x15, 1, 0) /* Pattern Match Offset High Byte (EPMO<12:8>) */ + /* 0x16-0x17: Reserved */ +#define ENC_ERXFCON REGADDR(0x18, 1, 0) /* Receive Filter Configuration */ +#define ENC_EPKTCNT REGADDR(0x19, 1, 0) /* Ethernet Packet Count */ + /* 0x1a: Reserved */ + /* 0x1b-0x1f: EIE, EIR, ESTAT, ECON2, ECON1 */ + +/* Receive Filter Configuration Bit Definitions */ + +#define ERXFCON_BCEN (1 << 0) /* Bit 0: Broadcast Filter Enable */ +#define ERXFCON_MCEN (1 << 1) /* Bit 1: Multicast Filter Enable */ +#define ERXFCON_HTEN (1 << 2) /* Bit 2: Hash Table Filter Enable */ +#define ERXFCON_MPEN (1 << 3) /* Bit 3: Magic Packet Filter Enable */ +#define ERXFCON_PMEN (1 << 4) /* Bit 4: Pattern Match Filter Enable */ +#define ERXFCON_CRCEN (1 << 5) /* Bit 5: Post-Filter CRC Check Enable */ +#define ERXFCON_ANDOR (1 << 6) /* Bit 6: AND/OR Filter Select */ +#define ERXFCON_UCEN (1 << 7) /* Bit 7: Unicast Filter Enable */ + +/* Bank 2 Control Register Addresses */ + +#define ENC_MACON1 REGADDR(0x00, 2, 1) /* MAC Control 1 */ + /* 0x01: Reserved */ +#define ENC_MACON3 REGADDR(0x02, 2, 1) /* MAC Control 3 */ +#define ENC_MACON4 REGADDR(0x03, 2, 1) /* MAC Control 4 */ +#define ENC_MABBIPG REGADDR(0x04, 2, 1) /* Back-to-Back Inter-Packet Gap (BBIPG<6:0>) */ + /* 0x05: Reserved */ +#define ENC_MAIPGL REGADDR(0x06, 2, 1) /* Non-Back-to-Back Inter-Packet Gap Low Byte (MAIPGL<6:0>) */ +#define ENC_MAIPGH REGADDR(0x07, 2, 1) /* Non-Back-to-Back Inter-Packet Gap High Byte (MAIPGH<6:0>) */ +#define ENC_MACLCON1 REGADDR(0x08, 2, 1) /* MAC Collision Control 1 */ +#define ENC_MACLCON2 REGADDR(0x09, 2, 1) /* MAC Collision Control 2 */ +#define ENC_MAMXFLL REGADDR(0x0a, 2, 1) /* Maximum Frame Length Low Byte (MAMXFL<7:0>) */ +#define ENC_MAMXFLH REGADDR(0x0b, 2, 1) /* Maximum Frame Length High Byte (MAMXFL<15:8>) */ + /* 0x0c-0x11: Reserved */ +#define ENC_MICMD REGADDR(0x12, 2, 1) /* MII Command Register */ + /* 0x13: Reserved */ +#define ENC_MIREGADR REGADDR(0x14, 2, 1) /* MII Register Address */ + /* 0x15: Reserved */ +#define ENC_MIWRL REGADDR(0x16, 2, 1) /* MII Write Data Low Byte (MIWR<7:0>) */ +#define ENC_MIWRH REGADDR(0x17, 2, 1) /* MII Write Data High Byte (MIWR<15:8>) */ +#define ENC_MIRDL REGADDR(0x18, 2, 1) /* MII Read Data Low Byte (MIRD<7:0>) */ +#define ENC_MIRDH REGADDR(0x19, 2, 1) /* MII Read Data High Byte(MIRD<15:8>) */ + /* 0x1a: Reserved */ + /* 0x1b-0x1f: EIE, EIR, ESTAT, ECON2, ECON1 */ + +/* MAC Control 1 Register Bit Definitions */ + +#define MACON1_MARXEN (1 << 0) /* Bit 0: MAC Receive Enable */ +#define MACON1_PASSALL (1 << 1) /* Bit 1: Pass All Received Frames Enable */ +#define MACON1_RXPAUS (1 << 2) /* Bit 2: Pause Control Frame Reception Enable */ +#define MACON1_TXPAUS (1 << 3) /* Bit 3: Pause Control Frame Transmission Enable */ + /* Bits 4-7: Unimplemented or reserved */ + +/* MAC Control 1 Register Bit Definitions */ + +#define MACON3_FULDPX (1 << 0) /* Bit 0: MAC Full-Duplex Enable */ +#define MACON3_FRMLNEN (1 << 1) /* Bit 1: Frame Length Checking Enable */ +#define MACON3_HFRMLEN (1 << 2) /* Bit 2: Huge Frame Enable */ +#define MACON3_PHDRLEN (1 << 3) /* Bit 3: Proprietary Header Enable */ +#define MACON3_TXCRCEN (1 << 4) /* Bit 4: Transmit CRC Enable */ +#define MACON3_PADCFG0 (1 << 5) /* Bit 5: Automatic Pad and CRC Configuration */ +#define MACON3_PADCFG1 (1 << 6) /* Bit 6: " " " " " " " " " " */ +#define MACON3_PADCFG2 (1 << 7) /* Bit 7: " " " " " " " " " " */ + +/* MAC Control 1 Register Bit Definitions */ + +#define MACON4_NOBKOFF (1 << 4) /* Bit 4: No Backoff Enable */ +#define MACON4_BPEN (1 << 5) /* Bit 5: No Backoff During Backpressure Enable */ +#define MACON4_DEFER (1 << 6) /* Bit 6: Defer Transmission Enable bit */ + +/* MII Command Register Bit Definitions */ + +#define MICMD_MIIRD (1 << 0) /* Bit 0: MII Read Enable */ +#define MICMD_MIISCAN (1 << 1) /* Bit 1: MII Scan Enable */ + +/* Bank 3 Control Register Addresses */ + +#define ENC_MAADR5 REGADDR(0x00, 3, 1) /* MAC Address Byte 5 (MAADR<15:8>) */ +#define ENC_MAADR6 REGADDR(0x01, 3, 1) /* MAC Address Byte 6 (MAADR<7:0>) */ +#define ENC_MAADR3 REGADDR(0x02, 3, 1) /* MAC Address Byte 3 (MAADR<31:24>), OUI Byte 3 */ +#define ENC_MAADR4 REGADDR(0x03, 3, 1) /* MAC Address Byte 4 (MAADR<23:16>) */ +#define ENC_MAADR1 REGADDR(0x04, 3, 1) /* MAC Address Byte 1 (MAADR<47:40>), OUI Byte 1 */ +#define ENC_MAADR2 REGADDR(0x05, 3, 1) /* MAC Address Byte 2 (MAADR<39:32>), OUI Byte 2 */ +#define ENC_EBSTSD REGADDR(0x06, 3, 0) /* Built-in Self-Test Fill Seed (EBSTSD<7:0>) */ +#define ENC_EBSTCON REGADDR(0x07, 3, 0) /* Built-in Self-Test Control */ +#define ENC_EBSTCSL REGADDR(0x08, 3, 0) /* Built-in Self-Test Checksum Low Byte (EBSTCS<7:0>) */ +#define ENC_EBSTCSH REGADDR(0x09, 3, 0) /* Built-in Self-Test Checksum High Byte (EBSTCS<15:8>) */ +#define ENC_MISTAT REGADDR(0x0a, 3, 1) /* MII Status Register */ + /* 0x0b-0x11: Reserved */ +#define ENC_EREVID REGADDR(0x12, 3, 0) /* Ethernet Revision ID */ + /* 0x13-0x14: Reserved */ +#define ENC_ECOCON REGADDR(0x15, 3, 0) /* Clock Output Control */ + /* 0x16: Reserved */ +#define ENC_EFLOCON REGADDR(0x17, 3, 0) /* Ethernet Flow Control */ +#define ENC_EPAUSL REGADDR(0x18, 3, 0) /* Pause Timer Value Low Byte (EPAUS<7:0>) */ +#define ENC_EPAUSH REGADDR(0x19, 3, 0) /* Pause Timer Value High Byte (EPAUS<15:8>) */ + /* 0x1a: Reserved */ + /* 0x1b-0x1f: EIE, EIR, ESTAT, ECON2, ECON1 */ + +/* Built-in Self-Test Control Register Bit Definitions */ + +#define EBSTCON_BISTST (1 << 0) /* Bit 0: Built-in Self-Test Start/Busy */ +#define EBSTCON_TME (1 << 1) /* Bit 1: Test Mode Enable */ +#define EBSTCON_TMSEL0 (1 << 2) /* Bit 2: Test Mode Select */ +#define EBSTCON_TMSEL1 (1 << 3) /* Bit 3: " " " " " " */ +#define EBSTCON_PSEL (1 << 4) /* Bit 4: Port Select */ +#define EBSTCON_PSV0 (1 << 5) /* Bit 5: Pattern Shift Value */ +#define EBSTCON_PSV1 (1 << 6) /* Bit 6: " " " " " */ +#define EBSTCON_PSV2 (1 << 7) /* Bit 7: " " " " " */ + +/* MII Status Register Register Bit Definitions */ + +#define MISTAT_BUSY (1 << 0) /* Bit 0: MII Management Busy */ +#define MISTAT_SCAN (1 << 1) /* Bit 1: MII Management Scan Operation */ +#define MISTAT_NVALID (1 << 2) /* Bit 2: MII Management Read Data Not Valid */ + /* Bits 3-7: Reserved or unimplemented */ + +/* Ethernet Flow Control Register Bit Definitions */ + +#define EFLOCON_FCEN0 (1 << 0) /* Bit 0: Flow Control Enable */ +#define EFLOCON_FCEN1 (1 << 1) /* Bit 1: " " " " " " */ +#define EFLOCON_FULDPXS (1 << 2) /* Bit 2: Read-Only MAC Full-Duplex Shadow */ + /* Bits 3-7: Reserved or unimplemented */ + +/* PHY Registers ************************************************************/ + +#define ENC_PHCON1 (0x00) /* PHY Control Register 1 */ +#define ENC_PHSTAT1 (0x01) /* PHY Status 1 */ +#define ENC_PHID1 (0x02) /* PHY ID Register 1 */ +#define ENC_PHID2 (0x03) /* PHY ID Register 2 */ +#define ENC_PHCON2 (0x10) /* PHY Control Register 2 */ +#define ENC_PHSTAT2 (0x11) /* PHY Status 2 */ +#define ENC_PHIE (0x12) /* PHY Interrupt Enable Register */ +#define ENC_PHIR (0x13) /* PHY Interrupt Request Register */ +#define ENC_PHLCON (0x14) + +/* PHY Control Register 1 Register Bit Definitions */ + +#define PHCON1_PDPXMD (1 << 8) /* Bit 8: PHY Duplex Mode */ +#define PHCON1_PPWRSV (1 << 11) /* Bit 11: PHY Power-Down */ +#define PHCON1_PLOOPBK (1 << 14) /* Bit 14: PHY Loopback */ +#define PHCON1_PRST (1 << 15) /* Bit 15: PHY Software Reset */ + +/* PHY Status 1 Register Bit Definitions */ + +#define PHSTAT1_JBSTAT (1 << 1) /* Bit 1: PHY Latching Jabber Status */ +#define PHSTAT1_LLSTAT (1 << 2) /* Bit 2: PHY Latching Link Status */ +#define PHSTAT1_PHDPX (1 << 11) /* Bit 11: PHY Half-Duplex Capable */ +#define PHSTAT1_PFDPX (1 << 12) /* Bit 12: PHY Full-Duplex Capable */ + +/* PHY Control Register 2 Register Bit Definitions */ + +#define PHCON2_HDLDIS (1 << 8) /* Bit 8: PHY Half-Duplex Loopback Disable */ +#define PHCON2_JABBER (1 << 10) /* Bit 10: Jabber Correction Disable */ +#define PHCON2_TXDIS (1 << 13) /* Bit 13: Twisted-Pair Transmitter Disable */ +#define PHCON2_FRCLINK (1 << 14) /* Bit 14: PHY Force Linkup */ + +/* PHY Status 2 Register Bit Definitions */ + +#define PHSTAT2_PLRITY (1 << 5) /* Bit 5: Polarity Status */ +#define PHSTAT2_DPXSTAT (1 << 9) /* Bit 9: PHY Duplex Status */ +#define PHSTAT2_LSTAT (1 << 10) /* Bit 10: PHY Link Status */ +#define PHSTAT2_COLSTAT (1 << 11) /* Bit 11: PHY Collision Status */ +#define PHSTAT2_RXSTAT (1 << 12) /* Bit 12: PHY Receive Status */ +#define PHSTAT2_TXSTAT (1 << 13) /* Bit 13: PHY Transmit Status */ + +/* PHY Interrupt Enable Register Bit Definitions */ + +#define PHIE_PGEIE (1 << 1) /* Bit 1: PHY Global Interrupt Enable */ +#define PHIE_PLNKIE (1 << 4) /* Bit 4: PHY Link Change Interrupt Enable */ + +/* PHIR Regiser Bit Definitions */ + +#define PHIR_PGIF (1 << 2) /* Bit 2: PHY Global Interrupt */ +#define PHIR_PLNKIF (1 << 4) /* Bit 4: PHY Link Change Interrupt */ + +/* PHLCON Regiser Bit Definitions */ + /* Bit 0: Reserved */ +#define PHLCON_STRCH (1 << 1) /* Bit 1: LED Pulse Stretching Enable */ +#define PHLCON_LFRQ0 (1 << 2) /* Bit 2: LED Pulse Stretch Time Configuration */ +#define PHLCON_LFRQ1 (1 << 3) /* Bit 3: " " " " " " " " " */ +#define PHLCON_LBCFG0 (1 << 4) /* Bit 4: LEDB Configuration */ +#define PHLCON_LBCFG1 (1 << 5) /* Bit 5: " " " " */ +#define PHLCON_LBCFG2 (1 << 6) /* Bit 6: " " " " */ +#define PHLCON_LBCFG3 (1 << 7) /* Bit 7: " " " " */ +#define PHLCON_LACFG0 (1 << 8) /* Bit 8: LEDA Configuration */ +#define PHLCON_LACFG1 (1 << 9) /* Bit 9: " " " " */ +#define PHLCON_LACFG2 (1 << 10) /* Bit 10: " " " " */ +#define PHLCON_LACFG3 (1 << 11) /* Bit 11: " " " " */ + +/* Packet Memory ************************************************************/ + +/* 8-Kbyte Transmit/Receive Packet Dual Port SRAM */ + +#define PKTMEM_START 0x0000 +#define PKTMEM_END 0x1fff + +/* maximum transfer unit */ +#define CONFIG_NET_ETH_MTU 1500 + +/* Packet Control Bits Definitions ******************************************/ + +#define PKTCTRL_POVERRIDE (1 << 0) /* Bit 0: Per Packet Override */ +#define PKTCTRL_PCRCEN (1 << 1) /* Bit 1: Per Packet CRC Enable */ +#define PKTCTRL_PPADEN (1 << 2) /* Bit 2: Per Packet Padding Enable */ +#define PKTCTRL_PHUGEEN (1 << 3) /* Bit 3: Per Packet Huge Frame Enable */ + +/* RX Status Bit Definitions ************************************************/ + +#define RXSTAT_LDEVENT (1 << 0) /* Bit 0: Long event or pack dropped */ + /* Bit 1: Reserved */ +#define RXSTAT_CEPS (1 << 2) /* Bit 2: Carrier event previously seen */ + /* Bit 3: Reserved */ +#define RXSTAT_CRCERROR (1 << 4) /* Bit 4: Frame CRC field bad */ +#define RXSTAT_LENERROR (1 << 5) /* Bit 5: Packet length != data length */ +#define RXSTAT_LENRANGE (1 << 6) /* Bit 6: Type/length field > 1500 bytes */ +#define RXSTAT_OK (1 << 7) /* Bit 7: Packet with valid CRC and no symbol errors */ +#define RXSTAT_MCAST (1 << 8) /* Bit 8: Packet with multicast address */ +#define RXSTAT_BCAST (1 << 9) /* Bit 9: Packet with broadcast address */ +#define RXSTAT_DRIBBLE (1 << 10) /* Bit 10: Additional bits received after packet */ +#define RXSTAT_CTRLFRAME (1 << 11) /* Bit 11: Control frame with valid type/length */ +#define RXSTAT_PAUSE (1 << 12) /* Bit 12: Control frame with pause frame opcde */ +#define RXSTAT_UNKOPCODE (1 << 13) /* Bit 13: Control frame with unknown opcode */ +#define RXSTAT_VLANTYPE (1 << 14) /* Bit 14: Current frame is a VLAN tagged frame */ + /* Bit 15: Zero */ +/* TSV bit definitions */ +#define TSV_LATECOL (1 << 5) /* Bit 5: Late Collision Error, RSV byte 3 */ + + + +/** @defgroup ETH_Duplex_Mode ETH Duplex Mode + * @{ + */ +#define ETH_MODE_FULLDUPLEX ((uint32_t)0x00000800) +#define ETH_MODE_HALFDUPLEX ((uint32_t)0x00000000) +/** + * @} + */ + +/** @defgroup ETH_Rx_Mode ETH Rx Mode + * @{ + */ +#define ETH_RXPOLLING_MODE ((uint32_t)0x00000000) +#define ETH_RXINTERRUPT_MODE ((uint32_t)0x00000001) +/** + * @} + */ + +/** @defgroup ETH_Checksum_Mode ETH Checksum Mode + * @{ + */ +#define ETH_CHECKSUM_BY_HARDWARE ((uint32_t)0x00000000) +#define ETH_CHECKSUM_BY_SOFTWARE ((uint32_t)0x00000001) +/** + * @} + */ + +/* Exported functions --------------------------------------------------------*/ +/** @addtogroup SPI_Exported_Functions + * @{ + */ + +/** @addtogroup SPI_Exported_Functions_Group1 + * @{ + */ +/* Initialization/de-initialization functions **********************************/ + + +/** + * Initialize the enc28j60 and configure the needed hardware resources + * param handle: Handle on data configuration. + * retval None + */ + +bool ENC_Start(ENC_HandleTypeDef *handle); + +/**************************************************************************** + * Function: ENC_SetMacAddr + * + * Description: + * Set the MAC address to the configured value. This is done after ifup + * or after a TX timeout. Note that this means that the interface must + * be down before configuring the MAC addr. + * + * Parameters: + * handle - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +void ENC_SetMacAddr(ENC_HandleTypeDef *handle); + +/**************************************************************************** + * Function: ENC_RestoreTXBuffer + * + * Description: + * Prepare TX buffer + * + * Parameters: + * handle - Reference to the driver state structure + * len - length of buffer + * + * Returned Value: + * ERR_OK 0 No error, everything OK. + * ERR_MEM -1 Out of memory error. + * ERR_TIMEOUT -3 Timeout. + * + * Assumptions: + * + ****************************************************************************/ + +int8_t ENC_RestoreTXBuffer(ENC_HandleTypeDef *handle, uint16_t len); + +/**************************************************************************** + * Function: ENC_WriteBuffer + * + * Description: + * Write a buffer of data. + * + * Parameters: + * buffer - A pointer to the buffer to write from + * buflen - The number of bytes to write + * + * Returned Value: + * None + * + * Assumptions: + * Read pointer is set to the correct address + * + ****************************************************************************/ + +void ENC_WriteBuffer(void *buffer, uint16_t buflen); + +/**************************************************************************** + * Function: ENC_Transmit + * + * Description: + * Start hardware transmission. Called either from: + * + * - pkif interrupt when an application responds to the receipt of data + * by trying to send something, or + * - From watchdog based polling. + * + * Parameters: + * handle - Reference to the driver state structure + * + * Returned Value: + * none + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef USE_PROTOTHREADS +PT_THREAD(ENC_Transmit(struct pt *pt, ENC_HandleTypeDef *handle)); +#else +void ENC_Transmit(ENC_HandleTypeDef *handle); +#endif + + /** + * @} + */ + + /** + * @} + */ + +/**************************************************************************** + * Function: ENC_GetReceivedFrame + * + * Description: + * Check if we have received packet, and if so, retrive them. + * + * Parameters: + * handle - Reference to the driver state structure + * + * Returned Value: + * true if new packet is available; false otherwise + * + * Assumptions: + * + ****************************************************************************/ + +bool ENC_GetReceivedFrame(ENC_HandleTypeDef *handle); + +/**************************************************************************** + * Function: ENC_IRQHandler + * + * Description: + * Perform interrupt handling logic outside of the interrupt handler (on + * the work queue thread). + * + * Parameters: + * handle - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +void ENC_IRQHandler(ENC_HandleTypeDef *handle); + +/**************************************************************************** + * Function: ENC_EnableInterrupts + * + * Description: + * Enable individual ENC28J60 interrupts + * + * Parameters: + * bits - The individual bits to enable + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +void ENC_EnableInterrupts(uint8_t bits); + +/**************************************************************************** + * Function: ENC_GetPkcnt + * + * Description: + * Get the number of pending receive packets + * + * Parameters: + * handle - Reference to the driver state structure + * + * Returned Value: + * the number of receive packet not processed yet + * + * Assumptions: + * + ****************************************************************************/ + +void ENC_GetPkcnt(ENC_HandleTypeDef *handle); + + +/**************************************************************************** + * Function: up_udelay + * + * Description: + * wait us µs + * + * Parameters: + * us - The amount of time to wait in µs + * + * Returned Value: + * none + * + * Assumptions: + * + ****************************************************************************/ + +__inline void up_udelay(uint32_t us); + +#endif /* ENC28J60_H_INCLUDED */ diff --git a/part14-ethernet/net/encspi.c b/part14-ethernet/net/encspi.c new file mode 100644 index 0000000..524f73f --- /dev/null +++ b/part14-ethernet/net/encspi.c @@ -0,0 +1,21 @@ +#include "../include/spi.h" + +void ENC_SPI_Select(unsigned char truefalse) { + spi_chip_select(!truefalse); // If it's true, select 0 (the ENC), if false, select 1 (i.e. deselect the ENC) +} + +void ENC_SPI_SendBuf(unsigned char *master2slave, unsigned char *slave2master, unsigned short bufferSize) { + spi_chip_select(0); + spi_send_recv(master2slave, slave2master, bufferSize); + spi_chip_select(1); // De-select the ENC +} + +void ENC_SPI_Send(unsigned char command) { + spi_chip_select(0); + spi_send(&command, 1); + spi_chip_select(1); // De-select the ENC +} + +void ENC_SPI_SendWithoutSelection(unsigned char command) { + spi_send(&command, 1); +}