mac_esp bus error handling, was Re: setjmp/longjmp?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 





On Wed, 16 Jan 2008, Andreas Schwab wrote:

The reason I ask is that the only working Mac pseudo DMA 
implementation I have found uses it (in NetBSD/FreeBSD, 
sys/arch/mac68k/obio/esp.c) to longjmp from the bus error handler back 
to the driver. My driver doesn't use that algorithm and doesn't work 
:-(

Anyone have any suggestions?

See <asm/uaccess.h> for how to use exception handlers.

OK, I've got the mac_esp driver mostly working (finally!). PIO works, PDMA 
writes (to memory) work. The one remaining bug is that sometimes a pseudo 
DMA read will crash with an exception inside buserr_c() itself (but mostly 
it works fine).

With PDMA, both reads and writes generate and trap a lot of bus errors 
(indicating full/empty FIFO state for memory read/write respectively).

My best guess is that there is a crash when the driver interrupts the 
wrong task at the wrong time and then does some PDMA, which generates an 
(expected) bus error, but trapping that bus error somehow leaves the 
machine in the wrong state for the task that was interrupted, leading to 
the crash? Perhaps the PDMA bus error handler needs to do some sort of 
fixup in some circumstances?

The PC given by the oops message is usually buserr_c+0x4a6/0x546

    35b8:       4e7b 4000       movec %d4,%sfc

but there was also a crash at buserr_c+0x3fc/0x546

    350e:       4e7b 4000       movec %d4,%sfc

I think those instructions are set_fs(), which is called by 
do_040writeback1() and probe040(). That's as about all I can tell. I'm out 
of my depth here. Any help would be greatly appreciated!

If anyone wants to take a look, the kernel binary is at 
http://www.telegraphics.com.au/~fthain/oops/vmlinux
The mac_esp driver patch and oops message follow.

Tia,
Finn


Unable to handle kernel NULL pointer dereference at virtual address 00000000
Oops: 00000000
Modules linked in:
PC: [<000035b8>] buserr_c+0x4a6/0x546
SR: 2704  SP: 0079ba60  a2: 00478030
d0: 00000020    d1: 00000020    d2: 00000000    d3: 00000000
d4: 00000001    d5: 00000000    a0: 50f10100    a1: 001d6bc8
Process dd (pid: 198, task=00478030)
Frame format=7 eff addr=0079ba18 ssw=00a5 faddr=50f10100
wb 1 stat/addr/data: 00a5 50f10100 00000000
wb 2 stat/addr/data: 0025 50f10100 00000000
wb 3 stat/addr/data: 0045 0079bb58 00000000
push data: 00000000 00102000 0000362a 0079baf8
Stack from 0079bac8:
        00000000 0007a120 00000000 00000000 00000001 00501f80 0802b001 005282a0
        00273078 0079bb14 0000271a 0079baf8 00000000 00000000 0007a120 00000000
        00000000 50f10100 00001a00 00478030 00000001 ffffffff 00000034 27040016
        6be47008 0079bb48 00a50025 00a500a5 50f10100 50f10100 00000000 50f10100
        00000000 50f10100 00000000 00000000 27040016 6be60008 00000000 00533050
        0802a800 00163b62 00000001 0079bec6 00000800 002aa970 005282a0 00163ba8
Call Trace: [<0007a120>] mem_open+0x4/0xe
 [<0000271a>] buserr+0x1e/0x24
 [<0007a120>] mem_open+0x4/0xe
 [<00001a00>] _stext+0xa00/0x1000
 [<00163b62>] esp_cur_dma_addr+0x0/0x46
 [<00163ba8>] esp_cur_dma_len+0x0/0x30
 [<00002700>] buserr+0x4/0x24
 [<001656d6>] esp_process_event+0x1f0/0x7d4
 [<00002200>] try_name+0x10c/0x1a0
 [<001654e6>] esp_process_event+0x0/0x7d4
 [<00165f1c>] __esp_interrupt+0x94/0x1c2
 [<00165e88>] __esp_interrupt+0x0/0x1c2
 [<00166076>] scsi_esp_intr+0x2c/0x6c
 [<00004186>] m68k_handle_int+0x26/0x36
 [<000089ac>] via2_irq+0x60/0x6a
 [<00004186>] m68k_handle_int+0x26/0x36
 [<000041b0>] __m68k_handle_int+0x1a/0x24
 [<000028ae>] auto_irqhandler_fixup+0x4/0x6
 [<0010234c>] init+0xc/0x18
 [<0010b7b0>] generic_make_request+0x128/0x1b2
 [<0006f058>] bio_alloc_bioset+0x9a/0x11c
 [<0010b90c>] submit_bio+0xd2/0xde
 [<0006f0ee>] bio_alloc+0x14/0x28
 [<0006e984>] submit_bh+0xca/0x15e
 [<0006d19a>] __block_write_full_page+0xe8/0x338
 [<0007026c>] blkdev_get_block+0x0/0x40
 [<0006e816>] block_write_full_page+0xaa/0xc4
 [<0007026c>] blkdev_get_block+0x0/0x40
 [<0007036a>] blkdev_writepage+0x12/0x18
 [<0007026c>] blkdev_get_block+0x0/0x40
 [<0003b010>] __writepage+0x16/0x34
 [<0003aeea>] write_cache_pages+0x184/0x294
 [<0003b050>] generic_writepages+0x22/0x2a
 [<0003affa>] __writepage+0x0/0x34
 [<0003b080>] do_writepages+0x28/0x44
 [<00035b2e>] __filemap_fdatawrite_range+0xb8/0xc6
 [<00010000>] slog2+0x5e0/0x8f0
 [<00007fff>] iop_listen+0x27/0x62
 [<00001000>] _stext+0x0/0x1000
 [<00035b58>] filemap_fdatawrite+0x1c/0x22
 [<00035e5a>] filemap_write_and_wait+0x18/0x3a
 [<0006bd26>] sync_blockdev+0x18/0x1e
 [<000711c8>] __blkdev_put+0xe8/0xfc
 [<000711e6>] blkdev_put+0xa/0xe
 [<00071212>] blkdev_close+0x28/0x2e
 [<0005278a>] __fput+0xf2/0x10c
 [<00052692>] fput+0x1e/0x24
 [<000514be>] filp_close+0x40/0x6a
 [<00051538>] sys_close+0x50/0x78
 [<00001000>] _stext+0x0/0x1000
 [<000027e2>] syscall+0x8/0xc
 [<00001000>] _stext+0x0/0x1000
 [<0000c00c>] PTENRP+0x7c/0x9c

Code: 7060 c081 7220 b280 6758 6d48 4a80 673c <4e7b> 4000 4e7b 4001 2605 6728 202b 004c 322b 003c 2740 0040 0241 00ff 3741 0038
Kernel panic - not syncing: Aiee, killing interrupt handler!



---
 drivers/scsi/Makefile   |    1 
 drivers/scsi/esp_scsi.c |   32 +-
 drivers/scsi/esp_scsi.h |   11 
 drivers/scsi/jazz_esp.c |   25 -
 drivers/scsi/mac_esp.c  |  654 ++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/sun_esp.c  |   25 -
 6 files changed, 706 insertions(+), 42 deletions(-)

Index: linux-2.6.22/drivers/scsi/esp_scsi.c
===================================================================
--- linux-2.6.22.orig/drivers/scsi/esp_scsi.c	2007-07-09 09:32:17.000000000 +1000
+++ linux-2.6.22/drivers/scsi/esp_scsi.c	2008-02-19 12:05:06.000000000 +1100
@@ -477,7 +477,7 @@ static void esp_write_tgt_sync(struct es
 	}
 }
 
