diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/ia64/hp/sim |
Linux-2.6.12-rc2v2.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!
Diffstat (limited to 'arch/ia64/hp/sim')
-rw-r--r-- | arch/ia64/hp/sim/Kconfig | 20 | ||||
-rw-r--r-- | arch/ia64/hp/sim/Makefile | 16 | ||||
-rw-r--r-- | arch/ia64/hp/sim/boot/Makefile | 37 | ||||
-rw-r--r-- | arch/ia64/hp/sim/boot/boot_head.S | 144 | ||||
-rw-r--r-- | arch/ia64/hp/sim/boot/bootloader.c | 176 | ||||
-rw-r--r-- | arch/ia64/hp/sim/boot/bootloader.lds | 65 | ||||
-rw-r--r-- | arch/ia64/hp/sim/boot/fw-emu.c | 398 | ||||
-rw-r--r-- | arch/ia64/hp/sim/boot/ssc.h | 35 | ||||
-rw-r--r-- | arch/ia64/hp/sim/hpsim.S | 10 | ||||
-rw-r--r-- | arch/ia64/hp/sim/hpsim_console.c | 65 | ||||
-rw-r--r-- | arch/ia64/hp/sim/hpsim_irq.c | 51 | ||||
-rw-r--r-- | arch/ia64/hp/sim/hpsim_machvec.c | 3 | ||||
-rw-r--r-- | arch/ia64/hp/sim/hpsim_setup.c | 52 | ||||
-rw-r--r-- | arch/ia64/hp/sim/hpsim_ssc.h | 36 | ||||
-rw-r--r-- | arch/ia64/hp/sim/simeth.c | 530 | ||||
-rw-r--r-- | arch/ia64/hp/sim/simscsi.c | 404 | ||||
-rw-r--r-- | arch/ia64/hp/sim/simserial.c | 1032 |
17 files changed, 3074 insertions, 0 deletions
diff --git a/arch/ia64/hp/sim/Kconfig b/arch/ia64/hp/sim/Kconfig new file mode 100644 index 000000000000..18ccb1266e18 --- /dev/null +++ b/arch/ia64/hp/sim/Kconfig | |||
@@ -0,0 +1,20 @@ | |||
1 | |||
2 | menu "HP Simulator drivers" | ||
3 | depends on IA64_HP_SIM || IA64_GENERIC | ||
4 | |||
5 | config HP_SIMETH | ||
6 | bool "Simulated Ethernet " | ||
7 | |||
8 | config HP_SIMSERIAL | ||
9 | bool "Simulated serial driver support" | ||
10 | |||
11 | config HP_SIMSERIAL_CONSOLE | ||
12 | bool "Console for HP simulator" | ||
13 | depends on HP_SIMSERIAL | ||
14 | |||
15 | config HP_SIMSCSI | ||
16 | tristate "Simulated SCSI disk" | ||
17 | depends on SCSI | ||
18 | |||
19 | endmenu | ||
20 | |||
diff --git a/arch/ia64/hp/sim/Makefile b/arch/ia64/hp/sim/Makefile new file mode 100644 index 000000000000..d10da47931d7 --- /dev/null +++ b/arch/ia64/hp/sim/Makefile | |||
@@ -0,0 +1,16 @@ | |||
1 | # | ||
2 | # ia64/platform/hp/sim/Makefile | ||
3 | # | ||
4 | # Copyright (C) 2002 Hewlett-Packard Co. | ||
5 | # David Mosberger-Tang <davidm@hpl.hp.com> | ||
6 | # Copyright (C) 1999 Silicon Graphics, Inc. | ||
7 | # Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) | ||
8 | # | ||
9 | |||
10 | obj-y := hpsim_irq.o hpsim_setup.o hpsim.o | ||
11 | obj-$(CONFIG_IA64_GENERIC) += hpsim_machvec.o | ||
12 | |||
13 | obj-$(CONFIG_HP_SIMETH) += simeth.o | ||
14 | obj-$(CONFIG_HP_SIMSERIAL) += simserial.o | ||
15 | obj-$(CONFIG_HP_SIMSERIAL_CONSOLE) += hpsim_console.o | ||
16 | obj-$(CONFIG_HP_SIMSCSI) += simscsi.o | ||
diff --git a/arch/ia64/hp/sim/boot/Makefile b/arch/ia64/hp/sim/boot/Makefile new file mode 100644 index 000000000000..df6e9968c845 --- /dev/null +++ b/arch/ia64/hp/sim/boot/Makefile | |||
@@ -0,0 +1,37 @@ | |||
1 | # | ||
2 | # ia64/boot/Makefile | ||
3 | # | ||
4 | # This file is subject to the terms and conditions of the GNU General Public | ||
5 | # License. See the file "COPYING" in the main directory of this archive | ||
6 | # for more details. | ||
7 | # | ||
8 | # Copyright (C) 1998, 2003 by David Mosberger-Tang <davidm@hpl.hp.com> | ||
9 | # | ||
10 | |||
11 | targets-$(CONFIG_IA64_HP_SIM) += bootloader | ||
12 | targets := vmlinux.bin vmlinux.gz $(targets-y) | ||
13 | |||
14 | quiet_cmd_cptotop = LN $@ | ||
15 | cmd_cptotop = ln -f $< $@ | ||
16 | |||
17 | vmlinux.gz: $(obj)/vmlinux.gz $(addprefix $(obj)/,$(targets-y)) | ||
18 | $(call cmd,cptotop) | ||
19 | @echo ' Kernel: $@ is ready' | ||
20 | |||
21 | boot: bootloader | ||
22 | |||
23 | bootloader: $(obj)/bootloader | ||
24 | $(call cmd,cptotop) | ||
25 | |||
26 | $(obj)/vmlinux.gz: $(obj)/vmlinux.bin FORCE | ||
27 | $(call if_changed,gzip) | ||
28 | |||
29 | $(obj)/vmlinux.bin: vmlinux FORCE | ||
30 | $(call if_changed,objcopy) | ||
31 | |||
32 | |||
33 | LDFLAGS_bootloader = -static -T | ||
34 | |||
35 | $(obj)/bootloader: $(src)/bootloader.lds $(obj)/bootloader.o $(obj)/boot_head.o $(obj)/fw-emu.o \ | ||
36 | lib/lib.a arch/ia64/lib/lib.a FORCE | ||
37 | $(call if_changed,ld) | ||
diff --git a/arch/ia64/hp/sim/boot/boot_head.S b/arch/ia64/hp/sim/boot/boot_head.S new file mode 100644 index 000000000000..9364199e5632 --- /dev/null +++ b/arch/ia64/hp/sim/boot/boot_head.S | |||
@@ -0,0 +1,144 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1998-2003 Hewlett-Packard Co | ||
3 | * David Mosberger-Tang <davidm@hpl.hp.com> | ||
4 | */ | ||
5 | |||
6 | #include <asm/asmmacro.h> | ||
7 | |||
8 | .bss | ||
9 | .align 16 | ||
10 | stack_mem: | ||
11 | .skip 16834 | ||
12 | |||
13 | .text | ||
14 | |||
15 | /* This needs to be defined because lib/string.c:strlcat() calls it in case of error... */ | ||
16 | GLOBAL_ENTRY(printk) | ||
17 | break 0 | ||
18 | END(printk) | ||
19 | |||
20 | GLOBAL_ENTRY(_start) | ||
21 | .prologue | ||
22 | .save rp, r0 | ||
23 | .body | ||
24 | movl gp = __gp | ||
25 | movl sp = stack_mem | ||
26 | bsw.1 | ||
27 | br.call.sptk.many rp=start_bootloader | ||
28 | END(_start) | ||
29 | |||
30 | /* | ||
31 | * Set a break point on this function so that symbols are available to set breakpoints in | ||
32 | * the kernel being debugged. | ||
33 | */ | ||
34 | GLOBAL_ENTRY(debug_break) | ||
35 | br.ret.sptk.many b0 | ||
36 | END(debug_break) | ||
37 | |||
38 | GLOBAL_ENTRY(ssc) | ||
39 | .regstk 5,0,0,0 | ||
40 | mov r15=in4 | ||
41 | break 0x80001 | ||
42 | br.ret.sptk.many b0 | ||
43 | END(ssc) | ||
44 | |||
45 | GLOBAL_ENTRY(jmp_to_kernel) | ||
46 | .regstk 2,0,0,0 | ||
47 | mov r28=in0 | ||
48 | mov b7=in1 | ||
49 | br.sptk.few b7 | ||
50 | END(jmp_to_kernel) | ||
51 | |||
52 | |||
53 | GLOBAL_ENTRY(pal_emulator_static) | ||
54 | mov r8=-1 | ||
55 | mov r9=256 | ||
56 | ;; | ||
57 | cmp.gtu p6,p7=r9,r28 /* r28 <= 255? */ | ||
58 | (p6) br.cond.sptk.few static | ||
59 | ;; | ||
60 | mov r9=512 | ||
61 | ;; | ||
62 | cmp.gtu p6,p7=r9,r28 | ||
63 | (p6) br.cond.sptk.few stacked | ||
64 | ;; | ||
65 | static: cmp.eq p6,p7=6,r28 /* PAL_PTCE_INFO */ | ||
66 | (p7) br.cond.sptk.few 1f | ||
67 | ;; | ||
68 | mov r8=0 /* status = 0 */ | ||
69 | movl r9=0x100000000 /* tc.base */ | ||
70 | movl r10=0x0000000200000003 /* count[0], count[1] */ | ||
71 | movl r11=0x1000000000002000 /* stride[0], stride[1] */ | ||
72 | br.cond.sptk.few rp | ||
73 | 1: cmp.eq p6,p7=14,r28 /* PAL_FREQ_RATIOS */ | ||
74 | (p7) br.cond.sptk.few 1f | ||
75 | mov r8=0 /* status = 0 */ | ||
76 | movl r9 =0x100000064 /* proc_ratio (1/100) */ | ||
77 | movl r10=0x100000100 /* bus_ratio<<32 (1/256) */ | ||
78 | movl r11=0x100000064 /* itc_ratio<<32 (1/100) */ | ||
79 | ;; | ||
80 | 1: cmp.eq p6,p7=19,r28 /* PAL_RSE_INFO */ | ||
81 | (p7) br.cond.sptk.few 1f | ||
82 | mov r8=0 /* status = 0 */ | ||
83 | mov r9=96 /* num phys stacked */ | ||
84 | mov r10=0 /* hints */ | ||
85 | mov r11=0 | ||
86 | br.cond.sptk.few rp | ||
87 | 1: cmp.eq p6,p7=1,r28 /* PAL_CACHE_FLUSH */ | ||
88 | (p7) br.cond.sptk.few 1f | ||
89 | mov r9=ar.lc | ||
90 | movl r8=524288 /* flush 512k million cache lines (16MB) */ | ||
91 | ;; | ||
92 | mov ar.lc=r8 | ||
93 | movl r8=0xe000000000000000 | ||
94 | ;; | ||
95 | .loop: fc r8 | ||
96 | add r8=32,r8 | ||
97 | br.cloop.sptk.few .loop | ||
98 | sync.i | ||
99 | ;; | ||
100 | srlz.i | ||
101 | ;; | ||
102 | mov ar.lc=r9 | ||
103 | mov r8=r0 | ||
104 | ;; | ||
105 | 1: cmp.eq p6,p7=15,r28 /* PAL_PERF_MON_INFO */ | ||
106 | (p7) br.cond.sptk.few 1f | ||
107 | mov r8=0 /* status = 0 */ | ||
108 | movl r9 =0x08122f04 /* generic=4 width=47 retired=8 cycles=18 */ | ||
109 | mov r10=0 /* reserved */ | ||
110 | mov r11=0 /* reserved */ | ||
111 | mov r16=0xffff /* implemented PMC */ | ||
112 | mov r17=0x3ffff /* implemented PMD */ | ||
113 | add r18=8,r29 /* second index */ | ||
114 | ;; | ||
115 | st8 [r29]=r16,16 /* store implemented PMC */ | ||
116 | st8 [r18]=r0,16 /* clear remaining bits */ | ||
117 | ;; | ||
118 | st8 [r29]=r0,16 /* clear remaining bits */ | ||
119 | st8 [r18]=r0,16 /* clear remaining bits */ | ||
120 | ;; | ||
121 | st8 [r29]=r17,16 /* store implemented PMD */ | ||
122 | st8 [r18]=r0,16 /* clear remaining bits */ | ||
123 | mov r16=0xf0 /* cycles count capable PMC */ | ||
124 | ;; | ||
125 | st8 [r29]=r0,16 /* clear remaining bits */ | ||
126 | st8 [r18]=r0,16 /* clear remaining bits */ | ||
127 | mov r17=0xf0 /* retired bundles capable PMC */ | ||
128 | ;; | ||
129 | st8 [r29]=r16,16 /* store cycles capable */ | ||
130 | st8 [r18]=r0,16 /* clear remaining bits */ | ||
131 | ;; | ||
132 | st8 [r29]=r0,16 /* clear remaining bits */ | ||
133 | st8 [r18]=r0,16 /* clear remaining bits */ | ||
134 | ;; | ||
135 | st8 [r29]=r17,16 /* store retired bundle capable */ | ||
136 | st8 [r18]=r0,16 /* clear remaining bits */ | ||
137 | ;; | ||
138 | st8 [r29]=r0,16 /* clear remaining bits */ | ||
139 | st8 [r18]=r0,16 /* clear remaining bits */ | ||
140 | ;; | ||
141 | 1: br.cond.sptk.few rp | ||
142 | stacked: | ||
143 | br.ret.sptk.few rp | ||
144 | END(pal_emulator_static) | ||
diff --git a/arch/ia64/hp/sim/boot/bootloader.c b/arch/ia64/hp/sim/boot/bootloader.c new file mode 100644 index 000000000000..51a7b7b4dd0e --- /dev/null +++ b/arch/ia64/hp/sim/boot/bootloader.c | |||
@@ -0,0 +1,176 @@ | |||
1 | /* | ||
2 | * arch/ia64/hp/sim/boot/bootloader.c | ||
3 | * | ||
4 | * Loads an ELF kernel. | ||
5 | * | ||
6 | * Copyright (C) 1998-2003 Hewlett-Packard Co | ||
7 | * David Mosberger-Tang <davidm@hpl.hp.com> | ||
8 | * Stephane Eranian <eranian@hpl.hp.com> | ||
9 | * | ||
10 | * 01/07/99 S.Eranian modified to pass command line arguments to kernel | ||
11 | */ | ||
12 | struct task_struct; /* forward declaration for elf.h */ | ||
13 | |||
14 | #include <linux/config.h> | ||
15 | #include <linux/elf.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/kernel.h> | ||
18 | |||
19 | #include <asm/elf.h> | ||
20 | #include <asm/intrinsics.h> | ||
21 | #include <asm/pal.h> | ||
22 | #include <asm/pgtable.h> | ||
23 | #include <asm/sal.h> | ||
24 | #include <asm/system.h> | ||
25 | |||
26 | #include "ssc.h" | ||
27 | |||
28 | struct disk_req { | ||
29 | unsigned long addr; | ||
30 | unsigned len; | ||
31 | }; | ||
32 | |||
33 | struct disk_stat { | ||
34 | int fd; | ||
35 | unsigned count; | ||
36 | }; | ||
37 | |||
38 | extern void jmp_to_kernel (unsigned long bp, unsigned long e_entry); | ||
39 | extern struct ia64_boot_param *sys_fw_init (const char *args, int arglen); | ||
40 | extern void debug_break (void); | ||
41 | |||
42 | static void | ||
43 | cons_write (const char *buf) | ||
44 | { | ||
45 | unsigned long ch; | ||
46 | |||
47 | while ((ch = *buf++) != '\0') { | ||
48 | ssc(ch, 0, 0, 0, SSC_PUTCHAR); | ||
49 | if (ch == '\n') | ||
50 | ssc('\r', 0, 0, 0, SSC_PUTCHAR); | ||
51 | } | ||
52 | } | ||
53 | |||
54 | #define MAX_ARGS 32 | ||
55 | |||
56 | void | ||
57 | start_bootloader (void) | ||
58 | { | ||
59 | static char mem[4096]; | ||
60 | static char buffer[1024]; | ||
61 | unsigned long off; | ||
62 | int fd, i; | ||
63 | struct disk_req req; | ||
64 | struct disk_stat stat; | ||
65 | struct elfhdr *elf; | ||
66 | struct elf_phdr *elf_phdr; /* program header */ | ||
67 | unsigned long e_entry, e_phoff, e_phnum; | ||
68 | register struct ia64_boot_param *bp; | ||
69 | char *kpath, *args; | ||
70 | long arglen = 0; | ||
71 | |||
72 | ssc(0, 0, 0, 0, SSC_CONSOLE_INIT); | ||
73 | |||
74 | /* | ||
75 | * S.Eranian: extract the commandline argument from the simulator | ||
76 | * | ||
77 | * The expected format is as follows: | ||
78 | * | ||
79 | * kernelname args... | ||
80 | * | ||
81 | * Both are optional but you can't have the second one without the first. | ||
82 | */ | ||
83 | arglen = ssc((long) buffer, 0, 0, 0, SSC_GET_ARGS); | ||
84 | |||
85 | kpath = "vmlinux"; | ||
86 | args = buffer; | ||
87 | if (arglen > 0) { | ||
88 | kpath = buffer; | ||
89 | while (*args != ' ' && *args != '\0') | ||
90 | ++args, --arglen; | ||
91 | if (*args == ' ') | ||
92 | *args++ = '\0', --arglen; | ||
93 | } | ||
94 | |||
95 | if (arglen <= 0) { | ||
96 | args = ""; | ||
97 | arglen = 1; | ||
98 | } | ||
99 | |||
100 | fd = ssc((long) kpath, 1, 0, 0, SSC_OPEN); | ||
101 | |||
102 | if (fd < 0) { | ||
103 | cons_write(kpath); | ||
104 | cons_write(": file not found, reboot now\n"); | ||
105 | for(;;); | ||
106 | } | ||
107 | stat.fd = fd; | ||
108 | off = 0; | ||
109 | |||
110 | req.len = sizeof(mem); | ||
111 | req.addr = (long) mem; | ||
112 | ssc(fd, 1, (long) &req, off, SSC_READ); | ||
113 | ssc((long) &stat, 0, 0, 0, SSC_WAIT_COMPLETION); | ||
114 | |||
115 | elf = (struct elfhdr *) mem; | ||
116 | if (elf->e_ident[0] == 0x7f && strncmp(elf->e_ident + 1, "ELF", 3) != 0) { | ||
117 | cons_write("not an ELF file\n"); | ||
118 | return; | ||
119 | } | ||
120 | if (elf->e_type != ET_EXEC) { | ||
121 | cons_write("not an ELF executable\n"); | ||
122 | return; | ||
123 | } | ||
124 | if (!elf_check_arch(elf)) { | ||
125 | cons_write("kernel not for this processor\n"); | ||
126 | return; | ||
127 | } | ||
128 | |||
129 | e_entry = elf->e_entry; | ||
130 | e_phnum = elf->e_phnum; | ||
131 | e_phoff = elf->e_phoff; | ||
132 | |||
133 | cons_write("loading "); | ||
134 | cons_write(kpath); | ||
135 | cons_write("...\n"); | ||
136 | |||
137 | for (i = 0; i < e_phnum; ++i) { | ||
138 | req.len = sizeof(*elf_phdr); | ||
139 | req.addr = (long) mem; | ||
140 | ssc(fd, 1, (long) &req, e_phoff, SSC_READ); | ||
141 | ssc((long) &stat, 0, 0, 0, SSC_WAIT_COMPLETION); | ||
142 | if (stat.count != sizeof(*elf_phdr)) { | ||
143 | cons_write("failed to read phdr\n"); | ||
144 | return; | ||
145 | } | ||
146 | e_phoff += sizeof(*elf_phdr); | ||
147 | |||
148 | elf_phdr = (struct elf_phdr *) mem; | ||
149 | |||
150 | if (elf_phdr->p_type != PT_LOAD) | ||
151 | continue; | ||
152 | |||
153 | req.len = elf_phdr->p_filesz; | ||
154 | req.addr = __pa(elf_phdr->p_paddr); | ||
155 | ssc(fd, 1, (long) &req, elf_phdr->p_offset, SSC_READ); | ||
156 | ssc((long) &stat, 0, 0, 0, SSC_WAIT_COMPLETION); | ||
157 | memset((char *)__pa(elf_phdr->p_paddr) + elf_phdr->p_filesz, 0, | ||
158 | elf_phdr->p_memsz - elf_phdr->p_filesz); | ||
159 | } | ||
160 | ssc(fd, 0, 0, 0, SSC_CLOSE); | ||
161 | |||
162 | cons_write("starting kernel...\n"); | ||
163 | |||
164 | /* fake an I/O base address: */ | ||
165 | ia64_setreg(_IA64_REG_AR_KR0, 0xffffc000000UL); | ||
166 | |||
167 | bp = sys_fw_init(args, arglen); | ||
168 | |||
169 | ssc(0, (long) kpath, 0, 0, SSC_LOAD_SYMBOLS); | ||
170 | |||
171 | debug_break(); | ||
172 | jmp_to_kernel((unsigned long) bp, e_entry); | ||
173 | |||
174 | cons_write("kernel returned!\n"); | ||
175 | ssc(-1, 0, 0, 0, SSC_EXIT); | ||
176 | } | ||
diff --git a/arch/ia64/hp/sim/boot/bootloader.lds b/arch/ia64/hp/sim/boot/bootloader.lds new file mode 100644 index 000000000000..69ae58531033 --- /dev/null +++ b/arch/ia64/hp/sim/boot/bootloader.lds | |||
@@ -0,0 +1,65 @@ | |||
1 | OUTPUT_FORMAT("elf64-ia64-little") | ||
2 | OUTPUT_ARCH(ia64) | ||
3 | ENTRY(_start) | ||
4 | SECTIONS | ||
5 | { | ||
6 | /* Read-only sections, merged into text segment: */ | ||
7 | . = 0x100000; | ||
8 | |||
9 | _text = .; | ||
10 | .text : { *(__ivt_section) *(.text) } | ||
11 | _etext = .; | ||
12 | |||
13 | /* Global data */ | ||
14 | _data = .; | ||
15 | .rodata : { *(.rodata) *(.rodata.*) } | ||
16 | .data : { *(.data) *(.gnu.linkonce.d*) CONSTRUCTORS } | ||
17 | __gp = ALIGN (8) + 0x200000; | ||
18 | .got : { *(.got.plt) *(.got) } | ||
19 | /* We want the small data sections together, so single-instruction offsets | ||
20 | can access them all, and initialized data all before uninitialized, so | ||
21 | we can shorten the on-disk segment size. */ | ||
22 | .sdata : { *(.sdata) } | ||
23 | _edata = .; | ||
24 | |||
25 | _bss = .; | ||
26 | .sbss : { *(.sbss) *(.scommon) } | ||
27 | .bss : { *(.bss) *(COMMON) } | ||
28 | . = ALIGN(64 / 8); | ||
29 | _end = . ; | ||
30 | |||
31 | /* Stabs debugging sections. */ | ||
32 | .stab 0 : { *(.stab) } | ||
33 | .stabstr 0 : { *(.stabstr) } | ||
34 | .stab.excl 0 : { *(.stab.excl) } | ||
35 | .stab.exclstr 0 : { *(.stab.exclstr) } | ||
36 | .stab.index 0 : { *(.stab.index) } | ||
37 | .stab.indexstr 0 : { *(.stab.indexstr) } | ||
38 | .comment 0 : { *(.comment) } | ||
39 | /* DWARF debug sections. | ||
40 | Symbols in the DWARF debugging sections are relative to the beginning | ||
41 | of the section so we begin them at 0. */ | ||
42 | /* DWARF 1 */ | ||
43 | .debug 0 : { *(.debug) } | ||
44 | .line 0 : { *(.line) } | ||
45 | /* GNU DWARF 1 extensions */ | ||
46 | .debug_srcinfo 0 : { *(.debug_srcinfo) } | ||
47 | .debug_sfnames 0 : { *(.debug_sfnames) } | ||
48 | /* DWARF 1.1 and DWARF 2 */ | ||
49 | .debug_aranges 0 : { *(.debug_aranges) } | ||
50 | .debug_pubnames 0 : { *(.debug_pubnames) } | ||
51 | /* DWARF 2 */ | ||
52 | .debug_info 0 : { *(.debug_info) } | ||
53 | .debug_abbrev 0 : { *(.debug_abbrev) } | ||
54 | .debug_line 0 : { *(.debug_line) } | ||
55 | .debug_frame 0 : { *(.debug_frame) } | ||
56 | .debug_str 0 : { *(.debug_str) } | ||
57 | .debug_loc 0 : { *(.debug_loc) } | ||
58 | .debug_macinfo 0 : { *(.debug_macinfo) } | ||
59 | /* SGI/MIPS DWARF 2 extensions */ | ||
60 | .debug_weaknames 0 : { *(.debug_weaknames) } | ||
61 | .debug_funcnames 0 : { *(.debug_funcnames) } | ||
62 | .debug_typenames 0 : { *(.debug_typenames) } | ||
63 | .debug_varnames 0 : { *(.debug_varnames) } | ||
64 | /* These must appear regardless of . */ | ||
65 | } | ||
diff --git a/arch/ia64/hp/sim/boot/fw-emu.c b/arch/ia64/hp/sim/boot/fw-emu.c new file mode 100644 index 000000000000..5c46928e3dc6 --- /dev/null +++ b/arch/ia64/hp/sim/boot/fw-emu.c | |||
@@ -0,0 +1,398 @@ | |||
1 | /* | ||
2 | * PAL & SAL emulation. | ||
3 | * | ||
4 | * Copyright (C) 1998-2001 Hewlett-Packard Co | ||
5 | * David Mosberger-Tang <davidm@hpl.hp.com> | ||
6 | */ | ||
7 | #include <linux/config.h> | ||
8 | |||
9 | #ifdef CONFIG_PCI | ||
10 | # include <linux/pci.h> | ||
11 | #endif | ||
12 | |||
13 | #include <linux/efi.h> | ||
14 | #include <asm/io.h> | ||
15 | #include <asm/pal.h> | ||
16 | #include <asm/sal.h> | ||
17 | |||
18 | #include "ssc.h" | ||
19 | |||
20 | #define MB (1024*1024UL) | ||
21 | |||
22 | #define SIMPLE_MEMMAP 1 | ||
23 | |||
24 | #if SIMPLE_MEMMAP | ||
25 | # define NUM_MEM_DESCS 4 | ||
26 | #else | ||
27 | # define NUM_MEM_DESCS 16 | ||
28 | #endif | ||
29 | |||
30 | static char fw_mem[( sizeof(struct ia64_boot_param) | ||
31 | + sizeof(efi_system_table_t) | ||
32 | + sizeof(efi_runtime_services_t) | ||
33 | + 1*sizeof(efi_config_table_t) | ||
34 | + sizeof(struct ia64_sal_systab) | ||
35 | + sizeof(struct ia64_sal_desc_entry_point) | ||
36 | + NUM_MEM_DESCS*(sizeof(efi_memory_desc_t)) | ||
37 | + 1024)] __attribute__ ((aligned (8))); | ||
38 | |||
39 | #define SECS_PER_HOUR (60 * 60) | ||
40 | #define SECS_PER_DAY (SECS_PER_HOUR * 24) | ||
41 | |||
42 | /* Compute the `struct tm' representation of *T, | ||
43 | offset OFFSET seconds east of UTC, | ||
44 | and store year, yday, mon, mday, wday, hour, min, sec into *TP. | ||
45 | Return nonzero if successful. */ | ||
46 | int | ||
47 | offtime (unsigned long t, efi_time_t *tp) | ||
48 | { | ||
49 | const unsigned short int __mon_yday[2][13] = | ||
50 | { | ||
51 | /* Normal years. */ | ||
52 | { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, | ||
53 | /* Leap years. */ | ||
54 | { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } | ||
55 | }; | ||
56 | long int days, rem, y; | ||
57 | const unsigned short int *ip; | ||
58 | |||
59 | days = t / SECS_PER_DAY; | ||
60 | rem = t % SECS_PER_DAY; | ||
61 | while (rem < 0) { | ||
62 | rem += SECS_PER_DAY; | ||
63 | --days; | ||
64 | } | ||
65 | while (rem >= SECS_PER_DAY) { | ||
66 | rem -= SECS_PER_DAY; | ||
67 | ++days; | ||
68 | } | ||
69 | tp->hour = rem / SECS_PER_HOUR; | ||
70 | rem %= SECS_PER_HOUR; | ||
71 | tp->minute = rem / 60; | ||
72 | tp->second = rem % 60; | ||
73 | /* January 1, 1970 was a Thursday. */ | ||
74 | y = 1970; | ||
75 | |||
76 | # define DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) | ||
77 | # define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400)) | ||
78 | # define __isleap(year) \ | ||
79 | ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) | ||
80 | |||
81 | while (days < 0 || days >= (__isleap (y) ? 366 : 365)) { | ||
82 | /* Guess a corrected year, assuming 365 days per year. */ | ||
83 | long int yg = y + days / 365 - (days % 365 < 0); | ||
84 | |||
85 | /* Adjust DAYS and Y to match the guessed year. */ | ||
86 | days -= ((yg - y) * 365 + LEAPS_THRU_END_OF (yg - 1) | ||
87 | - LEAPS_THRU_END_OF (y - 1)); | ||
88 | y = yg; | ||
89 | } | ||
90 | tp->year = y; | ||
91 | ip = __mon_yday[__isleap(y)]; | ||
92 | for (y = 11; days < (long int) ip[y]; --y) | ||
93 | continue; | ||
94 | days -= ip[y]; | ||
95 | tp->month = y + 1; | ||
96 | tp->day = days + 1; | ||
97 | return 1; | ||
98 | } | ||
99 | |||
100 | extern void pal_emulator_static (void); | ||
101 | |||
102 | /* Macro to emulate SAL call using legacy IN and OUT calls to CF8, CFC etc.. */ | ||
103 | |||
104 | #define BUILD_CMD(addr) ((0x80000000 | (addr)) & ~3) | ||
105 | |||
106 | #define REG_OFFSET(addr) (0x00000000000000FF & (addr)) | ||
107 | #define DEVICE_FUNCTION(addr) (0x000000000000FF00 & (addr)) | ||
108 | #define BUS_NUMBER(addr) (0x0000000000FF0000 & (addr)) | ||
109 | |||
110 | static efi_status_t | ||
111 | fw_efi_get_time (efi_time_t *tm, efi_time_cap_t *tc) | ||
112 | { | ||
113 | #if defined(CONFIG_IA64_HP_SIM) || defined(CONFIG_IA64_GENERIC) | ||
114 | struct { | ||
115 | int tv_sec; /* must be 32bits to work */ | ||
116 | int tv_usec; | ||
117 | } tv32bits; | ||
118 | |||
119 | ssc((unsigned long) &tv32bits, 0, 0, 0, SSC_GET_TOD); | ||
120 | |||
121 | memset(tm, 0, sizeof(*tm)); | ||
122 | offtime(tv32bits.tv_sec, tm); | ||
123 | |||
124 | if (tc) | ||
125 | memset(tc, 0, sizeof(*tc)); | ||
126 | #else | ||
127 | # error Not implemented yet... | ||
128 | #endif | ||
129 | return EFI_SUCCESS; | ||
130 | } | ||
131 | |||
132 | static void | ||
133 | efi_reset_system (int reset_type, efi_status_t status, unsigned long data_size, efi_char16_t *data) | ||
134 | { | ||
135 | #if defined(CONFIG_IA64_HP_SIM) || defined(CONFIG_IA64_GENERIC) | ||
136 | ssc(status, 0, 0, 0, SSC_EXIT); | ||
137 | #else | ||
138 | # error Not implemented yet... | ||
139 | #endif | ||
140 | } | ||
141 | |||
142 | static efi_status_t | ||
143 | efi_unimplemented (void) | ||
144 | { | ||
145 | return EFI_UNSUPPORTED; | ||
146 | } | ||
147 | |||
148 | static struct sal_ret_values | ||
149 | sal_emulator (long index, unsigned long in1, unsigned long in2, | ||
150 | unsigned long in3, unsigned long in4, unsigned long in5, | ||
151 | unsigned long in6, unsigned long in7) | ||
152 | { | ||
153 | long r9 = 0; | ||
154 | long r10 = 0; | ||
155 | long r11 = 0; | ||
156 | long status; | ||
157 | |||
158 | /* | ||
159 | * Don't do a "switch" here since that gives us code that | ||
160 | * isn't self-relocatable. | ||
161 | */ | ||
162 | status = 0; | ||
163 | if (index == SAL_FREQ_BASE) { | ||
164 | switch (in1) { | ||
165 | case SAL_FREQ_BASE_PLATFORM: | ||
166 | r9 = 200000000; | ||
167 | break; | ||
168 | |||
169 | case SAL_FREQ_BASE_INTERVAL_TIMER: | ||
170 | /* | ||
171 | * Is this supposed to be the cr.itc frequency | ||
172 | * or something platform specific? The SAL | ||
173 | * doc ain't exactly clear on this... | ||
174 | */ | ||
175 | r9 = 700000000; | ||
176 | break; | ||
177 | |||
178 | case SAL_FREQ_BASE_REALTIME_CLOCK: | ||
179 | r9 = 1; | ||
180 | break; | ||
181 | |||
182 | default: | ||
183 | status = -1; | ||
184 | break; | ||
185 | } | ||
186 | } else if (index == SAL_SET_VECTORS) { | ||
187 | ; | ||
188 | } else if (index == SAL_GET_STATE_INFO) { | ||
189 | ; | ||
190 | } else if (index == SAL_GET_STATE_INFO_SIZE) { | ||
191 | ; | ||
192 | } else if (index == SAL_CLEAR_STATE_INFO) { | ||
193 | ; | ||
194 | } else if (index == SAL_MC_RENDEZ) { | ||
195 | ; | ||
196 | } else if (index == SAL_MC_SET_PARAMS) { | ||
197 | ; | ||
198 | } else if (index == SAL_CACHE_FLUSH) { | ||
199 | ; | ||
200 | } else if (index == SAL_CACHE_INIT) { | ||
201 | ; | ||
202 | #ifdef CONFIG_PCI | ||
203 | } else if (index == SAL_PCI_CONFIG_READ) { | ||
204 | /* | ||
205 | * in1 contains the PCI configuration address and in2 | ||
206 | * the size of the read. The value that is read is | ||
207 | * returned via the general register r9. | ||
208 | */ | ||
209 | outl(BUILD_CMD(in1), 0xCF8); | ||
210 | if (in2 == 1) /* Reading byte */ | ||
211 | r9 = inb(0xCFC + ((REG_OFFSET(in1) & 3))); | ||
212 | else if (in2 == 2) /* Reading word */ | ||
213 | r9 = inw(0xCFC + ((REG_OFFSET(in1) & 2))); | ||
214 | else /* Reading dword */ | ||
215 | r9 = inl(0xCFC); | ||
216 | status = PCIBIOS_SUCCESSFUL; | ||
217 | } else if (index == SAL_PCI_CONFIG_WRITE) { | ||
218 | /* | ||
219 | * in1 contains the PCI configuration address, in2 the | ||
220 | * size of the write, and in3 the actual value to be | ||
221 | * written out. | ||
222 | */ | ||
223 | outl(BUILD_CMD(in1), 0xCF8); | ||
224 | if (in2 == 1) /* Writing byte */ | ||
225 | outb(in3, 0xCFC + ((REG_OFFSET(in1) & 3))); | ||
226 | else if (in2 == 2) /* Writing word */ | ||
227 | outw(in3, 0xCFC + ((REG_OFFSET(in1) & 2))); | ||
228 | else /* Writing dword */ | ||
229 | outl(in3, 0xCFC); | ||
230 | status = PCIBIOS_SUCCESSFUL; | ||
231 | #endif /* CONFIG_PCI */ | ||
232 | } else if (index == SAL_UPDATE_PAL) { | ||
233 | ; | ||
234 | } else { | ||
235 | status = -1; | ||
236 | } | ||
237 | return ((struct sal_ret_values) {status, r9, r10, r11}); | ||
238 | } | ||
239 | |||
240 | |||
241 | /* | ||
242 | * This is here to work around a bug in egcs-1.1.1b that causes the | ||
243 | * compiler to crash (seems like a bug in the new alias analysis code. | ||
244 | */ | ||
245 | void * | ||
246 | id (long addr) | ||
247 | { | ||
248 | return (void *) addr; | ||
249 | } | ||
250 | |||
251 | struct ia64_boot_param * | ||
252 | sys_fw_init (const char *args, int arglen) | ||
253 | { | ||
254 | efi_system_table_t *efi_systab; | ||
255 | efi_runtime_services_t *efi_runtime; | ||
256 | efi_config_table_t *efi_tables; | ||
257 | struct ia64_sal_systab *sal_systab; | ||
258 | efi_memory_desc_t *efi_memmap, *md; | ||
259 | unsigned long *pal_desc, *sal_desc; | ||
260 | struct ia64_sal_desc_entry_point *sal_ed; | ||
261 | struct ia64_boot_param *bp; | ||
262 | unsigned char checksum = 0; | ||
263 | char *cp, *cmd_line; | ||
264 | int i = 0; | ||
265 | # define MAKE_MD(typ, attr, start, end) \ | ||
266 | do { \ | ||
267 | md = efi_memmap + i++; \ | ||
268 | md->type = typ; \ | ||
269 | md->pad = 0; \ | ||
270 | md->phys_addr = start; \ | ||
271 | md->virt_addr = 0; \ | ||
272 | md->num_pages = (end - start) >> 12; \ | ||
273 | md->attribute = attr; \ | ||
274 | } while (0) | ||
275 | |||
276 | memset(fw_mem, 0, sizeof(fw_mem)); | ||
277 | |||
278 | pal_desc = (unsigned long *) &pal_emulator_static; | ||
279 | sal_desc = (unsigned long *) &sal_emulator; | ||
280 | |||
281 | cp = fw_mem; | ||
282 | efi_systab = (void *) cp; cp += sizeof(*efi_systab); | ||
283 | efi_runtime = (void *) cp; cp += sizeof(*efi_runtime); | ||
284 | efi_tables = (void *) cp; cp += sizeof(*efi_tables); | ||
285 | sal_systab = (void *) cp; cp += sizeof(*sal_systab); | ||
286 | sal_ed = (void *) cp; cp += sizeof(*sal_ed); | ||
287 | efi_memmap = (void *) cp; cp += NUM_MEM_DESCS*sizeof(*efi_memmap); | ||
288 | bp = (void *) cp; cp += sizeof(*bp); | ||
289 | cmd_line = (void *) cp; | ||
290 | |||
291 | if (args) { | ||
292 | if (arglen >= 1024) | ||
293 | arglen = 1023; | ||
294 | memcpy(cmd_line, args, arglen); | ||
295 | } else { | ||
296 | arglen = 0; | ||
297 | } | ||
298 | cmd_line[arglen] = '\0'; | ||
299 | |||
300 | memset(efi_systab, 0, sizeof(efi_systab)); | ||
301 | efi_systab->hdr.signature = EFI_SYSTEM_TABLE_SIGNATURE; | ||
302 | efi_systab->hdr.revision = EFI_SYSTEM_TABLE_REVISION; | ||
303 | efi_systab->hdr.headersize = sizeof(efi_systab->hdr); | ||
304 | efi_systab->fw_vendor = __pa("H\0e\0w\0l\0e\0t\0t\0-\0P\0a\0c\0k\0a\0r\0d\0\0"); | ||
305 | efi_systab->fw_revision = 1; | ||
306 | efi_systab->runtime = (void *) __pa(efi_runtime); | ||
307 | efi_systab->nr_tables = 1; | ||
308 | efi_systab->tables = __pa(efi_tables); | ||
309 | |||
310 | efi_runtime->hdr.signature = EFI_RUNTIME_SERVICES_SIGNATURE; | ||
311 | efi_runtime->hdr.revision = EFI_RUNTIME_SERVICES_REVISION; | ||
312 | efi_runtime->hdr.headersize = sizeof(efi_runtime->hdr); | ||
313 | efi_runtime->get_time = __pa(&fw_efi_get_time); | ||
314 | efi_runtime->set_time = __pa(&efi_unimplemented); | ||
315 | efi_runtime->get_wakeup_time = __pa(&efi_unimplemented); | ||
316 | efi_runtime->set_wakeup_time = __pa(&efi_unimplemented); | ||
317 | efi_runtime->set_virtual_address_map = __pa(&efi_unimplemented); | ||
318 | efi_runtime->get_variable = __pa(&efi_unimplemented); | ||
319 | efi_runtime->get_next_variable = __pa(&efi_unimplemented); | ||
320 | efi_runtime->set_variable = __pa(&efi_unimplemented); | ||
321 | efi_runtime->get_next_high_mono_count = __pa(&efi_unimplemented); | ||
322 | efi_runtime->reset_system = __pa(&efi_reset_system); | ||
323 | |||
324 | efi_tables->guid = SAL_SYSTEM_TABLE_GUID; | ||
325 | efi_tables->table = __pa(sal_systab); | ||
326 | |||
327 | /* fill in the SAL system table: */ | ||
328 | memcpy(sal_systab->signature, "SST_", 4); | ||
329 | sal_systab->size = sizeof(*sal_systab); | ||
330 | sal_systab->sal_rev_minor = 1; | ||
331 | sal_systab->sal_rev_major = 0; | ||
332 | sal_systab->entry_count = 1; | ||
333 | |||
334 | #ifdef CONFIG_IA64_GENERIC | ||
335 | strcpy(sal_systab->oem_id, "Generic"); | ||
336 | strcpy(sal_systab->product_id, "IA-64 system"); | ||
337 | #endif | ||
338 | |||
339 | #ifdef CONFIG_IA64_HP_SIM | ||
340 | strcpy(sal_systab->oem_id, "Hewlett-Packard"); | ||
341 | strcpy(sal_systab->product_id, "HP-simulator"); | ||
342 | #endif | ||
343 | |||
344 | #ifdef CONFIG_IA64_SDV | ||
345 | strcpy(sal_systab->oem_id, "Intel"); | ||
346 | strcpy(sal_systab->product_id, "SDV"); | ||
347 | #endif | ||
348 | |||
349 | /* fill in an entry point: */ | ||
350 | sal_ed->type = SAL_DESC_ENTRY_POINT; | ||
351 | sal_ed->pal_proc = __pa(pal_desc[0]); | ||
352 | sal_ed->sal_proc = __pa(sal_desc[0]); | ||
353 | sal_ed->gp = __pa(sal_desc[1]); | ||
354 | |||
355 | for (cp = (char *) sal_systab; cp < (char *) efi_memmap; ++cp) | ||
356 | checksum += *cp; | ||
357 | |||
358 | sal_systab->checksum = -checksum; | ||
359 | |||
360 | #if SIMPLE_MEMMAP | ||
361 | /* simulate free memory at physical address zero */ | ||
362 | MAKE_MD(EFI_BOOT_SERVICES_DATA, EFI_MEMORY_WB, 0*MB, 1*MB); | ||
363 | MAKE_MD(EFI_PAL_CODE, EFI_MEMORY_WB, 1*MB, 2*MB); | ||
364 | MAKE_MD(EFI_CONVENTIONAL_MEMORY, EFI_MEMORY_WB, 2*MB, 130*MB); | ||
365 | MAKE_MD(EFI_CONVENTIONAL_MEMORY, EFI_MEMORY_WB, 4096*MB, 4128*MB); | ||
366 | #else | ||
367 | MAKE_MD( 4, 0x9, 0x0000000000000000, 0x0000000000001000); | ||
368 | MAKE_MD( 7, 0x9, 0x0000000000001000, 0x000000000008a000); | ||
369 | MAKE_MD( 4, 0x9, 0x000000000008a000, 0x00000000000a0000); | ||
370 | MAKE_MD( 5, 0x8000000000000009, 0x00000000000c0000, 0x0000000000100000); | ||
371 | MAKE_MD( 7, 0x9, 0x0000000000100000, 0x0000000004400000); | ||
372 | MAKE_MD( 2, 0x9, 0x0000000004400000, 0x0000000004be5000); | ||
373 | MAKE_MD( 7, 0x9, 0x0000000004be5000, 0x000000007f77e000); | ||
374 | MAKE_MD( 6, 0x8000000000000009, 0x000000007f77e000, 0x000000007fb94000); | ||
375 | MAKE_MD( 6, 0x8000000000000009, 0x000000007fb94000, 0x000000007fb95000); | ||
376 | MAKE_MD( 6, 0x8000000000000009, 0x000000007fb95000, 0x000000007fc00000); | ||
377 | MAKE_MD(13, 0x8000000000000009, 0x000000007fc00000, 0x000000007fc3a000); | ||
378 | MAKE_MD( 7, 0x9, 0x000000007fc3a000, 0x000000007fea0000); | ||
379 | MAKE_MD( 5, 0x8000000000000009, 0x000000007fea0000, 0x000000007fea8000); | ||
380 | MAKE_MD( 7, 0x9, 0x000000007fea8000, 0x000000007feab000); | ||
381 | MAKE_MD( 5, 0x8000000000000009, 0x000000007feab000, 0x000000007ffff000); | ||
382 | MAKE_MD( 7, 0x9, 0x00000000ff400000, 0x0000000104000000); | ||
383 | #endif | ||
384 | |||
385 | bp->efi_systab = __pa(&fw_mem); | ||
386 | bp->efi_memmap = __pa(efi_memmap); | ||
387 | bp->efi_memmap_size = NUM_MEM_DESCS*sizeof(efi_memory_desc_t); | ||
388 | bp->efi_memdesc_size = sizeof(efi_memory_desc_t); | ||
389 | bp->efi_memdesc_version = 1; | ||
390 | bp->command_line = __pa(cmd_line); | ||
391 | bp->console_info.num_cols = 80; | ||
392 | bp->console_info.num_rows = 25; | ||
393 | bp->console_info.orig_x = 0; | ||
394 | bp->console_info.orig_y = 24; | ||
395 | bp->fpswa = 0; | ||
396 | |||
397 | return bp; | ||
398 | } | ||
diff --git a/arch/ia64/hp/sim/boot/ssc.h b/arch/ia64/hp/sim/boot/ssc.h new file mode 100644 index 000000000000..3b94c03e43a9 --- /dev/null +++ b/arch/ia64/hp/sim/boot/ssc.h | |||
@@ -0,0 +1,35 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1998-2003 Hewlett-Packard Co | ||
3 | * David Mosberger-Tang <davidm@hpl.hp.com> | ||
4 | * Stephane Eranian <eranian@hpl.hp.com> | ||
5 | */ | ||
6 | #ifndef ssc_h | ||
7 | #define ssc_h | ||
8 | |||
9 | /* Simulator system calls: */ | ||
10 | |||
11 | #define SSC_CONSOLE_INIT 20 | ||
12 | #define SSC_GETCHAR 21 | ||
13 | #define SSC_PUTCHAR 31 | ||
14 | #define SSC_OPEN 50 | ||
15 | #define SSC_CLOSE 51 | ||
16 | #define SSC_READ 52 | ||
17 | #define SSC_WRITE 53 | ||
18 | #define SSC_GET_COMPLETION 54 | ||
19 | #define SSC_WAIT_COMPLETION 55 | ||
20 | #define SSC_CONNECT_INTERRUPT 58 | ||
21 | #define SSC_GENERATE_INTERRUPT 59 | ||
22 | #define SSC_SET_PERIODIC_INTERRUPT 60 | ||
23 | #define SSC_GET_RTC 65 | ||
24 | #define SSC_EXIT 66 | ||
25 | #define SSC_LOAD_SYMBOLS 69 | ||
26 | #define SSC_GET_TOD 74 | ||
27 | |||
28 | #define SSC_GET_ARGS 75 | ||
29 | |||
30 | /* | ||
31 | * Simulator system call. | ||
32 | */ | ||
33 | extern long ssc (long arg0, long arg1, long arg2, long arg3, int nr); | ||
34 | |||
35 | #endif /* ssc_h */ | ||
diff --git a/arch/ia64/hp/sim/hpsim.S b/arch/ia64/hp/sim/hpsim.S new file mode 100644 index 000000000000..ff16e8a857d1 --- /dev/null +++ b/arch/ia64/hp/sim/hpsim.S | |||
@@ -0,0 +1,10 @@ | |||
1 | #include <asm/asmmacro.h> | ||
2 | |||
3 | /* | ||
4 | * Simulator system call. | ||
5 | */ | ||
6 | GLOBAL_ENTRY(ia64_ssc) | ||
7 | mov r15=r36 | ||
8 | break 0x80001 | ||
9 | br.ret.sptk.many rp | ||
10 | END(ia64_ssc) | ||
diff --git a/arch/ia64/hp/sim/hpsim_console.c b/arch/ia64/hp/sim/hpsim_console.c new file mode 100644 index 000000000000..5deff21e5877 --- /dev/null +++ b/arch/ia64/hp/sim/hpsim_console.c | |||
@@ -0,0 +1,65 @@ | |||
1 | /* | ||
2 | * Platform dependent support for HP simulator. | ||
3 | * | ||
4 | * Copyright (C) 1998, 1999, 2002 Hewlett-Packard Co | ||
5 | * David Mosberger-Tang <davidm@hpl.hp.com> | ||
6 | * Copyright (C) 1999 Vijay Chander <vijay@engr.sgi.com> | ||
7 | */ | ||
8 | #include <linux/config.h> | ||
9 | |||
10 | #include <linux/init.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/param.h> | ||
13 | #include <linux/string.h> | ||
14 | #include <linux/types.h> | ||
15 | #include <linux/tty.h> | ||
16 | #include <linux/kdev_t.h> | ||
17 | #include <linux/console.h> | ||
18 | |||
19 | #include <asm/delay.h> | ||
20 | #include <asm/irq.h> | ||
21 | #include <asm/pal.h> | ||
22 | #include <asm/machvec.h> | ||
23 | #include <asm/pgtable.h> | ||
24 | #include <asm/sal.h> | ||
25 | |||
26 | #include "hpsim_ssc.h" | ||
27 | |||
28 | static int simcons_init (struct console *, char *); | ||
29 | static void simcons_write (struct console *, const char *, unsigned); | ||
30 | static struct tty_driver *simcons_console_device (struct console *, int *); | ||
31 | |||
32 | struct console hpsim_cons = { | ||
33 | .name = "simcons", | ||
34 | .write = simcons_write, | ||
35 | .device = simcons_console_device, | ||
36 | .setup = simcons_init, | ||
37 | .flags = CON_PRINTBUFFER, | ||
38 | .index = -1, | ||
39 | }; | ||
40 | |||
41 | static int | ||
42 | simcons_init (struct console *cons, char *options) | ||
43 | { | ||
44 | return 0; | ||
45 | } | ||
46 | |||
47 | static void | ||
48 | simcons_write (struct console *cons, const char *buf, unsigned count) | ||
49 | { | ||
50 | unsigned long ch; | ||
51 | |||
52 | while (count-- > 0) { | ||
53 | ch = *buf++; | ||
54 | ia64_ssc(ch, 0, 0, 0, SSC_PUTCHAR); | ||
55 | if (ch == '\n') | ||
56 | ia64_ssc('\r', 0, 0, 0, SSC_PUTCHAR); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | static struct tty_driver *simcons_console_device (struct console *c, int *index) | ||
61 | { | ||
62 | extern struct tty_driver *hp_simserial_driver; | ||
63 | *index = c->index; | ||
64 | return hp_simserial_driver; | ||
65 | } | ||
diff --git a/arch/ia64/hp/sim/hpsim_irq.c b/arch/ia64/hp/sim/hpsim_irq.c new file mode 100644 index 000000000000..c0d25a2a3e9c --- /dev/null +++ b/arch/ia64/hp/sim/hpsim_irq.c | |||
@@ -0,0 +1,51 @@ | |||
1 | /* | ||
2 | * Platform dependent support for HP simulator. | ||
3 | * | ||
4 | * Copyright (C) 1998-2001 Hewlett-Packard Co | ||
5 | * Copyright (C) 1998-2001 David Mosberger-Tang <davidm@hpl.hp.com> | ||
6 | */ | ||
7 | |||
8 | #include <linux/init.h> | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/sched.h> | ||
11 | #include <linux/irq.h> | ||
12 | |||
13 | static unsigned int | ||
14 | hpsim_irq_startup (unsigned int irq) | ||
15 | { | ||
16 | return 0; | ||
17 | } | ||
18 | |||
19 | static void | ||
20 | hpsim_irq_noop (unsigned int irq) | ||
21 | { | ||
22 | } | ||
23 | |||
24 | static void | ||
25 | hpsim_set_affinity_noop (unsigned int a, cpumask_t b) | ||
26 | { | ||
27 | } | ||
28 | |||
29 | static struct hw_interrupt_type irq_type_hp_sim = { | ||
30 | .typename = "hpsim", | ||
31 | .startup = hpsim_irq_startup, | ||
32 | .shutdown = hpsim_irq_noop, | ||
33 | .enable = hpsim_irq_noop, | ||
34 | .disable = hpsim_irq_noop, | ||
35 | .ack = hpsim_irq_noop, | ||
36 | .end = hpsim_irq_noop, | ||
37 | .set_affinity = hpsim_set_affinity_noop, | ||
38 | }; | ||
39 | |||
40 | void __init | ||
41 | hpsim_irq_init (void) | ||
42 | { | ||
43 | irq_desc_t *idesc; | ||
44 | int i; | ||
45 | |||
46 | for (i = 0; i < NR_IRQS; ++i) { | ||
47 | idesc = irq_descp(i); | ||
48 | if (idesc->handler == &no_irq_type) | ||
49 | idesc->handler = &irq_type_hp_sim; | ||
50 | } | ||
51 | } | ||
diff --git a/arch/ia64/hp/sim/hpsim_machvec.c b/arch/ia64/hp/sim/hpsim_machvec.c new file mode 100644 index 000000000000..c21419359185 --- /dev/null +++ b/arch/ia64/hp/sim/hpsim_machvec.c | |||
@@ -0,0 +1,3 @@ | |||
1 | #define MACHVEC_PLATFORM_NAME hpsim | ||
2 | #define MACHVEC_PLATFORM_HEADER <asm/machvec_hpsim.h> | ||
3 | #include <asm/machvec_init.h> | ||
diff --git a/arch/ia64/hp/sim/hpsim_setup.c b/arch/ia64/hp/sim/hpsim_setup.c new file mode 100644 index 000000000000..694fc86bfbd5 --- /dev/null +++ b/arch/ia64/hp/sim/hpsim_setup.c | |||
@@ -0,0 +1,52 @@ | |||
1 | /* | ||
2 | * Platform dependent support for HP simulator. | ||
3 | * | ||
4 | * Copyright (C) 1998, 1999, 2002 Hewlett-Packard Co | ||
5 | * David Mosberger-Tang <davidm@hpl.hp.com> | ||
6 | * Copyright (C) 1999 Vijay Chander <vijay@engr.sgi.com> | ||
7 | */ | ||
8 | #include <linux/config.h> | ||
9 | #include <linux/console.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/kdev_t.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/major.h> | ||
14 | #include <linux/param.h> | ||
15 | #include <linux/root_dev.h> | ||
16 | #include <linux/string.h> | ||
17 | #include <linux/types.h> | ||
18 | |||
19 | #include <asm/delay.h> | ||
20 | #include <asm/irq.h> | ||
21 | #include <asm/pal.h> | ||
22 | #include <asm/machvec.h> | ||
23 | #include <asm/pgtable.h> | ||
24 | #include <asm/sal.h> | ||
25 | |||
26 | #include "hpsim_ssc.h" | ||
27 | |||
28 | void | ||
29 | ia64_ssc_connect_irq (long intr, long irq) | ||
30 | { | ||
31 | ia64_ssc(intr, irq, 0, 0, SSC_CONNECT_INTERRUPT); | ||
32 | } | ||
33 | |||
34 | void | ||
35 | ia64_ctl_trace (long on) | ||
36 | { | ||
37 | ia64_ssc(on, 0, 0, 0, SSC_CTL_TRACE); | ||
38 | } | ||
39 | |||
40 | void __init | ||
41 | hpsim_setup (char **cmdline_p) | ||
42 | { | ||
43 | ROOT_DEV = Root_SDA1; /* default to first SCSI drive */ | ||
44 | |||
45 | #ifdef CONFIG_HP_SIMSERIAL_CONSOLE | ||
46 | { | ||
47 | extern struct console hpsim_cons; | ||
48 | if (ia64_platform_is("hpsim")) | ||
49 | register_console(&hpsim_cons); | ||
50 | } | ||
51 | #endif | ||
52 | } | ||
diff --git a/arch/ia64/hp/sim/hpsim_ssc.h b/arch/ia64/hp/sim/hpsim_ssc.h new file mode 100644 index 000000000000..bfa3906274b3 --- /dev/null +++ b/arch/ia64/hp/sim/hpsim_ssc.h | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | * Platform dependent support for HP simulator. | ||
3 | * | ||
4 | * Copyright (C) 1998, 1999 Hewlett-Packard Co | ||
5 | * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com> | ||
6 | * Copyright (C) 1999 Vijay Chander <vijay@engr.sgi.com> | ||
7 | */ | ||
8 | #ifndef _IA64_PLATFORM_HPSIM_SSC_H | ||
9 | #define _IA64_PLATFORM_HPSIM_SSC_H | ||
10 | |||
11 | /* Simulator system calls: */ | ||
12 | |||
13 | #define SSC_CONSOLE_INIT 20 | ||
14 | #define SSC_GETCHAR 21 | ||
15 | #define SSC_PUTCHAR 31 | ||
16 | #define SSC_CONNECT_INTERRUPT 58 | ||
17 | #define SSC_GENERATE_INTERRUPT 59 | ||
18 | #define SSC_SET_PERIODIC_INTERRUPT 60 | ||
19 | #define SSC_GET_RTC 65 | ||
20 | #define SSC_EXIT 66 | ||
21 | #define SSC_LOAD_SYMBOLS 69 | ||
22 | #define SSC_GET_TOD 74 | ||
23 | #define SSC_CTL_TRACE 76 | ||
24 | |||
25 | #define SSC_NETDEV_PROBE 100 | ||
26 | #define SSC_NETDEV_SEND 101 | ||
27 | #define SSC_NETDEV_RECV 102 | ||
28 | #define SSC_NETDEV_ATTACH 103 | ||
29 | #define SSC_NETDEV_DETACH 104 | ||
30 | |||
31 | /* | ||
32 | * Simulator system call. | ||
33 | */ | ||
34 | extern long ia64_ssc (long arg0, long arg1, long arg2, long arg3, int nr); | ||
35 | |||
36 | #endif /* _IA64_PLATFORM_HPSIM_SSC_H */ | ||
diff --git a/arch/ia64/hp/sim/simeth.c b/arch/ia64/hp/sim/simeth.c new file mode 100644 index 000000000000..ae84a1018a89 --- /dev/null +++ b/arch/ia64/hp/sim/simeth.c | |||
@@ -0,0 +1,530 @@ | |||
1 | /* | ||
2 | * Simulated Ethernet Driver | ||
3 | * | ||
4 | * Copyright (C) 1999-2001, 2003 Hewlett-Packard Co | ||
5 | * Stephane Eranian <eranian@hpl.hp.com> | ||
6 | */ | ||
7 | #include <linux/config.h> | ||
8 | #include <linux/kernel.h> | ||
9 | #include <linux/sched.h> | ||
10 | #include <linux/types.h> | ||
11 | #include <linux/in.h> | ||
12 | #include <linux/string.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/netdevice.h> | ||
17 | #include <linux/etherdevice.h> | ||
18 | #include <linux/inetdevice.h> | ||
19 | #include <linux/if_ether.h> | ||
20 | #include <linux/if_arp.h> | ||
21 | #include <linux/skbuff.h> | ||
22 | #include <linux/notifier.h> | ||
23 | #include <linux/bitops.h> | ||
24 | #include <asm/system.h> | ||
25 | #include <asm/irq.h> | ||
26 | |||
27 | #define SIMETH_RECV_MAX 10 | ||
28 | |||
29 | /* | ||
30 | * Maximum possible received frame for Ethernet. | ||
31 | * We preallocate an sk_buff of that size to avoid costly | ||
32 | * memcpy for temporary buffer into sk_buff. We do basically | ||
33 | * what's done in other drivers, like eepro with a ring. | ||
34 | * The difference is, of course, that we don't have real DMA !!! | ||
35 | */ | ||
36 | #define SIMETH_FRAME_SIZE ETH_FRAME_LEN | ||
37 | |||
38 | |||
39 | #define SSC_NETDEV_PROBE 100 | ||
40 | #define SSC_NETDEV_SEND 101 | ||
41 | #define SSC_NETDEV_RECV 102 | ||
42 | #define SSC_NETDEV_ATTACH 103 | ||
43 | #define SSC_NETDEV_DETACH 104 | ||
44 | |||
45 | #define NETWORK_INTR 8 | ||
46 | |||
47 | struct simeth_local { | ||
48 | struct net_device_stats stats; | ||
49 | int simfd; /* descriptor in the simulator */ | ||
50 | }; | ||
51 | |||
52 | static int simeth_probe1(void); | ||
53 | static int simeth_open(struct net_device *dev); | ||
54 | static int simeth_close(struct net_device *dev); | ||
55 | static int simeth_tx(struct sk_buff *skb, struct net_device *dev); | ||
56 | static int simeth_rx(struct net_device *dev); | ||
57 | static struct net_device_stats *simeth_get_stats(struct net_device *dev); | ||
58 | static irqreturn_t simeth_interrupt(int irq, void *dev_id, struct pt_regs * regs); | ||
59 | static void set_multicast_list(struct net_device *dev); | ||
60 | static int simeth_device_event(struct notifier_block *this,unsigned long event, void *ptr); | ||
61 | |||
62 | static char *simeth_version="0.3"; | ||
63 | |||
64 | /* | ||
65 | * This variable is used to establish a mapping between the Linux/ia64 kernel | ||
66 | * and the host linux kernel. | ||
67 | * | ||
68 | * As of today, we support only one card, even though most of the code | ||
69 | * is ready for many more. The mapping is then: | ||
70 | * linux/ia64 -> linux/x86 | ||
71 | * eth0 -> eth1 | ||
72 | * | ||
73 | * In the future, we some string operations, we could easily support up | ||
74 | * to 10 cards (0-9). | ||
75 | * | ||
76 | * The default mapping can be changed on the kernel command line by | ||
77 | * specifying simeth=ethX (or whatever string you want). | ||
78 | */ | ||
79 | static char *simeth_device="eth0"; /* default host interface to use */ | ||
80 | |||
81 | |||
82 | |||
83 | static volatile unsigned int card_count; /* how many cards "found" so far */ | ||
84 | static int simeth_debug; /* set to 1 to get debug information */ | ||
85 | |||
86 | /* | ||
87 | * Used to catch IFF_UP & IFF_DOWN events | ||
88 | */ | ||
89 | static struct notifier_block simeth_dev_notifier = { | ||
90 | simeth_device_event, | ||
91 | 0 | ||
92 | }; | ||
93 | |||
94 | |||
95 | /* | ||
96 | * Function used when using a kernel command line option. | ||
97 | * | ||
98 | * Format: simeth=interface_name (like eth0) | ||
99 | */ | ||
100 | static int __init | ||
101 | simeth_setup(char *str) | ||
102 | { | ||
103 | simeth_device = str; | ||
104 | return 1; | ||
105 | } | ||
106 | |||
107 | __setup("simeth=", simeth_setup); | ||
108 | |||
109 | /* | ||
110 | * Function used to probe for simeth devices when not installed | ||
111 | * as a loadable module | ||
112 | */ | ||
113 | |||
114 | int __init | ||
115 | simeth_probe (void) | ||
116 | { | ||
117 | int r; | ||
118 | |||
119 | printk(KERN_INFO "simeth: v%s\n", simeth_version); | ||
120 | |||
121 | r = simeth_probe1(); | ||
122 | |||
123 | if (r == 0) register_netdevice_notifier(&simeth_dev_notifier); | ||
124 | |||
125 | return r; | ||
126 | } | ||
127 | |||
128 | extern long ia64_ssc (long, long, long, long, int); | ||
129 | extern void ia64_ssc_connect_irq (long intr, long irq); | ||
130 | |||
131 | static inline int | ||
132 | netdev_probe(char *name, unsigned char *ether) | ||
133 | { | ||
134 | return ia64_ssc(__pa(name), __pa(ether), 0,0, SSC_NETDEV_PROBE); | ||
135 | } | ||
136 | |||
137 | |||
138 | static inline int | ||
139 | netdev_connect(int irq) | ||
140 | { | ||
141 | /* XXX Fix me | ||
142 | * this does not support multiple cards | ||
143 | * also no return value | ||
144 | */ | ||
145 | ia64_ssc_connect_irq(NETWORK_INTR, irq); | ||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | static inline int | ||
150 | netdev_attach(int fd, int irq, unsigned int ipaddr) | ||
151 | { | ||
152 | /* this puts the host interface in the right mode (start interrupting) */ | ||
153 | return ia64_ssc(fd, ipaddr, 0,0, SSC_NETDEV_ATTACH); | ||
154 | } | ||
155 | |||
156 | |||
157 | static inline int | ||
158 | netdev_detach(int fd) | ||
159 | { | ||
160 | /* | ||
161 | * inactivate the host interface (don't interrupt anymore) */ | ||
162 | return ia64_ssc(fd, 0,0,0, SSC_NETDEV_DETACH); | ||
163 | } | ||
164 | |||
165 | static inline int | ||
166 | netdev_send(int fd, unsigned char *buf, unsigned int len) | ||
167 | { | ||
168 | return ia64_ssc(fd, __pa(buf), len, 0, SSC_NETDEV_SEND); | ||
169 | } | ||
170 | |||
171 | static inline int | ||
172 | netdev_read(int fd, unsigned char *buf, unsigned int len) | ||
173 | { | ||
174 | return ia64_ssc(fd, __pa(buf), len, 0, SSC_NETDEV_RECV); | ||
175 | } | ||
176 | |||
177 | /* | ||
178 | * Function shared with module code, so cannot be in init section | ||
179 | * | ||
180 | * So far this function "detects" only one card (test_&_set) but could | ||
181 | * be extended easily. | ||
182 | * | ||
183 | * Return: | ||
184 | * - -ENODEV is no device found | ||
185 | * - -ENOMEM is no more memory | ||
186 | * - 0 otherwise | ||
187 | */ | ||
188 | static int | ||
189 | simeth_probe1(void) | ||
190 | { | ||
191 | unsigned char mac_addr[ETH_ALEN]; | ||
192 | struct simeth_local *local; | ||
193 | struct net_device *dev; | ||
194 | int fd, i, err; | ||
195 | |||
196 | /* | ||
197 | * XXX Fix me | ||
198 | * let's support just one card for now | ||
199 | */ | ||
200 | if (test_and_set_bit(0, &card_count)) | ||
201 | return -ENODEV; | ||
202 | |||
203 | /* | ||
204 | * check with the simulator for the device | ||
205 | */ | ||
206 | fd = netdev_probe(simeth_device, mac_addr); | ||
207 | if (fd == -1) | ||
208 | return -ENODEV; | ||
209 | |||
210 | dev = alloc_etherdev(sizeof(struct simeth_local)); | ||
211 | if (!dev) | ||
212 | return -ENOMEM; | ||
213 | |||
214 | memcpy(dev->dev_addr, mac_addr, sizeof(mac_addr)); | ||
215 | |||
216 | local = dev->priv; | ||
217 | local->simfd = fd; /* keep track of underlying file descriptor */ | ||
218 | |||
219 | dev->open = simeth_open; | ||
220 | dev->stop = simeth_close; | ||
221 | dev->hard_start_xmit = simeth_tx; | ||
222 | dev->get_stats = simeth_get_stats; | ||
223 | dev->set_multicast_list = set_multicast_list; /* no yet used */ | ||
224 | |||
225 | err = register_netdev(dev); | ||
226 | if (err) { | ||
227 | free_netdev(dev); | ||
228 | return err; | ||
229 | } | ||
230 | |||
231 | dev->irq = assign_irq_vector(AUTO_ASSIGN); | ||
232 | |||
233 | /* | ||
234 | * attach the interrupt in the simulator, this does enable interrupts | ||
235 | * until a netdev_attach() is called | ||
236 | */ | ||
237 | netdev_connect(dev->irq); | ||
238 | |||
239 | printk(KERN_INFO "%s: hosteth=%s simfd=%d, HwAddr", | ||
240 | dev->name, simeth_device, local->simfd); | ||
241 | for(i = 0; i < ETH_ALEN; i++) { | ||
242 | printk(" %2.2x", dev->dev_addr[i]); | ||
243 | } | ||
244 | printk(", IRQ %d\n", dev->irq); | ||
245 | |||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | /* | ||
250 | * actually binds the device to an interrupt vector | ||
251 | */ | ||
252 | static int | ||
253 | simeth_open(struct net_device *dev) | ||
254 | { | ||
255 | if (request_irq(dev->irq, simeth_interrupt, 0, "simeth", dev)) { | ||
256 | printk(KERN_WARNING "simeth: unable to get IRQ %d.\n", dev->irq); | ||
257 | return -EAGAIN; | ||
258 | } | ||
259 | |||
260 | netif_start_queue(dev); | ||
261 | |||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | /* copied from lapbether.c */ | ||
266 | static __inline__ int dev_is_ethdev(struct net_device *dev) | ||
267 | { | ||
268 | return ( dev->type == ARPHRD_ETHER && strncmp(dev->name, "dummy", 5)); | ||
269 | } | ||
270 | |||
271 | |||
272 | /* | ||
273 | * Handler for IFF_UP or IFF_DOWN | ||
274 | * | ||
275 | * The reason for that is that we don't want to be interrupted when the | ||
276 | * interface is down. There is no way to unconnect in the simualtor. Instead | ||
277 | * we use this function to shutdown packet processing in the frame filter | ||
278 | * in the simulator. Thus no interrupts are generated | ||
279 | * | ||
280 | * | ||
281 | * That's also the place where we pass the IP address of this device to the | ||
282 | * simulator so that that we can start filtering packets for it | ||
283 | * | ||
284 | * There may be a better way of doing this, but I don't know which yet. | ||
285 | */ | ||
286 | static int | ||
287 | simeth_device_event(struct notifier_block *this,unsigned long event, void *ptr) | ||
288 | { | ||
289 | struct net_device *dev = ptr; | ||
290 | struct simeth_local *local; | ||
291 | struct in_device *in_dev; | ||
292 | struct in_ifaddr **ifap = NULL; | ||
293 | struct in_ifaddr *ifa = NULL; | ||
294 | int r; | ||
295 | |||
296 | |||
297 | if ( ! dev ) { | ||
298 | printk(KERN_WARNING "simeth_device_event dev=0\n"); | ||
299 | return NOTIFY_DONE; | ||
300 | } | ||
301 | |||
302 | if ( event != NETDEV_UP && event != NETDEV_DOWN ) return NOTIFY_DONE; | ||
303 | |||
304 | /* | ||
305 | * Check whether or not it's for an ethernet device | ||
306 | * | ||
307 | * XXX Fixme: This works only as long as we support one | ||
308 | * type of ethernet device. | ||
309 | */ | ||
310 | if ( !dev_is_ethdev(dev) ) return NOTIFY_DONE; | ||
311 | |||
312 | if ((in_dev=dev->ip_ptr) != NULL) { | ||
313 | for (ifap=&in_dev->ifa_list; (ifa=*ifap) != NULL; ifap=&ifa->ifa_next) | ||
314 | if (strcmp(dev->name, ifa->ifa_label) == 0) break; | ||
315 | } | ||
316 | if ( ifa == NULL ) { | ||
317 | printk(KERN_ERR "simeth_open: can't find device %s's ifa\n", dev->name); | ||
318 | return NOTIFY_DONE; | ||
319 | } | ||
320 | |||
321 | printk(KERN_INFO "simeth_device_event: %s ipaddr=0x%x\n", | ||
322 | dev->name, htonl(ifa->ifa_local)); | ||
323 | |||
324 | /* | ||
325 | * XXX Fix me | ||
326 | * if the device was up, and we're simply reconfiguring it, not sure | ||
327 | * we get DOWN then UP. | ||
328 | */ | ||
329 | |||
330 | local = dev->priv; | ||
331 | /* now do it for real */ | ||
332 | r = event == NETDEV_UP ? | ||
333 | netdev_attach(local->simfd, dev->irq, htonl(ifa->ifa_local)): | ||
334 | netdev_detach(local->simfd); | ||
335 | |||
336 | printk(KERN_INFO "simeth: netdev_attach/detach: event=%s ->%d\n", | ||
337 | event == NETDEV_UP ? "attach":"detach", r); | ||
338 | |||
339 | return NOTIFY_DONE; | ||
340 | } | ||
341 | |||
342 | static int | ||
343 | simeth_close(struct net_device *dev) | ||
344 | { | ||
345 | netif_stop_queue(dev); | ||
346 | |||
347 | free_irq(dev->irq, dev); | ||
348 | |||
349 | return 0; | ||
350 | } | ||
351 | |||
352 | /* | ||
353 | * Only used for debug | ||
354 | */ | ||
355 | static void | ||
356 | frame_print(unsigned char *from, unsigned char *frame, int len) | ||
357 | { | ||
358 | int i; | ||
359 | |||
360 | printk("%s: (%d) %02x", from, len, frame[0] & 0xff); | ||
361 | for(i=1; i < 6; i++ ) { | ||
362 | printk(":%02x", frame[i] &0xff); | ||
363 | } | ||
364 | printk(" %2x", frame[6] &0xff); | ||
365 | for(i=7; i < 12; i++ ) { | ||
366 | printk(":%02x", frame[i] &0xff); | ||
367 | } | ||
368 | printk(" [%02x%02x]\n", frame[12], frame[13]); | ||
369 | |||
370 | for(i=14; i < len; i++ ) { | ||
371 | printk("%02x ", frame[i] &0xff); | ||
372 | if ( (i%10)==0) printk("\n"); | ||
373 | } | ||
374 | printk("\n"); | ||
375 | } | ||
376 | |||
377 | |||
378 | /* | ||
379 | * Function used to transmit of frame, very last one on the path before | ||
380 | * going to the simulator. | ||
381 | */ | ||
382 | static int | ||
383 | simeth_tx(struct sk_buff *skb, struct net_device *dev) | ||
384 | { | ||
385 | struct simeth_local *local = dev->priv; | ||
386 | |||
387 | #if 0 | ||
388 | /* ensure we have at least ETH_ZLEN bytes (min frame size) */ | ||
389 | unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; | ||
390 | /* Where do the extra padding bytes comes from inthe skbuff ? */ | ||
391 | #else | ||
392 | /* the real driver in the host system is going to take care of that | ||
393 | * or maybe it's the NIC itself. | ||
394 | */ | ||
395 | unsigned int length = skb->len; | ||
396 | #endif | ||
397 | |||
398 | local->stats.tx_bytes += skb->len; | ||
399 | local->stats.tx_packets++; | ||
400 | |||
401 | |||
402 | if (simeth_debug > 5) frame_print("simeth_tx", skb->data, length); | ||
403 | |||
404 | netdev_send(local->simfd, skb->data, length); | ||
405 | |||
406 | /* | ||
407 | * we are synchronous on write, so we don't simulate a | ||
408 | * trasnmit complete interrupt, thus we don't need to arm a tx | ||
409 | */ | ||
410 | |||
411 | dev_kfree_skb(skb); | ||
412 | return 0; | ||
413 | } | ||
414 | |||
415 | static inline struct sk_buff * | ||
416 | make_new_skb(struct net_device *dev) | ||
417 | { | ||
418 | struct sk_buff *nskb; | ||
419 | |||
420 | /* | ||
421 | * The +2 is used to make sure that the IP header is nicely | ||
422 | * aligned (on 4byte boundary I assume 14+2=16) | ||
423 | */ | ||
424 | nskb = dev_alloc_skb(SIMETH_FRAME_SIZE + 2); | ||
425 | if ( nskb == NULL ) { | ||
426 | printk(KERN_NOTICE "%s: memory squeeze. dropping packet.\n", dev->name); | ||
427 | return NULL; | ||
428 | } | ||
429 | nskb->dev = dev; | ||
430 | |||
431 | skb_reserve(nskb, 2); /* Align IP on 16 byte boundaries */ | ||
432 | |||
433 | skb_put(nskb,SIMETH_FRAME_SIZE); | ||
434 | |||
435 | return nskb; | ||
436 | } | ||
437 | |||
438 | /* | ||
439 | * called from interrupt handler to process a received frame | ||
440 | */ | ||
441 | static int | ||
442 | simeth_rx(struct net_device *dev) | ||
443 | { | ||
444 | struct simeth_local *local; | ||
445 | struct sk_buff *skb; | ||
446 | int len; | ||
447 | int rcv_count = SIMETH_RECV_MAX; | ||
448 | |||
449 | local = dev->priv; | ||
450 | /* | ||
451 | * the loop concept has been borrowed from other drivers | ||
452 | * looks to me like it's a throttling thing to avoid pushing to many | ||
453 | * packets at one time into the stack. Making sure we can process them | ||
454 | * upstream and make forward progress overall | ||
455 | */ | ||
456 | do { | ||
457 | if ( (skb=make_new_skb(dev)) == NULL ) { | ||
458 | printk(KERN_NOTICE "%s: memory squeeze. dropping packet.\n", dev->name); | ||
459 | local->stats.rx_dropped++; | ||
460 | return 0; | ||
461 | } | ||
462 | /* | ||
463 | * Read only one frame at a time | ||
464 | */ | ||
465 | len = netdev_read(local->simfd, skb->data, SIMETH_FRAME_SIZE); | ||
466 | if ( len == 0 ) { | ||
467 | if ( simeth_debug > 0 ) printk(KERN_WARNING "%s: count=%d netdev_read=0\n", | ||
468 | dev->name, SIMETH_RECV_MAX-rcv_count); | ||
469 | break; | ||
470 | } | ||
471 | #if 0 | ||
472 | /* | ||
473 | * XXX Fix me | ||
474 | * Should really do a csum+copy here | ||
475 | */ | ||
476 | memcpy(skb->data, frame, len); | ||
477 | #endif | ||
478 | skb->protocol = eth_type_trans(skb, dev); | ||
479 | |||
480 | if ( simeth_debug > 6 ) frame_print("simeth_rx", skb->data, len); | ||
481 | |||
482 | /* | ||
483 | * push the packet up & trigger software interrupt | ||
484 | */ | ||
485 | netif_rx(skb); | ||
486 | |||
487 | local->stats.rx_packets++; | ||
488 | local->stats.rx_bytes += len; | ||
489 | |||
490 | } while ( --rcv_count ); | ||
491 | |||
492 | return len; /* 0 = nothing left to read, otherwise, we can try again */ | ||
493 | } | ||
494 | |||
495 | /* | ||
496 | * Interrupt handler (Yes, we can do it too !!!) | ||
497 | */ | ||
498 | static irqreturn_t | ||
499 | simeth_interrupt(int irq, void *dev_id, struct pt_regs * regs) | ||
500 | { | ||
501 | struct net_device *dev = dev_id; | ||
502 | |||
503 | if ( dev == NULL ) { | ||
504 | printk(KERN_WARNING "simeth: irq %d for unknown device\n", irq); | ||
505 | return IRQ_NONE; | ||
506 | } | ||
507 | |||
508 | /* | ||
509 | * very simple loop because we get interrupts only when receiving | ||
510 | */ | ||
511 | while (simeth_rx(dev)); | ||
512 | return IRQ_HANDLED; | ||
513 | } | ||
514 | |||
515 | static struct net_device_stats * | ||
516 | simeth_get_stats(struct net_device *dev) | ||
517 | { | ||
518 | struct simeth_local *local = dev->priv; | ||
519 | |||
520 | return &local->stats; | ||
521 | } | ||
522 | |||
523 | /* fake multicast ability */ | ||
524 | static void | ||
525 | set_multicast_list(struct net_device *dev) | ||
526 | { | ||
527 | printk(KERN_WARNING "%s: set_multicast_list called\n", dev->name); | ||
528 | } | ||
529 | |||
530 | __initcall(simeth_probe); | ||
diff --git a/arch/ia64/hp/sim/simscsi.c b/arch/ia64/hp/sim/simscsi.c new file mode 100644 index 000000000000..56405dbfd739 --- /dev/null +++ b/arch/ia64/hp/sim/simscsi.c | |||
@@ -0,0 +1,404 @@ | |||
1 | /* | ||
2 | * Simulated SCSI driver. | ||
3 | * | ||
4 | * Copyright (C) 1999, 2001-2003 Hewlett-Packard Co | ||
5 | * David Mosberger-Tang <davidm@hpl.hp.com> | ||
6 | * Stephane Eranian <eranian@hpl.hp.com> | ||
7 | * | ||
8 | * 02/01/15 David Mosberger Updated for v2.5.1 | ||
9 | * 99/12/18 David Mosberger Added support for READ10/WRITE10 needed by linux v2.3.33 | ||
10 | */ | ||
11 | #include <linux/blkdev.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/interrupt.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/timer.h> | ||
16 | #include <asm/irq.h> | ||
17 | |||
18 | #include <scsi/scsi.h> | ||
19 | #include <scsi/scsi_cmnd.h> | ||
20 | #include <scsi/scsi_device.h> | ||
21 | #include <scsi/scsi_host.h> | ||
22 | |||
23 | #define DEBUG_SIMSCSI 0 | ||
24 | |||
25 | #define SIMSCSI_REQ_QUEUE_LEN 64 | ||
26 | #define DEFAULT_SIMSCSI_ROOT "/var/ski-disks/sd" | ||
27 | |||
28 | /* Simulator system calls: */ | ||
29 | |||
30 | #define SSC_OPEN 50 | ||
31 | #define SSC_CLOSE 51 | ||
32 | #define SSC_READ 52 | ||
33 | #define SSC_WRITE 53 | ||
34 | #define SSC_GET_COMPLETION 54 | ||
35 | #define SSC_WAIT_COMPLETION 55 | ||
36 | |||
37 | #define SSC_WRITE_ACCESS 2 | ||
38 | #define SSC_READ_ACCESS 1 | ||
39 | |||
40 | #if DEBUG_SIMSCSI | ||
41 | int simscsi_debug; | ||
42 | # define DBG simscsi_debug | ||
43 | #else | ||
44 | # define DBG 0 | ||
45 | #endif | ||
46 | |||
47 | static struct Scsi_Host *host; | ||
48 | |||
49 | static void simscsi_interrupt (unsigned long val); | ||
50 | static DECLARE_TASKLET(simscsi_tasklet, simscsi_interrupt, 0); | ||
51 | |||
52 | struct disk_req { | ||
53 | unsigned long addr; | ||
54 | unsigned len; | ||
55 | }; | ||
56 | |||
57 | struct disk_stat { | ||
58 | int fd; | ||
59 | unsigned count; | ||
60 | }; | ||
61 | |||
62 | extern long ia64_ssc (long arg0, long arg1, long arg2, long arg3, int nr); | ||
63 | |||
64 | static int desc[16] = { | ||
65 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 | ||
66 | }; | ||
67 | |||
68 | static struct queue_entry { | ||
69 | struct scsi_cmnd *sc; | ||
70 | } queue[SIMSCSI_REQ_QUEUE_LEN]; | ||
71 | |||
72 | static int rd, wr; | ||
73 | static atomic_t num_reqs = ATOMIC_INIT(0); | ||
74 | |||
75 | /* base name for default disks */ | ||
76 | static char *simscsi_root = DEFAULT_SIMSCSI_ROOT; | ||
77 | |||
78 | #define MAX_ROOT_LEN 128 | ||
79 | |||
80 | /* | ||
81 | * used to setup a new base for disk images | ||
82 | * to use /foo/bar/disk[a-z] as disk images | ||
83 | * you have to specify simscsi=/foo/bar/disk on the command line | ||
84 | */ | ||
85 | static int __init | ||
86 | simscsi_setup (char *s) | ||
87 | { | ||
88 | /* XXX Fix me we may need to strcpy() ? */ | ||
89 | if (strlen(s) > MAX_ROOT_LEN) { | ||
90 | printk(KERN_ERR "simscsi_setup: prefix too long---using default %s\n", | ||
91 | simscsi_root); | ||
92 | } | ||
93 | simscsi_root = s; | ||
94 | return 1; | ||
95 | } | ||
96 | |||
97 | __setup("simscsi=", simscsi_setup); | ||
98 | |||
99 | static void | ||
100 | simscsi_interrupt (unsigned long val) | ||
101 | { | ||
102 | struct scsi_cmnd *sc; | ||
103 | |||
104 | while ((sc = queue[rd].sc) != 0) { | ||
105 | atomic_dec(&num_reqs); | ||
106 | queue[rd].sc = 0; | ||
107 | if (DBG) | ||
108 | printk("simscsi_interrupt: done with %ld\n", sc->serial_number); | ||
109 | (*sc->scsi_done)(sc); | ||
110 | rd = (rd + 1) % SIMSCSI_REQ_QUEUE_LEN; | ||
111 | } | ||
112 | } | ||
113 | |||
114 | static int | ||
115 | simscsi_biosparam (struct scsi_device *sdev, struct block_device *n, | ||
116 | sector_t capacity, int ip[]) | ||
117 | { | ||
118 | ip[0] = 64; /* heads */ | ||
119 | ip[1] = 32; /* sectors */ | ||
120 | ip[2] = capacity >> 11; /* cylinders */ | ||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | static void | ||
125 | simscsi_readwrite (struct scsi_cmnd *sc, int mode, unsigned long offset, unsigned long len) | ||
126 | { | ||
127 | struct disk_stat stat; | ||
128 | struct disk_req req; | ||
129 | |||
130 | req.addr = __pa(sc->request_buffer); | ||
131 | req.len = len; /* # of bytes to transfer */ | ||
132 | |||
133 | if (sc->request_bufflen < req.len) | ||
134 | return; | ||
135 | |||
136 | stat.fd = desc[sc->device->id]; | ||
137 | if (DBG) | ||
138 | printk("simscsi_%s @ %lx (off %lx)\n", | ||
139 | mode == SSC_READ ? "read":"write", req.addr, offset); | ||
140 | ia64_ssc(stat.fd, 1, __pa(&req), offset, mode); | ||
141 | ia64_ssc(__pa(&stat), 0, 0, 0, SSC_WAIT_COMPLETION); | ||
142 | |||
143 | if (stat.count == req.len) { | ||
144 | sc->result = GOOD; | ||
145 | } else { | ||
146 | sc->result = DID_ERROR << 16; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | static void | ||
151 | simscsi_sg_readwrite (struct scsi_cmnd *sc, int mode, unsigned long offset) | ||
152 | { | ||
153 | int list_len = sc->use_sg; | ||
154 | struct scatterlist *sl = (struct scatterlist *)sc->buffer; | ||
155 | struct disk_stat stat; | ||
156 | struct disk_req req; | ||
157 | |||
158 | stat.fd = desc[sc->device->id]; | ||
159 | |||
160 | while (list_len) { | ||
161 | req.addr = __pa(page_address(sl->page) + sl->offset); | ||
162 | req.len = sl->length; | ||
163 | if (DBG) | ||
164 | printk("simscsi_sg_%s @ %lx (off %lx) use_sg=%d len=%d\n", | ||
165 | mode == SSC_READ ? "read":"write", req.addr, offset, | ||
166 | list_len, sl->length); | ||
167 | ia64_ssc(stat.fd, 1, __pa(&req), offset, mode); | ||
168 | ia64_ssc(__pa(&stat), 0, 0, 0, SSC_WAIT_COMPLETION); | ||
169 | |||
170 | /* should not happen in our case */ | ||
171 | if (stat.count != req.len) { | ||
172 | sc->result = DID_ERROR << 16; | ||
173 | return; | ||
174 | } | ||
175 | offset += sl->length; | ||
176 | sl++; | ||
177 | list_len--; | ||
178 | } | ||
179 | sc->result = GOOD; | ||
180 | } | ||
181 | |||
182 | /* | ||
183 | * function handling both READ_6/WRITE_6 (non-scatter/gather mode) | ||
184 | * commands. | ||
185 | * Added 02/26/99 S.Eranian | ||
186 | */ | ||
187 | static void | ||
188 | simscsi_readwrite6 (struct scsi_cmnd *sc, int mode) | ||
189 | { | ||
190 | unsigned long offset; | ||
191 | |||
192 | offset = (((sc->cmnd[1] & 0x1f) << 16) | (sc->cmnd[2] << 8) | sc->cmnd[3])*512; | ||
193 | if (sc->use_sg > 0) | ||
194 | simscsi_sg_readwrite(sc, mode, offset); | ||
195 | else | ||
196 | simscsi_readwrite(sc, mode, offset, sc->cmnd[4]*512); | ||
197 | } | ||
198 | |||
199 | static size_t | ||
200 | simscsi_get_disk_size (int fd) | ||
201 | { | ||
202 | struct disk_stat stat; | ||
203 | size_t bit, sectors = 0; | ||
204 | struct disk_req req; | ||
205 | char buf[512]; | ||
206 | |||
207 | /* | ||
208 | * This is a bit kludgey: the simulator doesn't provide a direct way of determining | ||
209 | * the disk size, so we do a binary search, assuming a maximum disk size of 4GB. | ||
210 | */ | ||
211 | for (bit = (4UL << 30)/512; bit != 0; bit >>= 1) { | ||
212 | req.addr = __pa(&buf); | ||
213 | req.len = sizeof(buf); | ||
214 | ia64_ssc(fd, 1, __pa(&req), ((sectors | bit) - 1)*512, SSC_READ); | ||
215 | stat.fd = fd; | ||
216 | ia64_ssc(__pa(&stat), 0, 0, 0, SSC_WAIT_COMPLETION); | ||
217 | if (stat.count == sizeof(buf)) | ||
218 | sectors |= bit; | ||
219 | } | ||
220 | return sectors - 1; /* return last valid sector number */ | ||
221 | } | ||
222 | |||
223 | static void | ||
224 | simscsi_readwrite10 (struct scsi_cmnd *sc, int mode) | ||
225 | { | ||
226 | unsigned long offset; | ||
227 | |||
228 | offset = ( (sc->cmnd[2] << 24) | (sc->cmnd[3] << 16) | ||
229 | | (sc->cmnd[4] << 8) | (sc->cmnd[5] << 0))*512; | ||
230 | if (sc->use_sg > 0) | ||
231 | simscsi_sg_readwrite(sc, mode, offset); | ||
232 | else | ||
233 | simscsi_readwrite(sc, mode, offset, ((sc->cmnd[7] << 8) | sc->cmnd[8])*512); | ||
234 | } | ||
235 | |||
236 | static int | ||
237 | simscsi_queuecommand (struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) | ||
238 | { | ||
239 | unsigned int target_id = sc->device->id; | ||
240 | char fname[MAX_ROOT_LEN+16]; | ||
241 | size_t disk_size; | ||
242 | char *buf; | ||
243 | #if DEBUG_SIMSCSI | ||
244 | register long sp asm ("sp"); | ||
245 | |||
246 | if (DBG) | ||
247 | printk("simscsi_queuecommand: target=%d,cmnd=%u,sc=%lu,sp=%lx,done=%p\n", | ||
248 | target_id, sc->cmnd[0], sc->serial_number, sp, done); | ||
249 | #endif | ||
250 | |||
251 | sc->result = DID_BAD_TARGET << 16; | ||
252 | sc->scsi_done = done; | ||
253 | if (target_id <= 15 && sc->device->lun == 0) { | ||
254 | switch (sc->cmnd[0]) { | ||
255 | case INQUIRY: | ||
256 | if (sc->request_bufflen < 35) { | ||
257 | break; | ||
258 | } | ||
259 | sprintf (fname, "%s%c", simscsi_root, 'a' + target_id); | ||
260 | desc[target_id] = ia64_ssc(__pa(fname), SSC_READ_ACCESS|SSC_WRITE_ACCESS, | ||
261 | 0, 0, SSC_OPEN); | ||
262 | if (desc[target_id] < 0) { | ||
263 | /* disk doesn't exist... */ | ||
264 | break; | ||
265 | } | ||
266 | buf = sc->request_buffer; | ||
267 | buf[0] = 0; /* magnetic disk */ | ||
268 | buf[1] = 0; /* not a removable medium */ | ||
269 | buf[2] = 2; /* SCSI-2 compliant device */ | ||
270 | buf[3] = 2; /* SCSI-2 response data format */ | ||
271 | buf[4] = 31; /* additional length (bytes) */ | ||
272 | buf[5] = 0; /* reserved */ | ||
273 | buf[6] = 0; /* reserved */ | ||
274 | buf[7] = 0; /* various flags */ | ||
275 | memcpy(buf + 8, "HP SIMULATED DISK 0.00", 28); | ||
276 | sc->result = GOOD; | ||
277 | break; | ||
278 | |||
279 | case TEST_UNIT_READY: | ||
280 | sc->result = GOOD; | ||
281 | break; | ||
282 | |||
283 | case READ_6: | ||
284 | if (desc[target_id] < 0 ) | ||
285 | break; | ||
286 | simscsi_readwrite6(sc, SSC_READ); | ||
287 | break; | ||
288 | |||
289 | case READ_10: | ||
290 | if (desc[target_id] < 0 ) | ||
291 | break; | ||
292 | simscsi_readwrite10(sc, SSC_READ); | ||
293 | break; | ||
294 | |||
295 | case WRITE_6: | ||
296 | if (desc[target_id] < 0) | ||
297 | break; | ||
298 | simscsi_readwrite6(sc, SSC_WRITE); | ||
299 | break; | ||
300 | |||
301 | case WRITE_10: | ||
302 | if (desc[target_id] < 0) | ||
303 | break; | ||
304 | simscsi_readwrite10(sc, SSC_WRITE); | ||
305 | break; | ||
306 | |||
307 | |||
308 | case READ_CAPACITY: | ||
309 | if (desc[target_id] < 0 || sc->request_bufflen < 8) { | ||
310 | break; | ||
311 | } | ||
312 | buf = sc->request_buffer; | ||
313 | |||
314 | disk_size = simscsi_get_disk_size(desc[target_id]); | ||
315 | |||
316 | /* pretend to be a 1GB disk (partition table contains real stuff): */ | ||
317 | buf[0] = (disk_size >> 24) & 0xff; | ||
318 | buf[1] = (disk_size >> 16) & 0xff; | ||
319 | buf[2] = (disk_size >> 8) & 0xff; | ||
320 | buf[3] = (disk_size >> 0) & 0xff; | ||
321 | /* set block size of 512 bytes: */ | ||
322 | buf[4] = 0; | ||
323 | buf[5] = 0; | ||
324 | buf[6] = 2; | ||
325 | buf[7] = 0; | ||
326 | sc->result = GOOD; | ||
327 | break; | ||
328 | |||
329 | case MODE_SENSE: | ||
330 | case MODE_SENSE_10: | ||
331 | /* sd.c uses this to determine whether disk does write-caching. */ | ||
332 | memset(sc->request_buffer, 0, 128); | ||
333 | sc->result = GOOD; | ||
334 | break; | ||
335 | |||
336 | case START_STOP: | ||
337 | printk(KERN_ERR "START_STOP\n"); | ||
338 | break; | ||
339 | |||
340 | default: | ||
341 | panic("simscsi: unknown SCSI command %u\n", sc->cmnd[0]); | ||
342 | } | ||
343 | } | ||
344 | if (sc->result == DID_BAD_TARGET) { | ||
345 | sc->result |= DRIVER_SENSE << 24; | ||
346 | sc->sense_buffer[0] = 0x70; | ||
347 | sc->sense_buffer[2] = 0x00; | ||
348 | } | ||
349 | if (atomic_read(&num_reqs) >= SIMSCSI_REQ_QUEUE_LEN) { | ||
350 | panic("Attempt to queue command while command is pending!!"); | ||
351 | } | ||
352 | atomic_inc(&num_reqs); | ||
353 | queue[wr].sc = sc; | ||
354 | wr = (wr + 1) % SIMSCSI_REQ_QUEUE_LEN; | ||
355 | |||
356 | tasklet_schedule(&simscsi_tasklet); | ||
357 | return 0; | ||
358 | } | ||
359 | |||
360 | static int | ||
361 | simscsi_host_reset (struct scsi_cmnd *sc) | ||
362 | { | ||
363 | printk(KERN_ERR "simscsi_host_reset: not implemented\n"); | ||
364 | return 0; | ||
365 | } | ||
366 | |||
367 | static struct scsi_host_template driver_template = { | ||
368 | .name = "simulated SCSI host adapter", | ||
369 | .proc_name = "simscsi", | ||
370 | .queuecommand = simscsi_queuecommand, | ||
371 | .eh_host_reset_handler = simscsi_host_reset, | ||
372 | .bios_param = simscsi_biosparam, | ||
373 | .can_queue = SIMSCSI_REQ_QUEUE_LEN, | ||
374 | .this_id = -1, | ||
375 | .sg_tablesize = SG_ALL, | ||
376 | .max_sectors = 1024, | ||
377 | .cmd_per_lun = SIMSCSI_REQ_QUEUE_LEN, | ||
378 | .use_clustering = DISABLE_CLUSTERING, | ||
379 | }; | ||
380 | |||
381 | static int __init | ||
382 | simscsi_init(void) | ||
383 | { | ||
384 | int error; | ||
385 | |||
386 | host = scsi_host_alloc(&driver_template, 0); | ||
387 | if (!host) | ||
388 | return -ENOMEM; | ||
389 | |||
390 | error = scsi_add_host(host, NULL); | ||
391 | if (!error) | ||
392 | scsi_scan_host(host); | ||
393 | return error; | ||
394 | } | ||
395 | |||
396 | static void __exit | ||
397 | simscsi_exit(void) | ||
398 | { | ||
399 | scsi_remove_host(host); | ||
400 | scsi_host_put(host); | ||
401 | } | ||
402 | |||
403 | module_init(simscsi_init); | ||
404 | module_exit(simscsi_exit); | ||
diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c new file mode 100644 index 000000000000..786e70718ce4 --- /dev/null +++ b/arch/ia64/hp/sim/simserial.c | |||
@@ -0,0 +1,1032 @@ | |||
1 | /* | ||
2 | * Simulated Serial Driver (fake serial) | ||
3 | * | ||
4 | * This driver is mostly used for bringup purposes and will go away. | ||
5 | * It has a strong dependency on the system console. All outputs | ||
6 | * are rerouted to the same facility as the one used by printk which, in our | ||
7 | * case means sys_sim.c console (goes via the simulator). The code hereafter | ||
8 | * is completely leveraged from the serial.c driver. | ||
9 | * | ||
10 | * Copyright (C) 1999-2000, 2002-2003 Hewlett-Packard Co | ||
11 | * Stephane Eranian <eranian@hpl.hp.com> | ||
12 | * David Mosberger-Tang <davidm@hpl.hp.com> | ||
13 | * | ||
14 | * 02/04/00 D. Mosberger Merged in serial.c bug fixes in rs_close(). | ||
15 | * 02/25/00 D. Mosberger Synced up with 2.3.99pre-5 version of serial.c. | ||
16 | * 07/30/02 D. Mosberger Replace sti()/cli() with explicit spinlocks & local irq masking | ||
17 | */ | ||
18 | |||
19 | #include <linux/config.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/sched.h> | ||
23 | #include <linux/tty.h> | ||
24 | #include <linux/tty_flip.h> | ||
25 | #include <linux/major.h> | ||
26 | #include <linux/fcntl.h> | ||
27 | #include <linux/mm.h> | ||
28 | #include <linux/slab.h> | ||
29 | #include <linux/console.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/serial.h> | ||
32 | #include <linux/serialP.h> | ||
33 | |||
34 | #include <asm/irq.h> | ||
35 | #include <asm/hw_irq.h> | ||
36 | #include <asm/uaccess.h> | ||
37 | |||
38 | #ifdef CONFIG_KDB | ||
39 | # include <linux/kdb.h> | ||
40 | #endif | ||
41 | |||
42 | #undef SIMSERIAL_DEBUG /* define this to get some debug information */ | ||
43 | |||
44 | #define KEYBOARD_INTR 3 /* must match with simulator! */ | ||
45 | |||
46 | #define NR_PORTS 1 /* only one port for now */ | ||
47 | #define SERIAL_INLINE 1 | ||
48 | |||
49 | #ifdef SERIAL_INLINE | ||
50 | #define _INLINE_ inline | ||
51 | #endif | ||
52 | |||
53 | #define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT) | ||
54 | |||
55 | #define SSC_GETCHAR 21 | ||
56 | |||
57 | extern long ia64_ssc (long, long, long, long, int); | ||
58 | extern void ia64_ssc_connect_irq (long intr, long irq); | ||
59 | |||
60 | static char *serial_name = "SimSerial driver"; | ||
61 | static char *serial_version = "0.6"; | ||
62 | |||
63 | /* | ||
64 | * This has been extracted from asm/serial.h. We need one eventually but | ||
65 | * I don't know exactly what we're going to put in it so just fake one | ||
66 | * for now. | ||
67 | */ | ||
68 | #define BASE_BAUD ( 1843200 / 16 ) | ||
69 | |||
70 | #define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) | ||
71 | |||
72 | /* | ||
73 | * Most of the values here are meaningless to this particular driver. | ||
74 | * However some values must be preserved for the code (leveraged from serial.c | ||
75 | * to work correctly). | ||
76 | * port must not be 0 | ||
77 | * type must not be UNKNOWN | ||
78 | * So I picked arbitrary (guess from where?) values instead | ||
79 | */ | ||
80 | static struct serial_state rs_table[NR_PORTS]={ | ||
81 | /* UART CLK PORT IRQ FLAGS */ | ||
82 | { 0, BASE_BAUD, 0x3F8, 0, STD_COM_FLAGS,0,PORT_16550 } /* ttyS0 */ | ||
83 | }; | ||
84 | |||
85 | /* | ||
86 | * Just for the fun of it ! | ||
87 | */ | ||
88 | static struct serial_uart_config uart_config[] = { | ||
89 | { "unknown", 1, 0 }, | ||
90 | { "8250", 1, 0 }, | ||
91 | { "16450", 1, 0 }, | ||
92 | { "16550", 1, 0 }, | ||
93 | { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, | ||
94 | { "cirrus", 1, 0 }, | ||
95 | { "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH }, | ||
96 | { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | | ||
97 | UART_STARTECH }, | ||
98 | { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO}, | ||
99 | { 0, 0} | ||
100 | }; | ||
101 | |||
102 | struct tty_driver *hp_simserial_driver; | ||
103 | |||
104 | static struct async_struct *IRQ_ports[NR_IRQS]; | ||
105 | |||
106 | static struct console *console; | ||
107 | |||
108 | static unsigned char *tmp_buf; | ||
109 | static DECLARE_MUTEX(tmp_buf_sem); | ||
110 | |||
111 | extern struct console *console_drivers; /* from kernel/printk.c */ | ||
112 | |||
113 | /* | ||
114 | * ------------------------------------------------------------ | ||
115 | * rs_stop() and rs_start() | ||
116 | * | ||
117 | * This routines are called before setting or resetting tty->stopped. | ||
118 | * They enable or disable transmitter interrupts, as necessary. | ||
119 | * ------------------------------------------------------------ | ||
120 | */ | ||
121 | static void rs_stop(struct tty_struct *tty) | ||
122 | { | ||
123 | #ifdef SIMSERIAL_DEBUG | ||
124 | printk("rs_stop: tty->stopped=%d tty->hw_stopped=%d tty->flow_stopped=%d\n", | ||
125 | tty->stopped, tty->hw_stopped, tty->flow_stopped); | ||
126 | #endif | ||
127 | |||
128 | } | ||
129 | |||
130 | static void rs_start(struct tty_struct *tty) | ||
131 | { | ||
132 | #if SIMSERIAL_DEBUG | ||
133 | printk("rs_start: tty->stopped=%d tty->hw_stopped=%d tty->flow_stopped=%d\n", | ||
134 | tty->stopped, tty->hw_stopped, tty->flow_stopped); | ||
135 | #endif | ||
136 | } | ||
137 | |||
138 | static void receive_chars(struct tty_struct *tty, struct pt_regs *regs) | ||
139 | { | ||
140 | unsigned char ch; | ||
141 | static unsigned char seen_esc = 0; | ||
142 | |||
143 | while ( (ch = ia64_ssc(0, 0, 0, 0, SSC_GETCHAR)) ) { | ||
144 | if ( ch == 27 && seen_esc == 0 ) { | ||
145 | seen_esc = 1; | ||
146 | continue; | ||
147 | } else { | ||
148 | if ( seen_esc==1 && ch == 'O' ) { | ||
149 | seen_esc = 2; | ||
150 | continue; | ||
151 | } else if ( seen_esc == 2 ) { | ||
152 | if ( ch == 'P' ) show_state(); /* F1 key */ | ||
153 | #ifdef CONFIG_KDB | ||
154 | if ( ch == 'S' ) | ||
155 | kdb(KDB_REASON_KEYBOARD, 0, (kdb_eframe_t) regs); | ||
156 | #endif | ||
157 | |||
158 | seen_esc = 0; | ||
159 | continue; | ||
160 | } | ||
161 | } | ||
162 | seen_esc = 0; | ||
163 | if (tty->flip.count >= TTY_FLIPBUF_SIZE) break; | ||
164 | |||
165 | *tty->flip.char_buf_ptr = ch; | ||
166 | |||
167 | *tty->flip.flag_buf_ptr = 0; | ||
168 | |||
169 | tty->flip.flag_buf_ptr++; | ||
170 | tty->flip.char_buf_ptr++; | ||
171 | tty->flip.count++; | ||
172 | } | ||
173 | tty_flip_buffer_push(tty); | ||
174 | } | ||
175 | |||
176 | /* | ||
177 | * This is the serial driver's interrupt routine for a single port | ||
178 | */ | ||
179 | static irqreturn_t rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs) | ||
180 | { | ||
181 | struct async_struct * info; | ||
182 | |||
183 | /* | ||
184 | * I don't know exactly why they don't use the dev_id opaque data | ||
185 | * pointer instead of this extra lookup table | ||
186 | */ | ||
187 | info = IRQ_ports[irq]; | ||
188 | if (!info || !info->tty) { | ||
189 | printk(KERN_INFO "simrs_interrupt_single: info|tty=0 info=%p problem\n", info); | ||
190 | return IRQ_NONE; | ||
191 | } | ||
192 | /* | ||
193 | * pretty simple in our case, because we only get interrupts | ||
194 | * on inbound traffic | ||
195 | */ | ||
196 | receive_chars(info->tty, regs); | ||
197 | return IRQ_HANDLED; | ||
198 | } | ||
199 | |||
200 | /* | ||
201 | * ------------------------------------------------------------------- | ||
202 | * Here ends the serial interrupt routines. | ||
203 | * ------------------------------------------------------------------- | ||
204 | */ | ||
205 | |||
206 | #if 0 | ||
207 | /* | ||
208 | * not really used in our situation so keep them commented out for now | ||
209 | */ | ||
210 | static DECLARE_TASK_QUEUE(tq_serial); /* used to be at the top of the file */ | ||
211 | static void do_serial_bh(void) | ||
212 | { | ||
213 | run_task_queue(&tq_serial); | ||
214 | printk(KERN_ERR "do_serial_bh: called\n"); | ||
215 | } | ||
216 | #endif | ||
217 | |||
218 | static void do_softint(void *private_) | ||
219 | { | ||
220 | printk(KERN_ERR "simserial: do_softint called\n"); | ||
221 | } | ||
222 | |||
223 | static void rs_put_char(struct tty_struct *tty, unsigned char ch) | ||
224 | { | ||
225 | struct async_struct *info = (struct async_struct *)tty->driver_data; | ||
226 | unsigned long flags; | ||
227 | |||
228 | if (!tty || !info->xmit.buf) return; | ||
229 | |||
230 | local_irq_save(flags); | ||
231 | if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) == 0) { | ||
232 | local_irq_restore(flags); | ||
233 | return; | ||
234 | } | ||
235 | info->xmit.buf[info->xmit.head] = ch; | ||
236 | info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1); | ||
237 | local_irq_restore(flags); | ||
238 | } | ||
239 | |||
240 | static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done) | ||
241 | { | ||
242 | int count; | ||
243 | unsigned long flags; | ||
244 | |||
245 | |||
246 | local_irq_save(flags); | ||
247 | |||
248 | if (info->x_char) { | ||
249 | char c = info->x_char; | ||
250 | |||
251 | console->write(console, &c, 1); | ||
252 | |||
253 | info->state->icount.tx++; | ||
254 | info->x_char = 0; | ||
255 | |||
256 | goto out; | ||
257 | } | ||
258 | |||
259 | if (info->xmit.head == info->xmit.tail || info->tty->stopped || info->tty->hw_stopped) { | ||
260 | #ifdef SIMSERIAL_DEBUG | ||
261 | printk("transmit_chars: head=%d, tail=%d, stopped=%d\n", | ||
262 | info->xmit.head, info->xmit.tail, info->tty->stopped); | ||
263 | #endif | ||
264 | goto out; | ||
265 | } | ||
266 | /* | ||
267 | * We removed the loop and try to do it in to chunks. We need | ||
268 | * 2 operations maximum because it's a ring buffer. | ||
269 | * | ||
270 | * First from current to tail if possible. | ||
271 | * Then from the beginning of the buffer until necessary | ||
272 | */ | ||
273 | |||
274 | count = min(CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE), | ||
275 | SERIAL_XMIT_SIZE - info->xmit.tail); | ||
276 | console->write(console, info->xmit.buf+info->xmit.tail, count); | ||
277 | |||
278 | info->xmit.tail = (info->xmit.tail+count) & (SERIAL_XMIT_SIZE-1); | ||
279 | |||
280 | /* | ||
281 | * We have more at the beginning of the buffer | ||
282 | */ | ||
283 | count = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); | ||
284 | if (count) { | ||
285 | console->write(console, info->xmit.buf, count); | ||
286 | info->xmit.tail += count; | ||
287 | } | ||
288 | out: | ||
289 | local_irq_restore(flags); | ||
290 | } | ||
291 | |||
292 | static void rs_flush_chars(struct tty_struct *tty) | ||
293 | { | ||
294 | struct async_struct *info = (struct async_struct *)tty->driver_data; | ||
295 | |||
296 | if (info->xmit.head == info->xmit.tail || tty->stopped || tty->hw_stopped || | ||
297 | !info->xmit.buf) | ||
298 | return; | ||
299 | |||
300 | transmit_chars(info, NULL); | ||
301 | } | ||
302 | |||
303 | |||
304 | static int rs_write(struct tty_struct * tty, | ||
305 | const unsigned char *buf, int count) | ||
306 | { | ||
307 | int c, ret = 0; | ||
308 | struct async_struct *info = (struct async_struct *)tty->driver_data; | ||
309 | unsigned long flags; | ||
310 | |||
311 | if (!tty || !info->xmit.buf || !tmp_buf) return 0; | ||
312 | |||
313 | local_irq_save(flags); | ||
314 | while (1) { | ||
315 | c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); | ||
316 | if (count < c) | ||
317 | c = count; | ||
318 | if (c <= 0) { | ||
319 | break; | ||
320 | } | ||
321 | memcpy(info->xmit.buf + info->xmit.head, buf, c); | ||
322 | info->xmit.head = ((info->xmit.head + c) & | ||
323 | (SERIAL_XMIT_SIZE-1)); | ||
324 | buf += c; | ||
325 | count -= c; | ||
326 | ret += c; | ||
327 | } | ||
328 | local_irq_restore(flags); | ||
329 | /* | ||
330 | * Hey, we transmit directly from here in our case | ||
331 | */ | ||
332 | if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) | ||
333 | && !tty->stopped && !tty->hw_stopped) { | ||
334 | transmit_chars(info, NULL); | ||
335 | } | ||
336 | return ret; | ||
337 | } | ||
338 | |||
339 | static int rs_write_room(struct tty_struct *tty) | ||
340 | { | ||
341 | struct async_struct *info = (struct async_struct *)tty->driver_data; | ||
342 | |||
343 | return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); | ||
344 | } | ||
345 | |||
346 | static int rs_chars_in_buffer(struct tty_struct *tty) | ||
347 | { | ||
348 | struct async_struct *info = (struct async_struct *)tty->driver_data; | ||
349 | |||
350 | return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); | ||
351 | } | ||
352 | |||
353 | static void rs_flush_buffer(struct tty_struct *tty) | ||
354 | { | ||
355 | struct async_struct *info = (struct async_struct *)tty->driver_data; | ||
356 | unsigned long flags; | ||
357 | |||
358 | local_irq_save(flags); | ||
359 | info->xmit.head = info->xmit.tail = 0; | ||
360 | local_irq_restore(flags); | ||
361 | |||
362 | wake_up_interruptible(&tty->write_wait); | ||
363 | |||
364 | if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && | ||
365 | tty->ldisc.write_wakeup) | ||
366 | (tty->ldisc.write_wakeup)(tty); | ||
367 | } | ||
368 | |||
369 | /* | ||
370 | * This function is used to send a high-priority XON/XOFF character to | ||
371 | * the device | ||
372 | */ | ||
373 | static void rs_send_xchar(struct tty_struct *tty, char ch) | ||
374 | { | ||
375 | struct async_struct *info = (struct async_struct *)tty->driver_data; | ||
376 | |||
377 | info->x_char = ch; | ||
378 | if (ch) { | ||
379 | /* | ||
380 | * I guess we could call console->write() directly but | ||
381 | * let's do that for now. | ||
382 | */ | ||
383 | transmit_chars(info, NULL); | ||
384 | } | ||
385 | } | ||
386 | |||
387 | /* | ||
388 | * ------------------------------------------------------------ | ||
389 | * rs_throttle() | ||
390 | * | ||
391 | * This routine is called by the upper-layer tty layer to signal that | ||
392 | * incoming characters should be throttled. | ||
393 | * ------------------------------------------------------------ | ||
394 | */ | ||
395 | static void rs_throttle(struct tty_struct * tty) | ||
396 | { | ||
397 | if (I_IXOFF(tty)) rs_send_xchar(tty, STOP_CHAR(tty)); | ||
398 | |||
399 | printk(KERN_INFO "simrs_throttle called\n"); | ||
400 | } | ||
401 | |||
402 | static void rs_unthrottle(struct tty_struct * tty) | ||
403 | { | ||
404 | struct async_struct *info = (struct async_struct *)tty->driver_data; | ||
405 | |||
406 | if (I_IXOFF(tty)) { | ||
407 | if (info->x_char) | ||
408 | info->x_char = 0; | ||
409 | else | ||
410 | rs_send_xchar(tty, START_CHAR(tty)); | ||
411 | } | ||
412 | printk(KERN_INFO "simrs_unthrottle called\n"); | ||
413 | } | ||
414 | |||
415 | /* | ||
416 | * rs_break() --- routine which turns the break handling on or off | ||
417 | */ | ||
418 | static void rs_break(struct tty_struct *tty, int break_state) | ||
419 | { | ||
420 | } | ||
421 | |||
422 | static int rs_ioctl(struct tty_struct *tty, struct file * file, | ||
423 | unsigned int cmd, unsigned long arg) | ||
424 | { | ||
425 | if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && | ||
426 | (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && | ||
427 | (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { | ||
428 | if (tty->flags & (1 << TTY_IO_ERROR)) | ||
429 | return -EIO; | ||
430 | } | ||
431 | |||
432 | switch (cmd) { | ||
433 | case TIOCMGET: | ||
434 | printk(KERN_INFO "rs_ioctl: TIOCMGET called\n"); | ||
435 | return -EINVAL; | ||
436 | case TIOCMBIS: | ||
437 | case TIOCMBIC: | ||
438 | case TIOCMSET: | ||
439 | printk(KERN_INFO "rs_ioctl: TIOCMBIS/BIC/SET called\n"); | ||
440 | return -EINVAL; | ||
441 | case TIOCGSERIAL: | ||
442 | printk(KERN_INFO "simrs_ioctl TIOCGSERIAL called\n"); | ||
443 | return 0; | ||
444 | case TIOCSSERIAL: | ||
445 | printk(KERN_INFO "simrs_ioctl TIOCSSERIAL called\n"); | ||
446 | return 0; | ||
447 | case TIOCSERCONFIG: | ||
448 | printk(KERN_INFO "rs_ioctl: TIOCSERCONFIG called\n"); | ||
449 | return -EINVAL; | ||
450 | |||
451 | case TIOCSERGETLSR: /* Get line status register */ | ||
452 | printk(KERN_INFO "rs_ioctl: TIOCSERGETLSR called\n"); | ||
453 | return -EINVAL; | ||
454 | |||
455 | case TIOCSERGSTRUCT: | ||
456 | printk(KERN_INFO "rs_ioctl: TIOCSERGSTRUCT called\n"); | ||
457 | #if 0 | ||
458 | if (copy_to_user((struct async_struct *) arg, | ||
459 | info, sizeof(struct async_struct))) | ||
460 | return -EFAULT; | ||
461 | #endif | ||
462 | return 0; | ||
463 | |||
464 | /* | ||
465 | * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change | ||
466 | * - mask passed in arg for lines of interest | ||
467 | * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) | ||
468 | * Caller should use TIOCGICOUNT to see which one it was | ||
469 | */ | ||
470 | case TIOCMIWAIT: | ||
471 | printk(KERN_INFO "rs_ioctl: TIOCMIWAIT: called\n"); | ||
472 | return 0; | ||
473 | /* | ||
474 | * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) | ||
475 | * Return: write counters to the user passed counter struct | ||
476 | * NB: both 1->0 and 0->1 transitions are counted except for | ||
477 | * RI where only 0->1 is counted. | ||
478 | */ | ||
479 | case TIOCGICOUNT: | ||
480 | printk(KERN_INFO "rs_ioctl: TIOCGICOUNT called\n"); | ||
481 | return 0; | ||
482 | |||
483 | case TIOCSERGWILD: | ||
484 | case TIOCSERSWILD: | ||
485 | /* "setserial -W" is called in Debian boot */ | ||
486 | printk (KERN_INFO "TIOCSER?WILD ioctl obsolete, ignored.\n"); | ||
487 | return 0; | ||
488 | |||
489 | default: | ||
490 | return -ENOIOCTLCMD; | ||
491 | } | ||
492 | return 0; | ||
493 | } | ||
494 | |||
495 | #define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) | ||
496 | |||
497 | static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios) | ||
498 | { | ||
499 | unsigned int cflag = tty->termios->c_cflag; | ||
500 | |||
501 | if ( (cflag == old_termios->c_cflag) | ||
502 | && ( RELEVANT_IFLAG(tty->termios->c_iflag) | ||
503 | == RELEVANT_IFLAG(old_termios->c_iflag))) | ||
504 | return; | ||
505 | |||
506 | |||
507 | /* Handle turning off CRTSCTS */ | ||
508 | if ((old_termios->c_cflag & CRTSCTS) && | ||
509 | !(tty->termios->c_cflag & CRTSCTS)) { | ||
510 | tty->hw_stopped = 0; | ||
511 | rs_start(tty); | ||
512 | } | ||
513 | } | ||
514 | /* | ||
515 | * This routine will shutdown a serial port; interrupts are disabled, and | ||
516 | * DTR is dropped if the hangup on close termio flag is on. | ||
517 | */ | ||
518 | static void shutdown(struct async_struct * info) | ||
519 | { | ||
520 | unsigned long flags; | ||
521 | struct serial_state *state; | ||
522 | int retval; | ||
523 | |||
524 | if (!(info->flags & ASYNC_INITIALIZED)) return; | ||
525 | |||
526 | state = info->state; | ||
527 | |||
528 | #ifdef SIMSERIAL_DEBUG | ||
529 | printk("Shutting down serial port %d (irq %d)....", info->line, | ||
530 | state->irq); | ||
531 | #endif | ||
532 | |||
533 | local_irq_save(flags); | ||
534 | { | ||
535 | /* | ||
536 | * First unlink the serial port from the IRQ chain... | ||
537 | */ | ||
538 | if (info->next_port) | ||
539 | info->next_port->prev_port = info->prev_port; | ||
540 | if (info->prev_port) | ||
541 | info->prev_port->next_port = info->next_port; | ||
542 | else | ||
543 | IRQ_ports[state->irq] = info->next_port; | ||
544 | |||
545 | /* | ||
546 | * Free the IRQ, if necessary | ||
547 | */ | ||
548 | if (state->irq && (!IRQ_ports[state->irq] || | ||
549 | !IRQ_ports[state->irq]->next_port)) { | ||
550 | if (IRQ_ports[state->irq]) { | ||
551 | free_irq(state->irq, NULL); | ||
552 | retval = request_irq(state->irq, rs_interrupt_single, | ||
553 | IRQ_T(info), "serial", NULL); | ||
554 | |||
555 | if (retval) | ||
556 | printk(KERN_ERR "serial shutdown: request_irq: error %d" | ||
557 | " Couldn't reacquire IRQ.\n", retval); | ||
558 | } else | ||
559 | free_irq(state->irq, NULL); | ||
560 | } | ||
561 | |||
562 | if (info->xmit.buf) { | ||
563 | free_page((unsigned long) info->xmit.buf); | ||
564 | info->xmit.buf = 0; | ||
565 | } | ||
566 | |||
567 | if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); | ||
568 | |||
569 | info->flags &= ~ASYNC_INITIALIZED; | ||
570 | } | ||
571 | local_irq_restore(flags); | ||
572 | } | ||
573 | |||
574 | /* | ||
575 | * ------------------------------------------------------------ | ||
576 | * rs_close() | ||
577 | * | ||
578 | * This routine is called when the serial port gets closed. First, we | ||
579 | * wait for the last remaining data to be sent. Then, we unlink its | ||
580 | * async structure from the interrupt chain if necessary, and we free | ||
581 | * that IRQ if nothing is left in the chain. | ||
582 | * ------------------------------------------------------------ | ||
583 | */ | ||
584 | static void rs_close(struct tty_struct *tty, struct file * filp) | ||
585 | { | ||
586 | struct async_struct * info = (struct async_struct *)tty->driver_data; | ||
587 | struct serial_state *state; | ||
588 | unsigned long flags; | ||
589 | |||
590 | if (!info ) return; | ||
591 | |||
592 | state = info->state; | ||
593 | |||
594 | local_irq_save(flags); | ||
595 | if (tty_hung_up_p(filp)) { | ||
596 | #ifdef SIMSERIAL_DEBUG | ||
597 | printk("rs_close: hung_up\n"); | ||
598 | #endif | ||
599 | local_irq_restore(flags); | ||
600 | return; | ||
601 | } | ||
602 | #ifdef SIMSERIAL_DEBUG | ||
603 | printk("rs_close ttys%d, count = %d\n", info->line, state->count); | ||
604 | #endif | ||
605 | if ((tty->count == 1) && (state->count != 1)) { | ||
606 | /* | ||
607 | * Uh, oh. tty->count is 1, which means that the tty | ||
608 | * structure will be freed. state->count should always | ||
609 | * be one in these conditions. If it's greater than | ||
610 | * one, we've got real problems, since it means the | ||
611 | * serial port won't be shutdown. | ||
612 | */ | ||
613 | printk(KERN_ERR "rs_close: bad serial port count; tty->count is 1, " | ||
614 | "state->count is %d\n", state->count); | ||
615 | state->count = 1; | ||
616 | } | ||
617 | if (--state->count < 0) { | ||
618 | printk(KERN_ERR "rs_close: bad serial port count for ttys%d: %d\n", | ||
619 | info->line, state->count); | ||
620 | state->count = 0; | ||
621 | } | ||
622 | if (state->count) { | ||
623 | local_irq_restore(flags); | ||
624 | return; | ||
625 | } | ||
626 | info->flags |= ASYNC_CLOSING; | ||
627 | local_irq_restore(flags); | ||
628 | |||
629 | /* | ||
630 | * Now we wait for the transmit buffer to clear; and we notify | ||
631 | * the line discipline to only process XON/XOFF characters. | ||
632 | */ | ||
633 | shutdown(info); | ||
634 | if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); | ||
635 | if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); | ||
636 | info->event = 0; | ||
637 | info->tty = 0; | ||
638 | if (info->blocked_open) { | ||
639 | if (info->close_delay) { | ||
640 | current->state = TASK_INTERRUPTIBLE; | ||
641 | schedule_timeout(info->close_delay); | ||
642 | } | ||
643 | wake_up_interruptible(&info->open_wait); | ||
644 | } | ||
645 | info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); | ||
646 | wake_up_interruptible(&info->close_wait); | ||
647 | } | ||
648 | |||
649 | /* | ||
650 | * rs_wait_until_sent() --- wait until the transmitter is empty | ||
651 | */ | ||
652 | static void rs_wait_until_sent(struct tty_struct *tty, int timeout) | ||
653 | { | ||
654 | } | ||
655 | |||
656 | |||
657 | /* | ||
658 | * rs_hangup() --- called by tty_hangup() when a hangup is signaled. | ||
659 | */ | ||
660 | static void rs_hangup(struct tty_struct *tty) | ||
661 | { | ||
662 | struct async_struct * info = (struct async_struct *)tty->driver_data; | ||
663 | struct serial_state *state = info->state; | ||
664 | |||
665 | #ifdef SIMSERIAL_DEBUG | ||
666 | printk("rs_hangup: called\n"); | ||
667 | #endif | ||
668 | |||
669 | state = info->state; | ||
670 | |||
671 | rs_flush_buffer(tty); | ||
672 | if (info->flags & ASYNC_CLOSING) | ||
673 | return; | ||
674 | shutdown(info); | ||
675 | |||
676 | info->event = 0; | ||
677 | state->count = 0; | ||
678 | info->flags &= ~ASYNC_NORMAL_ACTIVE; | ||
679 | info->tty = 0; | ||
680 | wake_up_interruptible(&info->open_wait); | ||
681 | } | ||
682 | |||
683 | |||
684 | static int get_async_struct(int line, struct async_struct **ret_info) | ||
685 | { | ||
686 | struct async_struct *info; | ||
687 | struct serial_state *sstate; | ||
688 | |||
689 | sstate = rs_table + line; | ||
690 | sstate->count++; | ||
691 | if (sstate->info) { | ||
692 | *ret_info = sstate->info; | ||
693 | return 0; | ||
694 | } | ||
695 | info = kmalloc(sizeof(struct async_struct), GFP_KERNEL); | ||
696 | if (!info) { | ||
697 | sstate->count--; | ||
698 | return -ENOMEM; | ||
699 | } | ||
700 | memset(info, 0, sizeof(struct async_struct)); | ||
701 | init_waitqueue_head(&info->open_wait); | ||
702 | init_waitqueue_head(&info->close_wait); | ||
703 | init_waitqueue_head(&info->delta_msr_wait); | ||
704 | info->magic = SERIAL_MAGIC; | ||
705 | info->port = sstate->port; | ||
706 | info->flags = sstate->flags; | ||
707 | info->xmit_fifo_size = sstate->xmit_fifo_size; | ||
708 | info->line = line; | ||
709 | INIT_WORK(&info->work, do_softint, info); | ||
710 | info->state = sstate; | ||
711 | if (sstate->info) { | ||
712 | kfree(info); | ||
713 | *ret_info = sstate->info; | ||
714 | return 0; | ||
715 | } | ||
716 | *ret_info = sstate->info = info; | ||
717 | return 0; | ||
718 | } | ||
719 | |||
720 | static int | ||
721 | startup(struct async_struct *info) | ||
722 | { | ||
723 | unsigned long flags; | ||
724 | int retval=0; | ||
725 | irqreturn_t (*handler)(int, void *, struct pt_regs *); | ||
726 | struct serial_state *state= info->state; | ||
727 | unsigned long page; | ||
728 | |||
729 | page = get_zeroed_page(GFP_KERNEL); | ||
730 | if (!page) | ||
731 | return -ENOMEM; | ||
732 | |||
733 | local_irq_save(flags); | ||
734 | |||
735 | if (info->flags & ASYNC_INITIALIZED) { | ||
736 | free_page(page); | ||
737 | goto errout; | ||
738 | } | ||
739 | |||
740 | if (!state->port || !state->type) { | ||
741 | if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); | ||
742 | free_page(page); | ||
743 | goto errout; | ||
744 | } | ||
745 | if (info->xmit.buf) | ||
746 | free_page(page); | ||
747 | else | ||
748 | info->xmit.buf = (unsigned char *) page; | ||
749 | |||
750 | #ifdef SIMSERIAL_DEBUG | ||
751 | printk("startup: ttys%d (irq %d)...", info->line, state->irq); | ||
752 | #endif | ||
753 | |||
754 | /* | ||
755 | * Allocate the IRQ if necessary | ||
756 | */ | ||
757 | if (state->irq && (!IRQ_ports[state->irq] || | ||
758 | !IRQ_ports[state->irq]->next_port)) { | ||
759 | if (IRQ_ports[state->irq]) { | ||
760 | retval = -EBUSY; | ||
761 | goto errout; | ||
762 | } else | ||
763 | handler = rs_interrupt_single; | ||
764 | |||
765 | retval = request_irq(state->irq, handler, IRQ_T(info), "simserial", NULL); | ||
766 | if (retval) { | ||
767 | if (capable(CAP_SYS_ADMIN)) { | ||
768 | if (info->tty) | ||
769 | set_bit(TTY_IO_ERROR, | ||
770 | &info->tty->flags); | ||
771 | retval = 0; | ||
772 | } | ||
773 | goto errout; | ||
774 | } | ||
775 | } | ||
776 | |||
777 | /* | ||
778 | * Insert serial port into IRQ chain. | ||
779 | */ | ||
780 | info->prev_port = 0; | ||
781 | info->next_port = IRQ_ports[state->irq]; | ||
782 | if (info->next_port) | ||
783 | info->next_port->prev_port = info; | ||
784 | IRQ_ports[state->irq] = info; | ||
785 | |||
786 | if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); | ||
787 | |||
788 | info->xmit.head = info->xmit.tail = 0; | ||
789 | |||
790 | #if 0 | ||
791 | /* | ||
792 | * Set up serial timers... | ||
793 | */ | ||
794 | timer_table[RS_TIMER].expires = jiffies + 2*HZ/100; | ||
795 | timer_active |= 1 << RS_TIMER; | ||
796 | #endif | ||
797 | |||
798 | /* | ||
799 | * Set up the tty->alt_speed kludge | ||
800 | */ | ||
801 | if (info->tty) { | ||
802 | if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) | ||
803 | info->tty->alt_speed = 57600; | ||
804 | if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) | ||
805 | info->tty->alt_speed = 115200; | ||
806 | if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) | ||
807 | info->tty->alt_speed = 230400; | ||
808 | if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) | ||
809 | info->tty->alt_speed = 460800; | ||
810 | } | ||
811 | |||
812 | info->flags |= ASYNC_INITIALIZED; | ||
813 | local_irq_restore(flags); | ||
814 | return 0; | ||
815 | |||
816 | errout: | ||
817 | local_irq_restore(flags); | ||
818 | return retval; | ||
819 | } | ||
820 | |||
821 | |||
822 | /* | ||
823 | * This routine is called whenever a serial port is opened. It | ||
824 | * enables interrupts for a serial port, linking in its async structure into | ||
825 | * the IRQ chain. It also performs the serial-specific | ||
826 | * initialization for the tty structure. | ||
827 | */ | ||
828 | static int rs_open(struct tty_struct *tty, struct file * filp) | ||
829 | { | ||
830 | struct async_struct *info; | ||
831 | int retval, line; | ||
832 | unsigned long page; | ||
833 | |||
834 | line = tty->index; | ||
835 | if ((line < 0) || (line >= NR_PORTS)) | ||
836 | return -ENODEV; | ||
837 | retval = get_async_struct(line, &info); | ||
838 | if (retval) | ||
839 | return retval; | ||
840 | tty->driver_data = info; | ||
841 | info->tty = tty; | ||
842 | |||
843 | #ifdef SIMSERIAL_DEBUG | ||
844 | printk("rs_open %s, count = %d\n", tty->name, info->state->count); | ||
845 | #endif | ||
846 | info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; | ||
847 | |||
848 | if (!tmp_buf) { | ||
849 | page = get_zeroed_page(GFP_KERNEL); | ||
850 | if (!page) | ||
851 | return -ENOMEM; | ||
852 | if (tmp_buf) | ||
853 | free_page(page); | ||
854 | else | ||
855 | tmp_buf = (unsigned char *) page; | ||
856 | } | ||
857 | |||
858 | /* | ||
859 | * If the port is the middle of closing, bail out now | ||
860 | */ | ||
861 | if (tty_hung_up_p(filp) || | ||
862 | (info->flags & ASYNC_CLOSING)) { | ||
863 | if (info->flags & ASYNC_CLOSING) | ||
864 | interruptible_sleep_on(&info->close_wait); | ||
865 | #ifdef SERIAL_DO_RESTART | ||
866 | return ((info->flags & ASYNC_HUP_NOTIFY) ? | ||
867 | -EAGAIN : -ERESTARTSYS); | ||
868 | #else | ||
869 | return -EAGAIN; | ||
870 | #endif | ||
871 | } | ||
872 | |||
873 | /* | ||
874 | * Start up serial port | ||
875 | */ | ||
876 | retval = startup(info); | ||
877 | if (retval) { | ||
878 | return retval; | ||
879 | } | ||
880 | |||
881 | /* | ||
882 | * figure out which console to use (should be one already) | ||
883 | */ | ||
884 | console = console_drivers; | ||
885 | while (console) { | ||
886 | if ((console->flags & CON_ENABLED) && console->write) break; | ||
887 | console = console->next; | ||
888 | } | ||
889 | |||
890 | #ifdef SIMSERIAL_DEBUG | ||
891 | printk("rs_open ttys%d successful\n", info->line); | ||
892 | #endif | ||
893 | return 0; | ||
894 | } | ||
895 | |||
896 | /* | ||
897 | * /proc fs routines.... | ||
898 | */ | ||
899 | |||
900 | static inline int line_info(char *buf, struct serial_state *state) | ||
901 | { | ||
902 | return sprintf(buf, "%d: uart:%s port:%lX irq:%d\n", | ||
903 | state->line, uart_config[state->type].name, | ||
904 | state->port, state->irq); | ||
905 | } | ||
906 | |||
907 | static int rs_read_proc(char *page, char **start, off_t off, int count, | ||
908 | int *eof, void *data) | ||
909 | { | ||
910 | int i, len = 0, l; | ||
911 | off_t begin = 0; | ||
912 | |||
913 | len += sprintf(page, "simserinfo:1.0 driver:%s\n", serial_version); | ||
914 | for (i = 0; i < NR_PORTS && len < 4000; i++) { | ||
915 | l = line_info(page + len, &rs_table[i]); | ||
916 | len += l; | ||
917 | if (len+begin > off+count) | ||
918 | goto done; | ||
919 | if (len+begin < off) { | ||
920 | begin += len; | ||
921 | len = 0; | ||
922 | } | ||
923 | } | ||
924 | *eof = 1; | ||
925 | done: | ||
926 | if (off >= len+begin) | ||
927 | return 0; | ||
928 | *start = page + (begin-off); | ||
929 | return ((count < begin+len-off) ? count : begin+len-off); | ||
930 | } | ||
931 | |||
932 | /* | ||
933 | * --------------------------------------------------------------------- | ||
934 | * rs_init() and friends | ||
935 | * | ||
936 | * rs_init() is called at boot-time to initialize the serial driver. | ||
937 | * --------------------------------------------------------------------- | ||
938 | */ | ||
939 | |||
940 | /* | ||
941 | * This routine prints out the appropriate serial driver version | ||
942 | * number, and identifies which options were configured into this | ||
943 | * driver. | ||
944 | */ | ||
945 | static inline void show_serial_version(void) | ||
946 | { | ||
947 | printk(KERN_INFO "%s version %s with", serial_name, serial_version); | ||
948 | printk(KERN_INFO " no serial options enabled\n"); | ||
949 | } | ||
950 | |||
951 | static struct tty_operations hp_ops = { | ||
952 | .open = rs_open, | ||
953 | .close = rs_close, | ||
954 | .write = rs_write, | ||
955 | .put_char = rs_put_char, | ||
956 | .flush_chars = rs_flush_chars, | ||
957 | .write_room = rs_write_room, | ||
958 | .chars_in_buffer = rs_chars_in_buffer, | ||
959 | .flush_buffer = rs_flush_buffer, | ||
960 | .ioctl = rs_ioctl, | ||
961 | .throttle = rs_throttle, | ||
962 | .unthrottle = rs_unthrottle, | ||
963 | .send_xchar = rs_send_xchar, | ||
964 | .set_termios = rs_set_termios, | ||
965 | .stop = rs_stop, | ||
966 | .start = rs_start, | ||
967 | .hangup = rs_hangup, | ||
968 | .break_ctl = rs_break, | ||
969 | .wait_until_sent = rs_wait_until_sent, | ||
970 | .read_proc = rs_read_proc, | ||
971 | }; | ||
972 | |||
973 | /* | ||
974 | * The serial driver boot-time initialization code! | ||
975 | */ | ||
976 | static int __init | ||
977 | simrs_init (void) | ||
978 | { | ||
979 | int i; | ||
980 | struct serial_state *state; | ||
981 | |||
982 | if (!ia64_platform_is("hpsim")) | ||
983 | return -ENODEV; | ||
984 | |||
985 | hp_simserial_driver = alloc_tty_driver(1); | ||
986 | if (!hp_simserial_driver) | ||
987 | return -ENOMEM; | ||
988 | |||
989 | show_serial_version(); | ||
990 | |||
991 | /* Initialize the tty_driver structure */ | ||
992 | |||
993 | hp_simserial_driver->owner = THIS_MODULE; | ||
994 | hp_simserial_driver->driver_name = "simserial"; | ||
995 | hp_simserial_driver->name = "ttyS"; | ||
996 | hp_simserial_driver->major = TTY_MAJOR; | ||
997 | hp_simserial_driver->minor_start = 64; | ||
998 | hp_simserial_driver->type = TTY_DRIVER_TYPE_SERIAL; | ||
999 | hp_simserial_driver->subtype = SERIAL_TYPE_NORMAL; | ||
1000 | hp_simserial_driver->init_termios = tty_std_termios; | ||
1001 | hp_simserial_driver->init_termios.c_cflag = | ||
1002 | B9600 | CS8 | CREAD | HUPCL | CLOCAL; | ||
1003 | hp_simserial_driver->flags = TTY_DRIVER_REAL_RAW; | ||
1004 | tty_set_operations(hp_simserial_driver, &hp_ops); | ||
1005 | |||
1006 | /* | ||
1007 | * Let's have a little bit of fun ! | ||
1008 | */ | ||
1009 | for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { | ||
1010 | |||
1011 | if (state->type == PORT_UNKNOWN) continue; | ||
1012 | |||
1013 | if (!state->irq) { | ||
1014 | state->irq = assign_irq_vector(AUTO_ASSIGN); | ||
1015 | ia64_ssc_connect_irq(KEYBOARD_INTR, state->irq); | ||
1016 | } | ||
1017 | |||
1018 | printk(KERN_INFO "ttyS%d at 0x%04lx (irq = %d) is a %s\n", | ||
1019 | state->line, | ||
1020 | state->port, state->irq, | ||
1021 | uart_config[state->type].name); | ||
1022 | } | ||
1023 | |||
1024 | if (tty_register_driver(hp_simserial_driver)) | ||
1025 | panic("Couldn't register simserial driver\n"); | ||
1026 | |||
1027 | return 0; | ||
1028 | } | ||
1029 | |||
1030 | #ifndef MODULE | ||
1031 | __initcall(simrs_init); | ||
1032 | #endif | ||