2007-08-26 02:08:11 +04:00
/*
* AVR power - management chip interface for the Buffalo Linkstation /
* Kurobox Platform .
*
* Author : 2006 ( c ) G . Liakhovetski
* g . liakhovetski @ gmx . de
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed " as is " without any warranty of
* any kind , whether express or implied .
*/
2006-12-02 00:53:48 +03:00
# include <linux/workqueue.h>
# include <linux/string.h>
# include <linux/delay.h>
# include <linux/serial_reg.h>
# include <linux/serial_8250.h>
# include <asm/io.h>
# include <asm/prom.h>
# include <asm/termbits.h>
2007-08-20 17:50:28 +04:00
# include "mpc10x.h"
2006-12-02 00:53:48 +03:00
static void __iomem * avr_addr ;
static unsigned long avr_clock ;
static struct work_struct wd_work ;
2006-12-05 22:36:26 +03:00
static void wd_stop ( struct work_struct * unused )
2006-12-02 00:53:48 +03:00
{
const char string [ ] = " AAAAFFFFJJJJ>>>>VVVV>>>>ZZZZVVVVKKKK " ;
int i = 0 , rescue = 8 ;
int len = strlen ( string ) ;
while ( rescue - - ) {
int j ;
char lsr = in_8 ( avr_addr + UART_LSR ) ;
if ( lsr & ( UART_LSR_THRE | UART_LSR_TEMT ) ) {
for ( j = 0 ; j < 16 & & i < len ; j + + , i + + )
out_8 ( avr_addr + UART_TX , string [ i ] ) ;
if ( i = = len ) {
/* Read "OK" back: 4ms for the last "KKKK"
plus a couple bytes back */
msleep ( 7 ) ;
printk ( " linkstation: disarming the AVR watchdog: " ) ;
while ( in_8 ( avr_addr + UART_LSR ) & UART_LSR_DR )
printk ( " %c " , in_8 ( avr_addr + UART_RX ) ) ;
break ;
}
}
msleep ( 17 ) ;
}
printk ( " \n " ) ;
}
# define AVR_QUOT(clock) ((clock) + 8 * 9600) / (16 * 9600)
void avr_uart_configure ( void )
{
unsigned char cval = UART_LCR_WLEN8 ;
unsigned int quot = AVR_QUOT ( avr_clock ) ;
if ( ! avr_addr | | ! avr_clock )
return ;
out_8 ( avr_addr + UART_LCR , cval ) ; /* initialise UART */
out_8 ( avr_addr + UART_MCR , 0 ) ;
out_8 ( avr_addr + UART_IER , 0 ) ;
cval | = UART_LCR_STOP | UART_LCR_PARITY | UART_LCR_EPAR ;
out_8 ( avr_addr + UART_LCR , cval ) ; /* Set character format */
out_8 ( avr_addr + UART_LCR , cval | UART_LCR_DLAB ) ; /* set DLAB */
out_8 ( avr_addr + UART_DLL , quot & 0xff ) ; /* LS of divisor */
out_8 ( avr_addr + UART_DLM , quot > > 8 ) ; /* MS of divisor */
out_8 ( avr_addr + UART_LCR , cval ) ; /* reset DLAB */
out_8 ( avr_addr + UART_FCR , UART_FCR_ENABLE_FIFO ) ; /* enable FIFO */
}
void avr_uart_send ( const char c )
{
if ( ! avr_addr | | ! avr_clock )
return ;
out_8 ( avr_addr + UART_TX , c ) ;
out_8 ( avr_addr + UART_TX , c ) ;
out_8 ( avr_addr + UART_TX , c ) ;
out_8 ( avr_addr + UART_TX , c ) ;
}
static void __init ls_uart_init ( void )
{
local_irq_disable ( ) ;
# ifndef CONFIG_SERIAL_8250
out_8 ( avr_addr + UART_FCR , UART_FCR_ENABLE_FIFO ) ; /* enable FIFO */
out_8 ( avr_addr + UART_FCR , UART_FCR_ENABLE_FIFO |
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT ) ; /* clear FIFOs */
out_8 ( avr_addr + UART_FCR , 0 ) ;
out_8 ( avr_addr + UART_IER , 0 ) ;
/* Clear up interrupts */
( void ) in_8 ( avr_addr + UART_LSR ) ;
( void ) in_8 ( avr_addr + UART_RX ) ;
( void ) in_8 ( avr_addr + UART_IIR ) ;
( void ) in_8 ( avr_addr + UART_MSR ) ;
# endif
avr_uart_configure ( ) ;
local_irq_enable ( ) ;
}
static int __init ls_uarts_init ( void )
{
struct device_node * avr ;
phys_addr_t phys_addr ;
int len ;
2007-08-26 02:08:11 +04:00
if ( ! machine_is ( linkstation ) )
return 0 ;
2006-12-02 00:53:48 +03:00
avr = of_find_node_by_path ( " /soc10x/serial@80004500 " ) ;
if ( ! avr )
return - EINVAL ;
2007-04-03 16:26:41 +04:00
avr_clock = * ( u32 * ) of_get_property ( avr , " clock-frequency " , & len ) ;
phys_addr = ( ( u32 * ) of_get_property ( avr , " reg " , & len ) ) [ 0 ] ;
2006-12-02 00:53:48 +03:00
if ( ! avr_clock | | ! phys_addr )
return - EINVAL ;
avr_addr = ioremap ( phys_addr , 32 ) ;
if ( ! avr_addr )
return - EFAULT ;
ls_uart_init ( ) ;
2006-12-05 22:36:26 +03:00
INIT_WORK ( & wd_work , wd_stop ) ;
2006-12-02 00:53:48 +03:00
schedule_work ( & wd_work ) ;
return 0 ;
}
late_initcall ( ls_uarts_init ) ;