-static u32 esp_dma_length_limit(struct esp *esp, u32 dma_addr, u32 dma_len)
+u32 esp_dma_length_limit(struct esp *esp, u32 dma_addr, u32 dma_len)
 {
 	if (esp->rev == FASHME) {
 		/* Arbitrary segment boundaries, 24-bit counts.  */
@@ -506,6 +506,7 @@ static u32 esp_dma_length_limit(struct e
 	}
 	return dma_len;
 }
+EXPORT_SYMBOL(esp_dma_length_limit);
 
 static int esp_need_to_nego_wide(struct esp_target_data *tp)
 {
@@ -982,7 +983,7 @@ static int esp_check_spur_intr(struct es
 			 */
 			if (!esp->ops->dma_error(esp)) {
 				printk(KERN_ERR PFX "esp%d: Spurious irq, "
-				       "sreg=%x.\n",
+				       "sreg=%02x.\n",
 				       esp->host->unique_id, esp->sreg);
 				return -1;
 			}
@@ -1451,6 +1452,9 @@ static void esp_msgin_sdtr(struct esp *e
 	if (offset > 15)
 		goto do_reject;
 
+	if (esp->flags & ESP_FLAG_DISABLE_SYNC)
+		offset = 0;
+
 	if (offset) {
 		int rounded_up, one_clock;
 
@@ -1701,7 +1705,7 @@ again:
 		else
 			ent->flags &= ~ESP_CMD_FLAG_WRITE;
 
-		dma_len = esp_dma_length_limit(esp, dma_addr, dma_len);
+		dma_len = esp->ops->dma_length_limit(esp, dma_addr, dma_len);
 		esp->data_dma_len = dma_len;
 
 		if (!dma_len) {
@@ -1765,7 +1769,6 @@ again:
 		esp_advance_dma(esp, ent, cmd, bytes_sent);
 		esp_event(esp, ESP_EVENT_CHECK_PHASE);
 		goto again;
-		break;
 	}
 
 	case ESP_EVENT_STATUS: {
@@ -2239,7 +2242,7 @@ static void esp_bootup_reset(struct esp 
 
 static void __devinit esp_set_clock_params(struct esp *esp)
 {
-	int fmhz;
+	int fhz;
 	u8 ccf;
 
 	/* This is getting messy but it has to be done correctly or else
@@ -2274,9 +2277,9 @@ static void __devinit esp_set_clock_para
 	 *    This entails the smallest and largest sync period we could ever
 	 *    handle on this ESP.
 	 */
-	fmhz = esp->cfreq;
+	fhz = esp->cfreq;
 
-	ccf = ((fmhz / 1000000) + 4) / 5;
+	ccf = ((fhz / 1000000) + 4) / 5;
 	if (ccf == 1)
 		ccf = 2;
 
@@ -2285,16 +2288,15 @@ static void __devinit esp_set_clock_para
 	 * been unable to find the clock-frequency PROM property.  All
 	 * other machines provide useful values it seems.
 	 */
-	if (fmhz <= 5000000 || ccf < 1 || ccf > 8) {
-		fmhz = 20000000;
+	if (fhz <= 5000000 || ccf < 1 || ccf > 8) {
+		fhz = 20000000;
 		ccf = 4;
 	}
 
 	esp->cfact = (ccf == 8 ? 0 : ccf);
-	esp->cfreq = fmhz;
-	esp->ccycle = ESP_MHZ_TO_CYCLE(fmhz);
-	esp->ctick = ESP_TICK(ccf, esp->ccycle);
-	esp->neg_defp = ESP_NEG_DEFP(fmhz, ccf);
+	esp->cfreq = fhz;
+	esp->ccycle = ESP_HZ_TO_CYCLE(fhz);
+	esp->neg_defp = ESP_NEG_DEFP(fhz, ccf);
 	esp->sync_defp = SYNC_DEFP_SLOW;
 }
 
@@ -2328,10 +2330,10 @@ int __devinit scsi_esp_register(struct e
 	esp_bootup_reset(esp);
 
 	printk(KERN_INFO PFX "esp%u, regs[%1p:%1p] irq[%u]\n",
-	       esp->host->unique_id, esp->regs, esp->dma_regs,
+	       instance, esp->regs, esp->dma_regs,
 	       esp->host->irq);
 	printk(KERN_INFO PFX "esp%u is a %s, %u MHz (ccf=%u), SCSI ID %u\n",
-	       esp->host->unique_id, esp_chip_names[esp->rev],
+	       instance, esp_chip_names[esp->rev],
 	       esp->cfreq / 1000000, esp->cfact, esp->scsi_id);
 
 	/* Let the SCSI bus reset settle. */
Index: linux-2.6.22/drivers/scsi/esp_scsi.h
===================================================================
--- linux-2.6.22.orig/drivers/scsi/esp_scsi.h	2007-07-09 09:32:17.000000000 +1000
+++ linux-2.6.22/drivers/scsi/esp_scsi.h	2008-02-19 12:05:06.000000000 +1100
@@ -224,8 +224,7 @@
 #define ESP_TIMEO_CONST       8192
 #define ESP_NEG_DEFP(mhz, cfact) \
         ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (8192 * (cfact)))
-#define ESP_MHZ_TO_CYCLE(mhertz)  ((1000000000) / ((mhertz) / 1000))
-#define ESP_TICK(ccf, cycle)  ((7682 * (ccf) * (cycle) / 1000))
+#define ESP_HZ_TO_CYCLE(hertz)  ((1000000000) / ((hertz) / 1000))
 
 /* For slow to medium speed input clock rates we shoot for 5mb/s, but for high
  * input clock rates we try to do 10mb/s although I don't think a transfer can
@@ -368,6 +367,12 @@ struct esp_driver_ops {
 	 */
 	int (*irq_pending)(struct esp *esp);
 
+	/* Return the maximum allowable size of a DMA transfer for a
+	 * given buffer.
+	 */
+	u32 (*dma_length_limit)(struct esp *esp, u32 dma_addr,
+	                        u32 dma_len);
+
 	/* Reset the DMA engine entirely.  On return, ESP interrupts
 	 * should be enabled.  Often the interrupt enabling is
 	 * controlled in the DMA engine.
@@ -471,6 +476,7 @@ struct esp {
 #define ESP_FLAG_DOING_SLOWCMD	0x00000004
 #define ESP_FLAG_WIDE_CAPABLE	0x00000008
 #define ESP_FLAG_QUICKIRQ_CHECK	0x00000010
+#define ESP_FLAG_DISABLE_SYNC	0x00000020
 
 	u8			select_state;
 #define ESP_SELECT_NONE		0x00 /* Not selecting */
@@ -499,7 +505,6 @@ struct esp {
 	u32			cfact;
 	u32			cfreq;
 	u32			ccycle;
-	u32			ctick;
 	u32			neg_defp;
 	u32			sync_defp;
 
Index: linux-2.6.22/drivers/scsi/Makefile
===================================================================
--- linux-2.6.22.orig/drivers/scsi/Makefile	2008-02-19 12:05:06.000000000 +1100
+++ linux-2.6.22/drivers/scsi/Makefile	2008-02-19 12:05:06.000000000 +1100
@@ -52,6 +52,7 @@ obj-$(CONFIG_FASTLANE_SCSI)	+= NCR53C9x.
 obj-$(CONFIG_OKTAGON_SCSI)	+= NCR53C9x.o	oktagon_esp_mod.o
 obj-$(CONFIG_ATARI_SCSI)	+= atari_scsi.o
 obj-$(CONFIG_MAC_SCSI)		+= mac_scsi.o
+obj-$(CONFIG_SCSI_MAC_ESP)	+= esp_scsi.o	mac_esp.o
 obj-$(CONFIG_SUN3_SCSI)		+= sun3_scsi.o  sun3_scsi_vme.o
 obj-$(CONFIG_MVME16x_SCSI)	+= 53c700.o	mvme16x_scsi.o
 obj-$(CONFIG_BVME6000_SCSI)	+= 53c700.o	bvme6000_scsi.o
Index: linux-2.6.22/drivers/scsi/mac_esp.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22/drivers/scsi/mac_esp.c	2008-02-19 13:00:48.000000000 +1100
@@ -0,0 +1,654 @@
+/* mac_esp.c: ESP front-end for Macintosh Quadra systems.
+ *
+ * Adapted from jazz_esp.c and the old mac_esp.c.
+ *
+ * The pseudo DMA algorithm is based on the one used in NetBSD.
+ * See sys/arch/mac68k/obio/esp.c for some background information.
+ *
+ * Copyright (C) 2007-2008 Finn Thain
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+#include <linux/delay.h>
+
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <asm/nubus.h>
+#include <asm/macints.h>
+#include <asm/macintosh.h>
+
+#include <scsi/scsi_host.h>
+
+#include "esp_scsi.h"
+
+#define DRV_MODULE_NAME     "mac_esp"
+#define PFX                 DRV_MODULE_NAME ": "
+#define DRV_VERSION         "1.000"
+#define DRV_MODULE_RELDATE  "Sept 15, 2007"
+
+#define MAC_ESP_IO_BASE          0x50F00000
+#define MAC_ESP_REGS_QUADRA      (MAC_ESP_IO_BASE + 0x10000)
+#define MAC_ESP_REGS_QUADRA2     (MAC_ESP_IO_BASE + 0xF000)
+#define MAC_ESP_REGS_QUADRA3     (MAC_ESP_IO_BASE + 0x18000)
+#define MAC_ESP_REGS_SPACING     0x402
+#define MAC_ESP_PDMA_REG         0xF9800024
+#define MAC_ESP_PDMA_REG_SPACING 0x4
+#define MAC_ESP_PDMA_IO_OFFSET   0x100
+
+#define esp_read8(REG)      mac_esp_read8(esp, REG)
+#define esp_write8(VAL,REG) mac_esp_write8(esp, VAL, REG)
+
+struct mac_esp_priv {
+	struct esp *esp;
+	void __iomem *pdma_regs;
+	void __iomem *pdma_io;
+	int error;
+};
+static struct platform_device *internal_esp, *external_esp;
+
+#define MAC_ESP_GET_PRIV(esp) ((struct mac_esp_priv *) \
+                               platform_get_drvdata((struct platform_device *) \
+			                            (esp->dev)))
+
+static inline void mac_esp_write8(struct esp *esp, u8 val, unsigned long reg)
+{
+	nubus_writeb(val, esp->regs + reg * 16);
+}
+
+static inline u8 mac_esp_read8(struct esp *esp, unsigned long reg)
+{
+	return nubus_readb(esp->regs + reg * 16);
+}
+
+/* For pseudo DMA and PIO we need the virtual address
+ * so this address mapping is the identity mapping.
+ */
+
+static dma_addr_t mac_esp_map_single(struct esp *esp, void *buf,
+                                     size_t sz, int dir)
+{
+	return (dma_addr_t)buf;
+}
+
+static int mac_esp_map_sg(struct esp *esp, struct scatterlist *sg,
+                          int num_sg, int dir)
+{
+	int i;
+
+	for (i = 0; i < num_sg; i++)
+		sg[i].dma_address = (u32)page_address(sg[i].page) + sg[i].offset;
+	return num_sg;
+}
+
+static void mac_esp_unmap_single(struct esp *esp, dma_addr_t addr,
+                                 size_t sz, int dir)
+{
+	/* Nothing to do. */
+}
+
+static void mac_esp_unmap_sg(struct esp *esp, struct scatterlist *sg,
+                             int num_sg, int dir)
+{
+	/* Nothing to do. */
+}
+
+static void mac_esp_reset_dma(struct esp *esp)
+{
+	/* Nothing to do. */
+}
+
+static void mac_esp_dma_drain(struct esp *esp)
+{
+	/* Nothing to do. */
+}
+
+static void mac_esp_dma_invalidate(struct esp *esp)
+{
+	/* Nothing to do. */
+}
+
+static int mac_esp_dma_error(struct esp *esp)
+{
+	return MAC_ESP_GET_PRIV(esp)->error;
+}
+
+static inline int mac_esp_wait_for_empty_fifo(struct esp *esp)
+{
+	struct mac_esp_priv *mep = MAC_ESP_GET_PRIV(esp);
+	int i = 500000;
+
+	do {
+		if (!(esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES))
+			return 0;
+
+		if (esp_read8(ESP_STATUS) & ESP_STAT_INTR)
+			return 1;
+
+		udelay(2);
+	} while (--i);
+
+	printk(KERN_ERR PFX "FIFO is not empty (sreg %02x).\n",
+	       esp_read8(ESP_STATUS));
+	mep->error = 1;
+	return 1;
+}
+
+static inline int mac_esp_wait_for_dreq(struct esp *esp)
+{
+	struct mac_esp_priv *mep = MAC_ESP_GET_PRIV(esp);
+	int i = 500000;
+
+	do {
+		if (mep->pdma_regs == NULL) {
+			if (mac_irq_pending(IRQ_MAC_SCSIDRQ))
+				return 0;
+		} else {
+			if (nubus_readl(mep->pdma_regs) & 0x200)
+				return 0;
+		}
+
+		if (esp_read8(ESP_STATUS) & ESP_STAT_INTR)
+			return 1;
+
+		udelay(2);
+	} while (--i);
+
+	printk(KERN_ERR PFX "PDMA timeout (sreg %02x).\n",
+	       esp_read8(ESP_STATUS));
+	mep->error = 1;
+	return 1;
+}
+
+#define MAC_ESP_PDMA_LOOP(operands) \
+	asm volatile ( \
+	     "       tstw %2                   \n" \
+	     "       jbeq 20f                  \n" \
+	     "1:     movew " operands "        \n" \
+	     "2:     movew " operands "        \n" \
+	     "3:     movew " operands "        \n" \
+	     "4:     movew " operands "        \n" \
+	     "5:     movew " operands "        \n" \
+	     "6:     movew " operands "        \n" \
+	     "7:     movew " operands "        \n" \
+	     "8:     movew " operands "        \n" \
+	     "9:     movew " operands "        \n" \
+	     "10:    movew " operands "        \n" \
+	     "11:    movew " operands "        \n" \
+	     "12:    movew " operands "        \n" \
+	     "13:    movew " operands "        \n" \
+	     "14:    movew " operands "        \n" \
+	     "15:    movew " operands "        \n" \
+	     "16:    movew " operands "        \n" \
+	     "       subqw #1,%2               \n" \
+	     "       jbne 1b                   \n" \
+	     "20:    tstw %3                   \n" \
+	     "       jbeq 30f                  \n" \
+	     "21:    movew " operands "        \n" \
+	     "       subqw #1,%3               \n" \
+	     "       jbne 21b                  \n" \
+	     "30:    tstw %4                   \n" \
+	     "       jbeq 40f                  \n" \
+	     "31:    moveb " operands "        \n" \
+	     "32:    nop                       \n" \
+	     "40:                              \n" \
+	     "                                 \n" \
+	     "       .section __ex_table,\"a\" \n" \
+	     "       .align  4                 \n" \
+	     "       .long   1b,40b            \n" \
+	     "       .long   2b,40b            \n" \
+	     "       .long   3b,40b            \n" \
+	     "       .long   4b,40b            \n" \
+	     "       .long   5b,40b            \n" \
+	     "       .long   6b,40b            \n" \
+	     "       .long   7b,40b            \n" \
+	     "       .long   8b,40b            \n" \
+	     "       .long   9b,40b            \n" \
+	     "       .long  10b,40b            \n" \
+	     "       .long  11b,40b            \n" \
+	     "       .long  12b,40b            \n" \
+	     "       .long  13b,40b            \n" \
+	     "       .long  14b,40b            \n" \
+	     "       .long  15b,40b            \n" \
+	     "       .long  16b,40b            \n" \
+	     "       .long  21b,40b            \n" \
+	     "       .long  31b,40b            \n" \
+	     "       .long  32b,40b            \n" \
+	     "       .previous                 \n" \
+	     : "+a" (addr) \
+	     : "a" (mep->pdma_io), "r" (count32), "r" (count2), "g" (esp_count))
+
+static void mac_esp_send_pdma_cmd(struct esp *esp, u32 addr, u32 esp_count,
+                                  u32 dma_count, int write, u8 cmd)
+{
+	struct mac_esp_priv *mep = MAC_ESP_GET_PRIV(esp);
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	mep->error = 0;
+
+	if (!write)
+		scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+
+	esp_write8((esp_count >> 0) & 0xFF, ESP_TCLOW);
+	esp_write8((esp_count >> 8) & 0xFF, ESP_TCMED);
+
+	scsi_esp_cmd(esp, cmd);
+
+	do {
+		unsigned int count32 = esp_count >> 5;
+		unsigned int count2 = (esp_count & 0x1F) >> 1;
+		unsigned int start_addr = addr;
+
+		if (mac_esp_wait_for_dreq(esp))
+			break;
+
+		if (write) {
+			MAC_ESP_PDMA_LOOP("%1@,%0@+");
+
+			esp_count -= addr - start_addr;
+		} else {
+			unsigned int n;
+
+			MAC_ESP_PDMA_LOOP("%0@+,%1@");
+
+			if (mac_esp_wait_for_empty_fifo(esp))
+				break;
+
+			n = (esp_read8(ESP_TCMED) << 8) + esp_read8(ESP_TCLOW);
+			addr = start_addr + esp_count - n;
+			esp_count = n;
+		}
+	} while (esp_count);
+
+	local_irq_restore(flags);
+}
+
+/*
+ * Programmed IO routines follow.
+ */
+
+static inline int mac_esp_wait_for_fifo(struct esp *esp)
+{
+	int i = 500000;
+
+	do {
+		if (esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES)
+			return 0;
+
+		udelay(2);
+	} while (--i);
+
+	printk(KERN_ERR PFX "FIFO is empty (sreg %02x).\n",
+	       esp_read8(ESP_STATUS));
+	return 1;
+}
+
+static inline int mac_esp_wait_for_intr(struct esp *esp)
+{
+	int i = 500000;
+
+	do {
+		esp->sreg = esp_read8(ESP_STATUS);
+		if (esp->sreg & ESP_STAT_INTR)
+			return 0;
+
+		udelay(2);
+	} while (--i);
+
+	printk(KERN_ERR PFX "IRQ timeout (sreg %02x).\n", esp->sreg);
+	return 1;
+}
+
+#define MAC_ESP_PIO_LOOP(operands, reg1) \
+	asm volatile ( \
+	     "1:     moveb " operands " \n" \
+	     "       subqw #1,%1        \n" \
+	     "       jbne 1b            \n" \
+	     : "+a" (addr), "+r" (reg1) \
+	     : "a" (fifo))
+
+#define MAC_ESP_PIO_FILL(operands, reg1) \
+	asm volatile ( \
+	     "       moveb " operands " \n" \
+	     "       moveb " operands " \n" \
+	     "       moveb " operands " \n" \
+	     "       moveb " operands " \n" \
+	     "       moveb " operands " \n" \
+	     "       moveb " operands " \n" \
+	     "       moveb " operands " \n" \
+	     "       moveb " operands " \n" \
+	     "       moveb " operands " \n" \
+	     "       moveb " operands " \n" \
+	     "       moveb " operands " \n" \
+	     "       moveb " operands " \n" \
+	     "       moveb " operands " \n" \
+	     "       moveb " operands " \n" \
+	     "       moveb " operands " \n" \
+	     "       moveb " operands " \n" \
+	     "       subqw #8,%1        \n" \
+	     "       subqw #8,%1        \n" \
+	     : "+a" (addr), "+r" (reg1) \
+	     : "a" (fifo))
+
+#define MAC_ESP_FIFO_SIZE 16
+
+static void mac_esp_send_pio_cmd(struct esp *esp, u32 addr, u32 esp_count,
+                                 u32 dma_count, int write, u8 cmd)
+{
+	unsigned long flags;
+	struct mac_esp_priv *mep = MAC_ESP_GET_PRIV(esp);
+	u8 *fifo = esp->regs + ESP_FDATA * 16;
+
+	local_irq_save(flags);
+
+	cmd &= ~ESP_CMD_DMA;
+	mep->error = 0;
+
+	if (write) {
+		scsi_esp_cmd(esp, cmd);
+
+		if (!mac_esp_wait_for_intr(esp))
+			if (mac_esp_wait_for_fifo(esp))
+				esp_count = 0;
+	} else {
+		scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+
+		if (esp_count >= MAC_ESP_FIFO_SIZE)
+			MAC_ESP_PIO_FILL("%0@+,%2@", esp_count);
+		else
+			MAC_ESP_PIO_LOOP("%0@+,%2@", esp_count);
+
+		scsi_esp_cmd(esp, cmd);
+	}
+
+	while (esp_count) {
+		unsigned int n;
+
+		if (mac_esp_wait_for_intr(esp)) {
+			mep->error = 1;
+			break;
+		}
+
+		if (esp->sreg & ESP_STAT_SPAM) {
+			printk(KERN_ERR PFX "gross error.\n");
+			mep->error = 1;
+			break;
+		}
+
+		n = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES;
+
+		if (write) {
+			if (n > esp_count)
+				n = esp_count;
+			esp_count -= n;
+
+			MAC_ESP_PIO_LOOP("%2@,%0@+", n);
+
+			if ((esp->sreg & ESP_STAT_PMASK) == ESP_STATP)
+				break;
+
+			if (esp_count) {
+				esp->ireg = esp_read8(ESP_INTRPT);
+				if (esp->ireg & ESP_INTR_DC)
+					break;
+
+				scsi_esp_cmd(esp, ESP_CMD_TI);
+			}
+		} else {
+			esp->ireg = esp_read8(ESP_INTRPT);
+			if (esp->ireg & ESP_INTR_DC)
+				break;
+
+			n = MAC_ESP_FIFO_SIZE - n;
+			if (esp_count < n)
+				n = esp_count;
+
+			if (n == MAC_ESP_FIFO_SIZE) {
+				MAC_ESP_PIO_FILL("%0@+,%2@", esp_count);
+			} else {
+				esp_count -= n;
+				MAC_ESP_PIO_LOOP("%0@+,%2@", n);
+			}
+
+			scsi_esp_cmd(esp, ESP_CMD_TI);
+		}
+	}
+
+	local_irq_restore(flags);
+}
+
+static int mac_esp_irq_pending(struct esp *esp)
+{
+	if (esp_read8(ESP_STATUS) & ESP_STAT_INTR)
+		return 1;
+	return 0;
+}
+
+static u32 mac_esp_dma_length_limit(struct esp *esp, u32 dma_addr, u32 dma_len)
+{
+	return dma_len > 0xFFFF ? 0xFFFF : dma_len;
+}
+
+static struct esp_driver_ops mac_esp_ops = {
+	.esp_write8       = mac_esp_write8,
+	.esp_read8        = mac_esp_read8,
+	.map_single       = mac_esp_map_single,
+	.map_sg           = mac_esp_map_sg,
+	.unmap_single     = mac_esp_unmap_single,
+	.unmap_sg         = mac_esp_unmap_sg,
+	.irq_pending      = mac_esp_irq_pending,
+	.dma_length_limit = mac_esp_dma_length_limit,
+	.reset_dma        = mac_esp_reset_dma,
+	.dma_drain        = mac_esp_dma_drain,
+	.dma_invalidate   = mac_esp_dma_invalidate,
+	.send_dma_cmd     = mac_esp_send_pdma_cmd,
+	.dma_error        = mac_esp_dma_error,
+};
+
+static int __devinit esp_mac_probe(struct platform_device *dev)
+{
+	struct scsi_host_template *tpnt = &scsi_esp_template;
+	struct Scsi_Host *host;
+	struct esp *esp;
+	int err;
+	int chips_present;
+	struct mac_esp_priv *mep;
+
+	switch (macintosh_config->scsi_type) {
+	case MAC_SCSI_QUADRA:
+	case MAC_SCSI_QUADRA3:
+		chips_present = 1;
+		break;
+	case MAC_SCSI_QUADRA2:
+		if ((macintosh_config->ident == MAC_MODEL_Q900) ||
+		    (macintosh_config->ident == MAC_MODEL_Q950))
+			chips_present = 2;
+		else
+			chips_present = 1;
+		break;
+	default:
+		chips_present = 0;
+	}
+
+	if (dev->id + 1 > chips_present)
+		return -ENODEV;
+
+	host = scsi_host_alloc(tpnt, sizeof(struct esp));
+
+	err = -ENOMEM;
+	if (!host)
+		goto fail;
+
+	host->max_id = 8;
+	host->use_clustering = DISABLE_CLUSTERING;
+	esp = host_to_esp(host);
+
+	esp->host = host;
+	esp->dev = dev;
+
+	esp->command_block = dma_alloc_coherent(esp->dev, 16,
+	                                        &esp->command_block_dma,
+	                                        GFP_KERNEL);
+	if (!esp->command_block)
+		goto fail_unlink;
+
+	esp->scsi_id = 7;
+	host->this_id = esp->scsi_id;
+	esp->scsi_id_mask = 1 << esp->scsi_id;
+
+	mep = kzalloc(sizeof(struct mac_esp_priv), GFP_KERNEL);
+	if (!mep)
+		goto fail_free_command_block;
+	mep->esp = esp;
+	platform_set_drvdata(dev, mep);
+
+	switch (macintosh_config->scsi_type) {
+	case MAC_SCSI_QUADRA:
+		esp->cfreq     = 16500000;
+		esp->regs      = (void __iomem *)MAC_ESP_REGS_QUADRA;
+		mep->pdma_io   = esp->regs + MAC_ESP_PDMA_IO_OFFSET;
+		mep->pdma_regs = NULL;
+		break;
+	case MAC_SCSI_QUADRA2:
+		esp->cfreq     = 25000000;
+		esp->regs      = (void __iomem *)(MAC_ESP_REGS_QUADRA2 +
+		                 dev->id * MAC_ESP_REGS_SPACING);
+		mep->pdma_io   = esp->regs + MAC_ESP_PDMA_IO_OFFSET;
+		mep->pdma_regs = (void __iomem *)(MAC_ESP_PDMA_REG +
+		                 dev->id * MAC_ESP_PDMA_REG_SPACING);
+		nubus_writel(0x1d1, mep->pdma_regs);
+		break;
+	case MAC_SCSI_QUADRA3:
+		/* These quadras have a real DMA controller (the PSC) but we
+		 * don't know how to drive it so we must use PIO instead.
+		 */
+		esp->cfreq     = 25000000;
+		esp->regs      = (void __iomem *)MAC_ESP_REGS_QUADRA3;
+		mep->pdma_io   = NULL;
+		mep->pdma_regs = NULL;
+		break;
+	}
+
+	esp->ops = &mac_esp_ops;
+	if (mep->pdma_io == NULL) {
+		printk(KERN_INFO PFX "using PIO for controller %d\n", dev->id);
+		esp_write8(0, ESP_TCLOW);
+		esp_write8(0, ESP_TCMED);
+		esp->flags = ESP_FLAG_DISABLE_SYNC;
+		mac_esp_ops.send_dma_cmd = mac_esp_send_pio_cmd;
+	} else {
+		printk(KERN_INFO PFX "using PDMA for controller %d\n", dev->id);
+	}
+
+	host->irq = IRQ_MAC_SCSI;
+	err = request_irq(host->irq, scsi_esp_intr,
+	                  IRQF_SHARED, "Mac ESP", esp);
+	if (err < 0)
+		goto fail_free_priv;
+
+	err = scsi_esp_register(esp, &dev->dev);
+	if (err)
+		goto fail_free_irq;
+
+	return 0;
+
+fail_free_irq:
+	free_irq(host->irq, esp);
+fail_free_priv:
+	kfree(mep);
+fail_free_command_block:
+	dma_free_coherent(esp->dev, 16,
+	                  esp->command_block,
+	                  esp->command_block_dma);
+fail_unlink:
+	scsi_host_put(host);
+fail:
+	return err;
+}
+
+static int __devexit esp_mac_remove(struct platform_device *dev)
+{
+	struct mac_esp_priv *mep = platform_get_drvdata(dev);
+	struct esp *esp = mep->esp;
+	unsigned int irq = esp->host->irq;
+
+	scsi_esp_unregister(esp);
+
+	free_irq(irq, esp);
+	dma_free_coherent(esp->dev, 16,
+	                  esp->command_block,
+	                  esp->command_block_dma);
+
+	scsi_host_put(esp->host);
+
+	kfree(mep);
+
+	return 0;
+}
+
+static struct platform_driver esp_mac_driver = {
+	.probe    = esp_mac_probe,
+	.remove   = __devexit_p(esp_mac_remove),
+	.driver   = {
+		.name     = DRV_MODULE_NAME,
+	},
+};
+
+static int __init mac_esp_init(void)
+{
+	int err;
+
+	if ((err = platform_driver_register(&esp_mac_driver)))
+		return err;
+
+	internal_esp = platform_device_alloc(DRV_MODULE_NAME, 0);
+	if (internal_esp && platform_device_add(internal_esp)) {
+		platform_device_put(internal_esp);
+		internal_esp = NULL;
+	}
+
+	external_esp = platform_device_alloc(DRV_MODULE_NAME, 1);
+	if (external_esp && platform_device_add(external_esp)) {
+		platform_device_put(external_esp);
+		external_esp = NULL;
+	}
+
+	if (internal_esp || external_esp) {
+		return 0;
+	} else {
+		platform_driver_unregister(&esp_mac_driver);
+		return -ENOMEM;
+	}
+}
+
+static void __exit mac_esp_exit(void)
+{
+	platform_driver_unregister(&esp_mac_driver);
+
+	if (internal_esp) {
+		platform_device_unregister(internal_esp);
+		internal_esp = NULL;
+	}
+	if (external_esp) {
+		platform_device_unregister(external_esp);
+		external_esp = NULL;
+	}
+}
+
+MODULE_DESCRIPTION("Mac ESP SCSI driver");
+MODULE_AUTHOR("Finn Thain <fthain@xxxxxxxxxxxxxxxxxxx>");
+MODULE_LICENSE("GPLv2");
+MODULE_VERSION(DRV_VERSION);
+
+module_init(mac_esp_init);
+module_exit(mac_esp_exit);
Index: linux-2.6.22/drivers/scsi/sun_esp.c
===================================================================
--- linux-2.6.22.orig/drivers/scsi/sun_esp.c	2007-07-09 09:32:17.000000000 +1000
+++ linux-2.6.22/drivers/scsi/sun_esp.c	2008-02-19 12:05:06.000000000 +1100
@@ -461,18 +461,19 @@ static int sbus_esp_dma_error(struct esp
 }
 
 static const struct esp_driver_ops sbus_esp_ops = {
-	.esp_write8	=	sbus_esp_write8,
-	.esp_read8	=	sbus_esp_read8,
-	.map_single	=	sbus_esp_map_single,
-	.map_sg		=	sbus_esp_map_sg,
-	.unmap_single	=	sbus_esp_unmap_single,
-	.unmap_sg	=	sbus_esp_unmap_sg,
-	.irq_pending	=	sbus_esp_irq_pending,
-	.reset_dma	=	sbus_esp_reset_dma,
-	.dma_drain	=	sbus_esp_dma_drain,
-	.dma_invalidate	=	sbus_esp_dma_invalidate,
-	.send_dma_cmd	=	sbus_esp_send_dma_cmd,
-	.dma_error	=	sbus_esp_dma_error,
+	.esp_write8       =	sbus_esp_write8,
+	.esp_read8        =	sbus_esp_read8,
+	.map_single       =	sbus_esp_map_single,
+	.map_sg           =	sbus_esp_map_sg,
+	.unmap_single     =	sbus_esp_unmap_single,
+	.unmap_sg         =	sbus_esp_unmap_sg,
+	.irq_pending      =	sbus_esp_irq_pending,
+	.dma_length_limit =	esp_dma_length_limit,
+	.reset_dma        =	sbus_esp_reset_dma,
+	.dma_drain        =	sbus_esp_dma_drain,
+	.dma_invalidate   =	sbus_esp_dma_invalidate,
+	.send_dma_cmd     =	sbus_esp_send_dma_cmd,
+	.dma_error        =	sbus_esp_dma_error,
 };
 
 static int __devinit esp_sbus_probe_one(struct device *dev,
Index: linux-2.6.22/drivers/scsi/jazz_esp.c
===================================================================
--- linux-2.6.22.orig/drivers/scsi/jazz_esp.c	2007-07-09 09:32:17.000000000 +1000
+++ linux-2.6.22/drivers/scsi/jazz_esp.c	2008-02-19 12:05:06.000000000 +1100
@@ -114,18 +114,19 @@ static int jazz_esp_dma_error(struct esp
 }
 
 static const struct esp_driver_ops jazz_esp_ops = {
-	.esp_write8	=	jazz_esp_write8,
-	.esp_read8	=	jazz_esp_read8,
-	.map_single	=	jazz_esp_map_single,
-	.map_sg		=	jazz_esp_map_sg,
-	.unmap_single	=	jazz_esp_unmap_single,
-	.unmap_sg	=	jazz_esp_unmap_sg,
-	.irq_pending	=	jazz_esp_irq_pending,
-	.reset_dma	=	jazz_esp_reset_dma,
-	.dma_drain	=	jazz_esp_dma_drain,
-	.dma_invalidate	=	jazz_esp_dma_invalidate,
-	.send_dma_cmd	=	jazz_esp_send_dma_cmd,
-	.dma_error	=	jazz_esp_dma_error,
+	.esp_write8       =	jazz_esp_write8,
+	.esp_read8        =	jazz_esp_read8,
+	.map_single       =	jazz_esp_map_single,
+	.map_sg           =	jazz_esp_map_sg,
+	.unmap_single     =	jazz_esp_unmap_single,
+	.unmap_sg         =	jazz_esp_unmap_sg,
+	.irq_pending      =	jazz_esp_irq_pending,
+	.dma_length_limit =	esp_dma_length_limit,
+	.reset_dma        =	jazz_esp_reset_dma,
+	.dma_drain        =	jazz_esp_dma_drain,
+	.dma_invalidate   =	jazz_esp_dma_invalidate,
+	.send_dma_cmd     =	jazz_esp_send_dma_cmd,
+	.dma_error        =	jazz_esp_dma_error,
 };
 
 static int __devinit esp_jazz_probe(struct platform_device *dev)
-
To unsubscribe from this list: send the line "unsubscribe linux-m68k" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Video for Linux]     [Yosemite News]     [Linux S/390]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux