diff options
author | Ed Lin <ed.lin@promise.com> | 2006-09-27 07:23:41 -0400 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.il.steeleye.com> | 2006-10-01 16:05:52 -0400 |
commit | fb4f66be59f3dcc66fda2e681f1fc77b5cc4508d (patch) | |
tree | 89f40c17fdb8b1f334ec3350c30186a0e4e3348e | |
parent | f903d7b7a80b7c3103335d506533790a322da87b (diff) |
[SCSI] stex: add new device (id 0x8650) support
A new device (id 0x8650, nickname 'yosemite') support is added.
It's basically the same, except for following items:
- mapping of id and lun by firmware
- special handling for some commands in interrupt routine
- change of internal copy function for these special commands
- different reset handling code
- different shutdown notification command
Signed-off-by: Ed Lin <ed.lin@promise.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
-rw-r--r-- | drivers/scsi/stex.c | 194 |
1 files changed, 164 insertions, 30 deletions
diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c index cfb29716b5e2..a54e6c1026b7 100644 --- a/drivers/scsi/stex.c +++ b/drivers/scsi/stex.c | |||
@@ -11,7 +11,7 @@ | |||
11 | * Written By: | 11 | * Written By: |
12 | * Ed Lin <promise_linux@promise.com> | 12 | * Ed Lin <promise_linux@promise.com> |
13 | * | 13 | * |
14 | * Version: 2.9.0.13 | 14 | * Version: 3.0.0.1 |
15 | * | 15 | * |
16 | */ | 16 | */ |
17 | 17 | ||
@@ -37,11 +37,11 @@ | |||
37 | #include <scsi/scsi_tcq.h> | 37 | #include <scsi/scsi_tcq.h> |
38 | 38 | ||
39 | #define DRV_NAME "stex" | 39 | #define DRV_NAME "stex" |
40 | #define ST_DRIVER_VERSION "2.9.0.13" | 40 | #define ST_DRIVER_VERSION "3.0.0.1" |
41 | #define ST_VER_MAJOR 2 | 41 | #define ST_VER_MAJOR 3 |
42 | #define ST_VER_MINOR 9 | 42 | #define ST_VER_MINOR 0 |
43 | #define ST_OEM 0 | 43 | #define ST_OEM 0 |
44 | #define ST_BUILD_VER 13 | 44 | #define ST_BUILD_VER 1 |
45 | 45 | ||
46 | enum { | 46 | enum { |
47 | /* MU register offset */ | 47 | /* MU register offset */ |
@@ -120,12 +120,18 @@ enum { | |||
120 | 120 | ||
121 | st_shasta = 0, | 121 | st_shasta = 0, |
122 | st_vsc = 1, | 122 | st_vsc = 1, |
123 | st_yosemite = 2, | ||
123 | 124 | ||
124 | PASSTHRU_REQ_TYPE = 0x00000001, | 125 | PASSTHRU_REQ_TYPE = 0x00000001, |
125 | PASSTHRU_REQ_NO_WAKEUP = 0x00000100, | 126 | PASSTHRU_REQ_NO_WAKEUP = 0x00000100, |
126 | ST_INTERNAL_TIMEOUT = 30, | 127 | ST_INTERNAL_TIMEOUT = 30, |
127 | 128 | ||
129 | ST_TO_CMD = 0, | ||
130 | ST_FROM_CMD = 1, | ||
131 | |||
128 | /* vendor specific commands of Promise */ | 132 | /* vendor specific commands of Promise */ |
133 | MGT_CMD = 0xd8, | ||
134 | SINBAND_MGT_CMD = 0xd9, | ||
129 | ARRAY_CMD = 0xe0, | 135 | ARRAY_CMD = 0xe0, |
130 | CONTROLLER_CMD = 0xe1, | 136 | CONTROLLER_CMD = 0xe1, |
131 | DEBUGGING_CMD = 0xe2, | 137 | DEBUGGING_CMD = 0xe2, |
@@ -133,14 +139,48 @@ enum { | |||
133 | 139 | ||
134 | PASSTHRU_GET_ADAPTER = 0x05, | 140 | PASSTHRU_GET_ADAPTER = 0x05, |
135 | PASSTHRU_GET_DRVVER = 0x10, | 141 | PASSTHRU_GET_DRVVER = 0x10, |
142 | |||
143 | CTLR_CONFIG_CMD = 0x03, | ||
144 | CTLR_SHUTDOWN = 0x0d, | ||
145 | |||
136 | CTLR_POWER_STATE_CHANGE = 0x0e, | 146 | CTLR_POWER_STATE_CHANGE = 0x0e, |
137 | CTLR_POWER_SAVING = 0x01, | 147 | CTLR_POWER_SAVING = 0x01, |
138 | 148 | ||
139 | PASSTHRU_SIGNATURE = 0x4e415041, | 149 | PASSTHRU_SIGNATURE = 0x4e415041, |
150 | MGT_CMD_SIGNATURE = 0xba, | ||
140 | 151 | ||
141 | INQUIRY_EVPD = 0x01, | 152 | INQUIRY_EVPD = 0x01, |
142 | }; | 153 | }; |
143 | 154 | ||
155 | /* SCSI inquiry data */ | ||
156 | typedef struct st_inq { | ||
157 | u8 DeviceType :5; | ||
158 | u8 DeviceTypeQualifier :3; | ||
159 | u8 DeviceTypeModifier :7; | ||
160 | u8 RemovableMedia :1; | ||
161 | u8 Versions; | ||
162 | u8 ResponseDataFormat :4; | ||
163 | u8 HiSupport :1; | ||
164 | u8 NormACA :1; | ||
165 | u8 ReservedBit :1; | ||
166 | u8 AERC :1; | ||
167 | u8 AdditionalLength; | ||
168 | u8 Reserved[2]; | ||
169 | u8 SoftReset :1; | ||
170 | u8 CommandQueue :1; | ||
171 | u8 Reserved2 :1; | ||
172 | u8 LinkedCommands :1; | ||
173 | u8 Synchronous :1; | ||
174 | u8 Wide16Bit :1; | ||
175 | u8 Wide32Bit :1; | ||
176 | u8 RelativeAddressing :1; | ||
177 | u8 VendorId[8]; | ||
178 | u8 ProductId[16]; | ||
179 | u8 ProductRevisionLevel[4]; | ||
180 | u8 VendorSpecific[20]; | ||
181 | u8 Reserved3[40]; | ||
182 | } ST_INQ; | ||
183 | |||
144 | struct st_sgitem { | 184 | struct st_sgitem { |
145 | u8 ctrl; /* SG_CF_xxx */ | 185 | u8 ctrl; /* SG_CF_xxx */ |
146 | u8 reserved[3]; | 186 | u8 reserved[3]; |
@@ -242,7 +282,8 @@ struct st_drvver { | |||
242 | #define MU_REQ_BUFFER_SIZE (MU_REQ_COUNT * sizeof(struct req_msg)) | 282 | #define MU_REQ_BUFFER_SIZE (MU_REQ_COUNT * sizeof(struct req_msg)) |
243 | #define MU_STATUS_BUFFER_SIZE (MU_STATUS_COUNT * sizeof(struct status_msg)) | 283 | #define MU_STATUS_BUFFER_SIZE (MU_STATUS_COUNT * sizeof(struct status_msg)) |
244 | #define MU_BUFFER_SIZE (MU_REQ_BUFFER_SIZE + MU_STATUS_BUFFER_SIZE) | 284 | #define MU_BUFFER_SIZE (MU_REQ_BUFFER_SIZE + MU_STATUS_BUFFER_SIZE) |
245 | #define STEX_BUFFER_SIZE (MU_BUFFER_SIZE + sizeof(struct st_frame)) | 285 | #define STEX_EXTRA_SIZE max(sizeof(struct st_frame), sizeof(ST_INQ)) |
286 | #define STEX_BUFFER_SIZE (MU_BUFFER_SIZE + STEX_EXTRA_SIZE) | ||
246 | 287 | ||
247 | struct st_ccb { | 288 | struct st_ccb { |
248 | struct req_msg *req; | 289 | struct req_msg *req; |
@@ -403,7 +444,7 @@ static int stex_map_sg(struct st_hba *hba, | |||
403 | } | 444 | } |
404 | 445 | ||
405 | static void stex_internal_copy(struct scsi_cmnd *cmd, | 446 | static void stex_internal_copy(struct scsi_cmnd *cmd, |
406 | const void *src, size_t *count, int sg_count) | 447 | const void *src, size_t *count, int sg_count, int direction) |
407 | { | 448 | { |
408 | size_t lcount; | 449 | size_t lcount; |
409 | size_t len; | 450 | size_t len; |
@@ -427,7 +468,10 @@ static void stex_internal_copy(struct scsi_cmnd *cmd, | |||
427 | } else | 468 | } else |
428 | d = cmd->request_buffer; | 469 | d = cmd->request_buffer; |
429 | 470 | ||
430 | memcpy(d, s, len); | 471 | if (direction == ST_TO_CMD) |
472 | memcpy(d, s, len); | ||
473 | else | ||
474 | memcpy(s, d, len); | ||
431 | 475 | ||
432 | lcount -= len; | 476 | lcount -= len; |
433 | if (cmd->use_sg) | 477 | if (cmd->use_sg) |
@@ -449,7 +493,7 @@ static int stex_direct_copy(struct scsi_cmnd *cmd, | |||
449 | return 0; | 493 | return 0; |
450 | } | 494 | } |
451 | 495 | ||
452 | stex_internal_copy(cmd, src, &cp_len, n_elem); | 496 | stex_internal_copy(cmd, src, &cp_len, n_elem, ST_TO_CMD); |
453 | 497 | ||
454 | if (cmd->use_sg) | 498 | if (cmd->use_sg) |
455 | pci_unmap_sg(hba->pdev, cmd->request_buffer, | 499 | pci_unmap_sg(hba->pdev, cmd->request_buffer, |
@@ -480,7 +524,7 @@ static void stex_controller_info(struct st_hba *hba, struct st_ccb *ccb) | |||
480 | p->subid = | 524 | p->subid = |
481 | hba->pdev->subsystem_vendor << 16 | hba->pdev->subsystem_device; | 525 | hba->pdev->subsystem_vendor << 16 | hba->pdev->subsystem_device; |
482 | 526 | ||
483 | stex_internal_copy(ccb->cmd, p, &count, ccb->sg_count); | 527 | stex_internal_copy(ccb->cmd, p, &count, ccb->sg_count, ST_TO_CMD); |
484 | } | 528 | } |
485 | 529 | ||
486 | static void | 530 | static void |
@@ -594,8 +638,14 @@ stex_queuecommand(struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *)) | |||
594 | return SCSI_MLQUEUE_HOST_BUSY; | 638 | return SCSI_MLQUEUE_HOST_BUSY; |
595 | 639 | ||
596 | req = stex_alloc_req(hba); | 640 | req = stex_alloc_req(hba); |
597 | req->lun = lun; | 641 | |
598 | req->target = id; | 642 | if (hba->cardtype == st_yosemite) { |
643 | req->lun = lun * (ST_MAX_TARGET_NUM - 1) + id; | ||
644 | req->target = 0; | ||
645 | } else { | ||
646 | req->lun = lun; | ||
647 | req->target = id; | ||
648 | } | ||
599 | 649 | ||
600 | /* cdb */ | 650 | /* cdb */ |
601 | memcpy(req->cdb, cmd->cmnd, STEX_CDB_LENGTH); | 651 | memcpy(req->cdb, cmd->cmnd, STEX_CDB_LENGTH); |
@@ -679,7 +729,51 @@ static void stex_copy_data(struct st_ccb *ccb, | |||
679 | 729 | ||
680 | if (ccb->cmd == NULL) | 730 | if (ccb->cmd == NULL) |
681 | return; | 731 | return; |
682 | stex_internal_copy(ccb->cmd, resp->variable, &count, ccb->sg_count); | 732 | stex_internal_copy(ccb->cmd, |
733 | resp->variable, &count, ccb->sg_count, ST_TO_CMD); | ||
734 | } | ||
735 | |||
736 | static void stex_ys_commands(struct st_hba *hba, | ||
737 | struct st_ccb *ccb, struct status_msg *resp) | ||
738 | { | ||
739 | size_t count; | ||
740 | |||
741 | if (ccb->cmd->cmnd[0] == MGT_CMD && | ||
742 | resp->scsi_status != SAM_STAT_CHECK_CONDITION) { | ||
743 | ccb->cmd->request_bufflen = | ||
744 | le32_to_cpu(*(__le32 *)&resp->variable[0]); | ||
745 | return; | ||
746 | } | ||
747 | |||
748 | if (resp->srb_status != 0) | ||
749 | return; | ||
750 | |||
751 | /* determine inquiry command status by DeviceTypeQualifier */ | ||
752 | if (ccb->cmd->cmnd[0] == INQUIRY && | ||
753 | resp->scsi_status == SAM_STAT_GOOD) { | ||
754 | ST_INQ *inq_data; | ||
755 | |||
756 | count = STEX_EXTRA_SIZE; | ||
757 | stex_internal_copy(ccb->cmd, hba->copy_buffer, | ||
758 | &count, ccb->sg_count, ST_FROM_CMD); | ||
759 | inq_data = (ST_INQ *)hba->copy_buffer; | ||
760 | if (inq_data->DeviceTypeQualifier != 0) | ||
761 | ccb->srb_status = SRB_STATUS_SELECTION_TIMEOUT; | ||
762 | else | ||
763 | ccb->srb_status = SRB_STATUS_SUCCESS; | ||
764 | } else if (ccb->cmd->cmnd[0] == REPORT_LUNS) { | ||
765 | u8 *report_lun_data = (u8 *)hba->copy_buffer; | ||
766 | |||
767 | count = STEX_EXTRA_SIZE; | ||
768 | stex_internal_copy(ccb->cmd, report_lun_data, | ||
769 | &count, ccb->sg_count, ST_FROM_CMD); | ||
770 | if (report_lun_data[2] || report_lun_data[3]) { | ||
771 | report_lun_data[2] = 0x00; | ||
772 | report_lun_data[3] = 0x08; | ||
773 | stex_internal_copy(ccb->cmd, report_lun_data, | ||
774 | &count, ccb->sg_count, ST_TO_CMD); | ||
775 | } | ||
776 | } | ||
683 | } | 777 | } |
684 | 778 | ||
685 | static void stex_mu_intr(struct st_hba *hba, u32 doorbell) | 779 | static void stex_mu_intr(struct st_hba *hba, u32 doorbell) |
@@ -701,8 +795,17 @@ static void stex_mu_intr(struct st_hba *hba, u32 doorbell) | |||
701 | return; | 795 | return; |
702 | } | 796 | } |
703 | 797 | ||
704 | if (unlikely(hba->mu_status != MU_STATE_STARTED || | 798 | /* |
705 | hba->out_req_cnt <= 0)) { | 799 | * it's not a valid status payload if: |
800 | * 1. there are no pending requests(e.g. during init stage) | ||
801 | * 2. there are some pending requests, but the controller is in | ||
802 | * reset status, and its type is not st_yosemite | ||
803 | * firmware of st_yosemite in reset status will return pending requests | ||
804 | * to driver, so we allow it to pass | ||
805 | */ | ||
806 | if (unlikely(hba->out_req_cnt <= 0 || | ||
807 | (hba->mu_status == MU_STATE_RESETTING && | ||
808 | hba->cardtype != st_yosemite))) { | ||
706 | hba->status_tail = hba->status_head; | 809 | hba->status_tail = hba->status_head; |
707 | goto update_status; | 810 | goto update_status; |
708 | } | 811 | } |
@@ -722,6 +825,7 @@ static void stex_mu_intr(struct st_hba *hba, u32 doorbell) | |||
722 | if (unlikely(ccb->req == NULL)) { | 825 | if (unlikely(ccb->req == NULL)) { |
723 | printk(KERN_WARNING DRV_NAME | 826 | printk(KERN_WARNING DRV_NAME |
724 | "(%s): lagging req\n", pci_name(hba->pdev)); | 827 | "(%s): lagging req\n", pci_name(hba->pdev)); |
828 | hba->out_req_cnt--; | ||
725 | continue; | 829 | continue; |
726 | } | 830 | } |
727 | 831 | ||
@@ -740,9 +844,13 @@ static void stex_mu_intr(struct st_hba *hba, u32 doorbell) | |||
740 | ccb->scsi_status = resp->scsi_status; | 844 | ccb->scsi_status = resp->scsi_status; |
741 | 845 | ||
742 | if (likely(ccb->cmd != NULL)) { | 846 | if (likely(ccb->cmd != NULL)) { |
847 | if (hba->cardtype == st_yosemite) | ||
848 | stex_ys_commands(hba, ccb, resp); | ||
849 | |||
743 | if (unlikely(ccb->cmd->cmnd[0] == PASSTHRU_CMD && | 850 | if (unlikely(ccb->cmd->cmnd[0] == PASSTHRU_CMD && |
744 | ccb->cmd->cmnd[1] == PASSTHRU_GET_ADAPTER)) | 851 | ccb->cmd->cmnd[1] == PASSTHRU_GET_ADAPTER)) |
745 | stex_controller_info(hba, ccb); | 852 | stex_controller_info(hba, ccb); |
853 | |||
746 | stex_unmap_sg(hba, ccb->cmd); | 854 | stex_unmap_sg(hba, ccb->cmd); |
747 | stex_scsi_done(ccb); | 855 | stex_scsi_done(ccb); |
748 | hba->out_req_cnt--; | 856 | hba->out_req_cnt--; |
@@ -947,6 +1055,7 @@ static int stex_reset(struct scsi_cmnd *cmd) | |||
947 | { | 1055 | { |
948 | struct st_hba *hba; | 1056 | struct st_hba *hba; |
949 | unsigned long flags; | 1057 | unsigned long flags; |
1058 | unsigned long before; | ||
950 | hba = (struct st_hba *) &cmd->device->host->hostdata[0]; | 1059 | hba = (struct st_hba *) &cmd->device->host->hostdata[0]; |
951 | 1060 | ||
952 | hba->mu_status = MU_STATE_RESETTING; | 1061 | hba->mu_status = MU_STATE_RESETTING; |
@@ -954,20 +1063,37 @@ static int stex_reset(struct scsi_cmnd *cmd) | |||
954 | if (hba->cardtype == st_shasta) | 1063 | if (hba->cardtype == st_shasta) |
955 | stex_hard_reset(hba); | 1064 | stex_hard_reset(hba); |
956 | 1065 | ||
957 | if (stex_handshake(hba)) { | 1066 | if (hba->cardtype != st_yosemite) { |
958 | printk(KERN_WARNING DRV_NAME | 1067 | if (stex_handshake(hba)) { |
959 | "(%s): resetting: handshake failed\n", | 1068 | printk(KERN_WARNING DRV_NAME |
960 | pci_name(hba->pdev)); | 1069 | "(%s): resetting: handshake failed\n", |
961 | return FAILED; | 1070 | pci_name(hba->pdev)); |
1071 | return FAILED; | ||
1072 | } | ||
1073 | spin_lock_irqsave(hba->host->host_lock, flags); | ||
1074 | hba->req_head = 0; | ||
1075 | hba->req_tail = 0; | ||
1076 | hba->status_head = 0; | ||
1077 | hba->status_tail = 0; | ||
1078 | hba->out_req_cnt = 0; | ||
1079 | spin_unlock_irqrestore(hba->host->host_lock, flags); | ||
1080 | return SUCCESS; | ||
962 | } | 1081 | } |
963 | spin_lock_irqsave(hba->host->host_lock, flags); | ||
964 | hba->req_head = 0; | ||
965 | hba->req_tail = 0; | ||
966 | hba->status_head = 0; | ||
967 | hba->status_tail = 0; | ||
968 | hba->out_req_cnt = 0; | ||
969 | spin_unlock_irqrestore(hba->host->host_lock, flags); | ||
970 | 1082 | ||
1083 | /* st_yosemite */ | ||
1084 | writel(MU_INBOUND_DOORBELL_RESET, hba->mmio_base + IDBL); | ||
1085 | readl(hba->mmio_base + IDBL); /* flush */ | ||
1086 | before = jiffies; | ||
1087 | while (hba->out_req_cnt > 0) { | ||
1088 | if (time_after(jiffies, before + ST_INTERNAL_TIMEOUT * HZ)) { | ||
1089 | printk(KERN_WARNING DRV_NAME | ||
1090 | "(%s): reset timeout\n", pci_name(hba->pdev)); | ||
1091 | return FAILED; | ||
1092 | } | ||
1093 | msleep(1); | ||
1094 | } | ||
1095 | |||
1096 | hba->mu_status = MU_STATE_STARTED; | ||
971 | return SUCCESS; | 1097 | return SUCCESS; |
972 | } | 1098 | } |
973 | 1099 | ||
@@ -1155,9 +1281,16 @@ static void stex_hba_stop(struct st_hba *hba) | |||
1155 | req = stex_alloc_req(hba); | 1281 | req = stex_alloc_req(hba); |
1156 | memset(req->cdb, 0, STEX_CDB_LENGTH); | 1282 | memset(req->cdb, 0, STEX_CDB_LENGTH); |
1157 | 1283 | ||
1158 | req->cdb[0] = CONTROLLER_CMD; | 1284 | if (hba->cardtype == st_yosemite) { |
1159 | req->cdb[1] = CTLR_POWER_STATE_CHANGE; | 1285 | req->cdb[0] = MGT_CMD; |
1160 | req->cdb[2] = CTLR_POWER_SAVING; | 1286 | req->cdb[1] = MGT_CMD_SIGNATURE; |
1287 | req->cdb[2] = CTLR_CONFIG_CMD; | ||
1288 | req->cdb[3] = CTLR_SHUTDOWN; | ||
1289 | } else { | ||
1290 | req->cdb[0] = CONTROLLER_CMD; | ||
1291 | req->cdb[1] = CTLR_POWER_STATE_CHANGE; | ||
1292 | req->cdb[2] = CTLR_POWER_SAVING; | ||
1293 | } | ||
1161 | 1294 | ||
1162 | hba->ccb[tag].cmd = NULL; | 1295 | hba->ccb[tag].cmd = NULL; |
1163 | hba->ccb[tag].sg_count = 0; | 1296 | hba->ccb[tag].sg_count = 0; |
@@ -1221,6 +1354,7 @@ static struct pci_device_id stex_pci_tbl[] = { | |||
1221 | { 0x105a, 0x8301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_shasta }, | 1354 | { 0x105a, 0x8301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_shasta }, |
1222 | { 0x105a, 0x8302, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_shasta }, | 1355 | { 0x105a, 0x8302, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_shasta }, |
1223 | { 0x1725, 0x7250, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_vsc }, | 1356 | { 0x1725, 0x7250, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_vsc }, |
1357 | { 0x105a, 0x8650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_yosemite }, | ||
1224 | { } /* terminate list */ | 1358 | { } /* terminate list */ |
1225 | }; | 1359 | }; |
1226 | MODULE_DEVICE_TABLE(pci, stex_pci_tbl); | 1360 | MODULE_DEVICE_TABLE(pci, stex_pci_tbl); |