- The final step takes a Long Time(tm) and builds the userspace, which will need to be added to the root filesystem.
I've still got to document how to build the root filesystem.
+
+
## Device Driver Templates
+
I asked Claude about templates for coding device drivers. Here's what it said:
+
+
### Uart
+
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.
+
+
What you change: the two CHIP-SPECIFIC sections near the top
+
+
- 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.
+
- Eight small hardware functions, the only code that touches the chip:
+
- `rx_ready` / `getc`
+
- `tx_ready` / `putc`
+
- `clear_errors`
+
- `set_intr`
+
- `intr_pending`
+
- `program`, which sets speed and frame format from termios flags. The example assumes a 16× divisor UART.
+
- The `compatible` string your device-tree node will use.
+
+
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.
+
+
#### How data moves:
+
+
- 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.
+
- 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.
+
- Console: the console uses polling and works from very early in boot. BREAK on the console drops into DDB.
+
+
Setup steps, all listed at the top of the file:
+
+
1. Add the device/attach/file lines to files.hb68k.
+
1. Add a major number to majors.hb68k. 14 is free in your tree.
+
1. Add a myuart* at fdt? pass 5 line to the kernel config.
+
1. Add a device-tree node.
+
1. On the target, make the device node by hand (mknod /dev/ttyM0 c 14 0), since MAKEDEV won’t know about the driver.
+
+
Deliberately left out:
+
+
- modem control (carrier is treated as always present)
+
- hardware flow control
+
- split input/output speeds
+
- sending BREAK
+
+
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.
+
+
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.
+
+
Myuart
+
C
+
+
### Timer
+
+
Confirmed: a NULL argument makes the dispatcher pass the interrupt frame. Now a compile test:
+
+
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.
+
+
What you change: the CHIP-SPECIFIC sections
+
+
- Registers: a placeholder register map for a simple down-counter (control, status, 24-bit reload).
+
- Limits and reload behaviour:
+
- MYTIMER_COUNT_MAX is the largest reload value.
+
- MYTIMER_PRESCALE is any fixed divider in front of the counter.
+
- 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.
+
- Three hardware functions:
+
- hw_stop() stops the counter and clears pending interrupts.
+
- hw_start(count) loads the count and starts periodic interrupts.
+
- hw_ack() clears the interrupt. Getting this wrong makes the machine appear to hang, because the CPU re-enters the handler immediately.
+
+
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):
+
+
Function When it’s called What it does
+
clk_init once, late in boot establishes the interrupt at IPL_SCHED
+
clk_arm(interval_us) once, right after init starts periodic interrupts, e.g. every 10 000 µs for 100 Hz
+
clk_intr at the start of every tick acknowledges the interrupt, and reloads if needed
+
clk_disarm shutdown or error stops the timer
+
+
The file header explains:
+
+
- How netbsd,hardclock / netbsd,statclock under /chosen pick each timer’s role, and what happens without them.
+
- Why the timer belongs at IPL 6.
+
- Which HZ values are allowed (they must divide 1 000 000).
+
- 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.
+
+
Setup is lighter than the UART’s:
+
+
- Add device/attach/file to files.hb68k.
+
- Add mytimer* at fdt? pass 3 to the config.
+
- Add a device-tree node with clocks or clock-frequency, plus an interrupts entry.
+
+
There’s no major number or /dev node, since nothing opens a timer.
+
+
Left out deliberately:
+
+
- 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.
+
- Randomising the statclock interval. hb68k’s clock code doesn’t do this yet either.