hc
2024-12-19 9370bb92b2d16684ee45cf24e879c93c509162da
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// SPDX-License-Identifier: GPL-2.0
/*
 *  linux/arch/m68k/hp300/time.c
 *
 *  Copyright (C) 1998 Philip Blundell <philb@gnu.org>
 *
 *  This file contains the HP300-specific time handling code.
 */
 
#include <asm/ptrace.h>
#include <linux/clocksource.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/kernel_stat.h>
#include <linux/interrupt.h>
#include <asm/machdep.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/traps.h>
#include <asm/blinken.h>
 
static u64 hp300_read_clk(struct clocksource *cs);
 
static struct clocksource hp300_clk = {
   .name   = "timer",
   .rating = 250,
   .read   = hp300_read_clk,
   .mask   = CLOCKSOURCE_MASK(32),
   .flags  = CLOCK_SOURCE_IS_CONTINUOUS,
};
 
static u32 clk_total, clk_offset;
 
/* Clock hardware definitions */
 
#define CLOCKBASE    0xf05f8000
 
#define    CLKCR1        0x1
#define    CLKCR2        0x3
#define    CLKCR3        CLKCR1
#define    CLKSR        CLKCR2
#define    CLKMSB1        0x5
#define    CLKLSB1        0x7
#define    CLKMSB2        0x9
#define    CLKMSB3        0xD
 
#define    CLKSR_INT1    BIT(0)
 
/* This is for machines which generate the exact clock. */
 
#define HP300_TIMER_CLOCK_FREQ 250000
#define HP300_TIMER_CYCLES     (HP300_TIMER_CLOCK_FREQ / HZ)
#define INTVAL                 (HP300_TIMER_CYCLES - 1)
 
static irqreturn_t hp300_tick(int irq, void *dev_id)
{
   irq_handler_t timer_routine = dev_id;
   unsigned long flags;
   unsigned long tmp;
 
   local_irq_save(flags);
   in_8(CLOCKBASE + CLKSR);
   asm volatile ("movpw %1@(5),%0" : "=d" (tmp) : "a" (CLOCKBASE));
   clk_total += INTVAL;
   clk_offset = 0;
   timer_routine(0, NULL);
   local_irq_restore(flags);
 
   /* Turn off the network and SCSI leds */
   blinken_leds(0, 0xe0);
   return IRQ_HANDLED;
}
 
static u64 hp300_read_clk(struct clocksource *cs)
{
   unsigned long flags;
   unsigned char lsb, msb, msb_new;
   u32 ticks;
 
   local_irq_save(flags);
   /* Read current timer 1 value */
   msb = in_8(CLOCKBASE + CLKMSB1);
again:
   if ((in_8(CLOCKBASE + CLKSR) & CLKSR_INT1) && msb > 0)
       clk_offset = INTVAL;
   lsb = in_8(CLOCKBASE + CLKLSB1);
   msb_new = in_8(CLOCKBASE + CLKMSB1);
   if (msb_new != msb) {
       msb = msb_new;
       goto again;
   }
 
   ticks = INTVAL - ((msb << 8) | lsb);
   ticks += clk_offset + clk_total;
   local_irq_restore(flags);
 
   return ticks;
}
 
void __init hp300_sched_init(irq_handler_t vector)
{
  out_8(CLOCKBASE + CLKCR2, 0x1);        /* select CR1 */
  out_8(CLOCKBASE + CLKCR1, 0x1);        /* reset */
 
  asm volatile(" movpw %0,%1@(5)" : : "d" (INTVAL), "a" (CLOCKBASE));
 
  if (request_irq(IRQ_AUTO_6, hp300_tick, IRQF_TIMER, "timer tick", vector))
    pr_err("Couldn't register timer interrupt\n");
 
  out_8(CLOCKBASE + CLKCR2, 0x1);        /* select CR1 */
  out_8(CLOCKBASE + CLKCR1, 0x40);        /* enable irq */
 
  clocksource_register_hz(&hp300_clk, HP300_TIMER_CLOCK_FREQ);
}