2005-04-17 02:20:36 +04:00
/*
* arch / m68k / q40 / config . c
*
* Copyright ( C ) 1999 Richard Zidlicky
*
* originally based on :
*
* linux / bvme / config . c
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file README . legal in the main directory of this archive
* for more details .
*/
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/tty.h>
# include <linux/console.h>
# include <linux/linkage.h>
# include <linux/init.h>
# include <linux/major.h>
# include <linux/serial_reg.h>
# include <linux/rtc.h>
# include <linux/vt_kern.h>
# include <asm/io.h>
# include <asm/rtc.h>
# include <asm/bootinfo.h>
# include <asm/system.h>
# include <asm/pgtable.h>
# include <asm/setup.h>
# include <asm/irq.h>
# include <asm/traps.h>
# include <asm/machdep.h>
# include <asm/q40_master.h>
2007-05-02 00:32:43 +04:00
extern irqreturn_t q40_process_int ( int level , struct pt_regs * regs ) ;
extern void q40_init_IRQ ( void ) ;
2005-04-17 02:20:36 +04:00
static void q40_get_model ( char * model ) ;
static int q40_get_hardware_list ( char * buffer ) ;
2006-10-09 15:19:47 +04:00
extern void q40_sched_init ( irq_handler_t handler ) ;
2005-04-17 02:20:36 +04:00
2007-05-02 00:32:43 +04:00
extern unsigned long q40_gettimeoffset ( void ) ;
extern int q40_hwclk ( int , struct rtc_time * ) ;
extern unsigned int q40_get_ss ( void ) ;
extern int q40_set_clock_mmss ( unsigned long ) ;
2005-04-17 02:20:36 +04:00
static int q40_get_rtc_pll ( struct rtc_pll_info * pll ) ;
static int q40_set_rtc_pll ( struct rtc_pll_info * pll ) ;
2007-05-02 00:32:43 +04:00
extern void q40_reset ( void ) ;
2005-04-17 02:20:36 +04:00
void q40_halt ( void ) ;
extern void q40_waitbut ( void ) ;
2007-05-02 00:32:43 +04:00
void q40_set_vectors ( void ) ;
2005-04-17 02:20:36 +04:00
2007-05-02 00:32:43 +04:00
extern void q40_mksound ( unsigned int /*freq*/ , unsigned int /*ticks*/ ) ;
2005-04-17 02:20:36 +04:00
static void q40_mem_console_write ( struct console * co , const char * b ,
2007-05-02 00:32:43 +04:00
unsigned int count ) ;
2005-04-17 02:20:36 +04:00
extern int ql_ticks ;
static struct console q40_console_driver = {
2007-05-02 00:32:43 +04:00
. name = " debug " ,
2007-05-02 00:32:45 +04:00
. write = q40_mem_console_write ,
2007-05-02 00:32:43 +04:00
. flags = CON_PRINTBUFFER ,
. index = - 1 ,
2005-04-17 02:20:36 +04:00
} ;
/* early debugging function:*/
extern char * q40_mem_cptr ; /*=(char *)0xff020000;*/
static int _cpleft ;
static void q40_mem_console_write ( struct console * co , const char * s ,
unsigned int count )
{
2007-05-02 00:32:43 +04:00
const char * p = s ;
if ( count < _cpleft ) {
while ( count - - > 0 ) {
* q40_mem_cptr = * p + + ;
q40_mem_cptr + = 4 ;
_cpleft - - ;
}
}
2005-04-17 02:20:36 +04:00
}
2007-05-02 00:32:43 +04:00
2007-05-02 00:32:45 +04:00
static int __init q40_debug_setup ( char * arg )
{
/* useful for early debugging stages - writes kernel messages into SRAM */
if ( MACH_IS_Q40 & & ! strncmp ( arg , " mem " , 3 ) ) {
/*printk("using NVRAM debug, q40_mem_cptr=%p\n",q40_mem_cptr);*/
_cpleft = 2000 - ( ( long ) q40_mem_cptr - 0xff020000 ) / 4 ;
register_console ( & q40_console_driver ) ;
}
return 0 ;
}
early_param ( " debug " , q40_debug_setup ) ;
2005-04-17 02:20:36 +04:00
#if 0
void printq40 ( char * str )
{
2007-05-02 00:32:43 +04:00
int l = strlen ( str ) ;
char * p = q40_mem_cptr ;
while ( l - - > 0 & & _cpleft - - > 0 ) {
* p = * str + + ;
p + = 4 ;
}
q40_mem_cptr = p ;
2005-04-17 02:20:36 +04:00
}
# endif
2007-05-02 00:32:43 +04:00
static int halted ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_HEARTBEAT
static void q40_heartbeat ( int on )
{
2007-05-02 00:32:43 +04:00
if ( halted )
return ;
2005-04-17 02:20:36 +04:00
2007-05-02 00:32:43 +04:00
if ( on )
Q40_LED_ON ( ) ;
else
Q40_LED_OFF ( ) ;
2005-04-17 02:20:36 +04:00
}
# endif
void q40_reset ( void )
{
2007-05-02 00:32:43 +04:00
halted = 1 ;
printk ( " \n \n ******************************************* \n "
2005-04-17 02:20:36 +04:00
" Called q40_reset : press the RESET button!! \n "
" ******************************************* \n " ) ;
Q40_LED_ON ( ) ;
2007-05-02 00:32:43 +04:00
while ( 1 )
;
2005-04-17 02:20:36 +04:00
}
void q40_halt ( void )
{
2007-05-02 00:32:43 +04:00
halted = 1 ;
printk ( " \n \n ******************* \n "
" Called q40_halt \n "
" ******************* \n " ) ;
2005-04-17 02:20:36 +04:00
Q40_LED_ON ( ) ;
2007-05-02 00:32:43 +04:00
while ( 1 )
;
2005-04-17 02:20:36 +04:00
}
static void q40_get_model ( char * model )
{
2007-05-02 00:32:43 +04:00
sprintf ( model , " Q40 " ) ;
2005-04-17 02:20:36 +04:00
}
/* No hardware options on Q40? */
static int q40_get_hardware_list ( char * buffer )
{
2007-05-02 00:32:43 +04:00
* buffer = ' \0 ' ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-05-02 00:32:43 +04:00
static unsigned int serports [ ] =
{
0x3f8 , 0x2f8 , 0x3e8 , 0x2e8 , 0
} ;
2005-04-17 02:20:36 +04:00
void q40_disable_irqs ( void )
{
2007-05-02 00:32:43 +04:00
unsigned i , j ;
2005-04-17 02:20:36 +04:00
2007-05-02 00:32:43 +04:00
j = 0 ;
while ( ( i = serports [ j + + ] ) )
outb ( 0 , i + UART_IER ) ;
master_outb ( 0 , EXT_ENABLE_REG ) ;
master_outb ( 0 , KEY_IRQ_ENABLE_REG ) ;
2005-04-17 02:20:36 +04:00
}
void __init config_q40 ( void )
{
2007-05-02 00:32:43 +04:00
mach_sched_init = q40_sched_init ;
2005-04-17 02:20:36 +04:00
2007-05-02 00:32:43 +04:00
mach_init_IRQ = q40_init_IRQ ;
mach_gettimeoffset = q40_gettimeoffset ;
mach_hwclk = q40_hwclk ;
mach_get_ss = q40_get_ss ;
mach_get_rtc_pll = q40_get_rtc_pll ;
mach_set_rtc_pll = q40_set_rtc_pll ;
mach_set_clock_mmss = q40_set_clock_mmss ;
2005-04-17 02:20:36 +04:00
2007-05-02 00:32:43 +04:00
mach_reset = q40_reset ;
mach_get_model = q40_get_model ;
mach_get_hardware_list = q40_get_hardware_list ;
2005-04-17 02:20:36 +04:00
# if defined(CONFIG_INPUT_M68K_BEEP) || defined(CONFIG_INPUT_M68K_BEEP_MODULE)
2007-05-02 00:32:43 +04:00
mach_beep = q40_mksound ;
2005-04-17 02:20:36 +04:00
# endif
# ifdef CONFIG_HEARTBEAT
2007-05-02 00:32:43 +04:00
mach_heartbeat = q40_heartbeat ;
2005-04-17 02:20:36 +04:00
# endif
2007-05-02 00:32:43 +04:00
mach_halt = q40_halt ;
/* disable a few things that SMSQ might have left enabled */
q40_disable_irqs ( ) ;
/* no DMA at all, but ide-scsi requires it.. make sure
* all physical RAM fits into the boundary - otherwise
* allocator may play costly and useless tricks */
mach_max_dma_address = 1024 * 1024 * 1024 ;
2005-04-17 02:20:36 +04:00
}
int q40_parse_bootinfo ( const struct bi_record * rec )
{
2007-05-02 00:32:43 +04:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
2007-05-02 00:32:43 +04:00
static inline unsigned char bcd2bin ( unsigned char b )
2005-04-17 02:20:36 +04:00
{
2007-05-02 00:32:43 +04:00
return ( b > > 4 ) * 10 + ( b & 15 ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-02 00:32:43 +04:00
static inline unsigned char bin2bcd ( unsigned char b )
2005-04-17 02:20:36 +04:00
{
2007-05-02 00:32:43 +04:00
return ( b / 10 ) * 16 + ( b % 10 ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-02 00:32:43 +04:00
unsigned long q40_gettimeoffset ( void )
2005-04-17 02:20:36 +04:00
{
2007-05-02 00:32:43 +04:00
return 5000 * ( ql_ticks ! = 0 ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Looks like op is non - zero for setting the clock , and zero for
* reading the clock .
*
* struct hwclk_time {
* unsigned sec ; 0. .59
* unsigned min ; 0. .59
* unsigned hour ; 0. .23
* unsigned day ; 1. .31
* unsigned mon ; 0. .11
* unsigned year ; 00. . .
* int wday ; 0. .6 , 0 is Sunday , - 1 means unknown / don ' t set
* } ;
*/
int q40_hwclk ( int op , struct rtc_time * t )
{
2007-05-02 00:32:43 +04:00
if ( op ) {
/* Write.... */
Q40_RTC_CTRL | = Q40_RTC_WRITE ;
2005-04-17 02:20:36 +04:00
Q40_RTC_SECS = bin2bcd ( t - > tm_sec ) ;
Q40_RTC_MINS = bin2bcd ( t - > tm_min ) ;
Q40_RTC_HOUR = bin2bcd ( t - > tm_hour ) ;
Q40_RTC_DATE = bin2bcd ( t - > tm_mday ) ;
Q40_RTC_MNTH = bin2bcd ( t - > tm_mon + 1 ) ;
Q40_RTC_YEAR = bin2bcd ( t - > tm_year % 100 ) ;
if ( t - > tm_wday > = 0 )
Q40_RTC_DOW = bin2bcd ( t - > tm_wday + 1 ) ;
2007-05-02 00:32:43 +04:00
Q40_RTC_CTRL & = ~ ( Q40_RTC_WRITE ) ;
} else {
/* Read.... */
Q40_RTC_CTRL | = Q40_RTC_READ ;
t - > tm_year = bcd2bin ( Q40_RTC_YEAR ) ;
t - > tm_mon = bcd2bin ( Q40_RTC_MNTH ) - 1 ;
t - > tm_mday = bcd2bin ( Q40_RTC_DATE ) ;
t - > tm_hour = bcd2bin ( Q40_RTC_HOUR ) ;
t - > tm_min = bcd2bin ( Q40_RTC_MINS ) ;
t - > tm_sec = bcd2bin ( Q40_RTC_SECS ) ;
Q40_RTC_CTRL & = ~ ( Q40_RTC_READ ) ;
if ( t - > tm_year < 70 )
t - > tm_year + = 100 ;
t - > tm_wday = bcd2bin ( Q40_RTC_DOW ) - 1 ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
unsigned int q40_get_ss ( void )
{
return bcd2bin ( Q40_RTC_SECS ) ;
}
/*
* Set the minutes and seconds from seconds value ' nowtime ' . Fail if
* clock is out by > 30 minutes . Logic lifted from atari code .
*/
2007-05-02 00:32:43 +04:00
int q40_set_clock_mmss ( unsigned long nowtime )
2005-04-17 02:20:36 +04:00
{
int retval = 0 ;
short real_seconds = nowtime % 60 , real_minutes = ( nowtime / 60 ) % 60 ;
int rtc_minutes ;
2007-05-02 00:32:43 +04:00
rtc_minutes = bcd2bin ( Q40_RTC_MINS ) ;
2005-04-17 02:20:36 +04:00
2007-05-02 00:32:43 +04:00
if ( ( rtc_minutes < real_minutes ?
real_minutes - rtc_minutes :
rtc_minutes - real_minutes ) < 30 ) {
Q40_RTC_CTRL | = Q40_RTC_WRITE ;
2005-04-17 02:20:36 +04:00
Q40_RTC_MINS = bin2bcd ( real_minutes ) ;
Q40_RTC_SECS = bin2bcd ( real_seconds ) ;
Q40_RTC_CTRL & = ~ ( Q40_RTC_WRITE ) ;
2007-05-02 00:32:43 +04:00
} else
2005-04-17 02:20:36 +04:00
retval = - 1 ;
return retval ;
}
/* get and set PLL calibration of RTC clock */
# define Q40_RTC_PLL_MASK ((1<<5)-1)
# define Q40_RTC_PLL_SIGN (1<<5)
static int q40_get_rtc_pll ( struct rtc_pll_info * pll )
{
2007-05-02 00:32:43 +04:00
int tmp = Q40_RTC_CTRL ;
2005-04-17 02:20:36 +04:00
pll - > pll_value = tmp & Q40_RTC_PLL_MASK ;
if ( tmp & Q40_RTC_PLL_SIGN )
pll - > pll_value = - pll - > pll_value ;
2007-05-02 00:32:43 +04:00
pll - > pll_max = 31 ;
pll - > pll_min = - 31 ;
pll - > pll_posmult = 512 ;
pll - > pll_negmult = 256 ;
pll - > pll_clock = 125829120 ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int q40_set_rtc_pll ( struct rtc_pll_info * pll )
{
2007-05-02 00:32:43 +04:00
if ( ! pll - > pll_ctrl ) {
2005-04-17 02:20:36 +04:00
/* the docs are a bit unclear so I am doublesetting */
/* RTC_WRITE here ... */
int tmp = ( pll - > pll_value & 31 ) | ( pll - > pll_value < 0 ? 32 : 0 ) |
Q40_RTC_WRITE ;
Q40_RTC_CTRL | = Q40_RTC_WRITE ;
Q40_RTC_CTRL = tmp ;
Q40_RTC_CTRL & = ~ ( Q40_RTC_WRITE ) ;
return 0 ;
} else
return - EINVAL ;
}