162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * arch/m68k/bvme6000/config.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 1997 Richard Hirst [richard@sleepie.demon.co.uk] 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Based on: 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * linux/amiga/config.c 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Copyright (C) 1993 Hamish Macdonald 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 1362306a36Sopenharmony_ci * License. See the file README.legal in the main directory of this archive 1462306a36Sopenharmony_ci * for more details. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/types.h> 1862306a36Sopenharmony_ci#include <linux/kernel.h> 1962306a36Sopenharmony_ci#include <linux/mm.h> 2062306a36Sopenharmony_ci#include <linux/tty.h> 2162306a36Sopenharmony_ci#include <linux/clocksource.h> 2262306a36Sopenharmony_ci#include <linux/console.h> 2362306a36Sopenharmony_ci#include <linux/linkage.h> 2462306a36Sopenharmony_ci#include <linux/init.h> 2562306a36Sopenharmony_ci#include <linux/major.h> 2662306a36Sopenharmony_ci#include <linux/rtc.h> 2762306a36Sopenharmony_ci#include <linux/interrupt.h> 2862306a36Sopenharmony_ci#include <linux/bcd.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <asm/bootinfo.h> 3162306a36Sopenharmony_ci#include <asm/bootinfo-vme.h> 3262306a36Sopenharmony_ci#include <asm/byteorder.h> 3362306a36Sopenharmony_ci#include <asm/setup.h> 3462306a36Sopenharmony_ci#include <asm/irq.h> 3562306a36Sopenharmony_ci#include <asm/traps.h> 3662306a36Sopenharmony_ci#include <asm/machdep.h> 3762306a36Sopenharmony_ci#include <asm/bvme6000hw.h> 3862306a36Sopenharmony_ci#include <asm/config.h> 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic void bvme6000_get_model(char *model); 4162306a36Sopenharmony_ciextern void bvme6000_sched_init(void); 4262306a36Sopenharmony_ciextern int bvme6000_hwclk (int, struct rtc_time *); 4362306a36Sopenharmony_ciextern void bvme6000_reset (void); 4462306a36Sopenharmony_civoid bvme6000_set_vectors (void); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ciint __init bvme6000_parse_bootinfo(const struct bi_record *bi) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci if (be16_to_cpu(bi->tag) == BI_VME_TYPE) 5062306a36Sopenharmony_ci return 0; 5162306a36Sopenharmony_ci else 5262306a36Sopenharmony_ci return 1; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_civoid bvme6000_reset(void) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci volatile PitRegsPtr pit = (PitRegsPtr)BVME_PIT_BASE; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci pr_info("\r\n\nCalled bvme6000_reset\r\n" 6062306a36Sopenharmony_ci "\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r"); 6162306a36Sopenharmony_ci /* The string of returns is to delay the reset until the whole 6262306a36Sopenharmony_ci * message is output. */ 6362306a36Sopenharmony_ci /* Enable the watchdog, via PIT port C bit 4 */ 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci pit->pcddr |= 0x10; /* WDOG enable */ 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci while(1) 6862306a36Sopenharmony_ci ; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic void bvme6000_get_model(char *model) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci sprintf(model, "BVME%d000", m68k_cputype == CPU_68060 ? 6 : 4); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* 7762306a36Sopenharmony_ci * This function is called during kernel startup to initialize 7862306a36Sopenharmony_ci * the bvme6000 IRQ handling routines. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_cistatic void __init bvme6000_init_IRQ(void) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci m68k_setup_user_interrupt(VEC_USER, 192); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_civoid __init config_bvme6000(void) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci volatile PitRegsPtr pit = (PitRegsPtr)BVME_PIT_BASE; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* Board type is only set by newer versions of vmelilo/tftplilo */ 9062306a36Sopenharmony_ci if (!vme_brdtype) { 9162306a36Sopenharmony_ci if (m68k_cputype == CPU_68060) 9262306a36Sopenharmony_ci vme_brdtype = VME_TYPE_BVME6000; 9362306a36Sopenharmony_ci else 9462306a36Sopenharmony_ci vme_brdtype = VME_TYPE_BVME4000; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci#if 0 9762306a36Sopenharmony_ci /* Call bvme6000_set_vectors() so ABORT will work, along with BVMBug 9862306a36Sopenharmony_ci * debugger. Note trap_init() will splat the abort vector, but 9962306a36Sopenharmony_ci * bvme6000_init_IRQ() will put it back again. Hopefully. */ 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci bvme6000_set_vectors(); 10262306a36Sopenharmony_ci#endif 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci mach_sched_init = bvme6000_sched_init; 10562306a36Sopenharmony_ci mach_init_IRQ = bvme6000_init_IRQ; 10662306a36Sopenharmony_ci mach_hwclk = bvme6000_hwclk; 10762306a36Sopenharmony_ci mach_reset = bvme6000_reset; 10862306a36Sopenharmony_ci mach_get_model = bvme6000_get_model; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci pr_info("Board is %sconfigured as a System Controller\n", 11162306a36Sopenharmony_ci *config_reg_ptr & BVME_CONFIG_SW1 ? "" : "not "); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* Now do the PIT configuration */ 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci pit->pgcr = 0x00; /* Unidirectional 8 bit, no handshake for now */ 11662306a36Sopenharmony_ci pit->psrr = 0x18; /* PIACK and PIRQ functions enabled */ 11762306a36Sopenharmony_ci pit->pacr = 0x00; /* Sub Mode 00, H2 i/p, no DMA */ 11862306a36Sopenharmony_ci pit->padr = 0x00; /* Just to be tidy! */ 11962306a36Sopenharmony_ci pit->paddr = 0x00; /* All inputs for now (safest) */ 12062306a36Sopenharmony_ci pit->pbcr = 0x80; /* Sub Mode 1x, H4 i/p, no DMA */ 12162306a36Sopenharmony_ci pit->pbdr = 0xbc | (*config_reg_ptr & BVME_CONFIG_SW1 ? 0 : 0x40); 12262306a36Sopenharmony_ci /* PRI, SYSCON?, Level3, SCC clks from xtal */ 12362306a36Sopenharmony_ci pit->pbddr = 0xf3; /* Mostly outputs */ 12462306a36Sopenharmony_ci pit->pcdr = 0x01; /* PA transceiver disabled */ 12562306a36Sopenharmony_ci pit->pcddr = 0x03; /* WDOG disable */ 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* Disable snooping for Ethernet and VME accesses */ 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci bvme_acr_addrctl = 0; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ciirqreturn_t bvme6000_abort_int (int irq, void *dev_id) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci unsigned long *new = (unsigned long *)vectors; 13662306a36Sopenharmony_ci unsigned long *old = (unsigned long *)0xf8000000; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* Wait for button release */ 13962306a36Sopenharmony_ci while (*(volatile unsigned char *)BVME_LOCAL_IRQ_STAT & BVME_ABORT_STATUS) 14062306a36Sopenharmony_ci ; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci *(new+4) = *(old+4); /* Illegal instruction */ 14362306a36Sopenharmony_ci *(new+9) = *(old+9); /* Trace */ 14462306a36Sopenharmony_ci *(new+47) = *(old+47); /* Trap #15 */ 14562306a36Sopenharmony_ci *(new+0x1f) = *(old+0x1f); /* ABORT switch */ 14662306a36Sopenharmony_ci return IRQ_HANDLED; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic u64 bvme6000_read_clk(struct clocksource *cs); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic struct clocksource bvme6000_clk = { 15262306a36Sopenharmony_ci .name = "rtc", 15362306a36Sopenharmony_ci .rating = 250, 15462306a36Sopenharmony_ci .read = bvme6000_read_clk, 15562306a36Sopenharmony_ci .mask = CLOCKSOURCE_MASK(32), 15662306a36Sopenharmony_ci .flags = CLOCK_SOURCE_IS_CONTINUOUS, 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic u32 clk_total, clk_offset; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci#define RTC_TIMER_CLOCK_FREQ 8000000 16262306a36Sopenharmony_ci#define RTC_TIMER_CYCLES (RTC_TIMER_CLOCK_FREQ / HZ) 16362306a36Sopenharmony_ci#define RTC_TIMER_COUNT ((RTC_TIMER_CYCLES / 2) - 1) 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic irqreturn_t bvme6000_timer_int (int irq, void *dev_id) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci unsigned long flags; 16862306a36Sopenharmony_ci volatile RtcPtr_t rtc = (RtcPtr_t)BVME_RTC_BASE; 16962306a36Sopenharmony_ci unsigned char msr; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci local_irq_save(flags); 17262306a36Sopenharmony_ci msr = rtc->msr & 0xc0; 17362306a36Sopenharmony_ci rtc->msr = msr | 0x20; /* Ack the interrupt */ 17462306a36Sopenharmony_ci clk_total += RTC_TIMER_CYCLES; 17562306a36Sopenharmony_ci clk_offset = 0; 17662306a36Sopenharmony_ci legacy_timer_tick(1); 17762306a36Sopenharmony_ci local_irq_restore(flags); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return IRQ_HANDLED; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/* 18362306a36Sopenharmony_ci * Set up the RTC timer 1 to mode 2, so T1 output toggles every 5ms 18462306a36Sopenharmony_ci * (40000 x 125ns). It will interrupt every 10ms, when T1 goes low. 18562306a36Sopenharmony_ci * So, when reading the elapsed time, you should read timer1, 18662306a36Sopenharmony_ci * subtract it from 39999, and then add 40000 if T1 is high. 18762306a36Sopenharmony_ci * That gives you the number of 125ns ticks in to the 10ms period, 18862306a36Sopenharmony_ci * so divide by 8 to get the microsecond result. 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_civoid bvme6000_sched_init (void) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci volatile RtcPtr_t rtc = (RtcPtr_t)BVME_RTC_BASE; 19462306a36Sopenharmony_ci unsigned char msr = rtc->msr & 0xc0; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci rtc->msr = 0; /* Ensure timer registers accessible */ 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (request_irq(BVME_IRQ_RTC, bvme6000_timer_int, IRQF_TIMER, "timer", 19962306a36Sopenharmony_ci NULL)) 20062306a36Sopenharmony_ci panic ("Couldn't register timer int"); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci rtc->t1cr_omr = 0x04; /* Mode 2, ext clk */ 20362306a36Sopenharmony_ci rtc->t1msb = RTC_TIMER_COUNT >> 8; 20462306a36Sopenharmony_ci rtc->t1lsb = RTC_TIMER_COUNT & 0xff; 20562306a36Sopenharmony_ci rtc->irr_icr1 &= 0xef; /* Route timer 1 to INTR pin */ 20662306a36Sopenharmony_ci rtc->msr = 0x40; /* Access int.cntrl, etc */ 20762306a36Sopenharmony_ci rtc->pfr_icr0 = 0x80; /* Just timer 1 ints enabled */ 20862306a36Sopenharmony_ci rtc->irr_icr1 = 0; 20962306a36Sopenharmony_ci rtc->t1cr_omr = 0x0a; /* INTR+T1 active lo, push-pull */ 21062306a36Sopenharmony_ci rtc->t0cr_rtmr &= 0xdf; /* Stop timers in standby */ 21162306a36Sopenharmony_ci rtc->msr = 0; /* Access timer 1 control */ 21262306a36Sopenharmony_ci rtc->t1cr_omr = 0x05; /* Mode 2, ext clk, GO */ 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci rtc->msr = msr; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci clocksource_register_hz(&bvme6000_clk, RTC_TIMER_CLOCK_FREQ); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (request_irq(BVME_IRQ_ABORT, bvme6000_abort_int, 0, 21962306a36Sopenharmony_ci "abort", bvme6000_abort_int)) 22062306a36Sopenharmony_ci panic ("Couldn't register abort int"); 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/* 22562306a36Sopenharmony_ci * NOTE: Don't accept any readings within 5us of rollover, as 22662306a36Sopenharmony_ci * the T1INT bit may be a little slow getting set. There is also 22762306a36Sopenharmony_ci * a fault in the chip, meaning that reads may produce invalid 22862306a36Sopenharmony_ci * results... 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic u64 bvme6000_read_clk(struct clocksource *cs) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci unsigned long flags; 23462306a36Sopenharmony_ci volatile RtcPtr_t rtc = (RtcPtr_t)BVME_RTC_BASE; 23562306a36Sopenharmony_ci volatile PitRegsPtr pit = (PitRegsPtr)BVME_PIT_BASE; 23662306a36Sopenharmony_ci unsigned char msr, msb; 23762306a36Sopenharmony_ci unsigned char t1int, t1op; 23862306a36Sopenharmony_ci u32 v = 800000, ov; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci local_irq_save(flags); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci msr = rtc->msr & 0xc0; 24362306a36Sopenharmony_ci rtc->msr = 0; /* Ensure timer registers accessible */ 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci do { 24662306a36Sopenharmony_ci ov = v; 24762306a36Sopenharmony_ci t1int = rtc->msr & 0x20; 24862306a36Sopenharmony_ci t1op = pit->pcdr & 0x04; 24962306a36Sopenharmony_ci rtc->t1cr_omr |= 0x40; /* Latch timer1 */ 25062306a36Sopenharmony_ci msb = rtc->t1msb; /* Read timer1 */ 25162306a36Sopenharmony_ci v = (msb << 8) | rtc->t1lsb; /* Read timer1 */ 25262306a36Sopenharmony_ci } while (t1int != (rtc->msr & 0x20) || 25362306a36Sopenharmony_ci t1op != (pit->pcdr & 0x04) || 25462306a36Sopenharmony_ci abs(ov-v) > 80 || 25562306a36Sopenharmony_ci v > RTC_TIMER_COUNT - (RTC_TIMER_COUNT / 100)); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci v = RTC_TIMER_COUNT - v; 25862306a36Sopenharmony_ci if (!t1op) /* If in second half cycle.. */ 25962306a36Sopenharmony_ci v += RTC_TIMER_CYCLES / 2; 26062306a36Sopenharmony_ci if (msb > 0 && t1int) 26162306a36Sopenharmony_ci clk_offset = RTC_TIMER_CYCLES; 26262306a36Sopenharmony_ci rtc->msr = msr; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci v += clk_offset + clk_total; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci local_irq_restore(flags); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return v; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/* 27262306a36Sopenharmony_ci * Looks like op is non-zero for setting the clock, and zero for 27362306a36Sopenharmony_ci * reading the clock. 27462306a36Sopenharmony_ci * 27562306a36Sopenharmony_ci * struct hwclk_time { 27662306a36Sopenharmony_ci * unsigned sec; 0..59 27762306a36Sopenharmony_ci * unsigned min; 0..59 27862306a36Sopenharmony_ci * unsigned hour; 0..23 27962306a36Sopenharmony_ci * unsigned day; 1..31 28062306a36Sopenharmony_ci * unsigned mon; 0..11 28162306a36Sopenharmony_ci * unsigned year; 00... 28262306a36Sopenharmony_ci * int wday; 0..6, 0 is Sunday, -1 means unknown/don't set 28362306a36Sopenharmony_ci * }; 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ciint bvme6000_hwclk(int op, struct rtc_time *t) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci volatile RtcPtr_t rtc = (RtcPtr_t)BVME_RTC_BASE; 28962306a36Sopenharmony_ci unsigned char msr = rtc->msr & 0xc0; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci rtc->msr = 0x40; /* Ensure clock and real-time-mode-register 29262306a36Sopenharmony_ci * are accessible */ 29362306a36Sopenharmony_ci if (op) 29462306a36Sopenharmony_ci { /* Write.... */ 29562306a36Sopenharmony_ci rtc->t0cr_rtmr = t->tm_year%4; 29662306a36Sopenharmony_ci rtc->bcd_tenms = 0; 29762306a36Sopenharmony_ci rtc->bcd_sec = bin2bcd(t->tm_sec); 29862306a36Sopenharmony_ci rtc->bcd_min = bin2bcd(t->tm_min); 29962306a36Sopenharmony_ci rtc->bcd_hr = bin2bcd(t->tm_hour); 30062306a36Sopenharmony_ci rtc->bcd_dom = bin2bcd(t->tm_mday); 30162306a36Sopenharmony_ci rtc->bcd_mth = bin2bcd(t->tm_mon + 1); 30262306a36Sopenharmony_ci rtc->bcd_year = bin2bcd(t->tm_year%100); 30362306a36Sopenharmony_ci if (t->tm_wday >= 0) 30462306a36Sopenharmony_ci rtc->bcd_dow = bin2bcd(t->tm_wday+1); 30562306a36Sopenharmony_ci rtc->t0cr_rtmr = t->tm_year%4 | 0x08; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci else 30862306a36Sopenharmony_ci { /* Read.... */ 30962306a36Sopenharmony_ci do { 31062306a36Sopenharmony_ci t->tm_sec = bcd2bin(rtc->bcd_sec); 31162306a36Sopenharmony_ci t->tm_min = bcd2bin(rtc->bcd_min); 31262306a36Sopenharmony_ci t->tm_hour = bcd2bin(rtc->bcd_hr); 31362306a36Sopenharmony_ci t->tm_mday = bcd2bin(rtc->bcd_dom); 31462306a36Sopenharmony_ci t->tm_mon = bcd2bin(rtc->bcd_mth)-1; 31562306a36Sopenharmony_ci t->tm_year = bcd2bin(rtc->bcd_year); 31662306a36Sopenharmony_ci if (t->tm_year < 70) 31762306a36Sopenharmony_ci t->tm_year += 100; 31862306a36Sopenharmony_ci t->tm_wday = bcd2bin(rtc->bcd_dow)-1; 31962306a36Sopenharmony_ci } while (t->tm_sec != bcd2bin(rtc->bcd_sec)); 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci rtc->msr = msr; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci return 0; 32562306a36Sopenharmony_ci} 326