Blame
|
1 | # NetBSD Port |
||||||
|
2 | |||||||
| 3 | > [!WARNING] |
|||||||
| 4 | > This page is very much a work in progress! |
|||||||
| 5 | ||||||||
| 6 | ||||||||
| 7 | ## Building |
|||||||
| 8 | I've forked a copy of the NetBSD sources. |
|||||||
| 9 | ### Kernel |
|||||||
| 10 | NetBSD contains a port called **hb68k** which is designed for any 680x0 sbc type of board and I used that to create a variant for my SBC, which I've called **SBC3**. |
|||||||
| 11 | ||||||||
| 12 | ``` |
|||||||
| 13 | git clone https://github.com/znac049/netbsd-src.git |
|||||||
| 14 | cd netbsd-src |
|||||||
|
15 | ./build.sh -U -u -j4 -O ~/obj -m hb68k tools |
||||||
|
16 | ./build.sh -U -u -j4 -O ~/obj -m hb68k kernel=SBC3 |
||||||
| 17 | ./build.sh -U -u -j4 -O ~/obj -m hb68k distribution |
|||||||
| 18 | ``` |
|||||||
| 19 | ||||||||
|
20 | - The first step builds the tools (compiler etc) that will be used to build the kernel and userspace. |
||||||
| 21 | - The next step builds the kernel. |
|||||||
|
22 | - The final step takes a Long Time(tm) and builds the userspace, which will need to be added to the root filesystem. |
||||||
|
23 | |||||||
| 24 | I've still got to document how to build the root filesystem. |
|||||||
|
25 | |||||||
| 26 | ## Device Driver Templates |
|||||||
| 27 | I asked Claude about templates for coding device drivers. Here's what it said: |
|||||||
| 28 | ||||||||
| 29 | ### Uart |
|||||||
| 30 | The template is myuart.c. It’s a complete single-channel UART driver for hb68k: it attaches through the device tree, can be the system console, and gives you a normal tty with interrupt-driven I/O. |
|||||||
| 31 | ||||||||
| 32 | What you change: the two CHIP-SPECIFIC sections near the top |
|||||||
| 33 | ||||||||
| 34 | - Register map: placeholder offsets and bit definitions for data, status, control, baud divisor and interrupt enable. The real byte offset is reg << reg-shift, so register spacing comes from the device tree rather than the code. |
|||||||
| 35 | - Eight small hardware functions, the only code that touches the chip: |
|||||||
| 36 | - `rx_ready` / `getc` |
|||||||
| 37 | - `tx_ready` / `putc` |
|||||||
| 38 | - `clear_errors` |
|||||||
| 39 | - `set_intr` |
|||||||
| 40 | - `intr_pending` |
|||||||
| 41 | - `program`, which sets speed and frame format from termios flags. The example assumes a 16× divisor UART. |
|||||||
| 42 | - The `compatible` string your device-tree node will use. |
|||||||
| 43 | ||||||||
| 44 | Everything below those sections is generic, and you shouldn’t need to touch it for a first version: console, autoconfiguration, interrupt handling, soft interrupt, and the tty and cdevsw entry points. Each part has a comment explaining its role and the locking. |
|||||||
| 45 | ||||||||
| 46 | #### How data moves: |
|||||||
| 47 | ||||||||
| 48 | - Receive: the hard interrupt copies bytes into a ring buffer. A soft interrupt later passes them to the tty layer, because the hard interrupt mustn’t call into it. |
|||||||
| 49 | - Transmit: the tty layer hands over a chunk of output. The transmit interrupt feeds it to the chip, then switches itself off when the chunk is done. |
|||||||
| 50 | - Console: the console uses polling and works from very early in boot. BREAK on the console drops into DDB. |
|||||||
| 51 | ||||||||
| 52 | Setup steps, all listed at the top of the file: |
|||||||
| 53 | ||||||||
| 54 | 1. Add the device/attach/file lines to files.hb68k. |
|||||||
| 55 | 1. Add a major number to majors.hb68k. 14 is free in your tree. |
|||||||
| 56 | 1. Add a myuart* at fdt? pass 5 line to the kernel config. |
|||||||
| 57 | 1. Add a device-tree node. |
|||||||
| 58 | 1. On the target, make the device node by hand (mknod /dev/ttyM0 c 14 0), since MAKEDEV won’t know about the driver. |
|||||||
| 59 | ||||||||
| 60 | Deliberately left out: |
|||||||
| 61 | ||||||||
| 62 | - modem control (carrier is treated as always present) |
|||||||
| 63 | - hardware flow control |
|||||||
| 64 | - split input/output speeds |
|||||||
| 65 | - sending BREAK |
|||||||
| 66 | ||||||||
| 67 | The ioctl function marks where the last of these goes. Your mc68681_duart.c is a worked example of the same structure extended to a two-channel chip, with an IVR, if you want to compare. |
|||||||
| 68 | ||||||||
| 69 | One constraint carries over from your board: the driver’s lock is a spin mutex at IPL_VM, so the chip’s interrupt level in the device tree must be 5 or lower. |
|||||||
| 70 | ||||||||
| 71 | Myuart |
|||||||
| 72 | C |
|||||||
| 73 | ||||||||
| 74 | ### Timer |
|||||||
| 75 | ||||||||
| 76 | Confirmed: a NULL argument makes the dispatcher pass the interrupt frame. Now a compile test: |
|||||||
| 77 | ||||||||
| 78 | The timer template is mytimer.c, built the same way as the UART one. I compiled it into an SBC3 kernel twice, once each way for the auto-reload setting, and both built cleanly. Then I took it back out of the tree. |
|||||||
| 79 | ||||||||
| 80 | What you change: the CHIP-SPECIFIC sections |
|||||||
| 81 | ||||||||
| 82 | - Registers: a placeholder register map for a simple down-counter (control, status, 24-bit reload). |
|||||||
| 83 | - Limits and reload behaviour: |
|||||||
| 84 | - MYTIMER_COUNT_MAX is the largest reload value. |
|||||||
| 85 | - MYTIMER_PRESCALE is any fixed divider in front of the counter. |
|||||||
| 86 | - MYTIMER_AUTO_RELOAD says whether the chip restarts itself at zero. If it doesn’t, the driver restarts it by hand on every tick. That loses a little time per tick, so the clock runs slightly slow. |
|||||||
| 87 | - Three hardware functions: |
|||||||
| 88 | - hw_stop() stops the counter and clears pending interrupts. |
|||||||
| 89 | - hw_start(count) loads the count and starts periodic interrupts. |
|||||||
| 90 | - hw_ack() clears the interrupt. Getting this wrong makes the machine appear to hang, because the CPU re-enters the handler immediately. |
|||||||
| 91 | ||||||||
| 92 | How it fits into hb68k: a timer driver provides only mechanism. The clock code in hb68k/clock.c owns the policy and calls the driver through four functions in a small table (struct clock_if): |
|||||||
| 93 | ||||||||
| 94 | Function When it’s called What it does |
|||||||
| 95 | clk_init once, late in boot establishes the interrupt at IPL_SCHED |
|||||||
| 96 | clk_arm(interval_us) once, right after init starts periodic interrupts, e.g. every 10 000 µs for 100 Hz |
|||||||
| 97 | clk_intr at the start of every tick acknowledges the interrupt, and reloads if needed |
|||||||
| 98 | clk_disarm shutdown or error stops the timer |
|||||||
| 99 | ||||||||
| 100 | The file header explains: |
|||||||
| 101 | ||||||||
| 102 | - How netbsd,hardclock / netbsd,statclock under /chosen pick each timer’s role, and what happens without them. |
|||||||
| 103 | - Why the timer belongs at IPL 6. |
|||||||
| 104 | - Which HZ values are allowed (they must divide 1 000 000). |
|||||||
| 105 | - Why the interrupt handler’s argument must be NULL. I checked this in the dispatcher: with NULL, the clock code receives the interrupt frame, which it needs. |
|||||||
| 106 | ||||||||
| 107 | Setup is lighter than the UART’s: |
|||||||
| 108 | ||||||||
| 109 | - Add device/attach/file to files.hb68k. |
|||||||
| 110 | - Add mytimer* at fdt? pass 3 to the config. |
|||||||
| 111 | - Add a device-tree node with clocks or clock-frequency, plus an interrupts entry. |
|||||||
| 112 | ||||||||
| 113 | There’s no major number or /dev node, since nothing opens a timer. |
|||||||
| 114 | ||||||||
| 115 | Left out deliberately: |
|||||||
| 116 | ||||||||
| 117 | - A high-resolution timecounter. That needs a free-running counter, not a counter that resets every tick, so it depends on what your chip offers. |
|||||||
| 118 | - Randomising the statclock interval. hb68k’s clock code doesn’t do this yet either. |
|||||||
| 119 | ||||||||
| 120 | Mytimer |
|||||||
| 121 | C |
|||||||