forked from Minki/linux
331b831983
I've always found this flag confusing. Now that devfs is no longer around, it has been renamed, and the documentation for when this flag should be used has been updated. Also fixes all drivers that use this flag. Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
462 lines
9.4 KiB
C
462 lines
9.4 KiB
C
/*
|
|
* Copyright (C) 2003, Axis Communications AB.
|
|
*/
|
|
|
|
#include <linux/config.h>
|
|
#include <linux/console.h>
|
|
#include <linux/init.h>
|
|
#include <linux/major.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/tty.h>
|
|
#include <asm/system.h>
|
|
#include <asm/io.h>
|
|
#include <asm/arch/hwregs/ser_defs.h>
|
|
#include <asm/arch/hwregs/dma_defs.h>
|
|
#include <asm/arch/pinmux.h>
|
|
|
|
#include <asm/irq.h>
|
|
#include <asm/arch/hwregs/intr_vect_defs.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
|
|
}
|
|
};
|
|
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];
|
|
#else
|
|
NULL;
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef CONFIG_ETRAXFS_SIM
|
|
extern void print_str( const char *str );
|
|
static char buffer[1024];
|
|
static char msg[] = "Debug: ";
|
|
static int buffer_pos = sizeof(msg) - 1;
|
|
#endif
|
|
|
|
extern struct tty_driver *serial_driver;
|
|
|
|
static void
|
|
start_port(struct dbg_port* p)
|
|
{
|
|
if (!p)
|
|
return;
|
|
|
|
if (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);
|
|
|
|
/* 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};
|
|
|
|
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);
|
|
}
|
|
|
|
/* No debug */
|
|
#ifdef CONFIG_ETRAX_DEBUG_PORT_NULL
|
|
|
|
static void
|
|
console_write(struct console *co, const char *buf, unsigned int len)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Target debug */
|
|
#elif !defined(CONFIG_ETRAXFS_SIM)
|
|
|
|
static void
|
|
console_write_direct(struct console *co, const char *buf, unsigned int len)
|
|
{
|
|
int i;
|
|
reg_ser_r_stat_din stat;
|
|
reg_ser_rw_tr_dma_en tr_dma_en, old;
|
|
|
|
/* Switch to manual mode */
|
|
tr_dma_en = old = REG_RD (ser, port->instance, rw_tr_dma_en);
|
|
if (tr_dma_en.en == regk_ser_yes) {
|
|
tr_dma_en.en = regk_ser_no;
|
|
REG_WR(ser, port->instance, rw_tr_dma_en, tr_dma_en);
|
|
}
|
|
|
|
/* Send data */
|
|
for (i = 0; i < len; i++) {
|
|
/* LF -> CRLF */
|
|
if (buf[i] == '\n') {
|
|
do {
|
|
stat = REG_RD (ser, port->instance, r_stat_din);
|
|
} while (!stat.tr_rdy);
|
|
REG_WR_INT (ser, port->instance, rw_dout, '\r');
|
|
}
|
|
/* 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, buf[i]);
|
|
}
|
|
|
|
/* Restore mode */
|
|
if (tr_dma_en.en != old.en)
|
|
REG_WR(ser, port->instance, rw_tr_dma_en, old);
|
|
}
|
|
|
|
static void
|
|
console_write(struct console *co, const char *buf, unsigned int len)
|
|
{
|
|
if (!port)
|
|
return;
|
|
console_write_direct(co, buf, len);
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
/* VCS debug */
|
|
|
|
static void
|
|
console_write(struct console *co, const char *buf, unsigned int len)
|
|
{
|
|
char* pos;
|
|
pos = memchr(buf, '\n', len);
|
|
if (pos) {
|
|
int l = ++pos - buf;
|
|
memcpy(buffer + buffer_pos, buf, l);
|
|
memcpy(buffer, msg, sizeof(msg) - 1);
|
|
buffer[buffer_pos + l] = '\0';
|
|
print_str(buffer);
|
|
buffer_pos = sizeof(msg) - 1;
|
|
if (pos - buf != len) {
|
|
memcpy(buffer + buffer_pos, pos, len - l);
|
|
buffer_pos += len - l;
|
|
}
|
|
} else {
|
|
memcpy(buffer + buffer_pos, buf, len);
|
|
buffer_pos += len;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
int raw_printk(const char *fmt, ...)
|
|
{
|
|
static char buf[1024];
|
|
int printed_len;
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
printed_len = vsnprintf(buf, sizeof(buf), fmt, args);
|
|
va_end(args);
|
|
console_write(NULL, buf, strlen(buf));
|
|
return printed_len;
|
|
}
|
|
|
|
void
|
|
stupid_debug(char* buf)
|
|
{
|
|
console_write(NULL, buf, strlen(buf));
|
|
}
|
|
|
|
#ifdef CONFIG_ETRAX_KGDB
|
|
/* Use polling to get a single character from the kernel debug port */
|
|
int
|
|
getDebugChar(void)
|
|
{
|
|
reg_ser_rs_status_data stat;
|
|
reg_ser_rw_ack_intr ack_intr = { 0 };
|
|
|
|
do {
|
|
stat = REG_RD(ser, kgdb_instance, rs_status_data);
|
|
} while (!stat.data_avail);
|
|
|
|
/* Ack the data_avail interrupt. */
|
|
ack_intr.data_avail = 1;
|
|
REG_WR(ser, kgdb_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_status_data stat;
|
|
do {
|
|
stat = REG_RD (ser, kgdb_instance, r_status_data);
|
|
} while (!stat.tr_ready);
|
|
REG_WR (ser, kgdb_instance, rw_data_out, REG_TYPE_CONV(reg_ser_rw_data_out, int, val));
|
|
}
|
|
#endif /* CONFIG_ETRAX_KGDB */
|
|
|
|
static int __init
|
|
console_setup(struct console *co, char *options)
|
|
{
|
|
char* s;
|
|
|
|
if (options) {
|
|
port = &ports[co->index];
|
|
port->baudrate = 115200;
|
|
port->parity = 'N';
|
|
port->bits = 8;
|
|
port->baudrate = simple_strtoul(options, NULL, 10);
|
|
s = options;
|
|
while(*s >= '0' && *s <= '9')
|
|
s++;
|
|
if (*s) port->parity = *s++;
|
|
if (*s) port->bits = *s++ - '0';
|
|
port->started = 0;
|
|
start_port(port);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* This is a dummy serial device that throws away anything written to it.
|
|
* This is used when no debug output is wanted.
|
|
*/
|
|
static struct tty_driver dummy_driver;
|
|
|
|
static int dummy_open(struct tty_struct *tty, struct file * filp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void dummy_close(struct tty_struct *tty, struct file * filp)
|
|
{
|
|
}
|
|
|
|
static int dummy_write(struct tty_struct * tty,
|
|
const unsigned char *buf, int count)
|
|
{
|
|
return count;
|
|
}
|
|
|
|
static int
|
|
dummy_write_room(struct tty_struct *tty)
|
|
{
|
|
return 8192;
|
|
}
|
|
|
|
void __init
|
|
init_dummy_console(void)
|
|
{
|
|
memset(&dummy_driver, 0, sizeof(struct tty_driver));
|
|
dummy_driver.driver_name = "serial";
|
|
dummy_driver.name = "ttyS";
|
|
dummy_driver.major = TTY_MAJOR;
|
|
dummy_driver.minor_start = 68;
|
|
dummy_driver.num = 1; /* etrax100 has 4 serial ports */
|
|
dummy_driver.type = TTY_DRIVER_TYPE_SERIAL;
|
|
dummy_driver.subtype = SERIAL_TYPE_NORMAL;
|
|
dummy_driver.init_termios = tty_std_termios;
|
|
dummy_driver.init_termios.c_cflag =
|
|
B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */
|
|
dummy_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
|
|
|
|
dummy_driver.open = dummy_open;
|
|
dummy_driver.close = dummy_close;
|
|
dummy_driver.write = dummy_write;
|
|
dummy_driver.write_room = dummy_write_room;
|
|
if (tty_register_driver(&dummy_driver))
|
|
panic("Couldn't register dummy serial driver\n");
|
|
}
|
|
|
|
static struct tty_driver*
|
|
crisv32_console_device(struct console* co, int *index)
|
|
{
|
|
if (port)
|
|
*index = port->nbr;
|
|
return port ? serial_driver : &dummy_driver;
|
|
}
|
|
|
|
static struct console sercons = {
|
|
name : "ttyS",
|
|
write: console_write,
|
|
read : NULL,
|
|
device : crisv32_console_device,
|
|
unblank : NULL,
|
|
setup : console_setup,
|
|
flags : CON_PRINTBUFFER,
|
|
index : -1,
|
|
cflag : 0,
|
|
next : NULL
|
|
};
|
|
static struct console sercons0 = {
|
|
name : "ttyS",
|
|
write: console_write,
|
|
read : NULL,
|
|
device : crisv32_console_device,
|
|
unblank : NULL,
|
|
setup : console_setup,
|
|
flags : CON_PRINTBUFFER,
|
|
index : 0,
|
|
cflag : 0,
|
|
next : NULL
|
|
};
|
|
|
|
static struct console sercons1 = {
|
|
name : "ttyS",
|
|
write: console_write,
|
|
read : NULL,
|
|
device : crisv32_console_device,
|
|
unblank : NULL,
|
|
setup : console_setup,
|
|
flags : CON_PRINTBUFFER,
|
|
index : 1,
|
|
cflag : 0,
|
|
next : NULL
|
|
};
|
|
static struct console sercons2 = {
|
|
name : "ttyS",
|
|
write: console_write,
|
|
read : NULL,
|
|
device : crisv32_console_device,
|
|
unblank : NULL,
|
|
setup : console_setup,
|
|
flags : CON_PRINTBUFFER,
|
|
index : 2,
|
|
cflag : 0,
|
|
next : NULL
|
|
};
|
|
static struct console sercons3 = {
|
|
name : "ttyS",
|
|
write: console_write,
|
|
read : NULL,
|
|
device : crisv32_console_device,
|
|
unblank : NULL,
|
|
setup : console_setup,
|
|
flags : CON_PRINTBUFFER,
|
|
index : 3,
|
|
cflag : 0,
|
|
next : NULL
|
|
};
|
|
|
|
/* Register console for printk's, etc. */
|
|
int __init
|
|
init_etrax_debug(void)
|
|
{
|
|
static int first = 1;
|
|
|
|
if (!first) {
|
|
unregister_console(&sercons);
|
|
register_console(&sercons0);
|
|
register_console(&sercons1);
|
|
register_console(&sercons2);
|
|
register_console(&sercons3);
|
|
init_dummy_console();
|
|
return 0;
|
|
}
|
|
first = 0;
|
|
register_console(&sercons);
|
|
start_port(port);
|
|
|
|
#ifdef CONFIG_ETRAX_KGDB
|
|
start_port(kgdb_port);
|
|
#endif /* CONFIG_ETRAX_KGDB */
|
|
return 0;
|
|
}
|
|
|
|
__initcall(init_etrax_debug);
|