Blame

e09617 Bob Green 2026-10-28 17:13:32
1
# NetBSD Port
26eeaf Bob Green 2026-10-28 17:21:46
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
a34307 Bob Green 2026-10-28 17:23:37
15
./build.sh -U -u -j4 -O ~/obj -m hb68k tools
26eeaf Bob Green 2026-10-28 17:21:46
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
e0d053 Bob Green 2026-10-28 17:22:31
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.
26eeaf Bob Green 2026-10-28 17:21:46
22
- The final step takes a Long Time(tm) and builds the userspace, which will need to be added to the root filesystem.
e0d053 Bob Green 2026-10-28 17:22:31
23
0f85d3 Bob Green 2026-11-02 13:19:53
24
### Root File System
25
```
26
./mksbc3img.sh -T ~/obj/tooldir.Linux-6.8.0-147-generic-x86_64 -k ~/obj/sys/arch/hb68k/compile/SBC3/netbsd -s 4096 sbc.img
27
```
865e89 Bob Green 2026-10-30 07:28:07
28
29
## Device Driver Templates
30
I asked Claude about templates for coding device drivers. Here's what it said:
31
32
### Uart
33
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.
34
35
What you change: the two CHIP-SPECIFIC sections near the top
36
37
- 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.
38
- Eight small hardware functions, the only code that touches the chip:
39
- `rx_ready` / `getc`
40
- `tx_ready` / `putc`
41
- `clear_errors`
42
- `set_intr`
43
- `intr_pending`
44
- `program`, which sets speed and frame format from termios flags. The example assumes a 16× divisor UART.
45
- The `compatible` string your device-tree node will use.
46
47
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.
48
49
#### How data moves:
50
51
- 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.
52
- 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.
53
- Console: the console uses polling and works from very early in boot. BREAK on the console drops into DDB.
54
55
Setup steps, all listed at the top of the file:
56
57
1. Add the device/attach/file lines to files.hb68k.
58
1. Add a major number to majors.hb68k. 14 is free in your tree.
59
1. Add a myuart* at fdt? pass 5 line to the kernel config.
60
1. Add a device-tree node.
61
1. On the target, make the device node by hand (mknod /dev/ttyM0 c 14 0), since MAKEDEV won’t know about the driver.
62
63
Deliberately left out:
64
65
- modem control (carrier is treated as always present)
66
- hardware flow control
67
- split input/output speeds
68
- sending BREAK
69
70
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.
71
72
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.
73
2dac7b Bob Green 2026-10-30 07:29:49
74
```
75
/* $NetBSD$ */
76
77
/*-
78
* Copyright (c) 2026 The NetBSD Foundation, Inc.
79
* All rights reserved.
80
*
81
* Redistribution and use in source and binary forms, with or without
82
* modification, are permitted provided that the following conditions
83
* are met:
84
* 1. Redistributions of source code must retain the above copyright
85
* notice, this list of conditions and the following disclaimer.
86
* 2. Redistributions in binary form must reproduce the above copyright
87
* notice, this list of conditions and the following disclaimer in the
88
* documentation and/or other materials provided with the distribution.
89
*
90
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
91
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
92
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
93
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
94
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
95
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
96
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
97
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
98
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
99
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
100
* POSSIBILITY OF SUCH DAMAGE.
101
*/
102
103
/*
104
* myuart -- template driver for a simple, single-channel UART attached
105
* via Flattened Device Tree (FDT), with console and tty support.
106
*
107
* ======================================================================
108
* HOW TO USE THIS TEMPLATE
109
* ======================================================================
110
*
111
* 1. Copy this file to e.g. sys/arch/hb68k/dev/<chip>.c and replace
112
* "myuart" with your driver name throughout (keep it short; it
113
* becomes the device name, e.g. "myuart0").
114
*
115
* 2. Fill in the section marked "CHIP-SPECIFIC" below. Everything
116
* else is generic NetBSD plumbing that works for most UARTs and
117
* shouldn't need touching for a first version.
118
*
119
* 3. Hook it into the build:
120
*
121
* sys/arch/hb68k/conf/files.hb68k:
122
* device myuart: tty
123
* attach myuart at fdt
124
* file arch/hb68k/dev/myuart.c myuart
125
*
126
* sys/arch/hb68k/conf/majors.hb68k (pick an unused number < 144):
127
* device-major myuart char 14 myuart
128
*
129
* Kernel config (e.g. conf/SBC3):
130
* myuart* at fdt? pass 5
131
*
132
* 4. Describe the chip in the device tree:
133
*
134
* serial@XXXXXX {
135
* compatible = "vendor,mychip"; (must match compat_data[])
136
* reg = <0xXXXXXX 0x10>; (base, size of register block)
137
* reg-shift = <1>; (log2 of register spacing)
138
* clock-frequency = <1843200>; (UART input clock, Hz)
139
* interrupts = <3 0>; (IPL, vector; 0 = auto-vector)
140
* };
141
*
142
* To make it the console, point /chosen/stdout-path at it, e.g.
143
* stdout-path = "serial1:9600n8";
144
* with an alias "serial1" for the node. The platform code must
145
* provide a static mapping for its registers (as sbc3_machdep.c
146
* does for the I/O window), because the console is set up before
147
* the VM system is running.
148
*
149
* 5. Device nodes: MAKEDEV doesn't know about a new driver, so create
150
* them by hand on the target (char major from step 3, minor = unit):
151
* mknod /dev/ttyM0 c 14 0
152
* and add a line to /etc/ttys if you want a login prompt on it.
153
*
154
* ======================================================================
155
* HOW THE PIECES FIT TOGETHER
156
* ======================================================================
157
*
158
* Autoconfiguration myuart_match() / myuart_attach() are called when
159
* the FDT bus finds a node whose "compatible"
160
* matches compat_data[]. attach maps the registers,
161
* allocates a tty and hooks up the interrupt.
162
*
163
* Console myuart_cnattach() runs very early (from
164
* consinit(), before autoconfiguration) if the
165
* node is /chosen/stdout-path. The console uses
166
* polled I/O only: myuart_cngetc()/myuart_cnputc().
167
*
168
* Hard interrupt myuart_intr() runs at the device's hardware IPL.
169
* It must be quick: it moves received bytes into a
170
* ring buffer, feeds the transmitter, and schedules
171
* the soft interrupt. It must not call into the
172
* tty layer.
173
*
174
* Soft interrupt myuart_soft() runs later at IPL_SOFTSERIAL. It
175
* hands received bytes to the line discipline and,
176
* when a transmit burst finishes, asks the tty
177
* layer for more output.
178
*
179
* tty entry points open/close/read/write/ioctl/poll come from the
180
* cdevsw table; myuartstart() and myuartparam()
181
* are called by the tty layer to start output and
182
* to change speed/framing.
183
*
184
* Locking sc_lock is a spin mutex at the device's IPL. It
185
* protects the ring buffer, the transmit pointers,
186
* the interrupt-enable shadow and register access
187
* shared between the interrupt handler and the
188
* rest of the driver.
189
*
190
* This template deliberately leaves out modem control (carrier is
191
* assumed always present), hardware flow control, split speeds, and
192
* power management. Add them later if your hardware needs them.
193
*/
194
195
#include <sys/cdefs.h>
196
__KERNEL_RCSID(0, "$NetBSD$");
197
198
#include <sys/param.h>
199
#include <sys/bus.h>
200
#include <sys/conf.h>
201
#include <sys/device.h>
202
#include <sys/fcntl.h>
203
#include <sys/intr.h>
204
#include <sys/kauth.h>
205
#include <sys/poll.h>
206
#include <sys/proc.h>
207
#include <sys/syslog.h>
208
#include <sys/systm.h>
209
#include <sys/tty.h>
210
211
#include <dev/cons.h>
212
213
#include <dev/fdt/fdtvar.h>
214
#include <dev/fdt/fdt_console.h>
215
216
/*
217
* ######################################################################
218
* CHIP-SPECIFIC: registers
219
* ######################################################################
220
*
221
* Replace these with your chip's register map. Offsets are register
222
* *numbers*; the real byte offset is (reg << reg-shift), so the same
223
* code works whether the chip sits on every byte, every other byte,
224
* or every fourth byte.
225
*
226
* The placeholder layout below is a typical simple UART:
227
* DATA read: receive holding register, write: transmit holding
228
* STATUS read-only status bits
229
* CTRL frame format and enables
230
* BAUD baud rate divisor (16 bits, split over two registers)
231
* IER interrupt enable bits
232
*/
233
#define MYUART_REG_DATA 0
234
#define MYUART_REG_STATUS 1
235
#define MYUART_REG_CTRL 2
236
#define MYUART_REG_BAUDLO 3
237
#define MYUART_REG_BAUDHI 4
238
#define MYUART_REG_IER 5
239
240
/* STATUS bits */
241
#define MYUART_SR_RXRDY 0x01 /* a received byte is waiting */
242
#define MYUART_SR_TXRDY 0x02 /* transmit register is empty */
243
#define MYUART_SR_OVERRUN 0x10 /* receiver overrun */
244
#define MYUART_SR_PARITY 0x20 /* parity error on this byte */
245
#define MYUART_SR_FRAMING 0x40 /* framing error on this byte */
246
#define MYUART_SR_BREAK 0x80 /* break received */
247
248
/* CTRL bits */
249
#define MYUART_CTRL_ENABLE 0x01 /* enable receiver + transmitter */
250
#define MYUART_CTRL_8BIT 0x02 /* 8 data bits (else 7) */
251
#define MYUART_CTRL_PARENB 0x04 /* parity enable */
252
#define MYUART_CTRL_PARODD 0x08 /* odd parity (else even) */
253
#define MYUART_CTRL_2STOP 0x10 /* 2 stop bits (else 1) */
254
255
/* IER bits */
256
#define MYUART_IER_RX 0x01 /* interrupt when RXRDY */
257
#define MYUART_IER_TX 0x02 /* interrupt when TXRDY */
258
259
#define MYUART_DEFAULT_FREQ 1843200 /* if the DT has no clock-frequency */
260
#define MYUART_DEFAULT_SPEED 9600 /* if stdout-path has no speed */
261
262
/*
263
* Everything needed to talk to the registers. Shared by the console
264
* (which runs before the softc exists) and the attached device.
265
*/
266
struct myuart_regs {
267
bus_space_tag_t ur_bst;
268
bus_space_handle_t ur_bsh;
269
u_int ur_shift; /* reg-shift */
270
u_int ur_freq; /* input clock, Hz */
271
};
272
273
static inline uint8_t
274
myuart_read(const struct myuart_regs *ur, u_int reg)
275
{
276
return bus_space_read_1(ur->ur_bst, ur->ur_bsh, reg << ur->ur_shift);
277
}
278
279
static inline void
280
myuart_write(const struct myuart_regs *ur, u_int reg, uint8_t val)
281
{
282
bus_space_write_1(ur->ur_bst, ur->ur_bsh, reg << ur->ur_shift, val);
283
}
284
285
/*
286
* ######################################################################
287
* CHIP-SPECIFIC: low-level operations
288
* ######################################################################
289
*
290
* These eight small functions are the whole hardware interface. The
291
* rest of the driver only ever touches the chip through them.
292
*/
293
294
/* Is a received byte waiting? Returns the status bits for that byte. */
295
static inline bool
296
myuart_hw_rx_ready(const struct myuart_regs *ur, uint8_t *srp)
297
{
298
const uint8_t sr = myuart_read(ur, MYUART_REG_STATUS);
299
300
*srp = sr;
301
return (sr & MYUART_SR_RXRDY) != 0;
302
}
303
304
/* Read the received byte. Call only after rx_ready returned true. */
305
static inline uint8_t
306
myuart_hw_getc(const struct myuart_regs *ur)
307
{
308
return myuart_read(ur, MYUART_REG_DATA);
309
}
310
311
/* Can another byte be written to the transmitter? */
312
static inline bool
313
myuart_hw_tx_ready(const struct myuart_regs *ur)
314
{
315
return (myuart_read(ur, MYUART_REG_STATUS) & MYUART_SR_TXRDY) != 0;
316
}
317
318
/* Write one byte to the transmitter. */
319
static inline void
320
myuart_hw_putc(const struct myuart_regs *ur, uint8_t c)
321
{
322
myuart_write(ur, MYUART_REG_DATA, c);
323
}
324
325
/*
326
* Clear any error / break condition after it has been read. Many
327
* chips clear errors automatically when the data register is read;
328
* if yours does, leave this empty.
329
*/
330
static inline void
331
myuart_hw_clear_errors(const struct myuart_regs *ur)
332
{
333
/* e.g. myuart_write(ur, MYUART_REG_CTRL, ... | RESET_ERRORS); */
334
}
335
336
/*
337
* Set the interrupt enables. `rx' and `tx' say which sources may
338
* interrupt. The transmit interrupt must only be enabled while there
339
* is something to send: most UARTs assert "transmitter empty"
340
* continuously, so leaving it enabled would interrupt forever.
341
*/
342
static inline void
343
myuart_hw_set_intr(const struct myuart_regs *ur, bool rx, bool tx)
344
{
345
myuart_write(ur, MYUART_REG_IER,
346
(rx ? MYUART_IER_RX : 0) | (tx ? MYUART_IER_TX : 0));
347
}
348
349
/*
350
* Did this chip cause the current interrupt? Used to share an
351
* interrupt level with other devices. If your chip has an "interrupt
352
* pending" bit, test that instead.
353
*/
354
static inline bool
355
myuart_hw_intr_pending(const struct myuart_regs *ur, bool tx_enabled)
356
{
357
const uint8_t sr = myuart_read(ur, MYUART_REG_STATUS);
358
359
return (sr & (MYUART_SR_RXRDY | MYUART_SR_BREAK)) != 0 ||
360
(tx_enabled && (sr & MYUART_SR_TXRDY) != 0);
361
}
362
363
/*
364
* Program speed and frame format, and enable the receiver and
365
* transmitter. Returns 0, or EINVAL if the speed or format can't be
366
* done. Called with interrupts for this device blocked.
367
*
368
* `cflag' uses the termios(4) bits: CSIZE (CS5..CS8), CSTOPB, PARENB,
369
* PARODD.
370
*/
371
static int
372
myuart_hw_program(const struct myuart_regs *ur, int speed, tcflag_t cflag)
373
{
374
uint8_t ctrl = MYUART_CTRL_ENABLE;
375
u_int div;
376
377
if (speed <= 0) {
378
return EINVAL;
379
}
380
381
/*
382
* Example for a 16x-oversampling divisor UART:
383
* divisor = clock / (16 * speed), rounded to nearest.
384
* Reject rates more than 3% off.
385
*/
386
div = (ur->ur_freq + 8 * speed) / (16 * speed);
387
if (div == 0 || div > 0xffff) {
388
return EINVAL;
389
}
390
const u_int actual = ur->ur_freq / (16 * div);
391
const u_int err = actual > (u_int)speed ?
392
actual - speed : speed - actual;
393
if (err * 100 > (u_int)speed * 3) {
394
return EINVAL;
395
}
396
397
switch (ISSET(cflag, CSIZE)) {
398
case CS8:
399
ctrl |= MYUART_CTRL_8BIT;
400
break;
401
case CS7:
402
break;
403
default:
404
return EINVAL; /* this example chip only does 7/8 */
405
}
406
if (ISSET(cflag, PARENB)) {
407
ctrl |= MYUART_CTRL_PARENB;
408
if (ISSET(cflag, PARODD)) {
409
ctrl |= MYUART_CTRL_PARODD;
410
}
411
}
412
if (ISSET(cflag, CSTOPB)) {
413
ctrl |= MYUART_CTRL_2STOP;
414
}
415
416
myuart_write(ur, MYUART_REG_CTRL, 0); /* quiesce */
417
myuart_write(ur, MYUART_REG_BAUDLO, div & 0xff);
418
myuart_write(ur, MYUART_REG_BAUDHI, (div >> 8) & 0xff);
419
myuart_write(ur, MYUART_REG_CTRL, ctrl);
420
421
return 0;
422
}
423
424
/*
425
* ######################################################################
426
* Device tree matching
427
* ######################################################################
428
*
429
* List every "compatible" string this driver handles.
430
*/
431
static const struct device_compatible_entry compat_data[] = {
432
{ .compat = "vendor,mychip" },
433
DEVICE_COMPAT_EOL
434
};
435
436
/*
437
* ######################################################################
438
* Generic driver below this line
439
* ######################################################################
440
*/
441
442
#define MYUART_RBUF_SIZE 256 /* receive ring size, power of 2 */
443
444
struct myuart_softc {
445
device_t sc_dev;
446
struct myuart_regs sc_regs;
447
kmutex_t sc_lock; /* see "Locking" above */
448
void *sc_ih; /* hardware interrupt */
449
void *sc_si; /* soft interrupt */
450
struct tty *sc_tty;
451
452
bool sc_console; /* we are the console */
453
bool sc_rx_on; /* shadow of RX intr enable */
454
bool sc_tx_on; /* shadow of TX intr enable */
455
456
/* Receive ring: (status << 8) | data, filled by myuart_intr(). */
457
uint16_t sc_rbuf[MYUART_RBUF_SIZE];
458
u_int sc_rbput;
459
u_int sc_rbget;
460
u_int sc_rbovf; /* bytes dropped, ring full */
461
462
/* Transmit burst, set up by myuartstart(). */
463
u_char *sc_tba; /* next byte to send */
464
int sc_tbc; /* bytes left */
465
bool sc_tx_done; /* burst finished */
466
};
467
468
static int myuart_match(device_t, cfdata_t, void *);
469
static void myuart_attach(device_t, device_t, void *);
470
471
CFATTACH_DECL_NEW(myuart, sizeof(struct myuart_softc),
472
myuart_match, myuart_attach, NULL, NULL);
473
474
extern struct cfdriver myuart_cd;
475
476
static dev_type_open(myuartopen);
477
static dev_type_close(myuartclose);
478
static dev_type_read(myuartread);
479
static dev_type_write(myuartwrite);
480
static dev_type_ioctl(myuartioctl);
481
static dev_type_stop(myuartstop);
482
static dev_type_tty(myuarttty);
483
static dev_type_poll(myuartpoll);
484
485
/* The character device switch; its major number comes from majors.*. */
486
const struct cdevsw myuart_cdevsw = {
487
.d_open = myuartopen,
488
.d_close = myuartclose,
489
.d_read = myuartread,
490
.d_write = myuartwrite,
491
.d_ioctl = myuartioctl,
492
.d_stop = myuartstop,
493
.d_tty = myuarttty,
494
.d_poll = myuartpoll,
495
.d_mmap = nommap,
496
.d_kqfilter = ttykqfilter,
497
.d_discard = nodiscard,
498
.d_flag = D_TTY
499
};
500
501
static void myuartstart(struct tty *);
502
static int myuartparam(struct tty *, struct termios *);
503
static int myuart_intr(void *);
504
static void myuart_soft(void *);
505
506
/* Call with sc_lock held: update the hardware interrupt enables. */
507
static void
508
myuart_update_intr(struct myuart_softc *sc)
509
{
510
KASSERT(mutex_owned(&sc->sc_lock));
511
myuart_hw_set_intr(&sc->sc_regs, sc->sc_rx_on, sc->sc_tx_on);
512
}
513
514
/*
515
* ----------------------------------------------------------------------
516
* Console
517
* ----------------------------------------------------------------------
518
*
519
* The console is set up long before autoconfiguration, so it can't use
520
* the softc. Its state lives in this static structure instead.
521
*/
522
static struct {
523
struct myuart_regs regs;
524
bus_addr_t addr; /* to recognise ourselves in attach */
525
int speed;
526
tcflag_t cflag;
527
bool active;
528
} myuart_cons;
529
530
static cnm_state_t myuart_cnm_state; /* console "magic" (break -> DDB) */
531
532
static int
533
myuart_cngetc(dev_t dev)
534
{
535
uint8_t sr;
536
int s, c;
537
538
s = splhigh();
539
while (!myuart_hw_rx_ready(&myuart_cons.regs, &sr)) {
540
continue;
541
}
542
c = myuart_hw_getc(&myuart_cons.regs);
543
splx(s);
544
545
return c;
546
}
547
548
static void
549
myuart_cnputc(dev_t dev, int c)
550
{
551
int s;
552
553
s = splhigh();
554
while (!myuart_hw_tx_ready(&myuart_cons.regs)) {
555
continue;
556
}
557
myuart_hw_putc(&myuart_cons.regs, c);
558
splx(s);
559
}
560
561
static void
562
myuart_cnpollc(dev_t dev, int on)
563
{
564
/* Nothing to do: the console routines always poll. */
565
}
566
567
static struct consdev myuart_consdev = {
568
.cn_getc = myuart_cngetc,
569
.cn_putc = myuart_cnputc,
570
.cn_pollc = myuart_cnpollc,
571
.cn_dev = NODEV, /* filled in by myuart_attach() */
572
.cn_pri = CN_NORMAL,
573
};
574
575
static int
576
myuart_console_match(int phandle)
577
{
578
return of_compatible_match(phandle, compat_data);
579
}
580
581
/*
582
* Called from consinit() when /chosen/stdout-path points at one of our
583
* nodes. `uart_freq' is the node's clock-frequency (or the platform's
584
* fallback).
585
*/
586
static void
587
myuart_cnattach(struct fdt_attach_args *faa, u_int uart_freq)
588
{
589
const int phandle = faa->faa_phandle;
590
struct myuart_regs * const ur = &myuart_cons.regs;
591
bus_addr_t addr;
592
bus_size_t size;
593
594
if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
595
return;
596
}
597
598
ur->ur_bst = faa->faa_bst;
599
ur->ur_shift = fdtbus_get_reg_shift(phandle, 0);
600
ur->ur_freq = uart_freq ? uart_freq : MYUART_DEFAULT_FREQ;
601
602
/* This must hit a static mapping: UVM isn't running yet. */
603
if (bus_space_map(ur->ur_bst, addr, size, 0, &ur->ur_bsh) != 0) {
604
return;
605
}
606
607
/* Speed and format from stdout-path, e.g. "serial0:9600n8". */
608
myuart_cons.speed = fdtbus_get_stdout_speed();
609
if (myuart_cons.speed < 0) {
610
myuart_cons.speed = MYUART_DEFAULT_SPEED;
611
}
612
myuart_cons.cflag = fdtbus_get_stdout_flags();
613
614
myuart_hw_set_intr(ur, false, false);
615
if (myuart_hw_program(ur, myuart_cons.speed,
616
myuart_cons.cflag) != 0) {
617
/* Fall back to something sane rather than staying silent. */
618
myuart_cons.speed = MYUART_DEFAULT_SPEED;
619
myuart_cons.cflag = CS8 | CREAD | CLOCAL;
620
if (myuart_hw_program(ur, myuart_cons.speed,
621
myuart_cons.cflag) != 0) {
622
return;
623
}
624
}
625
626
myuart_cons.addr = addr;
627
myuart_cons.active = true;
628
cn_tab = &myuart_consdev;
629
}
630
631
static const struct fdt_console myuart_fdt_console = {
632
.match = myuart_console_match,
633
.consinit = myuart_cnattach,
634
};
635
636
FDT_CONSOLE(myuart, &myuart_fdt_console);
637
638
/*
639
* ----------------------------------------------------------------------
640
* Autoconfiguration
641
* ----------------------------------------------------------------------
642
*/
643
644
static int
645
myuart_match(device_t parent, cfdata_t cf, void *aux)
646
{
647
struct fdt_attach_args * const faa = aux;
648
649
return of_compatible_match(faa->faa_phandle, compat_data);
650
}
651
652
static void
653
myuart_attach(device_t parent, device_t self, void *aux)
654
{
655
struct myuart_softc * const sc = device_private(self);
656
struct fdt_attach_args * const faa = aux;
657
const int phandle = faa->faa_phandle;
658
struct myuart_regs * const ur = &sc->sc_regs;
659
char intrstr[128];
660
bus_addr_t addr;
661
bus_size_t size;
662
struct tty *tp;
663
664
sc->sc_dev = self;
665
666
/* --- Registers ------------------------------------------------ */
667
if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
668
aprint_error(": couldn't get registers\n");
669
return;
670
}
671
ur->ur_bst = faa->faa_bst;
672
ur->ur_shift = fdtbus_get_reg_shift(phandle, 0);
673
if (of_getprop_uint32(phandle, "clock-frequency", &ur->ur_freq) != 0) {
674
ur->ur_freq = MYUART_DEFAULT_FREQ;
675
}
676
if (bus_space_map(ur->ur_bst, addr, size, 0, &ur->ur_bsh) != 0) {
677
aprint_error(": couldn't map registers\n");
678
return;
679
}
680
681
sc->sc_console = myuart_cons.active && myuart_cons.addr == addr;
682
683
/*
684
* The lock is a spin mutex at IPL_VM, which on hb68k blocks
685
* hardware levels 1-5. So the device's interrupt level (first
686
* cell of "interrupts") must be 5 or lower.
687
*/
688
mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM);
689
690
aprint_naive("\n");
691
aprint_normal(": UART, %u Hz clock%s\n", ur->ur_freq,
692
sc->sc_console ? ", console" : "");
693
694
/* --- Quiet the chip (but leave the console's settings alone) - */
695
mutex_spin_enter(&sc->sc_lock);
696
sc->sc_rx_on = sc->sc_tx_on = false;
697
myuart_update_intr(sc);
698
mutex_spin_exit(&sc->sc_lock);
699
700
/* --- tty ------------------------------------------------------ */
701
tp = tty_alloc();
702
tp->t_oproc = myuartstart; /* tty layer: "start output" */
703
tp->t_param = myuartparam; /* tty layer: "change settings" */
704
tty_attach(tp);
705
sc->sc_tty = tp;
706
707
if (sc->sc_console) {
708
/* Tell the console layer which /dev entry is the console. */
709
myuart_consdev.cn_dev = makedev(
710
cdevsw_lookup_major(&myuart_cdevsw), device_unit(self));
711
aprint_normal_dev(self, "console\n");
712
}
713
714
/* --- Interrupts ----------------------------------------------- */
715
sc->sc_si = softint_establish(SOFTINT_SERIAL, myuart_soft, sc);
716
if (sc->sc_si == NULL) {
717
aprint_error_dev(self, "couldn't establish soft interrupt\n");
718
return;
719
}
720
721
if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
722
aprint_error_dev(self, "couldn't decode interrupt\n");
723
return;
724
}
725
sc->sc_ih = fdtbus_intr_establish_xname(phandle, 0, IPL_VM,
726
FDT_INTR_MPSAFE, myuart_intr, sc, device_xname(self));
727
if (sc->sc_ih == NULL) {
728
aprint_error_dev(self,
729
"couldn't establish interrupt at %s\n", intrstr);
730
return;
731
}
732
aprint_normal_dev(self, "interrupting at %s\n", intrstr);
733
734
/*
735
* The console listens all the time, so that a BREAK (or the
736
* configured magic sequence) can drop into DDB even when no
737
* process has the tty open.
738
*/
739
if (sc->sc_console) {
740
cn_init_magic(&myuart_cnm_state);
741
cn_set_magic("\047\001"); /* BREAK */
742
743
mutex_spin_enter(&sc->sc_lock);
744
sc->sc_rx_on = true;
745
myuart_update_intr(sc);
746
mutex_spin_exit(&sc->sc_lock);
747
}
748
}
749
750
/*
751
* ----------------------------------------------------------------------
752
* Hardware interrupt
753
* ----------------------------------------------------------------------
754
*
755
* Runs at the device's IPL with sc_lock held. Keep it short.
756
*/
757
758
static void
759
myuart_rxintr(struct myuart_softc *sc)
760
{
761
struct myuart_regs * const ur = &sc->sc_regs;
762
const bool open = ISSET(sc->sc_tty->t_state, TS_ISOPEN);
763
uint8_t sr, c;
764
765
while (myuart_hw_rx_ready(ur, &sr)) {
766
c = myuart_hw_getc(ur);
767
768
if (sr & (MYUART_SR_OVERRUN | MYUART_SR_PARITY |
769
MYUART_SR_FRAMING | MYUART_SR_BREAK)) {
770
myuart_hw_clear_errors(ur);
771
}
772
773
/* Console magic: BREAK (or a byte sequence) -> DDB. */
774
if (sc->sc_console) {
775
if (sr & MYUART_SR_BREAK) {
776
cn_check_magic(myuart_consdev.cn_dev,
777
CNC_BREAK, myuart_cnm_state);
778
} else {
779
cn_check_magic(myuart_consdev.cn_dev,
780
c, myuart_cnm_state);
781
}
782
}
783
784
if (!open || (sr & MYUART_SR_BREAK)) {
785
continue; /* nobody listening, or a BREAK */
786
}
787
788
if (sc->sc_rbput - sc->sc_rbget >= MYUART_RBUF_SIZE) {
789
sc->sc_rbovf++; /* ring full: drop it */
790
continue;
791
}
792
sc->sc_rbuf[sc->sc_rbput++ & (MYUART_RBUF_SIZE - 1)] =
793
((uint16_t)sr << 8) | c;
794
}
795
}
796
797
static void
798
myuart_txintr(struct myuart_softc *sc)
799
{
800
struct myuart_regs * const ur = &sc->sc_regs;
801
802
/* Fill the transmitter (or its FIFO) as far as it will go. */
803
while (sc->sc_tbc > 0 && myuart_hw_tx_ready(ur)) {
804
myuart_hw_putc(ur, *sc->sc_tba++);
805
sc->sc_tbc--;
806
}
807
808
if (sc->sc_tbc == 0) {
809
/* Burst done: stop TX interrupts, let the softint refill. */
810
sc->sc_tx_on = false;
811
myuart_update_intr(sc);
812
sc->sc_tx_done = true;
813
}
814
}
815
816
static int
817
myuart_intr(void *arg)
818
{
819
struct myuart_softc * const sc = arg;
820
821
mutex_spin_enter(&sc->sc_lock);
822
if (!myuart_hw_intr_pending(&sc->sc_regs, sc->sc_tx_on)) {
823
mutex_spin_exit(&sc->sc_lock);
824
return 0; /* not ours (shared interrupt level) */
825
}
826
827
myuart_rxintr(sc);
828
if (sc->sc_tx_on) {
829
myuart_txintr(sc);
830
}
831
mutex_spin_exit(&sc->sc_lock);
832
833
softint_schedule(sc->sc_si);
834
return 1;
835
}
836
837
/*
838
* ----------------------------------------------------------------------
839
* Soft interrupt
840
* ----------------------------------------------------------------------
841
*
842
* Runs at IPL_SOFTSERIAL, where it's safe to call the tty layer.
843
*/
844
static void
845
myuart_soft(void *arg)
846
{
847
struct myuart_softc * const sc = arg;
848
struct tty * const tp = sc->sc_tty;
849
uint16_t ent;
850
u_int ovf;
851
bool tx_done;
852
int c;
853
854
/* Report ring-buffer overflows (not too often: use log(9)). */
855
mutex_spin_enter(&sc->sc_lock);
856
ovf = sc->sc_rbovf;
857
sc->sc_rbovf = 0;
858
mutex_spin_exit(&sc->sc_lock);
859
if (ovf != 0) {
860
log(LOG_WARNING, "%s: %u receive overflow(s)\n",
861
device_xname(sc->sc_dev), ovf);
862
}
863
864
/* Hand received bytes to the line discipline. */
865
for (;;) {
866
mutex_spin_enter(&sc->sc_lock);
867
if (sc->sc_rbget == sc->sc_rbput) {
868
mutex_spin_exit(&sc->sc_lock);
869
break;
870
}
871
ent = sc->sc_rbuf[sc->sc_rbget++ & (MYUART_RBUF_SIZE - 1)];
872
mutex_spin_exit(&sc->sc_lock);
873
874
c = ent & 0xff;
875
if (ent & (MYUART_SR_FRAMING << 8)) {
876
SET(c, TTY_FE);
877
}
878
if (ent & (MYUART_SR_PARITY << 8)) {
879
SET(c, TTY_PE);
880
}
881
if (ISSET(tp->t_state, TS_ISOPEN)) {
882
(*tp->t_linesw->l_rint)(c, tp);
883
}
884
}
885
886
/* If a transmit burst finished, release it and ask for more. */
887
mutex_spin_enter(&sc->sc_lock);
888
tx_done = sc->sc_tx_done;
889
sc->sc_tx_done = false;
890
mutex_spin_exit(&sc->sc_lock);
891
892
if (tx_done) {
893
CLR(tp->t_state, TS_BUSY);
894
if (ISSET(tp->t_state, TS_FLUSH)) {
895
CLR(tp->t_state, TS_FLUSH);
896
} else {
897
ndflush(&tp->t_outq,
898
(int)(sc->sc_tba - tp->t_outq.c_cf));
899
}
900
(*tp->t_linesw->l_start)(tp);
901
}
902
}
903
904
/*
905
* ----------------------------------------------------------------------
906
* tty layer callbacks
907
* ----------------------------------------------------------------------
908
*/
909
910
static struct myuart_softc *
911
myuart_lookup(dev_t dev)
912
{
913
struct myuart_softc *sc;
914
915
sc = device_lookup_private(&myuart_cd, TTUNIT(dev));
916
if (sc == NULL || sc->sc_si == NULL || sc->sc_ih == NULL) {
917
return NULL; /* no such unit, or attach failed */
918
}
919
return sc;
920
}
921
922
/*
923
* Start output: take a contiguous chunk of the tty's output queue and
924
* let the transmit interrupt send it.
925
*/
926
static void
927
myuartstart(struct tty *tp)
928
{
929
struct myuart_softc * const sc = myuart_lookup(tp->t_dev);
930
int s;
931
932
if (sc == NULL) {
933
return;
934
}
935
936
s = spltty();
937
if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) {
938
goto out; /* already sending, or stopped (^S) */
939
}
940
if (!ttypull(tp)) {
941
goto out; /* nothing to send */
942
}
943
944
mutex_spin_enter(&sc->sc_lock);
945
sc->sc_tba = tp->t_outq.c_cf;
946
sc->sc_tbc = ndqb(&tp->t_outq, 0);
947
SET(tp->t_state, TS_BUSY);
948
sc->sc_tx_on = true; /* interrupts as soon as TX is empty */
949
myuart_update_intr(sc);
950
mutex_spin_exit(&sc->sc_lock);
951
out:
952
splx(s);
953
}
954
955
/* Stop output (e.g. on ^S or a flush). */
956
static void
957
myuartstop(struct tty *tp, int flag)
958
{
959
struct myuart_softc * const sc = myuart_lookup(tp->t_dev);
960
961
if (sc == NULL) {
962
return;
963
}
964
965
mutex_spin_enter(&sc->sc_lock);
966
if (ISSET(tp->t_state, TS_BUSY)) {
967
sc->sc_tbc = 0; /* finish after the current byte */
968
if (!ISSET(tp->t_state, TS_TTSTOP)) {
969
SET(tp->t_state, TS_FLUSH);
970
}
971
}
972
mutex_spin_exit(&sc->sc_lock);
973
}
974
975
/* Change speed / framing (stty, tcsetattr). */
976
static int
977
myuartparam(struct tty *tp, struct termios *t)
978
{
979
struct myuart_softc * const sc = myuart_lookup(tp->t_dev);
980
int error;
981
982
if (sc == NULL) {
983
return ENXIO;
984
}
985
986
/* Unsupported in this template: split speeds, flow control. */
987
if (t->c_ispeed != 0 && t->c_ispeed != t->c_ospeed) {
988
return EINVAL;
989
}
990
if (ISSET(t->c_cflag, CRTSCTS | MDMBUF)) {
991
return EINVAL;
992
}
993
994
mutex_spin_enter(&sc->sc_lock);
995
error = myuart_hw_program(&sc->sc_regs, t->c_ospeed, t->c_cflag);
996
mutex_spin_exit(&sc->sc_lock);
997
if (error) {
998
return error;
999
}
1000
1001
tp->t_ispeed = 0;
1002
tp->t_ospeed = t->c_ospeed;
1003
tp->t_cflag = t->c_cflag;
1004
1005
/* No modem control: carrier is always present. */
1006
(void)(*tp->t_linesw->l_modem)(tp, 1);
1007
1008
return 0;
1009
}
1010
1011
/*
1012
* ----------------------------------------------------------------------
1013
* cdevsw entry points
1014
* ----------------------------------------------------------------------
1015
*/
1016
1017
static int
1018
myuartopen(dev_t dev, int flag, int mode, struct lwp *l)
1019
{
1020
struct myuart_softc * const sc = myuart_lookup(dev);
1021
struct tty *tp;
1022
int error;
1023
1024
if (sc == NULL) {
1025
return ENXIO;
1026
}
1027
tp = sc->sc_tty;
1028
1029
if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp)) {
1030
return EBUSY;
1031
}
1032
1033
/* First open: set defaults and turn on receive interrupts. */
1034
if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
1035
struct termios t;
1036
1037
tp->t_dev = dev;
1038
1039
t.c_ispeed = 0;
1040
if (sc->sc_console) {
1041
t.c_ospeed = myuart_cons.speed;
1042
t.c_cflag = myuart_cons.cflag;
1043
} else {
1044
t.c_ospeed = TTYDEF_SPEED;
1045
t.c_cflag = TTYDEF_CFLAG;
1046
}
1047
SET(t.c_cflag, CLOCAL);
1048
CLR(t.c_cflag, CRTSCTS | MDMBUF);
1049
1050
tp->t_ospeed = 0; /* force myuartparam() to program */
1051
(void)myuartparam(tp, &t);
1052
tp->t_iflag = TTYDEF_IFLAG;
1053
tp->t_oflag = TTYDEF_OFLAG;
1054
tp->t_lflag = TTYDEF_LFLAG;
1055
ttychars(tp);
1056
ttsetwater(tp);
1057
1058
mutex_spin_enter(&sc->sc_lock);
1059
sc->sc_rbget = sc->sc_rbput = 0;
1060
sc->sc_rx_on = true;
1061
myuart_update_intr(sc);
1062
mutex_spin_exit(&sc->sc_lock);
1063
}
1064
1065
error = ttyopen(tp, TTDIALOUT(dev), ISSET(flag, O_NONBLOCK));
1066
if (error == 0) {
1067
error = (*tp->t_linesw->l_open)(dev, tp);
1068
}
1069
return error;
1070
}
1071
1072
static int
1073
myuartclose(dev_t dev, int flag, int mode, struct lwp *l)
1074
{
1075
struct myuart_softc * const sc = myuart_lookup(dev);
1076
struct tty *tp;
1077
1078
if (sc == NULL) {
1079
return 0;
1080
}
1081
tp = sc->sc_tty;
1082
1083
if (!ISSET(tp->t_state, TS_ISOPEN)) {
1084
return 0;
1085
}
1086
(*tp->t_linesw->l_close)(tp, flag);
1087
ttyclose(tp);
1088
1089
/* Last close: go quiet (the console keeps listening for BREAK). */
1090
if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0 &&
1091
!sc->sc_console) {
1092
mutex_spin_enter(&sc->sc_lock);
1093
sc->sc_rx_on = sc->sc_tx_on = false;
1094
myuart_update_intr(sc);
1095
mutex_spin_exit(&sc->sc_lock);
1096
}
1097
return 0;
1098
}
1099
1100
static int
1101
myuartread(dev_t dev, struct uio *uio, int flag)
1102
{
1103
struct myuart_softc * const sc = myuart_lookup(dev);
1104
1105
if (sc == NULL) {
1106
return ENXIO;
1107
}
1108
return (*sc->sc_tty->t_linesw->l_read)(sc->sc_tty, uio, flag);
1109
}
1110
1111
static int
1112
myuartwrite(dev_t dev, struct uio *uio, int flag)
1113
{
1114
struct myuart_softc * const sc = myuart_lookup(dev);
1115
1116
if (sc == NULL) {
1117
return ENXIO;
1118
}
1119
return (*sc->sc_tty->t_linesw->l_write)(sc->sc_tty, uio, flag);
1120
}
1121
1122
static int
1123
myuartpoll(dev_t dev, int events, struct lwp *l)
1124
{
1125
struct myuart_softc * const sc = myuart_lookup(dev);
1126
1127
if (sc == NULL) {
1128
return POLLHUP;
1129
}
1130
return (*sc->sc_tty->t_linesw->l_poll)(sc->sc_tty, events, l);
1131
}
1132
1133
static struct tty *
1134
myuarttty(dev_t dev)
1135
{
1136
struct myuart_softc * const sc = myuart_lookup(dev);
1137
1138
return sc != NULL ? sc->sc_tty : NULL;
1139
}
1140
1141
static int
1142
myuartioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
1143
{
1144
struct myuart_softc * const sc = myuart_lookup(dev);
1145
struct tty *tp;
1146
int error;
1147
1148
if (sc == NULL) {
1149
return ENXIO;
1150
}
1151
tp = sc->sc_tty;
1152
1153
/* Line discipline first, then the generic tty ioctls. */
1154
error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
1155
if (error != EPASSTHROUGH) {
1156
return error;
1157
}
1158
error = ttioctl(tp, cmd, data, flag, l);
1159
if (error != EPASSTHROUGH) {
1160
return error;
1161
}
1162
1163
/*
1164
* Device-specific ioctls go here, e.g. TIOCSBRK / TIOCCBRK to
1165
* start and stop sending a break, or TIOCMGET / TIOCMSET for
1166
* modem control lines.
1167
*/
1168
return EPASSTHROUGH;
1169
}
1170
```
865e89 Bob Green 2026-10-30 07:28:07
1171
1172
### Timer
1173
1174
Confirmed: a NULL argument makes the dispatcher pass the interrupt frame. Now a compile test:
1175
1176
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.
1177
1178
What you change: the CHIP-SPECIFIC sections
1179
1180
- Registers: a placeholder register map for a simple down-counter (control, status, 24-bit reload).
1181
- Limits and reload behaviour:
1182
- MYTIMER_COUNT_MAX is the largest reload value.
1183
- MYTIMER_PRESCALE is any fixed divider in front of the counter.
1184
- 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.
1185
- Three hardware functions:
1186
- hw_stop() stops the counter and clears pending interrupts.
1187
- hw_start(count) loads the count and starts periodic interrupts.
1188
- hw_ack() clears the interrupt. Getting this wrong makes the machine appear to hang, because the CPU re-enters the handler immediately.
1189
1190
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):
1191
112700 Bob Green 2026-10-30 07:48:23
1192
| Function | When it’s called | What it does |
1193
| ---------------------- | -------------------------- | ------------ |
1194
| `clk_init` | once, late in boot | establishes the interrupt at IPL_SCHED |
1195
| `clk_arm(interval_us)` | once, right after init | starts periodic interrupts, e.g. every 10 000 µs for 100 Hz |
1196
| `clk_intr` | at the start of every tick | acknowledges the interrupt, and reloads if needed |
1197
| `clk_disarm` | shutdown or error | stops the timer |
865e89 Bob Green 2026-10-30 07:28:07
1198
1199
The file header explains:
1200
1201
- How netbsd,hardclock / netbsd,statclock under /chosen pick each timer’s role, and what happens without them.
1202
- Why the timer belongs at IPL 6.
1203
- Which HZ values are allowed (they must divide 1 000 000).
1204
- 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.
1205
1206
Setup is lighter than the UART’s:
1207
1208
- Add device/attach/file to files.hb68k.
1209
- Add mytimer* at fdt? pass 3 to the config.
1210
- Add a device-tree node with clocks or clock-frequency, plus an interrupts entry.
1211
1212
There’s no major number or /dev node, since nothing opens a timer.
1213
1214
Left out deliberately:
1215
1216
- 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.
1217
- Randomising the statclock interval. hb68k’s clock code doesn’t do this yet either.
1218
2dac7b Bob Green 2026-10-30 07:29:49
1219
```
1220
/* $NetBSD$ */
1221
1222
/*-
1223
* Copyright (c) 2026 The NetBSD Foundation, Inc.
1224
* All rights reserved.
1225
*
1226
* Redistribution and use in source and binary forms, with or without
1227
* modification, are permitted provided that the following conditions
1228
* are met:
1229
* 1. Redistributions of source code must retain the above copyright
1230
* notice, this list of conditions and the following disclaimer.
1231
* 2. Redistributions in binary form must reproduce the above copyright
1232
* notice, this list of conditions and the following disclaimer in the
1233
* documentation and/or other materials provided with the distribution.
1234
*
1235
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1236
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1237
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1238
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
1239
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1240
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
1241
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
1242
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
1243
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1244
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
1245
* POSSIBILITY OF SUCH DAMAGE.
1246
*/
1247
1248
/*
1249
* mytimer -- template driver for a periodic hardware timer used as a
1250
* NetBSD/hb68k system clock, attached via Flattened Device Tree (FDT).
1251
*
1252
* ======================================================================
1253
* HOW TO USE THIS TEMPLATE
1254
* ======================================================================
1255
*
1256
* 1. Copy this file to e.g. sys/arch/hb68k/dev/<chip>_timer.c and
1257
* replace "mytimer" with your driver name throughout (it becomes
1258
* the device name, e.g. "mytimer0").
1259
*
1260
* 2. Fill in the sections marked "CHIP-SPECIFIC" below. Everything
1261
* else is generic hb68k plumbing.
1262
*
1263
* 3. Hook it into the build:
1264
*
1265
* sys/arch/hb68k/conf/files.hb68k:
1266
* device mytimer
1267
* attach mytimer at fdt
1268
* file arch/hb68k/dev/mytimer.c mytimer
1269
*
1270
* Kernel config (e.g. conf/SBC3). Timers attach after the
1271
* interrupt controller (pass 2), hence pass 3:
1272
* mytimer* at fdt? pass 3
1273
*
1274
* A timer has no /dev entry, so it needs no major number.
1275
*
1276
* 4. Describe the chip in the device tree:
1277
*
1278
* timer@XXXXXX {
1279
* compatible = "vendor,mytimer"; (must match compat_data[])
1280
* reg = <0xXXXXXX 0x10>; (base, size of register block)
1281
* reg-shift = <1>; (log2 of register spacing)
1282
* clocks = <&osc>; (a fixed-clock node), OR
1283
* clock-frequency = <1000000>; (input clock in Hz)
1284
* interrupts = <6 0>; (IPL, vector; 0 = auto-vector)
1285
* };
1286
*
1287
* and say which job it does under /chosen, using an alias:
1288
*
1289
* chosen {
1290
* netbsd,hardclock = "timer0"; (the main system tick)
1291
* netbsd,statclock = "timer1"; (optional, see below)
1292
* };
1293
* aliases {
1294
* timer0 = "/obio/timer@XXXXXX";
1295
* };
1296
*
1297
* Without those properties, the first timer to attach becomes the
1298
* hardclock and the second (if any) the statclock.
1299
*
1300
* ======================================================================
1301
* HOW hb68k USES A TIMER
1302
* ======================================================================
1303
*
1304
* The machine-independent clock code in sys/arch/hb68k/hb68k/clock.c
1305
* owns the policy; a timer driver only provides mechanism, through the
1306
* five-member `struct clock_if' (machine/clockvar.h):
1307
*
1308
* clk_dev The timer's device_t.
1309
*
1310
* clk_init Called once, from cpu_initclocks(), late in boot. Must
1311
* establish the hardware interrupt with the handler it is
1312
* given (at IPL_SCHED) and return 0, or an error.
1313
*
1314
* clk_arm Start the timer interrupting periodically, every
1315
* `interval_us' microseconds (10000 for the usual
1316
* hz = 100). Called once, right after clk_init.
1317
*
1318
* clk_intr Called by the clock code at the start of every timer
1319
* interrupt. Acknowledge the interrupt here and, if the
1320
* chip doesn't reload by itself, restart the count.
1321
*
1322
* clk_disarm Stop the timer (used at shutdown / error paths).
1323
*
1324
* The driver registers itself with clock_attach() at the end of
1325
* attach. The clock code then decides which role the timer plays:
1326
*
1327
* hardclock Drives the system tick: timekeeping, scheduling,
1328
* callouts. Every system needs one.
1329
*
1330
* statclock Optional second timer for CPU-usage statistics and
1331
* profiling. With only one timer, the hardclock does both
1332
* jobs, and boot prints "No statclock; using hardclock."
1333
*
1334
* Interrupt level: the hardclock runs at IPL_SCHED, which on hb68k is
1335
* hardware level 6, so wire the timer to IPL 6 and use 6 as the first
1336
* cell of "interrupts". A lower level works, but the clock interrupt
1337
* can then be delayed or blocked by other devices and by the soft
1338
* interrupt level (1), so the system time will drift.
1339
*
1340
* hz must divide 1000000 exactly (the clock code checks this): use
1341
* 50, 100, 200, 250, 400, 500 or 1000. Set it in the kernel config
1342
* with "options HZ=..." if the default of 100 doesn't suit the
1343
* hardware.
1344
*/
1345
1346
#include <sys/cdefs.h>
1347
__KERNEL_RCSID(0, "$NetBSD$");
1348
1349
#include <sys/param.h>
1350
#include <sys/bus.h>
1351
#include <sys/device.h>
1352
#include <sys/intr.h>
1353
#include <sys/systm.h>
1354
1355
#include <machine/clockvar.h>
1356
1357
#include <dev/fdt/fdtvar.h>
1358
1359
/*
1360
* ######################################################################
1361
* CHIP-SPECIFIC: registers
1362
* ######################################################################
1363
*
1364
* Replace these with your chip's register map. Offsets are register
1365
* *numbers*; the real byte offset is (reg << reg-shift).
1366
*
1367
* The placeholder layout below is a typical simple down-counter:
1368
* CTRL enable, interrupt enable, auto-reload
1369
* STATUS "counter reached zero" flag, write 1 to clear
1370
* RELOAD0..2 24-bit reload (preload) value, low byte first
1371
*/
1372
#define MYTIMER_REG_CTRL 0
1373
#define MYTIMER_REG_STATUS 1
1374
#define MYTIMER_REG_RELOAD0 2 /* bits 7..0 */
1375
#define MYTIMER_REG_RELOAD1 3 /* bits 15..8 */
1376
#define MYTIMER_REG_RELOAD2 4 /* bits 23..16 */
1377
1378
/* CTRL bits */
1379
#define MYTIMER_CTRL_RUN 0x01 /* counter running */
1380
#define MYTIMER_CTRL_IRQEN 0x02 /* interrupt on reaching zero */
1381
#define MYTIMER_CTRL_RELOAD 0x04 /* reload from RELOAD on zero */
1382
1383
/* STATUS bits */
1384
#define MYTIMER_SR_ZERO 0x01 /* counter hit zero (write 1 = clear) */
1385
1386
/*
1387
* Counter limits. MAX is the largest value the reload register can
1388
* hold; PRESCALE is any fixed divider between the input clock and the
1389
* counter (1 if none). Together with the input clock these decide the
1390
* longest interval the timer can produce: MAX * PRESCALE / freq.
1391
*/
1392
#define MYTIMER_COUNT_MAX 0x00ffffffU
1393
#define MYTIMER_PRESCALE 1
1394
1395
/*
1396
* Set to true if the counter reloads itself when it reaches zero
1397
* (most timers can). If false, clk_intr() restarts it by hand; the
1398
* time spent getting to the interrupt handler is then lost on every
1399
* tick, and the clock runs slightly slow.
1400
*/
1401
#define MYTIMER_AUTO_RELOAD true
1402
1403
/*
1404
* ######################################################################
1405
* Device tree matching
1406
* ######################################################################
1407
*/
1408
static const struct device_compatible_entry compat_data[] = {
1409
{ .compat = "vendor,mytimer" },
1410
DEVICE_COMPAT_EOL
1411
};
1412
1413
/*
1414
* ######################################################################
1415
* Driver state
1416
* ######################################################################
1417
*/
1418
struct mytimer_softc {
1419
device_t sc_dev;
1420
bus_space_tag_t sc_bst;
1421
bus_space_handle_t sc_bsh;
1422
u_int sc_shift; /* reg-shift */
1423
uint32_t sc_freq; /* counter decrement rate, Hz */
1424
uint32_t sc_reload; /* count for one interval */
1425
void *sc_ih; /* interrupt handle */
1426
struct clock_if sc_clkif; /* our interface to clock.c */
1427
};
1428
1429
static inline uint8_t
1430
mytimer_read(struct mytimer_softc *sc, u_int reg)
1431
{
1432
return bus_space_read_1(sc->sc_bst, sc->sc_bsh, reg << sc->sc_shift);
1433
}
1434
1435
static inline void
1436
mytimer_write(struct mytimer_softc *sc, u_int reg, uint8_t val)
1437
{
1438
bus_space_write_1(sc->sc_bst, sc->sc_bsh, reg << sc->sc_shift, val);
1439
}
1440
1441
/*
1442
* ######################################################################
1443
* CHIP-SPECIFIC: low-level operations
1444
* ######################################################################
1445
*
1446
* The only code that touches the hardware.
1447
*/
1448
1449
/* Stop the counter and clear any pending interrupt. */
1450
static void
1451
mytimer_hw_stop(struct mytimer_softc *sc)
1452
{
1453
mytimer_write(sc, MYTIMER_REG_CTRL, 0);
1454
mytimer_write(sc, MYTIMER_REG_STATUS, MYTIMER_SR_ZERO);
1455
}
1456
1457
/*
1458
* Load `count' and start counting down, interrupting at zero. With
1459
* MYTIMER_AUTO_RELOAD the chip then repeats on its own.
1460
*/
1461
static void
1462
mytimer_hw_start(struct mytimer_softc *sc, uint32_t count)
1463
{
1464
mytimer_write(sc, MYTIMER_REG_RELOAD0, count & 0xff);
1465
mytimer_write(sc, MYTIMER_REG_RELOAD1, (count >> 8) & 0xff);
1466
mytimer_write(sc, MYTIMER_REG_RELOAD2, (count >> 16) & 0xff);
1467
mytimer_write(sc, MYTIMER_REG_CTRL,
1468
MYTIMER_CTRL_RUN | MYTIMER_CTRL_IRQEN |
1469
(MYTIMER_AUTO_RELOAD ? MYTIMER_CTRL_RELOAD : 0));
1470
}
1471
1472
/*
1473
* Acknowledge the interrupt so the chip drops its request line. If
1474
* this is forgotten, the CPU takes the interrupt again the moment the
1475
* handler returns, and the machine appears to hang.
1476
*/
1477
static void
1478
mytimer_hw_ack(struct mytimer_softc *sc)
1479
{
1480
mytimer_write(sc, MYTIMER_REG_STATUS, MYTIMER_SR_ZERO);
1481
}
1482
1483
/*
1484
* ######################################################################
1485
* Generic driver below this line
1486
* ######################################################################
1487
*/
1488
1489
/* Convert an interval in microseconds to counter ticks. */
1490
static uint32_t
1491
mytimer_us_to_ticks(struct mytimer_softc *sc, unsigned int interval_us)
1492
{
1493
const uint64_t ticks =
1494
((uint64_t)sc->sc_freq * interval_us + 500000) / 1000000;
1495
1496
if (ticks == 0 || ticks > MYTIMER_COUNT_MAX) {
1497
/*
1498
* The input clock is too slow (ticks == 0) or too fast
1499
* (ticks too big) for this interval. Change the clock,
1500
* the prescaler, or HZ.
1501
*/
1502
panic("%s: can't make a %u us interval from %u Hz "
1503
"(ticks=%llu)", device_xname(sc->sc_dev), interval_us,
1504
sc->sc_freq, (unsigned long long)ticks);
1505
}
1506
1507
return (uint32_t)ticks;
1508
}
1509
1510
/* clock_if: establish our interrupt. `intrfunc' is clock.c's handler. */
1511
static int
1512
mytimer_init(struct clock_if *clk, int (*intrfunc)(void *))
1513
{
1514
struct mytimer_softc * const sc =
1515
container_of(clk, struct mytimer_softc, sc_clkif);
1516
const int phandle = devhandle_to_of(device_handle(sc->sc_dev));
1517
char intrstr[128];
1518
1519
if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
1520
aprint_error_dev(sc->sc_dev, "couldn't decode interrupt\n");
1521
return ENXIO;
1522
}
1523
1524
/*
1525
* The handler's argument must be NULL: clock.c's handler then
1526
* receives the interrupt frame, which hardclock() needs to tell
1527
* whether the interrupted code was in user or kernel mode.
1528
*/
1529
sc->sc_ih = fdtbus_intr_establish_xname(phandle, 0, IPL_SCHED,
1530
FDT_INTR_MPSAFE, intrfunc, NULL, device_xname(sc->sc_dev));
1531
if (sc->sc_ih == NULL) {
1532
aprint_error_dev(sc->sc_dev,
1533
"couldn't establish interrupt at %s\n", intrstr);
1534
return ENXIO;
1535
}
1536
aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", intrstr);
1537
1538
return 0;
1539
}
1540
1541
/* clock_if: start periodic interrupts every `interval_us'. */
1542
static int
1543
mytimer_arm(struct clock_if *clk, unsigned int interval_us)
1544
{
1545
struct mytimer_softc * const sc =
1546
container_of(clk, struct mytimer_softc, sc_clkif);
1547
1548
sc->sc_reload = mytimer_us_to_ticks(sc, interval_us);
1549
mytimer_hw_stop(sc);
1550
mytimer_hw_start(sc, sc->sc_reload);
1551
1552
return 0;
1553
}
1554
1555
/* clock_if: called at the start of every timer interrupt. */
1556
static void
1557
mytimer_intr(struct clock_if *clk)
1558
{
1559
struct mytimer_softc * const sc =
1560
container_of(clk, struct mytimer_softc, sc_clkif);
1561
1562
mytimer_hw_ack(sc);
1563
if (!MYTIMER_AUTO_RELOAD) {
1564
mytimer_hw_start(sc, sc->sc_reload);
1565
}
1566
}
1567
1568
/* clock_if: stop the timer. */
1569
static void
1570
mytimer_disarm(struct clock_if *clk)
1571
{
1572
struct mytimer_softc * const sc =
1573
container_of(clk, struct mytimer_softc, sc_clkif);
1574
1575
mytimer_hw_stop(sc);
1576
}
1577
1578
/*
1579
* ----------------------------------------------------------------------
1580
* Autoconfiguration
1581
* ----------------------------------------------------------------------
1582
*/
1583
1584
static int
1585
mytimer_match(device_t parent, cfdata_t cf, void *aux)
1586
{
1587
struct fdt_attach_args * const faa = aux;
1588
1589
return of_compatible_match(faa->faa_phandle, compat_data);
1590
}
1591
1592
static void
1593
mytimer_attach(device_t parent, device_t self, void *aux)
1594
{
1595
struct mytimer_softc * const sc = device_private(self);
1596
struct fdt_attach_args * const faa = aux;
1597
const int phandle = faa->faa_phandle;
1598
bus_addr_t addr;
1599
bus_size_t size;
1600
struct clk *clk;
1601
uint32_t freq;
1602
1603
sc->sc_dev = self;
1604
sc->sc_bst = faa->faa_bst;
1605
1606
/* --- Registers ------------------------------------------------ */
1607
if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
1608
aprint_error(": couldn't get registers\n");
1609
return;
1610
}
1611
sc->sc_shift = fdtbus_get_reg_shift(phandle, 0);
1612
if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
1613
aprint_error(": couldn't map registers\n");
1614
return;
1615
}
1616
1617
/*
1618
* --- Input clock ----------------------------------------------
1619
* Prefer a "clocks" reference to a clock node; fall back to a
1620
* plain "clock-frequency" property.
1621
*/
1622
clk = fdtbus_clock_get_index(phandle, 0);
1623
if (clk != NULL) {
1624
freq = clk_get_rate(clk);
1625
} else if (of_getprop_uint32(phandle, "clock-frequency", &freq) != 0) {
1626
aprint_error(": no \"clocks\" or \"clock-frequency\"\n");
1627
return;
1628
}
1629
sc->sc_freq = freq / MYTIMER_PRESCALE;
1630
if (sc->sc_freq == 0) {
1631
aprint_error(": invalid clock rate %u Hz\n", freq);
1632
return;
1633
}
1634
1635
/* Make sure the timer is quiet until the clock code arms it. */
1636
mytimer_hw_stop(sc);
1637
1638
aprint_naive("\n");
1639
aprint_normal(": timer, %u Hz count rate\n", sc->sc_freq);
1640
1641
/* --- Register with the clock code ------------------------------ */
1642
sc->sc_clkif.clk_dev = self;
1643
sc->sc_clkif.clk_init = mytimer_init;
1644
sc->sc_clkif.clk_arm = mytimer_arm;
1645
sc->sc_clkif.clk_intr = mytimer_intr;
1646
sc->sc_clkif.clk_disarm = mytimer_disarm;
1647
clock_attach(&sc->sc_clkif);
1648
}
1649
1650
CFATTACH_DECL_NEW(mytimer, sizeof(struct mytimer_softc),
1651
mytimer_match, mytimer_attach, NULL, NULL);
1652
```