aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/plat-omap
diff options
context:
space:
mode:
authorDavid Brownell <dbrownell@users.sourceforge.net>2006-12-06 20:14:03 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2007-05-08 15:35:48 -0400
commit994c84ea526ea1c348f63dc3aa7cfeec931bb612 (patch)
treee06a3809b04a297ca82dd9a23d9caaeb83a4db09 /arch/arm/plat-omap
parent771af222eb98a60629aa1437901c7f266ff4dc89 (diff)
ARM: OMAP: h4 must have blinky leds!!
This adds generic support for the "debug board" LEDs used by most of TI's OMAP reference boards, and board-specific support for the H4. It's derived from the not-as-generic stuff used by OMAP1 H2/H3/P2. Those should be able to switch easily to this version, and clean up some of the omap1-specific code. In addition to H4 support, one key improvement is supporting not just the "old" ARM debug LED API (with timer and idle LEDs, plus four that can be handy for kernel debugging), but it also supports the "new" generic LED API (most useful for usermode stuff IMO). Either or both APIs can be enabled. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/plat-omap')
-rw-r--r--arch/arm/plat-omap/Kconfig5
-rw-r--r--arch/arm/plat-omap/Makefile2
-rw-r--r--arch/arm/plat-omap/debug-leds.c319
3 files changed, 325 insertions, 1 deletions
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
index f2dc363de66b..ce8bc3304e19 100644
--- a/arch/arm/plat-omap/Kconfig
+++ b/arch/arm/plat-omap/Kconfig
@@ -19,6 +19,11 @@ endchoice
19 19
20comment "OMAP Feature Selections" 20comment "OMAP Feature Selections"
21 21
22config OMAP_DEBUG_LEDS
23 bool
24 help
25 For debug card leds on TI reference boards.
26
22config OMAP_RESET_CLOCKS 27config OMAP_RESET_CLOCKS
23 bool "Reset unused clocks during boot" 28 bool "Reset unused clocks during boot"
24 depends on ARCH_OMAP 29 depends on ARCH_OMAP
diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile
index 2896b4546411..4ce277d3485a 100644
--- a/arch/arm/plat-omap/Makefile
+++ b/arch/arm/plat-omap/Makefile
@@ -16,4 +16,4 @@ obj-$(CONFIG_ARCH_OMAP16XX) += ocpi.o
16 16
17obj-$(CONFIG_CPU_FREQ) += cpu-omap.o 17obj-$(CONFIG_CPU_FREQ) += cpu-omap.o
18obj-$(CONFIG_OMAP_DM_TIMER) += dmtimer.o 18obj-$(CONFIG_OMAP_DM_TIMER) += dmtimer.o
19 19obj-$(CONFIG_OMAP_DEBUG_LEDS) += debug-leds.o
diff --git a/arch/arm/plat-omap/debug-leds.c b/arch/arm/plat-omap/debug-leds.c
new file mode 100644
index 000000000000..511d6a50041e
--- /dev/null
+++ b/arch/arm/plat-omap/debug-leds.c
@@ -0,0 +1,319 @@
1/*
2 * linux/arch/arm/plat-omap/debug-leds.c
3 *
4 * Copyright 2003 by Texas Instruments Incorporated
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#include <linux/init.h>
12#include <linux/platform_device.h>
13#include <linux/leds.h>
14
15#include <asm/io.h>
16#include <asm/hardware.h>
17#include <asm/leds.h>
18#include <asm/system.h>
19#include <asm/mach-types.h>
20
21#include <asm/arch/fpga.h>
22#include <asm/arch/gpio.h>
23
24
25/* Many OMAP development platforms reuse the same "debug board"; these
26 * platforms include H2, H3, H4, and Perseus2. There are 16 LEDs on the
27 * debug board (all green), accessed through FPGA registers.
28 *
29 * The "surfer" expansion board and H2 sample board also have two-color
30 * green+red LEDs (in parallel), used here for timer and idle indicators
31 * in preference to the ones on the debug board, for a "Disco LED" effect.
32 *
33 * This driver exports either the original ARM LED API, the new generic
34 * one, or both.
35 */
36
37static spinlock_t lock;
38static struct h2p2_dbg_fpga __iomem *fpga;
39static u16 led_state, hw_led_state;
40
41
42#ifdef CONFIG_LEDS
43#define old_led_api() 1
44#else
45#define old_led_api() 0
46#endif
47
48#ifdef CONFIG_LEDS_OMAP_DEBUG
49#define new_led_api() 1
50#else
51#define new_led_api() 0
52#endif
53
54
55/*-------------------------------------------------------------------------*/
56
57/* original ARM debug LED API:
58 * - timer and idle leds (some boards use non-FPGA leds here);
59 * - up to 4 generic leds, easily accessed in-kernel (any context)
60 */
61
62#define GPIO_LED_RED 3
63#define GPIO_LED_GREEN OMAP_MPUIO(4)
64
65#define LED_STATE_ENABLED 0x01
66#define LED_STATE_CLAIMED 0x02
67#define LED_TIMER_ON 0x04
68
69#define GPIO_IDLE GPIO_LED_GREEN
70#define GPIO_TIMER GPIO_LED_RED
71
72static void h2p2_dbg_leds_event(led_event_t evt)
73{
74 unsigned long flags;
75
76 spin_lock_irqsave(&lock, flags);
77
78 if (!(led_state & LED_STATE_ENABLED) && evt != led_start)
79 goto done;
80
81 switch (evt) {
82 case led_start:
83 if (fpga)
84 led_state |= LED_STATE_ENABLED;
85 break;
86
87 case led_stop:
88 case led_halted:
89 /* all leds off during suspend or shutdown */
90
91 if (!(machine_is_omap_perseus2() || machine_is_omap_h4())) {
92 omap_set_gpio_dataout(GPIO_TIMER, 0);
93 omap_set_gpio_dataout(GPIO_IDLE, 0);
94 }
95
96 __raw_writew(~0, &fpga->leds);
97 led_state &= ~LED_STATE_ENABLED;
98 goto done;
99
100 case led_claim:
101 led_state |= LED_STATE_CLAIMED;
102 hw_led_state = 0;
103 break;
104
105 case led_release:
106 led_state &= ~LED_STATE_CLAIMED;
107 break;
108
109#ifdef CONFIG_LEDS_TIMER
110 case led_timer:
111 led_state ^= LED_TIMER_ON;
112
113 if (machine_is_omap_perseus2() || machine_is_omap_h4())
114 hw_led_state ^= H2P2_DBG_FPGA_P2_LED_TIMER;
115 else {
116 omap_set_gpio_dataout(GPIO_TIMER,
117 led_state & LED_TIMER_ON);
118 goto done;
119 }
120
121 break;
122#endif
123
124#ifdef CONFIG_LEDS_CPU
125 /* LED lit iff busy */
126 case led_idle_start:
127 if (machine_is_omap_perseus2() || machine_is_omap_h4())
128 hw_led_state &= ~H2P2_DBG_FPGA_P2_LED_IDLE;
129 else {
130 omap_set_gpio_dataout(GPIO_IDLE, 1);
131 goto done;
132 }
133
134 break;
135
136 case led_idle_end:
137 if (machine_is_omap_perseus2() || machine_is_omap_h4())
138 hw_led_state |= H2P2_DBG_FPGA_P2_LED_IDLE;
139 else {
140 omap_set_gpio_dataout(GPIO_IDLE, 0);
141 goto done;
142 }
143
144 break;
145#endif
146
147 case led_green_on:
148 hw_led_state |= H2P2_DBG_FPGA_LED_GREEN;
149 break;
150 case led_green_off:
151 hw_led_state &= ~H2P2_DBG_FPGA_LED_GREEN;
152 break;
153
154 case led_amber_on:
155 hw_led_state |= H2P2_DBG_FPGA_LED_AMBER;
156 break;
157 case led_amber_off:
158 hw_led_state &= ~H2P2_DBG_FPGA_LED_AMBER;
159 break;
160
161 case led_red_on:
162 hw_led_state |= H2P2_DBG_FPGA_LED_RED;
163 break;
164 case led_red_off:
165 hw_led_state &= ~H2P2_DBG_FPGA_LED_RED;
166 break;
167
168 case led_blue_on:
169 hw_led_state |= H2P2_DBG_FPGA_LED_BLUE;
170 break;
171 case led_blue_off:
172 hw_led_state &= ~H2P2_DBG_FPGA_LED_BLUE;
173 break;
174
175 default:
176 break;
177 }
178
179
180 /*
181 * Actually burn the LEDs
182 */
183 if (led_state & LED_STATE_ENABLED)
184 __raw_writew(~hw_led_state, &fpga->leds);
185
186done:
187 spin_unlock_irqrestore(&lock, flags);
188}
189
190/*-------------------------------------------------------------------------*/
191
192/* "new" LED API
193 * - with syfs access and generic triggering
194 * - not readily accessible to in-kernel drivers
195 */
196
197struct dbg_led {
198 struct led_classdev cdev;
199 u16 mask;
200};
201
202static struct dbg_led dbg_leds[] = {
203 /* REVISIT at least H2 uses different timer & cpu leds... */
204#ifndef CONFIG_LEDS_TIMER
205 { .mask = 1 << 0, .cdev.name = "d4:green", }, /* timer */
206#endif
207#ifndef CONFIG_LEDS_CPU
208 { .mask = 1 << 1, .cdev.name = "d5:green", }, /* !idle */
209#endif
210 { .mask = 1 << 2, .cdev.name = "d6:green", },
211 { .mask = 1 << 3, .cdev.name = "d7:green", },
212
213 { .mask = 1 << 4, .cdev.name = "d8:green", },
214 { .mask = 1 << 5, .cdev.name = "d9:green", },
215 { .mask = 1 << 6, .cdev.name = "d10:green", },
216 { .mask = 1 << 7, .cdev.name = "d11:green", },
217
218 { .mask = 1 << 8, .cdev.name = "d12:green", },
219 { .mask = 1 << 9, .cdev.name = "d13:green", },
220 { .mask = 1 << 10, .cdev.name = "d14:green", },
221 { .mask = 1 << 11, .cdev.name = "d15:green", },
222
223#ifndef CONFIG_LEDS
224 { .mask = 1 << 12, .cdev.name = "d16:green", },
225 { .mask = 1 << 13, .cdev.name = "d17:green", },
226 { .mask = 1 << 14, .cdev.name = "d18:green", },
227 { .mask = 1 << 15, .cdev.name = "d19:green", },
228#endif
229};
230
231static void
232fpga_led_set(struct led_classdev *cdev, enum led_brightness value)
233{
234 struct dbg_led *led = container_of(cdev, struct dbg_led, cdev);
235 unsigned long flags;
236
237 spin_lock_irqsave(&lock, flags);
238 if (value == LED_OFF)
239 hw_led_state &= ~led->mask;
240 else
241 hw_led_state |= led->mask;
242 __raw_writew(~hw_led_state, &fpga->leds);
243 spin_unlock_irqrestore(&lock, flags);
244}
245
246static void __init newled_init(struct device *dev)
247{
248 unsigned i;
249 struct dbg_led *led;
250 int status;
251
252 for (i = 0, led = dbg_leds; i < ARRAY_SIZE(dbg_leds); i++, led++) {
253 led->cdev.brightness_set = fpga_led_set;
254 status = led_classdev_register(dev, &led->cdev);
255 if (status < 0)
256 break;
257 }
258 return;
259}
260
261
262/*-------------------------------------------------------------------------*/
263
264static int /* __init */ fpga_probe(struct platform_device *pdev)
265{
266 struct resource *iomem;
267
268 spin_lock_init(&lock);
269
270 iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
271 if (!iomem)
272 return -ENODEV;
273
274 fpga = ioremap(iomem->start, H2P2_DBG_FPGA_SIZE);
275 __raw_writew(~0, &fpga->leds);
276
277 if (old_led_api()) {
278 leds_event = h2p2_dbg_leds_event;
279 leds_event(led_start);
280 }
281
282 if (new_led_api()) {
283 newled_init(&pdev->dev);
284 }
285
286 return 0;
287}
288
289static int fpga_suspend_late(struct platform_device *pdev, pm_message_t mesg)
290{
291 __raw_writew(~0, &fpga->leds);
292 return 0;
293}
294
295static int fpga_resume_early(struct platform_device *pdev)
296{
297 __raw_writew(~hw_led_state, &fpga->leds);
298 return 0;
299}
300
301
302static struct platform_driver led_driver = {
303 .driver.name = "omap_dbg_led",
304 .probe = fpga_probe,
305 .suspend_late = fpga_suspend_late,
306 .resume_early = fpga_resume_early,
307};
308
309static int __init fpga_init(void)
310{
311 if (machine_is_omap_h4()
312 || machine_is_omap_h3()
313 || machine_is_omap_h2()
314 || machine_is_omap_perseus2()
315 )
316 return platform_driver_register(&led_driver);
317 return 0;
318}
319fs_initcall(fpga_init);
t">]; dev->irq = mca_irqmap[(pos1>>4)&0x7]; /* * XXX: Transciever selection is done * differently on the MCA version. * How to get it to select something * other than external/AUI is currently * unknown. This code is just for looks. -- ASF */ if ((pos0 & 0x7) == 0x1) dev->if_port = AUI; else if ((pos0 & 0x7) == 0x5) { if (pos1 & 0x80) dev->if_port = BNC; else dev->if_port = TPE; } mca_set_adapter_name(slot, "Intel EtherExpress 16 MCA"); mca_set_adapter_procfn(slot, NULL, dev); mca_mark_as_used(slot); break; } } #endif if (ioaddr&0xfe00) { if (!request_region(ioaddr, EEXP_IO_EXTENT, "EtherExpress")) return -EBUSY; err = eexp_hw_probe(dev,ioaddr); release_region(ioaddr, EEXP_IO_EXTENT); return err; } else if (ioaddr) return -ENXIO; for (port=&ports[0] ; *port ; port++ ) { unsigned short sum = 0; int i; if (!request_region(*port, EEXP_IO_EXTENT, "EtherExpress")) continue; for ( i=0 ; i<4 ; i++ ) { unsigned short t; t = inb(*port + ID_PORT); sum |= (t>>4) << ((t & 0x03)<<2); } if (sum==0xbaba && !eexp_hw_probe(dev,*port)) { release_region(*port, EEXP_IO_EXTENT); return 0; } release_region(*port, EEXP_IO_EXTENT); dev->irq = dev_irq; } return -ENODEV; } #ifndef MODULE struct net_device * __init express_probe(int unit) { struct net_device *dev = alloc_etherdev(sizeof(struct net_local)); int err; if (!dev) return ERR_PTR(-ENOMEM); sprintf(dev->name, "eth%d", unit); netdev_boot_setup_check(dev); err = do_express_probe(dev); if (!err) return dev; free_netdev(dev); return ERR_PTR(err); } #endif /* * open and initialize the adapter, ready for use */ static int eexp_open(struct net_device *dev) { int ret; unsigned short ioaddr = dev->base_addr; struct net_local *lp = netdev_priv(dev); #if NET_DEBUG > 6 printk(KERN_DEBUG "%s: eexp_open()\n", dev->name); #endif if (!dev->irq || !irqrmap[dev->irq]) return -ENXIO; ret = request_irq(dev->irq,&eexp_irq,0,dev->name,dev); if (ret) return ret; if (!request_region(ioaddr, EEXP_IO_EXTENT, "EtherExpress")) { printk(KERN_WARNING "EtherExpress io port %x, is busy.\n" , ioaddr); goto err_out1; } if (!request_region(ioaddr+0x4000, EEXP_IO_EXTENT, "EtherExpress shadow")) { printk(KERN_WARNING "EtherExpress io port %x, is busy.\n" , ioaddr+0x4000); goto err_out2; } if (!request_region(ioaddr+0x8000, EEXP_IO_EXTENT, "EtherExpress shadow")) { printk(KERN_WARNING "EtherExpress io port %x, is busy.\n" , ioaddr+0x8000); goto err_out3; } if (!request_region(ioaddr+0xc000, EEXP_IO_EXTENT, "EtherExpress shadow")) { printk(KERN_WARNING "EtherExpress io port %x, is busy.\n" , ioaddr+0xc000); goto err_out4; } if (lp->width) { printk("%s: forcing ASIC to 8-bit mode\n", dev->name); outb(inb(dev->base_addr+Config)&~4, dev->base_addr+Config); } eexp_hw_init586(dev); netif_start_queue(dev); #if NET_DEBUG > 6 printk(KERN_DEBUG "%s: leaving eexp_open()\n", dev->name); #endif return 0; err_out4: release_region(ioaddr+0x8000, EEXP_IO_EXTENT); err_out3: release_region(ioaddr+0x4000, EEXP_IO_EXTENT); err_out2: release_region(ioaddr, EEXP_IO_EXTENT); err_out1: free_irq(dev->irq, dev); return -EBUSY; } /* * close and disable the interface, leaving the 586 in reset. */ static int eexp_close(struct net_device *dev) { unsigned short ioaddr = dev->base_addr; struct net_local *lp = netdev_priv(dev); int irq = dev->irq; netif_stop_queue(dev); outb(SIRQ_dis|irqrmap[irq],ioaddr+SET_IRQ); lp->started = 0; scb_command(dev, SCB_CUsuspend|SCB_RUsuspend); outb(0,ioaddr+SIGNAL_CA); free_irq(irq,dev); outb(i586_RST,ioaddr+EEPROM_Ctrl); release_region(ioaddr, EEXP_IO_EXTENT); release_region(ioaddr+0x4000, 16); release_region(ioaddr+0x8000, 16); release_region(ioaddr+0xc000, 16); return 0; } /* * Return interface stats */ static struct net_device_stats *eexp_stats(struct net_device *dev) { struct net_local *lp = netdev_priv(dev); return &lp->stats; } /* * This gets called when a higher level thinks we are broken. Check that * nothing has become jammed in the CU. */ static void unstick_cu(struct net_device *dev) { struct net_local *lp = netdev_priv(dev); unsigned short ioaddr = dev->base_addr; if (lp->started) { if ((jiffies - dev->trans_start)>50) { if (lp->tx_link==lp->last_tx_restart) { unsigned short boguscount=200,rsst; printk(KERN_WARNING "%s: Retransmit timed out, status %04x, resetting...\n", dev->name, scb_status(dev)); eexp_hw_txinit(dev); lp->last_tx_restart = 0; scb_wrcbl(dev, lp->tx_link); scb_command(dev, SCB_CUstart); outb(0,ioaddr+SIGNAL_CA); while (!SCB_complete(rsst=scb_status(dev))) { if (!--boguscount) { boguscount=200; printk(KERN_WARNING "%s: Reset timed out status %04x, retrying...\n", dev->name,rsst); scb_wrcbl(dev, lp->tx_link); scb_command(dev, SCB_CUstart); outb(0,ioaddr+SIGNAL_CA); } } netif_wake_queue(dev); } else { unsigned short status = scb_status(dev); if (SCB_CUdead(status)) { unsigned short txstatus = eexp_hw_lasttxstat(dev); printk(KERN_WARNING "%s: Transmit timed out, CU not active status %04x %04x, restarting...\n", dev->name, status, txstatus); eexp_hw_txrestart(dev); } else { unsigned short txstatus = eexp_hw_lasttxstat(dev); if (netif_queue_stopped(dev) && !txstatus) { printk(KERN_WARNING "%s: CU wedged, status %04x %04x, resetting...\n", dev->name,status,txstatus); eexp_hw_init586(dev); netif_wake_queue(dev); } else { printk(KERN_WARNING "%s: transmit timed out\n", dev->name); } } } } } else { if ((jiffies-lp->init_time)>10) { unsigned short status = scb_status(dev); printk(KERN_WARNING "%s: i82586 startup timed out, status %04x, resetting...\n", dev->name, status); eexp_hw_init586(dev); netif_wake_queue(dev); } } } static void eexp_timeout(struct net_device *dev) { struct net_local *lp = netdev_priv(dev); #ifdef CONFIG_SMP unsigned long flags; #endif int status; disable_irq(dev->irq); /* * Best would be to use synchronize_irq(); spin_lock() here * lets make it work first.. */ #ifdef CONFIG_SMP spin_lock_irqsave(&lp->lock, flags); #endif status = scb_status(dev); unstick_cu(dev); printk(KERN_INFO "%s: transmit timed out, %s?\n", dev->name, (SCB_complete(status)?"lost interrupt": "board on fire")); lp->stats.tx_errors++; lp->last_tx = jiffies; if (!SCB_complete(status)) { scb_command(dev, SCB_CUabort); outb(0,dev->base_addr+SIGNAL_CA); } netif_wake_queue(dev); #ifdef CONFIG_SMP spin_unlock_irqrestore(&lp->lock, flags); #endif } /* * Called to transmit a packet, or to allow us to right ourselves * if the kernel thinks we've died. */ static int eexp_xmit(struct sk_buff *buf, struct net_device *dev) { struct net_local *lp = netdev_priv(dev); short length = buf->len; #ifdef CONFIG_SMP unsigned long flags; #endif #if NET_DEBUG > 6 printk(KERN_DEBUG "%s: eexp_xmit()\n", dev->name); #endif if (buf->len < ETH_ZLEN) { buf = skb_padto(buf, ETH_ZLEN); if (buf == NULL) return 0; length = ETH_ZLEN; } disable_irq(dev->irq); /* * Best would be to use synchronize_irq(); spin_lock() here * lets make it work first.. */ #ifdef CONFIG_SMP spin_lock_irqsave(&lp->lock, flags); #endif { unsigned short *data = (unsigned short *)buf->data; lp->stats.tx_bytes += length; eexp_hw_tx_pio(dev,data,length); } dev_kfree_skb(buf); #ifdef CONFIG_SMP spin_unlock_irqrestore(&lp->lock, flags); #endif enable_irq(dev->irq); return 0; } /* * Handle an EtherExpress interrupt * If we've finished initializing, start the RU and CU up. * If we've already started, reap tx buffers, handle any received packets, * check to make sure we've not become wedged. */ /* * Handle an EtherExpress interrupt * If we've finished initializing, start the RU and CU up. * If we've already started, reap tx buffers, handle any received packets, * check to make sure we've not become wedged. */ static unsigned short eexp_start_irq(struct net_device *dev, unsigned short status) { unsigned short ack_cmd = SCB_ack(status); struct net_local *lp = netdev_priv(dev); unsigned short ioaddr = dev->base_addr; if ((dev->flags & IFF_UP) && !(lp->started & STARTED_CU)) { short diag_status, tdr_status; while (SCB_CUstat(status)==2) status = scb_status(dev); #if NET_DEBUG > 4 printk("%s: CU went non-active (status %04x)\n", dev->name, status); #endif outw(CONF_DIAG_RESULT & ~31, ioaddr + SM_PTR); diag_status = inw(ioaddr + SHADOW(CONF_DIAG_RESULT)); if (diag_status & 1<<11) { printk(KERN_WARNING "%s: 82586 failed self-test\n", dev->name); } else if (!(diag_status & 1<<13)) { printk(KERN_WARNING "%s: 82586 self-test failed to complete\n", dev->name); } outw(CONF_TDR_RESULT & ~31, ioaddr + SM_PTR); tdr_status = inw(ioaddr + SHADOW(CONF_TDR_RESULT)); if (tdr_status & (TDR_SHORT|TDR_OPEN)) { printk(KERN_WARNING "%s: TDR reports cable %s at %d tick%s\n", dev->name, (tdr_status & TDR_SHORT)?"short":"broken", tdr_status & TDR_TIME, ((tdr_status & TDR_TIME) != 1) ? "s" : ""); } else if (tdr_status & TDR_XCVRPROBLEM) { printk(KERN_WARNING "%s: TDR reports transceiver problem\n", dev->name); } else if (tdr_status & TDR_LINKOK) { #if NET_DEBUG > 4 printk(KERN_DEBUG "%s: TDR reports link OK\n", dev->name); #endif } else { printk("%s: TDR is ga-ga (status %04x)\n", dev->name, tdr_status); } lp->started |= STARTED_CU; scb_wrcbl(dev, lp->tx_link); /* if the RU isn't running, start it now */ if (!(lp->started & STARTED_RU)) { ack_cmd |= SCB_RUstart; scb_wrrfa(dev, lp->rx_buf_start); lp->rx_ptr = lp->rx_buf_start; lp->started |= STARTED_RU; } ack_cmd |= SCB_CUstart | 0x2000; } if ((dev->flags & IFF_UP) && !(lp->started & STARTED_RU) && SCB_RUstat(status)==4) lp->started|=STARTED_RU; return ack_cmd; } static void eexp_cmd_clear(struct net_device *dev) { unsigned long int oldtime = jiffies; while (scb_rdcmd(dev) && ((jiffies-oldtime)<10)); if (scb_rdcmd(dev)) { printk("%s: command didn't clear\n", dev->name); } } static irqreturn_t eexp_irq(int irq, void *dev_info, struct pt_regs *regs) { struct net_device *dev = dev_info; struct net_local *lp; unsigned short ioaddr,status,ack_cmd; unsigned short old_read_ptr, old_write_ptr; if (dev==NULL) { printk(KERN_WARNING "eexpress: irq %d for unknown device\n", irq); return IRQ_NONE; } lp = netdev_priv(dev); ioaddr = dev->base_addr; spin_lock(&lp->lock); old_read_ptr = inw(ioaddr+READ_PTR); old_write_ptr = inw(ioaddr+WRITE_PTR); outb(SIRQ_dis|irqrmap[irq],ioaddr+SET_IRQ); status = scb_status(dev); #if NET_DEBUG > 4 printk(KERN_DEBUG "%s: interrupt (status %x)\n", dev->name, status); #endif if (lp->started == (STARTED_CU | STARTED_RU)) { do { eexp_cmd_clear(dev); ack_cmd = SCB_ack(status); scb_command(dev, ack_cmd); outb(0,ioaddr+SIGNAL_CA); eexp_cmd_clear(dev); if (SCB_complete(status)) { if (!eexp_hw_lasttxstat(dev)) { printk("%s: tx interrupt but no status\n", dev->name); } } if (SCB_rxdframe(status)) eexp_hw_rx_pio(dev); status = scb_status(dev); } while (status & 0xc000); if (SCB_RUdead(status)) { printk(KERN_WARNING "%s: RU stopped: status %04x\n", dev->name,status); #if 0 printk(KERN_WARNING "%s: cur_rfd=%04x, cur_rbd=%04x\n", dev->name, lp->cur_rfd, lp->cur_rbd); outw(lp->cur_rfd, ioaddr+READ_PTR); printk(KERN_WARNING "%s: [%04x]\n", dev->name, inw(ioaddr+DATAPORT)); outw(lp->cur_rfd+6, ioaddr+READ_PTR); printk(KERN_WARNING "%s: rbd is %04x\n", dev->name, rbd= inw(ioaddr+DATAPORT)); outw(rbd, ioaddr+READ_PTR); printk(KERN_WARNING "%s: [%04x %04x] ", dev->name, inw(ioaddr+DATAPORT), inw(ioaddr+DATAPORT)); outw(rbd+8, ioaddr+READ_PTR); printk("[%04x]\n", inw(ioaddr+DATAPORT)); #endif lp->stats.rx_errors++; #if 1 eexp_hw_rxinit(dev); #else lp->cur_rfd = lp->first_rfd; #endif scb_wrrfa(dev, lp->rx_buf_start); scb_command(dev, SCB_RUstart); outb(0,ioaddr+SIGNAL_CA); } } else { if (status & 0x8000) ack_cmd = eexp_start_irq(dev, status); else ack_cmd = SCB_ack(status); scb_command(dev, ack_cmd); outb(0,ioaddr+SIGNAL_CA); } eexp_cmd_clear(dev); outb(SIRQ_en|irqrmap[irq],ioaddr+SET_IRQ); #if NET_DEBUG > 6 printk("%s: leaving eexp_irq()\n", dev->name); #endif outw(old_read_ptr, ioaddr+READ_PTR); outw(old_write_ptr, ioaddr+WRITE_PTR); spin_unlock(&lp->lock); return IRQ_HANDLED; } /* * Hardware access functions */ /* * Set the cable type to use. */ static void eexp_hw_set_interface(struct net_device *dev) { unsigned char oldval = inb(dev->base_addr + 0x300e); oldval &= ~0x82; switch (dev->if_port) { case TPE: oldval |= 0x2; case BNC: oldval |= 0x80; break; } outb(oldval, dev->base_addr+0x300e); mdelay(20); } /* * Check all the receive buffers, and hand any received packets * to the upper levels. Basic sanity check on each frame * descriptor, though we don't bother trying to fix broken ones. */ static void eexp_hw_rx_pio(struct net_device *dev) { struct net_local *lp = netdev_priv(dev); unsigned short rx_block = lp->rx_ptr; unsigned short boguscount = lp->num_rx_bufs; unsigned short ioaddr = dev->base_addr; unsigned short status; #if NET_DEBUG > 6 printk(KERN_DEBUG "%s: eexp_hw_rx()\n", dev->name); #endif do { unsigned short rfd_cmd, rx_next, pbuf, pkt_len; outw(rx_block, ioaddr + READ_PTR); status = inw(ioaddr + DATAPORT); if (FD_Done(status)) { rfd_cmd = inw(ioaddr + DATAPORT); rx_next = inw(ioaddr + DATAPORT); pbuf = inw(ioaddr + DATAPORT); outw(pbuf, ioaddr + READ_PTR); pkt_len = inw(ioaddr + DATAPORT); if (rfd_cmd!=0x0000) { printk(KERN_WARNING "%s: rfd_cmd not zero:0x%04x\n", dev->name, rfd_cmd); continue; } else if (pbuf!=rx_block+0x16) { printk(KERN_WARNING "%s: rfd and rbd out of sync 0x%04x 0x%04x\n", dev->name, rx_block+0x16, pbuf); continue; } else if ((pkt_len & 0xc000)!=0xc000) { printk(KERN_WARNING "%s: EOF or F not set on received buffer (%04x)\n", dev->name, pkt_len & 0xc000); continue; } else if (!FD_OK(status)) { lp->stats.rx_errors++; if (FD_CRC(status)) lp->stats.rx_crc_errors++; if (FD_Align(status)) lp->stats.rx_frame_errors++; if (FD_Resrc(status)) lp->stats.rx_fifo_errors++; if (FD_DMA(status)) lp->stats.rx_over_errors++; if (FD_Short(status)) lp->stats.rx_length_errors++; } else { struct sk_buff *skb; pkt_len &= 0x3fff; skb = dev_alloc_skb(pkt_len+16); if (skb == NULL) { printk(KERN_WARNING "%s: Memory squeeze, dropping packet\n",dev->name); lp->stats.rx_dropped++; break; } skb->dev = dev; skb_reserve(skb, 2); outw(pbuf+10, ioaddr+READ_PTR); insw(ioaddr+DATAPORT, skb_put(skb,pkt_len),(pkt_len+1)>>1); skb->protocol = eth_type_trans(skb,dev); netif_rx(skb); dev->last_rx = jiffies; lp->stats.rx_packets++; lp->stats.rx_bytes += pkt_len; } outw(rx_block, ioaddr+WRITE_PTR); outw(0, ioaddr+DATAPORT); outw(0, ioaddr+DATAPORT); rx_block = rx_next; } } while (FD_Done(status) && boguscount--); lp->rx_ptr = rx_block; } /* * Hand a packet to the card for transmission * If we get here, we MUST have already checked * to make sure there is room in the transmit * buffer region. */ static void eexp_hw_tx_pio(struct net_device *dev, unsigned short *buf, unsigned short len) { struct net_local *lp = netdev_priv(dev); unsigned short ioaddr = dev->base_addr; if (LOCKUP16 || lp->width) { /* Stop the CU so that there is no chance that it jumps off to a bogus address while we are writing the pointer to the next transmit packet in 8-bit mode -- this eliminates the "CU wedged" errors in 8-bit mode. (Zoltan Szilagyi 10-12-96) */ scb_command(dev, SCB_CUsuspend); outw(0xFFFF, ioaddr+SIGNAL_CA); } outw(lp->tx_head, ioaddr + WRITE_PTR); outw(0x0000, ioaddr + DATAPORT); outw(Cmd_INT|Cmd_Xmit, ioaddr + DATAPORT); outw(lp->tx_head+0x08, ioaddr + DATAPORT); outw(lp->tx_head+0x0e, ioaddr + DATAPORT); outw(0x0000, ioaddr + DATAPORT); outw(0x0000, ioaddr + DATAPORT); outw(lp->tx_head+0x08, ioaddr + DATAPORT); outw(0x8000|len, ioaddr + DATAPORT); outw(-1, ioaddr + DATAPORT); outw(lp->tx_head+0x16, ioaddr + DATAPORT); outw(0, ioaddr + DATAPORT); outsw(ioaddr + DATAPORT, buf, (len+1)>>1); outw(lp->tx_tail+0xc, ioaddr + WRITE_PTR); outw(lp->tx_head, ioaddr + DATAPORT); dev->trans_start = jiffies; lp->tx_tail = lp->tx_head; if (lp->tx_head==TX_BUF_START+((lp->num_tx_bufs-1)*TX_BUF_SIZE)) lp->tx_head = TX_BUF_START; else lp->tx_head += TX_BUF_SIZE; if (lp->tx_head != lp->tx_reap) netif_wake_queue(dev); if (LOCKUP16 || lp->width) { /* Restart the CU so that the packet can actually