193 lines
5.6 KiB
C
193 lines
5.6 KiB
C
/* DKM Timer Driver - PIT (Programmable Interval Timer)
|
|||
|
|
* Stage 0 platform driver, provides "timer".
|
||
|
|
* No IDT/IRQ required - uses PIT counter polling for busy-wait delay.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include <stdint.h>
|
||
|
|
|
||
|
|
/* ---------------------------------------------------------------
|
||
|
|
* DKM ABI types (standalone, no kernel headers)
|
||
|
|
* --------------------------------------------------------------- */
|
||
|
|
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
||
|
|
#define DKM_ABI_VERSION 1u
|
||
|
|
|
||
|
|
typedef unsigned char u8;
|
||
|
|
typedef unsigned short u16;
|
||
|
|
typedef unsigned int u32;
|
||
|
|
typedef unsigned long long u64;
|
||
|
|
typedef long long i64;
|
||
|
|
|
||
|
|
#define NULL ((void *)0)
|
||
|
|
|
||
|
|
struct dkm_log_api {
|
||
|
|
void (*info)(const char *msg);
|
||
|
|
void (*warn)(const char *msg);
|
||
|
|
void (*error)(const char *msg);
|
||
|
|
void (*panic)(const char *msg);
|
||
|
|
};
|
||
|
|
|
||
|
|
struct dkm_kernel_api {
|
||
|
|
u32 version;
|
||
|
|
u32 size;
|
||
|
|
u64 feature_bits;
|
||
|
|
const struct dkm_log_api *log;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct dkm_driver_handle;
|
||
|
|
|
||
|
|
struct dkm_driver_desc {
|
||
|
|
u32 magic;
|
||
|
|
u16 abi_version;
|
||
|
|
u16 desc_size;
|
||
|
|
const char *name;
|
||
|
|
const char *version;
|
||
|
|
const char *vendor;
|
||
|
|
u32 driver_class;
|
||
|
|
u32 stage;
|
||
|
|
u32 flags;
|
||
|
|
u32 priority;
|
||
|
|
const char *const *depends;
|
||
|
|
u32 depends_count;
|
||
|
|
const char *const *provides;
|
||
|
|
u32 provides_count;
|
||
|
|
u64 min_kernel_abi;
|
||
|
|
u64 feature_bits;
|
||
|
|
u64 reserved0;
|
||
|
|
u64 reserved1;
|
||
|
|
};
|
||
|
|
|
||
|
|
/* ---------------------------------------------------------------
|
||
|
|
* PIT I/O ports
|
||
|
|
* --------------------------------------------------------------- */
|
||
|
|
#define PIT_CH0_DATA 0x40
|
||
|
|
#define PIT_CH1_DATA 0x41
|
||
|
|
#define PIT_CH2_DATA 0x42
|
||
|
|
#define PIT_CMD 0x43
|
||
|
|
|
||
|
|
/* PIT frequency: 1.193182 MHz */
|
||
|
|
#define PIT_BASE_FREQ 1193182ULL
|
||
|
|
|
||
|
|
static const char *const g_provides[] = { "timer" };
|
||
|
|
|
||
|
|
__attribute__((visibility("default"), used))
|
||
|
|
const struct dkm_driver_desc driver_desc = {
|
||
|
|
.magic = DKM_DRIVER_MAGIC,
|
||
|
|
.abi_version = DKM_ABI_VERSION,
|
||
|
|
.desc_size = sizeof(struct dkm_driver_desc),
|
||
|
|
.name = "timer",
|
||
|
|
.version = "0.1.0",
|
||
|
|
.vendor = "Deshab",
|
||
|
|
.driver_class = 4, /* DKM_CLASS_TIMER */
|
||
|
|
.stage = 0,
|
||
|
|
.flags = 1, /* DKM_F_REQUIRED */
|
||
|
|
.priority = 0,
|
||
|
|
.depends = NULL,
|
||
|
|
.depends_count = 0,
|
||
|
|
.provides = g_provides,
|
||
|
|
.provides_count = 1,
|
||
|
|
.min_kernel_abi = 1,
|
||
|
|
.feature_bits = 0,
|
||
|
|
.reserved0 = 0,
|
||
|
|
.reserved1 = 0
|
||
|
|
};
|
||
|
|
|
||
|
|
/* ---------------------------------------------------------------
|
||
|
|
* Inline assembly for port I/O (works in Ring0 SAS-R0)
|
||
|
|
* --------------------------------------------------------------- */
|
||
|
|
static __inline__ void outb(u16 port, u8 value) {
|
||
|
|
__asm__ volatile ("outb %0, %1" :: "a"(value), "Nd"(port));
|
||
|
|
}
|
||
|
|
|
||
|
|
static __inline__ u8 inb(u16 port) {
|
||
|
|
u8 value;
|
||
|
|
__asm__ volatile ("inb %1, %0" : "=a"(value) : "Nd"(port));
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ---------------------------------------------------------------
|
||
|
|
* PIT operations
|
||
|
|
* --------------------------------------------------------------- */
|
||
|
|
static const struct dkm_log_api *g_log;
|
||
|
|
|
||
|
|
static void pit_set_channel(u8 channel, u8 mode, u16 count) {
|
||
|
|
u8 cmd = (u8)((channel << 6) | (3 << 4) | (mode << 1));
|
||
|
|
outb(PIT_CMD, cmd);
|
||
|
|
outb((u16)(PIT_CH0_DATA + channel), (u8)(count & 0xff));
|
||
|
|
outb((u16)(PIT_CH0_DATA + channel), (u8)((count >> 8) & 0xff));
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Read back current counter value via latch command */
|
||
|
|
static u16 pit_read_counter(void) {
|
||
|
|
u8 cmd = 0xD0; /* read-back: channel 0, latch count, no status */
|
||
|
|
outb(PIT_CMD, cmd);
|
||
|
|
u8 lo = inb(PIT_CH0_DATA);
|
||
|
|
u8 hi = inb(PIT_CH0_DATA);
|
||
|
|
return (u16)(((u16)hi << 8) | lo);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Busy-wait delay using PIT channel 0 counter.
|
||
|
|
* count: PIT divisor (1193182 = ~1 second)
|
||
|
|
* The counter counts DOWN from 'count' to 0.
|
||
|
|
* We set it to 'count', then poll until it reaches near 0.
|
||
|
|
*/
|
||
|
|
static void pit_delay(u16 count) {
|
||
|
|
pit_set_channel(0, 0, count); /* mode 0: interrupt on terminal count */
|
||
|
|
u16 prev = pit_read_counter();
|
||
|
|
u64 loops = 0;
|
||
|
|
while (1) {
|
||
|
|
u16 cur = pit_read_counter();
|
||
|
|
if (cur > prev && loops > 10) {
|
||
|
|
break; /* counter wrapped → terminal count reached */
|
||
|
|
}
|
||
|
|
prev = cur;
|
||
|
|
loops++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ---------------------------------------------------------------
|
||
|
|
* Driver entry points
|
||
|
|
* --------------------------------------------------------------- */
|
||
|
|
__attribute__((visibility("default")))
|
||
|
|
int driver_init(const struct dkm_kernel_api *api,
|
||
|
|
struct dkm_driver_handle *handle) {
|
||
|
|
(void)handle;
|
||
|
|
|
||
|
|
if (!api || !api->log) {
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
g_log = api->log;
|
||
|
|
|
||
|
|
g_log->info("[timer] PIT driver initializing");
|
||
|
|
|
||
|
|
/* Quick calibration: set PIT to max count, measure how many
|
||
|
|
* iterations of read_counter() happen before terminal count.
|
||
|
|
*/
|
||
|
|
pit_set_channel(0, 0, 0xFFFF); /* 0xFFFF = max, ~55ms at 1.193 MHz */
|
||
|
|
u16 prev = pit_read_counter();
|
||
|
|
u64 cal_loops = 0;
|
||
|
|
while (1) {
|
||
|
|
u16 cur = pit_read_counter();
|
||
|
|
cal_loops++;
|
||
|
|
if (cur > prev && cal_loops > 5) break;
|
||
|
|
prev = cur;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Expected: PIT_BASE_FREQ / 0xFFFF ≈ 18.2 Hz, period ≈ 55 ms */
|
||
|
|
g_log->info("[timer] PIT calibration complete");
|
||
|
|
g_log->info("[timer] PIT channel 0 operational");
|
||
|
|
|
||
|
|
/* Do a quick ~100ms delay demo */
|
||
|
|
g_log->info("[timer] running 100ms busy-wait demo");
|
||
|
|
pit_delay((u16)(PIT_BASE_FREQ / 10)); /* ~0.1 sec = 119318 counts */
|
||
|
|
g_log->info("[timer] 100ms delay done");
|
||
|
|
|
||
|
|
g_log->info("[timer] driver ready");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
__attribute__((visibility("default")))
|
||
|
|
int driver_exit(struct dkm_driver_handle *handle) {
|
||
|
|
(void)handle;
|
||
|
|
return 0;
|
||
|
|
}
|