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