mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2025-12-27 15:00:16 +00:00
bsps/i386: Move shared files to bsps
This patch is a part of the BSP source reorganization. Update #3285.
This commit is contained in:
@@ -77,9 +77,9 @@ endif
|
||||
|
||||
# console (non-graphics support)
|
||||
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/console/serial_mouse_config.c
|
||||
librtemsbsp_a_SOURCES += ../shared/comm/uart.c
|
||||
librtemsbsp_a_SOURCES += ../shared/comm/tty_drv.c
|
||||
librtemsbsp_a_SOURCES += ../shared/realmode_int/realmode_int.c
|
||||
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/console/uart.c
|
||||
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/console/tty_drv.c
|
||||
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/start/realmode_int.c
|
||||
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/dev/serial/legacy-console.c
|
||||
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/console/console_select.c
|
||||
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/console/console_control.c
|
||||
@@ -91,9 +91,9 @@ librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/console/uart_bus_pci.
|
||||
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/console/gdb_select.c
|
||||
|
||||
# gdb
|
||||
librtemsbsp_a_SOURCES += ../shared/comm/i386-stub.c
|
||||
librtemsbsp_a_SOURCES += ../shared/comm/i386-stub-glue.c
|
||||
librtemsbsp_a_SOURCES += ../shared/comm/gdb_glue.c
|
||||
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/console/i386-stub.c
|
||||
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/console/i386-stub-glue.c
|
||||
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/console/gdb_glue.c
|
||||
|
||||
# gnat
|
||||
|
||||
@@ -132,8 +132,8 @@ librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/ata/idecfg.c
|
||||
endif
|
||||
|
||||
if HAS_SMP
|
||||
librtemsbsp_a_SOURCES += ../shared/smp/getcpuid.c
|
||||
librtemsbsp_a_SOURCES += ../shared/smp/smp-imps.c
|
||||
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/start/getcpuid.c
|
||||
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/start/smp-imps.c
|
||||
|
||||
project_lib_DATA += appstart.$(OBJEXT)
|
||||
appcpustart.$(OBJEXT): ../../../../../../bsps/i386/pc386/start/start16.S
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
i386/pc386 GDB Stub
|
||||
|
||||
The i386 GDB stub has been updated to use the libchip drivers for the
|
||||
NS16550. Make sure you have detect the device and you have added a console
|
||||
entry. On the PC the legacy and PCI boards are supported.
|
||||
|
||||
This GDB stub glue code is specific to the UART protocol defined in libbchip.
|
||||
|
||||
The pc386 BSP has boot command line options to manage GDB support.
|
||||
|
||||
a) Find the minor number of the console device:
|
||||
|
||||
#include <console_private.h>
|
||||
|
||||
rtems_device_minor_number minor = 0;
|
||||
|
||||
if (console_find_console_entry("/dev/com1",
|
||||
strlen("/dev/com1") - 1, &minor) == NULL)
|
||||
error("driver not found\n");
|
||||
|
||||
Note, this call is part of the private console API and may change.
|
||||
|
||||
b) To start GDB stub, run this:
|
||||
|
||||
#include <bsp.h>
|
||||
|
||||
/* Init GDB glue */
|
||||
i386_stub_glue_init(minor);
|
||||
|
||||
/* Init GDB stub itself */
|
||||
set_debug_traps();
|
||||
|
||||
/*
|
||||
* Init GDB break in capability,
|
||||
* has to be called after
|
||||
* set_debug_traps
|
||||
*/
|
||||
i386_stub_glue_init_breakin();
|
||||
|
||||
/* Put breakpoint in */
|
||||
breakpoint();
|
||||
|
||||
c) To run use GDB:
|
||||
|
||||
$ i386-rtems4.12-gdb hello.exe
|
||||
GNU gdb (GDB) 7.11
|
||||
Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
|
||||
This is free software: you are free to change and redistribute it.
|
||||
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
|
||||
and "show warranty" for details.
|
||||
This GDB was configured as "--host=x86_64-freebsd10.1 --target=i386-rtems4.12".
|
||||
Type "show configuration" for configuration details.
|
||||
For bug reporting instructions, please see:
|
||||
<http://www.gnu.org/software/gdb/bugs/>.
|
||||
Find the GDB manual and other documentation resources online at:
|
||||
<http://www.gnu.org/software/gdb/documentation/>.
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "word"...
|
||||
Reading symbols from hello.exe...done.
|
||||
(gdb) target remote /dev/cuaU5
|
||||
Remote debugging using /dev/cuaU5
|
||||
0x00103fda in breakpoint () at i386-stub.c:1004
|
||||
1004 BREAKPOINT ();
|
||||
(gdb) b Init
|
||||
Breakpoint 1 at 0x1001e0: file init.c, line 29.
|
||||
(gdb) c
|
||||
Continuing.
|
||||
|
||||
Breakpoint 1, Init (ignored=1269800) at init.c:29
|
||||
29 {
|
||||
(gdb)
|
||||
|
||||
Pressing ^C works and if running the board should halt when GDB connects.
|
||||
|
||||
e) Use ser2net to provide reomve access over a network to a board. Install the
|
||||
ser2net package and add a configuration for the port GDB connects to. For
|
||||
example:
|
||||
|
||||
0005:raw:0:/dev/cuaU5:115200
|
||||
|
||||
Start ser2net running then connect GDB using:
|
||||
|
||||
(gdb) target remote myhost:30005
|
||||
@@ -1,61 +0,0 @@
|
||||
/* gdb_glue
|
||||
*
|
||||
* Interface to initialize the GDB.
|
||||
*
|
||||
* COPYRIGHT (c) 1989-1998.
|
||||
* On-Line Applications Research Corporation (OAR).
|
||||
*
|
||||
* The license and distribution terms for this file may be
|
||||
* found in the file LICENSE in this distribution or at
|
||||
* http://www.rtems.org/license/LICENSE.
|
||||
*/
|
||||
|
||||
#include <bsp.h>
|
||||
#include <stdio.h>
|
||||
#include <uart.h>
|
||||
|
||||
#define BREAKPOINT() __asm__ (" int $3");
|
||||
|
||||
extern int BSPConsolePort;
|
||||
void i386_stub_glue_init(int);
|
||||
void i386_stub_glue_init_breakin(void);
|
||||
void set_debug_traps(void);
|
||||
|
||||
/* Init GDB glue */
|
||||
void init_remote_gdb( void )
|
||||
{
|
||||
if(BSPConsolePort != BSP_UART_COM2)
|
||||
{
|
||||
/*
|
||||
* If com2 is not used as console use it for
|
||||
* debugging
|
||||
*/
|
||||
|
||||
i386_stub_glue_init(BSP_UART_COM2);
|
||||
printf( "Remote GDB using COM2...\n" );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise use com1 */
|
||||
i386_stub_glue_init(BSP_UART_COM1);
|
||||
printf( "Remote GDB using COM1...\n" );
|
||||
}
|
||||
|
||||
printf( "Remote GDB: setting traps...\n" );
|
||||
/* Init GDB stub itself */
|
||||
set_debug_traps();
|
||||
|
||||
printf( "Remote GDB: waiting remote connection....\n" );
|
||||
|
||||
/*
|
||||
* Init GDB break in capability,
|
||||
* has to be called after
|
||||
* set_debug_traps
|
||||
*/
|
||||
i386_stub_glue_init_breakin();
|
||||
|
||||
/* Put breakpoint in */
|
||||
/* breakpoint(); */
|
||||
/* BREAKPOINT(); */
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016.
|
||||
* Chris Johns <chrisj@rtems.org>
|
||||
*
|
||||
* This software is Copyright (C) 1998 by T.sqware - all rights limited
|
||||
* It is provided in to the public domain "as is", can be freely modified
|
||||
* as far as this copyight notice is kept unchanged, but does not imply
|
||||
* an endorsement by T.sqware of the product in which it is included.
|
||||
*/
|
||||
|
||||
#include <bsp.h>
|
||||
#include <bsp/irq-generic.h>
|
||||
#include <libchip/serial.h>
|
||||
|
||||
#include "../../../../../../../bsps/shared/dev/serial/legacy-console.h"
|
||||
|
||||
int putDebugChar(int ch); /* write a single character */
|
||||
int getDebugChar(void); /* read and return a single char */
|
||||
|
||||
/* Check is any characters received are a ^C */
|
||||
int i386_gdb_uart_ctrl_c_check(void);
|
||||
|
||||
/* Raw interrupt handler. */
|
||||
void i386_gdb_uart_isr(void);
|
||||
|
||||
/* assign an exception handler */
|
||||
void exceptionHandler(int, void (*handler)(void));
|
||||
|
||||
/* User supplied remote debug option. */
|
||||
extern int remote_debug;
|
||||
|
||||
/* Current uart and port used by the gdb stub */
|
||||
static int uart_current;
|
||||
static int uart_vector;
|
||||
static console_tbl* port_current;
|
||||
|
||||
/*
|
||||
* Interrupt service routine for all, it does it check whether ^C is received
|
||||
* if yes it will flip TF bit before returning.
|
||||
*
|
||||
* Note: it should be installed as raw interrupt handler.
|
||||
*
|
||||
* Warning: I do not like the use of the global data, I am not
|
||||
* sure if this is SMP safe.
|
||||
*/
|
||||
int i386_gdb_uart_isr_regsav[4] RTEMS_UNUSED;
|
||||
__asm__ (".p2align 4");
|
||||
__asm__ (".text");
|
||||
__asm__ (".globl i386_gdb_uart_isr");
|
||||
__asm__ ("i386_gdb_uart_isr:");
|
||||
__asm__ (" pusha"); /* Push all */
|
||||
__asm__ (" call i386_gdb_uart_ctrl_c_check"); /* Look for ^C */
|
||||
__asm__ (" movl %eax, i386_gdb_uart_isr_regsav"); /* Save eax */
|
||||
__asm__ (" popa"); /* Pop all */
|
||||
__asm__ (" xchgl %eax, i386_gdb_uart_isr_regsav"); /* Exchange eax */
|
||||
__asm__ (" cmpl $0, %eax"); /* 1 == ^C */
|
||||
__asm__ (" je i386_gdb_uart_isr_1"); /* branch if 0 */
|
||||
__asm__ (" movl %ebx, i386_gdb_uart_isr_regsav + 4"); /* Save ebx */
|
||||
__asm__ (" movl %edx, i386_gdb_uart_isr_regsav + 8"); /* Save edx */
|
||||
__asm__ (" popl %ebx"); /* Pop eip */
|
||||
__asm__ (" popl %edx"); /* Pop cs */
|
||||
__asm__ (" popl %eax"); /* Pop flags */
|
||||
__asm__ (" orl $0x100, %eax"); /* Modify it */
|
||||
__asm__ (" pushl %eax"); /* Push it back */
|
||||
__asm__ (" pushl %edx"); /* Push cs */
|
||||
__asm__ (" pushl %ebx"); /* Push eip */
|
||||
__asm__ (" movl i386_gdb_uart_isr_regsav + 4, %ebx"); /* Restore ebx */
|
||||
__asm__ (" movl i386_gdb_uart_isr_regsav + 8, %edx"); /* Restore edx */
|
||||
__asm__ ("i386_gdb_uart_isr_1:");
|
||||
__asm__ (" movl i386_gdb_uart_isr_regsav, %eax"); /* Restore eax */
|
||||
__asm__ (" iret"); /* Done */
|
||||
|
||||
static int gdb_hello_index;
|
||||
static const char *const gdb_hello = "+";
|
||||
|
||||
static inline uint8_t BSP_i8259a_irq_in_service_reg(uint32_t ioport)
|
||||
{
|
||||
uint8_t isr;
|
||||
outport_byte(ioport, PIC_OCW3_SEL | PIC_OCW3_RR | PIC_OCW3_RIS);
|
||||
inport_byte(ioport, isr);
|
||||
outport_byte(ioport, PIC_OCW3_SEL | PIC_OCW3_RR);
|
||||
return isr;
|
||||
}
|
||||
|
||||
static inline void BSP_irq_ack_at_i8259a(const int irqLine)
|
||||
{
|
||||
uint8_t slave_isr = 0;
|
||||
if (irqLine >= 8) {
|
||||
outport_byte(PIC_SLAVE_COMMAND_IO_PORT, PIC_EOI);
|
||||
slave_isr = BSP_i8259a_irq_in_service_reg(PIC_SLAVE_COMMAND_IO_PORT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Only issue the EOI to the master if there are no more interrupts in
|
||||
* service for the slave. i8259a data sheet page 18, The Special Fully Nested
|
||||
* Mode, b.
|
||||
*/
|
||||
if (slave_isr == 0)
|
||||
outport_byte(PIC_MASTER_COMMAND_IO_PORT, PIC_EOI);
|
||||
}
|
||||
|
||||
int i386_gdb_uart_ctrl_c_check(void)
|
||||
{
|
||||
BSP_irq_ack_at_i8259a(uart_vector);
|
||||
if (port_current) {
|
||||
int c = 0;
|
||||
while (c >= 0) {
|
||||
c = port_current->pDeviceFns->deviceRead(uart_current);
|
||||
if (c == 3) {
|
||||
gdb_hello_index = 0;
|
||||
return 1;
|
||||
} else if (gdb_hello[gdb_hello_index] == (char) c) {
|
||||
++gdb_hello_index;
|
||||
if (gdb_hello[gdb_hello_index] == '\0') {
|
||||
gdb_hello_index = 0;
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
gdb_hello_index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
nop(const rtems_raw_irq_connect_data* notused)
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
isOn(const rtems_raw_irq_connect_data* notused)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int i386_stub_glue_uart(void)
|
||||
{
|
||||
if (port_current == NULL)
|
||||
return -1;
|
||||
return uart_current;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize glue code linking i386-stub with the rest of
|
||||
* the system
|
||||
*/
|
||||
void
|
||||
i386_stub_glue_init(int uart)
|
||||
{
|
||||
rtems_device_minor_number minor = (rtems_device_minor_number) uart;
|
||||
|
||||
port_current = console_find_console_entry(NULL, 0, &minor);
|
||||
|
||||
if (port_current == NULL) {
|
||||
printk("GDB: invalid minor number for UART\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uart_current = uart;
|
||||
|
||||
/* Intialise the UART, assuming polled drivers */
|
||||
port_current->pDeviceFns->deviceInitialize(uart);
|
||||
}
|
||||
|
||||
static void BSP_uart_on(const rtems_raw_irq_connect_data* used)
|
||||
{
|
||||
bsp_interrupt_vector_enable(used->idtIndex - BSP_IRQ_VECTOR_BASE);
|
||||
}
|
||||
|
||||
static void BSP_uart_off(const rtems_raw_irq_connect_data* used)
|
||||
{
|
||||
bsp_interrupt_vector_disable(used->idtIndex - BSP_IRQ_VECTOR_BASE);
|
||||
}
|
||||
|
||||
/*
|
||||
* In order to have a possibility to break into
|
||||
* running program, one has to call this function
|
||||
*/
|
||||
void i386_stub_glue_init_breakin(void)
|
||||
{
|
||||
rtems_raw_irq_connect_data uart_raw_irq_data;
|
||||
|
||||
if (port_current == NULL) {
|
||||
printk("GDB: no port initialised\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((port_current->ulIntVector == 0) || (port_current->ulIntVector > 16)) {
|
||||
printk("GDB: no UART interrupt support\n");
|
||||
}
|
||||
else {
|
||||
uart_vector = port_current->ulIntVector;
|
||||
uart_raw_irq_data.idtIndex = port_current->ulIntVector + BSP_IRQ_VECTOR_BASE;
|
||||
|
||||
if (!i386_get_current_idt_entry(&uart_raw_irq_data)) {
|
||||
printk("GBD: cannot get idt entry\n");
|
||||
rtems_fatal_error_occurred(1);
|
||||
}
|
||||
|
||||
if (!i386_delete_idt_entry(&uart_raw_irq_data)) {
|
||||
printk("GDB: cannot delete idt entry\n");
|
||||
rtems_fatal_error_occurred(1);
|
||||
}
|
||||
|
||||
uart_raw_irq_data.on = BSP_uart_on;
|
||||
uart_raw_irq_data.off = BSP_uart_off;
|
||||
|
||||
/* Install ISR */
|
||||
uart_raw_irq_data.idtIndex = port_current->ulIntVector + BSP_IRQ_VECTOR_BASE;
|
||||
uart_raw_irq_data.hdl = i386_gdb_uart_isr;
|
||||
|
||||
if (!i386_set_idt_entry (&uart_raw_irq_data)) {
|
||||
printk("GDB: raw exception handler connection failed\n");
|
||||
rtems_fatal_error_occurred(1);
|
||||
}
|
||||
|
||||
/* Enable interrupts, this is a bit of a hack because we
|
||||
* have to know the device but there is no other call. */
|
||||
(*port_current->setRegister)(port_current->ulCtrlPort1, 1, 0x01);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
putDebugChar(int ch)
|
||||
{
|
||||
if (port_current != NULL) {
|
||||
port_current->pDeviceFns->deviceWritePolled(uart_current, ch);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int getDebugChar(void)
|
||||
{
|
||||
int c = -1;
|
||||
|
||||
if (port_current != NULL) {
|
||||
while (c < 0)
|
||||
c = port_current->pDeviceFns->deviceRead(uart_current);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void exceptionHandler(int vector, void (*handler)(void))
|
||||
{
|
||||
rtems_raw_irq_connect_data excep_raw_irq_data;
|
||||
|
||||
excep_raw_irq_data.idtIndex = vector;
|
||||
|
||||
if(!i386_get_current_idt_entry(&excep_raw_irq_data))
|
||||
{
|
||||
printk("GDB: cannot get idt entry\n");
|
||||
rtems_fatal_error_occurred(1);
|
||||
}
|
||||
|
||||
if(!i386_delete_idt_entry(&excep_raw_irq_data))
|
||||
{
|
||||
printk("GDB: cannot delete idt entry\n");
|
||||
rtems_fatal_error_occurred(1);
|
||||
}
|
||||
|
||||
excep_raw_irq_data.on = nop;
|
||||
excep_raw_irq_data.off = nop;
|
||||
excep_raw_irq_data.isOn = isOn;
|
||||
excep_raw_irq_data.hdl = handler;
|
||||
|
||||
if (!i386_set_idt_entry (&excep_raw_irq_data)) {
|
||||
printk("GDB: raw exception handler connection failed\n");
|
||||
rtems_fatal_error_occurred(1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,413 +0,0 @@
|
||||
/***************************************************************************
|
||||
*
|
||||
* MODULE DESCRIPTION:
|
||||
* This module implements the RTEMS drivers for the PC serial ports
|
||||
* as /dev/ttyS1 for COM1 and /dev/ttyS2 as COM2. If one of the ports
|
||||
* is used as the console, this driver would fail to initialize.
|
||||
*
|
||||
* This code was based on the console driver. It is based on the
|
||||
* current termios framework. This is just a shell around the
|
||||
* termios support.
|
||||
*
|
||||
* by: Rosimildo da Silva:
|
||||
* rdasilva@connecttel.com
|
||||
* http://www.connecttel.com
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <bsp.h>
|
||||
#include <bsp/irq.h>
|
||||
#include <rtems/bspIo.h>
|
||||
#include <rtems/libio.h>
|
||||
#include <rtems/termiostypes.h>
|
||||
#include <termios.h>
|
||||
#include <uart.h>
|
||||
#include <libcpu/cpuModel.h>
|
||||
#include <bsp/tty_drv.h>
|
||||
|
||||
int BSP_poll_read(int);
|
||||
|
||||
/* Internal routines */
|
||||
static int tty1_conSetAttr( int minor, const struct termios *t);
|
||||
static int tty2_conSetAttr( int minor, const struct termios *t);
|
||||
|
||||
extern BSP_polling_getchar_function_type BSP_poll_char;
|
||||
extern int BSPConsolePort;
|
||||
extern void rtems_set_waiting_id_comx( int port, rtems_id id, rtems_event_set event );
|
||||
|
||||
/*
|
||||
* TTYS1 - device driver INITIALIZE entry point.
|
||||
*/
|
||||
rtems_device_driver
|
||||
tty1_initialize(rtems_device_major_number major,
|
||||
rtems_device_minor_number minor,
|
||||
void *arg)
|
||||
{
|
||||
rtems_status_code status;
|
||||
|
||||
/* Check if this port is not been used as console */
|
||||
if( BSPConsolePort == BSP_UART_COM1 )
|
||||
{
|
||||
status = -1;
|
||||
printk("TTYS1: port selected as console.\n");
|
||||
rtems_fatal_error_occurred( status );
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up TERMIOS
|
||||
*/
|
||||
rtems_termios_initialize();
|
||||
|
||||
/*
|
||||
* Do device-specific initialization
|
||||
*/
|
||||
/* 9600-8-N-1, without hardware flow control */
|
||||
BSP_uart_init( BSP_UART_COM1, 9600, CHR_8_BITS, 0, 0, 0 );
|
||||
status = rtems_interrupt_handler_install(
|
||||
BSP_UART_COM1_IRQ,
|
||||
"tty_drv",
|
||||
RTEMS_INTERRUPT_UNIQUE,
|
||||
BSP_uart_termios_isr_com1,
|
||||
NULL
|
||||
);
|
||||
assert(status == RTEMS_SUCCESSFUL);
|
||||
/*
|
||||
* Register the device
|
||||
*/
|
||||
status = rtems_io_register_name ("/dev/ttyS1", major, 0);
|
||||
if (status != RTEMS_SUCCESSFUL)
|
||||
{
|
||||
printk("Error registering ttyS1 device!\n");
|
||||
rtems_fatal_error_occurred (status);
|
||||
}
|
||||
printk("Device: /dev/ttyS1 initialized.\n");
|
||||
return RTEMS_SUCCESSFUL;
|
||||
} /* tty_initialize */
|
||||
|
||||
static int tty1_last_close(int major, int minor, void *arg)
|
||||
{
|
||||
rtems_status_code status;
|
||||
|
||||
status = rtems_interrupt_handler_remove(
|
||||
BSP_UART_COM1_IRQ,
|
||||
BSP_uart_termios_isr_com1,
|
||||
NULL
|
||||
);
|
||||
assert(status == RTEMS_SUCCESSFUL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* TTY1 - device driver OPEN entry point
|
||||
*/
|
||||
rtems_device_driver
|
||||
tty1_open(rtems_device_major_number major,
|
||||
rtems_device_minor_number minor,
|
||||
void *arg)
|
||||
{
|
||||
rtems_status_code status;
|
||||
#ifndef USE_TASK_DRIVEN
|
||||
static rtems_termios_callbacks cb =
|
||||
{
|
||||
NULL, /* firstOpen */
|
||||
tty1_last_close, /* lastClose */
|
||||
NULL, /* poll read */
|
||||
BSP_uart_termios_write_com1, /* write */
|
||||
tty1_conSetAttr, /* setAttributes */
|
||||
NULL, /* stopRemoteTx */
|
||||
NULL, /* startRemoteTx */
|
||||
TERMIOS_IRQ_DRIVEN /* outputUsesInterrupts */
|
||||
};
|
||||
#else
|
||||
static rtems_termios_callbacks cb =
|
||||
{
|
||||
NULL, /* firstOpen */
|
||||
NULL, /* lastClose */
|
||||
BSP_uart_termios_read_com1, /* poll read */
|
||||
BSP_uart_termios_write_com1, /* write */
|
||||
tty1_conSetAttr, /* setAttributes */
|
||||
NULL, /* stopRemoteTx */
|
||||
NULL, /* startRemoteTx */
|
||||
TERMIOS_TASK_DRIVEN /* outputUsesInterrupts */
|
||||
};
|
||||
#endif
|
||||
|
||||
status = rtems_termios_open( major, minor, arg, &cb );
|
||||
if(status != RTEMS_SUCCESSFUL)
|
||||
{
|
||||
printk("Error openning tty1 device\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pass data area info down to driver
|
||||
*/
|
||||
BSP_uart_termios_set( BSP_UART_COM1,
|
||||
((rtems_libio_open_close_args_t *)arg)->iop->data1 );
|
||||
/* Enable interrupts on channel */
|
||||
BSP_uart_intr_ctrl( BSP_UART_COM1, BSP_UART_INTR_CTRL_TERMIOS);
|
||||
return RTEMS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
/*
|
||||
* TTY - device driver CLOSE entry point
|
||||
*/
|
||||
rtems_device_driver
|
||||
tty_close(rtems_device_major_number major,
|
||||
rtems_device_minor_number minor,
|
||||
void *arg)
|
||||
{
|
||||
|
||||
return (rtems_termios_close (arg));
|
||||
|
||||
} /* tty_close */
|
||||
|
||||
/*
|
||||
* TTY device driver READ entry point.
|
||||
* Read characters from the tty device.
|
||||
*/
|
||||
rtems_device_driver
|
||||
tty_read(rtems_device_major_number major,
|
||||
rtems_device_minor_number minor,
|
||||
void *arg)
|
||||
{
|
||||
return rtems_termios_read (arg);
|
||||
} /* tty_read */
|
||||
|
||||
/*
|
||||
* TTY device driver WRITE entry point.
|
||||
* Write characters to the tty device.
|
||||
*/
|
||||
rtems_device_driver
|
||||
tty_write(rtems_device_major_number major,
|
||||
rtems_device_minor_number minor,
|
||||
void * arg)
|
||||
{
|
||||
return rtems_termios_write (arg);
|
||||
|
||||
} /* tty_write */
|
||||
|
||||
/*
|
||||
* Handle ioctl request. This is a generic internal
|
||||
* routine to handle both devices.
|
||||
*/
|
||||
static rtems_device_driver tty_control( int port, void *arg )
|
||||
{
|
||||
rtems_libio_ioctl_args_t *args = arg;
|
||||
switch( args->command )
|
||||
{
|
||||
default:
|
||||
return rtems_termios_ioctl (arg);
|
||||
break;
|
||||
}
|
||||
args->ioctl_return = 0;
|
||||
return RTEMS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle ioctl request for ttyS1.
|
||||
*/
|
||||
rtems_device_driver
|
||||
tty1_control(rtems_device_major_number major,
|
||||
rtems_device_minor_number minor,
|
||||
void * arg
|
||||
)
|
||||
{
|
||||
return tty_control( BSP_UART_COM1, arg );
|
||||
}
|
||||
|
||||
static int
|
||||
conSetAttr(int port, int minor, const struct termios *t)
|
||||
{
|
||||
unsigned long baud, databits, parity, stopbits;
|
||||
|
||||
baud = rtems_termios_baud_to_number(t->c_ospeed);
|
||||
if ( baud > 115200 )
|
||||
rtems_fatal_error_occurred (RTEMS_INTERNAL_ERROR);
|
||||
|
||||
if (t->c_cflag & PARENB) {
|
||||
/* Parity is enabled */
|
||||
if (t->c_cflag & PARODD) {
|
||||
/* Parity is odd */
|
||||
parity = PEN;
|
||||
}
|
||||
else {
|
||||
/* Parity is even */
|
||||
parity = PEN | EPS;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* No parity */
|
||||
parity = 0;
|
||||
}
|
||||
|
||||
switch (t->c_cflag & CSIZE) {
|
||||
case CS5: databits = CHR_5_BITS; break;
|
||||
case CS6: databits = CHR_6_BITS; break;
|
||||
case CS7: databits = CHR_7_BITS; break;
|
||||
default: /* just to avoid warnings -- all cases are covered */
|
||||
case CS8: databits = CHR_8_BITS; break;
|
||||
}
|
||||
|
||||
if (t->c_cflag & CSTOPB) {
|
||||
/* 2 stop bits */
|
||||
stopbits = STB;
|
||||
}
|
||||
else {
|
||||
/* 1 stop bit */
|
||||
stopbits = 0;
|
||||
}
|
||||
|
||||
printk("Setting attributes, port=%X, baud=%" PRId32 ", linemode = 0x%02" PRIx32 "\n",
|
||||
port, baud, databits | parity | stopbits );
|
||||
BSP_uart_set_attributes(port, baud, databits, parity, stopbits);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle ioctl request for ttyS2.
|
||||
*/
|
||||
static int
|
||||
tty1_conSetAttr( int minor, const struct termios *t)
|
||||
{
|
||||
return conSetAttr( BSP_UART_COM1, minor, t );
|
||||
}
|
||||
|
||||
/*
|
||||
* TTY2 device driver INITIALIZE entry point.
|
||||
*/
|
||||
rtems_device_driver
|
||||
tty2_initialize(rtems_device_major_number major,
|
||||
rtems_device_minor_number minor,
|
||||
void *arg)
|
||||
{
|
||||
rtems_status_code status;
|
||||
|
||||
/* Check if this port is not been used as console */
|
||||
if( BSPConsolePort == BSP_UART_COM2 )
|
||||
{
|
||||
status = -1;
|
||||
printk("TTY2: port selected as console.\n");
|
||||
rtems_fatal_error_occurred( status );
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up TERMIOS
|
||||
*/
|
||||
rtems_termios_initialize();
|
||||
|
||||
/*
|
||||
* Do device-specific initialization
|
||||
*/
|
||||
/* 9600-8-N-1, without hardware flow control */
|
||||
BSP_uart_init( BSP_UART_COM2, 9600, CHR_8_BITS, 0, 0, 0);
|
||||
status = rtems_interrupt_handler_install(
|
||||
BSP_UART_COM2_IRQ,
|
||||
"tty_drv",
|
||||
RTEMS_INTERRUPT_UNIQUE,
|
||||
BSP_uart_termios_isr_com2,
|
||||
NULL
|
||||
);
|
||||
assert(status == RTEMS_SUCCESSFUL);
|
||||
|
||||
/*
|
||||
* Register the device
|
||||
*/
|
||||
status = rtems_io_register_name ("/dev/ttyS2", major, 0);
|
||||
if (status != RTEMS_SUCCESSFUL)
|
||||
{
|
||||
printk("Error registering tty2 device!\n");
|
||||
rtems_fatal_error_occurred (status);
|
||||
}
|
||||
printk("Device: /dev/ttyS2 initialized.\n");
|
||||
return RTEMS_SUCCESSFUL;
|
||||
} /* tty_initialize */
|
||||
|
||||
static int tty2_last_close(int major, int minor, void *arg)
|
||||
{
|
||||
rtems_status_code status;
|
||||
|
||||
status = rtems_interrupt_handler_remove(
|
||||
BSP_UART_COM2_IRQ,
|
||||
BSP_uart_termios_isr_com2,
|
||||
NULL
|
||||
);
|
||||
assert(status == RTEMS_SUCCESSFUL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* TTY2 device driver OPEN entry point
|
||||
*/
|
||||
rtems_device_driver
|
||||
tty2_open(rtems_device_major_number major,
|
||||
rtems_device_minor_number minor,
|
||||
void *arg)
|
||||
{
|
||||
rtems_status_code status;
|
||||
#ifndef USE_TASK_DRIVEN
|
||||
static rtems_termios_callbacks cb =
|
||||
{
|
||||
NULL, /* firstOpen */
|
||||
tty2_last_close, /* lastClose */
|
||||
NULL, /* poll read */
|
||||
BSP_uart_termios_write_com2, /* write */
|
||||
tty2_conSetAttr, /* setAttributes */
|
||||
NULL, /* stopRemoteTx */
|
||||
NULL, /* startRemoteTx */
|
||||
TERMIOS_IRQ_DRIVEN /* outputUsesInterrupts */
|
||||
};
|
||||
#else
|
||||
static rtems_termios_callbacks cb =
|
||||
{
|
||||
NULL, /* firstOpen */
|
||||
NULL, /* lastClose */
|
||||
BSP_uart_termios_read_com2, /* poll read */
|
||||
BSP_uart_termios_write_com2, /* write */
|
||||
tty2_conSetAttr, /* setAttributes */
|
||||
NULL, /* stopRemoteTx */
|
||||
NULL, /* startRemoteTx */
|
||||
TERMIOS_TASK_DRIVEN /* outputUsesInterrupts */
|
||||
};
|
||||
#endif
|
||||
|
||||
status = rtems_termios_open (major, minor, arg, &cb);
|
||||
if(status != RTEMS_SUCCESSFUL)
|
||||
{
|
||||
printk("Error openning tty1 device\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pass data area info down to driver
|
||||
*/
|
||||
BSP_uart_termios_set( BSP_UART_COM2,
|
||||
((rtems_libio_open_close_args_t *)arg)->iop->data1 );
|
||||
/* Enable interrupts on channel */
|
||||
BSP_uart_intr_ctrl( BSP_UART_COM2, BSP_UART_INTR_CTRL_TERMIOS);
|
||||
return RTEMS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle ioctl request for TTY2
|
||||
*/
|
||||
rtems_device_driver
|
||||
tty2_control(rtems_device_major_number major,
|
||||
rtems_device_minor_number minor,
|
||||
void * arg
|
||||
)
|
||||
{
|
||||
return tty_control( BSP_UART_COM2, arg );
|
||||
}
|
||||
|
||||
static int
|
||||
tty2_conSetAttr( int minor, const struct termios *t)
|
||||
{
|
||||
return conSetAttr( BSP_UART_COM2, minor, t );
|
||||
}
|
||||
@@ -1,959 +0,0 @@
|
||||
/*
|
||||
* This software is Copyright (C) 1998 by T.sqware - all rights limited
|
||||
* It is provided in to the public domain "as is", can be freely modified
|
||||
* as far as this copyight notice is kept unchanged, but does not imply
|
||||
* an endorsement by T.sqware of the product in which it is included.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <bsp.h>
|
||||
#include <bsp/irq.h>
|
||||
#include <uart.h>
|
||||
#include <rtems/libio.h>
|
||||
#include <rtems/termiostypes.h>
|
||||
#include <termios.h>
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
* Basic 16552 driver
|
||||
*/
|
||||
|
||||
struct uart_data
|
||||
{
|
||||
int ioMode;
|
||||
int hwFlow;
|
||||
unsigned int ier;
|
||||
unsigned long baud;
|
||||
unsigned long databits;
|
||||
unsigned long parity;
|
||||
unsigned long stopbits;
|
||||
};
|
||||
|
||||
static struct uart_data uart_data[2];
|
||||
|
||||
/*
|
||||
* Macros to read/write register of uart, if configuration is
|
||||
* different just rewrite these macros
|
||||
*/
|
||||
|
||||
static inline unsigned char
|
||||
uread(int uart, unsigned int reg)
|
||||
{
|
||||
register unsigned char val;
|
||||
|
||||
if (uart == 0) {
|
||||
inport_byte(COM1_BASE_IO+reg, val);
|
||||
} else {
|
||||
inport_byte(COM2_BASE_IO+reg, val);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void
|
||||
uwrite(int uart, int reg, unsigned int val)
|
||||
{
|
||||
if (uart == 0) {
|
||||
outport_byte(COM1_BASE_IO+reg, val);
|
||||
} else {
|
||||
outport_byte(COM2_BASE_IO+reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
uartError(int uart)
|
||||
{
|
||||
unsigned char uartStatus, dummy;
|
||||
|
||||
uartStatus = uread(uart, LSR);
|
||||
(void) uartStatus; /* avoid set but not used warning */
|
||||
dummy = uread(uart, RBR);
|
||||
(void) dummy; /* avoid set but not used warning */
|
||||
|
||||
#ifdef UARTDEBUG
|
||||
if (uartStatus & OE)
|
||||
printk("********* Over run Error **********\n");
|
||||
if (uartStatus & PE)
|
||||
printk("********* Parity Error **********\n");
|
||||
if (uartStatus & FE)
|
||||
printk("********* Framing Error **********\n");
|
||||
if (uartStatus & BI)
|
||||
printk("********* Parity Error **********\n");
|
||||
if (uartStatus & ERFIFO)
|
||||
printk("********* Error receive Fifo **********\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Uart initialization, it is hardcoded to 8 bit, no parity,
|
||||
* one stop bit, FIFO, things to be changed
|
||||
* are baud rate and nad hw flow control,
|
||||
* and longest rx fifo setting
|
||||
*/
|
||||
void
|
||||
BSP_uart_init
|
||||
(
|
||||
int uart,
|
||||
unsigned long baud,
|
||||
unsigned long databits,
|
||||
unsigned long parity,
|
||||
unsigned long stopbits,
|
||||
int hwFlow
|
||||
)
|
||||
{
|
||||
/* Sanity check */
|
||||
assert(uart == BSP_UART_COM1 || uart == BSP_UART_COM2);
|
||||
|
||||
switch(baud)
|
||||
{
|
||||
case 50:
|
||||
case 75:
|
||||
case 110:
|
||||
case 134:
|
||||
case 300:
|
||||
case 600:
|
||||
case 1200:
|
||||
case 2400:
|
||||
case 9600:
|
||||
case 19200:
|
||||
case 38400:
|
||||
case 57600:
|
||||
case 115200:
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set DLAB bit to 1 */
|
||||
uwrite(uart, LCR, DLAB);
|
||||
|
||||
/* Set baud rate */
|
||||
uwrite(uart, DLL, (BSPBaseBaud/baud) & 0xff);
|
||||
uwrite(uart, DLM, ((BSPBaseBaud/baud) >> 8) & 0xff);
|
||||
|
||||
/* 8-bit, no parity , 1 stop */
|
||||
uwrite(uart, LCR, databits | parity | stopbits);
|
||||
|
||||
/* Set DTR, RTS and OUT2 high */
|
||||
uwrite(uart, MCR, DTR | RTS | OUT_2);
|
||||
|
||||
/* Enable FIFO */
|
||||
uwrite(uart, FCR, FIFO_EN | XMIT_RESET | RCV_RESET | RECEIVE_FIFO_TRIGGER12);
|
||||
|
||||
/* Disable Interrupts */
|
||||
uwrite(uart, IER, 0);
|
||||
|
||||
/* Read status to clear them */
|
||||
uread(uart, LSR);
|
||||
uread(uart, RBR);
|
||||
uread(uart, MSR);
|
||||
|
||||
/* Remember state */
|
||||
uart_data[uart].baud = baud;
|
||||
uart_data[uart].databits = databits;
|
||||
uart_data[uart].parity = parity;
|
||||
uart_data[uart].stopbits = stopbits;
|
||||
uart_data[uart].hwFlow = hwFlow;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set baud
|
||||
*/
|
||||
|
||||
void
|
||||
BSP_uart_set_baud(
|
||||
int uart,
|
||||
unsigned long baud
|
||||
)
|
||||
{
|
||||
/* Sanity check */
|
||||
assert(uart == BSP_UART_COM1 || uart == BSP_UART_COM2);
|
||||
|
||||
BSP_uart_set_attributes( uart, baud, uart_data[uart].databits,
|
||||
uart_data[uart].parity, uart_data[uart].stopbits );
|
||||
}
|
||||
|
||||
/*
|
||||
* Set all attributes
|
||||
*/
|
||||
|
||||
void
|
||||
BSP_uart_set_attributes
|
||||
(
|
||||
int uart,
|
||||
unsigned long baud,
|
||||
unsigned long databits,
|
||||
unsigned long parity,
|
||||
unsigned long stopbits
|
||||
)
|
||||
{
|
||||
unsigned char mcr, ier;
|
||||
|
||||
/* Sanity check */
|
||||
assert(uart == BSP_UART_COM1 || uart == BSP_UART_COM2);
|
||||
|
||||
/*
|
||||
* This function may be called whenever TERMIOS parameters
|
||||
* are changed, so we have to make sure that baud change is
|
||||
* indeed required
|
||||
*/
|
||||
|
||||
if( (baud == uart_data[uart].baud) &&
|
||||
(databits == uart_data[uart].databits) &&
|
||||
(parity == uart_data[uart].parity) &&
|
||||
(stopbits == uart_data[uart].stopbits) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mcr = uread(uart, MCR);
|
||||
ier = uread(uart, IER);
|
||||
|
||||
BSP_uart_init(uart, baud, databits, parity, stopbits, uart_data[uart].hwFlow);
|
||||
|
||||
uwrite(uart, MCR, mcr);
|
||||
uwrite(uart, IER, ier);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable/disable interrupts
|
||||
*/
|
||||
void
|
||||
BSP_uart_intr_ctrl(int uart, int cmd)
|
||||
{
|
||||
int iStatus = (int)INTERRUPT_DISABLE;
|
||||
|
||||
assert(uart == BSP_UART_COM1 || uart == BSP_UART_COM2);
|
||||
|
||||
switch(cmd)
|
||||
{
|
||||
case BSP_UART_INTR_CTRL_ENABLE:
|
||||
iStatus |= (RECEIVE_ENABLE | RECEIVER_LINE_ST_ENABLE | TRANSMIT_ENABLE);
|
||||
if ( uart_data[uart].hwFlow ) {
|
||||
iStatus |= MODEM_ENABLE;
|
||||
}
|
||||
break;
|
||||
case BSP_UART_INTR_CTRL_TERMIOS:
|
||||
iStatus |= (RECEIVE_ENABLE | RECEIVER_LINE_ST_ENABLE);
|
||||
if ( uart_data[uart].hwFlow ) {
|
||||
iStatus |= MODEM_ENABLE;
|
||||
}
|
||||
break;
|
||||
case BSP_UART_INTR_CTRL_GDB:
|
||||
iStatus |= RECEIVE_ENABLE;
|
||||
break;
|
||||
}
|
||||
|
||||
uart_data[uart].ier = iStatus;
|
||||
uwrite(uart, IER, iStatus);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
BSP_uart_throttle(int uart)
|
||||
{
|
||||
unsigned int mcr;
|
||||
|
||||
assert(uart == BSP_UART_COM1 || uart == BSP_UART_COM2);
|
||||
|
||||
if(!uart_data[uart].hwFlow)
|
||||
{
|
||||
/* Should not happen */
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
mcr = uread (uart, MCR);
|
||||
/* RTS down */
|
||||
mcr &= ~RTS;
|
||||
uwrite(uart, MCR, mcr);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
BSP_uart_unthrottle(int uart)
|
||||
{
|
||||
unsigned int mcr;
|
||||
|
||||
assert(uart == BSP_UART_COM1 || uart == BSP_UART_COM2);
|
||||
|
||||
if(!uart_data[uart].hwFlow)
|
||||
{
|
||||
/* Should not happen */
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
mcr = uread (uart, MCR);
|
||||
/* RTS up */
|
||||
mcr |= RTS;
|
||||
uwrite(uart, MCR, mcr);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Status function, -1 if error
|
||||
* detected, 0 if no received chars available,
|
||||
* 1 if received char available, 2 if break
|
||||
* is detected, it will eat break and error
|
||||
* chars. It ignores overruns - we cannot do
|
||||
* anything about - it execpt count statistics
|
||||
* and we are not counting it.
|
||||
*/
|
||||
int
|
||||
BSP_uart_polled_status(int uart)
|
||||
{
|
||||
unsigned char val;
|
||||
|
||||
assert(uart == BSP_UART_COM1 || uart == BSP_UART_COM2);
|
||||
|
||||
val = uread(uart, LSR);
|
||||
|
||||
if(val & BI)
|
||||
{
|
||||
/* BREAK found, eat character */
|
||||
uread(uart, RBR);
|
||||
return BSP_UART_STATUS_BREAK;
|
||||
}
|
||||
|
||||
if((val & (DR | OE | FE)) == 1)
|
||||
{
|
||||
/* No error, character present */
|
||||
return BSP_UART_STATUS_CHAR;
|
||||
}
|
||||
|
||||
if((val & (DR | OE | FE)) == 0)
|
||||
{
|
||||
/* Nothing */
|
||||
return BSP_UART_STATUS_NOCHAR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Framing or parity error
|
||||
* eat character
|
||||
*/
|
||||
uread(uart, RBR);
|
||||
|
||||
return BSP_UART_STATUS_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Polled mode write function
|
||||
*/
|
||||
void
|
||||
BSP_uart_polled_write(int uart, int val)
|
||||
{
|
||||
unsigned char val1;
|
||||
|
||||
/* Sanity check */
|
||||
assert(uart == BSP_UART_COM1 || uart == BSP_UART_COM2);
|
||||
|
||||
for(;;)
|
||||
{
|
||||
if((val1=uread(uart, LSR)) & THRE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(uart_data[uart].hwFlow)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if(uread(uart, MSR) & CTS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uwrite(uart, THR, val & 0xff);
|
||||
|
||||
/*
|
||||
* Wait for character to be transmitted.
|
||||
* This ensures that printk and printf play nicely together
|
||||
* when using the same serial port.
|
||||
* Yes, there's a performance hit here, but if we're doing
|
||||
* polled writes to a serial port we're probably not that
|
||||
* interested in efficiency anyway.....
|
||||
*/
|
||||
for(;;)
|
||||
{
|
||||
if((val1=uread(uart, LSR)) & THRE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
BSP_output_char_via_serial(char val)
|
||||
{
|
||||
BSP_uart_polled_write(BSPConsolePort, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Polled mode read function
|
||||
*/
|
||||
int
|
||||
BSP_uart_polled_read(int uart)
|
||||
{
|
||||
unsigned char val;
|
||||
|
||||
assert(uart == BSP_UART_COM1 || uart == BSP_UART_COM2);
|
||||
|
||||
for(;;)
|
||||
{
|
||||
if(uread(uart, LSR) & DR)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
val = uread(uart, RBR);
|
||||
|
||||
return (int)(val & 0xff);
|
||||
}
|
||||
|
||||
int
|
||||
BSP_poll_char_via_serial(void)
|
||||
{
|
||||
return BSP_uart_polled_read(BSPConsolePort);
|
||||
}
|
||||
|
||||
/* ================ Termios support =================*/
|
||||
|
||||
static volatile int termios_stopped_com1 = 0;
|
||||
static volatile int termios_tx_active_com1 = 0;
|
||||
static void* termios_ttyp_com1 = NULL;
|
||||
static char termios_tx_hold_com1 = 0;
|
||||
static volatile char termios_tx_hold_valid_com1 = 0;
|
||||
|
||||
static volatile int termios_stopped_com2 = 0;
|
||||
static volatile int termios_tx_active_com2 = 0;
|
||||
static void* termios_ttyp_com2 = NULL;
|
||||
static char termios_tx_hold_com2 = 0;
|
||||
static volatile char termios_tx_hold_valid_com2 = 0;
|
||||
|
||||
static void ( *driver_input_handler_com1 )( void *, char *, int ) = 0;
|
||||
static void ( *driver_input_handler_com2 )( void *, char *, int ) = 0;
|
||||
|
||||
/*
|
||||
* Set channel parameters
|
||||
*/
|
||||
void
|
||||
BSP_uart_termios_set(int uart, void *ttyp)
|
||||
{
|
||||
struct rtems_termios_tty *p = (struct rtems_termios_tty *)ttyp;
|
||||
unsigned char val;
|
||||
assert(uart == BSP_UART_COM1 || uart == BSP_UART_COM2);
|
||||
|
||||
if(uart == BSP_UART_COM1)
|
||||
{
|
||||
uart_data[uart].ioMode = p->device.outputUsesInterrupts;
|
||||
if(uart_data[uart].hwFlow)
|
||||
{
|
||||
val = uread(uart, MSR);
|
||||
|
||||
termios_stopped_com1 = (val & CTS) ? 0 : 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
termios_stopped_com1 = 0;
|
||||
}
|
||||
termios_tx_active_com1 = 0;
|
||||
termios_ttyp_com1 = ttyp;
|
||||
termios_tx_hold_com1 = 0;
|
||||
termios_tx_hold_valid_com1 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
uart_data[uart].ioMode = p->device.outputUsesInterrupts;
|
||||
if(uart_data[uart].hwFlow)
|
||||
{
|
||||
val = uread(uart, MSR);
|
||||
|
||||
termios_stopped_com2 = (val & CTS) ? 0 : 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
termios_stopped_com2 = 0;
|
||||
}
|
||||
termios_tx_active_com2 = 0;
|
||||
termios_ttyp_com2 = ttyp;
|
||||
termios_tx_hold_com2 = 0;
|
||||
termios_tx_hold_valid_com2 = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
BSP_uart_termios_read_com1(int uart)
|
||||
{
|
||||
int off = (int)0;
|
||||
char buf[40];
|
||||
|
||||
/* read bytes */
|
||||
while (( off < sizeof(buf) ) && ( uread(BSP_UART_COM1, LSR) & DR )) {
|
||||
buf[off++] = uread(BSP_UART_COM1, RBR);
|
||||
}
|
||||
|
||||
/* write out data */
|
||||
if ( off > 0 ) {
|
||||
rtems_termios_enqueue_raw_characters(termios_ttyp_com1, buf, off);
|
||||
}
|
||||
|
||||
/* enable receive interrupts */
|
||||
uart_data[BSP_UART_COM1].ier |= (RECEIVE_ENABLE | RECEIVER_LINE_ST_ENABLE);
|
||||
uwrite(BSP_UART_COM1, IER, uart_data[BSP_UART_COM1].ier);
|
||||
|
||||
return ( EOF );
|
||||
}
|
||||
|
||||
int
|
||||
BSP_uart_termios_read_com2(int uart)
|
||||
{
|
||||
int off = (int)0;
|
||||
char buf[40];
|
||||
|
||||
/* read current byte */
|
||||
while (( off < sizeof(buf) ) && ( uread(BSP_UART_COM2, LSR) & DR )) {
|
||||
buf[off++] = uread(BSP_UART_COM2, RBR);
|
||||
}
|
||||
|
||||
/* write out data */
|
||||
if ( off > 0 ) {
|
||||
rtems_termios_enqueue_raw_characters(termios_ttyp_com2, buf, off);
|
||||
}
|
||||
|
||||
/* enable receive interrupts */
|
||||
uart_data[BSP_UART_COM2].ier |= (RECEIVE_ENABLE | RECEIVER_LINE_ST_ENABLE);
|
||||
uwrite(BSP_UART_COM2, IER, uart_data[BSP_UART_COM2].ier);
|
||||
|
||||
return ( EOF );
|
||||
}
|
||||
|
||||
ssize_t
|
||||
BSP_uart_termios_write_com1(int minor, const char *buf, size_t len)
|
||||
{
|
||||
if(len <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(buf != NULL);
|
||||
|
||||
/* If there TX buffer is busy - something is royally screwed up */
|
||||
assert((uread(BSP_UART_COM1, LSR) & THRE) != 0);
|
||||
|
||||
if(termios_stopped_com1)
|
||||
{
|
||||
/* CTS low */
|
||||
termios_tx_hold_com1 = *buf;
|
||||
termios_tx_hold_valid_com1 = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write character */
|
||||
uwrite(BSP_UART_COM1, THR, *buf & 0xff);
|
||||
|
||||
/* Enable interrupts if necessary */
|
||||
if ( !termios_tx_active_com1 ) {
|
||||
termios_tx_active_com1 = 1;
|
||||
uart_data[BSP_UART_COM1].ier |= TRANSMIT_ENABLE;
|
||||
uwrite(BSP_UART_COM1, IER, uart_data[BSP_UART_COM1].ier);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
BSP_uart_termios_write_com2(int minor, const char *buf, size_t len)
|
||||
{
|
||||
if(len <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(buf != NULL);
|
||||
|
||||
/* If there TX buffer is busy - something is royally screwed up */
|
||||
assert((uread(BSP_UART_COM2, LSR) & THRE) != 0);
|
||||
|
||||
if(termios_stopped_com2)
|
||||
{
|
||||
/* CTS low */
|
||||
termios_tx_hold_com2 = *buf;
|
||||
termios_tx_hold_valid_com2 = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write character */
|
||||
uwrite(BSP_UART_COM2, THR, *buf & 0xff);
|
||||
|
||||
/* Enable interrupts if necessary */
|
||||
if ( !termios_tx_active_com2 ) {
|
||||
termios_tx_active_com2 = 1;
|
||||
uart_data[BSP_UART_COM2].ier |= TRANSMIT_ENABLE;
|
||||
uwrite(BSP_UART_COM2, IER, uart_data[BSP_UART_COM2].ier);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
BSP_uart_termios_isr_com1(void *ignored)
|
||||
{
|
||||
unsigned char buf[40];
|
||||
unsigned char val;
|
||||
int off, ret, vect;
|
||||
|
||||
off = 0;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
vect = uread(BSP_UART_COM1, IIR) & 0xf;
|
||||
|
||||
switch(vect)
|
||||
{
|
||||
case MODEM_STATUS :
|
||||
val = uread(BSP_UART_COM1, MSR);
|
||||
if(uart_data[BSP_UART_COM1].hwFlow)
|
||||
{
|
||||
if(val & CTS)
|
||||
{
|
||||
/* CTS high */
|
||||
termios_stopped_com1 = 0;
|
||||
if(termios_tx_hold_valid_com1)
|
||||
{
|
||||
termios_tx_hold_valid_com1 = 0;
|
||||
BSP_uart_termios_write_com1(0, &termios_tx_hold_com1,
|
||||
1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* CTS low */
|
||||
termios_stopped_com1 = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NO_MORE_INTR :
|
||||
/* No more interrupts */
|
||||
if(off != 0)
|
||||
{
|
||||
/* Update rx buffer */
|
||||
if( driver_input_handler_com1 )
|
||||
{
|
||||
driver_input_handler_com1( termios_ttyp_com1, (char *)buf, off );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Update rx buffer */
|
||||
rtems_termios_enqueue_raw_characters(termios_ttyp_com1, (char *)buf, off );
|
||||
}
|
||||
}
|
||||
return;
|
||||
case TRANSMITTER_HODING_REGISTER_EMPTY :
|
||||
/*
|
||||
* TX holding empty: we have to disable these interrupts
|
||||
* if there is nothing more to send.
|
||||
*/
|
||||
|
||||
/* If nothing else to send disable interrupts */
|
||||
ret = rtems_termios_dequeue_characters(termios_ttyp_com1, 1);
|
||||
if ( ret == 0 ) {
|
||||
termios_tx_active_com1 = 0;
|
||||
uart_data[BSP_UART_COM1].ier &= ~(TRANSMIT_ENABLE);
|
||||
uwrite(BSP_UART_COM1, IER, uart_data[BSP_UART_COM1].ier);
|
||||
}
|
||||
break;
|
||||
case RECEIVER_DATA_AVAIL :
|
||||
case CHARACTER_TIMEOUT_INDICATION:
|
||||
if ( uart_data[BSP_UART_COM1].ioMode == TERMIOS_TASK_DRIVEN ) {
|
||||
/* ensure interrupts are enabled */
|
||||
if ( uart_data[BSP_UART_COM1].ier & RECEIVE_ENABLE ) {
|
||||
/* disable interrupts and notify termios */
|
||||
uart_data[BSP_UART_COM1].ier &= ~(RECEIVE_ENABLE | RECEIVER_LINE_ST_ENABLE);
|
||||
uwrite(BSP_UART_COM1, IER, uart_data[BSP_UART_COM1].ier);
|
||||
rtems_termios_rxirq_occured(termios_ttyp_com1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* RX data ready */
|
||||
assert(off < sizeof(buf));
|
||||
buf[off++] = uread(BSP_UART_COM1, RBR);
|
||||
}
|
||||
break;
|
||||
case RECEIVER_ERROR:
|
||||
/* RX error: eat character */
|
||||
uartError(BSP_UART_COM1);
|
||||
break;
|
||||
default:
|
||||
/* Should not happen */
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BSP_uart_termios_isr_com2(void *ignored)
|
||||
{
|
||||
unsigned char buf[40];
|
||||
unsigned char val;
|
||||
int off, ret, vect;
|
||||
|
||||
off = 0;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
vect = uread(BSP_UART_COM2, IIR) & 0xf;
|
||||
|
||||
switch(vect)
|
||||
{
|
||||
case MODEM_STATUS :
|
||||
val = uread(BSP_UART_COM2, MSR);
|
||||
if(uart_data[BSP_UART_COM2].hwFlow)
|
||||
{
|
||||
if(val & CTS)
|
||||
{
|
||||
/* CTS high */
|
||||
termios_stopped_com2 = 0;
|
||||
if(termios_tx_hold_valid_com2)
|
||||
{
|
||||
termios_tx_hold_valid_com2 = 0;
|
||||
BSP_uart_termios_write_com2(0, &termios_tx_hold_com2,
|
||||
1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* CTS low */
|
||||
termios_stopped_com2 = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NO_MORE_INTR :
|
||||
/* No more interrupts */
|
||||
if(off != 0)
|
||||
{
|
||||
/* Update rx buffer */
|
||||
if( driver_input_handler_com2 )
|
||||
{
|
||||
driver_input_handler_com2( termios_ttyp_com2, (char *)buf, off );
|
||||
}
|
||||
else
|
||||
{
|
||||
rtems_termios_enqueue_raw_characters(termios_ttyp_com2, (char *)buf, off);
|
||||
}
|
||||
}
|
||||
return;
|
||||
case TRANSMITTER_HODING_REGISTER_EMPTY :
|
||||
/*
|
||||
* TX holding empty: we have to disable these interrupts
|
||||
* if there is nothing more to send.
|
||||
*/
|
||||
|
||||
/* If nothing else to send disable interrupts */
|
||||
ret = rtems_termios_dequeue_characters(termios_ttyp_com2, 1);
|
||||
if ( ret == 0 ) {
|
||||
termios_tx_active_com2 = 0;
|
||||
uart_data[BSP_UART_COM2].ier &= ~(TRANSMIT_ENABLE);
|
||||
uwrite(BSP_UART_COM2, IER, uart_data[BSP_UART_COM2].ier);
|
||||
}
|
||||
break;
|
||||
case RECEIVER_DATA_AVAIL :
|
||||
case CHARACTER_TIMEOUT_INDICATION:
|
||||
if ( uart_data[BSP_UART_COM2].ioMode == TERMIOS_TASK_DRIVEN ) {
|
||||
/* ensure interrupts are enabled */
|
||||
if ( uart_data[BSP_UART_COM2].ier & RECEIVE_ENABLE ) {
|
||||
/* disable interrupts and notify termios */
|
||||
uart_data[BSP_UART_COM2].ier &= ~(RECEIVE_ENABLE | RECEIVER_LINE_ST_ENABLE);
|
||||
uwrite(BSP_UART_COM2, IER, uart_data[BSP_UART_COM2].ier);
|
||||
rtems_termios_rxirq_occured(termios_ttyp_com2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* RX data ready */
|
||||
assert(off < sizeof(buf));
|
||||
buf[off++] = uread(BSP_UART_COM2, RBR);
|
||||
}
|
||||
break;
|
||||
case RECEIVER_ERROR:
|
||||
/* RX error: eat character */
|
||||
uartError(BSP_UART_COM2);
|
||||
break;
|
||||
default:
|
||||
/* Should not happen */
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ================= GDB support ===================*/
|
||||
int BSP_uart_dbgisr_com_regsav[4] RTEMS_UNUSED;
|
||||
|
||||
/*
|
||||
* Interrupt service routine for COM1 - all,
|
||||
* it does it check whether ^C is received
|
||||
* if yes it will flip TF bit before returning
|
||||
* Note: it should be installed as raw interrupt
|
||||
* handler
|
||||
*/
|
||||
|
||||
__asm__ (".p2align 4");
|
||||
__asm__ (".text");
|
||||
__asm__ (".globl BSP_uart_dbgisr_com1");
|
||||
__asm__ ("BSP_uart_dbgisr_com1:");
|
||||
__asm__ (" movl %eax, BSP_uart_dbgisr_com_regsav"); /* Save eax */
|
||||
__asm__ (" movl %ebx, BSP_uart_dbgisr_com_regsav + 4"); /* Save ebx */
|
||||
__asm__ (" movl %edx, BSP_uart_dbgisr_com_regsav + 8"); /* Save edx */
|
||||
|
||||
__asm__ (" movl $0, %ebx"); /* Clear flag */
|
||||
|
||||
/*
|
||||
* We know that only receive related interrupts
|
||||
* are available, eat chars
|
||||
*/
|
||||
__asm__ ("uart_dbgisr_com1_1:");
|
||||
__asm__ (" movw $0x3FD, %dx");
|
||||
__asm__ (" inb %dx, %al"); /* Read LSR */
|
||||
__asm__ (" andb $1, %al");
|
||||
__asm__ (" cmpb $0, %al");
|
||||
__asm__ (" je uart_dbgisr_com1_2");
|
||||
__asm__ (" movw $0x3F8, %dx");
|
||||
__asm__ (" inb %dx, %al"); /* Get input character */
|
||||
__asm__ (" cmpb $3, %al");
|
||||
__asm__ (" jne uart_dbgisr_com1_1");
|
||||
|
||||
/* ^C received, set flag */
|
||||
__asm__ (" movl $1, %ebx");
|
||||
__asm__ (" jmp uart_dbgisr_com1_1");
|
||||
|
||||
/* All chars read */
|
||||
__asm__ ("uart_dbgisr_com1_2:");
|
||||
|
||||
/* If flag is set we have to tweak TF */
|
||||
__asm__ (" cmpl $0, %ebx");
|
||||
__asm__ (" je uart_dbgisr_com1_3");
|
||||
|
||||
/* Flag is set */
|
||||
__asm__ (" movl BSP_uart_dbgisr_com_regsav+4, %ebx"); /* Restore ebx */
|
||||
__asm__ (" movl BSP_uart_dbgisr_com_regsav+8, %edx"); /* Restore edx */
|
||||
|
||||
/* Set TF bit */
|
||||
__asm__ (" popl %eax"); /* Pop eip */
|
||||
__asm__ (" movl %eax, BSP_uart_dbgisr_com_regsav + 4"); /* Save it */
|
||||
__asm__ (" popl %eax"); /* Pop cs */
|
||||
__asm__ (" movl %eax, BSP_uart_dbgisr_com_regsav + 8"); /* Save it */
|
||||
__asm__ (" popl %eax"); /* Pop flags */
|
||||
__asm__ (" orl $0x100, %eax"); /* Modify it */
|
||||
__asm__ (" pushl %eax"); /* Push it back */
|
||||
__asm__ (" movl BSP_uart_dbgisr_com_regsav+8, %eax"); /* Put back cs */
|
||||
__asm__ (" pushl %eax");
|
||||
__asm__ (" movl BSP_uart_dbgisr_com_regsav+4, %eax"); /* Put back eip */
|
||||
__asm__ (" pushl %eax");
|
||||
|
||||
/* Acknowledge IRQ */
|
||||
__asm__ (" movb $0x20, %al");
|
||||
__asm__ (" outb %al, $0x20");
|
||||
__asm__ (" movl BSP_uart_dbgisr_com_regsav, %eax"); /* Restore eax */
|
||||
__asm__ (" iret"); /* Done */
|
||||
|
||||
/* Flag is not set */
|
||||
__asm__ ("uart_dbgisr_com1_3:");
|
||||
__asm__ (" movl BSP_uart_dbgisr_com_regsav+4, %ebx"); /* Restore ebx */
|
||||
__asm__ (" movl BSP_uart_dbgisr_com_regsav+8, %edx"); /* Restore edx */
|
||||
|
||||
/* Acknowledge irq */
|
||||
__asm__ (" movb $0x20, %al");
|
||||
__asm__ (" outb %al, $0x20");
|
||||
__asm__ (" movl BSP_uart_dbgisr_com_regsav, %eax"); /* Restore eax */
|
||||
__asm__ (" iret"); /* Done */
|
||||
|
||||
/*
|
||||
* Interrupt service routine for COM2 - all,
|
||||
* it does it check whether ^C is received
|
||||
* if yes it will flip TF bit before returning
|
||||
* Note: it has to be installed as raw interrupt
|
||||
* handler
|
||||
*/
|
||||
__asm__ (".p2align 4");
|
||||
__asm__ (".text");
|
||||
__asm__ (".globl BSP_uart_dbgisr_com2");
|
||||
__asm__ ("BSP_uart_dbgisr_com2:");
|
||||
__asm__ (" movl %eax, BSP_uart_dbgisr_com_regsav"); /* Save eax */
|
||||
__asm__ (" movl %ebx, BSP_uart_dbgisr_com_regsav + 4"); /* Save ebx */
|
||||
__asm__ (" movl %edx, BSP_uart_dbgisr_com_regsav + 8"); /* Save edx */
|
||||
|
||||
__asm__ (" movl $0, %ebx"); /* Clear flag */
|
||||
|
||||
/*
|
||||
* We know that only receive related interrupts
|
||||
* are available, eat chars
|
||||
*/
|
||||
__asm__ ("uart_dbgisr_com2_1:");
|
||||
__asm__ (" movw $0x2FD, %dx");
|
||||
__asm__ (" inb %dx, %al"); /* Read LSR */
|
||||
__asm__ (" andb $1, %al");
|
||||
__asm__ (" cmpb $0, %al");
|
||||
__asm__ (" je uart_dbgisr_com2_2");
|
||||
__asm__ (" movw $0x2F8, %dx");
|
||||
__asm__ (" inb %dx, %al"); /* Get input character */
|
||||
__asm__ (" cmpb $3, %al");
|
||||
__asm__ (" jne uart_dbgisr_com2_1");
|
||||
|
||||
/* ^C received, set flag */
|
||||
__asm__ (" movl $1, %ebx");
|
||||
__asm__ (" jmp uart_dbgisr_com2_1");
|
||||
|
||||
/* All chars read */
|
||||
__asm__ ("uart_dbgisr_com2_2:");
|
||||
|
||||
/* If flag is set we have to tweak TF */
|
||||
__asm__ (" cmpl $0, %ebx");
|
||||
__asm__ (" je uart_dbgisr_com2_3");
|
||||
|
||||
/* Flag is set */
|
||||
__asm__ (" movl BSP_uart_dbgisr_com_regsav+4, %ebx"); /* Restore ebx */
|
||||
__asm__ (" movl BSP_uart_dbgisr_com_regsav+8, %edx"); /* Restore edx */
|
||||
|
||||
/* Set TF bit */
|
||||
__asm__ (" popl %eax"); /* Pop eip */
|
||||
__asm__ (" movl %eax, BSP_uart_dbgisr_com_regsav + 4"); /* Save it */
|
||||
__asm__ (" popl %eax"); /* Pop cs */
|
||||
__asm__ (" movl %eax, BSP_uart_dbgisr_com_regsav + 8"); /* Save it */
|
||||
__asm__ (" popl %eax"); /* Pop flags */
|
||||
__asm__ (" orl $0x100, %eax"); /* Modify it */
|
||||
__asm__ (" pushl %eax"); /* Push it back */
|
||||
__asm__ (" movl BSP_uart_dbgisr_com_regsav+8, %eax"); /* Put back cs */
|
||||
__asm__ (" pushl %eax");
|
||||
__asm__ (" movl BSP_uart_dbgisr_com_regsav+4, %eax"); /* Put back eip */
|
||||
__asm__ (" pushl %eax");
|
||||
|
||||
/* Acknowledge IRQ */
|
||||
__asm__ (" movb $0x20, %al");
|
||||
__asm__ (" outb %al, $0x20");
|
||||
__asm__ (" movl BSP_uart_dbgisr_com_regsav, %eax"); /* Restore eax */
|
||||
__asm__ (" iret"); /* Done */
|
||||
|
||||
/* Flag is not set */
|
||||
__asm__ ("uart_dbgisr_com2_3:");
|
||||
__asm__ (" movl BSP_uart_dbgisr_com_regsav+4, %ebx"); /* Restore ebx */
|
||||
__asm__ (" movl BSP_uart_dbgisr_com_regsav+8, %edx"); /* Restore edx */
|
||||
|
||||
/* Acknowledge irq */
|
||||
__asm__ (" movb $0x20, %al");
|
||||
__asm__ (" outb %al, $0x20");
|
||||
__asm__ (" movl BSP_uart_dbgisr_com_regsav, %eax"); /* Restore eax */
|
||||
__asm__ (" iret"); /* Done */
|
||||
@@ -1,454 +0,0 @@
|
||||
/**
|
||||
* @file realmode_int.c
|
||||
*
|
||||
* @ingroup i386_shared
|
||||
*
|
||||
* @brief Real mode interrupt call implementation
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014 - CTU in Prague
|
||||
* Jan Doležal ( dolezj21@fel.cvut.cz )
|
||||
*
|
||||
* The license and distribution terms for this file may be
|
||||
* found in the file LICENSE in this distribution or at
|
||||
* http://www.rtems.org/license/LICENSE.
|
||||
*/
|
||||
|
||||
#include <bsp/realmode_int.h>
|
||||
#include <string.h>
|
||||
#include <rtems/score/cpu.h>
|
||||
|
||||
/*
|
||||
* offsets to \a i386_realmode_interrupt_registers declared in realmode_int.h
|
||||
* used in inline assmbler for better readability
|
||||
*/
|
||||
#define IR_EAX_OFF "0x00"
|
||||
#define IR_EBX_OFF "0x04"
|
||||
#define IR_ECX_OFF "0x08"
|
||||
#define IR_EDX_OFF "0x0C"
|
||||
#define IR_ESI_OFF "0x10"
|
||||
#define IR_EDI_OFF "0x14"
|
||||
#define IR_DS_OFF "0x18"
|
||||
#define IR_ES_OFF "0x1A"
|
||||
#define IR_FS_OFF "0x1C"
|
||||
#define IR_GS_OFF "0x1E"
|
||||
|
||||
/*
|
||||
* offsets to \a rm_int_regs_bkp_param
|
||||
*/
|
||||
#define BKP_ESP_OFF "0x20"
|
||||
#define BKP_SS_OFF "0x24"
|
||||
#define BKP_DS_OFF "0x26"
|
||||
#define RM_ENTRY "0x28"
|
||||
#define PM_ENTRY "0x2C"
|
||||
|
||||
/**
|
||||
* @brief parameters, results, backup values accessible in real mode
|
||||
*
|
||||
* @note Struct members not necessarily used in C. This serves also as
|
||||
* layout of memory and it is used within inline assembler.
|
||||
*/
|
||||
typedef struct {
|
||||
i386_realmode_interrupt_registers inoutregs;
|
||||
/** spot for back up of protected mode stack pointer */
|
||||
uint32_t pm_esp_bkp;
|
||||
/** spot for back up of protected mode stack selector */
|
||||
uint16_t pm_ss_bkp;
|
||||
/** spot for back up of protected mode data selector */
|
||||
uint16_t ds_bkp;
|
||||
/** spot for setting up long indirect jump offset
|
||||
to real mode from 16bit protected mode */
|
||||
uint16_t rm_entry;
|
||||
/** spot for setting up long indirect jump segment
|
||||
to real mode from 16bit protected mode */
|
||||
uint16_t rm_code_segment;
|
||||
/** returning offset for long indirect jump back
|
||||
to 32bit protected mode */
|
||||
uint32_t pm_entry;
|
||||
/** returning selector for long indirect jump back
|
||||
to 32bit protected mode */
|
||||
uint16_t pm_code_selector;
|
||||
/* if this struct is to be modified update offset definitions as well */
|
||||
} RTEMS_PACKED rm_int_regs_bkp_param;
|
||||
|
||||
/* offsets to \a pm_bkp_and_param */
|
||||
#define BKP_IDTR_LIM "0x00"
|
||||
#define BKP_IDTR_BASE "0x02"
|
||||
#define BKP_ES_OFF "0x06"
|
||||
#define BKP_FS_OFF "0x08"
|
||||
#define BKP_GS_OFF "0x0A"
|
||||
#define RML_ENTRY "0x0C"
|
||||
#define RML_D_SEL "0x12"
|
||||
#define RM_SS "0x14"
|
||||
#define RM_SP "0x16"
|
||||
#define RM_DS "0x18"
|
||||
|
||||
/**
|
||||
* @brief backup values, pointers/parameters accessible in protected mode
|
||||
*
|
||||
* @note Struct members not necessarily used in C. This serves also as
|
||||
* layout of memory and it is used within inline assembler.
|
||||
*/
|
||||
typedef struct {
|
||||
/** spot for backup protected mode interrupt descriptor table register */
|
||||
uint16_t idtr_lim_bkp;
|
||||
/** @see idtr_lim_bkp */
|
||||
uint32_t idtr_base_bkp;
|
||||
/** spot to backup of ES register value in 32bit protected mode */
|
||||
uint16_t es_bkp;
|
||||
/** spot to backup of FS register value in 32bit protected mode */
|
||||
uint16_t fs_bkp;
|
||||
/** spot to backup of GS register value in 32bit protected mode */
|
||||
uint16_t gs_bkp;
|
||||
/** values for indirect jump to 16bit protected mode */
|
||||
uint32_t rml_entry;
|
||||
/** @see rml_entry */
|
||||
uint16_t rml_code_selector;
|
||||
/** data selector for 16bit protected mode */
|
||||
uint16_t rml_data_selector;
|
||||
/** values determinig location of real mode stack */
|
||||
uint16_t rm_stack_segment;
|
||||
/** @see rm_stack_segment */
|
||||
uint16_t rm_stack_pointer;
|
||||
/** data segment for real mode */
|
||||
uint16_t rm_data_segment;
|
||||
} RTEMS_PACKED pm_bkp_and_param;
|
||||
|
||||
/* addresses where we are going to put Interrupt buffer,
|
||||
* parameter/returned/preserved values, stack and copy code
|
||||
* for calling BIOS interrupt real mode interface
|
||||
* The value is chosen arbitrarily in the first 640kB
|
||||
* to be accessible for real mode. It should be out of range
|
||||
* used by RTEMS because its base address is above 1MB.
|
||||
* It has to be above first 4kB (or better 64kB) which could
|
||||
* be used by BIOS.
|
||||
*/
|
||||
#define REAL_MODE_SPOT 0x12000
|
||||
/* REAL_MODE_SPOT value is also top of real mode stack */
|
||||
|
||||
/* buffers positions and lengths */
|
||||
#define DEFAULT_BUFFER_SIZE 512
|
||||
static void *default_rm_buffer_spot = (void *)REAL_MODE_SPOT;
|
||||
static uint16_t default_rm_buffer_size = DEFAULT_BUFFER_SIZE;
|
||||
|
||||
/* real mode stack */
|
||||
#define STACK_SIZE 8192
|
||||
#define INT_STACK_TOP REAL_MODE_SPOT
|
||||
|
||||
/******************************
|
||||
* STACK * *
|
||||
****************************** REAL_MODE_SPOT
|
||||
* INT_BUF * 512 B *
|
||||
******************************
|
||||
* INT_REGs * 50 B *
|
||||
******************************
|
||||
* INT_FNC *~149 B *
|
||||
******************************/
|
||||
|
||||
#define __DP_TYPE uint8_t
|
||||
#define __DP_YES ((__DP_TYPE)1)
|
||||
#define __DP_NO ((__DP_TYPE)-1)
|
||||
#define __DP_FAIL ((__DP_TYPE)0)
|
||||
static __DP_TYPE descsPrepared = __DP_NO;
|
||||
|
||||
/* rml - real mode alike */
|
||||
#define rml_limit 0xFFFF
|
||||
static uint16_t rml_code_dsc_index = 0;
|
||||
static uint16_t rml_data_dsc_index = 0;
|
||||
|
||||
/**
|
||||
* @brief Prepares real-mode like descriptors to be used for switching
|
||||
* to real mode.
|
||||
*
|
||||
* Descriptors will be placed to the GDT.
|
||||
*
|
||||
* @param[in] base32 32-bit physical address to be used as base for 16-bit
|
||||
* protected mode descriptors
|
||||
* @retval __DP_YES descriptors are prepared
|
||||
* @retval __DP_FAIL descriptors allocation failed (GDT too small)
|
||||
*/
|
||||
static __DP_TYPE prepareRMDescriptors (void *base32) {
|
||||
static void *prevBase = (void *)-1;
|
||||
/* check if descriptors were prepared already */
|
||||
if (descsPrepared == __DP_YES && prevBase == base32)
|
||||
return descsPrepared;
|
||||
|
||||
if (descsPrepared == __DP_FAIL)
|
||||
return descsPrepared;
|
||||
|
||||
/* create 'real mode like' segment descriptors, for switching to real mode */
|
||||
rml_code_dsc_index = i386_next_empty_gdt_entry();
|
||||
if (rml_code_dsc_index == 0)
|
||||
{
|
||||
/* not enough space in GDT */
|
||||
descsPrepared = __DP_FAIL;
|
||||
return descsPrepared;
|
||||
}
|
||||
|
||||
segment_descriptors flags_desc;
|
||||
memset(&flags_desc, 0, sizeof(flags_desc));
|
||||
flags_desc.type = 0xE; /* bits 4 */
|
||||
flags_desc.descriptor_type = 0x1; /* bits 1 */
|
||||
flags_desc.privilege = 0x0; /* bits 2 */
|
||||
flags_desc.present = 0x1; /* bits 1 */
|
||||
flags_desc.available = 0x0; /* bits 1 */
|
||||
flags_desc.fixed_value_bits = 0x0; /* bits 1 */
|
||||
flags_desc.operation_size = 0x0; /* bits 1 */
|
||||
flags_desc.granularity = 0x0; /* bits 1 */
|
||||
i386_fill_segment_desc_base((unsigned)base32, &flags_desc);
|
||||
i386_fill_segment_desc_limit(rml_limit, &flags_desc);
|
||||
if (i386_raw_gdt_entry(rml_code_dsc_index, &flags_desc) == 0)
|
||||
{
|
||||
/* selector to GDT out of range */
|
||||
descsPrepared = __DP_FAIL;
|
||||
return descsPrepared;
|
||||
}
|
||||
|
||||
rml_data_dsc_index = i386_next_empty_gdt_entry();
|
||||
if (rml_data_dsc_index == 0)
|
||||
{
|
||||
/* not enough space in GDT for both descriptors */
|
||||
descsPrepared = __DP_FAIL;
|
||||
return descsPrepared;
|
||||
}
|
||||
|
||||
flags_desc.type = 0x2; /* bits 4 */
|
||||
if (i386_raw_gdt_entry(rml_data_dsc_index, &flags_desc) == 0)
|
||||
{
|
||||
/* selector to GDT out of range */
|
||||
descsPrepared = __DP_FAIL;
|
||||
return descsPrepared;
|
||||
}
|
||||
prevBase = base32;
|
||||
descsPrepared = __DP_YES;
|
||||
return descsPrepared;
|
||||
}
|
||||
|
||||
void *i386_get_default_rm_buffer(uint16_t *size) {
|
||||
*size = default_rm_buffer_size;
|
||||
return default_rm_buffer_spot;
|
||||
}
|
||||
|
||||
int i386_real_interrupt_call(uint8_t interrupt_number,
|
||||
i386_realmode_interrupt_registers *ir)
|
||||
{
|
||||
uint32_t pagingon;
|
||||
rm_int_regs_bkp_param *int_passed_regs_spot;
|
||||
/* place where the code switching to realmode and executing
|
||||
interrupt is coppied */
|
||||
void *rm_swtch_code_dst;
|
||||
void *rm_stack_top;
|
||||
|
||||
size_t cpLength;
|
||||
void *cpBeg;
|
||||
|
||||
/* values that can be passed from protected mode are stored in this struct
|
||||
and they are passed later to the inline assembler executing interrupt */
|
||||
volatile pm_bkp_and_param pm_bkp, *pm_bkp_addr;
|
||||
unsigned short unused_offset;
|
||||
|
||||
__asm__ volatile( "\t"
|
||||
"movl %%cr0, %%eax\n\t"
|
||||
"andl %1, %%eax\n"
|
||||
: "=a"(pagingon)
|
||||
: "i"(CR0_PAGING)
|
||||
);
|
||||
if (pagingon)
|
||||
return 0;
|
||||
|
||||
/* located under 1MB for real mode to be able to get/set values */
|
||||
int_passed_regs_spot = (rm_int_regs_bkp_param *)
|
||||
(default_rm_buffer_spot+default_rm_buffer_size);
|
||||
/* position for real mode code reallocation to the first 1MB of RAM */
|
||||
rm_swtch_code_dst = (void *)((uint32_t)int_passed_regs_spot +
|
||||
sizeof(*int_passed_regs_spot));
|
||||
rm_stack_top = (void *)INT_STACK_TOP;
|
||||
|
||||
if (prepareRMDescriptors(int_passed_regs_spot) != __DP_YES)
|
||||
return 0;
|
||||
|
||||
pm_bkp_addr = &pm_bkp;
|
||||
i386_Physical_to_real(
|
||||
rm_stack_top - STACK_SIZE,
|
||||
(unsigned short *)&pm_bkp.rm_stack_segment,
|
||||
(unsigned short *)&pm_bkp.rm_stack_pointer
|
||||
);
|
||||
pm_bkp.rm_stack_pointer += STACK_SIZE;
|
||||
pm_bkp.rml_code_selector = (rml_code_dsc_index<<3);
|
||||
pm_bkp.rml_entry = ((uint32_t)rm_swtch_code_dst -
|
||||
(uint32_t)int_passed_regs_spot);
|
||||
pm_bkp.rml_data_selector = (rml_data_dsc_index<<3);
|
||||
i386_Physical_to_real(
|
||||
int_passed_regs_spot,
|
||||
(unsigned short *)&pm_bkp.rm_data_segment,
|
||||
&unused_offset
|
||||
);
|
||||
|
||||
int_passed_regs_spot->inoutregs = *ir;
|
||||
/* offset from the beginning of coppied code */
|
||||
uint16_t rm_entry_offset;
|
||||
__asm__ volatile(
|
||||
"movw $(rment-cp_beg), %0\n\t"
|
||||
: "=r"(rm_entry_offset)
|
||||
);
|
||||
i386_Physical_to_real(
|
||||
rm_swtch_code_dst+rm_entry_offset,
|
||||
(unsigned short *)&int_passed_regs_spot->rm_code_segment,
|
||||
(unsigned short *)&int_passed_regs_spot->rm_entry
|
||||
);
|
||||
__asm__ volatile(
|
||||
"movl $(cp_end), %0\n\t"
|
||||
"movw %%cs, %1\n\t"
|
||||
: "=mr"(int_passed_regs_spot->pm_entry),
|
||||
"=mr"(int_passed_regs_spot->pm_code_selector)
|
||||
);
|
||||
/* copy code for switch to real mode and
|
||||
executing interrupt to first MB of RAM */
|
||||
__asm__ volatile( "\t"
|
||||
"mov $cp_end-cp_beg, %0\n\t"
|
||||
"mov $cp_beg, %1\n\t"
|
||||
: "=rm"(cpLength), "=rm"(cpBeg)
|
||||
);
|
||||
memcpy(rm_swtch_code_dst, cpBeg, cpLength);
|
||||
/* write interrupt number to be executed */
|
||||
uint16_t interrupt_number_off;
|
||||
uint8_t *interrupt_number_ptr;
|
||||
__asm__ volatile( "\t"
|
||||
"movw $intnum-cp_beg, %0\n\t"
|
||||
: "=rm"(interrupt_number_off)
|
||||
);
|
||||
interrupt_number_ptr = (uint8_t *)(rm_swtch_code_dst+interrupt_number_off);
|
||||
*interrupt_number_ptr = interrupt_number;
|
||||
/* execute code that jumps to coppied function, which switches to real mode,
|
||||
loads registers with values passed to interrupt and executes interrupt */
|
||||
__asm__ volatile( "\t"
|
||||
/* backup stack */
|
||||
"movl %[regs_spot], %%ebx\n\t"
|
||||
"movl %%esp, "BKP_ESP_OFF"(%%ebx)\n\t"
|
||||
"movw %%ss, "BKP_SS_OFF"(%%ebx)\n\t"
|
||||
/* backup data selector */
|
||||
"movw %%ds, "BKP_DS_OFF"(%%ebx)\n\t"
|
||||
/* backup other selectors */
|
||||
"movl %[pm_bkp], %%esi\n\t"
|
||||
"movw %%es, "BKP_ES_OFF"(%%esi)\n\t"
|
||||
"movw %%fs, "BKP_FS_OFF"(%%esi)\n\t"
|
||||
"movw %%gs, "BKP_GS_OFF"(%%esi)\n\t"
|
||||
/* hopefully loader does not damage interrupt table on the beginning of
|
||||
memory; that means length: 0x3FF, base: 0x0 */
|
||||
/* preserve idtr */
|
||||
"movl %%esi, %%eax\n\t"
|
||||
"addl $"BKP_IDTR_LIM", %%eax\n\t"
|
||||
"cli\n\t"
|
||||
"sidt (%%eax)\n\t"
|
||||
"movl $rmidt, %%eax\n\t"
|
||||
"lidt (%%eax)\n\t"
|
||||
/* prepare 'real mode like' data selector */
|
||||
"movw "RML_D_SEL"(%%esi), %%ax\n\t"
|
||||
/* prepare real mode data segment value */
|
||||
"xorl %%edx,%%edx\n\t"
|
||||
"movw "RM_DS"(%%esi), %%dx\n\t"
|
||||
/* prepare real mode stack values */
|
||||
"movw "RM_SS"(%%esi), %%cx\n\t"
|
||||
"movzwl "RM_SP"(%%esi), %%esp\n\t"
|
||||
/* jump to copied function and */
|
||||
/* load 'real mode like' code selector */
|
||||
"ljmp *"RML_ENTRY"(%%esi)\n"
|
||||
"rmidt:"/* limit and base for realmode interrupt descriptor table */
|
||||
".word 0x3FF\n\t"
|
||||
".long 0\n\t"
|
||||
/* load 'real mode like' data selectors */
|
||||
"cp_beg: .code16\n\t"
|
||||
"movw %%ax, %%ss\n\t"
|
||||
"movw %%ax, %%ds\n\t"
|
||||
"movw %%ax, %%es\n\t"
|
||||
"movw %%ax, %%fs\n\t"
|
||||
"movw %%ax, %%gs\n\t"
|
||||
/* disable protected mode */
|
||||
"movl %%cr0, %%eax\n\t"
|
||||
"and %[cr0_prot_dis], %%ax\n\t"
|
||||
"movl %%eax, %%cr0\n\t"
|
||||
/* base for data selector of 16-bit protected mode is
|
||||
at beginning of passed regs */
|
||||
/* flush prefetch queue by far jumping */
|
||||
"ljmp *"RM_ENTRY"\n\t"
|
||||
"rment: "
|
||||
/* establish rm stack - esp was already set in 32-bit protected mode*/
|
||||
"movw %%cx, %%ss\n\t"
|
||||
/* set data segment (value prepared in 32-bit prot mode) */
|
||||
"movw %%dx, %%ds\n\t"
|
||||
/* count real mode pointer so we don't need to overuse address
|
||||
prefix (by using 32bit addresses in 16bit context) */
|
||||
"shll $4,%%edx\n\t"
|
||||
"subl %%edx,%%ebx\n\t"
|
||||
/* prepare values to be used after interrupt call */
|
||||
"pushw %%bx\n\t"
|
||||
"pushw %%ds\n\t"
|
||||
/* fill registers with parameters */
|
||||
"movw " IR_DS_OFF"(%%bx), %%ax\n\t"
|
||||
"pushw %%ax\n\t"
|
||||
"movl "IR_EAX_OFF"(%%bx), %%eax\n\t"
|
||||
"movl "IR_ECX_OFF"(%%bx), %%ecx\n\t"
|
||||
"movl "IR_EDX_OFF"(%%bx), %%edx\n\t"
|
||||
"movl "IR_EDI_OFF"(%%bx), %%edi\n\t"
|
||||
"movl "IR_ESI_OFF"(%%bx), %%esi\n\t"
|
||||
"movw " IR_ES_OFF"(%%bx), %%es\n\t"
|
||||
"movw " IR_FS_OFF"(%%bx), %%fs\n\t"
|
||||
"movw " IR_GS_OFF"(%%bx), %%gs\n\t"
|
||||
/* prepare ebx register */
|
||||
"movl "IR_EBX_OFF"(%%bx), %%ebx\n\t"
|
||||
/* prepare ds */
|
||||
"popw %%ds\n\t"
|
||||
/* interrupt instruction */
|
||||
".byte 0xCD\n\t"
|
||||
"intnum: .byte 0x0\n\t"
|
||||
/* fill return structure */
|
||||
"pushw %%ds\n\t"
|
||||
"pushl %%ebx\n\t"
|
||||
"movw 0x6(%%esp), %%ds\n\t"
|
||||
"movw 0x8(%%esp),%%bx\n\t" /* regs_spot */
|
||||
"movl %%eax,"IR_EAX_OFF"(%%bx)\n\t"
|
||||
"popl %%eax\n\t"
|
||||
"movl %%eax,"IR_EBX_OFF"(%%bx)\n\t"
|
||||
"movl %%ecx,"IR_ECX_OFF"(%%bx)\n\t"
|
||||
"movl %%edx,"IR_EDX_OFF"(%%bx)\n\t"
|
||||
"movl %%esi,"IR_ESI_OFF"(%%bx)\n\t"
|
||||
"movl %%edi,"IR_EDI_OFF"(%%bx)\n\t"
|
||||
"popw %%ax\n\t"
|
||||
"movw %%ax, " IR_DS_OFF"(%%bx)\n\t"
|
||||
"movw %%es, " IR_ES_OFF"(%%bx)\n\t"
|
||||
"movw %%fs, " IR_FS_OFF"(%%bx)\n\t"
|
||||
"movw %%gs, " IR_GS_OFF"(%%bx)\n\t"
|
||||
/* prepare protected mode data segment */
|
||||
"movw "BKP_DS_OFF"(%%bx), %%ax\n\t"
|
||||
/* restore protected mode stack values */
|
||||
"movl "BKP_ESP_OFF"(%%bx),%%esp\n\t"
|
||||
"movw "BKP_SS_OFF"(%%bx), %%dx\n\t"
|
||||
/* return to protected mode */
|
||||
"movl %%cr0, %%ecx \n\t"
|
||||
"or %[cr0_prot_ena], %%cx\n\t"
|
||||
"movl %%ecx, %%cr0 \n\t"
|
||||
"ljmpl *"PM_ENTRY"(%%bx)\n\t"
|
||||
".code32\n"
|
||||
/* reload segmentation registers */
|
||||
"cp_end:"
|
||||
"movw %%ax, %%ds\n\t"
|
||||
/* restore stack segment in protected mode context */
|
||||
"movw %%dx, %%ss\n\t"
|
||||
"movl %[pm_bkp], %%esi\n\t"
|
||||
"movw "BKP_ES_OFF"(%%esi), %%es\n\t"
|
||||
"movw "BKP_FS_OFF"(%%esi), %%fs\n\t"
|
||||
"movw "BKP_GS_OFF"(%%esi), %%gs\n\t"
|
||||
/* restore IDTR */
|
||||
"addl $"BKP_IDTR_LIM", %%esi\n\t"
|
||||
"lidt (%%esi)\n\t"
|
||||
:
|
||||
: [regs_spot]"m"(int_passed_regs_spot),
|
||||
[pm_bkp]"m"(pm_bkp_addr),
|
||||
[cr0_prot_ena]"i"(CR0_PROTECTION_ENABLE),
|
||||
[cr0_prot_dis]"i"(~CR0_PROTECTION_ENABLE)
|
||||
: "memory", "ebx", "ecx", "edx", "esi", "edi"
|
||||
);
|
||||
*ir = int_passed_regs_spot->inoutregs;
|
||||
return 1;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
The following files were developed by Erich Boleyn <erich@uruk.org>.
|
||||
The original files were copied from http://www.uruk.org/mps/ and committed
|
||||
into cvs as is. These files will be modified for RTEMS smp support,
|
||||
but the original files are being commited in order to track any file
|
||||
changes that occur. We are attempting to keep these changes as minimal
|
||||
and conainted as possible.
|
||||
|
||||
+ smp-imps.c
|
||||
+ smp-imps.h
|
||||
+ ../irq/apic.h
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* COPYRIGHT (c) 2011.
|
||||
* On-Line Applications Research Corporation (OAR).
|
||||
*
|
||||
* The license and distribution terms for this file may be
|
||||
* found in the file LICENSE in this distribution or at
|
||||
* http://www.rtems.org/license/LICENSE.
|
||||
*/
|
||||
|
||||
#include <rtems/score/cpu.h>
|
||||
|
||||
#include <bsp/apic.h>
|
||||
#include <bsp/smp-imps.h>
|
||||
|
||||
static int lapic_dummy = 0;
|
||||
unsigned imps_lapic_addr = ((unsigned)(&lapic_dummy)) - LAPIC_ID;
|
||||
|
||||
uint32_t _CPU_SMP_Get_current_processor( void )
|
||||
{
|
||||
return APIC_ID(IMPS_LAPIC_READ(LAPIC_ID));
|
||||
}
|
||||
|
||||
@@ -1,828 +0,0 @@
|
||||
/*
|
||||
* Author: Erich Boleyn <erich@uruk.org>
|
||||
* http://www.uruk.org/~erich/
|
||||
*
|
||||
* Copyright (c) 1997-2011 Erich Boleyn. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Source file implementing Intel MultiProcessor Specification (MPS)
|
||||
* version 1.1 and 1.4 SMP hardware control for Intel Architecture CPUs,
|
||||
* with hooks for running correctly on a standard PC without the hardware.
|
||||
*
|
||||
* This file was created from information in the Intel MPS version 1.4
|
||||
* document, order number 242016-004, which can be ordered from the
|
||||
* Intel literature center.
|
||||
*
|
||||
* General limitations of this code:
|
||||
*
|
||||
* (1) : This code has never been tested on an MPS-compatible system with
|
||||
* 486 CPUs, but is expected to work.
|
||||
* (2) : Presumes "int", "long", and "unsigned" are 32 bits in size, and
|
||||
* that 32-bit pointers and memory addressing is used uniformly.
|
||||
*/
|
||||
|
||||
#define _SMP_IMPS_C
|
||||
|
||||
/*
|
||||
* Includes here
|
||||
*/
|
||||
#if 0
|
||||
#define IMPS_DEBUG
|
||||
#endif
|
||||
|
||||
#include <bsp/apic.h>
|
||||
#include <bsp/smp-imps.h>
|
||||
#include <bsp/irq.h>
|
||||
#include <rtems/score/smpimpl.h>
|
||||
|
||||
/*
|
||||
* XXXXX The following absolutely must be defined!!!
|
||||
*
|
||||
* The "KERNEL_PRINT" could be made a null macro with no danger, of
|
||||
* course, but pretty much nothing would work without the other
|
||||
* ones defined.
|
||||
*/
|
||||
|
||||
#if 0
|
||||
#define KERNEL_PRINT(x) /* some kind of print function */
|
||||
#define CMOS_WRITE_BYTE(x,y) /* write unsigned char "y" at CMOS loc "x" */
|
||||
#define CMOS_READ_BYTE(x) /* read unsigned char at CMOS loc "x" */
|
||||
#define PHYS_TO_VIRTUAL(x) /* convert physical address "x" to virtual */
|
||||
#define VIRTUAL_TO_PHYS(x) /* convert virtual address "x" to physical */
|
||||
#define UDELAY(x) /* delay roughly at least "x" microsecs */
|
||||
#define READ_MSR_LO(x) /* Read MSR low function */
|
||||
#else
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <rtems.h>
|
||||
#include <rtems/bspIo.h>
|
||||
#include <rtems/score/cpu.h>
|
||||
#include <assert.h>
|
||||
|
||||
extern void _pc386_delay(void);
|
||||
|
||||
/* #define KERNEL_PRINT(_format) printk(_format) */
|
||||
|
||||
static void CMOS_WRITE_BYTE(
|
||||
unsigned int offset,
|
||||
unsigned char value
|
||||
)
|
||||
{
|
||||
if ( offset < 128 ) {
|
||||
outport_byte( 0x70, offset );
|
||||
outport_byte( 0x71, value );
|
||||
} else {
|
||||
outport_byte( 0x72, offset );
|
||||
outport_byte( 0x73, value );
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned char CMOS_READ_BYTE(
|
||||
unsigned int offset
|
||||
)
|
||||
{
|
||||
unsigned char value;
|
||||
if ( offset < 128 ) {
|
||||
outport_byte( 0x70, offset );
|
||||
inport_byte( 0x71, value );
|
||||
} else {
|
||||
outport_byte( 0x72, offset );
|
||||
inport_byte( 0x73, value );
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
#define PHYS_TO_VIRTUAL(_x) _x
|
||||
#define VIRTUAL_TO_PHYS(_x) _x
|
||||
static void UDELAY(int x)
|
||||
{ int _i = x;
|
||||
while ( _i-- )
|
||||
_pc386_delay();
|
||||
}
|
||||
|
||||
#define READ_MSR_LO(_x) \
|
||||
(unsigned int)(read_msr(_x) & 0xffffffff)
|
||||
|
||||
static inline unsigned long long read_msr(unsigned int msr)
|
||||
{
|
||||
unsigned long long value;
|
||||
|
||||
asm volatile("rdmsr" : "=A" (value) : "c" (msr));
|
||||
return value;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Defines that are here so as not to be in the global header file.
|
||||
*/
|
||||
#define EBDA_SEG_ADDR 0x40E
|
||||
#define BIOS_RESET_VECTOR 0x467
|
||||
#define LAPIC_ADDR_DEFAULT 0xFEE00000uL
|
||||
#define IOAPIC_ADDR_DEFAULT 0xFEC00000uL
|
||||
#define CMOS_RESET_CODE 0xF
|
||||
#define CMOS_RESET_JUMP 0xa
|
||||
#define CMOS_BASE_MEMORY 0x15
|
||||
|
||||
/*
|
||||
* Static defines here for SMP use.
|
||||
*/
|
||||
|
||||
#define DEF_ENTRIES 23
|
||||
|
||||
static struct {
|
||||
imps_processor proc[2];
|
||||
imps_bus bus[2];
|
||||
imps_ioapic ioapic;
|
||||
imps_interrupt intin[16];
|
||||
imps_interrupt lintin[2];
|
||||
} defconfig = {
|
||||
{ { IMPS_BCT_PROCESSOR, 0, 0, 0, 0, 0},
|
||||
{ IMPS_BCT_PROCESSOR, 1, 0, 0, 0, 0} },
|
||||
{ { IMPS_BCT_BUS, 0, {'E', 'I', 'S', 'A', ' ', ' '}},
|
||||
{ 255, 1, {'P', 'C', 'I', ' ', ' ', ' '}} },
|
||||
{ IMPS_BCT_IOAPIC, 0, 0, IMPS_FLAG_ENABLED, IOAPIC_ADDR_DEFAULT },
|
||||
{ { IMPS_BCT_IO_INTERRUPT, IMPS_INT_EXTINT, 0, 0, 0, 0xFF, 0},
|
||||
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 1, 0xFF, 1},
|
||||
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 0, 0xFF, 2},
|
||||
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 3, 0xFF, 3},
|
||||
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 4, 0xFF, 4},
|
||||
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 5, 0xFF, 5},
|
||||
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 6, 0xFF, 6},
|
||||
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 7, 0xFF, 7},
|
||||
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 8, 0xFF, 8},
|
||||
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 9, 0xFF, 9},
|
||||
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 10, 0xFF, 10},
|
||||
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 11, 0xFF, 11},
|
||||
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 12, 0xFF, 12},
|
||||
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 13, 0xFF, 13},
|
||||
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 14, 0xFF, 14},
|
||||
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 15, 0xFF, 15} },
|
||||
{ { IMPS_BCT_LOCAL_INTERRUPT, IMPS_INT_EXTINT, 0, 0, 15, 0xFF, 0},
|
||||
{ IMPS_BCT_LOCAL_INTERRUPT, IMPS_INT_NMI, 0, 0, 15, 0xFF, 1} }
|
||||
};
|
||||
|
||||
/*
|
||||
* Exported globals here.
|
||||
*/
|
||||
|
||||
volatile int imps_release_cpus = 0;
|
||||
int imps_enabled = 0;
|
||||
int imps_num_cpus = 1;
|
||||
unsigned char imps_cpu_apic_map[IMPS_MAX_CPUS];
|
||||
unsigned char imps_apic_cpu_map[IMPS_MAX_CPUS];
|
||||
|
||||
/* now defined in getcpuid.c */
|
||||
extern unsigned imps_lapic_addr;
|
||||
|
||||
static void secondary_cpu_initialize(void);
|
||||
|
||||
/*
|
||||
* MPS checksum function
|
||||
*
|
||||
* Function finished.
|
||||
*/
|
||||
static int
|
||||
get_checksum(unsigned start, int length)
|
||||
{
|
||||
unsigned sum = 0;
|
||||
|
||||
while (length-- > 0) {
|
||||
sum += *((unsigned char *) (start++));
|
||||
}
|
||||
|
||||
return (sum&0xFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* APIC ICR write and status check function.
|
||||
*/
|
||||
static int
|
||||
send_ipi(unsigned int dst, unsigned int v)
|
||||
{
|
||||
int to, send_status;
|
||||
|
||||
IMPS_LAPIC_WRITE(LAPIC_ICR+0x10, (dst << 24));
|
||||
IMPS_LAPIC_WRITE(LAPIC_ICR, v);
|
||||
|
||||
/* Wait for send to finish */
|
||||
to = 0;
|
||||
do {
|
||||
UDELAY(100);
|
||||
send_status = IMPS_LAPIC_READ(LAPIC_ICR) & LAPIC_ICR_STATUS_PEND;
|
||||
} while (send_status && (to++ < 1000));
|
||||
|
||||
return (to < 1000);
|
||||
}
|
||||
|
||||
/*
|
||||
* Primary function for booting individual CPUs.
|
||||
*
|
||||
* This must be modified to perform whatever OS-specific initialization
|
||||
* that is required.
|
||||
*/
|
||||
static int
|
||||
boot_cpu(imps_processor *proc)
|
||||
{
|
||||
int apicid = proc->apic_id, success = 1;
|
||||
unsigned bootaddr;
|
||||
unsigned bios_reset_vector = PHYS_TO_VIRTUAL(BIOS_RESET_VECTOR);
|
||||
|
||||
/*
|
||||
* Copy boot code for secondary CPUs here. Find it in between
|
||||
* "patch_code_start" and "patch_code_end" symbols. The other CPUs
|
||||
* will start there in 16-bit real mode under the 1MB boundary.
|
||||
* "patch_code_start" should be placed at a 4K-aligned address
|
||||
* under the 1MB boundary.
|
||||
*/
|
||||
|
||||
uint32_t *reset;
|
||||
|
||||
bootaddr = (512-64)*1024;
|
||||
reset= (uint32_t *)bootaddr;
|
||||
|
||||
memcpy(
|
||||
(char *) bootaddr,
|
||||
_binary_appstart_bin_start,
|
||||
(size_t)_binary_appstart_bin_size
|
||||
);
|
||||
|
||||
reset[1] = (uint32_t)secondary_cpu_initialize;
|
||||
reset[2] = (uint32_t)_Per_CPU_Get_by_index(apicid)->interrupt_stack_high;
|
||||
|
||||
/*
|
||||
* Generic CPU startup sequence starts here.
|
||||
*/
|
||||
|
||||
/* set BIOS reset vector */
|
||||
CMOS_WRITE_BYTE(CMOS_RESET_CODE, CMOS_RESET_JUMP);
|
||||
*((volatile unsigned *) bios_reset_vector) = ((bootaddr & 0xFF000) << 12);
|
||||
|
||||
/* clear the APIC error register */
|
||||
IMPS_LAPIC_WRITE(LAPIC_ESR, 0);
|
||||
IMPS_LAPIC_READ(LAPIC_ESR);
|
||||
|
||||
/* assert INIT IPI */
|
||||
send_ipi(
|
||||
apicid,
|
||||
LAPIC_ICR_TM_LEVEL | LAPIC_ICR_LEVELASSERT | LAPIC_ICR_DM_INIT
|
||||
);
|
||||
UDELAY(10000);
|
||||
|
||||
/* de-assert INIT IPI */
|
||||
send_ipi(apicid, LAPIC_ICR_TM_LEVEL | LAPIC_ICR_DM_INIT);
|
||||
|
||||
UDELAY(10000);
|
||||
|
||||
/*
|
||||
* Send Startup IPIs if not an old pre-integrated APIC.
|
||||
*/
|
||||
|
||||
if (proc->apic_ver >= APIC_VER_NEW) {
|
||||
int i;
|
||||
for (i = 1; i <= 2; i++) {
|
||||
send_ipi(apicid, LAPIC_ICR_DM_SIPI | ((bootaddr >> 12) & 0xFF));
|
||||
UDELAY(1000);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic CPU startup sequence ends here, the rest is cleanup.
|
||||
*/
|
||||
|
||||
/* clear the APIC error register */
|
||||
IMPS_LAPIC_WRITE(LAPIC_ESR, 0);
|
||||
IMPS_LAPIC_READ(LAPIC_ESR);
|
||||
|
||||
/* clean up BIOS reset vector */
|
||||
CMOS_WRITE_BYTE(CMOS_RESET_CODE, 0);
|
||||
*((volatile unsigned *) bios_reset_vector) = 0;
|
||||
|
||||
printk("\n");
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/*
|
||||
* read bios stuff and fill tables
|
||||
*/
|
||||
static void
|
||||
add_processor(imps_processor *proc)
|
||||
{
|
||||
int apicid = proc->apic_id;
|
||||
|
||||
printk(" Processor [APIC id %d ver %d]: ", apicid, proc->apic_ver);
|
||||
if (!(proc->flags & IMPS_FLAG_ENABLED)) {
|
||||
printk("DISABLED\n");
|
||||
return;
|
||||
}
|
||||
if (proc->flags & (IMPS_CPUFLAG_BOOT)) {
|
||||
printk("#0 BootStrap Processor (BSP)\n");
|
||||
return;
|
||||
}
|
||||
if (boot_cpu(proc)) {
|
||||
|
||||
/* XXXXX add OS-specific setup for secondary CPUs here */
|
||||
|
||||
imps_cpu_apic_map[imps_num_cpus] = apicid;
|
||||
imps_apic_cpu_map[apicid] = imps_num_cpus;
|
||||
imps_num_cpus++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
add_bus(imps_bus *bus)
|
||||
{
|
||||
char str[8];
|
||||
|
||||
memcpy(str, bus->bus_type, 6);
|
||||
str[6] = 0;
|
||||
printk(" Bus id %d is %s\n", bus->id, str);
|
||||
|
||||
/* XXXXX add OS-specific code here */
|
||||
}
|
||||
|
||||
static void
|
||||
add_ioapic(imps_ioapic *ioapic)
|
||||
{
|
||||
printk(" I/O APIC id %d ver %d, address: 0x%x ",
|
||||
ioapic->id, ioapic->ver, ioapic->addr);
|
||||
if (!(ioapic->flags & IMPS_FLAG_ENABLED)) {
|
||||
printk("DISABLED\n");
|
||||
return;
|
||||
}
|
||||
printk("\n");
|
||||
|
||||
/* XXXXX add OS-specific code here */
|
||||
}
|
||||
|
||||
static void
|
||||
imps_read_config_table(unsigned start, int count)
|
||||
{
|
||||
while (count-- > 0) {
|
||||
switch (*((unsigned char *)start)) {
|
||||
case IMPS_BCT_PROCESSOR:
|
||||
if ( imps_num_cpus < rtems_configuration_get_maximum_processors() ) {
|
||||
if (_SMP_Should_start_processor((uint32_t) imps_num_cpus)) {
|
||||
add_processor((imps_processor *)start);
|
||||
}
|
||||
} else
|
||||
imps_num_cpus++;
|
||||
start += 12; /* 20 total */
|
||||
break;
|
||||
case IMPS_BCT_BUS:
|
||||
add_bus((imps_bus *)start);
|
||||
break;
|
||||
case IMPS_BCT_IOAPIC:
|
||||
add_ioapic((imps_ioapic *)start);
|
||||
break;
|
||||
#if 0 /* XXXXX uncomment this if "add_io_interrupt" is implemented */
|
||||
case IMPS_BCT_IO_INTERRUPT:
|
||||
add_io_interrupt((imps_interrupt *)start);
|
||||
break;
|
||||
#endif
|
||||
#if 0 /* XXXXX uncomment this if "add_local_interrupt" is implemented */
|
||||
case IMPS_BCT_LOCAL_INTERRUPT:
|
||||
add_local_interupt((imps_interrupt *)start);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
start += 8;
|
||||
}
|
||||
if ( imps_num_cpus > rtems_configuration_get_maximum_processors() ) {
|
||||
printk(
|
||||
"WARNING!! Found more CPUs (%d) than configured for (%d)!!\n",
|
||||
imps_num_cpus - 1,
|
||||
rtems_configuration_get_maximum_processors()
|
||||
);
|
||||
imps_num_cpus = rtems_configuration_get_maximum_processors();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
imps_bad_bios(imps_fps *fps_ptr)
|
||||
{
|
||||
int sum;
|
||||
imps_cth *local_cth_ptr
|
||||
= (imps_cth *) PHYS_TO_VIRTUAL(fps_ptr->cth_ptr);
|
||||
|
||||
if (fps_ptr->feature_info[0] > IMPS_FPS_DEFAULT_MAX) {
|
||||
printk(" Invalid MP System Configuration type %d\n",
|
||||
fps_ptr->feature_info[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (fps_ptr->cth_ptr) {
|
||||
sum = get_checksum((unsigned)local_cth_ptr,
|
||||
local_cth_ptr->base_length);
|
||||
if (local_cth_ptr->sig != IMPS_CTH_SIGNATURE || sum) {
|
||||
printk(
|
||||
" Bad MP Config Table sig 0x%x and/or checksum 0x%x\n",
|
||||
(unsigned)(fps_ptr->cth_ptr),
|
||||
sum
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
if (local_cth_ptr->spec_rev != fps_ptr->spec_rev) {
|
||||
printk(
|
||||
" Bad MP Config Table sub-revision # %d\n",
|
||||
local_cth_ptr->spec_rev
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
if (local_cth_ptr->extended_length) {
|
||||
sum = (get_checksum(((unsigned)local_cth_ptr)
|
||||
+ local_cth_ptr->base_length,
|
||||
local_cth_ptr->extended_length)
|
||||
+ local_cth_ptr->extended_checksum) & 0xFF;
|
||||
if (sum) {
|
||||
printk(" Bad Extended MP Config Table checksum 0x%x\n", sum);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else if (!fps_ptr->feature_info[0]) {
|
||||
printk(" Missing configuration information\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
imps_read_bios(imps_fps *fps_ptr)
|
||||
{
|
||||
int apicid;
|
||||
unsigned cth_start, cth_count;
|
||||
imps_cth *local_cth_ptr
|
||||
= (imps_cth *)PHYS_TO_VIRTUAL(fps_ptr->cth_ptr);
|
||||
char *str_ptr;
|
||||
|
||||
printk("Intel MultiProcessor Spec 1.%d BIOS support detected\n",
|
||||
fps_ptr->spec_rev);
|
||||
|
||||
/*
|
||||
* Do all checking of errors which would definitely
|
||||
* lead to failure of the SMP boot here.
|
||||
*/
|
||||
if (imps_bad_bios(fps_ptr)) {
|
||||
printk(" Disabling MPS support\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (fps_ptr->feature_info[1] & IMPS_FPS_IMCRP_BIT) {
|
||||
str_ptr = "IMCR and PIC";
|
||||
} else {
|
||||
str_ptr = "Virtual Wire";
|
||||
}
|
||||
if (fps_ptr->cth_ptr) {
|
||||
imps_lapic_addr = local_cth_ptr->lapic_addr;
|
||||
} else {
|
||||
imps_lapic_addr = LAPIC_ADDR_DEFAULT;
|
||||
}
|
||||
printk(" APIC config: \"%s mode\" Local APIC address: 0x%x\n",
|
||||
str_ptr, imps_lapic_addr);
|
||||
if (imps_lapic_addr != (READ_MSR_LO(0x1b) & 0xFFFFF000)) {
|
||||
printk("Inconsistent Local APIC address, Disabling SMP support\n");
|
||||
return;
|
||||
}
|
||||
imps_lapic_addr = PHYS_TO_VIRTUAL(imps_lapic_addr);
|
||||
|
||||
/*
|
||||
* Setup primary CPU.
|
||||
*/
|
||||
apicid = IMPS_LAPIC_READ(LAPIC_SPIV);
|
||||
IMPS_LAPIC_WRITE(LAPIC_SPIV, apicid|LAPIC_SPIV_ENABLE_APIC);
|
||||
apicid = APIC_ID(IMPS_LAPIC_READ(LAPIC_ID));
|
||||
imps_cpu_apic_map[0] = apicid;
|
||||
imps_apic_cpu_map[apicid] = 0;
|
||||
|
||||
if (fps_ptr->cth_ptr) {
|
||||
char str1[16], str2[16];
|
||||
memcpy(str1, local_cth_ptr->oem_id, 8);
|
||||
str1[8] = 0;
|
||||
memcpy(str2, local_cth_ptr->prod_id, 12);
|
||||
str2[12] = 0;
|
||||
printk(" OEM id: %s Product id: %s\n", str1, str2);
|
||||
cth_start = ((unsigned) local_cth_ptr) + sizeof(imps_cth);
|
||||
cth_count = local_cth_ptr->entry_count;
|
||||
} else {
|
||||
*((volatile unsigned *) IOAPIC_ADDR_DEFAULT) = IOAPIC_ID;
|
||||
defconfig.ioapic.id
|
||||
= APIC_ID(*((volatile unsigned *)
|
||||
(IOAPIC_ADDR_DEFAULT+IOAPIC_RW)));
|
||||
*((volatile unsigned *) IOAPIC_ADDR_DEFAULT) = IOAPIC_VER;
|
||||
defconfig.ioapic.ver
|
||||
= APIC_VERSION(*((volatile unsigned *)
|
||||
(IOAPIC_ADDR_DEFAULT+IOAPIC_RW)));
|
||||
defconfig.proc[apicid].flags
|
||||
= IMPS_FLAG_ENABLED|IMPS_CPUFLAG_BOOT;
|
||||
defconfig.proc[!apicid].flags = IMPS_FLAG_ENABLED;
|
||||
imps_num_cpus = 2;
|
||||
if (fps_ptr->feature_info[0] == 1
|
||||
|| fps_ptr->feature_info[0] == 5) {
|
||||
memcpy(defconfig.bus[0].bus_type, "ISA ", 6);
|
||||
}
|
||||
if (fps_ptr->feature_info[0] == 4
|
||||
|| fps_ptr->feature_info[0] == 7) {
|
||||
memcpy(defconfig.bus[0].bus_type, "MCA ", 6);
|
||||
}
|
||||
if (fps_ptr->feature_info[0] > 4) {
|
||||
defconfig.proc[0].apic_ver = 0x10;
|
||||
defconfig.proc[1].apic_ver = 0x10;
|
||||
defconfig.bus[1].type = IMPS_BCT_BUS;
|
||||
}
|
||||
if (fps_ptr->feature_info[0] == 2) {
|
||||
defconfig.intin[2].type = 255;
|
||||
defconfig.intin[13].type = 255;
|
||||
}
|
||||
if (fps_ptr->feature_info[0] == 7) {
|
||||
defconfig.intin[0].type = 255;
|
||||
}
|
||||
cth_start = (unsigned) &defconfig;
|
||||
cth_count = DEF_ENTRIES;
|
||||
}
|
||||
imps_read_config_table(cth_start, cth_count);
|
||||
|
||||
/* %%%%% ESB read extended entries here */
|
||||
|
||||
imps_enabled = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a region to check, this actually looks for the "MP Floating
|
||||
* Pointer Structure". The return value indicates if the correct
|
||||
* signature and checksum for a floating pointer structure of the
|
||||
* appropriate spec revision was found. If so, then do not search
|
||||
* further.
|
||||
*
|
||||
* NOTE: The memory scan will always be in the bottom 1 MB.
|
||||
*
|
||||
* This function presumes that "start" will always be aligned to a 16-bit
|
||||
* boundary.
|
||||
*
|
||||
* Function finished.
|
||||
*/
|
||||
static int
|
||||
imps_scan(unsigned start, unsigned length)
|
||||
{
|
||||
printk("Scanning from 0x%x for %d bytes\n", start, length);
|
||||
|
||||
while (length > 0) {
|
||||
imps_fps *fps_ptr = (imps_fps *) PHYS_TO_VIRTUAL(start);
|
||||
|
||||
if (fps_ptr->sig == IMPS_FPS_SIGNATURE
|
||||
&& fps_ptr->length == 1
|
||||
&& (fps_ptr->spec_rev == 1 || fps_ptr->spec_rev == 4)
|
||||
&& !get_checksum(start, 16)) {
|
||||
printk("Found MP Floating Structure Pointer at %x\n", start);
|
||||
imps_read_bios(fps_ptr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
length -= 16;
|
||||
start += 16;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !defined(__rtems__)
|
||||
/*
|
||||
* This is the primary function to "force" SMP support, with
|
||||
* the assumption that you have consecutively numbered APIC ids.
|
||||
*/
|
||||
int
|
||||
imps_force(int ncpus)
|
||||
{
|
||||
int apicid, i;
|
||||
imps_processor p;
|
||||
|
||||
printk("Intel MultiProcessor \"Force\" Support\n");
|
||||
|
||||
imps_lapic_addr = (READ_MSR_LO(0x1b) & 0xFFFFF000);
|
||||
imps_lapic_addr = PHYS_TO_VIRTUAL(imps_lapic_addr);
|
||||
|
||||
/*
|
||||
* Setup primary CPU.
|
||||
*/
|
||||
apicid = IMPS_LAPIC_READ(LAPIC_SPIV);
|
||||
IMPS_LAPIC_WRITE(LAPIC_SPIV, apicid|LAPIC_SPIV_ENABLE_APIC);
|
||||
apicid = APIC_ID(IMPS_LAPIC_READ(LAPIC_ID));
|
||||
imps_cpu_apic_map[0] = apicid;
|
||||
imps_apic_cpu_map[apicid] = 0;
|
||||
|
||||
p.type = 0;
|
||||
p.apic_ver = 0x10;
|
||||
p.signature = p.features = 0;
|
||||
|
||||
for (i = 0; i < ncpus; i++) {
|
||||
if (apicid == i) {
|
||||
p.flags = IMPS_FLAG_ENABLED | IMPS_CPUFLAG_BOOT;
|
||||
} else {
|
||||
p.flags = IMPS_FLAG_ENABLED;
|
||||
}
|
||||
p.apic_id = i;
|
||||
add_processor(&p);
|
||||
}
|
||||
|
||||
return imps_num_cpus;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is the primary function for probing for MPS compatible hardware
|
||||
* and BIOS information. Call this during the early stages of OS startup,
|
||||
* before memory can be messed up.
|
||||
*
|
||||
* The probe looks for the "MP Floating Pointer Structure" at locations
|
||||
* listed at the top of page 4-2 of the spec.
|
||||
*
|
||||
* Environment requirements from the OS to run:
|
||||
*
|
||||
* (1) : A non-linear virtual to physical memory mapping is probably OK,
|
||||
* as (I think) the structures all fall within page boundaries,
|
||||
* but a linear mapping is recommended. Currently assumes that
|
||||
* the mapping will remain identical over time (which should be
|
||||
* OK since it only accesses memory which shouldn't be munged
|
||||
* by the OS anyway).
|
||||
* (2) : The OS only consumes memory which the BIOS says is OK to use,
|
||||
* and not any of the BIOS standard areas (the areas 0x400 to
|
||||
* 0x600, the EBDA, 0xE0000 to 0xFFFFF, and unreported physical
|
||||
* RAM). Sometimes a small amount of physical RAM is not
|
||||
* reported by the BIOS, to be used to store MPS and other
|
||||
* information.
|
||||
* (3) : It must be possible to read the CMOS.
|
||||
* (4) : There must be between 512K and 640K of lower memory (this is a
|
||||
* sanity check).
|
||||
*
|
||||
* Function finished.
|
||||
*/
|
||||
static int
|
||||
imps_probe(void)
|
||||
{
|
||||
/*
|
||||
* Determine possible address of the EBDA
|
||||
*/
|
||||
unsigned ebda_addr = *((unsigned short *)
|
||||
PHYS_TO_VIRTUAL(EBDA_SEG_ADDR)) << 4;
|
||||
|
||||
/*
|
||||
* Determine amount of installed lower memory (not *available*
|
||||
* lower memory).
|
||||
*
|
||||
* NOTE: This should work reliably as long as we verify the
|
||||
* machine is at least a system that could possibly have
|
||||
* MPS compatibility to begin with.
|
||||
*/
|
||||
unsigned mem_lower = ((CMOS_READ_BYTE(CMOS_BASE_MEMORY+1) << 8)
|
||||
| CMOS_READ_BYTE(CMOS_BASE_MEMORY)) << 10;
|
||||
|
||||
#ifdef IMPS_DEBUG
|
||||
imps_enabled = 0;
|
||||
imps_num_cpus = 1;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Sanity check : if this isn't reasonable, it is almost impossibly
|
||||
* unlikely to be an MPS compatible machine, so return failure.
|
||||
*/
|
||||
if (mem_lower < 512*1024 || mem_lower > 640*1024) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ebda_addr > mem_lower - 1024
|
||||
|| ebda_addr + *((unsigned char *) PHYS_TO_VIRTUAL(ebda_addr))
|
||||
* 1024 > mem_lower) {
|
||||
ebda_addr = 0;
|
||||
}
|
||||
|
||||
if (((ebda_addr && imps_scan(ebda_addr, 1024))
|
||||
|| (!ebda_addr && imps_scan(mem_lower - 1024, 1024))
|
||||
|| imps_scan(0xF0000, 0x10000)) && imps_enabled) {
|
||||
return imps_num_cpus;
|
||||
}
|
||||
|
||||
/*
|
||||
* If no BIOS info on MPS hardware is found, then return failure.
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* RTEMS SMP BSP Support
|
||||
*/
|
||||
static void smp_apic_ack(void)
|
||||
{
|
||||
(void) IMPS_LAPIC_READ(LAPIC_SPIV); /* dummy read */
|
||||
IMPS_LAPIC_WRITE(LAPIC_EOI, 0 ); /* ACK the interrupt */
|
||||
}
|
||||
|
||||
/* FIXME: There should be a header file for this */
|
||||
void Clock_isr(void *arg);
|
||||
|
||||
static void bsp_inter_processor_interrupt(void *arg)
|
||||
{
|
||||
unsigned long message;
|
||||
|
||||
(void) arg;
|
||||
|
||||
smp_apic_ack();
|
||||
|
||||
message = _SMP_Inter_processor_interrupt_handler();
|
||||
|
||||
if ((message & SMP_MESSAGE_CLOCK_TICK) != 0) {
|
||||
Clock_isr(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipi_install_irq(void)
|
||||
{
|
||||
rtems_status_code status;
|
||||
|
||||
status = rtems_interrupt_handler_install(
|
||||
16,
|
||||
"smp-imps",
|
||||
RTEMS_INTERRUPT_UNIQUE,
|
||||
bsp_inter_processor_interrupt,
|
||||
NULL
|
||||
);
|
||||
assert(status == RTEMS_SUCCESSFUL);
|
||||
}
|
||||
|
||||
#ifdef __SSE__
|
||||
extern void enable_sse(void);
|
||||
#endif
|
||||
|
||||
/* pc386 specific initialization */
|
||||
static void secondary_cpu_initialize(void)
|
||||
{
|
||||
int apicid;
|
||||
|
||||
asm volatile( "lidt IDT_Descriptor" );
|
||||
|
||||
apicid = IMPS_LAPIC_READ(LAPIC_SPIV);
|
||||
IMPS_LAPIC_WRITE(LAPIC_SPIV, apicid|LAPIC_SPIV_ENABLE_APIC);
|
||||
|
||||
#ifdef __SSE__
|
||||
enable_sse();
|
||||
#endif
|
||||
|
||||
_SMP_Start_multitasking_on_secondary_processor();
|
||||
}
|
||||
|
||||
uint32_t _CPU_SMP_Initialize( void )
|
||||
{
|
||||
/* XXX need to deal with finding too many cores */
|
||||
|
||||
return (uint32_t) imps_probe();
|
||||
}
|
||||
|
||||
void _CPU_SMP_Prepare_start_multitasking( void )
|
||||
{
|
||||
/* Do nothing */
|
||||
}
|
||||
|
||||
bool _CPU_SMP_Start_processor( uint32_t cpu_index )
|
||||
{
|
||||
(void) cpu_index;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void _CPU_SMP_Finalize_initialization( uint32_t cpu_count )
|
||||
{
|
||||
if ( cpu_count > 1 )
|
||||
ipi_install_irq();
|
||||
}
|
||||
|
||||
void _CPU_SMP_Send_interrupt( uint32_t target_processor_index )
|
||||
{
|
||||
send_ipi( target_processor_index, 0x30 );
|
||||
}
|
||||
Reference in New Issue
Block a user