Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/tc/Makefile b/drivers/tc/Makefile
new file mode 100644
index 0000000..83b5bd7
--- /dev/null
+++ b/drivers/tc/Makefile
@@ -0,0 +1,23 @@
+#
+# Makefile for the linux kernel.
+#
+
+# Object file lists.
+
+obj-$(CONFIG_TC) += tc.o
+obj-$(CONFIG_ZS) += zs.o
+obj-$(CONFIG_VT) += lk201.o lk201-map.o lk201-remap.o
+
+$(obj)/lk201-map.o: $(obj)/lk201-map.c
+
+# Uncomment if you're changing the keymap and have an appropriate
+# loadkeys version for the map. By default, we'll use the shipped
+# versions.
+# GENERATE_KEYMAP := 1
+
+ifdef GENERATE_KEYMAP
+
+$(obj)/lk201-map.c: $(obj)/%.c: $(src)/%.map
+	loadkeys --mktable $< > $@
+
+endif
diff --git a/drivers/tc/lk201-map.c_shipped b/drivers/tc/lk201-map.c_shipped
new file mode 100644
index 0000000..a9df8f5
--- /dev/null
+++ b/drivers/tc/lk201-map.c_shipped
@@ -0,0 +1,265 @@
+
+/* Do not edit this file! It was automatically generated by   */
+/*    loadkeys --mktable defkeymap.map > defkeymap.c          */
+
+#include <linux/types.h>
+#include <linux/keyboard.h>
+#include <linux/kd.h>
+
+u_short plain_map[NR_KEYS] = {
+	0xf200,	0xf100,	0xf101,	0xf102,	0xf103,	0xf104,	0xf105,	0xf106,
+	0xf107,	0xf108,	0xf109,	0xf10a,	0xf10b,	0xf10c,	0xf10d,	0xf11b,
+	0xf11c,	0xf110,	0xf111,	0xf112,	0xf113,	0xf060,	0xf031,	0xf032,
+	0xf033,	0xf034,	0xf035,	0xf036,	0xf037,	0xf038,	0xf039,	0xf030,
+	0xf02d,	0xf03d,	0xf07f,	0xf114,	0xf115,	0xf116,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf009,	0xfb71,	0xfb77,	0xfb65,	0xfb72,	0xfb74,
+	0xfb79,	0xfb75,	0xfb69,	0xfb6f,	0xfb70,	0xf05b,	0xf05d,	0xf201,
+	0xf117,	0xf118,	0xf119,	0xf307,	0xf308,	0xf309,	0xf30b,	0xf702,
+	0xf207,	0xfb61,	0xfb73,	0xfb64,	0xfb66,	0xfb67,	0xfb68,	0xfb6a,
+	0xfb6b,	0xfb6c,	0xf03b,	0xf027,	0xf05c,	0xf603,	0xf304,	0xf305,
+	0xf306,	0xf200,	0xf700,	0xf03e,	0xfb7a,	0xfb78,	0xfb63,	0xfb76,
+	0xfb62,	0xfb6e,	0xfb6d,	0xf02c,	0xf02e,	0xf02f,	0xf200,	0xf601,
+	0xf600,	0xf602,	0xf301,	0xf302,	0xf303,	0xf30e,	0xf200,	0xf703,
+	0xf020,	0xf200,	0xf200,	0xf300,	0xf310,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+};
+
+static u_short shift_map[NR_KEYS] = {
+	0xf200,	0xf100,	0xf101,	0xf102,	0xf103,	0xf104,	0xf105,	0xf106,
+	0xf107,	0xf108,	0xf109,	0xf10a,	0xf10b,	0xf10c,	0xf10d,	0xf203,
+	0xf11c,	0xf110,	0xf111,	0xf112,	0xf113,	0xf07e,	0xf021,	0xf040,
+	0xf023,	0xf024,	0xf025,	0xf05e,	0xf026,	0xf02a,	0xf028,	0xf029,
+	0xf05f,	0xf02b,	0xf07f,	0xf114,	0xf115,	0xf116,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf009,	0xfb51,	0xfb57,	0xfb45,	0xfb52,	0xfb54,
+	0xfb59,	0xfb55,	0xfb49,	0xfb4f,	0xfb50,	0xf07b,	0xf07d,	0xf201,
+	0xf117,	0xf20b,	0xf20a,	0xf307,	0xf308,	0xf309,	0xf30b,	0xf702,
+	0xf207,	0xfb41,	0xfb53,	0xfb44,	0xfb46,	0xfb47,	0xfb48,	0xfb4a,
+	0xfb4b,	0xfb4c,	0xf03a,	0xf022,	0xf07c,	0xf603,	0xf304,	0xf305,
+	0xf306,	0xf200,	0xf700,	0xf03c,	0xfb5a,	0xfb58,	0xfb43,	0xfb56,
+	0xfb42,	0xfb4e,	0xfb4d,	0xf03c,	0xf03e,	0xf03f,	0xf200,	0xf601,
+	0xf600,	0xf602,	0xf301,	0xf302,	0xf303,	0xf30e,	0xf200,	0xf703,
+	0xf020,	0xf200,	0xf200,	0xf300,	0xf310,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+};
+
+static u_short altgr_map[NR_KEYS] = {
+	0xf200,	0xf100,	0xf101,	0xf102,	0xf103,	0xf104,	0xf105,	0xf106,
+	0xf107,	0xf108,	0xf109,	0xf10a,	0xf10b,	0xf10c,	0xf10d,	0xf202,
+	0xf11c,	0xf110,	0xf111,	0xf112,	0xf113,	0xf200,	0xf200,	0xf040,
+	0xf200,	0xf024,	0xf200,	0xf200,	0xf07b,	0xf05b,	0xf05d,	0xf07d,
+	0xf05c,	0xf200,	0xf200,	0xf114,	0xf115,	0xf116,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xfb71,	0xfb77,	0xf918,	0xfb72,	0xfb74,
+	0xfb79,	0xfb75,	0xfb69,	0xfb6f,	0xfb70,	0xf200,	0xf07e,	0xf201,
+	0xf117,	0xf118,	0xf119,	0xf911,	0xf912,	0xf913,	0xf30b,	0xf702,
+	0xf207,	0xf914,	0xfb73,	0xf917,	0xf919,	0xfb67,	0xfb68,	0xfb6a,
+	0xfb6b,	0xfb6c,	0xf200,	0xf200,	0xf200,	0xf603,	0xf90e,	0xf90f,
+	0xf910,	0xf200,	0xf700,	0xf200,	0xfb7a,	0xfb78,	0xf916,	0xfb76,
+	0xf915,	0xfb6e,	0xfb6d,	0xf200,	0xf200,	0xf200,	0xf200,	0xf601,
+	0xf600,	0xf602,	0xf90b,	0xf90c,	0xf90d,	0xf30e,	0xf200,	0xf703,
+	0xf200,	0xf200,	0xf200,	0xf90a,	0xf310,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+};
+
+static u_short ctrl_map[NR_KEYS] = {
+	0xf200,	0xf100,	0xf101,	0xf102,	0xf103,	0xf104,	0xf105,	0xf106,
+	0xf107,	0xf108,	0xf109,	0xf10a,	0xf10b,	0xf10c,	0xf10d,	0xf204,
+	0xf11c,	0xf110,	0xf111,	0xf112,	0xf113,	0xf81b,	0xf200,	0xf000,
+	0xf01b,	0xf01c,	0xf01d,	0xf01e,	0xf01f,	0xf07f,	0xf200,	0xf200,
+	0xf01f,	0xf200,	0xf008,	0xf114,	0xf115,	0xf116,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf011,	0xf017,	0xf005,	0xf012,	0xf014,
+	0xf019,	0xf015,	0xf009,	0xf00f,	0xf010,	0xf01b,	0xf01d,	0xf201,
+	0xf117,	0xf118,	0xf119,	0xf307,	0xf308,	0xf309,	0xf30b,	0xf702,
+	0xf207,	0xf001,	0xf013,	0xf004,	0xf006,	0xf007,	0xf008,	0xf00a,
+	0xf00b,	0xf00c,	0xf200,	0xf007,	0xf01c,	0xf603,	0xf304,	0xf305,
+	0xf306,	0xf200,	0xf700,	0xf200,	0xf01a,	0xf018,	0xf003,	0xf016,
+	0xf002,	0xf00e,	0xf00d,	0xf200,	0xf20e,	0xf07f,	0xf200,	0xf601,
+	0xf600,	0xf602,	0xf301,	0xf302,	0xf303,	0xf30e,	0xf200,	0xf703,
+	0xf000,	0xf200,	0xf200,	0xf300,	0xf310,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+};
+
+static u_short shift_ctrl_map[NR_KEYS] = {
+	0xf200,	0xf100,	0xf101,	0xf102,	0xf103,	0xf104,	0xf105,	0xf106,
+	0xf107,	0xf108,	0xf109,	0xf10a,	0xf10b,	0xf10c,	0xf10d,	0xf200,
+	0xf11c,	0xf110,	0xf111,	0xf112,	0xf113,	0xf200,	0xf200,	0xf000,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf01f,	0xf200,	0xf200,	0xf114,	0xf115,	0xf116,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf011,	0xf017,	0xf005,	0xf012,	0xf014,
+	0xf019,	0xf015,	0xf009,	0xf00f,	0xf010,	0xf200,	0xf200,	0xf201,
+	0xf117,	0xf118,	0xf119,	0xf307,	0xf308,	0xf309,	0xf30b,	0xf702,
+	0xf207,	0xf001,	0xf013,	0xf004,	0xf006,	0xf007,	0xf008,	0xf00a,
+	0xf00b,	0xf00c,	0xf200,	0xf200,	0xf200,	0xf603,	0xf304,	0xf305,
+	0xf306,	0xf200,	0xf700,	0xf200,	0xf01a,	0xf018,	0xf003,	0xf016,
+	0xf002,	0xf00e,	0xf00d,	0xf200,	0xf200,	0xf200,	0xf200,	0xf601,
+	0xf600,	0xf602,	0xf301,	0xf302,	0xf303,	0xf30e,	0xf200,	0xf703,
+	0xf200,	0xf200,	0xf200,	0xf300,	0xf310,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+};
+
+static u_short alt_map[NR_KEYS] = {
+	0xf200,	0xf500,	0xf501,	0xf502,	0xf503,	0xf504,	0xf505,	0xf506,
+	0xf507,	0xf508,	0xf509,	0xf50a,	0xf50b,	0xf50c,	0xf50d,	0xf200,
+	0xf11c,	0xf510,	0xf511,	0xf512,	0xf513,	0xf01b,	0xf831,	0xf832,
+	0xf833,	0xf834,	0xf835,	0xf836,	0xf837,	0xf838,	0xf839,	0xf830,
+	0xf82d,	0xf83d,	0xf87f,	0xf114,	0xf115,	0xf116,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf809,	0xf871,	0xf877,	0xf865,	0xf872,	0xf874,
+	0xf879,	0xf875,	0xf869,	0xf86f,	0xf870,	0xf85b,	0xf85d,	0xf80d,
+	0xf117,	0xf118,	0xf119,	0xf907,	0xf908,	0xf909,	0xf30b,	0xf702,
+	0xf207,	0xf861,	0xf873,	0xf864,	0xf866,	0xf867,	0xf868,	0xf86a,
+	0xf86b,	0xf86c,	0xf83b,	0xf827,	0xf85c,	0xf603,	0xf904,	0xf905,
+	0xf906,	0xf200,	0xf700,	0xf200,	0xf87a,	0xf878,	0xf863,	0xf876,
+	0xf862,	0xf86e,	0xf86d,	0xf82c,	0xf82e,	0xf82f,	0xf200,	0xf210,
+	0xf600,	0xf211,	0xf901,	0xf902,	0xf903,	0xf30e,	0xf200,	0xf703,
+	0xf820,	0xf200,	0xf200,	0xf900,	0xf310,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+};
+
+static u_short ctrl_alt_map[NR_KEYS] = {
+	0xf200,	0xf500,	0xf501,	0xf502,	0xf503,	0xf504,	0xf505,	0xf506,
+	0xf507,	0xf508,	0xf509,	0xf50a,	0xf50b,	0xf50c,	0xf50d,	0xf200,
+	0xf11c,	0xf510,	0xf511,	0xf512,	0xf513,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf114,	0xf115,	0xf20c,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf811,	0xf817,	0xf805,	0xf812,	0xf814,
+	0xf819,	0xf815,	0xf809,	0xf80f,	0xf810,	0xf200,	0xf200,	0xf201,
+	0xf117,	0xf118,	0xf119,	0xf307,	0xf308,	0xf309,	0xf30b,	0xf702,
+	0xf207,	0xf801,	0xf813,	0xf804,	0xf806,	0xf807,	0xf808,	0xf80a,
+	0xf80b,	0xf80c,	0xf200,	0xf200,	0xf200,	0xf603,	0xf304,	0xf305,
+	0xf306,	0xf200,	0xf700,	0xf200,	0xf81a,	0xf818,	0xf803,	0xf816,
+	0xf802,	0xf80e,	0xf80d,	0xf200,	0xf200,	0xf200,	0xf200,	0xf601,
+	0xf600,	0xf602,	0xf301,	0xf302,	0xf303,	0xf30e,	0xf200,	0xf703,
+	0xf200,	0xf200,	0xf200,	0xf300,	0xf20c,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+};
+
+ushort *key_maps[MAX_NR_KEYMAPS] = {
+	plain_map, shift_map, altgr_map, 0,
+	ctrl_map, shift_ctrl_map, 0, 0,
+	alt_map, 0, 0, 0,
+	ctrl_alt_map,	0
+};
+
+unsigned int keymap_count = 7;
+
+
+/*
+ * Philosophy: most people do not define more strings, but they who do
+ * often want quite a lot of string space. So, we statically allocate
+ * the default and allocate dynamically in chunks of 512 bytes.
+ */
+
+char func_buf[] = {
+	'\033', '[', '[', 'A', 0, 
+	'\033', '[', '[', 'B', 0, 
+	'\033', '[', '[', 'C', 0, 
+	'\033', '[', '[', 'D', 0, 
+	'\033', '[', '[', 'E', 0, 
+	'\033', '[', '1', '7', '~', 0, 
+	'\033', '[', '1', '8', '~', 0, 
+	'\033', '[', '1', '9', '~', 0, 
+	'\033', '[', '2', '0', '~', 0, 
+	'\033', '[', '2', '1', '~', 0, 
+	'\033', '[', '2', '3', '~', 0, 
+	'\033', '[', '2', '4', '~', 0, 
+	'\033', '[', '2', '5', '~', 0, 
+	'\033', '[', '2', '6', '~', 0, 
+	'\033', '[', '2', '8', '~', 0, 
+	'\033', '[', '2', '9', '~', 0, 
+	'\033', '[', '3', '1', '~', 0, 
+	'\033', '[', '3', '2', '~', 0, 
+	'\033', '[', '3', '3', '~', 0, 
+	'\033', '[', '3', '4', '~', 0, 
+	'\033', '[', '1', '~', 0, 
+	'\033', '[', '2', '~', 0, 
+	'\033', '[', '3', '~', 0, 
+	'\033', '[', '4', '~', 0, 
+	'\033', '[', '5', '~', 0, 
+	'\033', '[', '6', '~', 0, 
+	'\033', '[', 'M', 0, 
+	'\033', '[', 'P', 0, 
+};
+
+
+char *funcbufptr = func_buf;
+int funcbufsize = sizeof(func_buf);
+int funcbufleft = 0;          /* space left */
+
+char *func_table[MAX_NR_FUNC] = {
+	func_buf + 0,
+	func_buf + 5,
+	func_buf + 10,
+	func_buf + 15,
+	func_buf + 20,
+	func_buf + 25,
+	func_buf + 31,
+	func_buf + 37,
+	func_buf + 43,
+	func_buf + 49,
+	func_buf + 55,
+	func_buf + 61,
+	func_buf + 67,
+	func_buf + 73,
+	func_buf + 79,
+	func_buf + 85,
+	func_buf + 91,
+	func_buf + 97,
+	func_buf + 103,
+	func_buf + 109,
+	func_buf + 115,
+	func_buf + 120,
+	func_buf + 125,
+	func_buf + 130,
+	func_buf + 135,
+	func_buf + 140,
+	func_buf + 145,
+	0,
+	0,
+	func_buf + 149,
+	0,
+};
+
+struct kbdiacr accent_table[MAX_DIACR] = {
+	{'`', 'A', 'À'},	{'`', 'a', 'à'},
+	{'\'', 'A', 'Á'},	{'\'', 'a', 'á'},
+	{'^', 'A', 'Â'},	{'^', 'a', 'â'},
+	{'~', 'A', 'Ã'},	{'~', 'a', 'ã'},
+	{'"', 'A', 'Ä'},	{'"', 'a', 'ä'},
+	{'O', 'A', 'Å'},	{'o', 'a', 'å'},
+	{'0', 'A', 'Å'},	{'0', 'a', 'å'},
+	{'A', 'A', 'Å'},	{'a', 'a', 'å'},
+	{'A', 'E', 'Æ'},	{'a', 'e', 'æ'},
+	{',', 'C', 'Ç'},	{',', 'c', 'ç'},
+	{'`', 'E', 'È'},	{'`', 'e', 'è'},
+	{'\'', 'E', 'É'},	{'\'', 'e', 'é'},
+	{'^', 'E', 'Ê'},	{'^', 'e', 'ê'},
+	{'"', 'E', 'Ë'},	{'"', 'e', 'ë'},
+	{'`', 'I', 'Ì'},	{'`', 'i', 'ì'},
+	{'\'', 'I', 'Í'},	{'\'', 'i', 'í'},
+	{'^', 'I', 'Î'},	{'^', 'i', 'î'},
+	{'"', 'I', 'Ï'},	{'"', 'i', 'ï'},
+	{'-', 'D', 'Ð'},	{'-', 'd', 'ð'},
+	{'~', 'N', 'Ñ'},	{'~', 'n', 'ñ'},
+	{'`', 'O', 'Ò'},	{'`', 'o', 'ò'},
+	{'\'', 'O', 'Ó'},	{'\'', 'o', 'ó'},
+	{'^', 'O', 'Ô'},	{'^', 'o', 'ô'},
+	{'~', 'O', 'Õ'},	{'~', 'o', 'õ'},
+	{'"', 'O', 'Ö'},	{'"', 'o', 'ö'},
+	{'/', 'O', 'Ø'},	{'/', 'o', 'ø'},
+	{'`', 'U', 'Ù'},	{'`', 'u', 'ù'},
+	{'\'', 'U', 'Ú'},	{'\'', 'u', 'ú'},
+	{'^', 'U', 'Û'},	{'^', 'u', 'û'},
+	{'"', 'U', 'Ü'},	{'"', 'u', 'ü'},
+	{'\'', 'Y', 'Ý'},	{'\'', 'y', 'ý'},
+	{'T', 'H', 'Þ'},	{'t', 'h', 'þ'},
+	{'s', 's', 'ß'},	{'"', 'y', 'ÿ'},
+	{'s', 'z', 'ß'},	{'i', 'j', 'ÿ'},
+};
+
+unsigned int accent_table_size = 68;
diff --git a/drivers/tc/lk201-map.map b/drivers/tc/lk201-map.map
new file mode 100644
index 0000000..2c636b4
--- /dev/null
+++ b/drivers/tc/lk201-map.map
@@ -0,0 +1,356 @@
+# Default 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   0x15 = grave		tilde
+	alt     keycode   0x15 = Escape
+	control keycode   0x15 = Meta_Escape
+keycode   0x16 = one              exclam
+	alt     keycode   0x16 = Meta_one
+keycode   0x17 = two              at               at
+	control	keycode   0x17 = nul
+	shift	control	keycode   0x17 = nul
+	alt	keycode   0x17 = Meta_two
+keycode   0x18 = three            numbersign
+	control keycode   0x18 = Escape
+	alt     keycode   0x18 = Meta_three
+keycode   0x19 = four             dollar           dollar
+	control keycode   0x19 = Control_backslash
+	alt     keycode   0x19 = Meta_four
+keycode   0x1a = five             percent
+	control keycode   0x1a = Control_bracketright
+	alt     keycode   0x1a = Meta_five
+keycode   0x1b = six              asciicircum
+	control keycode   0x1b = Control_asciicircum
+	alt     keycode   0x1b = Meta_six
+keycode   0x1c = seven            ampersand        braceleft
+	control keycode   0x1c = Control_underscore
+	alt     keycode   0x1c = Meta_seven
+keycode   0x1d = eight            asterisk         bracketleft
+	control keycode   0x1d = Delete
+	alt     keycode   0x1d = Meta_eight
+keycode  0x1e = nine             parenleft        bracketright
+	alt     keycode  0x1e = Meta_nine
+keycode  0x1f = zero             parenright       braceright
+	alt     keycode  0x1f = Meta_zero
+keycode  0x20 = minus            underscore       backslash
+	control	keycode  0x20 = Control_underscore
+	shift	control	keycode  0x20 = Control_underscore
+	alt	keycode  0x20 = Meta_minus
+keycode  0x21 = equal            plus
+	alt     keycode  0x21 = Meta_equal
+keycode  0x22 = Delete           Delete
+	control keycode  0x22 = BackSpace
+	alt     keycode  0x22 = Meta_Delete
+keycode  0x2a = Tab              Tab
+	alt     keycode  0x2a = Meta_Tab
+keycode  0x2b = q
+keycode  0x2c = w
+keycode  0x2d = e
+	altgr   keycode  0x2d = Hex_E
+keycode  0x2e = r
+keycode  0x2f = t
+keycode  0x30 = y
+keycode  0x31 = u
+keycode  0x32 = i
+keycode  0x33 = o
+keycode  0x34 = p
+keycode  0x35 = bracketleft      braceleft
+	control keycode  0x35 = Escape
+	alt     keycode  0x35 = Meta_bracketleft
+keycode  0x36 = bracketright     braceright       asciitilde
+	control keycode  0x36 = Control_bracketright
+	alt     keycode  0x36 = Meta_bracketright
+keycode  0x37 = Return
+	alt     keycode  0x37 = Meta_Control_m
+keycode  0x3f = Control
+keycode  0x41 = a
+	altgr   keycode  0x41 = Hex_A
+keycode  0x42 = s
+keycode  0x43 = d
+	altgr   keycode  0x43 = Hex_D
+keycode  0x44 = f
+	altgr   keycode  0x44 = Hex_F
+keycode  0x45 = g
+keycode  0x46 = h
+keycode  0x47 = j
+keycode  0x48 = k
+keycode  0x49 = l
+keycode  0x4a = semicolon        colon
+	alt     keycode  0x4a = Meta_semicolon
+keycode  0x4b = apostrophe       quotedbl
+	control keycode  0x4b = Control_g
+	alt     keycode  0x4b = Meta_apostrophe
+# keycode  41 = grave            asciitilde
+#	control keycode  41 = nul
+#	alt     keycode  41 = Meta_grave
+keycode  0x52 = Shift
+keycode  0x4c = backslash        bar
+	control keycode  0x4c = Control_backslash
+	alt     keycode  0x4c = Meta_backslash
+keycode  0x53 = greater	less
+keycode  0x54 = z
+keycode  0x55 = x
+keycode  0x56 = c
+	altgr   keycode  0x56 = Hex_C
+keycode  0x57 = v
+keycode  0x58 = b
+	altgr   keycode  0x58 = Hex_B
+keycode  0x59 = n
+keycode  0x5a = m
+keycode  0x5b = comma            less
+	alt     keycode  0x5b = Meta_comma
+keycode  0x5c = period           greater
+	control keycode  0x5c = Compose
+	alt     keycode  0x5c = Meta_period
+keycode  0x5d = slash            question
+	control keycode  0x5d = Delete
+	alt     keycode  0x5d = Meta_slash
+
+keycode  0x67 = Alt
+keycode  0x68 = space            space
+	control keycode  0x68 = nul
+	alt     keycode  0x68 = Meta_space
+keycode  0x40 = Caps_Lock
+keycode  0x01 = F1
+	control keycode  0x01 = F1
+	alt     keycode  0x01 = Console_1
+	control alt     keycode  0x01 = Console_1
+keycode  0x02 = F2
+	control keycode  0x02 = F2
+	alt     keycode  0x02 = Console_2
+	control alt     keycode  0x02 = Console_2
+keycode  0x03 = F3
+	control keycode  0x03 = F3
+	alt     keycode  0x03 = Console_3
+	control alt     keycode  0x03 = Console_3
+keycode  0x04 = F4
+	control keycode  0x04 = F4
+	alt     keycode  0x04 = Console_4
+	control alt     keycode  0x04 = Console_4
+keycode  0x05 = F5
+	control keycode  0x05 = F5
+	alt     keycode  0x05 = Console_5
+	control alt     keycode  0x05 = Console_5
+keycode  0x06 = F6
+	control keycode  0x06 = F6
+	alt     keycode  0x06 = Console_6
+	control alt     keycode  0x06 = Console_6
+keycode  0x07 = F7
+	control keycode  0x07 = F7
+	alt     keycode  0x07 = Console_7
+	control alt     keycode  0x07 = Console_7
+keycode  0x08 = F8
+	control keycode  0x08 = F8
+	alt     keycode  0x08 = Console_8
+	control alt     keycode  0x08 = Console_8
+keycode  0x09 = F9
+	control keycode  0x09 = F9
+	alt     keycode  0x09 = Console_9
+	control alt     keycode  0x09 = Console_9
+keycode  0x0a = F10
+	control keycode  0x0a = F10
+	alt     keycode  0x0a = Console_10
+	control alt     keycode  0x0a = Console_10
+keycode  0x0b = F11
+	control keycode  0x0b = F11
+	alt     keycode  0x0b = Console_11
+	control alt     keycode  0x0b = Console_11
+keycode  0x0c = F12
+	control keycode  0x0c = F12
+	alt     keycode  0x0c = Console_12
+	control alt     keycode  0x0c = Console_12
+keycode  0x0d = F13
+	control keycode  0x0d = F13
+	alt     keycode  0x0d = Console_13
+	control alt     keycode  0x0d = Console_13
+keycode  0x0e = F14
+	control keycode  0x0e = F14
+	alt     keycode  0x0e = Console_14
+	control alt     keycode  0x0e = Console_14
+
+keycode  0x11 = F17
+	control keycode  0x11 = F17
+	alt     keycode  0x11 = Console_17
+	control alt     keycode  0x11 = Console_17
+keycode  0x12 = F18
+	control keycode  0x12 = F18
+	alt     keycode  0x12 = Console_18
+	control alt     keycode  0x12 = Console_18
+keycode  0x13 = F19
+	control keycode  0x13 = F19
+	alt     keycode  0x13 = Console_19
+	control alt     keycode  0x13 = Console_19
+keycode  0x14 = F20
+	control keycode  0x14 = F20
+	alt     keycode  0x14 = Console_20
+	control alt     keycode  0x14 = Console_20
+
+
+keycode  0x3b = KP_7
+	alt     keycode  0x3b = Ascii_7
+	altgr   keycode  0x3b = Hex_7
+keycode  0x3c = KP_8
+	alt     keycode  0x3c = Ascii_8
+	altgr   keycode  0x3c = Hex_8
+keycode  0x3d = KP_9
+	alt     keycode  0x3d = Ascii_9
+	altgr   keycode  0x3d = Hex_9
+keycode  0x3e = KP_Subtract
+keycode  0x4e = KP_4
+	alt     keycode  0x4e = Ascii_4
+	altgr   keycode  0x4e = Hex_4
+keycode  0x4f = KP_5
+	alt     keycode  0x4f = Ascii_5
+	altgr   keycode  0x4f = Hex_5
+keycode  0x50 = KP_6
+	alt     keycode  0x50 = Ascii_6
+	altgr   keycode  0x50 = Hex_6
+keycode  0x62 = KP_1
+	alt     keycode  0x62 = Ascii_1
+	altgr   keycode  0x62 = Hex_1
+keycode  0x63 = KP_2
+	alt     keycode  0x63 = Ascii_2
+	altgr   keycode  0x63 = Hex_2
+keycode  0x64 = KP_3
+	alt     keycode  0x64 = Ascii_3
+	altgr   keycode  0x64 = Hex_3
+keycode  0x6b = KP_0
+	alt     keycode  0x6b = Ascii_0
+	altgr   keycode  0x6b = Hex_0
+keycode  0x6c = KP_Period
+#	altgr   control keycode  0x6c = Boot
+	control alt     keycode  0x6c = Boot
+keycode  0x65 = KP_Enter
+
+keycode  0x3f = Control
+
+# keycode 100 = AltGr
+
+keycode 0x23 = Find
+keycode 0x4d = Up
+keycode 0x39 = Prior
+	shift   keycode 0x39 = Scroll_Backward
+keycode 0x5f = Left
+	alt     keycode 0x5f = Decr_Console
+keycode 0x61 = Right
+	alt     keycode 0x61 = Incr_Console
+keycode 0x38 = Select
+keycode 0x60 = Down
+keycode 0x3a = Next
+	shift   keycode 0x3a = Scroll_Forward
+keycode 0x24 = Insert
+keycode 0x25 = Remove
+#	altgr   control keycode 0x25 = Boot
+	control alt     keycode 0x25 = Boot
+
+keycode 0x0f = Help      Show_Memory      Show_Registers
+	control keycode  0x0f = Show_State
+
+keycode 0x10 = Do
+
+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/drivers/tc/lk201-remap.c b/drivers/tc/lk201-remap.c
new file mode 100644
index 0000000..d39098c
--- /dev/null
+++ b/drivers/tc/lk201-remap.c
@@ -0,0 +1,172 @@
+/*
+ * Keyboard mappings for DEC LK201/401/501 keyboards
+ *
+ * 17.05.99 Michael Engel (engel@unix-ag.org)
+ *
+ * DEC US keyboards generate keycodes in the range 0x55 - 0xfb
+ *
+ * This conflicts with Linux scancode conventions which define
+ * 0x00-0x7f as "normal" and 0x80-0xff as "shifted" scancodes, so we
+ * have to remap the keycodes to 0x00-0x7f with the scancodeRemap
+ * array. The generated scancode is simply the number of the key counted
+ * from the left upper to the right lower corner of the keyboard ...
+ *
+ * These scancodes are then being remapped (I hope ;-)) with the
+ * lk501*map[] arrays which define scancode -> Linux code mapping
+ *
+ * Oh man is this horrible ;-)
+ *
+ * Scancodes with dual labels exist for keyboards as follows:
+ *
+ * code:  left label          / right label
+ *
+ * 0x73:  LKx01, LK421        / LK443, LK444
+ * 0x74:  LKx01, LK421        / LK443, LK444
+ * 0x7c:  LKx01, LK421        / LK443, LK444
+ * 0x8a:  LKx01, LK421        / LK443, LK444
+ * 0x8b:  LKx01, LK421        / LK443, LK444
+ * 0x8c:  LKx01, LK421        / LK443, LK444
+ * 0x8d:  LKx01, LK421        / LK443, LK444
+ * 0x8e:  LKx01, LK421        / LK443, LK444
+ * 0x8f:  LKx01, LK421        / LK443, LK444
+ * 0x9c:  LKx01, LK421        / LK443, LK444
+ * 0xa1:  LKx01, LK421        / LK443, LK444
+ * 0xa2:  LKx01, LK421        / LK443, LK444
+ * 0xa3:  LKx01, LK421        / LK443, LK444
+ * 0xa4:  LKx01, LK421        / LK443, LK444
+ * 0xad:         LK421        / LK443, LK444
+ * 0xc9:  LKx01, LK421, LK443 /        LK444
+ * 0xf7:  LKx01,        LK443 /        LK444
+ */
+
+unsigned char scancodeRemap[256] = {
+/* ----- 								*/
+/*  0 */ 0,		0,		0,		0,
+/* ----- 								*/
+/*  4 */ 0,		0,		0,		0,
+/* ----- 								*/
+/*  8 */ 0,		0,		0,		0,
+/* ----- 								*/
+/*  c */ 0,		0,		0,		0,
+/* ----- 								*/
+/* 10 */ 0,		0,		0,		0,
+/* ----- 								*/
+/* 14 */ 0,		0,		0,		0,
+/* ----- 								*/
+/* 18 */ 0,		0,		0,		0,
+/* ----- 								*/
+/* 1c */ 0,		0,		0,		0,
+/* ----- 								*/
+/* 20 */ 0,		0,		0,		0,
+/* ----- 								*/
+/* 24 */ 0,		0,		0,		0,
+/* ----- 								*/
+/* 28 */ 0,		0,		0,		0,
+/* ----- 								*/
+/* 2c */ 0,		0,		0,		0,
+/* ----- 								*/
+/* 30 */ 0,		0,		0,		0,
+/* ----- 								*/
+/* 34 */ 0,		0,		0,		0,
+/* ----- 								*/
+/* 38 */ 0,		0,		0,		0,
+/* ----- 								*/
+/* 3c */ 0,		0,		0,		0,
+/* ----- 								*/
+/* 40 */ 0,		0,		0,		0,
+/* ----- 								*/
+/* 44 */ 0,		0,		0,		0,
+/* ----- 								*/
+/* 48 */ 0,		0,		0,		0,
+/* ----- 								*/
+/* 4c */ 0,		0,		0,		0,
+/* ----- 								*/
+/* 50 */ 0,		0,		0,		0,
+/* ----- 	 	ESC		F1		F2 		*/
+/* 54 */ 0,		0,		0x01,  		0x02,
+/* ----- F3		F4		F5				*/
+/* 58 */ 0x03,  	0x04,		0x05,		0,
+/* ----- 								*/
+/* 5c */ 0,		0,		0,		0,
+/* ----- 								*/
+/* 60 */ 0,		0,		0,		0,
+/* ----- F6		F7		F8		F9		*/
+/* 64 */ 0x06,		0x07,		0x08,		0x09,
+/* ----- F10								*/
+/* 68 */ 0x0a,		0,		0,		0,
+/* ----- 								*/
+/* 6c */ 0,		0,		0,		0,
+/* ----- 		F11   		F12		F13/PRNT SCRN	*/
+/* 70 */ 0,		0x0b,  		0x0c,		0x0d,
+/* ----- F14/SCRL LCK							*/
+/* 74 */ 0x0e,		0,		0,		0,
+/* ----- 								*/
+/* 78 */ 0,		0,		0,		0,
+/* ----- HELP/PAUSE	DO						*/
+/* 7c */ 0x0f,		0x10,		0,		0,
+/* ----- F17		F18		F19		F20		*/
+/* 80 */ 0x11,		0x12,		0x13,		0x14,
+/* ----- 								*/
+/* 84 */ 0,		0,		0,		0,
+/* ----- 				FIND/INSERT	INSERT/HOME	*/
+/* 88 */ 0,		0,		0x23,		0x24,
+/* ----- REMOVE/PG UP	SELECT/DELETE	PREVIOUS/END	NEXT/PG DN	*/
+/* 8c */ 0x25,		0x38,		0x39,		0x3a,
+/* ----- 				KP 0				*/
+/* 90 */ 0,		0,		0x6b,		0,
+/* ----- KP .		KP ENTER	KP 1		KP 2		*/
+/* 94 */ 0x6c,		0x65,		0x62,		0x63,
+/* ----- KP 3		KP 4		KP 5		KP 6		*/
+/* 98 */ 0x64,		0x4e,		0x4f,		0x50,
+/* ----- KP ,/KP +	KP 7		KP 8		KP 9		*/
+/* 9c */ 0x51,		0x3b,		0x3c,		0x3d,
+/* ----- KP -		KP F1/NUM LCK	KP F2/KP /	KP F3/KP *	*/
+/* a0 */ 0x3e,		0x26,		0x27,		0x28,
+/* ----- KP F4/KP -					LEFT		*/
+/* a4 */ 0x29,		0,		0,		0x5f,
+/* ----- RIGHT		DOWN		UP		SHIFT Rt	*/
+/* a8 */ 0x61,		0x60, 		0x4d,		0x5e,
+/* ----- ALT		COMP Rt/CTRL Rt	SHIFT		CONTROL		*/
+/* ac */ 0,		0,		0x52,		0x3f,
+/* ----- CAPS		COMPOSE		ALT Rt				*/
+/* b0 */ 0x40,		0x67,		0,		0,
+/* ----- 								*/
+/* b4 */ 0,		0,		0,		0,
+/* ----- 								*/
+/* b8 */ 0,		0,		0,		0,
+/* ----- BKSP		RET		TAB		`		*/
+/* bc */ 0x22,		0x37,		0x2a,		0x15,
+/* ----- 1		q		a		z		*/
+/* c0 */ 0x16,		0x2b,		0x41,		0x54,
+/* ----- 		2		w		s		*/
+/* c4 */ 0,		0x17,		0x2c,		0x42,
+/* ----- x		</\\				3		*/
+/* c8 */ 0x55,		0x53,		0,		0x18,
+/* ----- e		d		c				*/
+/* cc */ 0x2d,		0x43,		0x56,		0,
+/* ----- 4		r		f		v		*/
+/* d0 */ 0x19,		0x2e,		0x44,		0x57,
+/* ----- SPACE				5		t		*/
+/* d4 */ 0x68,		0,		0x1a,		0x2f,
+/* ----- g		b				6		*/
+/* d8 */ 0x45,		0x58,		0,		0x1b,
+/* ----- y		h		n				*/
+/* dc */ 0x30,		0x46,		0x59,		0,
+/* ----- 7		u		j		m		*/
+/* e0 */ 0x1c,		0x31,		0x47,		0x5a,
+/* ----- 		8		i		k		*/
+/* e4 */ 0,		0x1d,		0x32,		0x48,
+/* ----- ,				9		o		*/
+/* e8 */ 0x5b,		0,		0x1e,		0x33,
+/* ----- l		.				0		*/
+/* ec */ 0x49,		0x5c,		0,		0x1f,
+/* ----- p				;		/		*/
+/* f0 */ 0x34,		0,		0x4a,		0x5d,
+/* ----- 		=		]		\\/\'		*/
+/* f4 */ 0,		0x21,		0x36,		0x4c,
+/* ----- 		-		[		\'		*/
+/* f8 */ 0,		0x20,		0x35,		0x4b,
+/* ----- 								*/
+/* fc */ 0,		0,		0,		0,
+};
+
diff --git a/drivers/tc/lk201.c b/drivers/tc/lk201.c
new file mode 100644
index 0000000..cf10d5c
--- /dev/null
+++ b/drivers/tc/lk201.c
@@ -0,0 +1,441 @@
+/*
+ *
+ * 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.
+ *
+ * Copyright (C) 1999-2002 Harald Koerfgen <hkoerfg@web.de>
+ * Copyright (C) 2001, 2002, 2003, 2004  Maciej W. Rozycki
+ */
+
+#include <linux/config.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kbd_ll.h>
+#include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
+
+#include <asm/keyboard.h>
+#include <asm/dec/tc.h>
+#include <asm/dec/machtype.h>
+#include <asm/dec/serial.h>
+
+#include "lk201.h"
+
+/*
+ * Only handle DECstations that have an LK201 interface.
+ * Maxine uses LK501 at the Access.Bus and various DECsystems
+ * have no keyboard interface at all.
+ */
+#define LK_IFACE	(mips_machtype == MACH_DS23100    || \
+			 mips_machtype == MACH_DS5000_200 || \
+			 mips_machtype == MACH_DS5000_1XX || \
+			 mips_machtype == MACH_DS5000_2X0)
+/*
+ * These use the Z8530 SCC.  Others use the DZ11.
+ */
+#define LK_IFACE_ZS	(mips_machtype == MACH_DS5000_1XX || \
+			 mips_machtype == MACH_DS5000_2X0)
+
+/* Simple translation table for the SysRq keys */
+
+#ifdef CONFIG_MAGIC_SYSRQ
+/*
+ * Actually no translation at all, at least until we figure out
+ * how to define SysRq for LK201 and friends. --macro
+ */
+unsigned char lk201_sysrq_xlate[128];
+unsigned char *kbd_sysrq_xlate = lk201_sysrq_xlate;
+
+unsigned char kbd_sysrq_key = -1;
+#endif
+
+#define KEYB_LINE	3
+
+static int __init lk201_init(void *);
+static void __init lk201_info(void *);
+static void lk201_rx_char(unsigned char, unsigned char);
+
+static struct dec_serial_hook lk201_hook = {
+	.init_channel	= lk201_init,
+	.init_info	= lk201_info,
+	.rx_char	= NULL,
+	.poll_rx_char	= NULL,
+	.poll_tx_char	= NULL,
+	.cflags		= B4800 | CS8 | CSTOPB | CLOCAL,
+};
+
+/*
+ * This is used during keyboard initialisation
+ */
+static unsigned char lk201_reset_string[] = {
+	LK_CMD_SET_DEFAULTS,
+	LK_CMD_MODE(LK_MODE_RPT_DOWN, 1),
+	LK_CMD_MODE(LK_MODE_RPT_DOWN, 2),
+	LK_CMD_MODE(LK_MODE_RPT_DOWN, 3),
+	LK_CMD_MODE(LK_MODE_RPT_DOWN, 4),
+	LK_CMD_MODE(LK_MODE_DOWN_UP, 5),
+	LK_CMD_MODE(LK_MODE_DOWN_UP, 6),
+	LK_CMD_MODE(LK_MODE_RPT_DOWN, 7),
+	LK_CMD_MODE(LK_MODE_RPT_DOWN, 8),
+	LK_CMD_MODE(LK_MODE_RPT_DOWN, 9),
+	LK_CMD_MODE(LK_MODE_RPT_DOWN, 10),
+	LK_CMD_MODE(LK_MODE_RPT_DOWN, 11),
+	LK_CMD_MODE(LK_MODE_RPT_DOWN, 12),
+	LK_CMD_MODE(LK_MODE_DOWN, 13),
+	LK_CMD_MODE(LK_MODE_RPT_DOWN, 14),
+	LK_CMD_DIS_KEYCLK,
+	LK_CMD_ENB_BELL, LK_PARAM_VOLUME(4),
+};
+
+static void *lk201_handle;
+
+static int lk201_send(unsigned char ch)
+{
+	if (lk201_hook.poll_tx_char(lk201_handle, ch)) {
+		printk(KERN_ERR "lk201: transmit timeout\n");
+		return -EIO;
+	}
+	return 0;
+}
+
+static inline int lk201_get_id(void)
+{
+	return lk201_send(LK_CMD_REQ_ID);
+}
+
+static int lk201_reset(void)
+{
+	int i, r;
+
+	for (i = 0; i < sizeof(lk201_reset_string); i++) {
+		r = lk201_send(lk201_reset_string[i]);
+		if (r < 0)
+			return r;
+	}
+	return 0;
+}
+
+static void lk201_report(unsigned char id[6])
+{
+	char *report = "lk201: keyboard attached, ";
+
+	switch (id[2]) {
+	case LK_STAT_PWRUP_OK:
+		printk(KERN_INFO "%sself-test OK\n", report);
+		break;
+	case LK_STAT_PWRUP_KDOWN:
+		/* The keyboard will resend the power-up ID
+		   after all keys are released, so we don't
+		   bother handling the error specially.  Still
+		   there may be a short-circuit inside.
+		 */
+		printk(KERN_ERR "%skey down (stuck?), code: 0x%02x\n",
+		       report, id[3]);
+		break;
+	case LK_STAT_PWRUP_ERROR:
+		printk(KERN_ERR "%sself-test failure\n", report);
+		break;
+	default:
+		printk(KERN_ERR "%sunknown error: 0x%02x\n",
+		       report, id[2]);
+	}
+}
+
+static void lk201_id(unsigned char id[6])
+{
+	/*
+	 * Report whether there is an LK201 or an LK401
+	 * The LK401 has ALT keys...
+	 */
+	switch (id[4]) {
+	case 1:
+		printk(KERN_INFO "lk201: LK201 detected\n");
+		break;
+	case 2:
+		printk(KERN_INFO "lk201: LK401 detected\n");
+		break;
+	case 3:
+		printk(KERN_INFO "lk201: LK443 detected\n");
+		break;
+	case 4:
+		printk(KERN_INFO "lk201: LK421 detected\n");
+		break;
+	default:
+		printk(KERN_WARNING
+		       "lk201: unknown keyboard detected, ID %d\n", id[4]);
+		printk(KERN_WARNING "lk201: ... please report to "
+		       "<linux-mips@linux-mips.org>\n");
+	}
+}
+
+#define DEFAULT_KEYB_REP_DELAY	(250/5)	/* [5ms] */
+#define DEFAULT_KEYB_REP_RATE	30	/* [cps] */
+
+static struct kbd_repeat kbdrate = {
+	DEFAULT_KEYB_REP_DELAY,
+	DEFAULT_KEYB_REP_RATE
+};
+
+static void parse_kbd_rate(struct kbd_repeat *r)
+{
+	if (r->delay <= 0)
+		r->delay = kbdrate.delay;
+	if (r->rate <= 0)
+		r->rate = kbdrate.rate;
+
+	if (r->delay < 5)
+		r->delay = 5;
+	if (r->delay > 630)
+		r->delay = 630;
+	if (r->rate < 12)
+		r->rate = 12;
+	if (r->rate > 127)
+		r->rate = 127;
+	if (r->rate == 125)
+		r->rate = 124;
+}
+
+static int write_kbd_rate(struct kbd_repeat *rep)
+{
+	int delay, rate;
+	int i;
+
+	delay = rep->delay / 5;
+	rate = rep->rate;
+	for (i = 0; i < 4; i++) {
+		if (lk201_hook.poll_tx_char(lk201_handle,
+					    LK_CMD_RPT_RATE(i)))
+			return 1;
+		if (lk201_hook.poll_tx_char(lk201_handle,
+					    LK_PARAM_DELAY(delay)))
+			return 1;
+		if (lk201_hook.poll_tx_char(lk201_handle,
+					    LK_PARAM_RATE(rate)))
+			return 1;
+	}
+	return 0;
+}
+
+static int lk201_kbd_rate(struct kbd_repeat *rep)
+{
+	if (rep == NULL)
+		return -EINVAL;
+
+	parse_kbd_rate(rep);
+
+	if (write_kbd_rate(rep)) {
+		memcpy(rep, &kbdrate, sizeof(struct kbd_repeat));
+		return -EIO;
+	}
+
+	memcpy(&kbdrate, rep, sizeof(struct kbd_repeat));
+
+	return 0;
+}
+
+static void lk201_kd_mksound(unsigned int hz, unsigned int ticks)
+{
+	if (!ticks)
+		return;
+
+	/*
+	 * Can't set frequency and we "approximate"
+	 * duration by volume. ;-)
+	 */
+	ticks /= HZ / 32;
+	if (ticks > 7)
+		ticks = 7;
+	ticks = 7 - ticks;
+
+	if (lk201_hook.poll_tx_char(lk201_handle, LK_CMD_ENB_BELL))
+		return;
+	if (lk201_hook.poll_tx_char(lk201_handle, LK_PARAM_VOLUME(ticks)))
+		return;
+	if (lk201_hook.poll_tx_char(lk201_handle, LK_CMD_BELL))
+		return;
+}
+
+void kbd_leds(unsigned char leds)
+{
+	unsigned char l = 0;
+
+	if (!lk201_handle)		/* FIXME */
+		return;
+
+	/* FIXME -- Only Hold and Lock LEDs for now. --macro */
+	if (leds & LED_SCR)
+		l |= LK_LED_HOLD;
+	if (leds & LED_CAP)
+		l |= LK_LED_LOCK;
+
+	if (lk201_hook.poll_tx_char(lk201_handle, LK_CMD_LEDS_ON))
+		return;
+	if (lk201_hook.poll_tx_char(lk201_handle, LK_PARAM_LED_MASK(l)))
+		return;
+	if (lk201_hook.poll_tx_char(lk201_handle, LK_CMD_LEDS_OFF))
+		return;
+	if (lk201_hook.poll_tx_char(lk201_handle, LK_PARAM_LED_MASK(~l)))
+		return;
+}
+
+int kbd_setkeycode(unsigned int scancode, unsigned int keycode)
+{
+	return -EINVAL;
+}
+
+int kbd_getkeycode(unsigned int scancode)
+{
+	return -EINVAL;
+}
+
+int kbd_translate(unsigned char scancode, unsigned char *keycode,
+		  char raw_mode)
+{
+	*keycode = scancode;
+	return 1;
+}
+
+char kbd_unexpected_up(unsigned char keycode)
+{
+	return 0x80;
+}
+
+static void lk201_rx_char(unsigned char ch, unsigned char fl)
+{
+	static unsigned char id[6];
+	static int id_i;
+
+	static int shift_state = 0;
+	static int prev_scancode;
+	unsigned char c = scancodeRemap[ch];
+
+	if (fl != TTY_NORMAL && fl != TTY_OVERRUN) {
+		printk(KERN_ERR "lk201: keyboard receive error: 0x%02x\n", fl);
+		return;
+	}
+
+	/* Assume this is a power-up ID. */
+	if (ch == LK_STAT_PWRUP_ID && !id_i) {
+		id[id_i++] = ch;
+		return;
+	}
+
+	/* Handle the power-up sequence. */
+	if (id_i) {
+		id[id_i++] = ch;
+		if (id_i == 4) {
+			/* OK, the power-up concluded. */
+			lk201_report(id);
+			if (id[2] == LK_STAT_PWRUP_OK)
+				lk201_get_id();
+			else {
+				id_i = 0;
+				printk(KERN_ERR "lk201: keyboard power-up "
+				       "error, skipping initialization\n");
+			}
+		} else if (id_i == 6) {
+			/* We got the ID; report it and start operation. */
+			id_i = 0;
+			lk201_id(id);
+			lk201_reset();
+		}
+		return;
+	}
+
+	/* Everything else is a scancode/status response. */
+	id_i = 0;
+	switch (ch) {
+	case LK_STAT_RESUME_ERR:
+	case LK_STAT_ERROR:
+	case LK_STAT_INHIBIT_ACK:
+	case LK_STAT_TEST_ACK:
+	case LK_STAT_MODE_KEYDOWN:
+	case LK_STAT_MODE_ACK:
+		break;
+	case LK_KEY_LOCK:
+		shift_state ^= LK_LOCK;
+		handle_scancode(c, (shift_state & LK_LOCK) ? 1 : 0);
+		break;
+	case LK_KEY_SHIFT:
+		shift_state ^= LK_SHIFT;
+		handle_scancode(c, (shift_state & LK_SHIFT) ? 1 : 0);
+		break;
+	case LK_KEY_CTRL:
+		shift_state ^= LK_CTRL;
+		handle_scancode(c, (shift_state & LK_CTRL) ? 1 : 0);
+		break;
+	case LK_KEY_COMP:
+		shift_state ^= LK_COMP;
+		handle_scancode(c, (shift_state & LK_COMP) ? 1 : 0);
+		break;
+	case LK_KEY_RELEASE:
+		if (shift_state & LK_SHIFT)
+			handle_scancode(scancodeRemap[LK_KEY_SHIFT], 0);
+		if (shift_state & LK_CTRL)
+			handle_scancode(scancodeRemap[LK_KEY_CTRL], 0);
+		if (shift_state & LK_COMP)
+			handle_scancode(scancodeRemap[LK_KEY_COMP], 0);
+		if (shift_state & LK_LOCK)
+			handle_scancode(scancodeRemap[LK_KEY_LOCK], 0);
+		shift_state = 0;
+		break;
+	case LK_KEY_REPEAT:
+		handle_scancode(prev_scancode, 1);
+		break;
+	default:
+		prev_scancode = c;
+		handle_scancode(c, 1);
+		break;
+	}
+	tasklet_schedule(&keyboard_tasklet);
+}
+
+static void __init lk201_info(void *handle)
+{
+}
+
+static int __init lk201_init(void *handle)
+{
+	/* First install handlers. */
+	lk201_handle = handle;
+	kbd_rate = lk201_kbd_rate;
+	kd_mksound = lk201_kd_mksound;
+
+	lk201_hook.rx_char = lk201_rx_char;
+
+	/* Then just issue a reset -- the handlers will do the rest. */
+	lk201_send(LK_CMD_POWER_UP);
+
+	return 0;
+}
+
+void __init kbd_init_hw(void)
+{
+	/* Maxine uses LK501 at the Access.Bus. */
+	if (!LK_IFACE)
+		return;
+
+	printk(KERN_INFO "lk201: DECstation LK keyboard driver v0.05.\n");
+
+	if (LK_IFACE_ZS) {
+		/*
+		 * kbd_init_hw() is being called before
+		 * rs_init() so just register the kbd hook
+		 * and let zs_init do the rest :-)
+		 */
+		if (!register_dec_serial_hook(KEYB_LINE, &lk201_hook))
+			unregister_dec_serial_hook(KEYB_LINE);
+	} else {
+		/*
+		 * TODO: modify dz.c to allow similar hooks
+		 * for LK201 handling on DS2100, DS3100, and DS5000/200
+		 */
+		printk(KERN_ERR "lk201: support for DZ11 not yet ready.\n");
+	}
+}
diff --git a/drivers/tc/lk201.h b/drivers/tc/lk201.h
new file mode 100644
index 0000000..99f3203
--- /dev/null
+++ b/drivers/tc/lk201.h
@@ -0,0 +1,125 @@
+/*
+ *	Commands to the keyboard processor
+ */
+
+#define LK_PARAM		0x80	/* start/end parameter list */
+
+#define LK_CMD_RESUME		0x8b	/* resume transmission to the host */
+#define LK_CMD_INHIBIT		0x89	/* stop transmission to the host */
+#define LK_CMD_LEDS_ON		0x13	/* light LEDs */
+					/* 1st param: led bitmask */
+#define LK_CMD_LEDS_OFF		0x11	/* turn off LEDs */
+					/* 1st param: led bitmask */
+#define LK_CMD_DIS_KEYCLK	0x99	/* disable the keyclick */
+#define LK_CMD_ENB_KEYCLK	0x1b	/* enable the keyclick */
+					/* 1st param: volume */
+#define LK_CMD_DIS_CTLCLK	0xb9	/* disable the Ctrl keyclick */
+#define LK_CMD_ENB_CTLCLK	0xbb	/* enable the Ctrl keyclick */
+#define LK_CMD_SOUND_CLK	0x9f	/* emit a keyclick */
+#define LK_CMD_DIS_BELL		0xa1	/* disable the bell */
+#define LK_CMD_ENB_BELL		0x23	/* enable the bell */
+					/* 1st param: volume */
+#define LK_CMD_BELL		0xa7	/* emit a bell */
+#define LK_CMD_TMP_NORPT	0xd1	/* disable typematic */
+					/* for the currently pressed key */
+#define LK_CMD_ENB_RPT		0xe3	/* enable typematic */
+					/* for RPT_DOWN groups */
+#define LK_CMD_DIS_RPT		0xe1	/* disable typematic */
+					/* for RPT_DOWN groups */
+#define LK_CMD_RPT_TO_DOWN	0xd9	/* set RPT_DOWN groups to DOWN */
+#define LK_CMD_REQ_ID		0xab	/* request the keyboard ID */
+#define LK_CMD_POWER_UP		0xfd	/* init power-up sequence */
+#define LK_CMD_TEST_MODE	0xcb	/* enter the factory test mode */
+#define LK_CMD_TEST_EXIT	0x80	/* exit the factory test mode */
+#define LK_CMD_SET_DEFAULTS	0xd3	/* set power-up defaults */
+
+#define LK_CMD_MODE(m,div)	(LK_PARAM|(((div)&0xf)<<3)|(((m)&0x3)<<1))
+					/* select the repeat mode */
+					/* for the selected key group */
+#define LK_CMD_MODE_AR(m,div)	((((div)&0xf)<<3)|(((m)&0x3)<<1))
+					/* select the repeat mode */
+					/* and the repeat register */
+					/* for the selected key group */
+					/* 1st param: register number */
+#define LK_CMD_RPT_RATE(r)	(0x78|(((r)&0x3)<<1))
+					/* set the delay and repeat rate */
+					/* for the selected repeat register */
+					/* 1st param: initial delay */
+					/* 2nd param: repeat rate */
+
+/* there are 4 leds, represent them in the low 4 bits of a byte */
+#define LK_PARAM_LED_MASK(ledbmap)	(LK_PARAM|((ledbmap)&0xf))
+#define LK_LED_WAIT		0x1	/* Wait LED */
+#define LK_LED_COMP		0x2	/* Compose LED */
+#define LK_LED_LOCK		0x4	/* Lock LED */
+#define LK_LED_HOLD		0x8	/* Hold Screen LED */
+
+/* max volume is 0, lowest is 0x7 */
+#define LK_PARAM_VOLUME(v)		(LK_PARAM|((v)&0x7))
+
+/* mode set command details, div is a key group number */
+#define LK_MODE_DOWN		0x0	/* make only */
+#define LK_MODE_RPT_DOWN	0x1	/* make and typematic */
+#define LK_MODE_DOWN_UP		0x3	/* make and release */
+
+/* there are 4 repeat registers */
+#define LK_PARAM_AR(r)		(LK_PARAM|((v)&0x3))
+
+/*
+ * Mappings between key groups and keycodes are as follows:
+ *
+ *  1: 0xbf - 0xff -- alphanumeric,
+ *  2: 0x91 - 0xa5 -- numeric keypad,
+ *  3: 0xbc        -- Backspace,
+ *  4: 0xbd - 0xbe -- Tab, Return,
+ *  5: 0xb0 - 0xb2 -- Lock, Compose Character,
+ *  6: 0xad - 0xaf -- Ctrl, Shift,
+ *  7: 0xa6 - 0xa8 -- Left Arrow, Right Arrow,
+ *  8: 0xa9 - 0xac -- Up Arrow, Down Arrow, Right Shift,
+ *  9: 0x88 - 0x90 -- editor keypad,
+ * 10: 0x56 - 0x62 -- F1 - F5,
+ * 11: 0x63 - 0x6e -- F6 - F10,
+ * 12: 0x6f - 0x7a -- F11 - F14,
+ * 13: 0x7b - 0x7d -- Help, Do,
+ * 14: 0x7e - 0x87 -- F17 - F20.
+ *
+ * Notes:
+ * 1. Codes in the 0x00 - 0x40 range are reserved.
+ * 2. The assignment of the 0x41 - 0x55 range is undiscovered, probably 10.
+ */
+
+/* delay is 5 - 630 ms; 0x00 and 0x7f are reserved */
+#define LK_PARAM_DELAY(t)	((t)&0x7f)
+
+/* rate is 12 - 127 Hz; 0x00 - 0x0b and 0x7d (power-up!) are reserved */
+#define LK_PARAM_RATE(r)	(LK_PARAM|((r)&0x7f))
+
+#define LK_SHIFT 1<<0
+#define LK_CTRL 1<<1
+#define LK_LOCK 1<<2
+#define LK_COMP 1<<3
+
+#define LK_KEY_SHIFT		0xae
+#define LK_KEY_CTRL		0xaf
+#define LK_KEY_LOCK		0xb0
+#define LK_KEY_COMP		0xb1
+
+#define LK_KEY_RELEASE		0xb3	/* all keys released */
+#define LK_KEY_REPEAT		0xb4	/* repeat the last key */
+
+/* status responses */
+#define LK_STAT_RESUME_ERR	0xb5	/* keystrokes lost while inhibited */
+#define LK_STAT_ERROR		0xb6	/* an invalid command received */
+#define LK_STAT_INHIBIT_ACK	0xb7	/* transmission inhibited */
+#define LK_STAT_TEST_ACK	0xb8	/* the factory test mode entered */
+#define LK_STAT_MODE_KEYDOWN	0xb9	/* a key is down on a change */
+					/* to the DOWN_UP mode; */
+					/* the keycode follows */
+#define LK_STAT_MODE_ACK	0xba	/* the mode command succeeded */
+
+#define LK_STAT_PWRUP_ID	0x01	/* the power-up response start mark */
+#define LK_STAT_PWRUP_OK	0x00	/* the power-up self test OK */
+#define LK_STAT_PWRUP_KDOWN	0x3d	/* a key was down during the test */
+#define LK_STAT_PWRUP_ERROR	0x3e	/* keyboard self test failure */
+
+extern unsigned char scancodeRemap[256];
diff --git a/drivers/tc/tc.c b/drivers/tc/tc.c
new file mode 100644
index 0000000..a89ef4d
--- /dev/null
+++ b/drivers/tc/tc.c
@@ -0,0 +1,260 @@
+/*
+ * tc-init: We assume the TURBOchannel to be up and running so
+ * just probe for Modules and fill in the global data structure
+ * tc_bus.
+ *
+ * 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.
+ *
+ * Copyright (c) Harald Koerfgen, 1998
+ * Copyright (c) 2001, 2003  Maciej W. Rozycki
+ */
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <asm/addrspace.h>
+#include <asm/errno.h>
+#include <asm/dec/machtype.h>
+#include <asm/dec/prom.h>
+#include <asm/dec/tcinfo.h>
+#include <asm/dec/tcmodule.h>
+#include <asm/dec/interrupts.h>
+#include <asm/paccess.h>
+#include <asm/ptrace.h>
+
+#define TC_DEBUG
+
+MODULE_LICENSE("GPL");
+slot_info tc_bus[MAX_SLOT];
+static int num_tcslots;
+static tcinfo *info;
+
+unsigned long system_base;
+
+/*
+ * Interface to the world. Read comment in include/asm-mips/tc.h.
+ */
+
+int search_tc_card(const char *name)
+{
+	int slot;
+	slot_info *sip;
+
+	for (slot = 0; slot < num_tcslots; slot++) {
+		sip = &tc_bus[slot];
+		if ((sip->flags & FREE) &&
+		    (strncmp(sip->name, name, strlen(name)) == 0)) {
+			return slot;
+		}
+	}
+
+	return -ENODEV;
+}
+
+void claim_tc_card(int slot)
+{
+	if (tc_bus[slot].flags & IN_USE) {
+		printk("claim_tc_card: attempting to claim a card already in use\n");
+		return;
+	}
+	tc_bus[slot].flags &= ~FREE;
+	tc_bus[slot].flags |= IN_USE;
+}
+
+void release_tc_card(int slot)
+{
+	if (tc_bus[slot].flags & FREE) {
+		printk("release_tc_card: "
+		       "attempting to release a card already free\n");
+		return;
+	}
+	tc_bus[slot].flags &= ~IN_USE;
+	tc_bus[slot].flags |= FREE;
+}
+
+unsigned long get_tc_base_addr(int slot)
+{
+	return tc_bus[slot].base_addr;
+}
+
+unsigned long get_tc_irq_nr(int slot)
+{
+	return tc_bus[slot].interrupt;
+}
+
+unsigned long get_tc_speed(void)
+{
+	return 100000 * (10000 / (unsigned long)info->clk_period);
+}
+
+/*
+ * Probing for TURBOchannel modules
+ */
+static void __init tc_probe(unsigned long startaddr, unsigned long size,
+			    int slots)
+{
+	int i, slot, err;
+	long offset;
+	unsigned char pattern[4];
+	unsigned char *module;
+
+	for (slot = 0; slot < slots; slot++) {
+		module = (char *)(startaddr + slot * size);
+
+		offset = OLDCARD;
+
+		err = 0;
+		err |= get_dbe(pattern[0], module + OLDCARD + TC_PATTERN0);
+		err |= get_dbe(pattern[1], module + OLDCARD + TC_PATTERN1);
+		err |= get_dbe(pattern[2], module + OLDCARD + TC_PATTERN2);
+		err |= get_dbe(pattern[3], module + OLDCARD + TC_PATTERN3);
+		if (err)
+			continue;
+
+		if (pattern[0] != 0x55 || pattern[1] != 0x00 ||
+		    pattern[2] != 0xaa || pattern[3] != 0xff) {
+			offset = NEWCARD;
+
+			err = 0;
+			err |= get_dbe(pattern[0], module + TC_PATTERN0);
+			err |= get_dbe(pattern[1], module + TC_PATTERN1);
+			err |= get_dbe(pattern[2], module + TC_PATTERN2);
+			err |= get_dbe(pattern[3], module + TC_PATTERN3);
+			if (err)
+				continue;
+		}
+
+		if (pattern[0] != 0x55 || pattern[1] != 0x00 ||
+		    pattern[2] != 0xaa || pattern[3] != 0xff)
+			continue;
+
+		tc_bus[slot].base_addr = (unsigned long)module;
+		for(i = 0; i < 8; i++) {
+			tc_bus[slot].firmware[i] =
+				module[TC_FIRM_VER + offset + 4 * i];
+			tc_bus[slot].vendor[i] =
+				module[TC_VENDOR + offset + 4 * i];
+			tc_bus[slot].name[i] =
+				module[TC_MODULE + offset + 4 * i];
+		}
+		tc_bus[slot].firmware[8] = 0;
+		tc_bus[slot].vendor[8] = 0;
+		tc_bus[slot].name[8] = 0;
+		/*
+		 * Looks unneccesary, but we may change
+		 * TC? in the future
+		 */
+		switch (slot) {
+		case 0:
+			tc_bus[slot].interrupt = dec_interrupt[DEC_IRQ_TC0];
+			break;
+		case 1:
+			tc_bus[slot].interrupt = dec_interrupt[DEC_IRQ_TC1];
+			break;
+		case 2:
+			tc_bus[slot].interrupt = dec_interrupt[DEC_IRQ_TC2];
+			break;
+		/*
+		 * Yuck! DS5000/200 onboard devices
+		 */
+		case 5:
+			tc_bus[slot].interrupt = dec_interrupt[DEC_IRQ_TC5];
+			break;
+		case 6:
+			tc_bus[slot].interrupt = dec_interrupt[DEC_IRQ_TC6];
+			break;
+		default:
+			tc_bus[slot].interrupt = -1;
+			break;
+		}
+	}
+}
+
+/*
+ * the main entry
+ */
+void __init tc_init(void)
+{
+	int tc_clock;
+	int i;
+	unsigned long slot0addr;
+	unsigned long slot_size;
+
+	if (!TURBOCHANNEL)
+		return;
+
+	for (i = 0; i < MAX_SLOT; i++) {
+		tc_bus[i].base_addr = 0;
+		tc_bus[i].name[0] = 0;
+		tc_bus[i].vendor[0] = 0;
+		tc_bus[i].firmware[0] = 0;
+		tc_bus[i].interrupt = -1;
+		tc_bus[i].flags = FREE;
+	}
+
+	info = (tcinfo *) rex_gettcinfo();
+	slot0addr = (unsigned long)KSEG1ADDR(rex_slot_address(0));
+
+	switch (mips_machtype) {
+	case MACH_DS5000_200:
+		num_tcslots = 7;
+		break;
+	case MACH_DS5000_1XX:
+	case MACH_DS5000_2X0:
+	case MACH_DS5900:
+		num_tcslots = 3;
+		break;
+	case MACH_DS5000_XX:
+	default:
+		num_tcslots = 2;
+		break;
+	}
+
+	tc_clock = 10000 / info->clk_period;
+
+	if (TURBOCHANNEL && info->slot_size && slot0addr) {
+		printk("TURBOchannel rev. %1d at %2d.%1d MHz ", info->revision,
+			tc_clock / 10, tc_clock % 10);
+		printk("(with%s parity)\n", info->parity ? "" : "out");
+
+		slot_size = info->slot_size << 20;
+
+		tc_probe(slot0addr, slot_size, num_tcslots);
+
+  		/*
+  		 * All TURBOchannel DECstations have the onboard devices
+ 		 * where the (num_tcslots + 0 or 1 on DS5k/xx) Option Module
+ 		 * would be.
+ 		 */
+ 		if(mips_machtype == MACH_DS5000_XX)
+ 			i = 1;
+		else
+ 			i = 0;
+
+ 	        system_base = slot0addr + slot_size * (num_tcslots + i);
+
+#ifdef TC_DEBUG
+		for (i = 0; i < num_tcslots; i++)
+			if (tc_bus[i].base_addr) {
+				printk("    slot %d: ", i);
+				printk("%s %s %s\n", tc_bus[i].vendor,
+					tc_bus[i].name, tc_bus[i].firmware);
+			}
+#endif
+		ioport_resource.end = KSEG2 - 1;
+	}
+}
+
+subsys_initcall(tc_init);
+
+EXPORT_SYMBOL(search_tc_card);
+EXPORT_SYMBOL(claim_tc_card);
+EXPORT_SYMBOL(release_tc_card);
+EXPORT_SYMBOL(get_tc_base_addr);
+EXPORT_SYMBOL(get_tc_irq_nr);
+EXPORT_SYMBOL(get_tc_speed);
+EXPORT_SYMBOL(system_base);
diff --git a/drivers/tc/zs.c b/drivers/tc/zs.c
new file mode 100644
index 0000000..4382ee6
--- /dev/null
+++ b/drivers/tc/zs.c
@@ -0,0 +1,2253 @@
+/*
+ * decserial.c: Serial port driver for IOASIC DECstations.
+ *
+ * Derived from drivers/sbus/char/sunserial.c by Paul Mackerras.
+ * Derived from drivers/macintosh/macserial.c by Harald Koerfgen.
+ *
+ * DECstation changes
+ * Copyright (C) 1998-2000 Harald Koerfgen
+ * Copyright (C) 2000, 2001, 2002, 2003, 2004  Maciej W. Rozycki
+ *
+ * For the rest of the code the original Copyright applies:
+ * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ *
+ * Note: for IOASIC systems the wiring is as follows:
+ *
+ * mouse/keyboard:
+ * DIN-7 MJ-4  signal        SCC
+ * 2     1     TxD       <-  A.TxD
+ * 3     4     RxD       ->  A.RxD
+ *
+ * EIA-232/EIA-423:
+ * DB-25 MMJ-6 signal        SCC
+ * 2     2     TxD       <-  B.TxD
+ * 3     5     RxD       ->  B.RxD
+ * 4           RTS       <- ~A.RTS
+ * 5           CTS       -> ~B.CTS
+ * 6     6     DSR       -> ~A.SYNC
+ * 8           CD        -> ~B.DCD
+ * 12          DSRS(DCE) -> ~A.CTS  (*)
+ * 15          TxC       ->  B.TxC
+ * 17          RxC       ->  B.RxC
+ * 20    1     DTR       <- ~A.DTR
+ * 22          RI        -> ~A.DCD
+ * 23          DSRS(DTE) <- ~B.RTS
+ *
+ * (*) EIA-232 defines the signal at this pin to be SCD, while DSRS(DCE)
+ *     is shared with DSRS(DTE) at pin 23.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#ifdef CONFIG_SERIAL_DEC_CONSOLE
+#include <linux/console.h>
+#endif
+
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/bootinfo.h>
+#include <asm/dec/serial.h>
+
+#ifdef CONFIG_MACH_DECSTATION
+#include <asm/dec/interrupts.h>
+#include <asm/dec/machtype.h>
+#include <asm/dec/tc.h>
+#include <asm/dec/ioasic_addrs.h>
+#endif
+#ifdef CONFIG_KGDB
+#include <asm/kgdb.h>
+#endif
+#ifdef CONFIG_MAGIC_SYSRQ
+#include <linux/sysrq.h>
+#endif
+
+#include "zs.h"
+
+/*
+ * It would be nice to dynamically allocate everything that
+ * depends on NUM_SERIAL, so we could support any number of
+ * Z8530s, but for now...
+ */
+#define NUM_SERIAL	2		/* Max number of ZS chips supported */
+#define NUM_CHANNELS	(NUM_SERIAL * 2)	/* 2 channels per chip */
+#define CHANNEL_A_NR  (zs_parms->channel_a_offset > zs_parms->channel_b_offset)
+                                        /* Number of channel A in the chip */
+#define ZS_CHAN_IO_SIZE 8
+#define ZS_CLOCK        7372800 	/* Z8530 RTxC input clock rate */
+
+#define RECOVERY_DELAY  udelay(2)
+
+struct zs_parms {
+	unsigned long scc0;
+	unsigned long scc1;
+	int channel_a_offset;
+	int channel_b_offset;
+	int irq0;
+	int irq1;
+	int clock;
+};
+
+static struct zs_parms *zs_parms;
+
+#ifdef CONFIG_MACH_DECSTATION
+static struct zs_parms ds_parms = {
+	scc0 : IOASIC_SCC0,
+	scc1 : IOASIC_SCC1,
+	channel_a_offset : 1,
+	channel_b_offset : 9,
+	irq0 : -1,
+	irq1 : -1,
+	clock : ZS_CLOCK
+};
+#endif
+
+#ifdef CONFIG_MACH_DECSTATION
+#define DS_BUS_PRESENT (IOASIC)
+#else
+#define DS_BUS_PRESENT 0
+#endif
+
+#define BUS_PRESENT (DS_BUS_PRESENT)
+
+struct dec_zschannel zs_channels[NUM_CHANNELS];
+struct dec_serial zs_soft[NUM_CHANNELS];
+int zs_channels_found;
+struct dec_serial *zs_chain;	/* list of all channels */
+
+struct tty_struct zs_ttys[NUM_CHANNELS];
+
+#ifdef CONFIG_SERIAL_DEC_CONSOLE
+static struct console sercons;
+#endif
+#if defined(CONFIG_SERIAL_DEC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) && \
+   !defined(MODULE)
+static unsigned long break_pressed; /* break, really ... */
+#endif
+
+static unsigned char zs_init_regs[16] __initdata = {
+	0,				/* write 0 */
+	0,				/* write 1 */
+	0,				/* write 2 */
+	0,				/* write 3 */
+	(X16CLK),			/* write 4 */
+	0,				/* write 5 */
+	0, 0, 0,			/* write 6, 7, 8 */
+	(MIE | DLC | NV),		/* write 9 */
+	(NRZ),				/* write 10 */
+	(TCBR | RCBR),			/* write 11 */
+	0, 0,				/* BRG time constant, write 12 + 13 */
+	(BRSRC | BRENABL),		/* write 14 */
+	0				/* write 15 */
+};
+
+DECLARE_TASK_QUEUE(tq_zs_serial);
+
+static struct tty_driver *serial_driver;
+
+/* serial subtype definitions */
+#define SERIAL_TYPE_NORMAL	1
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/*
+ * Debugging.
+ */
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_THROTTLE
+#undef SERIAL_PARANOIA_CHECK
+
+#undef ZS_DEBUG_REGS
+
+#ifdef SERIAL_DEBUG_THROTTLE
+#define _tty_name(tty,buf) tty_name(tty,buf)
+#endif
+
+#define RS_STROBE_TIME 10
+#define RS_ISR_PASS_LIMIT 256
+
+#define _INLINE_ inline
+
+static void probe_sccs(void);
+static void change_speed(struct dec_serial *info);
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write.  We need to
+ * lock it in case the copy_from_user blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char tmp_buf[4096]; /* This is cheating */
+static DECLARE_MUTEX(tmp_buf_sem);
+
+static inline int serial_paranoia_check(struct dec_serial *info,
+					char *name, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+	static const char *badmagic =
+		"Warning: bad magic number for serial struct %s in %s\n";
+	static const char *badinfo =
+		"Warning: null mac_serial for %s in %s\n";
+
+	if (!info) {
+		printk(badinfo, name, routine);
+		return 1;
+	}
+	if (info->magic != SERIAL_MAGIC) {
+		printk(badmagic, name, routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts
+ */
+static int baud_table[] = {
+	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+	9600, 19200, 38400, 57600, 115200, 0 };
+
+/*
+ * Reading and writing Z8530 registers.
+ */
+static inline unsigned char read_zsreg(struct dec_zschannel *channel,
+				       unsigned char reg)
+{
+	unsigned char retval;
+
+	if (reg != 0) {
+		*channel->control = reg & 0xf;
+		fast_iob(); RECOVERY_DELAY;
+	}
+	retval = *channel->control;
+	RECOVERY_DELAY;
+	return retval;
+}
+
+static inline void write_zsreg(struct dec_zschannel *channel,
+			       unsigned char reg, unsigned char value)
+{
+	if (reg != 0) {
+		*channel->control = reg & 0xf;
+		fast_iob(); RECOVERY_DELAY;
+	}
+	*channel->control = value;
+	fast_iob(); RECOVERY_DELAY;
+	return;
+}
+
+static inline unsigned char read_zsdata(struct dec_zschannel *channel)
+{
+	unsigned char retval;
+
+	retval = *channel->data;
+	RECOVERY_DELAY;
+	return retval;
+}
+
+static inline void write_zsdata(struct dec_zschannel *channel,
+				unsigned char value)
+{
+	*channel->data = value;
+	fast_iob(); RECOVERY_DELAY;
+	return;
+}
+
+static inline void load_zsregs(struct dec_zschannel *channel,
+			       unsigned char *regs)
+{
+/*	ZS_CLEARERR(channel);
+	ZS_CLEARFIFO(channel); */
+	/* Load 'em up */
+	write_zsreg(channel, R3, regs[R3] & ~RxENABLE);
+	write_zsreg(channel, R5, regs[R5] & ~TxENAB);
+	write_zsreg(channel, R4, regs[R4]);
+	write_zsreg(channel, R9, regs[R9]);
+	write_zsreg(channel, R1, regs[R1]);
+	write_zsreg(channel, R2, regs[R2]);
+	write_zsreg(channel, R10, regs[R10]);
+	write_zsreg(channel, R11, regs[R11]);
+	write_zsreg(channel, R12, regs[R12]);
+	write_zsreg(channel, R13, regs[R13]);
+	write_zsreg(channel, R14, regs[R14]);
+	write_zsreg(channel, R15, regs[R15]);
+	write_zsreg(channel, R3, regs[R3]);
+	write_zsreg(channel, R5, regs[R5]);
+	return;
+}
+
+/* Sets or clears DTR/RTS on the requested line */
+static inline void zs_rtsdtr(struct dec_serial *info, int which, int set)
+{
+        unsigned long flags;
+
+
+	save_flags(flags); cli();
+	if (info->zs_channel != info->zs_chan_a) {
+		if (set) {
+			info->zs_chan_a->curregs[5] |= (which & (RTS | DTR));
+		} else {
+			info->zs_chan_a->curregs[5] &= ~(which & (RTS | DTR));
+		}
+		write_zsreg(info->zs_chan_a, 5, info->zs_chan_a->curregs[5]);
+	}
+	restore_flags(flags);
+}
+
+/* Utility routines for the Zilog */
+static inline int get_zsbaud(struct dec_serial *ss)
+{
+	struct dec_zschannel *channel = ss->zs_channel;
+	int brg;
+
+	/* The baud rate is split up between two 8-bit registers in
+	 * what is termed 'BRG time constant' format in my docs for
+	 * the chip, it is a function of the clk rate the chip is
+	 * receiving which happens to be constant.
+	 */
+	brg = (read_zsreg(channel, 13) << 8);
+	brg |= read_zsreg(channel, 12);
+	return BRG_TO_BPS(brg, (zs_parms->clock/(ss->clk_divisor)));
+}
+
+/* On receive, this clears errors and the receiver interrupts */
+static inline void rs_recv_clear(struct dec_zschannel *zsc)
+{
+	write_zsreg(zsc, 0, ERR_RES);
+	write_zsreg(zsc, 0, RES_H_IUS); /* XXX this is unnecessary */
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines.  All of the following
+ * subroutines are declared as inline and are folded into
+ * rs_interrupt().  They were separated out for readability's sake.
+ *
+ * 				- Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static _INLINE_ void rs_sched_event(struct dec_serial *info,
+				  int event)
+{
+	info->event |= 1 << event;
+	queue_task(&info->tqueue, &tq_zs_serial);
+	mark_bh(SERIAL_BH);
+}
+
+static _INLINE_ void receive_chars(struct dec_serial *info,
+				   struct pt_regs *regs)
+{
+	struct tty_struct *tty = info->tty;
+	unsigned char ch, stat, flag;
+
+	while ((read_zsreg(info->zs_channel, R0) & Rx_CH_AV) != 0) {
+
+		stat = read_zsreg(info->zs_channel, R1);
+		ch = read_zsdata(info->zs_channel);
+
+		if (!tty && (!info->hook || !info->hook->rx_char))
+			continue;
+
+		flag = TTY_NORMAL;
+		if (info->tty_break) {
+			info->tty_break = 0;
+			flag = TTY_BREAK;
+			if (info->flags & ZILOG_SAK)
+				do_SAK(tty);
+			/* Ignore the null char got when BREAK is removed.  */
+			if (ch == 0)
+				continue;
+		} else {
+			if (stat & Rx_OVR) {
+				flag = TTY_OVERRUN;
+			} else if (stat & FRM_ERR) {
+				flag = TTY_FRAME;
+			} else if (stat & PAR_ERR) {
+				flag = TTY_PARITY;
+			}
+			if (flag != TTY_NORMAL)
+				/* reset the error indication */
+				write_zsreg(info->zs_channel, R0, ERR_RES);
+		}
+
+#if defined(CONFIG_SERIAL_DEC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) && \
+   !defined(MODULE)
+		if (break_pressed && info->line == sercons.index) {
+			/* Ignore the null char got when BREAK is removed.  */
+			if (ch == 0)
+				continue;
+			if (time_before(jiffies, break_pressed + HZ * 5)) {
+				handle_sysrq(ch, regs, NULL);
+				break_pressed = 0;
+				continue;
+			}
+			break_pressed = 0;
+		}
+#endif
+
+		if (info->hook && info->hook->rx_char) {
+			(*info->hook->rx_char)(ch, flag);
+			return;
+  		}
+
+		tty_insert_flip_char(tty, ch, flag);
+	}
+	if (tty)
+		tty_flip_buffer_push(tty);
+}
+
+static void transmit_chars(struct dec_serial *info)
+{
+	if ((read_zsreg(info->zs_channel, R0) & Tx_BUF_EMP) == 0)
+		return;
+	info->tx_active = 0;
+
+	if (info->x_char) {
+		/* Send next char */
+		write_zsdata(info->zs_channel, info->x_char);
+		info->x_char = 0;
+		info->tx_active = 1;
+		return;
+	}
+
+	if ((info->xmit_cnt <= 0) || (info->tty && info->tty->stopped)
+	    || info->tx_stopped) {
+		write_zsreg(info->zs_channel, R0, RES_Tx_P);
+		return;
+	}
+	/* Send char */
+	write_zsdata(info->zs_channel, info->xmit_buf[info->xmit_tail++]);
+	info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+	info->xmit_cnt--;
+	info->tx_active = 1;
+
+	if (info->xmit_cnt < WAKEUP_CHARS)
+		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+}
+
+static _INLINE_ void status_handle(struct dec_serial *info)
+{
+	unsigned char stat;
+
+	/* Get status from Read Register 0 */
+	stat = read_zsreg(info->zs_channel, R0);
+
+	if ((stat & BRK_ABRT) && !(info->read_reg_zero & BRK_ABRT)) {
+#if defined(CONFIG_SERIAL_DEC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) && \
+   !defined(MODULE)
+		if (info->line == sercons.index) {
+			if (!break_pressed)
+				break_pressed = jiffies;
+		} else
+#endif
+			info->tty_break = 1;
+	}
+
+	if (info->zs_channel != info->zs_chan_a) {
+
+		/* Check for DCD transitions */
+		if (info->tty && !C_CLOCAL(info->tty) &&
+		    ((stat ^ info->read_reg_zero) & DCD) != 0 ) {
+			if (stat & DCD) {
+				wake_up_interruptible(&info->open_wait);
+			} else {
+				tty_hangup(info->tty);
+			}
+		}
+
+		/* Check for CTS transitions */
+		if (info->tty && C_CRTSCTS(info->tty)) {
+			if ((stat & CTS) != 0) {
+				if (info->tx_stopped) {
+					info->tx_stopped = 0;
+					if (!info->tx_active)
+						transmit_chars(info);
+				}
+			} else {
+				info->tx_stopped = 1;
+			}
+		}
+
+	}
+
+	/* Clear status condition... */
+	write_zsreg(info->zs_channel, R0, RES_EXT_INT);
+	info->read_reg_zero = stat;
+}
+
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct dec_serial *info = (struct dec_serial *) dev_id;
+	unsigned char zs_intreg;
+	int shift;
+
+	/* NOTE: The read register 3, which holds the irq status,
+	 *       does so for both channels on each chip.  Although
+	 *       the status value itself must be read from the A
+	 *       channel and is only valid when read from channel A.
+	 *       Yes... broken hardware...
+	 */
+#define CHAN_IRQMASK (CHBRxIP | CHBTxIP | CHBEXT)
+
+	if (info->zs_chan_a == info->zs_channel)
+		shift = 3;	/* Channel A */
+	else
+		shift = 0;	/* Channel B */
+
+	for (;;) {
+		zs_intreg = read_zsreg(info->zs_chan_a, R3) >> shift;
+		if ((zs_intreg & CHAN_IRQMASK) == 0)
+			break;
+
+		if (zs_intreg & CHBRxIP) {
+			receive_chars(info, regs);
+		}
+		if (zs_intreg & CHBTxIP) {
+			transmit_chars(info);
+		}
+		if (zs_intreg & CHBEXT) {
+			status_handle(info);
+		}
+	}
+
+	/* Why do we need this ? */
+	write_zsreg(info->zs_channel, 0, RES_H_IUS);
+}
+
+#ifdef ZS_DEBUG_REGS
+void zs_dump (void) {
+	int i, j;
+	for (i = 0; i < zs_channels_found; i++) {
+		struct dec_zschannel *ch = &zs_channels[i];
+		if ((long)ch->control == UNI_IO_BASE+UNI_SCC1A_CTRL) {
+			for (j = 0; j < 15; j++) {
+				printk("W%d = 0x%x\t",
+				       j, (int)ch->curregs[j]);
+			}
+			for (j = 0; j < 15; j++) {
+				printk("R%d = 0x%x\t",
+				       j, (int)read_zsreg(ch,j));
+			}
+			printk("\n\n");
+		}
+	}
+}
+#endif
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * ------------------------------------------------------------
+ */
+static void rs_stop(struct tty_struct *tty)
+{
+	struct dec_serial *info = (struct dec_serial *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->name, "rs_stop"))
+		return;
+
+#if 1
+	save_flags(flags); cli();
+	if (info->zs_channel->curregs[5] & TxENAB) {
+		info->zs_channel->curregs[5] &= ~TxENAB;
+		write_zsreg(info->zs_channel, 5, info->zs_channel->curregs[5]);
+	}
+	restore_flags(flags);
+#endif
+}
+
+static void rs_start(struct tty_struct *tty)
+{
+	struct dec_serial *info = (struct dec_serial *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->name, "rs_start"))
+		return;
+
+	save_flags(flags); cli();
+#if 1
+	if (info->xmit_cnt && info->xmit_buf && !(info->zs_channel->curregs[5] & TxENAB)) {
+		info->zs_channel->curregs[5] |= TxENAB;
+		write_zsreg(info->zs_channel, 5, info->zs_channel->curregs[5]);
+	}
+#else
+	if (info->xmit_cnt && info->xmit_buf && !info->tx_active) {
+		transmit_chars(info);
+	}
+#endif
+	restore_flags(flags);
+}
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using rs_sched_event(), and they get done here.
+ */
+static void do_serial_bh(void)
+{
+	run_task_queue(&tq_zs_serial);
+}
+
+static void do_softint(void *private_)
+{
+	struct dec_serial	*info = (struct dec_serial *) private_;
+	struct tty_struct	*tty;
+
+	tty = info->tty;
+	if (!tty)
+		return;
+
+	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+		tty_wakeup(tty);
+	}
+}
+
+int zs_startup(struct dec_serial * info)
+{
+	unsigned long flags;
+
+	if (info->flags & ZILOG_INITIALIZED)
+		return 0;
+
+	if (!info->xmit_buf) {
+		info->xmit_buf = (unsigned char *) get_zeroed_page(GFP_KERNEL);
+		if (!info->xmit_buf)
+			return -ENOMEM;
+	}
+
+	save_flags(flags); cli();
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("starting up ttyS%d (irq %d)...", info->line, info->irq);
+#endif
+
+	/*
+	 * Clear the receive FIFO.
+	 */
+	ZS_CLEARFIFO(info->zs_channel);
+	info->xmit_fifo_size = 1;
+
+	/*
+	 * Clear the interrupt registers.
+	 */
+	write_zsreg(info->zs_channel, R0, ERR_RES);
+	write_zsreg(info->zs_channel, R0, RES_H_IUS);
+
+	/*
+	 * Set the speed of the serial port
+	 */
+	change_speed(info);
+
+	/*
+	 * Turn on RTS and DTR.
+	 */
+	zs_rtsdtr(info, RTS | DTR, 1);
+
+	/*
+	 * Finally, enable sequencing and interrupts
+	 */
+	info->zs_channel->curregs[R1] &= ~RxINT_MASK;
+	info->zs_channel->curregs[R1] |= (RxINT_ALL | TxINT_ENAB |
+					  EXT_INT_ENAB);
+	info->zs_channel->curregs[R3] |= RxENABLE;
+	info->zs_channel->curregs[R5] |= TxENAB;
+	info->zs_channel->curregs[R15] |= (DCDIE | CTSIE | TxUIE | BRKIE);
+	write_zsreg(info->zs_channel, R1, info->zs_channel->curregs[R1]);
+	write_zsreg(info->zs_channel, R3, info->zs_channel->curregs[R3]);
+	write_zsreg(info->zs_channel, R5, info->zs_channel->curregs[R5]);
+	write_zsreg(info->zs_channel, R15, info->zs_channel->curregs[R15]);
+
+	/*
+	 * And clear the interrupt registers again for luck.
+	 */
+	write_zsreg(info->zs_channel, R0, ERR_RES);
+	write_zsreg(info->zs_channel, R0, RES_H_IUS);
+
+	/* Save the current value of RR0 */
+	info->read_reg_zero = read_zsreg(info->zs_channel, R0);
+
+	if (info->tty)
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+	info->flags |= ZILOG_INITIALIZED;
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void shutdown(struct dec_serial * info)
+{
+	unsigned long	flags;
+
+	if (!(info->flags & ZILOG_INITIALIZED))
+		return;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("Shutting down serial port %d (irq %d)....", info->line,
+	       info->irq);
+#endif
+
+	save_flags(flags); cli(); /* Disable interrupts */
+
+	if (info->xmit_buf) {
+		free_page((unsigned long) info->xmit_buf);
+		info->xmit_buf = 0;
+	}
+
+	info->zs_channel->curregs[1] = 0;
+	write_zsreg(info->zs_channel, 1, info->zs_channel->curregs[1]);	/* no interrupts */
+
+	info->zs_channel->curregs[3] &= ~RxENABLE;
+	write_zsreg(info->zs_channel, 3, info->zs_channel->curregs[3]);
+
+	info->zs_channel->curregs[5] &= ~TxENAB;
+	write_zsreg(info->zs_channel, 5, info->zs_channel->curregs[5]);
+	if (!info->tty || C_HUPCL(info->tty)) {
+		zs_rtsdtr(info, RTS | DTR, 0);
+	}
+
+	if (info->tty)
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	info->flags &= ~ZILOG_INITIALIZED;
+	restore_flags(flags);
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct dec_serial *info)
+{
+	unsigned cflag;
+	int	i;
+	int	brg, bits;
+	unsigned long flags;
+
+	if (!info->hook) {
+		if (!info->tty || !info->tty->termios)
+			return;
+		cflag = info->tty->termios->c_cflag;
+		if (!info->port)
+			return;
+	} else {
+		cflag = info->hook->cflags;
+	}
+
+	i = cflag & CBAUD;
+	if (i & CBAUDEX) {
+		i &= ~CBAUDEX;
+		if (i < 1 || i > 2) {
+			if (!info->hook)
+				info->tty->termios->c_cflag &= ~CBAUDEX;
+			else
+				info->hook->cflags &= ~CBAUDEX;
+		} else
+			i += 15;
+	}
+
+	save_flags(flags); cli();
+	info->zs_baud = baud_table[i];
+	if (info->zs_baud) {
+		brg = BPS_TO_BRG(info->zs_baud, zs_parms->clock/info->clk_divisor);
+		info->zs_channel->curregs[12] = (brg & 255);
+		info->zs_channel->curregs[13] = ((brg >> 8) & 255);
+		zs_rtsdtr(info, DTR, 1);
+	} else {
+		zs_rtsdtr(info, RTS | DTR, 0);
+		return;
+	}
+
+	/* byte size and parity */
+	info->zs_channel->curregs[3] &= ~RxNBITS_MASK;
+	info->zs_channel->curregs[5] &= ~TxNBITS_MASK;
+	switch (cflag & CSIZE) {
+	case CS5:
+		bits = 7;
+		info->zs_channel->curregs[3] |= Rx5;
+		info->zs_channel->curregs[5] |= Tx5;
+		break;
+	case CS6:
+		bits = 8;
+		info->zs_channel->curregs[3] |= Rx6;
+		info->zs_channel->curregs[5] |= Tx6;
+		break;
+	case CS7:
+		bits = 9;
+		info->zs_channel->curregs[3] |= Rx7;
+		info->zs_channel->curregs[5] |= Tx7;
+		break;
+	case CS8:
+	default: /* defaults to 8 bits */
+		bits = 10;
+		info->zs_channel->curregs[3] |= Rx8;
+		info->zs_channel->curregs[5] |= Tx8;
+		break;
+	}
+
+	info->timeout = ((info->xmit_fifo_size*HZ*bits) / info->zs_baud);
+        info->timeout += HZ/50;         /* Add .02 seconds of slop */
+
+	info->zs_channel->curregs[4] &= ~(SB_MASK | PAR_ENA | PAR_EVEN);
+	if (cflag & CSTOPB) {
+		info->zs_channel->curregs[4] |= SB2;
+	} else {
+		info->zs_channel->curregs[4] |= SB1;
+	}
+	if (cflag & PARENB) {
+		info->zs_channel->curregs[4] |= PAR_ENA;
+	}
+	if (!(cflag & PARODD)) {
+		info->zs_channel->curregs[4] |= PAR_EVEN;
+	}
+
+	if (!(cflag & CLOCAL)) {
+		if (!(info->zs_channel->curregs[15] & DCDIE))
+			info->read_reg_zero = read_zsreg(info->zs_channel, 0);
+		info->zs_channel->curregs[15] |= DCDIE;
+	} else
+		info->zs_channel->curregs[15] &= ~DCDIE;
+	if (cflag & CRTSCTS) {
+		info->zs_channel->curregs[15] |= CTSIE;
+		if ((read_zsreg(info->zs_channel, 0) & CTS) == 0)
+			info->tx_stopped = 1;
+	} else {
+		info->zs_channel->curregs[15] &= ~CTSIE;
+		info->tx_stopped = 0;
+	}
+
+	/* Load up the new values */
+	load_zsregs(info->zs_channel, info->zs_channel->curregs);
+
+	restore_flags(flags);
+}
+
+static void rs_flush_chars(struct tty_struct *tty)
+{
+	struct dec_serial *info = (struct dec_serial *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->name, "rs_flush_chars"))
+		return;
+
+	if (info->xmit_cnt <= 0 || tty->stopped || info->tx_stopped ||
+	    !info->xmit_buf)
+		return;
+
+	/* Enable transmitter */
+	save_flags(flags); cli();
+	transmit_chars(info);
+	restore_flags(flags);
+}
+
+static int rs_write(struct tty_struct * tty,
+		    const unsigned char *buf, int count)
+{
+	int	c, total = 0;
+	struct dec_serial *info = (struct dec_serial *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->name, "rs_write"))
+		return 0;
+
+	if (!tty || !info->xmit_buf)
+		return 0;
+
+	save_flags(flags);
+	while (1) {
+		cli();
+		c = min(count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+				   SERIAL_XMIT_SIZE - info->xmit_head));
+		if (c <= 0)
+			break;
+
+		if (from_user) {
+			down(&tmp_buf_sem);
+			copy_from_user(tmp_buf, buf, c);
+			c = min(c, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+				       SERIAL_XMIT_SIZE - info->xmit_head));
+			memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+			up(&tmp_buf_sem);
+		} else
+			memcpy(info->xmit_buf + info->xmit_head, buf, c);
+		info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+		info->xmit_cnt += c;
+		restore_flags(flags);
+		buf += c;
+		count -= c;
+		total += c;
+	}
+
+	if (info->xmit_cnt && !tty->stopped && !info->tx_stopped
+	    && !info->tx_active)
+		transmit_chars(info);
+	restore_flags(flags);
+	return total;
+}
+
+static int rs_write_room(struct tty_struct *tty)
+{
+	struct dec_serial *info = (struct dec_serial *)tty->driver_data;
+	int	ret;
+
+	if (serial_paranoia_check(info, tty->name, "rs_write_room"))
+		return 0;
+	ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+	if (ret < 0)
+		ret = 0;
+	return ret;
+}
+
+static int rs_chars_in_buffer(struct tty_struct *tty)
+{
+	struct dec_serial *info = (struct dec_serial *)tty->driver_data;
+
+	if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer"))
+		return 0;
+	return info->xmit_cnt;
+}
+
+static void rs_flush_buffer(struct tty_struct *tty)
+{
+	struct dec_serial *info = (struct dec_serial *)tty->driver_data;
+
+	if (serial_paranoia_check(info, tty->name, "rs_flush_buffer"))
+		return;
+	cli();
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+	sti();
+	tty_wakeup(tty);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void rs_throttle(struct tty_struct * tty)
+{
+	struct dec_serial *info = (struct dec_serial *)tty->driver_data;
+	unsigned long flags;
+
+#ifdef SERIAL_DEBUG_THROTTLE
+	char	buf[64];
+
+	printk("throttle %s: %d....\n", _tty_name(tty, buf),
+	       tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (serial_paranoia_check(info, tty->name, "rs_throttle"))
+		return;
+
+	if (I_IXOFF(tty)) {
+		save_flags(flags); cli();
+		info->x_char = STOP_CHAR(tty);
+		if (!info->tx_active)
+			transmit_chars(info);
+		restore_flags(flags);
+	}
+
+	if (C_CRTSCTS(tty)) {
+		zs_rtsdtr(info, RTS, 0);
+	}
+}
+
+static void rs_unthrottle(struct tty_struct * tty)
+{
+	struct dec_serial *info = (struct dec_serial *)tty->driver_data;
+	unsigned long flags;
+
+#ifdef SERIAL_DEBUG_THROTTLE
+	char	buf[64];
+
+	printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
+	       tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (serial_paranoia_check(info, tty->name, "rs_unthrottle"))
+		return;
+
+	if (I_IXOFF(tty)) {
+		save_flags(flags); cli();
+		if (info->x_char)
+			info->x_char = 0;
+		else {
+			info->x_char = START_CHAR(tty);
+			if (!info->tx_active)
+				transmit_chars(info);
+		}
+		restore_flags(flags);
+	}
+
+	if (C_CRTSCTS(tty)) {
+		zs_rtsdtr(info, RTS, 1);
+	}
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int get_serial_info(struct dec_serial * info,
+			   struct serial_struct * retinfo)
+{
+	struct serial_struct tmp;
+
+	if (!retinfo)
+		return -EFAULT;
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.type = info->type;
+	tmp.line = info->line;
+	tmp.port = info->port;
+	tmp.irq = info->irq;
+	tmp.flags = info->flags;
+	tmp.baud_base = info->baud_base;
+	tmp.close_delay = info->close_delay;
+	tmp.closing_wait = info->closing_wait;
+	tmp.custom_divisor = info->custom_divisor;
+	return copy_to_user(retinfo,&tmp,sizeof(*retinfo)) ? -EFAULT : 0;
+}
+
+static int set_serial_info(struct dec_serial * info,
+			   struct serial_struct * new_info)
+{
+	struct serial_struct new_serial;
+	struct dec_serial old_info;
+	int 			retval = 0;
+
+	if (!new_info)
+		return -EFAULT;
+	copy_from_user(&new_serial,new_info,sizeof(new_serial));
+	old_info = *info;
+
+	if (!capable(CAP_SYS_ADMIN)) {
+		if ((new_serial.baud_base != info->baud_base) ||
+		    (new_serial.type != info->type) ||
+		    (new_serial.close_delay != info->close_delay) ||
+		    ((new_serial.flags & ~ZILOG_USR_MASK) !=
+		     (info->flags & ~ZILOG_USR_MASK)))
+			return -EPERM;
+		info->flags = ((info->flags & ~ZILOG_USR_MASK) |
+			       (new_serial.flags & ZILOG_USR_MASK));
+		info->custom_divisor = new_serial.custom_divisor;
+		goto check_and_exit;
+	}
+
+	if (info->count > 1)
+		return -EBUSY;
+
+	/*
+	 * OK, past this point, all the error checking has been done.
+	 * At this point, we start making changes.....
+	 */
+
+	info->baud_base = new_serial.baud_base;
+	info->flags = ((info->flags & ~ZILOG_FLAGS) |
+			(new_serial.flags & ZILOG_FLAGS));
+	info->type = new_serial.type;
+	info->close_delay = new_serial.close_delay;
+	info->closing_wait = new_serial.closing_wait;
+
+check_and_exit:
+	retval = zs_startup(info);
+	return retval;
+}
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * 	    is emptied.  On bus types like RS485, the transmitter must
+ * 	    release the bus after transmitting. This must be done when
+ * 	    the transmit shift register is empty, not be done when the
+ * 	    transmit holding register is empty.  This functionality
+ * 	    allows an RS485 driver to be written in user space.
+ */
+static int get_lsr_info(struct dec_serial * info, unsigned int *value)
+{
+	unsigned char status;
+
+	cli();
+	status = read_zsreg(info->zs_channel, 0);
+	sti();
+	put_user(status,value);
+	return 0;
+}
+
+static int rs_tiocmget(struct tty_struct *tty, struct file *file)
+{
+	struct dec_serial * info = (struct dec_serial *)tty->driver_data;
+	unsigned char control, status_a, status_b;
+	unsigned int result;
+
+	if (info->hook)
+		return -ENODEV;
+
+	if (serial_paranoia_check(info, tty->name, __FUNCTION__))
+		return -ENODEV;
+
+	if (tty->flags & (1 << TTY_IO_ERROR))
+		return -EIO;
+
+	if (info->zs_channel == info->zs_chan_a)
+		result = 0;
+	else {
+		cli();
+		control = info->zs_chan_a->curregs[5];
+		status_a = read_zsreg(info->zs_chan_a, 0);
+		status_b = read_zsreg(info->zs_channel, 0);
+		sti();
+		result =  ((control  & RTS) ? TIOCM_RTS: 0)
+			| ((control  & DTR) ? TIOCM_DTR: 0)
+			| ((status_b & DCD) ? TIOCM_CAR: 0)
+			| ((status_a & DCD) ? TIOCM_RNG: 0)
+			| ((status_a & SYNC_HUNT) ? TIOCM_DSR: 0)
+			| ((status_b & CTS) ? TIOCM_CTS: 0);
+	}
+	return result;
+}
+
+static int rs_tiocmset(struct tty_struct *tty, struct file *file,
+                       unsigned int set, unsigned int clear)
+{
+	struct dec_serial * info = (struct dec_serial *)tty->driver_data;
+	int error;
+	unsigned int arg, bits;
+
+	if (info->hook)
+		return -ENODEV;
+
+	if (serial_paranoia_check(info, tty->name, __FUNCTION__))
+		return -ENODEV;
+
+	if (tty->flags & (1 << TTY_IO_ERROR))
+		return -EIO;
+
+	if (info->zs_channel == info->zs_chan_a)
+		return 0;
+
+	get_user(arg, value);
+	cli();
+	if (set & TIOCM_RTS)
+		info->zs_chan_a->curregs[5] |= RTS;
+	if (set & TIOCM_DTR)
+		info->zs_chan_a->curregs[5] |= DTR;
+	if (clear & TIOCM_RTS)
+		info->zs_chan_a->curregs[5] &= ~RTS;
+	if (clear & TIOCM_DTR)
+		info->zs_chan_a->curregs[5] &= ~DTR;
+	write_zsreg(info->zs_chan_a, 5, info->zs_chan_a->curregs[5]);
+	sti();
+	return 0;
+}
+
+/*
+ * rs_break - turn transmit break condition on/off
+ */
+static void rs_break(struct tty_struct *tty, int break_state)
+{
+	struct dec_serial *info = (struct dec_serial *) tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->name, "rs_break"))
+		return;
+	if (!info->port)
+		return;
+
+	save_flags(flags); cli();
+	if (break_state == -1)
+		info->zs_channel->curregs[5] |= SND_BRK;
+	else
+		info->zs_channel->curregs[5] &= ~SND_BRK;
+	write_zsreg(info->zs_channel, 5, info->zs_channel->curregs[5]);
+	restore_flags(flags);
+}
+
+static int rs_ioctl(struct tty_struct *tty, struct file * file,
+		    unsigned int cmd, unsigned long arg)
+{
+	int error;
+	struct dec_serial * info = (struct dec_serial *)tty->driver_data;
+
+	if (info->hook)
+		return -ENODEV;
+
+	if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
+		return -ENODEV;
+
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD)  &&
+	    (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
+		if (tty->flags & (1 << TTY_IO_ERROR))
+		    return -EIO;
+	}
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		if (!access_ok(VERIFY_WRITE, (void *)arg,
+			       sizeof(struct serial_struct)))
+			return -EFAULT;
+		return get_serial_info(info, (struct serial_struct *)arg);
+
+	case TIOCSSERIAL:
+		return set_serial_info(info, (struct serial_struct *)arg);
+
+	case TIOCSERGETLSR:			/* Get line status register */
+		if (!access_ok(VERIFY_WRITE, (void *)arg,
+			       sizeof(unsigned int)))
+			return -EFAULT;
+		return get_lsr_info(info, (unsigned int *)arg);
+
+	case TIOCSERGSTRUCT:
+		if (!access_ok(VERIFY_WRITE, (void *)arg,
+			       sizeof(struct dec_serial)))
+			return -EFAULT;
+		copy_from_user((struct dec_serial *)arg, info,
+			       sizeof(struct dec_serial));
+		return 0;
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	struct dec_serial *info = (struct dec_serial *)tty->driver_data;
+	int was_stopped;
+
+	if (tty->termios->c_cflag == old_termios->c_cflag)
+		return;
+	was_stopped = info->tx_stopped;
+
+	change_speed(info);
+
+	if (was_stopped && !info->tx_stopped)
+		rs_start(tty);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_close()
+ *
+ * This routine is called when the serial port gets closed.
+ * Wait for the last remaining data to be sent.
+ * ------------------------------------------------------------
+ */
+static void rs_close(struct tty_struct *tty, struct file * filp)
+{
+	struct dec_serial * info = (struct dec_serial *)tty->driver_data;
+	unsigned long flags;
+
+	if (!info || serial_paranoia_check(info, tty->name, "rs_close"))
+		return;
+
+	save_flags(flags); cli();
+
+	if (tty_hung_up_p(filp)) {
+		restore_flags(flags);
+		return;
+	}
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("rs_close ttyS%d, count = %d\n", info->line, info->count);
+#endif
+	if ((tty->count == 1) && (info->count != 1)) {
+		/*
+		 * Uh, oh.  tty->count is 1, which means that the tty
+		 * structure will be freed.  Info->count should always
+		 * be one in these conditions.  If it's greater than
+		 * one, we've got real problems, since it means the
+		 * serial port won't be shutdown.
+		 */
+		printk("rs_close: bad serial port count; tty->count is 1, "
+		       "info->count is %d\n", info->count);
+		info->count = 1;
+	}
+	if (--info->count < 0) {
+		printk("rs_close: bad serial port count for ttyS%d: %d\n",
+		       info->line, info->count);
+		info->count = 0;
+	}
+	if (info->count) {
+		restore_flags(flags);
+		return;
+	}
+	info->flags |= ZILOG_CLOSING;
+	/*
+	 * Now we wait for the transmit buffer to clear; and we notify
+	 * the line discipline to only process XON/XOFF characters.
+	 */
+	tty->closing = 1;
+	if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE)
+		tty_wait_until_sent(tty, info->closing_wait);
+	/*
+	 * At this point we stop accepting input.  To do this, we
+	 * disable the receiver and receive interrupts.
+	 */
+	info->zs_channel->curregs[3] &= ~RxENABLE;
+	write_zsreg(info->zs_channel, 3, info->zs_channel->curregs[3]);
+	info->zs_channel->curregs[1] = 0;	/* disable any rx ints */
+	write_zsreg(info->zs_channel, 1, info->zs_channel->curregs[1]);
+	ZS_CLEARFIFO(info->zs_channel);
+	if (info->flags & ZILOG_INITIALIZED) {
+		/*
+		 * Before we drop DTR, make sure the SCC transmitter
+		 * has completely drained.
+		 */
+		rs_wait_until_sent(tty, info->timeout);
+	}
+
+	shutdown(info);
+	if (tty->driver->flush_buffer)
+		tty->driver->flush_buffer(tty);
+	tty_ldisc_flush(tty);
+	tty->closing = 0;
+	info->event = 0;
+	info->tty = 0;
+	if (info->blocked_open) {
+		if (info->close_delay) {
+			msleep_interruptible(jiffies_to_msecs(info->close_delay));
+		}
+		wake_up_interruptible(&info->open_wait);
+	}
+	info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CLOSING);
+	wake_up_interruptible(&info->close_wait);
+	restore_flags(flags);
+}
+
+/*
+ * rs_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+	struct dec_serial *info = (struct dec_serial *) tty->driver_data;
+	unsigned long orig_jiffies;
+	int char_time;
+
+	if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent"))
+		return;
+
+	orig_jiffies = jiffies;
+	/*
+	 * Set the check interval to be 1/5 of the estimated time to
+	 * send a single character, and make it at least 1.  The check
+	 * interval should also be less than the timeout.
+	 */
+	char_time = (info->timeout - HZ/50) / info->xmit_fifo_size;
+	char_time = char_time / 5;
+	if (char_time == 0)
+		char_time = 1;
+	if (timeout)
+		char_time = min(char_time, timeout);
+	while ((read_zsreg(info->zs_channel, 1) & Tx_BUF_EMP) == 0) {
+		msleep_interruptible(jiffies_to_msecs(char_time));
+		if (signal_pending(current))
+			break;
+		if (timeout && time_after(jiffies, orig_jiffies + timeout))
+			break;
+	}
+	current->state = TASK_RUNNING;
+}
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+void rs_hangup(struct tty_struct *tty)
+{
+	struct dec_serial * info = (struct dec_serial *)tty->driver_data;
+
+	if (serial_paranoia_check(info, tty->name, "rs_hangup"))
+		return;
+
+	rs_flush_buffer(tty);
+	shutdown(info);
+	info->event = 0;
+	info->count = 0;
+	info->flags &= ~ZILOG_NORMAL_ACTIVE;
+	info->tty = 0;
+	wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_open() and friends
+ * ------------------------------------------------------------
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+			   struct dec_serial *info)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	int		retval;
+	int		do_clocal = 0;
+
+	/*
+	 * If the device is in the middle of being closed, then block
+	 * until it's done, and then try again.
+	 */
+	if (info->flags & ZILOG_CLOSING) {
+		interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+		return ((info->flags & ZILOG_HUP_NOTIFY) ?
+			-EAGAIN : -ERESTARTSYS);
+#else
+		return -EAGAIN;
+#endif
+	}
+
+	/*
+	 * If non-blocking mode is set, or the port is not enabled,
+	 * then make the check up front and then exit.
+	 */
+	if ((filp->f_flags & O_NONBLOCK) ||
+	    (tty->flags & (1 << TTY_IO_ERROR))) {
+		info->flags |= ZILOG_NORMAL_ACTIVE;
+		return 0;
+	}
+
+	if (tty->termios->c_cflag & CLOCAL)
+		do_clocal = 1;
+
+	/*
+	 * Block waiting for the carrier detect and the line to become
+	 * free (i.e., not in use by the callout).  While we are in
+	 * this loop, info->count is dropped by one, so that
+	 * rs_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	retval = 0;
+	add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+	printk("block_til_ready before block: ttyS%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	cli();
+	if (!tty_hung_up_p(filp))
+		info->count--;
+	sti();
+	info->blocked_open++;
+	while (1) {
+		cli();
+		if (tty->termios->c_cflag & CBAUD)
+			zs_rtsdtr(info, RTS | DTR, 1);
+		sti();
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (tty_hung_up_p(filp) ||
+		    !(info->flags & ZILOG_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+			if (info->flags & ZILOG_HUP_NOTIFY)
+				retval = -EAGAIN;
+			else
+				retval = -ERESTARTSYS;
+#else
+			retval = -EAGAIN;
+#endif
+			break;
+		}
+		if (!(info->flags & ZILOG_CLOSING) &&
+		    (do_clocal || (read_zsreg(info->zs_channel, 0) & DCD)))
+			break;
+		if (signal_pending(current)) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+#ifdef SERIAL_DEBUG_OPEN
+		printk("block_til_ready blocking: ttyS%d, count = %d\n",
+		       info->line, info->count);
+#endif
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&info->open_wait, &wait);
+	if (!tty_hung_up_p(filp))
+		info->count++;
+	info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+	printk("block_til_ready after blocking: ttyS%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	if (retval)
+		return retval;
+	info->flags |= ZILOG_NORMAL_ACTIVE;
+	return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * enables interrupts for a serial port, linking in its ZILOG structure into
+ * the IRQ chain.   It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+int rs_open(struct tty_struct *tty, struct file * filp)
+{
+	struct dec_serial	*info;
+	int 			retval, line;
+
+	line = tty->index;
+	if ((line < 0) || (line >= zs_channels_found))
+		return -ENODEV;
+	info = zs_soft + line;
+
+	if (info->hook)
+		return -ENODEV;
+
+	if (serial_paranoia_check(info, tty->name, "rs_open"))
+		return -ENODEV;
+#ifdef SERIAL_DEBUG_OPEN
+	printk("rs_open %s, count = %d\n", tty->name, info->count);
+#endif
+
+	info->count++;
+	tty->driver_data = info;
+	info->tty = tty;
+
+	/*
+	 * If the port is the middle of closing, bail out now
+	 */
+	if (tty_hung_up_p(filp) ||
+	    (info->flags & ZILOG_CLOSING)) {
+		if (info->flags & ZILOG_CLOSING)
+			interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+		return ((info->flags & ZILOG_HUP_NOTIFY) ?
+			-EAGAIN : -ERESTARTSYS);
+#else
+		return -EAGAIN;
+#endif
+	}
+
+	/*
+	 * Start up serial port
+	 */
+	retval = zs_startup(info);
+	if (retval)
+		return retval;
+
+	retval = block_til_ready(tty, filp, info);
+	if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+		printk("rs_open returning after block_til_ready with %d\n",
+		       retval);
+#endif
+		return retval;
+	}
+
+#ifdef CONFIG_SERIAL_DEC_CONSOLE
+	if (sercons.cflag && sercons.index == line) {
+		tty->termios->c_cflag = sercons.cflag;
+		sercons.cflag = 0;
+		change_speed(info);
+	}
+#endif
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("rs_open %s successful...", tty->name);
+#endif
+/* tty->low_latency = 1; */
+	return 0;
+}
+
+/* Finally, routines used to initialize the serial driver. */
+
+static void __init show_serial_version(void)
+{
+	printk("DECstation Z8530 serial driver version 0.09\n");
+}
+
+/*  Initialize Z8530s zs_channels
+ */
+
+static void __init probe_sccs(void)
+{
+	struct dec_serial **pp;
+	int i, n, n_chips = 0, n_channels, chip, channel;
+	unsigned long flags;
+
+	/*
+	 * did we get here by accident?
+	 */
+	if(!BUS_PRESENT) {
+		printk("Not on JUNKIO machine, skipping probe_sccs\n");
+		return;
+	}
+
+	/*
+	 * When serial console is activated, tc_init has not been called yet
+	 * and system_base is undefined. Unfortunately we have to hardcode
+	 * system_base for this case :-(. HK
+	 */
+	switch(mips_machtype) {
+#ifdef CONFIG_MACH_DECSTATION
+	case MACH_DS5000_2X0:
+	case MACH_DS5900:
+		system_base = KSEG1ADDR(0x1f800000);
+		n_chips = 2;
+		zs_parms = &ds_parms;
+		zs_parms->irq0 = dec_interrupt[DEC_IRQ_SCC0];
+		zs_parms->irq1 = dec_interrupt[DEC_IRQ_SCC1];
+		break;
+	case MACH_DS5000_1XX:
+		system_base = KSEG1ADDR(0x1c000000);
+		n_chips = 2;
+		zs_parms = &ds_parms;
+		zs_parms->irq0 = dec_interrupt[DEC_IRQ_SCC0];
+		zs_parms->irq1 = dec_interrupt[DEC_IRQ_SCC1];
+		break;
+	case MACH_DS5000_XX:
+		system_base = KSEG1ADDR(0x1c000000);
+		n_chips = 1;
+		zs_parms = &ds_parms;
+		zs_parms->irq0 = dec_interrupt[DEC_IRQ_SCC0];
+		break;
+#endif
+	default:
+		panic("zs: unsupported bus");
+	}
+	if (!zs_parms)
+		panic("zs: uninitialized parms");
+
+	pp = &zs_chain;
+
+	n_channels = 0;
+
+	for (chip = 0; chip < n_chips; chip++) {
+		for (channel = 0; channel <= 1; channel++) {
+			/*
+			 * The sccs reside on the high byte of the 16 bit IOBUS
+			 */
+			zs_channels[n_channels].control =
+				(volatile unsigned char *)system_base +
+			  (0 == chip ? zs_parms->scc0 : zs_parms->scc1) +
+			  (0 == channel ? zs_parms->channel_a_offset :
+			                  zs_parms->channel_b_offset);
+			zs_channels[n_channels].data =
+				zs_channels[n_channels].control + 4;
+
+#ifndef CONFIG_SERIAL_DEC_CONSOLE
+			/*
+			 * We're called early and memory managment isn't up, yet.
+			 * Thus check_region would fail.
+			 */
+			if (!request_region((unsigned long)
+					 zs_channels[n_channels].control,
+					 ZS_CHAN_IO_SIZE, "SCC"))
+				panic("SCC I/O region is not free");
+#endif
+			zs_soft[n_channels].zs_channel = &zs_channels[n_channels];
+			/* HACK alert! */
+			if (!(chip & 1))
+				zs_soft[n_channels].irq = zs_parms->irq0;
+			else
+				zs_soft[n_channels].irq = zs_parms->irq1;
+
+			/*
+			 *  Identification of channel A. Location of channel A
+                         *  inside chip depends on mapping of internal address
+			 *  the chip decodes channels by.
+			 *  CHANNEL_A_NR returns either 0 (in case of
+			 *  DECstations) or 1 (in case of Baget).
+			 */
+			if (CHANNEL_A_NR == channel)
+				zs_soft[n_channels].zs_chan_a =
+				    &zs_channels[n_channels+1-2*CHANNEL_A_NR];
+			else
+				zs_soft[n_channels].zs_chan_a =
+				    &zs_channels[n_channels];
+
+			*pp = &zs_soft[n_channels];
+			pp = &zs_soft[n_channels].zs_next;
+			n_channels++;
+		}
+	}
+
+	*pp = 0;
+	zs_channels_found = n_channels;
+
+	for (n = 0; n < zs_channels_found; n++) {
+		for (i = 0; i < 16; i++) {
+			zs_soft[n].zs_channel->curregs[i] = zs_init_regs[i];
+		}
+	}
+
+	save_and_cli(flags);
+	for (n = 0; n < zs_channels_found; n++) {
+		if (n % 2 == 0) {
+			write_zsreg(zs_soft[n].zs_chan_a, R9, FHWRES);
+			udelay(10);
+			write_zsreg(zs_soft[n].zs_chan_a, R9, 0);
+		}
+		load_zsregs(zs_soft[n].zs_channel,
+			    zs_soft[n].zs_channel->curregs);
+	}
+	restore_flags(flags);
+}
+
+static struct tty_operations serial_ops = {
+	.open = rs_open,
+	.close = rs_close,
+	.write = rs_write,
+	.flush_chars = rs_flush_chars,
+	.write_room = rs_write_room,
+	.chars_in_buffer = rs_chars_in_buffer,
+	.flush_buffer = rs_flush_buffer,
+	.ioctl = rs_ioctl,
+	.throttle = rs_throttle,
+	.unthrottle = rs_unthrottle,
+	.set_termios = rs_set_termios,
+	.stop = rs_stop,
+	.start = rs_start,
+	.hangup = rs_hangup,
+	.break_ctl = rs_break,
+	.wait_until_sent = rs_wait_until_sent,
+	.tiocmget = rs_tiocmget,
+	.tiocmset = rs_tiocmset,
+};
+
+/* zs_init inits the driver */
+int __init zs_init(void)
+{
+	int channel, i;
+	struct dec_serial *info;
+
+	if(!BUS_PRESENT)
+		return -ENODEV;
+
+	/* Setup base handler, and timer table. */
+	init_bh(SERIAL_BH, do_serial_bh);
+
+	/* Find out how many Z8530 SCCs we have */
+	if (zs_chain == 0)
+		probe_sccs();
+	serial_driver = alloc_tty_driver(zs_channels_found);
+	if (!serial_driver)
+		return -ENOMEM;
+
+	show_serial_version();
+
+	/* Initialize the tty_driver structure */
+	/* Not all of this is exactly right for us. */
+
+	serial_driver->owner = THIS_MODULE;
+	serial_driver->devfs_name = "tts/";
+	serial_driver->name = "ttyS";
+	serial_driver->major = TTY_MAJOR;
+	serial_driver->minor_start = 64;
+	serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
+	serial_driver->subtype = SERIAL_TYPE_NORMAL;
+	serial_driver->init_termios = tty_std_termios;
+	serial_driver->init_termios.c_cflag =
+		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	serial_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
+	tty_set_operations(serial_driver, &serial_ops);
+
+	if (tty_register_driver(serial_driver))
+		panic("Couldn't register serial driver");
+
+	for (info = zs_chain, i = 0; info; info = info->zs_next, i++) {
+
+		/* Needed before interrupts are enabled. */
+		info->tty = 0;
+		info->x_char = 0;
+
+		if (info->hook && info->hook->init_info) {
+			(*info->hook->init_info)(info);
+			continue;
+		}
+
+		info->magic = SERIAL_MAGIC;
+		info->port = (int) info->zs_channel->control;
+		info->line = i;
+		info->custom_divisor = 16;
+		info->close_delay = 50;
+		info->closing_wait = 3000;
+		info->event = 0;
+		info->count = 0;
+		info->blocked_open = 0;
+		info->tqueue.routine = do_softint;
+		info->tqueue.data = info;
+		init_waitqueue_head(&info->open_wait);
+		init_waitqueue_head(&info->close_wait);
+		printk("ttyS%02d at 0x%08x (irq = %d) is a Z85C30 SCC\n",
+		       info->line, info->port, info->irq);
+		tty_register_device(serial_driver, info->line, NULL);
+
+	}
+
+	for (channel = 0; channel < zs_channels_found; ++channel) {
+		zs_soft[channel].clk_divisor = 16;
+		zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]);
+
+		if (request_irq(zs_soft[channel].irq, rs_interrupt, SA_SHIRQ,
+				"scc", &zs_soft[channel]))
+			printk(KERN_ERR "decserial: can't get irq %d\n",
+			       zs_soft[channel].irq);
+
+		if (zs_soft[channel].hook) {
+			zs_startup(&zs_soft[channel]);
+			if (zs_soft[channel].hook->init_channel)
+				(*zs_soft[channel].hook->init_channel)
+					(&zs_soft[channel]);
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * polling I/O routines
+ */
+static int
+zs_poll_tx_char(void *handle, unsigned char ch)
+{
+	struct dec_serial *info = handle;
+	struct dec_zschannel *chan = info->zs_channel;
+	int    ret;
+
+	if(chan) {
+		int loops = 10000;
+
+		while (loops && !(read_zsreg(chan, 0) & Tx_BUF_EMP))
+			loops--;
+
+		if (loops) {
+			write_zsdata(chan, ch);
+			ret = 0;
+		} else
+			ret = -EAGAIN;
+
+		return ret;
+	} else
+		return -ENODEV;
+}
+
+static int
+zs_poll_rx_char(void *handle)
+{
+	struct dec_serial *info = handle;
+        struct dec_zschannel *chan = info->zs_channel;
+        int    ret;
+
+	if(chan) {
+                int loops = 10000;
+
+		while (loops && !(read_zsreg(chan, 0) & Rx_CH_AV))
+			loops--;
+
+                if (loops)
+                        ret = read_zsdata(chan);
+                else
+                        ret = -EAGAIN;
+
+		return ret;
+	} else
+		return -ENODEV;
+}
+
+int register_zs_hook(unsigned int channel, struct dec_serial_hook *hook)
+{
+	struct dec_serial *info = &zs_soft[channel];
+
+	if (info->hook) {
+		printk("%s: line %d has already a hook registered\n",
+		       __FUNCTION__, channel);
+
+		return 0;
+	} else {
+		hook->poll_rx_char = zs_poll_rx_char;
+		hook->poll_tx_char = zs_poll_tx_char;
+		info->hook = hook;
+
+		return 1;
+	}
+}
+
+int unregister_zs_hook(unsigned int channel)
+{
+	struct dec_serial *info = &zs_soft[channel];
+
+        if (info->hook) {
+                info->hook = NULL;
+                return 1;
+        } else {
+                printk("%s: trying to unregister hook on line %d,"
+                       " but none is registered\n", __FUNCTION__, channel);
+                return 0;
+        }
+}
+
+/*
+ * ------------------------------------------------------------
+ * Serial console driver
+ * ------------------------------------------------------------
+ */
+#ifdef CONFIG_SERIAL_DEC_CONSOLE
+
+
+/*
+ *	Print a string to the serial port trying not to disturb
+ *	any possible real use of the port...
+ */
+static void serial_console_write(struct console *co, const char *s,
+				 unsigned count)
+{
+	struct dec_serial *info;
+	int i;
+
+	info = zs_soft + co->index;
+
+	for (i = 0; i < count; i++, s++) {
+		if(*s == '\n')
+			zs_poll_tx_char(info, '\r');
+		zs_poll_tx_char(info, *s);
+	}
+}
+
+static struct tty_driver *serial_console_device(struct console *c, int *index)
+{
+	*index = c->index;
+	return serial_driver;
+}
+
+/*
+ *	Setup initial baud/bits/parity. We do two things here:
+ *	- construct a cflag setting for the first rs_open()
+ *	- initialize the serial port
+ *	Return non-zero if we didn't find a serial port.
+ */
+static int __init serial_console_setup(struct console *co, char *options)
+{
+	struct dec_serial *info;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int cflag = CREAD | HUPCL | CLOCAL;
+	int clk_divisor = 16;
+	int brg;
+	char *s;
+	unsigned long flags;
+
+	if(!BUS_PRESENT)
+		return -ENODEV;
+
+	info = zs_soft + co->index;
+
+	if (zs_chain == 0)
+		probe_sccs();
+
+	info->is_cons = 1;
+
+	if (options) {
+		baud = simple_strtoul(options, NULL, 10);
+		s = options;
+		while(*s >= '0' && *s <= '9')
+			s++;
+		if (*s)
+			parity = *s++;
+		if (*s)
+			bits   = *s - '0';
+	}
+
+	/*
+	 *	Now construct a cflag setting.
+	 */
+	switch(baud) {
+	case 1200:
+		cflag |= B1200;
+		break;
+	case 2400:
+		cflag |= B2400;
+		break;
+	case 4800:
+		cflag |= B4800;
+		break;
+	case 19200:
+		cflag |= B19200;
+		break;
+	case 38400:
+		cflag |= B38400;
+		break;
+	case 57600:
+		cflag |= B57600;
+		break;
+	case 115200:
+		cflag |= B115200;
+		break;
+	case 9600:
+	default:
+		cflag |= B9600;
+		/*
+		 * Set this to a sane value to prevent a divide error.
+		 */
+		baud  = 9600;
+		break;
+	}
+	switch(bits) {
+	case 7:
+		cflag |= CS7;
+		break;
+	default:
+	case 8:
+		cflag |= CS8;
+		break;
+	}
+	switch(parity) {
+	case 'o': case 'O':
+		cflag |= PARODD;
+		break;
+	case 'e': case 'E':
+		cflag |= PARENB;
+		break;
+	}
+	co->cflag = cflag;
+
+	save_and_cli(flags);
+
+	/*
+	 * Set up the baud rate generator.
+	 */
+	brg = BPS_TO_BRG(baud, zs_parms->clock / clk_divisor);
+	info->zs_channel->curregs[R12] = (brg & 255);
+	info->zs_channel->curregs[R13] = ((brg >> 8) & 255);
+
+	/*
+	 * Set byte size and parity.
+	 */
+	if (bits == 7) {
+		info->zs_channel->curregs[R3] |= Rx7;
+		info->zs_channel->curregs[R5] |= Tx7;
+	} else {
+		info->zs_channel->curregs[R3] |= Rx8;
+		info->zs_channel->curregs[R5] |= Tx8;
+	}
+	if (cflag & PARENB) {
+		info->zs_channel->curregs[R4] |= PAR_ENA;
+	}
+	if (!(cflag & PARODD)) {
+		info->zs_channel->curregs[R4] |= PAR_EVEN;
+	}
+	info->zs_channel->curregs[R4] |= SB1;
+
+	/*
+	 * Turn on RTS and DTR.
+	 */
+	zs_rtsdtr(info, RTS | DTR, 1);
+
+	/*
+	 * Finally, enable sequencing.
+	 */
+	info->zs_channel->curregs[R3] |= RxENABLE;
+	info->zs_channel->curregs[R5] |= TxENAB;
+
+	/*
+	 * Clear the interrupt registers.
+	 */
+	write_zsreg(info->zs_channel, R0, ERR_RES);
+	write_zsreg(info->zs_channel, R0, RES_H_IUS);
+
+	/*
+	 * Load up the new values.
+	 */
+	load_zsregs(info->zs_channel, info->zs_channel->curregs);
+
+	/* Save the current value of RR0 */
+	info->read_reg_zero = read_zsreg(info->zs_channel, R0);
+
+	zs_soft[co->index].clk_divisor = clk_divisor;
+	zs_soft[co->index].zs_baud = get_zsbaud(&zs_soft[co->index]);
+
+	restore_flags(flags);
+
+	return 0;
+}
+
+static struct console sercons = {
+	.name		= "ttyS",
+	.write		= serial_console_write,
+	.device		= serial_console_device,
+	.setup		= serial_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+};
+
+/*
+ *	Register console.
+ */
+void __init zs_serial_console_init(void)
+{
+	register_console(&sercons);
+}
+#endif /* ifdef CONFIG_SERIAL_DEC_CONSOLE */
+
+#ifdef CONFIG_KGDB
+struct dec_zschannel *zs_kgdbchan;
+static unsigned char scc_inittab[] = {
+	9,  0x80,	/* reset A side (CHRA) */
+	13, 0,		/* set baud rate divisor */
+	12, 1,
+	14, 1,		/* baud rate gen enable, src=rtxc (BRENABL) */
+	11, 0x50,	/* clocks = br gen (RCBR | TCBR) */
+	5,  0x6a,	/* tx 8 bits, assert RTS (Tx8 | TxENAB | RTS) */
+	4,  0x44,	/* x16 clock, 1 stop (SB1 | X16CLK)*/
+	3,  0xc1,	/* rx enable, 8 bits (RxENABLE | Rx8)*/
+};
+
+/* These are for receiving and sending characters under the kgdb
+ * source level kernel debugger.
+ */
+void putDebugChar(char kgdb_char)
+{
+	struct dec_zschannel *chan = zs_kgdbchan;
+	while ((read_zsreg(chan, 0) & Tx_BUF_EMP) == 0)
+		RECOVERY_DELAY;
+	write_zsdata(chan, kgdb_char);
+}
+char getDebugChar(void)
+{
+	struct dec_zschannel *chan = zs_kgdbchan;
+	while((read_zsreg(chan, 0) & Rx_CH_AV) == 0)
+		eieio(); /*barrier();*/
+	return read_zsdata(chan);
+}
+void kgdb_interruptible(int yes)
+{
+	struct dec_zschannel *chan = zs_kgdbchan;
+	int one, nine;
+	nine = read_zsreg(chan, 9);
+	if (yes == 1) {
+		one = EXT_INT_ENAB|RxINT_ALL;
+		nine |= MIE;
+		printk("turning serial ints on\n");
+	} else {
+		one = RxINT_DISAB;
+		nine &= ~MIE;
+		printk("turning serial ints off\n");
+	}
+	write_zsreg(chan, 1, one);
+	write_zsreg(chan, 9, nine);
+}
+
+static int kgdbhook_init_channel(void *handle)
+{
+	return 0;
+}
+
+static void kgdbhook_init_info(void *handle)
+{
+}
+
+static void kgdbhook_rx_char(void *handle, unsigned char ch, unsigned char fl)
+{
+	struct dec_serial *info = handle;
+
+	if (fl != TTY_NORMAL)
+		return;
+	if (ch == 0x03 || ch == '$')
+		breakpoint();
+}
+
+/* This sets up the serial port we're using, and turns on
+ * interrupts for that channel, so kgdb is usable once we're done.
+ */
+static inline void kgdb_chaninit(struct dec_zschannel *ms, int intson, int bps)
+{
+	int brg;
+	int i, x;
+	volatile char *sccc = ms->control;
+	brg = BPS_TO_BRG(bps, zs_parms->clock/16);
+	printk("setting bps on kgdb line to %d [brg=%x]\n", bps, brg);
+	for (i = 20000; i != 0; --i) {
+		x = *sccc; eieio();
+	}
+	for (i = 0; i < sizeof(scc_inittab); ++i) {
+		write_zsreg(ms, scc_inittab[i], scc_inittab[i+1]);
+		i++;
+	}
+}
+/* This is called at boot time to prime the kgdb serial debugging
+ * serial line.  The 'tty_num' argument is 0 for /dev/ttya and 1
+ * for /dev/ttyb which is determined in setup_arch() from the
+ * boot command line flags.
+ */
+struct dec_serial_hook zs_kgdbhook = {
+	.init_channel	= kgdbhook_init_channel,
+	.init_info	= kgdbhook_init_info,
+	.rx_char	= kgdbhook_rx_char,
+	.cflags		= B38400 | CS8 | CLOCAL,
+}
+
+void __init zs_kgdb_hook(int tty_num)
+{
+	/* Find out how many Z8530 SCCs we have */
+	if (zs_chain == 0)
+		probe_sccs();
+	zs_soft[tty_num].zs_channel = &zs_channels[tty_num];
+	zs_kgdbchan = zs_soft[tty_num].zs_channel;
+	zs_soft[tty_num].change_needed = 0;
+	zs_soft[tty_num].clk_divisor = 16;
+	zs_soft[tty_num].zs_baud = 38400;
+ 	zs_soft[tty_num].hook = &zs_kgdbhook; /* This runs kgdb */
+	/* Turn on transmitter/receiver at 8-bits/char */
+        kgdb_chaninit(zs_soft[tty_num].zs_channel, 1, 38400);
+	printk("KGDB: on channel %d initialized\n", tty_num);
+	set_debug_traps(); /* init stub */
+}
+#endif /* ifdef CONFIG_KGDB */
+
+
diff --git a/drivers/tc/zs.h b/drivers/tc/zs.h
new file mode 100644
index 0000000..c52edff
--- /dev/null
+++ b/drivers/tc/zs.h
@@ -0,0 +1,405 @@
+/*
+ * drivers/tc/zs.h: Definitions for the DECstation Z85C30 serial driver.
+ *
+ * Adapted from drivers/sbus/char/sunserial.h by Paul Mackerras.
+ * Adapted from drivers/macintosh/macserial.h by Harald Koerfgen.
+ *
+ * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 2004  Maciej W. Rozycki
+ */
+#ifndef _DECSERIAL_H
+#define _DECSERIAL_H
+
+#include <asm/dec/serial.h>
+
+#define NUM_ZSREGS    16
+
+struct serial_struct {
+	int	type;
+	int	line;
+	int	port;
+	int	irq;
+	int	flags;
+	int	xmit_fifo_size;
+	int	custom_divisor;
+	int	baud_base;
+	unsigned short	close_delay;
+	char	reserved_char[2];
+	int	hub6;
+	unsigned short	closing_wait; /* time to wait before closing */
+	unsigned short	closing_wait2; /* no longer used... */
+	int	reserved[4];
+};
+
+/*
+ * For the close wait times, 0 means wait forever for serial port to
+ * flush its output.  65535 means don't wait at all.
+ */
+#define ZILOG_CLOSING_WAIT_INF	0
+#define ZILOG_CLOSING_WAIT_NONE	65535
+
+/*
+ * Definitions for ZILOG_struct (and serial_struct) flags field
+ */
+#define ZILOG_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes
+				   on the callout port */
+#define ZILOG_FOURPORT  0x0002	/* Set OU1, OUT2 per AST Fourport settings */
+#define ZILOG_SAK	0x0004	/* Secure Attention Key (Orange book) */
+#define ZILOG_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
+
+#define ZILOG_SPD_MASK	0x0030
+#define ZILOG_SPD_HI	0x0010	/* Use 56000 instead of 38400 bps */
+
+#define ZILOG_SPD_VHI	0x0020  /* Use 115200 instead of 38400 bps */
+#define ZILOG_SPD_CUST	0x0030  /* Use user-specified divisor */
+
+#define ZILOG_SKIP_TEST	0x0040 /* Skip UART test during autoconfiguration */
+#define ZILOG_AUTO_IRQ  0x0080 /* Do automatic IRQ during autoconfiguration */
+#define ZILOG_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
+#define ZILOG_PGRP_LOCKOUT    0x0200 /* Lock out cua opens based on pgrp */
+#define ZILOG_CALLOUT_NOHUP   0x0400 /* Don't do hangups for cua device */
+
+#define ZILOG_FLAGS	0x0FFF	/* Possible legal ZILOG flags */
+#define ZILOG_USR_MASK 0x0430	/* Legal flags that non-privileged
+				 * users can set or reset */
+
+/* Internal flags used only by kernel/chr_drv/serial.c */
+#define ZILOG_INITIALIZED	0x80000000 /* Serial port was initialized */
+#define ZILOG_CALLOUT_ACTIVE	0x40000000 /* Call out device is active */
+#define ZILOG_NORMAL_ACTIVE	0x20000000 /* Normal device is active */
+#define ZILOG_BOOT_AUTOCONF	0x10000000 /* Autoconfigure port on bootup */
+#define ZILOG_CLOSING		0x08000000 /* Serial port is closing */
+#define ZILOG_CTS_FLOW		0x04000000 /* Do CTS flow control */
+#define ZILOG_CHECK_CD		0x02000000 /* i.e., CLOCAL */
+
+/* Software state per channel */
+
+#ifdef __KERNEL__
+/*
+ * This is our internal structure for each serial port's state.
+ *
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+struct dec_zschannel {
+	volatile unsigned char *control;
+	volatile unsigned char *data;
+
+	/* Current write register values */
+	unsigned char curregs[NUM_ZSREGS];
+};
+
+struct dec_serial {
+	struct dec_serial	*zs_next;	/* For IRQ servicing chain.  */
+	struct dec_zschannel	*zs_channel;	/* Channel registers.  */
+	struct dec_zschannel	*zs_chan_a;	/* A side registers.  */
+	unsigned char		read_reg_zero;
+
+	struct dec_serial_hook	*hook;		/* Hook on this channel.  */
+	int			tty_break;	/* Set on BREAK condition.  */
+	int			is_cons;	/* Is this our console.  */
+	int			tx_active;	/* Char is being xmitted.  */
+	int			tx_stopped;	/* Output is suspended.  */
+
+	/*
+	 * We need to know the current clock divisor
+	 * to read the bps rate the chip has currently loaded.
+	 */
+	int			clk_divisor;	/* May be 1, 16, 32, or 64.  */
+	int			zs_baud;
+
+	char			change_needed;
+
+	int			magic;
+	int			baud_base;
+	int			port;
+	int			irq;
+	int			flags; 		/* Defined in tty.h.  */
+	int			type; 		/* UART type.  */
+	struct tty_struct 	*tty;
+	int			read_status_mask;
+	int			ignore_status_mask;
+	int			timeout;
+	int			xmit_fifo_size;
+	int			custom_divisor;
+	int			x_char;		/* XON/XOFF character.  */
+	int			close_delay;
+	unsigned short		closing_wait;
+	unsigned short		closing_wait2;
+	unsigned long		event;
+	unsigned long		last_active;
+	int			line;
+	int			count;		/* # of fds on device.  */
+	int			blocked_open;	/* # of blocked opens.  */
+	unsigned char 		*xmit_buf;
+	int			xmit_head;
+	int			xmit_tail;
+	int			xmit_cnt;
+	struct tq_struct	tqueue;
+	struct tq_struct	tqueue_hangup;
+	wait_queue_head_t	open_wait;
+	wait_queue_head_t	close_wait;
+};
+
+
+#define SERIAL_MAGIC 0x5301
+
+/*
+ * The size of the serial xmit buffer is 1 page, or 4096 bytes
+ */
+#define SERIAL_XMIT_SIZE 4096
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
+ */
+#define RS_EVENT_WRITE_WAKEUP	0
+
+#endif /* __KERNEL__ */
+
+/* Conversion routines to/from brg time constants from/to bits
+ * per second.
+ */
+#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
+#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
+
+/* The Zilog register set */
+
+#define	FLAG	0x7e
+
+/* Write Register 0 */
+#define	R0	0		/* Register selects */
+#define	R1	1
+#define	R2	2
+#define	R3	3
+#define	R4	4
+#define	R5	5
+#define	R6	6
+#define	R7	7
+#define	R8	8
+#define	R9	9
+#define	R10	10
+#define	R11	11
+#define	R12	12
+#define	R13	13
+#define	R14	14
+#define	R15	15
+
+#define	NULLCODE	0	/* Null Code */
+#define	POINT_HIGH	0x8	/* Select upper half of registers */
+#define	RES_EXT_INT	0x10	/* Reset Ext. Status Interrupts */
+#define	SEND_ABORT	0x18	/* HDLC Abort */
+#define	RES_RxINT_FC	0x20	/* Reset RxINT on First Character */
+#define	RES_Tx_P	0x28	/* Reset TxINT Pending */
+#define	ERR_RES		0x30	/* Error Reset */
+#define	RES_H_IUS	0x38	/* Reset highest IUS */
+
+#define	RES_Rx_CRC	0x40	/* Reset Rx CRC Checker */
+#define	RES_Tx_CRC	0x80	/* Reset Tx CRC Checker */
+#define	RES_EOM_L	0xC0	/* Reset EOM latch */
+
+/* Write Register 1 */
+
+#define	EXT_INT_ENAB	0x1	/* Ext Int Enable */
+#define	TxINT_ENAB	0x2	/* Tx Int Enable */
+#define	PAR_SPEC	0x4	/* Parity is special condition */
+
+#define	RxINT_DISAB	0	/* Rx Int Disable */
+#define	RxINT_FCERR	0x8	/* Rx Int on First Character Only or Error */
+#define	RxINT_ALL	0x10	/* Int on all Rx Characters or error */
+#define	RxINT_ERR	0x18	/* Int on error only */
+#define	RxINT_MASK	0x18
+
+#define	WT_RDY_RT	0x20	/* Wait/Ready on R/T */
+#define	WT_FN_RDYFN	0x40	/* Wait/FN/Ready FN */
+#define	WT_RDY_ENAB	0x80	/* Wait/Ready Enable */
+
+/* Write Register #2 (Interrupt Vector) */
+
+/* Write Register 3 */
+
+#define	RxENABLE	0x1	/* Rx Enable */
+#define	SYNC_L_INH	0x2	/* Sync Character Load Inhibit */
+#define	ADD_SM		0x4	/* Address Search Mode (SDLC) */
+#define	RxCRC_ENAB	0x8	/* Rx CRC Enable */
+#define	ENT_HM		0x10	/* Enter Hunt Mode */
+#define	AUTO_ENAB	0x20	/* Auto Enables */
+#define	Rx5		0x0	/* Rx 5 Bits/Character */
+#define	Rx7		0x40	/* Rx 7 Bits/Character */
+#define	Rx6		0x80	/* Rx 6 Bits/Character */
+#define	Rx8		0xc0	/* Rx 8 Bits/Character */
+#define RxNBITS_MASK	0xc0
+
+/* Write Register 4 */
+
+#define	PAR_ENA		0x1	/* Parity Enable */
+#define	PAR_EVEN	0x2	/* Parity Even/Odd* */
+
+#define	SYNC_ENAB	0	/* Sync Modes Enable */
+#define	SB1		0x4	/* 1 stop bit/char */
+#define	SB15		0x8	/* 1.5 stop bits/char */
+#define	SB2		0xc	/* 2 stop bits/char */
+#define SB_MASK		0xc
+
+#define	MONSYNC		0	/* 8 Bit Sync character */
+#define	BISYNC		0x10	/* 16 bit sync character */
+#define	SDLC		0x20	/* SDLC Mode (01111110 Sync Flag) */
+#define	EXTSYNC		0x30	/* External Sync Mode */
+
+#define	X1CLK		0x0	/* x1 clock mode */
+#define	X16CLK		0x40	/* x16 clock mode */
+#define	X32CLK		0x80	/* x32 clock mode */
+#define	X64CLK		0xC0	/* x64 clock mode */
+#define XCLK_MASK	0xC0
+
+/* Write Register 5 */
+
+#define	TxCRC_ENAB	0x1	/* Tx CRC Enable */
+#define	RTS		0x2	/* RTS */
+#define	SDLC_CRC	0x4	/* SDLC/CRC-16 */
+#define	TxENAB		0x8	/* Tx Enable */
+#define	SND_BRK		0x10	/* Send Break */
+#define	Tx5		0x0	/* Tx 5 bits (or less)/character */
+#define	Tx7		0x20	/* Tx 7 bits/character */
+#define	Tx6		0x40	/* Tx 6 bits/character */
+#define	Tx8		0x60	/* Tx 8 bits/character */
+#define TxNBITS_MASK	0x60
+#define	DTR		0x80	/* DTR */
+
+/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
+
+/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
+
+/* Write Register 8 (transmit buffer) */
+
+/* Write Register 9 (Master interrupt control) */
+#define	VIS	1	/* Vector Includes Status */
+#define	NV	2	/* No Vector */
+#define	DLC	4	/* Disable Lower Chain */
+#define	MIE	8	/* Master Interrupt Enable */
+#define	STATHI	0x10	/* Status high */
+#define SOFTACK 0x20    /* Software Interrupt Acknowledge */
+#define	NORESET	0	/* No reset on write to R9 */
+#define	CHRB	0x40	/* Reset channel B */
+#define	CHRA	0x80	/* Reset channel A */
+#define	FHWRES	0xc0	/* Force hardware reset */
+
+/* Write Register 10 (misc control bits) */
+#define	BIT6	1	/* 6 bit/8bit sync */
+#define	LOOPMODE 2	/* SDLC Loop mode */
+#define	ABUNDER	4	/* Abort/flag on SDLC xmit underrun */
+#define	MARKIDLE 8	/* Mark/flag on idle */
+#define	GAOP	0x10	/* Go active on poll */
+#define	NRZ	0	/* NRZ mode */
+#define	NRZI	0x20	/* NRZI mode */
+#define	FM1	0x40	/* FM1 (transition = 1) */
+#define	FM0	0x60	/* FM0 (transition = 0) */
+#define	CRCPS	0x80	/* CRC Preset I/O */
+
+/* Write Register 11 (Clock Mode control) */
+#define	TRxCXT	0	/* TRxC = Xtal output */
+#define	TRxCTC	1	/* TRxC = Transmit clock */
+#define	TRxCBR	2	/* TRxC = BR Generator Output */
+#define	TRxCDP	3	/* TRxC = DPLL output */
+#define	TRxCOI	4	/* TRxC O/I */
+#define	TCRTxCP	0	/* Transmit clock = RTxC pin */
+#define	TCTRxCP	8	/* Transmit clock = TRxC pin */
+#define	TCBR	0x10	/* Transmit clock = BR Generator output */
+#define	TCDPLL	0x18	/* Transmit clock = DPLL output */
+#define	RCRTxCP	0	/* Receive clock = RTxC pin */
+#define	RCTRxCP	0x20	/* Receive clock = TRxC pin */
+#define	RCBR	0x40	/* Receive clock = BR Generator output */
+#define	RCDPLL	0x60	/* Receive clock = DPLL output */
+#define	RTxCX	0x80	/* RTxC Xtal/No Xtal */
+
+/* Write Register 12 (lower byte of baud rate generator time constant) */
+
+/* Write Register 13 (upper byte of baud rate generator time constant) */
+
+/* Write Register 14 (Misc control bits) */
+#define	BRENABL	1	/* Baud rate generator enable */
+#define	BRSRC	2	/* Baud rate generator source */
+#define	DTRREQ	4	/* DTR/Request function */
+#define	AUTOECHO 8	/* Auto Echo */
+#define	LOOPBAK	0x10	/* Local loopback */
+#define	SEARCH	0x20	/* Enter search mode */
+#define	RMC	0x40	/* Reset missing clock */
+#define	DISDPLL	0x60	/* Disable DPLL */
+#define	SSBR	0x80	/* Set DPLL source = BR generator */
+#define	SSRTxC	0xa0	/* Set DPLL source = RTxC */
+#define	SFMM	0xc0	/* Set FM mode */
+#define	SNRZI	0xe0	/* Set NRZI mode */
+
+/* Write Register 15 (external/status interrupt control) */
+#define	ZCIE	2	/* Zero count IE */
+#define	DCDIE	8	/* DCD IE */
+#define	SYNCIE	0x10	/* Sync/hunt IE */
+#define	CTSIE	0x20	/* CTS IE */
+#define	TxUIE	0x40	/* Tx Underrun/EOM IE */
+#define	BRKIE	0x80	/* Break/Abort IE */
+
+
+/* Read Register 0 */
+#define	Rx_CH_AV	0x1	/* Rx Character Available */
+#define	ZCOUNT		0x2	/* Zero count */
+#define	Tx_BUF_EMP	0x4	/* Tx Buffer empty */
+#define	DCD		0x8	/* DCD */
+#define	SYNC_HUNT	0x10	/* Sync/hunt */
+#define	CTS		0x20	/* CTS */
+#define	TxEOM		0x40	/* Tx underrun */
+#define	BRK_ABRT	0x80	/* Break/Abort */
+
+/* Read Register 1 */
+#define	ALL_SNT		0x1	/* All sent */
+/* Residue Data for 8 Rx bits/char programmed */
+#define	RES3		0x8	/* 0/3 */
+#define	RES4		0x4	/* 0/4 */
+#define	RES5		0xc	/* 0/5 */
+#define	RES6		0x2	/* 0/6 */
+#define	RES7		0xa	/* 0/7 */
+#define	RES8		0x6	/* 0/8 */
+#define	RES18		0xe	/* 1/8 */
+#define	RES28		0x0	/* 2/8 */
+/* Special Rx Condition Interrupts */
+#define	PAR_ERR		0x10	/* Parity error */
+#define	Rx_OVR		0x20	/* Rx Overrun Error */
+#define	FRM_ERR		0x40	/* CRC/Framing Error */
+#define	END_FR		0x80	/* End of Frame (SDLC) */
+
+/* Read Register 2 (channel b only) - Interrupt vector */
+
+/* Read Register 3 (interrupt pending register) ch a only */
+#define	CHBEXT	0x1		/* Channel B Ext/Stat IP */
+#define	CHBTxIP	0x2		/* Channel B Tx IP */
+#define	CHBRxIP	0x4		/* Channel B Rx IP */
+#define	CHAEXT	0x8		/* Channel A Ext/Stat IP */
+#define	CHATxIP	0x10		/* Channel A Tx IP */
+#define	CHARxIP	0x20		/* Channel A Rx IP */
+
+/* Read Register 8 (receive data register) */
+
+/* Read Register 10  (misc status bits) */
+#define	ONLOOP	2		/* On loop */
+#define	LOOPSEND 0x10		/* Loop sending */
+#define	CLK2MIS	0x40		/* Two clocks missing */
+#define	CLK1MIS	0x80		/* One clock missing */
+
+/* Read Register 12 (lower byte of baud rate generator constant) */
+
+/* Read Register 13 (upper byte of baud rate generator constant) */
+
+/* Read Register 15 (value of WR 15) */
+
+/* Misc macros */
+#define ZS_CLEARERR(channel)    (write_zsreg(channel, 0, ERR_RES))
+#define ZS_CLEARFIFO(channel)   do { volatile unsigned char garbage; \
+				     garbage = read_zsdata(channel); \
+				     garbage = read_zsdata(channel); \
+				     garbage = read_zsdata(channel); \
+				} while(0)
+
+#endif /* !(_DECSERIAL_H) */