2983 lines
79 KiB
C
2983 lines
79 KiB
C
|
/* $OpenBSD: arc.c,v 1.124 2022/09/06 15:22:11 kn Exp $ */
|
||
|
|
||
|
/*
|
||
|
* Copyright (c) 2006 David Gwynne <dlg@openbsd.org>
|
||
|
*
|
||
|
* Permission to use, copy, modify, and distribute this software for any
|
||
|
* purpose with or without fee is hereby granted, provided that the above
|
||
|
* copyright notice and this permission notice appear in all copies.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Ching Huang Support ARC1880,1882,1213,1223,1214
|
||
|
*/
|
||
|
#include "bio.h"
|
||
|
|
||
|
#include <sys/param.h>
|
||
|
#include <sys/systm.h>
|
||
|
#include <sys/buf.h>
|
||
|
#include <sys/kernel.h>
|
||
|
#include <sys/malloc.h>
|
||
|
#include <sys/mutex.h>
|
||
|
#include <sys/device.h>
|
||
|
#include <sys/rwlock.h>
|
||
|
#include <sys/task.h>
|
||
|
|
||
|
#include <machine/bus.h>
|
||
|
|
||
|
#include <dev/pci/pcireg.h>
|
||
|
#include <dev/pci/pcivar.h>
|
||
|
#include <dev/pci/pcidevs.h>
|
||
|
|
||
|
#include <scsi/scsi_all.h>
|
||
|
#include <scsi/scsiconf.h>
|
||
|
|
||
|
#include <sys/sensors.h>
|
||
|
#if NBIO > 0
|
||
|
#include <sys/ioctl.h>
|
||
|
#include <dev/biovar.h>
|
||
|
#endif
|
||
|
|
||
|
#ifdef ARC_DEBUG
|
||
|
#define ARC_D_INIT (1<<0)
|
||
|
#define ARC_D_RW (1<<1)
|
||
|
#define ARC_D_DB (1<<2)
|
||
|
|
||
|
int arcdebug = 0;
|
||
|
|
||
|
#define DPRINTF(p...) do { if (arcdebug) printf(p); } while (0)
|
||
|
#define DNPRINTF(n, p...) do { if ((n) & arcdebug) printf(p); } while (0)
|
||
|
|
||
|
#else
|
||
|
#define DPRINTF(p...) /* p */
|
||
|
#define DNPRINTF(n, p...) /* n, p */
|
||
|
#endif
|
||
|
|
||
|
#define ARC_HBA_TYPE_A 0x00000001
|
||
|
#define ARC_HBA_TYPE_B 0x00000002
|
||
|
#define ARC_HBA_TYPE_C 0x00000003
|
||
|
#define ARC_HBA_TYPE_D 0x00000004
|
||
|
|
||
|
#define ARC_RA_PCI_BAR PCI_MAPREG_START
|
||
|
#define ARC_RB_DOORBELL_BAR PCI_MAPREG_START
|
||
|
#define ARC_RB_RWBUFFER_BAR PCI_MAPREG_PPB_END
|
||
|
#define ARC_RC_PCI_BAR PCI_MAPREG_PCB_END
|
||
|
#define ARC_RD_PCI_BAR PCI_MAPREG_START
|
||
|
|
||
|
#define ARCMSR_MAX_CCB_COUNT 264
|
||
|
#define ARCMSR_MAX_HBB_POSTQUEUE 264
|
||
|
#define ARCMSR_MAX_HBD_POSTQUEUE 256
|
||
|
|
||
|
/* Areca boards using the Intel IOP are Type A (RA) */
|
||
|
|
||
|
#define ARC_RA_INB_MSG0 0x0010
|
||
|
#define ARC_RA_INB_MSG0_NOP (0x00000000)
|
||
|
#define ARC_RA_INB_MSG0_GET_CONFIG (0x00000001)
|
||
|
#define ARC_RA_INB_MSG0_SET_CONFIG (0x00000002)
|
||
|
#define ARC_RA_INB_MSG0_ABORT_CMD (0x00000003)
|
||
|
#define ARC_RA_INB_MSG0_STOP_BGRB (0x00000004)
|
||
|
#define ARC_RA_INB_MSG0_FLUSH_CACHE (0x00000005)
|
||
|
#define ARC_RA_INB_MSG0_START_BGRB (0x00000006)
|
||
|
#define ARC_RA_INB_MSG0_CHK331PENDING (0x00000007)
|
||
|
#define ARC_RA_INB_MSG0_SYNC_TIMER (0x00000008)
|
||
|
#define ARC_RA_INB_MSG1 0x0014
|
||
|
#define ARC_RA_OUTB_ADDR0 0x0018
|
||
|
#define ARC_RA_OUTB_ADDR1 0x001c
|
||
|
#define ARC_RA_OUTB_ADDR1_FIRMWARE_OK (1<<31)
|
||
|
#define ARC_RA_INB_DOORBELL 0x0020
|
||
|
#define ARC_RA_INB_DOORBELL_WRITE_OK (1<<0)
|
||
|
#define ARC_RA_INB_DOORBELL_READ_OK (1<<1)
|
||
|
#define ARC_RA_OUTB_DOORBELL 0x002c
|
||
|
#define ARC_RA_OUTB_DOORBELL_WRITE_OK (1<<0)
|
||
|
#define ARC_RA_OUTB_DOORBELL_READ_OK (1<<1)
|
||
|
#define ARC_RA_INTRSTAT 0x0030
|
||
|
#define ARC_RA_INTRSTAT_MSG0 (1<<0)
|
||
|
#define ARC_RA_INTRSTAT_MSG1 (1<<1)
|
||
|
#define ARC_RA_INTRSTAT_DOORBELL (1<<2)
|
||
|
#define ARC_RA_INTRSTAT_POSTQUEUE (1<<3)
|
||
|
#define ARC_RA_INTRSTAT_PCI (1<<4)
|
||
|
#define ARC_RA_INTR_STAT_ALL 0x1F
|
||
|
#define ARC_RA_INTRMASK 0x0034
|
||
|
#define ARC_RA_INTRMASK_MSG0 (1<<0)
|
||
|
#define ARC_RA_INTRMASK_MSG1 (1<<1)
|
||
|
#define ARC_RA_INTRMASK_DOORBELL (1<<2)
|
||
|
#define ARC_RA_INTRMASK_POSTQUEUE (1<<3)
|
||
|
#define ARC_RA_INTRMASK_PCI (1<<4)
|
||
|
#define ARC_RA_INTR_MASK_ALL 0x1F
|
||
|
#define ARC_RA_POST_QUEUE 0x0040
|
||
|
#define ARC_RA_POST_QUEUE_ADDR_SHIFT 5
|
||
|
#define ARC_RA_POST_QUEUE_IAMBIOS (1<<30)
|
||
|
#define ARC_RA_POST_QUEUE_BIGFRAME (1<<31)
|
||
|
#define ARC_RA_REPLY_QUEUE 0x0044
|
||
|
#define ARC_RA_REPLY_QUEUE_ADDR_SHIFT 5
|
||
|
#define ARC_RA_REPLY_QUEUE_ERR (1<<28)
|
||
|
#define ARC_RA_REPLY_QUEUE_IAMBIOS (1<<30)
|
||
|
#define ARC_RA_MSGBUF 0x0a00
|
||
|
#define ARC_RA_MSGBUF_LEN 1024
|
||
|
#define ARC_RA_IOC_WBUF_LEN 0x0e00
|
||
|
#define ARC_RA_IOC_WBUF 0x0e04
|
||
|
#define ARC_RA_IOC_RBUF_LEN 0x0f00
|
||
|
#define ARC_RA_IOC_RBUF 0x0f04
|
||
|
#define ARC_RA_IOC_RWBUF_MAXLEN 124 /* for both RBUF and WBUF */
|
||
|
|
||
|
/* Areca boards using the Marvel IOP0 are Type B (RB) */
|
||
|
|
||
|
#define ARC_RB_DRV2IOP_DOORBELL 0x00020400
|
||
|
#define ARC_RB_DRV2IOP_DOORBELL_MASK 0x00020404
|
||
|
#define ARC_RB_IOP2DRV_DOORBELL 0x00020408
|
||
|
#define ARC_RB_IOP2DRV_DOORBELL_FIRMWARE_OK (1<<31)
|
||
|
#define ARC_RB_IOP2DRV_DOORBELL_MASK 0x0002040c
|
||
|
|
||
|
/* Areca boards using the LSI IOP are Type C (RC) */
|
||
|
|
||
|
#define ARC_RC_INB_DOORBELL 0x20
|
||
|
#define ARC_RC_INTR_STAT 0x30
|
||
|
#define ARC_RC_INTR_MASK 0x34
|
||
|
#define ARC_RC_OUTB_DOORBELL 0x9C
|
||
|
#define ARC_RC_OUTB_DOORBELL_CLR 0xA0
|
||
|
#define ARC_RC_D2I_MSG_CMD_DONE 0x08
|
||
|
#define ARC_RC_I2D_MSG_CMD_DONE 0x08
|
||
|
#define ARC_RC_I2D_MSG_CMD_DONE_CLR 0x08
|
||
|
#define ARC_RC_INB_MSGADDR0 0xB0
|
||
|
#define ARC_RC_INB_MSGADDR1 0xB4
|
||
|
#define ARC_RC_OUTB_MSGADDR0 0xB8
|
||
|
#define ARC_RC_OUTB_MSGADDR1 0xBC
|
||
|
#define ARC_RC_OUTB_MSG_FIRMWARE_OK 0x80000000
|
||
|
#define ARC_RC_INB_POSTQ_LOW 0xC0
|
||
|
#define ARC_RC_INB_POSTQ_HIGH 0xC4
|
||
|
#define ARC_RC_OUTB_REPLYQ_LOW 0xC8
|
||
|
#define ARC_RC_OUTB_REPLYQ_HIGH 0xCC
|
||
|
#define ARC_RC_MSG_WBUF_LEN 0x2000
|
||
|
#define ARC_RC_MSG_WBUF 0x2004
|
||
|
#define ARC_RC_MSG_RBUF_LEN 0x2100
|
||
|
#define ARC_RC_MSG_RBUF 0x2104
|
||
|
#define ARC_RC_MSG_RWBUF 0x2200
|
||
|
|
||
|
#define ARC_RC_INB_MSG0_NOP (0x00000000)
|
||
|
#define ARC_RC_INB_MSG0_GET_CONFIG (0x00000001)
|
||
|
#define ARC_RC_INB_MSG0_SET_CONFIG (0x00000002)
|
||
|
#define ARC_RC_INB_MSG0_ABORT_CMD (0x00000003)
|
||
|
#define ARC_RC_INB_MSG0_STOP_BGRB (0x00000004)
|
||
|
#define ARC_RC_INB_MSG0_FLUSH_CACHE (0x00000005)
|
||
|
#define ARC_RC_INB_MSG0_START_BGRB (0x00000006)
|
||
|
#define ARC_RC_INB_MSG0_CHK331PENDING (0x00000007)
|
||
|
#define ARC_RC_INB_MSG0_SYNC_TIMER (0x00000008)
|
||
|
|
||
|
#define ARC_RC_D2I_DATA_WRITE_OK 0x00000002
|
||
|
#define ARC_RC_D2I_DATA_READ_OK 0x00000004
|
||
|
#define ARC_RC_D2I_MESSAGE_CMD_DONE 0x00000008
|
||
|
#define ARC_RC_D2I_POSTQUEUE_THROTTLING 0x00000010
|
||
|
#define ARC_RC_I2D_DATA_WRITE_OK 0x00000002
|
||
|
#define ARC_RC_I2D_DATA_WRITE_OK_CLEAR 0x00000002
|
||
|
#define ARC_RC_I2D_DATA_READ_OK 0x00000004
|
||
|
#define ARC_RC_I2D_DATA_READ_OK_CLEAR 0x00000004
|
||
|
#define ARC_RC_I2D_MESSAGE_CMD_DONE 0x00000008
|
||
|
#define ARC_RC_I2D_MESSAGE_CMD_DONE_CLEAR 0x00000008
|
||
|
#define ARC_RC_MESSAGE_FIRMWARE_OK 0x80000000
|
||
|
|
||
|
#define ARC_RC_INTR_STAT_UTILITY_A (1<<0)
|
||
|
#define ARC_RC_INTR_STAT_DOORBELL (1<<2)
|
||
|
#define ARC_RC_INTR_STAT_POSTQUEUE (1<<3)
|
||
|
#define ARC_RC_INTR_MASK_ALL 0x0000000D
|
||
|
#define ARC_RC_INTR_MASK_UTILITY_A (1<<0)
|
||
|
#define ARC_RC_INTR_MASK_DOORBELL (1<<2)
|
||
|
#define ARC_RC_INTR_MASK_POSTQUEUE (1<<3)
|
||
|
#define ARC_RC_REPLY_QUEUE_ERR 1
|
||
|
#define ARC_RC_THROTTLE 12
|
||
|
|
||
|
/* Areca boards using the Marvell IOP 9580 are Type D (RD) */
|
||
|
|
||
|
#define ARC_RD_INTR_STAT 0x200
|
||
|
#define ARC_RD_HOST_INT_ENABLE 0x204
|
||
|
#define ARC_RD_INTR_ENABLE 0x20C
|
||
|
#define ARC_RD_D2I_MSG_CMD_DONE 0x08
|
||
|
#define ARC_RD_I2D_MSG_CMD_DONE 0x2000000
|
||
|
#define ARC_RD_I2D_MSG_CMD_DONE_CLR 0x2000000
|
||
|
#define ARC_RD_INB_MSGADDR0 0x400
|
||
|
#define ARC_RD_INB_MSGADDR1 0x404
|
||
|
#define ARC_RD_OUTB_MSGADDR0 0x420
|
||
|
#define ARC_RD_OUTB_MSGADDR1 0x424
|
||
|
#define ARC_RD_INB_DOORBELL 0x460
|
||
|
#define ARC_RD_OUTB_DOORBELL 0x480
|
||
|
#define ARC_RD_OUTB_DOORBELL_CLR 0x480
|
||
|
#define ARC_RD_OUTB_DOORBELL_ENABLE 0x484
|
||
|
#define ARC_RD_OUTB_MSG_FIRMWARE_OK 0x80000000
|
||
|
#define ARC_RD_INB_POSTQ_LOW 0x1000
|
||
|
#define ARC_RD_INB_POSTQ_HIGH 0x1004
|
||
|
#define ARC_RD_OUTB_REPLYQ_LOW 0x1060
|
||
|
#define ARC_RD_OUTB_REPLYQ_HIGH 0x1064
|
||
|
|
||
|
#define ARC_RD_INB_WRITE_PTR 0x1018
|
||
|
#define ARC_RD_INB_READ_PTR 0x101C
|
||
|
#define ARC_RD_OUTB_COPY_PTR 0x106C
|
||
|
#define ARC_RD_OUTB_READ_PTR 0x1070
|
||
|
#define ARC_RD_OUTB_INTR_CAUSE 0x1088
|
||
|
#define ARC_RD_OUTB_INT_ENABLE 0x108C
|
||
|
#define ARC_RD_MSG_WBUF_LEN 0x2000
|
||
|
#define ARC_RD_MSG_WBUF 0x2004
|
||
|
#define ARC_RD_MSG_RBUF_LEN 0x2100
|
||
|
#define ARC_RD_MSG_RBUF 0x2104
|
||
|
#define ARC_RD_MSG_RWBUF 0x2200
|
||
|
|
||
|
#define ARC_RD_INB_MSG0_NOP (0x00000000)
|
||
|
#define ARC_RD_INB_MSG0_GET_CONFIG (0x00000001)
|
||
|
#define ARC_RD_INB_MSG0_SET_CONFIG (0x00000002)
|
||
|
#define ARC_RD_INB_MSG0_ABORT_CMD (0x00000003)
|
||
|
#define ARC_RD_INB_MSG0_STOP_BGRB (0x00000004)
|
||
|
#define ARC_RD_INB_MSG0_FLUSH_CACHE (0x00000005)
|
||
|
#define ARC_RD_INB_MSG0_START_BGRB (0x00000006)
|
||
|
#define ARC_RD_INB_MSG0_CHK331PENDING (0x00000007)
|
||
|
#define ARC_RD_INB_MSG0_SYNC_TIMER (0x00000008)
|
||
|
|
||
|
#define ARC_RD_D2I_DATA_WRITE_OK 0x00000001
|
||
|
#define ARC_RD_D2I_DATA_READ_OK 0x00000002
|
||
|
#define ARC_RD_D2I_MESSAGE_CMD_DONE 0x02000000
|
||
|
#define ARC_RD_D2I_POSTQUEUE_THROTTLING 0x00000010
|
||
|
#define ARC_RD_I2D_DATA_WRITE_OK 0x00000001
|
||
|
#define ARC_RD_I2D_DATA_WRITE_CLEAR 0x00000001
|
||
|
#define ARC_RD_I2D_DATA_READ_OK 0x00000002
|
||
|
#define ARC_RD_I2D_DATA_READ_CLEAR 0x00000002
|
||
|
#define ARC_RD_I2D_MESSAGE_CMD_DONE 0x02000000
|
||
|
#define ARC_RD_I2D_MESSAGE_CMD_DONE_CLEAR 0x02000000
|
||
|
#define ARC_RD_MESSAGE_FIRMWARE_OK 0x80000000
|
||
|
|
||
|
#define ARC_RD_INTR_STAT_DOORBELL 0x00001000
|
||
|
#define ARC_RD_INTR_STAT_POSTQUEUE 0x00000010
|
||
|
#define ARC_RD_INTR_ENABLE_ALL 0x00001010
|
||
|
#define ARC_RD_INTR_DISABLE_ALL 0x00000000
|
||
|
#define ARC_RD_INTR_ENABLE_DOORBELL 0x00001000
|
||
|
#define ARC_RD_INTR_ENABLE_POSTQUEUE 0x00000010
|
||
|
#define ARC_RD_REPLY_QUEUE_ERR 1
|
||
|
#define ARC_RD_OUTB_LIST_INT_CLR 1
|
||
|
|
||
|
struct arc_msg_firmware_info {
|
||
|
u_int32_t signature;
|
||
|
#define ARC_FWINFO_SIGNATURE_GET_CONFIG (0x87974060)
|
||
|
#define ARC_FWINFO_SIGNATURE_SET_CONFIG (0x87974063)
|
||
|
u_int32_t request_len;
|
||
|
u_int32_t queue_len;
|
||
|
u_int32_t sdram_size;
|
||
|
u_int32_t sata_ports;
|
||
|
u_int8_t vendor[40];
|
||
|
u_int8_t model[8];
|
||
|
u_int8_t fw_version[16];
|
||
|
u_int8_t device_map[16];
|
||
|
u_int32_t cfgVersion;
|
||
|
u_int8_t cfgSerial[16];
|
||
|
u_int32_t cfgPicStatus;
|
||
|
} __packed;
|
||
|
|
||
|
/* definitions of the firmware commands sent via the doorbells */
|
||
|
|
||
|
struct arc_fw_hdr {
|
||
|
u_int8_t byte1;
|
||
|
u_int8_t byte2;
|
||
|
u_int8_t byte3;
|
||
|
} __packed;
|
||
|
|
||
|
/* the fw header must always equal this */
|
||
|
struct arc_fw_hdr arc_fw_hdr = { 0x5e, 0x01, 0x61 };
|
||
|
|
||
|
struct arc_fw_bufhdr {
|
||
|
struct arc_fw_hdr hdr;
|
||
|
u_int16_t len;
|
||
|
} __packed;
|
||
|
|
||
|
#define ARC_FW_RAIDINFO 0x20 /* opcode + raid# */
|
||
|
#define ARC_FW_VOLINFO 0x21 /* opcode + vol# */
|
||
|
#define ARC_FW_DISKINFO 0x22 /* opcode + physdisk# */
|
||
|
#define ARC_FW_SYSINFO 0x23 /* opcode. reply is fw_sysinfo */
|
||
|
#define ARC_FW_MUTE_ALARM 0x30 /* opcode only */
|
||
|
#define ARC_FW_SET_ALARM 0x31 /* opcode + 1 byte for setting */
|
||
|
#define ARC_FW_SET_ALARM_DISABLE 0x00
|
||
|
#define ARC_FW_SET_ALARM_ENABLE 0x01
|
||
|
#define ARC_FW_NOP 0x38 /* opcode only */
|
||
|
|
||
|
#define ARC_FW_CMD_OK 0x41
|
||
|
#define ARC_FW_BLINK 0x43
|
||
|
#define ARC_FW_BLINK_ENABLE 0x00
|
||
|
#define ARC_FW_BLINK_DISABLE 0x01
|
||
|
#define ARC_FW_CMD_PASS_REQD 0x4d
|
||
|
|
||
|
struct arc_fw_comminfo {
|
||
|
u_int8_t baud_rate;
|
||
|
u_int8_t data_bits;
|
||
|
u_int8_t stop_bits;
|
||
|
u_int8_t parity;
|
||
|
u_int8_t flow_control;
|
||
|
} __packed;
|
||
|
|
||
|
struct arc_fw_scsiattr {
|
||
|
u_int8_t channel; /* channel for SCSI target (0/1) */
|
||
|
u_int8_t target;
|
||
|
u_int8_t lun;
|
||
|
u_int8_t tagged;
|
||
|
u_int8_t cache;
|
||
|
u_int8_t speed;
|
||
|
} __packed;
|
||
|
|
||
|
struct arc_fw_raidinfo {
|
||
|
u_int8_t set_name[16];
|
||
|
u_int32_t capacity;
|
||
|
u_int32_t capacity2;
|
||
|
u_int32_t fail_mask;
|
||
|
u_int8_t device_array[32];
|
||
|
u_int8_t member_devices;
|
||
|
u_int8_t new_member_devices;
|
||
|
u_int8_t raid_state;
|
||
|
u_int8_t volumes;
|
||
|
u_int8_t volume_list[16];
|
||
|
u_int8_t reserved1[3];
|
||
|
u_int8_t free_segments;
|
||
|
u_int32_t raw_stripes[8];
|
||
|
u_int32_t reserved2[3];
|
||
|
u_int8_t vol_ListX[112];
|
||
|
u_int8_t devEncArray[32];
|
||
|
} __packed;
|
||
|
|
||
|
struct arc_fw_volinfo {
|
||
|
u_int8_t set_name[16];
|
||
|
u_int32_t capacity;
|
||
|
u_int32_t capacity2;
|
||
|
u_int32_t fail_mask;
|
||
|
u_int32_t stripe_size; /* in blocks */
|
||
|
u_int32_t new_fail_mask;
|
||
|
u_int32_t new_stripe_size;
|
||
|
u_int32_t volume_status;
|
||
|
#define ARC_FW_VOL_STATUS_NORMAL 0x00
|
||
|
#define ARC_FW_VOL_STATUS_INITTING (1<<0)
|
||
|
#define ARC_FW_VOL_STATUS_FAILED (1<<1)
|
||
|
#define ARC_FW_VOL_STATUS_MIGRATING (1<<2)
|
||
|
#define ARC_FW_VOL_STATUS_REBUILDING (1<<3)
|
||
|
#define ARC_FW_VOL_STATUS_NEED_INIT (1<<4)
|
||
|
#define ARC_FW_VOL_STATUS_NEED_MIGRATE (1<<5)
|
||
|
#define ARC_FW_VOL_STATUS_INIT_FLAG (1<<6)
|
||
|
#define ARC_FW_VOL_STATUS_NEED_REGEN (1<<7)
|
||
|
#define ARC_FW_VOL_STATUS_CHECKING (1<<8)
|
||
|
#define ARC_FW_VOL_STATUS_NEED_CHECK (1<<9)
|
||
|
u_int32_t progress;
|
||
|
struct arc_fw_scsiattr scsi_attr;
|
||
|
u_int8_t member_disks;
|
||
|
u_int8_t raid_level;
|
||
|
#define ARC_FW_VOL_RAIDLEVEL_0 0x00
|
||
|
#define ARC_FW_VOL_RAIDLEVEL_1 0x01
|
||
|
#define ARC_FW_VOL_RAIDLEVEL_3 0x02
|
||
|
#define ARC_FW_VOL_RAIDLEVEL_5 0x03
|
||
|
#define ARC_FW_VOL_RAIDLEVEL_6 0x04
|
||
|
#define ARC_FW_VOL_RAIDLEVEL_PASSTHRU 0x05
|
||
|
u_int8_t new_member_disks;
|
||
|
u_int8_t new_raid_level;
|
||
|
u_int8_t raid_set_number;
|
||
|
u_int8_t vol_state0;
|
||
|
u_int32_t host_speed;
|
||
|
u_int32_t vol_state;
|
||
|
u_int8_t vol_array[16];
|
||
|
u_int8_t num_5060volumes;
|
||
|
u_int8_t reserved[43];
|
||
|
} __packed;
|
||
|
|
||
|
struct arc_fw_diskinfo {
|
||
|
u_int8_t model[40];
|
||
|
u_int8_t serial[20];
|
||
|
u_int8_t firmware_rev[8];
|
||
|
u_int32_t capacity;
|
||
|
u_int32_t capacity2;
|
||
|
u_int8_t device_state;
|
||
|
u_int8_t pio_mode;
|
||
|
u_int8_t current_udma_mode;
|
||
|
u_int8_t udma_mode;
|
||
|
u_int8_t hot_spare_type;
|
||
|
u_int8_t raid_number; /* 0xff unowned */
|
||
|
struct arc_fw_scsiattr scsi_attr;
|
||
|
u_int8_t reserved[170];
|
||
|
} __packed;
|
||
|
|
||
|
struct arc_fw_sysinfo {
|
||
|
u_int8_t vendor_name[40];
|
||
|
u_int8_t serial_number[16];
|
||
|
u_int8_t firmware_version[16];
|
||
|
u_int8_t boot_version[16];
|
||
|
u_int8_t mb_version[16];
|
||
|
u_int8_t model_name[8];
|
||
|
|
||
|
u_int8_t local_ip[4];
|
||
|
u_int8_t current_ip[4];
|
||
|
|
||
|
u_int32_t time_tick;
|
||
|
u_int32_t cpu_speed;
|
||
|
u_int32_t icache;
|
||
|
u_int32_t dcache;
|
||
|
u_int32_t scache;
|
||
|
u_int32_t memory_size;
|
||
|
u_int32_t memory_speed;
|
||
|
u_int32_t events;
|
||
|
|
||
|
u_int8_t gsiMacAddress[6];
|
||
|
u_int8_t gsiDhcp;
|
||
|
u_int8_t alarm;
|
||
|
u_int8_t channel_usage;
|
||
|
u_int8_t max_ata_mode;
|
||
|
u_int8_t sdram_ecc;
|
||
|
u_int8_t rebuild_priority;
|
||
|
struct arc_fw_comminfo comm_a;
|
||
|
struct arc_fw_comminfo comm_b;
|
||
|
u_int8_t ide_channels;
|
||
|
u_int8_t scsi_host_channels;
|
||
|
u_int8_t ide_host_channels;
|
||
|
u_int8_t max_volume_set;
|
||
|
u_int8_t max_raid_set;
|
||
|
u_int8_t ether_port;
|
||
|
u_int8_t raid6_engine;
|
||
|
u_int8_t reserved[75];
|
||
|
} __packed;
|
||
|
|
||
|
struct arc_iop;
|
||
|
struct arc_ccb;
|
||
|
SLIST_HEAD(arc_ccb_list, arc_ccb);
|
||
|
|
||
|
struct InBound_SRB {
|
||
|
u_int32_t addressLow; /* pointer to SRB block */
|
||
|
u_int32_t addressHigh;
|
||
|
u_int32_t length; /* in DWORDs */
|
||
|
u_int32_t reserved0;
|
||
|
};
|
||
|
|
||
|
struct OutBound_SRB {
|
||
|
u_int32_t addressLow; /* pointer to SRB block */
|
||
|
u_int32_t addressHigh;
|
||
|
};
|
||
|
|
||
|
struct arc_HBD_Msgu {
|
||
|
struct InBound_SRB post_qbuffer[ARCMSR_MAX_HBD_POSTQUEUE];
|
||
|
struct OutBound_SRB done_qbuffer[ARCMSR_MAX_HBD_POSTQUEUE+1];
|
||
|
u_int16_t postq_index;
|
||
|
u_int16_t doneq_index;
|
||
|
};
|
||
|
|
||
|
#define ARC_MAX_CMDQ_PTR_LEN sizeof(struct arc_HBD_Msgu)
|
||
|
|
||
|
struct arc_msg_scsicmd {
|
||
|
u_int8_t bus;
|
||
|
u_int8_t target;
|
||
|
u_int8_t lun;
|
||
|
u_int8_t function;
|
||
|
|
||
|
u_int8_t cdb_len;
|
||
|
u_int8_t sgl_len;
|
||
|
u_int8_t flags;
|
||
|
#define ARC_MSG_SCSICMD_FLAG_SGL_BSIZE_512 (1<<0)
|
||
|
#define ARC_MSG_SCSICMD_FLAG_FROM_BIOS (1<<1)
|
||
|
#define ARC_MSG_SCSICMD_FLAG_WRITE (1<<2)
|
||
|
#define ARC_MSG_SCSICMD_FLAG_SIMPLEQ (0x00)
|
||
|
#define ARC_MSG_SCSICMD_FLAG_HEADQ (0x08)
|
||
|
#define ARC_MSG_SCSICMD_FLAG_ORDERQ (0x10)
|
||
|
u_int8_t msgPages;
|
||
|
|
||
|
u_int32_t context;
|
||
|
u_int32_t data_len;
|
||
|
|
||
|
#define ARC_MSG_CDBLEN 16
|
||
|
u_int8_t cdb[ARC_MSG_CDBLEN];
|
||
|
|
||
|
u_int8_t status;
|
||
|
#define ARC_MSG_STATUS_SELTIMEOUT 0xf0
|
||
|
#define ARC_MSG_STATUS_ABORTED 0xf1
|
||
|
#define ARC_MSG_STATUS_INIT_FAIL 0xf2
|
||
|
#define ARC_MSG_SENSELEN 15
|
||
|
u_int8_t sense_data[ARC_MSG_SENSELEN];
|
||
|
|
||
|
/* followed by an sgl */
|
||
|
} __packed;
|
||
|
|
||
|
struct arc_sge {
|
||
|
u_int32_t sg_length;
|
||
|
#define ARC_SGE_64BIT (1<<24)
|
||
|
u_int32_t sg_lo_addr;
|
||
|
u_int32_t sg_hi_addr;
|
||
|
} __packed;
|
||
|
|
||
|
#define ARC_MAX_TARGET 16
|
||
|
#define ARC_MAX_LUN 8
|
||
|
#define ARC_MAX_IOCMDLEN 512
|
||
|
#define ARC_BLOCKSIZE 512
|
||
|
|
||
|
/* the firmware deals with up to 256 or 512 byte command frames. */
|
||
|
/* sizeof(struct arc_msg_scsicmd) + (sizeof(struct arc_sge) * 38) == 508 */
|
||
|
#define ARC_SGL_MAXLEN 38
|
||
|
/* sizeof(struct arc_msg_scsicmd) + (sizeof(struct arc_sge) * 17) == 252 */
|
||
|
#define ARC_SGL_256LEN 17
|
||
|
|
||
|
struct arc_io_cmd {
|
||
|
struct arc_msg_scsicmd cmd;
|
||
|
struct arc_sge sgl[ARC_SGL_MAXLEN];
|
||
|
u_int32_t reserved1;
|
||
|
struct arc_ccb *ccb;
|
||
|
u_int32_t reserved2[6];
|
||
|
} __packed;
|
||
|
|
||
|
#define ARC_IO_CMD_LEN 512+32
|
||
|
|
||
|
/* stuff to manage a scsi command */
|
||
|
struct arc_ccb {
|
||
|
struct arc_softc *ccb_sc;
|
||
|
|
||
|
struct scsi_xfer *ccb_xs;
|
||
|
|
||
|
bus_dmamap_t ccb_dmamap;
|
||
|
bus_addr_t cmd_dma_offset;
|
||
|
struct arc_io_cmd *ccb_cmd;
|
||
|
u_int32_t ccb_cmd_post;
|
||
|
|
||
|
SLIST_ENTRY(arc_ccb) ccb_link;
|
||
|
u_int32_t arc_io_cmd_length;
|
||
|
};
|
||
|
|
||
|
struct arc_softc {
|
||
|
struct device sc_dev;
|
||
|
const struct arc_iop *sc_iop;
|
||
|
|
||
|
pci_chipset_tag_t sc_pc;
|
||
|
pcitag_t sc_tag;
|
||
|
|
||
|
bus_space_tag_t sc_iot;
|
||
|
bus_space_handle_t sc_ioh;
|
||
|
bus_size_t sc_ios;
|
||
|
bus_dma_tag_t sc_dmat;
|
||
|
|
||
|
void *sc_ih;
|
||
|
|
||
|
u_int32_t sc_req_count;
|
||
|
|
||
|
struct arc_dmamem *sc_requests;
|
||
|
struct arc_ccb *sc_ccbs;
|
||
|
struct arc_ccb_list sc_ccb_free;
|
||
|
struct mutex sc_ccb_mtx;
|
||
|
|
||
|
struct scsi_iopool sc_iopool;
|
||
|
struct scsibus_softc *sc_scsibus;
|
||
|
|
||
|
struct rwlock sc_lock;
|
||
|
volatile int sc_talking;
|
||
|
|
||
|
struct ksensor *sc_sensors;
|
||
|
struct ksensordev sc_sensordev;
|
||
|
int sc_nsensors;
|
||
|
|
||
|
u_int32_t sc_ledmask;
|
||
|
u_int32_t sc_adp_type;
|
||
|
u_int32_t sc_ccb_phys_hi;
|
||
|
u_int32_t postQ_buffer;
|
||
|
u_int32_t doneQ_buffer;
|
||
|
bus_addr_t cmdQ_ptr_offset;
|
||
|
struct arc_HBD_Msgu *pmu;
|
||
|
};
|
||
|
#define DEVNAME(_s) ((_s)->sc_dev.dv_xname)
|
||
|
|
||
|
/* wrap up the bus_dma api */
|
||
|
struct arc_dmamem {
|
||
|
bus_dmamap_t adm_map;
|
||
|
bus_dma_segment_t adm_seg;
|
||
|
size_t adm_size;
|
||
|
caddr_t adm_kva;
|
||
|
};
|
||
|
#define ARC_DMA_MAP(_adm) ((_adm)->adm_map)
|
||
|
#define ARC_DMA_DVA(_adm) ((_adm)->adm_map->dm_segs[0].ds_addr)
|
||
|
#define ARC_DMA_KVA(_adm) ((void *)(_adm)->adm_kva)
|
||
|
|
||
|
int arc_match(struct device *, void *, void *);
|
||
|
void arc_attach(struct device *, struct device *, void *);
|
||
|
int arc_detach(struct device *, int);
|
||
|
int arc_activate(struct device *, int);
|
||
|
int arc_intr(void *);
|
||
|
int arc_intr_A(void *);
|
||
|
int arc_intr_C(void *);
|
||
|
int arc_intr_D(void *);
|
||
|
|
||
|
/* interface for scsi midlayer to talk to */
|
||
|
void arc_scsi_cmd(struct scsi_xfer *);
|
||
|
|
||
|
/* code to deal with getting bits in and out of the bus space */
|
||
|
u_int32_t arc_read(struct arc_softc *, bus_size_t);
|
||
|
void arc_read_region(struct arc_softc *, bus_size_t,
|
||
|
void *, size_t);
|
||
|
void arc_write(struct arc_softc *, bus_size_t, u_int32_t);
|
||
|
void arc_write_region(struct arc_softc *, bus_size_t,
|
||
|
void *, size_t);
|
||
|
int arc_wait_eq(struct arc_softc *, bus_size_t,
|
||
|
u_int32_t, u_int32_t);
|
||
|
int arc_wait_ne(struct arc_softc *, bus_size_t,
|
||
|
u_int32_t, u_int32_t);
|
||
|
int arc_msg0(struct arc_softc *, u_int32_t);
|
||
|
|
||
|
struct arc_dmamem *arc_dmamem_alloc(struct arc_softc *, size_t);
|
||
|
void arc_dmamem_free(struct arc_softc *,
|
||
|
struct arc_dmamem *);
|
||
|
void arc_free_ccb_src(struct arc_softc *sc);
|
||
|
|
||
|
int arc_alloc_ccbs(struct arc_softc *);
|
||
|
struct arc_ccb *arc_get_ccb(struct arc_softc *);
|
||
|
void arc_put_ccb(struct arc_softc *, struct arc_ccb *);
|
||
|
int arc_load_xs(struct arc_ccb *);
|
||
|
int arc_complete(struct arc_softc *, struct arc_ccb *,
|
||
|
int);
|
||
|
void arc_scsi_cmd_done(struct arc_softc *, struct arc_ccb *,
|
||
|
u_int32_t);
|
||
|
|
||
|
int arc_map_pci_resources(struct arc_softc *,
|
||
|
struct pci_attach_args *);
|
||
|
void arc_unmap_pci_resources(struct arc_softc *);
|
||
|
int arc_chipA_firmware(struct arc_softc *);
|
||
|
int arc_chipB_firmware(struct arc_softc *);
|
||
|
int arc_chipC_firmware(struct arc_softc *);
|
||
|
int arc_chipD_firmware(struct arc_softc *);
|
||
|
void arc_enable_all_intr(struct arc_softc *);
|
||
|
void arc_disable_all_intr(struct arc_softc *);
|
||
|
void arc_stop_bgrb_proc(struct arc_softc *sc);
|
||
|
void arc_flush_cache(struct arc_softc *sc);
|
||
|
void arc_iop_set_conf(struct arc_softc *sc);
|
||
|
|
||
|
#if NBIO > 0
|
||
|
/* stuff to do messaging via the doorbells */
|
||
|
void arc_lock(struct arc_softc *);
|
||
|
void arc_unlock(struct arc_softc *);
|
||
|
void arc_wait(struct arc_softc *);
|
||
|
u_int8_t arc_msg_cksum(void *, u_int16_t);
|
||
|
int arc_msgbuf(struct arc_softc *, void *, size_t,
|
||
|
void *, size_t, int);
|
||
|
|
||
|
/* bioctl */
|
||
|
int arc_bioctl(struct device *, u_long, caddr_t);
|
||
|
int arc_bio_inq(struct arc_softc *, struct bioc_inq *);
|
||
|
int arc_bio_vol(struct arc_softc *, struct bioc_vol *);
|
||
|
int arc_bio_disk(struct arc_softc *, struct bioc_disk *);
|
||
|
int arc_bio_alarm(struct arc_softc *, struct bioc_alarm *);
|
||
|
int arc_bio_alarm_state(struct arc_softc *,
|
||
|
struct bioc_alarm *);
|
||
|
int arc_bio_blink(struct arc_softc *, struct bioc_blink *);
|
||
|
|
||
|
int arc_bio_getvol(struct arc_softc *, int,
|
||
|
struct arc_fw_volinfo *);
|
||
|
|
||
|
#ifndef SMALL_KERNEL
|
||
|
struct arc_task {
|
||
|
struct task t;
|
||
|
struct arc_softc *sc;
|
||
|
};
|
||
|
/* sensors */
|
||
|
void arc_create_sensors(void *);
|
||
|
void arc_refresh_sensors(void *);
|
||
|
#endif /* SMALL_KERNEL */
|
||
|
#endif
|
||
|
|
||
|
const struct cfattach arc_ca = {
|
||
|
sizeof(struct arc_softc), arc_match, arc_attach, arc_detach,
|
||
|
arc_activate
|
||
|
};
|
||
|
|
||
|
struct cfdriver arc_cd = {
|
||
|
NULL, "arc", DV_DULL
|
||
|
};
|
||
|
|
||
|
const struct scsi_adapter arc_switch = {
|
||
|
arc_scsi_cmd, NULL, NULL, NULL, NULL
|
||
|
};
|
||
|
|
||
|
/* real stuff for dealing with the hardware */
|
||
|
struct arc_iop {
|
||
|
int (*iop_query_firmware)(struct arc_softc *);
|
||
|
};
|
||
|
|
||
|
static const struct arc_iop arc_intel = {
|
||
|
arc_chipA_firmware
|
||
|
};
|
||
|
|
||
|
static const struct arc_iop arc_marvell = {
|
||
|
arc_chipB_firmware
|
||
|
};
|
||
|
|
||
|
static const struct arc_iop arc_lsi = {
|
||
|
arc_chipC_firmware
|
||
|
};
|
||
|
|
||
|
static const struct arc_iop arc_marvell2 = {
|
||
|
arc_chipD_firmware
|
||
|
};
|
||
|
|
||
|
struct arc_board {
|
||
|
pcireg_t ab_vendor;
|
||
|
pcireg_t ab_product;
|
||
|
const struct arc_iop *ab_iop;
|
||
|
};
|
||
|
const struct arc_board *arc_match_board(struct pci_attach_args *);
|
||
|
|
||
|
static const struct arc_board arc_devices[] = {
|
||
|
{ PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1110, &arc_intel },
|
||
|
{ PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1120, &arc_intel },
|
||
|
{ PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1130, &arc_intel },
|
||
|
{ PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1160, &arc_intel },
|
||
|
{ PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1170, &arc_intel },
|
||
|
{ PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1200, &arc_intel },
|
||
|
{ PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1200_B, &arc_marvell },
|
||
|
{ PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1202, &arc_intel },
|
||
|
{ PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1210, &arc_intel },
|
||
|
{ PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1214, &arc_marvell2 },
|
||
|
{ PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1220, &arc_intel },
|
||
|
{ PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1230, &arc_intel },
|
||
|
{ PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1260, &arc_intel },
|
||
|
{ PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1270, &arc_intel },
|
||
|
{ PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1280, &arc_intel },
|
||
|
{ PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1380, &arc_intel },
|
||
|
{ PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1381, &arc_intel },
|
||
|
{ PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1680, &arc_intel },
|
||
|
{ PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1681, &arc_intel },
|
||
|
{ PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1880, &arc_lsi }
|
||
|
};
|
||
|
|
||
|
const struct arc_board *
|
||
|
arc_match_board(struct pci_attach_args *pa)
|
||
|
{
|
||
|
const struct arc_board *ab;
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < sizeof(arc_devices) / sizeof(arc_devices[0]); i++) {
|
||
|
ab = &arc_devices[i];
|
||
|
|
||
|
if (PCI_VENDOR(pa->pa_id) == ab->ab_vendor &&
|
||
|
PCI_PRODUCT(pa->pa_id) == ab->ab_product)
|
||
|
return (ab);
|
||
|
}
|
||
|
|
||
|
return (NULL);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_match(struct device *parent, void *match, void *aux)
|
||
|
{
|
||
|
return ((arc_match_board(aux) == NULL) ? 0 : 1);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
arc_attach(struct device *parent, struct device *self, void *aux)
|
||
|
{
|
||
|
struct arc_softc *sc = (struct arc_softc *)self;
|
||
|
struct pci_attach_args *pa = aux;
|
||
|
struct scsibus_attach_args saa;
|
||
|
|
||
|
sc->sc_talking = 0;
|
||
|
rw_init(&sc->sc_lock, "arcmsg");
|
||
|
|
||
|
sc->sc_iop = arc_match_board(pa)->ab_iop;
|
||
|
if(sc->sc_iop == &arc_intel)
|
||
|
sc->sc_adp_type = ARC_HBA_TYPE_A;
|
||
|
else if(sc->sc_iop == &arc_marvell)
|
||
|
sc->sc_adp_type = ARC_HBA_TYPE_B;
|
||
|
else if(sc->sc_iop == &arc_lsi)
|
||
|
sc->sc_adp_type = ARC_HBA_TYPE_C;
|
||
|
else if(sc->sc_iop == &arc_marvell2)
|
||
|
sc->sc_adp_type = ARC_HBA_TYPE_D;
|
||
|
if (arc_map_pci_resources(sc, pa) != 0) {
|
||
|
/* error message printed by arc_map_pci_resources */
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (arc_alloc_ccbs(sc) != 0) {
|
||
|
/* error message printed by arc_alloc_ccbs */
|
||
|
goto unmap_pci;
|
||
|
}
|
||
|
|
||
|
arc_iop_set_conf(sc);
|
||
|
|
||
|
if (sc->sc_iop->iop_query_firmware(sc) != 0) {
|
||
|
/* error message printed by arc_query_firmware */
|
||
|
goto unmap_pci;
|
||
|
}
|
||
|
|
||
|
saa.saa_adapter = &arc_switch;
|
||
|
saa.saa_adapter_softc = sc;
|
||
|
saa.saa_adapter_target = SDEV_NO_ADAPTER_TARGET;
|
||
|
saa.saa_adapter_buswidth = ARC_MAX_TARGET;
|
||
|
saa.saa_luns = 8;
|
||
|
saa.saa_openings = sc->sc_req_count;
|
||
|
saa.saa_pool = &sc->sc_iopool;
|
||
|
saa.saa_quirks = saa.saa_flags = 0;
|
||
|
saa.saa_wwpn = saa.saa_wwnn = 0;
|
||
|
|
||
|
sc->sc_scsibus = (struct scsibus_softc *)config_found(self, &saa,
|
||
|
scsiprint);
|
||
|
|
||
|
/* enable interrupts */
|
||
|
arc_enable_all_intr(sc);
|
||
|
|
||
|
#if NBIO > 0
|
||
|
if (bio_register(self, arc_bioctl) != 0)
|
||
|
panic("%s: bioctl registration failed", DEVNAME(sc));
|
||
|
|
||
|
#ifndef SMALL_KERNEL
|
||
|
/*
|
||
|
* you need to talk to the firmware to get volume info. our firmware
|
||
|
* interface relies on being able to sleep, so we need to use a thread
|
||
|
* to do the work.
|
||
|
*/
|
||
|
{
|
||
|
struct arc_task *at;
|
||
|
at = malloc(sizeof(*at), M_TEMP, M_WAITOK);
|
||
|
|
||
|
at->sc = sc;
|
||
|
task_set(&at->t, arc_create_sensors, at);
|
||
|
task_add(systq, &at->t);
|
||
|
}
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
return;
|
||
|
unmap_pci:
|
||
|
arc_unmap_pci_resources(sc);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_activate(struct device *self, int act)
|
||
|
{
|
||
|
int rv = 0;
|
||
|
|
||
|
switch (act) {
|
||
|
case DVACT_POWERDOWN:
|
||
|
rv = config_activate_children(self, act);
|
||
|
arc_detach(self, 0);
|
||
|
break;
|
||
|
default:
|
||
|
rv = config_activate_children(self, act);
|
||
|
break;
|
||
|
}
|
||
|
return (rv);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_detach(struct device *self, int flags)
|
||
|
{
|
||
|
struct arc_softc *sc = (struct arc_softc *)self;
|
||
|
|
||
|
arc_stop_bgrb_proc(sc);
|
||
|
arc_flush_cache(sc);
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_intr_A(void *arg)
|
||
|
{
|
||
|
struct arc_softc *sc = arg;
|
||
|
struct arc_ccb *ccb = NULL;
|
||
|
char *kva = ARC_DMA_KVA(sc->sc_requests);
|
||
|
struct arc_io_cmd *cmd;
|
||
|
u_int32_t reg, intrstat, error;
|
||
|
int ret = 0;
|
||
|
|
||
|
intrstat = arc_read(sc, ARC_RA_INTRSTAT);
|
||
|
intrstat &= ARC_RA_INTRSTAT_POSTQUEUE | ARC_RA_INTRSTAT_DOORBELL |
|
||
|
ARC_RA_INTRSTAT_MSG0;
|
||
|
arc_write(sc, ARC_RA_INTRSTAT, intrstat);
|
||
|
|
||
|
if (intrstat & ARC_RA_INTRSTAT_DOORBELL) {
|
||
|
ret = 1;
|
||
|
if (sc->sc_talking) {
|
||
|
/* if an ioctl is talking, wake it up */
|
||
|
arc_write(sc, ARC_RA_INTRMASK,
|
||
|
~ARC_RA_INTRMASK_POSTQUEUE);
|
||
|
wakeup(sc);
|
||
|
} else {
|
||
|
/* otherwise drop it */
|
||
|
reg = arc_read(sc, ARC_RA_OUTB_DOORBELL);
|
||
|
arc_write(sc, ARC_RA_OUTB_DOORBELL, reg);
|
||
|
if (reg & ARC_RA_OUTB_DOORBELL_WRITE_OK)
|
||
|
arc_write(sc, ARC_RA_INB_DOORBELL,
|
||
|
ARC_RA_INB_DOORBELL_READ_OK);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (intrstat & ARC_RA_INTRSTAT_POSTQUEUE) {
|
||
|
while ((reg = arc_read(sc, ARC_RA_REPLY_QUEUE)) != 0xffffffff) {
|
||
|
ret = 1;
|
||
|
cmd = (struct arc_io_cmd *)(kva +
|
||
|
((reg << ARC_RA_REPLY_QUEUE_ADDR_SHIFT) -
|
||
|
(u_int32_t)ARC_DMA_DVA(sc->sc_requests)));
|
||
|
ccb = cmd->ccb;
|
||
|
|
||
|
bus_dmamap_sync(sc->sc_dmat, ARC_DMA_MAP(sc->sc_requests),
|
||
|
ccb->cmd_dma_offset, ARC_MAX_IOCMDLEN,
|
||
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
||
|
|
||
|
error = (reg & ARC_RA_REPLY_QUEUE_ERR)? 1:0;
|
||
|
arc_scsi_cmd_done(sc, ccb, error);
|
||
|
}
|
||
|
}
|
||
|
return (ret);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_intr_C(void *arg)
|
||
|
{
|
||
|
struct arc_softc *sc = arg;
|
||
|
struct arc_ccb *ccb = NULL;
|
||
|
char *kva = ARC_DMA_KVA(sc->sc_requests);
|
||
|
struct arc_io_cmd *cmd;
|
||
|
u_int32_t reg, intrstat, obmsg, error;
|
||
|
int ret = 0, throttling;
|
||
|
|
||
|
intrstat = arc_read(sc, ARC_RC_INTR_STAT);
|
||
|
if (!(intrstat & (ARC_RC_INTR_STAT_POSTQUEUE |
|
||
|
ARC_RC_INTR_STAT_DOORBELL)))
|
||
|
return (ret);
|
||
|
|
||
|
if (intrstat & ARC_RC_INTR_STAT_DOORBELL) {
|
||
|
ret = 1;
|
||
|
if (sc->sc_talking) {
|
||
|
/* if an ioctl is talking, wake it up */
|
||
|
arc_write(sc, ARC_RC_INTR_MASK,
|
||
|
~ARC_RC_INTR_MASK_POSTQUEUE);
|
||
|
wakeup(sc);
|
||
|
} else {
|
||
|
/* otherwise drop it */
|
||
|
reg = arc_read(sc, ARC_RC_OUTB_DOORBELL);
|
||
|
arc_write(sc, ARC_RC_OUTB_DOORBELL_CLR, reg);
|
||
|
if (reg & ARC_RC_I2D_DATA_WRITE_OK) {
|
||
|
arc_write(sc, ARC_RC_INB_DOORBELL,
|
||
|
ARC_RC_I2D_DATA_READ_OK);
|
||
|
}
|
||
|
/* if (reg & ARC_RC_I2D_DATA_READ_OK) {
|
||
|
arc_write(sc, ARC_RC_INB_DOORBELL,
|
||
|
ARC_RC_D2I_DATA_WRITE_OK);
|
||
|
}
|
||
|
*/
|
||
|
if (reg & ARC_RC_I2D_MESSAGE_CMD_DONE) {
|
||
|
arc_write(sc, ARC_RC_OUTB_DOORBELL_CLR,
|
||
|
ARC_RC_I2D_MSG_CMD_DONE_CLR);
|
||
|
obmsg = arc_read(sc, ARC_RC_MSG_RWBUF);
|
||
|
if (obmsg == ARC_FWINFO_SIGNATURE_GET_CONFIG)
|
||
|
; /* handle devices hot-plug */
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (intrstat & ARC_RC_INTR_STAT_POSTQUEUE) {
|
||
|
ret = 1;
|
||
|
throttling = 0;
|
||
|
while (arc_read(sc, ARC_RC_INTR_STAT) &
|
||
|
ARC_RC_INTR_STAT_POSTQUEUE) {
|
||
|
reg = arc_read(sc, ARC_RC_OUTB_REPLYQ_LOW);
|
||
|
cmd = (struct arc_io_cmd *)(kva + ((reg & 0xFFFFFFE0) -
|
||
|
(u_int32_t)ARC_DMA_DVA(sc->sc_requests)));
|
||
|
ccb = cmd->ccb;
|
||
|
|
||
|
bus_dmamap_sync(sc->sc_dmat, ARC_DMA_MAP(sc->sc_requests),
|
||
|
ccb->cmd_dma_offset, ARC_MAX_IOCMDLEN,
|
||
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
||
|
|
||
|
error = (reg & ARC_RC_REPLY_QUEUE_ERR);
|
||
|
arc_scsi_cmd_done(sc, ccb, error);
|
||
|
throttling++;
|
||
|
if(throttling == ARC_RC_THROTTLE) {
|
||
|
arc_write(sc, ARC_RC_INB_DOORBELL,
|
||
|
ARC_RC_D2I_POSTQUEUE_THROTTLING);
|
||
|
throttling = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (ret);
|
||
|
}
|
||
|
|
||
|
static u_int16_t
|
||
|
arcmsr_get_doneq_index(struct arc_HBD_Msgu *phbdmu)
|
||
|
{
|
||
|
u_int16_t doneq_index, index_stripped;
|
||
|
|
||
|
doneq_index = phbdmu->doneq_index;
|
||
|
if (doneq_index & 0x4000) {
|
||
|
index_stripped = doneq_index & 0xFF;
|
||
|
index_stripped += 1;
|
||
|
index_stripped %= ARCMSR_MAX_HBD_POSTQUEUE;
|
||
|
phbdmu->doneq_index = index_stripped ?
|
||
|
(index_stripped | 0x4000) : index_stripped;
|
||
|
} else {
|
||
|
index_stripped = doneq_index;
|
||
|
index_stripped += 1;
|
||
|
index_stripped %= ARCMSR_MAX_HBD_POSTQUEUE;
|
||
|
phbdmu->doneq_index = index_stripped ?
|
||
|
index_stripped : (index_stripped | 0x4000);
|
||
|
}
|
||
|
return (phbdmu->doneq_index);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_intr_D(void *arg)
|
||
|
{
|
||
|
struct arc_softc *sc = arg;
|
||
|
struct arc_ccb *ccb = NULL;
|
||
|
char *kva = ARC_DMA_KVA(sc->sc_requests);
|
||
|
struct arc_io_cmd *cmd;
|
||
|
u_int32_t reg, intrstat, obmsg, error;
|
||
|
u_int32_t ob_write_ptr;
|
||
|
u_int16_t doneq_index;
|
||
|
int ret = 0;
|
||
|
struct arc_HBD_Msgu *pmu;
|
||
|
|
||
|
intrstat = arc_read(sc, ARC_RD_INTR_STAT);
|
||
|
if (!(intrstat & (ARC_RD_INTR_STAT_POSTQUEUE |
|
||
|
ARC_RD_INTR_STAT_DOORBELL)))
|
||
|
return (ret);
|
||
|
|
||
|
if (intrstat & ARC_RD_INTR_STAT_DOORBELL) {
|
||
|
ret = 1;
|
||
|
if (sc->sc_talking) {
|
||
|
/* if an ioctl is talking, wake it up */
|
||
|
arc_write(sc, ARC_RD_INTR_ENABLE,
|
||
|
ARC_RD_INTR_ENABLE_POSTQUEUE);
|
||
|
wakeup(sc);
|
||
|
} else {
|
||
|
/* otherwise drop it */
|
||
|
reg = arc_read(sc, ARC_RD_OUTB_DOORBELL);
|
||
|
arc_write(sc, ARC_RD_OUTB_DOORBELL, reg);
|
||
|
if (reg & ARC_RD_I2D_DATA_WRITE_OK) {
|
||
|
arc_write(sc, ARC_RD_INB_DOORBELL,
|
||
|
ARC_RD_I2D_DATA_READ_OK);
|
||
|
}
|
||
|
/* if (reg & ARC_RD_I2D_DATA_READ_OK) {
|
||
|
arc_write(sc, ARC_RD_INB_DOORBELL,
|
||
|
ARC_RD_D2I_DATA_WRITE_OK);
|
||
|
}
|
||
|
*/
|
||
|
if (reg & ARC_RD_I2D_MESSAGE_CMD_DONE) {
|
||
|
arc_write(sc, ARC_RD_OUTB_DOORBELL_CLR,
|
||
|
ARC_RD_I2D_MSG_CMD_DONE_CLR);
|
||
|
obmsg = arc_read(sc, ARC_RD_MSG_RWBUF);
|
||
|
if (obmsg == ARC_FWINFO_SIGNATURE_GET_CONFIG)
|
||
|
; /* handle devices hot-plug */
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (intrstat & ARC_RD_INTR_STAT_POSTQUEUE) {
|
||
|
ret = 1;
|
||
|
arc_write(sc, ARC_RD_OUTB_INTR_CAUSE, ARC_RD_OUTB_LIST_INT_CLR);
|
||
|
bus_dmamap_sync(sc->sc_dmat, ARC_DMA_MAP(sc->sc_requests),
|
||
|
sc->cmdQ_ptr_offset, ARC_MAX_CMDQ_PTR_LEN,
|
||
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
||
|
pmu = sc->pmu;
|
||
|
ob_write_ptr = pmu->done_qbuffer[0].addressLow;
|
||
|
doneq_index = pmu->doneq_index;
|
||
|
while ((doneq_index & 0xFF) != (ob_write_ptr & 0xFF)) {
|
||
|
doneq_index = arcmsr_get_doneq_index(pmu);
|
||
|
reg = pmu->done_qbuffer[(doneq_index & 0xFF)+1].addressLow;
|
||
|
cmd = (struct arc_io_cmd *)(kva + ((reg & 0xFFFFFFF0) -
|
||
|
(u_int32_t)ARC_DMA_DVA(sc->sc_requests)));
|
||
|
ccb = cmd->ccb;
|
||
|
bus_dmamap_sync(sc->sc_dmat, ARC_DMA_MAP(sc->sc_requests),
|
||
|
ccb->cmd_dma_offset, ARC_MAX_IOCMDLEN,
|
||
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
||
|
error = (reg & ARC_RD_REPLY_QUEUE_ERR);
|
||
|
arc_scsi_cmd_done(sc, ccb, error);
|
||
|
arc_write(sc, ARC_RD_OUTB_READ_PTR, doneq_index);
|
||
|
ob_write_ptr = pmu->done_qbuffer[0].addressLow;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (ret);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_intr(void *arg)
|
||
|
{
|
||
|
struct arc_softc *sc = arg;
|
||
|
int ret = 0;
|
||
|
|
||
|
switch(sc->sc_adp_type) {
|
||
|
case ARC_HBA_TYPE_A:
|
||
|
ret = arc_intr_A(arg);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_C:
|
||
|
ret = arc_intr_C(arg);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_D:
|
||
|
ret = arc_intr_D(arg);
|
||
|
break;
|
||
|
}
|
||
|
return (ret);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
arc_scsi_cmd(struct scsi_xfer *xs)
|
||
|
{
|
||
|
struct scsi_link *link = xs->sc_link;
|
||
|
struct arc_softc *sc = link->bus->sb_adapter_softc;
|
||
|
struct arc_ccb *ccb;
|
||
|
struct arc_msg_scsicmd *cmd;
|
||
|
u_int32_t reg, cdb_len;
|
||
|
int s;
|
||
|
struct arc_HBD_Msgu *pmu;
|
||
|
u_int16_t index_stripped;
|
||
|
u_int16_t postq_index;
|
||
|
struct InBound_SRB *pinbound_srb;
|
||
|
|
||
|
if (xs->cmdlen > ARC_MSG_CDBLEN) {
|
||
|
bzero(&xs->sense, sizeof(xs->sense));
|
||
|
xs->sense.error_code = SSD_ERRCODE_VALID | 0x70;
|
||
|
xs->sense.flags = SKEY_ILLEGAL_REQUEST;
|
||
|
xs->sense.add_sense_code = 0x20;
|
||
|
xs->error = XS_SENSE;
|
||
|
scsi_done(xs);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ccb = xs->io;
|
||
|
ccb->ccb_xs = xs;
|
||
|
|
||
|
if (arc_load_xs(ccb) != 0) {
|
||
|
xs->error = XS_DRIVER_STUFFUP;
|
||
|
scsi_done(xs);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
cmd = &ccb->ccb_cmd->cmd;
|
||
|
reg = ccb->ccb_cmd_post;
|
||
|
ccb->ccb_cmd->ccb = ccb;
|
||
|
/* bus is always 0 */
|
||
|
cmd->target = link->target;
|
||
|
cmd->lun = link->lun;
|
||
|
cmd->function = 1; /* XXX magic number */
|
||
|
|
||
|
cmd->cdb_len = xs->cmdlen;
|
||
|
cmd->sgl_len = ccb->ccb_dmamap->dm_nsegs;
|
||
|
if (xs->flags & SCSI_DATA_OUT)
|
||
|
cmd->flags = ARC_MSG_SCSICMD_FLAG_WRITE;
|
||
|
if (ccb->ccb_dmamap->dm_nsegs > ARC_SGL_256LEN) {
|
||
|
cmd->flags |= ARC_MSG_SCSICMD_FLAG_SGL_BSIZE_512;
|
||
|
/* reg |= ARC_RA_POST_QUEUE_BIGFRAME; */
|
||
|
}
|
||
|
|
||
|
cmd->data_len = htole32(xs->datalen);
|
||
|
|
||
|
bcopy(&xs->cmd, cmd->cdb, xs->cmdlen);
|
||
|
|
||
|
/* we've built the command, let's put it on the hw */
|
||
|
bus_dmamap_sync(sc->sc_dmat, ARC_DMA_MAP(sc->sc_requests),
|
||
|
ccb->cmd_dma_offset, ARC_MAX_IOCMDLEN,
|
||
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
||
|
|
||
|
s = splbio();
|
||
|
switch(sc->sc_adp_type) {
|
||
|
case ARC_HBA_TYPE_A:
|
||
|
if (cmd->flags & ARC_MSG_SCSICMD_FLAG_SGL_BSIZE_512)
|
||
|
reg |= ARC_RA_POST_QUEUE_BIGFRAME;
|
||
|
arc_write(sc, ARC_RA_POST_QUEUE, reg);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_C:
|
||
|
cdb_len = sizeof(struct arc_msg_scsicmd) +
|
||
|
sizeof(struct arc_sge) * ccb->ccb_dmamap->dm_nsegs;
|
||
|
if (cdb_len > 0x300)
|
||
|
cdb_len = 0x300;
|
||
|
reg = reg | ((cdb_len - 1) >> 6) | 1;
|
||
|
if (sc->sc_ccb_phys_hi)
|
||
|
arc_write(sc, ARC_RC_INB_POSTQ_HIGH, sc->sc_ccb_phys_hi);
|
||
|
arc_write(sc, ARC_RC_INB_POSTQ_LOW, reg);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_D:
|
||
|
pmu = sc->pmu;
|
||
|
postq_index = pmu->postq_index;
|
||
|
pinbound_srb = (struct InBound_SRB *)&pmu->post_qbuffer[postq_index & 0xFF];
|
||
|
|
||
|
pinbound_srb->addressHigh = sc->sc_ccb_phys_hi;
|
||
|
pinbound_srb->addressLow = ccb->ccb_cmd_post;
|
||
|
pinbound_srb->length = ccb->arc_io_cmd_length >> 2;
|
||
|
cmd->context = ccb->ccb_cmd_post;
|
||
|
if (postq_index & 0x4000) {
|
||
|
index_stripped = postq_index & 0xFF;
|
||
|
index_stripped += 1;
|
||
|
index_stripped %= ARCMSR_MAX_HBD_POSTQUEUE;
|
||
|
pmu->postq_index = index_stripped ? (index_stripped | 0x4000) : index_stripped;
|
||
|
} else {
|
||
|
index_stripped = postq_index;
|
||
|
index_stripped += 1;
|
||
|
index_stripped %= ARCMSR_MAX_HBD_POSTQUEUE;
|
||
|
pmu->postq_index = index_stripped ? index_stripped : (index_stripped | 0x4000);
|
||
|
}
|
||
|
bus_dmamap_sync(sc->sc_dmat, ARC_DMA_MAP(sc->sc_requests),
|
||
|
sc->cmdQ_ptr_offset, ARC_MAX_CMDQ_PTR_LEN,
|
||
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
||
|
arc_write(sc, ARC_RD_INB_WRITE_PTR, postq_index);
|
||
|
break;
|
||
|
}
|
||
|
if (xs->flags & SCSI_POLL) {
|
||
|
if (arc_complete(sc, ccb, xs->timeout) != 0) {
|
||
|
xs->error = XS_DRIVER_STUFFUP;
|
||
|
scsi_done(xs);
|
||
|
}
|
||
|
}
|
||
|
splx(s);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_load_xs(struct arc_ccb *ccb)
|
||
|
{
|
||
|
struct arc_softc *sc = ccb->ccb_sc;
|
||
|
struct scsi_xfer *xs = ccb->ccb_xs;
|
||
|
bus_dmamap_t dmap = ccb->ccb_dmamap;
|
||
|
struct arc_sge *sgl = ccb->ccb_cmd->sgl, *sge;
|
||
|
u_int64_t addr;
|
||
|
int i, error;
|
||
|
u_int32_t msg_length;
|
||
|
|
||
|
if (xs->datalen == 0)
|
||
|
{
|
||
|
ccb->arc_io_cmd_length = sizeof(struct arc_msg_scsicmd);
|
||
|
ccb->ccb_cmd->cmd.msgPages = 1;
|
||
|
return (0);
|
||
|
}
|
||
|
error = bus_dmamap_load(sc->sc_dmat, dmap,
|
||
|
xs->data, xs->datalen, NULL,
|
||
|
(xs->flags & SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK);
|
||
|
if (error != 0) {
|
||
|
printf("%s: error %d loading dmamap\n", DEVNAME(sc), error);
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < dmap->dm_nsegs; i++) {
|
||
|
sge = &sgl[i];
|
||
|
|
||
|
sge->sg_length = htole32(ARC_SGE_64BIT | dmap->dm_segs[i].ds_len);
|
||
|
addr = dmap->dm_segs[i].ds_addr;
|
||
|
sge->sg_hi_addr = htole32((u_int32_t)(addr >> 32));
|
||
|
sge->sg_lo_addr = htole32((u_int32_t)addr);
|
||
|
}
|
||
|
ccb->arc_io_cmd_length = sizeof(struct arc_msg_scsicmd) +
|
||
|
sizeof(struct arc_sge) * dmap->dm_nsegs;
|
||
|
msg_length = ccb->arc_io_cmd_length;
|
||
|
ccb->ccb_cmd->cmd.msgPages = (msg_length/256) + ((msg_length % 256) ? 1 : 0);
|
||
|
|
||
|
bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize,
|
||
|
(xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD :
|
||
|
BUS_DMASYNC_PREWRITE);
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
arc_scsi_cmd_done(struct arc_softc *sc, struct arc_ccb *ccb, u_int32_t error)
|
||
|
{
|
||
|
struct scsi_xfer *xs = ccb->ccb_xs;
|
||
|
struct arc_msg_scsicmd *cmd;
|
||
|
|
||
|
if (xs->datalen != 0) {
|
||
|
bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap, 0,
|
||
|
ccb->ccb_dmamap->dm_mapsize, (xs->flags & SCSI_DATA_IN) ?
|
||
|
BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
|
||
|
bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap);
|
||
|
}
|
||
|
|
||
|
if (error) {
|
||
|
cmd = &ccb->ccb_cmd->cmd;
|
||
|
DPRINTF("%s: arc_scsi_cmd_done error! target 0x%x, lun 0x%x, "
|
||
|
"status = 0x%x\n", DEVNAME(sc), cmd->target, cmd->lun,
|
||
|
cmd->status);
|
||
|
DPRINTF("%s: scsi cdb: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x"
|
||
|
", 0x%x, 0x%x, 0x%x\n", DEVNAME(sc), cmd->cdb[0], cmd->cdb[1],
|
||
|
cmd->cdb[2], cmd->cdb[3],cmd->cdb[4], cmd->cdb[5],
|
||
|
cmd->cdb[6], cmd->cdb[7],cmd->cdb[8], cmd->cdb[9]);
|
||
|
|
||
|
switch (cmd->status) {
|
||
|
case ARC_MSG_STATUS_SELTIMEOUT:
|
||
|
case ARC_MSG_STATUS_ABORTED:
|
||
|
case ARC_MSG_STATUS_INIT_FAIL:
|
||
|
xs->status = SCSI_OK;
|
||
|
xs->error = XS_SELTIMEOUT;
|
||
|
break;
|
||
|
|
||
|
case SCSI_CHECK:
|
||
|
bzero(&xs->sense, sizeof(xs->sense));
|
||
|
bcopy(cmd->sense_data, &xs->sense,
|
||
|
min(ARC_MSG_SENSELEN, sizeof(xs->sense)));
|
||
|
xs->sense.error_code = SSD_ERRCODE_VALID | 0x70;
|
||
|
xs->status = SCSI_CHECK;
|
||
|
xs->error = XS_SENSE;
|
||
|
xs->resid = 0;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
/* unknown device status */
|
||
|
xs->error = XS_BUSY; /* try again later? */
|
||
|
xs->status = SCSI_BUSY;
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
xs->status = SCSI_OK;
|
||
|
xs->error = XS_NOERROR;
|
||
|
xs->resid = 0;
|
||
|
}
|
||
|
|
||
|
scsi_done(xs);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_complete(struct arc_softc *sc, struct arc_ccb *nccb, int timeout)
|
||
|
{
|
||
|
struct arc_ccb *ccb = NULL;
|
||
|
char *kva = ARC_DMA_KVA(sc->sc_requests);
|
||
|
struct arc_io_cmd *cmd;
|
||
|
u_int32_t reg, error, write_ptr;
|
||
|
u_int16_t doneq_index;
|
||
|
struct arc_HBD_Msgu *phbdmu;
|
||
|
int ret = 0;
|
||
|
|
||
|
arc_disable_all_intr(sc);
|
||
|
do {
|
||
|
switch(sc->sc_adp_type) {
|
||
|
case ARC_HBA_TYPE_A:
|
||
|
reg = arc_read(sc, ARC_RA_REPLY_QUEUE);
|
||
|
error = (reg & ARC_RA_REPLY_QUEUE_ERR)? 1:0;
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_C:
|
||
|
reg = arc_read(sc, ARC_RC_OUTB_REPLYQ_LOW);
|
||
|
error = (reg & ARC_RC_REPLY_QUEUE_ERR);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_D:
|
||
|
phbdmu = sc->pmu;
|
||
|
write_ptr = phbdmu->done_qbuffer[0].addressLow;
|
||
|
doneq_index = phbdmu->doneq_index;
|
||
|
if((write_ptr & 0xff) == (doneq_index & 0xff)) {
|
||
|
Loop0:
|
||
|
reg = 0xffffffff;
|
||
|
}
|
||
|
else {
|
||
|
doneq_index = arcmsr_get_doneq_index(phbdmu);
|
||
|
reg = phbdmu->done_qbuffer[(doneq_index & 0xFF)+1].addressLow;
|
||
|
if (reg == 0)
|
||
|
goto Loop0;
|
||
|
arc_write(sc, ARC_RD_OUTB_READ_PTR, doneq_index);
|
||
|
}
|
||
|
error = (reg & ARC_RD_REPLY_QUEUE_ERR);
|
||
|
break;
|
||
|
}
|
||
|
if (reg == 0xffffffff) {
|
||
|
if (timeout-- == 0) {
|
||
|
return (1);
|
||
|
}
|
||
|
delay(1000);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
switch(sc->sc_adp_type) {
|
||
|
case ARC_HBA_TYPE_A:
|
||
|
cmd = (struct arc_io_cmd *)(kva +
|
||
|
((reg << ARC_RA_REPLY_QUEUE_ADDR_SHIFT) -
|
||
|
ARC_DMA_DVA(sc->sc_requests)));
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_C:
|
||
|
case ARC_HBA_TYPE_D:
|
||
|
cmd = (struct arc_io_cmd *)(kva + ((reg & 0xFFFFFFE0) -
|
||
|
ARC_DMA_DVA(sc->sc_requests)));
|
||
|
break;
|
||
|
}
|
||
|
ccb = cmd->ccb;
|
||
|
|
||
|
bus_dmamap_sync(sc->sc_dmat, ARC_DMA_MAP(sc->sc_requests),
|
||
|
ccb->cmd_dma_offset, ARC_MAX_IOCMDLEN,
|
||
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
||
|
|
||
|
arc_scsi_cmd_done(sc, ccb, error);
|
||
|
} while (nccb != ccb);
|
||
|
arc_enable_all_intr(sc);
|
||
|
|
||
|
return (ret);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
arc_enable_all_intr(struct arc_softc *sc)
|
||
|
{
|
||
|
u_int32_t int_mask;
|
||
|
|
||
|
switch(sc->sc_adp_type) {
|
||
|
case ARC_HBA_TYPE_A:
|
||
|
int_mask = arc_read(sc, ARC_RA_INTRMASK);
|
||
|
int_mask &= ~(ARC_RA_INTRMASK_POSTQUEUE |
|
||
|
ARC_RA_INTRMASK_DOORBELL | ARC_RA_INTRMASK_MSG0);
|
||
|
arc_write(sc, ARC_RA_INTRMASK, int_mask);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_C:
|
||
|
int_mask = arc_read(sc, ARC_RC_INTR_MASK);
|
||
|
int_mask &= ~(ARC_RC_INTR_MASK_POSTQUEUE |
|
||
|
ARC_RC_INTR_MASK_DOORBELL | ARC_RC_INTR_MASK_UTILITY_A);
|
||
|
arc_write(sc, ARC_RC_INTR_MASK, int_mask);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_D:
|
||
|
int_mask = arc_read(sc, ARC_RD_INTR_ENABLE);
|
||
|
int_mask |= ARC_RD_INTR_ENABLE_ALL;
|
||
|
arc_write(sc, ARC_RD_INTR_ENABLE, int_mask);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
arc_disable_all_intr(struct arc_softc *sc)
|
||
|
{
|
||
|
u_int32_t int_mask;
|
||
|
|
||
|
switch(sc->sc_adp_type) {
|
||
|
case ARC_HBA_TYPE_A:
|
||
|
int_mask = arc_read(sc, ARC_RA_INTRMASK);
|
||
|
int_mask |= ARC_RA_INTR_MASK_ALL;
|
||
|
arc_write(sc, ARC_RA_INTRMASK, int_mask);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_C:
|
||
|
int_mask = arc_read(sc, ARC_RC_INTR_MASK);
|
||
|
int_mask |= ARC_RC_INTR_MASK_ALL;
|
||
|
arc_write(sc, ARC_RC_INTR_MASK, int_mask);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_D:
|
||
|
int_mask = arc_read(sc, ARC_RD_INTR_ENABLE);
|
||
|
int_mask &= ~ARC_RD_INTR_ENABLE_ALL;
|
||
|
arc_write(sc, ARC_RD_INTR_ENABLE, ARC_RD_INTR_DISABLE_ALL);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_map_pci_resources(struct arc_softc *sc, struct pci_attach_args *pa)
|
||
|
{
|
||
|
pcireg_t memtype;
|
||
|
pci_intr_handle_t ih;
|
||
|
|
||
|
sc->sc_pc = pa->pa_pc;
|
||
|
sc->sc_tag = pa->pa_tag;
|
||
|
sc->sc_dmat = pa->pa_dmat;
|
||
|
|
||
|
switch(sc->sc_adp_type) {
|
||
|
case ARC_HBA_TYPE_A:
|
||
|
memtype = pci_mapreg_type(sc->sc_pc, sc->sc_tag, ARC_RA_PCI_BAR);
|
||
|
if (pci_mapreg_map(pa, ARC_RA_PCI_BAR, memtype, 0, &sc->sc_iot,
|
||
|
&sc->sc_ioh, NULL, &sc->sc_ios, 0) != 0) {
|
||
|
printf(": unable to map ARC_HBA_TYPE_A system"
|
||
|
" interface register\n");
|
||
|
return(1);
|
||
|
}
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_C:
|
||
|
memtype = pci_mapreg_type(sc->sc_pc, sc->sc_tag, ARC_RC_PCI_BAR);
|
||
|
if (pci_mapreg_map(pa, ARC_RC_PCI_BAR, memtype, 0, &sc->sc_iot,
|
||
|
&sc->sc_ioh, NULL, &sc->sc_ios, 0) != 0) {
|
||
|
printf(": unable to map ARC_HBA_TYPE_C system"
|
||
|
" interface register\n");
|
||
|
return(1);
|
||
|
}
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_D:
|
||
|
memtype = pci_mapreg_type(sc->sc_pc, sc->sc_tag, ARC_RD_PCI_BAR);
|
||
|
if (pci_mapreg_map(pa, ARC_RD_PCI_BAR, memtype, 0, &sc->sc_iot,
|
||
|
&sc->sc_ioh, NULL, &sc->sc_ios, 0) != 0) {
|
||
|
printf(": unable to map ARC_HBA_TYPE_D system"
|
||
|
" interface register\n");
|
||
|
return(1);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
arc_disable_all_intr(sc);
|
||
|
|
||
|
if (pci_intr_map(pa, &ih) != 0) {
|
||
|
printf(": unable to map interrupt\n");
|
||
|
goto unmap;
|
||
|
}
|
||
|
sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO,
|
||
|
arc_intr, sc, DEVNAME(sc));
|
||
|
if (sc->sc_ih == NULL) {
|
||
|
printf(": unable to map interrupt\n");
|
||
|
goto unmap;
|
||
|
}
|
||
|
printf(": %s\n", pci_intr_string(pa->pa_pc, ih));
|
||
|
|
||
|
return (0);
|
||
|
|
||
|
unmap:
|
||
|
bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
|
||
|
sc->sc_ios = 0;
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
arc_unmap_pci_resources(struct arc_softc *sc)
|
||
|
{
|
||
|
pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
|
||
|
bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
|
||
|
sc->sc_ios = 0;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_chipA_firmware(struct arc_softc *sc)
|
||
|
{
|
||
|
struct arc_msg_firmware_info fwinfo;
|
||
|
char string[81]; /* sizeof(vendor)*2+1 */
|
||
|
u_int32_t ob_doorbell;
|
||
|
|
||
|
if (arc_wait_eq(sc, ARC_RA_OUTB_ADDR1, ARC_RA_OUTB_ADDR1_FIRMWARE_OK,
|
||
|
ARC_RA_OUTB_ADDR1_FIRMWARE_OK) != 0) {
|
||
|
printf("%s: timeout waiting for firmware ok\n", DEVNAME(sc));
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
if (arc_msg0(sc, ARC_RA_INB_MSG0_GET_CONFIG) != 0) {
|
||
|
printf("%s: timeout waiting for get config\n", DEVNAME(sc));
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
arc_read_region(sc, ARC_RA_MSGBUF, &fwinfo, sizeof(fwinfo));
|
||
|
|
||
|
DNPRINTF(ARC_D_INIT, "%s: signature: 0x%08x\n", DEVNAME(sc),
|
||
|
letoh32(fwinfo.signature));
|
||
|
|
||
|
if (letoh32(fwinfo.signature) != ARC_FWINFO_SIGNATURE_GET_CONFIG) {
|
||
|
printf("%s: invalid firmware info from iop\n", DEVNAME(sc));
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
DNPRINTF(ARC_D_INIT, "%s: request_len: %d\n", DEVNAME(sc),
|
||
|
letoh32(fwinfo.request_len));
|
||
|
DNPRINTF(ARC_D_INIT, "%s: queue_len: %d\n", DEVNAME(sc),
|
||
|
letoh32(fwinfo.queue_len));
|
||
|
DNPRINTF(ARC_D_INIT, "%s: sdram_size: %d\n", DEVNAME(sc),
|
||
|
letoh32(fwinfo.sdram_size));
|
||
|
DNPRINTF(ARC_D_INIT, "%s: sata_ports: %d\n", DEVNAME(sc),
|
||
|
letoh32(fwinfo.sata_ports));
|
||
|
|
||
|
scsi_strvis(string, fwinfo.vendor, sizeof(fwinfo.vendor));
|
||
|
DNPRINTF(ARC_D_INIT, "%s: vendor: \"%s\"\n", DEVNAME(sc), string);
|
||
|
scsi_strvis(string, fwinfo.model, sizeof(fwinfo.model));
|
||
|
DNPRINTF(ARC_D_INIT, "%s: model: \"%s\"\n", DEVNAME(sc), string);
|
||
|
|
||
|
scsi_strvis(string, fwinfo.fw_version, sizeof(fwinfo.fw_version));
|
||
|
DNPRINTF(ARC_D_INIT, "%s: firmware: \"%s\"\n", DEVNAME(sc), string);
|
||
|
|
||
|
if (letoh32(fwinfo.request_len) != ARC_MAX_IOCMDLEN) {
|
||
|
printf("%s: unexpected request frame size (%d != %d)\n",
|
||
|
DEVNAME(sc), letoh32(fwinfo.request_len), ARC_MAX_IOCMDLEN);
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
sc->sc_req_count = letoh32(fwinfo.queue_len);
|
||
|
|
||
|
if (arc_msg0(sc, ARC_RA_INB_MSG0_START_BGRB) != 0) {
|
||
|
printf("%s: timeout waiting to start bg rebuild\n",
|
||
|
DEVNAME(sc));
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
/* clear doorbell buffer */
|
||
|
ob_doorbell = arc_read(sc, ARC_RA_OUTB_DOORBELL);
|
||
|
arc_write(sc, ARC_RA_OUTB_DOORBELL, ob_doorbell);
|
||
|
arc_write(sc, ARC_RA_INB_DOORBELL, ARC_RA_INB_DOORBELL_READ_OK);
|
||
|
|
||
|
printf("%s: %d ports, %dMB SDRAM, firmware %s\n",
|
||
|
DEVNAME(sc), letoh32(fwinfo.sata_ports),
|
||
|
letoh32(fwinfo.sdram_size), string);
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_chipB_firmware(struct arc_softc *sc)
|
||
|
{
|
||
|
if (arc_wait_eq(sc, ARC_RB_IOP2DRV_DOORBELL,
|
||
|
ARC_RA_OUTB_ADDR1_FIRMWARE_OK,
|
||
|
ARC_RA_OUTB_ADDR1_FIRMWARE_OK) != 0) {
|
||
|
printf("%s: timeout waiting for firmware ok\n", DEVNAME(sc));
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_chipC_firmware(struct arc_softc *sc)
|
||
|
{
|
||
|
struct arc_msg_firmware_info fwinfo;
|
||
|
char string[81]; /* sizeof(vendor)*2+1 */
|
||
|
u_int32_t ob_doorbell;
|
||
|
|
||
|
if (arc_wait_eq(sc, ARC_RC_OUTB_MSGADDR1, ARC_RC_OUTB_MSG_FIRMWARE_OK,
|
||
|
ARC_RC_OUTB_MSG_FIRMWARE_OK) != 0) {
|
||
|
printf("%s: timeout waiting for firmware ok\n", DEVNAME(sc));
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
if (arc_msg0(sc, ARC_RC_INB_MSG0_GET_CONFIG) != 0) {
|
||
|
printf("%s: timeout waiting for get config\n", DEVNAME(sc));
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
arc_read_region(sc, ARC_RC_MSG_RWBUF, &fwinfo, sizeof(fwinfo));
|
||
|
|
||
|
DNPRINTF(ARC_D_INIT, "%s: signature: 0x%08x\n", DEVNAME(sc),
|
||
|
letoh32(fwinfo.signature));
|
||
|
|
||
|
if (letoh32(fwinfo.signature) != ARC_FWINFO_SIGNATURE_GET_CONFIG) {
|
||
|
printf("%s: invalid firmware info from iop\n", DEVNAME(sc));
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
DNPRINTF(ARC_D_INIT, "%s: request_len: %d\n", DEVNAME(sc),
|
||
|
letoh32(fwinfo.request_len));
|
||
|
DNPRINTF(ARC_D_INIT, "%s: queue_len: %d\n", DEVNAME(sc),
|
||
|
letoh32(fwinfo.queue_len));
|
||
|
DNPRINTF(ARC_D_INIT, "%s: sdram_size: %d\n", DEVNAME(sc),
|
||
|
letoh32(fwinfo.sdram_size));
|
||
|
DNPRINTF(ARC_D_INIT, "%s: sata_ports: %d\n", DEVNAME(sc),
|
||
|
letoh32(fwinfo.sata_ports));
|
||
|
|
||
|
scsi_strvis(string, fwinfo.vendor, sizeof(fwinfo.vendor));
|
||
|
DNPRINTF(ARC_D_INIT, "%s: vendor: \"%s\"\n", DEVNAME(sc), string);
|
||
|
scsi_strvis(string, fwinfo.model, sizeof(fwinfo.model));
|
||
|
DNPRINTF(ARC_D_INIT, "%s: model: \"%s\"\n", DEVNAME(sc), string);
|
||
|
|
||
|
scsi_strvis(string, fwinfo.fw_version, sizeof(fwinfo.fw_version));
|
||
|
DNPRINTF(ARC_D_INIT, "%s: firmware: \"%s\"\n", DEVNAME(sc), string);
|
||
|
|
||
|
if (letoh32(fwinfo.request_len) != ARC_MAX_IOCMDLEN) {
|
||
|
printf("%s: unexpected request frame size (%d != %d)\n",
|
||
|
DEVNAME(sc), letoh32(fwinfo.request_len), ARC_MAX_IOCMDLEN);
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
sc->sc_req_count = letoh32(fwinfo.queue_len);
|
||
|
|
||
|
if (arc_msg0(sc, ARC_RC_INB_MSG0_START_BGRB) != 0) {
|
||
|
printf("%s: timeout waiting to start bg rebuild\n",
|
||
|
DEVNAME(sc));
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
/* clear doorbell buffer */
|
||
|
ob_doorbell = arc_read(sc, ARC_RC_OUTB_DOORBELL);
|
||
|
arc_write(sc, ARC_RC_OUTB_DOORBELL_CLR, ob_doorbell);
|
||
|
arc_write(sc, ARC_RC_INB_DOORBELL, ARC_RC_D2I_DATA_READ_OK);
|
||
|
|
||
|
printf("%s: %d ports, %dMB SDRAM, firmware %s\n",
|
||
|
DEVNAME(sc), letoh32(fwinfo.sata_ports),
|
||
|
letoh32(fwinfo.sdram_size), string);
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_chipD_firmware(struct arc_softc *sc)
|
||
|
{
|
||
|
struct arc_msg_firmware_info fwinfo;
|
||
|
char string[81]; /* sizeof(vendor)*2+1 */
|
||
|
u_int32_t ob_doorbell;
|
||
|
|
||
|
if (arc_wait_eq(sc, ARC_RD_OUTB_MSGADDR1, ARC_RD_OUTB_MSG_FIRMWARE_OK,
|
||
|
ARC_RD_OUTB_MSG_FIRMWARE_OK) != 0) {
|
||
|
printf("%s: timeout waiting for firmware ok\n", DEVNAME(sc));
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
if ((arc_read(sc, ARC_RD_OUTB_DOORBELL) & ARC_RD_I2D_MESSAGE_CMD_DONE))
|
||
|
arc_write(sc, ARC_RD_OUTB_DOORBELL, ARC_RD_I2D_MESSAGE_CMD_DONE_CLEAR);
|
||
|
|
||
|
if (arc_msg0(sc, ARC_RD_INB_MSG0_GET_CONFIG) != 0) {
|
||
|
printf("%s: timeout waiting for get config\n", DEVNAME(sc));
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
arc_read_region(sc, ARC_RD_MSG_RWBUF, &fwinfo, sizeof(fwinfo));
|
||
|
|
||
|
DNPRINTF(ARC_D_INIT, "%s: signature: 0x%08x\n", DEVNAME(sc),
|
||
|
letoh32(fwinfo.signature));
|
||
|
|
||
|
if (letoh32(fwinfo.signature) != ARC_FWINFO_SIGNATURE_GET_CONFIG) {
|
||
|
printf("%s: invalid firmware info from iop\n", DEVNAME(sc));
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
DNPRINTF(ARC_D_INIT, "%s: request_len: %d\n", DEVNAME(sc),
|
||
|
letoh32(fwinfo.request_len));
|
||
|
DNPRINTF(ARC_D_INIT, "%s: queue_len: %d\n", DEVNAME(sc),
|
||
|
letoh32(fwinfo.queue_len));
|
||
|
DNPRINTF(ARC_D_INIT, "%s: sdram_size: %d\n", DEVNAME(sc),
|
||
|
letoh32(fwinfo.sdram_size));
|
||
|
DNPRINTF(ARC_D_INIT, "%s: sata_ports: %d\n", DEVNAME(sc),
|
||
|
letoh32(fwinfo.sata_ports));
|
||
|
|
||
|
scsi_strvis(string, fwinfo.vendor, sizeof(fwinfo.vendor));
|
||
|
DNPRINTF(ARC_D_INIT, "%s: vendor: \"%s\"\n", DEVNAME(sc), string);
|
||
|
scsi_strvis(string, fwinfo.model, sizeof(fwinfo.model));
|
||
|
DNPRINTF(ARC_D_INIT, "%s: model: \"%s\"\n", DEVNAME(sc), string);
|
||
|
|
||
|
scsi_strvis(string, fwinfo.fw_version, sizeof(fwinfo.fw_version));
|
||
|
DNPRINTF(ARC_D_INIT, "%s: firmware: \"%s\"\n", DEVNAME(sc), string);
|
||
|
|
||
|
if (letoh32(fwinfo.request_len) != ARC_MAX_IOCMDLEN) {
|
||
|
printf("%s: unexpected request frame size (%d != %d)\n",
|
||
|
DEVNAME(sc), letoh32(fwinfo.request_len), ARC_MAX_IOCMDLEN);
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
sc->sc_req_count = letoh32(fwinfo.queue_len) - 1;
|
||
|
|
||
|
if (arc_msg0(sc, ARC_RD_INB_MSG0_START_BGRB) != 0) {
|
||
|
printf("%s: timeout waiting to start bg rebuild\n",
|
||
|
DEVNAME(sc));
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
/* clear doorbell buffer */
|
||
|
ob_doorbell = arc_read(sc, ARC_RD_OUTB_DOORBELL);
|
||
|
arc_write(sc, ARC_RD_OUTB_DOORBELL_CLR, ob_doorbell);
|
||
|
arc_write(sc, ARC_RD_INB_DOORBELL, ARC_RD_D2I_DATA_READ_OK);
|
||
|
|
||
|
printf("%s: %d ports, %dMB SDRAM, firmware %s\n",
|
||
|
DEVNAME(sc), letoh32(fwinfo.sata_ports),
|
||
|
letoh32(fwinfo.sdram_size), string);
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
arc_stop_bgrb_proc(struct arc_softc *sc)
|
||
|
{
|
||
|
switch(sc->sc_adp_type) {
|
||
|
case ARC_HBA_TYPE_A:
|
||
|
if (arc_msg0(sc, ARC_RA_INB_MSG0_STOP_BGRB) != 0)
|
||
|
printf("%s: timeout waiting to stop bg rebuild\n",
|
||
|
DEVNAME(sc));
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_C:
|
||
|
if (arc_msg0(sc, ARC_RC_INB_MSG0_STOP_BGRB) != 0)
|
||
|
printf("%s: timeout waiting to stop bg rebuild\n",
|
||
|
DEVNAME(sc));
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_D:
|
||
|
if (arc_msg0(sc, ARC_RD_INB_MSG0_STOP_BGRB) != 0)
|
||
|
printf("%s: timeout waiting to stop bg rebuild\n",
|
||
|
DEVNAME(sc));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
arc_flush_cache(struct arc_softc *sc)
|
||
|
{
|
||
|
switch(sc->sc_adp_type) {
|
||
|
case ARC_HBA_TYPE_A:
|
||
|
if (arc_msg0(sc, ARC_RA_INB_MSG0_FLUSH_CACHE) != 0)
|
||
|
printf("%s: timeout waiting to flush cache\n",
|
||
|
DEVNAME(sc));
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_C:
|
||
|
if (arc_msg0(sc, ARC_RC_INB_MSG0_FLUSH_CACHE) != 0)
|
||
|
printf("%s: timeout waiting to flush cache\n",
|
||
|
DEVNAME(sc));
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_D:
|
||
|
if (arc_msg0(sc, ARC_RD_INB_MSG0_FLUSH_CACHE) != 0)
|
||
|
printf("%s: timeout waiting to flush cache\n",
|
||
|
DEVNAME(sc));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
arc_iop_set_conf(struct arc_softc *sc)
|
||
|
{
|
||
|
u_int32_t ccb_phys_hi;
|
||
|
struct arc_HBD_Msgu *phbdmu;
|
||
|
|
||
|
ccb_phys_hi = sc->sc_ccb_phys_hi;
|
||
|
switch (sc->sc_adp_type) {
|
||
|
case ARC_HBA_TYPE_A:
|
||
|
arc_write(sc, ARC_RA_MSGBUF, ARC_FWINFO_SIGNATURE_SET_CONFIG);
|
||
|
arc_write(sc, ARC_RA_MSGBUF+1, ccb_phys_hi);
|
||
|
arc_msg0(sc, ARC_RA_INB_MSG0_SET_CONFIG);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_C:
|
||
|
arc_write(sc, ARC_RC_MSG_RWBUF, ARC_FWINFO_SIGNATURE_SET_CONFIG);
|
||
|
arc_write(sc, ARC_RC_MSG_RWBUF+1, ccb_phys_hi);
|
||
|
arc_msg0(sc, ARC_RC_INB_MSG0_SET_CONFIG);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_D:
|
||
|
phbdmu = sc->pmu;
|
||
|
phbdmu->postq_index = 0;
|
||
|
phbdmu->doneq_index = 0x40FF;
|
||
|
arc_write(sc, ARC_RD_MSG_RWBUF, ARC_FWINFO_SIGNATURE_SET_CONFIG);
|
||
|
arc_write(sc, ARC_RD_MSG_RWBUF+4, ccb_phys_hi);
|
||
|
arc_write(sc, ARC_RD_MSG_RWBUF+8, sc->postQ_buffer);
|
||
|
arc_write(sc, ARC_RD_MSG_RWBUF+12, sc->doneQ_buffer);
|
||
|
arc_write(sc, ARC_RD_MSG_RWBUF+16, 0x100);
|
||
|
arc_msg0(sc, ARC_RD_INB_MSG0_SET_CONFIG);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if NBIO > 0
|
||
|
int
|
||
|
arc_bioctl(struct device *self, u_long cmd, caddr_t addr)
|
||
|
{
|
||
|
struct arc_softc *sc = (struct arc_softc *)self;
|
||
|
int error = 0;
|
||
|
|
||
|
DPRINTF("%s: arc_bioctl\n", DEVNAME(sc));
|
||
|
switch (cmd) {
|
||
|
case BIOCINQ:
|
||
|
error = arc_bio_inq(sc, (struct bioc_inq *)addr);
|
||
|
break;
|
||
|
|
||
|
case BIOCVOL:
|
||
|
error = arc_bio_vol(sc, (struct bioc_vol *)addr);
|
||
|
break;
|
||
|
|
||
|
case BIOCDISK:
|
||
|
error = arc_bio_disk(sc, (struct bioc_disk *)addr);
|
||
|
break;
|
||
|
|
||
|
case BIOCALARM:
|
||
|
error = arc_bio_alarm(sc, (struct bioc_alarm *)addr);
|
||
|
break;
|
||
|
|
||
|
case BIOCBLINK:
|
||
|
error = arc_bio_blink(sc, (struct bioc_blink *)addr);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
error = ENOTTY;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return (error);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_bio_alarm(struct arc_softc *sc, struct bioc_alarm *ba)
|
||
|
{
|
||
|
u_int8_t request[2];
|
||
|
u_int8_t reply[1];
|
||
|
size_t len;
|
||
|
int error = 0;
|
||
|
|
||
|
DPRINTF("%s: arc_bio_alarm\n", DEVNAME(sc));
|
||
|
switch (ba->ba_opcode) {
|
||
|
case BIOC_SAENABLE:
|
||
|
case BIOC_SADISABLE:
|
||
|
request[0] = ARC_FW_SET_ALARM;
|
||
|
request[1] = (ba->ba_opcode == BIOC_SAENABLE) ?
|
||
|
ARC_FW_SET_ALARM_ENABLE : ARC_FW_SET_ALARM_DISABLE;
|
||
|
len = sizeof(request);
|
||
|
|
||
|
break;
|
||
|
|
||
|
case BIOC_SASILENCE:
|
||
|
request[0] = ARC_FW_MUTE_ALARM;
|
||
|
len = 1;
|
||
|
|
||
|
break;
|
||
|
|
||
|
case BIOC_GASTATUS:
|
||
|
/* system info is too big/ugly to deal with here */
|
||
|
return (arc_bio_alarm_state(sc, ba));
|
||
|
|
||
|
default:
|
||
|
return (EOPNOTSUPP);
|
||
|
}
|
||
|
|
||
|
arc_lock(sc);
|
||
|
error = arc_msgbuf(sc, request, len, reply, sizeof(reply), 0);
|
||
|
arc_unlock(sc);
|
||
|
|
||
|
if (error != 0)
|
||
|
return (error);
|
||
|
|
||
|
switch (reply[0]) {
|
||
|
case ARC_FW_CMD_OK:
|
||
|
return (0);
|
||
|
case ARC_FW_CMD_PASS_REQD:
|
||
|
return (EPERM);
|
||
|
default:
|
||
|
return (EIO);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_bio_alarm_state(struct arc_softc *sc, struct bioc_alarm *ba)
|
||
|
{
|
||
|
u_int8_t request = ARC_FW_SYSINFO;
|
||
|
struct arc_fw_sysinfo *sysinfo;
|
||
|
int error = 0;
|
||
|
|
||
|
sysinfo = malloc(sizeof(struct arc_fw_sysinfo), M_TEMP, M_WAITOK);
|
||
|
|
||
|
request = ARC_FW_SYSINFO;
|
||
|
|
||
|
arc_lock(sc);
|
||
|
error = arc_msgbuf(sc, &request, sizeof(request),
|
||
|
sysinfo, sizeof(struct arc_fw_sysinfo), 0);
|
||
|
arc_unlock(sc);
|
||
|
|
||
|
if (error != 0)
|
||
|
goto out;
|
||
|
|
||
|
ba->ba_status = sysinfo->alarm;
|
||
|
|
||
|
out:
|
||
|
free(sysinfo, M_TEMP, sizeof *sysinfo);
|
||
|
return (error);
|
||
|
}
|
||
|
|
||
|
|
||
|
int
|
||
|
arc_bio_inq(struct arc_softc *sc, struct bioc_inq *bi)
|
||
|
{
|
||
|
u_int8_t request[2];
|
||
|
struct arc_fw_sysinfo *sysinfo;
|
||
|
struct arc_fw_volinfo *volinfo;
|
||
|
int maxvols, nvols = 0, i;
|
||
|
int error = 0;
|
||
|
char string[20];
|
||
|
|
||
|
DPRINTF("%s: arc_bio_inq\n", DEVNAME(sc));
|
||
|
sysinfo = malloc(sizeof(struct arc_fw_sysinfo), M_TEMP, M_WAITOK);
|
||
|
volinfo = malloc(sizeof(struct arc_fw_volinfo), M_TEMP, M_WAITOK);
|
||
|
|
||
|
arc_lock(sc);
|
||
|
|
||
|
request[0] = ARC_FW_SYSINFO;
|
||
|
error = arc_msgbuf(sc, request, 1, sysinfo,
|
||
|
sizeof(struct arc_fw_sysinfo), 0);
|
||
|
if (error != 0) {
|
||
|
DPRINTF("%s: arc_bio_inq get sysinfo failed!\n", DEVNAME(sc));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
maxvols = sysinfo->max_volume_set;
|
||
|
|
||
|
request[0] = ARC_FW_VOLINFO;
|
||
|
for (i = 0; i < maxvols; i++) {
|
||
|
request[1] = i;
|
||
|
error = arc_msgbuf(sc, request, sizeof(request), volinfo,
|
||
|
sizeof(struct arc_fw_volinfo), 0);
|
||
|
if (error != 0) {
|
||
|
DPRINTF("%s: arc_bio_inq get volinfo failed!\n", DEVNAME(sc));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* I can't find an easy way to see if the volume exists or not
|
||
|
* except to say that if it has no capacity then it isn't there.
|
||
|
* Ignore passthru volumes, bioc_vol doesn't understand them.
|
||
|
*/
|
||
|
if ((volinfo->capacity != 0 || volinfo->capacity2 != 0) &&
|
||
|
volinfo->raid_level != ARC_FW_VOL_RAIDLEVEL_PASSTHRU) {
|
||
|
nvols++;
|
||
|
scsi_strvis(string, volinfo->set_name, 16);
|
||
|
DPRINTF("%s: volume set: \"%s\"\n", DEVNAME(sc), string);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
strlcpy(bi->bi_dev, DEVNAME(sc), sizeof(bi->bi_dev));
|
||
|
bi->bi_novol = nvols;
|
||
|
DPRINTF("%s: volume set number = %d\n", DEVNAME(sc), nvols);
|
||
|
out:
|
||
|
arc_unlock(sc);
|
||
|
free(volinfo, M_TEMP, sizeof *volinfo);
|
||
|
free(sysinfo, M_TEMP, sizeof *sysinfo);
|
||
|
return (error);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_bio_blink(struct arc_softc *sc, struct bioc_blink *blink)
|
||
|
{
|
||
|
u_int8_t request[6];
|
||
|
u_int32_t mask;
|
||
|
int error = 0;
|
||
|
|
||
|
DPRINTF("%s: arc_bio_blink\n", DEVNAME(sc));
|
||
|
request[0] = ARC_FW_BLINK;
|
||
|
request[1] = ARC_FW_BLINK_ENABLE;
|
||
|
|
||
|
switch (blink->bb_status) {
|
||
|
case BIOC_SBUNBLINK:
|
||
|
sc->sc_ledmask &= ~(1 << blink->bb_target);
|
||
|
break;
|
||
|
case BIOC_SBBLINK:
|
||
|
sc->sc_ledmask |= (1 << blink->bb_target);
|
||
|
break;
|
||
|
default:
|
||
|
return (EINVAL);
|
||
|
}
|
||
|
|
||
|
mask = htole32(sc->sc_ledmask);
|
||
|
bcopy(&mask, &request[2], 4);
|
||
|
|
||
|
arc_lock(sc);
|
||
|
error = arc_msgbuf(sc, request, sizeof(request), NULL, 0, 0);
|
||
|
arc_unlock(sc);
|
||
|
if (error)
|
||
|
return (EIO);
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_bio_getvol(struct arc_softc *sc, int vol, struct arc_fw_volinfo *volinfo)
|
||
|
{
|
||
|
u_int8_t request[2];
|
||
|
struct arc_fw_sysinfo *sysinfo;
|
||
|
int error = 0;
|
||
|
int maxvols, nvols = 0, i;
|
||
|
|
||
|
DPRINTF("%s: arc_bio_getvol\n", DEVNAME(sc));
|
||
|
sysinfo = malloc(sizeof(struct arc_fw_sysinfo), M_TEMP, M_WAITOK);
|
||
|
|
||
|
request[0] = ARC_FW_SYSINFO;
|
||
|
error = arc_msgbuf(sc, request, 1, sysinfo,
|
||
|
sizeof(struct arc_fw_sysinfo), 0);
|
||
|
if (error != 0)
|
||
|
goto out;
|
||
|
|
||
|
maxvols = sysinfo->max_volume_set;
|
||
|
|
||
|
request[0] = ARC_FW_VOLINFO;
|
||
|
for (i = 0; i < maxvols; i++) {
|
||
|
request[1] = i;
|
||
|
error = arc_msgbuf(sc, request, sizeof(request), volinfo,
|
||
|
sizeof(struct arc_fw_volinfo), 0);
|
||
|
if (error != 0)
|
||
|
goto out;
|
||
|
|
||
|
if ((volinfo->capacity == 0 && volinfo->capacity2 == 0) ||
|
||
|
volinfo->raid_level == ARC_FW_VOL_RAIDLEVEL_PASSTHRU)
|
||
|
continue;
|
||
|
|
||
|
if (nvols == vol)
|
||
|
break;
|
||
|
|
||
|
nvols++;
|
||
|
}
|
||
|
|
||
|
if (nvols != vol ||
|
||
|
(volinfo->capacity == 0 && volinfo->capacity2 == 0) ||
|
||
|
volinfo->raid_level == ARC_FW_VOL_RAIDLEVEL_PASSTHRU) {
|
||
|
error = ENODEV;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
out:
|
||
|
free(sysinfo, M_TEMP, sizeof *sysinfo);
|
||
|
return (error);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_bio_vol(struct arc_softc *sc, struct bioc_vol *bv)
|
||
|
{
|
||
|
struct arc_fw_volinfo *volinfo;
|
||
|
struct scsi_link *sc_link;
|
||
|
struct device *dev;
|
||
|
u_int64_t blocks;
|
||
|
u_int32_t status;
|
||
|
int error = 0;
|
||
|
|
||
|
DPRINTF("%s: arc_bio_vol\n", DEVNAME(sc));
|
||
|
volinfo = malloc(sizeof(struct arc_fw_volinfo), M_TEMP, M_WAITOK);
|
||
|
|
||
|
arc_lock(sc);
|
||
|
error = arc_bio_getvol(sc, bv->bv_volid, volinfo);
|
||
|
arc_unlock(sc);
|
||
|
|
||
|
if (error != 0)
|
||
|
goto out;
|
||
|
|
||
|
bv->bv_percent = -1;
|
||
|
bv->bv_seconds = 0;
|
||
|
|
||
|
status = letoh32(volinfo->volume_status);
|
||
|
if (status == 0x0) {
|
||
|
if (letoh32(volinfo->fail_mask) == 0x0)
|
||
|
bv->bv_status = BIOC_SVONLINE;
|
||
|
else
|
||
|
bv->bv_status = BIOC_SVDEGRADED;
|
||
|
} else if (status & ARC_FW_VOL_STATUS_NEED_REGEN)
|
||
|
bv->bv_status = BIOC_SVDEGRADED;
|
||
|
else if (status & ARC_FW_VOL_STATUS_FAILED)
|
||
|
bv->bv_status = BIOC_SVOFFLINE;
|
||
|
else if (status & ARC_FW_VOL_STATUS_INITTING) {
|
||
|
bv->bv_status = BIOC_SVBUILDING;
|
||
|
bv->bv_percent = letoh32(volinfo->progress) / 10;
|
||
|
} else if (status & ARC_FW_VOL_STATUS_REBUILDING) {
|
||
|
bv->bv_status = BIOC_SVREBUILD;
|
||
|
bv->bv_percent = letoh32(volinfo->progress) / 10;
|
||
|
}
|
||
|
|
||
|
blocks = (u_int64_t)letoh32(volinfo->capacity2) << 32;
|
||
|
blocks += (u_int64_t)letoh32(volinfo->capacity);
|
||
|
bv->bv_size = blocks * ARC_BLOCKSIZE; /* XXX */
|
||
|
|
||
|
switch (volinfo->raid_level) {
|
||
|
case ARC_FW_VOL_RAIDLEVEL_0:
|
||
|
bv->bv_level = 0;
|
||
|
break;
|
||
|
case ARC_FW_VOL_RAIDLEVEL_1:
|
||
|
bv->bv_level = 1;
|
||
|
break;
|
||
|
case ARC_FW_VOL_RAIDLEVEL_3:
|
||
|
bv->bv_level = 3;
|
||
|
break;
|
||
|
case ARC_FW_VOL_RAIDLEVEL_5:
|
||
|
bv->bv_level = 5;
|
||
|
break;
|
||
|
case ARC_FW_VOL_RAIDLEVEL_6:
|
||
|
bv->bv_level = 6;
|
||
|
break;
|
||
|
case ARC_FW_VOL_RAIDLEVEL_PASSTHRU:
|
||
|
default:
|
||
|
bv->bv_level = -1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
bv->bv_nodisk = volinfo->member_disks;
|
||
|
sc_link = scsi_get_link(sc->sc_scsibus, volinfo->scsi_attr.target,
|
||
|
volinfo->scsi_attr.lun);
|
||
|
if (sc_link != NULL) {
|
||
|
dev = sc_link->device_softc;
|
||
|
strlcpy(bv->bv_dev, dev->dv_xname, sizeof(bv->bv_dev));
|
||
|
}
|
||
|
|
||
|
out:
|
||
|
free(volinfo, M_TEMP, sizeof *volinfo);
|
||
|
return (error);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_bio_disk(struct arc_softc *sc, struct bioc_disk *bd)
|
||
|
{
|
||
|
u_int8_t request[2];
|
||
|
struct arc_fw_volinfo *volinfo;
|
||
|
struct arc_fw_raidinfo *raidinfo;
|
||
|
struct arc_fw_diskinfo *diskinfo;
|
||
|
int error = 0;
|
||
|
u_int64_t blocks;
|
||
|
char model[81];
|
||
|
char serial[41];
|
||
|
char rev[17];
|
||
|
|
||
|
DPRINTF("%s: arc_bio_disk\n", DEVNAME(sc));
|
||
|
volinfo = malloc(sizeof(struct arc_fw_volinfo), M_TEMP, M_WAITOK);
|
||
|
raidinfo = malloc(sizeof(struct arc_fw_raidinfo), M_TEMP, M_WAITOK);
|
||
|
diskinfo = malloc(sizeof(struct arc_fw_diskinfo), M_TEMP, M_WAITOK);
|
||
|
|
||
|
arc_lock(sc);
|
||
|
|
||
|
error = arc_bio_getvol(sc, bd->bd_volid, volinfo);
|
||
|
if (error != 0)
|
||
|
goto out;
|
||
|
|
||
|
request[0] = ARC_FW_RAIDINFO;
|
||
|
request[1] = volinfo->raid_set_number;
|
||
|
error = arc_msgbuf(sc, request, sizeof(request), raidinfo,
|
||
|
sizeof(struct arc_fw_raidinfo), 0);
|
||
|
if (error != 0)
|
||
|
goto out;
|
||
|
|
||
|
if (bd->bd_diskid > raidinfo->member_devices) {
|
||
|
error = ENODEV;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
if (raidinfo->device_array[bd->bd_diskid] == 0xff) {
|
||
|
/*
|
||
|
* the disk doesn't exist anymore. bio is too dumb to be
|
||
|
* able to display that, so put it on another bus
|
||
|
*/
|
||
|
bd->bd_channel = 1;
|
||
|
bd->bd_target = 0;
|
||
|
bd->bd_lun = 0;
|
||
|
bd->bd_status = BIOC_SDOFFLINE;
|
||
|
strlcpy(bd->bd_vendor, "disk missing", sizeof(bd->bd_vendor));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
request[0] = ARC_FW_DISKINFO;
|
||
|
request[1] = raidinfo->device_array[bd->bd_diskid];
|
||
|
error = arc_msgbuf(sc, request, sizeof(request), diskinfo,
|
||
|
sizeof(struct arc_fw_diskinfo), 1);
|
||
|
if (error != 0)
|
||
|
goto out;
|
||
|
|
||
|
#if 0
|
||
|
bd->bd_channel = diskinfo->scsi_attr.channel;
|
||
|
bd->bd_target = diskinfo->scsi_attr.target;
|
||
|
bd->bd_lun = diskinfo->scsi_attr.lun;
|
||
|
#endif
|
||
|
/*
|
||
|
* the firmware doesnt seem to fill scsi_attr in, so fake it with
|
||
|
* the diskid.
|
||
|
*/
|
||
|
bd->bd_channel = 0;
|
||
|
bd->bd_target = raidinfo->device_array[bd->bd_diskid];
|
||
|
bd->bd_lun = 0;
|
||
|
|
||
|
bd->bd_status = BIOC_SDONLINE;
|
||
|
blocks = (u_int64_t)letoh32(diskinfo->capacity2) << 32;
|
||
|
blocks += (u_int64_t)letoh32(diskinfo->capacity);
|
||
|
bd->bd_size = blocks * ARC_BLOCKSIZE; /* XXX */
|
||
|
|
||
|
scsi_strvis(model, diskinfo->model, sizeof(diskinfo->model));
|
||
|
scsi_strvis(serial, diskinfo->serial, sizeof(diskinfo->serial));
|
||
|
scsi_strvis(rev, diskinfo->firmware_rev,
|
||
|
sizeof(diskinfo->firmware_rev));
|
||
|
|
||
|
snprintf(bd->bd_vendor, sizeof(bd->bd_vendor), "%s %s",
|
||
|
model, rev);
|
||
|
strlcpy(bd->bd_serial, serial, sizeof(bd->bd_serial));
|
||
|
|
||
|
out:
|
||
|
arc_unlock(sc);
|
||
|
free(diskinfo, M_TEMP, sizeof *diskinfo);
|
||
|
free(raidinfo, M_TEMP, sizeof *raidinfo);
|
||
|
free(volinfo, M_TEMP, sizeof *volinfo);
|
||
|
return (error);
|
||
|
}
|
||
|
|
||
|
u_int8_t
|
||
|
arc_msg_cksum(void *cmd, u_int16_t len)
|
||
|
{
|
||
|
u_int8_t *buf = cmd;
|
||
|
u_int8_t cksum;
|
||
|
int i;
|
||
|
|
||
|
cksum = (u_int8_t)(len >> 8) + (u_int8_t)len;
|
||
|
for (i = 0; i < len; i++)
|
||
|
cksum += buf[i];
|
||
|
|
||
|
return (cksum);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_msgbuf(struct arc_softc *sc, void *wptr, size_t wbuflen, void *rptr,
|
||
|
size_t rbuflen, int sreadok)
|
||
|
{
|
||
|
u_int8_t rwbuf[ARC_RA_IOC_RWBUF_MAXLEN];
|
||
|
u_int8_t *wbuf, *rbuf, cksum;
|
||
|
int wlen, wdone = 0, rlen, rdone = 0;
|
||
|
u_int16_t rlenhdr = 0;
|
||
|
struct arc_fw_bufhdr *bufhdr;
|
||
|
u_int32_t reg, rwlen, write_ok, read_ok;
|
||
|
int error = 0;
|
||
|
#ifdef ARC_DEBUG
|
||
|
int i;
|
||
|
#endif
|
||
|
|
||
|
DPRINTF("%s: arc_msgbuf wbuflen: %zu rbuflen: %zu\n",
|
||
|
DEVNAME(sc), wbuflen, rbuflen);
|
||
|
|
||
|
switch(sc->sc_adp_type) {
|
||
|
case ARC_HBA_TYPE_A:
|
||
|
reg = arc_read(sc, ARC_RA_OUTB_DOORBELL);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_C:
|
||
|
reg = arc_read(sc, ARC_RC_OUTB_DOORBELL);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_D:
|
||
|
reg = arc_read(sc, ARC_RD_OUTB_DOORBELL);
|
||
|
break;
|
||
|
}
|
||
|
/* if (reg)
|
||
|
return (EBUSY); */
|
||
|
|
||
|
wlen = sizeof(struct arc_fw_bufhdr) + wbuflen + 1; /* 1 for cksum */
|
||
|
wbuf = malloc(wlen, M_TEMP, M_WAITOK);
|
||
|
|
||
|
rlen = sizeof(struct arc_fw_bufhdr) + rbuflen + 1; /* 1 for cksum */
|
||
|
rbuf = malloc(rlen, M_TEMP, M_WAITOK);
|
||
|
|
||
|
DNPRINTF(ARC_D_DB, "%s: arc_msgbuf wlen: %d rlen: %d\n", DEVNAME(sc),
|
||
|
wlen, rlen);
|
||
|
|
||
|
bufhdr = (struct arc_fw_bufhdr *)wbuf;
|
||
|
bufhdr->hdr = arc_fw_hdr;
|
||
|
bufhdr->len = htole16(wbuflen);
|
||
|
bcopy(wptr, wbuf + sizeof(struct arc_fw_bufhdr), wbuflen);
|
||
|
wbuf[wlen - 1] = arc_msg_cksum(wptr, wbuflen);
|
||
|
|
||
|
/* reg = ARC_RA_OUTB_DOORBELL_READ_OK; */
|
||
|
read_ok = 1;
|
||
|
do {
|
||
|
if ((read_ok) && wdone < wlen) {
|
||
|
bzero(rwbuf, sizeof(rwbuf));
|
||
|
rwlen = (wlen - wdone) % sizeof(rwbuf);
|
||
|
bcopy(&wbuf[wdone], rwbuf, rwlen);
|
||
|
|
||
|
#ifdef ARC_DEBUG
|
||
|
if (arcdebug & ARC_D_DB) {
|
||
|
printf("%s: write %d:", DEVNAME(sc), rwlen);
|
||
|
for (i = 0; i < rwlen; i++)
|
||
|
printf(" 0x%02x", rwbuf[i]);
|
||
|
printf("\n");
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
switch(sc->sc_adp_type) {
|
||
|
case ARC_HBA_TYPE_A:
|
||
|
/* copy the chunk to the hw */
|
||
|
arc_write(sc, ARC_RA_IOC_WBUF_LEN, rwlen);
|
||
|
arc_write_region(sc, ARC_RA_IOC_WBUF, rwbuf,
|
||
|
sizeof(rwbuf));
|
||
|
|
||
|
/* say we have a buffer for the hw */
|
||
|
arc_write(sc, ARC_RA_INB_DOORBELL,
|
||
|
ARC_RA_INB_DOORBELL_WRITE_OK);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_C:
|
||
|
/* copy the chunk to the hw */
|
||
|
arc_write(sc, ARC_RC_MSG_WBUF_LEN, rwlen);
|
||
|
arc_write_region(sc, ARC_RC_MSG_WBUF, rwbuf,
|
||
|
sizeof(rwbuf));
|
||
|
|
||
|
/* say we have a buffer for the hw */
|
||
|
arc_write(sc, ARC_RC_INB_DOORBELL,
|
||
|
ARC_RC_D2I_DATA_WRITE_OK);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_D:
|
||
|
/* copy the chunk to the hw */
|
||
|
arc_write(sc, ARC_RD_MSG_WBUF_LEN, rwlen);
|
||
|
arc_write_region(sc, ARC_RD_MSG_WBUF, rwbuf,
|
||
|
sizeof(rwbuf));
|
||
|
|
||
|
/* say we have a buffer for the hw */
|
||
|
arc_write(sc, ARC_RD_INB_DOORBELL,
|
||
|
ARC_RD_D2I_DATA_WRITE_OK);
|
||
|
break;
|
||
|
}
|
||
|
wdone += rwlen;
|
||
|
}
|
||
|
|
||
|
if (rptr == NULL)
|
||
|
goto out;
|
||
|
|
||
|
switch(sc->sc_adp_type) {
|
||
|
case ARC_HBA_TYPE_A:
|
||
|
while ((reg = arc_read(sc, ARC_RA_OUTB_DOORBELL)) == 0)
|
||
|
arc_wait(sc);
|
||
|
arc_write(sc, ARC_RA_OUTB_DOORBELL, reg);
|
||
|
write_ok = reg & ARC_RA_OUTB_DOORBELL_WRITE_OK;
|
||
|
read_ok = reg & ARC_RA_OUTB_DOORBELL_READ_OK;
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_C:
|
||
|
while ((reg = arc_read(sc, ARC_RC_OUTB_DOORBELL)) == 0)
|
||
|
arc_wait(sc);
|
||
|
arc_write(sc, ARC_RC_OUTB_DOORBELL_CLR, reg);
|
||
|
write_ok = reg & ARC_RC_I2D_DATA_WRITE_OK;
|
||
|
read_ok = reg & ARC_RC_I2D_DATA_READ_OK;
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_D:
|
||
|
while ((reg = arc_read(sc, ARC_RD_OUTB_DOORBELL)) == 0)
|
||
|
arc_wait(sc);
|
||
|
arc_write(sc, ARC_RD_OUTB_DOORBELL_CLR, reg);
|
||
|
write_ok = reg & ARC_RD_I2D_DATA_WRITE_OK;
|
||
|
read_ok = reg & ARC_RD_I2D_DATA_READ_OK;
|
||
|
break;
|
||
|
}
|
||
|
DNPRINTF(ARC_D_DB, "%s: reg: 0x%08x\n", DEVNAME(sc), reg);
|
||
|
|
||
|
if ((write_ok) && rdone < rlen) {
|
||
|
switch(sc->sc_adp_type) {
|
||
|
case ARC_HBA_TYPE_A:
|
||
|
rwlen = arc_read(sc, ARC_RA_IOC_RBUF_LEN);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_C:
|
||
|
rwlen = arc_read(sc, ARC_RC_MSG_RBUF_LEN);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_D:
|
||
|
rwlen = arc_read(sc, ARC_RD_MSG_RBUF_LEN);
|
||
|
break;
|
||
|
}
|
||
|
if (rwlen > sizeof(rwbuf)) {
|
||
|
DNPRINTF(ARC_D_DB, "%s: rwlen too big\n",
|
||
|
DEVNAME(sc));
|
||
|
error = EIO;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
switch(sc->sc_adp_type) {
|
||
|
case ARC_HBA_TYPE_A:
|
||
|
arc_read_region(sc, ARC_RA_IOC_RBUF, rwbuf,
|
||
|
sizeof(rwbuf));
|
||
|
arc_write(sc, ARC_RA_INB_DOORBELL,
|
||
|
ARC_RA_INB_DOORBELL_READ_OK);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_C:
|
||
|
arc_read_region(sc, ARC_RC_MSG_RBUF, rwbuf,
|
||
|
sizeof(rwbuf));
|
||
|
arc_write(sc, ARC_RC_INB_DOORBELL,
|
||
|
ARC_RC_I2D_DATA_READ_OK);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_D:
|
||
|
arc_read_region(sc, ARC_RD_MSG_RBUF, rwbuf,
|
||
|
sizeof(rwbuf));
|
||
|
arc_write(sc, ARC_RD_INB_DOORBELL,
|
||
|
ARC_RD_I2D_DATA_READ_OK);
|
||
|
break;
|
||
|
}
|
||
|
if ((rlen > 3) && (rdone == 3)) {
|
||
|
rlen = *(u_int16_t *)rwbuf;
|
||
|
rlen = sizeof(struct arc_fw_bufhdr) + rlen + 1;
|
||
|
}
|
||
|
#ifdef ARC_DEBUG
|
||
|
printf("%s: len: %d+%d=%d/%d\n", DEVNAME(sc),
|
||
|
rwlen, rdone, rwlen + rdone, rlen);
|
||
|
if (arcdebug & ARC_D_DB) {
|
||
|
printf("%s: read:", DEVNAME(sc));
|
||
|
for (i = 0; i < rwlen; i++)
|
||
|
printf(" 0x%02x", rwbuf[i]);
|
||
|
printf("\n");
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if ((rdone + rwlen) > rlen) {
|
||
|
DNPRINTF(ARC_D_DB, "%s: rwbuf too big\n",
|
||
|
DEVNAME(sc));
|
||
|
error = EIO;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
bcopy(rwbuf, &rbuf[rdone], rwlen);
|
||
|
rdone += rwlen;
|
||
|
|
||
|
/*
|
||
|
* Allow for short reads, by reading the length
|
||
|
* value from the response header and shrinking our
|
||
|
* idea of size, if required.
|
||
|
* This deals with the growth of diskinfo struct from
|
||
|
* 128 to 132 bytes.
|
||
|
*/
|
||
|
if (sreadok && rdone >= sizeof(struct arc_fw_bufhdr) &&
|
||
|
rlenhdr == 0) {
|
||
|
bufhdr = (struct arc_fw_bufhdr *)rbuf;
|
||
|
rlenhdr = letoh16(bufhdr->len);
|
||
|
if (rlenhdr < rbuflen) {
|
||
|
rbuflen = rlenhdr;
|
||
|
rlen = sizeof(struct arc_fw_bufhdr) +
|
||
|
rbuflen + 1; /* 1 for cksum */
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} while (rdone != rlen);
|
||
|
|
||
|
bufhdr = (struct arc_fw_bufhdr *)rbuf;
|
||
|
if (memcmp(&bufhdr->hdr, &arc_fw_hdr, sizeof(bufhdr->hdr)) != 0) {
|
||
|
DNPRINTF(ARC_D_DB, "%s: rbuf hdr is wrong\n", DEVNAME(sc));
|
||
|
error = EIO;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
if (bufhdr->len != htole16(rbuflen)) {
|
||
|
DNPRINTF(ARC_D_DB, "%s: get_len: 0x%x, req_len: 0x%zu\n",
|
||
|
DEVNAME(sc), bufhdr->len, rbuflen);
|
||
|
}
|
||
|
|
||
|
bcopy(rbuf + sizeof(struct arc_fw_bufhdr), rptr, bufhdr->len);
|
||
|
cksum = arc_msg_cksum(rptr, bufhdr->len);
|
||
|
if (rbuf[rlen - 1] != cksum) {
|
||
|
DNPRINTF(ARC_D_DB, "%s: invalid cksum, got :0x%x, calculated:"
|
||
|
" 0x%x\n", DEVNAME(sc), rbuf[rlen-1], cksum);
|
||
|
error = EIO;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
out:
|
||
|
free(wbuf, M_TEMP, 0);
|
||
|
free(rbuf, M_TEMP, 0);
|
||
|
|
||
|
return (error);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
arc_lock(struct arc_softc *sc)
|
||
|
{
|
||
|
int s;
|
||
|
u_int32_t int_mask;
|
||
|
|
||
|
rw_enter_write(&sc->sc_lock);
|
||
|
s = splbio();
|
||
|
switch(sc->sc_adp_type) {
|
||
|
case ARC_HBA_TYPE_A:
|
||
|
int_mask = arc_read(sc, ARC_RA_INTRMASK) | ARC_RA_INTRMASK_DOORBELL;
|
||
|
arc_write(sc, ARC_RA_INTRMASK, int_mask);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_C:
|
||
|
int_mask = arc_read(sc, ARC_RC_INTR_MASK) | ARC_RC_INTR_MASK_DOORBELL;
|
||
|
arc_write(sc, ARC_RC_INTR_MASK, int_mask);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_D:
|
||
|
int_mask = arc_read(sc, ARC_RD_INTR_ENABLE) & ~ARC_RD_INTR_ENABLE_DOORBELL;
|
||
|
arc_write(sc, ARC_RD_INTR_ENABLE, int_mask);
|
||
|
break;
|
||
|
}
|
||
|
sc->sc_talking = 1;
|
||
|
splx(s);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
arc_unlock(struct arc_softc *sc)
|
||
|
{
|
||
|
int s;
|
||
|
u_int32_t int_mask;
|
||
|
|
||
|
s = splbio();
|
||
|
sc->sc_talking = 0;
|
||
|
switch(sc->sc_adp_type) {
|
||
|
case ARC_HBA_TYPE_A:
|
||
|
int_mask = arc_read(sc, ARC_RA_INTRMASK) & ~ARC_RA_INTRMASK_DOORBELL;
|
||
|
arc_write(sc, ARC_RA_INTRMASK, int_mask);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_C:
|
||
|
int_mask = arc_read(sc, ARC_RC_INTR_MASK) & ~ARC_RC_INTR_MASK_DOORBELL;
|
||
|
arc_write(sc, ARC_RC_INTR_MASK, int_mask);
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_D:
|
||
|
int_mask = arc_read(sc, ARC_RD_INTR_ENABLE) | ARC_RD_INTR_ENABLE_DOORBELL;
|
||
|
arc_write(sc, ARC_RD_INTR_ENABLE, int_mask);
|
||
|
break;
|
||
|
}
|
||
|
splx(s);
|
||
|
rw_exit_write(&sc->sc_lock);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
arc_wait(struct arc_softc *sc)
|
||
|
{
|
||
|
int error, s;
|
||
|
u_int32_t int_mask;
|
||
|
|
||
|
s = splbio();
|
||
|
switch(sc->sc_adp_type) {
|
||
|
case ARC_HBA_TYPE_A:
|
||
|
int_mask = arc_read(sc, ARC_RA_INTRMASK) & ~ARC_RA_INTRMASK_DOORBELL;
|
||
|
arc_write(sc, ARC_RA_INTRMASK, int_mask);
|
||
|
error = tsleep_nsec(sc, PWAIT, "arcdb", SEC_TO_NSEC(1));
|
||
|
if (error == EWOULDBLOCK) {
|
||
|
int_mask = arc_read(sc, ARC_RA_INTRMASK) | ARC_RA_INTRMASK_DOORBELL;
|
||
|
arc_write(sc, ARC_RA_INTRMASK, int_mask);
|
||
|
}
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_C:
|
||
|
int_mask = arc_read(sc, ARC_RC_INTR_MASK) & ~ARC_RC_INTR_MASK_DOORBELL;
|
||
|
arc_write(sc, ARC_RC_INTR_MASK, int_mask);
|
||
|
error = tsleep_nsec(sc, PWAIT, "arcdb", SEC_TO_NSEC(1));
|
||
|
if (error == EWOULDBLOCK) {
|
||
|
int_mask = arc_read(sc, ARC_RC_INTR_MASK) | ARC_RC_INTR_MASK_DOORBELL;
|
||
|
arc_write(sc, ARC_RC_INTR_MASK, int_mask);
|
||
|
}
|
||
|
break;
|
||
|
case ARC_HBA_TYPE_D:
|
||
|
int_mask = arc_read(sc, ARC_RD_INTR_ENABLE) | ARC_RD_INTR_ENABLE_DOORBELL;
|
||
|
arc_write(sc, ARC_RD_INTR_ENABLE, int_mask);
|
||
|
error = tsleep_nsec(sc, PWAIT, "arcdb", SEC_TO_NSEC(1));
|
||
|
if (error == EWOULDBLOCK) {
|
||
|
int_mask = arc_read(sc, ARC_RD_INTR_ENABLE) & ~ARC_RD_INTR_ENABLE_DOORBELL;
|
||
|
arc_write(sc, ARC_RD_INTR_ENABLE, int_mask);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
splx(s);
|
||
|
}
|
||
|
|
||
|
#ifndef SMALL_KERNEL
|
||
|
void
|
||
|
arc_create_sensors(void *xat)
|
||
|
{
|
||
|
struct arc_task *at = xat;
|
||
|
struct arc_softc *sc = at->sc;
|
||
|
struct bioc_inq bi;
|
||
|
struct bioc_vol bv;
|
||
|
int i;
|
||
|
|
||
|
free(at, M_TEMP, sizeof(*at));
|
||
|
|
||
|
DPRINTF("%s: arc_create_sensors\n", DEVNAME(sc));
|
||
|
/*
|
||
|
* XXX * this is bollocks. the firmware has garbage coming out of it
|
||
|
* so we have to wait a bit for it to finish spewing.
|
||
|
*/
|
||
|
tsleep_nsec(sc, PWAIT, "arcspew", SEC_TO_NSEC(2));
|
||
|
|
||
|
bzero(&bi, sizeof(bi));
|
||
|
if (arc_bio_inq(sc, &bi) != 0) {
|
||
|
printf("%s: unable to query firmware for sensor info\n",
|
||
|
DEVNAME(sc));
|
||
|
return;
|
||
|
}
|
||
|
sc->sc_nsensors = bi.bi_novol;
|
||
|
|
||
|
sc->sc_sensors = mallocarray(sc->sc_nsensors, sizeof(struct ksensor),
|
||
|
M_DEVBUF, M_WAITOK | M_ZERO);
|
||
|
|
||
|
strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
|
||
|
sizeof(sc->sc_sensordev.xname));
|
||
|
|
||
|
for (i = 0; i < sc->sc_nsensors; i++) {
|
||
|
bzero(&bv, sizeof(bv));
|
||
|
bv.bv_volid = i;
|
||
|
if (arc_bio_vol(sc, &bv) != 0) {
|
||
|
DPRINTF("%s: arc_bio_vol failed!\n", DEVNAME(sc));
|
||
|
goto bad;
|
||
|
}
|
||
|
|
||
|
sc->sc_sensors[i].type = SENSOR_DRIVE;
|
||
|
sc->sc_sensors[i].status = SENSOR_S_UNKNOWN;
|
||
|
|
||
|
strlcpy(sc->sc_sensors[i].desc, bv.bv_dev,
|
||
|
sizeof(sc->sc_sensors[i].desc));
|
||
|
|
||
|
sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
|
||
|
}
|
||
|
|
||
|
if (sensor_task_register(sc, arc_refresh_sensors, 120) == NULL) {
|
||
|
DPRINTF("%s: sensor_task_register failed!\n", DEVNAME(sc));
|
||
|
goto bad;
|
||
|
}
|
||
|
|
||
|
sensordev_install(&sc->sc_sensordev);
|
||
|
|
||
|
return;
|
||
|
|
||
|
bad:
|
||
|
free(sc->sc_sensors, M_DEVBUF,
|
||
|
sc->sc_nsensors * sizeof(struct ksensor));
|
||
|
}
|
||
|
|
||
|
void
|
||
|
arc_refresh_sensors(void *arg)
|
||
|
{
|
||
|
struct arc_softc *sc = arg;
|
||
|
struct bioc_vol bv;
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < sc->sc_nsensors; i++) {
|
||
|
bzero(&bv, sizeof(bv));
|
||
|
bv.bv_volid = i;
|
||
|
if (arc_bio_vol(sc, &bv)) {
|
||
|
sc->sc_sensors[i].flags = SENSOR_FINVALID;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
switch(bv.bv_status) {
|
||
|
case BIOC_SVOFFLINE:
|
||
|
sc->sc_sensors[i].value = SENSOR_DRIVE_FAIL;
|
||
|
sc->sc_sensors[i].status = SENSOR_S_CRIT;
|
||
|
break;
|
||
|
|
||
|
case BIOC_SVDEGRADED:
|
||
|
sc->sc_sensors[i].value = SENSOR_DRIVE_PFAIL;
|
||
|
sc->sc_sensors[i].status = SENSOR_S_WARN;
|
||
|
break;
|
||
|
|
||
|
case BIOC_SVSCRUB:
|
||
|
case BIOC_SVONLINE:
|
||
|
sc->sc_sensors[i].value = SENSOR_DRIVE_ONLINE;
|
||
|
sc->sc_sensors[i].status = SENSOR_S_OK;
|
||
|
break;
|
||
|
|
||
|
case BIOC_SVINVALID:
|
||
|
/* FALLTHROUGH */
|
||
|
default:
|
||
|
sc->sc_sensors[i].value = 0; /* unknown */
|
||
|
sc->sc_sensors[i].status = SENSOR_S_UNKNOWN;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
#endif /* SMALL_KERNEL */
|
||
|
#endif /* NBIO > 0 */
|
||
|
|
||
|
u_int32_t
|
||
|
arc_read(struct arc_softc *sc, bus_size_t r)
|
||
|
{
|
||
|
u_int32_t v;
|
||
|
|
||
|
bus_space_barrier(sc->sc_iot, sc->sc_ioh, r, 4,
|
||
|
BUS_SPACE_BARRIER_READ);
|
||
|
v = bus_space_read_4(sc->sc_iot, sc->sc_ioh, r);
|
||
|
|
||
|
DNPRINTF(ARC_D_RW, "%s: arc_read 0x%lx 0x%08x\n", DEVNAME(sc), r, v);
|
||
|
|
||
|
return (v);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
arc_read_region(struct arc_softc *sc, bus_size_t r, void *buf, size_t len)
|
||
|
{
|
||
|
bus_space_barrier(sc->sc_iot, sc->sc_ioh, r, len,
|
||
|
BUS_SPACE_BARRIER_READ);
|
||
|
bus_space_read_raw_region_4(sc->sc_iot, sc->sc_ioh, r, buf, len);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
arc_write(struct arc_softc *sc, bus_size_t r, u_int32_t v)
|
||
|
{
|
||
|
DNPRINTF(ARC_D_RW, "%s: arc_write 0x%lx 0x%08x\n", DEVNAME(sc), r, v);
|
||
|
|
||
|
bus_space_write_4(sc->sc_iot, sc->sc_ioh, r, v);
|
||
|
bus_space_barrier(sc->sc_iot, sc->sc_ioh, r, 4,
|
||
|
BUS_SPACE_BARRIER_WRITE);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
arc_write_region(struct arc_softc *sc, bus_size_t r, void *buf, size_t len)
|
||
|
{
|
||
|
bus_space_write_raw_region_4(sc->sc_iot, sc->sc_ioh, r, buf, len);
|
||
|
bus_space_barrier(sc->sc_iot, sc->sc_ioh, r, len,
|
||
|
BUS_SPACE_BARRIER_WRITE);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_wait_eq(struct arc_softc *sc, bus_size_t r, u_int32_t mask,
|
||
|
u_int32_t target)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
DNPRINTF(ARC_D_RW, "%s: arc_wait_eq 0x%lx 0x%08x 0x%08x\n",
|
||
|
DEVNAME(sc), r, mask, target);
|
||
|
|
||
|
for (i = 0; i < 10000; i++) {
|
||
|
if ((arc_read(sc, r) & mask) == target)
|
||
|
return (0);
|
||
|
delay(1000);
|
||
|
}
|
||
|
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_wait_ne(struct arc_softc *sc, bus_size_t r, u_int32_t mask,
|
||
|
u_int32_t target)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
DNPRINTF(ARC_D_RW, "%s: arc_wait_ne 0x%lx 0x%08x 0x%08x\n",
|
||
|
DEVNAME(sc), r, mask, target);
|
||
|
|
||
|
for (i = 0; i < 10000; i++) {
|
||
|
if ((arc_read(sc, r) & mask) != target)
|
||
|
return (0);
|
||
|
delay(1000);
|
||
|
}
|
||
|
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_msg0(struct arc_softc *sc, u_int32_t m)
|
||
|
{
|
||
|
switch(sc->sc_adp_type) {
|
||
|
case ARC_HBA_TYPE_A:
|
||
|
/* post message */
|
||
|
arc_write(sc, ARC_RA_INB_MSG0, m);
|
||
|
/* wait for the fw to do it */
|
||
|
if (arc_wait_eq(sc, ARC_RA_INTRSTAT, ARC_RA_INTRSTAT_MSG0,
|
||
|
ARC_RA_INTRSTAT_MSG0) != 0)
|
||
|
return (1);
|
||
|
|
||
|
/* ack it */
|
||
|
arc_write(sc, ARC_RA_INTRSTAT, ARC_RA_INTRSTAT_MSG0);
|
||
|
break;
|
||
|
|
||
|
case ARC_HBA_TYPE_C:
|
||
|
/* post message */
|
||
|
arc_write(sc, ARC_RC_INB_MSGADDR0, m);
|
||
|
arc_write(sc, ARC_RC_INB_DOORBELL, ARC_RC_D2I_MSG_CMD_DONE);
|
||
|
/* wait for the fw to do it */
|
||
|
if (arc_wait_eq(sc, ARC_RC_OUTB_DOORBELL, ARC_RC_I2D_MSG_CMD_DONE,
|
||
|
ARC_RC_I2D_MSG_CMD_DONE) != 0)
|
||
|
return (1);
|
||
|
|
||
|
/* ack it */
|
||
|
arc_write(sc, ARC_RC_OUTB_DOORBELL_CLR, ARC_RC_I2D_MSG_CMD_DONE_CLR);
|
||
|
break;
|
||
|
|
||
|
case ARC_HBA_TYPE_D:
|
||
|
/* post message */
|
||
|
arc_write(sc, ARC_RD_INB_MSGADDR0, m);
|
||
|
/* wait for the fw to do it */
|
||
|
if (arc_wait_eq(sc, ARC_RD_OUTB_DOORBELL, ARC_RD_I2D_MSG_CMD_DONE,
|
||
|
ARC_RD_I2D_MSG_CMD_DONE) != 0)
|
||
|
return (1);
|
||
|
|
||
|
/* ack it */
|
||
|
arc_write(sc, ARC_RD_OUTB_DOORBELL_CLR, ARC_RD_I2D_MSG_CMD_DONE_CLR);
|
||
|
break;
|
||
|
}
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
struct arc_dmamem *
|
||
|
arc_dmamem_alloc(struct arc_softc *sc, size_t size)
|
||
|
{
|
||
|
struct arc_dmamem *adm;
|
||
|
int nsegs;
|
||
|
|
||
|
adm = malloc(sizeof(*adm), M_DEVBUF, M_NOWAIT | M_ZERO);
|
||
|
if (adm == NULL)
|
||
|
return (NULL);
|
||
|
|
||
|
adm->adm_size = size;
|
||
|
|
||
|
if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
|
||
|
BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &adm->adm_map) != 0)
|
||
|
goto admfree;
|
||
|
|
||
|
if (bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &adm->adm_seg,
|
||
|
1, &nsegs, BUS_DMA_NOWAIT | BUS_DMA_ZERO) != 0)
|
||
|
goto destroy;
|
||
|
|
||
|
if (bus_dmamem_map(sc->sc_dmat, &adm->adm_seg, nsegs, size,
|
||
|
&adm->adm_kva, BUS_DMA_NOWAIT) != 0)
|
||
|
goto free;
|
||
|
|
||
|
if (bus_dmamap_load(sc->sc_dmat, adm->adm_map, adm->adm_kva, size,
|
||
|
NULL, BUS_DMA_NOWAIT) != 0)
|
||
|
goto unmap;
|
||
|
|
||
|
return (adm);
|
||
|
|
||
|
unmap:
|
||
|
bus_dmamem_unmap(sc->sc_dmat, adm->adm_kva, size);
|
||
|
free:
|
||
|
bus_dmamem_free(sc->sc_dmat, &adm->adm_seg, 1);
|
||
|
destroy:
|
||
|
bus_dmamap_destroy(sc->sc_dmat, adm->adm_map);
|
||
|
admfree:
|
||
|
free(adm, M_DEVBUF, sizeof *adm);
|
||
|
|
||
|
return (NULL);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
arc_dmamem_free(struct arc_softc *sc, struct arc_dmamem *adm)
|
||
|
{
|
||
|
bus_dmamap_unload(sc->sc_dmat, adm->adm_map);
|
||
|
bus_dmamem_unmap(sc->sc_dmat, adm->adm_kva, adm->adm_size);
|
||
|
bus_dmamem_free(sc->sc_dmat, &adm->adm_seg, 1);
|
||
|
bus_dmamap_destroy(sc->sc_dmat, adm->adm_map);
|
||
|
free(adm, M_DEVBUF, sizeof *adm);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
arc_alloc_ccbs(struct arc_softc *sc)
|
||
|
{
|
||
|
struct arc_ccb *ccb;
|
||
|
u_int8_t *cmd;
|
||
|
u_int32_t i, size, len;
|
||
|
|
||
|
SLIST_INIT(&sc->sc_ccb_free);
|
||
|
mtx_init(&sc->sc_ccb_mtx, IPL_BIO);
|
||
|
|
||
|
size = sizeof(struct arc_ccb) * ARCMSR_MAX_CCB_COUNT;
|
||
|
sc->sc_ccbs = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
|
||
|
|
||
|
len = ARC_IO_CMD_LEN;
|
||
|
size = ARCMSR_MAX_CCB_COUNT * len;
|
||
|
if(sc->sc_adp_type == ARC_HBA_TYPE_D)
|
||
|
size += sizeof(struct arc_HBD_Msgu);
|
||
|
sc->sc_requests = arc_dmamem_alloc(sc, size);
|
||
|
if (sc->sc_requests == NULL) {
|
||
|
printf("%s: unable to allocate ccb dmamem\n", DEVNAME(sc));
|
||
|
goto free_ccbs;
|
||
|
}
|
||
|
cmd = ARC_DMA_KVA(sc->sc_requests);
|
||
|
|
||
|
for (i = 0; i < ARCMSR_MAX_CCB_COUNT; i++) {
|
||
|
ccb = &sc->sc_ccbs[i];
|
||
|
|
||
|
if (bus_dmamap_create(sc->sc_dmat, MAXPHYS, ARC_SGL_MAXLEN,
|
||
|
MAXPHYS, 0, 0, &ccb->ccb_dmamap) != 0) {
|
||
|
printf("%s: unable to create dmamap for ccb %d\n",
|
||
|
DEVNAME(sc), i);
|
||
|
goto free_maps;
|
||
|
}
|
||
|
|
||
|
ccb->ccb_sc = sc;
|
||
|
ccb->cmd_dma_offset = len * i;
|
||
|
|
||
|
ccb->ccb_cmd = (struct arc_io_cmd *)&cmd[ccb->cmd_dma_offset];
|
||
|
ccb->ccb_cmd_post = (ARC_DMA_DVA(sc->sc_requests) +
|
||
|
ccb->cmd_dma_offset);
|
||
|
if ((sc->sc_adp_type != ARC_HBA_TYPE_C) &&
|
||
|
(sc->sc_adp_type != ARC_HBA_TYPE_D))
|
||
|
ccb->ccb_cmd_post = ccb->ccb_cmd_post >>
|
||
|
ARC_RA_POST_QUEUE_ADDR_SHIFT;
|
||
|
arc_put_ccb(sc, ccb);
|
||
|
}
|
||
|
sc->sc_ccb_phys_hi = (u_int64_t)ARC_DMA_DVA(sc->sc_requests) >> 32;
|
||
|
if(sc->sc_adp_type == ARC_HBA_TYPE_D) {
|
||
|
sc->postQ_buffer = ARC_DMA_DVA(sc->sc_requests) +
|
||
|
(ARCMSR_MAX_CCB_COUNT * len);
|
||
|
sc->doneQ_buffer = sc->postQ_buffer + (sizeof(struct InBound_SRB) *
|
||
|
ARCMSR_MAX_HBD_POSTQUEUE);
|
||
|
sc->pmu = (struct arc_HBD_Msgu *)&cmd[ARCMSR_MAX_CCB_COUNT * len];
|
||
|
sc->cmdQ_ptr_offset = ARCMSR_MAX_CCB_COUNT * len;
|
||
|
}
|
||
|
scsi_iopool_init(&sc->sc_iopool, sc,
|
||
|
(void *(*)(void *))arc_get_ccb,
|
||
|
(void (*)(void *, void *))arc_put_ccb);
|
||
|
|
||
|
return (0);
|
||
|
|
||
|
free_maps:
|
||
|
while ((ccb = arc_get_ccb(sc)) != NULL)
|
||
|
bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap);
|
||
|
arc_dmamem_free(sc, sc->sc_requests);
|
||
|
|
||
|
free_ccbs:
|
||
|
free(sc->sc_ccbs, M_DEVBUF, sizeof(struct arc_ccb) * ARCMSR_MAX_CCB_COUNT);
|
||
|
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
arc_free_ccb_src(struct arc_softc *sc)
|
||
|
{
|
||
|
struct arc_ccb *ccb;
|
||
|
|
||
|
while ((ccb = arc_get_ccb(sc)) != NULL)
|
||
|
bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap);
|
||
|
arc_dmamem_free(sc, sc->sc_requests);
|
||
|
free(sc->sc_ccbs, M_DEVBUF, 0);
|
||
|
}
|
||
|
|
||
|
struct arc_ccb *
|
||
|
arc_get_ccb(struct arc_softc *sc)
|
||
|
{
|
||
|
struct arc_ccb *ccb;
|
||
|
|
||
|
mtx_enter(&sc->sc_ccb_mtx);
|
||
|
ccb = SLIST_FIRST(&sc->sc_ccb_free);
|
||
|
if (ccb != NULL)
|
||
|
SLIST_REMOVE_HEAD(&sc->sc_ccb_free, ccb_link);
|
||
|
mtx_leave(&sc->sc_ccb_mtx);
|
||
|
|
||
|
return (ccb);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
arc_put_ccb(struct arc_softc *sc, struct arc_ccb *ccb)
|
||
|
{
|
||
|
ccb->ccb_xs = NULL;
|
||
|
bzero(ccb->ccb_cmd, ARC_IO_CMD_LEN);
|
||
|
mtx_enter(&sc->sc_ccb_mtx);
|
||
|
SLIST_INSERT_HEAD(&sc->sc_ccb_free, ccb, ccb_link);
|
||
|
mtx_leave(&sc->sc_ccb_mtx);
|
||
|
}
|