URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/or1k/trunk/linux/linux-2.4/arch/m68k/q40
- from Rev 1275 to Rev 1765
- ↔ Reverse comparison
Rev 1275 → Rev 1765
/q40ints.c
0,0 → 1,465
/* |
* arch/m68k/q40/q40ints.c |
* |
* Copyright (C) 1999,2001 Richard Zidlicky |
* |
* This file is subject to the terms and conditions of the GNU General Public |
* License. See the file COPYING in the main directory of this archive |
* for more details. |
* |
* .. used to be loosely based on bvme6000ints.c |
* |
*/ |
|
#include <linux/config.h> |
#include <linux/types.h> |
#include <linux/kernel.h> |
#include <linux/errno.h> |
#include <linux/string.h> |
#include <linux/sched.h> |
|
#include <asm/rtc.h> |
#include <asm/ptrace.h> |
#include <asm/system.h> |
#include <asm/irq.h> |
#include <asm/hardirq.h> |
#include <asm/traps.h> |
|
#include <asm/q40_master.h> |
#include <asm/q40ints.h> |
|
/* |
* Q40 IRQs are defined as follows: |
* 3,4,5,6,7,10,11,14,15 : ISA dev IRQs |
* 16-31: reserved |
* 32 : keyboard int |
* 33 : frame int (50/200 Hz periodic timer) |
* 34 : sample int (10/20 KHz periodic timer) |
* |
*/ |
|
extern int ints_inited; |
|
|
void q40_irq2_handler (int, void *, struct pt_regs *fp); |
|
|
extern void (*q40_sys_default_handler[]) (int, void *, struct pt_regs *); |
|
static void q40_defhand (int irq, void *dev_id, struct pt_regs *fp); |
static void sys_default_handler(int lev, void *dev_id, struct pt_regs *regs); |
|
|
#define DEVNAME_SIZE 24 |
|
static struct q40_irq_node { |
void (*handler)(int, void *, struct pt_regs *); |
unsigned long flags; |
void *dev_id; |
/* struct q40_irq_node *next;*/ |
char devname[DEVNAME_SIZE]; |
unsigned count; |
unsigned short state; |
} irq_tab[Q40_IRQ_MAX+1]; |
|
short unsigned q40_ablecount[Q40_IRQ_MAX+1]; |
|
/* |
* void q40_init_IRQ (void) |
* |
* Parameters: None |
* |
* Returns: Nothing |
* |
* This function is called during kernel startup to initialize |
* the q40 IRQ handling routines. |
*/ |
|
static int disabled=0; |
|
void q40_init_IRQ (void) |
{ |
int i; |
|
disabled=0; |
for (i = 0; i <= Q40_IRQ_MAX; i++) { |
irq_tab[i].handler = q40_defhand; |
irq_tab[i].flags = 0; |
irq_tab[i].dev_id = NULL; |
/* irq_tab[i].next = NULL;*/ |
irq_tab[i].devname[0] = 0; |
irq_tab[i].count = 0; |
irq_tab[i].state =0; |
q40_ablecount[i]=0; /* all enabled */ |
} |
|
/* setup handler for ISA ints */ |
sys_request_irq(IRQ2,q40_irq2_handler, 0, "q40 ISA and master chip", NULL); |
|
/* now enable some ints.. */ |
master_outb(1,EXT_ENABLE_REG); /* ISA IRQ 5-15 */ |
|
/* make sure keyboard IRQ is disabled */ |
master_outb(0,KEY_IRQ_ENABLE_REG); |
} |
|
int q40_request_irq(unsigned int irq, |
void (*handler)(int, void *, struct pt_regs *), |
unsigned long flags, const char *devname, void *dev_id) |
{ |
/*printk("q40_request_irq %d, %s\n",irq,devname);*/ |
|
if (irq > Q40_IRQ_MAX || (irq>15 && irq<32)) { |
printk("%s: Incorrect IRQ %d from %s\n", __FUNCTION__, irq, devname); |
return -ENXIO; |
} |
|
/* test for ISA ints not implemented by HW */ |
switch (irq) |
{ |
case 1: case 2: case 8: case 9: |
case 12: case 13: |
printk("%s: ISA IRQ %d from %s not implemented by HW\n", __FUNCTION__, irq, devname); |
return -ENXIO; |
case 11: |
printk("warning IRQ 10 and 11 not distinguishable\n"); |
irq=10; |
} |
|
if (irq<Q40_IRQ_SAMPLE) |
{ |
if (irq_tab[irq].dev_id != NULL) |
{ |
printk("%s: IRQ %d from %s is not replaceable\n", |
__FUNCTION__, irq, irq_tab[irq].devname); |
return -EBUSY; |
} |
/*printk("IRQ %d set to handler %p\n",irq,handler);*/ |
if (dev_id==NULL) |
{ |
printk("WARNING: dev_id == NULL in request_irq\n"); |
dev_id=(void*)1; |
} |
irq_tab[irq].handler = handler; |
irq_tab[irq].flags = flags; |
irq_tab[irq].dev_id = dev_id; |
strncpy(irq_tab[irq].devname,devname,DEVNAME_SIZE); |
irq_tab[irq].state = 0; |
return 0; |
} |
else { |
/* Q40_IRQ_SAMPLE :somewhat special actions required here ..*/ |
sys_request_irq(4,handler,flags,devname,dev_id); |
sys_request_irq(6,handler,flags,devname,dev_id); |
return 0; |
} |
} |
|
void q40_free_irq(unsigned int irq, void *dev_id) |
{ |
if (irq > Q40_IRQ_MAX || (irq>15 && irq<32)) { |
printk("%s: Incorrect IRQ %d, dev_id %x \n", __FUNCTION__, irq, (unsigned)dev_id); |
return; |
} |
|
/* test for ISA ints not implemented by HW */ |
switch (irq) |
{ |
case 1: case 2: case 8: case 9: |
case 12: case 13: |
printk("%s: ISA IRQ %d from %x invalid\n", __FUNCTION__, irq, (unsigned)dev_id); |
return; |
case 11: irq=10; |
} |
|
if (irq<Q40_IRQ_SAMPLE) |
{ |
if (irq_tab[irq].dev_id != dev_id) |
printk("%s: Removing probably wrong IRQ %d from %s\n", |
__FUNCTION__, irq, irq_tab[irq].devname); |
|
irq_tab[irq].handler = q40_defhand; |
irq_tab[irq].flags = 0; |
irq_tab[irq].dev_id = NULL; |
/* irq_tab[irq].devname = NULL; */ |
/* do not reset state !! */ |
} |
else |
{ /* == Q40_IRQ_SAMPLE */ |
sys_free_irq(4,dev_id); |
sys_free_irq(6,dev_id); |
} |
} |
|
|
void q40_process_int (int level, struct pt_regs *fp) |
{ |
printk("unexpected interrupt vec=%x, pc=%lx, d0=%lx, d0_orig=%lx, d1=%lx, d2=%lx\n", |
level, fp->pc, fp->d0, fp->orig_d0, fp->d1, fp->d2); |
printk("\tIIRQ_REG = %x, EIRQ_REG = %x\n",master_inb(IIRQ_REG),master_inb(EIRQ_REG)); |
} |
|
/* |
* this stuff doesn't really belong here.. |
*/ |
|
int ql_ticks=0; /* 200Hz ticks since last jiffie */ |
static int sound_ticks=0; |
|
#define SVOL 45 |
|
void q40_mksound(unsigned int hz, unsigned int ticks) |
{ |
/* for now ignore hz, except that hz==0 switches off sound */ |
/* simply alternate the ampl (128-SVOL)-(128+SVOL)-..-.. at 200Hz */ |
if (hz==0) |
{ |
if (sound_ticks) |
sound_ticks=1; |
|
*DAC_LEFT=128; |
*DAC_RIGHT=128; |
|
return; |
} |
/* sound itself is done in q40_timer_int */ |
if (sound_ticks == 0) sound_ticks=1000; /* pretty long beep */ |
sound_ticks=ticks<<1; |
} |
|
static void (*q40_timer_routine)(int, void *, struct pt_regs *); |
|
static void q40_timer_int (int irq, void * dev, struct pt_regs * regs) |
{ |
ql_ticks = ql_ticks ? 0 : 1; |
if (sound_ticks) |
{ |
unsigned char sval=(sound_ticks & 1) ? 128-SVOL : 128+SVOL; |
sound_ticks--; |
*DAC_LEFT=sval; |
*DAC_RIGHT=sval; |
} |
|
if (ql_ticks) return; |
|
q40_timer_routine(irq, dev, regs); |
} |
|
void q40_sched_init (void (*timer_routine)(int, void *, struct pt_regs *)) |
{ |
int timer_irq; |
|
q40_timer_routine = timer_routine; |
timer_irq=Q40_IRQ_FRAME; |
|
if (request_irq(timer_irq, q40_timer_int, 0, |
"timer", q40_timer_int)) |
panic ("Couldn't register timer int"); |
|
master_outb(-1,FRAME_CLEAR_REG); |
master_outb( 1,FRAME_RATE_REG); |
} |
|
|
/* |
* tables to translate bits into IRQ numbers |
* it is a good idea to order the entries by priority |
* |
*/ |
|
struct IRQ_TABLE{ unsigned mask; int irq ;}; |
#if 0 |
static struct IRQ_TABLE iirqs[]={ |
{Q40_IRQ_FRAME_MASK,Q40_IRQ_FRAME}, |
{Q40_IRQ_KEYB_MASK,Q40_IRQ_KEYBOARD}, |
{0,0}}; |
#endif |
static struct IRQ_TABLE eirqs[]={ |
{Q40_IRQ3_MASK,3}, /* ser 1 */ |
{Q40_IRQ4_MASK,4}, /* ser 2 */ |
{Q40_IRQ14_MASK,14}, /* IDE 1 */ |
{Q40_IRQ15_MASK,15}, /* IDE 2 */ |
{Q40_IRQ6_MASK,6}, /* floppy, handled elsewhere */ |
{Q40_IRQ7_MASK,7}, /* par */ |
|
{Q40_IRQ5_MASK,5}, |
{Q40_IRQ10_MASK,10}, |
|
|
|
|
{0,0}}; |
|
/* complain only this many times about spurious ints : */ |
static int ccleirq=60; /* ISA dev IRQ's*/ |
/*static int cclirq=60;*/ /* internal */ |
|
/* FIXME: add shared ints,mask,unmask,probing.... */ |
|
#define IRQ_INPROGRESS 1 |
/*static unsigned short saved_mask;*/ |
//static int do_tint=0; |
|
#define DEBUG_Q40INT |
/*#define IP_USE_DISABLE *//* would be nice, but crashes ???? */ |
|
static int mext_disabled=0; /* ext irq disabled by master chip? */ |
static int aliased_irq=0; /* how many times inside handler ?*/ |
|
|
/* got level 2 interrupt, dispatch to ISA or keyboard/timer IRQs */ |
void q40_irq2_handler (int vec, void *devname, struct pt_regs *fp) |
{ |
unsigned mir, mer; |
int irq,i; |
|
mir=master_inb(IIRQ_REG); |
if (mir&Q40_IRQ_FRAME_MASK) { |
irq_tab[Q40_IRQ_FRAME].count++; |
irq_tab[Q40_IRQ_FRAME].handler(Q40_IRQ_FRAME,irq_tab[Q40_IRQ_FRAME].dev_id,fp); |
master_outb(-1,FRAME_CLEAR_REG); |
} |
if ((mir&Q40_IRQ_SER_MASK) || (mir&Q40_IRQ_EXT_MASK)) { |
mer=master_inb(EIRQ_REG); |
for (i=0; eirqs[i].mask; i++) { |
if (mer&(eirqs[i].mask)) { |
irq=eirqs[i].irq; |
/* |
* There is a little mess wrt which IRQ really caused this irq request. The |
* main problem is that IIRQ_REG and EIRQ_REG reflect the state when they |
* are read - which is long after the request came in. In theory IRQs should |
* not just go away but they occassionally do |
*/ |
if (irq>4 && irq<=15 && mext_disabled) { |
/*aliased_irq++;*/ |
goto iirq; |
} |
if (irq_tab[irq].handler == q40_defhand ) { |
printk("handler for IRQ %d not defined\n",irq); |
continue; /* ignore uninited INTs :-( */ |
} |
if ( irq_tab[irq].state & IRQ_INPROGRESS ) { |
/* some handlers do sti() for irq latency reasons, */ |
/* however reentering an active irq handler is not permitted */ |
#ifdef IP_USE_DISABLE |
/* in theory this is the better way to do it because it still */ |
/* lets through eg the serial irqs, unfortunately it crashes */ |
disable_irq(irq); |
disabled=1; |
#else |
/*printk("IRQ_INPROGRESS detected for irq %d, disabling - %s disabled\n",irq,disabled ? "already" : "not yet"); */ |
fp->sr = (((fp->sr) & (~0x700))+0x200); |
disabled=1; |
#endif |
goto iirq; |
} |
irq_tab[irq].count++; |
irq_tab[irq].state |= IRQ_INPROGRESS; |
irq_tab[irq].handler(irq,irq_tab[irq].dev_id,fp); |
irq_tab[irq].state &= ~IRQ_INPROGRESS; |
|
/* naively enable everything, if that fails than */ |
/* this function will be reentered immediately thus */ |
/* getting another chance to disable the IRQ */ |
|
if ( disabled ) { |
#ifdef IP_USE_DISABLE |
if (irq>4){ |
disabled=0; |
enable_irq(irq);} |
#else |
disabled=0; |
/*printk("reenabling irq %d\n",irq); */ |
#endif |
} |
return; |
} |
} |
if (mer && ccleirq>0 && !aliased_irq) |
printk("ISA interrupt from unknown source? EIRQ_REG = %x\n",mer),ccleirq--; |
} |
iirq: |
mir=master_inb(IIRQ_REG); |
/* should test whether keyboard irq is really enabled, doing it in defhand */ |
if (mir&Q40_IRQ_KEYB_MASK) { |
irq_tab[Q40_IRQ_KEYBOARD].count++; |
irq_tab[Q40_IRQ_KEYBOARD].handler(Q40_IRQ_KEYBOARD,irq_tab[Q40_IRQ_KEYBOARD].dev_id,fp); |
} |
} |
|
int q40_get_irq_list (char *buf) |
{ |
int i, len = 0; |
|
for (i = 0; i <= Q40_IRQ_MAX; i++) |
{ |
if (irq_tab[i].count) |
len += sprintf (buf+len, "%sIRQ %02d: %8d %s%s\n", |
(i<=15) ? "ISA-" : " " , |
i, irq_tab[i].count, |
irq_tab[i].devname[0] ? irq_tab[i].devname : "?", |
irq_tab[i].handler == q40_defhand ? |
" (now unassigned)" : ""); |
} |
return len; |
} |
|
|
static void q40_defhand (int irq, void *dev_id, struct pt_regs *fp) |
{ |
if (irq!=Q40_IRQ_KEYBOARD) |
printk ("Unknown q40 interrupt %d\n", irq); |
else master_outb(-1,KEYBOARD_UNLOCK_REG); |
} |
static void sys_default_handler(int lev, void *dev_id, struct pt_regs *regs) |
{ |
printk ("Uninitialised interrupt level %d\n", lev); |
} |
|
void (*q40_sys_default_handler[SYS_IRQS]) (int, void *, struct pt_regs *) = { |
sys_default_handler,sys_default_handler,sys_default_handler,sys_default_handler, |
sys_default_handler,sys_default_handler,sys_default_handler,sys_default_handler |
}; |
|
|
void q40_enable_irq (unsigned int irq) |
{ |
if ( irq>=5 && irq<=15 ) |
{ |
mext_disabled--; |
if (mext_disabled>0) |
printk("q40_enable_irq : nested disable/enable\n"); |
if (mext_disabled==0) |
master_outb(1,EXT_ENABLE_REG); |
} |
} |
|
|
void q40_disable_irq (unsigned int irq) |
{ |
/* disable ISA iqs : only do something if the driver has been |
* verified to be Q40 "compatible" - right now IDE, NE2K |
* Any driver should not attempt to sleep across disable_irq !! |
*/ |
|
if ( irq>=5 && irq<=15 ) { |
master_outb(0,EXT_ENABLE_REG); |
mext_disabled++; |
if (mext_disabled>1) printk("disable_irq nesting count %d\n",mext_disabled); |
} |
} |
|
unsigned long q40_probe_irq_on (void) |
{ |
printk("irq probing not working - reconfigure the driver to avoid this\n"); |
return -1; |
} |
int q40_probe_irq_off (unsigned long irqs) |
{ |
return -1; |
} |
/* |
* Local variables: |
* compile-command: "m68k-linux-gcc -D__KERNEL__ -I/home/rz/lx/linux-2.2.6/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -ffixed-a2 -m68040 -c -o q40ints.o q40ints.c" |
* End: |
*/ |
/config.c
0,0 → 1,405
/* |
* 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/config.h> |
#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/rtc.h> |
#include <asm/machdep.h> |
#include <asm/q40_master.h> |
#include <asm/keyboard.h> |
|
extern void floppy_setup(char *str, int *ints); |
|
extern int q40kbd_translate(unsigned char scancode, unsigned char *keycode, |
char raw_mode); |
extern void q40_process_int (int level, struct pt_regs *regs); |
extern void (*q40_sys_default_handler[]) (int, void *, struct pt_regs *); /* added just for debugging */ |
extern void q40_init_IRQ (void); |
extern void q40_free_irq (unsigned int, void *); |
extern int q40_get_irq_list (char *); |
extern void q40_enable_irq (unsigned int); |
extern void q40_disable_irq (unsigned int); |
static void q40_get_model(char *model); |
static int q40_get_hardware_list(char *buffer); |
extern int q40_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *devname, void *dev_id); |
extern void q40_sched_init(void (*handler)(int, void *, struct pt_regs *)); |
|
extern unsigned long q40_gettimeoffset (void); |
extern void q40_gettod (int *year, int *mon, int *day, int *hour, |
int *min, int *sec); |
extern int q40_hwclk (int, struct rtc_time *); |
extern unsigned int q40_get_ss (void); |
extern int q40_set_clock_mmss (unsigned long); |
static int q40_get_rtc_pll(struct rtc_pll_info *pll); |
static int q40_set_rtc_pll(struct rtc_pll_info *pll); |
extern void q40_reset (void); |
void q40_halt(void); |
extern void q40_waitbut(void); |
void q40_set_vectors (void); |
|
void q40_mksound(unsigned int /*freq*/, unsigned int /*ticks*/ ); |
|
extern char *saved_command_line; |
extern char m68k_debug_device[]; |
static void q40_mem_console_write(struct console *co, const char *b, |
unsigned int count); |
|
extern int ql_ticks; |
|
static struct console q40_console_driver = { |
name: "debug", |
flags: CON_PRINTBUFFER, |
index: -1, |
}; |
|
|
/* early debugging function:*/ |
extern char *q40_mem_cptr; /*=(char *)0xff020000;*/ |
static int _cpleft; |
|
#if 0 |
int q40_kbd_translate(unsigned char keycode, unsigned char *keycodep, char raw_mode) |
{ |
*keycodep = keycode; |
return 1; |
} |
#endif |
|
static void q40_mem_console_write(struct console *co, const char *s, |
unsigned int count) |
{ |
char *p=(char *)s; |
|
if (count<_cpleft) |
while (count-- >0){ |
*q40_mem_cptr=*p++; |
q40_mem_cptr+=4; |
_cpleft--; |
} |
} |
#if 0 |
void printq40(char *str) |
{ |
int l=strlen(str); |
char *p=q40_mem_cptr; |
|
while (l-- >0 && _cpleft-- >0) |
{ |
*p=*str++; |
p+=4; |
} |
q40_mem_cptr=p; |
} |
#endif |
|
#if 0 |
int q40_kbdrate (struct kbd_repeat *k) |
{ |
return 0; |
} |
#endif |
|
static int halted=0; |
|
#ifdef CONFIG_HEARTBEAT |
static void q40_heartbeat(int on) |
{ |
if (halted) return; |
|
if (on) |
Q40_LED_ON(); |
else |
Q40_LED_OFF(); |
} |
#endif |
|
void q40_reset() |
{ |
halted=1; |
printk ("\n\n*******************************************\n" |
"Called q40_reset : press the RESET button!! \n" |
"*******************************************\n"); |
Q40_LED_ON(); |
while(1) ; |
} |
void q40_halt() |
{ |
halted=1; |
printk ("\n\n*******************\n" |
" Called q40_halt\n" |
"*******************\n"); |
Q40_LED_ON(); |
while(1) ; |
} |
|
static void q40_get_model(char *model) |
{ |
sprintf(model, "Q40"); |
} |
|
/* No hardware options on Q40? */ |
|
static int q40_get_hardware_list(char *buffer) |
{ |
*buffer = '\0'; |
return 0; |
} |
|
static unsigned int serports[]={0x3f8,0x2f8,0x3e8,0x2e8,0}; |
void q40_disable_irqs(void) |
{ |
unsigned i,j; |
|
j=0; |
while((i=serports[j++])) outb(0,i+UART_IER); |
master_outb(0,EXT_ENABLE_REG); |
master_outb(0,KEY_IRQ_ENABLE_REG); |
} |
|
void __init config_q40(void) |
{ |
mach_sched_init = q40_sched_init; |
|
#ifdef CONFIG_VT |
mach_keyb_init = q40kbd_init_hw; |
mach_kbd_translate = q40kbd_translate; |
kd_mksound = q40_mksound; |
#endif |
mach_init_IRQ = q40_init_IRQ; |
mach_gettimeoffset = q40_gettimeoffset; |
mach_gettod = q40_gettod; |
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; |
|
mach_reset = q40_reset; |
mach_free_irq = q40_free_irq; |
mach_process_int = q40_process_int; |
mach_get_irq_list = q40_get_irq_list; |
mach_request_irq = q40_request_irq; |
enable_irq = q40_enable_irq; |
disable_irq = q40_disable_irq; |
mach_default_handler = &q40_sys_default_handler; |
mach_get_model = q40_get_model; |
mach_get_hardware_list = q40_get_hardware_list; |
|
#ifdef CONFIG_MAGIC_SYSRQ |
mach_sysrq_key = 0x54; |
#endif |
#ifdef CONFIG_HEARTBEAT |
mach_heartbeat = q40_heartbeat; |
#endif |
mach_halt = q40_halt; |
#ifdef CONFIG_DUMMY_CONSOLE |
conswitchp = &dummy_con; |
#endif |
|
/* 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; |
|
/* useful for early debugging stages - writes kernel messages into SRAM */ |
if (!strncmp( m68k_debug_device,"mem",3 )) |
{ |
/*printk("using NVRAM debug, q40_mem_cptr=%p\n",q40_mem_cptr);*/ |
_cpleft=2000-((long)q40_mem_cptr-0xff020000)/4; |
q40_console_driver.write = q40_mem_console_write; |
register_console(&q40_console_driver); |
} |
} |
|
|
int q40_parse_bootinfo(const struct bi_record *rec) |
{ |
return 1; |
} |
|
|
static inline unsigned char bcd2bin (unsigned char b) |
{ |
return ((b>>4)*10 + (b&15)); |
} |
|
static inline unsigned char bin2bcd (unsigned char b) |
{ |
return (((b/10)*16) + (b%10)); |
} |
|
|
unsigned long q40_gettimeoffset (void) |
{ |
return 5000*(ql_ticks!=0); |
} |
|
extern void q40_gettod (int *year, int *mon, int *day, int *hour, |
int *min, int *sec) |
{ |
Q40_RTC_CTRL |= Q40_RTC_READ; |
*year = bcd2bin (Q40_RTC_YEAR); |
*mon = bcd2bin (Q40_RTC_MNTH); |
*day = bcd2bin (Q40_RTC_DATE); |
*hour = bcd2bin (Q40_RTC_HOUR); |
*min = bcd2bin (Q40_RTC_MINS); |
*sec = bcd2bin (Q40_RTC_SECS); |
Q40_RTC_CTRL &= ~(Q40_RTC_READ); |
|
} |
|
|
|
/* |
* 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) |
{ |
if (op) |
{ /* Write.... */ |
Q40_RTC_CTRL |= Q40_RTC_WRITE; |
|
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); |
|
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; |
|
} |
|
return 0; |
} |
|
unsigned int q40_get_ss() |
{ |
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. |
*/ |
|
int q40_set_clock_mmss (unsigned long nowtime) |
{ |
int retval = 0; |
short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60; |
|
int rtc_minutes; |
|
|
rtc_minutes = bcd2bin (Q40_RTC_MINS); |
|
if ((rtc_minutes < real_minutes |
? real_minutes - rtc_minutes |
: rtc_minutes - real_minutes) < 30) |
{ |
Q40_RTC_CTRL |= Q40_RTC_WRITE; |
Q40_RTC_MINS = bin2bcd(real_minutes); |
Q40_RTC_SECS = bin2bcd(real_seconds); |
Q40_RTC_CTRL &= ~(Q40_RTC_WRITE); |
} |
else |
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) |
{ |
int tmp=Q40_RTC_CTRL; |
pll->pll_value = tmp & Q40_RTC_PLL_MASK; |
if (tmp & Q40_RTC_PLL_SIGN) |
pll->pll_value = -pll->pll_value; |
pll->pll_max=31; |
pll->pll_min=-31; |
pll->pll_posmult=512; |
pll->pll_negmult=256; |
pll->pll_clock=125829120; |
return 0; |
} |
|
static int q40_set_rtc_pll(struct rtc_pll_info *pll) |
{ |
if (!pll->pll_ctrl){ |
/* 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; |
} |
/Makefile
0,0 → 1,14
# |
# Makefile for Linux arch/m68k/q40 source directory |
# |
# Note! Dependencies are done automagically by 'make dep', which also |
# removes any old dependencies. DON'T put your own dependencies here |
# unless it's something special (ie not a .c file). |
# |
# Note 2! The CFLAGS definitions are now in the main makefile... |
|
O_TARGET := q40.o |
|
obj-y := config.o q40ints.o |
|
include $(TOPDIR)/Rules.make |
/README
0,0 → 1,138
Linux for the Q40 |
================= |
|
You may try http://www.geocities.com/SiliconValley/Bay/2602/ for |
some up to date information. Booter and other tools will be also |
available from this place or ftp.uni-erlangen.de/linux/680x0/q40/ |
and mirrors. |
|
Hints to documentation usually refer to the linux source tree in |
/usr/src/linux/Documentation unless URL given. |
|
It seems IRQ unmasking can't be safely done on a Q40. IRQ probing |
is not implemented - do not try it! (See below) |
|
For a list of kernel command-line options read the documentation for the |
particular device drivers. |
|
The floppy imposes a very high interrupt load on the CPU, approx 30K/s. |
When something blocks interrupts (HD) it will loose some of them, so far |
this is not known to have caused any data loss. On highly loaded systems |
it can make the floppy very slow or practically stop. Other Q40 OS' simply |
poll the floppy for this reason - something that can't be done in Linux. |
Only possible cure is getting a 82072 controller with fifo instead of |
the 8272A. |
|
drivers used by the Q40, apart from the very obvious (console etc.): |
drivers/char/q40_keyb.c # use PC keymaps for national keyboards |
serial.c # normal PC driver - any speed |
lp.c # printer driver |
genrtc.c # RTC |
char/joystick/* # most of this should work, not |
# in default config.in |
block/q40ide.c # startup for ide |
ide* # see Documentation/ide.txt |
floppy.c # normal PC driver, DMA emu in asm/floppy.h |
# and arch/m68k/kernel/entry.S |
# see drivers/block/README.fd |
net/ne.c |
video/q40fb.c |
parport/* |
sound/dmasound_core.c |
dmasound_q40.c |
|
Various other PC drivers can be enabled simply by adding them to |
arch/m68k/config.in, especially 8 bit devices should be without any |
problems. For cards using 16bit io/mem more care is required, like |
checking byte order issues, hacking memcpy_*_io etc. |
|
|
Debugging |
========= |
|
Upon startup the kernel will usually output "ABCQGHIJ" into the SRAM, |
preceded by the booter signature. This is a trace just in case something |
went wrong during earliest setup stages of head.S. |
**Changed** to preserve SRAM contents by default, this is only done when |
requested - SRAM must start with '%LX$' signature to do this. '-d' option |
to 'lxx' loader enables this. |
|
SRAM can also be used as additional console device, use debug=mem. |
This will save kernel startup msgs into SRAM, the screen will display |
only the penguin - and shell prompt if it gets that far.. |
Unfortunately only 2000 bytes are available. |
|
Serial console works and can also be used for debugging, see loader_txt |
|
Most problems seem to be caused by fawlty or badly configured io-cards or |
hard drives anyway. |
Make sure to configure the parallel port as SPP and remove IRQ/DMA jumpers |
for first testing. The Q40 does not support DMA and may have trouble with |
parallel ports version of interrupts. |
|
|
Q40 Hardware Description |
======================== |
|
This is just an overview, see asm-m68k/* for details ask if you have any |
questions. |
|
The Q40 consists of a 68040@40 MHz, 1MB video RAM, up to 32MB RAM, AT-style |
keyboard interface, 1 Programmable LED, 2x8bit DACs and up to 1MB ROM, 1MB |
shadow ROM. |
The Q60 has any of 68060 or 68LC060 and up to 128 MB RAM. |
|
Most interfacing like floppy, IDE, serial and parallel ports is done via ISA |
slots. The ISA io and mem range is mapped (sparse&byteswapped!) into separate |
regions of the memory. |
The main interrupt register IIRQ_REG will indicate whether an IRQ was internal |
or from some ISA devices, EIRQ_REG can distinguish up to 8 ISA IRQs. |
|
The Q40 custom chip is programmable to provide 2 periodic timers: |
- 50 or 200 Hz - level 2, !!THIS CANT BE DISABLED!! |
- 10 or 20 KHz - level 4, used for dma-sound |
|
Linux uses the 200 Hz interrupt for timer and beep by default. |
|
|
Interrupts |
========== |
|
q40 master chip handles only a subset of level triggered interrupts. |
|
Linux has some requirements wrt interrupt architecture, these are |
to my knowledge: |
(a) interrupt handler must not be reentered even when sti() is called |
from within handler |
(b) working enable/disable_irq |
|
Luckily these requirements are only important for drivers shared |
with other architectures - ide,serial,parallel, ethernet. |
q40ints.c now contains a trivial hack for (a), (b) is more difficult |
because only irq's 4-15 can be disabled - and only all of them at once. |
Thus disable_irq() can effectively block the machine if the driver goes |
asleep. |
One thing to keep in mind when hacking around the interrupt code is |
that there is no way to find out which IRQ caused a request, [EI]IRQ_REG |
displays current state of the various IRQ lines. |
|
Keyboard |
======== |
|
q40 receives AT make/break codes from the keyboard, these are translated to |
the PC scancodes x86 Linux uses. So by theory every national keyboard should |
work just by loading the appropriate x86 keytable - see any national-HOWTO. |
|
Unfortunately the AT->PC translation isn't quite trivial and even worse, my |
documentation of it is absolutely minimal - thus some exotic keys may not |
behave exactly as expected. |
|
There is still hope that it can be fixed completely though. If you encounter |
problems, email me ideally this: |
- exact keypress/release sequence |
- 'showkey -s' run on q40, non-X session |
- 'showkey -s' run on a PC, non-X session |
- AT codes as displayed by the q40 debugging ROM |
btw if the showkey output from PC and Q40 doesn't differ then you have some |
classic configuration problem - don't send me anything in this case |
|