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