diff options
Diffstat (limited to 'arch/xtensa/platforms/iss')
-rw-r--r-- | arch/xtensa/platforms/iss/Makefile | 8 | ||||
-rw-r--r-- | arch/xtensa/platforms/iss/console.c | 297 | ||||
-rw-r--r-- | arch/xtensa/platforms/iss/io.c | 32 | ||||
-rw-r--r-- | arch/xtensa/platforms/iss/network.c | 823 | ||||
-rw-r--r-- | arch/xtensa/platforms/iss/setup.c | 110 |
5 files changed, 1270 insertions, 0 deletions
diff --git a/arch/xtensa/platforms/iss/Makefile b/arch/xtensa/platforms/iss/Makefile new file mode 100644 index 000000000000..af96e314d71f --- /dev/null +++ b/arch/xtensa/platforms/iss/Makefile | |||
@@ -0,0 +1,8 @@ | |||
1 | # $Id: Makefile,v 1.1.1.1 2002/08/28 16:10:14 aroll Exp $ | ||
2 | # | ||
3 | # Makefile for the Xtensa Instruction Set Simulator (ISS) | ||
4 | # "prom monitor" library routines under Linux. | ||
5 | # | ||
6 | |||
7 | obj-y = io.o console.o setup.o network.o | ||
8 | |||
diff --git a/arch/xtensa/platforms/iss/console.c b/arch/xtensa/platforms/iss/console.c new file mode 100644 index 000000000000..9141e3690731 --- /dev/null +++ b/arch/xtensa/platforms/iss/console.c | |||
@@ -0,0 +1,297 @@ | |||
1 | /* | ||
2 | * arch/xtensa/platform-iss/console.c | ||
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) 2001-2005 Tensilica Inc. | ||
9 | * Authors Christian Zankel, Joe Taylor | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/console.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/mm.h> | ||
19 | #include <linux/major.h> | ||
20 | #include <linux/param.h> | ||
21 | #include <linux/serial.h> | ||
22 | #include <linux/serialP.h> | ||
23 | |||
24 | #include <asm/uaccess.h> | ||
25 | #include <asm/irq.h> | ||
26 | |||
27 | #include <asm/platform/simcall.h> | ||
28 | |||
29 | #include <linux/tty.h> | ||
30 | #include <linux/tty_flip.h> | ||
31 | |||
32 | #ifdef SERIAL_INLINE | ||
33 | #define _INLINE_ inline | ||
34 | #endif | ||
35 | |||
36 | #define SERIAL_MAX_NUM_LINES 1 | ||
37 | #define SERIAL_TIMER_VALUE (20 * HZ) | ||
38 | |||
39 | static struct tty_driver *serial_driver; | ||
40 | static struct timer_list serial_timer; | ||
41 | |||
42 | static DEFINE_SPINLOCK(timer_lock); | ||
43 | |||
44 | int errno; | ||
45 | |||
46 | static int __simc (int a, int b, int c, int d, int e, int f) __attribute__((__noinline__)); | ||
47 | static int __simc (int a, int b, int c, int d, int e, int f) | ||
48 | { | ||
49 | int ret; | ||
50 | __asm__ __volatile__ ("simcall\n" | ||
51 | "mov %0, a2\n" | ||
52 | "mov %1, a3\n" : "=a" (ret), "=a" (errno) | ||
53 | : : "a2", "a3"); | ||
54 | return ret; | ||
55 | } | ||
56 | |||
57 | static char *serial_version = "0.1"; | ||
58 | static char *serial_name = "ISS serial driver"; | ||
59 | |||
60 | /* | ||
61 | * This routine is called whenever a serial port is opened. It | ||
62 | * enables interrupts for a serial port, linking in its async structure into | ||
63 | * the IRQ chain. It also performs the serial-specific | ||
64 | * initialization for the tty structure. | ||
65 | */ | ||
66 | |||
67 | static void rs_poll(unsigned long); | ||
68 | |||
69 | static int rs_open(struct tty_struct *tty, struct file * filp) | ||
70 | { | ||
71 | int line = tty->index; | ||
72 | |||
73 | if ((line < 0) || (line >= SERIAL_MAX_NUM_LINES)) | ||
74 | return -ENODEV; | ||
75 | |||
76 | spin_lock(&timer_lock); | ||
77 | |||
78 | if (tty->count == 1) { | ||
79 | init_timer(&serial_timer); | ||
80 | serial_timer.data = (unsigned long) tty; | ||
81 | serial_timer.function = rs_poll; | ||
82 | mod_timer(&serial_timer, jiffies + SERIAL_TIMER_VALUE); | ||
83 | } | ||
84 | spin_unlock(&timer_lock); | ||
85 | |||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | |||
90 | /* | ||
91 | * ------------------------------------------------------------ | ||
92 | * iss_serial_close() | ||
93 | * | ||
94 | * This routine is called when the serial port gets closed. First, we | ||
95 | * wait for the last remaining data to be sent. Then, we unlink its | ||
96 | * async structure from the interrupt chain if necessary, and we free | ||
97 | * that IRQ if nothing is left in the chain. | ||
98 | * ------------------------------------------------------------ | ||
99 | */ | ||
100 | static void rs_close(struct tty_struct *tty, struct file * filp) | ||
101 | { | ||
102 | spin_lock(&timer_lock); | ||
103 | if (tty->count == 1) | ||
104 | del_timer_sync(&serial_timer); | ||
105 | spin_unlock(&timer_lock); | ||
106 | } | ||
107 | |||
108 | |||
109 | static int rs_write(struct tty_struct * tty, | ||
110 | const unsigned char *buf, int count) | ||
111 | { | ||
112 | /* see drivers/char/serialX.c to reference original version */ | ||
113 | |||
114 | __simc (SYS_write, 1, (unsigned long)buf, count, 0, 0); | ||
115 | return count; | ||
116 | } | ||
117 | |||
118 | static void rs_poll(unsigned long priv) | ||
119 | { | ||
120 | struct tty_struct* tty = (struct tty_struct*) priv; | ||
121 | |||
122 | struct timeval tv = { .tv_sec = 0, .tv_usec = 0 }; | ||
123 | int i = 0; | ||
124 | unsigned char c; | ||
125 | |||
126 | spin_lock(&timer_lock); | ||
127 | |||
128 | while (__simc(SYS_select_one, 0, XTISS_SELECT_ONE_READ, (int)&tv,0,0)){ | ||
129 | __simc (SYS_read, 0, (unsigned long)&c, 1, 0, 0); | ||
130 | tty_insert_flip_char(tty, c, TTY_NORMAL); | ||
131 | i++; | ||
132 | } | ||
133 | |||
134 | if (i) | ||
135 | tty_flip_buffer_push(tty); | ||
136 | |||
137 | |||
138 | mod_timer(&serial_timer, jiffies + SERIAL_TIMER_VALUE); | ||
139 | spin_unlock(&timer_lock); | ||
140 | } | ||
141 | |||
142 | |||
143 | static void rs_put_char(struct tty_struct *tty, unsigned char ch) | ||
144 | { | ||
145 | char buf[2]; | ||
146 | |||
147 | if (!tty) | ||
148 | return; | ||
149 | |||
150 | buf[0] = ch; | ||
151 | buf[1] = '\0'; /* Is this NULL necessary? */ | ||
152 | __simc (SYS_write, 1, (unsigned long) buf, 1, 0, 0); | ||
153 | } | ||
154 | |||
155 | static void rs_flush_chars(struct tty_struct *tty) | ||
156 | { | ||
157 | } | ||
158 | |||
159 | static int rs_write_room(struct tty_struct *tty) | ||
160 | { | ||
161 | /* Let's say iss can always accept 2K characters.. */ | ||
162 | return 2 * 1024; | ||
163 | } | ||
164 | |||
165 | static int rs_chars_in_buffer(struct tty_struct *tty) | ||
166 | { | ||
167 | /* the iss doesn't buffer characters */ | ||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | static void rs_hangup(struct tty_struct *tty) | ||
172 | { | ||
173 | /* Stub, once again.. */ | ||
174 | } | ||
175 | |||
176 | static void rs_wait_until_sent(struct tty_struct *tty, int timeout) | ||
177 | { | ||
178 | /* Stub, once again.. */ | ||
179 | } | ||
180 | |||
181 | static int rs_read_proc(char *page, char **start, off_t off, int count, | ||
182 | int *eof, void *data) | ||
183 | { | ||
184 | int len = 0; | ||
185 | off_t begin = 0; | ||
186 | |||
187 | len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version); | ||
188 | *eof = 1; | ||
189 | |||
190 | if (off >= len + begin) | ||
191 | return 0; | ||
192 | |||
193 | *start = page + (off - begin); | ||
194 | return ((count < begin + len - off) ? count : begin + len - off); | ||
195 | } | ||
196 | |||
197 | |||
198 | static struct tty_operations serial_ops = { | ||
199 | .open = rs_open, | ||
200 | .close = rs_close, | ||
201 | .write = rs_write, | ||
202 | .put_char = rs_put_char, | ||
203 | .flush_chars = rs_flush_chars, | ||
204 | .write_room = rs_write_room, | ||
205 | .chars_in_buffer = rs_chars_in_buffer, | ||
206 | .hangup = rs_hangup, | ||
207 | .wait_until_sent = rs_wait_until_sent, | ||
208 | .read_proc = rs_read_proc | ||
209 | }; | ||
210 | |||
211 | int __init rs_init(void) | ||
212 | { | ||
213 | serial_driver = alloc_tty_driver(1); | ||
214 | |||
215 | printk ("%s %s\n", serial_name, serial_version); | ||
216 | |||
217 | /* Initialize the tty_driver structure */ | ||
218 | |||
219 | serial_driver->owner = THIS_MODULE; | ||
220 | serial_driver->driver_name = "iss_serial"; | ||
221 | serial_driver->name = "ttyS"; | ||
222 | serial_driver->major = TTY_MAJOR; | ||
223 | serial_driver->minor_start = 64; | ||
224 | serial_driver->type = TTY_DRIVER_TYPE_SERIAL; | ||
225 | serial_driver->subtype = SERIAL_TYPE_NORMAL; | ||
226 | serial_driver->init_termios = tty_std_termios; | ||
227 | serial_driver->init_termios.c_cflag = | ||
228 | B9600 | CS8 | CREAD | HUPCL | CLOCAL; | ||
229 | serial_driver->flags = TTY_DRIVER_REAL_RAW; | ||
230 | |||
231 | tty_set_operations(serial_driver, &serial_ops); | ||
232 | |||
233 | if (tty_register_driver(serial_driver)) | ||
234 | panic("Couldn't register serial driver\n"); | ||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | |||
239 | static __exit void rs_exit(void) | ||
240 | { | ||
241 | int error; | ||
242 | |||
243 | if ((error = tty_unregister_driver(serial_driver))) | ||
244 | printk("ISS_SERIAL: failed to unregister serial driver (%d)\n", | ||
245 | error); | ||
246 | put_tty_driver(serial_driver); | ||
247 | } | ||
248 | |||
249 | |||
250 | /* We use `late_initcall' instead of just `__initcall' as a workaround for | ||
251 | * the fact that (1) simcons_tty_init can't be called before tty_init, | ||
252 | * (2) tty_init is called via `module_init', (3) if statically linked, | ||
253 | * module_init == device_init, and (4) there's no ordering of init lists. | ||
254 | * We can do this easily because simcons is always statically linked, but | ||
255 | * other tty drivers that depend on tty_init and which must use | ||
256 | * `module_init' to declare their init routines are likely to be broken. | ||
257 | */ | ||
258 | |||
259 | late_initcall(rs_init); | ||
260 | |||
261 | |||
262 | #ifdef CONFIG_SERIAL_CONSOLE | ||
263 | |||
264 | static void iss_console_write(struct console *co, const char *s, unsigned count) | ||
265 | { | ||
266 | int len = strlen(s); | ||
267 | |||
268 | if (s != 0 && *s != 0) | ||
269 | __simc (SYS_write, 1, (unsigned long)s, | ||
270 | count < len ? count : len,0,0); | ||
271 | } | ||
272 | |||
273 | static struct tty_driver* iss_console_device(struct console *c, int *index) | ||
274 | { | ||
275 | *index = c->index; | ||
276 | return serial_driver; | ||
277 | } | ||
278 | |||
279 | |||
280 | static struct console sercons = { | ||
281 | .name = "ttyS", | ||
282 | .write = iss_console_write, | ||
283 | .device = iss_console_device, | ||
284 | .flags = CON_PRINTBUFFER, | ||
285 | .index = -1 | ||
286 | }; | ||
287 | |||
288 | static int __init iss_console_init(void) | ||
289 | { | ||
290 | register_console(&sercons); | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | console_initcall(iss_console_init); | ||
295 | |||
296 | #endif /* CONFIG_SERIAL_CONSOLE */ | ||
297 | |||
diff --git a/arch/xtensa/platforms/iss/io.c b/arch/xtensa/platforms/iss/io.c new file mode 100644 index 000000000000..5b161a5cb65f --- /dev/null +++ b/arch/xtensa/platforms/iss/io.c | |||
@@ -0,0 +1,32 @@ | |||
1 | /* This file isn't really needed right now. */ | ||
2 | |||
3 | #if 0 | ||
4 | |||
5 | #include <asm/io.h> | ||
6 | #include <xtensa/simcall.h> | ||
7 | |||
8 | extern int __simc (); | ||
9 | |||
10 | |||
11 | char iss_serial_getc() | ||
12 | { | ||
13 | char c; | ||
14 | __simc( SYS_read, 0, &c, 1 ); | ||
15 | return c; | ||
16 | } | ||
17 | |||
18 | void iss_serial_putc( char c ) | ||
19 | { | ||
20 | __simc( SYS_write, 1, &c, 1 ); | ||
21 | } | ||
22 | |||
23 | void iss_serial_puts( char *s ) | ||
24 | { | ||
25 | if( s != 0 && *s != 0 ) | ||
26 | __simc( SYS_write, 1, s, strlen(s) ); | ||
27 | } | ||
28 | |||
29 | /*#error Need I/O ports to specific hardware!*/ | ||
30 | |||
31 | #endif | ||
32 | |||
diff --git a/arch/xtensa/platforms/iss/network.c b/arch/xtensa/platforms/iss/network.c new file mode 100644 index 000000000000..a2e252217428 --- /dev/null +++ b/arch/xtensa/platforms/iss/network.c | |||
@@ -0,0 +1,823 @@ | |||
1 | /* | ||
2 | * | ||
3 | * arch/xtensa/platform-iss/network.c | ||
4 | * | ||
5 | * Platform specific initialization. | ||
6 | * | ||
7 | * Authors: Chris Zankel <chris@zankel.net> | ||
8 | * Based on work form the UML team. | ||
9 | * | ||
10 | * Copyright 2005 Tensilica Inc. | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify it | ||
13 | * under the terms of the GNU General Public License as published by the | ||
14 | * Free Software Foundation; either version 2 of the License, or (at your | ||
15 | * option) any later version. | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #include <linux/list.h> | ||
20 | #include <linux/irq.h> | ||
21 | #include <linux/spinlock.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/timer.h> | ||
24 | #include <linux/if_ether.h> | ||
25 | #include <linux/inetdevice.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/if_tun.h> | ||
28 | #include <linux/etherdevice.h> | ||
29 | #include <linux/interrupt.h> | ||
30 | #include <linux/ioctl.h> | ||
31 | #include <linux/bootmem.h> | ||
32 | #include <linux/ethtool.h> | ||
33 | #include <linux/rtnetlink.h> | ||
34 | #include <linux/platform_device.h> | ||
35 | |||
36 | #include <asm/platform/simcall.h> | ||
37 | |||
38 | #define DRIVER_NAME "iss-netdev" | ||
39 | #define ETH_MAX_PACKET 1500 | ||
40 | #define ETH_HEADER_OTHER 14 | ||
41 | #define ISS_NET_TIMER_VALUE (2 * HZ) | ||
42 | |||
43 | |||
44 | static DEFINE_SPINLOCK(opened_lock); | ||
45 | static LIST_HEAD(opened); | ||
46 | |||
47 | static DEFINE_SPINLOCK(devices_lock); | ||
48 | static LIST_HEAD(devices); | ||
49 | |||
50 | /* ------------------------------------------------------------------------- */ | ||
51 | |||
52 | /* We currently only support the TUNTAP transport protocol. */ | ||
53 | |||
54 | #define TRANSPORT_TUNTAP_NAME "tuntap" | ||
55 | #define TRANSPORT_TUNTAP_MTU ETH_MAX_PACKET | ||
56 | |||
57 | struct tuntap_info { | ||
58 | char dev_name[IFNAMSIZ]; | ||
59 | int fixed_config; | ||
60 | unsigned char gw[ETH_ALEN]; | ||
61 | int fd; | ||
62 | }; | ||
63 | |||
64 | /* ------------------------------------------------------------------------- */ | ||
65 | |||
66 | |||
67 | /* This structure contains out private information for the driver. */ | ||
68 | |||
69 | struct iss_net_private { | ||
70 | |||
71 | struct list_head device_list; | ||
72 | struct list_head opened_list; | ||
73 | |||
74 | spinlock_t lock; | ||
75 | struct net_device *dev; | ||
76 | struct platform_device pdev; | ||
77 | struct timer_list tl; | ||
78 | struct net_device_stats stats; | ||
79 | |||
80 | struct timer_list timer; | ||
81 | unsigned int timer_val; | ||
82 | |||
83 | int index; | ||
84 | int mtu; | ||
85 | |||
86 | unsigned char mac[ETH_ALEN]; | ||
87 | int have_mac; | ||
88 | |||
89 | struct { | ||
90 | union { | ||
91 | struct tuntap_info tuntap; | ||
92 | } info; | ||
93 | |||
94 | int (*open)(struct iss_net_private *lp); | ||
95 | void (*close)(struct iss_net_private *lp); | ||
96 | int (*read)(struct iss_net_private *lp, struct sk_buff **skb); | ||
97 | int (*write)(struct iss_net_private *lp, struct sk_buff **skb); | ||
98 | unsigned short (*protocol)(struct sk_buff *skb); | ||
99 | int (*poll)(struct iss_net_private *lp); | ||
100 | } tp; | ||
101 | |||
102 | }; | ||
103 | |||
104 | /* ======================= ISS SIMCALL INTERFACE =========================== */ | ||
105 | |||
106 | /* Note: __simc must _not_ be declared inline! */ | ||
107 | |||
108 | static int errno; | ||
109 | |||
110 | static int __simc (int a, int b, int c, int d, int e, int f) __attribute__((__noinline__)); | ||
111 | static int __simc (int a, int b, int c, int d, int e, int f) | ||
112 | { | ||
113 | int ret; | ||
114 | __asm__ __volatile__ ("simcall\n" | ||
115 | "mov %0, a2\n" | ||
116 | "mov %1, a3\n" : "=a" (ret), "=a" (errno) | ||
117 | : : "a2", "a3"); | ||
118 | return ret; | ||
119 | } | ||
120 | |||
121 | static int inline simc_open(char *file, int flags, int mode) | ||
122 | { | ||
123 | return __simc(SYS_open, (int) file, flags, mode, 0, 0); | ||
124 | } | ||
125 | |||
126 | static int inline simc_close(int fd) | ||
127 | { | ||
128 | return __simc(SYS_close, fd, 0, 0, 0, 0); | ||
129 | } | ||
130 | |||
131 | static int inline simc_ioctl(int fd, int request, void *arg) | ||
132 | { | ||
133 | return __simc(SYS_ioctl, fd, request, (int) arg, 0, 0); | ||
134 | } | ||
135 | |||
136 | static int inline simc_read(int fd, void *buf, size_t count) | ||
137 | { | ||
138 | return __simc(SYS_read, fd, (int) buf, count, 0, 0); | ||
139 | } | ||
140 | |||
141 | static int inline simc_write(int fd, void *buf, size_t count) | ||
142 | { | ||
143 | return __simc(SYS_write, fd, (int) buf, count, 0, 0); | ||
144 | } | ||
145 | |||
146 | static int inline simc_poll(int fd) | ||
147 | { | ||
148 | struct timeval tv = { .tv_sec = 0, .tv_usec = 0 }; | ||
149 | |||
150 | return __simc(SYS_select_one, fd, XTISS_SELECT_ONE_READ, (int)&tv,0,0); | ||
151 | } | ||
152 | |||
153 | /* ================================ HELPERS ================================ */ | ||
154 | |||
155 | |||
156 | static char *split_if_spec(char *str, ...) | ||
157 | { | ||
158 | char **arg, *end; | ||
159 | va_list ap; | ||
160 | |||
161 | va_start(ap, str); | ||
162 | while ((arg = va_arg(ap, char**)) != NULL) { | ||
163 | if (*str == '\0') | ||
164 | return NULL; | ||
165 | end = strchr(str, ','); | ||
166 | if (end != str) | ||
167 | *arg = str; | ||
168 | if (end == NULL) | ||
169 | return NULL; | ||
170 | *end ++ = '\0'; | ||
171 | str = end; | ||
172 | } | ||
173 | va_end(ap); | ||
174 | return str; | ||
175 | } | ||
176 | |||
177 | |||
178 | #if 0 | ||
179 | /* Adjust SKB. */ | ||
180 | |||
181 | struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra) | ||
182 | { | ||
183 | if ((skb != NULL) && (skb_tailroom(skb) < extra)) { | ||
184 | struct sk_buff *skb2; | ||
185 | |||
186 | skb2 = skb_copy_expand(skb, 0, extra, GFP_ATOMIC); | ||
187 | dev_kfree_skb(skb); | ||
188 | skb = skb2; | ||
189 | } | ||
190 | if (skb != NULL) | ||
191 | skb_put(skb, extra); | ||
192 | |||
193 | return skb; | ||
194 | } | ||
195 | #endif | ||
196 | |||
197 | /* Return the IP address as a string for a given device. */ | ||
198 | |||
199 | static void dev_ip_addr(void *d, char *buf, char *bin_buf) | ||
200 | { | ||
201 | struct net_device *dev = d; | ||
202 | struct in_device *ip = dev->ip_ptr; | ||
203 | struct in_ifaddr *in; | ||
204 | __be32 addr; | ||
205 | |||
206 | if ((ip == NULL) || ((in = ip->ifa_list) == NULL)) { | ||
207 | printk(KERN_WARNING "Device not assigned an IP address!\n"); | ||
208 | return; | ||
209 | } | ||
210 | |||
211 | addr = in->ifa_address; | ||
212 | sprintf(buf, "%d.%d.%d.%d", addr & 0xff, (addr >> 8) & 0xff, | ||
213 | (addr >> 16) & 0xff, addr >> 24); | ||
214 | |||
215 | if (bin_buf) { | ||
216 | bin_buf[0] = addr & 0xff; | ||
217 | bin_buf[1] = (addr >> 8) & 0xff; | ||
218 | bin_buf[2] = (addr >> 16) & 0xff; | ||
219 | bin_buf[3] = addr >> 24; | ||
220 | } | ||
221 | } | ||
222 | |||
223 | /* Set Ethernet address of the specified device. */ | ||
224 | |||
225 | static void inline set_ether_mac(void *d, unsigned char *addr) | ||
226 | { | ||
227 | struct net_device *dev = d; | ||
228 | memcpy(dev->dev_addr, addr, ETH_ALEN); | ||
229 | } | ||
230 | |||
231 | |||
232 | /* ======================= TUNTAP TRANSPORT INTERFACE ====================== */ | ||
233 | |||
234 | static int tuntap_open(struct iss_net_private *lp) | ||
235 | { | ||
236 | struct ifreq ifr; | ||
237 | char *dev_name = lp->tp.info.tuntap.dev_name; | ||
238 | int err = -EINVAL; | ||
239 | int fd; | ||
240 | |||
241 | /* We currently only support a fixed configuration. */ | ||
242 | |||
243 | if (!lp->tp.info.tuntap.fixed_config) | ||
244 | return -EINVAL; | ||
245 | |||
246 | if ((fd = simc_open("/dev/net/tun", 02, 0)) < 0) { /* O_RDWR */ | ||
247 | printk("Failed to open /dev/net/tun, returned %d " | ||
248 | "(errno = %d)\n", fd, errno); | ||
249 | return fd; | ||
250 | } | ||
251 | |||
252 | memset(&ifr, 0, sizeof ifr); | ||
253 | ifr.ifr_flags = IFF_TAP | IFF_NO_PI; | ||
254 | strlcpy(ifr.ifr_name, dev_name, sizeof ifr.ifr_name); | ||
255 | |||
256 | if ((err = simc_ioctl(fd, TUNSETIFF, (void*) &ifr)) < 0) { | ||
257 | printk("Failed to set interface, returned %d " | ||
258 | "(errno = %d)\n", err, errno); | ||
259 | simc_close(fd); | ||
260 | return err; | ||
261 | } | ||
262 | |||
263 | lp->tp.info.tuntap.fd = fd; | ||
264 | return err; | ||
265 | } | ||
266 | |||
267 | static void tuntap_close(struct iss_net_private *lp) | ||
268 | { | ||
269 | #if 0 | ||
270 | if (lp->tp.info.tuntap.fixed_config) | ||
271 | iter_addresses(lp->tp.info.tuntap.dev, close_addr, lp->host.dev_name); | ||
272 | #endif | ||
273 | simc_close(lp->tp.info.tuntap.fd); | ||
274 | lp->tp.info.tuntap.fd = -1; | ||
275 | } | ||
276 | |||
277 | static int tuntap_read (struct iss_net_private *lp, struct sk_buff **skb) | ||
278 | { | ||
279 | #if 0 | ||
280 | *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER); | ||
281 | if (*skb == NULL) | ||
282 | return -ENOMEM; | ||
283 | #endif | ||
284 | |||
285 | return simc_read(lp->tp.info.tuntap.fd, | ||
286 | (*skb)->data, (*skb)->dev->mtu + ETH_HEADER_OTHER); | ||
287 | } | ||
288 | |||
289 | static int tuntap_write (struct iss_net_private *lp, struct sk_buff **skb) | ||
290 | { | ||
291 | return simc_write(lp->tp.info.tuntap.fd, (*skb)->data, (*skb)->len); | ||
292 | } | ||
293 | |||
294 | unsigned short tuntap_protocol(struct sk_buff *skb) | ||
295 | { | ||
296 | return eth_type_trans(skb, skb->dev); | ||
297 | } | ||
298 | |||
299 | static int tuntap_poll(struct iss_net_private *lp) | ||
300 | { | ||
301 | return simc_poll(lp->tp.info.tuntap.fd); | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * Currently only a device name is supported. | ||
306 | * ethX=tuntap[,[mac address][,[device name]]] | ||
307 | */ | ||
308 | |||
309 | static int tuntap_probe(struct iss_net_private *lp, int index, char *init) | ||
310 | { | ||
311 | const int len = strlen(TRANSPORT_TUNTAP_NAME); | ||
312 | char *dev_name = NULL, *mac_str = NULL, *rem = NULL; | ||
313 | |||
314 | /* Transport should be 'tuntap': ethX=tuntap,mac,dev_name */ | ||
315 | |||
316 | if (strncmp(init, TRANSPORT_TUNTAP_NAME, len)) | ||
317 | return 0; | ||
318 | |||
319 | if (*(init += strlen(TRANSPORT_TUNTAP_NAME)) == ',') { | ||
320 | if ((rem=split_if_spec(init+1, &mac_str, &dev_name)) != NULL) { | ||
321 | printk("Extra garbage on specification : '%s'\n", rem); | ||
322 | return 0; | ||
323 | } | ||
324 | } else if (*init != '\0') { | ||
325 | printk("Invalid argument: %s. Skipping device!\n", init); | ||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | if (dev_name) { | ||
330 | strncpy(lp->tp.info.tuntap.dev_name, dev_name, | ||
331 | sizeof lp->tp.info.tuntap.dev_name); | ||
332 | lp->tp.info.tuntap.fixed_config = 1; | ||
333 | } else | ||
334 | strcpy(lp->tp.info.tuntap.dev_name, TRANSPORT_TUNTAP_NAME); | ||
335 | |||
336 | |||
337 | #if 0 | ||
338 | if (setup_etheraddr(mac_str, lp->mac)) | ||
339 | lp->have_mac = 1; | ||
340 | #endif | ||
341 | lp->mtu = TRANSPORT_TUNTAP_MTU; | ||
342 | |||
343 | //lp->info.tuntap.gate_addr = gate_addr; | ||
344 | |||
345 | lp->tp.info.tuntap.fd = -1; | ||
346 | |||
347 | lp->tp.open = tuntap_open; | ||
348 | lp->tp.close = tuntap_close; | ||
349 | lp->tp.read = tuntap_read; | ||
350 | lp->tp.write = tuntap_write; | ||
351 | lp->tp.protocol = tuntap_protocol; | ||
352 | lp->tp.poll = tuntap_poll; | ||
353 | |||
354 | printk("TUN/TAP backend - "); | ||
355 | #if 0 | ||
356 | if (lp->host.gate_addr != NULL) | ||
357 | printk("IP = %s", lp->host.gate_addr); | ||
358 | #endif | ||
359 | printk("\n"); | ||
360 | |||
361 | return 1; | ||
362 | } | ||
363 | |||
364 | /* ================================ ISS NET ================================ */ | ||
365 | |||
366 | static int iss_net_rx(struct net_device *dev) | ||
367 | { | ||
368 | struct iss_net_private *lp = dev->priv; | ||
369 | int pkt_len; | ||
370 | struct sk_buff *skb; | ||
371 | |||
372 | /* Check if there is any new data. */ | ||
373 | |||
374 | if (lp->tp.poll(lp) == 0) | ||
375 | return 0; | ||
376 | |||
377 | /* Try to allocate memory, if it fails, try again next round. */ | ||
378 | |||
379 | if ((skb = dev_alloc_skb(dev->mtu + 2 + ETH_HEADER_OTHER)) == NULL) { | ||
380 | lp->stats.rx_dropped++; | ||
381 | return 0; | ||
382 | } | ||
383 | |||
384 | skb_reserve(skb, 2); | ||
385 | |||
386 | /* Setup skb */ | ||
387 | |||
388 | skb->dev = dev; | ||
389 | skb_reset_mac_header(skb); | ||
390 | pkt_len = lp->tp.read(lp, &skb); | ||
391 | skb_put(skb, pkt_len); | ||
392 | |||
393 | if (pkt_len > 0) { | ||
394 | skb_trim(skb, pkt_len); | ||
395 | skb->protocol = lp->tp.protocol(skb); | ||
396 | |||
397 | lp->stats.rx_bytes += skb->len; | ||
398 | lp->stats.rx_packets++; | ||
399 | // netif_rx(skb); | ||
400 | netif_rx_ni(skb); | ||
401 | return pkt_len; | ||
402 | } | ||
403 | kfree_skb(skb); | ||
404 | return pkt_len; | ||
405 | } | ||
406 | |||
407 | static int iss_net_poll(void) | ||
408 | { | ||
409 | struct list_head *ele; | ||
410 | int err, ret = 0; | ||
411 | |||
412 | spin_lock(&opened_lock); | ||
413 | |||
414 | list_for_each(ele, &opened) { | ||
415 | struct iss_net_private *lp; | ||
416 | |||
417 | lp = list_entry(ele, struct iss_net_private, opened_list); | ||
418 | |||
419 | if (!netif_running(lp->dev)) | ||
420 | break; | ||
421 | |||
422 | spin_lock(&lp->lock); | ||
423 | |||
424 | while ((err = iss_net_rx(lp->dev)) > 0) | ||
425 | ret++; | ||
426 | |||
427 | spin_unlock(&lp->lock); | ||
428 | |||
429 | if (err < 0) { | ||
430 | printk(KERN_ERR "Device '%s' read returned %d, " | ||
431 | "shutting it down\n", lp->dev->name, err); | ||
432 | dev_close(lp->dev); | ||
433 | } else { | ||
434 | // FIXME reactivate_fd(lp->fd, ISS_ETH_IRQ); | ||
435 | } | ||
436 | } | ||
437 | |||
438 | spin_unlock(&opened_lock); | ||
439 | return ret; | ||
440 | } | ||
441 | |||
442 | |||
443 | static void iss_net_timer(unsigned long priv) | ||
444 | { | ||
445 | struct iss_net_private* lp = (struct iss_net_private*) priv; | ||
446 | |||
447 | spin_lock(&lp->lock); | ||
448 | |||
449 | iss_net_poll(); | ||
450 | |||
451 | mod_timer(&lp->timer, jiffies + lp->timer_val); | ||
452 | |||
453 | spin_unlock(&lp->lock); | ||
454 | } | ||
455 | |||
456 | |||
457 | static int iss_net_open(struct net_device *dev) | ||
458 | { | ||
459 | struct iss_net_private *lp = dev->priv; | ||
460 | char addr[sizeof "255.255.255.255\0"]; | ||
461 | int err; | ||
462 | |||
463 | spin_lock(&lp->lock); | ||
464 | |||
465 | if ((err = lp->tp.open(lp)) < 0) | ||
466 | goto out; | ||
467 | |||
468 | if (!lp->have_mac) { | ||
469 | dev_ip_addr(dev, addr, &lp->mac[2]); | ||
470 | set_ether_mac(dev, lp->mac); | ||
471 | } | ||
472 | |||
473 | netif_start_queue(dev); | ||
474 | |||
475 | /* clear buffer - it can happen that the host side of the interface | ||
476 | * is full when we get here. In this case, new data is never queued, | ||
477 | * SIGIOs never arrive, and the net never works. | ||
478 | */ | ||
479 | while ((err = iss_net_rx(dev)) > 0) | ||
480 | ; | ||
481 | |||
482 | spin_lock(&opened_lock); | ||
483 | list_add(&lp->opened_list, &opened); | ||
484 | spin_unlock(&opened_lock); | ||
485 | |||
486 | init_timer(&lp->timer); | ||
487 | lp->timer_val = ISS_NET_TIMER_VALUE; | ||
488 | lp->timer.data = (unsigned long) lp; | ||
489 | lp->timer.function = iss_net_timer; | ||
490 | mod_timer(&lp->timer, jiffies + lp->timer_val); | ||
491 | |||
492 | out: | ||
493 | spin_unlock(&lp->lock); | ||
494 | return err; | ||
495 | } | ||
496 | |||
497 | static int iss_net_close(struct net_device *dev) | ||
498 | { | ||
499 | struct iss_net_private *lp = dev->priv; | ||
500 | printk("iss_net_close!\n"); | ||
501 | netif_stop_queue(dev); | ||
502 | spin_lock(&lp->lock); | ||
503 | |||
504 | spin_lock(&opened_lock); | ||
505 | list_del(&opened); | ||
506 | spin_unlock(&opened_lock); | ||
507 | |||
508 | del_timer_sync(&lp->timer); | ||
509 | |||
510 | lp->tp.close(lp); | ||
511 | |||
512 | spin_unlock(&lp->lock); | ||
513 | return 0; | ||
514 | } | ||
515 | |||
516 | static int iss_net_start_xmit(struct sk_buff *skb, struct net_device *dev) | ||
517 | { | ||
518 | struct iss_net_private *lp = dev->priv; | ||
519 | unsigned long flags; | ||
520 | int len; | ||
521 | |||
522 | netif_stop_queue(dev); | ||
523 | spin_lock_irqsave(&lp->lock, flags); | ||
524 | |||
525 | len = lp->tp.write(lp, &skb); | ||
526 | |||
527 | if (len == skb->len) { | ||
528 | lp->stats.tx_packets++; | ||
529 | lp->stats.tx_bytes += skb->len; | ||
530 | dev->trans_start = jiffies; | ||
531 | netif_start_queue(dev); | ||
532 | |||
533 | /* this is normally done in the interrupt when tx finishes */ | ||
534 | netif_wake_queue(dev); | ||
535 | |||
536 | } else if (len == 0) { | ||
537 | netif_start_queue(dev); | ||
538 | lp->stats.tx_dropped++; | ||
539 | |||
540 | } else { | ||
541 | netif_start_queue(dev); | ||
542 | printk(KERN_ERR "iss_net_start_xmit: failed(%d)\n", len); | ||
543 | } | ||
544 | |||
545 | spin_unlock_irqrestore(&lp->lock, flags); | ||
546 | |||
547 | dev_kfree_skb(skb); | ||
548 | return 0; | ||
549 | } | ||
550 | |||
551 | |||
552 | static struct net_device_stats *iss_net_get_stats(struct net_device *dev) | ||
553 | { | ||
554 | struct iss_net_private *lp = dev->priv; | ||
555 | return &lp->stats; | ||
556 | } | ||
557 | |||
558 | static void iss_net_set_multicast_list(struct net_device *dev) | ||
559 | { | ||
560 | #if 0 | ||
561 | if (dev->flags & IFF_PROMISC) | ||
562 | return; | ||
563 | else if (dev->mc_count) | ||
564 | dev->flags |= IFF_ALLMULTI; | ||
565 | else | ||
566 | dev->flags &= ~IFF_ALLMULTI; | ||
567 | #endif | ||
568 | } | ||
569 | |||
570 | static void iss_net_tx_timeout(struct net_device *dev) | ||
571 | { | ||
572 | #if 0 | ||
573 | dev->trans_start = jiffies; | ||
574 | netif_wake_queue(dev); | ||
575 | #endif | ||
576 | } | ||
577 | |||
578 | static int iss_net_set_mac(struct net_device *dev, void *addr) | ||
579 | { | ||
580 | #if 0 | ||
581 | struct iss_net_private *lp = dev->priv; | ||
582 | struct sockaddr *hwaddr = addr; | ||
583 | |||
584 | spin_lock(&lp->lock); | ||
585 | memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN); | ||
586 | spin_unlock(&lp->lock); | ||
587 | #endif | ||
588 | |||
589 | return 0; | ||
590 | } | ||
591 | |||
592 | static int iss_net_change_mtu(struct net_device *dev, int new_mtu) | ||
593 | { | ||
594 | #if 0 | ||
595 | struct iss_net_private *lp = dev->priv; | ||
596 | int err = 0; | ||
597 | |||
598 | spin_lock(&lp->lock); | ||
599 | |||
600 | // FIXME not needed new_mtu = transport_set_mtu(new_mtu, &lp->user); | ||
601 | |||
602 | if (new_mtu < 0) | ||
603 | err = new_mtu; | ||
604 | else | ||
605 | dev->mtu = new_mtu; | ||
606 | |||
607 | spin_unlock(&lp->lock); | ||
608 | return err; | ||
609 | #endif | ||
610 | return -EINVAL; | ||
611 | } | ||
612 | |||
613 | void iss_net_user_timer_expire(unsigned long _conn) | ||
614 | { | ||
615 | } | ||
616 | |||
617 | |||
618 | static struct platform_driver iss_net_driver = { | ||
619 | .driver = { | ||
620 | .name = DRIVER_NAME, | ||
621 | }, | ||
622 | }; | ||
623 | |||
624 | static int driver_registered; | ||
625 | |||
626 | static int iss_net_configure(int index, char *init) | ||
627 | { | ||
628 | struct net_device *dev; | ||
629 | struct iss_net_private *lp; | ||
630 | int err; | ||
631 | |||
632 | if ((dev = alloc_etherdev(sizeof *lp)) == NULL) { | ||
633 | printk(KERN_ERR "eth_configure: failed to allocate device\n"); | ||
634 | return 1; | ||
635 | } | ||
636 | |||
637 | /* Initialize private element. */ | ||
638 | |||
639 | lp = dev->priv; | ||
640 | *lp = ((struct iss_net_private) { | ||
641 | .device_list = LIST_HEAD_INIT(lp->device_list), | ||
642 | .opened_list = LIST_HEAD_INIT(lp->opened_list), | ||
643 | .lock = SPIN_LOCK_UNLOCKED, | ||
644 | .dev = dev, | ||
645 | .index = index, | ||
646 | //.fd = -1, | ||
647 | .mac = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0 }, | ||
648 | .have_mac = 0, | ||
649 | }); | ||
650 | |||
651 | /* | ||
652 | * Try all transport protocols. | ||
653 | * Note: more protocols can be added by adding '&& !X_init(lp, eth)'. | ||
654 | */ | ||
655 | |||
656 | if (!tuntap_probe(lp, index, init)) { | ||
657 | printk("Invalid arguments. Skipping device!\n"); | ||
658 | goto errout; | ||
659 | } | ||
660 | |||
661 | printk(KERN_INFO "Netdevice %d ", index); | ||
662 | if (lp->have_mac) | ||
663 | printk("(%02x:%02x:%02x:%02x:%02x:%02x) ", | ||
664 | lp->mac[0], lp->mac[1], | ||
665 | lp->mac[2], lp->mac[3], | ||
666 | lp->mac[4], lp->mac[5]); | ||
667 | printk(": "); | ||
668 | |||
669 | /* sysfs register */ | ||
670 | |||
671 | if (!driver_registered) { | ||
672 | platform_driver_register(&iss_net_driver); | ||
673 | driver_registered = 1; | ||
674 | } | ||
675 | |||
676 | spin_lock(&devices_lock); | ||
677 | list_add(&lp->device_list, &devices); | ||
678 | spin_unlock(&devices_lock); | ||
679 | |||
680 | lp->pdev.id = index; | ||
681 | lp->pdev.name = DRIVER_NAME; | ||
682 | platform_device_register(&lp->pdev); | ||
683 | SET_NETDEV_DEV(dev,&lp->pdev.dev); | ||
684 | |||
685 | /* | ||
686 | * If this name ends up conflicting with an existing registered | ||
687 | * netdevice, that is OK, register_netdev{,ice}() will notice this | ||
688 | * and fail. | ||
689 | */ | ||
690 | snprintf(dev->name, sizeof dev->name, "eth%d", index); | ||
691 | |||
692 | dev->mtu = lp->mtu; | ||
693 | dev->open = iss_net_open; | ||
694 | dev->hard_start_xmit = iss_net_start_xmit; | ||
695 | dev->stop = iss_net_close; | ||
696 | dev->get_stats = iss_net_get_stats; | ||
697 | dev->set_multicast_list = iss_net_set_multicast_list; | ||
698 | dev->tx_timeout = iss_net_tx_timeout; | ||
699 | dev->set_mac_address = iss_net_set_mac; | ||
700 | dev->change_mtu = iss_net_change_mtu; | ||
701 | dev->watchdog_timeo = (HZ >> 1); | ||
702 | dev->irq = -1; | ||
703 | |||
704 | rtnl_lock(); | ||
705 | err = register_netdevice(dev); | ||
706 | rtnl_unlock(); | ||
707 | |||
708 | if (err) { | ||
709 | printk("Error registering net device!\n"); | ||
710 | /* XXX: should we call ->remove() here? */ | ||
711 | free_netdev(dev); | ||
712 | return 1; | ||
713 | } | ||
714 | |||
715 | init_timer(&lp->tl); | ||
716 | lp->tl.function = iss_net_user_timer_expire; | ||
717 | |||
718 | #if 0 | ||
719 | if (lp->have_mac) | ||
720 | set_ether_mac(dev, lp->mac); | ||
721 | #endif | ||
722 | return 0; | ||
723 | |||
724 | errout: | ||
725 | // FIXME: unregister; free, etc.. | ||
726 | return -EIO; | ||
727 | |||
728 | } | ||
729 | |||
730 | /* ------------------------------------------------------------------------- */ | ||
731 | |||
732 | /* Filled in during early boot */ | ||
733 | |||
734 | struct list_head eth_cmd_line = LIST_HEAD_INIT(eth_cmd_line); | ||
735 | |||
736 | struct iss_net_init { | ||
737 | struct list_head list; | ||
738 | char *init; /* init string */ | ||
739 | int index; | ||
740 | }; | ||
741 | |||
742 | /* | ||
743 | * Parse the command line and look for 'ethX=...' fields, and register all | ||
744 | * those fields. They will be later initialized in iss_net_init. | ||
745 | */ | ||
746 | |||
747 | #define ERR KERN_ERR "iss_net_setup: " | ||
748 | |||
749 | static int iss_net_setup(char *str) | ||
750 | { | ||
751 | struct iss_net_private *device = NULL; | ||
752 | struct iss_net_init *new; | ||
753 | struct list_head *ele; | ||
754 | char *end; | ||
755 | int n; | ||
756 | |||
757 | n = simple_strtoul(str, &end, 0); | ||
758 | if (end == str) { | ||
759 | printk(ERR "Failed to parse '%s'\n", str); | ||
760 | return 1; | ||
761 | } | ||
762 | if (n < 0) { | ||
763 | printk(ERR "Device %d is negative\n", n); | ||
764 | return 1; | ||
765 | } | ||
766 | if (*(str = end) != '=') { | ||
767 | printk(ERR "Expected '=' after device number\n"); | ||
768 | return 1; | ||
769 | } | ||
770 | |||
771 | spin_lock(&devices_lock); | ||
772 | |||
773 | list_for_each(ele, &devices) { | ||
774 | device = list_entry(ele, struct iss_net_private, device_list); | ||
775 | if (device->index == n) | ||
776 | break; | ||
777 | } | ||
778 | |||
779 | spin_unlock(&devices_lock); | ||
780 | |||
781 | if (device && device->index == n) { | ||
782 | printk(ERR "Device %d already configured\n", n); | ||
783 | return 1; | ||
784 | } | ||
785 | |||
786 | if ((new = alloc_bootmem(sizeof new)) == NULL) { | ||
787 | printk("Alloc_bootmem failed\n"); | ||
788 | return 1; | ||
789 | } | ||
790 | |||
791 | INIT_LIST_HEAD(&new->list); | ||
792 | new->index = n; | ||
793 | new->init = str + 1; | ||
794 | |||
795 | list_add_tail(&new->list, ð_cmd_line); | ||
796 | return 1; | ||
797 | } | ||
798 | |||
799 | #undef ERR | ||
800 | |||
801 | __setup("eth=", iss_net_setup); | ||
802 | |||
803 | /* | ||
804 | * Initialize all ISS Ethernet devices previously registered in iss_net_setup. | ||
805 | */ | ||
806 | |||
807 | static int iss_net_init(void) | ||
808 | { | ||
809 | struct list_head *ele, *next; | ||
810 | |||
811 | /* Walk through all Ethernet devices specified in the command line. */ | ||
812 | |||
813 | list_for_each_safe(ele, next, ð_cmd_line) { | ||
814 | struct iss_net_init *eth; | ||
815 | eth = list_entry(ele, struct iss_net_init, list); | ||
816 | iss_net_configure(eth->index, eth->init); | ||
817 | } | ||
818 | |||
819 | return 1; | ||
820 | } | ||
821 | |||
822 | module_init(iss_net_init); | ||
823 | |||
diff --git a/arch/xtensa/platforms/iss/setup.c b/arch/xtensa/platforms/iss/setup.c new file mode 100644 index 000000000000..f60c8cf6dfbe --- /dev/null +++ b/arch/xtensa/platforms/iss/setup.c | |||
@@ -0,0 +1,110 @@ | |||
1 | /* | ||
2 | * | ||
3 | * arch/xtensa/platform-iss/setup.c | ||
4 | * | ||
5 | * Platform specific initialization. | ||
6 | * | ||
7 | * Authors: Chris Zankel <chris@zankel.net> | ||
8 | * Joe Taylor <joe@tensilica.com> | ||
9 | * | ||
10 | * Copyright 2001 - 2005 Tensilica Inc. | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify it | ||
13 | * under the terms of the GNU General Public License as published by the | ||
14 | * Free Software Foundation; either version 2 of the License, or (at your | ||
15 | * option) any later version. | ||
16 | * | ||
17 | */ | ||
18 | #include <linux/stddef.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/reboot.h> | ||
23 | #include <linux/kdev_t.h> | ||
24 | #include <linux/types.h> | ||
25 | #include <linux/major.h> | ||
26 | #include <linux/blkdev.h> | ||
27 | #include <linux/console.h> | ||
28 | #include <linux/delay.h> | ||
29 | #include <linux/stringify.h> | ||
30 | #include <linux/notifier.h> | ||
31 | |||
32 | #include <asm/platform.h> | ||
33 | #include <asm/bootparam.h> | ||
34 | |||
35 | |||
36 | void __init platform_init(bp_tag_t* bootparam) | ||
37 | { | ||
38 | |||
39 | } | ||
40 | |||
41 | void platform_halt(void) | ||
42 | { | ||
43 | printk (" ** Called platform_halt(), looping forever! **\n"); | ||
44 | while (1); | ||
45 | } | ||
46 | |||
47 | void platform_power_off(void) | ||
48 | { | ||
49 | printk (" ** Called platform_power_off(), looping forever! **\n"); | ||
50 | while (1); | ||
51 | } | ||
52 | void platform_restart(void) | ||
53 | { | ||
54 | /* Flush and reset the mmu, simulate a processor reset, and | ||
55 | * jump to the reset vector. */ | ||
56 | |||
57 | __asm__ __volatile__("movi a2, 15\n\t" | ||
58 | "wsr a2, " __stringify(ICOUNTLEVEL) "\n\t" | ||
59 | "movi a2, 0\n\t" | ||
60 | "wsr a2, " __stringify(ICOUNT) "\n\t" | ||
61 | "wsr a2, " __stringify(IBREAKENABLE) "\n\t" | ||
62 | "wsr a2, " __stringify(LCOUNT) "\n\t" | ||
63 | "movi a2, 0x1f\n\t" | ||
64 | "wsr a2, " __stringify(PS) "\n\t" | ||
65 | "isync\n\t" | ||
66 | "jx %0\n\t" | ||
67 | : | ||
68 | : "a" (XCHAL_RESET_VECTOR_VADDR) | ||
69 | : "a2"); | ||
70 | |||
71 | /* control never gets here */ | ||
72 | } | ||
73 | |||
74 | extern void iss_net_poll(void); | ||
75 | |||
76 | const char twirl[]="|/-\\|/-\\"; | ||
77 | |||
78 | void platform_heartbeat(void) | ||
79 | { | ||
80 | #if 0 | ||
81 | static int i = 0, j = 0; | ||
82 | |||
83 | if (--i < 0) { | ||
84 | i = 99; | ||
85 | printk("\r%c\r", twirl[j++]); | ||
86 | if (j == 8) | ||
87 | j = 0; | ||
88 | } | ||
89 | #endif | ||
90 | } | ||
91 | |||
92 | |||
93 | |||
94 | static int | ||
95 | iss_panic_event(struct notifier_block *this, unsigned long event, void *ptr) | ||
96 | { | ||
97 | __asm__ __volatile__("movi a2, -1; simcall\n"); | ||
98 | return NOTIFY_DONE; | ||
99 | } | ||
100 | |||
101 | static struct notifier_block iss_panic_block = { | ||
102 | iss_panic_event, | ||
103 | NULL, | ||
104 | 0 | ||
105 | }; | ||
106 | |||
107 | void __init platform_setup(char **p_cmdline) | ||
108 | { | ||
109 | atomic_notifier_chain_register(&panic_notifier_list, &iss_panic_block); | ||
110 | } | ||