162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/arch/m68k/hp300/time.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1998 Philip Blundell <philb@gnu.org> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This file contains the HP300-specific time handling code. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <asm/ptrace.h> 1162306a36Sopenharmony_ci#include <linux/clocksource.h> 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/sched.h> 1562306a36Sopenharmony_ci#include <linux/kernel_stat.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <asm/machdep.h> 1862306a36Sopenharmony_ci#include <asm/irq.h> 1962306a36Sopenharmony_ci#include <asm/io.h> 2062306a36Sopenharmony_ci#include <asm/traps.h> 2162306a36Sopenharmony_ci#include <asm/blinken.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic u64 hp300_read_clk(struct clocksource *cs); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic struct clocksource hp300_clk = { 2662306a36Sopenharmony_ci .name = "timer", 2762306a36Sopenharmony_ci .rating = 250, 2862306a36Sopenharmony_ci .read = hp300_read_clk, 2962306a36Sopenharmony_ci .mask = CLOCKSOURCE_MASK(32), 3062306a36Sopenharmony_ci .flags = CLOCK_SOURCE_IS_CONTINUOUS, 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic u32 clk_total, clk_offset; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* Clock hardware definitions */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define CLOCKBASE 0xf05f8000 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define CLKCR1 0x1 4062306a36Sopenharmony_ci#define CLKCR2 0x3 4162306a36Sopenharmony_ci#define CLKCR3 CLKCR1 4262306a36Sopenharmony_ci#define CLKSR CLKCR2 4362306a36Sopenharmony_ci#define CLKMSB1 0x5 4462306a36Sopenharmony_ci#define CLKLSB1 0x7 4562306a36Sopenharmony_ci#define CLKMSB2 0x9 4662306a36Sopenharmony_ci#define CLKMSB3 0xD 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define CLKSR_INT1 BIT(0) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* This is for machines which generate the exact clock. */ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define HP300_TIMER_CLOCK_FREQ 250000 5362306a36Sopenharmony_ci#define HP300_TIMER_CYCLES (HP300_TIMER_CLOCK_FREQ / HZ) 5462306a36Sopenharmony_ci#define INTVAL (HP300_TIMER_CYCLES - 1) 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic irqreturn_t hp300_tick(int irq, void *dev_id) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci unsigned long flags; 5962306a36Sopenharmony_ci unsigned long tmp; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci local_irq_save(flags); 6262306a36Sopenharmony_ci in_8(CLOCKBASE + CLKSR); 6362306a36Sopenharmony_ci asm volatile ("movpw %1@(5),%0" : "=d" (tmp) : "a" (CLOCKBASE)); 6462306a36Sopenharmony_ci clk_total += INTVAL; 6562306a36Sopenharmony_ci clk_offset = 0; 6662306a36Sopenharmony_ci legacy_timer_tick(1); 6762306a36Sopenharmony_ci timer_heartbeat(); 6862306a36Sopenharmony_ci local_irq_restore(flags); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* Turn off the network and SCSI leds */ 7162306a36Sopenharmony_ci blinken_leds(0, 0xe0); 7262306a36Sopenharmony_ci return IRQ_HANDLED; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic u64 hp300_read_clk(struct clocksource *cs) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci unsigned long flags; 7862306a36Sopenharmony_ci unsigned char lsb, msb, msb_new; 7962306a36Sopenharmony_ci u32 ticks; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci local_irq_save(flags); 8262306a36Sopenharmony_ci /* Read current timer 1 value */ 8362306a36Sopenharmony_ci msb = in_8(CLOCKBASE + CLKMSB1); 8462306a36Sopenharmony_ciagain: 8562306a36Sopenharmony_ci if ((in_8(CLOCKBASE + CLKSR) & CLKSR_INT1) && msb > 0) 8662306a36Sopenharmony_ci clk_offset = INTVAL; 8762306a36Sopenharmony_ci lsb = in_8(CLOCKBASE + CLKLSB1); 8862306a36Sopenharmony_ci msb_new = in_8(CLOCKBASE + CLKMSB1); 8962306a36Sopenharmony_ci if (msb_new != msb) { 9062306a36Sopenharmony_ci msb = msb_new; 9162306a36Sopenharmony_ci goto again; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci ticks = INTVAL - ((msb << 8) | lsb); 9562306a36Sopenharmony_ci ticks += clk_offset + clk_total; 9662306a36Sopenharmony_ci local_irq_restore(flags); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return ticks; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_civoid __init hp300_sched_init(void) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci out_8(CLOCKBASE + CLKCR2, 0x1); /* select CR1 */ 10462306a36Sopenharmony_ci out_8(CLOCKBASE + CLKCR1, 0x1); /* reset */ 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci asm volatile(" movpw %0,%1@(5)" : : "d" (INTVAL), "a" (CLOCKBASE)); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (request_irq(IRQ_AUTO_6, hp300_tick, IRQF_TIMER, "timer tick", NULL)) 10962306a36Sopenharmony_ci pr_err("Couldn't register timer interrupt\n"); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci out_8(CLOCKBASE + CLKCR2, 0x1); /* select CR1 */ 11262306a36Sopenharmony_ci out_8(CLOCKBASE + CLKCR1, 0x40); /* enable irq */ 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci clocksource_register_hz(&hp300_clk, HP300_TIMER_CLOCK_FREQ); 11562306a36Sopenharmony_ci} 116