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