| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * 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]; |
| #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; |
| } |