-Reinitialize the uart bit modes -Ignore framing/break errors on receive. Unclear why they seem to always be set in the fifo, but seems to be safe to ignore for now -Make sure platform_pgetc returns the right error code -Initialize the output ports to all gpios
261 lines
7.6 KiB
C
261 lines
7.6 KiB
C
/*
|
|
* Copyright (c) 2021 Travis Geiselbrecht
|
|
*
|
|
* Use of this source code is governed by a MIT-style
|
|
* license that can be found in the LICENSE file or at
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
#include "platform_p.h"
|
|
|
|
#include <assert.h>
|
|
#include <lk/err.h>
|
|
#include <lk/debug.h>
|
|
#include <lk/reg.h>
|
|
#include <lk/trace.h>
|
|
#include <lib/cbuf.h>
|
|
#include <kernel/debug.h>
|
|
#include <kernel/thread.h>
|
|
#include <platform/interrupts.h>
|
|
#include <platform/rosco-m68k.h>
|
|
#include <platform/timer.h>
|
|
#include <platform.h>
|
|
|
|
#define LOCAL_TRACE 0
|
|
|
|
// driver for a 68c681 acting as a dual uart and a system timer and a few gpios
|
|
|
|
// ticks in units of ms
|
|
#define TICK_HZ (100U)
|
|
#define TICK_MS (1000U / TICK_HZ)
|
|
STATIC_ASSERT(TICK_HZ * TICK_MS == 1000U);
|
|
static volatile uint32_t ticks;
|
|
|
|
// periodic timer callback stuff
|
|
static platform_timer_callback t_callback;
|
|
static void *t_arg;
|
|
static lk_time_t t_next_periodic_tick;
|
|
static lk_time_t t_periodic_interval;
|
|
|
|
// uart stuff
|
|
#define RXBUF_SIZE 128
|
|
static char uart_rx_buf_data[RXBUF_SIZE];
|
|
static cbuf_t uart_rx_buf;
|
|
|
|
static volatile uint8_t * const DUART_BASE = (void *)0xf00001;
|
|
|
|
// registers, swizzled according to
|
|
// https://github.com/rosco-m68k/rosco_m68k/blob/develop/code/shared/rosco_m68k_public.asm
|
|
enum {
|
|
DUART_REG_MR1A_RW = 0x00, // mode 1 register, channel A
|
|
DUART_REG_MR2A_RW = 0x00, // mode 2 register, channel A
|
|
DUART_REG_SRA_R = 0x01, // status register, channel A
|
|
DUART_REG_CSRA_W = 0x01, // clock select register, channel A
|
|
DUART_REG_MISR_R = 0x02, // masked interrupt status register
|
|
DUART_REG_CRA_W = 0x02, // command register, channel A
|
|
DUART_REG_RHRA_R = 0x03, // rx holding register, channel A
|
|
DUART_REG_THRA_W = 0x03, // tx holding register, channel A
|
|
DUART_REG_IPCR_R = 0x04, // input port config register
|
|
DUART_REG_ACR_W = 0x04, // auxillary control register
|
|
DUART_REG_ISR_R = 0x05, // interrupt status register
|
|
DUART_REG_IMR_W = 0x05, // interrupt mask register
|
|
DUART_REG_CTU_RW = 0x06, // counter timer, upper byte
|
|
DUART_REG_CTL_RW = 0x07, // counter timer, lower byte
|
|
|
|
DUART_REG_MR1B_RW = 0x08, // mode register, channel B
|
|
DUART_REG_SRB_R = 0x09, // status register, channel B
|
|
DUART_REG_CSRB_W = 0x09, // clock select register, channel B
|
|
DUART_REG_CRB_W = 0x0a, // command register, channel B
|
|
DUART_REG_RHRB_R = 0x0b, // rx holding register, channel B
|
|
DUART_REG_THRB_W = 0x0b, // tx holding register, channel B
|
|
|
|
DUART_REG_IVR_RW = 0x0c, // interrupt vector register
|
|
DUART_REG_IP_R = 0x0d, // input port
|
|
DUART_REG_OPCR_W = 0x0d, // output port configuration register
|
|
|
|
DUART_REG_SCC_R = 0x0e, // start counter/timer command
|
|
DUART_REG_STC_R = 0x0f, // stop counter/timer command
|
|
|
|
DUART_REG_SOPBC_W = 0x0e, // set output port bits command
|
|
DUART_REG_COPBC_W = 0x0f, // clear output port bits command
|
|
};
|
|
|
|
// save a copy of whatever IMR was set to before
|
|
static uint8_t cached_imr;
|
|
|
|
static void write_reg(uint reg, uint8_t val) {
|
|
DUART_BASE[reg * 2] = val;
|
|
}
|
|
|
|
static uint8_t read_reg(uint reg) {
|
|
return DUART_BASE[reg * 2];
|
|
}
|
|
|
|
void duart_early_init(void) {
|
|
// clear all IRQs
|
|
cached_imr = 0;
|
|
write_reg(DUART_REG_IMR_W, cached_imr);
|
|
|
|
// Set the IRQ vector to 0x45
|
|
write_reg(DUART_REG_IVR_RW, 0x45);
|
|
|
|
// set the mode
|
|
// !rxrts, rxrdy int, char error mode, n81
|
|
write_reg(DUART_REG_MR1A_RW, (0b10 << 3) | (0b11 << 0));
|
|
// normal channel, no txrts, no cts, stop bit 1
|
|
write_reg(DUART_REG_MR2A_RW, 0x7);
|
|
|
|
// set up a periodic counter at TICK_HZ
|
|
read_reg(DUART_REG_STC_R); // stop the counter
|
|
|
|
// compute the counter
|
|
uint16_t count = 3686400 / 2 / TICK_HZ;
|
|
write_reg(DUART_REG_CTL_RW, count & 0xff);
|
|
write_reg(DUART_REG_CTU_RW, (count >> 8) & 0xff);
|
|
|
|
// set timer mode
|
|
write_reg(DUART_REG_ACR_W, (0b110 << 4)); // timer mode, X1/CLK
|
|
|
|
// start timer
|
|
read_reg(DUART_REG_SCC_R); // start counter
|
|
|
|
// unmask irq
|
|
cached_imr = (1<<3); // counter #1 ready
|
|
write_reg(DUART_REG_IMR_W, cached_imr);
|
|
|
|
// configure the output ports
|
|
write_reg(DUART_REG_OPCR_W, 0);
|
|
}
|
|
|
|
void duart_init(void) {
|
|
// finish uart init to get rx going
|
|
cbuf_initialize_etc(&uart_rx_buf, RXBUF_SIZE, uart_rx_buf_data);
|
|
|
|
// enable uart RX irq
|
|
cached_imr |= (1<<1); // RXRDY/FFULLA
|
|
write_reg(DUART_REG_IMR_W, cached_imr);
|
|
}
|
|
|
|
status_t platform_set_periodic_timer(platform_timer_callback callback, void *arg, lk_time_t interval) {
|
|
LTRACEF("cb %p, arg %p, interval %u\n", callback, arg, interval);
|
|
t_callback = callback;
|
|
t_arg = arg;
|
|
t_periodic_interval = interval;
|
|
t_next_periodic_tick = current_time() + interval;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
enum handler_return duart_irq(void) {
|
|
enum handler_return ret = INT_NO_RESCHEDULE;
|
|
|
|
uint8_t isr = read_reg(DUART_REG_ISR_R);
|
|
|
|
LTRACEF_LEVEL(2, "isr %#hhx\n", isr);
|
|
|
|
if (likely(isr & (1<<3))) { // counter #1 ready
|
|
ticks += TICK_MS;
|
|
|
|
// ack the timer hardware
|
|
read_reg(DUART_REG_STC_R);
|
|
|
|
// call back the registered timer
|
|
if (likely(t_callback)) {
|
|
lk_time_t now = current_time();
|
|
if (unlikely(now >= t_next_periodic_tick)) {
|
|
ret = t_callback(t_arg, now);
|
|
t_next_periodic_tick += t_periodic_interval;
|
|
}
|
|
}
|
|
}
|
|
if (isr & (1<<1)) { // RXRDY/FFULLA
|
|
uint8_t status = read_reg(DUART_REG_SRA_R);
|
|
LTRACEF("RXRDY/FFULLA, status %#hhx\n", status);
|
|
if (status & (1<<0)) { // RXRDY
|
|
// XXX disable, for some reason the top 3 status bits are always set
|
|
#if 0
|
|
if (unlikely(status & (0b111 << 5))) { // any of break, framing, or parity error
|
|
// consume this byte
|
|
__UNUSED volatile uint8_t hole = read_reg(DUART_REG_RHRA_R);
|
|
} else
|
|
#endif
|
|
{
|
|
char c = read_reg(DUART_REG_RHRA_R);
|
|
cbuf_write_char(&uart_rx_buf, c, false);
|
|
ret = INT_RESCHEDULE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void platform_dputc(char c) {
|
|
if (c == '\n') {
|
|
platform_dputc('\r');
|
|
}
|
|
|
|
// spin while TXRDY is clear
|
|
while ((read_reg(DUART_REG_SRA_R) & (1 << 2)) == 0) // TXRDY
|
|
;
|
|
write_reg(DUART_REG_THRA_W, c);
|
|
}
|
|
|
|
int platform_dgetc(char *c, bool wait) {
|
|
return cbuf_read_char(&uart_rx_buf, c, wait);
|
|
}
|
|
|
|
int platform_pgetc(char *c, bool wait) {
|
|
for (;;) {
|
|
uint8_t status = read_reg(DUART_REG_SRA_R);
|
|
if (status & (1<<0)) { // RXRDY
|
|
// XXX disable, for some reason the top 3 status bits are always set
|
|
#if 0
|
|
if (status & (0b111 << 5)) { // any of break, framing, or parity error
|
|
// consume this byte
|
|
__UNUSED volatile uint8_t hole = read_reg(DUART_REG_RHRA_R);
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
*c = read_reg(DUART_REG_RHRA_R);
|
|
return 0;
|
|
}
|
|
if (wait) {
|
|
continue;
|
|
}
|
|
break;
|
|
} while (0);
|
|
|
|
return -1;
|
|
}
|
|
|
|
lk_bigtime_t current_time_hires(void) {
|
|
// TODO: look at the current countdown registers
|
|
return ticks * 1000ULL;
|
|
}
|
|
|
|
lk_time_t current_time(void) {
|
|
return ticks;
|
|
}
|
|
|
|
void target_set_debug_led(unsigned int led, bool on) {
|
|
uint8_t bit = 0;
|
|
switch (led) {
|
|
case 0:
|
|
bit = 5; // green LED
|
|
break;
|
|
case 1:
|
|
bit = 3; // red LED
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
if (on) {
|
|
write_reg(DUART_REG_SOPBC_W, (1<<bit));
|
|
} else {
|
|
write_reg(DUART_REG_COPBC_W, (1<<bit));
|
|
}
|
|
}
|
|
|