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>
2008-10-13 23:58:47 +04:00
# include <linux/bcd.h>
2012-01-23 11:27:54 +04:00
# include <linux/platform_device.h>
2005-04-17 02:20:36 +04:00
# include <asm/io.h>
# include <asm/rtc.h>
# include <asm/bootinfo.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 void q40_init_IRQ ( void ) ;
2005-04-17 02:20:36 +04:00
static void q40_get_model ( char * model ) ;
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
2012-11-08 22:34:55 +04:00
static u32 q40_gettimeoffset ( void ) ;
2008-07-17 23:16:26 +04:00
static int q40_hwclk ( int , struct rtc_time * ) ;
static unsigned int q40_get_ss ( void ) ;
static 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_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
2008-07-17 23:16:26 +04:00
static void q40_reset ( void )
2005-04-17 02:20:36 +04:00
{
2007-05-02 00:32:43 +04:00
halted = 1 ;
printk ( " \n \n ******************************************* \n "
2010-02-06 20:47:11 +03:00
" Called q40_reset : press the RESET button!! \n "
2005-04-17 02:20:36 +04:00
" ******************************************* \n " ) ;
Q40_LED_ON ( ) ;
2007-05-02 00:32:43 +04:00
while ( 1 )
;
2005-04-17 02:20:36 +04:00
}
2008-07-17 23:16:26 +04:00
static void q40_halt ( void )
2005-04-17 02:20:36 +04:00
{
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
}
2007-05-02 00:32:43 +04:00
static unsigned int serports [ ] =
{
0x3f8 , 0x2f8 , 0x3e8 , 0x2e8 , 0
} ;
2008-07-17 23:16:26 +04:00
static void q40_disable_irqs ( void )
2005-04-17 02:20:36 +04:00
{
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 ;
2012-11-08 22:34:55 +04:00
arch_gettimeoffset = q40_gettimeoffset ;
2007-05-02 00:32:43 +04:00
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 ;
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
}
2012-11-08 22:34:55 +04:00
static u32 q40_gettimeoffset ( void )
2005-04-17 02:20:36 +04:00
{
2012-11-08 22:34:55 +04:00
return 5000 * ( ql_ticks ! = 0 ) * 1000 ;
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
* } ;
*/
2008-07-17 23:16:26 +04:00
static int q40_hwclk ( int op , struct rtc_time * t )
2005-04-17 02:20:36 +04:00
{
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 ;
}
2008-07-17 23:16:26 +04:00
static unsigned int q40_get_ss ( void )
2005-04-17 02:20:36 +04:00
{
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 .
*/
2008-07-17 23:16:26 +04:00
static 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 ;
}
2012-01-23 11:27:54 +04:00
static __init int q40_add_kbd_device ( void )
{
struct platform_device * pdev ;
2012-03-18 16:20:27 +04:00
if ( ! MACH_IS_Q40 )
return - ENODEV ;
2012-01-23 11:27:54 +04:00
pdev = platform_device_register_simple ( " q40kbd " , - 1 , NULL , 0 ) ;
2013-07-15 05:50:32 +04:00
return PTR_ERR_OR_ZERO ( pdev ) ;
2012-01-23 11:27:54 +04:00
}
arch_initcall ( q40_add_kbd_device ) ;