2005-04-16 15:20:36 -07:00
/*
* Driver for the 98626 / 98644 / internal serial interface on hp300 / hp400
* ( based on the National Semiconductor INS8250 / NS16550AF / WD16C552 UARTs )
*
* Ported from 2.2 and modified to use the normal 8250 driver
* by Kars de Jong < jongk @ linux - m68k . org > , May 2004.
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/string.h>
# include <linux/kernel.h>
# include <linux/serial.h>
# include <linux/serial_core.h>
# include <linux/delay.h>
# include <linux/dio.h>
# include <linux/console.h>
# include <asm/io.h>
2005-04-26 15:37:45 +01:00
# include "8250.h"
2005-04-16 15:20:36 -07:00
# if !defined(CONFIG_HPDCA) && !defined(CONFIG_HPAPCI)
# warning CONFIG_8250 defined but neither CONFIG_HPDCA nor CONFIG_HPAPCI defined, are you sure?
# endif
# ifdef CONFIG_HPAPCI
struct hp300_port
{
struct hp300_port * next ; /* next port */
int line ; /* line (tty) number */
} ;
static struct hp300_port * hp300_ports ;
# endif
# ifdef CONFIG_HPDCA
static int __devinit hpdca_init_one ( struct dio_dev * d ,
const struct dio_device_id * ent ) ;
static void __devexit hpdca_remove_one ( struct dio_dev * d ) ;
static struct dio_device_id hpdca_dio_tbl [ ] = {
{ DIO_ID_DCA0 } ,
{ DIO_ID_DCA0REM } ,
{ DIO_ID_DCA1 } ,
{ DIO_ID_DCA1REM } ,
{ 0 }
} ;
static struct dio_driver hpdca_driver = {
. name = " hpdca " ,
. id_table = hpdca_dio_tbl ,
. probe = hpdca_init_one ,
. remove = __devexit_p ( hpdca_remove_one ) ,
} ;
# endif
extern int hp300_uart_scode ;
/* Offset to UART registers from base of DCA */
# define UART_OFFSET 17
# define DCA_ID 0x01 /* ID (read), reset (write) */
# define DCA_IC 0x03 /* Interrupt control */
/* Interrupt control */
# define DCA_IC_IE 0x80 /* Master interrupt enable */
# define HPDCA_BAUD_BASE 153600
/* Base address of the Frodo part */
# define FRODO_BASE (0x41c000)
/*
* Where we find the 8250 - like APCI ports , and how far apart they are .
*/
# define FRODO_APCIBASE 0x0
# define FRODO_APCISPACE 0x20
# define FRODO_APCI_OFFSET(x) (FRODO_APCIBASE + ((x) * FRODO_APCISPACE))
# define HPAPCI_BAUD_BASE 500400
# ifdef CONFIG_SERIAL_8250_CONSOLE
/*
* Parse the bootinfo to find descriptions for headless console and
* debug serial ports and register them with the 8250 driver .
* This function should be called before serial_console_init ( ) is called
* to make sure the serial console will be available for use . IA - 64 kernel
* calls this function from setup_arch ( ) after the EFI and ACPI tables have
* been parsed .
*/
int __init hp300_setup_serial_console ( void )
{
int scode ;
struct uart_port port ;
memset ( & port , 0 , sizeof ( port ) ) ;
if ( hp300_uart_scode < 0 | | hp300_uart_scode > DIO_SCMAX )
return 0 ;
if ( DIO_SCINHOLE ( hp300_uart_scode ) )
return 0 ;
scode = hp300_uart_scode ;
/* Memory mapped I/O */
port . iotype = UPIO_MEM ;
port . flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF ;
port . type = PORT_UNKNOWN ;
/* Check for APCI console */
if ( scode = = 256 ) {
# ifdef CONFIG_HPAPCI
printk ( KERN_INFO " Serial console is HP APCI 1 \n " ) ;
port . uartclk = HPAPCI_BAUD_BASE * 16 ;
port . mapbase = ( FRODO_BASE + FRODO_APCI_OFFSET ( 1 ) ) ;
port . membase = ( char * ) ( port . mapbase + DIO_VIRADDRBASE ) ;
port . regshift = 2 ;
add_preferred_console ( " ttyS " , port . line , " 9600n8 " ) ;
# else
printk ( KERN_WARNING " Serial console is APCI but support is disabled (CONFIG_HPAPCI)! \n " ) ;
return 0 ;
# endif
}
else {
# ifdef CONFIG_HPDCA
unsigned long pa = dio_scodetophysaddr ( scode ) ;
if ( ! pa ) {
return 0 ;
}
printk ( KERN_INFO " Serial console is HP DCA at select code %d \n " , scode ) ;
port . uartclk = HPDCA_BAUD_BASE * 16 ;
port . mapbase = ( pa + UART_OFFSET ) ;
port . membase = ( char * ) ( port . mapbase + DIO_VIRADDRBASE ) ;
port . regshift = 1 ;
port . irq = DIO_IPL ( pa + DIO_VIRADDRBASE ) ;
/* Enable board-interrupts */
out_8 ( pa + DIO_VIRADDRBASE + DCA_IC , DCA_IC_IE ) ;
if ( DIO_ID ( pa + DIO_VIRADDRBASE ) & 0x80 ) {
add_preferred_console ( " ttyS " , port . line , " 9600n8 " ) ;
}
# else
printk ( KERN_WARNING " Serial console is DCA but support is disabled (CONFIG_HPDCA)! \n " ) ;
return 0 ;
# endif
}
if ( early_serial_setup ( & port ) < 0 ) {
printk ( KERN_WARNING " hp300_setup_serial_console(): early_serial_setup() failed. \n " ) ;
}
return 0 ;
}
# endif /* CONFIG_SERIAL_8250_CONSOLE */
# ifdef CONFIG_HPDCA
static int __devinit hpdca_init_one ( struct dio_dev * d ,
const struct dio_device_id * ent )
{
2005-04-26 15:37:45 +01:00
struct uart_port port ;
2005-04-16 15:20:36 -07:00
int line ;
# ifdef CONFIG_SERIAL_8250_CONSOLE
if ( hp300_uart_scode = = d - > scode ) {
/* Already got it. */
return 0 ;
}
# endif
2005-04-26 15:37:45 +01:00
memset ( & port , 0 , sizeof ( struct uart_port ) ) ;
2005-04-16 15:20:36 -07:00
/* Memory mapped I/O */
2005-04-26 15:37:45 +01:00
port . iotype = UPIO_MEM ;
port . flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF ;
port . irq = d - > ipl ;
port . uartclk = HPDCA_BAUD_BASE * 16 ;
port . mapbase = ( d - > resource . start + UART_OFFSET ) ;
port . membase = ( char * ) ( port . mapbase + DIO_VIRADDRBASE ) ;
port . regshift = 1 ;
port . dev = & d - > dev ;
line = serial8250_register_port ( & port ) ;
2005-04-16 15:20:36 -07:00
if ( line < 0 ) {
printk ( KERN_NOTICE " 8250_hp300: register_serial() DCA scode %d "
2005-04-26 15:37:45 +01:00
" irq %d failed \n " , d - > scode , port . irq ) ;
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
}
/* Enable board-interrupts */
out_8 ( d - > resource . start + DIO_VIRADDRBASE + DCA_IC , DCA_IC_IE ) ;
dio_set_drvdata ( d , ( void * ) line ) ;
/* Reset the DCA */
out_8 ( d - > resource . start + DIO_VIRADDRBASE + DCA_ID , 0xff ) ;
udelay ( 100 ) ;
return 0 ;
}
# endif
static int __init hp300_8250_init ( void )
{
static int called = 0 ;
int num_ports ;
# ifdef CONFIG_HPAPCI
int line ;
unsigned long base ;
2005-04-26 15:37:45 +01:00
struct uart_port uport ;
2005-04-16 15:20:36 -07:00
struct hp300_port * port ;
int i ;
# endif
if ( called )
return - ENODEV ;
called = 1 ;
if ( ! MACH_IS_HP300 )
return - ENODEV ;
num_ports = 0 ;
# ifdef CONFIG_HPDCA
if ( dio_module_init ( & hpdca_driver ) = = 0 )
num_ports + + ;
# endif
# ifdef CONFIG_HPAPCI
if ( hp300_model < HP_400 ) {
if ( ! num_ports )
return - ENODEV ;
return 0 ;
}
/* These models have the Frodo chip.
* Port 0 is reserved for the Apollo Domain keyboard .
* Port 1 is either the console or the DCA .
*/
for ( i = 1 ; i < 4 ; i + + ) {
/* Port 1 is the console on a 425e, on other machines it's mapped to
* DCA .
*/
# ifdef CONFIG_SERIAL_8250_CONSOLE
if ( i = = 1 ) {
continue ;
}
# endif
/* Create new serial device */
port = kmalloc ( sizeof ( struct hp300_port ) , GFP_KERNEL ) ;
if ( ! port )
return - ENOMEM ;
2005-04-26 15:37:45 +01:00
memset ( & uport , 0 , sizeof ( struct uart_port ) ) ;
2005-04-16 15:20:36 -07:00
base = ( FRODO_BASE + FRODO_APCI_OFFSET ( i ) ) ;
/* Memory mapped I/O */
2005-04-26 15:37:45 +01:00
uport . iotype = UPIO_MEM ;
uport . flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF ;
2005-04-16 15:20:36 -07:00
/* XXX - no interrupt support yet */
2005-04-26 15:37:45 +01:00
uport . irq = 0 ;
uport . uartclk = HPAPCI_BAUD_BASE * 16 ;
uport . mapbase = base ;
uport . membase = ( char * ) ( base + DIO_VIRADDRBASE ) ;
uport . regshift = 2 ;
2005-04-16 15:20:36 -07:00
2005-04-26 15:37:45 +01:00
line = serial8250_register_port ( & uport ) ;
2005-04-16 15:20:36 -07:00
if ( line < 0 ) {
printk ( KERN_NOTICE " 8250_hp300: register_serial() APCI %d "
2005-04-26 15:37:45 +01:00
" irq %d failed \n " , i , uport . irq ) ;
2005-04-16 15:20:36 -07:00
kfree ( port ) ;
continue ;
}
port - > line = line ;
port - > next = hp300_ports ;
hp300_ports = port ;
num_ports + + ;
}
# endif
/* Any boards found? */
if ( ! num_ports )
return - ENODEV ;
return 0 ;
}
# ifdef CONFIG_HPDCA
static void __devexit hpdca_remove_one ( struct dio_dev * d )
{
int line ;
line = ( int ) dio_get_drvdata ( d ) ;
if ( d - > resource . start ) {
/* Disable board-interrupts */
out_8 ( d - > resource . start + DIO_VIRADDRBASE + DCA_IC , 0 ) ;
}
2005-04-26 15:37:45 +01:00
serial8250_unregister_port ( line ) ;
2005-04-16 15:20:36 -07:00
}
# endif
static void __exit hp300_8250_exit ( void )
{
# ifdef CONFIG_HPAPCI
struct hp300_port * port , * to_free ;
for ( port = hp300_ports ; port ; ) {
2005-04-26 15:37:45 +01:00
serial8250_unregister_port ( port - > line ) ;
2005-04-16 15:20:36 -07:00
to_free = port ;
port = port - > next ;
kfree ( to_free ) ;
}
hp300_ports = NULL ;
# endif
# ifdef CONFIG_HPDCA
dio_unregister_driver ( & hpdca_driver ) ;
# endif
}
module_init ( hp300_8250_init ) ;
module_exit ( hp300_8250_exit ) ;
MODULE_DESCRIPTION ( " HP DCA/APCI serial driver " ) ;
MODULE_AUTHOR ( " Kars de Jong <jongk@linux-m68k.org> " ) ;
MODULE_LICENSE ( " GPL " ) ;