| /* | |
| * Copyright (C) 2003, Axis Communications AB. | |
| */ | |
| #include <linux/console.h> | |
| #include <linux/kernel.h> | |
| #include <linux/init.h> | |
| #include <linux/string.h> | |
| #include <hwregs/reg_rdwr.h> | |
| #include <hwregs/reg_map.h> | |
| #include <hwregs/ser_defs.h> | |
| #include <hwregs/dma_defs.h> | |
| #include <mach/pinmux.h> | |
| struct dbg_port | |
| { | |
| unsigned char nbr; | |
| unsigned long instance; | |
| unsigned int started; | |
| unsigned long baudrate; | |
| unsigned char parity; | |
| unsigned int bits; | |
| }; | |
| struct dbg_port ports[] = | |
| { | |
| { | |
| 0, | |
| regi_ser0, | |
| 0, | |
| 115200, | |
| 'N', | |
| 8 | |
| }, | |
| { | |
| 1, | |
| regi_ser1, | |
| 0, | |
| 115200, | |
| 'N', | |
| 8 | |
| }, | |
| { | |
| 2, | |
| regi_ser2, | |
| 0, | |
| 115200, | |
| 'N', | |
| 8 | |
| }, | |
| { | |
| 3, | |
| regi_ser3, | |
| 0, | |
| 115200, | |
| 'N', | |
| 8 | |
| }, | |
| #if CONFIG_ETRAX_SERIAL_PORTS == 5 | |
| { | |
| 4, | |
| regi_ser4, | |
| 0, | |
| 115200, | |
| 'N', | |
| 8 | |
| }, | |
| #endif | |
| }; | |
| static struct dbg_port *port = | |
| #if defined(CONFIG_ETRAX_DEBUG_PORT0) | |
| &ports[0]; | |
| #elif defined(CONFIG_ETRAX_DEBUG_PORT1) | |
| &ports[1]; | |
| #elif defined(CONFIG_ETRAX_DEBUG_PORT2) | |
| &ports[2]; | |
| #elif defined(CONFIG_ETRAX_DEBUG_PORT3) | |
| &ports[3]; | |
| #elif defined(CONFIG_ETRAX_DEBUG_PORT4) | |
| &ports[4]; | |
| #else | |
| NULL; | |
| #endif | |
| #ifdef CONFIG_ETRAX_KGDB | |
| static struct dbg_port *kgdb_port = | |
| #if defined(CONFIG_ETRAX_KGDB_PORT0) | |
| &ports[0]; | |
| #elif defined(CONFIG_ETRAX_KGDB_PORT1) | |
| &ports[1]; | |
| #elif defined(CONFIG_ETRAX_KGDB_PORT2) | |
| &ports[2]; | |
| #elif defined(CONFIG_ETRAX_KGDB_PORT3) | |
| &ports[3]; | |
| #elif defined(CONFIG_ETRAX_KGDB_PORT4) | |
| &ports[4]; | |
| #else | |
| NULL; | |
| #endif | |
| #endif | |
| static void start_port(struct dbg_port *p) | |
| { | |
| /* Set up serial port registers */ | |
| reg_ser_rw_tr_ctrl tr_ctrl = {0}; | |
| reg_ser_rw_tr_dma_en tr_dma_en = {0}; | |
| reg_ser_rw_rec_ctrl rec_ctrl = {0}; | |
| reg_ser_rw_tr_baud_div tr_baud_div = {0}; | |
| reg_ser_rw_rec_baud_div rec_baud_div = {0}; | |
| if (!p || p->started) | |
| return; | |
| p->started = 1; | |
| if (p->nbr == 1) | |
| crisv32_pinmux_alloc_fixed(pinmux_ser1); | |
| else if (p->nbr == 2) | |
| crisv32_pinmux_alloc_fixed(pinmux_ser2); | |
| else if (p->nbr == 3) | |
| crisv32_pinmux_alloc_fixed(pinmux_ser3); | |
| #if CONFIG_ETRAX_SERIAL_PORTS == 5 | |
| else if (p->nbr == 4) | |
| crisv32_pinmux_alloc_fixed(pinmux_ser4); | |
| #endif | |
| tr_ctrl.base_freq = rec_ctrl.base_freq = regk_ser_f29_493; | |
| tr_dma_en.en = rec_ctrl.dma_mode = regk_ser_no; | |
| tr_baud_div.div = rec_baud_div.div = 29493000 / p->baudrate / 8; | |
| tr_ctrl.en = rec_ctrl.en = 1; | |
| if (p->parity == 'O') { | |
| tr_ctrl.par_en = regk_ser_yes; | |
| tr_ctrl.par = regk_ser_odd; | |
| rec_ctrl.par_en = regk_ser_yes; | |
| rec_ctrl.par = regk_ser_odd; | |
| } else if (p->parity == 'E') { | |
| tr_ctrl.par_en = regk_ser_yes; | |
| tr_ctrl.par = regk_ser_even; | |
| rec_ctrl.par_en = regk_ser_yes; | |
| rec_ctrl.par = regk_ser_odd; | |
| } | |
| if (p->bits == 7) { | |
| tr_ctrl.data_bits = regk_ser_bits7; | |
| rec_ctrl.data_bits = regk_ser_bits7; | |
| } | |
| REG_WR (ser, p->instance, rw_tr_baud_div, tr_baud_div); | |
| REG_WR (ser, p->instance, rw_rec_baud_div, rec_baud_div); | |
| REG_WR (ser, p->instance, rw_tr_dma_en, tr_dma_en); | |
| REG_WR (ser, p->instance, rw_tr_ctrl, tr_ctrl); | |
| REG_WR (ser, p->instance, rw_rec_ctrl, rec_ctrl); | |
| } | |
| #ifdef CONFIG_ETRAX_KGDB | |
| /* Use polling to get a single character from the kernel debug port */ | |
| int getDebugChar(void) | |
| { | |
| reg_ser_rs_stat_din stat; | |
| reg_ser_rw_ack_intr ack_intr = { 0 }; | |
| do { | |
| stat = REG_RD(ser, kgdb_port->instance, rs_stat_din); | |
| } while (!stat.dav); | |
| /* Ack the data_avail interrupt. */ | |
| ack_intr.dav = 1; | |
| REG_WR(ser, kgdb_port->instance, rw_ack_intr, ack_intr); | |
| return stat.data; | |
| } | |
| /* Use polling to put a single character to the kernel debug port */ | |
| void putDebugChar(int val) | |
| { | |
| reg_ser_r_stat_din stat; | |
| do { | |
| stat = REG_RD(ser, kgdb_port->instance, r_stat_din); | |
| } while (!stat.tr_rdy); | |
| REG_WR_INT(ser, kgdb_port->instance, rw_dout, val); | |
| } | |
| #endif /* CONFIG_ETRAX_KGDB */ | |
| static void __init early_putch(int c) | |
| { | |
| reg_ser_r_stat_din stat; | |
| /* Wait until transmitter is ready and send. */ | |
| do | |
| stat = REG_RD(ser, port->instance, r_stat_din); | |
| while (!stat.tr_rdy); | |
| REG_WR_INT(ser, port->instance, rw_dout, c); | |
| } | |
| static void __init | |
| early_console_write(struct console *con, const char *s, unsigned n) | |
| { | |
| extern void reset_watchdog(void); | |
| int i; | |
| /* Send data. */ | |
| for (i = 0; i < n; i++) { | |
| /* TODO: the '\n' -> '\n\r' translation should be done at the | |
| receiver. Remove it when the serial driver removes it. */ | |
| if (s[i] == '\n') | |
| early_putch('\r'); | |
| early_putch(s[i]); | |
| reset_watchdog(); | |
| } | |
| } | |
| static struct console early_console_dev __initdata = { | |
| .name = "early", | |
| .write = early_console_write, | |
| .flags = CON_PRINTBUFFER | CON_BOOT, | |
| .index = -1 | |
| }; | |
| /* Register console for printk's, etc. */ | |
| int __init init_etrax_debug(void) | |
| { | |
| start_port(port); | |
| /* Register an early console if a debug port was chosen. */ | |
| register_console(&early_console_dev); | |
| #ifdef CONFIG_ETRAX_KGDB | |
| start_port(kgdb_port); | |
| #endif /* CONFIG_ETRAX_KGDB */ | |
| return 0; | |
| } |