summaryrefslogtreecommitdiff
path: root/arch/m68k/hp300
diff options
context:
space:
mode:
Diffstat (limited to 'arch/m68k/hp300')
-rw-r--r--arch/m68k/hp300/Makefile5
-rw-r--r--arch/m68k/hp300/README.hp30014
-rw-r--r--arch/m68k/hp300/config.c279
-rw-r--r--arch/m68k/hp300/hp300map.map252
-rw-r--r--arch/m68k/hp300/ints.c175
-rw-r--r--arch/m68k/hp300/ints.h9
-rw-r--r--arch/m68k/hp300/ksyms.c9
-rw-r--r--arch/m68k/hp300/reboot.S16
-rw-r--r--arch/m68k/hp300/time.c78
-rw-r--r--arch/m68k/hp300/time.h4
10 files changed, 841 insertions, 0 deletions
diff --git a/arch/m68k/hp300/Makefile b/arch/m68k/hp300/Makefile
new file mode 100644
index 00000000000..89b6317899e
--- /dev/null
+++ b/arch/m68k/hp300/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for Linux arch/m68k/hp300 source directory
+#
+
+obj-y := ksyms.o config.o ints.o time.o reboot.o
diff --git a/arch/m68k/hp300/README.hp300 b/arch/m68k/hp300/README.hp300
new file mode 100644
index 00000000000..47073fbd400
--- /dev/null
+++ b/arch/m68k/hp300/README.hp300
@@ -0,0 +1,14 @@
+HP300 notes
+-----------
+
+The Linux/HP web page is at <http://www.tazenda.demon.co.uk/phil/linux-hp/>
+
+Currently only 9000/340 machines have been tested. Any amount of RAM should
+work now but I've only tried 16MB and 12MB.
+
+The serial console is probably broken at the moment but the Topcat/HIL keyboard
+combination seems to work for me. Your mileage may vary.
+
+The LANCE driver works after a fashion but only if you reset the chip before
+every packet. This doesn't make for very speedy operation.
+
diff --git a/arch/m68k/hp300/config.c b/arch/m68k/hp300/config.c
new file mode 100644
index 00000000000..a0b854f3f94
--- /dev/null
+++ b/arch/m68k/hp300/config.c
@@ -0,0 +1,279 @@
+/*
+ * linux/arch/m68k/hp300/config.c
+ *
+ * Copyright (C) 1998 Philip Blundell <philb@gnu.org>
+ *
+ * This file contains the HP300-specific initialisation code. It gets
+ * called by setup.c.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/console.h>
+
+#include <asm/bootinfo.h>
+#include <asm/machdep.h>
+#include <asm/blinken.h>
+#include <asm/io.h> /* readb() and writeb() */
+#include <asm/hp300hw.h>
+#include <asm/rtc.h>
+
+#include "ints.h"
+#include "time.h"
+
+unsigned long hp300_model;
+unsigned long hp300_uart_scode = -1;
+unsigned char ledstate;
+
+static char s_hp330[] __initdata = "330";
+static char s_hp340[] __initdata = "340";
+static char s_hp345[] __initdata = "345";
+static char s_hp360[] __initdata = "360";
+static char s_hp370[] __initdata = "370";
+static char s_hp375[] __initdata = "375";
+static char s_hp380[] __initdata = "380";
+static char s_hp385[] __initdata = "385";
+static char s_hp400[] __initdata = "400";
+static char s_hp425t[] __initdata = "425t";
+static char s_hp425s[] __initdata = "425s";
+static char s_hp425e[] __initdata = "425e";
+static char s_hp433t[] __initdata = "433t";
+static char s_hp433s[] __initdata = "433s";
+static char *hp300_models[] __initdata = {
+ [HP_320] = NULL,
+ [HP_330] = s_hp330,
+ [HP_340] = s_hp340,
+ [HP_345] = s_hp345,
+ [HP_350] = NULL,
+ [HP_360] = s_hp360,
+ [HP_370] = s_hp370,
+ [HP_375] = s_hp375,
+ [HP_380] = s_hp380,
+ [HP_385] = s_hp385,
+ [HP_400] = s_hp400,
+ [HP_425T] = s_hp425t,
+ [HP_425S] = s_hp425s,
+ [HP_425E] = s_hp425e,
+ [HP_433T] = s_hp433t,
+ [HP_433S] = s_hp433s,
+};
+
+static char hp300_model_name[13] = "HP9000/";
+
+extern void hp300_reset(void);
+extern irqreturn_t (*hp300_default_handler[])(int, void *, struct pt_regs *);
+extern int show_hp300_interrupts(struct seq_file *, void *);
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+extern int hp300_setup_serial_console(void) __init;
+#endif
+
+int __init hp300_parse_bootinfo(const struct bi_record *record)
+{
+ int unknown = 0;
+ const unsigned long *data = record->data;
+
+ switch (record->tag) {
+ case BI_HP300_MODEL:
+ hp300_model = *data;
+ break;
+
+ case BI_HP300_UART_SCODE:
+ hp300_uart_scode = *data;
+ break;
+
+ case BI_HP300_UART_ADDR:
+ /* serial port address: ignored here */
+ break;
+
+ default:
+ unknown = 1;
+ }
+
+ return unknown;
+}
+
+#ifdef CONFIG_HEARTBEAT
+static void hp300_pulse(int x)
+{
+ if (x)
+ blinken_leds(0x10, 0);
+ else
+ blinken_leds(0, 0x10);
+}
+#endif
+
+static void hp300_get_model(char *model)
+{
+ strcpy(model, hp300_model_name);
+}
+
+#define RTCBASE 0xf0420000
+#define RTC_DATA 0x1
+#define RTC_CMD 0x3
+
+#define RTC_BUSY 0x02
+#define RTC_DATA_RDY 0x01
+
+#define rtc_busy() (in_8(RTCBASE + RTC_CMD) & RTC_BUSY)
+#define rtc_data_available() (in_8(RTCBASE + RTC_CMD) & RTC_DATA_RDY)
+#define rtc_status() (in_8(RTCBASE + RTC_CMD))
+#define rtc_command(x) out_8(RTCBASE + RTC_CMD, (x))
+#define rtc_read_data() (in_8(RTCBASE + RTC_DATA))
+#define rtc_write_data(x) out_8(RTCBASE + RTC_DATA, (x))
+
+#define RTC_SETREG 0xe0
+#define RTC_WRITEREG 0xc2
+#define RTC_READREG 0xc3
+
+#define RTC_REG_SEC2 0
+#define RTC_REG_SEC1 1
+#define RTC_REG_MIN2 2
+#define RTC_REG_MIN1 3
+#define RTC_REG_HOUR2 4
+#define RTC_REG_HOUR1 5
+#define RTC_REG_WDAY 6
+#define RTC_REG_DAY2 7
+#define RTC_REG_DAY1 8
+#define RTC_REG_MON2 9
+#define RTC_REG_MON1 10
+#define RTC_REG_YEAR2 11
+#define RTC_REG_YEAR1 12
+
+#define RTC_HOUR1_24HMODE 0x8
+
+#define RTC_STAT_MASK 0xf0
+#define RTC_STAT_RDY 0x40
+
+static inline unsigned char hp300_rtc_read(unsigned char reg)
+{
+ unsigned char s, ret;
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ while (rtc_busy());
+ rtc_command(RTC_SETREG);
+ while (rtc_busy());
+ rtc_write_data(reg);
+ while (rtc_busy());
+ rtc_command(RTC_READREG);
+
+ do {
+ while (!rtc_data_available());
+ s = rtc_status();
+ ret = rtc_read_data();
+ } while ((s & RTC_STAT_MASK) != RTC_STAT_RDY);
+
+ local_irq_restore(flags);
+
+ return ret;
+}
+
+static inline unsigned char hp300_rtc_write(unsigned char reg,
+ unsigned char val)
+{
+ unsigned char s, ret;
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ while (rtc_busy());
+ rtc_command(RTC_SETREG);
+ while (rtc_busy());
+ rtc_write_data((val << 4) | reg);
+ while (rtc_busy());
+ rtc_command(RTC_WRITEREG);
+ while (rtc_busy());
+ rtc_command(RTC_READREG);
+
+ do {
+ while (!rtc_data_available());
+ s = rtc_status();
+ ret = rtc_read_data();
+ } while ((s & RTC_STAT_MASK) != RTC_STAT_RDY);
+
+ local_irq_restore(flags);
+
+ return ret;
+}
+
+static int hp300_hwclk(int op, struct rtc_time *t)
+{
+ if (!op) { /* read */
+ t->tm_sec = hp300_rtc_read(RTC_REG_SEC1) * 10 +
+ hp300_rtc_read(RTC_REG_SEC2);
+ t->tm_min = hp300_rtc_read(RTC_REG_MIN1) * 10 +
+ hp300_rtc_read(RTC_REG_MIN2);
+ t->tm_hour = (hp300_rtc_read(RTC_REG_HOUR1) & 3) * 10 +
+ hp300_rtc_read(RTC_REG_HOUR2);
+ t->tm_wday = -1;
+ t->tm_mday = hp300_rtc_read(RTC_REG_DAY1) * 10 +
+ hp300_rtc_read(RTC_REG_DAY2);
+ t->tm_mon = hp300_rtc_read(RTC_REG_MON1) * 10 +
+ hp300_rtc_read(RTC_REG_MON2) - 1;
+ t->tm_year = hp300_rtc_read(RTC_REG_YEAR1) * 10 +
+ hp300_rtc_read(RTC_REG_YEAR2);
+ if (t->tm_year <= 69)
+ t->tm_year += 100;
+ } else {
+ hp300_rtc_write(RTC_REG_SEC1, t->tm_sec / 10);
+ hp300_rtc_write(RTC_REG_SEC2, t->tm_sec % 10);
+ hp300_rtc_write(RTC_REG_MIN1, t->tm_min / 10);
+ hp300_rtc_write(RTC_REG_MIN2, t->tm_min % 10);
+ hp300_rtc_write(RTC_REG_HOUR1,
+ ((t->tm_hour / 10) & 3) | RTC_HOUR1_24HMODE);
+ hp300_rtc_write(RTC_REG_HOUR2, t->tm_hour % 10);
+ hp300_rtc_write(RTC_REG_DAY1, t->tm_mday / 10);
+ hp300_rtc_write(RTC_REG_DAY2, t->tm_mday % 10);
+ hp300_rtc_write(RTC_REG_MON1, (t->tm_mon + 1) / 10);
+ hp300_rtc_write(RTC_REG_MON2, (t->tm_mon + 1) % 10);
+ if (t->tm_year >= 100)
+ t->tm_year -= 100;
+ hp300_rtc_write(RTC_REG_YEAR1, t->tm_year / 10);
+ hp300_rtc_write(RTC_REG_YEAR2, t->tm_year % 10);
+ }
+
+ return 0;
+}
+
+static unsigned int hp300_get_ss(void)
+{
+ return hp300_rtc_read(RTC_REG_SEC1) * 10 +
+ hp300_rtc_read(RTC_REG_SEC2);
+}
+
+void __init config_hp300(void)
+{
+ mach_sched_init = hp300_sched_init;
+ mach_init_IRQ = hp300_init_IRQ;
+ mach_request_irq = hp300_request_irq;
+ mach_free_irq = hp300_free_irq;
+ mach_get_model = hp300_get_model;
+ mach_get_irq_list = show_hp300_interrupts;
+ mach_gettimeoffset = hp300_gettimeoffset;
+ mach_default_handler = &hp300_default_handler;
+ mach_hwclk = hp300_hwclk;
+ mach_get_ss = hp300_get_ss;
+ mach_reset = hp300_reset;
+#ifdef CONFIG_HEARTBEAT
+ mach_heartbeat = hp300_pulse;
+#endif
+#ifdef CONFIG_DUMMY_CONSOLE
+ conswitchp = &dummy_con;
+#endif
+ mach_max_dma_address = 0xffffffff;
+
+ if (hp300_model >= HP_330 && hp300_model <= HP_433S && hp300_model != HP_350) {
+ printk(KERN_INFO "Detected HP9000 model %s\n", hp300_models[hp300_model-HP_320]);
+ strcat(hp300_model_name, hp300_models[hp300_model-HP_320]);
+ }
+ else {
+ panic("Unknown HP9000 Model");
+ }
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+ hp300_setup_serial_console();
+#endif
+}
diff --git a/arch/m68k/hp300/hp300map.map b/arch/m68k/hp300/hp300map.map
new file mode 100644
index 00000000000..6b45f0abc95
--- /dev/null
+++ b/arch/m68k/hp300/hp300map.map
@@ -0,0 +1,252 @@
+# HP300 kernel keymap. This uses 7 modifier combinations.
+keymaps 0-2,4-5,8,12
+# Change the above line into
+# keymaps 0-2,4-6,8,12
+# in case you want the entries
+# altgr control keycode 83 = Boot
+# altgr control keycode 111 = Boot
+# below.
+#
+# In fact AltGr is used very little, and one more keymap can
+# be saved by mapping AltGr to Alt (and adapting a few entries):
+# keycode 100 = Alt
+#
+keycode 1 =
+keycode 2 = Alt
+keycode 3 = Alt
+keycode 4 = Shift
+keycode 5 = Shift
+keycode 6 = Control
+keycode 7 =
+keycode 8 =
+keycode 9 =
+keycode 10 =
+keycode 11 =
+keycode 12 =
+keycode 13 =
+keycode 14 =
+keycode 15 =
+keycode 16 =
+keycode 17 =
+keycode 18 =
+keycode 19 =
+keycode 20 =
+keycode 21 =
+keycode 22 =
+keycode 23 =
+keycode 24 = b
+keycode 25 = v
+keycode 26 = c
+keycode 27 = x
+keycode 28 = z
+keycode 29 =
+keycode 30 =
+keycode 31 = Escape Delete
+keycode 32 =
+keycode 33 =
+keycode 34 =
+keycode 35 =
+keycode 36 =
+keycode 37 =
+keycode 38 =
+keycode 39 =
+keycode 40 = h
+keycode 41 = g
+keycode 42 = f
+keycode 43 = d
+keycode 44 = s
+keycode 45 = a
+keycode 46 =
+keycode 47 = Caps_Lock
+keycode 48 = u
+keycode 49 = y
+keycode 50 = t
+keycode 51 = r
+keycode 52 = e
+keycode 53 = w
+keycode 54 = q
+keycode 55 = Tab Tab
+ alt keycode 55 = Meta_Tab
+keycode 56 = seven ampersand
+keycode 57 = six asciicircum
+keycode 58 = five percent
+keycode 59 = four dollar
+keycode 60 = three numbersign
+keycode 61 = two at at
+keycode 62 = one exclam exclam
+keycode 63 = grave asciitilde
+ control keycode 63 = nul
+ alt keycode 63 = Meta_grave
+keycode 64 =
+keycode 65 =
+keycode 66 =
+keycode 67 =
+keycode 68 =
+keycode 69 =
+keycode 70 =
+keycode 71 =
+keycode 72 =
+keycode 73 = F4
+ control keycode 73 = Console_4
+keycode 74 = F3
+ control keycode 74 = Console_3
+keycode 75 = F2
+ control keycode 75 = Console_2
+keycode 76 = F1
+ control keycode 76 = Console_1
+keycode 77 =
+keycode 78 =
+keycode 79 =
+keycode 80 =
+keycode 81 = F5
+ control keycode 81 = Console_5
+keycode 82 = F6
+ control keycode 82 = Console_6
+keycode 83 = F7
+ control keycode 83 = Console_7
+keycode 84 = F8
+ control keycode 84 = Console_8
+keycode 85 =
+keycode 86 =
+keycode 87 =
+keycode 88 = eight asterisk asterisk
+keycode 89 = nine parenleft bracketleft
+keycode 90 = zero parenright bracketright
+keycode 91 = minus underscore
+keycode 92 = equal plus
+keycode 93 = BackSpace
+keycode 94 =
+keycode 95 =
+keycode 96 = i
+keycode 97 = o
+keycode 98 = p
+keycode 99 = bracketleft braceleft
+keycode 100 = bracketright braceright
+keycode 101 = backslash bar
+ control keycode 101 = Control_backslash
+ alt keycode 101 = Meta_backslash
+keycode 102 =
+keycode 103 =
+keycode 104 = j
+keycode 105 = k
+keycode 106 = l
+keycode 107 = semicolon colon
+ alt keycode 107 = Meta_semicolon
+keycode 108 = apostrophe quotedbl
+ control keycode 108 = Control_g
+ alt keycode 108 = Meta_apostrophe
+keycode 109 = Return
+keycode 110 =
+keycode 111 =
+keycode 112 = m
+keycode 113 = comma less
+keycode 114 = period greater
+keycode 115 = slash question
+keycode 116 =
+keycode 117 =
+keycode 118 =
+keycode 119 =
+keycode 120 = n
+keycode 121 = space space
+keycode 122 =
+keycode 123 =
+keycode 124 = Left
+keycode 125 = Down
+keycode 126 = Up
+keycode 127 = Right
+string F1 = "\033[[A"
+string F2 = "\033[[B"
+string F3 = "\033[[C"
+string F4 = "\033[[D"
+string F5 = "\033[[E"
+string F6 = "\033[17~"
+string F7 = "\033[18~"
+string F8 = "\033[19~"
+string F9 = "\033[20~"
+string F10 = "\033[21~"
+string F11 = "\033[23~"
+string F12 = "\033[24~"
+string F13 = "\033[25~"
+string F14 = "\033[26~"
+string F15 = "\033[28~"
+string F16 = "\033[29~"
+string F17 = "\033[31~"
+string F18 = "\033[32~"
+string F19 = "\033[33~"
+string F20 = "\033[34~"
+string Find = "\033[1~"
+string Insert = "\033[2~"
+string Remove = "\033[3~"
+string Select = "\033[4~"
+string Prior = "\033[5~"
+string Next = "\033[6~"
+string Macro = "\033[M"
+string Pause = "\033[P"
+compose '`' 'A' to ''
+compose '`' 'a' to ''
+compose '\'' 'A' to ''
+compose '\'' 'a' to ''
+compose '^' 'A' to ''
+compose '^' 'a' to ''
+compose '~' 'A' to ''
+compose '~' 'a' to ''
+compose '"' 'A' to ''
+compose '"' 'a' to ''
+compose 'O' 'A' to ''
+compose 'o' 'a' to ''
+compose '0' 'A' to ''
+compose '0' 'a' to ''
+compose 'A' 'A' to ''
+compose 'a' 'a' to ''
+compose 'A' 'E' to ''
+compose 'a' 'e' to ''
+compose ',' 'C' to ''
+compose ',' 'c' to ''
+compose '`' 'E' to ''
+compose '`' 'e' to ''
+compose '\'' 'E' to ''
+compose '\'' 'e' to ''
+compose '^' 'E' to ''
+compose '^' 'e' to ''
+compose '"' 'E' to ''
+compose '"' 'e' to ''
+compose '`' 'I' to ''
+compose '`' 'i' to ''
+compose '\'' 'I' to ''
+compose '\'' 'i' to ''
+compose '^' 'I' to ''
+compose '^' 'i' to ''
+compose '"' 'I' to ''
+compose '"' 'i' to ''
+compose '-' 'D' to ''
+compose '-' 'd' to ''
+compose '~' 'N' to ''
+compose '~' 'n' to ''
+compose '`' 'O' to ''
+compose '`' 'o' to ''
+compose '\'' 'O' to ''
+compose '\'' 'o' to ''
+compose '^' 'O' to ''
+compose '^' 'o' to ''
+compose '~' 'O' to ''
+compose '~' 'o' to ''
+compose '"' 'O' to ''
+compose '"' 'o' to ''
+compose '/' 'O' to ''
+compose '/' 'o' to ''
+compose '`' 'U' to ''
+compose '`' 'u' to ''
+compose '\'' 'U' to ''
+compose '\'' 'u' to ''
+compose '^' 'U' to ''
+compose '^' 'u' to ''
+compose '"' 'U' to ''
+compose '"' 'u' to ''
+compose '\'' 'Y' to ''
+compose '\'' 'y' to ''
+compose 'T' 'H' to ''
+compose 't' 'h' to ''
+compose 's' 's' to ''
+compose '"' 'y' to ''
+compose 's' 'z' to ''
+compose 'i' 'j' to ''
diff --git a/arch/m68k/hp300/ints.c b/arch/m68k/hp300/ints.c
new file mode 100644
index 00000000000..0c5bb403e89
--- /dev/null
+++ b/arch/m68k/hp300/ints.c
@@ -0,0 +1,175 @@
+/*
+ * linux/arch/m68k/hp300/ints.c
+ *
+ * Copyright (C) 1998 Philip Blundell <philb@gnu.org>
+ *
+ * This file contains the HP300-specific interrupt handling.
+ * We only use the autovector interrupts, and therefore we need to
+ * maintain lists of devices sharing each ipl.
+ * [ipl list code added by Peter Maydell <pmaydell@chiark.greenend.org.uk> 06/1998]
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/kernel_stat.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <asm/machdep.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/traps.h>
+#include <asm/ptrace.h>
+#include <asm/errno.h>
+#include "ints.h"
+
+/* Each ipl has a linked list of interrupt service routines.
+ * Service routines are added via hp300_request_irq() and removed
+ * via hp300_free_irq(). The device driver should set IRQ_FLG_FAST
+ * if it needs to be serviced early (eg FIFOless UARTs); this will
+ * cause it to be added at the front of the queue rather than
+ * the back.
+ * Currently IRQ_FLG_SLOW and flags=0 are treated identically; if
+ * we needed three levels of priority we could distinguish them
+ * but this strikes me as mildly ugly...
+ */
+
+/* we start with no entries in any list */
+static irq_node_t *hp300_irq_list[HP300_NUM_IRQS];
+
+static spinlock_t irqlist_lock;
+
+/* This handler receives all interrupts, dispatching them to the registered handlers */
+static irqreturn_t hp300_int_handler(int irq, void *dev_id, struct pt_regs *fp)
+{
+ irq_node_t *t;
+ /* We just give every handler on the chain an opportunity to handle
+ * the interrupt, in priority order.
+ */
+ for(t = hp300_irq_list[irq]; t; t=t->next)
+ t->handler(irq, t->dev_id, fp);
+ /* We could put in some accounting routines, checks for stray interrupts,
+ * etc, in here. Note that currently we can't tell whether or not
+ * a handler handles the interrupt, though.
+ */
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t hp300_badint(int irq, void *dev_id, struct pt_regs *fp)
+{
+ num_spurious += 1;
+ return IRQ_NONE;
+}
+
+irqreturn_t (*hp300_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = {
+ [0] = hp300_badint,
+ [1] = hp300_int_handler,
+ [2] = hp300_int_handler,
+ [3] = hp300_int_handler,
+ [4] = hp300_int_handler,
+ [5] = hp300_int_handler,
+ [6] = hp300_int_handler,
+ [7] = hp300_int_handler
+};
+
+/* dev_id had better be unique to each handler because it's the only way we have
+ * to distinguish handlers when removing them...
+ *
+ * It would be pretty easy to support IRQ_FLG_LOCK (handler is not replacable)
+ * and IRQ_FLG_REPLACE (handler replaces existing one with this dev_id)
+ * if we wanted to. IRQ_FLG_FAST is needed for devices where interrupt latency
+ * matters (eg the dreaded FIFOless UART...)
+ */
+int hp300_request_irq(unsigned int irq,
+ irqreturn_t (*handler) (int, void *, struct pt_regs *),
+ unsigned long flags, const char *devname, void *dev_id)
+{
+ irq_node_t *t, *n = new_irq_node();
+
+ if (!n) /* oops, no free nodes */
+ return -ENOMEM;
+
+ spin_lock_irqsave(&irqlist_lock, flags);
+
+ if (!hp300_irq_list[irq]) {
+ /* no list yet */
+ hp300_irq_list[irq] = n;
+ n->next = NULL;
+ } else if (flags & IRQ_FLG_FAST) {
+ /* insert at head of list */
+ n->next = hp300_irq_list[irq];
+ hp300_irq_list[irq] = n;
+ } else {
+ /* insert at end of list */
+ for(t = hp300_irq_list[irq]; t->next; t = t->next)
+ /* do nothing */;
+ n->next = NULL;
+ t->next = n;
+ }
+
+ /* Fill in n appropriately */
+ n->handler = handler;
+ n->flags = flags;
+ n->dev_id = dev_id;
+ n->devname = devname;
+ spin_unlock_irqrestore(&irqlist_lock, flags);
+ return 0;
+}
+
+void hp300_free_irq(unsigned int irq, void *dev_id)
+{
+ irq_node_t *t;
+ unsigned long flags;
+
+ spin_lock_irqsave(&irqlist_lock, flags);
+
+ t = hp300_irq_list[irq];
+ if (!t) /* no handlers at all for that IRQ */
+ {
+ printk(KERN_ERR "hp300_free_irq: attempt to remove nonexistent handler for IRQ %d\n", irq);
+ spin_unlock_irqrestore(&irqlist_lock, flags);
+ return;
+ }
+
+ if (t->dev_id == dev_id)
+ { /* removing first handler on chain */
+ t->flags = IRQ_FLG_STD; /* we probably don't really need these */
+ t->dev_id = NULL;
+ t->devname = NULL;
+ t->handler = NULL; /* frees this irq_node_t */
+ hp300_irq_list[irq] = t->next;
+ spin_unlock_irqrestore(&irqlist_lock, flags);
+ return;
+ }
+
+ /* OK, must be removing from middle of the chain */
+
+ for (t = hp300_irq_list[irq]; t->next && t->next->dev_id != dev_id; t = t->next)
+ /* do nothing */;
+ if (!t->next)
+ {
+ printk(KERN_ERR "hp300_free_irq: attempt to remove nonexistent handler for IRQ %d\n", irq);
+ spin_unlock_irqrestore(&irqlist_lock, flags);
+ return;
+ }
+ /* remove the entry after t: */
+ t->next->flags = IRQ_FLG_STD;
+ t->next->dev_id = NULL;
+ t->next->devname = NULL;
+ t->next->handler = NULL;
+ t->next = t->next->next;
+
+ spin_unlock_irqrestore(&irqlist_lock, flags);
+}
+
+int show_hp300_interrupts(struct seq_file *p, void *v)
+{
+ return 0;
+}
+
+void __init hp300_init_IRQ(void)
+{
+ spin_lock_init(&irqlist_lock);
+}
diff --git a/arch/m68k/hp300/ints.h b/arch/m68k/hp300/ints.h
new file mode 100644
index 00000000000..8cfabe2f384
--- /dev/null
+++ b/arch/m68k/hp300/ints.h
@@ -0,0 +1,9 @@
+extern void hp300_init_IRQ(void);
+extern void (*hp300_handlers[8])(int, void *, struct pt_regs *);
+extern void hp300_free_irq(unsigned int irq, void *dev_id);
+extern int hp300_request_irq(unsigned int irq,
+ irqreturn_t (*handler) (int, void *, struct pt_regs *),
+ unsigned long flags, const char *devname, void *dev_id);
+
+/* number of interrupts, includes 0 (what's that?) */
+#define HP300_NUM_IRQS 8
diff --git a/arch/m68k/hp300/ksyms.c b/arch/m68k/hp300/ksyms.c
new file mode 100644
index 00000000000..8202830763d
--- /dev/null
+++ b/arch/m68k/hp300/ksyms.c
@@ -0,0 +1,9 @@
+/*
+ * linux/arch/m68k/hp300/ksyms.c
+ *
+ * Copyright (C) 1998 Philip Blundell <philb@gnu.org>
+ *
+ * This file contains the HP300-specific kernel symbols. None yet. :-)
+ */
+
+#include <linux/module.h>
diff --git a/arch/m68k/hp300/reboot.S b/arch/m68k/hp300/reboot.S
new file mode 100644
index 00000000000..52eb852e6b0
--- /dev/null
+++ b/arch/m68k/hp300/reboot.S
@@ -0,0 +1,16 @@
+/*
+ * linux/arch/m68k/hp300/reboot.S
+ *
+ * Copyright (C) 1998 Philip Blundell <philb@gnu.org>
+ *
+ * Do the dirty work of rebooting the machine. Basically we need to undo all the
+ * good stuff that head.S did when we started up. The caches and MMU must be
+ * disabled and then we jump back to the PROM. This is a bit gruesome but we put
+ * a brave face on it.
+ */
+
+/* XXX Doesn't work yet. Not sure why and can't be bothered to fix it at the moment. */
+
+ .globl hp300_reset
+hp300_reset:
+ jmp hp300_reset
diff --git a/arch/m68k/hp300/time.c b/arch/m68k/hp300/time.c
new file mode 100644
index 00000000000..8da5b1b31e6
--- /dev/null
+++ b/arch/m68k/hp300/time.c
@@ -0,0 +1,78 @@
+/*
+ * 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/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/system.h>
+#include <asm/traps.h>
+#include <asm/blinken.h>
+#include "ints.h"
+
+/* Clock hardware definitions */
+
+#define CLOCKBASE 0xf05f8000
+
+#define CLKCR1 0x1
+#define CLKCR2 0x3
+#define CLKCR3 CLKCR1
+#define CLKSR CLKCR2
+#define CLKMSB1 0x5
+#define CLKMSB2 0x9
+#define CLKMSB3 0xD
+
+/* This is for machines which generate the exact clock. */
+#define USECS_PER_JIFFY (1000000/HZ)
+
+#define INTVAL ((10000 / 4) - 1)
+
+static irqreturn_t hp300_tick(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long tmp;
+ irqreturn_t (*vector)(int, void *, struct pt_regs *) = dev_id;
+ in_8(CLOCKBASE + CLKSR);
+ asm volatile ("movpw %1@(5),%0" : "=d" (tmp) : "a" (CLOCKBASE));
+ /* Turn off the network and SCSI leds */
+ blinken_leds(0, 0xe0);
+ return vector(irq, NULL, regs);
+}
+
+unsigned long hp300_gettimeoffset(void)
+{
+ /* Read current timer 1 value */
+ unsigned char lsb, msb1, msb2;
+ unsigned short ticks;
+
+ msb1 = in_8(CLOCKBASE + 5);
+ lsb = in_8(CLOCKBASE + 7);
+ msb2 = in_8(CLOCKBASE + 5);
+ if (msb1 != msb2)
+ /* A carry happened while we were reading. Read it again */
+ lsb = in_8(CLOCKBASE + 7);
+ ticks = INTVAL - ((msb2 << 8) | lsb);
+ return (USECS_PER_JIFFY * ticks) / INTVAL;
+}
+
+void __init hp300_sched_init(irqreturn_t (*vector)(int, void *, struct pt_regs *))
+{
+ out_8(CLOCKBASE + CLKCR2, 0x1); /* select CR1 */
+ out_8(CLOCKBASE + CLKCR1, 0x1); /* reset */
+
+ asm volatile(" movpw %0,%1@(5)" : : "d" (INTVAL), "a" (CLOCKBASE));
+
+ cpu_request_irq(6, hp300_tick, IRQ_FLG_STD, "timer tick", vector);
+
+ out_8(CLOCKBASE + CLKCR2, 0x1); /* select CR1 */
+ out_8(CLOCKBASE + CLKCR1, 0x40); /* enable irq */
+}
diff --git a/arch/m68k/hp300/time.h b/arch/m68k/hp300/time.h
new file mode 100644
index 00000000000..8ef9987b49a
--- /dev/null
+++ b/arch/m68k/hp300/time.h
@@ -0,0 +1,4 @@
+extern void hp300_sched_init(irqreturn_t (*vector)(int, void *, struct pt_regs *));
+extern unsigned long hp300_gettimeoffset (void);
+
+