#define NSCC 1
#if NSCC > 0
#include <mach_kdb.h>
#include <platforms.h>
#include <kern/spl.h>
#include <mach/std_types.h>
#include <types.h>
#include <sys/syslog.h>
#include <ppc/misc_protos.h>
#include <ppc/proc_reg.h>
#include <ppc/exception.h>
#include <ppc/Firmware.h>
#include <ppc/serial_io.h>
#include <ppc/scc_8530.h>
#if MACH_KDB
#include <machine/db_machdep.h>
#endif
#define kdebug_state() (1)
#define delay(x) { volatile int _d_; for (_d_ = 0; _d_ < (10000*x); _d_++) ; }
#define NSCC_LINE 2
#define SCC_DMA_TRANSFERS 0
struct scc_tty scc_tty[NSCC_LINE];
#define scc_tty_for(chan) (&scc_tty[chan])
#define scc_dev_no(chan) ((chan)^0x01)
#define scc_chan(dev_no) ((dev_no)^0x01)
extern unsigned int disableSerialOuput;
int serial_initted = 0;
unsigned int scc_parm_done = 0;
extern unsigned int serialmode;
static struct scc_byte {
unsigned char reg;
unsigned char val;
} scc_init_hw[] = {
9, 0x80,
4, 0x44,
3, 0xC0,
5, 0xE2,
2, 0x00,
10, 0x00,
11, 0x50,
12, 0x0A,
13, 0x00,
3, 0xC1,
5, 0xEA,
14, 0x01,
15, 0x00,
0, 0x10,
0, 0x10,
#if 0
1, 0x12,
#else
1, 0x10,
#endif
9, 0x0A
};
static int scc_init_hw_count = sizeof(scc_init_hw)/sizeof(scc_init_hw[0]);
enum scc_error {SCC_ERR_NONE, SCC_ERR_PARITY, SCC_ERR_BREAK, SCC_ERR_OVERRUN};
#define SERIAL_CLOCK_FREQUENCY (115200*2)
#define convert_baud_rate(rate) ((((SERIAL_CLOCK_FREQUENCY) + (rate)) / (2 * (rate))) - 2)
#define DEFAULT_SPEED 57600
#define DEFAULT_PORT0_SPEED 1200
#define DEFAULT_FLAGS (TF_LITOUT|TF_ECHO)
int scc_param(struct scc_tty *tp);
struct scc_softc scc_softc[NSCC];
caddr_t scc_std[NSCC] = { (caddr_t) 0};
#define SCC_RR1_ERRS (SCC_RR1_FRAME_ERR|SCC_RR1_RX_OVERRUN|SCC_RR1_PARITY_ERR)
#define SCC_RR3_ALL (SCC_RR3_RX_IP_A|SCC_RR3_TX_IP_A|SCC_RR3_EXT_IP_A|\
SCC_RR3_RX_IP_B|SCC_RR3_TX_IP_B|SCC_RR3_EXT_IP_B)
#define DEBUG_SCC
#undef DEBUG_SCC
#ifdef DEBUG_SCC
static int total_chars, total_ints, total_overruns, total_errors, num_ints, max_chars;
static int chars_received[8];
static int __SCC_STATS = 0;
static int max_in_q = 0;
static int max_out_q = 0;
#endif
DECL_FUNNEL(, scc_funnel)
boolean_t scc_funnel_initted = FALSE;
#define SCC_FUNNEL scc_funnel
#define SCC_FUNNEL_INITTED scc_funnel_initted
boolean_t scc_uses_modem_control = FALSE;
decl_simple_lock_data(,scc_stomp)
void
initialize_serial( caddr_t scc_phys_base )
{
int i, chan, bits;
scc_regmap_t regs;
DECL_FUNNEL_VARS
assert( scc_phys_base );
if (!SCC_FUNNEL_INITTED) {
FUNNEL_INIT(&SCC_FUNNEL, master_processor);
SCC_FUNNEL_INITTED = TRUE;
}
FUNNEL_ENTER(&SCC_FUNNEL);
if (serial_initted) {
FUNNEL_EXIT(&SCC_FUNNEL);
return;
}
simple_lock_init(&scc_stomp, FALSE);
scc_softc[0].full_modem = TRUE;
scc_std[0] = scc_phys_base;
regs = scc_softc[0].regs = (scc_regmap_t)scc_std[0];
for (chan = 0; chan < NSCC_LINE; chan++) {
if (chan == 1)
scc_init_hw[0].val = 0x80;
for (i = 0; i < scc_init_hw_count; i++) {
scc_write_reg(regs, chan,
scc_init_hw[i].reg, scc_init_hw[i].val);
}
}
if (scc_probe()) {
for (i = 0; i < NSCC_LINE; i++) {
scc_softc[0].softr[i].wr5 = SCC_WR5_DTR | SCC_WR5_RTS;
scc_param(scc_tty_for(i));
scc_write_reg(regs, i, 9, SCC_WR9_NV);
scc_read_reg_zero(regs, 0, bits);
}
scc_parm_done = 1;
}
serial_initted = TRUE;
FUNNEL_EXIT(&SCC_FUNNEL);
return;
}
int
scc_probe(void)
{
scc_softc_t scc;
register int val, i;
register scc_regmap_t regs;
spl_t s;
DECL_FUNNEL_VARS
if (!SCC_FUNNEL_INITTED) {
FUNNEL_INIT(&SCC_FUNNEL, master_processor);
SCC_FUNNEL_INITTED = TRUE;
}
FUNNEL_ENTER(&SCC_FUNNEL);
regs = (scc_regmap_t)scc_std[0];
if (regs == (scc_regmap_t) 0) {
FUNNEL_EXIT(&SCC_FUNNEL);
return 0;
}
scc = &scc_softc[0];
scc->regs = regs;
s = splhigh();
for (i = 0; i < NSCC_LINE; i++) {
register struct scc_tty *tp;
tp = scc_tty_for(i);
tp->t_addr = (char*)(0x80000000L + (i&1));
if (i == 0) {
tp->t_ispeed = DEFAULT_PORT0_SPEED;
tp->t_ospeed = DEFAULT_PORT0_SPEED;
} else {
tp->t_ispeed = DEFAULT_SPEED;
tp->t_ospeed = DEFAULT_SPEED;
}
tp->t_flags = DEFAULT_FLAGS;
scc->softr[i].speed = -1;
tp->t_state |= TS_MIN;
tp->t_dev = scc_dev_no(i);
}
splx(s);
FUNNEL_EXIT(&SCC_FUNNEL);
return 1;
}
int
scc_getc(int unit, int line, boolean_t wait, boolean_t raw)
{
register scc_regmap_t regs;
unsigned char c, value;
int rcvalue, from_line;
spl_t s = splhigh();
DECL_FUNNEL_VARS
FUNNEL_ENTER(&SCC_FUNNEL);
simple_lock(&scc_stomp);
regs = scc_softc[0].regs;
again:
rcvalue = 0;
while (1) {
scc_read_reg_zero(regs, line, value);
if (value & SCC_RR0_RX_AVAIL)
break;
if (!wait) {
simple_unlock(&scc_stomp);
splx(s);
FUNNEL_EXIT(&SCC_FUNNEL);
return -1;
}
}
scc_read_reg(regs, line, SCC_RR1, value);
scc_read_data(regs, line, c);
#if MACH_KDB
if (console_is_serial() &&
c == ('_' & 0x1f)) {
simple_unlock(&scc_stomp);
Debugger("Serial Line Request");
simple_lock(&scc_stomp);
scc_write_reg(regs, line, SCC_RR0, SCC_RESET_HIGHEST_IUS);
if (wait) {
goto again;
}
simple_unlock(&scc_stomp);
splx(s);
FUNNEL_EXIT(&SCC_FUNNEL);
return -1;
}
#endif
if (value&(SCC_RR1_PARITY_ERR | SCC_RR1_RX_OVERRUN | SCC_RR1_FRAME_ERR)) {
scc_write_reg(regs, line, SCC_RR0, SCC_RESET_ERROR);
if (wait) {
scc_write_reg(regs, line, SCC_RR0, SCC_RESET_HIGHEST_IUS);
goto again;
}
}
scc_write_reg(regs, line, SCC_RR0, SCC_RESET_HIGHEST_IUS);
simple_unlock(&scc_stomp);
splx(s);
FUNNEL_EXIT(&SCC_FUNNEL);
return c;
}
int
scc_putc(int unit, int line, int c)
{
scc_regmap_t regs;
spl_t s;
unsigned char value;
DECL_FUNNEL_VARS
if (disableSerialOuput)
return 0;
s = splhigh();
FUNNEL_ENTER(&SCC_FUNNEL);
simple_lock(&scc_stomp);
regs = scc_softc[0].regs;
do {
scc_read_reg(regs, line, SCC_RR0, value);
if (value & SCC_RR0_TX_EMPTY)
break;
delay(1);
} while (1);
scc_write_data(regs, line, c);
do {
scc_read_reg(regs, line, SCC_RR0, value);
if (value & SCC_RR0_TX_EMPTY)
break;
} while (1);
scc_write_reg(regs, line, SCC_RR0, SCC_RESET_HIGHEST_IUS);
simple_unlock(&scc_stomp);
splx(s);
FUNNEL_EXIT(&SCC_FUNNEL);
return 0;
}
void
powermac_scc_set_datum(scc_regmap_t regs, unsigned int offset, unsigned char value)
{
volatile unsigned char *address = (unsigned char *) regs + offset;
assert(FUNNEL_IN_USE(&SCC_FUNNEL));
*address = value;
eieio();
assert(FUNNEL_IN_USE(&SCC_FUNNEL));
}
unsigned char
powermac_scc_get_datum(scc_regmap_t regs, unsigned int offset)
{
volatile unsigned char *address = (unsigned char *) regs + offset;
unsigned char value;
assert(FUNNEL_IN_USE(&SCC_FUNNEL));
value = *address; eieio();
return value;
assert(FUNNEL_IN_USE(&SCC_FUNNEL));
}
int
scc_param(struct scc_tty *tp)
{
scc_regmap_t regs;
unsigned char value;
unsigned short speed_value;
int bits, chan;
spl_t s;
struct scc_softreg *sr;
scc_softc_t scc;
assert(FUNNEL_IN_USE(&SCC_FUNNEL));
s = splhigh();
simple_lock(&scc_stomp);
chan = scc_chan(tp->t_dev);
scc = &scc_softc[0];
regs = scc->regs;
sr = &scc->softr[chan];
if ((sr->flags & (TF_ODDP|TF_EVENP)) == (tp->t_flags & (TF_ODDP|TF_EVENP))
&& sr->speed == tp->t_ispeed) {
assert(FUNNEL_IN_USE(&SCC_FUNNEL));
simple_unlock(&scc_stomp);
splx(s);
return 0;
}
if(scc_parm_done) {
scc_write_reg(regs, chan, 3, SCC_WR3_RX_8_BITS|SCC_WR3_RX_ENABLE);
sr->wr1 = SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_EXT_IE;
scc_write_reg(regs, chan, 1, sr->wr1);
scc_write_reg(regs, chan, 15, SCC_WR15_ENABLE_ESCC);
scc_write_reg(regs, chan, 7, SCC_WR7P_RX_FIFO);
scc_write_reg(regs, chan, 0, SCC_IE_NEXT_CHAR);
scc_write_reg(regs, chan, 0, SCC_RESET_EXT_IP);
scc_write_reg(regs, chan, 0, SCC_RESET_EXT_IP);
scc_write_reg(regs, chan, 9, SCC_WR9_MASTER_IE|SCC_WR9_NV);
scc_read_reg_zero(regs, 0, bits);
sr->wr1 = SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_EXT_IE;
scc_write_reg(regs, chan, 1, sr->wr1);
scc_write_reg(regs, chan, 0, SCC_IE_NEXT_CHAR);
simple_unlock(&scc_stomp);
splx(s);
return 0;
}
sr->flags = tp->t_flags;
sr->speed = tp->t_ispeed;
if (tp->t_ispeed == 0) {
sr->wr5 &= ~SCC_WR5_DTR;
scc_write_reg(regs, chan, 5, sr->wr5);
simple_unlock(&scc_stomp);
splx(s);
assert(FUNNEL_IN_USE(&SCC_FUNNEL));
return 0;
}
#if SCC_DMA_TRANSFERS
if (scc->dma_initted & (1<<chan))
scc->dma_ops->scc_dma_reset_rx(chan);
#endif
value = SCC_WR4_1_STOP;
if (tp->t_ispeed == 115200)
value |= SCC_WR4_CLK_x32;
else
value |= SCC_WR4_CLK_x16 ;
if ((tp->t_flags & (TF_ODDP | TF_EVENP)) == TF_EVENP)
value |= (SCC_WR4_EVEN_PARITY | SCC_WR4_PARITY_ENABLE);
else if ((tp->t_flags & (TF_ODDP | TF_EVENP)) == TF_ODDP)
value |= SCC_WR4_PARITY_ENABLE;
sr->wr4 = value;
scc_write_reg(regs, chan, 4, sr->wr4);
scc_write_reg(regs, chan, 3, SCC_WR3_RX_8_BITS);
sr->wr5 = SCC_WR5_TX_8_BITS | SCC_WR5_RTS | SCC_WR5_DTR;
scc_write_reg(regs, chan, 5, sr->wr5);
scc_write_reg(regs, chan, 14, 0);
speed_value = convert_baud_rate(tp->t_ispeed);
if (speed_value == 0xffff)
speed_value = 0;
scc_set_timing_base(regs, chan, speed_value);
if (tp->t_ispeed == 115200 || tp->t_ispeed == 230400) {
scc_write_reg(regs, chan, 11, 0);
} else {
scc_write_reg(regs, chan, 11, SCC_WR11_RCLK_BAUDR|SCC_WR11_XTLK_BAUDR);
scc_write_reg(regs, chan, 14, SCC_WR14_BAUDR_ENABLE);
}
scc_write_reg(regs, chan, 3, SCC_WR3_RX_8_BITS|SCC_WR3_RX_ENABLE);
sr->wr1 = SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_EXT_IE;
scc_write_reg(regs, chan, 1, sr->wr1);
scc_write_reg(regs, chan, 15, SCC_WR15_ENABLE_ESCC);
scc_write_reg(regs, chan, 7, SCC_WR7P_RX_FIFO);
scc_write_reg(regs, chan, 0, SCC_IE_NEXT_CHAR);
scc_write_reg(regs, chan, 0, SCC_RESET_EXT_IP);
scc_write_reg(regs, chan, 0, SCC_RESET_EXT_IP);
scc_write_reg(regs, chan, 9, SCC_WR9_MASTER_IE|SCC_WR9_NV);
scc_read_reg_zero(regs, 0, bits);
#if SCC_DMA_TRANSFERS
if (scc->dma_initted & (1<<chan)) {
scc->dma_ops->scc_dma_start_rx(chan);
scc->dma_ops->scc_dma_setup_8530(chan);
} else
#endif
{
sr->wr1 = SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_EXT_IE;
scc_write_reg(regs, chan, 1, sr->wr1);
scc_write_reg(regs, chan, 0, SCC_IE_NEXT_CHAR);
}
sr->wr5 |= SCC_WR5_TX_ENABLE;
scc_write_reg(regs, chan, 5, sr->wr5);
simple_unlock(&scc_stomp);
splx(s);
assert(FUNNEL_IN_USE(&SCC_FUNNEL));
return 0;
}
void
serial_keyboard_init(void)
{
if(!(serialmode & 2)) return;
kprintf("Serial keyboard started\n");
kernel_thread_with_priority(serial_keyboard_start, MAXPRI_STANDARD);
return;
}
void
serial_keyboard_start(void)
{
thread_t cthread;
cthread = current_thread();
stack_privilege(cthread);
serial_keyboard_poll();
panic("serial_keyboard_start: we can't get back here\n");
}
void
serial_keyboard_poll(void)
{
int chr;
uint64_t next;
extern void cons_cinput(char ch);
while(1) {
chr = scc_getc(0, 1, 0, 1);
if(chr < 0) break;
cons_cinput((char)chr);
}
clock_interval_to_deadline(16, 1000000, &next);
assert_wait((event_t)serial_keyboard_poll, THREAD_INTERRUPTIBLE);
thread_set_timer_deadline(next);
thread_block(serial_keyboard_poll);
panic("serial_keyboard_poll: Shouldn't never ever get here...\n");
}
#endif