mirror of
https://github.com/torvalds/linux.git
synced 2024-12-30 14:52:05 +00:00
79172ab20b
Since the scsi subsystem adopted the blk-mq API, a host with zero
sg_tablesize crashes with a NULL pointer dereference.
blk_queue_max_segments: set to minimum 1
scsi 0:0:0:0: Direct-Access QEMU QEMU HARDDISK 2.5+ PQ: 0 ANSI: 5
scsi target0:0:0: Beginning Domain Validation
scsi target0:0:0: Domain Validation skipping write tests
scsi target0:0:0: Ending Domain Validation
blk_queue_max_segments: set to minimum 1
scsi 0:0:1:0: Direct-Access QEMU QEMU HARDDISK 2.5+ PQ: 0 ANSI: 5
scsi target0:0:1: Beginning Domain Validation
scsi target0:0:1: Domain Validation skipping write tests
scsi target0:0:1: Ending Domain Validation
blk_queue_max_segments: set to minimum 1
scsi 0:0:2:0: CD-ROM QEMU QEMU CD-ROM 2.5+ PQ: 0 ANSI: 5
scsi target0:0:2: Beginning Domain Validation
scsi target0:0:2: Domain Validation skipping write tests
scsi target0:0:2: Ending Domain Validation
blk_queue_max_segments: set to minimum 1
blk_queue_max_segments: set to minimum 1
blk_queue_max_segments: set to minimum 1
blk_queue_max_segments: set to minimum 1
sr 0:0:2:0: Power-on or device reset occurred
sd 0:0:0:0: Power-on or device reset occurred
sd 0:0:1:0: Power-on or device reset occurred
sd 0:0:0:0: [sda] 10485762 512-byte logical blocks: (5.37 GB/5.00 GiB)
sd 0:0:0:0: [sda] Write Protect is off
sd 0:0:0:0: [sda] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
Unable to handle kernel NULL pointer dereference at virtual address (ptrval)
Oops: 00000000
Modules linked in:
PC: [<001cd874>] blk_mq_free_request+0x66/0xe2
SR: 2004 SP: (ptrval) a2: 00874520
d0: 00000000 d1: 00000000 d2: 009ba800 d3: 00000000
d4: 00000000 d5: 08000002 a0: 0087be68 a1: 009a81e0
Process kworker/u2:2 (pid: 15, task=(ptrval))
Frame format=7 eff addr=0000007a ssw=0505 faddr=0000007a
wb 1 stat/addr/data: 0000 00000000 00000000
wb 2 stat/addr/data: 0000 00000000 00000000
wb 3 stat/addr/data: 0000 0000007a 00000000
push data: 00000000 00000000 00000000 00000000
Stack from 0087bd98:
00000002 00000000 0087be72 009a7820 0087bdb4 001c4f6c 009a7820 0087bdd4
0024d200 009a7820 0024d0dc 0087be72 009baa00 0087be68 009a5000 0087be7c
00265d10 009a5000 0087be72 00000003 00000000 00000000 00000000 0087be68
00000bb8 00000005 00000000 00000000 00000000 00000000 00265c56 00000000
009ba60c 0036ddf4 00000002 ffffffff 009baa00 009ba600 009a50d6 0087be74
00227ba0 009baa08 00000001 009baa08 009ba60c 0036ddf4 00000000 00000000
Call Trace: [<001c4f6c>] blk_put_request+0xe/0x14
[<0024d200>] __scsi_execute+0x124/0x174
[<0024d0dc>] __scsi_execute+0x0/0x174
[<00265d10>] sd_revalidate_disk+0xba/0x1f02
[<00265c56>] sd_revalidate_disk+0x0/0x1f02
[<0036ddf4>] strlen+0x0/0x22
[<00227ba0>] device_add+0x3da/0x604
[<0036ddf4>] strlen+0x0/0x22
[<00267e64>] sd_probe+0x30c/0x4b4
[<0002da44>] process_one_work+0x0/0x402
[<0022b978>] really_probe+0x226/0x354
[<0022bc34>] driver_probe_device+0xa4/0xf0
[<0002da44>] process_one_work+0x0/0x402
[<0022bcd0>] __driver_attach_async_helper+0x50/0x70
[<00035dae>] async_run_entry_fn+0x36/0x130
[<0002db88>] process_one_work+0x144/0x402
[<0002e1aa>] worker_thread+0x0/0x570
[<0002e29a>] worker_thread+0xf0/0x570
[<0002e1aa>] worker_thread+0x0/0x570
[<003768d8>] schedule+0x0/0xb8
[<0003f58c>] __init_waitqueue_head+0x0/0x12
[<00033e92>] kthread+0xc2/0xf6
[<000331e8>] kthread_parkme+0x0/0x4e
[<003768d8>] schedule+0x0/0xb8
[<00033dd0>] kthread+0x0/0xf6
[<00002c10>] ret_from_kernel_thread+0xc/0x14
Code: 0280 0006 0800 56c0 4400 0280 0000 00ff <52b4> 0c3a 082b 0006 0013 6706 2042 53a8 00c4 4ab9 0047 3374 6640 202d 000c 670c
Disabling lock debugging due to kernel taint
Avoid this by setting sg_tablesize = 1.
Link: https://lore.kernel.org/r/4567bcae94523b47d6f3b77450ba305823bca479.1572656814.git.fthain@telegraphics.com.au
Reported-and-tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
References: commit 68ab2d76e4
("scsi: cxlflash: Set sg_tablesize to 1 instead of SG_NONE")
Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
548 lines
16 KiB
C
548 lines
16 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Generic Macintosh NCR5380 driver
|
|
*
|
|
* Copyright 1998, Michael Schmitz <mschmitz@lbl.gov>
|
|
*
|
|
* Copyright 2019 Finn Thain
|
|
*
|
|
* derived in part from:
|
|
*/
|
|
/*
|
|
* Generic Generic NCR5380 driver
|
|
*
|
|
* Copyright 1995, Russell King
|
|
*/
|
|
|
|
#include <linux/delay.h>
|
|
#include <linux/types.h>
|
|
#include <linux/module.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/init.h>
|
|
#include <linux/blkdev.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <asm/hwtest.h>
|
|
#include <asm/io.h>
|
|
#include <asm/macintosh.h>
|
|
#include <asm/macints.h>
|
|
#include <asm/setup.h>
|
|
|
|
#include <scsi/scsi_host.h>
|
|
|
|
/* Definitions for the core NCR5380 driver. */
|
|
|
|
#define NCR5380_implementation_fields int pdma_residual
|
|
|
|
#define NCR5380_read(reg) in_8(hostdata->io + ((reg) << 4))
|
|
#define NCR5380_write(reg, value) out_8(hostdata->io + ((reg) << 4), value)
|
|
|
|
#define NCR5380_dma_xfer_len macscsi_dma_xfer_len
|
|
#define NCR5380_dma_recv_setup macscsi_pread
|
|
#define NCR5380_dma_send_setup macscsi_pwrite
|
|
#define NCR5380_dma_residual macscsi_dma_residual
|
|
|
|
#define NCR5380_intr macscsi_intr
|
|
#define NCR5380_queue_command macscsi_queue_command
|
|
#define NCR5380_abort macscsi_abort
|
|
#define NCR5380_host_reset macscsi_host_reset
|
|
#define NCR5380_info macscsi_info
|
|
|
|
#include "NCR5380.h"
|
|
|
|
static int setup_can_queue = -1;
|
|
module_param(setup_can_queue, int, 0);
|
|
static int setup_cmd_per_lun = -1;
|
|
module_param(setup_cmd_per_lun, int, 0);
|
|
static int setup_sg_tablesize = -1;
|
|
module_param(setup_sg_tablesize, int, 0);
|
|
static int setup_use_pdma = 512;
|
|
module_param(setup_use_pdma, int, 0);
|
|
static int setup_hostid = -1;
|
|
module_param(setup_hostid, int, 0);
|
|
static int setup_toshiba_delay = -1;
|
|
module_param(setup_toshiba_delay, int, 0);
|
|
|
|
#ifndef MODULE
|
|
static int __init mac_scsi_setup(char *str)
|
|
{
|
|
int ints[8];
|
|
|
|
(void)get_options(str, ARRAY_SIZE(ints), ints);
|
|
|
|
if (ints[0] < 1) {
|
|
pr_err("Usage: mac5380=<can_queue>[,<cmd_per_lun>[,<sg_tablesize>[,<hostid>[,<use_tags>[,<use_pdma>[,<toshiba_delay>]]]]]]\n");
|
|
return 0;
|
|
}
|
|
if (ints[0] >= 1)
|
|
setup_can_queue = ints[1];
|
|
if (ints[0] >= 2)
|
|
setup_cmd_per_lun = ints[2];
|
|
if (ints[0] >= 3)
|
|
setup_sg_tablesize = ints[3];
|
|
if (ints[0] >= 4)
|
|
setup_hostid = ints[4];
|
|
/* ints[5] (use_tagged_queuing) is ignored */
|
|
if (ints[0] >= 6)
|
|
setup_use_pdma = ints[6];
|
|
if (ints[0] >= 7)
|
|
setup_toshiba_delay = ints[7];
|
|
return 1;
|
|
}
|
|
|
|
__setup("mac5380=", mac_scsi_setup);
|
|
#endif /* !MODULE */
|
|
|
|
/*
|
|
* According to "Inside Macintosh: Devices", Mac OS requires disk drivers to
|
|
* specify the number of bytes between the delays expected from a SCSI target.
|
|
* This allows the operating system to "prevent bus errors when a target fails
|
|
* to deliver the next byte within the processor bus error timeout period."
|
|
* Linux SCSI drivers lack knowledge of the timing behaviour of SCSI targets
|
|
* so bus errors are unavoidable.
|
|
*
|
|
* If a MOVE.B instruction faults, we assume that zero bytes were transferred
|
|
* and simply retry. That assumption probably depends on target behaviour but
|
|
* seems to hold up okay. The NOP provides synchronization: without it the
|
|
* fault can sometimes occur after the program counter has moved past the
|
|
* offending instruction. Post-increment addressing can't be used.
|
|
*/
|
|
|
|
#define MOVE_BYTE(operands) \
|
|
asm volatile ( \
|
|
"1: moveb " operands " \n" \
|
|
"11: nop \n" \
|
|
" addq #1,%0 \n" \
|
|
" subq #1,%1 \n" \
|
|
"40: \n" \
|
|
" \n" \
|
|
".section .fixup,\"ax\" \n" \
|
|
".even \n" \
|
|
"90: movel #1, %2 \n" \
|
|
" jra 40b \n" \
|
|
".previous \n" \
|
|
" \n" \
|
|
".section __ex_table,\"a\" \n" \
|
|
".align 4 \n" \
|
|
".long 1b,90b \n" \
|
|
".long 11b,90b \n" \
|
|
".previous \n" \
|
|
: "+a" (addr), "+r" (n), "+r" (result) : "a" (io))
|
|
|
|
/*
|
|
* If a MOVE.W (or MOVE.L) instruction faults, it cannot be retried because
|
|
* the residual byte count would be uncertain. In that situation the MOVE_WORD
|
|
* macro clears n in the fixup section to abort the transfer.
|
|
*/
|
|
|
|
#define MOVE_WORD(operands) \
|
|
asm volatile ( \
|
|
"1: movew " operands " \n" \
|
|
"11: nop \n" \
|
|
" subq #2,%1 \n" \
|
|
"40: \n" \
|
|
" \n" \
|
|
".section .fixup,\"ax\" \n" \
|
|
".even \n" \
|
|
"90: movel #0, %1 \n" \
|
|
" movel #2, %2 \n" \
|
|
" jra 40b \n" \
|
|
".previous \n" \
|
|
" \n" \
|
|
".section __ex_table,\"a\" \n" \
|
|
".align 4 \n" \
|
|
".long 1b,90b \n" \
|
|
".long 11b,90b \n" \
|
|
".previous \n" \
|
|
: "+a" (addr), "+r" (n), "+r" (result) : "a" (io))
|
|
|
|
#define MOVE_16_WORDS(operands) \
|
|
asm volatile ( \
|
|
"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" \
|
|
"17: nop \n" \
|
|
" subl #32,%1 \n" \
|
|
"40: \n" \
|
|
" \n" \
|
|
".section .fixup,\"ax\" \n" \
|
|
".even \n" \
|
|
"90: movel #0, %1 \n" \
|
|
" movel #2, %2 \n" \
|
|
" jra 40b \n" \
|
|
".previous \n" \
|
|
" \n" \
|
|
".section __ex_table,\"a\" \n" \
|
|
".align 4 \n" \
|
|
".long 1b,90b \n" \
|
|
".long 2b,90b \n" \
|
|
".long 3b,90b \n" \
|
|
".long 4b,90b \n" \
|
|
".long 5b,90b \n" \
|
|
".long 6b,90b \n" \
|
|
".long 7b,90b \n" \
|
|
".long 8b,90b \n" \
|
|
".long 9b,90b \n" \
|
|
".long 10b,90b \n" \
|
|
".long 11b,90b \n" \
|
|
".long 12b,90b \n" \
|
|
".long 13b,90b \n" \
|
|
".long 14b,90b \n" \
|
|
".long 15b,90b \n" \
|
|
".long 16b,90b \n" \
|
|
".long 17b,90b \n" \
|
|
".previous \n" \
|
|
: "+a" (addr), "+r" (n), "+r" (result) : "a" (io))
|
|
|
|
#define MAC_PDMA_DELAY 32
|
|
|
|
static inline int mac_pdma_recv(void __iomem *io, unsigned char *start, int n)
|
|
{
|
|
unsigned char *addr = start;
|
|
int result = 0;
|
|
|
|
if (n >= 1) {
|
|
MOVE_BYTE("%3@,%0@");
|
|
if (result)
|
|
goto out;
|
|
}
|
|
if (n >= 1 && ((unsigned long)addr & 1)) {
|
|
MOVE_BYTE("%3@,%0@");
|
|
if (result)
|
|
goto out;
|
|
}
|
|
while (n >= 32)
|
|
MOVE_16_WORDS("%3@,%0@+");
|
|
while (n >= 2)
|
|
MOVE_WORD("%3@,%0@+");
|
|
if (result)
|
|
return start - addr; /* Negated to indicate uncertain length */
|
|
if (n == 1)
|
|
MOVE_BYTE("%3@,%0@");
|
|
out:
|
|
return addr - start;
|
|
}
|
|
|
|
static inline int mac_pdma_send(unsigned char *start, void __iomem *io, int n)
|
|
{
|
|
unsigned char *addr = start;
|
|
int result = 0;
|
|
|
|
if (n >= 1) {
|
|
MOVE_BYTE("%0@,%3@");
|
|
if (result)
|
|
goto out;
|
|
}
|
|
if (n >= 1 && ((unsigned long)addr & 1)) {
|
|
MOVE_BYTE("%0@,%3@");
|
|
if (result)
|
|
goto out;
|
|
}
|
|
while (n >= 32)
|
|
MOVE_16_WORDS("%0@+,%3@");
|
|
while (n >= 2)
|
|
MOVE_WORD("%0@+,%3@");
|
|
if (result)
|
|
return start - addr; /* Negated to indicate uncertain length */
|
|
if (n == 1)
|
|
MOVE_BYTE("%0@,%3@");
|
|
out:
|
|
return addr - start;
|
|
}
|
|
|
|
/* The "SCSI DMA" chip on the IIfx implements this register. */
|
|
#define CTRL_REG 0x8
|
|
#define CTRL_INTERRUPTS_ENABLE BIT(1)
|
|
#define CTRL_HANDSHAKE_MODE BIT(3)
|
|
|
|
static inline void write_ctrl_reg(struct NCR5380_hostdata *hostdata, u32 value)
|
|
{
|
|
out_be32(hostdata->io + (CTRL_REG << 4), value);
|
|
}
|
|
|
|
static inline int macscsi_pread(struct NCR5380_hostdata *hostdata,
|
|
unsigned char *dst, int len)
|
|
{
|
|
u8 __iomem *s = hostdata->pdma_io + (INPUT_DATA_REG << 4);
|
|
unsigned char *d = dst;
|
|
int result = 0;
|
|
|
|
hostdata->pdma_residual = len;
|
|
|
|
while (!NCR5380_poll_politely(hostdata, BUS_AND_STATUS_REG,
|
|
BASR_DRQ | BASR_PHASE_MATCH,
|
|
BASR_DRQ | BASR_PHASE_MATCH, HZ / 64)) {
|
|
int bytes;
|
|
|
|
if (macintosh_config->ident == MAC_MODEL_IIFX)
|
|
write_ctrl_reg(hostdata, CTRL_HANDSHAKE_MODE |
|
|
CTRL_INTERRUPTS_ENABLE);
|
|
|
|
bytes = mac_pdma_recv(s, d, min(hostdata->pdma_residual, 512));
|
|
|
|
if (bytes > 0) {
|
|
d += bytes;
|
|
hostdata->pdma_residual -= bytes;
|
|
}
|
|
|
|
if (hostdata->pdma_residual == 0)
|
|
goto out;
|
|
|
|
if (NCR5380_poll_politely2(hostdata, STATUS_REG, SR_REQ, SR_REQ,
|
|
BUS_AND_STATUS_REG, BASR_ACK,
|
|
BASR_ACK, HZ / 64) < 0)
|
|
scmd_printk(KERN_DEBUG, hostdata->connected,
|
|
"%s: !REQ and !ACK\n", __func__);
|
|
if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH))
|
|
goto out;
|
|
|
|
if (bytes == 0)
|
|
udelay(MAC_PDMA_DELAY);
|
|
|
|
if (bytes >= 0)
|
|
continue;
|
|
|
|
dsprintk(NDEBUG_PSEUDO_DMA, hostdata->host,
|
|
"%s: bus error (%d/%d)\n", __func__, d - dst, len);
|
|
NCR5380_dprint(NDEBUG_PSEUDO_DMA, hostdata->host);
|
|
result = -1;
|
|
goto out;
|
|
}
|
|
|
|
scmd_printk(KERN_ERR, hostdata->connected,
|
|
"%s: phase mismatch or !DRQ\n", __func__);
|
|
NCR5380_dprint(NDEBUG_PSEUDO_DMA, hostdata->host);
|
|
result = -1;
|
|
out:
|
|
if (macintosh_config->ident == MAC_MODEL_IIFX)
|
|
write_ctrl_reg(hostdata, CTRL_INTERRUPTS_ENABLE);
|
|
return result;
|
|
}
|
|
|
|
static inline int macscsi_pwrite(struct NCR5380_hostdata *hostdata,
|
|
unsigned char *src, int len)
|
|
{
|
|
unsigned char *s = src;
|
|
u8 __iomem *d = hostdata->pdma_io + (OUTPUT_DATA_REG << 4);
|
|
int result = 0;
|
|
|
|
hostdata->pdma_residual = len;
|
|
|
|
while (!NCR5380_poll_politely(hostdata, BUS_AND_STATUS_REG,
|
|
BASR_DRQ | BASR_PHASE_MATCH,
|
|
BASR_DRQ | BASR_PHASE_MATCH, HZ / 64)) {
|
|
int bytes;
|
|
|
|
if (macintosh_config->ident == MAC_MODEL_IIFX)
|
|
write_ctrl_reg(hostdata, CTRL_HANDSHAKE_MODE |
|
|
CTRL_INTERRUPTS_ENABLE);
|
|
|
|
bytes = mac_pdma_send(s, d, min(hostdata->pdma_residual, 512));
|
|
|
|
if (bytes > 0) {
|
|
s += bytes;
|
|
hostdata->pdma_residual -= bytes;
|
|
}
|
|
|
|
if (hostdata->pdma_residual == 0) {
|
|
if (NCR5380_poll_politely(hostdata, TARGET_COMMAND_REG,
|
|
TCR_LAST_BYTE_SENT,
|
|
TCR_LAST_BYTE_SENT,
|
|
HZ / 64) < 0) {
|
|
scmd_printk(KERN_ERR, hostdata->connected,
|
|
"%s: Last Byte Sent timeout\n", __func__);
|
|
result = -1;
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
if (NCR5380_poll_politely2(hostdata, STATUS_REG, SR_REQ, SR_REQ,
|
|
BUS_AND_STATUS_REG, BASR_ACK,
|
|
BASR_ACK, HZ / 64) < 0)
|
|
scmd_printk(KERN_DEBUG, hostdata->connected,
|
|
"%s: !REQ and !ACK\n", __func__);
|
|
if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH))
|
|
goto out;
|
|
|
|
if (bytes == 0)
|
|
udelay(MAC_PDMA_DELAY);
|
|
|
|
if (bytes >= 0)
|
|
continue;
|
|
|
|
dsprintk(NDEBUG_PSEUDO_DMA, hostdata->host,
|
|
"%s: bus error (%d/%d)\n", __func__, s - src, len);
|
|
NCR5380_dprint(NDEBUG_PSEUDO_DMA, hostdata->host);
|
|
result = -1;
|
|
goto out;
|
|
}
|
|
|
|
scmd_printk(KERN_ERR, hostdata->connected,
|
|
"%s: phase mismatch or !DRQ\n", __func__);
|
|
NCR5380_dprint(NDEBUG_PSEUDO_DMA, hostdata->host);
|
|
result = -1;
|
|
out:
|
|
if (macintosh_config->ident == MAC_MODEL_IIFX)
|
|
write_ctrl_reg(hostdata, CTRL_INTERRUPTS_ENABLE);
|
|
return result;
|
|
}
|
|
|
|
static int macscsi_dma_xfer_len(struct NCR5380_hostdata *hostdata,
|
|
struct scsi_cmnd *cmd)
|
|
{
|
|
if (hostdata->flags & FLAG_NO_PSEUDO_DMA ||
|
|
cmd->SCp.this_residual < setup_use_pdma)
|
|
return 0;
|
|
|
|
return cmd->SCp.this_residual;
|
|
}
|
|
|
|
static int macscsi_dma_residual(struct NCR5380_hostdata *hostdata)
|
|
{
|
|
return hostdata->pdma_residual;
|
|
}
|
|
|
|
#include "NCR5380.c"
|
|
|
|
#define DRV_MODULE_NAME "mac_scsi"
|
|
#define PFX DRV_MODULE_NAME ": "
|
|
|
|
static struct scsi_host_template mac_scsi_template = {
|
|
.module = THIS_MODULE,
|
|
.proc_name = DRV_MODULE_NAME,
|
|
.name = "Macintosh NCR5380 SCSI",
|
|
.info = macscsi_info,
|
|
.queuecommand = macscsi_queue_command,
|
|
.eh_abort_handler = macscsi_abort,
|
|
.eh_host_reset_handler = macscsi_host_reset,
|
|
.can_queue = 16,
|
|
.this_id = 7,
|
|
.sg_tablesize = 1,
|
|
.cmd_per_lun = 2,
|
|
.dma_boundary = PAGE_SIZE - 1,
|
|
.cmd_size = NCR5380_CMD_SIZE,
|
|
.max_sectors = 128,
|
|
};
|
|
|
|
static int __init mac_scsi_probe(struct platform_device *pdev)
|
|
{
|
|
struct Scsi_Host *instance;
|
|
struct NCR5380_hostdata *hostdata;
|
|
int error;
|
|
int host_flags = 0;
|
|
struct resource *irq, *pio_mem, *pdma_mem = NULL;
|
|
|
|
pio_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!pio_mem)
|
|
return -ENODEV;
|
|
|
|
pdma_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
|
|
|
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
|
|
|
if (!hwreg_present((unsigned char *)pio_mem->start +
|
|
(STATUS_REG << 4))) {
|
|
pr_info(PFX "no device detected at %pap\n", &pio_mem->start);
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (setup_can_queue > 0)
|
|
mac_scsi_template.can_queue = setup_can_queue;
|
|
if (setup_cmd_per_lun > 0)
|
|
mac_scsi_template.cmd_per_lun = setup_cmd_per_lun;
|
|
if (setup_sg_tablesize > 0)
|
|
mac_scsi_template.sg_tablesize = setup_sg_tablesize;
|
|
if (setup_hostid >= 0)
|
|
mac_scsi_template.this_id = setup_hostid & 7;
|
|
|
|
instance = scsi_host_alloc(&mac_scsi_template,
|
|
sizeof(struct NCR5380_hostdata));
|
|
if (!instance)
|
|
return -ENOMEM;
|
|
|
|
if (irq)
|
|
instance->irq = irq->start;
|
|
else
|
|
instance->irq = NO_IRQ;
|
|
|
|
hostdata = shost_priv(instance);
|
|
hostdata->base = pio_mem->start;
|
|
hostdata->io = (u8 __iomem *)pio_mem->start;
|
|
|
|
if (pdma_mem && setup_use_pdma)
|
|
hostdata->pdma_io = (u8 __iomem *)pdma_mem->start;
|
|
else
|
|
host_flags |= FLAG_NO_PSEUDO_DMA;
|
|
|
|
host_flags |= setup_toshiba_delay > 0 ? FLAG_TOSHIBA_DELAY : 0;
|
|
|
|
error = NCR5380_init(instance, host_flags | FLAG_LATE_DMA_SETUP);
|
|
if (error)
|
|
goto fail_init;
|
|
|
|
if (instance->irq != NO_IRQ) {
|
|
error = request_irq(instance->irq, macscsi_intr, IRQF_SHARED,
|
|
"NCR5380", instance);
|
|
if (error)
|
|
goto fail_irq;
|
|
}
|
|
|
|
NCR5380_maybe_reset_bus(instance);
|
|
|
|
error = scsi_add_host(instance, NULL);
|
|
if (error)
|
|
goto fail_host;
|
|
|
|
platform_set_drvdata(pdev, instance);
|
|
|
|
scsi_scan_host(instance);
|
|
return 0;
|
|
|
|
fail_host:
|
|
if (instance->irq != NO_IRQ)
|
|
free_irq(instance->irq, instance);
|
|
fail_irq:
|
|
NCR5380_exit(instance);
|
|
fail_init:
|
|
scsi_host_put(instance);
|
|
return error;
|
|
}
|
|
|
|
static int __exit mac_scsi_remove(struct platform_device *pdev)
|
|
{
|
|
struct Scsi_Host *instance = platform_get_drvdata(pdev);
|
|
|
|
scsi_remove_host(instance);
|
|
if (instance->irq != NO_IRQ)
|
|
free_irq(instance->irq, instance);
|
|
NCR5380_exit(instance);
|
|
scsi_host_put(instance);
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver mac_scsi_driver = {
|
|
.remove = __exit_p(mac_scsi_remove),
|
|
.driver = {
|
|
.name = DRV_MODULE_NAME,
|
|
},
|
|
};
|
|
|
|
module_platform_driver_probe(mac_scsi_driver, mac_scsi_probe);
|
|
|
|
MODULE_ALIAS("platform:" DRV_MODULE_NAME);
|
|
MODULE_LICENSE("GPL");
|